X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=coders%2Fpsd.c;h=4c31d4eecda952d8cbb11aa4750c98fca25354da;hb=89a132cab90466bd6b791de4512b4cae0aa59c43;hp=80df869c69c245ec6e0b372efd32061767dc4850;hpb=25f1e05fffe192443b013a303171b8e7f064a01d;p=imagemagick diff --git a/coders/psd.c b/coders/psd.c index 80df869c6..4c31d4eec 100644 --- a/coders/psd.c +++ b/coders/psd.c @@ -20,13 +20,13 @@ % December 2013 % % % % % -% Copyright 1999-2017 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 % % % -% https://www.imagemagick.org/script/license.php % +% https://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, % @@ -36,6 +36,7 @@ % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % +% Photoshop spec @ https://www.adobe.com/devnet-apps/photoshop/fileformatashtml % */ @@ -116,7 +117,7 @@ typedef enum */ typedef struct _ChannelInfo { - short int + short type; size_t @@ -163,7 +164,7 @@ typedef struct _LayerInfo unsigned char clipping, flags, - name[256], + name[257], visible; unsigned short @@ -240,39 +241,58 @@ static MagickBooleanType IsPSD(const unsigned char *magick,const size_t length) % */ -static const char *CompositeOperatorToPSDBlendMode(CompositeOperator op) +static const char *CompositeOperatorToPSDBlendMode(Image *image) { - const char - *blend_mode; - - switch (op) + switch (image->compose) { - 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 HardMixCompositeOp: blend_mode = "hMix"; 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"; + case ColorBurnCompositeOp: + return(image->endian == LSBEndian ? "vidi" : "idiv"); + case ColorDodgeCompositeOp: + return(image->endian == LSBEndian ? " vid" : "div "); + case ColorizeCompositeOp: + return(image->endian == LSBEndian ? "rloc" : "colr"); + case DarkenCompositeOp: + return(image->endian == LSBEndian ? "krad" : "dark"); + case DifferenceCompositeOp: + return(image->endian == LSBEndian ? "ffid" : "diff"); + case DissolveCompositeOp: + return(image->endian == LSBEndian ? "ssid" : "diss"); + case ExclusionCompositeOp: + return(image->endian == LSBEndian ? "dums" : "smud"); + case HardLightCompositeOp: + return(image->endian == LSBEndian ? "tiLh" : "hLit"); + case HardMixCompositeOp: + return(image->endian == LSBEndian ? "xiMh" : "hMix"); + case HueCompositeOp: + return(image->endian == LSBEndian ? " euh" : "hue "); + case LightenCompositeOp: + return(image->endian == LSBEndian ? "etil" : "lite"); + case LinearBurnCompositeOp: + return(image->endian == LSBEndian ? "nrbl" : "lbrn"); + case LinearDodgeCompositeOp: + return(image->endian == LSBEndian ? "gddl" : "lddg"); + case LinearLightCompositeOp: + return(image->endian == LSBEndian ? "tiLl" : "lLit"); + case LuminizeCompositeOp: + return(image->endian == LSBEndian ? " mul" : "lum "); + case MultiplyCompositeOp: + return(image->endian == LSBEndian ? " lum" : "mul "); + case OverlayCompositeOp: + return(image->endian == LSBEndian ? "revo" : "over"); + case PinLightCompositeOp: + return(image->endian == LSBEndian ? "tiLp" : "pLit"); + case SaturateCompositeOp: + return(image->endian == LSBEndian ? " tas" : "sat "); + case ScreenCompositeOp: + return(image->endian == LSBEndian ? "nrcs" : "scrn"); + case SoftLightCompositeOp: + return(image->endian == LSBEndian ? "tiLs" : "sLit"); + case VividLightCompositeOp: + return(image->endian == LSBEndian ? "tiLv" : "vLit"); + case OverCompositeOp: + default: + return(image->endian == LSBEndian ? "mron" : "norm"); } - return(blend_mode); } /* @@ -299,8 +319,8 @@ static MagickBooleanType CorrectPSDAlphaBlend(const ImageInfo *image_info, return(MagickTrue); status=MagickTrue; #if defined(MAGICKCORE_OPENMP_SUPPORT) -#pragma omp parallel for schedule(static,4) shared(status) \ - magick_threads(image,image,image->rows,1) +#pragma omp parallel for schedule(static) shared(status) \ + magick_number_threads(image,image,image->rows,1) #endif for (y=0; y < (ssize_t) image->rows; y++) { @@ -374,11 +394,12 @@ static MagickBooleanType ApplyPSDLayerOpacity(Image *image,Quantum opacity, " applying layer opacity %.20g", (double) opacity); if (opacity == OpaqueAlpha) return(MagickTrue); - image->alpha_trait=BlendPixelTrait; + if (image->alpha_trait != BlendPixelTrait) + (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception); status=MagickTrue; #if defined(MAGICKCORE_OPENMP_SUPPORT) -#pragma omp parallel for schedule(static,4) shared(status) \ - magick_threads(image,image,image->rows,1) +#pragma omp parallel for schedule(static) shared(status) \ + magick_number_threads(image,image,image->rows,1) #endif for (y=0; y < (ssize_t) image->rows; y++) { @@ -431,12 +452,13 @@ static MagickBooleanType ApplyPSDOpacityMask(Image *image,const Image *mask, if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " applying opacity mask"); - complete_mask=CloneImage(image,image->columns,image->rows,MagickTrue, - exception); + complete_mask=CloneImage(image,0,0,MagickTrue,exception); + if (complete_mask == (Image *) NULL) + return(MagickFalse); complete_mask->alpha_trait=BlendPixelTrait; GetPixelInfo(complete_mask,&color); - color.red=background; - SetImageColor(complete_mask,&color,exception); + color.red=(MagickRealType) background; + (void) SetImageColor(complete_mask,&color,exception); status=CompositeImage(complete_mask,mask,OverCompositeOp,MagickTrue, mask->page.x-image->page.x,mask->page.y-image->page.y,exception); if (status == MagickFalse) @@ -446,8 +468,8 @@ static MagickBooleanType ApplyPSDOpacityMask(Image *image,const Image *mask, } image->alpha_trait=BlendPixelTrait; #if defined(MAGICKCORE_OPENMP_SUPPORT) -#pragma omp parallel for schedule(static,4) shared(status) \ - magick_threads(image,image,image->rows,1) +#pragma omp parallel for schedule(static) shared(status) \ + magick_number_threads(image,image,image->rows,1) #endif for (y=0; y < (ssize_t) image->rows; y++) { @@ -475,7 +497,7 @@ static MagickBooleanType ApplyPSDOpacityMask(Image *image,const Image *mask, alpha, intensity; - alpha=GetPixelAlpha(image,q); + alpha=(MagickRealType) GetPixelAlpha(image,q); intensity=GetPixelIntensity(complete_mask,p); if (revert == MagickFalse) SetPixelAlpha(image,ClampToQuantum(intensity*(QuantumScale*alpha)),q); @@ -509,7 +531,7 @@ static void PreservePSDOpacityMask(Image *image,LayerInfo* layer_info, random_info=AcquireRandomInfo(); key_info=GetRandomKey(random_info,2+1); key=(char *) GetStringInfoDatum(key_info); - key[8]=layer_info->mask.background; + key[8]=(char ) 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; @@ -670,18 +692,17 @@ static inline LayerInfo *DestroyLayerInfo(LayerInfo *layer_info, return (LayerInfo *) RelinquishMagickMemory(layer_info); } -static inline size_t GetPSDPacketSize(Image *image) +static inline size_t GetPSDPacketSize(const Image *image) { if (image->storage_class == PseudoClass) { if (image->colors > 256) return(2); - else if (image->depth > 8) - return(2); } - else - if (image->depth > 8) - return(2); + if (image->depth > 16) + return(4); + if (image->depth > 8) + return(2); return(1); } @@ -732,13 +753,16 @@ static MagickBooleanType NegateCMYK(Image *image,ExceptionInfo *exception) return(status); } -static void ParseImageResourceBlocks(Image *image, +static StringInfo *ParseImageResourceBlocks(Image *image, const unsigned char *blocks,size_t length, MagickBooleanType *has_merged_image,ExceptionInfo *exception) { const unsigned char *p; + ssize_t + offset; + StringInfo *profile; @@ -753,11 +777,10 @@ static void ParseImageResourceBlocks(Image *image, short_sans; if (length < 16) - return; + return((StringInfo *) NULL); profile=BlobToStringInfo((const unsigned char *) NULL,length); SetStringInfoDatum(profile,blocks); - (void) SetImageProfile(image,"8bim",profile,exception); - profile=DestroyStringInfo(profile); + SetStringInfoName(profile,"8bim"); for (p=blocks; (p >= blocks) && (p < (blocks+length-7)); ) { if (LocaleNCompare((const char *) p,"8BIM",4) != 0) @@ -769,10 +792,11 @@ static void ParseImageResourceBlocks(Image *image, name_length++; p+=name_length; if (p > (blocks+length-4)) - return; + break; p=PushLongPixel(MSBEndian,p,&count); - if ((p+count) > (blocks+length)) - return; + offset=(ssize_t) count; + if (((p+offset) < blocks) || ((p+offset) > (blocks+length))) + break; switch (id) { case 0x03ed: @@ -786,8 +810,8 @@ static void ParseImageResourceBlocks(Image *image, /* Resolution info. */ - if (count < 16) - return; + if (offset < 16) + break; p=PushShortPixel(MSBEndian,p,&resolution); image->resolution.x=(double) resolution; (void) FormatLocaleString(value,MagickPathExtent,"%g", @@ -809,21 +833,21 @@ static void ParseImageResourceBlocks(Image *image, } case 0x0421: { - if ((count > 3) && (*(p+4) == 0)) + if ((offset > 4) && (*(p+4) == 0)) *has_merged_image=MagickFalse; - p+=count; + p+=offset; break; } default: { - p+=count; + p+=offset; break; } } - if ((count & 0x01) != 0) + if ((offset & 0x01) != 0) p++; } - return; + return(profile); } static CompositeOperator PSDBlendModeToCompositeOperator(const char *mode) @@ -913,7 +937,7 @@ static inline void SetPSDPixel(Image *image,const size_t channels, SetPixelIndex(image,ScaleQuantumToShort(pixel),q); } color=image->colormap+(ssize_t) ConstrainColormapIndex(image, - GetPixelIndex(image,q),exception); + (ssize_t) GetPixelIndex(image,q),exception); if ((type == 0) && (channels > 1)) return; else @@ -934,11 +958,13 @@ static inline void SetPSDPixel(Image *image,const size_t channels, SetPixelRed(image,pixel,q); break; } + case -3: case 1: { SetPixelGreen(image,pixel,q); break; } + case -4: case 2: { SetPixelBlue(image,pixel,q); @@ -966,7 +992,7 @@ static inline void SetPSDPixel(Image *image,const size_t channels, } static MagickBooleanType ReadPSDChannelPixels(Image *image, - const size_t channels,const size_t row,const ssize_t type, + const size_t channels,const ssize_t row,const ssize_t type, const unsigned char *pixels,ExceptionInfo *exception) { Quantum @@ -984,9 +1010,6 @@ static MagickBooleanType ReadPSDChannelPixels(Image *image, size_t packet_size; - unsigned short - nibble; - p=pixels; q=GetAuthenticPixels(image,0,row,image->columns,1,exception); if (q == (Quantum *) NULL) @@ -996,11 +1019,22 @@ static MagickBooleanType ReadPSDChannelPixels(Image *image, { if (packet_size == 1) pixel=ScaleCharToQuantum(*p++); - else + else if (packet_size == 2) { + unsigned short + nibble; + p=PushShortPixel(MSBEndian,p,&nibble); pixel=ScaleShortToQuantum(nibble); } + else + { + MagickFloatType + nibble; + + p=PushFloatPixel(MSBEndian,p,&nibble); + pixel=ClampToQuantum((MagickRealType)QuantumRange*nibble); + } if (image->depth > 1) { SetPSDPixel(image,channels,type,packet_size,pixel,q,exception); @@ -1012,10 +1046,10 @@ static MagickBooleanType ReadPSDChannelPixels(Image *image, bit, number_bits; - number_bits=image->columns-x; + number_bits=(ssize_t) image->columns-x; if (number_bits > 8) number_bits=8; - for (bit = 0; bit < number_bits; bit++) + for (bit = 0; bit < (ssize_t) number_bits; bit++) { SetPSDPixel(image,channels,type,packet_size,(((unsigned char) pixel) & (0x01 << (7-bit))) != 0 ? 0 : QuantumRange,q,exception); @@ -1037,10 +1071,10 @@ static MagickBooleanType ReadPSDChannelRaw(Image *image,const size_t channels, status; size_t - count, row_size; ssize_t + count, y; unsigned char @@ -1062,8 +1096,11 @@ static MagickBooleanType ReadPSDChannelRaw(Image *image,const size_t channels, status=MagickFalse; count=ReadBlob(image,row_size,pixels); - if (count != row_size) - break; + if (count != (ssize_t) row_size) + { + status=MagickFalse; + break; + } status=ReadPSDChannelPixels(image,channels,y,type,pixels,exception); if (status == MagickFalse) @@ -1130,7 +1167,7 @@ static MagickBooleanType ReadPSDChannelRLE(Image *image,const PSDInfo *psd_info, if ((MagickOffsetType) length < sizes[y]) length=(size_t) sizes[y]; - if (length > row_size + 256) // arbitrary number + if (length > (row_size+2048)) /* arbitrary number */ { pixels=(unsigned char *) RelinquishMagickMemory(pixels); ThrowBinaryException(ResourceLimitError,"InvalidLength",image->filename); @@ -1144,7 +1181,7 @@ static MagickBooleanType ReadPSDChannelRLE(Image *image,const PSDInfo *psd_info, image->filename); } - (void) ResetMagickMemory(compact_pixels,0,length*sizeof(*compact_pixels)); + (void) memset(compact_pixels,0,length*sizeof(*compact_pixels)); status=MagickTrue; for (y=0; y < (ssize_t) image->rows; y++) @@ -1202,6 +1239,9 @@ static MagickBooleanType ReadPSDChannelZip(Image *image,const size_t channels, (void) LogMagickEvent(CoderEvent,GetMagickModule(), " layer data is ZIP compressed"); + if ((MagickSizeType) compact_size > GetBlobSize(image)) + ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile", + image->filename); compact_pixels=(unsigned char *) AcquireQuantumMemory(compact_size, sizeof(*compact_pixels)); if (compact_pixels == (unsigned char *) NULL) @@ -1221,12 +1261,13 @@ static MagickBooleanType ReadPSDChannelZip(Image *image,const size_t channels, } 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(stream)); + memset(&stream,0,sizeof(stream)); stream.data_type=Z_BINARY; stream.next_in=(Bytef *)compact_pixels; stream.avail_in=(uInt) compact_size; @@ -1249,31 +1290,39 @@ static MagickBooleanType ReadPSDChannelZip(Image *image,const size_t channels, pixels=(unsigned char *) RelinquishMagickMemory(pixels); return(MagickFalse); } + if (ret == Z_STREAM_END) + break; } (void) inflateEnd(&stream); } if (compression == ZipWithPrediction) - { - p=pixels; - while (count > 0) - { - length=image->columns; - while (--length) - { - if (packet_size == 2) - { - p[2]+=p[0]+((p[1]+p[3]) >> 8); - p[3]+=p[1]; - } - else - *(p+1)+=*p; - p+=packet_size; - } - p+=packet_size; - count-=row_size; - } - } + { + p=pixels; + while (count > 0) + { + length=image->columns; + while (--length) + { + if (packet_size == 2) + { + p[2]+=p[0]+((p[1]+p[3]) >> 8); + p[3]+=p[1]; + } + /* + else if (packet_size == 4) + { + TODO: Figure out what to do there. + } + */ + else + *(p+1)+=*p; + p+=packet_size; + } + p+=packet_size; + count-=row_size; + } + } status=MagickTrue; p=pixels; @@ -1323,26 +1372,27 @@ static MagickBooleanType ReadPSDChannel(Image *image, 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); - } + { + (void) SeekBlob(image,(MagickOffsetType) + 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) { - SetImageType(mask,GrayscaleType,exception); + (void) SetImageType(mask,GrayscaleType,exception); channel_image=mask; } } offset=TellBlob(image); - status=MagickTrue; + status=MagickFalse; switch(compression) { case Raw: status=ReadPSDChannelRaw(channel_image,psd_info->channels, - layer_info->channel_info[channel].type,exception); + (ssize_t) layer_info->channel_info[channel].type,exception); break; case RLE: { @@ -1354,7 +1404,7 @@ static MagickBooleanType ReadPSDChannel(Image *image, ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", image->filename); status=ReadPSDChannelRLE(channel_image,psd_info, - layer_info->channel_info[channel].type,sizes,exception); + (ssize_t) layer_info->channel_info[channel].type,sizes,exception); sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes); } break; @@ -1362,7 +1412,7 @@ static MagickBooleanType ReadPSDChannel(Image *image, case ZipWithoutPrediction: #ifdef MAGICKCORE_ZLIB_DELEGATE status=ReadPSDChannelZip(channel_image,layer_info->channels, - layer_info->channel_info[channel].type,compression, + (ssize_t) layer_info->channel_info[channel].type,compression, layer_info->channel_info[channel].size-2,exception); #else (void) ThrowMagickException(exception,GetMagickModule(), @@ -1376,15 +1426,21 @@ static MagickBooleanType ReadPSDChannel(Image *image, break; } - SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET); + (void) SeekBlob(image,offset+layer_info->channel_info[channel].size-2, + SEEK_SET); if (status == MagickFalse) { if (mask != (Image *) NULL) - DestroyImage(mask); + (void) DestroyImage(mask); ThrowBinaryException(CoderError,"UnableToDecompressImage", image->filename); } - layer_info->mask.image=mask; + if (mask != (Image *) NULL) + { + if (layer_info->mask.image != (Image *) NULL) + layer_info->mask.image=DestroyImage(layer_info->mask.image); + layer_info->mask.image=mask; + } return(status); } @@ -1412,11 +1468,6 @@ static MagickBooleanType ReadPSDLayer(Image *image,const ImageInfo *image_info, layer_info->blendkey); if (layer_info->visible == MagickFalse) layer_info->image->compose=NoCompositeOp; - if (psd_info->mode == CMYKMode) - SetImageColorspace(layer_info->image,CMYKColorspace,exception); - else if ((psd_info->mode == BitmapMode) || (psd_info->mode == DuotoneMode) || - (psd_info->mode == GrayscaleMode)) - SetImageColorspace(layer_info->image,GRAYColorspace,exception); /* Set up some hidden attributes for folks that need them. */ @@ -1440,12 +1491,21 @@ static MagickBooleanType ReadPSDLayer(Image *image,const ImageInfo *image_info, " reading data for channel %.20g",(double) j); compression=(PSDCompressionType) ReadBlobShort(layer_info->image); + + /* TODO: Remove this when we figure out how to support this */ + if ((compression == ZipWithPrediction) && (image->depth == 32)) + { + (void) ThrowMagickException(exception,GetMagickModule(), + TypeError,"CompressionNotSupported","ZipWithPrediction(32 bit)"); + return(MagickFalse); + } + layer_info->image->compression=ConvertPSDCompression(compression); if (layer_info->channel_info[j].type == -1) layer_info->image->alpha_trait=BlendPixelTrait; - status=ReadPSDChannel(layer_info->image,image_info,psd_info,layer_info,j, - compression,exception); + status=ReadPSDChannel(layer_info->image,image_info,psd_info,layer_info, + (size_t) j,compression,exception); if (status == MagickFalse) break; @@ -1482,6 +1542,101 @@ static MagickBooleanType ReadPSDLayer(Image *image,const ImageInfo *image_info, return(status); } +static MagickBooleanType CheckPSDChannels(const PSDInfo *psd_info, + LayerInfo *layer_info) +{ + int + channel_type; + + register ssize_t + i; + + if (layer_info->channels < psd_info->min_channels) + return(MagickFalse); + channel_type=RedChannel; + if (psd_info->min_channels >= 3) + channel_type|=(GreenChannel | BlueChannel); + if (psd_info->min_channels >= 4) + channel_type|=BlackChannel; + for (i=0; i < (ssize_t) layer_info->channels; i++) + { + short + type; + + type=layer_info->channel_info[i].type; + if (type == -1) + { + channel_type|=AlphaChannel; + continue; + } + if (type < -1) + continue; + if (type == 0) + channel_type&=~RedChannel; + else if (type == 1) + channel_type&=~GreenChannel; + else if (type == 2) + channel_type&=~BlueChannel; + else if (type == 3) + channel_type&=~BlackChannel; + } + if (channel_type == 0) + return(MagickTrue); + if ((channel_type == AlphaChannel) && + (layer_info->channels >= psd_info->min_channels + 1)) + return(MagickTrue); + return(MagickFalse); +} + +static void AttachPSDLayers(Image *image,LayerInfo *layer_info, + ssize_t number_layers) +{ + register ssize_t + i; + + ssize_t + j; + + for (i=0; i < 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) + { + layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info); + return; + } + 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; + layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info); +} + +static inline MagickBooleanType PSDSkipImage(const ImageInfo *image_info, + const size_t index) +{ + if (image_info->number_scenes == 0) + return(MagickFalse); + if (index < image_info->scene) + return(MagickTrue); + if (index > image_info->scene+image_info->number_scenes-1) + return(MagickTrue); + return(MagickFalse); +} + static MagickBooleanType ReadPSDLayersInternal(Image *image, const ImageInfo *image_info,const PSDInfo *psd_info, const MagickBooleanType skip_layers,ExceptionInfo *exception) @@ -1514,357 +1669,345 @@ static MagickBooleanType ReadPSDLayersInternal(Image *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 (count == 4) + ReversePSDString(image,type,(size_t) count); + if ((count != 4) || (LocaleNCompare(type,"8BIM",4) != 0)) return(MagickTrue); else { count=ReadBlob(image,4,(unsigned char *) type); - ReversePSDString(image,type,4); - if ((count != 0) && (LocaleNCompare(type,"Lr16",4) == 0)) + if (count == 4) + ReversePSDString(image,type,4); + if ((count == 4) && ((LocaleNCompare(type,"Lr16",4) == 0) || + (LocaleNCompare(type,"Lr32",4) == 0))) size=GetPSDSize(psd_info,image); else return(MagickTrue); } } - status=MagickTrue; - if (size != 0) - { - layer_info=(LayerInfo *) NULL; - number_layers=(short) ReadBlobShort(image); + if (size == 0) + return(MagickTrue); - if (number_layers < 0) - { - /* - 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"); - image->alpha_trait=BlendPixelTrait; - } + layer_info=(LayerInfo *) NULL; + number_layers=(ssize_t) ReadBlobSignedShort(image); + if (number_layers < 0) + { /* - We only need to know if the image has an alpha channel + The first alpha channel in the merged result contains the + transparency data for the merged result. */ - if (skip_layers != MagickFalse) - return(MagickTrue); + number_layers=MagickAbsoluteValue(number_layers); + if (image->debug != MagickFalse) + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " negative layer count corrected for"); + image->alpha_trait=BlendPixelTrait; + } + + /* + 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); + + if (number_layers == 0) + ThrowBinaryException(CorruptImageError,"InvalidNumberOfLayers", + image->filename); + layer_info=(LayerInfo *) AcquireQuantumMemory((size_t) number_layers, + sizeof(*layer_info)); + if (layer_info == (LayerInfo *) NULL) + { if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " image contains %.20g layers",(double) number_layers); + " allocation of LayerInfo failed"); + ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", + image->filename); + } + (void) memset(layer_info,0,(size_t) number_layers*sizeof(*layer_info)); - if (number_layers == 0) - ThrowBinaryException(CorruptImageError,"InvalidNumberOfLayers", - image->filename); + for (i=0; i < number_layers; i++) + { + ssize_t + x, + y; - layer_info=(LayerInfo *) AcquireQuantumMemory((size_t) number_layers, - sizeof(*layer_info)); - if (layer_info == (LayerInfo *) NULL) + if (image->debug != MagickFalse) + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " reading layer #%.20g",(double) i+1); + layer_info[i].page.y=(ssize_t) ReadBlobSignedLong(image); + layer_info[i].page.x=(ssize_t) ReadBlobSignedLong(image); + y=(ssize_t) ReadBlobSignedLong(image); + x=(ssize_t) 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); + } + if (image->debug != MagickFalse) + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " offset(%.20g,%.20g), size(%.20g,%.20g), channels=%.20g", + (double) layer_info[i].page.x,(double) layer_info[i].page.y, + (double) layer_info[i].page.height,(double) + 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) ReadBlobShort(image); + if ((layer_info[i].channel_info[j].type < -4) || + (layer_info[i].channel_info[j].type > 4)) { - if (image->debug != MagickFalse) - (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " allocation of LayerInfo failed"); - ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", + layer_info=DestroyLayerInfo(layer_info,number_layers); + ThrowBinaryException(CorruptImageError,"NoSuchImageChannel", image->filename); } - (void) ResetMagickMemory(layer_info,0,(size_t) number_layers* - sizeof(*layer_info)); - - for (i=0; i < number_layers; i++) + layer_info[i].channel_info[j].size=(size_t) GetPSDSize(psd_info, + image); + if (image->debug != MagickFalse) + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " channel[%.20g]: type=%.20g, size=%.20g",(double) j, + (double) layer_info[i].channel_info[j].type, + (double) layer_info[i].channel_info[j].size); + } + if (CheckPSDChannels(psd_info,&layer_info[i]) == MagickFalse) + { + layer_info=DestroyLayerInfo(layer_info,number_layers); + ThrowBinaryException(CorruptImageError,"ImproperImageHeader", + image->filename); + } + count=ReadBlob(image,4,(unsigned char *) type); + if (count == 4) + ReversePSDString(image,type,4); + if ((count != 4) || (LocaleNCompare(type,"8BIM",4) != 0)) { - ssize_t - x, - y; - - if (image->debug != MagickFalse) - (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " reading layer #%.20g",(double) i+1); - 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); - } if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " offset(%.20g,%.20g), size(%.20g,%.20g), channels=%.20g", - (double) layer_info[i].page.x,(double) layer_info[i].page.y, - (double) layer_info[i].page.height,(double) - 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) ReadBlobShort(image); - layer_info[i].channel_info[j].size=(size_t) GetPSDSize(psd_info, - image); - if (image->debug != MagickFalse) - (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " channel[%.20g]: type=%.20g, size=%.20g",(double) j, - (double) layer_info[i].channel_info[j].type, - (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) - (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " layer type was %.4s instead of 8BIM", type); - layer_info=DestroyLayerInfo(layer_info,number_layers); - ThrowBinaryException(CorruptImageError,"ImproperImageHeader", - image->filename); - } - count=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); + " layer type was %.4s instead of 8BIM", type); + layer_info=DestroyLayerInfo(layer_info,number_layers); + ThrowBinaryException(CorruptImageError,"ImproperImageHeader", + image->filename); + } + count=ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey); + if (count != 4) + { + layer_info=DestroyLayerInfo(layer_info,number_layers); + ThrowBinaryException(CorruptImageError,"ImproperImageHeader", + image->filename); + } + 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); + if (image->debug != MagickFalse) + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " blend=%.4s, opacity=%.20g, clipping=%s, flags=%d, visible=%s", + layer_info[i].blendkey,(double) layer_info[i].opacity, + layer_info[i].clipping ? "true" : "false",layer_info[i].flags, + layer_info[i].visible ? "true" : "false"); + (void) ReadBlobByte(image); /* filler */ + + size=ReadBlobLong(image); + if (size != 0) + { + MagickSizeType + combined_length, + length; + if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " blend=%.4s, opacity=%.20g, clipping=%s, flags=%d, visible=%s", - layer_info[i].blendkey,(double) layer_info[i].opacity, - layer_info[i].clipping ? "true" : "false",layer_info[i].flags, - layer_info[i].visible ? "true" : "false"); - (void) ReadBlobByte(image); /* filler */ - - size=ReadBlobLong(image); - if (size != 0) + " layer contains additional info"); + length=ReadBlobLong(image); + combined_length=length+4; + if (length != 0) { - MagickSizeType - combined_length, - length; - - if (image->debug != MagickFalse) - (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " layer contains additional info"); - length=ReadBlobLong(image); - combined_length=length+4; - if (length != 0) - { - /* - Layer mask info. - */ - 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.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,(MagickSizeType) (length-18)) == MagickFalse) - { - layer_info=DestroyLayerInfo(layer_info,number_layers); - ThrowBinaryException(CorruptImageError, - "UnexpectedEndOfFile",image->filename); - } - } - length=ReadBlobLong(image); - combined_length+=length+4; - if (length != 0) - { - /* - Layer blending ranges info. - */ - if (image->debug != MagickFalse) - (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " layer blending ranges: length=%.20g",(double) - ((MagickOffsetType) length)); - /* - We read it, but don't use it... - */ - for (j=0; j < (ssize_t) length; j+=8) - { - size_t blend_source=ReadBlobLong(image); - size_t blend_dest=ReadBlobLong(image); - if (image->debug != MagickFalse) - (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " source(%x), dest(%x)",(unsigned int) - blend_source,(unsigned int) blend_dest); - } - } /* - Layer name. + Layer mask info. */ - length=(MagickSizeType) (unsigned char) ReadBlobByte(image); - combined_length+=length+1; - 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); - if ((length % 4) != 0) + layer_info[i].mask.page.y=(ssize_t) ReadBlobSignedLong(image); + layer_info[i].mask.page.x=(ssize_t) 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)) { - 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); - } + 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; } - length=(MagickSizeType) size-combined_length; - if (length > 0) + if (image->debug != MagickFalse) + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " layer mask: offset(%.20g,%.20g), size(%.20g,%.20g), length=%.20g", + (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,(MagickSizeType) (length-18)) == MagickFalse) { - 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); + layer_info=DestroyLayerInfo(layer_info,number_layers); + ThrowBinaryException(CorruptImageError, + "UnexpectedEndOfFile",image->filename); } } - } - - for (i=0; i < number_layers; i++) - { - if ((layer_info[i].page.width == 0) || - (layer_info[i].page.height == 0)) + length=ReadBlobLong(image); + combined_length+=length+4; + if (length != 0) { + /* + Layer blending ranges info. + */ 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; + " layer blending ranges: length=%.20g",(double) + ((MagickOffsetType) length)); + if (DiscardBlobBytes(image,length) == MagickFalse) + { + layer_info=DestroyLayerInfo(layer_info,number_layers); + ThrowBinaryException(CorruptImageError, + "UnexpectedEndOfFile",image->filename); + } } - /* - Allocate layered image. + Layer name. */ - layer_info[i].image=CloneImage(image,layer_info[i].page.width, - layer_info[i].page.height,MagickFalse,exception); - if (layer_info[i].image == (Image *) NULL) - { - layer_info=DestroyLayerInfo(layer_info,number_layers); - if (image->debug != MagickFalse) - (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " allocation of image for layer %.20g failed",(double) i); - 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,exception); - layer_info[i].info=DestroyStringInfo(layer_info[i].info); - } - } - - if (image_info->ping == MagickFalse) - { - for (i=0; i < number_layers; i++) + length=(MagickSizeType) (unsigned char) ReadBlobByte(image); + combined_length+=length+1; + 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); + if ((length % 4) != 0) { - if (layer_info[i].image == (Image *) NULL) + length=4-(length % 4); + combined_length+=length; + /* Skip over the padding of the layer name */ + if (DiscardBlobBytes(image,length) == MagickFalse) { - 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; + layer_info=DestroyLayerInfo(layer_info,number_layers); + ThrowBinaryException(CorruptImageError, + "UnexpectedEndOfFile",image->filename); } - - 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; } - } - - if (status != MagickFalse) - { - for (i=0; i < number_layers; i++) + length=(MagickSizeType) size-combined_length; + if (length > 0) { - if (layer_info[i].image == (Image *) NULL) + unsigned char + *info; + + if (length > GetBlobSize(image)) { - for (j=i; j < number_layers - 1; j++) - layer_info[j] = layer_info[j+1]; - number_layers--; - i--; + 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); } + } + } - if (number_layers > 0) - { - 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; - } - layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info); - } - else - layer_info=DestroyLayerInfo(layer_info,number_layers); - } + for (i=0; i < number_layers; i++) + { + 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; + } + + /* + Allocate layered image. + */ + layer_info[i].image=CloneImage(image,layer_info[i].page.width, + layer_info[i].page.height,MagickFalse,exception); + if (layer_info[i].image == (Image *) NULL) + { + layer_info=DestroyLayerInfo(layer_info,number_layers); + if (image->debug != MagickFalse) + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " allocation of image for layer %.20g failed",(double) i); + 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,exception); + layer_info[i].info=DestroyStringInfo(layer_info[i].info); + } + } + if (image_info->ping != MagickFalse) + { + AttachPSDLayers(image,layer_info,number_layers); + return(MagickTrue); + } + status=MagickTrue; + for (i=0; i < number_layers; i++) + { + if ((layer_info[i].image == (Image *) NULL) || + (PSDSkipImage(image_info,i) != MagickFalse)) + { + for (j=0; j < (ssize_t) 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; + } + + 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,(MagickOffsetType) i, + (MagickSizeType) number_layers); + if (status == MagickFalse) + break; + } + + if (status != MagickFalse) + AttachPSDLayers(image,layer_info,number_layers); + else + layer_info=DestroyLayerInfo(layer_info,number_layers); return(status); } ModuleExport MagickBooleanType ReadPSDLayers(Image *image, - const ImageInfo *image_info,const PSDInfo *psd_info, - const MagickBooleanType skip_layers,ExceptionInfo *exception) + const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception) { PolicyDomain domain; @@ -1875,8 +2018,8 @@ ModuleExport MagickBooleanType ReadPSDLayers(Image *image, domain=CoderPolicyDomain; rights=ReadPolicyRights; if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse) - return(MagickFalse); - return(ReadPSDLayersInternal(image,image_info,psd_info,skip_layers, + return(MagickTrue); + return(ReadPSDLayersInternal(image,image_info,psd_info,MagickFalse, exception)); } @@ -1895,6 +2038,8 @@ static MagickBooleanType ReadPSDMergedImage(const ImageInfo *image_info, register ssize_t i; + if ((image_info->number_scenes != 0) && (image_info->scene != 0)) + return(MagickTrue); compression=(PSDCompressionType) ReadBlobMSBShort(image); image->compression=ConvertPSDCompression(compression); @@ -1917,14 +2062,22 @@ static MagickBooleanType ReadPSDMergedImage(const ImageInfo *image_info, status=MagickTrue; for (i=0; i < (ssize_t) psd_info->channels; i++) { + ssize_t + type; + + type=i; + if ((type == 1) && (psd_info->channels == 2)) + type=-1; + if (compression == RLE) - status=ReadPSDChannelRLE(image,psd_info,i,sizes+(i*image->rows), + status=ReadPSDChannelRLE(image,psd_info,type,sizes+(i*image->rows), exception); else - status=ReadPSDChannelRaw(image,psd_info->channels,i,exception); + status=ReadPSDChannelRaw(image,psd_info->channels,type,exception); if (status != MagickFalse) - status=SetImageProgress(image,LoadImagesTag,i,psd_info->channels); + status=SetImageProgress(image,LoadImagesTag,(MagickOffsetType) i, + psd_info->channels); if (status == MagickFalse) break; @@ -1965,11 +2118,14 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception) register ssize_t i; + size_t + imageListLength; + ssize_t count; - unsigned char - *data; + StringInfo + *profile; /* Open image file. @@ -1995,11 +2151,13 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception) 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) || + if ((count != 4) || (LocaleNCompare(psd_info.signature,"8BPS",4) != 0) || ((psd_info.version != 1) && (psd_info.version != 2))) ThrowReaderException(CorruptImageError,"ImproperImageHeader"); (void) ReadBlob(image,6,psd_info.reserved); psd_info.channels=ReadBlobMSBShort(image); + if (psd_info.channels < 1) + ThrowReaderException(CorruptImageError,"MissingImageChannel"); if (psd_info.channels > MaxPSDChannels) ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded"); psd_info.rows=ReadBlobMSBLong(image); @@ -2008,15 +2166,20 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception) (psd_info.columns > 30000))) ThrowReaderException(CorruptImageError,"ImproperImageHeader"); psd_info.depth=ReadBlobMSBShort(image); - if ((psd_info.depth != 1) && (psd_info.depth != 8) && (psd_info.depth != 16)) + if ((psd_info.depth != 1) && (psd_info.depth != 8) && + (psd_info.depth != 16) && (psd_info.depth != 32)) ThrowReaderException(CorruptImageError,"ImproperImageHeader"); psd_info.mode=ReadBlobMSBShort(image); + if ((psd_info.mode == IndexedMode) && (psd_info.channels > 3)) + ThrowReaderException(CorruptImageError,"ImproperImageHeader"); if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " Image is %.20g x %.20g with channels=%.20g, depth=%.20g, mode=%s", (double) psd_info.columns,(double) psd_info.rows,(double) psd_info.channels,(double) psd_info.depth,ModeToString((PSDImageType) psd_info.mode)); + if (EOFBlob(image) != MagickFalse) + ThrowReaderException(CorruptImageError,"ImproperImageHeader"); /* Initialize image. */ @@ -2026,56 +2189,53 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception) status=SetImageExtent(image,image->columns,image->rows,exception); if (status == MagickFalse) return(DestroyImageList(image)); - if (SetImageBackgroundColor(image,exception) == MagickFalse) - { - image=DestroyImageList(image); - return((Image *) NULL); - } + status=ResetImagePixels(image,exception); + if (status == MagickFalse) + return(DestroyImageList(image)); + psd_info.min_channels=3; if (psd_info.mode == LabMode) - SetImageColorspace(image,LabColorspace,exception); + (void) SetImageColorspace(image,LabColorspace,exception); if (psd_info.mode == CMYKMode) { - SetImageColorspace(image,CMYKColorspace,exception); - if (psd_info.channels > 4) - SetImageAlphaChannel(image,ActivateAlphaChannel,exception); + psd_info.min_channels=4; + (void) SetImageColorspace(image,CMYKColorspace,exception); } else if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) || (psd_info.mode == DuotoneMode)) { - status=AcquireImageColormap(image,psd_info.depth != 16 ? 256 : 65536, - exception); - if (status == MagickFalse) - ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); - if (image->debug != MagickFalse) - (void) LogMagickEvent(CoderEvent,GetMagickModule(), - " Image colormap allocated"); - SetImageColorspace(image,GRAYColorspace,exception); - if (psd_info.channels > 1) - SetImageAlphaChannel(image,ActivateAlphaChannel,exception); + if (psd_info.depth != 32) + { + status=AcquireImageColormap(image,(size_t) (psd_info.depth < 16 ? + 256 : 65536),exception); + if (status == MagickFalse) + ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); + if (image->debug != MagickFalse) + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + " Image colormap allocated"); + } + psd_info.min_channels=1; + (void) SetImageColorspace(image,GRAYColorspace,exception); } - else - if (psd_info.channels > 3) - SetImageAlphaChannel(image,ActivateAlphaChannel,exception); + if (psd_info.channels < psd_info.min_channels) + ThrowReaderException(CorruptImageError,"ImproperImageHeader"); /* Read PSD raster colormap only present for indexed and duotone images. */ length=ReadBlobMSBLong(image); + if ((psd_info.mode == IndexedMode) && (length < 3)) + ThrowReaderException(CorruptImageError,"ImproperImageHeader"); if (length != 0) { if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " reading colormap"); - if (psd_info.mode == DuotoneMode) + if ((psd_info.mode == DuotoneMode) || (psd_info.depth == 32)) { /* Duotone image data; the format of this data is undocumented. + 32 bits per pixel; the colormap is ignored. */ - data=(unsigned char *) AcquireQuantumMemory((size_t) length, - sizeof(*data)); - if (data == (unsigned char *) NULL) - ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); - (void) ReadBlob(image,(size_t) length,data); - data=(unsigned char *) RelinquishMagickMemory(data); + (void) SeekBlob(image,(const MagickOffsetType) length,SEEK_CUR); } else { @@ -2085,26 +2245,27 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception) /* Read PSD raster colormap. */ - number_colors=length/3; + number_colors=(size_t) length/3; if (number_colors > 65536) ThrowReaderException(CorruptImageError,"ImproperImageHeader"); if (AcquireImageColormap(image,number_colors,exception) == MagickFalse) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); for (i=0; i < (ssize_t) image->colors; i++) - image->colormap[i].red=ScaleCharToQuantum((unsigned char) - ReadBlobByte(image)); + image->colormap[i].red=(MagickRealType) ScaleCharToQuantum( + (unsigned char) ReadBlobByte(image)); for (i=0; i < (ssize_t) image->colors; i++) - image->colormap[i].green=ScaleCharToQuantum((unsigned char) - ReadBlobByte(image)); + image->colormap[i].green=(MagickRealType) ScaleCharToQuantum( + (unsigned char) ReadBlobByte(image)); for (i=0; i < (ssize_t) image->colors; i++) - image->colormap[i].blue=ScaleCharToQuantum((unsigned char) - ReadBlobByte(image)); + image->colormap[i].blue=(MagickRealType) ScaleCharToQuantum( + (unsigned char) ReadBlobByte(image)); image->alpha_trait=UndefinedPixelTrait; } } if ((image->depth == 1) && (image->storage_class != PseudoClass)) ThrowReaderException(CorruptImageError, "ImproperImageHeader"); has_merged_image=MagickTrue; + profile=(StringInfo *) NULL; length=ReadBlobMSBLong(image); if (length != 0) { @@ -2131,8 +2292,8 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception) blocks=(unsigned char *) RelinquishMagickMemory(blocks); ThrowReaderException(CorruptImageError,"ImproperImageHeader"); } - ParseImageResourceBlocks(image,blocks,(size_t) length,&has_merged_image, - exception); + profile=ParseImageResourceBlocks(image,blocks,(size_t) length, + &has_merged_image,exception); blocks=(unsigned char *) RelinquishMagickMemory(blocks); } /* @@ -2165,6 +2326,8 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception) if (ReadPSDLayersInternal(image,image_info,&psd_info,skip_layers, exception) != MagickTrue) { + if (profile != (StringInfo *) NULL) + profile=DestroyStringInfo(profile); (void) CloseBlob(image); image=DestroyImageList(image); return((Image *) NULL); @@ -2173,13 +2336,21 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception) /* Skip the rest of the layer and mask information. */ - SeekBlob(image,offset+length,SEEK_SET); + (void) SeekBlob(image,offset+length,SEEK_SET); } /* If we are only "pinging" the image, then we're done - so return. */ + if (EOFBlob(image) != MagickFalse) + { + if (profile != (StringInfo *) NULL) + profile=DestroyStringInfo(profile); + ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile"); + } if (image_info->ping != MagickFalse) { + if (profile != (StringInfo *) NULL) + profile=DestroyStringInfo(profile); (void) CloseBlob(image); return(GetFirstImageInList(image)); } @@ -2189,17 +2360,20 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception) if (image->debug != MagickFalse) (void) LogMagickEvent(CoderEvent,GetMagickModule(), " reading the precombined layer"); - if ((has_merged_image != MagickFalse) || (GetImageListLength(image) == 1)) + imageListLength=GetImageListLength(image); + if ((has_merged_image != MagickFalse) || (imageListLength == 1)) has_merged_image=(MagickBooleanType) ReadPSDMergedImage(image_info,image, &psd_info,exception); - if ((has_merged_image == MagickFalse) && (GetImageListLength(image) == 1) && + if ((has_merged_image == MagickFalse) && (imageListLength == 1) && (length != 0)) { - SeekBlob(image,offset,SEEK_SET); + (void) SeekBlob(image,offset,SEEK_SET); status=ReadPSDLayersInternal(image,image_info,&psd_info,MagickFalse, exception); if (status != MagickTrue) { + if (profile != (StringInfo *) NULL) + profile=DestroyStringInfo(profile); (void) CloseBlob(image); image=DestroyImageList(image); return((Image *) NULL); @@ -2210,14 +2384,34 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception) Image *merged; - if (GetImageListLength(image) == 1) - ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); - SetImageAlphaChannel(image,TransparentAlphaChannel,exception); - image->background_color.alpha=TransparentAlpha; + if (imageListLength == 1) + { + if (profile != (StringInfo *) NULL) + profile=DestroyStringInfo(profile); + ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); + } + image->background_color.alpha=(MagickRealType) TransparentAlpha; image->background_color.alpha_trait=BlendPixelTrait; + (void) SetImageBackgroundColor(image,exception); merged=MergeImageLayers(image,FlattenLayer,exception); ReplaceImageInList(&image,merged); } + if (profile != (StringInfo *) NULL) + { + Image + *next; + + i=0; + next=image; + while (next != (Image *) NULL) + { + if (PSDSkipImage(image_info,i++) == MagickFalse) + (void) SetImageProfile(next,GetStringInfoName(profile),profile, + exception); + next=next->next; + } + profile=DestroyStringInfo(profile); + } (void) CloseBlob(image); return(GetFirstImageInList(image)); } @@ -2325,25 +2519,25 @@ static inline ssize_t SetPSDOffset(const PSDInfo *psd_info,Image *image, { 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) + const MagickSizeType size,const MagickOffsetType offset) { - MagickSizeType + MagickOffsetType current_offset; ssize_t result; current_offset=TellBlob(image); - SeekBlob(image,offset,SEEK_SET); + (void) SeekBlob(image,offset,SEEK_SET); if (psd_info->version == 1) result=WriteBlobMSBShort(image,(unsigned short) size); else - result=(WriteBlobMSBLong(image,(unsigned short) size)); - SeekBlob(image,current_offset,SEEK_SET); + result=WriteBlobMSBLong(image,(unsigned int) size); + (void) SeekBlob(image,current_offset,SEEK_SET); return(result); } @@ -2351,26 +2545,23 @@ static inline ssize_t SetPSDSize(const PSDInfo *psd_info,Image *image, const MagickSizeType size) { if (psd_info->version == 1) - return(WriteBlobMSBLong(image,(unsigned int) size)); - return(WriteBlobMSBLongLong(image,size)); + return(WriteBlobLong(image,(unsigned int) size)); + return(WriteBlobLongLong(image,size)); } static inline ssize_t WritePSDSize(const PSDInfo *psd_info,Image *image, - const MagickSizeType size,const MagickSizeType offset) + const MagickSizeType size,const MagickOffsetType offset) { - MagickSizeType + MagickOffsetType 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); + (void) SeekBlob(image,offset,SEEK_SET); + result=SetPSDSize(psd_info,image,size); + (void) SeekBlob(image,current_offset,SEEK_SET); return(result); } @@ -2486,7 +2677,8 @@ static size_t PSDPackbitsEncodeImage(Image *image,const size_t length, } static size_t WriteCompressionStart(const PSDInfo *psd_info,Image *image, - const Image *next_image,const ssize_t channels) + const Image *next_image,const CompressionType compression, + const ssize_t channels) { size_t length; @@ -2495,19 +2687,19 @@ static size_t WriteCompressionStart(const PSDInfo *psd_info,Image *image, i, y; - if (next_image->compression == RLECompression) + if (compression == RLECompression) { - length=WriteBlobMSBShort(image,RLE); + length=(size_t) WriteBlobShort(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); + else if (compression == ZipCompression) + length=(size_t) WriteBlobShort(image,ZipWithoutPrediction); #endif else - length=WriteBlobMSBShort(image,Raw); + length=(size_t) WriteBlobShort(image,Raw); return(length); } @@ -2515,11 +2707,8 @@ 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, - ExceptionInfo *exception) + const CompressionType compression,ExceptionInfo *exception) { - int - y; - MagickBooleanType monochrome; @@ -2536,6 +2725,9 @@ static size_t WritePSDChannel(const PSDInfo *psd_info, count, length; + ssize_t + y; + unsigned char *pixels; @@ -2560,7 +2752,7 @@ static size_t WritePSDChannel(const PSDInfo *psd_info, if (separate != MagickFalse) { size_offset=TellBlob(image)+2; - count+=WriteCompressionStart(psd_info,image,next_image,1); + count+=WriteCompressionStart(psd_info,image,next_image,compression,1); } if (next_image->depth > 8) next_image->depth=16; @@ -2571,7 +2763,7 @@ static size_t WritePSDChannel(const PSDInfo *psd_info, return(0); pixels=(unsigned char *) GetQuantumPixels(quantum_info); #ifdef MAGICKCORE_ZLIB_DELEGATE - if (next_image->compression == ZipCompression) + if (compression == ZipCompression) { compressed_pixels=(unsigned char *) AcquireQuantumMemory(CHUNK, sizeof(*compressed_pixels)); @@ -2580,7 +2772,7 @@ static size_t WritePSDChannel(const PSDInfo *psd_info, quantum_info=DestroyQuantumInfo(quantum_info); return(0); } - ResetMagickMemory(&stream,0,sizeof(stream)); + memset(&stream,0,sizeof(stream)); stream.data_type=Z_BINARY; level=Z_DEFAULT_COMPRESSION; if ((image_info->quality > 0 && image_info->quality < 10)) @@ -2602,7 +2794,7 @@ static size_t WritePSDChannel(const PSDInfo *psd_info, if (monochrome != MagickFalse) for (i=0; i < (ssize_t) length; i++) pixels[i]=(~pixels[i]); - if (next_image->compression == RLECompression) + if (compression == RLECompression) { length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels, exception); @@ -2610,7 +2802,7 @@ static size_t WritePSDChannel(const PSDInfo *psd_info, size_offset+=WritePSDOffset(psd_info,image,length,size_offset); } #ifdef MAGICKCORE_ZLIB_DELEGATE - else if (next_image->compression == ZipCompression) + else if (compression == ZipCompression) { stream.avail_in=(uInt) length; stream.next_in=(Bytef *) pixels; @@ -2631,7 +2823,7 @@ static size_t WritePSDChannel(const PSDInfo *psd_info, count+=WriteBlob(image,length,pixels); } #ifdef MAGICKCORE_ZLIB_DELEGATE - if (next_image->compression == ZipCompression) + if (compression == ZipCompression) { (void) deflateEnd(&stream); compressed_pixels=(unsigned char *) RelinquishMagickMemory( @@ -2667,6 +2859,9 @@ static size_t WritePSDChannels(const PSDInfo *psd_info, MagickOffsetType size_offset,const MagickBooleanType separate, ExceptionInfo *exception) { + CompressionType + compression; + Image *mask; @@ -2686,7 +2881,10 @@ static size_t WritePSDChannels(const PSDInfo *psd_info, offset_length=0; rows_offset=0; compact_pixels=(unsigned char *) NULL; - if (next_image->compression == RLECompression) + compression=next_image->compression; + if (image_info->compression != UndefinedCompression) + compression=image_info->compression; + if (compression == RLECompression) { compact_pixels=AcquireCompactPixels(next_image,exception); if (compact_pixels == (unsigned char *) NULL) @@ -2698,19 +2896,22 @@ static size_t WritePSDChannels(const PSDInfo *psd_info, if (next_image->storage_class != PseudoClass) { if (IsImageGray(next_image) == MagickFalse) - channels=next_image->colorspace == CMYKColorspace ? 4 : 3; + channels=(size_t) (next_image->colorspace == CMYKColorspace ? 4 : + 3); if (next_image->alpha_trait != UndefinedPixelTrait) channels++; } rows_offset=TellBlob(image)+2; - count+=WriteCompressionStart(psd_info,image,next_image,channels); + count+=WriteCompressionStart(psd_info,image,next_image,compression, + (ssize_t) 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,exception); + IndexQuantum,compact_pixels,rows_offset,separate,compression, + exception); if (separate != MagickFalse) size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2; else @@ -2722,7 +2923,8 @@ static size_t WritePSDChannels(const PSDInfo *psd_info, if (IsImageGray(next_image) != MagickFalse) { length=WritePSDChannel(psd_info,image_info,image,next_image, - GrayQuantum,compact_pixels,rows_offset,separate,exception); + GrayQuantum,compact_pixels,rows_offset,separate,compression, + exception); if (separate != MagickFalse) size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2; else @@ -2735,7 +2937,8 @@ static size_t WritePSDChannels(const PSDInfo *psd_info, (void) NegateCMYK(next_image,exception); length=WritePSDChannel(psd_info,image_info,image,next_image, - RedQuantum,compact_pixels,rows_offset,separate,exception); + RedQuantum,compact_pixels,rows_offset,separate,compression, + exception); if (separate != MagickFalse) size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2; else @@ -2743,7 +2946,8 @@ static size_t WritePSDChannels(const PSDInfo *psd_info, count+=length; length=WritePSDChannel(psd_info,image_info,image,next_image, - GreenQuantum,compact_pixels,rows_offset,separate,exception); + GreenQuantum,compact_pixels,rows_offset,separate,compression, + exception); if (separate != MagickFalse) size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2; else @@ -2751,7 +2955,8 @@ static size_t WritePSDChannels(const PSDInfo *psd_info, count+=length; length=WritePSDChannel(psd_info,image_info,image,next_image, - BlueQuantum,compact_pixels,rows_offset,separate,exception); + BlueQuantum,compact_pixels,rows_offset,separate,compression, + exception); if (separate != MagickFalse) size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2; else @@ -2761,7 +2966,8 @@ static size_t WritePSDChannels(const PSDInfo *psd_info, if (next_image->colorspace == CMYKColorspace) { length=WritePSDChannel(psd_info,image_info,image,next_image, - BlackQuantum,compact_pixels,rows_offset,separate,exception); + BlackQuantum,compact_pixels,rows_offset,separate,compression, + exception); if (separate != MagickFalse) size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2; else @@ -2772,7 +2978,8 @@ static size_t WritePSDChannels(const PSDInfo *psd_info, if (next_image->alpha_trait != UndefinedPixelTrait) { length=WritePSDChannel(psd_info,image_info,image,next_image, - AlphaQuantum,compact_pixels,rows_offset,separate,exception); + AlphaQuantum,compact_pixels,rows_offset,separate,compression, + exception); if (separate != MagickFalse) size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2; else @@ -2795,14 +3002,15 @@ static size_t WritePSDChannels(const PSDInfo *psd_info, exception); if (mask != (Image *) NULL) { - if (mask->compression == RLECompression) + if (compression == RLECompression) { compact_pixels=AcquireCompactPixels(mask,exception); if (compact_pixels == (unsigned char *) NULL) return(0); } length=WritePSDChannel(psd_info,image_info,image,mask, - RedQuantum,compact_pixels,rows_offset,MagickTrue,exception); + RedQuantum,compact_pixels,rows_offset,MagickTrue,compression, + exception); (void) WritePSDSize(psd_info,image,length,size_offset); count+=length; compact_pixels=(unsigned char *) RelinquishMagickMemory( @@ -2881,7 +3089,7 @@ static inline size_t WriteChannelSize(const PSDInfo *psd_info,Image *image, size_t count; - count=WriteBlobMSBSignedShort(image,channel); + count=(size_t) WriteBlobShort(image,(const unsigned short) channel); count+=SetPSDSize(psd_info,image,0); return(count); } @@ -2930,7 +3138,7 @@ static void RemoveICCProfileFromResourceBlock(StringInfo *bim_profile) if ((quantum >= 12) && (quantum < (ssize_t) length)) { if ((q+quantum < (datum+length-16))) - (void) CopyMagickMemory(q,q+quantum,length-quantum-(q-datum)); + (void) memmove(q,q+quantum,length-quantum-(q-datum)); SetStringInfoLength(bim_profile,length-quantum); } break; @@ -2982,9 +3190,10 @@ static void RemoveResolutionFromResourceBlock(StringInfo *bim_profile) cnt=PSDQuantum(count); if (cnt < 0) return; - if ((id == 0x000003ed) && (cnt < (ssize_t) (length-12))) + if ((id == 0x000003ed) && (cnt < (ssize_t) (length-12)) && + ((ssize_t) length-(cnt+12)-(q-datum)) > 0) { - (void) CopyMagickMemory(q,q+cnt+12,length-(cnt+12)-(q-datum)); + (void) memmove(q,q+cnt+12,length-(cnt+12)-(q-datum)); SetStringInfoLength(bim_profile,length-(cnt+12)); break; } @@ -3054,10 +3263,10 @@ static const StringInfo *GetAdditionalInformation(const ImageInfo *image_info, { /* skip over signature */ p+=4; - key[0]=(*p++); - key[1]=(*p++); - key[2]=(*p++); - key[3]=(*p++); + key[0]=(char) (*p++); + key[1]=(char) (*p++); + key[2]=(char) (*p++); + key[3]=(char) (*p++); key[4]='\0'; size=(unsigned int) (*p++) << 24; size|=(unsigned int) (*p++) << 16; @@ -3080,7 +3289,7 @@ static const StringInfo *GetAdditionalInformation(const ImageInfo *image_info, if (found == MagickFalse) { if (remaining_length > 0) - p=(unsigned char *) CopyMagickMemory(p-12,p+size,remaining_length); + p=(unsigned char *) memmove(p-12,p+size,remaining_length); continue; } length+=(size_t) size+12; @@ -3090,12 +3299,13 @@ static const StringInfo *GetAdditionalInformation(const ImageInfo *image_info, if (length == 0) return(DestroyStringInfo(profile)); SetStringInfoLength(profile,(const size_t) length); - SetImageProfile(image,"psd:additional-info",info,exception); + (void) SetImageProfile(image,"psd:additional-info",info,exception); return(profile); } -static MagickBooleanType WritePSDImage(const ImageInfo *image_info, - Image *image,ExceptionInfo *exception) +static MagickBooleanType WritePSDLayersInternal(Image *image, + const ImageInfo *image_info,const PSDInfo *psd_info,size_t *layers_size, + ExceptionInfo *exception) { char layer_name[MagickPathExtent]; @@ -3104,7 +3314,6 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info, *property; const StringInfo - *icc_profile, *info; Image @@ -3118,9 +3327,6 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info, *layer_size_offsets, size_offset; - PSDInfo - psd_info; - register ssize_t i; @@ -3129,11 +3335,219 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info, layer_index, length, name_length, - num_channels, - packet_size, rounded_size, size; + status=MagickTrue; + base_image=GetNextImageInList(image); + if (base_image == (Image *) NULL) + base_image=image; + size=0; + size_offset=TellBlob(image); + (void) SetPSDSize(psd_info,image,0); + layer_count=0; + for (next_image=base_image; next_image != NULL; ) + { + layer_count++; + next_image=GetNextImageInList(next_image); + } + if (image->alpha_trait != UndefinedPixelTrait) + size+=WriteBlobShort(image,-(unsigned short) layer_count); + else + size+=WriteBlobShort(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,exception); + default_color=(unsigned char) (strlen(property) == 9 ? 255 : 0); + } + size+=WriteBlobSignedLong(image,(signed int) next_image->page.y); + size+=WriteBlobSignedLong(image,(signed int) next_image->page.x); + size+=WriteBlobSignedLong(image,(signed int) (next_image->page.y+ + next_image->rows)); + size+=WriteBlobSignedLong(image,(signed int) (next_image->page.x+ + next_image->columns)); + channels=1; + if ((next_image->storage_class != PseudoClass) && + (IsImageGray(next_image) == MagickFalse)) + channels=(unsigned short) (next_image->colorspace == CMYKColorspace ? 4 : + 3); + total_channels=channels; + if (next_image->alpha_trait != UndefinedPixelTrait) + total_channels++; + if (mask != (Image *) NULL) + total_channels++; + size+=WriteBlobShort(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->alpha_trait != UndefinedPixelTrait) + size+=WriteChannelSize(psd_info,image,-1); + if (mask != (Image *) NULL) + size+=WriteChannelSize(psd_info,image,-2); + size+=WriteBlobString(image,image->endian == LSBEndian ? "MIB8" :"8BIM"); + size+=WriteBlobString(image,CompositeOperatorToPSDBlendMode(next_image)); + 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,exception); + } + else + size+=WriteBlobByte(image,255); + size+=WriteBlobByte(image,0); + size+=WriteBlobByte(image,(const unsigned char) + (next_image->compose == NoCompositeOp ? 1 << 0x02 : 1)); /* layer properties - visible, etc. */ + size+=WriteBlobByte(image,0); + info=GetAdditionalInformation(image_info,next_image,exception); + property=(const char *) GetImageProperty(next_image,"label",exception); + if (property == (const char *) NULL) + { + (void) FormatLocaleString(layer_name,MagickPathExtent,"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+=WriteBlobLong(image,(unsigned int) name_length); + if (mask == (Image *) NULL) + size+=WriteBlobLong(image,0); + else + { + if (mask->compose != NoCompositeOp) + (void) ApplyPSDOpacityMask(next_image,mask,ScaleCharToQuantum( + default_color),MagickTrue,exception); + mask->page.y+=image->page.y; + mask->page.x+=image->page.x; + size+=WriteBlobLong(image,20); + size+=WriteBlobSignedLong(image,(const signed int) mask->page.y); + size+=WriteBlobSignedLong(image,(const signed int) mask->page.x); + size+=WriteBlobSignedLong(image,(const signed int) (mask->rows+ + mask->page.y)); + size+=WriteBlobSignedLong(image,(const signed int) (mask->columns+ + mask->page.x)); + size+=WriteBlobByte(image,default_color); + size+=WriteBlobByte(image,(const unsigned char) + (mask->compose == NoCompositeOp ? 2 : 0)); + size+=WriteBlobMSBShort(image,0); + } + size+=WriteBlobLong(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,exception); + if (length == 0) + { + status=MagickFalse; + break; + } + size+=length; + next_image=GetNextImageInList(next_image); + } + /* + Write the total size + */ + if (layers_size != (size_t*) NULL) + *layers_size=size; + 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); + /* + 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) + (void) DeleteImageRegistry(property); + next_image=GetNextImageInList(next_image); + } + + return(status); +} + +ModuleExport MagickBooleanType WritePSDLayers(Image * image, + const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception) +{ + PolicyDomain + domain; + + PolicyRights + rights; + + domain=CoderPolicyDomain; + rights=WritePolicyRights; + if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse) + return(MagickTrue); + return WritePSDLayersInternal(image,image_info,psd_info,(size_t*) NULL, + exception); +} + +static MagickBooleanType WritePSDImage(const ImageInfo *image_info, + Image *image,ExceptionInfo *exception) +{ + const StringInfo + *icc_profile; + + MagickBooleanType + status; + + PSDInfo + psd_info; + + register ssize_t + i; + + size_t + length, + num_channels, + packet_size; + StringInfo *bim_profile; @@ -3227,16 +3641,18 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info, */ (void) WriteBlobMSBLong(image,768); for (i=0; i < (ssize_t) image->colors; i++) - (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].red)); + (void) WriteBlobByte(image,ScaleQuantumToChar(ClampToQuantum( + image->colormap[i].red))); for ( ; i < 256; i++) (void) WriteBlobByte(image,0); for (i=0; i < (ssize_t) image->colors; i++) - (void) WriteBlobByte(image,ScaleQuantumToChar( - image->colormap[i].green)); + (void) WriteBlobByte(image,ScaleQuantumToChar(ClampToQuantum( + image->colormap[i].green))); for ( ; i < 256; i++) (void) WriteBlobByte(image,0); for (i=0; i < (ssize_t) image->colors; i++) - (void) WriteBlobByte(image,ScaleQuantumToChar(image->colormap[i].blue)); + (void) WriteBlobByte(image,ScaleQuantumToChar(ClampToQuantum( + image->colormap[i].blue))); for ( ; i < 256; i++) (void) WriteBlobByte(image,0); } @@ -3273,179 +3689,25 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info, icc_profile)); (void) WriteBlob(image,GetStringInfoLength(icc_profile), GetStringInfoDatum(icc_profile)); - if ((MagickOffsetType) GetStringInfoLength(icc_profile) != - PSDQuantum(GetStringInfoLength(icc_profile))) + if ((ssize_t) GetStringInfoLength(icc_profile) != PSDQuantum(GetStringInfoLength(icc_profile))) (void) WriteBlobByte(image,0); } - base_image=GetNextImageInList(image); - if (base_image == (Image *) NULL) - base_image=image; - 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; ) - { - layer_count++; - next_image=GetNextImageInList(next_image); - } - if (image->alpha_trait != UndefinedPixelTrait) - 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; + if (status != MagickFalse) + { + MagickOffsetType + size_offset; - mask=(Image *) NULL; - property=GetImageArtifact(next_image,"psd:opacity-mask"); - default_color=0; - if (property != (const char *) NULL) - { - mask=(Image *) GetImageRegistry(ImageRegistryType,property,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) && - (IsImageGray(next_image) == MagickFalse)) - channels=next_image->colorspace == CMYKColorspace ? 4U : 3U; - total_channels=channels; - if (next_image->alpha_trait != UndefinedPixelTrait) - 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->alpha_trait != UndefinedPixelTrait) - 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; + size_t + size; - opacity=(Quantum) StringToInteger(property); - size+=WriteBlobByte(image,ScaleQuantumToChar(opacity)); - (void) ApplyPSDLayerOpacity(next_image,opacity,MagickTrue,exception); - } - else - 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,exception); - property=(const char *) GetImageProperty(next_image,"label",exception); - if (property == (const char *) NULL) - { - (void) FormatLocaleString(layer_name,MagickPathExtent,"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 - { - if (mask->compose != NoCompositeOp) - (void) ApplyPSDOpacityMask(next_image,mask,ScaleCharToQuantum( - default_color),MagickTrue,exception); - mask->page.y+=image->page.y; - mask->page.x+=image->page.x; - size+=WriteBlobMSBLong(image,20); - size+=WriteBlobMSBSignedLong(image,mask->page.y); - size+=WriteBlobMSBSignedLong(image,mask->page.x); - size+=WriteBlobMSBSignedLong(image,(const signed int) mask->rows+ - mask->page.y); - size+=WriteBlobMSBSignedLong(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,exception); - if (length == 0) - { - status=MagickFalse; - break; - } - size+=length; - next_image=GetNextImageInList(next_image); - } + size_offset=TellBlob(image); + (void) SetPSDSize(&psd_info,image,0); + status=WritePSDLayersInternal(image,image_info,&psd_info,&size, + exception); + size_offset+=WritePSDSize(&psd_info,image,size+ + (psd_info.version == 1 ? 8 : 12),size_offset); + } (void) WriteBlobMSBLong(image,0); /* user mask data */ - /* - Write the total size - */ - size_offset+=WritePSDSize(&psd_info,image,size+ - (psd_info.version == 1 ? 8 : 16),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); - /* - 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); - } /* Write composite image. */ @@ -3457,6 +3719,8 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info, compression=image->compression; if (image->compression == ZipCompression) image->compression=RLECompression; + if (image_info->compression != UndefinedCompression) + image->compression=image_info->compression; if (WritePSDChannels(&psd_info,image_info,image,image,0,MagickFalse, exception) == 0) status=MagickFalse;