]> granicus.if.org Git - imagemagick/blobdiff - coders/psd.c
Fixed memory leak that was detected by oss-fuzz.
[imagemagick] / coders / psd.c
index 7b53f57252875f6ee1d1faeb79ed379bf4f00f81..4c31d4eecda952d8cbb11aa4750c98fca25354da 100644 (file)
@@ -26,7 +26,7 @@
 %  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
 %
 */
 \f
@@ -1587,6 +1588,55 @@ static MagickBooleanType CheckPSDChannels(const PSDInfo *psd_info,
   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)
@@ -1635,347 +1685,323 @@ static MagickBooleanType ReadPSDLayersInternal(Image *image,
             return(MagickTrue);
         }
     }
-  status=MagickTrue;
-  if (size != 0)
-    {
-      layer_info=(LayerInfo *) NULL;
-      number_layers=(ssize_t) ReadBlobSignedShort(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) memset(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=(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))
-            {
-              layer_info=DestroyLayerInfo(layer_info,number_layers);
-              ThrowBinaryException(CorruptImageError,"NoSuchImageChannel",
-                image->filename);
-            }
-          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))
-          {
-            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);
-        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);
+            "  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=(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))
-                  {
-                    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));
-                if (DiscardBlobBytes(image,length) == MagickFalse)
-                  {
-                    layer_info=DestroyLayerInfo(layer_info,number_layers);
-                    ThrowBinaryException(CorruptImageError,
-                      "UnexpectedEndOfFile",image->filename);
-                  }
-              }
             /*
-              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 < (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;
+                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,(MagickOffsetType) 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);
           }
+      }
+  }
+
+  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;
+      }
 
-          if (number_layers > 0)
+    /*
+      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)
             {
-              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=DestroyLayerInfo(layer_info,number_layers);
+              ThrowBinaryException(CorruptImageError,
+                "UnexpectedEndOfFile",image->filename);
             }
-          layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
         }
-      else
-        layer_info=DestroyLayerInfo(layer_info,number_layers);
-    }
+        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);
 }
@@ -2012,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);
 
@@ -2171,8 +2199,6 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
     {
       psd_info.min_channels=4;
       (void) SetImageColorspace(image,CMYKColorspace,exception);
-      if (psd_info.channels > 4)
-        (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
     }
   else if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) ||
            (psd_info.mode == DuotoneMode))
@@ -2189,12 +2215,7 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
         }
       psd_info.min_channels=1;
       (void) SetImageColorspace(image,GRAYColorspace,exception);
-      if (psd_info.channels > 1)
-        (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
     }
-  else
-    if (psd_info.channels > 3)
-      (void) SetImageAlphaChannel(image,ActivateAlphaChannel,exception);
   if (psd_info.channels < psd_info.min_channels)
     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
   /*
@@ -2377,8 +2398,18 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
     }
   if (profile != (StringInfo *) NULL)
     {
-      (void) SetImageProfile(image,GetStringInfoName(profile),profile,
-        exception);
+      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);