1 /*
2   SDL_image:  An example image loading library for use with SDL
3   Copyright (C) 1997-2019 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 "SDL_endian.h"
34 #include "SDL_image.h"
35 
36 #ifdef LOAD_LBM
37 
38 
39 #define MAXCOLORS 256
40 
41 /* Structure for an IFF picture ( BMHD = Bitmap Header ) */
42 
43 typedef struct
44 {
45     Uint16 w, h;        /* width & height of the bitmap in pixels */
46     Sint16 x, y;        /* screen coordinates of the bitmap */
47     Uint8 planes;       /* number of planes of the bitmap */
48     Uint8 mask;         /* mask type ( 0 => no mask ) */
49     Uint8 tcomp;        /* compression type */
50     Uint8 pad1;         /* dummy value, for padding */
51     Uint16 tcolor;      /* transparent color */
52     Uint8 xAspect,      /* pixel aspect ratio */
53           yAspect;
54     Sint16  Lpage;      /* width of the screen in pixels */
55     Sint16  Hpage;      /* height of the screen in pixels */
56 } BMHD;
57 
IMG_isLBM(SDL_RWops * src)58 int IMG_isLBM( SDL_RWops *src )
59 {
60     Sint64 start;
61     int   is_LBM;
62     Uint8 magic[4+4+4];
63 
64     if ( !src )
65         return 0;
66     start = SDL_RWtell(src);
67     is_LBM = 0;
68     if ( SDL_RWread( src, magic, sizeof(magic), 1 ) )
69     {
70         if ( !SDL_memcmp( magic, "FORM", 4 ) &&
71             ( !SDL_memcmp( magic + 8, "PBM ", 4 ) ||
72               !SDL_memcmp( magic + 8, "ILBM", 4 ) ) )
73         {
74             is_LBM = 1;
75         }
76     }
77     SDL_RWseek(src, start, RW_SEEK_SET);
78     return( is_LBM );
79 }
80 
IMG_LoadLBM_RW(SDL_RWops * src)81 SDL_Surface *IMG_LoadLBM_RW( SDL_RWops *src )
82 {
83     Sint64 start;
84     SDL_Surface *Image;
85     Uint8       id[4], pbm, colormap[MAXCOLORS*3], *MiniBuf, *ptr, count, color, msk;
86     Uint32      size, bytesloaded, nbcolors;
87     Uint32      i, j, bytesperline, nbplanes, stencil, plane, h;
88     Uint32      remainingbytes;
89     Uint32      width;
90     BMHD          bmhd;
91     char        *error;
92     Uint8       flagHAM,flagEHB;
93 
94     Image   = NULL;
95     error   = NULL;
96     MiniBuf = NULL;
97 
98     if ( !src ) {
99         /* The error message has been set in SDL_RWFromFile */
100         return NULL;
101     }
102     start = SDL_RWtell(src);
103 
104     if ( !SDL_RWread( src, id, 4, 1 ) )
105     {
106         error="error reading IFF chunk";
107         goto done;
108     }
109 
110     /* Should be the size of the file minus 4+4 ( 'FORM'+size ) */
111     if ( !SDL_RWread( src, &size, 4, 1 ) )
112     {
113         error="error reading IFF chunk size";
114         goto done;
115     }
116 
117     /* As size is not used here, no need to swap it */
118 
119     if ( SDL_memcmp( id, "FORM", 4 ) != 0 )
120     {
121         error="not a IFF file";
122         goto done;
123     }
124 
125     if ( !SDL_RWread( src, id, 4, 1 ) )
126     {
127         error="error reading IFF chunk";
128         goto done;
129     }
130 
131     pbm = 0;
132 
133     /* File format : PBM=Packed Bitmap, ILBM=Interleaved Bitmap */
134     if ( !SDL_memcmp( id, "PBM ", 4 ) ) pbm = 1;
135     else if ( SDL_memcmp( id, "ILBM", 4 ) )
136     {
137         error="not a IFF picture";
138         goto done;
139     }
140 
141     nbcolors = 0;
142 
143     SDL_memset( &bmhd, 0, sizeof( BMHD ) );
144     flagHAM = 0;
145     flagEHB = 0;
146 
147     while ( SDL_memcmp( id, "BODY", 4 ) != 0 )
148     {
149         if ( !SDL_RWread( src, id, 4, 1 ) )
150         {
151             error="error reading IFF chunk";
152             goto done;
153         }
154 
155         if ( !SDL_RWread( src, &size, 4, 1 ) )
156         {
157             error="error reading IFF chunk size";
158             goto done;
159         }
160 
161         bytesloaded = 0;
162 
163         size = SDL_SwapBE32( size );
164 
165         if ( !SDL_memcmp( id, "BMHD", 4 ) ) /* Bitmap header */
166         {
167             if ( !SDL_RWread( src, &bmhd, sizeof( BMHD ), 1 ) )
168             {
169                 error="error reading BMHD chunk";
170                 goto done;
171             }
172 
173             bytesloaded = sizeof( BMHD );
174 
175             bmhd.w      = SDL_SwapBE16( bmhd.w );
176             bmhd.h      = SDL_SwapBE16( bmhd.h );
177             bmhd.x      = SDL_SwapBE16( bmhd.x );
178             bmhd.y      = SDL_SwapBE16( bmhd.y );
179             bmhd.tcolor = SDL_SwapBE16( bmhd.tcolor );
180             bmhd.Lpage  = SDL_SwapBE16( bmhd.Lpage );
181             bmhd.Hpage  = SDL_SwapBE16( bmhd.Hpage );
182         }
183 
184         if ( !SDL_memcmp( id, "CMAP", 4 ) ) /* palette ( Color Map ) */
185         {
186             if (size > sizeof (colormap)) {
187                 error="colormap size is too large";
188                 goto done;
189             }
190 
191             if ( !SDL_RWread( src, &colormap, size, 1 ) )
192             {
193                 error="error reading CMAP chunk";
194                 goto done;
195             }
196 
197             bytesloaded = size;
198             nbcolors = size / 3;
199         }
200 
201         if ( !SDL_memcmp( id, "CAMG", 4 ) ) /* Amiga ViewMode  */
202         {
203             Uint32 viewmodes;
204             if ( !SDL_RWread( src, &viewmodes, sizeof(viewmodes), 1 ) )
205             {
206                 error="error reading CAMG chunk";
207                 goto done;
208             }
209 
210             bytesloaded = size;
211             viewmodes = SDL_SwapBE32( viewmodes );
212             if ( viewmodes & 0x0800 )
213                 flagHAM = 1;
214             if ( viewmodes & 0x0080 )
215                 flagEHB = 1;
216         }
217 
218         if ( SDL_memcmp( id, "BODY", 4 ) )
219         {
220             if ( size & 1 ) ++size;     /* padding ! */
221             size -= bytesloaded;
222             /* skip the remaining bytes of this chunk */
223             if ( size ) SDL_RWseek( src, size, RW_SEEK_CUR );
224         }
225     }
226 
227     /* compute some usefull values, based on the bitmap header */
228 
229     width = ( bmhd.w + 15 ) & 0xFFFFFFF0;  /* Width in pixels modulo 16 */
230 
231     bytesperline = ( ( bmhd.w + 15 ) / 16 ) * 2;
232 
233     nbplanes = bmhd.planes;
234 
235     if ( pbm )                         /* File format : 'Packed Bitmap' */
236     {
237         bytesperline *= 8;
238         nbplanes = 1;
239     }
240 
241     stencil = (bmhd.mask & 1);   /* There is a mask ( 'stencil' ) */
242 
243     /* Allocate memory for a temporary buffer ( used for
244            decompression/deinterleaving ) */
245 
246     MiniBuf = (Uint8 *)SDL_malloc( bytesperline * (nbplanes + stencil) );
247     if ( MiniBuf == NULL )
248     {
249         error="not enough memory for temporary buffer";
250         goto done;
251     }
252 
253     if ( ( Image = SDL_CreateRGBSurface( SDL_SWSURFACE, width, bmhd.h, (nbplanes==24 || flagHAM==1)?24:8, 0, 0, 0, 0 ) ) == NULL )
254        goto done;
255 
256     if ( bmhd.mask & 2 )               /* There is a transparent color */
257         SDL_SetColorKey( Image, SDL_TRUE, bmhd.tcolor );
258 
259     /* Update palette informations */
260 
261     /* There is no palette in 24 bits ILBM file */
262     if ( nbcolors>0 && flagHAM==0 )
263     {
264         /* FIXME: Should this include the stencil? See comment below */
265         int nbrcolorsfinal = 1 << (nbplanes + stencil);
266         ptr = &colormap[0];
267 
268         for ( i=0; i<nbcolors; i++ )
269         {
270             Image->format->palette->colors[i].r = *ptr++;
271             Image->format->palette->colors[i].g = *ptr++;
272             Image->format->palette->colors[i].b = *ptr++;
273         }
274 
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<<nbplanes)==64 )
281         {
282             nbcolors = 64;
283             ptr = &colormap[0];
284             for ( i=32; i<64; i++ )
285             {
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;
289             }
290         }
291 
292         /* If nbcolors < 2^nbplanes, repeat the colormap */
293         /* This happens when pictures have a stencil mask */
294         if ( nbrcolorsfinal > (1<<nbplanes) ) {
295             nbrcolorsfinal = (1<<nbplanes);
296         }
297         for ( i=nbcolors; i < (Uint32)nbrcolorsfinal; i++ )
298         {
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;
302         }
303         if ( !pbm )
304             Image->format->palette->ncolors = nbrcolorsfinal;
305     }
306 
307     /* Get the bitmap */
308 
309     for ( h=0; h < bmhd.h; h++ )
310     {
311         /* uncompress the datas of each planes */
312 
313         for ( plane=0; plane < (nbplanes+stencil); plane++ )
314         {
315             ptr = MiniBuf + ( plane * bytesperline );
316 
317             remainingbytes = bytesperline;
318 
319             if ( bmhd.tcomp == 1 )      /* Datas are compressed */
320             {
321                 do
322                 {
323                     if ( !SDL_RWread( src, &count, 1, 1 ) )
324                     {
325                         error="error reading BODY chunk";
326                         goto done;
327                     }
328 
329                     if ( count & 0x80 )
330                     {
331                         count ^= 0xFF;
332                         count += 2; /* now it */
333 
334                         if ( ( count > remainingbytes ) || !SDL_RWread( src, &color, 1, 1 ) )
335                         {
336                             error="error reading BODY chunk";
337                             goto done;
338                         }
339                         SDL_memset( ptr, color, count );
340                     }
341                     else
342                     {
343                         ++count;
344 
345                         if ( ( count > remainingbytes ) || !SDL_RWread( src, ptr, count, 1 ) )
346                         {
347                            error="error reading BODY chunk";
348                             goto done;
349                         }
350                     }
351 
352                     ptr += count;
353                     remainingbytes -= count;
354 
355                 } while ( remainingbytes > 0 );
356             }
357             else
358             {
359                 if ( !SDL_RWread( src, ptr, bytesperline, 1 ) )
360                 {
361                     error="error reading BODY chunk";
362                     goto done;
363                 }
364             }
365         }
366 
367         /* One line has been read, store it ! */
368 
369         ptr = (Uint8 *)Image->pixels;
370         if ( nbplanes==24 || flagHAM==1 )
371             ptr += h * width * 3;
372         else
373             ptr += h * width;
374 
375         if ( pbm )                 /* File format : 'Packed Bitmap' */
376         {
377            SDL_memcpy( ptr, MiniBuf, width );
378         }
379         else        /* We have to un-interlace the bits ! */
380         {
381             if ( nbplanes!=24 && flagHAM==0 )
382             {
383                 size = ( width + 7 ) / 8;
384 
385                 for ( i=0; i < size; i++ )
386                 {
387                     SDL_memset( ptr, 0, 8 );
388 
389                     for ( plane=0; plane < (nbplanes + stencil); plane++ )
390                     {
391                         color = *( MiniBuf + i + ( plane * bytesperline ) );
392                         msk = 0x80;
393 
394                         for ( j=0; j<8; j++ )
395                         {
396                             if ( ( plane + j ) <= 7 ) ptr[j] |= (Uint8)( color & msk ) >> ( 7 - plane - j );
397                             else                        ptr[j] |= (Uint8)( color & msk ) << ( plane + j - 7 );
398 
399                             msk >>= 1;
400                         }
401                     }
402                     ptr += 8;
403                 }
404             }
405             else
406             {
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 )
412                 {
413                     Uint8 maskBit = 0x80;
414                     for ( j=0; j<8; j++ )
415                     {
416                         Uint32 pixelcolor = 0;
417                         Uint32 maskColor = 1;
418                         Uint8 dataBody;
419                         for ( plane=0; plane < nbplanes; plane++ )
420                         {
421                             dataBody = MiniBuf[ plane*size+i/8 ];
422                             if ( dataBody&maskBit )
423                                 pixelcolor = pixelcolor | maskColor;
424                             maskColor = maskColor<<1;
425                         }
426                         /* HAM : 12 bits RGB image (4 bits per color component) */
427                         /* HAM8 : 18 bits RGB image (6 bits per color component) */
428                         if ( flagHAM )
429                         {
430                             switch( pixelcolor>>(nbplanes-2) )
431                             {
432                                 case 0: /* take direct color from palette */
433                                     finalcolor = colormap[ pixelcolor*3 ] + (colormap[ pixelcolor*3+1 ]<<8) + (colormap[ pixelcolor*3+2 ]<<16);
434                                     break;
435                                 case 1: /* modify only blue component */
436                                     finalcolor = finalcolor&0x00FFFF;
437                                     finalcolor = finalcolor | (pixelcolor<<(16+(10-nbplanes)));
438                                     break;
439                                 case 2: /* modify only red component */
440                                     finalcolor = finalcolor&0xFFFF00;
441                                     finalcolor = finalcolor | pixelcolor<<(10-nbplanes);
442                                     break;
443                                 case 3: /* modify only green component */
444                                     finalcolor = finalcolor&0xFF00FF;
445                                     finalcolor = finalcolor | (pixelcolor<<(8+(10-nbplanes)));
446                                     break;
447                             }
448                         }
449                         else
450                         {
451                             finalcolor = pixelcolor;
452                         }
453 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
454                             *ptr++ = (Uint8)(finalcolor>>16);
455                             *ptr++ = (Uint8)(finalcolor>>8);
456                             *ptr++ = (Uint8)(finalcolor);
457 #else
458                             *ptr++ = (Uint8)(finalcolor);
459                             *ptr++ = (Uint8)(finalcolor>>8);
460                             *ptr++ = (Uint8)(finalcolor>>16);
461 #endif
462                         maskBit = maskBit>>1;
463                     }
464                 }
465             }
466         }
467     }
468 
469 done:
470 
471     if ( MiniBuf ) SDL_free( MiniBuf );
472 
473     if ( error )
474     {
475         SDL_RWseek(src, start, RW_SEEK_SET);
476         if ( Image ) {
477             SDL_FreeSurface( Image );
478             Image = NULL;
479         }
480         IMG_SetError( "%s", error );
481     }
482 
483     return( Image );
484 }
485 
486 #else /* LOAD_LBM */
487 
488 /* See if an image is contained in a data source */
IMG_isLBM(SDL_RWops * src)489 int IMG_isLBM(SDL_RWops *src)
490 {
491     return(0);
492 }
493 
494 /* Load an IFF type image from an SDL datasource */
IMG_LoadLBM_RW(SDL_RWops * src)495 SDL_Surface *IMG_LoadLBM_RW(SDL_RWops *src)
496 {
497     return(NULL);
498 }
499 
500 #endif /* LOAD_LBM */
501