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