2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Adobe Photoshop Image Format %
23 % Copyright 1999-2018 ImageMagick Studio LLC, a non-profit organization %
24 % dedicated to making software imaging solutions freely available. %
26 % You may not use this file except in compliance with the License. You may %
27 % obtain a copy of the License at %
29 % https://www.imagemagick.org/script/license.php %
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. %
37 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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
83 #include "psd-private.h"
88 #define MaxPSDChannels 56
89 #define PSDQuantum(x) (((ssize_t) (x)+1) & -2)
92 Enumerated declaractions.
98 ZipWithoutPrediction = 2,
100 } PSDCompressionType;
109 MultichannelMode = 7,
115 Typedef declaractions.
117 typedef struct _ChannelInfo
126 typedef struct _MaskInfo
139 typedef struct _LayerInfo
142 channel_info[MaxPSDChannels];
177 Forward declarations.
179 static MagickBooleanType
180 WritePSDImage(const ImageInfo *,Image *,ExceptionInfo *);
183 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
193 % IsPSD()() returns MagickTrue if the image format type, identified by the
194 % magick string, is PSD.
196 % The format of the IsPSD method is:
198 % MagickBooleanType IsPSD(const unsigned char *magick,const size_t length)
200 % A description of each parameter follows:
202 % o magick: compare image format pattern against these bytes.
204 % o length: Specifies the length of the magick string.
207 static MagickBooleanType IsPSD(const unsigned char *magick,const size_t length)
211 if (LocaleNCompare((const char *) magick,"8BPS",4) == 0)
217 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
221 % R e a d P S D I m a g e %
225 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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.
231 % The format of the ReadPSDImage method is:
233 % Image *ReadPSDImage(image_info,ExceptionInfo *exception)
235 % A description of each parameter follows:
237 % o image_info: the image info.
239 % o exception: return any errors or warnings in this structure.
243 static const char *CompositeOperatorToPSDBlendMode(Image *image)
245 switch (image->compose)
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");
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:
293 return(image->endian == LSBEndian ? "mron" : "norm");
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.
302 static MagickBooleanType CorrectPSDAlphaBlend(const ImageInfo *image_info,
303 Image *image,ExceptionInfo* exception)
314 if (image->alpha_trait != BlendPixelTrait || image->colorspace != sRGBColorspace)
316 option=GetImageOption(image_info,"psd:alpha-unblend");
317 if (IsStringFalse(option) != MagickFalse)
320 #if defined(MAGICKCORE_OPENMP_SUPPORT)
321 #pragma omp parallel for schedule(static) shared(status) \
322 magick_number_threads(image,image,image->rows,1)
324 for (y=0; y < (ssize_t) image->rows; y++)
332 if (status == MagickFalse)
334 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
335 if (q == (Quantum *) NULL)
340 for (x=0; x < (ssize_t) image->columns; x++)
348 gamma=QuantumScale*GetPixelAlpha(image, q);
349 if (gamma != 0.0 && gamma != 1.0)
351 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
353 PixelChannel channel = GetPixelChannelChannel(image,i);
354 if (channel != AlphaPixelChannel)
355 q[i]=ClampToQuantum((q[i]-((1.0-gamma)*QuantumRange))/gamma);
358 q+=GetPixelChannels(image);
360 if (SyncAuthenticPixels(image,exception) == MagickFalse)
367 static inline CompressionType ConvertPSDCompression(
368 PSDCompressionType compression)
373 return RLECompression;
374 case ZipWithPrediction:
375 case ZipWithoutPrediction:
376 return ZipCompression;
378 return NoCompression;
382 static MagickBooleanType ApplyPSDLayerOpacity(Image *image,Quantum opacity,
383 MagickBooleanType revert,ExceptionInfo *exception)
391 if (image->debug != MagickFalse)
392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
393 " applying layer opacity %.20g", (double) opacity);
394 if (opacity == OpaqueAlpha)
396 if (image->alpha_trait != BlendPixelTrait)
397 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
399 #if defined(MAGICKCORE_OPENMP_SUPPORT)
400 #pragma omp parallel for schedule(static) shared(status) \
401 magick_number_threads(image,image,image->rows,1)
403 for (y=0; y < (ssize_t) image->rows; y++)
411 if (status == MagickFalse)
413 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
414 if (q == (Quantum *) NULL)
419 for (x=0; x < (ssize_t) image->columns; x++)
421 if (revert == MagickFalse)
422 SetPixelAlpha(image,(Quantum) (QuantumScale*(GetPixelAlpha(image,q))*
424 else if (opacity > 0)
425 SetPixelAlpha(image,(Quantum) (QuantumRange*(GetPixelAlpha(image,q)/
426 (MagickRealType) opacity)),q);
427 q+=GetPixelChannels(image);
429 if (SyncAuthenticPixels(image,exception) == MagickFalse)
436 static MagickBooleanType ApplyPSDOpacityMask(Image *image,const Image *mask,
437 Quantum background,MagickBooleanType revert,ExceptionInfo *exception)
451 if (image->debug != MagickFalse)
452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
453 " applying opacity mask");
454 complete_mask=CloneImage(image,image->columns,image->rows,MagickTrue,
456 if (complete_mask == (Image *) NULL)
458 complete_mask->alpha_trait=BlendPixelTrait;
459 GetPixelInfo(complete_mask,&color);
460 color.red=background;
461 SetImageColor(complete_mask,&color,exception);
462 status=CompositeImage(complete_mask,mask,OverCompositeOp,MagickTrue,
463 mask->page.x-image->page.x,mask->page.y-image->page.y,exception);
464 if (status == MagickFalse)
466 complete_mask=DestroyImage(complete_mask);
469 image->alpha_trait=BlendPixelTrait;
470 #if defined(MAGICKCORE_OPENMP_SUPPORT)
471 #pragma omp parallel for schedule(static) shared(status) \
472 magick_number_threads(image,image,image->rows,1)
474 for (y=0; y < (ssize_t) image->rows; y++)
485 if (status == MagickFalse)
487 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
488 p=GetAuthenticPixels(complete_mask,0,y,complete_mask->columns,1,exception);
489 if ((q == (Quantum *) NULL) || (p == (Quantum *) NULL))
494 for (x=0; x < (ssize_t) image->columns; x++)
500 alpha=GetPixelAlpha(image,q);
501 intensity=GetPixelIntensity(complete_mask,p);
502 if (revert == MagickFalse)
503 SetPixelAlpha(image,ClampToQuantum(intensity*(QuantumScale*alpha)),q);
504 else if (intensity > 0)
505 SetPixelAlpha(image,ClampToQuantum((alpha/intensity)*QuantumRange),q);
506 q+=GetPixelChannels(image);
507 p+=GetPixelChannels(complete_mask);
509 if (SyncAuthenticPixels(image,exception) == MagickFalse)
512 complete_mask=DestroyImage(complete_mask);
516 static void PreservePSDOpacityMask(Image *image,LayerInfo* layer_info,
517 ExceptionInfo *exception)
528 if (image->debug != MagickFalse)
529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
530 " preserving opacity mask");
531 random_info=AcquireRandomInfo();
532 key_info=GetRandomKey(random_info,2+1);
533 key=(char *) GetStringInfoDatum(key_info);
534 key[8]=layer_info->mask.background;
536 layer_info->mask.image->page.x+=layer_info->page.x;
537 layer_info->mask.image->page.y+=layer_info->page.y;
538 (void) SetImageRegistry(ImageRegistryType,(const char *) key,
539 layer_info->mask.image,exception);
540 (void) SetImageArtifact(layer_info->image,"psd:opacity-mask",
542 key_info=DestroyStringInfo(key_info);
543 random_info=DestroyRandomInfo(random_info);
546 static ssize_t DecodePSDPixels(const size_t number_compact_pixels,
547 const unsigned char *compact_pixels,const ssize_t depth,
548 const size_t number_pixels,unsigned char *pixels)
550 #define CheckNumberCompactPixels \
555 #define CheckNumberPixels(count) \
556 if (((ssize_t) i + count) > (ssize_t) number_pixels) \
573 packets=(ssize_t) number_compact_pixels;
574 for (i=0; (packets > 1) && (i < (ssize_t) number_pixels); )
577 length=(size_t) (*compact_pixels++);
583 CheckNumberCompactPixels;
584 pixel=(*compact_pixels++);
585 for (j=0; j < (ssize_t) length; j++)
591 CheckNumberPixels(8);
592 *pixels++=(pixel >> 7) & 0x01 ? 0U : 255U;
593 *pixels++=(pixel >> 6) & 0x01 ? 0U : 255U;
594 *pixels++=(pixel >> 5) & 0x01 ? 0U : 255U;
595 *pixels++=(pixel >> 4) & 0x01 ? 0U : 255U;
596 *pixels++=(pixel >> 3) & 0x01 ? 0U : 255U;
597 *pixels++=(pixel >> 2) & 0x01 ? 0U : 255U;
598 *pixels++=(pixel >> 1) & 0x01 ? 0U : 255U;
599 *pixels++=(pixel >> 0) & 0x01 ? 0U : 255U;
604 CheckNumberPixels(4);
605 *pixels++=(unsigned char) ((pixel >> 6) & 0x03);
606 *pixels++=(unsigned char) ((pixel >> 4) & 0x03);
607 *pixels++=(unsigned char) ((pixel >> 2) & 0x03);
608 *pixels++=(unsigned char) ((pixel & 0x03) & 0x03);
613 CheckNumberPixels(2);
614 *pixels++=(unsigned char) ((pixel >> 4) & 0xff);
615 *pixels++=(unsigned char) ((pixel & 0x0f) & 0xff);
620 CheckNumberPixels(1);
621 *pixels++=(unsigned char) pixel;
629 for (j=0; j < (ssize_t) length; j++)
631 CheckNumberCompactPixels;
636 CheckNumberPixels(8);
637 *pixels++=(*compact_pixels >> 7) & 0x01 ? 0U : 255U;
638 *pixels++=(*compact_pixels >> 6) & 0x01 ? 0U : 255U;
639 *pixels++=(*compact_pixels >> 5) & 0x01 ? 0U : 255U;
640 *pixels++=(*compact_pixels >> 4) & 0x01 ? 0U : 255U;
641 *pixels++=(*compact_pixels >> 3) & 0x01 ? 0U : 255U;
642 *pixels++=(*compact_pixels >> 2) & 0x01 ? 0U : 255U;
643 *pixels++=(*compact_pixels >> 1) & 0x01 ? 0U : 255U;
644 *pixels++=(*compact_pixels >> 0) & 0x01 ? 0U : 255U;
649 CheckNumberPixels(4);
650 *pixels++=(*compact_pixels >> 6) & 0x03;
651 *pixels++=(*compact_pixels >> 4) & 0x03;
652 *pixels++=(*compact_pixels >> 2) & 0x03;
653 *pixels++=(*compact_pixels & 0x03) & 0x03;
658 CheckNumberPixels(2);
659 *pixels++=(*compact_pixels >> 4) & 0xff;
660 *pixels++=(*compact_pixels & 0x0f) & 0xff;
665 CheckNumberPixels(1);
666 *pixels++=(*compact_pixels);
676 static inline LayerInfo *DestroyLayerInfo(LayerInfo *layer_info,
677 const ssize_t number_layers)
682 for (i=0; i<number_layers; i++)
684 if (layer_info[i].image != (Image *) NULL)
685 layer_info[i].image=DestroyImage(layer_info[i].image);
686 if (layer_info[i].mask.image != (Image *) NULL)
687 layer_info[i].mask.image=DestroyImage(layer_info[i].mask.image);
688 if (layer_info[i].info != (StringInfo *) NULL)
689 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
692 return (LayerInfo *) RelinquishMagickMemory(layer_info);
695 static inline size_t GetPSDPacketSize(const Image *image)
697 if (image->storage_class == PseudoClass)
699 if (image->colors > 256)
702 if (image->depth > 16)
704 if (image->depth > 8)
710 static inline MagickSizeType GetPSDSize(const PSDInfo *psd_info,Image *image)
712 if (psd_info->version == 1)
713 return((MagickSizeType) ReadBlobLong(image));
714 return((MagickSizeType) ReadBlobLongLong(image));
717 static inline size_t GetPSDRowSize(Image *image)
719 if (image->depth == 1)
720 return(((image->columns+7)/8)*GetPSDPacketSize(image));
722 return(image->columns*GetPSDPacketSize(image));
725 static const char *ModeToString(PSDImageType type)
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";
741 static MagickBooleanType NegateCMYK(Image *image,ExceptionInfo *exception)
749 channel_mask=SetImageChannelMask(image,(ChannelType)(AllChannels &~
751 status=NegateImage(image,MagickFalse,exception);
752 (void) SetImageChannelMask(image,channel_mask);
756 static StringInfo *ParseImageResourceBlocks(Image *image,
757 const unsigned char *blocks,size_t length,
758 MagickBooleanType *has_merged_image,ExceptionInfo *exception)
777 return((StringInfo *) NULL);
778 profile=BlobToStringInfo((const unsigned char *) NULL,length);
779 SetStringInfoDatum(profile,blocks);
780 SetStringInfoName(profile,"8bim");
781 for (p=blocks; (p >= blocks) && (p < (blocks+length-7)); )
783 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
786 p=PushShortPixel(MSBEndian,p,&id);
787 p=PushCharPixel(p,&name_length);
788 if ((name_length % 2) == 0)
791 if (p > (blocks+length-4))
793 p=PushLongPixel(MSBEndian,p,&count);
794 if ((p+count) > (blocks+length))
801 value[MagickPathExtent];
811 p=PushShortPixel(MSBEndian,p,&resolution);
812 image->resolution.x=(double) resolution;
813 (void) FormatLocaleString(value,MagickPathExtent,"%g",
814 image->resolution.x);
815 (void) SetImageProperty(image,"tiff:XResolution",value,exception);
816 p=PushShortPixel(MSBEndian,p,&short_sans);
817 p=PushShortPixel(MSBEndian,p,&short_sans);
818 p=PushShortPixel(MSBEndian,p,&short_sans);
819 p=PushShortPixel(MSBEndian,p,&resolution);
820 image->resolution.y=(double) resolution;
821 (void) FormatLocaleString(value,MagickPathExtent,"%g",
822 image->resolution.y);
823 (void) SetImageProperty(image,"tiff:YResolution",value,exception);
824 p=PushShortPixel(MSBEndian,p,&short_sans);
825 p=PushShortPixel(MSBEndian,p,&short_sans);
826 p=PushShortPixel(MSBEndian,p,&short_sans);
827 image->units=PixelsPerInchResolution;
832 if ((count > 3) && (*(p+4) == 0))
833 *has_merged_image=MagickFalse;
843 if ((count & 0x01) != 0)
849 static CompositeOperator PSDBlendModeToCompositeOperator(const char *mode)
851 if (mode == (const char *) NULL)
852 return(OverCompositeOp);
853 if (LocaleNCompare(mode,"norm",4) == 0)
854 return(OverCompositeOp);
855 if (LocaleNCompare(mode,"mul ",4) == 0)
856 return(MultiplyCompositeOp);
857 if (LocaleNCompare(mode,"diss",4) == 0)
858 return(DissolveCompositeOp);
859 if (LocaleNCompare(mode,"diff",4) == 0)
860 return(DifferenceCompositeOp);
861 if (LocaleNCompare(mode,"dark",4) == 0)
862 return(DarkenCompositeOp);
863 if (LocaleNCompare(mode,"lite",4) == 0)
864 return(LightenCompositeOp);
865 if (LocaleNCompare(mode,"hue ",4) == 0)
866 return(HueCompositeOp);
867 if (LocaleNCompare(mode,"sat ",4) == 0)
868 return(SaturateCompositeOp);
869 if (LocaleNCompare(mode,"colr",4) == 0)
870 return(ColorizeCompositeOp);
871 if (LocaleNCompare(mode,"lum ",4) == 0)
872 return(LuminizeCompositeOp);
873 if (LocaleNCompare(mode,"scrn",4) == 0)
874 return(ScreenCompositeOp);
875 if (LocaleNCompare(mode,"over",4) == 0)
876 return(OverlayCompositeOp);
877 if (LocaleNCompare(mode,"hLit",4) == 0)
878 return(HardLightCompositeOp);
879 if (LocaleNCompare(mode,"sLit",4) == 0)
880 return(SoftLightCompositeOp);
881 if (LocaleNCompare(mode,"smud",4) == 0)
882 return(ExclusionCompositeOp);
883 if (LocaleNCompare(mode,"div ",4) == 0)
884 return(ColorDodgeCompositeOp);
885 if (LocaleNCompare(mode,"idiv",4) == 0)
886 return(ColorBurnCompositeOp);
887 if (LocaleNCompare(mode,"lbrn",4) == 0)
888 return(LinearBurnCompositeOp);
889 if (LocaleNCompare(mode,"lddg",4) == 0)
890 return(LinearDodgeCompositeOp);
891 if (LocaleNCompare(mode,"lLit",4) == 0)
892 return(LinearLightCompositeOp);
893 if (LocaleNCompare(mode,"vLit",4) == 0)
894 return(VividLightCompositeOp);
895 if (LocaleNCompare(mode,"pLit",4) == 0)
896 return(PinLightCompositeOp);
897 if (LocaleNCompare(mode,"hMix",4) == 0)
898 return(HardMixCompositeOp);
899 return(OverCompositeOp);
902 static inline void ReversePSDString(Image *image,char *p,size_t length)
907 if (image->endian == MSBEndian)
911 for(--q; p < q; ++p, --q)
919 static inline void SetPSDPixel(Image *image,const size_t channels,
920 const ssize_t type,const size_t packet_size,const Quantum pixel,Quantum *q,
921 ExceptionInfo *exception)
923 if (image->storage_class == PseudoClass)
930 if (packet_size == 1)
931 SetPixelIndex(image,ScaleQuantumToChar(pixel),q);
933 SetPixelIndex(image,ScaleQuantumToShort(pixel),q);
935 color=image->colormap+(ssize_t) ConstrainColormapIndex(image,
936 GetPixelIndex(image,q),exception);
937 if ((type == 0) && (channels > 1))
940 color->alpha=(MagickRealType) pixel;
941 SetPixelViaPixelInfo(image,color,q);
948 SetPixelAlpha(image,pixel,q);
954 SetPixelRed(image,pixel,q);
960 SetPixelGreen(image,pixel,q);
966 SetPixelBlue(image,pixel,q);
971 if (image->colorspace == CMYKColorspace)
972 SetPixelBlack(image,pixel,q);
974 if (image->alpha_trait != UndefinedPixelTrait)
975 SetPixelAlpha(image,pixel,q);
980 if ((IssRGBCompatibleColorspace(image->colorspace) != MagickFalse) &&
983 if (image->alpha_trait != UndefinedPixelTrait)
984 SetPixelAlpha(image,pixel,q);
990 static MagickBooleanType ReadPSDChannelPixels(Image *image,
991 const size_t channels,const size_t row,const ssize_t type,
992 const unsigned char *pixels,ExceptionInfo *exception)
997 register const unsigned char
1010 q=GetAuthenticPixels(image,0,row,image->columns,1,exception);
1011 if (q == (Quantum *) NULL)
1013 packet_size=GetPSDPacketSize(image);
1014 for (x=0; x < (ssize_t) image->columns; x++)
1016 if (packet_size == 1)
1017 pixel=ScaleCharToQuantum(*p++);
1018 else if (packet_size == 2)
1023 p=PushShortPixel(MSBEndian,p,&nibble);
1024 pixel=ScaleShortToQuantum(nibble);
1031 p=PushFloatPixel(MSBEndian,p,&nibble);
1032 pixel=ClampToQuantum((MagickRealType)QuantumRange*nibble);
1034 if (image->depth > 1)
1036 SetPSDPixel(image,channels,type,packet_size,pixel,q,exception);
1037 q+=GetPixelChannels(image);
1045 number_bits=image->columns-x;
1046 if (number_bits > 8)
1048 for (bit = 0; bit < number_bits; bit++)
1050 SetPSDPixel(image,channels,type,packet_size,(((unsigned char) pixel)
1051 & (0x01 << (7-bit))) != 0 ? 0 : QuantumRange,q,exception);
1052 q+=GetPixelChannels(image);
1055 if (x != (ssize_t) image->columns)
1060 return(SyncAuthenticPixels(image,exception));
1063 static MagickBooleanType ReadPSDChannelRaw(Image *image,const size_t channels,
1064 const ssize_t type,ExceptionInfo *exception)
1079 if (image->debug != MagickFalse)
1080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1081 " layer data is RAW");
1083 row_size=GetPSDRowSize(image);
1084 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
1085 if (pixels == (unsigned char *) NULL)
1086 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1090 for (y=0; y < (ssize_t) image->rows; y++)
1094 count=ReadBlob(image,row_size,pixels);
1095 if (count != row_size)
1101 status=ReadPSDChannelPixels(image,channels,y,type,pixels,exception);
1102 if (status == MagickFalse)
1106 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1110 static inline MagickOffsetType *ReadPSDRLESizes(Image *image,
1111 const PSDInfo *psd_info,const size_t size)
1119 sizes=(MagickOffsetType *) AcquireQuantumMemory(size,sizeof(*sizes));
1120 if(sizes != (MagickOffsetType *) NULL)
1122 for (y=0; y < (ssize_t) size; y++)
1124 if (psd_info->version == 1)
1125 sizes[y]=(MagickOffsetType) ReadBlobShort(image);
1127 sizes[y]=(MagickOffsetType) ReadBlobLong(image);
1133 static MagickBooleanType ReadPSDChannelRLE(Image *image,const PSDInfo *psd_info,
1134 const ssize_t type,MagickOffsetType *sizes,ExceptionInfo *exception)
1151 if (image->debug != MagickFalse)
1152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1153 " layer data is RLE compressed");
1155 row_size=GetPSDRowSize(image);
1156 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
1157 if (pixels == (unsigned char *) NULL)
1158 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1162 for (y=0; y < (ssize_t) image->rows; y++)
1163 if ((MagickOffsetType) length < sizes[y])
1164 length=(size_t) sizes[y];
1166 if (length > (row_size+512)) // arbitrary number
1168 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1169 ThrowBinaryException(ResourceLimitError,"InvalidLength",image->filename);
1172 compact_pixels=(unsigned char *) AcquireQuantumMemory(length,sizeof(*pixels));
1173 if (compact_pixels == (unsigned char *) NULL)
1175 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1176 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1180 (void) memset(compact_pixels,0,length*sizeof(*compact_pixels));
1183 for (y=0; y < (ssize_t) image->rows; y++)
1187 count=ReadBlob(image,(size_t) sizes[y],compact_pixels);
1188 if (count != (ssize_t) sizes[y])
1191 count=DecodePSDPixels((size_t) sizes[y],compact_pixels,
1192 (ssize_t) (image->depth == 1 ? 123456 : image->depth),row_size,pixels);
1193 if (count != (ssize_t) row_size)
1196 status=ReadPSDChannelPixels(image,psd_info->channels,y,type,pixels,
1198 if (status == MagickFalse)
1202 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1203 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1207 #ifdef MAGICKCORE_ZLIB_DELEGATE
1208 static MagickBooleanType ReadPSDChannelZip(Image *image,const size_t channels,
1209 const ssize_t type,const PSDCompressionType compression,
1210 const size_t compact_size,ExceptionInfo *exception)
1215 register unsigned char
1234 if (image->debug != MagickFalse)
1235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1236 " layer data is ZIP compressed");
1238 if ((MagickSizeType) compact_size > GetBlobSize(image))
1239 ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile",
1241 compact_pixels=(unsigned char *) AcquireQuantumMemory(compact_size,
1242 sizeof(*compact_pixels));
1243 if (compact_pixels == (unsigned char *) NULL)
1244 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1247 packet_size=GetPSDPacketSize(image);
1248 row_size=image->columns*packet_size;
1249 count=image->rows*row_size;
1251 pixels=(unsigned char *) AcquireQuantumMemory(count,sizeof(*pixels));
1252 if (pixels == (unsigned char *) NULL)
1254 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1255 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1258 if (ReadBlob(image,compact_size,compact_pixels) != (ssize_t) compact_size)
1260 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1261 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1262 ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile",
1266 memset(&stream,0,sizeof(stream));
1267 stream.data_type=Z_BINARY;
1268 stream.next_in=(Bytef *)compact_pixels;
1269 stream.avail_in=(uInt) compact_size;
1270 stream.next_out=(Bytef *)pixels;
1271 stream.avail_out=(uInt) count;
1273 if (inflateInit(&stream) == Z_OK)
1278 while (stream.avail_out > 0)
1280 ret=inflate(&stream,Z_SYNC_FLUSH);
1281 if ((ret != Z_OK) && (ret != Z_STREAM_END))
1283 (void) inflateEnd(&stream);
1284 compact_pixels=(unsigned char *) RelinquishMagickMemory(
1286 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1287 return(MagickFalse);
1289 if (ret == Z_STREAM_END)
1292 (void) inflateEnd(&stream);
1295 if (compression == ZipWithPrediction)
1300 length=image->columns;
1303 if (packet_size == 2)
1305 p[2]+=p[0]+((p[1]+p[3]) >> 8);
1308 // else if (packet_size == 4)
1310 // TODO: Figure out what to do there.
1323 for (y=0; y < (ssize_t) image->rows; y++)
1325 status=ReadPSDChannelPixels(image,channels,y,type,p,exception);
1326 if (status == MagickFalse)
1332 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1333 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1338 static MagickBooleanType ReadPSDChannel(Image *image,
1339 const ImageInfo *image_info,const PSDInfo *psd_info,LayerInfo* layer_info,
1340 const size_t channel,const PSDCompressionType compression,
1341 ExceptionInfo *exception)
1353 channel_image=image;
1354 mask=(Image *) NULL;
1355 if ((layer_info->channel_info[channel].type < -1) &&
1356 (layer_info->mask.page.width > 0) && (layer_info->mask.page.height > 0))
1362 Ignore mask that is not a user supplied layer mask, if the mask is
1363 disabled or if the flags have unsupported values.
1365 option=GetImageOption(image_info,"psd:preserve-opacity-mask");
1366 if ((layer_info->channel_info[channel].type != -2) ||
1367 (layer_info->mask.flags > 2) || ((layer_info->mask.flags & 0x02) &&
1368 (IsStringTrue(option) == MagickFalse)))
1370 SeekBlob(image,layer_info->channel_info[channel].size-2,SEEK_CUR);
1373 mask=CloneImage(image,layer_info->mask.page.width,
1374 layer_info->mask.page.height,MagickFalse,exception);
1375 if (mask != (Image *) NULL)
1377 SetImageType(mask,GrayscaleType,exception);
1382 offset=TellBlob(image);
1387 status=ReadPSDChannelRaw(channel_image,psd_info->channels,
1388 layer_info->channel_info[channel].type,exception);
1395 sizes=ReadPSDRLESizes(channel_image,psd_info,channel_image->rows);
1396 if (sizes == (MagickOffsetType *) NULL)
1397 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1399 status=ReadPSDChannelRLE(channel_image,psd_info,
1400 layer_info->channel_info[channel].type,sizes,exception);
1401 sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
1404 case ZipWithPrediction:
1405 case ZipWithoutPrediction:
1406 #ifdef MAGICKCORE_ZLIB_DELEGATE
1407 status=ReadPSDChannelZip(channel_image,layer_info->channels,
1408 layer_info->channel_info[channel].type,compression,
1409 layer_info->channel_info[channel].size-2,exception);
1411 (void) ThrowMagickException(exception,GetMagickModule(),
1412 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
1413 "'%s' (ZLIB)",image->filename);
1417 (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
1418 "CompressionNotSupported","'%.20g'",(double) compression);
1422 SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
1423 if (status == MagickFalse)
1425 if (mask != (Image *) NULL)
1427 ThrowBinaryException(CoderError,"UnableToDecompressImage",
1430 if (mask != (Image *) NULL)
1432 if (layer_info->mask.image != (Image *) NULL)
1433 layer_info->mask.image=DestroyImage(layer_info->mask.image);
1434 layer_info->mask.image=mask;
1439 static MagickBooleanType ReadPSDLayer(Image *image,const ImageInfo *image_info,
1440 const PSDInfo *psd_info,LayerInfo* layer_info,ExceptionInfo *exception)
1443 message[MagickPathExtent];
1454 if (image->debug != MagickFalse)
1455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1456 " setting up new layer image");
1457 if (psd_info->mode != IndexedMode)
1458 (void) SetImageBackgroundColor(layer_info->image,exception);
1459 layer_info->image->compose=PSDBlendModeToCompositeOperator(
1460 layer_info->blendkey);
1461 if (layer_info->visible == MagickFalse)
1462 layer_info->image->compose=NoCompositeOp;
1464 Set up some hidden attributes for folks that need them.
1466 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
1467 (double) layer_info->page.x);
1468 (void) SetImageArtifact(layer_info->image,"psd:layer.x",message);
1469 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
1470 (double) layer_info->page.y);
1471 (void) SetImageArtifact(layer_info->image,"psd:layer.y",message);
1472 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",(double)
1473 layer_info->opacity);
1474 (void) SetImageArtifact(layer_info->image,"psd:layer.opacity",message);
1475 (void) SetImageProperty(layer_info->image,"label",(char *) layer_info->name,
1479 for (j=0; j < (ssize_t) layer_info->channels; j++)
1481 if (image->debug != MagickFalse)
1482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1483 " reading data for channel %.20g",(double) j);
1485 compression=(PSDCompressionType) ReadBlobShort(layer_info->image);
1487 /* TODO: Remove this when we figure out how to support this */
1488 if ((compression == ZipWithPrediction) && (image->depth == 32))
1490 (void) ThrowMagickException(exception,GetMagickModule(),
1491 TypeError,"CompressionNotSupported","ZipWithPrediction(32 bit)");
1492 return(MagickFalse);
1495 layer_info->image->compression=ConvertPSDCompression(compression);
1496 if (layer_info->channel_info[j].type == -1)
1497 layer_info->image->alpha_trait=BlendPixelTrait;
1499 status=ReadPSDChannel(layer_info->image,image_info,psd_info,layer_info,j,
1500 compression,exception);
1502 if (status == MagickFalse)
1506 if (status != MagickFalse)
1507 status=ApplyPSDLayerOpacity(layer_info->image,layer_info->opacity,
1508 MagickFalse,exception);
1510 if ((status != MagickFalse) &&
1511 (layer_info->image->colorspace == CMYKColorspace))
1512 status=NegateCMYK(layer_info->image,exception);
1514 if ((status != MagickFalse) && (layer_info->mask.image != (Image *) NULL))
1519 layer_info->mask.image->page.x=layer_info->mask.page.x;
1520 layer_info->mask.image->page.y=layer_info->mask.page.y;
1521 /* Do not composite the mask when it is disabled */
1522 if ((layer_info->mask.flags & 0x02) == 0x02)
1523 layer_info->mask.image->compose=NoCompositeOp;
1525 status=ApplyPSDOpacityMask(layer_info->image,layer_info->mask.image,
1526 layer_info->mask.background == 0 ? 0 : QuantumRange,MagickFalse,
1528 option=GetImageOption(image_info,"psd:preserve-opacity-mask");
1529 if (IsStringTrue(option) != MagickFalse)
1530 PreservePSDOpacityMask(image,layer_info,exception);
1531 layer_info->mask.image=DestroyImage(layer_info->mask.image);
1537 static MagickBooleanType CheckPSDChannels(const PSDInfo *psd_info,
1538 LayerInfo *layer_info)
1546 if (layer_info->channels < psd_info->min_channels)
1547 return(MagickFalse);
1548 channel_type=RedChannel;
1549 if (psd_info->min_channels >= 3)
1550 channel_type|=(GreenChannel | BlueChannel);
1551 if (psd_info->min_channels >= 4)
1552 channel_type|=BlackChannel;
1553 for (i=0; i < layer_info->channels; i++)
1558 type=layer_info->channel_info[i].type;
1561 channel_type|=AlphaChannel;
1567 channel_type&=~RedChannel;
1569 channel_type&=~GreenChannel;
1571 channel_type&=~BlueChannel;
1573 channel_type&=~BlackChannel;
1575 if (channel_type == 0)
1577 if ((channel_type == AlphaChannel) &&
1578 (layer_info->channels >= psd_info->min_channels + 1))
1580 return(MagickFalse);
1583 static MagickBooleanType ReadPSDLayersInternal(Image *image,
1584 const ImageInfo *image_info,const PSDInfo *psd_info,
1585 const MagickBooleanType skip_layers,ExceptionInfo *exception)
1607 size=GetPSDSize(psd_info,image);
1611 Skip layers & masks.
1613 (void) ReadBlobLong(image);
1614 count=ReadBlob(image,4,(unsigned char *) type);
1615 ReversePSDString(image,type,count);
1617 if ((count != 4) || (LocaleNCompare(type,"8BIM",4) != 0))
1621 count=ReadBlob(image,4,(unsigned char *) type);
1622 ReversePSDString(image,type,4);
1623 if ((count != 0) && ((LocaleNCompare(type,"Lr16",4) == 0) ||
1624 (LocaleNCompare(type,"Lr32",4) == 0)))
1625 size=GetPSDSize(psd_info,image);
1633 layer_info=(LayerInfo *) NULL;
1634 number_layers=(short) ReadBlobShort(image);
1636 if (number_layers < 0)
1639 The first alpha channel in the merged result contains the
1640 transparency data for the merged result.
1642 number_layers=MagickAbsoluteValue(number_layers);
1643 if (image->debug != MagickFalse)
1644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1645 " negative layer count corrected for");
1646 image->alpha_trait=BlendPixelTrait;
1650 We only need to know if the image has an alpha channel
1652 if (skip_layers != MagickFalse)
1655 if (image->debug != MagickFalse)
1656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1657 " image contains %.20g layers",(double) number_layers);
1659 if (number_layers == 0)
1660 ThrowBinaryException(CorruptImageError,"InvalidNumberOfLayers",
1663 layer_info=(LayerInfo *) AcquireQuantumMemory((size_t) number_layers,
1664 sizeof(*layer_info));
1665 if (layer_info == (LayerInfo *) NULL)
1667 if (image->debug != MagickFalse)
1668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1669 " allocation of LayerInfo failed");
1670 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1673 (void) memset(layer_info,0,(size_t) number_layers*
1674 sizeof(*layer_info));
1676 for (i=0; i < number_layers; i++)
1682 if (image->debug != MagickFalse)
1683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1684 " reading layer #%.20g",(double) i+1);
1685 layer_info[i].page.y=ReadBlobSignedLong(image);
1686 layer_info[i].page.x=ReadBlobSignedLong(image);
1687 y=ReadBlobSignedLong(image);
1688 x=ReadBlobSignedLong(image);
1689 layer_info[i].page.width=(size_t) (x-layer_info[i].page.x);
1690 layer_info[i].page.height=(size_t) (y-layer_info[i].page.y);
1691 layer_info[i].channels=ReadBlobShort(image);
1692 if (layer_info[i].channels > MaxPSDChannels)
1694 layer_info=DestroyLayerInfo(layer_info,number_layers);
1695 ThrowBinaryException(CorruptImageError,"MaximumChannelsExceeded",
1698 if (image->debug != MagickFalse)
1699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1700 " offset(%.20g,%.20g), size(%.20g,%.20g), channels=%.20g",
1701 (double) layer_info[i].page.x,(double) layer_info[i].page.y,
1702 (double) layer_info[i].page.height,(double)
1703 layer_info[i].page.width,(double) layer_info[i].channels);
1704 for (j=0; j < (ssize_t) layer_info[i].channels; j++)
1706 layer_info[i].channel_info[j].type=(short) ReadBlobShort(image);
1707 if ((layer_info[i].channel_info[j].type < -4) ||
1708 (layer_info[i].channel_info[j].type > 4))
1710 layer_info=DestroyLayerInfo(layer_info,number_layers);
1711 ThrowBinaryException(CorruptImageError,"NoSuchImageChannel",
1714 layer_info[i].channel_info[j].size=(size_t) GetPSDSize(psd_info,
1716 if (image->debug != MagickFalse)
1717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1718 " channel[%.20g]: type=%.20g, size=%.20g",(double) j,
1719 (double) layer_info[i].channel_info[j].type,
1720 (double) layer_info[i].channel_info[j].size);
1722 if (CheckPSDChannels(psd_info,&layer_info[i]) == MagickFalse)
1724 layer_info=DestroyLayerInfo(layer_info,number_layers);
1725 ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
1728 count=ReadBlob(image,4,(unsigned char *) type);
1729 ReversePSDString(image,type,4);
1730 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1732 if (image->debug != MagickFalse)
1733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1734 " layer type was %.4s instead of 8BIM", type);
1735 layer_info=DestroyLayerInfo(layer_info,number_layers);
1736 ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
1739 count=ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey);
1740 ReversePSDString(image,layer_info[i].blendkey,4);
1741 layer_info[i].opacity=(Quantum) ScaleCharToQuantum((unsigned char)
1742 ReadBlobByte(image));
1743 layer_info[i].clipping=(unsigned char) ReadBlobByte(image);
1744 layer_info[i].flags=(unsigned char) ReadBlobByte(image);
1745 layer_info[i].visible=!(layer_info[i].flags & 0x02);
1746 if (image->debug != MagickFalse)
1747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1748 " blend=%.4s, opacity=%.20g, clipping=%s, flags=%d, visible=%s",
1749 layer_info[i].blendkey,(double) layer_info[i].opacity,
1750 layer_info[i].clipping ? "true" : "false",layer_info[i].flags,
1751 layer_info[i].visible ? "true" : "false");
1752 (void) ReadBlobByte(image); /* filler */
1754 size=ReadBlobLong(image);
1761 if (image->debug != MagickFalse)
1762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1763 " layer contains additional info");
1764 length=ReadBlobLong(image);
1765 combined_length=length+4;
1771 layer_info[i].mask.page.y=ReadBlobSignedLong(image);
1772 layer_info[i].mask.page.x=ReadBlobSignedLong(image);
1773 layer_info[i].mask.page.height=(size_t) (ReadBlobSignedLong(image)-
1774 layer_info[i].mask.page.y);
1775 layer_info[i].mask.page.width=(size_t) (ReadBlobSignedLong(image)-
1776 layer_info[i].mask.page.x);
1777 layer_info[i].mask.background=(unsigned char) ReadBlobByte(
1779 layer_info[i].mask.flags=(unsigned char) ReadBlobByte(image);
1780 if (!(layer_info[i].mask.flags & 0x01))
1782 layer_info[i].mask.page.y=layer_info[i].mask.page.y-
1783 layer_info[i].page.y;
1784 layer_info[i].mask.page.x=layer_info[i].mask.page.x-
1785 layer_info[i].page.x;
1787 if (image->debug != MagickFalse)
1788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1789 " layer mask: offset(%.20g,%.20g), size(%.20g,%.20g), length=%.20g",
1790 (double) layer_info[i].mask.page.x,(double)
1791 layer_info[i].mask.page.y,(double)
1792 layer_info[i].mask.page.width,(double)
1793 layer_info[i].mask.page.height,(double) ((MagickOffsetType)
1796 Skip over the rest of the layer mask information.
1798 if (DiscardBlobBytes(image,(MagickSizeType) (length-18)) == MagickFalse)
1800 layer_info=DestroyLayerInfo(layer_info,number_layers);
1801 ThrowBinaryException(CorruptImageError,
1802 "UnexpectedEndOfFile",image->filename);
1805 length=ReadBlobLong(image);
1806 combined_length+=length+4;
1810 Layer blending ranges info.
1812 if (image->debug != MagickFalse)
1813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1814 " layer blending ranges: length=%.20g",(double)
1815 ((MagickOffsetType) length));
1816 if (DiscardBlobBytes(image,length) == MagickFalse)
1818 layer_info=DestroyLayerInfo(layer_info,number_layers);
1819 ThrowBinaryException(CorruptImageError,
1820 "UnexpectedEndOfFile",image->filename);
1826 length=(MagickSizeType) (unsigned char) ReadBlobByte(image);
1827 combined_length+=length+1;
1829 (void) ReadBlob(image,(size_t) length++,layer_info[i].name);
1830 layer_info[i].name[length]='\0';
1831 if (image->debug != MagickFalse)
1832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1833 " layer name: %s",layer_info[i].name);
1834 if ((length % 4) != 0)
1836 length=4-(length % 4);
1837 combined_length+=length;
1838 /* Skip over the padding of the layer name */
1839 if (DiscardBlobBytes(image,length) == MagickFalse)
1841 layer_info=DestroyLayerInfo(layer_info,number_layers);
1842 ThrowBinaryException(CorruptImageError,
1843 "UnexpectedEndOfFile",image->filename);
1846 length=(MagickSizeType) size-combined_length;
1852 if (length > GetBlobSize(image))
1854 layer_info=DestroyLayerInfo(layer_info,number_layers);
1855 ThrowBinaryException(CorruptImageError,
1856 "InsufficientImageDataInFile",image->filename);
1858 layer_info[i].info=AcquireStringInfo((const size_t) length);
1859 info=GetStringInfoDatum(layer_info[i].info);
1860 (void) ReadBlob(image,(const size_t) length,info);
1865 for (i=0; i < number_layers; i++)
1867 if ((layer_info[i].page.width == 0) || (layer_info[i].page.height == 0))
1869 if (image->debug != MagickFalse)
1870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1871 " layer data is empty");
1872 if (layer_info[i].info != (StringInfo *) NULL)
1873 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
1878 Allocate layered image.
1880 layer_info[i].image=CloneImage(image,layer_info[i].page.width,
1881 layer_info[i].page.height,MagickFalse,exception);
1882 if (layer_info[i].image == (Image *) NULL)
1884 layer_info=DestroyLayerInfo(layer_info,number_layers);
1885 if (image->debug != MagickFalse)
1886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1887 " allocation of image for layer %.20g failed",(double) i);
1888 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1891 if (layer_info[i].info != (StringInfo *) NULL)
1893 (void) SetImageProfile(layer_info[i].image,"psd:additional-info",
1894 layer_info[i].info,exception);
1895 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
1899 if (image_info->ping == MagickFalse)
1901 for (i=0; i < number_layers; i++)
1903 if (layer_info[i].image == (Image *) NULL)
1905 for (j=0; j < layer_info[i].channels; j++)
1907 if (DiscardBlobBytes(image,(MagickSizeType)
1908 layer_info[i].channel_info[j].size) == MagickFalse)
1910 layer_info=DestroyLayerInfo(layer_info,number_layers);
1911 ThrowBinaryException(CorruptImageError,
1912 "UnexpectedEndOfFile",image->filename);
1918 if (image->debug != MagickFalse)
1919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1920 " reading data for layer %.20g",(double) i);
1922 status=ReadPSDLayer(image,image_info,psd_info,&layer_info[i],
1924 if (status == MagickFalse)
1927 status=SetImageProgress(image,LoadImagesTag,i,(MagickSizeType)
1929 if (status == MagickFalse)
1934 if (status != MagickFalse)
1936 for (i=0; i < number_layers; i++)
1938 if (layer_info[i].image == (Image *) NULL)
1940 for (j=i; j < number_layers - 1; j++)
1941 layer_info[j] = layer_info[j+1];
1947 if (number_layers > 0)
1949 for (i=0; i < number_layers; i++)
1952 layer_info[i].image->previous=layer_info[i-1].image;
1953 if (i < (number_layers-1))
1954 layer_info[i].image->next=layer_info[i+1].image;
1955 layer_info[i].image->page=layer_info[i].page;
1957 image->next=layer_info[0].image;
1958 layer_info[0].image->previous=image;
1960 layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
1963 layer_info=DestroyLayerInfo(layer_info,number_layers);
1969 ModuleExport MagickBooleanType ReadPSDLayers(Image *image,
1970 const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception)
1978 domain=CoderPolicyDomain;
1979 rights=ReadPolicyRights;
1980 if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
1982 return(ReadPSDLayersInternal(image,image_info,psd_info,MagickFalse,
1986 static MagickBooleanType ReadPSDMergedImage(const ImageInfo *image_info,
1987 Image *image,const PSDInfo *psd_info,ExceptionInfo *exception)
2001 compression=(PSDCompressionType) ReadBlobMSBShort(image);
2002 image->compression=ConvertPSDCompression(compression);
2004 if (compression != Raw && compression != RLE)
2006 (void) ThrowMagickException(exception,GetMagickModule(),
2007 TypeWarning,"CompressionNotSupported","'%.20g'",(double) compression);
2008 return(MagickFalse);
2011 sizes=(MagickOffsetType *) NULL;
2012 if (compression == RLE)
2014 sizes=ReadPSDRLESizes(image,psd_info,image->rows*psd_info->channels);
2015 if (sizes == (MagickOffsetType *) NULL)
2016 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2021 for (i=0; i < (ssize_t) psd_info->channels; i++)
2027 if ((type == 1) && (psd_info->channels == 2))
2030 if (compression == RLE)
2031 status=ReadPSDChannelRLE(image,psd_info,type,sizes+(i*image->rows),
2034 status=ReadPSDChannelRaw(image,psd_info->channels,type,exception);
2036 if (status != MagickFalse)
2037 status=SetImageProgress(image,LoadImagesTag,i,psd_info->channels);
2039 if (status == MagickFalse)
2043 if ((status != MagickFalse) && (image->colorspace == CMYKColorspace))
2044 status=NegateCMYK(image,exception);
2046 if (status != MagickFalse)
2047 status=CorrectPSDAlphaBlend(image_info,image,exception);
2049 sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
2054 static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
2090 assert(image_info != (const ImageInfo *) NULL);
2091 assert(image_info->signature == MagickCoreSignature);
2092 if (image_info->debug != MagickFalse)
2093 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2094 image_info->filename);
2095 assert(exception != (ExceptionInfo *) NULL);
2096 assert(exception->signature == MagickCoreSignature);
2098 image=AcquireImage(image_info,exception);
2099 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2100 if (status == MagickFalse)
2102 image=DestroyImageList(image);
2103 return((Image *) NULL);
2108 image->endian=MSBEndian;
2109 count=ReadBlob(image,4,(unsigned char *) psd_info.signature);
2110 psd_info.version=ReadBlobMSBShort(image);
2111 if ((count != 4) || (LocaleNCompare(psd_info.signature,"8BPS",4) != 0) ||
2112 ((psd_info.version != 1) && (psd_info.version != 2)))
2113 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2114 (void) ReadBlob(image,6,psd_info.reserved);
2115 psd_info.channels=ReadBlobMSBShort(image);
2116 if (psd_info.channels < 1)
2117 ThrowReaderException(CorruptImageError,"MissingImageChannel");
2118 if (psd_info.channels > MaxPSDChannels)
2119 ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded");
2120 psd_info.rows=ReadBlobMSBLong(image);
2121 psd_info.columns=ReadBlobMSBLong(image);
2122 if ((psd_info.version == 1) && ((psd_info.rows > 30000) ||
2123 (psd_info.columns > 30000)))
2124 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2125 psd_info.depth=ReadBlobMSBShort(image);
2126 if ((psd_info.depth != 1) && (psd_info.depth != 8) &&
2127 (psd_info.depth != 16) && (psd_info.depth != 32))
2128 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2129 psd_info.mode=ReadBlobMSBShort(image);
2130 if ((psd_info.mode == IndexedMode) && (psd_info.channels > 3))
2131 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2132 if (image->debug != MagickFalse)
2133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2134 " Image is %.20g x %.20g with channels=%.20g, depth=%.20g, mode=%s",
2135 (double) psd_info.columns,(double) psd_info.rows,(double)
2136 psd_info.channels,(double) psd_info.depth,ModeToString((PSDImageType)
2138 if (EOFBlob(image) != MagickFalse)
2139 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2143 image->depth=psd_info.depth;
2144 image->columns=psd_info.columns;
2145 image->rows=psd_info.rows;
2146 status=SetImageExtent(image,image->columns,image->rows,exception);
2147 if (status == MagickFalse)
2148 return(DestroyImageList(image));
2149 status=ResetImagePixels(image,exception);
2150 if (status == MagickFalse)
2151 return(DestroyImageList(image));
2152 psd_info.min_channels=3;
2153 if (psd_info.mode == LabMode)
2154 SetImageColorspace(image,LabColorspace,exception);
2155 if (psd_info.mode == CMYKMode)
2157 psd_info.min_channels=4;
2158 SetImageColorspace(image,CMYKColorspace,exception);
2159 if (psd_info.channels > 4)
2160 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2162 else if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) ||
2163 (psd_info.mode == DuotoneMode))
2165 if (psd_info.depth != 32)
2167 status=AcquireImageColormap(image,psd_info.depth < 16 ? 256 : 65536,
2169 if (status == MagickFalse)
2170 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2171 if (image->debug != MagickFalse)
2172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2173 " Image colormap allocated");
2175 psd_info.min_channels=1;
2176 SetImageColorspace(image,GRAYColorspace,exception);
2177 if (psd_info.channels > 1)
2178 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2181 if (psd_info.channels > 3)
2182 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2183 if (psd_info.channels < psd_info.min_channels)
2184 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2186 Read PSD raster colormap only present for indexed and duotone images.
2188 length=ReadBlobMSBLong(image);
2189 if ((psd_info.mode == IndexedMode) && (length < 3))
2190 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2193 if (image->debug != MagickFalse)
2194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2195 " reading colormap");
2196 if ((psd_info.mode == DuotoneMode) || (psd_info.depth == 32))
2199 Duotone image data; the format of this data is undocumented.
2200 32 bits per pixel; the colormap is ignored.
2202 (void) SeekBlob(image,(const MagickOffsetType) length,SEEK_CUR);
2210 Read PSD raster colormap.
2212 number_colors=length/3;
2213 if (number_colors > 65536)
2214 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2215 if (AcquireImageColormap(image,number_colors,exception) == MagickFalse)
2216 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2217 for (i=0; i < (ssize_t) image->colors; i++)
2218 image->colormap[i].red=ScaleCharToQuantum((unsigned char)
2219 ReadBlobByte(image));
2220 for (i=0; i < (ssize_t) image->colors; i++)
2221 image->colormap[i].green=ScaleCharToQuantum((unsigned char)
2222 ReadBlobByte(image));
2223 for (i=0; i < (ssize_t) image->colors; i++)
2224 image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
2225 ReadBlobByte(image));
2226 image->alpha_trait=UndefinedPixelTrait;
2229 if ((image->depth == 1) && (image->storage_class != PseudoClass))
2230 ThrowReaderException(CorruptImageError, "ImproperImageHeader");
2231 has_merged_image=MagickTrue;
2232 profile=(StringInfo *) NULL;
2233 length=ReadBlobMSBLong(image);
2240 Image resources block.
2242 if (image->debug != MagickFalse)
2243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2244 " reading image resource blocks - %.20g bytes",(double)
2245 ((MagickOffsetType) length));
2246 if (length > GetBlobSize(image))
2247 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
2248 blocks=(unsigned char *) AcquireQuantumMemory((size_t) length,
2250 if (blocks == (unsigned char *) NULL)
2251 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2252 count=ReadBlob(image,(size_t) length,blocks);
2253 if ((count != (ssize_t) length) || (length < 4) ||
2254 (LocaleNCompare((char *) blocks,"8BIM",4) != 0))
2256 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
2257 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2259 profile=ParseImageResourceBlocks(image,blocks,(size_t) length,
2260 &has_merged_image,exception);
2261 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
2264 Layer and mask block.
2266 length=GetPSDSize(&psd_info,image);
2269 length=ReadBlobMSBLong(image);
2270 length=ReadBlobMSBLong(image);
2272 offset=TellBlob(image);
2273 skip_layers=MagickFalse;
2274 if ((image_info->number_scenes == 1) && (image_info->scene == 0) &&
2275 (has_merged_image != MagickFalse))
2277 if (image->debug != MagickFalse)
2278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2279 " read composite only");
2280 skip_layers=MagickTrue;
2284 if (image->debug != MagickFalse)
2285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2286 " image has no layers");
2290 if (ReadPSDLayersInternal(image,image_info,&psd_info,skip_layers,
2291 exception) != MagickTrue)
2293 if (profile != (StringInfo *) NULL)
2294 profile=DestroyStringInfo(profile);
2295 (void) CloseBlob(image);
2296 image=DestroyImageList(image);
2297 return((Image *) NULL);
2301 Skip the rest of the layer and mask information.
2303 SeekBlob(image,offset+length,SEEK_SET);
2306 If we are only "pinging" the image, then we're done - so return.
2308 if (EOFBlob(image) != MagickFalse)
2310 if (profile != (StringInfo *) NULL)
2311 profile=DestroyStringInfo(profile);
2312 ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
2314 if (image_info->ping != MagickFalse)
2316 if (profile != (StringInfo *) NULL)
2317 profile=DestroyStringInfo(profile);
2318 (void) CloseBlob(image);
2319 return(GetFirstImageInList(image));
2322 Read the precombined layer, present for PSD < 4 compatibility.
2324 if (image->debug != MagickFalse)
2325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2326 " reading the precombined layer");
2327 imageListLength=GetImageListLength(image);
2328 if ((has_merged_image != MagickFalse) || (imageListLength == 1))
2329 has_merged_image=(MagickBooleanType) ReadPSDMergedImage(image_info,image,
2330 &psd_info,exception);
2331 if ((has_merged_image == MagickFalse) && (imageListLength == 1) &&
2334 SeekBlob(image,offset,SEEK_SET);
2335 status=ReadPSDLayersInternal(image,image_info,&psd_info,MagickFalse,
2337 if (status != MagickTrue)
2339 if (profile != (StringInfo *) NULL)
2340 profile=DestroyStringInfo(profile);
2341 (void) CloseBlob(image);
2342 image=DestroyImageList(image);
2343 return((Image *) NULL);
2346 if (has_merged_image == MagickFalse)
2351 if (imageListLength == 1)
2353 if (profile != (StringInfo *) NULL)
2354 profile=DestroyStringInfo(profile);
2355 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
2357 SetImageAlphaChannel(image,TransparentAlphaChannel,exception);
2358 image->background_color.alpha=TransparentAlpha;
2359 image->background_color.alpha_trait=BlendPixelTrait;
2360 merged=MergeImageLayers(image,FlattenLayer,exception);
2361 ReplaceImageInList(&image,merged);
2363 if (profile != (StringInfo *) NULL)
2365 (void) SetImageProfile(image,GetStringInfoName(profile),profile,
2367 profile=DestroyStringInfo(profile);
2369 (void) CloseBlob(image);
2370 return(GetFirstImageInList(image));
2374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2378 % R e g i s t e r P S D I m a g e %
2382 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2384 % RegisterPSDImage() adds properties for the PSD image format to
2385 % the list of supported formats. The properties include the image format
2386 % tag, a method to read and/or write the format, whether the format
2387 % supports the saving of more than one frame to the same file or blob,
2388 % whether the format supports native in-memory I/O, and a brief
2389 % description of the format.
2391 % The format of the RegisterPSDImage method is:
2393 % size_t RegisterPSDImage(void)
2396 ModuleExport size_t RegisterPSDImage(void)
2401 entry=AcquireMagickInfo("PSD","PSB","Adobe Large Document Format");
2402 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2403 entry->encoder=(EncodeImageHandler *) WritePSDImage;
2404 entry->magick=(IsImageFormatHandler *) IsPSD;
2405 entry->flags|=CoderDecoderSeekableStreamFlag;
2406 entry->flags|=CoderEncoderSeekableStreamFlag;
2407 (void) RegisterMagickInfo(entry);
2408 entry=AcquireMagickInfo("PSD","PSD","Adobe Photoshop bitmap");
2409 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2410 entry->encoder=(EncodeImageHandler *) WritePSDImage;
2411 entry->magick=(IsImageFormatHandler *) IsPSD;
2412 entry->flags|=CoderDecoderSeekableStreamFlag;
2413 entry->flags|=CoderEncoderSeekableStreamFlag;
2414 (void) RegisterMagickInfo(entry);
2415 return(MagickImageCoderSignature);
2419 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2423 % U n r e g i s t e r P S D I m a g e %
2427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2429 % UnregisterPSDImage() removes format registrations made by the
2430 % PSD module from the list of supported formats.
2432 % The format of the UnregisterPSDImage method is:
2434 % UnregisterPSDImage(void)
2437 ModuleExport void UnregisterPSDImage(void)
2439 (void) UnregisterMagickInfo("PSB");
2440 (void) UnregisterMagickInfo("PSD");
2444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2448 % W r i t e P S D I m a g e %
2452 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2454 % WritePSDImage() writes an image in the Adobe Photoshop encoded image format.
2456 % The format of the WritePSDImage method is:
2458 % MagickBooleanType WritePSDImage(const ImageInfo *image_info,Image *image,
2459 % ExceptionInfo *exception)
2461 % A description of each parameter follows.
2463 % o image_info: the image info.
2465 % o image: The image.
2467 % o exception: return any errors or warnings in this structure.
2471 static inline ssize_t SetPSDOffset(const PSDInfo *psd_info,Image *image,
2472 const size_t offset)
2474 if (psd_info->version == 1)
2475 return(WriteBlobMSBShort(image,(unsigned short) offset));
2476 return(WriteBlobMSBLong(image,(unsigned int) offset));
2479 static inline ssize_t WritePSDOffset(const PSDInfo *psd_info,Image *image,
2480 const MagickSizeType size,const MagickSizeType offset)
2488 current_offset=TellBlob(image);
2489 SeekBlob(image,offset,SEEK_SET);
2490 if (psd_info->version == 1)
2491 result=WriteBlobMSBShort(image,(unsigned short) size);
2493 result=WriteBlobMSBLong(image,(unsigned int) size);
2494 SeekBlob(image,current_offset,SEEK_SET);
2498 static inline ssize_t SetPSDSize(const PSDInfo *psd_info,Image *image,
2499 const MagickSizeType size)
2501 if (psd_info->version == 1)
2502 return(WriteBlobLong(image,(unsigned int) size));
2503 return(WriteBlobLongLong(image,size));
2506 static inline ssize_t WritePSDSize(const PSDInfo *psd_info,Image *image,
2507 const MagickSizeType size,const MagickSizeType offset)
2515 current_offset=TellBlob(image);
2516 SeekBlob(image,offset,SEEK_SET);
2517 result=SetPSDSize(psd_info, image, size);
2518 SeekBlob(image,current_offset,SEEK_SET);
2522 static size_t PSDPackbitsEncodeImage(Image *image,const size_t length,
2523 const unsigned char *pixels,unsigned char *compact_pixels,
2524 ExceptionInfo *exception)
2533 register unsigned char
2540 Compress pixels with Packbits encoding.
2542 assert(image != (Image *) NULL);
2543 assert(image->signature == MagickCoreSignature);
2544 if (image->debug != MagickFalse)
2545 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2546 assert(pixels != (unsigned char *) NULL);
2547 assert(compact_pixels != (unsigned char *) NULL);
2548 packbits=(unsigned char *) AcquireQuantumMemory(128UL,sizeof(*packbits));
2549 if (packbits == (unsigned char *) NULL)
2550 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2553 for (i=(ssize_t) length; i != 0; )
2560 *q++=(unsigned char) 0;
2567 *q++=(unsigned char) 1;
2575 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2577 *q++=(unsigned char) ((256-3)+1);
2581 *q++=(unsigned char) 2;
2589 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2595 while (((ssize_t) count < i) && (*pixels == *(pixels+count)))
2602 *q++=(unsigned char) ((256-count)+1);
2611 while ((*(pixels+count) != *(pixels+count+1)) ||
2612 (*(pixels+count+1) != *(pixels+count+2)))
2614 packbits[count+1]=pixels[count];
2616 if (((ssize_t) count >= (i-3)) || (count >= 127))
2620 *packbits=(unsigned char) (count-1);
2621 for (j=0; j <= (ssize_t) count; j++)
2628 *q++=(unsigned char) 128; /* EOD marker */
2629 packbits=(unsigned char *) RelinquishMagickMemory(packbits);
2630 return((size_t) (q-compact_pixels));
2633 static size_t WriteCompressionStart(const PSDInfo *psd_info,Image *image,
2634 const Image *next_image,const CompressionType compression,
2635 const ssize_t channels)
2644 if (compression == RLECompression)
2646 length=WriteBlobShort(image,RLE);
2647 for (i=0; i < channels; i++)
2648 for (y=0; y < (ssize_t) next_image->rows; y++)
2649 length+=SetPSDOffset(psd_info,image,0);
2651 #ifdef MAGICKCORE_ZLIB_DELEGATE
2652 else if (compression == ZipCompression)
2653 length=WriteBlobShort(image,ZipWithoutPrediction);
2656 length=WriteBlobShort(image,Raw);
2660 static size_t WritePSDChannel(const PSDInfo *psd_info,
2661 const ImageInfo *image_info,Image *image,Image *next_image,
2662 const QuantumType quantum_type, unsigned char *compact_pixels,
2663 MagickOffsetType size_offset,const MagickBooleanType separate,
2664 const CompressionType compression,ExceptionInfo *exception)
2675 register const Quantum
2688 #ifdef MAGICKCORE_ZLIB_DELEGATE
2702 compressed_pixels=(unsigned char *) NULL;
2706 if (separate != MagickFalse)
2708 size_offset=TellBlob(image)+2;
2709 count+=WriteCompressionStart(psd_info,image,next_image,compression,1);
2711 if (next_image->depth > 8)
2712 next_image->depth=16;
2713 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
2714 MagickTrue : MagickFalse;
2715 quantum_info=AcquireQuantumInfo(image_info,next_image);
2716 if (quantum_info == (QuantumInfo *) NULL)
2718 pixels=(unsigned char *) GetQuantumPixels(quantum_info);
2719 #ifdef MAGICKCORE_ZLIB_DELEGATE
2720 if (compression == ZipCompression)
2722 compressed_pixels=(unsigned char *) AcquireQuantumMemory(CHUNK,
2723 sizeof(*compressed_pixels));
2724 if (compressed_pixels == (unsigned char *) NULL)
2726 quantum_info=DestroyQuantumInfo(quantum_info);
2729 memset(&stream,0,sizeof(stream));
2730 stream.data_type=Z_BINARY;
2731 level=Z_DEFAULT_COMPRESSION;
2732 if ((image_info->quality > 0 && image_info->quality < 10))
2733 level=(int) image_info->quality;
2734 if (deflateInit(&stream,level) != Z_OK)
2736 quantum_info=DestroyQuantumInfo(quantum_info);
2741 for (y=0; y < (ssize_t) next_image->rows; y++)
2743 p=GetVirtualPixels(next_image,0,y,next_image->columns,1,exception);
2744 if (p == (const Quantum *) NULL)
2746 length=ExportQuantumPixels(next_image,(CacheView *) NULL,quantum_info,
2747 quantum_type,pixels,exception);
2748 if (monochrome != MagickFalse)
2749 for (i=0; i < (ssize_t) length; i++)
2750 pixels[i]=(~pixels[i]);
2751 if (compression == RLECompression)
2753 length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels,
2755 count+=WriteBlob(image,length,compact_pixels);
2756 size_offset+=WritePSDOffset(psd_info,image,length,size_offset);
2758 #ifdef MAGICKCORE_ZLIB_DELEGATE
2759 else if (compression == ZipCompression)
2761 stream.avail_in=(uInt) length;
2762 stream.next_in=(Bytef *) pixels;
2763 if (y == (ssize_t) next_image->rows-1)
2766 stream.avail_out=(uInt) CHUNK;
2767 stream.next_out=(Bytef *) compressed_pixels;
2768 if (deflate(&stream,flush) == Z_STREAM_ERROR)
2770 length=(size_t) CHUNK-stream.avail_out;
2772 count+=WriteBlob(image,length,compressed_pixels);
2773 } while (stream.avail_out == 0);
2777 count+=WriteBlob(image,length,pixels);
2779 #ifdef MAGICKCORE_ZLIB_DELEGATE
2780 if (compression == ZipCompression)
2782 (void) deflateEnd(&stream);
2783 compressed_pixels=(unsigned char *) RelinquishMagickMemory(
2787 quantum_info=DestroyQuantumInfo(quantum_info);
2791 static unsigned char *AcquireCompactPixels(const Image *image,
2792 ExceptionInfo *exception)
2800 packet_size=image->depth > 8UL ? 2UL : 1UL;
2801 compact_pixels=(unsigned char *) AcquireQuantumMemory((9*
2802 image->columns)+1,packet_size*sizeof(*compact_pixels));
2803 if (compact_pixels == (unsigned char *) NULL)
2805 (void) ThrowMagickException(exception,GetMagickModule(),
2806 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2808 return(compact_pixels);
2811 static size_t WritePSDChannels(const PSDInfo *psd_info,
2812 const ImageInfo *image_info,Image *image,Image *next_image,
2813 MagickOffsetType size_offset,const MagickBooleanType separate,
2814 ExceptionInfo *exception)
2837 compact_pixels=(unsigned char *) NULL;
2838 compression=next_image->compression;
2839 if (image_info->compression != UndefinedCompression)
2840 compression=image_info->compression;
2841 if (compression == RLECompression)
2843 compact_pixels=AcquireCompactPixels(next_image,exception);
2844 if (compact_pixels == (unsigned char *) NULL)
2848 if (separate == MagickFalse)
2850 if (next_image->storage_class != PseudoClass)
2852 if (IsImageGray(next_image) == MagickFalse)
2853 channels=next_image->colorspace == CMYKColorspace ? 4 : 3;
2854 if (next_image->alpha_trait != UndefinedPixelTrait)
2857 rows_offset=TellBlob(image)+2;
2858 count+=WriteCompressionStart(psd_info,image,next_image,compression,
2860 offset_length=(next_image->rows*(psd_info->version == 1 ? 2 : 4));
2863 if (next_image->storage_class == PseudoClass)
2865 length=WritePSDChannel(psd_info,image_info,image,next_image,
2866 IndexQuantum,compact_pixels,rows_offset,separate,compression,
2868 if (separate != MagickFalse)
2869 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2871 rows_offset+=offset_length;
2876 if (IsImageGray(next_image) != MagickFalse)
2878 length=WritePSDChannel(psd_info,image_info,image,next_image,
2879 GrayQuantum,compact_pixels,rows_offset,separate,compression,
2881 if (separate != MagickFalse)
2882 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2884 rows_offset+=offset_length;
2889 if (next_image->colorspace == CMYKColorspace)
2890 (void) NegateCMYK(next_image,exception);
2892 length=WritePSDChannel(psd_info,image_info,image,next_image,
2893 RedQuantum,compact_pixels,rows_offset,separate,compression,
2895 if (separate != MagickFalse)
2896 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2898 rows_offset+=offset_length;
2901 length=WritePSDChannel(psd_info,image_info,image,next_image,
2902 GreenQuantum,compact_pixels,rows_offset,separate,compression,
2904 if (separate != MagickFalse)
2905 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2907 rows_offset+=offset_length;
2910 length=WritePSDChannel(psd_info,image_info,image,next_image,
2911 BlueQuantum,compact_pixels,rows_offset,separate,compression,
2913 if (separate != MagickFalse)
2914 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2916 rows_offset+=offset_length;
2919 if (next_image->colorspace == CMYKColorspace)
2921 length=WritePSDChannel(psd_info,image_info,image,next_image,
2922 BlackQuantum,compact_pixels,rows_offset,separate,compression,
2924 if (separate != MagickFalse)
2925 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2927 rows_offset+=offset_length;
2931 if (next_image->alpha_trait != UndefinedPixelTrait)
2933 length=WritePSDChannel(psd_info,image_info,image,next_image,
2934 AlphaQuantum,compact_pixels,rows_offset,separate,compression,
2936 if (separate != MagickFalse)
2937 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2939 rows_offset+=offset_length;
2943 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
2944 if (next_image->colorspace == CMYKColorspace)
2945 (void) NegateCMYK(next_image,exception);
2946 if (separate != MagickFalse)
2951 property=GetImageArtifact(next_image,"psd:opacity-mask");
2952 if (property != (const char *) NULL)
2954 mask=(Image *) GetImageRegistry(ImageRegistryType,property,
2956 if (mask != (Image *) NULL)
2958 if (compression == RLECompression)
2960 compact_pixels=AcquireCompactPixels(mask,exception);
2961 if (compact_pixels == (unsigned char *) NULL)
2964 length=WritePSDChannel(psd_info,image_info,image,mask,
2965 RedQuantum,compact_pixels,rows_offset,MagickTrue,compression,
2967 (void) WritePSDSize(psd_info,image,length,size_offset);
2969 compact_pixels=(unsigned char *) RelinquishMagickMemory(
2977 static size_t WritePascalString(Image *image,const char *value,size_t padding)
2990 length=(strlen(value) > 255UL ) ? 255UL : strlen(value);
2992 count+=WriteBlobByte(image,0);
2995 count+=WriteBlobByte(image,(unsigned char) length);
2996 count+=WriteBlob(image,length,(const unsigned char *) value);
2999 if ((length % padding) == 0)
3001 for (i=0; i < (ssize_t) (padding-(length % padding)); i++)
3002 count+=WriteBlobByte(image,0);
3006 static void WriteResolutionResourceBlock(Image *image)
3015 if (image->units == PixelsPerCentimeterResolution)
3017 x_resolution=2.54*65536.0*image->resolution.x+0.5;
3018 y_resolution=2.54*65536.0*image->resolution.y+0.5;
3023 x_resolution=65536.0*image->resolution.x+0.5;
3024 y_resolution=65536.0*image->resolution.y+0.5;
3027 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
3028 (void) WriteBlobMSBShort(image,0x03ED);
3029 (void) WriteBlobMSBShort(image,0);
3030 (void) WriteBlobMSBLong(image,16); /* resource size */
3031 (void) WriteBlobMSBLong(image,(unsigned int) (x_resolution+0.5));
3032 (void) WriteBlobMSBShort(image,units); /* horizontal resolution unit */
3033 (void) WriteBlobMSBShort(image,units); /* width unit */
3034 (void) WriteBlobMSBLong(image,(unsigned int) (y_resolution+0.5));
3035 (void) WriteBlobMSBShort(image,units); /* vertical resolution unit */
3036 (void) WriteBlobMSBShort(image,units); /* height unit */
3039 static inline size_t WriteChannelSize(const PSDInfo *psd_info,Image *image,
3040 const signed short channel)
3045 count=(size_t) WriteBlobShort(image,channel);
3046 count+=SetPSDSize(psd_info,image,0);
3050 static void RemoveICCProfileFromResourceBlock(StringInfo *bim_profile)
3052 register const unsigned char
3069 length=GetStringInfoLength(bim_profile);
3072 datum=GetStringInfoDatum(bim_profile);
3073 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
3075 register unsigned char
3078 q=(unsigned char *) p;
3079 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
3081 p=PushLongPixel(MSBEndian,p,&long_sans);
3082 p=PushShortPixel(MSBEndian,p,&id);
3083 p=PushShortPixel(MSBEndian,p,&short_sans);
3084 p=PushLongPixel(MSBEndian,p,&count);
3085 if (id == 0x0000040f)
3090 quantum=PSDQuantum(count)+12;
3091 if ((quantum >= 12) && (quantum < (ssize_t) length))
3093 if ((q+quantum < (datum+length-16)))
3094 (void) memmove(q,q+quantum,length-quantum-(q-datum));
3095 SetStringInfoLength(bim_profile,length-quantum);
3100 if ((count & 0x01) != 0)
3105 static void RemoveResolutionFromResourceBlock(StringInfo *bim_profile)
3107 register const unsigned char
3124 length=GetStringInfoLength(bim_profile);
3127 datum=GetStringInfoDatum(bim_profile);
3128 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
3130 register unsigned char
3136 q=(unsigned char *) p;
3137 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
3139 p=PushLongPixel(MSBEndian,p,&long_sans);
3140 p=PushShortPixel(MSBEndian,p,&id);
3141 p=PushShortPixel(MSBEndian,p,&short_sans);
3142 p=PushLongPixel(MSBEndian,p,&count);
3143 cnt=PSDQuantum(count);
3146 if ((id == 0x000003ed) && (cnt < (ssize_t) (length-12)) &&
3147 ((ssize_t) length-(cnt+12)-(q-datum)) > 0)
3149 (void) memmove(q,q+cnt+12,length-(cnt+12)-(q-datum));
3150 SetStringInfoLength(bim_profile,length-(cnt+12));
3154 if ((count & 0x01) != 0)
3159 static const StringInfo *GetAdditionalInformation(const ImageInfo *image_info,
3160 Image *image,ExceptionInfo *exception)
3162 #define PSDKeySize 5
3163 #define PSDAllowedLength 36
3168 /* Whitelist of keys from: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ */
3170 allowed[PSDAllowedLength][PSDKeySize] = {
3171 "blnc", "blwh", "brit", "brst", "clbl", "clrL", "curv", "expA", "FMsk",
3172 "GdFl", "grdm", "hue ", "hue2", "infx", "knko", "lclr", "levl", "lnsr",
3173 "lfx2", "luni", "lrFX", "lspf", "lyid", "lyvr", "mixr", "nvrt", "phfl",
3174 "post", "PtFl", "selc", "shpa", "sn2P", "SoCo", "thrs", "tsly", "vibA"
3200 info=GetImageProfile(image,"psd:additional-info");
3201 if (info == (const StringInfo *) NULL)
3202 return((const StringInfo *) NULL);
3203 option=GetImageOption(image_info,"psd:additional-info");
3204 if (LocaleCompare(option,"all") == 0)
3206 if (LocaleCompare(option,"selective") != 0)
3208 profile=RemoveImageProfile(image,"psd:additional-info");
3209 return(DestroyStringInfo(profile));
3211 length=GetStringInfoLength(info);
3212 p=GetStringInfoDatum(info);
3213 remaining_length=length;
3215 while (remaining_length >= 12)
3217 /* skip over signature */
3224 size=(unsigned int) (*p++) << 24;
3225 size|=(unsigned int) (*p++) << 16;
3226 size|=(unsigned int) (*p++) << 8;
3227 size|=(unsigned int) (*p++);
3228 size=size & 0xffffffff;
3229 remaining_length-=12;
3230 if ((size_t) size > remaining_length)
3231 return((const StringInfo *) NULL);
3233 for (i=0; i < PSDAllowedLength; i++)
3235 if (LocaleNCompare(key,allowed[i],PSDKeySize) != 0)
3241 remaining_length-=(size_t) size;
3242 if (found == MagickFalse)
3244 if (remaining_length > 0)
3245 p=(unsigned char *) memmove(p-12,p+size,remaining_length);
3248 length+=(size_t) size+12;
3251 profile=RemoveImageProfile(image,"psd:additional-info");
3253 return(DestroyStringInfo(profile));
3254 SetStringInfoLength(profile,(const size_t) length);
3255 SetImageProfile(image,"psd:additional-info",info,exception);
3259 static MagickBooleanType WritePSDLayersInternal(Image *image,
3260 const ImageInfo *image_info,const PSDInfo *psd_info,size_t *layers_size,
3261 ExceptionInfo *exception)
3264 layer_name[MagickPathExtent];
3280 *layer_size_offsets,
3295 base_image=GetNextImageInList(image);
3296 if (base_image == (Image *) NULL)
3299 size_offset=TellBlob(image);
3300 SetPSDSize(psd_info,image,0);
3302 for (next_image=base_image; next_image != NULL; )
3305 next_image=GetNextImageInList(next_image);
3307 if (image->alpha_trait != UndefinedPixelTrait)
3308 size+=WriteBlobShort(image,-(unsigned short) layer_count);
3310 size+=WriteBlobShort(image,(unsigned short) layer_count);
3311 layer_size_offsets=(MagickOffsetType *) AcquireQuantumMemory(
3312 (size_t) layer_count,sizeof(MagickOffsetType));
3313 if (layer_size_offsets == (MagickOffsetType *) NULL)
3314 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3316 for (next_image=base_image; next_image != NULL; )
3328 mask=(Image *) NULL;
3329 property=GetImageArtifact(next_image,"psd:opacity-mask");
3331 if (property != (const char *) NULL)
3333 mask=(Image *) GetImageRegistry(ImageRegistryType,property,exception);
3334 default_color=strlen(property) == 9 ? 255 : 0;
3336 size+=WriteBlobSignedLong(image,(signed int) next_image->page.y);
3337 size+=WriteBlobSignedLong(image,(signed int) next_image->page.x);
3338 size+=WriteBlobSignedLong(image,(signed int) (next_image->page.y+
3340 size+=WriteBlobSignedLong(image,(signed int) (next_image->page.x+
3341 next_image->columns));
3343 if ((next_image->storage_class != PseudoClass) &&
3344 (IsImageGray(next_image) == MagickFalse))
3345 channels=next_image->colorspace == CMYKColorspace ? 4U : 3U;
3346 total_channels=channels;
3347 if (next_image->alpha_trait != UndefinedPixelTrait)
3349 if (mask != (Image *) NULL)
3351 size+=WriteBlobShort(image,total_channels);
3352 layer_size_offsets[layer_index++]=TellBlob(image);
3353 for (i=0; i < (ssize_t) channels; i++)
3354 size+=WriteChannelSize(psd_info,image,(signed short) i);
3355 if (next_image->alpha_trait != UndefinedPixelTrait)
3356 size+=WriteChannelSize(psd_info,image,-1);
3357 if (mask != (Image *) NULL)
3358 size+=WriteChannelSize(psd_info,image,-2);
3359 size+=WriteBlobString(image,image->endian == LSBEndian ? "MIB8" :"8BIM");
3360 size+=WriteBlobString(image,CompositeOperatorToPSDBlendMode(next_image));
3361 property=GetImageArtifact(next_image,"psd:layer.opacity");
3362 if (property != (const char *) NULL)
3367 opacity=(Quantum) StringToInteger(property);
3368 size+=WriteBlobByte(image,ScaleQuantumToChar(opacity));
3369 (void) ApplyPSDLayerOpacity(next_image,opacity,MagickTrue,exception);
3372 size+=WriteBlobByte(image,255);
3373 size+=WriteBlobByte(image,0);
3374 size+=WriteBlobByte(image,next_image->compose==NoCompositeOp ?
3375 1 << 0x02 : 1); /* layer properties - visible, etc. */
3376 size+=WriteBlobByte(image,0);
3377 info=GetAdditionalInformation(image_info,next_image,exception);
3378 property=(const char *) GetImageProperty(next_image,"label",exception);
3379 if (property == (const char *) NULL)
3381 (void) FormatLocaleString(layer_name,MagickPathExtent,"L%.20g",
3382 (double) layer_index);
3383 property=layer_name;
3385 name_length=strlen(property)+1;
3386 if ((name_length % 4) != 0)
3387 name_length+=(4-(name_length % 4));
3388 if (info != (const StringInfo *) NULL)
3389 name_length+=GetStringInfoLength(info);
3391 if (mask != (Image *) NULL)
3393 size+=WriteBlobLong(image,(unsigned int) name_length);
3394 if (mask == (Image *) NULL)
3395 size+=WriteBlobLong(image,0);
3398 if (mask->compose != NoCompositeOp)
3399 (void) ApplyPSDOpacityMask(next_image,mask,ScaleCharToQuantum(
3400 default_color),MagickTrue,exception);
3401 mask->page.y+=image->page.y;
3402 mask->page.x+=image->page.x;
3403 size+=WriteBlobLong(image,20);
3404 size+=WriteBlobSignedLong(image,mask->page.y);
3405 size+=WriteBlobSignedLong(image,mask->page.x);
3406 size+=WriteBlobSignedLong(image,(const signed int) mask->rows+
3408 size+=WriteBlobSignedLong(image,(const signed int) mask->columns+
3410 size+=WriteBlobByte(image,default_color);
3411 size+=WriteBlobByte(image,mask->compose == NoCompositeOp ? 2 : 0);
3412 size+=WriteBlobMSBShort(image,0);
3414 size+=WriteBlobLong(image,0);
3415 size+=WritePascalString(image,property,4);
3416 if (info != (const StringInfo *) NULL)
3417 size+=WriteBlob(image,GetStringInfoLength(info),
3418 GetStringInfoDatum(info));
3419 next_image=GetNextImageInList(next_image);
3424 next_image=base_image;
3426 while (next_image != NULL)
3428 length=WritePSDChannels(psd_info,image_info,image,next_image,
3429 layer_size_offsets[layer_index++],MagickTrue,exception);
3436 next_image=GetNextImageInList(next_image);
3439 Write the total size
3441 if (layers_size != (size_t*) NULL)
3443 if ((size/2) != ((size+1)/2))
3444 rounded_size=size+1;
3447 (void) WritePSDSize(psd_info,image,rounded_size,size_offset);
3448 layer_size_offsets=(MagickOffsetType *) RelinquishMagickMemory(
3449 layer_size_offsets);
3451 Remove the opacity mask from the registry
3453 next_image=base_image;
3454 while (next_image != (Image *) NULL)
3456 property=GetImageArtifact(next_image,"psd:opacity-mask");
3457 if (property != (const char *) NULL)
3458 DeleteImageRegistry(property);
3459 next_image=GetNextImageInList(next_image);
3465 ModuleExport MagickBooleanType WritePSDLayers(Image * image,
3466 const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception)
3474 domain=CoderPolicyDomain;
3475 rights=WritePolicyRights;
3476 if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
3478 return WritePSDLayersInternal(image,image_info,psd_info,(size_t*) NULL,
3482 static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
3483 Image *image,ExceptionInfo *exception)
3508 assert(image_info != (const ImageInfo *) NULL);
3509 assert(image_info->signature == MagickCoreSignature);
3510 assert(image != (Image *) NULL);
3511 assert(image->signature == MagickCoreSignature);
3512 if (image->debug != MagickFalse)
3513 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3514 assert(exception != (ExceptionInfo *) NULL);
3515 assert(exception->signature == MagickCoreSignature);
3516 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
3517 if (status == MagickFalse)
3519 packet_size=(size_t) (image->depth > 8 ? 6 : 3);
3520 if (image->alpha_trait != UndefinedPixelTrait)
3521 packet_size+=image->depth > 8 ? 2 : 1;
3523 if ((LocaleCompare(image_info->magick,"PSB") == 0) ||
3524 (image->columns > 30000) || (image->rows > 30000))
3526 (void) WriteBlob(image,4,(const unsigned char *) "8BPS");
3527 (void) WriteBlobMSBShort(image,psd_info.version); /* version */
3528 for (i=1; i <= 6; i++)
3529 (void) WriteBlobByte(image, 0); /* 6 bytes of reserved */
3530 /* When the image has a color profile it won't be converted to gray scale */
3531 if ((GetImageProfile(image,"icc") == (StringInfo *) NULL) &&
3532 (SetImageGray(image,exception) != MagickFalse))
3533 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
3535 if ((image_info->type != TrueColorType) && (image_info->type !=
3536 TrueColorAlphaType) && (image->storage_class == PseudoClass))
3537 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
3540 if (image->storage_class == PseudoClass)
3541 (void) SetImageStorageClass(image,DirectClass,exception);
3542 if (image->colorspace != CMYKColorspace)
3543 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 4UL : 3UL);
3545 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 5UL : 4UL);
3547 (void) WriteBlobMSBShort(image,(unsigned short) num_channels);
3548 (void) WriteBlobMSBLong(image,(unsigned int) image->rows);
3549 (void) WriteBlobMSBLong(image,(unsigned int) image->columns);
3550 if (IsImageGray(image) != MagickFalse)
3558 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
3559 MagickTrue : MagickFalse;
3560 (void) WriteBlobMSBShort(image,(unsigned short)
3561 (monochrome != MagickFalse ? 1 : image->depth > 8 ? 16 : 8));
3562 (void) WriteBlobMSBShort(image,(unsigned short)
3563 (monochrome != MagickFalse ? BitmapMode : GrayscaleMode));
3567 (void) WriteBlobMSBShort(image,(unsigned short) (image->storage_class ==
3568 PseudoClass ? 8 : image->depth > 8 ? 16 : 8));
3570 if (((image_info->colorspace != UndefinedColorspace) ||
3571 (image->colorspace != CMYKColorspace)) &&
3572 (image_info->colorspace != CMYKColorspace))
3574 (void) TransformImageColorspace(image,sRGBColorspace,exception);
3575 (void) WriteBlobMSBShort(image,(unsigned short)
3576 (image->storage_class == PseudoClass ? IndexedMode : RGBMode));
3580 if (image->colorspace != CMYKColorspace)
3581 (void) TransformImageColorspace(image,CMYKColorspace,exception);
3582 (void) WriteBlobMSBShort(image,CMYKMode);
3585 if ((IsImageGray(image) != MagickFalse) ||
3586 (image->storage_class == DirectClass) || (image->colors > 256))
3587 (void) WriteBlobMSBLong(image,0);
3591 Write PSD raster colormap.
3593 (void) WriteBlobMSBLong(image,768);
3594 for (i=0; i < (ssize_t) image->colors; i++)
3595 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].red));
3596 for ( ; i < 256; i++)
3597 (void) WriteBlobByte(image,0);
3598 for (i=0; i < (ssize_t) image->colors; i++)
3599 (void) WriteBlobByte(image,ScaleQuantumToChar(
3600 image->colormap[i].green));
3601 for ( ; i < 256; i++)
3602 (void) WriteBlobByte(image,0);
3603 for (i=0; i < (ssize_t) image->colors; i++)
3604 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].blue));
3605 for ( ; i < 256; i++)
3606 (void) WriteBlobByte(image,0);
3609 Image resource block.
3611 length=28; /* 0x03EB */
3612 bim_profile=(StringInfo *) GetImageProfile(image,"8bim");
3613 icc_profile=GetImageProfile(image,"icc");
3614 if (bim_profile != (StringInfo *) NULL)
3616 bim_profile=CloneStringInfo(bim_profile);
3617 if (icc_profile != (StringInfo *) NULL)
3618 RemoveICCProfileFromResourceBlock(bim_profile);
3619 RemoveResolutionFromResourceBlock(bim_profile);
3620 length+=PSDQuantum(GetStringInfoLength(bim_profile));
3622 if (icc_profile != (const StringInfo *) NULL)
3623 length+=PSDQuantum(GetStringInfoLength(icc_profile))+12;
3624 (void) WriteBlobMSBLong(image,(unsigned int) length);
3625 WriteResolutionResourceBlock(image);
3626 if (bim_profile != (StringInfo *) NULL)
3628 (void) WriteBlob(image,GetStringInfoLength(bim_profile),
3629 GetStringInfoDatum(bim_profile));
3630 bim_profile=DestroyStringInfo(bim_profile);
3632 if (icc_profile != (StringInfo *) NULL)
3634 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
3635 (void) WriteBlobMSBShort(image,0x0000040F);
3636 (void) WriteBlobMSBShort(image,0);
3637 (void) WriteBlobMSBLong(image,(unsigned int) GetStringInfoLength(
3639 (void) WriteBlob(image,GetStringInfoLength(icc_profile),
3640 GetStringInfoDatum(icc_profile));
3641 if ((MagickOffsetType) GetStringInfoLength(icc_profile) !=
3642 PSDQuantum(GetStringInfoLength(icc_profile)))
3643 (void) WriteBlobByte(image,0);
3645 if (status != MagickFalse)
3653 size_offset=TellBlob(image);
3654 SetPSDSize(&psd_info,image,0);
3655 status=WritePSDLayersInternal(image,image_info,&psd_info,&size,
3657 size_offset+=WritePSDSize(&psd_info,image,size+
3658 (psd_info.version == 1 ? 8 : 12),size_offset);
3660 (void) WriteBlobMSBLong(image,0); /* user mask data */
3662 Write composite image.
3664 if (status != MagickFalse)
3669 compression=image->compression;
3670 if (image->compression == ZipCompression)
3671 image->compression=RLECompression;
3672 if (image_info->compression != UndefinedCompression)
3673 image->compression=image_info->compression;
3674 if (WritePSDChannels(&psd_info,image_info,image,image,0,MagickFalse,
3677 image->compression=compression;
3679 (void) CloseBlob(image);