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. */
16static void quit(int rc)
17{
18	SDL_Quit();
19	exit(rc);
20}
21
22/* Fill the screen with a gradient */
23static 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 */
63SDL_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
150static Uint32 flashes = 0;
151static Uint32 flashtime = 0;
152
153void 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
174static int sprite_visible = 0;
175static SDL_Surface *sprite;
176static SDL_Surface *backing;
177static SDL_Rect    position;
178static int         x_vel, y_vel;
179static int	   alpha_vel;
180
181int 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
241void AttractSprite(Uint16 x, Uint16 y)
242{
243	x_vel = ((int)x-position.x)/10;
244	y_vel = ((int)y-position.y)/10;
245}
246
247void 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
306void 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
322int 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
465fprintf(stderr, "Sleeping %d ticks\n", FRAME_TICKS-(ticks-lastticks));
466#endif
467			SDL_Delay(FRAME_TICKS-(ticks-lastticks));
468#ifdef CHECK_SLEEP_GRANULARITY
469fprintf(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#ifdef _WIN32_WCE
509					// there is no ESC key at all
510					done = 1;
511#else
512					if ( event.key.keysym.sym == SDLK_ESCAPE ) {
513						done = 1;
514					} else if (event.key.keysym.sym == SDLK_t) {
515						videoflags ^= SDL_FULLSCREEN;
516						screen = SDL_SetVideoMode(w, h, video_bpp, videoflags);
517						if ( screen == NULL ) {
518							fprintf(stderr, "Couldn't toggle video mode: %s\n",
519									SDL_GetError());
520							quit(2);
521						}
522						FillBackground(screen);
523					}
524#endif
525
526					break;
527				case SDL_QUIT:
528					done = 1;
529					break;
530				default:
531					break;
532			}
533		}
534	}
535	SDL_FreeSurface(light);
536	SDL_FreeSurface(sprite);
537	SDL_FreeSurface(backing);
538
539	/* Print out some timing information */
540	if ( flashes > 0 ) {
541		printf("%d alpha blits, ~%4.4f ms per blit\n",
542			flashes, (float)flashtime/flashes);
543	}
544
545	SDL_Quit();
546	return(0);
547}
548