blob: f1cb9ab8491f020bb6e4bfe986c53f94fbab4791 [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% %
cristyb56bb242014-11-25 17:12:48 +000023% Copyright 1999-2015 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"
68#include "MagickCore/pixel.h"
69#include "MagickCore/pixel-accessor.h"
70#include "MagickCore/profile.h"
71#include "MagickCore/property.h"
72#include "MagickCore/quantum-private.h"
73#include "MagickCore/static.h"
74#include "MagickCore/string_.h"
dirkb41f0802013-12-26 16:47:58 +000075#ifdef MAGICKCORE_ZLIB_DELEGATE
76#include <zlib.h>
77#endif
dirk14c08dc2015-05-25 11:52:13 +000078#include "psd-private.h"
79
cristy3ed852e2009-09-05 21:47:34 +000080/*
cristy2d3d87f2010-03-01 00:23:08 +000081 Define declaractions.
82*/
cristyaca3ec52010-03-02 14:55:01 +000083#define MaxPSDChannels 56
cristybb503372010-05-27 20:51:26 +000084#define PSDQuantum(x) (((ssize_t) (x)+1) & -2)
cristy2d3d87f2010-03-01 00:23:08 +000085
86/*
87 Enumerated declaractions.
88*/
89typedef enum
90{
dirkb41f0802013-12-26 16:47:58 +000091 Raw = 0,
92 RLE = 1,
93 ZipWithoutPrediction = 2,
94 ZipWithPrediction = 3
95} PSDCompressionType;
96
97typedef enum
98{
cristy2d3d87f2010-03-01 00:23:08 +000099 BitmapMode = 0,
100 GrayscaleMode = 1,
101 IndexedMode = 2,
102 RGBMode = 3,
103 CMYKMode = 4,
104 MultichannelMode = 7,
105 DuotoneMode = 8,
106 LabMode = 9
107} PSDImageType;
108
109/*
110 Typedef declaractions.
111*/
112typedef struct _ChannelInfo
113{
114 short int
115 type;
116
cristybb503372010-05-27 20:51:26 +0000117 size_t
cristy2d3d87f2010-03-01 00:23:08 +0000118 size;
119} ChannelInfo;
120
dirka41d97f2015-05-25 07:56:17 +0000121typedef struct _MaskInfo
122{
123 Image
124 *image;
125
126 RectangleInfo
127 page;
128
129 unsigned char
130 background,
131 flags;
132} MaskInfo;
133
cristy2d3d87f2010-03-01 00:23:08 +0000134typedef struct _LayerInfo
135{
cristy2d3d87f2010-03-01 00:23:08 +0000136 ChannelInfo
137 channel_info[MaxPSDChannels];
138
139 char
140 blendkey[4];
141
dirka41d97f2015-05-25 07:56:17 +0000142 Image
143 *image;
144
145 MaskInfo
146 mask;
147
cristy2d3d87f2010-03-01 00:23:08 +0000148 Quantum
149 opacity;
150
dirka41d97f2015-05-25 07:56:17 +0000151 RectangleInfo
152 page;
cristy2d3d87f2010-03-01 00:23:08 +0000153
cristybb503372010-05-27 20:51:26 +0000154 size_t
cristy2d3d87f2010-03-01 00:23:08 +0000155 offset_x,
156 offset_y;
157
158 unsigned char
dirka41d97f2015-05-25 07:56:17 +0000159 clipping,
160 flags,
161 name[256],
162 visible;
cristy2d3d87f2010-03-01 00:23:08 +0000163
dirka41d97f2015-05-25 07:56:17 +0000164 unsigned short
165 channels;
cristy2d3d87f2010-03-01 00:23:08 +0000166} LayerInfo;
dirka41d97f2015-05-25 07:56:17 +0000167
cristy2d3d87f2010-03-01 00:23:08 +0000168/*
cristy3ed852e2009-09-05 21:47:34 +0000169 Forward declarations.
170*/
171static MagickBooleanType
cristy3a37efd2011-08-28 20:31:03 +0000172 WritePSDImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000173
174/*
175%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
176% %
177% %
178% %
cristy3ed852e2009-09-05 21:47:34 +0000179% I s P S D %
180% %
181% %
182% %
183%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
184%
185% IsPSD()() returns MagickTrue if the image format type, identified by the
186% magick string, is PSD.
187%
188% The format of the IsPSD method is:
189%
190% MagickBooleanType IsPSD(const unsigned char *magick,const size_t length)
191%
192% A description of each parameter follows:
193%
194% o magick: compare image format pattern against these bytes.
195%
196% o length: Specifies the length of the magick string.
197%
198*/
199static MagickBooleanType IsPSD(const unsigned char *magick,const size_t length)
200{
201 if (length < 4)
202 return(MagickFalse);
203 if (LocaleNCompare((const char *) magick,"8BPS",4) == 0)
204 return(MagickTrue);
205 return(MagickFalse);
206}
207
208/*
209%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
210% %
211% %
212% %
213% R e a d P S D I m a g e %
214% %
215% %
216% %
217%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
218%
219% ReadPSDImage() reads an Adobe Photoshop image file and returns it. It
220% allocates the memory necessary for the new Image structure and returns a
221% pointer to the new image.
222%
223% The format of the ReadPSDImage method is:
224%
cristy90de83d2015-04-08 22:04:41 +0000225% Image *ReadPSDImage(image_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000226%
227% A description of each parameter follows:
228%
229% o image_info: the image info.
230%
231% o exception: return any errors or warnings in this structure.
232%
233*/
234
cristy19eb6412010-04-23 14:42:29 +0000235static const char *CompositeOperatorToPSDBlendMode(CompositeOperator op)
cristycd081772010-03-22 17:19:12 +0000236{
237 const char
238 *blend_mode;
239
cristy19eb6412010-04-23 14:42:29 +0000240 switch (op)
cristycd081772010-03-22 17:19:12 +0000241 {
cristyfdc035a2014-06-26 11:29:29 +0000242 case ColorBurnCompositeOp: blend_mode = "idiv"; break;
243 case ColorDodgeCompositeOp: blend_mode = "div "; break;
cristy216b90f2014-04-12 22:59:20 +0000244 case ColorizeCompositeOp: blend_mode = "colr"; break;
245 case DarkenCompositeOp: blend_mode = "dark"; break;
246 case DifferenceCompositeOp: blend_mode = "diff"; break;
247 case DissolveCompositeOp: blend_mode = "diss"; break;
cristyfdc035a2014-06-26 11:29:29 +0000248 case ExclusionCompositeOp: blend_mode = "smud"; break;
cristy216b90f2014-04-12 22:59:20 +0000249 case HardLightCompositeOp: blend_mode = "hLit"; break;
cristyfdc035a2014-06-26 11:29:29 +0000250 case HardMixCompositeOp: blend_mode = "hMix"; break;
cristy216b90f2014-04-12 22:59:20 +0000251 case HueCompositeOp: blend_mode = "hue "; break;
252 case LightenCompositeOp: blend_mode = "lite"; break;
cristyfdc035a2014-06-26 11:29:29 +0000253 case LinearBurnCompositeOp: blend_mode = "lbrn"; break;
254 case LinearDodgeCompositeOp:blend_mode = "lddg"; break;
255 case LinearLightCompositeOp:blend_mode = "lLit"; break;
cristy216b90f2014-04-12 22:59:20 +0000256 case LuminizeCompositeOp: blend_mode = "lum "; break;
257 case MultiplyCompositeOp: blend_mode = "mul "; break;
258 case OverCompositeOp: blend_mode = "norm"; break;
259 case OverlayCompositeOp: blend_mode = "over"; break;
cristyfdc035a2014-06-26 11:29:29 +0000260 case PinLightCompositeOp: blend_mode = "pLit"; break;
cristy216b90f2014-04-12 22:59:20 +0000261 case SaturateCompositeOp: blend_mode = "sat "; break;
262 case ScreenCompositeOp: blend_mode = "scrn"; break;
263 case SoftLightCompositeOp: blend_mode = "sLit"; break;
cristyfdc035a2014-06-26 11:29:29 +0000264 case VividLightCompositeOp: blend_mode = "vLit"; break;
cristy216b90f2014-04-12 22:59:20 +0000265 default: blend_mode = "norm";
cristycd081772010-03-22 17:19:12 +0000266 }
267 return(blend_mode);
268}
269
dirkb41f0802013-12-26 16:47:58 +0000270static inline CompressionType ConvertPSDCompression(
271 PSDCompressionType compression)
272{
273 switch (compression)
274 {
275 case RLE:
276 return RLECompression;
277 case ZipWithPrediction:
278 case ZipWithoutPrediction:
279 return ZipCompression;
280 default:
281 return NoCompression;
282 }
283}
284
285static MagickStatusType CorrectPSDOpacity(LayerInfo* layer_info,
286 ExceptionInfo *exception)
287{
288 register Quantum
289 *q;
290
291 register ssize_t
292 x;
293
294 ssize_t
295 y;
296
297 if (layer_info->opacity == OpaqueAlpha)
298 return(MagickTrue);
299
300 layer_info->image->alpha_trait=BlendPixelTrait;
301 for (y=0; y < (ssize_t) layer_info->image->rows; y++)
302 {
cristyd8083a62014-02-01 13:53:44 +0000303 q=GetAuthenticPixels(layer_info->image,0,y,layer_info->image->columns,1,
304 exception);
dirkb41f0802013-12-26 16:47:58 +0000305 if (q == (Quantum *) NULL)
306 break;
307 for (x=0; x < (ssize_t) layer_info->image->columns; x++)
308 {
309 SetPixelAlpha(layer_info->image,(Quantum) (QuantumScale*(GetPixelAlpha(
310 layer_info->image,q))*layer_info->opacity),q);
311 q+=GetPixelChannels(layer_info->image);
312 }
313 if (SyncAuthenticPixels(layer_info->image,exception) == MagickFalse)
314 return(MagickFalse);
315 }
316
317 return(MagickTrue);
318}
319
cristycd081772010-03-22 17:19:12 +0000320static ssize_t DecodePSDPixels(const size_t number_compact_pixels,
cristybb503372010-05-27 20:51:26 +0000321 const unsigned char *compact_pixels,const ssize_t depth,
cristycd081772010-03-22 17:19:12 +0000322 const size_t number_pixels,unsigned char *pixels)
323{
dirk891df412014-12-17 07:25:25 +0000324#define CheckNumberCompactPixels \
325 if (packets == 0) \
326 return(i); \
327 packets--
328
329#define CheckNumberPixels(count) \
330 if (((ssize_t) i + count) > (ssize_t) number_pixels) \
331 return(i); \
332 i+=count
333
cristycd081772010-03-22 17:19:12 +0000334 int
335 pixel;
336
337 register ssize_t
338 i,
339 j;
340
cristycd081772010-03-22 17:19:12 +0000341 size_t
342 length;
343
cristy802d3642011-04-27 02:02:41 +0000344 ssize_t
345 packets;
346
cristycd081772010-03-22 17:19:12 +0000347 packets=(ssize_t) number_compact_pixels;
348 for (i=0; (packets > 1) && (i < (ssize_t) number_pixels); )
349 {
dirkdb435e22015-01-24 11:38:04 +0000350 packets--;
dirk40084fa2014-02-03 21:29:38 +0000351 length=(size_t) (*compact_pixels++);
cristycd081772010-03-22 17:19:12 +0000352 if (length == 128)
353 continue;
354 if (length > 128)
355 {
356 length=256-length+1;
dirk891df412014-12-17 07:25:25 +0000357 CheckNumberCompactPixels;
cristycd081772010-03-22 17:19:12 +0000358 pixel=(*compact_pixels++);
cristy284c7d82010-04-24 00:19:14 +0000359 for (j=0; j < (ssize_t) length; j++)
cristycd081772010-03-22 17:19:12 +0000360 {
361 switch (depth)
362 {
363 case 1:
364 {
dirk891df412014-12-17 07:25:25 +0000365 CheckNumberPixels(8);
cristy284c7d82010-04-24 00:19:14 +0000366 *pixels++=(pixel >> 7) & 0x01 ? 0U : 255U;
367 *pixels++=(pixel >> 6) & 0x01 ? 0U : 255U;
368 *pixels++=(pixel >> 5) & 0x01 ? 0U : 255U;
369 *pixels++=(pixel >> 4) & 0x01 ? 0U : 255U;
370 *pixels++=(pixel >> 3) & 0x01 ? 0U : 255U;
371 *pixels++=(pixel >> 2) & 0x01 ? 0U : 255U;
372 *pixels++=(pixel >> 1) & 0x01 ? 0U : 255U;
373 *pixels++=(pixel >> 0) & 0x01 ? 0U : 255U;
cristycd081772010-03-22 17:19:12 +0000374 break;
375 }
376 case 2:
377 {
dirk891df412014-12-17 07:25:25 +0000378 CheckNumberPixels(4);
cristy284c7d82010-04-24 00:19:14 +0000379 *pixels++=(unsigned char) ((pixel >> 6) & 0x03);
380 *pixels++=(unsigned char) ((pixel >> 4) & 0x03);
381 *pixels++=(unsigned char) ((pixel >> 2) & 0x03);
382 *pixels++=(unsigned char) ((pixel & 0x03) & 0x03);
dirk891df412014-12-17 07:25:25 +0000383 break;
384 }
385 case 4:
386 {
387 CheckNumberPixels(2);
388 *pixels++=(unsigned char) ((pixel >> 4) & 0xff);
389 *pixels++=(unsigned char) ((pixel & 0x0f) & 0xff);
cristycd081772010-03-22 17:19:12 +0000390 break;
391 }
392 default:
393 {
dirk891df412014-12-17 07:25:25 +0000394 CheckNumberPixels(1);
cristy284c7d82010-04-24 00:19:14 +0000395 *pixels++=(unsigned char) pixel;
cristycd081772010-03-22 17:19:12 +0000396 break;
397 }
398 }
399 }
400 continue;
401 }
402 length++;
cristy284c7d82010-04-24 00:19:14 +0000403 for (j=0; j < (ssize_t) length; j++)
cristycd081772010-03-22 17:19:12 +0000404 {
405 switch (depth)
406 {
407 case 1:
408 {
dirk891df412014-12-17 07:25:25 +0000409 CheckNumberPixels(8);
cristy284c7d82010-04-24 00:19:14 +0000410 *pixels++=(*compact_pixels >> 7) & 0x01 ? 0U : 255U;
411 *pixels++=(*compact_pixels >> 6) & 0x01 ? 0U : 255U;
412 *pixels++=(*compact_pixels >> 5) & 0x01 ? 0U : 255U;
413 *pixels++=(*compact_pixels >> 4) & 0x01 ? 0U : 255U;
414 *pixels++=(*compact_pixels >> 3) & 0x01 ? 0U : 255U;
415 *pixels++=(*compact_pixels >> 2) & 0x01 ? 0U : 255U;
416 *pixels++=(*compact_pixels >> 1) & 0x01 ? 0U : 255U;
417 *pixels++=(*compact_pixels >> 0) & 0x01 ? 0U : 255U;
cristycd081772010-03-22 17:19:12 +0000418 break;
419 }
420 case 2:
421 {
dirk891df412014-12-17 07:25:25 +0000422 CheckNumberPixels(4);
cristy618a9662010-03-23 13:59:54 +0000423 *pixels++=(*compact_pixels >> 6) & 0x03;
424 *pixels++=(*compact_pixels >> 4) & 0x03;
425 *pixels++=(*compact_pixels >> 2) & 0x03;
426 *pixels++=(*compact_pixels & 0x03) & 0x03;
dirk891df412014-12-17 07:25:25 +0000427 break;
428 }
429 case 4:
430 {
431 CheckNumberPixels(2);
432 *pixels++=(*compact_pixels >> 4) & 0xff;
433 *pixels++=(*compact_pixels & 0x0f) & 0xff;
cristycd081772010-03-22 17:19:12 +0000434 break;
435 }
436 default:
437 {
dirk891df412014-12-17 07:25:25 +0000438 CheckNumberPixels(1);
cristycd081772010-03-22 17:19:12 +0000439 *pixels++=(*compact_pixels);
cristycd081772010-03-22 17:19:12 +0000440 break;
441 }
442 }
dirk891df412014-12-17 07:25:25 +0000443 CheckNumberCompactPixels;
cristycd081772010-03-22 17:19:12 +0000444 compact_pixels++;
445 }
446 }
447 return(i);
448}
449
dirkbd8bd852013-12-28 22:55:19 +0000450static inline LayerInfo *DestroyLayerInfo(LayerInfo *layer_info,
451 const ssize_t number_layers)
452{
453 ssize_t
454 i;
455
456 for (i=0; i<number_layers; i++)
457 {
458 if (layer_info[i].image != (Image *) NULL)
459 layer_info[i].image=DestroyImage(layer_info[i].image);
dirka41d97f2015-05-25 07:56:17 +0000460 if (layer_info[i].mask.image != (Image *) NULL)
461 layer_info[i].mask.image=DestroyImage(layer_info[i].mask.image);
dirkbd8bd852013-12-28 22:55:19 +0000462 }
463
464 return (LayerInfo *) RelinquishMagickMemory(layer_info);
465}
466
dirkb41f0802013-12-26 16:47:58 +0000467static inline size_t GetPSDPacketSize(Image *image)
cristy2d3d87f2010-03-01 00:23:08 +0000468{
dirkb41f0802013-12-26 16:47:58 +0000469 if (image->storage_class == PseudoClass)
470 {
471 if (image->colors > 256)
472 return(2);
473 else if (image->depth > 8)
474 return(2);
475 }
476 else
477 if (image->depth > 8)
478 return(2);
479
480 return(1);
cristy2d3d87f2010-03-01 00:23:08 +0000481}
482
dirk4d9863c2014-03-28 19:48:49 +0000483static inline MagickSizeType GetPSDSize(const PSDInfo *psd_info,Image *image)
cristy2d3d87f2010-03-01 00:23:08 +0000484{
485 if (psd_info->version == 1)
486 return((MagickSizeType) ReadBlobMSBLong(image));
487 return((MagickSizeType) ReadBlobMSBLongLong(image));
488}
489
dirkb41f0802013-12-26 16:47:58 +0000490static inline size_t GetPSDRowSize(Image *image)
491{
492 if (image->depth == 1)
493 return((image->columns+7)/8);
494 else
495 return(image->columns*GetPSDPacketSize(image));
496}
497
cristycd081772010-03-22 17:19:12 +0000498static const char *ModeToString(PSDImageType type)
cristy3ed852e2009-09-05 21:47:34 +0000499{
cristycd081772010-03-22 17:19:12 +0000500 switch (type)
cristy3ed852e2009-09-05 21:47:34 +0000501 {
502 case BitmapMode: return "Bitmap";
503 case GrayscaleMode: return "Grayscale";
504 case IndexedMode: return "Indexed";
505 case RGBMode: return "RGB";
506 case CMYKMode: return "CMYK";
507 case MultichannelMode: return "Multichannel";
508 case DuotoneMode: return "Duotone";
509 case LabMode: return "L*A*B";
510 default: return "unknown";
511 }
512}
513
dirkd9795a12014-11-11 22:01:21 +0000514static void NegateCMYK(Image *image,ExceptionInfo *exception)
515{
516 ChannelType
517 channel_mask;
518
519 channel_mask=SetImageChannelMask(image,(ChannelType)(AllChannels &~
520 AlphaChannel));
521 NegateImage(image,MagickFalse,exception);
522 (void) SetImageChannelMask(image,channel_mask);
523}
524
dirk7ec89322014-12-16 22:50:15 +0000525static void ParseImageResourceBlocks(Image *image,
dirk18c0e4d2014-02-22 22:24:05 +0000526 const unsigned char *blocks,size_t length,
527 MagickBooleanType *has_merged_image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000528{
529 const unsigned char
530 *p;
531
532 StringInfo
533 *profile;
534
cristy6befb0f2010-05-31 14:33:15 +0000535 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000536 count,
cristy6befb0f2010-05-31 14:33:15 +0000537 long_sans;
cristy3ed852e2009-09-05 21:47:34 +0000538
539 unsigned short
540 id,
541 short_sans;
542
543 if (length < 16)
dirk7ec89322014-12-16 22:50:15 +0000544 return;
cristyb3f97ae2015-05-18 12:29:32 +0000545 profile=BlobToStringInfo((const unsigned char *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000546 SetStringInfoDatum(profile,blocks);
cristyd15e6592011-10-15 00:13:06 +0000547 (void) SetImageProfile(image,"8bim",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000548 profile=DestroyStringInfo(profile);
549 for (p=blocks; (p >= blocks) && (p < (blocks+length-16)); )
550 {
551 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
552 break;
cristy6befb0f2010-05-31 14:33:15 +0000553 p=PushLongPixel(MSBEndian,p,&long_sans);
cristyf11065e2010-05-14 13:26:59 +0000554 p=PushShortPixel(MSBEndian,p,&id);
555 p=PushShortPixel(MSBEndian,p,&short_sans);
556 p=PushLongPixel(MSBEndian,p,&count);
dirk7ec89322014-12-16 22:50:15 +0000557 if (p+count > blocks+length)
558 return;
cristy3ed852e2009-09-05 21:47:34 +0000559 switch (id)
560 {
561 case 0x03ed:
562 {
cristy1e4a80b2010-05-14 16:18:26 +0000563 char
cristy151b66d2015-04-15 10:50:31 +0000564 value[MagickPathExtent];
cristy1e4a80b2010-05-14 16:18:26 +0000565
cristy3ed852e2009-09-05 21:47:34 +0000566 unsigned short
567 resolution;
568
569 /*
570 Resolution info.
571 */
cristyf11065e2010-05-14 13:26:59 +0000572 p=PushShortPixel(MSBEndian,p,&resolution);
cristy2a11bef2011-10-28 18:33:11 +0000573 image->resolution.x=(double) resolution;
cristy151b66d2015-04-15 10:50:31 +0000574 (void) FormatLocaleString(value,MagickPathExtent,"%g",image->resolution.x);
cristyd15e6592011-10-15 00:13:06 +0000575 (void) SetImageProperty(image,"tiff:XResolution",value,exception);
cristyf11065e2010-05-14 13:26:59 +0000576 p=PushShortPixel(MSBEndian,p,&short_sans);
577 p=PushShortPixel(MSBEndian,p,&short_sans);
578 p=PushShortPixel(MSBEndian,p,&short_sans);
579 p=PushShortPixel(MSBEndian,p,&resolution);
cristy2a11bef2011-10-28 18:33:11 +0000580 image->resolution.y=(double) resolution;
cristy151b66d2015-04-15 10:50:31 +0000581 (void) FormatLocaleString(value,MagickPathExtent,"%g",image->resolution.y);
cristyd15e6592011-10-15 00:13:06 +0000582 (void) SetImageProperty(image,"tiff:YResolution",value,exception);
cristyf11065e2010-05-14 13:26:59 +0000583 p=PushShortPixel(MSBEndian,p,&short_sans);
584 p=PushShortPixel(MSBEndian,p,&short_sans);
585 p=PushShortPixel(MSBEndian,p,&short_sans);
cristy15893bd2012-12-19 14:40:13 +0000586 image->units=PixelsPerInchResolution;
cristy3ed852e2009-09-05 21:47:34 +0000587 break;
588 }
dirk18c0e4d2014-02-22 22:24:05 +0000589 case 0x0421:
590 {
591 if (*(p+4) == 0)
592 *has_merged_image=MagickFalse;
dirkc3378432014-05-18 06:50:37 +0000593 p+=count;
594 break;
dirk18c0e4d2014-02-22 22:24:05 +0000595 }
cristy3ed852e2009-09-05 21:47:34 +0000596 default:
597 {
598 p+=count;
599 break;
600 }
601 }
602 if ((count & 0x01) != 0)
603 p++;
604 }
dirk7ec89322014-12-16 22:50:15 +0000605 return;
cristy3ed852e2009-09-05 21:47:34 +0000606}
607
cristycd081772010-03-22 17:19:12 +0000608static CompositeOperator PSDBlendModeToCompositeOperator(const char *mode)
cristy56ed31c2010-03-22 00:46:21 +0000609{
cristycd081772010-03-22 17:19:12 +0000610 if (mode == (const char *) NULL)
611 return(OverCompositeOp);
612 if (LocaleNCompare(mode,"norm",4) == 0)
613 return(OverCompositeOp);
614 if (LocaleNCompare(mode,"mul ",4) == 0)
615 return(MultiplyCompositeOp);
616 if (LocaleNCompare(mode,"diss",4) == 0)
617 return(DissolveCompositeOp);
618 if (LocaleNCompare(mode,"diff",4) == 0)
619 return(DifferenceCompositeOp);
620 if (LocaleNCompare(mode,"dark",4) == 0)
621 return(DarkenCompositeOp);
622 if (LocaleNCompare(mode,"lite",4) == 0)
623 return(LightenCompositeOp);
624 if (LocaleNCompare(mode,"hue ",4) == 0)
625 return(HueCompositeOp);
626 if (LocaleNCompare(mode,"sat ",4) == 0)
627 return(SaturateCompositeOp);
628 if (LocaleNCompare(mode,"colr",4) == 0)
629 return(ColorizeCompositeOp);
630 if (LocaleNCompare(mode,"lum ",4) == 0)
631 return(LuminizeCompositeOp);
632 if (LocaleNCompare(mode,"scrn",4) == 0)
633 return(ScreenCompositeOp);
634 if (LocaleNCompare(mode,"over",4) == 0)
635 return(OverlayCompositeOp);
636 if (LocaleNCompare(mode,"hLit",4) == 0)
cristy216b90f2014-04-12 22:59:20 +0000637 return(HardLightCompositeOp);
cristycd081772010-03-22 17:19:12 +0000638 if (LocaleNCompare(mode,"sLit",4) == 0)
cristy216b90f2014-04-12 22:59:20 +0000639 return(SoftLightCompositeOp);
cristycd081772010-03-22 17:19:12 +0000640 if (LocaleNCompare(mode,"smud",4) == 0)
cristyfdc035a2014-06-26 11:29:29 +0000641 return(ExclusionCompositeOp);
cristycd081772010-03-22 17:19:12 +0000642 if (LocaleNCompare(mode,"div ",4) == 0)
cristyfdc035a2014-06-26 11:29:29 +0000643 return(ColorDodgeCompositeOp);
cristycd081772010-03-22 17:19:12 +0000644 if (LocaleNCompare(mode,"idiv",4) == 0)
cristyfdc035a2014-06-26 11:29:29 +0000645 return(ColorBurnCompositeOp);
646 if (LocaleNCompare(mode,"lbrn",4) == 0)
647 return(LinearBurnCompositeOp);
648 if (LocaleNCompare(mode,"lddg",4) == 0)
649 return(LinearDodgeCompositeOp);
650 if (LocaleNCompare(mode,"lLit",4) == 0)
651 return(LinearLightCompositeOp);
652 if (LocaleNCompare(mode,"vLit",4) == 0)
653 return(VividLightCompositeOp);
654 if (LocaleNCompare(mode,"pLit",4) == 0)
655 return(PinLightCompositeOp);
656 if (LocaleNCompare(mode,"hMix",4) == 0)
657 return(HardMixCompositeOp);
cristycd081772010-03-22 17:19:12 +0000658 return(OverCompositeOp);
cristy56ed31c2010-03-22 00:46:21 +0000659}
660
dirkb41f0802013-12-26 16:47:58 +0000661static MagickStatusType ReadPSDChannelPixels(Image *image,
662 const size_t channels,const size_t row,const ssize_t type,
663 const unsigned char *pixels,ExceptionInfo *exception)
cristy56ed31c2010-03-22 00:46:21 +0000664{
cristy56ed31c2010-03-22 00:46:21 +0000665 Quantum
666 pixel;
667
cristy7753b2a2011-02-19 18:36:52 +0000668 register const unsigned char
669 *p;
670
cristy4c08aed2011-07-01 19:47:50 +0000671 register Quantum
cristy56ed31c2010-03-22 00:46:21 +0000672 *q;
673
cristy7753b2a2011-02-19 18:36:52 +0000674 register ssize_t
675 x;
cristy56ed31c2010-03-22 00:46:21 +0000676
677 size_t
678 packet_size;
679
dirkb41f0802013-12-26 16:47:58 +0000680 unsigned short
681 nibble;
682
683 p=pixels;
684 q=GetAuthenticPixels(image,0,row,image->columns,1,exception);
685 if (q == (Quantum *) NULL)
686 return MagickFalse;
687 packet_size=GetPSDPacketSize(image);
688 for (x=0; x < (ssize_t) image->columns; x++)
689 {
690 if (packet_size == 1)
691 pixel=ScaleCharToQuantum(*p++);
692 else
693 {
694 p=PushShortPixel(MSBEndian,p,&nibble);
695 pixel=ScaleShortToQuantum(nibble);
696 }
697 switch (type)
698 {
699 case -1:
700 {
dirkd9795a12014-11-11 22:01:21 +0000701 SetPixelAlpha(image,pixel,q);
dirkb41f0802013-12-26 16:47:58 +0000702 break;
703 }
dirka41d97f2015-05-25 07:56:17 +0000704 case -2:
dirkb41f0802013-12-26 16:47:58 +0000705 case 0:
706 {
707 SetPixelRed(image,pixel,q);
dirka41d97f2015-05-25 07:56:17 +0000708 if (channels == 1 || type == -2)
dirkb41f0802013-12-26 16:47:58 +0000709 SetPixelGray(image,pixel,q);
dirkb41f0802013-12-26 16:47:58 +0000710 if (image->storage_class == PseudoClass)
711 {
712 if (packet_size == 1)
713 SetPixelIndex(image,ScaleQuantumToChar(pixel),q);
714 else
715 SetPixelIndex(image,ScaleQuantumToShort(pixel),q);
cristy11a06d32015-01-04 12:03:27 +0000716 SetPixelViaPixelInfo(image,image->colormap+(ssize_t)
cristyf432c632014-12-07 15:11:28 +0000717 ConstrainColormapIndex(image,GetPixelIndex(image,q),exception),q);
dirkb41f0802013-12-26 16:47:58 +0000718 if (image->depth == 1)
719 {
720 ssize_t
721 bit,
722 number_bits;
723
724 number_bits=image->columns-x;
725 if (number_bits > 8)
726 number_bits=8;
727 for (bit=0; bit < number_bits; bit++)
728 {
729 SetPixelIndex(image,(((unsigned char) pixel) &
730 (0x01 << (7-bit))) != 0 ? 0 : 255,q);
cristy11a06d32015-01-04 12:03:27 +0000731 SetPixelViaPixelInfo(image,image->colormap+(ssize_t)
dirkb41f0802013-12-26 16:47:58 +0000732 GetPixelIndex(image,q),q);
733 q+=GetPixelChannels(image);
734 x++;
735 }
dirk5a4bdff2014-07-31 20:49:41 +0000736 x--;
737 continue;
dirkb41f0802013-12-26 16:47:58 +0000738 }
739 }
740 break;
741 }
742 case 1:
743 {
744 if (image->storage_class == PseudoClass)
dirkd9795a12014-11-11 22:01:21 +0000745 SetPixelAlpha(image,pixel,q);
dirkb41f0802013-12-26 16:47:58 +0000746 else
747 SetPixelGreen(image,pixel,q);
748 break;
749 }
750 case 2:
751 {
752 if (image->storage_class == PseudoClass)
dirkd9795a12014-11-11 22:01:21 +0000753 SetPixelAlpha(image,pixel,q);
dirkb41f0802013-12-26 16:47:58 +0000754 else
755 SetPixelBlue(image,pixel,q);
756 break;
757 }
758 case 3:
759 {
760 if (image->colorspace == CMYKColorspace)
761 SetPixelBlack(image,pixel,q);
762 else
cristy17f11b02014-12-20 19:37:04 +0000763 if (image->alpha_trait != UndefinedPixelTrait)
dirkd9795a12014-11-11 22:01:21 +0000764 SetPixelAlpha(image,pixel,q);
dirkb41f0802013-12-26 16:47:58 +0000765 break;
766 }
767 case 4:
768 {
769 if ((IssRGBCompatibleColorspace(image->colorspace) != MagickFalse) &&
770 (channels > 3))
771 break;
cristy17f11b02014-12-20 19:37:04 +0000772 if (image->alpha_trait != UndefinedPixelTrait)
dirkd9795a12014-11-11 22:01:21 +0000773 SetPixelAlpha(image,pixel,q);
dirkb41f0802013-12-26 16:47:58 +0000774 break;
775 }
776 default:
777 break;
778 }
779 q+=GetPixelChannels(image);
780 }
781 return(SyncAuthenticPixels(image,exception));
782}
783
784static MagickStatusType ReadPSDChannelRaw(Image *image,const size_t channels,
785 const ssize_t type,ExceptionInfo *exception)
786{
787 MagickStatusType
788 status;
789
790 size_t
791 count,
792 row_size;
793
794 ssize_t
795 y;
796
797 unsigned char
798 *pixels;
799
800 if (image->debug != MagickFalse)
801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
802 " layer data is RAW");
803
804 row_size=GetPSDRowSize(image);
dirk891df412014-12-17 07:25:25 +0000805 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
dirkb41f0802013-12-26 16:47:58 +0000806 if (pixels == (unsigned char *) NULL)
807 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
808 image->filename);
809
810 status=MagickTrue;
811 for (y=0; y < (ssize_t) image->rows; y++)
812 {
813 status=MagickFalse;
814
815 count=ReadBlob(image,row_size,pixels);
816 if (count != row_size)
817 break;
818
819 status=ReadPSDChannelPixels(image,channels,y,type,pixels,exception);
820 if (status == MagickFalse)
821 break;
822 }
823
824 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
825 return(status);
826}
827
828static inline MagickOffsetType *ReadPSDRLEOffsets(Image *image,
dirk4d9863c2014-03-28 19:48:49 +0000829 const PSDInfo *psd_info,const size_t size)
dirkb41f0802013-12-26 16:47:58 +0000830{
831 MagickOffsetType
832 *offsets;
833
834 ssize_t
835 y;
836
837 offsets=(MagickOffsetType *) AcquireQuantumMemory(size,sizeof(*offsets));
838 if(offsets != (MagickOffsetType *) NULL)
839 {
840 for (y=0; y < (ssize_t) size; y++)
841 {
842 if (psd_info->version == 1)
843 offsets[y]=(MagickOffsetType) ReadBlobMSBShort(image);
844 else
845 offsets[y]=(MagickOffsetType) ReadBlobMSBLong(image);
846 }
847 }
848 return offsets;
849}
850
dirk4d9863c2014-03-28 19:48:49 +0000851static MagickStatusType ReadPSDChannelRLE(Image *image,const PSDInfo *psd_info,
dirkb41f0802013-12-26 16:47:58 +0000852 const ssize_t type,MagickOffsetType *offsets,ExceptionInfo *exception)
853{
854 MagickStatusType
855 status;
856
857 size_t
858 length,
859 row_size;
860
cristy56ed31c2010-03-22 00:46:21 +0000861 ssize_t
cristyd05dca12010-07-25 02:32:32 +0000862 count,
863 y;
cristy56ed31c2010-03-22 00:46:21 +0000864
865 unsigned char
866 *compact_pixels,
867 *pixels;
868
dirkb41f0802013-12-26 16:47:58 +0000869 if (image->debug != MagickFalse)
870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
871 " layer data is RLE compressed");
cristy56ed31c2010-03-22 00:46:21 +0000872
dirkb41f0802013-12-26 16:47:58 +0000873 row_size=GetPSDRowSize(image);
dirk891df412014-12-17 07:25:25 +0000874 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
cristy56ed31c2010-03-22 00:46:21 +0000875 if (pixels == (unsigned char *) NULL)
876 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
877 image->filename);
cristye195f262010-04-16 18:12:35 +0000878
dirkb41f0802013-12-26 16:47:58 +0000879 length=0;
880 for (y=0; y < (ssize_t) image->rows; y++)
881 if ((MagickOffsetType) length < offsets[y])
882 length=(size_t) offsets[y];
883
884 if (length > row_size + 256) // arbitrary number
885 {
886 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
887 ThrowBinaryException(ResourceLimitError,"InvalidLength",
888 image->filename);
cristy56ed31c2010-03-22 00:46:21 +0000889 }
dirkb41f0802013-12-26 16:47:58 +0000890
dirk891df412014-12-17 07:25:25 +0000891 compact_pixels=(unsigned char *) AcquireQuantumMemory(length,sizeof(*pixels));
dirkb41f0802013-12-26 16:47:58 +0000892 if (compact_pixels == (unsigned char *) NULL)
893 {
894 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
895 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
896 image->filename);
897 }
898
899 (void) ResetMagickMemory(compact_pixels,0,length*sizeof(*compact_pixels));
900
901 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +0000902 for (y=0; y < (ssize_t) image->rows; y++)
cristy56ed31c2010-03-22 00:46:21 +0000903 {
dirkb41f0802013-12-26 16:47:58 +0000904 status=MagickFalse;
cristy3b004082010-08-12 19:56:38 +0000905
dirkb41f0802013-12-26 16:47:58 +0000906 count=ReadBlob(image,(size_t) offsets[y],compact_pixels);
907 if (count != (ssize_t) offsets[y])
908 break;
909
910 count=DecodePSDPixels((size_t) offsets[y],compact_pixels,
911 (ssize_t) (image->depth == 1 ? 123456 : image->depth),row_size,pixels);
912 if (count != (ssize_t) row_size)
913 break;
914
915 status=ReadPSDChannelPixels(image,psd_info->channels,y,type,pixels,
916 exception);
917 if (status == MagickFalse)
cristy56ed31c2010-03-22 00:46:21 +0000918 break;
919 }
dirkb41f0802013-12-26 16:47:58 +0000920
921 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
cristy56ed31c2010-03-22 00:46:21 +0000922 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
dirkb41f0802013-12-26 16:47:58 +0000923 return(status);
cristy56ed31c2010-03-22 00:46:21 +0000924}
925
dirkb41f0802013-12-26 16:47:58 +0000926#ifdef MAGICKCORE_ZLIB_DELEGATE
cristyff30f832014-11-05 14:04:21 +0000927static MagickStatusType ReadPSDChannelZip(Image *image,const size_t channels,
928 const ssize_t type,const PSDCompressionType compression,
929 const size_t compact_size,ExceptionInfo *exception)
930{
dirkb41f0802013-12-26 16:47:58 +0000931 MagickStatusType
932 status;
933
934 register unsigned char
935 *p;
936
937 size_t
938 count,
939 length,
940 packet_size,
941 row_size;
942
943 ssize_t
944 y;
945
946 unsigned char
947 *compact_pixels,
948 *pixels;
949
950 z_stream
951 stream;
952
953 if (image->debug != MagickFalse)
954 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
dirk245cc752014-01-16 18:31:58 +0000955 " layer data is ZIP compressed");
dirkb41f0802013-12-26 16:47:58 +0000956
957 compact_pixels=(unsigned char *) AcquireQuantumMemory(compact_size,
958 sizeof(*compact_pixels));
959 if (compact_pixels == (unsigned char *) NULL)
960 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
961 image->filename);
962
963 packet_size=GetPSDPacketSize(image);
964 row_size=image->columns*packet_size;
965 count=image->rows*row_size;
966
967 pixels=(unsigned char *) AcquireQuantumMemory(count,sizeof(*pixels));
968 if (pixels == (unsigned char *) NULL)
969 {
970 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
971 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
972 image->filename);
973 }
974
975 ResetMagickMemory(&stream, 0, sizeof(z_stream));
976 stream.data_type=Z_BINARY;
977 (void) ReadBlob(image,compact_size,compact_pixels);
978
979 stream.next_in=(Bytef *)compact_pixels;
cristy0e274112014-03-25 14:09:19 +0000980 stream.avail_in=(unsigned int) compact_size;
dirkb41f0802013-12-26 16:47:58 +0000981 stream.next_out=(Bytef *)pixels;
cristy0e274112014-03-25 14:09:19 +0000982 stream.avail_out=(unsigned int) count;
dirkb41f0802013-12-26 16:47:58 +0000983
984 if(inflateInit(&stream) == Z_OK)
985 {
986 int
987 ret;
988
989 while (stream.avail_out > 0)
990 {
991 ret=inflate(&stream, Z_SYNC_FLUSH);
992 if (ret != Z_OK && ret != Z_STREAM_END)
993 {
994 compact_pixels=(unsigned char *) RelinquishMagickMemory(
995 compact_pixels);
996 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
997 return(MagickFalse);
998 }
999 }
1000 }
1001
1002 if (compression == ZipWithPrediction)
1003 {
1004 p=pixels;
1005 while(count > 0)
1006 {
1007 length=image->columns;
1008 while(--length)
1009 {
1010 if (packet_size == 2)
1011 {
1012 p[2]+=p[0]+((p[1]+p[3]) >> 8);
1013 p[3]+=p[1];
1014 }
1015 else
1016 *(p+1)+=*p;
1017 p+=packet_size;
1018 }
1019 p+=packet_size;
1020 count-=row_size;
1021 }
1022 }
1023
1024 status=MagickTrue;
1025 p=pixels;
1026 for (y=0; y < (ssize_t) image->rows; y++)
1027 {
1028 status=ReadPSDChannelPixels(image,channels,y,type,p,exception);
1029 if (status == MagickFalse)
1030 break;
1031
1032 p+=row_size;
1033 }
1034
1035 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1036 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1037 return(status);
dirkb41f0802013-12-26 16:47:58 +00001038}
cristy0d67b922014-11-05 19:29:56 +00001039#endif
dirkb41f0802013-12-26 16:47:58 +00001040
dirk4d9863c2014-03-28 19:48:49 +00001041static MagickStatusType ReadPSDChannel(Image *image,const PSDInfo *psd_info,
dirka41d97f2015-05-25 07:56:17 +00001042 LayerInfo* layer_info,const size_t channel,
dirkb41f0802013-12-26 16:47:58 +00001043 const PSDCompressionType compression,ExceptionInfo *exception)
1044{
dirka41d97f2015-05-25 07:56:17 +00001045 Image
1046 *channel_image,
1047 *mask;
1048
dirkb41f0802013-12-26 16:47:58 +00001049 MagickOffsetType
1050 offset;
1051
1052 MagickStatusType
1053 status;
1054
dirka41d97f2015-05-25 07:56:17 +00001055 channel_image=image;
1056 mask=(Image *) NULL;
dirk245cc752014-01-16 18:31:58 +00001057 if (layer_info->channel_info[channel].type < -1)
1058 {
dirka41d97f2015-05-25 07:56:17 +00001059 /*
1060 Ignore mask that is not a user supplied layer mask, if the mask is
1061 disabled or if the flags have unsupported values.
1062 */
1063 if (layer_info->channel_info[channel].type != -2 ||
1064 (layer_info->mask.flags > 3) || (layer_info->mask.flags & 0x02))
1065 {
1066 SeekBlob(image,layer_info->channel_info[channel].size-2,SEEK_CUR);
1067 return(MagickTrue);
1068 }
1069 mask=CloneImage(image,layer_info->mask.page.width,
1070 layer_info->mask.page.height,MagickFalse,exception);
1071 SetImageType(mask,GrayscaleType,exception);
1072 channel_image=mask;
dirk245cc752014-01-16 18:31:58 +00001073 }
1074
dirka41d97f2015-05-25 07:56:17 +00001075 offset=TellBlob(channel_image);
dirkb41f0802013-12-26 16:47:58 +00001076 status=MagickTrue;
1077 switch(compression)
1078 {
1079 case Raw:
dirka41d97f2015-05-25 07:56:17 +00001080 return(ReadPSDChannelRaw(channel_image,psd_info->channels,
dirkb41f0802013-12-26 16:47:58 +00001081 layer_info->channel_info[channel].type,exception));
1082 case RLE:
1083 {
1084 MagickOffsetType
1085 *offsets;
1086
dirka41d97f2015-05-25 07:56:17 +00001087 offsets=ReadPSDRLEOffsets(channel_image,psd_info,channel_image->rows);
dirkb41f0802013-12-26 16:47:58 +00001088 if (offsets == (MagickOffsetType *) NULL)
1089 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1090 image->filename);
dirka41d97f2015-05-25 07:56:17 +00001091 status=ReadPSDChannelRLE(channel_image,psd_info,
1092 layer_info->channel_info[channel].type,offsets,exception);
dirkb41f0802013-12-26 16:47:58 +00001093 offsets=(MagickOffsetType *) RelinquishMagickMemory(offsets);
1094 }
1095 break;
1096 case ZipWithPrediction:
1097 case ZipWithoutPrediction:
1098#ifdef MAGICKCORE_ZLIB_DELEGATE
dirka41d97f2015-05-25 07:56:17 +00001099 status=ReadPSDChannelZip(channel_image,layer_info->channels,
dirkb41f0802013-12-26 16:47:58 +00001100 layer_info->channel_info[channel].type,compression,
1101 layer_info->channel_info[channel].size-2,exception);
1102#else
1103 SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
1104 (void) ThrowMagickException(exception,GetMagickModule(),
1105 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
1106 "'%s' (ZLIB)",image->filename);
1107#endif
1108 break;
1109 default:
1110 SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
1111 (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
1112 "CompressionNotSupported","'%.20g'",(double) compression);
1113 break;
1114 }
1115
1116 if (status == MagickFalse)
1117 SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
dirka41d97f2015-05-25 07:56:17 +00001118 if (mask != (Image *) NULL)
1119 {
1120 if (status != MagickFalse)
1121 {
1122 PixelInfo
1123 color;
1124
1125 layer_info->mask.image=CloneImage(image,image->columns,image->rows,
1126 MagickTrue,exception);
1127 layer_info->mask.image->alpha_trait=UndefinedPixelTrait;
1128 GetPixelInfo(layer_info->mask.image,&color);
1129 color.red=layer_info->mask.background == 0 ? 0 : QuantumRange;
1130 SetImageColor(layer_info->mask.image,&color,exception);
1131 (void) CompositeImage(layer_info->mask.image,mask,OverCompositeOp,
1132 MagickTrue,layer_info->mask.page.x,layer_info->mask.page.y,
1133 exception);
1134 }
1135 DestroyImage(mask);
1136 }
dirkb41f0802013-12-26 16:47:58 +00001137
1138 return(status);
1139}
1140
dirk4d9863c2014-03-28 19:48:49 +00001141static MagickStatusType ReadPSDLayer(Image *image,const PSDInfo *psd_info,
dirkb41f0802013-12-26 16:47:58 +00001142 LayerInfo* layer_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001143{
cristy3ed852e2009-09-05 21:47:34 +00001144 char
cristy151b66d2015-04-15 10:50:31 +00001145 message[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001146
dirkb41f0802013-12-26 16:47:58 +00001147 MagickStatusType
1148 status;
1149
1150 PSDCompressionType
1151 compression;
1152
1153 ssize_t
1154 j;
1155
1156 if (image->debug != MagickFalse)
1157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1158 " setting up new layer image");
1159 (void) SetImageBackgroundColor(layer_info->image,exception);
1160 layer_info->image->compose=PSDBlendModeToCompositeOperator(
1161 layer_info->blendkey);
1162 if (layer_info->visible == MagickFalse)
1163 layer_info->image->compose=NoCompositeOp;
1164 if (psd_info->mode == CMYKMode)
1165 SetImageColorspace(layer_info->image,CMYKColorspace,exception);
cristyd8083a62014-02-01 13:53:44 +00001166 if ((psd_info->mode == BitmapMode) || (psd_info->mode == GrayscaleMode) ||
dirkb41f0802013-12-26 16:47:58 +00001167 (psd_info->mode == DuotoneMode))
1168 SetImageColorspace(layer_info->image,GRAYColorspace,exception);
1169 /*
1170 Set up some hidden attributes for folks that need them.
1171 */
dirka41d97f2015-05-25 07:56:17 +00001172 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
dirkb41f0802013-12-26 16:47:58 +00001173 (double) layer_info->page.x);
1174 (void) SetImageArtifact(layer_info->image,"psd:layer.x",message);
cristy151b66d2015-04-15 10:50:31 +00001175 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
dirkb41f0802013-12-26 16:47:58 +00001176 (double) layer_info->page.y);
1177 (void) SetImageArtifact(layer_info->image,"psd:layer.y",message);
cristy151b66d2015-04-15 10:50:31 +00001178 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",(double)
dirkb41f0802013-12-26 16:47:58 +00001179 layer_info->opacity);
1180 (void) SetImageArtifact(layer_info->image,"psd:layer.opacity",message);
1181 (void) SetImageProperty(layer_info->image,"label",(char *) layer_info->name,
1182 exception);
1183
1184 status=MagickTrue;
dirkb41f0802013-12-26 16:47:58 +00001185 for (j=0; j < (ssize_t) layer_info->channels; j++)
1186 {
1187 if (image->debug != MagickFalse)
1188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1189 " reading data for channel %.20g",(double) j);
1190
1191 compression=(PSDCompressionType) ReadBlobMSBShort(layer_info->image);
1192 layer_info->image->compression=ConvertPSDCompression(compression);
dirk6f205312014-01-31 22:25:10 +00001193 if (layer_info->channel_info[j].type == -1)
1194 layer_info->image->alpha_trait=BlendPixelTrait;
dirkd9795a12014-11-11 22:01:21 +00001195
dirk6f205312014-01-31 22:25:10 +00001196 status=ReadPSDChannel(layer_info->image,psd_info,layer_info,j,
1197 compression,exception);
dirkb41f0802013-12-26 16:47:58 +00001198
1199 if (status == MagickFalse)
1200 break;
1201 }
1202
dirk6f205312014-01-31 22:25:10 +00001203 if (status != MagickFalse)
dirkb41f0802013-12-26 16:47:58 +00001204 status=CorrectPSDOpacity(layer_info,exception);
1205
dirka41d97f2015-05-25 07:56:17 +00001206 if (status != MagickFalse)
1207 {
1208 if (layer_info->image->colorspace == CMYKColorspace)
1209 NegateCMYK(layer_info->image,exception);
1210 if (layer_info->mask.image != (Image *) NULL)
1211 {
1212 CompositeImage(layer_info->image,layer_info->mask.image,
1213 CopyAlphaCompositeOp,MagickTrue,0,0,exception);
1214 layer_info->mask.image=DestroyImage(layer_info->mask.image);
1215 }
1216 }
dirkb41f0802013-12-26 16:47:58 +00001217
1218 return(status);
1219}
1220
dirk14c08dc2015-05-25 11:52:13 +00001221ModuleExport MagickStatusType ReadPSDLayers(Image *image,
1222 const ImageInfo *image_info,const PSDInfo *psd_info,
1223 const MagickBooleanType skip_layers,ExceptionInfo *exception)
dirkb41f0802013-12-26 16:47:58 +00001224{
1225 char
1226 type[4];
cristy3ed852e2009-09-05 21:47:34 +00001227
cristy3ed852e2009-09-05 21:47:34 +00001228 LayerInfo
1229 *layer_info;
1230
cristy56ed31c2010-03-22 00:46:21 +00001231 MagickSizeType
cristy56ed31c2010-03-22 00:46:21 +00001232 size;
1233
dirkb41f0802013-12-26 16:47:58 +00001234 MagickStatusType
1235 status;
cristydede49f2010-08-20 20:26:26 +00001236
cristybb503372010-05-27 20:51:26 +00001237 register ssize_t
dirkb41f0802013-12-26 16:47:58 +00001238 i;
cristydc05fc22011-01-22 19:43:15 +00001239
cristy3ed852e2009-09-05 21:47:34 +00001240 ssize_t
cristydc05fc22011-01-22 19:43:15 +00001241 count,
1242 j,
dirkb41f0802013-12-26 16:47:58 +00001243 number_layers;
1244
1245 size=GetPSDSize(psd_info,image);
1246 if (size == 0)
1247 {
dirkb41f0802013-12-26 16:47:58 +00001248 /*
1249 Skip layers & masks.
1250 */
dirkb41f0802013-12-26 16:47:58 +00001251 (void) ReadBlobMSBLong(image);
1252 count=ReadBlob(image,4,(unsigned char *) type);
cristy908a35b2015-06-14 22:19:29 +00001253 status=MagickFalse;
dirkb41f0802013-12-26 16:47:58 +00001254 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
dirkf3081522015-06-21 09:31:18 +00001255 return(MagickTrue);
dirkb41f0802013-12-26 16:47:58 +00001256 else
1257 {
1258 count=ReadBlob(image,4,(unsigned char *) type);
1259 if ((count != 0) && (LocaleNCompare(type,"Lr16",4) == 0))
1260 size=GetPSDSize(psd_info,image);
1261 else
dirkf3081522015-06-21 09:31:18 +00001262 return(MagickTrue);
dirkb41f0802013-12-26 16:47:58 +00001263 }
1264 }
dirkb41f0802013-12-26 16:47:58 +00001265 status=MagickTrue;
1266 if (size != 0)
1267 {
1268 layer_info=(LayerInfo *) NULL;
1269 number_layers=(short) ReadBlobMSBShort(image);
1270
dirkb41f0802013-12-26 16:47:58 +00001271 if (number_layers < 0)
1272 {
1273 /*
dirk6f205312014-01-31 22:25:10 +00001274 The first alpha channel in the merged result contains the
1275 transparency data for the merged result.
dirkb41f0802013-12-26 16:47:58 +00001276 */
1277 number_layers=MagickAbsoluteValue(number_layers);
1278 if (image->debug != MagickFalse)
1279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1280 " negative layer count corrected for");
dirk6f205312014-01-31 22:25:10 +00001281 image->alpha_trait=BlendPixelTrait;
dirkb41f0802013-12-26 16:47:58 +00001282 }
1283
dirkf3081522015-06-21 09:31:18 +00001284 /*
1285 We only need to know if the image has an alpha channel
1286 */
dirkfcaec622014-01-31 23:15:51 +00001287 if (skip_layers != MagickFalse)
1288 return(MagickTrue);
1289
dirkb41f0802013-12-26 16:47:58 +00001290 if (image->debug != MagickFalse)
1291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1292 " image contains %.20g layers",(double) number_layers);
1293
1294 if (number_layers == 0)
dirk419e5ad2014-12-16 23:14:58 +00001295 ThrowBinaryException(CorruptImageError,"InvalidNumberOfLayers",
1296 image->filename);
dirkb41f0802013-12-26 16:47:58 +00001297
1298 layer_info=(LayerInfo *) AcquireQuantumMemory((size_t) number_layers,
1299 sizeof(*layer_info));
1300 if (layer_info == (LayerInfo *) NULL)
1301 {
1302 if (image->debug != MagickFalse)
1303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1304 " allocation of LayerInfo failed");
1305 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1306 image->filename);
1307 }
1308 (void) ResetMagickMemory(layer_info,0,(size_t) number_layers*
1309 sizeof(*layer_info));
1310
1311 for (i=0; i < number_layers; i++)
1312 {
1313 int
1314 x,
1315 y;
1316
1317 if (image->debug != MagickFalse)
1318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1319 " reading layer #%.20g",(double) i+1);
1320 layer_info[i].page.y=(int) ReadBlobMSBLong(image);
1321 layer_info[i].page.x=(int) ReadBlobMSBLong(image);
1322 y=(int) ReadBlobMSBLong(image);
1323 x=(int) ReadBlobMSBLong(image);
1324 layer_info[i].page.width=(ssize_t) (x-layer_info[i].page.x);
1325 layer_info[i].page.height=(ssize_t) (y-layer_info[i].page.y);
1326 layer_info[i].channels=ReadBlobMSBShort(image);
1327 if (layer_info[i].channels > MaxPSDChannels)
1328 {
dirkbd8bd852013-12-28 22:55:19 +00001329 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirkb41f0802013-12-26 16:47:58 +00001330 ThrowBinaryException(CorruptImageError,"MaximumChannelsExceeded",
dirk419e5ad2014-12-16 23:14:58 +00001331 image->filename);
dirkb41f0802013-12-26 16:47:58 +00001332 }
1333 if (image->debug != MagickFalse)
1334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1335 " offset(%.20g,%.20g), size(%.20g,%.20g), channels=%.20g",
1336 (double) layer_info[i].page.x,(double) layer_info[i].page.y,
1337 (double) layer_info[i].page.height,(double)
1338 layer_info[i].page.width,(double) layer_info[i].channels);
1339 for (j=0; j < (ssize_t) layer_info[i].channels; j++)
1340 {
1341 layer_info[i].channel_info[j].type=(short) ReadBlobMSBShort(image);
1342 layer_info[i].channel_info[j].size=(size_t) GetPSDSize(psd_info,
1343 image);
1344 if (image->debug != MagickFalse)
1345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1346 " channel[%.20g]: type=%.20g, size=%.20g",(double) j,
1347 (double) layer_info[i].channel_info[j].type,
1348 (double) layer_info[i].channel_info[j].size);
1349 }
1350 count=ReadBlob(image,4,(unsigned char *) type);
1351 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1352 {
1353 if (image->debug != MagickFalse)
1354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1355 " layer type was %.4s instead of 8BIM", type);
dirkbd8bd852013-12-28 22:55:19 +00001356 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirkb41f0802013-12-26 16:47:58 +00001357 ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
1358 image->filename);
1359 }
1360 count=ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey);
dirkb52a4b72013-12-26 16:55:20 +00001361 layer_info[i].opacity=(Quantum) ScaleCharToQuantum((unsigned char)
cristyd8083a62014-02-01 13:53:44 +00001362 ReadBlobByte(image));
dirkb41f0802013-12-26 16:47:58 +00001363 layer_info[i].clipping=(unsigned char) ReadBlobByte(image);
1364 layer_info[i].flags=(unsigned char) ReadBlobByte(image);
1365 layer_info[i].visible=!(layer_info[i].flags & 0x02);
1366 if (image->debug != MagickFalse)
1367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1368 " blend=%.4s, opacity=%.20g, clipping=%s, flags=%d, visible=%s",
1369 layer_info[i].blendkey,(double) layer_info[i].opacity,
1370 layer_info[i].clipping ? "true" : "false",layer_info[i].flags,
1371 layer_info[i].visible ? "true" : "false");
1372 (void) ReadBlobByte(image); /* filler */
1373
1374 size=ReadBlobMSBLong(image);
1375 if (size != 0)
1376 {
1377 MagickSizeType
1378 combined_length,
1379 length;
1380
1381 if (image->debug != MagickFalse)
1382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1383 " layer contains additional info");
1384 length=ReadBlobMSBLong(image);
1385 combined_length=length+4;
1386 if (length != 0)
1387 {
1388 /*
1389 Layer mask info.
1390 */
dirka41d97f2015-05-25 07:56:17 +00001391 layer_info[i].mask.page.y=(int) ReadBlobMSBLong(image);
1392 layer_info[i].mask.page.x=(int) ReadBlobMSBLong(image);
1393 layer_info[i].mask.page.height=(size_t)
1394 (ReadBlobMSBLong(image)-layer_info[i].mask.page.y);
1395 layer_info[i].mask.page.width=(size_t)
1396 (ReadBlobMSBLong(image)-layer_info[i].mask.page.x);
1397 layer_info[i].mask.background=(unsigned char) ReadBlobByte(
1398 image);
1399 layer_info[i].mask.flags=(unsigned char) ReadBlobByte(image);
1400 if (!(layer_info[i].mask.flags & 0x01))
1401 {
1402 layer_info[i].mask.page.y=layer_info[i].mask.page.y-
1403 layer_info[i].page.y;
1404 layer_info[i].mask.page.x=layer_info[i].mask.page.x-
1405 layer_info[i].page.x;
1406 }
dirkb41f0802013-12-26 16:47:58 +00001407 if (image->debug != MagickFalse)
1408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1409 " layer mask: offset(%.20g,%.20g), size(%.20g,%.20g), length=%.20g",
dirka41d97f2015-05-25 07:56:17 +00001410 (double) layer_info[i].mask.page.x,(double)
1411 layer_info[i].mask.page.y,(double) layer_info[i].mask.page.width,
1412 (double) layer_info[i].mask.page.height,(double)
1413 ((MagickOffsetType) length)-18);
dirkb41f0802013-12-26 16:47:58 +00001414 /*
1415 Skip over the rest of the layer mask information.
1416 */
dirka41d97f2015-05-25 07:56:17 +00001417 if (DiscardBlobBytes(image,(MagickSizeType) (length-18)) == MagickFalse)
dirkb41f0802013-12-26 16:47:58 +00001418 {
dirkbd8bd852013-12-28 22:55:19 +00001419 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirk419e5ad2014-12-16 23:14:58 +00001420 ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile",
1421 image->filename);
dirkb41f0802013-12-26 16:47:58 +00001422 }
1423 }
1424 length=ReadBlobMSBLong(image);
1425 combined_length+=length+4;
1426 if (length != 0)
1427 {
1428 /*
1429 Layer blending ranges info.
1430 */
1431 if (image->debug != MagickFalse)
1432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1433 " layer blending ranges: length=%.20g",(double)
1434 ((MagickOffsetType) length));
1435 /*
1436 We read it, but don't use it...
1437 */
1438 for (j=0; j < (ssize_t) (length); j+=8)
1439 {
1440 size_t blend_source=ReadBlobMSBLong(image);
1441 size_t blend_dest=ReadBlobMSBLong(image);
1442 if (image->debug != MagickFalse)
1443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1444 " source(%x), dest(%x)",(unsigned int)
1445 blend_source,(unsigned int) blend_dest);
1446 }
1447 }
1448 /*
1449 Layer name.
1450 */
1451 length=(size_t) ReadBlobByte(image);
1452 combined_length+=length+1;
dirka41d97f2015-05-25 07:56:17 +00001453 if (length > 0)
1454 (void) ReadBlob(image,(size_t) length++,layer_info[i].name);
1455 layer_info[i].name[length]='\0';
dirkb41f0802013-12-26 16:47:58 +00001456 if (image->debug != MagickFalse)
1457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1458 " layer name: %s",layer_info[i].name);
1459 /*
1460 Skip the rest of the variable data until we support it.
1461 */
1462 if (image->debug != MagickFalse)
1463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1464 " unsupported data: length=%.20g",(double)
1465 ((MagickOffsetType) (size-combined_length)));
cristy1b875d22014-05-28 14:27:23 +00001466 if (DiscardBlobBytes(image,(MagickSizeType) (size-combined_length)) == MagickFalse)
dirkb41f0802013-12-26 16:47:58 +00001467 {
dirkbd8bd852013-12-28 22:55:19 +00001468 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirkb41f0802013-12-26 16:47:58 +00001469 ThrowBinaryException(CorruptImageError,
1470 "UnexpectedEndOfFile",image->filename);
1471 }
1472 }
1473 }
1474
1475 for (i=0; i < number_layers; i++)
1476 {
dirkb41f0802013-12-26 16:47:58 +00001477 if ((layer_info[i].page.width == 0) ||
1478 (layer_info[i].page.height == 0))
1479 {
1480 if (image->debug != MagickFalse)
1481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1482 " layer data is empty");
1483 continue;
1484 }
1485
dirkb41f0802013-12-26 16:47:58 +00001486 /*
1487 Allocate layered image.
1488 */
1489 layer_info[i].image=CloneImage(image,layer_info[i].page.width,
1490 layer_info[i].page.height,MagickFalse,exception);
1491 if (layer_info[i].image == (Image *) NULL)
1492 {
dirkbd8bd852013-12-28 22:55:19 +00001493 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirkb41f0802013-12-26 16:47:58 +00001494 if (image->debug != MagickFalse)
1495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1496 " allocation of image for layer %.20g failed",(double) i);
1497 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1498 image->filename);
1499 }
1500 }
1501
dirk4d9863c2014-03-28 19:48:49 +00001502 if (image_info->ping == MagickFalse)
dirkb41f0802013-12-26 16:47:58 +00001503 {
dirk4d9863c2014-03-28 19:48:49 +00001504 for (i=0; i < number_layers; i++)
dirkb41f0802013-12-26 16:47:58 +00001505 {
dirk4d9863c2014-03-28 19:48:49 +00001506 if (layer_info[i].image == (Image *) NULL)
dirkb41f0802013-12-26 16:47:58 +00001507 {
dirk4d9863c2014-03-28 19:48:49 +00001508 for (j=0; j < layer_info[i].channels; j++)
1509 {
cristy1b875d22014-05-28 14:27:23 +00001510 if (DiscardBlobBytes(image,(MagickSizeType)
dirk4d9863c2014-03-28 19:48:49 +00001511 layer_info[i].channel_info[j].size) == MagickFalse)
1512 {
1513 layer_info=DestroyLayerInfo(layer_info,number_layers);
1514 ThrowBinaryException(CorruptImageError,
1515 "UnexpectedEndOfFile",image->filename);
1516 }
1517 }
1518 continue;
dirkb41f0802013-12-26 16:47:58 +00001519 }
dirk4d9863c2014-03-28 19:48:49 +00001520
1521 if (image->debug != MagickFalse)
1522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1523 " reading data for layer %.20g",(double) i);
1524
1525 status=ReadPSDLayer(image,psd_info,&layer_info[i],exception);
1526 if (status == MagickFalse)
1527 break;
1528
1529 status=SetImageProgress(image,LoadImagesTag,i,(MagickSizeType)
1530 number_layers);
1531 if (status == MagickFalse)
1532 break;
dirkb41f0802013-12-26 16:47:58 +00001533 }
dirkb41f0802013-12-26 16:47:58 +00001534 }
1535
dirkb41f0802013-12-26 16:47:58 +00001536 if (status != MagickFalse)
1537 {
1538 for (i=0; i < number_layers; i++)
1539 {
1540 if (layer_info[i].image == (Image *) NULL)
1541 {
1542 for (j=i; j < number_layers - 1; j++)
1543 layer_info[j] = layer_info[j+1];
1544 number_layers--;
1545 i--;
1546 }
1547 }
1548
1549 if (number_layers > 0)
1550 {
1551 for (i=0; i < number_layers; i++)
1552 {
1553 if (i > 0)
1554 layer_info[i].image->previous=layer_info[i-1].image;
1555 if (i < (number_layers-1))
1556 layer_info[i].image->next=layer_info[i+1].image;
1557 layer_info[i].image->page=layer_info[i].page;
1558 }
1559 image->next=layer_info[0].image;
1560 layer_info[0].image->previous=image;
1561 }
1562 }
1563 layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
1564 }
1565
1566 return(status);
1567}
1568
dirk4d9863c2014-03-28 19:48:49 +00001569static MagickStatusType ReadPSDMergedImage(Image* image,
1570 const PSDInfo* psd_info,ExceptionInfo *exception)
dirkb41f0802013-12-26 16:47:58 +00001571{
1572 MagickOffsetType
1573 *offsets;
1574
1575 MagickStatusType
1576 status;
1577
1578 PSDCompressionType
1579 compression;
1580
1581 register ssize_t
1582 i;
1583
1584 compression=(PSDCompressionType) ReadBlobMSBShort(image);
1585 image->compression=ConvertPSDCompression(compression);
1586
1587 if (compression != Raw && compression != RLE)
1588 {
1589 (void) ThrowMagickException(exception,GetMagickModule(),
1590 TypeWarning,"CompressionNotSupported","'%.20g'",(double) compression);
1591 return(MagickFalse);
1592 }
1593
1594 offsets=(MagickOffsetType *) NULL;
1595 if (compression == RLE)
1596 {
1597 offsets=ReadPSDRLEOffsets(image,psd_info,image->rows*psd_info->channels);
1598 if (offsets == (MagickOffsetType *) NULL)
1599 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1600 image->filename);
1601 }
1602
1603 status=MagickTrue;
1604 for (i=0; i < (ssize_t) psd_info->channels; i++)
1605 {
1606 if (compression == RLE)
1607 status=ReadPSDChannelRLE(image,psd_info,i,offsets+(i*image->rows),
1608 exception);
1609 else
1610 status=ReadPSDChannelRaw(image,psd_info->channels,i,exception);
1611
1612 if (status == MagickFalse)
1613 break;
1614 status=SetImageProgress(image,LoadImagesTag,i,psd_info->channels);
1615 if (status == MagickFalse)
1616 break;
1617 }
1618
1619 if (image->colorspace == CMYKColorspace)
dirkd9795a12014-11-11 22:01:21 +00001620 (void) NegateCMYK(image,exception);
dirkb41f0802013-12-26 16:47:58 +00001621
1622 if (offsets != (MagickOffsetType *) NULL)
1623 offsets=(MagickOffsetType *) RelinquishMagickMemory(offsets);
1624
1625 return(status);
1626}
1627
cristy90de83d2015-04-08 22:04:41 +00001628static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
dirkb41f0802013-12-26 16:47:58 +00001629{
1630 Image
1631 *image;
1632
1633 MagickBooleanType
dirk18c0e4d2014-02-22 22:24:05 +00001634 has_merged_image,
dirka41d97f2015-05-25 07:56:17 +00001635 skip_layers;
dirkb41f0802013-12-26 16:47:58 +00001636
1637 MagickOffsetType
1638 offset;
1639
1640 MagickSizeType
1641 length;
1642
dirka41d97f2015-05-25 07:56:17 +00001643 MagickStatusType
1644 status;
1645
dirkb41f0802013-12-26 16:47:58 +00001646 PSDInfo
1647 psd_info;
1648
1649 register ssize_t
1650 i;
1651
1652 ssize_t
1653 count;
cristy3ed852e2009-09-05 21:47:34 +00001654
cristy3ed852e2009-09-05 21:47:34 +00001655 unsigned char
1656 *data;
1657
cristy3ed852e2009-09-05 21:47:34 +00001658 /*
1659 Open image file.
1660 */
1661 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001662 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001663 if (image_info->debug != MagickFalse)
1664 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1665 image_info->filename);
1666 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001667 assert(exception->signature == MagickCoreSignature);
dirkb41f0802013-12-26 16:47:58 +00001668
cristy9950d572011-10-01 18:22:35 +00001669 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001670 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1671 if (status == MagickFalse)
1672 {
1673 image=DestroyImageList(image);
1674 return((Image *) NULL);
1675 }
1676 /*
1677 Read image header.
1678 */
1679 count=ReadBlob(image,4,(unsigned char *) psd_info.signature);
1680 psd_info.version=ReadBlobMSBShort(image);
cristy50aea4a2010-03-09 17:37:44 +00001681 if ((count == 0) || (LocaleNCompare(psd_info.signature,"8BPS",4) != 0) ||
cristy2d3d87f2010-03-01 00:23:08 +00001682 ((psd_info.version != 1) && (psd_info.version != 2)))
cristy3ed852e2009-09-05 21:47:34 +00001683 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
dirkf3081522015-06-21 09:31:18 +00001684 (void) ReadBlob(image,6,psd_info.reserved);
cristy3ed852e2009-09-05 21:47:34 +00001685 psd_info.channels=ReadBlobMSBShort(image);
1686 if (psd_info.channels > MaxPSDChannels)
1687 ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded");
1688 psd_info.rows=ReadBlobMSBLong(image);
1689 psd_info.columns=ReadBlobMSBLong(image);
cristy2d3d87f2010-03-01 00:23:08 +00001690 if ((psd_info.version == 1) && ((psd_info.rows > 30000) ||
1691 (psd_info.columns > 30000)))
1692 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy3ed852e2009-09-05 21:47:34 +00001693 psd_info.depth=ReadBlobMSBShort(image);
cristy2d3d87f2010-03-01 00:23:08 +00001694 if ((psd_info.depth != 1) && (psd_info.depth != 8) && (psd_info.depth != 16))
1695 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy3ed852e2009-09-05 21:47:34 +00001696 psd_info.mode=ReadBlobMSBShort(image);
1697 if (image->debug != MagickFalse)
1698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001699 " Image is %.20g x %.20g with channels=%.20g, depth=%.20g, mode=%s",
1700 (double) psd_info.columns,(double) psd_info.rows,(double)
1701 psd_info.channels,(double) psd_info.depth,ModeToString((PSDImageType)
1702 psd_info.mode));
cristy3ed852e2009-09-05 21:47:34 +00001703 /*
1704 Initialize image.
1705 */
1706 image->depth=psd_info.depth;
1707 image->columns=psd_info.columns;
1708 image->rows=psd_info.rows;
cristyacabb842014-12-14 23:36:33 +00001709 status=SetImageExtent(image,image->columns,image->rows,exception);
1710 if (status == MagickFalse)
1711 return(DestroyImageList(image));
cristyea1a8aa2011-10-20 13:24:06 +00001712 if (SetImageBackgroundColor(image,exception) == MagickFalse)
cristy95524f92010-02-16 18:44:34 +00001713 {
cristy95524f92010-02-16 18:44:34 +00001714 image=DestroyImageList(image);
1715 return((Image *) NULL);
1716 }
cristy3ed852e2009-09-05 21:47:34 +00001717 if (psd_info.mode == LabMode)
cristye2c4f182012-05-12 14:11:53 +00001718 SetImageColorspace(image,LabColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001719 if (psd_info.mode == CMYKMode)
dirk84affaa2014-05-27 11:09:16 +00001720 {
1721 SetImageColorspace(image,CMYKColorspace,exception);
dirkdeca4042014-05-29 06:31:05 +00001722 image->alpha_trait=psd_info.channels > 4 ? BlendPixelTrait :
dirk84affaa2014-05-27 11:09:16 +00001723 UndefinedPixelTrait;
1724 }
1725 else if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) ||
cristy3ed852e2009-09-05 21:47:34 +00001726 (psd_info.mode == DuotoneMode))
1727 {
cristyafd0fd12013-09-17 12:41:18 +00001728 status=AcquireImageColormap(image,psd_info.depth != 16 ? 256 : 65536,
1729 exception);
1730 if (status == MagickFalse)
cristy52cf7f12010-02-07 18:07:56 +00001731 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00001732 if (image->debug != MagickFalse)
1733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristydede49f2010-08-20 20:26:26 +00001734 " Image colormap allocated");
cristye2c4f182012-05-12 14:11:53 +00001735 SetImageColorspace(image,GRAYColorspace,exception);
dirkdeca4042014-05-29 06:31:05 +00001736 image->alpha_trait=psd_info.channels > 1 ? BlendPixelTrait :
dirk84affaa2014-05-27 11:09:16 +00001737 UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00001738 }
dirk84affaa2014-05-27 11:09:16 +00001739 else
dirkdeca4042014-05-29 06:31:05 +00001740 image->alpha_trait=psd_info.channels > 3 ? BlendPixelTrait :
dirk84affaa2014-05-27 11:09:16 +00001741 UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00001742 /*
1743 Read PSD raster colormap only present for indexed and duotone images.
1744 */
1745 length=ReadBlobMSBLong(image);
1746 if (length != 0)
1747 {
1748 if (image->debug != MagickFalse)
1749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1750 " reading colormap");
1751 if (psd_info.mode == DuotoneMode)
1752 {
1753 /*
1754 Duotone image data; the format of this data is undocumented.
1755 */
cristy56ed31c2010-03-22 00:46:21 +00001756 data=(unsigned char *) AcquireQuantumMemory((size_t) length,
1757 sizeof(*data));
cristy3ed852e2009-09-05 21:47:34 +00001758 if (data == (unsigned char *) NULL)
1759 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
dirkf3081522015-06-21 09:31:18 +00001760 (void) ReadBlob(image,(size_t) length,data);
cristy3ed852e2009-09-05 21:47:34 +00001761 data=(unsigned char *) RelinquishMagickMemory(data);
1762 }
1763 else
1764 {
cristy18a9b972014-12-02 23:47:21 +00001765 size_t
1766 number_colors;
1767
cristy3ed852e2009-09-05 21:47:34 +00001768 /*
1769 Read PSD raster colormap.
1770 */
cristy18a9b972014-12-02 23:47:21 +00001771 number_colors=length/3;
1772 if (number_colors > 65536)
1773 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1774 if (AcquireImageColormap(image,number_colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001775 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00001776 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001777 image->colormap[i].red=ScaleCharToQuantum((unsigned char)
1778 ReadBlobByte(image));
cristybb503372010-05-27 20:51:26 +00001779 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001780 image->colormap[i].green=ScaleCharToQuantum((unsigned char)
1781 ReadBlobByte(image));
cristybb503372010-05-27 20:51:26 +00001782 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001783 image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
1784 ReadBlobByte(image));
cristy8a46d822012-08-28 23:32:39 +00001785 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00001786 }
1787 }
dirk18c0e4d2014-02-22 22:24:05 +00001788 has_merged_image=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001789 length=ReadBlobMSBLong(image);
1790 if (length != 0)
1791 {
1792 unsigned char
1793 *blocks;
1794
1795 /*
1796 Image resources block.
1797 */
1798 if (image->debug != MagickFalse)
1799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy2b9582a2011-07-04 17:38:56 +00001800 " reading image resource blocks - %.20g bytes",(double)
1801 ((MagickOffsetType) length));
dirk7ec89322014-12-16 22:50:15 +00001802 blocks=(unsigned char *) AcquireQuantumMemory((size_t) length,
cristy56ed31c2010-03-22 00:46:21 +00001803 sizeof(*blocks));
cristy3ed852e2009-09-05 21:47:34 +00001804 if (blocks == (unsigned char *) NULL)
1805 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristy56ed31c2010-03-22 00:46:21 +00001806 count=ReadBlob(image,(size_t) length,blocks);
1807 if ((count != (ssize_t) length) ||
cristy3ed852e2009-09-05 21:47:34 +00001808 (LocaleNCompare((char *) blocks,"8BIM",4) != 0))
1809 {
1810 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
1811 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1812 }
dirk7ec89322014-12-16 22:50:15 +00001813 ParseImageResourceBlocks(image,blocks,(size_t) length,&has_merged_image,
1814 exception);
cristy3ed852e2009-09-05 21:47:34 +00001815 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
1816 }
1817 /*
cristy3ed852e2009-09-05 21:47:34 +00001818 Layer and mask block.
1819 */
cristy2d3d87f2010-03-01 00:23:08 +00001820 length=GetPSDSize(&psd_info,image);
cristy3ed852e2009-09-05 21:47:34 +00001821 if (length == 8)
1822 {
1823 length=ReadBlobMSBLong(image);
1824 length=ReadBlobMSBLong(image);
1825 }
dirkb41f0802013-12-26 16:47:58 +00001826 offset=TellBlob(image);
1827 skip_layers=MagickFalse;
dirk18c0e4d2014-02-22 22:24:05 +00001828 if ((image_info->number_scenes == 1) && (image_info->scene == 0) &&
1829 (has_merged_image != MagickFalse))
cristyd4297022010-09-16 22:59:09 +00001830 {
cristy374b8ad2012-02-01 20:55:21 +00001831 if (image->debug != MagickFalse)
1832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1833 " read composite only");
dirkb41f0802013-12-26 16:47:58 +00001834 skip_layers=MagickTrue;
cristyd4297022010-09-16 22:59:09 +00001835 }
cristy3ed852e2009-09-05 21:47:34 +00001836 if (length == 0)
1837 {
1838 if (image->debug != MagickFalse)
1839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1840 " image has no layers");
1841 }
1842 else
1843 {
dirk4d9863c2014-03-28 19:48:49 +00001844 if (ReadPSDLayers(image,image_info,&psd_info,skip_layers,exception) !=
1845 MagickTrue)
dirk6f205312014-01-31 22:25:10 +00001846 {
1847 (void) CloseBlob(image);
dirk419e5ad2014-12-16 23:14:58 +00001848 image=DestroyImageList(image);
dirk6f205312014-01-31 22:25:10 +00001849 return((Image *) NULL);
1850 }
cristy3ed852e2009-09-05 21:47:34 +00001851
dirkb41f0802013-12-26 16:47:58 +00001852 /*
1853 Skip the rest of the layer and mask information.
1854 */
1855 SeekBlob(image,offset+length,SEEK_SET);
cristy3ed852e2009-09-05 21:47:34 +00001856 }
dirk4d9863c2014-03-28 19:48:49 +00001857 /*
cristy89e22602015-04-08 23:34:13 +00001858 If we are only "pinging" the image, then we're done - so return.
1859 */
1860 if (image_info->ping != MagickFalse)
1861 {
1862 (void) CloseBlob(image);
1863 return(GetFirstImageInList(image));
1864 }
1865 /*
dirkb41f0802013-12-26 16:47:58 +00001866 Read the precombined layer, present for PSD < 4 compatibility.
cristy3ed852e2009-09-05 21:47:34 +00001867 */
1868 if (image->debug != MagickFalse)
1869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1870 " reading the precombined layer");
cristy90de83d2015-04-08 22:04:41 +00001871 if ((has_merged_image != MagickFalse) || (GetImageListLength(image) == 1))
dirk2de94f52014-03-15 21:36:56 +00001872 has_merged_image=(MagickBooleanType) ReadPSDMergedImage(image,&psd_info,
1873 exception);
cristy90de83d2015-04-08 22:04:41 +00001874 if ((has_merged_image == MagickFalse) && (GetImageListLength(image) == 1) &&
1875 (length != 0))
dirk2de94f52014-03-15 21:36:56 +00001876 {
cristyb3f97ae2015-05-18 12:29:32 +00001877 MagickStatusType
1878 status;
1879
dirk2de94f52014-03-15 21:36:56 +00001880 SeekBlob(image,offset,SEEK_SET);
cristy90de83d2015-04-08 22:04:41 +00001881 status=ReadPSDLayers(image,image_info,&psd_info,MagickFalse,exception);
1882 if (status != MagickTrue)
dirk2de94f52014-03-15 21:36:56 +00001883 {
1884 (void) CloseBlob(image);
1885 return((Image *) NULL);
1886 }
1887 }
cristy90de83d2015-04-08 22:04:41 +00001888 if ((has_merged_image == MagickFalse) && (GetImageListLength(image) > 1))
dirk18c0e4d2014-02-22 22:24:05 +00001889 {
1890 Image
1891 *merged;
1892
1893 SetImageAlphaChannel(image,TransparentAlphaChannel,exception);
1894 image->background_color.alpha=TransparentAlpha;
1895 merged=MergeImageLayers(image,FlattenLayer,exception);
1896 ReplaceImageInList(&image,merged);
1897 }
cristy3ed852e2009-09-05 21:47:34 +00001898 (void) CloseBlob(image);
1899 return(GetFirstImageInList(image));
1900}
1901
1902/*
1903%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1904% %
1905% %
1906% %
1907% R e g i s t e r P S D I m a g e %
1908% %
1909% %
1910% %
1911%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1912%
1913% RegisterPSDImage() adds properties for the PSD image format to
1914% the list of supported formats. The properties include the image format
1915% tag, a method to read and/or write the format, whether the format
1916% supports the saving of more than one frame to the same file or blob,
1917% whether the format supports native in-memory I/O, and a brief
1918% description of the format.
1919%
1920% The format of the RegisterPSDImage method is:
1921%
cristybb503372010-05-27 20:51:26 +00001922% size_t RegisterPSDImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001923%
1924*/
cristybb503372010-05-27 20:51:26 +00001925ModuleExport size_t RegisterPSDImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001926{
1927 MagickInfo
1928 *entry;
1929
dirk06b627a2015-04-06 18:59:17 +00001930 entry=AcquireMagickInfo("PSD","PSB","Adobe Large Document Format");
cristyb4233012010-02-28 20:09:14 +00001931 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
1932 entry->encoder=(EncodeImageHandler *) WritePSDImage;
1933 entry->magick=(IsImageFormatHandler *) IsPSD;
dirk08e9a112015-02-22 01:51:41 +00001934 entry->flags|=CoderSeekableStreamFlag;
cristyb4233012010-02-28 20:09:14 +00001935 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001936 entry=AcquireMagickInfo("PSD","PSD","Adobe Photoshop bitmap");
cristy3ed852e2009-09-05 21:47:34 +00001937 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
1938 entry->encoder=(EncodeImageHandler *) WritePSDImage;
1939 entry->magick=(IsImageFormatHandler *) IsPSD;
dirk08e9a112015-02-22 01:51:41 +00001940 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +00001941 (void) RegisterMagickInfo(entry);
1942 return(MagickImageCoderSignature);
1943}
1944
1945/*
1946%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1947% %
1948% %
1949% %
1950% U n r e g i s t e r P S D I m a g e %
1951% %
1952% %
1953% %
1954%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1955%
1956% UnregisterPSDImage() removes format registrations made by the
1957% PSD module from the list of supported formats.
1958%
1959% The format of the UnregisterPSDImage method is:
1960%
1961% UnregisterPSDImage(void)
1962%
1963*/
1964ModuleExport void UnregisterPSDImage(void)
1965{
cristyb4233012010-02-28 20:09:14 +00001966 (void) UnregisterMagickInfo("PSB");
cristy3ed852e2009-09-05 21:47:34 +00001967 (void) UnregisterMagickInfo("PSD");
1968}
1969
1970/*
1971%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1972% %
1973% %
1974% %
1975% W r i t e P S D I m a g e %
1976% %
1977% %
1978% %
1979%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1980%
cristyb1459bc2010-03-23 21:41:43 +00001981% WritePSDImage() writes an image in the Adobe Photoshop encoded image format.
cristy3ed852e2009-09-05 21:47:34 +00001982%
1983% The format of the WritePSDImage method is:
1984%
dirk1ab941f2014-02-01 17:35:11 +00001985% MagickBooleanType WritePSDImage(const ImageInfo *image_info,Image *image,
1986% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001987%
1988% A description of each parameter follows.
1989%
1990% o image_info: the image info.
1991%
1992% o image: The image.
1993%
cristy3a37efd2011-08-28 20:31:03 +00001994% o exception: return any errors or warnings in this structure.
1995%
cristy3ed852e2009-09-05 21:47:34 +00001996*/
1997
cristy50aea4a2010-03-09 17:37:44 +00001998static inline ssize_t SetPSDOffset(const PSDInfo *psd_info,Image *image,
cristyf0460ee2010-03-06 02:55:11 +00001999 const size_t offset)
2000{
2001 if (psd_info->version == 1)
cristy56ed31c2010-03-22 00:46:21 +00002002 return(WriteBlobMSBShort(image,(unsigned short) offset));
2003 return(WriteBlobMSBLong(image,(unsigned short) offset));
cristyf0460ee2010-03-06 02:55:11 +00002004}
2005
cristy50aea4a2010-03-09 17:37:44 +00002006static inline ssize_t SetPSDSize(const PSDInfo *psd_info,Image *image,
cristyf0460ee2010-03-06 02:55:11 +00002007 const MagickSizeType size)
2008{
2009 if (psd_info->version == 1)
cristy56ed31c2010-03-22 00:46:21 +00002010 return(WriteBlobMSBLong(image,(unsigned int) size));
cristyf0460ee2010-03-06 02:55:11 +00002011 return(WriteBlobMSBLongLong(image,size));
2012}
2013
cristy4aff1572010-02-15 13:34:01 +00002014static size_t PSDPackbitsEncodeImage(Image *image,const size_t length,
cristy018f07f2011-09-04 21:15:19 +00002015 const unsigned char *pixels,unsigned char *compact_pixels,
2016 ExceptionInfo *exception)
cristy4aff1572010-02-15 13:34:01 +00002017{
2018 int
2019 count;
2020
cristybb503372010-05-27 20:51:26 +00002021 register ssize_t
cristy4aff1572010-02-15 13:34:01 +00002022 i,
2023 j;
2024
2025 register unsigned char
2026 *q;
2027
2028 unsigned char
2029 *packbits;
2030
2031 /*
2032 Compress pixels with Packbits encoding.
2033 */
2034 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002035 assert(image->signature == MagickCoreSignature);
cristy4aff1572010-02-15 13:34:01 +00002036 if (image->debug != MagickFalse)
2037 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2038 assert(pixels != (unsigned char *) NULL);
2039 packbits=(unsigned char *) AcquireQuantumMemory(128UL,sizeof(*packbits));
2040 if (packbits == (unsigned char *) NULL)
2041 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2042 image->filename);
cristyb1459bc2010-03-23 21:41:43 +00002043 q=compact_pixels;
cristybb503372010-05-27 20:51:26 +00002044 for (i=(ssize_t) length; i != 0; )
cristy4aff1572010-02-15 13:34:01 +00002045 {
2046 switch (i)
2047 {
2048 case 1:
2049 {
2050 i--;
2051 *q++=(unsigned char) 0;
2052 *q++=(*pixels);
2053 break;
2054 }
2055 case 2:
2056 {
2057 i-=2;
2058 *q++=(unsigned char) 1;
2059 *q++=(*pixels);
2060 *q++=pixels[1];
2061 break;
2062 }
2063 case 3:
2064 {
2065 i-=3;
2066 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2067 {
2068 *q++=(unsigned char) ((256-3)+1);
2069 *q++=(*pixels);
2070 break;
2071 }
2072 *q++=(unsigned char) 2;
2073 *q++=(*pixels);
2074 *q++=pixels[1];
2075 *q++=pixels[2];
2076 break;
2077 }
2078 default:
2079 {
2080 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2081 {
2082 /*
2083 Packed run.
2084 */
2085 count=3;
cristybb503372010-05-27 20:51:26 +00002086 while (((ssize_t) count < i) && (*pixels == *(pixels+count)))
cristy4aff1572010-02-15 13:34:01 +00002087 {
2088 count++;
2089 if (count >= 127)
2090 break;
2091 }
2092 i-=count;
2093 *q++=(unsigned char) ((256-count)+1);
2094 *q++=(*pixels);
2095 pixels+=count;
2096 break;
2097 }
2098 /*
2099 Literal run.
2100 */
2101 count=0;
2102 while ((*(pixels+count) != *(pixels+count+1)) ||
2103 (*(pixels+count+1) != *(pixels+count+2)))
2104 {
2105 packbits[count+1]=pixels[count];
2106 count++;
cristybb503372010-05-27 20:51:26 +00002107 if (((ssize_t) count >= (i-3)) || (count >= 127))
cristy4aff1572010-02-15 13:34:01 +00002108 break;
2109 }
2110 i-=count;
2111 *packbits=(unsigned char) (count-1);
cristybb503372010-05-27 20:51:26 +00002112 for (j=0; j <= (ssize_t) count; j++)
cristy4aff1572010-02-15 13:34:01 +00002113 *q++=packbits[j];
2114 pixels+=count;
2115 break;
2116 }
2117 }
2118 }
2119 *q++=(unsigned char) 128; /* EOD marker */
2120 packbits=(unsigned char *) RelinquishMagickMemory(packbits);
cristyb1459bc2010-03-23 21:41:43 +00002121 return((size_t) (q-compact_pixels));
cristy4aff1572010-02-15 13:34:01 +00002122}
2123
cristy875e28a2010-03-06 19:46:55 +00002124static void WritePackbitsLength(const PSDInfo *psd_info,
cristya20214e2010-08-28 16:52:13 +00002125 const ImageInfo *image_info,Image *image,Image *next_image,
cristy018f07f2011-09-04 21:15:19 +00002126 unsigned char *compact_pixels,const QuantumType quantum_type,
2127 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002128{
cristy3ed852e2009-09-05 21:47:34 +00002129 QuantumInfo
2130 *quantum_info;
2131
cristy4c08aed2011-07-01 19:47:50 +00002132 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002133 *p;
2134
2135 size_t
cristy4aff1572010-02-15 13:34:01 +00002136 length,
cristy3ed852e2009-09-05 21:47:34 +00002137 packet_size;
2138
cristy75f85ae2010-09-25 03:01:06 +00002139 ssize_t
2140 y;
2141
cristya20214e2010-08-28 16:52:13 +00002142 unsigned char
2143 *pixels;
2144
2145 if (next_image->depth > 8)
2146 next_image->depth=16;
2147 packet_size=next_image->depth > 8UL ? 2UL : 1UL;
cristyda16f162011-02-19 23:52:17 +00002148 (void) packet_size;
cristy5f766ef2014-12-14 21:12:47 +00002149 quantum_info=AcquireQuantumInfo(image_info,image);
cristyb3f97ae2015-05-18 12:29:32 +00002150 pixels=(unsigned char *) GetQuantumPixels(quantum_info);
cristya20214e2010-08-28 16:52:13 +00002151 for (y=0; y < (ssize_t) next_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002152 {
cristyc82a27b2011-10-21 01:07:16 +00002153 p=GetVirtualPixels(next_image,0,y,next_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002154 if (p == (const Quantum *) NULL)
cristy4aff1572010-02-15 13:34:01 +00002155 break;
cristya20214e2010-08-28 16:52:13 +00002156 length=ExportQuantumPixels(next_image,(CacheView *) NULL,quantum_info,
cristyc82a27b2011-10-21 01:07:16 +00002157 quantum_type,pixels,exception);
cristy018f07f2011-09-04 21:15:19 +00002158 length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels,
2159 exception);
cristy875e28a2010-03-06 19:46:55 +00002160 (void) SetPSDOffset(psd_info,image,length);
cristy4aff1572010-02-15 13:34:01 +00002161 }
2162 quantum_info=DestroyQuantumInfo(quantum_info);
2163}
2164
cristy875e28a2010-03-06 19:46:55 +00002165static void WriteOneChannel(const PSDInfo *psd_info,const ImageInfo *image_info,
cristya20214e2010-08-28 16:52:13 +00002166 Image *image,Image *next_image,unsigned char *compact_pixels,
cristy018f07f2011-09-04 21:15:19 +00002167 const QuantumType quantum_type,const MagickBooleanType compression_flag,
2168 ExceptionInfo *exception)
cristy4aff1572010-02-15 13:34:01 +00002169{
2170 int
2171 y;
2172
cristy0910f242010-04-01 18:55:09 +00002173 MagickBooleanType
2174 monochrome;
2175
cristy4aff1572010-02-15 13:34:01 +00002176 QuantumInfo
2177 *quantum_info;
2178
cristy4c08aed2011-07-01 19:47:50 +00002179 register const Quantum
cristy4aff1572010-02-15 13:34:01 +00002180 *p;
2181
cristybb503372010-05-27 20:51:26 +00002182 register ssize_t
cristy0910f242010-04-01 18:55:09 +00002183 i;
2184
cristy4aff1572010-02-15 13:34:01 +00002185 size_t
2186 length,
2187 packet_size;
2188
cristya20214e2010-08-28 16:52:13 +00002189 unsigned char
2190 *pixels;
2191
cristy50aea4a2010-03-09 17:37:44 +00002192 (void) psd_info;
cristy4aff1572010-02-15 13:34:01 +00002193 if ((compression_flag != MagickFalse) &&
cristya20214e2010-08-28 16:52:13 +00002194 (next_image->compression != RLECompression))
cristy4aff1572010-02-15 13:34:01 +00002195 (void) WriteBlobMSBShort(image,0);
cristya20214e2010-08-28 16:52:13 +00002196 if (next_image->depth > 8)
2197 next_image->depth=16;
dirka7014c82015-04-06 08:43:28 +00002198 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
cristyc82a27b2011-10-21 01:07:16 +00002199 MagickTrue : MagickFalse;
cristya20214e2010-08-28 16:52:13 +00002200 packet_size=next_image->depth > 8UL ? 2UL : 1UL;
cristyda16f162011-02-19 23:52:17 +00002201 (void) packet_size;
cristy5f766ef2014-12-14 21:12:47 +00002202 quantum_info=AcquireQuantumInfo(image_info,image);
cristyb3f97ae2015-05-18 12:29:32 +00002203 pixels=(unsigned char *) GetQuantumPixels(quantum_info);
cristya20214e2010-08-28 16:52:13 +00002204 for (y=0; y < (ssize_t) next_image->rows; y++)
cristy4aff1572010-02-15 13:34:01 +00002205 {
cristyc82a27b2011-10-21 01:07:16 +00002206 p=GetVirtualPixels(next_image,0,y,next_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002207 if (p == (const Quantum *) NULL)
cristy4aff1572010-02-15 13:34:01 +00002208 break;
cristya20214e2010-08-28 16:52:13 +00002209 length=ExportQuantumPixels(next_image,(CacheView *) NULL,quantum_info,
cristyc82a27b2011-10-21 01:07:16 +00002210 quantum_type,pixels,exception);
cristy0910f242010-04-01 18:55:09 +00002211 if (monochrome != MagickFalse)
cristybb503372010-05-27 20:51:26 +00002212 for (i=0; i < (ssize_t) length; i++)
cristy0910f242010-04-01 18:55:09 +00002213 pixels[i]=(~pixels[i]);
cristya20214e2010-08-28 16:52:13 +00002214 if (next_image->compression != RLECompression)
cristy4aff1572010-02-15 13:34:01 +00002215 (void) WriteBlob(image,length,pixels);
2216 else
2217 {
cristy018f07f2011-09-04 21:15:19 +00002218 length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels,
2219 exception);
cristyb1459bc2010-03-23 21:41:43 +00002220 (void) WriteBlob(image,length,compact_pixels);
cristy4aff1572010-02-15 13:34:01 +00002221 }
cristy3ed852e2009-09-05 21:47:34 +00002222 }
2223 quantum_info=DestroyQuantumInfo(quantum_info);
2224}
2225
cristy875e28a2010-03-06 19:46:55 +00002226static MagickBooleanType WriteImageChannels(const PSDInfo *psd_info,
cristya20214e2010-08-28 16:52:13 +00002227 const ImageInfo *image_info,Image *image,Image *next_image,
cristy018f07f2011-09-04 21:15:19 +00002228 const MagickBooleanType separate,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002229{
cristy3ed852e2009-09-05 21:47:34 +00002230 size_t
2231 channels,
2232 packet_size;
2233
2234 unsigned char
cristya20214e2010-08-28 16:52:13 +00002235 *compact_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002236
2237 /*
cristy875e28a2010-03-06 19:46:55 +00002238 Write uncompressed pixels as separate planes.
cristy3ed852e2009-09-05 21:47:34 +00002239 */
2240 channels=1;
cristya20214e2010-08-28 16:52:13 +00002241 packet_size=next_image->depth > 8UL ? 2UL : 1UL;
2242 compact_pixels=(unsigned char *) NULL;
2243 if (next_image->compression == RLECompression)
cristy4aff1572010-02-15 13:34:01 +00002244 {
cristya20214e2010-08-28 16:52:13 +00002245 compact_pixels=(unsigned char *) AcquireQuantumMemory(2*channels*
2246 next_image->columns,packet_size*sizeof(*compact_pixels));
2247 if (compact_pixels == (unsigned char *) NULL)
2248 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy4aff1572010-02-15 13:34:01 +00002249 }
dirka7014c82015-04-06 08:43:28 +00002250 if (IsImageGray(next_image) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002251 {
cristya20214e2010-08-28 16:52:13 +00002252 if (next_image->compression == RLECompression)
cristy4aff1572010-02-15 13:34:01 +00002253 {
2254 /*
2255 Packbits compression.
2256 */
2257 (void) WriteBlobMSBShort(image,1);
cristya20214e2010-08-28 16:52:13 +00002258 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002259 compact_pixels,GrayQuantum,exception);
cristy17f11b02014-12-20 19:37:04 +00002260 if (next_image->alpha_trait != UndefinedPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002261 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002262 compact_pixels,AlphaQuantum,exception);
cristy4aff1572010-02-15 13:34:01 +00002263 }
cristya20214e2010-08-28 16:52:13 +00002264 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002265 GrayQuantum,MagickTrue,exception);
cristy17f11b02014-12-20 19:37:04 +00002266 if (next_image->alpha_trait != UndefinedPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002267 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002268 AlphaQuantum,separate,exception);
cristy6886a752010-04-23 18:23:20 +00002269 (void) SetImageProgress(image,SaveImagesTag,0,1);
cristy3ed852e2009-09-05 21:47:34 +00002270 }
cristy0910f242010-04-01 18:55:09 +00002271 else
cristya20214e2010-08-28 16:52:13 +00002272 if (next_image->storage_class == PseudoClass)
cristy0910f242010-04-01 18:55:09 +00002273 {
cristya20214e2010-08-28 16:52:13 +00002274 if (next_image->compression == RLECompression)
cristy0910f242010-04-01 18:55:09 +00002275 {
2276 /*
2277 Packbits compression.
2278 */
2279 (void) WriteBlobMSBShort(image,1);
cristya20214e2010-08-28 16:52:13 +00002280 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002281 compact_pixels,IndexQuantum,exception);
cristy17f11b02014-12-20 19:37:04 +00002282 if (next_image->alpha_trait != UndefinedPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002283 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002284 compact_pixels,AlphaQuantum,exception);
cristy0910f242010-04-01 18:55:09 +00002285 }
cristya20214e2010-08-28 16:52:13 +00002286 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002287 IndexQuantum,MagickTrue,exception);
cristy17f11b02014-12-20 19:37:04 +00002288 if (next_image->alpha_trait != UndefinedPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002289 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002290 AlphaQuantum,separate,exception);
cristy0910f242010-04-01 18:55:09 +00002291 (void) SetImageProgress(image,SaveImagesTag,0,1);
2292 }
2293 else
2294 {
cristya20214e2010-08-28 16:52:13 +00002295 if (next_image->colorspace == CMYKColorspace)
dirkd9795a12014-11-11 22:01:21 +00002296 (void) NegateCMYK(next_image,exception);
cristya20214e2010-08-28 16:52:13 +00002297 if (next_image->compression == RLECompression)
cristy0910f242010-04-01 18:55:09 +00002298 {
2299 /*
2300 Packbits compression.
2301 */
2302 (void) WriteBlobMSBShort(image,1);
cristya20214e2010-08-28 16:52:13 +00002303 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002304 compact_pixels,RedQuantum,exception);
cristya20214e2010-08-28 16:52:13 +00002305 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002306 compact_pixels,GreenQuantum,exception);
cristya20214e2010-08-28 16:52:13 +00002307 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002308 compact_pixels,BlueQuantum,exception);
cristya20214e2010-08-28 16:52:13 +00002309 if (next_image->colorspace == CMYKColorspace)
2310 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002311 compact_pixels,BlackQuantum,exception);
cristy17f11b02014-12-20 19:37:04 +00002312 if (next_image->alpha_trait != UndefinedPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002313 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002314 compact_pixels,AlphaQuantum,exception);
cristy0910f242010-04-01 18:55:09 +00002315 }
2316 (void) SetImageProgress(image,SaveImagesTag,0,6);
cristya20214e2010-08-28 16:52:13 +00002317 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002318 RedQuantum,MagickTrue,exception);
cristy860cc732010-05-19 16:38:37 +00002319 (void) SetImageProgress(image,SaveImagesTag,1,6);
cristya20214e2010-08-28 16:52:13 +00002320 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002321 GreenQuantum,separate,exception);
cristy860cc732010-05-19 16:38:37 +00002322 (void) SetImageProgress(image,SaveImagesTag,2,6);
cristya20214e2010-08-28 16:52:13 +00002323 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002324 BlueQuantum,separate,exception);
cristy860cc732010-05-19 16:38:37 +00002325 (void) SetImageProgress(image,SaveImagesTag,3,6);
cristya20214e2010-08-28 16:52:13 +00002326 if (next_image->colorspace == CMYKColorspace)
2327 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002328 BlackQuantum,separate,exception);
cristy860cc732010-05-19 16:38:37 +00002329 (void) SetImageProgress(image,SaveImagesTag,4,6);
cristy17f11b02014-12-20 19:37:04 +00002330 if (next_image->alpha_trait != UndefinedPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002331 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
dirkc9e9ea32015-01-24 11:21:18 +00002332 AlphaQuantum,separate,exception);
cristy0910f242010-04-01 18:55:09 +00002333 (void) SetImageProgress(image,SaveImagesTag,5,6);
cristya20214e2010-08-28 16:52:13 +00002334 if (next_image->colorspace == CMYKColorspace)
dirkd9795a12014-11-11 22:01:21 +00002335 (void) NegateCMYK(next_image,exception);
cristy0910f242010-04-01 18:55:09 +00002336 }
cristya20214e2010-08-28 16:52:13 +00002337 if (next_image->compression == RLECompression)
2338 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
cristy3ed852e2009-09-05 21:47:34 +00002339 return(MagickTrue);
2340}
2341
cristy3ed852e2009-09-05 21:47:34 +00002342static void WritePascalString(Image* inImage,const char *inString,int inPad)
2343{
2344 size_t
cristya20214e2010-08-28 16:52:13 +00002345 length;
cristy3ed852e2009-09-05 21:47:34 +00002346
cristya20214e2010-08-28 16:52:13 +00002347 register ssize_t
2348 i;
cristy3ed852e2009-09-05 21:47:34 +00002349
cristya20214e2010-08-28 16:52:13 +00002350 /*
2351 Max length is 255.
2352 */
2353 length=(strlen(inString) > 255UL ) ? 255UL : strlen(inString);
2354 if (length == 0)
2355 (void) WriteBlobByte(inImage,0);
cristy3ed852e2009-09-05 21:47:34 +00002356 else
cristya20214e2010-08-28 16:52:13 +00002357 {
2358 (void) WriteBlobByte(inImage,(unsigned char) length);
2359 (void) WriteBlob(inImage, length, (const unsigned char *) inString);
2360 }
2361 length++;
2362 if ((length % inPad) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002363 return;
cristya20214e2010-08-28 16:52:13 +00002364 for (i=0; i < (ssize_t) (inPad-(length % inPad)); i++)
cristy3ed852e2009-09-05 21:47:34 +00002365 (void) WriteBlobByte(inImage,0);
2366}
2367
2368static void WriteResolutionResourceBlock(Image *image)
2369{
cristy56ed31c2010-03-22 00:46:21 +00002370 double
2371 x_resolution,
2372 y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00002373
2374 unsigned short
2375 units;
2376
cristy3ed852e2009-09-05 21:47:34 +00002377 if (image->units == PixelsPerCentimeterResolution)
2378 {
dirk87dabf62014-11-11 22:44:30 +00002379 x_resolution=2.54*65536.0*image->resolution.x+0.5;
cristy2a11bef2011-10-28 18:33:11 +00002380 y_resolution=2.54*65536.0*image->resolution.y+0.5;
cristy3ed852e2009-09-05 21:47:34 +00002381 units=2;
2382 }
dirk87dabf62014-11-11 22:44:30 +00002383 else
2384 {
2385 x_resolution=65536.0*image->resolution.x+0.5;
2386 y_resolution=65536.0*image->resolution.y+0.5;
2387 units=1;
2388 }
cristy3ed852e2009-09-05 21:47:34 +00002389 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
2390 (void) WriteBlobMSBShort(image,0x03ED);
2391 (void) WriteBlobMSBShort(image,0);
2392 (void) WriteBlobMSBLong(image,16); /* resource size */
cristy56ed31c2010-03-22 00:46:21 +00002393 (void) WriteBlobMSBLong(image,(unsigned int) (x_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002394 (void) WriteBlobMSBShort(image,units); /* horizontal resolution unit */
2395 (void) WriteBlobMSBShort(image,units); /* width unit */
cristy56ed31c2010-03-22 00:46:21 +00002396 (void) WriteBlobMSBLong(image,(unsigned int) (y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002397 (void) WriteBlobMSBShort(image,units); /* vertical resolution unit */
2398 (void) WriteBlobMSBShort(image,units); /* height unit */
2399}
2400
cristyd4d3f742010-04-25 20:36:50 +00002401static void RemoveICCProfileFromResourceBlock(StringInfo *bim_profile)
2402{
cristy0b796e62010-04-29 00:38:45 +00002403 register const unsigned char
cristyd4d3f742010-04-25 20:36:50 +00002404 *p;
2405
2406 size_t
2407 length;
2408
2409 unsigned char
2410 *datum;
2411
cristy6befb0f2010-05-31 14:33:15 +00002412 unsigned int
cristyd4d3f742010-04-25 20:36:50 +00002413 count,
cristy6befb0f2010-05-31 14:33:15 +00002414 long_sans;
cristyd4d3f742010-04-25 20:36:50 +00002415
2416 unsigned short
2417 id,
2418 short_sans;
2419
2420 length=GetStringInfoLength(bim_profile);
2421 if (length < 16)
cristy0b796e62010-04-29 00:38:45 +00002422 return;
cristyd4d3f742010-04-25 20:36:50 +00002423 datum=GetStringInfoDatum(bim_profile);
2424 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
2425 {
cristy4176bb22010-05-01 16:29:09 +00002426 register unsigned char
2427 *q;
2428
2429 q=(unsigned char *) p;
cristyd4d3f742010-04-25 20:36:50 +00002430 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
2431 break;
cristy6befb0f2010-05-31 14:33:15 +00002432 p=PushLongPixel(MSBEndian,p,&long_sans);
cristyd4d3f742010-04-25 20:36:50 +00002433 p=PushShortPixel(MSBEndian,p,&id);
2434 p=PushShortPixel(MSBEndian,p,&short_sans);
2435 p=PushLongPixel(MSBEndian,p,&count);
2436 if (id == 0x0000040f)
2437 {
cristy4176bb22010-05-01 16:29:09 +00002438 (void) CopyMagickMemory(q,q+PSDQuantum(count)+12,length-
2439 (PSDQuantum(count)+12)-(q-datum));
2440 SetStringInfoLength(bim_profile,length-(PSDQuantum(count)+12));
cristyd4d3f742010-04-25 20:36:50 +00002441 break;
2442 }
2443 p+=count;
2444 if ((count & 0x01) != 0)
2445 p++;
2446 }
2447}
2448
cristyf11065e2010-05-14 13:26:59 +00002449static void RemoveResolutionFromResourceBlock(StringInfo *bim_profile)
2450{
2451 register const unsigned char
2452 *p;
2453
2454 size_t
2455 length;
2456
2457 unsigned char
2458 *datum;
2459
cristy6befb0f2010-05-31 14:33:15 +00002460 unsigned int
cristyf11065e2010-05-14 13:26:59 +00002461 count,
cristy6befb0f2010-05-31 14:33:15 +00002462 long_sans;
cristyf11065e2010-05-14 13:26:59 +00002463
2464 unsigned short
2465 id,
2466 short_sans;
2467
2468 length=GetStringInfoLength(bim_profile);
2469 if (length < 16)
2470 return;
2471 datum=GetStringInfoDatum(bim_profile);
2472 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
2473 {
2474 register unsigned char
2475 *q;
2476
2477 q=(unsigned char *) p;
2478 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
2479 break;
cristy6befb0f2010-05-31 14:33:15 +00002480 p=PushLongPixel(MSBEndian,p,&long_sans);
cristyf11065e2010-05-14 13:26:59 +00002481 p=PushShortPixel(MSBEndian,p,&id);
2482 p=PushShortPixel(MSBEndian,p,&short_sans);
2483 p=PushLongPixel(MSBEndian,p,&count);
cristy94b11832011-09-08 19:46:03 +00002484 if ((id == 0x000003ed) && (PSDQuantum(count) < (ssize_t) (length-12)))
cristyf11065e2010-05-14 13:26:59 +00002485 {
2486 (void) CopyMagickMemory(q,q+PSDQuantum(count)+12,length-
2487 (PSDQuantum(count)+12)-(q-datum));
2488 SetStringInfoLength(bim_profile,length-(PSDQuantum(count)+12));
2489 break;
2490 }
2491 p+=count;
2492 if ((count & 0x01) != 0)
2493 p++;
2494 }
2495}
2496
cristy3a37efd2011-08-28 20:31:03 +00002497static MagickBooleanType WritePSDImage(const ImageInfo *image_info,Image *image,
2498 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002499{
2500 const char
cristya20214e2010-08-28 16:52:13 +00002501 *property;
cristy3ed852e2009-09-05 21:47:34 +00002502
2503 const StringInfo
cristy749d2152010-04-04 23:47:33 +00002504 *icc_profile;
cristy3ed852e2009-09-05 21:47:34 +00002505
cristya20214e2010-08-28 16:52:13 +00002506 Image
2507 *base_image,
2508 *next_image;
2509
cristy3ed852e2009-09-05 21:47:34 +00002510 MagickBooleanType
cristy3ed852e2009-09-05 21:47:34 +00002511 status;
2512
cristy875e28a2010-03-06 19:46:55 +00002513 PSDInfo
2514 psd_info;
2515
cristybb503372010-05-27 20:51:26 +00002516 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002517 i;
2518
2519 size_t
cristya20214e2010-08-28 16:52:13 +00002520 channel_size,
2521 channelLength,
2522 layer_count,
2523 layer_info_size,
cristy749d2152010-04-04 23:47:33 +00002524 length,
cristy3ed852e2009-09-05 21:47:34 +00002525 num_channels,
cristya20214e2010-08-28 16:52:13 +00002526 packet_size,
2527 rounded_layer_info_size;
cristy3ed852e2009-09-05 21:47:34 +00002528
cristy0b796e62010-04-29 00:38:45 +00002529 StringInfo
2530 *bim_profile;
2531
cristy3ed852e2009-09-05 21:47:34 +00002532 /*
cristy56ed31c2010-03-22 00:46:21 +00002533 Open image file.
cristy3ed852e2009-09-05 21:47:34 +00002534 */
2535 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002536 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002537 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002538 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00002539 if (image->debug != MagickFalse)
2540 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00002541 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002542 assert(exception->signature == MagickCoreSignature);
cristy3a37efd2011-08-28 20:31:03 +00002543 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002544 if (status == MagickFalse)
2545 return(status);
2546 packet_size=(size_t) (image->depth > 8 ? 6 : 3);
cristy17f11b02014-12-20 19:37:04 +00002547 if (image->alpha_trait != UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +00002548 packet_size+=image->depth > 8 ? 2 : 1;
cristy875e28a2010-03-06 19:46:55 +00002549 psd_info.version=1;
2550 if ((LocaleCompare(image_info->magick,"PSB") == 0) ||
2551 (image->columns > 30000) || (image->rows > 30000))
2552 psd_info.version=2;
cristy50aea4a2010-03-09 17:37:44 +00002553 (void) WriteBlob(image,4,(const unsigned char *) "8BPS");
cristy875e28a2010-03-06 19:46:55 +00002554 (void) WriteBlobMSBShort(image,psd_info.version); /* version */
2555 for (i=1; i <= 6; i++)
2556 (void) WriteBlobByte(image, 0); /* 6 bytes of reserved */
dirka7014c82015-04-06 08:43:28 +00002557 if (IsImageGray(image) != MagickFalse)
cristy17f11b02014-12-20 19:37:04 +00002558 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
cristy3ed852e2009-09-05 21:47:34 +00002559 else
cristy0910f242010-04-01 18:55:09 +00002560 if (image->storage_class == PseudoClass)
cristy17f11b02014-12-20 19:37:04 +00002561 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
cristy0910f242010-04-01 18:55:09 +00002562 else
2563 {
2564 if (image->colorspace != CMYKColorspace)
cristy17f11b02014-12-20 19:37:04 +00002565 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 4UL : 3UL);
cristy0910f242010-04-01 18:55:09 +00002566 else
cristy17f11b02014-12-20 19:37:04 +00002567 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 5UL : 4UL);
cristy0910f242010-04-01 18:55:09 +00002568 }
cristy3ed852e2009-09-05 21:47:34 +00002569 (void) WriteBlobMSBShort(image,(unsigned short) num_channels);
cristy56ed31c2010-03-22 00:46:21 +00002570 (void) WriteBlobMSBLong(image,(unsigned int) image->rows);
2571 (void) WriteBlobMSBLong(image,(unsigned int) image->columns);
dirka7014c82015-04-06 08:43:28 +00002572 if (IsImageGray(image) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002573 {
cristy2045da32010-04-16 00:59:35 +00002574 MagickBooleanType
2575 monochrome;
2576
cristy0910f242010-04-01 18:55:09 +00002577 /*
2578 Write depth & mode.
2579 */
dirka7014c82015-04-06 08:43:28 +00002580 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
cristy3a37efd2011-08-28 20:31:03 +00002581 MagickTrue : MagickFalse;
cristy284c7d82010-04-24 00:19:14 +00002582 (void) WriteBlobMSBShort(image,(unsigned short)
2583 (monochrome != MagickFalse ? 1 : image->depth > 8 ? 16 : 8));
cristy2b9582a2011-07-04 17:38:56 +00002584 (void) WriteBlobMSBShort(image,(unsigned short)
2585 (monochrome != MagickFalse ? BitmapMode : GrayscaleMode));
cristy3ed852e2009-09-05 21:47:34 +00002586 }
2587 else
2588 {
cristya20214e2010-08-28 16:52:13 +00002589 (void) WriteBlobMSBShort(image,(unsigned short) (image->storage_class ==
2590 PseudoClass ? 8 : image->depth > 8 ? 16 : 8));
dirk87dabf62014-11-11 22:44:30 +00002591
cristy48845392010-06-02 01:19:17 +00002592 if (((image_info->colorspace != UndefinedColorspace) ||
cristy0910f242010-04-01 18:55:09 +00002593 (image->colorspace != CMYKColorspace)) &&
cristy48845392010-06-02 01:19:17 +00002594 (image_info->colorspace != CMYKColorspace))
cristy0910f242010-04-01 18:55:09 +00002595 {
cristyaf8d3912014-02-21 14:50:33 +00002596 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy0910f242010-04-01 18:55:09 +00002597 (void) WriteBlobMSBShort(image,(unsigned short)
cristy2045da32010-04-16 00:59:35 +00002598 (image->storage_class == PseudoClass ? IndexedMode : RGBMode));
cristy0910f242010-04-01 18:55:09 +00002599 }
2600 else
2601 {
cristy48845392010-06-02 01:19:17 +00002602 if (image->colorspace != CMYKColorspace)
cristye941a752011-10-15 01:52:48 +00002603 (void) TransformImageColorspace(image,CMYKColorspace,exception);
cristy2045da32010-04-16 00:59:35 +00002604 (void) WriteBlobMSBShort(image,CMYKMode);
cristy0910f242010-04-01 18:55:09 +00002605 }
cristy3ed852e2009-09-05 21:47:34 +00002606 }
dirka7014c82015-04-06 08:43:28 +00002607 if ((IsImageGray(image) != MagickFalse) ||
cristyca9ddb02010-04-16 01:01:18 +00002608 (image->storage_class == DirectClass) || (image->colors > 256))
cristy3ed852e2009-09-05 21:47:34 +00002609 (void) WriteBlobMSBLong(image,0);
2610 else
2611 {
2612 /*
2613 Write PSD raster colormap.
2614 */
2615 (void) WriteBlobMSBLong(image,768);
cristybb503372010-05-27 20:51:26 +00002616 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002617 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].red));
2618 for ( ; i < 256; i++)
2619 (void) WriteBlobByte(image,0);
cristybb503372010-05-27 20:51:26 +00002620 for (i=0; i < (ssize_t) image->colors; i++)
cristya20214e2010-08-28 16:52:13 +00002621 (void) WriteBlobByte(image,ScaleQuantumToChar(
2622 image->colormap[i].green));
cristy3ed852e2009-09-05 21:47:34 +00002623 for ( ; i < 256; i++)
2624 (void) WriteBlobByte(image,0);
cristybb503372010-05-27 20:51:26 +00002625 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002626 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].blue));
2627 for ( ; i < 256; i++)
2628 (void) WriteBlobByte(image,0);
2629 }
2630 /*
2631 Image resource block.
2632 */
cristy749d2152010-04-04 23:47:33 +00002633 length=28; /* 0x03EB */
cristy0b796e62010-04-29 00:38:45 +00002634 bim_profile=(StringInfo *) GetImageProfile(image,"8bim");
cristy749d2152010-04-04 23:47:33 +00002635 icc_profile=GetImageProfile(image,"icc");
cristyd4d3f742010-04-25 20:36:50 +00002636 if (bim_profile != (StringInfo *) NULL)
2637 {
cristy0b796e62010-04-29 00:38:45 +00002638 bim_profile=CloneStringInfo(bim_profile);
cristyd4d3f742010-04-25 20:36:50 +00002639 if (icc_profile != (StringInfo *) NULL)
2640 RemoveICCProfileFromResourceBlock(bim_profile);
cristyf11065e2010-05-14 13:26:59 +00002641 RemoveResolutionFromResourceBlock(bim_profile);
cristyd4d3f742010-04-25 20:36:50 +00002642 length+=PSDQuantum(GetStringInfoLength(bim_profile));
2643 }
cristy0b796e62010-04-29 00:38:45 +00002644 if (icc_profile != (const StringInfo *) NULL)
cristy749d2152010-04-04 23:47:33 +00002645 length+=PSDQuantum(GetStringInfoLength(icc_profile))+12;
cristy284c7d82010-04-24 00:19:14 +00002646 (void) WriteBlobMSBLong(image,(unsigned int) length);
cristyf11065e2010-05-14 13:26:59 +00002647 WriteResolutionResourceBlock(image);
cristy4176bb22010-05-01 16:29:09 +00002648 if (bim_profile != (StringInfo *) NULL)
2649 {
2650 (void) WriteBlob(image,GetStringInfoLength(bim_profile),
2651 GetStringInfoDatum(bim_profile));
2652 bim_profile=DestroyStringInfo(bim_profile);
2653 }
cristy749d2152010-04-04 23:47:33 +00002654 if (icc_profile != (StringInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002655 {
cristy749d2152010-04-04 23:47:33 +00002656 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
cristy4176bb22010-05-01 16:29:09 +00002657 (void) WriteBlobMSBShort(image,0x0000040F);
cristy749d2152010-04-04 23:47:33 +00002658 (void) WriteBlobMSBShort(image,0);
cristy284c7d82010-04-24 00:19:14 +00002659 (void) WriteBlobMSBLong(image,(unsigned int) GetStringInfoLength(
2660 icc_profile));
cristy749d2152010-04-04 23:47:33 +00002661 (void) WriteBlob(image,GetStringInfoLength(icc_profile),
2662 GetStringInfoDatum(icc_profile));
cristye195f262010-04-16 18:12:35 +00002663 if ((MagickOffsetType) GetStringInfoLength(icc_profile) !=
cristy2045da32010-04-16 00:59:35 +00002664 PSDQuantum(GetStringInfoLength(icc_profile)))
cristy749d2152010-04-04 23:47:33 +00002665 (void) WriteBlobByte(image,0);
cristye6365592010-04-02 17:31:23 +00002666 }
cristy48845392010-06-02 01:19:17 +00002667 layer_count=0;
2668 layer_info_size=2;
cristy2837bcc2010-08-07 23:57:39 +00002669 base_image=GetNextImageInList(image);
cristy17f11b02014-12-20 19:37:04 +00002670 if ((image->alpha_trait != UndefinedPixelTrait) && (base_image == (Image *) NULL))
cristy2837bcc2010-08-07 23:57:39 +00002671 base_image=image;
cristya20214e2010-08-28 16:52:13 +00002672 next_image=base_image;
2673 while ( next_image != NULL )
2674 {
2675 packet_size=next_image->depth > 8 ? 2UL : 1UL;
dirka7014c82015-04-06 08:43:28 +00002676 if (IsImageGray(next_image) != MagickFalse)
cristy17f11b02014-12-20 19:37:04 +00002677 num_channels=next_image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL;
cristy3ed852e2009-09-05 21:47:34 +00002678 else
cristya20214e2010-08-28 16:52:13 +00002679 if (next_image->storage_class == PseudoClass)
cristy17f11b02014-12-20 19:37:04 +00002680 num_channels=next_image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL;
cristy2045da32010-04-16 00:59:35 +00002681 else
cristya20214e2010-08-28 16:52:13 +00002682 if (next_image->colorspace != CMYKColorspace)
cristy17f11b02014-12-20 19:37:04 +00002683 num_channels=next_image->alpha_trait != UndefinedPixelTrait ? 4UL : 3UL;
cristy2045da32010-04-16 00:59:35 +00002684 else
cristy17f11b02014-12-20 19:37:04 +00002685 num_channels=next_image->alpha_trait != UndefinedPixelTrait ? 5UL : 4UL;
cristya20214e2010-08-28 16:52:13 +00002686 channelLength=(size_t) (next_image->columns*next_image->rows*packet_size+2);
cristy48845392010-06-02 01:19:17 +00002687 layer_info_size+=(size_t) (4*4+2+num_channels*6+(psd_info.version == 1 ? 8 :
2688 16)+4*1+4+num_channels*channelLength);
cristyd15e6592011-10-15 00:13:06 +00002689 property=(const char *) GetImageProperty(next_image,"label",exception);
cristya20214e2010-08-28 16:52:13 +00002690 if (property == (const char *) NULL)
2691 layer_info_size+=16;
cristyde9b8f52010-03-12 01:17:00 +00002692 else
2693 {
cristya20214e2010-08-28 16:52:13 +00002694 size_t
2695 length;
2696
2697 length=strlen(property);
2698 layer_info_size+=8+length+(4-(length % 4));
cristyde9b8f52010-03-12 01:17:00 +00002699 }
cristy4aff1572010-02-15 13:34:01 +00002700 layer_count++;
cristya20214e2010-08-28 16:52:13 +00002701 next_image=GetNextImageInList(next_image);
cristy3ed852e2009-09-05 21:47:34 +00002702 }
cristy144f1b62010-05-18 00:52:09 +00002703 if (layer_count == 0)
cristy875e28a2010-03-06 19:46:55 +00002704 (void) SetPSDSize(&psd_info,image,0);
cristy3ed852e2009-09-05 21:47:34 +00002705 else
cristya20214e2010-08-28 16:52:13 +00002706 {
cristy8e9bd3e2010-08-29 00:03:56 +00002707 CompressionType
2708 compression;
2709
2710 (void) SetPSDSize(&psd_info,image,layer_info_size+
2711 (psd_info.version == 1 ? 8 : 16));
2712 if ((layer_info_size/2) != ((layer_info_size+1)/2))
2713 rounded_layer_info_size=layer_info_size+1;
cristya20214e2010-08-28 16:52:13 +00002714 else
cristy8e9bd3e2010-08-29 00:03:56 +00002715 rounded_layer_info_size=layer_info_size;
2716 (void) SetPSDSize(&psd_info,image,rounded_layer_info_size);
dirk17303172015-04-05 21:20:07 +00002717 if (image->alpha_trait != UndefinedPixelTrait)
dirk8fb7ed52014-02-11 12:07:01 +00002718 (void) WriteBlobMSBShort(image,-(unsigned short) layer_count);
2719 else
2720 (void) WriteBlobMSBShort(image,(unsigned short) layer_count);
cristy8e9bd3e2010-08-29 00:03:56 +00002721 layer_count=1;
2722 compression=base_image->compression;
cristyf2a82ee2014-05-26 17:49:54 +00002723 for (next_image=base_image; next_image != NULL; )
cristy8e9bd3e2010-08-29 00:03:56 +00002724 {
2725 next_image->compression=NoCompression;
cristya4e5f472011-11-09 00:42:46 +00002726 (void) WriteBlobMSBLong(image,(unsigned int) next_image->page.y);
2727 (void) WriteBlobMSBLong(image,(unsigned int) next_image->page.x);
cristy0e274112014-03-25 14:09:19 +00002728 (void) WriteBlobMSBLong(image,(unsigned int) (next_image->page.y+
2729 next_image->rows));
2730 (void) WriteBlobMSBLong(image,(unsigned int) (next_image->page.x+
2731 next_image->columns));
cristy8e9bd3e2010-08-29 00:03:56 +00002732 packet_size=next_image->depth > 8 ? 2UL : 1UL;
2733 channel_size=(unsigned int) ((packet_size*next_image->rows*
2734 next_image->columns)+2);
dirka7014c82015-04-06 08:43:28 +00002735 if ((IsImageGray(next_image) != MagickFalse) ||
cristy8e9bd3e2010-08-29 00:03:56 +00002736 (next_image->storage_class == PseudoClass))
2737 {
2738 (void) WriteBlobMSBShort(image,(unsigned short)
cristy17f11b02014-12-20 19:37:04 +00002739 (next_image->alpha_trait != UndefinedPixelTrait ? 2 : 1));
cristy8e9bd3e2010-08-29 00:03:56 +00002740 (void) WriteBlobMSBShort(image,0);
2741 (void) SetPSDSize(&psd_info,image,channel_size);
cristy17f11b02014-12-20 19:37:04 +00002742 if (next_image->alpha_trait != UndefinedPixelTrait)
cristy8e9bd3e2010-08-29 00:03:56 +00002743 {
2744 (void) WriteBlobMSBShort(image,(unsigned short) -1);
2745 (void) SetPSDSize(&psd_info,image,channel_size);
2746 }
2747 }
2748 else
2749 if (next_image->colorspace != CMYKColorspace)
2750 {
2751 (void) WriteBlobMSBShort(image,(unsigned short)
cristy17f11b02014-12-20 19:37:04 +00002752 (next_image->alpha_trait != UndefinedPixelTrait ? 4 : 3));
cristy8e9bd3e2010-08-29 00:03:56 +00002753 (void) WriteBlobMSBShort(image,0);
2754 (void) SetPSDSize(&psd_info,image,channel_size);
2755 (void) WriteBlobMSBShort(image,1);
2756 (void) SetPSDSize(&psd_info,image,channel_size);
2757 (void) WriteBlobMSBShort(image,2);
2758 (void) SetPSDSize(&psd_info,image,channel_size);
cristy17f11b02014-12-20 19:37:04 +00002759 if (next_image->alpha_trait != UndefinedPixelTrait)
cristy8e9bd3e2010-08-29 00:03:56 +00002760 {
2761 (void) WriteBlobMSBShort(image,(unsigned short) -1);
2762 (void) SetPSDSize(&psd_info,image,channel_size);
2763 }
2764 }
2765 else
2766 {
2767 (void) WriteBlobMSBShort(image,(unsigned short)
cristy8a46d822012-08-28 23:32:39 +00002768 (next_image->alpha_trait ? 5 : 4));
cristy8e9bd3e2010-08-29 00:03:56 +00002769 (void) WriteBlobMSBShort(image,0);
2770 (void) SetPSDSize(&psd_info,image,channel_size);
2771 (void) WriteBlobMSBShort(image,1);
2772 (void) SetPSDSize(&psd_info,image,channel_size);
2773 (void) WriteBlobMSBShort(image,2);
2774 (void) SetPSDSize(&psd_info,image,channel_size);
2775 (void) WriteBlobMSBShort(image,3);
2776 (void) SetPSDSize(&psd_info,image,channel_size);
cristy8a46d822012-08-28 23:32:39 +00002777 if (next_image->alpha_trait)
cristy8e9bd3e2010-08-29 00:03:56 +00002778 {
2779 (void) WriteBlobMSBShort(image,(unsigned short) -1);
2780 (void) SetPSDSize(&psd_info,image,channel_size);
2781 }
2782 }
2783 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
2784 (void) WriteBlob(image,4,(const unsigned char *)
2785 CompositeOperatorToPSDBlendMode(next_image->compose));
2786 (void) WriteBlobByte(image,255); /* layer opacity */
2787 (void) WriteBlobByte(image,0);
dirk48febd62014-04-08 04:26:07 +00002788 (void) WriteBlobByte(image,next_image->compose==NoCompositeOp ?
2789 1 << 0x02 : 1); /* layer properties - visible, etc. */
cristy8e9bd3e2010-08-29 00:03:56 +00002790 (void) WriteBlobByte(image,0);
cristyd15e6592011-10-15 00:13:06 +00002791 property=(const char *) GetImageProperty(next_image,"label",exception);
cristy8e9bd3e2010-08-29 00:03:56 +00002792 if (property == (const char *) NULL)
2793 {
dirk93b02b72013-11-16 16:03:36 +00002794 char
cristy151b66d2015-04-15 10:50:31 +00002795 layer_name[MagickPathExtent];
cristy7a1ce132013-11-14 12:38:14 +00002796
cristy8e9bd3e2010-08-29 00:03:56 +00002797 (void) WriteBlobMSBLong(image,16);
2798 (void) WriteBlobMSBLong(image,0);
2799 (void) WriteBlobMSBLong(image,0);
cristy151b66d2015-04-15 10:50:31 +00002800 (void) FormatLocaleString(layer_name,MagickPathExtent,"L%04ld",(long)
cristy7a1ce132013-11-14 12:38:14 +00002801 layer_count++);
2802 WritePascalString(image,layer_name,4);
cristy8e9bd3e2010-08-29 00:03:56 +00002803 }
2804 else
2805 {
2806 size_t
2807 length;
cristy3ed852e2009-09-05 21:47:34 +00002808
cristy8e9bd3e2010-08-29 00:03:56 +00002809 length=strlen(property);
2810 (void) WriteBlobMSBLong(image,(unsigned int) (length+(4-
2811 (length % 4))+8));
2812 (void) WriteBlobMSBLong(image,0);
2813 (void) WriteBlobMSBLong(image,0);
2814 WritePascalString(image,property,4);
2815 }
2816 next_image=GetNextImageInList(next_image);
2817 }
2818 /*
2819 Now the image data!
2820 */
2821 next_image=base_image;
2822 while (next_image != NULL)
2823 {
2824 status=WriteImageChannels(&psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002825 MagickTrue,exception);
cristy8e9bd3e2010-08-29 00:03:56 +00002826 next_image=GetNextImageInList(next_image);
2827 }
2828 (void) WriteBlobMSBLong(image,0); /* user mask data */
2829 base_image->compression=compression;
cristy144f1b62010-05-18 00:52:09 +00002830 }
cristy144f1b62010-05-18 00:52:09 +00002831 /*
2832 Write composite image.
2833 */
dirkf3081522015-06-21 09:31:18 +00002834 if (status != MagickFalse)
2835 status=WriteImageChannels(&psd_info,image_info,image,image,MagickFalse,
2836 exception);
cristy3ed852e2009-09-05 21:47:34 +00002837 (void) CloseBlob(image);
2838 return(status);
2839}