1 // ==========================================================
2 // Apple Macintosh QuickDraw/PICT Loader
3 //
4 // Design and implementation by
5 // - Amir Ebrahimi (amir@unity3d.com)
6 //
7 // Based on PICT loading code from paintlib (http://www.paintlib.de/paintlib/).
8 //
9 // Paintlib License:
10 // The paintlib source code and all documentation are copyright (c) 1996-2002
11 // Ulrich von Zadow and other contributors.
12 //
13 // The paintlib source code is supplied "AS IS". Ulrich von Zadow and other
14 // contributors disclaim all warranties, expressed or implied, including, without
15 // limitation, the warranties of merchantability and of fitness for any purpose.
16 // The authors assume no liability for direct, indirect, incidental, special,
17 // exemplary, or consequential damages, which may result from the use of paintlib,
18 // even if advised of the possibility of such damage.
19 //
20 // Permission is hereby granted to use, copy, modify, and distribute this source
21 // code, or portions hereof, for any purpose, without fee, subject to the following
22 // restrictions:
23 //
24 // 1. The origin of this source code must not be misrepresented.
25 // 2. Altered versions must be plainly marked as such and must not be misrepresented
26 //    as being the original source.
27 // 3. This Copyright notice may not be removed or altered from any source or altered
28 //    source distribution.
29 // 4. Executables containing paintlib or parts of it must state that the software
30 //    "contains paintlib code. paintlib is copyright (c) 1996-2002 Ulrich von Zadow
31 //    and other contributors.". This notice must be displayed in at least one place
32 //    where the copyright for the software itself is displayed. The documentation must
33 //    also contain this notice.
34 //
35 // Bug fixes were made to the original code to support version 2 PICT files
36 // properly.
37 //
38 // Additional resources:
39 // http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-458.html
40 // http://www.fileformat.info/format/macpict/egff.htm
41 //
42 // Notes (http://lists.apple.com/archives/java-dev/2006/Apr/msg00588.html):
43 // There are three main types of PICT files:
44 //  - Version 1
45 //  - Version 2
46 //  - Extended Version 2
47 //
48 // Some things to look out for:
49 //  - The bounds and target DPI are stored in a different place in all three.
50 //  - Some of the values are fixed-point shorts ( short / 65536f )
51 //  - Values are big endian
52 //  - All of this may be *preceded* by a 512 byte header--sometimes it is
53 //    there, and sometimes it isn't. You just have to check for the magic
54 //	  values in both places.
55 //
56 // This file is part of FreeImage 3
57 //
58 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
59 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
60 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
61 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
62 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
63 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
64 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
65 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
66 // THIS DISCLAIMER.
67 //
68 // Use at your own risk!
69 // ==========================================================
70 
71 #include "FreeImage.h"
72 #include "Utilities.h"
73 
74 // ==========================================================
75 // Plugin Interface
76 // ==========================================================
77 static int s_format_id;
78 
79 static const int outputMessageSize = 256;
80 
81 // ==========================================================
82 // Internal functions
83 // ==========================================================
84 
85 static BYTE
Read8(FreeImageIO * io,fi_handle handle)86 Read8(FreeImageIO *io, fi_handle handle) {
87 	BYTE i = 0;
88 	io->read_proc(&i, 1, 1, handle);
89 	return i;
90 }
91 
92 static WORD
Read16(FreeImageIO * io,fi_handle handle)93 Read16(FreeImageIO *io, fi_handle handle) {
94 	// reads a two-byte big-endian integer from the given file and returns its value.
95 	// assumes unsigned.
96 
97 	unsigned hi = Read8(io, handle);
98 	unsigned lo = Read8(io, handle);
99 	return (WORD)(lo + (hi << 8));
100 }
101 
102 static unsigned
Read32(FreeImageIO * io,fi_handle handle)103 Read32(FreeImageIO *io, fi_handle handle) {
104 	// reads a four-byte big-endian integer from the given file and returns its value.
105 	// assumes unsigned.
106 
107 	unsigned b3 = Read8(io, handle);
108 	unsigned b2 = Read8(io, handle);
109 	unsigned b1 = Read8(io, handle);
110 	unsigned b0 = Read8(io, handle);
111 	return (b3 << 24) + (b2 << 16) + (b1 << 8) + b0;
112 }
113 
114 // ----------------------------------------------------------
115 
116 struct OpDef
117 {
118 	const char * name;
119 	int    len;
120 	const char * description;
121 };
122 
123 // for reserved opcodes
124 #define res(length) { "reserved", (length), "reserved for Apple use" }
125 #define RGB_LEN 6
126 #define WORD_LEN -1
127 #define NA 0
128 
129 static OpDef optable[] =
130 {
131 /* 0x00 */  { "NOP",               0, "nop" },
132 /* 0x01 */  { "Clip",             NA, "clip" },
133 /* 0x02 */  { "BkPat",             8, "background pattern" },
134 /* 0x03 */  { "TxFont",            2, "text font (word)" },
135 /* 0x04 */  { "TxFace",            1, "text face (byte)" },
136 /* 0x05 */  { "TxMode",            2, "text mode (word)" },
137 /* 0x06 */  { "SpExtra",           4, "space extra (fixed point)" },
138 /* 0x07 */  { "PnSize",            4, "pen size (point)" },
139 /* 0x08 */  { "PnMode",            2, "pen mode (word)" },
140 /* 0x09 */  { "PnPat",             8, "pen pattern" },
141 /* 0x0a */  { "FillPat",           8, "fill pattern" },
142 /* 0x0b */  { "OvSize",            4, "oval size (point)" },
143 /* 0x0c */  { "Origin",            4, "dh, dv (word)" },
144 /* 0x0d */  { "TxSize",            2, "text size (word)" },
145 /* 0x0e */  { "FgColor",           4, "foreground color (longword)" },
146 /* 0x0f */  { "BkColor",           4, "background color (longword)" },
147 /* 0x10 */  { "TxRatio",           8, "numerator (point), denominator (point)" },
148 /* 0x11 */  { "Version",           1, "version (byte)" },
149 /* 0x12 */  { "BkPixPat",         NA, "color background pattern" },
150 /* 0x13 */  { "PnPixPat",         NA, "color pen pattern" },
151 /* 0x14 */  { "FillPixPat",       NA, "color fill pattern" },
152 /* 0x15 */  { "PnLocHFrac",        2, "fractional pen position" },
153 /* 0x16 */  { "ChExtra",           2, "extra for each character" },
154 /* 0x17 */  res(0),
155 /* 0x18 */  res(0),
156 /* 0x19 */  res(0),
157 /* 0x1a */  { "RGBFgCol",    RGB_LEN, "RGB foreColor" },
158 /* 0x1b */  { "RGBBkCol",    RGB_LEN, "RGB backColor" },
159 /* 0x1c */  { "HiliteMode",        0, "hilite mode flag" },
160 /* 0x1d */  { "HiliteColor", RGB_LEN, "RGB hilite color" },
161 /* 0x1e */  { "DefHilite",         0, "Use default hilite color" },
162 /* 0x1f */  { "OpColor",           6, "RGB OpColor for arithmetic modes" },
163 /* 0x20 */  { "Line",              8, "pnLoc (point), newPt (point)" },
164 /* 0x21 */  { "LineFrom",          4, "newPt (point)" },
165 /* 0x22 */  { "ShortLine",         6, "pnLoc (point, dh, dv (-128 .. 127))" },
166 /* 0x23 */  { "ShortLineFrom",     2, "dh, dv (-128 .. 127)" },
167 /* 0x24 */  res(WORD_LEN),
168 /* 0x25 */  res(WORD_LEN),
169 /* 0x26 */  res(WORD_LEN),
170 /* 0x27 */  res(WORD_LEN),
171 /* 0x28 */  { "LongText",         NA, "txLoc (point), count (0..255), text" },
172 /* 0x29 */  { "DHText",           NA, "dh (0..255), count (0..255), text" },
173 /* 0x2a */  { "DVText",           NA, "dv (0..255), count (0..255), text" },
174 /* 0x2b */  { "DHDVText",         NA, "dh, dv (0..255), count (0..255), text" },
175 /* 0x2c */  res(WORD_LEN),
176 /* 0x2d */  res(WORD_LEN),
177 /* 0x2e */  res(WORD_LEN),
178 /* 0x2f */  res(WORD_LEN),
179 /* 0x30 */  { "frameRect",         8, "rect" },
180 /* 0x31 */  { "paintRect",         8, "rect" },
181 /* 0x32 */  { "eraseRect",         8, "rect" },
182 /* 0x33 */  { "invertRect",        8, "rect" },
183 /* 0x34 */  { "fillRect",          8, "rect" },
184 /* 0x35 */  res(8),
185 /* 0x36 */  res(8),
186 /* 0x37 */  res(8),
187 /* 0x38 */  { "frameSameRect",     0, "rect" },
188 /* 0x39 */  { "paintSameRect",     0, "rect" },
189 /* 0x3a */  { "eraseSameRect",     0, "rect" },
190 /* 0x3b */  { "invertSameRect",    0, "rect" },
191 /* 0x3c */  { "fillSameRect",      0, "rect" },
192 /* 0x3d */  res(0),
193 /* 0x3e */  res(0),
194 /* 0x3f */  res(0),
195 /* 0x40 */  { "frameRRect",        8, "rect" },
196 /* 0x41 */  { "paintRRect",        8, "rect" },
197 /* 0x42 */  { "eraseRRect",        8, "rect" },
198 /* 0x43 */  { "invertRRect",       8, "rect" },
199 /* 0x44 */  { "fillRRrect",        8, "rect" },
200 /* 0x45 */  res(8),
201 /* 0x46 */  res(8),
202 /* 0x47 */  res(8),
203 /* 0x48 */  { "frameSameRRect",    0, "rect" },
204 /* 0x49 */  { "paintSameRRect",    0, "rect" },
205 /* 0x4a */  { "eraseSameRRect",    0, "rect" },
206 /* 0x4b */  { "invertSameRRect",   0, "rect" },
207 /* 0x4c */  { "fillSameRRect",     0, "rect" },
208 /* 0x4d */  res(0),
209 /* 0x4e */  res(0),
210 /* 0x4f */  res(0),
211 /* 0x50 */  { "frameOval",         8, "rect" },
212 /* 0x51 */  { "paintOval",         8, "rect" },
213 /* 0x52 */  { "eraseOval",         8, "rect" },
214 /* 0x53 */  { "invertOval",        8, "rect" },
215 /* 0x54 */  { "fillOval",          8, "rect" },
216 /* 0x55 */  res(8),
217 /* 0x56 */  res(8),
218 /* 0x57 */  res(8),
219 /* 0x58 */  { "frameSameOval",     0, "rect" },
220 /* 0x59 */  { "paintSameOval",     0, "rect" },
221 /* 0x5a */  { "eraseSameOval",     0, "rect" },
222 /* 0x5b */  { "invertSameOval",    0, "rect" },
223 /* 0x5c */  { "fillSameOval",      0, "rect" },
224 /* 0x5d */  res(0),
225 /* 0x5e */  res(0),
226 /* 0x5f */  res(0),
227 /* 0x60 */  { "frameArc",         12, "rect, startAngle, arcAngle" },
228 /* 0x61 */  { "paintArc",         12, "rect, startAngle, arcAngle" },
229 /* 0x62 */  { "eraseArc",         12, "rect, startAngle, arcAngle" },
230 /* 0x63 */  { "invertArc",        12, "rect, startAngle, arcAngle" },
231 /* 0x64 */  { "fillArc",          12, "rect, startAngle, arcAngle" },
232 /* 0x65 */  res(12),
233 /* 0x66 */  res(12),
234 /* 0x67 */  res(12),
235 /* 0x68 */  { "frameSameArc",      4, "rect, startAngle, arcAngle" },
236 /* 0x69 */  { "paintSameArc",      4, "rect, startAngle, arcAngle" },
237 /* 0x6a */  { "eraseSameArc",      4, "rect, startAngle, arcAngle" },
238 /* 0x6b */  { "invertSameArc",     4, "rect, startAngle, arcAngle" },
239 /* 0x6c */  { "fillSameArc",       4, "rect, startAngle, arcAngle" },
240 /* 0x6d */  res(4),
241 /* 0x6e */  res(4),
242 /* 0x6f */  res(4),
243 /* 0x70 */  { "framePoly",        NA, "poly" },
244 /* 0x71 */  { "paintPoly",        NA, "poly" },
245 /* 0x72 */  { "erasePoly",        NA, "poly" },
246 /* 0x73 */  { "invertPoly",       NA, "poly" },
247 /* 0x74 */  { "fillPoly",         NA, "poly" },
248 /* 0x75 */  res(NA),
249 /* 0x76 */  res(NA),
250 /* 0x77 */  res(NA),
251 /* 0x78 */  { "frameSamePoly",     0, "poly (NYI)" },
252 /* 0x79 */  { "paintSamePoly",     0, "poly (NYI)" },
253 /* 0x7a */  { "eraseSamePoly",     0, "poly (NYI)" },
254 /* 0x7b */  { "invertSamePoly",    0, "poly (NYI)" },
255 /* 0x7c */  { "fillSamePoly",      0, "poly (NYI)" },
256 /* 0x7d */  res(0),
257 /* 0x7e */  res(0),
258 /* 0x7f */  res(0),
259 /* 0x80 */  { "frameRgn",         NA, "region" },
260 /* 0x81 */  { "paintRgn",         NA, "region" },
261 /* 0x82 */  { "eraseRgn",         NA, "region" },
262 /* 0x83 */  { "invertRgn",        NA, "region" },
263 /* 0x84 */  { "fillRgn",          NA, "region" },
264 /* 0x85 */  res(NA),
265 /* 0x86 */  res(NA),
266 /* 0x87 */  res(NA),
267 /* 0x88 */  { "frameSameRgn",      0, "region (NYI)" },
268 /* 0x89 */  { "paintSameRgn",      0, "region (NYI)" },
269 /* 0x8a */  { "eraseSameRgn",      0, "region (NYI)" },
270 /* 0x8b */  { "invertSameRgn",     0, "region (NYI)" },
271 /* 0x8c */  { "fillSameRgn",       0, "region (NYI)" },
272 /* 0x8d */  res(0),
273 /* 0x8e */  res(0),
274 /* 0x8f */  res(0),
275 /* 0x90 */  { "BitsRect",         NA, "copybits, rect clipped" },
276 /* 0x91 */  { "BitsRgn",          NA, "copybits, rgn clipped" },
277 /* 0x92 */  res(WORD_LEN),
278 /* 0x93 */  res(WORD_LEN),
279 /* 0x94 */  res(WORD_LEN),
280 /* 0x95 */  res(WORD_LEN),
281 /* 0x96 */  res(WORD_LEN),
282 /* 0x97 */  res(WORD_LEN),
283 /* 0x98 */  { "PackBitsRect",     NA, "packed copybits, rect clipped" },
284 /* 0x99 */  { "PackBitsRgn",      NA, "packed copybits, rgn clipped" },
285 /* 0x9a */  { "Opcode_9A",        NA, "the mysterious opcode 9A" },
286 /* 0x9b */  res(WORD_LEN),
287 /* 0x9c */  res(WORD_LEN),
288 /* 0x9d */  res(WORD_LEN),
289 /* 0x9e */  res(WORD_LEN),
290 /* 0x9f */  res(WORD_LEN),
291 /* 0xa0 */  { "ShortComment",      2, "kind (word)" },
292 /* 0xa1 */  { "LongComment",      NA, "kind (word), size (word), data" }
293 };
294 
295 // ----------------------------------------------------------
296 
297 struct MacRect
298 {
299 	WORD top;
300 	WORD left;
301 	WORD bottom;
302 	WORD right;
303 };
304 
305 struct MacpixMap
306 {
307 	// Ptr baseAddr              // Not used in file.
308 	// short rowBytes            // read in seperatly.
309 	struct MacRect Bounds;
310 	WORD version;
311 	WORD packType;
312 	LONG packSize;
313 	LONG hRes;
314 	LONG vRes;
315 	WORD pixelType;
316 	WORD pixelSize;
317 	WORD cmpCount;
318 	WORD cmpSize;
319 	LONG planeBytes;
320 	LONG pmTable;
321 	LONG pmReserved;
322 };
323 
324 struct MacRGBColour
325 {
326 	WORD red;
327 	WORD green;
328 	WORD blue;
329 };
330 
331 struct MacPoint
332 {
333 	WORD x;
334 	WORD y;
335 };
336 
337 struct MacPattern // Klaube
338 {
339 	BYTE pix[64];
340 };
341 
342 // ----------------------------------------------------------
343 
344 static void
ReadRect(FreeImageIO * io,fi_handle handle,MacRect * rect)345 ReadRect( FreeImageIO *io, fi_handle handle, MacRect* rect ) {
346 	rect->top = Read16( io, handle );
347 	rect->left = Read16( io, handle );
348 	rect->bottom = Read16( io, handle );
349 	rect->right = Read16( io, handle );
350 }
351 
352 static void
ReadPixmap(FreeImageIO * io,fi_handle handle,MacpixMap * pPixMap)353 ReadPixmap( FreeImageIO *io, fi_handle handle, MacpixMap* pPixMap ) {
354 	pPixMap->version = Read16( io, handle );
355 	pPixMap->packType = Read16( io, handle );
356 	pPixMap->packSize = Read32( io, handle );
357 	pPixMap->hRes = Read16( io, handle );
358 	Read16( io, handle );
359 	pPixMap->vRes = Read16( io, handle );
360 	Read16( io, handle );
361 	pPixMap->pixelType = Read16( io, handle );
362 	pPixMap->pixelSize = Read16( io, handle );
363 	pPixMap->cmpCount = Read16( io, handle );
364 	pPixMap->cmpSize = Read16( io, handle );
365 	pPixMap->planeBytes = Read32( io, handle );
366 	pPixMap->pmTable = Read32( io, handle );
367 	pPixMap->pmReserved = Read32( io, handle );
368 }
369 
370 /**
371 Reads a mac color table into a bitmap palette.
372 */
373 static void
ReadColorTable(FreeImageIO * io,fi_handle handle,WORD * pNumColors,RGBQUAD * pPal)374 ReadColorTable( FreeImageIO *io, fi_handle handle, WORD* pNumColors, RGBQUAD* pPal ) {
375 	LONG        ctSeed;
376 	WORD        ctFlags;
377 	WORD        val;
378 	int         i;
379 
380 	ctSeed = Read32( io, handle );
381 	ctFlags = Read16( io, handle );
382 	WORD numColors = Read16( io, handle )+1;
383 	*pNumColors = numColors;
384 
385 	for (i = 0; i < numColors; i++) {
386 		val = Read16( io, handle );
387 		if (ctFlags & 0x8000) {
388 			// The indicies in a device colour table are bogus and
389 			// usually == 0, so I assume we allocate up the list of
390 			// colours in order.
391 			val = (WORD)i;
392 		}
393 		if (val >= numColors) {
394 			throw "pixel value greater than color table size.";
395 		}
396 		// Mac colour tables contain 16-bit values for R, G, and B...
397 		pPal[val].rgbRed = ((BYTE) (((WORD) (Read16( io, handle )) >> 8) & 0xFF));
398 		pPal[val].rgbGreen = ((BYTE) (((WORD) (Read16( io, handle )) >> 8) & 0xFF));
399 		pPal[val].rgbBlue = ((BYTE) (((WORD) (Read16( io, handle )) >> 8) & 0xFF));
400 	}
401 }
402 
403 /**
404 skips unneeded packbits.
405 pixelSize == Source bits per pixel.
406 */
407 static void
SkipBits(FreeImageIO * io,fi_handle handle,MacRect * bounds,WORD rowBytes,int pixelSize)408 SkipBits( FreeImageIO *io, fi_handle handle, MacRect* bounds, WORD rowBytes, int pixelSize ) {
409 	int    i;
410 	WORD   pixwidth;           // bytes per row when uncompressed.
411 
412 	int height = bounds->bottom - bounds->top;
413 	int width = bounds->right - bounds->left;
414 
415 	// High bit of rowBytes is flag.
416 	if (pixelSize <= 8) {
417 		rowBytes &= 0x7fff;
418 	}
419 	pixwidth = (WORD)width;
420 
421 	if (pixelSize == 16) {
422 		pixwidth *= 2;
423 	}
424 	if (rowBytes == 0) {
425 		rowBytes = pixwidth;
426 	}
427 	if (rowBytes < 8) {
428 		io->seek_proc( handle, rowBytes*height, SEEK_CUR );
429 	}
430 	else {
431 		for (i = 0; i < height; i++) {
432 			int lineLen;            // length of source line in bytes.
433 			if (rowBytes > 250) {
434 				lineLen = Read16( io, handle );
435 			} else {
436 				lineLen = Read8( io, handle );
437 			}
438 			io->seek_proc( handle, lineLen, SEEK_CUR );
439 		}
440 	}
441 }
442 
443 /**
444 Skip polygon or region
445 */
446 static void
SkipPolyOrRegion(FreeImageIO * io,fi_handle handle)447 SkipPolyOrRegion( FreeImageIO *io, fi_handle handle ) {
448 	WORD len = Read16( io, handle ) - 2;
449 	io->seek_proc(handle, len, SEEK_CUR);
450 }
451 
452 /**
453 Width in bytes for 8 bpp or less.
454 Width in pixels for 16 bpp.
455 Expands Width units to 32-bit pixel data.
456 */
457 static void
expandBuf(FreeImageIO * io,fi_handle handle,int width,int bpp,BYTE * dst)458 expandBuf( FreeImageIO *io, fi_handle handle, int width, int bpp, BYTE* dst ) {
459 	switch (bpp) {
460 		case 16:
461 			for ( int i=0; i<width; i++) {
462 				WORD src = Read16( io, handle );
463 				dst[ FI_RGBA_BLUE ] = (src & 31)*8;				// Blue
464 				dst[ FI_RGBA_GREEN ] = ((src >> 5) & 31)*8;		// Green
465 				dst[ FI_RGBA_RED ] = ((src >> 10) & 31)*8;		// Red
466 				dst[ FI_RGBA_ALPHA ] = 0xFF;					// Alpha
467 				dst += 4;
468 			}
469 			break;
470 		default:
471 			throw "Bad bits per pixel in expandBuf.";
472 	}
473 }
474 
475 /**
476 Expands Width units to 8-bit pixel data.
477 Max. 8 bpp source format.
478 */
479 static void
expandBuf8(FreeImageIO * io,fi_handle handle,int width,int bpp,BYTE * dst)480 expandBuf8( FreeImageIO *io, fi_handle handle, int width, int bpp, BYTE* dst )
481 {
482 	switch (bpp) {
483 		case 8:
484 			io->read_proc( dst, width, 1, handle );
485 			break;
486 		case 4:
487 			for (int i = 0; i < width; i++) {
488 				WORD src = Read8( io, handle );
489 				*dst = (src >> 4) & 15;
490 				*(dst+1) = (src & 15);
491 				dst += 2;
492 			}
493 			if (width & 1) { // Odd Width?
494 				WORD src = Read8( io, handle );
495 				*dst = (src >> 4) & 15;
496 				dst++;
497 			}
498 			break;
499 		case 2:
500 			for (int i = 0; i < width; i++) {
501 				WORD src = Read8( io, handle );
502 				*dst = (src >> 6) & 3;
503 				*(dst+1) = (src >> 4) & 3;
504 				*(dst+2) = (src >> 2) & 3;
505 				*(dst+3) = (src & 3);
506 				dst += 4;
507 			}
508 			if (width & 3)  { // Check for leftover pixels
509 				for (int i = 6; i > 8 - (width & 3) * 2; i -= 2) {
510 					WORD src = Read8( io, handle );
511 					*dst = (src >> i) & 3;
512 					dst++;
513 				}
514 			}
515 			break;
516 		case 1:
517 			for (int i = 0; i < width; i++) {
518 				WORD src = Read8( io, handle );
519 				*dst = (src >> 7) & 1;
520 				*(dst+1) = (src >> 6) & 1;
521 				*(dst+2) = (src >> 5) & 1;
522 				*(dst+3) = (src >> 4) & 1;
523 				*(dst+4) = (src >> 3) & 1;
524 				*(dst+5) = (src >> 2) & 1;
525 				*(dst+6) = (src >> 1) & 1;
526 				*(dst+7) = (src  & 1);
527 				dst += 8;
528 			}
529 			if (width & 7) {  // Check for leftover pixels
530 				for (int i = 7; i > (8-width & 7); i--) {
531 					WORD src = Read8( io, handle );
532 					*dst = (src >> i) & 1;
533 					dst++;
534 				}
535 			}
536 			break;
537 		default:
538 			throw "Bad bits per pixel in expandBuf8.";
539 	}
540 }
541 
542 static BYTE*
UnpackPictRow(FreeImageIO * io,fi_handle handle,BYTE * pLineBuf,int width,int rowBytes,int srcBytes)543 UnpackPictRow( FreeImageIO *io, fi_handle handle, BYTE* pLineBuf, int width, int rowBytes, int srcBytes ) {
544 
545 	if (rowBytes < 8) { // Ah-ha!  The bits aren't actually packed.  This will be easy.
546 		io->read_proc( pLineBuf, rowBytes, 1, handle );
547 	}
548 	else {
549 		BYTE* pCurPixel = pLineBuf;
550 
551 		// Unpack RLE. The data is packed bytewise.
552 		for (int j = 0; j < srcBytes; )	{
553 			BYTE FlagCounter = Read8( io, handle );
554 			if (FlagCounter & 0x80) {
555 				if (FlagCounter == 0x80) {
556 					// Special case: repeat value of 0.
557 					// Apple says ignore.
558 					j++;
559 				} else {
560 					// Packed data.
561 					int len = ((FlagCounter ^ 255) & 255) + 2;
562 					BYTE p = Read8( io, handle );
563 					memset( pCurPixel, p, len);
564 					pCurPixel += len;
565 					j += 2;
566 				}
567 			}
568 			else {
569 				// Unpacked data
570 				int len = (FlagCounter & 255) + 1;
571 				io->read_proc( pCurPixel, len, 1, handle );
572 				pCurPixel += len;
573 				j += len + 1;
574 			}
575 		}
576 	}
577 
578 	return pLineBuf;
579 }
580 
581 /**
582 This routine decompresses BitsRects with a packType of 4 (and 32 bits per pixel).
583 In this format, each line is separated into 8-bit-bitplanes and then compressed via RLE.
584 To decode, the routine decompresses each line & then juggles the bytes around to get pixel-oriented data.
585 NumBitPlanes == 3 if RGB, 4 if RGBA
586 */
587 static void
Unpack32Bits(FreeImageIO * io,fi_handle handle,FIBITMAP * dib,MacRect * bounds,WORD rowBytes,int numPlanes)588 Unpack32Bits( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacRect* bounds, WORD rowBytes, int numPlanes ) {
589 	int height = bounds->bottom - bounds->top;
590 	int width = bounds->right - bounds->left;
591 
592 	if (rowBytes == 0) {
593 		rowBytes = (WORD)( width * 4 );
594 	}
595 
596 	BYTE* pLineBuf = (BYTE*)malloc( rowBytes ); // Let's allocate enough for 4 bit planes
597 	if ( pLineBuf )	{
598 		try	{
599 			for ( int i = 0; i < height; i++ ) {
600 				// for each line do...
601 				int linelen;            // length of source line in bytes.
602 				if (rowBytes > 250) {
603 					linelen = Read16( io, handle );
604 				} else {
605 					linelen = Read8( io, handle);
606 				}
607 
608 				BYTE* pBuf = UnpackPictRow( io, handle, pLineBuf, width, rowBytes, linelen );
609 
610 				// Convert plane-oriented data into pixel-oriented data &
611 				// copy into destination bitmap.
612 				BYTE* dst = (BYTE*)FreeImage_GetScanLine( dib, height - 1 - i);
613 
614 				if ( numPlanes == 3 ) {
615 					for ( int j = 0; j < width; j++ ) {
616 						// For each pixel in line...
617 						dst[ FI_RGBA_BLUE ] = (*(pBuf+width*2));     // Blue
618 						dst[ FI_RGBA_GREEN ] = (*(pBuf+width));       // Green
619 						dst[ FI_RGBA_RED ] = (*pBuf);             // Red
620 						dst[ FI_RGBA_ALPHA ] = (0xFF);
621 						dst += 4;
622 						pBuf++;
623 					}
624 				} else {
625 					for ( int j = 0; j < width; j++ ) {
626 						// For each pixel in line...
627 						dst[ FI_RGBA_BLUE ] = (*(pBuf+width*3));     // Blue
628 						dst[ FI_RGBA_GREEN ] = (*(pBuf+width*2));     // Green
629 						dst[ FI_RGBA_RED ] = (*(pBuf+width));       // Red
630 						dst[ FI_RGBA_ALPHA ] = (*pBuf);
631 						dst += 4;
632 						pBuf++;
633 					}
634 				}
635 			}
636 		}
637 		catch( ... ) {
638 			free( pLineBuf );
639 			throw;
640 		}
641 	}
642 	free( pLineBuf );
643 }
644 
645 /**
646 Decompression routine for 8 bpp.
647 rowBytes is the number of bytes each source row would take if it were uncompressed.
648 This _isn't_ equal to the number of pixels in the row - it seems apple pads the data to a word boundary and then compresses it.
649 Of course, we have to decompress the excess data and then throw it away.
650 */
651 static void
Unpack8Bits(FreeImageIO * io,fi_handle handle,FIBITMAP * dib,MacRect * bounds,WORD rowBytes)652 Unpack8Bits( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacRect* bounds, WORD rowBytes ) {
653 	int height = bounds->bottom - bounds->top;
654 	int width = bounds->right - bounds->left;
655 
656 	// High bit of rowBytes is flag.
657 	rowBytes &= 0x7fff;
658 
659 	if (rowBytes == 0) {
660 		rowBytes = (WORD)width;
661 	}
662 
663 	for ( int i = 0; i < height; i++ ) {
664 		int linelen;            // length of source line in bytes.
665 		if (rowBytes > 250) {
666 			linelen = Read16( io, handle );
667 		} else {
668 			linelen = Read8( io, handle );
669 		}
670 		BYTE* dst = (BYTE*)FreeImage_GetScanLine( dib, height - 1 - i);
671 		dst = UnpackPictRow( io, handle, dst, width, rowBytes, linelen );
672 	}
673 }
674 
675 /**
676 Decompression routine for everything but 8 & 32 bpp.
677 This routine is slower than the two routines above since it has to deal with a lot of special cases :-(.
678 It's also a bit chaotic because of these special cases...
679 unpack8bits is basically a dumber version of unpackbits.
680 pixelSize == Source bits per pixel.
681 */
682 static void
UnpackBits(FreeImageIO * io,fi_handle handle,FIBITMAP * dib,MacRect * bounds,WORD rowBytes,int pixelSize)683 UnpackBits( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacRect* bounds, WORD rowBytes, int pixelSize ) {
684 	WORD   pixwidth;           // bytes per row when uncompressed.
685 	int    pkpixsize;
686 	int    PixelPerRLEUnit;
687 
688 	char outputMessage[ outputMessageSize ] = "";
689 
690 	int height = bounds->bottom - bounds->top;
691 	int width = bounds->right - bounds->left;
692 
693 	// High bit of rowBytes is flag.
694 	if (pixelSize <= 8) {
695 		rowBytes &= 0x7fff;
696 	}
697 
698 	pixwidth = (WORD)width;
699 	pkpixsize = 1;          // RLE unit: one byte for everything...
700 	if (pixelSize == 16) {    // ...except 16 bpp.
701 		pkpixsize = 2;
702 		pixwidth *= 2;
703 	}
704 
705 	if (rowBytes == 0) {
706 		rowBytes = pixwidth;
707 	}
708 
709 	{
710 		// I allocate the temporary line buffer here. I allocate too
711 		// much memory to compensate for sloppy (& hence fast) decompression.
712 		switch (pixelSize) {
713 			case 1:
714 				PixelPerRLEUnit = 8;
715 				break;
716 			case 2:
717 				PixelPerRLEUnit = 4;
718 				break;
719 			case 4:
720 				PixelPerRLEUnit = 2;
721 				break;
722 			case 8:
723 				PixelPerRLEUnit = 1;
724 				break;
725 			case 16:
726 				PixelPerRLEUnit = 1;
727 				break;
728 			default:
729 				sprintf( outputMessage, "Illegal bpp value in unpackbits: %d\n", pixelSize );
730 				throw outputMessage;
731 		}
732 
733 		if (rowBytes < 8) {
734 			// ah-ha!  The bits aren't actually packed.  This will be easy.
735 			for ( int i = 0; i < height; i++ ) {
736 				BYTE* dst = (BYTE*)FreeImage_GetScanLine( dib, height - 1 - i);
737 				if (pixelSize == 16) {
738 					expandBuf( io, handle, width, pixelSize, dst );
739 				} else {
740 					expandBuf8( io, handle, width, pixelSize, dst );
741 				}
742 			}
743 		}
744 		else {
745 			for ( int i = 0; i < height; i++ ) {
746 				// For each line do...
747 				int    linelen;            // length of source line in bytes.
748 				if (rowBytes > 250) {
749 					linelen = Read16( io, handle );
750 				} else {
751 					linelen = Read8( io, handle );
752 				}
753 
754 				BYTE* dst = (BYTE*)FreeImage_GetScanLine( dib, height - 1 - i);
755 				BYTE FlagCounter;
756 
757 				// Unpack RLE. The data is packed bytewise - except for
758 				// 16 bpp data, which is packed per pixel :-(.
759 				for ( int j = 0; j < linelen; ) {
760 					FlagCounter = Read8( io, handle );
761 					if (FlagCounter & 0x80) {
762 						if (FlagCounter == 0x80) {
763 							// Special case: repeat value of 0.
764 							// Apple says ignore.
765 							j++;
766 						}
767 						else {
768 							// Packed data.
769 							int len = ((FlagCounter ^ 255) & 255) + 2;
770 
771 							// This is slow for some formats...
772 							if (pixelSize == 16) {
773 								expandBuf( io, handle, 1, pixelSize, dst );
774 								for ( int k = 1; k < len; k++ ) {
775 									// Repeat the pixel len times.
776 									memcpy( dst+(k*4*PixelPerRLEUnit), dst,	4*PixelPerRLEUnit);
777 								}
778 								dst += len*4*PixelPerRLEUnit;
779 							}
780 							else {
781 								expandBuf8( io, handle, 1, pixelSize, dst );
782 								for ( int k = 1; k < len; k++ ) {
783 									// Repeat the expanded byte len times.
784 									memcpy( dst+(k*PixelPerRLEUnit), dst, PixelPerRLEUnit);
785 								}
786 								dst += len*PixelPerRLEUnit;
787 							}
788 							j += pkpixsize + 1;
789 						}
790 					}
791 					else {
792 						// Unpacked data
793 						int len = (FlagCounter & 255) + 1;
794 						if (pixelSize == 16) {
795 							expandBuf( io, handle, len, pixelSize, dst );
796 							dst += len*4*PixelPerRLEUnit;
797 						}
798 						else {
799 							expandBuf8( io, handle, len, pixelSize, dst );
800 							dst += len*PixelPerRLEUnit;
801 						}
802 						j += ( len * pkpixsize ) + 1;
803 					}
804 				}
805 			}
806 		}
807 	}
808 }
809 
810 static void
DecodeOp9a(FreeImageIO * io,fi_handle handle,FIBITMAP * dib,MacpixMap * pixMap)811 DecodeOp9a( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacpixMap* pixMap ) {
812 	// Do the actual unpacking.
813 	switch ( pixMap->pixelSize ) {
814 		case 32:
815 			Unpack32Bits( io, handle, dib, &pixMap->Bounds, 0, pixMap->cmpCount );
816 			break;
817 		case 8:
818 			Unpack8Bits( io, handle, dib, &pixMap->Bounds, 0 );
819 			break;
820 		default:
821 			UnpackBits( io, handle, dib, &pixMap->Bounds, 0, pixMap->pixelSize );
822 	}
823 }
824 
825 static void
DecodeBitmap(FreeImageIO * io,fi_handle handle,FIBITMAP * dib,BOOL isRegion,MacRect * bounds,WORD rowBytes)826 DecodeBitmap( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, BOOL isRegion, MacRect* bounds, WORD rowBytes ) {
827 	WORD mode = Read16( io, handle );
828 
829 	if ( isRegion ) {
830 		SkipPolyOrRegion( io, handle );
831 	}
832 
833 	RGBQUAD* pal = FreeImage_GetPalette( dib );
834 	if ( !pal ) {
835 		throw "No palette for bitmap!";
836 	}
837 
838 	for (int i = 0; i < 2; i++) {
839 		unsigned char val = i ? 0xFF : 0x0;
840 		pal[i].rgbRed = val;
841 		pal[i].rgbGreen = val;
842 		pal[i].rgbBlue = val;
843 	}
844 
845 	UnpackBits( io, handle, dib, bounds, rowBytes, 1 );
846 }
847 
848 static void
DecodePixmap(FreeImageIO * io,fi_handle handle,FIBITMAP * dib,BOOL isRegion,MacpixMap * pixMap,WORD rowBytes)849 DecodePixmap( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, BOOL isRegion, MacpixMap* pixMap, WORD rowBytes ) {
850 	// Read mac colour table into windows palette.
851 	WORD numColors;    // Palette size.
852 	RGBQUAD ct[256];
853 
854 	ReadColorTable( io, handle, &numColors, ct );
855 	if ( FreeImage_GetBPP( dib ) == 8 ) {
856 		RGBQUAD* pal = FreeImage_GetPalette( dib );
857 		if ( !pal ) {
858 			throw "No palette for bitmap!";
859 		}
860 
861 		for (int i = 0; i < numColors; i++) {
862 			pal[i].rgbRed = ct[ i ].rgbRed;
863 			pal[i].rgbGreen = ct[ i ].rgbGreen;
864 			pal[i].rgbBlue = ct[ i ].rgbBlue;
865 		}
866 	}
867 
868 	// Ignore source & destination rectangle as well as transfer mode.
869 	MacRect tempRect;
870 	ReadRect( io, handle, &tempRect );
871 	ReadRect( io, handle, &tempRect );
872 	WORD mode = Read16( io, handle );
873 
874 	if ( isRegion) {
875 		SkipPolyOrRegion( io, handle );
876 	}
877 
878 	switch ( pixMap->pixelSize ) {
879 		case 32:
880 			Unpack32Bits( io, handle, dib, &pixMap->Bounds, rowBytes, pixMap->cmpCount );
881 			break;
882 		case 8:
883 			Unpack8Bits( io, handle, dib, &pixMap->Bounds, rowBytes );
884 			break;
885 		default:
886 			UnpackBits( io, handle, dib, &pixMap->Bounds, rowBytes, pixMap->pixelSize );
887 	}
888 }
889 
890 // ==========================================================
891 // Plugin Implementation
892 // ==========================================================
893 
894 static const char * DLL_CALLCONV
Format()895 Format() {
896 	return "PICT";
897 }
898 
899 static const char * DLL_CALLCONV
Description()900 Description() {
901 	return "Macintosh PICT";
902 }
903 
904 static const char * DLL_CALLCONV
Extension()905 Extension() {
906 	return "pct,pict,pic";
907 }
908 
909 static const char * DLL_CALLCONV
MimeType()910 MimeType() {
911 	return "image/x-pict";
912 }
913 
914 static BOOL DLL_CALLCONV
Validate(FreeImageIO * io,fi_handle handle)915 Validate(FreeImageIO *io, fi_handle handle) {
916 	if(io->seek_proc(handle, 522, SEEK_SET) == 0) {
917 		BYTE pict_signature[] = { 0x00, 0x11, 0x02, 0xFF, 0x0C, 0X00 };
918 		BYTE signature[6];
919 
920 		if(io->read_proc(signature, 1, sizeof(pict_signature), handle)) {
921 			// v1.0 files have 0x11 (version operator) followed by 0x01 (version number)
922 			// v2.0 files have 0x0011 (version operator) followed by 0x02ff (version number)
923 			//   and additionally 0x0c00 as a header opcode
924 			// Currently, we are only supporting v2.0
925 			return (memcmp(pict_signature, signature, sizeof(pict_signature)) == 0);
926 		} else {
927 			return FALSE;
928 		}
929 	}
930 
931 	return FALSE;
932 }
933 
934 static BOOL DLL_CALLCONV
SupportsExportDepth(int depth)935 SupportsExportDepth(int depth) {
936 	return FALSE;
937 }
938 
939 static BOOL DLL_CALLCONV
SupportsExportType(FREE_IMAGE_TYPE type)940 SupportsExportType(FREE_IMAGE_TYPE type) {
941 	return FALSE;
942 }
943 
944 static BOOL DLL_CALLCONV
SupportsICCProfiles()945 SupportsICCProfiles() {
946 	return FALSE;
947 }
948 
949 /**
950 This plugin decodes macintosh PICT files with 1,2,4,8,16 and 32 bits per pixel as well as PICT/JPEG.
951 If an alpha channel is present in a 32-bit-PICT, it is decoded as well.
952 The PICT format is a general picture file format and can contain a lot of other elements besides bitmaps.
953 These elements are ignored.
954 */
955 static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO * io,fi_handle handle,int page,int flags,void * data)956 Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
957 	char outputMessage[ outputMessageSize ] = "";
958 	FIBITMAP* dib = NULL;
959 	try {
960 		// Skip empty 512 byte header.
961 		if ( !io->seek_proc(handle, 512, SEEK_CUR) == 0 )
962 			return NULL;
963 
964 		// Read PICT header
965 		Read16( io, handle ); // Skip version 1 picture size
966 
967 		MacRect frame;
968 		ReadRect( io, handle, &frame );
969 
970 		BYTE b = 0;
971 		while ((b = Read8(io, handle)) == 0);
972 		if ( b != 0x11 ) {
973 			throw "invalid header: version number missing.";
974 		}
975 
976 		int version = Read8( io, handle );
977 		if ( version == 2 && Read8( io, handle ) != 0xff ) {
978 			throw "invalid header: illegal version number.";
979 		}
980 
981 		enum PICTType {none, op9a, jpeg, pixmap, bitmap};
982 		PICTType pictType = none;
983 
984 		MacRect bounds;
985 		MacpixMap pixMap;
986 		int hRes = 0x480000; // in pixels/inch (72 by default == 0x480000 in fixed point)
987 		int vRes = 0x480000; // in pixels/inch (72 by default == 0x480000 in fixed point)
988 		WORD rowBytes = 0;
989 		BOOL isRegion = FALSE;
990 		BOOL done = FALSE;
991 		long currentPos = 0;
992 
993 		while ( !done ) {
994 			WORD opcode = 0;
995 
996 			// get the current stream position (used to avoid infinite loops)
997 			currentPos = io->tell_proc(handle);
998 
999 			if ((version == 1) || ((io->tell_proc( handle ) % 2) != 0)) {
1000 				// align to word for version 2
1001 				opcode = Read8( io, handle );
1002 			}
1003 			if (version == 2) {
1004 				opcode = Read16( io, handle );
1005 			}
1006 
1007 			if (opcode == 0xFF || opcode == 0xFFFF) {
1008 				done = TRUE;
1009 				throw "PICT contained only vector data!";
1010 			}
1011 			else if (opcode < 0xa2)	{
1012 				switch (opcode)	{
1013 					case 0x01:
1014 					{
1015 						// skip clipping rectangle
1016 						MacRect clipRect;
1017 						WORD len = Read16( io, handle );
1018 
1019 						if (len == 0x000a) {
1020 							/* null rgn */
1021 							ReadRect( io, handle, &clipRect );
1022 						} else {
1023 							io->seek_proc(handle, len - 2, SEEK_CUR);
1024 						}
1025 						break;
1026 					}
1027 					case 0x12:
1028 					case 0x13:
1029 					case 0x14:
1030 					{
1031 						// skip pattern definition
1032 						WORD       patType;
1033 						WORD       rowBytes;
1034 						MacpixMap  p;
1035 						WORD       numColors;
1036 
1037 						patType = Read16( io, handle );
1038 
1039 						switch( patType ) {
1040 							case 2:
1041 								io->seek_proc(handle, 8, SEEK_CUR);
1042 								io->seek_proc(handle, 5, SEEK_CUR);
1043 								break;
1044 							case 1:
1045 							{
1046 								io->seek_proc(handle, 8, SEEK_CUR);
1047 								rowBytes = Read16( io, handle );
1048 								ReadRect( io, handle, &p.Bounds );
1049 								ReadPixmap( io, handle, &p);
1050 
1051 								RGBQUAD ct[256];
1052 								ReadColorTable(io, handle, &numColors, ct );
1053 								SkipBits( io, handle, &p.Bounds, rowBytes, p.pixelSize );
1054 								break;
1055 							}
1056 							default:
1057 								throw "Unknown pattern type.";
1058 						}
1059 
1060 						break;
1061 					}
1062 					case 0x70:
1063 					case 0x71:
1064 					case 0x72:
1065 					case 0x73:
1066 					case 0x74:
1067 					case 0x75:
1068 					case 0x76:
1069 					case 0x77:
1070 					{
1071 						SkipPolyOrRegion( io, handle );
1072 						break;
1073 					}
1074 					case 0x90:
1075 					case 0x98:
1076 					{
1077 						// Bitmap/pixmap data clipped by a rectangle.
1078 						rowBytes = Read16( io, handle );    // Bytes per row in source when uncompressed.
1079 						isRegion = FALSE;
1080 
1081 						if ( rowBytes & 0x8000) {
1082 							pictType = pixmap;
1083 						} else {
1084 							pictType = bitmap;
1085 						}
1086 						done = TRUE;
1087 						break;
1088 					}
1089 					case 0x91:
1090 					case 0x99:
1091 					{
1092 						// Bitmap/pixmap data clipped by a region.
1093 						rowBytes = Read16( io, handle );    // Bytes per row in source when uncompressed.
1094 						isRegion = TRUE;
1095 
1096 						if ( rowBytes & 0x8000) {
1097 							pictType = pixmap;
1098 						} else {
1099 							pictType = bitmap;
1100 						}
1101 						done = TRUE;
1102 						break;
1103 					}
1104 					case 0x9a:
1105 					{
1106 						// DirectBitsRect.
1107 						Read32( io, handle );           // Skip fake len and fake EOF.
1108 						Read16( io, handle );			// bogus row bytes.
1109 
1110 						// Read in the PixMap fields.
1111 						ReadRect( io, handle, &pixMap.Bounds );
1112 						ReadPixmap( io, handle, &pixMap );
1113 
1114 						// Ignore source & destination rectangle as well as transfer mode.
1115 						MacRect dummy;
1116 						ReadRect( io, handle, &dummy );
1117 						ReadRect( io, handle, &dummy );
1118 						WORD mode = Read16( io, handle );
1119 
1120 						pictType=op9a;
1121 						done = TRUE;
1122 						break;
1123 					}
1124 					case 0xa1:
1125 					{
1126 						// long comment
1127 						WORD type;
1128 						WORD len;
1129 
1130 						type = Read16( io, handle );
1131 						len = Read16( io, handle);
1132 						if (len > 0) {
1133 							io->seek_proc(handle, len, SEEK_CUR);
1134 						}
1135 						break;
1136 					}
1137 					default:
1138 						// No function => skip to next opcode
1139 						if (optable[opcode].len == WORD_LEN) {
1140 							WORD len = Read16( io, handle );
1141 							io->seek_proc(handle, len, SEEK_CUR);
1142 						} else {
1143 							io->seek_proc(handle, optable[opcode].len, SEEK_CUR);
1144 						}
1145 						break;
1146 				}
1147 			}
1148 			else if (opcode == 0xc00) {
1149 				// version 2 header (26 bytes)
1150 				WORD minorVersion = Read16( io, handle );	// always FFFE (-2) for extended version 2
1151 				Read16( io, handle );						// reserved
1152 				hRes = Read32( io, handle );				// original horizontal resolution in pixels/inch
1153 				vRes = Read32( io, handle );				// original horizontal resolution in pixels/inch
1154 				MacRect dummy;
1155 				ReadRect( io, handle, &dummy );				// frame bounds at original resolution
1156 				Read32( io, handle );						// reserved
1157 			}
1158 			else if (opcode == 0x8200) {
1159 				// jpeg
1160 				long opLen = Read32( io, handle );
1161 				BOOL found = FALSE;
1162 				int i = 0;
1163 
1164 				// skip to JPEG header.
1165 				while ( !found && i < opLen ) {
1166 //					io->seek_proc( handle, 24, SEEK_CUR );
1167 //					MacRect dummy;
1168 //					ReadRect( io, handle, &dummy );
1169 //					io->seek_proc( handle, 122, SEEK_CUR );
1170 //					found = TRUE;
1171 					BYTE data[ 2 ];
1172 					if( io->read_proc( data, 2, 1, handle ) ) {
1173 						io->seek_proc( handle, -2, SEEK_CUR );
1174 
1175 						if ( data[0] == 0xFF && data[1] == 0xD8 ) {
1176 							found = TRUE;
1177 						} else {
1178 							Read8( io, handle );
1179 							i++;
1180 						}
1181 					}
1182 				}
1183 
1184 				if ( found ) {
1185 					// Pass the data to the JPEG decoder.
1186 					pictType = jpeg;
1187 				} else {
1188 					throw "PICT file contains unrecognized quicktime data.";
1189 				}
1190 				done = TRUE;
1191 			}
1192 			else if (opcode >= 0xa2 && opcode <= 0xaf) {
1193 				// reserved
1194 				WORD len = Read16( io, handle );
1195 				io->seek_proc(handle, len, SEEK_CUR);
1196 			}
1197 			else if ((opcode >= 0xb0 && opcode <= 0xcf) || (opcode >= 0x8000 && opcode <= 0x80ff)) {
1198 				// just a reserved opcode, no data
1199 			}
1200 			else if ((opcode >= 0xd0 && opcode <= 0xfe) || opcode >= 8100) {
1201 				// reserved
1202 				LONG len = Read32( io, handle );
1203 				io->seek_proc(handle, len, SEEK_CUR);
1204 			}
1205 			else if (opcode >= 0x100 && opcode <= 0x7fff) {
1206 				// reserved
1207 				io->seek_proc(handle, ((opcode >> 7) & 255), SEEK_CUR);
1208 			}
1209 			else {
1210 				sprintf( outputMessage, "Can't handle opcode %x.\n", opcode );
1211 				throw outputMessage;
1212 			}
1213 
1214 			if(currentPos == io->tell_proc(handle)) {
1215 				// we probaly reached the end of file as we can no longer move forward ...
1216 				throw "Invalid PICT file";
1217 			}
1218 		}
1219 
1220 		switch ( pictType )	{
1221 			case op9a:
1222 			{
1223 				bounds = pixMap.Bounds;
1224 				int width = bounds.right - bounds.left;
1225 				int height = bounds.bottom - bounds.top;
1226 
1227 				if ( pixMap.pixelSize > 8 ) {
1228 					dib = FreeImage_Allocate( width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
1229 				} else {
1230 					dib = FreeImage_Allocate( width, height, 8);
1231 				}
1232 				hRes = pixMap.hRes << 16;
1233 				vRes = pixMap.vRes << 16;
1234 				break;
1235 			}
1236 
1237 			case jpeg:
1238 			{
1239 				dib = FreeImage_LoadFromHandle( FIF_JPEG, io, handle );
1240 				break;
1241 			}
1242 
1243 			case pixmap:
1244 			{
1245 				// Decode version 2 pixmap
1246 				ReadRect( io, handle, &pixMap.Bounds );
1247 				ReadPixmap( io, handle, &pixMap );
1248 
1249 				bounds = pixMap.Bounds;
1250 				int width = bounds.right - bounds.left;
1251 				int height = bounds.bottom - bounds.top;
1252 
1253 				if ( pixMap.pixelSize > 8 ) {
1254 					dib = FreeImage_Allocate( width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
1255 				} else {
1256 					dib = FreeImage_Allocate( width, height, 8);
1257 				}
1258 				hRes = pixMap.hRes << 16;
1259 				vRes = pixMap.vRes << 16;
1260 				break;
1261 			}
1262 
1263 			case bitmap:
1264 			{
1265 				// Decode version 1 bitmap: 1 bpp.
1266 				MacRect srcRect;
1267 				MacRect dstRect;
1268 				WORD width;        // Width in pixels
1269 				WORD height;       // Height in pixels
1270 
1271 				ReadRect( io, handle, &bounds );
1272 				ReadRect( io, handle, &srcRect );
1273 				ReadRect( io, handle, &dstRect );
1274 
1275 				width = bounds.right - bounds.left;
1276 				height = bounds.bottom - bounds.top;
1277 
1278 				dib = FreeImage_Allocate(width, height, 8);
1279 				break;
1280 			}
1281 		}
1282 
1283 		if ( dib ) {
1284 			// need to convert resolution figures from fixed point, pixels/inch
1285 			// to floating point, pixels/meter.
1286 			float hres_ppm = hRes * ((float)39.4 / (float)65536.0);
1287 			float vres_ppm = vRes * ((float)39.4 / (float)65536.0);
1288 
1289 			FreeImage_SetDotsPerMeterX( dib, (LONG)hres_ppm );
1290 			FreeImage_SetDotsPerMeterY( dib, (LONG)vres_ppm );
1291 
1292 			switch( pictType ) {
1293 				case op9a:
1294 					DecodeOp9a( io, handle, dib, &pixMap );
1295 					break;
1296 				case jpeg:
1297 					// Already decoded if the embedded format was valid.
1298 					break;
1299 				case pixmap:
1300 					DecodePixmap( io, handle, dib, isRegion, &pixMap, rowBytes );
1301 					break;
1302 				case bitmap:
1303 					DecodeBitmap( io, handle, dib, isRegion, &bounds, rowBytes );
1304 					break;
1305 				default:
1306 					throw "invalid pict type";
1307 			}
1308 		}
1309 
1310 		return dib;
1311 	}
1312 	catch(const char *message) {
1313 		FreeImage_Unload( dib );
1314 		FreeImage_OutputMessageProc(s_format_id, message);
1315 	}
1316 
1317 	return NULL;
1318 }
1319 
1320 // ==========================================================
1321 //   Init
1322 // ==========================================================
1323 
1324 void DLL_CALLCONV
InitPICT(Plugin * plugin,int format_id)1325 InitPICT(Plugin *plugin, int format_id) {
1326 	s_format_id = format_id;
1327 
1328 	plugin->format_proc = Format;
1329 	plugin->description_proc = Description;
1330 	plugin->extension_proc = Extension;
1331 	plugin->regexpr_proc = NULL;
1332 	plugin->open_proc = NULL;
1333 	plugin->close_proc = NULL;
1334 	plugin->pagecount_proc = NULL;
1335 	plugin->pagecapability_proc = NULL;
1336 	plugin->load_proc = Load;
1337 	plugin->save_proc = NULL;
1338 	plugin->validate_proc = Validate;
1339 	plugin->mime_proc = MimeType;
1340 	plugin->supports_export_bpp_proc = SupportsExportDepth;
1341 	plugin->supports_export_type_proc = SupportsExportType;
1342 	plugin->supports_icc_profiles_proc = SupportsICCProfiles;
1343 }
1344