Coverage Report

Created: 2025-08-28 06:33

/src/freeimage-svn/FreeImage/trunk/Source/FreeImage/PluginIFF.cpp
Line
Count
Source (jump to first uncovered line)
1
// ==========================================================
2
// Deluxe Paint Loader
3
//
4
// Design and implementation by
5
// - Floris van den Berg (flvdberg@wxs.nl)
6
// - Mark Sibly (marksibly@blitzbasic.com)
7
// - Aaron Shumate (trek@startreker.com)
8
// - Hervé Drolon (drolon@infonie.fr)
9
//
10
// This file is part of FreeImage 3
11
//
12
// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
13
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
14
// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
15
// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
16
// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
17
// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
18
// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
19
// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
20
// THIS DISCLAIMER.
21
//
22
// Use at your own risk!
23
// ==========================================================
24
25
#include "FreeImage.h"
26
#include "Utilities.h"
27
28
// ----------------------------------------------------------
29
//  Internal typedefs and structures
30
// ----------------------------------------------------------
31
32
#ifdef _WIN32
33
#pragma pack(push, 1)
34
#else
35
#pragma pack(1)
36
#endif
37
38
typedef struct {
39
    WORD w, h;                    /* raster width & height in pixels */
40
    WORD x, y;                    /* position for this image */
41
    BYTE nPlanes;                 /* # source bitplanes */
42
    BYTE masking;                 /* masking technique */
43
    BYTE compression;             /* compression algorithm */
44
    BYTE pad1;                    /* UNUSED.  For consistency, put 0 here.*/
45
    WORD transparentColor;        /* transparent "color number" */
46
    BYTE xAspect, yAspect;        /* aspect ratio, a rational number x/y */
47
    WORD pageWidth, pageHeight;   /* source "page" size in pixels */
48
} BMHD;
49
50
#ifdef _WIN32
51
#pragma pack(pop)
52
#else
53
#pragma pack()
54
#endif
55
56
#ifndef FREEIMAGE_BIGENDIAN
57
static void
58
0
SwapHeader(BMHD *header) {
59
0
  SwapShort(&header->w);
60
0
  SwapShort(&header->h);
61
0
  SwapShort(&header->x);
62
0
  SwapShort(&header->y);
63
0
  SwapShort(&header->transparentColor);
64
0
  SwapShort(&header->pageWidth);
65
0
  SwapShort(&header->pageHeight);
66
0
}
67
#endif
68
69
// ----------------------------------------------------------
70
71
/* IFF chunk IDs */
72
73
typedef DWORD IFF_ID;
74
75
23.1k
#define MAKE_ID(a, b, c, d)         ((IFF_ID)(a)<<24 | (IFF_ID)(b)<<16 | (IFF_ID)(c)<<8 | (IFF_ID)(d))
76
77
23.1k
#define ID_FORM     MAKE_ID('F', 'O', 'R', 'M')     /* EA IFF 85 group identifier */
78
#define ID_CAT      MAKE_ID('C', 'A', 'T', ' ')     /* EA IFF 85 group identifier */
79
#define ID_LIST     MAKE_ID('L', 'I', 'S', 'T')     /* EA IFF 85 group identifier */
80
#define ID_PROP     MAKE_ID('P', 'R', 'O', 'P')     /* EA IFF 85 group identifier */
81
#define ID_END      MAKE_ID('E', 'N', 'D', ' ')     /* unofficial END-of-FORM identifier (see Amiga RKM Devices Ed.3 page 376) */
82
83
3
#define ID_ILBM     MAKE_ID('I', 'L', 'B', 'M')     /* EA IFF 85 raster bitmap form */
84
#define ID_DEEP     MAKE_ID('D', 'E', 'E', 'P')     /* Chunky pixel image files (Used in TV Paint) */
85
#define ID_RGB8     MAKE_ID('R', 'G', 'B', '8')     /* RGB image forms, Turbo Silver (Impulse) */
86
#define ID_RGBN     MAKE_ID('R', 'G', 'B', 'N')     /* RGB image forms, Turbo Silver (Impulse) */
87
3
#define ID_PBM      MAKE_ID('P', 'B', 'M', ' ')     /* 256-color chunky format (DPaint 2 ?) */
88
#define ID_ACBM     MAKE_ID('A', 'C', 'B', 'M')     /* Amiga Contiguous Bitmap (AmigaBasic) */
89
/* generic */
90
#define ID_FVER     MAKE_ID('F', 'V', 'E', 'R')     /* AmigaOS version string */
91
#define ID_JUNK     MAKE_ID('J', 'U', 'N', 'K')     /* always ignore this chunk */
92
#define ID_ANNO     MAKE_ID('A', 'N', 'N', 'O')     /* EA IFF 85 Generic Annotation chunk */
93
#define ID_AUTH     MAKE_ID('A', 'U', 'T', 'H')     /* EA IFF 85 Generic Author chunk */
94
#define ID_CHRS     MAKE_ID('C', 'H', 'R', 'S')     /* EA IFF 85 Generic character string chunk */
95
#define ID_NAME     MAKE_ID('N', 'A', 'M', 'E')     /* EA IFF 85 Generic Name of art, music, etc. chunk */
96
#define ID_TEXT     MAKE_ID('T', 'E', 'X', 'T')     /* EA IFF 85 Generic unformatted ASCII text chunk */
97
#define ID_copy     MAKE_ID('(', 'c', ')', ' ')     /* EA IFF 85 Generic Copyright text chunk */
98
/* ILBM chunks */
99
0
#define ID_BMHD     MAKE_ID('B', 'M', 'H', 'D')     /* ILBM BitmapHeader */
100
0
#define ID_CMAP     MAKE_ID('C', 'M', 'A', 'P')     /* ILBM 8bit RGB colormap */
101
#define ID_GRAB     MAKE_ID('G', 'R', 'A', 'B')     /* ILBM "hotspot" coordiantes */
102
#define ID_DEST     MAKE_ID('D', 'E', 'S', 'T')     /* ILBM destination image info */
103
#define ID_SPRT     MAKE_ID('S', 'P', 'R', 'T')     /* ILBM sprite identifier */
104
#define ID_CAMG     MAKE_ID('C', 'A', 'M', 'G')     /* Amiga viewportmodes */
105
0
#define ID_BODY     MAKE_ID('B', 'O', 'D', 'Y')     /* ILBM image data */
106
#define ID_CRNG     MAKE_ID('C', 'R', 'N', 'G')     /* color cycling */
107
#define ID_CCRT     MAKE_ID('C', 'C', 'R', 'T')     /* color cycling */
108
#define ID_CLUT     MAKE_ID('C', 'L', 'U', 'T')     /* Color Lookup Table chunk */
109
#define ID_DPI      MAKE_ID('D', 'P', 'I', ' ')     /* Dots per inch chunk */
110
#define ID_DPPV     MAKE_ID('D', 'P', 'P', 'V')     /* DPaint perspective chunk (EA) */
111
#define ID_DRNG     MAKE_ID('D', 'R', 'N', 'G')     /* DPaint IV enhanced color cycle chunk (EA) */
112
#define ID_EPSF     MAKE_ID('E', 'P', 'S', 'F')     /* Encapsulated Postscript chunk */
113
#define ID_CMYK     MAKE_ID('C', 'M', 'Y', 'K')     /* Cyan, Magenta, Yellow, & Black color map (Soft-Logik) */
114
#define ID_CNAM     MAKE_ID('C', 'N', 'A', 'M')     /* Color naming chunk (Soft-Logik) */
115
#define ID_PCHG     MAKE_ID('P', 'C', 'H', 'G')     /* Line by line palette control information (Sebastiano Vigna) */
116
#define ID_PRVW     MAKE_ID('P', 'R', 'V', 'W')     /* A mini duplicate ILBM used for preview (Gary Bonham) */
117
#define ID_XBMI     MAKE_ID('X', 'B', 'M', 'I')     /* eXtended BitMap Information (Soft-Logik) */
118
#define ID_CTBL     MAKE_ID('C', 'T', 'B', 'L')     /* Newtek Dynamic Ham color chunk */
119
#define ID_DYCP     MAKE_ID('D', 'Y', 'C', 'P')     /* Newtek Dynamic Ham chunk */
120
#define ID_SHAM     MAKE_ID('S', 'H', 'A', 'M')     /* Sliced HAM color chunk */
121
#define ID_ABIT     MAKE_ID('A', 'B', 'I', 'T')     /* ACBM body chunk */
122
#define ID_DCOL     MAKE_ID('D', 'C', 'O', 'L')     /* unofficial direct color */
123
124
// ==========================================================
125
// Plugin Interface
126
// ==========================================================
127
128
static int s_format_id;
129
130
// ==========================================================
131
// Plugin Implementation
132
// ==========================================================
133
134
static const char * DLL_CALLCONV
135
2
Format() {
136
2
  return "IFF";
137
2
}
138
139
static const char * DLL_CALLCONV
140
0
Description() {
141
0
  return "IFF Interleaved Bitmap";
142
0
}
143
144
static const char * DLL_CALLCONV
145
0
Extension() {
146
0
  return "iff,lbm";
147
0
}
148
149
static const char * DLL_CALLCONV
150
0
RegExpr() {
151
0
  return NULL;
152
0
}
153
154
static const char * DLL_CALLCONV
155
0
MimeType() {
156
0
  return "image/x-iff";
157
0
}
158
159
static BOOL DLL_CALLCONV
160
23.1k
Validate(FreeImageIO *io, fi_handle handle) {
161
23.1k
  DWORD type = 0;
162
163
  // read chunk type
164
23.1k
  io->read_proc(&type, 4, 1, handle);
165
23.1k
#ifndef FREEIMAGE_BIGENDIAN
166
23.1k
  SwapLong(&type);
167
23.1k
#endif
168
169
23.1k
  if(type != ID_FORM)
170
23.1k
    return FALSE;
171
    
172
  // skip 4 bytes
173
3
  io->read_proc(&type, 4, 1, handle);
174
175
  // read chunk type
176
3
  io->read_proc(&type, 4, 1, handle);
177
3
#ifndef FREEIMAGE_BIGENDIAN
178
3
  SwapLong(&type);
179
3
#endif
180
181
  // File format : ID_PBM = Packed Bitmap, ID_ILBM = Interleaved Bitmap
182
3
  return (type == ID_ILBM) || (type == ID_PBM);
183
23.1k
}
184
185
186
static BOOL DLL_CALLCONV
187
0
SupportsExportDepth(int depth) {
188
0
  return FALSE;
189
0
}
190
191
static BOOL DLL_CALLCONV 
192
0
SupportsExportType(FREE_IMAGE_TYPE type) {
193
0
  return FALSE;
194
0
}
195
196
// ----------------------------------------------------------
197
198
static FIBITMAP * DLL_CALLCONV
199
0
Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
200
0
  if (handle != NULL) {
201
0
    FIBITMAP *dib = NULL;
202
203
0
    DWORD type, size;
204
205
0
    io->read_proc(&type, 4, 1, handle);
206
0
#ifndef FREEIMAGE_BIGENDIAN
207
0
    SwapLong(&type);
208
0
#endif
209
210
0
    if(type != ID_FORM)
211
0
      return NULL;
212
213
0
    io->read_proc(&size, 4, 1, handle);
214
0
#ifndef FREEIMAGE_BIGENDIAN
215
0
    SwapLong(&size);
216
0
#endif
217
218
0
    io->read_proc(&type, 4, 1, handle);
219
0
#ifndef FREEIMAGE_BIGENDIAN
220
0
    SwapLong(&type);
221
0
#endif
222
223
0
    if((type != ID_ILBM) && (type != ID_PBM))
224
0
      return NULL;
225
226
0
    size -= 4;
227
228
0
    unsigned width = 0, height = 0, planes = 0, depth = 0, comp = 0;
229
230
0
    while (size) {
231
0
      DWORD ch_type,ch_size;
232
233
0
      io->read_proc(&ch_type, 4, 1, handle);
234
0
#ifndef FREEIMAGE_BIGENDIAN
235
0
      SwapLong(&ch_type);
236
0
#endif
237
238
0
      io->read_proc(&ch_size,4,1,handle );
239
0
#ifndef FREEIMAGE_BIGENDIAN
240
0
      SwapLong(&ch_size);
241
0
#endif
242
243
0
      unsigned ch_end = io->tell_proc(handle) + ch_size;
244
245
0
      if (ch_type == ID_BMHD) {     // Bitmap Header
246
0
        if (dib)
247
0
          FreeImage_Unload(dib);
248
249
0
        BMHD bmhd;
250
251
0
        io->read_proc(&bmhd, sizeof(bmhd), 1, handle);
252
0
#ifndef FREEIMAGE_BIGENDIAN
253
0
        SwapHeader(&bmhd);
254
0
#endif
255
256
0
        width = bmhd.w;
257
0
        height = bmhd.h;
258
0
        planes = bmhd.nPlanes;
259
0
        comp = bmhd.compression;
260
261
0
        if(bmhd.masking & 1)
262
0
          planes++; // there is a mask ( 'stencil' )
263
264
0
        if (planes > 8 && planes != 24)
265
0
          return NULL;
266
267
0
        depth = planes > 8 ? 24 : 8;
268
269
0
        if( depth == 24 ) {
270
0
          dib = FreeImage_Allocate(width, height, depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
271
0
        } else {
272
0
          dib = FreeImage_Allocate(width, height, depth);
273
0
        }
274
0
      } else if (ch_type == ID_CMAP) { // Palette (Color Map)
275
0
        if (!dib)
276
0
          return NULL;
277
278
0
        RGBQUAD *pal = FreeImage_GetPalette(dib);
279
0
        if(pal != NULL) {
280
0
          unsigned palette_entries = MIN((unsigned)ch_size / 3, FreeImage_GetColorsUsed(dib));
281
0
          for (unsigned k = 0; k < palette_entries; k++) {         
282
0
            io->read_proc(&pal[k].rgbRed, 1, 1, handle );
283
0
            io->read_proc(&pal[k].rgbGreen, 1, 1, handle );
284
0
            io->read_proc(&pal[k].rgbBlue, 1, 1, handle );
285
0
          }
286
0
        }
287
0
      } else if (ch_type == ID_BODY) {
288
0
        if (!dib)
289
0
          return NULL;
290
291
0
        if (type == ID_PBM) {
292
          // NON INTERLACED (LBM)
293
294
0
          unsigned line = FreeImage_GetLine(dib) + 1 & ~1;
295
          
296
0
          for (unsigned i = 0; i < FreeImage_GetHeight(dib); i++) {
297
0
            BYTE *bits = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - i - 1);
298
299
0
            if (comp == 1) {
300
              // use RLE compression
301
302
0
              DWORD number_of_bytes_written = 0;
303
0
              BYTE rle_count;
304
0
              BYTE byte;
305
306
0
              while (number_of_bytes_written < line) {
307
0
                io->read_proc(&rle_count, 1, 1, handle);
308
309
0
                if (rle_count < 128) {
310
0
                  for (int k = 0; k < rle_count + 1; k++) {
311
0
                    io->read_proc(&byte, 1, 1, handle);
312
313
0
                    bits[number_of_bytes_written++] += byte;
314
0
                  }
315
0
                } else if (rle_count > 128) {
316
0
                  io->read_proc(&byte, 1, 1, handle);
317
318
0
                  for (int k = 0; k < 257 - rle_count; k++) {
319
0
                    bits[number_of_bytes_written++] += byte;
320
0
                  }
321
0
                }
322
0
              }
323
0
            } else {
324
              // don't use compression
325
326
0
              io->read_proc(bits, line, 1, handle);
327
0
            }
328
0
          }
329
330
0
          return dib;
331
0
        } else {
332
          // INTERLACED (ILBM)
333
334
0
          unsigned pixel_size = depth/8;
335
0
          unsigned n_width=(width+15)&~15;
336
0
          unsigned plane_size = n_width/8;
337
0
          unsigned src_size = plane_size * planes;
338
0
          BYTE *src = (BYTE*)malloc(src_size);
339
0
          BYTE *dest = FreeImage_GetBits(dib);
340
341
0
          dest += FreeImage_GetPitch(dib) * height;
342
343
0
          for (unsigned y = 0; y < height; y++) {
344
0
            dest -= FreeImage_GetPitch(dib);
345
346
            // read all planes in one hit,
347
            // 'coz PSP compresses across planes...
348
349
0
            if (comp) {
350
              // unpacker algorithm
351
352
0
              for(unsigned x = 0; x < src_size;) {
353
                // read the next source byte into t
354
0
                signed char t = 0;
355
0
                io->read_proc(&t, 1, 1, handle);
356
                
357
0
                if (t >= 0) {
358
                  // t = [0..127] => copy the next t+1 bytes literally
359
0
                  unsigned size_to_read = t + 1;
360
361
0
                  if((size_to_read + x) > src_size) {
362
                    // sanity check for buffer overruns 
363
0
                    size_to_read = src_size - x;
364
0
                    io->read_proc(src + x, size_to_read, 1, handle);
365
0
                    x += (t + 1);
366
0
                  } else {
367
0
                    io->read_proc(src + x, size_to_read, 1, handle);
368
0
                    x += size_to_read;
369
0
                  }
370
0
                } else if (t != -128) {
371
                  // t = [-1..-127]  => replicate the next byte -t+1 times
372
0
                  BYTE b = 0;
373
0
                  io->read_proc(&b, 1, 1, handle);
374
0
                  unsigned size_to_copy = (unsigned)(-(int)t + 1);
375
376
0
                  if((size_to_copy + x) > src_size) {
377
                    // sanity check for buffer overruns 
378
0
                    size_to_copy = src_size - x;
379
0
                    memset(src + x, b, size_to_copy);
380
0
                    x += (unsigned)(-(int)t + 1);
381
0
                  } else {
382
0
                    memset(src + x, b, size_to_copy);
383
0
                    x += size_to_copy;
384
0
                  }
385
0
                }
386
                // t = -128 => noop
387
0
              }
388
0
            } else {
389
0
              io->read_proc(src, src_size, 1, handle);
390
0
            }
391
392
            // lazy planar->chunky...
393
394
0
            for (unsigned x = 0; x < width; x++) {
395
0
              for (unsigned n = 0; n < planes; n++) {
396
0
                BYTE bit = (BYTE)( src[n * plane_size + (x / 8)] >> ((x^7) & 7) );
397
398
0
                dest[x * pixel_size + (n / 8)] |= (bit & 1) << (n & 7);
399
0
              }
400
0
            }
401
402
0
#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
403
0
            if (depth == 24) {
404
0
              for (unsigned x = 0; x < width; ++x){
405
0
                INPLACESWAP(dest[x * 3], dest[x * 3 + 2]);
406
0
              }
407
0
            }
408
0
#endif
409
0
          }
410
411
0
          free(src);
412
413
0
          return dib;
414
0
        }
415
0
      }
416
417
      // Every odd-length chunk is followed by a 0 pad byte.  This pad
418
      //  byte is not counted in ch_size.
419
0
      if (ch_size & 1) {
420
0
        ch_size++;
421
0
        ch_end++;
422
0
      }
423
424
0
      io->seek_proc(handle, ch_end - io->tell_proc(handle), SEEK_CUR);
425
426
0
      size -= ch_size + 8;
427
0
    }
428
429
0
    if (dib)
430
0
      FreeImage_Unload(dib);
431
0
  }
432
433
0
  return 0;
434
0
}
435
436
// ==========================================================
437
//   Init
438
// ==========================================================
439
440
void DLL_CALLCONV
441
2
InitIFF(Plugin *plugin, int format_id) {
442
2
  s_format_id = format_id;
443
444
2
  plugin->format_proc = Format;
445
2
  plugin->description_proc = Description;
446
2
  plugin->extension_proc = Extension;
447
2
  plugin->regexpr_proc = RegExpr;
448
2
  plugin->open_proc = NULL;
449
2
  plugin->close_proc = NULL;
450
2
  plugin->pagecount_proc = NULL;
451
2
  plugin->pagecapability_proc = NULL;
452
2
  plugin->load_proc = Load;
453
2
  plugin->save_proc = NULL;
454
2
  plugin->validate_proc = Validate;
455
2
  plugin->mime_proc = MimeType;
456
2
  plugin->supports_export_bpp_proc = SupportsExportDepth;
457
2
  plugin->supports_export_type_proc = SupportsExportType;
458
2
  plugin->supports_icc_profiles_proc = NULL;
459
2
}