]> granicus.if.org Git - imagemagick/blob - coders/psd.c
https://github.com/ImageMagick/ImageMagick/issues/832
[imagemagick] / coders / psd.c
1 /*
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                                %
16 %                                   Cristy                                    %
17 %                              Leonard Rosenthol                              %
18 %                                 July 1992                                   %
19 %                                Dirk Lemstra                                 %
20 %                                December 2013                                %
21 %                                                                             %
22 %                                                                             %
23 %  Copyright 1999-2017 ImageMagick Studio LLC, a non-profit organization      %
24 %  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 %    https://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 \f
42 /*
43   Include declarations.
44 */
45 #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/channel.h"
52 #include "MagickCore/colormap.h"
53 #include "MagickCore/colormap-private.h"
54 #include "MagickCore/colorspace.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/image.h"
61 #include "MagickCore/image-private.h"
62 #include "MagickCore/list.h"
63 #include "MagickCore/log.h"
64 #include "MagickCore/magick.h"
65 #include "MagickCore/memory_.h"
66 #include "MagickCore/module.h"
67 #include "MagickCore/monitor-private.h"
68 #include "MagickCore/option.h"
69 #include "MagickCore/pixel.h"
70 #include "MagickCore/pixel-accessor.h"
71 #include "MagickCore/policy.h"
72 #include "MagickCore/profile.h"
73 #include "MagickCore/property.h"
74 #include "MagickCore/registry.h"
75 #include "MagickCore/quantum-private.h"
76 #include "MagickCore/static.h"
77 #include "MagickCore/string_.h"
78 #include "MagickCore/string-private.h"
79 #include "MagickCore/thread-private.h"
80 #ifdef MAGICKCORE_ZLIB_DELEGATE
81 #include <zlib.h>
82 #endif
83 #include "psd-private.h"
84
85 /*
86   Define declaractions.
87 */
88 #define MaxPSDChannels  56
89 #define PSDQuantum(x) (((ssize_t) (x)+1) & -2)
90 \f
91 /*
92   Enumerated declaractions.
93 */
94 typedef enum
95 {
96   Raw = 0,
97   RLE = 1,
98   ZipWithoutPrediction = 2,
99   ZipWithPrediction = 3
100 } PSDCompressionType;
101
102 typedef enum
103 {
104   BitmapMode = 0,
105   GrayscaleMode = 1,
106   IndexedMode = 2,
107   RGBMode = 3,
108   CMYKMode = 4,
109   MultichannelMode = 7,
110   DuotoneMode = 8,
111   LabMode = 9
112 } PSDImageType;
113 \f
114 /*
115   Typedef declaractions.
116 */
117 typedef struct _ChannelInfo
118 {
119   short int
120     type;
121
122   size_t
123     size;
124 } ChannelInfo;
125
126 typedef struct _MaskInfo
127 {
128   Image
129     *image;
130
131   RectangleInfo
132     page;
133
134   unsigned char
135     background,
136     flags;
137 } MaskInfo;
138
139 typedef struct _LayerInfo
140 {
141   ChannelInfo
142     channel_info[MaxPSDChannels];
143
144   char
145     blendkey[4];
146
147   Image
148     *image;
149
150   MaskInfo
151     mask;
152
153   Quantum
154     opacity;
155
156   RectangleInfo
157     page;
158
159   size_t
160     offset_x,
161     offset_y;
162
163   unsigned char
164     clipping,
165     flags,
166     name[256],
167     visible;
168
169   unsigned short
170     channels;
171
172   StringInfo
173     *info;
174 } LayerInfo;
175
176 /*
177   Forward declarations.
178 */
179 static MagickBooleanType
180   WritePSDImage(const ImageInfo *,Image *,ExceptionInfo *);
181 \f
182 /*
183 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
184 %                                                                             %
185 %                                                                             %
186 %                                                                             %
187 %   I s P S D                                                                 %
188 %                                                                             %
189 %                                                                             %
190 %                                                                             %
191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
192 %
193 %  IsPSD()() returns MagickTrue if the image format type, identified by the
194 %  magick string, is PSD.
195 %
196 %  The format of the IsPSD method is:
197 %
198 %      MagickBooleanType IsPSD(const unsigned char *magick,const size_t length)
199 %
200 %  A description of each parameter follows:
201 %
202 %    o magick: compare image format pattern against these bytes.
203 %
204 %    o length: Specifies the length of the magick string.
205 %
206 */
207 static MagickBooleanType IsPSD(const unsigned char *magick,const size_t length)
208 {
209   if (length < 4)
210     return(MagickFalse);
211   if (LocaleNCompare((const char *) magick,"8BPS",4) == 0)
212     return(MagickTrue);
213   return(MagickFalse);
214 }
215 \f
216 /*
217 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
218 %                                                                             %
219 %                                                                             %
220 %                                                                             %
221 %   R e a d P S D I m a g e                                                   %
222 %                                                                             %
223 %                                                                             %
224 %                                                                             %
225 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
226 %
227 %  ReadPSDImage() reads an Adobe Photoshop image file and returns it.  It
228 %  allocates the memory necessary for the new Image structure and returns a
229 %  pointer to the new image.
230 %
231 %  The format of the ReadPSDImage method is:
232 %
233 %      Image *ReadPSDImage(image_info,ExceptionInfo *exception)
234 %
235 %  A description of each parameter follows:
236 %
237 %    o image_info: the image info.
238 %
239 %    o exception: return any errors or warnings in this structure.
240 %
241 */
242
243 static const char *CompositeOperatorToPSDBlendMode(Image *image)
244 {
245   switch (image->compose)
246   {
247     case ColorBurnCompositeOp:
248       return(image->endian == LSBEndian ? "vidi" : "idiv");
249     case ColorDodgeCompositeOp:
250       return(image->endian == LSBEndian ? " vid" : "div ");
251     case ColorizeCompositeOp:
252       return(image->endian == LSBEndian ? "rloc" : "colr");
253     case DarkenCompositeOp:
254       return(image->endian == LSBEndian ? "krad" : "dark");
255     case DifferenceCompositeOp:
256       return(image->endian == LSBEndian ? "ffid" : "diff");
257     case DissolveCompositeOp:
258       return(image->endian == LSBEndian ? "ssid" : "diss");
259     case ExclusionCompositeOp:
260       return(image->endian == LSBEndian ? "dums" : "smud");
261     case HardLightCompositeOp:
262       return(image->endian == LSBEndian ? "tiLh" : "hLit");
263     case HardMixCompositeOp:
264       return(image->endian == LSBEndian ? "xiMh" : "hMix");
265     case HueCompositeOp:
266       return(image->endian == LSBEndian ? " euh" : "hue ");
267     case LightenCompositeOp:
268       return(image->endian == LSBEndian ? "etil" : "lite");
269     case LinearBurnCompositeOp:
270       return(image->endian == LSBEndian ? "nrbl" : "lbrn");
271     case LinearDodgeCompositeOp:
272       return(image->endian == LSBEndian ? "gddl" : "lddg");
273     case LinearLightCompositeOp:
274       return(image->endian == LSBEndian ? "tiLl" : "lLit");
275     case LuminizeCompositeOp:
276       return(image->endian == LSBEndian ? " mul" : "lum ");
277     case MultiplyCompositeOp:
278       return(image->endian == LSBEndian ? " lum" : "mul ");
279     case OverlayCompositeOp:
280       return(image->endian == LSBEndian ? "revo" : "over");
281     case PinLightCompositeOp:
282       return(image->endian == LSBEndian ? "tiLp" : "pLit");
283     case SaturateCompositeOp:
284       return(image->endian == LSBEndian ? " tas" : "sat ");
285     case ScreenCompositeOp:
286       return(image->endian == LSBEndian ? "nrcs" : "scrn");
287     case SoftLightCompositeOp:
288       return(image->endian == LSBEndian ? "tiLs" : "sLit");
289     case VividLightCompositeOp:
290       return(image->endian == LSBEndian ? "tiLv" : "vLit");
291     case OverCompositeOp:
292     default:
293       return(image->endian == LSBEndian ? "mron" : "norm");
294   }
295 }
296
297 /*
298   For some reason Photoshop seems to blend semi-transparent pixels with white.
299   This method reverts the blending. This can be disabled by setting the
300   option 'psd:alpha-unblend' to off.
301 */
302 static MagickBooleanType CorrectPSDAlphaBlend(const ImageInfo *image_info,
303   Image *image,ExceptionInfo* exception)
304 {
305   const char
306     *option;
307
308   MagickBooleanType
309     status;
310
311   ssize_t
312     y;
313
314   if (image->alpha_trait != BlendPixelTrait || image->colorspace != sRGBColorspace)
315     return(MagickTrue);
316   option=GetImageOption(image_info,"psd:alpha-unblend");
317   if (IsStringFalse(option) != MagickFalse)
318     return(MagickTrue);
319   status=MagickTrue;
320 #if defined(MAGICKCORE_OPENMP_SUPPORT)
321 #pragma omp parallel for schedule(static,4) shared(status) \
322   magick_number_threads(image,image,image->rows,1)
323 #endif
324   for (y=0; y < (ssize_t) image->rows; y++)
325   {
326     register Quantum
327       *magick_restrict q;
328
329     register ssize_t
330       x;
331
332     if (status == MagickFalse)
333       continue;
334     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
335     if (q == (Quantum *) NULL)
336     {
337       status=MagickFalse;
338       continue;
339     }
340     for (x=0; x < (ssize_t) image->columns; x++)
341     {
342       double
343         gamma;
344
345       register ssize_t
346         i;
347
348       gamma=QuantumScale*GetPixelAlpha(image, q);
349       if (gamma != 0.0 && gamma != 1.0)
350         {
351           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
352           {
353             PixelChannel channel = GetPixelChannelChannel(image,i);
354             if (channel != AlphaPixelChannel)
355               q[i]=ClampToQuantum((q[i]-((1.0-gamma)*QuantumRange))/gamma);
356           }
357         }
358       q+=GetPixelChannels(image);
359     }
360     if (SyncAuthenticPixels(image,exception) == MagickFalse)
361       status=MagickFalse;
362   }
363
364   return(status);
365 }
366
367 static inline CompressionType ConvertPSDCompression(
368   PSDCompressionType compression)
369 {
370   switch (compression)
371   {
372     case RLE:
373       return RLECompression;
374     case ZipWithPrediction:
375     case ZipWithoutPrediction:
376       return ZipCompression;
377     default:
378       return NoCompression;
379   }
380 }
381
382 static MagickBooleanType ApplyPSDLayerOpacity(Image *image,Quantum opacity,
383   MagickBooleanType revert,ExceptionInfo *exception)
384 {
385   MagickBooleanType
386     status;
387
388   ssize_t
389     y;
390
391   if (image->debug != MagickFalse)
392     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
393       "  applying layer opacity %.20g", (double) opacity);
394   if (opacity == OpaqueAlpha)
395     return(MagickTrue);
396   image->alpha_trait=BlendPixelTrait;
397   status=MagickTrue;
398 #if defined(MAGICKCORE_OPENMP_SUPPORT)
399 #pragma omp parallel for schedule(static,4) shared(status) \
400   magick_number_threads(image,image,image->rows,1)
401 #endif
402   for (y=0; y < (ssize_t) image->rows; y++)
403   {
404     register Quantum
405       *magick_restrict q;
406
407     register ssize_t
408       x;
409
410     if (status == MagickFalse)
411       continue;
412     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
413     if (q == (Quantum *) NULL)
414       {
415         status=MagickFalse;
416         continue;
417       }
418     for (x=0; x < (ssize_t) image->columns; x++)
419     {
420       if (revert == MagickFalse)
421         SetPixelAlpha(image,(Quantum) (QuantumScale*(GetPixelAlpha(image,q))*
422           opacity),q);
423       else if (opacity > 0)
424         SetPixelAlpha(image,(Quantum) (QuantumRange*(GetPixelAlpha(image,q)/
425           (MagickRealType) opacity)),q);
426       q+=GetPixelChannels(image);
427     }
428     if (SyncAuthenticPixels(image,exception) == MagickFalse)
429       status=MagickFalse;
430   }
431
432   return(status);
433 }
434
435 static MagickBooleanType ApplyPSDOpacityMask(Image *image,const Image *mask,
436   Quantum background,MagickBooleanType revert,ExceptionInfo *exception)
437 {
438   Image
439     *complete_mask;
440
441   MagickBooleanType
442     status;
443
444   PixelInfo
445     color;
446
447   ssize_t
448     y;
449
450   if (image->debug != MagickFalse)
451     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
452       "  applying opacity mask");
453   complete_mask=CloneImage(image,image->columns,image->rows,MagickTrue,
454     exception);
455   if (complete_mask == (Image *) NULL)
456     return(MagickFalse);
457   complete_mask->alpha_trait=BlendPixelTrait;
458   GetPixelInfo(complete_mask,&color);
459   color.red=background;
460   SetImageColor(complete_mask,&color,exception);
461   status=CompositeImage(complete_mask,mask,OverCompositeOp,MagickTrue,
462     mask->page.x-image->page.x,mask->page.y-image->page.y,exception);
463   if (status == MagickFalse)
464     {
465       complete_mask=DestroyImage(complete_mask);
466       return(status);
467     }
468   image->alpha_trait=BlendPixelTrait;
469 #if defined(MAGICKCORE_OPENMP_SUPPORT)
470 #pragma omp parallel for schedule(static,4) shared(status) \
471   magick_number_threads(image,image,image->rows,1)
472 #endif
473   for (y=0; y < (ssize_t) image->rows; y++)
474   {
475     register Quantum
476       *magick_restrict q;
477
478     register Quantum
479       *p;
480
481     register ssize_t
482       x;
483
484     if (status == MagickFalse)
485       continue;
486     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
487     p=GetAuthenticPixels(complete_mask,0,y,complete_mask->columns,1,exception);
488     if ((q == (Quantum *) NULL) || (p == (Quantum *) NULL))
489       {
490         status=MagickFalse;
491         continue;
492       }
493     for (x=0; x < (ssize_t) image->columns; x++)
494     {
495       MagickRealType
496         alpha,
497         intensity;
498
499       alpha=GetPixelAlpha(image,q);
500       intensity=GetPixelIntensity(complete_mask,p);
501       if (revert == MagickFalse)
502         SetPixelAlpha(image,ClampToQuantum(intensity*(QuantumScale*alpha)),q);
503       else if (intensity > 0)
504         SetPixelAlpha(image,ClampToQuantum((alpha/intensity)*QuantumRange),q);
505       q+=GetPixelChannels(image);
506       p+=GetPixelChannels(complete_mask);
507     }
508     if (SyncAuthenticPixels(image,exception) == MagickFalse)
509       status=MagickFalse;
510   }
511   complete_mask=DestroyImage(complete_mask);
512   return(status);
513 }
514
515 static void PreservePSDOpacityMask(Image *image,LayerInfo* layer_info,
516   ExceptionInfo *exception)
517 {
518   char
519     *key;
520
521   RandomInfo
522     *random_info;
523
524   StringInfo
525     *key_info;
526
527   if (image->debug != MagickFalse)
528     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
529       "  preserving opacity mask");
530   random_info=AcquireRandomInfo();
531   key_info=GetRandomKey(random_info,2+1);
532   key=(char *) GetStringInfoDatum(key_info);
533   key[8]=layer_info->mask.background;
534   key[9]='\0';
535   layer_info->mask.image->page.x+=layer_info->page.x;
536   layer_info->mask.image->page.y+=layer_info->page.y;
537   (void) SetImageRegistry(ImageRegistryType,(const char *) key,
538     layer_info->mask.image,exception);
539   (void) SetImageArtifact(layer_info->image,"psd:opacity-mask",
540     (const char *) key);
541   key_info=DestroyStringInfo(key_info);
542   random_info=DestroyRandomInfo(random_info);
543 }
544
545 static ssize_t DecodePSDPixels(const size_t number_compact_pixels,
546   const unsigned char *compact_pixels,const ssize_t depth,
547   const size_t number_pixels,unsigned char *pixels)
548 {
549 #define CheckNumberCompactPixels \
550   if (packets == 0) \
551     return(i); \
552   packets--
553
554 #define CheckNumberPixels(count) \
555   if (((ssize_t) i + count) > (ssize_t) number_pixels) \
556     return(i); \
557   i+=count
558
559   int
560     pixel;
561
562   register ssize_t
563     i,
564     j;
565
566   size_t
567     length;
568
569   ssize_t
570     packets;
571
572   packets=(ssize_t) number_compact_pixels;
573   for (i=0; (packets > 1) && (i < (ssize_t) number_pixels); )
574   {
575     packets--;
576     length=(size_t) (*compact_pixels++);
577     if (length == 128)
578       continue;
579     if (length > 128)
580       {
581         length=256-length+1;
582         CheckNumberCompactPixels;
583         pixel=(*compact_pixels++);
584         for (j=0; j < (ssize_t) length; j++)
585         {
586           switch (depth)
587           {
588             case 1:
589             {
590               CheckNumberPixels(8);
591               *pixels++=(pixel >> 7) & 0x01 ? 0U : 255U;
592               *pixels++=(pixel >> 6) & 0x01 ? 0U : 255U;
593               *pixels++=(pixel >> 5) & 0x01 ? 0U : 255U;
594               *pixels++=(pixel >> 4) & 0x01 ? 0U : 255U;
595               *pixels++=(pixel >> 3) & 0x01 ? 0U : 255U;
596               *pixels++=(pixel >> 2) & 0x01 ? 0U : 255U;
597               *pixels++=(pixel >> 1) & 0x01 ? 0U : 255U;
598               *pixels++=(pixel >> 0) & 0x01 ? 0U : 255U;
599               break;
600             }
601             case 2:
602             {
603               CheckNumberPixels(4);
604               *pixels++=(unsigned char) ((pixel >> 6) & 0x03);
605               *pixels++=(unsigned char) ((pixel >> 4) & 0x03);
606               *pixels++=(unsigned char) ((pixel >> 2) & 0x03);
607               *pixels++=(unsigned char) ((pixel & 0x03) & 0x03);
608               break;
609             }
610             case 4:
611             {
612               CheckNumberPixels(2);
613               *pixels++=(unsigned char) ((pixel >> 4) & 0xff);
614               *pixels++=(unsigned char) ((pixel & 0x0f) & 0xff);
615               break;
616             }
617             default:
618             {
619               CheckNumberPixels(1);
620               *pixels++=(unsigned char) pixel;
621               break;
622             }
623           }
624         }
625         continue;
626       }
627     length++;
628     for (j=0; j < (ssize_t) length; j++)
629     {
630       CheckNumberCompactPixels;
631       switch (depth)
632       {
633         case 1:
634         {
635           CheckNumberPixels(8);
636           *pixels++=(*compact_pixels >> 7) & 0x01 ? 0U : 255U;
637           *pixels++=(*compact_pixels >> 6) & 0x01 ? 0U : 255U;
638           *pixels++=(*compact_pixels >> 5) & 0x01 ? 0U : 255U;
639           *pixels++=(*compact_pixels >> 4) & 0x01 ? 0U : 255U;
640           *pixels++=(*compact_pixels >> 3) & 0x01 ? 0U : 255U;
641           *pixels++=(*compact_pixels >> 2) & 0x01 ? 0U : 255U;
642           *pixels++=(*compact_pixels >> 1) & 0x01 ? 0U : 255U;
643           *pixels++=(*compact_pixels >> 0) & 0x01 ? 0U : 255U;
644           break;
645         }
646         case 2:
647         {
648           CheckNumberPixels(4);
649           *pixels++=(*compact_pixels >> 6) & 0x03;
650           *pixels++=(*compact_pixels >> 4) & 0x03;
651           *pixels++=(*compact_pixels >> 2) & 0x03;
652           *pixels++=(*compact_pixels & 0x03) & 0x03;
653           break;
654         }
655         case 4:
656         {
657           CheckNumberPixels(2);
658           *pixels++=(*compact_pixels >> 4) & 0xff;
659           *pixels++=(*compact_pixels & 0x0f) & 0xff;
660           break;
661         }
662         default:
663         {
664           CheckNumberPixels(1);
665           *pixels++=(*compact_pixels);
666           break;
667         }
668       }
669       compact_pixels++;
670     }
671   }
672   return(i);
673 }
674
675 static inline LayerInfo *DestroyLayerInfo(LayerInfo *layer_info,
676   const ssize_t number_layers)
677 {
678   ssize_t
679     i;
680
681   for (i=0; i<number_layers; i++)
682   {
683     if (layer_info[i].image != (Image *) NULL)
684       layer_info[i].image=DestroyImage(layer_info[i].image);
685     if (layer_info[i].mask.image != (Image *) NULL)
686       layer_info[i].mask.image=DestroyImage(layer_info[i].mask.image);
687     if (layer_info[i].info != (StringInfo *) NULL)
688       layer_info[i].info=DestroyStringInfo(layer_info[i].info);
689   }
690
691   return (LayerInfo *) RelinquishMagickMemory(layer_info);
692 }
693
694 static inline size_t GetPSDPacketSize(Image *image)
695 {
696   if (image->storage_class == PseudoClass)
697     {
698       if (image->colors > 256)
699         return(2);
700       else if (image->depth > 8)
701         return(2);
702     }
703   else
704     if (image->depth > 8)
705       return(2);
706
707   return(1);
708 }
709
710 static inline MagickSizeType GetPSDSize(const PSDInfo *psd_info,Image *image)
711 {
712   if (psd_info->version == 1)
713     return((MagickSizeType) ReadBlobLong(image));
714   return((MagickSizeType) ReadBlobLongLong(image));
715 }
716
717 static inline size_t GetPSDRowSize(Image *image)
718 {
719   if (image->depth == 1)
720     return(((image->columns+7)/8)*GetPSDPacketSize(image));
721   else
722     return(image->columns*GetPSDPacketSize(image));
723 }
724
725 static const char *ModeToString(PSDImageType type)
726 {
727   switch (type)
728   {
729     case BitmapMode: return "Bitmap";
730     case GrayscaleMode: return "Grayscale";
731     case IndexedMode: return "Indexed";
732     case RGBMode: return "RGB";
733     case CMYKMode:  return "CMYK";
734     case MultichannelMode: return "Multichannel";
735     case DuotoneMode: return "Duotone";
736     case LabMode: return "L*A*B";
737     default: return "unknown";
738   }
739 }
740
741 static MagickBooleanType NegateCMYK(Image *image,ExceptionInfo *exception)
742 {
743   ChannelType
744     channel_mask;
745
746   MagickBooleanType
747     status;
748
749   channel_mask=SetImageChannelMask(image,(ChannelType)(AllChannels &~
750     AlphaChannel));
751   status=NegateImage(image,MagickFalse,exception);
752   (void) SetImageChannelMask(image,channel_mask);
753   return(status);
754 }
755
756 static void ParseImageResourceBlocks(Image *image,
757   const unsigned char *blocks,size_t length,
758   MagickBooleanType *has_merged_image,ExceptionInfo *exception)
759 {
760   const unsigned char
761     *p;
762
763   StringInfo
764     *profile;
765
766   unsigned char
767     name_length;
768
769   unsigned int
770     count;
771
772   unsigned short
773     id,
774     short_sans;
775
776   if (length < 16)
777     return;
778   profile=BlobToStringInfo((const unsigned char *) NULL,length);
779   SetStringInfoDatum(profile,blocks);
780   (void) SetImageProfile(image,"8bim",profile,exception);
781   profile=DestroyStringInfo(profile);
782   for (p=blocks; (p >= blocks) && (p < (blocks+length-7)); )
783   {
784     if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
785       break;
786     p+=4;
787     p=PushShortPixel(MSBEndian,p,&id);
788     p=PushCharPixel(p,&name_length);
789     if ((name_length % 2) == 0)
790       name_length++;
791     p+=name_length;
792     if (p > (blocks+length-4))
793       return;
794     p=PushLongPixel(MSBEndian,p,&count);
795     if ((p+count) > (blocks+length))
796       return;
797     switch (id)
798     {
799       case 0x03ed:
800       {
801         char
802           value[MagickPathExtent];
803
804         unsigned short
805           resolution;
806
807         /*
808           Resolution info.
809         */
810         if (count < 16)
811           return;
812         p=PushShortPixel(MSBEndian,p,&resolution);
813         image->resolution.x=(double) resolution;
814         (void) FormatLocaleString(value,MagickPathExtent,"%g",
815           image->resolution.x);
816         (void) SetImageProperty(image,"tiff:XResolution",value,exception);
817         p=PushShortPixel(MSBEndian,p,&short_sans);
818         p=PushShortPixel(MSBEndian,p,&short_sans);
819         p=PushShortPixel(MSBEndian,p,&short_sans);
820         p=PushShortPixel(MSBEndian,p,&resolution);
821         image->resolution.y=(double) resolution;
822         (void) FormatLocaleString(value,MagickPathExtent,"%g",
823           image->resolution.y);
824         (void) SetImageProperty(image,"tiff:YResolution",value,exception);
825         p=PushShortPixel(MSBEndian,p,&short_sans);
826         p=PushShortPixel(MSBEndian,p,&short_sans);
827         p=PushShortPixel(MSBEndian,p,&short_sans);
828         image->units=PixelsPerInchResolution;
829         break;
830       }
831       case 0x0421:
832       {
833         if ((count > 3) && (*(p+4) == 0))
834           *has_merged_image=MagickFalse;
835         p+=count;
836         break;
837       }
838       default:
839       {
840         p+=count;
841         break;
842       }
843     }
844     if ((count & 0x01) != 0)
845       p++;
846   }
847   return;
848 }
849
850 static CompositeOperator PSDBlendModeToCompositeOperator(const char *mode)
851 {
852   if (mode == (const char *) NULL)
853     return(OverCompositeOp);
854   if (LocaleNCompare(mode,"norm",4) == 0)
855     return(OverCompositeOp);
856   if (LocaleNCompare(mode,"mul ",4) == 0)
857     return(MultiplyCompositeOp);
858   if (LocaleNCompare(mode,"diss",4) == 0)
859     return(DissolveCompositeOp);
860   if (LocaleNCompare(mode,"diff",4) == 0)
861     return(DifferenceCompositeOp);
862   if (LocaleNCompare(mode,"dark",4) == 0)
863     return(DarkenCompositeOp);
864   if (LocaleNCompare(mode,"lite",4) == 0)
865     return(LightenCompositeOp);
866   if (LocaleNCompare(mode,"hue ",4) == 0)
867     return(HueCompositeOp);
868   if (LocaleNCompare(mode,"sat ",4) == 0)
869     return(SaturateCompositeOp);
870   if (LocaleNCompare(mode,"colr",4) == 0)
871     return(ColorizeCompositeOp);
872   if (LocaleNCompare(mode,"lum ",4) == 0)
873     return(LuminizeCompositeOp);
874   if (LocaleNCompare(mode,"scrn",4) == 0)
875     return(ScreenCompositeOp);
876   if (LocaleNCompare(mode,"over",4) == 0)
877     return(OverlayCompositeOp);
878   if (LocaleNCompare(mode,"hLit",4) == 0)
879     return(HardLightCompositeOp);
880   if (LocaleNCompare(mode,"sLit",4) == 0)
881     return(SoftLightCompositeOp);
882   if (LocaleNCompare(mode,"smud",4) == 0)
883     return(ExclusionCompositeOp);
884   if (LocaleNCompare(mode,"div ",4) == 0)
885     return(ColorDodgeCompositeOp);
886   if (LocaleNCompare(mode,"idiv",4) == 0)
887     return(ColorBurnCompositeOp);
888   if (LocaleNCompare(mode,"lbrn",4) == 0)
889     return(LinearBurnCompositeOp);
890   if (LocaleNCompare(mode,"lddg",4) == 0)
891     return(LinearDodgeCompositeOp);
892   if (LocaleNCompare(mode,"lLit",4) == 0)
893     return(LinearLightCompositeOp);
894   if (LocaleNCompare(mode,"vLit",4) == 0)
895     return(VividLightCompositeOp);
896   if (LocaleNCompare(mode,"pLit",4) == 0)
897     return(PinLightCompositeOp);
898   if (LocaleNCompare(mode,"hMix",4) == 0)
899     return(HardMixCompositeOp);
900   return(OverCompositeOp);
901 }
902
903 static inline void ReversePSDString(Image *image,char *p,size_t length)
904 {
905   char
906     *q;
907
908   if (image->endian == MSBEndian)
909     return;
910
911   q=p+length;
912   for(--q; p < q; ++p, --q)
913   {
914     *p = *p ^ *q,
915     *q = *p ^ *q,
916     *p = *p ^ *q;
917   }
918 }
919
920 static inline void SetPSDPixel(Image *image,const size_t channels,
921   const ssize_t type,const size_t packet_size,const Quantum pixel,Quantum *q,
922   ExceptionInfo *exception)
923 {
924   if (image->storage_class == PseudoClass)
925     {
926       PixelInfo
927         *color;
928
929       if (type == 0)
930         {
931           if (packet_size == 1)
932             SetPixelIndex(image,ScaleQuantumToChar(pixel),q);
933           else
934             SetPixelIndex(image,ScaleQuantumToShort(pixel),q);
935         }
936       color=image->colormap+(ssize_t) ConstrainColormapIndex(image,
937         GetPixelIndex(image,q),exception);
938       if ((type == 0) && (channels > 1))
939         return;
940       else
941         color->alpha=(MagickRealType) pixel;
942       SetPixelViaPixelInfo(image,color,q);
943       return;
944     }
945   switch (type)
946   {
947     case -1:
948     {
949       SetPixelAlpha(image,pixel,q);
950       break;
951     }
952     case -2:
953     case 0:
954     {
955       SetPixelRed(image,pixel,q);
956       break;
957     }
958     case 1:
959     {
960       SetPixelGreen(image,pixel,q);
961       break;
962     }
963     case 2:
964     {
965       SetPixelBlue(image,pixel,q);
966       break;
967     }
968     case 3:
969     {
970       if (image->colorspace == CMYKColorspace)
971         SetPixelBlack(image,pixel,q);
972       else
973         if (image->alpha_trait != UndefinedPixelTrait)
974           SetPixelAlpha(image,pixel,q);
975       break;
976     }
977     case 4:
978     {
979       if ((IssRGBCompatibleColorspace(image->colorspace) != MagickFalse) &&
980           (channels > 3))
981         break;
982       if (image->alpha_trait != UndefinedPixelTrait)
983         SetPixelAlpha(image,pixel,q);
984       break;
985     }
986   }
987 }
988
989 static MagickBooleanType ReadPSDChannelPixels(Image *image,
990   const size_t channels,const size_t row,const ssize_t type,
991   const unsigned char *pixels,ExceptionInfo *exception)
992 {
993   Quantum
994     pixel;
995
996   register const unsigned char
997     *p;
998
999   register Quantum
1000     *q;
1001
1002   register ssize_t
1003     x;
1004
1005   size_t
1006     packet_size;
1007
1008   unsigned short
1009     nibble;
1010
1011   p=pixels;
1012   q=GetAuthenticPixels(image,0,row,image->columns,1,exception);
1013   if (q == (Quantum *) NULL)
1014     return MagickFalse;
1015   packet_size=GetPSDPacketSize(image);
1016   for (x=0; x < (ssize_t) image->columns; x++)
1017   {
1018     if (packet_size == 1)
1019       pixel=ScaleCharToQuantum(*p++);
1020     else
1021       {
1022         p=PushShortPixel(MSBEndian,p,&nibble);
1023         pixel=ScaleShortToQuantum(nibble);
1024       }
1025     if (image->depth > 1)
1026       {
1027         SetPSDPixel(image,channels,type,packet_size,pixel,q,exception);
1028         q+=GetPixelChannels(image);
1029       }
1030     else
1031       {
1032         ssize_t
1033           bit,
1034           number_bits;
1035
1036         number_bits=image->columns-x;
1037         if (number_bits > 8)
1038           number_bits=8;
1039         for (bit = 0; bit < number_bits; bit++)
1040         {
1041           SetPSDPixel(image,channels,type,packet_size,(((unsigned char) pixel)
1042             & (0x01 << (7-bit))) != 0 ? 0 : QuantumRange,q,exception);
1043           q+=GetPixelChannels(image);
1044           x++;
1045         }
1046         if (x != (ssize_t) image->columns)
1047           x--;
1048         continue;
1049       }
1050   }
1051   return(SyncAuthenticPixels(image,exception));
1052 }
1053
1054 static MagickBooleanType ReadPSDChannelRaw(Image *image,const size_t channels,
1055   const ssize_t type,ExceptionInfo *exception)
1056 {
1057   MagickBooleanType
1058     status;
1059
1060   size_t
1061     count,
1062     row_size;
1063
1064   ssize_t
1065     y;
1066
1067   unsigned char
1068     *pixels;
1069
1070   if (image->debug != MagickFalse)
1071     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1072        "      layer data is RAW");
1073
1074   row_size=GetPSDRowSize(image);
1075   pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
1076   if (pixels == (unsigned char *) NULL)
1077     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1078       image->filename);
1079
1080   status=MagickTrue;
1081   for (y=0; y < (ssize_t) image->rows; y++)
1082   {
1083     status=MagickFalse;
1084
1085     count=ReadBlob(image,row_size,pixels);
1086     if (count != row_size)
1087       break;
1088
1089     status=ReadPSDChannelPixels(image,channels,y,type,pixels,exception);
1090     if (status == MagickFalse)
1091       break;
1092   }
1093
1094   pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1095   return(status);
1096 }
1097
1098 static inline MagickOffsetType *ReadPSDRLESizes(Image *image,
1099   const PSDInfo *psd_info,const size_t size)
1100 {
1101   MagickOffsetType
1102     *sizes;
1103
1104   ssize_t
1105     y;
1106
1107   sizes=(MagickOffsetType *) AcquireQuantumMemory(size,sizeof(*sizes));
1108   if(sizes != (MagickOffsetType *) NULL)
1109     {
1110       for (y=0; y < (ssize_t) size; y++)
1111       {
1112         if (psd_info->version == 1)
1113           sizes[y]=(MagickOffsetType) ReadBlobShort(image);
1114         else
1115           sizes[y]=(MagickOffsetType) ReadBlobLong(image);
1116       }
1117     }
1118   return sizes;
1119 }
1120
1121 static MagickBooleanType ReadPSDChannelRLE(Image *image,const PSDInfo *psd_info,
1122   const ssize_t type,MagickOffsetType *sizes,ExceptionInfo *exception)
1123 {
1124   MagickBooleanType
1125     status;
1126
1127   size_t
1128     length,
1129     row_size;
1130
1131   ssize_t
1132     count,
1133     y;
1134
1135   unsigned char
1136     *compact_pixels,
1137     *pixels;
1138
1139   if (image->debug != MagickFalse)
1140     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1141        "      layer data is RLE compressed");
1142
1143   row_size=GetPSDRowSize(image);
1144   pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
1145   if (pixels == (unsigned char *) NULL)
1146     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1147       image->filename);
1148
1149   length=0;
1150   for (y=0; y < (ssize_t) image->rows; y++)
1151     if ((MagickOffsetType) length < sizes[y])
1152       length=(size_t) sizes[y];
1153
1154   if (length > row_size + 256) // arbitrary number
1155     {
1156       pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1157       ThrowBinaryException(ResourceLimitError,"InvalidLength",image->filename);
1158     }
1159
1160   compact_pixels=(unsigned char *) AcquireQuantumMemory(length,sizeof(*pixels));
1161   if (compact_pixels == (unsigned char *) NULL)
1162     {
1163       pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1164       ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1165         image->filename);
1166     }
1167
1168   (void) ResetMagickMemory(compact_pixels,0,length*sizeof(*compact_pixels));
1169
1170   status=MagickTrue;
1171   for (y=0; y < (ssize_t) image->rows; y++)
1172   {
1173     status=MagickFalse;
1174
1175     count=ReadBlob(image,(size_t) sizes[y],compact_pixels);
1176     if (count != (ssize_t) sizes[y])
1177       break;
1178
1179     count=DecodePSDPixels((size_t) sizes[y],compact_pixels,
1180       (ssize_t) (image->depth == 1 ? 123456 : image->depth),row_size,pixels);
1181     if (count != (ssize_t) row_size)
1182       break;
1183
1184     status=ReadPSDChannelPixels(image,psd_info->channels,y,type,pixels,
1185       exception);
1186     if (status == MagickFalse)
1187       break;
1188   }
1189
1190   compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1191   pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1192   return(status);
1193 }
1194
1195 #ifdef MAGICKCORE_ZLIB_DELEGATE
1196 static MagickBooleanType ReadPSDChannelZip(Image *image,const size_t channels,
1197   const ssize_t type,const PSDCompressionType compression,
1198   const size_t compact_size,ExceptionInfo *exception)
1199 {
1200   MagickBooleanType
1201     status;
1202
1203   register unsigned char
1204     *p;
1205
1206   size_t
1207     count,
1208     length,
1209     packet_size,
1210     row_size;
1211
1212   ssize_t
1213     y;
1214
1215   unsigned char
1216     *compact_pixels,
1217     *pixels;
1218
1219   z_stream
1220     stream;
1221
1222   if (image->debug != MagickFalse)
1223     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1224        "      layer data is ZIP compressed");
1225
1226   compact_pixels=(unsigned char *) AcquireQuantumMemory(compact_size,
1227     sizeof(*compact_pixels));
1228   if (compact_pixels == (unsigned char *) NULL)
1229     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1230       image->filename);
1231
1232   packet_size=GetPSDPacketSize(image);
1233   row_size=image->columns*packet_size;
1234   count=image->rows*row_size;
1235
1236   pixels=(unsigned char *) AcquireQuantumMemory(count,sizeof(*pixels));
1237   if (pixels == (unsigned char *) NULL)
1238     {
1239       compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1240       ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1241         image->filename);
1242     }
1243   if (ReadBlob(image,compact_size,compact_pixels) != (ssize_t) compact_size)
1244     {
1245       compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1246       ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile",
1247         image->filename);
1248     }
1249
1250   ResetMagickMemory(&stream,0,sizeof(stream));
1251   stream.data_type=Z_BINARY;
1252   stream.next_in=(Bytef *)compact_pixels;
1253   stream.avail_in=(uInt) compact_size;
1254   stream.next_out=(Bytef *)pixels;
1255   stream.avail_out=(uInt) count;
1256
1257   if (inflateInit(&stream) == Z_OK)
1258     {
1259       int
1260         ret;
1261
1262       while (stream.avail_out > 0)
1263       {
1264         ret=inflate(&stream,Z_SYNC_FLUSH);
1265         if ((ret != Z_OK) && (ret != Z_STREAM_END))
1266           {
1267             (void) inflateEnd(&stream);
1268             compact_pixels=(unsigned char *) RelinquishMagickMemory(
1269               compact_pixels);
1270             pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1271             return(MagickFalse);
1272           }
1273       }
1274       (void) inflateEnd(&stream);
1275     }
1276
1277   if (compression == ZipWithPrediction)
1278   {
1279      p=pixels;
1280      while (count > 0)
1281      {
1282        length=image->columns;
1283        while (--length)
1284        {
1285          if (packet_size == 2)
1286            {
1287              p[2]+=p[0]+((p[1]+p[3]) >> 8);
1288              p[3]+=p[1];
1289            }
1290          else
1291           *(p+1)+=*p;
1292          p+=packet_size;
1293        }
1294        p+=packet_size;
1295        count-=row_size;
1296      }
1297   }
1298
1299   status=MagickTrue;
1300   p=pixels;
1301   for (y=0; y < (ssize_t) image->rows; y++)
1302   {
1303     status=ReadPSDChannelPixels(image,channels,y,type,p,exception);
1304     if (status == MagickFalse)
1305       break;
1306
1307     p+=row_size;
1308   }
1309
1310   compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1311   pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1312   return(status);
1313 }
1314 #endif
1315
1316 static MagickBooleanType ReadPSDChannel(Image *image,
1317   const ImageInfo *image_info,const PSDInfo *psd_info,LayerInfo* layer_info,
1318   const size_t channel,const PSDCompressionType compression,
1319   ExceptionInfo *exception)
1320 {
1321   Image
1322     *channel_image,
1323     *mask;
1324
1325   MagickOffsetType
1326     offset;
1327
1328   MagickBooleanType
1329     status;
1330
1331   channel_image=image;
1332   mask=(Image *) NULL;
1333   if ((layer_info->channel_info[channel].type < -1) &&
1334       (layer_info->mask.page.width > 0) && (layer_info->mask.page.height > 0))
1335     {
1336       const char
1337         *option;
1338
1339       /*
1340         Ignore mask that is not a user supplied layer mask, if the mask is
1341         disabled or if the flags have unsupported values.
1342       */
1343       option=GetImageOption(image_info,"psd:preserve-opacity-mask");
1344       if ((layer_info->channel_info[channel].type != -2) ||
1345           (layer_info->mask.flags > 2) || ((layer_info->mask.flags & 0x02) &&
1346            (IsStringTrue(option) == MagickFalse)))
1347         {
1348           SeekBlob(image,layer_info->channel_info[channel].size-2,SEEK_CUR);
1349           return(MagickTrue);
1350         }
1351       mask=CloneImage(image,layer_info->mask.page.width,
1352         layer_info->mask.page.height,MagickFalse,exception);
1353       if (mask != (Image *) NULL)
1354         {
1355           SetImageType(mask,GrayscaleType,exception);
1356           channel_image=mask;
1357         }
1358     }
1359
1360   offset=TellBlob(image);
1361   status=MagickFalse;
1362   switch(compression)
1363   {
1364     case Raw:
1365       status=ReadPSDChannelRaw(channel_image,psd_info->channels,
1366         layer_info->channel_info[channel].type,exception);
1367       break;
1368     case RLE:
1369       {
1370         MagickOffsetType
1371           *sizes;
1372
1373         sizes=ReadPSDRLESizes(channel_image,psd_info,channel_image->rows);
1374         if (sizes == (MagickOffsetType *) NULL)
1375           ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1376             image->filename);
1377         status=ReadPSDChannelRLE(channel_image,psd_info,
1378           layer_info->channel_info[channel].type,sizes,exception);
1379         sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
1380       }
1381       break;
1382     case ZipWithPrediction:
1383     case ZipWithoutPrediction:
1384 #ifdef MAGICKCORE_ZLIB_DELEGATE
1385       status=ReadPSDChannelZip(channel_image,layer_info->channels,
1386         layer_info->channel_info[channel].type,compression,
1387         layer_info->channel_info[channel].size-2,exception);
1388 #else
1389       (void) ThrowMagickException(exception,GetMagickModule(),
1390           MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
1391             "'%s' (ZLIB)",image->filename);
1392 #endif
1393       break;
1394     default:
1395       (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
1396         "CompressionNotSupported","'%.20g'",(double) compression);
1397       break;
1398   }
1399
1400   SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
1401   if (status == MagickFalse)
1402     {
1403       if (mask != (Image *) NULL)
1404         DestroyImage(mask);
1405       ThrowBinaryException(CoderError,"UnableToDecompressImage",
1406         image->filename);
1407     }
1408   layer_info->mask.image=mask;
1409   return(status);
1410 }
1411
1412 static MagickBooleanType ReadPSDLayer(Image *image,const ImageInfo *image_info,
1413   const PSDInfo *psd_info,LayerInfo* layer_info,ExceptionInfo *exception)
1414 {
1415   char
1416     message[MagickPathExtent];
1417
1418   MagickBooleanType
1419     status;
1420
1421   PSDCompressionType
1422     compression;
1423
1424   ssize_t
1425     j;
1426
1427   if (image->debug != MagickFalse)
1428     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1429       "    setting up new layer image");
1430   if (psd_info->mode != IndexedMode)
1431     (void) SetImageBackgroundColor(layer_info->image,exception);
1432   layer_info->image->compose=PSDBlendModeToCompositeOperator(
1433     layer_info->blendkey);
1434   if (layer_info->visible == MagickFalse)
1435     layer_info->image->compose=NoCompositeOp;
1436   if (psd_info->mode == CMYKMode)
1437     SetImageColorspace(layer_info->image,CMYKColorspace,exception);
1438   else if ((psd_info->mode == BitmapMode) || (psd_info->mode == DuotoneMode) ||
1439            (psd_info->mode == GrayscaleMode))
1440     SetImageColorspace(layer_info->image,GRAYColorspace,exception);
1441   /*
1442     Set up some hidden attributes for folks that need them.
1443   */
1444   (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
1445     (double) layer_info->page.x);
1446   (void) SetImageArtifact(layer_info->image,"psd:layer.x",message);
1447   (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
1448     (double) layer_info->page.y);
1449   (void) SetImageArtifact(layer_info->image,"psd:layer.y",message);
1450   (void) FormatLocaleString(message,MagickPathExtent,"%.20g",(double)
1451     layer_info->opacity);
1452   (void) SetImageArtifact(layer_info->image,"psd:layer.opacity",message);
1453   (void) SetImageProperty(layer_info->image,"label",(char *) layer_info->name,
1454     exception);
1455
1456   status=MagickTrue;
1457   for (j=0; j < (ssize_t) layer_info->channels; j++)
1458   {
1459     if (image->debug != MagickFalse)
1460       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1461         "    reading data for channel %.20g",(double) j);
1462
1463     compression=(PSDCompressionType) ReadBlobShort(layer_info->image);
1464     layer_info->image->compression=ConvertPSDCompression(compression);
1465     if (layer_info->channel_info[j].type == -1)
1466       layer_info->image->alpha_trait=BlendPixelTrait;
1467
1468     status=ReadPSDChannel(layer_info->image,image_info,psd_info,layer_info,j,
1469       compression,exception);
1470
1471     if (status == MagickFalse)
1472       break;
1473   }
1474
1475   if (status != MagickFalse)
1476     status=ApplyPSDLayerOpacity(layer_info->image,layer_info->opacity,
1477       MagickFalse,exception);
1478
1479   if ((status != MagickFalse) &&
1480       (layer_info->image->colorspace == CMYKColorspace))
1481     status=NegateCMYK(layer_info->image,exception);
1482
1483   if ((status != MagickFalse) && (layer_info->mask.image != (Image *) NULL))
1484     {
1485       const char
1486         *option;
1487
1488       layer_info->mask.image->page.x=layer_info->mask.page.x;
1489       layer_info->mask.image->page.y=layer_info->mask.page.y;
1490       /* Do not composite the mask when it is disabled */
1491       if ((layer_info->mask.flags & 0x02) == 0x02)
1492         layer_info->mask.image->compose=NoCompositeOp;
1493       else
1494         status=ApplyPSDOpacityMask(layer_info->image,layer_info->mask.image,
1495           layer_info->mask.background == 0 ? 0 : QuantumRange,MagickFalse,
1496           exception);
1497       option=GetImageOption(image_info,"psd:preserve-opacity-mask");
1498       if (IsStringTrue(option) != MagickFalse)
1499         PreservePSDOpacityMask(image,layer_info,exception);
1500       layer_info->mask.image=DestroyImage(layer_info->mask.image);
1501     }
1502
1503   return(status);
1504 }
1505
1506 static MagickBooleanType ReadPSDLayersInternal(Image *image,
1507   const ImageInfo *image_info,const PSDInfo *psd_info,
1508   const MagickBooleanType skip_layers,ExceptionInfo *exception)
1509 {
1510   char
1511     type[4];
1512
1513   LayerInfo
1514     *layer_info;
1515
1516   MagickSizeType
1517     size;
1518
1519   MagickBooleanType
1520     status;
1521
1522   register ssize_t
1523     i;
1524
1525   ssize_t
1526     count,
1527     j,
1528     number_layers;
1529
1530   size=GetPSDSize(psd_info,image);
1531   if (size == 0)
1532     {
1533       /*
1534         Skip layers & masks.
1535       */
1536       (void) ReadBlobLong(image);
1537       count=ReadBlob(image,4,(unsigned char *) type);
1538       ReversePSDString(image,type,4);
1539       status=MagickFalse;
1540       if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1541         return(MagickTrue);
1542       else
1543         {
1544           count=ReadBlob(image,4,(unsigned char *) type);
1545           ReversePSDString(image,type,4);
1546           if ((count != 0) && (LocaleNCompare(type,"Lr16",4) == 0))
1547             size=GetPSDSize(psd_info,image);
1548           else
1549             return(MagickTrue);
1550         }
1551     }
1552   status=MagickTrue;
1553   if (size != 0)
1554     {
1555       layer_info=(LayerInfo *) NULL;
1556       number_layers=(short) ReadBlobShort(image);
1557
1558       if (number_layers < 0)
1559         {
1560           /*
1561             The first alpha channel in the merged result contains the
1562             transparency data for the merged result.
1563           */
1564           number_layers=MagickAbsoluteValue(number_layers);
1565           if (image->debug != MagickFalse)
1566             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1567               "  negative layer count corrected for");
1568           image->alpha_trait=BlendPixelTrait;
1569         }
1570
1571       /*
1572         We only need to know if the image has an alpha channel
1573       */
1574       if (skip_layers != MagickFalse)
1575         return(MagickTrue);
1576
1577       if (image->debug != MagickFalse)
1578         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1579           "  image contains %.20g layers",(double) number_layers);
1580
1581       if (number_layers == 0)
1582         ThrowBinaryException(CorruptImageError,"InvalidNumberOfLayers",
1583           image->filename);
1584
1585       layer_info=(LayerInfo *) AcquireQuantumMemory((size_t) number_layers,
1586         sizeof(*layer_info));
1587       if (layer_info == (LayerInfo *) NULL)
1588         {
1589           if (image->debug != MagickFalse)
1590             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1591               "  allocation of LayerInfo failed");
1592           ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1593             image->filename);
1594         }
1595       (void) ResetMagickMemory(layer_info,0,(size_t) number_layers*
1596         sizeof(*layer_info));
1597
1598       for (i=0; i < number_layers; i++)
1599       {
1600         ssize_t
1601           x,
1602           y;
1603
1604         if (image->debug != MagickFalse)
1605           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1606             "  reading layer #%.20g",(double) i+1);
1607         layer_info[i].page.y=ReadBlobSignedLong(image);
1608         layer_info[i].page.x=ReadBlobSignedLong(image);
1609         y=ReadBlobSignedLong(image);
1610         x=ReadBlobSignedLong(image);
1611         layer_info[i].page.width=(size_t) (x-layer_info[i].page.x);
1612         layer_info[i].page.height=(size_t) (y-layer_info[i].page.y);
1613         layer_info[i].channels=ReadBlobShort(image);
1614         if (layer_info[i].channels > MaxPSDChannels)
1615           {
1616             layer_info=DestroyLayerInfo(layer_info,number_layers);
1617             ThrowBinaryException(CorruptImageError,"MaximumChannelsExceeded",
1618               image->filename);
1619           }
1620         if (image->debug != MagickFalse)
1621           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1622             "    offset(%.20g,%.20g), size(%.20g,%.20g), channels=%.20g",
1623             (double) layer_info[i].page.x,(double) layer_info[i].page.y,
1624             (double) layer_info[i].page.height,(double)
1625             layer_info[i].page.width,(double) layer_info[i].channels);
1626         for (j=0; j < (ssize_t) layer_info[i].channels; j++)
1627         {
1628           layer_info[i].channel_info[j].type=(short) ReadBlobShort(image);
1629           layer_info[i].channel_info[j].size=(size_t) GetPSDSize(psd_info,
1630             image);
1631           if (image->debug != MagickFalse)
1632             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1633               "    channel[%.20g]: type=%.20g, size=%.20g",(double) j,
1634               (double) layer_info[i].channel_info[j].type,
1635               (double) layer_info[i].channel_info[j].size);
1636         }
1637         count=ReadBlob(image,4,(unsigned char *) type);
1638         ReversePSDString(image,type,4);
1639         if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1640           {
1641             if (image->debug != MagickFalse)
1642               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1643                 "  layer type was %.4s instead of 8BIM", type);
1644             layer_info=DestroyLayerInfo(layer_info,number_layers);
1645             ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
1646               image->filename);
1647           }
1648         count=ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey);
1649         ReversePSDString(image,layer_info[i].blendkey,4);
1650         layer_info[i].opacity=(Quantum) ScaleCharToQuantum((unsigned char)
1651           ReadBlobByte(image));
1652         layer_info[i].clipping=(unsigned char) ReadBlobByte(image);
1653         layer_info[i].flags=(unsigned char) ReadBlobByte(image);
1654         layer_info[i].visible=!(layer_info[i].flags & 0x02);
1655         if (image->debug != MagickFalse)
1656           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1657             "   blend=%.4s, opacity=%.20g, clipping=%s, flags=%d, visible=%s",
1658             layer_info[i].blendkey,(double) layer_info[i].opacity,
1659             layer_info[i].clipping ? "true" : "false",layer_info[i].flags,
1660             layer_info[i].visible ? "true" : "false");
1661         (void) ReadBlobByte(image);  /* filler */
1662
1663         size=ReadBlobLong(image);
1664         if (size != 0)
1665           {
1666             MagickSizeType
1667               combined_length,
1668               length;
1669
1670             if (image->debug != MagickFalse)
1671               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1672                 "    layer contains additional info");
1673             length=ReadBlobLong(image);
1674             combined_length=length+4;
1675             if (length != 0)
1676               {
1677                 /*
1678                   Layer mask info.
1679                 */
1680                 layer_info[i].mask.page.y=ReadBlobSignedLong(image);
1681                 layer_info[i].mask.page.x=ReadBlobSignedLong(image);
1682                 layer_info[i].mask.page.height=(size_t) (ReadBlobSignedLong(image)-
1683                   layer_info[i].mask.page.y);
1684                 layer_info[i].mask.page.width=(size_t) (ReadBlobSignedLong(image)-
1685                   layer_info[i].mask.page.x);
1686                 layer_info[i].mask.background=(unsigned char) ReadBlobByte(
1687                   image);
1688                 layer_info[i].mask.flags=(unsigned char) ReadBlobByte(image);
1689                 if (!(layer_info[i].mask.flags & 0x01))
1690                   {
1691                     layer_info[i].mask.page.y=layer_info[i].mask.page.y-
1692                       layer_info[i].page.y;
1693                     layer_info[i].mask.page.x=layer_info[i].mask.page.x-
1694                       layer_info[i].page.x;
1695                   }
1696                 if (image->debug != MagickFalse)
1697                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1698                     "      layer mask: offset(%.20g,%.20g), size(%.20g,%.20g), length=%.20g",
1699                     (double) layer_info[i].mask.page.x,(double)
1700                     layer_info[i].mask.page.y,(double)
1701                     layer_info[i].mask.page.width,(double)
1702                     layer_info[i].mask.page.height,(double) ((MagickOffsetType)
1703                     length)-18);
1704                 /*
1705                   Skip over the rest of the layer mask information.
1706                 */
1707                 if (DiscardBlobBytes(image,(MagickSizeType) (length-18)) == MagickFalse)
1708                   {
1709                     layer_info=DestroyLayerInfo(layer_info,number_layers);
1710                     ThrowBinaryException(CorruptImageError,
1711                       "UnexpectedEndOfFile",image->filename);
1712                   }
1713               }
1714             length=ReadBlobLong(image);
1715             combined_length+=length+4;
1716             if (length != 0)
1717               {
1718                 /*
1719                   Layer blending ranges info.
1720                 */
1721                 if (image->debug != MagickFalse)
1722                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1723                     "      layer blending ranges: length=%.20g",(double)
1724                     ((MagickOffsetType) length));
1725                 if (DiscardBlobBytes(image,length) == MagickFalse)
1726                   {
1727                     layer_info=DestroyLayerInfo(layer_info,number_layers);
1728                     ThrowBinaryException(CorruptImageError,
1729                       "UnexpectedEndOfFile",image->filename);
1730                   }
1731               }
1732             /*
1733               Layer name.
1734             */
1735             length=(MagickSizeType) (unsigned char) ReadBlobByte(image);
1736             combined_length+=length+1;
1737             if (length > 0)
1738               (void) ReadBlob(image,(size_t) length++,layer_info[i].name);
1739             layer_info[i].name[length]='\0';
1740             if (image->debug != MagickFalse)
1741               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1742                 "      layer name: %s",layer_info[i].name);
1743             if ((length % 4) != 0)
1744               {
1745                 length=4-(length % 4);
1746                 combined_length+=length;
1747                 /* Skip over the padding of the layer name */
1748                 if (DiscardBlobBytes(image,length) == MagickFalse)
1749                   {
1750                     layer_info=DestroyLayerInfo(layer_info,number_layers);
1751                     ThrowBinaryException(CorruptImageError,
1752                       "UnexpectedEndOfFile",image->filename);
1753                   }
1754               }
1755             length=(MagickSizeType) size-combined_length;
1756             if (length > 0)
1757               {
1758                 unsigned char
1759                   *info;
1760
1761                 if (length > GetBlobSize(image))
1762                   {
1763                     layer_info=DestroyLayerInfo(layer_info,number_layers);
1764                     ThrowBinaryException(CorruptImageError,
1765                       "InsufficientImageDataInFile",image->filename);
1766                   }
1767                 layer_info[i].info=AcquireStringInfo((const size_t) length);
1768                 info=GetStringInfoDatum(layer_info[i].info);
1769                 (void) ReadBlob(image,(const size_t) length,info);
1770               }
1771           }
1772       }
1773
1774       for (i=0; i < number_layers; i++)
1775       {
1776         if ((layer_info[i].page.width == 0) || (layer_info[i].page.height == 0))
1777           {
1778             if (image->debug != MagickFalse)
1779               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1780                 "      layer data is empty");
1781             if (layer_info[i].info != (StringInfo *) NULL)
1782               layer_info[i].info=DestroyStringInfo(layer_info[i].info);
1783             continue;
1784           }
1785
1786         /*
1787           Allocate layered image.
1788         */
1789         layer_info[i].image=CloneImage(image,layer_info[i].page.width,
1790           layer_info[i].page.height,MagickFalse,exception);
1791         if (layer_info[i].image == (Image *) NULL)
1792           {
1793             layer_info=DestroyLayerInfo(layer_info,number_layers);
1794             if (image->debug != MagickFalse)
1795               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1796                 "  allocation of image for layer %.20g failed",(double) i);
1797             ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1798               image->filename);
1799           }
1800
1801         if (layer_info[i].info != (StringInfo *) NULL)
1802           {
1803             (void) SetImageProfile(layer_info[i].image,"psd:additional-info",
1804               layer_info[i].info,exception);
1805             layer_info[i].info=DestroyStringInfo(layer_info[i].info);
1806           }
1807       }
1808
1809       if (image_info->ping == MagickFalse)
1810         {
1811           for (i=0; i < number_layers; i++)
1812           {
1813             if (layer_info[i].image == (Image *) NULL)
1814               {
1815                 for (j=0; j < layer_info[i].channels; j++)
1816                 {
1817                   if (DiscardBlobBytes(image,(MagickSizeType)
1818                       layer_info[i].channel_info[j].size) == MagickFalse)
1819                     {
1820                       layer_info=DestroyLayerInfo(layer_info,number_layers);
1821                       ThrowBinaryException(CorruptImageError,
1822                         "UnexpectedEndOfFile",image->filename);
1823                     }
1824                 }
1825                 continue;
1826               }
1827
1828             if (image->debug != MagickFalse)
1829               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1830                 "  reading data for layer %.20g",(double) i);
1831
1832             status=ReadPSDLayer(image,image_info,psd_info,&layer_info[i],
1833               exception);
1834             if (status == MagickFalse)
1835               break;
1836
1837             status=SetImageProgress(image,LoadImagesTag,i,(MagickSizeType)
1838               number_layers);
1839             if (status == MagickFalse)
1840               break;
1841           }
1842         }
1843
1844       if (status != MagickFalse)
1845         {
1846           for (i=0; i < number_layers; i++)
1847           {
1848             if (layer_info[i].image == (Image *) NULL)
1849               {
1850                 for (j=i; j < number_layers - 1; j++)
1851                   layer_info[j] = layer_info[j+1];
1852                 number_layers--;
1853                 i--;
1854               }
1855           }
1856
1857           if (number_layers > 0)
1858             {
1859               for (i=0; i < number_layers; i++)
1860               {
1861                 if (i > 0)
1862                   layer_info[i].image->previous=layer_info[i-1].image;
1863                 if (i < (number_layers-1))
1864                   layer_info[i].image->next=layer_info[i+1].image;
1865                 layer_info[i].image->page=layer_info[i].page;
1866               }
1867               image->next=layer_info[0].image;
1868               layer_info[0].image->previous=image;
1869             }
1870           layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
1871         }
1872       else
1873         layer_info=DestroyLayerInfo(layer_info,number_layers);
1874     }
1875
1876   return(status);
1877 }
1878
1879 ModuleExport MagickBooleanType ReadPSDLayers(Image *image,
1880   const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception)
1881 {
1882   PolicyDomain
1883     domain;
1884
1885   PolicyRights
1886     rights;
1887
1888   domain=CoderPolicyDomain;
1889   rights=ReadPolicyRights;
1890   if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
1891     return(MagickTrue);
1892   return(ReadPSDLayersInternal(image,image_info,psd_info,MagickFalse,
1893     exception));
1894 }
1895
1896 static MagickBooleanType ReadPSDMergedImage(const ImageInfo *image_info,
1897   Image *image,const PSDInfo *psd_info,ExceptionInfo *exception)
1898 {
1899   MagickOffsetType
1900     *sizes;
1901
1902   MagickBooleanType
1903     status;
1904
1905   PSDCompressionType
1906     compression;
1907
1908   register ssize_t
1909     i;
1910
1911   compression=(PSDCompressionType) ReadBlobMSBShort(image);
1912   image->compression=ConvertPSDCompression(compression);
1913
1914   if (compression != Raw && compression != RLE)
1915     {
1916       (void) ThrowMagickException(exception,GetMagickModule(),
1917         TypeWarning,"CompressionNotSupported","'%.20g'",(double) compression);
1918       return(MagickFalse);
1919     }
1920
1921   sizes=(MagickOffsetType *) NULL;
1922   if (compression == RLE)
1923     {
1924       sizes=ReadPSDRLESizes(image,psd_info,image->rows*psd_info->channels);
1925       if (sizes == (MagickOffsetType *) NULL)
1926         ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1927           image->filename);
1928     }
1929
1930   status=MagickTrue;
1931   for (i=0; i < (ssize_t) psd_info->channels; i++)
1932   {
1933     if (compression == RLE)
1934       status=ReadPSDChannelRLE(image,psd_info,i,sizes+(i*image->rows),
1935         exception);
1936     else
1937       status=ReadPSDChannelRaw(image,psd_info->channels,i,exception);
1938
1939     if (status != MagickFalse)
1940       status=SetImageProgress(image,LoadImagesTag,i,psd_info->channels);
1941
1942     if (status == MagickFalse)
1943       break;
1944   }
1945
1946   if ((status != MagickFalse) && (image->colorspace == CMYKColorspace))
1947     status=NegateCMYK(image,exception);
1948
1949   if (status != MagickFalse)
1950     status=CorrectPSDAlphaBlend(image_info,image,exception);
1951
1952   sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
1953
1954   return(status);
1955 }
1956
1957 static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
1958 {
1959   Image
1960     *image;
1961
1962   MagickBooleanType
1963     has_merged_image,
1964     skip_layers;
1965
1966   MagickOffsetType
1967     offset;
1968
1969   MagickSizeType
1970     length;
1971
1972   MagickBooleanType
1973     status;
1974
1975   PSDInfo
1976     psd_info;
1977
1978   register ssize_t
1979     i;
1980
1981   ssize_t
1982     count;
1983
1984   unsigned char
1985     *data;
1986
1987   /*
1988     Open image file.
1989   */
1990   assert(image_info != (const ImageInfo *) NULL);
1991   assert(image_info->signature == MagickCoreSignature);
1992   if (image_info->debug != MagickFalse)
1993     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1994       image_info->filename);
1995   assert(exception != (ExceptionInfo *) NULL);
1996   assert(exception->signature == MagickCoreSignature);
1997
1998   image=AcquireImage(image_info,exception);
1999   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2000   if (status == MagickFalse)
2001     {
2002       image=DestroyImageList(image);
2003       return((Image *) NULL);
2004     }
2005   /*
2006     Read image header.
2007   */
2008   image->endian=MSBEndian;
2009   count=ReadBlob(image,4,(unsigned char *) psd_info.signature);
2010   psd_info.version=ReadBlobMSBShort(image);
2011   if ((count == 0) || (LocaleNCompare(psd_info.signature,"8BPS",4) != 0) ||
2012       ((psd_info.version != 1) && (psd_info.version != 2)))
2013     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2014   (void) ReadBlob(image,6,psd_info.reserved);
2015   psd_info.channels=ReadBlobMSBShort(image);
2016   if (psd_info.channels > MaxPSDChannels)
2017     ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded");
2018   psd_info.rows=ReadBlobMSBLong(image);
2019   psd_info.columns=ReadBlobMSBLong(image);
2020   if ((psd_info.version == 1) && ((psd_info.rows > 30000) ||
2021       (psd_info.columns > 30000)))
2022     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2023   psd_info.depth=ReadBlobMSBShort(image);
2024   if ((psd_info.depth != 1) && (psd_info.depth != 8) && (psd_info.depth != 16))
2025     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2026   psd_info.mode=ReadBlobMSBShort(image);
2027   if (image->debug != MagickFalse)
2028     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2029       "  Image is %.20g x %.20g with channels=%.20g, depth=%.20g, mode=%s",
2030       (double) psd_info.columns,(double) psd_info.rows,(double)
2031       psd_info.channels,(double) psd_info.depth,ModeToString((PSDImageType)
2032       psd_info.mode));
2033   if (EOFBlob(image) != MagickFalse)
2034     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2035   /*
2036     Initialize image.
2037   */
2038   image->depth=psd_info.depth;
2039   image->columns=psd_info.columns;
2040   image->rows=psd_info.rows;
2041   status=SetImageExtent(image,image->columns,image->rows,exception);
2042   if (status == MagickFalse)
2043     return(DestroyImageList(image));
2044   if (SetImageBackgroundColor(image,exception) == MagickFalse)
2045     {
2046       image=DestroyImageList(image);
2047       return((Image *) NULL);
2048     }
2049   if (psd_info.mode == LabMode)
2050     SetImageColorspace(image,LabColorspace,exception);
2051   if (psd_info.mode == CMYKMode)
2052     {
2053       SetImageColorspace(image,CMYKColorspace,exception);
2054       if (psd_info.channels > 4)
2055         SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2056     }
2057   else if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) ||
2058            (psd_info.mode == DuotoneMode))
2059     {
2060       status=AcquireImageColormap(image,psd_info.depth != 16 ? 256 : 65536,
2061         exception);
2062       if (status == MagickFalse)
2063         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2064       if (image->debug != MagickFalse)
2065         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2066           "  Image colormap allocated");
2067       SetImageColorspace(image,GRAYColorspace,exception);
2068       if (psd_info.channels > 1)
2069         SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2070     }
2071   else
2072     if (psd_info.channels > 3)
2073       SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2074   /*
2075     Read PSD raster colormap only present for indexed and duotone images.
2076   */
2077   length=ReadBlobMSBLong(image);
2078   if (length != 0)
2079     {
2080       if (image->debug != MagickFalse)
2081         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2082           "  reading colormap");
2083       if (psd_info.mode == DuotoneMode)
2084         {
2085           /*
2086             Duotone image data;  the format of this data is undocumented.
2087           */
2088           data=(unsigned char *) AcquireQuantumMemory((size_t) length,
2089             sizeof(*data));
2090           if (data == (unsigned char *) NULL)
2091             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2092           (void) ReadBlob(image,(size_t) length,data);
2093           data=(unsigned char *) RelinquishMagickMemory(data);
2094         }
2095       else
2096         {
2097           size_t
2098             number_colors;
2099
2100           /*
2101             Read PSD raster colormap.
2102           */
2103           number_colors=length/3;
2104           if (number_colors > 65536)
2105             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2106           if (AcquireImageColormap(image,number_colors,exception) == MagickFalse)
2107             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2108           for (i=0; i < (ssize_t) image->colors; i++)
2109             image->colormap[i].red=ScaleCharToQuantum((unsigned char)
2110               ReadBlobByte(image));
2111           for (i=0; i < (ssize_t) image->colors; i++)
2112             image->colormap[i].green=ScaleCharToQuantum((unsigned char)
2113               ReadBlobByte(image));
2114           for (i=0; i < (ssize_t) image->colors; i++)
2115             image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
2116               ReadBlobByte(image));
2117           image->alpha_trait=UndefinedPixelTrait;
2118         }
2119     }
2120   if ((image->depth == 1) && (image->storage_class != PseudoClass))
2121     ThrowReaderException(CorruptImageError, "ImproperImageHeader");
2122   has_merged_image=MagickTrue;
2123   length=ReadBlobMSBLong(image);
2124   if (length != 0)
2125     {
2126       unsigned char
2127         *blocks;
2128
2129       /*
2130         Image resources block.
2131       */
2132       if (image->debug != MagickFalse)
2133         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2134           "  reading image resource blocks - %.20g bytes",(double)
2135           ((MagickOffsetType) length));
2136       if (length > GetBlobSize(image))
2137         ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
2138       blocks=(unsigned char *) AcquireQuantumMemory((size_t) length,
2139         sizeof(*blocks));
2140       if (blocks == (unsigned char *) NULL)
2141         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2142       count=ReadBlob(image,(size_t) length,blocks);
2143       if ((count != (ssize_t) length) || (length < 4) ||
2144           (LocaleNCompare((char *) blocks,"8BIM",4) != 0))
2145         {
2146           blocks=(unsigned char *) RelinquishMagickMemory(blocks);
2147           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2148         }
2149       ParseImageResourceBlocks(image,blocks,(size_t) length,&has_merged_image,
2150         exception);
2151       blocks=(unsigned char *) RelinquishMagickMemory(blocks);
2152     }
2153   /*
2154     Layer and mask block.
2155   */
2156   length=GetPSDSize(&psd_info,image);
2157   if (length == 8)
2158     {
2159       length=ReadBlobMSBLong(image);
2160       length=ReadBlobMSBLong(image);
2161     }
2162   offset=TellBlob(image);
2163   skip_layers=MagickFalse;
2164   if ((image_info->number_scenes == 1) && (image_info->scene == 0) &&
2165       (has_merged_image != MagickFalse))
2166     {
2167       if (image->debug != MagickFalse)
2168         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2169           "  read composite only");
2170       skip_layers=MagickTrue;
2171     }
2172   if (length == 0)
2173     {
2174       if (image->debug != MagickFalse)
2175         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2176           "  image has no layers");
2177     }
2178   else
2179     {
2180       if (ReadPSDLayersInternal(image,image_info,&psd_info,skip_layers,
2181             exception) != MagickTrue)
2182         {
2183           (void) CloseBlob(image);
2184           image=DestroyImageList(image);
2185           return((Image *) NULL);
2186         }
2187
2188       /*
2189          Skip the rest of the layer and mask information.
2190       */
2191       SeekBlob(image,offset+length,SEEK_SET);
2192     }
2193   /*
2194     If we are only "pinging" the image, then we're done - so return.
2195   */
2196   if (EOFBlob(image) != MagickFalse)
2197     ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
2198   if (image_info->ping != MagickFalse)
2199     {
2200       (void) CloseBlob(image);
2201       return(GetFirstImageInList(image));
2202     }
2203   /*
2204     Read the precombined layer, present for PSD < 4 compatibility.
2205   */
2206   if (image->debug != MagickFalse)
2207     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2208       "  reading the precombined layer");
2209   if ((has_merged_image != MagickFalse) || (GetImageListLength(image) == 1))
2210     has_merged_image=(MagickBooleanType) ReadPSDMergedImage(image_info,image,
2211       &psd_info,exception);
2212   if ((has_merged_image == MagickFalse) && (GetImageListLength(image) == 1) &&
2213       (length != 0))
2214     {
2215       SeekBlob(image,offset,SEEK_SET);
2216       status=ReadPSDLayersInternal(image,image_info,&psd_info,MagickFalse,
2217         exception);
2218       if (status != MagickTrue)
2219         {
2220           (void) CloseBlob(image);
2221           image=DestroyImageList(image);
2222           return((Image *) NULL);
2223         }
2224     }
2225   if (has_merged_image == MagickFalse)
2226     {
2227       Image
2228         *merged;
2229
2230       if (GetImageListLength(image) == 1)
2231         ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
2232       SetImageAlphaChannel(image,TransparentAlphaChannel,exception);
2233       image->background_color.alpha=TransparentAlpha;
2234       image->background_color.alpha_trait=BlendPixelTrait;
2235       merged=MergeImageLayers(image,FlattenLayer,exception);
2236       ReplaceImageInList(&image,merged);
2237     }
2238   (void) CloseBlob(image);
2239   return(GetFirstImageInList(image));
2240 }
2241 \f
2242 /*
2243 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2244 %                                                                             %
2245 %                                                                             %
2246 %                                                                             %
2247 %   R e g i s t e r P S D I m a g e                                           %
2248 %                                                                             %
2249 %                                                                             %
2250 %                                                                             %
2251 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2252 %
2253 %  RegisterPSDImage() adds properties for the PSD image format to
2254 %  the list of supported formats.  The properties include the image format
2255 %  tag, a method to read and/or write the format, whether the format
2256 %  supports the saving of more than one frame to the same file or blob,
2257 %  whether the format supports native in-memory I/O, and a brief
2258 %  description of the format.
2259 %
2260 %  The format of the RegisterPSDImage method is:
2261 %
2262 %      size_t RegisterPSDImage(void)
2263 %
2264 */
2265 ModuleExport size_t RegisterPSDImage(void)
2266 {
2267   MagickInfo
2268     *entry;
2269
2270   entry=AcquireMagickInfo("PSD","PSB","Adobe Large Document Format");
2271   entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2272   entry->encoder=(EncodeImageHandler *) WritePSDImage;
2273   entry->magick=(IsImageFormatHandler *) IsPSD;
2274   entry->flags|=CoderDecoderSeekableStreamFlag;
2275   entry->flags|=CoderEncoderSeekableStreamFlag;
2276   (void) RegisterMagickInfo(entry);
2277   entry=AcquireMagickInfo("PSD","PSD","Adobe Photoshop bitmap");
2278   entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2279   entry->encoder=(EncodeImageHandler *) WritePSDImage;
2280   entry->magick=(IsImageFormatHandler *) IsPSD;
2281   entry->flags|=CoderDecoderSeekableStreamFlag;
2282   entry->flags|=CoderEncoderSeekableStreamFlag;
2283   (void) RegisterMagickInfo(entry);
2284   return(MagickImageCoderSignature);
2285 }
2286 \f
2287 /*
2288 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2289 %                                                                             %
2290 %                                                                             %
2291 %                                                                             %
2292 %   U n r e g i s t e r P S D I m a g e                                       %
2293 %                                                                             %
2294 %                                                                             %
2295 %                                                                             %
2296 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2297 %
2298 %  UnregisterPSDImage() removes format registrations made by the
2299 %  PSD module from the list of supported formats.
2300 %
2301 %  The format of the UnregisterPSDImage method is:
2302 %
2303 %      UnregisterPSDImage(void)
2304 %
2305 */
2306 ModuleExport void UnregisterPSDImage(void)
2307 {
2308   (void) UnregisterMagickInfo("PSB");
2309   (void) UnregisterMagickInfo("PSD");
2310 }
2311 \f
2312 /*
2313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2314 %                                                                             %
2315 %                                                                             %
2316 %                                                                             %
2317 %   W r i t e P S D I m a g e                                                 %
2318 %                                                                             %
2319 %                                                                             %
2320 %                                                                             %
2321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2322 %
2323 %  WritePSDImage() writes an image in the Adobe Photoshop encoded image format.
2324 %
2325 %  The format of the WritePSDImage method is:
2326 %
2327 %      MagickBooleanType WritePSDImage(const ImageInfo *image_info,Image *image,
2328 %        ExceptionInfo *exception)
2329 %
2330 %  A description of each parameter follows.
2331 %
2332 %    o image_info: the image info.
2333 %
2334 %    o image:  The image.
2335 %
2336 %    o exception: return any errors or warnings in this structure.
2337 %
2338 */
2339
2340 static inline ssize_t SetPSDOffset(const PSDInfo *psd_info,Image *image,
2341   const size_t offset)
2342 {
2343   if (psd_info->version == 1)
2344     return(WriteBlobMSBShort(image,(unsigned short) offset));
2345   return(WriteBlobMSBLong(image,(unsigned short) offset));
2346 }
2347
2348 static inline ssize_t WritePSDOffset(const PSDInfo *psd_info,Image *image,
2349   const MagickSizeType size,const MagickSizeType offset)
2350 {
2351   MagickSizeType
2352     current_offset;
2353
2354   ssize_t
2355     result;
2356
2357   current_offset=TellBlob(image);
2358   SeekBlob(image,offset,SEEK_SET);
2359   if (psd_info->version == 1)
2360     result=WriteBlobMSBShort(image,(unsigned short) size);
2361   else
2362     result=(WriteBlobMSBLong(image,(unsigned short) size));
2363   SeekBlob(image,current_offset,SEEK_SET);
2364   return(result);
2365 }
2366
2367 static inline ssize_t SetPSDSize(const PSDInfo *psd_info,Image *image,
2368   const MagickSizeType size)
2369 {
2370   if (psd_info->version == 1)
2371     return(WriteBlobLong(image,(unsigned int) size));
2372   return(WriteBlobLongLong(image,size));
2373 }
2374
2375 static inline ssize_t WritePSDSize(const PSDInfo *psd_info,Image *image,
2376   const MagickSizeType size,const MagickSizeType offset)
2377 {
2378   MagickSizeType
2379     current_offset;
2380
2381   ssize_t
2382     result;
2383
2384   current_offset=TellBlob(image);
2385   SeekBlob(image,offset,SEEK_SET);
2386   result=SetPSDSize(psd_info, image, size);
2387   SeekBlob(image,current_offset,SEEK_SET);
2388   return(result);
2389 }
2390
2391 static size_t PSDPackbitsEncodeImage(Image *image,const size_t length,
2392   const unsigned char *pixels,unsigned char *compact_pixels,
2393   ExceptionInfo *exception)
2394 {
2395   int
2396     count;
2397
2398   register ssize_t
2399     i,
2400     j;
2401
2402   register unsigned char
2403     *q;
2404
2405   unsigned char
2406     *packbits;
2407
2408   /*
2409     Compress pixels with Packbits encoding.
2410   */
2411   assert(image != (Image *) NULL);
2412   assert(image->signature == MagickCoreSignature);
2413   if (image->debug != MagickFalse)
2414     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2415   assert(pixels != (unsigned char *) NULL);
2416   assert(compact_pixels != (unsigned char *) NULL);
2417   packbits=(unsigned char *) AcquireQuantumMemory(128UL,sizeof(*packbits));
2418   if (packbits == (unsigned char *) NULL)
2419     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2420       image->filename);
2421   q=compact_pixels;
2422   for (i=(ssize_t) length; i != 0; )
2423   {
2424     switch (i)
2425     {
2426       case 1:
2427       {
2428         i--;
2429         *q++=(unsigned char) 0;
2430         *q++=(*pixels);
2431         break;
2432       }
2433       case 2:
2434       {
2435         i-=2;
2436         *q++=(unsigned char) 1;
2437         *q++=(*pixels);
2438         *q++=pixels[1];
2439         break;
2440       }
2441       case 3:
2442       {
2443         i-=3;
2444         if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2445           {
2446             *q++=(unsigned char) ((256-3)+1);
2447             *q++=(*pixels);
2448             break;
2449           }
2450         *q++=(unsigned char) 2;
2451         *q++=(*pixels);
2452         *q++=pixels[1];
2453         *q++=pixels[2];
2454         break;
2455       }
2456       default:
2457       {
2458         if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2459           {
2460             /*
2461               Packed run.
2462             */
2463             count=3;
2464             while (((ssize_t) count < i) && (*pixels == *(pixels+count)))
2465             {
2466               count++;
2467               if (count >= 127)
2468                 break;
2469             }
2470             i-=count;
2471             *q++=(unsigned char) ((256-count)+1);
2472             *q++=(*pixels);
2473             pixels+=count;
2474             break;
2475           }
2476         /*
2477           Literal run.
2478         */
2479         count=0;
2480         while ((*(pixels+count) != *(pixels+count+1)) ||
2481                (*(pixels+count+1) != *(pixels+count+2)))
2482         {
2483           packbits[count+1]=pixels[count];
2484           count++;
2485           if (((ssize_t) count >= (i-3)) || (count >= 127))
2486             break;
2487         }
2488         i-=count;
2489         *packbits=(unsigned char) (count-1);
2490         for (j=0; j <= (ssize_t) count; j++)
2491           *q++=packbits[j];
2492         pixels+=count;
2493         break;
2494       }
2495     }
2496   }
2497   *q++=(unsigned char) 128;  /* EOD marker */
2498   packbits=(unsigned char *) RelinquishMagickMemory(packbits);
2499   return((size_t) (q-compact_pixels));
2500 }
2501
2502 static size_t WriteCompressionStart(const PSDInfo *psd_info,Image *image,
2503   const Image *next_image,const ssize_t channels)
2504 {
2505   size_t
2506     length;
2507
2508   ssize_t
2509     i,
2510     y;
2511
2512   if (next_image->compression == RLECompression)
2513     {
2514       length=WriteBlobShort(image,RLE);
2515       for (i=0; i < channels; i++)
2516         for (y=0; y < (ssize_t) next_image->rows; y++)
2517           length+=SetPSDOffset(psd_info,image,0);
2518     }
2519 #ifdef MAGICKCORE_ZLIB_DELEGATE
2520   else if (next_image->compression == ZipCompression)
2521     length=WriteBlobShort(image,ZipWithoutPrediction);
2522 #endif
2523   else
2524     length=WriteBlobShort(image,Raw);
2525   return(length);
2526 }
2527
2528 static size_t WritePSDChannel(const PSDInfo *psd_info,
2529   const ImageInfo *image_info,Image *image,Image *next_image,
2530   const QuantumType quantum_type, unsigned char *compact_pixels,
2531   MagickOffsetType size_offset,const MagickBooleanType separate,
2532   ExceptionInfo *exception)
2533 {
2534   int
2535     y;
2536
2537   MagickBooleanType
2538     monochrome;
2539
2540   QuantumInfo
2541     *quantum_info;
2542
2543   register const Quantum
2544     *p;
2545
2546   register ssize_t
2547     i;
2548
2549   size_t
2550     count,
2551     length;
2552
2553   unsigned char
2554     *pixels;
2555
2556 #ifdef MAGICKCORE_ZLIB_DELEGATE
2557
2558 #define CHUNK 16384
2559
2560   int
2561     flush,
2562     level;
2563
2564   unsigned char
2565     *compressed_pixels;
2566
2567   z_stream
2568     stream;
2569
2570   compressed_pixels=(unsigned char *) NULL;
2571   flush=Z_NO_FLUSH;
2572 #endif
2573   count=0;
2574   if (separate != MagickFalse)
2575     {
2576       size_offset=TellBlob(image)+2;
2577       count+=WriteCompressionStart(psd_info,image,next_image,1);
2578     }
2579   if (next_image->depth > 8)
2580     next_image->depth=16;
2581   monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
2582     MagickTrue : MagickFalse;
2583   quantum_info=AcquireQuantumInfo(image_info,next_image);
2584   if (quantum_info == (QuantumInfo *) NULL)
2585     return(0);
2586   pixels=(unsigned char *) GetQuantumPixels(quantum_info);
2587 #ifdef MAGICKCORE_ZLIB_DELEGATE
2588   if (next_image->compression == ZipCompression)
2589     {
2590       compressed_pixels=(unsigned char *) AcquireQuantumMemory(CHUNK,
2591         sizeof(*compressed_pixels));
2592       if (compressed_pixels == (unsigned char *) NULL)
2593         {
2594           quantum_info=DestroyQuantumInfo(quantum_info);
2595           return(0);
2596         }
2597       ResetMagickMemory(&stream,0,sizeof(stream));
2598       stream.data_type=Z_BINARY;
2599       level=Z_DEFAULT_COMPRESSION;
2600       if ((image_info->quality > 0 && image_info->quality < 10))
2601         level=(int) image_info->quality;
2602       if (deflateInit(&stream,level) != Z_OK)
2603         {
2604           quantum_info=DestroyQuantumInfo(quantum_info);
2605           return(0);
2606         }
2607     }
2608 #endif
2609   for (y=0; y < (ssize_t) next_image->rows; y++)
2610   {
2611     p=GetVirtualPixels(next_image,0,y,next_image->columns,1,exception);
2612     if (p == (const Quantum *) NULL)
2613       break;
2614     length=ExportQuantumPixels(next_image,(CacheView *) NULL,quantum_info,
2615       quantum_type,pixels,exception);
2616     if (monochrome != MagickFalse)
2617       for (i=0; i < (ssize_t) length; i++)
2618         pixels[i]=(~pixels[i]);
2619     if (next_image->compression == RLECompression)
2620       {
2621         length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels,
2622           exception);
2623         count+=WriteBlob(image,length,compact_pixels);
2624         size_offset+=WritePSDOffset(psd_info,image,length,size_offset);
2625       }
2626 #ifdef MAGICKCORE_ZLIB_DELEGATE
2627     else if (next_image->compression == ZipCompression)
2628       {
2629         stream.avail_in=(uInt) length;
2630         stream.next_in=(Bytef *) pixels;
2631         if (y == (ssize_t) next_image->rows-1)
2632           flush=Z_FINISH;
2633         do {
2634             stream.avail_out=(uInt) CHUNK;
2635             stream.next_out=(Bytef *) compressed_pixels;
2636             if (deflate(&stream,flush) == Z_STREAM_ERROR)
2637               break;
2638             length=(size_t) CHUNK-stream.avail_out;
2639             if (length > 0)
2640               count+=WriteBlob(image,length,compressed_pixels);
2641         } while (stream.avail_out == 0);
2642       }
2643 #endif
2644     else
2645       count+=WriteBlob(image,length,pixels);
2646   }
2647 #ifdef MAGICKCORE_ZLIB_DELEGATE
2648   if (next_image->compression == ZipCompression)
2649     {
2650       (void) deflateEnd(&stream);
2651       compressed_pixels=(unsigned char *) RelinquishMagickMemory(
2652         compressed_pixels);
2653     }
2654 #endif
2655   quantum_info=DestroyQuantumInfo(quantum_info);
2656   return(count);
2657 }
2658
2659 static unsigned char *AcquireCompactPixels(const Image *image,
2660   ExceptionInfo *exception)
2661 {
2662   size_t
2663     packet_size;
2664
2665   unsigned char
2666     *compact_pixels;
2667
2668   packet_size=image->depth > 8UL ? 2UL : 1UL;
2669   compact_pixels=(unsigned char *) AcquireQuantumMemory((9*
2670     image->columns)+1,packet_size*sizeof(*compact_pixels));
2671   if (compact_pixels == (unsigned char *) NULL)
2672     {
2673       (void) ThrowMagickException(exception,GetMagickModule(),
2674         ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2675     }
2676   return(compact_pixels);
2677 }
2678
2679 static size_t WritePSDChannels(const PSDInfo *psd_info,
2680   const ImageInfo *image_info,Image *image,Image *next_image,
2681   MagickOffsetType size_offset,const MagickBooleanType separate,
2682   ExceptionInfo *exception)
2683 {
2684   Image
2685     *mask;
2686
2687   MagickOffsetType
2688     rows_offset;
2689
2690   size_t
2691     channels,
2692     count,
2693     length,
2694     offset_length;
2695
2696   unsigned char
2697     *compact_pixels;
2698
2699   count=0;
2700   offset_length=0;
2701   rows_offset=0;
2702   compact_pixels=(unsigned char *) NULL;
2703   if (next_image->compression == RLECompression)
2704     {
2705       compact_pixels=AcquireCompactPixels(next_image,exception);
2706       if (compact_pixels == (unsigned char *) NULL)
2707         return(0);
2708     }
2709   channels=1;
2710   if (separate == MagickFalse)
2711     {
2712       if (next_image->storage_class != PseudoClass)
2713         {
2714           if (IsImageGray(next_image) == MagickFalse)
2715             channels=next_image->colorspace == CMYKColorspace ? 4 : 3;
2716           if (next_image->alpha_trait != UndefinedPixelTrait)
2717             channels++;
2718         }
2719       rows_offset=TellBlob(image)+2;
2720       count+=WriteCompressionStart(psd_info,image,next_image,channels);
2721       offset_length=(next_image->rows*(psd_info->version == 1 ? 2 : 4));
2722     }
2723   size_offset+=2;
2724   if (next_image->storage_class == PseudoClass)
2725     {
2726       length=WritePSDChannel(psd_info,image_info,image,next_image,
2727         IndexQuantum,compact_pixels,rows_offset,separate,exception);
2728       if (separate != MagickFalse)
2729         size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2730       else
2731         rows_offset+=offset_length;
2732       count+=length;
2733     }
2734   else
2735     {
2736       if (IsImageGray(next_image) != MagickFalse)
2737         {
2738           length=WritePSDChannel(psd_info,image_info,image,next_image,
2739             GrayQuantum,compact_pixels,rows_offset,separate,exception);
2740           if (separate != MagickFalse)
2741             size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2742           else
2743             rows_offset+=offset_length;
2744           count+=length;
2745         }
2746       else
2747         {
2748           if (next_image->colorspace == CMYKColorspace)
2749             (void) NegateCMYK(next_image,exception);
2750
2751           length=WritePSDChannel(psd_info,image_info,image,next_image,
2752             RedQuantum,compact_pixels,rows_offset,separate,exception);
2753           if (separate != MagickFalse)
2754             size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2755           else
2756             rows_offset+=offset_length;
2757           count+=length;
2758
2759           length=WritePSDChannel(psd_info,image_info,image,next_image,
2760             GreenQuantum,compact_pixels,rows_offset,separate,exception);
2761           if (separate != MagickFalse)
2762             size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2763           else
2764             rows_offset+=offset_length;
2765           count+=length;
2766
2767           length=WritePSDChannel(psd_info,image_info,image,next_image,
2768             BlueQuantum,compact_pixels,rows_offset,separate,exception);
2769           if (separate != MagickFalse)
2770             size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2771           else
2772             rows_offset+=offset_length;
2773           count+=length;
2774
2775           if (next_image->colorspace == CMYKColorspace)
2776             {
2777               length=WritePSDChannel(psd_info,image_info,image,next_image,
2778                 BlackQuantum,compact_pixels,rows_offset,separate,exception);
2779               if (separate != MagickFalse)
2780                 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2781               else
2782                 rows_offset+=offset_length;
2783               count+=length;
2784             }
2785         }
2786       if (next_image->alpha_trait != UndefinedPixelTrait)
2787         {
2788           length=WritePSDChannel(psd_info,image_info,image,next_image,
2789             AlphaQuantum,compact_pixels,rows_offset,separate,exception);
2790           if (separate != MagickFalse)
2791             size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2792           else
2793             rows_offset+=offset_length;
2794           count+=length;
2795         }
2796     }
2797   compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
2798   if (next_image->colorspace == CMYKColorspace)
2799     (void) NegateCMYK(next_image,exception);
2800   if (separate != MagickFalse)
2801     {
2802       const char
2803         *property;
2804
2805       property=GetImageArtifact(next_image,"psd:opacity-mask");
2806       if (property != (const char *) NULL)
2807         {
2808           mask=(Image *) GetImageRegistry(ImageRegistryType,property,
2809             exception);
2810           if (mask != (Image *) NULL)
2811             {
2812               if (mask->compression == RLECompression)
2813                 {
2814                   compact_pixels=AcquireCompactPixels(mask,exception);
2815                   if (compact_pixels == (unsigned char *) NULL)
2816                     return(0);
2817                 }
2818               length=WritePSDChannel(psd_info,image_info,image,mask,
2819                 RedQuantum,compact_pixels,rows_offset,MagickTrue,exception);
2820               (void) WritePSDSize(psd_info,image,length,size_offset);
2821               count+=length;
2822               compact_pixels=(unsigned char *) RelinquishMagickMemory(
2823                 compact_pixels);
2824             }
2825         }
2826     }
2827   return(count);
2828 }
2829
2830 static size_t WritePascalString(Image *image,const char *value,size_t padding)
2831 {
2832   size_t
2833     count,
2834     length;
2835
2836   register ssize_t
2837     i;
2838
2839   /*
2840     Max length is 255.
2841   */
2842   count=0;
2843   length=(strlen(value) > 255UL ) ? 255UL : strlen(value);
2844   if (length ==  0)
2845     count+=WriteBlobByte(image,0);
2846   else
2847     {
2848       count+=WriteBlobByte(image,(unsigned char) length);
2849       count+=WriteBlob(image,length,(const unsigned char *) value);
2850     }
2851   length++;
2852   if ((length % padding) == 0)
2853     return(count);
2854   for (i=0; i < (ssize_t) (padding-(length % padding)); i++)
2855     count+=WriteBlobByte(image,0);
2856   return(count);
2857 }
2858
2859 static void WriteResolutionResourceBlock(Image *image)
2860 {
2861   double
2862     x_resolution,
2863     y_resolution;
2864
2865   unsigned short
2866     units;
2867
2868   if (image->units == PixelsPerCentimeterResolution)
2869     {
2870       x_resolution=2.54*65536.0*image->resolution.x+0.5;
2871       y_resolution=2.54*65536.0*image->resolution.y+0.5;
2872       units=2;
2873     }
2874   else
2875     {
2876       x_resolution=65536.0*image->resolution.x+0.5;
2877       y_resolution=65536.0*image->resolution.y+0.5;
2878       units=1;
2879     }
2880   (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
2881   (void) WriteBlobMSBShort(image,0x03ED);
2882   (void) WriteBlobMSBShort(image,0);
2883   (void) WriteBlobMSBLong(image,16); /* resource size */
2884   (void) WriteBlobMSBLong(image,(unsigned int) (x_resolution+0.5));
2885   (void) WriteBlobMSBShort(image,units); /* horizontal resolution unit */
2886   (void) WriteBlobMSBShort(image,units); /* width unit */
2887   (void) WriteBlobMSBLong(image,(unsigned int) (y_resolution+0.5));
2888   (void) WriteBlobMSBShort(image,units); /* vertical resolution unit */
2889   (void) WriteBlobMSBShort(image,units); /* height unit */
2890 }
2891
2892 static inline size_t WriteChannelSize(const PSDInfo *psd_info,Image *image,
2893   const signed short channel)
2894 {
2895   size_t
2896     count;
2897
2898   count=(size_t) WriteBlobShort(image,channel);
2899   count+=SetPSDSize(psd_info,image,0);
2900   return(count);
2901 }
2902
2903 static void RemoveICCProfileFromResourceBlock(StringInfo *bim_profile)
2904 {
2905   register const unsigned char
2906     *p;
2907
2908   size_t
2909     length;
2910
2911   unsigned char
2912     *datum;
2913
2914   unsigned int
2915     count,
2916     long_sans;
2917
2918   unsigned short
2919     id,
2920     short_sans;
2921
2922   length=GetStringInfoLength(bim_profile);
2923   if (length < 16)
2924     return;
2925   datum=GetStringInfoDatum(bim_profile);
2926   for (p=datum; (p >= datum) && (p < (datum+length-16)); )
2927   {
2928     register unsigned char
2929       *q;
2930
2931     q=(unsigned char *) p;
2932     if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
2933       break;
2934     p=PushLongPixel(MSBEndian,p,&long_sans);
2935     p=PushShortPixel(MSBEndian,p,&id);
2936     p=PushShortPixel(MSBEndian,p,&short_sans);
2937     p=PushLongPixel(MSBEndian,p,&count);
2938     if (id == 0x0000040f)
2939       {
2940         ssize_t
2941           quantum;
2942
2943         quantum=PSDQuantum(count)+12;
2944         if ((quantum >= 12) && (quantum < (ssize_t) length))
2945           {
2946             if ((q+quantum < (datum+length-16)))
2947               (void) CopyMagickMemory(q,q+quantum,length-quantum-(q-datum));
2948             SetStringInfoLength(bim_profile,length-quantum);
2949           }
2950         break;
2951       }
2952     p+=count;
2953     if ((count & 0x01) != 0)
2954       p++;
2955   }
2956 }
2957
2958 static void RemoveResolutionFromResourceBlock(StringInfo *bim_profile)
2959 {
2960   register const unsigned char
2961     *p;
2962
2963   size_t
2964     length;
2965
2966   unsigned char
2967     *datum;
2968
2969   unsigned int
2970     count,
2971     long_sans;
2972
2973   unsigned short
2974     id,
2975     short_sans;
2976
2977   length=GetStringInfoLength(bim_profile);
2978   if (length < 16)
2979     return;
2980   datum=GetStringInfoDatum(bim_profile);
2981   for (p=datum; (p >= datum) && (p < (datum+length-16)); )
2982   {
2983     register unsigned char
2984       *q;
2985
2986     ssize_t
2987       cnt;
2988
2989     q=(unsigned char *) p;
2990     if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
2991       return;
2992     p=PushLongPixel(MSBEndian,p,&long_sans);
2993     p=PushShortPixel(MSBEndian,p,&id);
2994     p=PushShortPixel(MSBEndian,p,&short_sans);
2995     p=PushLongPixel(MSBEndian,p,&count);
2996     cnt=PSDQuantum(count);
2997     if (cnt < 0)
2998       return;
2999     if ((id == 0x000003ed) && (cnt < (ssize_t) (length-12)))
3000       {
3001         (void) CopyMagickMemory(q,q+cnt+12,length-(cnt+12)-(q-datum));
3002         SetStringInfoLength(bim_profile,length-(cnt+12));
3003         break;
3004       }
3005     p+=count;
3006     if ((count & 0x01) != 0)
3007       p++;
3008   }
3009 }
3010
3011 static const StringInfo *GetAdditionalInformation(const ImageInfo *image_info,
3012   Image *image,ExceptionInfo *exception)
3013 {
3014 #define PSDKeySize 5
3015 #define PSDAllowedLength 36
3016
3017   char
3018     key[PSDKeySize];
3019
3020   /* Whitelist of keys from: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ */
3021   const char
3022     allowed[PSDAllowedLength][PSDKeySize] = {
3023       "blnc", "blwh", "brit", "brst", "clbl", "clrL", "curv", "expA", "FMsk",
3024       "GdFl", "grdm", "hue ", "hue2", "infx", "knko", "lclr", "levl", "lnsr",
3025       "lfx2", "luni", "lrFX", "lspf", "lyid", "lyvr", "mixr", "nvrt", "phfl",
3026       "post", "PtFl", "selc", "shpa", "sn2P", "SoCo", "thrs", "tsly", "vibA"
3027     },
3028     *option;
3029
3030   const StringInfo
3031     *info;
3032
3033   MagickBooleanType
3034     found;
3035
3036   register size_t
3037     i;
3038
3039   size_t
3040     remaining_length,
3041     length;
3042
3043   StringInfo
3044     *profile;
3045
3046   unsigned char
3047     *p;
3048
3049   unsigned int
3050     size;
3051
3052   info=GetImageProfile(image,"psd:additional-info");
3053   if (info == (const StringInfo *) NULL)
3054     return((const StringInfo *) NULL);
3055   option=GetImageOption(image_info,"psd:additional-info");
3056   if (LocaleCompare(option,"all") == 0)
3057     return(info);
3058   if (LocaleCompare(option,"selective") != 0)
3059     {
3060       profile=RemoveImageProfile(image,"psd:additional-info");
3061       return(DestroyStringInfo(profile));
3062     }
3063   length=GetStringInfoLength(info);
3064   p=GetStringInfoDatum(info);
3065   remaining_length=length;
3066   length=0;
3067   while (remaining_length >= 12)
3068   {
3069     /* skip over signature */
3070     p+=4;
3071     key[0]=(*p++);
3072     key[1]=(*p++);
3073     key[2]=(*p++);
3074     key[3]=(*p++);
3075     key[4]='\0';
3076     size=(unsigned int) (*p++) << 24;
3077     size|=(unsigned int) (*p++) << 16;
3078     size|=(unsigned int) (*p++) << 8;
3079     size|=(unsigned int) (*p++);
3080     size=size & 0xffffffff;
3081     remaining_length-=12;
3082     if ((size_t) size > remaining_length)
3083       return((const StringInfo *) NULL);
3084     found=MagickFalse;
3085     for (i=0; i < PSDAllowedLength; i++)
3086     {
3087       if (LocaleNCompare(key,allowed[i],PSDKeySize) != 0)
3088         continue;
3089
3090       found=MagickTrue;
3091       break;
3092     }
3093     remaining_length-=(size_t) size;
3094     if (found == MagickFalse)
3095       {
3096         if (remaining_length > 0)
3097           p=(unsigned char *) CopyMagickMemory(p-12,p+size,remaining_length);
3098         continue;
3099       }
3100     length+=(size_t) size+12;
3101     p+=size;
3102   }
3103   profile=RemoveImageProfile(image,"psd:additional-info");
3104   if (length == 0)
3105     return(DestroyStringInfo(profile));
3106   SetStringInfoLength(profile,(const size_t) length);
3107   SetImageProfile(image,"psd:additional-info",info,exception);
3108   return(profile);
3109 }
3110
3111 static MagickBooleanType WritePSDLayersInternal(Image *image,
3112   const ImageInfo *image_info,const PSDInfo *psd_info,size_t *layers_size,
3113   ExceptionInfo *exception)
3114 {
3115   char
3116     layer_name[MagickPathExtent];
3117
3118   const char
3119     *property;
3120
3121   const StringInfo
3122     *info;
3123
3124   Image
3125     *base_image,
3126     *next_image;
3127
3128   MagickBooleanType
3129     status;
3130
3131   MagickOffsetType
3132     *layer_size_offsets,
3133     size_offset;
3134
3135   register ssize_t
3136     i;
3137
3138   size_t
3139     layer_count,
3140     layer_index,
3141     length,
3142     name_length,
3143     rounded_size,
3144     size;
3145
3146   status=MagickTrue;
3147   base_image=GetNextImageInList(image);
3148   if (base_image == (Image *) NULL)
3149     base_image=image;
3150   size=0;
3151   size_offset=TellBlob(image);
3152   SetPSDSize(psd_info,image,0);
3153   layer_count=0;
3154   for (next_image=base_image; next_image != NULL; )
3155   {
3156     layer_count++;
3157     next_image=GetNextImageInList(next_image);
3158   }
3159   if (image->alpha_trait != UndefinedPixelTrait)
3160     size+=WriteBlobShort(image,-(unsigned short) layer_count);
3161   else
3162     size+=WriteBlobShort(image,(unsigned short) layer_count);
3163   layer_size_offsets=(MagickOffsetType *) AcquireQuantumMemory(
3164     (size_t) layer_count,sizeof(MagickOffsetType));
3165   if (layer_size_offsets == (MagickOffsetType *) NULL)
3166     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3167   layer_index=0;
3168   for (next_image=base_image; next_image != NULL; )
3169   {
3170     Image
3171       *mask;
3172
3173     unsigned char
3174       default_color;
3175
3176     unsigned short
3177       channels,
3178       total_channels;
3179
3180     mask=(Image *) NULL;
3181     property=GetImageArtifact(next_image,"psd:opacity-mask");
3182     default_color=0;
3183     if (property != (const char *) NULL)
3184       {
3185         mask=(Image *) GetImageRegistry(ImageRegistryType,property,exception);
3186         default_color=strlen(property) == 9 ? 255 : 0;
3187       }
3188     size+=WriteBlobSignedLong(image,(signed int) next_image->page.y);
3189     size+=WriteBlobSignedLong(image,(signed int) next_image->page.x);
3190     size+=WriteBlobSignedLong(image,(signed int) (next_image->page.y+
3191       next_image->rows));
3192     size+=WriteBlobSignedLong(image,(signed int) (next_image->page.x+
3193       next_image->columns));
3194     channels=1U;
3195     if ((next_image->storage_class != PseudoClass) &&
3196         (IsImageGray(next_image) == MagickFalse))
3197       channels=next_image->colorspace == CMYKColorspace ? 4U : 3U;
3198     total_channels=channels;
3199     if (next_image->alpha_trait != UndefinedPixelTrait)
3200       total_channels++;
3201     if (mask != (Image *) NULL)
3202       total_channels++;
3203     size+=WriteBlobShort(image,total_channels);
3204     layer_size_offsets[layer_index++]=TellBlob(image);
3205     for (i=0; i < (ssize_t) channels; i++)
3206       size+=WriteChannelSize(psd_info,image,(signed short) i);
3207     if (next_image->alpha_trait != UndefinedPixelTrait)
3208       size+=WriteChannelSize(psd_info,image,-1);
3209     if (mask != (Image *) NULL)
3210       size+=WriteChannelSize(psd_info,image,-2);
3211     size+=WriteBlobString(image,image->endian == LSBEndian ? "MIB8" :"8BIM");
3212     size+=WriteBlobString(image,CompositeOperatorToPSDBlendMode(image));
3213     property=GetImageArtifact(next_image,"psd:layer.opacity");
3214     if (property != (const char *) NULL)
3215       {
3216         Quantum
3217           opacity;
3218
3219         opacity=(Quantum) StringToInteger(property);
3220         size+=WriteBlobByte(image,ScaleQuantumToChar(opacity));
3221         (void) ApplyPSDLayerOpacity(next_image,opacity,MagickTrue,exception);
3222       }
3223     else
3224       size+=WriteBlobByte(image,255);
3225     size+=WriteBlobByte(image,0);
3226     size+=WriteBlobByte(image,next_image->compose==NoCompositeOp ?
3227       1 << 0x02 : 1); /* layer properties - visible, etc. */
3228     size+=WriteBlobByte(image,0);
3229     info=GetAdditionalInformation(image_info,next_image,exception);
3230     property=(const char *) GetImageProperty(next_image,"label",exception);
3231     if (property == (const char *) NULL)
3232       {
3233         (void) FormatLocaleString(layer_name,MagickPathExtent,"L%.20g",
3234           (double) layer_index);
3235         property=layer_name;
3236       }
3237     name_length=strlen(property)+1;
3238     if ((name_length % 4) != 0)
3239       name_length+=(4-(name_length % 4));
3240     if (info != (const StringInfo *) NULL)
3241       name_length+=GetStringInfoLength(info);
3242     name_length+=8;
3243     if (mask != (Image *) NULL)
3244       name_length+=20;
3245     size+=WriteBlobLong(image,(unsigned int) name_length);
3246     if (mask == (Image *) NULL)
3247       size+=WriteBlobLong(image,0);
3248     else
3249       {
3250         if (mask->compose != NoCompositeOp)
3251           (void) ApplyPSDOpacityMask(next_image,mask,ScaleCharToQuantum(
3252             default_color),MagickTrue,exception);
3253         mask->page.y+=image->page.y;
3254         mask->page.x+=image->page.x;
3255         size+=WriteBlobLong(image,20);
3256         size+=WriteBlobSignedLong(image,mask->page.y);
3257         size+=WriteBlobSignedLong(image,mask->page.x);
3258         size+=WriteBlobSignedLong(image,(const signed int) mask->rows+
3259           mask->page.y);
3260         size+=WriteBlobSignedLong(image,(const signed int) mask->columns+
3261           mask->page.x);
3262         size+=WriteBlobByte(image,default_color);
3263         size+=WriteBlobByte(image,mask->compose == NoCompositeOp ? 2 : 0);
3264         size+=WriteBlobMSBShort(image,0);
3265       }
3266     size+=WriteBlobLong(image,0);
3267     size+=WritePascalString(image,property,4);
3268     if (info != (const StringInfo *) NULL)
3269       size+=WriteBlob(image,GetStringInfoLength(info),
3270         GetStringInfoDatum(info));
3271     next_image=GetNextImageInList(next_image);
3272   }
3273   /*
3274     Now the image data!
3275   */
3276   next_image=base_image;
3277   layer_index=0;
3278   while (next_image != NULL)
3279   {
3280     length=WritePSDChannels(psd_info,image_info,image,next_image,
3281       layer_size_offsets[layer_index++],MagickTrue,exception);
3282     if (length == 0)
3283       {
3284         status=MagickFalse;
3285         break;
3286       }
3287     size+=length;
3288     next_image=GetNextImageInList(next_image);
3289   }
3290   /*
3291     Write the total size
3292   */
3293   if (layers_size != (size_t*) NULL)
3294     *layers_size=size;
3295   if ((size/2) != ((size+1)/2))
3296     rounded_size=size+1;
3297   else
3298     rounded_size=size;
3299   (void) WritePSDSize(psd_info,image,rounded_size,size_offset);
3300   layer_size_offsets=(MagickOffsetType *) RelinquishMagickMemory(
3301     layer_size_offsets);
3302   /*
3303     Remove the opacity mask from the registry
3304   */
3305   next_image=base_image;
3306   while (next_image != (Image *) NULL)
3307   {
3308     property=GetImageArtifact(next_image,"psd:opacity-mask");
3309     if (property != (const char *) NULL)
3310       DeleteImageRegistry(property);
3311     next_image=GetNextImageInList(next_image);
3312   }
3313
3314   return(status);
3315 }
3316
3317 ModuleExport MagickBooleanType WritePSDLayers(Image * image,
3318   const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception)
3319 {
3320   PolicyDomain
3321     domain;
3322
3323   PolicyRights
3324     rights;
3325
3326   domain=CoderPolicyDomain;
3327   rights=WritePolicyRights;
3328   if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
3329     return(MagickTrue);
3330   return WritePSDLayersInternal(image,image_info,psd_info,(size_t*) NULL,
3331     exception);
3332 }
3333
3334 static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
3335   Image *image,ExceptionInfo *exception)
3336 {
3337   const StringInfo
3338     *icc_profile;
3339
3340   MagickBooleanType
3341     status;
3342
3343   PSDInfo
3344     psd_info;
3345
3346   register ssize_t
3347     i;
3348
3349   size_t
3350     length,
3351     num_channels,
3352     packet_size;
3353
3354   StringInfo
3355     *bim_profile;
3356
3357   /*
3358     Open image file.
3359   */
3360   assert(image_info != (const ImageInfo *) NULL);
3361   assert(image_info->signature == MagickCoreSignature);
3362   assert(image != (Image *) NULL);
3363   assert(image->signature == MagickCoreSignature);
3364   if (image->debug != MagickFalse)
3365     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3366   assert(exception != (ExceptionInfo *) NULL);
3367   assert(exception->signature == MagickCoreSignature);
3368   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
3369   if (status == MagickFalse)
3370     return(status);
3371   packet_size=(size_t) (image->depth > 8 ? 6 : 3);
3372   if (image->alpha_trait != UndefinedPixelTrait)
3373     packet_size+=image->depth > 8 ? 2 : 1;
3374   psd_info.version=1;
3375   if ((LocaleCompare(image_info->magick,"PSB") == 0) ||
3376       (image->columns > 30000) || (image->rows > 30000))
3377     psd_info.version=2;
3378   (void) WriteBlob(image,4,(const unsigned char *) "8BPS");
3379   (void) WriteBlobMSBShort(image,psd_info.version);  /* version */
3380   for (i=1; i <= 6; i++)
3381     (void) WriteBlobByte(image, 0);  /* 6 bytes of reserved */
3382   /* When the image has a color profile it won't be converted to gray scale */
3383   if ((GetImageProfile(image,"icc") == (StringInfo *) NULL) &&
3384       (SetImageGray(image,exception) != MagickFalse))
3385     num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
3386   else
3387     if ((image_info->type != TrueColorType) && (image_info->type !=
3388          TrueColorAlphaType) && (image->storage_class == PseudoClass))
3389       num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
3390     else
3391       {
3392         if (image->storage_class == PseudoClass)
3393           (void) SetImageStorageClass(image,DirectClass,exception);
3394         if (image->colorspace != CMYKColorspace)
3395           num_channels=(image->alpha_trait != UndefinedPixelTrait ? 4UL : 3UL);
3396         else
3397           num_channels=(image->alpha_trait != UndefinedPixelTrait ? 5UL : 4UL);
3398       }
3399   (void) WriteBlobMSBShort(image,(unsigned short) num_channels);
3400   (void) WriteBlobMSBLong(image,(unsigned int) image->rows);
3401   (void) WriteBlobMSBLong(image,(unsigned int) image->columns);
3402   if (IsImageGray(image) != MagickFalse)
3403     {
3404       MagickBooleanType
3405         monochrome;
3406
3407       /*
3408         Write depth & mode.
3409       */
3410       monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
3411         MagickTrue : MagickFalse;
3412       (void) WriteBlobMSBShort(image,(unsigned short)
3413         (monochrome != MagickFalse ? 1 : image->depth > 8 ? 16 : 8));
3414       (void) WriteBlobMSBShort(image,(unsigned short)
3415         (monochrome != MagickFalse ? BitmapMode : GrayscaleMode));
3416     }
3417   else
3418     {
3419       (void) WriteBlobMSBShort(image,(unsigned short) (image->storage_class ==
3420         PseudoClass ? 8 : image->depth > 8 ? 16 : 8));
3421
3422       if (((image_info->colorspace != UndefinedColorspace) ||
3423            (image->colorspace != CMYKColorspace)) &&
3424           (image_info->colorspace != CMYKColorspace))
3425         {
3426           (void) TransformImageColorspace(image,sRGBColorspace,exception);
3427           (void) WriteBlobMSBShort(image,(unsigned short)
3428             (image->storage_class == PseudoClass ? IndexedMode : RGBMode));
3429         }
3430       else
3431         {
3432           if (image->colorspace != CMYKColorspace)
3433             (void) TransformImageColorspace(image,CMYKColorspace,exception);
3434           (void) WriteBlobMSBShort(image,CMYKMode);
3435         }
3436     }
3437   if ((IsImageGray(image) != MagickFalse) ||
3438       (image->storage_class == DirectClass) || (image->colors > 256))
3439     (void) WriteBlobMSBLong(image,0);
3440   else
3441     {
3442       /*
3443         Write PSD raster colormap.
3444       */
3445       (void) WriteBlobMSBLong(image,768);
3446       for (i=0; i < (ssize_t) image->colors; i++)
3447         (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].red));
3448       for ( ; i < 256; i++)
3449         (void) WriteBlobByte(image,0);
3450       for (i=0; i < (ssize_t) image->colors; i++)
3451         (void) WriteBlobByte(image,ScaleQuantumToChar(
3452           image->colormap[i].green));
3453       for ( ; i < 256; i++)
3454         (void) WriteBlobByte(image,0);
3455       for (i=0; i < (ssize_t) image->colors; i++)
3456         (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].blue));
3457       for ( ; i < 256; i++)
3458         (void) WriteBlobByte(image,0);
3459     }
3460   /*
3461     Image resource block.
3462   */
3463   length=28; /* 0x03EB */
3464   bim_profile=(StringInfo *) GetImageProfile(image,"8bim");
3465   icc_profile=GetImageProfile(image,"icc");
3466   if (bim_profile != (StringInfo *) NULL)
3467     {
3468       bim_profile=CloneStringInfo(bim_profile);
3469       if (icc_profile != (StringInfo *) NULL)
3470         RemoveICCProfileFromResourceBlock(bim_profile);
3471       RemoveResolutionFromResourceBlock(bim_profile);
3472       length+=PSDQuantum(GetStringInfoLength(bim_profile));
3473     }
3474   if (icc_profile != (const StringInfo *) NULL)
3475     length+=PSDQuantum(GetStringInfoLength(icc_profile))+12;
3476   (void) WriteBlobMSBLong(image,(unsigned int) length);
3477   WriteResolutionResourceBlock(image);
3478   if (bim_profile != (StringInfo *) NULL)
3479     {
3480       (void) WriteBlob(image,GetStringInfoLength(bim_profile),
3481         GetStringInfoDatum(bim_profile));
3482       bim_profile=DestroyStringInfo(bim_profile);
3483     }
3484   if (icc_profile != (StringInfo *) NULL)
3485     {
3486       (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
3487       (void) WriteBlobMSBShort(image,0x0000040F);
3488       (void) WriteBlobMSBShort(image,0);
3489       (void) WriteBlobMSBLong(image,(unsigned int) GetStringInfoLength(
3490         icc_profile));
3491       (void) WriteBlob(image,GetStringInfoLength(icc_profile),
3492         GetStringInfoDatum(icc_profile));
3493       if ((MagickOffsetType) GetStringInfoLength(icc_profile) !=
3494           PSDQuantum(GetStringInfoLength(icc_profile)))
3495         (void) WriteBlobByte(image,0);
3496     }
3497   if (status != MagickFalse)
3498     {
3499       MagickOffsetType
3500         size_offset;
3501
3502       size_t
3503         size;
3504
3505       size_offset=TellBlob(image);
3506       SetPSDSize(&psd_info,image,0);
3507       status=WritePSDLayersInternal(image,image_info,&psd_info,&size,
3508         exception);
3509       size_offset+=WritePSDSize(&psd_info,image,size+
3510         (psd_info.version == 1 ? 8 : 16),size_offset);
3511     }
3512   (void) WriteBlobMSBLong(image,0);  /* user mask data */
3513   /*
3514     Write composite image.
3515   */
3516   if (status != MagickFalse)
3517     {
3518       CompressionType
3519         compression;
3520
3521       compression=image->compression;
3522       if (image->compression == ZipCompression)
3523         image->compression=RLECompression;
3524       if (WritePSDChannels(&psd_info,image_info,image,image,0,MagickFalse,
3525           exception) == 0)
3526         status=MagickFalse;
3527       image->compression=compression;
3528     }
3529   (void) CloseBlob(image);
3530   return(status);
3531 }