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