1 // ==========================================================
2 // Deluxe Paint Loader
3 //
4 // Design and implementation by
5 // - Floris van den Berg (flvdberg@wxs.nl)
6 // - Mark Sibly (marksibly@blitzbasic.com)
7 // - Aaron Shumate (trek@startreker.com)
8 // - Herv� Drolon (drolon@infonie.fr)
9 //
10 // This file is part of FreeImage 3
11 //
12 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
13 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
14 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
15 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
16 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
17 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
18 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
19 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
20 // THIS DISCLAIMER.
21 //
22 // Use at your own risk!
23 // ==========================================================
24 
25 #include "FreeImage.h"
26 #include "Utilities.h"
27 
28 // ----------------------------------------------------------
29 //  Internal typedefs and structures
30 // ----------------------------------------------------------
31 
32 #ifdef _WIN32
33 #pragma pack(push, 1)
34 #else
35 #pragma pack(1)
36 #endif
37 
38 typedef struct {
39     WORD w, h;                    /* raster width & height in pixels */
40     WORD x, y;                    /* position for this image */
41     BYTE nPlanes;                 /* # source bitplanes */
42     BYTE masking;                 /* masking technique */
43     BYTE compression;             /* compression algorithm */
44     BYTE pad1;                    /* UNUSED.  For consistency, put 0 here.*/
45     WORD transparentColor;        /* transparent "color number" */
46     BYTE xAspect, yAspect;        /* aspect ratio, a rational number x/y */
47     WORD pageWidth, pageHeight;   /* source "page" size in pixels */
48 } BMHD;
49 
50 #ifdef _WIN32
51 #pragma pack(pop)
52 #else
53 #pragma pack()
54 #endif
55 
56 #ifndef FREEIMAGE_BIGENDIAN
57 static void
SwapHeader(BMHD * header)58 SwapHeader(BMHD *header) {
59 	SwapShort(&header->w);
60 	SwapShort(&header->h);
61 	SwapShort(&header->x);
62 	SwapShort(&header->y);
63 	SwapShort(&header->transparentColor);
64 	SwapShort(&header->pageWidth);
65 	SwapShort(&header->pageHeight);
66 }
67 #endif
68 
69 // ----------------------------------------------------------
70 
71 /* IFF chunk IDs */
72 
73 typedef DWORD IFF_ID;
74 
75 #define MAKE_ID(a, b, c, d)         ((IFF_ID)(a)<<24 | (IFF_ID)(b)<<16 | (IFF_ID)(c)<<8 | (IFF_ID)(d))
76 
77 #define ID_FORM     MAKE_ID('F', 'O', 'R', 'M')     /* EA IFF 85 group identifier */
78 #define ID_CAT      MAKE_ID('C', 'A', 'T', ' ')     /* EA IFF 85 group identifier */
79 #define ID_LIST     MAKE_ID('L', 'I', 'S', 'T')     /* EA IFF 85 group identifier */
80 #define ID_PROP     MAKE_ID('P', 'R', 'O', 'P')     /* EA IFF 85 group identifier */
81 #define ID_END      MAKE_ID('E', 'N', 'D', ' ')     /* unofficial END-of-FORM identifier (see Amiga RKM Devices Ed.3 page 376) */
82 
83 #define ID_ILBM     MAKE_ID('I', 'L', 'B', 'M')     /* EA IFF 85 raster bitmap form */
84 #define ID_DEEP     MAKE_ID('D', 'E', 'E', 'P')     /* Chunky pixel image files (Used in TV Paint) */
85 #define ID_RGB8     MAKE_ID('R', 'G', 'B', '8')     /* RGB image forms, Turbo Silver (Impulse) */
86 #define ID_RGBN     MAKE_ID('R', 'G', 'B', 'N')     /* RGB image forms, Turbo Silver (Impulse) */
87 #define ID_PBM      MAKE_ID('P', 'B', 'M', ' ')     /* 256-color chunky format (DPaint 2 ?) */
88 #define ID_ACBM     MAKE_ID('A', 'C', 'B', 'M')     /* Amiga Contiguous Bitmap (AmigaBasic) */
89 /* generic */
90 #define ID_FVER     MAKE_ID('F', 'V', 'E', 'R')     /* AmigaOS version string */
91 #define ID_JUNK     MAKE_ID('J', 'U', 'N', 'K')     /* always ignore this chunk */
92 #define ID_ANNO     MAKE_ID('A', 'N', 'N', 'O')     /* EA IFF 85 Generic Annotation chunk */
93 #define ID_AUTH     MAKE_ID('A', 'U', 'T', 'H')     /* EA IFF 85 Generic Author chunk */
94 #define ID_CHRS     MAKE_ID('C', 'H', 'R', 'S')     /* EA IFF 85 Generic character string chunk */
95 #define ID_NAME     MAKE_ID('N', 'A', 'M', 'E')     /* EA IFF 85 Generic Name of art, music, etc. chunk */
96 #define ID_TEXT     MAKE_ID('T', 'E', 'X', 'T')     /* EA IFF 85 Generic unformatted ASCII text chunk */
97 #define ID_copy     MAKE_ID('(', 'c', ')', ' ')     /* EA IFF 85 Generic Copyright text chunk */
98 /* ILBM chunks */
99 #define ID_BMHD     MAKE_ID('B', 'M', 'H', 'D')     /* ILBM BitmapHeader */
100 #define ID_CMAP     MAKE_ID('C', 'M', 'A', 'P')     /* ILBM 8bit RGB colormap */
101 #define ID_GRAB     MAKE_ID('G', 'R', 'A', 'B')     /* ILBM "hotspot" coordiantes */
102 #define ID_DEST     MAKE_ID('D', 'E', 'S', 'T')     /* ILBM destination image info */
103 #define ID_SPRT     MAKE_ID('S', 'P', 'R', 'T')     /* ILBM sprite identifier */
104 #define ID_CAMG     MAKE_ID('C', 'A', 'M', 'G')     /* Amiga viewportmodes */
105 #define ID_BODY     MAKE_ID('B', 'O', 'D', 'Y')     /* ILBM image data */
106 #define ID_CRNG     MAKE_ID('C', 'R', 'N', 'G')     /* color cycling */
107 #define ID_CCRT     MAKE_ID('C', 'C', 'R', 'T')     /* color cycling */
108 #define ID_CLUT     MAKE_ID('C', 'L', 'U', 'T')     /* Color Lookup Table chunk */
109 #define ID_DPI      MAKE_ID('D', 'P', 'I', ' ')     /* Dots per inch chunk */
110 #define ID_DPPV     MAKE_ID('D', 'P', 'P', 'V')     /* DPaint perspective chunk (EA) */
111 #define ID_DRNG     MAKE_ID('D', 'R', 'N', 'G')     /* DPaint IV enhanced color cycle chunk (EA) */
112 #define ID_EPSF     MAKE_ID('E', 'P', 'S', 'F')     /* Encapsulated Postscript chunk */
113 #define ID_CMYK     MAKE_ID('C', 'M', 'Y', 'K')     /* Cyan, Magenta, Yellow, & Black color map (Soft-Logik) */
114 #define ID_CNAM     MAKE_ID('C', 'N', 'A', 'M')     /* Color naming chunk (Soft-Logik) */
115 #define ID_PCHG     MAKE_ID('P', 'C', 'H', 'G')     /* Line by line palette control information (Sebastiano Vigna) */
116 #define ID_PRVW     MAKE_ID('P', 'R', 'V', 'W')     /* A mini duplicate ILBM used for preview (Gary Bonham) */
117 #define ID_XBMI     MAKE_ID('X', 'B', 'M', 'I')     /* eXtended BitMap Information (Soft-Logik) */
118 #define ID_CTBL     MAKE_ID('C', 'T', 'B', 'L')     /* Newtek Dynamic Ham color chunk */
119 #define ID_DYCP     MAKE_ID('D', 'Y', 'C', 'P')     /* Newtek Dynamic Ham chunk */
120 #define ID_SHAM     MAKE_ID('S', 'H', 'A', 'M')     /* Sliced HAM color chunk */
121 #define ID_ABIT     MAKE_ID('A', 'B', 'I', 'T')     /* ACBM body chunk */
122 #define ID_DCOL     MAKE_ID('D', 'C', 'O', 'L')     /* unofficial direct color */
123 
124 // ==========================================================
125 // Plugin Interface
126 // ==========================================================
127 
128 static int s_format_id;
129 
130 // ==========================================================
131 // Plugin Implementation
132 // ==========================================================
133 
134 static const char * DLL_CALLCONV
Format()135 Format() {
136 	return "IFF";
137 }
138 
139 static const char * DLL_CALLCONV
Description()140 Description() {
141 	return "IFF Interleaved Bitmap";
142 }
143 
144 static const char * DLL_CALLCONV
Extension()145 Extension() {
146 	return "iff,lbm";
147 }
148 
149 static const char * DLL_CALLCONV
RegExpr()150 RegExpr() {
151 	return NULL;
152 }
153 
154 static const char * DLL_CALLCONV
MimeType()155 MimeType() {
156 	return "image/x-iff";
157 }
158 
159 static BOOL DLL_CALLCONV
Validate(FreeImageIO * io,fi_handle handle)160 Validate(FreeImageIO *io, fi_handle handle) {
161 	DWORD type = 0;
162 
163 	// read chunk type
164 	io->read_proc(&type, 4, 1, handle);
165 #ifndef FREEIMAGE_BIGENDIAN
166 	SwapLong(&type);
167 #endif
168 
169 	if(type != ID_FORM)
170 		return FALSE;
171 
172 	// skip 4 bytes
173 	io->read_proc(&type, 4, 1, handle);
174 
175 	// read chunk type
176 	io->read_proc(&type, 4, 1, handle);
177 #ifndef FREEIMAGE_BIGENDIAN
178 	SwapLong(&type);
179 #endif
180 
181 	// File format : ID_PBM = Packed Bitmap, ID_ILBM = Interleaved Bitmap
182 	return (type == ID_ILBM) || (type == ID_PBM);
183 }
184 
185 
186 static BOOL DLL_CALLCONV
SupportsExportDepth(int depth)187 SupportsExportDepth(int depth) {
188 	return FALSE;
189 }
190 
191 static BOOL DLL_CALLCONV
SupportsExportType(FREE_IMAGE_TYPE type)192 SupportsExportType(FREE_IMAGE_TYPE type) {
193 	return FALSE;
194 }
195 
196 // ----------------------------------------------------------
197 
198 static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO * io,fi_handle handle,int page,int flags,void * data)199 Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
200 	if (handle != NULL) {
201 		FIBITMAP *dib = NULL;
202 
203 		DWORD type, size;
204 
205 		io->read_proc(&type, 4, 1, handle);
206 #ifndef FREEIMAGE_BIGENDIAN
207 		SwapLong(&type);
208 #endif
209 
210 		if(type != ID_FORM)
211 			return NULL;
212 
213 		io->read_proc(&size, 4, 1, handle);
214 #ifndef FREEIMAGE_BIGENDIAN
215 		SwapLong(&size);
216 #endif
217 
218 		io->read_proc(&type, 4, 1, handle);
219 #ifndef FREEIMAGE_BIGENDIAN
220 		SwapLong(&type);
221 #endif
222 
223 		if((type != ID_ILBM) && (type != ID_PBM))
224 			return NULL;
225 
226 		size -= 4;
227 
228 		unsigned width = 0, height = 0, planes = 0, depth = 0, comp = 0;
229 
230 		while (size) {
231 			DWORD ch_type,ch_size;
232 
233 			io->read_proc(&ch_type, 4, 1, handle);
234 #ifndef FREEIMAGE_BIGENDIAN
235 			SwapLong(&ch_type);
236 #endif
237 
238 			io->read_proc(&ch_size,4,1,handle );
239 #ifndef FREEIMAGE_BIGENDIAN
240 			SwapLong(&ch_size);
241 #endif
242 
243 			unsigned ch_end = io->tell_proc(handle) + ch_size;
244 
245 			if (ch_type == ID_BMHD) {			// Bitmap Header
246 				if (dib)
247 					FreeImage_Unload(dib);
248 
249 				BMHD bmhd;
250 
251 				io->read_proc(&bmhd, sizeof(bmhd), 1, handle);
252 #ifndef FREEIMAGE_BIGENDIAN
253 				SwapHeader(&bmhd);
254 #endif
255 
256 				width = bmhd.w;
257 				height = bmhd.h;
258 				planes = bmhd.nPlanes;
259 				comp = bmhd.compression;
260 
261 				if(bmhd.masking & 1)
262 					planes++;	// there is a mask ( 'stencil' )
263 
264 				if (planes > 8 && planes != 24)
265 					return NULL;
266 
267 				depth = planes > 8 ? 24 : 8;
268 
269 				if( depth == 24 ) {
270 					dib = FreeImage_Allocate(width, height, depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
271 				} else {
272 					dib = FreeImage_Allocate(width, height, depth);
273 				}
274 			} else if (ch_type == ID_CMAP) {	// Palette (Color Map)
275 				if (!dib)
276 					return NULL;
277 
278 				RGBQUAD *pal = FreeImage_GetPalette(dib);
279 				if(pal != NULL) {
280 					unsigned palette_entries = MIN((unsigned)ch_size / 3, FreeImage_GetColorsUsed(dib));
281 					for (unsigned k = 0; k < palette_entries; k++) {
282 						io->read_proc(&pal[k].rgbRed, 1, 1, handle );
283 						io->read_proc(&pal[k].rgbGreen, 1, 1, handle );
284 						io->read_proc(&pal[k].rgbBlue, 1, 1, handle );
285 					}
286 				}
287 			} else if (ch_type == ID_BODY) {
288 				if (!dib)
289 					return NULL;
290 
291 				if (type == ID_PBM) {
292 					// NON INTERLACED (LBM)
293 
294 					unsigned line = FreeImage_GetLine(dib) + 1 & ~1;
295 
296 					for (unsigned i = 0; i < FreeImage_GetHeight(dib); i++) {
297 						BYTE *bits = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - i - 1);
298 
299 						if (comp == 1) {
300 							// use RLE compression
301 
302 							DWORD number_of_bytes_written = 0;
303 							BYTE rle_count;
304 							BYTE byte;
305 
306 							while (number_of_bytes_written < line) {
307 								io->read_proc(&rle_count, 1, 1, handle);
308 
309 								if (rle_count < 128) {
310 									for (int k = 0; k < rle_count + 1; k++) {
311 										io->read_proc(&byte, 1, 1, handle);
312 
313 										bits[number_of_bytes_written++] += byte;
314 									}
315 								} else if (rle_count > 128) {
316 									io->read_proc(&byte, 1, 1, handle);
317 
318 									for (int k = 0; k < 257 - rle_count; k++) {
319 										bits[number_of_bytes_written++] += byte;
320 									}
321 								}
322 							}
323 						} else {
324 							// don't use compression
325 
326 							io->read_proc(bits, line, 1, handle);
327 						}
328 					}
329 
330 					return dib;
331 				} else {
332 					// INTERLACED (ILBM)
333 
334 					unsigned pixel_size = depth/8;
335 					unsigned n_width=(width+15)&~15;
336 					unsigned plane_size = n_width/8;
337 					unsigned src_size = plane_size * planes;
338 					BYTE *src = (BYTE*)malloc(src_size);
339 					BYTE *dest = FreeImage_GetBits(dib);
340 
341 					dest += FreeImage_GetPitch(dib) * height;
342 
343 					for (unsigned y = 0; y < height; y++) {
344 						dest -= FreeImage_GetPitch(dib);
345 
346 						// read all planes in one hit,
347 						// 'coz PSP compresses across planes...
348 
349 						if (comp) {
350 							// unpacker algorithm
351 
352 							for(unsigned x = 0; x < src_size;) {
353 								// read the next source byte into t
354 								signed char t = 0;
355 								io->read_proc(&t, 1, 1, handle);
356 
357 								if (t >= 0) {
358 									// t = [0..127] => copy the next t+1 bytes literally
359 									unsigned size_to_read = t + 1;
360 
361 									if((size_to_read + x) > src_size) {
362 										// sanity check for buffer overruns
363 										size_to_read = src_size - x;
364 										io->read_proc(src + x, size_to_read, 1, handle);
365 										x += (t + 1);
366 									} else {
367 										io->read_proc(src + x, size_to_read, 1, handle);
368 										x += size_to_read;
369 									}
370 								} else if (t != -128) {
371 									// t = [-1..-127]  => replicate the next byte -t+1 times
372 									BYTE b = 0;
373 									io->read_proc(&b, 1, 1, handle);
374 									unsigned size_to_copy = (unsigned)(-(int)t + 1);
375 
376 									if((size_to_copy + x) > src_size) {
377 										// sanity check for buffer overruns
378 										size_to_copy = src_size - x;
379 										memset(src + x, b, size_to_copy);
380 										x += (unsigned)(-(int)t + 1);
381 									} else {
382 										memset(src + x, b, size_to_copy);
383 										x += size_to_copy;
384 									}
385 								}
386 								// t = -128 => noop
387 							}
388 						} else {
389 							io->read_proc(src, src_size, 1, handle);
390 						}
391 
392 						// lazy planar->chunky...
393 
394 						for (unsigned x = 0; x < width; x++) {
395 							for (unsigned n = 0; n < planes; n++) {
396 								BYTE bit = (BYTE)( src[n * plane_size + (x / 8)] >> ((x^7) & 7) );
397 
398 								dest[x * pixel_size + (n / 8)] |= (bit & 1) << (n & 7);
399 							}
400 						}
401 
402 #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
403 						if (depth == 24) {
404 							for (unsigned x = 0; x < width; ++x){
405 								INPLACESWAP(dest[x * 3], dest[x * 3 + 2]);
406 							}
407 						}
408 #endif
409 					}
410 
411 					free(src);
412 
413 					return dib;
414 				}
415 			}
416 
417 			// Every odd-length chunk is followed by a 0 pad byte.  This pad
418 			//  byte is not counted in ch_size.
419 			if (ch_size & 1) {
420 				ch_size++;
421 				ch_end++;
422 			}
423 
424 			io->seek_proc(handle, ch_end - io->tell_proc(handle), SEEK_CUR);
425 
426 			size -= ch_size + 8;
427 		}
428 
429 		if (dib)
430 			FreeImage_Unload(dib);
431 	}
432 
433 	return 0;
434 }
435 
436 // ==========================================================
437 //   Init
438 // ==========================================================
439 
440 void DLL_CALLCONV
InitIFF(Plugin * plugin,int format_id)441 InitIFF(Plugin *plugin, int format_id) {
442 	s_format_id = format_id;
443 
444 	plugin->format_proc = Format;
445 	plugin->description_proc = Description;
446 	plugin->extension_proc = Extension;
447 	plugin->regexpr_proc = RegExpr;
448 	plugin->open_proc = NULL;
449 	plugin->close_proc = NULL;
450 	plugin->pagecount_proc = NULL;
451 	plugin->pagecapability_proc = NULL;
452 	plugin->load_proc = Load;
453 	plugin->save_proc = NULL;
454 	plugin->validate_proc = Validate;
455 	plugin->mime_proc = MimeType;
456 	plugin->supports_export_bpp_proc = SupportsExportDepth;
457 	plugin->supports_export_type_proc = SupportsExportType;
458 	plugin->supports_icc_profiles_proc = NULL;
459 }
460