/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2009 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Sam Lantinga slouken@libsdl.org */ /* This is a ILBM image file loading framework Load IFF pictures, PBM & ILBM packing methods, with or without stencil Written by Daniel Morais ( Daniel AT Morais DOT com ) in September 2001. 24 bits ILBM files support added by Marc Le Douarain (http://www.multimania.com/mavati) in December 2002. EHB and HAM (specific Amiga graphic chip modes) support added by Marc Le Douarain (http://www.multimania.com/mavati) in December 2003. Stencil and colorkey fixes by David Raulo (david.raulo AT free DOT fr) in February 2004. Buffer overflow fix in RLE decompression by David Raulo in January 2008. */ #include #include #include #include "SDL_endian.h" #include "SDL_image.h" #ifdef LOAD_LBM #define MAXCOLORS 256 /* Structure for an IFF picture ( BMHD = Bitmap Header ) */ typedef struct { Uint16 w, h; /* width & height of the bitmap in pixels */ Sint16 x, y; /* screen coordinates of the bitmap */ Uint8 planes; /* number of planes of the bitmap */ Uint8 mask; /* mask type ( 0 => no mask ) */ Uint8 tcomp; /* compression type */ Uint8 pad1; /* dummy value, for padding */ Uint16 tcolor; /* transparent color */ Uint8 xAspect, /* pixel aspect ratio */ yAspect; Sint16 Lpage; /* width of the screen in pixels */ Sint16 Hpage; /* height of the screen in pixels */ } BMHD; int IMG_isLBM( SDL_RWops *src ) { int start; int is_LBM; Uint8 magic[4+4+4]; if ( !src ) return 0; start = SDL_RWtell(src); is_LBM = 0; if ( SDL_RWread( src, magic, sizeof(magic), 1 ) ) { if ( !memcmp( magic, "FORM", 4 ) && ( !memcmp( magic + 8, "PBM ", 4 ) || !memcmp( magic + 8, "ILBM", 4 ) ) ) { is_LBM = 1; } } SDL_RWseek(src, start, RW_SEEK_SET); return( is_LBM ); } SDL_Surface *IMG_LoadLBM_RW( SDL_RWops *src ) { int start; SDL_Surface *Image; Uint8 id[4], pbm, colormap[MAXCOLORS*3], *MiniBuf, *ptr, count, color, msk; Uint32 size, bytesloaded, nbcolors; Uint32 i, j, bytesperline, nbplanes, stencil, plane, h; Uint32 remainingbytes; Uint32 width; BMHD bmhd; char *error; Uint8 flagHAM,flagEHB; Image = NULL; error = NULL; MiniBuf = NULL; if ( !src ) { /* The error message has been set in SDL_RWFromFile */ return NULL; } start = SDL_RWtell(src); if ( !SDL_RWread( src, id, 4, 1 ) ) { error="error reading IFF chunk"; goto done; } /* Should be the size of the file minus 4+4 ( 'FORM'+size ) */ if ( !SDL_RWread( src, &size, 4, 1 ) ) { error="error reading IFF chunk size"; goto done; } /* As size is not used here, no need to swap it */ if ( memcmp( id, "FORM", 4 ) != 0 ) { error="not a IFF file"; goto done; } if ( !SDL_RWread( src, id, 4, 1 ) ) { error="error reading IFF chunk"; goto done; } pbm = 0; /* File format : PBM=Packed Bitmap, ILBM=Interleaved Bitmap */ if ( !memcmp( id, "PBM ", 4 ) ) pbm = 1; else if ( memcmp( id, "ILBM", 4 ) ) { error="not a IFF picture"; goto done; } nbcolors = 0; memset( &bmhd, 0, sizeof( BMHD ) ); flagHAM = 0; flagEHB = 0; while ( memcmp( id, "BODY", 4 ) != 0 ) { if ( !SDL_RWread( src, id, 4, 1 ) ) { error="error reading IFF chunk"; goto done; } if ( !SDL_RWread( src, &size, 4, 1 ) ) { error="error reading IFF chunk size"; goto done; } bytesloaded = 0; size = SDL_SwapBE32( size ); if ( !memcmp( id, "BMHD", 4 ) ) /* Bitmap header */ { if ( !SDL_RWread( src, &bmhd, sizeof( BMHD ), 1 ) ) { error="error reading BMHD chunk"; goto done; } bytesloaded = sizeof( BMHD ); bmhd.w = SDL_SwapBE16( bmhd.w ); bmhd.h = SDL_SwapBE16( bmhd.h ); bmhd.x = SDL_SwapBE16( bmhd.x ); bmhd.y = SDL_SwapBE16( bmhd.y ); bmhd.tcolor = SDL_SwapBE16( bmhd.tcolor ); bmhd.Lpage = SDL_SwapBE16( bmhd.Lpage ); bmhd.Hpage = SDL_SwapBE16( bmhd.Hpage ); } if ( !memcmp( id, "CMAP", 4 ) ) /* palette ( Color Map ) */ { if ( !SDL_RWread( src, &colormap, size, 1 ) ) { error="error reading CMAP chunk"; goto done; } bytesloaded = size; nbcolors = size / 3; } if ( !memcmp( id, "CAMG", 4 ) ) /* Amiga ViewMode */ { Uint32 viewmodes; if ( !SDL_RWread( src, &viewmodes, sizeof(viewmodes), 1 ) ) { error="error reading CAMG chunk"; goto done; } bytesloaded = size; viewmodes = SDL_SwapBE32( viewmodes ); if ( viewmodes & 0x0800 ) flagHAM = 1; if ( viewmodes & 0x0080 ) flagEHB = 1; } if ( memcmp( id, "BODY", 4 ) ) { if ( size & 1 ) ++size; /* padding ! */ size -= bytesloaded; /* skip the remaining bytes of this chunk */ if ( size ) SDL_RWseek( src, size, RW_SEEK_CUR ); } } /* compute some usefull values, based on the bitmap header */ width = ( bmhd.w + 15 ) & 0xFFFFFFF0; /* Width in pixels modulo 16 */ bytesperline = ( ( bmhd.w + 15 ) / 16 ) * 2; nbplanes = bmhd.planes; if ( pbm ) /* File format : 'Packed Bitmap' */ { bytesperline *= 8; nbplanes = 1; } stencil = (bmhd.mask & 1); /* There is a mask ( 'stencil' ) */ /* Allocate memory for a temporary buffer ( used for decompression/deinterleaving ) */ MiniBuf = (void *)malloc( bytesperline * (nbplanes + stencil) ); if ( MiniBuf == NULL ) { error="no enough memory for temporary buffer"; goto done; } if ( ( Image = SDL_CreateRGBSurface( SDL_SWSURFACE, width, bmhd.h, (bmhd.planes==24 || flagHAM==1)?24:8, 0, 0, 0, 0 ) ) == NULL ) goto done; if ( bmhd.mask & 2 ) /* There is a transparent color */ SDL_SetColorKey( Image, SDL_SRCCOLORKEY, bmhd.tcolor ); /* Update palette informations */ /* There is no palette in 24 bits ILBM file */ if ( nbcolors>0 && flagHAM==0 ) { /* FIXME: Should this include the stencil? See comment below */ int nbrcolorsfinal = 1 << (nbplanes + stencil); ptr = &colormap[0]; for ( i=0; iformat->palette->colors[i].r = *ptr++; Image->format->palette->colors[i].g = *ptr++; Image->format->palette->colors[i].b = *ptr++; } /* Amiga EHB mode (Extra-Half-Bright) */ /* 6 bitplanes mode with a 32 colors palette */ /* The 32 last colors are the same but divided by 2 */ /* Some Amiga pictures save 64 colors with 32 last wrong colors, */ /* they shouldn't !, and here we overwrite these 32 bad colors. */ if ( (nbcolors==32 || flagEHB ) && (1<format->palette->colors[i].r = (*ptr++)/2; Image->format->palette->colors[i].g = (*ptr++)/2; Image->format->palette->colors[i].b = (*ptr++)/2; } } /* If nbcolors < 2^nbplanes, repeat the colormap */ /* This happens when pictures have a stencil mask */ if ( nbrcolorsfinal > (1<format->palette->colors[i].r = Image->format->palette->colors[i%nbcolors].r; Image->format->palette->colors[i].g = Image->format->palette->colors[i%nbcolors].g; Image->format->palette->colors[i].b = Image->format->palette->colors[i%nbcolors].b; } if ( !pbm ) Image->format->palette->ncolors = nbrcolorsfinal; } /* Get the bitmap */ for ( h=0; h < bmhd.h; h++ ) { /* uncompress the datas of each planes */ for ( plane=0; plane < (nbplanes+stencil); plane++ ) { ptr = MiniBuf + ( plane * bytesperline ); remainingbytes = bytesperline; if ( bmhd.tcomp == 1 ) /* Datas are compressed */ { do { if ( !SDL_RWread( src, &count, 1, 1 ) ) { error="error reading BODY chunk"; goto done; } if ( count & 0x80 ) { count ^= 0xFF; count += 2; /* now it */ if ( ( count > remainingbytes ) || !SDL_RWread( src, &color, 1, 1 ) ) { error="error reading BODY chunk"; goto done; } memset( ptr, color, count ); } else { ++count; if ( ( count > remainingbytes ) || !SDL_RWread( src, ptr, count, 1 ) ) { error="error reading BODY chunk"; goto done; } } ptr += count; remainingbytes -= count; } while ( remainingbytes > 0 ); } else { if ( !SDL_RWread( src, ptr, bytesperline, 1 ) ) { error="error reading BODY chunk"; goto done; } } } /* One line has been read, store it ! */ ptr = Image->pixels; if ( nbplanes==24 || flagHAM==1 ) ptr += h * width * 3; else ptr += h * width; if ( pbm ) /* File format : 'Packed Bitmap' */ { memcpy( ptr, MiniBuf, width ); } else /* We have to un-interlace the bits ! */ { if ( nbplanes!=24 && flagHAM==0 ) { size = ( width + 7 ) / 8; for ( i=0; i < size; i++ ) { memset( ptr, 0, 8 ); for ( plane=0; plane < (nbplanes + stencil); plane++ ) { color = *( MiniBuf + i + ( plane * bytesperline ) ); msk = 0x80; for ( j=0; j<8; j++ ) { if ( ( plane + j ) <= 7 ) ptr[j] |= (Uint8)( color & msk ) >> ( 7 - plane - j ); else ptr[j] |= (Uint8)( color & msk ) << ( plane + j - 7 ); msk >>= 1; } } ptr += 8; } } else { Uint32 finalcolor = 0; size = ( width + 7 ) / 8; /* 24 bitplanes ILBM : R0...R7,G0...G7,B0...B7 */ /* or HAM (6 bitplanes) or HAM8 (8 bitplanes) modes */ for ( i=0; i>(nbplanes-2) ) { case 0: /* take direct color from palette */ finalcolor = colormap[ pixelcolor*3 ] + (colormap[ pixelcolor*3+1 ]<<8) + (colormap[ pixelcolor*3+2 ]<<16); break; case 1: /* modify only blue component */ finalcolor = finalcolor&0x00FFFF; finalcolor = finalcolor | (pixelcolor<<(16+(10-nbplanes))); break; case 2: /* modify only red component */ finalcolor = finalcolor&0xFFFF00; finalcolor = finalcolor | pixelcolor<<(10-nbplanes); break; case 3: /* modify only green component */ finalcolor = finalcolor&0xFF00FF; finalcolor = finalcolor | (pixelcolor<<(8+(10-nbplanes))); break; } } else { finalcolor = pixelcolor; } if ( SDL_BYTEORDER == SDL_LIL_ENDIAN ) { *ptr++ = (Uint8)(finalcolor>>16); *ptr++ = (Uint8)(finalcolor>>8); *ptr++ = (Uint8)(finalcolor); } else { *ptr++ = (Uint8)(finalcolor); *ptr++ = (Uint8)(finalcolor>>8); *ptr++ = (Uint8)(finalcolor>>16); } maskBit = maskBit>>1; } } } } } done: if ( MiniBuf ) free( MiniBuf ); if ( error ) { SDL_RWseek(src, start, RW_SEEK_SET); if ( Image ) { SDL_FreeSurface( Image ); Image = NULL; } IMG_SetError( error ); } return( Image ); } #else /* LOAD_LBM */ /* See if an image is contained in a data source */ int IMG_isLBM(SDL_RWops *src) { return(0); } /* Load an IFF type image from an SDL datasource */ SDL_Surface *IMG_LoadLBM_RW(SDL_RWops *src) { return(NULL); } #endif /* LOAD_LBM */