IMG_bmp.c
author Thomas Bernard <miniupnp@free.fr>
Fri, 30 Nov 2018 11:04:15 +0100
branchSDL-1.2
changeset 634 68f958f43339
parent 607 1a1189c2978f
permissions -rw-r--r--
IMG_xcf.c: Avoid infinite loop in read_xcf_header()
     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     /* sanity check image size, so we don't overflow integers, etc. */
   666     if ((biWidth < 0) || (biWidth > 0xFFFFFF) ||
   667         (biHeight < 0) || (biHeight > 0xFFFFFF)) {
   668         IMG_SetError("Unsupported or invalid ICO dimensions");
   669         was_error = SDL_TRUE;
   670         goto done;
   671     }
   672 
   673     /* sanity check image size, so we don't overflow integers, etc. */
   674     if ((biWidth < 0) || (biWidth > 0xFFFFFF) ||
   675         (biHeight < 0) || (biHeight > 0xFFFFFF)) {
   676         IMG_SetError("Unsupported or invalid ICO dimensions");
   677         was_error = SDL_TRUE;
   678         goto done;
   679     }
   680 
   681     /* Create a RGBA surface */
   682     biHeight = biHeight >> 1;
   683     //printf("%d x %d\n", biWidth, biHeight);
   684     surface =
   685         SDL_CreateRGBSurface(0, biWidth, biHeight, 32, 0x00FF0000,
   686                              0x0000FF00, 0x000000FF, 0xFF000000);
   687     if (surface == NULL) {
   688         was_error = SDL_TRUE;
   689         goto done;
   690     }
   691 
   692     /* Load the palette, if any */
   693     //printf("bc %d bused %d\n", biBitCount, biClrUsed);
   694     if (biBitCount <= 8) {
   695         if (biClrUsed == 0) {
   696             biClrUsed = 1 << biBitCount;
   697         }
   698         if (biClrUsed > (sizeof(palette)/sizeof(palette[0]))) {
   699             IMG_SetError("Unsupported or incorrect biClrUsed field");
   700             was_error = SDL_TRUE;
   701             goto done;
   702         }
   703         for (i = 0; i < (int) biClrUsed; ++i) {
   704             SDL_RWread(src, &palette[i], 4, 1);
   705         }
   706     }
   707 
   708     /* Read the surface pixels.  Note that the bmp image is upside down */
   709     bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
   710     switch (ExpandBMP) {
   711     case 1:
   712         bmpPitch = (biWidth + 7) >> 3;
   713         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   714         break;
   715     case 4:
   716         bmpPitch = (biWidth + 1) >> 1;
   717         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   718         break;
   719     case 8:
   720         bmpPitch = biWidth;
   721         pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   722         break;
   723     default:
   724         bmpPitch = biWidth * 4;
   725         pad = 0;
   726         break;
   727     }
   728     while (bits > (Uint8 *) surface->pixels) {
   729         bits -= surface->pitch;
   730         switch (ExpandBMP) {
   731         case 1:
   732         case 4:
   733         case 8:
   734             {
   735                 Uint8 pixel = 0;
   736                 int shift = (8 - ExpandBMP);
   737                 for (i = 0; i < surface->w; ++i) {
   738                     if (i % (8 / ExpandBMP) == 0) {
   739                         if (!SDL_RWread(src, &pixel, 1, 1)) {
   740                             IMG_SetError("Error reading from ICO");
   741                             was_error = SDL_TRUE;
   742                             goto done;
   743                         }
   744                     }
   745                     *((Uint32 *) bits + i) = (palette[pixel >> shift]);
   746                     pixel <<= ExpandBMP;
   747                 }
   748             }
   749             break;
   750 
   751         default:
   752             if (SDL_RWread(src, bits, 1, surface->pitch)
   753                 != surface->pitch) {
   754                 SDL_Error(SDL_EFREAD);
   755                 was_error = SDL_TRUE;
   756                 goto done;
   757             }
   758             break;
   759         }
   760         /* Skip padding bytes, ugh */
   761         if (pad) {
   762             Uint8 padbyte;
   763             for (i = 0; i < pad; ++i) {
   764                 SDL_RWread(src, &padbyte, 1, 1);
   765             }
   766         }
   767     }
   768     /* Read the mask pixels.  Note that the bmp image is upside down */
   769     bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch);
   770     ExpandBMP = 1;
   771     bmpPitch = (biWidth + 7) >> 3;
   772     pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0);
   773     while (bits > (Uint8 *) surface->pixels) {
   774         Uint8 pixel = 0;
   775         int shift = (8 - ExpandBMP);
   776 
   777         bits -= surface->pitch;
   778         for (i = 0; i < surface->w; ++i) {
   779             if (i % (8 / ExpandBMP) == 0) {
   780                 if (!SDL_RWread(src, &pixel, 1, 1)) {
   781                     IMG_SetError("Error reading from ICO");
   782                     was_error = SDL_TRUE;
   783                     goto done;
   784                 }
   785             }
   786             *((Uint32 *) bits + i) |= ((pixel >> shift) ? 0 : 0xFF000000);
   787             pixel <<= ExpandBMP;
   788         }
   789         /* Skip padding bytes, ugh */
   790         if (pad) {
   791             Uint8 padbyte;
   792             for (i = 0; i < pad; ++i) {
   793                 SDL_RWread(src, &padbyte, 1, 1);
   794             }
   795         }
   796     }
   797   done:
   798     if (was_error) {
   799         if (src) {
   800             SDL_RWseek(src, fp_offset, RW_SEEK_SET);
   801         }
   802         if (surface) {
   803             SDL_FreeSurface(surface);
   804         }
   805         surface = NULL;
   806     }
   807     if (freesrc && src) {
   808         SDL_RWclose(src);
   809     }
   810     return (surface);
   811 }
   812 
   813 /* Load a BMP type image from an SDL datasource */
   814 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
   815 {
   816 	return(LoadBMP_RW(src, 0));
   817 }
   818 
   819 /* Load a ICO type image from an SDL datasource */
   820 SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
   821 {
   822 	return(LoadICOCUR_RW(src, 1, 0));
   823 }
   824 
   825 /* Load a CUR type image from an SDL datasource */
   826 SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
   827 {
   828 	return(LoadICOCUR_RW(src, 2, 0));
   829 }
   830 
   831 #else
   832 
   833 /* See if an image is contained in a data source */
   834 int IMG_isBMP(SDL_RWops *src)
   835 {
   836 	return(0);
   837 }
   838 
   839 int IMG_isICO(SDL_RWops *src)
   840 {
   841 	return(0);
   842 }
   843 
   844 int IMG_isCUR(SDL_RWops *src)
   845 {
   846 	return(0);
   847 }
   848 
   849 /* Load a BMP type image from an SDL datasource */
   850 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
   851 {
   852 	return(NULL);
   853 }
   854 
   855 /* Load a BMP type image from an SDL datasource */
   856 SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src)
   857 {
   858 	return(NULL);
   859 }
   860 
   861 /* Load a BMP type image from an SDL datasource */
   862 SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src)
   863 {
   864 	return(NULL);
   865 }
   866 
   867 #endif /* LOAD_BMP */
   868 
   869 #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */