File ImageMagick-psd.c-update.patch of Package ImageMagick.8212

Index: ImageMagick-6.8.8-1/coders/psd.c
===================================================================
--- ImageMagick-6.8.8-1.orig/coders/psd.c	2018-02-12 17:15:17.254806314 +0100
+++ ImageMagick-6.8.8-1/coders/psd.c	2018-02-12 18:10:59.147680186 +0100
@@ -20,13 +20,13 @@
 %                                December 2013                                %
 %                                                                             %
 %                                                                             %
-%  Copyright 1999-2014 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://www.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,          %
@@ -44,9 +44,11 @@
 */
 #include "magick/studio.h"
 #include "magick/artifact.h"
+#include "magick/attribute.h"
 #include "magick/blob.h"
 #include "magick/blob-private.h"
 #include "magick/cache.h"
+#include "magick/channel.h"
 #include "magick/colormap.h"
 #include "magick/colormap-private.h"
 #include "magick/colorspace.h"
@@ -63,22 +65,29 @@
 #include "magick/memory_.h"
 #include "magick/module.h"
 #include "magick/monitor-private.h"
+#include "magick/option.h"
 #include "magick/pixel.h"
 #include "magick/pixel-accessor.h"
+#include "magick/policy.h"
 #include "magick/profile.h"
 #include "magick/property.h"
+#include "magick/registry.h"
 #include "magick/quantum-private.h"
 #include "magick/static.h"
 #include "magick/string_.h"
+#include "magick/string-private.h"
+#include "magick/thread-private.h"
 #ifdef MAGICKCORE_ZLIB_DELEGATE
 #include <zlib.h>
 #endif
+
 /*
   Define declaractions.
 */
 #define MaxPSDChannels  56
 #define PSDQuantum(x) (((ssize_t) (x)+1) & -2)
-
+
+
 /*
   Enumerated declaractions.
 */
@@ -101,7 +110,7 @@ typedef enum
   DuotoneMode = 8,
   LabMode = 9
 } PSDImageType;
-
+
 /*
   Typedef declaractions.
 */
@@ -114,64 +123,75 @@ typedef struct _ChannelInfo
     size;
 } ChannelInfo;
 
-typedef struct _LayerInfo
+typedef struct _MaskInfo
 {
+  Image
+    *image;
+
   RectangleInfo
-    page,
-    mask;
+    page;
+
+  unsigned char
+    background,
+    flags;
+} MaskInfo;
+
+typedef struct _PSDInfo
+{
+  char
+    signature[4];
+
+  size_t
+    rows,
+    columns;
+
+  unsigned char
+    reserved[6];
 
   unsigned short
-    channels;
+    channels,
+    depth,
+    mode,
+    version;
+} PSDInfo;
 
+typedef struct _LayerInfo
+{
   ChannelInfo
     channel_info[MaxPSDChannels];
 
   char
     blendkey[4];
 
-  MagickBooleanType
-    has_merged_alpha;
+  Image
+    *image;
+
+  MaskInfo
+    mask;
 
   Quantum
     opacity;
 
-  unsigned char
-    clipping,
-    visible,
-    flags;
+  RectangleInfo
+    page;
 
   size_t
     offset_x,
     offset_y;
 
   unsigned char
-    name[256];
-
-  Image
-    *image;
-} LayerInfo;
-
-typedef struct _PSDInfo
-{
-  char
-    signature[4];
+    clipping,
+    flags,
+    name[256],
+    visible;
 
   unsigned short
-    channels,
-    version;
-
-  unsigned char
-    reserved[6];
+    channels;
 
-  size_t
-    rows,
-    columns;
+  StringInfo
+    *info;
+} LayerInfo;
 
-  unsigned short
-    depth,
-    mode;
-} PSDInfo;
-
 /*
   Forward declarations.
 */
@@ -229,7 +249,7 @@ static MagickBooleanType IsPSD(const uns
 %
 %  The format of the ReadPSDImage method is:
 %
-%      Image *ReadPSDImage(image_info)
+%      Image *ReadPSDImage(image_info,ExceptionInfo *exception)
 %
 %  A description of each parameter follows:
 %
@@ -246,24 +266,93 @@ static const char *CompositeOperatorToPS
 
   switch (op)
   {
-    case OverCompositeOp:    blend_mode = "norm";  break;
-    case MultiplyCompositeOp:  blend_mode = "mul ";  break;
-    case DissolveCompositeOp:  blend_mode = "diss";  break;
-    case DifferenceCompositeOp:  blend_mode = "diff";  break;
-    case DarkenCompositeOp:    blend_mode = "dark";  break;
-    case LightenCompositeOp:  blend_mode = "lite";  break;
-    case HueCompositeOp:    blend_mode = "hue ";  break;
-    case SaturateCompositeOp:  blend_mode = "sat ";  break;
-    case ColorizeCompositeOp:  blend_mode = "colr";  break;
-    case LuminizeCompositeOp:  blend_mode = "lum ";  break;
-    case ScreenCompositeOp:    blend_mode = "scrn";  break;
-    case OverlayCompositeOp:  blend_mode = "over";  break;
-    default:
-      blend_mode = "norm";
+    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 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";  break;
   }
   return(blend_mode);
 }
 
+/*
+For some reason Photoshop seems to blend semi-transparent pixels with white.
+This method reverts the blending. This can be disabled by setting the
+option 'psd:alpha-unblend' to off.
+*/
+static MagickBooleanType CorrectPSDAlphaBlend(const ImageInfo *image_info,
+  Image *image, ExceptionInfo* exception)
+{
+  const char
+    *option;
+
+  MagickBooleanType
+    status;
+
+  ssize_t
+    y;
+
+  if (image->matte == MagickFalse || image->colorspace != sRGBColorspace)
+    return(MagickTrue);
+  option=GetImageOption(image_info,"psd:alpha-unblend");
+  if (IsStringNotFalse(option) == MagickFalse)
+    return(MagickTrue);
+  status=MagickTrue;
+  for (y=0; y < (ssize_t) image->rows; y++)
+  {
+    register PixelPacket
+      *restrict q;
+
+    register ssize_t
+      x;
+
+    if (status == MagickFalse)
+      continue;
+    q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
+    if (q == (PixelPacket *) NULL)
+      {
+        status=MagickFalse;
+        continue;
+      }
+    for (x=0; x < (ssize_t) image->columns; x++)
+    {
+      double
+        gamma;
+
+      gamma=QuantumScale*GetPixelAlpha(q);
+      if (gamma != 0.0 && gamma != 1.0)
+        {
+          SetPixelRed(q,(GetPixelRed(q)-((1.0-gamma)*QuantumRange))/gamma);
+          SetPixelGreen(q,(GetPixelGreen(q)-((1.0-gamma)*QuantumRange))/gamma);
+          SetPixelBlue(q,(GetPixelBlue(q)-((1.0-gamma)*QuantumRange))/gamma);
+        }
+      q++;
+    }
+    if (SyncAuthenticPixels(image,exception) == MagickFalse)
+      status=MagickFalse;
+  }
+
+  return(status);
+}
+
 static inline CompressionType ConvertPSDCompression(
   PSDCompressionType compression)
 {
@@ -279,38 +368,157 @@ static inline CompressionType ConvertPSD
   }
 }
 
-static MagickStatusType CorrectPSDOpacity(LayerInfo* layer_info,
-  ExceptionInfo *exception)
+static MagickBooleanType ApplyPSDLayerOpacity(Image *image,Quantum opacity,
+  MagickBooleanType revert,ExceptionInfo *exception)
 {
-  register PixelPacket
-    *q;
-
-  register ssize_t
-    x;
+  MagickBooleanType
+    status;
 
   ssize_t
     y;
 
-  if (layer_info->opacity == OpaqueOpacity)
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+      "  applying layer opacity %.20g", (double) opacity);
+  if (opacity == QuantumRange)
     return(MagickTrue);
-
-  layer_info->image->matte=MagickTrue;
-  for (y=0; y < (ssize_t) layer_info->image->rows; y++)
+  image->matte=MagickTrue;
+  status=MagickTrue;
+  for (y=0; y < (ssize_t) image->rows; y++)
   {
-    q=GetAuthenticPixels(layer_info->image,0,y,layer_info->image->columns,1,
-      exception);
+    register PixelPacket
+      *restrict q;
+
+    register ssize_t
+      x;
+
+    if (status == MagickFalse)
+      continue;
+    q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
     if (q == (PixelPacket *) NULL)
-      break;
-    for (x=0; x < (ssize_t) layer_info->image->columns; x++)
+      {
+        status=MagickFalse;
+        continue;
+      }
+    for (x=0; x < (ssize_t) image->columns; x++)
     {
-      q->opacity=(Quantum) (QuantumRange-(Quantum) (QuantumScale*(
-        (QuantumRange-q->opacity)*(QuantumRange-layer_info->opacity))));
+      if (revert == MagickFalse)
+        SetPixelAlpha(q,(Quantum) (QuantumScale*(GetPixelAlpha(q)*opacity)));
+      else if (opacity > 0)
+        SetPixelAlpha(q,(Quantum) (QuantumRange*(GetPixelAlpha(q)/
+          (MagickRealType) opacity)));
       q++;
     }
-    if (SyncAuthenticPixels(layer_info->image,exception) == MagickFalse)
-      return(MagickFalse);
+    if (SyncAuthenticPixels(image,exception) == MagickFalse)
+      status=MagickFalse;
+  }
+  return(status);
+}
+
+static MagickBooleanType ApplyPSDOpacityMask(Image *image,const Image *mask,
+  Quantum background,MagickBooleanType revert,ExceptionInfo *exception)
+{
+  Image
+    *complete_mask;
+
+  MagickBooleanType
+    status;
+
+  MagickPixelPacket
+    color;
+
+  ssize_t
+    y;
+
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+      "  applying opacity mask");
+  complete_mask=CloneImage(image,image->columns,image->rows,MagickTrue,
+    exception);
+  if (complete_mask == (Image *) NULL)
+    return(MagickFalse);
+  complete_mask->matte=MagickTrue;
+  GetMagickPixelPacket(complete_mask,&color);
+  color.red=background;
+  SetImageColor(complete_mask,&color);
+  status=CompositeImage(complete_mask,OverCompositeOp,mask,
+    mask->page.x-image->page.x,mask->page.y-image->page.y);
+  if (status == MagickFalse)
+    {
+      complete_mask=DestroyImage(complete_mask);
+      return(status);
+    }
+  image->matte=MagickTrue;
+  for (y=0; y < (ssize_t) image->rows; y++)
+  {
+    register PixelPacket
+      *restrict q;
+
+    register PixelPacket
+      *p;
+
+    register ssize_t
+      x;
+
+    if (status == MagickFalse)
+      continue;
+    q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
+    p=GetAuthenticPixels(complete_mask,0,y,complete_mask->columns,1,exception);
+    if ((q == (PixelPacket *) NULL) || (p == (PixelPacket *) NULL))
+      {
+        status=MagickFalse;
+        continue;
+      }
+    for (x=0; x < (ssize_t) image->columns; x++)
+    {
+      MagickRealType
+        alpha,
+        intensity;
+
+      alpha=GetPixelAlpha(q);
+      intensity=GetPixelIntensity(complete_mask,p);
+      if (revert == MagickFalse)
+        SetPixelAlpha(q,ClampToQuantum(intensity*(QuantumScale*alpha)));
+      else if (intensity > 0)
+        SetPixelAlpha(q,ClampToQuantum((alpha/intensity)*QuantumRange));
+      q++;
+      p++;
+    }
+    if (SyncAuthenticPixels(image,exception) == MagickFalse)
+      status=MagickFalse;
   }
-  return(MagickTrue);
+  complete_mask=DestroyImage(complete_mask);
+  return(status);
+}
+
+static void PreservePSDOpacityMask(Image *image,LayerInfo* layer_info,
+  ExceptionInfo *exception)
+{
+  char
+    *key;
+
+  RandomInfo
+    *random_info;
+
+  StringInfo
+    *key_info;
+
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+      "  preserving opacity mask");
+  random_info=AcquireRandomInfo();
+  key_info=GetRandomKey(random_info,2+1);
+  key=(char *) GetStringInfoDatum(key_info);
+  key[8]=layer_info->mask.background;
+  key[9]='\0';
+  layer_info->mask.image->page.x+=layer_info->page.x;
+  layer_info->mask.image->page.y+=layer_info->page.y;
+  (void) SetImageRegistry(ImageRegistryType,(const char *) key,
+    layer_info->mask.image,exception);
+  (void) SetImageArtifact(layer_info->image,"psd:opacity-mask",
+    (const char *) key);
+  key_info=DestroyStringInfo(key_info);
+  random_info=DestroyRandomInfo(random_info);
 }
 
 static ssize_t DecodePSDPixels(const size_t number_compact_pixels,
@@ -343,8 +551,8 @@ static ssize_t DecodePSDPixels(const siz
   packets=(ssize_t) number_compact_pixels;
   for (i=0; (packets > 1) && (i < (ssize_t) number_pixels); )
   {
-    CheckNumberCompactPixels;
-    length=(*compact_pixels++);
+    packets--;
+    length=(size_t) (*compact_pixels++);
     if (length == 128)
       continue;
     if (length > 128)
@@ -369,13 +577,6 @@ static ssize_t DecodePSDPixels(const siz
               *pixels++=(pixel >> 0) & 0x01 ? 0U : 255U;
               break;
             }
-            case 4:
-            {
-              CheckNumberPixels(2);
-              *pixels++=(unsigned char) ((pixel >> 4) & 0xff);
-              *pixels++=(unsigned char) ((pixel & 0x0f) & 0xff);
-              break;
-            }
             case 2:
             {
               CheckNumberPixels(4);
@@ -385,6 +586,13 @@ static ssize_t DecodePSDPixels(const siz
               *pixels++=(unsigned char) ((pixel & 0x03) & 0x03);
               break;
             }
+            case 4:
+            {
+              CheckNumberPixels(2);
+              *pixels++=(unsigned char) ((pixel >> 4) & 0xff);
+              *pixels++=(unsigned char) ((pixel & 0x0f) & 0xff);
+              break;
+            }
             default:
             {
               CheckNumberPixels(1);
@@ -414,13 +622,6 @@ static ssize_t DecodePSDPixels(const siz
           *pixels++=(*compact_pixels >> 0) & 0x01 ? 0U : 255U;
           break;
         }
-        case 4:
-        {
-          CheckNumberPixels(2);
-          *pixels++=(*compact_pixels >> 4) & 0xff;
-          *pixels++=(*compact_pixels & 0x0f) & 0xff;
-          break;
-        }
         case 2:
         {
           CheckNumberPixels(4);
@@ -430,6 +631,13 @@ static ssize_t DecodePSDPixels(const siz
           *pixels++=(*compact_pixels & 0x03) & 0x03;
           break;
         }
+        case 4:
+        {
+          CheckNumberPixels(2);
+          *pixels++=(*compact_pixels >> 4) & 0xff;
+          *pixels++=(*compact_pixels & 0x0f) & 0xff;
+          break;
+        }
         default:
         {
           CheckNumberPixels(1);
@@ -453,6 +661,10 @@ static inline LayerInfo *DestroyLayerInf
   {
     if (layer_info[i].image != (Image *) NULL)
       layer_info[i].image=DestroyImage(layer_info[i].image);
+    if (layer_info[i].mask.image != (Image *) NULL)
+      layer_info[i].mask.image=DestroyImage(layer_info[i].mask.image);
+    if (layer_info[i].info != (StringInfo *) NULL)
+      layer_info[i].info=DestroyStringInfo(layer_info[i].info);
   }
 
   return (LayerInfo *) RelinquishMagickMemory(layer_info);
@@ -474,11 +686,11 @@ static inline size_t GetPSDPacketSize(Im
   return(1);
 }
 
-static inline MagickSizeType GetPSDSize(PSDInfo *psd_info,Image *image)
+static inline MagickSizeType GetPSDSize(const PSDInfo *psd_info,Image *image)
 {
   if (psd_info->version == 1)
-    return((MagickSizeType) ReadBlobMSBLong(image));
-  return((MagickSizeType) ReadBlobMSBLongLong(image));
+    return((MagickSizeType) ReadBlobLong(image));
+  return((MagickSizeType) ReadBlobLongLong(image));
 }
 
 static inline size_t GetPSDRowSize(Image *image)
@@ -496,6 +708,21 @@ static inline ssize_t MagickAbsoluteValu
   return(x);
 }
 
+static signed int ReadBlobSignedLong(Image *image)
+{
+  union
+  {
+    unsigned int
+      unsigned_value;
+
+    signed int
+      signed_value;
+  } quantum;
+
+  quantum.unsigned_value=ReadBlobLong(image);
+  return(quantum.signed_value);
+}
+
 static const char *ModeToString(PSDImageType type)
 {
   switch (type)
@@ -512,8 +739,9 @@ static const char *ModeToString(PSDImage
   }
 }
 
-static MagickBooleanType ParseImageResourceBlocks(Image *image,
-  const unsigned char *blocks,size_t length)
+static void ParseImageResourceBlocks(Image *image,
+  const unsigned char *blocks,size_t length,
+  MagickBooleanType *has_merged_image)
 {
   const unsigned char
     *p;
@@ -521,30 +749,37 @@ static MagickBooleanType ParseImageResou
   StringInfo
     *profile;
 
+  unsigned char
+    name_length;
+
   unsigned int
-    count,
-    long_sans;
+    count;
 
   unsigned short
     id,
     short_sans;
 
   if (length < 16)
-    return(MagickFalse);
+    return;
   profile=BlobToStringInfo((const void *) NULL,length);
   SetStringInfoDatum(profile,blocks);
   (void) SetImageProfile(image,"8bim",profile);
   profile=DestroyStringInfo(profile);
-  for (p=blocks; (p >= blocks) && (p < (blocks+length-16)); )
+  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))
+      return;
     p=PushLongPixel(MSBEndian,p,&count);
-    if (p+count > blocks+length)
-      return MagickFalse;
+    if ((p+count) > (blocks+length))
+      return;
     switch (id)
     {
       case 0x03ed:
@@ -558,6 +793,8 @@ static MagickBooleanType ParseImageResou
         /*
           Resolution info.
         */
+        if (count < 16)
+          return;
         p=PushShortPixel(MSBEndian,p,&resolution);
         image->x_resolution=(double) resolution;
         (void) FormatLocaleString(value,MaxTextExtent,"%g",
@@ -577,6 +814,13 @@ static MagickBooleanType ParseImageResou
         image->units=PixelsPerInchResolution;
         break;
       }
+      case 0x0421:
+      {
+        if ((count > 3) && (*(p+4) == 0))
+          *has_merged_image=MagickFalse;
+        p+=count;
+        break;
+      }
       default:
       {
         p+=count;
@@ -586,7 +830,7 @@ static MagickBooleanType ParseImageResou
     if ((count & 0x01) != 0)
       p++;
   }
-  return(MagickTrue);
+  return;
 }
 
 static CompositeOperator PSDBlendModeToCompositeOperator(const char *mode)
@@ -618,30 +862,68 @@ static CompositeOperator PSDBlendModeToC
   if (LocaleNCompare(mode,"over",4) == 0)
     return(OverlayCompositeOp);
   if (LocaleNCompare(mode,"hLit",4) == 0)
-    return(OverCompositeOp);
+    return(HardLightCompositeOp);
   if (LocaleNCompare(mode,"sLit",4) == 0)
-    return(OverCompositeOp);
+    return(SoftLightCompositeOp);
   if (LocaleNCompare(mode,"smud",4) == 0)
-    return(OverCompositeOp);
+    return(ExclusionCompositeOp);
   if (LocaleNCompare(mode,"div ",4) == 0)
-    return(OverCompositeOp);
+    return(ColorDodgeCompositeOp);
   if (LocaleNCompare(mode,"idiv",4) == 0)
-    return(OverCompositeOp);
+    return(ColorBurnCompositeOp);
+  if (LocaleNCompare(mode,"lbrn",4) == 0)
+    return(LinearBurnCompositeOp);
+  if (LocaleNCompare(mode,"lddg",4) == 0)
+    return(LinearDodgeCompositeOp);
+  if (LocaleNCompare(mode,"lLit",4) == 0)
+    return(LinearLightCompositeOp);
+  if (LocaleNCompare(mode,"vLit",4) == 0)
+    return(VividLightCompositeOp);
+  if (LocaleNCompare(mode,"pLit",4) == 0)
+    return(PinLightCompositeOp);
   return(OverCompositeOp);
 }
 
+static inline void ReversePSDString(Image *image,char *p,size_t length)
+{
+  char
+    *q;
+
+  if (image->endian == MSBEndian)
+    return;
+
+  q=p+length;
+  for(--q; p < q; ++p, --q)
+  {
+    *p = *p ^ *q,
+    *q = *p ^ *q,
+    *p = *p ^ *q;
+  }
+}
+
 static inline void SetPSDPixel(Image *image,const size_t channels,
   const ssize_t type,const size_t packet_size,const Quantum pixel,
-  PixelPacket *q,IndexPacket *indexes,ssize_t x,ExceptionInfo *exception)
+  PixelPacket *q,IndexPacket *indexes,ssize_t x)
 {
   if (image->storage_class == PseudoClass)
     {
-      if (packet_size == 1)
-        SetPixelIndex(indexes+x,ScaleQuantumToChar(pixel));
+      PixelPacket
+        *color;
+
+      if (type == 0)
+        {
+          if (packet_size == 1)
+            SetPixelIndex(indexes+x,ScaleQuantumToChar(pixel));
+          else
+            SetPixelIndex(indexes+x,ScaleQuantumToShort(pixel));
+        }
+      color=image->colormap+(ssize_t) ConstrainColormapIndex(image,
+        GetPixelIndex(indexes+x));
+      if ((type == 0) && (channels > 1))
+        return;
       else
-        SetPixelIndex(indexes+x,ScaleQuantumToShort(pixel));
-      SetPixelRGBO(q,image->colormap+(ssize_t)
-        ConstrainColormapIndex(image,GetPixelIndex(indexes+x)));
+        SetPixelAlpha(color,pixel);
+      SetPixelRGBO(q,color);
       return;
     }
   switch (type)
@@ -655,7 +937,7 @@ static inline void SetPSDPixel(Image *im
     case 0:
     {
       SetPixelRed(q,pixel);
-      if (channels == 1 || type == -2)
+      if (channels < 3 || type == -2)
         {
           SetPixelGreen(q,GetPixelRed(q));
           SetPixelBlue(q,GetPixelRed(q));
@@ -664,18 +946,12 @@ static inline void SetPSDPixel(Image *im
     }
     case 1:
     {
-      if (image->storage_class == PseudoClass)
-        SetPixelAlpha(q,pixel);
-      else
-        SetPixelGreen(q,pixel);
+      SetPixelGreen(q,pixel);
       break;
     }
     case 2:
     {
-      if (image->storage_class == PseudoClass)
-        SetPixelAlpha(q,pixel);
-      else
-        SetPixelBlue(q,pixel);
+      SetPixelBlue(q,pixel);
       break;
     }
     case 3:
@@ -699,9 +975,9 @@ static inline void SetPSDPixel(Image *im
   }
 }
 
-static MagickStatusType ReadPSDChannelPixels(Image *image,
-  const size_t channels,const size_t row,const ssize_t type,
-  const unsigned char *pixels,ExceptionInfo *exception)
+static MagickBooleanType ReadPSDChannelPixels(Image *image,const size_t channels,
+  const size_t row,const ssize_t type,const unsigned char *pixels,
+  ExceptionInfo *exception)
 {
   Quantum
     pixel;
@@ -741,7 +1017,7 @@ static MagickStatusType ReadPSDChannelPi
       }
     if (image->depth > 1)
       {
-        SetPSDPixel(image,channels,type,packet_size,pixel,q++,indexes,x,exception);
+        SetPSDPixel(image,channels,type,packet_size,pixel,q++,indexes,x);
       }
     else
       {
@@ -754,10 +1030,10 @@ static MagickStatusType ReadPSDChannelPi
           number_bits=8;
         for (bit=0; bit < number_bits; bit++)
         {
-          SetPSDPixel(image,channels,type,packet_size,pixel,q++,indexes,x++,
-            exception);
+          SetPSDPixel(image,channels,type,packet_size,(((unsigned char) pixel)
+            & (0x01 << (7-bit))) != 0 ? 0 : QuantumRange,q++,indexes,x++);
         }
-        if (x != image->columns)
+        if (x != (ssize_t) image->columns)
           x--;
         continue;
       }
@@ -765,10 +1041,10 @@ static MagickStatusType ReadPSDChannelPi
   return(SyncAuthenticPixels(image,exception));
 }
 
-static MagickStatusType ReadPSDChannelRaw(Image *image,const size_t channels,
+static MagickBooleanType ReadPSDChannelRaw(Image *image,const size_t channels,
   const ssize_t type,ExceptionInfo *exception)
 {
-  MagickStatusType
+  MagickBooleanType
     status;
 
   size_t
@@ -786,7 +1062,7 @@ static MagickStatusType ReadPSDChannelRa
        "      layer data is RAW");
 
   row_size=GetPSDRowSize(image);
-  pixels=(unsigned char *) AcquireQuantumMemory(row_size,8*sizeof(*pixels));
+  pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
   if (pixels == (unsigned char *) NULL)
     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
       image->filename);
@@ -809,33 +1085,33 @@ static MagickStatusType ReadPSDChannelRa
   return(status);
 }
 
-static inline MagickOffsetType *ReadPSDRLEOffsets(Image *image,
-  PSDInfo *psd_info,const size_t size)
+static inline MagickOffsetType *ReadPSDRLESizes(Image *image,
+  const PSDInfo *psd_info,const size_t size)
 {
   MagickOffsetType
-    *offsets;
+    *sizes;
 
   ssize_t
     y;
 
-  offsets=(MagickOffsetType *) AcquireQuantumMemory(size,sizeof(*offsets));
-  if(offsets != (MagickOffsetType *) NULL)
+  sizes=(MagickOffsetType *) AcquireQuantumMemory(size,sizeof(*sizes));
+  if(sizes != (MagickOffsetType *) NULL)
     {
       for (y=0; y < (ssize_t) size; y++)
       {
         if (psd_info->version == 1)
-          offsets[y]=(MagickOffsetType) ReadBlobMSBShort(image);
+          sizes[y]=(MagickOffsetType) ReadBlobShort(image);
         else
-          offsets[y]=(MagickOffsetType) ReadBlobMSBLong(image);
+          sizes[y]=(MagickOffsetType) ReadBlobLong(image);
       }
     }
-  return offsets;
+  return sizes;
 }
 
-static MagickStatusType ReadPSDChannelRLE(Image *image,PSDInfo *psd_info,
-  const ssize_t type,MagickOffsetType *offsets,ExceptionInfo *exception)
+static MagickBooleanType ReadPSDChannelRLE(Image *image,const PSDInfo *psd_info,
+  const ssize_t type,MagickOffsetType *sizes,ExceptionInfo *exception)
 {
-  MagickStatusType
+  MagickBooleanType
     status;
 
   size_t
@@ -855,18 +1131,23 @@ static MagickStatusType ReadPSDChannelRL
        "      layer data is RLE compressed");
 
   row_size=GetPSDRowSize(image);
-  pixels=(unsigned char *) AcquireQuantumMemory(row_size,8*sizeof(*pixels));
+  pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
   if (pixels == (unsigned char *) NULL)
     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
       image->filename);
 
   length=0;
   for (y=0; y < (ssize_t) image->rows; y++)
-    if ((MagickOffsetType) length < offsets[y])
-      length=(size_t) offsets[y];
+    if ((MagickOffsetType) length < sizes[y])
+      length=(size_t) sizes[y];
 
-  compact_pixels=(unsigned char *) AcquireQuantumMemory(length,
-    8*sizeof(*pixels));
+  if (length > (row_size+512))
+    {
+      pixels=(unsigned char *) RelinquishMagickMemory(pixels);
+      ThrowBinaryException(ResourceLimitError,"InvalidLength",image->filename);
+    }
+
+  compact_pixels=(unsigned char *) AcquireQuantumMemory(length,sizeof(*pixels));
   if (compact_pixels == (unsigned char *) NULL)
     {
       pixels=(unsigned char *) RelinquishMagickMemory(pixels);
@@ -881,11 +1162,11 @@ static MagickStatusType ReadPSDChannelRL
   {
     status=MagickFalse;
 
-    count=ReadBlob(image,(size_t) offsets[y],compact_pixels);
-    if (count != (ssize_t) offsets[y])
+    count=ReadBlob(image,(size_t) sizes[y],compact_pixels);
+    if (count != (ssize_t) sizes[y])
       break;
 
-    count=DecodePSDPixels((size_t) offsets[y],compact_pixels,
+    count=DecodePSDPixels((size_t) sizes[y],compact_pixels,
       (ssize_t) (image->depth == 1 ? 123456 : image->depth),row_size,pixels);
     if (count != (ssize_t) row_size)
       break;
@@ -901,13 +1182,12 @@ static MagickStatusType ReadPSDChannelRL
   return(status);
 }
 
-static MagickStatusType ReadPSDChannelZip(Image *image,
-  const size_t channels,const ssize_t type,
-  const PSDCompressionType compression,const size_t compact_size,
-  ExceptionInfo *exception)
-{
 #ifdef MAGICKCORE_ZLIB_DELEGATE
-  MagickStatusType
+static MagickBooleanType ReadPSDChannelZip(Image *image,const size_t channels,
+  const ssize_t type,const PSDCompressionType compression,
+  const size_t compact_size,ExceptionInfo *exception)
+{
+  MagickBooleanType
     status;
 
   register unsigned char
@@ -931,7 +1211,7 @@ static MagickStatusType ReadPSDChannelZi
 
   if (image->debug != MagickFalse)
     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-       "      layer data is RLE compressed");
+       "      layer data is ZIP compressed");
 
   compact_pixels=(unsigned char *) AcquireQuantumMemory(compact_size,
     sizeof(*compact_pixels));
@@ -950,32 +1230,37 @@ static MagickStatusType ReadPSDChannelZi
       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(z_stream));
+  ResetMagickMemory(&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=compact_size;
+  stream.avail_in=(uInt) compact_size;
   stream.next_out=(Bytef *)pixels;
-  stream.avail_out=count;
+  stream.avail_out=(uInt) count;
 
-  if(inflateInit(&stream) == Z_OK)
+  if (inflateInit(&stream) == Z_OK)
     {
       int
         ret;
 
       while (stream.avail_out > 0)
       {
-        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);
-        }
+        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;
       }
@@ -985,10 +1270,10 @@ static MagickStatusType ReadPSDChannelZi
   if (compression == ZipWithPrediction)
   {
      p=pixels;
-     while(count > 0)
+     while (count > 0)
      {
        length=image->columns;
-       while(--length)
+       while (--length)
        {
          if (packet_size == 2)
            {
@@ -1018,84 +1303,112 @@ static MagickStatusType ReadPSDChannelZi
   compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
   pixels=(unsigned char *) RelinquishMagickMemory(pixels);
   return(status);
-#else
-  magick_unreferenced(image);
-  magick_unreferenced(channels);
-  magick_unreferenced(type);
-  magick_unreferenced(compression);
-  magick_unreferenced(compact_size);
-  magick_unreferenced(exception);
-  return(MagickFalse);
-#endif
 }
+#endif
 
-static MagickStatusType ReadPSDChannel(Image *image,PSDInfo *psd_info,
-  const LayerInfo* layer_info,const size_t channel,
-  const PSDCompressionType compression,ExceptionInfo *exception)
+static MagickBooleanType ReadPSDChannel(Image *image,
+  const ImageInfo *image_info,const PSDInfo *psd_info,LayerInfo* layer_info,
+  const size_t channel,const PSDCompressionType compression,
+  ExceptionInfo *exception)
 {
+  Image
+    *channel_image,
+    *mask;
+
   MagickOffsetType
     offset;
 
-  MagickStatusType
+  MagickBooleanType
     status;
 
+  channel_image=image;
+  mask=(Image *) NULL;
+  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.
+      */
+      option=GetImageOption(image_info,"psd:preserve-opacity-mask");
+      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);
+      }
+      mask=CloneImage(image,layer_info->mask.page.width,
+        layer_info->mask.page.height,MagickFalse,exception);
+      if (mask != (Image *) NULL)
+        {
+          mask->matte=MagickFalse;
+          channel_image=mask;
+        }
+    }
+
   offset=TellBlob(image);
-  status=MagickTrue;
+  status=MagickFalse;
   switch(compression)
   {
     case Raw:
-      return(ReadPSDChannelRaw(image,psd_info->channels,
-        layer_info->channel_info[channel].type,exception));
+      status=ReadPSDChannelRaw(channel_image,psd_info->channels,
+        layer_info->channel_info[channel].type,exception);
+      break;
     case RLE:
       {
         MagickOffsetType
-          *offsets;
+          *sizes;
 
-        offsets=ReadPSDRLEOffsets(image,psd_info,image->rows);
-        if (offsets == (MagickOffsetType *) NULL)
+        sizes=ReadPSDRLESizes(channel_image,psd_info,channel_image->rows);
+        if (sizes == (MagickOffsetType *) NULL)
           ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
             image->filename);
-        status=ReadPSDChannelRLE(image,psd_info,
-                 layer_info->channel_info[channel].type,offsets,exception);
-        offsets=(MagickOffsetType *) RelinquishMagickMemory(offsets);
+        status=ReadPSDChannelRLE(channel_image,psd_info,
+          layer_info->channel_info[channel].type,sizes,exception);
+        sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
       }
       break;
     case ZipWithPrediction:
     case ZipWithoutPrediction:
 #ifdef MAGICKCORE_ZLIB_DELEGATE
-      status=ReadPSDChannelZip(image,layer_info->channels,
+      status=ReadPSDChannelZip(channel_image,layer_info->channels,
         layer_info->channel_info[channel].type,compression,
         layer_info->channel_info[channel].size-2,exception);
 #else
-      SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
       (void) ThrowMagickException(exception,GetMagickModule(),
           MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
             "'%s' (ZLIB)",image->filename);
 #endif
       break;
     default:
-      SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
       (void) ThrowMagickException(exception,GetMagickModule(),TypeWarning,
         "CompressionNotSupported","'%.20g'",(double) compression);
       break;
   }
 
+  SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
   if (status == MagickFalse)
-    SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
-
+    {
+      if (mask != (Image *) NULL)
+        DestroyImage(mask);
+      ThrowBinaryException(CoderError,"UnableToDecompressImage",
+        image->filename);
+    }
+  layer_info->mask.image=mask;
   return(status);
 }
 
-static MagickStatusType ReadPSDLayer(Image *image,PSDInfo *psd_info,
-  LayerInfo* layer_info,ExceptionInfo *exception)
+static MagickBooleanType ReadPSDLayer(Image *image,const ImageInfo *image_info,
+  const PSDInfo *psd_info,LayerInfo* layer_info,ExceptionInfo *exception)
 {
   char
     message[MaxTextExtent];
 
   MagickBooleanType
-    correct_opacity;
-
-  MagickStatusType
     status;
 
   PSDCompressionType
@@ -1107,20 +1420,24 @@ static MagickStatusType ReadPSDLayer(Ima
   if (image->debug != MagickFalse)
     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
       "    setting up new layer image");
-  (void) SetImageBackgroundColor(layer_info->image);
+  if (psd_info->mode != IndexedMode)
+    (void) SetImageBackgroundColor(layer_info->image);
   layer_info->image->compose=PSDBlendModeToCompositeOperator(
     layer_info->blendkey);
   if (layer_info->visible == MagickFalse)
-    layer_info->image->compose=NoCompositeOp;
+    {
+      layer_info->image->compose=NoCompositeOp;
+      (void) SetImageArtifact(layer_info->image,"psd:layer.invisible","true");
+    }
   if (psd_info->mode == CMYKMode)
     SetImageColorspace(layer_info->image,CMYKColorspace);
-  if ((psd_info->mode == BitmapMode) || (psd_info->mode == GrayscaleMode) ||
-     (psd_info->mode == DuotoneMode))
-   SetImageColorspace(layer_info->image,GRAYColorspace);
+  else if ((psd_info->mode == BitmapMode) || (psd_info->mode == DuotoneMode) ||
+           (psd_info->mode == GrayscaleMode))
+    SetImageColorspace(layer_info->image,GRAYColorspace);
   /*
     Set up some hidden attributes for folks that need them.
   */
-  (void) FormatLocaleString(message,MaxTextExtent,"%.20gld",
+  (void) FormatLocaleString(message,MaxTextExtent,"%.20g",
     (double) layer_info->page.x);
   (void) SetImageArtifact(layer_info->image,"psd:layer.x",message);
   (void) FormatLocaleString(message,MaxTextExtent,"%.20g",
@@ -1132,49 +1449,59 @@ static MagickStatusType ReadPSDLayer(Ima
   (void) SetImageProperty(layer_info->image,"label",(char *) layer_info->name);
 
   status=MagickTrue;
-  correct_opacity=MagickFalse;
   for (j=0; j < (ssize_t) layer_info->channels; j++)
   {
     if (image->debug != MagickFalse)
       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
         "    reading data for channel %.20g",(double) j);
 
-    compression=(PSDCompressionType) ReadBlobMSBShort(layer_info->image);
+    compression=(PSDCompressionType) ReadBlobShort(layer_info->image);
     layer_info->image->compression=ConvertPSDCompression(compression);
-    if (layer_info->has_merged_alpha != MagickFalse &&
-        layer_info->channel_info[j].type == -1)
-      {
-        layer_info->has_merged_alpha=MagickFalse;
-        image->matte=MagickTrue;
-        status=ReadPSDChannel(image,psd_info,layer_info,j,compression,
-          exception);
-      }
-    else
-    {
-      correct_opacity=MagickTrue;
-      if (layer_info->channel_info[j].type == -1)
-        layer_info->image->matte=MagickTrue;
-
-      status=ReadPSDChannel(layer_info->image,psd_info,layer_info,j,
-        compression,exception);
-      InheritException(exception,&layer_info->image->exception);
-    }
+    if (layer_info->channel_info[j].type == -1)
+      layer_info->image->matte=MagickTrue;
+
+    status=ReadPSDChannel(layer_info->image,image_info,psd_info,layer_info,j,
+      compression,exception);
+    InheritException(exception,&layer_info->image->exception);
 
     if (status == MagickFalse)
       break;
   }
 
-  if (status != MagickFalse && correct_opacity != MagickFalse)
-    status=CorrectPSDOpacity(layer_info,exception);
-
-  if (status != MagickFalse && layer_info->image->colorspace == CMYKColorspace)
-   (void) NegateImage(layer_info->image,MagickFalse);
+  if (status != MagickFalse)
+    status=ApplyPSDLayerOpacity(layer_info->image,layer_info->opacity,
+      MagickFalse,exception);
+
+  if ((status != MagickFalse) &&
+      (layer_info->image->colorspace == CMYKColorspace))
+    status=NegateImage(layer_info->image,MagickFalse);
+
+  if (status != MagickFalse && layer_info->mask.image != (Image *) NULL)
+    {
+      const char
+        *option;
+      
+      layer_info->mask.image->page.x=layer_info->mask.page.x;
+      layer_info->mask.image->page.y=layer_info->mask.page.y;
+      /* Do not composite the mask when it is disabled */
+      if ((layer_info->mask.flags & 0x02) == 0x02)
+        layer_info->mask.image->compose=NoCompositeOp;
+      else
+        status=ApplyPSDOpacityMask(layer_info->image,layer_info->mask.image,
+          layer_info->mask.background == 0 ? 0 : QuantumRange,MagickFalse,
+          exception);
+      option=GetImageOption(image_info,"psd:preserve-opacity-mask");
+      if (IsStringTrue(option) != MagickFalse)
+        PreservePSDOpacityMask(image,layer_info,exception);
+      layer_info->mask.image=DestroyImage(layer_info->mask.image);
+    }
 
   return(status);
 }
 
-static MagickStatusType ReadPSDLayers(Image *image,PSDInfo *psd_info,
-  ExceptionInfo *exception)
+static MagickBooleanType ReadPSDLayersInternal(Image *image,
+  const ImageInfo *image_info,const PSDInfo *psd_info,
+  const MagickBooleanType skip_layers,ExceptionInfo *exception)
 {
   char
     type[4];
@@ -1182,13 +1509,10 @@ static MagickStatusType ReadPSDLayers(Im
   LayerInfo
     *layer_info;
 
-  MagickBooleanType
-   has_merged_alpha;
-
   MagickSizeType
     size;
 
-  MagickStatusType
+  MagickBooleanType
     status;
 
   register ssize_t
@@ -1202,53 +1526,50 @@ static MagickStatusType ReadPSDLayers(Im
   size=GetPSDSize(psd_info,image);
   if (size == 0)
     {
-      size_t
-        quantum;
-
       /*
         Skip layers & masks.
       */
-      quantum=psd_info->version == 1 ? 4UL : 8UL;
-      (void) ReadBlobMSBLong(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 (DiscardBlobBytes(image,size-quantum-8) == MagickFalse)
-            ThrowBinaryException(CorruptImageError,
-              "UnexpectedEndOfFile",image->filename);
-        }
+        return(MagickTrue);
       else
         {
           count=ReadBlob(image,4,(unsigned char *) type);
+          ReversePSDString(image,type,4);
           if ((count != 0) && (LocaleNCompare(type,"Lr16",4) == 0))
             size=GetPSDSize(psd_info,image);
           else
-            if (DiscardBlobBytes(image,size-quantum-12) == MagickFalse)
-              ThrowBinaryException(CorruptImageError,
-                "UnexpectedEndOfFile",image->filename);
+            return(MagickTrue);
         }
     }
-
   status=MagickTrue;
   if (size != 0)
     {
       layer_info=(LayerInfo *) NULL;
-      number_layers=(short) ReadBlobMSBShort(image);
+      number_layers=(short) ReadBlobShort(image);
 
-      has_merged_alpha=MagickFalse;
       if (number_layers < 0)
         {
           /*
-            The first alpha channel contains the transparency data for the
-            merged result.
+            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");
-          has_merged_alpha=MagickTrue;
+          image->matte=MagickTrue;
         }
 
+      /*
+        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);
@@ -1272,25 +1593,25 @@ static MagickStatusType ReadPSDLayers(Im
 
       for (i=0; i < number_layers; i++)
       {
-        int
+        ssize_t
           x,
           y;
 
         if (image->debug != MagickFalse)
           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
             "  reading layer #%.20g",(double) i+1);
-        layer_info[i].page.y=(int) ReadBlobMSBLong(image);
-        layer_info[i].page.x=(int) ReadBlobMSBLong(image);
-        y=(int) ReadBlobMSBLong(image);
-        x=(int) ReadBlobMSBLong(image);
-        layer_info[i].page.width=(ssize_t) (x-layer_info[i].page.x);
-        layer_info[i].page.height=(ssize_t) (y-layer_info[i].page.y);
-        layer_info[i].channels=ReadBlobMSBShort(image);
+        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);
+              image->filename);
           }
         if (image->debug != MagickFalse)
           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
@@ -1300,7 +1621,7 @@ static MagickStatusType ReadPSDLayers(Im
             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) ReadBlobMSBShort(image);
+          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)
@@ -1310,6 +1631,7 @@ static MagickStatusType ReadPSDLayers(Im
               (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)
@@ -1319,9 +1641,10 @@ static MagickStatusType ReadPSDLayers(Im
             ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
               image->filename);
           }
-        count=ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey);
-        layer_info[i].opacity=(Quantum) (QuantumRange-ScaleCharToQuantum(
-          (unsigned char) ReadBlobByte(image)));
+        (void) 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);
@@ -1333,7 +1656,7 @@ static MagickStatusType ReadPSDLayers(Im
             layer_info[i].visible ? "true" : "false");
         (void) ReadBlobByte(image);  /* filler */
 
-        size=ReadBlobMSBLong(image);
+        size=ReadBlobLong(image);
         if (size != 0)
           {
             MagickSizeType
@@ -1343,37 +1666,47 @@ static MagickStatusType ReadPSDLayers(Im
             if (image->debug != MagickFalse)
               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                 "    layer contains additional info");
-            length=ReadBlobMSBLong(image);
+            length=ReadBlobLong(image);
             combined_length=length+4;
             if (length != 0)
               {
                 /*
                   Layer mask info.
                 */
-                layer_info[i].mask.y=(int) ReadBlobMSBLong(image);
-                layer_info[i].mask.x=(int) ReadBlobMSBLong(image);
-                layer_info[i].mask.height=(size_t)
-                  (ReadBlobMSBLong(image)-layer_info[i].mask.y);
-                layer_info[i].mask.width=(size_t)
-                  (ReadBlobMSBLong(image)-layer_info[i].mask.x);
+                layer_info[i].mask.page.y=ReadBlobSignedLong(image);
+                layer_info[i].mask.page.x=ReadBlobSignedLong(image);
+                layer_info[i].mask.page.height=(size_t) (ReadBlobSignedLong(image)-
+                  layer_info[i].mask.page.y);
+                layer_info[i].mask.page.width=(size_t) (ReadBlobSignedLong(image)-
+                  layer_info[i].mask.page.x);
+                layer_info[i].mask.background=(unsigned char) ReadBlobByte(
+                  image);
+                layer_info[i].mask.flags=(unsigned char) ReadBlobByte(image);
+                if (!(layer_info[i].mask.flags & 0x01))
+                  {
+                    layer_info[i].mask.page.y=layer_info[i].mask.page.y-
+                      layer_info[i].page.y;
+                    layer_info[i].mask.page.x=layer_info[i].mask.page.x-
+                      layer_info[i].page.x;
+                  }
                 if (image->debug != MagickFalse)
                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                     "      layer mask: offset(%.20g,%.20g), size(%.20g,%.20g), length=%.20g",
-                    (double) layer_info[i].mask.x,(double) 
-                    layer_info[i].mask.y,(double) layer_info[i].mask.width,
-                    (double) layer_info[i].mask.height,(double)
-                    ((MagickOffsetType) length)-16);
+                    (double) layer_info[i].mask.page.x,(double)
+                    layer_info[i].mask.page.y,(double) layer_info[i].mask.page.width,
+                    (double) layer_info[i].mask.page.height,(double)
+                    ((MagickOffsetType) length)-18);
                 /*
                   Skip over the rest of the layer mask information.
                 */
-                if (DiscardBlobBytes(image,length-16) == MagickFalse)
+                if (DiscardBlobBytes(image,(MagickSizeType) (length-18)) == MagickFalse)
                   {
                     layer_info=DestroyLayerInfo(layer_info,number_layers);
                     ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile",
                       image->filename);
                   }
               }
-            length=ReadBlobMSBLong(image);
+            length=ReadBlobLong(image);
             combined_length+=length+4;
             if (length != 0)
               {
@@ -1394,63 +1727,58 @@ static MagickStatusType ReadPSDLayers(Im
             /*
               Layer name.
             */
-            length=(size_t) (unsigned char)ReadBlobByte(image);
-            if (length > GetBlobSize(image))
-              {
-                layer_info=DestroyLayerInfo(layer_info,number_layers);
-                ThrowBinaryException(CorruptImageError,
-                                     "InsufficientImageDataInFile",image->filename);
-              }
+            length=(MagickSizeType) (unsigned char) ReadBlobByte(image);
             combined_length+=length+1;
-            for (j=0; j < (ssize_t) length; j++)
-              layer_info[i].name[j]=(unsigned char) ReadBlobByte(image);
-            layer_info[i].name[j]='\0';
+            if (length > 0)
+              (void) ReadBlob(image,(size_t) length++,layer_info[i].name);
+            layer_info[i].name[length]='\0';
             if (image->debug != MagickFalse)
               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                 "      layer name: %s",layer_info[i].name);
-            /*
-               Skip the rest of the variable data until we support it.
-             */
-             if (image->debug != MagickFalse)
-               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                 "      unsupported data: length=%.20g",(double)
-                 ((MagickOffsetType) (size-combined_length)));
-             if (DiscardBlobBytes(image,size-combined_length) == MagickFalse)
-               {
-                 layer_info=DestroyLayerInfo(layer_info,number_layers);
-                 ThrowBinaryException(CorruptImageError,
-                   "UnexpectedEndOfFile",image->filename);
-               }
+            if ((length % 4) != 0)
+              {
+                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);
+                  }
+              }
+            length=(MagickSizeType) size-combined_length;
+            if (length > 0)
+              {
+                unsigned char
+                  *info;
+
+                if (length > GetBlobSize(image))
+                  {
+                    layer_info=DestroyLayerInfo(layer_info,number_layers);
+                    ThrowBinaryException(CorruptImageError,
+                      "InsufficientImageDataInFile",image->filename);
+                  }
+                layer_info[i].info=AcquireStringInfo((const size_t) length);
+                info=GetStringInfoDatum(layer_info[i].info);
+                (void) ReadBlob(image,(const size_t) length,info);
+              }
           }
       }
 
       for (i=0; i < number_layers; i++)
       {
-        layer_info[i].has_merged_alpha=MagickFalse;
-
         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 (has_merged_alpha)
-          {
-            for (j=0; j < layer_info[i].channels; i++)
-            {
-              if (layer_info[i].channel_info[j].size > 2 &&
-                  layer_info[i].channel_info[j].type == -1)
-                {
-                  layer_info[i].has_merged_alpha=MagickTrue;
-                  break;
-                }
-            }
-            has_merged_alpha=MagickFalse;
-          }
-
         /*
           Allocate layered image.
         */
@@ -1465,79 +1793,109 @@ static MagickStatusType ReadPSDLayers(Im
             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);
+            layer_info[i].info=DestroyStringInfo(layer_info[i].info);
+          }
       }
 
-      for (i=0; i < number_layers; i++)
-      {
-        if (layer_info[i].image == (Image *) NULL)
+      if (image_info->ping == MagickFalse)
         {
-          for (j=0; j < layer_info[i].channels; j++)
+          for (i=0; i < number_layers; i++)
           {
-            if (DiscardBlobBytes(image,layer_info[i].channel_info[j].size) ==
-                  MagickFalse)
+            if (layer_info[i].image == (Image *) NULL)
               {
-                layer_info=DestroyLayerInfo(layer_info,number_layers);
-                ThrowBinaryException(CorruptImageError,
-                  "UnexpectedEndOfFile",image->filename);
+                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;
               }
-          }
-          continue;
-        }
-
-        if (image->debug != MagickFalse)
-          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-            "  reading data for layer %.20g",(double) i);
 
-        status=ReadPSDLayer(image,psd_info,&layer_info[i],exception);
-        if (status == MagickFalse)
-          break;
+            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;
-      }
+            status=SetImageProgress(image,LoadImagesTag,i,(MagickSizeType)
+              number_layers);
+            if (status == MagickFalse)
+              break;
+          }
+        }
 
       if (status != MagickFalse)
-      {
-        for (i=0; i < number_layers; i++)
         {
-          if (layer_info[i].image == (Image *) NULL)
+          for (i=0; i < number_layers; i++)
           {
-            for (j=i; j < number_layers - 1; j++)
-              layer_info[j] = layer_info[j+1];
-            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)
-          {
-            for (i=0; i < number_layers; i++)
+          if (number_layers > 0)
             {
-              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;
+              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;
             }
-            image->next=layer_info[0].image;
-            layer_info[0].image->previous=image;
-          }
-      }
-      layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
+          layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
+        }
+      else
+        layer_info=DestroyLayerInfo(layer_info,number_layers);
     }
 
   return(status);
 }
 
-static MagickStatusType ReadPSDMergedImage(Image* image,PSDInfo* psd_info,
-  ExceptionInfo *exception)
+ModuleExport MagickBooleanType ReadPSDLayers(Image *image,
+  const ImageInfo *image_info,const PSDInfo *psd_info,
+  const MagickBooleanType skip_layers,ExceptionInfo *exception)
+{
+  PolicyDomain
+    domain;
+
+  PolicyRights
+    rights;
+
+  domain=CoderPolicyDomain;
+  rights=ReadPolicyRights;
+  if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
+    return(MagickFalse);
+  return(ReadPSDLayersInternal(image,image_info,psd_info,skip_layers,
+    exception));
+}
+
+static MagickBooleanType ReadPSDMergedImage(const ImageInfo *image_info,
+  Image* image,const PSDInfo* psd_info,ExceptionInfo *exception)
 {
   MagickOffsetType
-    *offsets;
+    *sizes;
 
-  MagickStatusType
+  MagickBooleanType
     status;
 
   PSDCompressionType
@@ -1556,49 +1914,50 @@ static MagickStatusType ReadPSDMergedIma
       return(MagickFalse);
     }
 
-  offsets=(MagickOffsetType *) NULL;
+  sizes=(MagickOffsetType *) NULL;
   if (compression == RLE)
-  {
-    offsets=ReadPSDRLEOffsets(image,psd_info,image->rows*psd_info->channels);
-    if (offsets == (MagickOffsetType *) NULL)
-      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
-        image->filename);
-  }
+    {
+      sizes=ReadPSDRLESizes(image,psd_info,image->rows*psd_info->channels);
+      if (sizes == (MagickOffsetType *) NULL)
+        ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+          image->filename);
+    }
 
   status=MagickTrue;
   for (i=0; i < (ssize_t) psd_info->channels; i++)
   {
     if (compression == RLE)
-      status=ReadPSDChannelRLE(image,psd_info,i,offsets+(i*image->rows),
+      status=ReadPSDChannelRLE(image,psd_info,i,sizes+(i*image->rows),
         exception);
     else
       status=ReadPSDChannelRaw(image,psd_info->channels,i,exception);
 
-    if (status == MagickFalse)
-      break;
-    status=SetImageProgress(image,LoadImagesTag,i,psd_info->channels);
+    if (status != MagickFalse)
+      status=SetImageProgress(image,LoadImagesTag,i,psd_info->channels);
+
     if (status == MagickFalse)
       break;
   }
 
-  if (image->colorspace == CMYKColorspace)
-    (void) NegateImage(image,MagickFalse);
+  if ((status != MagickFalse) && (image->colorspace == CMYKColorspace))
+    status=NegateImage(image,MagickFalse);
+
+  if (status != MagickFalse)
+    status=CorrectPSDAlphaBlend(image_info,image,exception);
 
-  if (offsets != (MagickOffsetType *) NULL)
-    offsets=(MagickOffsetType *) RelinquishMagickMemory(offsets);
+  sizes=(MagickOffsetType *) RelinquishMagickMemory(sizes);
 
   return(status);
 }
 
-static Image *ReadPSDImage(const ImageInfo *image_info,
-  ExceptionInfo *exception)
+static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
 {
   Image
     *image;
 
   MagickBooleanType
-    skip_layers,
-    status;
+    has_merged_image,
+    skip_layers;
 
   MagickOffsetType
     offset;
@@ -1606,6 +1965,9 @@ static Image *ReadPSDImage(const ImageIn
   MagickSizeType
     length;
 
+  MagickBooleanType
+    status;
+
   PSDInfo
     psd_info;
 
@@ -1639,12 +2001,13 @@ static Image *ReadPSDImage(const ImageIn
   /*
     Read image header.
   */
+  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) ||
       ((psd_info.version != 1) && (psd_info.version != 2)))
     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
-  count=ReadBlob(image,6,psd_info.reserved);
+  (void) ReadBlob(image,6,psd_info.reserved);
   psd_info.channels=ReadBlobMSBShort(image);
   if (psd_info.channels > MaxPSDChannels)
     ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded");
@@ -1671,6 +2034,12 @@ static Image *ReadPSDImage(const ImageIn
   image->depth=psd_info.depth;
   image->columns=psd_info.columns;
   image->rows=psd_info.rows;
+  status=SetImageExtent(image,image->columns,image->rows);
+  if (status == MagickFalse)
+    {
+      InheritException(exception,&image->exception);
+      return(DestroyImageList(image));
+    }
   if (SetImageBackgroundColor(image) == MagickFalse)
     {
       InheritException(exception,&image->exception);
@@ -1680,9 +2049,12 @@ static Image *ReadPSDImage(const ImageIn
   if (psd_info.mode == LabMode)
     SetImageColorspace(image,LabColorspace);
   if (psd_info.mode == CMYKMode)
-    SetImageColorspace(image,CMYKColorspace);
-  if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) ||
-      (psd_info.mode == DuotoneMode))
+    {
+      SetImageColorspace(image,CMYKColorspace);
+      image->matte=psd_info.channels > 4 ? MagickTrue : MagickFalse;
+    }
+  else if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) ||
+           (psd_info.mode == DuotoneMode))
     {
       status=AcquireImageColormap(image,psd_info.depth != 16 ? 256 : 65536);
       if (status == MagickFalse)
@@ -1691,8 +2063,10 @@ static Image *ReadPSDImage(const ImageIn
         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
           "  Image colormap allocated");
       SetImageColorspace(image,GRAYColorspace);
+      image->matte=psd_info.channels > 1 ? MagickTrue : MagickFalse;
     }
-  image->matte=MagickFalse;
+  else
+    image->matte=psd_info.channels > 3 ? MagickTrue : MagickFalse;
   /*
     Read PSD raster colormap only present for indexed and duotone images.
   */
@@ -1711,7 +2085,7 @@ static Image *ReadPSDImage(const ImageIn
             sizeof(*data));
           if (data == (unsigned char *) NULL)
             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
-          count=ReadBlob(image,(size_t) length,data);
+          (void) ReadBlob(image,(size_t) length,data);
           data=(unsigned char *) RelinquishMagickMemory(data);
         }
       else
@@ -1741,6 +2115,7 @@ static Image *ReadPSDImage(const ImageIn
     }
   if ((image->depth == 1) && (image->storage_class != PseudoClass))
     ThrowReaderException(CorruptImageError, "ImproperImageHeader");
+  has_merged_image=MagickTrue;
   length=ReadBlobMSBLong(image);
   if (length != 0)
     {
@@ -1756,7 +2131,7 @@ static Image *ReadPSDImage(const ImageIn
           ((MagickOffsetType) length));
       if (length > GetBlobSize(image))
         ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
-      blocks=(unsigned char *) AcquireQuantumMemory((size_t) length+16,
+      blocks=(unsigned char *) AcquireQuantumMemory((size_t) length,
         sizeof(*blocks));
       if (blocks == (unsigned char *) NULL)
         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
@@ -1767,20 +2142,9 @@ static Image *ReadPSDImage(const ImageIn
           blocks=(unsigned char *) RelinquishMagickMemory(blocks);
           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
         }
-      (void) ParseImageResourceBlocks(image,blocks,(size_t) length);
+      ParseImageResourceBlocks(image,blocks,(size_t) length,&has_merged_image);
       blocks=(unsigned char *) RelinquishMagickMemory(blocks);
     }
-   /*
-     If we are only "pinging" the image, then we're done - so return.
-   */
-  if (EOFBlob(image) != MagickFalse)
-    ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
-  if (image_info->ping != MagickFalse)
-    {
-      (void) CloseBlob(image);
-      image=DestroyImageList(image);
-      return(GetFirstImageInList(image));
-    }
   /*
     Layer and mask block.
   */
@@ -1792,7 +2156,8 @@ static Image *ReadPSDImage(const ImageIn
     }
   offset=TellBlob(image);
   skip_layers=MagickFalse;
-  if ((image_info->number_scenes == 1) && (image_info->scene == 0))
+  if ((image_info->number_scenes == 1) && (image_info->scene == 0) &&
+      (has_merged_image != MagickFalse))
     {
       if (image->debug != MagickFalse)
         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
@@ -1807,25 +2172,62 @@ static Image *ReadPSDImage(const ImageIn
     }
   else
     {
-      if (skip_layers == MagickFalse)
-        if (ReadPSDLayers(image,&psd_info,exception) != MagickTrue)
-          {
-            ThrowReaderException(image->exception.severity, image->exception.reason);
-          }
-
+      if (ReadPSDLayersInternal(image,image_info,&psd_info,skip_layers,
+            exception) != MagickTrue)
+        {
+          (void) CloseBlob(image);
+          image=DestroyImageList(image);
+          return((Image *) NULL);
+        }
       /*
          Skip the rest of the layer and mask information.
       */
       SeekBlob(image,offset+length,SEEK_SET);
     }
-
+  /*
+    If we are only "pinging" the image, then we're done - so return.
+  */
+  if (EOFBlob(image) != MagickFalse)
+    ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
+  if (image_info->ping != MagickFalse)
+    {
+      (void) CloseBlob(image);
+      return(GetFirstImageInList(image));
+    }
   /*
     Read the precombined layer, present for PSD < 4 compatibility.
   */
   if (image->debug != MagickFalse)
     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
       "  reading the precombined layer");
-  (void) ReadPSDMergedImage(image,&psd_info,exception);
+  if (has_merged_image != MagickFalse || GetImageListLength(image) == 1)
+    has_merged_image=(MagickBooleanType) ReadPSDMergedImage(image_info,image,
+      &psd_info,exception);
+  if ((has_merged_image == MagickFalse) && (GetImageListLength(image) == 1) &&
+      (length != 0))
+    {
+      SeekBlob(image,offset,SEEK_SET);
+      status=ReadPSDLayersInternal(image,image_info,&psd_info,MagickFalse,
+        exception);
+      if (status != MagickTrue)
+        {
+          (void) CloseBlob(image);
+          image=DestroyImageList(image);
+          return((Image *) NULL);
+        }
+    }
+  if (has_merged_image == MagickFalse)
+    {
+      Image
+        *merged;
+
+      if (GetImageListLength(image) == 1)
+        ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
+      SetImageAlphaChannel(image,TransparentAlphaChannel);
+      image->background_color.opacity=TransparentOpacity;
+      merged=MergeImageLayers(image,FlattenLayer,exception);
+      ReplaceImageInList(&image,merged);
+    }
   (void) CloseBlob(image);
   return(GetFirstImageInList(image));
 }
@@ -1932,7 +2334,26 @@ static inline ssize_t SetPSDOffset(const
 {
   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)
+{
+  MagickSizeType
+    current_offset;
+
+  ssize_t
+    result;
+
+  current_offset=TellBlob(image);
+  SeekBlob(image,offset,SEEK_SET);
+  if (psd_info->version == 1)
+    result=WriteBlobMSBShort(image,(unsigned short) size);
+  else
+    result=WriteBlobMSBLong(image,(unsigned int) size);
+  SeekBlob(image,current_offset,SEEK_SET);
+  return(result);
 }
 
 static inline ssize_t SetPSDSize(const PSDInfo *psd_info,Image *image,
@@ -1943,6 +2364,25 @@ static inline ssize_t SetPSDSize(const P
   return(WriteBlobMSBLongLong(image,size));
 }
 
+static inline ssize_t WritePSDSize(const PSDInfo *psd_info,Image *image,
+  const MagickSizeType size,const MagickSizeType offset)
+{
+  MagickSizeType
+    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);
+  return(result);
+}
+
 static size_t PSDPackbitsEncodeImage(Image *image,const size_t length,
   const unsigned char *pixels,unsigned char *compact_pixels)
 {
@@ -1967,6 +2407,7 @@ static size_t PSDPackbitsEncodeImage(Ima
   if (image->debug != MagickFalse)
     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   assert(pixels != (unsigned char *) NULL);
+  assert(compact_pixels != (unsigned char *) NULL);
   packbits=(unsigned char *) AcquireQuantumMemory(128UL,sizeof(*packbits));
   if (packbits == (unsigned char *) NULL)
     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
@@ -2052,48 +2493,36 @@ static size_t PSDPackbitsEncodeImage(Ima
   return((size_t) (q-compact_pixels));
 }
 
-static void WritePackbitsLength(const PSDInfo *psd_info,
-  const ImageInfo *image_info,Image *image,Image *next_image,
-  unsigned char *compact_pixels,const QuantumType quantum_type)
+static size_t WriteCompressionStart(const PSDInfo *psd_info,Image *image,
+  const Image *next_image,const ssize_t channels)
 {
-  QuantumInfo
-    *quantum_info;
-
-  register const PixelPacket
-    *p;
-
   size_t
-    length,
-    packet_size;
+    length;
 
   ssize_t
+    i,
     y;
 
-  unsigned char
-    *pixels;
-
-  if (next_image->depth > 8)
-    next_image->depth=16;
-  packet_size=next_image->depth > 8UL ? 2UL : 1UL;
-  (void) packet_size;
-  quantum_info=AcquireQuantumInfo(image_info,image);
-  pixels=GetQuantumPixels(quantum_info);
-  for (y=0; y < (ssize_t) next_image->rows; y++)
-  {
-    p=GetVirtualPixels(next_image,0,y,next_image->columns,1,&image->exception);
-    if (p == (const PixelPacket *) NULL)
-      break;
-    length=ExportQuantumPixels(next_image,(CacheView *) NULL,quantum_info,
-      quantum_type,pixels,&image->exception);
-    length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels);
-    (void) SetPSDOffset(psd_info,image,length);
-  }
-  quantum_info=DestroyQuantumInfo(quantum_info);
+  if (next_image->compression == RLECompression)
+    {
+      length=WriteBlobMSBShort(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);
+#endif
+  else
+    length=WriteBlobMSBShort(image,Raw);
+  return(length);
 }
 
-static void WriteOneChannel(const PSDInfo *psd_info,const ImageInfo *image_info,
-  Image *image,Image *next_image,unsigned char *compact_pixels,
-  const QuantumType quantum_type,const MagickBooleanType compression_flag)
+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)
 {
   int
     y;
@@ -2111,24 +2540,65 @@ static void WriteOneChannel(const PSDInf
     i;
 
   size_t
-    length,
-    packet_size;
+    count,
+    length;
 
   unsigned char
     *pixels;
 
-  (void) psd_info;
-  if ((compression_flag != MagickFalse) &&
-      (next_image->compression != RLECompression))
-    (void) WriteBlobMSBShort(image,0);
+#ifdef MAGICKCORE_ZLIB_DELEGATE
+
+#define CHUNK 16384
+
+  int
+    flush,
+    level;
+
+  unsigned char
+    *compressed_pixels;
+
+  z_stream
+    stream;
+
+  compressed_pixels=(unsigned char *) NULL;
+  flush=Z_NO_FLUSH;
+#endif
+  count=0;
+  if (separate != MagickFalse)
+    {
+      size_offset=TellBlob(image)+2;
+      count+=WriteCompressionStart(psd_info,image,next_image,1);
+    }
   if (next_image->depth > 8)
     next_image->depth=16;
   monochrome=IsMonochromeImage(image,&image->exception) && (image->depth == 1)
     ? MagickTrue : MagickFalse;
-  packet_size=next_image->depth > 8UL ? 2UL : 1UL;
-  (void) packet_size;
   quantum_info=AcquireQuantumInfo(image_info,next_image);
+  if (quantum_info == (QuantumInfo *) NULL)
+    return(0);
   pixels=GetQuantumPixels(quantum_info);
+#ifdef MAGICKCORE_ZLIB_DELEGATE
+  if (next_image->compression == ZipCompression)
+    {
+      compressed_pixels=(unsigned char *) AcquireQuantumMemory(CHUNK,
+        sizeof(*compressed_pixels));
+      if (compressed_pixels == (unsigned char *) NULL)
+        {
+          quantum_info=DestroyQuantumInfo(quantum_info);
+          return(0);
+        }
+      ResetMagickMemory(&stream,0,sizeof(stream));
+      stream.data_type=Z_BINARY;
+      level=Z_DEFAULT_COMPRESSION;
+      if ((image_info->quality > 0 && image_info->quality < 10))
+        level=(int) image_info->quality;
+      if (deflateInit(&stream,level) != Z_OK)
+        {
+          quantum_info=DestroyQuantumInfo(quantum_info);
+          return(0);
+        }
+    }
+#endif
   for (y=0; y < (ssize_t) next_image->rows; y++)
   {
     p=GetVirtualPixels(next_image,0,y,next_image->columns,1,&image->exception);
@@ -2139,149 +2609,219 @@ static void WriteOneChannel(const PSDInf
     if (monochrome != MagickFalse)
       for (i=0; i < (ssize_t) length; i++)
         pixels[i]=(~pixels[i]);
-    if (next_image->compression != RLECompression)
-      (void) WriteBlob(image,length,pixels);
-    else
+    if (next_image->compression == RLECompression)
       {
         length=PSDPackbitsEncodeImage(image,length,pixels,compact_pixels);
-        (void) WriteBlob(image,length,compact_pixels);
+        count+=WriteBlob(image,length,compact_pixels);
+        size_offset+=WritePSDOffset(psd_info,image,length,size_offset);
       }
+#ifdef MAGICKCORE_ZLIB_DELEGATE
+    else if (next_image->compression == ZipCompression)
+      {
+        stream.avail_in=(uInt) length;
+        stream.next_in=(Bytef *) pixels;
+        if (y == (ssize_t) next_image->rows-1)
+          flush=Z_FINISH;
+        do {
+            stream.avail_out=(uInt) CHUNK;
+            stream.next_out=(Bytef *) compressed_pixels;
+            if (deflate(&stream,flush) == Z_STREAM_ERROR)
+              break;
+            length=(size_t) CHUNK-stream.avail_out;
+            if (length > 0)
+              count+=WriteBlob(image,length,compressed_pixels);
+        } while (stream.avail_out == 0);
+      }
+#endif
+    else
+      count+=WriteBlob(image,length,pixels);
   }
+#ifdef MAGICKCORE_ZLIB_DELEGATE
+  if (next_image->compression == ZipCompression)
+    {
+      (void) deflateEnd(&stream);
+      compressed_pixels=(unsigned char *) RelinquishMagickMemory(
+        compressed_pixels);
+    }
+#endif
   quantum_info=DestroyQuantumInfo(quantum_info);
+  return(count);
 }
 
-static MagickBooleanType WriteImageChannels(const PSDInfo *psd_info,
+static unsigned char *AcquireCompactPixels(Image *image)
+{
+  size_t
+    packet_size;
+
+  unsigned char
+    *compact_pixels;
+
+  packet_size=image->depth > 8UL ? 2UL : 1UL;
+  compact_pixels=(unsigned char *) AcquireQuantumMemory((9*
+    image->columns)+1,packet_size*sizeof(*compact_pixels));
+  if (compact_pixels == (unsigned char *) NULL)
+    {
+      (void) ThrowMagickException(&image->exception,GetMagickModule(),
+        ResourceLimitError,"MemoryAllocationFailed","`%s'",
+        image->filename);
+    }
+  return(compact_pixels);
+}
+
+static ssize_t WritePSDChannels(const PSDInfo *psd_info,
   const ImageInfo *image_info,Image *image,Image *next_image,
-  const MagickBooleanType separate)
+  MagickOffsetType size_offset,const MagickBooleanType separate)
 {
-  int
-    i;
+  Image
+    *mask;
+
+  MagickOffsetType
+    rows_offset;
 
   size_t
     channels,
-    packet_size;
+    count,
+    length,
+    offset_length;
 
   unsigned char
     *compact_pixels;
 
-  /*
-    Write uncompressed pixels as separate planes.
-  */
-  channels=1;
-  packet_size=next_image->depth > 8UL ? 2UL : 1UL;
+  count=0;
+  offset_length=0;
+  rows_offset=0;
   compact_pixels=(unsigned char *) NULL;
   if (next_image->compression == RLECompression)
     {
-      compact_pixels=(unsigned char *) AcquireQuantumMemory((9*channels*
-        next_image->columns)+1,packet_size*sizeof(*compact_pixels));
+      compact_pixels=AcquireCompactPixels(next_image);
       if (compact_pixels == (unsigned char *) NULL)
-        ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
+        return(0);
     }
-  i=0;
-  if (IsGrayImage(next_image,&next_image->exception) != MagickFalse)
+  channels=1;
+  if (separate == MagickFalse)
     {
-      if (next_image->compression == RLECompression)
+      if (next_image->storage_class != PseudoClass)
         {
-          /*
-            Packbits compression.
-          */
-          (void) WriteBlobMSBShort(image,1);
-          WritePackbitsLength(psd_info,image_info,image,next_image,
-            compact_pixels,GrayQuantum);
+          if (IsGrayImage(next_image,&next_image->exception) == MagickFalse)
+            channels=next_image->colorspace == CMYKColorspace ? 4 : 3;
           if (next_image->matte != MagickFalse)
-            WritePackbitsLength(psd_info,image_info,image,next_image,
-              compact_pixels,AlphaQuantum);
+            channels++;
         }
-      WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
-        GrayQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
-        MagickFalse);
-      if (next_image->matte != MagickFalse)
-        WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
-          AlphaQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
-          MagickFalse);
-      (void) SetImageProgress(image,SaveImagesTag,0,1);
+      rows_offset=TellBlob(image)+2;
+      count+=WriteCompressionStart(psd_info,image,next_image,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);
+      if (separate != MagickFalse)
+        size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
+      else
+        rows_offset+=offset_length;
+      count+=length;
     }
   else
-    if (next_image->storage_class == PseudoClass)
-      {
-        if (next_image->compression == RLECompression)
-          {
-            /*
-              Packbits compression.
-            */
-            (void) WriteBlobMSBShort(image,1);
-            WritePackbitsLength(psd_info,image_info,image,next_image,
-              compact_pixels,IndexQuantum);
-            if (next_image->matte != MagickFalse)
-              WritePackbitsLength(psd_info,image_info,image,next_image,
-                compact_pixels,AlphaQuantum);
-          }
-        WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
-          IndexQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
-          MagickFalse);
-        if (next_image->matte != MagickFalse)
-          WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
-            AlphaQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
-            MagickFalse);
-        (void) SetImageProgress(image,SaveImagesTag,0,1);
-      }
-    else
-      {
-        if (next_image->colorspace == CMYKColorspace)
-          (void) NegateImage(next_image,MagickFalse);
-        if (next_image->compression == RLECompression)
-          {
-            /*
-              Packbits compression.
-            */
-            (void) WriteBlobMSBShort(image,1);
-            WritePackbitsLength(psd_info,image_info,image,next_image,
-              compact_pixels,RedQuantum);
-            WritePackbitsLength(psd_info,image_info,image,next_image,
-              compact_pixels,GreenQuantum);
-            WritePackbitsLength(psd_info,image_info,image,next_image,
-              compact_pixels,BlueQuantum);
-            if (next_image->colorspace == CMYKColorspace)
-              WritePackbitsLength(psd_info,image_info,image,next_image,
-                compact_pixels,BlackQuantum);
-            if (next_image->matte != MagickFalse)
-              WritePackbitsLength(psd_info,image_info,image,next_image,
-                compact_pixels,AlphaQuantum);
-          }
-        (void) SetImageProgress(image,SaveImagesTag,0,6);
-        WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
-          RedQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
-          MagickFalse);
-        (void) SetImageProgress(image,SaveImagesTag,1,6);
-        WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
-          GreenQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
-          MagickFalse);
-        (void) SetImageProgress(image,SaveImagesTag,2,6);
-        WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
-          BlueQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
-          MagickFalse);
-        (void) SetImageProgress(image,SaveImagesTag,3,6);
-        if (next_image->colorspace == CMYKColorspace)
-          WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
-            BlackQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
-            MagickFalse);
-        (void) SetImageProgress(image,SaveImagesTag,4,6);
-        if (next_image->matte != MagickFalse)
-          WriteOneChannel(psd_info,image_info,image,next_image,compact_pixels,
-            AlphaQuantum,(i++ == 0) || (separate != MagickFalse) ? MagickTrue :
-            MagickFalse);
-        (void) SetImageProgress(image,SaveImagesTag,5,6);
-        if (next_image->colorspace == CMYKColorspace)
-          (void) NegateImage(next_image,MagickFalse);
-      }
-  if (next_image->compression == RLECompression)
-    compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
-  return(MagickTrue);
+    {
+      if (IsGrayImage(next_image,&next_image->exception) != MagickFalse)
+        {
+          length=WritePSDChannel(psd_info,image_info,image,next_image,
+            GrayQuantum,compact_pixels,rows_offset,separate);
+          if (separate != MagickFalse)
+            size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
+          else
+            rows_offset+=offset_length;
+          count+=length;
+        }
+      else
+        {
+          if (next_image->colorspace == CMYKColorspace)
+            (void) NegateImage(next_image,MagickFalse);
+
+          length=WritePSDChannel(psd_info,image_info,image,next_image,
+            RedQuantum,compact_pixels,rows_offset,separate);
+          if (separate != MagickFalse)
+            size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
+          else
+            rows_offset+=offset_length;
+          count+=length;
+
+          length=WritePSDChannel(psd_info,image_info,image,next_image,
+            GreenQuantum,compact_pixels,rows_offset,separate);
+          if (separate != MagickFalse)
+            size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
+          else
+            rows_offset+=offset_length;
+          count+=length;
+
+          length=WritePSDChannel(psd_info,image_info,image,next_image,
+            BlueQuantum,compact_pixels,rows_offset,separate);
+          if (separate != MagickFalse)
+            size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
+          else
+            rows_offset+=offset_length;
+          count+=length;
+
+          if (next_image->colorspace == CMYKColorspace)
+            {
+              length=WritePSDChannel(psd_info,image_info,image,next_image,
+                BlackQuantum,compact_pixels,rows_offset,separate);
+              if (separate != MagickFalse)
+                size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
+              else
+                rows_offset+=offset_length;
+              count+=length;
+            }
+        }
+      if (next_image->matte != MagickFalse)
+        {
+          length=WritePSDChannel(psd_info,image_info,image,next_image,
+            AlphaQuantum,compact_pixels,rows_offset,separate);
+          if (separate != MagickFalse)
+            size_offset+=WritePSDSize(psd_info,image,length,size_offset)+2;
+          else
+            rows_offset+=offset_length;
+          count+=length;
+        }
+    }
+  compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
+  if (next_image->colorspace == CMYKColorspace)
+    (void) NegateImage(next_image,MagickFalse);
+  if (separate != MagickFalse)
+    {
+      const char
+        *property;
+
+      property=GetImageArtifact(next_image,"psd:opacity-mask");
+      if (property != (const char *) NULL)
+        {
+          mask=(Image *) GetImageRegistry(ImageRegistryType,property,
+            &image->exception);
+          if (mask != (Image *) NULL)
+            {
+              if (mask->compression == RLECompression)
+                {
+                  compact_pixels=AcquireCompactPixels(mask);
+                  if (compact_pixels == (unsigned char *) NULL)
+                    return(0);
+                }
+              length=WritePSDChannel(psd_info,image_info,image,mask,
+                RedQuantum,compact_pixels,rows_offset,MagickTrue);
+              (void) WritePSDSize(psd_info,image,length,size_offset);
+              count+=length;
+              compact_pixels=(unsigned char *) RelinquishMagickMemory(
+                compact_pixels);
+            }
+        }
+    }
+  return(count);
 }
 
-static void WritePascalString(Image* inImage,const char *inString,int inPad)
+static size_t WritePascalString(Image *image,const char *value,size_t padding)
 {
   size_t
+    count,
     length;
 
   register ssize_t
@@ -2290,19 +2830,21 @@ static void WritePascalString(Image* inI
   /*
     Max length is 255.
   */
-  length=(strlen(inString) > 255UL ) ? 255UL : strlen(inString);
+  count=0;
+  length=(strlen(value) > 255UL ) ? 255UL : strlen(value);
   if (length ==  0)
-    (void) WriteBlobByte(inImage,0);
+    count+=WriteBlobByte(image,0);
   else
     {
-      (void) WriteBlobByte(inImage,(unsigned char) length);
-      (void) WriteBlob(inImage, length, (const unsigned char *) inString);
+      count+=WriteBlobByte(image,(unsigned char) length);
+      count+=WriteBlob(image,length,(const unsigned char *) value);
     }
   length++;
-  if ((length % inPad) == 0)
-    return;
-  for (i=0; i < (ssize_t) (inPad-(length % inPad)); i++)
-    (void) WriteBlobByte(inImage,0);
+  if ((length % padding) == 0)
+    return(count);
+  for (i=0; i < (ssize_t) (padding-(length % padding)); i++)
+    count+=WriteBlobByte(image,0);
+  return(count);
 }
 
 static void WriteResolutionResourceBlock(Image *image)
@@ -2314,15 +2856,18 @@ static void WriteResolutionResourceBlock
   unsigned short
     units;
 
-  x_resolution=65536.0*image->x_resolution+0.5;
-  y_resolution=65536.0*image->y_resolution+0.5;
-  units=1;
   if (image->units == PixelsPerCentimeterResolution)
     {
-      x_resolution=2.54*65536.0*image->x_resolution*0.5;
+      x_resolution=2.54*65536.0*image->x_resolution+0.5;
       y_resolution=2.54*65536.0*image->y_resolution+0.5;
       units=2;
     }
+  else
+    {
+      x_resolution=65536.0*image->x_resolution+0.5;
+      y_resolution=65536.0*image->y_resolution+0.5;
+      units=1;
+    }
   (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
   (void) WriteBlobMSBShort(image,0x03ED);
   (void) WriteBlobMSBShort(image,0);
@@ -2335,6 +2880,17 @@ static void WriteResolutionResourceBlock
   (void) WriteBlobMSBShort(image,units); /* height unit */
 }
 
+static inline size_t WriteChannelSize(const PSDInfo *psd_info,Image *image,
+  const signed short channel)
+{
+  size_t
+    count;
+
+  count=WriteBlobMSBShort(image,channel);
+  count+=SetPSDSize(psd_info,image,0);
+  return(count);
+}
+
 static void RemoveICCProfileFromResourceBlock(StringInfo *bim_profile)
 {
   register const unsigned char
@@ -2372,11 +2928,15 @@ static void RemoveICCProfileFromResource
     p=PushLongPixel(MSBEndian,p,&count);
     if (id == 0x0000040f)
       {
-        if ((q+PSDQuantum(count)+12) < (datum+length-16))
+        ssize_t
+          quantum;
+
+        quantum=PSDQuantum(count)+12;
+        if ((quantum >= 12) && (quantum < (ssize_t) length))
           {
-            (void) CopyMagickMemory(q,q+PSDQuantum(count)+12,length-
-              (PSDQuantum(count)+12)-(q-datum));
-            SetStringInfoLength(bim_profile,length-(PSDQuantum(count)+12));
+            if ((q+quantum < (datum+length-16)))
+              (void) CopyMagickMemory(q,q+quantum,length-quantum-(q-datum));
+            SetStringInfoLength(bim_profile,length-quantum);
           }
         break;
       }
@@ -2439,13 +2999,118 @@ static void RemoveResolutionFromResource
   }
 }
 
-static MagickBooleanType WritePSDImage(const ImageInfo *image_info,Image *image)
+static const StringInfo *GetAdditionalInformation(const ImageInfo *image_info,
+  Image *image)
+{
+#define PSDKeySize 5
+#define PSDAllowedLength 36
+
+  char
+    key[PSDKeySize];
+
+  /* Whitelist of keys from: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ */
+  const char
+    allowed[PSDAllowedLength][PSDKeySize] = {
+      "blnc", "blwh", "brit", "brst", "clbl", "clrL", "curv", "expA", "FMsk",
+      "GdFl", "grdm", "hue ", "hue2", "infx", "knko", "lclr", "levl", "lnsr",
+      "lfx2", "luni", "lrFX", "lspf", "lyid", "lyvr", "mixr", "nvrt", "phfl",
+      "post", "PtFl", "selc", "shpa", "sn2P", "SoCo", "thrs", "tsly", "vibA"
+    },
+    *option;
+
+  const StringInfo
+    *info;
+
+  MagickBooleanType
+    found;
+
+  register size_t
+    i;
+
+  size_t
+    remaining_length,
+    length;
+
+  StringInfo
+    *profile;
+
+  unsigned char
+    *p;
+
+  unsigned int
+    size;
+
+  info=GetImageProfile(image,"psd:additional-info");
+  if (info == (const StringInfo *) NULL)
+    return((const StringInfo *) NULL);
+  option=GetImageOption(image_info,"psd:additional-info");
+  if (LocaleCompare(option,"all") == 0)
+    return(info);
+  if (LocaleCompare(option,"selective") != 0)
+    {
+      profile=RemoveImageProfile(image,"psd:additional-info");
+      return(DestroyStringInfo(profile));
+    }
+  length=GetStringInfoLength(info);
+  p=GetStringInfoDatum(info);
+  remaining_length=length;
+  length=0;
+  while (remaining_length >= 12)
+  {
+    /* skip over signature */
+    p+=4;
+    key[0]=(*p++);
+    key[1]=(*p++);
+    key[2]=(*p++);
+    key[3]=(*p++);
+    key[4]='\0';
+    size=(unsigned int) (*p++) << 24;
+    size|=(unsigned int) (*p++) << 16;
+    size|=(unsigned int) (*p++) << 8;
+    size|=(unsigned int) (*p++);
+    size=size & 0xffffffff;
+    remaining_length-=12;
+    if ((size_t) size > remaining_length)
+      return((const StringInfo *) NULL);
+    found=MagickFalse;
+    for (i=0; i < PSDAllowedLength; i++)
+    {
+      if (LocaleNCompare(key,allowed[i],PSDKeySize) != 0)
+        continue;
+
+      found=MagickTrue;
+      break;
+    }
+    remaining_length-=(size_t) size;
+    if (found == MagickFalse)
+      {
+        if (remaining_length > 0)
+          p=(unsigned char *) CopyMagickMemory(p-12,p+size,remaining_length);
+        continue;
+      }
+    length+=(size_t) size+12;
+    p+=size;
+  }
+  profile=RemoveImageProfile(image,"psd:additional-info");
+  if (length == 0)
+    return(DestroyStringInfo(profile));
+  SetStringInfoLength(profile,(const size_t) length);
+  SetImageProfile(image,"psd:additional-info",info);
+  return(profile);
+}
+
+static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
+  Image *image)
 {
+  char
+    layer_name[MaxTextExtent];
+
   const char
     *property;
 
   const StringInfo
-    *icc_profile;
+    *icc_profile,
+    *info;
 
   Image
     *base_image,
@@ -2454,6 +3119,10 @@ static MagickBooleanType WritePSDImage(c
   MagickBooleanType
     status;
 
+  MagickOffsetType
+    *layer_size_offsets,
+    size_offset;
+
   PSDInfo
     psd_info;
 
@@ -2461,14 +3130,14 @@ static MagickBooleanType WritePSDImage(c
     i;
 
   size_t
-    channel_size,
-    channelLength,
     layer_count,
-    layer_info_size,
+    layer_index,
     length,
+    name_length,
     num_channels,
     packet_size,
-    rounded_layer_info_size;
+    rounded_size,
+    size;
 
   StringInfo
     *bim_profile;
@@ -2496,13 +3165,18 @@ static MagickBooleanType WritePSDImage(c
   (void) WriteBlobMSBShort(image,psd_info.version);  /* version */
   for (i=1; i <= 6; i++)
     (void) WriteBlobByte(image, 0);  /* 6 bytes of reserved */
-  if (IsGrayImage(image,&image->exception) != MagickFalse)
+  /* When the image has a color profile it won't be converted to gray scale */
+  if ((GetImageProfile(image,"icc") == (StringInfo *) NULL) &&
+      (IsGrayImage(image,&image->exception) != MagickFalse))
     num_channels=(image->matte != MagickFalse ? 2UL : 1UL);
   else
-    if (image->storage_class == PseudoClass)
+    if ((image_info->type != TrueColorType) && (image_info->type !=
+         TrueColorMatteType) && (image->storage_class == PseudoClass))
       num_channels=(image->matte != MagickFalse ? 2UL : 1UL);
     else
       {
+        if (image->storage_class == PseudoClass)
+          (void) SetImageStorageClass(image,DirectClass);
         if (image->colorspace != CMYKColorspace)
           num_channels=(image->matte != MagickFalse ? 4UL : 3UL);
         else
@@ -2534,8 +3208,7 @@ static MagickBooleanType WritePSDImage(c
            (image->colorspace != CMYKColorspace)) &&
           (image_info->colorspace != CMYKColorspace))
         {
-          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
-            (void) TransformImageColorspace(image,sRGBColorspace);
+          (void) TransformImageColorspace(image,sRGBColorspace);
           (void) WriteBlobMSBShort(image,(unsigned short)
             (image->storage_class == PseudoClass ? IndexedMode : RGBMode));
         }
@@ -2606,171 +3279,193 @@ static MagickBooleanType WritePSDImage(c
           PSDQuantum(GetStringInfoLength(icc_profile)))
         (void) WriteBlobByte(image,0);
     }
-  layer_count=0;
-  layer_info_size=2;
   base_image=GetNextImageInList(image);
-  if ((image->matte != MagickFalse) && (base_image == (Image *) NULL))
+  if (base_image == (Image *)NULL)
     base_image=image;
-  next_image=base_image;
-  while ( next_image != NULL )
+  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; )
   {
-    packet_size=next_image->depth > 8 ? 2UL : 1UL;
-    if (IsGrayImage(next_image,&image->exception) != MagickFalse)
-      num_channels=next_image->matte != MagickFalse ? 2UL : 1UL;
+    layer_count++;
+    next_image=GetNextImageInList(next_image);
+  }
+  if (image->matte != MagickFalse)
+    size+=WriteBlobMSBShort(image,-(unsigned short) layer_count);
+  else
+    size+=WriteBlobMSBShort(image,(unsigned short) layer_count);
+  layer_size_offsets=(MagickOffsetType *) AcquireQuantumMemory(
+    (size_t) layer_count,sizeof(MagickOffsetType));
+  if (layer_size_offsets == (MagickOffsetType *) NULL)
+    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
+  layer_index=0;
+  for (next_image=base_image; next_image != NULL; )
+  {
+    Image
+      *mask;
+
+    unsigned char
+      default_color;
+
+    unsigned short
+      channels,
+      total_channels;
+
+    mask=(Image *) NULL;
+    property=GetImageArtifact(next_image,"psd:opacity-mask");
+    default_color=0;
+    if (property != (const char *) NULL)
+      {
+        mask=(Image *) GetImageRegistry(ImageRegistryType,property,
+          &image->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) &&
+        (IsGrayImage(next_image,&next_image->exception) == MagickFalse))
+      channels=next_image->colorspace == CMYKColorspace ? 4U : 3U;
+    total_channels=channels;
+    if (next_image->matte != MagickFalse)
+      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->matte != MagickFalse)
+      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,
+          &image->exception);
+      }
     else
-      if (next_image->storage_class == PseudoClass)
-        num_channels=next_image->matte != MagickFalse ? 2UL : 1UL;
-      else
-        if (next_image->colorspace != CMYKColorspace)
-          num_channels=next_image->matte != MagickFalse ? 4UL : 3UL;
-        else
-          num_channels=next_image->matte != MagickFalse ? 5UL : 4UL;
-    channelLength=(size_t) (next_image->columns*next_image->rows*packet_size+2);
-    layer_info_size+=(size_t) (4*4+2+num_channels*6+(psd_info.version == 1 ? 8 :
-      16)+4*1+4+num_channels*channelLength);
+      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);
     property=(const char *) GetImageProperty(next_image,"label");
     if (property == (const char *) NULL)
-      layer_info_size+=16;
+      {
+        (void) FormatLocaleString(layer_name,MaxTextExtent,"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
       {
-        size_t
-          length;
-
-        length=strlen(property);
-        layer_info_size+=8+length+(4-(length % 4));
+        if (mask->compose != NoCompositeOp)
+          (void) ApplyPSDOpacityMask(next_image,mask,ScaleCharToQuantum(
+            default_color),MagickTrue,&image->exception);
+        mask->page.y+=image->page.y;
+        mask->page.x+=image->page.x;
+        size+=WriteBlobMSBLong(image,20);
+        size+=WriteBlobMSBLong(image,mask->page.y);
+        size+=WriteBlobMSBLong(image,mask->page.x);
+        size+=WriteBlobMSBLong(image,(const signed int) mask->rows+
+          mask->page.y);
+        size+=WriteBlobMSBLong(image,(const signed int) mask->columns+
+          mask->page.x);
+        size+=WriteBlobByte(image,default_color);
+        size+=WriteBlobByte(image,mask->compose == NoCompositeOp ? 2 : 0);
+        size+=WriteBlobMSBShort(image,0);
+      }
+    size+=WriteBlobMSBLong(image,0);
+    size+=WritePascalString(image,property,4);
+    if (info != (const StringInfo *) NULL)
+      size+=WriteBlob(image,GetStringInfoLength(info),
+        GetStringInfoDatum(info));
+    next_image=GetNextImageInList(next_image);
+  }
+  /*
+    Now the image data!
+  */
+  next_image=base_image;
+  layer_index=0;
+  while (next_image != NULL)
+  {
+    length=WritePSDChannels(&psd_info,image_info,image,next_image,
+      layer_size_offsets[layer_index++],MagickTrue);
+    if (length == 0)
+      {
+        status=MagickFalse;
+        break;
       }
-    layer_count++;
+    size+=length;
+    next_image=GetNextImageInList(next_image);
+  }
+  (void) WriteBlobMSBLong(image,0);  /* user mask data */
+  /*
+    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);
   }
-  if (layer_count == 0)
-    (void) SetPSDSize(&psd_info,image,0);
+  /*
+    Write the total size
+  */
+  size_offset+=WritePSDSize(&psd_info,image,size+
+    (psd_info.version == 1 ? 8 : 12),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);
+  /*
+    Write composite image.
+  */
+  if (status != MagickFalse)
     {
       CompressionType
         compression;
 
-      (void) SetPSDSize(&psd_info,image,layer_info_size+
-        (psd_info.version == 1 ? 8 : 16));
-      if ((layer_info_size/2) != ((layer_info_size+1)/2))
-        rounded_layer_info_size=layer_info_size+1;
-      else
-        rounded_layer_info_size=layer_info_size;
-      (void) SetPSDSize(&psd_info,image,rounded_layer_info_size);
-      (void) WriteBlobMSBShort(image,(unsigned short) layer_count);
-      layer_count=1;
-      compression=base_image->compression;
-      next_image=base_image;
-      while (next_image != NULL)
-      {
-        next_image->compression=NoCompression;
-        (void) WriteBlobMSBLong(image,(unsigned int) next_image->page.y);
-        (void) WriteBlobMSBLong(image,(unsigned int) next_image->page.x);
-        (void) WriteBlobMSBLong(image,(unsigned int) next_image->page.y+
-          next_image->rows);
-        (void) WriteBlobMSBLong(image,(unsigned int) next_image->page.x+
-          next_image->columns);
-        packet_size=next_image->depth > 8 ? 2UL : 1UL;
-        channel_size=(unsigned int) ((packet_size*next_image->rows*
-          next_image->columns)+2);
-        if ((IsGrayImage(next_image,&image->exception) != MagickFalse) ||
-            (next_image->storage_class == PseudoClass))
-          {
-             (void) WriteBlobMSBShort(image,(unsigned short)
-               (next_image->matte != MagickFalse ? 2 : 1));
-             (void) WriteBlobMSBShort(image,0);
-             (void) SetPSDSize(&psd_info,image,channel_size);
-             if (next_image->matte != MagickFalse)
-               {
-                 (void) WriteBlobMSBShort(image,(unsigned short) -1);
-                 (void) SetPSDSize(&psd_info,image,channel_size);
-               }
-           }
-          else
-            if (next_image->colorspace != CMYKColorspace)
-              {
-                (void) WriteBlobMSBShort(image,(unsigned short)
-                  (next_image->matte != MagickFalse ? 4 : 3));
-               (void) WriteBlobMSBShort(image,0);
-               (void) SetPSDSize(&psd_info,image,channel_size);
-               (void) WriteBlobMSBShort(image,1);
-               (void) SetPSDSize(&psd_info,image,channel_size);
-               (void) WriteBlobMSBShort(image,2);
-               (void) SetPSDSize(&psd_info,image,channel_size);
-               if (next_image->matte!= MagickFalse )
-                 {
-                   (void) WriteBlobMSBShort(image,(unsigned short) -1);
-                   (void) SetPSDSize(&psd_info,image,channel_size);
-                 }
-             }
-           else
-             {
-               (void) WriteBlobMSBShort(image,(unsigned short)
-                 (next_image->matte ? 5 : 4));
-               (void) WriteBlobMSBShort(image,0);
-               (void) SetPSDSize(&psd_info,image,channel_size);
-               (void) WriteBlobMSBShort(image,1);
-               (void) SetPSDSize(&psd_info,image,channel_size);
-               (void) WriteBlobMSBShort(image,2);
-               (void) SetPSDSize(&psd_info,image,channel_size);
-               (void) WriteBlobMSBShort(image,3);
-               (void) SetPSDSize(&psd_info,image,channel_size);
-               if (next_image->matte)
-                 {
-                   (void) WriteBlobMSBShort(image,(unsigned short) -1);
-                   (void) SetPSDSize(&psd_info,image,channel_size);
-                 }
-             }
-        (void) WriteBlob(image,4,(const unsigned char *) "8BIM");
-        (void) WriteBlob(image,4,(const unsigned char *)
-          CompositeOperatorToPSDBlendMode(next_image->compose));
-        (void) WriteBlobByte(image,255); /* layer opacity */
-        (void) WriteBlobByte(image,0);
-        (void) WriteBlobByte(image,1); /* layer propertys - visible, etc. */
-        (void) WriteBlobByte(image,0);
-        property=(const char *) GetImageProperty(next_image,"label");
-        if (property == (const char *) NULL)
-          {
-            char
-              layer_name[MaxTextExtent];
-
-            (void) WriteBlobMSBLong(image,16);
-            (void) WriteBlobMSBLong(image,0);
-            (void) WriteBlobMSBLong(image,0);
-            (void) FormatLocaleString(layer_name,MaxTextExtent,"L%06ld",(long)
-              layer_count++);
-            WritePascalString(image,layer_name,4);
-          }
-        else
-          {
-            size_t
-              length;
-
-            length=strlen(property);
-            (void) WriteBlobMSBLong(image,(unsigned int) (length+(4-
-              (length % 4))+8));
-            (void) WriteBlobMSBLong(image,0);
-            (void) WriteBlobMSBLong(image,0);
-            WritePascalString(image,property,4);
-          }
-        next_image=GetNextImageInList(next_image);
-      }
-      /*
-        Now the image data!
-      */
-      next_image=base_image;
-      while (next_image != NULL)
-      {
-        status=WriteImageChannels(&psd_info,image_info,image,next_image,
-          MagickTrue);
-        next_image=GetNextImageInList(next_image);
-      }
-      (void) WriteBlobMSBLong(image,0);  /* user mask data */
-      base_image->compression=compression;
+      compression=image->compression;
+      if (image->compression == ZipCompression)
+        image->compression=RLECompression;
+      if (WritePSDChannels(&psd_info,image_info,image,image,0,
+          MagickFalse) == 0)
+        status=MagickFalse;
+      image->compression=compression;
     }
-  /*
-    Write composite image.
-  */
-  status=WriteImageChannels(&psd_info,image_info,image,image,MagickFalse);
   (void) CloseBlob(image);
   return(status);
 }