]> granicus.if.org Git - imagemagick/blobdiff - coders/psd.c
Fixed memory leak that was detected by oss-fuzz.
[imagemagick] / coders / psd.c
index 1b40c9f86e704ffe4cd4e675e5b1802dfcce50f2..4c31d4eecda952d8cbb11aa4750c98fca25354da 100644 (file)
 %                                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                                            %
 %                                                                             %
-%    http://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
@@ -68,6 +69,7 @@
 #include "MagickCore/option.h"
 #include "MagickCore/pixel.h"
 #include "MagickCore/pixel-accessor.h"
+#include "MagickCore/policy.h"
 #include "MagickCore/profile.h"
 #include "MagickCore/property.h"
 #include "MagickCore/registry.h"
@@ -115,7 +117,7 @@ typedef enum
 */
 typedef struct _ChannelInfo
 {
-  short int
+  short
     type;
 
   size_t
@@ -162,7 +164,7 @@ typedef struct _LayerInfo
   unsigned char
     clipping,
     flags,
-    name[256],
+    name[257],
     visible;
 
   unsigned short
@@ -239,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);
 }
 
 /*
@@ -298,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++)
   {
@@ -330,7 +351,7 @@ static MagickBooleanType CorrectPSDAlphaBlend(const ImageInfo *image_info,
         {
           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
           {
-            PixelChannel channel=GetPixelChannelChannel(image,i);
+            PixelChannel channel = GetPixelChannelChannel(image,i);
             if (channel != AlphaPixelChannel)
               q[i]=ClampToQuantum((q[i]-((1.0-gamma)*QuantumRange))/gamma);
           }
@@ -373,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++)
   {
@@ -430,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)
@@ -445,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++)
   {
@@ -474,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);
@@ -508,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;
@@ -669,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);
 }
@@ -731,40 +753,50 @@ 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;
 
+  unsigned char
+    name_length;
+
   unsigned int
-    count,
-    long_sans;
+    count;
 
   unsigned short
     id,
     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);
-  for (p=blocks; (p >= blocks) && (p < (blocks+length-16)); )
+  SetStringInfoName(profile,"8bim");
+  for (p=blocks; (p >= blocks) && (p < (blocks+length-7)); )
   {
     if (LocaleNCompare((const char *) p,"8BIM",4) != 0)
       break;
-    p=PushLongPixel(MSBEndian,p,&long_sans);
+    p+=4;
     p=PushShortPixel(MSBEndian,p,&id);
-    p=PushShortPixel(MSBEndian,p,&short_sans);
+    p=PushCharPixel(p,&name_length);
+    if ((name_length % 2) == 0)
+      name_length++;
+    p+=name_length;
+    if (p > (blocks+length-4))
+      break;
     p=PushLongPixel(MSBEndian,p,&count);
-    if ((p+count) > (blocks+length-16))
-      return;
+    offset=(ssize_t) count;
+    if (((p+offset) < blocks) || ((p+offset) > (blocks+length)))
+      break;
     switch (id)
     {
       case 0x03ed:
@@ -778,16 +810,20 @@ static void ParseImageResourceBlocks(Image *image,
         /*
           Resolution info.
         */
+        if (offset < 16)
+          break;
         p=PushShortPixel(MSBEndian,p,&resolution);
         image->resolution.x=(double) resolution;
-        (void) FormatLocaleString(value,MagickPathExtent,"%g",image->resolution.x);
+        (void) FormatLocaleString(value,MagickPathExtent,"%g",
+          image->resolution.x);
         (void) SetImageProperty(image,"tiff:XResolution",value,exception);
         p=PushShortPixel(MSBEndian,p,&short_sans);
         p=PushShortPixel(MSBEndian,p,&short_sans);
         p=PushShortPixel(MSBEndian,p,&short_sans);
         p=PushShortPixel(MSBEndian,p,&resolution);
         image->resolution.y=(double) resolution;
-        (void) FormatLocaleString(value,MagickPathExtent,"%g",image->resolution.y);
+        (void) FormatLocaleString(value,MagickPathExtent,"%g",
+          image->resolution.y);
         (void) SetImageProperty(image,"tiff:YResolution",value,exception);
         p=PushShortPixel(MSBEndian,p,&short_sans);
         p=PushShortPixel(MSBEndian,p,&short_sans);
@@ -797,21 +833,21 @@ static void ParseImageResourceBlocks(Image *image,
       }
       case 0x0421:
       {
-        if (*(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)
@@ -890,43 +926,48 @@ static inline void SetPSDPixel(Image *image,const size_t channels,
 {
   if (image->storage_class == PseudoClass)
     {
-      if (packet_size == 1)
-        SetPixelIndex(image,ScaleQuantumToChar(pixel),q);
+      PixelInfo
+        *color;
+
+      if (type == 0)
+        {
+          if (packet_size == 1)
+            SetPixelIndex(image,ScaleQuantumToChar(pixel),q);
+          else
+            SetPixelIndex(image,ScaleQuantumToShort(pixel),q);
+        }
+      color=image->colormap+(ssize_t) ConstrainColormapIndex(image,
+        (ssize_t) GetPixelIndex(image,q),exception);
+      if ((type == 0) && (channels > 1))
+        return;
       else
-        SetPixelIndex(image,ScaleQuantumToShort(pixel),q);
-      SetPixelViaPixelInfo(image,image->colormap+(ssize_t)
-        ConstrainColormapIndex(image,GetPixelIndex(image,q),exception),q);
+        color->alpha=(MagickRealType) pixel;
+      SetPixelViaPixelInfo(image,color,q);
       return;
     }
   switch (type)
   {
     case -1:
     {
-      SetPixelAlpha(image, pixel,q);
+      SetPixelAlpha(image,pixel,q);
       break;
     }
     case -2:
     case 0:
     {
       SetPixelRed(image,pixel,q);
-      if (channels == 1 || type == -2)
-        SetPixelGray(image,pixel,q);
       break;
     }
+    case -3:
     case 1:
     {
-      if (image->storage_class == PseudoClass)
-        SetPixelAlpha(image,pixel,q);
-      else
-        SetPixelGreen(image,pixel,q);
+      SetPixelGreen(image,pixel,q);
       break;
     }
+    case -4:
     case 2:
     {
-      if (image->storage_class == PseudoClass)
-        SetPixelAlpha(image,pixel,q);
-      else
-        SetPixelBlue(image,pixel,q);
+      SetPixelBlue(image,pixel,q);
       break;
     }
     case 3:
@@ -951,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
@@ -969,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)
@@ -981,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);
@@ -996,11 +1045,11 @@ static MagickBooleanType ReadPSDChannelPixels(Image *image,
         ssize_t
           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);
@@ -1022,10 +1071,10 @@ static MagickBooleanType ReadPSDChannelRaw(Image *image,const size_t channels,
     status;
 
   size_t
-    count,
     row_size;
 
   ssize_t
+    count,
     y;
 
   unsigned char
@@ -1047,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)
@@ -1115,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);
@@ -1129,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++)
@@ -1187,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)
@@ -1204,11 +1259,16 @@ static MagickBooleanType ReadPSDChannelZip(Image *image,const size_t channels,
       ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
         image->filename);
     }
+  if (ReadBlob(image,compact_size,compact_pixels) != (ssize_t) compact_size)
+    {
+      pixels=(unsigned char *) RelinquishMagickMemory(pixels);
+      compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
+      ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile",
+        image->filename);
+    }
 
-  ResetMagickMemory(&stream,0,sizeof(stream));
+  memset(&stream,0,sizeof(stream));
   stream.data_type=Z_BINARY;
-  (void) ReadBlob(image,compact_size,compact_pixels);
-
   stream.next_in=(Bytef *)compact_pixels;
   stream.avail_in=(uInt) compact_size;
   stream.next_out=(Bytef *)pixels;
@@ -1224,35 +1284,45 @@ static MagickBooleanType ReadPSDChannelZip(Image *image,const size_t channels,
         ret=inflate(&stream,Z_SYNC_FLUSH);
         if ((ret != Z_OK) && (ret != Z_STREAM_END))
           {
+            (void) inflateEnd(&stream);
             compact_pixels=(unsigned char *) RelinquishMagickMemory(
               compact_pixels);
             pixels=(unsigned char *) RelinquishMagickMemory(pixels);
             return(MagickFalse);
           }
+        if (ret == Z_STREAM_END)
+          break;
       }
+      (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;
@@ -1288,10 +1358,12 @@ static MagickBooleanType ReadPSDChannel(Image *image,
 
   channel_image=image;
   mask=(Image *) NULL;
-  if (layer_info->channel_info[channel].type < -1)
+  if ((layer_info->channel_info[channel].type < -1) &&
+      (layer_info->mask.page.width > 0) && (layer_info->mask.page.height > 0))
     {
       const char
         *option;
+
       /*
         Ignore mask that is not a user supplied layer mask, if the mask is
         disabled or if the flags have unsupported values.
@@ -1300,23 +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);
-      SetImageType(mask,GrayscaleType,exception);
-      channel_image=mask;
+      if (mask != (Image *) NULL)
+        {
+          (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:
       {
@@ -1328,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;
@@ -1336,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(),
@@ -1350,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);
 }
 
@@ -1386,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.
   */
@@ -1414,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;
@@ -1437,7 +1523,7 @@ static MagickBooleanType ReadPSDLayer(Image *image,const ImageInfo *image_info,
     {
       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 */
@@ -1456,7 +1542,102 @@ static MagickBooleanType ReadPSDLayer(Image *image,const ImageInfo *image_info,
   return(status);
 }
 
-ModuleExport MagickBooleanType ReadPSDLayers(Image *image,
+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)
 {
@@ -1488,347 +1669,360 @@ ModuleExport MagickBooleanType ReadPSDLayers(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) (ReadBlobLong(image)-
-                  layer_info[i].mask.page.y);
-                layer_info[i].mask.page.width=(size_t) (ReadBlobLong(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;
-
-                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)
+        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=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);
+            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);
+              }
           }
-
-        if (layer_info[i].info != (StringInfo *) NULL)
+        length=(MagickSizeType) size-combined_length;
+        if (length > 0)
           {
-            (void) SetImageProfile(layer_info[i].image,"psd:additional-info",
-              layer_info[i].info,exception);
-            layer_info[i].info=DestroyStringInfo(layer_info[i].info);
-          }
-      }
+            unsigned char
+              *info;
 
-      if (image_info->ping == MagickFalse)
-        {
-          for (i=0; i < number_layers; i++)
-          {
-            if (layer_info[i].image == (Image *) NULL)
+            if (length > GetBlobSize(image))
               {
-                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,
+                  "InsufficientImageDataInFile",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;
+            layer_info[i].info=AcquireStringInfo((const size_t) length);
+            info=GetStringInfoDatum(layer_info[i].info);
+            (void) ReadBlob(image,(const size_t) length,info);
           }
-        }
+      }
+  }
 
-      if (status != MagickFalse)
-        {
-          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--;
-              }
-          }
+  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);
 }
 
+ModuleExport MagickBooleanType ReadPSDLayers(Image *image,
+  const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception)
+{
+  PolicyDomain
+    domain;
+
+  PolicyRights
+    rights;
+
+  domain=CoderPolicyDomain;
+  rights=ReadPolicyRights;
+  if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
+    return(MagickTrue);
+  return(ReadPSDLayersInternal(image,image_info,psd_info,MagickFalse,
+    exception));
+}
+
 static MagickBooleanType ReadPSDMergedImage(const ImageInfo *image_info,
   Image *image,const PSDInfo *psd_info,ExceptionInfo *exception)
 {
@@ -1844,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);
 
@@ -1866,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;
@@ -1914,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.
@@ -1944,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);
@@ -1957,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.
   */
@@ -1975,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
         {
@@ -2034,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)
     {
@@ -2067,6 +2279,8 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
           "  reading image resource blocks - %.20g bytes",(double)
           ((MagickOffsetType) length));
+      if (length > GetBlobSize(image))
+        ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
       blocks=(unsigned char *) AcquireQuantumMemory((size_t) length,
         sizeof(*blocks));
       if (blocks == (unsigned char *) NULL)
@@ -2078,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);
     }
   /*
@@ -2109,9 +2323,11 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
     }
   else
     {
-      if (ReadPSDLayers(image,image_info,&psd_info,skip_layers,exception) !=
-          MagickTrue)
+      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);
@@ -2120,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));
     }
@@ -2136,16 +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);
-      status=ReadPSDLayers(image,image_info,&psd_info,MagickFalse,exception);
+      (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);
@@ -2156,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));
 }
@@ -2200,13 +2448,15 @@ ModuleExport size_t RegisterPSDImage(void)
   entry->decoder=(DecodeImageHandler *) ReadPSDImage;
   entry->encoder=(EncodeImageHandler *) WritePSDImage;
   entry->magick=(IsImageFormatHandler *) IsPSD;
-  entry->flags|=CoderSeekableStreamFlag;
+  entry->flags|=CoderDecoderSeekableStreamFlag;
+  entry->flags|=CoderEncoderSeekableStreamFlag;
   (void) RegisterMagickInfo(entry);
   entry=AcquireMagickInfo("PSD","PSD","Adobe Photoshop bitmap");
   entry->decoder=(DecodeImageHandler *) ReadPSDImage;
   entry->encoder=(EncodeImageHandler *) WritePSDImage;
   entry->magick=(IsImageFormatHandler *) IsPSD;
-  entry->flags|=CoderSeekableStreamFlag;
+  entry->flags|=CoderDecoderSeekableStreamFlag;
+  entry->flags|=CoderEncoderSeekableStreamFlag;
   (void) RegisterMagickInfo(entry);
   return(MagickImageCoderSignature);
 }
@@ -2269,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);
 }
 
@@ -2295,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);
 }
 
@@ -2430,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;
@@ -2439,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);
 }
 
@@ -2459,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;
 
@@ -2480,6 +2725,9 @@ static size_t WritePSDChannel(const PSDInfo *psd_info,
     count,
     length;
 
+  ssize_t
+    y;
+
   unsigned char
     *pixels;
 
@@ -2504,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;
@@ -2515,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));
@@ -2524,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))
@@ -2546,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);
@@ -2554,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;
@@ -2575,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(
@@ -2611,6 +2859,9 @@ static size_t WritePSDChannels(const PSDInfo *psd_info,
   MagickOffsetType size_offset,const MagickBooleanType separate,
   ExceptionInfo *exception)
 {
+  CompressionType
+    compression;
+
   Image
     *mask;
 
@@ -2630,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)
@@ -2642,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
@@ -2666,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
@@ -2679,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
@@ -2687,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
@@ -2695,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
@@ -2705,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
@@ -2716,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
@@ -2739,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(
@@ -2825,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);
 }
@@ -2874,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;
@@ -2926,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;
       }
@@ -2998,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;
@@ -3024,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;
@@ -3034,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];
@@ -3048,7 +3314,6 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
     *property;
 
   const StringInfo
-    *icc_profile,
     *info;
 
   Image
@@ -3062,9 +3327,6 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
     *layer_size_offsets,
     size_offset;
 
-  PSDInfo
-    psd_info;
-
   register ssize_t
     i;
 
@@ -3073,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;
 
@@ -3106,7 +3576,9 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
   (void) WriteBlobMSBShort(image,psd_info.version);  /* version */
   for (i=1; i <= 6; i++)
     (void) WriteBlobByte(image, 0);  /* 6 bytes of reserved */
-  if (SetImageGray(image,exception) != MagickFalse)
+  /* When the image has a color profile it won't be converted to gray scale */
+  if ((GetImageProfile(image,"icc") == (StringInfo *) NULL) &&
+      (SetImageGray(image,exception) != MagickFalse))
     num_channels=(image->alpha_trait != UndefinedPixelTrait ? 2UL : 1UL);
   else
     if ((image_info->type != TrueColorType) && (image_info->type !=
@@ -3169,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);
     }
@@ -3215,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;
+  if (status != MagickFalse)
+    {
+      MagickOffsetType
+        size_offset;
 
-    unsigned short
-      channels,
-      total_channels;
+      size_t
+        size;
 
-    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;
-
-        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+=WriteBlobMSBLong(image,(const unsigned int) mask->rows+
-          mask->page.y);
-        size+=WriteBlobMSBLong(image,(const unsigned 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.
   */
@@ -3399,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;