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