IMG_lbm.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 09 Feb 2004 12:40:36 +0000
changeset 99 9256da39d67e
parent 98 9f94c4674cc9
child 103 27f1072602da
permissions -rw-r--r--
Date: Sat, 7 Feb 2004 13:26:01 +0100
From: delq
Subject: Re: [SDL] better ILBM support for SDL_Image

- fixed reading of colorkeyed images (they were previously assumed to
have a stencil bitplane, but since the body didn't have it, the decoding
failed). Added a missing call to SDL_SetColorKey() too.

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