4 * A simple test of runtime palette modification for animation
5 * (using the SDL_SetPalette() API).
13 /* This isn't in the Windows headers */
15 #define M_PI 3.14159265358979323846
28 #define MIN(a, b) ((a) < (b) ? (a) : (b))
31 #define MAX(a, b) ((a) > (b) ? (a) : (b))
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.
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}
57 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
58 static void quit(int rc
)
64 static void sdlerr(char *when
)
66 fprintf(stderr
, "SDL error: %s: %s\n", when
, SDL_GetError());
70 /* create a background surface */
71 static SDL_Surface
*make_bg(SDL_Surface
*screen
, int startcol
)
74 SDL_Surface
*bg
= SDL_CreateRGBSurface(SDL_SWSURFACE
, screen
->w
, screen
->h
,
77 sdlerr("creating background surface");
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);
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
;
90 for(j
= 0; j
< SCRW
; j
++) {
94 v
+= p
[-bg
->pitch
] + 65 - startcol
;
95 p
[j
] = startcol
+ (v
& 63);
96 d
+= ((rand() >> 3) % 3) - 1;
99 SDL_UnlockSurface(bg
);
104 * Return a surface flipped horisontally. Only works for 8bpp;
105 * extension to arbitrary bitness is left as an exercise for the reader.
107 static SDL_Surface
*hflip(SDL_Surface
*s
)
110 SDL_Surface
*z
= SDL_CreateRGBSurface(SDL_SWSURFACE
, s
->w
, s
->h
, 8,
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");
118 for(i
= 0; i
< s
->h
; i
++) {
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
++)
126 SDL_UnlockSurface(z
);
127 SDL_UnlockSurface(s
);
131 int main(int argc
, char **argv
)
136 SDL_Surface
*boat
[2];
137 unsigned vidflags
= 0;
140 int fade_level
, fade_dir
;
141 int boatcols
, frames
, i
, red
;
142 int boatx
[NBOATS
], boaty
[NBOATS
], boatdir
[NBOATS
];
146 if(SDL_Init(SDL_INIT_VIDEO
) < 0)
147 sdlerr("initialising SDL");
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)
157 else if(strcmp(*argv
, "-gamma") == 0)
159 else if(strcmp(*argv
, "-gammaramp") == 0)
163 "usage: testpalette "
164 " [-hw] [-fullscreen] [-nofade] [-gamma] [-gammaramp]\n");
169 /* Ask explicitly for 8bpp and a hardware palette */
170 if((screen
= SDL_SetVideoMode(SCRW
, SCRH
, 8, vidflags
| SDL_HWPALETTE
)) == NULL
) {
171 fprintf(stderr
, "error setting %dx%d 8bpp indexed mode: %s\n",
172 SCRW
, SCRH
, SDL_GetError());
176 if (vidflags
& SDL_FULLSCREEN
) SDL_ShowCursor (SDL_FALSE
);
178 if((boat
[0] = SDL_LoadBMP("sail.bmp")) == NULL
)
179 sdlerr("loading sail.bmp");
180 /* We've chosen magenta (#ff00ff) as colour key for the boat */
181 SDL_SetColorKey(boat
[0], SDL_SRCCOLORKEY
| SDL_RLEACCEL
,
182 SDL_MapRGB(boat
[0]->format
, 0xff, 0x00, 0xff));
183 boatcols
= boat
[0]->format
->palette
->ncolors
;
184 boat
[1] = hflip(boat
[0]);
185 SDL_SetColorKey(boat
[1], SDL_SRCCOLORKEY
| SDL_RLEACCEL
,
186 SDL_MapRGB(boat
[1]->format
, 0xff, 0x00, 0xff));
189 * First set the physical screen palette to black, so the user won't
190 * see our initial drawing on the screen.
192 memset(cmap
, 0, sizeof(cmap
));
193 SDL_SetPalette(screen
, SDL_PHYSPAL
, cmap
, 0, 256);
196 * Proper palette management is important when playing games with the
197 * colormap. We have divided the palette as follows:
199 * index 0..(boatcols-1): used for the boat
200 * index boatcols..(boatcols+63): used for the waves
202 SDL_SetPalette(screen
, SDL_LOGPAL
,
203 boat
[0]->format
->palette
->colors
, 0, boatcols
);
204 SDL_SetPalette(screen
, SDL_LOGPAL
, wavemap
, boatcols
, 64);
207 * Now the logical screen palette is set, and will remain unchanged.
208 * The boats already have the same palette so fast blits can be used.
210 memcpy(cmap
, screen
->format
->palette
->colors
, 256 * sizeof(SDL_Color
));
212 /* save the index of the red colour for later */
213 red
= SDL_MapRGB(screen
->format
, 0xff, 0x00, 0x00);
215 bg
= make_bg(screen
, boatcols
); /* make a nice wavy background surface */
217 /* initial screen contents */
218 if(SDL_BlitSurface(bg
, NULL
, screen
, NULL
) < 0)
219 sdlerr("blitting background to screen");
220 SDL_Flip(screen
); /* actually put the background on screen */
222 /* determine initial boat placements */
223 for(i
= 0; i
< NBOATS
; i
++) {
224 boatx
[i
] = (rand() % (SCRW
+ boat
[0]->w
)) - boat
[0]->w
;
225 boaty
[i
] = i
* (SCRH
- boat
[0]->h
) / (NBOATS
- 1);
226 boatdir
[i
] = ((rand() >> 5) & 1) * 2 - 1;
229 start
= SDL_GetTicks();
235 SDL_Rect updates
[NBOATS
];
239 /* A small event loop: just exit on any key or mouse button event */
240 while(SDL_PollEvent(&e
)) {
241 if(e
.type
== SDL_KEYDOWN
|| e
.type
== SDL_QUIT
242 || e
.type
== SDL_MOUSEBUTTONDOWN
) {
250 for(i
= 0; i
< NBOATS
; i
++) {
251 int old_x
= boatx
[i
];
252 /* update boat position */
253 boatx
[i
] += boatdir
[i
] * SPEED
;
254 if(boatx
[i
] <= -boat
[0]->w
|| boatx
[i
] >= SCRW
)
255 boatdir
[i
] = -boatdir
[i
];
257 /* paint over the old boat position */
262 if(SDL_BlitSurface(bg
, &r
, screen
, &r
) < 0)
263 sdlerr("blitting background");
265 /* construct update rectangle (bounding box of old and new pos) */
266 updates
[i
].x
= MIN(old_x
, boatx
[i
]);
267 updates
[i
].y
= boaty
[i
];
268 updates
[i
].w
= boat
[0]->w
+ SPEED
;
269 updates
[i
].h
= boat
[0]->h
;
270 /* clip update rectangle to screen */
271 if(updates
[i
].x
< 0) {
272 updates
[i
].w
+= updates
[i
].x
;
275 if(updates
[i
].x
+ updates
[i
].w
> SCRW
)
276 updates
[i
].w
= SCRW
- updates
[i
].x
;
279 for(i
= 0; i
< NBOATS
; i
++) {
280 /* paint boat on new position */
283 if(SDL_BlitSurface(boat
[(boatdir
[i
] + 1) / 2], NULL
,
285 sdlerr("blitting boat");
288 /* cycle wave palette */
289 for(i
= 0; i
< 64; i
++)
290 cmap
[boatcols
+ ((i
+ frames
) & 63)] = wavemap
[i
];
293 /* Fade the entire palette in/out */
294 fade_level
+= fade_dir
;
297 /* Fade linearly in gamma level (lousy) */
298 float level
= (float)fade_level
/ fade_max
;
299 if(SDL_SetGamma(level
, level
, level
) < 0)
300 sdlerr("setting gamma");
302 } else if(gamma_ramp
) {
303 /* Fade using gamma ramp (better) */
305 for(i
= 0; i
< 256; i
++)
306 ramp
[i
] = (i
* fade_level
/ fade_max
) << 8;
307 if(SDL_SetGammaRamp(ramp
, ramp
, ramp
) < 0)
308 sdlerr("setting gamma ramp");
311 /* Fade using direct palette manipulation (best) */
312 memcpy(cmap
, screen
->format
->palette
->colors
,
313 boatcols
* sizeof(SDL_Color
));
314 for(i
= 0; i
< boatcols
+ 64; i
++) {
315 cmap
[i
].r
= cmap
[i
].r
* fade_level
/ fade_max
;
316 cmap
[i
].g
= cmap
[i
].g
* fade_level
/ fade_max
;
317 cmap
[i
].b
= cmap
[i
].b
* fade_level
/ fade_max
;
320 if(fade_level
== fade_max
)
324 /* pulse the red colour (done after the fade, for a night effect) */
325 redphase
= frames
% 64;
326 cmap
[red
].r
= (int)(255 * sin(redphase
* M_PI
/ 63));
328 SDL_SetPalette(screen
, SDL_PHYSPAL
, cmap
, 0, boatcols
+ 64);
330 /* update changed areas of the screen */
331 SDL_UpdateRects(screen
, NBOATS
, updates
);
333 } while(fade_level
> 0);
335 printf("%d frames, %.2f fps\n",
336 frames
, 1000.0 * frames
/ (SDL_GetTicks() - start
));
338 if (vidflags
& SDL_FULLSCREEN
) SDL_ShowCursor (SDL_TRUE
);