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