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