2 SDL_image: An example image loading library for use with SDL
3 Copyright (C) 1997-2009 Sam Lantinga
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 /* This is a ILBM image file loading framework
24 Load IFF pictures, PBM & ILBM packing methods, with or without stencil
25 Written by Daniel Morais ( Daniel AT Morais DOT com ) in September 2001.
26 24 bits ILBM files support added by Marc Le Douarain (http://www.multimania.com/mavati)
28 EHB and HAM (specific Amiga graphic chip modes) support added by Marc Le Douarain
29 (http://www.multimania.com/mavati) in December 2003.
30 Stencil and colorkey fixes by David Raulo (david.raulo AT free DOT fr) in February 2004.
31 Buffer overflow fix in RLE decompression by David Raulo in January 2008.
38 #include "SDL_endian.h"
39 #include "SDL_image.h"
46 /* Structure for an IFF picture ( BMHD = Bitmap Header ) */
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 */
59 Sint16 Lpage; /* width of the screen in pixels */
60 Sint16 Hpage; /* height of the screen in pixels */
63 int IMG_isLBM( SDL_RWops *src )
71 start = SDL_RWtell(src);
73 if ( SDL_RWread( src, magic, sizeof(magic), 1 ) )
75 if ( !memcmp( magic, "FORM", 4 ) &&
76 ( !memcmp( magic + 8, "PBM ", 4 ) ||
77 !memcmp( magic + 8, "ILBM", 4 ) ) )
82 SDL_RWseek(src, start, RW_SEEK_SET);
86 SDL_Surface *IMG_LoadLBM_RW( SDL_RWops *src )
90 Uint8 id[4], pbm, colormap[MAXCOLORS*3], *MiniBuf, *ptr, count, color, msk;
91 Uint32 size, bytesloaded, nbcolors;
92 Uint32 i, j, bytesperline, nbplanes, stencil, plane, h;
93 Uint32 remainingbytes;
97 Uint8 flagHAM,flagEHB;
104 /* The error message has been set in SDL_RWFromFile */
107 start = SDL_RWtell(src);
109 if ( !SDL_RWread( src, id, 4, 1 ) )
111 error="error reading IFF chunk";
115 /* Should be the size of the file minus 4+4 ( 'FORM'+size ) */
116 if ( !SDL_RWread( src, &size, 4, 1 ) )
118 error="error reading IFF chunk size";
122 /* As size is not used here, no need to swap it */
124 if ( memcmp( id, "FORM", 4 ) != 0 )
126 error="not a IFF file";
130 if ( !SDL_RWread( src, id, 4, 1 ) )
132 error="error reading IFF chunk";
138 /* File format : PBM=Packed Bitmap, ILBM=Interleaved Bitmap */
139 if ( !memcmp( id, "PBM ", 4 ) ) pbm = 1;
140 else if ( memcmp( id, "ILBM", 4 ) )
142 error="not a IFF picture";
148 memset( &bmhd, 0, sizeof( BMHD ) );
152 while ( memcmp( id, "BODY", 4 ) != 0 )
154 if ( !SDL_RWread( src, id, 4, 1 ) )
156 error="error reading IFF chunk";
160 if ( !SDL_RWread( src, &size, 4, 1 ) )
162 error="error reading IFF chunk size";
168 size = SDL_SwapBE32( size );
170 if ( !memcmp( id, "BMHD", 4 ) ) /* Bitmap header */
172 if ( !SDL_RWread( src, &bmhd, sizeof( BMHD ), 1 ) )
174 error="error reading BMHD chunk";
178 bytesloaded = sizeof( BMHD );
180 bmhd.w = SDL_SwapBE16( bmhd.w );
181 bmhd.h = SDL_SwapBE16( bmhd.h );
182 bmhd.x = SDL_SwapBE16( bmhd.x );
183 bmhd.y = SDL_SwapBE16( bmhd.y );
184 bmhd.tcolor = SDL_SwapBE16( bmhd.tcolor );
185 bmhd.Lpage = SDL_SwapBE16( bmhd.Lpage );
186 bmhd.Hpage = SDL_SwapBE16( bmhd.Hpage );
189 if ( !memcmp( id, "CMAP", 4 ) ) /* palette ( Color Map ) */
191 if ( !SDL_RWread( src, &colormap, size, 1 ) )
193 error="error reading CMAP chunk";
201 if ( !memcmp( id, "CAMG", 4 ) ) /* Amiga ViewMode */
204 if ( !SDL_RWread( src, &viewmodes, sizeof(viewmodes), 1 ) )
206 error="error reading CAMG chunk";
211 viewmodes = SDL_SwapBE32( viewmodes );
212 if ( viewmodes & 0x0800 )
214 if ( viewmodes & 0x0080 )
218 if ( memcmp( id, "BODY", 4 ) )
220 if ( size & 1 ) ++size; /* padding ! */
222 /* skip the remaining bytes of this chunk */
223 if ( size ) SDL_RWseek( src, size, RW_SEEK_CUR );
227 /* compute some usefull values, based on the bitmap header */
229 width = ( bmhd.w + 15 ) & 0xFFFFFFF0; /* Width in pixels modulo 16 */
231 bytesperline = ( ( bmhd.w + 15 ) / 16 ) * 2;
233 nbplanes = bmhd.planes;
235 if ( pbm ) /* File format : 'Packed Bitmap' */
241 stencil = (bmhd.mask & 1); /* There is a mask ( 'stencil' ) */
243 /* Allocate memory for a temporary buffer ( used for
244 decompression/deinterleaving ) */
246 MiniBuf = (void *)malloc( bytesperline * (nbplanes + stencil) );
247 if ( MiniBuf == NULL )
249 error="no enough memory for temporary buffer";
253 if ( ( Image = SDL_CreateRGBSurface( SDL_SWSURFACE, width, bmhd.h, (bmhd.planes==24 || flagHAM==1)?24:8, 0, 0, 0, 0 ) ) == NULL )
256 if ( bmhd.mask & 2 ) /* There is a transparent color */
257 SDL_SetColorKey( Image, SDL_SRCCOLORKEY, bmhd.tcolor );
259 /* Update palette informations */
261 /* There is no palette in 24 bits ILBM file */
262 if ( nbcolors>0 && flagHAM==0 )
264 /* FIXME: Should this include the stencil? See comment below */
265 int nbrcolorsfinal = 1 << (nbplanes + stencil);
268 for ( i=0; i<nbcolors; i++ )
270 Image->format->palette->colors[i].r = *ptr++;
271 Image->format->palette->colors[i].g = *ptr++;
272 Image->format->palette->colors[i].b = *ptr++;
275 /* Amiga EHB mode (Extra-Half-Bright) */
276 /* 6 bitplanes mode with a 32 colors palette */
277 /* The 32 last colors are the same but divided by 2 */
278 /* Some Amiga pictures save 64 colors with 32 last wrong colors, */
279 /* they shouldn't !, and here we overwrite these 32 bad colors. */
280 if ( (nbcolors==32 || flagEHB ) && (1<<bmhd.planes)==64 )
284 for ( i=32; i<64; i++ )
286 Image->format->palette->colors[i].r = (*ptr++)/2;
287 Image->format->palette->colors[i].g = (*ptr++)/2;
288 Image->format->palette->colors[i].b = (*ptr++)/2;
292 /* If nbcolors < 2^nbplanes, repeat the colormap */
293 /* This happens when pictures have a stencil mask */
294 if ( nbrcolorsfinal > (1<<bmhd.planes) ) {
295 nbrcolorsfinal = (1<<bmhd.planes);
297 for ( i=nbcolors; i < (Uint32)nbrcolorsfinal; i++ )
299 Image->format->palette->colors[i].r = Image->format->palette->colors[i%nbcolors].r;
300 Image->format->palette->colors[i].g = Image->format->palette->colors[i%nbcolors].g;
301 Image->format->palette->colors[i].b = Image->format->palette->colors[i%nbcolors].b;
304 Image->format->palette->ncolors = nbrcolorsfinal;
309 for ( h=0; h < bmhd.h; h++ )
311 /* uncompress the datas of each planes */
313 for ( plane=0; plane < (nbplanes+stencil); plane++ )
315 ptr = MiniBuf + ( plane * bytesperline );
317 remainingbytes = bytesperline;
319 if ( bmhd.tcomp == 1 ) /* Datas are compressed */
323 if ( !SDL_RWread( src, &count, 1, 1 ) )
325 error="error reading BODY chunk";
332 count += 2; /* now it */
334 if ( ( count > remainingbytes ) || !SDL_RWread( src, &color, 1, 1 ) )
336 error="error reading BODY chunk";
339 memset( ptr, color, count );
345 if ( ( count > remainingbytes ) || !SDL_RWread( src, ptr, count, 1 ) )
347 error="error reading BODY chunk";
353 remainingbytes -= count;
355 } while ( remainingbytes > 0 );
359 if ( !SDL_RWread( src, ptr, bytesperline, 1 ) )
361 error="error reading BODY chunk";
367 /* One line has been read, store it ! */
370 if ( nbplanes==24 || flagHAM==1 )
371 ptr += h * width * 3;
375 if ( pbm ) /* File format : 'Packed Bitmap' */
377 memcpy( ptr, MiniBuf, width );
379 else /* We have to un-interlace the bits ! */
381 if ( nbplanes!=24 && flagHAM==0 )
383 size = ( width + 7 ) / 8;
385 for ( i=0; i < size; i++ )
389 for ( plane=0; plane < (nbplanes + stencil); plane++ )
391 color = *( MiniBuf + i + ( plane * bytesperline ) );
394 for ( j=0; j<8; j++ )
396 if ( ( plane + j ) <= 7 ) ptr[j] |= (Uint8)( color & msk ) >> ( 7 - plane - j );
397 else ptr[j] |= (Uint8)( color & msk ) << ( plane + j - 7 );
407 Uint32 finalcolor = 0;
408 size = ( width + 7 ) / 8;
409 /* 24 bitplanes ILBM : R0...R7,G0...G7,B0...B7 */
410 /* or HAM (6 bitplanes) or HAM8 (8 bitplanes) modes */
411 for ( i=0; i<width; i=i+8 )
413 Uint8 maskBit = 0x80;
414 for ( j=0; j<8; j++ )
416 Uint32 pixelcolor = 0;
417 Uint32 maskColor = 1;
419 for ( plane=0; plane < nbplanes; plane++ )
421 dataBody = MiniBuf[ plane*size+i/8 ];
422 if ( dataBody&maskBit )
423 pixelcolor = pixelcolor | maskColor;
424 maskColor = maskColor<<1;
426 /* HAM : 12 bits RGB image (4 bits per color component) */
427 /* HAM8 : 18 bits RGB image (6 bits per color component) */
430 switch( pixelcolor>>(nbplanes-2) )
432 case 0: /* take direct color from palette */
433 finalcolor = colormap[ pixelcolor*3 ] + (colormap[ pixelcolor*3+1 ]<<8) + (colormap[ pixelcolor*3+2 ]<<16);
435 case 1: /* modify only blue component */
436 finalcolor = finalcolor&0x00FFFF;
437 finalcolor = finalcolor | (pixelcolor<<(16+(10-nbplanes)));
439 case 2: /* modify only red component */
440 finalcolor = finalcolor&0xFFFF00;
441 finalcolor = finalcolor | pixelcolor<<(10-nbplanes);
443 case 3: /* modify only green component */
444 finalcolor = finalcolor&0xFF00FF;
445 finalcolor = finalcolor | (pixelcolor<<(8+(10-nbplanes)));
451 finalcolor = pixelcolor;
453 if ( SDL_BYTEORDER == SDL_LIL_ENDIAN )
455 *ptr++ = (Uint8)(finalcolor>>16);
456 *ptr++ = (Uint8)(finalcolor>>8);
457 *ptr++ = (Uint8)(finalcolor);
461 *ptr++ = (Uint8)(finalcolor);
462 *ptr++ = (Uint8)(finalcolor>>8);
463 *ptr++ = (Uint8)(finalcolor>>16);
466 maskBit = maskBit>>1;
475 if ( MiniBuf ) free( MiniBuf );
479 SDL_RWseek(src, start, RW_SEEK_SET);
481 SDL_FreeSurface( Image );
484 IMG_SetError( error );
492 /* See if an image is contained in a data source */
493 int IMG_isLBM(SDL_RWops *src)
498 /* Load an IFF type image from an SDL datasource */
499 SDL_Surface *IMG_LoadLBM_RW(SDL_RWops *src)
504 #endif /* LOAD_LBM */