2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Adobe Photoshop Image Format %
23 % Copyright 1999-2017 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(CompositeOperator op)
250 case ColorBurnCompositeOp: blend_mode = "idiv"; break;
251 case ColorDodgeCompositeOp: blend_mode = "div "; break;
252 case ColorizeCompositeOp: blend_mode = "colr"; break;
253 case DarkenCompositeOp: blend_mode = "dark"; break;
254 case DifferenceCompositeOp: blend_mode = "diff"; break;
255 case DissolveCompositeOp: blend_mode = "diss"; break;
256 case ExclusionCompositeOp: blend_mode = "smud"; break;
257 case HardLightCompositeOp: blend_mode = "hLit"; break;
258 case HardMixCompositeOp: blend_mode = "hMix"; break;
259 case HueCompositeOp: blend_mode = "hue "; break;
260 case LightenCompositeOp: blend_mode = "lite"; break;
261 case LinearBurnCompositeOp: blend_mode = "lbrn"; break;
262 case LinearDodgeCompositeOp:blend_mode = "lddg"; break;
263 case LinearLightCompositeOp:blend_mode = "lLit"; break;
264 case LuminizeCompositeOp: blend_mode = "lum "; break;
265 case MultiplyCompositeOp: blend_mode = "mul "; break;
266 case OverCompositeOp: blend_mode = "norm"; break;
267 case OverlayCompositeOp: blend_mode = "over"; break;
268 case PinLightCompositeOp: blend_mode = "pLit"; break;
269 case SaturateCompositeOp: blend_mode = "sat "; break;
270 case ScreenCompositeOp: blend_mode = "scrn"; break;
271 case SoftLightCompositeOp: blend_mode = "sLit"; break;
272 case VividLightCompositeOp: blend_mode = "vLit"; break;
273 default: blend_mode = "norm";
279 For some reason Photoshop seems to blend semi-transparent pixels with white.
280 This method reverts the blending. This can be disabled by setting the
281 option 'psd:alpha-unblend' to off.
283 static MagickBooleanType CorrectPSDAlphaBlend(const ImageInfo *image_info,
284 Image *image,ExceptionInfo* exception)
295 if (image->alpha_trait != BlendPixelTrait || image->colorspace != sRGBColorspace)
297 option=GetImageOption(image_info,"psd:alpha-unblend");
298 if (IsStringFalse(option) != MagickFalse)
301 #if defined(MAGICKCORE_OPENMP_SUPPORT)
302 #pragma omp parallel for schedule(static,4) shared(status) \
303 magick_threads(image,image,image->rows,1)
305 for (y=0; y < (ssize_t) image->rows; y++)
313 if (status == MagickFalse)
315 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
316 if (q == (Quantum *) NULL)
321 for (x=0; x < (ssize_t) image->columns; x++)
329 gamma=QuantumScale*GetPixelAlpha(image, q);
330 if (gamma != 0.0 && gamma != 1.0)
332 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
334 PixelChannel channel=GetPixelChannelChannel(image,i);
335 if (channel != AlphaPixelChannel)
336 q[i]=ClampToQuantum((q[i]-((1.0-gamma)*QuantumRange))/gamma);
339 q+=GetPixelChannels(image);
341 if (SyncAuthenticPixels(image,exception) == MagickFalse)
348 static inline CompressionType ConvertPSDCompression(
349 PSDCompressionType compression)
354 return RLECompression;
355 case ZipWithPrediction:
356 case ZipWithoutPrediction:
357 return ZipCompression;
359 return NoCompression;
363 static MagickBooleanType ApplyPSDLayerOpacity(Image *image,Quantum opacity,
364 MagickBooleanType revert,ExceptionInfo *exception)
372 if (image->debug != MagickFalse)
373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
374 " applying layer opacity %.20g", (double) opacity);
375 if (opacity == OpaqueAlpha)
377 image->alpha_trait=BlendPixelTrait;
379 #if defined(MAGICKCORE_OPENMP_SUPPORT)
380 #pragma omp parallel for schedule(static,4) shared(status) \
381 magick_threads(image,image,image->rows,1)
383 for (y=0; y < (ssize_t) image->rows; y++)
391 if (status == MagickFalse)
393 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
394 if (q == (Quantum *) NULL)
399 for (x=0; x < (ssize_t) image->columns; x++)
401 if (revert == MagickFalse)
402 SetPixelAlpha(image,(Quantum) (QuantumScale*(GetPixelAlpha(image,q))*
404 else if (opacity > 0)
405 SetPixelAlpha(image,(Quantum) (QuantumRange*(GetPixelAlpha(image,q)/
406 (MagickRealType) opacity)),q);
407 q+=GetPixelChannels(image);
409 if (SyncAuthenticPixels(image,exception) == MagickFalse)
416 static MagickBooleanType ApplyPSDOpacityMask(Image *image,const Image *mask,
417 Quantum background,MagickBooleanType revert,ExceptionInfo *exception)
431 if (image->debug != MagickFalse)
432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
433 " applying opacity mask");
434 complete_mask=CloneImage(image,image->columns,image->rows,MagickTrue,
436 complete_mask->alpha_trait=BlendPixelTrait;
437 GetPixelInfo(complete_mask,&color);
438 color.red=background;
439 SetImageColor(complete_mask,&color,exception);
440 status=CompositeImage(complete_mask,mask,OverCompositeOp,MagickTrue,
441 mask->page.x-image->page.x,mask->page.y-image->page.y,exception);
442 if (status == MagickFalse)
444 complete_mask=DestroyImage(complete_mask);
447 image->alpha_trait=BlendPixelTrait;
448 #if defined(MAGICKCORE_OPENMP_SUPPORT)
449 #pragma omp parallel for schedule(static,4) shared(status) \
450 magick_threads(image,image,image->rows,1)
452 for (y=0; y < (ssize_t) image->rows; y++)
463 if (status == MagickFalse)
465 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
466 p=GetAuthenticPixels(complete_mask,0,y,complete_mask->columns,1,exception);
467 if ((q == (Quantum *) NULL) || (p == (Quantum *) NULL))
472 for (x=0; x < (ssize_t) image->columns; x++)
478 alpha=GetPixelAlpha(image,q);
479 intensity=GetPixelIntensity(complete_mask,p);
480 if (revert == MagickFalse)
481 SetPixelAlpha(image,ClampToQuantum(intensity*(QuantumScale*alpha)),q);
482 else if (intensity > 0)
483 SetPixelAlpha(image,ClampToQuantum((alpha/intensity)*QuantumRange),q);
484 q+=GetPixelChannels(image);
485 p+=GetPixelChannels(complete_mask);
487 if (SyncAuthenticPixels(image,exception) == MagickFalse)
490 complete_mask=DestroyImage(complete_mask);
494 static void PreservePSDOpacityMask(Image *image,LayerInfo* layer_info,
495 ExceptionInfo *exception)
506 if (image->debug != MagickFalse)
507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
508 " preserving opacity mask");
509 random_info=AcquireRandomInfo();
510 key_info=GetRandomKey(random_info,2+1);
511 key=(char *) GetStringInfoDatum(key_info);
512 key[8]=layer_info->mask.background;
514 layer_info->mask.image->page.x+=layer_info->page.x;
515 layer_info->mask.image->page.y+=layer_info->page.y;
516 (void) SetImageRegistry(ImageRegistryType,(const char *) key,
517 layer_info->mask.image,exception);
518 (void) SetImageArtifact(layer_info->image,"psd:opacity-mask",
520 key_info=DestroyStringInfo(key_info);
521 random_info=DestroyRandomInfo(random_info);
524 static ssize_t DecodePSDPixels(const size_t number_compact_pixels,
525 const unsigned char *compact_pixels,const ssize_t depth,
526 const size_t number_pixels,unsigned char *pixels)
528 #define CheckNumberCompactPixels \
533 #define CheckNumberPixels(count) \
534 if (((ssize_t) i + count) > (ssize_t) number_pixels) \
551 packets=(ssize_t) number_compact_pixels;
552 for (i=0; (packets > 1) && (i < (ssize_t) number_pixels); )
555 length=(size_t) (*compact_pixels++);
561 CheckNumberCompactPixels;
562 pixel=(*compact_pixels++);
563 for (j=0; j < (ssize_t) length; j++)
569 CheckNumberPixels(8);
570 *pixels++=(pixel >> 7) & 0x01 ? 0U : 255U;
571 *pixels++=(pixel >> 6) & 0x01 ? 0U : 255U;
572 *pixels++=(pixel >> 5) & 0x01 ? 0U : 255U;
573 *pixels++=(pixel >> 4) & 0x01 ? 0U : 255U;
574 *pixels++=(pixel >> 3) & 0x01 ? 0U : 255U;
575 *pixels++=(pixel >> 2) & 0x01 ? 0U : 255U;
576 *pixels++=(pixel >> 1) & 0x01 ? 0U : 255U;
577 *pixels++=(pixel >> 0) & 0x01 ? 0U : 255U;
582 CheckNumberPixels(4);
583 *pixels++=(unsigned char) ((pixel >> 6) & 0x03);
584 *pixels++=(unsigned char) ((pixel >> 4) & 0x03);
585 *pixels++=(unsigned char) ((pixel >> 2) & 0x03);
586 *pixels++=(unsigned char) ((pixel & 0x03) & 0x03);
591 CheckNumberPixels(2);
592 *pixels++=(unsigned char) ((pixel >> 4) & 0xff);
593 *pixels++=(unsigned char) ((pixel & 0x0f) & 0xff);
598 CheckNumberPixels(1);
599 *pixels++=(unsigned char) pixel;
607 for (j=0; j < (ssize_t) length; j++)
609 CheckNumberCompactPixels;
614 CheckNumberPixels(8);
615 *pixels++=(*compact_pixels >> 7) & 0x01 ? 0U : 255U;
616 *pixels++=(*compact_pixels >> 6) & 0x01 ? 0U : 255U;
617 *pixels++=(*compact_pixels >> 5) & 0x01 ? 0U : 255U;
618 *pixels++=(*compact_pixels >> 4) & 0x01 ? 0U : 255U;
619 *pixels++=(*compact_pixels >> 3) & 0x01 ? 0U : 255U;
620 *pixels++=(*compact_pixels >> 2) & 0x01 ? 0U : 255U;
621 *pixels++=(*compact_pixels >> 1) & 0x01 ? 0U : 255U;
622 *pixels++=(*compact_pixels >> 0) & 0x01 ? 0U : 255U;
627 CheckNumberPixels(4);
628 *pixels++=(*compact_pixels >> 6) & 0x03;
629 *pixels++=(*compact_pixels >> 4) & 0x03;
630 *pixels++=(*compact_pixels >> 2) & 0x03;
631 *pixels++=(*compact_pixels & 0x03) & 0x03;
636 CheckNumberPixels(2);
637 *pixels++=(*compact_pixels >> 4) & 0xff;
638 *pixels++=(*compact_pixels & 0x0f) & 0xff;
643 CheckNumberPixels(1);
644 *pixels++=(*compact_pixels);
654 static inline LayerInfo *DestroyLayerInfo(LayerInfo *layer_info,
655 const ssize_t number_layers)
660 for (i=0; i<number_layers; i++)
662 if (layer_info[i].image != (Image *) NULL)
663 layer_info[i].image=DestroyImage(layer_info[i].image);
664 if (layer_info[i].mask.image != (Image *) NULL)
665 layer_info[i].mask.image=DestroyImage(layer_info[i].mask.image);
666 if (layer_info[i].info != (StringInfo *) NULL)
667 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
670 return (LayerInfo *) RelinquishMagickMemory(layer_info);
673 static inline size_t GetPSDPacketSize(Image *image)
675 if (image->storage_class == PseudoClass)
677 if (image->colors > 256)
679 else if (image->depth > 8)
683 if (image->depth > 8)
689 static inline MagickSizeType GetPSDSize(const PSDInfo *psd_info,Image *image)
691 if (psd_info->version == 1)
692 return((MagickSizeType) ReadBlobLong(image));
693 return((MagickSizeType) ReadBlobLongLong(image));
696 static inline size_t GetPSDRowSize(Image *image)
698 if (image->depth == 1)
699 return(((image->columns+7)/8)*GetPSDPacketSize(image));
701 return(image->columns*GetPSDPacketSize(image));
704 static const char *ModeToString(PSDImageType type)
708 case BitmapMode: return "Bitmap";
709 case GrayscaleMode: return "Grayscale";
710 case IndexedMode: return "Indexed";
711 case RGBMode: return "RGB";
712 case CMYKMode: return "CMYK";
713 case MultichannelMode: return "Multichannel";
714 case DuotoneMode: return "Duotone";
715 case LabMode: return "L*A*B";
716 default: return "unknown";
720 static MagickBooleanType NegateCMYK(Image *image,ExceptionInfo *exception)
728 channel_mask=SetImageChannelMask(image,(ChannelType)(AllChannels &~
730 status=NegateImage(image,MagickFalse,exception);
731 (void) SetImageChannelMask(image,channel_mask);
735 static void ParseImageResourceBlocks(Image *image,
736 const unsigned char *blocks,size_t length,
737 MagickBooleanType *has_merged_image,ExceptionInfo *exception)
757 profile=BlobToStringInfo((const unsigned char *) NULL,length);
758 SetStringInfoDatum(profile,blocks);
759 (void) SetImageProfile(image,"8bim",profile,exception);
760 profile=DestroyStringInfo(profile);
761 for (p=blocks; (p >= blocks) && (p < (blocks+length-7)); )
763 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
766 p=PushShortPixel(MSBEndian,p,&id);
767 p=PushCharPixel(p,&name_length);
768 if ((name_length % 2) == 0)
771 if (p > (blocks+length-4))
773 p=PushLongPixel(MSBEndian,p,&count);
774 if ((p+count) > (blocks+length))
781 value[MagickPathExtent];
791 p=PushShortPixel(MSBEndian,p,&resolution);
792 image->resolution.x=(double) resolution;
793 (void) FormatLocaleString(value,MagickPathExtent,"%g",
794 image->resolution.x);
795 (void) SetImageProperty(image,"tiff:XResolution",value,exception);
796 p=PushShortPixel(MSBEndian,p,&short_sans);
797 p=PushShortPixel(MSBEndian,p,&short_sans);
798 p=PushShortPixel(MSBEndian,p,&short_sans);
799 p=PushShortPixel(MSBEndian,p,&resolution);
800 image->resolution.y=(double) resolution;
801 (void) FormatLocaleString(value,MagickPathExtent,"%g",
802 image->resolution.y);
803 (void) SetImageProperty(image,"tiff:YResolution",value,exception);
804 p=PushShortPixel(MSBEndian,p,&short_sans);
805 p=PushShortPixel(MSBEndian,p,&short_sans);
806 p=PushShortPixel(MSBEndian,p,&short_sans);
807 image->units=PixelsPerInchResolution;
812 if ((count > 3) && (*(p+4) == 0))
813 *has_merged_image=MagickFalse;
823 if ((count & 0x01) != 0)
829 static CompositeOperator PSDBlendModeToCompositeOperator(const char *mode)
831 if (mode == (const char *) NULL)
832 return(OverCompositeOp);
833 if (LocaleNCompare(mode,"norm",4) == 0)
834 return(OverCompositeOp);
835 if (LocaleNCompare(mode,"mul ",4) == 0)
836 return(MultiplyCompositeOp);
837 if (LocaleNCompare(mode,"diss",4) == 0)
838 return(DissolveCompositeOp);
839 if (LocaleNCompare(mode,"diff",4) == 0)
840 return(DifferenceCompositeOp);
841 if (LocaleNCompare(mode,"dark",4) == 0)
842 return(DarkenCompositeOp);
843 if (LocaleNCompare(mode,"lite",4) == 0)
844 return(LightenCompositeOp);
845 if (LocaleNCompare(mode,"hue ",4) == 0)
846 return(HueCompositeOp);
847 if (LocaleNCompare(mode,"sat ",4) == 0)
848 return(SaturateCompositeOp);
849 if (LocaleNCompare(mode,"colr",4) == 0)
850 return(ColorizeCompositeOp);
851 if (LocaleNCompare(mode,"lum ",4) == 0)
852 return(LuminizeCompositeOp);
853 if (LocaleNCompare(mode,"scrn",4) == 0)
854 return(ScreenCompositeOp);
855 if (LocaleNCompare(mode,"over",4) == 0)
856 return(OverlayCompositeOp);
857 if (LocaleNCompare(mode,"hLit",4) == 0)
858 return(HardLightCompositeOp);
859 if (LocaleNCompare(mode,"sLit",4) == 0)
860 return(SoftLightCompositeOp);
861 if (LocaleNCompare(mode,"smud",4) == 0)
862 return(ExclusionCompositeOp);
863 if (LocaleNCompare(mode,"div ",4) == 0)
864 return(ColorDodgeCompositeOp);
865 if (LocaleNCompare(mode,"idiv",4) == 0)
866 return(ColorBurnCompositeOp);
867 if (LocaleNCompare(mode,"lbrn",4) == 0)
868 return(LinearBurnCompositeOp);
869 if (LocaleNCompare(mode,"lddg",4) == 0)
870 return(LinearDodgeCompositeOp);
871 if (LocaleNCompare(mode,"lLit",4) == 0)
872 return(LinearLightCompositeOp);
873 if (LocaleNCompare(mode,"vLit",4) == 0)
874 return(VividLightCompositeOp);
875 if (LocaleNCompare(mode,"pLit",4) == 0)
876 return(PinLightCompositeOp);
877 if (LocaleNCompare(mode,"hMix",4) == 0)
878 return(HardMixCompositeOp);
879 return(OverCompositeOp);
882 static inline void ReversePSDString(Image *image,char *p,size_t length)
887 if (image->endian == MSBEndian)
891 for(--q; p < q; ++p, --q)
899 static inline void SetPSDPixel(Image *image,const size_t channels,
900 const ssize_t type,const size_t packet_size,const Quantum pixel,Quantum *q,
901 ExceptionInfo *exception)
903 if (image->storage_class == PseudoClass)
910 if (packet_size == 1)
911 SetPixelIndex(image,ScaleQuantumToChar(pixel),q);
913 SetPixelIndex(image,ScaleQuantumToShort(pixel),q);
915 color=image->colormap+(ssize_t) ConstrainColormapIndex(image,
916 GetPixelIndex(image,q),exception);
917 if ((type == 0) && (channels > 1))
920 color->alpha=(MagickRealType) pixel;
921 SetPixelViaPixelInfo(image,color,q);
928 SetPixelAlpha(image,pixel,q);
934 SetPixelRed(image,pixel,q);
939 SetPixelGreen(image,pixel,q);
944 SetPixelBlue(image,pixel,q);
949 if (image->colorspace == CMYKColorspace)
950 SetPixelBlack(image,pixel,q);
952 if (image->alpha_trait != UndefinedPixelTrait)
953 SetPixelAlpha(image,pixel,q);
958 if ((IssRGBCompatibleColorspace(image->colorspace) != MagickFalse) &&
961 if (image->alpha_trait != UndefinedPixelTrait)
962 SetPixelAlpha(image,pixel,q);
968 static MagickBooleanType ReadPSDChannelPixels(Image *image,
969 const size_t channels,const size_t row,const ssize_t type,
970 const unsigned char *pixels,ExceptionInfo *exception)
975 register const unsigned char
991 q=GetAuthenticPixels(image,0,row,image->columns,1,exception);
992 if (q == (Quantum *) NULL)
994 packet_size=GetPSDPacketSize(image);
995 for (x=0; x < (ssize_t) image->columns; x++)
997 if (packet_size == 1)
998 pixel=ScaleCharToQuantum(*p++);
1001 p=PushShortPixel(MSBEndian,p,&nibble);
1002 pixel=ScaleShortToQuantum(nibble);
1004 if (image->depth > 1)
1006 SetPSDPixel(image,channels,type,packet_size,pixel,q,exception);
1007 q+=GetPixelChannels(image);
1015 number_bits=image->columns-x;
1016 if (number_bits > 8)
1018 for (bit = 0; bit < number_bits; bit++)
1020 SetPSDPixel(image,channels,type,packet_size,(((unsigned char) pixel)
1021 & (0x01 << (7-bit))) != 0 ? 0 : QuantumRange,q,exception);
1022 q+=GetPixelChannels(image);
1025 if (x != (ssize_t) image->columns)
1030 return(SyncAuthenticPixels(image,exception));
1033 static MagickBooleanType ReadPSDChannelRaw(Image *image,const size_t channels,
1034 const ssize_t type,ExceptionInfo *exception)
1049 if (image->debug != MagickFalse)
1050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1051 " layer data is RAW");
1053 row_size=GetPSDRowSize(image);
1054 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
1055 if (pixels == (unsigned char *) NULL)
1056 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1060 for (y=0; y < (ssize_t) image->rows; y++)
1064 count=ReadBlob(image,row_size,pixels);
1065 if (count != row_size)
1068 status=ReadPSDChannelPixels(image,channels,y,type,pixels,exception);
1069 if (status == MagickFalse)
1073 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1077 static inline MagickOffsetType *ReadPSDRLESizes(Image *image,
1078 const PSDInfo *psd_info,const size_t size)
1086 sizes=(MagickOffsetType *) AcquireQuantumMemory(size,sizeof(*sizes));
1087 if(sizes != (MagickOffsetType *) NULL)
1089 for (y=0; y < (ssize_t) size; y++)
1091 if (psd_info->version == 1)
1092 sizes[y]=(MagickOffsetType) ReadBlobShort(image);
1094 sizes[y]=(MagickOffsetType) ReadBlobLong(image);
1100 static MagickBooleanType ReadPSDChannelRLE(Image *image,const PSDInfo *psd_info,
1101 const ssize_t type,MagickOffsetType *sizes,ExceptionInfo *exception)
1118 if (image->debug != MagickFalse)
1119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1120 " layer data is RLE compressed");
1122 row_size=GetPSDRowSize(image);
1123 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
1124 if (pixels == (unsigned char *) NULL)
1125 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1129 for (y=0; y < (ssize_t) image->rows; y++)
1130 if ((MagickOffsetType) length < sizes[y])
1131 length=(size_t) sizes[y];
1133 if (length > row_size + 256) // arbitrary number
1135 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1136 ThrowBinaryException(ResourceLimitError,"InvalidLength",image->filename);
1139 compact_pixels=(unsigned char *) AcquireQuantumMemory(length,sizeof(*pixels));
1140 if (compact_pixels == (unsigned char *) NULL)
1142 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1143 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1147 (void) ResetMagickMemory(compact_pixels,0,length*sizeof(*compact_pixels));
1150 for (y=0; y < (ssize_t) image->rows; y++)
1154 count=ReadBlob(image,(size_t) sizes[y],compact_pixels);
1155 if (count != (ssize_t) sizes[y])
1158 count=DecodePSDPixels((size_t) sizes[y],compact_pixels,
1159 (ssize_t) (image->depth == 1 ? 123456 : image->depth),row_size,pixels);
1160 if (count != (ssize_t) row_size)
1163 status=ReadPSDChannelPixels(image,psd_info->channels,y,type,pixels,
1165 if (status == MagickFalse)
1169 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1170 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1174 #ifdef MAGICKCORE_ZLIB_DELEGATE
1175 static MagickBooleanType ReadPSDChannelZip(Image *image,const size_t channels,
1176 const ssize_t type,const PSDCompressionType compression,
1177 const size_t compact_size,ExceptionInfo *exception)
1182 register unsigned char
1201 if (image->debug != MagickFalse)
1202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1203 " layer data is ZIP compressed");
1205 compact_pixels=(unsigned char *) AcquireQuantumMemory(compact_size,
1206 sizeof(*compact_pixels));
1207 if (compact_pixels == (unsigned char *) NULL)
1208 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1211 packet_size=GetPSDPacketSize(image);
1212 row_size=image->columns*packet_size;
1213 count=image->rows*row_size;
1215 pixels=(unsigned char *) AcquireQuantumMemory(count,sizeof(*pixels));
1216 if (pixels == (unsigned char *) NULL)
1218 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1219 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1222 if (ReadBlob(image,compact_size,compact_pixels) != (ssize_t) compact_size)
1224 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1225 ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile",
1229 ResetMagickMemory(&stream,0,sizeof(stream));
1230 stream.data_type=Z_BINARY;
1231 stream.next_in=(Bytef *)compact_pixels;
1232 stream.avail_in=(uInt) compact_size;
1233 stream.next_out=(Bytef *)pixels;
1234 stream.avail_out=(uInt) count;
1236 if (inflateInit(&stream) == Z_OK)
1241 while (stream.avail_out > 0)
1243 ret=inflate(&stream,Z_SYNC_FLUSH);
1244 if ((ret != Z_OK) && (ret != Z_STREAM_END))
1246 (void) inflateEnd(&stream);
1247 compact_pixels=(unsigned char *) RelinquishMagickMemory(
1249 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1250 return(MagickFalse);
1253 (void) inflateEnd(&stream);
1256 if (compression == ZipWithPrediction)
1261 length=image->columns;
1264 if (packet_size == 2)
1266 p[2]+=p[0]+((p[1]+p[3]) >> 8);
1280 for (y=0; y < (ssize_t) image->rows; y++)
1282 status=ReadPSDChannelPixels(image,channels,y,type,p,exception);
1283 if (status == MagickFalse)
1289 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1290 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1295 static MagickBooleanType ReadPSDChannel(Image *image,
1296 const ImageInfo *image_info,const PSDInfo *psd_info,LayerInfo* layer_info,
1297 const size_t channel,const PSDCompressionType compression,
1298 ExceptionInfo *exception)
1310 channel_image=image;
1311 mask=(Image *) NULL;
1312 if (layer_info->channel_info[channel].type < -1)
1317 Ignore mask that is not a user supplied layer mask, if the mask is
1318 disabled or if the flags have unsupported values.
1320 option=GetImageOption(image_info,"psd:preserve-opacity-mask");
1321 if ((layer_info->channel_info[channel].type != -2) ||
1322 (layer_info->mask.flags > 2) || ((layer_info->mask.flags & 0x02) &&
1323 (IsStringTrue(option) == MagickFalse)))
1325 SeekBlob(image,layer_info->channel_info[channel].size-2,SEEK_CUR);
1328 mask=CloneImage(image,layer_info->mask.page.width,
1329 layer_info->mask.page.height,MagickFalse,exception);
1330 if (mask != (Image *) NULL)
1332 SetImageType(mask,GrayscaleType,exception);
1337 offset=TellBlob(image);
1342 status=ReadPSDChannelRaw(channel_image,psd_info->channels,
1343 layer_info->channel_info[channel].type,exception);
1350 sizes=ReadPSDRLESizes(channel_image,psd_info,channel_image->rows);
1351 if (sizes == (MagickOffsetType *) NULL)
1352 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1354 status=ReadPSDChannelRLE(channel_image,psd_info,
1355 layer_info->channel_info[channel].type,sizes,exception);
1356 sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
1359 case ZipWithPrediction:
1360 case ZipWithoutPrediction:
1361 #ifdef MAGICKCORE_ZLIB_DELEGATE
1362 status=ReadPSDChannelZip(channel_image,layer_info->channels,
1363 layer_info->channel_info[channel].type,compression,
1364 layer_info->channel_info[channel].size-2,exception);
1366 (void) ThrowMagickException(exception,GetMagickModule(),
1367 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
1368 "'%s' (ZLIB)",image->filename);
1372 (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
1373 "CompressionNotSupported","'%.20g'",(double) compression);
1377 SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
1378 if (status == MagickFalse)
1380 if (mask != (Image *) NULL)
1382 ThrowBinaryException(CoderError,"UnableToDecompressImage",
1385 layer_info->mask.image=mask;
1389 static MagickBooleanType ReadPSDLayer(Image *image,const ImageInfo *image_info,
1390 const PSDInfo *psd_info,LayerInfo* layer_info,ExceptionInfo *exception)
1393 message[MagickPathExtent];
1404 if (image->debug != MagickFalse)
1405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1406 " setting up new layer image");
1407 if (psd_info->mode != IndexedMode)
1408 (void) SetImageBackgroundColor(layer_info->image,exception);
1409 layer_info->image->compose=PSDBlendModeToCompositeOperator(
1410 layer_info->blendkey);
1411 if (layer_info->visible == MagickFalse)
1412 layer_info->image->compose=NoCompositeOp;
1413 if (psd_info->mode == CMYKMode)
1414 SetImageColorspace(layer_info->image,CMYKColorspace,exception);
1415 else if ((psd_info->mode == BitmapMode) || (psd_info->mode == DuotoneMode) ||
1416 (psd_info->mode == GrayscaleMode))
1417 SetImageColorspace(layer_info->image,GRAYColorspace,exception);
1419 Set up some hidden attributes for folks that need them.
1421 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
1422 (double) layer_info->page.x);
1423 (void) SetImageArtifact(layer_info->image,"psd:layer.x",message);
1424 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
1425 (double) layer_info->page.y);
1426 (void) SetImageArtifact(layer_info->image,"psd:layer.y",message);
1427 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",(double)
1428 layer_info->opacity);
1429 (void) SetImageArtifact(layer_info->image,"psd:layer.opacity",message);
1430 (void) SetImageProperty(layer_info->image,"label",(char *) layer_info->name,
1434 for (j=0; j < (ssize_t) layer_info->channels; j++)
1436 if (image->debug != MagickFalse)
1437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1438 " reading data for channel %.20g",(double) j);
1440 compression=(PSDCompressionType) ReadBlobShort(layer_info->image);
1441 layer_info->image->compression=ConvertPSDCompression(compression);
1442 if (layer_info->channel_info[j].type == -1)
1443 layer_info->image->alpha_trait=BlendPixelTrait;
1445 status=ReadPSDChannel(layer_info->image,image_info,psd_info,layer_info,j,
1446 compression,exception);
1448 if (status == MagickFalse)
1452 if (status != MagickFalse)
1453 status=ApplyPSDLayerOpacity(layer_info->image,layer_info->opacity,
1454 MagickFalse,exception);
1456 if ((status != MagickFalse) &&
1457 (layer_info->image->colorspace == CMYKColorspace))
1458 status=NegateCMYK(layer_info->image,exception);
1460 if ((status != MagickFalse) && (layer_info->mask.image != (Image *) NULL))
1465 layer_info->mask.image->page.x=layer_info->mask.page.x;
1466 layer_info->mask.image->page.y=layer_info->mask.page.y;
1467 /* Do not composite the mask when it is disabled */
1468 if ((layer_info->mask.flags & 0x02) == 0x02)
1469 layer_info->mask.image->compose=NoCompositeOp;
1471 status=ApplyPSDOpacityMask(layer_info->image,layer_info->mask.image,
1472 layer_info->mask.background == 0 ? 0 : QuantumRange,MagickFalse,
1474 option=GetImageOption(image_info,"psd:preserve-opacity-mask");
1475 if (IsStringTrue(option) != MagickFalse)
1476 PreservePSDOpacityMask(image,layer_info,exception);
1477 layer_info->mask.image=DestroyImage(layer_info->mask.image);
1483 static MagickBooleanType ReadPSDLayersInternal(Image *image,
1484 const ImageInfo *image_info,const PSDInfo *psd_info,
1485 const MagickBooleanType skip_layers,ExceptionInfo *exception)
1507 size=GetPSDSize(psd_info,image);
1511 Skip layers & masks.
1513 (void) ReadBlobLong(image);
1514 count=ReadBlob(image,4,(unsigned char *) type);
1515 ReversePSDString(image,type,4);
1517 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1521 count=ReadBlob(image,4,(unsigned char *) type);
1522 ReversePSDString(image,type,4);
1523 if ((count != 0) && (LocaleNCompare(type,"Lr16",4) == 0))
1524 size=GetPSDSize(psd_info,image);
1532 layer_info=(LayerInfo *) NULL;
1533 number_layers=(short) ReadBlobShort(image);
1535 if (number_layers < 0)
1538 The first alpha channel in the merged result contains the
1539 transparency data for the merged result.
1541 number_layers=MagickAbsoluteValue(number_layers);
1542 if (image->debug != MagickFalse)
1543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1544 " negative layer count corrected for");
1545 image->alpha_trait=BlendPixelTrait;
1549 We only need to know if the image has an alpha channel
1551 if (skip_layers != MagickFalse)
1554 if (image->debug != MagickFalse)
1555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1556 " image contains %.20g layers",(double) number_layers);
1558 if (number_layers == 0)
1559 ThrowBinaryException(CorruptImageError,"InvalidNumberOfLayers",
1562 layer_info=(LayerInfo *) AcquireQuantumMemory((size_t) number_layers,
1563 sizeof(*layer_info));
1564 if (layer_info == (LayerInfo *) NULL)
1566 if (image->debug != MagickFalse)
1567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1568 " allocation of LayerInfo failed");
1569 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1572 (void) ResetMagickMemory(layer_info,0,(size_t) number_layers*
1573 sizeof(*layer_info));
1575 for (i=0; i < number_layers; i++)
1581 if (image->debug != MagickFalse)
1582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1583 " reading layer #%.20g",(double) i+1);
1584 layer_info[i].page.y=ReadBlobSignedLong(image);
1585 layer_info[i].page.x=ReadBlobSignedLong(image);
1586 y=ReadBlobSignedLong(image);
1587 x=ReadBlobSignedLong(image);
1588 layer_info[i].page.width=(size_t) (x-layer_info[i].page.x);
1589 layer_info[i].page.height=(size_t) (y-layer_info[i].page.y);
1590 layer_info[i].channels=ReadBlobShort(image);
1591 if (layer_info[i].channels > MaxPSDChannels)
1593 layer_info=DestroyLayerInfo(layer_info,number_layers);
1594 ThrowBinaryException(CorruptImageError,"MaximumChannelsExceeded",
1597 if (image->debug != MagickFalse)
1598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1599 " offset(%.20g,%.20g), size(%.20g,%.20g), channels=%.20g",
1600 (double) layer_info[i].page.x,(double) layer_info[i].page.y,
1601 (double) layer_info[i].page.height,(double)
1602 layer_info[i].page.width,(double) layer_info[i].channels);
1603 for (j=0; j < (ssize_t) layer_info[i].channels; j++)
1605 layer_info[i].channel_info[j].type=(short) ReadBlobShort(image);
1606 layer_info[i].channel_info[j].size=(size_t) GetPSDSize(psd_info,
1608 if (image->debug != MagickFalse)
1609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1610 " channel[%.20g]: type=%.20g, size=%.20g",(double) j,
1611 (double) layer_info[i].channel_info[j].type,
1612 (double) layer_info[i].channel_info[j].size);
1614 count=ReadBlob(image,4,(unsigned char *) type);
1615 ReversePSDString(image,type,4);
1616 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1618 if (image->debug != MagickFalse)
1619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1620 " layer type was %.4s instead of 8BIM", type);
1621 layer_info=DestroyLayerInfo(layer_info,number_layers);
1622 ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
1625 count=ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey);
1626 ReversePSDString(image,layer_info[i].blendkey,4);
1627 layer_info[i].opacity=(Quantum) ScaleCharToQuantum((unsigned char)
1628 ReadBlobByte(image));
1629 layer_info[i].clipping=(unsigned char) ReadBlobByte(image);
1630 layer_info[i].flags=(unsigned char) ReadBlobByte(image);
1631 layer_info[i].visible=!(layer_info[i].flags & 0x02);
1632 if (image->debug != MagickFalse)
1633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1634 " blend=%.4s, opacity=%.20g, clipping=%s, flags=%d, visible=%s",
1635 layer_info[i].blendkey,(double) layer_info[i].opacity,
1636 layer_info[i].clipping ? "true" : "false",layer_info[i].flags,
1637 layer_info[i].visible ? "true" : "false");
1638 (void) ReadBlobByte(image); /* filler */
1640 size=ReadBlobLong(image);
1647 if (image->debug != MagickFalse)
1648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1649 " layer contains additional info");
1650 length=ReadBlobLong(image);
1651 combined_length=length+4;
1657 layer_info[i].mask.page.y=ReadBlobSignedLong(image);
1658 layer_info[i].mask.page.x=ReadBlobSignedLong(image);
1659 layer_info[i].mask.page.height=(size_t) (ReadBlobLong(image)-
1660 layer_info[i].mask.page.y);
1661 layer_info[i].mask.page.width=(size_t) (ReadBlobLong(image)-
1662 layer_info[i].mask.page.x);
1663 layer_info[i].mask.background=(unsigned char) ReadBlobByte(
1665 layer_info[i].mask.flags=(unsigned char) ReadBlobByte(image);
1666 if (!(layer_info[i].mask.flags & 0x01))
1668 layer_info[i].mask.page.y=layer_info[i].mask.page.y-
1669 layer_info[i].page.y;
1670 layer_info[i].mask.page.x=layer_info[i].mask.page.x-
1671 layer_info[i].page.x;
1673 if (image->debug != MagickFalse)
1674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1675 " layer mask: offset(%.20g,%.20g), size(%.20g,%.20g), length=%.20g",
1676 (double) layer_info[i].mask.page.x,(double)
1677 layer_info[i].mask.page.y,(double)
1678 layer_info[i].mask.page.width,(double)
1679 layer_info[i].mask.page.height,(double) ((MagickOffsetType)
1682 Skip over the rest of the layer mask information.
1684 if (DiscardBlobBytes(image,(MagickSizeType) (length-18)) == MagickFalse)
1686 layer_info=DestroyLayerInfo(layer_info,number_layers);
1687 ThrowBinaryException(CorruptImageError,
1688 "UnexpectedEndOfFile",image->filename);
1691 length=ReadBlobLong(image);
1692 combined_length+=length+4;
1696 Layer blending ranges info.
1698 if (image->debug != MagickFalse)
1699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1700 " layer blending ranges: length=%.20g",(double)
1701 ((MagickOffsetType) length));
1703 We read it, but don't use it...
1705 for (j=0; j < (ssize_t) length; j+=8)
1707 size_t blend_source=ReadBlobLong(image);
1708 size_t blend_dest=ReadBlobLong(image);
1709 if (image->debug != MagickFalse)
1710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1711 " source(%x), dest(%x)",(unsigned int)
1712 blend_source,(unsigned int) blend_dest);
1718 length=(MagickSizeType) (unsigned char) ReadBlobByte(image);
1719 combined_length+=length+1;
1721 (void) ReadBlob(image,(size_t) length++,layer_info[i].name);
1722 layer_info[i].name[length]='\0';
1723 if (image->debug != MagickFalse)
1724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1725 " layer name: %s",layer_info[i].name);
1726 if ((length % 4) != 0)
1728 length=4-(length % 4);
1729 combined_length+=length;
1730 /* Skip over the padding of the layer name */
1731 if (DiscardBlobBytes(image,length) == MagickFalse)
1733 layer_info=DestroyLayerInfo(layer_info,number_layers);
1734 ThrowBinaryException(CorruptImageError,
1735 "UnexpectedEndOfFile",image->filename);
1738 length=(MagickSizeType) size-combined_length;
1744 layer_info[i].info=AcquireStringInfo((const size_t) length);
1745 info=GetStringInfoDatum(layer_info[i].info);
1746 (void) ReadBlob(image,(const size_t) length,info);
1751 for (i=0; i < number_layers; i++)
1753 if ((layer_info[i].page.width == 0) ||
1754 (layer_info[i].page.height == 0))
1756 if (image->debug != MagickFalse)
1757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1758 " layer data is empty");
1759 if (layer_info[i].info != (StringInfo *) NULL)
1760 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
1765 Allocate layered image.
1767 layer_info[i].image=CloneImage(image,layer_info[i].page.width,
1768 layer_info[i].page.height,MagickFalse,exception);
1769 if (layer_info[i].image == (Image *) NULL)
1771 layer_info=DestroyLayerInfo(layer_info,number_layers);
1772 if (image->debug != MagickFalse)
1773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1774 " allocation of image for layer %.20g failed",(double) i);
1775 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1779 if (layer_info[i].info != (StringInfo *) NULL)
1781 (void) SetImageProfile(layer_info[i].image,"psd:additional-info",
1782 layer_info[i].info,exception);
1783 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
1787 if (image_info->ping == MagickFalse)
1789 for (i=0; i < number_layers; i++)
1791 if (layer_info[i].image == (Image *) NULL)
1793 for (j=0; j < layer_info[i].channels; j++)
1795 if (DiscardBlobBytes(image,(MagickSizeType)
1796 layer_info[i].channel_info[j].size) == MagickFalse)
1798 layer_info=DestroyLayerInfo(layer_info,number_layers);
1799 ThrowBinaryException(CorruptImageError,
1800 "UnexpectedEndOfFile",image->filename);
1806 if (image->debug != MagickFalse)
1807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1808 " reading data for layer %.20g",(double) i);
1810 status=ReadPSDLayer(image,image_info,psd_info,&layer_info[i],
1812 if (status == MagickFalse)
1815 status=SetImageProgress(image,LoadImagesTag,i,(MagickSizeType)
1817 if (status == MagickFalse)
1822 if (status != MagickFalse)
1824 for (i=0; i < number_layers; i++)
1826 if (layer_info[i].image == (Image *) NULL)
1828 for (j=i; j < number_layers - 1; j++)
1829 layer_info[j] = layer_info[j+1];
1835 if (number_layers > 0)
1837 for (i=0; i < number_layers; i++)
1840 layer_info[i].image->previous=layer_info[i-1].image;
1841 if (i < (number_layers-1))
1842 layer_info[i].image->next=layer_info[i+1].image;
1843 layer_info[i].image->page=layer_info[i].page;
1845 image->next=layer_info[0].image;
1846 layer_info[0].image->previous=image;
1848 layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
1851 layer_info=DestroyLayerInfo(layer_info,number_layers);
1857 ModuleExport MagickBooleanType ReadPSDLayers(Image *image,
1858 const ImageInfo *image_info,const PSDInfo *psd_info,
1859 const MagickBooleanType skip_layers,ExceptionInfo *exception)
1867 domain=CoderPolicyDomain;
1868 rights=ReadPolicyRights;
1869 if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
1870 return(MagickFalse);
1871 return(ReadPSDLayersInternal(image,image_info,psd_info,skip_layers,
1875 static MagickBooleanType ReadPSDMergedImage(const ImageInfo *image_info,
1876 Image *image,const PSDInfo *psd_info,ExceptionInfo *exception)
1890 compression=(PSDCompressionType) ReadBlobMSBShort(image);
1891 image->compression=ConvertPSDCompression(compression);
1893 if (compression != Raw && compression != RLE)
1895 (void) ThrowMagickException(exception,GetMagickModule(),
1896 TypeWarning,"CompressionNotSupported","'%.20g'",(double) compression);
1897 return(MagickFalse);
1900 sizes=(MagickOffsetType *) NULL;
1901 if (compression == RLE)
1903 sizes=ReadPSDRLESizes(image,psd_info,image->rows*psd_info->channels);
1904 if (sizes == (MagickOffsetType *) NULL)
1905 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1910 for (i=0; i < (ssize_t) psd_info->channels; i++)
1912 if (compression == RLE)
1913 status=ReadPSDChannelRLE(image,psd_info,i,sizes+(i*image->rows),
1916 status=ReadPSDChannelRaw(image,psd_info->channels,i,exception);
1918 if (status != MagickFalse)
1919 status=SetImageProgress(image,LoadImagesTag,i,psd_info->channels);
1921 if (status == MagickFalse)
1925 if ((status != MagickFalse) && (image->colorspace == CMYKColorspace))
1926 status=NegateCMYK(image,exception);
1928 if (status != MagickFalse)
1929 status=CorrectPSDAlphaBlend(image_info,image,exception);
1931 sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
1936 static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
1969 assert(image_info != (const ImageInfo *) NULL);
1970 assert(image_info->signature == MagickCoreSignature);
1971 if (image_info->debug != MagickFalse)
1972 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1973 image_info->filename);
1974 assert(exception != (ExceptionInfo *) NULL);
1975 assert(exception->signature == MagickCoreSignature);
1977 image=AcquireImage(image_info,exception);
1978 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1979 if (status == MagickFalse)
1981 image=DestroyImageList(image);
1982 return((Image *) NULL);
1987 image->endian=MSBEndian;
1988 count=ReadBlob(image,4,(unsigned char *) psd_info.signature);
1989 psd_info.version=ReadBlobMSBShort(image);
1990 if ((count == 0) || (LocaleNCompare(psd_info.signature,"8BPS",4) != 0) ||
1991 ((psd_info.version != 1) && (psd_info.version != 2)))
1992 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1993 (void) ReadBlob(image,6,psd_info.reserved);
1994 psd_info.channels=ReadBlobMSBShort(image);
1995 if (psd_info.channels > MaxPSDChannels)
1996 ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded");
1997 psd_info.rows=ReadBlobMSBLong(image);
1998 psd_info.columns=ReadBlobMSBLong(image);
1999 if ((psd_info.version == 1) && ((psd_info.rows > 30000) ||
2000 (psd_info.columns > 30000)))
2001 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2002 psd_info.depth=ReadBlobMSBShort(image);
2003 if ((psd_info.depth != 1) && (psd_info.depth != 8) && (psd_info.depth != 16))
2004 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2005 psd_info.mode=ReadBlobMSBShort(image);
2006 if (image->debug != MagickFalse)
2007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2008 " Image is %.20g x %.20g with channels=%.20g, depth=%.20g, mode=%s",
2009 (double) psd_info.columns,(double) psd_info.rows,(double)
2010 psd_info.channels,(double) psd_info.depth,ModeToString((PSDImageType)
2015 image->depth=psd_info.depth;
2016 image->columns=psd_info.columns;
2017 image->rows=psd_info.rows;
2018 status=SetImageExtent(image,image->columns,image->rows,exception);
2019 if (status == MagickFalse)
2020 return(DestroyImageList(image));
2021 if (SetImageBackgroundColor(image,exception) == MagickFalse)
2023 image=DestroyImageList(image);
2024 return((Image *) NULL);
2026 if (psd_info.mode == LabMode)
2027 SetImageColorspace(image,LabColorspace,exception);
2028 if (psd_info.mode == CMYKMode)
2030 SetImageColorspace(image,CMYKColorspace,exception);
2031 if (psd_info.channels > 4)
2032 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2034 else if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) ||
2035 (psd_info.mode == DuotoneMode))
2037 status=AcquireImageColormap(image,psd_info.depth != 16 ? 256 : 65536,
2039 if (status == MagickFalse)
2040 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2041 if (image->debug != MagickFalse)
2042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2043 " Image colormap allocated");
2044 SetImageColorspace(image,GRAYColorspace,exception);
2045 if (psd_info.channels > 1)
2046 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2049 if (psd_info.channels > 3)
2050 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2052 Read PSD raster colormap only present for indexed and duotone images.
2054 length=ReadBlobMSBLong(image);
2057 if (image->debug != MagickFalse)
2058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2059 " reading colormap");
2060 if (psd_info.mode == DuotoneMode)
2063 Duotone image data; the format of this data is undocumented.
2065 data=(unsigned char *) AcquireQuantumMemory((size_t) length,
2067 if (data == (unsigned char *) NULL)
2068 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2069 (void) ReadBlob(image,(size_t) length,data);
2070 data=(unsigned char *) RelinquishMagickMemory(data);
2078 Read PSD raster colormap.
2080 number_colors=length/3;
2081 if (number_colors > 65536)
2082 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2083 if (AcquireImageColormap(image,number_colors,exception) == MagickFalse)
2084 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2085 for (i=0; i < (ssize_t) image->colors; i++)
2086 image->colormap[i].red=ScaleCharToQuantum((unsigned char)
2087 ReadBlobByte(image));
2088 for (i=0; i < (ssize_t) image->colors; i++)
2089 image->colormap[i].green=ScaleCharToQuantum((unsigned char)
2090 ReadBlobByte(image));
2091 for (i=0; i < (ssize_t) image->colors; i++)
2092 image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
2093 ReadBlobByte(image));
2094 image->alpha_trait=UndefinedPixelTrait;
2097 if ((image->depth == 1) && (image->storage_class != PseudoClass))
2098 ThrowReaderException(CorruptImageError, "ImproperImageHeader");
2099 has_merged_image=MagickTrue;
2100 length=ReadBlobMSBLong(image);
2107 Image resources block.
2109 if (image->debug != MagickFalse)
2110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2111 " reading image resource blocks - %.20g bytes",(double)
2112 ((MagickOffsetType) length));
2113 if (length > GetBlobSize(image))
2114 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
2115 blocks=(unsigned char *) AcquireQuantumMemory((size_t) length,
2117 if (blocks == (unsigned char *) NULL)
2118 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2119 count=ReadBlob(image,(size_t) length,blocks);
2120 if ((count != (ssize_t) length) || (length < 4) ||
2121 (LocaleNCompare((char *) blocks,"8BIM",4) != 0))
2123 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
2124 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2126 ParseImageResourceBlocks(image,blocks,(size_t) length,&has_merged_image,
2128 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
2131 Layer and mask block.
2133 length=GetPSDSize(&psd_info,image);
2136 length=ReadBlobMSBLong(image);
2137 length=ReadBlobMSBLong(image);
2139 offset=TellBlob(image);
2140 skip_layers=MagickFalse;
2141 if ((image_info->number_scenes == 1) && (image_info->scene == 0) &&
2142 (has_merged_image != MagickFalse))
2144 if (image->debug != MagickFalse)
2145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2146 " read composite only");
2147 skip_layers=MagickTrue;
2151 if (image->debug != MagickFalse)
2152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2153 " image has no layers");
2157 if (ReadPSDLayersInternal(image,image_info,&psd_info,skip_layers,
2158 exception) != MagickTrue)
2160 (void) CloseBlob(image);
2161 image=DestroyImageList(image);
2162 return((Image *) NULL);
2166 Skip the rest of the layer and mask information.
2168 SeekBlob(image,offset+length,SEEK_SET);
2171 If we are only "pinging" the image, then we're done - so return.
2173 if (image_info->ping != MagickFalse)
2175 (void) CloseBlob(image);
2176 return(GetFirstImageInList(image));
2179 Read the precombined layer, present for PSD < 4 compatibility.
2181 if (image->debug != MagickFalse)
2182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2183 " reading the precombined layer");
2184 if ((has_merged_image != MagickFalse) || (GetImageListLength(image) == 1))
2185 has_merged_image=(MagickBooleanType) ReadPSDMergedImage(image_info,image,
2186 &psd_info,exception);
2187 if ((has_merged_image == MagickFalse) && (GetImageListLength(image) == 1) &&
2190 SeekBlob(image,offset,SEEK_SET);
2191 status=ReadPSDLayersInternal(image,image_info,&psd_info,MagickFalse,
2193 if (status != MagickTrue)
2195 (void) CloseBlob(image);
2196 image=DestroyImageList(image);
2197 return((Image *) NULL);
2200 if (has_merged_image == MagickFalse)
2205 if (GetImageListLength(image) == 1)
2206 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
2207 SetImageAlphaChannel(image,TransparentAlphaChannel,exception);
2208 image->background_color.alpha=TransparentAlpha;
2209 image->background_color.alpha_trait=BlendPixelTrait;
2210 merged=MergeImageLayers(image,FlattenLayer,exception);
2211 ReplaceImageInList(&image,merged);
2213 (void) CloseBlob(image);
2214 return(GetFirstImageInList(image));
2218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2222 % R e g i s t e r P S D I m a g e %
2226 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2228 % RegisterPSDImage() adds properties for the PSD image format to
2229 % the list of supported formats. The properties include the image format
2230 % tag, a method to read and/or write the format, whether the format
2231 % supports the saving of more than one frame to the same file or blob,
2232 % whether the format supports native in-memory I/O, and a brief
2233 % description of the format.
2235 % The format of the RegisterPSDImage method is:
2237 % size_t RegisterPSDImage(void)
2240 ModuleExport size_t RegisterPSDImage(void)
2245 entry=AcquireMagickInfo("PSD","PSB","Adobe Large Document Format");
2246 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2247 entry->encoder=(EncodeImageHandler *) WritePSDImage;
2248 entry->magick=(IsImageFormatHandler *) IsPSD;
2249 entry->flags|=CoderDecoderSeekableStreamFlag;
2250 entry->flags|=CoderEncoderSeekableStreamFlag;
2251 (void) RegisterMagickInfo(entry);
2252 entry=AcquireMagickInfo("PSD","PSD","Adobe Photoshop bitmap");
2253 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2254 entry->encoder=(EncodeImageHandler *) WritePSDImage;
2255 entry->magick=(IsImageFormatHandler *) IsPSD;
2256 entry->flags|=CoderDecoderSeekableStreamFlag;
2257 entry->flags|=CoderEncoderSeekableStreamFlag;
2258 (void) RegisterMagickInfo(entry);
2259 return(MagickImageCoderSignature);
2263 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2267 % U n r e g i s t e r P S D I m a g e %
2271 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2273 % UnregisterPSDImage() removes format registrations made by the
2274 % PSD module from the list of supported formats.
2276 % The format of the UnregisterPSDImage method is:
2278 % UnregisterPSDImage(void)
2281 ModuleExport void UnregisterPSDImage(void)
2283 (void) UnregisterMagickInfo("PSB");
2284 (void) UnregisterMagickInfo("PSD");
2288 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2292 % W r i t e P S D I m a g e %
2296 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2298 % WritePSDImage() writes an image in the Adobe Photoshop encoded image format.
2300 % The format of the WritePSDImage method is:
2302 % MagickBooleanType WritePSDImage(const ImageInfo *image_info,Image *image,
2303 % ExceptionInfo *exception)
2305 % A description of each parameter follows.
2307 % o image_info: the image info.
2309 % o image: The image.
2311 % o exception: return any errors or warnings in this structure.
2315 static inline ssize_t SetPSDOffset(const PSDInfo *psd_info,Image *image,
2316 const size_t offset)
2318 if (psd_info->version == 1)
2319 return(WriteBlobMSBShort(image,(unsigned short) offset));
2320 return(WriteBlobMSBLong(image,(unsigned short) offset));
2323 static inline ssize_t WritePSDOffset(const PSDInfo *psd_info,Image *image,
2324 const MagickSizeType size,const MagickSizeType offset)
2332 current_offset=TellBlob(image);
2333 SeekBlob(image,offset,SEEK_SET);
2334 if (psd_info->version == 1)
2335 result=WriteBlobMSBShort(image,(unsigned short) size);
2337 result=(WriteBlobMSBLong(image,(unsigned short) size));
2338 SeekBlob(image,current_offset,SEEK_SET);
2342 static inline ssize_t SetPSDSize(const PSDInfo *psd_info,Image *image,
2343 const MagickSizeType size)
2345 if (psd_info->version == 1)
2346 return(WriteBlobMSBLong(image,(unsigned int) size));
2347 return(WriteBlobMSBLongLong(image,size));
2350 static inline ssize_t WritePSDSize(const PSDInfo *psd_info,Image *image,
2351 const MagickSizeType size,const MagickSizeType offset)
2359 current_offset=TellBlob(image);
2360 SeekBlob(image,offset,SEEK_SET);
2361 if (psd_info->version == 1)
2362 result=WriteBlobMSBLong(image,(unsigned int) size);
2364 result=WriteBlobMSBLongLong(image,size);
2365 SeekBlob(image,current_offset,SEEK_SET);
2369 static size_t PSDPackbitsEncodeImage(Image *image,const size_t length,
2370 const unsigned char *pixels,unsigned char *compact_pixels,
2371 ExceptionInfo *exception)
2380 register unsigned char
2387 Compress pixels with Packbits encoding.
2389 assert(image != (Image *) NULL);
2390 assert(image->signature == MagickCoreSignature);
2391 if (image->debug != MagickFalse)
2392 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2393 assert(pixels != (unsigned char *) NULL);
2394 assert(compact_pixels != (unsigned char *) NULL);
2395 packbits=(unsigned char *) AcquireQuantumMemory(128UL,sizeof(*packbits));
2396 if (packbits == (unsigned char *) NULL)
2397 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2400 for (i=(ssize_t) length; i != 0; )
2407 *q++=(unsigned char) 0;
2414 *q++=(unsigned char) 1;
2422 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2424 *q++=(unsigned char) ((256-3)+1);
2428 *q++=(unsigned char) 2;
2436 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2442 while (((ssize_t) count < i) && (*pixels == *(pixels+count)))
2449 *q++=(unsigned char) ((256-count)+1);
2458 while ((*(pixels+count) != *(pixels+count+1)) ||
2459 (*(pixels+count+1) != *(pixels+count+2)))
2461 packbits[count+1]=pixels[count];
2463 if (((ssize_t) count >= (i-3)) || (count >= 127))
2467 *packbits=(unsigned char) (count-1);
2468 for (j=0; j <= (ssize_t) count; j++)
2475 *q++=(unsigned char) 128; /* EOD marker */
2476 packbits=(unsigned char *) RelinquishMagickMemory(packbits);
2477 return((size_t) (q-compact_pixels));
2480 static size_t WriteCompressionStart(const PSDInfo *psd_info,Image *image,
2481 const Image *next_image,const ssize_t channels)
2490 if (next_image->compression == RLECompression)
2492 length=WriteBlobMSBShort(image,RLE);
2493 for (i=0; i < channels; i++)
2494 for (y=0; y < (ssize_t) next_image->rows; y++)
2495 length+=SetPSDOffset(psd_info,image,0);
2497 #ifdef MAGICKCORE_ZLIB_DELEGATE
2498 else if (next_image->compression == ZipCompression)
2499 length=WriteBlobMSBShort(image,ZipWithoutPrediction);
2502 length=WriteBlobMSBShort(image,Raw);
2506 static size_t WritePSDChannel(const PSDInfo *psd_info,
2507 const ImageInfo *image_info,Image *image,Image *next_image,
2508 const QuantumType quantum_type, unsigned char *compact_pixels,
2509 MagickOffsetType size_offset,const MagickBooleanType separate,
2510 ExceptionInfo *exception)
2521 register const Quantum
2534 #ifdef MAGICKCORE_ZLIB_DELEGATE
2548 compressed_pixels=(unsigned char *) NULL;
2552 if (separate != MagickFalse)
2554 size_offset=TellBlob(image)+2;
2555 count+=WriteCompressionStart(psd_info,image,next_image,1);
2557 if (next_image->depth > 8)
2558 next_image->depth=16;
2559 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
2560 MagickTrue : MagickFalse;
2561 quantum_info=AcquireQuantumInfo(image_info,next_image);
2562 if (quantum_info == (QuantumInfo *) NULL)
2564 pixels=(unsigned char *) GetQuantumPixels(quantum_info);
2565 #ifdef MAGICKCORE_ZLIB_DELEGATE
2566 if (next_image->compression == ZipCompression)
2568 compressed_pixels=(unsigned char *) AcquireQuantumMemory(CHUNK,
2569 sizeof(*compressed_pixels));
2570 if (compressed_pixels == (unsigned char *) NULL)
2572 quantum_info=DestroyQuantumInfo(quantum_info);
2575 ResetMagickMemory(&stream,0,sizeof(stream));
2576 stream.data_type=Z_BINARY;
2577 level=Z_DEFAULT_COMPRESSION;
2578 if ((image_info->quality > 0 && image_info->quality < 10))
2579 level=(int) image_info->quality;
2580 if (deflateInit(&stream,level) != Z_OK)
2582 quantum_info=DestroyQuantumInfo(quantum_info);
2587 for (y=0; y < (ssize_t) next_image->rows; y++)
2589 p=GetVirtualPixels(next_image,0,y,next_image->columns,1,exception);
2590 if (p == (const Quantum *) NULL)
2592 length=ExportQuantumPixels(next_image,(CacheView *) NULL,quantum_info,
2593 quantum_type,pixels,exception);
2594 if (monochrome != MagickFalse)
2595 for (i=0; i < (ssize_t) length; i++)
2596 pixels[i]=(~pixels[i]);
2597 if (next_image->compression == RLECompression)
2599 length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels,
2601 count+=WriteBlob(image,length,compact_pixels);
2602 size_offset+=WritePSDOffset(psd_info,image,length,size_offset);
2604 #ifdef MAGICKCORE_ZLIB_DELEGATE
2605 else if (next_image->compression == ZipCompression)
2607 stream.avail_in=(uInt) length;
2608 stream.next_in=(Bytef *) pixels;
2609 if (y == (ssize_t) next_image->rows-1)
2612 stream.avail_out=(uInt) CHUNK;
2613 stream.next_out=(Bytef *) compressed_pixels;
2614 if (deflate(&stream,flush) == Z_STREAM_ERROR)
2616 length=(size_t) CHUNK-stream.avail_out;
2618 count+=WriteBlob(image,length,compressed_pixels);
2619 } while (stream.avail_out == 0);
2623 count+=WriteBlob(image,length,pixels);
2625 #ifdef MAGICKCORE_ZLIB_DELEGATE
2626 if (next_image->compression == ZipCompression)
2628 (void) deflateEnd(&stream);
2629 compressed_pixels=(unsigned char *) RelinquishMagickMemory(
2633 quantum_info=DestroyQuantumInfo(quantum_info);
2637 static unsigned char *AcquireCompactPixels(const Image *image,
2638 ExceptionInfo *exception)
2646 packet_size=image->depth > 8UL ? 2UL : 1UL;
2647 compact_pixels=(unsigned char *) AcquireQuantumMemory((9*
2648 image->columns)+1,packet_size*sizeof(*compact_pixels));
2649 if (compact_pixels == (unsigned char *) NULL)
2651 (void) ThrowMagickException(exception,GetMagickModule(),
2652 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2654 return(compact_pixels);
2657 static size_t WritePSDChannels(const PSDInfo *psd_info,
2658 const ImageInfo *image_info,Image *image,Image *next_image,
2659 MagickOffsetType size_offset,const MagickBooleanType separate,
2660 ExceptionInfo *exception)
2680 compact_pixels=(unsigned char *) NULL;
2681 if (next_image->compression == RLECompression)
2683 compact_pixels=AcquireCompactPixels(next_image,exception);
2684 if (compact_pixels == (unsigned char *) NULL)
2688 if (separate == MagickFalse)
2690 if (next_image->storage_class != PseudoClass)
2692 if (IsImageGray(next_image) == MagickFalse)
2693 channels=next_image->colorspace == CMYKColorspace ? 4 : 3;
2694 if (next_image->alpha_trait != UndefinedPixelTrait)
2697 rows_offset=TellBlob(image)+2;
2698 count+=WriteCompressionStart(psd_info,image,next_image,channels);
2699 offset_length=(next_image->rows*(psd_info->version == 1 ? 2 : 4));
2702 if (next_image->storage_class == PseudoClass)
2704 length=WritePSDChannel(psd_info,image_info,image,next_image,
2705 IndexQuantum,compact_pixels,rows_offset,separate,exception);
2706 if (separate != MagickFalse)
2707 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2709 rows_offset+=offset_length;
2714 if (IsImageGray(next_image) != MagickFalse)
2716 length=WritePSDChannel(psd_info,image_info,image,next_image,
2717 GrayQuantum,compact_pixels,rows_offset,separate,exception);
2718 if (separate != MagickFalse)
2719 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2721 rows_offset+=offset_length;
2726 if (next_image->colorspace == CMYKColorspace)
2727 (void) NegateCMYK(next_image,exception);
2729 length=WritePSDChannel(psd_info,image_info,image,next_image,
2730 RedQuantum,compact_pixels,rows_offset,separate,exception);
2731 if (separate != MagickFalse)
2732 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2734 rows_offset+=offset_length;
2737 length=WritePSDChannel(psd_info,image_info,image,next_image,
2738 GreenQuantum,compact_pixels,rows_offset,separate,exception);
2739 if (separate != MagickFalse)
2740 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2742 rows_offset+=offset_length;
2745 length=WritePSDChannel(psd_info,image_info,image,next_image,
2746 BlueQuantum,compact_pixels,rows_offset,separate,exception);
2747 if (separate != MagickFalse)
2748 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2750 rows_offset+=offset_length;
2753 if (next_image->colorspace == CMYKColorspace)
2755 length=WritePSDChannel(psd_info,image_info,image,next_image,
2756 BlackQuantum,compact_pixels,rows_offset,separate,exception);
2757 if (separate != MagickFalse)
2758 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2760 rows_offset+=offset_length;
2764 if (next_image->alpha_trait != UndefinedPixelTrait)
2766 length=WritePSDChannel(psd_info,image_info,image,next_image,
2767 AlphaQuantum,compact_pixels,rows_offset,separate,exception);
2768 if (separate != MagickFalse)
2769 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2771 rows_offset+=offset_length;
2775 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
2776 if (next_image->colorspace == CMYKColorspace)
2777 (void) NegateCMYK(next_image,exception);
2778 if (separate != MagickFalse)
2783 property=GetImageArtifact(next_image,"psd:opacity-mask");
2784 if (property != (const char *) NULL)
2786 mask=(Image *) GetImageRegistry(ImageRegistryType,property,
2788 if (mask != (Image *) NULL)
2790 if (mask->compression == RLECompression)
2792 compact_pixels=AcquireCompactPixels(mask,exception);
2793 if (compact_pixels == (unsigned char *) NULL)
2796 length=WritePSDChannel(psd_info,image_info,image,mask,
2797 RedQuantum,compact_pixels,rows_offset,MagickTrue,exception);
2798 (void) WritePSDSize(psd_info,image,length,size_offset);
2800 compact_pixels=(unsigned char *) RelinquishMagickMemory(
2808 static size_t WritePascalString(Image *image,const char *value,size_t padding)
2821 length=(strlen(value) > 255UL ) ? 255UL : strlen(value);
2823 count+=WriteBlobByte(image,0);
2826 count+=WriteBlobByte(image,(unsigned char) length);
2827 count+=WriteBlob(image,length,(const unsigned char *) value);
2830 if ((length % padding) == 0)
2832 for (i=0; i < (ssize_t) (padding-(length % padding)); i++)
2833 count+=WriteBlobByte(image,0);
2837 static void WriteResolutionResourceBlock(Image *image)
2846 if (image->units == PixelsPerCentimeterResolution)
2848 x_resolution=2.54*65536.0*image->resolution.x+0.5;
2849 y_resolution=2.54*65536.0*image->resolution.y+0.5;
2854 x_resolution=65536.0*image->resolution.x+0.5;
2855 y_resolution=65536.0*image->resolution.y+0.5;
2858 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
2859 (void) WriteBlobMSBShort(image,0x03ED);
2860 (void) WriteBlobMSBShort(image,0);
2861 (void) WriteBlobMSBLong(image,16); /* resource size */
2862 (void) WriteBlobMSBLong(image,(unsigned int) (x_resolution+0.5));
2863 (void) WriteBlobMSBShort(image,units); /* horizontal resolution unit */
2864 (void) WriteBlobMSBShort(image,units); /* width unit */
2865 (void) WriteBlobMSBLong(image,(unsigned int) (y_resolution+0.5));
2866 (void) WriteBlobMSBShort(image,units); /* vertical resolution unit */
2867 (void) WriteBlobMSBShort(image,units); /* height unit */
2870 static inline size_t WriteChannelSize(const PSDInfo *psd_info,Image *image,
2871 const signed short channel)
2876 count=WriteBlobMSBSignedShort(image,channel);
2877 count+=SetPSDSize(psd_info,image,0);
2881 static void RemoveICCProfileFromResourceBlock(StringInfo *bim_profile)
2883 register const unsigned char
2900 length=GetStringInfoLength(bim_profile);
2903 datum=GetStringInfoDatum(bim_profile);
2904 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
2906 register unsigned char
2909 q=(unsigned char *) p;
2910 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
2912 p=PushLongPixel(MSBEndian,p,&long_sans);
2913 p=PushShortPixel(MSBEndian,p,&id);
2914 p=PushShortPixel(MSBEndian,p,&short_sans);
2915 p=PushLongPixel(MSBEndian,p,&count);
2916 if (id == 0x0000040f)
2921 quantum=PSDQuantum(count)+12;
2922 if ((quantum >= 12) && (quantum < (ssize_t) length))
2924 if ((q+quantum < (datum+length-16)))
2925 (void) CopyMagickMemory(q,q+quantum,length-quantum-(q-datum));
2926 SetStringInfoLength(bim_profile,length-quantum);
2931 if ((count & 0x01) != 0)
2936 static void RemoveResolutionFromResourceBlock(StringInfo *bim_profile)
2938 register const unsigned char
2955 length=GetStringInfoLength(bim_profile);
2958 datum=GetStringInfoDatum(bim_profile);
2959 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
2961 register unsigned char
2967 q=(unsigned char *) p;
2968 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
2970 p=PushLongPixel(MSBEndian,p,&long_sans);
2971 p=PushShortPixel(MSBEndian,p,&id);
2972 p=PushShortPixel(MSBEndian,p,&short_sans);
2973 p=PushLongPixel(MSBEndian,p,&count);
2974 cnt=PSDQuantum(count);
2977 if ((id == 0x000003ed) && (cnt < (ssize_t) (length-12)))
2979 (void) CopyMagickMemory(q,q+cnt+12,length-(cnt+12)-(q-datum));
2980 SetStringInfoLength(bim_profile,length-(cnt+12));
2984 if ((count & 0x01) != 0)
2989 static const StringInfo *GetAdditionalInformation(const ImageInfo *image_info,
2990 Image *image,ExceptionInfo *exception)
2992 #define PSDKeySize 5
2993 #define PSDAllowedLength 36
2998 /* Whitelist of keys from: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ */
3000 allowed[PSDAllowedLength][PSDKeySize] = {
3001 "blnc", "blwh", "brit", "brst", "clbl", "clrL", "curv", "expA", "FMsk",
3002 "GdFl", "grdm", "hue ", "hue2", "infx", "knko", "lclr", "levl", "lnsr",
3003 "lfx2", "luni", "lrFX", "lspf", "lyid", "lyvr", "mixr", "nvrt", "phfl",
3004 "post", "PtFl", "selc", "shpa", "sn2P", "SoCo", "thrs", "tsly", "vibA"
3030 info=GetImageProfile(image,"psd:additional-info");
3031 if (info == (const StringInfo *) NULL)
3032 return((const StringInfo *) NULL);
3033 option=GetImageOption(image_info,"psd:additional-info");
3034 if (LocaleCompare(option,"all") == 0)
3036 if (LocaleCompare(option,"selective") != 0)
3038 profile=RemoveImageProfile(image,"psd:additional-info");
3039 return(DestroyStringInfo(profile));
3041 length=GetStringInfoLength(info);
3042 p=GetStringInfoDatum(info);
3043 remaining_length=length;
3045 while (remaining_length >= 12)
3047 /* skip over signature */
3054 size=(unsigned int) (*p++) << 24;
3055 size|=(unsigned int) (*p++) << 16;
3056 size|=(unsigned int) (*p++) << 8;
3057 size|=(unsigned int) (*p++);
3058 size=size & 0xffffffff;
3059 remaining_length-=12;
3060 if ((size_t) size > remaining_length)
3061 return((const StringInfo *) NULL);
3063 for (i=0; i < PSDAllowedLength; i++)
3065 if (LocaleNCompare(key,allowed[i],PSDKeySize) != 0)
3071 remaining_length-=(size_t) size;
3072 if (found == MagickFalse)
3074 if (remaining_length > 0)
3075 p=(unsigned char *) CopyMagickMemory(p-12,p+size,remaining_length);
3078 length+=(size_t) size+12;
3081 profile=RemoveImageProfile(image,"psd:additional-info");
3083 return(DestroyStringInfo(profile));
3084 SetStringInfoLength(profile,(const size_t) length);
3085 SetImageProfile(image,"psd:additional-info",info,exception);
3089 static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
3090 Image *image,ExceptionInfo *exception)
3093 layer_name[MagickPathExtent];
3110 *layer_size_offsets,
3135 assert(image_info != (const ImageInfo *) NULL);
3136 assert(image_info->signature == MagickCoreSignature);
3137 assert(image != (Image *) NULL);
3138 assert(image->signature == MagickCoreSignature);
3139 if (image->debug != MagickFalse)
3140 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3141 assert(exception != (ExceptionInfo *) NULL);
3142 assert(exception->signature == MagickCoreSignature);
3143 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
3144 if (status == MagickFalse)
3146 packet_size=(size_t) (image->depth > 8 ? 6 : 3);
3147 if (image->alpha_trait != UndefinedPixelTrait)
3148 packet_size+=image->depth > 8 ? 2 : 1;
3150 if ((LocaleCompare(image_info->magick,"PSB") == 0) ||
3151 (image->columns > 30000) || (image->rows > 30000))
3153 (void) WriteBlob(image,4,(const unsigned char *) "8BPS");
3154 (void) WriteBlobMSBShort(image,psd_info.version); /* version */
3155 for (i=1; i <= 6; i++)
3156 (void) WriteBlobByte(image, 0); /* 6 bytes of reserved */
3157 /* When the image has a color profile it won't be converted to gray scale */
3158 if ((GetImageProfile(image,"icc") == (StringInfo *) NULL) &&
3159 (SetImageGray(image,exception) != MagickFalse))
3160 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
3162 if ((image_info->type != TrueColorType) && (image_info->type !=
3163 TrueColorAlphaType) && (image->storage_class == PseudoClass))
3164 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
3167 if (image->storage_class == PseudoClass)
3168 (void) SetImageStorageClass(image,DirectClass,exception);
3169 if (image->colorspace != CMYKColorspace)
3170 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 4UL : 3UL);
3172 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 5UL : 4UL);
3174 (void) WriteBlobMSBShort(image,(unsigned short) num_channels);
3175 (void) WriteBlobMSBLong(image,(unsigned int) image->rows);
3176 (void) WriteBlobMSBLong(image,(unsigned int) image->columns);
3177 if (IsImageGray(image) != MagickFalse)
3185 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
3186 MagickTrue : MagickFalse;
3187 (void) WriteBlobMSBShort(image,(unsigned short)
3188 (monochrome != MagickFalse ? 1 : image->depth > 8 ? 16 : 8));
3189 (void) WriteBlobMSBShort(image,(unsigned short)
3190 (monochrome != MagickFalse ? BitmapMode : GrayscaleMode));
3194 (void) WriteBlobMSBShort(image,(unsigned short) (image->storage_class ==
3195 PseudoClass ? 8 : image->depth > 8 ? 16 : 8));
3197 if (((image_info->colorspace != UndefinedColorspace) ||
3198 (image->colorspace != CMYKColorspace)) &&
3199 (image_info->colorspace != CMYKColorspace))
3201 (void) TransformImageColorspace(image,sRGBColorspace,exception);
3202 (void) WriteBlobMSBShort(image,(unsigned short)
3203 (image->storage_class == PseudoClass ? IndexedMode : RGBMode));
3207 if (image->colorspace != CMYKColorspace)
3208 (void) TransformImageColorspace(image,CMYKColorspace,exception);
3209 (void) WriteBlobMSBShort(image,CMYKMode);
3212 if ((IsImageGray(image) != MagickFalse) ||
3213 (image->storage_class == DirectClass) || (image->colors > 256))
3214 (void) WriteBlobMSBLong(image,0);
3218 Write PSD raster colormap.
3220 (void) WriteBlobMSBLong(image,768);
3221 for (i=0; i < (ssize_t) image->colors; i++)
3222 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].red));
3223 for ( ; i < 256; i++)
3224 (void) WriteBlobByte(image,0);
3225 for (i=0; i < (ssize_t) image->colors; i++)
3226 (void) WriteBlobByte(image,ScaleQuantumToChar(
3227 image->colormap[i].green));
3228 for ( ; i < 256; i++)
3229 (void) WriteBlobByte(image,0);
3230 for (i=0; i < (ssize_t) image->colors; i++)
3231 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].blue));
3232 for ( ; i < 256; i++)
3233 (void) WriteBlobByte(image,0);
3236 Image resource block.
3238 length=28; /* 0x03EB */
3239 bim_profile=(StringInfo *) GetImageProfile(image,"8bim");
3240 icc_profile=GetImageProfile(image,"icc");
3241 if (bim_profile != (StringInfo *) NULL)
3243 bim_profile=CloneStringInfo(bim_profile);
3244 if (icc_profile != (StringInfo *) NULL)
3245 RemoveICCProfileFromResourceBlock(bim_profile);
3246 RemoveResolutionFromResourceBlock(bim_profile);
3247 length+=PSDQuantum(GetStringInfoLength(bim_profile));
3249 if (icc_profile != (const StringInfo *) NULL)
3250 length+=PSDQuantum(GetStringInfoLength(icc_profile))+12;
3251 (void) WriteBlobMSBLong(image,(unsigned int) length);
3252 WriteResolutionResourceBlock(image);
3253 if (bim_profile != (StringInfo *) NULL)
3255 (void) WriteBlob(image,GetStringInfoLength(bim_profile),
3256 GetStringInfoDatum(bim_profile));
3257 bim_profile=DestroyStringInfo(bim_profile);
3259 if (icc_profile != (StringInfo *) NULL)
3261 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
3262 (void) WriteBlobMSBShort(image,0x0000040F);
3263 (void) WriteBlobMSBShort(image,0);
3264 (void) WriteBlobMSBLong(image,(unsigned int) GetStringInfoLength(
3266 (void) WriteBlob(image,GetStringInfoLength(icc_profile),
3267 GetStringInfoDatum(icc_profile));
3268 if ((MagickOffsetType) GetStringInfoLength(icc_profile) !=
3269 PSDQuantum(GetStringInfoLength(icc_profile)))
3270 (void) WriteBlobByte(image,0);
3272 base_image=GetNextImageInList(image);
3273 if (base_image == (Image *) NULL)
3276 size_offset=TellBlob(image);
3277 SetPSDSize(&psd_info,image,0);
3278 SetPSDSize(&psd_info,image,0);
3280 for (next_image=base_image; next_image != NULL; )
3283 next_image=GetNextImageInList(next_image);
3285 if (image->alpha_trait != UndefinedPixelTrait)
3286 size+=WriteBlobMSBShort(image,-(unsigned short) layer_count);
3288 size+=WriteBlobMSBShort(image,(unsigned short) layer_count);
3289 layer_size_offsets=(MagickOffsetType *) AcquireQuantumMemory(
3290 (size_t) layer_count,sizeof(MagickOffsetType));
3291 if (layer_size_offsets == (MagickOffsetType *) NULL)
3292 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3294 for (next_image=base_image; next_image != NULL; )
3306 mask=(Image *) NULL;
3307 property=GetImageArtifact(next_image,"psd:opacity-mask");
3309 if (property != (const char *) NULL)
3311 mask=(Image *) GetImageRegistry(ImageRegistryType,property,exception);
3312 default_color=strlen(property) == 9 ? 255 : 0;
3314 size+=WriteBlobMSBLong(image,(unsigned int) next_image->page.y);
3315 size+=WriteBlobMSBLong(image,(unsigned int) next_image->page.x);
3316 size+=WriteBlobMSBLong(image,(unsigned int) (next_image->page.y+
3318 size+=WriteBlobMSBLong(image,(unsigned int) (next_image->page.x+
3319 next_image->columns));
3321 if ((next_image->storage_class != PseudoClass) &&
3322 (IsImageGray(next_image) == MagickFalse))
3323 channels=next_image->colorspace == CMYKColorspace ? 4U : 3U;
3324 total_channels=channels;
3325 if (next_image->alpha_trait != UndefinedPixelTrait)
3327 if (mask != (Image *) NULL)
3329 size+=WriteBlobMSBShort(image,total_channels);
3330 layer_size_offsets[layer_index++]=TellBlob(image);
3331 for (i=0; i < (ssize_t) channels; i++)
3332 size+=WriteChannelSize(&psd_info,image,(signed short) i);
3333 if (next_image->alpha_trait != UndefinedPixelTrait)
3334 size+=WriteChannelSize(&psd_info,image,-1);
3335 if (mask != (Image *) NULL)
3336 size+=WriteChannelSize(&psd_info,image,-2);
3337 size+=WriteBlob(image,4,(const unsigned char *) "8BIM");
3338 size+=WriteBlob(image,4,(const unsigned char *)
3339 CompositeOperatorToPSDBlendMode(next_image->compose));
3340 property=GetImageArtifact(next_image,"psd:layer.opacity");
3341 if (property != (const char *) NULL)
3346 opacity=(Quantum) StringToInteger(property);
3347 size+=WriteBlobByte(image,ScaleQuantumToChar(opacity));
3348 (void) ApplyPSDLayerOpacity(next_image,opacity,MagickTrue,exception);
3351 size+=WriteBlobByte(image,255);
3352 size+=WriteBlobByte(image,0);
3353 size+=WriteBlobByte(image,next_image->compose==NoCompositeOp ?
3354 1 << 0x02 : 1); /* layer properties - visible, etc. */
3355 size+=WriteBlobByte(image,0);
3356 info=GetAdditionalInformation(image_info,next_image,exception);
3357 property=(const char *) GetImageProperty(next_image,"label",exception);
3358 if (property == (const char *) NULL)
3360 (void) FormatLocaleString(layer_name,MagickPathExtent,"L%.20g",
3361 (double) layer_index);
3362 property=layer_name;
3364 name_length=strlen(property)+1;
3365 if ((name_length % 4) != 0)
3366 name_length+=(4-(name_length % 4));
3367 if (info != (const StringInfo *) NULL)
3368 name_length+=GetStringInfoLength(info);
3370 if (mask != (Image *) NULL)
3372 size+=WriteBlobMSBLong(image,(unsigned int) name_length);
3373 if (mask == (Image *) NULL)
3374 size+=WriteBlobMSBLong(image,0);
3377 if (mask->compose != NoCompositeOp)
3378 (void) ApplyPSDOpacityMask(next_image,mask,ScaleCharToQuantum(
3379 default_color),MagickTrue,exception);
3380 mask->page.y+=image->page.y;
3381 mask->page.x+=image->page.x;
3382 size+=WriteBlobMSBLong(image,20);
3383 size+=WriteBlobMSBSignedLong(image,mask->page.y);
3384 size+=WriteBlobMSBSignedLong(image,mask->page.x);
3385 size+=WriteBlobMSBLong(image,(const unsigned int) mask->rows+
3387 size+=WriteBlobMSBLong(image,(const unsigned int) mask->columns+
3389 size+=WriteBlobByte(image,default_color);
3390 size+=WriteBlobByte(image,mask->compose == NoCompositeOp ? 2 : 0);
3391 size+=WriteBlobMSBShort(image,0);
3393 size+=WriteBlobMSBLong(image,0);
3394 size+=WritePascalString(image,property,4);
3395 if (info != (const StringInfo *) NULL)
3396 size+=WriteBlob(image,GetStringInfoLength(info),
3397 GetStringInfoDatum(info));
3398 next_image=GetNextImageInList(next_image);
3403 next_image=base_image;
3405 while (next_image != NULL)
3407 length=WritePSDChannels(&psd_info,image_info,image,next_image,
3408 layer_size_offsets[layer_index++],MagickTrue,exception);
3415 next_image=GetNextImageInList(next_image);
3417 (void) WriteBlobMSBLong(image,0); /* user mask data */
3419 Write the total size
3421 size_offset+=WritePSDSize(&psd_info,image,size+
3422 (psd_info.version == 1 ? 8 : 16),size_offset);
3423 if ((size/2) != ((size+1)/2))
3424 rounded_size=size+1;
3427 (void) WritePSDSize(&psd_info,image,rounded_size,size_offset);
3428 layer_size_offsets=(MagickOffsetType *) RelinquishMagickMemory(
3429 layer_size_offsets);
3431 Remove the opacity mask from the registry
3433 next_image=base_image;
3434 while (next_image != (Image *) NULL)
3436 property=GetImageArtifact(next_image,"psd:opacity-mask");
3437 if (property != (const char *) NULL)
3438 DeleteImageRegistry(property);
3439 next_image=GetNextImageInList(next_image);
3442 Write composite image.
3444 if (status != MagickFalse)
3449 compression=image->compression;
3450 if (image->compression == ZipCompression)
3451 image->compression=RLECompression;
3452 if (WritePSDChannels(&psd_info,image_info,image,image,0,MagickFalse,
3455 image->compression=compression;
3457 (void) CloseBlob(image);