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(Image *image)
245 switch (image->compose)
247 case ColorBurnCompositeOp:
248 return(image->endian == LSBEndian ? "vidi" : "idiv");
249 case ColorDodgeCompositeOp:
250 return(image->endian == LSBEndian ? " vid" : "div ");
251 case ColorizeCompositeOp:
252 return(image->endian == LSBEndian ? "rloc" : "colr");
253 case DarkenCompositeOp:
254 return(image->endian == LSBEndian ? "krad" : "dark");
255 case DifferenceCompositeOp:
256 return(image->endian == LSBEndian ? "ffid" : "diff");
257 case DissolveCompositeOp:
258 return(image->endian == LSBEndian ? "ssid" : "diss");
259 case ExclusionCompositeOp:
260 return(image->endian == LSBEndian ? "dums" : "smud");
261 case HardLightCompositeOp:
262 return(image->endian == LSBEndian ? "tiLh" : "hLit");
263 case HardMixCompositeOp:
264 return(image->endian == LSBEndian ? "xiMh" : "hMix");
266 return(image->endian == LSBEndian ? " euh" : "hue ");
267 case LightenCompositeOp:
268 return(image->endian == LSBEndian ? "etil" : "lite");
269 case LinearBurnCompositeOp:
270 return(image->endian == LSBEndian ? "nrbl" : "lbrn");
271 case LinearDodgeCompositeOp:
272 return(image->endian == LSBEndian ? "gddl" : "lddg");
273 case LinearLightCompositeOp:
274 return(image->endian == LSBEndian ? "tiLl" : "lLit");
275 case LuminizeCompositeOp:
276 return(image->endian == LSBEndian ? " mul" : "lum ");
277 case MultiplyCompositeOp:
278 return(image->endian == LSBEndian ? " lum" : "mul ");
279 case OverlayCompositeOp:
280 return(image->endian == LSBEndian ? "revo" : "over");
281 case PinLightCompositeOp:
282 return(image->endian == LSBEndian ? "tiLp" : "pLit");
283 case SaturateCompositeOp:
284 return(image->endian == LSBEndian ? " tas" : "sat ");
285 case ScreenCompositeOp:
286 return(image->endian == LSBEndian ? "nrcs" : "scrn");
287 case SoftLightCompositeOp:
288 return(image->endian == LSBEndian ? "tiLs" : "sLit");
289 case VividLightCompositeOp:
290 return(image->endian == LSBEndian ? "tiLv" : "vLit");
291 case OverCompositeOp:
293 return(image->endian == LSBEndian ? "mron" : "norm");
298 For some reason Photoshop seems to blend semi-transparent pixels with white.
299 This method reverts the blending. This can be disabled by setting the
300 option 'psd:alpha-unblend' to off.
302 static MagickBooleanType CorrectPSDAlphaBlend(const ImageInfo *image_info,
303 Image *image,ExceptionInfo* exception)
314 if (image->alpha_trait != BlendPixelTrait || image->colorspace != sRGBColorspace)
316 option=GetImageOption(image_info,"psd:alpha-unblend");
317 if (IsStringFalse(option) != MagickFalse)
320 #if defined(MAGICKCORE_OPENMP_SUPPORT)
321 #pragma omp parallel for schedule(static,4) shared(status) \
322 magick_number_threads(image,image,image->rows,1)
324 for (y=0; y < (ssize_t) image->rows; y++)
332 if (status == MagickFalse)
334 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
335 if (q == (Quantum *) NULL)
340 for (x=0; x < (ssize_t) image->columns; x++)
348 gamma=QuantumScale*GetPixelAlpha(image, q);
349 if (gamma != 0.0 && gamma != 1.0)
351 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
353 PixelChannel channel = GetPixelChannelChannel(image,i);
354 if (channel != AlphaPixelChannel)
355 q[i]=ClampToQuantum((q[i]-((1.0-gamma)*QuantumRange))/gamma);
358 q+=GetPixelChannels(image);
360 if (SyncAuthenticPixels(image,exception) == MagickFalse)
367 static inline CompressionType ConvertPSDCompression(
368 PSDCompressionType compression)
373 return RLECompression;
374 case ZipWithPrediction:
375 case ZipWithoutPrediction:
376 return ZipCompression;
378 return NoCompression;
382 static MagickBooleanType ApplyPSDLayerOpacity(Image *image,Quantum opacity,
383 MagickBooleanType revert,ExceptionInfo *exception)
391 if (image->debug != MagickFalse)
392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
393 " applying layer opacity %.20g", (double) opacity);
394 if (opacity == OpaqueAlpha)
396 image->alpha_trait=BlendPixelTrait;
398 #if defined(MAGICKCORE_OPENMP_SUPPORT)
399 #pragma omp parallel for schedule(static,4) shared(status) \
400 magick_number_threads(image,image,image->rows,1)
402 for (y=0; y < (ssize_t) image->rows; y++)
410 if (status == MagickFalse)
412 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
413 if (q == (Quantum *) NULL)
418 for (x=0; x < (ssize_t) image->columns; x++)
420 if (revert == MagickFalse)
421 SetPixelAlpha(image,(Quantum) (QuantumScale*(GetPixelAlpha(image,q))*
423 else if (opacity > 0)
424 SetPixelAlpha(image,(Quantum) (QuantumRange*(GetPixelAlpha(image,q)/
425 (MagickRealType) opacity)),q);
426 q+=GetPixelChannels(image);
428 if (SyncAuthenticPixels(image,exception) == MagickFalse)
435 static MagickBooleanType ApplyPSDOpacityMask(Image *image,const Image *mask,
436 Quantum background,MagickBooleanType revert,ExceptionInfo *exception)
450 if (image->debug != MagickFalse)
451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
452 " applying opacity mask");
453 complete_mask=CloneImage(image,image->columns,image->rows,MagickTrue,
455 if (complete_mask == (Image *) NULL)
457 complete_mask->alpha_trait=BlendPixelTrait;
458 GetPixelInfo(complete_mask,&color);
459 color.red=background;
460 SetImageColor(complete_mask,&color,exception);
461 status=CompositeImage(complete_mask,mask,OverCompositeOp,MagickTrue,
462 mask->page.x-image->page.x,mask->page.y-image->page.y,exception);
463 if (status == MagickFalse)
465 complete_mask=DestroyImage(complete_mask);
468 image->alpha_trait=BlendPixelTrait;
469 #if defined(MAGICKCORE_OPENMP_SUPPORT)
470 #pragma omp parallel for schedule(static,4) shared(status) \
471 magick_number_threads(image,image,image->rows,1)
473 for (y=0; y < (ssize_t) image->rows; y++)
484 if (status == MagickFalse)
486 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
487 p=GetAuthenticPixels(complete_mask,0,y,complete_mask->columns,1,exception);
488 if ((q == (Quantum *) NULL) || (p == (Quantum *) NULL))
493 for (x=0; x < (ssize_t) image->columns; x++)
499 alpha=GetPixelAlpha(image,q);
500 intensity=GetPixelIntensity(complete_mask,p);
501 if (revert == MagickFalse)
502 SetPixelAlpha(image,ClampToQuantum(intensity*(QuantumScale*alpha)),q);
503 else if (intensity > 0)
504 SetPixelAlpha(image,ClampToQuantum((alpha/intensity)*QuantumRange),q);
505 q+=GetPixelChannels(image);
506 p+=GetPixelChannels(complete_mask);
508 if (SyncAuthenticPixels(image,exception) == MagickFalse)
511 complete_mask=DestroyImage(complete_mask);
515 static void PreservePSDOpacityMask(Image *image,LayerInfo* layer_info,
516 ExceptionInfo *exception)
527 if (image->debug != MagickFalse)
528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
529 " preserving opacity mask");
530 random_info=AcquireRandomInfo();
531 key_info=GetRandomKey(random_info,2+1);
532 key=(char *) GetStringInfoDatum(key_info);
533 key[8]=layer_info->mask.background;
535 layer_info->mask.image->page.x+=layer_info->page.x;
536 layer_info->mask.image->page.y+=layer_info->page.y;
537 (void) SetImageRegistry(ImageRegistryType,(const char *) key,
538 layer_info->mask.image,exception);
539 (void) SetImageArtifact(layer_info->image,"psd:opacity-mask",
541 key_info=DestroyStringInfo(key_info);
542 random_info=DestroyRandomInfo(random_info);
545 static ssize_t DecodePSDPixels(const size_t number_compact_pixels,
546 const unsigned char *compact_pixels,const ssize_t depth,
547 const size_t number_pixels,unsigned char *pixels)
549 #define CheckNumberCompactPixels \
554 #define CheckNumberPixels(count) \
555 if (((ssize_t) i + count) > (ssize_t) number_pixels) \
572 packets=(ssize_t) number_compact_pixels;
573 for (i=0; (packets > 1) && (i < (ssize_t) number_pixels); )
576 length=(size_t) (*compact_pixels++);
582 CheckNumberCompactPixels;
583 pixel=(*compact_pixels++);
584 for (j=0; j < (ssize_t) length; j++)
590 CheckNumberPixels(8);
591 *pixels++=(pixel >> 7) & 0x01 ? 0U : 255U;
592 *pixels++=(pixel >> 6) & 0x01 ? 0U : 255U;
593 *pixels++=(pixel >> 5) & 0x01 ? 0U : 255U;
594 *pixels++=(pixel >> 4) & 0x01 ? 0U : 255U;
595 *pixels++=(pixel >> 3) & 0x01 ? 0U : 255U;
596 *pixels++=(pixel >> 2) & 0x01 ? 0U : 255U;
597 *pixels++=(pixel >> 1) & 0x01 ? 0U : 255U;
598 *pixels++=(pixel >> 0) & 0x01 ? 0U : 255U;
603 CheckNumberPixels(4);
604 *pixels++=(unsigned char) ((pixel >> 6) & 0x03);
605 *pixels++=(unsigned char) ((pixel >> 4) & 0x03);
606 *pixels++=(unsigned char) ((pixel >> 2) & 0x03);
607 *pixels++=(unsigned char) ((pixel & 0x03) & 0x03);
612 CheckNumberPixels(2);
613 *pixels++=(unsigned char) ((pixel >> 4) & 0xff);
614 *pixels++=(unsigned char) ((pixel & 0x0f) & 0xff);
619 CheckNumberPixels(1);
620 *pixels++=(unsigned char) pixel;
628 for (j=0; j < (ssize_t) length; j++)
630 CheckNumberCompactPixels;
635 CheckNumberPixels(8);
636 *pixels++=(*compact_pixels >> 7) & 0x01 ? 0U : 255U;
637 *pixels++=(*compact_pixels >> 6) & 0x01 ? 0U : 255U;
638 *pixels++=(*compact_pixels >> 5) & 0x01 ? 0U : 255U;
639 *pixels++=(*compact_pixels >> 4) & 0x01 ? 0U : 255U;
640 *pixels++=(*compact_pixels >> 3) & 0x01 ? 0U : 255U;
641 *pixels++=(*compact_pixels >> 2) & 0x01 ? 0U : 255U;
642 *pixels++=(*compact_pixels >> 1) & 0x01 ? 0U : 255U;
643 *pixels++=(*compact_pixels >> 0) & 0x01 ? 0U : 255U;
648 CheckNumberPixels(4);
649 *pixels++=(*compact_pixels >> 6) & 0x03;
650 *pixels++=(*compact_pixels >> 4) & 0x03;
651 *pixels++=(*compact_pixels >> 2) & 0x03;
652 *pixels++=(*compact_pixels & 0x03) & 0x03;
657 CheckNumberPixels(2);
658 *pixels++=(*compact_pixels >> 4) & 0xff;
659 *pixels++=(*compact_pixels & 0x0f) & 0xff;
664 CheckNumberPixels(1);
665 *pixels++=(*compact_pixels);
675 static inline LayerInfo *DestroyLayerInfo(LayerInfo *layer_info,
676 const ssize_t number_layers)
681 for (i=0; i<number_layers; i++)
683 if (layer_info[i].image != (Image *) NULL)
684 layer_info[i].image=DestroyImage(layer_info[i].image);
685 if (layer_info[i].mask.image != (Image *) NULL)
686 layer_info[i].mask.image=DestroyImage(layer_info[i].mask.image);
687 if (layer_info[i].info != (StringInfo *) NULL)
688 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
691 return (LayerInfo *) RelinquishMagickMemory(layer_info);
694 static inline size_t GetPSDPacketSize(Image *image)
696 if (image->storage_class == PseudoClass)
698 if (image->colors > 256)
700 else if (image->depth > 8)
704 if (image->depth > 8)
710 static inline MagickSizeType GetPSDSize(const PSDInfo *psd_info,Image *image)
712 if (psd_info->version == 1)
713 return((MagickSizeType) ReadBlobLong(image));
714 return((MagickSizeType) ReadBlobLongLong(image));
717 static inline size_t GetPSDRowSize(Image *image)
719 if (image->depth == 1)
720 return(((image->columns+7)/8)*GetPSDPacketSize(image));
722 return(image->columns*GetPSDPacketSize(image));
725 static const char *ModeToString(PSDImageType type)
729 case BitmapMode: return "Bitmap";
730 case GrayscaleMode: return "Grayscale";
731 case IndexedMode: return "Indexed";
732 case RGBMode: return "RGB";
733 case CMYKMode: return "CMYK";
734 case MultichannelMode: return "Multichannel";
735 case DuotoneMode: return "Duotone";
736 case LabMode: return "L*A*B";
737 default: return "unknown";
741 static MagickBooleanType NegateCMYK(Image *image,ExceptionInfo *exception)
749 channel_mask=SetImageChannelMask(image,(ChannelType)(AllChannels &~
751 status=NegateImage(image,MagickFalse,exception);
752 (void) SetImageChannelMask(image,channel_mask);
756 static void ParseImageResourceBlocks(Image *image,
757 const unsigned char *blocks,size_t length,
758 MagickBooleanType *has_merged_image,ExceptionInfo *exception)
778 profile=BlobToStringInfo((const unsigned char *) NULL,length);
779 SetStringInfoDatum(profile,blocks);
780 (void) SetImageProfile(image,"8bim",profile,exception);
781 profile=DestroyStringInfo(profile);
782 for (p=blocks; (p >= blocks) && (p < (blocks+length-7)); )
784 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
787 p=PushShortPixel(MSBEndian,p,&id);
788 p=PushCharPixel(p,&name_length);
789 if ((name_length % 2) == 0)
792 if (p > (blocks+length-4))
794 p=PushLongPixel(MSBEndian,p,&count);
795 if ((p+count) > (blocks+length))
802 value[MagickPathExtent];
812 p=PushShortPixel(MSBEndian,p,&resolution);
813 image->resolution.x=(double) resolution;
814 (void) FormatLocaleString(value,MagickPathExtent,"%g",
815 image->resolution.x);
816 (void) SetImageProperty(image,"tiff:XResolution",value,exception);
817 p=PushShortPixel(MSBEndian,p,&short_sans);
818 p=PushShortPixel(MSBEndian,p,&short_sans);
819 p=PushShortPixel(MSBEndian,p,&short_sans);
820 p=PushShortPixel(MSBEndian,p,&resolution);
821 image->resolution.y=(double) resolution;
822 (void) FormatLocaleString(value,MagickPathExtent,"%g",
823 image->resolution.y);
824 (void) SetImageProperty(image,"tiff:YResolution",value,exception);
825 p=PushShortPixel(MSBEndian,p,&short_sans);
826 p=PushShortPixel(MSBEndian,p,&short_sans);
827 p=PushShortPixel(MSBEndian,p,&short_sans);
828 image->units=PixelsPerInchResolution;
833 if ((count > 3) && (*(p+4) == 0))
834 *has_merged_image=MagickFalse;
844 if ((count & 0x01) != 0)
850 static CompositeOperator PSDBlendModeToCompositeOperator(const char *mode)
852 if (mode == (const char *) NULL)
853 return(OverCompositeOp);
854 if (LocaleNCompare(mode,"norm",4) == 0)
855 return(OverCompositeOp);
856 if (LocaleNCompare(mode,"mul ",4) == 0)
857 return(MultiplyCompositeOp);
858 if (LocaleNCompare(mode,"diss",4) == 0)
859 return(DissolveCompositeOp);
860 if (LocaleNCompare(mode,"diff",4) == 0)
861 return(DifferenceCompositeOp);
862 if (LocaleNCompare(mode,"dark",4) == 0)
863 return(DarkenCompositeOp);
864 if (LocaleNCompare(mode,"lite",4) == 0)
865 return(LightenCompositeOp);
866 if (LocaleNCompare(mode,"hue ",4) == 0)
867 return(HueCompositeOp);
868 if (LocaleNCompare(mode,"sat ",4) == 0)
869 return(SaturateCompositeOp);
870 if (LocaleNCompare(mode,"colr",4) == 0)
871 return(ColorizeCompositeOp);
872 if (LocaleNCompare(mode,"lum ",4) == 0)
873 return(LuminizeCompositeOp);
874 if (LocaleNCompare(mode,"scrn",4) == 0)
875 return(ScreenCompositeOp);
876 if (LocaleNCompare(mode,"over",4) == 0)
877 return(OverlayCompositeOp);
878 if (LocaleNCompare(mode,"hLit",4) == 0)
879 return(HardLightCompositeOp);
880 if (LocaleNCompare(mode,"sLit",4) == 0)
881 return(SoftLightCompositeOp);
882 if (LocaleNCompare(mode,"smud",4) == 0)
883 return(ExclusionCompositeOp);
884 if (LocaleNCompare(mode,"div ",4) == 0)
885 return(ColorDodgeCompositeOp);
886 if (LocaleNCompare(mode,"idiv",4) == 0)
887 return(ColorBurnCompositeOp);
888 if (LocaleNCompare(mode,"lbrn",4) == 0)
889 return(LinearBurnCompositeOp);
890 if (LocaleNCompare(mode,"lddg",4) == 0)
891 return(LinearDodgeCompositeOp);
892 if (LocaleNCompare(mode,"lLit",4) == 0)
893 return(LinearLightCompositeOp);
894 if (LocaleNCompare(mode,"vLit",4) == 0)
895 return(VividLightCompositeOp);
896 if (LocaleNCompare(mode,"pLit",4) == 0)
897 return(PinLightCompositeOp);
898 if (LocaleNCompare(mode,"hMix",4) == 0)
899 return(HardMixCompositeOp);
900 return(OverCompositeOp);
903 static inline void ReversePSDString(Image *image,char *p,size_t length)
908 if (image->endian == MSBEndian)
912 for(--q; p < q; ++p, --q)
920 static inline void SetPSDPixel(Image *image,const size_t channels,
921 const ssize_t type,const size_t packet_size,const Quantum pixel,Quantum *q,
922 ExceptionInfo *exception)
924 if (image->storage_class == PseudoClass)
931 if (packet_size == 1)
932 SetPixelIndex(image,ScaleQuantumToChar(pixel),q);
934 SetPixelIndex(image,ScaleQuantumToShort(pixel),q);
936 color=image->colormap+(ssize_t) ConstrainColormapIndex(image,
937 GetPixelIndex(image,q),exception);
938 if ((type == 0) && (channels > 1))
941 color->alpha=(MagickRealType) pixel;
942 SetPixelViaPixelInfo(image,color,q);
949 SetPixelAlpha(image,pixel,q);
955 SetPixelRed(image,pixel,q);
960 SetPixelGreen(image,pixel,q);
965 SetPixelBlue(image,pixel,q);
970 if (image->colorspace == CMYKColorspace)
971 SetPixelBlack(image,pixel,q);
973 if (image->alpha_trait != UndefinedPixelTrait)
974 SetPixelAlpha(image,pixel,q);
979 if ((IssRGBCompatibleColorspace(image->colorspace) != MagickFalse) &&
982 if (image->alpha_trait != UndefinedPixelTrait)
983 SetPixelAlpha(image,pixel,q);
989 static MagickBooleanType ReadPSDChannelPixels(Image *image,
990 const size_t channels,const size_t row,const ssize_t type,
991 const unsigned char *pixels,ExceptionInfo *exception)
996 register const unsigned char
1012 q=GetAuthenticPixels(image,0,row,image->columns,1,exception);
1013 if (q == (Quantum *) NULL)
1015 packet_size=GetPSDPacketSize(image);
1016 for (x=0; x < (ssize_t) image->columns; x++)
1018 if (packet_size == 1)
1019 pixel=ScaleCharToQuantum(*p++);
1022 p=PushShortPixel(MSBEndian,p,&nibble);
1023 pixel=ScaleShortToQuantum(nibble);
1025 if (image->depth > 1)
1027 SetPSDPixel(image,channels,type,packet_size,pixel,q,exception);
1028 q+=GetPixelChannels(image);
1036 number_bits=image->columns-x;
1037 if (number_bits > 8)
1039 for (bit = 0; bit < number_bits; bit++)
1041 SetPSDPixel(image,channels,type,packet_size,(((unsigned char) pixel)
1042 & (0x01 << (7-bit))) != 0 ? 0 : QuantumRange,q,exception);
1043 q+=GetPixelChannels(image);
1046 if (x != (ssize_t) image->columns)
1051 return(SyncAuthenticPixels(image,exception));
1054 static MagickBooleanType ReadPSDChannelRaw(Image *image,const size_t channels,
1055 const ssize_t type,ExceptionInfo *exception)
1070 if (image->debug != MagickFalse)
1071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1072 " layer data is RAW");
1074 row_size=GetPSDRowSize(image);
1075 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
1076 if (pixels == (unsigned char *) NULL)
1077 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1081 for (y=0; y < (ssize_t) image->rows; y++)
1085 count=ReadBlob(image,row_size,pixels);
1086 if (count != row_size)
1089 status=ReadPSDChannelPixels(image,channels,y,type,pixels,exception);
1090 if (status == MagickFalse)
1094 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1098 static inline MagickOffsetType *ReadPSDRLESizes(Image *image,
1099 const PSDInfo *psd_info,const size_t size)
1107 sizes=(MagickOffsetType *) AcquireQuantumMemory(size,sizeof(*sizes));
1108 if(sizes != (MagickOffsetType *) NULL)
1110 for (y=0; y < (ssize_t) size; y++)
1112 if (psd_info->version == 1)
1113 sizes[y]=(MagickOffsetType) ReadBlobShort(image);
1115 sizes[y]=(MagickOffsetType) ReadBlobLong(image);
1121 static MagickBooleanType ReadPSDChannelRLE(Image *image,const PSDInfo *psd_info,
1122 const ssize_t type,MagickOffsetType *sizes,ExceptionInfo *exception)
1139 if (image->debug != MagickFalse)
1140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1141 " layer data is RLE compressed");
1143 row_size=GetPSDRowSize(image);
1144 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
1145 if (pixels == (unsigned char *) NULL)
1146 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1150 for (y=0; y < (ssize_t) image->rows; y++)
1151 if ((MagickOffsetType) length < sizes[y])
1152 length=(size_t) sizes[y];
1154 if (length > row_size + 256) // arbitrary number
1156 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1157 ThrowBinaryException(ResourceLimitError,"InvalidLength",image->filename);
1160 compact_pixels=(unsigned char *) AcquireQuantumMemory(length,sizeof(*pixels));
1161 if (compact_pixels == (unsigned char *) NULL)
1163 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1164 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1168 (void) ResetMagickMemory(compact_pixels,0,length*sizeof(*compact_pixels));
1171 for (y=0; y < (ssize_t) image->rows; y++)
1175 count=ReadBlob(image,(size_t) sizes[y],compact_pixels);
1176 if (count != (ssize_t) sizes[y])
1179 count=DecodePSDPixels((size_t) sizes[y],compact_pixels,
1180 (ssize_t) (image->depth == 1 ? 123456 : image->depth),row_size,pixels);
1181 if (count != (ssize_t) row_size)
1184 status=ReadPSDChannelPixels(image,psd_info->channels,y,type,pixels,
1186 if (status == MagickFalse)
1190 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1191 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1195 #ifdef MAGICKCORE_ZLIB_DELEGATE
1196 static MagickBooleanType ReadPSDChannelZip(Image *image,const size_t channels,
1197 const ssize_t type,const PSDCompressionType compression,
1198 const size_t compact_size,ExceptionInfo *exception)
1203 register unsigned char
1222 if (image->debug != MagickFalse)
1223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1224 " layer data is ZIP compressed");
1226 compact_pixels=(unsigned char *) AcquireQuantumMemory(compact_size,
1227 sizeof(*compact_pixels));
1228 if (compact_pixels == (unsigned char *) NULL)
1229 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1232 packet_size=GetPSDPacketSize(image);
1233 row_size=image->columns*packet_size;
1234 count=image->rows*row_size;
1236 pixels=(unsigned char *) AcquireQuantumMemory(count,sizeof(*pixels));
1237 if (pixels == (unsigned char *) NULL)
1239 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1240 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1243 if (ReadBlob(image,compact_size,compact_pixels) != (ssize_t) compact_size)
1245 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1246 ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile",
1250 ResetMagickMemory(&stream,0,sizeof(stream));
1251 stream.data_type=Z_BINARY;
1252 stream.next_in=(Bytef *)compact_pixels;
1253 stream.avail_in=(uInt) compact_size;
1254 stream.next_out=(Bytef *)pixels;
1255 stream.avail_out=(uInt) count;
1257 if (inflateInit(&stream) == Z_OK)
1262 while (stream.avail_out > 0)
1264 ret=inflate(&stream,Z_SYNC_FLUSH);
1265 if ((ret != Z_OK) && (ret != Z_STREAM_END))
1267 (void) inflateEnd(&stream);
1268 compact_pixels=(unsigned char *) RelinquishMagickMemory(
1270 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1271 return(MagickFalse);
1274 (void) inflateEnd(&stream);
1277 if (compression == ZipWithPrediction)
1282 length=image->columns;
1285 if (packet_size == 2)
1287 p[2]+=p[0]+((p[1]+p[3]) >> 8);
1301 for (y=0; y < (ssize_t) image->rows; y++)
1303 status=ReadPSDChannelPixels(image,channels,y,type,p,exception);
1304 if (status == MagickFalse)
1310 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1311 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1316 static MagickBooleanType ReadPSDChannel(Image *image,
1317 const ImageInfo *image_info,const PSDInfo *psd_info,LayerInfo* layer_info,
1318 const size_t channel,const PSDCompressionType compression,
1319 ExceptionInfo *exception)
1331 channel_image=image;
1332 mask=(Image *) NULL;
1333 if ((layer_info->channel_info[channel].type < -1) &&
1334 (layer_info->mask.page.width > 0) && (layer_info->mask.page.height > 0))
1340 Ignore mask that is not a user supplied layer mask, if the mask is
1341 disabled or if the flags have unsupported values.
1343 option=GetImageOption(image_info,"psd:preserve-opacity-mask");
1344 if ((layer_info->channel_info[channel].type != -2) ||
1345 (layer_info->mask.flags > 2) || ((layer_info->mask.flags & 0x02) &&
1346 (IsStringTrue(option) == MagickFalse)))
1348 SeekBlob(image,layer_info->channel_info[channel].size-2,SEEK_CUR);
1351 mask=CloneImage(image,layer_info->mask.page.width,
1352 layer_info->mask.page.height,MagickFalse,exception);
1353 if (mask != (Image *) NULL)
1355 SetImageType(mask,GrayscaleType,exception);
1360 offset=TellBlob(image);
1365 status=ReadPSDChannelRaw(channel_image,psd_info->channels,
1366 layer_info->channel_info[channel].type,exception);
1373 sizes=ReadPSDRLESizes(channel_image,psd_info,channel_image->rows);
1374 if (sizes == (MagickOffsetType *) NULL)
1375 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1377 status=ReadPSDChannelRLE(channel_image,psd_info,
1378 layer_info->channel_info[channel].type,sizes,exception);
1379 sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
1382 case ZipWithPrediction:
1383 case ZipWithoutPrediction:
1384 #ifdef MAGICKCORE_ZLIB_DELEGATE
1385 status=ReadPSDChannelZip(channel_image,layer_info->channels,
1386 layer_info->channel_info[channel].type,compression,
1387 layer_info->channel_info[channel].size-2,exception);
1389 (void) ThrowMagickException(exception,GetMagickModule(),
1390 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
1391 "'%s' (ZLIB)",image->filename);
1395 (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
1396 "CompressionNotSupported","'%.20g'",(double) compression);
1400 SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
1401 if (status == MagickFalse)
1403 if (mask != (Image *) NULL)
1405 ThrowBinaryException(CoderError,"UnableToDecompressImage",
1408 layer_info->mask.image=mask;
1412 static MagickBooleanType ReadPSDLayer(Image *image,const ImageInfo *image_info,
1413 const PSDInfo *psd_info,LayerInfo* layer_info,ExceptionInfo *exception)
1416 message[MagickPathExtent];
1427 if (image->debug != MagickFalse)
1428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1429 " setting up new layer image");
1430 if (psd_info->mode != IndexedMode)
1431 (void) SetImageBackgroundColor(layer_info->image,exception);
1432 layer_info->image->compose=PSDBlendModeToCompositeOperator(
1433 layer_info->blendkey);
1434 if (layer_info->visible == MagickFalse)
1435 layer_info->image->compose=NoCompositeOp;
1436 if (psd_info->mode == CMYKMode)
1437 SetImageColorspace(layer_info->image,CMYKColorspace,exception);
1438 else if ((psd_info->mode == BitmapMode) || (psd_info->mode == DuotoneMode) ||
1439 (psd_info->mode == GrayscaleMode))
1440 SetImageColorspace(layer_info->image,GRAYColorspace,exception);
1442 Set up some hidden attributes for folks that need them.
1444 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
1445 (double) layer_info->page.x);
1446 (void) SetImageArtifact(layer_info->image,"psd:layer.x",message);
1447 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
1448 (double) layer_info->page.y);
1449 (void) SetImageArtifact(layer_info->image,"psd:layer.y",message);
1450 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",(double)
1451 layer_info->opacity);
1452 (void) SetImageArtifact(layer_info->image,"psd:layer.opacity",message);
1453 (void) SetImageProperty(layer_info->image,"label",(char *) layer_info->name,
1457 for (j=0; j < (ssize_t) layer_info->channels; j++)
1459 if (image->debug != MagickFalse)
1460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1461 " reading data for channel %.20g",(double) j);
1463 compression=(PSDCompressionType) ReadBlobShort(layer_info->image);
1464 layer_info->image->compression=ConvertPSDCompression(compression);
1465 if (layer_info->channel_info[j].type == -1)
1466 layer_info->image->alpha_trait=BlendPixelTrait;
1468 status=ReadPSDChannel(layer_info->image,image_info,psd_info,layer_info,j,
1469 compression,exception);
1471 if (status == MagickFalse)
1475 if (status != MagickFalse)
1476 status=ApplyPSDLayerOpacity(layer_info->image,layer_info->opacity,
1477 MagickFalse,exception);
1479 if ((status != MagickFalse) &&
1480 (layer_info->image->colorspace == CMYKColorspace))
1481 status=NegateCMYK(layer_info->image,exception);
1483 if ((status != MagickFalse) && (layer_info->mask.image != (Image *) NULL))
1488 layer_info->mask.image->page.x=layer_info->mask.page.x;
1489 layer_info->mask.image->page.y=layer_info->mask.page.y;
1490 /* Do not composite the mask when it is disabled */
1491 if ((layer_info->mask.flags & 0x02) == 0x02)
1492 layer_info->mask.image->compose=NoCompositeOp;
1494 status=ApplyPSDOpacityMask(layer_info->image,layer_info->mask.image,
1495 layer_info->mask.background == 0 ? 0 : QuantumRange,MagickFalse,
1497 option=GetImageOption(image_info,"psd:preserve-opacity-mask");
1498 if (IsStringTrue(option) != MagickFalse)
1499 PreservePSDOpacityMask(image,layer_info,exception);
1500 layer_info->mask.image=DestroyImage(layer_info->mask.image);
1506 static MagickBooleanType ReadPSDLayersInternal(Image *image,
1507 const ImageInfo *image_info,const PSDInfo *psd_info,
1508 const MagickBooleanType skip_layers,ExceptionInfo *exception)
1530 size=GetPSDSize(psd_info,image);
1534 Skip layers & masks.
1536 (void) ReadBlobLong(image);
1537 count=ReadBlob(image,4,(unsigned char *) type);
1538 ReversePSDString(image,type,4);
1540 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1544 count=ReadBlob(image,4,(unsigned char *) type);
1545 ReversePSDString(image,type,4);
1546 if ((count != 0) && (LocaleNCompare(type,"Lr16",4) == 0))
1547 size=GetPSDSize(psd_info,image);
1555 layer_info=(LayerInfo *) NULL;
1556 number_layers=(short) ReadBlobShort(image);
1558 if (number_layers < 0)
1561 The first alpha channel in the merged result contains the
1562 transparency data for the merged result.
1564 number_layers=MagickAbsoluteValue(number_layers);
1565 if (image->debug != MagickFalse)
1566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1567 " negative layer count corrected for");
1568 image->alpha_trait=BlendPixelTrait;
1572 We only need to know if the image has an alpha channel
1574 if (skip_layers != MagickFalse)
1577 if (image->debug != MagickFalse)
1578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1579 " image contains %.20g layers",(double) number_layers);
1581 if (number_layers == 0)
1582 ThrowBinaryException(CorruptImageError,"InvalidNumberOfLayers",
1585 layer_info=(LayerInfo *) AcquireQuantumMemory((size_t) number_layers,
1586 sizeof(*layer_info));
1587 if (layer_info == (LayerInfo *) NULL)
1589 if (image->debug != MagickFalse)
1590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1591 " allocation of LayerInfo failed");
1592 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1595 (void) ResetMagickMemory(layer_info,0,(size_t) number_layers*
1596 sizeof(*layer_info));
1598 for (i=0; i < number_layers; i++)
1604 if (image->debug != MagickFalse)
1605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1606 " reading layer #%.20g",(double) i+1);
1607 layer_info[i].page.y=ReadBlobSignedLong(image);
1608 layer_info[i].page.x=ReadBlobSignedLong(image);
1609 y=ReadBlobSignedLong(image);
1610 x=ReadBlobSignedLong(image);
1611 layer_info[i].page.width=(size_t) (x-layer_info[i].page.x);
1612 layer_info[i].page.height=(size_t) (y-layer_info[i].page.y);
1613 layer_info[i].channels=ReadBlobShort(image);
1614 if (layer_info[i].channels > MaxPSDChannels)
1616 layer_info=DestroyLayerInfo(layer_info,number_layers);
1617 ThrowBinaryException(CorruptImageError,"MaximumChannelsExceeded",
1620 if (image->debug != MagickFalse)
1621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1622 " offset(%.20g,%.20g), size(%.20g,%.20g), channels=%.20g",
1623 (double) layer_info[i].page.x,(double) layer_info[i].page.y,
1624 (double) layer_info[i].page.height,(double)
1625 layer_info[i].page.width,(double) layer_info[i].channels);
1626 for (j=0; j < (ssize_t) layer_info[i].channels; j++)
1628 layer_info[i].channel_info[j].type=(short) ReadBlobShort(image);
1629 layer_info[i].channel_info[j].size=(size_t) GetPSDSize(psd_info,
1631 if (image->debug != MagickFalse)
1632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1633 " channel[%.20g]: type=%.20g, size=%.20g",(double) j,
1634 (double) layer_info[i].channel_info[j].type,
1635 (double) layer_info[i].channel_info[j].size);
1637 count=ReadBlob(image,4,(unsigned char *) type);
1638 ReversePSDString(image,type,4);
1639 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1641 if (image->debug != MagickFalse)
1642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1643 " layer type was %.4s instead of 8BIM", type);
1644 layer_info=DestroyLayerInfo(layer_info,number_layers);
1645 ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
1648 count=ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey);
1649 ReversePSDString(image,layer_info[i].blendkey,4);
1650 layer_info[i].opacity=(Quantum) ScaleCharToQuantum((unsigned char)
1651 ReadBlobByte(image));
1652 layer_info[i].clipping=(unsigned char) ReadBlobByte(image);
1653 layer_info[i].flags=(unsigned char) ReadBlobByte(image);
1654 layer_info[i].visible=!(layer_info[i].flags & 0x02);
1655 if (image->debug != MagickFalse)
1656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1657 " blend=%.4s, opacity=%.20g, clipping=%s, flags=%d, visible=%s",
1658 layer_info[i].blendkey,(double) layer_info[i].opacity,
1659 layer_info[i].clipping ? "true" : "false",layer_info[i].flags,
1660 layer_info[i].visible ? "true" : "false");
1661 (void) ReadBlobByte(image); /* filler */
1663 size=ReadBlobLong(image);
1670 if (image->debug != MagickFalse)
1671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1672 " layer contains additional info");
1673 length=ReadBlobLong(image);
1674 combined_length=length+4;
1680 layer_info[i].mask.page.y=ReadBlobSignedLong(image);
1681 layer_info[i].mask.page.x=ReadBlobSignedLong(image);
1682 layer_info[i].mask.page.height=(size_t) (ReadBlobSignedLong(image)-
1683 layer_info[i].mask.page.y);
1684 layer_info[i].mask.page.width=(size_t) (ReadBlobSignedLong(image)-
1685 layer_info[i].mask.page.x);
1686 layer_info[i].mask.background=(unsigned char) ReadBlobByte(
1688 layer_info[i].mask.flags=(unsigned char) ReadBlobByte(image);
1689 if (!(layer_info[i].mask.flags & 0x01))
1691 layer_info[i].mask.page.y=layer_info[i].mask.page.y-
1692 layer_info[i].page.y;
1693 layer_info[i].mask.page.x=layer_info[i].mask.page.x-
1694 layer_info[i].page.x;
1696 if (image->debug != MagickFalse)
1697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1698 " layer mask: offset(%.20g,%.20g), size(%.20g,%.20g), length=%.20g",
1699 (double) layer_info[i].mask.page.x,(double)
1700 layer_info[i].mask.page.y,(double)
1701 layer_info[i].mask.page.width,(double)
1702 layer_info[i].mask.page.height,(double) ((MagickOffsetType)
1705 Skip over the rest of the layer mask information.
1707 if (DiscardBlobBytes(image,(MagickSizeType) (length-18)) == MagickFalse)
1709 layer_info=DestroyLayerInfo(layer_info,number_layers);
1710 ThrowBinaryException(CorruptImageError,
1711 "UnexpectedEndOfFile",image->filename);
1714 length=ReadBlobLong(image);
1715 combined_length+=length+4;
1719 Layer blending ranges info.
1721 if (image->debug != MagickFalse)
1722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1723 " layer blending ranges: length=%.20g",(double)
1724 ((MagickOffsetType) length));
1725 if (DiscardBlobBytes(image,length) == MagickFalse)
1727 layer_info=DestroyLayerInfo(layer_info,number_layers);
1728 ThrowBinaryException(CorruptImageError,
1729 "UnexpectedEndOfFile",image->filename);
1735 length=(MagickSizeType) (unsigned char) ReadBlobByte(image);
1736 combined_length+=length+1;
1738 (void) ReadBlob(image,(size_t) length++,layer_info[i].name);
1739 layer_info[i].name[length]='\0';
1740 if (image->debug != MagickFalse)
1741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1742 " layer name: %s",layer_info[i].name);
1743 if ((length % 4) != 0)
1745 length=4-(length % 4);
1746 combined_length+=length;
1747 /* Skip over the padding of the layer name */
1748 if (DiscardBlobBytes(image,length) == MagickFalse)
1750 layer_info=DestroyLayerInfo(layer_info,number_layers);
1751 ThrowBinaryException(CorruptImageError,
1752 "UnexpectedEndOfFile",image->filename);
1755 length=(MagickSizeType) size-combined_length;
1761 if (length > GetBlobSize(image))
1763 layer_info=DestroyLayerInfo(layer_info,number_layers);
1764 ThrowBinaryException(CorruptImageError,
1765 "InsufficientImageDataInFile",image->filename);
1767 layer_info[i].info=AcquireStringInfo((const size_t) length);
1768 info=GetStringInfoDatum(layer_info[i].info);
1769 (void) ReadBlob(image,(const size_t) length,info);
1774 for (i=0; i < number_layers; i++)
1776 if ((layer_info[i].page.width == 0) || (layer_info[i].page.height == 0))
1778 if (image->debug != MagickFalse)
1779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1780 " layer data is empty");
1781 if (layer_info[i].info != (StringInfo *) NULL)
1782 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
1787 Allocate layered image.
1789 layer_info[i].image=CloneImage(image,layer_info[i].page.width,
1790 layer_info[i].page.height,MagickFalse,exception);
1791 if (layer_info[i].image == (Image *) NULL)
1793 layer_info=DestroyLayerInfo(layer_info,number_layers);
1794 if (image->debug != MagickFalse)
1795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1796 " allocation of image for layer %.20g failed",(double) i);
1797 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1801 if (layer_info[i].info != (StringInfo *) NULL)
1803 (void) SetImageProfile(layer_info[i].image,"psd:additional-info",
1804 layer_info[i].info,exception);
1805 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
1809 if (image_info->ping == MagickFalse)
1811 for (i=0; i < number_layers; i++)
1813 if (layer_info[i].image == (Image *) NULL)
1815 for (j=0; j < layer_info[i].channels; j++)
1817 if (DiscardBlobBytes(image,(MagickSizeType)
1818 layer_info[i].channel_info[j].size) == MagickFalse)
1820 layer_info=DestroyLayerInfo(layer_info,number_layers);
1821 ThrowBinaryException(CorruptImageError,
1822 "UnexpectedEndOfFile",image->filename);
1828 if (image->debug != MagickFalse)
1829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1830 " reading data for layer %.20g",(double) i);
1832 status=ReadPSDLayer(image,image_info,psd_info,&layer_info[i],
1834 if (status == MagickFalse)
1837 status=SetImageProgress(image,LoadImagesTag,i,(MagickSizeType)
1839 if (status == MagickFalse)
1844 if (status != MagickFalse)
1846 for (i=0; i < number_layers; i++)
1848 if (layer_info[i].image == (Image *) NULL)
1850 for (j=i; j < number_layers - 1; j++)
1851 layer_info[j] = layer_info[j+1];
1857 if (number_layers > 0)
1859 for (i=0; i < number_layers; i++)
1862 layer_info[i].image->previous=layer_info[i-1].image;
1863 if (i < (number_layers-1))
1864 layer_info[i].image->next=layer_info[i+1].image;
1865 layer_info[i].image->page=layer_info[i].page;
1867 image->next=layer_info[0].image;
1868 layer_info[0].image->previous=image;
1870 layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
1873 layer_info=DestroyLayerInfo(layer_info,number_layers);
1879 ModuleExport MagickBooleanType ReadPSDLayers(Image *image,
1880 const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception)
1888 domain=CoderPolicyDomain;
1889 rights=ReadPolicyRights;
1890 if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
1892 return(ReadPSDLayersInternal(image,image_info,psd_info,MagickFalse,
1896 static MagickBooleanType ReadPSDMergedImage(const ImageInfo *image_info,
1897 Image *image,const PSDInfo *psd_info,ExceptionInfo *exception)
1911 compression=(PSDCompressionType) ReadBlobMSBShort(image);
1912 image->compression=ConvertPSDCompression(compression);
1914 if (compression != Raw && compression != RLE)
1916 (void) ThrowMagickException(exception,GetMagickModule(),
1917 TypeWarning,"CompressionNotSupported","'%.20g'",(double) compression);
1918 return(MagickFalse);
1921 sizes=(MagickOffsetType *) NULL;
1922 if (compression == RLE)
1924 sizes=ReadPSDRLESizes(image,psd_info,image->rows*psd_info->channels);
1925 if (sizes == (MagickOffsetType *) NULL)
1926 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1931 for (i=0; i < (ssize_t) psd_info->channels; i++)
1933 if (compression == RLE)
1934 status=ReadPSDChannelRLE(image,psd_info,i,sizes+(i*image->rows),
1937 status=ReadPSDChannelRaw(image,psd_info->channels,i,exception);
1939 if (status != MagickFalse)
1940 status=SetImageProgress(image,LoadImagesTag,i,psd_info->channels);
1942 if (status == MagickFalse)
1946 if ((status != MagickFalse) && (image->colorspace == CMYKColorspace))
1947 status=NegateCMYK(image,exception);
1949 if (status != MagickFalse)
1950 status=CorrectPSDAlphaBlend(image_info,image,exception);
1952 sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
1957 static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
1990 assert(image_info != (const ImageInfo *) NULL);
1991 assert(image_info->signature == MagickCoreSignature);
1992 if (image_info->debug != MagickFalse)
1993 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1994 image_info->filename);
1995 assert(exception != (ExceptionInfo *) NULL);
1996 assert(exception->signature == MagickCoreSignature);
1998 image=AcquireImage(image_info,exception);
1999 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2000 if (status == MagickFalse)
2002 image=DestroyImageList(image);
2003 return((Image *) NULL);
2008 image->endian=MSBEndian;
2009 count=ReadBlob(image,4,(unsigned char *) psd_info.signature);
2010 psd_info.version=ReadBlobMSBShort(image);
2011 if ((count == 0) || (LocaleNCompare(psd_info.signature,"8BPS",4) != 0) ||
2012 ((psd_info.version != 1) && (psd_info.version != 2)))
2013 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2014 (void) ReadBlob(image,6,psd_info.reserved);
2015 psd_info.channels=ReadBlobMSBShort(image);
2016 if (psd_info.channels > MaxPSDChannels)
2017 ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded");
2018 psd_info.rows=ReadBlobMSBLong(image);
2019 psd_info.columns=ReadBlobMSBLong(image);
2020 if ((psd_info.version == 1) && ((psd_info.rows > 30000) ||
2021 (psd_info.columns > 30000)))
2022 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2023 psd_info.depth=ReadBlobMSBShort(image);
2024 if ((psd_info.depth != 1) && (psd_info.depth != 8) && (psd_info.depth != 16))
2025 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2026 psd_info.mode=ReadBlobMSBShort(image);
2027 if (image->debug != MagickFalse)
2028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2029 " Image is %.20g x %.20g with channels=%.20g, depth=%.20g, mode=%s",
2030 (double) psd_info.columns,(double) psd_info.rows,(double)
2031 psd_info.channels,(double) psd_info.depth,ModeToString((PSDImageType)
2033 if (EOFBlob(image) != MagickFalse)
2034 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2038 image->depth=psd_info.depth;
2039 image->columns=psd_info.columns;
2040 image->rows=psd_info.rows;
2041 status=SetImageExtent(image,image->columns,image->rows,exception);
2042 if (status == MagickFalse)
2043 return(DestroyImageList(image));
2044 if (SetImageBackgroundColor(image,exception) == MagickFalse)
2046 image=DestroyImageList(image);
2047 return((Image *) NULL);
2049 if (psd_info.mode == LabMode)
2050 SetImageColorspace(image,LabColorspace,exception);
2051 if (psd_info.mode == CMYKMode)
2053 SetImageColorspace(image,CMYKColorspace,exception);
2054 if (psd_info.channels > 4)
2055 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2057 else if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) ||
2058 (psd_info.mode == DuotoneMode))
2060 status=AcquireImageColormap(image,psd_info.depth != 16 ? 256 : 65536,
2062 if (status == MagickFalse)
2063 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2064 if (image->debug != MagickFalse)
2065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2066 " Image colormap allocated");
2067 SetImageColorspace(image,GRAYColorspace,exception);
2068 if (psd_info.channels > 1)
2069 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2072 if (psd_info.channels > 3)
2073 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2075 Read PSD raster colormap only present for indexed and duotone images.
2077 length=ReadBlobMSBLong(image);
2080 if (image->debug != MagickFalse)
2081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2082 " reading colormap");
2083 if (psd_info.mode == DuotoneMode)
2086 Duotone image data; the format of this data is undocumented.
2088 data=(unsigned char *) AcquireQuantumMemory((size_t) length,
2090 if (data == (unsigned char *) NULL)
2091 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2092 (void) ReadBlob(image,(size_t) length,data);
2093 data=(unsigned char *) RelinquishMagickMemory(data);
2101 Read PSD raster colormap.
2103 number_colors=length/3;
2104 if (number_colors > 65536)
2105 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2106 if (AcquireImageColormap(image,number_colors,exception) == MagickFalse)
2107 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2108 for (i=0; i < (ssize_t) image->colors; i++)
2109 image->colormap[i].red=ScaleCharToQuantum((unsigned char)
2110 ReadBlobByte(image));
2111 for (i=0; i < (ssize_t) image->colors; i++)
2112 image->colormap[i].green=ScaleCharToQuantum((unsigned char)
2113 ReadBlobByte(image));
2114 for (i=0; i < (ssize_t) image->colors; i++)
2115 image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
2116 ReadBlobByte(image));
2117 image->alpha_trait=UndefinedPixelTrait;
2120 if ((image->depth == 1) && (image->storage_class != PseudoClass))
2121 ThrowReaderException(CorruptImageError, "ImproperImageHeader");
2122 has_merged_image=MagickTrue;
2123 length=ReadBlobMSBLong(image);
2130 Image resources block.
2132 if (image->debug != MagickFalse)
2133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2134 " reading image resource blocks - %.20g bytes",(double)
2135 ((MagickOffsetType) length));
2136 if (length > GetBlobSize(image))
2137 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
2138 blocks=(unsigned char *) AcquireQuantumMemory((size_t) length,
2140 if (blocks == (unsigned char *) NULL)
2141 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2142 count=ReadBlob(image,(size_t) length,blocks);
2143 if ((count != (ssize_t) length) || (length < 4) ||
2144 (LocaleNCompare((char *) blocks,"8BIM",4) != 0))
2146 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
2147 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2149 ParseImageResourceBlocks(image,blocks,(size_t) length,&has_merged_image,
2151 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
2154 Layer and mask block.
2156 length=GetPSDSize(&psd_info,image);
2159 length=ReadBlobMSBLong(image);
2160 length=ReadBlobMSBLong(image);
2162 offset=TellBlob(image);
2163 skip_layers=MagickFalse;
2164 if ((image_info->number_scenes == 1) && (image_info->scene == 0) &&
2165 (has_merged_image != MagickFalse))
2167 if (image->debug != MagickFalse)
2168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2169 " read composite only");
2170 skip_layers=MagickTrue;
2174 if (image->debug != MagickFalse)
2175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2176 " image has no layers");
2180 if (ReadPSDLayersInternal(image,image_info,&psd_info,skip_layers,
2181 exception) != MagickTrue)
2183 (void) CloseBlob(image);
2184 image=DestroyImageList(image);
2185 return((Image *) NULL);
2189 Skip the rest of the layer and mask information.
2191 SeekBlob(image,offset+length,SEEK_SET);
2194 If we are only "pinging" the image, then we're done - so return.
2196 if (EOFBlob(image) != MagickFalse)
2197 ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
2198 if (image_info->ping != MagickFalse)
2200 (void) CloseBlob(image);
2201 return(GetFirstImageInList(image));
2204 Read the precombined layer, present for PSD < 4 compatibility.
2206 if (image->debug != MagickFalse)
2207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2208 " reading the precombined layer");
2209 if ((has_merged_image != MagickFalse) || (GetImageListLength(image) == 1))
2210 has_merged_image=(MagickBooleanType) ReadPSDMergedImage(image_info,image,
2211 &psd_info,exception);
2212 if ((has_merged_image == MagickFalse) && (GetImageListLength(image) == 1) &&
2215 SeekBlob(image,offset,SEEK_SET);
2216 status=ReadPSDLayersInternal(image,image_info,&psd_info,MagickFalse,
2218 if (status != MagickTrue)
2220 (void) CloseBlob(image);
2221 image=DestroyImageList(image);
2222 return((Image *) NULL);
2225 if (has_merged_image == MagickFalse)
2230 if (GetImageListLength(image) == 1)
2231 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
2232 SetImageAlphaChannel(image,TransparentAlphaChannel,exception);
2233 image->background_color.alpha=TransparentAlpha;
2234 image->background_color.alpha_trait=BlendPixelTrait;
2235 merged=MergeImageLayers(image,FlattenLayer,exception);
2236 ReplaceImageInList(&image,merged);
2238 (void) CloseBlob(image);
2239 return(GetFirstImageInList(image));
2243 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2247 % R e g i s t e r P S D I m a g e %
2251 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2253 % RegisterPSDImage() adds properties for the PSD image format to
2254 % the list of supported formats. The properties include the image format
2255 % tag, a method to read and/or write the format, whether the format
2256 % supports the saving of more than one frame to the same file or blob,
2257 % whether the format supports native in-memory I/O, and a brief
2258 % description of the format.
2260 % The format of the RegisterPSDImage method is:
2262 % size_t RegisterPSDImage(void)
2265 ModuleExport size_t RegisterPSDImage(void)
2270 entry=AcquireMagickInfo("PSD","PSB","Adobe Large Document Format");
2271 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2272 entry->encoder=(EncodeImageHandler *) WritePSDImage;
2273 entry->magick=(IsImageFormatHandler *) IsPSD;
2274 entry->flags|=CoderDecoderSeekableStreamFlag;
2275 entry->flags|=CoderEncoderSeekableStreamFlag;
2276 (void) RegisterMagickInfo(entry);
2277 entry=AcquireMagickInfo("PSD","PSD","Adobe Photoshop bitmap");
2278 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2279 entry->encoder=(EncodeImageHandler *) WritePSDImage;
2280 entry->magick=(IsImageFormatHandler *) IsPSD;
2281 entry->flags|=CoderDecoderSeekableStreamFlag;
2282 entry->flags|=CoderEncoderSeekableStreamFlag;
2283 (void) RegisterMagickInfo(entry);
2284 return(MagickImageCoderSignature);
2288 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2292 % U n r e g i s t e r P S D I m a g e %
2296 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2298 % UnregisterPSDImage() removes format registrations made by the
2299 % PSD module from the list of supported formats.
2301 % The format of the UnregisterPSDImage method is:
2303 % UnregisterPSDImage(void)
2306 ModuleExport void UnregisterPSDImage(void)
2308 (void) UnregisterMagickInfo("PSB");
2309 (void) UnregisterMagickInfo("PSD");
2313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2317 % W r i t e P S D I m a g e %
2321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2323 % WritePSDImage() writes an image in the Adobe Photoshop encoded image format.
2325 % The format of the WritePSDImage method is:
2327 % MagickBooleanType WritePSDImage(const ImageInfo *image_info,Image *image,
2328 % ExceptionInfo *exception)
2330 % A description of each parameter follows.
2332 % o image_info: the image info.
2334 % o image: The image.
2336 % o exception: return any errors or warnings in this structure.
2340 static inline ssize_t SetPSDOffset(const PSDInfo *psd_info,Image *image,
2341 const size_t offset)
2343 if (psd_info->version == 1)
2344 return(WriteBlobMSBShort(image,(unsigned short) offset));
2345 return(WriteBlobMSBLong(image,(unsigned short) offset));
2348 static inline ssize_t WritePSDOffset(const PSDInfo *psd_info,Image *image,
2349 const MagickSizeType size,const MagickSizeType offset)
2357 current_offset=TellBlob(image);
2358 SeekBlob(image,offset,SEEK_SET);
2359 if (psd_info->version == 1)
2360 result=WriteBlobMSBShort(image,(unsigned short) size);
2362 result=(WriteBlobMSBLong(image,(unsigned short) size));
2363 SeekBlob(image,current_offset,SEEK_SET);
2367 static inline ssize_t SetPSDSize(const PSDInfo *psd_info,Image *image,
2368 const MagickSizeType size)
2370 if (psd_info->version == 1)
2371 return(WriteBlobLong(image,(unsigned int) size));
2372 return(WriteBlobLongLong(image,size));
2375 static inline ssize_t WritePSDSize(const PSDInfo *psd_info,Image *image,
2376 const MagickSizeType size,const MagickSizeType offset)
2384 current_offset=TellBlob(image);
2385 SeekBlob(image,offset,SEEK_SET);
2386 result=SetPSDSize(psd_info, image, size);
2387 SeekBlob(image,current_offset,SEEK_SET);
2391 static size_t PSDPackbitsEncodeImage(Image *image,const size_t length,
2392 const unsigned char *pixels,unsigned char *compact_pixels,
2393 ExceptionInfo *exception)
2402 register unsigned char
2409 Compress pixels with Packbits encoding.
2411 assert(image != (Image *) NULL);
2412 assert(image->signature == MagickCoreSignature);
2413 if (image->debug != MagickFalse)
2414 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2415 assert(pixels != (unsigned char *) NULL);
2416 assert(compact_pixels != (unsigned char *) NULL);
2417 packbits=(unsigned char *) AcquireQuantumMemory(128UL,sizeof(*packbits));
2418 if (packbits == (unsigned char *) NULL)
2419 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2422 for (i=(ssize_t) length; i != 0; )
2429 *q++=(unsigned char) 0;
2436 *q++=(unsigned char) 1;
2444 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2446 *q++=(unsigned char) ((256-3)+1);
2450 *q++=(unsigned char) 2;
2458 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2464 while (((ssize_t) count < i) && (*pixels == *(pixels+count)))
2471 *q++=(unsigned char) ((256-count)+1);
2480 while ((*(pixels+count) != *(pixels+count+1)) ||
2481 (*(pixels+count+1) != *(pixels+count+2)))
2483 packbits[count+1]=pixels[count];
2485 if (((ssize_t) count >= (i-3)) || (count >= 127))
2489 *packbits=(unsigned char) (count-1);
2490 for (j=0; j <= (ssize_t) count; j++)
2497 *q++=(unsigned char) 128; /* EOD marker */
2498 packbits=(unsigned char *) RelinquishMagickMemory(packbits);
2499 return((size_t) (q-compact_pixels));
2502 static size_t WriteCompressionStart(const PSDInfo *psd_info,Image *image,
2503 const Image *next_image,const ssize_t channels)
2512 if (next_image->compression == RLECompression)
2514 length=WriteBlobShort(image,RLE);
2515 for (i=0; i < channels; i++)
2516 for (y=0; y < (ssize_t) next_image->rows; y++)
2517 length+=SetPSDOffset(psd_info,image,0);
2519 #ifdef MAGICKCORE_ZLIB_DELEGATE
2520 else if (next_image->compression == ZipCompression)
2521 length=WriteBlobShort(image,ZipWithoutPrediction);
2524 length=WriteBlobShort(image,Raw);
2528 static size_t WritePSDChannel(const PSDInfo *psd_info,
2529 const ImageInfo *image_info,Image *image,Image *next_image,
2530 const QuantumType quantum_type, unsigned char *compact_pixels,
2531 MagickOffsetType size_offset,const MagickBooleanType separate,
2532 ExceptionInfo *exception)
2543 register const Quantum
2556 #ifdef MAGICKCORE_ZLIB_DELEGATE
2570 compressed_pixels=(unsigned char *) NULL;
2574 if (separate != MagickFalse)
2576 size_offset=TellBlob(image)+2;
2577 count+=WriteCompressionStart(psd_info,image,next_image,1);
2579 if (next_image->depth > 8)
2580 next_image->depth=16;
2581 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
2582 MagickTrue : MagickFalse;
2583 quantum_info=AcquireQuantumInfo(image_info,next_image);
2584 if (quantum_info == (QuantumInfo *) NULL)
2586 pixels=(unsigned char *) GetQuantumPixels(quantum_info);
2587 #ifdef MAGICKCORE_ZLIB_DELEGATE
2588 if (next_image->compression == ZipCompression)
2590 compressed_pixels=(unsigned char *) AcquireQuantumMemory(CHUNK,
2591 sizeof(*compressed_pixels));
2592 if (compressed_pixels == (unsigned char *) NULL)
2594 quantum_info=DestroyQuantumInfo(quantum_info);
2597 ResetMagickMemory(&stream,0,sizeof(stream));
2598 stream.data_type=Z_BINARY;
2599 level=Z_DEFAULT_COMPRESSION;
2600 if ((image_info->quality > 0 && image_info->quality < 10))
2601 level=(int) image_info->quality;
2602 if (deflateInit(&stream,level) != Z_OK)
2604 quantum_info=DestroyQuantumInfo(quantum_info);
2609 for (y=0; y < (ssize_t) next_image->rows; y++)
2611 p=GetVirtualPixels(next_image,0,y,next_image->columns,1,exception);
2612 if (p == (const Quantum *) NULL)
2614 length=ExportQuantumPixels(next_image,(CacheView *) NULL,quantum_info,
2615 quantum_type,pixels,exception);
2616 if (monochrome != MagickFalse)
2617 for (i=0; i < (ssize_t) length; i++)
2618 pixels[i]=(~pixels[i]);
2619 if (next_image->compression == RLECompression)
2621 length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels,
2623 count+=WriteBlob(image,length,compact_pixels);
2624 size_offset+=WritePSDOffset(psd_info,image,length,size_offset);
2626 #ifdef MAGICKCORE_ZLIB_DELEGATE
2627 else if (next_image->compression == ZipCompression)
2629 stream.avail_in=(uInt) length;
2630 stream.next_in=(Bytef *) pixels;
2631 if (y == (ssize_t) next_image->rows-1)
2634 stream.avail_out=(uInt) CHUNK;
2635 stream.next_out=(Bytef *) compressed_pixels;
2636 if (deflate(&stream,flush) == Z_STREAM_ERROR)
2638 length=(size_t) CHUNK-stream.avail_out;
2640 count+=WriteBlob(image,length,compressed_pixels);
2641 } while (stream.avail_out == 0);
2645 count+=WriteBlob(image,length,pixels);
2647 #ifdef MAGICKCORE_ZLIB_DELEGATE
2648 if (next_image->compression == ZipCompression)
2650 (void) deflateEnd(&stream);
2651 compressed_pixels=(unsigned char *) RelinquishMagickMemory(
2655 quantum_info=DestroyQuantumInfo(quantum_info);
2659 static unsigned char *AcquireCompactPixels(const Image *image,
2660 ExceptionInfo *exception)
2668 packet_size=image->depth > 8UL ? 2UL : 1UL;
2669 compact_pixels=(unsigned char *) AcquireQuantumMemory((9*
2670 image->columns)+1,packet_size*sizeof(*compact_pixels));
2671 if (compact_pixels == (unsigned char *) NULL)
2673 (void) ThrowMagickException(exception,GetMagickModule(),
2674 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2676 return(compact_pixels);
2679 static size_t WritePSDChannels(const PSDInfo *psd_info,
2680 const ImageInfo *image_info,Image *image,Image *next_image,
2681 MagickOffsetType size_offset,const MagickBooleanType separate,
2682 ExceptionInfo *exception)
2702 compact_pixels=(unsigned char *) NULL;
2703 if (next_image->compression == RLECompression)
2705 compact_pixels=AcquireCompactPixels(next_image,exception);
2706 if (compact_pixels == (unsigned char *) NULL)
2710 if (separate == MagickFalse)
2712 if (next_image->storage_class != PseudoClass)
2714 if (IsImageGray(next_image) == MagickFalse)
2715 channels=next_image->colorspace == CMYKColorspace ? 4 : 3;
2716 if (next_image->alpha_trait != UndefinedPixelTrait)
2719 rows_offset=TellBlob(image)+2;
2720 count+=WriteCompressionStart(psd_info,image,next_image,channels);
2721 offset_length=(next_image->rows*(psd_info->version == 1 ? 2 : 4));
2724 if (next_image->storage_class == PseudoClass)
2726 length=WritePSDChannel(psd_info,image_info,image,next_image,
2727 IndexQuantum,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;
2736 if (IsImageGray(next_image) != MagickFalse)
2738 length=WritePSDChannel(psd_info,image_info,image,next_image,
2739 GrayQuantum,compact_pixels,rows_offset,separate,exception);
2740 if (separate != MagickFalse)
2741 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2743 rows_offset+=offset_length;
2748 if (next_image->colorspace == CMYKColorspace)
2749 (void) NegateCMYK(next_image,exception);
2751 length=WritePSDChannel(psd_info,image_info,image,next_image,
2752 RedQuantum,compact_pixels,rows_offset,separate,exception);
2753 if (separate != MagickFalse)
2754 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2756 rows_offset+=offset_length;
2759 length=WritePSDChannel(psd_info,image_info,image,next_image,
2760 GreenQuantum,compact_pixels,rows_offset,separate,exception);
2761 if (separate != MagickFalse)
2762 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2764 rows_offset+=offset_length;
2767 length=WritePSDChannel(psd_info,image_info,image,next_image,
2768 BlueQuantum,compact_pixels,rows_offset,separate,exception);
2769 if (separate != MagickFalse)
2770 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2772 rows_offset+=offset_length;
2775 if (next_image->colorspace == CMYKColorspace)
2777 length=WritePSDChannel(psd_info,image_info,image,next_image,
2778 BlackQuantum,compact_pixels,rows_offset,separate,exception);
2779 if (separate != MagickFalse)
2780 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2782 rows_offset+=offset_length;
2786 if (next_image->alpha_trait != UndefinedPixelTrait)
2788 length=WritePSDChannel(psd_info,image_info,image,next_image,
2789 AlphaQuantum,compact_pixels,rows_offset,separate,exception);
2790 if (separate != MagickFalse)
2791 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2793 rows_offset+=offset_length;
2797 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
2798 if (next_image->colorspace == CMYKColorspace)
2799 (void) NegateCMYK(next_image,exception);
2800 if (separate != MagickFalse)
2805 property=GetImageArtifact(next_image,"psd:opacity-mask");
2806 if (property != (const char *) NULL)
2808 mask=(Image *) GetImageRegistry(ImageRegistryType,property,
2810 if (mask != (Image *) NULL)
2812 if (mask->compression == RLECompression)
2814 compact_pixels=AcquireCompactPixels(mask,exception);
2815 if (compact_pixels == (unsigned char *) NULL)
2818 length=WritePSDChannel(psd_info,image_info,image,mask,
2819 RedQuantum,compact_pixels,rows_offset,MagickTrue,exception);
2820 (void) WritePSDSize(psd_info,image,length,size_offset);
2822 compact_pixels=(unsigned char *) RelinquishMagickMemory(
2830 static size_t WritePascalString(Image *image,const char *value,size_t padding)
2843 length=(strlen(value) > 255UL ) ? 255UL : strlen(value);
2845 count+=WriteBlobByte(image,0);
2848 count+=WriteBlobByte(image,(unsigned char) length);
2849 count+=WriteBlob(image,length,(const unsigned char *) value);
2852 if ((length % padding) == 0)
2854 for (i=0; i < (ssize_t) (padding-(length % padding)); i++)
2855 count+=WriteBlobByte(image,0);
2859 static void WriteResolutionResourceBlock(Image *image)
2868 if (image->units == PixelsPerCentimeterResolution)
2870 x_resolution=2.54*65536.0*image->resolution.x+0.5;
2871 y_resolution=2.54*65536.0*image->resolution.y+0.5;
2876 x_resolution=65536.0*image->resolution.x+0.5;
2877 y_resolution=65536.0*image->resolution.y+0.5;
2880 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
2881 (void) WriteBlobMSBShort(image,0x03ED);
2882 (void) WriteBlobMSBShort(image,0);
2883 (void) WriteBlobMSBLong(image,16); /* resource size */
2884 (void) WriteBlobMSBLong(image,(unsigned int) (x_resolution+0.5));
2885 (void) WriteBlobMSBShort(image,units); /* horizontal resolution unit */
2886 (void) WriteBlobMSBShort(image,units); /* width unit */
2887 (void) WriteBlobMSBLong(image,(unsigned int) (y_resolution+0.5));
2888 (void) WriteBlobMSBShort(image,units); /* vertical resolution unit */
2889 (void) WriteBlobMSBShort(image,units); /* height unit */
2892 static inline size_t WriteChannelSize(const PSDInfo *psd_info,Image *image,
2893 const signed short channel)
2898 count=(size_t) WriteBlobShort(image,channel);
2899 count+=SetPSDSize(psd_info,image,0);
2903 static void RemoveICCProfileFromResourceBlock(StringInfo *bim_profile)
2905 register const unsigned char
2922 length=GetStringInfoLength(bim_profile);
2925 datum=GetStringInfoDatum(bim_profile);
2926 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
2928 register unsigned char
2931 q=(unsigned char *) p;
2932 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
2934 p=PushLongPixel(MSBEndian,p,&long_sans);
2935 p=PushShortPixel(MSBEndian,p,&id);
2936 p=PushShortPixel(MSBEndian,p,&short_sans);
2937 p=PushLongPixel(MSBEndian,p,&count);
2938 if (id == 0x0000040f)
2943 quantum=PSDQuantum(count)+12;
2944 if ((quantum >= 12) && (quantum < (ssize_t) length))
2946 if ((q+quantum < (datum+length-16)))
2947 (void) CopyMagickMemory(q,q+quantum,length-quantum-(q-datum));
2948 SetStringInfoLength(bim_profile,length-quantum);
2953 if ((count & 0x01) != 0)
2958 static void RemoveResolutionFromResourceBlock(StringInfo *bim_profile)
2960 register const unsigned char
2977 length=GetStringInfoLength(bim_profile);
2980 datum=GetStringInfoDatum(bim_profile);
2981 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
2983 register unsigned char
2989 q=(unsigned char *) p;
2990 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
2992 p=PushLongPixel(MSBEndian,p,&long_sans);
2993 p=PushShortPixel(MSBEndian,p,&id);
2994 p=PushShortPixel(MSBEndian,p,&short_sans);
2995 p=PushLongPixel(MSBEndian,p,&count);
2996 cnt=PSDQuantum(count);
2999 if ((id == 0x000003ed) && (cnt < (ssize_t) (length-12)))
3001 (void) CopyMagickMemory(q,q+cnt+12,length-(cnt+12)-(q-datum));
3002 SetStringInfoLength(bim_profile,length-(cnt+12));
3006 if ((count & 0x01) != 0)
3011 static const StringInfo *GetAdditionalInformation(const ImageInfo *image_info,
3012 Image *image,ExceptionInfo *exception)
3014 #define PSDKeySize 5
3015 #define PSDAllowedLength 36
3020 /* Whitelist of keys from: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ */
3022 allowed[PSDAllowedLength][PSDKeySize] = {
3023 "blnc", "blwh", "brit", "brst", "clbl", "clrL", "curv", "expA", "FMsk",
3024 "GdFl", "grdm", "hue ", "hue2", "infx", "knko", "lclr", "levl", "lnsr",
3025 "lfx2", "luni", "lrFX", "lspf", "lyid", "lyvr", "mixr", "nvrt", "phfl",
3026 "post", "PtFl", "selc", "shpa", "sn2P", "SoCo", "thrs", "tsly", "vibA"
3052 info=GetImageProfile(image,"psd:additional-info");
3053 if (info == (const StringInfo *) NULL)
3054 return((const StringInfo *) NULL);
3055 option=GetImageOption(image_info,"psd:additional-info");
3056 if (LocaleCompare(option,"all") == 0)
3058 if (LocaleCompare(option,"selective") != 0)
3060 profile=RemoveImageProfile(image,"psd:additional-info");
3061 return(DestroyStringInfo(profile));
3063 length=GetStringInfoLength(info);
3064 p=GetStringInfoDatum(info);
3065 remaining_length=length;
3067 while (remaining_length >= 12)
3069 /* skip over signature */
3076 size=(unsigned int) (*p++) << 24;
3077 size|=(unsigned int) (*p++) << 16;
3078 size|=(unsigned int) (*p++) << 8;
3079 size|=(unsigned int) (*p++);
3080 size=size & 0xffffffff;
3081 remaining_length-=12;
3082 if ((size_t) size > remaining_length)
3083 return((const StringInfo *) NULL);
3085 for (i=0; i < PSDAllowedLength; i++)
3087 if (LocaleNCompare(key,allowed[i],PSDKeySize) != 0)
3093 remaining_length-=(size_t) size;
3094 if (found == MagickFalse)
3096 if (remaining_length > 0)
3097 p=(unsigned char *) CopyMagickMemory(p-12,p+size,remaining_length);
3100 length+=(size_t) size+12;
3103 profile=RemoveImageProfile(image,"psd:additional-info");
3105 return(DestroyStringInfo(profile));
3106 SetStringInfoLength(profile,(const size_t) length);
3107 SetImageProfile(image,"psd:additional-info",info,exception);
3111 static MagickBooleanType WritePSDLayersInternal(Image *image,
3112 const ImageInfo *image_info,const PSDInfo *psd_info,size_t *layers_size,
3113 ExceptionInfo *exception)
3116 layer_name[MagickPathExtent];
3132 *layer_size_offsets,
3147 base_image=GetNextImageInList(image);
3148 if (base_image == (Image *) NULL)
3151 size_offset=TellBlob(image);
3152 SetPSDSize(psd_info,image,0);
3154 for (next_image=base_image; next_image != NULL; )
3157 next_image=GetNextImageInList(next_image);
3159 if (image->alpha_trait != UndefinedPixelTrait)
3160 size+=WriteBlobShort(image,-(unsigned short) layer_count);
3162 size+=WriteBlobShort(image,(unsigned short) layer_count);
3163 layer_size_offsets=(MagickOffsetType *) AcquireQuantumMemory(
3164 (size_t) layer_count,sizeof(MagickOffsetType));
3165 if (layer_size_offsets == (MagickOffsetType *) NULL)
3166 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3168 for (next_image=base_image; next_image != NULL; )
3180 mask=(Image *) NULL;
3181 property=GetImageArtifact(next_image,"psd:opacity-mask");
3183 if (property != (const char *) NULL)
3185 mask=(Image *) GetImageRegistry(ImageRegistryType,property,exception);
3186 default_color=strlen(property) == 9 ? 255 : 0;
3188 size+=WriteBlobSignedLong(image,(signed int) next_image->page.y);
3189 size+=WriteBlobSignedLong(image,(signed int) next_image->page.x);
3190 size+=WriteBlobSignedLong(image,(signed int) (next_image->page.y+
3192 size+=WriteBlobSignedLong(image,(signed int) (next_image->page.x+
3193 next_image->columns));
3195 if ((next_image->storage_class != PseudoClass) &&
3196 (IsImageGray(next_image) == MagickFalse))
3197 channels=next_image->colorspace == CMYKColorspace ? 4U : 3U;
3198 total_channels=channels;
3199 if (next_image->alpha_trait != UndefinedPixelTrait)
3201 if (mask != (Image *) NULL)
3203 size+=WriteBlobShort(image,total_channels);
3204 layer_size_offsets[layer_index++]=TellBlob(image);
3205 for (i=0; i < (ssize_t) channels; i++)
3206 size+=WriteChannelSize(psd_info,image,(signed short) i);
3207 if (next_image->alpha_trait != UndefinedPixelTrait)
3208 size+=WriteChannelSize(psd_info,image,-1);
3209 if (mask != (Image *) NULL)
3210 size+=WriteChannelSize(psd_info,image,-2);
3211 size+=WriteBlobString(image,image->endian == LSBEndian ? "MIB8" :"8BIM");
3212 size+=WriteBlobString(image,CompositeOperatorToPSDBlendMode(image));
3213 property=GetImageArtifact(next_image,"psd:layer.opacity");
3214 if (property != (const char *) NULL)
3219 opacity=(Quantum) StringToInteger(property);
3220 size+=WriteBlobByte(image,ScaleQuantumToChar(opacity));
3221 (void) ApplyPSDLayerOpacity(next_image,opacity,MagickTrue,exception);
3224 size+=WriteBlobByte(image,255);
3225 size+=WriteBlobByte(image,0);
3226 size+=WriteBlobByte(image,next_image->compose==NoCompositeOp ?
3227 1 << 0x02 : 1); /* layer properties - visible, etc. */
3228 size+=WriteBlobByte(image,0);
3229 info=GetAdditionalInformation(image_info,next_image,exception);
3230 property=(const char *) GetImageProperty(next_image,"label",exception);
3231 if (property == (const char *) NULL)
3233 (void) FormatLocaleString(layer_name,MagickPathExtent,"L%.20g",
3234 (double) layer_index);
3235 property=layer_name;
3237 name_length=strlen(property)+1;
3238 if ((name_length % 4) != 0)
3239 name_length+=(4-(name_length % 4));
3240 if (info != (const StringInfo *) NULL)
3241 name_length+=GetStringInfoLength(info);
3243 if (mask != (Image *) NULL)
3245 size+=WriteBlobLong(image,(unsigned int) name_length);
3246 if (mask == (Image *) NULL)
3247 size+=WriteBlobLong(image,0);
3250 if (mask->compose != NoCompositeOp)
3251 (void) ApplyPSDOpacityMask(next_image,mask,ScaleCharToQuantum(
3252 default_color),MagickTrue,exception);
3253 mask->page.y+=image->page.y;
3254 mask->page.x+=image->page.x;
3255 size+=WriteBlobLong(image,20);
3256 size+=WriteBlobSignedLong(image,mask->page.y);
3257 size+=WriteBlobSignedLong(image,mask->page.x);
3258 size+=WriteBlobSignedLong(image,(const signed int) mask->rows+
3260 size+=WriteBlobSignedLong(image,(const signed int) mask->columns+
3262 size+=WriteBlobByte(image,default_color);
3263 size+=WriteBlobByte(image,mask->compose == NoCompositeOp ? 2 : 0);
3264 size+=WriteBlobMSBShort(image,0);
3266 size+=WriteBlobLong(image,0);
3267 size+=WritePascalString(image,property,4);
3268 if (info != (const StringInfo *) NULL)
3269 size+=WriteBlob(image,GetStringInfoLength(info),
3270 GetStringInfoDatum(info));
3271 next_image=GetNextImageInList(next_image);
3276 next_image=base_image;
3278 while (next_image != NULL)
3280 length=WritePSDChannels(psd_info,image_info,image,next_image,
3281 layer_size_offsets[layer_index++],MagickTrue,exception);
3288 next_image=GetNextImageInList(next_image);
3291 Write the total size
3293 if (layers_size != (size_t*) NULL)
3295 if ((size/2) != ((size+1)/2))
3296 rounded_size=size+1;
3299 (void) WritePSDSize(psd_info,image,rounded_size,size_offset);
3300 layer_size_offsets=(MagickOffsetType *) RelinquishMagickMemory(
3301 layer_size_offsets);
3303 Remove the opacity mask from the registry
3305 next_image=base_image;
3306 while (next_image != (Image *) NULL)
3308 property=GetImageArtifact(next_image,"psd:opacity-mask");
3309 if (property != (const char *) NULL)
3310 DeleteImageRegistry(property);
3311 next_image=GetNextImageInList(next_image);
3317 ModuleExport MagickBooleanType WritePSDLayers(Image * image,
3318 const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception)
3326 domain=CoderPolicyDomain;
3327 rights=WritePolicyRights;
3328 if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
3330 return WritePSDLayersInternal(image,image_info,psd_info,(size_t*) NULL,
3334 static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
3335 Image *image,ExceptionInfo *exception)
3360 assert(image_info != (const ImageInfo *) NULL);
3361 assert(image_info->signature == MagickCoreSignature);
3362 assert(image != (Image *) NULL);
3363 assert(image->signature == MagickCoreSignature);
3364 if (image->debug != MagickFalse)
3365 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3366 assert(exception != (ExceptionInfo *) NULL);
3367 assert(exception->signature == MagickCoreSignature);
3368 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
3369 if (status == MagickFalse)
3371 packet_size=(size_t) (image->depth > 8 ? 6 : 3);
3372 if (image->alpha_trait != UndefinedPixelTrait)
3373 packet_size+=image->depth > 8 ? 2 : 1;
3375 if ((LocaleCompare(image_info->magick,"PSB") == 0) ||
3376 (image->columns > 30000) || (image->rows > 30000))
3378 (void) WriteBlob(image,4,(const unsigned char *) "8BPS");
3379 (void) WriteBlobMSBShort(image,psd_info.version); /* version */
3380 for (i=1; i <= 6; i++)
3381 (void) WriteBlobByte(image, 0); /* 6 bytes of reserved */
3382 /* When the image has a color profile it won't be converted to gray scale */
3383 if ((GetImageProfile(image,"icc") == (StringInfo *) NULL) &&
3384 (SetImageGray(image,exception) != MagickFalse))
3385 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
3387 if ((image_info->type != TrueColorType) && (image_info->type !=
3388 TrueColorAlphaType) && (image->storage_class == PseudoClass))
3389 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
3392 if (image->storage_class == PseudoClass)
3393 (void) SetImageStorageClass(image,DirectClass,exception);
3394 if (image->colorspace != CMYKColorspace)
3395 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 4UL : 3UL);
3397 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 5UL : 4UL);
3399 (void) WriteBlobMSBShort(image,(unsigned short) num_channels);
3400 (void) WriteBlobMSBLong(image,(unsigned int) image->rows);
3401 (void) WriteBlobMSBLong(image,(unsigned int) image->columns);
3402 if (IsImageGray(image) != MagickFalse)
3410 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
3411 MagickTrue : MagickFalse;
3412 (void) WriteBlobMSBShort(image,(unsigned short)
3413 (monochrome != MagickFalse ? 1 : image->depth > 8 ? 16 : 8));
3414 (void) WriteBlobMSBShort(image,(unsigned short)
3415 (monochrome != MagickFalse ? BitmapMode : GrayscaleMode));
3419 (void) WriteBlobMSBShort(image,(unsigned short) (image->storage_class ==
3420 PseudoClass ? 8 : image->depth > 8 ? 16 : 8));
3422 if (((image_info->colorspace != UndefinedColorspace) ||
3423 (image->colorspace != CMYKColorspace)) &&
3424 (image_info->colorspace != CMYKColorspace))
3426 (void) TransformImageColorspace(image,sRGBColorspace,exception);
3427 (void) WriteBlobMSBShort(image,(unsigned short)
3428 (image->storage_class == PseudoClass ? IndexedMode : RGBMode));
3432 if (image->colorspace != CMYKColorspace)
3433 (void) TransformImageColorspace(image,CMYKColorspace,exception);
3434 (void) WriteBlobMSBShort(image,CMYKMode);
3437 if ((IsImageGray(image) != MagickFalse) ||
3438 (image->storage_class == DirectClass) || (image->colors > 256))
3439 (void) WriteBlobMSBLong(image,0);
3443 Write PSD raster colormap.
3445 (void) WriteBlobMSBLong(image,768);
3446 for (i=0; i < (ssize_t) image->colors; i++)
3447 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].red));
3448 for ( ; i < 256; i++)
3449 (void) WriteBlobByte(image,0);
3450 for (i=0; i < (ssize_t) image->colors; i++)
3451 (void) WriteBlobByte(image,ScaleQuantumToChar(
3452 image->colormap[i].green));
3453 for ( ; i < 256; i++)
3454 (void) WriteBlobByte(image,0);
3455 for (i=0; i < (ssize_t) image->colors; i++)
3456 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].blue));
3457 for ( ; i < 256; i++)
3458 (void) WriteBlobByte(image,0);
3461 Image resource block.
3463 length=28; /* 0x03EB */
3464 bim_profile=(StringInfo *) GetImageProfile(image,"8bim");
3465 icc_profile=GetImageProfile(image,"icc");
3466 if (bim_profile != (StringInfo *) NULL)
3468 bim_profile=CloneStringInfo(bim_profile);
3469 if (icc_profile != (StringInfo *) NULL)
3470 RemoveICCProfileFromResourceBlock(bim_profile);
3471 RemoveResolutionFromResourceBlock(bim_profile);
3472 length+=PSDQuantum(GetStringInfoLength(bim_profile));
3474 if (icc_profile != (const StringInfo *) NULL)
3475 length+=PSDQuantum(GetStringInfoLength(icc_profile))+12;
3476 (void) WriteBlobMSBLong(image,(unsigned int) length);
3477 WriteResolutionResourceBlock(image);
3478 if (bim_profile != (StringInfo *) NULL)
3480 (void) WriteBlob(image,GetStringInfoLength(bim_profile),
3481 GetStringInfoDatum(bim_profile));
3482 bim_profile=DestroyStringInfo(bim_profile);
3484 if (icc_profile != (StringInfo *) NULL)
3486 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
3487 (void) WriteBlobMSBShort(image,0x0000040F);
3488 (void) WriteBlobMSBShort(image,0);
3489 (void) WriteBlobMSBLong(image,(unsigned int) GetStringInfoLength(
3491 (void) WriteBlob(image,GetStringInfoLength(icc_profile),
3492 GetStringInfoDatum(icc_profile));
3493 if ((MagickOffsetType) GetStringInfoLength(icc_profile) !=
3494 PSDQuantum(GetStringInfoLength(icc_profile)))
3495 (void) WriteBlobByte(image,0);
3497 if (status != MagickFalse)
3505 size_offset=TellBlob(image);
3506 SetPSDSize(&psd_info,image,0);
3507 status=WritePSDLayersInternal(image,image_info,&psd_info,&size,
3509 size_offset+=WritePSDSize(&psd_info,image,size+
3510 (psd_info.version == 1 ? 8 : 16),size_offset);
3512 (void) WriteBlobMSBLong(image,0); /* user mask data */
3514 Write composite image.
3516 if (status != MagickFalse)
3521 compression=image->compression;
3522 if (image->compression == ZipCompression)
3523 image->compression=RLECompression;
3524 if (WritePSDChannels(&psd_info,image_info,image,image,0,MagickFalse,
3527 image->compression=compression;
3529 (void) CloseBlob(image);