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,0,0,MagickTrue,exception);
455 if (complete_mask == (Image *) NULL)
457 complete_mask->alpha_trait=BlendPixelTrait;
458 GetPixelInfo(complete_mask,&color);
459 color.red=(MagickRealType) background;
460 (void) SetImageColor(complete_mask,&color,exception);
461 status=CompositeImage(complete_mask,mask,OverCompositeOp,MagickTrue,
462 mask->page.x-image->page.x,mask->page.y-image->page.y,exception);
463 if (status == MagickFalse)
465 complete_mask=DestroyImage(complete_mask);
468 image->alpha_trait=BlendPixelTrait;
469 #if defined(MAGICKCORE_OPENMP_SUPPORT)
470 #pragma omp parallel for schedule(static) shared(status) \
471 magick_number_threads(image,image,image->rows,1)
473 for (y=0; y < (ssize_t) image->rows; y++)
484 if (status == MagickFalse)
486 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
487 p=GetAuthenticPixels(complete_mask,0,y,complete_mask->columns,1,exception);
488 if ((q == (Quantum *) NULL) || (p == (Quantum *) NULL))
493 for (x=0; x < (ssize_t) image->columns; x++)
499 alpha=(MagickRealType) GetPixelAlpha(image,q);
500 intensity=GetPixelIntensity(complete_mask,p);
501 if (revert == MagickFalse)
502 SetPixelAlpha(image,ClampToQuantum(intensity*(QuantumScale*alpha)),q);
503 else if (intensity > 0)
504 SetPixelAlpha(image,ClampToQuantum((alpha/intensity)*QuantumRange),q);
505 q+=GetPixelChannels(image);
506 p+=GetPixelChannels(complete_mask);
508 if (SyncAuthenticPixels(image,exception) == MagickFalse)
511 complete_mask=DestroyImage(complete_mask);
515 static void PreservePSDOpacityMask(Image *image,LayerInfo* layer_info,
516 ExceptionInfo *exception)
527 if (image->debug != MagickFalse)
528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
529 " preserving opacity mask");
530 random_info=AcquireRandomInfo();
531 key_info=GetRandomKey(random_info,2+1);
532 key=(char *) GetStringInfoDatum(key_info);
533 key[8]=(char ) layer_info->mask.background;
535 layer_info->mask.image->page.x+=layer_info->page.x;
536 layer_info->mask.image->page.y+=layer_info->page.y;
537 (void) SetImageRegistry(ImageRegistryType,(const char *) key,
538 layer_info->mask.image,exception);
539 (void) SetImageArtifact(layer_info->image,"psd:opacity-mask",
541 key_info=DestroyStringInfo(key_info);
542 random_info=DestroyRandomInfo(random_info);
545 static ssize_t DecodePSDPixels(const size_t number_compact_pixels,
546 const unsigned char *compact_pixels,const ssize_t depth,
547 const size_t number_pixels,unsigned char *pixels)
549 #define CheckNumberCompactPixels \
554 #define CheckNumberPixels(count) \
555 if (((ssize_t) i + count) > (ssize_t) number_pixels) \
572 packets=(ssize_t) number_compact_pixels;
573 for (i=0; (packets > 1) && (i < (ssize_t) number_pixels); )
576 length=(size_t) (*compact_pixels++);
582 CheckNumberCompactPixels;
583 pixel=(*compact_pixels++);
584 for (j=0; j < (ssize_t) length; j++)
590 CheckNumberPixels(8);
591 *pixels++=(pixel >> 7) & 0x01 ? 0U : 255U;
592 *pixels++=(pixel >> 6) & 0x01 ? 0U : 255U;
593 *pixels++=(pixel >> 5) & 0x01 ? 0U : 255U;
594 *pixels++=(pixel >> 4) & 0x01 ? 0U : 255U;
595 *pixels++=(pixel >> 3) & 0x01 ? 0U : 255U;
596 *pixels++=(pixel >> 2) & 0x01 ? 0U : 255U;
597 *pixels++=(pixel >> 1) & 0x01 ? 0U : 255U;
598 *pixels++=(pixel >> 0) & 0x01 ? 0U : 255U;
603 CheckNumberPixels(4);
604 *pixels++=(unsigned char) ((pixel >> 6) & 0x03);
605 *pixels++=(unsigned char) ((pixel >> 4) & 0x03);
606 *pixels++=(unsigned char) ((pixel >> 2) & 0x03);
607 *pixels++=(unsigned char) ((pixel & 0x03) & 0x03);
612 CheckNumberPixels(2);
613 *pixels++=(unsigned char) ((pixel >> 4) & 0xff);
614 *pixels++=(unsigned char) ((pixel & 0x0f) & 0xff);
619 CheckNumberPixels(1);
620 *pixels++=(unsigned char) pixel;
628 for (j=0; j < (ssize_t) length; j++)
630 CheckNumberCompactPixels;
635 CheckNumberPixels(8);
636 *pixels++=(*compact_pixels >> 7) & 0x01 ? 0U : 255U;
637 *pixels++=(*compact_pixels >> 6) & 0x01 ? 0U : 255U;
638 *pixels++=(*compact_pixels >> 5) & 0x01 ? 0U : 255U;
639 *pixels++=(*compact_pixels >> 4) & 0x01 ? 0U : 255U;
640 *pixels++=(*compact_pixels >> 3) & 0x01 ? 0U : 255U;
641 *pixels++=(*compact_pixels >> 2) & 0x01 ? 0U : 255U;
642 *pixels++=(*compact_pixels >> 1) & 0x01 ? 0U : 255U;
643 *pixels++=(*compact_pixels >> 0) & 0x01 ? 0U : 255U;
648 CheckNumberPixels(4);
649 *pixels++=(*compact_pixels >> 6) & 0x03;
650 *pixels++=(*compact_pixels >> 4) & 0x03;
651 *pixels++=(*compact_pixels >> 2) & 0x03;
652 *pixels++=(*compact_pixels & 0x03) & 0x03;
657 CheckNumberPixels(2);
658 *pixels++=(*compact_pixels >> 4) & 0xff;
659 *pixels++=(*compact_pixels & 0x0f) & 0xff;
664 CheckNumberPixels(1);
665 *pixels++=(*compact_pixels);
675 static inline LayerInfo *DestroyLayerInfo(LayerInfo *layer_info,
676 const ssize_t number_layers)
681 for (i=0; i<number_layers; i++)
683 if (layer_info[i].image != (Image *) NULL)
684 layer_info[i].image=DestroyImage(layer_info[i].image);
685 if (layer_info[i].mask.image != (Image *) NULL)
686 layer_info[i].mask.image=DestroyImage(layer_info[i].mask.image);
687 if (layer_info[i].info != (StringInfo *) NULL)
688 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
691 return (LayerInfo *) RelinquishMagickMemory(layer_info);
694 static inline size_t GetPSDPacketSize(const Image *image)
696 if (image->storage_class == PseudoClass)
698 if (image->colors > 256)
701 if (image->depth > 16)
703 if (image->depth > 8)
709 static inline MagickSizeType GetPSDSize(const PSDInfo *psd_info,Image *image)
711 if (psd_info->version == 1)
712 return((MagickSizeType) ReadBlobLong(image));
713 return((MagickSizeType) ReadBlobLongLong(image));
716 static inline size_t GetPSDRowSize(Image *image)
718 if (image->depth == 1)
719 return(((image->columns+7)/8)*GetPSDPacketSize(image));
721 return(image->columns*GetPSDPacketSize(image));
724 static const char *ModeToString(PSDImageType type)
728 case BitmapMode: return "Bitmap";
729 case GrayscaleMode: return "Grayscale";
730 case IndexedMode: return "Indexed";
731 case RGBMode: return "RGB";
732 case CMYKMode: return "CMYK";
733 case MultichannelMode: return "Multichannel";
734 case DuotoneMode: return "Duotone";
735 case LabMode: return "L*A*B";
736 default: return "unknown";
740 static MagickBooleanType NegateCMYK(Image *image,ExceptionInfo *exception)
748 channel_mask=SetImageChannelMask(image,(ChannelType)(AllChannels &~
750 status=NegateImage(image,MagickFalse,exception);
751 (void) SetImageChannelMask(image,channel_mask);
755 static StringInfo *ParseImageResourceBlocks(Image *image,
756 const unsigned char *blocks,size_t length,
757 MagickBooleanType *has_merged_image,ExceptionInfo *exception)
779 return((StringInfo *) NULL);
780 profile=BlobToStringInfo((const unsigned char *) NULL,length);
781 SetStringInfoDatum(profile,blocks);
782 SetStringInfoName(profile,"8bim");
783 for (p=blocks; (p >= blocks) && (p < (blocks+length-7)); )
785 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
788 p=PushShortPixel(MSBEndian,p,&id);
789 p=PushCharPixel(p,&name_length);
790 if ((name_length % 2) == 0)
793 if (p > (blocks+length-4))
795 p=PushLongPixel(MSBEndian,p,&value);
796 offset=(ssize_t) value;
797 if (((p+offset) < blocks) || ((p+offset) > (blocks+length)))
804 value[MagickPathExtent];
814 p=PushShortPixel(MSBEndian,p,&resolution);
815 image->resolution.x=(double) resolution;
816 (void) FormatLocaleString(value,MagickPathExtent,"%g",
817 image->resolution.x);
818 (void) SetImageProperty(image,"tiff:XResolution",value,exception);
819 p=PushShortPixel(MSBEndian,p,&short_sans);
820 p=PushShortPixel(MSBEndian,p,&short_sans);
821 p=PushShortPixel(MSBEndian,p,&short_sans);
822 p=PushShortPixel(MSBEndian,p,&resolution);
823 image->resolution.y=(double) resolution;
824 (void) FormatLocaleString(value,MagickPathExtent,"%g",
825 image->resolution.y);
826 (void) SetImageProperty(image,"tiff:YResolution",value,exception);
827 p=PushShortPixel(MSBEndian,p,&short_sans);
828 p=PushShortPixel(MSBEndian,p,&short_sans);
829 p=PushShortPixel(MSBEndian,p,&short_sans);
830 image->units=PixelsPerInchResolution;
835 if ((offset > 4) && (*(p+4) == 0))
836 *has_merged_image=MagickFalse;
846 if ((offset & 0x01) != 0)
852 static CompositeOperator PSDBlendModeToCompositeOperator(const char *mode)
854 if (mode == (const char *) NULL)
855 return(OverCompositeOp);
856 if (LocaleNCompare(mode,"norm",4) == 0)
857 return(OverCompositeOp);
858 if (LocaleNCompare(mode,"mul ",4) == 0)
859 return(MultiplyCompositeOp);
860 if (LocaleNCompare(mode,"diss",4) == 0)
861 return(DissolveCompositeOp);
862 if (LocaleNCompare(mode,"diff",4) == 0)
863 return(DifferenceCompositeOp);
864 if (LocaleNCompare(mode,"dark",4) == 0)
865 return(DarkenCompositeOp);
866 if (LocaleNCompare(mode,"lite",4) == 0)
867 return(LightenCompositeOp);
868 if (LocaleNCompare(mode,"hue ",4) == 0)
869 return(HueCompositeOp);
870 if (LocaleNCompare(mode,"sat ",4) == 0)
871 return(SaturateCompositeOp);
872 if (LocaleNCompare(mode,"colr",4) == 0)
873 return(ColorizeCompositeOp);
874 if (LocaleNCompare(mode,"lum ",4) == 0)
875 return(LuminizeCompositeOp);
876 if (LocaleNCompare(mode,"scrn",4) == 0)
877 return(ScreenCompositeOp);
878 if (LocaleNCompare(mode,"over",4) == 0)
879 return(OverlayCompositeOp);
880 if (LocaleNCompare(mode,"hLit",4) == 0)
881 return(HardLightCompositeOp);
882 if (LocaleNCompare(mode,"sLit",4) == 0)
883 return(SoftLightCompositeOp);
884 if (LocaleNCompare(mode,"smud",4) == 0)
885 return(ExclusionCompositeOp);
886 if (LocaleNCompare(mode,"div ",4) == 0)
887 return(ColorDodgeCompositeOp);
888 if (LocaleNCompare(mode,"idiv",4) == 0)
889 return(ColorBurnCompositeOp);
890 if (LocaleNCompare(mode,"lbrn",4) == 0)
891 return(LinearBurnCompositeOp);
892 if (LocaleNCompare(mode,"lddg",4) == 0)
893 return(LinearDodgeCompositeOp);
894 if (LocaleNCompare(mode,"lLit",4) == 0)
895 return(LinearLightCompositeOp);
896 if (LocaleNCompare(mode,"vLit",4) == 0)
897 return(VividLightCompositeOp);
898 if (LocaleNCompare(mode,"pLit",4) == 0)
899 return(PinLightCompositeOp);
900 if (LocaleNCompare(mode,"hMix",4) == 0)
901 return(HardMixCompositeOp);
902 return(OverCompositeOp);
905 static inline void ReversePSDString(Image *image,char *p,size_t length)
910 if (image->endian == MSBEndian)
914 for(--q; p < q; ++p, --q)
922 static inline void SetPSDPixel(Image *image,const size_t channels,
923 const ssize_t type,const size_t packet_size,const Quantum pixel,Quantum *q,
924 ExceptionInfo *exception)
926 if (image->storage_class == PseudoClass)
933 if (packet_size == 1)
934 SetPixelIndex(image,ScaleQuantumToChar(pixel),q);
936 SetPixelIndex(image,ScaleQuantumToShort(pixel),q);
938 color=image->colormap+(ssize_t) ConstrainColormapIndex(image,
939 (ssize_t) GetPixelIndex(image,q),exception);
940 if ((type == 0) && (channels > 1))
943 color->alpha=(MagickRealType) pixel;
944 SetPixelViaPixelInfo(image,color,q);
951 SetPixelAlpha(image,pixel,q);
957 SetPixelRed(image,pixel,q);
963 SetPixelGreen(image,pixel,q);
969 SetPixelBlue(image,pixel,q);
974 if (image->colorspace == CMYKColorspace)
975 SetPixelBlack(image,pixel,q);
977 if (image->alpha_trait != UndefinedPixelTrait)
978 SetPixelAlpha(image,pixel,q);
983 if ((IssRGBCompatibleColorspace(image->colorspace) != MagickFalse) &&
986 if (image->alpha_trait != UndefinedPixelTrait)
987 SetPixelAlpha(image,pixel,q);
993 static MagickBooleanType ReadPSDChannelPixels(Image *image,
994 const size_t channels,const ssize_t row,const ssize_t type,
995 const unsigned char *pixels,ExceptionInfo *exception)
1000 register const unsigned char
1013 q=GetAuthenticPixels(image,0,row,image->columns,1,exception);
1014 if (q == (Quantum *) NULL)
1016 packet_size=GetPSDPacketSize(image);
1017 for (x=0; x < (ssize_t) image->columns; x++)
1019 if (packet_size == 1)
1020 pixel=ScaleCharToQuantum(*p++);
1021 else if (packet_size == 2)
1026 p=PushShortPixel(MSBEndian,p,&nibble);
1027 pixel=ScaleShortToQuantum(nibble);
1034 p=PushFloatPixel(MSBEndian,p,&nibble);
1035 pixel=ClampToQuantum((MagickRealType)QuantumRange*nibble);
1037 if (image->depth > 1)
1039 SetPSDPixel(image,channels,type,packet_size,pixel,q,exception);
1040 q+=GetPixelChannels(image);
1050 number_bits=(ssize_t) image->columns-x;
1051 if (number_bits > 8)
1053 for (bit = 0; bit < (ssize_t) number_bits; bit++)
1055 SetPSDPixel(image,channels,type,packet_size,(((unsigned char) pixel)
1056 & (0x01 << (7-bit))) != 0 ? 0 : QuantumRange,q,exception);
1057 q+=GetPixelChannels(image);
1060 if (x != (ssize_t) image->columns)
1065 return(SyncAuthenticPixels(image,exception));
1068 static MagickBooleanType ReadPSDChannelRaw(Image *image,const size_t channels,
1069 const ssize_t type,ExceptionInfo *exception)
1084 if (image->debug != MagickFalse)
1085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1086 " layer data is RAW");
1088 row_size=GetPSDRowSize(image);
1089 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
1090 if (pixels == (unsigned char *) NULL)
1091 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1095 for (y=0; y < (ssize_t) image->rows; y++)
1099 count=ReadBlob(image,row_size,pixels);
1100 if (count != (ssize_t) row_size)
1106 status=ReadPSDChannelPixels(image,channels,y,type,pixels,exception);
1107 if (status == MagickFalse)
1111 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1115 static inline MagickOffsetType *ReadPSDRLESizes(Image *image,
1116 const PSDInfo *psd_info,const size_t size)
1124 sizes=(MagickOffsetType *) AcquireQuantumMemory(size,sizeof(*sizes));
1125 if(sizes != (MagickOffsetType *) NULL)
1127 for (y=0; y < (ssize_t) size; y++)
1129 if (psd_info->version == 1)
1130 sizes[y]=(MagickOffsetType) ReadBlobShort(image);
1132 sizes[y]=(MagickOffsetType) ReadBlobLong(image);
1138 static MagickBooleanType ReadPSDChannelRLE(Image *image,const PSDInfo *psd_info,
1139 const ssize_t type,MagickOffsetType *sizes,ExceptionInfo *exception)
1156 if (image->debug != MagickFalse)
1157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1158 " layer data is RLE compressed");
1160 row_size=GetPSDRowSize(image);
1161 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
1162 if (pixels == (unsigned char *) NULL)
1163 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1167 for (y=0; y < (ssize_t) image->rows; y++)
1168 if ((MagickOffsetType) length < sizes[y])
1169 length=(size_t) sizes[y];
1171 if (length > (row_size+2048)) /* arbitrary number */
1173 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1174 ThrowBinaryException(ResourceLimitError,"InvalidLength",image->filename);
1177 compact_pixels=(unsigned char *) AcquireQuantumMemory(length,sizeof(*pixels));
1178 if (compact_pixels == (unsigned char *) NULL)
1180 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1181 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1185 (void) memset(compact_pixels,0,length*sizeof(*compact_pixels));
1188 for (y=0; y < (ssize_t) image->rows; y++)
1192 count=ReadBlob(image,(size_t) sizes[y],compact_pixels);
1193 if (count != (ssize_t) sizes[y])
1196 count=DecodePSDPixels((size_t) sizes[y],compact_pixels,
1197 (ssize_t) (image->depth == 1 ? 123456 : image->depth),row_size,pixels);
1198 if (count != (ssize_t) row_size)
1201 status=ReadPSDChannelPixels(image,psd_info->channels,y,type,pixels,
1203 if (status == MagickFalse)
1207 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1208 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1212 #ifdef MAGICKCORE_ZLIB_DELEGATE
1213 static MagickBooleanType ReadPSDChannelZip(Image *image,const size_t channels,
1214 const ssize_t type,const PSDCompressionType compression,
1215 const size_t compact_size,ExceptionInfo *exception)
1220 register unsigned char
1239 if (image->debug != MagickFalse)
1240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1241 " layer data is ZIP compressed");
1243 if ((MagickSizeType) compact_size > GetBlobSize(image))
1244 ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile",
1246 compact_pixels=(unsigned char *) AcquireQuantumMemory(compact_size,
1247 sizeof(*compact_pixels));
1248 if (compact_pixels == (unsigned char *) NULL)
1249 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1252 packet_size=GetPSDPacketSize(image);
1253 row_size=image->columns*packet_size;
1254 count=image->rows*row_size;
1256 pixels=(unsigned char *) AcquireQuantumMemory(count,sizeof(*pixels));
1257 if (pixels == (unsigned char *) NULL)
1259 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1260 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1263 if (ReadBlob(image,compact_size,compact_pixels) != (ssize_t) compact_size)
1265 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1266 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1267 ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile",
1271 memset(&stream,0,sizeof(stream));
1272 stream.data_type=Z_BINARY;
1273 stream.next_in=(Bytef *)compact_pixels;
1274 stream.avail_in=(uInt) compact_size;
1275 stream.next_out=(Bytef *)pixels;
1276 stream.avail_out=(uInt) count;
1278 if (inflateInit(&stream) == Z_OK)
1283 while (stream.avail_out > 0)
1285 ret=inflate(&stream,Z_SYNC_FLUSH);
1286 if ((ret != Z_OK) && (ret != Z_STREAM_END))
1288 (void) inflateEnd(&stream);
1289 compact_pixels=(unsigned char *) RelinquishMagickMemory(
1291 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1292 return(MagickFalse);
1294 if (ret == Z_STREAM_END)
1297 (void) inflateEnd(&stream);
1300 if (compression == ZipWithPrediction)
1305 length=image->columns;
1308 if (packet_size == 2)
1310 p[2]+=p[0]+((p[1]+p[3]) >> 8);
1314 else if (packet_size == 4)
1316 TODO: Figure out what to do there.
1330 for (y=0; y < (ssize_t) image->rows; y++)
1332 status=ReadPSDChannelPixels(image,channels,y,type,p,exception);
1333 if (status == MagickFalse)
1339 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1340 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1345 static MagickBooleanType ReadPSDChannel(Image *image,
1346 const ImageInfo *image_info,const PSDInfo *psd_info,LayerInfo* layer_info,
1347 const size_t channel,const PSDCompressionType compression,
1348 ExceptionInfo *exception)
1360 channel_image=image;
1361 mask=(Image *) NULL;
1362 if ((layer_info->channel_info[channel].type < -1) &&
1363 (layer_info->mask.page.width > 0) && (layer_info->mask.page.height > 0))
1369 Ignore mask that is not a user supplied layer mask, if the mask is
1370 disabled or if the flags have unsupported values.
1372 option=GetImageOption(image_info,"psd:preserve-opacity-mask");
1373 if ((layer_info->channel_info[channel].type != -2) ||
1374 (layer_info->mask.flags > 2) || ((layer_info->mask.flags & 0x02) &&
1375 (IsStringTrue(option) == MagickFalse)))
1377 (void) SeekBlob(image,(MagickOffsetType)
1378 layer_info->channel_info[channel].size-2,SEEK_CUR);
1381 mask=CloneImage(image,layer_info->mask.page.width,
1382 layer_info->mask.page.height,MagickFalse,exception);
1383 if (mask != (Image *) NULL)
1385 (void) SetImageType(mask,GrayscaleType,exception);
1390 offset=TellBlob(image);
1395 status=ReadPSDChannelRaw(channel_image,psd_info->channels,
1396 (ssize_t) layer_info->channel_info[channel].type,exception);
1403 sizes=ReadPSDRLESizes(channel_image,psd_info,channel_image->rows);
1404 if (sizes == (MagickOffsetType *) NULL)
1405 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1407 status=ReadPSDChannelRLE(channel_image,psd_info,
1408 (ssize_t) layer_info->channel_info[channel].type,sizes,exception);
1409 sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
1412 case ZipWithPrediction:
1413 case ZipWithoutPrediction:
1414 #ifdef MAGICKCORE_ZLIB_DELEGATE
1415 status=ReadPSDChannelZip(channel_image,layer_info->channels,
1416 (ssize_t) layer_info->channel_info[channel].type,compression,
1417 layer_info->channel_info[channel].size-2,exception);
1419 (void) ThrowMagickException(exception,GetMagickModule(),
1420 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
1421 "'%s' (ZLIB)",image->filename);
1425 (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
1426 "CompressionNotSupported","'%.20g'",(double) compression);
1430 (void) SeekBlob(image,offset+layer_info->channel_info[channel].size-2,
1432 if (status == MagickFalse)
1434 if (mask != (Image *) NULL)
1435 (void) DestroyImage(mask);
1436 ThrowBinaryException(CoderError,"UnableToDecompressImage",
1439 if (mask != (Image *) NULL)
1441 if (layer_info->mask.image != (Image *) NULL)
1442 layer_info->mask.image=DestroyImage(layer_info->mask.image);
1443 layer_info->mask.image=mask;
1448 static MagickBooleanType ReadPSDLayer(Image *image,const ImageInfo *image_info,
1449 const PSDInfo *psd_info,LayerInfo* layer_info,ExceptionInfo *exception)
1452 message[MagickPathExtent];
1463 if (image->debug != MagickFalse)
1464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1465 " setting up new layer image");
1466 if (psd_info->mode != IndexedMode)
1467 (void) SetImageBackgroundColor(layer_info->image,exception);
1468 layer_info->image->compose=PSDBlendModeToCompositeOperator(
1469 layer_info->blendkey);
1470 if (layer_info->visible == MagickFalse)
1471 layer_info->image->compose=NoCompositeOp;
1473 Set up some hidden attributes for folks that need them.
1475 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
1476 (double) layer_info->page.x);
1477 (void) SetImageArtifact(layer_info->image,"psd:layer.x",message);
1478 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
1479 (double) layer_info->page.y);
1480 (void) SetImageArtifact(layer_info->image,"psd:layer.y",message);
1481 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",(double)
1482 layer_info->opacity);
1483 (void) SetImageArtifact(layer_info->image,"psd:layer.opacity",message);
1484 (void) SetImageProperty(layer_info->image,"label",(char *) layer_info->name,
1488 for (j=0; j < (ssize_t) layer_info->channels; j++)
1490 if (image->debug != MagickFalse)
1491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1492 " reading data for channel %.20g",(double) j);
1494 compression=(PSDCompressionType) ReadBlobShort(layer_info->image);
1496 /* TODO: Remove this when we figure out how to support this */
1497 if ((compression == ZipWithPrediction) && (image->depth == 32))
1499 (void) ThrowMagickException(exception,GetMagickModule(),
1500 TypeError,"CompressionNotSupported","ZipWithPrediction(32 bit)");
1501 return(MagickFalse);
1504 layer_info->image->compression=ConvertPSDCompression(compression);
1505 if (layer_info->channel_info[j].type == -1)
1506 layer_info->image->alpha_trait=BlendPixelTrait;
1508 status=ReadPSDChannel(layer_info->image,image_info,psd_info,layer_info,
1509 (size_t) j,compression,exception);
1511 if (status == MagickFalse)
1515 if (status != MagickFalse)
1516 status=ApplyPSDLayerOpacity(layer_info->image,layer_info->opacity,
1517 MagickFalse,exception);
1519 if ((status != MagickFalse) &&
1520 (layer_info->image->colorspace == CMYKColorspace))
1521 status=NegateCMYK(layer_info->image,exception);
1523 if ((status != MagickFalse) && (layer_info->mask.image != (Image *) NULL))
1528 layer_info->mask.image->page.x=layer_info->mask.page.x;
1529 layer_info->mask.image->page.y=layer_info->mask.page.y;
1530 /* Do not composite the mask when it is disabled */
1531 if ((layer_info->mask.flags & 0x02) == 0x02)
1532 layer_info->mask.image->compose=NoCompositeOp;
1534 status=ApplyPSDOpacityMask(layer_info->image,layer_info->mask.image,
1535 layer_info->mask.background == 0 ? 0 : QuantumRange,MagickFalse,
1537 option=GetImageOption(image_info,"psd:preserve-opacity-mask");
1538 if (IsStringTrue(option) != MagickFalse)
1539 PreservePSDOpacityMask(image,layer_info,exception);
1540 layer_info->mask.image=DestroyImage(layer_info->mask.image);
1546 static MagickBooleanType CheckPSDChannels(const PSDInfo *psd_info,
1547 LayerInfo *layer_info)
1555 if (layer_info->channels < psd_info->min_channels)
1556 return(MagickFalse);
1557 channel_type=RedChannel;
1558 if (psd_info->min_channels >= 3)
1559 channel_type|=(GreenChannel | BlueChannel);
1560 if (psd_info->min_channels >= 4)
1561 channel_type|=BlackChannel;
1562 for (i=0; i < (ssize_t) layer_info->channels; i++)
1567 type=layer_info->channel_info[i].type;
1570 channel_type|=AlphaChannel;
1576 channel_type&=~RedChannel;
1578 channel_type&=~GreenChannel;
1580 channel_type&=~BlueChannel;
1582 channel_type&=~BlackChannel;
1584 if (channel_type == 0)
1586 if ((channel_type == AlphaChannel) &&
1587 (layer_info->channels >= psd_info->min_channels + 1))
1589 return(MagickFalse);
1592 static MagickBooleanType ReadPSDLayersInternal(Image *image,
1593 const ImageInfo *image_info,const PSDInfo *psd_info,
1594 const MagickBooleanType skip_layers,ExceptionInfo *exception)
1616 size=GetPSDSize(psd_info,image);
1620 Skip layers & masks.
1622 (void) ReadBlobLong(image);
1623 count=ReadBlob(image,4,(unsigned char *) type);
1625 ReversePSDString(image,type,(size_t) count);
1626 if ((count != 4) || (LocaleNCompare(type,"8BIM",4) != 0))
1630 count=ReadBlob(image,4,(unsigned char *) type);
1632 ReversePSDString(image,type,4);
1633 if ((count == 4) && ((LocaleNCompare(type,"Lr16",4) == 0) ||
1634 (LocaleNCompare(type,"Lr32",4) == 0)))
1635 size=GetPSDSize(psd_info,image);
1643 layer_info=(LayerInfo *) NULL;
1644 number_layers=(ssize_t) ReadBlobShort(image);
1646 if (number_layers < 0)
1649 The first alpha channel in the merged result contains the
1650 transparency data for the merged result.
1652 number_layers=MagickAbsoluteValue(number_layers);
1653 if (image->debug != MagickFalse)
1654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1655 " negative layer count corrected for");
1656 image->alpha_trait=BlendPixelTrait;
1660 We only need to know if the image has an alpha channel
1662 if (skip_layers != MagickFalse)
1665 if (image->debug != MagickFalse)
1666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1667 " image contains %.20g layers",(double) number_layers);
1669 if (number_layers == 0)
1670 ThrowBinaryException(CorruptImageError,"InvalidNumberOfLayers",
1673 layer_info=(LayerInfo *) AcquireQuantumMemory((size_t) number_layers,
1674 sizeof(*layer_info));
1675 if (layer_info == (LayerInfo *) NULL)
1677 if (image->debug != MagickFalse)
1678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1679 " allocation of LayerInfo failed");
1680 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1683 (void) memset(layer_info,0,(size_t) number_layers*
1684 sizeof(*layer_info));
1686 for (i=0; i < number_layers; i++)
1692 if (image->debug != MagickFalse)
1693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1694 " reading layer #%.20g",(double) i+1);
1695 layer_info[i].page.y=(ssize_t) ReadBlobSignedLong(image);
1696 layer_info[i].page.x=(ssize_t) ReadBlobSignedLong(image);
1697 y=(ssize_t) ReadBlobSignedLong(image);
1698 x=(ssize_t) ReadBlobSignedLong(image);
1699 layer_info[i].page.width=(size_t) (x-layer_info[i].page.x);
1700 layer_info[i].page.height=(size_t) (y-layer_info[i].page.y);
1701 layer_info[i].channels=ReadBlobShort(image);
1702 if (layer_info[i].channels > MaxPSDChannels)
1704 layer_info=DestroyLayerInfo(layer_info,number_layers);
1705 ThrowBinaryException(CorruptImageError,"MaximumChannelsExceeded",
1708 if (image->debug != MagickFalse)
1709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1710 " offset(%.20g,%.20g), size(%.20g,%.20g), channels=%.20g",
1711 (double) layer_info[i].page.x,(double) layer_info[i].page.y,
1712 (double) layer_info[i].page.height,(double)
1713 layer_info[i].page.width,(double) layer_info[i].channels);
1714 for (j=0; j < (ssize_t) layer_info[i].channels; j++)
1716 layer_info[i].channel_info[j].type=(short) ReadBlobShort(image);
1717 if ((layer_info[i].channel_info[j].type < -4) ||
1718 (layer_info[i].channel_info[j].type > 4))
1720 layer_info=DestroyLayerInfo(layer_info,number_layers);
1721 ThrowBinaryException(CorruptImageError,"NoSuchImageChannel",
1724 layer_info[i].channel_info[j].size=(size_t) GetPSDSize(psd_info,
1726 if (image->debug != MagickFalse)
1727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1728 " channel[%.20g]: type=%.20g, size=%.20g",(double) j,
1729 (double) layer_info[i].channel_info[j].type,
1730 (double) layer_info[i].channel_info[j].size);
1732 if (CheckPSDChannels(psd_info,&layer_info[i]) == MagickFalse)
1734 layer_info=DestroyLayerInfo(layer_info,number_layers);
1735 ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
1738 count=ReadBlob(image,4,(unsigned char *) type);
1740 ReversePSDString(image,type,4);
1741 if ((count != 4) || (LocaleNCompare(type,"8BIM",4) != 0))
1743 if (image->debug != MagickFalse)
1744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1745 " layer type was %.4s instead of 8BIM", type);
1746 layer_info=DestroyLayerInfo(layer_info,number_layers);
1747 ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
1750 count=ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey);
1753 layer_info=DestroyLayerInfo(layer_info,number_layers);
1754 ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
1757 ReversePSDString(image,layer_info[i].blendkey,4);
1758 layer_info[i].opacity=(Quantum) ScaleCharToQuantum((unsigned char)
1759 ReadBlobByte(image));
1760 layer_info[i].clipping=(unsigned char) ReadBlobByte(image);
1761 layer_info[i].flags=(unsigned char) ReadBlobByte(image);
1762 layer_info[i].visible=!(layer_info[i].flags & 0x02);
1763 if (image->debug != MagickFalse)
1764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1765 " blend=%.4s, opacity=%.20g, clipping=%s, flags=%d, visible=%s",
1766 layer_info[i].blendkey,(double) layer_info[i].opacity,
1767 layer_info[i].clipping ? "true" : "false",layer_info[i].flags,
1768 layer_info[i].visible ? "true" : "false");
1769 (void) ReadBlobByte(image); /* filler */
1771 size=ReadBlobLong(image);
1778 if (image->debug != MagickFalse)
1779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1780 " layer contains additional info");
1781 length=ReadBlobLong(image);
1782 combined_length=length+4;
1788 layer_info[i].mask.page.y=(ssize_t) ReadBlobSignedLong(image);
1789 layer_info[i].mask.page.x=(ssize_t) ReadBlobSignedLong(image);
1790 layer_info[i].mask.page.height=(size_t)
1791 (ReadBlobSignedLong(image)-layer_info[i].mask.page.y);
1792 layer_info[i].mask.page.width=(size_t) (
1793 ReadBlobSignedLong(image)-layer_info[i].mask.page.x);
1794 layer_info[i].mask.background=(unsigned char) ReadBlobByte(
1796 layer_info[i].mask.flags=(unsigned char) ReadBlobByte(image);
1797 if (!(layer_info[i].mask.flags & 0x01))
1799 layer_info[i].mask.page.y=layer_info[i].mask.page.y-
1800 layer_info[i].page.y;
1801 layer_info[i].mask.page.x=layer_info[i].mask.page.x-
1802 layer_info[i].page.x;
1804 if (image->debug != MagickFalse)
1805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1806 " layer mask: offset(%.20g,%.20g), size(%.20g,%.20g), length=%.20g",
1807 (double) layer_info[i].mask.page.x,(double)
1808 layer_info[i].mask.page.y,(double)
1809 layer_info[i].mask.page.width,(double)
1810 layer_info[i].mask.page.height,(double) ((MagickOffsetType)
1813 Skip over the rest of the layer mask information.
1815 if (DiscardBlobBytes(image,(MagickSizeType) (length-18)) == MagickFalse)
1817 layer_info=DestroyLayerInfo(layer_info,number_layers);
1818 ThrowBinaryException(CorruptImageError,
1819 "UnexpectedEndOfFile",image->filename);
1822 length=ReadBlobLong(image);
1823 combined_length+=length+4;
1827 Layer blending ranges info.
1829 if (image->debug != MagickFalse)
1830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1831 " layer blending ranges: length=%.20g",(double)
1832 ((MagickOffsetType) length));
1833 if (DiscardBlobBytes(image,length) == MagickFalse)
1835 layer_info=DestroyLayerInfo(layer_info,number_layers);
1836 ThrowBinaryException(CorruptImageError,
1837 "UnexpectedEndOfFile",image->filename);
1843 length=(MagickSizeType) (unsigned char) ReadBlobByte(image);
1844 combined_length+=length+1;
1846 (void) ReadBlob(image,(size_t) length++,layer_info[i].name);
1847 layer_info[i].name[length]='\0';
1848 if (image->debug != MagickFalse)
1849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1850 " layer name: %s",layer_info[i].name);
1851 if ((length % 4) != 0)
1853 length=4-(length % 4);
1854 combined_length+=length;
1855 /* Skip over the padding of the layer name */
1856 if (DiscardBlobBytes(image,length) == MagickFalse)
1858 layer_info=DestroyLayerInfo(layer_info,number_layers);
1859 ThrowBinaryException(CorruptImageError,
1860 "UnexpectedEndOfFile",image->filename);
1863 length=(MagickSizeType) size-combined_length;
1869 if (length > GetBlobSize(image))
1871 layer_info=DestroyLayerInfo(layer_info,number_layers);
1872 ThrowBinaryException(CorruptImageError,
1873 "InsufficientImageDataInFile",image->filename);
1875 layer_info[i].info=AcquireStringInfo((const size_t) length);
1876 info=GetStringInfoDatum(layer_info[i].info);
1877 (void) ReadBlob(image,(const size_t) length,info);
1882 for (i=0; i < number_layers; i++)
1884 if ((layer_info[i].page.width == 0) || (layer_info[i].page.height == 0))
1886 if (image->debug != MagickFalse)
1887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1888 " layer data is empty");
1889 if (layer_info[i].info != (StringInfo *) NULL)
1890 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
1895 Allocate layered image.
1897 layer_info[i].image=CloneImage(image,layer_info[i].page.width,
1898 layer_info[i].page.height,MagickFalse,exception);
1899 if (layer_info[i].image == (Image *) NULL)
1901 layer_info=DestroyLayerInfo(layer_info,number_layers);
1902 if (image->debug != MagickFalse)
1903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1904 " allocation of image for layer %.20g failed",(double) i);
1905 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1908 if (layer_info[i].info != (StringInfo *) NULL)
1910 (void) SetImageProfile(layer_info[i].image,"psd:additional-info",
1911 layer_info[i].info,exception);
1912 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
1916 if (image_info->ping == MagickFalse)
1918 for (i=0; i < number_layers; i++)
1920 if (layer_info[i].image == (Image *) NULL)
1922 for (j=0; j < (ssize_t) layer_info[i].channels; j++)
1924 if (DiscardBlobBytes(image,(MagickSizeType)
1925 layer_info[i].channel_info[j].size) == MagickFalse)
1927 layer_info=DestroyLayerInfo(layer_info,number_layers);
1928 ThrowBinaryException(CorruptImageError,
1929 "UnexpectedEndOfFile",image->filename);
1935 if (image->debug != MagickFalse)
1936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1937 " reading data for layer %.20g",(double) i);
1939 status=ReadPSDLayer(image,image_info,psd_info,&layer_info[i],
1941 if (status == MagickFalse)
1944 status=SetImageProgress(image,LoadImagesTag,(MagickOffsetType) i,
1945 (MagickSizeType) number_layers);
1946 if (status == MagickFalse)
1951 if (status != MagickFalse)
1953 for (i=0; i < number_layers; i++)
1955 if (layer_info[i].image == (Image *) NULL)
1957 for (j=i; j < number_layers - 1; j++)
1958 layer_info[j] = layer_info[j+1];
1964 if (number_layers > 0)
1966 for (i=0; i < number_layers; i++)
1969 layer_info[i].image->previous=layer_info[i-1].image;
1970 if (i < (number_layers-1))
1971 layer_info[i].image->next=layer_info[i+1].image;
1972 layer_info[i].image->page=layer_info[i].page;
1974 image->next=layer_info[0].image;
1975 layer_info[0].image->previous=image;
1977 layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
1980 layer_info=DestroyLayerInfo(layer_info,number_layers);
1986 ModuleExport MagickBooleanType ReadPSDLayers(Image *image,
1987 const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception)
1995 domain=CoderPolicyDomain;
1996 rights=ReadPolicyRights;
1997 if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
1999 return(ReadPSDLayersInternal(image,image_info,psd_info,MagickFalse,
2003 static MagickBooleanType ReadPSDMergedImage(const ImageInfo *image_info,
2004 Image *image,const PSDInfo *psd_info,ExceptionInfo *exception)
2018 compression=(PSDCompressionType) ReadBlobMSBShort(image);
2019 image->compression=ConvertPSDCompression(compression);
2021 if (compression != Raw && compression != RLE)
2023 (void) ThrowMagickException(exception,GetMagickModule(),
2024 TypeWarning,"CompressionNotSupported","'%.20g'",(double) compression);
2025 return(MagickFalse);
2028 sizes=(MagickOffsetType *) NULL;
2029 if (compression == RLE)
2031 sizes=ReadPSDRLESizes(image,psd_info,image->rows*psd_info->channels);
2032 if (sizes == (MagickOffsetType *) NULL)
2033 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2038 for (i=0; i < (ssize_t) psd_info->channels; i++)
2044 if ((type == 1) && (psd_info->channels == 2))
2047 if (compression == RLE)
2048 status=ReadPSDChannelRLE(image,psd_info,type,sizes+(i*image->rows),
2051 status=ReadPSDChannelRaw(image,psd_info->channels,type,exception);
2053 if (status != MagickFalse)
2054 status=SetImageProgress(image,LoadImagesTag,(MagickOffsetType) i,
2055 psd_info->channels);
2057 if (status == MagickFalse)
2061 if ((status != MagickFalse) && (image->colorspace == CMYKColorspace))
2062 status=NegateCMYK(image,exception);
2064 if (status != MagickFalse)
2065 status=CorrectPSDAlphaBlend(image_info,image,exception);
2067 sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
2072 static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
2108 assert(image_info != (const ImageInfo *) NULL);
2109 assert(image_info->signature == MagickCoreSignature);
2110 if (image_info->debug != MagickFalse)
2111 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2112 image_info->filename);
2113 assert(exception != (ExceptionInfo *) NULL);
2114 assert(exception->signature == MagickCoreSignature);
2116 image=AcquireImage(image_info,exception);
2117 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2118 if (status == MagickFalse)
2120 image=DestroyImageList(image);
2121 return((Image *) NULL);
2126 image->endian=MSBEndian;
2127 count=ReadBlob(image,4,(unsigned char *) psd_info.signature);
2128 psd_info.version=ReadBlobMSBShort(image);
2129 if ((count != 4) || (LocaleNCompare(psd_info.signature,"8BPS",4) != 0) ||
2130 ((psd_info.version != 1) && (psd_info.version != 2)))
2131 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2132 (void) ReadBlob(image,6,psd_info.reserved);
2133 psd_info.channels=ReadBlobMSBShort(image);
2134 if (psd_info.channels < 1)
2135 ThrowReaderException(CorruptImageError,"MissingImageChannel");
2136 if (psd_info.channels > MaxPSDChannels)
2137 ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded");
2138 psd_info.rows=ReadBlobMSBLong(image);
2139 psd_info.columns=ReadBlobMSBLong(image);
2140 if ((psd_info.version == 1) && ((psd_info.rows > 30000) ||
2141 (psd_info.columns > 30000)))
2142 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2143 psd_info.depth=ReadBlobMSBShort(image);
2144 if ((psd_info.depth != 1) && (psd_info.depth != 8) &&
2145 (psd_info.depth != 16) && (psd_info.depth != 32))
2146 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2147 psd_info.mode=ReadBlobMSBShort(image);
2148 if ((psd_info.mode == IndexedMode) && (psd_info.channels > 3))
2149 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2150 if (image->debug != MagickFalse)
2151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2152 " Image is %.20g x %.20g with channels=%.20g, depth=%.20g, mode=%s",
2153 (double) psd_info.columns,(double) psd_info.rows,(double)
2154 psd_info.channels,(double) psd_info.depth,ModeToString((PSDImageType)
2156 if (EOFBlob(image) != MagickFalse)
2157 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2161 image->depth=psd_info.depth;
2162 image->columns=psd_info.columns;
2163 image->rows=psd_info.rows;
2164 status=SetImageExtent(image,image->columns,image->rows,exception);
2165 if (status == MagickFalse)
2166 return(DestroyImageList(image));
2167 status=ResetImagePixels(image,exception);
2168 if (status == MagickFalse)
2169 return(DestroyImageList(image));
2170 psd_info.min_channels=3;
2171 if (psd_info.mode == LabMode)
2172 (void) SetImageColorspace(image,LabColorspace,exception);
2173 if (psd_info.mode == CMYKMode)
2175 psd_info.min_channels=4;
2176 (void) SetImageColorspace(image,CMYKColorspace,exception);
2177 if (psd_info.channels > 4)
2178 (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2180 else if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) ||
2181 (psd_info.mode == DuotoneMode))
2183 if (psd_info.depth != 32)
2185 status=AcquireImageColormap(image,(size_t) (psd_info.depth < 16 ?
2186 256 : 65536),exception);
2187 if (status == MagickFalse)
2188 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2189 if (image->debug != MagickFalse)
2190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2191 " Image colormap allocated");
2193 psd_info.min_channels=1;
2194 (void) SetImageColorspace(image,GRAYColorspace,exception);
2195 if (psd_info.channels > 1)
2196 (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2199 if (psd_info.channels > 3)
2200 (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2201 if (psd_info.channels < psd_info.min_channels)
2202 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2204 Read PSD raster colormap only present for indexed and duotone images.
2206 length=ReadBlobMSBLong(image);
2207 if ((psd_info.mode == IndexedMode) && (length < 3))
2208 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2211 if (image->debug != MagickFalse)
2212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2213 " reading colormap");
2214 if ((psd_info.mode == DuotoneMode) || (psd_info.depth == 32))
2217 Duotone image data; the format of this data is undocumented.
2218 32 bits per pixel; the colormap is ignored.
2220 (void) SeekBlob(image,(const MagickOffsetType) length,SEEK_CUR);
2228 Read PSD raster colormap.
2230 number_colors=(size_t) length/3;
2231 if (number_colors > 65536)
2232 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2233 if (AcquireImageColormap(image,number_colors,exception) == MagickFalse)
2234 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2235 for (i=0; i < (ssize_t) image->colors; i++)
2236 image->colormap[i].red=(MagickRealType) ScaleCharToQuantum(
2237 (unsigned char) ReadBlobByte(image));
2238 for (i=0; i < (ssize_t) image->colors; i++)
2239 image->colormap[i].green=(MagickRealType) ScaleCharToQuantum(
2240 (unsigned char) ReadBlobByte(image));
2241 for (i=0; i < (ssize_t) image->colors; i++)
2242 image->colormap[i].blue=(MagickRealType) ScaleCharToQuantum(
2243 (unsigned char) ReadBlobByte(image));
2244 image->alpha_trait=UndefinedPixelTrait;
2247 if ((image->depth == 1) && (image->storage_class != PseudoClass))
2248 ThrowReaderException(CorruptImageError, "ImproperImageHeader");
2249 has_merged_image=MagickTrue;
2250 profile=(StringInfo *) NULL;
2251 length=ReadBlobMSBLong(image);
2258 Image resources block.
2260 if (image->debug != MagickFalse)
2261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2262 " reading image resource blocks - %.20g bytes",(double)
2263 ((MagickOffsetType) length));
2264 if (length > GetBlobSize(image))
2265 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
2266 blocks=(unsigned char *) AcquireQuantumMemory((size_t) length,
2268 if (blocks == (unsigned char *) NULL)
2269 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2270 count=ReadBlob(image,(size_t) length,blocks);
2271 if ((count != (ssize_t) length) || (length < 4) ||
2272 (LocaleNCompare((char *) blocks,"8BIM",4) != 0))
2274 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
2275 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2277 profile=ParseImageResourceBlocks(image,blocks,(size_t) length,
2278 &has_merged_image,exception);
2279 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
2282 Layer and mask block.
2284 length=GetPSDSize(&psd_info,image);
2287 length=ReadBlobMSBLong(image);
2288 length=ReadBlobMSBLong(image);
2290 offset=TellBlob(image);
2291 skip_layers=MagickFalse;
2292 if ((image_info->number_scenes == 1) && (image_info->scene == 0) &&
2293 (has_merged_image != MagickFalse))
2295 if (image->debug != MagickFalse)
2296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2297 " read composite only");
2298 skip_layers=MagickTrue;
2302 if (image->debug != MagickFalse)
2303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2304 " image has no layers");
2308 if (ReadPSDLayersInternal(image,image_info,&psd_info,skip_layers,
2309 exception) != MagickTrue)
2311 if (profile != (StringInfo *) NULL)
2312 profile=DestroyStringInfo(profile);
2313 (void) CloseBlob(image);
2314 image=DestroyImageList(image);
2315 return((Image *) NULL);
2319 Skip the rest of the layer and mask information.
2321 (void) SeekBlob(image,offset+length,SEEK_SET);
2324 If we are only "pinging" the image, then we're done - so return.
2326 if (EOFBlob(image) != MagickFalse)
2328 if (profile != (StringInfo *) NULL)
2329 profile=DestroyStringInfo(profile);
2330 ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
2332 if (image_info->ping != MagickFalse)
2334 if (profile != (StringInfo *) NULL)
2335 profile=DestroyStringInfo(profile);
2336 (void) CloseBlob(image);
2337 return(GetFirstImageInList(image));
2340 Read the precombined layer, present for PSD < 4 compatibility.
2342 if (image->debug != MagickFalse)
2343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2344 " reading the precombined layer");
2345 imageListLength=GetImageListLength(image);
2346 if ((has_merged_image != MagickFalse) || (imageListLength == 1))
2347 has_merged_image=(MagickBooleanType) ReadPSDMergedImage(image_info,image,
2348 &psd_info,exception);
2349 if ((has_merged_image == MagickFalse) && (imageListLength == 1) &&
2352 (void) SeekBlob(image,offset,SEEK_SET);
2353 status=ReadPSDLayersInternal(image,image_info,&psd_info,MagickFalse,
2355 if (status != MagickTrue)
2357 if (profile != (StringInfo *) NULL)
2358 profile=DestroyStringInfo(profile);
2359 (void) CloseBlob(image);
2360 image=DestroyImageList(image);
2361 return((Image *) NULL);
2364 if (has_merged_image == MagickFalse)
2369 if (imageListLength == 1)
2371 if (profile != (StringInfo *) NULL)
2372 profile=DestroyStringInfo(profile);
2373 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
2375 image->background_color.alpha=(MagickRealType) TransparentAlpha;
2376 image->background_color.alpha_trait=BlendPixelTrait;
2377 (void) SetImageBackgroundColor(image,exception);
2378 merged=MergeImageLayers(image,FlattenLayer,exception);
2379 ReplaceImageInList(&image,merged);
2381 if (profile != (StringInfo *) NULL)
2383 (void) SetImageProfile(image,GetStringInfoName(profile),profile,
2385 profile=DestroyStringInfo(profile);
2387 (void) CloseBlob(image);
2388 return(GetFirstImageInList(image));
2392 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2396 % R e g i s t e r P S D I m a g e %
2400 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2402 % RegisterPSDImage() adds properties for the PSD image format to
2403 % the list of supported formats. The properties include the image format
2404 % tag, a method to read and/or write the format, whether the format
2405 % supports the saving of more than one frame to the same file or blob,
2406 % whether the format supports native in-memory I/O, and a brief
2407 % description of the format.
2409 % The format of the RegisterPSDImage method is:
2411 % size_t RegisterPSDImage(void)
2414 ModuleExport size_t RegisterPSDImage(void)
2419 entry=AcquireMagickInfo("PSD","PSB","Adobe Large Document Format");
2420 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2421 entry->encoder=(EncodeImageHandler *) WritePSDImage;
2422 entry->magick=(IsImageFormatHandler *) IsPSD;
2423 entry->flags|=CoderDecoderSeekableStreamFlag;
2424 entry->flags|=CoderEncoderSeekableStreamFlag;
2425 (void) RegisterMagickInfo(entry);
2426 entry=AcquireMagickInfo("PSD","PSD","Adobe Photoshop bitmap");
2427 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2428 entry->encoder=(EncodeImageHandler *) WritePSDImage;
2429 entry->magick=(IsImageFormatHandler *) IsPSD;
2430 entry->flags|=CoderDecoderSeekableStreamFlag;
2431 entry->flags|=CoderEncoderSeekableStreamFlag;
2432 (void) RegisterMagickInfo(entry);
2433 return(MagickImageCoderSignature);
2437 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2441 % U n r e g i s t e r P S D I m a g e %
2445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2447 % UnregisterPSDImage() removes format registrations made by the
2448 % PSD module from the list of supported formats.
2450 % The format of the UnregisterPSDImage method is:
2452 % UnregisterPSDImage(void)
2455 ModuleExport void UnregisterPSDImage(void)
2457 (void) UnregisterMagickInfo("PSB");
2458 (void) UnregisterMagickInfo("PSD");
2462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2466 % W r i t e P S D I m a g e %
2470 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2472 % WritePSDImage() writes an image in the Adobe Photoshop encoded image format.
2474 % The format of the WritePSDImage method is:
2476 % MagickBooleanType WritePSDImage(const ImageInfo *image_info,Image *image,
2477 % ExceptionInfo *exception)
2479 % A description of each parameter follows.
2481 % o image_info: the image info.
2483 % o image: The image.
2485 % o exception: return any errors or warnings in this structure.
2489 static inline ssize_t SetPSDOffset(const PSDInfo *psd_info,Image *image,
2490 const size_t offset)
2492 if (psd_info->version == 1)
2493 return(WriteBlobMSBShort(image,(unsigned short) offset));
2494 return(WriteBlobMSBLong(image,(unsigned int) offset));
2497 static inline ssize_t WritePSDOffset(const PSDInfo *psd_info,Image *image,
2498 const MagickSizeType size,const MagickOffsetType offset)
2506 current_offset=TellBlob(image);
2507 (void) SeekBlob(image,offset,SEEK_SET);
2508 if (psd_info->version == 1)
2509 result=WriteBlobMSBShort(image,(unsigned short) size);
2511 result=WriteBlobMSBLong(image,(unsigned int) size);
2512 (void) SeekBlob(image,current_offset,SEEK_SET);
2516 static inline ssize_t SetPSDSize(const PSDInfo *psd_info,Image *image,
2517 const MagickSizeType size)
2519 if (psd_info->version == 1)
2520 return(WriteBlobLong(image,(unsigned int) size));
2521 return(WriteBlobLongLong(image,size));
2524 static inline ssize_t WritePSDSize(const PSDInfo *psd_info,Image *image,
2525 const MagickSizeType size,const MagickOffsetType offset)
2533 current_offset=TellBlob(image);
2534 (void) SeekBlob(image,offset,SEEK_SET);
2535 result=SetPSDSize(psd_info,image,size);
2536 (void) SeekBlob(image,current_offset,SEEK_SET);
2540 static size_t PSDPackbitsEncodeImage(Image *image,const size_t length,
2541 const unsigned char *pixels,unsigned char *compact_pixels,
2542 ExceptionInfo *exception)
2551 register unsigned char
2558 Compress pixels with Packbits encoding.
2560 assert(image != (Image *) NULL);
2561 assert(image->signature == MagickCoreSignature);
2562 if (image->debug != MagickFalse)
2563 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2564 assert(pixels != (unsigned char *) NULL);
2565 assert(compact_pixels != (unsigned char *) NULL);
2566 packbits=(unsigned char *) AcquireQuantumMemory(128UL,sizeof(*packbits));
2567 if (packbits == (unsigned char *) NULL)
2568 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2571 for (i=(ssize_t) length; i != 0; )
2578 *q++=(unsigned char) 0;
2585 *q++=(unsigned char) 1;
2593 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2595 *q++=(unsigned char) ((256-3)+1);
2599 *q++=(unsigned char) 2;
2607 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2613 while (((ssize_t) count < i) && (*pixels == *(pixels+count)))
2620 *q++=(unsigned char) ((256-count)+1);
2629 while ((*(pixels+count) != *(pixels+count+1)) ||
2630 (*(pixels+count+1) != *(pixels+count+2)))
2632 packbits[count+1]=pixels[count];
2634 if (((ssize_t) count >= (i-3)) || (count >= 127))
2638 *packbits=(unsigned char) (count-1);
2639 for (j=0; j <= (ssize_t) count; j++)
2646 *q++=(unsigned char) 128; /* EOD marker */
2647 packbits=(unsigned char *) RelinquishMagickMemory(packbits);
2648 return((size_t) (q-compact_pixels));
2651 static size_t WriteCompressionStart(const PSDInfo *psd_info,Image *image,
2652 const Image *next_image,const CompressionType compression,
2653 const ssize_t channels)
2662 if (compression == RLECompression)
2664 length=(size_t) WriteBlobShort(image,RLE);
2665 for (i=0; i < channels; i++)
2666 for (y=0; y < (ssize_t) next_image->rows; y++)
2667 length+=SetPSDOffset(psd_info,image,0);
2669 #ifdef MAGICKCORE_ZLIB_DELEGATE
2670 else if (compression == ZipCompression)
2671 length=(size_t) WriteBlobShort(image,ZipWithoutPrediction);
2674 length=(size_t) WriteBlobShort(image,Raw);
2678 static size_t WritePSDChannel(const PSDInfo *psd_info,
2679 const ImageInfo *image_info,Image *image,Image *next_image,
2680 const QuantumType quantum_type, unsigned char *compact_pixels,
2681 MagickOffsetType size_offset,const MagickBooleanType separate,
2682 const CompressionType compression,ExceptionInfo *exception)
2690 register const Quantum
2706 #ifdef MAGICKCORE_ZLIB_DELEGATE
2720 compressed_pixels=(unsigned char *) NULL;
2724 if (separate != MagickFalse)
2726 size_offset=TellBlob(image)+2;
2727 count+=WriteCompressionStart(psd_info,image,next_image,compression,1);
2729 if (next_image->depth > 8)
2730 next_image->depth=16;
2731 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
2732 MagickTrue : MagickFalse;
2733 quantum_info=AcquireQuantumInfo(image_info,next_image);
2734 if (quantum_info == (QuantumInfo *) NULL)
2736 pixels=(unsigned char *) GetQuantumPixels(quantum_info);
2737 #ifdef MAGICKCORE_ZLIB_DELEGATE
2738 if (compression == ZipCompression)
2740 compressed_pixels=(unsigned char *) AcquireQuantumMemory(CHUNK,
2741 sizeof(*compressed_pixels));
2742 if (compressed_pixels == (unsigned char *) NULL)
2744 quantum_info=DestroyQuantumInfo(quantum_info);
2747 memset(&stream,0,sizeof(stream));
2748 stream.data_type=Z_BINARY;
2749 level=Z_DEFAULT_COMPRESSION;
2750 if ((image_info->quality > 0 && image_info->quality < 10))
2751 level=(int) image_info->quality;
2752 if (deflateInit(&stream,level) != Z_OK)
2754 quantum_info=DestroyQuantumInfo(quantum_info);
2759 for (y=0; y < (ssize_t) next_image->rows; y++)
2761 p=GetVirtualPixels(next_image,0,y,next_image->columns,1,exception);
2762 if (p == (const Quantum *) NULL)
2764 length=ExportQuantumPixels(next_image,(CacheView *) NULL,quantum_info,
2765 quantum_type,pixels,exception);
2766 if (monochrome != MagickFalse)
2767 for (i=0; i < (ssize_t) length; i++)
2768 pixels[i]=(~pixels[i]);
2769 if (compression == RLECompression)
2771 length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels,
2773 count+=WriteBlob(image,length,compact_pixels);
2774 size_offset+=WritePSDOffset(psd_info,image,length,size_offset);
2776 #ifdef MAGICKCORE_ZLIB_DELEGATE
2777 else if (compression == ZipCompression)
2779 stream.avail_in=(uInt) length;
2780 stream.next_in=(Bytef *) pixels;
2781 if (y == (ssize_t) next_image->rows-1)
2784 stream.avail_out=(uInt) CHUNK;
2785 stream.next_out=(Bytef *) compressed_pixels;
2786 if (deflate(&stream,flush) == Z_STREAM_ERROR)
2788 length=(size_t) CHUNK-stream.avail_out;
2790 count+=WriteBlob(image,length,compressed_pixels);
2791 } while (stream.avail_out == 0);
2795 count+=WriteBlob(image,length,pixels);
2797 #ifdef MAGICKCORE_ZLIB_DELEGATE
2798 if (compression == ZipCompression)
2800 (void) deflateEnd(&stream);
2801 compressed_pixels=(unsigned char *) RelinquishMagickMemory(
2805 quantum_info=DestroyQuantumInfo(quantum_info);
2809 static unsigned char *AcquireCompactPixels(const Image *image,
2810 ExceptionInfo *exception)
2818 packet_size=image->depth > 8UL ? 2UL : 1UL;
2819 compact_pixels=(unsigned char *) AcquireQuantumMemory((9*
2820 image->columns)+1,packet_size*sizeof(*compact_pixels));
2821 if (compact_pixels == (unsigned char *) NULL)
2823 (void) ThrowMagickException(exception,GetMagickModule(),
2824 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2826 return(compact_pixels);
2829 static size_t WritePSDChannels(const PSDInfo *psd_info,
2830 const ImageInfo *image_info,Image *image,Image *next_image,
2831 MagickOffsetType size_offset,const MagickBooleanType separate,
2832 ExceptionInfo *exception)
2855 compact_pixels=(unsigned char *) NULL;
2856 compression=next_image->compression;
2857 if (image_info->compression != UndefinedCompression)
2858 compression=image_info->compression;
2859 if (compression == RLECompression)
2861 compact_pixels=AcquireCompactPixels(next_image,exception);
2862 if (compact_pixels == (unsigned char *) NULL)
2866 if (separate == MagickFalse)
2868 if (next_image->storage_class != PseudoClass)
2870 if (IsImageGray(next_image) == MagickFalse)
2871 channels=(size_t) (next_image->colorspace == CMYKColorspace ? 4 :
2873 if (next_image->alpha_trait != UndefinedPixelTrait)
2876 rows_offset=TellBlob(image)+2;
2877 count+=WriteCompressionStart(psd_info,image,next_image,compression,
2878 (ssize_t) channels);
2879 offset_length=(next_image->rows*(psd_info->version == 1 ? 2 : 4));
2882 if (next_image->storage_class == PseudoClass)
2884 length=WritePSDChannel(psd_info,image_info,image,next_image,
2885 IndexQuantum,compact_pixels,rows_offset,separate,compression,
2887 if (separate != MagickFalse)
2888 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2890 rows_offset+=offset_length;
2895 if (IsImageGray(next_image) != MagickFalse)
2897 length=WritePSDChannel(psd_info,image_info,image,next_image,
2898 GrayQuantum,compact_pixels,rows_offset,separate,compression,
2900 if (separate != MagickFalse)
2901 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2903 rows_offset+=offset_length;
2908 if (next_image->colorspace == CMYKColorspace)
2909 (void) NegateCMYK(next_image,exception);
2911 length=WritePSDChannel(psd_info,image_info,image,next_image,
2912 RedQuantum,compact_pixels,rows_offset,separate,compression,
2914 if (separate != MagickFalse)
2915 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2917 rows_offset+=offset_length;
2920 length=WritePSDChannel(psd_info,image_info,image,next_image,
2921 GreenQuantum,compact_pixels,rows_offset,separate,compression,
2923 if (separate != MagickFalse)
2924 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2926 rows_offset+=offset_length;
2929 length=WritePSDChannel(psd_info,image_info,image,next_image,
2930 BlueQuantum,compact_pixels,rows_offset,separate,compression,
2932 if (separate != MagickFalse)
2933 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2935 rows_offset+=offset_length;
2938 if (next_image->colorspace == CMYKColorspace)
2940 length=WritePSDChannel(psd_info,image_info,image,next_image,
2941 BlackQuantum,compact_pixels,rows_offset,separate,compression,
2943 if (separate != MagickFalse)
2944 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2946 rows_offset+=offset_length;
2950 if (next_image->alpha_trait != UndefinedPixelTrait)
2952 length=WritePSDChannel(psd_info,image_info,image,next_image,
2953 AlphaQuantum,compact_pixels,rows_offset,separate,compression,
2955 if (separate != MagickFalse)
2956 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2958 rows_offset+=offset_length;
2962 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
2963 if (next_image->colorspace == CMYKColorspace)
2964 (void) NegateCMYK(next_image,exception);
2965 if (separate != MagickFalse)
2970 property=GetImageArtifact(next_image,"psd:opacity-mask");
2971 if (property != (const char *) NULL)
2973 mask=(Image *) GetImageRegistry(ImageRegistryType,property,
2975 if (mask != (Image *) NULL)
2977 if (compression == RLECompression)
2979 compact_pixels=AcquireCompactPixels(mask,exception);
2980 if (compact_pixels == (unsigned char *) NULL)
2983 length=WritePSDChannel(psd_info,image_info,image,mask,
2984 RedQuantum,compact_pixels,rows_offset,MagickTrue,compression,
2986 (void) WritePSDSize(psd_info,image,length,size_offset);
2988 compact_pixels=(unsigned char *) RelinquishMagickMemory(
2996 static size_t WritePascalString(Image *image,const char *value,size_t padding)
3009 length=(strlen(value) > 255UL ) ? 255UL : strlen(value);
3011 count+=WriteBlobByte(image,0);
3014 count+=WriteBlobByte(image,(unsigned char) length);
3015 count+=WriteBlob(image,length,(const unsigned char *) value);
3018 if ((length % padding) == 0)
3020 for (i=0; i < (ssize_t) (padding-(length % padding)); i++)
3021 count+=WriteBlobByte(image,0);
3025 static void WriteResolutionResourceBlock(Image *image)
3034 if (image->units == PixelsPerCentimeterResolution)
3036 x_resolution=2.54*65536.0*image->resolution.x+0.5;
3037 y_resolution=2.54*65536.0*image->resolution.y+0.5;
3042 x_resolution=65536.0*image->resolution.x+0.5;
3043 y_resolution=65536.0*image->resolution.y+0.5;
3046 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
3047 (void) WriteBlobMSBShort(image,0x03ED);
3048 (void) WriteBlobMSBShort(image,0);
3049 (void) WriteBlobMSBLong(image,16); /* resource size */
3050 (void) WriteBlobMSBLong(image,(unsigned int) (x_resolution+0.5));
3051 (void) WriteBlobMSBShort(image,units); /* horizontal resolution unit */
3052 (void) WriteBlobMSBShort(image,units); /* width unit */
3053 (void) WriteBlobMSBLong(image,(unsigned int) (y_resolution+0.5));
3054 (void) WriteBlobMSBShort(image,units); /* vertical resolution unit */
3055 (void) WriteBlobMSBShort(image,units); /* height unit */
3058 static inline size_t WriteChannelSize(const PSDInfo *psd_info,Image *image,
3059 const signed short channel)
3064 count=(size_t) WriteBlobShort(image,(const unsigned short) channel);
3065 count+=SetPSDSize(psd_info,image,0);
3069 static void RemoveICCProfileFromResourceBlock(StringInfo *bim_profile)
3071 register const unsigned char
3088 length=GetStringInfoLength(bim_profile);
3091 datum=GetStringInfoDatum(bim_profile);
3092 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
3094 register unsigned char
3097 q=(unsigned char *) p;
3098 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
3100 p=PushLongPixel(MSBEndian,p,&long_sans);
3101 p=PushShortPixel(MSBEndian,p,&id);
3102 p=PushShortPixel(MSBEndian,p,&short_sans);
3103 p=PushLongPixel(MSBEndian,p,&count);
3104 if (id == 0x0000040f)
3109 quantum=PSDQuantum(count)+12;
3110 if ((quantum >= 12) && (quantum < (ssize_t) length))
3112 if ((q+quantum < (datum+length-16)))
3113 (void) memmove(q,q+quantum,length-quantum-(q-datum));
3114 SetStringInfoLength(bim_profile,length-quantum);
3119 if ((count & 0x01) != 0)
3124 static void RemoveResolutionFromResourceBlock(StringInfo *bim_profile)
3126 register const unsigned char
3143 length=GetStringInfoLength(bim_profile);
3146 datum=GetStringInfoDatum(bim_profile);
3147 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
3149 register unsigned char
3155 q=(unsigned char *) p;
3156 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
3158 p=PushLongPixel(MSBEndian,p,&long_sans);
3159 p=PushShortPixel(MSBEndian,p,&id);
3160 p=PushShortPixel(MSBEndian,p,&short_sans);
3161 p=PushLongPixel(MSBEndian,p,&count);
3162 cnt=PSDQuantum(count);
3165 if ((id == 0x000003ed) && (cnt < (ssize_t) (length-12)) &&
3166 ((ssize_t) length-(cnt+12)-(q-datum)) > 0)
3168 (void) memmove(q,q+cnt+12,length-(cnt+12)-(q-datum));
3169 SetStringInfoLength(bim_profile,length-(cnt+12));
3173 if ((count & 0x01) != 0)
3178 static const StringInfo *GetAdditionalInformation(const ImageInfo *image_info,
3179 Image *image,ExceptionInfo *exception)
3181 #define PSDKeySize 5
3182 #define PSDAllowedLength 36
3187 /* Whitelist of keys from: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ */
3189 allowed[PSDAllowedLength][PSDKeySize] = {
3190 "blnc", "blwh", "brit", "brst", "clbl", "clrL", "curv", "expA", "FMsk",
3191 "GdFl", "grdm", "hue ", "hue2", "infx", "knko", "lclr", "levl", "lnsr",
3192 "lfx2", "luni", "lrFX", "lspf", "lyid", "lyvr", "mixr", "nvrt", "phfl",
3193 "post", "PtFl", "selc", "shpa", "sn2P", "SoCo", "thrs", "tsly", "vibA"
3219 info=GetImageProfile(image,"psd:additional-info");
3220 if (info == (const StringInfo *) NULL)
3221 return((const StringInfo *) NULL);
3222 option=GetImageOption(image_info,"psd:additional-info");
3223 if (LocaleCompare(option,"all") == 0)
3225 if (LocaleCompare(option,"selective") != 0)
3227 profile=RemoveImageProfile(image,"psd:additional-info");
3228 return(DestroyStringInfo(profile));
3230 length=GetStringInfoLength(info);
3231 p=GetStringInfoDatum(info);
3232 remaining_length=length;
3234 while (remaining_length >= 12)
3236 /* skip over signature */
3238 key[0]=(char) (*p++);
3239 key[1]=(char) (*p++);
3240 key[2]=(char) (*p++);
3241 key[3]=(char) (*p++);
3243 size=(unsigned int) (*p++) << 24;
3244 size|=(unsigned int) (*p++) << 16;
3245 size|=(unsigned int) (*p++) << 8;
3246 size|=(unsigned int) (*p++);
3247 size=size & 0xffffffff;
3248 remaining_length-=12;
3249 if ((size_t) size > remaining_length)
3250 return((const StringInfo *) NULL);
3252 for (i=0; i < PSDAllowedLength; i++)
3254 if (LocaleNCompare(key,allowed[i],PSDKeySize) != 0)
3260 remaining_length-=(size_t) size;
3261 if (found == MagickFalse)
3263 if (remaining_length > 0)
3264 p=(unsigned char *) memmove(p-12,p+size,remaining_length);
3267 length+=(size_t) size+12;
3270 profile=RemoveImageProfile(image,"psd:additional-info");
3272 return(DestroyStringInfo(profile));
3273 SetStringInfoLength(profile,(const size_t) length);
3274 (void) SetImageProfile(image,"psd:additional-info",info,exception);
3278 static MagickBooleanType WritePSDLayersInternal(Image *image,
3279 const ImageInfo *image_info,const PSDInfo *psd_info,size_t *layers_size,
3280 ExceptionInfo *exception)
3283 layer_name[MagickPathExtent];
3299 *layer_size_offsets,
3314 base_image=GetNextImageInList(image);
3315 if (base_image == (Image *) NULL)
3318 size_offset=TellBlob(image);
3319 (void) SetPSDSize(psd_info,image,0);
3321 for (next_image=base_image; next_image != NULL; )
3324 next_image=GetNextImageInList(next_image);
3326 if (image->alpha_trait != UndefinedPixelTrait)
3327 size+=WriteBlobShort(image,-(unsigned short) layer_count);
3329 size+=WriteBlobShort(image,(unsigned short) layer_count);
3330 layer_size_offsets=(MagickOffsetType *) AcquireQuantumMemory(
3331 (size_t) layer_count,sizeof(MagickOffsetType));
3332 if (layer_size_offsets == (MagickOffsetType *) NULL)
3333 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3335 for (next_image=base_image; next_image != NULL; )
3347 mask=(Image *) NULL;
3348 property=GetImageArtifact(next_image,"psd:opacity-mask");
3350 if (property != (const char *) NULL)
3352 mask=(Image *) GetImageRegistry(ImageRegistryType,property,exception);
3353 default_color=(unsigned char) (strlen(property) == 9 ? 255 : 0);
3355 size+=WriteBlobSignedLong(image,(signed int) next_image->page.y);
3356 size+=WriteBlobSignedLong(image,(signed int) next_image->page.x);
3357 size+=WriteBlobSignedLong(image,(signed int) (next_image->page.y+
3359 size+=WriteBlobSignedLong(image,(signed int) (next_image->page.x+
3360 next_image->columns));
3362 if ((next_image->storage_class != PseudoClass) &&
3363 (IsImageGray(next_image) == MagickFalse))
3364 channels=(unsigned short) (next_image->colorspace == CMYKColorspace ? 4 :
3366 total_channels=channels;
3367 if (next_image->alpha_trait != UndefinedPixelTrait)
3369 if (mask != (Image *) NULL)
3371 size+=WriteBlobShort(image,total_channels);
3372 layer_size_offsets[layer_index++]=TellBlob(image);
3373 for (i=0; i < (ssize_t) channels; i++)
3374 size+=WriteChannelSize(psd_info,image,(signed short) i);
3375 if (next_image->alpha_trait != UndefinedPixelTrait)
3376 size+=WriteChannelSize(psd_info,image,-1);
3377 if (mask != (Image *) NULL)
3378 size+=WriteChannelSize(psd_info,image,-2);
3379 size+=WriteBlobString(image,image->endian == LSBEndian ? "MIB8" :"8BIM");
3380 size+=WriteBlobString(image,CompositeOperatorToPSDBlendMode(next_image));
3381 property=GetImageArtifact(next_image,"psd:layer.opacity");
3382 if (property != (const char *) NULL)
3387 opacity=(Quantum) StringToInteger(property);
3388 size+=WriteBlobByte(image,ScaleQuantumToChar(opacity));
3389 (void) ApplyPSDLayerOpacity(next_image,opacity,MagickTrue,exception);
3392 size+=WriteBlobByte(image,255);
3393 size+=WriteBlobByte(image,0);
3394 size+=WriteBlobByte(image,(const unsigned char)
3395 (next_image->compose == NoCompositeOp ? 1 << 0x02 : 1)); /* layer properties - visible, etc. */
3396 size+=WriteBlobByte(image,0);
3397 info=GetAdditionalInformation(image_info,next_image,exception);
3398 property=(const char *) GetImageProperty(next_image,"label",exception);
3399 if (property == (const char *) NULL)
3401 (void) FormatLocaleString(layer_name,MagickPathExtent,"L%.20g",
3402 (double) layer_index);
3403 property=layer_name;
3405 name_length=strlen(property)+1;
3406 if ((name_length % 4) != 0)
3407 name_length+=(4-(name_length % 4));
3408 if (info != (const StringInfo *) NULL)
3409 name_length+=GetStringInfoLength(info);
3411 if (mask != (Image *) NULL)
3413 size+=WriteBlobLong(image,(unsigned int) name_length);
3414 if (mask == (Image *) NULL)
3415 size+=WriteBlobLong(image,0);
3418 if (mask->compose != NoCompositeOp)
3419 (void) ApplyPSDOpacityMask(next_image,mask,ScaleCharToQuantum(
3420 default_color),MagickTrue,exception);
3421 mask->page.y+=image->page.y;
3422 mask->page.x+=image->page.x;
3423 size+=WriteBlobLong(image,20);
3424 size+=WriteBlobSignedLong(image,(const signed int) mask->page.y);
3425 size+=WriteBlobSignedLong(image,(const signed int) mask->page.x);
3426 size+=WriteBlobSignedLong(image,(const signed int) (mask->rows+
3428 size+=WriteBlobSignedLong(image,(const signed int) (mask->columns+
3430 size+=WriteBlobByte(image,default_color);
3431 size+=WriteBlobByte(image,(const unsigned char)
3432 (mask->compose == NoCompositeOp ? 2 : 0));
3433 size+=WriteBlobMSBShort(image,0);
3435 size+=WriteBlobLong(image,0);
3436 size+=WritePascalString(image,property,4);
3437 if (info != (const StringInfo *) NULL)
3438 size+=WriteBlob(image,GetStringInfoLength(info),
3439 GetStringInfoDatum(info));
3440 next_image=GetNextImageInList(next_image);
3445 next_image=base_image;
3447 while (next_image != NULL)
3449 length=WritePSDChannels(psd_info,image_info,image,next_image,
3450 layer_size_offsets[layer_index++],MagickTrue,exception);
3457 next_image=GetNextImageInList(next_image);
3460 Write the total size
3462 if (layers_size != (size_t*) NULL)
3464 if ((size/2) != ((size+1)/2))
3465 rounded_size=size+1;
3468 (void) WritePSDSize(psd_info,image,rounded_size,size_offset);
3469 layer_size_offsets=(MagickOffsetType *) RelinquishMagickMemory(
3470 layer_size_offsets);
3472 Remove the opacity mask from the registry
3474 next_image=base_image;
3475 while (next_image != (Image *) NULL)
3477 property=GetImageArtifact(next_image,"psd:opacity-mask");
3478 if (property != (const char *) NULL)
3479 (void) DeleteImageRegistry(property);
3480 next_image=GetNextImageInList(next_image);
3486 ModuleExport MagickBooleanType WritePSDLayers(Image * image,
3487 const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception)
3495 domain=CoderPolicyDomain;
3496 rights=WritePolicyRights;
3497 if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
3499 return WritePSDLayersInternal(image,image_info,psd_info,(size_t*) NULL,
3503 static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
3504 Image *image,ExceptionInfo *exception)
3529 assert(image_info != (const ImageInfo *) NULL);
3530 assert(image_info->signature == MagickCoreSignature);
3531 assert(image != (Image *) NULL);
3532 assert(image->signature == MagickCoreSignature);
3533 if (image->debug != MagickFalse)
3534 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3535 assert(exception != (ExceptionInfo *) NULL);
3536 assert(exception->signature == MagickCoreSignature);
3537 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
3538 if (status == MagickFalse)
3540 packet_size=(size_t) (image->depth > 8 ? 6 : 3);
3541 if (image->alpha_trait != UndefinedPixelTrait)
3542 packet_size+=image->depth > 8 ? 2 : 1;
3544 if ((LocaleCompare(image_info->magick,"PSB") == 0) ||
3545 (image->columns > 30000) || (image->rows > 30000))
3547 (void) WriteBlob(image,4,(const unsigned char *) "8BPS");
3548 (void) WriteBlobMSBShort(image,psd_info.version); /* version */
3549 for (i=1; i <= 6; i++)
3550 (void) WriteBlobByte(image, 0); /* 6 bytes of reserved */
3551 /* When the image has a color profile it won't be converted to gray scale */
3552 if ((GetImageProfile(image,"icc") == (StringInfo *) NULL) &&
3553 (SetImageGray(image,exception) != MagickFalse))
3554 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
3556 if ((image_info->type != TrueColorType) && (image_info->type !=
3557 TrueColorAlphaType) && (image->storage_class == PseudoClass))
3558 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
3561 if (image->storage_class == PseudoClass)
3562 (void) SetImageStorageClass(image,DirectClass,exception);
3563 if (image->colorspace != CMYKColorspace)
3564 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 4UL : 3UL);
3566 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 5UL : 4UL);
3568 (void) WriteBlobMSBShort(image,(unsigned short) num_channels);
3569 (void) WriteBlobMSBLong(image,(unsigned int) image->rows);
3570 (void) WriteBlobMSBLong(image,(unsigned int) image->columns);
3571 if (IsImageGray(image) != MagickFalse)
3579 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
3580 MagickTrue : MagickFalse;
3581 (void) WriteBlobMSBShort(image,(unsigned short)
3582 (monochrome != MagickFalse ? 1 : image->depth > 8 ? 16 : 8));
3583 (void) WriteBlobMSBShort(image,(unsigned short)
3584 (monochrome != MagickFalse ? BitmapMode : GrayscaleMode));
3588 (void) WriteBlobMSBShort(image,(unsigned short) (image->storage_class ==
3589 PseudoClass ? 8 : image->depth > 8 ? 16 : 8));
3591 if (((image_info->colorspace != UndefinedColorspace) ||
3592 (image->colorspace != CMYKColorspace)) &&
3593 (image_info->colorspace != CMYKColorspace))
3595 (void) TransformImageColorspace(image,sRGBColorspace,exception);
3596 (void) WriteBlobMSBShort(image,(unsigned short)
3597 (image->storage_class == PseudoClass ? IndexedMode : RGBMode));
3601 if (image->colorspace != CMYKColorspace)
3602 (void) TransformImageColorspace(image,CMYKColorspace,exception);
3603 (void) WriteBlobMSBShort(image,CMYKMode);
3606 if ((IsImageGray(image) != MagickFalse) ||
3607 (image->storage_class == DirectClass) || (image->colors > 256))
3608 (void) WriteBlobMSBLong(image,0);
3612 Write PSD raster colormap.
3614 (void) WriteBlobMSBLong(image,768);
3615 for (i=0; i < (ssize_t) image->colors; i++)
3616 (void) WriteBlobByte(image,ScaleQuantumToChar(ClampToQuantum(
3617 image->colormap[i].red)));
3618 for ( ; i < 256; i++)
3619 (void) WriteBlobByte(image,0);
3620 for (i=0; i < (ssize_t) image->colors; i++)
3621 (void) WriteBlobByte(image,ScaleQuantumToChar(ClampToQuantum(
3622 image->colormap[i].green)));
3623 for ( ; i < 256; i++)
3624 (void) WriteBlobByte(image,0);
3625 for (i=0; i < (ssize_t) image->colors; i++)
3626 (void) WriteBlobByte(image,ScaleQuantumToChar(ClampToQuantum(
3627 image->colormap[i].blue)));
3628 for ( ; i < 256; i++)
3629 (void) WriteBlobByte(image,0);
3632 Image resource block.
3634 length=28; /* 0x03EB */
3635 bim_profile=(StringInfo *) GetImageProfile(image,"8bim");
3636 icc_profile=GetImageProfile(image,"icc");
3637 if (bim_profile != (StringInfo *) NULL)
3639 bim_profile=CloneStringInfo(bim_profile);
3640 if (icc_profile != (StringInfo *) NULL)
3641 RemoveICCProfileFromResourceBlock(bim_profile);
3642 RemoveResolutionFromResourceBlock(bim_profile);
3643 length+=PSDQuantum(GetStringInfoLength(bim_profile));
3645 if (icc_profile != (const StringInfo *) NULL)
3646 length+=PSDQuantum(GetStringInfoLength(icc_profile))+12;
3647 (void) WriteBlobMSBLong(image,(unsigned int) length);
3648 WriteResolutionResourceBlock(image);
3649 if (bim_profile != (StringInfo *) NULL)
3651 (void) WriteBlob(image,GetStringInfoLength(bim_profile),
3652 GetStringInfoDatum(bim_profile));
3653 bim_profile=DestroyStringInfo(bim_profile);
3655 if (icc_profile != (StringInfo *) NULL)
3657 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
3658 (void) WriteBlobMSBShort(image,0x0000040F);
3659 (void) WriteBlobMSBShort(image,0);
3660 (void) WriteBlobMSBLong(image,(unsigned int) GetStringInfoLength(
3662 (void) WriteBlob(image,GetStringInfoLength(icc_profile),
3663 GetStringInfoDatum(icc_profile));
3664 if ((ssize_t) GetStringInfoLength(icc_profile) != PSDQuantum(GetStringInfoLength(icc_profile)))
3665 (void) WriteBlobByte(image,0);
3667 if (status != MagickFalse)
3675 size_offset=TellBlob(image);
3676 (void) SetPSDSize(&psd_info,image,0);
3677 status=WritePSDLayersInternal(image,image_info,&psd_info,&size,
3679 size_offset+=WritePSDSize(&psd_info,image,size+
3680 (psd_info.version == 1 ? 8 : 12),size_offset);
3682 (void) WriteBlobMSBLong(image,0); /* user mask data */
3684 Write composite image.
3686 if (status != MagickFalse)
3691 compression=image->compression;
3692 if (image->compression == ZipCompression)
3693 image->compression=RLECompression;
3694 if (image_info->compression != UndefinedCompression)
3695 image->compression=image_info->compression;
3696 if (WritePSDChannels(&psd_info,image_info,image,image,0,MagickFalse,
3699 image->compression=compression;
3701 (void) CloseBlob(image);