]> granicus.if.org Git - imagemagick/commitdiff
Added support for reading a user supplied layer mask in PSD files.
authordirk <dirk@git.imagemagick.org>
Mon, 25 May 2015 07:56:17 +0000 (07:56 +0000)
committerdirk <dirk@git.imagemagick.org>
Mon, 25 May 2015 07:56:17 +0000 (07:56 +0000)
coders/psd.c

index 54dd96771f9ed6d6ca2bcd7db0c4b721f726d590..1ce127853645f49068686762000551a253bdd18d 100644 (file)
@@ -117,41 +117,53 @@ typedef struct _ChannelInfo
     size;
 } ChannelInfo;
 
-typedef struct _LayerInfo
+typedef struct _MaskInfo
 {
+  Image
+    *image;
+
   RectangleInfo
-    page,
-    mask;
+    page;
 
-  unsigned short
-    channels;
+  unsigned char
+    background,
+    flags;
+} MaskInfo;
 
+typedef struct _LayerInfo
+{
   ChannelInfo
     channel_info[MaxPSDChannels];
 
   char
     blendkey[4];
 
+  Image
+    *image;
+
+  MaskInfo
+    mask;
+
   Quantum
     opacity;
 
-  unsigned char
-    clipping,
-    visible,
-    flags;
+  RectangleInfo
+    page;
 
   size_t
     offset_x,
     offset_y;
 
   unsigned char
-    name[256];
+    clipping,
+    flags,
+    name[256],
+    visible;
 
-  Image
-    *image;
+  unsigned short
+    channels;
 } LayerInfo;
-
-typedef struct _PSDInfo
+\ftypedef struct _PSDInfo
 {
   char
     signature[4];
@@ -171,7 +183,8 @@ typedef struct _PSDInfo
     depth,
     mode;
 } PSDInfo;
-\f
+
+
 /*
   Forward declarations.
 */
@@ -464,6 +477,8 @@ static inline LayerInfo *DestroyLayerInfo(LayerInfo *layer_info,
   {
     if (layer_info[i].image != (Image *) NULL)
       layer_info[i].image=DestroyImage(layer_info[i].image);
+    if (layer_info[i].mask.image != (Image *) NULL)
+      layer_info[i].mask.image=DestroyImage(layer_info[i].mask.image);
   }
 
   return (LayerInfo *) RelinquishMagickMemory(layer_info);
@@ -706,10 +721,11 @@ static MagickStatusType ReadPSDChannelPixels(Image *image,
         SetPixelAlpha(image,pixel,q);
         break;
       }
+      case -2:
       case 0:
       {
         SetPixelRed(image,pixel,q);
-        if (channels == 1)
+        if (channels == 1 || type == -2)
           SetPixelGray(image,pixel,q);
         if (image->storage_class == PseudoClass)
           {
@@ -1043,47 +1059,64 @@ static MagickStatusType ReadPSDChannelZip(Image *image,const size_t channels,
 #endif
 
 static MagickStatusType ReadPSDChannel(Image *image,const PSDInfo *psd_info,
-  const LayerInfo* layer_info,const size_t channel,
+  LayerInfo* layer_info,const size_t channel,
   const PSDCompressionType compression,ExceptionInfo *exception)
 {
+  Image
+    *channel_image,
+    *mask;
+
   MagickOffsetType
     offset;
 
   MagickStatusType
     status;
 
+  channel_image=image;
+  mask=(Image *) NULL;
   if (layer_info->channel_info[channel].type < -1)
   {
-    /* ignore user supplied layer mask */
-    SeekBlob(image,layer_info->channel_info[channel].size-2,SEEK_CUR);
-    return(MagickTrue);
+    /*
+      Ignore mask that is not a user supplied layer mask, if the mask is
+      disabled or if the flags have unsupported values.
+    */
+    if (layer_info->channel_info[channel].type != -2 ||
+        (layer_info->mask.flags > 3) || (layer_info->mask.flags & 0x02))
+    {
+      SeekBlob(image,layer_info->channel_info[channel].size-2,SEEK_CUR);
+      return(MagickTrue);
+    }
+    mask=CloneImage(image,layer_info->mask.page.width,
+      layer_info->mask.page.height,MagickFalse,exception);
+    SetImageType(mask,GrayscaleType,exception);
+    channel_image=mask;
   }
 
-  offset=TellBlob(image);
+  offset=TellBlob(channel_image);
   status=MagickTrue;
   switch(compression)
   {
     case Raw:
-      return(ReadPSDChannelRaw(image,psd_info->channels,
+      return(ReadPSDChannelRaw(channel_image,psd_info->channels,
         layer_info->channel_info[channel].type,exception));
     case RLE:
       {
         MagickOffsetType
           *offsets;
 
-        offsets=ReadPSDRLEOffsets(image,psd_info,image->rows);
+        offsets=ReadPSDRLEOffsets(channel_image,psd_info,channel_image->rows);
         if (offsets == (MagickOffsetType *) NULL)
           ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
             image->filename);
-        status=ReadPSDChannelRLE(image,psd_info,
-                 layer_info->channel_info[channel].type,offsets,exception);
+        status=ReadPSDChannelRLE(channel_image,psd_info,
+          layer_info->channel_info[channel].type,offsets,exception);
         offsets=(MagickOffsetType *) RelinquishMagickMemory(offsets);
       }
       break;
     case ZipWithPrediction:
     case ZipWithoutPrediction:
 #ifdef MAGICKCORE_ZLIB_DELEGATE
-      status=ReadPSDChannelZip(image,layer_info->channels,
+      status=ReadPSDChannelZip(channel_image,layer_info->channels,
         layer_info->channel_info[channel].type,compression,
         layer_info->channel_info[channel].size-2,exception);
 #else
@@ -1102,6 +1135,25 @@ static MagickStatusType ReadPSDChannel(Image *image,const PSDInfo *psd_info,
 
   if (status == MagickFalse)
     SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
+  if (mask != (Image *) NULL)
+  {
+    if (status != MagickFalse)
+      {
+        PixelInfo
+          color;
+
+        layer_info->mask.image=CloneImage(image,image->columns,image->rows,
+          MagickTrue,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);
+  }
 
   return(status);
 }
@@ -1137,7 +1189,7 @@ static MagickStatusType ReadPSDLayer(Image *image,const PSDInfo *psd_info,
   /*
     Set up some hidden attributes for folks that need them.
   */
-  (void) FormatLocaleString(message,MagickPathExtent,"%.20gld",
+  (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
     (double) layer_info->page.x);
   (void) SetImageArtifact(layer_info->image,"psd:layer.x",message);
   (void) FormatLocaleString(message,MagickPathExtent,"%.20g",
@@ -1171,8 +1223,17 @@ static MagickStatusType ReadPSDLayer(Image *image,const PSDInfo *psd_info,
   if (status != MagickFalse)
     status=CorrectPSDOpacity(layer_info,exception);
 
-  if (status != MagickFalse && layer_info->image->colorspace == CMYKColorspace)
-   (void) NegateCMYK(layer_info->image,exception);
+  if (status != MagickFalse)
+    {
+      if (layer_info->image->colorspace == CMYKColorspace)
+        NegateCMYK(layer_info->image,exception);
+      if (layer_info->mask.image != (Image *) NULL)
+        {
+          CompositeImage(layer_info->image,layer_info->mask.image,
+            CopyAlphaCompositeOp,MagickTrue,0,0,exception);
+          layer_info->mask.image=DestroyImage(layer_info->mask.image);
+        }
+    }
 
   return(status);
 }
@@ -1355,23 +1416,33 @@ static MagickStatusType ReadPSDLayers(Image *image,const ImageInfo *image_info,
                 /*
                   Layer mask info.
                 */
-                layer_info[i].mask.y=(int) ReadBlobMSBLong(image);
-                layer_info[i].mask.x=(int) ReadBlobMSBLong(image);
-                layer_info[i].mask.height=(size_t)
-                  (ReadBlobMSBLong(image)-layer_info[i].mask.y);
-                layer_info[i].mask.width=(size_t)
-                  (ReadBlobMSBLong(image)-layer_info[i].mask.x);
+                layer_info[i].mask.page.y=(int) ReadBlobMSBLong(image);
+                layer_info[i].mask.page.x=(int) ReadBlobMSBLong(image);
+                layer_info[i].mask.page.height=(size_t)
+                  (ReadBlobMSBLong(image)-layer_info[i].mask.page.y);
+                layer_info[i].mask.page.width=(size_t)
+                  (ReadBlobMSBLong(image)-layer_info[i].mask.page.x);
+                layer_info[i].mask.background=(unsigned char) ReadBlobByte(
+                  image);
+                layer_info[i].mask.flags=(unsigned char) ReadBlobByte(image);
+                if (!(layer_info[i].mask.flags & 0x01))
+                  {
+                    layer_info[i].mask.page.y=layer_info[i].mask.page.y-
+                      layer_info[i].page.y;
+                    layer_info[i].mask.page.x=layer_info[i].mask.page.x-
+                      layer_info[i].page.x;
+                  }
                 if (image->debug != MagickFalse)
                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                     "      layer mask: offset(%.20g,%.20g), size(%.20g,%.20g), length=%.20g",
-                    (double) layer_info[i].mask.x,(double) 
-                    layer_info[i].mask.y,(double) layer_info[i].mask.width,
-                    (double) layer_info[i].mask.height,(double)
-                    ((MagickOffsetType) length)-16);
+                    (double) layer_info[i].mask.page.x,(double) 
+                    layer_info[i].mask.page.y,(double) layer_info[i].mask.page.width,
+                    (double) layer_info[i].mask.page.height,(double)
+                    ((MagickOffsetType) length)-18);
                 /*
                   Skip over the rest of the layer mask information.
                 */
-                if (DiscardBlobBytes(image,(MagickSizeType) (length-16)) == MagickFalse)
+                if (DiscardBlobBytes(image,(MagickSizeType) (length-18)) == MagickFalse)
                   {
                     layer_info=DestroyLayerInfo(layer_info,number_layers);
                     ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile",
@@ -1407,9 +1478,9 @@ static MagickStatusType ReadPSDLayers(Image *image,const ImageInfo *image_info,
             */
             length=(size_t) ReadBlobByte(image);
             combined_length+=length+1;
-            for (j=0; j < (ssize_t) length; j++)
-              layer_info[i].name[j]=(unsigned char) ReadBlobByte(image);
-            layer_info[i].name[j]='\0';
+            if (length > 0)
+              (void) ReadBlob(image,(size_t) length++,layer_info[i].name);
+            layer_info[i].name[length]='\0';
             if (image->debug != MagickFalse)
               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                 "      layer name: %s",layer_info[i].name);
@@ -1589,8 +1660,7 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
 
   MagickBooleanType
     has_merged_image,
-    skip_layers,
-    status;
+    skip_layers;
 
   MagickOffsetType
     offset;
@@ -1598,6 +1668,9 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
   MagickSizeType
     length;
 
+  MagickStatusType
+    status;
+
   PSDInfo
     psd_info;