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