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