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
SwapHeader(SGIHeader * header)119 SwapHeader(SGIHeader *header) {
120 SwapShort(&header->magic);
121 SwapShort(&header->dimension);
122 SwapShort(&header->xsize);
123 SwapShort(&header->ysize);
124 SwapShort(&header->zsize);
125 SwapLong((DWORD*)&header->pixmin);
126 SwapLong((DWORD*)&header->pixmax);
127 SwapLong((DWORD*)&header->colormap);
128 }
129 #endif
130
131 static int
get_rlechar(FreeImageIO * io,fi_handle handle,RLEStatus * pstatus)132 get_rlechar(FreeImageIO *io, fi_handle handle, RLEStatus *pstatus) {
133 if (!pstatus->cnt) {
134 int cnt = 0;
135 while (0 == cnt) {
136 BYTE packed = 0;
137 if(io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1) {
138 return EOF;
139 }
140 cnt = packed;
141 }
142 if (cnt == EOF) {
143 return EOF;
144 }
145 pstatus->cnt = cnt & 0x7F;
146 if (cnt & 0x80) {
147 pstatus->val = -1;
148 } else {
149 BYTE packed = 0;
150 if(io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1) {
151 return EOF;
152 }
153 pstatus->val = packed;
154 }
155 }
156 pstatus->cnt--;
157 if (pstatus->val == -1) {
158 BYTE packed = 0;
159 if(io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1) {
160 return EOF;
161 }
162 return packed;
163 }
164 else {
165 return pstatus->val;
166 }
167 }
168
169 static const char * DLL_CALLCONV
Format()170 Format() {
171 return "SGI";
172 }
173
174 static const char * DLL_CALLCONV
Description()175 Description() {
176 return "SGI Image Format";
177 }
178
179 static const char * DLL_CALLCONV
Extension()180 Extension() {
181 return "sgi,rgb,rgba,bw";
182 }
183
184 static const char * DLL_CALLCONV
RegExpr()185 RegExpr() {
186 return NULL;
187 }
188
189 static const char * DLL_CALLCONV
MimeType()190 MimeType() {
191 return "image/x-sgi";
192 }
193
194 static BOOL DLL_CALLCONV
Validate(FreeImageIO * io,fi_handle handle)195 Validate(FreeImageIO *io, fi_handle handle) {
196 BYTE sgi_signature[2] = { 0x01, 0xDA };
197 BYTE signature[2] = { 0, 0 };
198
199 io->read_proc(signature, 1, sizeof(sgi_signature), handle);
200
201 return (memcmp(sgi_signature, signature, sizeof(sgi_signature)) == 0);
202 }
203
204 static BOOL DLL_CALLCONV
SupportsExportDepth(int depth)205 SupportsExportDepth(int depth) {
206 return FALSE;
207 }
208
209 static BOOL DLL_CALLCONV
SupportsExportType(FREE_IMAGE_TYPE type)210 SupportsExportType(FREE_IMAGE_TYPE type) {
211 return FALSE;
212 }
213
214 static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO * io,fi_handle handle,int page,int flags,void * data)215 Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
216 int width = 0, height = 0, zsize = 0;
217 int i, dim;
218 int bitcount;
219 SGIHeader sgiHeader;
220 RLEStatus my_rle_status;
221 FIBITMAP *dib = NULL;
222 LONG *pRowIndex = NULL;
223
224 try {
225 // read the header
226 memset(&sgiHeader, 0, sizeof(SGIHeader));
227 if(io->read_proc(&sgiHeader, 1, sizeof(SGIHeader), handle) < sizeof(SGIHeader)) {
228 throw SGI_LESS_THAN_HEADER_LENGTH;
229 }
230 #ifndef FREEIMAGE_BIGENDIAN
231 SwapHeader(&sgiHeader);
232 #endif
233 if(sgiHeader.magic != 474) {
234 throw FI_MSG_ERROR_MAGIC_NUMBER;
235 }
236
237 BOOL bIsRLE = (sgiHeader.storage == 1) ? TRUE : FALSE;
238
239 // check for unsupported image types
240 if (sgiHeader.bpc != 1) {
241 // Expected one byte per color component
242 throw SGI_16_BIT_COMPONENTS_NOT_SUPPORTED;
243 }
244 if (sgiHeader.colormap != 0) {
245 // Indexed or dithered images not supported
246 throw SGI_COLORMAPS_NOT_SUPPORTED;
247 }
248
249 // get the width & height
250 dim = sgiHeader.dimension;
251 width = sgiHeader.xsize;
252 if (dim < 3) {
253 zsize = 1;
254 } else {
255 zsize = sgiHeader.zsize;
256 }
257
258 if (dim < 2) {
259 height = 1;
260 } else {
261 height = sgiHeader.ysize;
262 }
263
264 if(bIsRLE) {
265 // read the Offset Tables
266 int index_len = height * zsize;
267 pRowIndex = (LONG*)malloc(index_len * sizeof(LONG));
268 if(!pRowIndex) {
269 throw FI_MSG_ERROR_MEMORY;
270 }
271
272 if ((unsigned)index_len != io->read_proc(pRowIndex, sizeof(LONG), index_len, handle)) {
273 throw SGI_EOF_IN_RLE_INDEX;
274 }
275
276 #ifndef FREEIMAGE_BIGENDIAN
277 // Fix byte order in index
278 for (i = 0; i < index_len; i++) {
279 SwapLong((DWORD*)&pRowIndex[i]);
280 }
281 #endif
282 // Discard row size index
283 for (i = 0; i < (int)(index_len * sizeof(LONG)); i++) {
284 BYTE packed = 0;
285 if( io->read_proc(&packed, sizeof(BYTE), 1, handle) < 1 ) {
286 throw SGI_EOF_IN_RLE_INDEX;
287 }
288 }
289 }
290
291 switch(zsize) {
292 case 1:
293 bitcount = 8;
294 break;
295 case 2:
296 //Grayscale+Alpha. Need to fake RGBA
297 bitcount = 32;
298 break;
299 case 3:
300 bitcount = 24;
301 break;
302 case 4:
303 bitcount = 32;
304 break;
305 default:
306 throw SGI_INVALID_CHANNEL_COUNT;
307 }
308
309 dib = FreeImage_Allocate(width, height, bitcount);
310 if(!dib) {
311 throw FI_MSG_ERROR_DIB_MEMORY;
312 }
313
314 if (bitcount == 8) {
315 // 8-bit SGI files are grayscale images, so we'll generate
316 // a grayscale palette.
317 RGBQUAD *pclrs = FreeImage_GetPalette(dib);
318 for (i = 0; i < 256; i++) {
319 pclrs[i].rgbRed = (BYTE)i;
320 pclrs[i].rgbGreen = (BYTE)i;
321 pclrs[i].rgbBlue = (BYTE)i;
322 pclrs[i].rgbReserved = 0;
323 }
324 }
325
326 // decode the image
327
328 memset(&my_rle_status, 0, sizeof(RLEStatus));
329
330 int ns = FreeImage_GetPitch(dib);
331 BYTE *pStartRow = FreeImage_GetScanLine(dib, 0);
332 int offset_table[] = { 2, 1, 0, 3 };
333 int numChannels = zsize;
334 if (zsize < 3) {
335 offset_table[0] = 0;
336 }
337 if (zsize == 2)
338 {
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 offset_table[1] = 3;
344 numChannels = 4;
345 }
346
347 LONG *pri = pRowIndex;
348 for (i = 0; i < zsize; i++) {
349 BYTE *pRow = pStartRow + offset_table[i];
350 for (int j = 0; j < height; j++, pRow += ns, pri++) {
351 BYTE *p = pRow;
352 if (bIsRLE) {
353 my_rle_status.cnt = 0;
354 io->seek_proc(handle, *pri, SEEK_SET);
355 }
356 for (int k = 0; k < width; k++, p += numChannels) {
357 int ch;
358 BYTE packed = 0;
359 if (bIsRLE) {
360 ch = get_rlechar(io, handle, &my_rle_status);
361 packed = (BYTE)ch;
362 }
363 else {
364 ch = io->read_proc(&packed, sizeof(BYTE), 1, handle);
365 }
366 if (ch == EOF) {
367 throw SGI_EOF_IN_IMAGE_DATA;
368 }
369 *p = packed;
370 }
371 }
372 }
373
374 if (zsize == 2)
375 {
376 BYTE *pRow = pStartRow;
377 //If faking RGBA from grayscale + alpha, copy first channel to second and third
378 for (int i=0; i<height; i++, pRow += ns)
379 {
380 BYTE *pPixel = pRow;
381 for (int j=0; j<width; j++)
382 {
383 pPixel[2] = pPixel[1] = pPixel[0];
384 pPixel += 4;
385 }
386 }
387 }
388 if(pRowIndex)
389 free(pRowIndex);
390
391 return dib;
392
393 } catch(const char *text) {
394 if(pRowIndex) free(pRowIndex);
395 if(dib) FreeImage_Unload(dib);
396 FreeImage_OutputMessageProc(s_format_id, text);
397 return NULL;
398 }
399 }
400
401 // ==========================================================
402 // Init
403 // ==========================================================
404
405 void DLL_CALLCONV
InitSGI(Plugin * plugin,int format_id)406 InitSGI(Plugin *plugin, int format_id) {
407 s_format_id = format_id;
408
409 plugin->format_proc = Format;
410 plugin->description_proc = Description;
411 plugin->extension_proc = Extension;
412 plugin->regexpr_proc = RegExpr;
413 plugin->open_proc = NULL;
414 plugin->close_proc = NULL;
415 plugin->pagecount_proc = NULL;
416 plugin->pagecapability_proc = NULL;
417 plugin->load_proc = Load;
418 plugin->save_proc = NULL;
419 plugin->validate_proc = Validate;
420 plugin->mime_proc = MimeType;
421 plugin->supports_export_bpp_proc = SupportsExportDepth;
422 plugin->supports_export_type_proc = SupportsExportType;
423 plugin->supports_icc_profiles_proc = NULL;
424 }
425
426