blob: 64a0dbd700858155f089d515cd2b5e7e1b4fec90 [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% %
cristyfe676ee2013-11-18 13:03:38 +000023% Copyright 1999-2014 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"
51#include "MagickCore/colormap.h"
52#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000053#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000054#include "MagickCore/constitute.h"
55#include "MagickCore/enhance.h"
56#include "MagickCore/exception.h"
57#include "MagickCore/exception-private.h"
58#include "MagickCore/image.h"
59#include "MagickCore/image-private.h"
60#include "MagickCore/list.h"
61#include "MagickCore/log.h"
62#include "MagickCore/magick.h"
63#include "MagickCore/memory_.h"
64#include "MagickCore/module.h"
65#include "MagickCore/monitor-private.h"
66#include "MagickCore/pixel.h"
67#include "MagickCore/pixel-accessor.h"
68#include "MagickCore/profile.h"
69#include "MagickCore/property.h"
70#include "MagickCore/quantum-private.h"
71#include "MagickCore/static.h"
72#include "MagickCore/string_.h"
dirkb41f0802013-12-26 16:47:58 +000073#ifdef MAGICKCORE_ZLIB_DELEGATE
74#include <zlib.h>
75#endif
cristy3ed852e2009-09-05 21:47:34 +000076
77/*
cristy2d3d87f2010-03-01 00:23:08 +000078 Define declaractions.
79*/
cristyaca3ec52010-03-02 14:55:01 +000080#define MaxPSDChannels 56
cristybb503372010-05-27 20:51:26 +000081#define PSDQuantum(x) (((ssize_t) (x)+1) & -2)
cristy2d3d87f2010-03-01 00:23:08 +000082
83/*
84 Enumerated declaractions.
85*/
86typedef enum
87{
dirkb41f0802013-12-26 16:47:58 +000088 Raw = 0,
89 RLE = 1,
90 ZipWithoutPrediction = 2,
91 ZipWithPrediction = 3
92} PSDCompressionType;
93
94typedef enum
95{
cristy2d3d87f2010-03-01 00:23:08 +000096 BitmapMode = 0,
97 GrayscaleMode = 1,
98 IndexedMode = 2,
99 RGBMode = 3,
100 CMYKMode = 4,
101 MultichannelMode = 7,
102 DuotoneMode = 8,
103 LabMode = 9
104} PSDImageType;
105
106/*
107 Typedef declaractions.
108*/
109typedef struct _ChannelInfo
110{
111 short int
112 type;
113
cristybb503372010-05-27 20:51:26 +0000114 size_t
cristy2d3d87f2010-03-01 00:23:08 +0000115 size;
116} ChannelInfo;
117
118typedef struct _LayerInfo
119{
120 RectangleInfo
121 page,
122 mask;
123
124 unsigned short
125 channels;
126
127 ChannelInfo
128 channel_info[MaxPSDChannels];
129
130 char
131 blendkey[4];
132
133 Quantum
134 opacity;
135
136 unsigned char
137 clipping,
138 visible,
139 flags;
140
cristybb503372010-05-27 20:51:26 +0000141 size_t
cristy2d3d87f2010-03-01 00:23:08 +0000142 offset_x,
143 offset_y;
144
145 unsigned char
146 name[256];
147
148 Image
149 *image;
150} LayerInfo;
151
152typedef struct _PSDInfo
153{
154 char
155 signature[4];
156
157 unsigned short
158 channels,
159 version;
160
161 unsigned char
162 reserved[6];
163
cristybb503372010-05-27 20:51:26 +0000164 size_t
cristy2d3d87f2010-03-01 00:23:08 +0000165 rows,
166 columns;
167
168 unsigned short
169 depth,
170 mode;
171} PSDInfo;
172
173/*
cristy3ed852e2009-09-05 21:47:34 +0000174 Forward declarations.
175*/
176static MagickBooleanType
cristy3a37efd2011-08-28 20:31:03 +0000177 WritePSDImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000178
179/*
180%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
181% %
182% %
183% %
cristy3ed852e2009-09-05 21:47:34 +0000184% I s P S D %
185% %
186% %
187% %
188%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
189%
190% IsPSD()() returns MagickTrue if the image format type, identified by the
191% magick string, is PSD.
192%
193% The format of the IsPSD method is:
194%
195% MagickBooleanType IsPSD(const unsigned char *magick,const size_t length)
196%
197% A description of each parameter follows:
198%
199% o magick: compare image format pattern against these bytes.
200%
201% o length: Specifies the length of the magick string.
202%
203*/
204static MagickBooleanType IsPSD(const unsigned char *magick,const size_t length)
205{
206 if (length < 4)
207 return(MagickFalse);
208 if (LocaleNCompare((const char *) magick,"8BPS",4) == 0)
209 return(MagickTrue);
210 return(MagickFalse);
211}
212
213/*
214%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
215% %
216% %
217% %
218% R e a d P S D I m a g e %
219% %
220% %
221% %
222%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
223%
224% ReadPSDImage() reads an Adobe Photoshop image file and returns it. It
225% allocates the memory necessary for the new Image structure and returns a
226% pointer to the new image.
227%
228% The format of the ReadPSDImage method is:
229%
cristycd081772010-03-22 17:19:12 +0000230% Image *ReadPSDImage(image_info)
cristy3ed852e2009-09-05 21:47:34 +0000231%
232% A description of each parameter follows:
233%
234% o image_info: the image info.
235%
236% o exception: return any errors or warnings in this structure.
237%
238*/
239
cristy19eb6412010-04-23 14:42:29 +0000240static const char *CompositeOperatorToPSDBlendMode(CompositeOperator op)
cristycd081772010-03-22 17:19:12 +0000241{
242 const char
243 *blend_mode;
244
cristy19eb6412010-04-23 14:42:29 +0000245 switch (op)
cristycd081772010-03-22 17:19:12 +0000246 {
247 case OverCompositeOp: blend_mode = "norm"; break;
248 case MultiplyCompositeOp: blend_mode = "mul "; break;
249 case DissolveCompositeOp: blend_mode = "diss"; break;
250 case DifferenceCompositeOp: blend_mode = "diff"; break;
251 case DarkenCompositeOp: blend_mode = "dark"; break;
252 case LightenCompositeOp: blend_mode = "lite"; break;
253 case HueCompositeOp: blend_mode = "hue "; break;
254 case SaturateCompositeOp: blend_mode = "sat "; break;
255 case ColorizeCompositeOp: blend_mode = "colr"; break;
256 case LuminizeCompositeOp: blend_mode = "lum "; break;
257 case ScreenCompositeOp: blend_mode = "scrn"; break;
258 case OverlayCompositeOp: blend_mode = "over"; break;
259 default:
260 blend_mode = "norm";
261 }
262 return(blend_mode);
263}
264
dirkb41f0802013-12-26 16:47:58 +0000265static inline CompressionType ConvertPSDCompression(
266 PSDCompressionType compression)
267{
268 switch (compression)
269 {
270 case RLE:
271 return RLECompression;
272 case ZipWithPrediction:
273 case ZipWithoutPrediction:
274 return ZipCompression;
275 default:
276 return NoCompression;
277 }
278}
279
280static MagickStatusType CorrectPSDOpacity(LayerInfo* layer_info,
281 ExceptionInfo *exception)
282{
283 register Quantum
284 *q;
285
286 register ssize_t
287 x;
288
289 ssize_t
290 y;
291
292 if (layer_info->opacity == OpaqueAlpha)
293 return(MagickTrue);
294
295 layer_info->image->alpha_trait=BlendPixelTrait;
296 for (y=0; y < (ssize_t) layer_info->image->rows; y++)
297 {
cristyd8083a62014-02-01 13:53:44 +0000298 q=GetAuthenticPixels(layer_info->image,0,y,layer_info->image->columns,1,
299 exception);
dirkb41f0802013-12-26 16:47:58 +0000300 if (q == (Quantum *) NULL)
301 break;
302 for (x=0; x < (ssize_t) layer_info->image->columns; x++)
303 {
304 SetPixelAlpha(layer_info->image,(Quantum) (QuantumScale*(GetPixelAlpha(
305 layer_info->image,q))*layer_info->opacity),q);
306 q+=GetPixelChannels(layer_info->image);
307 }
308 if (SyncAuthenticPixels(layer_info->image,exception) == MagickFalse)
309 return(MagickFalse);
310 }
311
312 return(MagickTrue);
313}
314
cristycd081772010-03-22 17:19:12 +0000315static ssize_t DecodePSDPixels(const size_t number_compact_pixels,
cristybb503372010-05-27 20:51:26 +0000316 const unsigned char *compact_pixels,const ssize_t depth,
cristycd081772010-03-22 17:19:12 +0000317 const size_t number_pixels,unsigned char *pixels)
318{
319 int
320 pixel;
321
322 register ssize_t
323 i,
324 j;
325
cristycd081772010-03-22 17:19:12 +0000326 size_t
327 length;
328
cristy802d3642011-04-27 02:02:41 +0000329 ssize_t
330 packets;
331
cristycd081772010-03-22 17:19:12 +0000332 packets=(ssize_t) number_compact_pixels;
333 for (i=0; (packets > 1) && (i < (ssize_t) number_pixels); )
334 {
dirk40084fa2014-02-03 21:29:38 +0000335 length=(size_t) (*compact_pixels++);
cristycd081772010-03-22 17:19:12 +0000336 packets--;
337 if (length == 128)
338 continue;
339 if (length > 128)
340 {
341 length=256-length+1;
dirk40084fa2014-02-03 21:29:38 +0000342 if ((ssize_t) length + i > (ssize_t) number_pixels)
343 length=number_pixels-(size_t) i;
cristycd081772010-03-22 17:19:12 +0000344 pixel=(*compact_pixels++);
345 packets--;
cristy284c7d82010-04-24 00:19:14 +0000346 for (j=0; j < (ssize_t) length; j++)
cristycd081772010-03-22 17:19:12 +0000347 {
348 switch (depth)
349 {
350 case 1:
351 {
cristy284c7d82010-04-24 00:19:14 +0000352 *pixels++=(pixel >> 7) & 0x01 ? 0U : 255U;
353 *pixels++=(pixel >> 6) & 0x01 ? 0U : 255U;
354 *pixels++=(pixel >> 5) & 0x01 ? 0U : 255U;
355 *pixels++=(pixel >> 4) & 0x01 ? 0U : 255U;
356 *pixels++=(pixel >> 3) & 0x01 ? 0U : 255U;
357 *pixels++=(pixel >> 2) & 0x01 ? 0U : 255U;
358 *pixels++=(pixel >> 1) & 0x01 ? 0U : 255U;
359 *pixels++=(pixel >> 0) & 0x01 ? 0U : 255U;
cristycd081772010-03-22 17:19:12 +0000360 i+=8;
361 break;
362 }
363 case 4:
364 {
cristy284c7d82010-04-24 00:19:14 +0000365 *pixels++=(unsigned char) ((pixel >> 4) & 0xff);
366 *pixels++=(unsigned char) ((pixel & 0x0f) & 0xff);
cristycd081772010-03-22 17:19:12 +0000367 i+=2;
368 break;
369 }
370 case 2:
371 {
cristy284c7d82010-04-24 00:19:14 +0000372 *pixels++=(unsigned char) ((pixel >> 6) & 0x03);
373 *pixels++=(unsigned char) ((pixel >> 4) & 0x03);
374 *pixels++=(unsigned char) ((pixel >> 2) & 0x03);
375 *pixels++=(unsigned char) ((pixel & 0x03) & 0x03);
cristycd081772010-03-22 17:19:12 +0000376 i+=4;
377 break;
378 }
379 default:
380 {
cristy284c7d82010-04-24 00:19:14 +0000381 *pixels++=(unsigned char) pixel;
cristycd081772010-03-22 17:19:12 +0000382 i++;
383 break;
384 }
385 }
386 }
387 continue;
388 }
389 length++;
dirk40084fa2014-02-03 21:29:38 +0000390 if ((ssize_t) length + i > (ssize_t) number_pixels)
391 length=number_pixels-(size_t) i;
cristy284c7d82010-04-24 00:19:14 +0000392 for (j=0; j < (ssize_t) length; j++)
cristycd081772010-03-22 17:19:12 +0000393 {
394 switch (depth)
395 {
396 case 1:
397 {
cristy284c7d82010-04-24 00:19:14 +0000398 *pixels++=(*compact_pixels >> 7) & 0x01 ? 0U : 255U;
399 *pixels++=(*compact_pixels >> 6) & 0x01 ? 0U : 255U;
400 *pixels++=(*compact_pixels >> 5) & 0x01 ? 0U : 255U;
401 *pixels++=(*compact_pixels >> 4) & 0x01 ? 0U : 255U;
402 *pixels++=(*compact_pixels >> 3) & 0x01 ? 0U : 255U;
403 *pixels++=(*compact_pixels >> 2) & 0x01 ? 0U : 255U;
404 *pixels++=(*compact_pixels >> 1) & 0x01 ? 0U : 255U;
405 *pixels++=(*compact_pixels >> 0) & 0x01 ? 0U : 255U;
cristycd081772010-03-22 17:19:12 +0000406 i+=8;
407 break;
408 }
409 case 4:
410 {
cristy618a9662010-03-23 13:59:54 +0000411 *pixels++=(*compact_pixels >> 4) & 0xff;
412 *pixels++=(*compact_pixels & 0x0f) & 0xff;
cristycd081772010-03-22 17:19:12 +0000413 i+=2;
414 break;
415 }
416 case 2:
417 {
cristy618a9662010-03-23 13:59:54 +0000418 *pixels++=(*compact_pixels >> 6) & 0x03;
419 *pixels++=(*compact_pixels >> 4) & 0x03;
420 *pixels++=(*compact_pixels >> 2) & 0x03;
421 *pixels++=(*compact_pixels & 0x03) & 0x03;
cristycd081772010-03-22 17:19:12 +0000422 i+=4;
423 break;
424 }
425 default:
426 {
427 *pixels++=(*compact_pixels);
428 i++;
429 break;
430 }
431 }
432 compact_pixels++;
433 }
434 }
435 return(i);
436}
437
dirkbd8bd852013-12-28 22:55:19 +0000438static inline LayerInfo *DestroyLayerInfo(LayerInfo *layer_info,
439 const ssize_t number_layers)
440{
441 ssize_t
442 i;
443
444 for (i=0; i<number_layers; i++)
445 {
446 if (layer_info[i].image != (Image *) NULL)
447 layer_info[i].image=DestroyImage(layer_info[i].image);
448 }
449
450 return (LayerInfo *) RelinquishMagickMemory(layer_info);
451}
452
dirkb41f0802013-12-26 16:47:58 +0000453static inline size_t GetPSDPacketSize(Image *image)
cristy2d3d87f2010-03-01 00:23:08 +0000454{
dirkb41f0802013-12-26 16:47:58 +0000455 if (image->storage_class == PseudoClass)
456 {
457 if (image->colors > 256)
458 return(2);
459 else if (image->depth > 8)
460 return(2);
461 }
462 else
463 if (image->depth > 8)
464 return(2);
465
466 return(1);
cristy2d3d87f2010-03-01 00:23:08 +0000467}
468
469static inline MagickSizeType GetPSDSize(PSDInfo *psd_info,Image *image)
470{
471 if (psd_info->version == 1)
472 return((MagickSizeType) ReadBlobMSBLong(image));
473 return((MagickSizeType) ReadBlobMSBLongLong(image));
474}
475
dirkb41f0802013-12-26 16:47:58 +0000476static inline size_t GetPSDRowSize(Image *image)
477{
478 if (image->depth == 1)
479 return((image->columns+7)/8);
480 else
481 return(image->columns*GetPSDPacketSize(image));
482}
483
cristybb503372010-05-27 20:51:26 +0000484static inline ssize_t MagickAbsoluteValue(const ssize_t x)
cristy3ed852e2009-09-05 21:47:34 +0000485{
486 if (x < 0)
487 return(-x);
488 return(x);
489}
490
cristycd081772010-03-22 17:19:12 +0000491static const char *ModeToString(PSDImageType type)
cristy3ed852e2009-09-05 21:47:34 +0000492{
cristycd081772010-03-22 17:19:12 +0000493 switch (type)
cristy3ed852e2009-09-05 21:47:34 +0000494 {
495 case BitmapMode: return "Bitmap";
496 case GrayscaleMode: return "Grayscale";
497 case IndexedMode: return "Indexed";
498 case RGBMode: return "RGB";
499 case CMYKMode: return "CMYK";
500 case MultichannelMode: return "Multichannel";
501 case DuotoneMode: return "Duotone";
502 case LabMode: return "L*A*B";
503 default: return "unknown";
504 }
505}
506
507static MagickBooleanType ParseImageResourceBlocks(Image *image,
cristyd15e6592011-10-15 00:13:06 +0000508 const unsigned char *blocks,size_t length,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000509{
510 const unsigned char
511 *p;
512
513 StringInfo
514 *profile;
515
cristy6befb0f2010-05-31 14:33:15 +0000516 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000517 count,
cristy6befb0f2010-05-31 14:33:15 +0000518 long_sans;
cristy3ed852e2009-09-05 21:47:34 +0000519
520 unsigned short
521 id,
522 short_sans;
523
524 if (length < 16)
525 return(MagickFalse);
cristy8723e4b2011-09-01 13:11:19 +0000526 profile=BlobToStringInfo((const void *) NULL,length);
cristy3ed852e2009-09-05 21:47:34 +0000527 SetStringInfoDatum(profile,blocks);
cristyd15e6592011-10-15 00:13:06 +0000528 (void) SetImageProfile(image,"8bim",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000529 profile=DestroyStringInfo(profile);
530 for (p=blocks; (p >= blocks) && (p < (blocks+length-16)); )
531 {
532 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
533 break;
cristy6befb0f2010-05-31 14:33:15 +0000534 p=PushLongPixel(MSBEndian,p,&long_sans);
cristyf11065e2010-05-14 13:26:59 +0000535 p=PushShortPixel(MSBEndian,p,&id);
536 p=PushShortPixel(MSBEndian,p,&short_sans);
537 p=PushLongPixel(MSBEndian,p,&count);
cristy3ed852e2009-09-05 21:47:34 +0000538 switch (id)
539 {
540 case 0x03ed:
541 {
cristy1e4a80b2010-05-14 16:18:26 +0000542 char
543 value[MaxTextExtent];
544
cristy3ed852e2009-09-05 21:47:34 +0000545 unsigned short
546 resolution;
547
548 /*
549 Resolution info.
550 */
cristyf11065e2010-05-14 13:26:59 +0000551 p=PushShortPixel(MSBEndian,p,&resolution);
cristy2a11bef2011-10-28 18:33:11 +0000552 image->resolution.x=(double) resolution;
553 (void) FormatLocaleString(value,MaxTextExtent,"%g",image->resolution.x);
cristyd15e6592011-10-15 00:13:06 +0000554 (void) SetImageProperty(image,"tiff:XResolution",value,exception);
cristyf11065e2010-05-14 13:26:59 +0000555 p=PushShortPixel(MSBEndian,p,&short_sans);
556 p=PushShortPixel(MSBEndian,p,&short_sans);
557 p=PushShortPixel(MSBEndian,p,&short_sans);
558 p=PushShortPixel(MSBEndian,p,&resolution);
cristy2a11bef2011-10-28 18:33:11 +0000559 image->resolution.y=(double) resolution;
560 (void) FormatLocaleString(value,MaxTextExtent,"%g",image->resolution.y);
cristyd15e6592011-10-15 00:13:06 +0000561 (void) SetImageProperty(image,"tiff:YResolution",value,exception);
cristyf11065e2010-05-14 13:26:59 +0000562 p=PushShortPixel(MSBEndian,p,&short_sans);
563 p=PushShortPixel(MSBEndian,p,&short_sans);
564 p=PushShortPixel(MSBEndian,p,&short_sans);
cristy15893bd2012-12-19 14:40:13 +0000565 image->units=PixelsPerInchResolution;
cristy3ed852e2009-09-05 21:47:34 +0000566 break;
567 }
568 default:
569 {
570 p+=count;
571 break;
572 }
573 }
574 if ((count & 0x01) != 0)
575 p++;
576 }
577 return(MagickTrue);
578}
579
cristycd081772010-03-22 17:19:12 +0000580static CompositeOperator PSDBlendModeToCompositeOperator(const char *mode)
cristy56ed31c2010-03-22 00:46:21 +0000581{
cristycd081772010-03-22 17:19:12 +0000582 if (mode == (const char *) NULL)
583 return(OverCompositeOp);
584 if (LocaleNCompare(mode,"norm",4) == 0)
585 return(OverCompositeOp);
586 if (LocaleNCompare(mode,"mul ",4) == 0)
587 return(MultiplyCompositeOp);
588 if (LocaleNCompare(mode,"diss",4) == 0)
589 return(DissolveCompositeOp);
590 if (LocaleNCompare(mode,"diff",4) == 0)
591 return(DifferenceCompositeOp);
592 if (LocaleNCompare(mode,"dark",4) == 0)
593 return(DarkenCompositeOp);
594 if (LocaleNCompare(mode,"lite",4) == 0)
595 return(LightenCompositeOp);
596 if (LocaleNCompare(mode,"hue ",4) == 0)
597 return(HueCompositeOp);
598 if (LocaleNCompare(mode,"sat ",4) == 0)
599 return(SaturateCompositeOp);
600 if (LocaleNCompare(mode,"colr",4) == 0)
601 return(ColorizeCompositeOp);
602 if (LocaleNCompare(mode,"lum ",4) == 0)
603 return(LuminizeCompositeOp);
604 if (LocaleNCompare(mode,"scrn",4) == 0)
605 return(ScreenCompositeOp);
606 if (LocaleNCompare(mode,"over",4) == 0)
607 return(OverlayCompositeOp);
608 if (LocaleNCompare(mode,"hLit",4) == 0)
609 return(OverCompositeOp);
610 if (LocaleNCompare(mode,"sLit",4) == 0)
611 return(OverCompositeOp);
612 if (LocaleNCompare(mode,"smud",4) == 0)
613 return(OverCompositeOp);
614 if (LocaleNCompare(mode,"div ",4) == 0)
615 return(OverCompositeOp);
616 if (LocaleNCompare(mode,"idiv",4) == 0)
617 return(OverCompositeOp);
618 return(OverCompositeOp);
cristy56ed31c2010-03-22 00:46:21 +0000619}
620
dirkb41f0802013-12-26 16:47:58 +0000621static MagickStatusType ReadPSDChannelPixels(Image *image,
622 const size_t channels,const size_t row,const ssize_t type,
623 const unsigned char *pixels,ExceptionInfo *exception)
cristy56ed31c2010-03-22 00:46:21 +0000624{
cristy56ed31c2010-03-22 00:46:21 +0000625 Quantum
626 pixel;
627
cristy7753b2a2011-02-19 18:36:52 +0000628 register const unsigned char
629 *p;
630
cristy4c08aed2011-07-01 19:47:50 +0000631 register Quantum
cristy56ed31c2010-03-22 00:46:21 +0000632 *q;
633
cristy7753b2a2011-02-19 18:36:52 +0000634 register ssize_t
635 x;
cristy56ed31c2010-03-22 00:46:21 +0000636
637 size_t
638 packet_size;
639
dirkb41f0802013-12-26 16:47:58 +0000640 unsigned short
641 nibble;
642
643 p=pixels;
644 q=GetAuthenticPixels(image,0,row,image->columns,1,exception);
645 if (q == (Quantum *) NULL)
646 return MagickFalse;
647 packet_size=GetPSDPacketSize(image);
648 for (x=0; x < (ssize_t) image->columns; x++)
649 {
650 if (packet_size == 1)
651 pixel=ScaleCharToQuantum(*p++);
652 else
653 {
654 p=PushShortPixel(MSBEndian,p,&nibble);
655 pixel=ScaleShortToQuantum(nibble);
656 }
657 switch (type)
658 {
659 case -1:
660 {
661 SetPixelAlpha(image,pixel,q);
662 break;
663 }
664 case 0:
665 {
666 SetPixelRed(image,pixel,q);
667 if (channels == 1)
668 SetPixelGray(image,pixel,q);
669 else
670 SetPixelRed(image,pixel,q);
671 if (image->storage_class == PseudoClass)
672 {
673 if (packet_size == 1)
674 SetPixelIndex(image,ScaleQuantumToChar(pixel),q);
675 else
676 SetPixelIndex(image,ScaleQuantumToShort(pixel),q);
677 SetPixelInfoPixel(image,image->colormap+(ssize_t)
678 GetPixelIndex(image,q),q);
679 if (image->depth == 1)
680 {
681 ssize_t
682 bit,
683 number_bits;
684
685 number_bits=image->columns-x;
686 if (number_bits > 8)
687 number_bits=8;
688 for (bit=0; bit < number_bits; bit++)
689 {
690 SetPixelIndex(image,(((unsigned char) pixel) &
691 (0x01 << (7-bit))) != 0 ? 0 : 255,q);
692 SetPixelInfoPixel(image,image->colormap+(ssize_t)
693 GetPixelIndex(image,q),q);
694 q+=GetPixelChannels(image);
695 x++;
696 }
697 }
698 }
699 break;
700 }
701 case 1:
702 {
703 if (image->storage_class == PseudoClass)
704 SetPixelAlpha(image,pixel,q);
705 else
706 SetPixelGreen(image,pixel,q);
707 break;
708 }
709 case 2:
710 {
711 if (image->storage_class == PseudoClass)
712 SetPixelAlpha(image,pixel,q);
713 else
714 SetPixelBlue(image,pixel,q);
715 break;
716 }
717 case 3:
718 {
719 if (image->colorspace == CMYKColorspace)
720 SetPixelBlack(image,pixel,q);
721 else
722 if (image->alpha_trait == BlendPixelTrait)
723 SetPixelAlpha(image,pixel,q);
724 break;
725 }
726 case 4:
727 {
728 if ((IssRGBCompatibleColorspace(image->colorspace) != MagickFalse) &&
729 (channels > 3))
730 break;
731 if (image->alpha_trait == BlendPixelTrait)
732 SetPixelAlpha(image,pixel,q);
733 break;
734 }
735 default:
736 break;
737 }
738 q+=GetPixelChannels(image);
739 }
740 return(SyncAuthenticPixels(image,exception));
741}
742
743static MagickStatusType ReadPSDChannelRaw(Image *image,const size_t channels,
744 const ssize_t type,ExceptionInfo *exception)
745{
746 MagickStatusType
747 status;
748
749 size_t
750 count,
751 row_size;
752
753 ssize_t
754 y;
755
756 unsigned char
757 *pixels;
758
759 if (image->debug != MagickFalse)
760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
761 " layer data is RAW");
762
763 row_size=GetPSDRowSize(image);
764 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
765 if (pixels == (unsigned char *) NULL)
766 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
767 image->filename);
768
769 status=MagickTrue;
770 for (y=0; y < (ssize_t) image->rows; y++)
771 {
772 status=MagickFalse;
773
774 count=ReadBlob(image,row_size,pixels);
775 if (count != row_size)
776 break;
777
778 status=ReadPSDChannelPixels(image,channels,y,type,pixels,exception);
779 if (status == MagickFalse)
780 break;
781 }
782
783 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
784 return(status);
785}
786
787static inline MagickOffsetType *ReadPSDRLEOffsets(Image *image,
788 PSDInfo *psd_info,const size_t size)
789{
790 MagickOffsetType
791 *offsets;
792
793 ssize_t
794 y;
795
796 offsets=(MagickOffsetType *) AcquireQuantumMemory(size,sizeof(*offsets));
797 if(offsets != (MagickOffsetType *) NULL)
798 {
799 for (y=0; y < (ssize_t) size; y++)
800 {
801 if (psd_info->version == 1)
802 offsets[y]=(MagickOffsetType) ReadBlobMSBShort(image);
803 else
804 offsets[y]=(MagickOffsetType) ReadBlobMSBLong(image);
805 }
806 }
807 return offsets;
808}
809
810static MagickStatusType ReadPSDChannelRLE(Image *image,PSDInfo *psd_info,
811 const ssize_t type,MagickOffsetType *offsets,ExceptionInfo *exception)
812{
813 MagickStatusType
814 status;
815
816 size_t
817 length,
818 row_size;
819
cristy56ed31c2010-03-22 00:46:21 +0000820 ssize_t
cristyd05dca12010-07-25 02:32:32 +0000821 count,
822 y;
cristy56ed31c2010-03-22 00:46:21 +0000823
824 unsigned char
825 *compact_pixels,
826 *pixels;
827
dirkb41f0802013-12-26 16:47:58 +0000828 if (image->debug != MagickFalse)
829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
830 " layer data is RLE compressed");
cristy56ed31c2010-03-22 00:46:21 +0000831
dirkb41f0802013-12-26 16:47:58 +0000832 row_size=GetPSDRowSize(image);
833 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
cristy56ed31c2010-03-22 00:46:21 +0000834 if (pixels == (unsigned char *) NULL)
835 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
836 image->filename);
cristye195f262010-04-16 18:12:35 +0000837
dirkb41f0802013-12-26 16:47:58 +0000838 length=0;
839 for (y=0; y < (ssize_t) image->rows; y++)
840 if ((MagickOffsetType) length < offsets[y])
841 length=(size_t) offsets[y];
842
843 if (length > row_size + 256) // arbitrary number
844 {
845 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
846 ThrowBinaryException(ResourceLimitError,"InvalidLength",
847 image->filename);
cristy56ed31c2010-03-22 00:46:21 +0000848 }
dirkb41f0802013-12-26 16:47:58 +0000849
850 compact_pixels=(unsigned char *) AcquireQuantumMemory(length,
851 sizeof(*pixels));
852 if (compact_pixels == (unsigned char *) NULL)
853 {
854 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
855 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
856 image->filename);
857 }
858
859 (void) ResetMagickMemory(compact_pixels,0,length*sizeof(*compact_pixels));
860
861 status=MagickTrue;
cristybb503372010-05-27 20:51:26 +0000862 for (y=0; y < (ssize_t) image->rows; y++)
cristy56ed31c2010-03-22 00:46:21 +0000863 {
dirkb41f0802013-12-26 16:47:58 +0000864 status=MagickFalse;
cristy3b004082010-08-12 19:56:38 +0000865
dirkb41f0802013-12-26 16:47:58 +0000866 count=ReadBlob(image,(size_t) offsets[y],compact_pixels);
867 if (count != (ssize_t) offsets[y])
868 break;
869
870 count=DecodePSDPixels((size_t) offsets[y],compact_pixels,
871 (ssize_t) (image->depth == 1 ? 123456 : image->depth),row_size,pixels);
872 if (count != (ssize_t) row_size)
873 break;
874
875 status=ReadPSDChannelPixels(image,psd_info->channels,y,type,pixels,
876 exception);
877 if (status == MagickFalse)
cristy56ed31c2010-03-22 00:46:21 +0000878 break;
879 }
dirkb41f0802013-12-26 16:47:58 +0000880
881 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
cristy56ed31c2010-03-22 00:46:21 +0000882 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
dirkb41f0802013-12-26 16:47:58 +0000883 return(status);
cristy56ed31c2010-03-22 00:46:21 +0000884}
885
dirkb41f0802013-12-26 16:47:58 +0000886static MagickStatusType ReadPSDChannelZip(Image *image,
887 const size_t channels,const ssize_t type,
888 const PSDCompressionType compression,const size_t compact_size,
889 ExceptionInfo *exception)
890{
891#ifdef MAGICKCORE_ZLIB_DELEGATE
892 MagickStatusType
893 status;
894
895 register unsigned char
896 *p;
897
898 size_t
899 count,
900 length,
901 packet_size,
902 row_size;
903
904 ssize_t
905 y;
906
907 unsigned char
908 *compact_pixels,
909 *pixels;
910
911 z_stream
912 stream;
913
914 if (image->debug != MagickFalse)
915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
dirk245cc752014-01-16 18:31:58 +0000916 " layer data is ZIP compressed");
dirkb41f0802013-12-26 16:47:58 +0000917
918 compact_pixels=(unsigned char *) AcquireQuantumMemory(compact_size,
919 sizeof(*compact_pixels));
920 if (compact_pixels == (unsigned char *) NULL)
921 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
922 image->filename);
923
924 packet_size=GetPSDPacketSize(image);
925 row_size=image->columns*packet_size;
926 count=image->rows*row_size;
927
928 pixels=(unsigned char *) AcquireQuantumMemory(count,sizeof(*pixels));
929 if (pixels == (unsigned char *) NULL)
930 {
931 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
932 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
933 image->filename);
934 }
935
936 ResetMagickMemory(&stream, 0, sizeof(z_stream));
937 stream.data_type=Z_BINARY;
938 (void) ReadBlob(image,compact_size,compact_pixels);
939
940 stream.next_in=(Bytef *)compact_pixels;
941 stream.avail_in=compact_size;
942 stream.next_out=(Bytef *)pixels;
943 stream.avail_out=count;
944
945 if(inflateInit(&stream) == Z_OK)
946 {
947 int
948 ret;
949
950 while (stream.avail_out > 0)
951 {
952 ret=inflate(&stream, Z_SYNC_FLUSH);
953 if (ret != Z_OK && ret != Z_STREAM_END)
954 {
955 compact_pixels=(unsigned char *) RelinquishMagickMemory(
956 compact_pixels);
957 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
958 return(MagickFalse);
959 }
960 }
961 }
962
963 if (compression == ZipWithPrediction)
964 {
965 p=pixels;
966 while(count > 0)
967 {
968 length=image->columns;
969 while(--length)
970 {
971 if (packet_size == 2)
972 {
973 p[2]+=p[0]+((p[1]+p[3]) >> 8);
974 p[3]+=p[1];
975 }
976 else
977 *(p+1)+=*p;
978 p+=packet_size;
979 }
980 p+=packet_size;
981 count-=row_size;
982 }
983 }
984
985 status=MagickTrue;
986 p=pixels;
987 for (y=0; y < (ssize_t) image->rows; y++)
988 {
989 status=ReadPSDChannelPixels(image,channels,y,type,p,exception);
990 if (status == MagickFalse)
991 break;
992
993 p+=row_size;
994 }
995
996 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
997 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
998 return(status);
999#else
1000 magick_unreferenced(image);
1001 magick_unreferenced(channels);
1002 magick_unreferenced(type);
1003 magick_unreferenced(compression);
1004 magick_unreferenced(compact_size);
1005 magick_unreferenced(exception);
1006 return(MagickFalse);
1007#endif
1008}
1009
1010static MagickStatusType ReadPSDChannel(Image *image,PSDInfo *psd_info,
1011 const LayerInfo* layer_info,const size_t channel,
1012 const PSDCompressionType compression,ExceptionInfo *exception)
1013{
1014 MagickOffsetType
1015 offset;
1016
1017 MagickStatusType
1018 status;
1019
dirk245cc752014-01-16 18:31:58 +00001020 if (layer_info->channel_info[channel].type < -1)
1021 {
1022 /* ignore user supplied layer mask */
1023 SeekBlob(image,layer_info->channel_info[channel].size-2,SEEK_CUR);
1024 return(MagickTrue);
1025 }
1026
dirkb41f0802013-12-26 16:47:58 +00001027 offset=TellBlob(image);
1028 status=MagickTrue;
1029 switch(compression)
1030 {
1031 case Raw:
1032 return(ReadPSDChannelRaw(image,psd_info->channels,
1033 layer_info->channel_info[channel].type,exception));
1034 case RLE:
1035 {
1036 MagickOffsetType
1037 *offsets;
1038
1039 offsets=ReadPSDRLEOffsets(image,psd_info,image->rows);
1040 if (offsets == (MagickOffsetType *) NULL)
1041 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1042 image->filename);
1043 status=ReadPSDChannelRLE(image,psd_info,
1044 layer_info->channel_info[channel].type,offsets,exception);
1045 offsets=(MagickOffsetType *) RelinquishMagickMemory(offsets);
1046 }
1047 break;
1048 case ZipWithPrediction:
1049 case ZipWithoutPrediction:
1050#ifdef MAGICKCORE_ZLIB_DELEGATE
1051 status=ReadPSDChannelZip(image,layer_info->channels,
1052 layer_info->channel_info[channel].type,compression,
1053 layer_info->channel_info[channel].size-2,exception);
1054#else
1055 SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
1056 (void) ThrowMagickException(exception,GetMagickModule(),
1057 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
1058 "'%s' (ZLIB)",image->filename);
1059#endif
1060 break;
1061 default:
1062 SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
1063 (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
1064 "CompressionNotSupported","'%.20g'",(double) compression);
1065 break;
1066 }
1067
1068 if (status == MagickFalse)
1069 SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
1070
1071 return(status);
1072}
1073
1074static MagickStatusType ReadPSDLayer(Image *image,PSDInfo *psd_info,
1075 LayerInfo* layer_info,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001076{
cristy3ed852e2009-09-05 21:47:34 +00001077 char
dirkb41f0802013-12-26 16:47:58 +00001078 message[MaxTextExtent];
cristy3ed852e2009-09-05 21:47:34 +00001079
dirkb41f0802013-12-26 16:47:58 +00001080 MagickStatusType
1081 status;
1082
1083 PSDCompressionType
1084 compression;
1085
1086 ssize_t
1087 j;
1088
1089 if (image->debug != MagickFalse)
1090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1091 " setting up new layer image");
1092 (void) SetImageBackgroundColor(layer_info->image,exception);
1093 layer_info->image->compose=PSDBlendModeToCompositeOperator(
1094 layer_info->blendkey);
1095 if (layer_info->visible == MagickFalse)
1096 layer_info->image->compose=NoCompositeOp;
1097 if (psd_info->mode == CMYKMode)
1098 SetImageColorspace(layer_info->image,CMYKColorspace,exception);
cristyd8083a62014-02-01 13:53:44 +00001099 if ((psd_info->mode == BitmapMode) || (psd_info->mode == GrayscaleMode) ||
dirkb41f0802013-12-26 16:47:58 +00001100 (psd_info->mode == DuotoneMode))
1101 SetImageColorspace(layer_info->image,GRAYColorspace,exception);
1102 /*
1103 Set up some hidden attributes for folks that need them.
1104 */
1105 (void) FormatLocaleString(message,MaxTextExtent,"%.20gld",
1106 (double) layer_info->page.x);
1107 (void) SetImageArtifact(layer_info->image,"psd:layer.x",message);
1108 (void) FormatLocaleString(message,MaxTextExtent,"%.20g",
1109 (double) layer_info->page.y);
1110 (void) SetImageArtifact(layer_info->image,"psd:layer.y",message);
1111 (void) FormatLocaleString(message,MaxTextExtent,"%.20g",(double)
1112 layer_info->opacity);
1113 (void) SetImageArtifact(layer_info->image,"psd:layer.opacity",message);
1114 (void) SetImageProperty(layer_info->image,"label",(char *) layer_info->name,
1115 exception);
1116
1117 status=MagickTrue;
dirkb41f0802013-12-26 16:47:58 +00001118 for (j=0; j < (ssize_t) layer_info->channels; j++)
1119 {
1120 if (image->debug != MagickFalse)
1121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1122 " reading data for channel %.20g",(double) j);
1123
1124 compression=(PSDCompressionType) ReadBlobMSBShort(layer_info->image);
1125 layer_info->image->compression=ConvertPSDCompression(compression);
dirk6f205312014-01-31 22:25:10 +00001126 if (layer_info->channel_info[j].type == -1)
1127 layer_info->image->alpha_trait=BlendPixelTrait;
1128 status=ReadPSDChannel(layer_info->image,psd_info,layer_info,j,
1129 compression,exception);
dirkb41f0802013-12-26 16:47:58 +00001130
1131 if (status == MagickFalse)
1132 break;
1133 }
1134
dirk6f205312014-01-31 22:25:10 +00001135 if (status != MagickFalse)
dirkb41f0802013-12-26 16:47:58 +00001136 status=CorrectPSDOpacity(layer_info,exception);
1137
1138 if (status != MagickFalse && layer_info->image->colorspace == CMYKColorspace)
1139 (void) NegateImage(layer_info->image,MagickFalse,exception);
1140
1141 return(status);
1142}
1143
1144static MagickStatusType ReadPSDLayers(Image *image,PSDInfo *psd_info,
dirk6f205312014-01-31 22:25:10 +00001145 MagickBooleanType skip_layers,ExceptionInfo *exception)
dirkb41f0802013-12-26 16:47:58 +00001146{
1147 char
1148 type[4];
cristy3ed852e2009-09-05 21:47:34 +00001149
cristy3ed852e2009-09-05 21:47:34 +00001150 LayerInfo
1151 *layer_info;
1152
cristy56ed31c2010-03-22 00:46:21 +00001153 MagickSizeType
cristy56ed31c2010-03-22 00:46:21 +00001154 size;
1155
dirkb41f0802013-12-26 16:47:58 +00001156 MagickStatusType
1157 status;
cristydede49f2010-08-20 20:26:26 +00001158
cristybb503372010-05-27 20:51:26 +00001159 register ssize_t
dirkb41f0802013-12-26 16:47:58 +00001160 i;
cristydc05fc22011-01-22 19:43:15 +00001161
cristy3ed852e2009-09-05 21:47:34 +00001162 ssize_t
cristydc05fc22011-01-22 19:43:15 +00001163 count,
1164 j,
dirkb41f0802013-12-26 16:47:58 +00001165 number_layers;
1166
1167 size=GetPSDSize(psd_info,image);
1168 if (size == 0)
1169 {
1170 size_t
1171 quantum;
1172
1173 /*
1174 Skip layers & masks.
1175 */
1176 quantum=psd_info->version == 1 ? 4UL : 8UL;
1177 (void) ReadBlobMSBLong(image);
1178 count=ReadBlob(image,4,(unsigned char *) type);
1179 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1180 {
1181 if (DiscardBlobBytes(image,size-quantum-8) == MagickFalse)
1182 ThrowFileException(exception,CorruptImageError,
1183 "UnexpectedEndOfFile",image->filename);
1184 }
1185 else
1186 {
1187 count=ReadBlob(image,4,(unsigned char *) type);
1188 if ((count != 0) && (LocaleNCompare(type,"Lr16",4) == 0))
1189 size=GetPSDSize(psd_info,image);
1190 else
1191 if (DiscardBlobBytes(image,size-quantum-12) == MagickFalse)
1192 ThrowFileException(exception,CorruptImageError,
1193 "UnexpectedEndOfFile",image->filename);
1194 }
1195 }
1196
1197 status=MagickTrue;
1198 if (size != 0)
1199 {
1200 layer_info=(LayerInfo *) NULL;
1201 number_layers=(short) ReadBlobMSBShort(image);
1202
dirkb41f0802013-12-26 16:47:58 +00001203 if (number_layers < 0)
1204 {
1205 /*
dirk6f205312014-01-31 22:25:10 +00001206 The first alpha channel in the merged result contains the
1207 transparency data for the merged result.
dirkb41f0802013-12-26 16:47:58 +00001208 */
1209 number_layers=MagickAbsoluteValue(number_layers);
1210 if (image->debug != MagickFalse)
1211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1212 " negative layer count corrected for");
dirk6f205312014-01-31 22:25:10 +00001213 image->alpha_trait=BlendPixelTrait;
dirkb41f0802013-12-26 16:47:58 +00001214 }
1215
dirkfcaec622014-01-31 23:15:51 +00001216 if (skip_layers != MagickFalse)
1217 return(MagickTrue);
1218
dirkb41f0802013-12-26 16:47:58 +00001219 if (image->debug != MagickFalse)
1220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1221 " image contains %.20g layers",(double) number_layers);
1222
1223 if (number_layers == 0)
1224 return(MagickFalse);
1225
1226 layer_info=(LayerInfo *) AcquireQuantumMemory((size_t) number_layers,
1227 sizeof(*layer_info));
1228 if (layer_info == (LayerInfo *) NULL)
1229 {
1230 if (image->debug != MagickFalse)
1231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1232 " allocation of LayerInfo failed");
1233 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1234 image->filename);
1235 }
1236 (void) ResetMagickMemory(layer_info,0,(size_t) number_layers*
1237 sizeof(*layer_info));
1238
1239 for (i=0; i < number_layers; i++)
1240 {
1241 int
1242 x,
1243 y;
1244
1245 if (image->debug != MagickFalse)
1246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1247 " reading layer #%.20g",(double) i+1);
1248 layer_info[i].page.y=(int) ReadBlobMSBLong(image);
1249 layer_info[i].page.x=(int) ReadBlobMSBLong(image);
1250 y=(int) ReadBlobMSBLong(image);
1251 x=(int) ReadBlobMSBLong(image);
1252 layer_info[i].page.width=(ssize_t) (x-layer_info[i].page.x);
1253 layer_info[i].page.height=(ssize_t) (y-layer_info[i].page.y);
1254 layer_info[i].channels=ReadBlobMSBShort(image);
1255 if (layer_info[i].channels > MaxPSDChannels)
1256 {
dirkbd8bd852013-12-28 22:55:19 +00001257 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirkb41f0802013-12-26 16:47:58 +00001258 ThrowBinaryException(CorruptImageError,"MaximumChannelsExceeded",
1259 image->filename);
1260 }
1261 if (image->debug != MagickFalse)
1262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1263 " offset(%.20g,%.20g), size(%.20g,%.20g), channels=%.20g",
1264 (double) layer_info[i].page.x,(double) layer_info[i].page.y,
1265 (double) layer_info[i].page.height,(double)
1266 layer_info[i].page.width,(double) layer_info[i].channels);
1267 for (j=0; j < (ssize_t) layer_info[i].channels; j++)
1268 {
1269 layer_info[i].channel_info[j].type=(short) ReadBlobMSBShort(image);
1270 layer_info[i].channel_info[j].size=(size_t) GetPSDSize(psd_info,
1271 image);
1272 if (image->debug != MagickFalse)
1273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1274 " channel[%.20g]: type=%.20g, size=%.20g",(double) j,
1275 (double) layer_info[i].channel_info[j].type,
1276 (double) layer_info[i].channel_info[j].size);
1277 }
1278 count=ReadBlob(image,4,(unsigned char *) type);
1279 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1280 {
1281 if (image->debug != MagickFalse)
1282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1283 " layer type was %.4s instead of 8BIM", type);
dirkbd8bd852013-12-28 22:55:19 +00001284 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirkb41f0802013-12-26 16:47:58 +00001285 ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
1286 image->filename);
1287 }
1288 count=ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey);
dirkb52a4b72013-12-26 16:55:20 +00001289 layer_info[i].opacity=(Quantum) ScaleCharToQuantum((unsigned char)
cristyd8083a62014-02-01 13:53:44 +00001290 ReadBlobByte(image));
dirkb41f0802013-12-26 16:47:58 +00001291 layer_info[i].clipping=(unsigned char) ReadBlobByte(image);
1292 layer_info[i].flags=(unsigned char) ReadBlobByte(image);
1293 layer_info[i].visible=!(layer_info[i].flags & 0x02);
1294 if (image->debug != MagickFalse)
1295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1296 " blend=%.4s, opacity=%.20g, clipping=%s, flags=%d, visible=%s",
1297 layer_info[i].blendkey,(double) layer_info[i].opacity,
1298 layer_info[i].clipping ? "true" : "false",layer_info[i].flags,
1299 layer_info[i].visible ? "true" : "false");
1300 (void) ReadBlobByte(image); /* filler */
1301
1302 size=ReadBlobMSBLong(image);
1303 if (size != 0)
1304 {
1305 MagickSizeType
1306 combined_length,
1307 length;
1308
1309 if (image->debug != MagickFalse)
1310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1311 " layer contains additional info");
1312 length=ReadBlobMSBLong(image);
1313 combined_length=length+4;
1314 if (length != 0)
1315 {
1316 /*
1317 Layer mask info.
1318 */
1319 layer_info[i].mask.y=(int) ReadBlobMSBLong(image);
1320 layer_info[i].mask.x=(int) ReadBlobMSBLong(image);
1321 layer_info[i].mask.height=(size_t)
1322 (ReadBlobMSBLong(image)-layer_info[i].mask.y);
1323 layer_info[i].mask.width=(size_t)
1324 (ReadBlobMSBLong(image)-layer_info[i].mask.x);
1325 if (image->debug != MagickFalse)
1326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1327 " layer mask: offset(%.20g,%.20g), size(%.20g,%.20g), length=%.20g",
1328 (double) layer_info[i].mask.x,(double)
1329 layer_info[i].mask.y,(double) layer_info[i].mask.width,
1330 (double) layer_info[i].mask.height,(double)
1331 ((MagickOffsetType) length)-16);
1332 /*
1333 Skip over the rest of the layer mask information.
1334 */
1335 if (DiscardBlobBytes(image,length-16) == MagickFalse)
1336 {
dirkbd8bd852013-12-28 22:55:19 +00001337 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirkb41f0802013-12-26 16:47:58 +00001338 ThrowFileException(exception,CorruptImageError,
1339 "UnexpectedEndOfFile",image->filename);
1340 }
1341 }
1342 length=ReadBlobMSBLong(image);
1343 combined_length+=length+4;
1344 if (length != 0)
1345 {
1346 /*
1347 Layer blending ranges info.
1348 */
1349 if (image->debug != MagickFalse)
1350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1351 " layer blending ranges: length=%.20g",(double)
1352 ((MagickOffsetType) length));
1353 /*
1354 We read it, but don't use it...
1355 */
1356 for (j=0; j < (ssize_t) (length); j+=8)
1357 {
1358 size_t blend_source=ReadBlobMSBLong(image);
1359 size_t blend_dest=ReadBlobMSBLong(image);
1360 if (image->debug != MagickFalse)
1361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1362 " source(%x), dest(%x)",(unsigned int)
1363 blend_source,(unsigned int) blend_dest);
1364 }
1365 }
1366 /*
1367 Layer name.
1368 */
1369 length=(size_t) ReadBlobByte(image);
1370 combined_length+=length+1;
1371 for (j=0; j < (ssize_t) length; j++)
1372 layer_info[i].name[j]=(unsigned char) ReadBlobByte(image);
1373 layer_info[i].name[j]='\0';
1374 if (image->debug != MagickFalse)
1375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1376 " layer name: %s",layer_info[i].name);
1377 /*
1378 Skip the rest of the variable data until we support it.
1379 */
1380 if (image->debug != MagickFalse)
1381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1382 " unsupported data: length=%.20g",(double)
1383 ((MagickOffsetType) (size-combined_length)));
1384 if (DiscardBlobBytes(image,size-combined_length) == MagickFalse)
1385 {
dirkbd8bd852013-12-28 22:55:19 +00001386 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirkb41f0802013-12-26 16:47:58 +00001387 ThrowBinaryException(CorruptImageError,
1388 "UnexpectedEndOfFile",image->filename);
1389 }
1390 }
1391 }
1392
1393 for (i=0; i < number_layers; i++)
1394 {
dirkb41f0802013-12-26 16:47:58 +00001395 if ((layer_info[i].page.width == 0) ||
1396 (layer_info[i].page.height == 0))
1397 {
1398 if (image->debug != MagickFalse)
1399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1400 " layer data is empty");
1401 continue;
1402 }
1403
dirkb41f0802013-12-26 16:47:58 +00001404 /*
1405 Allocate layered image.
1406 */
1407 layer_info[i].image=CloneImage(image,layer_info[i].page.width,
1408 layer_info[i].page.height,MagickFalse,exception);
1409 if (layer_info[i].image == (Image *) NULL)
1410 {
dirkbd8bd852013-12-28 22:55:19 +00001411 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirkb41f0802013-12-26 16:47:58 +00001412 if (image->debug != MagickFalse)
1413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1414 " allocation of image for layer %.20g failed",(double) i);
1415 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1416 image->filename);
1417 }
1418 }
1419
1420 for (i=0; i < number_layers; i++)
1421 {
1422 if (layer_info[i].image == (Image *) NULL)
1423 {
1424 for (j=0; j < layer_info[i].channels; j++)
1425 {
1426 if (DiscardBlobBytes(image,layer_info[i].channel_info[j].size) ==
1427 MagickFalse)
1428 {
dirkbd8bd852013-12-28 22:55:19 +00001429 layer_info=DestroyLayerInfo(layer_info,number_layers);
dirkb41f0802013-12-26 16:47:58 +00001430 ThrowBinaryException(CorruptImageError,
1431 "UnexpectedEndOfFile",image->filename);
1432 }
1433 }
1434 continue;
1435 }
1436
1437 if (image->debug != MagickFalse)
1438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1439 " reading data for layer %.20g",(double) i);
1440
1441 status=ReadPSDLayer(image,psd_info,&layer_info[i],exception);
1442 if (status == MagickFalse)
1443 break;
1444
1445 status=SetImageProgress(image,LoadImagesTag,i,(MagickSizeType)
1446 number_layers);
1447 if (status == MagickFalse)
1448 break;
1449 }
1450
1451 if (status != MagickFalse)
1452 {
1453 for (i=0; i < number_layers; i++)
1454 {
1455 if (layer_info[i].image == (Image *) NULL)
1456 {
1457 for (j=i; j < number_layers - 1; j++)
1458 layer_info[j] = layer_info[j+1];
1459 number_layers--;
1460 i--;
1461 }
1462 }
1463
1464 if (number_layers > 0)
1465 {
1466 for (i=0; i < number_layers; i++)
1467 {
1468 if (i > 0)
1469 layer_info[i].image->previous=layer_info[i-1].image;
1470 if (i < (number_layers-1))
1471 layer_info[i].image->next=layer_info[i+1].image;
1472 layer_info[i].image->page=layer_info[i].page;
1473 }
1474 image->next=layer_info[0].image;
1475 layer_info[0].image->previous=image;
1476 }
1477 }
1478 layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
1479 }
1480
1481 return(status);
1482}
1483
1484static MagickStatusType ReadPSDMergedImage(Image* image,PSDInfo* psd_info,
1485 ExceptionInfo *exception)
1486{
1487 MagickOffsetType
1488 *offsets;
1489
1490 MagickStatusType
1491 status;
1492
1493 PSDCompressionType
1494 compression;
1495
1496 register ssize_t
1497 i;
1498
1499 compression=(PSDCompressionType) ReadBlobMSBShort(image);
1500 image->compression=ConvertPSDCompression(compression);
1501
1502 if (compression != Raw && compression != RLE)
1503 {
1504 (void) ThrowMagickException(exception,GetMagickModule(),
1505 TypeWarning,"CompressionNotSupported","'%.20g'",(double) compression);
1506 return(MagickFalse);
1507 }
1508
1509 offsets=(MagickOffsetType *) NULL;
1510 if (compression == RLE)
1511 {
1512 offsets=ReadPSDRLEOffsets(image,psd_info,image->rows*psd_info->channels);
1513 if (offsets == (MagickOffsetType *) NULL)
1514 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1515 image->filename);
1516 }
1517
1518 status=MagickTrue;
1519 for (i=0; i < (ssize_t) psd_info->channels; i++)
1520 {
1521 if (compression == RLE)
1522 status=ReadPSDChannelRLE(image,psd_info,i,offsets+(i*image->rows),
1523 exception);
1524 else
1525 status=ReadPSDChannelRaw(image,psd_info->channels,i,exception);
1526
1527 if (status == MagickFalse)
1528 break;
1529 status=SetImageProgress(image,LoadImagesTag,i,psd_info->channels);
1530 if (status == MagickFalse)
1531 break;
1532 }
1533
1534 if (image->colorspace == CMYKColorspace)
1535 (void) NegateImage(image,MagickFalse,exception);
1536
1537 if (offsets != (MagickOffsetType *) NULL)
1538 offsets=(MagickOffsetType *) RelinquishMagickMemory(offsets);
1539
1540 return(status);
1541}
1542
1543static Image *ReadPSDImage(const ImageInfo *image_info,
1544 ExceptionInfo *exception)
1545{
1546 Image
1547 *image;
1548
1549 MagickBooleanType
1550 skip_layers,
1551 status;
1552
1553 MagickOffsetType
1554 offset;
1555
1556 MagickSizeType
1557 length;
1558
1559 PSDInfo
1560 psd_info;
1561
1562 register ssize_t
1563 i;
1564
1565 ssize_t
1566 count;
cristy3ed852e2009-09-05 21:47:34 +00001567
cristy3ed852e2009-09-05 21:47:34 +00001568 unsigned char
1569 *data;
1570
cristy3ed852e2009-09-05 21:47:34 +00001571 /*
1572 Open image file.
1573 */
1574 assert(image_info != (const ImageInfo *) NULL);
1575 assert(image_info->signature == MagickSignature);
1576 if (image_info->debug != MagickFalse)
1577 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1578 image_info->filename);
1579 assert(exception != (ExceptionInfo *) NULL);
1580 assert(exception->signature == MagickSignature);
dirkb41f0802013-12-26 16:47:58 +00001581
cristy9950d572011-10-01 18:22:35 +00001582 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001583 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1584 if (status == MagickFalse)
1585 {
1586 image=DestroyImageList(image);
1587 return((Image *) NULL);
1588 }
1589 /*
1590 Read image header.
1591 */
1592 count=ReadBlob(image,4,(unsigned char *) psd_info.signature);
1593 psd_info.version=ReadBlobMSBShort(image);
cristy50aea4a2010-03-09 17:37:44 +00001594 if ((count == 0) || (LocaleNCompare(psd_info.signature,"8BPS",4) != 0) ||
cristy2d3d87f2010-03-01 00:23:08 +00001595 ((psd_info.version != 1) && (psd_info.version != 2)))
cristy3ed852e2009-09-05 21:47:34 +00001596 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1597 count=ReadBlob(image,6,psd_info.reserved);
1598 psd_info.channels=ReadBlobMSBShort(image);
1599 if (psd_info.channels > MaxPSDChannels)
1600 ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded");
1601 psd_info.rows=ReadBlobMSBLong(image);
1602 psd_info.columns=ReadBlobMSBLong(image);
cristy2d3d87f2010-03-01 00:23:08 +00001603 if ((psd_info.version == 1) && ((psd_info.rows > 30000) ||
1604 (psd_info.columns > 30000)))
1605 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy3ed852e2009-09-05 21:47:34 +00001606 psd_info.depth=ReadBlobMSBShort(image);
cristy2d3d87f2010-03-01 00:23:08 +00001607 if ((psd_info.depth != 1) && (psd_info.depth != 8) && (psd_info.depth != 16))
1608 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy3ed852e2009-09-05 21:47:34 +00001609 psd_info.mode=ReadBlobMSBShort(image);
1610 if (image->debug != MagickFalse)
1611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001612 " Image is %.20g x %.20g with channels=%.20g, depth=%.20g, mode=%s",
1613 (double) psd_info.columns,(double) psd_info.rows,(double)
1614 psd_info.channels,(double) psd_info.depth,ModeToString((PSDImageType)
1615 psd_info.mode));
cristy3ed852e2009-09-05 21:47:34 +00001616 /*
1617 Initialize image.
1618 */
1619 image->depth=psd_info.depth;
1620 image->columns=psd_info.columns;
1621 image->rows=psd_info.rows;
cristyea1a8aa2011-10-20 13:24:06 +00001622 if (SetImageBackgroundColor(image,exception) == MagickFalse)
cristy95524f92010-02-16 18:44:34 +00001623 {
cristy95524f92010-02-16 18:44:34 +00001624 image=DestroyImageList(image);
1625 return((Image *) NULL);
1626 }
cristy3ed852e2009-09-05 21:47:34 +00001627 if (psd_info.mode == LabMode)
cristye2c4f182012-05-12 14:11:53 +00001628 SetImageColorspace(image,LabColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001629 if (psd_info.mode == CMYKMode)
dirkb41f0802013-12-26 16:47:58 +00001630 SetImageColorspace(image,CMYKColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001631 if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) ||
1632 (psd_info.mode == DuotoneMode))
1633 {
cristyafd0fd12013-09-17 12:41:18 +00001634 status=AcquireImageColormap(image,psd_info.depth != 16 ? 256 : 65536,
1635 exception);
1636 if (status == MagickFalse)
cristy52cf7f12010-02-07 18:07:56 +00001637 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00001638 if (image->debug != MagickFalse)
1639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristydede49f2010-08-20 20:26:26 +00001640 " Image colormap allocated");
cristye2c4f182012-05-12 14:11:53 +00001641 SetImageColorspace(image,GRAYColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001642 }
cristy8a46d822012-08-28 23:32:39 +00001643 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00001644 /*
1645 Read PSD raster colormap only present for indexed and duotone images.
1646 */
1647 length=ReadBlobMSBLong(image);
1648 if (length != 0)
1649 {
1650 if (image->debug != MagickFalse)
1651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1652 " reading colormap");
1653 if (psd_info.mode == DuotoneMode)
1654 {
1655 /*
1656 Duotone image data; the format of this data is undocumented.
1657 */
cristy56ed31c2010-03-22 00:46:21 +00001658 data=(unsigned char *) AcquireQuantumMemory((size_t) length,
1659 sizeof(*data));
cristy3ed852e2009-09-05 21:47:34 +00001660 if (data == (unsigned char *) NULL)
1661 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristy56ed31c2010-03-22 00:46:21 +00001662 count=ReadBlob(image,(size_t) length,data);
cristy3ed852e2009-09-05 21:47:34 +00001663 data=(unsigned char *) RelinquishMagickMemory(data);
1664 }
1665 else
1666 {
1667 /*
1668 Read PSD raster colormap.
1669 */
cristyd8083a62014-02-01 13:53:44 +00001670 if (AcquireImageColormap(image,(size_t) (length/3),exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001671 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00001672 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001673 image->colormap[i].red=ScaleCharToQuantum((unsigned char)
1674 ReadBlobByte(image));
cristybb503372010-05-27 20:51:26 +00001675 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001676 image->colormap[i].green=ScaleCharToQuantum((unsigned char)
1677 ReadBlobByte(image));
cristybb503372010-05-27 20:51:26 +00001678 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001679 image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
1680 ReadBlobByte(image));
cristy8a46d822012-08-28 23:32:39 +00001681 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00001682 }
1683 }
1684 length=ReadBlobMSBLong(image);
1685 if (length != 0)
1686 {
1687 unsigned char
1688 *blocks;
1689
1690 /*
1691 Image resources block.
1692 */
1693 if (image->debug != MagickFalse)
1694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy2b9582a2011-07-04 17:38:56 +00001695 " reading image resource blocks - %.20g bytes",(double)
1696 ((MagickOffsetType) length));
cristy56ed31c2010-03-22 00:46:21 +00001697 blocks=(unsigned char *) AcquireQuantumMemory((size_t) length,
1698 sizeof(*blocks));
cristy3ed852e2009-09-05 21:47:34 +00001699 if (blocks == (unsigned char *) NULL)
1700 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristy56ed31c2010-03-22 00:46:21 +00001701 count=ReadBlob(image,(size_t) length,blocks);
1702 if ((count != (ssize_t) length) ||
cristy3ed852e2009-09-05 21:47:34 +00001703 (LocaleNCompare((char *) blocks,"8BIM",4) != 0))
1704 {
1705 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
1706 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1707 }
dirkb41f0802013-12-26 16:47:58 +00001708 (void) ParseImageResourceBlocks(image,blocks,(size_t) length,exception);
cristy3ed852e2009-09-05 21:47:34 +00001709 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
1710 }
cristy00d3d872013-08-08 14:25:39 +00001711 /*
1712 If we are only "pinging" the image, then we're done - so return.
dirkb41f0802013-12-26 16:47:58 +00001713 */
cristy00d3d872013-08-08 14:25:39 +00001714 if (image_info->ping != MagickFalse)
1715 {
1716 (void) CloseBlob(image);
1717 return(GetFirstImageInList(image));
1718 }
cristy3ed852e2009-09-05 21:47:34 +00001719 /*
cristy3ed852e2009-09-05 21:47:34 +00001720 Layer and mask block.
1721 */
cristy2d3d87f2010-03-01 00:23:08 +00001722 length=GetPSDSize(&psd_info,image);
cristy3ed852e2009-09-05 21:47:34 +00001723 if (length == 8)
1724 {
1725 length=ReadBlobMSBLong(image);
1726 length=ReadBlobMSBLong(image);
1727 }
dirkb41f0802013-12-26 16:47:58 +00001728 offset=TellBlob(image);
1729 skip_layers=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00001730 if ((image_info->number_scenes == 1) && (image_info->scene == 0))
cristyd4297022010-09-16 22:59:09 +00001731 {
cristy374b8ad2012-02-01 20:55:21 +00001732 if (image->debug != MagickFalse)
1733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1734 " read composite only");
dirkb41f0802013-12-26 16:47:58 +00001735 skip_layers=MagickTrue;
cristyd4297022010-09-16 22:59:09 +00001736 }
cristy3ed852e2009-09-05 21:47:34 +00001737 if (length == 0)
1738 {
1739 if (image->debug != MagickFalse)
1740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1741 " image has no layers");
1742 }
1743 else
1744 {
dirk6f205312014-01-31 22:25:10 +00001745 if (ReadPSDLayers(image,&psd_info,skip_layers,exception) != MagickTrue)
1746 {
1747 (void) CloseBlob(image);
1748 return((Image *) NULL);
1749 }
cristy3ed852e2009-09-05 21:47:34 +00001750
dirkb41f0802013-12-26 16:47:58 +00001751 /*
1752 Skip the rest of the layer and mask information.
1753 */
1754 SeekBlob(image,offset+length,SEEK_SET);
cristy3ed852e2009-09-05 21:47:34 +00001755 }
dirkb41f0802013-12-26 16:47:58 +00001756
cristy3ed852e2009-09-05 21:47:34 +00001757 /*
dirkb41f0802013-12-26 16:47:58 +00001758 Read the precombined layer, present for PSD < 4 compatibility.
cristy3ed852e2009-09-05 21:47:34 +00001759 */
1760 if (image->debug != MagickFalse)
1761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1762 " reading the precombined layer");
dirkb41f0802013-12-26 16:47:58 +00001763 (void) ReadPSDMergedImage(image,&psd_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001764 (void) CloseBlob(image);
1765 return(GetFirstImageInList(image));
1766}
1767
1768/*
1769%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1770% %
1771% %
1772% %
1773% R e g i s t e r P S D I m a g e %
1774% %
1775% %
1776% %
1777%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1778%
1779% RegisterPSDImage() adds properties for the PSD image format to
1780% the list of supported formats. The properties include the image format
1781% tag, a method to read and/or write the format, whether the format
1782% supports the saving of more than one frame to the same file or blob,
1783% whether the format supports native in-memory I/O, and a brief
1784% description of the format.
1785%
1786% The format of the RegisterPSDImage method is:
1787%
cristybb503372010-05-27 20:51:26 +00001788% size_t RegisterPSDImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001789%
1790*/
cristybb503372010-05-27 20:51:26 +00001791ModuleExport size_t RegisterPSDImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001792{
1793 MagickInfo
1794 *entry;
1795
cristyb4233012010-02-28 20:09:14 +00001796 entry=SetMagickInfo("PSB");
1797 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
1798 entry->encoder=(EncodeImageHandler *) WritePSDImage;
1799 entry->magick=(IsImageFormatHandler *) IsPSD;
cristyffaf9782011-04-13 19:50:51 +00001800 entry->seekable_stream=MagickTrue;
cristyb4233012010-02-28 20:09:14 +00001801 entry->description=ConstantString("Adobe Large Document Format");
1802 entry->module=ConstantString("PSD");
1803 (void) RegisterMagickInfo(entry);
cristy3ed852e2009-09-05 21:47:34 +00001804 entry=SetMagickInfo("PSD");
1805 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
1806 entry->encoder=(EncodeImageHandler *) WritePSDImage;
1807 entry->magick=(IsImageFormatHandler *) IsPSD;
cristyffaf9782011-04-13 19:50:51 +00001808 entry->seekable_stream=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00001809 entry->description=ConstantString("Adobe Photoshop bitmap");
1810 entry->module=ConstantString("PSD");
1811 (void) RegisterMagickInfo(entry);
1812 return(MagickImageCoderSignature);
1813}
1814
1815/*
1816%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1817% %
1818% %
1819% %
1820% U n r e g i s t e r P S D I m a g e %
1821% %
1822% %
1823% %
1824%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1825%
1826% UnregisterPSDImage() removes format registrations made by the
1827% PSD module from the list of supported formats.
1828%
1829% The format of the UnregisterPSDImage method is:
1830%
1831% UnregisterPSDImage(void)
1832%
1833*/
1834ModuleExport void UnregisterPSDImage(void)
1835{
cristyb4233012010-02-28 20:09:14 +00001836 (void) UnregisterMagickInfo("PSB");
cristy3ed852e2009-09-05 21:47:34 +00001837 (void) UnregisterMagickInfo("PSD");
1838}
1839
1840/*
1841%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1842% %
1843% %
1844% %
1845% W r i t e P S D I m a g e %
1846% %
1847% %
1848% %
1849%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1850%
cristyb1459bc2010-03-23 21:41:43 +00001851% WritePSDImage() writes an image in the Adobe Photoshop encoded image format.
cristy3ed852e2009-09-05 21:47:34 +00001852%
1853% The format of the WritePSDImage method is:
1854%
dirk1ab941f2014-02-01 17:35:11 +00001855% MagickBooleanType WritePSDImage(const ImageInfo *image_info,Image *image,
1856% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001857%
1858% A description of each parameter follows.
1859%
1860% o image_info: the image info.
1861%
1862% o image: The image.
1863%
cristy3a37efd2011-08-28 20:31:03 +00001864% o exception: return any errors or warnings in this structure.
1865%
cristy3ed852e2009-09-05 21:47:34 +00001866*/
1867
cristy50aea4a2010-03-09 17:37:44 +00001868static inline ssize_t SetPSDOffset(const PSDInfo *psd_info,Image *image,
cristyf0460ee2010-03-06 02:55:11 +00001869 const size_t offset)
1870{
1871 if (psd_info->version == 1)
cristy56ed31c2010-03-22 00:46:21 +00001872 return(WriteBlobMSBShort(image,(unsigned short) offset));
1873 return(WriteBlobMSBLong(image,(unsigned short) offset));
cristyf0460ee2010-03-06 02:55:11 +00001874}
1875
cristy50aea4a2010-03-09 17:37:44 +00001876static inline ssize_t SetPSDSize(const PSDInfo *psd_info,Image *image,
cristyf0460ee2010-03-06 02:55:11 +00001877 const MagickSizeType size)
1878{
1879 if (psd_info->version == 1)
cristy56ed31c2010-03-22 00:46:21 +00001880 return(WriteBlobMSBLong(image,(unsigned int) size));
cristyf0460ee2010-03-06 02:55:11 +00001881 return(WriteBlobMSBLongLong(image,size));
1882}
1883
cristy4aff1572010-02-15 13:34:01 +00001884static size_t PSDPackbitsEncodeImage(Image *image,const size_t length,
cristy018f07f2011-09-04 21:15:19 +00001885 const unsigned char *pixels,unsigned char *compact_pixels,
1886 ExceptionInfo *exception)
cristy4aff1572010-02-15 13:34:01 +00001887{
1888 int
1889 count;
1890
cristybb503372010-05-27 20:51:26 +00001891 register ssize_t
cristy4aff1572010-02-15 13:34:01 +00001892 i,
1893 j;
1894
1895 register unsigned char
1896 *q;
1897
1898 unsigned char
1899 *packbits;
1900
1901 /*
1902 Compress pixels with Packbits encoding.
1903 */
1904 assert(image != (Image *) NULL);
1905 assert(image->signature == MagickSignature);
1906 if (image->debug != MagickFalse)
1907 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1908 assert(pixels != (unsigned char *) NULL);
1909 packbits=(unsigned char *) AcquireQuantumMemory(128UL,sizeof(*packbits));
1910 if (packbits == (unsigned char *) NULL)
1911 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1912 image->filename);
cristyb1459bc2010-03-23 21:41:43 +00001913 q=compact_pixels;
cristybb503372010-05-27 20:51:26 +00001914 for (i=(ssize_t) length; i != 0; )
cristy4aff1572010-02-15 13:34:01 +00001915 {
1916 switch (i)
1917 {
1918 case 1:
1919 {
1920 i--;
1921 *q++=(unsigned char) 0;
1922 *q++=(*pixels);
1923 break;
1924 }
1925 case 2:
1926 {
1927 i-=2;
1928 *q++=(unsigned char) 1;
1929 *q++=(*pixels);
1930 *q++=pixels[1];
1931 break;
1932 }
1933 case 3:
1934 {
1935 i-=3;
1936 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
1937 {
1938 *q++=(unsigned char) ((256-3)+1);
1939 *q++=(*pixels);
1940 break;
1941 }
1942 *q++=(unsigned char) 2;
1943 *q++=(*pixels);
1944 *q++=pixels[1];
1945 *q++=pixels[2];
1946 break;
1947 }
1948 default:
1949 {
1950 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
1951 {
1952 /*
1953 Packed run.
1954 */
1955 count=3;
cristybb503372010-05-27 20:51:26 +00001956 while (((ssize_t) count < i) && (*pixels == *(pixels+count)))
cristy4aff1572010-02-15 13:34:01 +00001957 {
1958 count++;
1959 if (count >= 127)
1960 break;
1961 }
1962 i-=count;
1963 *q++=(unsigned char) ((256-count)+1);
1964 *q++=(*pixels);
1965 pixels+=count;
1966 break;
1967 }
1968 /*
1969 Literal run.
1970 */
1971 count=0;
1972 while ((*(pixels+count) != *(pixels+count+1)) ||
1973 (*(pixels+count+1) != *(pixels+count+2)))
1974 {
1975 packbits[count+1]=pixels[count];
1976 count++;
cristybb503372010-05-27 20:51:26 +00001977 if (((ssize_t) count >= (i-3)) || (count >= 127))
cristy4aff1572010-02-15 13:34:01 +00001978 break;
1979 }
1980 i-=count;
1981 *packbits=(unsigned char) (count-1);
cristybb503372010-05-27 20:51:26 +00001982 for (j=0; j <= (ssize_t) count; j++)
cristy4aff1572010-02-15 13:34:01 +00001983 *q++=packbits[j];
1984 pixels+=count;
1985 break;
1986 }
1987 }
1988 }
1989 *q++=(unsigned char) 128; /* EOD marker */
1990 packbits=(unsigned char *) RelinquishMagickMemory(packbits);
cristyb1459bc2010-03-23 21:41:43 +00001991 return((size_t) (q-compact_pixels));
cristy4aff1572010-02-15 13:34:01 +00001992}
1993
cristy875e28a2010-03-06 19:46:55 +00001994static void WritePackbitsLength(const PSDInfo *psd_info,
cristya20214e2010-08-28 16:52:13 +00001995 const ImageInfo *image_info,Image *image,Image *next_image,
cristy018f07f2011-09-04 21:15:19 +00001996 unsigned char *compact_pixels,const QuantumType quantum_type,
1997 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001998{
cristy3ed852e2009-09-05 21:47:34 +00001999 QuantumInfo
2000 *quantum_info;
2001
cristy4c08aed2011-07-01 19:47:50 +00002002 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00002003 *p;
2004
2005 size_t
cristy4aff1572010-02-15 13:34:01 +00002006 length,
cristy3ed852e2009-09-05 21:47:34 +00002007 packet_size;
2008
cristy75f85ae2010-09-25 03:01:06 +00002009 ssize_t
2010 y;
2011
cristya20214e2010-08-28 16:52:13 +00002012 unsigned char
2013 *pixels;
2014
2015 if (next_image->depth > 8)
2016 next_image->depth=16;
2017 packet_size=next_image->depth > 8UL ? 2UL : 1UL;
cristyda16f162011-02-19 23:52:17 +00002018 (void) packet_size;
cristy4aff1572010-02-15 13:34:01 +00002019 quantum_info=AcquireQuantumInfo(image_info,image);
cristya20214e2010-08-28 16:52:13 +00002020 pixels=GetQuantumPixels(quantum_info);
2021 for (y=0; y < (ssize_t) next_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002022 {
cristyc82a27b2011-10-21 01:07:16 +00002023 p=GetVirtualPixels(next_image,0,y,next_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002024 if (p == (const Quantum *) NULL)
cristy4aff1572010-02-15 13:34:01 +00002025 break;
cristya20214e2010-08-28 16:52:13 +00002026 length=ExportQuantumPixels(next_image,(CacheView *) NULL,quantum_info,
cristyc82a27b2011-10-21 01:07:16 +00002027 quantum_type,pixels,exception);
cristy018f07f2011-09-04 21:15:19 +00002028 length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels,
2029 exception);
cristy875e28a2010-03-06 19:46:55 +00002030 (void) SetPSDOffset(psd_info,image,length);
cristy4aff1572010-02-15 13:34:01 +00002031 }
2032 quantum_info=DestroyQuantumInfo(quantum_info);
2033}
2034
cristy875e28a2010-03-06 19:46:55 +00002035static void WriteOneChannel(const PSDInfo *psd_info,const ImageInfo *image_info,
cristya20214e2010-08-28 16:52:13 +00002036 Image *image,Image *next_image,unsigned char *compact_pixels,
cristy018f07f2011-09-04 21:15:19 +00002037 const QuantumType quantum_type,const MagickBooleanType compression_flag,
2038 ExceptionInfo *exception)
cristy4aff1572010-02-15 13:34:01 +00002039{
2040 int
2041 y;
2042
cristy0910f242010-04-01 18:55:09 +00002043 MagickBooleanType
2044 monochrome;
2045
cristy4aff1572010-02-15 13:34:01 +00002046 QuantumInfo
2047 *quantum_info;
2048
cristy4c08aed2011-07-01 19:47:50 +00002049 register const Quantum
cristy4aff1572010-02-15 13:34:01 +00002050 *p;
2051
cristybb503372010-05-27 20:51:26 +00002052 register ssize_t
cristy0910f242010-04-01 18:55:09 +00002053 i;
2054
cristy4aff1572010-02-15 13:34:01 +00002055 size_t
2056 length,
2057 packet_size;
2058
cristya20214e2010-08-28 16:52:13 +00002059 unsigned char
2060 *pixels;
2061
cristy50aea4a2010-03-09 17:37:44 +00002062 (void) psd_info;
cristy4aff1572010-02-15 13:34:01 +00002063 if ((compression_flag != MagickFalse) &&
cristya20214e2010-08-28 16:52:13 +00002064 (next_image->compression != RLECompression))
cristy4aff1572010-02-15 13:34:01 +00002065 (void) WriteBlobMSBShort(image,0);
cristya20214e2010-08-28 16:52:13 +00002066 if (next_image->depth > 8)
2067 next_image->depth=16;
cristyc82a27b2011-10-21 01:07:16 +00002068 monochrome=IsImageMonochrome(image,exception) && (image->depth == 1) ?
2069 MagickTrue : MagickFalse;
cristya20214e2010-08-28 16:52:13 +00002070 packet_size=next_image->depth > 8UL ? 2UL : 1UL;
cristyda16f162011-02-19 23:52:17 +00002071 (void) packet_size;
cristy4aff1572010-02-15 13:34:01 +00002072 quantum_info=AcquireQuantumInfo(image_info,image);
cristya20214e2010-08-28 16:52:13 +00002073 pixels=GetQuantumPixels(quantum_info);
2074 for (y=0; y < (ssize_t) next_image->rows; y++)
cristy4aff1572010-02-15 13:34:01 +00002075 {
cristyc82a27b2011-10-21 01:07:16 +00002076 p=GetVirtualPixels(next_image,0,y,next_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002077 if (p == (const Quantum *) NULL)
cristy4aff1572010-02-15 13:34:01 +00002078 break;
cristya20214e2010-08-28 16:52:13 +00002079 length=ExportQuantumPixels(next_image,(CacheView *) NULL,quantum_info,
cristyc82a27b2011-10-21 01:07:16 +00002080 quantum_type,pixels,exception);
cristy0910f242010-04-01 18:55:09 +00002081 if (monochrome != MagickFalse)
cristybb503372010-05-27 20:51:26 +00002082 for (i=0; i < (ssize_t) length; i++)
cristy0910f242010-04-01 18:55:09 +00002083 pixels[i]=(~pixels[i]);
cristya20214e2010-08-28 16:52:13 +00002084 if (next_image->compression != RLECompression)
cristy4aff1572010-02-15 13:34:01 +00002085 (void) WriteBlob(image,length,pixels);
2086 else
2087 {
cristy018f07f2011-09-04 21:15:19 +00002088 length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels,
2089 exception);
cristyb1459bc2010-03-23 21:41:43 +00002090 (void) WriteBlob(image,length,compact_pixels);
cristy4aff1572010-02-15 13:34:01 +00002091 }
cristy3ed852e2009-09-05 21:47:34 +00002092 }
2093 quantum_info=DestroyQuantumInfo(quantum_info);
2094}
2095
cristy875e28a2010-03-06 19:46:55 +00002096static MagickBooleanType WriteImageChannels(const PSDInfo *psd_info,
cristya20214e2010-08-28 16:52:13 +00002097 const ImageInfo *image_info,Image *image,Image *next_image,
cristy018f07f2011-09-04 21:15:19 +00002098 const MagickBooleanType separate,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002099{
2100 int
2101 i;
2102
2103 size_t
2104 channels,
2105 packet_size;
2106
2107 unsigned char
cristya20214e2010-08-28 16:52:13 +00002108 *compact_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002109
2110 /*
cristy875e28a2010-03-06 19:46:55 +00002111 Write uncompressed pixels as separate planes.
cristy3ed852e2009-09-05 21:47:34 +00002112 */
2113 channels=1;
cristya20214e2010-08-28 16:52:13 +00002114 packet_size=next_image->depth > 8UL ? 2UL : 1UL;
2115 compact_pixels=(unsigned char *) NULL;
2116 if (next_image->compression == RLECompression)
cristy4aff1572010-02-15 13:34:01 +00002117 {
cristya20214e2010-08-28 16:52:13 +00002118 compact_pixels=(unsigned char *) AcquireQuantumMemory(2*channels*
2119 next_image->columns,packet_size*sizeof(*compact_pixels));
2120 if (compact_pixels == (unsigned char *) NULL)
2121 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy4aff1572010-02-15 13:34:01 +00002122 }
cristy3ed852e2009-09-05 21:47:34 +00002123 i=0;
cristyc82a27b2011-10-21 01:07:16 +00002124 if (IsImageGray(next_image,exception) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002125 {
cristya20214e2010-08-28 16:52:13 +00002126 if (next_image->compression == RLECompression)
cristy4aff1572010-02-15 13:34:01 +00002127 {
2128 /*
2129 Packbits compression.
2130 */
2131 (void) WriteBlobMSBShort(image,1);
cristya20214e2010-08-28 16:52:13 +00002132 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002133 compact_pixels,GrayQuantum,exception);
cristy8a46d822012-08-28 23:32:39 +00002134 if (next_image->alpha_trait == BlendPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002135 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002136 compact_pixels,AlphaQuantum,exception);
cristy4aff1572010-02-15 13:34:01 +00002137 }
cristya20214e2010-08-28 16:52:13 +00002138 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
2139 GrayQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
cristy018f07f2011-09-04 21:15:19 +00002140 MagickFalse,exception);
cristy8a46d822012-08-28 23:32:39 +00002141 if (next_image->alpha_trait == BlendPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002142 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
2143 AlphaQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
cristy018f07f2011-09-04 21:15:19 +00002144 MagickFalse,exception);
cristy6886a752010-04-23 18:23:20 +00002145 (void) SetImageProgress(image,SaveImagesTag,0,1);
cristy3ed852e2009-09-05 21:47:34 +00002146 }
cristy0910f242010-04-01 18:55:09 +00002147 else
cristya20214e2010-08-28 16:52:13 +00002148 if (next_image->storage_class == PseudoClass)
cristy0910f242010-04-01 18:55:09 +00002149 {
cristya20214e2010-08-28 16:52:13 +00002150 if (next_image->compression == RLECompression)
cristy0910f242010-04-01 18:55:09 +00002151 {
2152 /*
2153 Packbits compression.
2154 */
2155 (void) WriteBlobMSBShort(image,1);
cristya20214e2010-08-28 16:52:13 +00002156 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002157 compact_pixels,IndexQuantum,exception);
cristy8a46d822012-08-28 23:32:39 +00002158 if (next_image->alpha_trait == BlendPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002159 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002160 compact_pixels,AlphaQuantum,exception);
cristy0910f242010-04-01 18:55:09 +00002161 }
cristya20214e2010-08-28 16:52:13 +00002162 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
2163 IndexQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
cristy018f07f2011-09-04 21:15:19 +00002164 MagickFalse,exception);
cristy8a46d822012-08-28 23:32:39 +00002165 if (next_image->alpha_trait == BlendPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002166 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
2167 AlphaQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
cristy018f07f2011-09-04 21:15:19 +00002168 MagickFalse,exception);
cristy0910f242010-04-01 18:55:09 +00002169 (void) SetImageProgress(image,SaveImagesTag,0,1);
2170 }
2171 else
2172 {
cristya20214e2010-08-28 16:52:13 +00002173 if (next_image->colorspace == CMYKColorspace)
cristyc82a27b2011-10-21 01:07:16 +00002174 (void) NegateImage(next_image,MagickFalse,exception);
cristya20214e2010-08-28 16:52:13 +00002175 if (next_image->compression == RLECompression)
cristy0910f242010-04-01 18:55:09 +00002176 {
2177 /*
2178 Packbits compression.
2179 */
2180 (void) WriteBlobMSBShort(image,1);
cristya20214e2010-08-28 16:52:13 +00002181 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002182 compact_pixels,RedQuantum,exception);
cristya20214e2010-08-28 16:52:13 +00002183 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002184 compact_pixels,GreenQuantum,exception);
cristya20214e2010-08-28 16:52:13 +00002185 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002186 compact_pixels,BlueQuantum,exception);
cristya20214e2010-08-28 16:52:13 +00002187 if (next_image->colorspace == CMYKColorspace)
2188 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002189 compact_pixels,BlackQuantum,exception);
cristy8a46d822012-08-28 23:32:39 +00002190 if (next_image->alpha_trait == BlendPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002191 WritePackbitsLength(psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002192 compact_pixels,AlphaQuantum,exception);
cristy0910f242010-04-01 18:55:09 +00002193 }
2194 (void) SetImageProgress(image,SaveImagesTag,0,6);
cristya20214e2010-08-28 16:52:13 +00002195 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
2196 RedQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
cristy018f07f2011-09-04 21:15:19 +00002197 MagickFalse,exception);
cristy860cc732010-05-19 16:38:37 +00002198 (void) SetImageProgress(image,SaveImagesTag,1,6);
cristya20214e2010-08-28 16:52:13 +00002199 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
2200 GreenQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
cristy018f07f2011-09-04 21:15:19 +00002201 MagickFalse,exception);
cristy860cc732010-05-19 16:38:37 +00002202 (void) SetImageProgress(image,SaveImagesTag,2,6);
cristya20214e2010-08-28 16:52:13 +00002203 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
2204 BlueQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
cristy018f07f2011-09-04 21:15:19 +00002205 MagickFalse,exception);
cristy860cc732010-05-19 16:38:37 +00002206 (void) SetImageProgress(image,SaveImagesTag,3,6);
cristya20214e2010-08-28 16:52:13 +00002207 if (next_image->colorspace == CMYKColorspace)
2208 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
2209 BlackQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
cristy018f07f2011-09-04 21:15:19 +00002210 MagickFalse,exception);
cristy860cc732010-05-19 16:38:37 +00002211 (void) SetImageProgress(image,SaveImagesTag,4,6);
cristy8a46d822012-08-28 23:32:39 +00002212 if (next_image->alpha_trait == BlendPixelTrait)
cristya20214e2010-08-28 16:52:13 +00002213 WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
2214 AlphaQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
cristy018f07f2011-09-04 21:15:19 +00002215 MagickFalse,exception);
cristy0910f242010-04-01 18:55:09 +00002216 (void) SetImageProgress(image,SaveImagesTag,5,6);
cristya20214e2010-08-28 16:52:13 +00002217 if (next_image->colorspace == CMYKColorspace)
cristyc82a27b2011-10-21 01:07:16 +00002218 (void) NegateImage(next_image,MagickFalse,exception);
cristy0910f242010-04-01 18:55:09 +00002219 }
cristya20214e2010-08-28 16:52:13 +00002220 if (next_image->compression == RLECompression)
2221 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
cristy3ed852e2009-09-05 21:47:34 +00002222 return(MagickTrue);
2223}
2224
cristy3ed852e2009-09-05 21:47:34 +00002225static void WritePascalString(Image* inImage,const char *inString,int inPad)
2226{
2227 size_t
cristya20214e2010-08-28 16:52:13 +00002228 length;
cristy3ed852e2009-09-05 21:47:34 +00002229
cristya20214e2010-08-28 16:52:13 +00002230 register ssize_t
2231 i;
cristy3ed852e2009-09-05 21:47:34 +00002232
cristya20214e2010-08-28 16:52:13 +00002233 /*
2234 Max length is 255.
2235 */
2236 length=(strlen(inString) > 255UL ) ? 255UL : strlen(inString);
2237 if (length == 0)
2238 (void) WriteBlobByte(inImage,0);
cristy3ed852e2009-09-05 21:47:34 +00002239 else
cristya20214e2010-08-28 16:52:13 +00002240 {
2241 (void) WriteBlobByte(inImage,(unsigned char) length);
2242 (void) WriteBlob(inImage, length, (const unsigned char *) inString);
2243 }
2244 length++;
2245 if ((length % inPad) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002246 return;
cristya20214e2010-08-28 16:52:13 +00002247 for (i=0; i < (ssize_t) (inPad-(length % inPad)); i++)
cristy3ed852e2009-09-05 21:47:34 +00002248 (void) WriteBlobByte(inImage,0);
2249}
2250
2251static void WriteResolutionResourceBlock(Image *image)
2252{
cristy56ed31c2010-03-22 00:46:21 +00002253 double
2254 x_resolution,
2255 y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00002256
2257 unsigned short
2258 units;
2259
cristy2a11bef2011-10-28 18:33:11 +00002260 x_resolution=65536.0*image->resolution.x+0.5;
2261 y_resolution=65536.0*image->resolution.y+0.5;
cristy3ed852e2009-09-05 21:47:34 +00002262 units=1;
2263 if (image->units == PixelsPerCentimeterResolution)
2264 {
cristy2a11bef2011-10-28 18:33:11 +00002265 x_resolution=2.54*65536.0*image->resolution.x*0.5;
2266 y_resolution=2.54*65536.0*image->resolution.y+0.5;
cristy3ed852e2009-09-05 21:47:34 +00002267 units=2;
2268 }
2269 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
2270 (void) WriteBlobMSBShort(image,0x03ED);
2271 (void) WriteBlobMSBShort(image,0);
2272 (void) WriteBlobMSBLong(image,16); /* resource size */
cristy56ed31c2010-03-22 00:46:21 +00002273 (void) WriteBlobMSBLong(image,(unsigned int) (x_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002274 (void) WriteBlobMSBShort(image,units); /* horizontal resolution unit */
2275 (void) WriteBlobMSBShort(image,units); /* width unit */
cristy56ed31c2010-03-22 00:46:21 +00002276 (void) WriteBlobMSBLong(image,(unsigned int) (y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00002277 (void) WriteBlobMSBShort(image,units); /* vertical resolution unit */
2278 (void) WriteBlobMSBShort(image,units); /* height unit */
2279}
2280
cristyd4d3f742010-04-25 20:36:50 +00002281static void RemoveICCProfileFromResourceBlock(StringInfo *bim_profile)
2282{
cristy0b796e62010-04-29 00:38:45 +00002283 register const unsigned char
cristyd4d3f742010-04-25 20:36:50 +00002284 *p;
2285
2286 size_t
2287 length;
2288
2289 unsigned char
2290 *datum;
2291
cristy6befb0f2010-05-31 14:33:15 +00002292 unsigned int
cristyd4d3f742010-04-25 20:36:50 +00002293 count,
cristy6befb0f2010-05-31 14:33:15 +00002294 long_sans;
cristyd4d3f742010-04-25 20:36:50 +00002295
2296 unsigned short
2297 id,
2298 short_sans;
2299
2300 length=GetStringInfoLength(bim_profile);
2301 if (length < 16)
cristy0b796e62010-04-29 00:38:45 +00002302 return;
cristyd4d3f742010-04-25 20:36:50 +00002303 datum=GetStringInfoDatum(bim_profile);
2304 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
2305 {
cristy4176bb22010-05-01 16:29:09 +00002306 register unsigned char
2307 *q;
2308
2309 q=(unsigned char *) p;
cristyd4d3f742010-04-25 20:36:50 +00002310 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
2311 break;
cristy6befb0f2010-05-31 14:33:15 +00002312 p=PushLongPixel(MSBEndian,p,&long_sans);
cristyd4d3f742010-04-25 20:36:50 +00002313 p=PushShortPixel(MSBEndian,p,&id);
2314 p=PushShortPixel(MSBEndian,p,&short_sans);
2315 p=PushLongPixel(MSBEndian,p,&count);
2316 if (id == 0x0000040f)
2317 {
cristy4176bb22010-05-01 16:29:09 +00002318 (void) CopyMagickMemory(q,q+PSDQuantum(count)+12,length-
2319 (PSDQuantum(count)+12)-(q-datum));
2320 SetStringInfoLength(bim_profile,length-(PSDQuantum(count)+12));
cristyd4d3f742010-04-25 20:36:50 +00002321 break;
2322 }
2323 p+=count;
2324 if ((count & 0x01) != 0)
2325 p++;
2326 }
2327}
2328
cristyf11065e2010-05-14 13:26:59 +00002329static void RemoveResolutionFromResourceBlock(StringInfo *bim_profile)
2330{
2331 register const unsigned char
2332 *p;
2333
2334 size_t
2335 length;
2336
2337 unsigned char
2338 *datum;
2339
cristy6befb0f2010-05-31 14:33:15 +00002340 unsigned int
cristyf11065e2010-05-14 13:26:59 +00002341 count,
cristy6befb0f2010-05-31 14:33:15 +00002342 long_sans;
cristyf11065e2010-05-14 13:26:59 +00002343
2344 unsigned short
2345 id,
2346 short_sans;
2347
2348 length=GetStringInfoLength(bim_profile);
2349 if (length < 16)
2350 return;
2351 datum=GetStringInfoDatum(bim_profile);
2352 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
2353 {
2354 register unsigned char
2355 *q;
2356
2357 q=(unsigned char *) p;
2358 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
2359 break;
cristy6befb0f2010-05-31 14:33:15 +00002360 p=PushLongPixel(MSBEndian,p,&long_sans);
cristyf11065e2010-05-14 13:26:59 +00002361 p=PushShortPixel(MSBEndian,p,&id);
2362 p=PushShortPixel(MSBEndian,p,&short_sans);
2363 p=PushLongPixel(MSBEndian,p,&count);
cristy94b11832011-09-08 19:46:03 +00002364 if ((id == 0x000003ed) && (PSDQuantum(count) < (ssize_t) (length-12)))
cristyf11065e2010-05-14 13:26:59 +00002365 {
2366 (void) CopyMagickMemory(q,q+PSDQuantum(count)+12,length-
2367 (PSDQuantum(count)+12)-(q-datum));
2368 SetStringInfoLength(bim_profile,length-(PSDQuantum(count)+12));
2369 break;
2370 }
2371 p+=count;
2372 if ((count & 0x01) != 0)
2373 p++;
2374 }
2375}
2376
cristy3a37efd2011-08-28 20:31:03 +00002377static MagickBooleanType WritePSDImage(const ImageInfo *image_info,Image *image,
2378 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002379{
2380 const char
cristya20214e2010-08-28 16:52:13 +00002381 *property;
cristy3ed852e2009-09-05 21:47:34 +00002382
2383 const StringInfo
cristy749d2152010-04-04 23:47:33 +00002384 *icc_profile;
cristy3ed852e2009-09-05 21:47:34 +00002385
cristya20214e2010-08-28 16:52:13 +00002386 Image
2387 *base_image,
2388 *next_image;
2389
cristy3ed852e2009-09-05 21:47:34 +00002390 MagickBooleanType
cristy3ed852e2009-09-05 21:47:34 +00002391 status;
2392
cristy875e28a2010-03-06 19:46:55 +00002393 PSDInfo
2394 psd_info;
2395
cristybb503372010-05-27 20:51:26 +00002396 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002397 i;
2398
2399 size_t
cristya20214e2010-08-28 16:52:13 +00002400 channel_size,
2401 channelLength,
2402 layer_count,
2403 layer_info_size,
cristy749d2152010-04-04 23:47:33 +00002404 length,
cristy3ed852e2009-09-05 21:47:34 +00002405 num_channels,
cristya20214e2010-08-28 16:52:13 +00002406 packet_size,
2407 rounded_layer_info_size;
cristy3ed852e2009-09-05 21:47:34 +00002408
cristy0b796e62010-04-29 00:38:45 +00002409 StringInfo
2410 *bim_profile;
2411
cristy3ed852e2009-09-05 21:47:34 +00002412 /*
cristy56ed31c2010-03-22 00:46:21 +00002413 Open image file.
cristy3ed852e2009-09-05 21:47:34 +00002414 */
2415 assert(image_info != (const ImageInfo *) NULL);
2416 assert(image_info->signature == MagickSignature);
2417 assert(image != (Image *) NULL);
2418 assert(image->signature == MagickSignature);
2419 if (image->debug != MagickFalse)
2420 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00002421 assert(exception != (ExceptionInfo *) NULL);
2422 assert(exception->signature == MagickSignature);
2423 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00002424 if (status == MagickFalse)
2425 return(status);
2426 packet_size=(size_t) (image->depth > 8 ? 6 : 3);
cristy8a46d822012-08-28 23:32:39 +00002427 if (image->alpha_trait == BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +00002428 packet_size+=image->depth > 8 ? 2 : 1;
cristy875e28a2010-03-06 19:46:55 +00002429 psd_info.version=1;
2430 if ((LocaleCompare(image_info->magick,"PSB") == 0) ||
2431 (image->columns > 30000) || (image->rows > 30000))
2432 psd_info.version=2;
cristy50aea4a2010-03-09 17:37:44 +00002433 (void) WriteBlob(image,4,(const unsigned char *) "8BPS");
cristy875e28a2010-03-06 19:46:55 +00002434 (void) WriteBlobMSBShort(image,psd_info.version); /* version */
2435 for (i=1; i <= 6; i++)
2436 (void) WriteBlobByte(image, 0); /* 6 bytes of reserved */
cristy3a37efd2011-08-28 20:31:03 +00002437 if (IsImageGray(image,exception) != MagickFalse)
cristy8a46d822012-08-28 23:32:39 +00002438 num_channels=(image->alpha_trait == BlendPixelTrait ? 2UL : 1UL);
cristy3ed852e2009-09-05 21:47:34 +00002439 else
cristy0910f242010-04-01 18:55:09 +00002440 if (image->storage_class == PseudoClass)
cristy8a46d822012-08-28 23:32:39 +00002441 num_channels=(image->alpha_trait == BlendPixelTrait ? 2UL : 1UL);
cristy0910f242010-04-01 18:55:09 +00002442 else
2443 {
2444 if (image->colorspace != CMYKColorspace)
cristy8a46d822012-08-28 23:32:39 +00002445 num_channels=(image->alpha_trait == BlendPixelTrait ? 4UL : 3UL);
cristy0910f242010-04-01 18:55:09 +00002446 else
cristy8a46d822012-08-28 23:32:39 +00002447 num_channels=(image->alpha_trait == BlendPixelTrait ? 5UL : 4UL);
cristy0910f242010-04-01 18:55:09 +00002448 }
cristy3ed852e2009-09-05 21:47:34 +00002449 (void) WriteBlobMSBShort(image,(unsigned short) num_channels);
cristy56ed31c2010-03-22 00:46:21 +00002450 (void) WriteBlobMSBLong(image,(unsigned int) image->rows);
2451 (void) WriteBlobMSBLong(image,(unsigned int) image->columns);
cristy3a37efd2011-08-28 20:31:03 +00002452 if (IsImageGray(image,exception) != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002453 {
cristy2045da32010-04-16 00:59:35 +00002454 MagickBooleanType
2455 monochrome;
2456
cristy0910f242010-04-01 18:55:09 +00002457 /*
2458 Write depth & mode.
2459 */
cristy3a37efd2011-08-28 20:31:03 +00002460 monochrome=IsImageMonochrome(image,exception) && (image->depth == 1) ?
2461 MagickTrue : MagickFalse;
cristy284c7d82010-04-24 00:19:14 +00002462 (void) WriteBlobMSBShort(image,(unsigned short)
2463 (monochrome != MagickFalse ? 1 : image->depth > 8 ? 16 : 8));
cristy2b9582a2011-07-04 17:38:56 +00002464 (void) WriteBlobMSBShort(image,(unsigned short)
2465 (monochrome != MagickFalse ? BitmapMode : GrayscaleMode));
cristy3ed852e2009-09-05 21:47:34 +00002466 }
2467 else
2468 {
cristya20214e2010-08-28 16:52:13 +00002469 (void) WriteBlobMSBShort(image,(unsigned short) (image->storage_class ==
2470 PseudoClass ? 8 : image->depth > 8 ? 16 : 8));
cristy48845392010-06-02 01:19:17 +00002471 if (((image_info->colorspace != UndefinedColorspace) ||
cristy0910f242010-04-01 18:55:09 +00002472 (image->colorspace != CMYKColorspace)) &&
cristy48845392010-06-02 01:19:17 +00002473 (image_info->colorspace != CMYKColorspace))
cristy0910f242010-04-01 18:55:09 +00002474 {
cristy3d9f5ba2012-06-26 13:37:31 +00002475 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
cristy8d951092012-02-08 18:54:56 +00002476 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy0910f242010-04-01 18:55:09 +00002477 (void) WriteBlobMSBShort(image,(unsigned short)
cristy2045da32010-04-16 00:59:35 +00002478 (image->storage_class == PseudoClass ? IndexedMode : RGBMode));
cristy0910f242010-04-01 18:55:09 +00002479 }
2480 else
2481 {
cristy48845392010-06-02 01:19:17 +00002482 if (image->colorspace != CMYKColorspace)
cristye941a752011-10-15 01:52:48 +00002483 (void) TransformImageColorspace(image,CMYKColorspace,exception);
cristy2045da32010-04-16 00:59:35 +00002484 (void) WriteBlobMSBShort(image,CMYKMode);
cristy0910f242010-04-01 18:55:09 +00002485 }
cristy3ed852e2009-09-05 21:47:34 +00002486 }
cristy3a37efd2011-08-28 20:31:03 +00002487 if ((IsImageGray(image,exception) != MagickFalse) ||
cristyca9ddb02010-04-16 01:01:18 +00002488 (image->storage_class == DirectClass) || (image->colors > 256))
cristy3ed852e2009-09-05 21:47:34 +00002489 (void) WriteBlobMSBLong(image,0);
2490 else
2491 {
2492 /*
2493 Write PSD raster colormap.
2494 */
2495 (void) WriteBlobMSBLong(image,768);
cristybb503372010-05-27 20:51:26 +00002496 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002497 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].red));
2498 for ( ; i < 256; i++)
2499 (void) WriteBlobByte(image,0);
cristybb503372010-05-27 20:51:26 +00002500 for (i=0; i < (ssize_t) image->colors; i++)
cristya20214e2010-08-28 16:52:13 +00002501 (void) WriteBlobByte(image,ScaleQuantumToChar(
2502 image->colormap[i].green));
cristy3ed852e2009-09-05 21:47:34 +00002503 for ( ; i < 256; i++)
2504 (void) WriteBlobByte(image,0);
cristybb503372010-05-27 20:51:26 +00002505 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002506 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].blue));
2507 for ( ; i < 256; i++)
2508 (void) WriteBlobByte(image,0);
2509 }
2510 /*
2511 Image resource block.
2512 */
cristy749d2152010-04-04 23:47:33 +00002513 length=28; /* 0x03EB */
cristy0b796e62010-04-29 00:38:45 +00002514 bim_profile=(StringInfo *) GetImageProfile(image,"8bim");
cristy749d2152010-04-04 23:47:33 +00002515 icc_profile=GetImageProfile(image,"icc");
cristyd4d3f742010-04-25 20:36:50 +00002516 if (bim_profile != (StringInfo *) NULL)
2517 {
cristy0b796e62010-04-29 00:38:45 +00002518 bim_profile=CloneStringInfo(bim_profile);
cristyd4d3f742010-04-25 20:36:50 +00002519 if (icc_profile != (StringInfo *) NULL)
2520 RemoveICCProfileFromResourceBlock(bim_profile);
cristyf11065e2010-05-14 13:26:59 +00002521 RemoveResolutionFromResourceBlock(bim_profile);
cristyd4d3f742010-04-25 20:36:50 +00002522 length+=PSDQuantum(GetStringInfoLength(bim_profile));
2523 }
cristy0b796e62010-04-29 00:38:45 +00002524 if (icc_profile != (const StringInfo *) NULL)
cristy749d2152010-04-04 23:47:33 +00002525 length+=PSDQuantum(GetStringInfoLength(icc_profile))+12;
cristy284c7d82010-04-24 00:19:14 +00002526 (void) WriteBlobMSBLong(image,(unsigned int) length);
cristyf11065e2010-05-14 13:26:59 +00002527 WriteResolutionResourceBlock(image);
cristy4176bb22010-05-01 16:29:09 +00002528 if (bim_profile != (StringInfo *) NULL)
2529 {
2530 (void) WriteBlob(image,GetStringInfoLength(bim_profile),
2531 GetStringInfoDatum(bim_profile));
2532 bim_profile=DestroyStringInfo(bim_profile);
2533 }
cristy749d2152010-04-04 23:47:33 +00002534 if (icc_profile != (StringInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002535 {
cristy749d2152010-04-04 23:47:33 +00002536 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
cristy4176bb22010-05-01 16:29:09 +00002537 (void) WriteBlobMSBShort(image,0x0000040F);
cristy749d2152010-04-04 23:47:33 +00002538 (void) WriteBlobMSBShort(image,0);
cristy284c7d82010-04-24 00:19:14 +00002539 (void) WriteBlobMSBLong(image,(unsigned int) GetStringInfoLength(
2540 icc_profile));
cristy749d2152010-04-04 23:47:33 +00002541 (void) WriteBlob(image,GetStringInfoLength(icc_profile),
2542 GetStringInfoDatum(icc_profile));
cristye195f262010-04-16 18:12:35 +00002543 if ((MagickOffsetType) GetStringInfoLength(icc_profile) !=
cristy2045da32010-04-16 00:59:35 +00002544 PSDQuantum(GetStringInfoLength(icc_profile)))
cristy749d2152010-04-04 23:47:33 +00002545 (void) WriteBlobByte(image,0);
cristye6365592010-04-02 17:31:23 +00002546 }
cristy48845392010-06-02 01:19:17 +00002547 layer_count=0;
2548 layer_info_size=2;
cristy2837bcc2010-08-07 23:57:39 +00002549 base_image=GetNextImageInList(image);
cristy8a46d822012-08-28 23:32:39 +00002550 if ((image->alpha_trait == BlendPixelTrait) && (base_image == (Image *) NULL))
cristy2837bcc2010-08-07 23:57:39 +00002551 base_image=image;
cristya20214e2010-08-28 16:52:13 +00002552 next_image=base_image;
2553 while ( next_image != NULL )
2554 {
2555 packet_size=next_image->depth > 8 ? 2UL : 1UL;
cristy3a37efd2011-08-28 20:31:03 +00002556 if (IsImageGray(next_image,exception) != MagickFalse)
cristy8a46d822012-08-28 23:32:39 +00002557 num_channels=next_image->alpha_trait == BlendPixelTrait ? 2UL : 1UL;
cristy3ed852e2009-09-05 21:47:34 +00002558 else
cristya20214e2010-08-28 16:52:13 +00002559 if (next_image->storage_class == PseudoClass)
cristy8a46d822012-08-28 23:32:39 +00002560 num_channels=next_image->alpha_trait == BlendPixelTrait ? 2UL : 1UL;
cristy2045da32010-04-16 00:59:35 +00002561 else
cristya20214e2010-08-28 16:52:13 +00002562 if (next_image->colorspace != CMYKColorspace)
cristy8a46d822012-08-28 23:32:39 +00002563 num_channels=next_image->alpha_trait == BlendPixelTrait ? 4UL : 3UL;
cristy2045da32010-04-16 00:59:35 +00002564 else
cristy8a46d822012-08-28 23:32:39 +00002565 num_channels=next_image->alpha_trait == BlendPixelTrait ? 5UL : 4UL;
cristya20214e2010-08-28 16:52:13 +00002566 channelLength=(size_t) (next_image->columns*next_image->rows*packet_size+2);
cristy48845392010-06-02 01:19:17 +00002567 layer_info_size+=(size_t) (4*4+2+num_channels*6+(psd_info.version == 1 ? 8 :
2568 16)+4*1+4+num_channels*channelLength);
cristyd15e6592011-10-15 00:13:06 +00002569 property=(const char *) GetImageProperty(next_image,"label",exception);
cristya20214e2010-08-28 16:52:13 +00002570 if (property == (const char *) NULL)
2571 layer_info_size+=16;
cristyde9b8f52010-03-12 01:17:00 +00002572 else
2573 {
cristya20214e2010-08-28 16:52:13 +00002574 size_t
2575 length;
2576
2577 length=strlen(property);
2578 layer_info_size+=8+length+(4-(length % 4));
cristyde9b8f52010-03-12 01:17:00 +00002579 }
cristy4aff1572010-02-15 13:34:01 +00002580 layer_count++;
cristya20214e2010-08-28 16:52:13 +00002581 next_image=GetNextImageInList(next_image);
cristy3ed852e2009-09-05 21:47:34 +00002582 }
cristy144f1b62010-05-18 00:52:09 +00002583 if (layer_count == 0)
cristy875e28a2010-03-06 19:46:55 +00002584 (void) SetPSDSize(&psd_info,image,0);
cristy3ed852e2009-09-05 21:47:34 +00002585 else
cristya20214e2010-08-28 16:52:13 +00002586 {
cristy8e9bd3e2010-08-29 00:03:56 +00002587 CompressionType
2588 compression;
2589
2590 (void) SetPSDSize(&psd_info,image,layer_info_size+
2591 (psd_info.version == 1 ? 8 : 16));
2592 if ((layer_info_size/2) != ((layer_info_size+1)/2))
2593 rounded_layer_info_size=layer_info_size+1;
cristya20214e2010-08-28 16:52:13 +00002594 else
cristy8e9bd3e2010-08-29 00:03:56 +00002595 rounded_layer_info_size=layer_info_size;
2596 (void) SetPSDSize(&psd_info,image,rounded_layer_info_size);
2597 (void) WriteBlobMSBShort(image,(unsigned short) layer_count);
2598 layer_count=1;
2599 compression=base_image->compression;
2600 next_image=base_image;
2601 while (next_image != NULL)
2602 {
2603 next_image->compression=NoCompression;
cristya4e5f472011-11-09 00:42:46 +00002604 (void) WriteBlobMSBLong(image,(unsigned int) next_image->page.y);
2605 (void) WriteBlobMSBLong(image,(unsigned int) next_image->page.x);
2606 (void) WriteBlobMSBLong(image,(unsigned int) next_image->page.y+
2607 next_image->rows);
2608 (void) WriteBlobMSBLong(image,(unsigned int) next_image->page.x+
2609 next_image->columns);
cristy8e9bd3e2010-08-29 00:03:56 +00002610 packet_size=next_image->depth > 8 ? 2UL : 1UL;
2611 channel_size=(unsigned int) ((packet_size*next_image->rows*
2612 next_image->columns)+2);
cristy3a37efd2011-08-28 20:31:03 +00002613 if ((IsImageGray(next_image,exception) != MagickFalse) ||
cristy8e9bd3e2010-08-29 00:03:56 +00002614 (next_image->storage_class == PseudoClass))
2615 {
2616 (void) WriteBlobMSBShort(image,(unsigned short)
cristy8a46d822012-08-28 23:32:39 +00002617 (next_image->alpha_trait == BlendPixelTrait ? 2 : 1));
cristy8e9bd3e2010-08-29 00:03:56 +00002618 (void) WriteBlobMSBShort(image,0);
2619 (void) SetPSDSize(&psd_info,image,channel_size);
cristy8a46d822012-08-28 23:32:39 +00002620 if (next_image->alpha_trait == BlendPixelTrait)
cristy8e9bd3e2010-08-29 00:03:56 +00002621 {
2622 (void) WriteBlobMSBShort(image,(unsigned short) -1);
2623 (void) SetPSDSize(&psd_info,image,channel_size);
2624 }
2625 }
2626 else
2627 if (next_image->colorspace != CMYKColorspace)
2628 {
2629 (void) WriteBlobMSBShort(image,(unsigned short)
cristy8a46d822012-08-28 23:32:39 +00002630 (next_image->alpha_trait == BlendPixelTrait ? 4 : 3));
cristy8e9bd3e2010-08-29 00:03:56 +00002631 (void) WriteBlobMSBShort(image,0);
2632 (void) SetPSDSize(&psd_info,image,channel_size);
2633 (void) WriteBlobMSBShort(image,1);
2634 (void) SetPSDSize(&psd_info,image,channel_size);
2635 (void) WriteBlobMSBShort(image,2);
2636 (void) SetPSDSize(&psd_info,image,channel_size);
cristy8a46d822012-08-28 23:32:39 +00002637 if (next_image->alpha_trait == BlendPixelTrait)
cristy8e9bd3e2010-08-29 00:03:56 +00002638 {
2639 (void) WriteBlobMSBShort(image,(unsigned short) -1);
2640 (void) SetPSDSize(&psd_info,image,channel_size);
2641 }
2642 }
2643 else
2644 {
2645 (void) WriteBlobMSBShort(image,(unsigned short)
cristy8a46d822012-08-28 23:32:39 +00002646 (next_image->alpha_trait ? 5 : 4));
cristy8e9bd3e2010-08-29 00:03:56 +00002647 (void) WriteBlobMSBShort(image,0);
2648 (void) SetPSDSize(&psd_info,image,channel_size);
2649 (void) WriteBlobMSBShort(image,1);
2650 (void) SetPSDSize(&psd_info,image,channel_size);
2651 (void) WriteBlobMSBShort(image,2);
2652 (void) SetPSDSize(&psd_info,image,channel_size);
2653 (void) WriteBlobMSBShort(image,3);
2654 (void) SetPSDSize(&psd_info,image,channel_size);
cristy8a46d822012-08-28 23:32:39 +00002655 if (next_image->alpha_trait)
cristy8e9bd3e2010-08-29 00:03:56 +00002656 {
2657 (void) WriteBlobMSBShort(image,(unsigned short) -1);
2658 (void) SetPSDSize(&psd_info,image,channel_size);
2659 }
2660 }
2661 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
2662 (void) WriteBlob(image,4,(const unsigned char *)
2663 CompositeOperatorToPSDBlendMode(next_image->compose));
2664 (void) WriteBlobByte(image,255); /* layer opacity */
2665 (void) WriteBlobByte(image,0);
2666 (void) WriteBlobByte(image,1); /* layer propertys - visible, etc. */
2667 (void) WriteBlobByte(image,0);
cristyd15e6592011-10-15 00:13:06 +00002668 property=(const char *) GetImageProperty(next_image,"label",exception);
cristy8e9bd3e2010-08-29 00:03:56 +00002669 if (property == (const char *) NULL)
2670 {
dirk93b02b72013-11-16 16:03:36 +00002671 char
cristy7a1ce132013-11-14 12:38:14 +00002672 layer_name[MaxTextExtent];
2673
cristy8e9bd3e2010-08-29 00:03:56 +00002674 (void) WriteBlobMSBLong(image,16);
2675 (void) WriteBlobMSBLong(image,0);
2676 (void) WriteBlobMSBLong(image,0);
cristy7a1ce132013-11-14 12:38:14 +00002677 (void) FormatLocaleString(layer_name,MaxTextExtent,"L%06ld",(long)
2678 layer_count++);
2679 WritePascalString(image,layer_name,4);
cristy8e9bd3e2010-08-29 00:03:56 +00002680 }
2681 else
2682 {
2683 size_t
2684 length;
cristy3ed852e2009-09-05 21:47:34 +00002685
cristy8e9bd3e2010-08-29 00:03:56 +00002686 length=strlen(property);
2687 (void) WriteBlobMSBLong(image,(unsigned int) (length+(4-
2688 (length % 4))+8));
2689 (void) WriteBlobMSBLong(image,0);
2690 (void) WriteBlobMSBLong(image,0);
2691 WritePascalString(image,property,4);
2692 }
2693 next_image=GetNextImageInList(next_image);
2694 }
2695 /*
2696 Now the image data!
2697 */
2698 next_image=base_image;
2699 while (next_image != NULL)
2700 {
2701 status=WriteImageChannels(&psd_info,image_info,image,next_image,
cristy018f07f2011-09-04 21:15:19 +00002702 MagickTrue,exception);
cristy8e9bd3e2010-08-29 00:03:56 +00002703 next_image=GetNextImageInList(next_image);
2704 }
2705 (void) WriteBlobMSBLong(image,0); /* user mask data */
2706 base_image->compression=compression;
cristy144f1b62010-05-18 00:52:09 +00002707 }
cristy144f1b62010-05-18 00:52:09 +00002708 /*
2709 Write composite image.
2710 */
cristy018f07f2011-09-04 21:15:19 +00002711 status=WriteImageChannels(&psd_info,image_info,image,image,MagickFalse,
2712 exception);
cristy3ed852e2009-09-05 21:47:34 +00002713 (void) CloseBlob(image);
2714 return(status);
2715}