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