1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2000-2009 by Denton Woods
5 // Last modified: 03/08/2009
6 //
7 // Filename: src-IL/src/il_ilbm.c
8 //
9 // Description: IFF ILBM file (.iff, .ilbm, .lbm) functions
10 //   IFF ILBM loader, ported from SDL_Image library (IMG_lbm.c)
11 //   http://www.libsdl.org/cgi/viewvc.cgi/trunk/SDL_image/IMG_lbm.c?view=markup
12 //
13 //   Handles Amiga ILBM and PBM images (including .lbm files saved by the PC
14 //   version of dpaint)
15 //   Handles ExtraHalfBright and HAM images.
16 //
17 //   Adapted from SDL_image by Ben Campbell (http://scumways.com) 2009-02-23
18 //
19 //-----------------------------------------------------------------------------
20 
21 
22 // TODO: sort out the .iff extension confusion: .iff is currently handled by
23 // Maya IFF/CIMG handler (il_iff.c), but it should defer to this one if
24 // fileturns out to be an ILBM. I think the best solution would be to
25 // rename the IFF handler to CIMG, and create a new iff handler to act as
26 // a front end, passing off to either il_ilbm or il_cimg...
27 // For now, this handler only handles .lbm and .ilbm extenstions (but
28 // traditionally, .iff is more common).
29 
30 #include "il_internal.h"
31 #ifndef IL_NO_ILBM
32 #include <stdlib.h>
33 
34 ILboolean   iIsValidIlbm(void);
35 ILboolean   iLoadIlbmInternal(void);
36 static ILboolean load_ilbm(void);
37 static int isLBM(void );
38 
39 
ilIsValidIlbm(ILconst_string FileName)40 ILboolean ilIsValidIlbm(ILconst_string FileName)
41 {
42     ILHANDLE    f;
43     ILboolean   bIlbm = IL_FALSE;
44 
45     if (!iCheckExtension(FileName, IL_TEXT("iff")) &&
46         !iCheckExtension(FileName, IL_TEXT("ilbm")) &&
47         !iCheckExtension(FileName, IL_TEXT("lbm")) &&
48         !iCheckExtension(FileName, IL_TEXT("ham")) ) {
49         ilSetError(IL_INVALID_EXTENSION);
50         return bIlbm;
51     }
52 
53     f = iopenr(FileName);
54     if (f == NULL) {
55         ilSetError(IL_COULD_NOT_OPEN_FILE);
56         return bIlbm;
57     }
58 
59     bIlbm = ilIsValidIlbmF(f);
60     icloser(f);
61 
62     return bIlbm;
63 }
64 
65 
ilIsValidIlbmF(ILHANDLE File)66 ILboolean ilIsValidIlbmF(ILHANDLE File)
67 {
68     ILuint      FirstPos;
69     ILboolean   bRet;
70 
71     iSetInputFile(File);
72     FirstPos = itell();
73     bRet = iIsValidIlbm();
74     iseek(FirstPos, IL_SEEK_SET);
75 
76     return bRet;
77 }
78 
79 
ilIsValidIlbmL(const void * Lump,ILuint Size)80 ILboolean ilIsValidIlbmL(const void *Lump, ILuint Size)
81 {
82     iSetInputLump(Lump, Size);
83     return iIsValidIlbm();
84 }
85 
86 
iIsValidIlbm()87 ILboolean iIsValidIlbm()
88 {
89     return isLBM() ? IL_TRUE:IL_FALSE;
90 }
91 
92 
93 // Reads a file
ilLoadIlbm(ILconst_string FileName)94 ILboolean ilLoadIlbm(ILconst_string FileName)
95 {
96     ILHANDLE    IlbmFile;
97     ILboolean   bIlbm = IL_FALSE;
98 
99     IlbmFile = iopenr(FileName);
100     if (IlbmFile == NULL) {
101         ilSetError(IL_COULD_NOT_OPEN_FILE);
102         return bIlbm;
103     }
104 
105     bIlbm = ilLoadIlbmF(IlbmFile);
106     icloser(IlbmFile);
107 
108     return bIlbm;
109 }
110 
111 
112 // Reads an already-opened file
ilLoadIlbmF(ILHANDLE File)113 ILboolean ilLoadIlbmF(ILHANDLE File)
114 {
115     ILuint      FirstPos;
116     ILboolean   bRet;
117 
118     iSetInputFile(File);
119     FirstPos = itell();
120     bRet = iLoadIlbmInternal();
121     iseek(FirstPos, IL_SEEK_SET);
122 
123     return bRet;
124 }
125 
126 
127 // Reads from a memory "lump"
ilLoadIlbmL(const void * Lump,ILuint Size)128 ILboolean ilLoadIlbmL(const void *Lump, ILuint Size)
129 {
130     iSetInputLump(Lump, Size);
131     return iLoadIlbmInternal();
132 }
133 
134 
iLoadIlbmInternal()135 ILboolean iLoadIlbmInternal()
136 {
137     if (iCurImage == NULL) {
138         ilSetError(IL_ILLEGAL_OPERATION);
139         return IL_FALSE;
140     }
141     if (!iIsValidIlbm()) {
142         ilSetError(IL_INVALID_VALUE);
143         return IL_FALSE;
144     }
145 
146     if (!load_ilbm() )
147     {
148         return IL_FALSE;
149     }
150 
151     return ilFixImage();
152 }
153 
154 
155 /* some defines to allow us to use the SDL_image source in as unmodified form
156  * as possible, so it'll be easy to diff. Yes, it does look evil, but it's
157  * really just some local syntactic sugar - most things correspond directly
158  * to DevIL calls. It's not that bad, honest ;-)
159  * This will make it _much_ easier to track changes to SDL_Image.
160  * - BenC
161  */
162 
163 #define Sint8 ILbyte
164 #define Sint16 ILshort
165 #define Sint32 ILint
166 #define Uint8 ILubyte
167 #define Uint16 ILushort
168 #define Uint32 ILuint
169 
170 #define SDL_RWops void
171 #define SDL_RWtell(s) itell()
172 #define SDL_RWread(s,ptr,size,nmemb) iread(ptr,size,nmemb)
173 #define SDL_RWseek(s,offset,whence) iseek(offset, IL_ ## whence)
174 
175 
176 /* use different function names to avoid any possible symbol contamination
177  * (user might also be linking with libSDL) */
178 #define SDL_SwapBE32(i) iSDL_SwapBE32(i)
179 #define SDL_SwapBE16(s) iSDL_SwapBE16(s)
iSDL_SwapBE16(Uint16 s)180 static Uint16 iSDL_SwapBE16( Uint16 s )
181     { Uint16 foo = s; iSwapUShort(&foo); return foo; }
iSDL_SwapBE32(Uint32 i)182 static Uint32 iSDL_SwapBE32( Uint32 i )
183     { Uint32 foo = i; iSwapUInt(&foo); return foo; }
184 
185 /* rest of this file is as unchanged as possible from IMG_lbm.c - BenC */
186 /***************************************************************************/
187 
188 /* This is a ILBM image file loading framework
189    Load IFF pictures, PBM & ILBM packing methods, with or without stencil
190    Written by Daniel Morais ( Daniel AT Morais DOT com ) in September 2001.
191    24 bits ILBM files support added by Marc Le Douarain (http://www.multimania.com/mavati)
192    in December 2002.
193    EHB and HAM (specific Amiga graphic chip modes) support added by Marc Le Douarain
194    (http://www.multimania.com/mavati) in December 2003.
195    Stencil and colorkey fixes by David Raulo (david.raulo AT free DOT fr) in February 2004.
196    Buffer overflow fix in RLE decompression by David Raulo in January 2008.
197 */
198 
199 
200 #define MAXCOLORS 256
201 
202 /* Structure for an IFF picture ( BMHD = Bitmap Header ) */
203 
204 /* TODO: SDL_Image seems to get away without any struct
205  * packing... should it be added? */
206 typedef struct
207 {
208     Uint16 w, h;        /* width & height of the bitmap in pixels */
209     Sint16 x, y;        /* screen coordinates of the bitmap */
210     Uint8 planes;       /* number of planes of the bitmap */
211     Uint8 mask;         /* mask type ( 0 => no mask ) */
212     Uint8 tcomp;        /* compression type */
213     Uint8 pad1;         /* dummy value, for padding */
214     Uint16 tcolor;      /* transparent color */
215     Uint8 xAspect,      /* pixel aspect ratio */
216          yAspect;
217     Sint16  Lpage;      /* width of the screen in pixels */
218     Sint16  Hpage;      /* height of the screen in pixels */
219 } BMHD;
220 
isLBM()221 static int isLBM()
222 {
223     SDL_RWops* src = 0;
224     int start;
225     int   is_LBM;
226     Uint8 magic[4+4+4];
227 
228     start = SDL_RWtell(src);
229     is_LBM = 0;
230     if ( SDL_RWread( src, magic, sizeof(magic), 1 ) )
231     {
232         if ( !memcmp( magic, "FORM", 4 ) &&
233             ( !memcmp( magic + 8, "PBM ", 4 ) ||
234               !memcmp( magic + 8, "ILBM", 4 ) ) )
235         {
236             is_LBM = 1;
237         }
238     }
239     SDL_RWseek(src, start, SEEK_SET);
240     return( is_LBM );
241 }
242 
load_ilbm(void)243 static ILboolean load_ilbm(void)
244 {
245     SDL_RWops* src = 0;
246     struct { Uint8 r; Uint8 g; Uint8 b; } scratch_pal[MAXCOLORS];
247     ILenum      format; /* IL_RGB (ham or 24bit) or IL_COLOUR_INDEX */
248 
249     int start;
250     Uint8       id[4], pbm, colormap[MAXCOLORS*3], *MiniBuf, *ptr, count, color, msk;
251     Uint32      size, bytesloaded, nbcolors;
252     Uint32      i, j, bytesperline, nbplanes, plane, h;
253     Uint32      remainingbytes;
254     Uint32      width;
255     BMHD          bmhd;
256     char        *error;
257     Uint8       flagHAM,flagEHB;
258 
259     error   = NULL;
260     MiniBuf = NULL;
261 
262     start = SDL_RWtell(src);
263     if ( !SDL_RWread( src, id, 4, 1 ) )
264     {
265         error="error reading IFF chunk";
266         goto done;
267     }
268 
269     /* Should be the size of the file minus 4+4 ( 'FORM'+size ) */
270     if ( !SDL_RWread( src, &size, 4, 1 ) )
271     {
272         error="error reading IFF chunk size";
273         goto done;
274     }
275 
276     /* As size is not used here, no need to swap it */
277 
278     if ( memcmp( id, "FORM", 4 ) != 0 )
279     {
280         ilSetError(IL_INVALID_FILE_HEADER);
281         error="not a IFF file";
282         goto done;
283     }
284 
285     if ( !SDL_RWread( src, id, 4, 1 ) )
286     {
287         error="error reading IFF chunk";
288         goto done;
289     }
290 
291     pbm = 0;
292 
293     /* File format : PBM=Packed Bitmap, ILBM=Interleaved Bitmap */
294     if ( !memcmp( id, "PBM ", 4 ) ) pbm = 1;
295     else if ( memcmp( id, "ILBM", 4 ) )
296     {
297         ilSetError(IL_INVALID_FILE_HEADER);
298         error="not a IFF picture";
299         goto done;
300     }
301 
302     nbcolors = 0;
303 
304     memset( &bmhd, 0, sizeof( BMHD ) );
305     flagHAM = 0;
306     flagEHB = 0;
307 
308     while ( memcmp( id, "BODY", 4 ) != 0 )
309     {
310         if ( !SDL_RWread( src, id, 4, 1 ) )
311         {
312             error="error reading IFF chunk";
313             goto done;
314         }
315 
316         if ( !SDL_RWread( src, &size, 4, 1 ) )
317         {
318             error="error reading IFF chunk size";
319             goto done;
320         }
321 
322         bytesloaded = 0;
323 
324         size = SDL_SwapBE32( size );
325 
326         if ( !memcmp( id, "BMHD", 4 ) ) /* Bitmap header */
327         {
328             if ( !SDL_RWread( src, &bmhd, sizeof( BMHD ), 1 ) )
329             {
330                 error="error reading BMHD chunk";
331                 goto done;
332             }
333 
334             bytesloaded = sizeof( BMHD );
335 
336             bmhd.w      = SDL_SwapBE16( bmhd.w );
337             bmhd.h      = SDL_SwapBE16( bmhd.h );
338             bmhd.x      = SDL_SwapBE16( bmhd.x );
339             bmhd.y      = SDL_SwapBE16( bmhd.y );
340             bmhd.tcolor = SDL_SwapBE16( bmhd.tcolor );
341             bmhd.Lpage  = SDL_SwapBE16( bmhd.Lpage );
342             bmhd.Hpage  = SDL_SwapBE16( bmhd.Hpage );
343         }
344 
345         if ( !memcmp( id, "CMAP", 4 ) ) /* palette ( Color Map ) */
346         {
347             if ( !SDL_RWread( src, &colormap, size, 1 ) )
348             {
349                 error="error reading CMAP chunk";
350                 goto done;
351             }
352 
353             bytesloaded = size;
354             nbcolors = size / 3;
355         }
356 
357         if ( !memcmp( id, "CAMG", 4 ) ) /* Amiga ViewMode  */
358         {
359             Uint32 viewmodes;
360             if ( !SDL_RWread( src, &viewmodes, sizeof(viewmodes), 1 ) )
361             {
362                 error="error reading CAMG chunk";
363                 goto done;
364             }
365 
366             bytesloaded = size;
367             viewmodes = SDL_SwapBE32( viewmodes );
368             if ( viewmodes & 0x0800 )
369                 flagHAM = 1;
370             if ( viewmodes & 0x0080 )
371                 flagEHB = 1;
372         }
373 
374         if ( memcmp( id, "BODY", 4 ) )
375         {
376             if ( size & 1 ) ++size;     /* padding ! */
377             size -= bytesloaded;
378             /* skip the remaining bytes of this chunk */
379             if ( size ) SDL_RWseek( src, size, SEEK_CUR );
380         }
381     }
382 
383     /* compute some usefull values, based on the bitmap header */
384 
385     width = ( bmhd.w + 15 ) & 0xFFFFFFF0;  /* Width in pixels modulo 16 */
386 
387     bytesperline = ( ( bmhd.w + 15 ) / 16 ) * 2;
388 
389     nbplanes = bmhd.planes;
390 
391     if ( pbm )                         /* File format : 'Packed Bitmap' */
392     {
393         bytesperline *= 8;
394         nbplanes = 1;
395     }
396 
397     if ( bmhd.mask & 1 ) ++nbplanes;   /* There is a mask ( 'stencil' ) */
398 
399     /* Allocate memory for a temporary buffer ( used for
400            decompression/deinterleaving ) */
401 
402     if ( ( MiniBuf = (void *)malloc( bytesperline * nbplanes ) ) == NULL )
403     {
404         ilSetError( IL_OUT_OF_MEMORY );
405         error="no enough memory for temporary buffer";
406         goto done;
407     }
408 
409     if( bmhd.planes==24 || flagHAM==1 ) {
410         format = IL_BGR;
411     } else {
412         format = IL_COLOUR_INDEX;
413     }
414     if( !ilTexImage( width, bmhd.h, 1, (format==IL_COLOUR_INDEX)?1:3, format, IL_UNSIGNED_BYTE, NULL ) )
415         goto done;
416 	iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
417 
418 #if 0 /*  No transparent colour support in DevIL? (TODO: confirm) */
419     if ( bmhd.mask & 2 )               /* There is a transparent color */
420         SDL_SetColorKey( Image, SDL_SRCCOLORKEY, bmhd.tcolor );
421 #endif
422 
423     /* Update palette informations */
424 
425     /* There is no palette in 24 bits ILBM file */
426     if ( nbcolors>0 && flagHAM==0 )
427     {
428         int nbrcolorsfinal = 1 << nbplanes;
429         ptr = &colormap[0];
430 
431         for ( i=0; i<nbcolors; i++ )
432         {
433             scratch_pal[i].r = *ptr++;
434             scratch_pal[i].g = *ptr++;
435             scratch_pal[i].b = *ptr++;
436         }
437 
438         /* Amiga EHB mode (Extra-Half-Bright) */
439         /* 6 bitplanes mode with a 32 colors palette */
440         /* The 32 last colors are the same but divided by 2 */
441         /* Some Amiga pictures save 64 colors with 32 last wrong colors, */
442         /* they shouldn't !, and here we overwrite these 32 bad colors. */
443         if ( (nbcolors==32 || flagEHB ) && (1<<bmhd.planes)==64 )
444         {
445             nbcolors = 64;
446             ptr = &colormap[0];
447             for ( i=32; i<64; i++ )
448             {
449                 scratch_pal[i].r = (*ptr++)/2;
450                 scratch_pal[i].g = (*ptr++)/2;
451                 scratch_pal[i].b = (*ptr++)/2;
452             }
453         }
454 
455         /* If nbcolors < 2^nbplanes, repeat the colormap */
456         /* This happens when pictures have a stencil mask */
457         if ( nbrcolorsfinal > (1<<bmhd.planes) ) {
458             nbrcolorsfinal = (1<<bmhd.planes);
459         }
460         for ( i=nbcolors; i < (Uint32)nbrcolorsfinal; i++ )
461         {
462             scratch_pal[i].r = scratch_pal[i%nbcolors].r;
463             scratch_pal[i].g = scratch_pal[i%nbcolors].g;
464             scratch_pal[i].b = scratch_pal[i%nbcolors].b;
465         }
466 
467         if ( !pbm )
468             ilRegisterPal( scratch_pal, 3*nbrcolorsfinal, IL_PAL_RGB24 );
469 
470     }
471 
472     /* Get the bitmap */
473 
474     for ( h=0; h < bmhd.h; h++ )
475     {
476         /* uncompress the datas of each planes */
477 
478         for ( plane=0; plane < nbplanes; plane++ )
479         {
480             ptr = MiniBuf + ( plane * bytesperline );
481 
482             remainingbytes = bytesperline;
483 
484             if ( bmhd.tcomp == 1 )      /* Datas are compressed */
485             {
486                 do
487                 {
488                     if ( !SDL_RWread( src, &count, 1, 1 ) )
489                     {
490                         error="error reading BODY chunk";
491                         goto done;
492                     }
493 
494                     if ( count & 0x80 )
495                     {
496                         count ^= 0xFF;
497                         count += 2; /* now it */
498 
499                         if ( ( count > remainingbytes ) || !SDL_RWread( src, &color, 1, 1 ) )
500                         {
501                             if( count>remainingbytes)
502                                 ilSetError(IL_ILLEGAL_FILE_VALUE );
503                            error="error reading BODY chunk";
504                             goto done;
505                         }
506                         memset( ptr, color, count );
507                     }
508                     else
509                     {
510                         ++count;
511 
512                         if ( ( count > remainingbytes ) || !SDL_RWread( src, ptr, count, 1 ) )
513                         {
514                             if( count>remainingbytes)
515                                 ilSetError(IL_ILLEGAL_FILE_VALUE );
516                            error="error reading BODY chunk";
517                             goto done;
518                         }
519                     }
520 
521                     ptr += count;
522                     remainingbytes -= count;
523 
524                 } while ( remainingbytes > 0 );
525             }
526             else
527             {
528                 if ( !SDL_RWread( src, ptr, bytesperline, 1 ) )
529                 {
530                     error="error reading BODY chunk";
531                     goto done;
532                 }
533             }
534         }
535 
536         /* One line has been read, store it ! */
537 
538         ptr = ilGetData();
539         if ( nbplanes==24 || flagHAM==1 )
540             ptr += h * width * 3;
541         else
542             ptr += h * width;
543 
544         if ( pbm )                 /* File format : 'Packed Bitmap' */
545         {
546            memcpy( ptr, MiniBuf, width );
547         }
548         else        /* We have to un-interlace the bits ! */
549         {
550             if ( nbplanes!=24 && flagHAM==0 )
551             {
552                 size = ( width + 7 ) / 8;
553 
554                 for ( i=0; i < size; i++ )
555                 {
556                     memset( ptr, 0, 8 );
557 
558                     for ( plane=0; plane < nbplanes; plane++ )
559                     {
560                         color = *( MiniBuf + i + ( plane * bytesperline ) );
561                         msk = 0x80;
562 
563                         for ( j=0; j<8; j++ )
564                         {
565                             if ( ( plane + j ) <= 7 ) ptr[j] |= (Uint8)( color & msk ) >> ( 7 - plane - j );
566                             else                        ptr[j] |= (Uint8)( color & msk ) << ( plane + j - 7 );
567 
568                             msk >>= 1;
569                         }
570                     }
571                     ptr += 8;
572                 }
573             }
574             else
575             {
576                 Uint32 finalcolor = 0;
577                 size = ( width + 7 ) / 8;
578                 /* 24 bitplanes ILBM : R0...R7,G0...G7,B0...B7 */
579                 /* or HAM (6 bitplanes) or HAM8 (8 bitplanes) modes */
580                 for ( i=0; i<width; i=i+8 )
581                 {
582                     Uint8 maskBit = 0x80;
583                     for ( j=0; j<8; j++ )
584                     {
585                         Uint32 pixelcolor = 0;
586                         Uint32 maskColor = 1;
587                         Uint8 dataBody;
588                         for ( plane=0; plane < nbplanes; plane++ )
589                         {
590                             dataBody = MiniBuf[ plane*size+i/8 ];
591                             if ( dataBody&maskBit )
592                                 pixelcolor = pixelcolor | maskColor;
593                             maskColor = maskColor<<1;
594                         }
595                         /* HAM : 12 bits RGB image (4 bits per color component) */
596                         /* HAM8 : 18 bits RGB image (6 bits per color component) */
597                         if ( flagHAM )
598                         {
599                             switch( pixelcolor>>(nbplanes-2) )
600                             {
601                                 case 0: /* take direct color from palette */
602                                     finalcolor = colormap[ pixelcolor*3 ] + (colormap[ pixelcolor*3+1 ]<<8) + (colormap[ pixelcolor*3+2 ]<<16);
603                                     break;
604                                 case 1: /* modify only blue component */
605                                     finalcolor = finalcolor&0x00FFFF;
606                                     finalcolor = finalcolor | (pixelcolor<<(16+(10-nbplanes)));
607                                     break;
608                                 case 2: /* modify only red component */
609                                     finalcolor = finalcolor&0xFFFF00;
610                                     finalcolor = finalcolor | pixelcolor<<(10-nbplanes);
611                                     break;
612                                 case 3: /* modify only green component */
613                                     finalcolor = finalcolor&0xFF00FF;
614                                     finalcolor = finalcolor | (pixelcolor<<(8+(10-nbplanes)));
615                                     break;
616                             }
617                         }
618                         else
619                         {
620                             finalcolor = pixelcolor;
621                         }
622 #if BYTE_ORDER == LITTLE_ENDIAN
623                         {
624                             *ptr++ = (Uint8)(finalcolor>>16);
625                             *ptr++ = (Uint8)(finalcolor>>8);
626                             *ptr++ = (Uint8)(finalcolor);
627                         }
628 #else
629                         {
630                             *ptr++ = (Uint8)(finalcolor);
631                             *ptr++ = (Uint8)(finalcolor>>8);
632                             *ptr++ = (Uint8)(finalcolor>>16);
633                         }
634 #endif
635                         maskBit = maskBit>>1;
636                     }
637                 }
638             }
639         }
640     }
641 
642 done:
643 
644     if ( MiniBuf ) free( MiniBuf );
645 
646     if ( error )
647     {
648         /* fprintf(stderr,"il_ilbm.c: '%s'\n",error); */
649         return IL_FALSE;
650     }
651 
652     return IL_TRUE;
653 }
654 
655 
656 
657 #endif//IL_NO_ILBM
658 
659