]> granicus.if.org Git - imagemagick/commitdiff
Added support for reading zip compressed PSD files.
authordirk <dirk@git.imagemagick.org>
Thu, 26 Dec 2013 16:47:58 +0000 (16:47 +0000)
committerdirk <dirk@git.imagemagick.org>
Thu, 26 Dec 2013 16:47:58 +0000 (16:47 +0000)
coders/psd.c

index 962d1ec05b444482b164259a18497f4bd18d7a22..a6cd545933cbe665fe0e9d1c0bcd58d776911451 100644 (file)
@@ -16,6 +16,8 @@
 %                                   Cristy                                    %
 %                              Leonard Rosenthol                              %
 %                                 July 1992                                   %
+%                                Dirk Lemstra                                 %
+%                                December 2013                                %
 %                                                                             %
 %                                                                             %
 %  Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization      %
@@ -68,6 +70,9 @@
 #include "MagickCore/quantum-private.h"
 #include "MagickCore/static.h"
 #include "MagickCore/string_.h"
+#ifdef MAGICKCORE_ZLIB_DELEGATE
+#include <zlib.h>
+#endif
 \f
 /*
   Define declaractions.
 /*
   Enumerated declaractions.
 */
+typedef enum
+{
+  Raw = 0,
+  RLE = 1,
+  ZipWithoutPrediction = 2,
+  ZipWithPrediction = 3
+} PSDCompressionType;
+
 typedef enum
 {
   BitmapMode = 0,
@@ -117,6 +130,9 @@ typedef struct _LayerInfo
   char
     blendkey[4];
 
+  MagickBooleanType
+    has_merged_alpha;
+
   Quantum
     opacity;
 
@@ -143,7 +159,6 @@ typedef struct _PSDInfo
 
   unsigned short
     channels,
-    color_channels,
     version;
 
   unsigned char
@@ -250,6 +265,56 @@ static const char *CompositeOperatorToPSDBlendMode(CompositeOperator op)
   return(blend_mode);
 }
 
+static inline CompressionType ConvertPSDCompression(
+  PSDCompressionType compression)
+{
+  switch (compression)
+  {
+    case RLE:
+      return RLECompression;
+    case ZipWithPrediction:
+    case ZipWithoutPrediction:
+      return ZipCompression;
+    default:
+      return NoCompression;
+  }
+}
+
+static MagickStatusType CorrectPSDOpacity(LayerInfo* layer_info,
+  ExceptionInfo *exception)
+{
+  register Quantum
+    *q;
+
+  register ssize_t
+    x;
+
+  ssize_t
+    y;
+
+  if (layer_info->opacity == OpaqueAlpha)
+    return(MagickTrue);
+
+  layer_info->image->alpha_trait=BlendPixelTrait;
+  for (y=0; y < (ssize_t) layer_info->image->rows; y++)
+  {
+    q=GetAuthenticPixels(layer_info->image,0,y,
+      layer_info->image->columns,1,exception);
+    if (q == (Quantum *) NULL)
+      break;
+    for (x=0; x < (ssize_t) layer_info->image->columns; x++)
+    {
+      SetPixelAlpha(layer_info->image,(Quantum) (QuantumScale*(GetPixelAlpha(
+        layer_info->image,q))*layer_info->opacity),q);
+      q+=GetPixelChannels(layer_info->image);
+    }
+    if (SyncAuthenticPixels(layer_info->image,exception) == MagickFalse)
+      return(MagickFalse);
+  }
+
+  return(MagickTrue);
+}
+
 static ssize_t DecodePSDPixels(const size_t number_compact_pixels,
   const unsigned char *compact_pixels,const ssize_t depth,
   const size_t number_pixels,unsigned char *pixels)
@@ -369,11 +434,20 @@ static ssize_t DecodePSDPixels(const size_t number_compact_pixels,
   return(i);
 }
 
-static inline MagickOffsetType GetPSDOffset(PSDInfo *psd_info,Image *image)
+static inline size_t GetPSDPacketSize(Image *image)
 {
-  if (psd_info->version == 1)
-    return((MagickOffsetType) ReadBlobMSBShort(image));
-  return((MagickOffsetType) ReadBlobMSBLong(image));
+  if (image->storage_class == PseudoClass)
+    {
+      if (image->colors > 256)
+        return(2);
+      else if (image->depth > 8)
+        return(2);
+    }
+  else
+    if (image->depth > 8)
+      return(2);
+
+  return(1);
 }
 
 static inline MagickSizeType GetPSDSize(PSDInfo *psd_info,Image *image)
@@ -383,6 +457,14 @@ static inline MagickSizeType GetPSDSize(PSDInfo *psd_info,Image *image)
   return((MagickSizeType) ReadBlobMSBLongLong(image));
 }
 
+static inline size_t GetPSDRowSize(Image *image)
+{
+  if (image->depth == 1)
+    return((image->columns+7)/8);
+  else
+    return(image->columns*GetPSDPacketSize(image));
+}
+
 static inline ssize_t MagickAbsoluteValue(const ssize_t x)
 {
   if (x < 0)
@@ -520,12 +602,10 @@ static CompositeOperator PSDBlendModeToCompositeOperator(const char *mode)
   return(OverCompositeOp);
 }
 
-static MagickBooleanType ReadPSDLayer(Image *image,const size_t channels,
-  const ssize_t type,const MagickOffsetType *offsets,ExceptionInfo *exception)
+static MagickStatusType ReadPSDChannelPixels(Image *image,
+  const size_t channels,const size_t row,const ssize_t type,
+  const unsigned char *pixels,ExceptionInfo *exception)
 {
-  ColorspaceType
-    colorspace;
-
   Quantum
     pixel;
 
@@ -541,273 +621,998 @@ static MagickBooleanType ReadPSDLayer(Image *image,const size_t channels,
   size_t
     packet_size;
 
-  ssize_t
-    count,
-    y;
-
-  unsigned char
-    *compact_pixels,
-    *pixels;
-
   unsigned short
     nibble;
 
-  packet_size=1;
-  if (image->storage_class == PseudoClass)
-    {
-      if (image->colors > 256)
-        packet_size++;
-      else
-        if (image->depth > 8)
-          packet_size++;
-    }
-  else
-    if (image->depth > 8)
-      packet_size++;
-  pixels=(unsigned char *) AcquireQuantumMemory(image->columns+256,packet_size*
-    sizeof(*pixels));
-  if (pixels == (unsigned char *) NULL)
-    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
-      image->filename);
-  (void) ResetMagickMemory(pixels,0,image->columns*packet_size*sizeof(*pixels));
-  compact_pixels=(unsigned char *) NULL;
-  if (image->compression == RLECompression)
-    {
-      size_t
-        length;
-
-      length=0;
-      for (y=0; y < (ssize_t) image->rows; y++)
-        if ((MagickOffsetType) length < offsets[y])
-          length=(size_t) offsets[y];
-      compact_pixels=(unsigned char *) AcquireQuantumMemory(length+256,
-        sizeof(*pixels));
-      if (compact_pixels == (unsigned char *) NULL)
-        ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
-          image->filename);
-      (void) ResetMagickMemory(compact_pixels,0,length*sizeof(*compact_pixels));
-    }
-  colorspace=image->colorspace;
-  for (y=0; y < (ssize_t) image->rows; y++)
+  p=pixels;
+  q=GetAuthenticPixels(image,0,row,image->columns,1,exception);
+  if (q == (Quantum *) NULL)
+    return MagickFalse;
+  packet_size=GetPSDPacketSize(image);
+  for (x=0; x < (ssize_t) image->columns; x++)
   {
-    if (image->depth == 1)
+    if (packet_size == 1)
+      pixel=ScaleCharToQuantum(*p++);
+    else
+      {
+        p=PushShortPixel(MSBEndian,p,&nibble);
+        pixel=ScaleShortToQuantum(nibble);
+      }
+    switch (type)
+    {
+      case -1:
+      {
+        SetPixelAlpha(image,pixel,q);
+        break;
+      }
+      case 0:
       {
-        if (image->compression != RLECompression)
-          count=ReadBlob(image,(image->columns+7)/8,pixels);
+        SetPixelRed(image,pixel,q);
+        if (channels == 1)
+          SetPixelGray(image,pixel,q);
         else
+          SetPixelRed(image,pixel,q);
+        if (image->storage_class == PseudoClass)
           {
-            count=ReadBlob(image,(size_t) offsets[y],compact_pixels);
-            if (count != (ssize_t) offsets[y])
-              break;
-            count=DecodePSDPixels((size_t) offsets[y],compact_pixels,
-              (ssize_t) 123456,(size_t) ((image->columns+7)/8),pixels);
+            if (packet_size == 1)
+              SetPixelIndex(image,ScaleQuantumToChar(pixel),q);
+            else
+              SetPixelIndex(image,ScaleQuantumToShort(pixel),q);
+            SetPixelInfoPixel(image,image->colormap+(ssize_t)
+              GetPixelIndex(image,q),q);
+            if (image->depth == 1)
+              {
+                ssize_t
+                  bit,
+                  number_bits;
+  
+                number_bits=image->columns-x;
+                if (number_bits > 8)
+                  number_bits=8;
+                for (bit=0; bit < number_bits; bit++)
+                {
+                  SetPixelIndex(image,(((unsigned char) pixel) &
+                    (0x01 << (7-bit))) != 0 ? 0 : 255,q);
+                  SetPixelInfoPixel(image,image->colormap+(ssize_t)
+                    GetPixelIndex(image,q),q);
+                  q+=GetPixelChannels(image);
+                  x++;
+                }
+              }
           }
-        if (count < (ssize_t) ((image->columns+7)/8))
-          break;
+        break;
       }
-    else
+      case 1:
       {
-        if (image->compression != RLECompression)
-          count=ReadBlob(image,packet_size*image->columns,pixels);
+        if (image->storage_class == PseudoClass)
+          SetPixelAlpha(image,pixel,q);
         else
-          {
-            count=ReadBlob(image,(size_t) offsets[y],compact_pixels);
-            if (count != (ssize_t) offsets[y])
-              break;
-            count=DecodePSDPixels((size_t) offsets[y],compact_pixels,
-              (ssize_t) image->depth,packet_size*image->columns,pixels);
-          }
-        if (count < (ssize_t) (packet_size*image->columns))
-          break;
+          SetPixelGreen(image,pixel,q);
+        break;
       }
-    q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
-    if (q == (Quantum *) NULL)
-      break;
-    p=pixels;
-    for (x=0; x < (ssize_t) image->columns; x++)
-    {
-      if (packet_size == 1)
-        pixel=ScaleCharToQuantum(*p++);
-      else
-        {
-          p=PushShortPixel(MSBEndian,p,&nibble);
-          pixel=ScaleShortToQuantum(nibble);
-        }
-      switch (type)
+      case 2:
       {
-        case -1:
-        {
+        if (image->storage_class == PseudoClass)
           SetPixelAlpha(image,pixel,q);
-          break;
-        }
-        case 0:
-        {
-          SetPixelRed(image,pixel,q);
-          if (channels == 1)
-            SetPixelGray(image,pixel,q);
-          else
-            SetPixelRed(image,pixel,q);
-          if (image->storage_class == PseudoClass)
-            {
-              if (packet_size == 1)
-                SetPixelIndex(image,ScaleQuantumToChar(pixel),q);
-              else
-                SetPixelIndex(image,ScaleQuantumToShort(pixel),q);
-              SetPixelInfoPixel(image,image->colormap+(ssize_t)
-                GetPixelIndex(image,q),q);
-              if (image->depth == 1)
-                {
-                  ssize_t
-                    bit,
-                    number_bits;
-
-                  number_bits=image->columns-x;
-                  if (number_bits > 8)
-                    number_bits=8;
-                  for (bit=0; bit < number_bits; bit++)
-                  {
-                    SetPixelIndex(image,(((unsigned char) pixel) &
-                      (0x01 << (7-bit))) != 0 ? 0 : 255,q);
-                    SetPixelInfoPixel(image,image->colormap+(ssize_t)
-                      GetPixelIndex(image,q),q);
-                    q+=GetPixelChannels(image);
-                    x++;
-                  }
-                }
-            }
-          break;
-        }
-        case 1:
-        {
-          if (image->storage_class == PseudoClass)
-            SetPixelAlpha(image,pixel,q);
-          else
-            SetPixelGreen(image,pixel,q);
-          break;
-        }
-        case 2:
-        {
-          if (image->storage_class == PseudoClass)
-            SetPixelAlpha(image,pixel,q);
-          else
-            SetPixelBlue(image,pixel,q);
-          break;
-        }
-        case 3:
-        {
-          if (image->colorspace == CMYKColorspace)
-            SetPixelBlack(image,pixel,q);
-          else
-            if (image->alpha_trait == BlendPixelTrait)
-              SetPixelAlpha(image,pixel,q);
-          break;
-        }
-        case 4:
-        {
-          if ((IssRGBCompatibleColorspace(image->colorspace) != MagickFalse) &&
-              (channels > 3))
-            break;
+        else
+          SetPixelBlue(image,pixel,q);
+        break;
+      }
+      case 3:
+      {
+        if (image->colorspace == CMYKColorspace)
+          SetPixelBlack(image,pixel,q);
+        else
           if (image->alpha_trait == BlendPixelTrait)
             SetPixelAlpha(image,pixel,q);
+        break;
+      }
+      case 4:
+      {
+        if ((IssRGBCompatibleColorspace(image->colorspace) != MagickFalse) &&
+            (channels > 3))
           break;
-        }
-        default:
-          break;
+        if (image->alpha_trait == BlendPixelTrait)
+          SetPixelAlpha(image,pixel,q);
+        break;
       }
-      q+=GetPixelChannels(image);
+      default:
+        break;
     }
-    if (SyncAuthenticPixels(image,exception) == MagickFalse)
-      break;
+    q+=GetPixelChannels(image);
   }
-  image->colorspace=colorspace;
-  if (image->compression == RLECompression)
-    compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
-  pixels=(unsigned char *) RelinquishMagickMemory(pixels);
-  return(MagickTrue);
+  return(SyncAuthenticPixels(image,exception));
 }
 
-static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
+static MagickStatusType ReadPSDChannelRaw(Image *image,const size_t channels,
+  const ssize_t type,ExceptionInfo *exception)
 {
-  char
-    message[MaxTextExtent],
-    type[4];
+  MagickStatusType
+    status;
 
-  Image
-    *image;
+  size_t
+    count,
+    row_size;
 
-  LayerInfo
-    *layer_info;
+  ssize_t
+    y;
 
-  MagickBooleanType
-    check_background,
-    status;
+  unsigned char
+    *pixels;
+
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+       "      layer data is RAW");
+
+  row_size=GetPSDRowSize(image);
+  pixels=(unsigned char *) AcquireQuantumMemory(row_size,sizeof(*pixels));
+  if (pixels == (unsigned char *) NULL)
+    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+      image->filename);
+
+  status=MagickTrue;
+  for (y=0; y < (ssize_t) image->rows; y++)
+  {
+    status=MagickFalse;
+
+    count=ReadBlob(image,row_size,pixels);
+    if (count != row_size)
+      break;
+
+    status=ReadPSDChannelPixels(image,channels,y,type,pixels,exception);
+    if (status == MagickFalse)
+      break;
+  }
+
+  pixels=(unsigned char *) RelinquishMagickMemory(pixels);
+  return(status);
+}
 
+static inline MagickOffsetType *ReadPSDRLEOffsets(Image *image,
+  PSDInfo *psd_info,const size_t size)
+{
   MagickOffsetType
-    offset,
     *offsets;
 
-  MagickSizeType
-    combinedlength,
-    length,
-    size;
-
-  PSDInfo
-    psd_info;
+  ssize_t
+    y;
 
-  register Quantum
-    *q;
+  offsets=(MagickOffsetType *) AcquireQuantumMemory(size,sizeof(*offsets));
+  if(offsets != (MagickOffsetType *) NULL)
+    {
+      for (y=0; y < (ssize_t) size; y++)
+      {
+        if (psd_info->version == 1)
+          offsets[y]=(MagickOffsetType) ReadBlobMSBShort(image);
+        else
+          offsets[y]=(MagickOffsetType) ReadBlobMSBLong(image);
+      }
+    }
+  return offsets;
+}
 
-  register ssize_t
-    i,
-    x;
+static MagickStatusType ReadPSDChannelRLE(Image *image,PSDInfo *psd_info,
+  const ssize_t type,MagickOffsetType *offsets,ExceptionInfo *exception)
+{
+  MagickStatusType
+    status;
 
   size_t
-    mask_size,
-    skip_first_alpha = 0;
+    length,
+    row_size;
 
   ssize_t
     count,
-    j,
-    number_layers,
     y;
 
   unsigned char
-    *data;
+    *compact_pixels,
+    *pixels;
 
-  unsigned short
-    compression;
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+       "      layer data is RLE compressed");
 
-  /*
-    Open image file.
-  */
-  assert(image_info != (const ImageInfo *) NULL);
-  assert(image_info->signature == MagickSignature);
-  if (image_info->debug != MagickFalse)
-    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
-      image_info->filename);
-  assert(exception != (ExceptionInfo *) NULL);
-  assert(exception->signature == MagickSignature);
-  image=AcquireImage(image_info,exception);
-  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
-  if (status == MagickFalse)
+  row_size=GetPSDRowSize(image);
+  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 (length > row_size + 256) // arbitrary number
     {
-      image=DestroyImageList(image);
-      return((Image *) NULL);
+      pixels=(unsigned char *) RelinquishMagickMemory(pixels);
+      ThrowBinaryException(ResourceLimitError,"InvalidLength",
+        image->filename);
     }
-  /*
-    Read image header.
-  */
-  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);
-  psd_info.channels=ReadBlobMSBShort(image);
-  psd_info.color_channels=psd_info.channels;
-  if (psd_info.channels > MaxPSDChannels)
-    ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded");
-  psd_info.rows=ReadBlobMSBLong(image);
+
+  compact_pixels=(unsigned char *) AcquireQuantumMemory(length,
+    sizeof(*pixels));
+  if (compact_pixels == (unsigned char *) NULL)
+    {
+      pixels=(unsigned char *) RelinquishMagickMemory(pixels);
+      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+        image->filename);
+    }
+
+  (void) ResetMagickMemory(compact_pixels,0,length*sizeof(*compact_pixels));
+
+  status=MagickTrue;
+  for (y=0; y < (ssize_t) image->rows; y++)
+  {
+    status=MagickFalse;
+
+    count=ReadBlob(image,(size_t) offsets[y],compact_pixels);
+    if (count != (ssize_t) offsets[y])
+      break;
+
+    count=DecodePSDPixels((size_t) offsets[y],compact_pixels,
+      (ssize_t) (image->depth == 1 ? 123456 : image->depth),row_size,pixels);
+    if (count != (ssize_t) row_size)
+      break;
+
+    status=ReadPSDChannelPixels(image,psd_info->channels,y,type,pixels,
+      exception);
+    if (status == MagickFalse)
+      break;
+  }
+
+  compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
+  pixels=(unsigned char *) RelinquishMagickMemory(pixels);
+  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
+    status;
+
+  register unsigned char
+    *p;
+
+  size_t
+    count,
+    length,
+    packet_size,
+    row_size;
+
+  ssize_t
+    y;
+
+  unsigned char
+    *compact_pixels,
+    *pixels;
+
+  z_stream
+    stream;
+
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+       "      layer data is RLE compressed");
+
+  compact_pixels=(unsigned char *) AcquireQuantumMemory(compact_size,
+    sizeof(*compact_pixels));
+  if (compact_pixels == (unsigned char *) NULL)
+    ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+      image->filename);
+
+  packet_size=GetPSDPacketSize(image);
+  row_size=image->columns*packet_size;
+  count=image->rows*row_size;
+
+  pixels=(unsigned char *) AcquireQuantumMemory(count,sizeof(*pixels));
+  if (pixels == (unsigned char *) NULL)
+    {
+      compact_pixels=(unsigned char *) RelinquishMagickMemory(compact_pixels);
+      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+        image->filename);
+    }
+
+  ResetMagickMemory(&stream, 0, sizeof(z_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.next_out=(Bytef *)pixels;
+  stream.avail_out=count;
+
+  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)
+        {
+          compact_pixels=(unsigned char *) RelinquishMagickMemory(
+            compact_pixels);
+          pixels=(unsigned char *) RelinquishMagickMemory(pixels);
+          return(MagickFalse);
+        }
+      }
+    }
+
+  if (compression == ZipWithPrediction)
+  {
+     p=pixels;
+     while(count > 0)
+     {
+       length=image->columns;
+       while(--length)
+       {
+         if (packet_size == 2)
+           {
+             p[2]+=p[0]+((p[1]+p[3]) >> 8);
+             p[3]+=p[1];
+           }
+         else
+          *(p+1)+=*p;
+         p+=packet_size;
+       }
+       p+=packet_size;
+       count-=row_size;
+     }
+  }
+
+  status=MagickTrue;
+  p=pixels;
+  for (y=0; y < (ssize_t) image->rows; y++)
+  {
+    status=ReadPSDChannelPixels(image,channels,y,type,p,exception);
+    if (status == MagickFalse)
+      break;
+
+    p+=row_size;
+  }
+
+  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
+}
+
+static MagickStatusType ReadPSDChannel(Image *image,PSDInfo *psd_info,
+  const LayerInfo* layer_info,const size_t channel,
+  const PSDCompressionType compression,ExceptionInfo *exception)
+{
+  MagickOffsetType
+    offset;
+
+  MagickStatusType
+    status;
+
+  offset=TellBlob(image);
+  status=MagickTrue;
+  switch(compression)
+  {
+    case Raw:
+      return(ReadPSDChannelRaw(image,psd_info->channels,
+        layer_info->channel_info[channel].type,exception));
+    case RLE:
+      {
+        MagickOffsetType
+          *offsets;
+
+        offsets=ReadPSDRLEOffsets(image,psd_info,image->rows);
+        if (offsets == (MagickOffsetType *) NULL)
+          ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+            image->filename);
+        status=ReadPSDChannelRLE(image,psd_info,
+                 layer_info->channel_info[channel].type,offsets,exception);
+        offsets=(MagickOffsetType *) RelinquishMagickMemory(offsets);
+      }
+      break;
+    case ZipWithPrediction:
+    case ZipWithoutPrediction:
+#ifdef MAGICKCORE_ZLIB_DELEGATE
+      status=ReadPSDChannelZip(image,layer_info->channels,
+        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;
+  }
+
+  if (status == MagickFalse)
+    SeekBlob(image,offset+layer_info->channel_info[channel].size-2,SEEK_SET);
+
+  return(status);
+}
+
+static MagickStatusType ReadPSDLayer(Image *image,PSDInfo *psd_info,
+  LayerInfo* layer_info,ExceptionInfo *exception)
+{
+  char
+    message[MaxTextExtent];
+
+  MagickBooleanType
+    correct_opacity;
+
+  MagickStatusType
+    status;
+
+  PSDCompressionType
+    compression;
+
+  ssize_t
+    j;
+
+  if (image->debug != MagickFalse)
+    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+      "    setting up new layer image");
+  (void) SetImageBackgroundColor(layer_info->image,exception);
+  layer_info->image->compose=PSDBlendModeToCompositeOperator(
+    layer_info->blendkey);
+  if (layer_info->visible == MagickFalse)
+    layer_info->image->compose=NoCompositeOp;
+  if (psd_info->mode == CMYKMode)
+    SetImageColorspace(layer_info->image,CMYKColorspace,exception);
+  if ((psd_info->mode == BitmapMode) ||
+      (psd_info->mode == GrayscaleMode) ||
+      (psd_info->mode == DuotoneMode))
+    SetImageColorspace(layer_info->image,GRAYColorspace,exception);
+  /*
+    Set up some hidden attributes for folks that need them.
+  */
+  (void) FormatLocaleString(message,MaxTextExtent,"%.20gld",
+    (double) layer_info->page.x);
+  (void) SetImageArtifact(layer_info->image,"psd:layer.x",message);
+  (void) FormatLocaleString(message,MaxTextExtent,"%.20g",
+    (double) layer_info->page.y);
+  (void) SetImageArtifact(layer_info->image,"psd:layer.y",message);
+  (void) FormatLocaleString(message,MaxTextExtent,"%.20g",(double)
+    layer_info->opacity);
+  (void) SetImageArtifact(layer_info->image,"psd:layer.opacity",message);
+  (void) SetImageProperty(layer_info->image,"label",(char *) layer_info->name,
+    exception);
+
+  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);
+    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->alpha_trait=BlendPixelTrait;
+        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->alpha_trait=BlendPixelTrait;
+
+      status=ReadPSDChannel(layer_info->image,psd_info,layer_info,j,
+        compression,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,exception);
+
+  return(status);
+}
+
+static MagickStatusType ReadPSDLayers(Image *image,PSDInfo *psd_info,
+  ExceptionInfo *exception)
+{
+  char
+    type[4];
+
+  LayerInfo
+    *layer_info;
+
+  MagickBooleanType
+   has_merged_alpha;
+
+  MagickSizeType
+    size;
+
+  MagickStatusType
+    status;
+
+  register ssize_t
+    i;
+
+  ssize_t
+    count,
+    j,
+    number_layers;
+
+  size=GetPSDSize(psd_info,image);
+  if (size == 0)
+    {
+      size_t
+        quantum;
+
+      /*
+        Skip layers & masks.
+      */
+      quantum=psd_info->version == 1 ? 4UL : 8UL;
+      (void) ReadBlobMSBLong(image);
+      count=ReadBlob(image,4,(unsigned char *) type);
+      if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
+        {
+          if (DiscardBlobBytes(image,size-quantum-8) == MagickFalse)
+            ThrowFileException(exception,CorruptImageError,
+              "UnexpectedEndOfFile",image->filename);
+        }
+      else
+        {
+          count=ReadBlob(image,4,(unsigned char *) type);
+          if ((count != 0) && (LocaleNCompare(type,"Lr16",4) == 0))
+            size=GetPSDSize(psd_info,image);
+          else
+            if (DiscardBlobBytes(image,size-quantum-12) == MagickFalse)
+              ThrowFileException(exception,CorruptImageError,
+                "UnexpectedEndOfFile",image->filename);
+        }
+    }
+
+  status=MagickTrue;
+  if (size != 0)
+    {
+      layer_info=(LayerInfo *) NULL;
+      number_layers=(short) ReadBlobMSBShort(image);
+
+      has_merged_alpha=MagickFalse;
+      if (number_layers < 0)
+        {
+          /*
+            The first alpha channel 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;
+        }
+
+      if (image->debug != MagickFalse)
+        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+          "  image contains %.20g layers",(double) number_layers);
+
+      if (number_layers == 0)
+        return(MagickFalse);
+
+      layer_info=(LayerInfo *) AcquireQuantumMemory((size_t) number_layers,
+        sizeof(*layer_info));
+      if (layer_info == (LayerInfo *) NULL)
+        {
+          if (image->debug != MagickFalse)
+            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+              "  allocation of LayerInfo failed");
+          ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+            image->filename);
+        }
+      (void) ResetMagickMemory(layer_info,0,(size_t) number_layers*
+        sizeof(*layer_info));
+
+      for (i=0; i < number_layers; i++)
+      {
+        int
+          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);
+        if (layer_info[i].channels > MaxPSDChannels)
+          {
+            layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
+            ThrowBinaryException(CorruptImageError,"MaximumChannelsExceeded",
+            image->filename);
+          }
+        if (image->debug != MagickFalse)
+          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+            "    offset(%.20g,%.20g), size(%.20g,%.20g), channels=%.20g",
+            (double) layer_info[i].page.x,(double) layer_info[i].page.y,
+            (double) layer_info[i].page.height,(double)
+            layer_info[i].page.width,(double) layer_info[i].channels);
+        for (j=0; j < (ssize_t) layer_info[i].channels; j++)
+        {
+          layer_info[i].channel_info[j].type=(short) ReadBlobMSBShort(image);
+          layer_info[i].channel_info[j].size=(size_t) GetPSDSize(psd_info,
+            image);
+          if (image->debug != MagickFalse)
+            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+              "    channel[%.20g]: type=%.20g, size=%.20g",(double) j,
+              (double) layer_info[i].channel_info[j].type,
+              (double) layer_info[i].channel_info[j].size);
+        }
+        count=ReadBlob(image,4,(unsigned char *) type);
+        if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
+          {
+            if (image->debug != MagickFalse)
+              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                "  layer type was %.4s instead of 8BIM", type);
+            layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
+            ThrowBinaryException(CorruptImageError,"ImproperImageHeader",
+              image->filename);
+          }
+        count=ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey);
+        layer_info[i].opacity=(Quantum) ScaleCharToQuantum((unsigned char)\r
+              ReadBlobByte(image));
+        layer_info[i].clipping=(unsigned char) ReadBlobByte(image);
+        layer_info[i].flags=(unsigned char) ReadBlobByte(image);
+        layer_info[i].visible=!(layer_info[i].flags & 0x02);
+        if (image->debug != MagickFalse)
+          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+            "   blend=%.4s, opacity=%.20g, clipping=%s, flags=%d, visible=%s",
+            layer_info[i].blendkey,(double) layer_info[i].opacity,
+            layer_info[i].clipping ? "true" : "false",layer_info[i].flags,
+            layer_info[i].visible ? "true" : "false");
+        (void) ReadBlobByte(image);  /* filler */
+
+        size=ReadBlobMSBLong(image);
+        if (size != 0)
+          {
+            MagickSizeType
+              combined_length,
+              length;
+
+            if (image->debug != MagickFalse)
+              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                "    layer contains additional info");
+            length=ReadBlobMSBLong(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);
+                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);
+                /*
+                  Skip over the rest of the layer mask information.
+                */
+                if (DiscardBlobBytes(image,length-16) == MagickFalse)
+                  {
+                    layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
+                    ThrowFileException(exception,CorruptImageError,
+                      "UnexpectedEndOfFile",image->filename);
+                  }
+              }
+            length=ReadBlobMSBLong(image);
+            combined_length+=length+4;
+            if (length != 0)
+              {
+                /*
+                  Layer blending ranges info.
+                */
+                if (image->debug != MagickFalse)
+                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                    "      layer blending ranges: length=%.20g",(double)
+                    ((MagickOffsetType) length));
+                /*
+                  We read it, but don't use it...
+                */
+                for (j=0; j < (ssize_t) (length); j+=8)
+                {
+                  size_t blend_source=ReadBlobMSBLong(image);
+                  size_t blend_dest=ReadBlobMSBLong(image);
+                  if (image->debug != MagickFalse)
+                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                      "        source(%x), dest(%x)",(unsigned int)
+                      blend_source,(unsigned int) blend_dest);
+                }
+              }
+            /*
+              Layer name.
+            */
+            length=(size_t) ReadBlobByte(image);
+            combined_length+=length+1;
+            for (j=0; j < (ssize_t) length; j++)
+              layer_info[i].name[j]=(unsigned char) ReadBlobByte(image);
+            layer_info[i].name[j]='\0';
+            if (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=(LayerInfo *) RelinquishMagickMemory(layer_info);
+                 ThrowBinaryException(CorruptImageError,
+                   "UnexpectedEndOfFile",image->filename);
+               }
+          }
+      }
+
+      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");
+            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.
+        */
+        layer_info[i].image=CloneImage(image,layer_info[i].page.width,
+          layer_info[i].page.height,MagickFalse,exception);
+        if (layer_info[i].image == (Image *) NULL)
+          {
+            for (j=0; j < i; j++)
+              layer_info[j].image=DestroyImage(layer_info[j].image);
+            layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
+            if (image->debug != MagickFalse)
+              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                "  allocation of image for layer %.20g failed",(double) i);
+            ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+              image->filename);
+          }
+      }
+
+      for (i=0; i < number_layers; i++)
+      {
+        if (layer_info[i].image == (Image *) NULL)
+        {
+          for (j=0; j < layer_info[i].channels; j++)
+          {
+            if (DiscardBlobBytes(image,layer_info[i].channel_info[j].size) ==
+                  MagickFalse)
+              {
+                layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
+                ThrowBinaryException(CorruptImageError,
+                  "UnexpectedEndOfFile",image->filename);
+              }
+          }
+          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;
+
+        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 (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 (i > 0)
+                layer_info[i].image->previous=layer_info[i-1].image;
+              if (i < (number_layers-1))
+                layer_info[i].image->next=layer_info[i+1].image;
+              layer_info[i].image->page=layer_info[i].page;
+            }
+            image->next=layer_info[0].image;
+            layer_info[0].image->previous=image;
+          }
+      }
+      layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
+    }
+
+  return(status);
+}
+
+static MagickStatusType ReadPSDMergedImage(Image* image,PSDInfo* psd_info,
+  ExceptionInfo *exception)
+{
+  MagickOffsetType
+    *offsets;
+
+  MagickStatusType
+    status;
+
+  PSDCompressionType
+    compression;
+
+  register ssize_t
+    i;
+
+  compression=(PSDCompressionType) ReadBlobMSBShort(image);
+  image->compression=ConvertPSDCompression(compression);
+
+  if (compression != Raw && compression != RLE)
+    {
+      (void) ThrowMagickException(exception,GetMagickModule(),
+        TypeWarning,"CompressionNotSupported","'%.20g'",(double) compression);
+      return(MagickFalse);
+    }
+
+  offsets=(MagickOffsetType *) NULL;
+  if (compression == RLE)
+  {
+    offsets=ReadPSDRLEOffsets(image,psd_info,image->rows*psd_info->channels);
+    if (offsets == (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),
+        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)
+      break;
+  }
+
+  if (image->colorspace == CMYKColorspace)
+    (void) NegateImage(image,MagickFalse,exception);
+
+  if (offsets != (MagickOffsetType *) NULL)
+    offsets=(MagickOffsetType *) RelinquishMagickMemory(offsets);
+
+  return(status);
+}
+
+static Image *ReadPSDImage(const ImageInfo *image_info,
+  ExceptionInfo *exception)
+{
+  Image
+    *image;
+
+  MagickBooleanType
+    skip_layers,
+    status;
+
+  MagickOffsetType
+    offset;
+
+  MagickSizeType
+    length;
+
+  PSDInfo
+    psd_info;
+
+  register ssize_t
+    i;
+
+  ssize_t
+    count;
+
+  unsigned char
+    *data;
+
+  /*
+    Open image file.
+  */
+  assert(image_info != (const ImageInfo *) NULL);
+  assert(image_info->signature == MagickSignature);
+  if (image_info->debug != MagickFalse)
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+      image_info->filename);
+  assert(exception != (ExceptionInfo *) NULL);
+  assert(exception->signature == MagickSignature);
+
+  image=AcquireImage(image_info,exception);
+  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
+  if (status == MagickFalse)
+    {
+      image=DestroyImageList(image);
+      return((Image *) NULL);
+    }
+  /*
+    Read image header.
+  */
+  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);
+  psd_info.channels=ReadBlobMSBShort(image);
+  if (psd_info.channels > MaxPSDChannels)
+    ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded");
+  psd_info.rows=ReadBlobMSBLong(image);
   psd_info.columns=ReadBlobMSBLong(image);
   if ((psd_info.version == 1) && ((psd_info.rows > 30000) ||
       (psd_info.columns > 30000)))
@@ -833,28 +1638,17 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
       image=DestroyImageList(image);
       return((Image *) NULL);
     }
-  image->alpha_trait=psd_info.channels >= 4 ? BlendPixelTrait :
-    UndefinedPixelTrait;
   if (psd_info.mode == LabMode)
     SetImageColorspace(image,LabColorspace,exception);
-  psd_info.color_channels=3;
   if (psd_info.mode == CMYKMode)
-    {
-      psd_info.color_channels=4;
-      SetImageColorspace(image,CMYKColorspace,exception);
-      image->alpha_trait=psd_info.channels >= 5 ? BlendPixelTrait :
-        UndefinedPixelTrait;
-    }
+    SetImageColorspace(image,CMYKColorspace,exception);
   if ((psd_info.mode == BitmapMode) || (psd_info.mode == GrayscaleMode) ||
       (psd_info.mode == DuotoneMode))
     {
-      psd_info.color_channels=1;
       status=AcquireImageColormap(image,psd_info.depth != 16 ? 256 : 65536,
         exception);
       if (status == MagickFalse)
         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
-      image->alpha_trait=psd_info.channels >= 2 ? BlendPixelTrait :
-        UndefinedPixelTrait;
       if (image->debug != MagickFalse)
         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
           "  Image colormap allocated");
@@ -887,7 +1681,8 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
           /*
             Read PSD raster colormap.
           */
-          if (AcquireImageColormap(image,(size_t) (length/3),exception) == MagickFalse)
+          if (AcquireImageColormap(image,(size_t) (length/3),
+                exception) == MagickFalse)
             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
           for (i=0; i < (ssize_t) image->colors; i++)
             image->colormap[i].red=ScaleCharToQuantum((unsigned char)
@@ -925,13 +1720,12 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
           blocks=(unsigned char *) RelinquishMagickMemory(blocks);
           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
         }
-      (void) ParseImageResourceBlocks(image,blocks,(size_t) length,
-        exception);
+      (void) ParseImageResourceBlocks(image,blocks,(size_t) length,exception);
       blocks=(unsigned char *) RelinquishMagickMemory(blocks);
     }
    /*
      If we are only "pinging" the image, then we're done - so return.
-  */
+   */
   if (image_info->ping != MagickFalse)
     {
       (void) CloseBlob(image);
@@ -940,21 +1734,20 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
   /*
     Layer and mask block.
   */
-  layer_info=(LayerInfo *) NULL;
-  number_layers=1;
   length=GetPSDSize(&psd_info,image);
   if (length == 8)
     {
       length=ReadBlobMSBLong(image);
       length=ReadBlobMSBLong(image);
     }
-  check_background=MagickFalse;
+  offset=TellBlob(image);
+  skip_layers=MagickFalse;
   if ((image_info->number_scenes == 1) && (image_info->scene == 0))
     {
       if (image->debug != MagickFalse)
         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
           "  read composite only");
-      check_background=MagickTrue;
+      skip_layers=MagickTrue;
     }
   if (length == 0)
     {
@@ -964,518 +1757,26 @@ static Image *ReadPSDImage(const ImageInfo *image_info,ExceptionInfo *exception)
     }
   else
     {
-      offset=TellBlob(image);
-      size=GetPSDSize(&psd_info,image);
-      if (size == 0)
-        {
-          size_t
-            quantum;
-
-          unsigned long
-            tag;
-
-          /*
-            Skip layers & masks.
-          */
-          quantum=psd_info.version == 1 ? 4UL : 8UL;
-          tag=ReadBlobMSBLong(image);
-          (void) tag;
-          count=ReadBlob(image,4,(unsigned char *) type);
-          if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
-            {
-              if (DiscardBlobBytes(image,length-quantum-8) == MagickFalse)
-                ThrowFileException(exception,CorruptImageError,
-                  "UnexpectedEndOfFile",image->filename);
-            }
-          else
-            {
-              count=ReadBlob(image,4,(unsigned char *) type);
-              if ((count != 0) && (LocaleNCompare(type,"Lr16",4) == 0))
-                size=GetPSDSize(&psd_info,image);
-              else
-                if (DiscardBlobBytes(image,length-quantum-12) == MagickFalse)
-                  ThrowFileException(exception,CorruptImageError,
-                    "UnexpectedEndOfFile",image->filename);
-            }
-        }
-      if (size != 0)
-        {
-          MagickOffsetType
-            layer_offset;
-
-          image->alpha_trait=psd_info.channels > psd_info.color_channels ?
-            BlendPixelTrait : UndefinedPixelTrait;
-
-          if (image->debug != MagickFalse)
-            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-              image->alpha_trait ? "  image has matte" : "  image has no matte");
-
-          layer_offset=offset+length;
-          number_layers=(short) ReadBlobMSBShort(image);
-          if (image->debug != MagickFalse)
-            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-              "  image contains %.20g layers",(double) number_layers);
-          if (number_layers < 0)
-            {
-              /*
-                Weird hack in PSD format to ignore first alpha channel.
-              */
-              skip_first_alpha=1;
-              (void) skip_first_alpha;
-              number_layers=MagickAbsoluteValue(number_layers);
-              if (image->debug != MagickFalse)
-                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                  "  negative layer count corrected for");
-            }
-          layer_info=(LayerInfo *) AcquireQuantumMemory((size_t) number_layers,
-            sizeof(*layer_info));
-          if (layer_info == (LayerInfo *) NULL)
-            {
-              if (image->debug != MagickFalse)
-                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                  "  allocation of LayerInfo failed");
-              ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
-            }
-          (void) ResetMagickMemory(layer_info,0,(size_t) number_layers*
-            sizeof(*layer_info));
-          for (i=0; i < number_layers; i++)
-          {
-            int
-              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);
-            if (check_background != MagickFalse)
-              {
-                size_t
-                  quantum;
-
-                if (layer_info[i].channels == psd_info.color_channels)
-                  image->alpha_trait=UndefinedPixelTrait;
-                quantum=psd_info.version == 1 ? 4UL : 8UL;
-                if (DiscardBlobBytes(image,length-20-quantum) == MagickFalse)
-                  ThrowFileException(exception,CorruptImageError,
-                    "UnexpectedEndOfFile",image->filename);
-                break;
-              }
-            if (layer_info[i].channels > MaxPSDChannels)
-              ThrowReaderException(CorruptImageError,"MaximumChannelsExceeded");
-            if (image->debug != MagickFalse)
-              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                "    offset(%.20g,%.20g), size(%.20g,%.20g), channels=%.20g",
-                (double) layer_info[i].page.x,(double) layer_info[i].page.y,
-                (double) layer_info[i].page.height,(double)
-                layer_info[i].page.width,(double) layer_info[i].channels);
-            for (j=0; j < (ssize_t) layer_info[i].channels; j++)
-            {
-              layer_info[i].channel_info[j].type=(short)
-                ReadBlobMSBShort(image);
-              layer_info[i].channel_info[j].size=(size_t)
-                GetPSDSize(&psd_info,image);
-              if (image->debug != MagickFalse)
-                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                  "    channel[%.20g]: type=%.20g, size=%.20g",(double) j,
-                  (double) layer_info[i].channel_info[j].type,
-                  (double) layer_info[i].channel_info[j].size);
-            }
-            count=ReadBlob(image,4,(unsigned char *) type);
-            if ((count == 0) || (LocaleNCompare(type,"8BIM",4) != 0))
-              {
-                if (image->debug != MagickFalse)
-                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                    "  layer type was %.4s instead of 8BIM", type);
-                 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
-              }
-            count=ReadBlob(image,4,(unsigned char *) layer_info[i].blendkey);
-            layer_info[i].opacity=(Quantum) ScaleCharToQuantum((unsigned char)
-              ReadBlobByte(image));
-            layer_info[i].clipping=(unsigned char) ReadBlobByte(image);
-            layer_info[i].flags=(unsigned char) ReadBlobByte(image);
-            layer_info[i].visible=!(layer_info[i].flags & 0x02);
-            if (image->debug != MagickFalse)
-              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                "   blend=%.4s, opacity=%.20g, clipping=%s, flags=%d, visible=%s",
-                layer_info[i].blendkey,(double) layer_info[i].opacity,
-                layer_info[i].clipping ? "true" : "false",layer_info[i].flags,
-                layer_info[i].visible ? "true" : "false");
-            (void) ReadBlobByte(image);  /* filler */
-            combinedlength=0;
-            size=ReadBlobMSBLong(image);
-            if (size != 0)
-              {
-                if (image->debug != MagickFalse)
-                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                    "    layer contains additional info");
-                length=ReadBlobMSBLong(image);
-                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);
-                    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));
-                    /*
-                      Skip over the rest of the layer mask information.
-                    */
-                    if (DiscardBlobBytes(image,length-16) == MagickFalse)
-                      ThrowFileException(exception,CorruptImageError,
-                        "UnexpectedEndOfFile",image->filename);
-                  }
-                combinedlength+=length+4;  /* +4 for length */
-                length=ReadBlobMSBLong(image);
-                if (length != 0)
-                  {
-                    /*
-                      Layer blending ranges info.
-                    */
-                    if (image->debug != MagickFalse)
-                      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                        "      layer blending ranges: length=%.20g",(double)
-                        ((MagickOffsetType) length));
-                    /*
-                      We read it, but don't use it...
-                    */
-                    for (j=0; j < (ssize_t) (length); j+=8)
-                    {
-                      size_t blend_source=ReadBlobMSBLong(image);
-                      size_t blend_dest=ReadBlobMSBLong(image);
-                      if (image->debug != MagickFalse)
-                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                          "        source(%x), dest(%x)",(unsigned int)
-                          blend_source,(unsigned int) blend_dest);
-                    }
-                  }
-                combinedlength+=length+4;
-                /*
-                  Layer name.
-                */
-                length=(size_t) ReadBlobByte(image);
-                for (j=0; j < (ssize_t) length; j++)
-                  layer_info[i].name[j]=(unsigned char) ReadBlobByte(image);
-                layer_info[i].name[j]='\0';
-                if (image->debug != MagickFalse)
-                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                    "      layer name: %s",layer_info[i].name);
-                combinedlength+=length+1;
-
-#if     0  /* still in development */
-          /*
-            Adjustment layers and other stuff...
-          */
+      if (skip_layers == MagickFalse)
+        if (ReadPSDLayers(image,&psd_info,exception) != MagickTrue)
           {
-            char  alsig[4], alkey[4];
-
-            count=ReadBlob(image,4,alsig);
-            if ((count == 0) || (LocaleNCompare(alsig,"8BIM",4) != 0)) {
-              if (debug != MagickFalse)
-              {
-                if (image->debug != MagickFalse)
-                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  adjustment layer type was %.4s instead of 8BIM", alsig);
-              }
-              ThrowReaderException(CorruptImageError,"ImproperImageHeader");
-            }
-            count=ReadBlob(image,4,alkey);
-            length=ReadBlobMSBLong(image);
-              if (debug != MagickFalse)
-              {
-                if (image->debug != MagickFalse)
-                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                            "      adjustment layer key: %.4s, data length=%.20g",
-                            alkey, (double) length);
-              }
-
-              if ( length ) {
-              for (j=0; j < (ssize_t) (length); j++)
-                (void) ReadBlobByte(image);
-              }
-
+            (void) CloseBlob(image);
+            return((Image *) NULL);
           }
-          combinedlength += 12 + length;  /* sig, key, length + the actual length*/
-#endif
 
-               /*
-                  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-combinedlength)));
-                if (DiscardBlobBytes(image,size-combinedlength) == MagickFalse)
-                  ThrowFileException(exception,CorruptImageError,
-                    "UnexpectedEndOfFile",image->filename);
-              }
-            /*
-              Allocate layered image.
-            */
-            layer_info[i].image=CloneImage(image,layer_info[i].page.width,
-              layer_info[i].page.height == ~0UL ? 1 : layer_info[i].page.height,
-              MagickFalse,exception);
-            if (layer_info[i].image == (Image *) NULL)
-              {
-                for (j=0; j < i; j++)
-                  layer_info[j].image=DestroyImage(layer_info[j].image);
-                if (image->debug != MagickFalse)
-                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                    "  allocation of image for layer %.20g failed",(double) i);
-                ThrowReaderException(ResourceLimitError,
-                  "MemoryAllocationFailed");
-              }
-            if (image->debug != MagickFalse)
-              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                "    setting up new layer image");
-            if (image_info->ping != MagickFalse)
-              (void) SetImageBackgroundColor(layer_info[i].image,exception);
-            layer_info[i].image->compose=
-              PSDBlendModeToCompositeOperator(layer_info[i].blendkey);
-            if (layer_info[i].visible == MagickFalse)
-              layer_info[i].image->compose=NoCompositeOp;
-            if (psd_info.mode == CMYKMode)
-              SetImageColorspace(layer_info[i].image,CMYKColorspace,exception);
-            if ((psd_info.mode == BitmapMode) ||
-                (psd_info.mode == GrayscaleMode) ||
-                (psd_info.mode == DuotoneMode))
-              SetImageColorspace(layer_info[i].image,GRAYColorspace,exception);
-            for (j=0; j < (ssize_t) layer_info[i].channels; j++)
-              if (layer_info[i].channel_info[j].type == -1)
-                layer_info[i].image->alpha_trait=BlendPixelTrait;
-            /*
-              Set up some hidden attributes for folks that need them.
-            */
-            (void) FormatLocaleString(message,MaxTextExtent,"%.20gld",
-              (double) layer_info[i].page.x);
-            (void) SetImageArtifact(layer_info[i].image,"psd:layer.x",message);
-            (void) FormatLocaleString(message,MaxTextExtent,"%.20g",
-              (double) layer_info[i].page.y);
-            (void) SetImageArtifact(layer_info[i].image,"psd:layer.y",message);
-            (void) FormatLocaleString(message,MaxTextExtent,"%.20g",
-              (double) layer_info[i].opacity);
-            (void) SetImageArtifact(layer_info[i].image,"psd:layer.opacity",
-              message);
-            (void) SetImageProperty(layer_info[i].image,"label",(char *)
-              layer_info[i].name,exception);
-          }
-        if (check_background == MagickFalse)
-          {
-           if (image->debug != MagickFalse)
-             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-               "  reading image data for layers");
-           /*
-             Read pixel data for each layer.
-           */
-           for (i=0; i < number_layers; i++)
-           {
-             if (image->debug != MagickFalse)
-               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                 "  reading data for layer %.20g",(double) i);
-               for (j=0; j < (ssize_t) layer_info[i].channels; j++)
-               {
-                 if (image->debug != MagickFalse)
-                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                     "    reading data for channel %.20g",(double) j);
-#if 1
-                 if (layer_info[i].channel_info[j].size <= (2*layer_info[i].image->rows))
-                   {
-                     ssize_t
-                       k;
-
-                     if (image->debug != MagickFalse)
-                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                         "      layer data is empty");
-                     /*
-                       A layer without data.
-                     */
-                     for (k=0; k < (ssize_t) layer_info[i].channel_info[j].size; k++)
-                       (void) ReadBlobByte(layer_info[i].image);
-                     continue;
-                   }
-#endif
-                 offsets=(MagickOffsetType *) NULL;
-                 layer_info[i].image->compression=NoCompression;
-                 compression=ReadBlobMSBShort(layer_info[i].image);
-                 if ((layer_info[i].page.height != 0) &&
-                     (layer_info[i].page.width != 0))
-                   {
-                     if (compression == 1)
-                       {
-                         /*
-                           Read RLE compressed data.
-                         */
-                         layer_info[i].image->compression=RLECompression;
-                         if (image->debug != MagickFalse)
-                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                             "      layer data is RLE compressed");
-                         offsets=(MagickOffsetType *) AcquireQuantumMemory(
-                           layer_info[i].image->rows,sizeof(*offsets));
-                         if (offsets == (MagickOffsetType *) NULL)
-                           ThrowReaderException(ResourceLimitError,
-                             "MemoryAllocationFailed");
-                         for (y=0; y < (ssize_t) layer_info[i].image->rows; y++)
-                           offsets[y]=GetPSDOffset(&psd_info,
-                             layer_info[i].image);
-                       }
-                     status=ReadPSDLayer(layer_info[i].image,
-                       layer_info[i].channels,
-                       layer_info[i].channel_info[j].type,offsets,exception);
-                     if (compression == 1)
-                       offsets=(MagickOffsetType *) RelinquishMagickMemory(
-                         offsets);
-                     if (status == MagickFalse)
-                       break;
-                   }
-                 }
-               if (layer_info[i].opacity != OpaqueAlpha)
-                 {
-                   /*
-                     Correct for opacity level.
-                   */
-                   for (y=0; y < (ssize_t) layer_info[i].image->rows; y++)
-                   {
-                     q=GetAuthenticPixels(layer_info[i].image,0,y,
-                       layer_info[i].image->columns,1,exception);
-                     if (q == (Quantum *) NULL)
-                       break;
-                     for (x=0; x < (ssize_t) layer_info[i].image->columns; x++)
-                     {
-                       SetPixelAlpha(layer_info[i].image,(Quantum)
-                         (QuantumScale*(GetPixelAlpha(layer_info[i].image,q))*
-                         layer_info[i].opacity),q);
-                       q+=GetPixelChannels(layer_info[i].image);
-                     }
-                     if (SyncAuthenticPixels(layer_info[i].image,exception) == MagickFalse)
-                       break;
-                   }
-                 }
-               if (layer_info[i].image->colorspace == CMYKColorspace)
-                 (void) NegateImage(layer_info[i].image,MagickFalse,exception);
-               status=SetImageProgress(image,LoadImagesTag,i,(MagickSizeType)
-                 number_layers);
-               if (status == MagickFalse)
-                 break;
-             }
-           /* added by palf -> invisible group layer make layer of this group
-              invisible I consider that all layer with width and height null are
-              layer for group layer */
-          {
-            short inside_layer = 0;
-            short layer_visible = 0;
-            for (i=number_layers-1; i >=0; i--)
-            {
-              if ((layer_info[i].page.width == 0) ||
-                  (layer_info[i].page.height == 0))
-                {
-                  if (inside_layer == 0)
-                    {
-                      inside_layer=1;
-                      layer_visible=(short int) layer_info[i].visible;
-                    }
-                  else
-                    {
-                      inside_layer = 0;
-                    }
-                }
-              else
-                if ((inside_layer == 1) && (layer_visible == 0))
-                  {
-                    layer_info[i].visible=(unsigned char) layer_visible;
-                    layer_info[i].image->compose=NoCompositeOp;
-                  }
-            }
-          }
-          /* added by palf -> suppression of empty layer */
-          /* I consider that all layer with width and height null are layer for group layer */
-          for (i=0; i < number_layers; i++)
-          {
-            if ((layer_info[i].page.width == 0) ||
-                (layer_info[i].page.height == 0))
-              {
-                if (layer_info[i].image != (Image *) NULL)
-                  layer_info[i].image=DestroyImage(layer_info[i].image);
-                for (j=i; j < number_layers - 1; j++)
-                  layer_info[j] = layer_info[j+1];
-                number_layers--;
-                i--;
-              }
-           }
-           mask_size = ReadBlobMSBLong(image);  /* global mask size: currently ignored */
-           (void) mask_size;
-           if (number_layers > 0)
-             {
-               if (image->debug != MagickFalse)
-                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                   "  putting layers into image list");
-               for (i=0; i < number_layers; i++)
-               {
-                 if (i > 0)
-                   layer_info[i].image->previous=layer_info[i-1].image;
-                 if (i < (number_layers-1))
-                   layer_info[i].image->next=layer_info[i+1].image;
-                 layer_info[i].image->page=layer_info[i].page;
-               }
-               image->next=layer_info[0].image;
-               layer_info[0].image->previous=image;
-               layer_info=(LayerInfo *) RelinquishMagickMemory(layer_info);
-             }
-           layer_offset-=TellBlob(image);
-           offset=SeekBlob(image,layer_offset,SEEK_CUR);
-         }
-      }
+      /*
+         Skip the rest of the layer and mask information.
+      */
+      SeekBlob(image,offset+length,SEEK_SET);
     }
+
   /*
-    Read the precombined layer, present for PSD < 4 compatibility
+    Read the precombined layer, present for PSD < 4 compatibility.
   */
   if (image->debug != MagickFalse)
     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
       "  reading the precombined layer");
-  offsets=(MagickOffsetType *) NULL;
-  image->compression=NoCompression;
-  compression=ReadBlobMSBShort(image);
-  if (compression == 1)
-    {
-      /*
-        Read Packbit encoded pixel data as separate planes.
-      */
-      image->compression=RLECompression;
-      offsets=(MagickOffsetType *) AcquireQuantumMemory(image->rows,
-        psd_info.channels*sizeof(*offsets));
-      if (offsets == (MagickOffsetType *) NULL)
-        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
-      for (i=0; i < (ssize_t) (image->rows*psd_info.channels); i++)
-        offsets[i]=GetPSDOffset(&psd_info,image);
-    }
-  for (i=0; i < (ssize_t) psd_info.channels; i++)
-  {
-    status=ReadPSDLayer(image,psd_info.channels,i,offsets+i*image->rows,
-      exception);
-    if (status == MagickFalse)
-      break;
-    status=SetImageProgress(image,LoadImagesTag,i,psd_info.channels);
-    if (status == MagickFalse)
-      break;
-  }
-  if (compression == 1)
-    offsets=(MagickOffsetType *) RelinquishMagickMemory(offsets);
-  if (image->colorspace == CMYKColorspace)
-    (void) NegateImage(image,MagickFalse,exception);
+  (void) ReadPSDMergedImage(image,&psd_info,exception);
   (void) CloseBlob(image);
   return(GetFirstImageInList(image));
 }