IMG_bmp.c
author couriersud
Tue, 13 Jan 2009 01:21:00 +0000
changeset 189 39d5ac4ec0f8
parent 186 5fbbeabf647f
child 202 8e1d88c68bda
permissions -rw-r--r--
Added ICO and CUR support to SDL_image
     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 	int 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 	int ExpandBMP;
   200 
   201 	/* The Win32 BMP file header (14 bytes) */
   202 	char   magic[2];
   203 	Uint32 bfSize;
   204 	Uint16 bfReserved1;
   205 	Uint16 bfReserved2;
   206 	Uint32 bfOffBits;
   207 
   208 	/* The Win32 BITMAPINFOHEADER struct (40 bytes) */
   209 	Uint32 biSize;
   210 	Sint32 biWidth;
   211 	Sint32 biHeight;
   212 	Uint16 biPlanes;
   213 	Uint16 biBitCount;
   214 	Uint32 biCompression;
   215 	Uint32 biSizeImage;
   216 	Sint32 biXPelsPerMeter;
   217 	Sint32 biYPelsPerMeter;
   218 	Uint32 biClrUsed;
   219 	Uint32 biClrImportant;
   220 
   221 	/* Make sure we are passed a valid data source */
   222 	surface = NULL;
   223 	was_error = 0;
   224 	if ( src == NULL ) {
   225 		was_error = 1;
   226 		goto done;
   227 	}
   228 
   229 	/* Read in the BMP file header */
   230 	fp_offset = SDL_RWtell(src);
   231 	SDL_ClearError();
   232 	if ( SDL_RWread(src, magic, 1, 2) != 2 ) {
   233 		SDL_Error(SDL_EFREAD);
   234 		was_error = 1;
   235 		goto done;
   236 	}
   237 	if ( strncmp(magic, "BM", 2) != 0 ) {
   238 		SDL_SetError("File is not a Windows BMP file");
   239 		was_error = 1;
   240 		goto done;
   241 	}
   242 	bfSize		= SDL_ReadLE32(src);
   243 	bfReserved1	= SDL_ReadLE16(src);
   244 	bfReserved2	= SDL_ReadLE16(src);
   245 	bfOffBits	= SDL_ReadLE32(src);
   246 
   247 	/* Read the Win32 BITMAPINFOHEADER */
   248 	biSize		= SDL_ReadLE32(src);
   249 	if ( biSize == 12 ) {
   250 		biWidth		= (Uint32)SDL_ReadLE16(src);
   251 		biHeight	= (Uint32)SDL_ReadLE16(src);
   252 		biPlanes	= SDL_ReadLE16(src);
   253 		biBitCount	= SDL_ReadLE16(src);
   254 		biCompression	= BI_RGB;
   255 		biSizeImage	= 0;
   256 		biXPelsPerMeter	= 0;
   257 		biYPelsPerMeter	= 0;
   258 		biClrUsed	= 0;
   259 		biClrImportant	= 0;
   260 	} else {
   261 		biWidth		= SDL_ReadLE32(src);
   262 		biHeight	= SDL_ReadLE32(src);
   263 		biPlanes	= SDL_ReadLE16(src);
   264 		biBitCount	= SDL_ReadLE16(src);
   265 		biCompression	= SDL_ReadLE32(src);
   266 		biSizeImage	= SDL_ReadLE32(src);
   267 		biXPelsPerMeter	= SDL_ReadLE32(src);
   268 		biYPelsPerMeter	= SDL_ReadLE32(src);
   269 		biClrUsed	= SDL_ReadLE32(src);
   270 		biClrImportant	= SDL_ReadLE32(src);
   271 	}
   272 
   273 	/* Check for read error */
   274 	if ( strcmp(SDL_GetError(), "") != 0 ) {
   275 		was_error = 1;
   276 		goto done;
   277 	}
   278 
   279 	/* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
   280 	switch (biBitCount) {
   281 		case 1:
   282 		case 4:
   283 			ExpandBMP = biBitCount;
   284 			biBitCount = 8;
   285 			break;
   286 		default:
   287 			ExpandBMP = 0;
   288 			break;
   289 	}
   290 
   291 	/* RLE4 and RLE8 BMP compression is supported */
   292 	Rmask = Gmask = Bmask = Amask = 0;
   293 	switch (biCompression) {
   294 		case BI_RGB:
   295 			/* If there are no masks, use the defaults */
   296 			if ( bfOffBits == (14+biSize) ) {
   297 				/* Default values for the BMP format */
   298 				switch (biBitCount) {
   299 					case 15:
   300 					case 16:
   301 						Rmask = 0x7C00;
   302 						Gmask = 0x03E0;
   303 						Bmask = 0x001F;
   304 						break;
   305 					case 24:
   306 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   307 					        Rmask = 0x000000FF;
   308 					        Gmask = 0x0000FF00;
   309 					        Bmask = 0x00FF0000;
   310 #else
   311 						Rmask = 0x00FF0000;
   312 						Gmask = 0x0000FF00;
   313 						Bmask = 0x000000FF;
   314 #endif
   315 						break;
   316 					case 32:
   317 						Amask = 0xFF000000;
   318 						Rmask = 0x00FF0000;
   319 						Gmask = 0x0000FF00;
   320 						Bmask = 0x000000FF;
   321 						break;
   322 					default:
   323 						break;
   324 				}
   325 				break;
   326 			}
   327 			/* Fall through -- read the RGB masks */
   328 
   329 		default:
   330 			switch (biBitCount) {
   331 				case 15:
   332 				case 16:
   333 				case 32:
   334 					Rmask = SDL_ReadLE32(src);
   335 					Gmask = SDL_ReadLE32(src);
   336 					Bmask = SDL_ReadLE32(src);
   337 					Amask = SDL_ReadLE32(src);
   338 					break;
   339 				default:
   340 					break;
   341 			}
   342 			break;
   343 	}
   344 
   345 	/* Create a compatible surface, note that the colors are RGB ordered */
   346 	surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
   347 			biWidth, biHeight, biBitCount, Rmask, Gmask, Bmask, Amask);
   348 	if ( surface == NULL ) {
   349 		was_error = 1;
   350 		goto done;
   351 	}
   352 
   353 	/* Load the palette, if any */
   354 	palette = (surface->format)->palette;
   355 	if ( palette ) {
   356 		if ( SDL_RWseek(src, fp_offset+14+biSize, SEEK_SET) < 0 ) {
   357 			SDL_Error(SDL_EFSEEK);
   358 			was_error = 1;
   359 			goto done;
   360 		}
   361 
   362 		/*
   363 		| guich: always use 1<<bpp b/c some bitmaps can bring wrong information
   364 		| for colorsUsed
   365 		*/
   366 		/* if ( biClrUsed == 0 ) {  */
   367 		biClrUsed = 1 << biBitCount;
   368 		/* } */
   369 		if ( biSize == 12 ) {
   370 			for ( i = 0; i < (int)biClrUsed; ++i ) {
   371 				SDL_RWread(src, &palette->colors[i].b, 1, 1);
   372 				SDL_RWread(src, &palette->colors[i].g, 1, 1);
   373 				SDL_RWread(src, &palette->colors[i].r, 1, 1);
   374 				palette->colors[i].unused = 0;
   375 			}	
   376 		} else {
   377 			for ( i = 0; i < (int)biClrUsed; ++i ) {
   378 				SDL_RWread(src, &palette->colors[i].b, 1, 1);
   379 				SDL_RWread(src, &palette->colors[i].g, 1, 1);
   380 				SDL_RWread(src, &palette->colors[i].r, 1, 1);
   381 				SDL_RWread(src, &palette->colors[i].unused, 1, 1);
   382 			}	
   383 		}
   384 		palette->ncolors = biClrUsed;
   385 	}
   386 
   387 	/* Read the surface pixels.  Note that the bmp image is upside down */
   388 	if ( SDL_RWseek(src, fp_offset+bfOffBits, SEEK_SET) < 0 ) {
   389 		SDL_Error(SDL_EFSEEK);
   390 		was_error = 1;
   391 		goto done;
   392 	}
   393 	if ((biCompression == BI_RLE4) || (biCompression == BI_RLE8)) {
   394 		was_error = readRlePixels(surface, src, biCompression == BI_RLE8);
   395 		if (was_error) SDL_SetError("Error reading from BMP");
   396 		goto done;
   397 	}
   398 	bits = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
   399 	switch (ExpandBMP) {
   400 		case 1:
   401 			bmpPitch = (biWidth + 7) >> 3;
   402 			pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
   403 			break;
   404 		case 4:
   405 			bmpPitch = (biWidth + 1) >> 1;
   406 			pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
   407 			break;
   408 		default:
   409 			pad  = ((surface->pitch%4) ?
   410 					(4-(surface->pitch%4)) : 0);
   411 			break;
   412 	}
   413 	while ( bits > (Uint8 *)surface->pixels ) {
   414 		bits -= surface->pitch;
   415 		switch (ExpandBMP) {
   416 			case 1:
   417 			case 4: {
   418 			Uint8 pixel = 0;
   419 			int   shift = (8-ExpandBMP);
   420 			for ( i=0; i<surface->w; ++i ) {
   421 				if ( i%(8/ExpandBMP) == 0 ) {
   422 					if ( !SDL_RWread(src, &pixel, 1, 1) ) {
   423 						SDL_SetError(
   424 					"Error reading from BMP");
   425 						was_error = 1;
   426 						goto done;
   427 					}
   428 				}
   429 				*(bits+i) = (pixel>>shift);
   430 				pixel <<= ExpandBMP;
   431 			} }
   432 			break;
   433 
   434 			default:
   435 			if ( SDL_RWread(src, bits, 1, surface->pitch)
   436 							 != surface->pitch ) {
   437 				SDL_Error(SDL_EFREAD);
   438 				was_error = 1;
   439 				goto done;
   440 			}
   441 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   442 			/* Byte-swap the pixels if needed. Note that the 24bpp
   443 			   case has already been taken care of above. */
   444 			switch(biBitCount) {
   445 				case 15:
   446 				case 16: {
   447 				        Uint16 *pix = (Uint16 *)bits;
   448 					for(i = 0; i < surface->w; i++)
   449 					        pix[i] = SDL_Swap16(pix[i]);
   450 					break;
   451 				}
   452 
   453 				case 32: {
   454 				        Uint32 *pix = (Uint32 *)bits;
   455 					for(i = 0; i < surface->w; i++)
   456 					        pix[i] = SDL_Swap32(pix[i]);
   457 					break;
   458 				}
   459 			}
   460 #endif
   461 			break;
   462 		}
   463 		/* Skip padding bytes, ugh */
   464 		if ( pad ) {
   465 			Uint8 padbyte;
   466 			for ( i=0; i<pad; ++i ) {
   467 				SDL_RWread(src, &padbyte, 1, 1);
   468 			}
   469 		}
   470 	}
   471 done:
   472 	if ( was_error ) {
   473 		if ( src ) {
   474 			SDL_RWseek(src, fp_offset, SEEK_SET);
   475 		}
   476 		if ( surface ) {
   477 			SDL_FreeSurface(surface);
   478 		}
   479 		surface = NULL;
   480 	}
   481 	if ( freesrc && src ) {
   482 		SDL_RWclose(src);
   483 	}
   484 	return(surface);
   485 }
   486 
   487 static Uint8
   488 SDL_Read8(SDL_RWops * src)
   489 {
   490     Uint8 value;
   491 
   492     SDL_RWread(src, &value, 1, 1);
   493     return (value);
   494 }
   495 
   496 static SDL_Surface *
   497 LoadICOCUR_RW(SDL_RWops * src, int type, int freesrc)
   498 {
   499     int was_error;
   500     long fp_offset;
   501     int bmpPitch;
   502     int i, pad;
   503     SDL_Surface *surface;
   504     Uint32 Rmask;
   505     Uint32 Gmask;
   506     Uint32 Bmask;
   507     Uint8 *bits;
   508     int ExpandBMP;
   509     int maxCol = 0;
   510     int icoOfs = 0;
   511     Uint32 palette[256];
   512 
   513     /* The Win32 ICO file header (14 bytes) */
   514     Uint16 bfReserved;
   515     Uint16 bfType;
   516     Uint16 bfCount;
   517 
   518     /* The Win32 BITMAPINFOHEADER struct (40 bytes) */
   519     Uint32 biSize;
   520     Sint32 biWidth;
   521     Sint32 biHeight;
   522     Uint16 biPlanes;
   523     Uint16 biBitCount;
   524     Uint32 biCompression;
   525     Uint32 biSizeImage;
   526     Sint32 biXPelsPerMeter;
   527     Sint32 biYPelsPerMeter;
   528     Uint32 biClrUsed;
   529     Uint32 biClrImportant;
   530 
   531     /* Make sure we are passed a valid data source */
   532     surface = NULL;
   533     was_error = 0;
   534     if (src == NULL) {
   535         was_error = 1;
   536         goto done;
   537     }
   538 
   539     /* Read in the ICO file header */
   540     fp_offset = SDL_RWtell(src);
   541     SDL_ClearError();
   542 
   543     bfReserved = SDL_ReadLE16(src);
   544     bfType = SDL_ReadLE16(src);
   545     bfCount = SDL_ReadLE16(src);
   546     if ((bfReserved != 0) || (bfType != type) || (bfCount == 0)) {
   547         SDL_SetError("File is not a Windows %s file", type == 1 ? "ICO" : "CUR");
   548         was_error = 1;
   549         goto done;
   550     }
   551 
   552     /* Read the Win32 Icon Directory */
   553     for (i = 0; i < bfCount; i++) {
   554         /* Icon Directory Entries */
   555         int bWidth = SDL_Read8(src);    /* Uint8, but 0 = 256 ! */
   556         int bHeight = SDL_Read8(src);   /* Uint8, but 0 = 256 ! */
   557         int bColorCount = SDL_Read8(src);       /* Uint8, but 0 = 256 ! */
   558         Uint8 bReserved = SDL_Read8(src);
   559         Uint16 wPlanes = SDL_ReadLE16(src);
   560         Uint16 wBitCount = SDL_ReadLE16(src);
   561         Uint32 dwBytesInRes = SDL_ReadLE32(src);
   562         Uint32 dwImageOffset = SDL_ReadLE32(src);
   563 
   564         if (!bWidth)
   565             bWidth = 256;
   566         if (!bHeight)
   567             bHeight = 256;
   568         if (!bColorCount)
   569             bColorCount = 256;
   570 
   571         //printf("%dx%d@%d - %08x\n", bWidth, bHeight, bColorCount, dwImageOffset);
   572         if (bColorCount > maxCol) {
   573             maxCol = bColorCount;
   574             icoOfs = dwImageOffset;
   575             //printf("marked\n");
   576         }
   577     }
   578 
   579     /* Advance to the DIB Data */
   580     if (SDL_RWseek(src, icoOfs, RW_SEEK_SET) < 0) {
   581         SDL_Error(SDL_EFSEEK);
   582         was_error = 1;
   583         goto done;
   584     }
   585 
   586     /* Read the Win32 BITMAPINFOHEADER */
   587     biSize = SDL_ReadLE32(src);
   588     if (biSize == 40) {
   589         biWidth = SDL_ReadLE32(src);
   590         biHeight = SDL_ReadLE32(src);
   591         biPlanes = SDL_ReadLE16(src);
   592         biBitCount = SDL_ReadLE16(src);
   593         biCompression = SDL_ReadLE32(src);
   594         biSizeImage = SDL_ReadLE32(src);
   595         biXPelsPerMeter = SDL_ReadLE32(src);
   596         biYPelsPerMeter = SDL_ReadLE32(src);
   597         biClrUsed = SDL_ReadLE32(src);
   598         biClrImportant = SDL_ReadLE32(src);
   599     } else {
   600         SDL_SetError("Unsupported ICO bitmap format");
   601         was_error = 1;
   602         goto done;
   603     }
   604 
   605     /* Check for read error */
   606     if (SDL_strcmp(SDL_GetError(), "") != 0) {
   607         was_error = 1;
   608         goto done;
   609     }
   610 
   611     /* We don't support any BMP compression right now */
   612     switch (biCompression) {
   613     case BI_RGB:
   614         /* Default values for the BMP format */
   615         switch (biBitCount) {
   616         case 1:
   617         case 4:
   618             ExpandBMP = biBitCount;
   619             biBitCount = 8;
   620             break;
   621         case 8:
   622             ExpandBMP = 8;
   623             break;
   624         case 32:
   625             Rmask = 0x00FF0000;
   626             Gmask = 0x0000FF00;
   627             Bmask = 0x000000FF;
   628             ExpandBMP = 0;
   629             break;
   630         default:
   631             SDL_SetError("ICO file with unsupported bit count");
   632             was_error = 1;
   633             goto done;
   634         }
   635         break;
   636     default:
   637         SDL_SetError("Compressed ICO files not supported");
   638         was_error = 1;
   639         goto done;
   640     }
   641 
   642     /* Create a RGBA surface */
   643     biHeight = biHeight >> 1;
   644     //printf("%d x %d\n", biWidth, biHeight);
   645     surface =
   646         SDL_CreateRGBSurface(0, biWidth, biHeight, 32, 0x00FF0000,
   647                              0x0000FF00, 0x000000FF, 0xFF000000);
   648     if (surface == NULL) {
   649         was_error = 1;
   650         goto done;
   651     }
   652 
   653     /* Load the palette, if any */
   654     //printf("bc %d bused %d\n", biBitCount, biClrUsed);
   655     if (biBitCount <= 8) {
   656         if (biClrUsed == 0) {
   657             biClrUsed = 1 << biBitCount;
   658         }
   659         for (i = 0; i < (int) biClrUsed; ++i) {
   660             SDL_RWread(src, &palette[i], 4, 1);
   661         }
   662     }
   663 
   664     /* Read the surface pixels.  Note that the bmp image is upside down */
   665     bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
   666     switch (ExpandBMP) {
   667     case 1:
   668         bmpPitch = (biWidth + 7) >> 3;
   669         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   670         break;
   671     case 4:
   672         bmpPitch = (biWidth + 1) >> 1;
   673         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   674         break;
   675     case 8:
   676         bmpPitch = biWidth;
   677         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   678         break;
   679     default:
   680         bmpPitch = biWidth * 4;
   681         pad = 0;
   682         break;
   683     }
   684     while (bits > (Uint8 *) surface->pixels) {
   685         bits -= surface->pitch;
   686         switch (ExpandBMP) {
   687         case 1:
   688         case 4:
   689         case 8:
   690             {
   691                 Uint8 pixel = 0;
   692                 int shift = (8 - ExpandBMP);
   693                 for (i = 0; i < surface->w; ++i) {
   694                     if (i % (8 / ExpandBMP) == 0) {
   695                         if (!SDL_RWread(src, &pixel, 1, 1)) {
   696                             SDL_SetError("Error reading from ICO");
   697                             was_error = 1;
   698                             goto done;
   699                         }
   700                     }
   701                     *((Uint32 *) bits + i) = (palette[pixel >> shift]);
   702                     pixel <<= ExpandBMP;
   703                 }
   704             }
   705             break;
   706 
   707         default:
   708             if (SDL_RWread(src, bits, 1, surface->pitch)
   709                 != surface->pitch) {
   710                 SDL_Error(SDL_EFREAD);
   711                 was_error = 1;
   712                 goto done;
   713             }
   714             break;
   715         }
   716         /* Skip padding bytes, ugh */
   717         if (pad) {
   718             Uint8 padbyte;
   719             for (i = 0; i < pad; ++i) {
   720                 SDL_RWread(src, &padbyte, 1, 1);
   721             }
   722         }
   723     }
   724     /* Read the mask pixels.  Note that the bmp image is upside down */
   725     bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
   726     ExpandBMP = 1;
   727     bmpPitch = (biWidth + 7) >> 3;
   728     pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   729     while (bits > (Uint8 *) surface->pixels) {
   730         Uint8 pixel = 0;
   731         int shift = (8 - ExpandBMP);
   732 
   733         bits -= surface->pitch;
   734         for (i = 0; i < surface->w; ++i) {
   735             if (i % (8 / ExpandBMP) == 0) {
   736                 if (!SDL_RWread(src, &pixel, 1, 1)) {
   737                     SDL_SetError("Error reading from ICO");
   738                     was_error = 1;
   739                     goto done;
   740                 }
   741             }
   742             *((Uint32 *) bits + i) |= ((pixel >> shift) ? 0 : 0xFF000000);
   743             pixel <<= ExpandBMP;
   744         }
   745         /* Skip padding bytes, ugh */
   746         if (pad) {
   747             Uint8 padbyte;
   748             for (i = 0; i < pad; ++i) {
   749                 SDL_RWread(src, &padbyte, 1, 1);
   750             }
   751         }
   752     }
   753   done:
   754     if (was_error) {
   755         if (src) {
   756             SDL_RWseek(src, fp_offset, RW_SEEK_SET);
   757         }
   758         if (surface) {
   759             SDL_FreeSurface(surface);
   760         }
   761         surface = NULL;
   762     }
   763     if (freesrc && src) {
   764         SDL_RWclose(src);
   765     }
   766     return (surface);
   767 }
   768 
   769 /* Load a BMP type image from an SDL datasource */
   770 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
   771 {
   772 	return(LoadBMP_RW(src, 0));
   773 }
   774 
   775 /* Load a ICO type image from an SDL datasource */
   776 SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
   777 {
   778 	return(LoadICOCUR_RW(src, 1, 0));
   779 }
   780 
   781 /* Load a CUR type image from an SDL datasource */
   782 SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
   783 {
   784 	return(LoadICOCUR_RW(src, 2, 0));
   785 }
   786 
   787 #else
   788 
   789 /* See if an image is contained in a data source */
   790 int IMG_isBMP(SDL_RWops *src)
   791 {
   792 	return(0);
   793 }
   794 
   795 int IMG_isICO(SDL_RWops *src)
   796 {
   797 	return(0);
   798 }
   799 
   800 int IMG_isCUR(SDL_RWops *src)
   801 {
   802 	return(0);
   803 }
   804 
   805 /* Load a BMP type image from an SDL datasource */
   806 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
   807 {
   808 	return(NULL);
   809 }
   810 
   811 /* Load a BMP type image from an SDL datasource */
   812 SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
   813 {
   814 	return(NULL);
   815 }
   816 
   817 /* Load a BMP type image from an SDL datasource */
   818 SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
   819 {
   820 	return(NULL);
   821 }
   822 
   823 #endif /* LOAD_BMP */