/src/freeimage-svn/FreeImage/trunk/Source/FreeImage/PluginPICT.cpp
Line | Count | Source (jump to first uncovered line) |
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 |
86 | 0 | Read8(FreeImageIO *io, fi_handle handle) { |
87 | 0 | BYTE i = 0; |
88 | 0 | io->read_proc(&i, 1, 1, handle); |
89 | 0 | return i; |
90 | 0 | } |
91 | | |
92 | | static WORD |
93 | 0 | 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 | 0 | unsigned hi = Read8(io, handle); |
98 | 0 | unsigned lo = Read8(io, handle); |
99 | 0 | return (WORD)(lo + (hi << 8)); |
100 | 0 | } |
101 | | |
102 | | static unsigned |
103 | 0 | 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 | 0 | unsigned b3 = Read8(io, handle); |
108 | 0 | unsigned b2 = Read8(io, handle); |
109 | 0 | unsigned b1 = Read8(io, handle); |
110 | 0 | unsigned b0 = Read8(io, handle); |
111 | 0 | return (b3 << 24) + (b2 << 16) + (b1 << 8) + b0; |
112 | 0 | } |
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 | 0 | #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 |
345 | 0 | ReadRect( FreeImageIO *io, fi_handle handle, MacRect* rect ) { |
346 | 0 | rect->top = Read16( io, handle ); |
347 | 0 | rect->left = Read16( io, handle ); |
348 | 0 | rect->bottom = Read16( io, handle ); |
349 | 0 | rect->right = Read16( io, handle ); |
350 | 0 | } |
351 | | |
352 | | static void |
353 | 0 | ReadPixmap( FreeImageIO *io, fi_handle handle, MacpixMap* pPixMap ) { |
354 | 0 | pPixMap->version = Read16( io, handle ); |
355 | 0 | pPixMap->packType = Read16( io, handle ); |
356 | 0 | pPixMap->packSize = Read32( io, handle ); |
357 | 0 | pPixMap->hRes = Read16( io, handle ); |
358 | 0 | Read16( io, handle ); |
359 | 0 | pPixMap->vRes = Read16( io, handle ); |
360 | 0 | Read16( io, handle ); |
361 | 0 | pPixMap->pixelType = Read16( io, handle ); |
362 | 0 | pPixMap->pixelSize = Read16( io, handle ); |
363 | 0 | pPixMap->cmpCount = Read16( io, handle ); |
364 | 0 | pPixMap->cmpSize = Read16( io, handle ); |
365 | 0 | pPixMap->planeBytes = Read32( io, handle ); |
366 | 0 | pPixMap->pmTable = Read32( io, handle ); |
367 | 0 | pPixMap->pmReserved = Read32( io, handle ); |
368 | 0 | } |
369 | | |
370 | | /** |
371 | | Reads a mac color table into a bitmap palette. |
372 | | */ |
373 | | static void |
374 | 0 | ReadColorTable( FreeImageIO *io, fi_handle handle, WORD* pNumColors, RGBQUAD* pPal ) { |
375 | 0 | LONG ctSeed; |
376 | 0 | WORD ctFlags; |
377 | 0 | WORD val; |
378 | 0 | int i; |
379 | | |
380 | 0 | ctSeed = Read32( io, handle ); |
381 | 0 | ctFlags = Read16( io, handle ); |
382 | 0 | WORD numColors = Read16( io, handle )+1; |
383 | 0 | *pNumColors = numColors; |
384 | | |
385 | 0 | for (i = 0; i < numColors; i++) { |
386 | 0 | val = Read16( io, handle ); |
387 | 0 | 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 | 0 | val = (WORD)i; |
392 | 0 | } |
393 | 0 | if (val >= numColors) { |
394 | 0 | throw "pixel value greater than color table size."; |
395 | 0 | } |
396 | | // Mac colour tables contain 16-bit values for R, G, and B... |
397 | 0 | pPal[val].rgbRed = ((BYTE) (((WORD) (Read16( io, handle )) >> 8) & 0xFF)); |
398 | 0 | pPal[val].rgbGreen = ((BYTE) (((WORD) (Read16( io, handle )) >> 8) & 0xFF)); |
399 | 0 | pPal[val].rgbBlue = ((BYTE) (((WORD) (Read16( io, handle )) >> 8) & 0xFF)); |
400 | 0 | } |
401 | 0 | } |
402 | | |
403 | | /** |
404 | | skips unneeded packbits. |
405 | | pixelSize == Source bits per pixel. |
406 | | */ |
407 | | static void |
408 | 0 | SkipBits( FreeImageIO *io, fi_handle handle, MacRect* bounds, WORD rowBytes, int pixelSize ) { |
409 | 0 | int i; |
410 | 0 | WORD pixwidth; // bytes per row when uncompressed. |
411 | | |
412 | 0 | int height = bounds->bottom - bounds->top; |
413 | 0 | int width = bounds->right - bounds->left; |
414 | | |
415 | | // High bit of rowBytes is flag. |
416 | 0 | if (pixelSize <= 8) { |
417 | 0 | rowBytes &= 0x7fff; |
418 | 0 | } |
419 | 0 | pixwidth = (WORD)width; |
420 | | |
421 | 0 | if (pixelSize == 16) { |
422 | 0 | pixwidth *= 2; |
423 | 0 | } |
424 | 0 | if (rowBytes == 0) { |
425 | 0 | rowBytes = pixwidth; |
426 | 0 | } |
427 | 0 | if (rowBytes < 8) { |
428 | 0 | io->seek_proc( handle, rowBytes*height, SEEK_CUR ); |
429 | 0 | } |
430 | 0 | else { |
431 | 0 | for (i = 0; i < height; i++) { |
432 | 0 | int lineLen; // length of source line in bytes. |
433 | 0 | if (rowBytes > 250) { |
434 | 0 | lineLen = Read16( io, handle ); |
435 | 0 | } else { |
436 | 0 | lineLen = Read8( io, handle ); |
437 | 0 | } |
438 | 0 | io->seek_proc( handle, lineLen, SEEK_CUR ); |
439 | 0 | } |
440 | 0 | } |
441 | 0 | } |
442 | | |
443 | | /** |
444 | | Skip polygon or region |
445 | | */ |
446 | | static void |
447 | 0 | SkipPolyOrRegion( FreeImageIO *io, fi_handle handle ) { |
448 | 0 | WORD len = Read16( io, handle ) - 2; |
449 | 0 | io->seek_proc(handle, len, SEEK_CUR); |
450 | 0 | } |
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 |
458 | 0 | expandBuf( FreeImageIO *io, fi_handle handle, int width, int bpp, BYTE* dst ) { |
459 | 0 | switch (bpp) { |
460 | 0 | case 16: |
461 | 0 | for ( int i=0; i<width; i++) { |
462 | 0 | WORD src = Read16( io, handle ); |
463 | 0 | dst[ FI_RGBA_BLUE ] = (src & 31)*8; // Blue |
464 | 0 | dst[ FI_RGBA_GREEN ] = ((src >> 5) & 31)*8; // Green |
465 | 0 | dst[ FI_RGBA_RED ] = ((src >> 10) & 31)*8; // Red |
466 | 0 | dst[ FI_RGBA_ALPHA ] = 0xFF; // Alpha |
467 | 0 | dst += 4; |
468 | 0 | } |
469 | 0 | break; |
470 | 0 | default: |
471 | 0 | throw "Bad bits per pixel in expandBuf."; |
472 | 0 | } |
473 | 0 | } |
474 | | |
475 | | /** |
476 | | Expands Width units to 8-bit pixel data. |
477 | | Max. 8 bpp source format. |
478 | | */ |
479 | | static void |
480 | | expandBuf8( FreeImageIO *io, fi_handle handle, int width, int bpp, BYTE* dst ) |
481 | 0 | { |
482 | 0 | switch (bpp) { |
483 | 0 | case 8: |
484 | 0 | io->read_proc( dst, width, 1, handle ); |
485 | 0 | break; |
486 | 0 | case 4: |
487 | 0 | for (int i = 0; i < width; i++) { |
488 | 0 | WORD src = Read8( io, handle ); |
489 | 0 | *dst = (src >> 4) & 15; |
490 | 0 | *(dst+1) = (src & 15); |
491 | 0 | dst += 2; |
492 | 0 | } |
493 | 0 | if (width & 1) { // Odd Width? |
494 | 0 | WORD src = Read8( io, handle ); |
495 | 0 | *dst = (src >> 4) & 15; |
496 | 0 | dst++; |
497 | 0 | } |
498 | 0 | break; |
499 | 0 | case 2: |
500 | 0 | for (int i = 0; i < width; i++) { |
501 | 0 | WORD src = Read8( io, handle ); |
502 | 0 | *dst = (src >> 6) & 3; |
503 | 0 | *(dst+1) = (src >> 4) & 3; |
504 | 0 | *(dst+2) = (src >> 2) & 3; |
505 | 0 | *(dst+3) = (src & 3); |
506 | 0 | dst += 4; |
507 | 0 | } |
508 | 0 | if (width & 3) { // Check for leftover pixels |
509 | 0 | for (int i = 6; i > 8 - (width & 3) * 2; i -= 2) { |
510 | 0 | WORD src = Read8( io, handle ); |
511 | 0 | *dst = (src >> i) & 3; |
512 | 0 | dst++; |
513 | 0 | } |
514 | 0 | } |
515 | 0 | break; |
516 | 0 | case 1: |
517 | 0 | for (int i = 0; i < width; i++) { |
518 | 0 | WORD src = Read8( io, handle ); |
519 | 0 | *dst = (src >> 7) & 1; |
520 | 0 | *(dst+1) = (src >> 6) & 1; |
521 | 0 | *(dst+2) = (src >> 5) & 1; |
522 | 0 | *(dst+3) = (src >> 4) & 1; |
523 | 0 | *(dst+4) = (src >> 3) & 1; |
524 | 0 | *(dst+5) = (src >> 2) & 1; |
525 | 0 | *(dst+6) = (src >> 1) & 1; |
526 | 0 | *(dst+7) = (src & 1); |
527 | 0 | dst += 8; |
528 | 0 | } |
529 | 0 | if (width & 7) { // Check for leftover pixels |
530 | 0 | for (int i = 7; i > (8-width & 7); i--) { |
531 | 0 | WORD src = Read8( io, handle ); |
532 | 0 | *dst = (src >> i) & 1; |
533 | 0 | dst++; |
534 | 0 | } |
535 | 0 | } |
536 | 0 | break; |
537 | 0 | default: |
538 | 0 | throw "Bad bits per pixel in expandBuf8."; |
539 | 0 | } |
540 | 0 | } |
541 | | |
542 | | static BYTE* |
543 | 0 | UnpackPictRow( FreeImageIO *io, fi_handle handle, BYTE* pLineBuf, int width, int rowBytes, int srcBytes ) { |
544 | |
|
545 | 0 | if (rowBytes < 8) { // Ah-ha! The bits aren't actually packed. This will be easy. |
546 | 0 | io->read_proc( pLineBuf, rowBytes, 1, handle ); |
547 | 0 | } |
548 | 0 | else { |
549 | 0 | BYTE* pCurPixel = pLineBuf; |
550 | | |
551 | | // Unpack RLE. The data is packed bytewise. |
552 | 0 | for (int j = 0; j < srcBytes; ) { |
553 | 0 | BYTE FlagCounter = Read8( io, handle ); |
554 | 0 | if (FlagCounter & 0x80) { |
555 | 0 | if (FlagCounter == 0x80) { |
556 | | // Special case: repeat value of 0. |
557 | | // Apple says ignore. |
558 | 0 | j++; |
559 | 0 | } else { |
560 | | // Packed data. |
561 | 0 | int len = ((FlagCounter ^ 255) & 255) + 2; |
562 | 0 | BYTE p = Read8( io, handle ); |
563 | 0 | memset( pCurPixel, p, len); |
564 | 0 | pCurPixel += len; |
565 | 0 | j += 2; |
566 | 0 | } |
567 | 0 | } |
568 | 0 | else { |
569 | | // Unpacked data |
570 | 0 | int len = (FlagCounter & 255) + 1; |
571 | 0 | io->read_proc( pCurPixel, len, 1, handle ); |
572 | 0 | pCurPixel += len; |
573 | 0 | j += len + 1; |
574 | 0 | } |
575 | 0 | } |
576 | 0 | } |
577 | | |
578 | 0 | return pLineBuf; |
579 | 0 | } |
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 |
588 | 0 | Unpack32Bits( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacRect* bounds, WORD rowBytes, int numPlanes ) { |
589 | 0 | int height = bounds->bottom - bounds->top; |
590 | 0 | int width = bounds->right - bounds->left; |
591 | | |
592 | 0 | if (rowBytes == 0) { |
593 | 0 | rowBytes = (WORD)( width * 4 ); |
594 | 0 | } |
595 | | |
596 | 0 | BYTE* pLineBuf = (BYTE*)malloc( rowBytes ); // Let's allocate enough for 4 bit planes |
597 | 0 | if ( pLineBuf ) { |
598 | 0 | try { |
599 | 0 | for ( int i = 0; i < height; i++ ) { |
600 | | // for each line do... |
601 | 0 | int linelen; // length of source line in bytes. |
602 | 0 | if (rowBytes > 250) { |
603 | 0 | linelen = Read16( io, handle ); |
604 | 0 | } else { |
605 | 0 | linelen = Read8( io, handle); |
606 | 0 | } |
607 | | |
608 | 0 | 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 | 0 | BYTE* dst = (BYTE*)FreeImage_GetScanLine( dib, height - 1 - i); |
613 | | |
614 | 0 | if ( numPlanes == 3 ) { |
615 | 0 | for ( int j = 0; j < width; j++ ) { |
616 | | // For each pixel in line... |
617 | 0 | dst[ FI_RGBA_BLUE ] = (*(pBuf+width*2)); // Blue |
618 | 0 | dst[ FI_RGBA_GREEN ] = (*(pBuf+width)); // Green |
619 | 0 | dst[ FI_RGBA_RED ] = (*pBuf); // Red |
620 | 0 | dst[ FI_RGBA_ALPHA ] = (0xFF); |
621 | 0 | dst += 4; |
622 | 0 | pBuf++; |
623 | 0 | } |
624 | 0 | } else { |
625 | 0 | for ( int j = 0; j < width; j++ ) { |
626 | | // For each pixel in line... |
627 | 0 | dst[ FI_RGBA_BLUE ] = (*(pBuf+width*3)); // Blue |
628 | 0 | dst[ FI_RGBA_GREEN ] = (*(pBuf+width*2)); // Green |
629 | 0 | dst[ FI_RGBA_RED ] = (*(pBuf+width)); // Red |
630 | 0 | dst[ FI_RGBA_ALPHA ] = (*pBuf); |
631 | 0 | dst += 4; |
632 | 0 | pBuf++; |
633 | 0 | } |
634 | 0 | } |
635 | 0 | } |
636 | 0 | } |
637 | 0 | catch( ... ) { |
638 | 0 | free( pLineBuf ); |
639 | 0 | throw; |
640 | 0 | } |
641 | 0 | } |
642 | 0 | free( pLineBuf ); |
643 | 0 | } |
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 |
652 | 0 | Unpack8Bits( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacRect* bounds, WORD rowBytes ) { |
653 | 0 | int height = bounds->bottom - bounds->top; |
654 | 0 | int width = bounds->right - bounds->left; |
655 | | |
656 | | // High bit of rowBytes is flag. |
657 | 0 | rowBytes &= 0x7fff; |
658 | | |
659 | 0 | if (rowBytes == 0) { |
660 | 0 | rowBytes = (WORD)width; |
661 | 0 | } |
662 | | |
663 | 0 | for ( int i = 0; i < height; i++ ) { |
664 | 0 | int linelen; // length of source line in bytes. |
665 | 0 | if (rowBytes > 250) { |
666 | 0 | linelen = Read16( io, handle ); |
667 | 0 | } else { |
668 | 0 | linelen = Read8( io, handle ); |
669 | 0 | } |
670 | 0 | BYTE* dst = (BYTE*)FreeImage_GetScanLine( dib, height - 1 - i); |
671 | 0 | dst = UnpackPictRow( io, handle, dst, width, rowBytes, linelen ); |
672 | 0 | } |
673 | 0 | } |
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 |
683 | 0 | UnpackBits( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacRect* bounds, WORD rowBytes, int pixelSize ) { |
684 | 0 | WORD pixwidth; // bytes per row when uncompressed. |
685 | 0 | int pkpixsize; |
686 | 0 | int PixelPerRLEUnit; |
687 | |
|
688 | 0 | char outputMessage[ outputMessageSize ] = ""; |
689 | | |
690 | 0 | int height = bounds->bottom - bounds->top; |
691 | 0 | int width = bounds->right - bounds->left; |
692 | | |
693 | | // High bit of rowBytes is flag. |
694 | 0 | if (pixelSize <= 8) { |
695 | 0 | rowBytes &= 0x7fff; |
696 | 0 | } |
697 | | |
698 | 0 | pixwidth = (WORD)width; |
699 | 0 | pkpixsize = 1; // RLE unit: one byte for everything... |
700 | 0 | if (pixelSize == 16) { // ...except 16 bpp. |
701 | 0 | pkpixsize = 2; |
702 | 0 | pixwidth *= 2; |
703 | 0 | } |
704 | | |
705 | 0 | if (rowBytes == 0) { |
706 | 0 | rowBytes = pixwidth; |
707 | 0 | } |
708 | | |
709 | 0 | { |
710 | | // I allocate the temporary line buffer here. I allocate too |
711 | | // much memory to compensate for sloppy (& hence fast) decompression. |
712 | 0 | switch (pixelSize) { |
713 | 0 | case 1: |
714 | 0 | PixelPerRLEUnit = 8; |
715 | 0 | break; |
716 | 0 | case 2: |
717 | 0 | PixelPerRLEUnit = 4; |
718 | 0 | break; |
719 | 0 | case 4: |
720 | 0 | PixelPerRLEUnit = 2; |
721 | 0 | break; |
722 | 0 | case 8: |
723 | 0 | PixelPerRLEUnit = 1; |
724 | 0 | break; |
725 | 0 | case 16: |
726 | 0 | PixelPerRLEUnit = 1; |
727 | 0 | break; |
728 | 0 | default: |
729 | 0 | sprintf( outputMessage, "Illegal bpp value in unpackbits: %d\n", pixelSize ); |
730 | 0 | throw outputMessage; |
731 | 0 | } |
732 | | |
733 | 0 | if (rowBytes < 8) { |
734 | | // ah-ha! The bits aren't actually packed. This will be easy. |
735 | 0 | for ( int i = 0; i < height; i++ ) { |
736 | 0 | BYTE* dst = (BYTE*)FreeImage_GetScanLine( dib, height - 1 - i); |
737 | 0 | if (pixelSize == 16) { |
738 | 0 | expandBuf( io, handle, width, pixelSize, dst ); |
739 | 0 | } else { |
740 | 0 | expandBuf8( io, handle, width, pixelSize, dst ); |
741 | 0 | } |
742 | 0 | } |
743 | 0 | } |
744 | 0 | else { |
745 | 0 | for ( int i = 0; i < height; i++ ) { |
746 | | // For each line do... |
747 | 0 | int linelen; // length of source line in bytes. |
748 | 0 | if (rowBytes > 250) { |
749 | 0 | linelen = Read16( io, handle ); |
750 | 0 | } else { |
751 | 0 | linelen = Read8( io, handle ); |
752 | 0 | } |
753 | | |
754 | 0 | BYTE* dst = (BYTE*)FreeImage_GetScanLine( dib, height - 1 - i); |
755 | 0 | BYTE FlagCounter; |
756 | | |
757 | | // Unpack RLE. The data is packed bytewise - except for |
758 | | // 16 bpp data, which is packed per pixel :-(. |
759 | 0 | for ( int j = 0; j < linelen; ) { |
760 | 0 | FlagCounter = Read8( io, handle ); |
761 | 0 | if (FlagCounter & 0x80) { |
762 | 0 | if (FlagCounter == 0x80) { |
763 | | // Special case: repeat value of 0. |
764 | | // Apple says ignore. |
765 | 0 | j++; |
766 | 0 | } |
767 | 0 | else { |
768 | | // Packed data. |
769 | 0 | int len = ((FlagCounter ^ 255) & 255) + 2; |
770 | | |
771 | | // This is slow for some formats... |
772 | 0 | if (pixelSize == 16) { |
773 | 0 | expandBuf( io, handle, 1, pixelSize, dst ); |
774 | 0 | for ( int k = 1; k < len; k++ ) { |
775 | | // Repeat the pixel len times. |
776 | 0 | memcpy( dst+(k*4*PixelPerRLEUnit), dst, 4*PixelPerRLEUnit); |
777 | 0 | } |
778 | 0 | dst += len*4*PixelPerRLEUnit; |
779 | 0 | } |
780 | 0 | else { |
781 | 0 | expandBuf8( io, handle, 1, pixelSize, dst ); |
782 | 0 | for ( int k = 1; k < len; k++ ) { |
783 | | // Repeat the expanded byte len times. |
784 | 0 | memcpy( dst+(k*PixelPerRLEUnit), dst, PixelPerRLEUnit); |
785 | 0 | } |
786 | 0 | dst += len*PixelPerRLEUnit; |
787 | 0 | } |
788 | 0 | j += pkpixsize + 1; |
789 | 0 | } |
790 | 0 | } |
791 | 0 | else { |
792 | | // Unpacked data |
793 | 0 | int len = (FlagCounter & 255) + 1; |
794 | 0 | if (pixelSize == 16) { |
795 | 0 | expandBuf( io, handle, len, pixelSize, dst ); |
796 | 0 | dst += len*4*PixelPerRLEUnit; |
797 | 0 | } |
798 | 0 | else { |
799 | 0 | expandBuf8( io, handle, len, pixelSize, dst ); |
800 | 0 | dst += len*PixelPerRLEUnit; |
801 | 0 | } |
802 | 0 | j += ( len * pkpixsize ) + 1; |
803 | 0 | } |
804 | 0 | } |
805 | 0 | } |
806 | 0 | } |
807 | 0 | } |
808 | 0 | } |
809 | | |
810 | | static void |
811 | 0 | DecodeOp9a( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, MacpixMap* pixMap ) { |
812 | | // Do the actual unpacking. |
813 | 0 | switch ( pixMap->pixelSize ) { |
814 | 0 | case 32: |
815 | 0 | Unpack32Bits( io, handle, dib, &pixMap->Bounds, 0, pixMap->cmpCount ); |
816 | 0 | break; |
817 | 0 | case 8: |
818 | 0 | Unpack8Bits( io, handle, dib, &pixMap->Bounds, 0 ); |
819 | 0 | break; |
820 | 0 | default: |
821 | 0 | UnpackBits( io, handle, dib, &pixMap->Bounds, 0, pixMap->pixelSize ); |
822 | 0 | } |
823 | 0 | } |
824 | | |
825 | | static void |
826 | 0 | DecodeBitmap( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, BOOL isRegion, MacRect* bounds, WORD rowBytes ) { |
827 | 0 | WORD mode = Read16( io, handle ); |
828 | | |
829 | 0 | if ( isRegion ) { |
830 | 0 | SkipPolyOrRegion( io, handle ); |
831 | 0 | } |
832 | | |
833 | 0 | RGBQUAD* pal = FreeImage_GetPalette( dib ); |
834 | 0 | if ( !pal ) { |
835 | 0 | throw "No palette for bitmap!"; |
836 | 0 | } |
837 | | |
838 | 0 | for (int i = 0; i < 2; i++) { |
839 | 0 | unsigned char val = i ? 0xFF : 0x0; |
840 | 0 | pal[i].rgbRed = val; |
841 | 0 | pal[i].rgbGreen = val; |
842 | 0 | pal[i].rgbBlue = val; |
843 | 0 | } |
844 | | |
845 | 0 | UnpackBits( io, handle, dib, bounds, rowBytes, 1 ); |
846 | 0 | } |
847 | | |
848 | | static void |
849 | 0 | DecodePixmap( FreeImageIO *io, fi_handle handle, FIBITMAP* dib, BOOL isRegion, MacpixMap* pixMap, WORD rowBytes ) { |
850 | | // Read mac colour table into windows palette. |
851 | 0 | WORD numColors; // Palette size. |
852 | 0 | RGBQUAD ct[256]; |
853 | | |
854 | 0 | ReadColorTable( io, handle, &numColors, ct ); |
855 | 0 | if ( FreeImage_GetBPP( dib ) == 8 ) { |
856 | 0 | RGBQUAD* pal = FreeImage_GetPalette( dib ); |
857 | 0 | if ( !pal ) { |
858 | 0 | throw "No palette for bitmap!"; |
859 | 0 | } |
860 | | |
861 | 0 | for (int i = 0; i < numColors; i++) { |
862 | 0 | pal[i].rgbRed = ct[ i ].rgbRed; |
863 | 0 | pal[i].rgbGreen = ct[ i ].rgbGreen; |
864 | 0 | pal[i].rgbBlue = ct[ i ].rgbBlue; |
865 | 0 | } |
866 | 0 | } |
867 | | |
868 | | // Ignore source & destination rectangle as well as transfer mode. |
869 | 0 | MacRect tempRect; |
870 | 0 | ReadRect( io, handle, &tempRect ); |
871 | 0 | ReadRect( io, handle, &tempRect ); |
872 | 0 | WORD mode = Read16( io, handle ); |
873 | | |
874 | 0 | if ( isRegion) { |
875 | 0 | SkipPolyOrRegion( io, handle ); |
876 | 0 | } |
877 | | |
878 | 0 | switch ( pixMap->pixelSize ) { |
879 | 0 | case 32: |
880 | 0 | Unpack32Bits( io, handle, dib, &pixMap->Bounds, rowBytes, pixMap->cmpCount ); |
881 | 0 | break; |
882 | 0 | case 8: |
883 | 0 | Unpack8Bits( io, handle, dib, &pixMap->Bounds, rowBytes ); |
884 | 0 | break; |
885 | 0 | default: |
886 | 0 | UnpackBits( io, handle, dib, &pixMap->Bounds, rowBytes, pixMap->pixelSize ); |
887 | 0 | } |
888 | 0 | } |
889 | | |
890 | | // ========================================================== |
891 | | // Plugin Implementation |
892 | | // ========================================================== |
893 | | |
894 | | static const char * DLL_CALLCONV |
895 | 2 | Format() { |
896 | 2 | return "PICT"; |
897 | 2 | } |
898 | | |
899 | | static const char * DLL_CALLCONV |
900 | 0 | Description() { |
901 | 0 | return "Macintosh PICT"; |
902 | 0 | } |
903 | | |
904 | | static const char * DLL_CALLCONV |
905 | 0 | Extension() { |
906 | 0 | return "pct,pict,pic"; |
907 | 0 | } |
908 | | |
909 | | static const char * DLL_CALLCONV |
910 | 0 | MimeType() { |
911 | 0 | return "image/x-pict"; |
912 | 0 | } |
913 | | |
914 | | static BOOL DLL_CALLCONV |
915 | 24.2k | Validate(FreeImageIO *io, fi_handle handle) { |
916 | 24.2k | if(io->seek_proc(handle, 522, SEEK_SET) == 0) { |
917 | 24.2k | BYTE pict_signature[] = { 0x00, 0x11, 0x02, 0xFF, 0x0C, 0X00 }; |
918 | 24.2k | BYTE signature[6]; |
919 | | |
920 | 24.2k | 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 | 16.4k | return (memcmp(pict_signature, signature, sizeof(pict_signature)) == 0); |
926 | 16.4k | } else { |
927 | 7.79k | return FALSE; |
928 | 7.79k | } |
929 | 24.2k | } |
930 | | |
931 | 0 | return FALSE; |
932 | 24.2k | } |
933 | | |
934 | | static BOOL DLL_CALLCONV |
935 | 0 | SupportsExportDepth(int depth) { |
936 | 0 | return FALSE; |
937 | 0 | } |
938 | | |
939 | | static BOOL DLL_CALLCONV |
940 | 0 | SupportsExportType(FREE_IMAGE_TYPE type) { |
941 | 0 | return FALSE; |
942 | 0 | } |
943 | | |
944 | | static BOOL DLL_CALLCONV |
945 | 0 | SupportsICCProfiles() { |
946 | 0 | return FALSE; |
947 | 0 | } |
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 |
956 | 0 | Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { |
957 | 0 | char outputMessage[ outputMessageSize ] = ""; |
958 | 0 | FIBITMAP* dib = NULL; |
959 | 0 | try { |
960 | | // Skip empty 512 byte header. |
961 | 0 | if ( !io->seek_proc(handle, 512, SEEK_CUR) == 0 ) |
962 | 0 | return NULL; |
963 | | |
964 | | // Read PICT header |
965 | 0 | Read16( io, handle ); // Skip version 1 picture size |
966 | | |
967 | 0 | MacRect frame; |
968 | 0 | ReadRect( io, handle, &frame ); |
969 | |
|
970 | 0 | BYTE b = 0; |
971 | 0 | while ((b = Read8(io, handle)) == 0); |
972 | 0 | if ( b != 0x11 ) { |
973 | 0 | throw "invalid header: version number missing."; |
974 | 0 | } |
975 | | |
976 | 0 | int version = Read8( io, handle ); |
977 | 0 | if ( version == 2 && Read8( io, handle ) != 0xff ) { |
978 | 0 | throw "invalid header: illegal version number."; |
979 | 0 | } |
980 | | |
981 | 0 | enum PICTType {none, op9a, jpeg, pixmap, bitmap}; |
982 | 0 | PICTType pictType = none; |
983 | | |
984 | 0 | MacRect bounds; |
985 | 0 | MacpixMap pixMap; |
986 | 0 | int hRes = 0x480000; // in pixels/inch (72 by default == 0x480000 in fixed point) |
987 | 0 | int vRes = 0x480000; // in pixels/inch (72 by default == 0x480000 in fixed point) |
988 | 0 | WORD rowBytes = 0; |
989 | 0 | BOOL isRegion = FALSE; |
990 | 0 | BOOL done = FALSE; |
991 | 0 | long currentPos = 0; |
992 | |
|
993 | 0 | while ( !done ) { |
994 | 0 | WORD opcode = 0; |
995 | | |
996 | | // get the current stream position (used to avoid infinite loops) |
997 | 0 | currentPos = io->tell_proc(handle); |
998 | | |
999 | 0 | if ((version == 1) || ((io->tell_proc( handle ) % 2) != 0)) { |
1000 | | // align to word for version 2 |
1001 | 0 | opcode = Read8( io, handle ); |
1002 | 0 | } |
1003 | 0 | if (version == 2) { |
1004 | 0 | opcode = Read16( io, handle ); |
1005 | 0 | } |
1006 | | |
1007 | 0 | if (opcode == 0xFF || opcode == 0xFFFF) { |
1008 | 0 | done = TRUE; |
1009 | 0 | throw "PICT contained only vector data!"; |
1010 | 0 | } |
1011 | 0 | else if (opcode < 0xa2) { |
1012 | 0 | switch (opcode) { |
1013 | 0 | case 0x01: |
1014 | 0 | { |
1015 | | // skip clipping rectangle |
1016 | 0 | MacRect clipRect; |
1017 | 0 | WORD len = Read16( io, handle ); |
1018 | |
|
1019 | 0 | if (len == 0x000a) { |
1020 | | /* null rgn */ |
1021 | 0 | ReadRect( io, handle, &clipRect ); |
1022 | 0 | } else { |
1023 | 0 | io->seek_proc(handle, len - 2, SEEK_CUR); |
1024 | 0 | } |
1025 | 0 | break; |
1026 | 0 | } |
1027 | 0 | case 0x12: |
1028 | 0 | case 0x13: |
1029 | 0 | case 0x14: |
1030 | 0 | { |
1031 | | // skip pattern definition |
1032 | 0 | WORD patType; |
1033 | 0 | WORD rowBytes; |
1034 | 0 | MacpixMap p; |
1035 | 0 | WORD numColors; |
1036 | | |
1037 | 0 | patType = Read16( io, handle ); |
1038 | | |
1039 | 0 | switch( patType ) { |
1040 | 0 | case 2: |
1041 | 0 | io->seek_proc(handle, 8, SEEK_CUR); |
1042 | 0 | io->seek_proc(handle, 5, SEEK_CUR); |
1043 | 0 | break; |
1044 | 0 | case 1: |
1045 | 0 | { |
1046 | 0 | io->seek_proc(handle, 8, SEEK_CUR); |
1047 | 0 | rowBytes = Read16( io, handle ); |
1048 | 0 | ReadRect( io, handle, &p.Bounds ); |
1049 | 0 | ReadPixmap( io, handle, &p); |
1050 | | |
1051 | 0 | RGBQUAD ct[256]; |
1052 | 0 | ReadColorTable(io, handle, &numColors, ct ); |
1053 | 0 | SkipBits( io, handle, &p.Bounds, rowBytes, p.pixelSize ); |
1054 | 0 | break; |
1055 | 0 | } |
1056 | 0 | default: |
1057 | 0 | throw "Unknown pattern type."; |
1058 | 0 | } |
1059 | | |
1060 | 0 | break; |
1061 | 0 | } |
1062 | 0 | case 0x70: |
1063 | 0 | case 0x71: |
1064 | 0 | case 0x72: |
1065 | 0 | case 0x73: |
1066 | 0 | case 0x74: |
1067 | 0 | case 0x75: |
1068 | 0 | case 0x76: |
1069 | 0 | case 0x77: |
1070 | 0 | { |
1071 | 0 | SkipPolyOrRegion( io, handle ); |
1072 | 0 | break; |
1073 | 0 | } |
1074 | 0 | case 0x90: |
1075 | 0 | case 0x98: |
1076 | 0 | { |
1077 | | // Bitmap/pixmap data clipped by a rectangle. |
1078 | 0 | rowBytes = Read16( io, handle ); // Bytes per row in source when uncompressed. |
1079 | 0 | isRegion = FALSE; |
1080 | | |
1081 | 0 | if ( rowBytes & 0x8000) { |
1082 | 0 | pictType = pixmap; |
1083 | 0 | } else { |
1084 | 0 | pictType = bitmap; |
1085 | 0 | } |
1086 | 0 | done = TRUE; |
1087 | 0 | break; |
1088 | 0 | } |
1089 | 0 | case 0x91: |
1090 | 0 | case 0x99: |
1091 | 0 | { |
1092 | | // Bitmap/pixmap data clipped by a region. |
1093 | 0 | rowBytes = Read16( io, handle ); // Bytes per row in source when uncompressed. |
1094 | 0 | isRegion = TRUE; |
1095 | | |
1096 | 0 | if ( rowBytes & 0x8000) { |
1097 | 0 | pictType = pixmap; |
1098 | 0 | } else { |
1099 | 0 | pictType = bitmap; |
1100 | 0 | } |
1101 | 0 | done = TRUE; |
1102 | 0 | break; |
1103 | 0 | } |
1104 | 0 | case 0x9a: |
1105 | 0 | { |
1106 | | // DirectBitsRect. |
1107 | 0 | Read32( io, handle ); // Skip fake len and fake EOF. |
1108 | 0 | Read16( io, handle ); // bogus row bytes. |
1109 | | |
1110 | | // Read in the PixMap fields. |
1111 | 0 | ReadRect( io, handle, &pixMap.Bounds ); |
1112 | 0 | ReadPixmap( io, handle, &pixMap ); |
1113 | | |
1114 | | // Ignore source & destination rectangle as well as transfer mode. |
1115 | 0 | MacRect dummy; |
1116 | 0 | ReadRect( io, handle, &dummy ); |
1117 | 0 | ReadRect( io, handle, &dummy ); |
1118 | 0 | WORD mode = Read16( io, handle ); |
1119 | | |
1120 | 0 | pictType=op9a; |
1121 | 0 | done = TRUE; |
1122 | 0 | break; |
1123 | 0 | } |
1124 | 0 | case 0xa1: |
1125 | 0 | { |
1126 | | // long comment |
1127 | 0 | WORD type; |
1128 | 0 | WORD len; |
1129 | | |
1130 | 0 | type = Read16( io, handle ); |
1131 | 0 | len = Read16( io, handle); |
1132 | 0 | if (len > 0) { |
1133 | 0 | io->seek_proc(handle, len, SEEK_CUR); |
1134 | 0 | } |
1135 | 0 | break; |
1136 | 0 | } |
1137 | 0 | default: |
1138 | | // No function => skip to next opcode |
1139 | 0 | if (optable[opcode].len == WORD_LEN) { |
1140 | 0 | WORD len = Read16( io, handle ); |
1141 | 0 | io->seek_proc(handle, len, SEEK_CUR); |
1142 | 0 | } else { |
1143 | 0 | io->seek_proc(handle, optable[opcode].len, SEEK_CUR); |
1144 | 0 | } |
1145 | 0 | break; |
1146 | 0 | } |
1147 | 0 | } |
1148 | 0 | else if (opcode == 0xc00) { |
1149 | | // version 2 header (26 bytes) |
1150 | 0 | WORD minorVersion = Read16( io, handle ); // always FFFE (-2) for extended version 2 |
1151 | 0 | Read16( io, handle ); // reserved |
1152 | 0 | hRes = Read32( io, handle ); // original horizontal resolution in pixels/inch |
1153 | 0 | vRes = Read32( io, handle ); // original horizontal resolution in pixels/inch |
1154 | 0 | MacRect dummy; |
1155 | 0 | ReadRect( io, handle, &dummy ); // frame bounds at original resolution |
1156 | 0 | Read32( io, handle ); // reserved |
1157 | 0 | } |
1158 | 0 | else if (opcode == 0x8200) { |
1159 | | // jpeg |
1160 | 0 | long opLen = Read32( io, handle ); |
1161 | 0 | BOOL found = FALSE; |
1162 | 0 | int i = 0; |
1163 | | |
1164 | | // skip to JPEG header. |
1165 | 0 | 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 | 0 | BYTE data[ 2 ]; |
1172 | 0 | if( io->read_proc( data, 2, 1, handle ) ) { |
1173 | 0 | io->seek_proc( handle, -2, SEEK_CUR ); |
1174 | | |
1175 | 0 | if ( data[0] == 0xFF && data[1] == 0xD8 ) { |
1176 | 0 | found = TRUE; |
1177 | 0 | } else { |
1178 | 0 | Read8( io, handle ); |
1179 | 0 | i++; |
1180 | 0 | } |
1181 | 0 | } |
1182 | 0 | } |
1183 | | |
1184 | 0 | if ( found ) { |
1185 | | // Pass the data to the JPEG decoder. |
1186 | 0 | pictType = jpeg; |
1187 | 0 | } else { |
1188 | 0 | throw "PICT file contains unrecognized quicktime data."; |
1189 | 0 | } |
1190 | 0 | done = TRUE; |
1191 | 0 | } |
1192 | 0 | else if (opcode >= 0xa2 && opcode <= 0xaf) { |
1193 | | // reserved |
1194 | 0 | WORD len = Read16( io, handle ); |
1195 | 0 | io->seek_proc(handle, len, SEEK_CUR); |
1196 | 0 | } |
1197 | 0 | else if ((opcode >= 0xb0 && opcode <= 0xcf) || (opcode >= 0x8000 && opcode <= 0x80ff)) { |
1198 | | // just a reserved opcode, no data |
1199 | 0 | } |
1200 | 0 | else if ((opcode >= 0xd0 && opcode <= 0xfe) || opcode >= 8100) { |
1201 | | // reserved |
1202 | 0 | LONG len = Read32( io, handle ); |
1203 | 0 | io->seek_proc(handle, len, SEEK_CUR); |
1204 | 0 | } |
1205 | 0 | else if (opcode >= 0x100 && opcode <= 0x7fff) { |
1206 | | // reserved |
1207 | 0 | io->seek_proc(handle, ((opcode >> 7) & 255), SEEK_CUR); |
1208 | 0 | } |
1209 | 0 | else { |
1210 | 0 | sprintf( outputMessage, "Can't handle opcode %x.\n", opcode ); |
1211 | 0 | throw outputMessage; |
1212 | 0 | } |
1213 | | |
1214 | 0 | if(currentPos == io->tell_proc(handle)) { |
1215 | | // we probaly reached the end of file as we can no longer move forward ... |
1216 | 0 | throw "Invalid PICT file"; |
1217 | 0 | } |
1218 | 0 | } |
1219 | | |
1220 | 0 | switch ( pictType ) { |
1221 | 0 | case op9a: |
1222 | 0 | { |
1223 | 0 | bounds = pixMap.Bounds; |
1224 | 0 | int width = bounds.right - bounds.left; |
1225 | 0 | int height = bounds.bottom - bounds.top; |
1226 | | |
1227 | 0 | if ( pixMap.pixelSize > 8 ) { |
1228 | 0 | dib = FreeImage_Allocate( width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); |
1229 | 0 | } else { |
1230 | 0 | dib = FreeImage_Allocate( width, height, 8); |
1231 | 0 | } |
1232 | 0 | hRes = pixMap.hRes << 16; |
1233 | 0 | vRes = pixMap.vRes << 16; |
1234 | 0 | break; |
1235 | 0 | } |
1236 | | |
1237 | 0 | case jpeg: |
1238 | 0 | { |
1239 | 0 | dib = FreeImage_LoadFromHandle( FIF_JPEG, io, handle ); |
1240 | 0 | break; |
1241 | 0 | } |
1242 | | |
1243 | 0 | case pixmap: |
1244 | 0 | { |
1245 | | // Decode version 2 pixmap |
1246 | 0 | ReadRect( io, handle, &pixMap.Bounds ); |
1247 | 0 | ReadPixmap( io, handle, &pixMap ); |
1248 | | |
1249 | 0 | bounds = pixMap.Bounds; |
1250 | 0 | int width = bounds.right - bounds.left; |
1251 | 0 | int height = bounds.bottom - bounds.top; |
1252 | |
|
1253 | 0 | if ( pixMap.pixelSize > 8 ) { |
1254 | 0 | dib = FreeImage_Allocate( width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); |
1255 | 0 | } else { |
1256 | 0 | dib = FreeImage_Allocate( width, height, 8); |
1257 | 0 | } |
1258 | 0 | hRes = pixMap.hRes << 16; |
1259 | 0 | vRes = pixMap.vRes << 16; |
1260 | 0 | break; |
1261 | 0 | } |
1262 | | |
1263 | 0 | case bitmap: |
1264 | 0 | { |
1265 | | // Decode version 1 bitmap: 1 bpp. |
1266 | 0 | MacRect srcRect; |
1267 | 0 | MacRect dstRect; |
1268 | 0 | WORD width; // Width in pixels |
1269 | 0 | WORD height; // Height in pixels |
1270 | | |
1271 | 0 | ReadRect( io, handle, &bounds ); |
1272 | 0 | ReadRect( io, handle, &srcRect ); |
1273 | 0 | ReadRect( io, handle, &dstRect ); |
1274 | | |
1275 | 0 | width = bounds.right - bounds.left; |
1276 | 0 | height = bounds.bottom - bounds.top; |
1277 | | |
1278 | 0 | dib = FreeImage_Allocate(width, height, 8); |
1279 | 0 | break; |
1280 | 0 | } |
1281 | 0 | } |
1282 | | |
1283 | 0 | if ( dib ) { |
1284 | | // need to convert resolution figures from fixed point, pixels/inch |
1285 | | // to floating point, pixels/meter. |
1286 | 0 | float hres_ppm = hRes * ((float)39.4 / (float)65536.0); |
1287 | 0 | float vres_ppm = vRes * ((float)39.4 / (float)65536.0); |
1288 | | |
1289 | 0 | FreeImage_SetDotsPerMeterX( dib, (LONG)hres_ppm ); |
1290 | 0 | FreeImage_SetDotsPerMeterY( dib, (LONG)vres_ppm ); |
1291 | | |
1292 | 0 | switch( pictType ) { |
1293 | 0 | case op9a: |
1294 | 0 | DecodeOp9a( io, handle, dib, &pixMap ); |
1295 | 0 | break; |
1296 | 0 | case jpeg: |
1297 | | // Already decoded if the embedded format was valid. |
1298 | 0 | break; |
1299 | 0 | case pixmap: |
1300 | 0 | DecodePixmap( io, handle, dib, isRegion, &pixMap, rowBytes ); |
1301 | 0 | break; |
1302 | 0 | case bitmap: |
1303 | 0 | DecodeBitmap( io, handle, dib, isRegion, &bounds, rowBytes ); |
1304 | 0 | break; |
1305 | 0 | default: |
1306 | 0 | throw "invalid pict type"; |
1307 | 0 | } |
1308 | 0 | } |
1309 | | |
1310 | 0 | return dib; |
1311 | 0 | } |
1312 | 0 | catch(const char *message) { |
1313 | 0 | FreeImage_Unload( dib ); |
1314 | 0 | FreeImage_OutputMessageProc(s_format_id, message); |
1315 | 0 | } |
1316 | | |
1317 | 0 | return NULL; |
1318 | 0 | } |
1319 | | |
1320 | | // ========================================================== |
1321 | | // Init |
1322 | | // ========================================================== |
1323 | | |
1324 | | void DLL_CALLCONV |
1325 | 2 | InitPICT(Plugin *plugin, int format_id) { |
1326 | 2 | s_format_id = format_id; |
1327 | | |
1328 | 2 | plugin->format_proc = Format; |
1329 | 2 | plugin->description_proc = Description; |
1330 | 2 | plugin->extension_proc = Extension; |
1331 | 2 | plugin->regexpr_proc = NULL; |
1332 | 2 | plugin->open_proc = NULL; |
1333 | 2 | plugin->close_proc = NULL; |
1334 | 2 | plugin->pagecount_proc = NULL; |
1335 | 2 | plugin->pagecapability_proc = NULL; |
1336 | 2 | plugin->load_proc = Load; |
1337 | 2 | plugin->save_proc = NULL; |
1338 | 2 | plugin->validate_proc = Validate; |
1339 | 2 | plugin->mime_proc = MimeType; |
1340 | 2 | plugin->supports_export_bpp_proc = SupportsExportDepth; |
1341 | 2 | plugin->supports_export_type_proc = SupportsExportType; |
1342 | 2 | plugin->supports_icc_profiles_proc = SupportsICCProfiles; |
1343 | 2 | } |