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