blob: 6b393c2a846c19e9291493d0b2f2c77b1a6f4f9d [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP SSSSS DDDD %
7% P P SS D D %
8% PPPP SSS D D %
9% P SS D D %
10% P SSSSS DDDD %
11% %
12% %
13% Read/Write Adobe Photoshop Image Format %
14% %
15% Software Design %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% Leonard Rosenthol %
18% July 1992 %
dirkb41f0802013-12-26 16:47:58 +000019% Dirk Lemstra %
20% December 2013 %
cristy3ed852e2009-09-05 21:47:34 +000021% %
22% %
Cristy7ce65e72015-12-12 18:03:16 -050023% Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000024% dedicated to making software imaging solutions freely available. %
25% %
26% You may not use this file except in compliance with the License. You may %
27% obtain a copy of the License at %
28% %
29% http://www.imagemagick.org/script/license.php %
30% %
31% Unless required by applicable law or agreed to in writing, software %
32% distributed under the License is distributed on an "AS IS" BASIS, %
33% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
34% See the License for the specific language governing permissions and %
35% limitations under the License. %
36% %
37%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
38%
39%
40*/
41
42/*
43 Include declarations.
44*/
cristy4c08aed2011-07-01 19:47:50 +000045#include "MagickCore/studio.h"
46#include "MagickCore/artifact.h"
47#include "MagickCore/attribute.h"
48#include "MagickCore/blob.h"
49#include "MagickCore/blob-private.h"
50#include "MagickCore/cache.h"
dirk18c0e4d2014-02-22 22:24:05 +000051#include "MagickCore/channel.h"
cristy4c08aed2011-07-01 19:47:50 +000052#include "MagickCore/colormap.h"
cristyf432c632014-12-07 15:11:28 +000053#include "MagickCore/colormap-private.h"
cristy4c08aed2011-07-01 19:47:50 +000054#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000055#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000056#include "MagickCore/constitute.h"
57#include "MagickCore/enhance.h"
58#include "MagickCore/exception.h"
59#include "MagickCore/exception-private.h"
60#include "MagickCore/image.h"
61#include "MagickCore/image-private.h"
62#include "MagickCore/list.h"
63#include "MagickCore/log.h"
64#include "MagickCore/magick.h"
65#include "MagickCore/memory_.h"
66#include "MagickCore/module.h"
67#include "MagickCore/monitor-private.h"
dirkb20a1792015-10-15 23:32:37 +020068#include "MagickCore/option.h"
cristy4c08aed2011-07-01 19:47:50 +000069#include "MagickCore/pixel.h"
70#include "MagickCore/pixel-accessor.h"
71#include "MagickCore/profile.h"
72#include "MagickCore/property.h"
73#include "MagickCore/quantum-private.h"
74#include "MagickCore/static.h"
75#include "MagickCore/string_.h"
dirkb20a1792015-10-15 23:32:37 +020076#include "MagickCore/thread-private.h"
dirkb41f0802013-12-26 16:47:58 +000077#ifdef MAGICKCORE_ZLIB_DELEGATE
78#include <zlib.h>
79#endif
dirk14c08dc2015-05-25 11:52:13 +000080#include "psd-private.h"
81
cristy3ed852e2009-09-05 21:47:34 +000082/*
cristy2d3d87f2010-03-01 00:23:08 +000083 Define declaractions.
84*/
cristyaca3ec52010-03-02 14:55:01 +000085#define MaxPSDChannels 56
cristybb503372010-05-27 20:51:26 +000086#define PSDQuantum(x) (((ssize_t) (x)+1) & -2)
cristy2d3d87f2010-03-01 00:23:08 +000087
88/*
89 Enumerated declaractions.
90*/
91typedef enum
92{
dirkb41f0802013-12-26 16:47:58 +000093 Raw = 0,
94 RLE = 1,
95 ZipWithoutPrediction = 2,
96 ZipWithPrediction = 3
97} PSDCompressionType;
98
99typedef enum
100{
cristy2d3d87f2010-03-01 00:23:08 +0000101 BitmapMode = 0,
102 GrayscaleMode = 1,
103 IndexedMode = 2,
104 RGBMode = 3,
105 CMYKMode = 4,
106 MultichannelMode = 7,
107 DuotoneMode = 8,
108 LabMode = 9
109} PSDImageType;
110
111/*
112 Typedef declaractions.
113*/
114typedef struct _ChannelInfo
115{
116 short int
117 type;
118
cristybb503372010-05-27 20:51:26 +0000119 size_t
cristy2d3d87f2010-03-01 00:23:08 +0000120 size;
121} ChannelInfo;
122
dirka41d97f2015-05-25 07:56:17 +0000123typedef struct _MaskInfo
124{
125 Image
126 *image;
127
128 RectangleInfo
129 page;
130
131 unsigned char
132 background,
133 flags;
134} MaskInfo;
135
cristy2d3d87f2010-03-01 00:23:08 +0000136typedef struct _LayerInfo
137{
cristy2d3d87f2010-03-01 00:23:08 +0000138 ChannelInfo
139 channel_info[MaxPSDChannels];
140
141 char
142 blendkey[4];
143
dirka41d97f2015-05-25 07:56:17 +0000144 Image
145 *image;
146
147 MaskInfo
148 mask;
149
cristy2d3d87f2010-03-01 00:23:08 +0000150 Quantum
151 opacity;
152
dirka41d97f2015-05-25 07:56:17 +0000153 RectangleInfo
154 page;
cristy2d3d87f2010-03-01 00:23:08 +0000155
cristybb503372010-05-27 20:51:26 +0000156 size_t
cristy2d3d87f2010-03-01 00:23:08 +0000157 offset_x,
158 offset_y;
159
160 unsigned char
dirka41d97f2015-05-25 07:56:17 +0000161 clipping,
162 flags,
163 name[256],
164 visible;
cristy2d3d87f2010-03-01 00:23:08 +0000165
dirka41d97f2015-05-25 07:56:17 +0000166 unsigned short
167 channels;
cristy2d3d87f2010-03-01 00:23:08 +0000168} LayerInfo;
dirka41d97f2015-05-25 07:56:17 +0000169
cristy2d3d87f2010-03-01 00:23:08 +0000170/*
cristy3ed852e2009-09-05 21:47:34 +0000171 Forward declarations.
172*/
173static MagickBooleanType
cristy3a37efd2011-08-28 20:31:03 +0000174 WritePSDImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000175
176/*
177%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
178% %
179% %
180% %
cristy3ed852e2009-09-05 21:47:34 +0000181% I s P S D %
182% %
183% %
184% %
185%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
186%
187% IsPSD()() returns MagickTrue if the image format type, identified by the
188% magick string, is PSD.
189%
190% The format of the IsPSD method is:
191%
192% MagickBooleanType IsPSD(const unsigned char *magick,const size_t length)
193%
194% A description of each parameter follows:
195%
196% o magick: compare image format pattern against these bytes.
197%
198% o length: Specifies the length of the magick string.
199%
200*/
201static MagickBooleanType IsPSD(const unsigned char *magick,const size_t length)
202{
203 if (length < 4)
204 return(MagickFalse);
205 if (LocaleNCompare((const char *) magick,"8BPS",4) == 0)
206 return(MagickTrue);
207 return(MagickFalse);
208}
209
210/*
211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
212% %
213% %
214% %
215% R e a d P S D I m a g e %
216% %
217% %
218% %
219%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
220%
221% ReadPSDImage() reads an Adobe Photoshop image file and returns it. It
222% allocates the memory necessary for the new Image structure and returns a
223% pointer to the new image.
224%
225% The format of the ReadPSDImage method is:
226%
cristy90de83d2015-04-08 22:04:41 +0000227% Image *ReadPSDImage(image_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000228%
229% A description of each parameter follows:
230%
231% o image_info: the image info.
232%
233% o exception: return any errors or warnings in this structure.
234%
235*/
236
cristy19eb6412010-04-23 14:42:29 +0000237static const char *CompositeOperatorToPSDBlendMode(CompositeOperator op)
cristycd081772010-03-22 17:19:12 +0000238{
239 const char
240 *blend_mode;
241
cristy19eb6412010-04-23 14:42:29 +0000242 switch (op)
cristycd081772010-03-22 17:19:12 +0000243 {
cristyfdc035a2014-06-26 11:29:29 +0000244 case ColorBurnCompositeOp: blend_mode = "idiv"; break;
245 case ColorDodgeCompositeOp: blend_mode = "div "; break;
cristy216b90f2014-04-12 22:59:20 +0000246 case ColorizeCompositeOp: blend_mode = "colr"; break;
247 case DarkenCompositeOp: blend_mode = "dark"; break;
248 case DifferenceCompositeOp: blend_mode = "diff"; break;
249 case DissolveCompositeOp: blend_mode = "diss"; break;
cristyfdc035a2014-06-26 11:29:29 +0000250 case ExclusionCompositeOp: blend_mode = "smud"; break;
cristy216b90f2014-04-12 22:59:20 +0000251 case HardLightCompositeOp: blend_mode = "hLit"; break;
cristyfdc035a2014-06-26 11:29:29 +0000252 case HardMixCompositeOp: blend_mode = "hMix"; break;
cristy216b90f2014-04-12 22:59:20 +0000253 case HueCompositeOp: blend_mode = "hue "; break;
254 case LightenCompositeOp: blend_mode = "lite"; break;
cristyfdc035a2014-06-26 11:29:29 +0000255 case LinearBurnCompositeOp: blend_mode = "lbrn"; break;
256 case LinearDodgeCompositeOp:blend_mode = "lddg"; break;
257 case LinearLightCompositeOp:blend_mode = "lLit"; break;
cristy216b90f2014-04-12 22:59:20 +0000258 case LuminizeCompositeOp: blend_mode = "lum "; break;
259 case MultiplyCompositeOp: blend_mode = "mul "; break;
260 case OverCompositeOp: blend_mode = "norm"; break;
261 case OverlayCompositeOp: blend_mode = "over"; break;
cristyfdc035a2014-06-26 11:29:29 +0000262 case PinLightCompositeOp: blend_mode = "pLit"; break;
cristy216b90f2014-04-12 22:59:20 +0000263 case SaturateCompositeOp: blend_mode = "sat "; break;
264 case ScreenCompositeOp: blend_mode = "scrn"; break;
265 case SoftLightCompositeOp: blend_mode = "sLit"; break;
cristyfdc035a2014-06-26 11:29:29 +0000266 case VividLightCompositeOp: blend_mode = "vLit"; break;
cristy216b90f2014-04-12 22:59:20 +0000267 default: blend_mode = "norm";
cristycd081772010-03-22 17:19:12 +0000268 }
269 return(blend_mode);
270}
271
dirkb20a1792015-10-15 23:32:37 +0200272/*
273 For some reason Photoshop seems to blend semi-transparent pixels with white.
274 This method reverts the blending. This can be disabled by setting the
275 option 'psd:alpha-unblend' to off.
276*/
277static MagickBooleanType CorrectPSDAlphaBlend(const ImageInfo *image_info,
278 Image *image,ExceptionInfo* exception)
279{
280 const char
281 *option;
282
283 MagickBooleanType
284 status;
285
286 ssize_t
287 y;
288
289 if (image->alpha_trait != BlendPixelTrait || image->colorspace != sRGBColorspace)
290 return(MagickTrue);
291 option=GetImageOption(image_info,"psd:alpha-unblend");
292 if (IsStringFalse(option) != MagickFalse)
293 return(MagickTrue);
294 status=MagickTrue;
295#if defined(MAGICKCORE_OPENMP_SUPPORT)
296#pragma omp parallel for schedule(static,4) shared(status) \
297 magick_threads(image,image,image->rows,1)
298#endif
299 for (y=0; y < (ssize_t) image->rows; y++)
300 {
301 register Quantum
dirk05d2ff72015-11-18 23:13:43 +0100302 *magick_restrict q;
dirkb20a1792015-10-15 23:32:37 +0200303
304 register ssize_t
305 x;
306
307 if (status == MagickFalse)
308 continue;
309 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
310 if (q == (Quantum *) NULL)
311 {
312 status=MagickFalse;
313 continue;
314 }
315 for (x=0; x < (ssize_t) image->columns; x++)
316 {
317 double
318 gamma;
319
320 register ssize_t
321 i;
322
323 gamma=QuantumScale*GetPixelAlpha(image, q);
324 if (gamma != 0.0 && gamma != 1.0)
325 {
326 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
327 {
328 PixelChannel channel=GetPixelChannelChannel(image,i);
329 if (channel != AlphaPixelChannel)
330 q[i]=ClampToQuantum((q[i]-((1.0-gamma)*QuantumRange))/gamma);
331 }
332 }
333 q+=GetPixelChannels(image);
334 }
335 if (SyncAuthenticPixels(image,exception) == MagickFalse)
336 status=MagickFalse;
337 }
338
339 return(status);
340}
341
dirkb41f0802013-12-26 16:47:58 +0000342static inline CompressionType ConvertPSDCompression(
343 PSDCompressionType compression)
344{
345 switch (compression)
346 {
347 case RLE:
348 return RLECompression;
349 case ZipWithPrediction:
350 case ZipWithoutPrediction:
351 return ZipCompression;
352 default:
353 return NoCompression;
354 }
355}
356
dirkb20a1792015-10-15 23:32:37 +0200357static MagickBooleanType CorrectPSDOpacity(LayerInfo *layer_info,
dirkb41f0802013-12-26 16:47:58 +0000358 ExceptionInfo *exception)
359{
dirkb20a1792015-10-15 23:32:37 +0200360 MagickBooleanType
361 status;
dirkb41f0802013-12-26 16:47:58 +0000362
363 ssize_t
364 y;
365
366 if (layer_info->opacity == OpaqueAlpha)
367 return(MagickTrue);
368
369 layer_info->image->alpha_trait=BlendPixelTrait;
dirkb20a1792015-10-15 23:32:37 +0200370 status=MagickTrue;
371#if defined(MAGICKCORE_OPENMP_SUPPORT)
372#pragma omp parallel for schedule(static,4) shared(status) \
373 magick_threads(layer_info->image,layer_info->image,layer_info->image->rows,1)
374#endif
dirkb41f0802013-12-26 16:47:58 +0000375 for (y=0; y < (ssize_t) layer_info->image->rows; y++)
376 {
dirkb20a1792015-10-15 23:32:37 +0200377 register Quantum
dirk05d2ff72015-11-18 23:13:43 +0100378 *magick_restrict q;
dirkb20a1792015-10-15 23:32:37 +0200379
380 register ssize_t
381 x;
382
383 if (status == MagickFalse)
384 continue;
cristyd8083a62014-02-01 13:53:44 +0000385 q=GetAuthenticPixels(layer_info->image,0,y,layer_info->image->columns,1,
386 exception);
dirkb20a1792015-10-15 23:32:37 +0200387 if (q == (Quantum *)NULL)
388 {
389 status=MagickFalse;
390 continue;
391 }
dirkb41f0802013-12-26 16:47:58 +0000392 for (x=0; x < (ssize_t) layer_info->image->columns; x++)
393 {
394 SetPixelAlpha(layer_info->image,(Quantum) (QuantumScale*(GetPixelAlpha(
395 layer_info->image,q))*layer_info->opacity),q);
396 q+=GetPixelChannels(layer_info->image);
397 }
398 if (SyncAuthenticPixels(layer_info->image,exception) == MagickFalse)
dirkb20a1792015-10-15 23:32:37 +0200399 status=MagickFalse;
dirkb41f0802013-12-26 16:47:58 +0000400 }
401
dirkb20a1792015-10-15 23:32:37 +0200402 return(status);
dirkb41f0802013-12-26 16:47:58 +0000403}
404
cristycd081772010-03-22 17:19:12 +0000405static ssize_t DecodePSDPixels(const size_t number_compact_pixels,
cristybb503372010-05-27 20:51:26 +0000406 const unsigned char *compact_pixels,const ssize_t depth,
cristycd081772010-03-22 17:19:12 +0000407 const size_t number_pixels,unsigned char *pixels)
408{
dirk891df412014-12-17 07:25:25 +0000409#define CheckNumberCompactPixels \
410 if (packets == 0) \
411 return(i); \
412 packets--
413
414#define CheckNumberPixels(count) \
415 if (((ssize_t) i + count) > (ssize_t) number_pixels) \
416 return(i); \
417 i+=count
418
cristycd081772010-03-22 17:19:12 +0000419 int
420 pixel;
421
422 register ssize_t
423 i,
424 j;
425
cristycd081772010-03-22 17:19:12 +0000426 size_t
427 length;
428
cristy802d3642011-04-27 02:02:41 +0000429 ssize_t
430 packets;
431
cristycd081772010-03-22 17:19:12 +0000432 packets=(ssize_t) number_compact_pixels;
433 for (i=0; (packets > 1) && (i < (ssize_t) number_pixels); )
434 {
dirkdb435e22015-01-24 11:38:04 +0000435 packets--;
dirk40084fa2014-02-03 21:29:38 +0000436 length=(size_t) (*compact_pixels++);
cristycd081772010-03-22 17:19:12 +0000437 if (length == 128)
438 continue;
439 if (length > 128)
440 {
441 length=256-length+1;
dirk891df412014-12-17 07:25:25 +0000442 CheckNumberCompactPixels;
cristycd081772010-03-22 17:19:12 +0000443 pixel=(*compact_pixels++);
cristy284c7d82010-04-24 00:19:14 +0000444 for (j=0; j < (ssize_t) length; j++)
cristycd081772010-03-22 17:19:12 +0000445 {
446 switch (depth)
447 {
448 case 1:
449 {
dirk891df412014-12-17 07:25:25 +0000450 CheckNumberPixels(8);
cristy284c7d82010-04-24 00:19:14 +0000451 *pixels++=(pixel >> 7) & 0x01 ? 0U : 255U;
452 *pixels++=(pixel >> 6) & 0x01 ? 0U : 255U;
453 *pixels++=(pixel >> 5) & 0x01 ? 0U : 255U;
454 *pixels++=(pixel >> 4) & 0x01 ? 0U : 255U;
455 *pixels++=(pixel >> 3) & 0x01 ? 0U : 255U;
456 *pixels++=(pixel >> 2) & 0x01 ? 0U : 255U;
457 *pixels++=(pixel >> 1) & 0x01 ? 0U : 255U;
458 *pixels++=(pixel >> 0) & 0x01 ? 0U : 255U;
cristycd081772010-03-22 17:19:12 +0000459 break;
460 }
461 case 2:
462 {
dirk891df412014-12-17 07:25:25 +0000463 CheckNumberPixels(4);
cristy284c7d82010-04-24 00:19:14 +0000464 *pixels++=(unsigned char) ((pixel >> 6) & 0x03);
465 *pixels++=(unsigned char) ((pixel >> 4) & 0x03);
466 *pixels++=(unsigned char) ((pixel >> 2) & 0x03);
467 *pixels++=(unsigned char) ((pixel & 0x03) & 0x03);
dirk891df412014-12-17 07:25:25 +0000468 break;
469 }
470 case 4:
471 {
472 CheckNumberPixels(2);
473 *pixels++=(unsigned char) ((pixel >> 4) & 0xff);
474 *pixels++=(unsigned char) ((pixel & 0x0f) & 0xff);
cristycd081772010-03-22 17:19:12 +0000475 break;
476 }
477 default:
478 {
dirk891df412014-12-17 07:25:25 +0000479 CheckNumberPixels(1);
cristy284c7d82010-04-24 00:19:14 +0000480 *pixels++=(unsigned char) pixel;
cristycd081772010-03-22 17:19:12 +0000481 break;
482 }
483 }
484 }
485 continue;
486 }
487 length++;
cristy284c7d82010-04-24 00:19:14 +0000488 for (j=0; j < (ssize_t) length; j++)
cristycd081772010-03-22 17:19:12 +0000489 {
490 switch (depth)
491 {
492 case 1:
493 {
dirk891df412014-12-17 07:25:25 +0000494 CheckNumberPixels(8);
cristy284c7d82010-04-24 00:19:14 +0000495 *pixels++=(*compact_pixels >> 7) & 0x01 ? 0U : 255U;
496 *pixels++=(*compact_pixels >> 6) & 0x01 ? 0U : 255U;
497 *pixels++=(*compact_pixels >> 5) & 0x01 ? 0U : 255U;
498 *pixels++=(*compact_pixels >> 4) & 0x01 ? 0U : 255U;
499 *pixels++=(*compact_pixels >> 3) & 0x01 ? 0U : 255U;
500 *pixels++=(*compact_pixels >> 2) & 0x01 ? 0U : 255U;
501 *pixels++=(*compact_pixels >> 1) & 0x01 ? 0U : 255U;
502 *pixels++=(*compact_pixels >> 0) & 0x01 ? 0U : 255U;
cristycd081772010-03-22 17:19:12 +0000503 break;
504 }
505 case 2:
506 {
dirk891df412014-12-17 07:25:25 +0000507 CheckNumberPixels(4);
cristy618a9662010-03-23 13:59:54 +0000508 *pixels++=(*compact_pixels >> 6) & 0x03;
509 *pixels++=(*compact_pixels >> 4) & 0x03;
510 *pixels++=(*compact_pixels >> 2) & 0x03;
511 *pixels++=(*compact_pixels & 0x03) & 0x03;
dirk891df412014-12-17 07:25:25 +0000512 break;
513 }
514 case 4:
515 {
516 CheckNumberPixels(2);
517 *pixels++=(*compact_pixels >> 4) & 0xff;
518 *pixels++=(*compact_pixels & 0x0f) & 0xff;
cristycd081772010-03-22 17:19:12 +0000519 break;
520 }
521 default:
522 {
dirk891df412014-12-17 07:25:25 +0000523 CheckNumberPixels(1);
cristycd081772010-03-22 17:19:12 +0000524 *pixels++=(*compact_pixels);
cristycd081772010-03-22 17:19:12 +0000525 break;
526 }
527 }
dirk891df412014-12-17 07:25:25 +0000528 CheckNumberCompactPixels;
cristycd081772010-03-22 17:19:12 +0000529 compact_pixels++;
530 }
531 }
532 return(i);
533}
534
dirkbd8bd852013-12-28 22:55:19 +0000535static inline LayerInfo *DestroyLayerInfo(LayerInfo *layer_info,
536 const ssize_t number_layers)
537{
538 ssize_t
539 i;
540
541 for (i=0; i<number_layers; i++)
542 {
543 if (layer_info[i].image != (Image *) NULL)
544 layer_info[i].image=DestroyImage(layer_info[i].image);
dirka41d97f2015-05-25 07:56:17 +0000545 if (layer_info[i].mask.image != (Image *) NULL)
546 layer_info[i].mask.image=DestroyImage(layer_info[i].mask.image);
dirkbd8bd852013-12-28 22:55:19 +0000547 }
548
549 return (LayerInfo *) RelinquishMagickMemory(layer_info);
550}
551
dirkb41f0802013-12-26 16:47:58 +0000552static inline size_t GetPSDPacketSize(Image *image)
cristy2d3d87f2010-03-01 00:23:08 +0000553{
dirkb41f0802013-12-26 16:47:58 +0000554 if (image->storage_class == PseudoClass)
555 {
556 if (image->colors > 256)
557 return(2);
558 else if (image->depth > 8)
559 return(2);
560 }
561 else
562 if (image->depth > 8)
563 return(2);
564
565 return(1);
cristy2d3d87f2010-03-01 00:23:08 +0000566}
567
dirk4d9863c2014-03-28 19:48:49 +0000568static inline MagickSizeType GetPSDSize(const PSDInfo *psd_info,Image *image)
cristy2d3d87f2010-03-01 00:23:08 +0000569{
570 if (psd_info->version == 1)
dirk87038d52015-08-11 23:56:45 +0200571 return((MagickSizeType) ReadBlobLong(image));
572 return((MagickSizeType) ReadBlobLongLong(image));
cristy2d3d87f2010-03-01 00:23:08 +0000573}
574
dirkb41f0802013-12-26 16:47:58 +0000575static inline size_t GetPSDRowSize(Image *image)
576{
577 if (image->depth == 1)
578 return((image->columns+7)/8);
579 else
580 return(image->columns*GetPSDPacketSize(image));
581}
582
cristycd081772010-03-22 17:19:12 +0000583static const char *ModeToString(PSDImageType type)
cristy3ed852e2009-09-05 21:47:34 +0000584{
cristycd081772010-03-22 17:19:12 +0000585 switch (type)
cristy3ed852e2009-09-05 21:47:34 +0000586 {
587 case BitmapMode: return "Bitmap";
588 case GrayscaleMode: return "Grayscale";
589 case IndexedMode: return "Indexed";
590 case RGBMode: return "RGB";
591 case CMYKMode: return "CMYK";
592 case MultichannelMode: return "Multichannel";
593 case DuotoneMode: return "Duotone";
594 case LabMode: return "L*A*B";
595 default: return "unknown";
596 }
597}
598
dirkb20a1792015-10-15 23:32:37 +0200599static MagickBooleanType NegateCMYK(Image *image,ExceptionInfo *exception)
dirkd9795a12014-11-11 22:01:21 +0000600{
601 ChannelType
602 channel_mask;
603
dirkb20a1792015-10-15 23:32:37 +0200604 MagickBooleanType
605 status;
606
dirkd9795a12014-11-11 22:01:21 +0000607 channel_mask=SetImageChannelMask(image,(ChannelType)(AllChannels &~
608 AlphaChannel));
dirkb20a1792015-10-15 23:32:37 +0200609 status=NegateImage(image,MagickFalse,exception);
dirkd9795a12014-11-11 22:01:21 +0000610 (void) SetImageChannelMask(image,channel_mask);
dirkb20a1792015-10-15 23:32:37 +0200611 return(status);
dirkd9795a12014-11-11 22:01:21 +0000612}
613
dirk7ec89322014-12-16 22:50:15 +0000614static void ParseImageResourceBlocks(Image *image,
dirk18c0e4d2014-02-22 22:24:05 +0000615 const unsigned char *blocks,size_t length,
616 MagickBooleanType *has_merged_image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000617{
618 const unsigned char
619 *p;
620
621 StringInfo
622 *profile;
623
cristy6befb0f2010-05-31 14:33:15 +0000624 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000625 count,
cristy6befb0f2010-05-31 14:33:15 +0000626 long_sans;
cristy3ed852e2009-09-05 21:47:34 +0000627
628 unsigned short
629 id,
630 short_sans;
631
632 if (length < 16)
dirk7ec89322014-12-16 22:50:15 +0000633 return;
cristyb3f97ae2015-05-18 12:29:32 +0000634 profile=BlobToStringInfo((const unsigned char *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000635 SetStringInfoDatum(profile,blocks);
cristyd15e6592011-10-15 00:13:06 +0000636 (void) SetImageProfile(image,"8bim",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000637 profile=DestroyStringInfo(profile);
638 for (p=blocks; (p >= blocks) && (p < (blocks+length-16)); )
639 {
640 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
641 break;
cristy6befb0f2010-05-31 14:33:15 +0000642 p=PushLongPixel(MSBEndian,p,&long_sans);
cristyf11065e2010-05-14 13:26:59 +0000643 p=PushShortPixel(MSBEndian,p,&id);
644 p=PushShortPixel(MSBEndian,p,&short_sans);
645 p=PushLongPixel(MSBEndian,p,&count);
dirk7ec89322014-12-16 22:50:15 +0000646 if (p+count > blocks+length)
647 return;
cristy3ed852e2009-09-05 21:47:34 +0000648 switch (id)
649 {
650 case 0x03ed:
651 {
cristy1e4a80b2010-05-14 16:18:26 +0000652 char
cristy151b66d2015-04-15 10:50:31 +0000653 value[MagickPathExtent];
cristy1e4a80b2010-05-14 16:18:26 +0000654
cristy3ed852e2009-09-05 21:47:34 +0000655 unsigned short
656 resolution;
657
658 /*
659 Resolution info.
660 */
cristyf11065e2010-05-14 13:26:59 +0000661 p=PushShortPixel(MSBEndian,p,&resolution);
cristy2a11bef2011-10-28 18:33:11 +0000662 image->resolution.x=(double) resolution;
cristy151b66d2015-04-15 10:50:31 +0000663 (void) FormatLocaleString(value,MagickPathExtent,"%g",image->resolution.x);
cristyd15e6592011-10-15 00:13:06 +0000664 (void) SetImageProperty(image,"tiff:XResolution",value,exception);
cristyf11065e2010-05-14 13:26:59 +0000665 p=PushShortPixel(MSBEndian,p,&short_sans);
666 p=PushShortPixel(MSBEndian,p,&short_sans);
667 p=PushShortPixel(MSBEndian,p,&short_sans);
668 p=PushShortPixel(MSBEndian,p,&resolution);
cristy2a11bef2011-10-28 18:33:11 +0000669 image->resolution.y=(double) resolution;
cristy151b66d2015-04-15 10:50:31 +0000670 (void) FormatLocaleString(value,MagickPathExtent,"%g",image->resolution.y);
cristyd15e6592011-10-15 00:13:06 +0000671 (void) SetImageProperty(image,"tiff:YResolution",value,exception);
cristyf11065e2010-05-14 13:26:59 +0000672 p=PushShortPixel(MSBEndian,p,&short_sans);
673 p=PushShortPixel(MSBEndian,p,&short_sans);
674 p=PushShortPixel(MSBEndian,p,&short_sans);
cristy15893bd2012-12-19 14:40:13 +0000675 image->units=PixelsPerInchResolution;
cristy3ed852e2009-09-05 21:47:34 +0000676 break;
677 }
dirk18c0e4d2014-02-22 22:24:05 +0000678 case 0x0421:
679 {
680 if (*(p+4) == 0)
681 *has_merged_image=MagickFalse;
dirkc3378432014-05-18 06:50:37 +0000682 p+=count;
683 break;
dirk18c0e4d2014-02-22 22:24:05 +0000684 }
cristy3ed852e2009-09-05 21:47:34 +0000685 default:
686 {
687 p+=count;
688 break;
689 }
690 }
691 if ((count & 0x01) != 0)
692 p++;
693 }
dirk7ec89322014-12-16 22:50:15 +0000694 return;
cristy3ed852e2009-09-05 21:47:34 +0000695}
696
cristycd081772010-03-22 17:19:12 +0000697static CompositeOperator PSDBlendModeToCompositeOperator(const char *mode)
cristy56ed31c2010-03-22 00:46:21 +0000698{
cristycd081772010-03-22 17:19:12 +0000699 if (mode == (const char *) NULL)
700 return(OverCompositeOp);
701 if (LocaleNCompare(mode,"norm",4) == 0)
702 return(OverCompositeOp);
703 if (LocaleNCompare(mode,"mul ",4) == 0)
704 return(MultiplyCompositeOp);
705 if (LocaleNCompare(mode,"diss",4) == 0)
706 return(DissolveCompositeOp);
707 if (LocaleNCompare(mode,"diff",4) == 0)
708 return(DifferenceCompositeOp);
709 if (LocaleNCompare(mode,"dark",4) == 0)
710 return(DarkenCompositeOp);
711 if (LocaleNCompare(mode,"lite",4) == 0)
712 return(LightenCompositeOp);
713 if (LocaleNCompare(mode,"hue ",4) == 0)
714 return(HueCompositeOp);
715 if (LocaleNCompare(mode,"sat ",4) == 0)
716 return(SaturateCompositeOp);
717 if (LocaleNCompare(mode,"colr",4) == 0)
718 return(ColorizeCompositeOp);
719 if (LocaleNCompare(mode,"lum ",4) == 0)
720 return(LuminizeCompositeOp);
721 if (LocaleNCompare(mode,"scrn",4) == 0)
722 return(ScreenCompositeOp);
723 if (LocaleNCompare(mode,"over",4) == 0)
724 return(OverlayCompositeOp);
725 if (LocaleNCompare(mode,"hLit",4) == 0)
cristy216b90f2014-04-12 22:59:20 +0000726 return(HardLightCompositeOp);
cristycd081772010-03-22 17:19:12 +0000727 if (LocaleNCompare(mode,"sLit",4) == 0)
cristy216b90f2014-04-12 22:59:20 +0000728 return(SoftLightCompositeOp);
cristycd081772010-03-22 17:19:12 +0000729 if (LocaleNCompare(mode,"smud",4) == 0)
cristyfdc035a2014-06-26 11:29:29 +0000730 return(ExclusionCompositeOp);
cristycd081772010-03-22 17:19:12 +0000731 if (LocaleNCompare(mode,"div ",4) == 0)
cristyfdc035a2014-06-26 11:29:29 +0000732 return(ColorDodgeCompositeOp);
cristycd081772010-03-22 17:19:12 +0000733 if (LocaleNCompare(mode,"idiv",4) == 0)
cristyfdc035a2014-06-26 11:29:29 +0000734 return(ColorBurnCompositeOp);
735 if (LocaleNCompare(mode,"lbrn",4) == 0)
736 return(LinearBurnCompositeOp);
737 if (LocaleNCompare(mode,"lddg",4) == 0)
738 return(LinearDodgeCompositeOp);
739 if (LocaleNCompare(mode,"lLit",4) == 0)
740 return(LinearLightCompositeOp);
741 if (LocaleNCompare(mode,"vLit",4) == 0)
742 return(VividLightCompositeOp);
743 if (LocaleNCompare(mode,"pLit",4) == 0)
744 return(PinLightCompositeOp);
745 if (LocaleNCompare(mode,"hMix",4) == 0)
746 return(HardMixCompositeOp);
cristycd081772010-03-22 17:19:12 +0000747 return(OverCompositeOp);
cristy56ed31c2010-03-22 00:46:21 +0000748}
749
dirk87038d52015-08-11 23:56:45 +0200750static inline void ReversePSDString(Image *image,char *p,size_t length)
751{
752 char
753 *q;
754
755 if (image->endian == MSBEndian)
756 return;
757
758 q=p+length;
759 for(--q; p < q; ++p, --q)
760 {
761 *p = *p ^ *q,
762 *q = *p ^ *q,
763 *p = *p ^ *q;
764 }
765}
766
dirk280215b2016-01-16 00:07:51 +0100767static inline void SetPSDPixel(Image *image,const size_t channels,
768 const ssize_t type,const size_t packet_size,const Quantum pixel,Quantum *q,
769 ExceptionInfo *exception)
770{
771 if (image->storage_class == PseudoClass)
772 {
773 if (packet_size == 1)
774 SetPixelIndex(image,ScaleQuantumToChar(pixel),q);
775 else
776 SetPixelIndex(image,ScaleQuantumToShort(pixel),q);
777 SetPixelViaPixelInfo(image,image->colormap+(ssize_t)
778 ConstrainColormapIndex(image,GetPixelIndex(image,q),exception),q);
779 return;
780 }
781 switch (type)
782 {
783 case -1:
784 {
785 SetPixelAlpha(image, pixel,q);
786 break;
787 }
788 case -2:
789 case 0:
790 {
791 SetPixelRed(image,pixel,q);
792 if (channels == 1 || type == -2)
793 SetPixelGray(image,pixel,q);
794 break;
795 }
796 case 1:
797 {
798 if (image->storage_class == PseudoClass)
799 SetPixelAlpha(image,pixel,q);
800 else
801 SetPixelGreen(image,pixel,q);
802 break;
803 }
804 case 2:
805 {
806 if (image->storage_class == PseudoClass)
807 SetPixelAlpha(image,pixel,q);
808 else
809 SetPixelBlue(image,pixel,q);
810 break;
811 }
812 case 3:
813 {
814 if (image->colorspace == CMYKColorspace)
815 SetPixelBlack(image,pixel,q);
816 else
817 if (image->alpha_trait != UndefinedPixelTrait)
818 SetPixelAlpha(image,pixel,q);
819 break;
820 }
821 case 4:
822 {
823 if ((IssRGBCompatibleColorspace(image->colorspace) != MagickFalse) &&
824 (channels > 3))
825 break;
826 if (image->alpha_trait != UndefinedPixelTrait)
827 SetPixelAlpha(image,pixel,q);
828 break;
829 }
830 }
831}
832
dirkb20a1792015-10-15 23:32:37 +0200833static MagickBooleanType ReadPSDChannelPixels(Image *image,
dirkb41f0802013-12-26 16:47:58 +0000834 const size_t channels,const size_t row,const ssize_t type,
835 const unsigned char *pixels,ExceptionInfo *exception)
cristy56ed31c2010-03-22 00:46:21 +0000836{
cristy56ed31c2010-03-22 00:46:21 +0000837 Quantum
838 pixel;
839
cristy7753b2a2011-02-19 18:36:52 +0000840 register const unsigned char
841 *p;
842
cristy4c08aed2011-07-01 19:47:50 +0000843 register Quantum
cristy56ed31c2010-03-22 00:46:21 +0000844 *q;
845
cristy7753b2a2011-02-19 18:36:52 +0000846 register ssize_t
847 x;
cristy56ed31c2010-03-22 00:46:21 +0000848
849 size_t
850 packet_size;
851
dirkb41f0802013-12-26 16:47:58 +0000852 unsigned short
853 nibble;
854
855 p=pixels;
856 q=GetAuthenticPixels(image,0,row,image->columns,1,exception);
857 if (q == (Quantum *) NULL)
858 return MagickFalse;
859 packet_size=GetPSDPacketSize(image);
860 for (x=0; x < (ssize_t) image->columns; x++)
861 {
862 if (packet_size == 1)
863 pixel=ScaleCharToQuantum(*p++);
864 else
865 {
866 p=PushShortPixel(MSBEndian,p,&nibble);
867 pixel=ScaleShortToQuantum(nibble);
868 }
dirk280215b2016-01-16 00:07:51 +0100869 if (image->depth > 1)
dirkb41f0802013-12-26 16:47:58 +0000870 {
dirk280215b2016-01-16 00:07:51 +0100871 SetPSDPixel(image,channels,type,packet_size,pixel,q,exception);
872 q+=GetPixelChannels(image);
dirkb41f0802013-12-26 16:47:58 +0000873 }
dirk280215b2016-01-16 00:07:51 +0100874 else
dirkb41f0802013-12-26 16:47:58 +0000875 {
dirk280215b2016-01-16 00:07:51 +0100876 ssize_t
877 bit,
878 number_bits;
879
880 number_bits=image->columns-x;
881 if (number_bits > 8)
882 number_bits=8;
883 for (bit = 0; bit < number_bits; bit++)
884 {
885 SetPSDPixel(image,channels,type,packet_size,(((unsigned char) pixel)
886 & (0x01 << (7-bit))) != 0 ? 0 : 255,q,exception);
887 q+=GetPixelChannels(image);
888 x++;
889 }
890 if (x != image->columns)
891 x--;
892 continue;
dirkb41f0802013-12-26 16:47:58 +0000893 }
dirkb41f0802013-12-26 16:47:58 +0000894 }
895 return(SyncAuthenticPixels(image,exception));
896}
897
dirkb20a1792015-10-15 23:32:37 +0200898static MagickBooleanType ReadPSDChannelRaw(Image *image,const size_t channels,
dirkb41f0802013-12-26 16:47:58 +0000899 const ssize_t type,ExceptionInfo *exception)
900{
dirkb20a1792015-10-15 23:32:37 +0200901 MagickBooleanType
dirkb41f0802013-12-26 16:47:58 +0000902 status;
903
904 size_t
905 count,
906 row_size;
907
908 ssize_t
909 y;
910
911 unsigned char
912 *pixels;
913
914 if (image->debug != MagickFalse)
915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
916 " layer data is RAW");
917
918 row_size=GetPSDRowSize(image);
dirk891df412014-12-17 07:25:25 +0000919 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
dirkb41f0802013-12-26 16:47:58 +0000920 if (pixels == (unsigned char *) NULL)
921 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
922 image->filename);
923
924 status=MagickTrue;
925 for (y=0; y < (ssize_t) image->rows; y++)
926 {
927 status=MagickFalse;
928
929 count=ReadBlob(image,row_size,pixels);
930 if (count != row_size)
931 break;
932
933 status=ReadPSDChannelPixels(image,channels,y,type,pixels,exception);
934 if (status == MagickFalse)
935 break;
936 }
937
938 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
939 return(status);
940}
941
942static inline MagickOffsetType *ReadPSDRLEOffsets(Image *image,
dirk4d9863c2014-03-28 19:48:49 +0000943 const PSDInfo *psd_info,const size_t size)
dirkb41f0802013-12-26 16:47:58 +0000944{
945 MagickOffsetType
946 *offsets;
947
948 ssize_t
949 y;
950
951 offsets=(MagickOffsetType *) AcquireQuantumMemory(size,sizeof(*offsets));
952 if(offsets != (MagickOffsetType *) NULL)
953 {
954 for (y=0; y < (ssize_t) size; y++)
955 {
956 if (psd_info->version == 1)
dirk87038d52015-08-11 23:56:45 +0200957 offsets[y]=(MagickOffsetType) ReadBlobShort(image);
dirkb41f0802013-12-26 16:47:58 +0000958 else
dirk87038d52015-08-11 23:56:45 +0200959 offsets[y]=(MagickOffsetType) ReadBlobLong(image);
dirkb41f0802013-12-26 16:47:58 +0000960 }
961 }
962 return offsets;
963}
964
dirkb20a1792015-10-15 23:32:37 +0200965static MagickBooleanType ReadPSDChannelRLE(Image *image,const PSDInfo *psd_info,
dirkb41f0802013-12-26 16:47:58 +0000966 const ssize_t type,MagickOffsetType *offsets,ExceptionInfo *exception)
967{
dirkb20a1792015-10-15 23:32:37 +0200968 MagickBooleanType
dirkb41f0802013-12-26 16:47:58 +0000969 status;
970
971 size_t
972 length,
973 row_size;
974
cristy56ed31c2010-03-22 00:46:21 +0000975 ssize_t
cristyd05dca12010-07-25 02:32:32 +0000976 count,
977 y;
cristy56ed31c2010-03-22 00:46:21 +0000978
979 unsigned char
980 *compact_pixels,
981 *pixels;
982
dirkb41f0802013-12-26 16:47:58 +0000983 if (image->debug != MagickFalse)
984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
985 " layer data is RLE compressed");
cristy56ed31c2010-03-22 00:46:21 +0000986
dirkb41f0802013-12-26 16:47:58 +0000987 row_size=GetPSDRowSize(image);
dirk891df412014-12-17 07:25:25 +0000988 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
cristy56ed31c2010-03-22 00:46:21 +0000989 if (pixels == (unsigned char *) NULL)
990 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
991 image->filename);
cristye195f262010-04-16 18:12:35 +0000992
dirkb41f0802013-12-26 16:47:58 +0000993 length=0;
994 for (y=0; y < (ssize_t) image->rows; y++)
995 if ((MagickOffsetType) length < offsets[y])
996 length=(size_t) offsets[y];
997
998 if (length > row_size + 256) // arbitrary number
999 {
1000 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1001 ThrowBinaryException(ResourceLimitError,"InvalidLength",
1002 image->filename);
cristy56ed31c2010-03-22 00:46:21 +00001003 }
dirkb41f0802013-12-26 16:47:58 +00001004
dirk891df412014-12-17 07:25:25 +00001005 compact_pixels=(unsigned char *) AcquireQuantumMemory(length,sizeof(*pixels));
dirkb41f0802013-12-26 16:47:58 +00001006 if (compact_pixels == (unsigned char *) NULL)
1007 {
1008 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1009 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1010 image->filename);
1011 }
1012
1013 (void) ResetMagickMemory(compact_pixels,0,length*sizeof(*compact_pixels));
1014
1015 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +00001016 for (y=0; y < (ssize_t) image->rows; y++)
cristy56ed31c2010-03-22 00:46:21 +00001017 {
dirkb41f0802013-12-26 16:47:58 +00001018 status=MagickFalse;
cristy3b004082010-08-12 19:56:38 +00001019
dirkb41f0802013-12-26 16:47:58 +00001020 count=ReadBlob(image,(size_t) offsets[y],compact_pixels);
1021 if (count != (ssize_t) offsets[y])
1022 break;
1023
1024 count=DecodePSDPixels((size_t) offsets[y],compact_pixels,
1025 (ssize_t) (image->depth == 1 ? 123456 : image->depth),row_size,pixels);
1026 if (count != (ssize_t) row_size)
1027 break;
1028
1029 status=ReadPSDChannelPixels(image,psd_info->channels,y,type,pixels,
1030 exception);
1031 if (status == MagickFalse)
cristy56ed31c2010-03-22 00:46:21 +00001032 break;
1033 }
dirkb41f0802013-12-26 16:47:58 +00001034
1035 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
cristy56ed31c2010-03-22 00:46:21 +00001036 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
dirkb41f0802013-12-26 16:47:58 +00001037 return(status);
cristy56ed31c2010-03-22 00:46:21 +00001038}
1039
dirkb41f0802013-12-26 16:47:58 +00001040#ifdef MAGICKCORE_ZLIB_DELEGATE
dirkb20a1792015-10-15 23:32:37 +02001041static MagickBooleanType ReadPSDChannelZip(Image *image,const size_t channels,
cristyff30f832014-11-05 14:04:21 +00001042 const ssize_t type,const PSDCompressionType compression,
1043 const size_t compact_size,ExceptionInfo *exception)
1044{
dirkb20a1792015-10-15 23:32:37 +02001045 MagickBooleanType
dirkb41f0802013-12-26 16:47:58 +00001046 status;
1047
1048 register unsigned char
1049 *p;
1050
1051 size_t
1052 count,
1053 length,
1054 packet_size,
1055 row_size;
1056
1057 ssize_t
1058 y;
1059
1060 unsigned char
1061 *compact_pixels,
1062 *pixels;
1063
1064 z_stream
1065 stream;
1066
1067 if (image->debug != MagickFalse)
1068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
dirk245cc752014-01-16 18:31:58 +00001069 " layer data is ZIP compressed");
dirkb41f0802013-12-26 16:47:58 +00001070
1071 compact_pixels=(unsigned char *) AcquireQuantumMemory(compact_size,
1072 sizeof(*compact_pixels));
1073 if (compact_pixels == (unsigned char *) NULL)
1074 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1075 image->filename);
1076
1077 packet_size=GetPSDPacketSize(image);
1078 row_size=image->columns*packet_size;
1079 count=image->rows*row_size;
1080
1081 pixels=(unsigned char *) AcquireQuantumMemory(count,sizeof(*pixels));
1082 if (pixels == (unsigned char *) NULL)
1083 {
1084 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1085 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1086 image->filename);
1087 }
1088
1089 ResetMagickMemory(&stream, 0, sizeof(z_stream));
1090 stream.data_type=Z_BINARY;
1091 (void) ReadBlob(image,compact_size,compact_pixels);
1092
1093 stream.next_in=(Bytef *)compact_pixels;
cristy0e274112014-03-25 14:09:19 +00001094 stream.avail_in=(unsigned int) compact_size;
dirkb41f0802013-12-26 16:47:58 +00001095 stream.next_out=(Bytef *)pixels;
cristy0e274112014-03-25 14:09:19 +00001096 stream.avail_out=(unsigned int) count;
dirkb41f0802013-12-26 16:47:58 +00001097
1098 if(inflateInit(&stream) == Z_OK)
1099 {
1100 int
1101 ret;
1102
1103 while (stream.avail_out > 0)
1104 {
1105 ret=inflate(&stream, Z_SYNC_FLUSH);
1106 if (ret != Z_OK && ret != Z_STREAM_END)
1107 {
1108 compact_pixels=(unsigned char *) RelinquishMagickMemory(
1109 compact_pixels);
1110 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1111 return(MagickFalse);
1112 }
1113 }
1114 }
1115
1116 if (compression == ZipWithPrediction)
1117 {
1118 p=pixels;
1119 while(count > 0)
1120 {
1121 length=image->columns;
1122 while(--length)
1123 {
1124 if (packet_size == 2)
1125 {
1126 p[2]+=p[0]+((p[1]+p[3]) >> 8);
1127 p[3]+=p[1];
1128 }
1129 else
1130 *(p+1)+=*p;
1131 p+=packet_size;
1132 }
1133 p+=packet_size;
1134 count-=row_size;
1135 }
1136 }
1137
1138 status=MagickTrue;
1139 p=pixels;
1140 for (y=0; y < (ssize_t) image->rows; y++)
1141 {
1142 status=ReadPSDChannelPixels(image,channels,y,type,p,exception);
1143 if (status == MagickFalse)
1144 break;
1145
1146 p+=row_size;
1147 }
1148
1149 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1150 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1151 return(status);
dirkb41f0802013-12-26 16:47:58 +00001152}
cristy0d67b922014-11-05 19:29:56 +00001153#endif
dirkb41f0802013-12-26 16:47:58 +00001154
dirkb20a1792015-10-15 23:32:37 +02001155static MagickBooleanType ReadPSDChannel(Image *image,const PSDInfo *psd_info,
dirka41d97f2015-05-25 07:56:17 +00001156 LayerInfo* layer_info,const size_t channel,
dirkb41f0802013-12-26 16:47:58 +00001157 const PSDCompressionType compression,ExceptionInfo *exception)
1158{
dirka41d97f2015-05-25 07:56:17 +00001159 Image
1160 *channel_image,
1161 *mask;
1162
dirkb41f0802013-12-26 16:47:58 +00001163 MagickOffsetType
1164 offset;
1165
dirkb20a1792015-10-15 23:32:37 +02001166 MagickBooleanType
dirkb41f0802013-12-26 16:47:58 +00001167 status;
1168
dirka41d97f2015-05-25 07:56:17 +00001169 channel_image=image;
1170 mask=(Image *) NULL;
dirk245cc752014-01-16 18:31:58 +00001171 if (layer_info->channel_info[channel].type < -1)
1172 {
dirka41d97f2015-05-25 07:56:17 +00001173 /*
1174 Ignore mask that is not a user supplied layer mask, if the mask is
1175 disabled or if the flags have unsupported values.
1176 */
1177 if (layer_info->channel_info[channel].type != -2 ||
1178 (layer_info->mask.flags > 3) || (layer_info->mask.flags & 0x02))
1179 {
1180 SeekBlob(image,layer_info->channel_info[channel].size-2,SEEK_CUR);
1181 return(MagickTrue);
1182 }
1183 mask=CloneImage(image,layer_info->mask.page.width,
1184 layer_info->mask.page.height,MagickFalse,exception);
1185 SetImageType(mask,GrayscaleType,exception);
1186 channel_image=mask;
dirk245cc752014-01-16 18:31:58 +00001187 }
1188
dirka41d97f2015-05-25 07:56:17 +00001189 offset=TellBlob(channel_image);
dirkb41f0802013-12-26 16:47:58 +00001190 status=MagickTrue;
1191 switch(compression)
1192 {
1193 case Raw:
dirkfe3a57e2015-12-18 21:33:46 +01001194 status=ReadPSDChannelRaw(channel_image,psd_info->channels,
1195 layer_info->channel_info[channel].type,exception);
1196 break;
dirkb41f0802013-12-26 16:47:58 +00001197 case RLE:
1198 {
1199 MagickOffsetType
1200 *offsets;
1201
dirka41d97f2015-05-25 07:56:17 +00001202 offsets=ReadPSDRLEOffsets(channel_image,psd_info,channel_image->rows);
dirkb41f0802013-12-26 16:47:58 +00001203 if (offsets == (MagickOffsetType *) NULL)
1204 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1205 image->filename);
dirka41d97f2015-05-25 07:56:17 +00001206 status=ReadPSDChannelRLE(channel_image,psd_info,
1207 layer_info->channel_info[channel].type,offsets,exception);
dirkb41f0802013-12-26 16:47:58 +00001208 offsets=(MagickOffsetType *) RelinquishMagickMemory(offsets);
1209 }
1210 break;
1211 case ZipWithPrediction:
1212 case ZipWithoutPrediction:
1213#ifdef MAGICKCORE_ZLIB_DELEGATE
dirka41d97f2015-05-25 07:56:17 +00001214 status=ReadPSDChannelZip(channel_image,layer_info->channels,
dirkb41f0802013-12-26 16:47:58 +00001215 layer_info->channel_info[channel].type,compression,
1216 layer_info->channel_info[channel].size-2,exception);
1217#else
1218 SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
1219 (void) ThrowMagickException(exception,GetMagickModule(),
1220 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
1221 "'%s' (ZLIB)",image->filename);
1222#endif
1223 break;
1224 default:
1225 SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
1226 (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
1227 "CompressionNotSupported","'%.20g'",(double) compression);
1228 break;
1229 }
1230
1231 if (status == MagickFalse)
dirkd5a91322015-12-10 11:18:19 +01001232 {
1233 if (mask != (Image *) NULL)
1234 DestroyImage(mask);
1235 SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
1236 ThrowBinaryException(CoderError,"UnableToDecompressImage",
1237 image->filename);
1238 }
dirka41d97f2015-05-25 07:56:17 +00001239 if (mask != (Image *) NULL)
1240 {
1241 if (status != MagickFalse)
1242 {
1243 PixelInfo
1244 color;
1245
1246 layer_info->mask.image=CloneImage(image,image->columns,image->rows,
1247 MagickTrue,exception);
1248 layer_info->mask.image->alpha_trait=UndefinedPixelTrait;
1249 GetPixelInfo(layer_info->mask.image,&color);
1250 color.red=layer_info->mask.background == 0 ? 0 : QuantumRange;
1251 SetImageColor(layer_info->mask.image,&color,exception);
1252 (void) CompositeImage(layer_info->mask.image,mask,OverCompositeOp,
1253 MagickTrue,layer_info->mask.page.x,layer_info->mask.page.y,
1254 exception);
1255 }
1256 DestroyImage(mask);
1257 }
dirkb41f0802013-12-26 16:47:58 +00001258
1259 return(status);
1260}
1261
dirkb20a1792015-10-15 23:32:37 +02001262static MagickBooleanType ReadPSDLayer(Image *image,const PSDInfo *psd_info,
dirkb41f0802013-12-26 16:47:58 +00001263 LayerInfo* layer_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001264{
cristy3ed852e2009-09-05 21:47:34 +00001265 char
cristy151b66d2015-04-15 10:50:31 +00001266 message[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001267
dirkb20a1792015-10-15 23:32:37 +02001268 MagickBooleanType
dirkb41f0802013-12-26 16:47:58 +00001269 status;
1270
1271 PSDCompressionType
1272 compression;
1273
1274 ssize_t
1275 j;
1276
1277 if (image->debug != MagickFalse)
1278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1279 " setting up new layer image");
1280 (void) SetImageBackgroundColor(layer_info->image,exception);
1281 layer_info->image->compose=PSDBlendModeToCompositeOperator(
1282 layer_info->blendkey);
1283 if (layer_info->visible == MagickFalse)
1284 layer_info->image->compose=NoCompositeOp;
1285 if (psd_info->mode == CMYKMode)
1286 SetImageColorspace(layer_info->image,CMYKColorspace,exception);
cristyd8083a62014-02-01 13:53:44 +00001287 if ((psd_info->mode == BitmapMode) || (psd_info->mode == GrayscaleMode) ||
dirkb41f0802013-12-26 16:47:58 +00001288 (psd_info->mode == DuotoneMode))
1289 SetImageColorspace(layer_info->image,GRAYColorspace,exception);
1290 /*
1291 Set up some hidden attributes for folks that need them.
1292 */
dirka41d97f2015-05-25 07:56:17 +00001293 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
dirkb41f0802013-12-26 16:47:58 +00001294 (double) layer_info->page.x);
1295 (void) SetImageArtifact(layer_info->image,"psd:layer.x",message);
cristy151b66d2015-04-15 10:50:31 +00001296 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
dirkb41f0802013-12-26 16:47:58 +00001297 (double) layer_info->page.y);
1298 (void) SetImageArtifact(layer_info->image,"psd:layer.y",message);
cristy151b66d2015-04-15 10:50:31 +00001299 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",(double)
dirkb41f0802013-12-26 16:47:58 +00001300 layer_info->opacity);
1301 (void) SetImageArtifact(layer_info->image,"psd:layer.opacity",message);
1302 (void) SetImageProperty(layer_info->image,"label",(char *) layer_info->name,
1303 exception);
1304
1305 status=MagickTrue;
dirkb41f0802013-12-26 16:47:58 +00001306 for (j=0; j < (ssize_t) layer_info->channels; j++)
1307 {
1308 if (image->debug != MagickFalse)
1309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1310 " reading data for channel %.20g",(double) j);
1311
dirk87038d52015-08-11 23:56:45 +02001312 compression=(PSDCompressionType) ReadBlobShort(layer_info->image);
dirkb41f0802013-12-26 16:47:58 +00001313 layer_info->image->compression=ConvertPSDCompression(compression);
dirk6f205312014-01-31 22:25:10 +00001314 if (layer_info->channel_info[j].type == -1)
1315 layer_info->image->alpha_trait=BlendPixelTrait;
dirkd9795a12014-11-11 22:01:21 +00001316
dirk6f205312014-01-31 22:25:10 +00001317 status=ReadPSDChannel(layer_info->image,psd_info,layer_info,j,
1318 compression,exception);
dirkb41f0802013-12-26 16:47:58 +00001319
1320 if (status == MagickFalse)
1321 break;
1322 }
1323
dirk6f205312014-01-31 22:25:10 +00001324 if (status != MagickFalse)
dirkb41f0802013-12-26 16:47:58 +00001325 status=CorrectPSDOpacity(layer_info,exception);
1326
dirkb20a1792015-10-15 23:32:37 +02001327 if ((status != MagickFalse) &&
1328 (layer_info->image->colorspace == CMYKColorspace))
1329 status=NegateCMYK(layer_info->image,exception);
1330
1331 if ((status != MagickFalse) && (layer_info->mask.image != (Image *) NULL))
dirka41d97f2015-05-25 07:56:17 +00001332 {
dirkb20a1792015-10-15 23:32:37 +02001333 status=CompositeImage(layer_info->image,layer_info->mask.image,
1334 CopyAlphaCompositeOp,MagickTrue,0,0,exception);
1335 layer_info->mask.image=DestroyImage(layer_info->mask.image);
dirka41d97f2015-05-25 07:56:17 +00001336 }
dirkb41f0802013-12-26 16:47:58 +00001337
1338 return(status);
1339}
1340
dirkb20a1792015-10-15 23:32:37 +02001341ModuleExport MagickBooleanType ReadPSDLayers(Image *image,
dirk14c08dc2015-05-25 11:52:13 +00001342 const ImageInfo *image_info,const PSDInfo *psd_info,
1343 const MagickBooleanType skip_layers,ExceptionInfo *exception)
dirkb41f0802013-12-26 16:47:58 +00001344{
1345 char
1346 type[4];
cristy3ed852e2009-09-05 21:47:34 +00001347
cristy3ed852e2009-09-05 21:47:34 +00001348 LayerInfo
1349 *layer_info;
1350
cristy56ed31c2010-03-22 00:46:21 +00001351 MagickSizeType
cristy56ed31c2010-03-22 00:46:21 +00001352 size;
1353
dirkb20a1792015-10-15 23:32:37 +02001354 MagickBooleanType
dirkb41f0802013-12-26 16:47:58 +00001355 status;
cristydede49f2010-08-20 20:26:26 +00001356
cristybb503372010-05-27 20:51:26 +00001357 register ssize_t
dirkb41f0802013-12-26 16:47:58 +00001358 i;
cristydc05fc22011-01-22 19:43:15 +00001359
cristy3ed852e2009-09-05 21:47:34 +00001360 ssize_t
cristydc05fc22011-01-22 19:43:15 +00001361 count,
1362 j,
dirkb41f0802013-12-26 16:47:58 +00001363 number_layers;
1364
1365 size=GetPSDSize(psd_info,image);
1366 if (size == 0)
1367 {
dirkb41f0802013-12-26 16:47:58 +00001368 /*
1369 Skip layers & masks.
1370 */
dirk87038d52015-08-11 23:56:45 +02001371 (void) ReadBlobLong(image);
dirkb41f0802013-12-26 16:47:58 +00001372 count=ReadBlob(image,4,(unsigned char *) type);
dirk87038d52015-08-11 23:56:45 +02001373 ReversePSDString(image,type,4);
cristy908a35b2015-06-14 22:19:29 +00001374 status=MagickFalse;
dirkb41f0802013-12-26 16:47:58 +00001375 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
dirkf3081522015-06-21 09:31:18 +00001376 return(MagickTrue);
dirkb41f0802013-12-26 16:47:58 +00001377 else
1378 {
1379 count=ReadBlob(image,4,(unsigned char *) type);
dirk87038d52015-08-11 23:56:45 +02001380 ReversePSDString(image,type,4);
dirkb41f0802013-12-26 16:47:58 +00001381 if ((count != 0) && (LocaleNCompare(type,"Lr16",4) == 0))
1382 size=GetPSDSize(psd_info,image);
1383 else
dirkf3081522015-06-21 09:31:18 +00001384 return(MagickTrue);
dirkb41f0802013-12-26 16:47:58 +00001385 }
1386 }
dirkb41f0802013-12-26 16:47:58 +00001387 status=MagickTrue;
1388 if (size != 0)
1389 {
1390 layer_info=(LayerInfo *) NULL;
dirk87038d52015-08-11 23:56:45 +02001391 number_layers=(short) ReadBlobShort(image);
dirkb41f0802013-12-26 16:47:58 +00001392
dirkb41f0802013-12-26 16:47:58 +00001393 if (number_layers < 0)
1394 {
1395 /*
dirk6f205312014-01-31 22:25:10 +00001396 The first alpha channel in the merged result contains the
1397 transparency data for the merged result.
dirkb41f0802013-12-26 16:47:58 +00001398 */
1399 number_layers=MagickAbsoluteValue(number_layers);
1400 if (image->debug != MagickFalse)
1401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1402 " negative layer count corrected for");
dirk6f205312014-01-31 22:25:10 +00001403 image->alpha_trait=BlendPixelTrait;
dirkb41f0802013-12-26 16:47:58 +00001404 }
1405
dirkf3081522015-06-21 09:31:18 +00001406 /*
1407 We only need to know if the image has an alpha channel
1408 */
dirkfcaec622014-01-31 23:15:51 +00001409 if (skip_layers != MagickFalse)
1410 return(MagickTrue);
1411
dirkb41f0802013-12-26 16:47:58 +00001412 if (image->debug != MagickFalse)
1413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1414 " image contains %.20g layers",(double) number_layers);
1415
1416 if (number_layers == 0)
dirk419e5ad2014-12-16 23:14:58 +00001417 ThrowBinaryException(CorruptImageError,"InvalidNumberOfLayers",
1418 image->filename);
dirkb41f0802013-12-26 16:47:58 +00001419
1420 layer_info=(LayerInfo *) AcquireQuantumMemory((size_t) number_layers,
1421 sizeof(*layer_info));
1422 if (layer_info == (LayerInfo *) NULL)
1423 {
1424 if (image->debug != MagickFalse)
1425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1426 " allocation of LayerInfo failed");
1427 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1428 image->filename);
1429 }
1430 (void) ResetMagickMemory(layer_info,0,(size_t) number_layers*
1431 sizeof(*layer_info));
1432
1433 for (i=0; i < number_layers; i++)
1434 {
dirk87038d52015-08-11 23:56:45 +02001435 ssize_t
dirkb41f0802013-12-26 16:47:58 +00001436 x,
1437 y;
1438
1439 if (image->debug != MagickFalse)
1440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1441 " reading layer #%.20g",(double) i+1);
dirkca41d622015-10-13 07:14:08 +02001442 layer_info[i].page.y=(int) ReadBlobLong(image);
1443 layer_info[i].page.x=(int) ReadBlobLong(image);
1444 y=(int) ReadBlobLong(image);
1445 x=(int) ReadBlobLong(image);
1446 layer_info[i].page.width=(size_t) (x-layer_info[i].page.x);
1447 layer_info[i].page.height=(size_t) (y-layer_info[i].page.y);
dirk87038d52015-08-11 23:56:45 +02001448 layer_info[i].channels=ReadBlobShort(image);
dirkb41f0802013-12-26 16:47:58 +00001449 if (layer_info[i].channels > MaxPSDChannels)
1450 {
dirkbd8bd852013-12-28 22:55:19 +00001451 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirkb41f0802013-12-26 16:47:58 +00001452 ThrowBinaryException(CorruptImageError,"MaximumChannelsExceeded",
dirk419e5ad2014-12-16 23:14:58 +00001453 image->filename);
dirkb41f0802013-12-26 16:47:58 +00001454 }
1455 if (image->debug != MagickFalse)
1456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1457 " offset(%.20g,%.20g), size(%.20g,%.20g), channels=%.20g",
1458 (double) layer_info[i].page.x,(double) layer_info[i].page.y,
1459 (double) layer_info[i].page.height,(double)
1460 layer_info[i].page.width,(double) layer_info[i].channels);
1461 for (j=0; j < (ssize_t) layer_info[i].channels; j++)
1462 {
dirk87038d52015-08-11 23:56:45 +02001463 layer_info[i].channel_info[j].type=(short) ReadBlobShort(image);
dirkb41f0802013-12-26 16:47:58 +00001464 layer_info[i].channel_info[j].size=(size_t) GetPSDSize(psd_info,
1465 image);
1466 if (image->debug != MagickFalse)
1467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1468 " channel[%.20g]: type=%.20g, size=%.20g",(double) j,
1469 (double) layer_info[i].channel_info[j].type,
1470 (double) layer_info[i].channel_info[j].size);
1471 }
1472 count=ReadBlob(image,4,(unsigned char *) type);
dirk87038d52015-08-11 23:56:45 +02001473 ReversePSDString(image,type,4);
dirkb41f0802013-12-26 16:47:58 +00001474 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1475 {
1476 if (image->debug != MagickFalse)
1477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1478 " layer type was %.4s instead of 8BIM", type);
dirkbd8bd852013-12-28 22:55:19 +00001479 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirkb41f0802013-12-26 16:47:58 +00001480 ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
1481 image->filename);
1482 }
1483 count=ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey);
dirk87038d52015-08-11 23:56:45 +02001484 ReversePSDString(image,layer_info[i].blendkey,4);
dirkb52a4b72013-12-26 16:55:20 +00001485 layer_info[i].opacity=(Quantum) ScaleCharToQuantum((unsigned char)
cristyd8083a62014-02-01 13:53:44 +00001486 ReadBlobByte(image));
dirkb41f0802013-12-26 16:47:58 +00001487 layer_info[i].clipping=(unsigned char) ReadBlobByte(image);
1488 layer_info[i].flags=(unsigned char) ReadBlobByte(image);
1489 layer_info[i].visible=!(layer_info[i].flags & 0x02);
1490 if (image->debug != MagickFalse)
1491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1492 " blend=%.4s, opacity=%.20g, clipping=%s, flags=%d, visible=%s",
1493 layer_info[i].blendkey,(double) layer_info[i].opacity,
1494 layer_info[i].clipping ? "true" : "false",layer_info[i].flags,
1495 layer_info[i].visible ? "true" : "false");
1496 (void) ReadBlobByte(image); /* filler */
1497
dirk87038d52015-08-11 23:56:45 +02001498 size=ReadBlobLong(image);
dirkb41f0802013-12-26 16:47:58 +00001499 if (size != 0)
1500 {
1501 MagickSizeType
1502 combined_length,
1503 length;
1504
1505 if (image->debug != MagickFalse)
1506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1507 " layer contains additional info");
dirk87038d52015-08-11 23:56:45 +02001508 length=ReadBlobLong(image);
dirkb41f0802013-12-26 16:47:58 +00001509 combined_length=length+4;
1510 if (length != 0)
1511 {
1512 /*
1513 Layer mask info.
1514 */
dirkca41d622015-10-13 07:14:08 +02001515 layer_info[i].mask.page.y=(int) ReadBlobLong(image);
1516 layer_info[i].mask.page.x=(int) ReadBlobLong(image);
dirk87038d52015-08-11 23:56:45 +02001517 layer_info[i].mask.page.height=(size_t) (ReadBlobLong(image)-
1518 layer_info[i].mask.page.y);
1519 layer_info[i].mask.page.width=(size_t) (ReadBlobLong(image)-
1520 layer_info[i].mask.page.x);
dirka41d97f2015-05-25 07:56:17 +00001521 layer_info[i].mask.background=(unsigned char) ReadBlobByte(
1522 image);
1523 layer_info[i].mask.flags=(unsigned char) ReadBlobByte(image);
1524 if (!(layer_info[i].mask.flags & 0x01))
1525 {
1526 layer_info[i].mask.page.y=layer_info[i].mask.page.y-
1527 layer_info[i].page.y;
1528 layer_info[i].mask.page.x=layer_info[i].mask.page.x-
1529 layer_info[i].page.x;
1530 }
dirkb41f0802013-12-26 16:47:58 +00001531 if (image->debug != MagickFalse)
1532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1533 " layer mask: offset(%.20g,%.20g), size(%.20g,%.20g), length=%.20g",
dirka41d97f2015-05-25 07:56:17 +00001534 (double) layer_info[i].mask.page.x,(double)
1535 layer_info[i].mask.page.y,(double) layer_info[i].mask.page.width,
1536 (double) layer_info[i].mask.page.height,(double)
1537 ((MagickOffsetType) length)-18);
dirkb41f0802013-12-26 16:47:58 +00001538 /*
1539 Skip over the rest of the layer mask information.
1540 */
dirka41d97f2015-05-25 07:56:17 +00001541 if (DiscardBlobBytes(image,(MagickSizeType) (length-18)) == MagickFalse)
dirkb41f0802013-12-26 16:47:58 +00001542 {
dirkbd8bd852013-12-28 22:55:19 +00001543 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirk419e5ad2014-12-16 23:14:58 +00001544 ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile",
1545 image->filename);
dirkb41f0802013-12-26 16:47:58 +00001546 }
1547 }
dirk87038d52015-08-11 23:56:45 +02001548 length=ReadBlobLong(image);
dirkb41f0802013-12-26 16:47:58 +00001549 combined_length+=length+4;
1550 if (length != 0)
1551 {
1552 /*
1553 Layer blending ranges info.
1554 */
1555 if (image->debug != MagickFalse)
1556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1557 " layer blending ranges: length=%.20g",(double)
1558 ((MagickOffsetType) length));
1559 /*
1560 We read it, but don't use it...
1561 */
1562 for (j=0; j < (ssize_t) (length); j+=8)
1563 {
dirk87038d52015-08-11 23:56:45 +02001564 size_t blend_source=ReadBlobLong(image);
1565 size_t blend_dest=ReadBlobLong(image);
dirkb41f0802013-12-26 16:47:58 +00001566 if (image->debug != MagickFalse)
1567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1568 " source(%x), dest(%x)",(unsigned int)
1569 blend_source,(unsigned int) blend_dest);
1570 }
1571 }
1572 /*
1573 Layer name.
1574 */
1575 length=(size_t) ReadBlobByte(image);
1576 combined_length+=length+1;
dirkf0843192015-07-06 21:21:09 +00001577 if (length > 0)
dirka41d97f2015-05-25 07:56:17 +00001578 (void) ReadBlob(image,(size_t) length++,layer_info[i].name);
1579 layer_info[i].name[length]='\0';
dirkb41f0802013-12-26 16:47:58 +00001580 if (image->debug != MagickFalse)
1581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1582 " layer name: %s",layer_info[i].name);
1583 /*
1584 Skip the rest of the variable data until we support it.
1585 */
1586 if (image->debug != MagickFalse)
1587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1588 " unsupported data: length=%.20g",(double)
1589 ((MagickOffsetType) (size-combined_length)));
cristy1b875d22014-05-28 14:27:23 +00001590 if (DiscardBlobBytes(image,(MagickSizeType) (size-combined_length)) == MagickFalse)
dirkb41f0802013-12-26 16:47:58 +00001591 {
dirkbd8bd852013-12-28 22:55:19 +00001592 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirkb41f0802013-12-26 16:47:58 +00001593 ThrowBinaryException(CorruptImageError,
1594 "UnexpectedEndOfFile",image->filename);
1595 }
1596 }
1597 }
1598
1599 for (i=0; i < number_layers; i++)
1600 {
dirkb41f0802013-12-26 16:47:58 +00001601 if ((layer_info[i].page.width == 0) ||
1602 (layer_info[i].page.height == 0))
1603 {
1604 if (image->debug != MagickFalse)
1605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1606 " layer data is empty");
1607 continue;
1608 }
1609
dirkb41f0802013-12-26 16:47:58 +00001610 /*
1611 Allocate layered image.
1612 */
1613 layer_info[i].image=CloneImage(image,layer_info[i].page.width,
1614 layer_info[i].page.height,MagickFalse,exception);
1615 if (layer_info[i].image == (Image *) NULL)
1616 {
dirkbd8bd852013-12-28 22:55:19 +00001617 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirkb41f0802013-12-26 16:47:58 +00001618 if (image->debug != MagickFalse)
1619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1620 " allocation of image for layer %.20g failed",(double) i);
1621 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1622 image->filename);
1623 }
1624 }
1625
dirk4d9863c2014-03-28 19:48:49 +00001626 if (image_info->ping == MagickFalse)
dirkb41f0802013-12-26 16:47:58 +00001627 {
dirk4d9863c2014-03-28 19:48:49 +00001628 for (i=0; i < number_layers; i++)
dirkb41f0802013-12-26 16:47:58 +00001629 {
dirk4d9863c2014-03-28 19:48:49 +00001630 if (layer_info[i].image == (Image *) NULL)
dirkb41f0802013-12-26 16:47:58 +00001631 {
dirk4d9863c2014-03-28 19:48:49 +00001632 for (j=0; j < layer_info[i].channels; j++)
1633 {
cristy1b875d22014-05-28 14:27:23 +00001634 if (DiscardBlobBytes(image,(MagickSizeType)
dirk4d9863c2014-03-28 19:48:49 +00001635 layer_info[i].channel_info[j].size) == MagickFalse)
1636 {
1637 layer_info=DestroyLayerInfo(layer_info,number_layers);
1638 ThrowBinaryException(CorruptImageError,
1639 "UnexpectedEndOfFile",image->filename);
1640 }
1641 }
1642 continue;
dirkb41f0802013-12-26 16:47:58 +00001643 }
dirk4d9863c2014-03-28 19:48:49 +00001644
1645 if (image->debug != MagickFalse)
1646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1647 " reading data for layer %.20g",(double) i);
1648
1649 status=ReadPSDLayer(image,psd_info,&layer_info[i],exception);
1650 if (status == MagickFalse)
1651 break;
1652
1653 status=SetImageProgress(image,LoadImagesTag,i,(MagickSizeType)
1654 number_layers);
1655 if (status == MagickFalse)
1656 break;
dirkb41f0802013-12-26 16:47:58 +00001657 }
dirkb41f0802013-12-26 16:47:58 +00001658 }
1659
dirkb41f0802013-12-26 16:47:58 +00001660 if (status != MagickFalse)
1661 {
1662 for (i=0; i < number_layers; i++)
1663 {
1664 if (layer_info[i].image == (Image *) NULL)
1665 {
1666 for (j=i; j < number_layers - 1; j++)
1667 layer_info[j] = layer_info[j+1];
1668 number_layers--;
1669 i--;
1670 }
1671 }
1672
1673 if (number_layers > 0)
1674 {
1675 for (i=0; i < number_layers; i++)
1676 {
1677 if (i > 0)
1678 layer_info[i].image->previous=layer_info[i-1].image;
1679 if (i < (number_layers-1))
1680 layer_info[i].image->next=layer_info[i+1].image;
1681 layer_info[i].image->page=layer_info[i].page;
1682 }
1683 image->next=layer_info[0].image;
1684 layer_info[0].image->previous=image;
1685 }
dirkbd9f1e72015-12-16 21:37:00 +01001686 layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
dirkb41f0802013-12-26 16:47:58 +00001687 }
dirkbd9f1e72015-12-16 21:37:00 +01001688 else
1689 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirkb41f0802013-12-26 16:47:58 +00001690 }
1691
1692 return(status);
1693}
1694
dirkb20a1792015-10-15 23:32:37 +02001695static MagickBooleanType ReadPSDMergedImage(const ImageInfo *image_info,
1696 Image *image,const PSDInfo *psd_info,ExceptionInfo *exception)
dirkb41f0802013-12-26 16:47:58 +00001697{
1698 MagickOffsetType
1699 *offsets;
1700
dirkb20a1792015-10-15 23:32:37 +02001701 MagickBooleanType
dirkb41f0802013-12-26 16:47:58 +00001702 status;
1703
1704 PSDCompressionType
1705 compression;
1706
1707 register ssize_t
1708 i;
1709
1710 compression=(PSDCompressionType) ReadBlobMSBShort(image);
1711 image->compression=ConvertPSDCompression(compression);
1712
1713 if (compression != Raw && compression != RLE)
1714 {
1715 (void) ThrowMagickException(exception,GetMagickModule(),
1716 TypeWarning,"CompressionNotSupported","'%.20g'",(double) compression);
1717 return(MagickFalse);
1718 }
1719
1720 offsets=(MagickOffsetType *) NULL;
1721 if (compression == RLE)
1722 {
1723 offsets=ReadPSDRLEOffsets(image,psd_info,image->rows*psd_info->channels);
1724 if (offsets == (MagickOffsetType *) NULL)
1725 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1726 image->filename);
1727 }
1728
1729 status=MagickTrue;
1730 for (i=0; i < (ssize_t) psd_info->channels; i++)
1731 {
1732 if (compression == RLE)
1733 status=ReadPSDChannelRLE(image,psd_info,i,offsets+(i*image->rows),
1734 exception);
1735 else
1736 status=ReadPSDChannelRaw(image,psd_info->channels,i,exception);
1737
dirkb20a1792015-10-15 23:32:37 +02001738 if (status != MagickFalse)
1739 status=SetImageProgress(image,LoadImagesTag,i,psd_info->channels);
1740
dirkb41f0802013-12-26 16:47:58 +00001741 if (status == MagickFalse)
1742 break;
1743 }
1744
dirkb20a1792015-10-15 23:32:37 +02001745 if ((status != MagickFalse) && (image->colorspace == CMYKColorspace))
1746 status=NegateCMYK(image,exception);
1747
1748 if (status != MagickFalse)
1749 status=CorrectPSDAlphaBlend(image_info,image,exception);
dirkb41f0802013-12-26 16:47:58 +00001750
1751 if (offsets != (MagickOffsetType *) NULL)
1752 offsets=(MagickOffsetType *) RelinquishMagickMemory(offsets);
1753
1754 return(status);
1755}
1756
cristy90de83d2015-04-08 22:04:41 +00001757static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
dirkb41f0802013-12-26 16:47:58 +00001758{
1759 Image
1760 *image;
1761
1762 MagickBooleanType
dirk18c0e4d2014-02-22 22:24:05 +00001763 has_merged_image,
dirka41d97f2015-05-25 07:56:17 +00001764 skip_layers;
dirkb41f0802013-12-26 16:47:58 +00001765
1766 MagickOffsetType
1767 offset;
1768
1769 MagickSizeType
1770 length;
1771
dirkb20a1792015-10-15 23:32:37 +02001772 MagickBooleanType
dirka41d97f2015-05-25 07:56:17 +00001773 status;
1774
dirkb41f0802013-12-26 16:47:58 +00001775 PSDInfo
1776 psd_info;
1777
1778 register ssize_t
1779 i;
1780
1781 ssize_t
1782 count;
cristy3ed852e2009-09-05 21:47:34 +00001783
cristy3ed852e2009-09-05 21:47:34 +00001784 unsigned char
1785 *data;
1786
cristy3ed852e2009-09-05 21:47:34 +00001787 /*
1788 Open image file.
1789 */
1790 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001791 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001792 if (image_info->debug != MagickFalse)
1793 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1794 image_info->filename);
1795 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001796 assert(exception->signature == MagickCoreSignature);
dirkb41f0802013-12-26 16:47:58 +00001797
cristy9950d572011-10-01 18:22:35 +00001798 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001799 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1800 if (status == MagickFalse)
1801 {
1802 image=DestroyImageList(image);
1803 return((Image *) NULL);
1804 }
1805 /*
1806 Read image header.
1807 */
dirkfc16b542015-09-18 08:48:34 +02001808 image->endian=MSBEndian;
cristy3ed852e2009-09-05 21:47:34 +00001809 count=ReadBlob(image,4,(unsigned char *) psd_info.signature);
1810 psd_info.version=ReadBlobMSBShort(image);
cristy50aea4a2010-03-09 17:37:44 +00001811 if ((count == 0) || (LocaleNCompare(psd_info.signature,"8BPS",4) != 0) ||
cristy2d3d87f2010-03-01 00:23:08 +00001812 ((psd_info.version != 1) && (psd_info.version != 2)))
cristy3ed852e2009-09-05 21:47:34 +00001813 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
dirkf3081522015-06-21 09:31:18 +00001814 (void) ReadBlob(image,6,psd_info.reserved);
cristy3ed852e2009-09-05 21:47:34 +00001815 psd_info.channels=ReadBlobMSBShort(image);
1816 if (psd_info.channels > MaxPSDChannels)
1817 ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded");
1818 psd_info.rows=ReadBlobMSBLong(image);
1819 psd_info.columns=ReadBlobMSBLong(image);
cristy2d3d87f2010-03-01 00:23:08 +00001820 if ((psd_info.version == 1) && ((psd_info.rows > 30000) ||
1821 (psd_info.columns > 30000)))
1822 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy3ed852e2009-09-05 21:47:34 +00001823 psd_info.depth=ReadBlobMSBShort(image);
cristy2d3d87f2010-03-01 00:23:08 +00001824 if ((psd_info.depth != 1) && (psd_info.depth != 8) && (psd_info.depth != 16))
1825 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy3ed852e2009-09-05 21:47:34 +00001826 psd_info.mode=ReadBlobMSBShort(image);
1827 if (image->debug != MagickFalse)
1828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001829 " Image is %.20g x %.20g with channels=%.20g, depth=%.20g, mode=%s",
1830 (double) psd_info.columns,(double) psd_info.rows,(double)
1831 psd_info.channels,(double) psd_info.depth,ModeToString((PSDImageType)
1832 psd_info.mode));
cristy3ed852e2009-09-05 21:47:34 +00001833 /*
1834 Initialize image.
1835 */
1836 image->depth=psd_info.depth;
1837 image->columns=psd_info.columns;
1838 image->rows=psd_info.rows;
cristyacabb842014-12-14 23:36:33 +00001839 status=SetImageExtent(image,image->columns,image->rows,exception);
1840 if (status == MagickFalse)
1841 return(DestroyImageList(image));
cristyea1a8aa2011-10-20 13:24:06 +00001842 if (SetImageBackgroundColor(image,exception) == MagickFalse)
cristy95524f92010-02-16 18:44:34 +00001843 {
cristy95524f92010-02-16 18:44:34 +00001844 image=DestroyImageList(image);
1845 return((Image *) NULL);
1846 }
cristy3ed852e2009-09-05 21:47:34 +00001847 if (psd_info.mode == LabMode)
cristye2c4f182012-05-12 14:11:53 +00001848 SetImageColorspace(image,LabColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001849 if (psd_info.mode == CMYKMode)
dirk84affaa2014-05-27 11:09:16 +00001850 {
1851 SetImageColorspace(image,CMYKColorspace,exception);
dirkdeca4042014-05-29 06:31:05 +00001852 image->alpha_trait=psd_info.channels > 4 ? BlendPixelTrait :
dirk84affaa2014-05-27 11:09:16 +00001853 UndefinedPixelTrait;
1854 }
1855 else if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) ||
cristy3ed852e2009-09-05 21:47:34 +00001856 (psd_info.mode == DuotoneMode))
1857 {
cristyafd0fd12013-09-17 12:41:18 +00001858 status=AcquireImageColormap(image,psd_info.depth != 16 ? 256 : 65536,
1859 exception);
1860 if (status == MagickFalse)
cristy52cf7f12010-02-07 18:07:56 +00001861 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00001862 if (image->debug != MagickFalse)
1863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristydede49f2010-08-20 20:26:26 +00001864 " Image colormap allocated");
cristye2c4f182012-05-12 14:11:53 +00001865 SetImageColorspace(image,GRAYColorspace,exception);
dirkdeca4042014-05-29 06:31:05 +00001866 image->alpha_trait=psd_info.channels > 1 ? BlendPixelTrait :
dirk84affaa2014-05-27 11:09:16 +00001867 UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00001868 }
dirk84affaa2014-05-27 11:09:16 +00001869 else
dirkdeca4042014-05-29 06:31:05 +00001870 image->alpha_trait=psd_info.channels > 3 ? BlendPixelTrait :
dirk84affaa2014-05-27 11:09:16 +00001871 UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00001872 /*
1873 Read PSD raster colormap only present for indexed and duotone images.
1874 */
1875 length=ReadBlobMSBLong(image);
1876 if (length != 0)
1877 {
1878 if (image->debug != MagickFalse)
1879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1880 " reading colormap");
1881 if (psd_info.mode == DuotoneMode)
1882 {
1883 /*
1884 Duotone image data; the format of this data is undocumented.
1885 */
cristy56ed31c2010-03-22 00:46:21 +00001886 data=(unsigned char *) AcquireQuantumMemory((size_t) length,
1887 sizeof(*data));
cristy3ed852e2009-09-05 21:47:34 +00001888 if (data == (unsigned char *) NULL)
1889 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
dirkf3081522015-06-21 09:31:18 +00001890 (void) ReadBlob(image,(size_t) length,data);
cristy3ed852e2009-09-05 21:47:34 +00001891 data=(unsigned char *) RelinquishMagickMemory(data);
1892 }
1893 else
1894 {
cristy18a9b972014-12-02 23:47:21 +00001895 size_t
1896 number_colors;
1897
cristy3ed852e2009-09-05 21:47:34 +00001898 /*
1899 Read PSD raster colormap.
1900 */
cristy18a9b972014-12-02 23:47:21 +00001901 number_colors=length/3;
1902 if (number_colors > 65536)
1903 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1904 if (AcquireImageColormap(image,number_colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001905 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00001906 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001907 image->colormap[i].red=ScaleCharToQuantum((unsigned char)
1908 ReadBlobByte(image));
cristybb503372010-05-27 20:51:26 +00001909 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001910 image->colormap[i].green=ScaleCharToQuantum((unsigned char)
1911 ReadBlobByte(image));
cristybb503372010-05-27 20:51:26 +00001912 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001913 image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
1914 ReadBlobByte(image));
cristy8a46d822012-08-28 23:32:39 +00001915 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00001916 }
1917 }
dirk198fffa2016-01-15 11:17:30 +01001918 if ((image->depth == 1) && (image->storage_class != PseudoClass))
1919 ThrowReaderException(CorruptImageError, "ImproperImageHeader");
dirk18c0e4d2014-02-22 22:24:05 +00001920 has_merged_image=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001921 length=ReadBlobMSBLong(image);
1922 if (length != 0)
1923 {
1924 unsigned char
1925 *blocks;
1926
1927 /*
1928 Image resources block.
1929 */
1930 if (image->debug != MagickFalse)
1931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy2b9582a2011-07-04 17:38:56 +00001932 " reading image resource blocks - %.20g bytes",(double)
1933 ((MagickOffsetType) length));
dirk7ec89322014-12-16 22:50:15 +00001934 blocks=(unsigned char *) AcquireQuantumMemory((size_t) length,
cristy56ed31c2010-03-22 00:46:21 +00001935 sizeof(*blocks));
cristy3ed852e2009-09-05 21:47:34 +00001936 if (blocks == (unsigned char *) NULL)
1937 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristy56ed31c2010-03-22 00:46:21 +00001938 count=ReadBlob(image,(size_t) length,blocks);
1939 if ((count != (ssize_t) length) ||
cristy3ed852e2009-09-05 21:47:34 +00001940 (LocaleNCompare((char *) blocks,"8BIM",4) != 0))
1941 {
1942 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
1943 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1944 }
dirk7ec89322014-12-16 22:50:15 +00001945 ParseImageResourceBlocks(image,blocks,(size_t) length,&has_merged_image,
1946 exception);
cristy3ed852e2009-09-05 21:47:34 +00001947 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
1948 }
1949 /*
cristy3ed852e2009-09-05 21:47:34 +00001950 Layer and mask block.
1951 */
cristy2d3d87f2010-03-01 00:23:08 +00001952 length=GetPSDSize(&psd_info,image);
cristy3ed852e2009-09-05 21:47:34 +00001953 if (length == 8)
1954 {
1955 length=ReadBlobMSBLong(image);
1956 length=ReadBlobMSBLong(image);
1957 }
dirkb41f0802013-12-26 16:47:58 +00001958 offset=TellBlob(image);
1959 skip_layers=MagickFalse;
dirk18c0e4d2014-02-22 22:24:05 +00001960 if ((image_info->number_scenes == 1) && (image_info->scene == 0) &&
1961 (has_merged_image != MagickFalse))
cristyd4297022010-09-16 22:59:09 +00001962 {
cristy374b8ad2012-02-01 20:55:21 +00001963 if (image->debug != MagickFalse)
1964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1965 " read composite only");
dirkb41f0802013-12-26 16:47:58 +00001966 skip_layers=MagickTrue;
cristyd4297022010-09-16 22:59:09 +00001967 }
cristy3ed852e2009-09-05 21:47:34 +00001968 if (length == 0)
1969 {
1970 if (image->debug != MagickFalse)
1971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1972 " image has no layers");
1973 }
1974 else
1975 {
dirk4d9863c2014-03-28 19:48:49 +00001976 if (ReadPSDLayers(image,image_info,&psd_info,skip_layers,exception) !=
1977 MagickTrue)
dirk6f205312014-01-31 22:25:10 +00001978 {
1979 (void) CloseBlob(image);
dirk419e5ad2014-12-16 23:14:58 +00001980 image=DestroyImageList(image);
dirk6f205312014-01-31 22:25:10 +00001981 return((Image *) NULL);
1982 }
cristy3ed852e2009-09-05 21:47:34 +00001983
dirkb41f0802013-12-26 16:47:58 +00001984 /*
1985 Skip the rest of the layer and mask information.
1986 */
1987 SeekBlob(image,offset+length,SEEK_SET);
cristy3ed852e2009-09-05 21:47:34 +00001988 }
dirk4d9863c2014-03-28 19:48:49 +00001989 /*
cristy89e22602015-04-08 23:34:13 +00001990 If we are only "pinging" the image, then we're done - so return.
1991 */
1992 if (image_info->ping != MagickFalse)
1993 {
1994 (void) CloseBlob(image);
1995 return(GetFirstImageInList(image));
1996 }
1997 /*
dirkb41f0802013-12-26 16:47:58 +00001998 Read the precombined layer, present for PSD < 4 compatibility.
cristy3ed852e2009-09-05 21:47:34 +00001999 */
2000 if (image->debug != MagickFalse)
2001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2002 " reading the precombined layer");
cristy90de83d2015-04-08 22:04:41 +00002003 if ((has_merged_image != MagickFalse) || (GetImageListLength(image) == 1))
dirkb20a1792015-10-15 23:32:37 +02002004 has_merged_image=(MagickBooleanType) ReadPSDMergedImage(image_info,image,
2005 &psd_info,exception);
cristy90de83d2015-04-08 22:04:41 +00002006 if ((has_merged_image == MagickFalse) && (GetImageListLength(image) == 1) &&
2007 (length != 0))
dirk2de94f52014-03-15 21:36:56 +00002008 {
2009 SeekBlob(image,offset,SEEK_SET);
cristy90de83d2015-04-08 22:04:41 +00002010 status=ReadPSDLayers(image,image_info,&psd_info,MagickFalse,exception);
2011 if (status != MagickTrue)
dirk2de94f52014-03-15 21:36:56 +00002012 {
2013 (void) CloseBlob(image);
dirkb9ed4082015-12-17 07:19:27 +01002014 image=DestroyImageList(image);
dirk2de94f52014-03-15 21:36:56 +00002015 return((Image *) NULL);
2016 }
2017 }
cristy90de83d2015-04-08 22:04:41 +00002018 if ((has_merged_image == MagickFalse) && (GetImageListLength(image) > 1))
dirk18c0e4d2014-02-22 22:24:05 +00002019 {
2020 Image
2021 *merged;
2022
2023 SetImageAlphaChannel(image,TransparentAlphaChannel,exception);
2024 image->background_color.alpha=TransparentAlpha;
dirk7b5a0d52015-10-12 17:31:15 +02002025 image->background_color.alpha_trait=BlendPixelTrait;
dirk18c0e4d2014-02-22 22:24:05 +00002026 merged=MergeImageLayers(image,FlattenLayer,exception);
2027 ReplaceImageInList(&image,merged);
2028 }
cristy3ed852e2009-09-05 21:47:34 +00002029 (void) CloseBlob(image);
2030 return(GetFirstImageInList(image));
2031}
2032
2033/*
2034%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2035% %
2036% %
2037% %
2038% R e g i s t e r P S D I m a g e %
2039% %
2040% %
2041% %
2042%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2043%
2044% RegisterPSDImage() adds properties for the PSD image format to
2045% the list of supported formats. The properties include the image format
2046% tag, a method to read and/or write the format, whether the format
2047% supports the saving of more than one frame to the same file or blob,
2048% whether the format supports native in-memory I/O, and a brief
2049% description of the format.
2050%
2051% The format of the RegisterPSDImage method is:
2052%
cristybb503372010-05-27 20:51:26 +00002053% size_t RegisterPSDImage(void)
cristy3ed852e2009-09-05 21:47:34 +00002054%
2055*/
cristybb503372010-05-27 20:51:26 +00002056ModuleExport size_t RegisterPSDImage(void)
cristy3ed852e2009-09-05 21:47:34 +00002057{
2058 MagickInfo
2059 *entry;
2060
dirk06b627a2015-04-06 18:59:17 +00002061 entry=AcquireMagickInfo("PSD","PSB","Adobe Large Document Format");
cristyb4233012010-02-28 20:09:14 +00002062 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2063 entry->encoder=(EncodeImageHandler *) WritePSDImage;
2064 entry->magick=(IsImageFormatHandler *) IsPSD;
dirk08e9a112015-02-22 01:51:41 +00002065 entry->flags|=CoderSeekableStreamFlag;
cristyb4233012010-02-28 20:09:14 +00002066 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00002067 entry=AcquireMagickInfo("PSD","PSD","Adobe Photoshop bitmap");
cristy3ed852e2009-09-05 21:47:34 +00002068 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2069 entry->encoder=(EncodeImageHandler *) WritePSDImage;
2070 entry->magick=(IsImageFormatHandler *) IsPSD;
dirk08e9a112015-02-22 01:51:41 +00002071 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00002072 (void) RegisterMagickInfo(entry);
2073 return(MagickImageCoderSignature);
2074}
2075
2076/*
2077%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2078% %
2079% %
2080% %
2081% U n r e g i s t e r P S D I m a g e %
2082% %
2083% %
2084% %
2085%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2086%
2087% UnregisterPSDImage() removes format registrations made by the
2088% PSD module from the list of supported formats.
2089%
2090% The format of the UnregisterPSDImage method is:
2091%
2092% UnregisterPSDImage(void)
2093%
2094*/
2095ModuleExport void UnregisterPSDImage(void)
2096{
cristyb4233012010-02-28 20:09:14 +00002097 (void) UnregisterMagickInfo("PSB");
cristy3ed852e2009-09-05 21:47:34 +00002098 (void) UnregisterMagickInfo("PSD");
2099}
2100
2101/*
2102%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2103% %
2104% %
2105% %
2106% W r i t e P S D I m a g e %
2107% %
2108% %
2109% %
2110%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2111%
cristyb1459bc2010-03-23 21:41:43 +00002112% WritePSDImage() writes an image in the Adobe Photoshop encoded image format.
cristy3ed852e2009-09-05 21:47:34 +00002113%
2114% The format of the WritePSDImage method is:
2115%
dirk1ab941f2014-02-01 17:35:11 +00002116% MagickBooleanType WritePSDImage(const ImageInfo *image_info,Image *image,
2117% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002118%
2119% A description of each parameter follows.
2120%
2121% o image_info: the image info.
2122%
2123% o image: The image.
2124%
cristy3a37efd2011-08-28 20:31:03 +00002125% o exception: return any errors or warnings in this structure.
2126%
cristy3ed852e2009-09-05 21:47:34 +00002127*/
2128
cristy50aea4a2010-03-09 17:37:44 +00002129static inline ssize_t SetPSDOffset(const PSDInfo *psd_info,Image *image,
cristyf0460ee2010-03-06 02:55:11 +00002130 const size_t offset)
2131{
2132 if (psd_info->version == 1)
cristy56ed31c2010-03-22 00:46:21 +00002133 return(WriteBlobMSBShort(image,(unsigned short) offset));
2134 return(WriteBlobMSBLong(image,(unsigned short) offset));
cristyf0460ee2010-03-06 02:55:11 +00002135}
2136
cristy50aea4a2010-03-09 17:37:44 +00002137static inline ssize_t SetPSDSize(const PSDInfo *psd_info,Image *image,
cristyf0460ee2010-03-06 02:55:11 +00002138 const MagickSizeType size)
2139{
2140 if (psd_info->version == 1)
cristy56ed31c2010-03-22 00:46:21 +00002141 return(WriteBlobMSBLong(image,(unsigned int) size));
cristyf0460ee2010-03-06 02:55:11 +00002142 return(WriteBlobMSBLongLong(image,size));
2143}
2144
cristy4aff1572010-02-15 13:34:01 +00002145static size_t PSDPackbitsEncodeImage(Image *image,const size_t length,
cristy018f07f2011-09-04 21:15:19 +00002146 const unsigned char *pixels,unsigned char *compact_pixels,
2147 ExceptionInfo *exception)
cristy4aff1572010-02-15 13:34:01 +00002148{
2149 int
2150 count;
2151
cristybb503372010-05-27 20:51:26 +00002152 register ssize_t
cristy4aff1572010-02-15 13:34:01 +00002153 i,
2154 j;
2155
2156 register unsigned char
2157 *q;
2158
2159 unsigned char
2160 *packbits;
2161
2162 /*
2163 Compress pixels with Packbits encoding.
2164 */
2165 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002166 assert(image->signature == MagickCoreSignature);
cristy4aff1572010-02-15 13:34:01 +00002167 if (image->debug != MagickFalse)
2168 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2169 assert(pixels != (unsigned char *) NULL);
2170 packbits=(unsigned char *) AcquireQuantumMemory(128UL,sizeof(*packbits));
2171 if (packbits == (unsigned char *) NULL)
2172 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2173 image->filename);
cristyb1459bc2010-03-23 21:41:43 +00002174 q=compact_pixels;
cristybb503372010-05-27 20:51:26 +00002175 for (i=(ssize_t) length; i != 0; )
cristy4aff1572010-02-15 13:34:01 +00002176 {
2177 switch (i)
2178 {
2179 case 1:
2180 {
2181 i--;
2182 *q++=(unsigned char) 0;
2183 *q++=(*pixels);
2184 break;
2185 }
2186 case 2:
2187 {
2188 i-=2;
2189 *q++=(unsigned char) 1;
2190 *q++=(*pixels);
2191 *q++=pixels[1];
2192 break;
2193 }
2194 case 3:
2195 {
2196 i-=3;
2197 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2198 {
2199 *q++=(unsigned char) ((256-3)+1);
2200 *q++=(*pixels);
2201 break;
2202 }
2203 *q++=(unsigned char) 2;
2204 *q++=(*pixels);
2205 *q++=pixels[1];
2206 *q++=pixels[2];
2207 break;
2208 }
2209 default:
2210 {
2211 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2212 {
2213 /*
2214 Packed run.
2215 */
2216 count=3;
cristybb503372010-05-27 20:51:26 +00002217 while (((ssize_t) count < i) && (*pixels == *(pixels+count)))
cristy4aff1572010-02-15 13:34:01 +00002218 {
2219 count++;
2220 if (count >= 127)
2221 break;
2222 }
2223 i-=count;
2224 *q++=(unsigned char) ((256-count)+1);
2225 *q++=(*pixels);
2226 pixels+=count;
2227 break;
2228 }
2229 /*
2230 Literal run.
2231 */
2232 count=0;
2233 while ((*(pixels+count) != *(pixels+count+1)) ||
2234 (*(pixels+count+1) != *(pixels+count+2)))
2235 {
2236 packbits[count+1]=pixels[count];
2237 count++;
cristybb503372010-05-27 20:51:26 +00002238 if (((ssize_t) count >= (i-3)) || (count >= 127))
cristy4aff1572010-02-15 13:34:01 +00002239 break;
2240 }
2241 i-=count;
2242 *packbits=(unsigned char) (count-1);
cristybb503372010-05-27 20:51:26 +00002243 for (j=0; j <= (ssize_t) count; j++)
cristy4aff1572010-02-15 13:34:01 +00002244 *q++=packbits[j];
2245 pixels+=count;
2246 break;
2247 }
2248 }
2249 }
2250 *q++=(unsigned char) 128; /* EOD marker */
2251 packbits=(unsigned char *) RelinquishMagickMemory(packbits);
cristyb1459bc2010-03-23 21:41:43 +00002252 return((size_t) (q-compact_pixels));
cristy4aff1572010-02-15 13:34:01 +00002253}
2254
cristy875e28a2010-03-06 19:46:55 +00002255static void WritePackbitsLength(const PSDInfo *psd_info,
cristya20214e2010-08-28 16:52:13 +00002256 const ImageInfo *image_info,Image *image,Image *next_image,
cristy018f07f2011-09-04 21:15:19 +00002257 unsigned char *compact_pixels,const QuantumType quantum_type,
2258 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002259{
cristy3ed852e2009-09-05 21:47:34 +00002260 QuantumInfo
2261 *quantum_info;
2262
cristy4c08aed2011-07-01 19:47:50 +00002263 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002264 *p;
2265
2266 size_t
cristy4aff1572010-02-15 13:34:01 +00002267 length,
cristy3ed852e2009-09-05 21:47:34 +00002268 packet_size;
2269
cristy75f85ae2010-09-25 03:01:06 +00002270 ssize_t
2271 y;
2272
cristya20214e2010-08-28 16:52:13 +00002273 unsigned char
2274 *pixels;
2275
2276 if (next_image->depth > 8)
2277 next_image->depth=16;
2278 packet_size=next_image->depth > 8UL ? 2UL : 1UL;
cristyda16f162011-02-19 23:52:17 +00002279 (void) packet_size;
cristy5f766ef2014-12-14 21:12:47 +00002280 quantum_info=AcquireQuantumInfo(image_info,image);
cristyb3f97ae2015-05-18 12:29:32 +00002281 pixels=(unsigned char *) GetQuantumPixels(quantum_info);
cristya20214e2010-08-28 16:52:13 +00002282 for (y=0; y < (ssize_t) next_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002283 {
cristyc82a27b2011-10-21 01:07:16 +00002284 p=GetVirtualPixels(next_image,0,y,next_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002285 if (p == (const Quantum *) NULL)
cristy4aff1572010-02-15 13:34:01 +00002286 break;
cristya20214e2010-08-28 16:52:13 +00002287 length=ExportQuantumPixels(next_image,(CacheView *) NULL,quantum_info,
cristyc82a27b2011-10-21 01:07:16 +00002288 quantum_type,pixels,exception);
cristy018f07f2011-09-04 21:15:19 +00002289 length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels,
2290 exception);
cristy875e28a2010-03-06 19:46:55 +00002291 (void) SetPSDOffset(psd_info,image,length);
cristy4aff1572010-02-15 13:34:01 +00002292 }
2293 quantum_info=DestroyQuantumInfo(quantum_info);
2294}
2295
cristy875e28a2010-03-06 19:46:55 +00002296static void WriteOneChannel(const PSDInfo *psd_info,const ImageInfo *image_info,
cristya20214e2010-08-28 16:52:13 +00002297 Image *image,Image *next_image,unsigned char *compact_pixels,
cristy018f07f2011-09-04 21:15:19 +00002298 const QuantumType quantum_type,const MagickBooleanType compression_flag,
2299 ExceptionInfo *exception)
cristy4aff1572010-02-15 13:34:01 +00002300{
2301 int
2302 y;
2303
cristy0910f242010-04-01 18:55:09 +00002304 MagickBooleanType
2305 monochrome;
2306
cristy4aff1572010-02-15 13:34:01 +00002307 QuantumInfo
2308 *quantum_info;
2309
cristy4c08aed2011-07-01 19:47:50 +00002310 register const Quantum
cristy4aff1572010-02-15 13:34:01 +00002311 *p;
2312
cristybb503372010-05-27 20:51:26 +00002313 register ssize_t
cristy0910f242010-04-01 18:55:09 +00002314 i;
2315
cristy4aff1572010-02-15 13:34:01 +00002316 size_t
2317 length,
2318 packet_size;
2319
cristya20214e2010-08-28 16:52:13 +00002320 unsigned char
2321 *pixels;
2322
cristy50aea4a2010-03-09 17:37:44 +00002323 (void) psd_info;
cristy4aff1572010-02-15 13:34:01 +00002324 if ((compression_flag != MagickFalse) &&
cristya20214e2010-08-28 16:52:13 +00002325 (next_image->compression != RLECompression))
cristy4aff1572010-02-15 13:34:01 +00002326 (void) WriteBlobMSBShort(image,0);
cristya20214e2010-08-28 16:52:13 +00002327 if (next_image->depth > 8)
2328 next_image->depth=16;
dirka7014c82015-04-06 08:43:28 +00002329 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
cristyc82a27b2011-10-21 01:07:16 +00002330 MagickTrue : MagickFalse;
cristya20214e2010-08-28 16:52:13 +00002331 packet_size=next_image->depth > 8UL ? 2UL : 1UL;
cristyda16f162011-02-19 23:52:17 +00002332 (void) packet_size;
cristy5f766ef2014-12-14 21:12:47 +00002333 quantum_info=AcquireQuantumInfo(image_info,image);
cristyb3f97ae2015-05-18 12:29:32 +00002334 pixels=(unsigned char *) GetQuantumPixels(quantum_info);
cristya20214e2010-08-28 16:52:13 +00002335 for (y=0; y < (ssize_t) next_image->rows; y++)
cristy4aff1572010-02-15 13:34:01 +00002336 {
cristyc82a27b2011-10-21 01:07:16 +00002337 p=GetVirtualPixels(next_image,0,y,next_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002338 if (p == (const Quantum *) NULL)
cristy4aff1572010-02-15 13:34:01 +00002339 break;
cristya20214e2010-08-28 16:52:13 +00002340 length=ExportQuantumPixels(next_image,(CacheView *) NULL,quantum_info,
cristyc82a27b2011-10-21 01:07:16 +00002341 quantum_type,pixels,exception);
cristy0910f242010-04-01 18:55:09 +00002342 if (monochrome != MagickFalse)
cristybb503372010-05-27 20:51:26 +00002343 for (i=0; i < (ssize_t) length; i++)
cristy0910f242010-04-01 18:55:09 +00002344 pixels[i]=(~pixels[i]);
cristya20214e2010-08-28 16:52:13 +00002345 if (next_image->compression != RLECompression)
cristy4aff1572010-02-15 13:34:01 +00002346 (void) WriteBlob(image,length,pixels);
2347 else
2348 {
cristy018f07f2011-09-04 21:15:19 +00002349 length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels,
2350 exception);
cristyb1459bc2010-03-23 21:41:43 +00002351 (void) WriteBlob(image,length,compact_pixels);
cristy4aff1572010-02-15 13:34:01 +00002352 }
cristy3ed852e2009-09-05 21:47:34 +00002353 }
2354 quantum_info=DestroyQuantumInfo(quantum_info);
2355}
2356
cristy875e28a2010-03-06 19:46:55 +00002357static MagickBooleanType WriteImageChannels(const PSDInfo *psd_info,
cristya20214e2010-08-28 16:52:13 +00002358 const ImageInfo *image_info,Image *image,Image *next_image,
cristy018f07f2011-09-04 21:15:19 +00002359 const MagickBooleanType separate,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002360{
cristy3ed852e2009-09-05 21:47:34 +00002361 size_t
2362 channels,
2363 packet_size;
2364
2365 unsigned char
cristya20214e2010-08-28 16:52:13 +00002366 *compact_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002367
2368 /*
cristy875e28a2010-03-06 19:46:55 +00002369 Write uncompressed pixels as separate planes.
cristy3ed852e2009-09-05 21:47:34 +00002370 */
2371 channels=1;
cristya20214e2010-08-28 16:52:13 +00002372 packet_size=next_image->depth > 8UL ? 2UL : 1UL;
2373 compact_pixels=(unsigned char *) NULL;
2374 if (next_image->compression == RLECompression)
cristy4aff1572010-02-15 13:34:01 +00002375 {
dirk6f1879d2016-01-15 01:13:19 +01002376 compact_pixels=(unsigned char *) AcquireQuantumMemory((2*channels*
2377 next_image->columns)+1,packet_size*sizeof(*compact_pixels));
cristya20214e2010-08-28 16:52:13 +00002378 if (compact_pixels == (unsigned char *) NULL)
2379 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy4aff1572010-02-15 13:34:01 +00002380 }
dirka7014c82015-04-06 08:43:28 +00002381 if (IsImageGray(next_image) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002382 {
cristya20214e2010-08-28 16:52:13 +00002383 if (next_image->compression == RLECompression)
cristy4aff1572010-02-15 13:34:01 +00002384 {
2385 /*
2386 Packbits compression.
2387 */
2388 (void) WriteBlobMSBShort(image,1);
cristya20214e2010-08-28 16:52:13 +00002389 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002390 compact_pixels,GrayQuantum,exception);
cristy17f11b02014-12-20 19:37:04 +00002391 if (next_image->alpha_trait != UndefinedPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002392 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002393 compact_pixels,AlphaQuantum,exception);
cristy4aff1572010-02-15 13:34:01 +00002394 }
cristya20214e2010-08-28 16:52:13 +00002395 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002396 GrayQuantum,MagickTrue,exception);
cristy17f11b02014-12-20 19:37:04 +00002397 if (next_image->alpha_trait != UndefinedPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002398 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002399 AlphaQuantum,separate,exception);
cristy6886a752010-04-23 18:23:20 +00002400 (void) SetImageProgress(image,SaveImagesTag,0,1);
cristy3ed852e2009-09-05 21:47:34 +00002401 }
cristy0910f242010-04-01 18:55:09 +00002402 else
cristya20214e2010-08-28 16:52:13 +00002403 if (next_image->storage_class == PseudoClass)
cristy0910f242010-04-01 18:55:09 +00002404 {
cristya20214e2010-08-28 16:52:13 +00002405 if (next_image->compression == RLECompression)
cristy0910f242010-04-01 18:55:09 +00002406 {
2407 /*
2408 Packbits compression.
2409 */
2410 (void) WriteBlobMSBShort(image,1);
cristya20214e2010-08-28 16:52:13 +00002411 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002412 compact_pixels,IndexQuantum,exception);
cristy17f11b02014-12-20 19:37:04 +00002413 if (next_image->alpha_trait != UndefinedPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002414 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002415 compact_pixels,AlphaQuantum,exception);
cristy0910f242010-04-01 18:55:09 +00002416 }
cristya20214e2010-08-28 16:52:13 +00002417 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002418 IndexQuantum,MagickTrue,exception);
cristy17f11b02014-12-20 19:37:04 +00002419 if (next_image->alpha_trait != UndefinedPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002420 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002421 AlphaQuantum,separate,exception);
cristy0910f242010-04-01 18:55:09 +00002422 (void) SetImageProgress(image,SaveImagesTag,0,1);
2423 }
2424 else
2425 {
cristya20214e2010-08-28 16:52:13 +00002426 if (next_image->colorspace == CMYKColorspace)
dirkd9795a12014-11-11 22:01:21 +00002427 (void) NegateCMYK(next_image,exception);
cristya20214e2010-08-28 16:52:13 +00002428 if (next_image->compression == RLECompression)
cristy0910f242010-04-01 18:55:09 +00002429 {
2430 /*
2431 Packbits compression.
2432 */
2433 (void) WriteBlobMSBShort(image,1);
cristya20214e2010-08-28 16:52:13 +00002434 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002435 compact_pixels,RedQuantum,exception);
cristya20214e2010-08-28 16:52:13 +00002436 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002437 compact_pixels,GreenQuantum,exception);
cristya20214e2010-08-28 16:52:13 +00002438 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002439 compact_pixels,BlueQuantum,exception);
cristya20214e2010-08-28 16:52:13 +00002440 if (next_image->colorspace == CMYKColorspace)
2441 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002442 compact_pixels,BlackQuantum,exception);
cristy17f11b02014-12-20 19:37:04 +00002443 if (next_image->alpha_trait != UndefinedPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002444 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002445 compact_pixels,AlphaQuantum,exception);
cristy0910f242010-04-01 18:55:09 +00002446 }
2447 (void) SetImageProgress(image,SaveImagesTag,0,6);
cristya20214e2010-08-28 16:52:13 +00002448 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002449 RedQuantum,MagickTrue,exception);
cristy860cc732010-05-19 16:38:37 +00002450 (void) SetImageProgress(image,SaveImagesTag,1,6);
cristya20214e2010-08-28 16:52:13 +00002451 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002452 GreenQuantum,separate,exception);
cristy860cc732010-05-19 16:38:37 +00002453 (void) SetImageProgress(image,SaveImagesTag,2,6);
cristya20214e2010-08-28 16:52:13 +00002454 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002455 BlueQuantum,separate,exception);
cristy860cc732010-05-19 16:38:37 +00002456 (void) SetImageProgress(image,SaveImagesTag,3,6);
cristya20214e2010-08-28 16:52:13 +00002457 if (next_image->colorspace == CMYKColorspace)
2458 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002459 BlackQuantum,separate,exception);
cristy860cc732010-05-19 16:38:37 +00002460 (void) SetImageProgress(image,SaveImagesTag,4,6);
cristy17f11b02014-12-20 19:37:04 +00002461 if (next_image->alpha_trait != UndefinedPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002462 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002463 AlphaQuantum,separate,exception);
cristy0910f242010-04-01 18:55:09 +00002464 (void) SetImageProgress(image,SaveImagesTag,5,6);
cristya20214e2010-08-28 16:52:13 +00002465 if (next_image->colorspace == CMYKColorspace)
dirkd9795a12014-11-11 22:01:21 +00002466 (void) NegateCMYK(next_image,exception);
cristy0910f242010-04-01 18:55:09 +00002467 }
cristya20214e2010-08-28 16:52:13 +00002468 if (next_image->compression == RLECompression)
2469 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
cristy3ed852e2009-09-05 21:47:34 +00002470 return(MagickTrue);
2471}
2472
cristy3ed852e2009-09-05 21:47:34 +00002473static void WritePascalString(Image* inImage,const char *inString,int inPad)
2474{
2475 size_t
cristya20214e2010-08-28 16:52:13 +00002476 length;
cristy3ed852e2009-09-05 21:47:34 +00002477
cristya20214e2010-08-28 16:52:13 +00002478 register ssize_t
2479 i;
cristy3ed852e2009-09-05 21:47:34 +00002480
cristya20214e2010-08-28 16:52:13 +00002481 /*
2482 Max length is 255.
2483 */
2484 length=(strlen(inString) > 255UL ) ? 255UL : strlen(inString);
2485 if (length == 0)
2486 (void) WriteBlobByte(inImage,0);
cristy3ed852e2009-09-05 21:47:34 +00002487 else
cristya20214e2010-08-28 16:52:13 +00002488 {
2489 (void) WriteBlobByte(inImage,(unsigned char) length);
2490 (void) WriteBlob(inImage, length, (const unsigned char *) inString);
2491 }
2492 length++;
2493 if ((length % inPad) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002494 return;
cristya20214e2010-08-28 16:52:13 +00002495 for (i=0; i < (ssize_t) (inPad-(length % inPad)); i++)
cristy3ed852e2009-09-05 21:47:34 +00002496 (void) WriteBlobByte(inImage,0);
2497}
2498
2499static void WriteResolutionResourceBlock(Image *image)
2500{
cristy56ed31c2010-03-22 00:46:21 +00002501 double
2502 x_resolution,
2503 y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00002504
2505 unsigned short
2506 units;
2507
cristy3ed852e2009-09-05 21:47:34 +00002508 if (image->units == PixelsPerCentimeterResolution)
2509 {
dirk87dabf62014-11-11 22:44:30 +00002510 x_resolution=2.54*65536.0*image->resolution.x+0.5;
cristy2a11bef2011-10-28 18:33:11 +00002511 y_resolution=2.54*65536.0*image->resolution.y+0.5;
cristy3ed852e2009-09-05 21:47:34 +00002512 units=2;
2513 }
dirk87dabf62014-11-11 22:44:30 +00002514 else
2515 {
2516 x_resolution=65536.0*image->resolution.x+0.5;
2517 y_resolution=65536.0*image->resolution.y+0.5;
2518 units=1;
2519 }
cristy3ed852e2009-09-05 21:47:34 +00002520 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
2521 (void) WriteBlobMSBShort(image,0x03ED);
2522 (void) WriteBlobMSBShort(image,0);
2523 (void) WriteBlobMSBLong(image,16); /* resource size */
cristy56ed31c2010-03-22 00:46:21 +00002524 (void) WriteBlobMSBLong(image,(unsigned int) (x_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002525 (void) WriteBlobMSBShort(image,units); /* horizontal resolution unit */
2526 (void) WriteBlobMSBShort(image,units); /* width unit */
cristy56ed31c2010-03-22 00:46:21 +00002527 (void) WriteBlobMSBLong(image,(unsigned int) (y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002528 (void) WriteBlobMSBShort(image,units); /* vertical resolution unit */
2529 (void) WriteBlobMSBShort(image,units); /* height unit */
2530}
2531
cristyd4d3f742010-04-25 20:36:50 +00002532static void RemoveICCProfileFromResourceBlock(StringInfo *bim_profile)
2533{
cristy0b796e62010-04-29 00:38:45 +00002534 register const unsigned char
cristyd4d3f742010-04-25 20:36:50 +00002535 *p;
2536
2537 size_t
2538 length;
2539
2540 unsigned char
2541 *datum;
2542
cristy6befb0f2010-05-31 14:33:15 +00002543 unsigned int
cristyd4d3f742010-04-25 20:36:50 +00002544 count,
cristy6befb0f2010-05-31 14:33:15 +00002545 long_sans;
cristyd4d3f742010-04-25 20:36:50 +00002546
2547 unsigned short
2548 id,
2549 short_sans;
2550
2551 length=GetStringInfoLength(bim_profile);
2552 if (length < 16)
cristy0b796e62010-04-29 00:38:45 +00002553 return;
cristyd4d3f742010-04-25 20:36:50 +00002554 datum=GetStringInfoDatum(bim_profile);
2555 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
2556 {
cristy4176bb22010-05-01 16:29:09 +00002557 register unsigned char
2558 *q;
2559
2560 q=(unsigned char *) p;
cristyd4d3f742010-04-25 20:36:50 +00002561 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
2562 break;
cristy6befb0f2010-05-31 14:33:15 +00002563 p=PushLongPixel(MSBEndian,p,&long_sans);
cristyd4d3f742010-04-25 20:36:50 +00002564 p=PushShortPixel(MSBEndian,p,&id);
2565 p=PushShortPixel(MSBEndian,p,&short_sans);
2566 p=PushLongPixel(MSBEndian,p,&count);
2567 if (id == 0x0000040f)
2568 {
cristy4176bb22010-05-01 16:29:09 +00002569 (void) CopyMagickMemory(q,q+PSDQuantum(count)+12,length-
2570 (PSDQuantum(count)+12)-(q-datum));
2571 SetStringInfoLength(bim_profile,length-(PSDQuantum(count)+12));
cristyd4d3f742010-04-25 20:36:50 +00002572 break;
2573 }
2574 p+=count;
2575 if ((count & 0x01) != 0)
2576 p++;
2577 }
2578}
2579
cristyf11065e2010-05-14 13:26:59 +00002580static void RemoveResolutionFromResourceBlock(StringInfo *bim_profile)
2581{
2582 register const unsigned char
2583 *p;
2584
2585 size_t
2586 length;
2587
2588 unsigned char
2589 *datum;
2590
cristy6befb0f2010-05-31 14:33:15 +00002591 unsigned int
cristyf11065e2010-05-14 13:26:59 +00002592 count,
cristy6befb0f2010-05-31 14:33:15 +00002593 long_sans;
cristyf11065e2010-05-14 13:26:59 +00002594
2595 unsigned short
2596 id,
2597 short_sans;
2598
2599 length=GetStringInfoLength(bim_profile);
2600 if (length < 16)
2601 return;
2602 datum=GetStringInfoDatum(bim_profile);
2603 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
2604 {
2605 register unsigned char
2606 *q;
2607
2608 q=(unsigned char *) p;
2609 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
2610 break;
cristy6befb0f2010-05-31 14:33:15 +00002611 p=PushLongPixel(MSBEndian,p,&long_sans);
cristyf11065e2010-05-14 13:26:59 +00002612 p=PushShortPixel(MSBEndian,p,&id);
2613 p=PushShortPixel(MSBEndian,p,&short_sans);
2614 p=PushLongPixel(MSBEndian,p,&count);
cristy94b11832011-09-08 19:46:03 +00002615 if ((id == 0x000003ed) && (PSDQuantum(count) < (ssize_t) (length-12)))
cristyf11065e2010-05-14 13:26:59 +00002616 {
2617 (void) CopyMagickMemory(q,q+PSDQuantum(count)+12,length-
2618 (PSDQuantum(count)+12)-(q-datum));
2619 SetStringInfoLength(bim_profile,length-(PSDQuantum(count)+12));
2620 break;
2621 }
2622 p+=count;
2623 if ((count & 0x01) != 0)
2624 p++;
2625 }
2626}
2627
cristy3a37efd2011-08-28 20:31:03 +00002628static MagickBooleanType WritePSDImage(const ImageInfo *image_info,Image *image,
2629 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002630{
2631 const char
cristya20214e2010-08-28 16:52:13 +00002632 *property;
cristy3ed852e2009-09-05 21:47:34 +00002633
2634 const StringInfo
cristy749d2152010-04-04 23:47:33 +00002635 *icc_profile;
cristy3ed852e2009-09-05 21:47:34 +00002636
cristya20214e2010-08-28 16:52:13 +00002637 Image
2638 *base_image,
2639 *next_image;
2640
cristy3ed852e2009-09-05 21:47:34 +00002641 MagickBooleanType
cristy3ed852e2009-09-05 21:47:34 +00002642 status;
2643
cristy875e28a2010-03-06 19:46:55 +00002644 PSDInfo
2645 psd_info;
2646
cristybb503372010-05-27 20:51:26 +00002647 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002648 i;
2649
2650 size_t
cristya20214e2010-08-28 16:52:13 +00002651 channel_size,
2652 channelLength,
2653 layer_count,
2654 layer_info_size,
cristy749d2152010-04-04 23:47:33 +00002655 length,
cristy3ed852e2009-09-05 21:47:34 +00002656 num_channels,
cristya20214e2010-08-28 16:52:13 +00002657 packet_size,
2658 rounded_layer_info_size;
cristy3ed852e2009-09-05 21:47:34 +00002659
cristy0b796e62010-04-29 00:38:45 +00002660 StringInfo
2661 *bim_profile;
2662
cristy3ed852e2009-09-05 21:47:34 +00002663 /*
cristy56ed31c2010-03-22 00:46:21 +00002664 Open image file.
cristy3ed852e2009-09-05 21:47:34 +00002665 */
2666 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002667 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002668 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002669 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002670 if (image->debug != MagickFalse)
2671 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00002672 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002673 assert(exception->signature == MagickCoreSignature);
cristy3a37efd2011-08-28 20:31:03 +00002674 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002675 if (status == MagickFalse)
2676 return(status);
2677 packet_size=(size_t) (image->depth > 8 ? 6 : 3);
cristy17f11b02014-12-20 19:37:04 +00002678 if (image->alpha_trait != UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +00002679 packet_size+=image->depth > 8 ? 2 : 1;
cristy875e28a2010-03-06 19:46:55 +00002680 psd_info.version=1;
2681 if ((LocaleCompare(image_info->magick,"PSB") == 0) ||
2682 (image->columns > 30000) || (image->rows > 30000))
2683 psd_info.version=2;
cristy50aea4a2010-03-09 17:37:44 +00002684 (void) WriteBlob(image,4,(const unsigned char *) "8BPS");
cristy875e28a2010-03-06 19:46:55 +00002685 (void) WriteBlobMSBShort(image,psd_info.version); /* version */
2686 for (i=1; i <= 6; i++)
2687 (void) WriteBlobByte(image, 0); /* 6 bytes of reserved */
dirkf4be6692015-07-24 20:15:38 +00002688 if (SetImageGray(image,exception) != MagickFalse)
cristy17f11b02014-12-20 19:37:04 +00002689 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
cristy3ed852e2009-09-05 21:47:34 +00002690 else
dirke1f8f292015-10-09 00:32:39 +02002691 if ((image_info->type != TrueColorType) && (image_info->type !=
2692 TrueColorAlphaType) && (image->storage_class == PseudoClass))
cristy17f11b02014-12-20 19:37:04 +00002693 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
cristy0910f242010-04-01 18:55:09 +00002694 else
2695 {
dirke1f8f292015-10-09 00:32:39 +02002696 if (image->storage_class == PseudoClass)
2697 (void) SetImageStorageClass(image,DirectClass,exception);
cristy0910f242010-04-01 18:55:09 +00002698 if (image->colorspace != CMYKColorspace)
cristy17f11b02014-12-20 19:37:04 +00002699 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 4UL : 3UL);
cristy0910f242010-04-01 18:55:09 +00002700 else
cristy17f11b02014-12-20 19:37:04 +00002701 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 5UL : 4UL);
cristy0910f242010-04-01 18:55:09 +00002702 }
cristy3ed852e2009-09-05 21:47:34 +00002703 (void) WriteBlobMSBShort(image,(unsigned short) num_channels);
cristy56ed31c2010-03-22 00:46:21 +00002704 (void) WriteBlobMSBLong(image,(unsigned int) image->rows);
2705 (void) WriteBlobMSBLong(image,(unsigned int) image->columns);
dirka7014c82015-04-06 08:43:28 +00002706 if (IsImageGray(image) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002707 {
cristy2045da32010-04-16 00:59:35 +00002708 MagickBooleanType
2709 monochrome;
2710
cristy0910f242010-04-01 18:55:09 +00002711 /*
2712 Write depth & mode.
2713 */
dirka7014c82015-04-06 08:43:28 +00002714 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
cristy3a37efd2011-08-28 20:31:03 +00002715 MagickTrue : MagickFalse;
cristy284c7d82010-04-24 00:19:14 +00002716 (void) WriteBlobMSBShort(image,(unsigned short)
2717 (monochrome != MagickFalse ? 1 : image->depth > 8 ? 16 : 8));
cristy2b9582a2011-07-04 17:38:56 +00002718 (void) WriteBlobMSBShort(image,(unsigned short)
2719 (monochrome != MagickFalse ? BitmapMode : GrayscaleMode));
cristy3ed852e2009-09-05 21:47:34 +00002720 }
2721 else
2722 {
cristya20214e2010-08-28 16:52:13 +00002723 (void) WriteBlobMSBShort(image,(unsigned short) (image->storage_class ==
2724 PseudoClass ? 8 : image->depth > 8 ? 16 : 8));
dirk87dabf62014-11-11 22:44:30 +00002725
cristy48845392010-06-02 01:19:17 +00002726 if (((image_info->colorspace != UndefinedColorspace) ||
cristy0910f242010-04-01 18:55:09 +00002727 (image->colorspace != CMYKColorspace)) &&
cristy48845392010-06-02 01:19:17 +00002728 (image_info->colorspace != CMYKColorspace))
cristy0910f242010-04-01 18:55:09 +00002729 {
cristyaf8d3912014-02-21 14:50:33 +00002730 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy0910f242010-04-01 18:55:09 +00002731 (void) WriteBlobMSBShort(image,(unsigned short)
cristy2045da32010-04-16 00:59:35 +00002732 (image->storage_class == PseudoClass ? IndexedMode : RGBMode));
cristy0910f242010-04-01 18:55:09 +00002733 }
2734 else
2735 {
cristy48845392010-06-02 01:19:17 +00002736 if (image->colorspace != CMYKColorspace)
cristye941a752011-10-15 01:52:48 +00002737 (void) TransformImageColorspace(image,CMYKColorspace,exception);
cristy2045da32010-04-16 00:59:35 +00002738 (void) WriteBlobMSBShort(image,CMYKMode);
cristy0910f242010-04-01 18:55:09 +00002739 }
cristy3ed852e2009-09-05 21:47:34 +00002740 }
dirka7014c82015-04-06 08:43:28 +00002741 if ((IsImageGray(image) != MagickFalse) ||
cristyca9ddb02010-04-16 01:01:18 +00002742 (image->storage_class == DirectClass) || (image->colors > 256))
cristy3ed852e2009-09-05 21:47:34 +00002743 (void) WriteBlobMSBLong(image,0);
2744 else
2745 {
2746 /*
2747 Write PSD raster colormap.
2748 */
2749 (void) WriteBlobMSBLong(image,768);
cristybb503372010-05-27 20:51:26 +00002750 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002751 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].red));
2752 for ( ; i < 256; i++)
2753 (void) WriteBlobByte(image,0);
cristybb503372010-05-27 20:51:26 +00002754 for (i=0; i < (ssize_t) image->colors; i++)
cristya20214e2010-08-28 16:52:13 +00002755 (void) WriteBlobByte(image,ScaleQuantumToChar(
2756 image->colormap[i].green));
cristy3ed852e2009-09-05 21:47:34 +00002757 for ( ; i < 256; i++)
2758 (void) WriteBlobByte(image,0);
cristybb503372010-05-27 20:51:26 +00002759 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002760 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].blue));
2761 for ( ; i < 256; i++)
2762 (void) WriteBlobByte(image,0);
2763 }
2764 /*
2765 Image resource block.
2766 */
cristy749d2152010-04-04 23:47:33 +00002767 length=28; /* 0x03EB */
cristy0b796e62010-04-29 00:38:45 +00002768 bim_profile=(StringInfo *) GetImageProfile(image,"8bim");
cristy749d2152010-04-04 23:47:33 +00002769 icc_profile=GetImageProfile(image,"icc");
cristyd4d3f742010-04-25 20:36:50 +00002770 if (bim_profile != (StringInfo *) NULL)
2771 {
cristy0b796e62010-04-29 00:38:45 +00002772 bim_profile=CloneStringInfo(bim_profile);
cristyd4d3f742010-04-25 20:36:50 +00002773 if (icc_profile != (StringInfo *) NULL)
2774 RemoveICCProfileFromResourceBlock(bim_profile);
cristyf11065e2010-05-14 13:26:59 +00002775 RemoveResolutionFromResourceBlock(bim_profile);
cristyd4d3f742010-04-25 20:36:50 +00002776 length+=PSDQuantum(GetStringInfoLength(bim_profile));
2777 }
cristy0b796e62010-04-29 00:38:45 +00002778 if (icc_profile != (const StringInfo *) NULL)
cristy749d2152010-04-04 23:47:33 +00002779 length+=PSDQuantum(GetStringInfoLength(icc_profile))+12;
cristy284c7d82010-04-24 00:19:14 +00002780 (void) WriteBlobMSBLong(image,(unsigned int) length);
cristyf11065e2010-05-14 13:26:59 +00002781 WriteResolutionResourceBlock(image);
cristy4176bb22010-05-01 16:29:09 +00002782 if (bim_profile != (StringInfo *) NULL)
2783 {
2784 (void) WriteBlob(image,GetStringInfoLength(bim_profile),
2785 GetStringInfoDatum(bim_profile));
2786 bim_profile=DestroyStringInfo(bim_profile);
2787 }
cristy749d2152010-04-04 23:47:33 +00002788 if (icc_profile != (StringInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002789 {
cristy749d2152010-04-04 23:47:33 +00002790 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
cristy4176bb22010-05-01 16:29:09 +00002791 (void) WriteBlobMSBShort(image,0x0000040F);
cristy749d2152010-04-04 23:47:33 +00002792 (void) WriteBlobMSBShort(image,0);
cristy284c7d82010-04-24 00:19:14 +00002793 (void) WriteBlobMSBLong(image,(unsigned int) GetStringInfoLength(
2794 icc_profile));
cristy749d2152010-04-04 23:47:33 +00002795 (void) WriteBlob(image,GetStringInfoLength(icc_profile),
2796 GetStringInfoDatum(icc_profile));
cristye195f262010-04-16 18:12:35 +00002797 if ((MagickOffsetType) GetStringInfoLength(icc_profile) !=
cristy2045da32010-04-16 00:59:35 +00002798 PSDQuantum(GetStringInfoLength(icc_profile)))
cristy749d2152010-04-04 23:47:33 +00002799 (void) WriteBlobByte(image,0);
cristye6365592010-04-02 17:31:23 +00002800 }
cristy48845392010-06-02 01:19:17 +00002801 layer_count=0;
2802 layer_info_size=2;
cristy2837bcc2010-08-07 23:57:39 +00002803 base_image=GetNextImageInList(image);
cristy17f11b02014-12-20 19:37:04 +00002804 if ((image->alpha_trait != UndefinedPixelTrait) && (base_image == (Image *) NULL))
cristy2837bcc2010-08-07 23:57:39 +00002805 base_image=image;
cristya20214e2010-08-28 16:52:13 +00002806 next_image=base_image;
2807 while ( next_image != NULL )
2808 {
2809 packet_size=next_image->depth > 8 ? 2UL : 1UL;
dirka7014c82015-04-06 08:43:28 +00002810 if (IsImageGray(next_image) != MagickFalse)
cristy17f11b02014-12-20 19:37:04 +00002811 num_channels=next_image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL;
cristy3ed852e2009-09-05 21:47:34 +00002812 else
cristya20214e2010-08-28 16:52:13 +00002813 if (next_image->storage_class == PseudoClass)
cristy17f11b02014-12-20 19:37:04 +00002814 num_channels=next_image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL;
cristy2045da32010-04-16 00:59:35 +00002815 else
cristya20214e2010-08-28 16:52:13 +00002816 if (next_image->colorspace != CMYKColorspace)
cristy17f11b02014-12-20 19:37:04 +00002817 num_channels=next_image->alpha_trait != UndefinedPixelTrait ? 4UL : 3UL;
cristy2045da32010-04-16 00:59:35 +00002818 else
cristy17f11b02014-12-20 19:37:04 +00002819 num_channels=next_image->alpha_trait != UndefinedPixelTrait ? 5UL : 4UL;
cristya20214e2010-08-28 16:52:13 +00002820 channelLength=(size_t) (next_image->columns*next_image->rows*packet_size+2);
cristy48845392010-06-02 01:19:17 +00002821 layer_info_size+=(size_t) (4*4+2+num_channels*6+(psd_info.version == 1 ? 8 :
2822 16)+4*1+4+num_channels*channelLength);
cristyd15e6592011-10-15 00:13:06 +00002823 property=(const char *) GetImageProperty(next_image,"label",exception);
cristya20214e2010-08-28 16:52:13 +00002824 if (property == (const char *) NULL)
2825 layer_info_size+=16;
cristyde9b8f52010-03-12 01:17:00 +00002826 else
2827 {
cristya20214e2010-08-28 16:52:13 +00002828 size_t
dirkc93932a2015-10-01 16:56:10 +02002829 layer_length;
cristya20214e2010-08-28 16:52:13 +00002830
dirkc93932a2015-10-01 16:56:10 +02002831 layer_length=strlen(property);
2832 layer_info_size+=8+layer_length+(4-(layer_length % 4));
cristyde9b8f52010-03-12 01:17:00 +00002833 }
cristy4aff1572010-02-15 13:34:01 +00002834 layer_count++;
cristya20214e2010-08-28 16:52:13 +00002835 next_image=GetNextImageInList(next_image);
cristy3ed852e2009-09-05 21:47:34 +00002836 }
cristy144f1b62010-05-18 00:52:09 +00002837 if (layer_count == 0)
cristy875e28a2010-03-06 19:46:55 +00002838 (void) SetPSDSize(&psd_info,image,0);
cristy3ed852e2009-09-05 21:47:34 +00002839 else
cristya20214e2010-08-28 16:52:13 +00002840 {
cristy8e9bd3e2010-08-29 00:03:56 +00002841 CompressionType
2842 compression;
2843
2844 (void) SetPSDSize(&psd_info,image,layer_info_size+
2845 (psd_info.version == 1 ? 8 : 16));
2846 if ((layer_info_size/2) != ((layer_info_size+1)/2))
2847 rounded_layer_info_size=layer_info_size+1;
cristya20214e2010-08-28 16:52:13 +00002848 else
cristy8e9bd3e2010-08-29 00:03:56 +00002849 rounded_layer_info_size=layer_info_size;
2850 (void) SetPSDSize(&psd_info,image,rounded_layer_info_size);
dirk17303172015-04-05 21:20:07 +00002851 if (image->alpha_trait != UndefinedPixelTrait)
dirk8fb7ed52014-02-11 12:07:01 +00002852 (void) WriteBlobMSBShort(image,-(unsigned short) layer_count);
2853 else
2854 (void) WriteBlobMSBShort(image,(unsigned short) layer_count);
cristy8e9bd3e2010-08-29 00:03:56 +00002855 layer_count=1;
2856 compression=base_image->compression;
cristyf2a82ee2014-05-26 17:49:54 +00002857 for (next_image=base_image; next_image != NULL; )
cristy8e9bd3e2010-08-29 00:03:56 +00002858 {
2859 next_image->compression=NoCompression;
cristya4e5f472011-11-09 00:42:46 +00002860 (void) WriteBlobMSBLong(image,(unsigned int) next_image->page.y);
2861 (void) WriteBlobMSBLong(image,(unsigned int) next_image->page.x);
cristy0e274112014-03-25 14:09:19 +00002862 (void) WriteBlobMSBLong(image,(unsigned int) (next_image->page.y+
2863 next_image->rows));
2864 (void) WriteBlobMSBLong(image,(unsigned int) (next_image->page.x+
2865 next_image->columns));
cristy8e9bd3e2010-08-29 00:03:56 +00002866 packet_size=next_image->depth > 8 ? 2UL : 1UL;
2867 channel_size=(unsigned int) ((packet_size*next_image->rows*
2868 next_image->columns)+2);
dirka7014c82015-04-06 08:43:28 +00002869 if ((IsImageGray(next_image) != MagickFalse) ||
cristy8e9bd3e2010-08-29 00:03:56 +00002870 (next_image->storage_class == PseudoClass))
2871 {
2872 (void) WriteBlobMSBShort(image,(unsigned short)
cristy17f11b02014-12-20 19:37:04 +00002873 (next_image->alpha_trait != UndefinedPixelTrait ? 2 : 1));
cristy8e9bd3e2010-08-29 00:03:56 +00002874 (void) WriteBlobMSBShort(image,0);
2875 (void) SetPSDSize(&psd_info,image,channel_size);
cristy17f11b02014-12-20 19:37:04 +00002876 if (next_image->alpha_trait != UndefinedPixelTrait)
cristy8e9bd3e2010-08-29 00:03:56 +00002877 {
2878 (void) WriteBlobMSBShort(image,(unsigned short) -1);
2879 (void) SetPSDSize(&psd_info,image,channel_size);
2880 }
2881 }
2882 else
2883 if (next_image->colorspace != CMYKColorspace)
2884 {
2885 (void) WriteBlobMSBShort(image,(unsigned short)
cristy17f11b02014-12-20 19:37:04 +00002886 (next_image->alpha_trait != UndefinedPixelTrait ? 4 : 3));
cristy8e9bd3e2010-08-29 00:03:56 +00002887 (void) WriteBlobMSBShort(image,0);
2888 (void) SetPSDSize(&psd_info,image,channel_size);
2889 (void) WriteBlobMSBShort(image,1);
2890 (void) SetPSDSize(&psd_info,image,channel_size);
2891 (void) WriteBlobMSBShort(image,2);
2892 (void) SetPSDSize(&psd_info,image,channel_size);
cristy17f11b02014-12-20 19:37:04 +00002893 if (next_image->alpha_trait != UndefinedPixelTrait)
cristy8e9bd3e2010-08-29 00:03:56 +00002894 {
2895 (void) WriteBlobMSBShort(image,(unsigned short) -1);
2896 (void) SetPSDSize(&psd_info,image,channel_size);
2897 }
2898 }
2899 else
2900 {
2901 (void) WriteBlobMSBShort(image,(unsigned short)
cristy8a46d822012-08-28 23:32:39 +00002902 (next_image->alpha_trait ? 5 : 4));
cristy8e9bd3e2010-08-29 00:03:56 +00002903 (void) WriteBlobMSBShort(image,0);
2904 (void) SetPSDSize(&psd_info,image,channel_size);
2905 (void) WriteBlobMSBShort(image,1);
2906 (void) SetPSDSize(&psd_info,image,channel_size);
2907 (void) WriteBlobMSBShort(image,2);
2908 (void) SetPSDSize(&psd_info,image,channel_size);
2909 (void) WriteBlobMSBShort(image,3);
2910 (void) SetPSDSize(&psd_info,image,channel_size);
cristy8a46d822012-08-28 23:32:39 +00002911 if (next_image->alpha_trait)
cristy8e9bd3e2010-08-29 00:03:56 +00002912 {
2913 (void) WriteBlobMSBShort(image,(unsigned short) -1);
2914 (void) SetPSDSize(&psd_info,image,channel_size);
2915 }
2916 }
2917 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
2918 (void) WriteBlob(image,4,(const unsigned char *)
2919 CompositeOperatorToPSDBlendMode(next_image->compose));
2920 (void) WriteBlobByte(image,255); /* layer opacity */
2921 (void) WriteBlobByte(image,0);
dirk48febd62014-04-08 04:26:07 +00002922 (void) WriteBlobByte(image,next_image->compose==NoCompositeOp ?
2923 1 << 0x02 : 1); /* layer properties - visible, etc. */
cristy8e9bd3e2010-08-29 00:03:56 +00002924 (void) WriteBlobByte(image,0);
cristyd15e6592011-10-15 00:13:06 +00002925 property=(const char *) GetImageProperty(next_image,"label",exception);
cristy8e9bd3e2010-08-29 00:03:56 +00002926 if (property == (const char *) NULL)
2927 {
dirk93b02b72013-11-16 16:03:36 +00002928 char
cristy151b66d2015-04-15 10:50:31 +00002929 layer_name[MagickPathExtent];
cristy7a1ce132013-11-14 12:38:14 +00002930
cristy8e9bd3e2010-08-29 00:03:56 +00002931 (void) WriteBlobMSBLong(image,16);
2932 (void) WriteBlobMSBLong(image,0);
2933 (void) WriteBlobMSBLong(image,0);
cristy151b66d2015-04-15 10:50:31 +00002934 (void) FormatLocaleString(layer_name,MagickPathExtent,"L%04ld",(long)
cristy7a1ce132013-11-14 12:38:14 +00002935 layer_count++);
2936 WritePascalString(image,layer_name,4);
cristy8e9bd3e2010-08-29 00:03:56 +00002937 }
2938 else
2939 {
2940 size_t
dirkc93932a2015-10-01 16:56:10 +02002941 label_length;
cristy3ed852e2009-09-05 21:47:34 +00002942
dirkc93932a2015-10-01 16:56:10 +02002943 label_length=strlen(property);
2944 (void) WriteBlobMSBLong(image,(unsigned int) (label_length+(4-
2945 (label_length % 4))+8));
cristy8e9bd3e2010-08-29 00:03:56 +00002946 (void) WriteBlobMSBLong(image,0);
2947 (void) WriteBlobMSBLong(image,0);
2948 WritePascalString(image,property,4);
2949 }
2950 next_image=GetNextImageInList(next_image);
2951 }
2952 /*
2953 Now the image data!
2954 */
2955 next_image=base_image;
2956 while (next_image != NULL)
2957 {
2958 status=WriteImageChannels(&psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002959 MagickTrue,exception);
cristy8e9bd3e2010-08-29 00:03:56 +00002960 next_image=GetNextImageInList(next_image);
2961 }
2962 (void) WriteBlobMSBLong(image,0); /* user mask data */
2963 base_image->compression=compression;
cristy144f1b62010-05-18 00:52:09 +00002964 }
cristy144f1b62010-05-18 00:52:09 +00002965 /*
2966 Write composite image.
2967 */
dirkf3081522015-06-21 09:31:18 +00002968 if (status != MagickFalse)
2969 status=WriteImageChannels(&psd_info,image_info,image,image,MagickFalse,
2970 exception);
cristy3ed852e2009-09-05 21:47:34 +00002971 (void) CloseBlob(image);
2972 return(status);
2973}