test/testalpha.c
author Patrice Mandin <patmandin@gmail.com>
Thu, 19 Jan 2006 21:28:52 +0000
changeset 1257 448a9a64537b
parent 1152 51a8702d8ecd
child 1293 23b1ef7d003b
permissions -rw-r--r--
[PATCH] SDL_GetVideoMode() does not find best mode, part 2

Following commit 1.51, I come accross a problem when SDL must choose between
several video modes that could suit the one asked.

If I ask 320x240 with this list:
768x480 768x240 640x400 640x200 384x480 384x240 320x400 320x200

The smallest selectables modes are 384x240 and 320x400. And SDL choose the later
in this list, but 384x240 is more suitable. So I added a check to compare
the pixel count (surface) of modes, and select the one which has the smallest
pixel count.

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