2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
% PPPP RRRR OOO FFFFF IIIII L EEEEE %
7
% P P R R O O F I L E %
8
% PPPP RRRR O O FFF I L EEE %
10
% P R R OOO F IIIII LLLLL EEEEE %
13
% MagickCore Image Profile Methods %
20
% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
21
% dedicated to making software imaging solutions freely available. %
23
% You may not use this file except in compliance with the License. You may %
24
% obtain a copy of the License at %
26
% http://www.imagemagick.org/script/license.php %
28
% Unless required by applicable law or agreed to in writing, software %
29
% distributed under the License is distributed on an "AS IS" BASIS, %
30
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31
% See the License for the specific language governing permissions and %
32
% limitations under the License. %
34
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42
#include "magick/studio.h"
43
#include "magick/cache.h"
44
#include "magick/color.h"
45
#include "magick/colorspace-private.h"
46
#include "magick/configure.h"
47
#include "magick/exception.h"
48
#include "magick/exception-private.h"
49
#include "magick/hashmap.h"
50
#include "magick/image.h"
51
#include "magick/memory_.h"
52
#include "magick/monitor.h"
53
#include "magick/monitor-private.h"
54
#include "magick/option.h"
55
#include "magick/profile.h"
56
#include "magick/property.h"
57
#include "magick/quantum.h"
58
#include "magick/quantum-private.h"
59
#include "magick/resource_.h"
60
#include "magick/splay-tree.h"
61
#include "magick/string_.h"
62
#include "magick/thread-private.h"
63
#include "magick/token.h"
64
#include "magick/utility.h"
65
#if defined(MAGICKCORE_LCMS_DELEGATE)
66
#if defined(MAGICKCORE_HAVE_LCMS_LCMS2_H)
68
#include <lcms/lcms2.h>
69
#elif defined(MAGICKCORE_HAVE_LCMS2_H)
72
#elif defined(MAGICKCORE_HAVE_LCMS_LCMS_H)
73
#include <lcms/lcms.h>
82
#if !defined(LCMS_VERSION) || (LCMS_VERSION < 2000)
83
#define cmsSigCmykData icSigCmykData
84
#define cmsSigGrayData icSigGrayData
85
#define cmsSigLabData icSigLabData
86
#define cmsSigLuvData icSigLuvData
87
#define cmsSigRgbData icSigRgbData
88
#define cmsSigXYZData icSigXYZData
89
#define cmsSigYCbCrData icSigYCbCrData
90
#define cmsSigLinkClass icSigLinkClass
91
#define cmsColorSpaceSignature icColorSpaceSignature
92
#define cmsUInt32Number DWORD
93
#define cmsSetLogErrorHandler(handler) cmsSetErrorHandler(handler)
94
#define cmsCreateTransformTHR(context,source_profile,source_type, \
95
target_profile,target_type,intent,flags) cmsCreateTransform(source_profile, \
96
source_type,target_profile,target_type,intent,flags);
97
#define cmsOpenProfileFromMemTHR(context,profile,length) \
98
cmsOpenProfileFromMem(profile,length)
102
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106
% C l o n e I m a g e P r o f i l e s %
110
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
112
% CloneImageProfiles() clones one or more image profiles.
114
% The format of the CloneImageProfiles method is:
116
% MagickBooleanType CloneImageProfiles(Image *image,
117
% const Image *clone_image)
119
% A description of each parameter follows:
121
% o image: the image.
123
% o clone_image: the clone image.
126
MagickExport MagickBooleanType CloneImageProfiles(Image *image,
127
const Image *clone_image)
129
assert(image != (Image *) NULL);
130
assert(image->signature == MagickSignature);
131
if (image->debug != MagickFalse)
132
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
133
assert(clone_image != (const Image *) NULL);
134
assert(clone_image->signature == MagickSignature);
135
image->color_profile.length=clone_image->color_profile.length;
136
image->color_profile.info=clone_image->color_profile.info;
137
image->iptc_profile.length=clone_image->iptc_profile.length;
138
image->iptc_profile.info=clone_image->iptc_profile.info;
139
if (clone_image->profiles != (void *) NULL)
140
image->profiles=CloneSplayTree((SplayTreeInfo *) clone_image->profiles,
141
(void *(*)(void *)) ConstantString,(void *(*)(void *)) CloneStringInfo);
146
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
150
% D e l e t e I m a g e P r o f i l e %
154
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
156
% DeleteImageProfile() deletes a profile from the image by its name.
158
% The format of the DeleteImageProfile method is:
160
% MagickBooleanTyupe DeleteImageProfile(Image *image,const char *name)
162
% A description of each parameter follows:
164
% o image: the image.
166
% o name: the profile name.
169
MagickExport MagickBooleanType DeleteImageProfile(Image *image,const char *name)
171
assert(image != (Image *) NULL);
172
assert(image->signature == MagickSignature);
173
if (image->debug != MagickFalse)
174
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
175
if (image->profiles == (SplayTreeInfo *) NULL)
177
if (LocaleCompare(name,"icc") == 0)
180
Continue to support deprecated color profile for now.
182
image->color_profile.length=0;
183
image->color_profile.info=(unsigned char *) NULL;
185
if (LocaleCompare(name,"iptc") == 0)
188
Continue to support deprecated IPTC profile for now.
190
image->iptc_profile.length=0;
191
image->iptc_profile.info=(unsigned char *) NULL;
193
return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->profiles,name));
197
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
201
% D e s t r o y I m a g e P r o f i l e s %
205
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
207
% DestroyImageProfiles() releases memory associated with an image profile map.
209
% The format of the DestroyProfiles method is:
211
% void DestroyImageProfiles(Image *image)
213
% A description of each parameter follows:
215
% o image: the image.
218
MagickExport void DestroyImageProfiles(Image *image)
220
if (image->profiles != (SplayTreeInfo *) NULL)
221
image->profiles=DestroySplayTree((SplayTreeInfo *) image->profiles);
225
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
229
% G e t I m a g e P r o f i l e %
233
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
235
% GetImageProfile() gets a profile associated with an image by name.
237
% The format of the GetImageProfile method is:
239
% const StringInfo *GetImageProfile(const Image *image,const char *name)
241
% A description of each parameter follows:
243
% o image: the image.
245
% o name: the profile name.
248
MagickExport const StringInfo *GetImageProfile(const Image *image,
257
assert(image != (Image *) NULL);
258
assert(image->signature == MagickSignature);
259
if (image->debug != MagickFalse)
260
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
261
if (image->profiles == (SplayTreeInfo *) NULL)
262
return((StringInfo *) NULL);
263
(void) CopyMagickString(key,name,MaxTextExtent);
264
profile=(const StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
265
image->profiles,key);
270
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
274
% G e t N e x t I m a g e P r o f i l e %
278
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
280
% GetNextImageProfile() gets the next profile name for an image.
282
% The format of the GetNextImageProfile method is:
284
% char *GetNextImageProfile(const Image *image)
286
% A description of each parameter follows:
288
% o hash_info: the hash info.
291
MagickExport char *GetNextImageProfile(const Image *image)
293
assert(image != (Image *) NULL);
294
assert(image->signature == MagickSignature);
295
if (image->debug != MagickFalse)
296
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
297
if (image->profiles == (SplayTreeInfo *) NULL)
298
return((char *) NULL);
299
return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->profiles));
303
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
307
% P r o f i l e I m a g e %
311
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
313
% ProfileImage() associates, applies, or removes an ICM, IPTC, or generic
314
% profile with / to / from an image. If the profile is NULL, it is removed
315
% from the image otherwise added or applied. Use a name of '*' and a profile
316
% of NULL to remove all profiles from the image.
318
% ICC and ICM profiles are handled as follows: If the image does not have
319
% an associated color profile, the one you provide is associated with the
320
% image and the image pixels are not transformed. Otherwise, the colorspace
321
% transform defined by the existing and new profile are applied to the image
322
% pixels and the new profile is associated with the image.
324
% The format of the ProfileImage method is:
326
% MagickBooleanType ProfileImage(Image *image,const char *name,
327
% const void *datum,const size_t length,const MagickBooleanType clone)
329
% A description of each parameter follows:
331
% o image: the image.
333
% o name: Name of profile to add or remove: ICC, IPTC, or generic profile.
335
% o datum: the profile data.
337
% o length: the length of the profile.
339
% o clone: should be MagickFalse.
343
#if defined(MAGICKCORE_LCMS_DELEGATE)
345
static unsigned short **DestroyPixelThreadSet(unsigned short **pixels)
350
assert(pixels != (unsigned short **) NULL);
351
for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
352
if (pixels[i] != (unsigned short *) NULL)
353
pixels[i]=(unsigned short *) RelinquishMagickMemory(pixels[i]);
354
pixels=(unsigned short **) RelinquishMagickMemory(pixels);
358
static unsigned short **AcquirePixelThreadSet(const size_t columns,
359
const size_t channels)
370
number_threads=GetOpenMPMaximumThreads();
371
pixels=(unsigned short **) AcquireQuantumMemory(number_threads,
373
if (pixels == (unsigned short **) NULL)
374
return((unsigned short **) NULL);
375
(void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
376
for (i=0; i < (ssize_t) number_threads; i++)
378
pixels[i]=(unsigned short *) AcquireQuantumMemory(columns,channels*
380
if (pixels[i] == (unsigned short *) NULL)
381
return(DestroyPixelThreadSet(pixels));
386
static cmsHTRANSFORM *DestroyTransformThreadSet(cmsHTRANSFORM *transform)
391
assert(transform != (cmsHTRANSFORM *) NULL);
392
for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
393
if (transform[i] != (cmsHTRANSFORM) NULL)
394
cmsDeleteTransform(transform[i]);
395
transform=(cmsHTRANSFORM *) RelinquishMagickMemory(transform);
399
static cmsHTRANSFORM *AcquireTransformThreadSet(Image *image,
400
const cmsHPROFILE source_profile,const cmsUInt32Number source_type,
401
const cmsHPROFILE target_profile,const cmsUInt32Number target_type,
402
const int intent,const cmsUInt32Number flags)
413
number_threads=GetOpenMPMaximumThreads();
414
transform=(cmsHTRANSFORM *) AcquireQuantumMemory(number_threads,
416
if (transform == (cmsHTRANSFORM *) NULL)
417
return((cmsHTRANSFORM *) NULL);
418
(void) ResetMagickMemory(transform,0,number_threads*sizeof(*transform));
419
for (i=0; i < (ssize_t) number_threads; i++)
421
transform[i]=cmsCreateTransformTHR(image,source_profile,source_type,
422
target_profile,target_type,intent,flags);
423
if (transform[i] == (cmsHTRANSFORM) NULL)
424
return(DestroyTransformThreadSet(transform));
430
#if defined(MAGICKCORE_LCMS_DELEGATE)
431
#if defined(LCMS_VERSION) && (LCMS_VERSION >= 2000)
432
static void LCMSExceptionHandler(cmsContext context,cmsUInt32Number severity,
438
(void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%u, %s",
439
severity,message != (char *) NULL ? message : "no message");
440
image=(Image *) context;
441
if (image != (Image *) NULL)
442
(void) ThrowMagickException(&image->exception,GetMagickModule(),
443
ImageWarning,"UnableToTransformColorspace","`%s'",image->filename);
447
static int LCMSExceptionHandler(int severity,const char *message)
449
(void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%d, %s",
450
severity,message != (char *) NULL ? message : "no message");
456
MagickExport MagickBooleanType ProfileImage(Image *image,const char *name,
457
const void *datum,const size_t length,
458
const MagickBooleanType magick_unused(clone))
460
#define ProfileImageTag "Profile/Image"
461
#define ThrowProfileException(severity,tag,context) \
463
if (source_profile != (cmsHPROFILE) NULL) \
464
(void) cmsCloseProfile(source_profile); \
465
if (target_profile != (cmsHPROFILE) NULL) \
466
(void) cmsCloseProfile(target_profile); \
467
ThrowBinaryException(severity,tag,context); \
476
assert(image != (Image *) NULL);
477
assert(image->signature == MagickSignature);
478
if (image->debug != MagickFalse)
479
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
480
assert(name != (const char *) NULL);
481
if ((datum == (const void *) NULL) || (length == 0))
494
Delete image profile(s).
496
names=ConstantString(name);
497
(void) SubstituteString(&names,","," ");
498
arguments=StringToArgv(names,&number_arguments);
499
names=DestroyString(names);
500
if (arguments == (char **) NULL)
502
ResetImageProfileIterator(image);
503
for (name=GetNextImageProfile(image); name != (const char *) NULL; )
505
for (i=1; i < (ssize_t) number_arguments; i++)
507
if ((*arguments[i] == '!') &&
508
(LocaleCompare(name,arguments[i]+1) == 0))
510
if (GlobExpression(name,arguments[i],MagickTrue) != MagickFalse)
512
(void) DeleteImageProfile(image,name);
513
ResetImageProfileIterator(image);
517
name=GetNextImageProfile(image);
519
for (i=0; i < (ssize_t) number_arguments; i++)
520
arguments[i]=DestroyString(arguments[i]);
521
arguments=(char **) RelinquishMagickMemory(arguments);
525
Add a ICC, IPTC, or generic profile to the image.
528
profile=AcquireStringInfo((size_t) length);
529
SetStringInfoDatum(profile,(unsigned char *) datum);
530
if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
531
status=SetImageProfile(image,name,profile);
537
icc_profile=GetImageProfile(image,"icc");
538
if ((icc_profile != (const StringInfo *) NULL) &&
539
(CompareStringInfo(icc_profile,profile) == 0))
544
value=GetImageProperty(image,"exif:ColorSpace");
547
if (LocaleCompare(value,"1") != 0)
548
(void) SetsRGBImageProfile(image);
549
value=GetImageProperty(image,"exif:InteroperabilityIndex");
550
if (LocaleCompare(value,"R98.") != 0)
551
(void) SetsRGBImageProfile(image);
552
value=GetImageProperty(image,"exif:InteroperabilityIndex");
553
if (LocaleCompare(value,"R03.") != 0)
554
(void) SetAdobeRGB1998ImageProfile(image);
556
icc_profile=GetImageProfile(image,"icc");
558
if ((icc_profile != (const StringInfo *) NULL) &&
559
(CompareStringInfo(icc_profile,profile) == 0))
561
profile=DestroyStringInfo(profile);
564
#if !defined(MAGICKCORE_LCMS_DELEGATE)
565
(void) ThrowMagickException(&image->exception,GetMagickModule(),
566
MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (LCMS)",
574
Transform pixel colors as defined by the color profiles.
576
cmsSetLogErrorHandler(LCMSExceptionHandler);
577
source_profile=cmsOpenProfileFromMemTHR(image,
578
GetStringInfoDatum(profile),(cmsUInt32Number)
579
GetStringInfoLength(profile));
580
if (source_profile == (cmsHPROFILE) NULL)
581
ThrowBinaryException(ResourceLimitError,
582
"ColorspaceColorProfileMismatch",name);
583
if ((cmsGetDeviceClass(source_profile) != cmsSigLinkClass) &&
584
(icc_profile == (StringInfo *) NULL))
585
status=SetImageProfile(image,name,profile);
595
cmsColorSpaceSignature
629
**restrict source_pixels,
630
**restrict target_pixels;
632
exception=(&image->exception);
633
target_profile=(cmsHPROFILE) NULL;
634
if (icc_profile != (StringInfo *) NULL)
636
target_profile=source_profile;
637
source_profile=cmsOpenProfileFromMemTHR(image,
638
GetStringInfoDatum(icc_profile),(cmsUInt32Number)
639
GetStringInfoLength(icc_profile));
640
if (source_profile == (cmsHPROFILE) NULL)
641
ThrowProfileException(ResourceLimitError,
642
"ColorspaceColorProfileMismatch",name);
644
switch (cmsGetColorSpace(source_profile))
648
source_colorspace=CMYKColorspace;
649
source_type=(cmsUInt32Number) TYPE_CMYK_16;
655
source_colorspace=GRAYColorspace;
656
source_type=(cmsUInt32Number) TYPE_GRAY_16;
662
source_colorspace=LabColorspace;
663
source_type=(cmsUInt32Number) TYPE_Lab_16;
669
source_colorspace=YUVColorspace;
670
source_type=(cmsUInt32Number) TYPE_YUV_16;
676
source_colorspace=sRGBColorspace;
677
source_type=(cmsUInt32Number) TYPE_RGB_16;
683
source_colorspace=XYZColorspace;
684
source_type=(cmsUInt32Number) TYPE_XYZ_16;
688
case cmsSigYCbCrData:
690
source_colorspace=YCbCrColorspace;
691
source_type=(cmsUInt32Number) TYPE_YCbCr_16;
697
source_colorspace=UndefinedColorspace;
698
source_type=(cmsUInt32Number) TYPE_RGB_16;
703
signature=cmsGetPCS(source_profile);
704
if (target_profile != (cmsHPROFILE) NULL)
705
signature=cmsGetColorSpace(target_profile);
710
target_colorspace=CMYKColorspace;
711
target_type=(cmsUInt32Number) TYPE_CMYK_16;
717
target_colorspace=LabColorspace;
718
target_type=(cmsUInt32Number) TYPE_Lab_16;
724
target_colorspace=GRAYColorspace;
725
target_type=(cmsUInt32Number) TYPE_GRAY_16;
731
target_colorspace=YUVColorspace;
732
target_type=(cmsUInt32Number) TYPE_YUV_16;
738
target_colorspace=sRGBColorspace;
739
target_type=(cmsUInt32Number) TYPE_RGB_16;
745
target_colorspace=XYZColorspace;
746
target_type=(cmsUInt32Number) TYPE_XYZ_16;
750
case cmsSigYCbCrData:
752
target_colorspace=YCbCrColorspace;
753
target_type=(cmsUInt32Number) TYPE_YCbCr_16;
759
target_colorspace=UndefinedColorspace;
760
target_type=(cmsUInt32Number) TYPE_RGB_16;
765
if ((source_colorspace == UndefinedColorspace) ||
766
(target_colorspace == UndefinedColorspace))
767
ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
769
if ((source_colorspace == GRAYColorspace) &&
770
(IsGrayImage(image,exception) == MagickFalse))
771
ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
773
if ((source_colorspace == CMYKColorspace) &&
774
(image->colorspace != CMYKColorspace))
775
ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
777
if ((source_colorspace == XYZColorspace) &&
778
(image->colorspace != XYZColorspace))
779
ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
781
if ((source_colorspace == YCbCrColorspace) &&
782
(image->colorspace != YCbCrColorspace))
783
ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
785
if ((source_colorspace != CMYKColorspace) &&
786
(source_colorspace != GRAYColorspace) &&
787
(source_colorspace != LabColorspace) &&
788
(source_colorspace != XYZColorspace) &&
789
(source_colorspace != YCbCrColorspace) &&
790
(IssRGBCompatibleColorspace(image->colorspace) == MagickFalse))
791
ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
793
switch (image->rendering_intent)
795
case AbsoluteIntent: intent=INTENT_ABSOLUTE_COLORIMETRIC; break;
796
case PerceptualIntent: intent=INTENT_PERCEPTUAL; break;
797
case RelativeIntent: intent=INTENT_RELATIVE_COLORIMETRIC; break;
798
case SaturationIntent: intent=INTENT_SATURATION; break;
799
default: intent=INTENT_PERCEPTUAL; break;
801
flags=cmsFLAGS_HIGHRESPRECALC;
802
#if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
803
if (image->black_point_compensation != MagickFalse)
804
flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
806
transform=AcquireTransformThreadSet(image,source_profile,
807
source_type,target_profile,target_type,intent,flags);
808
if (transform == (cmsHTRANSFORM *) NULL)
809
ThrowProfileException(ImageError,"UnableToCreateColorTransform",
812
Transform image as dictated by the source & target image profiles.
814
source_pixels=AcquirePixelThreadSet(image->columns,source_channels);
815
target_pixels=AcquirePixelThreadSet(image->columns,target_channels);
816
if ((source_pixels == (unsigned short **) NULL) ||
817
(target_pixels == (unsigned short **) NULL))
819
transform=DestroyTransformThreadSet(transform);
820
ThrowProfileException(ResourceLimitError,
821
"MemoryAllocationFailed",image->filename);
823
if (SetImageStorageClass(image,DirectClass) == MagickFalse)
825
target_pixels=DestroyPixelThreadSet(target_pixels);
826
source_pixels=DestroyPixelThreadSet(source_pixels);
827
transform=DestroyTransformThreadSet(transform);
828
if (source_profile != (cmsHPROFILE) NULL)
829
(void) cmsCloseProfile(source_profile);
830
if (target_profile != (cmsHPROFILE) NULL)
831
(void) cmsCloseProfile(target_profile);
834
if (target_colorspace == CMYKColorspace)
835
(void) SetImageColorspace(image,target_colorspace);
838
image_view=AcquireAuthenticCacheView(image,exception);
839
#if defined(MAGICKCORE_OPENMP_SUPPORT)
840
#pragma omp parallel for schedule(static,4) shared(status) \
841
dynamic_number_threads(image,image->columns,image->rows,1)
843
for (y=0; y < (ssize_t) image->rows; y++)
846
id = GetOpenMPThreadId();
860
register unsigned short
863
if (status == MagickFalse)
865
q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
867
if (q == (PixelPacket *) NULL)
872
indexes=GetCacheViewAuthenticIndexQueue(image_view);
874
for (x=0; x < (ssize_t) image->columns; x++)
876
*p++=ScaleQuantumToShort(GetPixelRed(q));
877
if (source_channels > 1)
879
*p++=ScaleQuantumToShort(GetPixelGreen(q));
880
*p++=ScaleQuantumToShort(GetPixelBlue(q));
882
if (source_channels > 3)
883
*p++=ScaleQuantumToShort(GetPixelIndex(indexes+x));
886
cmsDoTransform(transform[id],source_pixels[id],target_pixels[id],
887
(unsigned int) image->columns);
890
for (x=0; x < (ssize_t) image->columns; x++)
892
SetPixelRed(q,ScaleShortToQuantum(*p));
893
SetPixelGreen(q,GetPixelRed(q));
894
SetPixelBlue(q,GetPixelRed(q));
896
if (target_channels > 1)
898
SetPixelGreen(q,ScaleShortToQuantum(*p));
900
SetPixelBlue(q,ScaleShortToQuantum(*p));
903
if (target_channels > 3)
905
SetPixelIndex(indexes+x,ScaleShortToQuantum(*p));
910
sync=SyncCacheViewAuthenticPixels(image_view,exception);
911
if (sync == MagickFalse)
913
if (image->progress_monitor != (MagickProgressMonitor) NULL)
918
#if defined(MAGICKCORE_OPENMP_SUPPORT)
919
#pragma omp critical (MagickCore_ProfileImage)
921
proceed=SetImageProgress(image,ProfileImageTag,progress++,
923
if (proceed == MagickFalse)
927
image_view=DestroyCacheView(image_view);
928
(void) SetImageColorspace(image,target_colorspace);
933
image->type=image->matte == MagickFalse ? TrueColorType :
939
image->type=image->matte == MagickFalse ? ColorSeparationType :
940
ColorSeparationMatteType;
945
image->type=image->matte == MagickFalse ? GrayscaleType :
952
target_pixels=DestroyPixelThreadSet(target_pixels);
953
source_pixels=DestroyPixelThreadSet(source_pixels);
954
transform=DestroyTransformThreadSet(transform);
955
if (cmsGetDeviceClass(source_profile) != cmsSigLinkClass)
956
status=SetImageProfile(image,name,profile);
957
if (target_profile != (cmsHPROFILE) NULL)
958
(void) cmsCloseProfile(target_profile);
960
(void) cmsCloseProfile(source_profile);
964
profile=DestroyStringInfo(profile);
969
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
973
% R e m o v e I m a g e P r o f i l e %
977
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
979
% RemoveImageProfile() removes a named profile from the image and returns its
982
% The format of the RemoveImageProfile method is:
984
% void *RemoveImageProfile(Image *image,const char *name)
986
% A description of each parameter follows:
988
% o image: the image.
990
% o name: the profile name.
993
MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
998
assert(image != (Image *) NULL);
999
assert(image->signature == MagickSignature);
1000
if (image->debug != MagickFalse)
1001
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1002
if (image->profiles == (SplayTreeInfo *) NULL)
1003
return((StringInfo *) NULL);
1004
if (LocaleCompare(name,"icc") == 0)
1007
Continue to support deprecated color profile for now.
1009
image->color_profile.length=0;
1010
image->color_profile.info=(unsigned char *) NULL;
1012
if (LocaleCompare(name,"iptc") == 0)
1015
Continue to support deprecated IPTC profile for now.
1017
image->iptc_profile.length=0;
1018
image->iptc_profile.info=(unsigned char *) NULL;
1020
profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
1021
image->profiles,name);
1026
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1030
% R e s e t P r o f i l e I t e r a t o r %
1034
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1036
% ResetImageProfileIterator() resets the image profile iterator. Use it in
1037
% conjunction with GetNextImageProfile() to iterate over all the profiles
1038
% associated with an image.
1040
% The format of the ResetImageProfileIterator method is:
1042
% ResetImageProfileIterator(Image *image)
1044
% A description of each parameter follows:
1046
% o image: the image.
1049
MagickExport void ResetImageProfileIterator(const Image *image)
1051
assert(image != (Image *) NULL);
1052
assert(image->signature == MagickSignature);
1053
if (image->debug != MagickFalse)
1054
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1055
if (image->profiles == (SplayTreeInfo *) NULL)
1057
ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
1061
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1065
% S e t I m a g e P r o f i l e %
1069
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1071
% SetImageProfile() adds a named profile to the image. If a profile with the
1072
% same name already exists, it is replaced. This method differs from the
1073
% ProfileImage() method in that it does not apply CMS color profiles.
1075
% The format of the SetImageProfile method is:
1077
% MagickBooleanType SetImageProfile(Image *image,const char *name,
1078
% const StringInfo *profile)
1080
% A description of each parameter follows:
1082
% o image: the image.
1084
% o name: the profile name, for example icc, exif, and 8bim (8bim is the
1085
% Photoshop wrapper for iptc profiles).
1087
% o profile: A StringInfo structure that contains the named profile.
1091
static void *DestroyProfile(void *profile)
1093
return((void *) DestroyStringInfo((StringInfo *) profile));
1096
static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1097
unsigned char *quantum)
1103
static inline const unsigned char *ReadResourceBytes(const unsigned char *p,
1104
const ssize_t count,unsigned char *quantum)
1109
for (i=0; i < count; i++)
1114
static inline const unsigned char *ReadResourceLong(const unsigned char *p,
1117
*quantum=(size_t) (*p++ << 24);
1118
*quantum|=(size_t) (*p++ << 16);
1119
*quantum|=(size_t) (*p++ << 8);
1120
*quantum|=(size_t) (*p++ << 0);
1124
static inline const unsigned char *ReadResourceShort(const unsigned char *p,
1125
unsigned short *quantum)
1127
*quantum=(unsigned short) (*p++ << 8);
1128
*quantum|=(unsigned short) (*p++ << 0);
1132
static MagickBooleanType GetProfilesFromResourceBlock(Image *image,
1133
const StringInfo *resource_block)
1138
register const unsigned char
1156
datum=GetStringInfoDatum(resource_block);
1157
length=GetStringInfoLength(resource_block);
1158
for (p=datum; p < (datum+length-16); )
1160
if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1163
p=ReadResourceShort(p,&id);
1164
p=ReadResourceByte(p,&length_byte);
1166
if (((length_byte+1) & 0x01) != 0)
1168
if (p > (datum+length-4))
1170
p=ReadResourceLong(p,&count);
1171
if ((p > (datum+length-count)) || (count > length))
1183
p=ReadResourceShort(p,&resolution)+6;
1184
image->x_resolution=(double) resolution;
1185
p=ReadResourceShort(p,&resolution)+6;
1186
image->y_resolution=(double) resolution;
1194
profile=AcquireStringInfo(count);
1195
SetStringInfoDatum(profile,p);
1196
(void) SetImageProfile(image,"iptc",profile);
1197
profile=DestroyStringInfo(profile);
1214
profile=AcquireStringInfo(count);
1215
SetStringInfoDatum(profile,p);
1216
(void) SetImageProfile(image,"icc",profile);
1217
profile=DestroyStringInfo(profile);
1226
profile=AcquireStringInfo(count);
1227
SetStringInfoDatum(profile,p);
1228
(void) SetImageProfile(image,"exif",profile);
1229
profile=DestroyStringInfo(profile);
1238
profile=AcquireStringInfo(count);
1239
SetStringInfoDatum(profile,p);
1240
(void) SetImageProfile(image,"xmp",profile);
1241
profile=DestroyStringInfo(profile);
1251
if ((count & 0x01) != 0)
1257
MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
1258
const StringInfo *profile)
1262
property[MaxTextExtent];
1267
assert(image != (Image *) NULL);
1268
assert(image->signature == MagickSignature);
1269
if (image->debug != MagickFalse)
1270
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1271
if (image->profiles == (SplayTreeInfo *) NULL)
1272
image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
1274
(void) CopyMagickString(key,name,MaxTextExtent);
1275
status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1276
ConstantString(key),CloneStringInfo(profile));
1277
if ((status != MagickFalse) &&
1278
((LocaleCompare(name,"icc") == 0) || (LocaleCompare(name,"icm") == 0)))
1284
Continue to support deprecated color profile member.
1286
icc_profile=GetImageProfile(image,name);
1287
if (icc_profile != (const StringInfo *) NULL)
1289
image->color_profile.length=GetStringInfoLength(icc_profile);
1290
image->color_profile.info=GetStringInfoDatum(icc_profile);
1293
if ((status != MagickFalse) &&
1294
((LocaleCompare(name,"iptc") == 0) || (LocaleCompare(name,"8bim") == 0)))
1300
Continue to support deprecated IPTC profile member.
1302
iptc_profile=GetImageProfile(image,name);
1303
if (iptc_profile != (const StringInfo *) NULL)
1305
image->iptc_profile.length=GetStringInfoLength(iptc_profile);
1306
image->iptc_profile.info=GetStringInfoDatum(iptc_profile);
1308
(void) GetProfilesFromResourceBlock(image,profile);
1311
Inject profile into image properties.
1313
(void) FormatLocaleString(property,MaxTextExtent,"%s:sans",name);
1314
(void) GetImageProperty(image,property);
1319
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1323
% S y n c I m a g e P r o f i l e s %
1327
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1329
% SyncImageProfiles() synchronizes image properties with the image profiles.
1330
% Currently we only support updating the EXIF resolution and orientation.
1332
% The format of the SyncImageProfiles method is:
1334
% MagickBooleanType SyncImageProfiles(Image *image)
1336
% A description of each parameter follows:
1338
% o image: the image.
1342
static inline int ReadProfileByte(unsigned char **p,size_t *length)
1354
static inline unsigned short ReadProfileShort(const EndianType endian,
1355
unsigned char *buffer)
1360
if (endian == MSBEndian)
1362
value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
1363
((unsigned char *) buffer)[1]);
1364
return((unsigned short) (value & 0xffff));
1366
value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
1367
return((unsigned short) (value & 0xffff));
1370
static inline size_t ReadProfileLong(const EndianType endian,
1371
unsigned char *buffer)
1376
if (endian == MSBEndian)
1378
value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
1379
(buffer[2] << 8) | buffer[3]);
1380
return((size_t) (value & 0xffffffff));
1382
value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
1383
(buffer[1] << 8 ) | (buffer[0]));
1384
return((size_t) (value & 0xffffffff));
1387
static inline void WriteProfileLong(const EndianType endian,
1388
const size_t value,unsigned char *p)
1393
if (endian == MSBEndian)
1395
buffer[0]=(unsigned char) (value >> 24);
1396
buffer[1]=(unsigned char) (value >> 16);
1397
buffer[2]=(unsigned char) (value >> 8);
1398
buffer[3]=(unsigned char) value;
1399
(void) CopyMagickMemory(p,buffer,4);
1402
buffer[0]=(unsigned char) value;
1403
buffer[1]=(unsigned char) (value >> 8);
1404
buffer[2]=(unsigned char) (value >> 16);
1405
buffer[3]=(unsigned char) (value >> 24);
1406
(void) CopyMagickMemory(p,buffer,4);
1409
static void WriteProfileShort(const EndianType endian,
1410
const unsigned short value,unsigned char *p)
1415
if (endian == MSBEndian)
1417
buffer[0]=(unsigned char) (value >> 8);
1418
buffer[1]=(unsigned char) value;
1419
(void) CopyMagickMemory(p,buffer,2);
1422
buffer[0]=(unsigned char) value;
1423
buffer[1]=(unsigned char) (value >> 8);
1424
(void) CopyMagickMemory(p,buffer,2);
1427
MagickExport MagickBooleanType SyncImageProfiles(Image *image)
1429
#define MaxDirectoryStack 16
1430
#define EXIF_DELIMITER "\n"
1431
#define EXIF_NUM_FORMATS 12
1432
#define TAG_EXIF_OFFSET 0x8769
1433
#define TAG_INTEROP_OFFSET 0xa005
1435
typedef struct _DirectoryInfo
1445
directory_stack[MaxDirectoryStack];
1464
format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1474
Set EXIF resolution tag.
1476
profile=(StringInfo *) GetImageProfile(image,"EXIF");
1477
if (profile == (StringInfo *) NULL)
1479
length=GetStringInfoLength(profile);
1480
exif=GetStringInfoDatum(profile);
1483
if (ReadProfileByte(&exif,&length) != 0x45)
1485
if (ReadProfileByte(&exif,&length) != 0x78)
1487
if (ReadProfileByte(&exif,&length) != 0x69)
1489
if (ReadProfileByte(&exif,&length) != 0x66)
1491
if (ReadProfileByte(&exif,&length) != 0x00)
1493
if (ReadProfileByte(&exif,&length) != 0x00)
1498
return(MagickFalse);
1499
id=(ssize_t) ReadProfileShort(LSBEndian,exif);
1507
return(MagickFalse);
1508
if (ReadProfileShort(endian,exif+2) != 0x002a)
1509
return(MagickFalse);
1511
This the offset to the first IFD.
1513
offset=(ssize_t) ((int) ReadProfileLong(endian,exif+4));
1514
if ((offset < 0) || ((size_t) offset >= length))
1515
return(MagickFalse);
1516
directory=exif+offset;
1519
exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
1520
(void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
1526
directory=directory_stack[level].directory;
1527
entry=directory_stack[level].entry;
1530
Determine how many entries there are in the current IFD.
1532
number_entries=ReadProfileShort(endian,directory);
1533
for ( ; entry < number_entries; entry++)
1535
register unsigned char
1547
q=(unsigned char *) (directory+2+(12*entry));
1548
if (GetValueFromSplayTree(exif_resources,q) == q)
1550
(void) AddValueToSplayTree(exif_resources,q,q);
1551
tag_value=(ssize_t) ReadProfileShort(endian,q);
1552
format=(ssize_t) ReadProfileShort(endian,q+2);
1553
if ((format-1) >= EXIF_NUM_FORMATS)
1555
components=(ssize_t) ((int) ReadProfileLong(endian,q+4));
1556
number_bytes=(size_t) components*format_bytes[format];
1557
if ((ssize_t) number_bytes < components)
1558
break; /* prevent overflow */
1559
if (number_bytes <= 4)
1567
The directory entry contains an offset.
1569
offset=(ssize_t) ((int) ReadProfileLong(endian,q+8));
1570
if ((ssize_t) (offset+number_bytes) < offset)
1571
continue; /* prevent overflow */
1572
if ((size_t) (offset+number_bytes) > length)
1574
p=(unsigned char *) (exif+offset);
1580
(void) WriteProfileLong(endian,(size_t) (image->x_resolution+0.5),p);
1581
(void) WriteProfileLong(endian,1UL,p+4);
1586
(void) WriteProfileLong(endian,(size_t) (image->y_resolution+0.5),p);
1587
(void) WriteProfileLong(endian,1UL,p+4);
1592
if (number_bytes == 4)
1594
(void) WriteProfileLong(endian,(size_t) image->orientation,p);
1597
(void) WriteProfileShort(endian,(unsigned short) image->orientation,
1603
if (number_bytes == 4)
1605
(void) WriteProfileLong(endian,(size_t) (image->units+1),p);
1608
(void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
1614
if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
1619
offset=(ssize_t) ((int) ReadProfileLong(endian,p));
1620
if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
1622
directory_stack[level].directory=directory;
1624
directory_stack[level].entry=entry;
1626
directory_stack[level].directory=exif+offset;
1627
directory_stack[level].entry=0;
1629
if ((directory+2+(12*number_entries)) > (exif+length))
1631
offset=(ssize_t) ((int) ReadProfileLong(endian,directory+2+(12*
1633
if ((offset != 0) && ((size_t) offset < length) &&
1634
(level < (MaxDirectoryStack-2)))
1636
directory_stack[level].directory=exif+offset;
1637
directory_stack[level].entry=0;
1644
} while (level > 0);
1645
exif_resources=DestroySplayTree(exif_resources);