test/testpalette.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 19 Jan 2011 22:20:44 -0800
changeset 5046 4cb778067834
parent 3589 06f0768a904c
permissions -rw-r--r--
Clearing the API changes for the 1.3.0 release
     1 /*
     2  * testpalette.c
     3  *
     4  * A simple test of runtime palette modification for animation
     5  * (using the SDL_SetPalette() API). 
     6  */
     7 
     8 #include <stdio.h>
     9 #include <stdlib.h>
    10 #include <string.h>
    11 #include <math.h>
    12 
    13 #include "SDL.h"
    14 
    15 /* screen size */
    16 #define SCRW 640
    17 #define SCRH 480
    18 
    19 #define NBOATS 5
    20 #define SPEED 2
    21 
    22 #ifndef MIN
    23 #define MIN(a, b) ((a) < (b) ? (a) : (b))
    24 #endif
    25 #ifndef MAX
    26 #define MAX(a, b) ((a) > (b) ? (a) : (b))
    27 #endif
    28 
    29 /*
    30  * wave colours: Made by taking a narrow cross-section of a wave picture
    31  * in Gimp, saving in PPM ascii format and formatting with Emacs macros.
    32  */
    33 static SDL_Color wavemap[] = {
    34     {0, 2, 103}, {0, 7, 110}, {0, 13, 117}, {0, 19, 125},
    35     {0, 25, 133}, {0, 31, 141}, {0, 37, 150}, {0, 43, 158},
    36     {0, 49, 166}, {0, 55, 174}, {0, 61, 182}, {0, 67, 190},
    37     {0, 73, 198}, {0, 79, 206}, {0, 86, 214}, {0, 96, 220},
    38     {5, 105, 224}, {12, 112, 226}, {19, 120, 227}, {26, 128, 229},
    39     {33, 135, 230}, {40, 143, 232}, {47, 150, 234}, {54, 158, 236},
    40     {61, 165, 238}, {68, 173, 239}, {75, 180, 241}, {82, 188, 242},
    41     {89, 195, 244}, {96, 203, 246}, {103, 210, 248}, {112, 218, 250},
    42     {124, 224, 250}, {135, 226, 251}, {146, 229, 251}, {156, 231, 252},
    43     {167, 233, 252}, {178, 236, 252}, {189, 238, 252}, {200, 240, 252},
    44     {211, 242, 252}, {222, 244, 252}, {233, 247, 252}, {242, 249, 252},
    45     {237, 250, 252}, {209, 251, 252}, {174, 251, 252}, {138, 252, 252},
    46     {102, 251, 252}, {63, 250, 252}, {24, 243, 252}, {7, 225, 252},
    47     {4, 203, 252}, {3, 181, 252}, {2, 158, 252}, {1, 136, 251},
    48     {0, 111, 248}, {0, 82, 234}, {0, 63, 213}, {0, 50, 192},
    49     {0, 39, 172}, {0, 28, 152}, {0, 17, 132}, {0, 7, 114}
    50 };
    51 
    52 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
    53 static void
    54 quit(int rc)
    55 {
    56     SDL_Quit();
    57     exit(rc);
    58 }
    59 
    60 static void
    61 sdlerr(char *when)
    62 {
    63     fprintf(stderr, "SDL error: %s: %s\n", when, SDL_GetError());
    64     quit(1);
    65 }
    66 
    67 /* create a background surface */
    68 static SDL_Surface *
    69 make_bg(SDL_Surface * screen, int startcol)
    70 {
    71     int i;
    72     SDL_Surface *bg =
    73         SDL_CreateRGBSurface(SDL_SWSURFACE, screen->w, screen->h,
    74                              8, 0, 0, 0, 0);
    75     if (!bg)
    76         sdlerr("creating background surface");
    77 
    78     /* set the palette to the logical screen palette so that blits
    79        won't be translated */
    80     SDL_SetSurfacePalette(bg, screen->format->palette);
    81 
    82     /* Make a wavy background pattern using colours 0-63 */
    83     if (SDL_LockSurface(bg) < 0)
    84         sdlerr("locking background");
    85     for (i = 0; i < SCRH; i++) {
    86         Uint8 *p = (Uint8 *) bg->pixels + i * bg->pitch;
    87         int j, d;
    88         d = 0;
    89         for (j = 0; j < SCRW; j++) {
    90             int v = MAX(d, -2);
    91             v = MIN(v, 2);
    92             if (i > 0)
    93                 v += p[-bg->pitch] + 65 - startcol;
    94             p[j] = startcol + (v & 63);
    95             d += ((rand() >> 3) % 3) - 1;
    96         }
    97     }
    98     SDL_UnlockSurface(bg);
    99     return (bg);
   100 }
   101 
   102 /*
   103  * Return a surface flipped horisontally. Only works for 8bpp;
   104  * extension to arbitrary bitness is left as an exercise for the reader.
   105  */
   106 static SDL_Surface *
   107 hflip(SDL_Surface * s)
   108 {
   109     int i;
   110     SDL_Surface *z = SDL_CreateRGBSurface(SDL_SWSURFACE, s->w, s->h, 8,
   111                                           0, 0, 0, 0);
   112     /* copy palette */
   113     SDL_SetColors(z, s->format->palette->colors,
   114                   0, s->format->palette->ncolors);
   115     if (SDL_LockSurface(s) < 0 || SDL_LockSurface(z) < 0)
   116         sdlerr("locking flip images");
   117 
   118     for (i = 0; i < s->h; i++) {
   119         int j;
   120         Uint8 *from = (Uint8 *) s->pixels + i * s->pitch;
   121         Uint8 *to = (Uint8 *) z->pixels + i * z->pitch + s->w - 1;
   122         for (j = 0; j < s->w; j++)
   123             to[-j] = from[j];
   124     }
   125 
   126     SDL_UnlockSurface(z);
   127     SDL_UnlockSurface(s);
   128     return z;
   129 }
   130 
   131 int
   132 main(int argc, char **argv)
   133 {
   134     SDL_Color cmap[256];
   135     SDL_Surface *screen;
   136     SDL_Surface *bg;
   137     SDL_Surface *boat[2];
   138     unsigned vidflags = 0;
   139     unsigned start;
   140     int fade_max = 400;
   141     int fade_level, fade_dir;
   142     int boatcols, frames, i, red;
   143     int boatx[NBOATS], boaty[NBOATS], boatdir[NBOATS];
   144     int gamma_fade = 0;
   145     int gamma_ramp = 0;
   146 
   147     if (SDL_Init(SDL_INIT_VIDEO) < 0)
   148         sdlerr("initialising SDL");
   149 
   150     while (--argc) {
   151         ++argv;
   152         if (strcmp(*argv, "-hw") == 0)
   153             vidflags |= SDL_HWSURFACE;
   154         else if (strcmp(*argv, "-fullscreen") == 0)
   155             vidflags |= SDL_FULLSCREEN;
   156         else if (strcmp(*argv, "-nofade") == 0)
   157             fade_max = 1;
   158         else if (strcmp(*argv, "-gamma") == 0)
   159             gamma_fade = 1;
   160         else if (strcmp(*argv, "-gammaramp") == 0)
   161             gamma_ramp = 1;
   162         else {
   163             fprintf(stderr,
   164                     "usage: testpalette "
   165                     " [-hw] [-fullscreen] [-nofade] [-gamma] [-gammaramp]\n");
   166             quit(1);
   167         }
   168     }
   169 
   170     /* Ask explicitly for 8bpp and a hardware palette */
   171     if ((screen =
   172          SDL_SetVideoMode(SCRW, SCRH, 8, vidflags | SDL_HWPALETTE)) == NULL) {
   173         fprintf(stderr, "error setting %dx%d 8bpp indexed mode: %s\n",
   174                 SCRW, SCRH, SDL_GetError());
   175         quit(1);
   176     }
   177 
   178     if (vidflags & SDL_FULLSCREEN)
   179         SDL_ShowCursor(SDL_FALSE);
   180 
   181     if ((boat[0] = SDL_LoadBMP("sail.bmp")) == NULL)
   182         sdlerr("loading sail.bmp");
   183     /* We've chosen magenta (#ff00ff) as colour key for the boat */
   184     SDL_SetColorKey(boat[0], SDL_SRCCOLORKEY | SDL_RLEACCEL,
   185                     SDL_MapRGB(boat[0]->format, 0xff, 0x00, 0xff));
   186     boatcols = boat[0]->format->palette->ncolors;
   187     if (boatcols >= 256)
   188         sdlerr("too many colors in sail.bmp");
   189     boat[1] = hflip(boat[0]);
   190     SDL_SetColorKey(boat[1], SDL_SRCCOLORKEY | SDL_RLEACCEL,
   191                     SDL_MapRGB(boat[1]->format, 0xff, 0x00, 0xff));
   192 
   193     /*
   194      * First set the physical screen palette to black, so the user won't
   195      * see our initial drawing on the screen.
   196      */
   197     memset(cmap, 0, sizeof(cmap));
   198     SDL_SetPalette(screen, SDL_PHYSPAL, cmap, 0, 256);
   199 
   200     /*
   201      * Proper palette management is important when playing games with the
   202      * colormap. We have divided the palette as follows:
   203      *
   204      * index 0..(boatcols-1):           used for the boat
   205      * index boatcols..(boatcols+63):   used for the waves
   206      */
   207     SDL_SetPalette(screen, SDL_LOGPAL,
   208                    boat[0]->format->palette->colors, 0, boatcols);
   209     SDL_SetPalette(screen, SDL_LOGPAL, wavemap, boatcols, 64);
   210 
   211     /*
   212      * Now the logical screen palette is set, and will remain unchanged.
   213      * The boats already have the same palette so fast blits can be used.
   214      */
   215     memcpy(cmap, screen->format->palette->colors, 256 * sizeof(SDL_Color));
   216 
   217     /* save the index of the red colour for later */
   218     red = SDL_MapRGB(screen->format, 0xff, 0x00, 0x00);
   219 
   220     bg = make_bg(screen, boatcols);     /* make a nice wavy background surface */
   221 
   222     /* initial screen contents */
   223     if (SDL_BlitSurface(bg, NULL, screen, NULL) < 0)
   224         sdlerr("blitting background to screen");
   225     SDL_Flip(screen);           /* actually put the background on screen */
   226 
   227     /* determine initial boat placements */
   228     for (i = 0; i < NBOATS; i++) {
   229         boatx[i] = (rand() % (SCRW + boat[0]->w)) - boat[0]->w;
   230         boaty[i] = i * (SCRH - boat[0]->h) / (NBOATS - 1);
   231         boatdir[i] = ((rand() >> 5) & 1) * 2 - 1;
   232     }
   233 
   234     start = SDL_GetTicks();
   235     frames = 0;
   236     fade_dir = 1;
   237     fade_level = 0;
   238     do {
   239         SDL_Event e;
   240         SDL_Rect updates[NBOATS];
   241         SDL_Rect r;
   242         int redphase;
   243 
   244         /* A small event loop: just exit on any key or mouse button event */
   245         while (SDL_PollEvent(&e)) {
   246             if (e.type == SDL_KEYDOWN || e.type == SDL_QUIT
   247                 || e.type == SDL_MOUSEBUTTONDOWN) {
   248                 if (fade_dir < 0)
   249                     fade_level = 0;
   250                 fade_dir = -1;
   251             }
   252         }
   253 
   254         /* move boats */
   255         for (i = 0; i < NBOATS; i++) {
   256             int old_x = boatx[i];
   257             /* update boat position */
   258             boatx[i] += boatdir[i] * SPEED;
   259             if (boatx[i] <= -boat[0]->w || boatx[i] >= SCRW)
   260                 boatdir[i] = -boatdir[i];
   261 
   262             /* paint over the old boat position */
   263             r.x = old_x;
   264             r.y = boaty[i];
   265             r.w = boat[0]->w;
   266             r.h = boat[0]->h;
   267             if (SDL_BlitSurface(bg, &r, screen, &r) < 0)
   268                 sdlerr("blitting background");
   269 
   270             /* construct update rectangle (bounding box of old and new pos) */
   271             updates[i].x = MIN(old_x, boatx[i]);
   272             updates[i].y = boaty[i];
   273             updates[i].w = boat[0]->w + SPEED;
   274             updates[i].h = boat[0]->h;
   275             /* clip update rectangle to screen */
   276             if (updates[i].x < 0) {
   277                 updates[i].w += updates[i].x;
   278                 updates[i].x = 0;
   279             }
   280             if (updates[i].x + updates[i].w > SCRW)
   281                 updates[i].w = SCRW - updates[i].x;
   282         }
   283 
   284         for (i = 0; i < NBOATS; i++) {
   285             /* paint boat on new position */
   286             r.x = boatx[i];
   287             r.y = boaty[i];
   288             if (SDL_BlitSurface(boat[(boatdir[i] + 1) / 2], NULL,
   289                                 screen, &r) < 0)
   290                 sdlerr("blitting boat");
   291         }
   292 
   293         /* cycle wave palette */
   294         for (i = 0; i < 64; i++)
   295             cmap[boatcols + ((i + frames) & 63)] = wavemap[i];
   296 
   297         if (fade_dir) {
   298             /* Fade the entire palette in/out */
   299             fade_level += fade_dir;
   300 
   301             if (gamma_fade) {
   302                 /* Fade linearly in gamma level (lousy) */
   303                 float level = (float) fade_level / fade_max;
   304                 if (SDL_SetGamma(level, level, level) < 0)
   305                     sdlerr("setting gamma");
   306 
   307             } else if (gamma_ramp) {
   308                 /* Fade using gamma ramp (better) */
   309                 Uint16 ramp[256];
   310                 for (i = 0; i < 256; i++)
   311                     ramp[i] = (i * fade_level / fade_max) << 8;
   312                 if (SDL_SetGammaRamp(ramp, ramp, ramp) < 0)
   313                     sdlerr("setting gamma ramp");
   314 
   315             } else {
   316                 /* Fade using direct palette manipulation (best) */
   317                 memcpy(cmap, screen->format->palette->colors,
   318                        boatcols * sizeof(SDL_Color));
   319                 for (i = 0; i < boatcols + 64; i++) {
   320                     cmap[i].r = cmap[i].r * fade_level / fade_max;
   321                     cmap[i].g = cmap[i].g * fade_level / fade_max;
   322                     cmap[i].b = cmap[i].b * fade_level / fade_max;
   323                 }
   324             }
   325             if (fade_level == fade_max)
   326                 fade_dir = 0;
   327         }
   328 
   329         /* pulse the red colour (done after the fade, for a night effect) */
   330         redphase = frames % 64;
   331         cmap[red].r = (int) (255 * sin(redphase * M_PI / 63));
   332 
   333         SDL_SetPalette(screen, SDL_PHYSPAL, cmap, 0, boatcols + 64);
   334 
   335         /* update changed areas of the screen */
   336         SDL_UpdateRects(screen, NBOATS, updates);
   337         frames++;
   338     } while (fade_level > 0);
   339 
   340     printf("%d frames, %.2f fps\n",
   341            frames, 1000.0 * frames / (SDL_GetTicks() - start));
   342 
   343     if (vidflags & SDL_FULLSCREEN)
   344         SDL_ShowCursor(SDL_TRUE);
   345     SDL_Quit();
   346     return 0;
   347 }