|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% PPPP RRRR OOO FFFFF IIIII L EEEEE %
|
|
|
% P P R R O O F I L E %
|
|
|
% PPPP RRRR O O FFF I L EEE %
|
|
|
% P R R O O F I L E %
|
|
|
% P R R OOO F IIIII LLLLL EEEEE %
|
|
|
% %
|
|
|
% %
|
|
|
% MagickCore Image Profile Methods %
|
|
|
% %
|
|
|
% Software Design %
|
|
|
% Cristy %
|
|
|
% July 1992 %
|
|
|
% %
|
|
|
% %
|
|
|
% Copyright 1999-2021 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 %
|
|
|
% %
|
|
|
% https://imagemagick.org/script/license.php %
|
|
|
% %
|
|
|
% Unless required by applicable law or agreed to in writing, software %
|
|
|
% distributed under the License is distributed on an "AS IS" BASIS, %
|
|
|
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
|
|
|
% See the License for the specific language governing permissions and %
|
|
|
% limitations under the License. %
|
|
|
% %
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
%
|
|
|
%
|
|
|
*/
|
|
|
|
|
|
/*
|
|
|
Include declarations.
|
|
|
*/
|
|
|
#include "MagickCore/studio.h"
|
|
|
#include "MagickCore/artifact.h"
|
|
|
#include "MagickCore/attribute.h"
|
|
|
#include "MagickCore/cache.h"
|
|
|
#include "MagickCore/color.h"
|
|
|
#include "MagickCore/colorspace-private.h"
|
|
|
#include "MagickCore/configure.h"
|
|
|
#include "MagickCore/exception.h"
|
|
|
#include "MagickCore/exception-private.h"
|
|
|
#include "MagickCore/image.h"
|
|
|
#include "MagickCore/linked-list.h"
|
|
|
#include "MagickCore/memory_.h"
|
|
|
#include "MagickCore/monitor.h"
|
|
|
#include "MagickCore/monitor-private.h"
|
|
|
#include "MagickCore/option.h"
|
|
|
#include "MagickCore/option-private.h"
|
|
|
#include "MagickCore/pixel-accessor.h"
|
|
|
#include "MagickCore/profile.h"
|
|
|
#include "MagickCore/profile-private.h"
|
|
|
#include "MagickCore/property.h"
|
|
|
#include "MagickCore/quantum.h"
|
|
|
#include "MagickCore/quantum-private.h"
|
|
|
#include "MagickCore/resource_.h"
|
|
|
#include "MagickCore/splay-tree.h"
|
|
|
#include "MagickCore/string_.h"
|
|
|
#include "MagickCore/string-private.h"
|
|
|
#include "MagickCore/thread-private.h"
|
|
|
#include "MagickCore/token.h"
|
|
|
#include "MagickCore/utility.h"
|
|
|
#if defined(MAGICKCORE_LCMS_DELEGATE)
|
|
|
#if defined(MAGICKCORE_HAVE_LCMS_LCMS2_H)
|
|
|
#include <wchar.h>
|
|
|
#include <lcms/lcms2.h>
|
|
|
#else
|
|
|
#include <wchar.h>
|
|
|
#include "lcms2.h"
|
|
|
#endif
|
|
|
#endif
|
|
|
#if defined(MAGICKCORE_XML_DELEGATE)
|
|
|
# if defined(MAGICKCORE_WINDOWS_SUPPORT)
|
|
|
# if !defined(__MINGW32__)
|
|
|
# include <win32config.h>
|
|
|
# endif
|
|
|
# endif
|
|
|
# include <libxml/parser.h>
|
|
|
# include <libxml/tree.h>
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
|
Forward declarations
|
|
|
*/
|
|
|
static MagickBooleanType
|
|
|
SetImageProfileInternal(Image *,const char *,const StringInfo *,
|
|
|
const MagickBooleanType,ExceptionInfo *);
|
|
|
|
|
|
static void
|
|
|
WriteTo8BimProfile(Image *,const char*,const StringInfo *);
|
|
|
|
|
|
/*
|
|
|
Typedef declarations
|
|
|
*/
|
|
|
struct _ProfileInfo
|
|
|
{
|
|
|
char
|
|
|
*name;
|
|
|
|
|
|
size_t
|
|
|
length;
|
|
|
|
|
|
unsigned char
|
|
|
*info;
|
|
|
|
|
|
size_t
|
|
|
signature;
|
|
|
};
|
|
|
|
|
|
typedef struct _CMSExceptionInfo
|
|
|
{
|
|
|
Image
|
|
|
*image;
|
|
|
|
|
|
ExceptionInfo
|
|
|
*exception;
|
|
|
} CMSExceptionInfo;
|
|
|
|
|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% C l o n e I m a g e P r o f i l e s %
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
%
|
|
|
% CloneImageProfiles() clones one or more image profiles.
|
|
|
%
|
|
|
% The format of the CloneImageProfiles method is:
|
|
|
%
|
|
|
% MagickBooleanType CloneImageProfiles(Image *image,
|
|
|
% const Image *clone_image)
|
|
|
%
|
|
|
% A description of each parameter follows:
|
|
|
%
|
|
|
% o image: the image.
|
|
|
%
|
|
|
% o clone_image: the clone image.
|
|
|
%
|
|
|
*/
|
|
|
MagickExport MagickBooleanType CloneImageProfiles(Image *image,
|
|
|
const Image *clone_image)
|
|
|
{
|
|
|
assert(image != (Image *) NULL);
|
|
|
assert(image->signature == MagickCoreSignature);
|
|
|
if (image->debug != MagickFalse)
|
|
|
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
|
|
|
assert(clone_image != (const Image *) NULL);
|
|
|
assert(clone_image->signature == MagickCoreSignature);
|
|
|
if (clone_image->profiles != (void *) NULL)
|
|
|
{
|
|
|
if (image->profiles != (void *) NULL)
|
|
|
DestroyImageProfiles(image);
|
|
|
image->profiles=CloneSplayTree((SplayTreeInfo *) clone_image->profiles,
|
|
|
(void *(*)(void *)) ConstantString,(void *(*)(void *)) CloneStringInfo);
|
|
|
}
|
|
|
return(MagickTrue);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% D e l e t e I m a g e P r o f i l e %
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
%
|
|
|
% DeleteImageProfile() deletes a profile from the image by its name.
|
|
|
%
|
|
|
% The format of the DeleteImageProfile method is:
|
|
|
%
|
|
|
% MagickBooleanTyupe DeleteImageProfile(Image *image,const char *name)
|
|
|
%
|
|
|
% A description of each parameter follows:
|
|
|
%
|
|
|
% o image: the image.
|
|
|
%
|
|
|
% o name: the profile name.
|
|
|
%
|
|
|
*/
|
|
|
MagickExport MagickBooleanType DeleteImageProfile(Image *image,const char *name)
|
|
|
{
|
|
|
assert(image != (Image *) NULL);
|
|
|
assert(image->signature == MagickCoreSignature);
|
|
|
if (image->debug != MagickFalse)
|
|
|
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
|
|
|
if (image->profiles == (SplayTreeInfo *) NULL)
|
|
|
return(MagickFalse);
|
|
|
WriteTo8BimProfile(image,name,(StringInfo *) NULL);
|
|
|
return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->profiles,name));
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% D e s t r o y I m a g e P r o f i l e s %
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
%
|
|
|
% DestroyImageProfiles() releases memory associated with an image profile map.
|
|
|
%
|
|
|
% The format of the DestroyProfiles method is:
|
|
|
%
|
|
|
% void DestroyImageProfiles(Image *image)
|
|
|
%
|
|
|
% A description of each parameter follows:
|
|
|
%
|
|
|
% o image: the image.
|
|
|
%
|
|
|
*/
|
|
|
MagickExport void DestroyImageProfiles(Image *image)
|
|
|
{
|
|
|
if (image->profiles != (SplayTreeInfo *) NULL)
|
|
|
image->profiles=DestroySplayTree((SplayTreeInfo *) image->profiles);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% G e t I m a g e P r o f i l e %
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
%
|
|
|
% GetImageProfile() gets a profile associated with an image by name.
|
|
|
%
|
|
|
% The format of the GetImageProfile method is:
|
|
|
%
|
|
|
% const StringInfo *GetImageProfile(const Image *image,const char *name)
|
|
|
%
|
|
|
% A description of each parameter follows:
|
|
|
%
|
|
|
% o image: the image.
|
|
|
%
|
|
|
% o name: the profile name.
|
|
|
%
|
|
|
*/
|
|
|
MagickExport const StringInfo *GetImageProfile(const Image *image,
|
|
|
const char *name)
|
|
|
{
|
|
|
const StringInfo
|
|
|
*profile;
|
|
|
|
|
|
assert(image != (Image *) NULL);
|
|
|
assert(image->signature == MagickCoreSignature);
|
|
|
if (image->debug != MagickFalse)
|
|
|
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
|
|
|
if (image->profiles == (SplayTreeInfo *) NULL)
|
|
|
return((StringInfo *) NULL);
|
|
|
profile=(const StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
|
|
|
image->profiles,name);
|
|
|
return(profile);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% G e t N e x t I m a g e P r o f i l e %
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
%
|
|
|
% GetNextImageProfile() gets the next profile name for an image.
|
|
|
%
|
|
|
% The format of the GetNextImageProfile method is:
|
|
|
%
|
|
|
% char *GetNextImageProfile(const Image *image)
|
|
|
%
|
|
|
% A description of each parameter follows:
|
|
|
%
|
|
|
% o hash_info: the hash info.
|
|
|
%
|
|
|
*/
|
|
|
MagickExport char *GetNextImageProfile(const Image *image)
|
|
|
{
|
|
|
assert(image != (Image *) NULL);
|
|
|
assert(image->signature == MagickCoreSignature);
|
|
|
if (image->debug != MagickFalse)
|
|
|
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
|
|
|
if (image->profiles == (SplayTreeInfo *) NULL)
|
|
|
return((char *) NULL);
|
|
|
return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->profiles));
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% P r o f i l e I m a g e %
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
%
|
|
|
% ProfileImage() associates, applies, or removes an ICM, IPTC, or generic
|
|
|
% profile with / to / from an image. If the profile is NULL, it is removed
|
|
|
% from the image otherwise added or applied. Use a name of '*' and a profile
|
|
|
% of NULL to remove all profiles from the image.
|
|
|
%
|
|
|
% ICC and ICM profiles are handled as follows: If the image does not have
|
|
|
% an associated color profile, the one you provide is associated with the
|
|
|
% image and the image pixels are not transformed. Otherwise, the colorspace
|
|
|
% transform defined by the existing and new profile are applied to the image
|
|
|
% pixels and the new profile is associated with the image.
|
|
|
%
|
|
|
% The format of the ProfileImage method is:
|
|
|
%
|
|
|
% MagickBooleanType ProfileImage(Image *image,const char *name,
|
|
|
% const void *datum,const size_t length,const MagickBooleanType clone)
|
|
|
%
|
|
|
% A description of each parameter follows:
|
|
|
%
|
|
|
% o image: the image.
|
|
|
%
|
|
|
% o name: Name of profile to add or remove: ICC, IPTC, or generic profile.
|
|
|
%
|
|
|
% o datum: the profile data.
|
|
|
%
|
|
|
% o length: the length of the profile.
|
|
|
%
|
|
|
% o clone: should be MagickFalse.
|
|
|
%
|
|
|
*/
|
|
|
|
|
|
#if defined(MAGICKCORE_LCMS_DELEGATE)
|
|
|
|
|
|
typedef struct _LCMSInfo
|
|
|
{
|
|
|
ColorspaceType
|
|
|
colorspace;
|
|
|
|
|
|
cmsUInt32Number
|
|
|
type;
|
|
|
|
|
|
size_t
|
|
|
channels;
|
|
|
|
|
|
cmsHPROFILE
|
|
|
profile;
|
|
|
|
|
|
int
|
|
|
intent;
|
|
|
|
|
|
double
|
|
|
scale,
|
|
|
translate;
|
|
|
|
|
|
void
|
|
|
**magick_restrict pixels;
|
|
|
} LCMSInfo;
|
|
|
|
|
|
#if LCMS_VERSION < 2060
|
|
|
static void* cmsGetContextUserData(cmsContext ContextID)
|
|
|
{
|
|
|
return(ContextID);
|
|
|
}
|
|
|
|
|
|
static cmsContext cmsCreateContext(void *magick_unused(Plugin),void *UserData)
|
|
|
{
|
|
|
magick_unreferenced(Plugin);
|
|
|
return((cmsContext) UserData);
|
|
|
}
|
|
|
|
|
|
static void cmsSetLogErrorHandlerTHR(cmsContext magick_unused(ContextID),
|
|
|
cmsLogErrorHandlerFunction Fn)
|
|
|
{
|
|
|
magick_unreferenced(ContextID);
|
|
|
cmsSetLogErrorHandler(Fn);
|
|
|
}
|
|
|
|
|
|
static void cmsDeleteContext(cmsContext magick_unused(ContextID))
|
|
|
{
|
|
|
magick_unreferenced(ContextID);
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
static void **DestroyPixelThreadSet(void **pixels)
|
|
|
{
|
|
|
ssize_t
|
|
|
i;
|
|
|
|
|
|
if (pixels == (void **) NULL)
|
|
|
return((void **) NULL);
|
|
|
for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
|
|
|
if (pixels[i] != (void *) NULL)
|
|
|
pixels[i]=RelinquishMagickMemory(pixels[i]);
|
|
|
pixels=(void **) RelinquishMagickMemory(pixels);
|
|
|
return(pixels);
|
|
|
}
|
|
|
|
|
|
static void **AcquirePixelThreadSet(const size_t columns,
|
|
|
const size_t channels,MagickBooleanType highres)
|
|
|
{
|
|
|
ssize_t
|
|
|
i;
|
|
|
|
|
|
size_t
|
|
|
number_threads;
|
|
|
|
|
|
size_t
|
|
|
size;
|
|
|
|
|
|
void
|
|
|
**pixels;
|
|
|
|
|
|
number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
|
|
|
pixels=(void **) AcquireQuantumMemory(number_threads,sizeof(*pixels));
|
|
|
if (pixels == (void **) NULL)
|
|
|
return((void **) NULL);
|
|
|
(void) memset(pixels,0,number_threads*sizeof(*pixels));
|
|
|
size=sizeof(double);
|
|
|
if (highres == MagickFalse)
|
|
|
size=sizeof(Quantum);
|
|
|
for (i=0; i < (ssize_t) number_threads; i++)
|
|
|
{
|
|
|
pixels[i]=AcquireQuantumMemory(columns,channels*size);
|
|
|
if (pixels[i] == (void *) NULL)
|
|
|
return(DestroyPixelThreadSet(pixels));
|
|
|
}
|
|
|
return(pixels);
|
|
|
}
|
|
|
|
|
|
static cmsHTRANSFORM *DestroyTransformThreadSet(cmsHTRANSFORM *transform)
|
|
|
{
|
|
|
ssize_t
|
|
|
i;
|
|
|
|
|
|
assert(transform != (cmsHTRANSFORM *) NULL);
|
|
|
for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
|
|
|
if (transform[i] != (cmsHTRANSFORM) NULL)
|
|
|
cmsDeleteTransform(transform[i]);
|
|
|
transform=(cmsHTRANSFORM *) RelinquishMagickMemory(transform);
|
|
|
return(transform);
|
|
|
}
|
|
|
|
|
|
static cmsHTRANSFORM *AcquireTransformThreadSet(const LCMSInfo *source_info,
|
|
|
const LCMSInfo *target_info,const cmsUInt32Number flags,
|
|
|
cmsContext cms_context)
|
|
|
{
|
|
|
cmsHTRANSFORM
|
|
|
*transform;
|
|
|
|
|
|
ssize_t
|
|
|
i;
|
|
|
|
|
|
size_t
|
|
|
number_threads;
|
|
|
|
|
|
number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
|
|
|
transform=(cmsHTRANSFORM *) AcquireQuantumMemory(number_threads,
|
|
|
sizeof(*transform));
|
|
|
if (transform == (cmsHTRANSFORM *) NULL)
|
|
|
return((cmsHTRANSFORM *) NULL);
|
|
|
(void) memset(transform,0,number_threads*sizeof(*transform));
|
|
|
for (i=0; i < (ssize_t) number_threads; i++)
|
|
|
{
|
|
|
transform[i]=cmsCreateTransformTHR(cms_context,source_info->profile,
|
|
|
source_info->type,target_info->profile,target_info->type,
|
|
|
target_info->intent,flags);
|
|
|
if (transform[i] == (cmsHTRANSFORM) NULL)
|
|
|
return(DestroyTransformThreadSet(transform));
|
|
|
}
|
|
|
return(transform);
|
|
|
}
|
|
|
|
|
|
static void CMSExceptionHandler(cmsContext context,cmsUInt32Number severity,
|
|
|
const char *message)
|
|
|
{
|
|
|
CMSExceptionInfo
|
|
|
*cms_exception;
|
|
|
|
|
|
ExceptionInfo
|
|
|
*exception;
|
|
|
|
|
|
Image
|
|
|
*image;
|
|
|
|
|
|
cms_exception=(CMSExceptionInfo *) cmsGetContextUserData(context);
|
|
|
if (cms_exception == (CMSExceptionInfo *) NULL)
|
|
|
return;
|
|
|
exception=cms_exception->exception;
|
|
|
if (exception == (ExceptionInfo *) NULL)
|
|
|
return;
|
|
|
image=cms_exception->image;
|
|
|
if (image == (Image *) NULL)
|
|
|
{
|
|
|
(void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
|
|
|
"UnableToTransformColorspace","`%s'","unknown context");
|
|
|
return;
|
|
|
}
|
|
|
if (image->debug != MagickFalse)
|
|
|
(void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%u, %s",
|
|
|
severity,message != (char *) NULL ? message : "no message");
|
|
|
(void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
|
|
|
"UnableToTransformColorspace","`%s', %s (#%u)",image->filename,
|
|
|
message != (char *) NULL ? message : "no message",severity);
|
|
|
}
|
|
|
|
|
|
static void TransformDoublePixels(const int id,const Image* image,
|
|
|
const LCMSInfo *source_info,const LCMSInfo *target_info,
|
|
|
const cmsHTRANSFORM *transform,Quantum *q)
|
|
|
{
|
|
|
#define GetLCMSPixel(source_info,pixel) \
|
|
|
(source_info->scale*QuantumScale*(pixel)+source_info->translate)
|
|
|
#define SetLCMSPixel(target_info,pixel) \
|
|
|
ClampToQuantum(target_info->scale*QuantumRange*(pixel)+target_info->translate)
|
|
|
|
|
|
double
|
|
|
*p;
|
|
|
|
|
|
ssize_t
|
|
|
x;
|
|
|
|
|
|
p=(double *) source_info->pixels[id];
|
|
|
for (x=0; x < (ssize_t) image->columns; x++)
|
|
|
{
|
|
|
*p++=GetLCMSPixel(source_info,GetPixelRed(image,q));
|
|
|
if (source_info->channels > 1)
|
|
|
{
|
|
|
*p++=GetLCMSPixel(source_info,GetPixelGreen(image,q));
|
|
|
*p++=GetLCMSPixel(source_info,GetPixelBlue(image,q));
|
|
|
}
|
|
|
if (source_info->channels > 3)
|
|
|
*p++=GetLCMSPixel(source_info,GetPixelBlack(image,q));
|
|
|
q+=GetPixelChannels(image);
|
|
|
}
|
|
|
cmsDoTransform(transform[id],source_info->pixels[id],
|
|
|
target_info->pixels[id],(unsigned int) image->columns);
|
|
|
p=(double *) target_info->pixels[id];
|
|
|
q-=GetPixelChannels(image)*image->columns;
|
|
|
for (x=0; x < (ssize_t) image->columns; x++)
|
|
|
{
|
|
|
if (target_info->channels == 1)
|
|
|
SetPixelGray(image,SetLCMSPixel(target_info,*p),q);
|
|
|
else
|
|
|
SetPixelRed(image,SetLCMSPixel(target_info,*p),q);
|
|
|
p++;
|
|
|
if (target_info->channels > 1)
|
|
|
{
|
|
|
SetPixelGreen(image,SetLCMSPixel(target_info,*p),q);
|
|
|
p++;
|
|
|
SetPixelBlue(image,SetLCMSPixel(target_info,*p),q);
|
|
|
p++;
|
|
|
}
|
|
|
if (target_info->channels > 3)
|
|
|
{
|
|
|
SetPixelBlack(image,SetLCMSPixel(target_info,*p),q);
|
|
|
p++;
|
|
|
}
|
|
|
q+=GetPixelChannels(image);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static void TransformQuantumPixels(const int id,const Image* image,
|
|
|
const LCMSInfo *source_info,const LCMSInfo *target_info,
|
|
|
const cmsHTRANSFORM *transform,Quantum *q)
|
|
|
{
|
|
|
Quantum
|
|
|
*p;
|
|
|
|
|
|
ssize_t
|
|
|
x;
|
|
|
|
|
|
p=(Quantum *) source_info->pixels[id];
|
|
|
for (x=0; x < (ssize_t) image->columns; x++)
|
|
|
{
|
|
|
*p++=GetPixelRed(image,q);
|
|
|
if (source_info->channels > 1)
|
|
|
{
|
|
|
*p++=GetPixelGreen(image,q);
|
|
|
*p++=GetPixelBlue(image,q);
|
|
|
}
|
|
|
if (source_info->channels > 3)
|
|
|
*p++=GetPixelBlack(image,q);
|
|
|
q+=GetPixelChannels(image);
|
|
|
}
|
|
|
cmsDoTransform(transform[id],source_info->pixels[id],
|
|
|
target_info->pixels[id],(unsigned int) image->columns);
|
|
|
p=(Quantum *) target_info->pixels[id];
|
|
|
q-=GetPixelChannels(image)*image->columns;
|
|
|
for (x=0; x < (ssize_t) image->columns; x++)
|
|
|
{
|
|
|
if (target_info->channels == 1)
|
|
|
SetPixelGray(image,*p++,q);
|
|
|
else
|
|
|
SetPixelRed(image,*p++,q);
|
|
|
if (target_info->channels > 1)
|
|
|
{
|
|
|
SetPixelGreen(image,*p++,q);
|
|
|
SetPixelBlue(image,*p++,q);
|
|
|
}
|
|
|
if (target_info->channels > 3)
|
|
|
SetPixelBlack(image,*p++,q);
|
|
|
q+=GetPixelChannels(image);
|
|
|
}
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
static MagickBooleanType SetsRGBImageProfile(Image *image,
|
|
|
ExceptionInfo *exception)
|
|
|
{
|
|
|
static unsigned char
|
|
|
sRGBProfile[] =
|
|
|
{
|
|
|
0x00, 0x00, 0x0c, 0x8c, 0x61, 0x72, 0x67, 0x6c, 0x02, 0x20, 0x00, 0x00,
|
|
|
0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20,
|
|
|
0x07, 0xde, 0x00, 0x01, 0x00, 0x06, 0x00, 0x16, 0x00, 0x0f, 0x00, 0x3a,
|
|
|
0x61, 0x63, 0x73, 0x70, 0x4d, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x49, 0x45, 0x43, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6,
|
|
|
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x61, 0x72, 0x67, 0x6c,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
|
|
|
0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x99,
|
|
|
0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0xec, 0x00, 0x00, 0x00, 0x67,
|
|
|
0x64, 0x6d, 0x6e, 0x64, 0x00, 0x00, 0x02, 0x54, 0x00, 0x00, 0x00, 0x70,
|
|
|
0x64, 0x6d, 0x64, 0x64, 0x00, 0x00, 0x02, 0xc4, 0x00, 0x00, 0x00, 0x88,
|
|
|
0x74, 0x65, 0x63, 0x68, 0x00, 0x00, 0x03, 0x4c, 0x00, 0x00, 0x00, 0x0c,
|
|
|
0x76, 0x75, 0x65, 0x64, 0x00, 0x00, 0x03, 0x58, 0x00, 0x00, 0x00, 0x67,
|
|
|
0x76, 0x69, 0x65, 0x77, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x24,
|
|
|
0x6c, 0x75, 0x6d, 0x69, 0x00, 0x00, 0x03, 0xe4, 0x00, 0x00, 0x00, 0x14,
|
|
|
0x6d, 0x65, 0x61, 0x73, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x24,
|
|
|
0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x00, 0x00, 0x14,
|
|
|
0x62, 0x6b, 0x70, 0x74, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x14,
|
|
|
0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x44, 0x00, 0x00, 0x00, 0x14,
|
|
|
0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x58, 0x00, 0x00, 0x00, 0x14,
|
|
|
0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x6c, 0x00, 0x00, 0x00, 0x14,
|
|
|
0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
|
|
|
0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
|
|
|
0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
|
|
|
0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
|
|
|
0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36,
|
|
|
0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x28, 0x45, 0x71, 0x75, 0x69, 0x76,
|
|
|
0x61, 0x6c, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x77, 0x77,
|
|
|
0x2e, 0x73, 0x72, 0x67, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x31, 0x39,
|
|
|
0x39, 0x38, 0x20, 0x48, 0x50, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c,
|
|
|
0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x3f, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31,
|
|
|
0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x28, 0x45, 0x71, 0x75,
|
|
|
0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77,
|
|
|
0x77, 0x77, 0x2e, 0x73, 0x72, 0x67, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x20,
|
|
|
0x31, 0x39, 0x39, 0x38, 0x20, 0x48, 0x50, 0x20, 0x70, 0x72, 0x6f, 0x66,
|
|
|
0x69, 0x6c, 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x72, 0x65, 0x61,
|
|
|
0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x47, 0x72, 0x61, 0x65, 0x6d,
|
|
|
0x65, 0x20, 0x57, 0x2e, 0x20, 0x47, 0x69, 0x6c, 0x6c, 0x2e, 0x20, 0x52,
|
|
|
0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f,
|
|
|
0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
|
|
|
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x20, 0x4e, 0x6f, 0x20, 0x57,
|
|
|
0x61, 0x72, 0x72, 0x61, 0x6e, 0x74, 0x79, 0x2c, 0x20, 0x55, 0x73, 0x65,
|
|
|
0x20, 0x61, 0x74, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x6f, 0x77, 0x6e,
|
|
|
0x20, 0x72, 0x69, 0x73, 0x6b, 0x2e, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20,
|
|
|
0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69,
|
|
|
0x65, 0x63, 0x2e, 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20, 0x68, 0x74, 0x74,
|
|
|
0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x65, 0x63, 0x2e,
|
|
|
0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e,
|
|
|
0x49, 0x45, 0x43, 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e,
|
|
|
0x31, 0x20, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47,
|
|
|
0x42, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61,
|
|
|
0x63, 0x65, 0x20, 0x2d, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x49, 0x45, 0x43,
|
|
|
0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x44,
|
|
|
0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47, 0x42, 0x20, 0x63,
|
|
|
0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20,
|
|
|
0x2d, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x20, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x43, 0x52, 0x54, 0x20, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x0d, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36,
|
|
|
0x2d, 0x32, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x0d, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36,
|
|
|
0x2d, 0x32, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x76, 0x69, 0x65, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xa4, 0x7c,
|
|
|
0x00, 0x14, 0x5f, 0x30, 0x00, 0x10, 0xce, 0x02, 0x00, 0x03, 0xed, 0xb2,
|
|
|
0x00, 0x04, 0x13, 0x0a, 0x00, 0x03, 0x5c, 0x67, 0x00, 0x00, 0x00, 0x01,
|
|
|
0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x0a, 0x3d,
|
|
|
0x00, 0x50, 0x00, 0x00, 0x00, 0x57, 0x1e, 0xb8, 0x6d, 0x65, 0x61, 0x73,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x02, 0x8f, 0x00, 0x00, 0x00, 0x02, 0x58, 0x59, 0x5a, 0x20,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0x51, 0x00, 0x01, 0x00, 0x00,
|
|
|
0x00, 0x01, 0x16, 0xcc, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xa0,
|
|
|
0x00, 0x00, 0x38, 0xf5, 0x00, 0x00, 0x03, 0x90, 0x58, 0x59, 0x5a, 0x20,
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x97, 0x00, 0x00, 0xb7, 0x87,
|
|
|
0x00, 0x00, 0x18, 0xd9, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
|
|
|
0x00, 0x00, 0x24, 0x9f, 0x00, 0x00, 0x0f, 0x84, 0x00, 0x00, 0xb6, 0xc4,
|
|
|
0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
|
|
|
0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x14, 0x00, 0x19,
|
|
|
0x00, 0x1e, 0x00, 0x23, 0x00, 0x28, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x37,
|
|
|
0x00, 0x3b, 0x00, 0x40, 0x00, 0x45, 0x00, 0x4a, 0x00, 0x4f, 0x00, 0x54,
|
|
|
0x00, 0x59, 0x00, 0x5e, 0x00, 0x63, 0x00, 0x68, 0x00, 0x6d, 0x00, 0x72,
|
|
|
0x00, 0x77, 0x00, 0x7c, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8b, 0x00, 0x90,
|
|
|
0x00, 0x95, 0x00, 0x9a, 0x00, 0x9f, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xae,
|
|
|
0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc1, 0x00, 0xc6, 0x00, 0xcb,
|
|
|
0x00, 0xd0, 0x00, 0xd5, 0x00, 0xdb, 0x00, 0xe0, 0x00, 0xe5, 0x00, 0xeb,
|
|
|
0x00, 0xf0, 0x00, 0xf6, 0x00, 0xfb, 0x01, 0x01, 0x01, 0x07, 0x01, 0x0d,
|
|
|
0x01, 0x13, 0x01, 0x19, 0x01, 0x1f, 0x01, 0x25, 0x01, 0x2b, 0x01, 0x32,
|
|
|
0x01, 0x38, 0x01, 0x3e, 0x01, 0x45, 0x01, 0x4c, 0x01, 0x52, 0x01, 0x59,
|
|
|
0x01, 0x60, 0x01, 0x67, 0x01, 0x6e, 0x01, 0x75, 0x01, 0x7c, 0x01, 0x83,
|
|
|
0x01, 0x8b, 0x01, 0x92, 0x01, 0x9a, 0x01, 0xa1, 0x01, 0xa9, 0x01, 0xb1,
|
|
|
0x01, 0xb9, 0x01, 0xc1, 0x01, 0xc9, 0x01, 0xd1, 0x01, 0xd9, 0x01, 0xe1,
|
|
|
0x01, 0xe9, 0x01, 0xf2, 0x01, 0xfa, 0x02, 0x03, 0x02, 0x0c, 0x02, 0x14,
|
|
|
0x02, 0x1d, 0x02, 0x26, 0x02, 0x2f, 0x02, 0x38, 0x02, 0x41, 0x02, 0x4b,
|
|
|
0x02, 0x54, 0x02, 0x5d, 0x02, 0x67, 0x02, 0x71, 0x02, 0x7a, 0x02, 0x84,
|
|
|
0x02, 0x8e, 0x02, 0x98, 0x02, 0xa2, 0x02, 0xac, 0x02, 0xb6, 0x02, 0xc1,
|
|
|
0x02, 0xcb, 0x02, 0xd5, 0x02, 0xe0, 0x02, 0xeb, 0x02, 0xf5, 0x03, 0x00,
|
|
|
0x03, 0x0b, 0x03, 0x16, 0x03, 0x21, 0x03, 0x2d, 0x03, 0x38, 0x03, 0x43,
|
|
|
0x03, 0x4f, 0x03, 0x5a, 0x03, 0x66, 0x03, 0x72, 0x03, 0x7e, 0x03, 0x8a,
|
|
|
0x03, 0x96, 0x03, 0xa2, 0x03, 0xae, 0x03, 0xba, 0x03, 0xc7, 0x03, 0xd3,
|
|
|
0x03, 0xe0, 0x03, 0xec, 0x03, 0xf9, 0x04, 0x06, 0x04, 0x13, 0x04, 0x20,
|
|
|
0x04, 0x2d, 0x04, 0x3b, 0x04, 0x48, 0x04, 0x55, 0x04, 0x63, 0x04, 0x71,
|
|
|
0x04, 0x7e, 0x04, 0x8c, 0x04, 0x9a, 0x04, 0xa8, 0x04, 0xb6, 0x04, 0xc4,
|
|
|
0x04, 0xd3, 0x04, 0xe1, 0x04, 0xf0, 0x04, 0xfe, 0x05, 0x0d, 0x05, 0x1c,
|
|
|
0x05, 0x2b, 0x05, 0x3a, 0x05, 0x49, 0x05, 0x58, 0x05, 0x67, 0x05, 0x77,
|
|
|
0x05, 0x86, 0x05, 0x96, 0x05, 0xa6, 0x05, 0xb5, 0x05, 0xc5, 0x05, 0xd5,
|
|
|
0x05, 0xe5, 0x05, 0xf6, 0x06, 0x06, 0x06, 0x16, 0x06, 0x27, 0x06, 0x37,
|
|
|
0x06, 0x48, 0x06, 0x59, 0x06, 0x6a, 0x06, 0x7b, 0x06, 0x8c, 0x06, 0x9d,
|
|
|
0x06, 0xaf, 0x06, 0xc0, 0x06, 0xd1, 0x06, 0xe3, 0x06, 0xf5, 0x07, 0x07,
|
|
|
0x07, 0x19, 0x07, 0x2b, 0x07, 0x3d, 0x07, 0x4f, 0x07, 0x61, 0x07, 0x74,
|
|
|
0x07, 0x86, 0x07, 0x99, 0x07, 0xac, 0x07, 0xbf, 0x07, 0xd2, 0x07, 0xe5,
|
|
|
0x07, 0xf8, 0x08, 0x0b, 0x08, 0x1f, 0x08, 0x32, 0x08, 0x46, 0x08, 0x5a,
|
|
|
0x08, 0x6e, 0x08, 0x82, 0x08, 0x96, 0x08, 0xaa, 0x08, 0xbe, 0x08, 0xd2,
|
|
|
0x08, 0xe7, 0x08, 0xfb, 0x09, 0x10, 0x09, 0x25, 0x09, 0x3a, 0x09, 0x4f,
|
|
|
0x09, 0x64, 0x09, 0x79, 0x09, 0x8f, 0x09, 0xa4, 0x09, 0xba, 0x09, 0xcf,
|
|
|
0x09, 0xe5, 0x09, 0xfb, 0x0a, 0x11, 0x0a, 0x27, 0x0a, 0x3d, 0x0a, 0x54,
|
|
|
0x0a, 0x6a, 0x0a, 0x81, 0x0a, 0x98, 0x0a, 0xae, 0x0a, 0xc5, 0x0a, 0xdc,
|
|
|
0x0a, 0xf3, 0x0b, 0x0b, 0x0b, 0x22, 0x0b, 0x39, 0x0b, 0x51, 0x0b, 0x69,
|
|
|
0x0b, 0x80, 0x0b, 0x98, 0x0b, 0xb0, 0x0b, 0xc8, 0x0b, 0xe1, 0x0b, 0xf9,
|
|
|
0x0c, 0x12, 0x0c, 0x2a, 0x0c, 0x43, 0x0c, 0x5c, 0x0c, 0x75, 0x0c, 0x8e,
|
|
|
0x0c, 0xa7, 0x0c, 0xc0, 0x0c, 0xd9, 0x0c, 0xf3, 0x0d, 0x0d, 0x0d, 0x26,
|
|
|
0x0d, 0x40, 0x0d, 0x5a, 0x0d, 0x74, 0x0d, 0x8e, 0x0d, 0xa9, 0x0d, 0xc3,
|
|
|
0x0d, 0xde, 0x0d, 0xf8, 0x0e, 0x13, 0x0e, 0x2e, 0x0e, 0x49, 0x0e, 0x64,
|
|
|
0x0e, 0x7f, 0x0e, 0x9b, 0x0e, 0xb6, 0x0e, 0xd2, 0x0e, 0xee, 0x0f, 0x09,
|
|
|
0x0f, 0x25, 0x0f, 0x41, 0x0f, 0x5e, 0x0f, 0x7a, 0x0f, 0x96, 0x0f, 0xb3,
|
|
|
0x0f, 0xcf, 0x0f, 0xec, 0x10, 0x09, 0x10, 0x26, 0x10, 0x43, 0x10, 0x61,
|
|
|
0x10, 0x7e, 0x10, 0x9b, 0x10, 0xb9, 0x10, 0xd7, 0x10, 0xf5, 0x11, 0x13,
|
|
|
0x11, 0x31, 0x11, 0x4f, 0x11, 0x6d, 0x11, 0x8c, 0x11, 0xaa, 0x11, 0xc9,
|
|
|
0x11, 0xe8, 0x12, 0x07, 0x12, 0x26, 0x12, 0x45, 0x12, 0x64, 0x12, 0x84,
|
|
|
0x12, 0xa3, 0x12, 0xc3, 0x12, 0xe3, 0x13, 0x03, 0x13, 0x23, 0x13, 0x43,
|
|
|
0x13, 0x63, 0x13, 0x83, 0x13, 0xa4, 0x13, 0xc5, 0x13, 0xe5, 0x14, 0x06,
|
|
|
0x14, 0x27, 0x14, 0x49, 0x14, 0x6a, 0x14, 0x8b, 0x14, 0xad, 0x14, 0xce,
|
|
|
0x14, 0xf0, 0x15, 0x12, 0x15, 0x34, 0x15, 0x56, 0x15, 0x78, 0x15, 0x9b,
|
|
|
0x15, 0xbd, 0x15, 0xe0, 0x16, 0x03, 0x16, 0x26, 0x16, 0x49, 0x16, 0x6c,
|
|
|
0x16, 0x8f, 0x16, 0xb2, 0x16, 0xd6, 0x16, 0xfa, 0x17, 0x1d, 0x17, 0x41,
|
|
|
0x17, 0x65, 0x17, 0x89, 0x17, 0xae, 0x17, 0xd2, 0x17, 0xf7, 0x18, 0x1b,
|
|
|
0x18, 0x40, 0x18, 0x65, 0x18, 0x8a, 0x18, 0xaf, 0x18, 0xd5, 0x18, 0xfa,
|
|
|
0x19, 0x20, 0x19, 0x45, 0x19, 0x6b, 0x19, 0x91, 0x19, 0xb7, 0x19, 0xdd,
|
|
|
0x1a, 0x04, 0x1a, 0x2a, 0x1a, 0x51, 0x1a, 0x77, 0x1a, 0x9e, 0x1a, 0xc5,
|
|
|
0x1a, 0xec, 0x1b, 0x14, 0x1b, 0x3b, 0x1b, 0x63, 0x1b, 0x8a, 0x1b, 0xb2,
|
|
|
0x1b, 0xda, 0x1c, 0x02, 0x1c, 0x2a, 0x1c, 0x52, 0x1c, 0x7b, 0x1c, 0xa3,
|
|
|
0x1c, 0xcc, 0x1c, 0xf5, 0x1d, 0x1e, 0x1d, 0x47, 0x1d, 0x70, 0x1d, 0x99,
|
|
|
0x1d, 0xc3, 0x1d, 0xec, 0x1e, 0x16, 0x1e, 0x40, 0x1e, 0x6a, 0x1e, 0x94,
|
|
|
0x1e, 0xbe, 0x1e, 0xe9, 0x1f, 0x13, 0x1f, 0x3e, 0x1f, 0x69, 0x1f, 0x94,
|
|
|
0x1f, 0xbf, 0x1f, 0xea, 0x20, 0x15, 0x20, 0x41, 0x20, 0x6c, 0x20, 0x98,
|
|
|
0x20, 0xc4, 0x20, 0xf0, 0x21, 0x1c, 0x21, 0x48, 0x21, 0x75, 0x21, 0xa1,
|
|
|
0x21, 0xce, 0x21, 0xfb, 0x22, 0x27, 0x22, 0x55, 0x22, 0x82, 0x22, 0xaf,
|
|
|
0x22, 0xdd, 0x23, 0x0a, 0x23, 0x38, 0x23, 0x66, 0x23, 0x94, 0x23, 0xc2,
|
|
|
0x23, 0xf0, 0x24, 0x1f, 0x24, 0x4d, 0x24, 0x7c, 0x24, 0xab, 0x24, 0xda,
|
|
|
0x25, 0x09, 0x25, 0x38, 0x25, 0x68, 0x25, 0x97, 0x25, 0xc7, 0x25, 0xf7,
|
|
|
0x26, 0x27, 0x26, 0x57, 0x26, 0x87, 0x26, 0xb7, 0x26, 0xe8, 0x27, 0x18,
|
|
|
0x27, 0x49, 0x27, 0x7a, 0x27, 0xab, 0x27, 0xdc, 0x28, 0x0d, 0x28, 0x3f,
|
|
|
0x28, 0x71, 0x28, 0xa2, 0x28, 0xd4, 0x29, 0x06, 0x29, 0x38, 0x29, 0x6b,
|
|
|
0x29, 0x9d, 0x29, 0xd0, 0x2a, 0x02, 0x2a, 0x35, 0x2a, 0x68, 0x2a, 0x9b,
|
|
|
0x2a, 0xcf, 0x2b, 0x02, 0x2b, 0x36, 0x2b, 0x69, 0x2b, 0x9d, 0x2b, 0xd1,
|
|
|
0x2c, 0x05, 0x2c, 0x39, 0x2c, 0x6e, 0x2c, 0xa2, 0x2c, 0xd7, 0x2d, 0x0c,
|
|
|
0x2d, 0x41, 0x2d, 0x76, 0x2d, 0xab, 0x2d, 0xe1, 0x2e, 0x16, 0x2e, 0x4c,
|
|
|
0x2e, 0x82, 0x2e, 0xb7, 0x2e, 0xee, 0x2f, 0x24, 0x2f, 0x5a, 0x2f, 0x91,
|
|
|
0x2f, 0xc7, 0x2f, 0xfe, 0x30, 0x35, 0x30, 0x6c, 0x30, 0xa4, 0x30, 0xdb,
|
|
|
0x31, 0x12, 0x31, 0x4a, 0x31, 0x82, 0x31, 0xba, 0x31, 0xf2, 0x32, 0x2a,
|
|
|
0x32, 0x63, 0x32, 0x9b, 0x32, 0xd4, 0x33, 0x0d, 0x33, 0x46, 0x33, 0x7f,
|
|
|
0x33, 0xb8, 0x33, 0xf1, 0x34, 0x2b, 0x34, 0x65, 0x34, 0x9e, 0x34, 0xd8,
|
|
|
0x35, 0x13, 0x35, 0x4d, 0x35, 0x87, 0x35, 0xc2, 0x35, 0xfd, 0x36, 0x37,
|
|
|
0x36, 0x72, 0x36, 0xae, 0x36, 0xe9, 0x37, 0x24, 0x37, 0x60, 0x37, 0x9c,
|
|
|
0x37, 0xd7, 0x38, 0x14, 0x38, 0x50, 0x38, 0x8c, 0x38, 0xc8, 0x39, 0x05,
|
|
|
0x39, 0x42, 0x39, 0x7f, 0x39, 0xbc, 0x39, 0xf9, 0x3a, 0x36, 0x3a, 0x74,
|
|
|
0x3a, 0xb2, 0x3a, 0xef, 0x3b, 0x2d, 0x3b, 0x6b, 0x3b, 0xaa, 0x3b, 0xe8,
|
|
|
0x3c, 0x27, 0x3c, 0x65, 0x3c, 0xa4, 0x3c, 0xe3, 0x3d, 0x22, 0x3d, 0x61,
|
|
|
0x3d, 0xa1, 0x3d, 0xe0, 0x3e, 0x20, 0x3e, 0x60, 0x3e, 0xa0, 0x3e, 0xe0,
|
|
|
0x3f, 0x21, 0x3f, 0x61, 0x3f, 0xa2, 0x3f, 0xe2, 0x40, 0x23, 0x40, 0x64,
|
|
|
0x40, 0xa6, 0x40, 0xe7, 0x41, 0x29, 0x41, 0x6a, 0x41, 0xac, 0x41, 0xee,
|
|
|
0x42, 0x30, 0x42, 0x72, 0x42, 0xb5, 0x42, 0xf7, 0x43, 0x3a, 0x43, 0x7d,
|
|
|
0x43, 0xc0, 0x44, 0x03, 0x44, 0x47, 0x44, 0x8a, 0x44, 0xce, 0x45, 0x12,
|
|
|
0x45, 0x55, 0x45, 0x9a, 0x45, 0xde, 0x46, 0x22, 0x46, 0x67, 0x46, 0xab,
|
|
|
0x46, 0xf0, 0x47, 0x35, 0x47, 0x7b, 0x47, 0xc0, 0x48, 0x05, 0x48, 0x4b,
|
|
|
0x48, 0x91, 0x48, 0xd7, 0x49, 0x1d, 0x49, 0x63, 0x49, 0xa9, 0x49, 0xf0,
|
|
|
0x4a, 0x37, 0x4a, 0x7d, 0x4a, 0xc4, 0x4b, 0x0c, 0x4b, 0x53, 0x4b, 0x9a,
|
|
|
0x4b, 0xe2, 0x4c, 0x2a, 0x4c, 0x72, 0x4c, 0xba, 0x4d, 0x02, 0x4d, 0x4a,
|
|
|
0x4d, 0x93, 0x4d, 0xdc, 0x4e, 0x25, 0x4e, 0x6e, 0x4e, 0xb7, 0x4f, 0x00,
|
|
|
0x4f, 0x49, 0x4f, 0x93, 0x4f, 0xdd, 0x50, 0x27, 0x50, 0x71, 0x50, 0xbb,
|
|
|
0x51, 0x06, 0x51, 0x50, 0x51, 0x9b, 0x51, 0xe6, 0x52, 0x31, 0x52, 0x7c,
|
|
|
0x52, 0xc7, 0x53, 0x13, 0x53, 0x5f, 0x53, 0xaa, 0x53, 0xf6, 0x54, 0x42,
|
|
|
0x54, 0x8f, 0x54, 0xdb, 0x55, 0x28, 0x55, 0x75, 0x55, 0xc2, 0x56, 0x0f,
|
|
|
0x56, 0x5c, 0x56, 0xa9, 0x56, 0xf7, 0x57, 0x44, 0x57, 0x92, 0x57, 0xe0,
|
|
|
0x58, 0x2f, 0x58, 0x7d, 0x58, 0xcb, 0x59, 0x1a, 0x59, 0x69, 0x59, 0xb8,
|
|
|
0x5a, 0x07, 0x5a, 0x56, 0x5a, 0xa6, 0x5a, 0xf5, 0x5b, 0x45, 0x5b, 0x95,
|
|
|
0x5b, 0xe5, 0x5c, 0x35, 0x5c, 0x86, 0x5c, 0xd6, 0x5d, 0x27, 0x5d, 0x78,
|
|
|
0x5d, 0xc9, 0x5e, 0x1a, 0x5e, 0x6c, 0x5e, 0xbd, 0x5f, 0x0f, 0x5f, 0x61,
|
|
|
0x5f, 0xb3, 0x60, 0x05, 0x60, 0x57, 0x60, 0xaa, 0x60, 0xfc, 0x61, 0x4f,
|
|
|
0x61, 0xa2, 0x61, 0xf5, 0x62, 0x49, 0x62, 0x9c, 0x62, 0xf0, 0x63, 0x43,
|
|
|
0x63, 0x97, 0x63, 0xeb, 0x64, 0x40, 0x64, 0x94, 0x64, 0xe9, 0x65, 0x3d,
|
|
|
0x65, 0x92, 0x65, 0xe7, 0x66, 0x3d, 0x66, 0x92, 0x66, 0xe8, 0x67, 0x3d,
|
|
|
0x67, 0x93, 0x67, 0xe9, 0x68, 0x3f, 0x68, 0x96, 0x68, 0xec, 0x69, 0x43,
|
|
|
0x69, 0x9a, 0x69, 0xf1, 0x6a, 0x48, 0x6a, 0x9f, 0x6a, 0xf7, 0x6b, 0x4f,
|
|
|
0x6b, 0xa7, 0x6b, 0xff, 0x6c, 0x57, 0x6c, 0xaf, 0x6d, 0x08, 0x6d, 0x60,
|
|
|
0x6d, 0xb9, 0x6e, 0x12, 0x6e, 0x6b, 0x6e, 0xc4, 0x6f, 0x1e, 0x6f, 0x78,
|
|
|
0x6f, 0xd1, 0x70, 0x2b, 0x70, 0x86, 0x70, 0xe0, 0x71, 0x3a, 0x71, 0x95,
|
|
|
0x71, 0xf0, 0x72, 0x4b, 0x72, 0xa6, 0x73, 0x01, 0x73, 0x5d, 0x73, 0xb8,
|
|
|
0x74, 0x14, 0x74, 0x70, 0x74, 0xcc, 0x75, 0x28, 0x75, 0x85, 0x75, 0xe1,
|
|
|
0x76, 0x3e, 0x76, 0x9b, 0x76, 0xf8, 0x77, 0x56, 0x77, 0xb3, 0x78, 0x11,
|
|
|
0x78, 0x6e, 0x78, 0xcc, 0x79, 0x2a, 0x79, 0x89, 0x79, 0xe7, 0x7a, 0x46,
|
|
|
0x7a, 0xa5, 0x7b, 0x04, 0x7b, 0x63, 0x7b, 0xc2, 0x7c, 0x21, 0x7c, 0x81,
|
|
|
0x7c, 0xe1, 0x7d, 0x41, 0x7d, 0xa1, 0x7e, 0x01, 0x7e, 0x62, 0x7e, 0xc2,
|
|
|
0x7f, 0x23, 0x7f, 0x84, 0x7f, 0xe5, 0x80, 0x47, 0x80, 0xa8, 0x81, 0x0a,
|
|
|
0x81, 0x6b, 0x81, 0xcd, 0x82, 0x30, 0x82, 0x92, 0x82, 0xf4, 0x83, 0x57,
|
|
|
0x83, 0xba, 0x84, 0x1d, 0x84, 0x80, 0x84, 0xe3, 0x85, 0x47, 0x85, 0xab,
|
|
|
0x86, 0x0e, 0x86, 0x72, 0x86, 0xd7, 0x87, 0x3b, 0x87, 0x9f, 0x88, 0x04,
|
|
|
0x88, 0x69, 0x88, 0xce, 0x89, 0x33, 0x89, 0x99, 0x89, 0xfe, 0x8a, 0x64,
|
|
|
0x8a, 0xca, 0x8b, 0x30, 0x8b, 0x96, 0x8b, 0xfc, 0x8c, 0x63, 0x8c, 0xca,
|
|
|
0x8d, 0x31, 0x8d, 0x98, 0x8d, 0xff, 0x8e, 0x66, 0x8e, 0xce, 0x8f, 0x36,
|
|
|
0x8f, 0x9e, 0x90, 0x06, 0x90, 0x6e, 0x90, 0xd6, 0x91, 0x3f, 0x91, 0xa8,
|
|
|
0x92, 0x11, 0x92, 0x7a, 0x92, 0xe3, 0x93, 0x4d, 0x93, 0xb6, 0x94, 0x20,
|
|
|
0x94, 0x8a, 0x94, 0xf4, 0x95, 0x5f, 0x95, 0xc9, 0x96, 0x34, 0x96, 0x9f,
|
|
|
0x97, 0x0a, 0x97, 0x75, 0x97, 0xe0, 0x98, 0x4c, 0x98, 0xb8, 0x99, 0x24,
|
|
|
0x99, 0x90, 0x99, 0xfc, 0x9a, 0x68, 0x9a, 0xd5, 0x9b, 0x42, 0x9b, 0xaf,
|
|
|
0x9c, 0x1c, 0x9c, 0x89, 0x9c, 0xf7, 0x9d, 0x64, 0x9d, 0xd2, 0x9e, 0x40,
|
|
|
0x9e, 0xae, 0x9f, 0x1d, 0x9f, 0x8b, 0x9f, 0xfa, 0xa0, 0x69, 0xa0, 0xd8,
|
|
|
0xa1, 0x47, 0xa1, 0xb6, 0xa2, 0x26, 0xa2, 0x96, 0xa3, 0x06, 0xa3, 0x76,
|
|
|
0xa3, 0xe6, 0xa4, 0x56, 0xa4, 0xc7, 0xa5, 0x38, 0xa5, 0xa9, 0xa6, 0x1a,
|
|
|
0xa6, 0x8b, 0xa6, 0xfd, 0xa7, 0x6e, 0xa7, 0xe0, 0xa8, 0x52, 0xa8, 0xc4,
|
|
|
0xa9, 0x37, 0xa9, 0xa9, 0xaa, 0x1c, 0xaa, 0x8f, 0xab, 0x02, 0xab, 0x75,
|
|
|
0xab, 0xe9, 0xac, 0x5c, 0xac, 0xd0, 0xad, 0x44, 0xad, 0xb8, 0xae, 0x2d,
|
|
|
0xae, 0xa1, 0xaf, 0x16, 0xaf, 0x8b, 0xb0, 0x00, 0xb0, 0x75, 0xb0, 0xea,
|
|
|
0xb1, 0x60, 0xb1, 0xd6, 0xb2, 0x4b, 0xb2, 0xc2, 0xb3, 0x38, 0xb3, 0xae,
|
|
|
0xb4, 0x25, 0xb4, 0x9c, 0xb5, 0x13, 0xb5, 0x8a, 0xb6, 0x01, 0xb6, 0x79,
|
|
|
0xb6, 0xf0, 0xb7, 0x68, 0xb7, 0xe0, 0xb8, 0x59, 0xb8, 0xd1, 0xb9, 0x4a,
|
|
|
0xb9, 0xc2, 0xba, 0x3b, 0xba, 0xb5, 0xbb, 0x2e, 0xbb, 0xa7, 0xbc, 0x21,
|
|
|
0xbc, 0x9b, 0xbd, 0x15, 0xbd, 0x8f, 0xbe, 0x0a, 0xbe, 0x84, 0xbe, 0xff,
|
|
|
0xbf, 0x7a, 0xbf, 0xf5, 0xc0, 0x70, 0xc0, 0xec, 0xc1, 0x67, 0xc1, 0xe3,
|
|
|
0xc2, 0x5f, 0xc2, 0xdb, 0xc3, 0x58, 0xc3, 0xd4, 0xc4, 0x51, 0xc4, 0xce,
|
|
|
0xc5, 0x4b, 0xc5, 0xc8, 0xc6, 0x46, 0xc6, 0xc3, 0xc7, 0x41, 0xc7, 0xbf,
|
|
|
0xc8, 0x3d, 0xc8, 0xbc, 0xc9, 0x3a, 0xc9, 0xb9, 0xca, 0x38, 0xca, 0xb7,
|
|
|
0xcb, 0x36, 0xcb, 0xb6, 0xcc, 0x35, 0xcc, 0xb5, 0xcd, 0x35, 0xcd, 0xb5,
|
|
|
0xce, 0x36, 0xce, 0xb6, 0xcf, 0x37, 0xcf, 0xb8, 0xd0, 0x39, 0xd0, 0xba,
|
|
|
0xd1, 0x3c, 0xd1, 0xbe, 0xd2, 0x3f, 0xd2, 0xc1, 0xd3, 0x44, 0xd3, 0xc6,
|
|
|
0xd4, 0x49, 0xd4, 0xcb, 0xd5, 0x4e, 0xd5, 0xd1, 0xd6, 0x55, 0xd6, 0xd8,
|
|
|
0xd7, 0x5c, 0xd7, 0xe0, 0xd8, 0x64, 0xd8, 0xe8, 0xd9, 0x6c, 0xd9, 0xf1,
|
|
|
0xda, 0x76, 0xda, 0xfb, 0xdb, 0x80, 0xdc, 0x05, 0xdc, 0x8a, 0xdd, 0x10,
|
|
|
0xdd, 0x96, 0xde, 0x1c, 0xde, 0xa2, 0xdf, 0x29, 0xdf, 0xaf, 0xe0, 0x36,
|
|
|
0xe0, 0xbd, 0xe1, 0x44, 0xe1, 0xcc, 0xe2, 0x53, 0xe2, 0xdb, 0xe3, 0x63,
|
|
|
0xe3, 0xeb, 0xe4, 0x73, 0xe4, 0xfc, 0xe5, 0x84, 0xe6, 0x0d, 0xe6, 0x96,
|
|
|
0xe7, 0x1f, 0xe7, 0xa9, 0xe8, 0x32, 0xe8, 0xbc, 0xe9, 0x46, 0xe9, 0xd0,
|
|
|
0xea, 0x5b, 0xea, 0xe5, 0xeb, 0x70, 0xeb, 0xfb, 0xec, 0x86, 0xed, 0x11,
|
|
|
0xed, 0x9c, 0xee, 0x28, 0xee, 0xb4, 0xef, 0x40, 0xef, 0xcc, 0xf0, 0x58,
|
|
|
0xf0, 0xe5, 0xf1, 0x72, 0xf1, 0xff, 0xf2, 0x8c, 0xf3, 0x19, 0xf3, 0xa7,
|
|
|
0xf4, 0x34, 0xf4, 0xc2, 0xf5, 0x50, 0xf5, 0xde, 0xf6, 0x6d, 0xf6, 0xfb,
|
|
|
0xf7, 0x8a, 0xf8, 0x19, 0xf8, 0xa8, 0xf9, 0x38, 0xf9, 0xc7, 0xfa, 0x57,
|
|
|
0xfa, 0xe7, 0xfb, 0x77, 0xfc, 0x07, 0xfc, 0x98, 0xfd, 0x29, 0xfd, 0xba,
|
|
|
0xfe, 0x4b, 0xfe, 0xdc, 0xff, 0x6d, 0xff, 0xff
|
|
|
};
|
|
|
|
|
|
StringInfo
|
|
|
*profile;
|
|
|
|
|
|
MagickBooleanType
|
|
|
status;
|
|
|
|
|
|
assert(image != (Image *) NULL);
|
|
|
assert(image->signature == MagickCoreSignature);
|
|
|
if (GetImageProfile(image,"icc") != (const StringInfo *) NULL)
|
|
|
return(MagickFalse);
|
|
|
profile=AcquireStringInfo(sizeof(sRGBProfile));
|
|
|
SetStringInfoDatum(profile,sRGBProfile);
|
|
|
status=SetImageProfile(image,"icc",profile,exception);
|
|
|
profile=DestroyStringInfo(profile);
|
|
|
return(status);
|
|
|
}
|
|
|
|
|
|
MagickExport MagickBooleanType ProfileImage(Image *image,const char *name,
|
|
|
const void *datum,const size_t length,ExceptionInfo *exception)
|
|
|
{
|
|
|
#define ProfileImageTag "Profile/Image"
|
|
|
#ifndef TYPE_XYZ_8
|
|
|
#define TYPE_XYZ_8 (COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(1))
|
|
|
#endif
|
|
|
#define ThrowProfileException(severity,tag,context) \
|
|
|
{ \
|
|
|
if (profile != (StringInfo *) NULL) \
|
|
|
profile=DestroyStringInfo(profile); \
|
|
|
if (cms_context != (cmsContext) NULL) \
|
|
|
cmsDeleteContext(cms_context); \
|
|
|
if (source_info.profile != (cmsHPROFILE) NULL) \
|
|
|
(void) cmsCloseProfile(source_info.profile); \
|
|
|
if (target_info.profile != (cmsHPROFILE) NULL) \
|
|
|
(void) cmsCloseProfile(target_info.profile); \
|
|
|
ThrowBinaryException(severity,tag,context); \
|
|
|
}
|
|
|
|
|
|
MagickBooleanType
|
|
|
status;
|
|
|
|
|
|
StringInfo
|
|
|
*profile;
|
|
|
|
|
|
assert(image != (Image *) NULL);
|
|
|
assert(image->signature == MagickCoreSignature);
|
|
|
if (image->debug != MagickFalse)
|
|
|
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
|
|
|
assert(name != (const char *) NULL);
|
|
|
if ((datum == (const void *) NULL) || (length == 0))
|
|
|
{
|
|
|
char
|
|
|
*next;
|
|
|
|
|
|
/*
|
|
|
Delete image profile(s).
|
|
|
*/
|
|
|
ResetImageProfileIterator(image);
|
|
|
for (next=GetNextImageProfile(image); next != (const char *) NULL; )
|
|
|
{
|
|
|
if (IsOptionMember(next,name) != MagickFalse)
|
|
|
{
|
|
|
(void) DeleteImageProfile(image,next);
|
|
|
ResetImageProfileIterator(image);
|
|
|
}
|
|
|
next=GetNextImageProfile(image);
|
|
|
}
|
|
|
return(MagickTrue);
|
|
|
}
|
|
|
/*
|
|
|
Add a ICC, IPTC, or generic profile to the image.
|
|
|
*/
|
|
|
status=MagickTrue;
|
|
|
profile=AcquireStringInfo((size_t) length);
|
|
|
SetStringInfoDatum(profile,(unsigned char *) datum);
|
|
|
if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
|
|
|
status=SetImageProfile(image,name,profile,exception);
|
|
|
else
|
|
|
{
|
|
|
const StringInfo
|
|
|
*icc_profile;
|
|
|
|
|
|
icc_profile=GetImageProfile(image,"icc");
|
|
|
if ((icc_profile != (const StringInfo *) NULL) &&
|
|
|
(CompareStringInfo(icc_profile,profile) == 0))
|
|
|
{
|
|
|
const char
|
|
|
*value;
|
|
|
|
|
|
value=GetImageProperty(image,"exif:ColorSpace",exception);
|
|
|
(void) value;
|
|
|
if (LocaleCompare(value,"1") != 0)
|
|
|
(void) SetsRGBImageProfile(image,exception);
|
|
|
value=GetImageProperty(image,"exif:InteroperabilityIndex",exception);
|
|
|
if (LocaleCompare(value,"R98.") != 0)
|
|
|
(void) SetsRGBImageProfile(image,exception);
|
|
|
icc_profile=GetImageProfile(image,"icc");
|
|
|
}
|
|
|
if ((icc_profile != (const StringInfo *) NULL) &&
|
|
|
(CompareStringInfo(icc_profile,profile) == 0))
|
|
|
{
|
|
|
profile=DestroyStringInfo(profile);
|
|
|
return(MagickTrue);
|
|
|
}
|
|
|
#if !defined(MAGICKCORE_LCMS_DELEGATE)
|
|
|
(void) ThrowMagickException(exception,GetMagickModule(),
|
|
|
MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
|
|
|
"'%s' (LCMS)",image->filename);
|
|
|
#else
|
|
|
{
|
|
|
cmsContext
|
|
|
cms_context;
|
|
|
|
|
|
CMSExceptionInfo
|
|
|
cms_exception;
|
|
|
|
|
|
LCMSInfo
|
|
|
source_info,
|
|
|
target_info;
|
|
|
|
|
|
/*
|
|
|
Transform pixel colors as defined by the color profiles.
|
|
|
*/
|
|
|
cms_exception.image=image;
|
|
|
cms_exception.exception=exception;
|
|
|
cms_context=cmsCreateContext(NULL,&cms_exception);
|
|
|
if (cms_context == (cmsContext) NULL)
|
|
|
ThrowBinaryException(ResourceLimitError,
|
|
|
"ColorspaceColorProfileMismatch",name);
|
|
|
cmsSetLogErrorHandlerTHR(cms_context,CMSExceptionHandler);
|
|
|
source_info.profile=cmsOpenProfileFromMemTHR(cms_context,
|
|
|
GetStringInfoDatum(profile),(cmsUInt32Number)
|
|
|
GetStringInfoLength(profile));
|
|
|
if (source_info.profile == (cmsHPROFILE) NULL)
|
|
|
{
|
|
|
cmsDeleteContext(cms_context);
|
|
|
ThrowBinaryException(ResourceLimitError,
|
|
|
"ColorspaceColorProfileMismatch",name);
|
|
|
}
|
|
|
if ((cmsGetDeviceClass(source_info.profile) != cmsSigLinkClass) &&
|
|
|
(icc_profile == (StringInfo *) NULL))
|
|
|
status=SetImageProfile(image,name,profile,exception);
|
|
|
else
|
|
|
{
|
|
|
CacheView
|
|
|
*image_view;
|
|
|
|
|
|
cmsColorSpaceSignature
|
|
|
signature;
|
|
|
|
|
|
cmsHTRANSFORM
|
|
|
*magick_restrict transform;
|
|
|
|
|
|
cmsUInt32Number
|
|
|
flags;
|
|
|
|
|
|
#if !defined(MAGICKCORE_HDRI_SUPPORT)
|
|
|
const char
|
|
|
*artifact;
|
|
|
#endif
|
|
|
|
|
|
MagickBooleanType
|
|
|
highres;
|
|
|
|
|
|
MagickOffsetType
|
|
|
progress;
|
|
|
|
|
|
ssize_t
|
|
|
y;
|
|
|
|
|
|
target_info.profile=(cmsHPROFILE) NULL;
|
|
|
if (icc_profile != (StringInfo *) NULL)
|
|
|
{
|
|
|
target_info.profile=source_info.profile;
|
|
|
source_info.profile=cmsOpenProfileFromMemTHR(cms_context,
|
|
|
GetStringInfoDatum(icc_profile),
|
|
|
(cmsUInt32Number) GetStringInfoLength(icc_profile));
|
|
|
if (source_info.profile == (cmsHPROFILE) NULL)
|
|
|
ThrowProfileException(ResourceLimitError,
|
|
|
"ColorspaceColorProfileMismatch",name);
|
|
|
}
|
|
|
highres=MagickTrue;
|
|
|
#if !defined(MAGICKCORE_HDRI_SUPPORT)
|
|
|
artifact=GetImageArtifact(image,"profile:highres-transform");
|
|
|
if (IsStringFalse(artifact) != MagickFalse)
|
|
|
highres=MagickFalse;
|
|
|
#endif
|
|
|
source_info.scale=1.0;
|
|
|
source_info.translate=0.0;
|
|
|
source_info.colorspace=sRGBColorspace;
|
|
|
source_info.channels=3;
|
|
|
switch (cmsGetColorSpace(source_info.profile))
|
|
|
{
|
|
|
case cmsSigCmykData:
|
|
|
{
|
|
|
source_info.colorspace=CMYKColorspace;
|
|
|
source_info.channels=4;
|
|
|
#if (MAGICKCORE_QUANTUM_DEPTH == 8)
|
|
|
if (highres == MagickFalse)
|
|
|
source_info.type=(cmsUInt32Number) TYPE_CMYK_8;
|
|
|
else
|
|
|
#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
|
|
|
if (highres == MagickFalse)
|
|
|
source_info.type=(cmsUInt32Number) TYPE_CMYK_16;
|
|
|
else
|
|
|
#endif
|
|
|
{
|
|
|
source_info.type=(cmsUInt32Number) TYPE_CMYK_DBL;
|
|
|
source_info.scale=100.0;
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
case cmsSigGrayData:
|
|
|
{
|
|
|
source_info.colorspace=GRAYColorspace;
|
|
|
source_info.channels=1;
|
|
|
#if (MAGICKCORE_QUANTUM_DEPTH == 8)
|
|
|
if (highres == MagickFalse)
|
|
|
source_info.type=(cmsUInt32Number) TYPE_GRAY_8;
|
|
|
else
|
|
|
#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
|
|
|
if (highres == MagickFalse)
|
|
|
source_info.type=(cmsUInt32Number) TYPE_GRAY_16;
|
|
|
else
|
|
|
#endif
|
|
|
source_info.type=(cmsUInt32Number) TYPE_GRAY_DBL;
|
|
|
break;
|
|
|
}
|
|
|
case cmsSigLabData:
|
|
|
{
|
|
|
source_info.colorspace=LabColorspace;
|
|
|
#if (MAGICKCORE_QUANTUM_DEPTH == 8)
|
|
|
if (highres == MagickFalse)
|
|
|
source_info.type=(cmsUInt32Number) TYPE_Lab_8;
|
|
|
else
|
|
|
#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
|
|
|
if (highres == MagickFalse)
|
|
|
source_info.type=(cmsUInt32Number) TYPE_Lab_16;
|
|
|
else
|
|
|
#endif
|
|
|
{
|
|
|
source_info.type=(cmsUInt32Number) TYPE_Lab_DBL;
|
|
|
source_info.scale=100.0;
|
|
|
source_info.translate=(-0.5);
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
case cmsSigRgbData:
|
|
|
{
|
|
|
source_info.colorspace=sRGBColorspace;
|
|
|
#if (MAGICKCORE_QUANTUM_DEPTH == 8)
|
|
|
if (highres == MagickFalse)
|
|
|
source_info.type=(cmsUInt32Number) TYPE_RGB_8;
|
|
|
else
|
|
|
#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
|
|
|
if (highres == MagickFalse)
|
|
|
source_info.type=(cmsUInt32Number) TYPE_RGB_16;
|
|
|
else
|
|
|
#endif
|
|
|
source_info.type=(cmsUInt32Number) TYPE_RGB_DBL;
|
|
|
break;
|
|
|
}
|
|
|
case cmsSigXYZData:
|
|
|
{
|
|
|
source_info.colorspace=XYZColorspace;
|
|
|
#if (MAGICKCORE_QUANTUM_DEPTH == 8)
|
|
|
if (highres == MagickFalse)
|
|
|
source_info.type=(cmsUInt32Number) TYPE_XYZ_8;
|
|
|
else
|
|
|
#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
|
|
|
if (highres == MagickFalse)
|
|
|
source_info.type=(cmsUInt32Number) TYPE_XYZ_16;
|
|
|
else
|
|
|
#endif
|
|
|
source_info.type=(cmsUInt32Number) TYPE_XYZ_DBL;
|
|
|
break;
|
|
|
}
|
|
|
default:
|
|
|
ThrowProfileException(ImageError,
|
|
|
"ColorspaceColorProfileMismatch",name);
|
|
|
}
|
|
|
signature=cmsGetPCS(source_info.profile);
|
|
|
if (target_info.profile != (cmsHPROFILE) NULL)
|
|
|
signature=cmsGetColorSpace(target_info.profile);
|
|
|
target_info.scale=1.0;
|
|
|
target_info.translate=0.0;
|
|
|
target_info.channels=3;
|
|
|
switch (signature)
|
|
|
{
|
|
|
case cmsSigCmykData:
|
|
|
{
|
|
|
target_info.colorspace=CMYKColorspace;
|
|
|
target_info.channels=4;
|
|
|
#if (MAGICKCORE_QUANTUM_DEPTH == 8)
|
|
|
if (highres == MagickFalse)
|
|
|
target_info.type=(cmsUInt32Number) TYPE_CMYK_8;
|
|
|
else
|
|
|
#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
|
|
|
if (highres == MagickFalse)
|
|
|
target_info.type=(cmsUInt32Number) TYPE_CMYK_16;
|
|
|
else
|
|
|
#endif
|
|
|
{
|
|
|
target_info.type=(cmsUInt32Number) TYPE_CMYK_DBL;
|
|
|
target_info.scale=0.01;
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
case cmsSigGrayData:
|
|
|
{
|
|
|
target_info.colorspace=GRAYColorspace;
|
|
|
target_info.channels=1;
|
|
|
#if (MAGICKCORE_QUANTUM_DEPTH == 8)
|
|
|
if (highres == MagickFalse)
|
|
|
target_info.type=(cmsUInt32Number) TYPE_GRAY_8;
|
|
|
else
|
|
|
#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
|
|
|
if (highres == MagickFalse)
|
|
|
target_info.type=(cmsUInt32Number) TYPE_GRAY_16;
|
|
|
else
|
|
|
#endif
|
|
|
target_info.type=(cmsUInt32Number) TYPE_GRAY_DBL;
|
|
|
break;
|
|
|
}
|
|
|
case cmsSigLabData:
|
|
|
{
|
|
|
target_info.colorspace=LabColorspace;
|
|
|
#if (MAGICKCORE_QUANTUM_DEPTH == 8)
|
|
|
if (highres == MagickFalse)
|
|
|
target_info.type=(cmsUInt32Number) TYPE_Lab_8;
|
|
|
else
|
|
|
#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
|
|
|
if (highres == MagickFalse)
|
|
|
target_info.type=(cmsUInt32Number) TYPE_Lab_16;
|
|
|
else
|
|
|
#endif
|
|
|
{
|
|
|
target_info.type=(cmsUInt32Number) TYPE_Lab_DBL;
|
|
|
target_info.scale=0.01;
|
|
|
target_info.translate=0.5;
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
case cmsSigRgbData:
|
|
|
{
|
|
|
target_info.colorspace=sRGBColorspace;
|
|
|
#if (MAGICKCORE_QUANTUM_DEPTH == 8)
|
|
|
if (highres == MagickFalse)
|
|
|
target_info.type=(cmsUInt32Number) TYPE_RGB_8;
|
|
|
else
|
|
|
#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
|
|
|
if (highres == MagickFalse)
|
|
|
target_info.type=(cmsUInt32Number) TYPE_RGB_16;
|
|
|
else
|
|
|
#endif
|
|
|
target_info.type=(cmsUInt32Number) TYPE_RGB_DBL;
|
|
|
break;
|
|
|
}
|
|
|
case cmsSigXYZData:
|
|
|
{
|
|
|
target_info.colorspace=XYZColorspace;
|
|
|
#if (MAGICKCORE_QUANTUM_DEPTH == 8)
|
|
|
if (highres == MagickFalse)
|
|
|
target_info.type=(cmsUInt32Number) TYPE_XYZ_8;
|
|
|
else
|
|
|
#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
|
|
|
if (highres == MagickFalse)
|
|
|
source_info.type=(cmsUInt32Number) TYPE_XYZ_16;
|
|
|
else
|
|
|
#endif
|
|
|
target_info.type=(cmsUInt32Number) TYPE_XYZ_DBL;
|
|
|
break;
|
|
|
}
|
|
|
default:
|
|
|
ThrowProfileException(ImageError,
|
|
|
"ColorspaceColorProfileMismatch",name);
|
|
|
}
|
|
|
switch (image->rendering_intent)
|
|
|
{
|
|
|
case AbsoluteIntent:
|
|
|
{
|
|
|
target_info.intent=INTENT_ABSOLUTE_COLORIMETRIC;
|
|
|
break;
|
|
|
}
|
|
|
case PerceptualIntent:
|
|
|
{
|
|
|
target_info.intent=INTENT_PERCEPTUAL;
|
|
|
break;
|
|
|
}
|
|
|
case RelativeIntent:
|
|
|
{
|
|
|
target_info.intent=INTENT_RELATIVE_COLORIMETRIC;
|
|
|
break;
|
|
|
}
|
|
|
case SaturationIntent:
|
|
|
{
|
|
|
target_info.intent=INTENT_SATURATION;
|
|
|
break;
|
|
|
}
|
|
|
default:
|
|
|
{
|
|
|
target_info.intent=INTENT_PERCEPTUAL;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
flags=cmsFLAGS_HIGHRESPRECALC;
|
|
|
#if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
|
|
|
if (image->black_point_compensation != MagickFalse)
|
|
|
flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
|
|
|
#endif
|
|
|
transform=AcquireTransformThreadSet(&source_info,&target_info,
|
|
|
flags,cms_context);
|
|
|
if (transform == (cmsHTRANSFORM *) NULL)
|
|
|
ThrowProfileException(ImageError,"UnableToCreateColorTransform",
|
|
|
name);
|
|
|
/*
|
|
|
Transform image as dictated by the source & target image profiles.
|
|
|
*/
|
|
|
source_info.pixels=AcquirePixelThreadSet(image->columns,
|
|
|
source_info.channels,highres);
|
|
|
target_info.pixels=AcquirePixelThreadSet(image->columns,
|
|
|
target_info.channels,highres);
|
|
|
if ((source_info.pixels == (void **) NULL) ||
|
|
|
(target_info.pixels == (void **) NULL))
|
|
|
{
|
|
|
target_info.pixels=DestroyPixelThreadSet(target_info.pixels);
|
|
|
source_info.pixels=DestroyPixelThreadSet(source_info.pixels);
|
|
|
transform=DestroyTransformThreadSet(transform);
|
|
|
ThrowProfileException(ResourceLimitError,
|
|
|
"MemoryAllocationFailed",image->filename);
|
|
|
}
|
|
|
if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
|
|
|
{
|
|
|
target_info.pixels=DestroyPixelThreadSet(target_info.pixels);
|
|
|
source_info.pixels=DestroyPixelThreadSet(source_info.pixels);
|
|
|
transform=DestroyTransformThreadSet(transform);
|
|
|
if (source_info.profile != (cmsHPROFILE) NULL)
|
|
|
(void) cmsCloseProfile(source_info.profile);
|
|
|
if (target_info.profile != (cmsHPROFILE) NULL)
|
|
|
(void) cmsCloseProfile(target_info.profile);
|
|
|
return(MagickFalse);
|
|
|
}
|
|
|
if (target_info.colorspace == CMYKColorspace)
|
|
|
(void) SetImageColorspace(image,target_info.colorspace,exception);
|
|
|
progress=0;
|
|
|
image_view=AcquireAuthenticCacheView(image,exception);
|
|
|
#if defined(MAGICKCORE_OPENMP_SUPPORT)
|
|
|
#pragma omp parallel for schedule(static) shared(status) \
|
|
|
magick_number_threads(image,image,image->rows,1)
|
|
|
#endif
|
|
|
for (y=0; y < (ssize_t) image->rows; y++)
|
|
|
{
|
|
|
const int
|
|
|
id = GetOpenMPThreadId();
|
|
|
|
|
|
MagickBooleanType
|
|
|
sync;
|
|
|
|
|
|
Quantum
|
|
|
*magick_restrict q;
|
|
|
|
|
|
if (status == MagickFalse)
|
|
|
continue;
|
|
|
q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
|
|
|
exception);
|
|
|
if (q == (Quantum *) NULL)
|
|
|
{
|
|
|
status=MagickFalse;
|
|
|
continue;
|
|
|
}
|
|
|
if (highres != MagickFalse)
|
|
|
TransformDoublePixels(id,image,&source_info,&target_info,transform,q);
|
|
|
else
|
|
|
TransformQuantumPixels(id,image,&source_info,&target_info,transform,q);
|
|
|
sync=SyncCacheViewAuthenticPixels(image_view,exception);
|
|
|
if (sync == MagickFalse)
|
|
|
status=MagickFalse;
|
|
|
if (image->progress_monitor != (MagickProgressMonitor) NULL)
|
|
|
{
|
|
|
MagickBooleanType
|
|
|
proceed;
|
|
|
|
|
|
#if defined(MAGICKCORE_OPENMP_SUPPORT)
|
|
|
#pragma omp atomic
|
|
|
#endif
|
|
|
progress++;
|
|
|
proceed=SetImageProgress(image,ProfileImageTag,progress,
|
|
|
image->rows);
|
|
|
if (proceed == MagickFalse)
|
|
|
status=MagickFalse;
|
|
|
}
|
|
|
}
|
|
|
image_view=DestroyCacheView(image_view);
|
|
|
(void) SetImageColorspace(image,target_info.colorspace,exception);
|
|
|
switch (signature)
|
|
|
{
|
|
|
case cmsSigRgbData:
|
|
|
{
|
|
|
image->type=image->alpha_trait == UndefinedPixelTrait ?
|
|
|
TrueColorType : TrueColorAlphaType;
|
|
|
break;
|
|
|
}
|
|
|
case cmsSigCmykData:
|
|
|
{
|
|
|
image->type=image->alpha_trait == UndefinedPixelTrait ?
|
|
|
ColorSeparationType : ColorSeparationAlphaType;
|
|
|
break;
|
|
|
}
|
|
|
case cmsSigGrayData:
|
|
|
{
|
|
|
image->type=image->alpha_trait == UndefinedPixelTrait ?
|
|
|
GrayscaleType : GrayscaleAlphaType;
|
|
|
break;
|
|
|
}
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
target_info.pixels=DestroyPixelThreadSet(target_info.pixels);
|
|
|
source_info.pixels=DestroyPixelThreadSet(source_info.pixels);
|
|
|
transform=DestroyTransformThreadSet(transform);
|
|
|
if ((status != MagickFalse) &&
|
|
|
(cmsGetDeviceClass(source_info.profile) != cmsSigLinkClass))
|
|
|
status=SetImageProfile(image,name,profile,exception);
|
|
|
if (target_info.profile != (cmsHPROFILE) NULL)
|
|
|
(void) cmsCloseProfile(target_info.profile);
|
|
|
}
|
|
|
(void) cmsCloseProfile(source_info.profile);
|
|
|
cmsDeleteContext(cms_context);
|
|
|
}
|
|
|
#endif
|
|
|
}
|
|
|
profile=DestroyStringInfo(profile);
|
|
|
return(status);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% R e m o v e I m a g e P r o f i l e %
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
%
|
|
|
% RemoveImageProfile() removes a named profile from the image and returns its
|
|
|
% value.
|
|
|
%
|
|
|
% The format of the RemoveImageProfile method is:
|
|
|
%
|
|
|
% void *RemoveImageProfile(Image *image,const char *name)
|
|
|
%
|
|
|
% A description of each parameter follows:
|
|
|
%
|
|
|
% o image: the image.
|
|
|
%
|
|
|
% o name: the profile name.
|
|
|
%
|
|
|
*/
|
|
|
MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
|
|
|
{
|
|
|
StringInfo
|
|
|
*profile;
|
|
|
|
|
|
assert(image != (Image *) NULL);
|
|
|
assert(image->signature == MagickCoreSignature);
|
|
|
if (image->debug != MagickFalse)
|
|
|
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
|
|
|
if (image->profiles == (SplayTreeInfo *) NULL)
|
|
|
return((StringInfo *) NULL);
|
|
|
WriteTo8BimProfile(image,name,(StringInfo *) NULL);
|
|
|
profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
|
|
|
image->profiles,name);
|
|
|
return(profile);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% R e s e t P r o f i l e I t e r a t o r %
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
%
|
|
|
% ResetImageProfileIterator() resets the image profile iterator. Use it in
|
|
|
% conjunction with GetNextImageProfile() to iterate over all the profiles
|
|
|
% associated with an image.
|
|
|
%
|
|
|
% The format of the ResetImageProfileIterator method is:
|
|
|
%
|
|
|
% ResetImageProfileIterator(Image *image)
|
|
|
%
|
|
|
% A description of each parameter follows:
|
|
|
%
|
|
|
% o image: the image.
|
|
|
%
|
|
|
*/
|
|
|
MagickExport void ResetImageProfileIterator(const Image *image)
|
|
|
{
|
|
|
assert(image != (Image *) NULL);
|
|
|
assert(image->signature == MagickCoreSignature);
|
|
|
if (image->debug != MagickFalse)
|
|
|
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
|
|
|
if (image->profiles == (SplayTreeInfo *) NULL)
|
|
|
return;
|
|
|
ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% S e t I m a g e P r o f i l e %
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
%
|
|
|
% SetImageProfile() adds a named profile to the image. If a profile with the
|
|
|
% same name already exists, it is replaced. This method differs from the
|
|
|
% ProfileImage() method in that it does not apply CMS color profiles.
|
|
|
%
|
|
|
% The format of the SetImageProfile method is:
|
|
|
%
|
|
|
% MagickBooleanType SetImageProfile(Image *image,const char *name,
|
|
|
% const StringInfo *profile)
|
|
|
%
|
|
|
% A description of each parameter follows:
|
|
|
%
|
|
|
% o image: the image.
|
|
|
%
|
|
|
% o name: the profile name, for example icc, exif, and 8bim (8bim is the
|
|
|
% Photoshop wrapper for iptc profiles).
|
|
|
%
|
|
|
% o profile: A StringInfo structure that contains the named profile.
|
|
|
%
|
|
|
*/
|
|
|
|
|
|
static void *DestroyProfile(void *profile)
|
|
|
{
|
|
|
return((void *) DestroyStringInfo((StringInfo *) profile));
|
|
|
}
|
|
|
|
|
|
static inline const unsigned char *ReadResourceByte(const unsigned char *p,
|
|
|
unsigned char *quantum)
|
|
|
{
|
|
|
*quantum=(*p++);
|
|
|
return(p);
|
|
|
}
|
|
|
|
|
|
static inline const unsigned char *ReadResourceLong(const unsigned char *p,
|
|
|
unsigned int *quantum)
|
|
|
{
|
|
|
*quantum=(unsigned int) (*p++) << 24;
|
|
|
*quantum|=(unsigned int) (*p++) << 16;
|
|
|
*quantum|=(unsigned int) (*p++) << 8;
|
|
|
*quantum|=(unsigned int) (*p++);
|
|
|
return(p);
|
|
|
}
|
|
|
|
|
|
static inline const unsigned char *ReadResourceShort(const unsigned char *p,
|
|
|
unsigned short *quantum)
|
|
|
{
|
|
|
*quantum=(unsigned short) (*p++) << 8;
|
|
|
*quantum|=(unsigned short) (*p++);
|
|
|
return(p);
|
|
|
}
|
|
|
|
|
|
static inline void WriteResourceLong(unsigned char *p,
|
|
|
const unsigned int quantum)
|
|
|
{
|
|
|
unsigned char
|
|
|
buffer[4];
|
|
|
|
|
|
buffer[0]=(unsigned char) (quantum >> 24);
|
|
|
buffer[1]=(unsigned char) (quantum >> 16);
|
|
|
buffer[2]=(unsigned char) (quantum >> 8);
|
|
|
buffer[3]=(unsigned char) quantum;
|
|
|
(void) memcpy(p,buffer,4);
|
|
|
}
|
|
|
|
|
|
static void WriteTo8BimProfile(Image *image,const char *name,
|
|
|
const StringInfo *profile)
|
|
|
{
|
|
|
const unsigned char
|
|
|
*datum,
|
|
|
*q;
|
|
|
|
|
|
const unsigned char
|
|
|
*p;
|
|
|
|
|
|
size_t
|
|
|
length;
|
|
|
|
|
|
StringInfo
|
|
|
*profile_8bim;
|
|
|
|
|
|
ssize_t
|
|
|
count;
|
|
|
|
|
|
unsigned char
|
|
|
length_byte;
|
|
|
|
|
|
unsigned int
|
|
|
value;
|
|
|
|
|
|
unsigned short
|
|
|
id,
|
|
|
profile_id;
|
|
|
|
|
|
if (LocaleCompare(name,"icc") == 0)
|
|
|
profile_id=0x040f;
|
|
|
else
|
|
|
if (LocaleCompare(name,"iptc") == 0)
|
|
|
profile_id=0x0404;
|
|
|
else
|
|
|
if (LocaleCompare(name,"xmp") == 0)
|
|
|
profile_id=0x0424;
|
|
|
else
|
|
|
return;
|
|
|
profile_8bim=(StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
|
|
|
image->profiles,"8bim");
|
|
|
if (profile_8bim == (StringInfo *) NULL)
|
|
|
return;
|
|
|
datum=GetStringInfoDatum(profile_8bim);
|
|
|
length=GetStringInfoLength(profile_8bim);
|
|
|
for (p=datum; p < (datum+length-16); )
|
|
|
{
|
|
|
q=p;
|
|
|
if (LocaleNCompare((char *) p,"8BIM",4) != 0)
|
|
|
break;
|
|
|
p+=4;
|
|
|
p=ReadResourceShort(p,&id);
|
|
|
p=ReadResourceByte(p,&length_byte);
|
|
|
p+=length_byte;
|
|
|
if (((length_byte+1) & 0x01) != 0)
|
|
|
p++;
|
|
|
if (p > (datum+length-4))
|
|
|
break;
|
|
|
p=ReadResourceLong(p,&value);
|
|
|
count=(ssize_t) value;
|
|
|
if ((count & 0x01) != 0)
|
|
|
count++;
|
|
|
if ((count < 0) || (p > (datum+length-count)) || (count > (ssize_t) length))
|
|
|
break;
|
|
|
if (id != profile_id)
|
|
|
p+=count;
|
|
|
else
|
|
|
{
|
|
|
size_t
|
|
|
extent,
|
|
|
offset;
|
|
|
|
|
|
ssize_t
|
|
|
extract_extent;
|
|
|
|
|
|
StringInfo
|
|
|
*extract_profile;
|
|
|
|
|
|
extract_extent=0;
|
|
|
extent=(datum+length)-(p+count);
|
|
|
if (profile == (StringInfo *) NULL)
|
|
|
{
|
|
|
offset=(q-datum);
|
|
|
extract_profile=AcquireStringInfo(offset+extent);
|
|
|
(void) memcpy(extract_profile->datum,datum,offset);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
offset=(p-datum);
|
|
|
extract_extent=profile->length;
|
|
|
if ((extract_extent & 0x01) != 0)
|
|
|
extract_extent++;
|
|
|
extract_profile=AcquireStringInfo(offset+extract_extent+extent);
|
|
|
(void) memcpy(extract_profile->datum,datum,offset-4);
|
|
|
WriteResourceLong(extract_profile->datum+offset-4,(unsigned int)
|
|
|
profile->length);
|
|
|
(void) memcpy(extract_profile->datum+offset,
|
|
|
profile->datum,profile->length);
|
|
|
}
|
|
|
(void) memcpy(extract_profile->datum+offset+extract_extent,
|
|
|
p+count,extent);
|
|
|
(void) AddValueToSplayTree((SplayTreeInfo *) image->profiles,
|
|
|
ConstantString("8bim"),CloneStringInfo(extract_profile));
|
|
|
extract_profile=DestroyStringInfo(extract_profile);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static void GetProfilesFromResourceBlock(Image *image,
|
|
|
const StringInfo *resource_block,ExceptionInfo *exception)
|
|
|
{
|
|
|
const unsigned char
|
|
|
*datum;
|
|
|
|
|
|
const unsigned char
|
|
|
*p;
|
|
|
|
|
|
size_t
|
|
|
length;
|
|
|
|
|
|
ssize_t
|
|
|
count;
|
|
|
|
|
|
StringInfo
|
|
|
*profile;
|
|
|
|
|
|
unsigned char
|
|
|
length_byte;
|
|
|
|
|
|
unsigned int
|
|
|
value;
|
|
|
|
|
|
unsigned short
|
|
|
id;
|
|
|
|
|
|
datum=GetStringInfoDatum(resource_block);
|
|
|
length=GetStringInfoLength(resource_block);
|
|
|
for (p=datum; p < (datum+length-16); )
|
|
|
{
|
|
|
if (LocaleNCompare((char *) p,"8BIM",4) != 0)
|
|
|
break;
|
|
|
p+=4;
|
|
|
p=ReadResourceShort(p,&id);
|
|
|
p=ReadResourceByte(p,&length_byte);
|
|
|
p+=length_byte;
|
|
|
if (((length_byte+1) & 0x01) != 0)
|
|
|
p++;
|
|
|
if (p > (datum+length-4))
|
|
|
break;
|
|
|
p=ReadResourceLong(p,&value);
|
|
|
count=(ssize_t) value;
|
|
|
if ((p > (datum+length-count)) || (count > (ssize_t) length) || (count < 0))
|
|
|
break;
|
|
|
switch (id)
|
|
|
{
|
|
|
case 0x03ed:
|
|
|
{
|
|
|
unsigned int
|
|
|
resolution;
|
|
|
|
|
|
unsigned short
|
|
|
units;
|
|
|
|
|
|
/*
|
|
|
Resolution.
|
|
|
*/
|
|
|
if (count < 10)
|
|
|
break;
|
|
|
p=ReadResourceLong(p,&resolution);
|
|
|
image->resolution.x=((double) resolution)/65536.0;
|
|
|
p=ReadResourceShort(p,&units)+2;
|
|
|
p=ReadResourceLong(p,&resolution)+4;
|
|
|
image->resolution.y=((double) resolution)/65536.0;
|
|
|
/*
|
|
|
Values are always stored as pixels per inch.
|
|
|
*/
|
|
|
if ((ResolutionType) units != PixelsPerCentimeterResolution)
|
|
|
image->units=PixelsPerInchResolution;
|
|
|
else
|
|
|
{
|
|
|
image->units=PixelsPerCentimeterResolution;
|
|
|
image->resolution.x/=2.54;
|
|
|
image->resolution.y/=2.54;
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
case 0x0404:
|
|
|
{
|
|
|
/*
|
|
|
IPTC Profile
|
|
|
*/
|
|
|
profile=AcquireStringInfo(count);
|
|
|
SetStringInfoDatum(profile,p);
|
|
|
(void) SetImageProfileInternal(image,"iptc",profile,MagickTrue,
|
|
|
exception);
|
|
|
profile=DestroyStringInfo(profile);
|
|
|
p+=count;
|
|
|
break;
|
|
|
}
|
|
|
case 0x040c:
|
|
|
{
|
|
|
/*
|
|
|
Thumbnail.
|
|
|
*/
|
|
|
p+=count;
|
|
|
break;
|
|
|
}
|
|
|
case 0x040f:
|
|
|
{
|
|
|
/*
|
|
|
ICC Profile.
|
|
|
*/
|
|
|
profile=AcquireStringInfo(count);
|
|
|
SetStringInfoDatum(profile,p);
|
|
|
(void) SetImageProfileInternal(image,"icc",profile,MagickTrue,
|
|
|
exception);
|
|
|
profile=DestroyStringInfo(profile);
|
|
|
p+=count;
|
|
|
break;
|
|
|
}
|
|
|
case 0x0422:
|
|
|
{
|
|
|
/*
|
|
|
EXIF Profile.
|
|
|
*/
|
|
|
profile=AcquireStringInfo(count);
|
|
|
SetStringInfoDatum(profile,p);
|
|
|
(void) SetImageProfileInternal(image,"exif",profile,MagickTrue,
|
|
|
exception);
|
|
|
profile=DestroyStringInfo(profile);
|
|
|
p+=count;
|
|
|
break;
|
|
|
}
|
|
|
case 0x0424:
|
|
|
{
|
|
|
/*
|
|
|
XMP Profile.
|
|
|
*/
|
|
|
profile=AcquireStringInfo(count);
|
|
|
SetStringInfoDatum(profile,p);
|
|
|
(void) SetImageProfileInternal(image,"xmp",profile,MagickTrue,
|
|
|
exception);
|
|
|
profile=DestroyStringInfo(profile);
|
|
|
p+=count;
|
|
|
break;
|
|
|
}
|
|
|
default:
|
|
|
{
|
|
|
p+=count;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
if ((count & 0x01) != 0)
|
|
|
p++;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static void PatchCorruptProfile(const char *name,StringInfo *profile)
|
|
|
{
|
|
|
unsigned char
|
|
|
*p;
|
|
|
|
|
|
size_t
|
|
|
length;
|
|
|
|
|
|
/*
|
|
|
Detect corrupt profiles and if discovered, repair.
|
|
|
*/
|
|
|
if (LocaleCompare(name,"xmp") == 0)
|
|
|
{
|
|
|
/*
|
|
|
Remove garbage after xpacket end.
|
|
|
*/
|
|
|
p=GetStringInfoDatum(profile);
|
|
|
p=(unsigned char *) strstr((const char *) p,"<?xpacket end=\"w\"?>");
|
|
|
if (p != (unsigned char *) NULL)
|
|
|
{
|
|
|
p+=19;
|
|
|
length=p-GetStringInfoDatum(profile);
|
|
|
if (length != GetStringInfoLength(profile))
|
|
|
{
|
|
|
*p='\0';
|
|
|
SetStringInfoLength(profile,length);
|
|
|
}
|
|
|
}
|
|
|
return;
|
|
|
}
|
|
|
if (LocaleCompare(name,"exif") == 0)
|
|
|
{
|
|
|
/*
|
|
|
Check if profile starts with byte order marker instead of Exif.
|
|
|
*/
|
|
|
p=GetStringInfoDatum(profile);
|
|
|
if ((LocaleNCompare((const char *) p,"MM",2) == 0) ||
|
|
|
(LocaleNCompare((const char *) p,"II",2) == 0))
|
|
|
{
|
|
|
const unsigned char
|
|
|
profile_start[] = "Exif\0\0";
|
|
|
|
|
|
StringInfo
|
|
|
*exif_profile;
|
|
|
|
|
|
exif_profile=AcquireStringInfo(6);
|
|
|
if (exif_profile != (StringInfo *) NULL)
|
|
|
{
|
|
|
SetStringInfoDatum(exif_profile,profile_start);
|
|
|
ConcatenateStringInfo(exif_profile,profile);
|
|
|
SetStringInfoLength(profile,GetStringInfoLength(exif_profile));
|
|
|
SetStringInfo(profile,exif_profile);
|
|
|
exif_profile=DestroyStringInfo(exif_profile);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#if defined(MAGICKCORE_XML_DELEGATE)
|
|
|
static MagickBooleanType ValidateXMPProfile(Image *image,
|
|
|
const StringInfo *profile,ExceptionInfo *exception)
|
|
|
{
|
|
|
xmlDocPtr
|
|
|
document;
|
|
|
|
|
|
/*
|
|
|
Parse XML profile.
|
|
|
*/
|
|
|
document=xmlReadMemory((const char *) GetStringInfoDatum(profile),(int)
|
|
|
GetStringInfoLength(profile),"xmp.xml",NULL,XML_PARSE_NOERROR |
|
|
|
XML_PARSE_NOWARNING);
|
|
|
if (document == (xmlDocPtr) NULL)
|
|
|
{
|
|
|
(void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
|
|
|
"CorruptImageProfile","`%s' (XMP)",image->filename);
|
|
|
return(MagickFalse);
|
|
|
}
|
|
|
xmlFreeDoc(document);
|
|
|
return(MagickTrue);
|
|
|
}
|
|
|
#else
|
|
|
static MagickBooleanType ValidateXMPProfile(Image *image,
|
|
|
const StringInfo *profile,ExceptionInfo *exception)
|
|
|
{
|
|
|
(void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateWarning,
|
|
|
"DelegateLibrarySupportNotBuiltIn","'%s' (XML)",image->filename);
|
|
|
return(MagickFalse);
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
static MagickBooleanType SetImageProfileInternal(Image *image,const char *name,
|
|
|
const StringInfo *profile,const MagickBooleanType recursive,
|
|
|
ExceptionInfo *exception)
|
|
|
{
|
|
|
char
|
|
|
key[MagickPathExtent];
|
|
|
|
|
|
MagickBooleanType
|
|
|
status;
|
|
|
|
|
|
StringInfo
|
|
|
*clone_profile;
|
|
|
|
|
|
assert(image != (Image *) NULL);
|
|
|
assert(image->signature == MagickCoreSignature);
|
|
|
if (image->debug != MagickFalse)
|
|
|
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
|
|
|
clone_profile=CloneStringInfo(profile);
|
|
|
PatchCorruptProfile(name,clone_profile);
|
|
|
if ((LocaleCompare(name,"xmp") == 0) &&
|
|
|
(ValidateXMPProfile(image,clone_profile,exception) == MagickFalse))
|
|
|
{
|
|
|
clone_profile=DestroyStringInfo(clone_profile);
|
|
|
return(MagickTrue);
|
|
|
}
|
|
|
if (image->profiles == (SplayTreeInfo *) NULL)
|
|
|
image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
|
|
|
DestroyProfile);
|
|
|
(void) CopyMagickString(key,name,MagickPathExtent);
|
|
|
LocaleLower(key);
|
|
|
status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
|
|
|
ConstantString(key),clone_profile);
|
|
|
if (status != MagickFalse)
|
|
|
{
|
|
|
if (LocaleCompare(name,"8bim") == 0)
|
|
|
GetProfilesFromResourceBlock(image,clone_profile,exception);
|
|
|
else
|
|
|
if (recursive == MagickFalse)
|
|
|
WriteTo8BimProfile(image,name,clone_profile);
|
|
|
}
|
|
|
return(status);
|
|
|
}
|
|
|
|
|
|
MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
|
|
|
const StringInfo *profile,ExceptionInfo *exception)
|
|
|
{
|
|
|
return(SetImageProfileInternal(image,name,profile,MagickFalse,exception));
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% S y n c I m a g e P r o f i l e s %
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
%
|
|
|
% SyncImageProfiles() synchronizes image properties with the image profiles.
|
|
|
% Currently we only support updating the EXIF resolution and orientation.
|
|
|
%
|
|
|
% The format of the SyncImageProfiles method is:
|
|
|
%
|
|
|
% MagickBooleanType SyncImageProfiles(Image *image)
|
|
|
%
|
|
|
% A description of each parameter follows:
|
|
|
%
|
|
|
% o image: the image.
|
|
|
%
|
|
|
*/
|
|
|
|
|
|
static inline int ReadProfileByte(unsigned char **p,size_t *length)
|
|
|
{
|
|
|
int
|
|
|
c;
|
|
|
|
|
|
if (*length < 1)
|
|
|
return(EOF);
|
|
|
c=(int) (*(*p)++);
|
|
|
(*length)--;
|
|
|
return(c);
|
|
|
}
|
|
|
|
|
|
static inline signed short ReadProfileShort(const EndianType endian,
|
|
|
unsigned char *buffer)
|
|
|
{
|
|
|
union
|
|
|
{
|
|
|
unsigned int
|
|
|
unsigned_value;
|
|
|
|
|
|
signed int
|
|
|
signed_value;
|
|
|
} quantum;
|
|
|
|
|
|
unsigned short
|
|
|
value;
|
|
|
|
|
|
if (endian == LSBEndian)
|
|
|
{
|
|
|
value=(unsigned short) buffer[1] << 8;
|
|
|
value|=(unsigned short) buffer[0];
|
|
|
quantum.unsigned_value=value & 0xffff;
|
|
|
return(quantum.signed_value);
|
|
|
}
|
|
|
value=(unsigned short) buffer[0] << 8;
|
|
|
value|=(unsigned short) buffer[1];
|
|
|
quantum.unsigned_value=value & 0xffff;
|
|
|
return(quantum.signed_value);
|
|
|
}
|
|
|
|
|
|
static inline signed int ReadProfileLong(const EndianType endian,
|
|
|
unsigned char *buffer)
|
|
|
{
|
|
|
union
|
|
|
{
|
|
|
unsigned int
|
|
|
unsigned_value;
|
|
|
|
|
|
signed int
|
|
|
signed_value;
|
|
|
} quantum;
|
|
|
|
|
|
unsigned int
|
|
|
value;
|
|
|
|
|
|
if (endian == LSBEndian)
|
|
|
{
|
|
|
value=(unsigned int) buffer[3] << 24;
|
|
|
value|=(unsigned int) buffer[2] << 16;
|
|
|
value|=(unsigned int) buffer[1] << 8;
|
|
|
value|=(unsigned int) buffer[0];
|
|
|
quantum.unsigned_value=value & 0xffffffff;
|
|
|
return(quantum.signed_value);
|
|
|
}
|
|
|
value=(unsigned int) buffer[0] << 24;
|
|
|
value|=(unsigned int) buffer[1] << 16;
|
|
|
value|=(unsigned int) buffer[2] << 8;
|
|
|
value|=(unsigned int) buffer[3];
|
|
|
quantum.unsigned_value=value & 0xffffffff;
|
|
|
return(quantum.signed_value);
|
|
|
}
|
|
|
|
|
|
static inline signed int ReadProfileMSBLong(unsigned char **p,size_t *length)
|
|
|
{
|
|
|
signed int
|
|
|
value;
|
|
|
|
|
|
if (*length < 4)
|
|
|
return(0);
|
|
|
value=ReadProfileLong(MSBEndian,*p);
|
|
|
(*length)-=4;
|
|
|
*p+=4;
|
|
|
return(value);
|
|
|
}
|
|
|
|
|
|
static inline signed short ReadProfileMSBShort(unsigned char **p,
|
|
|
size_t *length)
|
|
|
{
|
|
|
signed short
|
|
|
value;
|
|
|
|
|
|
if (*length < 2)
|
|
|
return(0);
|
|
|
value=ReadProfileShort(MSBEndian,*p);
|
|
|
(*length)-=2;
|
|
|
*p+=2;
|
|
|
return(value);
|
|
|
}
|
|
|
|
|
|
static inline void WriteProfileLong(const EndianType endian,
|
|
|
const size_t value,unsigned char *p)
|
|
|
{
|
|
|
unsigned char
|
|
|
buffer[4];
|
|
|
|
|
|
if (endian == LSBEndian)
|
|
|
{
|
|
|
buffer[0]=(unsigned char) value;
|
|
|
buffer[1]=(unsigned char) (value >> 8);
|
|
|
buffer[2]=(unsigned char) (value >> 16);
|
|
|
buffer[3]=(unsigned char) (value >> 24);
|
|
|
(void) memcpy(p,buffer,4);
|
|
|
return;
|
|
|
}
|
|
|
buffer[0]=(unsigned char) (value >> 24);
|
|
|
buffer[1]=(unsigned char) (value >> 16);
|
|
|
buffer[2]=(unsigned char) (value >> 8);
|
|
|
buffer[3]=(unsigned char) value;
|
|
|
(void) memcpy(p,buffer,4);
|
|
|
}
|
|
|
|
|
|
static void WriteProfileShort(const EndianType endian,
|
|
|
const unsigned short value,unsigned char *p)
|
|
|
{
|
|
|
unsigned char
|
|
|
buffer[2];
|
|
|
|
|
|
if (endian == LSBEndian)
|
|
|
{
|
|
|
buffer[0]=(unsigned char) value;
|
|
|
buffer[1]=(unsigned char) (value >> 8);
|
|
|
(void) memcpy(p,buffer,2);
|
|
|
return;
|
|
|
}
|
|
|
buffer[0]=(unsigned char) (value >> 8);
|
|
|
buffer[1]=(unsigned char) value;
|
|
|
(void) memcpy(p,buffer,2);
|
|
|
}
|
|
|
|
|
|
static MagickBooleanType Sync8BimProfile(Image *image,StringInfo *profile)
|
|
|
{
|
|
|
size_t
|
|
|
length;
|
|
|
|
|
|
ssize_t
|
|
|
count;
|
|
|
|
|
|
unsigned char
|
|
|
*p;
|
|
|
|
|
|
unsigned short
|
|
|
id;
|
|
|
|
|
|
length=GetStringInfoLength(profile);
|
|
|
p=GetStringInfoDatum(profile);
|
|
|
while (length != 0)
|
|
|
{
|
|
|
if (ReadProfileByte(&p,&length) != 0x38)
|
|
|
continue;
|
|
|
if (ReadProfileByte(&p,&length) != 0x42)
|
|
|
continue;
|
|
|
if (ReadProfileByte(&p,&length) != 0x49)
|
|
|
continue;
|
|
|
if (ReadProfileByte(&p,&length) != 0x4D)
|
|
|
continue;
|
|
|
if (length < 7)
|
|
|
return(MagickFalse);
|
|
|
id=ReadProfileMSBShort(&p,&length);
|
|
|
count=(ssize_t) ReadProfileByte(&p,&length);
|
|
|
if ((count >= (ssize_t) length) || (count < 0))
|
|
|
return(MagickFalse);
|
|
|
p+=count;
|
|
|
length-=count;
|
|
|
if ((*p & 0x01) == 0)
|
|
|
(void) ReadProfileByte(&p,&length);
|
|
|
count=(ssize_t) ReadProfileMSBLong(&p,&length);
|
|
|
if ((count > (ssize_t) length) || (count < 0))
|
|
|
return(MagickFalse);
|
|
|
if ((id == 0x3ED) && (count == 16))
|
|
|
{
|
|
|
if (image->units == PixelsPerCentimeterResolution)
|
|
|
WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
|
|
|
image->resolution.x*2.54*65536.0),p);
|
|
|
else
|
|
|
WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
|
|
|
image->resolution.x*65536.0),p);
|
|
|
WriteProfileShort(MSBEndian,(unsigned short) image->units,p+4);
|
|
|
if (image->units == PixelsPerCentimeterResolution)
|
|
|
WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
|
|
|
image->resolution.y*2.54*65536.0),p+8);
|
|
|
else
|
|
|
WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
|
|
|
image->resolution.y*65536.0),p+8);
|
|
|
WriteProfileShort(MSBEndian,(unsigned short) image->units,p+12);
|
|
|
}
|
|
|
p+=count;
|
|
|
length-=count;
|
|
|
}
|
|
|
return(MagickTrue);
|
|
|
}
|
|
|
|
|
|
MagickBooleanType SyncExifProfile(Image *image,StringInfo *profile)
|
|
|
{
|
|
|
#define MaxDirectoryStack 16
|
|
|
#define EXIF_DELIMITER "\n"
|
|
|
#define EXIF_NUM_FORMATS 12
|
|
|
#define TAG_EXIF_OFFSET 0x8769
|
|
|
#define TAG_INTEROP_OFFSET 0xa005
|
|
|
|
|
|
typedef struct _DirectoryInfo
|
|
|
{
|
|
|
unsigned char
|
|
|
*directory;
|
|
|
|
|
|
size_t
|
|
|
entry;
|
|
|
} DirectoryInfo;
|
|
|
|
|
|
DirectoryInfo
|
|
|
directory_stack[MaxDirectoryStack];
|
|
|
|
|
|
EndianType
|
|
|
endian;
|
|
|
|
|
|
size_t
|
|
|
entry,
|
|
|
length,
|
|
|
number_entries;
|
|
|
|
|
|
SplayTreeInfo
|
|
|
*exif_resources;
|
|
|
|
|
|
ssize_t
|
|
|
id,
|
|
|
level,
|
|
|
offset;
|
|
|
|
|
|
static int
|
|
|
format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
|
|
|
|
|
|
unsigned char
|
|
|
*directory,
|
|
|
*exif;
|
|
|
|
|
|
/*
|
|
|
Set EXIF resolution tag.
|
|
|
*/
|
|
|
length=GetStringInfoLength(profile);
|
|
|
exif=GetStringInfoDatum(profile);
|
|
|
if (length < 16)
|
|
|
return(MagickFalse);
|
|
|
id=(ssize_t) ReadProfileShort(LSBEndian,exif);
|
|
|
if ((id != 0x4949) && (id != 0x4D4D))
|
|
|
{
|
|
|
while (length != 0)
|
|
|
{
|
|
|
if (ReadProfileByte(&exif,&length) != 0x45)
|
|
|
continue;
|
|
|
if (ReadProfileByte(&exif,&length) != 0x78)
|
|
|
continue;
|
|
|
if (ReadProfileByte(&exif,&length) != 0x69)
|
|
|
continue;
|
|
|
if (ReadProfileByte(&exif,&length) != 0x66)
|
|
|
continue;
|
|
|
if (ReadProfileByte(&exif,&length) != 0x00)
|
|
|
continue;
|
|
|
if (ReadProfileByte(&exif,&length) != 0x00)
|
|
|
continue;
|
|
|
break;
|
|
|
}
|
|
|
if (length < 16)
|
|
|
return(MagickFalse);
|
|
|
id=(ssize_t) ReadProfileShort(LSBEndian,exif);
|
|
|
}
|
|
|
endian=LSBEndian;
|
|
|
if (id == 0x4949)
|
|
|
endian=LSBEndian;
|
|
|
else
|
|
|
if (id == 0x4D4D)
|
|
|
endian=MSBEndian;
|
|
|
else
|
|
|
return(MagickFalse);
|
|
|
if (ReadProfileShort(endian,exif+2) != 0x002a)
|
|
|
return(MagickFalse);
|
|
|
/*
|
|
|
This the offset to the first IFD.
|
|
|
*/
|
|
|
offset=(ssize_t) ReadProfileLong(endian,exif+4);
|
|
|
if ((offset < 0) || ((size_t) offset >= length))
|
|
|
return(MagickFalse);
|
|
|
directory=exif+offset;
|
|
|
level=0;
|
|
|
entry=0;
|
|
|
exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
|
|
|
(void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
|
|
|
do
|
|
|
{
|
|
|
if (level > 0)
|
|
|
{
|
|
|
level--;
|
|
|
directory=directory_stack[level].directory;
|
|
|
entry=directory_stack[level].entry;
|
|
|
}
|
|
|
if ((directory < exif) || (directory > (exif+length-2)))
|
|
|
break;
|
|
|
/*
|
|
|
Determine how many entries there are in the current IFD.
|
|
|
*/
|
|
|
number_entries=ReadProfileShort(endian,directory);
|
|
|
for ( ; entry < number_entries; entry++)
|
|
|
{
|
|
|
int
|
|
|
components;
|
|
|
|
|
|
unsigned char
|
|
|
*p,
|
|
|
*q;
|
|
|
|
|
|
size_t
|
|
|
number_bytes;
|
|
|
|
|
|
ssize_t
|
|
|
format,
|
|
|
tag_value;
|
|
|
|
|
|
q=(unsigned char *) (directory+2+(12*entry));
|
|
|
if (q > (exif+length-12))
|
|
|
break; /* corrupt EXIF */
|
|
|
if (GetValueFromSplayTree(exif_resources,q) == q)
|
|
|
break;
|
|
|
(void) AddValueToSplayTree(exif_resources,q,q);
|
|
|
tag_value=(ssize_t) ReadProfileShort(endian,q);
|
|
|
format=(ssize_t) ReadProfileShort(endian,q+2);
|
|
|
if ((format < 0) || ((format-1) >= EXIF_NUM_FORMATS))
|
|
|
break;
|
|
|
components=(int) ReadProfileLong(endian,q+4);
|
|
|
if (components < 0)
|
|
|
break; /* corrupt EXIF */
|
|
|
number_bytes=(size_t) components*format_bytes[format];
|
|
|
if ((ssize_t) number_bytes < components)
|
|
|
break; /* prevent overflow */
|
|
|
if (number_bytes <= 4)
|
|
|
p=q+8;
|
|
|
else
|
|
|
{
|
|
|
/*
|
|
|
The directory entry contains an offset.
|
|
|
*/
|
|
|
offset=(ssize_t) ReadProfileLong(endian,q+8);
|
|
|
if ((offset < 0) || ((size_t) (offset+number_bytes) > length))
|
|
|
continue;
|
|
|
if (~length < number_bytes)
|
|
|
continue; /* prevent overflow */
|
|
|
p=(unsigned char *) (exif+offset);
|
|
|
}
|
|
|
switch (tag_value)
|
|
|
{
|
|
|
case 0x011a:
|
|
|
{
|
|
|
(void) WriteProfileLong(endian,(size_t) (image->resolution.x+0.5),p);
|
|
|
if (number_bytes == 8)
|
|
|
(void) WriteProfileLong(endian,1UL,p+4);
|
|
|
break;
|
|
|
}
|
|
|
case 0x011b:
|
|
|
{
|
|
|
(void) WriteProfileLong(endian,(size_t) (image->resolution.y+0.5),p);
|
|
|
if (number_bytes == 8)
|
|
|
(void) WriteProfileLong(endian,1UL,p+4);
|
|
|
break;
|
|
|
}
|
|
|
case 0x0112:
|
|
|
{
|
|
|
if (number_bytes == 4)
|
|
|
{
|
|
|
(void) WriteProfileLong(endian,(size_t) image->orientation,p);
|
|
|
break;
|
|
|
}
|
|
|
(void) WriteProfileShort(endian,(unsigned short) image->orientation,
|
|
|
p);
|
|
|
break;
|
|
|
}
|
|
|
case 0x0128:
|
|
|
{
|
|
|
if (number_bytes == 4)
|
|
|
{
|
|
|
(void) WriteProfileLong(endian,(size_t) (image->units+1),p);
|
|
|
break;
|
|
|
}
|
|
|
(void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
|
|
|
break;
|
|
|
}
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
|
|
|
{
|
|
|
offset=(ssize_t) ReadProfileLong(endian,p);
|
|
|
if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
|
|
|
{
|
|
|
directory_stack[level].directory=directory;
|
|
|
entry++;
|
|
|
directory_stack[level].entry=entry;
|
|
|
level++;
|
|
|
directory_stack[level].directory=exif+offset;
|
|
|
directory_stack[level].entry=0;
|
|
|
level++;
|
|
|
if ((directory+2+(12*number_entries)) > (exif+length))
|
|
|
break;
|
|
|
offset=(ssize_t) ReadProfileLong(endian,directory+2+(12*
|
|
|
number_entries));
|
|
|
if ((offset != 0) && ((size_t) offset < length) &&
|
|
|
(level < (MaxDirectoryStack-2)))
|
|
|
{
|
|
|
directory_stack[level].directory=exif+offset;
|
|
|
directory_stack[level].entry=0;
|
|
|
level++;
|
|
|
}
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
} while (level > 0);
|
|
|
exif_resources=DestroySplayTree(exif_resources);
|
|
|
return(MagickTrue);
|
|
|
}
|
|
|
|
|
|
MagickPrivate MagickBooleanType SyncImageProfiles(Image *image)
|
|
|
{
|
|
|
MagickBooleanType
|
|
|
status;
|
|
|
|
|
|
StringInfo
|
|
|
*profile;
|
|
|
|
|
|
status=MagickTrue;
|
|
|
profile=(StringInfo *) GetImageProfile(image,"8BIM");
|
|
|
if (profile != (StringInfo *) NULL)
|
|
|
if (Sync8BimProfile(image,profile) == MagickFalse)
|
|
|
status=MagickFalse;
|
|
|
profile=(StringInfo *) GetImageProfile(image,"EXIF");
|
|
|
if (profile != (StringInfo *) NULL)
|
|
|
if (SyncExifProfile(image,profile) == MagickFalse)
|
|
|
status=MagickFalse;
|
|
|
return(status);
|
|
|
}
|
|
|
|
|
|
static void UpdateClipPath(unsigned char *blob,size_t length,
|
|
|
const size_t old_columns,const size_t old_rows,
|
|
|
const RectangleInfo *new_geometry)
|
|
|
{
|
|
|
ssize_t
|
|
|
i;
|
|
|
|
|
|
ssize_t
|
|
|
knot_count,
|
|
|
selector;
|
|
|
|
|
|
knot_count=0;
|
|
|
while (length != 0)
|
|
|
{
|
|
|
selector=(ssize_t) ReadProfileMSBShort(&blob,&length);
|
|
|
switch (selector)
|
|
|
{
|
|
|
case 0:
|
|
|
case 3:
|
|
|
{
|
|
|
if (knot_count != 0)
|
|
|
{
|
|
|
blob+=24;
|
|
|
length-=MagickMin(24,(ssize_t) length);
|
|
|
break;
|
|
|
}
|
|
|
/*
|
|
|
Expected subpath length record.
|
|
|
*/
|
|
|
knot_count=(ssize_t) ReadProfileMSBShort(&blob,&length);
|
|
|
blob+=22;
|
|
|
length-=MagickMin(22,(ssize_t) length);
|
|
|
break;
|
|
|
}
|
|
|
case 1:
|
|
|
case 2:
|
|
|
case 4:
|
|
|
case 5:
|
|
|
{
|
|
|
if (knot_count == 0)
|
|
|
{
|
|
|
/*
|
|
|
Unexpected subpath knot.
|
|
|
*/
|
|
|
blob+=24;
|
|
|
length-=MagickMin(24,(ssize_t) length);
|
|
|
break;
|
|
|
}
|
|
|
/*
|
|
|
Add sub-path knot
|
|
|
*/
|
|
|
for (i=0; i < 3; i++)
|
|
|
{
|
|
|
double
|
|
|
x,
|
|
|
y;
|
|
|
|
|
|
signed int
|
|
|
xx,
|
|
|
yy;
|
|
|
|
|
|
y=(double) ReadProfileMSBLong(&blob,&length);
|
|
|
y=y*old_rows/4096.0/4096.0;
|
|
|
y-=new_geometry->y;
|
|
|
yy=(signed int) ((y*4096*4096)/new_geometry->height);
|
|
|
WriteProfileLong(MSBEndian,(size_t) yy,blob-4);
|
|
|
x=(double) ReadProfileMSBLong(&blob,&length);
|
|
|
x=x*old_columns/4096.0/4096.0;
|
|
|
x-=new_geometry->x;
|
|
|
xx=(signed int) ((x*4096*4096)/new_geometry->width);
|
|
|
WriteProfileLong(MSBEndian,(size_t) xx,blob-4);
|
|
|
}
|
|
|
knot_count--;
|
|
|
break;
|
|
|
}
|
|
|
case 6:
|
|
|
case 7:
|
|
|
case 8:
|
|
|
default:
|
|
|
{
|
|
|
blob+=24;
|
|
|
length-=MagickMin(24,(ssize_t) length);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
MagickPrivate void Update8BIMClipPath(const Image *image,
|
|
|
const size_t old_columns,const size_t old_rows,
|
|
|
const RectangleInfo *new_geometry)
|
|
|
{
|
|
|
const StringInfo
|
|
|
*profile;
|
|
|
|
|
|
size_t
|
|
|
length;
|
|
|
|
|
|
ssize_t
|
|
|
count,
|
|
|
id;
|
|
|
|
|
|
unsigned char
|
|
|
*info;
|
|
|
|
|
|
assert(image != (Image *) NULL);
|
|
|
assert(new_geometry != (RectangleInfo *) NULL);
|
|
|
profile=GetImageProfile(image,"8bim");
|
|
|
if (profile == (StringInfo *) NULL)
|
|
|
return;
|
|
|
length=GetStringInfoLength(profile);
|
|
|
info=GetStringInfoDatum(profile);
|
|
|
while (length > 0)
|
|
|
{
|
|
|
if (ReadProfileByte(&info,&length) != (unsigned char) '8')
|
|
|
continue;
|
|
|
if (ReadProfileByte(&info,&length) != (unsigned char) 'B')
|
|
|
continue;
|
|
|
if (ReadProfileByte(&info,&length) != (unsigned char) 'I')
|
|
|
continue;
|
|
|
if (ReadProfileByte(&info,&length) != (unsigned char) 'M')
|
|
|
continue;
|
|
|
id=(ssize_t) ReadProfileMSBShort(&info,&length);
|
|
|
count=(ssize_t) ReadProfileByte(&info,&length);
|
|
|
if ((count != 0) && ((size_t) count <= length))
|
|
|
{
|
|
|
info+=count;
|
|
|
length-=count;
|
|
|
}
|
|
|
if ((count & 0x01) == 0)
|
|
|
(void) ReadProfileByte(&info,&length);
|
|
|
count=(ssize_t) ReadProfileMSBLong(&info,&length);
|
|
|
if ((count < 0) || ((size_t) count > length))
|
|
|
{
|
|
|
length=0;
|
|
|
continue;
|
|
|
}
|
|
|
if ((id > 1999) && (id < 2999))
|
|
|
UpdateClipPath(info,(size_t) count,old_columns,old_rows,new_geometry);
|
|
|
info+=count;
|
|
|
length-=MagickMin(count,(ssize_t) length);
|
|
|
}
|
|
|
}
|