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 #define RAS_MAGIC 0x59A66A95 // Magic number for Sun rasterfiles
58
59 // Sun supported type's
60
61 #define RT_OLD 0 // Old format (raw image in 68000 byte order)
62 #define RT_STANDARD 1 // Raw image in 68000 byte order
63 #define RT_BYTE_ENCODED 2 // Run-length encoding of bytes
64 #define RT_FORMAT_RGB 3 // XRGB or RGB instead of XBGR or BGR
65 #define RT_FORMAT_TIFF 4 // TIFF <-> standard rasterfile
66 #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 #define RMT_NONE 0 // maplength is expected to be 0
75 #define RMT_EQUAL_RGB 1 // red[maplength/3], green[maplength/3], blue[maplength/3]
76 #define RMT_RAW 2 // Raw colormap
77 #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
ReadData(FreeImageIO * io,fi_handle handle,BYTE * buf,DWORD length,BOOL rle)94 ReadData(FreeImageIO *io, fi_handle handle, BYTE *buf, DWORD length, BOOL rle) {
95 // Read either Run-Length Encoded or normal image data
96
97 static BYTE repchar, remaining= 0;
98
99 if (rle) {
100 // Run-length encoded read
101
102 while(length--) {
103 if (remaining) {
104 remaining--;
105 *(buf++)= repchar;
106 } else {
107 io->read_proc(&repchar, 1, 1, handle);
108
109 if (repchar == RESC) {
110 io->read_proc(&remaining, 1, 1, handle);
111
112 if (remaining == 0) {
113 *(buf++)= RESC;
114 } else {
115 io->read_proc(&repchar, 1, 1, handle);
116
117 *(buf++)= repchar;
118 }
119 } else {
120 *(buf++)= repchar;
121 }
122 }
123 }
124 } else {
125 // Normal read
126
127 io->read_proc(buf, length, 1, handle);
128 }
129 }
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
Format()142 Format() {
143 return "RAS";
144 }
145
146 static const char * DLL_CALLCONV
Description()147 Description() {
148 return "Sun Raster Image";
149 }
150
151 static const char * DLL_CALLCONV
Extension()152 Extension() {
153 return "ras";
154 }
155
156 static const char * DLL_CALLCONV
RegExpr()157 RegExpr() {
158 return NULL;
159 }
160
161 static const char * DLL_CALLCONV
MimeType()162 MimeType() {
163 return "image/x-cmu-raster";
164 }
165
166 static BOOL DLL_CALLCONV
Validate(FreeImageIO * io,fi_handle handle)167 Validate(FreeImageIO *io, fi_handle handle) {
168 BYTE ras_signature[] = { 0x59, 0xA6, 0x6A, 0x95 };
169 BYTE signature[4] = { 0, 0, 0, 0 };
170
171 io->read_proc(signature, 1, sizeof(ras_signature), handle);
172
173 return (memcmp(ras_signature, signature, sizeof(ras_signature)) == 0);
174 }
175
176 static BOOL DLL_CALLCONV
SupportsExportDepth(int depth)177 SupportsExportDepth(int depth) {
178 return FALSE;
179 }
180
181 static BOOL DLL_CALLCONV
SupportsExportType(FREE_IMAGE_TYPE type)182 SupportsExportType(FREE_IMAGE_TYPE type) {
183 return FALSE;
184 }
185
186 static BOOL DLL_CALLCONV
SupportsNoPixels()187 SupportsNoPixels() {
188 return TRUE;
189 }
190
191 // ----------------------------------------------------------
192
193 static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO * io,fi_handle handle,int page,int flags,void * data)194 Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
195 SUNHEADER header; // Sun file header
196 WORD linelength; // Length of raster line in bytes
197 WORD fill; // Number of fill bytes per raster line
198 BOOL rle; // TRUE if RLE file
199 BOOL isRGB; // TRUE if file type is RT_FORMAT_RGB
200 BYTE fillchar;
201
202 FIBITMAP *dib = NULL;
203 BYTE *bits; // Pointer to dib data
204 WORD x, y;
205
206 if(!handle) {
207 return NULL;
208 }
209
210 BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
211
212 try {
213 // Read SUN raster header
214
215 io->read_proc(&header, sizeof(SUNHEADER), 1, handle);
216
217 #ifndef FREEIMAGE_BIGENDIAN
218 // SUN rasterfiles are big endian only
219
220 SwapLong(&header.magic);
221 SwapLong(&header.width);
222 SwapLong(&header.height);
223 SwapLong(&header.depth);
224 SwapLong(&header.length);
225 SwapLong(&header.type);
226 SwapLong(&header.maptype);
227 SwapLong(&header.maplength);
228 #endif
229
230 // Verify SUN identifier
231
232 if (header.magic != RAS_MAGIC) {
233 throw FI_MSG_ERROR_MAGIC_NUMBER;
234 }
235
236 // Allocate a new DIB
237
238 switch(header.depth) {
239 case 1:
240 case 8:
241 dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth);
242 break;
243
244 case 24:
245 dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
246 break;
247
248 case 32:
249 dib = FreeImage_AllocateHeader(header_only, header.width, header.height, header.depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
250 break;
251 }
252
253 if (dib == NULL) {
254 throw FI_MSG_ERROR_DIB_MEMORY;
255 }
256
257 // Check the file format
258
259 rle = FALSE;
260 isRGB = FALSE;
261
262 switch(header.type) {
263 case RT_OLD:
264 case RT_STANDARD:
265 case RT_FORMAT_TIFF: // I don't even know what these format are...
266 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 break;
270
271 case RT_BYTE_ENCODED:
272 rle = TRUE;
273 break;
274
275 case RT_FORMAT_RGB:
276 isRGB = TRUE;
277 break;
278
279 default:
280 throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
281 }
282
283 // set up the colormap if needed
284
285 switch(header.maptype) {
286 case RMT_NONE :
287 {
288 if (header.depth < 24) {
289 // Create linear color ramp
290
291 RGBQUAD *pal = FreeImage_GetPalette(dib);
292
293 int numcolors = 1 << header.depth;
294
295 for (int i = 0; i < numcolors; i++) {
296 pal[i].rgbRed = (BYTE)((255 * i) / (numcolors - 1));
297 pal[i].rgbGreen = (BYTE)((255 * i) / (numcolors - 1));
298 pal[i].rgbBlue = (BYTE)((255 * i) / (numcolors - 1));
299 }
300 }
301
302 break;
303 }
304
305 case RMT_EQUAL_RGB:
306 {
307 BYTE *r, *g, *b;
308
309 // Read SUN raster colormap
310
311 int numcolors = 1 << header.depth;
312 if((DWORD)(3 * numcolors) > header.maplength) {
313 // some RAS may have less colors than the full palette
314 numcolors = header.maplength / 3;
315 } else {
316 throw "Invalid palette";
317 }
318
319 r = (BYTE*)malloc(3 * numcolors * sizeof(BYTE));
320 g = r + numcolors;
321 b = g + numcolors;
322
323 RGBQUAD *pal = FreeImage_GetPalette(dib);
324
325 io->read_proc(r, 3 * numcolors, 1, handle);
326
327 for (int i = 0; i < numcolors; i++) {
328 pal[i].rgbRed = r[i];
329 pal[i].rgbGreen = g[i];
330 pal[i].rgbBlue = b[i];
331 }
332
333 free(r);
334 break;
335 }
336
337 case RMT_RAW:
338 {
339 BYTE *colormap;
340
341 // Read (skip) SUN raster colormap.
342
343 colormap = (BYTE *)malloc(header.maplength * sizeof(BYTE));
344
345 io->read_proc(colormap, header.maplength, 1, handle);
346
347 free(colormap);
348 break;
349 }
350 }
351
352 if(header_only) {
353 // header only mode
354 return dib;
355 }
356
357 // Calculate the line + pitch
358 // Each row is multiple of 16 bits (2 bytes).
359
360 if (header.depth == 1) {
361 linelength = (WORD)((header.width / 8) + (header.width % 8 ? 1 : 0));
362 } else {
363 linelength = (WORD)header.width;
364 }
365
366 fill = (linelength % 2) ? 1 : 0;
367
368 unsigned pitch = FreeImage_GetPitch(dib);
369
370 // Read the image data
371
372 switch(header.depth) {
373 case 1:
374 case 8:
375 {
376 bits = FreeImage_GetBits(dib) + (header.height - 1) * pitch;
377
378 for (y = 0; y < header.height; y++) {
379 ReadData(io, handle, bits, linelength, rle);
380
381 bits -= pitch;
382
383 if (fill) {
384 ReadData(io, handle, &fillchar, fill, rle);
385 }
386 }
387
388 break;
389 }
390
391 case 24:
392 {
393 BYTE *buf, *bp;
394
395 buf = (BYTE*)malloc(header.width * 3);
396
397 for (y = 0; y < header.height; y++) {
398 bits = FreeImage_GetBits(dib) + (header.height - 1 - y) * pitch;
399
400 ReadData(io, handle, buf, header.width * 3, rle);
401
402 bp = buf;
403
404 if (isRGB) {
405 for (x = 0; x < header.width; x++) {
406 bits[FI_RGBA_RED] = *(bp++); // red
407 bits[FI_RGBA_GREEN] = *(bp++); // green
408 bits[FI_RGBA_BLUE] = *(bp++); // blue
409
410 bits += 3;
411 }
412 } else {
413 for (x = 0; x < header.width; x++) {
414 bits[FI_RGBA_RED] = *(bp + 2); // red
415 bits[FI_RGBA_GREEN] = *(bp + 1);// green
416 bits[FI_RGBA_BLUE] = *bp; // blue
417
418 bits += 3; bp += 3;
419 }
420 }
421
422 if (fill) {
423 ReadData(io, handle, &fillchar, fill, rle);
424 }
425 }
426
427 free(buf);
428 break;
429 }
430
431 case 32:
432 {
433 BYTE *buf, *bp;
434
435 buf = (BYTE*)malloc(header.width * 4);
436
437 for (y = 0; y < header.height; y++) {
438 bits = FreeImage_GetBits(dib) + (header.height - 1 - y) * pitch;
439
440 ReadData(io, handle, buf, header.width * 4, rle);
441
442 bp = buf;
443
444 if (isRGB) {
445 for (x = 0; x < header.width; x++) {
446 bits[FI_RGBA_ALPHA] = *(bp++); // alpha
447 bits[FI_RGBA_RED] = *(bp++); // red
448 bits[FI_RGBA_GREEN] = *(bp++); // green
449 bits[FI_RGBA_BLUE] = *(bp++); // blue
450
451 bits += 4;
452 }
453 }
454 else {
455 for (x = 0; x < header.width; x++) {
456 bits[FI_RGBA_RED] = *(bp + 3); // red
457 bits[FI_RGBA_GREEN] = *(bp + 2); // green
458 bits[FI_RGBA_BLUE] = *(bp + 1); // blue
459 bits[FI_RGBA_ALPHA] = *bp; // alpha
460
461 bits += 4;
462 bp += 4;
463 }
464 }
465
466 if (fill) {
467 ReadData(io, handle, &fillchar, fill, rle);
468 }
469 }
470
471 free(buf);
472 break;
473 }
474 }
475
476 return dib;
477
478 } catch (const char *text) {
479 if(dib) {
480 FreeImage_Unload(dib);
481 }
482 FreeImage_OutputMessageProc(s_format_id, text);
483 }
484
485 return NULL;
486 }
487
488 // ==========================================================
489 // Init
490 // ==========================================================
491
492 void DLL_CALLCONV
InitRAS(Plugin * plugin,int format_id)493 InitRAS(Plugin *plugin, int format_id) {
494 s_format_id = format_id;
495
496 plugin->format_proc = Format;
497 plugin->description_proc = Description;
498 plugin->extension_proc = Extension;
499 plugin->regexpr_proc = RegExpr;
500 plugin->open_proc = NULL;
501 plugin->close_proc = NULL;
502 plugin->pagecount_proc = NULL;
503 plugin->pagecapability_proc = NULL;
504 plugin->load_proc = Load;
505 plugin->save_proc = NULL;
506 plugin->validate_proc = Validate;
507 plugin->mime_proc = MimeType;
508 plugin->supports_export_bpp_proc = SupportsExportDepth;
509 plugin->supports_export_type_proc = SupportsExportType;
510 plugin->supports_icc_profiles_proc = NULL;
511 plugin->supports_no_pixels_proc = SupportsNoPixels;
512 }
513