Coverage Report

Created: 2026-10-16 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeimage-svn/FreeImage/trunk/Source/FreeImage/PluginSGI.cpp
Line
Count
Source
1
// ==========================================================
2
// SGI Loader
3
//
4
// Design and implementation by
5
// - Sherman Wilcox
6
// - Noam Gat
7
//
8
// References : 
9
// ------------
10
// - The SGI Image File Format, Version 1.0
11
// http://astronomy.swin.edu.au/~pbourke/dataformats/sgirgb/sgiversion.html
12
// - SGI RGB Image Format
13
// http://astronomy.swin.edu.au/~pbourke/dataformats/sgirgb/
14
//
15
//
16
// This file is part of FreeImage 3
17
//
18
// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
19
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
20
// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
21
// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
22
// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
23
// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
24
// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
25
// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
26
// THIS DISCLAIMER.
27
//
28
// Use at your own risk!
29
// ==========================================================
30
31
#include "FreeImage.h"
32
#include "Utilities.h"
33
34
// ----------------------------------------------------------
35
//   Constants + headers
36
// ----------------------------------------------------------
37
38
#ifdef _WIN32
39
#pragma pack(push, 1)
40
#else
41
#pragma pack(1)
42
#endif
43
44
typedef struct tagSGIHeader {
45
  /** IRIS image file magic number. This should be decimal 474. */
46
  WORD magic;
47
  /** Storage format: 0 for uncompressed, 1 for RLE compression. */
48
  BYTE storage;
49
  /** Number of bytes per pixel channel. Legally 1 or 2. */
50
  BYTE bpc;
51
  /**
52
  Number of dimensions. Legally 1, 2, or 3. 
53
  1 means a single row, XSIZE long
54
  2 means a single 2D image
55
  3 means multiple 2D images
56
  */
57
  WORD dimension; 
58
  /** X size in pixels */
59
  WORD xsize;
60
  /** Y size in pixels */
61
  WORD ysize;
62
  /**
63
  Number of channels. 
64
  1 indicates greyscale
65
  3 indicates RGB
66
  4 indicates RGB and Alpha
67
  */
68
  WORD zsize;
69
  /** Minimum pixel value. This is the lowest pixel value in the image.*/
70
  LONG pixmin;
71
  /** Maximum pixel value. This is the highest pixel value in the image.*/
72
  LONG pixmax;
73
  /** Ignored. Normally set to 0. */
74
  char dummy[4];
75
  /** Image name. Must be null terminated, therefore at most 79 bytes. */
76
  char imagename[80];
77
  /** 
78
  Colormap ID. 
79
  0 - normal mode
80
  1 - dithered, 3 mits for red and green, 2 for blue, obsolete
81
  2 - index colour, obsolete
82
  3 - not an image but a colourmap
83
  */
84
  LONG colormap;
85
  /** Ignored. Should be set to 0, makes the header 512 bytes. */
86
  char reserved[404];
87
} SGIHeader;
88
89
typedef struct tagRLEStatus {
90
  int cnt;
91
  int val;
92
} RLEStatus;
93
94
#ifdef _WIN32
95
#pragma pack(pop)
96
#else
97
#pragma pack()
98
#endif
99
100
static const char *SGI_LESS_THAN_HEADER_LENGTH = "Incorrect header size";
101
static const char *SGI_16_BIT_COMPONENTS_NOT_SUPPORTED = "No 16 bit support";
102
static const char *SGI_COLORMAPS_NOT_SUPPORTED = "No colormap support";
103
static const char *SGI_EOF_IN_RLE_INDEX = "EOF in run length encoding";
104
static const char *SGI_EOF_IN_IMAGE_DATA = "EOF in image data";
105
static const char *SGI_INVALID_CHANNEL_COUNT = "Invalid channel count";
106
107
// ==========================================================
108
// Plugin Interface
109
// ==========================================================
110
111
static int s_format_id;
112
113
// ==========================================================
114
// Plugin Implementation
115
// ==========================================================
116
117
#ifndef FREEIMAGE_BIGENDIAN
118
static void 
119
0
SwapHeader(SGIHeader *header) {
120
0
  SwapShort(&header->magic);
121
0
  SwapShort(&header->dimension);
122
0
  SwapShort(&header->xsize);
123
0
  SwapShort(&header->ysize);
124
0
  SwapShort(&header->zsize);
125
0
  SwapLong((DWORD*)&header->pixmin);
126
0
  SwapLong((DWORD*)&header->pixmax);
127
0
  SwapLong((DWORD*)&header->colormap);
128
0
}
129
#endif
130
131
static int 
132
0
get_rlechar(FreeImageIO *io, fi_handle handle, RLEStatus *pstatus) {
133
0
  if (!pstatus->cnt) {
134
0
    int cnt = 0;
135
0
    while (0 == cnt) {
136
0
      BYTE packed = 0;
137
0
      if(io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1) {
138
0
        return EOF;
139
0
      }
140
0
      cnt = packed;
141
0
    }
142
0
    if (cnt == EOF) {
143
0
      return EOF;
144
0
    }
145
0
    pstatus->cnt = cnt & 0x7F;
146
0
    if (cnt & 0x80) {
147
0
      pstatus->val = -1;
148
0
    } else {
149
0
      BYTE packed = 0;
150
0
      if(io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1) {
151
0
        return EOF;
152
0
      }
153
0
      pstatus->val = packed;
154
0
    }
155
0
  }
156
0
  pstatus->cnt--;
157
0
  if (pstatus->val == -1) {
158
0
    BYTE packed = 0;
159
0
    if(io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1) {
160
0
      return EOF;
161
0
    }
162
0
    return packed;
163
0
  }
164
0
  else {
165
0
    return pstatus->val;
166
0
  }
167
0
}
168
169
static const char * DLL_CALLCONV
170
2
Format() {
171
2
  return "SGI";
172
2
}
173
174
static const char * DLL_CALLCONV
175
0
Description() {
176
0
  return "SGI Image Format";
177
0
}
178
179
static const char * DLL_CALLCONV
180
0
Extension() {
181
0
  return "sgi,rgb,rgba,bw";
182
0
}
183
184
static const char * DLL_CALLCONV
185
0
RegExpr() {
186
0
  return NULL;
187
0
}
188
189
static const char * DLL_CALLCONV
190
0
MimeType() {
191
0
  return "image/x-sgi";
192
0
}
193
194
static BOOL DLL_CALLCONV
195
37.3k
Validate(FreeImageIO *io, fi_handle handle) {
196
37.3k
  BYTE sgi_signature[2] = { 0x01, 0xDA };
197
37.3k
  BYTE signature[2] = { 0, 0 };
198
199
37.3k
  io->read_proc(signature, 1, sizeof(sgi_signature), handle);
200
201
37.3k
  return (memcmp(sgi_signature, signature, sizeof(sgi_signature)) == 0);
202
37.3k
}
203
204
static BOOL DLL_CALLCONV
205
0
SupportsExportDepth(int depth) {
206
0
  return FALSE;
207
0
}
208
209
static BOOL DLL_CALLCONV 
210
0
SupportsExportType(FREE_IMAGE_TYPE type) {
211
0
  return FALSE;
212
0
}
213
214
static FIBITMAP * DLL_CALLCONV
215
0
Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
216
0
  int width = 0, height = 0, zsize = 0;
217
0
  int i, dim;
218
0
  int bitcount;
219
0
  SGIHeader sgiHeader;
220
0
  RLEStatus my_rle_status;
221
0
  FIBITMAP *dib = NULL;
222
0
  LONG *pRowIndex = NULL;
223
224
0
  try {
225
    // read the header
226
0
    memset(&sgiHeader, 0, sizeof(SGIHeader));
227
0
    if(io->read_proc(&sgiHeader, 1, sizeof(SGIHeader), handle) < sizeof(SGIHeader)) {
228
0
       throw SGI_LESS_THAN_HEADER_LENGTH;
229
0
    }
230
0
#ifndef FREEIMAGE_BIGENDIAN
231
0
    SwapHeader(&sgiHeader);
232
0
#endif
233
0
    if(sgiHeader.magic != 474) {
234
0
      throw FI_MSG_ERROR_MAGIC_NUMBER;
235
0
    }
236
    
237
0
    BOOL bIsRLE = (sgiHeader.storage == 1) ? TRUE : FALSE;
238
  
239
    // check for unsupported image types
240
0
    if (sgiHeader.bpc != 1) {
241
      // Expected one byte per color component
242
0
      throw SGI_16_BIT_COMPONENTS_NOT_SUPPORTED; 
243
0
    }
244
0
    if (sgiHeader.colormap != 0) {
245
      // Indexed or dithered images not supported
246
0
      throw SGI_COLORMAPS_NOT_SUPPORTED; 
247
0
    }
248
249
    // get the width & height
250
0
    dim = sgiHeader.dimension;
251
0
    width = sgiHeader.xsize;
252
0
    if (dim < 3) {
253
0
      zsize = 1;
254
0
    } else {
255
0
      zsize = sgiHeader.zsize;
256
0
    }
257
258
0
    if (dim < 2) {
259
0
      height = 1;
260
0
    } else {
261
0
      height = sgiHeader.ysize;
262
0
    }
263
    
264
0
    if(bIsRLE) {
265
      // read the Offset Tables 
266
0
      int index_len = height * zsize;
267
0
      pRowIndex = (LONG*)malloc(index_len * sizeof(LONG));
268
0
      if(!pRowIndex) {
269
0
        throw FI_MSG_ERROR_MEMORY;
270
0
      }
271
      
272
0
      if ((unsigned)index_len != io->read_proc(pRowIndex, sizeof(LONG), index_len, handle)) {
273
0
        throw SGI_EOF_IN_RLE_INDEX;
274
0
      }
275
      
276
0
#ifndef FREEIMAGE_BIGENDIAN   
277
      // Fix byte order in index
278
0
      for (i = 0; i < index_len; i++) {
279
0
        SwapLong((DWORD*)&pRowIndex[i]);
280
0
      }
281
0
#endif
282
      // Discard row size index
283
0
      for (i = 0; i < (int)(index_len * sizeof(LONG)); i++) {
284
0
        BYTE packed = 0;
285
0
        if( io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1 ) {
286
0
          throw SGI_EOF_IN_RLE_INDEX;
287
0
        }
288
0
      }
289
0
    }
290
    
291
0
    switch(zsize) {
292
0
      case 1:
293
0
        bitcount = 8;
294
0
        break;
295
0
      case 2:
296
        //Grayscale+Alpha. Need to fake RGBA
297
0
        bitcount = 32;
298
0
        break;
299
0
      case 3:
300
0
        bitcount = 24;
301
0
        break;
302
0
      case 4:
303
0
        bitcount = 32;
304
0
        break;
305
0
      default:
306
0
        throw SGI_INVALID_CHANNEL_COUNT;
307
0
    }
308
    
309
0
    dib = FreeImage_Allocate(width, height, bitcount);
310
0
    if(!dib) {
311
0
      throw FI_MSG_ERROR_DIB_MEMORY;
312
0
    }
313
    
314
0
    if (bitcount == 8) {
315
      // 8-bit SGI files are grayscale images, so we'll generate
316
      // a grayscale palette.
317
0
      RGBQUAD *pclrs = FreeImage_GetPalette(dib);
318
0
      for (i = 0; i < 256; i++) {
319
0
        pclrs[i].rgbRed = (BYTE)i;
320
0
        pclrs[i].rgbGreen = (BYTE)i;
321
0
        pclrs[i].rgbBlue = (BYTE)i;
322
0
        pclrs[i].rgbReserved = 0;
323
0
      }
324
0
    }
325
326
    // decode the image
327
328
0
    memset(&my_rle_status, 0, sizeof(RLEStatus));
329
    
330
0
    int ns = FreeImage_GetPitch(dib);                                                    
331
0
    BYTE *pStartRow = FreeImage_GetScanLine(dib, 0);
332
0
    int offset_table[] = { 2, 1, 0, 3 };
333
0
    int numChannels = zsize;
334
0
    if (zsize < 3) {
335
0
      offset_table[0] = 0;
336
0
    }
337
0
    if (zsize == 2)
338
0
    {
339
      //This is how faked grayscale+alpha works.
340
      //First channel goes into first 
341
      //second channel goes into alpha (4th channel)
342
      //Two channels are left empty and will be copied later
343
0
      offset_table[1] = 3;
344
0
      numChannels = 4;
345
0
    }
346
    
347
0
    LONG *pri = pRowIndex;
348
0
    for (i = 0; i < zsize; i++) {
349
0
      BYTE *pRow = pStartRow + offset_table[i];
350
0
      for (int j = 0; j < height; j++, pRow += ns, pri++) {
351
0
        BYTE *p = pRow;
352
0
        if (bIsRLE) {
353
0
          my_rle_status.cnt = 0;
354
0
          io->seek_proc(handle, *pri, SEEK_SET);
355
0
        }
356
0
        for (int k = 0; k < width; k++, p += numChannels) {
357
0
          int ch;
358
0
          BYTE packed = 0;
359
0
          if (bIsRLE) {
360
0
            ch = get_rlechar(io, handle, &my_rle_status);
361
0
            packed = (BYTE)ch;
362
0
          }
363
0
          else {
364
0
            ch = io->read_proc(&packed, sizeof(BYTE), 1, handle);
365
0
          }
366
0
          if (ch == EOF) {
367
0
            throw SGI_EOF_IN_IMAGE_DATA;
368
0
          }
369
0
          *p = packed;
370
0
        }
371
0
      }
372
0
    }
373
    
374
0
    if (zsize == 2)
375
0
    {
376
0
      BYTE *pRow = pStartRow;
377
      //If faking RGBA from grayscale + alpha, copy first channel to second and third
378
0
      for (int i=0; i<height; i++, pRow += ns)
379
0
      {
380
0
        BYTE *pPixel = pRow;
381
0
        for (int j=0; j<width; j++)
382
0
        {
383
0
          pPixel[2] = pPixel[1] = pPixel[0];
384
0
          pPixel += 4;
385
0
        }
386
0
      }
387
0
    }
388
0
    if(pRowIndex)
389
0
      free(pRowIndex);
390
391
0
    return dib;
392
393
0
  } catch(const char *text) {
394
0
    if(pRowIndex) free(pRowIndex);
395
0
    if(dib) FreeImage_Unload(dib);
396
0
    FreeImage_OutputMessageProc(s_format_id, text);
397
0
    return NULL;
398
0
  }
399
0
}
400
401
// ==========================================================
402
//   Init
403
// ==========================================================
404
405
void DLL_CALLCONV 
406
2
InitSGI(Plugin *plugin, int format_id) {
407
2
  s_format_id = format_id;
408
  
409
2
  plugin->format_proc = Format;
410
2
  plugin->description_proc = Description;
411
2
  plugin->extension_proc = Extension;
412
2
  plugin->regexpr_proc = RegExpr;
413
2
  plugin->open_proc = NULL;
414
2
  plugin->close_proc = NULL;
415
2
  plugin->pagecount_proc = NULL;
416
2
  plugin->pagecapability_proc = NULL;
417
2
  plugin->load_proc = Load;
418
2
  plugin->save_proc = NULL;
419
2
  plugin->validate_proc = Validate;
420
2
  plugin->mime_proc = MimeType;
421
2
  plugin->supports_export_bpp_proc = SupportsExportDepth;
422
2
  plugin->supports_export_type_proc = SupportsExportType;
423
  plugin->supports_icc_profiles_proc = NULL;
424
2
}
425