test/testalpha.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 03 Jun 2011 16:03:10 -0400
changeset 5547 4ccecd0901e2
parent 5535 96594ac5fd1a
permissions -rw-r--r--
Assert code's stdio interface was reading from the wrong variable.

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