/src/freeimage-svn/FreeImage/trunk/Source/FreeImage/PSDParser.cpp
Line | Count | Source |
1 | | // ========================================================== |
2 | | // Photoshop Loader |
3 | | // |
4 | | // Design and implementation by |
5 | | // - Hervé Drolon (drolon@infonie.fr) |
6 | | // - Mihail Naydenov (mnaydenov@users.sourceforge.net) |
7 | | // - Garrick Meeker (garrickmeeker@users.sourceforge.net) |
8 | | // |
9 | | // Based on LGPL code created and published by http://sourceforge.net/projects/elynx/ |
10 | | // Format is now publicly documented at: |
11 | | // https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ |
12 | | // |
13 | | // This file is part of FreeImage 3 |
14 | | // |
15 | | // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY |
16 | | // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES |
17 | | // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE |
18 | | // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED |
19 | | // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT |
20 | | // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY |
21 | | // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL |
22 | | // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER |
23 | | // THIS DISCLAIMER. |
24 | | // |
25 | | // Use at your own risk! |
26 | | // ========================================================== |
27 | | |
28 | | #include "FreeImage.h" |
29 | | #include "Utilities.h" |
30 | | #include "PSDParser.h" |
31 | | |
32 | | #include "../Metadata/FreeImageTag.h" |
33 | | |
34 | | // -------------------------------------------------------------------------- |
35 | | |
36 | | // PSD signature (= '8BPS') |
37 | 0 | #define PSD_SIGNATURE 0x38425053 |
38 | | // Image resource block signature (= '8BIM') |
39 | 0 | #define PSD_RESOURCE 0x3842494D |
40 | | |
41 | | // PSD color modes |
42 | 0 | #define PSDP_BITMAP 0 |
43 | 0 | #define PSDP_GRAYSCALE 1 |
44 | 0 | #define PSDP_INDEXED 2 |
45 | 0 | #define PSDP_RGB 3 |
46 | 0 | #define PSDP_CMYK 4 |
47 | 0 | #define PSDP_MULTICHANNEL 7 |
48 | 0 | #define PSDP_DUOTONE 8 |
49 | 0 | #define PSDP_LAB 9 |
50 | | |
51 | | // PSD compression schemes |
52 | 0 | #define PSDP_COMPRESSION_NONE 0 //! Raw data |
53 | 0 | #define PSDP_COMPRESSION_RLE 1 //! RLE compression (same as TIFF packed bits) |
54 | 0 | #define PSDP_COMPRESSION_ZIP 2 //! ZIP compression without prediction |
55 | 0 | #define PSDP_COMPRESSION_ZIP_PREDICTION 3 //! ZIP compression with prediction |
56 | | |
57 | | /** |
58 | | PSD image resources |
59 | | */ |
60 | | enum { |
61 | | //! Obsolete - Photoshop 2.0 |
62 | | PSDP_RES_RESOLUTION_INFO_V2 = 1000, |
63 | | //! ResolutionInfo structure |
64 | | PSDP_RES_RESOLUTION_INFO = 1005, |
65 | | //! DisplayInfo structure |
66 | | PSDP_RES_DISPLAY_INFO = 1007, |
67 | | //! IPTC-NAA record |
68 | | PSDP_RES_IPTC_NAA = 1028, |
69 | | //! (Photoshop 4.0) Thumbnail resource for Photoshop 4.0 only |
70 | | PSDP_RES_THUMBNAIL_PS4 = 1033, |
71 | | //! (Photoshop 4.0) Copyright flag |
72 | | PSDP_RES_COPYRIGHT = 1034, |
73 | | //! (Photoshop 5.0) Thumbnail resource (supersedes resource 1033) |
74 | | PSDP_RES_THUMBNAIL = 1036, |
75 | | //! (Photoshop 5.0) Global Angle |
76 | | PSDP_RES_GLOBAL_ANGLE = 1037, |
77 | | //! ICC profile |
78 | | PSDP_RES_ICC_PROFILE = 1039, |
79 | | //! (Photoshop 6.0) Indexed Color Table Count; 2 bytes for the number of colors in table that are actually defined |
80 | | PSDP_RES_INDEXED_COLORS = 1046, |
81 | | //! (Photoshop 6.0) Transparency Index. 2 bytes for the index of transparent color, if any. |
82 | | PSDP_RES_TRANSPARENCY_INDEX = 1047, |
83 | | //! (Photoshop 7.0) EXIF data 1 |
84 | | PSDP_RES_EXIF1 = 1058, |
85 | | //! (Photoshop 7.0) EXIF data 3 |
86 | | PSDP_RES_EXIF3 = 1059, |
87 | | //! (Photoshop 7.0) XMP metadata |
88 | | PSDP_RES_XMP = 1060, |
89 | | //! (Photoshop CS3) DisplayInfo structure |
90 | | PSDP_RES_DISPLAY_INFO_FLT = 1077, |
91 | | }; |
92 | | |
93 | 0 | #define SAFE_DELETE_ARRAY(_p_) { if (NULL != (_p_)) { delete [] (_p_); (_p_) = NULL; } } |
94 | | |
95 | | // -------------------------------------------------------------------------- |
96 | | |
97 | | template <int N> |
98 | | class PSDGetValue { |
99 | | public: |
100 | | static inline int get(const BYTE * iprBuffer) { return -1; } // error |
101 | | }; |
102 | | |
103 | | template <> |
104 | | class PSDGetValue<1> { |
105 | | public: |
106 | 0 | static inline BYTE get(const BYTE * iprBuffer) { return iprBuffer[0]; } |
107 | | }; |
108 | | |
109 | | template <> |
110 | | class PSDGetValue<2> { |
111 | | public: |
112 | 0 | static inline WORD get(const BYTE * iprBuffer) { |
113 | 0 | WORD v = ((const WORD*)iprBuffer)[0]; |
114 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
115 | 0 | SwapShort(&v); |
116 | 0 | #endif |
117 | 0 | return (int)v; |
118 | 0 | } |
119 | | }; |
120 | | |
121 | | template <> |
122 | | class PSDGetValue<4> { |
123 | | public: |
124 | 0 | static inline DWORD get(const BYTE * iprBuffer) { |
125 | 0 | DWORD v = ((const DWORD*)iprBuffer)[0]; |
126 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
127 | 0 | SwapLong(&v); |
128 | 0 | #endif |
129 | 0 | return v; |
130 | 0 | } |
131 | | }; |
132 | | |
133 | | template <> |
134 | | class PSDGetValue<8> { |
135 | | public: |
136 | 0 | static inline UINT64 get(const BYTE * iprBuffer) { |
137 | 0 | UINT64 v = ((const UINT64*)iprBuffer)[0]; |
138 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
139 | 0 | SwapInt64(&v); |
140 | 0 | #endif |
141 | 0 | return v; |
142 | 0 | } |
143 | | }; |
144 | | |
145 | 0 | #define psdGetValue(PTR, SIZE) PSDGetValue<SIZE>::get((PTR)) |
146 | 0 | #define psdGetLongValue(PTR, SIZE) PSDGetValue<SIZE>::get((PTR)) |
147 | | |
148 | | // -------------------------------------------------------------------------- |
149 | | |
150 | | static UINT64 |
151 | 0 | psdReadSize(FreeImageIO *io, fi_handle handle, const psdHeaderInfo& header) { |
152 | 0 | if(header._Version == 1) { |
153 | 0 | BYTE Length[4]; |
154 | 0 | io->read_proc(Length, sizeof(Length), 1, handle); |
155 | 0 | return psdGetLongValue(Length, sizeof(Length)); |
156 | 0 | } else { |
157 | 0 | BYTE Length[8]; |
158 | 0 | io->read_proc(Length, sizeof(Length), 1, handle); |
159 | 0 | return psdGetLongValue(Length, sizeof(Length)); |
160 | 0 | } |
161 | 0 | } |
162 | | |
163 | | // -------------------------------------------------------------------------- |
164 | | |
165 | | template <int N> |
166 | | class PSDSetValue { |
167 | | public: |
168 | | static inline void set(BYTE * iprBuffer, int v) {} // error |
169 | | }; |
170 | | |
171 | | template <> |
172 | | class PSDSetValue<1> { |
173 | | public: |
174 | 0 | static inline void set(BYTE * iprBuffer, BYTE v) { iprBuffer[0] = v; } |
175 | | }; |
176 | | |
177 | | template <> |
178 | | class PSDSetValue<2> { |
179 | | public: |
180 | 0 | static inline void set(BYTE * iprBuffer, WORD v) { |
181 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
182 | 0 | SwapShort(&v); |
183 | 0 | #endif |
184 | 0 | ((WORD*)iprBuffer)[0] = v; |
185 | 0 | } |
186 | | }; |
187 | | |
188 | | template <> |
189 | | class PSDSetValue<4> { |
190 | | public: |
191 | 0 | static inline void set(const BYTE * iprBuffer, DWORD v) { |
192 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
193 | 0 | SwapLong(&v); |
194 | 0 | #endif |
195 | 0 | ((DWORD*)iprBuffer)[0] = v; |
196 | 0 | } |
197 | | }; |
198 | | |
199 | | template <> |
200 | | class PSDSetValue<8> { |
201 | | public: |
202 | 0 | static inline void set(const BYTE * iprBuffer, UINT64 v) { |
203 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
204 | 0 | SwapInt64(&v); |
205 | 0 | #endif |
206 | 0 | ((UINT64*)iprBuffer)[0] = v; |
207 | 0 | } |
208 | | }; |
209 | | |
210 | 0 | #define psdSetValue(PTR, SIZE, V) PSDSetValue<SIZE>::set((PTR), (V)) |
211 | 0 | #define psdSetLongValue(PTR, SIZE, V) PSDSetValue<SIZE>::set((PTR), (V)) |
212 | | |
213 | | // -------------------------------------------------------------------------- |
214 | | |
215 | | static inline bool |
216 | 0 | psdWriteSize(FreeImageIO *io, fi_handle handle, const psdHeaderInfo& header, UINT64 v) { |
217 | 0 | if(header._Version == 1) { |
218 | 0 | BYTE Length[4]; |
219 | 0 | psdSetLongValue(Length, sizeof(Length), (DWORD)v); |
220 | 0 | return (io->write_proc(Length, sizeof(Length), 1, handle) == 1); |
221 | 0 | } else { |
222 | 0 | BYTE Length[8]; |
223 | 0 | psdSetLongValue(Length, sizeof(Length), v); |
224 | 0 | return (io->write_proc(Length, sizeof(Length), 1, handle) == 1); |
225 | 0 | } |
226 | 0 | } |
227 | | |
228 | | /** |
229 | | Return Exif metadata as a binary read-only buffer. |
230 | | The buffer is owned by the function and MUST NOT be freed by the caller. |
231 | | */ |
232 | | static BOOL |
233 | 0 | psd_write_exif_profile_raw(FIBITMAP *dib, BYTE **profile, unsigned *profile_size) { |
234 | | // marker identifying string for Exif = "Exif\0\0" |
235 | | // used by JPEG not PSD |
236 | 0 | BYTE exif_signature[6] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 }; |
237 | |
|
238 | 0 | FITAG *tag_exif = NULL; |
239 | 0 | FreeImage_GetMetadata(FIMD_EXIF_RAW, dib, g_TagLib_ExifRawFieldName, &tag_exif); |
240 | |
|
241 | 0 | if(tag_exif) { |
242 | 0 | const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag_exif); |
243 | | |
244 | | // verify the identifying string |
245 | 0 | if(memcmp(exif_signature, tag_value, sizeof(exif_signature)) != 0) { |
246 | | // not an Exif profile |
247 | 0 | return FALSE; |
248 | 0 | } |
249 | | |
250 | 0 | *profile = (BYTE*)tag_value + sizeof(exif_signature); |
251 | 0 | *profile_size = (unsigned)FreeImage_GetTagLength(tag_exif) - sizeof(exif_signature); |
252 | |
|
253 | 0 | return TRUE; |
254 | 0 | } |
255 | | |
256 | 0 | return FALSE; |
257 | 0 | } |
258 | | |
259 | | static BOOL |
260 | 0 | psd_set_xmp_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) { |
261 | | // create a tag |
262 | 0 | FITAG *tag = FreeImage_CreateTag(); |
263 | 0 | if (tag) { |
264 | 0 | FreeImage_SetTagID(tag, PSDP_RES_XMP); |
265 | 0 | FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName); |
266 | 0 | FreeImage_SetTagLength(tag, (DWORD)datalen); |
267 | 0 | FreeImage_SetTagCount(tag, (DWORD)datalen); |
268 | 0 | FreeImage_SetTagType(tag, FIDT_ASCII); |
269 | 0 | FreeImage_SetTagValue(tag, dataptr); |
270 | | |
271 | | // store the tag |
272 | 0 | FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag); |
273 | | |
274 | | // destroy the tag |
275 | 0 | FreeImage_DeleteTag(tag); |
276 | 0 | } |
277 | |
|
278 | 0 | return TRUE; |
279 | 0 | } |
280 | | |
281 | | /** |
282 | | Return XMP metadata as a binary read-only buffer. |
283 | | The buffer is owned by the function and MUST NOT be freed by the caller. |
284 | | */ |
285 | | static BOOL |
286 | 0 | psd_get_xmp_profile(FIBITMAP *dib, BYTE **profile, unsigned *profile_size) { |
287 | 0 | FITAG *tag_xmp = NULL; |
288 | 0 | FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag_xmp); |
289 | |
|
290 | 0 | if(tag_xmp && (NULL != FreeImage_GetTagValue(tag_xmp))) { |
291 | |
|
292 | 0 | *profile = (BYTE*)FreeImage_GetTagValue(tag_xmp); |
293 | 0 | *profile_size = (unsigned)FreeImage_GetTagLength(tag_xmp); |
294 | |
|
295 | 0 | return TRUE; |
296 | 0 | } |
297 | | |
298 | 0 | return FALSE; |
299 | 0 | } |
300 | | |
301 | | // -------------------------------------------------------------------------- |
302 | | |
303 | 0 | psdHeaderInfo::psdHeaderInfo() : _Version(-1), _Channels(-1), _Height(-1), _Width(-1), _BitsPerChannel(-1), _ColourMode(-1) { |
304 | 0 | } |
305 | | |
306 | 0 | psdHeaderInfo::~psdHeaderInfo() { |
307 | 0 | } |
308 | | |
309 | 0 | bool psdHeaderInfo::Read(FreeImageIO *io, fi_handle handle) { |
310 | 0 | psdHeader header; |
311 | |
|
312 | 0 | const int n = (int)io->read_proc(&header, sizeof(header), 1, handle); |
313 | 0 | if(!n) { |
314 | 0 | return false; |
315 | 0 | } |
316 | | |
317 | | // check the signature |
318 | 0 | int nSignature = psdGetValue(header.Signature, sizeof(header.Signature)); |
319 | 0 | if (PSD_SIGNATURE == nSignature) { |
320 | | // check the version |
321 | 0 | short nVersion = (short)psdGetValue( header.Version, sizeof(header.Version) ); |
322 | 0 | if (1 == nVersion || 2 == nVersion) { |
323 | 0 | _Version = nVersion; |
324 | | // header.Reserved must be zero |
325 | 0 | BYTE psd_reserved[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
326 | 0 | if(memcmp(header.Reserved, psd_reserved, 6) != 0) { |
327 | 0 | FreeImage_OutputMessageProc(FIF_PSD, "Warning: file header reserved member is not equal to zero"); |
328 | 0 | } |
329 | | // read the header |
330 | 0 | _Channels = (short)psdGetValue( header.Channels, sizeof(header.Channels) ); |
331 | 0 | _Height = psdGetValue( header.Rows, sizeof(header.Rows) ); |
332 | 0 | _Width = psdGetValue( header.Columns, sizeof(header.Columns) ); |
333 | 0 | _BitsPerChannel = (short)psdGetValue( header.Depth, sizeof(header.Depth) ); |
334 | 0 | _ColourMode = (short)psdGetValue( header.Mode, sizeof(header.Mode) ); |
335 | 0 | if (_Version == 1 && (_Width > 30000 || _Height > 30000)) { |
336 | 0 | return false; |
337 | 0 | } |
338 | | |
339 | 0 | return true; |
340 | 0 | } |
341 | 0 | } |
342 | | |
343 | 0 | return false; |
344 | 0 | } |
345 | | |
346 | 0 | bool psdHeaderInfo::Write(FreeImageIO *io, fi_handle handle) { |
347 | 0 | psdHeader header; |
348 | |
|
349 | 0 | psdSetValue(header.Signature, sizeof(header.Signature), PSD_SIGNATURE); |
350 | 0 | psdSetValue(header.Version, sizeof(header.Version), _Version); |
351 | 0 | memset(header.Reserved, 0, sizeof(header.Reserved)); |
352 | 0 | psdSetValue(header.Channels, sizeof(header.Channels), _Channels); |
353 | 0 | psdSetValue(header.Rows, sizeof(header.Rows), _Height); |
354 | 0 | psdSetValue(header.Columns, sizeof(header.Columns), _Width); |
355 | 0 | psdSetValue(header.Depth, sizeof(header.Depth), _BitsPerChannel); |
356 | 0 | psdSetValue(header.Mode, sizeof(header.Mode), _ColourMode); |
357 | 0 | return (io->write_proc(&header, sizeof(header), 1, handle) == 1); |
358 | 0 | } |
359 | | |
360 | | // -------------------------------------------------------------------------- |
361 | | |
362 | 0 | psdColourModeData::psdColourModeData() : _Length(-1), _plColourData(NULL) { |
363 | 0 | } |
364 | | |
365 | 0 | psdColourModeData::~psdColourModeData() { |
366 | 0 | SAFE_DELETE_ARRAY(_plColourData); |
367 | 0 | } |
368 | | |
369 | 0 | bool psdColourModeData::Read(FreeImageIO *io, fi_handle handle) { |
370 | 0 | if (0 < _Length) { |
371 | 0 | SAFE_DELETE_ARRAY(_plColourData); |
372 | 0 | } |
373 | |
|
374 | 0 | BYTE Length[4]; |
375 | 0 | io->read_proc(Length, sizeof(Length), 1, handle); |
376 | |
|
377 | 0 | _Length = psdGetValue( Length, sizeof(_Length) ); |
378 | 0 | if (0 < _Length) { |
379 | 0 | _plColourData = new BYTE[_Length]; |
380 | 0 | io->read_proc(_plColourData, _Length, 1, handle); |
381 | 0 | } |
382 | |
|
383 | 0 | return true; |
384 | 0 | } |
385 | | |
386 | 0 | bool psdColourModeData::Write(FreeImageIO *io, fi_handle handle) { |
387 | 0 | if(io->write_proc(&_Length, sizeof(_Length), 1, handle) != 1) { |
388 | 0 | return false; |
389 | 0 | } |
390 | 0 | if(0 < _Length) { |
391 | 0 | if(io->write_proc(_plColourData, _Length, 1, handle) != 1) { |
392 | 0 | return false; |
393 | 0 | } |
394 | 0 | } |
395 | 0 | return true; |
396 | 0 | } |
397 | | |
398 | 0 | bool psdColourModeData::FillPalette(FIBITMAP *dib) { |
399 | 0 | RGBQUAD *pal = FreeImage_GetPalette(dib); |
400 | 0 | if(pal) { |
401 | 0 | for (int i = 0; i < 256; i++) { |
402 | 0 | pal[i].rgbRed = _plColourData[i + 0*256]; |
403 | 0 | pal[i].rgbGreen = _plColourData[i + 1*256]; |
404 | 0 | pal[i].rgbBlue = _plColourData[i + 2*256]; |
405 | 0 | } |
406 | 0 | return true; |
407 | 0 | } |
408 | 0 | return false; |
409 | 0 | } |
410 | | |
411 | | // -------------------------------------------------------------------------- |
412 | | |
413 | 0 | psdImageResource::psdImageResource() : _plName (0) { |
414 | 0 | Reset(); |
415 | 0 | } |
416 | | |
417 | 0 | psdImageResource::~psdImageResource() { |
418 | 0 | SAFE_DELETE_ARRAY(_plName); |
419 | 0 | } |
420 | | |
421 | 0 | void psdImageResource::Reset() { |
422 | 0 | _Length = -1; |
423 | 0 | memset( _OSType, '\0', sizeof(_OSType) ); |
424 | 0 | _ID = -1; |
425 | 0 | SAFE_DELETE_ARRAY(_plName); |
426 | 0 | _Size = -1; |
427 | 0 | } |
428 | | |
429 | 0 | bool psdImageResource::Write(FreeImageIO *io, fi_handle handle, int ID, int Size) { |
430 | 0 | BYTE ShortValue[2], IntValue[4]; |
431 | |
|
432 | 0 | _ID = ID; |
433 | 0 | _Size = Size; |
434 | 0 | psdSetValue((BYTE*)_OSType, sizeof(_OSType), PSD_RESOURCE); |
435 | 0 | if(io->write_proc(_OSType, sizeof(_OSType), 1, handle) != 1) { |
436 | 0 | return false; |
437 | 0 | } |
438 | 0 | psdSetValue(ShortValue, sizeof(ShortValue), _ID); |
439 | 0 | if(io->write_proc(ShortValue, sizeof(ShortValue), 1, handle) != 1) { |
440 | 0 | return false; |
441 | 0 | } |
442 | 0 | psdSetValue(ShortValue, sizeof(ShortValue), 0); |
443 | 0 | if(io->write_proc(ShortValue, sizeof(ShortValue), 1, handle) != 1) { |
444 | 0 | return false; |
445 | 0 | } |
446 | 0 | psdSetValue(IntValue, sizeof(IntValue), _Size); |
447 | 0 | if(io->write_proc(IntValue, sizeof(IntValue), 1, handle) != 1) { |
448 | 0 | return false; |
449 | 0 | } |
450 | 0 | return true; |
451 | 0 | } |
452 | | |
453 | | // -------------------------------------------------------------------------- |
454 | | |
455 | 0 | psdResolutionInfo::psdResolutionInfo() : _widthUnit(-1), _heightUnit(-1), _hRes(-1), _vRes(-1), _hResUnit(-1), _vResUnit(-1) { |
456 | 0 | } |
457 | | |
458 | 0 | psdResolutionInfo::~psdResolutionInfo() { |
459 | 0 | } |
460 | | |
461 | 0 | int psdResolutionInfo::Read(FreeImageIO *io, fi_handle handle) { |
462 | 0 | BYTE IntValue[4], ShortValue[2]; |
463 | 0 | int nBytes=0, n; |
464 | | |
465 | | // Horizontal resolution in pixels per inch. |
466 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
467 | 0 | nBytes += n * sizeof(ShortValue); |
468 | 0 | _hRes = (short)psdGetValue(ShortValue, sizeof(_hRes) ); |
469 | | |
470 | | // 1=display horizontal resolution in pixels per inch; 2=display horizontal resolution in pixels per cm. |
471 | 0 | n = (int)io->read_proc(IntValue, sizeof(IntValue), 1, handle); |
472 | 0 | nBytes += n * sizeof(IntValue); |
473 | 0 | _hResUnit = psdGetValue(IntValue, sizeof(_hResUnit) ); |
474 | | |
475 | | // Display width as 1=inches; 2=cm; 3=points; 4=picas; 5=columns. |
476 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
477 | 0 | nBytes += n * sizeof(ShortValue); |
478 | 0 | _widthUnit = (short)psdGetValue(ShortValue, sizeof(_widthUnit) ); |
479 | | |
480 | | // Vertical resolution in pixels per inch. |
481 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
482 | 0 | nBytes += n * sizeof(ShortValue); |
483 | 0 | _vRes = (short)psdGetValue(ShortValue, sizeof(_vRes) ); |
484 | | |
485 | | // 1=display vertical resolution in pixels per inch; 2=display vertical resolution in pixels per cm. |
486 | 0 | n = (int)io->read_proc(IntValue, sizeof(IntValue), 1, handle); |
487 | 0 | nBytes += n * sizeof(IntValue); |
488 | 0 | _vResUnit = psdGetValue(IntValue, sizeof(_vResUnit) ); |
489 | | |
490 | | // Display height as 1=inches; 2=cm; 3=points; 4=picas; 5=columns. |
491 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
492 | 0 | nBytes += n * sizeof(ShortValue); |
493 | 0 | _heightUnit = (short)psdGetValue(ShortValue, sizeof(_heightUnit) ); |
494 | |
|
495 | 0 | return nBytes; |
496 | 0 | } |
497 | | |
498 | 0 | bool psdResolutionInfo::Write(FreeImageIO *io, fi_handle handle) { |
499 | 0 | BYTE IntValue[4], ShortValue[2]; |
500 | |
|
501 | 0 | if (!psdImageResource().Write(io, handle, PSDP_RES_RESOLUTION_INFO, 16)) { |
502 | 0 | return false; |
503 | 0 | } |
504 | | |
505 | | // Horizontal resolution in pixels per inch. |
506 | 0 | psdSetValue(ShortValue, sizeof(ShortValue), _hRes); |
507 | 0 | if(io->write_proc(ShortValue, sizeof(ShortValue), 1, handle) != 1) { |
508 | 0 | return false; |
509 | 0 | } |
510 | | |
511 | | // 1=display horizontal resolution in pixels per inch; 2=display horizontal resolution in pixels per cm. |
512 | 0 | psdSetValue(IntValue, sizeof(IntValue), _hResUnit); |
513 | 0 | if(io->write_proc(IntValue, sizeof(IntValue), 1, handle) != 1) { |
514 | 0 | return false; |
515 | 0 | } |
516 | | |
517 | | // Display width as 1=inches; 2=cm; 3=points; 4=picas; 5=columns. |
518 | 0 | psdSetValue(ShortValue, sizeof(ShortValue), _widthUnit); |
519 | 0 | if(io->write_proc(ShortValue, sizeof(ShortValue), 1, handle) != 1) { |
520 | 0 | return false; |
521 | 0 | } |
522 | | |
523 | | // Vertical resolution in pixels per inch. |
524 | 0 | psdSetValue(ShortValue, sizeof(ShortValue), _vRes); |
525 | 0 | if(io->write_proc(ShortValue, sizeof(ShortValue), 1, handle) != 1) { |
526 | 0 | return false; |
527 | 0 | } |
528 | | |
529 | | // 1=display vertical resolution in pixels per inch; 2=display vertical resolution in pixels per cm. |
530 | 0 | psdSetValue(IntValue, sizeof(IntValue), _vResUnit); |
531 | 0 | if(io->write_proc(IntValue, sizeof(IntValue), 1, handle) != 1) { |
532 | 0 | return false; |
533 | 0 | } |
534 | | |
535 | | // Display height as 1=inches; 2=cm; 3=points; 4=picas; 5=columns. |
536 | 0 | psdSetValue(ShortValue, sizeof(ShortValue), _heightUnit); |
537 | 0 | if(io->write_proc(ShortValue, sizeof(ShortValue), 1, handle) != 1) { |
538 | 0 | return false; |
539 | 0 | } |
540 | | |
541 | 0 | return true; |
542 | 0 | } |
543 | | |
544 | 0 | void psdResolutionInfo::GetResolutionInfo(unsigned &res_x, unsigned &res_y) { |
545 | 0 | if(_hResUnit == 1) { |
546 | | // convert pixels / inch to pixel / m |
547 | 0 | res_x = (unsigned) (_hRes / 0.0254000 + 0.5); |
548 | 0 | } else if(_hResUnit == 2) { |
549 | | // convert pixels / cm to pixel / m |
550 | 0 | res_x = (unsigned) (_hRes * 100.0 + 0.5); |
551 | 0 | } |
552 | |
|
553 | 0 | if(_vResUnit == 1) { |
554 | | // convert pixels / inch to pixel / m |
555 | 0 | res_y = (unsigned) (_vRes / 0.0254000 + 0.5); |
556 | 0 | } else if(_vResUnit == 2) { |
557 | | // convert pixels / cm to pixel / m |
558 | 0 | res_y = (unsigned) (_vRes * 100.0 + 0.5); |
559 | 0 | } |
560 | 0 | } |
561 | | |
562 | | // -------------------------------------------------------------------------- |
563 | | |
564 | 0 | psdResolutionInfo_v2::psdResolutionInfo_v2() { |
565 | 0 | _Channels = _Rows = _Columns = _Depth = _Mode = -1; |
566 | 0 | } |
567 | | |
568 | 0 | psdResolutionInfo_v2::~psdResolutionInfo_v2() { |
569 | 0 | } |
570 | | |
571 | 0 | int psdResolutionInfo_v2::Read(FreeImageIO *io, fi_handle handle) { |
572 | 0 | BYTE ShortValue[2]; |
573 | 0 | int nBytes=0, n; |
574 | |
|
575 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
576 | 0 | nBytes += n * sizeof(ShortValue); |
577 | 0 | _Channels = (short)psdGetValue(ShortValue, sizeof(_Channels) ); |
578 | |
|
579 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
580 | 0 | nBytes += n * sizeof(ShortValue); |
581 | 0 | _Rows = (short)psdGetValue(ShortValue, sizeof(_Rows) ); |
582 | |
|
583 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
584 | 0 | nBytes += n * sizeof(ShortValue); |
585 | 0 | _Columns = (short)psdGetValue(ShortValue, sizeof(_Columns) ); |
586 | |
|
587 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
588 | 0 | nBytes += n * sizeof(ShortValue); |
589 | 0 | _Depth = (short)psdGetValue(ShortValue, sizeof(_Depth) ); |
590 | |
|
591 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
592 | 0 | nBytes += n * sizeof(ShortValue); |
593 | 0 | _Mode = (short)psdGetValue(ShortValue, sizeof(_Mode) ); |
594 | |
|
595 | 0 | return nBytes; |
596 | 0 | } |
597 | | |
598 | 0 | bool psdResolutionInfo_v2::Write(FreeImageIO *io, fi_handle handle) { |
599 | 0 | BYTE ShortValue[2]; |
600 | |
|
601 | 0 | if(!psdImageResource().Write(io, handle, PSDP_RES_RESOLUTION_INFO_V2, 10)) |
602 | 0 | return false; |
603 | | |
604 | 0 | psdSetValue(ShortValue, sizeof(ShortValue), _Channels); |
605 | 0 | if(io->write_proc(ShortValue, sizeof(ShortValue), 1, handle) != 1) { |
606 | 0 | return false; |
607 | 0 | } |
608 | 0 | psdSetValue(ShortValue, sizeof(ShortValue), _Rows); |
609 | 0 | if(io->write_proc(ShortValue, sizeof(ShortValue), 1, handle) != 1) { |
610 | 0 | return false; |
611 | 0 | } |
612 | 0 | psdSetValue(ShortValue, sizeof(ShortValue), _Columns); |
613 | 0 | if(io->write_proc(ShortValue, sizeof(ShortValue), 1, handle) != 1) { |
614 | 0 | return false; |
615 | 0 | } |
616 | 0 | psdSetValue(ShortValue, sizeof(ShortValue), _Depth); |
617 | 0 | if(io->write_proc(ShortValue, sizeof(ShortValue), 1, handle) != 1) { |
618 | 0 | return false; |
619 | 0 | } |
620 | 0 | psdSetValue(ShortValue, sizeof(ShortValue), _Mode); |
621 | 0 | if(io->write_proc(ShortValue, sizeof(ShortValue), 1, handle) != 1) { |
622 | 0 | return false; |
623 | 0 | } |
624 | | |
625 | 0 | return true; |
626 | 0 | } |
627 | | |
628 | | // -------------------------------------------------------------------------- |
629 | | |
630 | 0 | psdDisplayInfo::psdDisplayInfo() { |
631 | 0 | _Opacity = _ColourSpace = -1; |
632 | 0 | for (unsigned n = 0; n < 4; ++n) { |
633 | 0 | _Colour[n] = 0; |
634 | 0 | } |
635 | 0 | _Kind = 0; |
636 | 0 | _padding = '0'; |
637 | 0 | } |
638 | | |
639 | 0 | psdDisplayInfo::~psdDisplayInfo() { |
640 | 0 | } |
641 | | |
642 | 0 | int psdDisplayInfo::Read(FreeImageIO *io, fi_handle handle) { |
643 | 0 | BYTE ShortValue[2]; |
644 | 0 | int nBytes=0, n; |
645 | |
|
646 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
647 | 0 | nBytes += n * sizeof(ShortValue); |
648 | 0 | _ColourSpace = (short)psdGetValue(ShortValue, sizeof(_ColourSpace) ); |
649 | |
|
650 | 0 | for (unsigned i = 0; i < 4; ++i) { |
651 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
652 | 0 | nBytes += n * sizeof(ShortValue); |
653 | 0 | _Colour[i] = (short)psdGetValue(ShortValue, sizeof(_Colour[i]) ); |
654 | 0 | } |
655 | |
|
656 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
657 | 0 | nBytes += n * sizeof(ShortValue); |
658 | 0 | _Opacity = (short)psdGetValue(ShortValue, sizeof(_Opacity) ); |
659 | 0 | if((_Opacity < 0) || (_Opacity > 100)) { |
660 | 0 | throw "Invalid DisplayInfo::Opacity value"; |
661 | 0 | } |
662 | | |
663 | 0 | BYTE c[1]; |
664 | 0 | n = (int)io->read_proc(c, sizeof(c), 1, handle); |
665 | 0 | nBytes += n * sizeof(c); |
666 | 0 | _Kind = (BYTE)psdGetValue(c, sizeof(c)); |
667 | |
|
668 | 0 | n = (int)io->read_proc(c, sizeof(c), 1, handle); |
669 | 0 | nBytes += n * sizeof(c); |
670 | |
|
671 | 0 | _padding = (BYTE)psdGetValue(c, sizeof(c)); |
672 | 0 | if(_padding != 0) { |
673 | 0 | throw "Invalid DisplayInfo::Padding value"; |
674 | 0 | } |
675 | | |
676 | 0 | return nBytes; |
677 | 0 | } |
678 | | |
679 | 0 | bool psdDisplayInfo::Write(FreeImageIO *io, fi_handle handle) { |
680 | 0 | BYTE ShortValue[2]; |
681 | |
|
682 | 0 | if(!psdImageResource().Write(io, handle, PSDP_RES_DISPLAY_INFO, 14)) |
683 | 0 | return false; |
684 | | |
685 | 0 | psdSetValue(ShortValue, sizeof(ShortValue), _ColourSpace); |
686 | 0 | if(io->write_proc(ShortValue, sizeof(ShortValue), 1, handle) != 1) { |
687 | 0 | return false; |
688 | 0 | } |
689 | 0 | for (unsigned i = 0; i < 4; ++i) { |
690 | 0 | psdSetValue(ShortValue, sizeof(ShortValue), _Colour[i]); |
691 | 0 | if(io->write_proc(ShortValue, sizeof(ShortValue), 1, handle) != 1) { |
692 | 0 | return false; |
693 | 0 | } |
694 | 0 | } |
695 | 0 | psdSetValue(ShortValue, sizeof(ShortValue), _Opacity); |
696 | 0 | if(io->write_proc(ShortValue, sizeof(ShortValue), 1, handle) != 1) { |
697 | 0 | return false; |
698 | 0 | } |
699 | 0 | BYTE c[1]; |
700 | 0 | psdSetValue(c, sizeof(c), _Kind); |
701 | 0 | if(io->write_proc(c, sizeof(c), 1, handle) != 1) { |
702 | 0 | return false; |
703 | 0 | } |
704 | 0 | psdSetValue(c, sizeof(c), 0); |
705 | 0 | if(io->write_proc(c, sizeof(c), 1, handle) != 1) { |
706 | 0 | return false; |
707 | 0 | } |
708 | | |
709 | 0 | return true; |
710 | 0 | } |
711 | | |
712 | | // -------------------------------------------------------------------------- |
713 | | |
714 | | psdThumbnail::psdThumbnail() : |
715 | 0 | _Format(-1), _Width(-1), _Height(-1), _WidthBytes(-1), _Size(-1), _CompressedSize(-1), _BitPerPixel(-1), _Planes(-1), _dib(NULL), _owned(true) { |
716 | 0 | } |
717 | | |
718 | 0 | psdThumbnail::~psdThumbnail() { |
719 | 0 | if (_owned) FreeImage_Unload(_dib); |
720 | 0 | } |
721 | | |
722 | | void |
723 | 0 | psdThumbnail::Init() { |
724 | 0 | if (_dib != NULL) { |
725 | 0 | _Format = 1; |
726 | 0 | _Width = (int)FreeImage_GetWidth(_dib); |
727 | 0 | _Height = (int)FreeImage_GetHeight(_dib); |
728 | 0 | _BitPerPixel = 24; |
729 | 0 | _Planes = 1; |
730 | 0 | _WidthBytes = (_Width * _BitPerPixel + 31) / 32 * 4; |
731 | 0 | _Size = _WidthBytes * _Height * _Planes; |
732 | 0 | _CompressedSize = _Size; |
733 | 0 | } |
734 | 0 | } |
735 | | |
736 | 0 | int psdThumbnail::Read(FreeImageIO *io, fi_handle handle, int iResourceSize, bool isBGR) { |
737 | 0 | BYTE ShortValue[2], IntValue[4]; |
738 | 0 | int nBytes=0, n; |
739 | | |
740 | | // remove the header size (28 bytes) from the total data size |
741 | 0 | int iTotalData = iResourceSize - 28; |
742 | |
|
743 | 0 | const long block_end = io->tell_proc(handle) + iTotalData; |
744 | |
|
745 | 0 | n = (int)io->read_proc(IntValue, sizeof(IntValue), 1, handle); |
746 | 0 | nBytes += n * sizeof(IntValue); |
747 | 0 | _Format = psdGetValue(IntValue, sizeof(_Format) ); |
748 | |
|
749 | 0 | n = (int)io->read_proc(IntValue, sizeof(IntValue), 1, handle); |
750 | 0 | nBytes += n * sizeof(IntValue); |
751 | 0 | _Width = psdGetValue(IntValue, sizeof(_Width) ); |
752 | |
|
753 | 0 | n = (int)io->read_proc(IntValue, sizeof(IntValue), 1, handle); |
754 | 0 | nBytes += n * sizeof(IntValue); |
755 | 0 | _Height = psdGetValue(IntValue, sizeof(_Height) ); |
756 | |
|
757 | 0 | n = (int)io->read_proc(IntValue, sizeof(IntValue), 1, handle); |
758 | 0 | nBytes += n * sizeof(IntValue); |
759 | 0 | _WidthBytes = psdGetValue(IntValue, sizeof(_WidthBytes) ); |
760 | |
|
761 | 0 | n = (int)io->read_proc(IntValue, sizeof(IntValue), 1, handle); |
762 | 0 | nBytes += n * sizeof(IntValue); |
763 | 0 | _Size = psdGetValue(IntValue, sizeof(_Size) ); |
764 | |
|
765 | 0 | n = (int)io->read_proc(IntValue, sizeof(IntValue), 1, handle); |
766 | 0 | nBytes += n * sizeof(IntValue); |
767 | 0 | _CompressedSize = psdGetValue(IntValue, sizeof(_CompressedSize) ); |
768 | |
|
769 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
770 | 0 | nBytes += n * sizeof(ShortValue); |
771 | 0 | _BitPerPixel = (short)psdGetValue(ShortValue, sizeof(_BitPerPixel) ); |
772 | |
|
773 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
774 | 0 | nBytes += n * sizeof(ShortValue); |
775 | 0 | _Planes = (short)psdGetValue(ShortValue, sizeof(_Planes) ); |
776 | |
|
777 | 0 | const long JFIF_startpos = io->tell_proc(handle); |
778 | |
|
779 | 0 | if(_dib) { |
780 | 0 | FreeImage_Unload(_dib); |
781 | 0 | } |
782 | |
|
783 | 0 | if(_Format == 1) { |
784 | | // kJpegRGB thumbnail image |
785 | 0 | _dib = FreeImage_LoadFromHandle(FIF_JPEG, io, handle); |
786 | 0 | if(isBGR) { |
787 | 0 | SwapRedBlue32(_dib); |
788 | 0 | } |
789 | | // HACK: manually go to end of thumbnail, because (for some reason) LoadFromHandle consumes more bytes then available! |
790 | 0 | io->seek_proc(handle, block_end, SEEK_SET); |
791 | 0 | } |
792 | 0 | else { |
793 | | // kRawRGB thumbnail image |
794 | 0 | _dib = FreeImage_Allocate(_Width, _Height, _BitPerPixel); |
795 | 0 | BYTE* dst_line_start = FreeImage_GetScanLine(_dib, _Height - 1);//<*** flipped |
796 | 0 | BYTE* line_start = new BYTE[_WidthBytes]; |
797 | 0 | const unsigned dstLineSize = FreeImage_GetPitch(_dib); |
798 | 0 | for(unsigned h = 0; h < (unsigned)_Height; ++h, dst_line_start -= dstLineSize) {//<*** flipped |
799 | 0 | io->read_proc(line_start, _WidthBytes, 1, handle); |
800 | 0 | iTotalData -= _WidthBytes; |
801 | 0 | memcpy(dst_line_start, line_start, _Width * _BitPerPixel / 8); |
802 | 0 | } |
803 | 0 | #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR |
804 | 0 | SwapRedBlue32(_dib); |
805 | 0 | #endif |
806 | 0 | SAFE_DELETE_ARRAY(line_start); |
807 | | |
808 | | // skip any remaining data |
809 | 0 | io->seek_proc(handle, iTotalData, SEEK_CUR); |
810 | 0 | return iResourceSize; |
811 | 0 | } |
812 | | |
813 | 0 | nBytes += (block_end - JFIF_startpos); |
814 | |
|
815 | 0 | return nBytes; |
816 | 0 | } |
817 | | |
818 | 0 | bool psdThumbnail::Write(FreeImageIO *io, fi_handle handle, bool isBGR) { |
819 | 0 | BYTE ShortValue[2], IntValue[4]; |
820 | |
|
821 | 0 | const long res_start_pos = io->tell_proc(handle); |
822 | 0 | const int ID = isBGR ? PSDP_RES_THUMBNAIL_PS4 : PSDP_RES_THUMBNAIL; |
823 | 0 | if(!psdImageResource().Write(io, handle, ID, 0)) |
824 | 0 | return false; |
825 | | |
826 | 0 | psdSetValue(IntValue, sizeof(IntValue), _Format); |
827 | 0 | if(io->write_proc(IntValue, sizeof(IntValue), 1, handle) != 1) { |
828 | 0 | return false; |
829 | 0 | } |
830 | 0 | psdSetValue(IntValue, sizeof(IntValue), _Width); |
831 | 0 | if(io->write_proc(IntValue, sizeof(IntValue), 1, handle) != 1) { |
832 | 0 | return false; |
833 | 0 | } |
834 | 0 | psdSetValue(IntValue, sizeof(IntValue), _Height); |
835 | 0 | if(io->write_proc(IntValue, sizeof(IntValue), 1, handle) != 1) { |
836 | 0 | return false; |
837 | 0 | } |
838 | 0 | psdSetValue(IntValue, sizeof(IntValue), _WidthBytes); |
839 | 0 | if(io->write_proc(IntValue, sizeof(IntValue), 1, handle) != 1) { |
840 | 0 | return false; |
841 | 0 | } |
842 | 0 | psdSetValue(IntValue, sizeof(IntValue), _Size); |
843 | 0 | if(io->write_proc(IntValue, sizeof(IntValue), 1, handle) != 1) { |
844 | 0 | return false; |
845 | 0 | } |
846 | 0 | const long compressed_pos = io->tell_proc(handle); |
847 | 0 | psdSetValue(IntValue, sizeof(IntValue), _CompressedSize); |
848 | 0 | if(io->write_proc(IntValue, sizeof(IntValue), 1, handle) != 1) { |
849 | 0 | return false; |
850 | 0 | } |
851 | 0 | psdSetValue(ShortValue, sizeof(ShortValue), _BitPerPixel); |
852 | 0 | if(io->write_proc(ShortValue, sizeof(ShortValue), 1, handle) != 1) { |
853 | 0 | return false; |
854 | 0 | } |
855 | 0 | psdSetValue(ShortValue, sizeof(ShortValue), _Planes); |
856 | 0 | if(io->write_proc(ShortValue, sizeof(ShortValue), 1, handle) != 1) { |
857 | 0 | return false; |
858 | 0 | } |
859 | 0 | if(_Format == 1) { |
860 | | // kJpegRGB thumbnail image |
861 | 0 | if(isBGR) { |
862 | 0 | SwapRedBlue32(_dib); |
863 | 0 | } |
864 | 0 | const long start_pos = io->tell_proc(handle); |
865 | 0 | FreeImage_SaveToHandle(FIF_JPEG, _dib, io, handle, JPEG_DEFAULT); |
866 | 0 | const long current_pos = io->tell_proc(handle); |
867 | 0 | _CompressedSize = (int)(current_pos - start_pos); |
868 | 0 | io->seek_proc(handle, compressed_pos, SEEK_SET); |
869 | 0 | psdSetValue(IntValue, sizeof(IntValue), _CompressedSize); |
870 | 0 | if(io->write_proc(IntValue, sizeof(IntValue), 1, handle) != 1) { |
871 | 0 | return false; |
872 | 0 | } |
873 | 0 | io->seek_proc(handle, current_pos, SEEK_SET); |
874 | 0 | } |
875 | 0 | else { |
876 | | // kRawRGB thumbnail image |
877 | | // ### Unimplemented (should be trivial) |
878 | 0 | _CompressedSize = 0; |
879 | 0 | } |
880 | | |
881 | 0 | int len = 28 + _CompressedSize; |
882 | | |
883 | | // Fix length of resource |
884 | 0 | io->seek_proc(handle, res_start_pos + 8, SEEK_SET); |
885 | 0 | psdSetValue(IntValue, sizeof(IntValue), len); |
886 | 0 | if(io->write_proc(IntValue, sizeof(IntValue), 1, handle) != 1) { |
887 | 0 | return false; |
888 | 0 | } |
889 | 0 | io->seek_proc(handle, 0, SEEK_END); |
890 | |
|
891 | 0 | if((len % 2) != 0) { |
892 | 0 | BYTE data[1]; |
893 | 0 | data[0] = 0; |
894 | 0 | if(io->write_proc(data, sizeof(data), 1, handle) != 1) { |
895 | 0 | return false; |
896 | 0 | } |
897 | 0 | } |
898 | | |
899 | 0 | return true; |
900 | 0 | } |
901 | | |
902 | | //--------------------------------------------------------------------------- |
903 | | |
904 | 0 | psdICCProfile::psdICCProfile() : _ProfileSize(0), _ProfileData(NULL), _owned(true) { |
905 | 0 | } |
906 | | |
907 | 0 | psdICCProfile::~psdICCProfile() { |
908 | 0 | clear(); |
909 | 0 | } |
910 | | |
911 | 0 | void psdICCProfile::clear() { if (_owned) { SAFE_DELETE_ARRAY(_ProfileData); } else { _ProfileData = NULL; } _ProfileSize = 0;} |
912 | | |
913 | 0 | int psdICCProfile::Read(FreeImageIO *io, fi_handle handle, int size) { |
914 | 0 | int nBytes = 0, n; |
915 | |
|
916 | 0 | clear(); |
917 | |
|
918 | 0 | _ProfileData = new (std::nothrow) BYTE[size]; |
919 | 0 | if(NULL != _ProfileData) { |
920 | 0 | n = (int)io->read_proc(_ProfileData, 1, size, handle); |
921 | 0 | _ProfileSize = size; |
922 | 0 | nBytes += n * sizeof(BYTE); |
923 | 0 | } |
924 | |
|
925 | 0 | return nBytes; |
926 | 0 | } |
927 | | |
928 | 0 | bool psdICCProfile::Write(FreeImageIO *io, fi_handle handle) { |
929 | 0 | if(!psdImageResource().Write(io, handle, PSDP_RES_ICC_PROFILE, _ProfileSize)) |
930 | 0 | return false; |
931 | | |
932 | 0 | if(NULL != _ProfileData) { |
933 | 0 | if(io->write_proc(_ProfileData, 1, _ProfileSize, handle) != _ProfileSize) { |
934 | 0 | return false; |
935 | 0 | } |
936 | 0 | if((_ProfileSize % 2) != 0) { |
937 | 0 | BYTE data[1]; |
938 | 0 | data[0] = 0; |
939 | 0 | if(io->write_proc(data, sizeof(data), 1, handle) != 1) { |
940 | 0 | return false; |
941 | 0 | } |
942 | 0 | } |
943 | 0 | } |
944 | | |
945 | 0 | return true; |
946 | 0 | } |
947 | | |
948 | | //--------------------------------------------------------------------------- |
949 | | |
950 | 0 | psdData::psdData() : _Size(0), _Data(NULL), _owned(true) { |
951 | 0 | } |
952 | | |
953 | 0 | psdData::~psdData() { |
954 | 0 | clear(); |
955 | 0 | } |
956 | | |
957 | 0 | void psdData::clear() { if (_owned) { SAFE_DELETE_ARRAY(_Data); } else { _Data = NULL; } _Size = 0;} |
958 | | |
959 | 0 | int psdData::Read(FreeImageIO *io, fi_handle handle, int size) { |
960 | 0 | int nBytes = 0, n; |
961 | |
|
962 | 0 | clear(); |
963 | |
|
964 | 0 | _Data = new (std::nothrow) BYTE[size]; |
965 | 0 | if(NULL != _Data) { |
966 | 0 | n = (int)io->read_proc(_Data, 1, size, handle); |
967 | 0 | _Size = (unsigned)size; |
968 | 0 | nBytes += n * sizeof(BYTE); |
969 | 0 | } |
970 | |
|
971 | 0 | return nBytes; |
972 | 0 | } |
973 | | |
974 | 0 | bool psdData::Write(FreeImageIO *io, fi_handle handle, int ID) { |
975 | 0 | if(!psdImageResource().Write(io, handle, ID, _Size)) |
976 | 0 | return false; |
977 | | |
978 | 0 | if(NULL != _Data) { |
979 | 0 | if(io->write_proc(_Data, 1, _Size, handle) != _Size) { |
980 | 0 | return false; |
981 | 0 | } |
982 | 0 | if((_Size % 2) != 0) { |
983 | 0 | BYTE data[1]; |
984 | 0 | data[0] = 0; |
985 | 0 | if(io->write_proc(data, sizeof(data), 1, handle) != 1) { |
986 | 0 | return false; |
987 | 0 | } |
988 | 0 | } |
989 | 0 | } |
990 | | |
991 | 0 | return true; |
992 | 0 | } |
993 | | |
994 | | //--------------------------------------------------------------------------- |
995 | | |
996 | | /** |
997 | | Invert only color components, skipping Alpha/Black |
998 | | (Can be useful as public/utility function) |
999 | | */ |
1000 | | static |
1001 | 0 | BOOL invertColor(FIBITMAP* dib) { |
1002 | 0 | FREE_IMAGE_TYPE type = FreeImage_GetImageType(dib); |
1003 | 0 | const unsigned Bpp = FreeImage_GetBPP(dib)/8; |
1004 | |
|
1005 | 0 | if((type == FIT_BITMAP && Bpp == 4) || type == FIT_RGBA16) { |
1006 | 0 | const unsigned width = FreeImage_GetWidth(dib); |
1007 | 0 | const unsigned height = FreeImage_GetHeight(dib); |
1008 | 0 | BYTE *line_start = FreeImage_GetScanLine(dib, 0); |
1009 | 0 | const unsigned pitch = FreeImage_GetPitch(dib); |
1010 | 0 | const unsigned triBpp = Bpp - (Bpp == 4 ? 1 : 2); |
1011 | |
|
1012 | 0 | for(unsigned y = 0; y < height; y++) { |
1013 | 0 | BYTE *line = line_start; |
1014 | |
|
1015 | 0 | for(unsigned x = 0; x < width; x++) { |
1016 | 0 | for(unsigned b=0; b < triBpp; ++b) { |
1017 | 0 | line[b] = ~line[b]; |
1018 | 0 | } |
1019 | |
|
1020 | 0 | line += Bpp; |
1021 | 0 | } |
1022 | 0 | line_start += pitch; |
1023 | 0 | } |
1024 | |
|
1025 | 0 | return TRUE; |
1026 | 0 | } |
1027 | 0 | else { |
1028 | 0 | return FreeImage_Invert(dib); |
1029 | 0 | } |
1030 | 0 | } |
1031 | | |
1032 | | //--------------------------------------------------------------------------- |
1033 | | |
1034 | 0 | psdParser::psdParser() { |
1035 | 0 | _bThumbnailFilled = false; |
1036 | 0 | _bDisplayInfoFilled = false; |
1037 | 0 | _bResolutionInfoFilled = false; |
1038 | 0 | _bResolutionInfoFilled_v2 = false; |
1039 | 0 | _bCopyright = false; |
1040 | 0 | _GlobalAngle = 30; |
1041 | 0 | _ColourCount = -1; |
1042 | 0 | _TransparentIndex = -1; |
1043 | 0 | _fi_flags = 0; |
1044 | 0 | _fi_format_id = FIF_UNKNOWN; |
1045 | 0 | } |
1046 | | |
1047 | 0 | psdParser::~psdParser() { |
1048 | 0 | } |
1049 | | |
1050 | 0 | unsigned psdParser::GetChannelOffset(FIBITMAP* bitmap, unsigned c) const { |
1051 | 0 | unsigned channelOffset = c; |
1052 | 0 | #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR |
1053 | | // Swap R/B indices for BGR -> RGB |
1054 | 0 | if(FreeImage_GetImageType(bitmap) == FIT_BITMAP && |
1055 | 0 | _headerInfo._ColourMode == PSDP_RGB && |
1056 | 0 | (c == 0 || c == 2)) { |
1057 | 0 | channelOffset = (2 - c); |
1058 | 0 | } |
1059 | 0 | #endif |
1060 | 0 | return channelOffset; |
1061 | 0 | } |
1062 | | |
1063 | 0 | bool psdParser::ReadLayerAndMaskInfoSection(FreeImageIO *io, fi_handle handle) { |
1064 | 0 | bool bSuccess = true; |
1065 | |
|
1066 | 0 | UINT64 nTotalBytes = psdReadSize(io, handle, _headerInfo); |
1067 | | |
1068 | | // Hack to handle large PSB files without using fseeko(). |
1069 | 0 | if (sizeof(long) < sizeof(UINT64)) { |
1070 | 0 | const long offset = 0x10000000; |
1071 | 0 | while (nTotalBytes > offset) { |
1072 | 0 | if (io->seek_proc(handle, offset, SEEK_CUR) != 0) { |
1073 | 0 | bSuccess = false; |
1074 | 0 | break; |
1075 | 0 | } |
1076 | 0 | nTotalBytes -= offset; |
1077 | 0 | } |
1078 | 0 | } |
1079 | 0 | if (bSuccess && nTotalBytes > 0) { |
1080 | 0 | if (io->seek_proc(handle, (long)nTotalBytes, SEEK_CUR) != 0) |
1081 | 0 | bSuccess = false; |
1082 | 0 | } |
1083 | |
|
1084 | 0 | return bSuccess; |
1085 | 0 | } |
1086 | | |
1087 | 0 | bool psdParser::ReadImageResources(FreeImageIO *io, fi_handle handle, LONG length) { |
1088 | 0 | psdImageResource oResource; |
1089 | 0 | bool bSuccess = false; |
1090 | |
|
1091 | 0 | if(length > 0) { |
1092 | 0 | oResource._Length = length; |
1093 | 0 | } else { |
1094 | 0 | BYTE Length[4]; |
1095 | 0 | int n = (int)io->read_proc(Length, sizeof(Length), 1, handle); |
1096 | |
|
1097 | 0 | oResource._Length = psdGetValue( Length, sizeof(oResource._Length) ); |
1098 | 0 | } |
1099 | |
|
1100 | 0 | int nBytes = 0; |
1101 | 0 | int nTotalBytes = oResource._Length; |
1102 | |
|
1103 | 0 | while(nBytes < nTotalBytes) { |
1104 | 0 | int n = 0; |
1105 | 0 | oResource.Reset(); |
1106 | |
|
1107 | 0 | n = (int)io->read_proc(oResource._OSType, sizeof(oResource._OSType), 1, handle); |
1108 | 0 | if(n != 1) { |
1109 | 0 | FreeImage_OutputMessageProc(_fi_format_id, "This file contains damaged data causing an unexpected end-of-file - stop reading resources"); |
1110 | 0 | return false; |
1111 | 0 | } |
1112 | 0 | nBytes += n * sizeof(oResource._OSType); |
1113 | |
|
1114 | 0 | if( (nBytes % 2) != 0 ) { |
1115 | 0 | return false; |
1116 | 0 | } |
1117 | | |
1118 | 0 | int nOSType = psdGetValue((BYTE*)&oResource._OSType, sizeof(oResource._OSType)); |
1119 | |
|
1120 | 0 | if ( PSD_RESOURCE == nOSType ) { |
1121 | 0 | BYTE ID[2]; |
1122 | 0 | n = (int)io->read_proc(ID, sizeof(ID), 1, handle); |
1123 | 0 | nBytes += n * sizeof(ID); |
1124 | |
|
1125 | 0 | oResource._ID = (short)psdGetValue( ID, sizeof(ID) ); |
1126 | |
|
1127 | 0 | BYTE SizeOfName; |
1128 | 0 | n = (int)io->read_proc(&SizeOfName, sizeof(SizeOfName), 1, handle); |
1129 | 0 | nBytes += n * sizeof(SizeOfName); |
1130 | |
|
1131 | 0 | int nSizeOfName = psdGetValue( &SizeOfName, sizeof(SizeOfName) ); |
1132 | 0 | if ( 0 < nSizeOfName ) { |
1133 | 0 | oResource._plName = new BYTE[nSizeOfName]; |
1134 | 0 | n = (int)io->read_proc(oResource._plName, nSizeOfName, 1, handle); |
1135 | 0 | nBytes += n * nSizeOfName; |
1136 | 0 | } |
1137 | |
|
1138 | 0 | if ( 0 == (nSizeOfName % 2) ) { |
1139 | 0 | n = (int)io->read_proc(&SizeOfName, sizeof(SizeOfName), 1, handle); |
1140 | 0 | nBytes += n * sizeof(SizeOfName); |
1141 | 0 | } |
1142 | |
|
1143 | 0 | BYTE Size[4]; |
1144 | 0 | n = (int)io->read_proc(Size, sizeof(Size), 1, handle); |
1145 | 0 | nBytes += n * sizeof(Size); |
1146 | |
|
1147 | 0 | oResource._Size = psdGetValue( Size, sizeof(oResource._Size) ); |
1148 | |
|
1149 | 0 | if ( 0 != (oResource._Size % 2) ) { |
1150 | | // resource data must be even |
1151 | 0 | oResource._Size++; |
1152 | 0 | } |
1153 | 0 | if ( 0 < oResource._Size ) { |
1154 | 0 | BYTE IntValue[4]; |
1155 | 0 | BYTE ShortValue[2]; |
1156 | |
|
1157 | 0 | switch( oResource._ID ) { |
1158 | 0 | case PSDP_RES_RESOLUTION_INFO_V2: |
1159 | | // Obsolete - Photoshop 2.0 |
1160 | 0 | _bResolutionInfoFilled_v2 = true; |
1161 | 0 | nBytes += _resolutionInfo_v2.Read(io, handle); |
1162 | 0 | break; |
1163 | | |
1164 | | // ResolutionInfo structure |
1165 | 0 | case PSDP_RES_RESOLUTION_INFO: |
1166 | 0 | _bResolutionInfoFilled = true; |
1167 | 0 | nBytes += _resolutionInfo.Read(io, handle); |
1168 | 0 | break; |
1169 | | |
1170 | | // DisplayInfo structure |
1171 | 0 | case PSDP_RES_DISPLAY_INFO: |
1172 | 0 | _bDisplayInfoFilled = true; |
1173 | 0 | nBytes += _displayInfo.Read(io, handle); |
1174 | 0 | break; |
1175 | | |
1176 | | // IPTC-NAA record |
1177 | 0 | case PSDP_RES_IPTC_NAA: |
1178 | 0 | nBytes += _iptc.Read(io, handle, oResource._Size); |
1179 | 0 | break; |
1180 | | // (Photoshop 4.0) Copyright flag |
1181 | | // Boolean indicating whether image is copyrighted. Can be set via Property suite or by user in File Info... |
1182 | 0 | case PSDP_RES_COPYRIGHT: |
1183 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
1184 | 0 | nBytes += n * sizeof(ShortValue); |
1185 | 0 | _bCopyright = (1 == psdGetValue(ShortValue, sizeof(ShortValue))); |
1186 | 0 | break; |
1187 | | |
1188 | | // (Photoshop 4.0) Thumbnail resource for Photoshop 4.0 only |
1189 | 0 | case PSDP_RES_THUMBNAIL_PS4: |
1190 | | // (Photoshop 5.0) Thumbnail resource (supersedes resource 1033) |
1191 | 0 | case PSDP_RES_THUMBNAIL: |
1192 | 0 | { |
1193 | 0 | _bThumbnailFilled = true; |
1194 | 0 | bool bBGR = (PSDP_RES_THUMBNAIL_PS4 == oResource._ID); |
1195 | 0 | nBytes += _thumbnail.Read(io, handle, oResource._Size, bBGR); |
1196 | 0 | break; |
1197 | 0 | } |
1198 | | |
1199 | | // (Photoshop 5.0) Global Angle |
1200 | | // 4 bytes that contain an integer between 0 and 359, which is the global |
1201 | | // lighting angle for effects layer. If not present, assumed to be 30. |
1202 | 0 | case PSDP_RES_GLOBAL_ANGLE: |
1203 | 0 | n = (int)io->read_proc(IntValue, sizeof(IntValue), 1, handle); |
1204 | 0 | nBytes += n * sizeof(IntValue); |
1205 | 0 | _GlobalAngle = psdGetValue(IntValue, sizeof(_GlobalAngle) ); |
1206 | 0 | break; |
1207 | | |
1208 | | // ICC profile |
1209 | 0 | case PSDP_RES_ICC_PROFILE: |
1210 | 0 | nBytes += _iccProfile.Read(io, handle, oResource._Size); |
1211 | 0 | break; |
1212 | | |
1213 | | // (Photoshop 6.0) Indexed Color Table Count |
1214 | | // 2 bytes for the number of colors in table that are actually defined |
1215 | 0 | case PSDP_RES_INDEXED_COLORS: |
1216 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
1217 | 0 | nBytes += n * sizeof(ShortValue); |
1218 | 0 | _ColourCount = (short)psdGetValue(ShortValue, sizeof(ShortValue) ); |
1219 | 0 | break; |
1220 | | |
1221 | | // (Photoshop 6.0) Transparency Index. |
1222 | | // 2 bytes for the index of transparent color, if any. |
1223 | 0 | case PSDP_RES_TRANSPARENCY_INDEX: |
1224 | 0 | n = (int)io->read_proc(ShortValue, sizeof(ShortValue), 1, handle); |
1225 | 0 | nBytes += n * sizeof(ShortValue); |
1226 | 0 | _TransparentIndex = (short)psdGetValue(ShortValue, sizeof(ShortValue) ); |
1227 | 0 | break; |
1228 | | |
1229 | | // (Photoshop 7.0) EXIF data 1 |
1230 | 0 | case PSDP_RES_EXIF1: |
1231 | 0 | nBytes += _exif1.Read(io, handle, oResource._Size); |
1232 | 0 | break; |
1233 | | // (Photoshop 7.0) EXIF data 3 |
1234 | 0 | case PSDP_RES_EXIF3: |
1235 | 0 | nBytes += _exif3.Read(io, handle, oResource._Size); |
1236 | 0 | break; |
1237 | | // (Photoshop 7.0) XMP metadata |
1238 | 0 | case PSDP_RES_XMP: |
1239 | 0 | nBytes += _xmp.Read(io, handle, oResource._Size); |
1240 | 0 | break; |
1241 | 0 | default: |
1242 | 0 | { |
1243 | | // skip resource |
1244 | 0 | unsigned skip_length = MIN(oResource._Size, nTotalBytes - nBytes); |
1245 | 0 | io->seek_proc(handle, skip_length, SEEK_CUR); |
1246 | 0 | nBytes += skip_length; |
1247 | 0 | } |
1248 | 0 | break; |
1249 | 0 | } |
1250 | 0 | } |
1251 | 0 | } |
1252 | 0 | } |
1253 | | |
1254 | 0 | if (nBytes == nTotalBytes) { |
1255 | 0 | bSuccess = true; |
1256 | 0 | } |
1257 | |
|
1258 | 0 | return bSuccess; |
1259 | |
|
1260 | 0 | } |
1261 | | |
1262 | 0 | void psdParser::ReadImageLine(BYTE* dst, const BYTE* src, unsigned lineSize, unsigned dstBpp, unsigned bytes) { |
1263 | 0 | switch (bytes) { |
1264 | 0 | case 4: |
1265 | 0 | { |
1266 | 0 | DWORD* d = (DWORD*)dst; |
1267 | 0 | const DWORD* s = (const DWORD*)src; |
1268 | 0 | dstBpp /= 4; |
1269 | 0 | while (lineSize > 0) { |
1270 | 0 | DWORD v = *s++; |
1271 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
1272 | 0 | SwapLong(&v); |
1273 | 0 | #endif |
1274 | 0 | *d = v; |
1275 | 0 | d += dstBpp; |
1276 | 0 | lineSize -= 4; |
1277 | 0 | } |
1278 | 0 | break; |
1279 | 0 | } |
1280 | 0 | case 2: |
1281 | 0 | { |
1282 | 0 | WORD* d = (WORD*)dst; |
1283 | 0 | const WORD* s = (const WORD*)src; |
1284 | 0 | dstBpp /= 2; |
1285 | 0 | while (lineSize > 0) { |
1286 | 0 | WORD v = *s++; |
1287 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
1288 | 0 | SwapShort(&v); |
1289 | 0 | #endif |
1290 | 0 | *d = v; |
1291 | 0 | d += dstBpp; |
1292 | 0 | lineSize -= 2; |
1293 | 0 | } |
1294 | 0 | break; |
1295 | 0 | } |
1296 | 0 | default: |
1297 | 0 | if (dstBpp == 1) { |
1298 | 0 | memcpy(dst, src, lineSize); |
1299 | 0 | } else { |
1300 | 0 | while (lineSize > 0) { |
1301 | 0 | *dst = *src++; |
1302 | 0 | dst += dstBpp; |
1303 | 0 | lineSize--; |
1304 | 0 | } |
1305 | 0 | } |
1306 | 0 | break; |
1307 | 0 | } |
1308 | 0 | } |
1309 | | |
1310 | 0 | void psdParser::UnpackRLE(BYTE* line, const BYTE* rle_line, BYTE* line_end, unsigned srcSize) { |
1311 | 0 | while (srcSize > 0) { |
1312 | |
|
1313 | 0 | int len = *rle_line++; |
1314 | 0 | srcSize--; |
1315 | | |
1316 | | // NOTE len is signed byte in PackBits RLE |
1317 | |
|
1318 | 0 | if ( len < 128 ) { //<- MSB is not set |
1319 | | // uncompressed packet |
1320 | | |
1321 | | // (len + 1) bytes of data are copied |
1322 | 0 | ++len; |
1323 | | |
1324 | | // assert we don't write beyound eol |
1325 | 0 | memcpy(line, rle_line, line + len > line_end ? line_end - line : len); |
1326 | 0 | line += len; |
1327 | 0 | rle_line += len; |
1328 | 0 | srcSize -= len; |
1329 | 0 | } |
1330 | 0 | else if ( len > 128 ) { //< MSB is set |
1331 | | // RLE compressed packet |
1332 | | |
1333 | | // One byte of data is repeated (–len + 1) times |
1334 | |
|
1335 | 0 | len ^= 0xFF; // same as (-len + 1) & 0xFF |
1336 | 0 | len += 2; // |
1337 | | |
1338 | | // assert we don't write beyound eol |
1339 | 0 | memset(line, *rle_line++, line + len > line_end ? line_end - line : len); |
1340 | 0 | line += len; |
1341 | 0 | srcSize--; |
1342 | 0 | } |
1343 | 0 | else if ( 128 == len ) { |
1344 | | // Do nothing |
1345 | 0 | } |
1346 | 0 | }//< rle_line |
1347 | 0 | } |
1348 | | |
1349 | 0 | FIBITMAP* psdParser::ReadImageData(FreeImageIO *io, fi_handle handle) { |
1350 | 0 | if (handle == NULL) { |
1351 | 0 | return NULL; |
1352 | 0 | } |
1353 | | |
1354 | 0 | bool header_only = (_fi_flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; |
1355 | |
|
1356 | 0 | WORD nCompression = 0; |
1357 | 0 | if (io->read_proc(&nCompression, sizeof(nCompression), 1, handle) != 1) { |
1358 | 0 | return NULL; |
1359 | 0 | } |
1360 | | |
1361 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
1362 | 0 | SwapShort(&nCompression); |
1363 | 0 | #endif |
1364 | | |
1365 | | // PSDP_COMPRESSION_ZIP and PSDP_COMPRESSION_ZIP_PREDICTION |
1366 | | // are only valid for layer data, not the composited data. |
1367 | 0 | if(nCompression != PSDP_COMPRESSION_NONE && |
1368 | 0 | nCompression != PSDP_COMPRESSION_RLE) { |
1369 | 0 | FreeImage_OutputMessageProc(_fi_format_id, "Unsupported compression %d", nCompression); |
1370 | 0 | return NULL; |
1371 | 0 | } |
1372 | | |
1373 | 0 | const unsigned nWidth = _headerInfo._Width; |
1374 | 0 | const unsigned nHeight = _headerInfo._Height; |
1375 | 0 | const unsigned nChannels = _headerInfo._Channels; |
1376 | 0 | const unsigned depth = _headerInfo._BitsPerChannel; |
1377 | 0 | const unsigned bytes = (depth == 1) ? 1 : depth / 8; |
1378 | | |
1379 | | // channel(plane) line (BYTE aligned) |
1380 | 0 | const unsigned lineSize = (_headerInfo._BitsPerChannel == 1) ? (nWidth + 7) / 8 : nWidth * bytes; |
1381 | |
|
1382 | 0 | if(nCompression == PSDP_COMPRESSION_RLE && depth > 16) { |
1383 | 0 | FreeImage_OutputMessageProc(_fi_format_id, "Unsupported RLE with depth %d", depth); |
1384 | 0 | return NULL; |
1385 | 0 | } |
1386 | | |
1387 | | // build output buffer |
1388 | | |
1389 | 0 | FIBITMAP* bitmap = NULL; |
1390 | 0 | unsigned dstCh = 0; |
1391 | |
|
1392 | 0 | short mode = _headerInfo._ColourMode; |
1393 | |
|
1394 | 0 | if(mode == PSDP_MULTICHANNEL && nChannels < 3) { |
1395 | | // CM |
1396 | 0 | mode = PSDP_GRAYSCALE; // C as gray, M as extra channel |
1397 | 0 | } |
1398 | |
|
1399 | 0 | bool needPalette = false; |
1400 | 0 | switch (mode) { |
1401 | 0 | case PSDP_BITMAP: |
1402 | 0 | case PSDP_DUOTONE: |
1403 | 0 | case PSDP_INDEXED: |
1404 | 0 | case PSDP_GRAYSCALE: |
1405 | 0 | dstCh = 1; |
1406 | 0 | switch(depth) { |
1407 | 0 | case 16: |
1408 | 0 | bitmap = FreeImage_AllocateHeaderT(header_only, FIT_UINT16, nWidth, nHeight, depth*dstCh); |
1409 | 0 | break; |
1410 | 0 | case 32: |
1411 | 0 | bitmap = FreeImage_AllocateHeaderT(header_only, FIT_FLOAT, nWidth, nHeight, depth*dstCh); |
1412 | 0 | break; |
1413 | 0 | default: // 1-, 8- |
1414 | 0 | needPalette = true; |
1415 | 0 | bitmap = FreeImage_AllocateHeader(header_only, nWidth, nHeight, depth*dstCh); |
1416 | 0 | break; |
1417 | 0 | } |
1418 | 0 | break; |
1419 | 0 | case PSDP_RGB: |
1420 | 0 | case PSDP_LAB: |
1421 | 0 | case PSDP_CMYK : |
1422 | 0 | case PSDP_MULTICHANNEL : |
1423 | | // force PSDP_MULTICHANNEL CMY as CMYK |
1424 | 0 | dstCh = (mode == PSDP_MULTICHANNEL && !header_only) ? 4 : MIN<unsigned>(nChannels, 4); |
1425 | 0 | if(dstCh < 3) { |
1426 | 0 | throw "Invalid number of channels"; |
1427 | 0 | } |
1428 | | |
1429 | 0 | switch(depth) { |
1430 | 0 | case 16: |
1431 | 0 | bitmap = FreeImage_AllocateHeaderT(header_only, dstCh < 4 ? FIT_RGB16 : FIT_RGBA16, nWidth, nHeight, depth*dstCh); |
1432 | 0 | break; |
1433 | 0 | case 32: |
1434 | 0 | bitmap = FreeImage_AllocateHeaderT(header_only, dstCh < 4 ? FIT_RGBF : FIT_RGBAF, nWidth, nHeight, depth*dstCh); |
1435 | 0 | break; |
1436 | 0 | default: |
1437 | 0 | bitmap = FreeImage_AllocateHeader(header_only, nWidth, nHeight, depth*dstCh); |
1438 | 0 | break; |
1439 | 0 | } |
1440 | 0 | break; |
1441 | 0 | default: |
1442 | 0 | throw "Unsupported color mode"; |
1443 | 0 | break; |
1444 | 0 | } |
1445 | 0 | if(!bitmap) { |
1446 | 0 | throw FI_MSG_ERROR_DIB_MEMORY; |
1447 | 0 | } |
1448 | | |
1449 | | // write thumbnail |
1450 | 0 | FreeImage_SetThumbnail(bitmap, _thumbnail.getDib()); |
1451 | | |
1452 | | // @todo Add some metadata model |
1453 | |
|
1454 | 0 | if(header_only) { |
1455 | 0 | return bitmap; |
1456 | 0 | } |
1457 | | |
1458 | | // Load pixels data |
1459 | | |
1460 | 0 | const unsigned dstChannels = dstCh; |
1461 | |
|
1462 | 0 | const unsigned dstBpp = (depth == 1) ? 1 : FreeImage_GetBPP(bitmap)/8; |
1463 | 0 | const unsigned dstLineSize = FreeImage_GetPitch(bitmap); |
1464 | 0 | BYTE* const dst_first_line = FreeImage_GetScanLine(bitmap, nHeight - 1);//<*** flipped |
1465 | |
|
1466 | 0 | BYTE* line_start = new BYTE[lineSize]; //< fileline cache |
1467 | |
|
1468 | 0 | switch ( nCompression ) { |
1469 | 0 | case PSDP_COMPRESSION_NONE: // raw data |
1470 | 0 | { |
1471 | 0 | for(unsigned c = 0; c < nChannels; c++) { |
1472 | 0 | if(c >= dstChannels) { |
1473 | | // @todo write extra channels |
1474 | 0 | break; |
1475 | 0 | } |
1476 | | |
1477 | 0 | const unsigned channelOffset = GetChannelOffset(bitmap, c) * bytes; |
1478 | |
|
1479 | 0 | BYTE* dst_line_start = dst_first_line + channelOffset; |
1480 | 0 | for(unsigned h = 0; h < nHeight; ++h, dst_line_start -= dstLineSize) {//<*** flipped |
1481 | 0 | io->read_proc(line_start, lineSize, 1, handle); |
1482 | 0 | ReadImageLine(dst_line_start, line_start, lineSize, dstBpp, bytes); |
1483 | 0 | } //< h |
1484 | 0 | }//< ch |
1485 | |
|
1486 | 0 | SAFE_DELETE_ARRAY(line_start); |
1487 | |
|
1488 | 0 | } |
1489 | 0 | break; |
1490 | | |
1491 | 0 | case PSDP_COMPRESSION_RLE: // RLE compression |
1492 | 0 | { |
1493 | | |
1494 | | // The RLE-compressed data is preceeded by a 2-byte line size for each row in the data, |
1495 | | // store an array of these |
1496 | | // Version 2 has 4-byte line sizes. |
1497 | | |
1498 | | // later use this array as DWORD rleLineSizeList[nChannels][nHeight]; |
1499 | 0 | DWORD *rleLineSizeList = new (std::nothrow) DWORD[nChannels*nHeight]; |
1500 | |
|
1501 | 0 | if(!rleLineSizeList) { |
1502 | 0 | FreeImage_Unload(bitmap); |
1503 | 0 | SAFE_DELETE_ARRAY(line_start); |
1504 | 0 | throw std::bad_alloc(); |
1505 | 0 | } |
1506 | 0 | if(_headerInfo._Version == 1) { |
1507 | 0 | WORD *rleLineSizeList2 = new (std::nothrow) WORD[nChannels*nHeight]; |
1508 | 0 | if(!rleLineSizeList2) { |
1509 | 0 | FreeImage_Unload(bitmap); |
1510 | 0 | SAFE_DELETE_ARRAY(line_start); |
1511 | 0 | throw std::bad_alloc(); |
1512 | 0 | } |
1513 | 0 | io->read_proc(rleLineSizeList2, 2, nChannels * nHeight, handle); |
1514 | 0 | for(unsigned index = 0; index < nChannels * nHeight; ++index) { |
1515 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
1516 | 0 | SwapShort(&rleLineSizeList2[index]); |
1517 | 0 | #endif |
1518 | 0 | rleLineSizeList[index] = rleLineSizeList2[index]; |
1519 | 0 | } |
1520 | 0 | SAFE_DELETE_ARRAY(rleLineSizeList2); |
1521 | 0 | } else { |
1522 | 0 | io->read_proc(rleLineSizeList, 4, nChannels * nHeight, handle); |
1523 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
1524 | 0 | for(unsigned index = 0; index < nChannels * nHeight; ++index) { |
1525 | 0 | SwapLong(&rleLineSizeList[index]); |
1526 | 0 | } |
1527 | 0 | #endif |
1528 | 0 | } |
1529 | | |
1530 | 0 | DWORD largestRLELine = 0; |
1531 | 0 | for(unsigned ch = 0; ch < nChannels; ++ch) { |
1532 | 0 | for(unsigned h = 0; h < nHeight; ++h) { |
1533 | 0 | const unsigned index = ch * nHeight + h; |
1534 | |
|
1535 | 0 | if(largestRLELine < rleLineSizeList[index]) { |
1536 | 0 | largestRLELine = rleLineSizeList[index]; |
1537 | 0 | } |
1538 | 0 | } |
1539 | 0 | } |
1540 | |
|
1541 | 0 | BYTE* rle_line_start = new (std::nothrow) BYTE[largestRLELine]; |
1542 | 0 | if(!rle_line_start) { |
1543 | 0 | FreeImage_Unload(bitmap); |
1544 | 0 | SAFE_DELETE_ARRAY(line_start); |
1545 | 0 | SAFE_DELETE_ARRAY(rleLineSizeList); |
1546 | 0 | throw std::bad_alloc(); |
1547 | 0 | } |
1548 | | |
1549 | | // Read the RLE data |
1550 | 0 | for (unsigned ch = 0; ch < nChannels; ch++) { |
1551 | 0 | if(ch >= dstChannels) { |
1552 | | // @todo write to extra channels |
1553 | 0 | break; |
1554 | 0 | } |
1555 | 0 | const BYTE* const line_end = line_start + lineSize; |
1556 | |
|
1557 | 0 | const unsigned channelOffset = GetChannelOffset(bitmap, ch) * bytes; |
1558 | |
|
1559 | 0 | BYTE* dst_line_start = dst_first_line + channelOffset; |
1560 | 0 | for(unsigned h = 0; h < nHeight; ++h, dst_line_start -= dstLineSize) {//<*** flipped |
1561 | 0 | const unsigned index = ch * nHeight + h; |
1562 | | |
1563 | | // - read and uncompress line - |
1564 | |
|
1565 | 0 | const DWORD rleLineSize = rleLineSizeList[index]; |
1566 | |
|
1567 | 0 | io->read_proc(rle_line_start, rleLineSize, 1, handle); |
1568 | | |
1569 | | // - write line to destination - |
1570 | |
|
1571 | 0 | UnpackRLE(line_start, rle_line_start, line_start + lineSize, rleLineSize); |
1572 | 0 | ReadImageLine(dst_line_start, line_start, lineSize, dstBpp, bytes); |
1573 | 0 | }//< h |
1574 | 0 | }//< ch |
1575 | |
|
1576 | 0 | SAFE_DELETE_ARRAY(line_start); |
1577 | 0 | SAFE_DELETE_ARRAY(rleLineSizeList); |
1578 | 0 | SAFE_DELETE_ARRAY(rle_line_start); |
1579 | 0 | } |
1580 | 0 | break; |
1581 | | |
1582 | | /* |
1583 | | * If layer data is ever supported, do something like this: |
1584 | | * Compressed size comes from layer info section. |
1585 | | * Prediction means unzip, then each pixel value is a delta from the previous pixel. |
1586 | | * Horizontally only. |
1587 | | */ |
1588 | | /* |
1589 | | case PSDP_COMPRESSION_ZIP: // ZIP without prediction |
1590 | | case PSDP_COMPRESSION_ZIP_PREDICTION: // ZIP with prediction |
1591 | | { |
1592 | | BYTE *compressed = NULL; |
1593 | | size_t compressedSize = 0; |
1594 | | BYTE *uncompressed = new (std::nothrow) BYTE[nHeight * lineSize]; |
1595 | | if(!uncompressed) { |
1596 | | FreeImage_Unload(bitmap); |
1597 | | SAFE_DELETE_ARRAY(line_start); |
1598 | | throw std::bad_alloc(); |
1599 | | } |
1600 | | DWORD size = FreeImage_ZLibGUnzip(uncompressed, nHeight * lineSize, compressed, compressedSize); |
1601 | | } |
1602 | | break; |
1603 | | */ |
1604 | 0 | default: // Unknown format |
1605 | 0 | break; |
1606 | |
|
1607 | 0 | } |
1608 | | |
1609 | | // --- Further process the bitmap --- |
1610 | | |
1611 | 0 | if((mode == PSDP_CMYK || mode == PSDP_MULTICHANNEL)) { |
1612 | | // CMYK values are "inverted", invert them back |
1613 | |
|
1614 | 0 | if(mode == PSDP_MULTICHANNEL) { |
1615 | 0 | invertColor(bitmap); |
1616 | 0 | } else { |
1617 | 0 | FreeImage_Invert(bitmap); |
1618 | 0 | } |
1619 | |
|
1620 | 0 | if((_fi_flags & PSD_CMYK) == PSD_CMYK) { |
1621 | | // keep as CMYK |
1622 | |
|
1623 | 0 | if(mode == PSDP_MULTICHANNEL) { |
1624 | | //### we force CMY to be CMYK, but CMY has no ICC. |
1625 | | // Create empty profile and add the flag. |
1626 | 0 | FreeImage_CreateICCProfile(bitmap, NULL, 0); |
1627 | 0 | FreeImage_GetICCProfile(bitmap)->flags |= FIICC_COLOR_IS_CMYK; |
1628 | 0 | } |
1629 | 0 | } |
1630 | 0 | else { |
1631 | | // convert to RGB |
1632 | |
|
1633 | 0 | ConvertCMYKtoRGBA(bitmap); |
1634 | | |
1635 | | // The ICC Profile is no longer valid |
1636 | 0 | _iccProfile.clear(); |
1637 | | |
1638 | | // remove the pending A if not present in source |
1639 | 0 | if(nChannels == 4 || nChannels == 3 ) { |
1640 | 0 | FIBITMAP* t = RemoveAlphaChannel(bitmap); |
1641 | 0 | if(t) { |
1642 | 0 | FreeImage_Unload(bitmap); |
1643 | 0 | bitmap = t; |
1644 | 0 | } // else: silently fail |
1645 | 0 | } |
1646 | 0 | } |
1647 | 0 | } |
1648 | 0 | else if ( mode == PSDP_LAB && !((_fi_flags & PSD_LAB) == PSD_LAB)) { |
1649 | 0 | ConvertLABtoRGB(bitmap); |
1650 | 0 | } |
1651 | 0 | else { |
1652 | 0 | if (needPalette && FreeImage_GetPalette(bitmap)) { |
1653 | |
|
1654 | 0 | if(mode == PSDP_BITMAP) { |
1655 | 0 | CREATE_GREYSCALE_PALETTE_REVERSE(FreeImage_GetPalette(bitmap), 2); |
1656 | 0 | } |
1657 | 0 | else if(mode == PSDP_INDEXED) { |
1658 | 0 | if(!_colourModeData._plColourData || _colourModeData._Length != 768 || _ColourCount < 0) { |
1659 | 0 | FreeImage_OutputMessageProc(_fi_format_id, "Indexed image has no palette. Using the default grayscale one."); |
1660 | 0 | } else { |
1661 | 0 | _colourModeData.FillPalette(bitmap); |
1662 | 0 | } |
1663 | 0 | } |
1664 | | // GRAYSCALE, DUOTONE - use default grayscale palette |
1665 | 0 | } |
1666 | 0 | } |
1667 | |
|
1668 | 0 | return bitmap; |
1669 | 0 | } |
1670 | | |
1671 | 0 | bool psdParser::WriteLayerAndMaskInfoSection(FreeImageIO *io, fi_handle handle) { |
1672 | | // Short section with no layers. |
1673 | 0 | BYTE IntValue[4]; |
1674 | |
|
1675 | 0 | UINT64 size; |
1676 | 0 | if(_headerInfo._Version == 1) { |
1677 | 0 | size = 8; |
1678 | 0 | } else { |
1679 | 0 | size = 12; |
1680 | 0 | } |
1681 | | // Length of whole info. |
1682 | 0 | if(!psdWriteSize(io, handle, _headerInfo, size)) { |
1683 | 0 | return false; |
1684 | 0 | } |
1685 | | // Length of layers info section. |
1686 | 0 | if(!psdWriteSize(io, handle, _headerInfo, 0)) { |
1687 | 0 | return false; |
1688 | 0 | } |
1689 | | // Length of global layer mask info section. Always 4 bytes. |
1690 | 0 | psdSetValue(IntValue, sizeof(IntValue), 0); |
1691 | 0 | if(io->write_proc(IntValue, sizeof(IntValue), 1, handle) != 1) { |
1692 | 0 | return false; |
1693 | 0 | } |
1694 | | // Additional layer information. |
1695 | 0 | return true; |
1696 | 0 | } |
1697 | | |
1698 | 0 | void psdParser::WriteImageLine(BYTE* dst, const BYTE* src, unsigned lineSize, unsigned srcBpp, unsigned bytes) { |
1699 | 0 | switch (bytes) { |
1700 | 0 | case 4: |
1701 | 0 | { |
1702 | 0 | DWORD* d = (DWORD*)dst; |
1703 | 0 | const DWORD* s = (const DWORD*)src; |
1704 | 0 | srcBpp /= 4; |
1705 | 0 | while (lineSize > 0) { |
1706 | 0 | DWORD v = *s; |
1707 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
1708 | 0 | SwapLong(&v); |
1709 | 0 | #endif |
1710 | 0 | *d++ = v; |
1711 | 0 | s += srcBpp; |
1712 | 0 | lineSize -= 4; |
1713 | 0 | } |
1714 | 0 | break; |
1715 | 0 | } |
1716 | 0 | case 2: |
1717 | 0 | { |
1718 | 0 | WORD* d = (WORD*)dst; |
1719 | 0 | const WORD* s = (const WORD*)src; |
1720 | 0 | srcBpp /= 2; |
1721 | 0 | while (lineSize > 0) { |
1722 | 0 | WORD v = *s; |
1723 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
1724 | 0 | SwapShort(&v); |
1725 | 0 | #endif |
1726 | 0 | *d++ = v; |
1727 | 0 | s += srcBpp; |
1728 | 0 | lineSize -= 2; |
1729 | 0 | } |
1730 | 0 | break; |
1731 | 0 | } |
1732 | 0 | default: |
1733 | 0 | if (srcBpp == 1) { |
1734 | 0 | memcpy(dst, src, lineSize); |
1735 | 0 | } else { |
1736 | 0 | while (lineSize > 0) { |
1737 | 0 | *dst++ = *src; |
1738 | 0 | src += srcBpp; |
1739 | 0 | lineSize--; |
1740 | 0 | } |
1741 | 0 | } |
1742 | 0 | break; |
1743 | 0 | } |
1744 | 0 | } |
1745 | | |
1746 | 0 | unsigned psdParser::PackRLE(BYTE* line_start, const BYTE* src_line, unsigned srcSize) { |
1747 | 0 | BYTE* line = line_start; |
1748 | 0 | while (srcSize > 0) { |
1749 | 0 | if(srcSize >= 2 && src_line[0] == src_line[1]) { |
1750 | 0 | int len = 2; |
1751 | 0 | while(len < 127 && len < (int)srcSize && src_line[0] == src_line[len]) |
1752 | 0 | len++; |
1753 | 0 | *line++ = (BYTE)((-len + 1) & 0xFF); |
1754 | 0 | *line++ = src_line[0]; |
1755 | 0 | src_line += len; |
1756 | 0 | srcSize -= len; |
1757 | 0 | } else { |
1758 | | // uncompressed packet |
1759 | | // (len + 1) bytes of data are copied |
1760 | 0 | int len = 1; |
1761 | 0 | while(len < 127 && len < (int)srcSize && |
1762 | 0 | (len+2 >= (int)srcSize || // check to switch to a run instead |
1763 | 0 | src_line[len] != src_line[len+1] || |
1764 | 0 | src_line[len] != src_line[len+2])) |
1765 | 0 | len++; |
1766 | 0 | *line++ = (BYTE)(len - 1); |
1767 | 0 | for(int i=0; i < len; i++) { |
1768 | 0 | *line++ = *src_line; |
1769 | 0 | src_line++; |
1770 | 0 | } |
1771 | 0 | srcSize -= len; |
1772 | 0 | } |
1773 | 0 | } |
1774 | 0 | return (unsigned)(line - line_start); |
1775 | 0 | } |
1776 | | |
1777 | 0 | bool psdParser::WriteImageData(FreeImageIO *io, fi_handle handle, FIBITMAP* dib) { |
1778 | 0 | if (handle == NULL) { |
1779 | 0 | return false; |
1780 | 0 | } |
1781 | | |
1782 | 0 | FIBITMAP* cmyk_dib = NULL; |
1783 | |
|
1784 | 0 | if (_headerInfo._ColourMode == PSDP_CMYK) { |
1785 | | // CMYK values must be "inverted" |
1786 | 0 | cmyk_dib = FreeImage_Clone(dib); |
1787 | 0 | if (cmyk_dib == NULL) { |
1788 | 0 | return false; |
1789 | 0 | } |
1790 | 0 | dib = cmyk_dib; |
1791 | 0 | FreeImage_Invert(dib); |
1792 | 0 | } |
1793 | | |
1794 | 0 | int nCompression = PSDP_COMPRESSION_RLE; |
1795 | 0 | if(_headerInfo._BitsPerChannel > 8) { |
1796 | | // RLE is nearly useless for 16-bit, as it only looks at 8-bit data for runs. |
1797 | 0 | nCompression = PSDP_COMPRESSION_NONE; |
1798 | 0 | } |
1799 | 0 | if((_fi_flags & PSD_NONE) == PSD_NONE) { |
1800 | 0 | nCompression = PSDP_COMPRESSION_NONE; |
1801 | 0 | } else if((_fi_flags & PSD_RLE) == PSD_RLE) { |
1802 | 0 | nCompression = PSDP_COMPRESSION_RLE; |
1803 | 0 | if (_headerInfo._BitsPerChannel > 16) { |
1804 | 0 | nCompression = PSDP_COMPRESSION_NONE; |
1805 | 0 | } |
1806 | 0 | } |
1807 | |
|
1808 | 0 | WORD CompressionValue = nCompression; |
1809 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
1810 | 0 | SwapShort(&CompressionValue); |
1811 | 0 | #endif |
1812 | |
|
1813 | 0 | if(io->write_proc(&CompressionValue, sizeof(CompressionValue), 1, handle) != 1) { |
1814 | 0 | return false; |
1815 | 0 | } |
1816 | | |
1817 | 0 | const unsigned nWidth = _headerInfo._Width; |
1818 | 0 | const unsigned nHeight = _headerInfo._Height; |
1819 | 0 | const unsigned nChannels = _headerInfo._Channels; |
1820 | 0 | const unsigned depth = _headerInfo._BitsPerChannel; |
1821 | 0 | const unsigned bytes = (depth == 1) ? 1 : depth / 8; |
1822 | | |
1823 | | // channel(plane) line (BYTE aligned) |
1824 | 0 | const unsigned lineSize = (_headerInfo._BitsPerChannel == 1) ? (nWidth + 7) / 8 : nWidth * bytes; |
1825 | |
|
1826 | 0 | const unsigned srcBpp = (depth == 1) ? 1 : FreeImage_GetBPP(dib)/8; |
1827 | 0 | const unsigned srcLineSize = FreeImage_GetPitch(dib); |
1828 | 0 | BYTE* const src_first_line = FreeImage_GetScanLine(dib, nHeight - 1);//<*** flipped |
1829 | 0 | BYTE* line_start = new BYTE[lineSize]; //< fileline cache |
1830 | |
|
1831 | 0 | switch ( nCompression ) { |
1832 | 0 | case PSDP_COMPRESSION_NONE: // raw data |
1833 | 0 | { |
1834 | 0 | for(unsigned c = 0; c < nChannels; c++) { |
1835 | 0 | const unsigned channelOffset = GetChannelOffset(dib, c) * bytes; |
1836 | |
|
1837 | 0 | BYTE* src_line_start = src_first_line + channelOffset; |
1838 | 0 | for(unsigned h = 0; h < nHeight; ++h, src_line_start -= srcLineSize) {//<*** flipped |
1839 | 0 | WriteImageLine(line_start, src_line_start, lineSize, srcBpp, bytes); |
1840 | 0 | if(io->write_proc(line_start, lineSize, 1, handle) != 1) { |
1841 | 0 | return false; |
1842 | 0 | } |
1843 | 0 | } //< h |
1844 | 0 | }//< ch |
1845 | 0 | } |
1846 | 0 | break; |
1847 | | |
1848 | 0 | case PSDP_COMPRESSION_RLE: // RLE compression |
1849 | 0 | { |
1850 | | // The RLE-compressed data is preceeded by a 2-byte line size for each row in the data, |
1851 | | // store an array of these |
1852 | | // Version 2 has 4-byte line sizes. |
1853 | | |
1854 | | // later use this array as WORD rleLineSizeList[nChannels][nHeight]; |
1855 | | // Every 127 bytes needs a length byte. |
1856 | 0 | BYTE* rle_line_start = new BYTE[lineSize + ((nWidth + 126) / 127)]; //< RLE buffer |
1857 | 0 | DWORD *rleLineSizeList = new (std::nothrow) DWORD[nChannels*nHeight]; |
1858 | |
|
1859 | 0 | if(!rleLineSizeList) { |
1860 | 0 | SAFE_DELETE_ARRAY(line_start); |
1861 | 0 | throw std::bad_alloc(); |
1862 | 0 | } |
1863 | 0 | memset(rleLineSizeList, 0, sizeof(DWORD)*nChannels*nHeight); |
1864 | 0 | const long offsets_pos = io->tell_proc(handle); |
1865 | 0 | if(_headerInfo._Version == 1) { |
1866 | 0 | if(io->write_proc(rleLineSizeList, nChannels*nHeight*2, 1, handle) != 1) { |
1867 | 0 | return false; |
1868 | 0 | } |
1869 | 0 | } else { |
1870 | 0 | if(io->write_proc(rleLineSizeList, nChannels*nHeight*4, 1, handle) != 1) { |
1871 | 0 | return false; |
1872 | 0 | } |
1873 | 0 | } |
1874 | 0 | for(unsigned c = 0; c < nChannels; c++) { |
1875 | 0 | const unsigned channelOffset = GetChannelOffset(dib, c) * bytes; |
1876 | |
|
1877 | 0 | BYTE* src_line_start = src_first_line + channelOffset; |
1878 | 0 | for(unsigned h = 0; h < nHeight; ++h, src_line_start -= srcLineSize) {//<*** flipped |
1879 | 0 | WriteImageLine(line_start, src_line_start, lineSize, srcBpp, bytes); |
1880 | 0 | unsigned len = PackRLE(rle_line_start, line_start, lineSize); |
1881 | 0 | rleLineSizeList[c * nHeight + h] = len; |
1882 | 0 | if(io->write_proc(rle_line_start, len, 1, handle) != 1) { |
1883 | 0 | return false; |
1884 | 0 | } |
1885 | 0 | } |
1886 | 0 | } |
1887 | 0 | SAFE_DELETE_ARRAY(rle_line_start); |
1888 | | // Fix length of resource |
1889 | 0 | io->seek_proc(handle, offsets_pos, SEEK_SET); |
1890 | 0 | if(_headerInfo._Version == 1) { |
1891 | 0 | WORD *rleLineSizeList2 = new (std::nothrow) WORD[nChannels*nHeight]; |
1892 | 0 | if(!rleLineSizeList2) { |
1893 | 0 | SAFE_DELETE_ARRAY(line_start); |
1894 | 0 | throw std::bad_alloc(); |
1895 | 0 | } |
1896 | 0 | for(unsigned index = 0; index < nChannels * nHeight; ++index) { |
1897 | 0 | rleLineSizeList2[index] = (WORD)rleLineSizeList[index]; |
1898 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
1899 | 0 | SwapShort(&rleLineSizeList2[index]); |
1900 | 0 | #endif |
1901 | 0 | } |
1902 | 0 | if(io->write_proc(rleLineSizeList2, nChannels*nHeight*2, 1, handle) != 1) { |
1903 | 0 | return false; |
1904 | 0 | } |
1905 | 0 | SAFE_DELETE_ARRAY(rleLineSizeList2); |
1906 | 0 | } else { |
1907 | 0 | #ifndef FREEIMAGE_BIGENDIAN |
1908 | 0 | for(unsigned index = 0; index < nChannels * nHeight; ++index) { |
1909 | 0 | SwapLong(&rleLineSizeList[index]); |
1910 | 0 | } |
1911 | 0 | #endif |
1912 | 0 | if(io->write_proc(rleLineSizeList, nChannels*nHeight*4, 1, handle) != 1) { |
1913 | 0 | return false; |
1914 | 0 | } |
1915 | 0 | } |
1916 | 0 | io->seek_proc(handle, 0, SEEK_END); |
1917 | 0 | } |
1918 | 0 | break; |
1919 | | |
1920 | 0 | case PSDP_COMPRESSION_ZIP: // ZIP without prediction |
1921 | 0 | case PSDP_COMPRESSION_ZIP_PREDICTION: // ZIP with prediction |
1922 | 0 | { |
1923 | 0 | } |
1924 | 0 | break; |
1925 | | |
1926 | 0 | default: // Unknown format |
1927 | 0 | break; |
1928 | 0 | } |
1929 | | |
1930 | 0 | SAFE_DELETE_ARRAY(line_start); |
1931 | |
|
1932 | 0 | if (cmyk_dib != NULL) { |
1933 | 0 | FreeImage_Unload(cmyk_dib); |
1934 | 0 | } |
1935 | |
|
1936 | 0 | return true; |
1937 | 0 | } |
1938 | | |
1939 | 0 | FIBITMAP* psdParser::Load(FreeImageIO *io, fi_handle handle, int s_format_id, int flags) { |
1940 | 0 | FIBITMAP *Bitmap = NULL; |
1941 | |
|
1942 | 0 | _fi_flags = flags; |
1943 | 0 | _fi_format_id = s_format_id; |
1944 | |
|
1945 | 0 | try { |
1946 | 0 | if (NULL == handle) { |
1947 | 0 | throw("Cannot open file"); |
1948 | 0 | } |
1949 | | |
1950 | 0 | if (!_headerInfo.Read(io, handle)) { |
1951 | 0 | throw("Error in header"); |
1952 | 0 | } |
1953 | | |
1954 | 0 | if (!_colourModeData.Read(io, handle)) { |
1955 | 0 | throw("Error in ColourMode Data"); |
1956 | 0 | } |
1957 | | |
1958 | 0 | if (!ReadImageResources(io, handle)) { |
1959 | 0 | throw("Error in Image Resource"); |
1960 | 0 | } |
1961 | | |
1962 | 0 | if (!ReadLayerAndMaskInfoSection(io, handle)) { |
1963 | 0 | throw("Error in Mask Info"); |
1964 | 0 | } |
1965 | | |
1966 | 0 | Bitmap = ReadImageData(io, handle); |
1967 | 0 | if (NULL == Bitmap) { |
1968 | 0 | throw("Error in Image Data"); |
1969 | 0 | } |
1970 | | |
1971 | | // set resolution info |
1972 | 0 | if(NULL != Bitmap) { |
1973 | 0 | unsigned res_x = 2835; // 72 dpi |
1974 | 0 | unsigned res_y = 2835; // 72 dpi |
1975 | 0 | if (_bResolutionInfoFilled) { |
1976 | 0 | _resolutionInfo.GetResolutionInfo(res_x, res_y); |
1977 | 0 | } |
1978 | 0 | FreeImage_SetDotsPerMeterX(Bitmap, res_x); |
1979 | 0 | FreeImage_SetDotsPerMeterY(Bitmap, res_y); |
1980 | 0 | } |
1981 | | |
1982 | | // set ICC profile |
1983 | 0 | if(NULL != _iccProfile._ProfileData) { |
1984 | 0 | FreeImage_CreateICCProfile(Bitmap, _iccProfile._ProfileData, _iccProfile._ProfileSize); |
1985 | 0 | if ((flags & PSD_CMYK) == PSD_CMYK) { |
1986 | 0 | short mode = _headerInfo._ColourMode; |
1987 | 0 | if((mode == PSDP_CMYK) || (mode == PSDP_MULTICHANNEL)) { |
1988 | 0 | FreeImage_GetICCProfile(Bitmap)->flags |= FIICC_COLOR_IS_CMYK; |
1989 | 0 | } |
1990 | 0 | } |
1991 | 0 | } |
1992 | | |
1993 | | // Metadata |
1994 | 0 | if(NULL != _iptc._Data) { |
1995 | 0 | read_iptc_profile(Bitmap, _iptc._Data, _iptc._Size); |
1996 | 0 | } |
1997 | 0 | if(NULL != _exif1._Data) { |
1998 | 0 | psd_read_exif_profile(Bitmap, _exif1._Data, _exif1._Size); |
1999 | 0 | psd_read_exif_profile_raw(Bitmap, _exif1._Data, _exif1._Size); |
2000 | 0 | } else if(NULL != _exif3._Data) { |
2001 | | // I have not found any files with this resource. |
2002 | | // Assume that we only want one Exif resource. |
2003 | 0 | assert(false); |
2004 | 0 | psd_read_exif_profile(Bitmap, _exif3._Data, _exif3._Size); |
2005 | 0 | psd_read_exif_profile_raw(Bitmap, _exif3._Data, _exif3._Size); |
2006 | 0 | } |
2007 | | |
2008 | | // XMP metadata |
2009 | 0 | if(NULL != _xmp._Data) { |
2010 | 0 | psd_set_xmp_profile(Bitmap, _xmp._Data, _xmp._Size); |
2011 | 0 | } |
2012 | |
|
2013 | 0 | } catch(const char *text) { |
2014 | 0 | FreeImage_OutputMessageProc(s_format_id, text); |
2015 | 0 | } |
2016 | 0 | catch(const std::exception& e) { |
2017 | 0 | FreeImage_OutputMessageProc(s_format_id, "%s", e.what()); |
2018 | 0 | } |
2019 | | |
2020 | 0 | return Bitmap; |
2021 | 0 | } |
2022 | | |
2023 | 0 | bool psdParser::Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { |
2024 | 0 | if (!dib || !handle) { |
2025 | 0 | return false; |
2026 | 0 | } |
2027 | | |
2028 | 0 | _fi_flags = flags; |
2029 | |
|
2030 | 0 | const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); |
2031 | |
|
2032 | 0 | const unsigned width = FreeImage_GetWidth(dib); |
2033 | 0 | const unsigned height = FreeImage_GetHeight(dib); |
2034 | 0 | const unsigned bitsperpixel = FreeImage_GetBPP(dib); |
2035 | |
|
2036 | 0 | const FIICCPROFILE* iccProfile = FreeImage_GetICCProfile(dib); |
2037 | | |
2038 | | // setup out-variables based on dib and flag options |
2039 | |
|
2040 | 0 | unsigned bitspersample; |
2041 | 0 | unsigned samplesperpixel; |
2042 | 0 | short colourMode = PSDP_RGB; |
2043 | |
|
2044 | 0 | if(image_type == FIT_BITMAP) { |
2045 | | // standard image: 1-, 4-, 8-, 16-, 24-, 32-bit |
2046 | 0 | if(bitsperpixel == 32) { |
2047 | | // 32-bit images : check for CMYK or alpha transparency |
2048 | |
|
2049 | 0 | if((((iccProfile->flags & FIICC_COLOR_IS_CMYK) == FIICC_COLOR_IS_CMYK) || ((flags & PSD_CMYK) == PSD_CMYK))) { |
2050 | 0 | colourMode = PSDP_CMYK; |
2051 | 0 | } |
2052 | 0 | samplesperpixel = 4; |
2053 | 0 | } else if(bitsperpixel == 24) { |
2054 | 0 | samplesperpixel = 3; |
2055 | 0 | } else if(bitsperpixel == 8) { |
2056 | 0 | samplesperpixel = 1; |
2057 | 0 | colourMode = PSDP_INDEXED; |
2058 | 0 | } else if(bitsperpixel == 1) { |
2059 | 0 | samplesperpixel = 1; |
2060 | 0 | colourMode = PSDP_BITMAP; |
2061 | 0 | } else { |
2062 | 0 | return false; |
2063 | 0 | } |
2064 | 0 | bitspersample = bitsperpixel / samplesperpixel; |
2065 | 0 | } else if(image_type == FIT_UINT16 || image_type == FIT_INT16) { |
2066 | | // Grayscale |
2067 | 0 | samplesperpixel = 1; |
2068 | 0 | bitspersample = bitsperpixel / samplesperpixel; |
2069 | 0 | colourMode = PSDP_GRAYSCALE; |
2070 | 0 | } else if(image_type == FIT_RGB16) { |
2071 | | // 48-bit RGB |
2072 | 0 | samplesperpixel = 3; |
2073 | 0 | bitspersample = bitsperpixel / samplesperpixel; |
2074 | 0 | } else if(image_type == FIT_RGBA16) { |
2075 | | // 64-bit RGBA |
2076 | 0 | samplesperpixel = 4; |
2077 | 0 | bitspersample = bitsperpixel / samplesperpixel; |
2078 | 0 | if((((iccProfile->flags & FIICC_COLOR_IS_CMYK) == FIICC_COLOR_IS_CMYK) || ((flags & PSD_CMYK) == PSD_CMYK))) { |
2079 | 0 | colourMode = PSDP_CMYK; |
2080 | 0 | } |
2081 | 0 | } else if(image_type == FIT_RGBF) { |
2082 | | // 96-bit RGBF |
2083 | 0 | samplesperpixel = 3; |
2084 | 0 | bitspersample = bitsperpixel / samplesperpixel; |
2085 | 0 | } else if (image_type == FIT_RGBAF) { |
2086 | | // 128-bit RGBAF |
2087 | 0 | samplesperpixel = 4; |
2088 | 0 | bitspersample = bitsperpixel / samplesperpixel; |
2089 | 0 | } else { |
2090 | | // special image type (int, long, double, ...) |
2091 | 0 | samplesperpixel = 1; |
2092 | 0 | bitspersample = bitsperpixel; |
2093 | 0 | } |
2094 | | |
2095 | 0 | _headerInfo._Version = (((flags & PSD_PSB) == PSD_PSB) || width > 30000 || height > 30000) ? 2 : 1; |
2096 | 0 | _headerInfo._Channels = samplesperpixel; |
2097 | 0 | _headerInfo._Height = height; |
2098 | 0 | _headerInfo._Width = width; |
2099 | 0 | _headerInfo._BitsPerChannel = bitsperpixel / samplesperpixel; |
2100 | 0 | _headerInfo._ColourMode = colourMode; |
2101 | 0 | if(!_headerInfo.Write(io, handle)) return false; |
2102 | | |
2103 | 0 | _colourModeData._Length = 0; |
2104 | 0 | _colourModeData._plColourData = NULL; |
2105 | 0 | if (FreeImage_GetPalette(dib) != NULL) { |
2106 | 0 | RGBQUAD *pal = FreeImage_GetPalette(dib); |
2107 | 0 | _colourModeData._Length = FreeImage_GetColorsUsed(dib) * 3; |
2108 | 0 | _colourModeData._plColourData = new BYTE[_colourModeData._Length]; |
2109 | 0 | for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++ ) { |
2110 | 0 | _colourModeData._plColourData[i + 0*256] = pal[i].rgbRed; |
2111 | 0 | _colourModeData._plColourData[i + 1*256] = pal[i].rgbGreen; |
2112 | 0 | _colourModeData._plColourData[i + 2*256] = pal[i].rgbBlue; |
2113 | 0 | } |
2114 | 0 | } |
2115 | |
|
2116 | 0 | if (!_colourModeData.Write(io, handle)) { |
2117 | 0 | return false; |
2118 | 0 | } |
2119 | | |
2120 | 0 | BYTE IntValue[4]; |
2121 | 0 | const long res_start_pos = io->tell_proc(handle); |
2122 | 0 | psdSetValue(IntValue, sizeof(IntValue), 0); |
2123 | 0 | if(io->write_proc(IntValue, sizeof(IntValue), 1, handle) != 1) { |
2124 | 0 | return false; |
2125 | 0 | } |
2126 | | |
2127 | 0 | _resolutionInfo._hRes = (short) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterX(dib)); |
2128 | 0 | _resolutionInfo._hResUnit = 1; // inches |
2129 | 0 | _resolutionInfo._widthUnit = 1; // inches |
2130 | 0 | _resolutionInfo._vRes = (short) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterY(dib)); |
2131 | 0 | _resolutionInfo._vResUnit = 1; // inches |
2132 | 0 | _resolutionInfo._heightUnit = 1; // inches |
2133 | 0 | if (!_resolutionInfo.Write(io, handle)) { |
2134 | 0 | return false; |
2135 | 0 | } |
2136 | | |
2137 | | // psdResolutionInfo_v2 is obsolete - Photoshop 2.0 |
2138 | | |
2139 | 0 | _displayInfo._ColourSpace = (colourMode == PSDP_CMYK) ? 2 : 0; |
2140 | 0 | memset(_displayInfo._Colour, 0, sizeof(_displayInfo._Colour)); |
2141 | 0 | _displayInfo._Opacity = 100; |
2142 | 0 | _displayInfo._Kind = 0; |
2143 | 0 | _displayInfo._padding = 0; |
2144 | 0 | if (!_displayInfo.Write(io, handle)) { |
2145 | 0 | return false; |
2146 | 0 | } |
2147 | | |
2148 | 0 | if(GetThumbnail() == NULL) { |
2149 | 0 | _thumbnail._owned = false; |
2150 | 0 | _thumbnail._dib = FreeImage_GetThumbnail(dib); |
2151 | 0 | } |
2152 | 0 | if(GetThumbnail() != NULL) { |
2153 | 0 | _thumbnail.Init(); |
2154 | 0 | if (!_thumbnail.Write(io, handle, false)) { |
2155 | 0 | return false; |
2156 | 0 | } |
2157 | 0 | } |
2158 | | |
2159 | 0 | if(iccProfile != NULL && iccProfile->size > 0) { |
2160 | 0 | _iccProfile.clear(); |
2161 | 0 | _iccProfile._owned = false; |
2162 | 0 | _iccProfile._ProfileSize = iccProfile->size; |
2163 | 0 | _iccProfile._ProfileData = (BYTE*)iccProfile->data; |
2164 | 0 | if (!_iccProfile.Write(io, handle)) { |
2165 | 0 | return false; |
2166 | 0 | } |
2167 | 0 | } |
2168 | | |
2169 | 0 | if(write_iptc_profile(dib, &_iptc._Data, &_iptc._Size)) { |
2170 | 0 | if (!_iptc.Write(io, handle, PSDP_RES_IPTC_NAA)) { |
2171 | 0 | return false; |
2172 | 0 | } |
2173 | 0 | } |
2174 | | |
2175 | 0 | if(psd_write_exif_profile_raw(dib, &_exif1._Data, &_exif1._Size)) { |
2176 | 0 | _exif1._owned = false; |
2177 | 0 | if (!_exif1.Write(io, handle, PSDP_RES_EXIF1)) { |
2178 | 0 | return false; |
2179 | 0 | } |
2180 | 0 | } |
2181 | | |
2182 | 0 | if(psd_get_xmp_profile(dib, &_xmp._Data, &_xmp._Size)) { |
2183 | 0 | _xmp._owned = false; |
2184 | 0 | if (!_xmp.Write(io, handle, PSDP_RES_XMP)) { |
2185 | 0 | return false; |
2186 | 0 | } |
2187 | 0 | } |
2188 | | |
2189 | | // Fix length of resources |
2190 | 0 | const long current_pos = io->tell_proc(handle); |
2191 | 0 | psdSetValue(IntValue, sizeof(IntValue), (int)(current_pos - res_start_pos - 4)); |
2192 | 0 | io->seek_proc(handle, res_start_pos, SEEK_SET); |
2193 | 0 | if(io->write_proc(IntValue, sizeof(IntValue), 1, handle) != 1) { |
2194 | 0 | return false; |
2195 | 0 | } |
2196 | 0 | io->seek_proc(handle, current_pos, SEEK_SET); |
2197 | |
|
2198 | 0 | if (!WriteLayerAndMaskInfoSection(io, handle)) { |
2199 | 0 | return false; |
2200 | 0 | } |
2201 | 0 | if (!WriteImageData(io, handle, dib)) { |
2202 | 0 | return false; |
2203 | 0 | } |
2204 | | |
2205 | 0 | return true; |
2206 | 0 | } |