IMG_bmp.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 26 Sep 2009 06:44:08 +0000
changeset 202 8e1d88c68bda
parent 189 39d5ac4ec0f8
child 236 cce1251de477
permissions -rw-r--r--
Fixed crash with right side up BMP files
     1 /*
     2     SDL_image:  An example image loading library for use with SDL
     3     Copyright (C) 1997-2009 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 /* This is a BMP image file loading framework */
    24 /* ICO/CUR file support is here as well since it uses similar internal
    25  * representation */
    26 
    27 #include <stdio.h>
    28 #include <string.h>
    29 
    30 #include "SDL_image.h"
    31 
    32 #ifdef LOAD_BMP
    33 
    34 /* See if an image is contained in a data source */
    35 int IMG_isBMP(SDL_RWops *src)
    36 {
    37 	int start;
    38 	int is_BMP;
    39 	char magic[2];
    40 
    41 	if ( !src )
    42 		return 0;
    43 	start = SDL_RWtell(src);
    44 	is_BMP = 0;
    45 	if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
    46 		if ( strncmp(magic, "BM", 2) == 0 ) {
    47 			is_BMP = 1;
    48 		}
    49 	}
    50 	SDL_RWseek(src, start, SEEK_SET);
    51 	return(is_BMP);
    52 }
    53 
    54 static int IMG_isICOCUR(SDL_RWops *src, int type)
    55 {
    56 	int start;
    57 	int is_ICOCUR;
    58 
    59 	/* The Win32 ICO file header (14 bytes) */
    60     Uint16 bfReserved;
    61     Uint16 bfType;
    62     Uint16 bfCount;
    63 
    64 	if ( !src )
    65 		return 0;
    66 	start = SDL_RWtell(src);
    67 	is_ICOCUR = 0;
    68     bfReserved = SDL_ReadLE16(src);
    69     bfType = SDL_ReadLE16(src);
    70     bfCount = SDL_ReadLE16(src);
    71     if ((bfReserved == 0) && (bfType == type) && (bfCount != 0)) 
    72     	is_ICOCUR = 1;
    73 	SDL_RWseek(src, start, SEEK_SET);
    74 
    75 	return (is_ICOCUR);
    76 }
    77 
    78 int IMG_isICO(SDL_RWops *src)
    79 {
    80 	return IMG_isICOCUR(src, 1);
    81 }
    82 
    83 int IMG_isCUR(SDL_RWops *src)
    84 {
    85 	return IMG_isICOCUR(src, 2);
    86 }
    87 
    88 #include "SDL_error.h"
    89 #include "SDL_video.h"
    90 #include "SDL_endian.h"
    91 
    92 /* Compression encodings for BMP files */
    93 #ifndef BI_RGB
    94 #define BI_RGB		0
    95 #define BI_RLE8		1
    96 #define BI_RLE4		2
    97 #define BI_BITFIELDS	3
    98 #endif
    99 
   100 static int readRlePixels(SDL_Surface * surface, SDL_RWops * src, int isRle8)
   101 {
   102 	/*
   103 	| Sets the surface pixels from src.  A bmp image is upside down.
   104 	*/
   105 	int pitch = surface->pitch;
   106 	int height = surface->h;
   107 	Uint8 *start = (Uint8 *)surface->pixels;
   108 	Uint8 *end = start + (height*pitch);
   109 	Uint8 *bits = end-pitch, *spot;
   110 	int ofs = 0;
   111 	Uint8 ch;
   112 	Uint8 needsPad;
   113 
   114 #define COPY_PIXEL(x)	spot = &bits[ofs++]; if(spot >= start && spot < end) *spot = (x)
   115 
   116 	for (;;) {
   117 		if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
   118 		/*
   119 		| encoded mode starts with a run length, and then a byte
   120 		| with two colour indexes to alternate between for the run
   121 		*/
   122 		if ( ch ) {
   123 			Uint8 pixel;
   124 			if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
   125 			if ( isRle8 ) {                 /* 256-color bitmap, compressed */
   126 				do {
   127 					COPY_PIXEL(pixel);
   128 				} while (--ch);
   129 			} else {                         /* 16-color bitmap, compressed */
   130 				Uint8 pixel0 = pixel >> 4;
   131 				Uint8 pixel1 = pixel & 0x0F;
   132 				for (;;) {
   133 					COPY_PIXEL(pixel0);	/* even count, high nibble */
   134 					if (!--ch) break;
   135 					COPY_PIXEL(pixel1);	/* odd count, low nibble */
   136 					if (!--ch) break;
   137 				}
   138 			}
   139 		} else {
   140 			/*
   141 			| A leading zero is an escape; it may signal the end of the bitmap,
   142 			| a cursor move, or some absolute data.
   143 			| zero tag may be absolute mode or an escape
   144 			*/
   145 			if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
   146 			switch (ch) {
   147 			case 0:                         /* end of line */
   148 				ofs = 0;
   149 				bits -= pitch;               /* go to previous */
   150 				break;
   151 			case 1:                         /* end of bitmap */
   152 				return 0;                    /* success! */
   153 			case 2:                         /* delta */
   154 				if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
   155 				ofs += ch;
   156 				if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
   157 				bits -= (ch * pitch);
   158 				break;
   159 			default:                        /* no compression */
   160 				if (isRle8) {
   161 					needsPad = ( ch & 1 );
   162 					do {
   163 						Uint8 pixel;
   164 						if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
   165 						COPY_PIXEL(pixel);
   166 					} while (--ch);
   167 				} else {
   168 					needsPad = ( ((ch+1)>>1) & 1 ); /* (ch+1)>>1: bytes size */
   169 					for (;;) {
   170 						Uint8 pixel;
   171 						if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
   172 						COPY_PIXEL(pixel >> 4);
   173 						if (!--ch) break;
   174 						COPY_PIXEL(pixel & 0x0F);
   175 						if (!--ch) break;
   176 					}
   177 				}
   178 				/* pad at even boundary */
   179 				if ( needsPad && !SDL_RWread(src, &ch, 1, 1) ) return 1;
   180 				break;
   181 			}
   182 		}
   183 	}
   184 }
   185 
   186 static SDL_Surface *LoadBMP_RW (SDL_RWops *src, int freesrc)
   187 {
   188 	SDL_bool was_error;
   189 	long fp_offset;
   190 	int bmpPitch;
   191 	int i, pad;
   192 	SDL_Surface *surface;
   193 	Uint32 Rmask;
   194 	Uint32 Gmask;
   195 	Uint32 Bmask;
   196 	Uint32 Amask;
   197 	SDL_Palette *palette;
   198 	Uint8 *bits;
   199 	Uint8 *top, *end;
   200 	SDL_bool topDown;
   201 	int ExpandBMP;
   202 
   203 	/* The Win32 BMP file header (14 bytes) */
   204 	char   magic[2];
   205 	Uint32 bfSize;
   206 	Uint16 bfReserved1;
   207 	Uint16 bfReserved2;
   208 	Uint32 bfOffBits;
   209 
   210 	/* The Win32 BITMAPINFOHEADER struct (40 bytes) */
   211 	Uint32 biSize;
   212 	Sint32 biWidth;
   213 	Sint32 biHeight;
   214 	Uint16 biPlanes;
   215 	Uint16 biBitCount;
   216 	Uint32 biCompression;
   217 	Uint32 biSizeImage;
   218 	Sint32 biXPelsPerMeter;
   219 	Sint32 biYPelsPerMeter;
   220 	Uint32 biClrUsed;
   221 	Uint32 biClrImportant;
   222 
   223 	/* Make sure we are passed a valid data source */
   224 	surface = NULL;
   225 	was_error = SDL_FALSE;
   226 	if ( src == NULL ) {
   227 		was_error = SDL_TRUE;
   228 		goto done;
   229 	}
   230 
   231 	/* Read in the BMP file header */
   232 	fp_offset = SDL_RWtell(src);
   233 	SDL_ClearError();
   234 	if ( SDL_RWread(src, magic, 1, 2) != 2 ) {
   235 		SDL_Error(SDL_EFREAD);
   236 		was_error = SDL_TRUE;
   237 		goto done;
   238 	}
   239 	if ( strncmp(magic, "BM", 2) != 0 ) {
   240 		SDL_SetError("File is not a Windows BMP file");
   241 		was_error = SDL_TRUE;
   242 		goto done;
   243 	}
   244 	bfSize		= SDL_ReadLE32(src);
   245 	bfReserved1	= SDL_ReadLE16(src);
   246 	bfReserved2	= SDL_ReadLE16(src);
   247 	bfOffBits	= SDL_ReadLE32(src);
   248 
   249 	/* Read the Win32 BITMAPINFOHEADER */
   250 	biSize		= SDL_ReadLE32(src);
   251 	if ( biSize == 12 ) {
   252 		biWidth		= (Uint32)SDL_ReadLE16(src);
   253 		biHeight	= (Uint32)SDL_ReadLE16(src);
   254 		biPlanes	= SDL_ReadLE16(src);
   255 		biBitCount	= SDL_ReadLE16(src);
   256 		biCompression	= BI_RGB;
   257 		biSizeImage	= 0;
   258 		biXPelsPerMeter	= 0;
   259 		biYPelsPerMeter	= 0;
   260 		biClrUsed	= 0;
   261 		biClrImportant	= 0;
   262 	} else {
   263 		biWidth		= SDL_ReadLE32(src);
   264 		biHeight	= SDL_ReadLE32(src);
   265 		biPlanes	= SDL_ReadLE16(src);
   266 		biBitCount	= SDL_ReadLE16(src);
   267 		biCompression	= SDL_ReadLE32(src);
   268 		biSizeImage	= SDL_ReadLE32(src);
   269 		biXPelsPerMeter	= SDL_ReadLE32(src);
   270 		biYPelsPerMeter	= SDL_ReadLE32(src);
   271 		biClrUsed	= SDL_ReadLE32(src);
   272 		biClrImportant	= SDL_ReadLE32(src);
   273 	}
   274 	if (biHeight < 0) {
   275 		topDown = SDL_TRUE;
   276 		biHeight = -biHeight;
   277 	} else {
   278 		topDown = SDL_FALSE;
   279 	}
   280 
   281 	/* Check for read error */
   282 	if ( strcmp(SDL_GetError(), "") != 0 ) {
   283 		was_error = SDL_TRUE;
   284 		goto done;
   285 	}
   286 
   287 	/* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
   288 	switch (biBitCount) {
   289 		case 1:
   290 		case 4:
   291 			ExpandBMP = biBitCount;
   292 			biBitCount = 8;
   293 			break;
   294 		default:
   295 			ExpandBMP = 0;
   296 			break;
   297 	}
   298 
   299 	/* RLE4 and RLE8 BMP compression is supported */
   300 	Rmask = Gmask = Bmask = Amask = 0;
   301 	switch (biCompression) {
   302 		case BI_RGB:
   303 			/* If there are no masks, use the defaults */
   304 			if ( bfOffBits == (14+biSize) ) {
   305 				/* Default values for the BMP format */
   306 				switch (biBitCount) {
   307 					case 15:
   308 					case 16:
   309 						Rmask = 0x7C00;
   310 						Gmask = 0x03E0;
   311 						Bmask = 0x001F;
   312 						break;
   313 					case 24:
   314 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   315 					        Rmask = 0x000000FF;
   316 					        Gmask = 0x0000FF00;
   317 					        Bmask = 0x00FF0000;
   318 #else
   319 						Rmask = 0x00FF0000;
   320 						Gmask = 0x0000FF00;
   321 						Bmask = 0x000000FF;
   322 #endif
   323 						break;
   324 					case 32:
   325 						Amask = 0xFF000000;
   326 						Rmask = 0x00FF0000;
   327 						Gmask = 0x0000FF00;
   328 						Bmask = 0x000000FF;
   329 						break;
   330 					default:
   331 						break;
   332 				}
   333 				break;
   334 			}
   335 			/* Fall through -- read the RGB masks */
   336 
   337 		default:
   338 			switch (biBitCount) {
   339 				case 15:
   340 				case 16:
   341 				case 32:
   342 					Rmask = SDL_ReadLE32(src);
   343 					Gmask = SDL_ReadLE32(src);
   344 					Bmask = SDL_ReadLE32(src);
   345 					Amask = SDL_ReadLE32(src);
   346 					break;
   347 				default:
   348 					break;
   349 			}
   350 			break;
   351 	}
   352 
   353 	/* Create a compatible surface, note that the colors are RGB ordered */
   354 	surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
   355 			biWidth, biHeight, biBitCount, Rmask, Gmask, Bmask, Amask);
   356 	if ( surface == NULL ) {
   357 		was_error = SDL_TRUE;
   358 		goto done;
   359 	}
   360 
   361 	/* Load the palette, if any */
   362 	palette = (surface->format)->palette;
   363 	if ( palette ) {
   364 		if ( SDL_RWseek(src, fp_offset+14+biSize, SEEK_SET) < 0 ) {
   365 			SDL_Error(SDL_EFSEEK);
   366 			was_error = SDL_TRUE;
   367 			goto done;
   368 		}
   369 
   370 		/*
   371 		| guich: always use 1<<bpp b/c some bitmaps can bring wrong information
   372 		| for colorsUsed
   373 		*/
   374 		/* if ( biClrUsed == 0 ) {  */
   375 		biClrUsed = 1 << biBitCount;
   376 		/* } */
   377 		if ( biSize == 12 ) {
   378 			for ( i = 0; i < (int)biClrUsed; ++i ) {
   379 				SDL_RWread(src, &palette->colors[i].b, 1, 1);
   380 				SDL_RWread(src, &palette->colors[i].g, 1, 1);
   381 				SDL_RWread(src, &palette->colors[i].r, 1, 1);
   382 				palette->colors[i].unused = 0;
   383 			}	
   384 		} else {
   385 			for ( i = 0; i < (int)biClrUsed; ++i ) {
   386 				SDL_RWread(src, &palette->colors[i].b, 1, 1);
   387 				SDL_RWread(src, &palette->colors[i].g, 1, 1);
   388 				SDL_RWread(src, &palette->colors[i].r, 1, 1);
   389 				SDL_RWread(src, &palette->colors[i].unused, 1, 1);
   390 			}	
   391 		}
   392 		palette->ncolors = biClrUsed;
   393 	}
   394 
   395 	/* Read the surface pixels.  Note that the bmp image is upside down */
   396 	if ( SDL_RWseek(src, fp_offset+bfOffBits, SEEK_SET) < 0 ) {
   397 		SDL_Error(SDL_EFSEEK);
   398 		was_error = SDL_TRUE;
   399 		goto done;
   400 	}
   401 	if ((biCompression == BI_RLE4) || (biCompression == BI_RLE8)) {
   402 		was_error = readRlePixels(surface, src, biCompression == BI_RLE8);
   403 		if (was_error) SDL_SetError("Error reading from BMP");
   404 		goto done;
   405 	}
   406 	top = (Uint8 *)surface->pixels;
   407 	end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
   408 	switch (ExpandBMP) {
   409 		case 1:
   410 			bmpPitch = (biWidth + 7) >> 3;
   411 			pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
   412 			break;
   413 		case 4:
   414 			bmpPitch = (biWidth + 1) >> 1;
   415 			pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
   416 			break;
   417 		default:
   418 			pad  = ((surface->pitch%4) ?
   419 					(4-(surface->pitch%4)) : 0);
   420 			break;
   421 	}
   422 	if ( topDown ) {
   423 		bits = top;
   424 	} else {
   425 		bits = end - surface->pitch;
   426 	}
   427 	while ( bits >= top && bits < end ) {
   428 		switch (ExpandBMP) {
   429 			case 1:
   430 			case 4: {
   431 			Uint8 pixel = 0;
   432 			int   shift = (8-ExpandBMP);
   433 			for ( i=0; i<surface->w; ++i ) {
   434 				if ( i%(8/ExpandBMP) == 0 ) {
   435 					if ( !SDL_RWread(src, &pixel, 1, 1) ) {
   436 						SDL_SetError(
   437 					"Error reading from BMP");
   438 						was_error = SDL_TRUE;
   439 						goto done;
   440 					}
   441 				}
   442 				*(bits+i) = (pixel>>shift);
   443 				pixel <<= ExpandBMP;
   444 			} }
   445 			break;
   446 
   447 			default:
   448 			if ( SDL_RWread(src, bits, 1, surface->pitch)
   449 							 != surface->pitch ) {
   450 				SDL_Error(SDL_EFREAD);
   451 				was_error = SDL_TRUE;
   452 				goto done;
   453 			}
   454 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   455 			/* Byte-swap the pixels if needed. Note that the 24bpp
   456 			   case has already been taken care of above. */
   457 			switch(biBitCount) {
   458 				case 15:
   459 				case 16: {
   460 				        Uint16 *pix = (Uint16 *)bits;
   461 					for(i = 0; i < surface->w; i++)
   462 					        pix[i] = SDL_Swap16(pix[i]);
   463 					break;
   464 				}
   465 
   466 				case 32: {
   467 				        Uint32 *pix = (Uint32 *)bits;
   468 					for(i = 0; i < surface->w; i++)
   469 					        pix[i] = SDL_Swap32(pix[i]);
   470 					break;
   471 				}
   472 			}
   473 #endif
   474 			break;
   475 		}
   476 		/* Skip padding bytes, ugh */
   477 		if ( pad ) {
   478 			Uint8 padbyte;
   479 			for ( i=0; i<pad; ++i ) {
   480 				SDL_RWread(src, &padbyte, 1, 1);
   481 			}
   482 		}
   483 		if ( topDown ) {
   484 			bits += surface->pitch;
   485 		} else {
   486 			bits -= surface->pitch;
   487 		}
   488 	}
   489 done:
   490 	if ( was_error ) {
   491 		if ( src ) {
   492 			SDL_RWseek(src, fp_offset, SEEK_SET);
   493 		}
   494 		if ( surface ) {
   495 			SDL_FreeSurface(surface);
   496 		}
   497 		surface = NULL;
   498 	}
   499 	if ( freesrc && src ) {
   500 		SDL_RWclose(src);
   501 	}
   502 	return(surface);
   503 }
   504 
   505 static Uint8
   506 SDL_Read8(SDL_RWops * src)
   507 {
   508     Uint8 value;
   509 
   510     SDL_RWread(src, &value, 1, 1);
   511     return (value);
   512 }
   513 
   514 static SDL_Surface *
   515 LoadICOCUR_RW(SDL_RWops * src, int type, int freesrc)
   516 {
   517     SDL_bool was_error;
   518     long fp_offset;
   519     int bmpPitch;
   520     int i, pad;
   521     SDL_Surface *surface;
   522     Uint32 Rmask;
   523     Uint32 Gmask;
   524     Uint32 Bmask;
   525     Uint8 *bits;
   526     int ExpandBMP;
   527     int maxCol = 0;
   528     int icoOfs = 0;
   529     Uint32 palette[256];
   530 
   531     /* The Win32 ICO file header (14 bytes) */
   532     Uint16 bfReserved;
   533     Uint16 bfType;
   534     Uint16 bfCount;
   535 
   536     /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
   537     Uint32 biSize;
   538     Sint32 biWidth;
   539     Sint32 biHeight;
   540     Uint16 biPlanes;
   541     Uint16 biBitCount;
   542     Uint32 biCompression;
   543     Uint32 biSizeImage;
   544     Sint32 biXPelsPerMeter;
   545     Sint32 biYPelsPerMeter;
   546     Uint32 biClrUsed;
   547     Uint32 biClrImportant;
   548 
   549     /* Make sure we are passed a valid data source */
   550     surface = NULL;
   551     was_error = SDL_FALSE;
   552     if (src == NULL) {
   553         was_error = SDL_TRUE;
   554         goto done;
   555     }
   556 
   557     /* Read in the ICO file header */
   558     fp_offset = SDL_RWtell(src);
   559     SDL_ClearError();
   560 
   561     bfReserved = SDL_ReadLE16(src);
   562     bfType = SDL_ReadLE16(src);
   563     bfCount = SDL_ReadLE16(src);
   564     if ((bfReserved != 0) || (bfType != type) || (bfCount == 0)) {
   565         SDL_SetError("File is not a Windows %s file", type == 1 ? "ICO" : "CUR");
   566         was_error = SDL_TRUE;
   567         goto done;
   568     }
   569 
   570     /* Read the Win32 Icon Directory */
   571     for (i = 0; i < bfCount; i++) {
   572         /* Icon Directory Entries */
   573         int bWidth = SDL_Read8(src);    /* Uint8, but 0 = 256 ! */
   574         int bHeight = SDL_Read8(src);   /* Uint8, but 0 = 256 ! */
   575         int bColorCount = SDL_Read8(src);       /* Uint8, but 0 = 256 ! */
   576         Uint8 bReserved = SDL_Read8(src);
   577         Uint16 wPlanes = SDL_ReadLE16(src);
   578         Uint16 wBitCount = SDL_ReadLE16(src);
   579         Uint32 dwBytesInRes = SDL_ReadLE32(src);
   580         Uint32 dwImageOffset = SDL_ReadLE32(src);
   581 
   582         if (!bWidth)
   583             bWidth = 256;
   584         if (!bHeight)
   585             bHeight = 256;
   586         if (!bColorCount)
   587             bColorCount = 256;
   588 
   589         //printf("%dx%d@%d - %08x\n", bWidth, bHeight, bColorCount, dwImageOffset);
   590         if (bColorCount > maxCol) {
   591             maxCol = bColorCount;
   592             icoOfs = dwImageOffset;
   593             //printf("marked\n");
   594         }
   595     }
   596 
   597     /* Advance to the DIB Data */
   598     if (SDL_RWseek(src, icoOfs, RW_SEEK_SET) < 0) {
   599         SDL_Error(SDL_EFSEEK);
   600         was_error = SDL_TRUE;
   601         goto done;
   602     }
   603 
   604     /* Read the Win32 BITMAPINFOHEADER */
   605     biSize = SDL_ReadLE32(src);
   606     if (biSize == 40) {
   607         biWidth = SDL_ReadLE32(src);
   608         biHeight = SDL_ReadLE32(src);
   609         biPlanes = SDL_ReadLE16(src);
   610         biBitCount = SDL_ReadLE16(src);
   611         biCompression = SDL_ReadLE32(src);
   612         biSizeImage = SDL_ReadLE32(src);
   613         biXPelsPerMeter = SDL_ReadLE32(src);
   614         biYPelsPerMeter = SDL_ReadLE32(src);
   615         biClrUsed = SDL_ReadLE32(src);
   616         biClrImportant = SDL_ReadLE32(src);
   617     } else {
   618         SDL_SetError("Unsupported ICO bitmap format");
   619         was_error = SDL_TRUE;
   620         goto done;
   621     }
   622 
   623     /* Check for read error */
   624     if (SDL_strcmp(SDL_GetError(), "") != 0) {
   625         was_error = SDL_TRUE;
   626         goto done;
   627     }
   628 
   629     /* We don't support any BMP compression right now */
   630     switch (biCompression) {
   631     case BI_RGB:
   632         /* Default values for the BMP format */
   633         switch (biBitCount) {
   634         case 1:
   635         case 4:
   636             ExpandBMP = biBitCount;
   637             biBitCount = 8;
   638             break;
   639         case 8:
   640             ExpandBMP = 8;
   641             break;
   642         case 32:
   643             Rmask = 0x00FF0000;
   644             Gmask = 0x0000FF00;
   645             Bmask = 0x000000FF;
   646             ExpandBMP = 0;
   647             break;
   648         default:
   649             SDL_SetError("ICO file with unsupported bit count");
   650             was_error = SDL_TRUE;
   651             goto done;
   652         }
   653         break;
   654     default:
   655         SDL_SetError("Compressed ICO files not supported");
   656         was_error = SDL_TRUE;
   657         goto done;
   658     }
   659 
   660     /* Create a RGBA surface */
   661     biHeight = biHeight >> 1;
   662     //printf("%d x %d\n", biWidth, biHeight);
   663     surface =
   664         SDL_CreateRGBSurface(0, biWidth, biHeight, 32, 0x00FF0000,
   665                              0x0000FF00, 0x000000FF, 0xFF000000);
   666     if (surface == NULL) {
   667         was_error = SDL_TRUE;
   668         goto done;
   669     }
   670 
   671     /* Load the palette, if any */
   672     //printf("bc %d bused %d\n", biBitCount, biClrUsed);
   673     if (biBitCount <= 8) {
   674         if (biClrUsed == 0) {
   675             biClrUsed = 1 << biBitCount;
   676         }
   677         for (i = 0; i < (int) biClrUsed; ++i) {
   678             SDL_RWread(src, &palette[i], 4, 1);
   679         }
   680     }
   681 
   682     /* Read the surface pixels.  Note that the bmp image is upside down */
   683     bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
   684     switch (ExpandBMP) {
   685     case 1:
   686         bmpPitch = (biWidth + 7) >> 3;
   687         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   688         break;
   689     case 4:
   690         bmpPitch = (biWidth + 1) >> 1;
   691         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   692         break;
   693     case 8:
   694         bmpPitch = biWidth;
   695         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   696         break;
   697     default:
   698         bmpPitch = biWidth * 4;
   699         pad = 0;
   700         break;
   701     }
   702     while (bits > (Uint8 *) surface->pixels) {
   703         bits -= surface->pitch;
   704         switch (ExpandBMP) {
   705         case 1:
   706         case 4:
   707         case 8:
   708             {
   709                 Uint8 pixel = 0;
   710                 int shift = (8 - ExpandBMP);
   711                 for (i = 0; i < surface->w; ++i) {
   712                     if (i % (8 / ExpandBMP) == 0) {
   713                         if (!SDL_RWread(src, &pixel, 1, 1)) {
   714                             SDL_SetError("Error reading from ICO");
   715                             was_error = SDL_TRUE;
   716                             goto done;
   717                         }
   718                     }
   719                     *((Uint32 *) bits + i) = (palette[pixel >> shift]);
   720                     pixel <<= ExpandBMP;
   721                 }
   722             }
   723             break;
   724 
   725         default:
   726             if (SDL_RWread(src, bits, 1, surface->pitch)
   727                 != surface->pitch) {
   728                 SDL_Error(SDL_EFREAD);
   729                 was_error = SDL_TRUE;
   730                 goto done;
   731             }
   732             break;
   733         }
   734         /* Skip padding bytes, ugh */
   735         if (pad) {
   736             Uint8 padbyte;
   737             for (i = 0; i < pad; ++i) {
   738                 SDL_RWread(src, &padbyte, 1, 1);
   739             }
   740         }
   741     }
   742     /* Read the mask pixels.  Note that the bmp image is upside down */
   743     bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
   744     ExpandBMP = 1;
   745     bmpPitch = (biWidth + 7) >> 3;
   746     pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   747     while (bits > (Uint8 *) surface->pixels) {
   748         Uint8 pixel = 0;
   749         int shift = (8 - ExpandBMP);
   750 
   751         bits -= surface->pitch;
   752         for (i = 0; i < surface->w; ++i) {
   753             if (i % (8 / ExpandBMP) == 0) {
   754                 if (!SDL_RWread(src, &pixel, 1, 1)) {
   755                     SDL_SetError("Error reading from ICO");
   756                     was_error = SDL_TRUE;
   757                     goto done;
   758                 }
   759             }
   760             *((Uint32 *) bits + i) |= ((pixel >> shift) ? 0 : 0xFF000000);
   761             pixel <<= ExpandBMP;
   762         }
   763         /* Skip padding bytes, ugh */
   764         if (pad) {
   765             Uint8 padbyte;
   766             for (i = 0; i < pad; ++i) {
   767                 SDL_RWread(src, &padbyte, 1, 1);
   768             }
   769         }
   770     }
   771   done:
   772     if (was_error) {
   773         if (src) {
   774             SDL_RWseek(src, fp_offset, RW_SEEK_SET);
   775         }
   776         if (surface) {
   777             SDL_FreeSurface(surface);
   778         }
   779         surface = NULL;
   780     }
   781     if (freesrc && src) {
   782         SDL_RWclose(src);
   783     }
   784     return (surface);
   785 }
   786 
   787 /* Load a BMP type image from an SDL datasource */
   788 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
   789 {
   790 	return(LoadBMP_RW(src, 0));
   791 }
   792 
   793 /* Load a ICO type image from an SDL datasource */
   794 SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
   795 {
   796 	return(LoadICOCUR_RW(src, 1, 0));
   797 }
   798 
   799 /* Load a CUR type image from an SDL datasource */
   800 SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
   801 {
   802 	return(LoadICOCUR_RW(src, 2, 0));
   803 }
   804 
   805 #else
   806 
   807 /* See if an image is contained in a data source */
   808 int IMG_isBMP(SDL_RWops *src)
   809 {
   810 	return(0);
   811 }
   812 
   813 int IMG_isICO(SDL_RWops *src)
   814 {
   815 	return(0);
   816 }
   817 
   818 int IMG_isCUR(SDL_RWops *src)
   819 {
   820 	return(0);
   821 }
   822 
   823 /* Load a BMP type image from an SDL datasource */
   824 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
   825 {
   826 	return(NULL);
   827 }
   828 
   829 /* Load a BMP type image from an SDL datasource */
   830 SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
   831 {
   832 	return(NULL);
   833 }
   834 
   835 /* Load a BMP type image from an SDL datasource */
   836 SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
   837 {
   838 	return(NULL);
   839 }
   840 
   841 #endif /* LOAD_BMP */