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