/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 | | |