IMG_lbm.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 10 May 2010 22:40:18 -0700
changeset 251 a6565b353095
parent 236 cce1251de477
child 280 ec4ae96c100c
permissions -rw-r--r--
Fixed decoding HAM6 image with masking
     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, RW_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, stencil, 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, RW_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 	stencil = (bmhd.mask & 1);   /* There is a mask ( 'stencil' ) */
   242 
   243 	/* Allocate memory for a temporary buffer ( used for
   244            decompression/deinterleaving ) */
   245 
   246 	MiniBuf = (void *)malloc( bytesperline * (nbplanes + stencil) );
   247 	if ( MiniBuf == NULL )
   248 	{
   249 		error="no enough memory for temporary buffer";
   250 		goto done;
   251 	}
   252 
   253 	if ( ( Image = SDL_CreateRGBSurface( SDL_SWSURFACE, width, bmhd.h, (bmhd.planes==24 || flagHAM==1)?24:8, 0, 0, 0, 0 ) ) == NULL )
   254 	   goto done;
   255 
   256 	if ( bmhd.mask & 2 )               /* There is a transparent color */
   257 		SDL_SetColorKey( Image, SDL_SRCCOLORKEY, bmhd.tcolor );
   258 
   259 	/* Update palette informations */
   260 
   261 	/* There is no palette in 24 bits ILBM file */
   262 	if ( nbcolors>0 && flagHAM==0 )
   263 	{
   264 		/* FIXME: Should this include the stencil? See comment below */
   265 		int nbrcolorsfinal = 1 << (nbplanes + stencil);
   266 		ptr = &colormap[0];
   267 
   268 		for ( i=0; i<nbcolors; i++ )
   269 		{
   270 			Image->format->palette->colors[i].r = *ptr++;
   271 			Image->format->palette->colors[i].g = *ptr++;
   272 			Image->format->palette->colors[i].b = *ptr++;
   273 		}
   274 
   275 		/* Amiga EHB mode (Extra-Half-Bright) */
   276 		/* 6 bitplanes mode with a 32 colors palette */
   277 		/* The 32 last colors are the same but divided by 2 */
   278 		/* Some Amiga pictures save 64 colors with 32 last wrong colors, */
   279 		/* they shouldn't !, and here we overwrite these 32 bad colors. */
   280 		if ( (nbcolors==32 || flagEHB ) && (1<<bmhd.planes)==64 )
   281 		{
   282 			nbcolors = 64;
   283 			ptr = &colormap[0];
   284 			for ( i=32; i<64; i++ )
   285 			{
   286 				Image->format->palette->colors[i].r = (*ptr++)/2;
   287 				Image->format->palette->colors[i].g = (*ptr++)/2;
   288 				Image->format->palette->colors[i].b = (*ptr++)/2;
   289 			}
   290 		}
   291 
   292 		/* If nbcolors < 2^nbplanes, repeat the colormap */
   293 		/* This happens when pictures have a stencil mask */
   294 		if ( nbrcolorsfinal > (1<<bmhd.planes) ) {
   295 			nbrcolorsfinal = (1<<bmhd.planes);
   296 		}
   297 		for ( i=nbcolors; i < (Uint32)nbrcolorsfinal; i++ )
   298 		{
   299 			Image->format->palette->colors[i].r = Image->format->palette->colors[i%nbcolors].r;
   300 			Image->format->palette->colors[i].g = Image->format->palette->colors[i%nbcolors].g;
   301 			Image->format->palette->colors[i].b = Image->format->palette->colors[i%nbcolors].b;
   302 		}
   303 		if ( !pbm )
   304 			Image->format->palette->ncolors = nbrcolorsfinal;
   305 	}
   306 
   307 	/* Get the bitmap */
   308 
   309 	for ( h=0; h < bmhd.h; h++ )
   310 	{
   311 		/* uncompress the datas of each planes */
   312 
   313 		for ( plane=0; plane < (nbplanes+stencil); plane++ )
   314 		{
   315 			ptr = MiniBuf + ( plane * bytesperline );
   316 
   317 			remainingbytes = bytesperline;
   318 
   319 			if ( bmhd.tcomp == 1 )	    /* Datas are compressed */
   320 			{
   321 				do
   322 				{
   323 					if ( !SDL_RWread( src, &count, 1, 1 ) )
   324 					{
   325 						error="error reading BODY chunk";
   326 						goto done;
   327 					}
   328 
   329 					if ( count & 0x80 )
   330 					{
   331 						count ^= 0xFF;
   332 						count += 2; /* now it */
   333 
   334 						if ( ( count > remainingbytes ) || !SDL_RWread( src, &color, 1, 1 ) )
   335 						{
   336 							error="error reading BODY chunk";
   337 							goto done;
   338 						}
   339 						memset( ptr, color, count );
   340 					}
   341 					else
   342 					{
   343 						++count;
   344 
   345 						if ( ( count > remainingbytes ) || !SDL_RWread( src, ptr, count, 1 ) )
   346 						{
   347 						   error="error reading BODY chunk";
   348 							goto done;
   349 						}
   350 					}
   351 
   352 					ptr += count;
   353 					remainingbytes -= count;
   354 
   355 				} while ( remainingbytes > 0 );
   356 			}
   357 			else
   358 			{
   359 				if ( !SDL_RWread( src, ptr, bytesperline, 1 ) )
   360 				{
   361 					error="error reading BODY chunk";
   362 					goto done;
   363 				}
   364 			}
   365 		}
   366 
   367 		/* One line has been read, store it ! */
   368 
   369 		ptr = Image->pixels;
   370 		if ( nbplanes==24 || flagHAM==1 )
   371 			ptr += h * width * 3;
   372 		else
   373 			ptr += h * width;
   374 
   375 		if ( pbm )                 /* File format : 'Packed Bitmap' */
   376 		{
   377 		   memcpy( ptr, MiniBuf, width );
   378 		}
   379 		else		/* We have to un-interlace the bits ! */
   380 		{
   381 			if ( nbplanes!=24 && flagHAM==0 )
   382 			{
   383 				size = ( width + 7 ) / 8;
   384 
   385 				for ( i=0; i < size; i++ )
   386 				{
   387 					memset( ptr, 0, 8 );
   388 
   389 					for ( plane=0; plane < (nbplanes + stencil); plane++ )
   390 					{
   391 						color = *( MiniBuf + i + ( plane * bytesperline ) );
   392 						msk = 0x80;
   393 
   394 						for ( j=0; j<8; j++ )
   395 						{
   396 							if ( ( plane + j ) <= 7 ) ptr[j] |= (Uint8)( color & msk ) >> ( 7 - plane - j );
   397 							else 	                    ptr[j] |= (Uint8)( color & msk ) << ( plane + j - 7 );
   398 
   399 							msk >>= 1;
   400 						}
   401 					}
   402 					ptr += 8;
   403 				}
   404 			}
   405 			else
   406 			{
   407 				Uint32 finalcolor = 0;
   408 				size = ( width + 7 ) / 8;
   409 				/* 24 bitplanes ILBM : R0...R7,G0...G7,B0...B7 */
   410 				/* or HAM (6 bitplanes) or HAM8 (8 bitplanes) modes */
   411 				for ( i=0; i<width; i=i+8 )
   412 				{
   413 					Uint8 maskBit = 0x80;
   414 					for ( j=0; j<8; j++ )
   415 					{
   416 						Uint32 pixelcolor = 0;
   417 						Uint32 maskColor = 1;
   418 						Uint8 dataBody;
   419 						for ( plane=0; plane < nbplanes; plane++ )
   420 						{
   421 							dataBody = MiniBuf[ plane*size+i/8 ];
   422 							if ( dataBody&maskBit )
   423 								pixelcolor = pixelcolor | maskColor;
   424 							maskColor = maskColor<<1;
   425 						}
   426 						/* HAM : 12 bits RGB image (4 bits per color component) */
   427 						/* HAM8 : 18 bits RGB image (6 bits per color component) */
   428 						if ( flagHAM )
   429 						{
   430 							switch( pixelcolor>>(nbplanes-2) )
   431 							{
   432 								case 0: /* take direct color from palette */
   433 									finalcolor = colormap[ pixelcolor*3 ] + (colormap[ pixelcolor*3+1 ]<<8) + (colormap[ pixelcolor*3+2 ]<<16);
   434 									break;
   435 								case 1: /* modify only blue component */
   436 									finalcolor = finalcolor&0x00FFFF;
   437 									finalcolor = finalcolor | (pixelcolor<<(16+(10-nbplanes)));
   438 									break;
   439 								case 2: /* modify only red component */
   440 									finalcolor = finalcolor&0xFFFF00;
   441 									finalcolor = finalcolor | pixelcolor<<(10-nbplanes);
   442 									break;
   443 								case 3: /* modify only green component */
   444 									finalcolor = finalcolor&0xFF00FF;
   445 									finalcolor = finalcolor | (pixelcolor<<(8+(10-nbplanes)));
   446 									break;
   447 							}
   448 						}
   449 						else
   450 						{
   451 							finalcolor = pixelcolor;
   452 						}
   453 						if ( SDL_BYTEORDER == SDL_LIL_ENDIAN )
   454 						{
   455 							*ptr++ = (Uint8)(finalcolor>>16);
   456 							*ptr++ = (Uint8)(finalcolor>>8);
   457 							*ptr++ = (Uint8)(finalcolor);
   458 						}
   459 						else
   460 						{
   461 							*ptr++ = (Uint8)(finalcolor);
   462 							*ptr++ = (Uint8)(finalcolor>>8);
   463 							*ptr++ = (Uint8)(finalcolor>>16);
   464 						}
   465 
   466 						maskBit = maskBit>>1;
   467 					}
   468 				}
   469 			}
   470 		}
   471 	}
   472 
   473 done:
   474 
   475 	if ( MiniBuf ) free( MiniBuf );
   476 
   477 	if ( error )
   478 	{
   479 		SDL_RWseek(src, start, RW_SEEK_SET);
   480 		if ( Image ) {
   481 			SDL_FreeSurface( Image );
   482 			Image = NULL;
   483 		}
   484 		IMG_SetError( error );
   485 	}
   486 
   487 	return( Image );
   488 }
   489 
   490 #else /* LOAD_LBM */
   491 
   492 /* See if an image is contained in a data source */
   493 int IMG_isLBM(SDL_RWops *src)
   494 {
   495 	return(0);
   496 }
   497 
   498 /* Load an IFF type image from an SDL datasource */
   499 SDL_Surface *IMG_LoadLBM_RW(SDL_RWops *src)
   500 {
   501 	return(NULL);
   502 }
   503 
   504 #endif /* LOAD_LBM */