| 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 | } |