From: dirk Date: Fri, 16 Sep 2016 20:12:04 +0000 (+0200) Subject: Fixed opacity mask implementation and added define to preserve the mask. X-Git-Tag: 7.0.3-1~22 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=823525fb0a9ccf23312a43ce6e583b792cc57124;p=imagemagick Fixed opacity mask implementation and added define to preserve the mask. --- diff --git a/ChangeLog b/ChangeLog index 2bb5ea129..d4d4113bd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2016-09-16 7.0.3-1 Dirk Lemstra + * Added layer RLE compression to the PSD encoder. + * Added define 'psd:preserve-opacity-mask' to preserve the opacity mask + in a PSD file. + 2016-09-05 7.0.3-0 Cristy * Release ImageMagick version 7.0.3-0, GIT revision 18786:10aa64c:20160905. diff --git a/coders/psd.c b/coders/psd.c index c2abdfafe..8efba1948 100644 --- a/coders/psd.c +++ b/coders/psd.c @@ -70,6 +70,7 @@ #include "MagickCore/pixel-accessor.h" #include "MagickCore/profile.h" #include "MagickCore/property.h" +#include "MagickCore/registry.h" #include "MagickCore/quantum-private.h" #include "MagickCore/static.h" #include "MagickCore/string_.h" @@ -358,8 +359,8 @@ static inline CompressionType ConvertPSDCompression( } } -static MagickBooleanType CorrectPSDOpacity(LayerInfo *layer_info, - ExceptionInfo *exception) +static MagickBooleanType ApplyPSDLayerOpacity(Image *image,Quantum opacity, + MagickBooleanType revert,ExceptionInfo *exception) { MagickBooleanType status; @@ -367,16 +368,16 @@ static MagickBooleanType CorrectPSDOpacity(LayerInfo *layer_info, ssize_t y; - if (layer_info->opacity == OpaqueAlpha) + if (opacity == OpaqueAlpha) return(MagickTrue); - layer_info->image->alpha_trait=BlendPixelTrait; + image->alpha_trait=BlendPixelTrait; status=MagickTrue; #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp parallel for schedule(static,4) shared(status) \ - magick_threads(layer_info->image,layer_info->image,layer_info->image->rows,1) + magick_threads(image,image,image->rows,1) #endif - for (y=0; y < (ssize_t) layer_info->image->rows; y++) + for (y=0; y < (ssize_t) image->rows; y++) { register Quantum *magick_restrict q; @@ -386,26 +387,105 @@ static MagickBooleanType CorrectPSDOpacity(LayerInfo *layer_info, if (status == MagickFalse) continue; - q=GetAuthenticPixels(layer_info->image,0,y,layer_info->image->columns,1, - exception); + q=GetAuthenticPixels(image,0,y,image->columns,1,exception); if (q == (Quantum *)NULL) { status=MagickFalse; continue; } - for (x=0; x < (ssize_t) layer_info->image->columns; x++) + for (x=0; x < (ssize_t) image->columns; x++) { - SetPixelAlpha(layer_info->image,(Quantum) (QuantumScale*(GetPixelAlpha( - layer_info->image,q))*layer_info->opacity),q); - q+=GetPixelChannels(layer_info->image); + if (revert == MagickFalse) + SetPixelAlpha(image,(Quantum) (QuantumScale*(GetPixelAlpha(image,q))* + opacity),q); + else if (opacity > 0) + SetPixelAlpha(image,(Quantum) (QuantumRange*(GetPixelAlpha(image,q)/ + opacity)),q); + q+=GetPixelChannels(image); } - if (SyncAuthenticPixels(layer_info->image,exception) == 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; + + PixelInfo + color; + + ssize_t + y; + + complete_mask=CloneImage(image,image->columns,image->rows,MagickTrue, + exception); + complete_mask->alpha_trait=BlendPixelTrait; + GetPixelInfo(complete_mask,&color); + color.red=background; + 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) + { + complete_mask=DestroyImage(complete_mask); + return(status); + } + 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) +#endif + for (y=0; y < (ssize_t) image->rows; y++) + { + register Quantum + *magick_restrict q; + + register Quantum + *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 == (Quantum *) NULL) || (p == (Quantum *) NULL)) + { + status=MagickFalse; + continue; + } + for (x=0; x < (ssize_t) image->columns; x++) + { + MagickRealType + alpha, + intensity; + + alpha=GetPixelAlpha(image,q); + intensity=GetPixelIntensity(complete_mask,p); + if (revert == MagickFalse) + SetPixelAlpha(image,ClampToQuantum(intensity*(QuantumScale*alpha)),q); + else if (intensity > 0) + SetPixelAlpha(image,ClampToQuantum((alpha/intensity)*QuantumRange),q); + q+=GetPixelChannels(image); + p+=GetPixelChannels(complete_mask); + } + if (SyncAuthenticPixels(image,exception) == MagickFalse) + status=MagickFalse; + } + complete_mask=DestroyImage(complete_mask); + + return(status); +} + static ssize_t DecodePSDPixels(const size_t number_compact_pixels, const unsigned char *compact_pixels,const ssize_t depth, const size_t number_pixels,unsigned char *pixels) @@ -1158,9 +1238,10 @@ static MagickBooleanType ReadPSDChannelZip(Image *image,const size_t channels, } #endif -static MagickBooleanType ReadPSDChannel(Image *image,const PSDInfo *psd_info, - 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, @@ -1176,12 +1257,17 @@ static MagickBooleanType ReadPSDChannel(Image *image,const PSDInfo *psd_info, mask=(Image *) NULL; if (layer_info->channel_info[channel].type < -1) { + 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 > 3) || (layer_info->mask.flags & 0x02)) + (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); @@ -1243,29 +1329,16 @@ static MagickBooleanType ReadPSDChannel(Image *image,const PSDInfo *psd_info, if (mask != (Image *) NULL) { if (status != MagickFalse) - { - PixelInfo - color; - - layer_info->mask.image=CloneImage(image,image->columns,image->rows, - MagickTrue,exception); - (void) SetImageGray(layer_info->mask.image,exception); - layer_info->mask.image->alpha_trait=UndefinedPixelTrait; - GetPixelInfo(layer_info->mask.image,&color); - color.red=layer_info->mask.background == 0 ? 0 : QuantumRange; - SetImageColor(layer_info->mask.image,&color,exception); - (void) CompositeImage(layer_info->mask.image,mask,OverCompositeOp, - MagickTrue,layer_info->mask.page.x,layer_info->mask.page.y, - exception); - } - DestroyImage(mask); + layer_info->mask.image=mask; + else + mask=DestroyImage(mask); } return(status); } -static MagickBooleanType ReadPSDLayer(Image *image,const 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[MagickPathExtent]; @@ -1319,7 +1392,7 @@ static MagickBooleanType ReadPSDLayer(Image *image,const PSDInfo *psd_info, if (layer_info->channel_info[j].type == -1) layer_info->image->alpha_trait=BlendPixelTrait; - status=ReadPSDChannel(layer_info->image,psd_info,layer_info,j, + status=ReadPSDChannel(layer_info->image,image_info,psd_info,layer_info,j, compression,exception); if (status == MagickFalse) @@ -1327,7 +1400,8 @@ static MagickBooleanType ReadPSDLayer(Image *image,const PSDInfo *psd_info, } if (status != MagickFalse) - status=CorrectPSDOpacity(layer_info,exception); + status=ApplyPSDLayerOpacity(layer_info->image,layer_info->opacity, + MagickFalse,exception); if ((status != MagickFalse) && (layer_info->image->colorspace == CMYKColorspace)) @@ -1335,8 +1409,44 @@ static MagickBooleanType ReadPSDLayer(Image *image,const PSDInfo *psd_info, if ((status != MagickFalse) && (layer_info->mask.image != (Image *) NULL)) { - status=CompositeImage(layer_info->image,layer_info->mask.image, - CopyAlphaCompositeOp,MagickTrue,0,0,exception); + 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) + { + char + *key; + + RandomInfo + *random_info; + + StringInfo + *key_info; + + 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); + } layer_info->mask.image=DestroyImage(layer_info->mask.image); } @@ -1667,7 +1777,7 @@ ModuleExport MagickBooleanType ReadPSDLayers(Image *image, (void) LogMagickEvent(CoderEvent,GetMagickModule(), " reading data for layer %.20g",(double) i); - status=ReadPSDLayer(image,psd_info,&layer_info[i],exception); + status=ReadPSDLayer(image,image_info,psd_info,&layer_info[i],exception); if (status == MagickFalse) break; @@ -2538,7 +2648,33 @@ static MagickBooleanType WriteImageChannels(const PSDInfo *psd_info, compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels); if (next_image->colorspace == CMYKColorspace) (void) NegateCMYK(next_image,exception); + if (separate != MagickFalse) + { + const char + *property; + property=GetImageArtifact(next_image,"psd:opacity-mask"); + if (property != (const char *) NULL) + { + mask=(Image *) GetImageRegistry(ImageRegistryType,property, + exception); + if (mask != (Image *) NULL) + { + if (mask->compression == RLECompression) + { + compact_pixels=AcquireCompactPixels(mask,exception); + if (compact_pixels == (unsigned char *) NULL) + return(0); + } + length=WriteOneChannel(psd_info,image_info,image,mask, + RedQuantum,compact_pixels,rows_offset,MagickTrue,exception); + (void) WritePSDSize(psd_info,image,length,size_offset); + count+=length; + compact_pixels=(unsigned char *) RelinquishMagickMemory( + compact_pixels); + } + } + } return(count); } @@ -3030,10 +3166,24 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info, 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=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+ @@ -3046,12 +3196,16 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info, 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)); @@ -3063,6 +3217,7 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info, opacity=(Quantum) StringToInteger(property); size+=WriteBlobByte(image,ScaleQuantumToChar(opacity)); + (void) ApplyPSDLayerOpacity(next_image,opacity,MagickTrue,exception); } else size+=WriteBlobByte(image,255); @@ -3083,8 +3238,28 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info, name_length+=(4-(name_length % 4)); if (info != (const StringInfo *) NULL) name_length+=GetStringInfoLength(info); - size+=WriteBlobMSBLong(image,(unsigned int) name_length+8); - size+=WriteBlobMSBLong(image,0); + 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+=WriteBlobMSBLong(image,mask->rows+mask->page.y); + size+=WriteBlobMSBLong(image,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) @@ -3121,6 +3296,17 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info, rounded_size=size; (void) WritePSDSize(&psd_info,image,rounded_size,size_offset); layer_size_offsets=RelinquishMagickMemory(layer_size_offsets); + /* + Remove the opacity mask from the registry + */ + next_image=base_image; + while (next_image != NULL) + { + property=GetImageArtifact(next_image,"psd:opacity-mask"); + if (property != (const char *) NULL) + DeleteImageRegistry(property); + next_image=GetNextImageInList(next_image); + } /* Write composite image. */