/src/freeimage-svn/FreeImage/trunk/Source/FreeImage/PluginRAS.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // ========================================================== |
2 | | // Sun rasterfile Loader |
3 | | // |
4 | | // Design and implementation by |
5 | | // - Hervé Drolon (drolon@infonie.fr) |
6 | | // |
7 | | // This file is part of FreeImage 3 |
8 | | // |
9 | | // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY |
10 | | // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES |
11 | | // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE |
12 | | // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED |
13 | | // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT |
14 | | // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY |
15 | | // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL |
16 | | // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER |
17 | | // THIS DISCLAIMER. |
18 | | // |
19 | | // Use at your own risk! |
20 | | // ========================================================== |
21 | | |
22 | | #include "FreeImage.h" |
23 | | #include "Utilities.h" |
24 | | |
25 | | // ---------------------------------------------------------- |
26 | | // Constants + headers |
27 | | // ---------------------------------------------------------- |
28 | | |
29 | | #ifdef _WIN32 |
30 | | #pragma pack(push, 1) |
31 | | #else |
32 | | #pragma pack(1) |
33 | | #endif |
34 | | |
35 | | typedef struct tagSUNHEADER { |
36 | | DWORD magic; // Magic number |
37 | | DWORD width; // Image width in pixels |
38 | | DWORD height; // Image height in pixels |
39 | | DWORD depth; // Depth (1, 8, 24 or 32 bits) of each pixel |
40 | | DWORD length; // Image length (in bytes) |
41 | | DWORD type; // Format of file (see RT_* below) |
42 | | DWORD maptype; // Type of colormap (see RMT_* below) |
43 | | DWORD maplength; // Length of colormap (in bytes) |
44 | | } SUNHEADER; |
45 | | |
46 | | #ifdef _WIN32 |
47 | | #pragma pack(pop) |
48 | | #else |
49 | | #pragma pack() |
50 | | #endif |
51 | | |
52 | | // ---------------------------------------------------------- |
53 | | |
54 | | // Following the header is the colormap, for maplength bytes (unless maplength is zero), |
55 | | // then the image. Each row of the image is rounded to 2 bytes. |
56 | | |
57 | 0 | #define RAS_MAGIC 0x59A66A95 // Magic number for Sun rasterfiles |
58 | | |
59 | | // Sun supported type's |
60 | | |
61 | 0 | #define RT_OLD 0 // Old format (raw image in 68000 byte order) |
62 | 0 | #define RT_STANDARD 1 // Raw image in 68000 byte order |
63 | 0 | #define RT_BYTE_ENCODED 2 // Run-length encoding of bytes |
64 | 0 | #define RT_FORMAT_RGB 3 // XRGB or RGB instead of XBGR or BGR |
65 | 0 | #define RT_FORMAT_TIFF 4 // TIFF <-> standard rasterfile |
66 | 0 | #define RT_FORMAT_IFF 5 // IFF (TAAC format) <-> standard rasterfile |
67 | | |
68 | | #define RT_EXPERIMENTAL 0xffff // Reserved for testing |
69 | | |
70 | | // These are the possible colormap types. |
71 | | // if it's in RGB format, the map is made up of three byte arrays |
72 | | // (red, green, then blue) that are each 1/3 of the colormap length. |
73 | | |
74 | 0 | #define RMT_NONE 0 // maplength is expected to be 0 |
75 | 0 | #define RMT_EQUAL_RGB 1 // red[maplength/3], green[maplength/3], blue[maplength/3] |
76 | 0 | #define RMT_RAW 2 // Raw colormap |
77 | 0 | #define RESC 128 // Run-length encoding escape character |
78 | | |
79 | | // ----- NOTES ----- |
80 | | // Each line of the image is rounded out to a multiple of 16 bits. |
81 | | // This corresponds to the rounding convention used by the memory pixrect |
82 | | // package (/usr/include/pixrect/memvar.h) of the SunWindows system. |
83 | | // The ras_encoding field (always set to 0 by Sun's supported software) |
84 | | // was renamed to ras_length in release 2.0. As a result, rasterfiles |
85 | | // of type 0 generated by the old software claim to have 0 length; for |
86 | | // compatibility, code reading rasterfiles must be prepared to compute the |
87 | | // TRUE length from the width, height, and depth fields. |
88 | | |
89 | | // ========================================================== |
90 | | // Internal functions |
91 | | // ========================================================== |
92 | | |
93 | | static void |
94 | 0 | ReadData(FreeImageIO *io, fi_handle handle, BYTE *buf, DWORD length, BOOL rle) { |
95 | | // Read either Run-Length Encoded or normal image data |
96 | |
|
97 | 0 | static BYTE repchar, remaining= 0; |
98 | |
|
99 | 0 | if (rle) { |
100 | | // Run-length encoded read |
101 | |
|
102 | 0 | while(length--) { |
103 | 0 | if (remaining) { |
104 | 0 | remaining--; |
105 | 0 | *(buf++)= repchar; |
106 | 0 | } else { |
107 | 0 | io->read_proc(&repchar, 1, 1, handle); |
108 | |
|
109 | 0 | if (repchar == RESC) { |
110 | 0 | io->read_proc(&remaining, 1, 1, handle); |
111 | |
|
112 | 0 | if (remaining == 0) { |
113 | 0 | *(buf++)= RESC; |
114 | 0 | } else { |
115 | 0 | io->read_proc(&repchar, 1, 1, handle); |
116 | |
|
117 | 0 | *(buf++)= repchar; |
118 | 0 | } |
119 | 0 | } else { |
120 | 0 | *(buf++)= repchar; |
121 | 0 | } |
122 | 0 | } |
123 | 0 | } |
124 | 0 | } else { |
125 | | // Normal read |
126 | | |
127 | 0 | io->read_proc(buf, length, 1, handle); |
128 | 0 | } |
129 | 0 | } |
130 | | |
131 | | // ========================================================== |
132 | | // Plugin Interface |
133 | | // ========================================================== |
134 | | |
135 | | static int s_format_id; |
136 | | |
137 | | // ========================================================== |
138 | | // Plugin Implementation |
139 | | // ========================================================== |
140 | | |
141 | | static const char * DLL_CALLCONV |
142 | 2 | Format() { |
143 | 2 | return "RAS"; |
144 | 2 | } |
145 | | |
146 | | static const char * DLL_CALLCONV |
147 | 0 | Description() { |
148 | 0 | return "Sun Raster Image"; |
149 | 0 | } |
150 | | |
151 | | static const char * DLL_CALLCONV |
152 | 0 | Extension() { |
153 | 0 | return "ras"; |
154 | 0 | } |
155 | | |
156 | | static const char * DLL_CALLCONV |
157 | 0 | RegExpr() { |
158 | 0 | return NULL; |
159 | 0 | } |
160 | | |
161 | | static const char * DLL_CALLCONV |
162 | 0 | MimeType() { |
163 | 0 | return "image/x-cmu-raster"; |
164 | 0 | } |
165 | | |
166 | | static BOOL DLL_CALLCONV |
167 | 29.9k | Validate(FreeImageIO *io, fi_handle handle) { |
168 | 29.9k | BYTE ras_signature[] = { 0x59, 0xA6, 0x6A, 0x95 }; |
169 | 29.9k | BYTE signature[4] = { 0, 0, 0, 0 }; |
170 | | |
171 | 29.9k | io->read_proc(signature, 1, sizeof(ras_signature), handle); |
172 | | |
173 | 29.9k | return (memcmp(ras_signature, signature, sizeof(ras_signature)) == 0); |
174 | 29.9k | } |
175 | | |
176 | | static BOOL DLL_CALLCONV |
177 | 0 | SupportsExportDepth(int depth) { |
178 | 0 | return FALSE; |
179 | 0 | } |
180 | | |
181 | | static BOOL DLL_CALLCONV |
182 | 0 | SupportsExportType(FREE_IMAGE_TYPE type) { |
183 | 0 | return FALSE; |
184 | 0 | } |
185 | | |
186 | | static BOOL DLL_CALLCONV |
187 | 0 | SupportsNoPixels() { |
188 | 0 | return TRUE; |
189 | 0 | } |
190 | | |
191 | | // ---------------------------------------------------------- |
192 | | |
193 | | static FIBITMAP * DLL_CALLCONV |
194 | 0 | Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { |
195 | 0 | SUNHEADER header; // Sun file header |
196 | 0 | WORD linelength; // Length of raster line in bytes |
197 | 0 | WORD fill; // Number of fill bytes per raster line |
198 | 0 | BOOL rle; // TRUE if RLE file |
199 | 0 | BOOL isRGB; // TRUE if file type is RT_FORMAT_RGB |
200 | 0 | BYTE fillchar; |
201 | |
|
202 | 0 | FIBITMAP *dib = NULL; |
203 | 0 | BYTE *bits; // Pointer to dib data |
204 | 0 | WORD x, y; |
205 | |
|
206 | 0 | if(!handle) { |
207 | 0 | return NULL; |
208 | 0 | } |
209 | | |
210 | 0 | BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; |
211 | |
|
212 | 0 | try { |
213 | | // Read SUN raster header |
214 | |
|
215 | 0 | io->read_proc(&header, sizeof(SUNHEADER), 1, handle); |
216 | |
|
217 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
218 | | // SUN rasterfiles are big endian only |
219 | |
|
220 | 0 | SwapLong(&header.magic); |
221 | 0 | SwapLong(&header.width); |
222 | 0 | SwapLong(&header.height); |
223 | 0 | SwapLong(&header.depth); |
224 | 0 | SwapLong(&header.length); |
225 | 0 | SwapLong(&header.type); |
226 | 0 | SwapLong(&header.maptype); |
227 | 0 | SwapLong(&header.maplength); |
228 | 0 | #endif |
229 | | |
230 | | // Verify SUN identifier |
231 | |
|
232 | 0 | if (header.magic != RAS_MAGIC) { |
233 | 0 | throw FI_MSG_ERROR_MAGIC_NUMBER; |
234 | 0 | } |
235 | | |
236 | | // Allocate a new DIB |
237 | | |
238 | 0 | switch(header.depth) { |
239 | 0 | case 1: |
240 | 0 | case 8: |
241 | 0 | dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth); |
242 | 0 | break; |
243 | | |
244 | 0 | case 24: |
245 | 0 | dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); |
246 | 0 | break; |
247 | | |
248 | 0 | case 32: |
249 | 0 | dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); |
250 | 0 | break; |
251 | 0 | } |
252 | | |
253 | 0 | if (dib == NULL) { |
254 | 0 | throw FI_MSG_ERROR_DIB_MEMORY; |
255 | 0 | } |
256 | | |
257 | | // Check the file format |
258 | | |
259 | 0 | rle = FALSE; |
260 | 0 | isRGB = FALSE; |
261 | |
|
262 | 0 | switch(header.type) { |
263 | 0 | case RT_OLD: |
264 | 0 | case RT_STANDARD: |
265 | 0 | case RT_FORMAT_TIFF: // I don't even know what these format are... |
266 | 0 | case RT_FORMAT_IFF: //The TIFF and IFF format types indicate that the raster |
267 | | //file was originally converted from either of these file formats. |
268 | | //so lets at least try to process them as RT_STANDARD |
269 | 0 | break; |
270 | | |
271 | 0 | case RT_BYTE_ENCODED: |
272 | 0 | rle = TRUE; |
273 | 0 | break; |
274 | | |
275 | 0 | case RT_FORMAT_RGB: |
276 | 0 | isRGB = TRUE; |
277 | 0 | break; |
278 | | |
279 | 0 | default: |
280 | 0 | throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; |
281 | 0 | } |
282 | | |
283 | | // set up the colormap if needed |
284 | | |
285 | 0 | switch(header.maptype) { |
286 | 0 | case RMT_NONE : |
287 | 0 | { |
288 | 0 | if (header.depth < 24) { |
289 | | // Create linear color ramp |
290 | |
|
291 | 0 | RGBQUAD *pal = FreeImage_GetPalette(dib); |
292 | |
|
293 | 0 | int numcolors = 1 << header.depth; |
294 | |
|
295 | 0 | for (int i = 0; i < numcolors; i++) { |
296 | 0 | pal[i].rgbRed = (BYTE)((255 * i) / (numcolors - 1)); |
297 | 0 | pal[i].rgbGreen = (BYTE)((255 * i) / (numcolors - 1)); |
298 | 0 | pal[i].rgbBlue = (BYTE)((255 * i) / (numcolors - 1)); |
299 | 0 | } |
300 | 0 | } |
301 | |
|
302 | 0 | break; |
303 | 0 | } |
304 | | |
305 | 0 | case RMT_EQUAL_RGB: |
306 | 0 | { |
307 | 0 | BYTE *r, *g, *b; |
308 | | |
309 | | // Read SUN raster colormap |
310 | |
|
311 | 0 | int numcolors = 1 << header.depth; |
312 | 0 | if((DWORD)(3 * numcolors) > header.maplength) { |
313 | | // some RAS may have less colors than the full palette |
314 | 0 | numcolors = header.maplength / 3; |
315 | 0 | } else { |
316 | 0 | throw "Invalid palette"; |
317 | 0 | } |
318 | | |
319 | 0 | r = (BYTE*)malloc(3 * numcolors * sizeof(BYTE)); |
320 | 0 | g = r + numcolors; |
321 | 0 | b = g + numcolors; |
322 | |
|
323 | 0 | RGBQUAD *pal = FreeImage_GetPalette(dib); |
324 | |
|
325 | 0 | io->read_proc(r, 3 * numcolors, 1, handle); |
326 | |
|
327 | 0 | for (int i = 0; i < numcolors; i++) { |
328 | 0 | pal[i].rgbRed = r[i]; |
329 | 0 | pal[i].rgbGreen = g[i]; |
330 | 0 | pal[i].rgbBlue = b[i]; |
331 | 0 | } |
332 | |
|
333 | 0 | free(r); |
334 | 0 | break; |
335 | 0 | } |
336 | | |
337 | 0 | case RMT_RAW: |
338 | 0 | { |
339 | 0 | BYTE *colormap; |
340 | | |
341 | | // Read (skip) SUN raster colormap. |
342 | |
|
343 | 0 | colormap = (BYTE *)malloc(header.maplength * sizeof(BYTE)); |
344 | |
|
345 | 0 | io->read_proc(colormap, header.maplength, 1, handle); |
346 | |
|
347 | 0 | free(colormap); |
348 | 0 | break; |
349 | 0 | } |
350 | 0 | } |
351 | | |
352 | 0 | if(header_only) { |
353 | | // header only mode |
354 | 0 | return dib; |
355 | 0 | } |
356 | | |
357 | | // Calculate the line + pitch |
358 | | // Each row is multiple of 16 bits (2 bytes). |
359 | | |
360 | 0 | if (header.depth == 1) { |
361 | 0 | linelength = (WORD)((header.width / 8) + (header.width % 8 ? 1 : 0)); |
362 | 0 | } else { |
363 | 0 | linelength = (WORD)header.width; |
364 | 0 | } |
365 | |
|
366 | 0 | fill = (linelength % 2) ? 1 : 0; |
367 | |
|
368 | 0 | unsigned pitch = FreeImage_GetPitch(dib); |
369 | | |
370 | | // Read the image data |
371 | | |
372 | 0 | switch(header.depth) { |
373 | 0 | case 1: |
374 | 0 | case 8: |
375 | 0 | { |
376 | 0 | bits = FreeImage_GetBits(dib) + (header.height - 1) * pitch; |
377 | |
|
378 | 0 | for (y = 0; y < header.height; y++) { |
379 | 0 | ReadData(io, handle, bits, linelength, rle); |
380 | |
|
381 | 0 | bits -= pitch; |
382 | |
|
383 | 0 | if (fill) { |
384 | 0 | ReadData(io, handle, &fillchar, fill, rle); |
385 | 0 | } |
386 | 0 | } |
387 | |
|
388 | 0 | break; |
389 | 0 | } |
390 | | |
391 | 0 | case 24: |
392 | 0 | { |
393 | 0 | BYTE *buf, *bp; |
394 | |
|
395 | 0 | buf = (BYTE*)malloc(header.width * 3); |
396 | |
|
397 | 0 | for (y = 0; y < header.height; y++) { |
398 | 0 | bits = FreeImage_GetBits(dib) + (header.height - 1 - y) * pitch; |
399 | |
|
400 | 0 | ReadData(io, handle, buf, header.width * 3, rle); |
401 | |
|
402 | 0 | bp = buf; |
403 | |
|
404 | 0 | if (isRGB) { |
405 | 0 | for (x = 0; x < header.width; x++) { |
406 | 0 | bits[FI_RGBA_RED] = *(bp++); // red |
407 | 0 | bits[FI_RGBA_GREEN] = *(bp++); // green |
408 | 0 | bits[FI_RGBA_BLUE] = *(bp++); // blue |
409 | |
|
410 | 0 | bits += 3; |
411 | 0 | } |
412 | 0 | } else { |
413 | 0 | for (x = 0; x < header.width; x++) { |
414 | 0 | bits[FI_RGBA_RED] = *(bp + 2); // red |
415 | 0 | bits[FI_RGBA_GREEN] = *(bp + 1);// green |
416 | 0 | bits[FI_RGBA_BLUE] = *bp; // blue |
417 | |
|
418 | 0 | bits += 3; bp += 3; |
419 | 0 | } |
420 | 0 | } |
421 | |
|
422 | 0 | if (fill) { |
423 | 0 | ReadData(io, handle, &fillchar, fill, rle); |
424 | 0 | } |
425 | 0 | } |
426 | |
|
427 | 0 | free(buf); |
428 | 0 | break; |
429 | 0 | } |
430 | | |
431 | 0 | case 32: |
432 | 0 | { |
433 | 0 | BYTE *buf, *bp; |
434 | |
|
435 | 0 | buf = (BYTE*)malloc(header.width * 4); |
436 | |
|
437 | 0 | for (y = 0; y < header.height; y++) { |
438 | 0 | bits = FreeImage_GetBits(dib) + (header.height - 1 - y) * pitch; |
439 | |
|
440 | 0 | ReadData(io, handle, buf, header.width * 4, rle); |
441 | |
|
442 | 0 | bp = buf; |
443 | |
|
444 | 0 | if (isRGB) { |
445 | 0 | for (x = 0; x < header.width; x++) { |
446 | 0 | bits[FI_RGBA_ALPHA] = *(bp++); // alpha |
447 | 0 | bits[FI_RGBA_RED] = *(bp++); // red |
448 | 0 | bits[FI_RGBA_GREEN] = *(bp++); // green |
449 | 0 | bits[FI_RGBA_BLUE] = *(bp++); // blue |
450 | |
|
451 | 0 | bits += 4; |
452 | 0 | } |
453 | 0 | } |
454 | 0 | else { |
455 | 0 | for (x = 0; x < header.width; x++) { |
456 | 0 | bits[FI_RGBA_RED] = *(bp + 3); // red |
457 | 0 | bits[FI_RGBA_GREEN] = *(bp + 2); // green |
458 | 0 | bits[FI_RGBA_BLUE] = *(bp + 1); // blue |
459 | 0 | bits[FI_RGBA_ALPHA] = *bp; // alpha |
460 | |
|
461 | 0 | bits += 4; |
462 | 0 | bp += 4; |
463 | 0 | } |
464 | 0 | } |
465 | |
|
466 | 0 | if (fill) { |
467 | 0 | ReadData(io, handle, &fillchar, fill, rle); |
468 | 0 | } |
469 | 0 | } |
470 | |
|
471 | 0 | free(buf); |
472 | 0 | break; |
473 | 0 | } |
474 | 0 | } |
475 | | |
476 | 0 | return dib; |
477 | |
|
478 | 0 | } catch (const char *text) { |
479 | 0 | if(dib) { |
480 | 0 | FreeImage_Unload(dib); |
481 | 0 | } |
482 | 0 | FreeImage_OutputMessageProc(s_format_id, text); |
483 | 0 | } |
484 | | |
485 | 0 | return NULL; |
486 | 0 | } |
487 | | |
488 | | // ========================================================== |
489 | | // Init |
490 | | // ========================================================== |
491 | | |
492 | | void DLL_CALLCONV |
493 | 2 | InitRAS(Plugin *plugin, int format_id) { |
494 | 2 | s_format_id = format_id; |
495 | | |
496 | 2 | plugin->format_proc = Format; |
497 | 2 | plugin->description_proc = Description; |
498 | 2 | plugin->extension_proc = Extension; |
499 | 2 | plugin->regexpr_proc = RegExpr; |
500 | 2 | plugin->open_proc = NULL; |
501 | 2 | plugin->close_proc = NULL; |
502 | 2 | plugin->pagecount_proc = NULL; |
503 | 2 | plugin->pagecapability_proc = NULL; |
504 | 2 | plugin->load_proc = Load; |
505 | 2 | plugin->save_proc = NULL; |
506 | 2 | plugin->validate_proc = Validate; |
507 | 2 | plugin->mime_proc = MimeType; |
508 | 2 | plugin->supports_export_bpp_proc = SupportsExportDepth; |
509 | 2 | plugin->supports_export_type_proc = SupportsExportType; |
510 | 2 | plugin->supports_icc_profiles_proc = NULL; |
511 | 2 | plugin->supports_no_pixels_proc = SupportsNoPixels; |
512 | 2 | } |