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