test/testalpha.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 11 Jan 2009 04:05:28 +0000
changeset 3017 3272431eeee2
parent 2822 cf271ff76061
child 3585 f8816ffa210b
permissions -rw-r--r--
Added testresample.c
     1 
     2 /* Simple program:  Fill a colormap with gray and stripe it down the screen,
     3 		    Then move an alpha valued sprite around the screen.
     4  */
     5 
     6 #include <stdio.h>
     7 #include <stdlib.h>
     8 #include <string.h>
     9 #include <math.h>
    10 
    11 #include "SDL.h"
    12 
    13 #define FRAME_TICKS	(1000/30)       /* 30 frames/second */
    14 
    15 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
    16 static void
    17 quit(int rc)
    18 {
    19     SDL_Quit();
    20     exit(rc);
    21 }
    22 
    23 /* Fill the screen with a gradient */
    24 static void
    25 FillBackground(SDL_Surface * screen)
    26 {
    27     Uint8 *buffer;
    28     Uint8 gradient;
    29     int i, k;
    30 
    31     /* Set the surface pixels and refresh! */
    32     if (SDL_LockSurface(screen) < 0) {
    33         fprintf(stderr, "Couldn't lock the display surface: %s\n",
    34                 SDL_GetError());
    35         quit(2);
    36     }
    37     buffer = (Uint8 *) screen->pixels;
    38     switch (screen->format->BytesPerPixel) {
    39     case 1:
    40     case 3:
    41         for (i = 0; i < screen->h; ++i) {
    42             memset(buffer, (i * 255) / screen->h,
    43                    screen->w * screen->format->BytesPerPixel);
    44             buffer += screen->pitch;
    45         }
    46         break;
    47     case 2:
    48         for (i = 0; i < screen->h; ++i) {
    49             Uint16 *buffer16;
    50             Uint16 color;
    51 
    52             gradient = ((i * 255) / screen->h);
    53             color = (Uint16) SDL_MapRGB(screen->format,
    54                                         gradient, gradient, gradient);
    55             buffer16 = (Uint16 *) buffer;
    56             for (k = 0; k < screen->w; k++) {
    57                 *buffer16++ = color;
    58             }
    59             buffer += screen->pitch;
    60         }
    61         break;
    62     case 4:
    63         for (i = 0; i < screen->h; ++i) {
    64             Uint32 *buffer32;
    65             Uint32 color;
    66 
    67             gradient = ((i * 255) / screen->h);
    68             color = SDL_MapRGB(screen->format, gradient, gradient, gradient);
    69             buffer32 = (Uint32 *) buffer;
    70             for (k = 0; k < screen->w; k++) {
    71                 *buffer32++ = color;
    72             }
    73             buffer += screen->pitch;
    74         }
    75         break;
    76     }
    77 
    78     SDL_UnlockSurface(screen);
    79     SDL_UpdateRect(screen, 0, 0, 0, 0);
    80 }
    81 
    82 /* Create a "light" -- a yellowish surface with variable alpha */
    83 SDL_Surface *
    84 CreateLight(int radius)
    85 {
    86     Uint8 trans, alphamask;
    87     int range, addition;
    88     int xdist, ydist;
    89     Uint16 x, y;
    90     Uint16 skip;
    91     Uint32 pixel;
    92     SDL_Surface *light;
    93 
    94 #ifdef LIGHT_16BIT
    95     Uint16 *buf;
    96 
    97     /* Create a 16 (4/4/4/4) bpp square with a full 4-bit alpha channel */
    98     /* Note: this isn't any faster than a 32 bit alpha surface */
    99     alphamask = 0x0000000F;
   100     light = SDL_CreateRGBSurface(SDL_SWSURFACE, 2 * radius, 2 * radius, 16,
   101                                  0x0000F000, 0x00000F00, 0x000000F0,
   102                                  alphamask);
   103 #else
   104     Uint32 *buf;
   105 
   106     /* Create a 32 (8/8/8/8) bpp square with a full 8-bit alpha channel */
   107     alphamask = 0x000000FF;
   108     light = SDL_CreateRGBSurface(SDL_SWSURFACE, 2 * radius, 2 * radius, 32,
   109                                  0xFF000000, 0x00FF0000, 0x0000FF00,
   110                                  alphamask);
   111     if (light == NULL) {
   112         fprintf(stderr, "Couldn't create light: %s\n", SDL_GetError());
   113         return (NULL);
   114     }
   115 #endif
   116 
   117     /* Fill with a light yellow-orange color */
   118     skip = light->pitch - (light->w * light->format->BytesPerPixel);
   119 #ifdef LIGHT_16BIT
   120     buf = (Uint16 *) light->pixels;
   121 #else
   122     buf = (Uint32 *) light->pixels;
   123 #endif
   124     /* Get a tranparent pixel value - we'll add alpha later */
   125     pixel = SDL_MapRGBA(light->format, 0xFF, 0xDD, 0x88, 0);
   126     for (y = 0; y < light->h; ++y) {
   127         for (x = 0; x < light->w; ++x) {
   128             *buf++ = pixel;
   129         }
   130         buf += skip;            /* Almost always 0, but just in case... */
   131     }
   132 
   133     /* Calculate alpha values for the surface. */
   134 #ifdef LIGHT_16BIT
   135     buf = (Uint16 *) light->pixels;
   136 #else
   137     buf = (Uint32 *) light->pixels;
   138 #endif
   139     for (y = 0; y < light->h; ++y) {
   140         for (x = 0; x < light->w; ++x) {
   141             /* Slow distance formula (from center of light) */
   142             xdist = x - (light->w / 2);
   143             ydist = y - (light->h / 2);
   144             range = (int) sqrt(xdist * xdist + ydist * ydist);
   145 
   146             /* Scale distance to range of transparency (0-255) */
   147             if (range > radius) {
   148                 trans = alphamask;
   149             } else {
   150                 /* Increasing transparency with distance */
   151                 trans = (Uint8) ((range * alphamask) / radius);
   152 
   153                 /* Lights are very transparent */
   154                 addition = (alphamask + 1) / 8;
   155                 if ((int) trans + addition > alphamask) {
   156                     trans = alphamask;
   157                 } else {
   158                     trans += addition;
   159                 }
   160             }
   161             /* We set the alpha component as the right N bits */
   162             *buf++ |= (255 - trans);
   163         }
   164         buf += skip;            /* Almost always 0, but just in case... */
   165     }
   166     /* Enable RLE acceleration of this alpha surface */
   167     SDL_SetAlpha(light, SDL_SRCALPHA | SDL_RLEACCEL, 0);
   168 
   169     /* We're done! */
   170     return (light);
   171 }
   172 
   173 static Uint32 flashes = 0;
   174 static Uint32 flashtime = 0;
   175 
   176 void
   177 FlashLight(SDL_Surface * screen, SDL_Surface * light, int x, int y)
   178 {
   179     SDL_Rect position;
   180     Uint32 ticks1;
   181     Uint32 ticks2;
   182 
   183     /* Easy, center light */
   184     position.x = x - (light->w / 2);
   185     position.y = y - (light->h / 2);
   186     position.w = light->w;
   187     position.h = light->h;
   188     ticks1 = SDL_GetTicks();
   189     SDL_BlitSurface(light, NULL, screen, &position);
   190     ticks2 = SDL_GetTicks();
   191     SDL_UpdateRects(screen, 1, &position);
   192     ++flashes;
   193 
   194     /* Update time spend doing alpha blitting */
   195     flashtime += (ticks2 - ticks1);
   196 }
   197 
   198 static int sprite_visible = 0;
   199 static SDL_Surface *sprite;
   200 static SDL_Surface *backing;
   201 static SDL_Rect position;
   202 static int x_vel, y_vel;
   203 static int alpha_vel;
   204 
   205 int
   206 LoadSprite(SDL_Surface * screen, char *file)
   207 {
   208     SDL_Surface *converted;
   209 
   210     /* Load the sprite image */
   211     sprite = SDL_LoadBMP(file);
   212     if (sprite == NULL) {
   213         fprintf(stderr, "Couldn't load %s: %s", file, SDL_GetError());
   214         return (-1);
   215     }
   216 
   217     /* Set transparent pixel as the pixel at (0,0) */
   218     if (sprite->format->palette) {
   219         SDL_SetColorKey(sprite, SDL_SRCCOLORKEY, *(Uint8 *) sprite->pixels);
   220     }
   221 
   222     /* Convert sprite to video format */
   223     converted = SDL_DisplayFormat(sprite);
   224     SDL_FreeSurface(sprite);
   225     if (converted == NULL) {
   226         fprintf(stderr, "Couldn't convert background: %s\n", SDL_GetError());
   227         return (-1);
   228     }
   229     sprite = converted;
   230 
   231     /* Create the background */
   232     backing = SDL_CreateRGBSurface(SDL_SWSURFACE, sprite->w, sprite->h, 8,
   233                                    0, 0, 0, 0);
   234     if (backing == NULL) {
   235         fprintf(stderr, "Couldn't create background: %s\n", SDL_GetError());
   236         SDL_FreeSurface(sprite);
   237         return (-1);
   238     }
   239 
   240     /* Convert background to video format */
   241     converted = SDL_DisplayFormat(backing);
   242     SDL_FreeSurface(backing);
   243     if (converted == NULL) {
   244         fprintf(stderr, "Couldn't convert background: %s\n", SDL_GetError());
   245         SDL_FreeSurface(sprite);
   246         return (-1);
   247     }
   248     backing = converted;
   249 
   250     /* Set the initial position of the sprite */
   251     position.x = (screen->w - sprite->w) / 2;
   252     position.y = (screen->h - sprite->h) / 2;
   253     position.w = sprite->w;
   254     position.h = sprite->h;
   255     x_vel = 0;
   256     y_vel = 0;
   257     alpha_vel = 1;
   258 
   259     /* We're ready to roll. :) */
   260     return (0);
   261 }
   262 
   263 void
   264 AttractSprite(Uint16 x, Uint16 y)
   265 {
   266     x_vel = ((int) x - position.x) / 10;
   267     y_vel = ((int) y - position.y) / 10;
   268 }
   269 
   270 void
   271 MoveSprite(SDL_Surface * screen, SDL_Surface * light)
   272 {
   273     SDL_Rect updates[2];
   274     Uint8 alpha;
   275 
   276     /* Erase the sprite if it was visible */
   277     if (sprite_visible) {
   278         updates[0] = position;
   279         SDL_BlitSurface(backing, NULL, screen, &updates[0]);
   280     } else {
   281         updates[0].x = 0;
   282         updates[0].y = 0;
   283         updates[0].w = 0;
   284         updates[0].h = 0;
   285         sprite_visible = 1;
   286     }
   287 
   288     /* Since the sprite is off the screen, we can do other drawing
   289        without being overwritten by the saved area behind the sprite.
   290      */
   291     if (light != NULL) {
   292         int x, y;
   293 
   294         SDL_GetMouseState(0, &x, &y);
   295         FlashLight(screen, light, x, y);
   296     }
   297 
   298     /* Move the sprite, bounce at the wall */
   299     position.x += x_vel;
   300     if ((position.x < 0) || (position.x >= screen->w)) {
   301         x_vel = -x_vel;
   302         position.x += x_vel;
   303     }
   304     position.y += y_vel;
   305     if ((position.y < 0) || (position.y >= screen->h)) {
   306         y_vel = -y_vel;
   307         position.y += y_vel;
   308     }
   309 
   310     /* Update transparency (fade in and out) */
   311     SDL_GetSurfaceAlphaMod(sprite, &alpha);
   312     if (((int) alpha + alpha_vel) < 0) {
   313         alpha_vel = -alpha_vel;
   314     } else if (((int) alpha + alpha_vel) > 255) {
   315         alpha_vel = -alpha_vel;
   316     }
   317     SDL_SetAlpha(sprite, SDL_SRCALPHA, (Uint8) (alpha + alpha_vel));
   318 
   319     /* Save the area behind the sprite */
   320     updates[1] = position;
   321     SDL_BlitSurface(screen, &updates[1], backing, NULL);
   322 
   323     /* Blit the sprite onto the screen */
   324     updates[1] = position;
   325     SDL_BlitSurface(sprite, NULL, screen, &updates[1]);
   326 
   327     /* Make it so! */
   328     SDL_UpdateRects(screen, 2, updates);
   329 }
   330 
   331 int
   332 main(int argc, char *argv[])
   333 {
   334     const SDL_VideoInfo *info;
   335     SDL_Surface *screen;
   336     int w, h;
   337     Uint8 video_bpp;
   338     Uint32 videoflags;
   339     int i, done;
   340     SDL_Event event;
   341     SDL_Surface *light;
   342     int mouse_pressed;
   343     Uint32 ticks, lastticks;
   344 
   345 
   346     /* Initialize SDL */
   347     if (SDL_Init(SDL_INIT_VIDEO) < 0) {
   348         fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
   349         return (1);
   350     }
   351 
   352     /* Alpha blending doesn't work well at 8-bit color */
   353 #ifdef _WIN32_WCE
   354     /* Pocket PC */
   355     w = 240;
   356     h = 320;
   357 #else
   358     w = 640;
   359     h = 480;
   360 #endif
   361     info = SDL_GetVideoInfo();
   362     if (info->vfmt->BitsPerPixel > 8) {
   363         video_bpp = info->vfmt->BitsPerPixel;
   364     } else {
   365         video_bpp = 16;
   366         fprintf(stderr, "forced 16 bpp mode\n");
   367     }
   368     videoflags = SDL_SWSURFACE;
   369     for (i = 1; argv[i]; ++i) {
   370         if (strcmp(argv[i], "-bpp") == 0) {
   371             video_bpp = atoi(argv[++i]);
   372             if (video_bpp <= 8) {
   373                 video_bpp = 16;
   374                 fprintf(stderr, "forced 16 bpp mode\n");
   375             }
   376         } else if (strcmp(argv[i], "-hw") == 0) {
   377             videoflags |= SDL_HWSURFACE;
   378         } else if (strcmp(argv[i], "-warp") == 0) {
   379             videoflags |= SDL_HWPALETTE;
   380         } else if (strcmp(argv[i], "-width") == 0 && argv[i + 1]) {
   381             w = atoi(argv[++i]);
   382         } else if (strcmp(argv[i], "-height") == 0 && argv[i + 1]) {
   383             h = atoi(argv[++i]);
   384         } else if (strcmp(argv[i], "-resize") == 0) {
   385             videoflags |= SDL_RESIZABLE;
   386         } else if (strcmp(argv[i], "-noframe") == 0) {
   387             videoflags |= SDL_NOFRAME;
   388         } else if (strcmp(argv[i], "-fullscreen") == 0) {
   389             videoflags |= SDL_FULLSCREEN;
   390         } else {
   391             fprintf(stderr,
   392                     "Usage: %s [-width N] [-height N] [-bpp N] [-warp] [-hw] [-fullscreen]\n",
   393                     argv[0]);
   394             quit(1);
   395         }
   396     }
   397 
   398     /* Set video mode */
   399     if ((screen = SDL_SetVideoMode(w, h, video_bpp, videoflags)) == NULL) {
   400         fprintf(stderr, "Couldn't set %dx%dx%d video mode: %s\n",
   401                 w, h, video_bpp, SDL_GetError());
   402         quit(2);
   403     }
   404     FillBackground(screen);
   405 
   406     /* Create the light */
   407     light = CreateLight(82);
   408     if (light == NULL) {
   409         quit(1);
   410     }
   411 
   412     /* Load the sprite */
   413     if (LoadSprite(screen, "icon.bmp") < 0) {
   414         SDL_FreeSurface(light);
   415         quit(1);
   416     }
   417 
   418     /* Print out information about our surfaces */
   419     printf("Screen is at %d bits per pixel\n", screen->format->BitsPerPixel);
   420     if ((screen->flags & SDL_HWSURFACE) == SDL_HWSURFACE) {
   421         printf("Screen is in video memory\n");
   422     } else {
   423         printf("Screen is in system memory\n");
   424     }
   425     if ((screen->flags & SDL_DOUBLEBUF) == SDL_DOUBLEBUF) {
   426         printf("Screen has double-buffering enabled\n");
   427     }
   428     if ((sprite->flags & SDL_HWSURFACE) == SDL_HWSURFACE) {
   429         printf("Sprite is in video memory\n");
   430     } else {
   431         printf("Sprite is in system memory\n");
   432     }
   433 
   434     /* Run a sample blit to trigger blit acceleration */
   435     MoveSprite(screen, NULL);
   436     if ((sprite->flags & SDL_HWACCEL) == SDL_HWACCEL) {
   437         printf("Sprite blit uses hardware alpha acceleration\n");
   438     } else {
   439         printf("Sprite blit dosn't uses hardware alpha acceleration\n");
   440     }
   441 
   442     /* Set a clipping rectangle to clip the outside edge of the screen */
   443     {
   444         SDL_Rect clip;
   445         clip.x = 32;
   446         clip.y = 32;
   447         clip.w = screen->w - (2 * 32);
   448         clip.h = screen->h - (2 * 32);
   449         SDL_SetClipRect(screen, &clip);
   450     }
   451 
   452     /* Wait for a keystroke */
   453     lastticks = SDL_GetTicks();
   454     done = 0;
   455     mouse_pressed = 0;
   456     while (!done) {
   457         /* Update the frame -- move the sprite */
   458         if (mouse_pressed) {
   459             MoveSprite(screen, light);
   460             mouse_pressed = 0;
   461         } else {
   462             MoveSprite(screen, NULL);
   463         }
   464 
   465         /* Slow down the loop to 30 frames/second */
   466         ticks = SDL_GetTicks();
   467         if ((ticks - lastticks) < FRAME_TICKS) {
   468 #ifdef CHECK_SLEEP_GRANULARITY
   469             fprintf(stderr, "Sleeping %d ticks\n",
   470                     FRAME_TICKS - (ticks - lastticks));
   471 #endif
   472             SDL_Delay(FRAME_TICKS - (ticks - lastticks));
   473 #ifdef CHECK_SLEEP_GRANULARITY
   474             fprintf(stderr, "Slept %d ticks\n", (SDL_GetTicks() - ticks));
   475 #endif
   476         }
   477         lastticks = ticks;
   478 
   479         /* Check for events */
   480         while (SDL_PollEvent(&event)) {
   481             switch (event.type) {
   482             case SDL_VIDEORESIZE:
   483                 screen =
   484                     SDL_SetVideoMode(event.resize.w, event.resize.h,
   485                                      video_bpp, videoflags);
   486                 if (screen) {
   487                     FillBackground(screen);
   488                 }
   489                 break;
   490                 /* Attract sprite while mouse is held down */
   491             case SDL_MOUSEMOTION:
   492                 if (event.motion.state != 0) {
   493                     AttractSprite(event.motion.x, event.motion.y);
   494                     mouse_pressed = 1;
   495                 }
   496                 break;
   497             case SDL_MOUSEBUTTONDOWN:
   498                 if (event.button.button == 1) {
   499                     AttractSprite(event.button.x, event.button.y);
   500                     mouse_pressed = 1;
   501                 } else {
   502                     SDL_Rect area;
   503 
   504                     area.x = event.button.x - 16;
   505                     area.y = event.button.y - 16;
   506                     area.w = 32;
   507                     area.h = 32;
   508                     SDL_FillRect(screen, &area,
   509                                  SDL_MapRGB(screen->format, 0, 0, 0));
   510                     SDL_UpdateRects(screen, 1, &area);
   511                 }
   512                 break;
   513             case SDL_KEYDOWN:
   514                 if (event.key.keysym.sym == SDLK_ESCAPE) {
   515                     done = 1;
   516                 }
   517                 break;
   518             case SDL_QUIT:
   519                 done = 1;
   520                 break;
   521             default:
   522                 break;
   523             }
   524         }
   525     }
   526     SDL_FreeSurface(light);
   527     SDL_FreeSurface(sprite);
   528     SDL_FreeSurface(backing);
   529 
   530     /* Print out some timing information */
   531     if (flashes > 0) {
   532         printf("%d alpha blits, ~%4.4f ms per blit\n",
   533                flashes, (float) flashtime / flashes);
   534     }
   535 
   536     SDL_Quit();
   537     return (0);
   538 }