IMG_bmp.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 08 Dec 2008 00:27:32 +0000
changeset 186 5fbbeabf647f
parent 184 578a70f2c6d7
child 189 39d5ac4ec0f8
permissions -rw-r--r--
Updated copyright date
     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 
    25 #include <stdio.h>
    26 #include <string.h>
    27 
    28 #include "SDL_image.h"
    29 
    30 #ifdef LOAD_BMP
    31 
    32 /* See if an image is contained in a data source */
    33 int IMG_isBMP(SDL_RWops *src)
    34 {
    35 	int start;
    36 	int is_BMP;
    37 	char magic[2];
    38 
    39 	if ( !src )
    40 		return 0;
    41 	start = SDL_RWtell(src);
    42 	is_BMP = 0;
    43 	if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
    44 		if ( strncmp(magic, "BM", 2) == 0 ) {
    45 			is_BMP = 1;
    46 		}
    47 	}
    48 	SDL_RWseek(src, start, SEEK_SET);
    49 	return(is_BMP);
    50 }
    51 
    52 #include "SDL_error.h"
    53 #include "SDL_video.h"
    54 #include "SDL_endian.h"
    55 
    56 /* Compression encodings for BMP files */
    57 #ifndef BI_RGB
    58 #define BI_RGB		0
    59 #define BI_RLE8		1
    60 #define BI_RLE4		2
    61 #define BI_BITFIELDS	3
    62 #endif
    63 
    64 static int readRlePixels(SDL_Surface * surface, SDL_RWops * src, int isRle8)
    65 {
    66 	/*
    67 	| Sets the surface pixels from src.  A bmp image is upside down.
    68 	*/
    69 	int pitch = surface->pitch;
    70 	int height = surface->h;
    71 	Uint8 *start = (Uint8 *)surface->pixels;
    72 	Uint8 *end = start + (height*pitch);
    73 	Uint8 *bits = end-pitch, *spot;
    74 	int ofs = 0;
    75 	Uint8 ch;
    76 	Uint8 needsPad;
    77 
    78 #define COPY_PIXEL(x)	spot = &bits[ofs++]; if(spot >= start && spot < end) *spot = (x)
    79 
    80 	for (;;) {
    81 		if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
    82 		/*
    83 		| encoded mode starts with a run length, and then a byte
    84 		| with two colour indexes to alternate between for the run
    85 		*/
    86 		if ( ch ) {
    87 			Uint8 pixel;
    88 			if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
    89 			if ( isRle8 ) {                 /* 256-color bitmap, compressed */
    90 				do {
    91 					COPY_PIXEL(pixel);
    92 				} while (--ch);
    93 			} else {                         /* 16-color bitmap, compressed */
    94 				Uint8 pixel0 = pixel >> 4;
    95 				Uint8 pixel1 = pixel & 0x0F;
    96 				for (;;) {
    97 					COPY_PIXEL(pixel0);	/* even count, high nibble */
    98 					if (!--ch) break;
    99 					COPY_PIXEL(pixel1);	/* odd count, low nibble */
   100 					if (!--ch) break;
   101 				}
   102 			}
   103 		} else {
   104 			/*
   105 			| A leading zero is an escape; it may signal the end of the bitmap,
   106 			| a cursor move, or some absolute data.
   107 			| zero tag may be absolute mode or an escape
   108 			*/
   109 			if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
   110 			switch (ch) {
   111 			case 0:                         /* end of line */
   112 				ofs = 0;
   113 				bits -= pitch;               /* go to previous */
   114 				break;
   115 			case 1:                         /* end of bitmap */
   116 				return 0;                    /* success! */
   117 			case 2:                         /* delta */
   118 				if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
   119 				ofs += ch;
   120 				if ( !SDL_RWread(src, &ch, 1, 1) ) return 1;
   121 				bits -= (ch * pitch);
   122 				break;
   123 			default:                        /* no compression */
   124 				if (isRle8) {
   125 					needsPad = ( ch & 1 );
   126 					do {
   127 						Uint8 pixel;
   128 						if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
   129 						COPY_PIXEL(pixel);
   130 					} while (--ch);
   131 				} else {
   132 					needsPad = ( ((ch+1)>>1) & 1 ); /* (ch+1)>>1: bytes size */
   133 					for (;;) {
   134 						Uint8 pixel;
   135 						if ( !SDL_RWread(src, &pixel, 1, 1) ) return 1;
   136 						COPY_PIXEL(pixel >> 4);
   137 						if (!--ch) break;
   138 						COPY_PIXEL(pixel & 0x0F);
   139 						if (!--ch) break;
   140 					}
   141 				}
   142 				/* pad at even boundary */
   143 				if ( needsPad && !SDL_RWread(src, &ch, 1, 1) ) return 1;
   144 				break;
   145 			}
   146 		}
   147 	}
   148 }
   149 
   150 static SDL_Surface *LoadBMP_RW (SDL_RWops *src, int freesrc)
   151 {
   152 	int was_error;
   153 	long fp_offset;
   154 	int bmpPitch;
   155 	int i, pad;
   156 	SDL_Surface *surface;
   157 	Uint32 Rmask;
   158 	Uint32 Gmask;
   159 	Uint32 Bmask;
   160 	Uint32 Amask;
   161 	SDL_Palette *palette;
   162 	Uint8 *bits;
   163 	int ExpandBMP;
   164 
   165 	/* The Win32 BMP file header (14 bytes) */
   166 	char   magic[2];
   167 	Uint32 bfSize;
   168 	Uint16 bfReserved1;
   169 	Uint16 bfReserved2;
   170 	Uint32 bfOffBits;
   171 
   172 	/* The Win32 BITMAPINFOHEADER struct (40 bytes) */
   173 	Uint32 biSize;
   174 	Sint32 biWidth;
   175 	Sint32 biHeight;
   176 	Uint16 biPlanes;
   177 	Uint16 biBitCount;
   178 	Uint32 biCompression;
   179 	Uint32 biSizeImage;
   180 	Sint32 biXPelsPerMeter;
   181 	Sint32 biYPelsPerMeter;
   182 	Uint32 biClrUsed;
   183 	Uint32 biClrImportant;
   184 
   185 	/* Make sure we are passed a valid data source */
   186 	surface = NULL;
   187 	was_error = 0;
   188 	if ( src == NULL ) {
   189 		was_error = 1;
   190 		goto done;
   191 	}
   192 
   193 	/* Read in the BMP file header */
   194 	fp_offset = SDL_RWtell(src);
   195 	SDL_ClearError();
   196 	if ( SDL_RWread(src, magic, 1, 2) != 2 ) {
   197 		SDL_Error(SDL_EFREAD);
   198 		was_error = 1;
   199 		goto done;
   200 	}
   201 	if ( strncmp(magic, "BM", 2) != 0 ) {
   202 		SDL_SetError("File is not a Windows BMP file");
   203 		was_error = 1;
   204 		goto done;
   205 	}
   206 	bfSize		= SDL_ReadLE32(src);
   207 	bfReserved1	= SDL_ReadLE16(src);
   208 	bfReserved2	= SDL_ReadLE16(src);
   209 	bfOffBits	= SDL_ReadLE32(src);
   210 
   211 	/* Read the Win32 BITMAPINFOHEADER */
   212 	biSize		= SDL_ReadLE32(src);
   213 	if ( biSize == 12 ) {
   214 		biWidth		= (Uint32)SDL_ReadLE16(src);
   215 		biHeight	= (Uint32)SDL_ReadLE16(src);
   216 		biPlanes	= SDL_ReadLE16(src);
   217 		biBitCount	= SDL_ReadLE16(src);
   218 		biCompression	= BI_RGB;
   219 		biSizeImage	= 0;
   220 		biXPelsPerMeter	= 0;
   221 		biYPelsPerMeter	= 0;
   222 		biClrUsed	= 0;
   223 		biClrImportant	= 0;
   224 	} else {
   225 		biWidth		= SDL_ReadLE32(src);
   226 		biHeight	= SDL_ReadLE32(src);
   227 		biPlanes	= SDL_ReadLE16(src);
   228 		biBitCount	= SDL_ReadLE16(src);
   229 		biCompression	= SDL_ReadLE32(src);
   230 		biSizeImage	= SDL_ReadLE32(src);
   231 		biXPelsPerMeter	= SDL_ReadLE32(src);
   232 		biYPelsPerMeter	= SDL_ReadLE32(src);
   233 		biClrUsed	= SDL_ReadLE32(src);
   234 		biClrImportant	= SDL_ReadLE32(src);
   235 	}
   236 
   237 	/* Check for read error */
   238 	if ( strcmp(SDL_GetError(), "") != 0 ) {
   239 		was_error = 1;
   240 		goto done;
   241 	}
   242 
   243 	/* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
   244 	switch (biBitCount) {
   245 		case 1:
   246 		case 4:
   247 			ExpandBMP = biBitCount;
   248 			biBitCount = 8;
   249 			break;
   250 		default:
   251 			ExpandBMP = 0;
   252 			break;
   253 	}
   254 
   255 	/* RLE4 and RLE8 BMP compression is supported */
   256 	Rmask = Gmask = Bmask = Amask = 0;
   257 	switch (biCompression) {
   258 		case BI_RGB:
   259 			/* If there are no masks, use the defaults */
   260 			if ( bfOffBits == (14+biSize) ) {
   261 				/* Default values for the BMP format */
   262 				switch (biBitCount) {
   263 					case 15:
   264 					case 16:
   265 						Rmask = 0x7C00;
   266 						Gmask = 0x03E0;
   267 						Bmask = 0x001F;
   268 						break;
   269 					case 24:
   270 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   271 					        Rmask = 0x000000FF;
   272 					        Gmask = 0x0000FF00;
   273 					        Bmask = 0x00FF0000;
   274 #else
   275 						Rmask = 0x00FF0000;
   276 						Gmask = 0x0000FF00;
   277 						Bmask = 0x000000FF;
   278 #endif
   279 						break;
   280 					case 32:
   281 						Amask = 0xFF000000;
   282 						Rmask = 0x00FF0000;
   283 						Gmask = 0x0000FF00;
   284 						Bmask = 0x000000FF;
   285 						break;
   286 					default:
   287 						break;
   288 				}
   289 				break;
   290 			}
   291 			/* Fall through -- read the RGB masks */
   292 
   293 		default:
   294 			switch (biBitCount) {
   295 				case 15:
   296 				case 16:
   297 				case 32:
   298 					Rmask = SDL_ReadLE32(src);
   299 					Gmask = SDL_ReadLE32(src);
   300 					Bmask = SDL_ReadLE32(src);
   301 					Amask = SDL_ReadLE32(src);
   302 					break;
   303 				default:
   304 					break;
   305 			}
   306 			break;
   307 	}
   308 
   309 	/* Create a compatible surface, note that the colors are RGB ordered */
   310 	surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
   311 			biWidth, biHeight, biBitCount, Rmask, Gmask, Bmask, Amask);
   312 	if ( surface == NULL ) {
   313 		was_error = 1;
   314 		goto done;
   315 	}
   316 
   317 	/* Load the palette, if any */
   318 	palette = (surface->format)->palette;
   319 	if ( palette ) {
   320 		if ( SDL_RWseek(src, fp_offset+14+biSize, SEEK_SET) < 0 ) {
   321 			SDL_Error(SDL_EFSEEK);
   322 			was_error = 1;
   323 			goto done;
   324 		}
   325 
   326 		/*
   327 		| guich: always use 1<<bpp b/c some bitmaps can bring wrong information
   328 		| for colorsUsed
   329 		*/
   330 		/* if ( biClrUsed == 0 ) {  */
   331 		biClrUsed = 1 << biBitCount;
   332 		/* } */
   333 		if ( biSize == 12 ) {
   334 			for ( i = 0; i < (int)biClrUsed; ++i ) {
   335 				SDL_RWread(src, &palette->colors[i].b, 1, 1);
   336 				SDL_RWread(src, &palette->colors[i].g, 1, 1);
   337 				SDL_RWread(src, &palette->colors[i].r, 1, 1);
   338 				palette->colors[i].unused = 0;
   339 			}	
   340 		} else {
   341 			for ( i = 0; i < (int)biClrUsed; ++i ) {
   342 				SDL_RWread(src, &palette->colors[i].b, 1, 1);
   343 				SDL_RWread(src, &palette->colors[i].g, 1, 1);
   344 				SDL_RWread(src, &palette->colors[i].r, 1, 1);
   345 				SDL_RWread(src, &palette->colors[i].unused, 1, 1);
   346 			}	
   347 		}
   348 		palette->ncolors = biClrUsed;
   349 	}
   350 
   351 	/* Read the surface pixels.  Note that the bmp image is upside down */
   352 	if ( SDL_RWseek(src, fp_offset+bfOffBits, SEEK_SET) < 0 ) {
   353 		SDL_Error(SDL_EFSEEK);
   354 		was_error = 1;
   355 		goto done;
   356 	}
   357 	if ((biCompression == BI_RLE4) || (biCompression == BI_RLE8)) {
   358 		was_error = readRlePixels(surface, src, biCompression == BI_RLE8);
   359 		if (was_error) SDL_SetError("Error reading from BMP");
   360 		goto done;
   361 	}
   362 	bits = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
   363 	switch (ExpandBMP) {
   364 		case 1:
   365 			bmpPitch = (biWidth + 7) >> 3;
   366 			pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
   367 			break;
   368 		case 4:
   369 			bmpPitch = (biWidth + 1) >> 1;
   370 			pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
   371 			break;
   372 		default:
   373 			pad  = ((surface->pitch%4) ?
   374 					(4-(surface->pitch%4)) : 0);
   375 			break;
   376 	}
   377 	while ( bits > (Uint8 *)surface->pixels ) {
   378 		bits -= surface->pitch;
   379 		switch (ExpandBMP) {
   380 			case 1:
   381 			case 4: {
   382 			Uint8 pixel = 0;
   383 			int   shift = (8-ExpandBMP);
   384 			for ( i=0; i<surface->w; ++i ) {
   385 				if ( i%(8/ExpandBMP) == 0 ) {
   386 					if ( !SDL_RWread(src, &pixel, 1, 1) ) {
   387 						SDL_SetError(
   388 					"Error reading from BMP");
   389 						was_error = 1;
   390 						goto done;
   391 					}
   392 				}
   393 				*(bits+i) = (pixel>>shift);
   394 				pixel <<= ExpandBMP;
   395 			} }
   396 			break;
   397 
   398 			default:
   399 			if ( SDL_RWread(src, bits, 1, surface->pitch)
   400 							 != surface->pitch ) {
   401 				SDL_Error(SDL_EFREAD);
   402 				was_error = 1;
   403 				goto done;
   404 			}
   405 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
   406 			/* Byte-swap the pixels if needed. Note that the 24bpp
   407 			   case has already been taken care of above. */
   408 			switch(biBitCount) {
   409 				case 15:
   410 				case 16: {
   411 				        Uint16 *pix = (Uint16 *)bits;
   412 					for(i = 0; i < surface->w; i++)
   413 					        pix[i] = SDL_Swap16(pix[i]);
   414 					break;
   415 				}
   416 
   417 				case 32: {
   418 				        Uint32 *pix = (Uint32 *)bits;
   419 					for(i = 0; i < surface->w; i++)
   420 					        pix[i] = SDL_Swap32(pix[i]);
   421 					break;
   422 				}
   423 			}
   424 #endif
   425 			break;
   426 		}
   427 		/* Skip padding bytes, ugh */
   428 		if ( pad ) {
   429 			Uint8 padbyte;
   430 			for ( i=0; i<pad; ++i ) {
   431 				SDL_RWread(src, &padbyte, 1, 1);
   432 			}
   433 		}
   434 	}
   435 done:
   436 	if ( was_error ) {
   437 		if ( src ) {
   438 			SDL_RWseek(src, fp_offset, SEEK_SET);
   439 		}
   440 		if ( surface ) {
   441 			SDL_FreeSurface(surface);
   442 		}
   443 		surface = NULL;
   444 	}
   445 	if ( freesrc && src ) {
   446 		SDL_RWclose(src);
   447 	}
   448 	return(surface);
   449 }
   450 
   451 /* Load a BMP type image from an SDL datasource */
   452 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
   453 {
   454 	return(LoadBMP_RW(src, 0));
   455 }
   456 
   457 #else
   458 
   459 /* See if an image is contained in a data source */
   460 int IMG_isBMP(SDL_RWops *src)
   461 {
   462 	return(0);
   463 }
   464 
   465 /* Load a BMP type image from an SDL datasource */
   466 SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src)
   467 {
   468 	return(NULL);
   469 }
   470 
   471 #endif /* LOAD_BMP */