2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Adobe Photoshop Image Format %
23 % Copyright 1999-2018 ImageMagick Studio LLC, a non-profit organization %
24 % dedicated to making software imaging solutions freely available. %
26 % You may not use this file except in compliance with the License. You may %
27 % obtain a copy of the License at %
29 % https://www.imagemagick.org/script/license.php %
31 % Unless required by applicable law or agreed to in writing, software %
32 % distributed under the License is distributed on an "AS IS" BASIS, %
33 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
34 % See the License for the specific language governing permissions and %
35 % limitations under the License. %
37 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
45 #include "MagickCore/studio.h"
46 #include "MagickCore/artifact.h"
47 #include "MagickCore/attribute.h"
48 #include "MagickCore/blob.h"
49 #include "MagickCore/blob-private.h"
50 #include "MagickCore/cache.h"
51 #include "MagickCore/channel.h"
52 #include "MagickCore/colormap.h"
53 #include "MagickCore/colormap-private.h"
54 #include "MagickCore/colorspace.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/image.h"
61 #include "MagickCore/image-private.h"
62 #include "MagickCore/list.h"
63 #include "MagickCore/log.h"
64 #include "MagickCore/magick.h"
65 #include "MagickCore/memory_.h"
66 #include "MagickCore/module.h"
67 #include "MagickCore/monitor-private.h"
68 #include "MagickCore/option.h"
69 #include "MagickCore/pixel.h"
70 #include "MagickCore/pixel-accessor.h"
71 #include "MagickCore/policy.h"
72 #include "MagickCore/profile.h"
73 #include "MagickCore/property.h"
74 #include "MagickCore/registry.h"
75 #include "MagickCore/quantum-private.h"
76 #include "MagickCore/static.h"
77 #include "MagickCore/string_.h"
78 #include "MagickCore/string-private.h"
79 #include "MagickCore/thread-private.h"
80 #ifdef MAGICKCORE_ZLIB_DELEGATE
83 #include "psd-private.h"
88 #define MaxPSDChannels 56
89 #define PSDQuantum(x) (((ssize_t) (x)+1) & -2)
92 Enumerated declaractions.
98 ZipWithoutPrediction = 2,
100 } PSDCompressionType;
109 MultichannelMode = 7,
115 Typedef declaractions.
117 typedef struct _ChannelInfo
126 typedef struct _MaskInfo
139 typedef struct _LayerInfo
142 channel_info[MaxPSDChannels];
177 Forward declarations.
179 static MagickBooleanType
180 WritePSDImage(const ImageInfo *,Image *,ExceptionInfo *);
183 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
193 % IsPSD()() returns MagickTrue if the image format type, identified by the
194 % magick string, is PSD.
196 % The format of the IsPSD method is:
198 % MagickBooleanType IsPSD(const unsigned char *magick,const size_t length)
200 % A description of each parameter follows:
202 % o magick: compare image format pattern against these bytes.
204 % o length: Specifies the length of the magick string.
207 static MagickBooleanType IsPSD(const unsigned char *magick,const size_t length)
211 if (LocaleNCompare((const char *) magick,"8BPS",4) == 0)
217 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
221 % R e a d P S D I m a g e %
225 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
227 % ReadPSDImage() reads an Adobe Photoshop image file and returns it. It
228 % allocates the memory necessary for the new Image structure and returns a
229 % pointer to the new image.
231 % The format of the ReadPSDImage method is:
233 % Image *ReadPSDImage(image_info,ExceptionInfo *exception)
235 % A description of each parameter follows:
237 % o image_info: the image info.
239 % o exception: return any errors or warnings in this structure.
243 static const char *CompositeOperatorToPSDBlendMode(Image *image)
245 switch (image->compose)
247 case ColorBurnCompositeOp:
248 return(image->endian == LSBEndian ? "vidi" : "idiv");
249 case ColorDodgeCompositeOp:
250 return(image->endian == LSBEndian ? " vid" : "div ");
251 case ColorizeCompositeOp:
252 return(image->endian == LSBEndian ? "rloc" : "colr");
253 case DarkenCompositeOp:
254 return(image->endian == LSBEndian ? "krad" : "dark");
255 case DifferenceCompositeOp:
256 return(image->endian == LSBEndian ? "ffid" : "diff");
257 case DissolveCompositeOp:
258 return(image->endian == LSBEndian ? "ssid" : "diss");
259 case ExclusionCompositeOp:
260 return(image->endian == LSBEndian ? "dums" : "smud");
261 case HardLightCompositeOp:
262 return(image->endian == LSBEndian ? "tiLh" : "hLit");
263 case HardMixCompositeOp:
264 return(image->endian == LSBEndian ? "xiMh" : "hMix");
266 return(image->endian == LSBEndian ? " euh" : "hue ");
267 case LightenCompositeOp:
268 return(image->endian == LSBEndian ? "etil" : "lite");
269 case LinearBurnCompositeOp:
270 return(image->endian == LSBEndian ? "nrbl" : "lbrn");
271 case LinearDodgeCompositeOp:
272 return(image->endian == LSBEndian ? "gddl" : "lddg");
273 case LinearLightCompositeOp:
274 return(image->endian == LSBEndian ? "tiLl" : "lLit");
275 case LuminizeCompositeOp:
276 return(image->endian == LSBEndian ? " mul" : "lum ");
277 case MultiplyCompositeOp:
278 return(image->endian == LSBEndian ? " lum" : "mul ");
279 case OverlayCompositeOp:
280 return(image->endian == LSBEndian ? "revo" : "over");
281 case PinLightCompositeOp:
282 return(image->endian == LSBEndian ? "tiLp" : "pLit");
283 case SaturateCompositeOp:
284 return(image->endian == LSBEndian ? " tas" : "sat ");
285 case ScreenCompositeOp:
286 return(image->endian == LSBEndian ? "nrcs" : "scrn");
287 case SoftLightCompositeOp:
288 return(image->endian == LSBEndian ? "tiLs" : "sLit");
289 case VividLightCompositeOp:
290 return(image->endian == LSBEndian ? "tiLv" : "vLit");
291 case OverCompositeOp:
293 return(image->endian == LSBEndian ? "mron" : "norm");
298 For some reason Photoshop seems to blend semi-transparent pixels with white.
299 This method reverts the blending. This can be disabled by setting the
300 option 'psd:alpha-unblend' to off.
302 static MagickBooleanType CorrectPSDAlphaBlend(const ImageInfo *image_info,
303 Image *image,ExceptionInfo* exception)
314 if (image->alpha_trait != BlendPixelTrait || image->colorspace != sRGBColorspace)
316 option=GetImageOption(image_info,"psd:alpha-unblend");
317 if (IsStringFalse(option) != MagickFalse)
320 #if defined(MAGICKCORE_OPENMP_SUPPORT)
321 #pragma omp parallel for schedule(static,4) shared(status) \
322 magick_number_threads(image,image,image->rows,1)
324 for (y=0; y < (ssize_t) image->rows; y++)
332 if (status == MagickFalse)
334 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
335 if (q == (Quantum *) NULL)
340 for (x=0; x < (ssize_t) image->columns; x++)
348 gamma=QuantumScale*GetPixelAlpha(image, q);
349 if (gamma != 0.0 && gamma != 1.0)
351 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
353 PixelChannel channel = GetPixelChannelChannel(image,i);
354 if (channel != AlphaPixelChannel)
355 q[i]=ClampToQuantum((q[i]-((1.0-gamma)*QuantumRange))/gamma);
358 q+=GetPixelChannels(image);
360 if (SyncAuthenticPixels(image,exception) == MagickFalse)
367 static inline CompressionType ConvertPSDCompression(
368 PSDCompressionType compression)
373 return RLECompression;
374 case ZipWithPrediction:
375 case ZipWithoutPrediction:
376 return ZipCompression;
378 return NoCompression;
382 static MagickBooleanType ApplyPSDLayerOpacity(Image *image,Quantum opacity,
383 MagickBooleanType revert,ExceptionInfo *exception)
391 if (image->debug != MagickFalse)
392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
393 " applying layer opacity %.20g", (double) opacity);
394 if (opacity == OpaqueAlpha)
396 if (image->alpha_trait != BlendPixelTrait)
397 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
399 #if defined(MAGICKCORE_OPENMP_SUPPORT)
400 #pragma omp parallel for schedule(static,4) shared(status) \
401 magick_number_threads(image,image,image->rows,1)
403 for (y=0; y < (ssize_t) image->rows; y++)
411 if (status == MagickFalse)
413 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
414 if (q == (Quantum *) NULL)
419 for (x=0; x < (ssize_t) image->columns; x++)
421 if (revert == MagickFalse)
422 SetPixelAlpha(image,(Quantum) (QuantumScale*(GetPixelAlpha(image,q))*
424 else if (opacity > 0)
425 SetPixelAlpha(image,(Quantum) (QuantumRange*(GetPixelAlpha(image,q)/
426 (MagickRealType) opacity)),q);
427 q+=GetPixelChannels(image);
429 if (SyncAuthenticPixels(image,exception) == MagickFalse)
436 static MagickBooleanType ApplyPSDOpacityMask(Image *image,const Image *mask,
437 Quantum background,MagickBooleanType revert,ExceptionInfo *exception)
451 if (image->debug != MagickFalse)
452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
453 " applying opacity mask");
454 complete_mask=CloneImage(image,image->columns,image->rows,MagickTrue,
456 if (complete_mask == (Image *) NULL)
458 complete_mask->alpha_trait=BlendPixelTrait;
459 GetPixelInfo(complete_mask,&color);
460 color.red=background;
461 SetImageColor(complete_mask,&color,exception);
462 status=CompositeImage(complete_mask,mask,OverCompositeOp,MagickTrue,
463 mask->page.x-image->page.x,mask->page.y-image->page.y,exception);
464 if (status == MagickFalse)
466 complete_mask=DestroyImage(complete_mask);
469 image->alpha_trait=BlendPixelTrait;
470 #if defined(MAGICKCORE_OPENMP_SUPPORT)
471 #pragma omp parallel for schedule(static,4) shared(status) \
472 magick_number_threads(image,image,image->rows,1)
474 for (y=0; y < (ssize_t) image->rows; y++)
485 if (status == MagickFalse)
487 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
488 p=GetAuthenticPixels(complete_mask,0,y,complete_mask->columns,1,exception);
489 if ((q == (Quantum *) NULL) || (p == (Quantum *) NULL))
494 for (x=0; x < (ssize_t) image->columns; x++)
500 alpha=GetPixelAlpha(image,q);
501 intensity=GetPixelIntensity(complete_mask,p);
502 if (revert == MagickFalse)
503 SetPixelAlpha(image,ClampToQuantum(intensity*(QuantumScale*alpha)),q);
504 else if (intensity > 0)
505 SetPixelAlpha(image,ClampToQuantum((alpha/intensity)*QuantumRange),q);
506 q+=GetPixelChannels(image);
507 p+=GetPixelChannels(complete_mask);
509 if (SyncAuthenticPixels(image,exception) == MagickFalse)
512 complete_mask=DestroyImage(complete_mask);
516 static void PreservePSDOpacityMask(Image *image,LayerInfo* layer_info,
517 ExceptionInfo *exception)
528 if (image->debug != MagickFalse)
529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
530 " preserving opacity mask");
531 random_info=AcquireRandomInfo();
532 key_info=GetRandomKey(random_info,2+1);
533 key=(char *) GetStringInfoDatum(key_info);
534 key[8]=layer_info->mask.background;
536 layer_info->mask.image->page.x+=layer_info->page.x;
537 layer_info->mask.image->page.y+=layer_info->page.y;
538 (void) SetImageRegistry(ImageRegistryType,(const char *) key,
539 layer_info->mask.image,exception);
540 (void) SetImageArtifact(layer_info->image,"psd:opacity-mask",
542 key_info=DestroyStringInfo(key_info);
543 random_info=DestroyRandomInfo(random_info);
546 static ssize_t DecodePSDPixels(const size_t number_compact_pixels,
547 const unsigned char *compact_pixels,const ssize_t depth,
548 const size_t number_pixels,unsigned char *pixels)
550 #define CheckNumberCompactPixels \
555 #define CheckNumberPixels(count) \
556 if (((ssize_t) i + count) > (ssize_t) number_pixels) \
573 packets=(ssize_t) number_compact_pixels;
574 for (i=0; (packets > 1) && (i < (ssize_t) number_pixels); )
577 length=(size_t) (*compact_pixels++);
583 CheckNumberCompactPixels;
584 pixel=(*compact_pixels++);
585 for (j=0; j < (ssize_t) length; j++)
591 CheckNumberPixels(8);
592 *pixels++=(pixel >> 7) & 0x01 ? 0U : 255U;
593 *pixels++=(pixel >> 6) & 0x01 ? 0U : 255U;
594 *pixels++=(pixel >> 5) & 0x01 ? 0U : 255U;
595 *pixels++=(pixel >> 4) & 0x01 ? 0U : 255U;
596 *pixels++=(pixel >> 3) & 0x01 ? 0U : 255U;
597 *pixels++=(pixel >> 2) & 0x01 ? 0U : 255U;
598 *pixels++=(pixel >> 1) & 0x01 ? 0U : 255U;
599 *pixels++=(pixel >> 0) & 0x01 ? 0U : 255U;
604 CheckNumberPixels(4);
605 *pixels++=(unsigned char) ((pixel >> 6) & 0x03);
606 *pixels++=(unsigned char) ((pixel >> 4) & 0x03);
607 *pixels++=(unsigned char) ((pixel >> 2) & 0x03);
608 *pixels++=(unsigned char) ((pixel & 0x03) & 0x03);
613 CheckNumberPixels(2);
614 *pixels++=(unsigned char) ((pixel >> 4) & 0xff);
615 *pixels++=(unsigned char) ((pixel & 0x0f) & 0xff);
620 CheckNumberPixels(1);
621 *pixels++=(unsigned char) pixel;
629 for (j=0; j < (ssize_t) length; j++)
631 CheckNumberCompactPixels;
636 CheckNumberPixels(8);
637 *pixels++=(*compact_pixels >> 7) & 0x01 ? 0U : 255U;
638 *pixels++=(*compact_pixels >> 6) & 0x01 ? 0U : 255U;
639 *pixels++=(*compact_pixels >> 5) & 0x01 ? 0U : 255U;
640 *pixels++=(*compact_pixels >> 4) & 0x01 ? 0U : 255U;
641 *pixels++=(*compact_pixels >> 3) & 0x01 ? 0U : 255U;
642 *pixels++=(*compact_pixels >> 2) & 0x01 ? 0U : 255U;
643 *pixels++=(*compact_pixels >> 1) & 0x01 ? 0U : 255U;
644 *pixels++=(*compact_pixels >> 0) & 0x01 ? 0U : 255U;
649 CheckNumberPixels(4);
650 *pixels++=(*compact_pixels >> 6) & 0x03;
651 *pixels++=(*compact_pixels >> 4) & 0x03;
652 *pixels++=(*compact_pixels >> 2) & 0x03;
653 *pixels++=(*compact_pixels & 0x03) & 0x03;
658 CheckNumberPixels(2);
659 *pixels++=(*compact_pixels >> 4) & 0xff;
660 *pixels++=(*compact_pixels & 0x0f) & 0xff;
665 CheckNumberPixels(1);
666 *pixels++=(*compact_pixels);
676 static inline LayerInfo *DestroyLayerInfo(LayerInfo *layer_info,
677 const ssize_t number_layers)
682 for (i=0; i<number_layers; i++)
684 if (layer_info[i].image != (Image *) NULL)
685 layer_info[i].image=DestroyImage(layer_info[i].image);
686 if (layer_info[i].mask.image != (Image *) NULL)
687 layer_info[i].mask.image=DestroyImage(layer_info[i].mask.image);
688 if (layer_info[i].info != (StringInfo *) NULL)
689 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
692 return (LayerInfo *) RelinquishMagickMemory(layer_info);
695 static inline size_t GetPSDPacketSize(const Image *image)
697 if (image->storage_class == PseudoClass)
699 if (image->colors > 256)
702 if (image->depth > 16)
704 if (image->depth > 8)
710 static inline MagickSizeType GetPSDSize(const PSDInfo *psd_info,Image *image)
712 if (psd_info->version == 1)
713 return((MagickSizeType) ReadBlobLong(image));
714 return((MagickSizeType) ReadBlobLongLong(image));
717 static inline size_t GetPSDRowSize(Image *image)
719 if (image->depth == 1)
720 return(((image->columns+7)/8)*GetPSDPacketSize(image));
722 return(image->columns*GetPSDPacketSize(image));
725 static const char *ModeToString(PSDImageType type)
729 case BitmapMode: return "Bitmap";
730 case GrayscaleMode: return "Grayscale";
731 case IndexedMode: return "Indexed";
732 case RGBMode: return "RGB";
733 case CMYKMode: return "CMYK";
734 case MultichannelMode: return "Multichannel";
735 case DuotoneMode: return "Duotone";
736 case LabMode: return "L*A*B";
737 default: return "unknown";
741 static MagickBooleanType NegateCMYK(Image *image,ExceptionInfo *exception)
749 channel_mask=SetImageChannelMask(image,(ChannelType)(AllChannels &~
751 status=NegateImage(image,MagickFalse,exception);
752 (void) SetImageChannelMask(image,channel_mask);
756 static 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
1009 q=GetAuthenticPixels(image,0,row,image->columns,1,exception);
1010 if (q == (Quantum *) NULL)
1012 packet_size=GetPSDPacketSize(image);
1013 for (x=0; x < (ssize_t) image->columns; x++)
1015 if (packet_size == 1)
1016 pixel=ScaleCharToQuantum(*p++);
1017 else if (packet_size == 2)
1022 p=PushShortPixel(MSBEndian,p,&nibble);
1023 pixel=ScaleShortToQuantum(nibble);
1030 p=PushFloatPixel(MSBEndian,p,&nibble);
1031 pixel=ClampToQuantum((MagickRealType)QuantumRange*nibble);
1033 if (image->depth > 1)
1035 SetPSDPixel(image,channels,type,packet_size,pixel,q,exception);
1036 q+=GetPixelChannels(image);
1044 number_bits=image->columns-x;
1045 if (number_bits > 8)
1047 for (bit = 0; bit < number_bits; bit++)
1049 SetPSDPixel(image,channels,type,packet_size,(((unsigned char) pixel)
1050 & (0x01 << (7-bit))) != 0 ? 0 : QuantumRange,q,exception);
1051 q+=GetPixelChannels(image);
1054 if (x != (ssize_t) image->columns)
1059 return(SyncAuthenticPixels(image,exception));
1062 static MagickBooleanType ReadPSDChannelRaw(Image *image,const size_t channels,
1063 const ssize_t type,ExceptionInfo *exception)
1078 if (image->debug != MagickFalse)
1079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1080 " layer data is RAW");
1082 row_size=GetPSDRowSize(image);
1083 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
1084 if (pixels == (unsigned char *) NULL)
1085 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1089 for (y=0; y < (ssize_t) image->rows; y++)
1093 count=ReadBlob(image,row_size,pixels);
1094 if (count != row_size)
1097 status=ReadPSDChannelPixels(image,channels,y,type,pixels,exception);
1098 if (status == MagickFalse)
1102 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1106 static inline MagickOffsetType *ReadPSDRLESizes(Image *image,
1107 const PSDInfo *psd_info,const size_t size)
1115 sizes=(MagickOffsetType *) AcquireQuantumMemory(size,sizeof(*sizes));
1116 if(sizes != (MagickOffsetType *) NULL)
1118 for (y=0; y < (ssize_t) size; y++)
1120 if (psd_info->version == 1)
1121 sizes[y]=(MagickOffsetType) ReadBlobShort(image);
1123 sizes[y]=(MagickOffsetType) ReadBlobLong(image);
1129 static MagickBooleanType ReadPSDChannelRLE(Image *image,const PSDInfo *psd_info,
1130 const ssize_t type,MagickOffsetType *sizes,ExceptionInfo *exception)
1147 if (image->debug != MagickFalse)
1148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1149 " layer data is RLE compressed");
1151 row_size=GetPSDRowSize(image);
1152 pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
1153 if (pixels == (unsigned char *) NULL)
1154 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1158 for (y=0; y < (ssize_t) image->rows; y++)
1159 if ((MagickOffsetType) length < sizes[y])
1160 length=(size_t) sizes[y];
1162 if (length > (row_size+512)) // arbitrary number
1164 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1165 ThrowBinaryException(ResourceLimitError,"InvalidLength",image->filename);
1168 compact_pixels=(unsigned char *) AcquireQuantumMemory(length,sizeof(*pixels));
1169 if (compact_pixels == (unsigned char *) NULL)
1171 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1172 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1176 (void) ResetMagickMemory(compact_pixels,0,length*sizeof(*compact_pixels));
1179 for (y=0; y < (ssize_t) image->rows; y++)
1183 count=ReadBlob(image,(size_t) sizes[y],compact_pixels);
1184 if (count != (ssize_t) sizes[y])
1187 count=DecodePSDPixels((size_t) sizes[y],compact_pixels,
1188 (ssize_t) (image->depth == 1 ? 123456 : image->depth),row_size,pixels);
1189 if (count != (ssize_t) row_size)
1192 status=ReadPSDChannelPixels(image,psd_info->channels,y,type,pixels,
1194 if (status == MagickFalse)
1198 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1199 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1203 #ifdef MAGICKCORE_ZLIB_DELEGATE
1204 static MagickBooleanType ReadPSDChannelZip(Image *image,const size_t channels,
1205 const ssize_t type,const PSDCompressionType compression,
1206 const size_t compact_size,ExceptionInfo *exception)
1211 register unsigned char
1230 if (image->debug != MagickFalse)
1231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1232 " layer data is ZIP compressed");
1234 compact_pixels=(unsigned char *) AcquireQuantumMemory(compact_size,
1235 sizeof(*compact_pixels));
1236 if (compact_pixels == (unsigned char *) NULL)
1237 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1240 packet_size=GetPSDPacketSize(image);
1241 row_size=image->columns*packet_size;
1242 count=image->rows*row_size;
1244 pixels=(unsigned char *) AcquireQuantumMemory(count,sizeof(*pixels));
1245 if (pixels == (unsigned char *) NULL)
1247 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1248 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1251 if (ReadBlob(image,compact_size,compact_pixels) != (ssize_t) compact_size)
1253 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1254 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1255 ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile",
1259 ResetMagickMemory(&stream,0,sizeof(stream));
1260 stream.data_type=Z_BINARY;
1261 stream.next_in=(Bytef *)compact_pixels;
1262 stream.avail_in=(uInt) compact_size;
1263 stream.next_out=(Bytef *)pixels;
1264 stream.avail_out=(uInt) count;
1266 if (inflateInit(&stream) == Z_OK)
1271 while (stream.avail_out > 0)
1273 ret=inflate(&stream,Z_SYNC_FLUSH);
1274 if ((ret != Z_OK) && (ret != Z_STREAM_END))
1276 (void) inflateEnd(&stream);
1277 compact_pixels=(unsigned char *) RelinquishMagickMemory(
1279 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1280 return(MagickFalse);
1282 if (ret == Z_STREAM_END)
1285 (void) inflateEnd(&stream);
1288 if (compression == ZipWithPrediction)
1293 length=image->columns;
1296 if (packet_size == 2)
1298 p[2]+=p[0]+((p[1]+p[3]) >> 8);
1301 // else if (packet_size == 4)
1303 // TODO: Figure out what to do there.
1316 for (y=0; y < (ssize_t) image->rows; y++)
1318 status=ReadPSDChannelPixels(image,channels,y,type,p,exception);
1319 if (status == MagickFalse)
1325 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
1326 pixels=(unsigned char *) RelinquishMagickMemory(pixels);
1331 static MagickBooleanType ReadPSDChannel(Image *image,
1332 const ImageInfo *image_info,const PSDInfo *psd_info,LayerInfo* layer_info,
1333 const size_t channel,const PSDCompressionType compression,
1334 ExceptionInfo *exception)
1346 channel_image=image;
1347 mask=(Image *) NULL;
1348 if ((layer_info->channel_info[channel].type < -1) &&
1349 (layer_info->mask.page.width > 0) && (layer_info->mask.page.height > 0))
1355 Ignore mask that is not a user supplied layer mask, if the mask is
1356 disabled or if the flags have unsupported values.
1358 option=GetImageOption(image_info,"psd:preserve-opacity-mask");
1359 if ((layer_info->channel_info[channel].type != -2) ||
1360 (layer_info->mask.flags > 2) || ((layer_info->mask.flags & 0x02) &&
1361 (IsStringTrue(option) == MagickFalse)))
1363 SeekBlob(image,layer_info->channel_info[channel].size-2,SEEK_CUR);
1366 mask=CloneImage(image,layer_info->mask.page.width,
1367 layer_info->mask.page.height,MagickFalse,exception);
1368 if (mask != (Image *) NULL)
1370 SetImageType(mask,GrayscaleType,exception);
1375 offset=TellBlob(image);
1380 status=ReadPSDChannelRaw(channel_image,psd_info->channels,
1381 layer_info->channel_info[channel].type,exception);
1388 sizes=ReadPSDRLESizes(channel_image,psd_info,channel_image->rows);
1389 if (sizes == (MagickOffsetType *) NULL)
1390 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1392 status=ReadPSDChannelRLE(channel_image,psd_info,
1393 layer_info->channel_info[channel].type,sizes,exception);
1394 sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
1397 case ZipWithPrediction:
1398 case ZipWithoutPrediction:
1399 #ifdef MAGICKCORE_ZLIB_DELEGATE
1400 status=ReadPSDChannelZip(channel_image,layer_info->channels,
1401 layer_info->channel_info[channel].type,compression,
1402 layer_info->channel_info[channel].size-2,exception);
1404 (void) ThrowMagickException(exception,GetMagickModule(),
1405 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
1406 "'%s' (ZLIB)",image->filename);
1410 (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
1411 "CompressionNotSupported","'%.20g'",(double) compression);
1415 SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
1416 if (status == MagickFalse)
1418 if (mask != (Image *) NULL)
1420 ThrowBinaryException(CoderError,"UnableToDecompressImage",
1423 layer_info->mask.image=mask;
1427 static MagickBooleanType ReadPSDLayer(Image *image,const ImageInfo *image_info,
1428 const PSDInfo *psd_info,LayerInfo* layer_info,ExceptionInfo *exception)
1431 message[MagickPathExtent];
1442 if (image->debug != MagickFalse)
1443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1444 " setting up new layer image");
1445 if (psd_info->mode != IndexedMode)
1446 (void) SetImageBackgroundColor(layer_info->image,exception);
1447 layer_info->image->compose=PSDBlendModeToCompositeOperator(
1448 layer_info->blendkey);
1449 if (layer_info->visible == MagickFalse)
1450 layer_info->image->compose=NoCompositeOp;
1452 Set up some hidden attributes for folks that need them.
1454 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
1455 (double) layer_info->page.x);
1456 (void) SetImageArtifact(layer_info->image,"psd:layer.x",message);
1457 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
1458 (double) layer_info->page.y);
1459 (void) SetImageArtifact(layer_info->image,"psd:layer.y",message);
1460 (void) FormatLocaleString(message,MagickPathExtent,"%.20g",(double)
1461 layer_info->opacity);
1462 (void) SetImageArtifact(layer_info->image,"psd:layer.opacity",message);
1463 (void) SetImageProperty(layer_info->image,"label",(char *) layer_info->name,
1467 for (j=0; j < (ssize_t) layer_info->channels; j++)
1469 if (image->debug != MagickFalse)
1470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1471 " reading data for channel %.20g",(double) j);
1473 compression=(PSDCompressionType) ReadBlobShort(layer_info->image);
1475 /* TODO: Remove this when we figure out how to support this */
1476 if ((compression == ZipWithPrediction) && (image->depth == 32))
1478 (void) ThrowMagickException(exception,GetMagickModule(),
1479 TypeError,"CompressionNotSupported","ZipWithPrediction(32 bit)");
1480 return(MagickFalse);
1483 layer_info->image->compression=ConvertPSDCompression(compression);
1484 if (layer_info->channel_info[j].type == -1)
1485 layer_info->image->alpha_trait=BlendPixelTrait;
1487 status=ReadPSDChannel(layer_info->image,image_info,psd_info,layer_info,j,
1488 compression,exception);
1490 if (status == MagickFalse)
1494 if (status != MagickFalse)
1495 status=ApplyPSDLayerOpacity(layer_info->image,layer_info->opacity,
1496 MagickFalse,exception);
1498 if ((status != MagickFalse) &&
1499 (layer_info->image->colorspace == CMYKColorspace))
1500 status=NegateCMYK(layer_info->image,exception);
1502 if ((status != MagickFalse) && (layer_info->mask.image != (Image *) NULL))
1507 layer_info->mask.image->page.x=layer_info->mask.page.x;
1508 layer_info->mask.image->page.y=layer_info->mask.page.y;
1509 /* Do not composite the mask when it is disabled */
1510 if ((layer_info->mask.flags & 0x02) == 0x02)
1511 layer_info->mask.image->compose=NoCompositeOp;
1513 status=ApplyPSDOpacityMask(layer_info->image,layer_info->mask.image,
1514 layer_info->mask.background == 0 ? 0 : QuantumRange,MagickFalse,
1516 option=GetImageOption(image_info,"psd:preserve-opacity-mask");
1517 if (IsStringTrue(option) != MagickFalse)
1518 PreservePSDOpacityMask(image,layer_info,exception);
1519 layer_info->mask.image=DestroyImage(layer_info->mask.image);
1525 static MagickBooleanType ReadPSDLayersInternal(Image *image,
1526 const ImageInfo *image_info,const PSDInfo *psd_info,
1527 const MagickBooleanType skip_layers,ExceptionInfo *exception)
1549 size=GetPSDSize(psd_info,image);
1553 Skip layers & masks.
1555 (void) ReadBlobLong(image);
1556 count=ReadBlob(image,4,(unsigned char *) type);
1557 ReversePSDString(image,type,4);
1559 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1563 count=ReadBlob(image,4,(unsigned char *) type);
1564 ReversePSDString(image,type,4);
1565 if ((count != 0) && ((LocaleNCompare(type,"Lr16",4) == 0) ||
1566 (LocaleNCompare(type,"Lr32",4) == 0)))
1567 size=GetPSDSize(psd_info,image);
1575 layer_info=(LayerInfo *) NULL;
1576 number_layers=(short) ReadBlobShort(image);
1578 if (number_layers < 0)
1581 The first alpha channel in the merged result contains the
1582 transparency data for the merged result.
1584 number_layers=MagickAbsoluteValue(number_layers);
1585 if (image->debug != MagickFalse)
1586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1587 " negative layer count corrected for");
1588 image->alpha_trait=BlendPixelTrait;
1592 We only need to know if the image has an alpha channel
1594 if (skip_layers != MagickFalse)
1597 if (image->debug != MagickFalse)
1598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1599 " image contains %.20g layers",(double) number_layers);
1601 if (number_layers == 0)
1602 ThrowBinaryException(CorruptImageError,"InvalidNumberOfLayers",
1605 layer_info=(LayerInfo *) AcquireQuantumMemory((size_t) number_layers,
1606 sizeof(*layer_info));
1607 if (layer_info == (LayerInfo *) NULL)
1609 if (image->debug != MagickFalse)
1610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1611 " allocation of LayerInfo failed");
1612 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1615 (void) ResetMagickMemory(layer_info,0,(size_t) number_layers*
1616 sizeof(*layer_info));
1618 for (i=0; i < number_layers; i++)
1624 if (image->debug != MagickFalse)
1625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1626 " reading layer #%.20g",(double) i+1);
1627 layer_info[i].page.y=ReadBlobSignedLong(image);
1628 layer_info[i].page.x=ReadBlobSignedLong(image);
1629 y=ReadBlobSignedLong(image);
1630 x=ReadBlobSignedLong(image);
1631 layer_info[i].page.width=(size_t) (x-layer_info[i].page.x);
1632 layer_info[i].page.height=(size_t) (y-layer_info[i].page.y);
1633 layer_info[i].channels=ReadBlobShort(image);
1634 if (layer_info[i].channels < 1)
1636 layer_info=DestroyLayerInfo(layer_info,number_layers);
1637 ThrowBinaryException(CorruptImageError,"MissingImageChannel",
1640 if (layer_info[i].channels > MaxPSDChannels)
1642 layer_info=DestroyLayerInfo(layer_info,number_layers);
1643 ThrowBinaryException(CorruptImageError,"MaximumChannelsExceeded",
1646 if (image->debug != MagickFalse)
1647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1648 " offset(%.20g,%.20g), size(%.20g,%.20g), channels=%.20g",
1649 (double) layer_info[i].page.x,(double) layer_info[i].page.y,
1650 (double) layer_info[i].page.height,(double)
1651 layer_info[i].page.width,(double) layer_info[i].channels);
1652 for (j=0; j < (ssize_t) layer_info[i].channels; j++)
1654 layer_info[i].channel_info[j].type=(short) ReadBlobShort(image);
1655 layer_info[i].channel_info[j].size=(size_t) GetPSDSize(psd_info,
1657 if (image->debug != MagickFalse)
1658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1659 " channel[%.20g]: type=%.20g, size=%.20g",(double) j,
1660 (double) layer_info[i].channel_info[j].type,
1661 (double) layer_info[i].channel_info[j].size);
1663 count=ReadBlob(image,4,(unsigned char *) type);
1664 ReversePSDString(image,type,4);
1665 if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
1667 if (image->debug != MagickFalse)
1668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1669 " layer type was %.4s instead of 8BIM", type);
1670 layer_info=DestroyLayerInfo(layer_info,number_layers);
1671 ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
1674 count=ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey);
1675 ReversePSDString(image,layer_info[i].blendkey,4);
1676 layer_info[i].opacity=(Quantum) ScaleCharToQuantum((unsigned char)
1677 ReadBlobByte(image));
1678 layer_info[i].clipping=(unsigned char) ReadBlobByte(image);
1679 layer_info[i].flags=(unsigned char) ReadBlobByte(image);
1680 layer_info[i].visible=!(layer_info[i].flags & 0x02);
1681 if (image->debug != MagickFalse)
1682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1683 " blend=%.4s, opacity=%.20g, clipping=%s, flags=%d, visible=%s",
1684 layer_info[i].blendkey,(double) layer_info[i].opacity,
1685 layer_info[i].clipping ? "true" : "false",layer_info[i].flags,
1686 layer_info[i].visible ? "true" : "false");
1687 (void) ReadBlobByte(image); /* filler */
1689 size=ReadBlobLong(image);
1696 if (image->debug != MagickFalse)
1697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1698 " layer contains additional info");
1699 length=ReadBlobLong(image);
1700 combined_length=length+4;
1706 layer_info[i].mask.page.y=ReadBlobSignedLong(image);
1707 layer_info[i].mask.page.x=ReadBlobSignedLong(image);
1708 layer_info[i].mask.page.height=(size_t) (ReadBlobSignedLong(image)-
1709 layer_info[i].mask.page.y);
1710 layer_info[i].mask.page.width=(size_t) (ReadBlobSignedLong(image)-
1711 layer_info[i].mask.page.x);
1712 layer_info[i].mask.background=(unsigned char) ReadBlobByte(
1714 layer_info[i].mask.flags=(unsigned char) ReadBlobByte(image);
1715 if (!(layer_info[i].mask.flags & 0x01))
1717 layer_info[i].mask.page.y=layer_info[i].mask.page.y-
1718 layer_info[i].page.y;
1719 layer_info[i].mask.page.x=layer_info[i].mask.page.x-
1720 layer_info[i].page.x;
1722 if (image->debug != MagickFalse)
1723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1724 " layer mask: offset(%.20g,%.20g), size(%.20g,%.20g), length=%.20g",
1725 (double) layer_info[i].mask.page.x,(double)
1726 layer_info[i].mask.page.y,(double)
1727 layer_info[i].mask.page.width,(double)
1728 layer_info[i].mask.page.height,(double) ((MagickOffsetType)
1731 Skip over the rest of the layer mask information.
1733 if (DiscardBlobBytes(image,(MagickSizeType) (length-18)) == MagickFalse)
1735 layer_info=DestroyLayerInfo(layer_info,number_layers);
1736 ThrowBinaryException(CorruptImageError,
1737 "UnexpectedEndOfFile",image->filename);
1740 length=ReadBlobLong(image);
1741 combined_length+=length+4;
1745 Layer blending ranges info.
1747 if (image->debug != MagickFalse)
1748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1749 " layer blending ranges: length=%.20g",(double)
1750 ((MagickOffsetType) length));
1751 if (DiscardBlobBytes(image,length) == MagickFalse)
1753 layer_info=DestroyLayerInfo(layer_info,number_layers);
1754 ThrowBinaryException(CorruptImageError,
1755 "UnexpectedEndOfFile",image->filename);
1761 length=(MagickSizeType) (unsigned char) ReadBlobByte(image);
1762 combined_length+=length+1;
1764 (void) ReadBlob(image,(size_t) length++,layer_info[i].name);
1765 layer_info[i].name[length]='\0';
1766 if (image->debug != MagickFalse)
1767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1768 " layer name: %s",layer_info[i].name);
1769 if ((length % 4) != 0)
1771 length=4-(length % 4);
1772 combined_length+=length;
1773 /* Skip over the padding of the layer name */
1774 if (DiscardBlobBytes(image,length) == MagickFalse)
1776 layer_info=DestroyLayerInfo(layer_info,number_layers);
1777 ThrowBinaryException(CorruptImageError,
1778 "UnexpectedEndOfFile",image->filename);
1781 length=(MagickSizeType) size-combined_length;
1787 if (length > GetBlobSize(image))
1789 layer_info=DestroyLayerInfo(layer_info,number_layers);
1790 ThrowBinaryException(CorruptImageError,
1791 "InsufficientImageDataInFile",image->filename);
1793 layer_info[i].info=AcquireStringInfo((const size_t) length);
1794 info=GetStringInfoDatum(layer_info[i].info);
1795 (void) ReadBlob(image,(const size_t) length,info);
1800 for (i=0; i < number_layers; i++)
1802 if ((layer_info[i].page.width == 0) || (layer_info[i].page.height == 0))
1804 if (image->debug != MagickFalse)
1805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1806 " layer data is empty");
1807 if (layer_info[i].info != (StringInfo *) NULL)
1808 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
1813 Allocate layered image.
1815 layer_info[i].image=CloneImage(image,layer_info[i].page.width,
1816 layer_info[i].page.height,MagickFalse,exception);
1817 if (layer_info[i].image == (Image *) NULL)
1819 layer_info=DestroyLayerInfo(layer_info,number_layers);
1820 if (image->debug != MagickFalse)
1821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1822 " allocation of image for layer %.20g failed",(double) i);
1823 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1827 if (layer_info[i].info != (StringInfo *) NULL)
1829 (void) SetImageProfile(layer_info[i].image,"psd:additional-info",
1830 layer_info[i].info,exception);
1831 layer_info[i].info=DestroyStringInfo(layer_info[i].info);
1835 if (image_info->ping == MagickFalse)
1837 for (i=0; i < number_layers; i++)
1839 if (layer_info[i].image == (Image *) NULL)
1841 for (j=0; j < layer_info[i].channels; j++)
1843 if (DiscardBlobBytes(image,(MagickSizeType)
1844 layer_info[i].channel_info[j].size) == MagickFalse)
1846 layer_info=DestroyLayerInfo(layer_info,number_layers);
1847 ThrowBinaryException(CorruptImageError,
1848 "UnexpectedEndOfFile",image->filename);
1854 if (image->debug != MagickFalse)
1855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1856 " reading data for layer %.20g",(double) i);
1858 status=ReadPSDLayer(image,image_info,psd_info,&layer_info[i],
1860 if (status == MagickFalse)
1863 status=SetImageProgress(image,LoadImagesTag,i,(MagickSizeType)
1865 if (status == MagickFalse)
1870 if (status != MagickFalse)
1872 for (i=0; i < number_layers; i++)
1874 if (layer_info[i].image == (Image *) NULL)
1876 for (j=i; j < number_layers - 1; j++)
1877 layer_info[j] = layer_info[j+1];
1883 if (number_layers > 0)
1885 for (i=0; i < number_layers; i++)
1888 layer_info[i].image->previous=layer_info[i-1].image;
1889 if (i < (number_layers-1))
1890 layer_info[i].image->next=layer_info[i+1].image;
1891 layer_info[i].image->page=layer_info[i].page;
1893 image->next=layer_info[0].image;
1894 layer_info[0].image->previous=image;
1896 layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
1899 layer_info=DestroyLayerInfo(layer_info,number_layers);
1905 ModuleExport MagickBooleanType ReadPSDLayers(Image *image,
1906 const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception)
1914 domain=CoderPolicyDomain;
1915 rights=ReadPolicyRights;
1916 if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
1918 return(ReadPSDLayersInternal(image,image_info,psd_info,MagickFalse,
1922 static MagickBooleanType ReadPSDMergedImage(const ImageInfo *image_info,
1923 Image *image,const PSDInfo *psd_info,ExceptionInfo *exception)
1937 compression=(PSDCompressionType) ReadBlobMSBShort(image);
1938 image->compression=ConvertPSDCompression(compression);
1940 if (compression != Raw && compression != RLE)
1942 (void) ThrowMagickException(exception,GetMagickModule(),
1943 TypeWarning,"CompressionNotSupported","'%.20g'",(double) compression);
1944 return(MagickFalse);
1947 sizes=(MagickOffsetType *) NULL;
1948 if (compression == RLE)
1950 sizes=ReadPSDRLESizes(image,psd_info,image->rows*psd_info->channels);
1951 if (sizes == (MagickOffsetType *) NULL)
1952 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1957 for (i=0; i < (ssize_t) psd_info->channels; i++)
1959 if (compression == RLE)
1960 status=ReadPSDChannelRLE(image,psd_info,i,sizes+(i*image->rows),
1963 status=ReadPSDChannelRaw(image,psd_info->channels,i,exception);
1965 if (status != MagickFalse)
1966 status=SetImageProgress(image,LoadImagesTag,i,psd_info->channels);
1968 if (status == MagickFalse)
1972 if ((status != MagickFalse) && (image->colorspace == CMYKColorspace))
1973 status=NegateCMYK(image,exception);
1975 if (status != MagickFalse)
1976 status=CorrectPSDAlphaBlend(image_info,image,exception);
1978 sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
1983 static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
2013 assert(image_info != (const ImageInfo *) NULL);
2014 assert(image_info->signature == MagickCoreSignature);
2015 if (image_info->debug != MagickFalse)
2016 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2017 image_info->filename);
2018 assert(exception != (ExceptionInfo *) NULL);
2019 assert(exception->signature == MagickCoreSignature);
2021 image=AcquireImage(image_info,exception);
2022 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2023 if (status == MagickFalse)
2025 image=DestroyImageList(image);
2026 return((Image *) NULL);
2031 image->endian=MSBEndian;
2032 count=ReadBlob(image,4,(unsigned char *) psd_info.signature);
2033 psd_info.version=ReadBlobMSBShort(image);
2034 if ((count == 0) || (LocaleNCompare(psd_info.signature,"8BPS",4) != 0) ||
2035 ((psd_info.version != 1) && (psd_info.version != 2)))
2036 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2037 (void) ReadBlob(image,6,psd_info.reserved);
2038 psd_info.channels=ReadBlobMSBShort(image);
2039 if (psd_info.channels < 1)
2040 ThrowReaderException(CorruptImageError,"MissingImageChannel");
2041 if (psd_info.channels > MaxPSDChannels)
2042 ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded");
2043 psd_info.rows=ReadBlobMSBLong(image);
2044 psd_info.columns=ReadBlobMSBLong(image);
2045 if ((psd_info.version == 1) && ((psd_info.rows > 30000) ||
2046 (psd_info.columns > 30000)))
2047 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2048 psd_info.depth=ReadBlobMSBShort(image);
2049 if ((psd_info.depth != 1) && (psd_info.depth != 8) &&
2050 (psd_info.depth != 16) && (psd_info.depth != 32))
2051 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2052 psd_info.mode=ReadBlobMSBShort(image);
2053 if (image->debug != MagickFalse)
2054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2055 " Image is %.20g x %.20g with channels=%.20g, depth=%.20g, mode=%s",
2056 (double) psd_info.columns,(double) psd_info.rows,(double)
2057 psd_info.channels,(double) psd_info.depth,ModeToString((PSDImageType)
2059 if (EOFBlob(image) != MagickFalse)
2060 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2064 image->depth=psd_info.depth;
2065 image->columns=psd_info.columns;
2066 image->rows=psd_info.rows;
2067 status=SetImageExtent(image,image->columns,image->rows,exception);
2068 if (status == MagickFalse)
2069 return(DestroyImageList(image));
2070 if (SetImageBackgroundColor(image,exception) == MagickFalse)
2072 image=DestroyImageList(image);
2073 return((Image *) NULL);
2075 if (psd_info.mode == LabMode)
2076 SetImageColorspace(image,LabColorspace,exception);
2077 if (psd_info.mode == CMYKMode)
2079 SetImageColorspace(image,CMYKColorspace,exception);
2080 if (psd_info.channels > 4)
2081 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2083 else if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) ||
2084 (psd_info.mode == DuotoneMode))
2086 if (psd_info.depth != 32)
2088 status=AcquireImageColormap(image,psd_info.depth < 16 ? 256 : 65536,
2090 if (status == MagickFalse)
2091 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2092 if (image->debug != MagickFalse)
2093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2094 " Image colormap allocated");
2096 SetImageColorspace(image,GRAYColorspace,exception);
2097 if (psd_info.channels > 1)
2098 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2101 if (psd_info.channels > 3)
2102 SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
2104 Read PSD raster colormap only present for indexed and duotone images.
2106 length=ReadBlobMSBLong(image);
2109 if (image->debug != MagickFalse)
2110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2111 " reading colormap");
2112 if ((psd_info.mode == DuotoneMode) || (psd_info.depth == 32))
2115 Duotone image data; the format of this data is undocumented.
2116 32 bits per pixel; the colormap is ignored.
2118 (void) SeekBlob(image,(const MagickOffsetType) length,SEEK_CUR);
2126 Read PSD raster colormap.
2128 number_colors=length/3;
2129 if (number_colors > 65536)
2130 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2131 if (AcquireImageColormap(image,number_colors,exception) == MagickFalse)
2132 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2133 for (i=0; i < (ssize_t) image->colors; i++)
2134 image->colormap[i].red=ScaleCharToQuantum((unsigned char)
2135 ReadBlobByte(image));
2136 for (i=0; i < (ssize_t) image->colors; i++)
2137 image->colormap[i].green=ScaleCharToQuantum((unsigned char)
2138 ReadBlobByte(image));
2139 for (i=0; i < (ssize_t) image->colors; i++)
2140 image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
2141 ReadBlobByte(image));
2142 image->alpha_trait=UndefinedPixelTrait;
2145 if ((image->depth == 1) && (image->storage_class != PseudoClass))
2146 ThrowReaderException(CorruptImageError, "ImproperImageHeader");
2147 has_merged_image=MagickTrue;
2148 length=ReadBlobMSBLong(image);
2155 Image resources block.
2157 if (image->debug != MagickFalse)
2158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2159 " reading image resource blocks - %.20g bytes",(double)
2160 ((MagickOffsetType) length));
2161 if (length > GetBlobSize(image))
2162 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
2163 blocks=(unsigned char *) AcquireQuantumMemory((size_t) length,
2165 if (blocks == (unsigned char *) NULL)
2166 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2167 count=ReadBlob(image,(size_t) length,blocks);
2168 if ((count != (ssize_t) length) || (length < 4) ||
2169 (LocaleNCompare((char *) blocks,"8BIM",4) != 0))
2171 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
2172 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2174 ParseImageResourceBlocks(image,blocks,(size_t) length,&has_merged_image,
2176 blocks=(unsigned char *) RelinquishMagickMemory(blocks);
2179 Layer and mask block.
2181 length=GetPSDSize(&psd_info,image);
2184 length=ReadBlobMSBLong(image);
2185 length=ReadBlobMSBLong(image);
2187 offset=TellBlob(image);
2188 skip_layers=MagickFalse;
2189 if ((image_info->number_scenes == 1) && (image_info->scene == 0) &&
2190 (has_merged_image != MagickFalse))
2192 if (image->debug != MagickFalse)
2193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2194 " read composite only");
2195 skip_layers=MagickTrue;
2199 if (image->debug != MagickFalse)
2200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2201 " image has no layers");
2205 if (ReadPSDLayersInternal(image,image_info,&psd_info,skip_layers,
2206 exception) != MagickTrue)
2208 (void) CloseBlob(image);
2209 image=DestroyImageList(image);
2210 return((Image *) NULL);
2214 Skip the rest of the layer and mask information.
2216 SeekBlob(image,offset+length,SEEK_SET);
2219 If we are only "pinging" the image, then we're done - so return.
2221 if (EOFBlob(image) != MagickFalse)
2222 ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
2223 if (image_info->ping != MagickFalse)
2225 (void) CloseBlob(image);
2226 return(GetFirstImageInList(image));
2229 Read the precombined layer, present for PSD < 4 compatibility.
2231 if (image->debug != MagickFalse)
2232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2233 " reading the precombined layer");
2234 if ((has_merged_image != MagickFalse) || (GetImageListLength(image) == 1))
2235 has_merged_image=(MagickBooleanType) ReadPSDMergedImage(image_info,image,
2236 &psd_info,exception);
2237 if ((has_merged_image == MagickFalse) && (GetImageListLength(image) == 1) &&
2240 SeekBlob(image,offset,SEEK_SET);
2241 status=ReadPSDLayersInternal(image,image_info,&psd_info,MagickFalse,
2243 if (status != MagickTrue)
2245 (void) CloseBlob(image);
2246 image=DestroyImageList(image);
2247 return((Image *) NULL);
2250 if (has_merged_image == MagickFalse)
2255 if (GetImageListLength(image) == 1)
2256 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
2257 SetImageAlphaChannel(image,TransparentAlphaChannel,exception);
2258 image->background_color.alpha=TransparentAlpha;
2259 image->background_color.alpha_trait=BlendPixelTrait;
2260 merged=MergeImageLayers(image,FlattenLayer,exception);
2261 ReplaceImageInList(&image,merged);
2263 (void) CloseBlob(image);
2264 return(GetFirstImageInList(image));
2268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2272 % R e g i s t e r P S D I m a g e %
2276 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2278 % RegisterPSDImage() adds properties for the PSD image format to
2279 % the list of supported formats. The properties include the image format
2280 % tag, a method to read and/or write the format, whether the format
2281 % supports the saving of more than one frame to the same file or blob,
2282 % whether the format supports native in-memory I/O, and a brief
2283 % description of the format.
2285 % The format of the RegisterPSDImage method is:
2287 % size_t RegisterPSDImage(void)
2290 ModuleExport size_t RegisterPSDImage(void)
2295 entry=AcquireMagickInfo("PSD","PSB","Adobe Large Document Format");
2296 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2297 entry->encoder=(EncodeImageHandler *) WritePSDImage;
2298 entry->magick=(IsImageFormatHandler *) IsPSD;
2299 entry->flags|=CoderDecoderSeekableStreamFlag;
2300 entry->flags|=CoderEncoderSeekableStreamFlag;
2301 (void) RegisterMagickInfo(entry);
2302 entry=AcquireMagickInfo("PSD","PSD","Adobe Photoshop bitmap");
2303 entry->decoder=(DecodeImageHandler *) ReadPSDImage;
2304 entry->encoder=(EncodeImageHandler *) WritePSDImage;
2305 entry->magick=(IsImageFormatHandler *) IsPSD;
2306 entry->flags|=CoderDecoderSeekableStreamFlag;
2307 entry->flags|=CoderEncoderSeekableStreamFlag;
2308 (void) RegisterMagickInfo(entry);
2309 return(MagickImageCoderSignature);
2313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2317 % U n r e g i s t e r P S D I m a g e %
2321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2323 % UnregisterPSDImage() removes format registrations made by the
2324 % PSD module from the list of supported formats.
2326 % The format of the UnregisterPSDImage method is:
2328 % UnregisterPSDImage(void)
2331 ModuleExport void UnregisterPSDImage(void)
2333 (void) UnregisterMagickInfo("PSB");
2334 (void) UnregisterMagickInfo("PSD");
2338 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2342 % W r i t e P S D I m a g e %
2346 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2348 % WritePSDImage() writes an image in the Adobe Photoshop encoded image format.
2350 % The format of the WritePSDImage method is:
2352 % MagickBooleanType WritePSDImage(const ImageInfo *image_info,Image *image,
2353 % ExceptionInfo *exception)
2355 % A description of each parameter follows.
2357 % o image_info: the image info.
2359 % o image: The image.
2361 % o exception: return any errors or warnings in this structure.
2365 static inline ssize_t SetPSDOffset(const PSDInfo *psd_info,Image *image,
2366 const size_t offset)
2368 if (psd_info->version == 1)
2369 return(WriteBlobMSBShort(image,(unsigned short) offset));
2370 return(WriteBlobMSBLong(image,(unsigned int) offset));
2373 static inline ssize_t WritePSDOffset(const PSDInfo *psd_info,Image *image,
2374 const MagickSizeType size,const MagickSizeType offset)
2382 current_offset=TellBlob(image);
2383 SeekBlob(image,offset,SEEK_SET);
2384 if (psd_info->version == 1)
2385 result=WriteBlobMSBShort(image,(unsigned short) size);
2387 result=WriteBlobMSBLong(image,(unsigned int) size);
2388 SeekBlob(image,current_offset,SEEK_SET);
2392 static inline ssize_t SetPSDSize(const PSDInfo *psd_info,Image *image,
2393 const MagickSizeType size)
2395 if (psd_info->version == 1)
2396 return(WriteBlobLong(image,(unsigned int) size));
2397 return(WriteBlobLongLong(image,size));
2400 static inline ssize_t WritePSDSize(const PSDInfo *psd_info,Image *image,
2401 const MagickSizeType size,const MagickSizeType offset)
2409 current_offset=TellBlob(image);
2410 SeekBlob(image,offset,SEEK_SET);
2411 result=SetPSDSize(psd_info, image, size);
2412 SeekBlob(image,current_offset,SEEK_SET);
2416 static size_t PSDPackbitsEncodeImage(Image *image,const size_t length,
2417 const unsigned char *pixels,unsigned char *compact_pixels,
2418 ExceptionInfo *exception)
2427 register unsigned char
2434 Compress pixels with Packbits encoding.
2436 assert(image != (Image *) NULL);
2437 assert(image->signature == MagickCoreSignature);
2438 if (image->debug != MagickFalse)
2439 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2440 assert(pixels != (unsigned char *) NULL);
2441 assert(compact_pixels != (unsigned char *) NULL);
2442 packbits=(unsigned char *) AcquireQuantumMemory(128UL,sizeof(*packbits));
2443 if (packbits == (unsigned char *) NULL)
2444 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
2447 for (i=(ssize_t) length; i != 0; )
2454 *q++=(unsigned char) 0;
2461 *q++=(unsigned char) 1;
2469 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2471 *q++=(unsigned char) ((256-3)+1);
2475 *q++=(unsigned char) 2;
2483 if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
2489 while (((ssize_t) count < i) && (*pixels == *(pixels+count)))
2496 *q++=(unsigned char) ((256-count)+1);
2505 while ((*(pixels+count) != *(pixels+count+1)) ||
2506 (*(pixels+count+1) != *(pixels+count+2)))
2508 packbits[count+1]=pixels[count];
2510 if (((ssize_t) count >= (i-3)) || (count >= 127))
2514 *packbits=(unsigned char) (count-1);
2515 for (j=0; j <= (ssize_t) count; j++)
2522 *q++=(unsigned char) 128; /* EOD marker */
2523 packbits=(unsigned char *) RelinquishMagickMemory(packbits);
2524 return((size_t) (q-compact_pixels));
2527 static size_t WriteCompressionStart(const PSDInfo *psd_info,Image *image,
2528 const Image *next_image,const CompressionType compression,
2529 const ssize_t channels)
2538 if (compression == RLECompression)
2540 length=WriteBlobShort(image,RLE);
2541 for (i=0; i < channels; i++)
2542 for (y=0; y < (ssize_t) next_image->rows; y++)
2543 length+=SetPSDOffset(psd_info,image,0);
2545 #ifdef MAGICKCORE_ZLIB_DELEGATE
2546 else if (compression == ZipCompression)
2547 length=WriteBlobShort(image,ZipWithoutPrediction);
2550 length=WriteBlobShort(image,Raw);
2554 static size_t WritePSDChannel(const PSDInfo *psd_info,
2555 const ImageInfo *image_info,Image *image,Image *next_image,
2556 const QuantumType quantum_type, unsigned char *compact_pixels,
2557 MagickOffsetType size_offset,const MagickBooleanType separate,
2558 const CompressionType compression,ExceptionInfo *exception)
2569 register const Quantum
2582 #ifdef MAGICKCORE_ZLIB_DELEGATE
2596 compressed_pixels=(unsigned char *) NULL;
2600 if (separate != MagickFalse)
2602 size_offset=TellBlob(image)+2;
2603 count+=WriteCompressionStart(psd_info,image,next_image,compression,1);
2605 if (next_image->depth > 8)
2606 next_image->depth=16;
2607 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
2608 MagickTrue : MagickFalse;
2609 quantum_info=AcquireQuantumInfo(image_info,next_image);
2610 if (quantum_info == (QuantumInfo *) NULL)
2612 pixels=(unsigned char *) GetQuantumPixels(quantum_info);
2613 #ifdef MAGICKCORE_ZLIB_DELEGATE
2614 if (compression == ZipCompression)
2616 compressed_pixels=(unsigned char *) AcquireQuantumMemory(CHUNK,
2617 sizeof(*compressed_pixels));
2618 if (compressed_pixels == (unsigned char *) NULL)
2620 quantum_info=DestroyQuantumInfo(quantum_info);
2623 ResetMagickMemory(&stream,0,sizeof(stream));
2624 stream.data_type=Z_BINARY;
2625 level=Z_DEFAULT_COMPRESSION;
2626 if ((image_info->quality > 0 && image_info->quality < 10))
2627 level=(int) image_info->quality;
2628 if (deflateInit(&stream,level) != Z_OK)
2630 quantum_info=DestroyQuantumInfo(quantum_info);
2635 for (y=0; y < (ssize_t) next_image->rows; y++)
2637 p=GetVirtualPixels(next_image,0,y,next_image->columns,1,exception);
2638 if (p == (const Quantum *) NULL)
2640 length=ExportQuantumPixels(next_image,(CacheView *) NULL,quantum_info,
2641 quantum_type,pixels,exception);
2642 if (monochrome != MagickFalse)
2643 for (i=0; i < (ssize_t) length; i++)
2644 pixels[i]=(~pixels[i]);
2645 if (compression == RLECompression)
2647 length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels,
2649 count+=WriteBlob(image,length,compact_pixels);
2650 size_offset+=WritePSDOffset(psd_info,image,length,size_offset);
2652 #ifdef MAGICKCORE_ZLIB_DELEGATE
2653 else if (compression == ZipCompression)
2655 stream.avail_in=(uInt) length;
2656 stream.next_in=(Bytef *) pixels;
2657 if (y == (ssize_t) next_image->rows-1)
2660 stream.avail_out=(uInt) CHUNK;
2661 stream.next_out=(Bytef *) compressed_pixels;
2662 if (deflate(&stream,flush) == Z_STREAM_ERROR)
2664 length=(size_t) CHUNK-stream.avail_out;
2666 count+=WriteBlob(image,length,compressed_pixels);
2667 } while (stream.avail_out == 0);
2671 count+=WriteBlob(image,length,pixels);
2673 #ifdef MAGICKCORE_ZLIB_DELEGATE
2674 if (compression == ZipCompression)
2676 (void) deflateEnd(&stream);
2677 compressed_pixels=(unsigned char *) RelinquishMagickMemory(
2681 quantum_info=DestroyQuantumInfo(quantum_info);
2685 static unsigned char *AcquireCompactPixels(const Image *image,
2686 ExceptionInfo *exception)
2694 packet_size=image->depth > 8UL ? 2UL : 1UL;
2695 compact_pixels=(unsigned char *) AcquireQuantumMemory((9*
2696 image->columns)+1,packet_size*sizeof(*compact_pixels));
2697 if (compact_pixels == (unsigned char *) NULL)
2699 (void) ThrowMagickException(exception,GetMagickModule(),
2700 ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
2702 return(compact_pixels);
2705 static size_t WritePSDChannels(const PSDInfo *psd_info,
2706 const ImageInfo *image_info,Image *image,Image *next_image,
2707 MagickOffsetType size_offset,const MagickBooleanType separate,
2708 ExceptionInfo *exception)
2731 compact_pixels=(unsigned char *) NULL;
2732 compression=next_image->compression;
2733 if (image_info->compression != UndefinedCompression)
2734 compression=image_info->compression;
2735 if (compression == RLECompression)
2737 compact_pixels=AcquireCompactPixels(next_image,exception);
2738 if (compact_pixels == (unsigned char *) NULL)
2742 if (separate == MagickFalse)
2744 if (next_image->storage_class != PseudoClass)
2746 if (IsImageGray(next_image) == MagickFalse)
2747 channels=next_image->colorspace == CMYKColorspace ? 4 : 3;
2748 if (next_image->alpha_trait != UndefinedPixelTrait)
2751 rows_offset=TellBlob(image)+2;
2752 count+=WriteCompressionStart(psd_info,image,next_image,compression,
2754 offset_length=(next_image->rows*(psd_info->version == 1 ? 2 : 4));
2757 if (next_image->storage_class == PseudoClass)
2759 length=WritePSDChannel(psd_info,image_info,image,next_image,
2760 IndexQuantum,compact_pixels,rows_offset,separate,compression,
2762 if (separate != MagickFalse)
2763 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2765 rows_offset+=offset_length;
2770 if (IsImageGray(next_image) != MagickFalse)
2772 length=WritePSDChannel(psd_info,image_info,image,next_image,
2773 GrayQuantum,compact_pixels,rows_offset,separate,compression,
2775 if (separate != MagickFalse)
2776 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2778 rows_offset+=offset_length;
2783 if (next_image->colorspace == CMYKColorspace)
2784 (void) NegateCMYK(next_image,exception);
2786 length=WritePSDChannel(psd_info,image_info,image,next_image,
2787 RedQuantum,compact_pixels,rows_offset,separate,compression,
2789 if (separate != MagickFalse)
2790 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2792 rows_offset+=offset_length;
2795 length=WritePSDChannel(psd_info,image_info,image,next_image,
2796 GreenQuantum,compact_pixels,rows_offset,separate,compression,
2798 if (separate != MagickFalse)
2799 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2801 rows_offset+=offset_length;
2804 length=WritePSDChannel(psd_info,image_info,image,next_image,
2805 BlueQuantum,compact_pixels,rows_offset,separate,compression,
2807 if (separate != MagickFalse)
2808 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2810 rows_offset+=offset_length;
2813 if (next_image->colorspace == CMYKColorspace)
2815 length=WritePSDChannel(psd_info,image_info,image,next_image,
2816 BlackQuantum,compact_pixels,rows_offset,separate,compression,
2818 if (separate != MagickFalse)
2819 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2821 rows_offset+=offset_length;
2825 if (next_image->alpha_trait != UndefinedPixelTrait)
2827 length=WritePSDChannel(psd_info,image_info,image,next_image,
2828 AlphaQuantum,compact_pixels,rows_offset,separate,compression,
2830 if (separate != MagickFalse)
2831 size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
2833 rows_offset+=offset_length;
2837 compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
2838 if (next_image->colorspace == CMYKColorspace)
2839 (void) NegateCMYK(next_image,exception);
2840 if (separate != MagickFalse)
2845 property=GetImageArtifact(next_image,"psd:opacity-mask");
2846 if (property != (const char *) NULL)
2848 mask=(Image *) GetImageRegistry(ImageRegistryType,property,
2850 if (mask != (Image *) NULL)
2852 if (compression == RLECompression)
2854 compact_pixels=AcquireCompactPixels(mask,exception);
2855 if (compact_pixels == (unsigned char *) NULL)
2858 length=WritePSDChannel(psd_info,image_info,image,mask,
2859 RedQuantum,compact_pixels,rows_offset,MagickTrue,compression,
2861 (void) WritePSDSize(psd_info,image,length,size_offset);
2863 compact_pixels=(unsigned char *) RelinquishMagickMemory(
2871 static size_t WritePascalString(Image *image,const char *value,size_t padding)
2884 length=(strlen(value) > 255UL ) ? 255UL : strlen(value);
2886 count+=WriteBlobByte(image,0);
2889 count+=WriteBlobByte(image,(unsigned char) length);
2890 count+=WriteBlob(image,length,(const unsigned char *) value);
2893 if ((length % padding) == 0)
2895 for (i=0; i < (ssize_t) (padding-(length % padding)); i++)
2896 count+=WriteBlobByte(image,0);
2900 static void WriteResolutionResourceBlock(Image *image)
2909 if (image->units == PixelsPerCentimeterResolution)
2911 x_resolution=2.54*65536.0*image->resolution.x+0.5;
2912 y_resolution=2.54*65536.0*image->resolution.y+0.5;
2917 x_resolution=65536.0*image->resolution.x+0.5;
2918 y_resolution=65536.0*image->resolution.y+0.5;
2921 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
2922 (void) WriteBlobMSBShort(image,0x03ED);
2923 (void) WriteBlobMSBShort(image,0);
2924 (void) WriteBlobMSBLong(image,16); /* resource size */
2925 (void) WriteBlobMSBLong(image,(unsigned int) (x_resolution+0.5));
2926 (void) WriteBlobMSBShort(image,units); /* horizontal resolution unit */
2927 (void) WriteBlobMSBShort(image,units); /* width unit */
2928 (void) WriteBlobMSBLong(image,(unsigned int) (y_resolution+0.5));
2929 (void) WriteBlobMSBShort(image,units); /* vertical resolution unit */
2930 (void) WriteBlobMSBShort(image,units); /* height unit */
2933 static inline size_t WriteChannelSize(const PSDInfo *psd_info,Image *image,
2934 const signed short channel)
2939 count=(size_t) WriteBlobShort(image,channel);
2940 count+=SetPSDSize(psd_info,image,0);
2944 static void RemoveICCProfileFromResourceBlock(StringInfo *bim_profile)
2946 register const unsigned char
2963 length=GetStringInfoLength(bim_profile);
2966 datum=GetStringInfoDatum(bim_profile);
2967 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
2969 register unsigned char
2972 q=(unsigned char *) p;
2973 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
2975 p=PushLongPixel(MSBEndian,p,&long_sans);
2976 p=PushShortPixel(MSBEndian,p,&id);
2977 p=PushShortPixel(MSBEndian,p,&short_sans);
2978 p=PushLongPixel(MSBEndian,p,&count);
2979 if (id == 0x0000040f)
2984 quantum=PSDQuantum(count)+12;
2985 if ((quantum >= 12) && (quantum < (ssize_t) length))
2987 if ((q+quantum < (datum+length-16)))
2988 (void) CopyMagickMemory(q,q+quantum,length-quantum-(q-datum));
2989 SetStringInfoLength(bim_profile,length-quantum);
2994 if ((count & 0x01) != 0)
2999 static void RemoveResolutionFromResourceBlock(StringInfo *bim_profile)
3001 register const unsigned char
3018 length=GetStringInfoLength(bim_profile);
3021 datum=GetStringInfoDatum(bim_profile);
3022 for (p=datum; (p >= datum) && (p < (datum+length-16)); )
3024 register unsigned char
3030 q=(unsigned char *) p;
3031 if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
3033 p=PushLongPixel(MSBEndian,p,&long_sans);
3034 p=PushShortPixel(MSBEndian,p,&id);
3035 p=PushShortPixel(MSBEndian,p,&short_sans);
3036 p=PushLongPixel(MSBEndian,p,&count);
3037 cnt=PSDQuantum(count);
3040 if ((id == 0x000003ed) && (cnt < (ssize_t) (length-12)))
3042 (void) CopyMagickMemory(q,q+cnt+12,length-(cnt+12)-(q-datum));
3043 SetStringInfoLength(bim_profile,length-(cnt+12));
3047 if ((count & 0x01) != 0)
3052 static const StringInfo *GetAdditionalInformation(const ImageInfo *image_info,
3053 Image *image,ExceptionInfo *exception)
3055 #define PSDKeySize 5
3056 #define PSDAllowedLength 36
3061 /* Whitelist of keys from: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ */
3063 allowed[PSDAllowedLength][PSDKeySize] = {
3064 "blnc", "blwh", "brit", "brst", "clbl", "clrL", "curv", "expA", "FMsk",
3065 "GdFl", "grdm", "hue ", "hue2", "infx", "knko", "lclr", "levl", "lnsr",
3066 "lfx2", "luni", "lrFX", "lspf", "lyid", "lyvr", "mixr", "nvrt", "phfl",
3067 "post", "PtFl", "selc", "shpa", "sn2P", "SoCo", "thrs", "tsly", "vibA"
3093 info=GetImageProfile(image,"psd:additional-info");
3094 if (info == (const StringInfo *) NULL)
3095 return((const StringInfo *) NULL);
3096 option=GetImageOption(image_info,"psd:additional-info");
3097 if (LocaleCompare(option,"all") == 0)
3099 if (LocaleCompare(option,"selective") != 0)
3101 profile=RemoveImageProfile(image,"psd:additional-info");
3102 return(DestroyStringInfo(profile));
3104 length=GetStringInfoLength(info);
3105 p=GetStringInfoDatum(info);
3106 remaining_length=length;
3108 while (remaining_length >= 12)
3110 /* skip over signature */
3117 size=(unsigned int) (*p++) << 24;
3118 size|=(unsigned int) (*p++) << 16;
3119 size|=(unsigned int) (*p++) << 8;
3120 size|=(unsigned int) (*p++);
3121 size=size & 0xffffffff;
3122 remaining_length-=12;
3123 if ((size_t) size > remaining_length)
3124 return((const StringInfo *) NULL);
3126 for (i=0; i < PSDAllowedLength; i++)
3128 if (LocaleNCompare(key,allowed[i],PSDKeySize) != 0)
3134 remaining_length-=(size_t) size;
3135 if (found == MagickFalse)
3137 if (remaining_length > 0)
3138 p=(unsigned char *) CopyMagickMemory(p-12,p+size,remaining_length);
3141 length+=(size_t) size+12;
3144 profile=RemoveImageProfile(image,"psd:additional-info");
3146 return(DestroyStringInfo(profile));
3147 SetStringInfoLength(profile,(const size_t) length);
3148 SetImageProfile(image,"psd:additional-info",info,exception);
3152 static MagickBooleanType WritePSDLayersInternal(Image *image,
3153 const ImageInfo *image_info,const PSDInfo *psd_info,size_t *layers_size,
3154 ExceptionInfo *exception)
3157 layer_name[MagickPathExtent];
3173 *layer_size_offsets,
3188 base_image=GetNextImageInList(image);
3189 if (base_image == (Image *) NULL)
3192 size_offset=TellBlob(image);
3193 SetPSDSize(psd_info,image,0);
3195 for (next_image=base_image; next_image != NULL; )
3198 next_image=GetNextImageInList(next_image);
3200 if (image->alpha_trait != UndefinedPixelTrait)
3201 size+=WriteBlobShort(image,-(unsigned short) layer_count);
3203 size+=WriteBlobShort(image,(unsigned short) layer_count);
3204 layer_size_offsets=(MagickOffsetType *) AcquireQuantumMemory(
3205 (size_t) layer_count,sizeof(MagickOffsetType));
3206 if (layer_size_offsets == (MagickOffsetType *) NULL)
3207 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
3209 for (next_image=base_image; next_image != NULL; )
3221 mask=(Image *) NULL;
3222 property=GetImageArtifact(next_image,"psd:opacity-mask");
3224 if (property != (const char *) NULL)
3226 mask=(Image *) GetImageRegistry(ImageRegistryType,property,exception);
3227 default_color=strlen(property) == 9 ? 255 : 0;
3229 size+=WriteBlobSignedLong(image,(signed int) next_image->page.y);
3230 size+=WriteBlobSignedLong(image,(signed int) next_image->page.x);
3231 size+=WriteBlobSignedLong(image,(signed int) (next_image->page.y+
3233 size+=WriteBlobSignedLong(image,(signed int) (next_image->page.x+
3234 next_image->columns));
3236 if ((next_image->storage_class != PseudoClass) &&
3237 (IsImageGray(next_image) == MagickFalse))
3238 channels=next_image->colorspace == CMYKColorspace ? 4U : 3U;
3239 total_channels=channels;
3240 if (next_image->alpha_trait != UndefinedPixelTrait)
3242 if (mask != (Image *) NULL)
3244 size+=WriteBlobShort(image,total_channels);
3245 layer_size_offsets[layer_index++]=TellBlob(image);
3246 for (i=0; i < (ssize_t) channels; i++)
3247 size+=WriteChannelSize(psd_info,image,(signed short) i);
3248 if (next_image->alpha_trait != UndefinedPixelTrait)
3249 size+=WriteChannelSize(psd_info,image,-1);
3250 if (mask != (Image *) NULL)
3251 size+=WriteChannelSize(psd_info,image,-2);
3252 size+=WriteBlobString(image,image->endian == LSBEndian ? "MIB8" :"8BIM");
3253 size+=WriteBlobString(image,CompositeOperatorToPSDBlendMode(image));
3254 property=GetImageArtifact(next_image,"psd:layer.opacity");
3255 if (property != (const char *) NULL)
3260 opacity=(Quantum) StringToInteger(property);
3261 size+=WriteBlobByte(image,ScaleQuantumToChar(opacity));
3262 (void) ApplyPSDLayerOpacity(next_image,opacity,MagickTrue,exception);
3265 size+=WriteBlobByte(image,255);
3266 size+=WriteBlobByte(image,0);
3267 size+=WriteBlobByte(image,next_image->compose==NoCompositeOp ?
3268 1 << 0x02 : 1); /* layer properties - visible, etc. */
3269 size+=WriteBlobByte(image,0);
3270 info=GetAdditionalInformation(image_info,next_image,exception);
3271 property=(const char *) GetImageProperty(next_image,"label",exception);
3272 if (property == (const char *) NULL)
3274 (void) FormatLocaleString(layer_name,MagickPathExtent,"L%.20g",
3275 (double) layer_index);
3276 property=layer_name;
3278 name_length=strlen(property)+1;
3279 if ((name_length % 4) != 0)
3280 name_length+=(4-(name_length % 4));
3281 if (info != (const StringInfo *) NULL)
3282 name_length+=GetStringInfoLength(info);
3284 if (mask != (Image *) NULL)
3286 size+=WriteBlobLong(image,(unsigned int) name_length);
3287 if (mask == (Image *) NULL)
3288 size+=WriteBlobLong(image,0);
3291 if (mask->compose != NoCompositeOp)
3292 (void) ApplyPSDOpacityMask(next_image,mask,ScaleCharToQuantum(
3293 default_color),MagickTrue,exception);
3294 mask->page.y+=image->page.y;
3295 mask->page.x+=image->page.x;
3296 size+=WriteBlobLong(image,20);
3297 size+=WriteBlobSignedLong(image,mask->page.y);
3298 size+=WriteBlobSignedLong(image,mask->page.x);
3299 size+=WriteBlobSignedLong(image,(const signed int) mask->rows+
3301 size+=WriteBlobSignedLong(image,(const signed int) mask->columns+
3303 size+=WriteBlobByte(image,default_color);
3304 size+=WriteBlobByte(image,mask->compose == NoCompositeOp ? 2 : 0);
3305 size+=WriteBlobMSBShort(image,0);
3307 size+=WriteBlobLong(image,0);
3308 size+=WritePascalString(image,property,4);
3309 if (info != (const StringInfo *) NULL)
3310 size+=WriteBlob(image,GetStringInfoLength(info),
3311 GetStringInfoDatum(info));
3312 next_image=GetNextImageInList(next_image);
3317 next_image=base_image;
3319 while (next_image != NULL)
3321 length=WritePSDChannels(psd_info,image_info,image,next_image,
3322 layer_size_offsets[layer_index++],MagickTrue,exception);
3329 next_image=GetNextImageInList(next_image);
3332 Write the total size
3334 if (layers_size != (size_t*) NULL)
3336 if ((size/2) != ((size+1)/2))
3337 rounded_size=size+1;
3340 (void) WritePSDSize(psd_info,image,rounded_size,size_offset);
3341 layer_size_offsets=(MagickOffsetType *) RelinquishMagickMemory(
3342 layer_size_offsets);
3344 Remove the opacity mask from the registry
3346 next_image=base_image;
3347 while (next_image != (Image *) NULL)
3349 property=GetImageArtifact(next_image,"psd:opacity-mask");
3350 if (property != (const char *) NULL)
3351 DeleteImageRegistry(property);
3352 next_image=GetNextImageInList(next_image);
3358 ModuleExport MagickBooleanType WritePSDLayers(Image * image,
3359 const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception)
3367 domain=CoderPolicyDomain;
3368 rights=WritePolicyRights;
3369 if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
3371 return WritePSDLayersInternal(image,image_info,psd_info,(size_t*) NULL,
3375 static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
3376 Image *image,ExceptionInfo *exception)
3401 assert(image_info != (const ImageInfo *) NULL);
3402 assert(image_info->signature == MagickCoreSignature);
3403 assert(image != (Image *) NULL);
3404 assert(image->signature == MagickCoreSignature);
3405 if (image->debug != MagickFalse)
3406 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3407 assert(exception != (ExceptionInfo *) NULL);
3408 assert(exception->signature == MagickCoreSignature);
3409 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
3410 if (status == MagickFalse)
3412 packet_size=(size_t) (image->depth > 8 ? 6 : 3);
3413 if (image->alpha_trait != UndefinedPixelTrait)
3414 packet_size+=image->depth > 8 ? 2 : 1;
3416 if ((LocaleCompare(image_info->magick,"PSB") == 0) ||
3417 (image->columns > 30000) || (image->rows > 30000))
3419 (void) WriteBlob(image,4,(const unsigned char *) "8BPS");
3420 (void) WriteBlobMSBShort(image,psd_info.version); /* version */
3421 for (i=1; i <= 6; i++)
3422 (void) WriteBlobByte(image, 0); /* 6 bytes of reserved */
3423 /* When the image has a color profile it won't be converted to gray scale */
3424 if ((GetImageProfile(image,"icc") == (StringInfo *) NULL) &&
3425 (SetImageGray(image,exception) != MagickFalse))
3426 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
3428 if ((image_info->type != TrueColorType) && (image_info->type !=
3429 TrueColorAlphaType) && (image->storage_class == PseudoClass))
3430 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
3433 if (image->storage_class == PseudoClass)
3434 (void) SetImageStorageClass(image,DirectClass,exception);
3435 if (image->colorspace != CMYKColorspace)
3436 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 4UL : 3UL);
3438 num_channels=(image->alpha_trait != UndefinedPixelTrait ? 5UL : 4UL);
3440 (void) WriteBlobMSBShort(image,(unsigned short) num_channels);
3441 (void) WriteBlobMSBLong(image,(unsigned int) image->rows);
3442 (void) WriteBlobMSBLong(image,(unsigned int) image->columns);
3443 if (IsImageGray(image) != MagickFalse)
3451 monochrome=IsImageMonochrome(image) && (image->depth == 1) ?
3452 MagickTrue : MagickFalse;
3453 (void) WriteBlobMSBShort(image,(unsigned short)
3454 (monochrome != MagickFalse ? 1 : image->depth > 8 ? 16 : 8));
3455 (void) WriteBlobMSBShort(image,(unsigned short)
3456 (monochrome != MagickFalse ? BitmapMode : GrayscaleMode));
3460 (void) WriteBlobMSBShort(image,(unsigned short) (image->storage_class ==
3461 PseudoClass ? 8 : image->depth > 8 ? 16 : 8));
3463 if (((image_info->colorspace != UndefinedColorspace) ||
3464 (image->colorspace != CMYKColorspace)) &&
3465 (image_info->colorspace != CMYKColorspace))
3467 (void) TransformImageColorspace(image,sRGBColorspace,exception);
3468 (void) WriteBlobMSBShort(image,(unsigned short)
3469 (image->storage_class == PseudoClass ? IndexedMode : RGBMode));
3473 if (image->colorspace != CMYKColorspace)
3474 (void) TransformImageColorspace(image,CMYKColorspace,exception);
3475 (void) WriteBlobMSBShort(image,CMYKMode);
3478 if ((IsImageGray(image) != MagickFalse) ||
3479 (image->storage_class == DirectClass) || (image->colors > 256))
3480 (void) WriteBlobMSBLong(image,0);
3484 Write PSD raster colormap.
3486 (void) WriteBlobMSBLong(image,768);
3487 for (i=0; i < (ssize_t) image->colors; i++)
3488 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].red));
3489 for ( ; i < 256; i++)
3490 (void) WriteBlobByte(image,0);
3491 for (i=0; i < (ssize_t) image->colors; i++)
3492 (void) WriteBlobByte(image,ScaleQuantumToChar(
3493 image->colormap[i].green));
3494 for ( ; i < 256; i++)
3495 (void) WriteBlobByte(image,0);
3496 for (i=0; i < (ssize_t) image->colors; i++)
3497 (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].blue));
3498 for ( ; i < 256; i++)
3499 (void) WriteBlobByte(image,0);
3502 Image resource block.
3504 length=28; /* 0x03EB */
3505 bim_profile=(StringInfo *) GetImageProfile(image,"8bim");
3506 icc_profile=GetImageProfile(image,"icc");
3507 if (bim_profile != (StringInfo *) NULL)
3509 bim_profile=CloneStringInfo(bim_profile);
3510 if (icc_profile != (StringInfo *) NULL)
3511 RemoveICCProfileFromResourceBlock(bim_profile);
3512 RemoveResolutionFromResourceBlock(bim_profile);
3513 length+=PSDQuantum(GetStringInfoLength(bim_profile));
3515 if (icc_profile != (const StringInfo *) NULL)
3516 length+=PSDQuantum(GetStringInfoLength(icc_profile))+12;
3517 (void) WriteBlobMSBLong(image,(unsigned int) length);
3518 WriteResolutionResourceBlock(image);
3519 if (bim_profile != (StringInfo *) NULL)
3521 (void) WriteBlob(image,GetStringInfoLength(bim_profile),
3522 GetStringInfoDatum(bim_profile));
3523 bim_profile=DestroyStringInfo(bim_profile);
3525 if (icc_profile != (StringInfo *) NULL)
3527 (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
3528 (void) WriteBlobMSBShort(image,0x0000040F);
3529 (void) WriteBlobMSBShort(image,0);
3530 (void) WriteBlobMSBLong(image,(unsigned int) GetStringInfoLength(
3532 (void) WriteBlob(image,GetStringInfoLength(icc_profile),
3533 GetStringInfoDatum(icc_profile));
3534 if ((MagickOffsetType) GetStringInfoLength(icc_profile) !=
3535 PSDQuantum(GetStringInfoLength(icc_profile)))
3536 (void) WriteBlobByte(image,0);
3538 if (status != MagickFalse)
3546 size_offset=TellBlob(image);
3547 SetPSDSize(&psd_info,image,0);
3548 status=WritePSDLayersInternal(image,image_info,&psd_info,&size,
3550 size_offset+=WritePSDSize(&psd_info,image,size+
3551 (psd_info.version == 1 ? 8 : 12),size_offset);
3553 (void) WriteBlobMSBLong(image,0); /* user mask data */
3555 Write composite image.
3557 if (status != MagickFalse)
3562 compression=image->compression;
3563 if (image->compression == ZipCompression)
3564 image->compression=RLECompression;
3565 if (image_info->compression != UndefinedCompression)
3566 image->compression=image_info->compression;
3567 if (WritePSDChannels(&psd_info,image_info,image,image,0,MagickFalse,
3570 image->compression=compression;
3572 (void) CloseBlob(image);