IMG_lbm.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 28 Jan 2012 10:51:24 -0500
changeset 326 fa6129ca8503
parent 320 aa5d55b11751
child 343 5bf0f0d6a74e
permissions -rw-r--r--
Fixed SDL_image header location
     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 /* This is a ILBM image file loading framework
    23    Load IFF pictures, PBM & ILBM packing methods, with or without stencil
    24    Written by Daniel Morais ( Daniel AT Morais DOT com ) in September 2001.
    25    24 bits ILBM files support added by Marc Le Douarain (http://www.multimania.com/mavati)
    26    in December 2002.
    27    EHB and HAM (specific Amiga graphic chip modes) support added by Marc Le Douarain
    28    (http://www.multimania.com/mavati) in December 2003.
    29    Stencil and colorkey fixes by David Raulo (david.raulo AT free DOT fr) in February 2004.
    30    Buffer overflow fix in RLE decompression by David Raulo in January 2008.
    31 */
    32 
    33 #include <stdio.h>
    34 #include <stdlib.h>
    35 #include <string.h>
    36 
    37 #include "SDL_endian.h"
    38 #include "SDL_image.h"
    39 
    40 #ifdef LOAD_LBM
    41 
    42 
    43 #define MAXCOLORS 256
    44 
    45 /* Structure for an IFF picture ( BMHD = Bitmap Header ) */
    46 
    47 typedef struct
    48 {
    49     Uint16 w, h;		/* width & height of the bitmap in pixels */
    50     Sint16 x, y;		/* screen coordinates of the bitmap */
    51     Uint8 planes;		/* number of planes of the bitmap */
    52     Uint8 mask;			/* mask type ( 0 => no mask ) */
    53     Uint8 tcomp;		/* compression type */
    54     Uint8 pad1;			/* dummy value, for padding */
    55     Uint16 tcolor;		/* transparent color */
    56     Uint8 xAspect,		/* pixel aspect ratio */
    57           yAspect;
    58     Sint16  Lpage;		/* width of the screen in pixels */
    59     Sint16  Hpage;		/* height of the screen in pixels */
    60 } BMHD;
    61 
    62 int IMG_isLBM( SDL_RWops *src )
    63 {
    64 	int start;
    65 	int   is_LBM;
    66 	Uint8 magic[4+4+4];
    67 
    68 	if ( !src ) 
    69 		return 0;
    70 	start = SDL_RWtell(src);
    71 	is_LBM = 0;
    72 	if ( SDL_RWread( src, magic, sizeof(magic), 1 ) )
    73 	{
    74 		if ( !memcmp( magic, "FORM", 4 ) &&
    75 			( !memcmp( magic + 8, "PBM ", 4 ) ||
    76 			  !memcmp( magic + 8, "ILBM", 4 ) ) )
    77 		{
    78 			is_LBM = 1;
    79 		}
    80 	}
    81 	SDL_RWseek(src, start, RW_SEEK_SET);
    82 	return( is_LBM );
    83 }
    84 
    85 SDL_Surface *IMG_LoadLBM_RW( SDL_RWops *src )
    86 {
    87 	int start;
    88 	SDL_Surface *Image;
    89 	Uint8       id[4], pbm, colormap[MAXCOLORS*3], *MiniBuf, *ptr, count, color, msk;
    90 	Uint32      size, bytesloaded, nbcolors;
    91 	Uint32      i, j, bytesperline, nbplanes, stencil, plane, h;
    92 	Uint32      remainingbytes;
    93 	Uint32      width;
    94 	BMHD	      bmhd;
    95 	char        *error;
    96 	Uint8       flagHAM,flagEHB;
    97 
    98 	Image   = NULL;
    99 	error   = NULL;
   100 	MiniBuf = NULL;
   101 
   102 	if ( !src ) {
   103 		/* The error message has been set in SDL_RWFromFile */
   104 		return NULL;
   105 	}
   106 	start = SDL_RWtell(src);
   107 
   108 	if ( !SDL_RWread( src, id, 4, 1 ) )
   109 	{
   110 		error="error reading IFF chunk";
   111 		goto done;
   112 	}
   113 
   114 	/* Should be the size of the file minus 4+4 ( 'FORM'+size ) */
   115 	if ( !SDL_RWread( src, &size, 4, 1 ) )
   116 	{
   117 		error="error reading IFF chunk size";
   118 		goto done;
   119 	}
   120 
   121 	/* As size is not used here, no need to swap it */
   122 
   123 	if ( memcmp( id, "FORM", 4 ) != 0 )
   124 	{
   125 		error="not a IFF file";
   126 		goto done;
   127 	}
   128 
   129 	if ( !SDL_RWread( src, id, 4, 1 ) )
   130 	{
   131 		error="error reading IFF chunk";
   132 		goto done;
   133 	}
   134 
   135 	pbm = 0;
   136 
   137 	/* File format : PBM=Packed Bitmap, ILBM=Interleaved Bitmap */
   138 	if ( !memcmp( id, "PBM ", 4 ) ) pbm = 1;
   139 	else if ( memcmp( id, "ILBM", 4 ) )
   140 	{
   141 		error="not a IFF picture";
   142 		goto done;
   143 	}
   144 
   145 	nbcolors = 0;
   146 
   147 	memset( &bmhd, 0, sizeof( BMHD ) );
   148 	flagHAM = 0;
   149 	flagEHB = 0;
   150 
   151 	while ( memcmp( id, "BODY", 4 ) != 0 )
   152 	{
   153 		if ( !SDL_RWread( src, id, 4, 1 ) ) 
   154 		{
   155 			error="error reading IFF chunk";
   156 			goto done;
   157 		}
   158 
   159 		if ( !SDL_RWread( src, &size, 4, 1 ) )
   160 		{
   161 			error="error reading IFF chunk size";
   162 			goto done;
   163 		}
   164 
   165 		bytesloaded = 0;
   166 
   167 		size = SDL_SwapBE32( size );
   168 
   169 		if ( !memcmp( id, "BMHD", 4 ) ) /* Bitmap header */
   170 		{
   171 			if ( !SDL_RWread( src, &bmhd, sizeof( BMHD ), 1 ) )
   172 			{
   173 				error="error reading BMHD chunk";
   174 				goto done;
   175 			}
   176 
   177 			bytesloaded = sizeof( BMHD );
   178 
   179 			bmhd.w 		= SDL_SwapBE16( bmhd.w );
   180 			bmhd.h 		= SDL_SwapBE16( bmhd.h );
   181 			bmhd.x 		= SDL_SwapBE16( bmhd.x );
   182 			bmhd.y 		= SDL_SwapBE16( bmhd.y );
   183 			bmhd.tcolor = SDL_SwapBE16( bmhd.tcolor );
   184 			bmhd.Lpage 	= SDL_SwapBE16( bmhd.Lpage );
   185 			bmhd.Hpage 	= SDL_SwapBE16( bmhd.Hpage );
   186 		}
   187 
   188 		if ( !memcmp( id, "CMAP", 4 ) ) /* palette ( Color Map ) */
   189 		{
   190 			if ( !SDL_RWread( src, &colormap, size, 1 ) )
   191 			{
   192 				error="error reading CMAP chunk";
   193 				goto done;
   194 			}
   195 
   196 			bytesloaded = size;
   197 			nbcolors = size / 3;
   198 		}
   199 
   200 		if ( !memcmp( id, "CAMG", 4 ) ) /* Amiga ViewMode  */
   201 		{
   202 			Uint32 viewmodes;
   203 			if ( !SDL_RWread( src, &viewmodes, sizeof(viewmodes), 1 ) )
   204 			{
   205 				error="error reading CAMG chunk";
   206 				goto done;
   207 			}
   208 
   209 			bytesloaded = size;
   210 			viewmodes = SDL_SwapBE32( viewmodes );
   211 			if ( viewmodes & 0x0800 )
   212 				flagHAM = 1;
   213 			if ( viewmodes & 0x0080 )
   214 				flagEHB = 1;
   215 		}
   216 
   217 		if ( memcmp( id, "BODY", 4 ) )
   218 		{
   219 			if ( size & 1 )	++size;  	/* padding ! */
   220 			size -= bytesloaded;
   221 			/* skip the remaining bytes of this chunk */
   222 			if ( size )	SDL_RWseek( src, size, RW_SEEK_CUR );
   223 		}
   224 	}
   225 
   226 	/* compute some usefull values, based on the bitmap header */
   227 
   228 	width = ( bmhd.w + 15 ) & 0xFFFFFFF0;  /* Width in pixels modulo 16 */
   229 
   230 	bytesperline = ( ( bmhd.w + 15 ) / 16 ) * 2;
   231 
   232 	nbplanes = bmhd.planes;
   233 
   234 	if ( pbm )                         /* File format : 'Packed Bitmap' */
   235 	{
   236 		bytesperline *= 8;
   237 		nbplanes = 1;
   238 	}
   239 
   240 	stencil = (bmhd.mask & 1);   /* There is a mask ( 'stencil' ) */
   241 
   242 	/* Allocate memory for a temporary buffer ( used for
   243            decompression/deinterleaving ) */
   244 
   245 	MiniBuf = (void *)malloc( bytesperline * (nbplanes + stencil) );
   246 	if ( MiniBuf == NULL )
   247 	{
   248 		error="no enough memory for temporary buffer";
   249 		goto done;
   250 	}
   251 
   252 	if ( ( Image = SDL_CreateRGBSurface( SDL_SWSURFACE, width, bmhd.h, (bmhd.planes==24 || flagHAM==1)?24:8, 0, 0, 0, 0 ) ) == NULL )
   253 	   goto done;
   254 
   255 	if ( bmhd.mask & 2 )               /* There is a transparent color */
   256 		SDL_SetColorKey( Image, SDL_TRUE, bmhd.tcolor );
   257 
   258 	/* Update palette informations */
   259 
   260 	/* There is no palette in 24 bits ILBM file */
   261 	if ( nbcolors>0 && flagHAM==0 )
   262 	{
   263 		/* FIXME: Should this include the stencil? See comment below */
   264 		int nbrcolorsfinal = 1 << (nbplanes + stencil);
   265 		ptr = &colormap[0];
   266 
   267 		for ( i=0; i<nbcolors; i++ )
   268 		{
   269 			Image->format->palette->colors[i].r = *ptr++;
   270 			Image->format->palette->colors[i].g = *ptr++;
   271 			Image->format->palette->colors[i].b = *ptr++;
   272 		}
   273 
   274 		/* Amiga EHB mode (Extra-Half-Bright) */
   275 		/* 6 bitplanes mode with a 32 colors palette */
   276 		/* The 32 last colors are the same but divided by 2 */
   277 		/* Some Amiga pictures save 64 colors with 32 last wrong colors, */
   278 		/* they shouldn't !, and here we overwrite these 32 bad colors. */
   279 		if ( (nbcolors==32 || flagEHB ) && (1<<bmhd.planes)==64 )
   280 		{
   281 			nbcolors = 64;
   282 			ptr = &colormap[0];
   283 			for ( i=32; i<64; i++ )
   284 			{
   285 				Image->format->palette->colors[i].r = (*ptr++)/2;
   286 				Image->format->palette->colors[i].g = (*ptr++)/2;
   287 				Image->format->palette->colors[i].b = (*ptr++)/2;
   288 			}
   289 		}
   290 
   291 		/* If nbcolors < 2^nbplanes, repeat the colormap */
   292 		/* This happens when pictures have a stencil mask */
   293 		if ( nbrcolorsfinal > (1<<bmhd.planes) ) {
   294 			nbrcolorsfinal = (1<<bmhd.planes);
   295 		}
   296 		for ( i=nbcolors; i < (Uint32)nbrcolorsfinal; i++ )
   297 		{
   298 			Image->format->palette->colors[i].r = Image->format->palette->colors[i%nbcolors].r;
   299 			Image->format->palette->colors[i].g = Image->format->palette->colors[i%nbcolors].g;
   300 			Image->format->palette->colors[i].b = Image->format->palette->colors[i%nbcolors].b;
   301 		}
   302 		if ( !pbm )
   303 			Image->format->palette->ncolors = nbrcolorsfinal;
   304 	}
   305 
   306 	/* Get the bitmap */
   307 
   308 	for ( h=0; h < bmhd.h; h++ )
   309 	{
   310 		/* uncompress the datas of each planes */
   311 
   312 		for ( plane=0; plane < (nbplanes+stencil); plane++ )
   313 		{
   314 			ptr = MiniBuf + ( plane * bytesperline );
   315 
   316 			remainingbytes = bytesperline;
   317 
   318 			if ( bmhd.tcomp == 1 )	    /* Datas are compressed */
   319 			{
   320 				do
   321 				{
   322 					if ( !SDL_RWread( src, &count, 1, 1 ) )
   323 					{
   324 						error="error reading BODY chunk";
   325 						goto done;
   326 					}
   327 
   328 					if ( count & 0x80 )
   329 					{
   330 						count ^= 0xFF;
   331 						count += 2; /* now it */
   332 
   333 						if ( ( count > remainingbytes ) || !SDL_RWread( src, &color, 1, 1 ) )
   334 						{
   335 							error="error reading BODY chunk";
   336 							goto done;
   337 						}
   338 						memset( ptr, color, count );
   339 					}
   340 					else
   341 					{
   342 						++count;
   343 
   344 						if ( ( count > remainingbytes ) || !SDL_RWread( src, ptr, count, 1 ) )
   345 						{
   346 						   error="error reading BODY chunk";
   347 							goto done;
   348 						}
   349 					}
   350 
   351 					ptr += count;
   352 					remainingbytes -= count;
   353 
   354 				} while ( remainingbytes > 0 );
   355 			}
   356 			else
   357 			{
   358 				if ( !SDL_RWread( src, ptr, bytesperline, 1 ) )
   359 				{
   360 					error="error reading BODY chunk";
   361 					goto done;
   362 				}
   363 			}
   364 		}
   365 
   366 		/* One line has been read, store it ! */
   367 
   368 		ptr = Image->pixels;
   369 		if ( nbplanes==24 || flagHAM==1 )
   370 			ptr += h * width * 3;
   371 		else
   372 			ptr += h * width;
   373 
   374 		if ( pbm )                 /* File format : 'Packed Bitmap' */
   375 		{
   376 		   memcpy( ptr, MiniBuf, width );
   377 		}
   378 		else		/* We have to un-interlace the bits ! */
   379 		{
   380 			if ( nbplanes!=24 && flagHAM==0 )
   381 			{
   382 				size = ( width + 7 ) / 8;
   383 
   384 				for ( i=0; i < size; i++ )
   385 				{
   386 					memset( ptr, 0, 8 );
   387 
   388 					for ( plane=0; plane < (nbplanes + stencil); plane++ )
   389 					{
   390 						color = *( MiniBuf + i + ( plane * bytesperline ) );
   391 						msk = 0x80;
   392 
   393 						for ( j=0; j<8; j++ )
   394 						{
   395 							if ( ( plane + j ) <= 7 ) ptr[j] |= (Uint8)( color & msk ) >> ( 7 - plane - j );
   396 							else 	                    ptr[j] |= (Uint8)( color & msk ) << ( plane + j - 7 );
   397 
   398 							msk >>= 1;
   399 						}
   400 					}
   401 					ptr += 8;
   402 				}
   403 			}
   404 			else
   405 			{
   406 				Uint32 finalcolor = 0;
   407 				size = ( width + 7 ) / 8;
   408 				/* 24 bitplanes ILBM : R0...R7,G0...G7,B0...B7 */
   409 				/* or HAM (6 bitplanes) or HAM8 (8 bitplanes) modes */
   410 				for ( i=0; i<width; i=i+8 )
   411 				{
   412 					Uint8 maskBit = 0x80;
   413 					for ( j=0; j<8; j++ )
   414 					{
   415 						Uint32 pixelcolor = 0;
   416 						Uint32 maskColor = 1;
   417 						Uint8 dataBody;
   418 						for ( plane=0; plane < nbplanes; plane++ )
   419 						{
   420 							dataBody = MiniBuf[ plane*size+i/8 ];
   421 							if ( dataBody&maskBit )
   422 								pixelcolor = pixelcolor | maskColor;
   423 							maskColor = maskColor<<1;
   424 						}
   425 						/* HAM : 12 bits RGB image (4 bits per color component) */
   426 						/* HAM8 : 18 bits RGB image (6 bits per color component) */
   427 						if ( flagHAM )
   428 						{
   429 							switch( pixelcolor>>(nbplanes-2) )
   430 							{
   431 								case 0: /* take direct color from palette */
   432 									finalcolor = colormap[ pixelcolor*3 ] + (colormap[ pixelcolor*3+1 ]<<8) + (colormap[ pixelcolor*3+2 ]<<16);
   433 									break;
   434 								case 1: /* modify only blue component */
   435 									finalcolor = finalcolor&0x00FFFF;
   436 									finalcolor = finalcolor | (pixelcolor<<(16+(10-nbplanes)));
   437 									break;
   438 								case 2: /* modify only red component */
   439 									finalcolor = finalcolor&0xFFFF00;
   440 									finalcolor = finalcolor | pixelcolor<<(10-nbplanes);
   441 									break;
   442 								case 3: /* modify only green component */
   443 									finalcolor = finalcolor&0xFF00FF;
   444 									finalcolor = finalcolor | (pixelcolor<<(8+(10-nbplanes)));
   445 									break;
   446 							}
   447 						}
   448 						else
   449 						{
   450 							finalcolor = pixelcolor;
   451 						}
   452 						if ( SDL_BYTEORDER == SDL_LIL_ENDIAN )
   453 						{
   454 							*ptr++ = (Uint8)(finalcolor>>16);
   455 							*ptr++ = (Uint8)(finalcolor>>8);
   456 							*ptr++ = (Uint8)(finalcolor);
   457 						}
   458 						else
   459 						{
   460 							*ptr++ = (Uint8)(finalcolor);
   461 							*ptr++ = (Uint8)(finalcolor>>8);
   462 							*ptr++ = (Uint8)(finalcolor>>16);
   463 						}
   464 
   465 						maskBit = maskBit>>1;
   466 					}
   467 				}
   468 			}
   469 		}
   470 	}
   471 
   472 done:
   473 
   474 	if ( MiniBuf ) free( MiniBuf );
   475 
   476 	if ( error )
   477 	{
   478 		SDL_RWseek(src, start, RW_SEEK_SET);
   479 		if ( Image ) {
   480 			SDL_FreeSurface( Image );
   481 			Image = NULL;
   482 		}
   483 		IMG_SetError( error );
   484 	}
   485 
   486 	return( Image );
   487 }
   488 
   489 #else /* LOAD_LBM */
   490 
   491 /* See if an image is contained in a data source */
   492 int IMG_isLBM(SDL_RWops *src)
   493 {
   494 	return(0);
   495 }
   496 
   497 /* Load an IFF type image from an SDL datasource */
   498 SDL_Surface *IMG_LoadLBM_RW(SDL_RWops *src)
   499 {
   500 	return(NULL);
   501 }
   502 
   503 #endif /* LOAD_LBM */