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