Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Alexander_Naumov:SLE-12:Update
ImageMagick.11106
ImageMagick-psd.c-update.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File ImageMagick-psd.c-update.patch of Package ImageMagick.11106
Index: ImageMagick-6.8.8-1/coders/psd.c =================================================================== --- ImageMagick-6.8.8-1.orig/coders/psd.c 2018-02-12 17:15:17.254806314 +0100 +++ ImageMagick-6.8.8-1/coders/psd.c 2018-02-12 18:10:59.147680186 +0100 @@ -20,13 +20,13 @@ % December 2013 % % % % % -% Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization % +% Copyright 1999-2018 ImageMagick Studio LLC, a non-profit organization % % dedicated to making software imaging solutions freely available. % % % % You may not use this file except in compliance with the License. You may % % obtain a copy of the License at % % % -% http://www.imagemagick.org/script/license.php % +% https://www.imagemagick.org/script/license.php % % % % Unless required by applicable law or agreed to in writing, software % % distributed under the License is distributed on an "AS IS" BASIS, % @@ -44,9 +44,11 @@ */ #include "magick/studio.h" #include "magick/artifact.h" +#include "magick/attribute.h" #include "magick/blob.h" #include "magick/blob-private.h" #include "magick/cache.h" +#include "magick/channel.h" #include "magick/colormap.h" #include "magick/colormap-private.h" #include "magick/colorspace.h" @@ -63,22 +65,29 @@ #include "magick/memory_.h" #include "magick/module.h" #include "magick/monitor-private.h" +#include "magick/option.h" #include "magick/pixel.h" #include "magick/pixel-accessor.h" +#include "magick/policy.h" #include "magick/profile.h" #include "magick/property.h" +#include "magick/registry.h" #include "magick/quantum-private.h" #include "magick/static.h" #include "magick/string_.h" +#include "magick/string-private.h" +#include "magick/thread-private.h" #ifdef MAGICKCORE_ZLIB_DELEGATE #include <zlib.h> #endif + /* Define declaractions. */ #define MaxPSDChannels 56 #define PSDQuantum(x) (((ssize_t) (x)+1) & -2) - + + /* Enumerated declaractions. */ @@ -101,7 +110,7 @@ typedef enum DuotoneMode = 8, LabMode = 9 } PSDImageType; - + /* Typedef declaractions. */ @@ -114,64 +123,75 @@ typedef struct _ChannelInfo size; } ChannelInfo; -typedef struct _LayerInfo +typedef struct _MaskInfo { + Image + *image; + RectangleInfo - page, - mask; + page; + + unsigned char + background, + flags; +} MaskInfo; + +typedef struct _PSDInfo +{ + char + signature[4]; + + size_t + rows, + columns; + + unsigned char + reserved[6]; unsigned short - channels; + channels, + depth, + mode, + version; +} PSDInfo; +typedef struct _LayerInfo +{ ChannelInfo channel_info[MaxPSDChannels]; char blendkey[4]; - MagickBooleanType - has_merged_alpha; + Image + *image; + + MaskInfo + mask; Quantum opacity; - unsigned char - clipping, - visible, - flags; + RectangleInfo + page; size_t offset_x, offset_y; unsigned char - name[256]; - - Image - *image; -} LayerInfo; - -typedef struct _PSDInfo -{ - char - signature[4]; + clipping, + flags, + name[256], + visible; unsigned short - channels, - version; - - unsigned char - reserved[6]; + channels; - size_t - rows, - columns; + StringInfo + *info; +} LayerInfo; - unsigned short - depth, - mode; -} PSDInfo; - /* Forward declarations. */ @@ -229,7 +249,7 @@ static MagickBooleanType IsPSD(const uns % % The format of the ReadPSDImage method is: % -% Image *ReadPSDImage(image_info) +% Image *ReadPSDImage(image_info,ExceptionInfo *exception) % % A description of each parameter follows: % @@ -246,24 +266,93 @@ static const char *CompositeOperatorToPS switch (op) { - case OverCompositeOp: blend_mode = "norm"; break; - case MultiplyCompositeOp: blend_mode = "mul "; break; - case DissolveCompositeOp: blend_mode = "diss"; break; - case DifferenceCompositeOp: blend_mode = "diff"; break; - case DarkenCompositeOp: blend_mode = "dark"; break; - case LightenCompositeOp: blend_mode = "lite"; break; - case HueCompositeOp: blend_mode = "hue "; break; - case SaturateCompositeOp: blend_mode = "sat "; break; - case ColorizeCompositeOp: blend_mode = "colr"; break; - case LuminizeCompositeOp: blend_mode = "lum "; break; - case ScreenCompositeOp: blend_mode = "scrn"; break; - case OverlayCompositeOp: blend_mode = "over"; break; - default: - blend_mode = "norm"; + case ColorBurnCompositeOp: blend_mode = "idiv"; break; + case ColorDodgeCompositeOp: blend_mode = "div "; break; + case ColorizeCompositeOp: blend_mode = "colr"; break; + case DarkenCompositeOp: blend_mode = "dark"; break; + case DifferenceCompositeOp: blend_mode = "diff"; break; + case DissolveCompositeOp: blend_mode = "diss"; break; + case ExclusionCompositeOp: blend_mode = "smud"; break; + case HardLightCompositeOp: blend_mode = "hLit"; break; + case HueCompositeOp: blend_mode = "hue "; break; + case LightenCompositeOp: blend_mode = "lite"; break; + case LinearBurnCompositeOp: blend_mode = "lbrn"; break; + case LinearDodgeCompositeOp:blend_mode = "lddg"; break; + case LinearLightCompositeOp:blend_mode = "lLit"; break; + case LuminizeCompositeOp: blend_mode = "lum "; break; + case MultiplyCompositeOp: blend_mode = "mul "; break; + case OverCompositeOp: blend_mode = "norm"; break; + case OverlayCompositeOp: blend_mode = "over"; break; + case PinLightCompositeOp: blend_mode = "pLit"; break; + case SaturateCompositeOp: blend_mode = "sat "; break; + case ScreenCompositeOp: blend_mode = "scrn"; break; + case SoftLightCompositeOp: blend_mode = "sLit"; break; + case VividLightCompositeOp: blend_mode = "vLit"; break; + default: blend_mode = "norm"; break; } return(blend_mode); } +/* +For some reason Photoshop seems to blend semi-transparent pixels with white. +This method reverts the blending. This can be disabled by setting the +option 'psd:alpha-unblend' to off. +*/ +static MagickBooleanType CorrectPSDAlphaBlend(const ImageInfo *image_info, + Image *image, ExceptionInfo* exception) +{ + const char + *option; + + MagickBooleanType + status; + + ssize_t + y; + + if (image->matte == MagickFalse || image->colorspace != sRGBColorspace) + return(MagickTrue); + option=GetImageOption(image_info,"psd:alpha-unblend"); + if (IsStringNotFalse(option) == MagickFalse) + return(MagickTrue); + status=MagickTrue; + for (y=0; y < (ssize_t) image->rows; y++) + { + register PixelPacket + *restrict q; + + register ssize_t + x; + + if (status == MagickFalse) + continue; + q=GetAuthenticPixels(image,0,y,image->columns,1,exception); + if (q == (PixelPacket *) NULL) + { + status=MagickFalse; + continue; + } + for (x=0; x < (ssize_t) image->columns; x++) + { + double + gamma; + + gamma=QuantumScale*GetPixelAlpha(q); + if (gamma != 0.0 && gamma != 1.0) + { + SetPixelRed(q,(GetPixelRed(q)-((1.0-gamma)*QuantumRange))/gamma); + SetPixelGreen(q,(GetPixelGreen(q)-((1.0-gamma)*QuantumRange))/gamma); + SetPixelBlue(q,(GetPixelBlue(q)-((1.0-gamma)*QuantumRange))/gamma); + } + q++; + } + if (SyncAuthenticPixels(image,exception) == MagickFalse) + status=MagickFalse; + } + + return(status); +} + static inline CompressionType ConvertPSDCompression( PSDCompressionType compression) { @@ -279,38 +368,157 @@ static inline CompressionType ConvertPSD } } -static MagickStatusType CorrectPSDOpacity(LayerInfo* layer_info, - ExceptionInfo *exception) +static MagickBooleanType ApplyPSDLayerOpacity(Image *image,Quantum opacity, + MagickBooleanType revert,ExceptionInfo *exception) { - register PixelPacket - *q; - - register ssize_t - x; + MagickBooleanType + status; ssize_t y; - if (layer_info->opacity == OpaqueOpacity) + if (image->debug != MagickFalse) + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " applying layer opacity %.20g", (double) opacity); + if (opacity == QuantumRange) return(MagickTrue); - - layer_info->image->matte=MagickTrue; - for (y=0; y < (ssize_t) layer_info->image->rows; y++) + image->matte=MagickTrue; + status=MagickTrue; + for (y=0; y < (ssize_t) image->rows; y++) { - q=GetAuthenticPixels(layer_info->image,0,y,layer_info->image->columns,1, - exception); + register PixelPacket + *restrict q; + + register ssize_t + x; + + if (status == MagickFalse) + continue; + q=GetAuthenticPixels(image,0,y,image->columns,1,exception); if (q == (PixelPacket *) NULL) - break; - for (x=0; x < (ssize_t) layer_info->image->columns; x++) + { + status=MagickFalse; + continue; + } + for (x=0; x < (ssize_t) image->columns; x++) { - q->opacity=(Quantum) (QuantumRange-(Quantum) (QuantumScale*( - (QuantumRange-q->opacity)*(QuantumRange-layer_info->opacity)))); + if (revert == MagickFalse) + SetPixelAlpha(q,(Quantum) (QuantumScale*(GetPixelAlpha(q)*opacity))); + else if (opacity > 0) + SetPixelAlpha(q,(Quantum) (QuantumRange*(GetPixelAlpha(q)/ + (MagickRealType) opacity))); q++; } - if (SyncAuthenticPixels(layer_info->image,exception) == MagickFalse) - return(MagickFalse); + if (SyncAuthenticPixels(image,exception) == MagickFalse) + status=MagickFalse; + } + return(status); +} + +static MagickBooleanType ApplyPSDOpacityMask(Image *image,const Image *mask, + Quantum background,MagickBooleanType revert,ExceptionInfo *exception) +{ + Image + *complete_mask; + + MagickBooleanType + status; + + MagickPixelPacket + color; + + ssize_t + y; + + if (image->debug != MagickFalse) + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " applying opacity mask"); + complete_mask=CloneImage(image,image->columns,image->rows,MagickTrue, + exception); + if (complete_mask == (Image *) NULL) + return(MagickFalse); + complete_mask->matte=MagickTrue; + GetMagickPixelPacket(complete_mask,&color); + color.red=background; + SetImageColor(complete_mask,&color); + status=CompositeImage(complete_mask,OverCompositeOp,mask, + mask->page.x-image->page.x,mask->page.y-image->page.y); + if (status == MagickFalse) + { + complete_mask=DestroyImage(complete_mask); + return(status); + } + image->matte=MagickTrue; + for (y=0; y < (ssize_t) image->rows; y++) + { + register PixelPacket + *restrict q; + + register PixelPacket + *p; + + register ssize_t + x; + + if (status == MagickFalse) + continue; + q=GetAuthenticPixels(image,0,y,image->columns,1,exception); + p=GetAuthenticPixels(complete_mask,0,y,complete_mask->columns,1,exception); + if ((q == (PixelPacket *) NULL) || (p == (PixelPacket *) NULL)) + { + status=MagickFalse; + continue; + } + for (x=0; x < (ssize_t) image->columns; x++) + { + MagickRealType + alpha, + intensity; + + alpha=GetPixelAlpha(q); + intensity=GetPixelIntensity(complete_mask,p); + if (revert == MagickFalse) + SetPixelAlpha(q,ClampToQuantum(intensity*(QuantumScale*alpha))); + else if (intensity > 0) + SetPixelAlpha(q,ClampToQuantum((alpha/intensity)*QuantumRange)); + q++; + p++; + } + if (SyncAuthenticPixels(image,exception) == MagickFalse) + status=MagickFalse; } - return(MagickTrue); + complete_mask=DestroyImage(complete_mask); + return(status); +} + +static void PreservePSDOpacityMask(Image *image,LayerInfo* layer_info, + ExceptionInfo *exception) +{ + char + *key; + + RandomInfo + *random_info; + + StringInfo + *key_info; + + if (image->debug != MagickFalse) + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " preserving opacity mask"); + random_info=AcquireRandomInfo(); + key_info=GetRandomKey(random_info,2+1); + key=(char *) GetStringInfoDatum(key_info); + key[8]=layer_info->mask.background; + key[9]='\0'; + layer_info->mask.image->page.x+=layer_info->page.x; + layer_info->mask.image->page.y+=layer_info->page.y; + (void) SetImageRegistry(ImageRegistryType,(const char *) key, + layer_info->mask.image,exception); + (void) SetImageArtifact(layer_info->image,"psd:opacity-mask", + (const char *) key); + key_info=DestroyStringInfo(key_info); + random_info=DestroyRandomInfo(random_info); } static ssize_t DecodePSDPixels(const size_t number_compact_pixels, @@ -343,8 +551,8 @@ static ssize_t DecodePSDPixels(const siz packets=(ssize_t) number_compact_pixels; for (i=0; (packets > 1) && (i < (ssize_t) number_pixels); ) { - CheckNumberCompactPixels; - length=(*compact_pixels++); + packets--; + length=(size_t) (*compact_pixels++); if (length == 128) continue; if (length > 128) @@ -369,13 +577,6 @@ static ssize_t DecodePSDPixels(const siz *pixels++=(pixel >> 0) & 0x01 ? 0U : 255U; break; } - case 4: - { - CheckNumberPixels(2); - *pixels++=(unsigned char) ((pixel >> 4) & 0xff); - *pixels++=(unsigned char) ((pixel & 0x0f) & 0xff); - break; - } case 2: { CheckNumberPixels(4); @@ -385,6 +586,13 @@ static ssize_t DecodePSDPixels(const siz *pixels++=(unsigned char) ((pixel & 0x03) & 0x03); break; } + case 4: + { + CheckNumberPixels(2); + *pixels++=(unsigned char) ((pixel >> 4) & 0xff); + *pixels++=(unsigned char) ((pixel & 0x0f) & 0xff); + break; + } default: { CheckNumberPixels(1); @@ -414,13 +622,6 @@ static ssize_t DecodePSDPixels(const siz *pixels++=(*compact_pixels >> 0) & 0x01 ? 0U : 255U; break; } - case 4: - { - CheckNumberPixels(2); - *pixels++=(*compact_pixels >> 4) & 0xff; - *pixels++=(*compact_pixels & 0x0f) & 0xff; - break; - } case 2: { CheckNumberPixels(4); @@ -430,6 +631,13 @@ static ssize_t DecodePSDPixels(const siz *pixels++=(*compact_pixels & 0x03) & 0x03; break; } + case 4: + { + CheckNumberPixels(2); + *pixels++=(*compact_pixels >> 4) & 0xff; + *pixels++=(*compact_pixels & 0x0f) & 0xff; + break; + } default: { CheckNumberPixels(1); @@ -453,6 +661,10 @@ static inline LayerInfo *DestroyLayerInf { if (layer_info[i].image != (Image *) NULL) layer_info[i].image=DestroyImage(layer_info[i].image); + if (layer_info[i].mask.image != (Image *) NULL) + layer_info[i].mask.image=DestroyImage(layer_info[i].mask.image); + if (layer_info[i].info != (StringInfo *) NULL) + layer_info[i].info=DestroyStringInfo(layer_info[i].info); } return (LayerInfo *) RelinquishMagickMemory(layer_info); @@ -474,11 +686,11 @@ static inline size_t GetPSDPacketSize(Im return(1); } -static inline MagickSizeType GetPSDSize(PSDInfo *psd_info,Image *image) +static inline MagickSizeType GetPSDSize(const PSDInfo *psd_info,Image *image) { if (psd_info->version == 1) - return((MagickSizeType) ReadBlobMSBLong(image)); - return((MagickSizeType) ReadBlobMSBLongLong(image)); + return((MagickSizeType) ReadBlobLong(image)); + return((MagickSizeType) ReadBlobLongLong(image)); } static inline size_t GetPSDRowSize(Image *image) @@ -496,6 +708,21 @@ static inline ssize_t MagickAbsoluteValu return(x); } +static signed int ReadBlobSignedLong(Image *image) +{ + union + { + unsigned int + unsigned_value; + + signed int + signed_value; + } quantum; + + quantum.unsigned_value=ReadBlobLong(image); + return(quantum.signed_value); +} + static const char *ModeToString(PSDImageType type) { switch (type) @@ -512,8 +739,9 @@ static const char *ModeToString(PSDImage } } -static MagickBooleanType ParseImageResourceBlocks(Image *image, - const unsigned char *blocks,size_t length) +static void ParseImageResourceBlocks(Image *image, + const unsigned char *blocks,size_t length, + MagickBooleanType *has_merged_image) { const unsigned char *p; @@ -521,30 +749,37 @@ static MagickBooleanType ParseImageResou StringInfo *profile; + unsigned char + name_length; + unsigned int - count, - long_sans; + count; unsigned short id, short_sans; if (length < 16) - return(MagickFalse); + return; profile=BlobToStringInfo((const void *) NULL,length); SetStringInfoDatum(profile,blocks); (void) SetImageProfile(image,"8bim",profile); profile=DestroyStringInfo(profile); - for (p=blocks; (p >= blocks) && (p < (blocks+length-16)); ) + for (p=blocks; (p >= blocks) && (p < (blocks+length-7)); ) { if (LocaleNCompare((const char *) p,"8BIM",4) != 0) break; - p=PushLongPixel(MSBEndian,p,&long_sans); + p+=4; p=PushShortPixel(MSBEndian,p,&id); - p=PushShortPixel(MSBEndian,p,&short_sans); + p=PushCharPixel(p,&name_length); + if (name_length % 2 == 0) + name_length++; + p+=name_length; + if (p > (blocks+length-4)) + return; p=PushLongPixel(MSBEndian,p,&count); - if (p+count > blocks+length) - return MagickFalse; + if ((p+count) > (blocks+length)) + return; switch (id) { case 0x03ed: @@ -558,6 +793,8 @@ static MagickBooleanType ParseImageResou /* Resolution info. */ + if (count < 16) + return; p=PushShortPixel(MSBEndian,p,&resolution); image->x_resolution=(double) resolution; (void) FormatLocaleString(value,MaxTextExtent,"%g", @@ -577,6 +814,13 @@ static MagickBooleanType ParseImageResou image->units=PixelsPerInchResolution; break; } + case 0x0421: + { + if ((count > 3) && (*(p+4) == 0)) + *has_merged_image=MagickFalse; + p+=count; + break; + } default: { p+=count; @@ -586,7 +830,7 @@ static MagickBooleanType ParseImageResou if ((count & 0x01) != 0) p++; } - return(MagickTrue); + return; } static CompositeOperator PSDBlendModeToCompositeOperator(const char *mode) @@ -618,30 +862,68 @@ static CompositeOperator PSDBlendModeToC if (LocaleNCompare(mode,"over",4) == 0) return(OverlayCompositeOp); if (LocaleNCompare(mode,"hLit",4) == 0) - return(OverCompositeOp); + return(HardLightCompositeOp); if (LocaleNCompare(mode,"sLit",4) == 0) - return(OverCompositeOp); + return(SoftLightCompositeOp); if (LocaleNCompare(mode,"smud",4) == 0) - return(OverCompositeOp); + return(ExclusionCompositeOp); if (LocaleNCompare(mode,"div ",4) == 0) - return(OverCompositeOp); + return(ColorDodgeCompositeOp); if (LocaleNCompare(mode,"idiv",4) == 0) - return(OverCompositeOp); + return(ColorBurnCompositeOp); + if (LocaleNCompare(mode,"lbrn",4) == 0) + return(LinearBurnCompositeOp); + if (LocaleNCompare(mode,"lddg",4) == 0) + return(LinearDodgeCompositeOp); + if (LocaleNCompare(mode,"lLit",4) == 0) + return(LinearLightCompositeOp); + if (LocaleNCompare(mode,"vLit",4) == 0) + return(VividLightCompositeOp); + if (LocaleNCompare(mode,"pLit",4) == 0) + return(PinLightCompositeOp); return(OverCompositeOp); } +static inline void ReversePSDString(Image *image,char *p,size_t length) +{ + char + *q; + + if (image->endian == MSBEndian) + return; + + q=p+length; + for(--q; p < q; ++p, --q) + { + *p = *p ^ *q, + *q = *p ^ *q, + *p = *p ^ *q; + } +} + static inline void SetPSDPixel(Image *image,const size_t channels, const ssize_t type,const size_t packet_size,const Quantum pixel, - PixelPacket *q,IndexPacket *indexes,ssize_t x,ExceptionInfo *exception) + PixelPacket *q,IndexPacket *indexes,ssize_t x) { if (image->storage_class == PseudoClass) { - if (packet_size == 1) - SetPixelIndex(indexes+x,ScaleQuantumToChar(pixel)); + PixelPacket + *color; + + if (type == 0) + { + if (packet_size == 1) + SetPixelIndex(indexes+x,ScaleQuantumToChar(pixel)); + else + SetPixelIndex(indexes+x,ScaleQuantumToShort(pixel)); + } + color=image->colormap+(ssize_t) ConstrainColormapIndex(image, + GetPixelIndex(indexes+x)); + if ((type == 0) && (channels > 1)) + return; else - SetPixelIndex(indexes+x,ScaleQuantumToShort(pixel)); - SetPixelRGBO(q,image->colormap+(ssize_t) - ConstrainColormapIndex(image,GetPixelIndex(indexes+x))); + SetPixelAlpha(color,pixel); + SetPixelRGBO(q,color); return; } switch (type) @@ -655,7 +937,7 @@ static inline void SetPSDPixel(Image *im case 0: { SetPixelRed(q,pixel); - if (channels == 1 || type == -2) + if (channels < 3 || type == -2) { SetPixelGreen(q,GetPixelRed(q)); SetPixelBlue(q,GetPixelRed(q)); @@ -664,18 +946,12 @@ static inline void SetPSDPixel(Image *im } case 1: { - if (image->storage_class == PseudoClass) - SetPixelAlpha(q,pixel); - else - SetPixelGreen(q,pixel); + SetPixelGreen(q,pixel); break; } case 2: { - if (image->storage_class == PseudoClass) - SetPixelAlpha(q,pixel); - else - SetPixelBlue(q,pixel); + SetPixelBlue(q,pixel); break; } case 3: @@ -699,9 +975,9 @@ static inline void SetPSDPixel(Image *im } } -static MagickStatusType ReadPSDChannelPixels(Image *image, - const size_t channels,const size_t row,const ssize_t type, - const unsigned char *pixels,ExceptionInfo *exception) +static MagickBooleanType ReadPSDChannelPixels(Image *image,const size_t channels, + const size_t row,const ssize_t type,const unsigned char *pixels, + ExceptionInfo *exception) { Quantum pixel; @@ -741,7 +1017,7 @@ static MagickStatusType ReadPSDChannelPi } if (image->depth > 1) { - SetPSDPixel(image,channels,type,packet_size,pixel,q++,indexes,x,exception); + SetPSDPixel(image,channels,type,packet_size,pixel,q++,indexes,x); } else { @@ -754,10 +1030,10 @@ static MagickStatusType ReadPSDChannelPi number_bits=8; for (bit=0; bit < number_bits; bit++) { - SetPSDPixel(image,channels,type,packet_size,pixel,q++,indexes,x++, - exception); + SetPSDPixel(image,channels,type,packet_size,(((unsigned char) pixel) + & (0x01 << (7-bit))) != 0 ? 0 : QuantumRange,q++,indexes,x++); } - if (x != image->columns) + if (x != (ssize_t) image->columns) x--; continue; } @@ -765,10 +1041,10 @@ static MagickStatusType ReadPSDChannelPi return(SyncAuthenticPixels(image,exception)); } -static MagickStatusType ReadPSDChannelRaw(Image *image,const size_t channels, +static MagickBooleanType ReadPSDChannelRaw(Image *image,const size_t channels, const ssize_t type,ExceptionInfo *exception) { - MagickStatusType + MagickBooleanType status; size_t @@ -786,7 +1062,7 @@ static MagickStatusType ReadPSDChannelRa " layer data is RAW"); row_size=GetPSDRowSize(image); - pixels=(unsigned char *) AcquireQuantumMemory(row_size,8*sizeof(*pixels)); + pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels)); if (pixels == (unsigned char *) NULL) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); @@ -809,33 +1085,33 @@ static MagickStatusType ReadPSDChannelRa return(status); } -static inline MagickOffsetType *ReadPSDRLEOffsets(Image *image, - PSDInfo *psd_info,const size_t size) +static inline MagickOffsetType *ReadPSDRLESizes(Image *image, + const PSDInfo *psd_info,const size_t size) { MagickOffsetType - *offsets; + *sizes; ssize_t y; - offsets=(MagickOffsetType *) AcquireQuantumMemory(size,sizeof(*offsets)); - if(offsets != (MagickOffsetType *) NULL) + sizes=(MagickOffsetType *) AcquireQuantumMemory(size,sizeof(*sizes)); + if(sizes != (MagickOffsetType *) NULL) { for (y=0; y < (ssize_t) size; y++) { if (psd_info->version == 1) - offsets[y]=(MagickOffsetType) ReadBlobMSBShort(image); + sizes[y]=(MagickOffsetType) ReadBlobShort(image); else - offsets[y]=(MagickOffsetType) ReadBlobMSBLong(image); + sizes[y]=(MagickOffsetType) ReadBlobLong(image); } } - return offsets; + return sizes; } -static MagickStatusType ReadPSDChannelRLE(Image *image,PSDInfo *psd_info, - const ssize_t type,MagickOffsetType *offsets,ExceptionInfo *exception) +static MagickBooleanType ReadPSDChannelRLE(Image *image,const PSDInfo *psd_info, + const ssize_t type,MagickOffsetType *sizes,ExceptionInfo *exception) { - MagickStatusType + MagickBooleanType status; size_t @@ -855,18 +1131,23 @@ static MagickStatusType ReadPSDChannelRL " layer data is RLE compressed"); row_size=GetPSDRowSize(image); - pixels=(unsigned char *) AcquireQuantumMemory(row_size,8*sizeof(*pixels)); + pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels)); if (pixels == (unsigned char *) NULL) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); length=0; for (y=0; y < (ssize_t) image->rows; y++) - if ((MagickOffsetType) length < offsets[y]) - length=(size_t) offsets[y]; + if ((MagickOffsetType) length < sizes[y]) + length=(size_t) sizes[y]; - compact_pixels=(unsigned char *) AcquireQuantumMemory(length, - 8*sizeof(*pixels)); + if (length > (row_size+512)) + { + pixels=(unsigned char *) RelinquishMagickMemory(pixels); + ThrowBinaryException(ResourceLimitError,"InvalidLength",image->filename); + } + + compact_pixels=(unsigned char *) AcquireQuantumMemory(length,sizeof(*pixels)); if (compact_pixels == (unsigned char *) NULL) { pixels=(unsigned char *) RelinquishMagickMemory(pixels); @@ -881,11 +1162,11 @@ static MagickStatusType ReadPSDChannelRL { status=MagickFalse; - count=ReadBlob(image,(size_t) offsets[y],compact_pixels); - if (count != (ssize_t) offsets[y]) + count=ReadBlob(image,(size_t) sizes[y],compact_pixels); + if (count != (ssize_t) sizes[y]) break; - count=DecodePSDPixels((size_t) offsets[y],compact_pixels, + count=DecodePSDPixels((size_t) sizes[y],compact_pixels, (ssize_t) (image->depth == 1 ? 123456 : image->depth),row_size,pixels); if (count != (ssize_t) row_size) break; @@ -901,13 +1182,12 @@ static MagickStatusType ReadPSDChannelRL return(status); } -static MagickStatusType ReadPSDChannelZip(Image *image, - const size_t channels,const ssize_t type, - const PSDCompressionType compression,const size_t compact_size, - ExceptionInfo *exception) -{ #ifdef MAGICKCORE_ZLIB_DELEGATE - MagickStatusType +static MagickBooleanType ReadPSDChannelZip(Image *image,const size_t channels, + const ssize_t type,const PSDCompressionType compression, + const size_t compact_size,ExceptionInfo *exception) +{ + MagickBooleanType status; register unsigned char @@ -931,7 +1211,7 @@ static MagickStatusType ReadPSDChannelZi if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " layer data is RLE compressed"); + " layer data is ZIP compressed"); compact_pixels=(unsigned char *) AcquireQuantumMemory(compact_size, sizeof(*compact_pixels)); @@ -950,32 +1230,37 @@ static MagickStatusType ReadPSDChannelZi ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); } + if (ReadBlob(image,compact_size,compact_pixels) != (ssize_t) compact_size) + { + pixels=(unsigned char *) RelinquishMagickMemory(pixels); + compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels); + ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile", + image->filename); + } - ResetMagickMemory(&stream, 0, sizeof(z_stream)); + ResetMagickMemory(&stream,0,sizeof(stream)); stream.data_type=Z_BINARY; - (void) ReadBlob(image,compact_size,compact_pixels); - stream.next_in=(Bytef *)compact_pixels; - stream.avail_in=compact_size; + stream.avail_in=(uInt) compact_size; stream.next_out=(Bytef *)pixels; - stream.avail_out=count; + stream.avail_out=(uInt) count; - if(inflateInit(&stream) == Z_OK) + if (inflateInit(&stream) == Z_OK) { int ret; while (stream.avail_out > 0) { - ret=inflate(&stream, Z_SYNC_FLUSH); - if (ret != Z_OK && ret != Z_STREAM_END) - { - (void) inflateEnd(&stream); - compact_pixels=(unsigned char *) RelinquishMagickMemory( - compact_pixels); - pixels=(unsigned char *) RelinquishMagickMemory(pixels); - return(MagickFalse); - } + ret=inflate(&stream,Z_SYNC_FLUSH); + if ((ret != Z_OK) && (ret != Z_STREAM_END)) + { + (void) inflateEnd(&stream); + compact_pixels=(unsigned char *) RelinquishMagickMemory( + compact_pixels); + pixels=(unsigned char *) RelinquishMagickMemory(pixels); + return(MagickFalse); + } if (ret == Z_STREAM_END) break; } @@ -985,10 +1270,10 @@ static MagickStatusType ReadPSDChannelZi if (compression == ZipWithPrediction) { p=pixels; - while(count > 0) + while (count > 0) { length=image->columns; - while(--length) + while (--length) { if (packet_size == 2) { @@ -1018,84 +1303,112 @@ static MagickStatusType ReadPSDChannelZi compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels); pixels=(unsigned char *) RelinquishMagickMemory(pixels); return(status); -#else - magick_unreferenced(image); - magick_unreferenced(channels); - magick_unreferenced(type); - magick_unreferenced(compression); - magick_unreferenced(compact_size); - magick_unreferenced(exception); - return(MagickFalse); -#endif } +#endif -static MagickStatusType ReadPSDChannel(Image *image,PSDInfo *psd_info, - const LayerInfo* layer_info,const size_t channel, - const PSDCompressionType compression,ExceptionInfo *exception) +static MagickBooleanType ReadPSDChannel(Image *image, + const ImageInfo *image_info,const PSDInfo *psd_info,LayerInfo* layer_info, + const size_t channel,const PSDCompressionType compression, + ExceptionInfo *exception) { + Image + *channel_image, + *mask; + MagickOffsetType offset; - MagickStatusType + MagickBooleanType status; + channel_image=image; + mask=(Image *) NULL; + if ((layer_info->channel_info[channel].type < -1) && + (layer_info->mask.page.width > 0) && (layer_info->mask.page.height > 0)) + { + const char + *option; + + /* + Ignore mask that is not a user supplied layer mask, if the mask is + disabled or if the flags have unsupported values. + */ + option=GetImageOption(image_info,"psd:preserve-opacity-mask"); + if ((layer_info->channel_info[channel].type != -2) || + (layer_info->mask.flags > 2) || ((layer_info->mask.flags & 0x02) && + (IsStringTrue(option) == MagickFalse))) + { + SeekBlob(image,layer_info->channel_info[channel].size-2,SEEK_CUR); + return(MagickTrue); + } + mask=CloneImage(image,layer_info->mask.page.width, + layer_info->mask.page.height,MagickFalse,exception); + if (mask != (Image *) NULL) + { + mask->matte=MagickFalse; + channel_image=mask; + } + } + offset=TellBlob(image); - status=MagickTrue; + status=MagickFalse; switch(compression) { case Raw: - return(ReadPSDChannelRaw(image,psd_info->channels, - layer_info->channel_info[channel].type,exception)); + status=ReadPSDChannelRaw(channel_image,psd_info->channels, + layer_info->channel_info[channel].type,exception); + break; case RLE: { MagickOffsetType - *offsets; + *sizes; - offsets=ReadPSDRLEOffsets(image,psd_info,image->rows); - if (offsets == (MagickOffsetType *) NULL) + sizes=ReadPSDRLESizes(channel_image,psd_info,channel_image->rows); + if (sizes == (MagickOffsetType *) NULL) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); - status=ReadPSDChannelRLE(image,psd_info, - layer_info->channel_info[channel].type,offsets,exception); - offsets=(MagickOffsetType *) RelinquishMagickMemory(offsets); + status=ReadPSDChannelRLE(channel_image,psd_info, + layer_info->channel_info[channel].type,sizes,exception); + sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes); } break; case ZipWithPrediction: case ZipWithoutPrediction: #ifdef MAGICKCORE_ZLIB_DELEGATE - status=ReadPSDChannelZip(image,layer_info->channels, + status=ReadPSDChannelZip(channel_image,layer_info->channels, layer_info->channel_info[channel].type,compression, layer_info->channel_info[channel].size-2,exception); #else - SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET); (void) ThrowMagickException(exception,GetMagickModule(), MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn", "'%s' (ZLIB)",image->filename); #endif break; default: - SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET); (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning, "CompressionNotSupported","'%.20g'",(double) compression); break; } + SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET); if (status == MagickFalse) - SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET); - + { + if (mask != (Image *) NULL) + DestroyImage(mask); + ThrowBinaryException(CoderError,"UnableToDecompressImage", + image->filename); + } + layer_info->mask.image=mask; return(status); } -static MagickStatusType ReadPSDLayer(Image *image,PSDInfo *psd_info, - LayerInfo* layer_info,ExceptionInfo *exception) +static MagickBooleanType ReadPSDLayer(Image *image,const ImageInfo *image_info, + const PSDInfo *psd_info,LayerInfo* layer_info,ExceptionInfo *exception) { char message[MaxTextExtent]; MagickBooleanType - correct_opacity; - - MagickStatusType status; PSDCompressionType @@ -1107,20 +1420,24 @@ static MagickStatusType ReadPSDLayer(Ima if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " setting up new layer image"); - (void) SetImageBackgroundColor(layer_info->image); + if (psd_info->mode != IndexedMode) + (void) SetImageBackgroundColor(layer_info->image); layer_info->image->compose=PSDBlendModeToCompositeOperator( layer_info->blendkey); if (layer_info->visible == MagickFalse) - layer_info->image->compose=NoCompositeOp; + { + layer_info->image->compose=NoCompositeOp; + (void) SetImageArtifact(layer_info->image,"psd:layer.invisible","true"); + } if (psd_info->mode == CMYKMode) SetImageColorspace(layer_info->image,CMYKColorspace); - if ((psd_info->mode == BitmapMode) || (psd_info->mode == GrayscaleMode) || - (psd_info->mode == DuotoneMode)) - SetImageColorspace(layer_info->image,GRAYColorspace); + else if ((psd_info->mode == BitmapMode) || (psd_info->mode == DuotoneMode) || + (psd_info->mode == GrayscaleMode)) + SetImageColorspace(layer_info->image,GRAYColorspace); /* Set up some hidden attributes for folks that need them. */ - (void) FormatLocaleString(message,MaxTextExtent,"%.20gld", + (void) FormatLocaleString(message,MaxTextExtent,"%.20g", (double) layer_info->page.x); (void) SetImageArtifact(layer_info->image,"psd:layer.x",message); (void) FormatLocaleString(message,MaxTextExtent,"%.20g", @@ -1132,49 +1449,59 @@ static MagickStatusType ReadPSDLayer(Ima (void) SetImageProperty(layer_info->image,"label",(char *) layer_info->name); status=MagickTrue; - correct_opacity=MagickFalse; for (j=0; j < (ssize_t) layer_info->channels; j++) { if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " reading data for channel %.20g",(double) j); - compression=(PSDCompressionType) ReadBlobMSBShort(layer_info->image); + compression=(PSDCompressionType) ReadBlobShort(layer_info->image); layer_info->image->compression=ConvertPSDCompression(compression); - if (layer_info->has_merged_alpha != MagickFalse && - layer_info->channel_info[j].type == -1) - { - layer_info->has_merged_alpha=MagickFalse; - image->matte=MagickTrue; - status=ReadPSDChannel(image,psd_info,layer_info,j,compression, - exception); - } - else - { - correct_opacity=MagickTrue; - if (layer_info->channel_info[j].type == -1) - layer_info->image->matte=MagickTrue; - - status=ReadPSDChannel(layer_info->image,psd_info,layer_info,j, - compression,exception); - InheritException(exception,&layer_info->image->exception); - } + if (layer_info->channel_info[j].type == -1) + layer_info->image->matte=MagickTrue; + + status=ReadPSDChannel(layer_info->image,image_info,psd_info,layer_info,j, + compression,exception); + InheritException(exception,&layer_info->image->exception); if (status == MagickFalse) break; } - if (status != MagickFalse && correct_opacity != MagickFalse) - status=CorrectPSDOpacity(layer_info,exception); - - if (status != MagickFalse && layer_info->image->colorspace == CMYKColorspace) - (void) NegateImage(layer_info->image,MagickFalse); + if (status != MagickFalse) + status=ApplyPSDLayerOpacity(layer_info->image,layer_info->opacity, + MagickFalse,exception); + + if ((status != MagickFalse) && + (layer_info->image->colorspace == CMYKColorspace)) + status=NegateImage(layer_info->image,MagickFalse); + + if (status != MagickFalse && layer_info->mask.image != (Image *) NULL) + { + const char + *option; + + layer_info->mask.image->page.x=layer_info->mask.page.x; + layer_info->mask.image->page.y=layer_info->mask.page.y; + /* Do not composite the mask when it is disabled */ + if ((layer_info->mask.flags & 0x02) == 0x02) + layer_info->mask.image->compose=NoCompositeOp; + else + status=ApplyPSDOpacityMask(layer_info->image,layer_info->mask.image, + layer_info->mask.background == 0 ? 0 : QuantumRange,MagickFalse, + exception); + option=GetImageOption(image_info,"psd:preserve-opacity-mask"); + if (IsStringTrue(option) != MagickFalse) + PreservePSDOpacityMask(image,layer_info,exception); + layer_info->mask.image=DestroyImage(layer_info->mask.image); + } return(status); } -static MagickStatusType ReadPSDLayers(Image *image,PSDInfo *psd_info, - ExceptionInfo *exception) +static MagickBooleanType ReadPSDLayersInternal(Image *image, + const ImageInfo *image_info,const PSDInfo *psd_info, + const MagickBooleanType skip_layers,ExceptionInfo *exception) { char type[4]; @@ -1182,13 +1509,10 @@ static MagickStatusType ReadPSDLayers(Im LayerInfo *layer_info; - MagickBooleanType - has_merged_alpha; - MagickSizeType size; - MagickStatusType + MagickBooleanType status; register ssize_t @@ -1202,53 +1526,50 @@ static MagickStatusType ReadPSDLayers(Im size=GetPSDSize(psd_info,image); if (size == 0) { - size_t - quantum; - /* Skip layers & masks. */ - quantum=psd_info->version == 1 ? 4UL : 8UL; - (void) ReadBlobMSBLong(image); + (void) ReadBlobLong(image); count=ReadBlob(image,4,(unsigned char *) type); + ReversePSDString(image,type,4); + status=MagickFalse; if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0)) - { - if (DiscardBlobBytes(image,size-quantum-8) == MagickFalse) - ThrowBinaryException(CorruptImageError, - "UnexpectedEndOfFile",image->filename); - } + return(MagickTrue); else { count=ReadBlob(image,4,(unsigned char *) type); + ReversePSDString(image,type,4); if ((count != 0) && (LocaleNCompare(type,"Lr16",4) == 0)) size=GetPSDSize(psd_info,image); else - if (DiscardBlobBytes(image,size-quantum-12) == MagickFalse) - ThrowBinaryException(CorruptImageError, - "UnexpectedEndOfFile",image->filename); + return(MagickTrue); } } - status=MagickTrue; if (size != 0) { layer_info=(LayerInfo *) NULL; - number_layers=(short) ReadBlobMSBShort(image); + number_layers=(short) ReadBlobShort(image); - has_merged_alpha=MagickFalse; if (number_layers < 0) { /* - The first alpha channel contains the transparency data for the - merged result. + The first alpha channel in the merged result contains the + transparency data for the merged result. */ number_layers=MagickAbsoluteValue(number_layers); if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " negative layer count corrected for"); - has_merged_alpha=MagickTrue; + image->matte=MagickTrue; } + /* + We only need to know if the image has an alpha channel + */ + if (skip_layers != MagickFalse) + return(MagickTrue); + if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " image contains %.20g layers",(double) number_layers); @@ -1272,25 +1593,25 @@ static MagickStatusType ReadPSDLayers(Im for (i=0; i < number_layers; i++) { - int + ssize_t x, y; if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " reading layer #%.20g",(double) i+1); - layer_info[i].page.y=(int) ReadBlobMSBLong(image); - layer_info[i].page.x=(int) ReadBlobMSBLong(image); - y=(int) ReadBlobMSBLong(image); - x=(int) ReadBlobMSBLong(image); - layer_info[i].page.width=(ssize_t) (x-layer_info[i].page.x); - layer_info[i].page.height=(ssize_t) (y-layer_info[i].page.y); - layer_info[i].channels=ReadBlobMSBShort(image); + layer_info[i].page.y=ReadBlobSignedLong(image); + layer_info[i].page.x=ReadBlobSignedLong(image); + y=ReadBlobSignedLong(image); + x=ReadBlobSignedLong(image); + layer_info[i].page.width=(size_t) (x-layer_info[i].page.x); + layer_info[i].page.height=(size_t) (y-layer_info[i].page.y); + layer_info[i].channels=ReadBlobShort(image); if (layer_info[i].channels > MaxPSDChannels) { layer_info=DestroyLayerInfo(layer_info,number_layers); ThrowBinaryException(CorruptImageError,"MaximumChannelsExceeded", - image->filename); + image->filename); } if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), @@ -1300,7 +1621,7 @@ static MagickStatusType ReadPSDLayers(Im layer_info[i].page.width,(double) layer_info[i].channels); for (j=0; j < (ssize_t) layer_info[i].channels; j++) { - layer_info[i].channel_info[j].type=(short) ReadBlobMSBShort(image); + layer_info[i].channel_info[j].type=(short) ReadBlobShort(image); layer_info[i].channel_info[j].size=(size_t) GetPSDSize(psd_info, image); if (image->debug != MagickFalse) @@ -1310,6 +1631,7 @@ static MagickStatusType ReadPSDLayers(Im (double) layer_info[i].channel_info[j].size); } count=ReadBlob(image,4,(unsigned char *) type); + ReversePSDString(image,type,4); if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0)) { if (image->debug != MagickFalse) @@ -1319,9 +1641,10 @@ static MagickStatusType ReadPSDLayers(Im ThrowBinaryException(CorruptImageError,"ImproperImageHeader", image->filename); } - count=ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey); - layer_info[i].opacity=(Quantum) (QuantumRange-ScaleCharToQuantum( - (unsigned char) ReadBlobByte(image))); + (void) ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey); + ReversePSDString(image,layer_info[i].blendkey,4); + layer_info[i].opacity=(Quantum) ScaleCharToQuantum((unsigned char) + ReadBlobByte(image)); layer_info[i].clipping=(unsigned char) ReadBlobByte(image); layer_info[i].flags=(unsigned char) ReadBlobByte(image); layer_info[i].visible=!(layer_info[i].flags & 0x02); @@ -1333,7 +1656,7 @@ static MagickStatusType ReadPSDLayers(Im layer_info[i].visible ? "true" : "false"); (void) ReadBlobByte(image); /* filler */ - size=ReadBlobMSBLong(image); + size=ReadBlobLong(image); if (size != 0) { MagickSizeType @@ -1343,37 +1666,47 @@ static MagickStatusType ReadPSDLayers(Im if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " layer contains additional info"); - length=ReadBlobMSBLong(image); + length=ReadBlobLong(image); combined_length=length+4; if (length != 0) { /* Layer mask info. */ - layer_info[i].mask.y=(int) ReadBlobMSBLong(image); - layer_info[i].mask.x=(int) ReadBlobMSBLong(image); - layer_info[i].mask.height=(size_t) - (ReadBlobMSBLong(image)-layer_info[i].mask.y); - layer_info[i].mask.width=(size_t) - (ReadBlobMSBLong(image)-layer_info[i].mask.x); + layer_info[i].mask.page.y=ReadBlobSignedLong(image); + layer_info[i].mask.page.x=ReadBlobSignedLong(image); + layer_info[i].mask.page.height=(size_t) (ReadBlobSignedLong(image)- + layer_info[i].mask.page.y); + layer_info[i].mask.page.width=(size_t) (ReadBlobSignedLong(image)- + layer_info[i].mask.page.x); + layer_info[i].mask.background=(unsigned char) ReadBlobByte( + image); + layer_info[i].mask.flags=(unsigned char) ReadBlobByte(image); + if (!(layer_info[i].mask.flags & 0x01)) + { + layer_info[i].mask.page.y=layer_info[i].mask.page.y- + layer_info[i].page.y; + layer_info[i].mask.page.x=layer_info[i].mask.page.x- + layer_info[i].page.x; + } if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " layer mask: offset(%.20g,%.20g), size(%.20g,%.20g), length=%.20g", - (double) layer_info[i].mask.x,(double) - layer_info[i].mask.y,(double) layer_info[i].mask.width, - (double) layer_info[i].mask.height,(double) - ((MagickOffsetType) length)-16); + (double) layer_info[i].mask.page.x,(double) + layer_info[i].mask.page.y,(double) layer_info[i].mask.page.width, + (double) layer_info[i].mask.page.height,(double) + ((MagickOffsetType) length)-18); /* Skip over the rest of the layer mask information. */ - if (DiscardBlobBytes(image,length-16) == MagickFalse) + if (DiscardBlobBytes(image,(MagickSizeType) (length-18)) == MagickFalse) { layer_info=DestroyLayerInfo(layer_info,number_layers); ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile", image->filename); } } - length=ReadBlobMSBLong(image); + length=ReadBlobLong(image); combined_length+=length+4; if (length != 0) { @@ -1394,63 +1727,58 @@ static MagickStatusType ReadPSDLayers(Im /* Layer name. */ - length=(size_t) (unsigned char)ReadBlobByte(image); - if (length > GetBlobSize(image)) - { - layer_info=DestroyLayerInfo(layer_info,number_layers); - ThrowBinaryException(CorruptImageError, - "InsufficientImageDataInFile",image->filename); - } + length=(MagickSizeType) (unsigned char) ReadBlobByte(image); combined_length+=length+1; - for (j=0; j < (ssize_t) length; j++) - layer_info[i].name[j]=(unsigned char) ReadBlobByte(image); - layer_info[i].name[j]='\0'; + if (length > 0) + (void) ReadBlob(image,(size_t) length++,layer_info[i].name); + layer_info[i].name[length]='\0'; if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " layer name: %s",layer_info[i].name); - /* - Skip the rest of the variable data until we support it. - */ - if (image->debug != MagickFalse) - (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " unsupported data: length=%.20g",(double) - ((MagickOffsetType) (size-combined_length))); - if (DiscardBlobBytes(image,size-combined_length) == MagickFalse) - { - layer_info=DestroyLayerInfo(layer_info,number_layers); - ThrowBinaryException(CorruptImageError, - "UnexpectedEndOfFile",image->filename); - } + if ((length % 4) != 0) + { + length=4-(length % 4); + combined_length+=length; + /* Skip over the padding of the layer name */ + if (DiscardBlobBytes(image,length) == MagickFalse) + { + layer_info=DestroyLayerInfo(layer_info,number_layers); + ThrowBinaryException(CorruptImageError, + "UnexpectedEndOfFile",image->filename); + } + } + length=(MagickSizeType) size-combined_length; + if (length > 0) + { + unsigned char + *info; + + if (length > GetBlobSize(image)) + { + layer_info=DestroyLayerInfo(layer_info,number_layers); + ThrowBinaryException(CorruptImageError, + "InsufficientImageDataInFile",image->filename); + } + layer_info[i].info=AcquireStringInfo((const size_t) length); + info=GetStringInfoDatum(layer_info[i].info); + (void) ReadBlob(image,(const size_t) length,info); + } } } for (i=0; i < number_layers; i++) { - layer_info[i].has_merged_alpha=MagickFalse; - if ((layer_info[i].page.width == 0) || (layer_info[i].page.height == 0)) { if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " layer data is empty"); + if (layer_info[i].info != (StringInfo *) NULL) + layer_info[i].info=DestroyStringInfo(layer_info[i].info); continue; } - if (has_merged_alpha) - { - for (j=0; j < layer_info[i].channels; i++) - { - if (layer_info[i].channel_info[j].size > 2 && - layer_info[i].channel_info[j].type == -1) - { - layer_info[i].has_merged_alpha=MagickTrue; - break; - } - } - has_merged_alpha=MagickFalse; - } - /* Allocate layered image. */ @@ -1465,79 +1793,109 @@ static MagickStatusType ReadPSDLayers(Im ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); } + + if (layer_info[i].info != (StringInfo *) NULL) + { + (void) SetImageProfile(layer_info[i].image,"psd:additional-info", + layer_info[i].info); + layer_info[i].info=DestroyStringInfo(layer_info[i].info); + } } - for (i=0; i < number_layers; i++) - { - if (layer_info[i].image == (Image *) NULL) + if (image_info->ping == MagickFalse) { - for (j=0; j < layer_info[i].channels; j++) + for (i=0; i < number_layers; i++) { - if (DiscardBlobBytes(image,layer_info[i].channel_info[j].size) == - MagickFalse) + if (layer_info[i].image == (Image *) NULL) { - layer_info=DestroyLayerInfo(layer_info,number_layers); - ThrowBinaryException(CorruptImageError, - "UnexpectedEndOfFile",image->filename); + for (j=0; j < layer_info[i].channels; j++) + { + if (DiscardBlobBytes(image,(MagickSizeType) + layer_info[i].channel_info[j].size) == MagickFalse) + { + layer_info=DestroyLayerInfo(layer_info,number_layers); + ThrowBinaryException(CorruptImageError, + "UnexpectedEndOfFile",image->filename); + } + } + continue; } - } - continue; - } - - if (image->debug != MagickFalse) - (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " reading data for layer %.20g",(double) i); - status=ReadPSDLayer(image,psd_info,&layer_info[i],exception); - if (status == MagickFalse) - break; + if (image->debug != MagickFalse) + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " reading data for layer %.20g",(double) i); + status=ReadPSDLayer(image,image_info,psd_info,&layer_info[i], + exception); + if (status == MagickFalse) + break; - status=SetImageProgress(image,LoadImagesTag,i,(MagickSizeType) - number_layers); - if (status == MagickFalse) - break; - } + status=SetImageProgress(image,LoadImagesTag,i,(MagickSizeType) + number_layers); + if (status == MagickFalse) + break; + } + } if (status != MagickFalse) - { - for (i=0; i < number_layers; i++) { - if (layer_info[i].image == (Image *) NULL) + for (i=0; i < number_layers; i++) { - for (j=i; j < number_layers - 1; j++) - layer_info[j] = layer_info[j+1]; - number_layers--; - i--; + if (layer_info[i].image == (Image *) NULL) + { + for (j=i; j < number_layers - 1; j++) + layer_info[j] = layer_info[j+1]; + number_layers--; + i--; + } } - } - if (number_layers > 0) - { - for (i=0; i < number_layers; i++) + if (number_layers > 0) { - if (i > 0) - layer_info[i].image->previous=layer_info[i-1].image; - if (i < (number_layers-1)) - layer_info[i].image->next=layer_info[i+1].image; - layer_info[i].image->page=layer_info[i].page; + for (i=0; i < number_layers; i++) + { + if (i > 0) + layer_info[i].image->previous=layer_info[i-1].image; + if (i < (number_layers-1)) + layer_info[i].image->next=layer_info[i+1].image; + layer_info[i].image->page=layer_info[i].page; + } + image->next=layer_info[0].image; + layer_info[0].image->previous=image; } - image->next=layer_info[0].image; - layer_info[0].image->previous=image; - } - } - layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info); + layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info); + } + else + layer_info=DestroyLayerInfo(layer_info,number_layers); } return(status); } -static MagickStatusType ReadPSDMergedImage(Image* image,PSDInfo* psd_info, - ExceptionInfo *exception) +ModuleExport MagickBooleanType ReadPSDLayers(Image *image, + const ImageInfo *image_info,const PSDInfo *psd_info, + const MagickBooleanType skip_layers,ExceptionInfo *exception) +{ + PolicyDomain + domain; + + PolicyRights + rights; + + domain=CoderPolicyDomain; + rights=ReadPolicyRights; + if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse) + return(MagickFalse); + return(ReadPSDLayersInternal(image,image_info,psd_info,skip_layers, + exception)); +} + +static MagickBooleanType ReadPSDMergedImage(const ImageInfo *image_info, + Image* image,const PSDInfo* psd_info,ExceptionInfo *exception) { MagickOffsetType - *offsets; + *sizes; - MagickStatusType + MagickBooleanType status; PSDCompressionType @@ -1556,49 +1914,50 @@ static MagickStatusType ReadPSDMergedIma return(MagickFalse); } - offsets=(MagickOffsetType *) NULL; + sizes=(MagickOffsetType *) NULL; if (compression == RLE) - { - offsets=ReadPSDRLEOffsets(image,psd_info,image->rows*psd_info->channels); - if (offsets == (MagickOffsetType *) NULL) - ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", - image->filename); - } + { + sizes=ReadPSDRLESizes(image,psd_info,image->rows*psd_info->channels); + if (sizes == (MagickOffsetType *) NULL) + ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", + image->filename); + } status=MagickTrue; for (i=0; i < (ssize_t) psd_info->channels; i++) { if (compression == RLE) - status=ReadPSDChannelRLE(image,psd_info,i,offsets+(i*image->rows), + status=ReadPSDChannelRLE(image,psd_info,i,sizes+(i*image->rows), exception); else status=ReadPSDChannelRaw(image,psd_info->channels,i,exception); - if (status == MagickFalse) - break; - status=SetImageProgress(image,LoadImagesTag,i,psd_info->channels); + if (status != MagickFalse) + status=SetImageProgress(image,LoadImagesTag,i,psd_info->channels); + if (status == MagickFalse) break; } - if (image->colorspace == CMYKColorspace) - (void) NegateImage(image,MagickFalse); + if ((status != MagickFalse) && (image->colorspace == CMYKColorspace)) + status=NegateImage(image,MagickFalse); + + if (status != MagickFalse) + status=CorrectPSDAlphaBlend(image_info,image,exception); - if (offsets != (MagickOffsetType *) NULL) - offsets=(MagickOffsetType *) RelinquishMagickMemory(offsets); + sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes); return(status); } -static Image *ReadPSDImage(const ImageInfo *image_info, - ExceptionInfo *exception) +static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception) { Image *image; MagickBooleanType - skip_layers, - status; + has_merged_image, + skip_layers; MagickOffsetType offset; @@ -1606,6 +1965,9 @@ static Image *ReadPSDImage(const ImageIn MagickSizeType length; + MagickBooleanType + status; + PSDInfo psd_info; @@ -1639,12 +2001,13 @@ static Image *ReadPSDImage(const ImageIn /* Read image header. */ + image->endian=MSBEndian; count=ReadBlob(image,4,(unsigned char *) psd_info.signature); psd_info.version=ReadBlobMSBShort(image); if ((count == 0) || (LocaleNCompare(psd_info.signature,"8BPS",4) != 0) || ((psd_info.version != 1) && (psd_info.version != 2))) ThrowReaderException(CorruptImageError,"ImproperImageHeader"); - count=ReadBlob(image,6,psd_info.reserved); + (void) ReadBlob(image,6,psd_info.reserved); psd_info.channels=ReadBlobMSBShort(image); if (psd_info.channels > MaxPSDChannels) ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded"); @@ -1671,6 +2034,12 @@ static Image *ReadPSDImage(const ImageIn image->depth=psd_info.depth; image->columns=psd_info.columns; image->rows=psd_info.rows; + status=SetImageExtent(image,image->columns,image->rows); + if (status == MagickFalse) + { + InheritException(exception,&image->exception); + return(DestroyImageList(image)); + } if (SetImageBackgroundColor(image) == MagickFalse) { InheritException(exception,&image->exception); @@ -1680,9 +2049,12 @@ static Image *ReadPSDImage(const ImageIn if (psd_info.mode == LabMode) SetImageColorspace(image,LabColorspace); if (psd_info.mode == CMYKMode) - SetImageColorspace(image,CMYKColorspace); - if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) || - (psd_info.mode == DuotoneMode)) + { + SetImageColorspace(image,CMYKColorspace); + image->matte=psd_info.channels > 4 ? MagickTrue : MagickFalse; + } + else if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) || + (psd_info.mode == DuotoneMode)) { status=AcquireImageColormap(image,psd_info.depth != 16 ? 256 : 65536); if (status == MagickFalse) @@ -1691,8 +2063,10 @@ static Image *ReadPSDImage(const ImageIn (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Image colormap allocated"); SetImageColorspace(image,GRAYColorspace); + image->matte=psd_info.channels > 1 ? MagickTrue : MagickFalse; } - image->matte=MagickFalse; + else + image->matte=psd_info.channels > 3 ? MagickTrue : MagickFalse; /* Read PSD raster colormap only present for indexed and duotone images. */ @@ -1711,7 +2085,7 @@ static Image *ReadPSDImage(const ImageIn sizeof(*data)); if (data == (unsigned char *) NULL) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); - count=ReadBlob(image,(size_t) length,data); + (void) ReadBlob(image,(size_t) length,data); data=(unsigned char *) RelinquishMagickMemory(data); } else @@ -1741,6 +2115,7 @@ static Image *ReadPSDImage(const ImageIn } if ((image->depth == 1) && (image->storage_class != PseudoClass)) ThrowReaderException(CorruptImageError, "ImproperImageHeader"); + has_merged_image=MagickTrue; length=ReadBlobMSBLong(image); if (length != 0) { @@ -1756,7 +2131,7 @@ static Image *ReadPSDImage(const ImageIn ((MagickOffsetType) length)); if (length > GetBlobSize(image)) ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); - blocks=(unsigned char *) AcquireQuantumMemory((size_t) length+16, + blocks=(unsigned char *) AcquireQuantumMemory((size_t) length, sizeof(*blocks)); if (blocks == (unsigned char *) NULL) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); @@ -1767,20 +2142,9 @@ static Image *ReadPSDImage(const ImageIn blocks=(unsigned char *) RelinquishMagickMemory(blocks); ThrowReaderException(CorruptImageError,"ImproperImageHeader"); } - (void) ParseImageResourceBlocks(image,blocks,(size_t) length); + ParseImageResourceBlocks(image,blocks,(size_t) length,&has_merged_image); blocks=(unsigned char *) RelinquishMagickMemory(blocks); } - /* - If we are only "pinging" the image, then we're done - so return. - */ - if (EOFBlob(image) != MagickFalse) - ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile"); - if (image_info->ping != MagickFalse) - { - (void) CloseBlob(image); - image=DestroyImageList(image); - return(GetFirstImageInList(image)); - } /* Layer and mask block. */ @@ -1792,7 +2156,8 @@ static Image *ReadPSDImage(const ImageIn } offset=TellBlob(image); skip_layers=MagickFalse; - if ((image_info->number_scenes == 1) && (image_info->scene == 0)) + if ((image_info->number_scenes == 1) && (image_info->scene == 0) && + (has_merged_image != MagickFalse)) { if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), @@ -1807,25 +2172,62 @@ static Image *ReadPSDImage(const ImageIn } else { - if (skip_layers == MagickFalse) - if (ReadPSDLayers(image,&psd_info,exception) != MagickTrue) - { - ThrowReaderException(image->exception.severity, image->exception.reason); - } - + if (ReadPSDLayersInternal(image,image_info,&psd_info,skip_layers, + exception) != MagickTrue) + { + (void) CloseBlob(image); + image=DestroyImageList(image); + return((Image *) NULL); + } /* Skip the rest of the layer and mask information. */ SeekBlob(image,offset+length,SEEK_SET); } - + /* + If we are only "pinging" the image, then we're done - so return. + */ + if (EOFBlob(image) != MagickFalse) + ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile"); + if (image_info->ping != MagickFalse) + { + (void) CloseBlob(image); + return(GetFirstImageInList(image)); + } /* Read the precombined layer, present for PSD < 4 compatibility. */ if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " reading the precombined layer"); - (void) ReadPSDMergedImage(image,&psd_info,exception); + if (has_merged_image != MagickFalse || GetImageListLength(image) == 1) + has_merged_image=(MagickBooleanType) ReadPSDMergedImage(image_info,image, + &psd_info,exception); + if ((has_merged_image == MagickFalse) && (GetImageListLength(image) == 1) && + (length != 0)) + { + SeekBlob(image,offset,SEEK_SET); + status=ReadPSDLayersInternal(image,image_info,&psd_info,MagickFalse, + exception); + if (status != MagickTrue) + { + (void) CloseBlob(image); + image=DestroyImageList(image); + return((Image *) NULL); + } + } + if (has_merged_image == MagickFalse) + { + Image + *merged; + + if (GetImageListLength(image) == 1) + ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); + SetImageAlphaChannel(image,TransparentAlphaChannel); + image->background_color.opacity=TransparentOpacity; + merged=MergeImageLayers(image,FlattenLayer,exception); + ReplaceImageInList(&image,merged); + } (void) CloseBlob(image); return(GetFirstImageInList(image)); } @@ -1932,7 +2334,26 @@ static inline ssize_t SetPSDOffset(const { if (psd_info->version == 1) return(WriteBlobMSBShort(image,(unsigned short) offset)); - return(WriteBlobMSBLong(image,(unsigned short) offset)); + return(WriteBlobMSBLong(image,(unsigned int) offset)); +} + +static inline ssize_t WritePSDOffset(const PSDInfo *psd_info,Image *image, + const MagickSizeType size,const MagickSizeType offset) +{ + MagickSizeType + current_offset; + + ssize_t + result; + + current_offset=TellBlob(image); + SeekBlob(image,offset,SEEK_SET); + if (psd_info->version == 1) + result=WriteBlobMSBShort(image,(unsigned short) size); + else + result=WriteBlobMSBLong(image,(unsigned int) size); + SeekBlob(image,current_offset,SEEK_SET); + return(result); } static inline ssize_t SetPSDSize(const PSDInfo *psd_info,Image *image, @@ -1943,6 +2364,25 @@ static inline ssize_t SetPSDSize(const P return(WriteBlobMSBLongLong(image,size)); } +static inline ssize_t WritePSDSize(const PSDInfo *psd_info,Image *image, + const MagickSizeType size,const MagickSizeType offset) +{ + MagickSizeType + current_offset; + + ssize_t + result; + + current_offset=TellBlob(image); + SeekBlob(image,offset,SEEK_SET); + if (psd_info->version == 1) + result=WriteBlobMSBLong(image,(unsigned int) size); + else + result=WriteBlobMSBLongLong(image,size); + SeekBlob(image,current_offset,SEEK_SET); + return(result); +} + static size_t PSDPackbitsEncodeImage(Image *image,const size_t length, const unsigned char *pixels,unsigned char *compact_pixels) { @@ -1967,6 +2407,7 @@ static size_t PSDPackbitsEncodeImage(Ima if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(pixels != (unsigned char *) NULL); + assert(compact_pixels != (unsigned char *) NULL); packbits=(unsigned char *) AcquireQuantumMemory(128UL,sizeof(*packbits)); if (packbits == (unsigned char *) NULL) ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", @@ -2052,48 +2493,36 @@ static size_t PSDPackbitsEncodeImage(Ima return((size_t) (q-compact_pixels)); } -static void WritePackbitsLength(const PSDInfo *psd_info, - const ImageInfo *image_info,Image *image,Image *next_image, - unsigned char *compact_pixels,const QuantumType quantum_type) +static size_t WriteCompressionStart(const PSDInfo *psd_info,Image *image, + const Image *next_image,const ssize_t channels) { - QuantumInfo - *quantum_info; - - register const PixelPacket - *p; - size_t - length, - packet_size; + length; ssize_t + i, y; - unsigned char - *pixels; - - if (next_image->depth > 8) - next_image->depth=16; - packet_size=next_image->depth > 8UL ? 2UL : 1UL; - (void) packet_size; - quantum_info=AcquireQuantumInfo(image_info,image); - pixels=GetQuantumPixels(quantum_info); - for (y=0; y < (ssize_t) next_image->rows; y++) - { - p=GetVirtualPixels(next_image,0,y,next_image->columns,1,&image->exception); - if (p == (const PixelPacket *) NULL) - break; - length=ExportQuantumPixels(next_image,(CacheView *) NULL,quantum_info, - quantum_type,pixels,&image->exception); - length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels); - (void) SetPSDOffset(psd_info,image,length); - } - quantum_info=DestroyQuantumInfo(quantum_info); + if (next_image->compression == RLECompression) + { + length=WriteBlobMSBShort(image,RLE); + for (i=0; i < channels; i++) + for (y=0; y < (ssize_t) next_image->rows; y++) + length+=SetPSDOffset(psd_info,image,0); + } +#ifdef MAGICKCORE_ZLIB_DELEGATE + else if (next_image->compression == ZipCompression) + length=WriteBlobMSBShort(image,ZipWithoutPrediction); +#endif + else + length=WriteBlobMSBShort(image,Raw); + return(length); } -static void WriteOneChannel(const PSDInfo *psd_info,const ImageInfo *image_info, - Image *image,Image *next_image,unsigned char *compact_pixels, - const QuantumType quantum_type,const MagickBooleanType compression_flag) +static size_t WritePSDChannel(const PSDInfo *psd_info, + const ImageInfo *image_info,Image *image,Image *next_image, + const QuantumType quantum_type, unsigned char *compact_pixels, + MagickOffsetType size_offset,const MagickBooleanType separate) { int y; @@ -2111,24 +2540,65 @@ static void WriteOneChannel(const PSDInf i; size_t - length, - packet_size; + count, + length; unsigned char *pixels; - (void) psd_info; - if ((compression_flag != MagickFalse) && - (next_image->compression != RLECompression)) - (void) WriteBlobMSBShort(image,0); +#ifdef MAGICKCORE_ZLIB_DELEGATE + +#define CHUNK 16384 + + int + flush, + level; + + unsigned char + *compressed_pixels; + + z_stream + stream; + + compressed_pixels=(unsigned char *) NULL; + flush=Z_NO_FLUSH; +#endif + count=0; + if (separate != MagickFalse) + { + size_offset=TellBlob(image)+2; + count+=WriteCompressionStart(psd_info,image,next_image,1); + } if (next_image->depth > 8) next_image->depth=16; monochrome=IsMonochromeImage(image,&image->exception) && (image->depth == 1) ? MagickTrue : MagickFalse; - packet_size=next_image->depth > 8UL ? 2UL : 1UL; - (void) packet_size; quantum_info=AcquireQuantumInfo(image_info,next_image); + if (quantum_info == (QuantumInfo *) NULL) + return(0); pixels=GetQuantumPixels(quantum_info); +#ifdef MAGICKCORE_ZLIB_DELEGATE + if (next_image->compression == ZipCompression) + { + compressed_pixels=(unsigned char *) AcquireQuantumMemory(CHUNK, + sizeof(*compressed_pixels)); + if (compressed_pixels == (unsigned char *) NULL) + { + quantum_info=DestroyQuantumInfo(quantum_info); + return(0); + } + ResetMagickMemory(&stream,0,sizeof(stream)); + stream.data_type=Z_BINARY; + level=Z_DEFAULT_COMPRESSION; + if ((image_info->quality > 0 && image_info->quality < 10)) + level=(int) image_info->quality; + if (deflateInit(&stream,level) != Z_OK) + { + quantum_info=DestroyQuantumInfo(quantum_info); + return(0); + } + } +#endif for (y=0; y < (ssize_t) next_image->rows; y++) { p=GetVirtualPixels(next_image,0,y,next_image->columns,1,&image->exception); @@ -2139,149 +2609,219 @@ static void WriteOneChannel(const PSDInf if (monochrome != MagickFalse) for (i=0; i < (ssize_t) length; i++) pixels[i]=(~pixels[i]); - if (next_image->compression != RLECompression) - (void) WriteBlob(image,length,pixels); - else + if (next_image->compression == RLECompression) { length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels); - (void) WriteBlob(image,length,compact_pixels); + count+=WriteBlob(image,length,compact_pixels); + size_offset+=WritePSDOffset(psd_info,image,length,size_offset); } +#ifdef MAGICKCORE_ZLIB_DELEGATE + else if (next_image->compression == ZipCompression) + { + stream.avail_in=(uInt) length; + stream.next_in=(Bytef *) pixels; + if (y == (ssize_t) next_image->rows-1) + flush=Z_FINISH; + do { + stream.avail_out=(uInt) CHUNK; + stream.next_out=(Bytef *) compressed_pixels; + if (deflate(&stream,flush) == Z_STREAM_ERROR) + break; + length=(size_t) CHUNK-stream.avail_out; + if (length > 0) + count+=WriteBlob(image,length,compressed_pixels); + } while (stream.avail_out == 0); + } +#endif + else + count+=WriteBlob(image,length,pixels); } +#ifdef MAGICKCORE_ZLIB_DELEGATE + if (next_image->compression == ZipCompression) + { + (void) deflateEnd(&stream); + compressed_pixels=(unsigned char *) RelinquishMagickMemory( + compressed_pixels); + } +#endif quantum_info=DestroyQuantumInfo(quantum_info); + return(count); } -static MagickBooleanType WriteImageChannels(const PSDInfo *psd_info, +static unsigned char *AcquireCompactPixels(Image *image) +{ + size_t + packet_size; + + unsigned char + *compact_pixels; + + packet_size=image->depth > 8UL ? 2UL : 1UL; + compact_pixels=(unsigned char *) AcquireQuantumMemory((9* + image->columns)+1,packet_size*sizeof(*compact_pixels)); + if (compact_pixels == (unsigned char *) NULL) + { + (void) ThrowMagickException(&image->exception,GetMagickModule(), + ResourceLimitError,"MemoryAllocationFailed","`%s'", + image->filename); + } + return(compact_pixels); +} + +static ssize_t WritePSDChannels(const PSDInfo *psd_info, const ImageInfo *image_info,Image *image,Image *next_image, - const MagickBooleanType separate) + MagickOffsetType size_offset,const MagickBooleanType separate) { - int - i; + Image + *mask; + + MagickOffsetType + rows_offset; size_t channels, - packet_size; + count, + length, + offset_length; unsigned char *compact_pixels; - /* - Write uncompressed pixels as separate planes. - */ - channels=1; - packet_size=next_image->depth > 8UL ? 2UL : 1UL; + count=0; + offset_length=0; + rows_offset=0; compact_pixels=(unsigned char *) NULL; if (next_image->compression == RLECompression) { - compact_pixels=(unsigned char *) AcquireQuantumMemory((9*channels* - next_image->columns)+1,packet_size*sizeof(*compact_pixels)); + compact_pixels=AcquireCompactPixels(next_image); if (compact_pixels == (unsigned char *) NULL) - ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); + return(0); } - i=0; - if (IsGrayImage(next_image,&next_image->exception) != MagickFalse) + channels=1; + if (separate == MagickFalse) { - if (next_image->compression == RLECompression) + if (next_image->storage_class != PseudoClass) { - /* - Packbits compression. - */ - (void) WriteBlobMSBShort(image,1); - WritePackbitsLength(psd_info,image_info,image,next_image, - compact_pixels,GrayQuantum); + if (IsGrayImage(next_image,&next_image->exception) == MagickFalse) + channels=next_image->colorspace == CMYKColorspace ? 4 : 3; if (next_image->matte != MagickFalse) - WritePackbitsLength(psd_info,image_info,image,next_image, - compact_pixels,AlphaQuantum); + channels++; } - WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels, - GrayQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue : - MagickFalse); - if (next_image->matte != MagickFalse) - WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels, - AlphaQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue : - MagickFalse); - (void) SetImageProgress(image,SaveImagesTag,0,1); + rows_offset=TellBlob(image)+2; + count+=WriteCompressionStart(psd_info,image,next_image,channels); + offset_length=(next_image->rows*(psd_info->version == 1 ? 2 : 4)); + } + size_offset+=2; + if (next_image->storage_class == PseudoClass) + { + length=WritePSDChannel(psd_info,image_info,image,next_image, + IndexQuantum,compact_pixels,rows_offset,separate); + if (separate != MagickFalse) + size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2; + else + rows_offset+=offset_length; + count+=length; } else - if (next_image->storage_class == PseudoClass) - { - if (next_image->compression == RLECompression) - { - /* - Packbits compression. - */ - (void) WriteBlobMSBShort(image,1); - WritePackbitsLength(psd_info,image_info,image,next_image, - compact_pixels,IndexQuantum); - if (next_image->matte != MagickFalse) - WritePackbitsLength(psd_info,image_info,image,next_image, - compact_pixels,AlphaQuantum); - } - WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels, - IndexQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue : - MagickFalse); - if (next_image->matte != MagickFalse) - WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels, - AlphaQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue : - MagickFalse); - (void) SetImageProgress(image,SaveImagesTag,0,1); - } - else - { - if (next_image->colorspace == CMYKColorspace) - (void) NegateImage(next_image,MagickFalse); - if (next_image->compression == RLECompression) - { - /* - Packbits compression. - */ - (void) WriteBlobMSBShort(image,1); - WritePackbitsLength(psd_info,image_info,image,next_image, - compact_pixels,RedQuantum); - WritePackbitsLength(psd_info,image_info,image,next_image, - compact_pixels,GreenQuantum); - WritePackbitsLength(psd_info,image_info,image,next_image, - compact_pixels,BlueQuantum); - if (next_image->colorspace == CMYKColorspace) - WritePackbitsLength(psd_info,image_info,image,next_image, - compact_pixels,BlackQuantum); - if (next_image->matte != MagickFalse) - WritePackbitsLength(psd_info,image_info,image,next_image, - compact_pixels,AlphaQuantum); - } - (void) SetImageProgress(image,SaveImagesTag,0,6); - WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels, - RedQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue : - MagickFalse); - (void) SetImageProgress(image,SaveImagesTag,1,6); - WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels, - GreenQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue : - MagickFalse); - (void) SetImageProgress(image,SaveImagesTag,2,6); - WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels, - BlueQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue : - MagickFalse); - (void) SetImageProgress(image,SaveImagesTag,3,6); - if (next_image->colorspace == CMYKColorspace) - WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels, - BlackQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue : - MagickFalse); - (void) SetImageProgress(image,SaveImagesTag,4,6); - if (next_image->matte != MagickFalse) - WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels, - AlphaQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue : - MagickFalse); - (void) SetImageProgress(image,SaveImagesTag,5,6); - if (next_image->colorspace == CMYKColorspace) - (void) NegateImage(next_image,MagickFalse); - } - if (next_image->compression == RLECompression) - compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels); - return(MagickTrue); + { + if (IsGrayImage(next_image,&next_image->exception) != MagickFalse) + { + length=WritePSDChannel(psd_info,image_info,image,next_image, + GrayQuantum,compact_pixels,rows_offset,separate); + if (separate != MagickFalse) + size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2; + else + rows_offset+=offset_length; + count+=length; + } + else + { + if (next_image->colorspace == CMYKColorspace) + (void) NegateImage(next_image,MagickFalse); + + length=WritePSDChannel(psd_info,image_info,image,next_image, + RedQuantum,compact_pixels,rows_offset,separate); + if (separate != MagickFalse) + size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2; + else + rows_offset+=offset_length; + count+=length; + + length=WritePSDChannel(psd_info,image_info,image,next_image, + GreenQuantum,compact_pixels,rows_offset,separate); + if (separate != MagickFalse) + size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2; + else + rows_offset+=offset_length; + count+=length; + + length=WritePSDChannel(psd_info,image_info,image,next_image, + BlueQuantum,compact_pixels,rows_offset,separate); + if (separate != MagickFalse) + size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2; + else + rows_offset+=offset_length; + count+=length; + + if (next_image->colorspace == CMYKColorspace) + { + length=WritePSDChannel(psd_info,image_info,image,next_image, + BlackQuantum,compact_pixels,rows_offset,separate); + if (separate != MagickFalse) + size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2; + else + rows_offset+=offset_length; + count+=length; + } + } + if (next_image->matte != MagickFalse) + { + length=WritePSDChannel(psd_info,image_info,image,next_image, + AlphaQuantum,compact_pixels,rows_offset,separate); + if (separate != MagickFalse) + size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2; + else + rows_offset+=offset_length; + count+=length; + } + } + compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels); + if (next_image->colorspace == CMYKColorspace) + (void) NegateImage(next_image,MagickFalse); + if (separate != MagickFalse) + { + const char + *property; + + property=GetImageArtifact(next_image,"psd:opacity-mask"); + if (property != (const char *) NULL) + { + mask=(Image *) GetImageRegistry(ImageRegistryType,property, + &image->exception); + if (mask != (Image *) NULL) + { + if (mask->compression == RLECompression) + { + compact_pixels=AcquireCompactPixels(mask); + if (compact_pixels == (unsigned char *) NULL) + return(0); + } + length=WritePSDChannel(psd_info,image_info,image,mask, + RedQuantum,compact_pixels,rows_offset,MagickTrue); + (void) WritePSDSize(psd_info,image,length,size_offset); + count+=length; + compact_pixels=(unsigned char *) RelinquishMagickMemory( + compact_pixels); + } + } + } + return(count); } -static void WritePascalString(Image* inImage,const char *inString,int inPad) +static size_t WritePascalString(Image *image,const char *value,size_t padding) { size_t + count, length; register ssize_t @@ -2290,19 +2830,21 @@ static void WritePascalString(Image* inI /* Max length is 255. */ - length=(strlen(inString) > 255UL ) ? 255UL : strlen(inString); + count=0; + length=(strlen(value) > 255UL ) ? 255UL : strlen(value); if (length == 0) - (void) WriteBlobByte(inImage,0); + count+=WriteBlobByte(image,0); else { - (void) WriteBlobByte(inImage,(unsigned char) length); - (void) WriteBlob(inImage, length, (const unsigned char *) inString); + count+=WriteBlobByte(image,(unsigned char) length); + count+=WriteBlob(image,length,(const unsigned char *) value); } length++; - if ((length % inPad) == 0) - return; - for (i=0; i < (ssize_t) (inPad-(length % inPad)); i++) - (void) WriteBlobByte(inImage,0); + if ((length % padding) == 0) + return(count); + for (i=0; i < (ssize_t) (padding-(length % padding)); i++) + count+=WriteBlobByte(image,0); + return(count); } static void WriteResolutionResourceBlock(Image *image) @@ -2314,15 +2856,18 @@ static void WriteResolutionResourceBlock unsigned short units; - x_resolution=65536.0*image->x_resolution+0.5; - y_resolution=65536.0*image->y_resolution+0.5; - units=1; if (image->units == PixelsPerCentimeterResolution) { - x_resolution=2.54*65536.0*image->x_resolution*0.5; + x_resolution=2.54*65536.0*image->x_resolution+0.5; y_resolution=2.54*65536.0*image->y_resolution+0.5; units=2; } + else + { + x_resolution=65536.0*image->x_resolution+0.5; + y_resolution=65536.0*image->y_resolution+0.5; + units=1; + } (void) WriteBlob(image,4,(const unsigned char *) "8BIM"); (void) WriteBlobMSBShort(image,0x03ED); (void) WriteBlobMSBShort(image,0); @@ -2335,6 +2880,17 @@ static void WriteResolutionResourceBlock (void) WriteBlobMSBShort(image,units); /* height unit */ } +static inline size_t WriteChannelSize(const PSDInfo *psd_info,Image *image, + const signed short channel) +{ + size_t + count; + + count=WriteBlobMSBShort(image,channel); + count+=SetPSDSize(psd_info,image,0); + return(count); +} + static void RemoveICCProfileFromResourceBlock(StringInfo *bim_profile) { register const unsigned char @@ -2372,11 +2928,15 @@ static void RemoveICCProfileFromResource p=PushLongPixel(MSBEndian,p,&count); if (id == 0x0000040f) { - if ((q+PSDQuantum(count)+12) < (datum+length-16)) + ssize_t + quantum; + + quantum=PSDQuantum(count)+12; + if ((quantum >= 12) && (quantum < (ssize_t) length)) { - (void) CopyMagickMemory(q,q+PSDQuantum(count)+12,length- - (PSDQuantum(count)+12)-(q-datum)); - SetStringInfoLength(bim_profile,length-(PSDQuantum(count)+12)); + if ((q+quantum < (datum+length-16))) + (void) CopyMagickMemory(q,q+quantum,length-quantum-(q-datum)); + SetStringInfoLength(bim_profile,length-quantum); } break; } @@ -2439,13 +2999,118 @@ static void RemoveResolutionFromResource } } -static MagickBooleanType WritePSDImage(const ImageInfo *image_info,Image *image) +static const StringInfo *GetAdditionalInformation(const ImageInfo *image_info, + Image *image) +{ +#define PSDKeySize 5 +#define PSDAllowedLength 36 + + char + key[PSDKeySize]; + + /* Whitelist of keys from: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ */ + const char + allowed[PSDAllowedLength][PSDKeySize] = { + "blnc", "blwh", "brit", "brst", "clbl", "clrL", "curv", "expA", "FMsk", + "GdFl", "grdm", "hue ", "hue2", "infx", "knko", "lclr", "levl", "lnsr", + "lfx2", "luni", "lrFX", "lspf", "lyid", "lyvr", "mixr", "nvrt", "phfl", + "post", "PtFl", "selc", "shpa", "sn2P", "SoCo", "thrs", "tsly", "vibA" + }, + *option; + + const StringInfo + *info; + + MagickBooleanType + found; + + register size_t + i; + + size_t + remaining_length, + length; + + StringInfo + *profile; + + unsigned char + *p; + + unsigned int + size; + + info=GetImageProfile(image,"psd:additional-info"); + if (info == (const StringInfo *) NULL) + return((const StringInfo *) NULL); + option=GetImageOption(image_info,"psd:additional-info"); + if (LocaleCompare(option,"all") == 0) + return(info); + if (LocaleCompare(option,"selective") != 0) + { + profile=RemoveImageProfile(image,"psd:additional-info"); + return(DestroyStringInfo(profile)); + } + length=GetStringInfoLength(info); + p=GetStringInfoDatum(info); + remaining_length=length; + length=0; + while (remaining_length >= 12) + { + /* skip over signature */ + p+=4; + key[0]=(*p++); + key[1]=(*p++); + key[2]=(*p++); + key[3]=(*p++); + key[4]='\0'; + size=(unsigned int) (*p++) << 24; + size|=(unsigned int) (*p++) << 16; + size|=(unsigned int) (*p++) << 8; + size|=(unsigned int) (*p++); + size=size & 0xffffffff; + remaining_length-=12; + if ((size_t) size > remaining_length) + return((const StringInfo *) NULL); + found=MagickFalse; + for (i=0; i < PSDAllowedLength; i++) + { + if (LocaleNCompare(key,allowed[i],PSDKeySize) != 0) + continue; + + found=MagickTrue; + break; + } + remaining_length-=(size_t) size; + if (found == MagickFalse) + { + if (remaining_length > 0) + p=(unsigned char *) CopyMagickMemory(p-12,p+size,remaining_length); + continue; + } + length+=(size_t) size+12; + p+=size; + } + profile=RemoveImageProfile(image,"psd:additional-info"); + if (length == 0) + return(DestroyStringInfo(profile)); + SetStringInfoLength(profile,(const size_t) length); + SetImageProfile(image,"psd:additional-info",info); + return(profile); +} + +static MagickBooleanType WritePSDImage(const ImageInfo *image_info, + Image *image) { + char + layer_name[MaxTextExtent]; + const char *property; const StringInfo - *icc_profile; + *icc_profile, + *info; Image *base_image, @@ -2454,6 +3119,10 @@ static MagickBooleanType WritePSDImage(c MagickBooleanType status; + MagickOffsetType + *layer_size_offsets, + size_offset; + PSDInfo psd_info; @@ -2461,14 +3130,14 @@ static MagickBooleanType WritePSDImage(c i; size_t - channel_size, - channelLength, layer_count, - layer_info_size, + layer_index, length, + name_length, num_channels, packet_size, - rounded_layer_info_size; + rounded_size, + size; StringInfo *bim_profile; @@ -2496,13 +3165,18 @@ static MagickBooleanType WritePSDImage(c (void) WriteBlobMSBShort(image,psd_info.version); /* version */ for (i=1; i <= 6; i++) (void) WriteBlobByte(image, 0); /* 6 bytes of reserved */ - if (IsGrayImage(image,&image->exception) != MagickFalse) + /* When the image has a color profile it won't be converted to gray scale */ + if ((GetImageProfile(image,"icc") == (StringInfo *) NULL) && + (IsGrayImage(image,&image->exception) != MagickFalse)) num_channels=(image->matte != MagickFalse ? 2UL : 1UL); else - if (image->storage_class == PseudoClass) + if ((image_info->type != TrueColorType) && (image_info->type != + TrueColorMatteType) && (image->storage_class == PseudoClass)) num_channels=(image->matte != MagickFalse ? 2UL : 1UL); else { + if (image->storage_class == PseudoClass) + (void) SetImageStorageClass(image,DirectClass); if (image->colorspace != CMYKColorspace) num_channels=(image->matte != MagickFalse ? 4UL : 3UL); else @@ -2534,8 +3208,7 @@ static MagickBooleanType WritePSDImage(c (image->colorspace != CMYKColorspace)) && (image_info->colorspace != CMYKColorspace)) { - if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) - (void) TransformImageColorspace(image,sRGBColorspace); + (void) TransformImageColorspace(image,sRGBColorspace); (void) WriteBlobMSBShort(image,(unsigned short) (image->storage_class == PseudoClass ? IndexedMode : RGBMode)); } @@ -2606,171 +3279,193 @@ static MagickBooleanType WritePSDImage(c PSDQuantum(GetStringInfoLength(icc_profile))) (void) WriteBlobByte(image,0); } - layer_count=0; - layer_info_size=2; base_image=GetNextImageInList(image); - if ((image->matte != MagickFalse) && (base_image == (Image *) NULL)) + if (base_image == (Image *)NULL) base_image=image; - next_image=base_image; - while ( next_image != NULL ) + size=0; + size_offset=TellBlob(image); + SetPSDSize(&psd_info,image,0); + SetPSDSize(&psd_info,image,0); + layer_count=0; + for (next_image=base_image; next_image != NULL; ) { - packet_size=next_image->depth > 8 ? 2UL : 1UL; - if (IsGrayImage(next_image,&image->exception) != MagickFalse) - num_channels=next_image->matte != MagickFalse ? 2UL : 1UL; + layer_count++; + next_image=GetNextImageInList(next_image); + } + if (image->matte != MagickFalse) + size+=WriteBlobMSBShort(image,-(unsigned short) layer_count); + else + size+=WriteBlobMSBShort(image,(unsigned short) layer_count); + layer_size_offsets=(MagickOffsetType *) AcquireQuantumMemory( + (size_t) layer_count,sizeof(MagickOffsetType)); + if (layer_size_offsets == (MagickOffsetType *) NULL) + ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); + layer_index=0; + for (next_image=base_image; next_image != NULL; ) + { + Image + *mask; + + unsigned char + default_color; + + unsigned short + channels, + total_channels; + + mask=(Image *) NULL; + property=GetImageArtifact(next_image,"psd:opacity-mask"); + default_color=0; + if (property != (const char *) NULL) + { + mask=(Image *) GetImageRegistry(ImageRegistryType,property, + &image->exception); + default_color=strlen(property) == 9 ? 255 : 0; + } + size+=WriteBlobMSBLong(image,(unsigned int) next_image->page.y); + size+=WriteBlobMSBLong(image,(unsigned int) next_image->page.x); + size+=WriteBlobMSBLong(image,(unsigned int) (next_image->page.y+ + next_image->rows)); + size+=WriteBlobMSBLong(image,(unsigned int) (next_image->page.x+ + next_image->columns)); + channels=1U; + if ((next_image->storage_class != PseudoClass) && + (IsGrayImage(next_image,&next_image->exception) == MagickFalse)) + channels=next_image->colorspace == CMYKColorspace ? 4U : 3U; + total_channels=channels; + if (next_image->matte != MagickFalse) + total_channels++; + if (mask != (Image *) NULL) + total_channels++; + size+=WriteBlobMSBShort(image,total_channels); + layer_size_offsets[layer_index++]=TellBlob(image); + for (i=0; i < (ssize_t) channels; i++) + size+=WriteChannelSize(&psd_info,image,(signed short) i); + if (next_image->matte != MagickFalse) + size+=WriteChannelSize(&psd_info,image,-1); + if (mask != (Image *) NULL) + size+=WriteChannelSize(&psd_info,image,-2); + size+=WriteBlob(image,4,(const unsigned char *) "8BIM"); + size+=WriteBlob(image,4,(const unsigned char *) + CompositeOperatorToPSDBlendMode(next_image->compose)); + property=GetImageArtifact(next_image,"psd:layer.opacity"); + if (property != (const char *) NULL) + { + Quantum + opacity; + + opacity=(Quantum) StringToInteger(property); + size+=WriteBlobByte(image,ScaleQuantumToChar(opacity)); + (void) ApplyPSDLayerOpacity(next_image,opacity,MagickTrue, + &image->exception); + } else - if (next_image->storage_class == PseudoClass) - num_channels=next_image->matte != MagickFalse ? 2UL : 1UL; - else - if (next_image->colorspace != CMYKColorspace) - num_channels=next_image->matte != MagickFalse ? 4UL : 3UL; - else - num_channels=next_image->matte != MagickFalse ? 5UL : 4UL; - channelLength=(size_t) (next_image->columns*next_image->rows*packet_size+2); - layer_info_size+=(size_t) (4*4+2+num_channels*6+(psd_info.version == 1 ? 8 : - 16)+4*1+4+num_channels*channelLength); + size+=WriteBlobByte(image,255); + size+=WriteBlobByte(image,0); + size+=WriteBlobByte(image,next_image->compose==NoCompositeOp ? + 1 << 0x02 : 1); /* layer properties - visible, etc. */ + size+=WriteBlobByte(image,0); + info=GetAdditionalInformation(image_info,next_image); property=(const char *) GetImageProperty(next_image,"label"); if (property == (const char *) NULL) - layer_info_size+=16; + { + (void) FormatLocaleString(layer_name,MaxTextExtent,"L%.20g", + (double) layer_index); + property=layer_name; + } + name_length=strlen(property)+1; + if ((name_length % 4) != 0) + name_length+=(4-(name_length % 4)); + if (info != (const StringInfo *) NULL) + name_length+=GetStringInfoLength(info); + name_length+=8; + if (mask != (Image *) NULL) + name_length+=20; + size+=WriteBlobMSBLong(image,(unsigned int) name_length); + if (mask == (Image *) NULL) + size+=WriteBlobMSBLong(image,0); else { - size_t - length; - - length=strlen(property); - layer_info_size+=8+length+(4-(length % 4)); + if (mask->compose != NoCompositeOp) + (void) ApplyPSDOpacityMask(next_image,mask,ScaleCharToQuantum( + default_color),MagickTrue,&image->exception); + mask->page.y+=image->page.y; + mask->page.x+=image->page.x; + size+=WriteBlobMSBLong(image,20); + size+=WriteBlobMSBLong(image,mask->page.y); + size+=WriteBlobMSBLong(image,mask->page.x); + size+=WriteBlobMSBLong(image,(const signed int) mask->rows+ + mask->page.y); + size+=WriteBlobMSBLong(image,(const signed int) mask->columns+ + mask->page.x); + size+=WriteBlobByte(image,default_color); + size+=WriteBlobByte(image,mask->compose == NoCompositeOp ? 2 : 0); + size+=WriteBlobMSBShort(image,0); + } + size+=WriteBlobMSBLong(image,0); + size+=WritePascalString(image,property,4); + if (info != (const StringInfo *) NULL) + size+=WriteBlob(image,GetStringInfoLength(info), + GetStringInfoDatum(info)); + next_image=GetNextImageInList(next_image); + } + /* + Now the image data! + */ + next_image=base_image; + layer_index=0; + while (next_image != NULL) + { + length=WritePSDChannels(&psd_info,image_info,image,next_image, + layer_size_offsets[layer_index++],MagickTrue); + if (length == 0) + { + status=MagickFalse; + break; } - layer_count++; + size+=length; + next_image=GetNextImageInList(next_image); + } + (void) WriteBlobMSBLong(image,0); /* user mask data */ + /* + Remove the opacity mask from the registry + */ + next_image=base_image; + while (next_image != (Image *) NULL) + { + property=GetImageArtifact(next_image,"psd:opacity-mask"); + if (property != (const char *) NULL) + DeleteImageRegistry(property); next_image=GetNextImageInList(next_image); } - if (layer_count == 0) - (void) SetPSDSize(&psd_info,image,0); + /* + Write the total size + */ + size_offset+=WritePSDSize(&psd_info,image,size+ + (psd_info.version == 1 ? 8 : 12),size_offset); + if ((size/2) != ((size+1)/2)) + rounded_size=size+1; else + rounded_size=size; + (void) WritePSDSize(&psd_info,image,rounded_size,size_offset); + layer_size_offsets=(MagickOffsetType *) RelinquishMagickMemory( + layer_size_offsets); + /* + Write composite image. + */ + if (status != MagickFalse) { CompressionType compression; - (void) SetPSDSize(&psd_info,image,layer_info_size+ - (psd_info.version == 1 ? 8 : 16)); - if ((layer_info_size/2) != ((layer_info_size+1)/2)) - rounded_layer_info_size=layer_info_size+1; - else - rounded_layer_info_size=layer_info_size; - (void) SetPSDSize(&psd_info,image,rounded_layer_info_size); - (void) WriteBlobMSBShort(image,(unsigned short) layer_count); - layer_count=1; - compression=base_image->compression; - next_image=base_image; - while (next_image != NULL) - { - next_image->compression=NoCompression; - (void) WriteBlobMSBLong(image,(unsigned int) next_image->page.y); - (void) WriteBlobMSBLong(image,(unsigned int) next_image->page.x); - (void) WriteBlobMSBLong(image,(unsigned int) next_image->page.y+ - next_image->rows); - (void) WriteBlobMSBLong(image,(unsigned int) next_image->page.x+ - next_image->columns); - packet_size=next_image->depth > 8 ? 2UL : 1UL; - channel_size=(unsigned int) ((packet_size*next_image->rows* - next_image->columns)+2); - if ((IsGrayImage(next_image,&image->exception) != MagickFalse) || - (next_image->storage_class == PseudoClass)) - { - (void) WriteBlobMSBShort(image,(unsigned short) - (next_image->matte != MagickFalse ? 2 : 1)); - (void) WriteBlobMSBShort(image,0); - (void) SetPSDSize(&psd_info,image,channel_size); - if (next_image->matte != MagickFalse) - { - (void) WriteBlobMSBShort(image,(unsigned short) -1); - (void) SetPSDSize(&psd_info,image,channel_size); - } - } - else - if (next_image->colorspace != CMYKColorspace) - { - (void) WriteBlobMSBShort(image,(unsigned short) - (next_image->matte != MagickFalse ? 4 : 3)); - (void) WriteBlobMSBShort(image,0); - (void) SetPSDSize(&psd_info,image,channel_size); - (void) WriteBlobMSBShort(image,1); - (void) SetPSDSize(&psd_info,image,channel_size); - (void) WriteBlobMSBShort(image,2); - (void) SetPSDSize(&psd_info,image,channel_size); - if (next_image->matte!= MagickFalse ) - { - (void) WriteBlobMSBShort(image,(unsigned short) -1); - (void) SetPSDSize(&psd_info,image,channel_size); - } - } - else - { - (void) WriteBlobMSBShort(image,(unsigned short) - (next_image->matte ? 5 : 4)); - (void) WriteBlobMSBShort(image,0); - (void) SetPSDSize(&psd_info,image,channel_size); - (void) WriteBlobMSBShort(image,1); - (void) SetPSDSize(&psd_info,image,channel_size); - (void) WriteBlobMSBShort(image,2); - (void) SetPSDSize(&psd_info,image,channel_size); - (void) WriteBlobMSBShort(image,3); - (void) SetPSDSize(&psd_info,image,channel_size); - if (next_image->matte) - { - (void) WriteBlobMSBShort(image,(unsigned short) -1); - (void) SetPSDSize(&psd_info,image,channel_size); - } - } - (void) WriteBlob(image,4,(const unsigned char *) "8BIM"); - (void) WriteBlob(image,4,(const unsigned char *) - CompositeOperatorToPSDBlendMode(next_image->compose)); - (void) WriteBlobByte(image,255); /* layer opacity */ - (void) WriteBlobByte(image,0); - (void) WriteBlobByte(image,1); /* layer propertys - visible, etc. */ - (void) WriteBlobByte(image,0); - property=(const char *) GetImageProperty(next_image,"label"); - if (property == (const char *) NULL) - { - char - layer_name[MaxTextExtent]; - - (void) WriteBlobMSBLong(image,16); - (void) WriteBlobMSBLong(image,0); - (void) WriteBlobMSBLong(image,0); - (void) FormatLocaleString(layer_name,MaxTextExtent,"L%06ld",(long) - layer_count++); - WritePascalString(image,layer_name,4); - } - else - { - size_t - length; - - length=strlen(property); - (void) WriteBlobMSBLong(image,(unsigned int) (length+(4- - (length % 4))+8)); - (void) WriteBlobMSBLong(image,0); - (void) WriteBlobMSBLong(image,0); - WritePascalString(image,property,4); - } - next_image=GetNextImageInList(next_image); - } - /* - Now the image data! - */ - next_image=base_image; - while (next_image != NULL) - { - status=WriteImageChannels(&psd_info,image_info,image,next_image, - MagickTrue); - next_image=GetNextImageInList(next_image); - } - (void) WriteBlobMSBLong(image,0); /* user mask data */ - base_image->compression=compression; + compression=image->compression; + if (image->compression == ZipCompression) + image->compression=RLECompression; + if (WritePSDChannels(&psd_info,image_info,image,image,0, + MagickFalse) == 0) + status=MagickFalse; + image->compression=compression; } - /* - Write composite image. - */ - status=WriteImageChannels(&psd_info,image_info,image,image,MagickFalse); (void) CloseBlob(image); return(status); }
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor