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