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