]> granicus.if.org Git - imagemagick/commitdiff
Added support for writing layered tiff files with -define tiff:write-layers=true.
authorDirk Lemstra <dirk@git.imagemagick.org>
Wed, 6 Sep 2017 19:24:30 +0000 (21:24 +0200)
committerDirk Lemstra <dirk@git.imagemagick.org>
Wed, 6 Sep 2017 19:24:30 +0000 (21:24 +0200)
ChangeLog
MagickCore/blob-private.h
MagickCore/blob.c
coders/psd-private.h
coders/psd.c
coders/tiff.c

index 52c62e6f46db952540d51272ef7c85bc45fb7bdb..6b0591a2e5b03fbe3b61914423f39167b5899cb7 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,8 @@
-2017-09-27  7.0.7-0 Cristy  <quetzlzacatenango@image...>
+2017-09-05 7.0.7-1 Dirk Lemstra <dirk@lem.....org>
+  * Added -define tiff:write-layers=true to add support for writing layered
+    tiff files.
+
+2017-09-03  7.0.7-0 Cristy  <quetzlzacatenango@image...>
   * Release ImageMagick version 7.0.7-0, GIT revision 20996:2f8ac2203:20170903.
 
 2017-08-28  7.0.7-0 Cristy  <quetzlzacatenango@image...>
index 285e0a75490f8bce2a06b25d0920a1ba3a472bb6..b5f04d3d5ddee4bbfbc77fec4d2f1e8b3d1652b8 100644 (file)
@@ -110,15 +110,15 @@ extern MagickExport ssize_t
   WriteBlobByte(Image *,const unsigned char),
   WriteBlobFloat(Image *,const float),
   WriteBlobLong(Image *,const unsigned int),
+  WriteBlobLongLong(Image *,const MagickSizeType),
   WriteBlobShort(Image *,const unsigned short),
+  WriteBlobSignedLong(Image *,const signed int),
   WriteBlobLSBLong(Image *,const unsigned int),
   WriteBlobLSBShort(Image *,const unsigned short),
   WriteBlobLSBSignedLong(Image *,const signed int),
   WriteBlobLSBSignedShort(Image *,const signed short),
   WriteBlobMSBLong(Image *,const unsigned int),
-  WriteBlobMSBLongLong(Image *,const MagickSizeType),
   WriteBlobMSBShort(Image *,const unsigned short),
-  WriteBlobMSBSignedLong(Image *,const signed int),
   WriteBlobMSBSignedShort(Image *,const signed short),
   WriteBlobString(Image *,const char *);
 
@@ -134,6 +134,7 @@ extern MagickExport unsigned short
 
 extern MagickExport void
   AttachBlob(BlobInfo *,const void *,const size_t),
+  AttachCustomStream(BlobInfo *,CustomStreamInfo *),
   *DetachBlob(BlobInfo *),
   DisassociateBlob(Image *),
   GetBlobInfo(BlobInfo *),
index a2481d5665587b3e5edc75d3bd25e37332f40127..ef09452a62037c6829160942b7d0466ce9645557 100644 (file)
@@ -275,6 +275,43 @@ MagickExport void AttachBlob(BlobInfo *blob_info,const void *blob,
 %                                                                             %
 %                                                                             %
 %                                                                             %
++   A t t a c h C u s t o m S t r e a m                                       %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  AttachCustomStream() attaches a CustomStreamInfo to the BlobInfo structure.
+%
+%  The format of the AttachCustomStream method is:
+%
+%      void AttachCustomStream(BlobInfo *blob_info,
+%        CustomStreamInfo *custom_stream)
+%
+%  A description of each parameter follows:
+%
+%    o blob_info: specifies a pointer to a BlobInfo structure.
+%
+%    o custom_stream: the custom stream info.
+%
+*/
+MagickExport void AttachCustomStream(BlobInfo *blob_info,
+  CustomStreamInfo *custom_stream)
+{
+  assert(blob_info != (BlobInfo *) NULL);
+  assert(custom_stream != (CustomStreamInfo *) NULL);
+  assert(custom_stream->signature == MagickCoreSignature);
+  if (blob_info->debug != MagickFalse)
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+  blob_info->type=CustomStream;
+  blob_info->custom_stream=custom_stream;
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
 +   B l o b T o F i l e                                                       %
 %                                                                             %
 %                                                                             %
@@ -5466,6 +5503,61 @@ MagickExport ssize_t WriteBlobLong(Image *image,const unsigned int value)
 %                                                                             %
 %                                                                             %
 %                                                                             %
++  W r i t e B l o b L o n g L o n g                                          %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  WriteBlobMSBLongLong() writes a long long value as a 64-bit quantity in the
+%  byte-order specified by the endian member of the image structure.
+%
+%  The format of the WriteBlobLongLong method is:
+%
+%      ssize_t WriteBlobLongLong(Image *image,const MagickSizeType value)
+%
+%  A description of each parameter follows.
+%
+%    o value:  Specifies the value to write.
+%
+%    o image: the image.
+%
+*/
+MagickExport ssize_t WriteBlobLongLong(Image *image,const MagickSizeType value)
+{
+  unsigned char
+    buffer[8];
+
+  assert(image != (Image *) NULL);
+  assert(image->signature == MagickCoreSignature);
+  if (image->endian == LSBEndian)
+    {
+      buffer[0]=(unsigned char) value;
+      buffer[1]=(unsigned char) (value >> 8);
+      buffer[2]=(unsigned char) (value >> 16);
+      buffer[3]=(unsigned char) (value >> 24);
+      buffer[4]=(unsigned char) (value >> 32);
+      buffer[5]=(unsigned char) (value >> 40);
+      buffer[6]=(unsigned char) (value >> 48);
+      buffer[7]=(unsigned char) (value >> 56);
+      return(WriteBlobStream(image,8,buffer));
+    }
+  buffer[0]=(unsigned char) (value >> 56);
+  buffer[1]=(unsigned char) (value >> 48);
+  buffer[2]=(unsigned char) (value >> 40);
+  buffer[3]=(unsigned char) (value >> 32);
+  buffer[4]=(unsigned char) (value >> 24);
+  buffer[5]=(unsigned char) (value >> 16);
+  buffer[6]=(unsigned char) (value >> 8);
+  buffer[7]=(unsigned char) value;
+  return(WriteBlobStream(image,8,buffer));
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
 +   W r i t e B l o b S h o r t                                               %
 %                                                                             %
 %                                                                             %
@@ -5509,6 +5601,63 @@ MagickExport ssize_t WriteBlobShort(Image *image,const unsigned short value)
 %                                                                             %
 %                                                                             %
 %                                                                             %
++  W r i t e B l o b S i g n e d L o n g                                      %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  WriteBlobSignedLong() writes a signed value as a 32-bit quantity in the
+%  byte-order specified by the endian member of the image structure.
+%
+%  The format of the WriteBlobSignedLong method is:
+%
+%      ssize_t WriteBlobSignedLong(Image *image,const signed int value)
+%
+%  A description of each parameter follows.
+%
+%    o image: the image.
+%
+%    o value: Specifies the value to write.
+%
+*/
+MagickExport ssize_t WriteBlobSignedLong(Image *image,const signed int value)
+{
+  union
+  {
+    unsigned int
+      unsigned_value;
+
+    signed int
+      signed_value;
+  } quantum;
+
+  unsigned char
+    buffer[4];
+
+  assert(image != (Image *) NULL);
+  assert(image->signature == MagickCoreSignature);
+  quantum.signed_value=value;
+  if (image->endian == LSBEndian)
+    {
+      buffer[0]=(unsigned char) quantum.unsigned_value;
+      buffer[1]=(unsigned char) (quantum.unsigned_value >> 8);
+      buffer[2]=(unsigned char) (quantum.unsigned_value >> 16);
+      buffer[3]=(unsigned char) (quantum.unsigned_value >> 24);
+      return(WriteBlobStream(image,4,buffer));
+    }
+  buffer[0]=(unsigned char) (quantum.unsigned_value >> 24);
+  buffer[1]=(unsigned char) (quantum.unsigned_value >> 16);
+  buffer[2]=(unsigned char) (quantum.unsigned_value >> 8);
+  buffer[3]=(unsigned char) quantum.unsigned_value;
+  return(WriteBlobStream(image,4,buffer));
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
 +  W r i t e B l o b L S B L o n g                                            %
 %                                                                             %
 %                                                                             %
@@ -5721,99 +5870,6 @@ MagickExport ssize_t WriteBlobMSBLong(Image *image,const unsigned int value)
 %                                                                             %
 %                                                                             %
 %                                                                             %
-+  W r i t e B l o b M S B L o n g L o n g                                    %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  WriteBlobMSBLongLong() writes a long long value as a 64-bit quantity in
-%  most-significant byte first order.
-%
-%  The format of the WriteBlobMSBLongLong method is:
-%
-%      ssize_t WriteBlobMSBLongLong(Image *image,const MagickSizeType value)
-%
-%  A description of each parameter follows.
-%
-%    o value:  Specifies the value to write.
-%
-%    o image: the image.
-%
-*/
-MagickExport ssize_t WriteBlobMSBLongLong(Image *image,
-  const MagickSizeType value)
-{
-  unsigned char
-    buffer[8];
-
-  assert(image != (Image *) NULL);
-  assert(image->signature == MagickCoreSignature);
-  buffer[0]=(unsigned char) (value >> 56);
-  buffer[1]=(unsigned char) (value >> 48);
-  buffer[2]=(unsigned char) (value >> 40);
-  buffer[3]=(unsigned char) (value >> 32);
-  buffer[4]=(unsigned char) (value >> 24);
-  buffer[5]=(unsigned char) (value >> 16);
-  buffer[6]=(unsigned char) (value >> 8);
-  buffer[7]=(unsigned char) value;
-  return(WriteBlobStream(image,8,buffer));
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
-+  W r i t e B l o b M S B S i g n e d L o n g                                %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  WriteBlobMSBSignedLong() writes a signed value as a 32-bit quantity in
-%  most-significant byte first order.
-%
-%  The format of the WriteBlobMSBSignedLong method is:
-%
-%      ssize_t WriteBlobMSBSignedLong(Image *image,const signed int value)
-%
-%  A description of each parameter follows.
-%
-%    o image: the image.
-%
-%    o value: Specifies the value to write.
-%
-*/
-MagickExport ssize_t WriteBlobMSBSignedLong(Image *image,const signed int value)
-{
-  union
-  {
-    unsigned int
-      unsigned_value;
-
-    signed int
-      signed_value;
-  } quantum;
-
-  unsigned char
-    buffer[4];
-
-  assert(image != (Image *) NULL);
-  assert(image->signature == MagickCoreSignature);
-  quantum.signed_value=value;
-  buffer[0]=(unsigned char) (quantum.unsigned_value >> 24);
-  buffer[1]=(unsigned char) (quantum.unsigned_value >> 16);
-  buffer[2]=(unsigned char) (quantum.unsigned_value >> 8);
-  buffer[3]=(unsigned char) quantum.unsigned_value;
-  return(WriteBlobStream(image,4,buffer));
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
 +   W r i t e B l o b M S B S i g n e d S h o r t                             %
 %                                                                             %
 %                                                                             %
index 64169d0372fb57c1bddeb47f2e1e0088b609d869..a69ba4182786dab2502aeb97432113d3eb44806a 100644 (file)
@@ -43,7 +43,9 @@ typedef struct _PSDInfo
 
 extern ModuleExport MagickBooleanType
   ReadPSDLayers(Image *,const ImageInfo *,const PSDInfo *,
-    const MagickBooleanType,ExceptionInfo *);
+    ExceptionInfo *),
+  WritePSDLayers(Image *,const ImageInfo *,const PSDInfo *,
+    ExceptionInfo *);
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }
index 88a63e577ee66b60153c83b54f06b6255e9c5db5..ac557e0b2425a1a4c0a4270f1b92ba00b0ca863f 100644 (file)
@@ -240,39 +240,58 @@ static MagickBooleanType IsPSD(const unsigned char *magick,const size_t length)
 %
 */
 
-static const char *CompositeOperatorToPSDBlendMode(CompositeOperator op)
+static const char *CompositeOperatorToPSDBlendMode(Image *image)
 {
-  const char
-    *blend_mode;
-
-  switch (op)
+  switch (image->compose)
   {
-    case ColorBurnCompositeOp:  blend_mode = "idiv";  break;
-    case ColorDodgeCompositeOp: blend_mode = "div ";  break;
-    case ColorizeCompositeOp:   blend_mode = "colr";  break;
-    case DarkenCompositeOp:     blend_mode = "dark";  break;
-    case DifferenceCompositeOp: blend_mode = "diff";  break;
-    case DissolveCompositeOp:   blend_mode = "diss";  break;
-    case ExclusionCompositeOp:  blend_mode = "smud";  break;
-    case HardLightCompositeOp:  blend_mode = "hLit";  break;
-    case HardMixCompositeOp:    blend_mode = "hMix";  break;
-    case HueCompositeOp:        blend_mode = "hue ";  break;
-    case LightenCompositeOp:    blend_mode = "lite";  break;
-    case LinearBurnCompositeOp: blend_mode = "lbrn";  break;
-    case LinearDodgeCompositeOp:blend_mode = "lddg";  break;
-    case LinearLightCompositeOp:blend_mode = "lLit";  break;
-    case LuminizeCompositeOp:   blend_mode = "lum ";  break;
-    case MultiplyCompositeOp:   blend_mode = "mul ";  break;
-    case OverCompositeOp:       blend_mode = "norm";  break;
-    case OverlayCompositeOp:    blend_mode = "over";  break;
-    case PinLightCompositeOp:   blend_mode = "pLit";  break;
-    case SaturateCompositeOp:   blend_mode = "sat ";  break;
-    case ScreenCompositeOp:     blend_mode = "scrn";  break;
-    case SoftLightCompositeOp:  blend_mode = "sLit";  break;
-    case VividLightCompositeOp: blend_mode = "vLit";  break;
-    default:                    blend_mode = "norm";
+    case ColorBurnCompositeOp:
+      return(image->endian == LSBEndian ? "vidi" : "idiv");
+    case ColorDodgeCompositeOp:
+      return(image->endian == LSBEndian ? " vid" : "div ");
+    case ColorizeCompositeOp:
+      return(image->endian == LSBEndian ? "rloc" : "colr");
+    case DarkenCompositeOp:
+      return(image->endian == LSBEndian ? "krad" : "dark");
+    case DifferenceCompositeOp:
+      return(image->endian == LSBEndian ? "ffid" : "diff");
+    case DissolveCompositeOp:
+      return(image->endian == LSBEndian ? "ssid" : "diss");
+    case ExclusionCompositeOp:
+      return(image->endian == LSBEndian ? "dums" : "smud");
+    case HardLightCompositeOp:
+      return(image->endian == LSBEndian ? "tiLh" : "hLit");
+    case HardMixCompositeOp:
+      return(image->endian == LSBEndian ? "xiMh" : "hMix");
+    case HueCompositeOp:
+      return(image->endian == LSBEndian ? " euh" : "hue ");
+    case LightenCompositeOp:
+      return(image->endian == LSBEndian ? "etil" : "lite");
+    case LinearBurnCompositeOp:
+      return(image->endian == LSBEndian ? "nrbl" : "lbrn");
+    case LinearDodgeCompositeOp:
+      return(image->endian == LSBEndian ? "gddl" : "lddg");
+    case LinearLightCompositeOp:
+      return(image->endian == LSBEndian ? "tiLl" : "lLit");
+    case LuminizeCompositeOp:
+      return(image->endian == LSBEndian ? " mul" : "lum ");
+    case MultiplyCompositeOp:
+      return(image->endian == LSBEndian ? " lum" : "mul ");
+    case OverlayCompositeOp:
+      return(image->endian == LSBEndian ? "revo" : "over");
+    case PinLightCompositeOp:
+      return(image->endian == LSBEndian ? "tiLp" : "pLit");
+    case SaturateCompositeOp:
+      return(image->endian == LSBEndian ? " tas" : "sat ");
+    case ScreenCompositeOp:
+      return(image->endian == LSBEndian ? "nrcs" : "scrn");
+    case SoftLightCompositeOp:
+      return(image->endian == LSBEndian ? "tiLs" : "sLit");
+    case VividLightCompositeOp:
+      return(image->endian == LSBEndian ? "tiLv" : "vLit");
+    case OverCompositeOp:
+    default:
+      return(image->endian == LSBEndian ? "mron" : "norm");
   }
-  return(blend_mode);
 }
 
 /*
@@ -1856,8 +1875,7 @@ static MagickBooleanType ReadPSDLayersInternal(Image *image,
 }
 
 ModuleExport MagickBooleanType ReadPSDLayers(Image *image,
-  const ImageInfo *image_info,const PSDInfo *psd_info,
-  const MagickBooleanType skip_layers,ExceptionInfo *exception)
+  const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception)
 {
   PolicyDomain
     domain;
@@ -1868,8 +1886,8 @@ ModuleExport MagickBooleanType ReadPSDLayers(Image *image,
   domain=CoderPolicyDomain;
   rights=ReadPolicyRights;
   if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
-    return(MagickFalse);
-  return(ReadPSDLayersInternal(image,image_info,psd_info,skip_layers,
+    return(MagickTrue);
+  return(ReadPSDLayersInternal(image,image_info,psd_info,MagickFalse,
     exception));
 }
 
@@ -2344,8 +2362,8 @@ static inline ssize_t SetPSDSize(const PSDInfo *psd_info,Image *image,
   const MagickSizeType size)
 {
   if (psd_info->version == 1)
-    return(WriteBlobMSBLong(image,(unsigned int) size));
-  return(WriteBlobMSBLongLong(image,size));
+    return(WriteBlobLong(image,(unsigned int) size));
+  return(WriteBlobLongLong(image,size));
 }
 
 static inline ssize_t WritePSDSize(const PSDInfo *psd_info,Image *image,
@@ -2359,10 +2377,7 @@ static inline ssize_t WritePSDSize(const PSDInfo *psd_info,Image *image,
 
   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);
+  result=SetPSDSize(psd_info, image, size);
   SeekBlob(image,current_offset,SEEK_SET);
   return(result);
 }
@@ -2490,17 +2505,17 @@ static size_t WriteCompressionStart(const PSDInfo *psd_info,Image *image,
 
   if (next_image->compression == RLECompression)
     {
-      length=WriteBlobMSBShort(image,RLE);
+      length=WriteBlobShort(image,RLE);
       for (i=0; i < channels; i++)
         for (y=0; y < (ssize_t) next_image->rows; y++)
           length+=SetPSDOffset(psd_info,image,0);
     }
 #ifdef MAGICKCORE_ZLIB_DELEGATE
   else if (next_image->compression == ZipCompression)
-    length=WriteBlobMSBShort(image,ZipWithoutPrediction);
+    length=WriteBlobShort(image,ZipWithoutPrediction);
 #endif
   else
-    length=WriteBlobMSBShort(image,Raw);
+    length=WriteBlobShort(image,Raw);
   return(length);
 }
 
@@ -2874,7 +2889,7 @@ static inline size_t WriteChannelSize(const PSDInfo *psd_info,Image *image,
   size_t
     count;
 
-  count=WriteBlobMSBSignedShort(image,channel);
+  count=(size_t) WriteBlobShort(image,channel);
   count+=SetPSDSize(psd_info,image,0);
   return(count);
 }
@@ -3087,8 +3102,9 @@ static const StringInfo *GetAdditionalInformation(const ImageInfo *image_info,
   return(profile);
 }
 
-static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
-  Image *image,ExceptionInfo *exception)
+static MagickBooleanType WritePSDLayersInternal(Image *image,
+  const ImageInfo *image_info,const PSDInfo *psd_info,size_t *layers_size,
+  ExceptionInfo *exception)
 {
   char
     layer_name[MagickPathExtent];
@@ -3097,7 +3113,6 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
     *property;
 
   const StringInfo
-    *icc_profile,
     *info;
 
   Image
@@ -3111,9 +3126,6 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
     *layer_size_offsets,
     size_offset;
 
-  PSDInfo
-    psd_info;
-
   register ssize_t
     i;
 
@@ -3122,11 +3134,221 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
     layer_index,
     length,
     name_length,
-    num_channels,
-    packet_size,
     rounded_size,
     size;
 
+  status=MagickTrue;
+  base_image=GetNextImageInList(image);
+  if (base_image == (Image *) NULL)
+    base_image=image;
+  size=0;
+  size_offset=TellBlob(image);
+  SetPSDSize(psd_info,image,0);
+  layer_count=0;
+  for (next_image=base_image; next_image != NULL; )
+  {
+    layer_count++;
+    next_image=GetNextImageInList(next_image);
+  }
+  if (image->alpha_trait != UndefinedPixelTrait)
+    size+=WriteBlobShort(image,-(unsigned short) layer_count);
+  else
+    size+=WriteBlobShort(image,(unsigned short) layer_count);
+  layer_size_offsets=(MagickOffsetType *) AcquireQuantumMemory(
+    (size_t) layer_count,sizeof(MagickOffsetType));
+  if (layer_size_offsets == (MagickOffsetType *) NULL)
+    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
+  layer_index=0;
+  for (next_image=base_image; next_image != NULL; )
+  {
+    Image
+      *mask;
+
+    unsigned char
+      default_color;
+
+    unsigned short
+      channels,
+      total_channels;
+
+    mask=(Image *) NULL;
+    property=GetImageArtifact(next_image,"psd:opacity-mask");
+    default_color=0;
+    if (property != (const char *) NULL)
+      {
+        mask=(Image *) GetImageRegistry(ImageRegistryType,property,exception);
+        default_color=strlen(property) == 9 ? 255 : 0;
+      }
+    size+=WriteBlobSignedLong(image,(signed int) next_image->page.y);
+    size+=WriteBlobSignedLong(image,(signed int) next_image->page.x);
+    size+=WriteBlobSignedLong(image,(signed int) (next_image->page.y+
+      next_image->rows));
+    size+=WriteBlobSignedLong(image,(signed int) (next_image->page.x+
+      next_image->columns));
+    channels=1U;
+    if ((next_image->storage_class != PseudoClass) &&
+        (IsImageGray(next_image) == MagickFalse))
+      channels=next_image->colorspace == CMYKColorspace ? 4U : 3U;
+    total_channels=channels;
+    if (next_image->alpha_trait != UndefinedPixelTrait)
+      total_channels++;
+    if (mask != (Image *) NULL)
+      total_channels++;
+    size+=WriteBlobShort(image,total_channels);
+    layer_size_offsets[layer_index++]=TellBlob(image);
+    for (i=0; i < (ssize_t) channels; i++)
+      size+=WriteChannelSize(psd_info,image,(signed short) i);
+    if (next_image->alpha_trait != UndefinedPixelTrait)
+      size+=WriteChannelSize(psd_info,image,-1);
+    if (mask != (Image *) NULL)
+      size+=WriteChannelSize(psd_info,image,-2);
+    size+=WriteBlobString(image,image->endian == LSBEndian ? "MIB8" :"8BIM");
+    size+=WriteBlobString(image,CompositeOperatorToPSDBlendMode(image));
+    property=GetImageArtifact(next_image,"psd:layer.opacity");
+    if (property != (const char *) NULL)
+      {
+        Quantum
+          opacity;
+
+        opacity=(Quantum) StringToInteger(property);
+        size+=WriteBlobByte(image,ScaleQuantumToChar(opacity));
+        (void) ApplyPSDLayerOpacity(next_image,opacity,MagickTrue,exception);
+      }
+    else
+      size+=WriteBlobByte(image,255);
+    size+=WriteBlobByte(image,0);
+    size+=WriteBlobByte(image,next_image->compose==NoCompositeOp ?
+      1 << 0x02 : 1); /* layer properties - visible, etc. */
+    size+=WriteBlobByte(image,0);
+    info=GetAdditionalInformation(image_info,next_image,exception);
+    property=(const char *) GetImageProperty(next_image,"label",exception);
+    if (property == (const char *) NULL)
+      {
+        (void) FormatLocaleString(layer_name,MagickPathExtent,"L%.20g",
+          (double) layer_index);
+        property=layer_name;
+      }
+    name_length=strlen(property)+1;
+    if ((name_length % 4) != 0)
+      name_length+=(4-(name_length % 4));
+    if (info != (const StringInfo *) NULL)
+      name_length+=GetStringInfoLength(info);
+    name_length+=8;
+    if (mask != (Image *) NULL)
+      name_length+=20;
+    size+=WriteBlobLong(image,(unsigned int) name_length);
+    if (mask == (Image *) NULL)
+      size+=WriteBlobLong(image,0);
+    else
+      {
+        if (mask->compose != NoCompositeOp)
+          (void) ApplyPSDOpacityMask(next_image,mask,ScaleCharToQuantum(
+            default_color),MagickTrue,exception);
+        mask->page.y+=image->page.y;
+        mask->page.x+=image->page.x;
+        size+=WriteBlobLong(image,20);
+        size+=WriteBlobSignedLong(image,mask->page.y);
+        size+=WriteBlobSignedLong(image,mask->page.x);
+        size+=WriteBlobSignedLong(image,(const signed int) mask->rows+
+          mask->page.y);
+        size+=WriteBlobSignedLong(image,(const signed int) mask->columns+
+          mask->page.x);
+        size+=WriteBlobByte(image,default_color);
+        size+=WriteBlobByte(image,mask->compose == NoCompositeOp ? 2 : 0);
+        size+=WriteBlobMSBShort(image,0);
+      }
+    size+=WriteBlobLong(image,0);
+    size+=WritePascalString(image,property,4);
+    if (info != (const StringInfo *) NULL)
+      size+=WriteBlob(image,GetStringInfoLength(info),
+        GetStringInfoDatum(info));
+    next_image=GetNextImageInList(next_image);
+  }
+  /*
+    Now the image data!
+  */
+  next_image=base_image;
+  layer_index=0;
+  while (next_image != NULL)
+  {
+    length=WritePSDChannels(psd_info,image_info,image,next_image,
+      layer_size_offsets[layer_index++],MagickTrue,exception);
+    if (length == 0)
+      {
+        status=MagickFalse;
+        break;
+      }
+    size+=length;
+    next_image=GetNextImageInList(next_image);
+  }
+  /*
+    Write the total size
+  */
+  if (layers_size != (size_t*) NULL)
+    *layers_size=size;
+  if ((size/2) != ((size+1)/2))
+    rounded_size=size+1;
+  else
+    rounded_size=size;
+  (void) WritePSDSize(psd_info,image,rounded_size,size_offset);
+  layer_size_offsets=(MagickOffsetType *) RelinquishMagickMemory(
+    layer_size_offsets);
+  /*
+    Remove the opacity mask from the registry
+  */
+  next_image=base_image;
+  while (next_image != (Image *) NULL)
+  {
+    property=GetImageArtifact(next_image,"psd:opacity-mask");
+    if (property != (const char *) NULL)
+      DeleteImageRegistry(property);
+    next_image=GetNextImageInList(next_image);
+  }
+
+  return(status);
+}
+
+ModuleExport MagickBooleanType WritePSDLayers(Image * image,
+  const ImageInfo *image_info,const PSDInfo *psd_info,ExceptionInfo *exception)
+{
+  PolicyDomain
+    domain;
+
+  PolicyRights
+    rights;
+
+  domain=CoderPolicyDomain;
+  rights=WritePolicyRights;
+  if (IsRightsAuthorized(domain,rights,"PSD") == MagickFalse)
+    return(MagickTrue);
+  return WritePSDLayersInternal(image,image_info,psd_info,(size_t*) NULL,
+    exception);
+}
+
+static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
+  Image *image,ExceptionInfo *exception)
+{
+  const StringInfo
+    *icc_profile;
+
+  Image
+    *base_image,
+    *next_image;
+
+  MagickBooleanType
+    status;
+
+  PSDInfo
+    psd_info;
+
+  register ssize_t
+    i;
+
+  size_t
+    length,
+    num_channels,
+    packet_size;
+
   StringInfo
     *bim_profile;
 
@@ -3270,175 +3492,22 @@ static MagickBooleanType WritePSDImage(const ImageInfo *image_info,
           PSDQuantum(GetStringInfoLength(icc_profile)))
         (void) WriteBlobByte(image,0);
     }
-  base_image=GetNextImageInList(image);
-  if (base_image == (Image *) NULL)
-    base_image=image;
-  size=0;
-  size_offset=TellBlob(image);
-  SetPSDSize(&psd_info,image,0);
-  SetPSDSize(&psd_info,image,0);
-  layer_count=0;
-  for (next_image=base_image; next_image != NULL; )
-  {
-    layer_count++;
-    next_image=GetNextImageInList(next_image);
-  }
-  if (image->alpha_trait != UndefinedPixelTrait)
-    size+=WriteBlobMSBShort(image,-(unsigned short) layer_count);
-  else
-    size+=WriteBlobMSBShort(image,(unsigned short) layer_count);
-  layer_size_offsets=(MagickOffsetType *) AcquireQuantumMemory(
-    (size_t) layer_count,sizeof(MagickOffsetType));
-  if (layer_size_offsets == (MagickOffsetType *) NULL)
-    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
-  layer_index=0;
-  for (next_image=base_image; next_image != NULL; )
-  {
-    Image
-      *mask;
-
-    unsigned char
-      default_color;
+  if (status != MagickFalse)
+    {
+      MagickOffsetType
+        size_offset;
 
-    unsigned short
-      channels,
-      total_channels;
+      size_t
+        size;
 
-    mask=(Image *) NULL;
-    property=GetImageArtifact(next_image,"psd:opacity-mask");
-    default_color=0;
-    if (property != (const char *) NULL)
-      {
-        mask=(Image *) GetImageRegistry(ImageRegistryType,property,exception);
-        default_color=strlen(property) == 9 ? 255 : 0;
-      }
-    size+=WriteBlobMSBLong(image,(unsigned int) next_image->page.y);
-    size+=WriteBlobMSBLong(image,(unsigned int) next_image->page.x);
-    size+=WriteBlobMSBLong(image,(unsigned int) (next_image->page.y+
-      next_image->rows));
-    size+=WriteBlobMSBLong(image,(unsigned int) (next_image->page.x+
-      next_image->columns));
-    channels=1U;
-    if ((next_image->storage_class != PseudoClass) &&
-        (IsImageGray(next_image) == MagickFalse))
-      channels=next_image->colorspace == CMYKColorspace ? 4U : 3U;
-    total_channels=channels;
-    if (next_image->alpha_trait != UndefinedPixelTrait)
-      total_channels++;
-    if (mask != (Image *) NULL)
-      total_channels++;
-    size+=WriteBlobMSBShort(image,total_channels);
-    layer_size_offsets[layer_index++]=TellBlob(image);
-    for (i=0; i < (ssize_t) channels; i++)
-      size+=WriteChannelSize(&psd_info,image,(signed short) i);
-    if (next_image->alpha_trait != UndefinedPixelTrait)
-      size+=WriteChannelSize(&psd_info,image,-1);
-    if (mask != (Image *) NULL)
-      size+=WriteChannelSize(&psd_info,image,-2);
-    size+=WriteBlob(image,4,(const unsigned char *) "8BIM");
-    size+=WriteBlob(image,4,(const unsigned char *)
-      CompositeOperatorToPSDBlendMode(next_image->compose));
-    property=GetImageArtifact(next_image,"psd:layer.opacity");
-    if (property != (const char *) NULL)
-      {
-        Quantum
-          opacity;
-
-        opacity=(Quantum) StringToInteger(property);
-        size+=WriteBlobByte(image,ScaleQuantumToChar(opacity));
-        (void) ApplyPSDLayerOpacity(next_image,opacity,MagickTrue,exception);
-      }
-    else
-      size+=WriteBlobByte(image,255);
-    size+=WriteBlobByte(image,0);
-    size+=WriteBlobByte(image,next_image->compose==NoCompositeOp ?
-      1 << 0x02 : 1); /* layer properties - visible, etc. */
-    size+=WriteBlobByte(image,0);
-    info=GetAdditionalInformation(image_info,next_image,exception);
-    property=(const char *) GetImageProperty(next_image,"label",exception);
-    if (property == (const char *) NULL)
-      {
-        (void) FormatLocaleString(layer_name,MagickPathExtent,"L%.20g",
-          (double) layer_index);
-        property=layer_name;
-      }
-    name_length=strlen(property)+1;
-    if ((name_length % 4) != 0)
-      name_length+=(4-(name_length % 4));
-    if (info != (const StringInfo *) NULL)
-      name_length+=GetStringInfoLength(info);
-    name_length+=8;
-    if (mask != (Image *) NULL)
-      name_length+=20;
-    size+=WriteBlobMSBLong(image,(unsigned int) name_length);
-    if (mask == (Image *) NULL)
-      size+=WriteBlobMSBLong(image,0);
-    else
-      {
-        if (mask->compose != NoCompositeOp)
-          (void) ApplyPSDOpacityMask(next_image,mask,ScaleCharToQuantum(
-            default_color),MagickTrue,exception);
-        mask->page.y+=image->page.y;
-        mask->page.x+=image->page.x;
-        size+=WriteBlobMSBLong(image,20);
-        size+=WriteBlobMSBSignedLong(image,mask->page.y);
-        size+=WriteBlobMSBSignedLong(image,mask->page.x);
-        size+=WriteBlobMSBSignedLong(image,(const signed int) mask->rows+
-          mask->page.y);
-        size+=WriteBlobMSBSignedLong(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,exception);
-    if (length == 0)
-      {
-        status=MagickFalse;
-        break;
-      }
-    size+=length;
-    next_image=GetNextImageInList(next_image);
-  }
+      size_offset=TellBlob(image);
+      SetPSDSize(&psd_info,image,0);
+      status=WritePSDLayersInternal(image,image_info,&psd_info,&size,
+        exception);
+      size_offset+=WritePSDSize(&psd_info,image,size+
+        (psd_info.version == 1 ? 8 : 16),size_offset);
+    }
   (void) WriteBlobMSBLong(image,0);  /* user mask data */
-  /*
-    Write the total size
-  */
-  size_offset+=WritePSDSize(&psd_info,image,size+
-    (psd_info.version == 1 ? 8 : 16),size_offset);
-  if ((size/2) != ((size+1)/2))
-    rounded_size=size+1;
-  else
-    rounded_size=size;
-  (void) WritePSDSize(&psd_info,image,rounded_size,size_offset);
-  layer_size_offsets=(MagickOffsetType *) RelinquishMagickMemory(
-    layer_size_offsets);
-  /*
-    Remove the opacity mask from the registry
-  */
-  next_image=base_image;
-  while (next_image != (Image *) NULL)
-  {
-    property=GetImageArtifact(next_image,"psd:opacity-mask");
-    if (property != (const char *) NULL)
-      DeleteImageRegistry(property);
-    next_image=GetNextImageInList(next_image);
-  }
   /*
     Write composite image.
   */
index 77512c668a8eb31fabc70daa04daf72b4f583228..ca34bc684604656ccea80d80c9124200651181f3 100644 (file)
@@ -121,6 +121,20 @@ typedef enum
   ReadGenericMethod
 } TIFFMethodType;
 
+typedef struct _PhotoshopProfile
+{
+  StringInfo
+    *data;
+
+  MagickOffsetType
+    offset;
+
+  size_t
+    length,
+    extent,
+    quantum;
+} PhotoshopProfile;
+
 #if defined(MAGICKCORE_HAVE_TIFFREADEXIFDIRECTORY)
 typedef struct _ExifInfo
 {
@@ -222,6 +236,65 @@ static MagickBooleanType
   WriteGROUP4Image(const ImageInfo *,Image *,ExceptionInfo *),
   WritePTIFImage(const ImageInfo *,Image *,ExceptionInfo *),
   WriteTIFFImage(const ImageInfo *,Image *,ExceptionInfo *);
+
+static MagickOffsetType TIFFSeekCustomStream(const MagickOffsetType offset,
+  const int whence,void *user_data)
+{
+  PhotoshopProfile
+    *profile;
+
+  profile=(PhotoshopProfile *) user_data;
+  switch (whence)
+  {
+    case SEEK_SET:
+    default:
+    {
+      if (offset < 0)
+        return(-1);
+      profile->offset=offset;
+      break;
+    }
+    case SEEK_CUR:
+    {
+      if ((profile->offset+offset) < 0)
+        return(-1);
+      profile->offset+=offset;
+      break;
+    }
+    case SEEK_END:
+    {
+      if (((MagickOffsetType) profile->length+offset) < 0)
+        return(-1);
+      profile->offset=profile->length+offset;
+      break;
+    }
+  }
+
+  return(profile->offset);
+}
+
+static MagickOffsetType TIFFTellCustomStream(void *user_data)
+{
+  PhotoshopProfile
+    *profile;
+
+  profile=(PhotoshopProfile *) user_data;
+  return(profile->offset);
+}
+
+static void InitPSDInfo(const Image *image, PSDInfo *info)
+{
+  info->version=1;
+  info->columns=image->columns;
+  info->rows=image->rows;
+  /* Setting the mode to a value that won't change the colorspace */
+  info->mode=10;
+  info->channels=1U;
+  if (image->storage_class == PseudoClass)
+    info->mode=2; // indexed mode
+  else
+    info->channels=(unsigned short) image->number_channels;
+}
 #endif
 \f
 /*
@@ -1008,6 +1081,45 @@ static TIFFMethodType GetJPEGMethod(Image* image,TIFF *tiff,uint16 photometric,
   return(method);
 }
 
+static ssize_t TIFFReadCustomStream(unsigned char *data,const size_t count,
+  void *user_data)
+{
+  PhotoshopProfile
+    *profile;
+
+  size_t
+    total;
+
+  ssize_t
+    remaining;
+
+  if (count == 0)
+    return(0);
+  profile=(PhotoshopProfile *) user_data;
+  remaining=(MagickOffsetType) profile->length-profile->offset;
+  if (remaining <= 0)
+    return(-1);
+  total=MagickMin(count, (size_t) remaining);
+  (void) memcpy(data,profile->data->datum+profile->offset,total);
+  return(total);
+}
+
+static CustomStreamInfo *TIFFAcquireCustomStreamForReading(
+  PhotoshopProfile *profile,ExceptionInfo *exception)
+{
+  CustomStreamInfo
+    *custom_stream;
+
+  custom_stream=AcquireCustomStreamInfo(exception);
+  if (custom_stream == (CustomStreamInfo *) NULL)
+    return(custom_stream);
+  SetCustomStreamData(custom_stream,(void *) profile);
+  SetCustomStreamReader(custom_stream,TIFFReadCustomStream);
+  SetCustomStreamSeeker(custom_stream,TIFFSeekCustomStream);
+  SetCustomStreamTeller(custom_stream,TIFFTellCustomStream);
+  return(custom_stream);
+}
+
 static void TIFFReadPhotoshopLayers(Image* image,const ImageInfo *image_info,
   ExceptionInfo *exception)
 {
@@ -1015,11 +1127,17 @@ static void TIFFReadPhotoshopLayers(Image* image,const ImageInfo *image_info,
     *option;
 
   const StringInfo
-    *layer_info;
+    *profile;
+
+  CustomStreamInfo
+    *custom_stream;
 
   Image
     *layers;
 
+  PhotoshopProfile
+    photoshop_profile;
+
   PSDInfo
     info;
 
@@ -1033,41 +1151,44 @@ static void TIFFReadPhotoshopLayers(Image* image,const ImageInfo *image_info,
   option=GetImageOption(image_info,"tiff:ignore-layers");
   if (option != (const char * ) NULL)
     return;
-  layer_info=GetImageProfile(image,"tiff:37724");
-  if (layer_info == (const StringInfo *) NULL)
+  profile=GetImageProfile(image,"tiff:37724");
+  if (profile == (const StringInfo *) NULL)
     return;
-  for (i=0; i < (ssize_t) layer_info->length-8; i++)
+  for (i=0; i < (ssize_t) profile->length-8; i++)
   {
-    if (LocaleNCompare((const char *) (layer_info->datum+i),
+    if (LocaleNCompare((const char *) (profile->datum+i),
         image->endian == MSBEndian ? "8BIM" : "MIB8",4) != 0)
       continue;
     i+=4;
-    if ((LocaleNCompare((const char *) (layer_info->datum+i),
+    if ((LocaleNCompare((const char *) (profile->datum+i),
          image->endian == MSBEndian ? "Layr" : "ryaL",4) == 0) ||
-        (LocaleNCompare((const char *) (layer_info->datum+i),
+        (LocaleNCompare((const char *) (profile->datum+i),
          image->endian == MSBEndian ? "LMsk" : "ksML",4) == 0) ||
-        (LocaleNCompare((const char *) (layer_info->datum+i),
+        (LocaleNCompare((const char *) (profile->datum+i),
          image->endian == MSBEndian ? "Lr16" : "61rL",4) == 0) ||
-        (LocaleNCompare((const char *) (layer_info->datum+i),
+        (LocaleNCompare((const char *) (profile->datum+i),
          image->endian == MSBEndian ? "Lr32" : "23rL",4) == 0))
       break;
   }
   i+=4;
-  if (i >= (ssize_t) (layer_info->length-8))
+  if (i >= (ssize_t) (profile->length-8))
+    return;
+  photoshop_profile.data=(StringInfo *) profile;
+  photoshop_profile.length=profile->length;
+  custom_stream=TIFFAcquireCustomStreamForReading(&photoshop_profile,exception);
+  if (custom_stream == (CustomStreamInfo *) NULL)
     return;
   layers=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
+  if (layers == (Image *) NULL)
+    {
+      custom_stream=DestroyCustomStreamInfo(custom_stream);
+      return;
+    }
   (void) DeleteImageProfile(layers,"tiff:37724");
-  AttachBlob(layers->blob,layer_info->datum,layer_info->length);
+  AttachCustomStream(layers->blob,custom_stream);
   SeekBlob(layers,(MagickOffsetType) i,SEEK_SET);
-  info.version=1;
-  info.columns=layers->columns;
-  info.rows=layers->rows;
-  info.channels=(unsigned short) layers->number_channels;
-  /* Setting the mode to a value that won't change the colorspace */
-  info.mode=10;
-  ReadPSDLayers(layers,image_info,&info,MagickFalse,exception);
-  /* we need to set the datum in case a realloc happened */
-  ((StringInfo *) layer_info)->datum=GetBlobStreamData(layers);
+  InitPSDInfo(layers,&info);
+  (void) ReadPSDLayers(layers,image_info,&info,exception);
   DeleteImageFromList(&layers);
   if (layers != (Image *) NULL)
     {
@@ -1080,6 +1201,7 @@ static void TIFFReadPhotoshopLayers(Image* image,const ImageInfo *image_info,
         layers=GetNextImageInList(layers);
       }
     }
+  custom_stream=DestroyCustomStreamInfo(custom_stream);
 }
 
 #if defined(__cplusplus) || defined(c_plusplus)
@@ -2931,6 +3053,137 @@ static int32 TIFFWritePixels(TIFF *tiff,TIFFInfo *tiff_info,ssize_t row,
   return(status);
 }
 
+static ssize_t TIFFWriteCustomStream(unsigned char *data,const size_t count,
+  void *user_data)
+{
+  PhotoshopProfile
+    *profile;
+
+  if (count == 0)
+    return(0);
+  profile=(PhotoshopProfile *) user_data;
+  if ((profile->offset+(MagickOffsetType) count) >=
+        (MagickOffsetType) profile->extent)
+    {
+      profile->extent+=count+profile->quantum;
+      profile->quantum<<=1;
+      SetStringInfoLength(profile->data,profile->extent);
+    }
+  (void) memcpy(profile->data->datum+profile->offset,data,count);
+  profile->offset+=count;
+  return(count);
+}
+
+static CustomStreamInfo *TIFFAcquireCustomStreamForWriting(
+  PhotoshopProfile *profile,ExceptionInfo *exception)
+{
+  CustomStreamInfo
+    *custom_stream;
+
+  custom_stream=AcquireCustomStreamInfo(exception);
+  if (custom_stream == (CustomStreamInfo *) NULL)
+    return(custom_stream);
+  SetCustomStreamData(custom_stream,(void *) profile);
+  SetCustomStreamWriter(custom_stream,TIFFWriteCustomStream);
+  SetCustomStreamSeeker(custom_stream,TIFFSeekCustomStream);
+  SetCustomStreamTeller(custom_stream,TIFFTellCustomStream);
+  return(custom_stream);
+}
+
+static MagickBooleanType TIFFWritePhotoshopLayers(Image* image,
+  const ImageInfo *image_info,ExceptionInfo *exception)
+{
+  BlobInfo
+    *blob;
+
+  CustomStreamInfo
+    *custom_stream;
+
+  Image
+    *next;
+
+  ImageInfo
+    *clone_info;
+
+  MagickBooleanType
+    status;
+
+  PhotoshopProfile
+    profile;
+
+  PSDInfo
+    info;
+
+  size_t
+    length;
+
+  StringInfo
+    *layers;
+
+  next=image->next;
+  if (next == (Image *) NULL)
+    return(MagickTrue);
+  clone_info=CloneImageInfo(image_info);
+  if (clone_info == (ImageInfo *) NULL)
+      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+        image->filename);
+  profile.offset=0;
+  profile.quantum=MagickMinBlobExtent;
+  layers=AcquireStringInfo(profile.quantum);
+  if (layers == (StringInfo *) NULL)
+    {
+      clone_info=DestroyImageInfo(clone_info);
+      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+        image->filename);
+    }
+  profile.data=layers;
+  profile.extent=layers->length;
+  custom_stream=TIFFAcquireCustomStreamForWriting(&profile,exception);
+  if (custom_stream == (CustomStreamInfo *) NULL)
+    {
+      clone_info=DestroyImageInfo(clone_info);
+      layers=DestroyStringInfo(layers);
+      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+        image->filename);
+    }
+  blob=CloneBlobInfo((BlobInfo *) NULL);
+  if (blob == (BlobInfo *) NULL)
+    {
+      clone_info=DestroyImageInfo(clone_info);
+      layers=DestroyStringInfo(layers);
+      custom_stream=DestroyCustomStreamInfo(custom_stream);
+      ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+        image->filename);
+    }
+  DestroyBlob(next);
+  next->blob=blob;
+  while (next != (Image *) NULL)
+    next=SyncNextImageInList(next);
+  next=image->next;
+  AttachCustomStream(next->blob,custom_stream);
+  InitPSDInfo(image,&info);
+  if (next->endian == UndefinedEndian)
+    next->endian=(HOST_FILLORDER == FILLORDER_LSB2MSB) ? LSBEndian : MSBEndian;
+  WriteBlobString(next,"Adobe Photoshop Document Data Block");
+  WriteBlobByte(next,0);
+  WriteBlobString(next,next->endian == LSBEndian ? "MIB8ryaL" : "8BIMLayr");
+  status=WritePSDLayers(next,clone_info,&info,exception);
+  if (status != MagickFalse)
+    {
+      SetStringInfoLength(layers,(size_t) profile.offset);
+      status=SetImageProfile(image,"tiff:37724",layers,exception);
+    }
+  while (next != (Image *) NULL)
+  {
+    CloseBlob(next);
+    next=next->next;
+  }
+  layers=DestroyStringInfo(layers);
+  clone_info=DestroyImageInfo(clone_info);
+  custom_stream=DestroyCustomStreamInfo(custom_stream);
+  return(status);
+}
+
 static void TIFFSetProfiles(TIFF *tiff,Image *image)
 {
   const char
@@ -2994,7 +3247,7 @@ static void TIFFSetProfiles(TIFF *tiff,Image *image)
   }
 }
 
-static void TIFFSetProperties(TIFF *tiff,const ImageInfo *image_info,
+static void TIFFSetProperties(TIFF *tiff,const MagickBooleanType adjoin,
   Image *image,ExceptionInfo *exception)
 {
   const char
@@ -3056,7 +3309,7 @@ static void TIFFSetProperties(TIFF *tiff,const ImageInfo *image_info,
 
       page=(uint16) image->scene;
       pages=(uint16) GetImageListLength(image);
-      if ((image_info->adjoin != MagickFalse) && (pages > 1))
+      if ((adjoin != MagickFalse) && (pages > 1))
         (void) TIFFSetField(tiff,TIFFTAG_SUBFILETYPE,FILETYPE_PAGE);
       (void) TIFFSetField(tiff,TIFFTAG_PAGENUMBER,page,pages);
     }
@@ -3145,6 +3398,7 @@ static MagickBooleanType WriteTIFFImage(const ImageInfo *image_info,
     endian_type;
 
   MagickBooleanType
+    adjoin,
     debug,
     status;
 
@@ -3228,6 +3482,7 @@ static MagickBooleanType WriteTIFFImage(const ImageInfo *image_info,
   scene=0;
   debug=IsEventLogging();
   (void) debug;
+  adjoin=image_info->adjoin;
   do
   {
     /*
@@ -3682,8 +3937,14 @@ static MagickBooleanType WriteTIFFImage(const ImageInfo *image_info,
         chromaticity[1]=(float) image->chromaticity.white_point.y;
         (void) TIFFSetField(tiff,TIFFTAG_WHITEPOINT,chromaticity);
       }
+    option=GetImageOption(image_info,"tiff:write-layers");
+    if (IsStringTrue(option) != MagickFalse)
+      {
+        (void) TIFFWritePhotoshopLayers(image,image_info,exception);
+        adjoin=MagickFalse;
+      }
     if ((LocaleCompare(image_info->magick,"PTIF") != 0) &&
-        (image_info->adjoin != MagickFalse) && (GetImageListLength(image) > 1))
+        (adjoin != MagickFalse) && (GetImageListLength(image) > 1))
       {
         (void) TIFFSetField(tiff,TIFFTAG_SUBFILETYPE,FILETYPE_PAGE);
         if (image->scene != 0)
@@ -3692,7 +3953,7 @@ static MagickBooleanType WriteTIFFImage(const ImageInfo *image_info,
       }
     if (image->orientation != UndefinedOrientation)
       (void) TIFFSetField(tiff,TIFFTAG_ORIENTATION,(uint16) image->orientation);
-    (void) TIFFSetProfiles(tiff,image);
+    TIFFSetProfiles(tiff,image);
     {
       uint16
         page,
@@ -3701,11 +3962,11 @@ static MagickBooleanType WriteTIFFImage(const ImageInfo *image_info,
       page=(uint16) scene;
       pages=(uint16) GetImageListLength(image);
       if ((LocaleCompare(image_info->magick,"PTIF") != 0) &&
-          (image_info->adjoin != MagickFalse) && (pages > 1))
+          (adjoin != MagickFalse) && (pages > 1))
         (void) TIFFSetField(tiff,TIFFTAG_SUBFILETYPE,FILETYPE_PAGE);
       (void) TIFFSetField(tiff,TIFFTAG_PAGENUMBER,page,pages);
     }
-    (void) TIFFSetProperties(tiff,image_info,image,exception);
+    (void) TIFFSetProperties(tiff,adjoin,image,exception);
 DisableMSCWarning(4127)
     if (0)
 RestoreMSCWarning
@@ -3966,7 +4227,7 @@ RestoreMSCWarning
       GetImageListLength(image));
     if (status == MagickFalse)
       break;
-  } while (image_info->adjoin != MagickFalse);
+  } while (adjoin != MagickFalse);
   TIFFClose(tiff);
   return(MagickTrue);
 }