test/testalpha.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 28 May 2006 13:04:16 +0000
branchSDL-1.3
changeset 1662 782fd950bd46
parent 1659 14717b52abc0
child 1668 4da1ee79c9af
permissions -rw-r--r--
Revamp of the video system in progress - adding support for multiple displays, multiple windows, and a full video mode selection API.

WARNING: None of the video drivers have been updated for the new API yet! The API is still under design and very fluid.

The code is now run through a consistent indent format:
indent -i4 -nut -nsc -br -ce

The headers are being converted to automatically generate doxygen documentation.
     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",
   209                  SDL_GetError ());
   210         return (-1);
   211     }
   212     sprite = converted;
   213 
   214     /* Create the background */
   215     backing = SDL_CreateRGBSurface (SDL_SWSURFACE, sprite->w, sprite->h, 8,
   216                                     0, 0, 0, 0);
   217     if (backing == NULL) {
   218         fprintf (stderr, "Couldn't create background: %s\n", SDL_GetError ());
   219         SDL_FreeSurface (sprite);
   220         return (-1);
   221     }
   222 
   223     /* Convert background to video format */
   224     converted = SDL_DisplayFormat (backing);
   225     SDL_FreeSurface (backing);
   226     if (converted == NULL) {
   227         fprintf (stderr, "Couldn't convert background: %s\n",
   228                  SDL_GetError ());
   229         SDL_FreeSurface (sprite);
   230         return (-1);
   231     }
   232     backing = converted;
   233 
   234     /* Set the initial position of the sprite */
   235     position.x = (screen->w - sprite->w) / 2;
   236     position.y = (screen->h - sprite->h) / 2;
   237     position.w = sprite->w;
   238     position.h = sprite->h;
   239     x_vel = 0;
   240     y_vel = 0;
   241     alpha_vel = 1;
   242 
   243     /* We're ready to roll. :) */
   244     return (0);
   245 }
   246 
   247 void
   248 AttractSprite (Uint16 x, Uint16 y)
   249 {
   250     x_vel = ((int) x - position.x) / 10;
   251     y_vel = ((int) y - position.y) / 10;
   252 }
   253 
   254 void
   255 MoveSprite (SDL_Surface * screen, SDL_Surface * light)
   256 {
   257     SDL_Rect updates[2];
   258     int alpha;
   259 
   260     /* Erase the sprite if it was visible */
   261     if (sprite_visible) {
   262         updates[0] = position;
   263         SDL_BlitSurface (backing, NULL, screen, &updates[0]);
   264     } else {
   265         updates[0].x = 0;
   266         updates[0].y = 0;
   267         updates[0].w = 0;
   268         updates[0].h = 0;
   269         sprite_visible = 1;
   270     }
   271 
   272     /* Since the sprite is off the screen, we can do other drawing
   273        without being overwritten by the saved area behind the sprite.
   274      */
   275     if (light != NULL) {
   276         int x, y;
   277 
   278         SDL_GetMouseState (&x, &y);
   279         FlashLight (screen, light, x, y);
   280     }
   281 
   282     /* Move the sprite, bounce at the wall */
   283     position.x += x_vel;
   284     if ((position.x < 0) || (position.x >= screen->w)) {
   285         x_vel = -x_vel;
   286         position.x += x_vel;
   287     }
   288     position.y += y_vel;
   289     if ((position.y < 0) || (position.y >= screen->h)) {
   290         y_vel = -y_vel;
   291         position.y += y_vel;
   292     }
   293 
   294     /* Update transparency (fade in and out) */
   295     alpha = sprite->format->alpha;
   296     if ((alpha + alpha_vel) < 0) {
   297         alpha_vel = -alpha_vel;
   298     } else if ((alpha + alpha_vel) > 255) {
   299         alpha_vel = -alpha_vel;
   300     }
   301     SDL_SetAlpha (sprite, SDL_SRCALPHA, (Uint8) (alpha + alpha_vel));
   302 
   303     /* Save the area behind the sprite */
   304     updates[1] = position;
   305     SDL_BlitSurface (screen, &updates[1], backing, NULL);
   306 
   307     /* Blit the sprite onto the screen */
   308     updates[1] = position;
   309     SDL_BlitSurface (sprite, NULL, screen, &updates[1]);
   310 
   311     /* Make it so! */
   312     SDL_UpdateRects (screen, 2, updates);
   313 }
   314 
   315 void
   316 WarpSprite (SDL_Surface * screen, int x, int y)
   317 {
   318     SDL_Rect updates[2];
   319 
   320     /* Erase, move, Draw, update */
   321     updates[0] = position;
   322     SDL_BlitSurface (backing, NULL, screen, &updates[0]);
   323     position.x = x - sprite->w / 2;     /* Center about X */
   324     position.y = y - sprite->h / 2;     /* Center about Y */
   325     updates[1] = position;
   326     SDL_BlitSurface (screen, &updates[1], backing, NULL);
   327     updates[1] = position;
   328     SDL_BlitSurface (sprite, NULL, screen, &updates[1]);
   329     SDL_UpdateRects (screen, 2, updates);
   330 }
   331 
   332 int
   333 main (int argc, char *argv[])
   334 {
   335     const SDL_VideoInfo *info;
   336     SDL_Surface *screen;
   337     int w, h;
   338     Uint8 video_bpp;
   339     Uint32 videoflags;
   340     int i, done;
   341     SDL_Event event;
   342     SDL_Surface *light;
   343     int mouse_pressed;
   344     Uint32 ticks, lastticks;
   345 
   346 
   347     /* Initialize SDL */
   348     if (SDL_Init (SDL_INIT_VIDEO) < 0) {
   349         fprintf (stderr, "Couldn't initialize SDL: %s\n", SDL_GetError ());
   350         return (1);
   351     }
   352 
   353     /* Alpha blending doesn't work well at 8-bit color */
   354 #ifdef _WIN32_WCE
   355     /* Pocket PC */
   356     w = 240;
   357     h = 320;
   358 #else
   359     w = 640;
   360     h = 480;
   361 #endif
   362     info = SDL_GetVideoInfo ();
   363     if (info->vfmt->BitsPerPixel > 8) {
   364         video_bpp = info->vfmt->BitsPerPixel;
   365     } else {
   366         video_bpp = 16;
   367         fprintf (stderr, "forced 16 bpp mode\n");
   368     }
   369     videoflags = SDL_SWSURFACE;
   370     for (i = 1; argv[i]; ++i) {
   371         if (strcmp (argv[i], "-bpp") == 0) {
   372             video_bpp = atoi (argv[++i]);
   373             if (video_bpp <= 8) {
   374                 video_bpp = 16;
   375                 fprintf (stderr, "forced 16 bpp mode\n");
   376             }
   377         } else if (strcmp (argv[i], "-hw") == 0) {
   378             videoflags |= SDL_HWSURFACE;
   379         } else if (strcmp (argv[i], "-warp") == 0) {
   380             videoflags |= SDL_HWPALETTE;
   381         } else if (strcmp (argv[i], "-width") == 0 && argv[i + 1]) {
   382             w = atoi (argv[++i]);
   383         } else if (strcmp (argv[i], "-height") == 0 && argv[i + 1]) {
   384             h = atoi (argv[++i]);
   385         } else if (strcmp (argv[i], "-resize") == 0) {
   386             videoflags |= SDL_RESIZABLE;
   387         } else if (strcmp (argv[i], "-noframe") == 0) {
   388             videoflags |= SDL_NOFRAME;
   389         } else if (strcmp (argv[i], "-fullscreen") == 0) {
   390             videoflags |= SDL_FULLSCREEN;
   391         } else {
   392             fprintf (stderr,
   393                      "Usage: %s [-width N] [-height N] [-bpp N] [-warp] [-hw] [-fullscreen]\n",
   394                      argv[0]);
   395             quit (1);
   396         }
   397     }
   398 
   399     /* Set video mode */
   400     if ((screen = SDL_SetVideoMode (w, h, video_bpp, videoflags)) == NULL) {
   401         fprintf (stderr, "Couldn't set %dx%dx%d video mode: %s\n",
   402                  w, h, video_bpp, SDL_GetError ());
   403         quit (2);
   404     }
   405     FillBackground (screen);
   406 
   407     /* Create the light */
   408     light = CreateLight (82);
   409     if (light == NULL) {
   410         quit (1);
   411     }
   412 
   413     /* Load the sprite */
   414     if (LoadSprite (screen, "icon.bmp") < 0) {
   415         SDL_FreeSurface (light);
   416         quit (1);
   417     }
   418 
   419     /* Print out information about our surfaces */
   420     printf ("Screen is at %d bits per pixel\n", screen->format->BitsPerPixel);
   421     if ((screen->flags & SDL_HWSURFACE) == SDL_HWSURFACE) {
   422         printf ("Screen is in video memory\n");
   423     } else {
   424         printf ("Screen is in system memory\n");
   425     }
   426     if ((screen->flags & SDL_DOUBLEBUF) == SDL_DOUBLEBUF) {
   427         printf ("Screen has double-buffering enabled\n");
   428     }
   429     if ((sprite->flags & SDL_HWSURFACE) == SDL_HWSURFACE) {
   430         printf ("Sprite is in video memory\n");
   431     } else {
   432         printf ("Sprite is in system memory\n");
   433     }
   434 
   435     /* Run a sample blit to trigger blit acceleration */
   436     MoveSprite (screen, NULL);
   437     if ((sprite->flags & SDL_HWACCEL) == SDL_HWACCEL) {
   438         printf ("Sprite blit uses hardware alpha acceleration\n");
   439     } else {
   440         printf ("Sprite blit dosn't uses hardware alpha acceleration\n");
   441     }
   442 
   443     /* Set a clipping rectangle to clip the outside edge of the screen */
   444     {
   445         SDL_Rect clip;
   446         clip.x = 32;
   447         clip.y = 32;
   448         clip.w = screen->w - (2 * 32);
   449         clip.h = screen->h - (2 * 32);
   450         SDL_SetClipRect (screen, &clip);
   451     }
   452 
   453     /* Wait for a keystroke */
   454     lastticks = SDL_GetTicks ();
   455     done = 0;
   456     mouse_pressed = 0;
   457     while (!done) {
   458         /* Update the frame -- move the sprite */
   459         if (mouse_pressed) {
   460             MoveSprite (screen, light);
   461             mouse_pressed = 0;
   462         } else {
   463             MoveSprite (screen, NULL);
   464         }
   465 
   466         /* Slow down the loop to 30 frames/second */
   467         ticks = SDL_GetTicks ();
   468         if ((ticks - lastticks) < FRAME_TICKS) {
   469 #ifdef CHECK_SLEEP_GRANULARITY
   470             fprintf (stderr, "Sleeping %d ticks\n",
   471                      FRAME_TICKS - (ticks - lastticks));
   472 #endif
   473             SDL_Delay (FRAME_TICKS - (ticks - lastticks));
   474 #ifdef CHECK_SLEEP_GRANULARITY
   475             fprintf (stderr, "Slept %d ticks\n", (SDL_GetTicks () - ticks));
   476 #endif
   477         }
   478         lastticks = ticks;
   479 
   480         /* Check for events */
   481         while (SDL_PollEvent (&event)) {
   482             switch (event.type) {
   483             case SDL_VIDEORESIZE:
   484                 screen =
   485                     SDL_SetVideoMode (event.resize.w, event.resize.h,
   486                                       video_bpp, videoflags);
   487                 if (screen) {
   488                     FillBackground (screen);
   489                 }
   490                 break;
   491                 /* Attract sprite while mouse is held down */
   492             case SDL_MOUSEMOTION:
   493                 if (event.motion.state != 0) {
   494                     AttractSprite (event.motion.x, event.motion.y);
   495                     mouse_pressed = 1;
   496                 }
   497                 break;
   498             case SDL_MOUSEBUTTONDOWN:
   499                 if (event.button.button == 1) {
   500                     AttractSprite (event.button.x, event.button.y);
   501                     mouse_pressed = 1;
   502                 } else {
   503                     SDL_Rect area;
   504 
   505                     area.x = event.button.x - 16;
   506                     area.y = event.button.y - 16;
   507                     area.w = 32;
   508                     area.h = 32;
   509                     SDL_FillRect (screen, &area, 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 }