IMG_bmp.c
changeset 101 5c27212dbfc6
parent 97 e1161bd417c4
child 106 65e8cd587119
     1.1 --- a/IMG_bmp.c	Sun May 16 18:16:58 2004 +0000
     1.2 +++ b/IMG_bmp.c	Sat Aug 21 13:25:56 2004 +0000
     1.3 @@ -46,10 +46,387 @@
     1.4  	return(is_BMP);
     1.5  }
     1.6  
     1.7 +#include "SDL_error.h"
     1.8 +#include "SDL_video.h"
     1.9 +#include "SDL_endian.h"
    1.10 +
    1.11 +/* Compression encodings for BMP files */
    1.12 +#ifndef BI_RGB
    1.13 +#define BI_RGB		0
    1.14 +#define BI_RLE8		1
    1.15 +#define BI_RLE4		2
    1.16 +#define BI_BITFIELDS	3
    1.17 +#endif
    1.18 +
    1.19 +static int readRlePixels(SDL_Surface * surface, SDL_RWops * src, int isRle8)
    1.20 +{
    1.21 +	/*
    1.22 +	| Sets the surface pixels from src.  A bmp image is upside down.
    1.23 +	*/
    1.24 +	int pitch = surface->pitch;
    1.25 +	int height = surface->h;
    1.26 +	Uint8 * bits = (Uint8 *)surface->pixels + ((height-1) * pitch);
    1.27 +	int ofs = 0;
    1.28 +	Uint8 ch;
    1.29 +	Uint8 needsPad;
    1.30 +
    1.31 +	for (;;) {
    1.32 +		if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
    1.33 +		/*
    1.34 +		| encoded mode starts with a run length, and then a byte
    1.35 +		| with two colour indexes to alternate between for the run
    1.36 +		*/
    1.37 +		if ( ch ) {
    1.38 +			Uint8 pixel;
    1.39 +			if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
    1.40 +			if ( isRle8 ) {                 /* 256-color bitmap, compressed */
    1.41 +				do {
    1.42 +					bits[ofs++] = pixel;
    1.43 +				} while (--ch);
    1.44 +			}else {                         /* 16-color bitmap, compressed */
    1.45 +				Uint8 pixel0 = pixel >> 4;
    1.46 +				Uint8 pixel1 = pixel & 0x0F;
    1.47 +				for (;;) {
    1.48 +					bits[ofs++] = pixel0;     /* even count, high nibble */
    1.49 +					if (!--ch) break;
    1.50 +					bits[ofs++] = pixel1;     /* odd count, low nibble */
    1.51 +					if (!--ch) break;
    1.52 +				}
    1.53 +			}
    1.54 +		} else {
    1.55 +			/*
    1.56 +			| A leading zero is an escape; it may signal the end of the bitmap,
    1.57 +			| a cursor move, or some absolute data.
    1.58 +			| zero tag may be absolute mode or an escape
    1.59 +			*/
    1.60 +			if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
    1.61 +			switch (ch) {
    1.62 +			case 0:                         /* end of line */
    1.63 +				ofs = 0;
    1.64 +				bits -= pitch;               /* go to previous */
    1.65 +				break;
    1.66 +			case 1:                         /* end of bitmap */
    1.67 +				return 0;                    /* success! */
    1.68 +			case 2:                         /* delta */
    1.69 +				if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
    1.70 +				ofs += ch;
    1.71 +				if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
    1.72 +				bits -= (ch * pitch);
    1.73 +				break;
    1.74 +			default:                        /* no compression */
    1.75 +				if (isRle8) {
    1.76 +					needsPad = ( ch & 1 );
    1.77 +					do {
    1.78 +						if ( !SDL_RWread(src, bits + ofs++, 1, 1) ) return 1;
    1.79 +					} while (--ch);
    1.80 +				} else {
    1.81 +					needsPad = ( ((ch+1)>>1) & 1 ); /* (ch+1)>>1: bytes size */
    1.82 +					for (;;) {
    1.83 +						Uint8 pixel;
    1.84 +						if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
    1.85 +						bits[ofs++] = pixel >> 4;
    1.86 +						if (!--ch) break;
    1.87 +						bits[ofs++] = pixel & 0x0F;
    1.88 +						if (!--ch) break;
    1.89 +					}
    1.90 +				}
    1.91 +				/* pad at even boundary */
    1.92 +				if ( needsPad && !SDL_RWread(src, &ch, 1, 1) ) return 1;
    1.93 +				break;
    1.94 +			}
    1.95 +		}
    1.96 +	}
    1.97 +}
    1.98 +
    1.99 +static SDL_Surface *LoadBMP_RW (SDL_RWops *src, int freesrc)
   1.100 +{
   1.101 +	int was_error;
   1.102 +	long fp_offset;
   1.103 +	int bmpPitch;
   1.104 +	int i, pad;
   1.105 +	SDL_Surface *surface;
   1.106 +	Uint32 Rmask;
   1.107 +	Uint32 Gmask;
   1.108 +	Uint32 Bmask;
   1.109 +	SDL_Palette *palette;
   1.110 +	Uint8 *bits;
   1.111 +	int ExpandBMP;
   1.112 +
   1.113 +	/* The Win32 BMP file header (14 bytes) */
   1.114 +	char   magic[2];
   1.115 +	Uint32 bfSize;
   1.116 +	Uint16 bfReserved1;
   1.117 +	Uint16 bfReserved2;
   1.118 +	Uint32 bfOffBits;
   1.119 +
   1.120 +	/* The Win32 BITMAPINFOHEADER struct (40 bytes) */
   1.121 +	Uint32 biSize;
   1.122 +	Sint32 biWidth;
   1.123 +	Sint32 biHeight;
   1.124 +	Uint16 biPlanes;
   1.125 +	Uint16 biBitCount;
   1.126 +	Uint32 biCompression;
   1.127 +	Uint32 biSizeImage;
   1.128 +	Sint32 biXPelsPerMeter;
   1.129 +	Sint32 biYPelsPerMeter;
   1.130 +	Uint32 biClrUsed;
   1.131 +	Uint32 biClrImportant;
   1.132 +
   1.133 +	/* Make sure we are passed a valid data source */
   1.134 +	surface = NULL;
   1.135 +	was_error = 0;
   1.136 +	if ( src == NULL ) {
   1.137 +		was_error = 1;
   1.138 +		goto done;
   1.139 +	}
   1.140 +
   1.141 +	/* Read in the BMP file header */
   1.142 +	fp_offset = SDL_RWtell(src);
   1.143 +	SDL_ClearError();
   1.144 +	if ( SDL_RWread(src, magic, 1, 2) != 2 ) {
   1.145 +		SDL_Error(SDL_EFREAD);
   1.146 +		was_error = 1;
   1.147 +		goto done;
   1.148 +	}
   1.149 +	if ( strncmp(magic, "BM", 2) != 0 ) {
   1.150 +		SDL_SetError("File is not a Windows BMP file");
   1.151 +		was_error = 1;
   1.152 +		goto done;
   1.153 +	}
   1.154 +	bfSize		= SDL_ReadLE32(src);
   1.155 +	bfReserved1	= SDL_ReadLE16(src);
   1.156 +	bfReserved2	= SDL_ReadLE16(src);
   1.157 +	bfOffBits	= SDL_ReadLE32(src);
   1.158 +
   1.159 +	/* Read the Win32 BITMAPINFOHEADER */
   1.160 +	biSize		= SDL_ReadLE32(src);
   1.161 +	if ( biSize == 12 ) {
   1.162 +		biWidth		= (Uint32)SDL_ReadLE16(src);
   1.163 +		biHeight	= (Uint32)SDL_ReadLE16(src);
   1.164 +		biPlanes	= SDL_ReadLE16(src);
   1.165 +		biBitCount	= SDL_ReadLE16(src);
   1.166 +		biCompression	= BI_RGB;
   1.167 +		biSizeImage	= 0;
   1.168 +		biXPelsPerMeter	= 0;
   1.169 +		biYPelsPerMeter	= 0;
   1.170 +		biClrUsed	= 0;
   1.171 +		biClrImportant	= 0;
   1.172 +	} else {
   1.173 +		biWidth		= SDL_ReadLE32(src);
   1.174 +		biHeight	= SDL_ReadLE32(src);
   1.175 +		biPlanes	= SDL_ReadLE16(src);
   1.176 +		biBitCount	= SDL_ReadLE16(src);
   1.177 +		biCompression	= SDL_ReadLE32(src);
   1.178 +		biSizeImage	= SDL_ReadLE32(src);
   1.179 +		biXPelsPerMeter	= SDL_ReadLE32(src);
   1.180 +		biYPelsPerMeter	= SDL_ReadLE32(src);
   1.181 +		biClrUsed	= SDL_ReadLE32(src);
   1.182 +		biClrImportant	= SDL_ReadLE32(src);
   1.183 +	}
   1.184 +
   1.185 +	/* Check for read error */
   1.186 +	if ( strcmp(SDL_GetError(), "") != 0 ) {
   1.187 +		was_error = 1;
   1.188 +		goto done;
   1.189 +	}
   1.190 +
   1.191 +	/* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
   1.192 +	switch (biBitCount) {
   1.193 +		case 1:
   1.194 +		case 4:
   1.195 +			ExpandBMP = biBitCount;
   1.196 +			biBitCount = 8;
   1.197 +			break;
   1.198 +		default:
   1.199 +			ExpandBMP = 0;
   1.200 +			break;
   1.201 +	}
   1.202 +
   1.203 +	/* RLE4 and RLE8 BMP compression is supported */
   1.204 +	Rmask = Gmask = Bmask = 0;
   1.205 +	switch (biCompression) {
   1.206 +		case BI_RGB:
   1.207 +			/* If there are no masks, use the defaults */
   1.208 +			if ( bfOffBits == (14+biSize) ) {
   1.209 +				/* Default values for the BMP format */
   1.210 +				switch (biBitCount) {
   1.211 +					case 15:
   1.212 +					case 16:
   1.213 +						Rmask = 0x7C00;
   1.214 +						Gmask = 0x03E0;
   1.215 +						Bmask = 0x001F;
   1.216 +						break;
   1.217 +					case 24:
   1.218 +#if SDL_BYTEORDER == SDL_BIG_ENDIAN
   1.219 +					        Rmask = 0x000000FF;
   1.220 +					        Gmask = 0x0000FF00;
   1.221 +					        Bmask = 0x00FF0000;
   1.222 +						break;
   1.223 +#endif
   1.224 +					case 32:
   1.225 +						Rmask = 0x00FF0000;
   1.226 +						Gmask = 0x0000FF00;
   1.227 +						Bmask = 0x000000FF;
   1.228 +						break;
   1.229 +					default:
   1.230 +						break;
   1.231 +				}
   1.232 +				break;
   1.233 +			}
   1.234 +			/* Fall through -- read the RGB masks */
   1.235 +
   1.236 +		default:
   1.237 +			switch (biBitCount) {
   1.238 +				case 15:
   1.239 +				case 16:
   1.240 +				case 32:
   1.241 +					Rmask = SDL_ReadLE32(src);
   1.242 +					Gmask = SDL_ReadLE32(src);
   1.243 +					Bmask = SDL_ReadLE32(src);
   1.244 +					break;
   1.245 +				default:
   1.246 +					break;
   1.247 +			}
   1.248 +			break;
   1.249 +	}
   1.250 +
   1.251 +	/* Create a compatible surface, note that the colors are RGB ordered */
   1.252 +	surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
   1.253 +			biWidth, biHeight, biBitCount, Rmask, Gmask, Bmask, 0);
   1.254 +	if ( surface == NULL ) {
   1.255 +		was_error = 1;
   1.256 +		goto done;
   1.257 +	}
   1.258 +
   1.259 +	/* Load the palette, if any */
   1.260 +	palette = (surface->format)->palette;
   1.261 +	if ( palette ) {
   1.262 +		/*
   1.263 +		| guich: always use 1<<bpp b/c some bitmaps can bring wrong information
   1.264 +		| for colorsUsed
   1.265 +		*/
   1.266 +		/* if ( biClrUsed == 0 ) {  */
   1.267 +		biClrUsed = 1 << biBitCount;
   1.268 +		/* } */
   1.269 +		if ( biSize == 12 ) {
   1.270 +			for ( i = 0; i < (int)biClrUsed; ++i ) {
   1.271 +				SDL_RWread(src, &palette->colors[i].b, 1, 1);
   1.272 +				SDL_RWread(src, &palette->colors[i].g, 1, 1);
   1.273 +				SDL_RWread(src, &palette->colors[i].r, 1, 1);
   1.274 +				palette->colors[i].unused = 0;
   1.275 +			}	
   1.276 +		} else {
   1.277 +			for ( i = 0; i < (int)biClrUsed; ++i ) {
   1.278 +				SDL_RWread(src, &palette->colors[i].b, 1, 1);
   1.279 +				SDL_RWread(src, &palette->colors[i].g, 1, 1);
   1.280 +				SDL_RWread(src, &palette->colors[i].r, 1, 1);
   1.281 +				SDL_RWread(src, &palette->colors[i].unused, 1, 1);
   1.282 +			}	
   1.283 +		}
   1.284 +		palette->ncolors = biClrUsed;
   1.285 +	}
   1.286 +
   1.287 +	/* Read the surface pixels.  Note that the bmp image is upside down */
   1.288 +	if ( SDL_RWseek(src, fp_offset+bfOffBits, SEEK_SET) < 0 ) {
   1.289 +		SDL_Error(SDL_EFSEEK);
   1.290 +		was_error = 1;
   1.291 +		goto done;
   1.292 +	}
   1.293 +	if ((biCompression == BI_RLE4) || (biCompression == BI_RLE8)) {
   1.294 +		was_error = readRlePixels(surface, src, biCompression == BI_RLE8);
   1.295 +		if (was_error) SDL_SetError("Error reading from BMP");
   1.296 +		goto done;
   1.297 +	}
   1.298 +	bits = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
   1.299 +	switch (ExpandBMP) {
   1.300 +		case 1:
   1.301 +			bmpPitch = (biWidth + 7) >> 3;
   1.302 +			pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
   1.303 +			break;
   1.304 +		case 4:
   1.305 +			bmpPitch = (biWidth + 1) >> 1;
   1.306 +			pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
   1.307 +			break;
   1.308 +		default:
   1.309 +			pad  = ((surface->pitch%4) ?
   1.310 +					(4-(surface->pitch%4)) : 0);
   1.311 +			break;
   1.312 +	}
   1.313 +	while ( bits > (Uint8 *)surface->pixels ) {
   1.314 +		bits -= surface->pitch;
   1.315 +		switch (ExpandBMP) {
   1.316 +			case 1:
   1.317 +			case 4: {
   1.318 +			Uint8 pixel = 0;
   1.319 +			int   shift = (8-ExpandBMP);
   1.320 +			for ( i=0; i<surface->w; ++i ) {
   1.321 +				if ( i%(8/ExpandBMP) == 0 ) {
   1.322 +					if ( !SDL_RWread(src, &pixel, 1, 1) ) {
   1.323 +						SDL_SetError(
   1.324 +					"Error reading from BMP");
   1.325 +						was_error = 1;
   1.326 +						goto done;
   1.327 +					}
   1.328 +				}
   1.329 +				*(bits+i) = (pixel>>shift);
   1.330 +				pixel <<= ExpandBMP;
   1.331 +			} }
   1.332 +			break;
   1.333 +
   1.334 +			default:
   1.335 +			if ( SDL_RWread(src, bits, 1, surface->pitch)
   1.336 +							 != surface->pitch ) {
   1.337 +				SDL_Error(SDL_EFREAD);
   1.338 +				was_error = 1;
   1.339 +				goto done;
   1.340 +			}
   1.341 +#if SDL_BYTEORDER == SDL_BIG_ENDIAN
   1.342 +			/* Byte-swap the pixels if needed. Note that the 24bpp
   1.343 +			   case has already been taken care of above. */
   1.344 +			switch(biBitCount) {
   1.345 +				case 15:
   1.346 +				case 16: {
   1.347 +				        Uint16 *pix = (Uint16 *)bits;
   1.348 +					for(i = 0; i < surface->w; i++)
   1.349 +					        pix[i] = SDL_Swap16(pix[i]);
   1.350 +					break;
   1.351 +				}
   1.352 +
   1.353 +				case 32: {
   1.354 +				        Uint32 *pix = (Uint32 *)bits;
   1.355 +					for(i = 0; i < surface->w; i++)
   1.356 +					        pix[i] = SDL_Swap32(pix[i]);
   1.357 +					break;
   1.358 +				}
   1.359 +			}
   1.360 +#endif
   1.361 +			break;
   1.362 +		}
   1.363 +		/* Skip padding bytes, ugh */
   1.364 +		if ( pad ) {
   1.365 +			Uint8 padbyte;
   1.366 +			for ( i=0; i<pad; ++i ) {
   1.367 +				SDL_RWread(src, &padbyte, 1, 1);
   1.368 +			}
   1.369 +		}
   1.370 +	}
   1.371 +done:
   1.372 +	if ( was_error ) {
   1.373 +		if ( surface ) {
   1.374 +			SDL_FreeSurface(surface);
   1.375 +		}
   1.376 +		surface = NULL;
   1.377 +	}
   1.378 +	if ( freesrc && src ) {
   1.379 +		SDL_RWclose(src);
   1.380 +	}
   1.381 +	return(surface);
   1.382 +}
   1.383 +
   1.384  /* Load a BMP type image from an SDL datasource */
   1.385  SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
   1.386  {
   1.387 -	return(SDL_LoadBMP_RW(src, 0));
   1.388 +	return(LoadBMP_RW(src, 0));
   1.389  }
   1.390  
   1.391  #else