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