test/testpalette.c
author Patrice Mandin <patmandin@gmail.com>
Thu, 19 Jan 2006 21:28:52 +0000
changeset 1257 448a9a64537b
parent 1151 be9c9c8f6d53
child 1429 aff0170f9f1b
permissions -rw-r--r--
[PATCH] SDL_GetVideoMode() does not find best mode, part 2

Following commit 1.51, I come accross a problem when SDL must choose between
several video modes that could suit the one asked.

If I ask 320x240 with this list:
768x480 768x240 640x400 640x200 384x480 384x240 320x400 320x200

The smallest selectables modes are 384x240 and 320x400. And SDL choose the later
in this list, but 384x240 is more suitable. So I added a check to compare
the pixel count (surface) of modes, and select the one which has the smallest
pixel count.

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