Pierre G. Richard - Fri, 30 Jul 2004 11:13:11 +0000 (UTC)
authorSam Lantinga <slouken@libsdl.org>
Sat, 21 Aug 2004 13:25:56 +0000
changeset 1015c27212dbfc6
parent 100 3feaaa4104f6
child 102 88edbd7cc3f1
Pierre G. Richard - Fri, 30 Jul 2004 11:13:11 +0000 (UTC)
* Added support for RLE encoded BMP files
CHANGES
IMG_bmp.c
     1.1 --- a/CHANGES	Sun May 16 18:16:58 2004 +0000
     1.2 +++ b/CHANGES	Sat Aug 21 13:25:56 2004 +0000
     1.3 @@ -1,4 +1,6 @@
     1.4  1.2.4:
     1.5 +Pierre G. Richard - Fri, 30 Jul 2004 11:13:11 +0000 (UTC)
     1.6 + * Added support for RLE encoded BMP files
     1.7  Marc Le Douarain - Fri, 26 Dec 2003 18:23:42 +0100
     1.8   * Added EHB and HAM mode support to the ILBM loader
     1.9  Sam Lantinga - Wed Nov 19 00:23:44 PST 2003
     2.1 --- a/IMG_bmp.c	Sun May 16 18:16:58 2004 +0000
     2.2 +++ b/IMG_bmp.c	Sat Aug 21 13:25:56 2004 +0000
     2.3 @@ -46,10 +46,387 @@
     2.4  	return(is_BMP);
     2.5  }
     2.6  
     2.7 +#include "SDL_error.h"
     2.8 +#include "SDL_video.h"
     2.9 +#include "SDL_endian.h"
    2.10 +
    2.11 +/* Compression encodings for BMP files */
    2.12 +#ifndef BI_RGB
    2.13 +#define BI_RGB		0
    2.14 +#define BI_RLE8		1
    2.15 +#define BI_RLE4		2
    2.16 +#define BI_BITFIELDS	3
    2.17 +#endif
    2.18 +
    2.19 +static int readRlePixels(SDL_Surface * surface, SDL_RWops * src, int isRle8)
    2.20 +{
    2.21 +	/*
    2.22 +	| Sets the surface pixels from src.  A bmp image is upside down.
    2.23 +	*/
    2.24 +	int pitch = surface->pitch;
    2.25 +	int height = surface->h;
    2.26 +	Uint8 * bits = (Uint8 *)surface->pixels + ((height-1) * pitch);
    2.27 +	int ofs = 0;
    2.28 +	Uint8 ch;
    2.29 +	Uint8 needsPad;
    2.30 +
    2.31 +	for (;;) {
    2.32 +		if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
    2.33 +		/*
    2.34 +		| encoded mode starts with a run length, and then a byte
    2.35 +		| with two colour indexes to alternate between for the run
    2.36 +		*/
    2.37 +		if ( ch ) {
    2.38 +			Uint8 pixel;
    2.39 +			if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
    2.40 +			if ( isRle8 ) {                 /* 256-color bitmap, compressed */
    2.41 +				do {
    2.42 +					bits[ofs++] = pixel;
    2.43 +				} while (--ch);
    2.44 +			}else {                         /* 16-color bitmap, compressed */
    2.45 +				Uint8 pixel0 = pixel >> 4;
    2.46 +				Uint8 pixel1 = pixel & 0x0F;
    2.47 +				for (;;) {
    2.48 +					bits[ofs++] = pixel0;     /* even count, high nibble */
    2.49 +					if (!--ch) break;
    2.50 +					bits[ofs++] = pixel1;     /* odd count, low nibble */
    2.51 +					if (!--ch) break;
    2.52 +				}
    2.53 +			}
    2.54 +		} else {
    2.55 +			/*
    2.56 +			| A leading zero is an escape; it may signal the end of the bitmap,
    2.57 +			| a cursor move, or some absolute data.
    2.58 +			| zero tag may be absolute mode or an escape
    2.59 +			*/
    2.60 +			if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
    2.61 +			switch (ch) {
    2.62 +			case 0:                         /* end of line */
    2.63 +				ofs = 0;
    2.64 +				bits -= pitch;               /* go to previous */
    2.65 +				break;
    2.66 +			case 1:                         /* end of bitmap */
    2.67 +				return 0;                    /* success! */
    2.68 +			case 2:                         /* delta */
    2.69 +				if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
    2.70 +				ofs += ch;
    2.71 +				if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
    2.72 +				bits -= (ch * pitch);
    2.73 +				break;
    2.74 +			default:                        /* no compression */
    2.75 +				if (isRle8) {
    2.76 +					needsPad = ( ch & 1 );
    2.77 +					do {
    2.78 +						if ( !SDL_RWread(src, bits + ofs++, 1, 1) ) return 1;
    2.79 +					} while (--ch);
    2.80 +				} else {
    2.81 +					needsPad = ( ((ch+1)>>1) & 1 ); /* (ch+1)>>1: bytes size */
    2.82 +					for (;;) {
    2.83 +						Uint8 pixel;
    2.84 +						if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
    2.85 +						bits[ofs++] = pixel >> 4;
    2.86 +						if (!--ch) break;
    2.87 +						bits[ofs++] = pixel & 0x0F;
    2.88 +						if (!--ch) break;
    2.89 +					}
    2.90 +				}
    2.91 +				/* pad at even boundary */
    2.92 +				if ( needsPad && !SDL_RWread(src, &ch, 1, 1) ) return 1;
    2.93 +				break;
    2.94 +			}
    2.95 +		}
    2.96 +	}
    2.97 +}
    2.98 +
    2.99 +static SDL_Surface *LoadBMP_RW (SDL_RWops *src, int freesrc)
   2.100 +{
   2.101 +	int was_error;
   2.102 +	long fp_offset;
   2.103 +	int bmpPitch;
   2.104 +	int i, pad;
   2.105 +	SDL_Surface *surface;
   2.106 +	Uint32 Rmask;
   2.107 +	Uint32 Gmask;
   2.108 +	Uint32 Bmask;
   2.109 +	SDL_Palette *palette;
   2.110 +	Uint8 *bits;
   2.111 +	int ExpandBMP;
   2.112 +
   2.113 +	/* The Win32 BMP file header (14 bytes) */
   2.114 +	char   magic[2];
   2.115 +	Uint32 bfSize;
   2.116 +	Uint16 bfReserved1;
   2.117 +	Uint16 bfReserved2;
   2.118 +	Uint32 bfOffBits;
   2.119 +
   2.120 +	/* The Win32 BITMAPINFOHEADER struct (40 bytes) */
   2.121 +	Uint32 biSize;
   2.122 +	Sint32 biWidth;
   2.123 +	Sint32 biHeight;
   2.124 +	Uint16 biPlanes;
   2.125 +	Uint16 biBitCount;
   2.126 +	Uint32 biCompression;
   2.127 +	Uint32 biSizeImage;
   2.128 +	Sint32 biXPelsPerMeter;
   2.129 +	Sint32 biYPelsPerMeter;
   2.130 +	Uint32 biClrUsed;
   2.131 +	Uint32 biClrImportant;
   2.132 +
   2.133 +	/* Make sure we are passed a valid data source */
   2.134 +	surface = NULL;
   2.135 +	was_error = 0;
   2.136 +	if ( src == NULL ) {
   2.137 +		was_error = 1;
   2.138 +		goto done;
   2.139 +	}
   2.140 +
   2.141 +	/* Read in the BMP file header */
   2.142 +	fp_offset = SDL_RWtell(src);
   2.143 +	SDL_ClearError();
   2.144 +	if ( SDL_RWread(src, magic, 1, 2) != 2 ) {
   2.145 +		SDL_Error(SDL_EFREAD);
   2.146 +		was_error = 1;
   2.147 +		goto done;
   2.148 +	}
   2.149 +	if ( strncmp(magic, "BM", 2) != 0 ) {
   2.150 +		SDL_SetError("File is not a Windows BMP file");
   2.151 +		was_error = 1;
   2.152 +		goto done;
   2.153 +	}
   2.154 +	bfSize		= SDL_ReadLE32(src);
   2.155 +	bfReserved1	= SDL_ReadLE16(src);
   2.156 +	bfReserved2	= SDL_ReadLE16(src);
   2.157 +	bfOffBits	= SDL_ReadLE32(src);
   2.158 +
   2.159 +	/* Read the Win32 BITMAPINFOHEADER */
   2.160 +	biSize		= SDL_ReadLE32(src);
   2.161 +	if ( biSize == 12 ) {
   2.162 +		biWidth		= (Uint32)SDL_ReadLE16(src);
   2.163 +		biHeight	= (Uint32)SDL_ReadLE16(src);
   2.164 +		biPlanes	= SDL_ReadLE16(src);
   2.165 +		biBitCount	= SDL_ReadLE16(src);
   2.166 +		biCompression	= BI_RGB;
   2.167 +		biSizeImage	= 0;
   2.168 +		biXPelsPerMeter	= 0;
   2.169 +		biYPelsPerMeter	= 0;
   2.170 +		biClrUsed	= 0;
   2.171 +		biClrImportant	= 0;
   2.172 +	} else {
   2.173 +		biWidth		= SDL_ReadLE32(src);
   2.174 +		biHeight	= SDL_ReadLE32(src);
   2.175 +		biPlanes	= SDL_ReadLE16(src);
   2.176 +		biBitCount	= SDL_ReadLE16(src);
   2.177 +		biCompression	= SDL_ReadLE32(src);
   2.178 +		biSizeImage	= SDL_ReadLE32(src);
   2.179 +		biXPelsPerMeter	= SDL_ReadLE32(src);
   2.180 +		biYPelsPerMeter	= SDL_ReadLE32(src);
   2.181 +		biClrUsed	= SDL_ReadLE32(src);
   2.182 +		biClrImportant	= SDL_ReadLE32(src);
   2.183 +	}
   2.184 +
   2.185 +	/* Check for read error */
   2.186 +	if ( strcmp(SDL_GetError(), "") != 0 ) {
   2.187 +		was_error = 1;
   2.188 +		goto done;
   2.189 +	}
   2.190 +
   2.191 +	/* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
   2.192 +	switch (biBitCount) {
   2.193 +		case 1:
   2.194 +		case 4:
   2.195 +			ExpandBMP = biBitCount;
   2.196 +			biBitCount = 8;
   2.197 +			break;
   2.198 +		default:
   2.199 +			ExpandBMP = 0;
   2.200 +			break;
   2.201 +	}
   2.202 +
   2.203 +	/* RLE4 and RLE8 BMP compression is supported */
   2.204 +	Rmask = Gmask = Bmask = 0;
   2.205 +	switch (biCompression) {
   2.206 +		case BI_RGB:
   2.207 +			/* If there are no masks, use the defaults */
   2.208 +			if ( bfOffBits == (14+biSize) ) {
   2.209 +				/* Default values for the BMP format */
   2.210 +				switch (biBitCount) {
   2.211 +					case 15:
   2.212 +					case 16:
   2.213 +						Rmask = 0x7C00;
   2.214 +						Gmask = 0x03E0;
   2.215 +						Bmask = 0x001F;
   2.216 +						break;
   2.217 +					case 24:
   2.218 +#if SDL_BYTEORDER == SDL_BIG_ENDIAN
   2.219 +					        Rmask = 0x000000FF;
   2.220 +					        Gmask = 0x0000FF00;
   2.221 +					        Bmask = 0x00FF0000;
   2.222 +						break;
   2.223 +#endif
   2.224 +					case 32:
   2.225 +						Rmask = 0x00FF0000;
   2.226 +						Gmask = 0x0000FF00;
   2.227 +						Bmask = 0x000000FF;
   2.228 +						break;
   2.229 +					default:
   2.230 +						break;
   2.231 +				}
   2.232 +				break;
   2.233 +			}
   2.234 +			/* Fall through -- read the RGB masks */
   2.235 +
   2.236 +		default:
   2.237 +			switch (biBitCount) {
   2.238 +				case 15:
   2.239 +				case 16:
   2.240 +				case 32:
   2.241 +					Rmask = SDL_ReadLE32(src);
   2.242 +					Gmask = SDL_ReadLE32(src);
   2.243 +					Bmask = SDL_ReadLE32(src);
   2.244 +					break;
   2.245 +				default:
   2.246 +					break;
   2.247 +			}
   2.248 +			break;
   2.249 +	}
   2.250 +
   2.251 +	/* Create a compatible surface, note that the colors are RGB ordered */
   2.252 +	surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
   2.253 +			biWidth, biHeight, biBitCount, Rmask, Gmask, Bmask, 0);
   2.254 +	if ( surface == NULL ) {
   2.255 +		was_error = 1;
   2.256 +		goto done;
   2.257 +	}
   2.258 +
   2.259 +	/* Load the palette, if any */
   2.260 +	palette = (surface->format)->palette;
   2.261 +	if ( palette ) {
   2.262 +		/*
   2.263 +		| guich: always use 1<<bpp b/c some bitmaps can bring wrong information
   2.264 +		| for colorsUsed
   2.265 +		*/
   2.266 +		/* if ( biClrUsed == 0 ) {  */
   2.267 +		biClrUsed = 1 << biBitCount;
   2.268 +		/* } */
   2.269 +		if ( biSize == 12 ) {
   2.270 +			for ( i = 0; i < (int)biClrUsed; ++i ) {
   2.271 +				SDL_RWread(src, &palette->colors[i].b, 1, 1);
   2.272 +				SDL_RWread(src, &palette->colors[i].g, 1, 1);
   2.273 +				SDL_RWread(src, &palette->colors[i].r, 1, 1);
   2.274 +				palette->colors[i].unused = 0;
   2.275 +			}	
   2.276 +		} else {
   2.277 +			for ( i = 0; i < (int)biClrUsed; ++i ) {
   2.278 +				SDL_RWread(src, &palette->colors[i].b, 1, 1);
   2.279 +				SDL_RWread(src, &palette->colors[i].g, 1, 1);
   2.280 +				SDL_RWread(src, &palette->colors[i].r, 1, 1);
   2.281 +				SDL_RWread(src, &palette->colors[i].unused, 1, 1);
   2.282 +			}	
   2.283 +		}
   2.284 +		palette->ncolors = biClrUsed;
   2.285 +	}
   2.286 +
   2.287 +	/* Read the surface pixels.  Note that the bmp image is upside down */
   2.288 +	if ( SDL_RWseek(src, fp_offset+bfOffBits, SEEK_SET) < 0 ) {
   2.289 +		SDL_Error(SDL_EFSEEK);
   2.290 +		was_error = 1;
   2.291 +		goto done;
   2.292 +	}
   2.293 +	if ((biCompression == BI_RLE4) || (biCompression == BI_RLE8)) {
   2.294 +		was_error = readRlePixels(surface, src, biCompression == BI_RLE8);
   2.295 +		if (was_error) SDL_SetError("Error reading from BMP");
   2.296 +		goto done;
   2.297 +	}
   2.298 +	bits = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
   2.299 +	switch (ExpandBMP) {
   2.300 +		case 1:
   2.301 +			bmpPitch = (biWidth + 7) >> 3;
   2.302 +			pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
   2.303 +			break;
   2.304 +		case 4:
   2.305 +			bmpPitch = (biWidth + 1) >> 1;
   2.306 +			pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
   2.307 +			break;
   2.308 +		default:
   2.309 +			pad  = ((surface->pitch%4) ?
   2.310 +					(4-(surface->pitch%4)) : 0);
   2.311 +			break;
   2.312 +	}
   2.313 +	while ( bits > (Uint8 *)surface->pixels ) {
   2.314 +		bits -= surface->pitch;
   2.315 +		switch (ExpandBMP) {
   2.316 +			case 1:
   2.317 +			case 4: {
   2.318 +			Uint8 pixel = 0;
   2.319 +			int   shift = (8-ExpandBMP);
   2.320 +			for ( i=0; i<surface->w; ++i ) {
   2.321 +				if ( i%(8/ExpandBMP) == 0 ) {
   2.322 +					if ( !SDL_RWread(src, &pixel, 1, 1) ) {
   2.323 +						SDL_SetError(
   2.324 +					"Error reading from BMP");
   2.325 +						was_error = 1;
   2.326 +						goto done;
   2.327 +					}
   2.328 +				}
   2.329 +				*(bits+i) = (pixel>>shift);
   2.330 +				pixel <<= ExpandBMP;
   2.331 +			} }
   2.332 +			break;
   2.333 +
   2.334 +			default:
   2.335 +			if ( SDL_RWread(src, bits, 1, surface->pitch)
   2.336 +							 != surface->pitch ) {
   2.337 +				SDL_Error(SDL_EFREAD);
   2.338 +				was_error = 1;
   2.339 +				goto done;
   2.340 +			}
   2.341 +#if SDL_BYTEORDER == SDL_BIG_ENDIAN
   2.342 +			/* Byte-swap the pixels if needed. Note that the 24bpp
   2.343 +			   case has already been taken care of above. */
   2.344 +			switch(biBitCount) {
   2.345 +				case 15:
   2.346 +				case 16: {
   2.347 +				        Uint16 *pix = (Uint16 *)bits;
   2.348 +					for(i = 0; i < surface->w; i++)
   2.349 +					        pix[i] = SDL_Swap16(pix[i]);
   2.350 +					break;
   2.351 +				}
   2.352 +
   2.353 +				case 32: {
   2.354 +				        Uint32 *pix = (Uint32 *)bits;
   2.355 +					for(i = 0; i < surface->w; i++)
   2.356 +					        pix[i] = SDL_Swap32(pix[i]);
   2.357 +					break;
   2.358 +				}
   2.359 +			}
   2.360 +#endif
   2.361 +			break;
   2.362 +		}
   2.363 +		/* Skip padding bytes, ugh */
   2.364 +		if ( pad ) {
   2.365 +			Uint8 padbyte;
   2.366 +			for ( i=0; i<pad; ++i ) {
   2.367 +				SDL_RWread(src, &padbyte, 1, 1);
   2.368 +			}
   2.369 +		}
   2.370 +	}
   2.371 +done:
   2.372 +	if ( was_error ) {
   2.373 +		if ( surface ) {
   2.374 +			SDL_FreeSurface(surface);
   2.375 +		}
   2.376 +		surface = NULL;
   2.377 +	}
   2.378 +	if ( freesrc && src ) {
   2.379 +		SDL_RWclose(src);
   2.380 +	}
   2.381 +	return(surface);
   2.382 +}
   2.383 +
   2.384  /* Load a BMP type image from an SDL datasource */
   2.385  SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
   2.386  {
   2.387 -	return(SDL_LoadBMP_RW(src, 0));
   2.388 +	return(LoadBMP_RW(src, 0));
   2.389  }
   2.390  
   2.391  #else