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