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)
755 profile=BlobToStringInfo((const unsigned char *) NULL,length);
756 SetStringInfoDatum(profile,blocks);
757 (void) SetImageProfile(image,"8bim",profile,exception);
758 profile=DestroyStringInfo(profile);
759 for (p=blocks; (p >= blocks) && (p < (blocks+length-16)); )
761 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
763 p=PushLongPixel(MSBEndian,p,&long_sans);
764 p=PushShortPixel(MSBEndian,p,&id);
765 p=PushShortPixel(MSBEndian,p,&short_sans);
766 p=PushLongPixel(MSBEndian,p,&count);
767 if ((p+count) > (blocks+length-16))
774 value[MagickPathExtent];
782 p=PushShortPixel(MSBEndian,p,&resolution);
783 image->resolution.x=(double) resolution;
784 (void) FormatLocaleString(value,MagickPathExtent,"%g",image->resolution.x);
785 (void) SetImageProperty(image,"tiff:XResolution",value,exception);
786 p=PushShortPixel(MSBEndian,p,&short_sans);
787 p=PushShortPixel(MSBEndian,p,&short_sans);
788 p=PushShortPixel(MSBEndian,p,&short_sans);
789 p=PushShortPixel(MSBEndian,p,&resolution);
790 image->resolution.y=(double) resolution;
791 (void) FormatLocaleString(value,MagickPathExtent,"%g",image->resolution.y);
792 (void) SetImageProperty(image,"tiff:YResolution",value,exception);
793 p=PushShortPixel(MSBEndian,p,&short_sans);
794 p=PushShortPixel(MSBEndian,p,&short_sans);
795 p=PushShortPixel(MSBEndian,p,&short_sans);
796 image->units=PixelsPerInchResolution;
802 *has_merged_image=MagickFalse;
812 if ((count & 0x01) != 0)
818 static CompositeOperator PSDBlendModeToCompositeOperator(const char *mode)
820 if (mode == (const char *) NULL)
821 return(OverCompositeOp);
822 if (LocaleNCompare(mode,"norm",4) == 0)
823 return(OverCompositeOp);
824 if (LocaleNCompare(mode,"mul ",4) == 0)
825 return(MultiplyCompositeOp);
826 if (LocaleNCompare(mode,"diss",4) == 0)
827 return(DissolveCompositeOp);
828 if (LocaleNCompare(mode,"diff",4) == 0)
829 return(DifferenceCompositeOp);
830 if (LocaleNCompare(mode,"dark",4) == 0)
831 return(DarkenCompositeOp);
832 if (LocaleNCompare(mode,"lite",4) == 0)
833 return(LightenCompositeOp);
834 if (LocaleNCompare(mode,"hue ",4) == 0)
835 return(HueCompositeOp);
836 if (LocaleNCompare(mode,"sat ",4) == 0)
837 return(SaturateCompositeOp);
838 if (LocaleNCompare(mode,"colr",4) == 0)
839 return(ColorizeCompositeOp);
840 if (LocaleNCompare(mode,"lum ",4) == 0)
841 return(LuminizeCompositeOp);
842 if (LocaleNCompare(mode,"scrn",4) == 0)
843 return(ScreenCompositeOp);
844 if (LocaleNCompare(mode,"over",4) == 0)
845 return(OverlayCompositeOp);
846 if (LocaleNCompare(mode,"hLit",4) == 0)
847 return(HardLightCompositeOp);
848 if (LocaleNCompare(mode,"sLit",4) == 0)
849 return(SoftLightCompositeOp);
850 if (LocaleNCompare(mode,"smud",4) == 0)
851 return(ExclusionCompositeOp);
852 if (LocaleNCompare(mode,"div ",4) == 0)
853 return(ColorDodgeCompositeOp);
854 if (LocaleNCompare(mode,"idiv",4) == 0)
855 return(ColorBurnCompositeOp);
856 if (LocaleNCompare(mode,"lbrn",4) == 0)
857 return(LinearBurnCompositeOp);
858 if (LocaleNCompare(mode,"lddg",4) == 0)
859 return(LinearDodgeCompositeOp);
860 if (LocaleNCompare(mode,"lLit",4) == 0)
861 return(LinearLightCompositeOp);
862 if (LocaleNCompare(mode,"vLit",4) == 0)
863 return(VividLightCompositeOp);
864 if (LocaleNCompare(mode,"pLit",4) == 0)
865 return(PinLightCompositeOp);
866 if (LocaleNCompare(mode,"hMix",4) == 0)
867 return(HardMixCompositeOp);
868 return(OverCompositeOp);
871 static inline void ReversePSDString(Image *image,char *p,size_t length)
876 if (image->endian == MSBEndian)
880 for(--q; p < q; ++p, --q)
888 static inline void SetPSDPixel(Image *image,const size_t channels,
889 const ssize_t type,const size_t packet_size,const Quantum pixel,Quantum *q,
890 ExceptionInfo *exception)
892 if (image->storage_class == PseudoClass)
894 if (packet_size == 1)
895 SetPixelIndex(image,ScaleQuantumToChar(pixel),q);
897 SetPixelIndex(image,ScaleQuantumToShort(pixel),q);
898 SetPixelViaPixelInfo(image,image->colormap+(ssize_t)
899 ConstrainColormapIndex(image,GetPixelIndex(image,q),exception),q);
906 SetPixelAlpha(image,pixel,q);
912 SetPixelRed(image,pixel,q);
917 if (image->storage_class == PseudoClass)
918 SetPixelAlpha(image,pixel,q);
920 SetPixelGreen(image,pixel,q);
925 if (image->storage_class == PseudoClass)
926 SetPixelAlpha(image,pixel,q);
928 SetPixelBlue(image,pixel,q);
933 if (image->colorspace == CMYKColorspace)
934 SetPixelBlack(image,pixel,q);
936 if (image->alpha_trait != UndefinedPixelTrait)
937 SetPixelAlpha(image,pixel,q);
942 if ((IssRGBCompatibleColorspace(image->colorspace) != MagickFalse) &&
945 if (image->alpha_trait != UndefinedPixelTrait)
946 SetPixelAlpha(image,pixel,q);
952 static MagickBooleanType ReadPSDChannelPixels(Image *image,
953 const size_t channels,const size_t row,const ssize_t type,
954 const unsigned char *pixels,ExceptionInfo *exception)
959 register const unsigned char
975 q=GetAuthenticPixels(image,0,row,image->columns,1,exception);
976 if (q == (Quantum *) NULL)
978 packet_size=GetPSDPacketSize(image);
979 for (x=0; x < (ssize_t) image->columns; x++)
981 if (packet_size == 1)
982 pixel=ScaleCharToQuantum(*p++);
985 p=PushShortPixel(MSBEndian,p,&nibble);
986 pixel=ScaleShortToQuantum(nibble);
988 if (image->depth > 1)
990 SetPSDPixel(image,channels,type,packet_size,pixel,q,exception);
991 q+=GetPixelChannels(image);
999 number_bits=image->columns-x;
1000 if (number_bits > 8)
1002 for (bit = 0; bit < number_bits; bit++)
1004 SetPSDPixel(image,channels,type,packet_size,(((unsigned char) pixel)
1005 & (0x01 << (7-bit))) != 0 ? 0 : QuantumRange,q,exception);
1006 q+=GetPixelChannels(image);
1009 if (x != (ssize_t) image->columns)
1014 return(SyncAuthenticPixels(image,exception));
1017 static MagickBooleanType ReadPSDChannelRaw(Image *image,const size_t channels,
1018 const ssize_t type,ExceptionInfo *exception)
1033 if (image->debug != MagickFalse)
1034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1035 " layer data is RAW");
1037 row_size=GetPSDRowSize(image);
1038 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
1039 if (pixels == (unsigned char *) NULL)
1040 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1044 for (y=0; y < (ssize_t) image->rows; y++)
1048 count=ReadBlob(image,row_size,pixels);
1049 if (count != row_size)
1052 status=ReadPSDChannelPixels(image,channels,y,type,pixels,exception);
1053 if (status == MagickFalse)
1057 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1061 static inline MagickOffsetType *ReadPSDRLESizes(Image *image,
1062 const PSDInfo *psd_info,const size_t size)
1070 sizes=(MagickOffsetType *) AcquireQuantumMemory(size,sizeof(*sizes));
1071 if(sizes != (MagickOffsetType *) NULL)
1073 for (y=0; y < (ssize_t) size; y++)
1075 if (psd_info->version == 1)
1076 sizes[y]=(MagickOffsetType) ReadBlobShort(image);
1078 sizes[y]=(MagickOffsetType) ReadBlobLong(image);
1084 static MagickBooleanType ReadPSDChannelRLE(Image *image,const PSDInfo *psd_info,
1085 const ssize_t type,MagickOffsetType *sizes,ExceptionInfo *exception)
1102 if (image->debug != MagickFalse)
1103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1104 " layer data is RLE compressed");
1106 row_size=GetPSDRowSize(image);
1107 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
1108 if (pixels == (unsigned char *) NULL)
1109 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1113 for (y=0; y < (ssize_t) image->rows; y++)
1114 if ((MagickOffsetType) length < sizes[y])
1115 length=(size_t) sizes[y];
1117 if (length > row_size + 256) // arbitrary number
1119 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1120 ThrowBinaryException(ResourceLimitError,"InvalidLength",image->filename);
1123 compact_pixels=(unsigned char *) AcquireQuantumMemory(length,sizeof(*pixels));
1124 if (compact_pixels == (unsigned char *) NULL)
1126 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1127 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1131 (void) ResetMagickMemory(compact_pixels,0,length*sizeof(*compact_pixels));
1134 for (y=0; y < (ssize_t) image->rows; y++)
1138 count=ReadBlob(image,(size_t) sizes[y],compact_pixels);
1139 if (count != (ssize_t) sizes[y])
1142 count=DecodePSDPixels((size_t) sizes[y],compact_pixels,
1143 (ssize_t) (image->depth == 1 ? 123456 : image->depth),row_size,pixels);
1144 if (count != (ssize_t) row_size)
1147 status=ReadPSDChannelPixels(image,psd_info->channels,y,type,pixels,
1149 if (status == MagickFalse)
1153 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1154 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1158 #ifdef MAGICKCORE_ZLIB_DELEGATE
1159 static MagickBooleanType ReadPSDChannelZip(Image *image,const size_t channels,
1160 const ssize_t type,const PSDCompressionType compression,
1161 const size_t compact_size,ExceptionInfo *exception)
1166 register unsigned char
1185 if (image->debug != MagickFalse)
1186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1187 " layer data is ZIP compressed");
1189 compact_pixels=(unsigned char *) AcquireQuantumMemory(compact_size,
1190 sizeof(*compact_pixels));
1191 if (compact_pixels == (unsigned char *) NULL)
1192 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1195 packet_size=GetPSDPacketSize(image);
1196 row_size=image->columns*packet_size;
1197 count=image->rows*row_size;
1199 pixels=(unsigned char *) AcquireQuantumMemory(count,sizeof(*pixels));
1200 if (pixels == (unsigned char *) NULL)
1202 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1203 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1206 if (ReadBlob(image,compact_size,compact_pixels) != (ssize_t) compact_size)
1208 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1209 ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile",
1213 ResetMagickMemory(&stream,0,sizeof(stream));
1214 stream.data_type=Z_BINARY;
1215 stream.next_in=(Bytef *)compact_pixels;
1216 stream.avail_in=(uInt) compact_size;
1217 stream.next_out=(Bytef *)pixels;
1218 stream.avail_out=(uInt) count;
1220 if (inflateInit(&stream) == Z_OK)
1225 while (stream.avail_out > 0)
1227 ret=inflate(&stream,Z_SYNC_FLUSH);
1228 if ((ret != Z_OK) && (ret != Z_STREAM_END))
1230 (void) inflateEnd(&stream);
1231 compact_pixels=(unsigned char *) RelinquishMagickMemory(
1233 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1234 return(MagickFalse);
1237 (void) inflateEnd(&stream);
1240 if (compression == ZipWithPrediction)
1245 length=image->columns;
1248 if (packet_size == 2)
1250 p[2]+=p[0]+((p[1]+p[3]) >> 8);
1264 for (y=0; y < (ssize_t) image->rows; y++)
1266 status=ReadPSDChannelPixels(image,channels,y,type,p,exception);
1267 if (status == MagickFalse)
1273 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1274 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1279 static MagickBooleanType ReadPSDChannel(Image *image,
1280 const ImageInfo *image_info,const PSDInfo *psd_info,LayerInfo* layer_info,
1281 const size_t channel,const PSDCompressionType compression,
1282 ExceptionInfo *exception)
1294 channel_image=image;
1295 mask=(Image *) NULL;
1296 if (layer_info->channel_info[channel].type < -1)
1301 Ignore mask that is not a user supplied layer mask, if the mask is
1302 disabled or if the flags have unsupported values.
1304 option=GetImageOption(image_info,"psd:preserve-opacity-mask");
1305 if ((layer_info->channel_info[channel].type != -2) ||
1306 (layer_info->mask.flags > 2) || ((layer_info->mask.flags & 0x02) &&
1307 (IsStringTrue(option) == MagickFalse)))
1309 SeekBlob(image,layer_info->channel_info[channel].size-2,SEEK_CUR);
1312 mask=CloneImage(image,layer_info->mask.page.width,
1313 layer_info->mask.page.height,MagickFalse,exception);
1314 if (mask != (Image *) NULL)
1316 SetImageType(mask,GrayscaleType,exception);
1321 offset=TellBlob(image);
1326 status=ReadPSDChannelRaw(channel_image,psd_info->channels,
1327 layer_info->channel_info[channel].type,exception);
1334 sizes=ReadPSDRLESizes(channel_image,psd_info,channel_image->rows);
1335 if (sizes == (MagickOffsetType *) NULL)
1336 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1338 status=ReadPSDChannelRLE(channel_image,psd_info,
1339 layer_info->channel_info[channel].type,sizes,exception);
1340 sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
1343 case ZipWithPrediction:
1344 case ZipWithoutPrediction:
1345 #ifdef MAGICKCORE_ZLIB_DELEGATE
1346 status=ReadPSDChannelZip(channel_image,layer_info->channels,
1347 layer_info->channel_info[channel].type,compression,
1348 layer_info->channel_info[channel].size-2,exception);
1350 (void) ThrowMagickException(exception,GetMagickModule(),
1351 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
1352 "'%s' (ZLIB)",image->filename);
1356 (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
1357 "CompressionNotSupported","'%.20g'",(double) compression);
1361 SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
1362 if (status == MagickFalse)
1364 if (mask != (Image *) NULL)
1366 ThrowBinaryException(CoderError,"UnableToDecompressImage",
1369 layer_info->mask.image=mask;
1373 static MagickBooleanType ReadPSDLayer(Image *image,const ImageInfo *image_info,
1374 const PSDInfo *psd_info,LayerInfo* layer_info,ExceptionInfo *exception)
1377 message[MagickPathExtent];
1388 if (image->debug != MagickFalse)
1389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1390 " setting up new layer image");
1391 if (psd_info->mode != IndexedMode)
1392 (void) SetImageBackgroundColor(layer_info->image,exception);
1393 layer_info->image->compose=PSDBlendModeToCompositeOperator(
1394 layer_info->blendkey);
1395 if (layer_info->visible == MagickFalse)
1396 layer_info->image->compose=NoCompositeOp;
1397 if (psd_info->mode == CMYKMode)
1398 SetImageColorspace(layer_info->image,CMYKColorspace,exception);
1399 else if ((psd_info->mode == BitmapMode) || (psd_info->mode == DuotoneMode) ||
1400 (psd_info->mode == GrayscaleMode))
1401 SetImageColorspace(layer_info->image,GRAYColorspace,exception);
1403 Set up some hidden attributes for folks that need them.
1405 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
1406 (double) layer_info->page.x);
1407 (void) SetImageArtifact(layer_info->image,"psd:layer.x",message);
1408 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
1409 (double) layer_info->page.y);
1410 (void) SetImageArtifact(layer_info->image,"psd:layer.y",message);
1411 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",(double)
1412 layer_info->opacity);
1413 (void) SetImageArtifact(layer_info->image,"psd:layer.opacity",message);
1414 (void) SetImageProperty(layer_info->image,"label",(char *) layer_info->name,
1418 for (j=0; j < (ssize_t) layer_info->channels; j++)
1420 if (image->debug != MagickFalse)
1421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1422 " reading data for channel %.20g",(double) j);
1424 compression=(PSDCompressionType) ReadBlobShort(layer_info->image);
1425 layer_info->image->compression=ConvertPSDCompression(compression);
1426 if (layer_info->channel_info[j].type == -1)
1427 layer_info->image->alpha_trait=BlendPixelTrait;
1429 status=ReadPSDChannel(layer_info->image,image_info,psd_info,layer_info,j,
1430 compression,exception);
1432 if (status == MagickFalse)
1436 if (status != MagickFalse)
1437 status=ApplyPSDLayerOpacity(layer_info->image,layer_info->opacity,
1438 MagickFalse,exception);
1440 if ((status != MagickFalse) &&
1441 (layer_info->image->colorspace == CMYKColorspace))
1442 status=NegateCMYK(layer_info->image,exception);
1444 if ((status != MagickFalse) && (layer_info->mask.image != (Image *) NULL))
1449 layer_info->mask.image->page.x=layer_info->mask.page.x;
1450 layer_info->mask.image->page.y=layer_info->mask.page.y;
1451 /* Do not composite the mask when it is disabled */
1452 if ((layer_info->mask.flags & 0x02) == 0x02)
1453 layer_info->mask.image->compose=NoCompositeOp;
1455 status=ApplyPSDOpacityMask(layer_info->image,layer_info->mask.image,
1456 layer_info->mask.background == 0 ? 0 : QuantumRange,MagickFalse,
1458 option=GetImageOption(image_info,"psd:preserve-opacity-mask");
1459 if (IsStringTrue(option) != MagickFalse)
1460 PreservePSDOpacityMask(image,layer_info,exception);
1461 layer_info->mask.image=DestroyImage(layer_info->mask.image);
1467 static MagickBooleanType ReadPSDLayersInternal(Image *image,
1468 const ImageInfo *image_info,const PSDInfo *psd_info,
1469 const MagickBooleanType skip_layers,ExceptionInfo *exception)
1491 size=GetPSDSize(psd_info,image);
1495 Skip layers & masks.
1497 (void) ReadBlobLong(image);
1498 count=ReadBlob(image,4,(unsigned char *) type);
1499 ReversePSDString(image,type,4);
1501 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1505 count=ReadBlob(image,4,(unsigned char *) type);
1506 ReversePSDString(image,type,4);
1507 if ((count != 0) && (LocaleNCompare(type,"Lr16",4) == 0))
1508 size=GetPSDSize(psd_info,image);
1516 layer_info=(LayerInfo *) NULL;
1517 number_layers=(short) ReadBlobShort(image);
1519 if (number_layers < 0)
1522 The first alpha channel in the merged result contains the
1523 transparency data for the merged result.
1525 number_layers=MagickAbsoluteValue(number_layers);
1526 if (image->debug != MagickFalse)
1527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1528 " negative layer count corrected for");
1529 image->alpha_trait=BlendPixelTrait;
1533 We only need to know if the image has an alpha channel
1535 if (skip_layers != MagickFalse)
1538 if (image->debug != MagickFalse)
1539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1540 " image contains %.20g layers",(double) number_layers);
1542 if (number_layers == 0)
1543 ThrowBinaryException(CorruptImageError,"InvalidNumberOfLayers",
1546 layer_info=(LayerInfo *) AcquireQuantumMemory((size_t) number_layers,
1547 sizeof(*layer_info));
1548 if (layer_info == (LayerInfo *) NULL)
1550 if (image->debug != MagickFalse)
1551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1552 " allocation of LayerInfo failed");
1553 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1556 (void) ResetMagickMemory(layer_info,0,(size_t) number_layers*
1557 sizeof(*layer_info));
1559 for (i=0; i < number_layers; i++)
1565 if (image->debug != MagickFalse)
1566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1567 " reading layer #%.20g",(double) i+1);
1568 layer_info[i].page.y=ReadBlobSignedLong(image);
1569 layer_info[i].page.x=ReadBlobSignedLong(image);
1570 y=ReadBlobSignedLong(image);
1571 x=ReadBlobSignedLong(image);
1572 layer_info[i].page.width=(size_t) (x-layer_info[i].page.x);
1573 layer_info[i].page.height=(size_t) (y-layer_info[i].page.y);
1574 layer_info[i].channels=ReadBlobShort(image);
1575 if (layer_info[i].channels > MaxPSDChannels)
1577 layer_info=DestroyLayerInfo(layer_info,number_layers);
1578 ThrowBinaryException(CorruptImageError,"MaximumChannelsExceeded",
1581 if (image->debug != MagickFalse)
1582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1583 " offset(%.20g,%.20g), size(%.20g,%.20g), channels=%.20g",
1584 (double) layer_info[i].page.x,(double) layer_info[i].page.y,
1585 (double) layer_info[i].page.height,(double)
1586 layer_info[i].page.width,(double) layer_info[i].channels);
1587 for (j=0; j < (ssize_t) layer_info[i].channels; j++)
1589 layer_info[i].channel_info[j].type=(short) ReadBlobShort(image);
1590 layer_info[i].channel_info[j].size=(size_t) GetPSDSize(psd_info,
1592 if (image->debug != MagickFalse)
1593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1594 " channel[%.20g]: type=%.20g, size=%.20g",(double) j,
1595 (double) layer_info[i].channel_info[j].type,
1596 (double) layer_info[i].channel_info[j].size);
1598 count=ReadBlob(image,4,(unsigned char *) type);
1599 ReversePSDString(image,type,4);
1600 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1602 if (image->debug != MagickFalse)
1603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1604 " layer type was %.4s instead of 8BIM", type);
1605 layer_info=DestroyLayerInfo(layer_info,number_layers);
1606 ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
1609 count=ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey);
1610 ReversePSDString(image,layer_info[i].blendkey,4);
1611 layer_info[i].opacity=(Quantum) ScaleCharToQuantum((unsigned char)
1612 ReadBlobByte(image));
1613 layer_info[i].clipping=(unsigned char) ReadBlobByte(image);
1614 layer_info[i].flags=(unsigned char) ReadBlobByte(image);
1615 layer_info[i].visible=!(layer_info[i].flags & 0x02);
1616 if (image->debug != MagickFalse)
1617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1618 " blend=%.4s, opacity=%.20g, clipping=%s, flags=%d, visible=%s",
1619 layer_info[i].blendkey,(double) layer_info[i].opacity,
1620 layer_info[i].clipping ? "true" : "false",layer_info[i].flags,
1621 layer_info[i].visible ? "true" : "false");
1622 (void) ReadBlobByte(image); /* filler */
1624 size=ReadBlobLong(image);
1631 if (image->debug != MagickFalse)
1632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1633 " layer contains additional info");
1634 length=ReadBlobLong(image);
1635 combined_length=length+4;
1641 layer_info[i].mask.page.y=ReadBlobSignedLong(image);
1642 layer_info[i].mask.page.x=ReadBlobSignedLong(image);
1643 layer_info[i].mask.page.height=(size_t) (ReadBlobLong(image)-
1644 layer_info[i].mask.page.y);
1645 layer_info[i].mask.page.width=(size_t) (ReadBlobLong(image)-
1646 layer_info[i].mask.page.x);
1647 layer_info[i].mask.background=(unsigned char) ReadBlobByte(
1649 layer_info[i].mask.flags=(unsigned char) ReadBlobByte(image);
1650 if (!(layer_info[i].mask.flags & 0x01))
1652 layer_info[i].mask.page.y=layer_info[i].mask.page.y-
1653 layer_info[i].page.y;
1654 layer_info[i].mask.page.x=layer_info[i].mask.page.x-
1655 layer_info[i].page.x;
1657 if (image->debug != MagickFalse)
1658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1659 " layer mask: offset(%.20g,%.20g), size(%.20g,%.20g), length=%.20g",
1660 (double) layer_info[i].mask.page.x,(double)
1661 layer_info[i].mask.page.y,(double) layer_info[i].mask.page.width,
1662 (double) layer_info[i].mask.page.height,(double)
1663 ((MagickOffsetType) length)-18);
1665 Skip over the rest of the layer mask information.
1667 if (DiscardBlobBytes(image,(MagickSizeType) (length-18)) == MagickFalse)
1669 layer_info=DestroyLayerInfo(layer_info,number_layers);
1670 ThrowBinaryException(CorruptImageError,
1671 "UnexpectedEndOfFile",image->filename);
1674 length=ReadBlobLong(image);
1675 combined_length+=length+4;
1679 Layer blending ranges info.
1681 if (image->debug != MagickFalse)
1682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1683 " layer blending ranges: length=%.20g",(double)
1684 ((MagickOffsetType) length));
1686 We read it, but don't use it...
1688 for (j=0; j < (ssize_t) length; j+=8)
1690 size_t blend_source=ReadBlobLong(image);
1691 size_t blend_dest=ReadBlobLong(image);
1692 if (image->debug != MagickFalse)
1693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1694 " source(%x), dest(%x)",(unsigned int)
1695 blend_source,(unsigned int) blend_dest);
1701 length=(MagickSizeType) (unsigned char) ReadBlobByte(image);
1702 combined_length+=length+1;
1704 (void) ReadBlob(image,(size_t) length++,layer_info[i].name);
1705 layer_info[i].name[length]='\0';
1706 if (image->debug != MagickFalse)
1707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1708 " layer name: %s",layer_info[i].name);
1709 if ((length % 4) != 0)
1711 length=4-(length % 4);
1712 combined_length+=length;
1713 /* Skip over the padding of the layer name */
1714 if (DiscardBlobBytes(image,length) == MagickFalse)
1716 layer_info=DestroyLayerInfo(layer_info,number_layers);
1717 ThrowBinaryException(CorruptImageError,
1718 "UnexpectedEndOfFile",image->filename);
1721 length=(MagickSizeType) size-combined_length;
1727 layer_info[i].info=AcquireStringInfo((const size_t) length);
1728 info=GetStringInfoDatum(layer_info[i].info);
1729 (void) ReadBlob(image,(const size_t) length,info);
1734 for (i=0; i < number_layers; i++)
1736 if ((layer_info[i].page.width == 0) ||
1737 (layer_info[i].page.height == 0))
1739 if (image->debug != MagickFalse)
1740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1741 " layer data is empty");
1742 if (layer_info[i].info != (StringInfo *) NULL)
1743 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
1748 Allocate layered image.
1750 layer_info[i].image=CloneImage(image,layer_info[i].page.width,
1751 layer_info[i].page.height,MagickFalse,exception);
1752 if (layer_info[i].image == (Image *) NULL)
1754 layer_info=DestroyLayerInfo(layer_info,number_layers);
1755 if (image->debug != MagickFalse)
1756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1757 " allocation of image for layer %.20g failed",(double) i);
1758 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1762 if (layer_info[i].info != (StringInfo *) NULL)
1764 (void) SetImageProfile(layer_info[i].image,"psd:additional-info",
1765 layer_info[i].info,exception);
1766 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
1770 if (image_info->ping == MagickFalse)
1772 for (i=0; i < number_layers; i++)
1774 if (layer_info[i].image == (Image *) NULL)
1776 for (j=0; j < layer_info[i].channels; j++)
1778 if (DiscardBlobBytes(image,(MagickSizeType)
1779 layer_info[i].channel_info[j].size) == MagickFalse)
1781 layer_info=DestroyLayerInfo(layer_info,number_layers);
1782 ThrowBinaryException(CorruptImageError,
1783 "UnexpectedEndOfFile",image->filename);
1789 if (image->debug != MagickFalse)
1790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1791 " reading data for layer %.20g",(double) i);
1793 status=ReadPSDLayer(image,image_info,psd_info,&layer_info[i],
1795 if (status == MagickFalse)
1798 status=SetImageProgress(image,LoadImagesTag,i,(MagickSizeType)
1800 if (status == MagickFalse)
1805 if (status != MagickFalse)
1807 for (i=0; i < number_layers; i++)
1809 if (layer_info[i].image == (Image *) NULL)
1811 for (j=i; j < number_layers - 1; j++)
1812 layer_info[j] = layer_info[j+1];
1818 if (number_layers > 0)
1820 for (i=0; i < number_layers; i++)
1823 layer_info[i].image->previous=layer_info[i-1].image;
1824 if (i < (number_layers-1))
1825 layer_info[i].image->next=layer_info[i+1].image;
1826 layer_info[i].image->page=layer_info[i].page;
1828 image->next=layer_info[0].image;
1829 layer_info[0].image->previous=image;
1831 layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
1834 layer_info=DestroyLayerInfo(layer_info,number_layers);
1840 ModuleExport MagickBooleanType ReadPSDLayers(Image *image,
1841 const ImageInfo *image_info,const PSDInfo *psd_info,
1842 const MagickBooleanType skip_layers,ExceptionInfo *exception)
1850 domain=CoderPolicyDomain;
1851 rights=ReadPolicyRights;
1852 if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
1853 return(MagickFalse);
1854 return(ReadPSDLayersInternal(image,image_info,psd_info,skip_layers,
1858 static MagickBooleanType ReadPSDMergedImage(const ImageInfo *image_info,
1859 Image *image,const PSDInfo *psd_info,ExceptionInfo *exception)
1873 compression=(PSDCompressionType) ReadBlobMSBShort(image);
1874 image->compression=ConvertPSDCompression(compression);
1876 if (compression != Raw && compression != RLE)
1878 (void) ThrowMagickException(exception,GetMagickModule(),
1879 TypeWarning,"CompressionNotSupported","'%.20g'",(double) compression);
1880 return(MagickFalse);
1883 sizes=(MagickOffsetType *) NULL;
1884 if (compression == RLE)
1886 sizes=ReadPSDRLESizes(image,psd_info,image->rows*psd_info->channels);
1887 if (sizes == (MagickOffsetType *) NULL)
1888 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1893 for (i=0; i < (ssize_t) psd_info->channels; i++)
1895 if (compression == RLE)
1896 status=ReadPSDChannelRLE(image,psd_info,i,sizes+(i*image->rows),
1899 status=ReadPSDChannelRaw(image,psd_info->channels,i,exception);
1901 if (status != MagickFalse)
1902 status=SetImageProgress(image,LoadImagesTag,i,psd_info->channels);
1904 if (status == MagickFalse)
1908 if ((status != MagickFalse) && (image->colorspace == CMYKColorspace))
1909 status=NegateCMYK(image,exception);
1911 if (status != MagickFalse)
1912 status=CorrectPSDAlphaBlend(image_info,image,exception);
1914 sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
1919 static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
1952 assert(image_info != (const ImageInfo *) NULL);
1953 assert(image_info->signature == MagickCoreSignature);
1954 if (image_info->debug != MagickFalse)
1955 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1956 image_info->filename);
1957 assert(exception != (ExceptionInfo *) NULL);
1958 assert(exception->signature == MagickCoreSignature);
1960 image=AcquireImage(image_info,exception);
1961 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1962 if (status == MagickFalse)
1964 image=DestroyImageList(image);
1965 return((Image *) NULL);
1970 image->endian=MSBEndian;
1971 count=ReadBlob(image,4,(unsigned char *) psd_info.signature);
1972 psd_info.version=ReadBlobMSBShort(image);
1973 if ((count == 0) || (LocaleNCompare(psd_info.signature,"8BPS",4) != 0) ||
1974 ((psd_info.version != 1) && (psd_info.version != 2)))
1975 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1976 (void) ReadBlob(image,6,psd_info.reserved);
1977 psd_info.channels=ReadBlobMSBShort(image);
1978 if (psd_info.channels > MaxPSDChannels)
1979 ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded");
1980 psd_info.rows=ReadBlobMSBLong(image);
1981 psd_info.columns=ReadBlobMSBLong(image);
1982 if ((psd_info.version == 1) && ((psd_info.rows > 30000) ||
1983 (psd_info.columns > 30000)))
1984 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1985 psd_info.depth=ReadBlobMSBShort(image);
1986 if ((psd_info.depth != 1) && (psd_info.depth != 8) && (psd_info.depth != 16))
1987 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1988 psd_info.mode=ReadBlobMSBShort(image);
1989 if (image->debug != MagickFalse)
1990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1991 " Image is %.20g x %.20g with channels=%.20g, depth=%.20g, mode=%s",
1992 (double) psd_info.columns,(double) psd_info.rows,(double)
1993 psd_info.channels,(double) psd_info.depth,ModeToString((PSDImageType)
1998 image->depth=psd_info.depth;
1999 image->columns=psd_info.columns;
2000 image->rows=psd_info.rows;
2001 status=SetImageExtent(image,image->columns,image->rows,exception);
2002 if (status == MagickFalse)
2003 return(DestroyImageList(image));
2004 if (SetImageBackgroundColor(image,exception) == MagickFalse)
2006 image=DestroyImageList(image);
2007 return((Image *) NULL);
2009 if (psd_info.mode == LabMode)
2010 SetImageColorspace(image,LabColorspace,exception);
2011 if (psd_info.mode == CMYKMode)
2013 SetImageColorspace(image,CMYKColorspace,exception);
2014 if (psd_info.channels > 4)
2015 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2017 else if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) ||
2018 (psd_info.mode == DuotoneMode))
2020 status=AcquireImageColormap(image,psd_info.depth != 16 ? 256 : 65536,
2022 if (status == MagickFalse)
2023 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2024 if (image->debug != MagickFalse)
2025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2026 " Image colormap allocated");
2027 SetImageColorspace(image,GRAYColorspace,exception);
2028 if (psd_info.channels > 1)
2029 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2032 if (psd_info.channels > 3)
2033 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2035 Read PSD raster colormap only present for indexed and duotone images.
2037 length=ReadBlobMSBLong(image);
2040 if (image->debug != MagickFalse)
2041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2042 " reading colormap");
2043 if (psd_info.mode == DuotoneMode)
2046 Duotone image data; the format of this data is undocumented.
2048 data=(unsigned char *) AcquireQuantumMemory((size_t) length,
2050 if (data == (unsigned char *) NULL)
2051 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2052 (void) ReadBlob(image,(size_t) length,data);
2053 data=(unsigned char *) RelinquishMagickMemory(data);
2061 Read PSD raster colormap.
2063 number_colors=length/3;
2064 if (number_colors > 65536)
2065 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2066 if (AcquireImageColormap(image,number_colors,exception) == MagickFalse)
2067 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2068 for (i=0; i < (ssize_t) image->colors; i++)
2069 image->colormap[i].red=ScaleCharToQuantum((unsigned char)
2070 ReadBlobByte(image));
2071 for (i=0; i < (ssize_t) image->colors; i++)
2072 image->colormap[i].green=ScaleCharToQuantum((unsigned char)
2073 ReadBlobByte(image));
2074 for (i=0; i < (ssize_t) image->colors; i++)
2075 image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
2076 ReadBlobByte(image));
2077 image->alpha_trait=UndefinedPixelTrait;
2080 if ((image->depth == 1) && (image->storage_class != PseudoClass))
2081 ThrowReaderException(CorruptImageError, "ImproperImageHeader");
2082 has_merged_image=MagickTrue;
2083 length=ReadBlobMSBLong(image);
2090 Image resources block.
2092 if (image->debug != MagickFalse)
2093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2094 " reading image resource blocks - %.20g bytes",(double)
2095 ((MagickOffsetType) length));
2096 blocks=(unsigned char *) AcquireQuantumMemory((size_t) length,
2098 if (blocks == (unsigned char *) NULL)
2099 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2100 count=ReadBlob(image,(size_t) length,blocks);
2101 if ((count != (ssize_t) length) || (length < 4) ||
2102 (LocaleNCompare((char *) blocks,"8BIM",4) != 0))
2104 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
2105 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2107 ParseImageResourceBlocks(image,blocks,(size_t) length,&has_merged_image,
2109 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
2112 Layer and mask block.
2114 length=GetPSDSize(&psd_info,image);
2117 length=ReadBlobMSBLong(image);
2118 length=ReadBlobMSBLong(image);
2120 offset=TellBlob(image);
2121 skip_layers=MagickFalse;
2122 if ((image_info->number_scenes == 1) && (image_info->scene == 0) &&
2123 (has_merged_image != MagickFalse))
2125 if (image->debug != MagickFalse)
2126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2127 " read composite only");
2128 skip_layers=MagickTrue;
2132 if (image->debug != MagickFalse)
2133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2134 " image has no layers");
2138 if (ReadPSDLayersInternal(image,image_info,&psd_info,skip_layers,
2139 exception) != MagickTrue)
2141 (void) CloseBlob(image);
2142 image=DestroyImageList(image);
2143 return((Image *) NULL);
2147 Skip the rest of the layer and mask information.
2149 SeekBlob(image,offset+length,SEEK_SET);
2152 If we are only "pinging" the image, then we're done - so return.
2154 if (image_info->ping != MagickFalse)
2156 (void) CloseBlob(image);
2157 return(GetFirstImageInList(image));
2160 Read the precombined layer, present for PSD < 4 compatibility.
2162 if (image->debug != MagickFalse)
2163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2164 " reading the precombined layer");
2165 if ((has_merged_image != MagickFalse) || (GetImageListLength(image) == 1))
2166 has_merged_image=(MagickBooleanType) ReadPSDMergedImage(image_info,image,
2167 &psd_info,exception);
2168 if ((has_merged_image == MagickFalse) && (GetImageListLength(image) == 1) &&
2171 SeekBlob(image,offset,SEEK_SET);
2172 status=ReadPSDLayersInternal(image,image_info,&psd_info,MagickFalse,
2174 if (status != MagickTrue)
2176 (void) CloseBlob(image);
2177 image=DestroyImageList(image);
2178 return((Image *) NULL);
2181 if (has_merged_image == MagickFalse)
2186 if (GetImageListLength(image) == 1)
2187 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
2188 SetImageAlphaChannel(image,TransparentAlphaChannel,exception);
2189 image->background_color.alpha=TransparentAlpha;
2190 image->background_color.alpha_trait=BlendPixelTrait;
2191 merged=MergeImageLayers(image,FlattenLayer,exception);
2192 ReplaceImageInList(&image,merged);
2194 (void) CloseBlob(image);
2195 return(GetFirstImageInList(image));
2199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2203 % R e g i s t e r P S D I m a g e %
2207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2209 % RegisterPSDImage() adds properties for the PSD image format to
2210 % the list of supported formats. The properties include the image format
2211 % tag, a method to read and/or write the format, whether the format
2212 % supports the saving of more than one frame to the same file or blob,
2213 % whether the format supports native in-memory I/O, and a brief
2214 % description of the format.
2216 % The format of the RegisterPSDImage method is:
2218 % size_t RegisterPSDImage(void)
2221 ModuleExport size_t RegisterPSDImage(void)
2226 entry=AcquireMagickInfo("PSD","PSB","Adobe Large Document Format");
2227 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2228 entry->encoder=(EncodeImageHandler *) WritePSDImage;
2229 entry->magick=(IsImageFormatHandler *) IsPSD;
2230 entry->flags|=CoderDecoderSeekableStreamFlag;
2231 entry->flags|=CoderEncoderSeekableStreamFlag;
2232 (void) RegisterMagickInfo(entry);
2233 entry=AcquireMagickInfo("PSD","PSD","Adobe Photoshop bitmap");
2234 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2235 entry->encoder=(EncodeImageHandler *) WritePSDImage;
2236 entry->magick=(IsImageFormatHandler *) IsPSD;
2237 entry->flags|=CoderDecoderSeekableStreamFlag;
2238 entry->flags|=CoderEncoderSeekableStreamFlag;
2239 (void) RegisterMagickInfo(entry);
2240 return(MagickImageCoderSignature);
2244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2248 % U n r e g i s t e r P S D I m a g e %
2252 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2254 % UnregisterPSDImage() removes format registrations made by the
2255 % PSD module from the list of supported formats.
2257 % The format of the UnregisterPSDImage method is:
2259 % UnregisterPSDImage(void)
2262 ModuleExport void UnregisterPSDImage(void)
2264 (void) UnregisterMagickInfo("PSB");
2265 (void) UnregisterMagickInfo("PSD");
2269 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2273 % W r i t e P S D I m a g e %
2277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2279 % WritePSDImage() writes an image in the Adobe Photoshop encoded image format.
2281 % The format of the WritePSDImage method is:
2283 % MagickBooleanType WritePSDImage(const ImageInfo *image_info,Image *image,
2284 % ExceptionInfo *exception)
2286 % A description of each parameter follows.
2288 % o image_info: the image info.
2290 % o image: The image.
2292 % o exception: return any errors or warnings in this structure.
2296 static inline ssize_t SetPSDOffset(const PSDInfo *psd_info,Image *image,
2297 const size_t offset)
2299 if (psd_info->version == 1)
2300 return(WriteBlobMSBShort(image,(unsigned short) offset));
2301 return(WriteBlobMSBLong(image,(unsigned short) offset));
2304 static inline ssize_t WritePSDOffset(const PSDInfo *psd_info,Image *image,
2305 const MagickSizeType size,const MagickSizeType offset)
2313 current_offset=TellBlob(image);
2314 SeekBlob(image,offset,SEEK_SET);
2315 if (psd_info->version == 1)
2316 result=WriteBlobMSBShort(image,(unsigned short) size);
2318 result=(WriteBlobMSBLong(image,(unsigned short) size));
2319 SeekBlob(image,current_offset,SEEK_SET);
2323 static inline ssize_t SetPSDSize(const PSDInfo *psd_info,Image *image,
2324 const MagickSizeType size)
2326 if (psd_info->version == 1)
2327 return(WriteBlobMSBLong(image,(unsigned int) size));
2328 return(WriteBlobMSBLongLong(image,size));
2331 static inline ssize_t WritePSDSize(const PSDInfo *psd_info,Image *image,
2332 const MagickSizeType size,const MagickSizeType offset)
2340 current_offset=TellBlob(image);
2341 SeekBlob(image,offset,SEEK_SET);
2342 if (psd_info->version == 1)
2343 result=WriteBlobMSBLong(image,(unsigned int) size);
2345 result=WriteBlobMSBLongLong(image,size);
2346 SeekBlob(image,current_offset,SEEK_SET);
2350 static size_t PSDPackbitsEncodeImage(Image *image,const size_t length,
2351 const unsigned char *pixels,unsigned char *compact_pixels,
2352 ExceptionInfo *exception)
2361 register unsigned char
2368 Compress pixels with Packbits encoding.
2370 assert(image != (Image *) NULL);
2371 assert(image->signature == MagickCoreSignature);
2372 if (image->debug != MagickFalse)
2373 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2374 assert(pixels != (unsigned char *) NULL);
2375 assert(compact_pixels != (unsigned char *) NULL);
2376 packbits=(unsigned char *) AcquireQuantumMemory(128UL,sizeof(*packbits));
2377 if (packbits == (unsigned char *) NULL)
2378 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2381 for (i=(ssize_t) length; i != 0; )
2388 *q++=(unsigned char) 0;
2395 *q++=(unsigned char) 1;
2403 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2405 *q++=(unsigned char) ((256-3)+1);
2409 *q++=(unsigned char) 2;
2417 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2423 while (((ssize_t) count < i) && (*pixels == *(pixels+count)))
2430 *q++=(unsigned char) ((256-count)+1);
2439 while ((*(pixels+count) != *(pixels+count+1)) ||
2440 (*(pixels+count+1) != *(pixels+count+2)))
2442 packbits[count+1]=pixels[count];
2444 if (((ssize_t) count >= (i-3)) || (count >= 127))
2448 *packbits=(unsigned char) (count-1);
2449 for (j=0; j <= (ssize_t) count; j++)
2456 *q++=(unsigned char) 128; /* EOD marker */
2457 packbits=(unsigned char *) RelinquishMagickMemory(packbits);
2458 return((size_t) (q-compact_pixels));
2461 static size_t WriteCompressionStart(const PSDInfo *psd_info,Image *image,
2462 const Image *next_image,const ssize_t channels)
2471 if (next_image->compression == RLECompression)
2473 length=WriteBlobMSBShort(image,RLE);
2474 for (i=0; i < channels; i++)
2475 for (y=0; y < (ssize_t) next_image->rows; y++)
2476 length+=SetPSDOffset(psd_info,image,0);
2478 #ifdef MAGICKCORE_ZLIB_DELEGATE
2479 else if (next_image->compression == ZipCompression)
2480 length=WriteBlobMSBShort(image,ZipWithoutPrediction);
2483 length=WriteBlobMSBShort(image,Raw);
2487 static size_t WritePSDChannel(const PSDInfo *psd_info,
2488 const ImageInfo *image_info,Image *image,Image *next_image,
2489 const QuantumType quantum_type, unsigned char *compact_pixels,
2490 MagickOffsetType size_offset,const MagickBooleanType separate,
2491 ExceptionInfo *exception)
2502 register const Quantum
2515 #ifdef MAGICKCORE_ZLIB_DELEGATE
2529 compressed_pixels=(unsigned char *) NULL;
2533 if (separate != MagickFalse)
2535 size_offset=TellBlob(image)+2;
2536 count+=WriteCompressionStart(psd_info,image,next_image,1);
2538 if (next_image->depth > 8)
2539 next_image->depth=16;
2540 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
2541 MagickTrue : MagickFalse;
2542 quantum_info=AcquireQuantumInfo(image_info,next_image);
2543 if (quantum_info == (QuantumInfo *) NULL)
2545 pixels=(unsigned char *) GetQuantumPixels(quantum_info);
2546 #ifdef MAGICKCORE_ZLIB_DELEGATE
2547 if (next_image->compression == ZipCompression)
2549 compressed_pixels=(unsigned char *) AcquireQuantumMemory(CHUNK,
2550 sizeof(*compressed_pixels));
2551 if (compressed_pixels == (unsigned char *) NULL)
2553 quantum_info=DestroyQuantumInfo(quantum_info);
2556 ResetMagickMemory(&stream,0,sizeof(stream));
2557 stream.data_type=Z_BINARY;
2558 level=Z_DEFAULT_COMPRESSION;
2559 if ((image_info->quality > 0 && image_info->quality < 10))
2560 level=(int) image_info->quality;
2561 if (deflateInit(&stream,level) != Z_OK)
2563 quantum_info=DestroyQuantumInfo(quantum_info);
2568 for (y=0; y < (ssize_t) next_image->rows; y++)
2570 p=GetVirtualPixels(next_image,0,y,next_image->columns,1,exception);
2571 if (p == (const Quantum *) NULL)
2573 length=ExportQuantumPixels(next_image,(CacheView *) NULL,quantum_info,
2574 quantum_type,pixels,exception);
2575 if (monochrome != MagickFalse)
2576 for (i=0; i < (ssize_t) length; i++)
2577 pixels[i]=(~pixels[i]);
2578 if (next_image->compression == RLECompression)
2580 length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels,
2582 count+=WriteBlob(image,length,compact_pixels);
2583 size_offset+=WritePSDOffset(psd_info,image,length,size_offset);
2585 #ifdef MAGICKCORE_ZLIB_DELEGATE
2586 else if (next_image->compression == ZipCompression)
2588 stream.avail_in=(uInt) length;
2589 stream.next_in=(Bytef *) pixels;
2590 if (y == (ssize_t) next_image->rows-1)
2593 stream.avail_out=(uInt) CHUNK;
2594 stream.next_out=(Bytef *) compressed_pixels;
2595 if (deflate(&stream,flush) == Z_STREAM_ERROR)
2597 length=(size_t) CHUNK-stream.avail_out;
2599 count+=WriteBlob(image,length,compressed_pixels);
2600 } while (stream.avail_out == 0);
2604 count+=WriteBlob(image,length,pixels);
2606 #ifdef MAGICKCORE_ZLIB_DELEGATE
2607 if (next_image->compression == ZipCompression)
2609 (void) deflateEnd(&stream);
2610 compressed_pixels=(unsigned char *) RelinquishMagickMemory(
2614 quantum_info=DestroyQuantumInfo(quantum_info);
2618 static unsigned char *AcquireCompactPixels(const Image *image,
2619 ExceptionInfo *exception)
2627 packet_size=image->depth > 8UL ? 2UL : 1UL;
2628 compact_pixels=(unsigned char *) AcquireQuantumMemory((9*
2629 image->columns)+1,packet_size*sizeof(*compact_pixels));
2630 if (compact_pixels == (unsigned char *) NULL)
2632 (void) ThrowMagickException(exception,GetMagickModule(),
2633 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2635 return(compact_pixels);
2638 static size_t WritePSDChannels(const PSDInfo *psd_info,
2639 const ImageInfo *image_info,Image *image,Image *next_image,
2640 MagickOffsetType size_offset,const MagickBooleanType separate,
2641 ExceptionInfo *exception)
2661 compact_pixels=(unsigned char *) NULL;
2662 if (next_image->compression == RLECompression)
2664 compact_pixels=AcquireCompactPixels(next_image,exception);
2665 if (compact_pixels == (unsigned char *) NULL)
2669 if (separate == MagickFalse)
2671 if (next_image->storage_class != PseudoClass)
2673 if (IsImageGray(next_image) == MagickFalse)
2674 channels=next_image->colorspace == CMYKColorspace ? 4 : 3;
2675 if (next_image->alpha_trait != UndefinedPixelTrait)
2678 rows_offset=TellBlob(image)+2;
2679 count+=WriteCompressionStart(psd_info,image,next_image,channels);
2680 offset_length=(next_image->rows*(psd_info->version == 1 ? 2 : 4));
2683 if (next_image->storage_class == PseudoClass)
2685 length=WritePSDChannel(psd_info,image_info,image,next_image,
2686 IndexQuantum,compact_pixels,rows_offset,separate,exception);
2687 if (separate != MagickFalse)
2688 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2690 rows_offset+=offset_length;
2695 if (IsImageGray(next_image) != MagickFalse)
2697 length=WritePSDChannel(psd_info,image_info,image,next_image,
2698 GrayQuantum,compact_pixels,rows_offset,separate,exception);
2699 if (separate != MagickFalse)
2700 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2702 rows_offset+=offset_length;
2707 if (next_image->colorspace == CMYKColorspace)
2708 (void) NegateCMYK(next_image,exception);
2710 length=WritePSDChannel(psd_info,image_info,image,next_image,
2711 RedQuantum,compact_pixels,rows_offset,separate,exception);
2712 if (separate != MagickFalse)
2713 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2715 rows_offset+=offset_length;
2718 length=WritePSDChannel(psd_info,image_info,image,next_image,
2719 GreenQuantum,compact_pixels,rows_offset,separate,exception);
2720 if (separate != MagickFalse)
2721 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2723 rows_offset+=offset_length;
2726 length=WritePSDChannel(psd_info,image_info,image,next_image,
2727 BlueQuantum,compact_pixels,rows_offset,separate,exception);
2728 if (separate != MagickFalse)
2729 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2731 rows_offset+=offset_length;
2734 if (next_image->colorspace == CMYKColorspace)
2736 length=WritePSDChannel(psd_info,image_info,image,next_image,
2737 BlackQuantum,compact_pixels,rows_offset,separate,exception);
2738 if (separate != MagickFalse)
2739 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2741 rows_offset+=offset_length;
2745 if (next_image->alpha_trait != UndefinedPixelTrait)
2747 length=WritePSDChannel(psd_info,image_info,image,next_image,
2748 AlphaQuantum,compact_pixels,rows_offset,separate,exception);
2749 if (separate != MagickFalse)
2750 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2752 rows_offset+=offset_length;
2756 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
2757 if (next_image->colorspace == CMYKColorspace)
2758 (void) NegateCMYK(next_image,exception);
2759 if (separate != MagickFalse)
2764 property=GetImageArtifact(next_image,"psd:opacity-mask");
2765 if (property != (const char *) NULL)
2767 mask=(Image *) GetImageRegistry(ImageRegistryType,property,
2769 if (mask != (Image *) NULL)
2771 if (mask->compression == RLECompression)
2773 compact_pixels=AcquireCompactPixels(mask,exception);
2774 if (compact_pixels == (unsigned char *) NULL)
2777 length=WritePSDChannel(psd_info,image_info,image,mask,
2778 RedQuantum,compact_pixels,rows_offset,MagickTrue,exception);
2779 (void) WritePSDSize(psd_info,image,length,size_offset);
2781 compact_pixels=(unsigned char *) RelinquishMagickMemory(
2789 static size_t WritePascalString(Image *image,const char *value,size_t padding)
2802 length=(strlen(value) > 255UL ) ? 255UL : strlen(value);
2804 count+=WriteBlobByte(image,0);
2807 count+=WriteBlobByte(image,(unsigned char) length);
2808 count+=WriteBlob(image,length,(const unsigned char *) value);
2811 if ((length % padding) == 0)
2813 for (i=0; i < (ssize_t) (padding-(length % padding)); i++)
2814 count+=WriteBlobByte(image,0);
2818 static void WriteResolutionResourceBlock(Image *image)
2827 if (image->units == PixelsPerCentimeterResolution)
2829 x_resolution=2.54*65536.0*image->resolution.x+0.5;
2830 y_resolution=2.54*65536.0*image->resolution.y+0.5;
2835 x_resolution=65536.0*image->resolution.x+0.5;
2836 y_resolution=65536.0*image->resolution.y+0.5;
2839 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
2840 (void) WriteBlobMSBShort(image,0x03ED);
2841 (void) WriteBlobMSBShort(image,0);
2842 (void) WriteBlobMSBLong(image,16); /* resource size */
2843 (void) WriteBlobMSBLong(image,(unsigned int) (x_resolution+0.5));
2844 (void) WriteBlobMSBShort(image,units); /* horizontal resolution unit */
2845 (void) WriteBlobMSBShort(image,units); /* width unit */
2846 (void) WriteBlobMSBLong(image,(unsigned int) (y_resolution+0.5));
2847 (void) WriteBlobMSBShort(image,units); /* vertical resolution unit */
2848 (void) WriteBlobMSBShort(image,units); /* height unit */
2851 static inline size_t WriteChannelSize(const PSDInfo *psd_info,Image *image,
2852 const signed short channel)
2857 count=WriteBlobMSBSignedShort(image,channel);
2858 count+=SetPSDSize(psd_info,image,0);
2862 static void RemoveICCProfileFromResourceBlock(StringInfo *bim_profile)
2864 register const unsigned char
2881 length=GetStringInfoLength(bim_profile);
2884 datum=GetStringInfoDatum(bim_profile);
2885 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
2887 register unsigned char
2890 q=(unsigned char *) p;
2891 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
2893 p=PushLongPixel(MSBEndian,p,&long_sans);
2894 p=PushShortPixel(MSBEndian,p,&id);
2895 p=PushShortPixel(MSBEndian,p,&short_sans);
2896 p=PushLongPixel(MSBEndian,p,&count);
2897 if (id == 0x0000040f)
2902 quantum=PSDQuantum(count)+12;
2903 if ((quantum >= 12) && (quantum < (ssize_t) length))
2905 if ((q+quantum < (datum+length-16)))
2906 (void) CopyMagickMemory(q,q+quantum,length-quantum-(q-datum));
2907 SetStringInfoLength(bim_profile,length-quantum);
2912 if ((count & 0x01) != 0)
2917 static void RemoveResolutionFromResourceBlock(StringInfo *bim_profile)
2919 register const unsigned char
2936 length=GetStringInfoLength(bim_profile);
2939 datum=GetStringInfoDatum(bim_profile);
2940 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
2942 register unsigned char
2948 q=(unsigned char *) p;
2949 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
2951 p=PushLongPixel(MSBEndian,p,&long_sans);
2952 p=PushShortPixel(MSBEndian,p,&id);
2953 p=PushShortPixel(MSBEndian,p,&short_sans);
2954 p=PushLongPixel(MSBEndian,p,&count);
2955 cnt=PSDQuantum(count);
2958 if ((id == 0x000003ed) && (cnt < (ssize_t) (length-12)))
2960 (void) CopyMagickMemory(q,q+cnt+12,length-(cnt+12)-(q-datum));
2961 SetStringInfoLength(bim_profile,length-(cnt+12));
2965 if ((count & 0x01) != 0)
2970 static const StringInfo *GetAdditionalInformation(const ImageInfo *image_info,
2971 Image *image,ExceptionInfo *exception)
2973 #define PSDKeySize 5
2974 #define PSDAllowedLength 36
2979 /* Whitelist of keys from: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ */
2981 allowed[PSDAllowedLength][PSDKeySize] = {
2982 "blnc", "blwh", "brit", "brst", "clbl", "clrL", "curv", "expA", "FMsk",
2983 "GdFl", "grdm", "hue ", "hue2", "infx", "knko", "lclr", "levl", "lnsr",
2984 "lfx2", "luni", "lrFX", "lspf", "lyid", "lyvr", "mixr", "nvrt", "phfl",
2985 "post", "PtFl", "selc", "shpa", "sn2P", "SoCo", "thrs", "tsly", "vibA"
3011 info=GetImageProfile(image,"psd:additional-info");
3012 if (info == (const StringInfo *) NULL)
3013 return((const StringInfo *) NULL);
3014 option=GetImageOption(image_info,"psd:additional-info");
3015 if (LocaleCompare(option,"all") == 0)
3017 if (LocaleCompare(option,"selective") != 0)
3019 profile=RemoveImageProfile(image,"psd:additional-info");
3020 return(DestroyStringInfo(profile));
3022 length=GetStringInfoLength(info);
3023 p=GetStringInfoDatum(info);
3024 remaining_length=length;
3026 while (remaining_length >= 12)
3028 /* skip over signature */
3035 size=(unsigned int) (*p++) << 24;
3036 size|=(unsigned int) (*p++) << 16;
3037 size|=(unsigned int) (*p++) << 8;
3038 size|=(unsigned int) (*p++);
3039 size=size & 0xffffffff;
3040 remaining_length-=12;
3041 if ((size_t) size > remaining_length)
3042 return((const StringInfo *) NULL);
3044 for (i=0; i < PSDAllowedLength; i++)
3046 if (LocaleNCompare(key,allowed[i],PSDKeySize) != 0)
3052 remaining_length-=(size_t) size;
3053 if (found == MagickFalse)
3055 if (remaining_length > 0)
3056 p=(unsigned char *) CopyMagickMemory(p-12,p+size,remaining_length);
3059 length+=(size_t) size+12;
3062 profile=RemoveImageProfile(image,"psd:additional-info");
3064 return(DestroyStringInfo(profile));
3065 SetStringInfoLength(profile,(const size_t) length);
3066 SetImageProfile(image,"psd:additional-info",info,exception);
3070 static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
3071 Image *image,ExceptionInfo *exception)
3074 layer_name[MagickPathExtent];
3091 *layer_size_offsets,
3116 assert(image_info != (const ImageInfo *) NULL);
3117 assert(image_info->signature == MagickCoreSignature);
3118 assert(image != (Image *) NULL);
3119 assert(image->signature == MagickCoreSignature);
3120 if (image->debug != MagickFalse)
3121 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3122 assert(exception != (ExceptionInfo *) NULL);
3123 assert(exception->signature == MagickCoreSignature);
3124 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
3125 if (status == MagickFalse)
3127 packet_size=(size_t) (image->depth > 8 ? 6 : 3);
3128 if (image->alpha_trait != UndefinedPixelTrait)
3129 packet_size+=image->depth > 8 ? 2 : 1;
3131 if ((LocaleCompare(image_info->magick,"PSB") == 0) ||
3132 (image->columns > 30000) || (image->rows > 30000))
3134 (void) WriteBlob(image,4,(const unsigned char *) "8BPS");
3135 (void) WriteBlobMSBShort(image,psd_info.version); /* version */
3136 for (i=1; i <= 6; i++)
3137 (void) WriteBlobByte(image, 0); /* 6 bytes of reserved */
3138 if (SetImageGray(image,exception) != MagickFalse)
3139 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
3141 if ((image_info->type != TrueColorType) && (image_info->type !=
3142 TrueColorAlphaType) && (image->storage_class == PseudoClass))
3143 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
3146 if (image->storage_class == PseudoClass)
3147 (void) SetImageStorageClass(image,DirectClass,exception);
3148 if (image->colorspace != CMYKColorspace)
3149 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 4UL : 3UL);
3151 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 5UL : 4UL);
3153 (void) WriteBlobMSBShort(image,(unsigned short) num_channels);
3154 (void) WriteBlobMSBLong(image,(unsigned int) image->rows);
3155 (void) WriteBlobMSBLong(image,(unsigned int) image->columns);
3156 if (IsImageGray(image) != MagickFalse)
3164 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
3165 MagickTrue : MagickFalse;
3166 (void) WriteBlobMSBShort(image,(unsigned short)
3167 (monochrome != MagickFalse ? 1 : image->depth > 8 ? 16 : 8));
3168 (void) WriteBlobMSBShort(image,(unsigned short)
3169 (monochrome != MagickFalse ? BitmapMode : GrayscaleMode));
3173 (void) WriteBlobMSBShort(image,(unsigned short) (image->storage_class ==
3174 PseudoClass ? 8 : image->depth > 8 ? 16 : 8));
3176 if (((image_info->colorspace != UndefinedColorspace) ||
3177 (image->colorspace != CMYKColorspace)) &&
3178 (image_info->colorspace != CMYKColorspace))
3180 (void) TransformImageColorspace(image,sRGBColorspace,exception);
3181 (void) WriteBlobMSBShort(image,(unsigned short)
3182 (image->storage_class == PseudoClass ? IndexedMode : RGBMode));
3186 if (image->colorspace != CMYKColorspace)
3187 (void) TransformImageColorspace(image,CMYKColorspace,exception);
3188 (void) WriteBlobMSBShort(image,CMYKMode);
3191 if ((IsImageGray(image) != MagickFalse) ||
3192 (image->storage_class == DirectClass) || (image->colors > 256))
3193 (void) WriteBlobMSBLong(image,0);
3197 Write PSD raster colormap.
3199 (void) WriteBlobMSBLong(image,768);
3200 for (i=0; i < (ssize_t) image->colors; i++)
3201 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].red));
3202 for ( ; i < 256; i++)
3203 (void) WriteBlobByte(image,0);
3204 for (i=0; i < (ssize_t) image->colors; i++)
3205 (void) WriteBlobByte(image,ScaleQuantumToChar(
3206 image->colormap[i].green));
3207 for ( ; i < 256; i++)
3208 (void) WriteBlobByte(image,0);
3209 for (i=0; i < (ssize_t) image->colors; i++)
3210 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].blue));
3211 for ( ; i < 256; i++)
3212 (void) WriteBlobByte(image,0);
3215 Image resource block.
3217 length=28; /* 0x03EB */
3218 bim_profile=(StringInfo *) GetImageProfile(image,"8bim");
3219 icc_profile=GetImageProfile(image,"icc");
3220 if (bim_profile != (StringInfo *) NULL)
3222 bim_profile=CloneStringInfo(bim_profile);
3223 if (icc_profile != (StringInfo *) NULL)
3224 RemoveICCProfileFromResourceBlock(bim_profile);
3225 RemoveResolutionFromResourceBlock(bim_profile);
3226 length+=PSDQuantum(GetStringInfoLength(bim_profile));
3228 if (icc_profile != (const StringInfo *) NULL)
3229 length+=PSDQuantum(GetStringInfoLength(icc_profile))+12;
3230 (void) WriteBlobMSBLong(image,(unsigned int) length);
3231 WriteResolutionResourceBlock(image);
3232 if (bim_profile != (StringInfo *) NULL)
3234 (void) WriteBlob(image,GetStringInfoLength(bim_profile),
3235 GetStringInfoDatum(bim_profile));
3236 bim_profile=DestroyStringInfo(bim_profile);
3238 if (icc_profile != (StringInfo *) NULL)
3240 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
3241 (void) WriteBlobMSBShort(image,0x0000040F);
3242 (void) WriteBlobMSBShort(image,0);
3243 (void) WriteBlobMSBLong(image,(unsigned int) GetStringInfoLength(
3245 (void) WriteBlob(image,GetStringInfoLength(icc_profile),
3246 GetStringInfoDatum(icc_profile));
3247 if ((MagickOffsetType) GetStringInfoLength(icc_profile) !=
3248 PSDQuantum(GetStringInfoLength(icc_profile)))
3249 (void) WriteBlobByte(image,0);
3251 base_image=GetNextImageInList(image);
3252 if (base_image == (Image *) NULL)
3255 size_offset=TellBlob(image);
3256 SetPSDSize(&psd_info,image,0);
3257 SetPSDSize(&psd_info,image,0);
3259 for (next_image=base_image; next_image != NULL; )
3262 next_image=GetNextImageInList(next_image);
3264 if (image->alpha_trait != UndefinedPixelTrait)
3265 size+=WriteBlobMSBShort(image,-(unsigned short) layer_count);
3267 size+=WriteBlobMSBShort(image,(unsigned short) layer_count);
3268 layer_size_offsets=(MagickOffsetType *) AcquireQuantumMemory(
3269 (size_t) layer_count,sizeof(MagickOffsetType));
3270 if (layer_size_offsets == (MagickOffsetType *) NULL)
3271 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3273 for (next_image=base_image; next_image != NULL; )
3285 mask=(Image *) NULL;
3286 property=GetImageArtifact(next_image,"psd:opacity-mask");
3288 if (property != (const char *) NULL)
3290 mask=(Image *) GetImageRegistry(ImageRegistryType,property,exception);
3291 default_color=strlen(property) == 9 ? 255 : 0;
3293 size+=WriteBlobMSBLong(image,(unsigned int) next_image->page.y);
3294 size+=WriteBlobMSBLong(image,(unsigned int) next_image->page.x);
3295 size+=WriteBlobMSBLong(image,(unsigned int) (next_image->page.y+
3297 size+=WriteBlobMSBLong(image,(unsigned int) (next_image->page.x+
3298 next_image->columns));
3300 if ((next_image->storage_class != PseudoClass) &&
3301 (IsImageGray(next_image) == MagickFalse))
3302 channels=next_image->colorspace == CMYKColorspace ? 4U : 3U;
3303 total_channels=channels;
3304 if (next_image->alpha_trait != UndefinedPixelTrait)
3306 if (mask != (Image *) NULL)
3308 size+=WriteBlobMSBShort(image,total_channels);
3309 layer_size_offsets[layer_index++]=TellBlob(image);
3310 for (i=0; i < (ssize_t) channels; i++)
3311 size+=WriteChannelSize(&psd_info,image,(signed short) i);
3312 if (next_image->alpha_trait != UndefinedPixelTrait)
3313 size+=WriteChannelSize(&psd_info,image,-1);
3314 if (mask != (Image *) NULL)
3315 size+=WriteChannelSize(&psd_info,image,-2);
3316 size+=WriteBlob(image,4,(const unsigned char *) "8BIM");
3317 size+=WriteBlob(image,4,(const unsigned char *)
3318 CompositeOperatorToPSDBlendMode(next_image->compose));
3319 property=GetImageArtifact(next_image,"psd:layer.opacity");
3320 if (property != (const char *) NULL)
3325 opacity=(Quantum) StringToInteger(property);
3326 size+=WriteBlobByte(image,ScaleQuantumToChar(opacity));
3327 (void) ApplyPSDLayerOpacity(next_image,opacity,MagickTrue,exception);
3330 size+=WriteBlobByte(image,255);
3331 size+=WriteBlobByte(image,0);
3332 size+=WriteBlobByte(image,next_image->compose==NoCompositeOp ?
3333 1 << 0x02 : 1); /* layer properties - visible, etc. */
3334 size+=WriteBlobByte(image,0);
3335 info=GetAdditionalInformation(image_info,next_image,exception);
3336 property=(const char *) GetImageProperty(next_image,"label",exception);
3337 if (property == (const char *) NULL)
3339 (void) FormatLocaleString(layer_name,MagickPathExtent,"L%.20g",
3340 (double) layer_index);
3341 property=layer_name;
3343 name_length=strlen(property)+1;
3344 if ((name_length % 4) != 0)
3345 name_length+=(4-(name_length % 4));
3346 if (info != (const StringInfo *) NULL)
3347 name_length+=GetStringInfoLength(info);
3349 if (mask != (Image *) NULL)
3351 size+=WriteBlobMSBLong(image,(unsigned int) name_length);
3352 if (mask == (Image *) NULL)
3353 size+=WriteBlobMSBLong(image,0);
3356 if (mask->compose != NoCompositeOp)
3357 (void) ApplyPSDOpacityMask(next_image,mask,ScaleCharToQuantum(
3358 default_color),MagickTrue,exception);
3359 mask->page.y+=image->page.y;
3360 mask->page.x+=image->page.x;
3361 size+=WriteBlobMSBLong(image,20);
3362 size+=WriteBlobMSBSignedLong(image,mask->page.y);
3363 size+=WriteBlobMSBSignedLong(image,mask->page.x);
3364 size+=WriteBlobMSBLong(image,(const unsigned int) mask->rows+
3366 size+=WriteBlobMSBLong(image,(const unsigned int) mask->columns+
3368 size+=WriteBlobByte(image,default_color);
3369 size+=WriteBlobByte(image,mask->compose == NoCompositeOp ? 2 : 0);
3370 size+=WriteBlobMSBShort(image,0);
3372 size+=WriteBlobMSBLong(image,0);
3373 size+=WritePascalString(image,property,4);
3374 if (info != (const StringInfo *) NULL)
3375 size+=WriteBlob(image,GetStringInfoLength(info),
3376 GetStringInfoDatum(info));
3377 next_image=GetNextImageInList(next_image);
3382 next_image=base_image;
3384 while (next_image != NULL)
3386 length=WritePSDChannels(&psd_info,image_info,image,next_image,
3387 layer_size_offsets[layer_index++],MagickTrue,exception);
3394 next_image=GetNextImageInList(next_image);
3396 (void) WriteBlobMSBLong(image,0); /* user mask data */
3398 Write the total size
3400 size_offset+=WritePSDSize(&psd_info,image,size+
3401 (psd_info.version == 1 ? 8 : 16),size_offset);
3402 if ((size/2) != ((size+1)/2))
3403 rounded_size=size+1;
3406 (void) WritePSDSize(&psd_info,image,rounded_size,size_offset);
3407 layer_size_offsets=(MagickOffsetType *) RelinquishMagickMemory(
3408 layer_size_offsets);
3410 Remove the opacity mask from the registry
3412 next_image=base_image;
3413 while (next_image != (Image *) NULL)
3415 property=GetImageArtifact(next_image,"psd:opacity-mask");
3416 if (property != (const char *) NULL)
3417 DeleteImageRegistry(property);
3418 next_image=GetNextImageInList(next_image);
3421 Write composite image.
3423 if (status != MagickFalse)
3428 compression=image->compression;
3429 if (image->compression == ZipCompression)
3430 image->compression=RLECompression;
3431 if (WritePSDChannels(&psd_info,image_info,image,image,0,MagickFalse,
3434 image->compression=compression;
3436 (void) CloseBlob(image);