1 /*
2 explosions.c
3
4 A simplified 3D world with alpha-blended objects that look kind of like
5 fireworks or sparks. Also has fun screen effects.
6
7 Run with "--help" for runtime keyboard and mouse controls.
8 See COPYING.txt for license!
9
10
11 by Bill Kendrick
12 bill@newbreedsoftware.com
13 http://www.newbreedsoftware.com/bill/
14
15 July 30, 2001 - October 6, 2001
16 July 31, 2005
17 */
18
19
20 #include <stdio.h> /* For fprintf()'ing errors & sprintf()'ing filenames */
21 #include <stdlib.h> /* For exit(), rand()/srand() and qsort() functions */
22 #include <math.h> /* For cos() and sin() trig. functions */
23 #include <SDL.h> /* For graphics init., blitting and event functions */
24 #include <SDL_image.h> /* For loading PNG-format image files */
25
26
27 #define VERSION "2001.10.06"
28
29
30 /* Constants: */
31
32 #define FPS 30 /* Max frames-per-second to display */
33 #define NUM_EXPLOSIONS 500 /* Max number of mouse-based explosions to track */
34 #define MAX_BITS 1000 /* Max number of bits we can cue/render */
35
36
37 #define F_WIDTH 640 /* Window width */
38 #define F_HEIGHT 480 /* Window height */
39
40 #define H_WIDTH (F_WIDTH / 2) /* Horizontal center of window */
41 #define H_HEIGHT (F_HEIGHT / 2) /* Vertical center of window */
42
43 #define BURST_PER_FRAME 20 /* How many to shoot out of mouse */
44 #define BURST_SPEED 5 /* How far to shoot out of mouse */
45 #define GRAVITY 0.50 /* Affects of gravity (per frame) */
46
47 #define DISTANCE 500.0 /* Distance (Z) to the 3D space's origin */
48 #define ASPECT 400.0 /* When doing "screen_x = (x / z) + (width / 2)"
49 and "screen_y = (y / z) + (height / 2)"
50 calculations, this scales down the effect of
51 the "Z" value. Changing it can cause a nifty
52 'fish-eye'-lens effect! :) */
53
54
55 /* Effect toggles' possible values: */
56
57 enum { FALSE, TRUE };
58 enum { NOZOOM, ZOOMIN, ZOOMOUT, NUM_ZOOM_MODES };
59
60
61 /* Effect toggles: */
62
63 int fade = FALSE; /* Toggle with [F] key */
64 int zoom = NOZOOM; /* Change with [Z] key */
65 int rotate = FALSE; /* Toggle with [R] key */
66 int blur = FALSE; /* Toggle with [B] key */
67 int dissolve = FALSE; /* Toggle with [D] key */
68 int heat = FALSE; /* Toggle with [H] key */
69
70
71 /* Number of "spark#.png" files available, and human-readable names: */
72
73 enum { YELLOW, GREEN, CYAN, BLUE, RAINBOW, NUMS, PEPPERS, NUM_BITMAPS };
74
75
76 /* Where in the PNGs each of the sprites begin, and their sizes: */
77 /* Probably almost easier to calculate, but we're drawing so many per frame! */
78
79 int src_start[9] = {0, 3, 8, 15, 24, 35, 48, 63, 80};
80 int src_width[9] = {3, 5, 7, 9, 11, 13, 15, 17, 19};
81
82
83 /* Types: */
84
85 /* Explosions that come out of the mouse, and which we must track, move,
86 * and, in the end, destroy: */
87
88 typedef struct explosion_type {
89 int alive, time, c; /* Is the explosion alive? How old? Color? */
90 float x, y, z; /* X,Y,Z coordinates in 3D space */
91 float xm, ym, zm; /* Speed/direction (eg, x < 0 == moving left) */
92 } explosion_type;
93
94
95 /* Each bit (which will be queued, sorted, and then rendered each frame) */
96
97 typedef struct bit_type {
98 int x, y, z, c; /* Screen X and Y coordinates.
99 Z corresponds to size (which sprite to draw)
100 and also is used when sorting (closer objects
101 are drawn last, so that they overlap the
102 farther ones */
103 } bit_type;
104
105
106 /* Globals */
107
108 SDL_Surface * screen; /* Window we're drawing into */
109 SDL_Surface * spark[NUM_BITMAPS]; /* Sprites (loaded from the PNG files) */
110 explosion_type explosions[NUM_EXPLOSIONS]; /* Explosions to track */
111 bit_type bits[MAX_BITS]; /* Queue for bits (sorted, then rendered) */
112 int num_bits; /* How many bits in the queue */
113 float anglex, angley; /* Angle to rotate world (controlled w/ arrow keys) */
114 float cos_anglex, sin_anglex, /* When world-rotation angles change, these */
115 cos_angley, sin_angley; /* are recalculated, so that trig functions
116 are called only a few times per frame,
117 rather than once for EVERY object drawn! */
118
119
120 /* Local function prototypes: */
121
122 void event_loop(void); /* Main event loop */
123 void setup_sdl(void); /* Open window, load PNG */
124 void init(void); /* Init variables */
125 void add_explosion(float x, float y, float z, /* Add expl-bit to array */
126 float xm, float ym, float zm,
127 int time, int color);
128 void calc3d(float x, float y, float z, /* Convert 2D to 3D */
129 int * sx, int * sy, int * ss);
130 void recalctrig(void); /* Re-COS/SIN after rotate */
131 void queuebit(float x, float y, float z, int c); /* Convert object for queue */
132 void addbit(int x, int y, int z, int c); /* Add object to queue */
133 void sortbits(void); /* Sort queue */
134 int compare_bits(const void * b1, const void * b2); /* (For sorting) */
135 void drawbits(void); /* Draw queue */
136 void fadeout(void); /* Fade effect */
137 void zoomout(int zoom_x, int zoom_y); /* Zoom-out effect */
138 void zoomin(int zoom_x, int zoom_y); /* Zoom-in effect */
139 void rotatescreen(int angle); /* Rotation effect */
140 void blurscreen(); /* Blur effect */
141 void dissolvescreen(); /* Dissolve effect */
142 void heatscreen(); /* Heat (flame) effect */
143 Uint32 getpixel(SDL_Surface * surface, /* (For effects) */
144 int x, int y);
145 void putpixel(SDL_Surface * surface, /* (For effects) */
146 int x, int y, Uint32 pixel);
147
148
149 /* --- MAIN --- */
150
main(int argc,char * argv[])151 int main(int argc, char * argv[])
152 {
153 int i;
154
155
156 /* Deal with any command-line options: */
157
158 for (i = 1; i < argc; i++)
159 {
160 if (strcmp(argv[i], "--version") ==0 ||
161 strcmp(argv[i], "-v") == 0)
162 {
163 /* Show version: */
164
165 printf("\n\"explosions\" demo version " VERSION "\n");
166 printf("By Bill Kendrick (bill@newbreedsoftware.com) 2001\n\n");
167
168
169 /* Quit happily: */
170
171 exit(0);
172 }
173 else if (strcmp(argv[i], "--help") ==0 ||
174 strcmp(argv[i], "-h") == 0)
175 {
176 /* Show help (runtime controls): */
177
178 printf("\n\"explosions\" demo help\n"
179 "Keyboard:\n"
180 " ARROWS - Rotate screen\n"
181 " F - Toggle 'fade' effect\n"
182 " Z - Switch to next 'zoom' effect\n"
183 " R - Toggle 'rotation' effect\n"
184 " B - Toggle 'blur' effect\n"
185 " D - Toggle 'dissolve' effect\n"
186 " H - Toggle 'heat' (fire) effect\n"
187 " KEYPAD - Move zoom center\n"
188 " PGUP/DN - Change rotation angle\n"
189 " ESCAPE - Quit\n\n");
190
191
192 /* Quit happily: */
193
194 exit(0);
195 }
196 else
197 {
198 /* Print usage: */
199
200 fprintf(stderr, "\n\"explosions\" demo Usage:\n");
201 fprintf(stderr, "%s [--version | --help]\n\n", argv[0]);
202
203
204 /* And quit with an error: */
205
206 exit(1);
207 }
208 }
209
210
211 /* Open window, create surface, load PNG images: */
212
213 setup_sdl();
214
215
216 /* Clear tracked explosions array and other globals: */
217
218 init();
219
220
221 /* MAIN EVENT LOOP! */
222
223 event_loop();
224
225
226 /* All done... */
227
228 SDL_Quit();
229
230 return(0);
231 }
232
233
234 /* Event loop: */
235
event_loop(void)236 void event_loop(void)
237 {
238 int i, done, count;
239 int mouse_down, mouse_x, mouse_y;
240 int left_down, right_down, up_down, down_down;
241 int kpleft_down, kpright_down, kpup_down, kpdown_down;
242 int pgdn_down, pgup_down;
243 int zoom_x, zoom_y, rotation_angle;
244 int color;
245 Uint32 now, then;
246 SDL_Event event;
247 SDLKey key;
248
249
250 /* No keys or mouse buttons have been pressed yet... */
251
252 mouse_down = 0;
253 mouse_x = 0;
254 mouse_y = 0;
255
256 left_down = 0;
257 right_down = 0;
258 up_down = 0;
259 down_down = 0;
260
261 pgdn_down = 0;
262 pgup_down = 0;
263
264 kpleft_down = 0;
265 kpright_down = 0;
266 kpup_down = 0;
267 kpdown_down = 0;
268
269
270 /* Reset effect variables: */
271
272 zoom_x = H_WIDTH;
273 zoom_y = H_HEIGHT;
274 rotation_angle = 5;
275
276
277 /* Reset global counter */
278
279 count = 0;
280
281
282 /* Current 'drawing' color (changed with BUTTON-3 clicks) */
283
284 color = 0;
285
286
287 /* MAIN EVENT LOOP IS HERE! */
288
289 done = 0; /* (We haven't decided to quit yet) */
290
291 do
292 {
293 /* Get the current time (used at the end of the loop, just before
294 * the while, to make sure framerate doesn't exceed chosen FPS! */
295
296 then = SDL_GetTicks();
297
298
299 /* Increase global counter */
300
301 count += 2;
302
303
304 /* Handle events: */
305
306 while (SDL_PollEvent(&event))
307 {
308 if (event.type == SDL_QUIT)
309 {
310 /* Quit request! (eg, the window's "close" button was clicked) */
311
312 done = 1;
313 }
314 else if (event.type == SDL_KEYDOWN)
315 {
316 /* A keypress! */
317
318 key = event.key.keysym.sym;
319
320 if (key == SDLK_ESCAPE)
321 done = 1; /* ESCAPE - quit! */
322 else if (key == SDLK_LEFT)
323 left_down = 1; /* LEFT - record that left is pressed */
324 else if (key == SDLK_RIGHT)
325 right_down = 1; /* (ditto for RIGHT) */
326 else if (key == SDLK_UP)
327 up_down = 1; /* (ditto for UP) */
328 else if (key == SDLK_DOWN)
329 down_down = 1; /* (ditto for DOWN) */
330 else if (key == SDLK_PAGEDOWN)
331 pgdn_down = 1; /* (ditto for PAGEDOWN) */
332 else if (key == SDLK_PAGEUP)
333 pgup_down = 1; /* (ditto for PAGEUP) */
334 else if (key == SDLK_KP4)
335 kpleft_down = 1; /* (ditto for KPLEFT) */
336 else if (key == SDLK_KP6)
337 kpright_down = 1; /* (ditto for KPRIGHT) */
338 else if (key == SDLK_KP8)
339 kpup_down = 1; /* (ditto for KPUP) */
340 else if (key == SDLK_KP2)
341 kpdown_down = 1; /* (ditto for KPDOWN) */
342 else if (key == SDLK_f)
343 fade = !fade; /* F - Toggle 'fade' effect */
344 else if (key == SDLK_z)
345 {
346 /* Z - Switch 'zoom' mode */
347
348 zoom = (zoom + 1) % NUM_ZOOM_MODES;
349 }
350 else if (key == SDLK_r)
351 rotate = !rotate; /* R - Toggle 'rotation' effect */
352 else if (key == SDLK_b)
353 blur = !blur; /* B - Toggle 'blur' effect */
354 else if (key == SDLK_d)
355 dissolve = !dissolve; /* D - Toggle 'dissolve' effect */
356 else if (key == SDLK_h)
357 heat = !heat; /* H - Toggle 'heat' (fire) effect */
358 }
359 else if (event.type == SDL_KEYUP)
360 {
361 /* A key-release! */
362
363 key = event.key.keysym.sym;
364
365 if (key == SDLK_LEFT)
366 left_down = 0; /* LEFT - record that left was released */
367 else if (key == SDLK_RIGHT)
368 right_down = 0; /* (ditto for RIGHT) */
369 else if (key == SDLK_UP)
370 up_down = 0; /* (ditto for UP) */
371 else if (key == SDLK_DOWN)
372 down_down = 0; /* (ditto for DOWN) */
373 else if (key == SDLK_PAGEDOWN)
374 pgdn_down = 0; /* (ditto for PAGEDOWN) */
375 else if (key == SDLK_PAGEUP)
376 pgup_down = 0; /* (ditto for PAGEUP) */
377 else if (key == SDLK_KP4)
378 kpleft_down = 0; /* (ditto for KPLEFT) */
379 else if (key == SDLK_KP6)
380 kpright_down = 0; /* (ditto for KPRIGHT) */
381 else if (key == SDLK_KP8)
382 kpup_down = 0; /* (ditto for KPUP) */
383 else if (key == SDLK_KP2)
384 kpdown_down = 0; /* (ditto for KPDOWN) */
385 }
386 else if (event.type == SDL_MOUSEBUTTONDOWN)
387 {
388 /* A mouse button was pressed, make note! */
389
390 mouse_down = 1;
391
392
393 /* If it's button-3, change the 'drawing color' */
394
395 if (event.button.button == 3)
396 color = ((color + 1) % NUM_BITMAPS);
397
398
399 /* Notice where the mouse was when user clicked: */
400
401 mouse_x = event.button.x;
402 mouse_y = event.button.y;
403 }
404 else if (event.type == SDL_MOUSEBUTTONUP)
405 {
406 /* A mouse button was released, make note! */
407
408 mouse_down = 0;
409 }
410 else if (event.type == SDL_MOUSEMOTION)
411 {
412 /* Mouse was moved! */
413
414 /* Notice where the mouse is now: (for all we know, the
415 * mouse button is still being held, so we should draw
416 * at the new location!) */
417
418 mouse_x = event.motion.x;
419 mouse_y = event.motion.y;
420 }
421 }
422
423
424 /* Handle rotation keys: */
425
426 if (left_down)
427 {
428 /* LEFT key is (still) depressed. Rotate screen accordingly. */
429
430 anglex = anglex + 5;
431
432
433 /* Recalculate trig. values ("cos_angelx", etc.) */
434
435 recalctrig();
436 }
437
438 if (right_down)
439 {
440 /* (ditto for RIGHT) */
441
442 anglex = anglex - 5;
443 recalctrig();
444 }
445
446 if (up_down)
447 {
448 /* (ditto for UP) */
449
450 angley = angley + 5;
451 recalctrig();
452 }
453
454 if (down_down)
455 {
456 /* (ditto for DOWN) */
457
458 angley = angley - 5;
459 recalctrig();
460 }
461
462 /* NOTE: The above is _not_ an "if / else if / else if ..." block!
463 * Since two keys can be pressed at once (say, LEFT and UP), we want
464 * to be able to rotate both ways at the same time (eg, diagonal!) :) */
465
466
467 /* Handle interactive effects controls keys: */
468
469 /* (Rotation effect) */
470
471 if (pgup_down)
472 {
473 /* PAGEUP key is (still) depressed. Change rotation effect angle. */
474
475 rotation_angle = (rotation_angle - 2) % 360;
476 }
477
478 if (pgdn_down)
479 {
480 /* (ditto for PAGEDOWN) */
481
482 rotation_angle = (rotation_angle + 2) % 360;
483 }
484
485
486 /* (Zoom effect) */
487
488 if (kpleft_down)
489 {
490 /* KEYPAD_LEFT key is (still) depressed. Move zoom effect. */
491
492 zoom_x--;
493 }
494
495 if (kpright_down)
496 {
497 /* (ditto for KEYPAD_RIGHT) */
498
499 zoom_x++;
500 }
501
502 if (kpup_down)
503 {
504 /* (ditto for KEYPAD_UP) */
505
506 zoom_y--;
507 }
508
509 if (kpdown_down)
510 {
511 /* (ditto for KEYPAD_DOWN) */
512
513 zoom_y++;
514 }
515
516
517 /* Add explosion bits: */
518
519 if (mouse_down)
520 {
521 /* Mouse is (still being) clicked, add a few explosion bits: */
522
523 for (i = 0; i < BURST_PER_FRAME; i++)
524 {
525 add_explosion(mouse_x - H_WIDTH, /* Starting X */
526 mouse_y - H_HEIGHT, /* Starting Y */
527 0, /* Starting Z */
528
529 /* Starting XM (random left/right) */
530
531 ((rand() % (BURST_SPEED * 10)) -
532 (BURST_SPEED * 5)) / 10.0,
533 -(rand() % (BURST_SPEED * 10) / 5.0),
534
535 /* Starting YM (random, mostly up) */
536
537 ((rand() % (BURST_SPEED * 10)) -
538 (BURST_SPEED * 5)) / 10.0,
539
540 /* Starting ZM (random in/out) */
541 (rand() % 10) + 20,
542
543 /* Current 'drawing color' */
544 color);
545 }
546 }
547
548
549 /* Move explosions: */
550
551 for (i = 0; i < NUM_EXPLOSIONS; i++)
552 {
553 /* For each explosion that we track that is 'alive'... */
554
555 if (explosions[i].alive)
556 {
557 /* Move its X,Y,Z coords based on the XM,YM,ZM values: */
558
559 explosions[i].x += explosions[i].xm;
560 explosions[i].y += explosions[i].ym;
561 explosions[i].z += explosions[i].zm;
562
563
564 /* Subtly increase the YM value to simulate gravity */
565
566 explosions[i].ym = explosions[i].ym + GRAVITY;
567
568
569 /* Count-down the explosion's lifespan */
570
571 explosions[i].time--;
572
573
574 /* When it hits 0, kill it! */
575
576 if (explosions[i].time <= 0)
577 explosions[i].alive = 0;
578 }
579 }
580
581
582 /* Clear bits queue: */
583
584 num_bits = 0;
585
586
587 /* "Draw" explosions: */
588 /* (eg, add them to the queue) */
589
590 for (i = 0; i < NUM_EXPLOSIONS; i++)
591 {
592 /* For each explosion that we track that is 'alive'... */
593
594 if (explosions[i].alive)
595 {
596 /* If it's not half-dead yet, always draw it...
597 * otherwise, draw it randomly half the time */
598
599 if (explosions[i].time > 10 ||
600 (rand() % 10) < 5)
601 {
602 /* Add this bit to the queue! */
603
604 queuebit(explosions[i].x,
605 explosions[i].y,
606 explosions[i].z,
607 explosions[i].c);
608 }
609 }
610 }
611
612
613 /* Draw green borders seen on the screen: */
614
615 for (i = -H_WIDTH; i <= H_WIDTH - 40; i = i + 40)
616 {
617 /* Top and bottom far lines: */
618
619 queuebit(i + (count % 40), H_HEIGHT, 40, GREEN);
620 queuebit(i - (count % 40) + 40, -H_HEIGHT, 40, GREEN);
621
622
623 /* Top and bottom near lines: */
624
625 queuebit(i + (count % 40), -H_HEIGHT, -40, GREEN);
626 queuebit(i - (count % 40) + 40, H_HEIGHT, -40, GREEN);
627 }
628
629 for (i = -H_HEIGHT; i <= H_HEIGHT - 40; i = i + 40)
630 {
631 /* Left and right far lines: */
632
633 queuebit(-H_WIDTH, i + (count % 40), 40, GREEN);
634 queuebit(H_WIDTH, i - (count % 40) + 40, 40, GREEN);
635
636
637 /* Left and right near lines: */
638
639 queuebit(-H_WIDTH, i - (count % 40) + 40, -40, GREEN);
640 queuebit(H_WIDTH, i + (count % 40), -40, GREEN);
641 }
642
643 /* NOTE: The "i +/- (count % 40)" stuff uses the global counter
644 * value to position the bits in such a way that as time goes on,
645 * the bits appear to be crawling around the edges. */
646
647
648 /* Draw the strange multicolored "Y" shape in the center: */
649
650 for (i = -20; i <= 0; i = i + 5)
651 {
652 queuebit(0, i, 0, GREEN);
653 queuebit(i, -i, 0, CYAN);
654 queuebit(-i, -i, 0, YELLOW);
655 }
656
657
658 /* Draw everything! */
659
660 if (fade == FALSE && zoom == NOZOOM && rotate == FALSE &&
661 blur == FALSE && dissolve == FALSE && heat == FALSE)
662 {
663 /* (Not one effect is enabled, clear the screen) */
664
665 SDL_FillRect(screen, /* In the window... */
666 NULL, /* The entire window (dest rect NULL for short) */
667 SDL_MapRGB(screen->format,
668 0x00, 0x00, 0x00)); /* R=0, G=0, B=0 .. black */
669 }
670 else
671 {
672 /* (One or more effect enabled, do them (instead of clearing) */
673
674
675 /* (Since we're accessing raw pixel data, lock the surface
676 if we need to) */
677
678 if (SDL_MUSTLOCK(screen))
679 SDL_LockSurface(screen);
680
681
682 if (heat == TRUE)
683 {
684 /* (Heat (burn) the screen) */
685
686 heatscreen();
687 }
688
689
690 if (fade == TRUE)
691 {
692 /* (Fade the screen some) */
693
694 fadeout();
695 }
696
697
698 if (zoom == ZOOMOUT)
699 {
700 /* (Zoom the screen away) */
701
702 zoomout(zoom_x - H_WIDTH, zoom_y - H_HEIGHT);
703 }
704 else if (zoom == ZOOMIN)
705 {
706 /* (Zoom the screen in) */
707
708 zoomin(zoom_x - H_WIDTH, zoom_y - H_HEIGHT);
709 }
710
711
712 if (rotate == TRUE)
713 {
714 /* (Rotate the screen) */
715
716 rotatescreen(rotation_angle);
717 }
718
719
720 if (blur == TRUE)
721 {
722 /* (Blur the screen) */
723
724 blurscreen();
725 }
726
727
728 if (dissolve == TRUE)
729 {
730 /* (Dissolve the screen) */
731
732 dissolvescreen();
733 }
734
735
736 /* NOTE: The above is _not_ an "if / else if / else if ..." block!
737 * Multiple effects can be enabled at once, so we need to do
738 * all of them! */
739
740
741 /* (Unlock the surface if it needed locking) */
742
743 if (SDL_MUSTLOCK(screen))
744 SDL_UnlockSurface(screen);
745 }
746
747
748 /* (Sort all of the bits in the queue according to distance) */
749
750 sortbits();
751
752
753 /* (Draw all of the (now sorted) bits) */
754
755 drawbits();
756
757
758 /* (Swap the window's "backbuffer" onto the screen... eg, make the
759 * switch from the previous frame, to the one we just drew, an
760 * 'atomic' (all-at-once) event) */
761
762 SDL_Flip(screen);
763
764
765 /* Pause for even frame rate: */
766
767 /* (What time is it now?) */
768
769 now = SDL_GetTicks();
770
771
772 /* (Has enough (or more than enough) time been eaten to make this
773 * frame appear at the appropriate time (based on the chosen FPS)?) */
774
775 if (now < then + (1000 / FPS))
776 {
777 /* (If not... idle until the time has passed) */
778
779 SDL_Delay(then + (1000 / FPS) - now);
780 }
781 }
782 while (done == 0);
783 }
784
785
786 /* Set up: */
787
setup_sdl(void)788 void setup_sdl(void)
789 {
790 int i;
791 char filename[512];
792
793
794 /* Init SDL's video stuff */
795
796 if (SDL_Init(SDL_INIT_VIDEO) < 0)
797 {
798 fprintf(stderr, "Error: %s\n", SDL_GetError());
799 exit(1);
800 }
801
802
803 /* Open a window: */
804
805 screen = SDL_SetVideoMode(F_WIDTH, F_HEIGHT, 16,
806 SDL_SWSURFACE | SDL_ANYFORMAT);
807 if (screen == NULL)
808 {
809 fprintf(stderr, "Error: %s\n", SDL_GetError());
810 exit(1);
811 }
812
813
814 /* Set window's title: */
815
816 SDL_WM_SetCaption("Explosions", "Explosions");
817
818
819 /* Load all of the PNG image files: */
820
821 for (i = 0; i < NUM_BITMAPS; i++)
822 {
823 /* Construct filename ("spark#.png") */
824
825 sprintf(filename, "/usr/local/share/explosions/spark%d.png", i + 1);
826
827
828 /* Load image file: (SDL_image library call; handles PNG & alpha!) */
829
830 spark[i] = IMG_Load(filename);
831 if (spark[i] == NULL)
832 {
833 fprintf(stderr, "Error: %s - %s\n", filename, SDL_GetError());
834 exit(1);
835 }
836 }
837
838
839 /* Seed random-number generator (so that it's pretty much random each
840 * time we start this program) */
841
842 srand(SDL_GetTicks());
843 }
844
845
846 /* Init: */
847
init(void)848 void init(void)
849 {
850 int i;
851
852
853 /* Initialize all tracked explosions (make them dead) */
854
855 for (i = 0; i < NUM_EXPLOSIONS; i++)
856 explosions[i].alive = 0;
857
858
859 /* Set angle to 'straight-on': */
860
861 anglex = 0;
862 angley = 0;
863
864 /* (and calculate the trig values) */
865
866 recalctrig();
867 }
868
869
870 /* Add an explosion: */
871
add_explosion(float x,float y,float z,float xm,float ym,float zm,int time,int color)872 void add_explosion(float x, float y, float z,
873 float xm, float ym, float zm,
874 int time, int color)
875 {
876 int found, i;
877
878
879 /* Look for an empty slot in the "explosions[]" array: */
880
881 found = -1;
882
883 for (i = 0; i < NUM_EXPLOSIONS && found == -1; i++)
884 {
885 if (explosions[i].alive == 0)
886 found = i;
887 }
888
889
890 /* If we found one, fill it! */
891
892 if (found != -1)
893 {
894 explosions[found].alive = 1; /* Alive is true */
895 explosions[found].time = time; /* Starting age */
896
897 explosions[found].x = x; /* X, Y, Z coords... */
898 explosions[found].y = y;
899 explosions[found].z = z;
900
901 explosions[found].xm = xm; /* XM, YM, ZM values... */
902 explosions[found].ym = ym;
903 explosions[found].zm = zm;
904
905 explosions[found].c = color; /* Color */
906 }
907 }
908
909
910 /* Convert 3D to 2D: */
911
calc3d(float x,float y,float z,int * sx,int * sy,int * sz)912 void calc3d(float x, float y, float z,
913 int * sx, int * sy, int * sz)
914 {
915 float xx, yy, zz;
916
917
918 /* Taking the "x", "y" and "z" values, rotate them based on the
919 * trig. values which should have been recalculated after the
920 * "anglex" and "angley" angles-of-rotation were changed... */
921
922 xx = (x * cos_anglex) - (z * sin_anglex);
923 zz = (x * sin_anglex) + (z * cos_anglex);
924
925 yy = (y * cos_angley) - (zz * sin_angley);
926 zz = (y * sin_angley) + (zz * cos_angley);
927
928
929 /* Based on the new (rotated) "xx", "yy" and "zz" values,
930 * convert them to screen coordinates ("sx" and "sy"), with
931 * the 3D world's (0,0,0) origin centered around the center of the window */
932
933 *sx = (xx / ((zz + DISTANCE) / ASPECT)) + H_WIDTH;
934 *sy = (yy / ((zz + DISTANCE) / ASPECT)) + H_HEIGHT;
935
936
937 /* Keep track of the rotated "Z" value, since we use it to determine
938 * which sprite to use (smaller ones for more distant objects, and
939 * larger sprites for closer objects), as well as use it to sort the
940 * queue of bits-to-draw based on distance (so they overlap each other
941 * realistically) */
942
943 *sz = ((zz + (DISTANCE / 4)) * 100) / (ASPECT / 10);
944 }
945
946
947 /* Recalc. trig.: */
948
recalctrig(void)949 void recalctrig(void)
950 {
951 /* Calculate some COS and SIN values based on the "anglex" and "angley"
952 * angle-of-rotation values. They are only calculated when the
953 * angles change (rather than calculating them for EACH object! Eek!) */
954
955 cos_anglex = cos(M_PI * anglex / 180.0);
956 sin_anglex = sin(M_PI * anglex / 180.0);
957
958 cos_angley = cos(M_PI * angley / 180.0);
959 sin_angley = sin(M_PI * angley / 180.0);
960 }
961
962
963 /* "Draw" one explosion bit: */
964 /* (Add it to the queue of things which will need to be sorted and drawn) */
965
queuebit(float x,float y,float z,int c)966 void queuebit(float x, float y, float z, int c)
967 {
968 int tmp_x, tmp_y, tmp_z;
969
970
971 /* Convert the X,Y,Z coordinates to screen coordinates and
972 * that "Z" distance value */
973
974 calc3d(x, y, z, &tmp_x, &tmp_y, &tmp_z);
975
976
977 /* If it's TOO close to the viewer, we won't have a large enough
978 * sprite to draw for it, so use the largest one. */
979 /* NOT THE BEST WAY OF DOING THIS, REALLY */
980
981 if (tmp_z < 0)
982 tmp_z = 0;
983
984
985 /* Add this bit to the queue of things to sort-and-draw... */
986
987 addbit(tmp_x, tmp_y, tmp_z, c);
988 }
989
990
991 /* Add a bit to the bit queue: */
992
addbit(int x,int y,int z,int c)993 void addbit(int x, int y, int z, int c)
994 {
995 /* As long as we haven't already filled the queue... */
996
997 if (num_bits < MAX_BITS)
998 {
999 /* Add a bit to the queue: */
1000
1001 bits[num_bits].x = x; /* Screen X */
1002 bits[num_bits].y = y; /* Screen Y */
1003 bits[num_bits].z = z; /* Distance value (for sprite size & sorting) */
1004 bits[num_bits].c = c; /* "Drawing color" */
1005
1006
1007 /* Increase the size of the queue */
1008
1009 num_bits++;
1010 }
1011 }
1012
1013
1014 /* Sort the bits by Z: */
1015
sortbits(void)1016 void sortbits(void)
1017 {
1018 qsort(bits, /* Thing to sort; the queue (an array of "bit_type"'s) */
1019 num_bits, /* How many there are; size of the queue */
1020 sizeof(bit_type), /* Size of data structures being sorted */
1021 compare_bits); /* Function to call to do the actual comparisons */
1022 }
1023
1024
1025 /* Comparison function for sortbits()'s qsort() call: */
1026
compare_bits(const void * b1,const void * b2)1027 int compare_bits(const void * b1, const void * b2)
1028 {
1029 /* Return a positive, negative, or zero value to qsort() depending
1030 on which of the two bits (b1 and b2) sent had a higher Z value: */
1031
1032 return (((bit_type *) b2) -> z) - (((bit_type *) b1) -> z);
1033 }
1034
1035
1036 /* Draw all bits in the queue: */
1037
drawbits(void)1038 void drawbits(void)
1039 {
1040 int i;
1041 SDL_Rect src, dest;
1042
1043
1044 /* For each bit in the (by now, sorted) queue... */
1045
1046 for (i = 0; i < num_bits; i++)
1047 {
1048 /* If the distance is something we can convert into one of the
1049 * sprite images: */
1050
1051 if (bits[i].z >= 0 && bits[i].z < 900)
1052 {
1053 /* Draw the bit at it's X,Y coordinates (taking into account
1054 * the size and shape of the sprite itself... we want the
1055 * center of the sprite to appear at the calculated screen
1056 * coordinates (x,y) */
1057
1058 dest.x = bits[i].x - (src_width[8 - (bits[i].z / 100)] / 2);
1059 dest.y = bits[i].y - (src_width[8 - (bits[i].z / 100)] / 2);
1060
1061
1062 /* Within the surface (the loaded PNG image file), use the
1063 * appropriate shape: */
1064
1065 src.x = src_start[8 - (bits[i].z / 100)];
1066 src.y = 0;
1067
1068 src.w = src_width[8 - (bits[i].z / 100)];
1069 src.h = src_width[8 - (bits[i].z / 100)];
1070
1071
1072 /* (The width and heights of the destination (in 'screen' surface)
1073 * should match those of the source (from the loaded PNG)) */
1074
1075 dest.w = src.w;
1076 dest.h = src.h;
1077
1078
1079 /* Actually copy the sprite onto the window's surface! WHEW!!! */
1080
1081 SDL_BlitSurface(spark[bits[i].c], /* Copy from appropriately-colored
1082 image... */
1083 &src, /* .. the appropriate sprite. */
1084 screen, /* Draw it into the window... */
1085 &dest); /* .. at the appropriate place! */
1086 }
1087 }
1088 }
1089
1090
1091 /* Fade every pixel in the window (in the "screen" surface) */
1092 /* This func. is called instead of the screen-clearing SDL_FillRect */
1093 /* if "zoom" is set to 'TRUE'... */
1094
fadeout(void)1095 void fadeout(void)
1096 {
1097 int x, y;
1098 Uint8 r, g, b;
1099 Uint32 nr, ng, nb;
1100
1101
1102 /* Traverse the entire window: */
1103
1104 for (y = 0; y < F_HEIGHT; y++)
1105 {
1106 for (x = 0; x < F_WIDTH; x++)
1107 {
1108 /* Get the RGB values of the pixel: */
1109
1110 SDL_GetRGB(getpixel(screen, x, y), screen->format, &r, &g, &b);
1111
1112
1113 /* Darken it some */
1114
1115 nr = (r * 3) >> 2; /* == (r * 3) / 4 == r * 0.75 */
1116 ng = (g * 3) >> 2;
1117 nb = (b * 3) >> 2;
1118
1119
1120 /* Draw the new pixel: */
1121
1122 putpixel(screen, x, y,
1123 SDL_MapRGB(screen->format,
1124 (Uint8) nr,
1125 (Uint8) ng,
1126 (Uint8) nb));
1127 }
1128 }
1129 }
1130
1131
1132 /* Zoom the entire image in the window ("screen surface") out some */
1133 /* This func. is called instead of the screen-clearing SDL_FillRect */
1134 /* if "zoom" is set to 'ZOOMOUT'... */
1135
zoomout(int zoom_x,int zoom_y)1136 void zoomout(int zoom_x, int zoom_y)
1137 {
1138 int x, y;
1139 SDL_Surface * tmp;
1140
1141
1142 /* Create a temp. surface to draw onto: */
1143
1144 tmp = SDL_CreateRGBSurface(SDL_SWSURFACE, F_WIDTH, F_HEIGHT, screen->format->BitsPerPixel,
1145 0, 0, 0, 0);
1146
1147 if (tmp == NULL)
1148 {
1149 /* Couldn't create it!? */
1150
1151 printf("%s\n", SDL_GetError());
1152 exit(1);
1153 }
1154
1155
1156 /* Lock temp. surface if we need to (accessing raw pixels) */
1157
1158 if (SDL_MUSTLOCK(tmp))
1159 SDL_LockSurface(tmp);
1160
1161
1162 /* For every pixel in the window... */
1163
1164 for (y = 0; y < F_HEIGHT; y++)
1165 {
1166 for (x = 0; x < F_WIDTH; x++)
1167 {
1168 /* Draw a smaller version of the existing image (screen) into
1169 the temp. surface: */
1170
1171 putpixel(tmp, /* Drawing into temp. surface */
1172 ((((x - H_WIDTH) * 3) / 4) + /* X scaled by .75*/
1173 H_WIDTH + /* (re-centered) */
1174 zoom_x), /* and moved (zoom_x offset) */
1175 ((((y - H_HEIGHT) * 3) / 4) +
1176 H_HEIGHT + /* (ditto for Y) */
1177 zoom_y),
1178 getpixel(screen, x, y)); /* A pixel from "screen" */
1179 }
1180 }
1181
1182
1183 /* Unlock temp. surface: */
1184
1185 if (SDL_MUSTLOCK(tmp))
1186 SDL_UnlockSurface(tmp);
1187
1188
1189 /* Copy entire temp. surface into window: */
1190
1191 SDL_BlitSurface(tmp, NULL, screen, NULL);
1192
1193
1194 /* Free the temp. surface from memory, now that we're done with it: */
1195
1196 SDL_FreeSurface(tmp);
1197 }
1198
1199
1200 /* Zoom the entire image in the window ("screen surface") in some */
1201 /* This func. is called instead of the screen-clearing SDL_FillRect */
1202 /* if "zoom" has been set to 'ZOOMIN'... */
1203
zoomin(int zoom_x,int zoom_y)1204 void zoomin(int zoom_x, int zoom_y)
1205 {
1206 int x, y;
1207 Uint32 pix;
1208 SDL_Surface * tmp;
1209 int nx, ny;
1210
1211
1212 /* Create a temp. surface to draw onto: */
1213
1214 tmp = SDL_CreateRGBSurface(SDL_SWSURFACE, F_WIDTH, F_HEIGHT,
1215 screen->format->BitsPerPixel,
1216 0, 0, 0, 0);
1217
1218 if (tmp == NULL)
1219 {
1220 /* Couldn't create it!? */
1221
1222 printf("%s\n", SDL_GetError());
1223 exit(1);
1224 }
1225
1226
1227 /* Lock temp. surface if we need to (accessing raw pixels) */
1228
1229 if (SDL_MUSTLOCK(tmp))
1230 SDL_LockSurface(tmp);
1231
1232
1233 /* For every pixel in the window... */
1234
1235 for (y = 0; y < F_HEIGHT; y++)
1236 {
1237 for (x = 0; x < F_WIDTH; x++)
1238 {
1239 /* Get the pixel's color: */
1240
1241 pix = getpixel(screen, x, y);
1242
1243
1244 /* Calculate new X/Y location: */
1245
1246 nx = ((((x - H_WIDTH) * 4) / 3) + /* X scaled up by 125% */
1247 H_WIDTH + /* (re-centered) */
1248 zoom_x); /* and moved (zoom_x offset) */
1249
1250 ny = ((((y - H_HEIGHT) * 4) / 3) +
1251 H_HEIGHT + /* (Ditto for Y) */
1252 zoom_y);
1253
1254
1255 /* Draw the pixel (2x2 to fill in gaps) into temp. surface */
1256
1257 putpixel(tmp, nx + 0, ny + 0, pix);
1258 putpixel(tmp, nx + 1, ny + 0, pix);
1259 putpixel(tmp, nx + 0, ny + 1, pix);
1260 putpixel(tmp, nx + 1, ny + 1, pix);
1261 }
1262 }
1263
1264
1265 /* Unlock temp. surface: */
1266
1267 if (SDL_MUSTLOCK(tmp))
1268 SDL_UnlockSurface(tmp);
1269
1270
1271 /* Copy entire temp. surface into window: */
1272
1273 SDL_BlitSurface(tmp, NULL, screen, NULL);
1274
1275
1276 /* Free the temp. surface from memory, now that we're done with it: */
1277
1278 SDL_FreeSurface(tmp);
1279 }
1280
1281
1282 /* Rotate the entire image in the window ("screen surface") some angle */
1283 /* This func. is called instead of the screen-clearing SDL_FillRect */
1284 /* if "rotate" has been set to 'TRUE'... */
1285
rotatescreen(int angle)1286 void rotatescreen(int angle)
1287 {
1288 int x, y;
1289 Uint32 pix;
1290 SDL_Surface * tmp;
1291 int nx, ny;
1292 float rad, cos_r, sin_r;
1293
1294
1295 /* Create a temp. surface to draw onto: */
1296
1297 tmp = SDL_CreateRGBSurface(SDL_SWSURFACE, F_WIDTH, F_HEIGHT,
1298 screen->format->BitsPerPixel,
1299 0, 0, 0, 0);
1300
1301 if (tmp == NULL)
1302 {
1303 /* Couldn't create it!? */
1304
1305 printf("%s\n", SDL_GetError());
1306 exit(1);
1307 }
1308
1309
1310 /* Lock temp. surface if we need to (accessing raw pixels) */
1311
1312 if (SDL_MUSTLOCK(tmp))
1313 SDL_LockSurface(tmp);
1314
1315
1316 /* Convert rotation angle into radians: */
1317
1318 rad = - (3.14159 * angle) / 180;
1319
1320
1321 /* Get COSINE and SINE of the angle, for use later
1322 (rather than calculating the same value over and over!) */
1323
1324 cos_r = cos(rad);
1325 sin_r = sin(rad);
1326
1327
1328 /* For every pixel on the screen: */
1329
1330 for (y = 0; y < F_HEIGHT; y++)
1331 {
1332 for (x = 0; x < F_WIDTH; x++)
1333 {
1334 /* Get the pixel's color: */
1335
1336 pix = getpixel(screen, x, y);
1337
1338
1339 /* Calculate new X/Y location: */
1340
1341 nx = (((x - H_WIDTH) * cos_r) - /* Rotated around... */
1342 ((y - H_HEIGHT) * sin_r) + /* ...old pixel's position */
1343 H_WIDTH); /* (Re-centered) */
1344
1345 ny = (((x - H_WIDTH) * sin_r) +
1346 ((y - H_HEIGHT) * cos_r) + /* (Similar for Y) */
1347 H_HEIGHT);
1348
1349
1350 /* Draw the pixel (2x2 to fill in gaps) into temp. surface */
1351
1352 putpixel(tmp, nx + 0, ny + 0, pix);
1353 putpixel(tmp, nx + 1, ny + 0, pix);
1354 putpixel(tmp, nx + 0, ny + 1, pix);
1355 putpixel(tmp, nx + 1, ny + 1, pix);
1356 }
1357 }
1358
1359
1360 /* Unlock temp. surface: */
1361
1362 if (SDL_MUSTLOCK(tmp))
1363 SDL_UnlockSurface(tmp);
1364
1365
1366 /* Copy entire temp. surface into window: */
1367
1368 SDL_BlitSurface(tmp, NULL, screen, NULL);
1369
1370
1371 /* Free the temp. surface from memory, now that we're done with it: */
1372
1373 SDL_FreeSurface(tmp);
1374 }
1375
1376
1377 /* Blur the entire image in the window ("screen surface") */
1378 /* This func. is called instead of the screen-clearing SDL_FillRect */
1379 /* if "blur" has been set to 'TRUE'... */
1380
blurscreen(void)1381 void blurscreen(void)
1382 {
1383 int x, y;
1384 int xx, yy;
1385 Uint8 r, g, b;
1386 Uint32 nr, ng, nb; /* Bigger (Uint32) because we total many RGBs */
1387 SDL_Surface * tmp;
1388
1389
1390 /* Create a temp. surface to draw onto: */
1391
1392 tmp = SDL_CreateRGBSurface(SDL_SWSURFACE, F_WIDTH, F_HEIGHT,
1393 screen->format->BitsPerPixel,
1394 0, 0, 0, 0);
1395
1396 if (tmp == NULL)
1397 {
1398 /* Couldn't create it!? */
1399
1400 printf("%s\n", SDL_GetError());
1401 exit(1);
1402 }
1403
1404
1405 /* Lock temp. surface if we need to (accessing raw pixels) */
1406
1407 if (SDL_MUSTLOCK(tmp))
1408 SDL_LockSurface(tmp);
1409
1410
1411 /* For every pixel (excluding outer edges)... */
1412
1413 for (y = 1; y < F_HEIGHT - 1; y += 2)
1414 {
1415 for (x = 1; x < F_WIDTH - 1; x += 2)
1416 {
1417 /* Clear RGB color totals: */
1418
1419 nr = 0;
1420 ng = 0;
1421 nb = 0;
1422
1423
1424 /* For the 9 pixels surrounding this location... */
1425
1426 for (yy = -1; yy <= 1; yy++)
1427 {
1428 for (xx = -1; xx <= 1; xx++)
1429 {
1430 /* Get the RGB values for the pixel: */
1431
1432 SDL_GetRGB(getpixel(screen, x + xx, y + yy),
1433 screen->format, &r, &g, &b);
1434
1435
1436 /* Increase the RGB color totals: */
1437
1438 nr = nr + r;
1439 ng = ng + g;
1440 nb = nb + b;
1441 }
1442 }
1443
1444
1445 /* Calculate the average (total divided by how many) */
1446
1447 nr = nr >> 4;
1448 ng = ng >> 4;
1449 nb = nb >> 4;
1450
1451
1452 /* Draw the averaged pixel onto the temp. surface: */
1453
1454 for (yy = y; yy < y + 2; yy++)
1455 {
1456 for (xx = x; xx < x + 2; xx++)
1457 {
1458 putpixel(tmp, xx, yy,
1459 SDL_MapRGB(screen->format,
1460 (Uint8) nr,
1461 (Uint8) ng,
1462 (Uint8) nb));
1463 }
1464 }
1465 }
1466 }
1467
1468
1469 /* Unlock temp. surface: */
1470
1471 if (SDL_MUSTLOCK(tmp))
1472 SDL_UnlockSurface(tmp);
1473
1474
1475 /* Copy entire temp. surface into window: */
1476
1477 SDL_BlitSurface(tmp, NULL, screen, NULL);
1478
1479
1480 /* Free the temp. surface from memory, now that we're done with it: */
1481
1482 SDL_FreeSurface(tmp);
1483 }
1484
1485
1486 /* Dissolve the entire image in the window ("screen surface") some angle */
1487 /* This func. is called instead of the screen-clearing SDL_FillRect */
1488 /* if "dissolve" has been set to 'TRUE'... */
1489
dissolvescreen(int angle)1490 void dissolvescreen(int angle)
1491 {
1492 int x, y;
1493
1494
1495 /* For every pixel... */
1496
1497 for (y = 0; y < F_HEIGHT; y++)
1498 {
1499 for (x = 0; x < F_WIDTH; x++)
1500 {
1501 /* Based on random chance (pick a number between 0 and 7...
1502 is it less than 3?...) */
1503
1504 if ((rand() % 8) < 3)
1505 {
1506 /* (...if so) Set the pixel to black: */
1507
1508 putpixel(screen, x, y,
1509 SDL_MapRGB(screen->format, 0x00, 0x00, 0x00));
1510 }
1511 }
1512 }
1513 }
1514
1515
1516 /* Heat (blur with fire) the entire image in the window */
1517 /* ("screen surface") some angle */
1518 /* This func. is called instead of the screen-clearing SDL_FillRect */
1519 /* if "heat" has been set to 'TRUE'... */
1520
heatscreen(int angle)1521 void heatscreen(int angle)
1522 {
1523 int x, y;
1524 int xx, yy, xoff;
1525 Uint8 r, g, b;
1526 Uint32 nr, ng, nb;
1527 SDL_Surface * tmp;
1528
1529
1530 /* Create a temp. surface to draw onto: */
1531
1532 tmp = SDL_CreateRGBSurface(SDL_SWSURFACE, F_WIDTH, F_HEIGHT,
1533 screen->format->BitsPerPixel,
1534 0, 0, 0, 0);
1535
1536 if (tmp == NULL)
1537 {
1538 /* Couldn't create it!? */
1539
1540 printf("%s\n", SDL_GetError());
1541 exit(1);
1542 }
1543
1544
1545 /* Lock temp. surface if we need to (accessing raw pixels) */
1546
1547 if (SDL_MUSTLOCK(tmp))
1548 SDL_LockSurface(tmp);
1549
1550
1551 /* For every pixel except the outer edges: */
1552
1553 for (y = 1; y < F_HEIGHT - 1; y++)
1554 {
1555 /* Pick a random offset for this row (for 'wavering' effect of heat) */
1556
1557 xoff = (rand() % 3) - 1;
1558
1559 for (x = 1; x < F_WIDTH - 1; x++)
1560 {
1561 /* Clear RGB totals: */
1562
1563 nr = 0;
1564 ng = 0;
1565 nb = 0;
1566
1567
1568 /* For the lower-surrounding locations of this pixel: */
1569
1570 for (yy = 0; yy <= 1; yy++)
1571 {
1572 for (xx = xoff - 1; xx <= xoff + 1; xx++)
1573 {
1574 /* Get the RGB values for the pixel: */
1575
1576 SDL_GetRGB(getpixel(screen, x + xx, y + yy),
1577 screen->format, &r, &g, &b);
1578
1579
1580 /* Set the new colors: */
1581
1582 nr += (r + /* Red gets increased a little */
1583 ((g + b) / 4)); /* more by the current B & G */
1584 ng += g;
1585 nb += b;
1586 }
1587 }
1588
1589
1590 /* "Average" the totals: */
1591
1592 nr = nr / 6; /* Red is true average of total */
1593 ng = ng / 7; /* Green is slightly smaller */
1594 nb = nb >> 3; /* Blue is even smaller */
1595
1596 /* NOTE: The above causes the color to have much more red,
1597 and slightly more green (therefore orange/yellow) than
1598 if all values were simply truly averaged (divided by 6 each) */
1599
1600
1601 /* If the red is greater than max possible value,
1602 cut it off (so it doesn't wrap around, causing weird
1603 effects... brighter-than-bright would become dark!!!) */
1604
1605 if (nr >= 0xFF)
1606 nr = 0xFF;
1607
1608
1609 /* Draw the new, more red pixel onto the temp. surface: */
1610
1611 putpixel(tmp, x, y,
1612 SDL_MapRGB(screen->format,
1613 (Uint8) nr,
1614 (Uint8) ng,
1615 (Uint8) nb));
1616 }
1617 }
1618
1619
1620 /* Unlock temp. surface: */
1621
1622 if (SDL_MUSTLOCK(tmp))
1623 SDL_UnlockSurface(tmp);
1624
1625
1626 /* Copy entire temp. surface into window: */
1627
1628 SDL_BlitSurface(tmp, NULL, screen, NULL);
1629
1630
1631 /* Free the temp. surface from memory, now that we're done with it: */
1632
1633 SDL_FreeSurface(tmp);
1634 }
1635
1636
1637 /* Get a single pixel from a surface (to convert to R, G, B) */
1638
getpixel(SDL_Surface * surface,int x,int y)1639 Uint32 getpixel(SDL_Surface * surface, int x, int y)
1640 {
1641 int bpp;
1642 Uint8 * p;
1643
1644
1645 /* Determine bytes-per-pixel for the surface in question: */
1646
1647 bpp = surface->format->BytesPerPixel;
1648
1649
1650 /* Set a pointer to the exact location in memory of the pixel
1651 in question: */
1652
1653 p = (Uint8 *) (surface->pixels + /* Start at beginning of RAM */
1654 (y * surface->pitch) + /* Go down Y lines */
1655 (x * bpp)); /* Go in X pixels */
1656
1657
1658 /* Assuming the X/Y values are within the bounds of this surface... */
1659
1660 if (x >= 0 && y >= 0 && x < surface -> w && y < surface -> h)
1661 {
1662 /* Return the correctly-sized piece of data containing the
1663 pixel's value (an 8-bit palette value, or a 16-, 24- or 32-bit
1664 RGB value) */
1665
1666 if (bpp == 1) /* 8-bit display */
1667 return *p;
1668 else if (bpp == 2) /* 16-bit display */
1669 return *(Uint16 *)p;
1670 else if (bpp == 3) /* 24-bit display */
1671 {
1672 /* Depending on the byte-order, it could be stored RGB or BGR! */
1673
1674 if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
1675 return p[0] << 16 | p[1] << 8 | p[2];
1676 else
1677 return p[0] | p[1] << 8 | p[2] << 16;
1678 }
1679 else if (bpp == 4) /* 32-bit display */
1680 return *(Uint32 *)p;
1681 else
1682 return 0; /* (Should never occur) */
1683 }
1684 else
1685 return 0; /* (Out of bounds? Just return zero) */
1686 }
1687
1688
1689 /* Draw a single pixel into the surface: */
1690
putpixel(SDL_Surface * surface,int x,int y,Uint32 pixel)1691 void putpixel(SDL_Surface * surface, int x, int y, Uint32 pixel)
1692 {
1693 int bpp;
1694 Uint8 * p;
1695
1696 /* Determine bytes-per-pixel for the surface in question: */
1697
1698 bpp = surface->format->BytesPerPixel;
1699
1700
1701 /* Set a pointer to the exact location in memory of the pixel
1702 in question: */
1703
1704 p = (Uint8 *) (surface->pixels + /* Start at beginning of RAM */
1705 (y * surface->pitch) + /* Go down Y lines */
1706 (x * bpp)); /* Go in X pixels */
1707
1708
1709 /* Assuming the X/Y values are within the bounds of this surface... */
1710
1711 if (x >= 0 && y >= 0 && x < surface -> w && y < surface -> h)
1712 {
1713 /* Set the (correctly-sized) piece of data in the surface's RAM
1714 to the pixel value sent in: */
1715
1716 if (bpp == 1)
1717 *p = pixel;
1718 else if (bpp == 2)
1719 *(Uint16 *)p = pixel;
1720 else if (bpp == 3)
1721 {
1722 if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
1723 {
1724 p[0] = (pixel >> 16) & 0xff;
1725 p[1] = (pixel >> 8) & 0xff;
1726 p[2] = pixel & 0xff;
1727 }
1728 else
1729 {
1730 p[0] = pixel & 0xff;
1731 p[1] = (pixel >> 8) & 0xff;
1732 p[2] = (pixel >> 16) & 0xff;
1733 }
1734 }
1735 else if (bpp == 4)
1736 {
1737 *(Uint32 *)p = pixel;
1738 }
1739 }
1740 }
1741