Coverage Report

Created: 2026-04-12 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}