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,4) 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,4) 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,4) 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 compact_pixels=(unsigned char *) AcquireQuantumMemory(compact_size,
1239 sizeof(*compact_pixels));
1240 if (compact_pixels == (unsigned char *) NULL)
1241 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1244 packet_size=GetPSDPacketSize(image);
1245 row_size=image->columns*packet_size;
1246 count=image->rows*row_size;
1248 pixels=(unsigned char *) AcquireQuantumMemory(count,sizeof(*pixels));
1249 if (pixels == (unsigned char *) NULL)
1251 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1252 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1255 if (ReadBlob(image,compact_size,compact_pixels) != (ssize_t) compact_size)
1257 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1258 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1259 ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile",
1263 memset(&stream,0,sizeof(stream));
1264 stream.data_type=Z_BINARY;
1265 stream.next_in=(Bytef *)compact_pixels;
1266 stream.avail_in=(uInt) compact_size;
1267 stream.next_out=(Bytef *)pixels;
1268 stream.avail_out=(uInt) count;
1270 if (inflateInit(&stream) == Z_OK)
1275 while (stream.avail_out > 0)
1277 ret=inflate(&stream,Z_SYNC_FLUSH);
1278 if ((ret != Z_OK) && (ret != Z_STREAM_END))
1280 (void) inflateEnd(&stream);
1281 compact_pixels=(unsigned char *) RelinquishMagickMemory(
1283 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1284 return(MagickFalse);
1286 if (ret == Z_STREAM_END)
1289 (void) inflateEnd(&stream);
1292 if (compression == ZipWithPrediction)
1297 length=image->columns;
1300 if (packet_size == 2)
1302 p[2]+=p[0]+((p[1]+p[3]) >> 8);
1305 // else if (packet_size == 4)
1307 // TODO: Figure out what to do there.
1320 for (y=0; y < (ssize_t) image->rows; y++)
1322 status=ReadPSDChannelPixels(image,channels,y,type,p,exception);
1323 if (status == MagickFalse)
1329 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1330 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1335 static MagickBooleanType ReadPSDChannel(Image *image,
1336 const ImageInfo *image_info,const PSDInfo *psd_info,LayerInfo* layer_info,
1337 const size_t channel,const PSDCompressionType compression,
1338 ExceptionInfo *exception)
1350 channel_image=image;
1351 mask=(Image *) NULL;
1352 if ((layer_info->channel_info[channel].type < -1) &&
1353 (layer_info->mask.page.width > 0) && (layer_info->mask.page.height > 0))
1359 Ignore mask that is not a user supplied layer mask, if the mask is
1360 disabled or if the flags have unsupported values.
1362 option=GetImageOption(image_info,"psd:preserve-opacity-mask");
1363 if ((layer_info->channel_info[channel].type != -2) ||
1364 (layer_info->mask.flags > 2) || ((layer_info->mask.flags & 0x02) &&
1365 (IsStringTrue(option) == MagickFalse)))
1367 SeekBlob(image,layer_info->channel_info[channel].size-2,SEEK_CUR);
1370 mask=CloneImage(image,layer_info->mask.page.width,
1371 layer_info->mask.page.height,MagickFalse,exception);
1372 if (mask != (Image *) NULL)
1374 SetImageType(mask,GrayscaleType,exception);
1379 offset=TellBlob(image);
1384 status=ReadPSDChannelRaw(channel_image,psd_info->channels,
1385 layer_info->channel_info[channel].type,exception);
1392 sizes=ReadPSDRLESizes(channel_image,psd_info,channel_image->rows);
1393 if (sizes == (MagickOffsetType *) NULL)
1394 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1396 status=ReadPSDChannelRLE(channel_image,psd_info,
1397 layer_info->channel_info[channel].type,sizes,exception);
1398 sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
1401 case ZipWithPrediction:
1402 case ZipWithoutPrediction:
1403 #ifdef MAGICKCORE_ZLIB_DELEGATE
1404 status=ReadPSDChannelZip(channel_image,layer_info->channels,
1405 layer_info->channel_info[channel].type,compression,
1406 layer_info->channel_info[channel].size-2,exception);
1408 (void) ThrowMagickException(exception,GetMagickModule(),
1409 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
1410 "'%s' (ZLIB)",image->filename);
1414 (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
1415 "CompressionNotSupported","'%.20g'",(double) compression);
1419 SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
1420 if (status == MagickFalse)
1422 if (mask != (Image *) NULL)
1424 ThrowBinaryException(CoderError,"UnableToDecompressImage",
1427 layer_info->mask.image=mask;
1431 static MagickBooleanType ReadPSDLayer(Image *image,const ImageInfo *image_info,
1432 const PSDInfo *psd_info,LayerInfo* layer_info,ExceptionInfo *exception)
1435 message[MagickPathExtent];
1446 if (image->debug != MagickFalse)
1447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1448 " setting up new layer image");
1449 if (psd_info->mode != IndexedMode)
1450 (void) SetImageBackgroundColor(layer_info->image,exception);
1451 layer_info->image->compose=PSDBlendModeToCompositeOperator(
1452 layer_info->blendkey);
1453 if (layer_info->visible == MagickFalse)
1454 layer_info->image->compose=NoCompositeOp;
1456 Set up some hidden attributes for folks that need them.
1458 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
1459 (double) layer_info->page.x);
1460 (void) SetImageArtifact(layer_info->image,"psd:layer.x",message);
1461 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
1462 (double) layer_info->page.y);
1463 (void) SetImageArtifact(layer_info->image,"psd:layer.y",message);
1464 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",(double)
1465 layer_info->opacity);
1466 (void) SetImageArtifact(layer_info->image,"psd:layer.opacity",message);
1467 (void) SetImageProperty(layer_info->image,"label",(char *) layer_info->name,
1471 for (j=0; j < (ssize_t) layer_info->channels; j++)
1473 if (image->debug != MagickFalse)
1474 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1475 " reading data for channel %.20g",(double) j);
1477 compression=(PSDCompressionType) ReadBlobShort(layer_info->image);
1479 /* TODO: Remove this when we figure out how to support this */
1480 if ((compression == ZipWithPrediction) && (image->depth == 32))
1482 (void) ThrowMagickException(exception,GetMagickModule(),
1483 TypeError,"CompressionNotSupported","ZipWithPrediction(32 bit)");
1484 return(MagickFalse);
1487 layer_info->image->compression=ConvertPSDCompression(compression);
1488 if (layer_info->channel_info[j].type == -1)
1489 layer_info->image->alpha_trait=BlendPixelTrait;
1491 status=ReadPSDChannel(layer_info->image,image_info,psd_info,layer_info,j,
1492 compression,exception);
1494 if (status == MagickFalse)
1498 if (status != MagickFalse)
1499 status=ApplyPSDLayerOpacity(layer_info->image,layer_info->opacity,
1500 MagickFalse,exception);
1502 if ((status != MagickFalse) &&
1503 (layer_info->image->colorspace == CMYKColorspace))
1504 status=NegateCMYK(layer_info->image,exception);
1506 if ((status != MagickFalse) && (layer_info->mask.image != (Image *) NULL))
1511 layer_info->mask.image->page.x=layer_info->mask.page.x;
1512 layer_info->mask.image->page.y=layer_info->mask.page.y;
1513 /* Do not composite the mask when it is disabled */
1514 if ((layer_info->mask.flags & 0x02) == 0x02)
1515 layer_info->mask.image->compose=NoCompositeOp;
1517 status=ApplyPSDOpacityMask(layer_info->image,layer_info->mask.image,
1518 layer_info->mask.background == 0 ? 0 : QuantumRange,MagickFalse,
1520 option=GetImageOption(image_info,"psd:preserve-opacity-mask");
1521 if (IsStringTrue(option) != MagickFalse)
1522 PreservePSDOpacityMask(image,layer_info,exception);
1523 layer_info->mask.image=DestroyImage(layer_info->mask.image);
1529 static MagickBooleanType CheckPSDChannels(const PSDInfo *psd_info,
1530 LayerInfo *layer_info)
1538 if (layer_info->channels < psd_info->min_channels)
1539 return(MagickFalse);
1540 channel_type=RedChannel;
1541 if (psd_info->min_channels >= 3)
1542 channel_type|=(GreenChannel | BlueChannel);
1543 if (psd_info->min_channels >= 4)
1544 channel_type|=BlackChannel;
1545 for (i=0; i < layer_info->channels; i++)
1550 type=layer_info->channel_info[i].type;
1553 channel_type|=AlphaChannel;
1559 channel_type&=~RedChannel;
1561 channel_type&=~GreenChannel;
1563 channel_type&=~BlueChannel;
1565 channel_type&=~BlackChannel;
1567 if (channel_type == 0)
1569 if ((channel_type == AlphaChannel) &&
1570 (layer_info->channels >= psd_info->min_channels + 1))
1572 return(MagickFalse);
1575 static MagickBooleanType ReadPSDLayersInternal(Image *image,
1576 const ImageInfo *image_info,const PSDInfo *psd_info,
1577 const MagickBooleanType skip_layers,ExceptionInfo *exception)
1599 size=GetPSDSize(psd_info,image);
1603 Skip layers & masks.
1605 (void) ReadBlobLong(image);
1606 count=ReadBlob(image,4,(unsigned char *) type);
1607 ReversePSDString(image,type,4);
1609 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1613 count=ReadBlob(image,4,(unsigned char *) type);
1614 ReversePSDString(image,type,4);
1615 if ((count != 0) && ((LocaleNCompare(type,"Lr16",4) == 0) ||
1616 (LocaleNCompare(type,"Lr32",4) == 0)))
1617 size=GetPSDSize(psd_info,image);
1625 layer_info=(LayerInfo *) NULL;
1626 number_layers=(short) ReadBlobShort(image);
1628 if (number_layers < 0)
1631 The first alpha channel in the merged result contains the
1632 transparency data for the merged result.
1634 number_layers=MagickAbsoluteValue(number_layers);
1635 if (image->debug != MagickFalse)
1636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1637 " negative layer count corrected for");
1638 image->alpha_trait=BlendPixelTrait;
1642 We only need to know if the image has an alpha channel
1644 if (skip_layers != MagickFalse)
1647 if (image->debug != MagickFalse)
1648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1649 " image contains %.20g layers",(double) number_layers);
1651 if (number_layers == 0)
1652 ThrowBinaryException(CorruptImageError,"InvalidNumberOfLayers",
1655 layer_info=(LayerInfo *) AcquireQuantumMemory((size_t) number_layers,
1656 sizeof(*layer_info));
1657 if (layer_info == (LayerInfo *) NULL)
1659 if (image->debug != MagickFalse)
1660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1661 " allocation of LayerInfo failed");
1662 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1665 (void) memset(layer_info,0,(size_t) number_layers*
1666 sizeof(*layer_info));
1668 for (i=0; i < number_layers; i++)
1674 if (image->debug != MagickFalse)
1675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1676 " reading layer #%.20g",(double) i+1);
1677 layer_info[i].page.y=ReadBlobSignedLong(image);
1678 layer_info[i].page.x=ReadBlobSignedLong(image);
1679 y=ReadBlobSignedLong(image);
1680 x=ReadBlobSignedLong(image);
1681 layer_info[i].page.width=(size_t) (x-layer_info[i].page.x);
1682 layer_info[i].page.height=(size_t) (y-layer_info[i].page.y);
1683 layer_info[i].channels=ReadBlobShort(image);
1684 if (layer_info[i].channels > MaxPSDChannels)
1686 layer_info=DestroyLayerInfo(layer_info,number_layers);
1687 ThrowBinaryException(CorruptImageError,"MaximumChannelsExceeded",
1690 if (image->debug != MagickFalse)
1691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1692 " offset(%.20g,%.20g), size(%.20g,%.20g), channels=%.20g",
1693 (double) layer_info[i].page.x,(double) layer_info[i].page.y,
1694 (double) layer_info[i].page.height,(double)
1695 layer_info[i].page.width,(double) layer_info[i].channels);
1696 for (j=0; j < (ssize_t) layer_info[i].channels; j++)
1698 layer_info[i].channel_info[j].type=(short) ReadBlobShort(image);
1699 if ((layer_info[i].channel_info[j].type < -4) ||
1700 (layer_info[i].channel_info[j].type > 4))
1702 layer_info=DestroyLayerInfo(layer_info,number_layers);
1703 ThrowBinaryException(CorruptImageError,"NoSuchImageChannel",
1706 layer_info[i].channel_info[j].size=(size_t) GetPSDSize(psd_info,
1708 if (image->debug != MagickFalse)
1709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1710 " channel[%.20g]: type=%.20g, size=%.20g",(double) j,
1711 (double) layer_info[i].channel_info[j].type,
1712 (double) layer_info[i].channel_info[j].size);
1714 if (CheckPSDChannels(psd_info,&layer_info[i]) == MagickFalse)
1716 layer_info=DestroyLayerInfo(layer_info,number_layers);
1717 ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
1720 count=ReadBlob(image,4,(unsigned char *) type);
1721 ReversePSDString(image,type,4);
1722 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1724 if (image->debug != MagickFalse)
1725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1726 " layer type was %.4s instead of 8BIM", type);
1727 layer_info=DestroyLayerInfo(layer_info,number_layers);
1728 ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
1731 count=ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey);
1732 ReversePSDString(image,layer_info[i].blendkey,4);
1733 layer_info[i].opacity=(Quantum) ScaleCharToQuantum((unsigned char)
1734 ReadBlobByte(image));
1735 layer_info[i].clipping=(unsigned char) ReadBlobByte(image);
1736 layer_info[i].flags=(unsigned char) ReadBlobByte(image);
1737 layer_info[i].visible=!(layer_info[i].flags & 0x02);
1738 if (image->debug != MagickFalse)
1739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1740 " blend=%.4s, opacity=%.20g, clipping=%s, flags=%d, visible=%s",
1741 layer_info[i].blendkey,(double) layer_info[i].opacity,
1742 layer_info[i].clipping ? "true" : "false",layer_info[i].flags,
1743 layer_info[i].visible ? "true" : "false");
1744 (void) ReadBlobByte(image); /* filler */
1746 size=ReadBlobLong(image);
1753 if (image->debug != MagickFalse)
1754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1755 " layer contains additional info");
1756 length=ReadBlobLong(image);
1757 combined_length=length+4;
1763 layer_info[i].mask.page.y=ReadBlobSignedLong(image);
1764 layer_info[i].mask.page.x=ReadBlobSignedLong(image);
1765 layer_info[i].mask.page.height=(size_t) (ReadBlobSignedLong(image)-
1766 layer_info[i].mask.page.y);
1767 layer_info[i].mask.page.width=(size_t) (ReadBlobSignedLong(image)-
1768 layer_info[i].mask.page.x);
1769 layer_info[i].mask.background=(unsigned char) ReadBlobByte(
1771 layer_info[i].mask.flags=(unsigned char) ReadBlobByte(image);
1772 if (!(layer_info[i].mask.flags & 0x01))
1774 layer_info[i].mask.page.y=layer_info[i].mask.page.y-
1775 layer_info[i].page.y;
1776 layer_info[i].mask.page.x=layer_info[i].mask.page.x-
1777 layer_info[i].page.x;
1779 if (image->debug != MagickFalse)
1780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1781 " layer mask: offset(%.20g,%.20g), size(%.20g,%.20g), length=%.20g",
1782 (double) layer_info[i].mask.page.x,(double)
1783 layer_info[i].mask.page.y,(double)
1784 layer_info[i].mask.page.width,(double)
1785 layer_info[i].mask.page.height,(double) ((MagickOffsetType)
1788 Skip over the rest of the layer mask information.
1790 if (DiscardBlobBytes(image,(MagickSizeType) (length-18)) == MagickFalse)
1792 layer_info=DestroyLayerInfo(layer_info,number_layers);
1793 ThrowBinaryException(CorruptImageError,
1794 "UnexpectedEndOfFile",image->filename);
1797 length=ReadBlobLong(image);
1798 combined_length+=length+4;
1802 Layer blending ranges info.
1804 if (image->debug != MagickFalse)
1805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1806 " layer blending ranges: length=%.20g",(double)
1807 ((MagickOffsetType) length));
1808 if (DiscardBlobBytes(image,length) == MagickFalse)
1810 layer_info=DestroyLayerInfo(layer_info,number_layers);
1811 ThrowBinaryException(CorruptImageError,
1812 "UnexpectedEndOfFile",image->filename);
1818 length=(MagickSizeType) (unsigned char) ReadBlobByte(image);
1819 combined_length+=length+1;
1821 (void) ReadBlob(image,(size_t) length++,layer_info[i].name);
1822 layer_info[i].name[length]='\0';
1823 if (image->debug != MagickFalse)
1824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1825 " layer name: %s",layer_info[i].name);
1826 if ((length % 4) != 0)
1828 length=4-(length % 4);
1829 combined_length+=length;
1830 /* Skip over the padding of the layer name */
1831 if (DiscardBlobBytes(image,length) == MagickFalse)
1833 layer_info=DestroyLayerInfo(layer_info,number_layers);
1834 ThrowBinaryException(CorruptImageError,
1835 "UnexpectedEndOfFile",image->filename);
1838 length=(MagickSizeType) size-combined_length;
1844 if (length > GetBlobSize(image))
1846 layer_info=DestroyLayerInfo(layer_info,number_layers);
1847 ThrowBinaryException(CorruptImageError,
1848 "InsufficientImageDataInFile",image->filename);
1850 layer_info[i].info=AcquireStringInfo((const size_t) length);
1851 info=GetStringInfoDatum(layer_info[i].info);
1852 (void) ReadBlob(image,(const size_t) length,info);
1857 for (i=0; i < number_layers; i++)
1859 if ((layer_info[i].page.width == 0) || (layer_info[i].page.height == 0))
1861 if (image->debug != MagickFalse)
1862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1863 " layer data is empty");
1864 if (layer_info[i].info != (StringInfo *) NULL)
1865 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
1870 Allocate layered image.
1872 layer_info[i].image=CloneImage(image,layer_info[i].page.width,
1873 layer_info[i].page.height,MagickFalse,exception);
1874 if (layer_info[i].image == (Image *) NULL)
1876 layer_info=DestroyLayerInfo(layer_info,number_layers);
1877 if (image->debug != MagickFalse)
1878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1879 " allocation of image for layer %.20g failed",(double) i);
1880 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1883 if (layer_info[i].info != (StringInfo *) NULL)
1885 (void) SetImageProfile(layer_info[i].image,"psd:additional-info",
1886 layer_info[i].info,exception);
1887 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
1891 if (image_info->ping == MagickFalse)
1893 for (i=0; i < number_layers; i++)
1895 if (layer_info[i].image == (Image *) NULL)
1897 for (j=0; j < layer_info[i].channels; j++)
1899 if (DiscardBlobBytes(image,(MagickSizeType)
1900 layer_info[i].channel_info[j].size) == MagickFalse)
1902 layer_info=DestroyLayerInfo(layer_info,number_layers);
1903 ThrowBinaryException(CorruptImageError,
1904 "UnexpectedEndOfFile",image->filename);
1910 if (image->debug != MagickFalse)
1911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1912 " reading data for layer %.20g",(double) i);
1914 status=ReadPSDLayer(image,image_info,psd_info,&layer_info[i],
1916 if (status == MagickFalse)
1919 status=SetImageProgress(image,LoadImagesTag,i,(MagickSizeType)
1921 if (status == MagickFalse)
1926 if (status != MagickFalse)
1928 for (i=0; i < number_layers; i++)
1930 if (layer_info[i].image == (Image *) NULL)
1932 for (j=i; j < number_layers - 1; j++)
1933 layer_info[j] = layer_info[j+1];
1939 if (number_layers > 0)
1941 for (i=0; i < number_layers; i++)
1944 layer_info[i].image->previous=layer_info[i-1].image;
1945 if (i < (number_layers-1))
1946 layer_info[i].image->next=layer_info[i+1].image;
1947 layer_info[i].image->page=layer_info[i].page;
1949 image->next=layer_info[0].image;
1950 layer_info[0].image->previous=image;
1952 layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
1955 layer_info=DestroyLayerInfo(layer_info,number_layers);
1961 ModuleExport MagickBooleanType ReadPSDLayers(Image *image,
1962 const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception)
1970 domain=CoderPolicyDomain;
1971 rights=ReadPolicyRights;
1972 if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
1974 return(ReadPSDLayersInternal(image,image_info,psd_info,MagickFalse,
1978 static MagickBooleanType ReadPSDMergedImage(const ImageInfo *image_info,
1979 Image *image,const PSDInfo *psd_info,ExceptionInfo *exception)
1993 compression=(PSDCompressionType) ReadBlobMSBShort(image);
1994 image->compression=ConvertPSDCompression(compression);
1996 if (compression != Raw && compression != RLE)
1998 (void) ThrowMagickException(exception,GetMagickModule(),
1999 TypeWarning,"CompressionNotSupported","'%.20g'",(double) compression);
2000 return(MagickFalse);
2003 sizes=(MagickOffsetType *) NULL;
2004 if (compression == RLE)
2006 sizes=ReadPSDRLESizes(image,psd_info,image->rows*psd_info->channels);
2007 if (sizes == (MagickOffsetType *) NULL)
2008 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2013 for (i=0; i < (ssize_t) psd_info->channels; i++)
2019 if ((type == 1) && (psd_info->channels == 2))
2022 if (compression == RLE)
2023 status=ReadPSDChannelRLE(image,psd_info,type,sizes+(i*image->rows),
2026 status=ReadPSDChannelRaw(image,psd_info->channels,type,exception);
2028 if (status != MagickFalse)
2029 status=SetImageProgress(image,LoadImagesTag,i,psd_info->channels);
2031 if (status == MagickFalse)
2035 if ((status != MagickFalse) && (image->colorspace == CMYKColorspace))
2036 status=NegateCMYK(image,exception);
2038 if (status != MagickFalse)
2039 status=CorrectPSDAlphaBlend(image_info,image,exception);
2041 sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
2046 static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
2079 assert(image_info != (const ImageInfo *) NULL);
2080 assert(image_info->signature == MagickCoreSignature);
2081 if (image_info->debug != MagickFalse)
2082 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2083 image_info->filename);
2084 assert(exception != (ExceptionInfo *) NULL);
2085 assert(exception->signature == MagickCoreSignature);
2087 image=AcquireImage(image_info,exception);
2088 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2089 if (status == MagickFalse)
2091 image=DestroyImageList(image);
2092 return((Image *) NULL);
2097 image->endian=MSBEndian;
2098 count=ReadBlob(image,4,(unsigned char *) psd_info.signature);
2099 psd_info.version=ReadBlobMSBShort(image);
2100 if ((count == 0) || (LocaleNCompare(psd_info.signature,"8BPS",4) != 0) ||
2101 ((psd_info.version != 1) && (psd_info.version != 2)))
2102 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2103 (void) ReadBlob(image,6,psd_info.reserved);
2104 psd_info.channels=ReadBlobMSBShort(image);
2105 if (psd_info.channels < 1)
2106 ThrowReaderException(CorruptImageError,"MissingImageChannel");
2107 if (psd_info.channels > MaxPSDChannels)
2108 ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded");
2109 psd_info.rows=ReadBlobMSBLong(image);
2110 psd_info.columns=ReadBlobMSBLong(image);
2111 if ((psd_info.version == 1) && ((psd_info.rows > 30000) ||
2112 (psd_info.columns > 30000)))
2113 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2114 psd_info.depth=ReadBlobMSBShort(image);
2115 if ((psd_info.depth != 1) && (psd_info.depth != 8) &&
2116 (psd_info.depth != 16) && (psd_info.depth != 32))
2117 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2118 psd_info.mode=ReadBlobMSBShort(image);
2119 if ((psd_info.mode == IndexedMode) && (psd_info.channels > 3))
2120 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2121 if (image->debug != MagickFalse)
2122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2123 " Image is %.20g x %.20g with channels=%.20g, depth=%.20g, mode=%s",
2124 (double) psd_info.columns,(double) psd_info.rows,(double)
2125 psd_info.channels,(double) psd_info.depth,ModeToString((PSDImageType)
2127 if (EOFBlob(image) != MagickFalse)
2128 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2132 image->depth=psd_info.depth;
2133 image->columns=psd_info.columns;
2134 image->rows=psd_info.rows;
2135 status=SetImageExtent(image,image->columns,image->rows,exception);
2136 if (status == MagickFalse)
2137 return(DestroyImageList(image));
2138 status=ResetImagePixels(image,exception);
2139 if (status == MagickFalse)
2140 return(DestroyImageList(image));
2141 psd_info.min_channels=3;
2142 if (psd_info.mode == LabMode)
2143 SetImageColorspace(image,LabColorspace,exception);
2144 if (psd_info.mode == CMYKMode)
2146 psd_info.min_channels=4;
2147 SetImageColorspace(image,CMYKColorspace,exception);
2148 if (psd_info.channels > 4)
2149 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2151 else if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) ||
2152 (psd_info.mode == DuotoneMode))
2154 if (psd_info.depth != 32)
2156 status=AcquireImageColormap(image,psd_info.depth < 16 ? 256 : 65536,
2158 if (status == MagickFalse)
2159 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2160 if (image->debug != MagickFalse)
2161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2162 " Image colormap allocated");
2164 psd_info.min_channels=1;
2165 SetImageColorspace(image,GRAYColorspace,exception);
2166 if (psd_info.channels > 1)
2167 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2170 if (psd_info.channels > 3)
2171 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2172 if (psd_info.channels < psd_info.min_channels)
2173 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2175 Read PSD raster colormap only present for indexed and duotone images.
2177 length=ReadBlobMSBLong(image);
2178 if ((psd_info.mode == IndexedMode) && (length < 3))
2179 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2182 if (image->debug != MagickFalse)
2183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2184 " reading colormap");
2185 if ((psd_info.mode == DuotoneMode) || (psd_info.depth == 32))
2188 Duotone image data; the format of this data is undocumented.
2189 32 bits per pixel; the colormap is ignored.
2191 (void) SeekBlob(image,(const MagickOffsetType) length,SEEK_CUR);
2199 Read PSD raster colormap.
2201 number_colors=length/3;
2202 if (number_colors > 65536)
2203 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2204 if (AcquireImageColormap(image,number_colors,exception) == MagickFalse)
2205 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2206 for (i=0; i < (ssize_t) image->colors; i++)
2207 image->colormap[i].red=ScaleCharToQuantum((unsigned char)
2208 ReadBlobByte(image));
2209 for (i=0; i < (ssize_t) image->colors; i++)
2210 image->colormap[i].green=ScaleCharToQuantum((unsigned char)
2211 ReadBlobByte(image));
2212 for (i=0; i < (ssize_t) image->colors; i++)
2213 image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
2214 ReadBlobByte(image));
2215 image->alpha_trait=UndefinedPixelTrait;
2218 if ((image->depth == 1) && (image->storage_class != PseudoClass))
2219 ThrowReaderException(CorruptImageError, "ImproperImageHeader");
2220 has_merged_image=MagickTrue;
2221 profile=(StringInfo *) NULL;
2222 length=ReadBlobMSBLong(image);
2229 Image resources block.
2231 if (image->debug != MagickFalse)
2232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2233 " reading image resource blocks - %.20g bytes",(double)
2234 ((MagickOffsetType) length));
2235 if (length > GetBlobSize(image))
2236 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
2237 blocks=(unsigned char *) AcquireQuantumMemory((size_t) length,
2239 if (blocks == (unsigned char *) NULL)
2240 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2241 count=ReadBlob(image,(size_t) length,blocks);
2242 if ((count != (ssize_t) length) || (length < 4) ||
2243 (LocaleNCompare((char *) blocks,"8BIM",4) != 0))
2245 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
2246 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2248 profile=ParseImageResourceBlocks(image,blocks,(size_t) length,
2249 &has_merged_image,exception);
2250 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
2253 Layer and mask block.
2255 length=GetPSDSize(&psd_info,image);
2258 length=ReadBlobMSBLong(image);
2259 length=ReadBlobMSBLong(image);
2261 offset=TellBlob(image);
2262 skip_layers=MagickFalse;
2263 if ((image_info->number_scenes == 1) && (image_info->scene == 0) &&
2264 (has_merged_image != MagickFalse))
2266 if (image->debug != MagickFalse)
2267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2268 " read composite only");
2269 skip_layers=MagickTrue;
2273 if (image->debug != MagickFalse)
2274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2275 " image has no layers");
2279 if (ReadPSDLayersInternal(image,image_info,&psd_info,skip_layers,
2280 exception) != MagickTrue)
2282 if (profile != (StringInfo *) NULL)
2283 profile=DestroyStringInfo(profile);
2284 (void) CloseBlob(image);
2285 image=DestroyImageList(image);
2286 return((Image *) NULL);
2290 Skip the rest of the layer and mask information.
2292 SeekBlob(image,offset+length,SEEK_SET);
2295 If we are only "pinging" the image, then we're done - so return.
2297 if (EOFBlob(image) != MagickFalse)
2299 if (profile != (StringInfo *) NULL)
2300 profile=DestroyStringInfo(profile);
2301 ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
2303 if (image_info->ping != MagickFalse)
2305 if (profile != (StringInfo *) NULL)
2306 profile=DestroyStringInfo(profile);
2307 (void) CloseBlob(image);
2308 return(GetFirstImageInList(image));
2311 Read the precombined layer, present for PSD < 4 compatibility.
2313 if (image->debug != MagickFalse)
2314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2315 " reading the precombined layer");
2316 if ((has_merged_image != MagickFalse) || (GetImageListLength(image) == 1))
2317 has_merged_image=(MagickBooleanType) ReadPSDMergedImage(image_info,image,
2318 &psd_info,exception);
2319 if ((has_merged_image == MagickFalse) && (GetImageListLength(image) == 1) &&
2322 SeekBlob(image,offset,SEEK_SET);
2323 status=ReadPSDLayersInternal(image,image_info,&psd_info,MagickFalse,
2325 if (status != MagickTrue)
2327 if (profile != (StringInfo *) NULL)
2328 profile=DestroyStringInfo(profile);
2329 (void) CloseBlob(image);
2330 image=DestroyImageList(image);
2331 return((Image *) NULL);
2334 if (has_merged_image == MagickFalse)
2339 if (GetImageListLength(image) == 1)
2341 if (profile != (StringInfo *) NULL)
2342 profile=DestroyStringInfo(profile);
2343 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
2345 SetImageAlphaChannel(image,TransparentAlphaChannel,exception);
2346 image->background_color.alpha=TransparentAlpha;
2347 image->background_color.alpha_trait=BlendPixelTrait;
2348 merged=MergeImageLayers(image,FlattenLayer,exception);
2349 ReplaceImageInList(&image,merged);
2351 if (profile != (StringInfo *) NULL)
2353 (void) SetImageProfile(image,GetStringInfoName(profile),profile,
2355 profile=DestroyStringInfo(profile);
2357 (void) CloseBlob(image);
2358 return(GetFirstImageInList(image));
2362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2366 % R e g i s t e r P S D I m a g e %
2370 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2372 % RegisterPSDImage() adds properties for the PSD image format to
2373 % the list of supported formats. The properties include the image format
2374 % tag, a method to read and/or write the format, whether the format
2375 % supports the saving of more than one frame to the same file or blob,
2376 % whether the format supports native in-memory I/O, and a brief
2377 % description of the format.
2379 % The format of the RegisterPSDImage method is:
2381 % size_t RegisterPSDImage(void)
2384 ModuleExport size_t RegisterPSDImage(void)
2389 entry=AcquireMagickInfo("PSD","PSB","Adobe Large Document Format");
2390 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2391 entry->encoder=(EncodeImageHandler *) WritePSDImage;
2392 entry->magick=(IsImageFormatHandler *) IsPSD;
2393 entry->flags|=CoderDecoderSeekableStreamFlag;
2394 entry->flags|=CoderEncoderSeekableStreamFlag;
2395 (void) RegisterMagickInfo(entry);
2396 entry=AcquireMagickInfo("PSD","PSD","Adobe Photoshop bitmap");
2397 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2398 entry->encoder=(EncodeImageHandler *) WritePSDImage;
2399 entry->magick=(IsImageFormatHandler *) IsPSD;
2400 entry->flags|=CoderDecoderSeekableStreamFlag;
2401 entry->flags|=CoderEncoderSeekableStreamFlag;
2402 (void) RegisterMagickInfo(entry);
2403 return(MagickImageCoderSignature);
2407 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2411 % U n r e g i s t e r P S D I m a g e %
2415 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2417 % UnregisterPSDImage() removes format registrations made by the
2418 % PSD module from the list of supported formats.
2420 % The format of the UnregisterPSDImage method is:
2422 % UnregisterPSDImage(void)
2425 ModuleExport void UnregisterPSDImage(void)
2427 (void) UnregisterMagickInfo("PSB");
2428 (void) UnregisterMagickInfo("PSD");
2432 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2436 % W r i t e P S D I m a g e %
2440 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2442 % WritePSDImage() writes an image in the Adobe Photoshop encoded image format.
2444 % The format of the WritePSDImage method is:
2446 % MagickBooleanType WritePSDImage(const ImageInfo *image_info,Image *image,
2447 % ExceptionInfo *exception)
2449 % A description of each parameter follows.
2451 % o image_info: the image info.
2453 % o image: The image.
2455 % o exception: return any errors or warnings in this structure.
2459 static inline ssize_t SetPSDOffset(const PSDInfo *psd_info,Image *image,
2460 const size_t offset)
2462 if (psd_info->version == 1)
2463 return(WriteBlobMSBShort(image,(unsigned short) offset));
2464 return(WriteBlobMSBLong(image,(unsigned int) offset));
2467 static inline ssize_t WritePSDOffset(const PSDInfo *psd_info,Image *image,
2468 const MagickSizeType size,const MagickSizeType offset)
2476 current_offset=TellBlob(image);
2477 SeekBlob(image,offset,SEEK_SET);
2478 if (psd_info->version == 1)
2479 result=WriteBlobMSBShort(image,(unsigned short) size);
2481 result=WriteBlobMSBLong(image,(unsigned int) size);
2482 SeekBlob(image,current_offset,SEEK_SET);
2486 static inline ssize_t SetPSDSize(const PSDInfo *psd_info,Image *image,
2487 const MagickSizeType size)
2489 if (psd_info->version == 1)
2490 return(WriteBlobLong(image,(unsigned int) size));
2491 return(WriteBlobLongLong(image,size));
2494 static inline ssize_t WritePSDSize(const PSDInfo *psd_info,Image *image,
2495 const MagickSizeType size,const MagickSizeType offset)
2503 current_offset=TellBlob(image);
2504 SeekBlob(image,offset,SEEK_SET);
2505 result=SetPSDSize(psd_info, image, size);
2506 SeekBlob(image,current_offset,SEEK_SET);
2510 static size_t PSDPackbitsEncodeImage(Image *image,const size_t length,
2511 const unsigned char *pixels,unsigned char *compact_pixels,
2512 ExceptionInfo *exception)
2521 register unsigned char
2528 Compress pixels with Packbits encoding.
2530 assert(image != (Image *) NULL);
2531 assert(image->signature == MagickCoreSignature);
2532 if (image->debug != MagickFalse)
2533 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2534 assert(pixels != (unsigned char *) NULL);
2535 assert(compact_pixels != (unsigned char *) NULL);
2536 packbits=(unsigned char *) AcquireQuantumMemory(128UL,sizeof(*packbits));
2537 if (packbits == (unsigned char *) NULL)
2538 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2541 for (i=(ssize_t) length; i != 0; )
2548 *q++=(unsigned char) 0;
2555 *q++=(unsigned char) 1;
2563 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2565 *q++=(unsigned char) ((256-3)+1);
2569 *q++=(unsigned char) 2;
2577 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2583 while (((ssize_t) count < i) && (*pixels == *(pixels+count)))
2590 *q++=(unsigned char) ((256-count)+1);
2599 while ((*(pixels+count) != *(pixels+count+1)) ||
2600 (*(pixels+count+1) != *(pixels+count+2)))
2602 packbits[count+1]=pixels[count];
2604 if (((ssize_t) count >= (i-3)) || (count >= 127))
2608 *packbits=(unsigned char) (count-1);
2609 for (j=0; j <= (ssize_t) count; j++)
2616 *q++=(unsigned char) 128; /* EOD marker */
2617 packbits=(unsigned char *) RelinquishMagickMemory(packbits);
2618 return((size_t) (q-compact_pixels));
2621 static size_t WriteCompressionStart(const PSDInfo *psd_info,Image *image,
2622 const Image *next_image,const CompressionType compression,
2623 const ssize_t channels)
2632 if (compression == RLECompression)
2634 length=WriteBlobShort(image,RLE);
2635 for (i=0; i < channels; i++)
2636 for (y=0; y < (ssize_t) next_image->rows; y++)
2637 length+=SetPSDOffset(psd_info,image,0);
2639 #ifdef MAGICKCORE_ZLIB_DELEGATE
2640 else if (compression == ZipCompression)
2641 length=WriteBlobShort(image,ZipWithoutPrediction);
2644 length=WriteBlobShort(image,Raw);
2648 static size_t WritePSDChannel(const PSDInfo *psd_info,
2649 const ImageInfo *image_info,Image *image,Image *next_image,
2650 const QuantumType quantum_type, unsigned char *compact_pixels,
2651 MagickOffsetType size_offset,const MagickBooleanType separate,
2652 const CompressionType compression,ExceptionInfo *exception)
2663 register const Quantum
2676 #ifdef MAGICKCORE_ZLIB_DELEGATE
2690 compressed_pixels=(unsigned char *) NULL;
2694 if (separate != MagickFalse)
2696 size_offset=TellBlob(image)+2;
2697 count+=WriteCompressionStart(psd_info,image,next_image,compression,1);
2699 if (next_image->depth > 8)
2700 next_image->depth=16;
2701 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
2702 MagickTrue : MagickFalse;
2703 quantum_info=AcquireQuantumInfo(image_info,next_image);
2704 if (quantum_info == (QuantumInfo *) NULL)
2706 pixels=(unsigned char *) GetQuantumPixels(quantum_info);
2707 #ifdef MAGICKCORE_ZLIB_DELEGATE
2708 if (compression == ZipCompression)
2710 compressed_pixels=(unsigned char *) AcquireQuantumMemory(CHUNK,
2711 sizeof(*compressed_pixels));
2712 if (compressed_pixels == (unsigned char *) NULL)
2714 quantum_info=DestroyQuantumInfo(quantum_info);
2717 memset(&stream,0,sizeof(stream));
2718 stream.data_type=Z_BINARY;
2719 level=Z_DEFAULT_COMPRESSION;
2720 if ((image_info->quality > 0 && image_info->quality < 10))
2721 level=(int) image_info->quality;
2722 if (deflateInit(&stream,level) != Z_OK)
2724 quantum_info=DestroyQuantumInfo(quantum_info);
2729 for (y=0; y < (ssize_t) next_image->rows; y++)
2731 p=GetVirtualPixels(next_image,0,y,next_image->columns,1,exception);
2732 if (p == (const Quantum *) NULL)
2734 length=ExportQuantumPixels(next_image,(CacheView *) NULL,quantum_info,
2735 quantum_type,pixels,exception);
2736 if (monochrome != MagickFalse)
2737 for (i=0; i < (ssize_t) length; i++)
2738 pixels[i]=(~pixels[i]);
2739 if (compression == RLECompression)
2741 length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels,
2743 count+=WriteBlob(image,length,compact_pixels);
2744 size_offset+=WritePSDOffset(psd_info,image,length,size_offset);
2746 #ifdef MAGICKCORE_ZLIB_DELEGATE
2747 else if (compression == ZipCompression)
2749 stream.avail_in=(uInt) length;
2750 stream.next_in=(Bytef *) pixels;
2751 if (y == (ssize_t) next_image->rows-1)
2754 stream.avail_out=(uInt) CHUNK;
2755 stream.next_out=(Bytef *) compressed_pixels;
2756 if (deflate(&stream,flush) == Z_STREAM_ERROR)
2758 length=(size_t) CHUNK-stream.avail_out;
2760 count+=WriteBlob(image,length,compressed_pixels);
2761 } while (stream.avail_out == 0);
2765 count+=WriteBlob(image,length,pixels);
2767 #ifdef MAGICKCORE_ZLIB_DELEGATE
2768 if (compression == ZipCompression)
2770 (void) deflateEnd(&stream);
2771 compressed_pixels=(unsigned char *) RelinquishMagickMemory(
2775 quantum_info=DestroyQuantumInfo(quantum_info);
2779 static unsigned char *AcquireCompactPixels(const Image *image,
2780 ExceptionInfo *exception)
2788 packet_size=image->depth > 8UL ? 2UL : 1UL;
2789 compact_pixels=(unsigned char *) AcquireQuantumMemory((9*
2790 image->columns)+1,packet_size*sizeof(*compact_pixels));
2791 if (compact_pixels == (unsigned char *) NULL)
2793 (void) ThrowMagickException(exception,GetMagickModule(),
2794 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2796 return(compact_pixels);
2799 static size_t WritePSDChannels(const PSDInfo *psd_info,
2800 const ImageInfo *image_info,Image *image,Image *next_image,
2801 MagickOffsetType size_offset,const MagickBooleanType separate,
2802 ExceptionInfo *exception)
2825 compact_pixels=(unsigned char *) NULL;
2826 compression=next_image->compression;
2827 if (image_info->compression != UndefinedCompression)
2828 compression=image_info->compression;
2829 if (compression == RLECompression)
2831 compact_pixels=AcquireCompactPixels(next_image,exception);
2832 if (compact_pixels == (unsigned char *) NULL)
2836 if (separate == MagickFalse)
2838 if (next_image->storage_class != PseudoClass)
2840 if (IsImageGray(next_image) == MagickFalse)
2841 channels=next_image->colorspace == CMYKColorspace ? 4 : 3;
2842 if (next_image->alpha_trait != UndefinedPixelTrait)
2845 rows_offset=TellBlob(image)+2;
2846 count+=WriteCompressionStart(psd_info,image,next_image,compression,
2848 offset_length=(next_image->rows*(psd_info->version == 1 ? 2 : 4));
2851 if (next_image->storage_class == PseudoClass)
2853 length=WritePSDChannel(psd_info,image_info,image,next_image,
2854 IndexQuantum,compact_pixels,rows_offset,separate,compression,
2856 if (separate != MagickFalse)
2857 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2859 rows_offset+=offset_length;
2864 if (IsImageGray(next_image) != MagickFalse)
2866 length=WritePSDChannel(psd_info,image_info,image,next_image,
2867 GrayQuantum,compact_pixels,rows_offset,separate,compression,
2869 if (separate != MagickFalse)
2870 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2872 rows_offset+=offset_length;
2877 if (next_image->colorspace == CMYKColorspace)
2878 (void) NegateCMYK(next_image,exception);
2880 length=WritePSDChannel(psd_info,image_info,image,next_image,
2881 RedQuantum,compact_pixels,rows_offset,separate,compression,
2883 if (separate != MagickFalse)
2884 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2886 rows_offset+=offset_length;
2889 length=WritePSDChannel(psd_info,image_info,image,next_image,
2890 GreenQuantum,compact_pixels,rows_offset,separate,compression,
2892 if (separate != MagickFalse)
2893 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2895 rows_offset+=offset_length;
2898 length=WritePSDChannel(psd_info,image_info,image,next_image,
2899 BlueQuantum,compact_pixels,rows_offset,separate,compression,
2901 if (separate != MagickFalse)
2902 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2904 rows_offset+=offset_length;
2907 if (next_image->colorspace == CMYKColorspace)
2909 length=WritePSDChannel(psd_info,image_info,image,next_image,
2910 BlackQuantum,compact_pixels,rows_offset,separate,compression,
2912 if (separate != MagickFalse)
2913 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2915 rows_offset+=offset_length;
2919 if (next_image->alpha_trait != UndefinedPixelTrait)
2921 length=WritePSDChannel(psd_info,image_info,image,next_image,
2922 AlphaQuantum,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 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
2932 if (next_image->colorspace == CMYKColorspace)
2933 (void) NegateCMYK(next_image,exception);
2934 if (separate != MagickFalse)
2939 property=GetImageArtifact(next_image,"psd:opacity-mask");
2940 if (property != (const char *) NULL)
2942 mask=(Image *) GetImageRegistry(ImageRegistryType,property,
2944 if (mask != (Image *) NULL)
2946 if (compression == RLECompression)
2948 compact_pixels=AcquireCompactPixels(mask,exception);
2949 if (compact_pixels == (unsigned char *) NULL)
2952 length=WritePSDChannel(psd_info,image_info,image,mask,
2953 RedQuantum,compact_pixels,rows_offset,MagickTrue,compression,
2955 (void) WritePSDSize(psd_info,image,length,size_offset);
2957 compact_pixels=(unsigned char *) RelinquishMagickMemory(
2965 static size_t WritePascalString(Image *image,const char *value,size_t padding)
2978 length=(strlen(value) > 255UL ) ? 255UL : strlen(value);
2980 count+=WriteBlobByte(image,0);
2983 count+=WriteBlobByte(image,(unsigned char) length);
2984 count+=WriteBlob(image,length,(const unsigned char *) value);
2987 if ((length % padding) == 0)
2989 for (i=0; i < (ssize_t) (padding-(length % padding)); i++)
2990 count+=WriteBlobByte(image,0);
2994 static void WriteResolutionResourceBlock(Image *image)
3003 if (image->units == PixelsPerCentimeterResolution)
3005 x_resolution=2.54*65536.0*image->resolution.x+0.5;
3006 y_resolution=2.54*65536.0*image->resolution.y+0.5;
3011 x_resolution=65536.0*image->resolution.x+0.5;
3012 y_resolution=65536.0*image->resolution.y+0.5;
3015 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
3016 (void) WriteBlobMSBShort(image,0x03ED);
3017 (void) WriteBlobMSBShort(image,0);
3018 (void) WriteBlobMSBLong(image,16); /* resource size */
3019 (void) WriteBlobMSBLong(image,(unsigned int) (x_resolution+0.5));
3020 (void) WriteBlobMSBShort(image,units); /* horizontal resolution unit */
3021 (void) WriteBlobMSBShort(image,units); /* width unit */
3022 (void) WriteBlobMSBLong(image,(unsigned int) (y_resolution+0.5));
3023 (void) WriteBlobMSBShort(image,units); /* vertical resolution unit */
3024 (void) WriteBlobMSBShort(image,units); /* height unit */
3027 static inline size_t WriteChannelSize(const PSDInfo *psd_info,Image *image,
3028 const signed short channel)
3033 count=(size_t) WriteBlobShort(image,channel);
3034 count+=SetPSDSize(psd_info,image,0);
3038 static void RemoveICCProfileFromResourceBlock(StringInfo *bim_profile)
3040 register const unsigned char
3057 length=GetStringInfoLength(bim_profile);
3060 datum=GetStringInfoDatum(bim_profile);
3061 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
3063 register unsigned char
3066 q=(unsigned char *) p;
3067 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
3069 p=PushLongPixel(MSBEndian,p,&long_sans);
3070 p=PushShortPixel(MSBEndian,p,&id);
3071 p=PushShortPixel(MSBEndian,p,&short_sans);
3072 p=PushLongPixel(MSBEndian,p,&count);
3073 if (id == 0x0000040f)
3078 quantum=PSDQuantum(count)+12;
3079 if ((quantum >= 12) && (quantum < (ssize_t) length))
3081 if ((q+quantum < (datum+length-16)))
3082 (void) memmove(q,q+quantum,length-quantum-(q-datum));
3083 SetStringInfoLength(bim_profile,length-quantum);
3088 if ((count & 0x01) != 0)
3093 static void RemoveResolutionFromResourceBlock(StringInfo *bim_profile)
3095 register const unsigned char
3112 length=GetStringInfoLength(bim_profile);
3115 datum=GetStringInfoDatum(bim_profile);
3116 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
3118 register unsigned char
3124 q=(unsigned char *) p;
3125 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
3127 p=PushLongPixel(MSBEndian,p,&long_sans);
3128 p=PushShortPixel(MSBEndian,p,&id);
3129 p=PushShortPixel(MSBEndian,p,&short_sans);
3130 p=PushLongPixel(MSBEndian,p,&count);
3131 cnt=PSDQuantum(count);
3134 if ((id == 0x000003ed) && (cnt < (ssize_t) (length-12)) &&
3135 ((ssize_t) length-(cnt+12)-(q-datum)) > 0)
3137 (void) memmove(q,q+cnt+12,length-(cnt+12)-(q-datum));
3138 SetStringInfoLength(bim_profile,length-(cnt+12));
3142 if ((count & 0x01) != 0)
3147 static const StringInfo *GetAdditionalInformation(const ImageInfo *image_info,
3148 Image *image,ExceptionInfo *exception)
3150 #define PSDKeySize 5
3151 #define PSDAllowedLength 36
3156 /* Whitelist of keys from: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ */
3158 allowed[PSDAllowedLength][PSDKeySize] = {
3159 "blnc", "blwh", "brit", "brst", "clbl", "clrL", "curv", "expA", "FMsk",
3160 "GdFl", "grdm", "hue ", "hue2", "infx", "knko", "lclr", "levl", "lnsr",
3161 "lfx2", "luni", "lrFX", "lspf", "lyid", "lyvr", "mixr", "nvrt", "phfl",
3162 "post", "PtFl", "selc", "shpa", "sn2P", "SoCo", "thrs", "tsly", "vibA"
3188 info=GetImageProfile(image,"psd:additional-info");
3189 if (info == (const StringInfo *) NULL)
3190 return((const StringInfo *) NULL);
3191 option=GetImageOption(image_info,"psd:additional-info");
3192 if (LocaleCompare(option,"all") == 0)
3194 if (LocaleCompare(option,"selective") != 0)
3196 profile=RemoveImageProfile(image,"psd:additional-info");
3197 return(DestroyStringInfo(profile));
3199 length=GetStringInfoLength(info);
3200 p=GetStringInfoDatum(info);
3201 remaining_length=length;
3203 while (remaining_length >= 12)
3205 /* skip over signature */
3212 size=(unsigned int) (*p++) << 24;
3213 size|=(unsigned int) (*p++) << 16;
3214 size|=(unsigned int) (*p++) << 8;
3215 size|=(unsigned int) (*p++);
3216 size=size & 0xffffffff;
3217 remaining_length-=12;
3218 if ((size_t) size > remaining_length)
3219 return((const StringInfo *) NULL);
3221 for (i=0; i < PSDAllowedLength; i++)
3223 if (LocaleNCompare(key,allowed[i],PSDKeySize) != 0)
3229 remaining_length-=(size_t) size;
3230 if (found == MagickFalse)
3232 if (remaining_length > 0)
3233 p=(unsigned char *) memmove(p-12,p+size,remaining_length);
3236 length+=(size_t) size+12;
3239 profile=RemoveImageProfile(image,"psd:additional-info");
3241 return(DestroyStringInfo(profile));
3242 SetStringInfoLength(profile,(const size_t) length);
3243 SetImageProfile(image,"psd:additional-info",info,exception);
3247 static MagickBooleanType WritePSDLayersInternal(Image *image,
3248 const ImageInfo *image_info,const PSDInfo *psd_info,size_t *layers_size,
3249 ExceptionInfo *exception)
3252 layer_name[MagickPathExtent];
3268 *layer_size_offsets,
3283 base_image=GetNextImageInList(image);
3284 if (base_image == (Image *) NULL)
3287 size_offset=TellBlob(image);
3288 SetPSDSize(psd_info,image,0);
3290 for (next_image=base_image; next_image != NULL; )
3293 next_image=GetNextImageInList(next_image);
3295 if (image->alpha_trait != UndefinedPixelTrait)
3296 size+=WriteBlobShort(image,-(unsigned short) layer_count);
3298 size+=WriteBlobShort(image,(unsigned short) layer_count);
3299 layer_size_offsets=(MagickOffsetType *) AcquireQuantumMemory(
3300 (size_t) layer_count,sizeof(MagickOffsetType));
3301 if (layer_size_offsets == (MagickOffsetType *) NULL)
3302 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3304 for (next_image=base_image; next_image != NULL; )
3316 mask=(Image *) NULL;
3317 property=GetImageArtifact(next_image,"psd:opacity-mask");
3319 if (property != (const char *) NULL)
3321 mask=(Image *) GetImageRegistry(ImageRegistryType,property,exception);
3322 default_color=strlen(property) == 9 ? 255 : 0;
3324 size+=WriteBlobSignedLong(image,(signed int) next_image->page.y);
3325 size+=WriteBlobSignedLong(image,(signed int) next_image->page.x);
3326 size+=WriteBlobSignedLong(image,(signed int) (next_image->page.y+
3328 size+=WriteBlobSignedLong(image,(signed int) (next_image->page.x+
3329 next_image->columns));
3331 if ((next_image->storage_class != PseudoClass) &&
3332 (IsImageGray(next_image) == MagickFalse))
3333 channels=next_image->colorspace == CMYKColorspace ? 4U : 3U;
3334 total_channels=channels;
3335 if (next_image->alpha_trait != UndefinedPixelTrait)
3337 if (mask != (Image *) NULL)
3339 size+=WriteBlobShort(image,total_channels);
3340 layer_size_offsets[layer_index++]=TellBlob(image);
3341 for (i=0; i < (ssize_t) channels; i++)
3342 size+=WriteChannelSize(psd_info,image,(signed short) i);
3343 if (next_image->alpha_trait != UndefinedPixelTrait)
3344 size+=WriteChannelSize(psd_info,image,-1);
3345 if (mask != (Image *) NULL)
3346 size+=WriteChannelSize(psd_info,image,-2);
3347 size+=WriteBlobString(image,image->endian == LSBEndian ? "MIB8" :"8BIM");
3348 size+=WriteBlobString(image,CompositeOperatorToPSDBlendMode(image));
3349 property=GetImageArtifact(next_image,"psd:layer.opacity");
3350 if (property != (const char *) NULL)
3355 opacity=(Quantum) StringToInteger(property);
3356 size+=WriteBlobByte(image,ScaleQuantumToChar(opacity));
3357 (void) ApplyPSDLayerOpacity(next_image,opacity,MagickTrue,exception);
3360 size+=WriteBlobByte(image,255);
3361 size+=WriteBlobByte(image,0);
3362 size+=WriteBlobByte(image,next_image->compose==NoCompositeOp ?
3363 1 << 0x02 : 1); /* layer properties - visible, etc. */
3364 size+=WriteBlobByte(image,0);
3365 info=GetAdditionalInformation(image_info,next_image,exception);
3366 property=(const char *) GetImageProperty(next_image,"label",exception);
3367 if (property == (const char *) NULL)
3369 (void) FormatLocaleString(layer_name,MagickPathExtent,"L%.20g",
3370 (double) layer_index);
3371 property=layer_name;
3373 name_length=strlen(property)+1;
3374 if ((name_length % 4) != 0)
3375 name_length+=(4-(name_length % 4));
3376 if (info != (const StringInfo *) NULL)
3377 name_length+=GetStringInfoLength(info);
3379 if (mask != (Image *) NULL)
3381 size+=WriteBlobLong(image,(unsigned int) name_length);
3382 if (mask == (Image *) NULL)
3383 size+=WriteBlobLong(image,0);
3386 if (mask->compose != NoCompositeOp)
3387 (void) ApplyPSDOpacityMask(next_image,mask,ScaleCharToQuantum(
3388 default_color),MagickTrue,exception);
3389 mask->page.y+=image->page.y;
3390 mask->page.x+=image->page.x;
3391 size+=WriteBlobLong(image,20);
3392 size+=WriteBlobSignedLong(image,mask->page.y);
3393 size+=WriteBlobSignedLong(image,mask->page.x);
3394 size+=WriteBlobSignedLong(image,(const signed int) mask->rows+
3396 size+=WriteBlobSignedLong(image,(const signed int) mask->columns+
3398 size+=WriteBlobByte(image,default_color);
3399 size+=WriteBlobByte(image,mask->compose == NoCompositeOp ? 2 : 0);
3400 size+=WriteBlobMSBShort(image,0);
3402 size+=WriteBlobLong(image,0);
3403 size+=WritePascalString(image,property,4);
3404 if (info != (const StringInfo *) NULL)
3405 size+=WriteBlob(image,GetStringInfoLength(info),
3406 GetStringInfoDatum(info));
3407 next_image=GetNextImageInList(next_image);
3412 next_image=base_image;
3414 while (next_image != NULL)
3416 length=WritePSDChannels(psd_info,image_info,image,next_image,
3417 layer_size_offsets[layer_index++],MagickTrue,exception);
3424 next_image=GetNextImageInList(next_image);
3427 Write the total size
3429 if (layers_size != (size_t*) NULL)
3431 if ((size/2) != ((size+1)/2))
3432 rounded_size=size+1;
3435 (void) WritePSDSize(psd_info,image,rounded_size,size_offset);
3436 layer_size_offsets=(MagickOffsetType *) RelinquishMagickMemory(
3437 layer_size_offsets);
3439 Remove the opacity mask from the registry
3441 next_image=base_image;
3442 while (next_image != (Image *) NULL)
3444 property=GetImageArtifact(next_image,"psd:opacity-mask");
3445 if (property != (const char *) NULL)
3446 DeleteImageRegistry(property);
3447 next_image=GetNextImageInList(next_image);
3453 ModuleExport MagickBooleanType WritePSDLayers(Image * image,
3454 const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception)
3462 domain=CoderPolicyDomain;
3463 rights=WritePolicyRights;
3464 if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
3466 return WritePSDLayersInternal(image,image_info,psd_info,(size_t*) NULL,
3470 static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
3471 Image *image,ExceptionInfo *exception)
3496 assert(image_info != (const ImageInfo *) NULL);
3497 assert(image_info->signature == MagickCoreSignature);
3498 assert(image != (Image *) NULL);
3499 assert(image->signature == MagickCoreSignature);
3500 if (image->debug != MagickFalse)
3501 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3502 assert(exception != (ExceptionInfo *) NULL);
3503 assert(exception->signature == MagickCoreSignature);
3504 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
3505 if (status == MagickFalse)
3507 packet_size=(size_t) (image->depth > 8 ? 6 : 3);
3508 if (image->alpha_trait != UndefinedPixelTrait)
3509 packet_size+=image->depth > 8 ? 2 : 1;
3511 if ((LocaleCompare(image_info->magick,"PSB") == 0) ||
3512 (image->columns > 30000) || (image->rows > 30000))
3514 (void) WriteBlob(image,4,(const unsigned char *) "8BPS");
3515 (void) WriteBlobMSBShort(image,psd_info.version); /* version */
3516 for (i=1; i <= 6; i++)
3517 (void) WriteBlobByte(image, 0); /* 6 bytes of reserved */
3518 /* When the image has a color profile it won't be converted to gray scale */
3519 if ((GetImageProfile(image,"icc") == (StringInfo *) NULL) &&
3520 (SetImageGray(image,exception) != MagickFalse))
3521 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
3523 if ((image_info->type != TrueColorType) && (image_info->type !=
3524 TrueColorAlphaType) && (image->storage_class == PseudoClass))
3525 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
3528 if (image->storage_class == PseudoClass)
3529 (void) SetImageStorageClass(image,DirectClass,exception);
3530 if (image->colorspace != CMYKColorspace)
3531 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 4UL : 3UL);
3533 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 5UL : 4UL);
3535 (void) WriteBlobMSBShort(image,(unsigned short) num_channels);
3536 (void) WriteBlobMSBLong(image,(unsigned int) image->rows);
3537 (void) WriteBlobMSBLong(image,(unsigned int) image->columns);
3538 if (IsImageGray(image) != MagickFalse)
3546 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
3547 MagickTrue : MagickFalse;
3548 (void) WriteBlobMSBShort(image,(unsigned short)
3549 (monochrome != MagickFalse ? 1 : image->depth > 8 ? 16 : 8));
3550 (void) WriteBlobMSBShort(image,(unsigned short)
3551 (monochrome != MagickFalse ? BitmapMode : GrayscaleMode));
3555 (void) WriteBlobMSBShort(image,(unsigned short) (image->storage_class ==
3556 PseudoClass ? 8 : image->depth > 8 ? 16 : 8));
3558 if (((image_info->colorspace != UndefinedColorspace) ||
3559 (image->colorspace != CMYKColorspace)) &&
3560 (image_info->colorspace != CMYKColorspace))
3562 (void) TransformImageColorspace(image,sRGBColorspace,exception);
3563 (void) WriteBlobMSBShort(image,(unsigned short)
3564 (image->storage_class == PseudoClass ? IndexedMode : RGBMode));
3568 if (image->colorspace != CMYKColorspace)
3569 (void) TransformImageColorspace(image,CMYKColorspace,exception);
3570 (void) WriteBlobMSBShort(image,CMYKMode);
3573 if ((IsImageGray(image) != MagickFalse) ||
3574 (image->storage_class == DirectClass) || (image->colors > 256))
3575 (void) WriteBlobMSBLong(image,0);
3579 Write PSD raster colormap.
3581 (void) WriteBlobMSBLong(image,768);
3582 for (i=0; i < (ssize_t) image->colors; i++)
3583 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].red));
3584 for ( ; i < 256; i++)
3585 (void) WriteBlobByte(image,0);
3586 for (i=0; i < (ssize_t) image->colors; i++)
3587 (void) WriteBlobByte(image,ScaleQuantumToChar(
3588 image->colormap[i].green));
3589 for ( ; i < 256; i++)
3590 (void) WriteBlobByte(image,0);
3591 for (i=0; i < (ssize_t) image->colors; i++)
3592 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].blue));
3593 for ( ; i < 256; i++)
3594 (void) WriteBlobByte(image,0);
3597 Image resource block.
3599 length=28; /* 0x03EB */
3600 bim_profile=(StringInfo *) GetImageProfile(image,"8bim");
3601 icc_profile=GetImageProfile(image,"icc");
3602 if (bim_profile != (StringInfo *) NULL)
3604 bim_profile=CloneStringInfo(bim_profile);
3605 if (icc_profile != (StringInfo *) NULL)
3606 RemoveICCProfileFromResourceBlock(bim_profile);
3607 RemoveResolutionFromResourceBlock(bim_profile);
3608 length+=PSDQuantum(GetStringInfoLength(bim_profile));
3610 if (icc_profile != (const StringInfo *) NULL)
3611 length+=PSDQuantum(GetStringInfoLength(icc_profile))+12;
3612 (void) WriteBlobMSBLong(image,(unsigned int) length);
3613 WriteResolutionResourceBlock(image);
3614 if (bim_profile != (StringInfo *) NULL)
3616 (void) WriteBlob(image,GetStringInfoLength(bim_profile),
3617 GetStringInfoDatum(bim_profile));
3618 bim_profile=DestroyStringInfo(bim_profile);
3620 if (icc_profile != (StringInfo *) NULL)
3622 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
3623 (void) WriteBlobMSBShort(image,0x0000040F);
3624 (void) WriteBlobMSBShort(image,0);
3625 (void) WriteBlobMSBLong(image,(unsigned int) GetStringInfoLength(
3627 (void) WriteBlob(image,GetStringInfoLength(icc_profile),
3628 GetStringInfoDatum(icc_profile));
3629 if ((MagickOffsetType) GetStringInfoLength(icc_profile) !=
3630 PSDQuantum(GetStringInfoLength(icc_profile)))
3631 (void) WriteBlobByte(image,0);
3633 if (status != MagickFalse)
3641 size_offset=TellBlob(image);
3642 SetPSDSize(&psd_info,image,0);
3643 status=WritePSDLayersInternal(image,image_info,&psd_info,&size,
3645 size_offset+=WritePSDSize(&psd_info,image,size+
3646 (psd_info.version == 1 ? 8 : 12),size_offset);
3648 (void) WriteBlobMSBLong(image,0); /* user mask data */
3650 Write composite image.
3652 if (status != MagickFalse)
3657 compression=image->compression;
3658 if (image->compression == ZipCompression)
3659 image->compression=RLECompression;
3660 if (image_info->compression != UndefinedCompression)
3661 image->compression=image_info->compression;
3662 if (WritePSDChannels(&psd_info,image_info,image,image,0,MagickFalse,
3665 image->compression=compression;
3667 (void) CloseBlob(image);