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-2013 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 "MagickCore/studio.h"
43 #include "MagickCore/attribute.h"
44 #include "MagickCore/cache.h"
45 #include "MagickCore/color.h"
46 #include "MagickCore/colorspace-private.h"
47 #include "MagickCore/configure.h"
48 #include "MagickCore/exception.h"
49 #include "MagickCore/exception-private.h"
50 #include "MagickCore/hashmap.h"
51 #include "MagickCore/image.h"
52 #include "MagickCore/memory_.h"
53 #include "MagickCore/monitor.h"
54 #include "MagickCore/monitor-private.h"
55 #include "MagickCore/option.h"
56 #include "MagickCore/pixel-accessor.h"
57 #include "MagickCore/profile.h"
58 #include "MagickCore/profile-private.h"
59 #include "MagickCore/property.h"
60 #include "MagickCore/quantum.h"
61 #include "MagickCore/quantum-private.h"
62 #include "MagickCore/resource_.h"
63 #include "MagickCore/splay-tree.h"
64 #include "MagickCore/string_.h"
65 #include "MagickCore/thread-private.h"
66 #include "MagickCore/token.h"
67 #include "MagickCore/utility.h"
68 #if defined(MAGICKCORE_LCMS_DELEGATE)
69 #if defined(MAGICKCORE_HAVE_LCMS_LCMS2_H)
71 #include <lcms/lcms2.h>
72 #elif defined(MAGICKCORE_HAVE_LCMS2_H)
75 #elif defined(MAGICKCORE_HAVE_LCMS_LCMS_H)
76 #include <lcms/lcms.h>
85 #if !defined(LCMS_VERSION) || (LCMS_VERSION < 2000)
86 #define cmsSigCmykData icSigCmykData
87 #define cmsSigGrayData icSigGrayData
88 #define cmsSigLabData icSigLabData
89 #define cmsSigLuvData icSigLuvData
90 #define cmsSigRgbData icSigRgbData
91 #define cmsSigXYZData icSigXYZData
92 #define cmsSigYCbCrData icSigYCbCrData
93 #define cmsSigLinkClass icSigLinkClass
94 #define cmsColorSpaceSignature icColorSpaceSignature
95 #define cmsUInt32Number DWORD
96 #define cmsSetLogErrorHandler(handler) cmsSetErrorHandler(handler)
97 #define cmsCreateTransformTHR(context,source_profile,source_type, \
98 target_profile,target_type,intent,flags) cmsCreateTransform(source_profile, \
99 source_type,target_profile,target_type,intent,flags);
100 #define cmsOpenProfileFromMemTHR(context,profile,length) \
101 cmsOpenProfileFromMem(profile,length)
122 typedef struct _CMSExceptionInfo
132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
136 % C l o n e I m a g e P r o f i l e s %
140 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
142 % CloneImageProfiles() clones one or more image profiles.
144 % The format of the CloneImageProfiles method is:
146 % MagickBooleanType CloneImageProfiles(Image *image,
147 % const Image *clone_image)
149 % A description of each parameter follows:
151 % o image: the image.
153 % o clone_image: the clone image.
156 MagickExport MagickBooleanType CloneImageProfiles(Image *image,
157 const Image *clone_image)
159 assert(image != (Image *) NULL);
160 assert(image->signature == MagickSignature);
161 if (image->debug != MagickFalse)
162 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
163 assert(clone_image != (const Image *) NULL);
164 assert(clone_image->signature == MagickSignature);
165 if (clone_image->profiles != (void *) NULL)
166 image->profiles=CloneSplayTree((SplayTreeInfo *) clone_image->profiles,
167 (void *(*)(void *)) ConstantString,(void *(*)(void *)) CloneStringInfo);
172 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
176 % D e l e t e I m a g e P r o f i l e %
180 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
182 % DeleteImageProfile() deletes a profile from the image by its name.
184 % The format of the DeleteImageProfile method is:
186 % MagickBooleanTyupe DeleteImageProfile(Image *image,const char *name)
188 % A description of each parameter follows:
190 % o image: the image.
192 % o name: the profile name.
195 MagickExport MagickBooleanType DeleteImageProfile(Image *image,const char *name)
197 assert(image != (Image *) NULL);
198 assert(image->signature == MagickSignature);
199 if (image->debug != MagickFalse)
200 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
201 if (image->profiles == (SplayTreeInfo *) NULL)
203 return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->profiles,name));
207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
211 % D e s t r o y I m a g e P r o f i l e s %
215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
217 % DestroyImageProfiles() releases memory associated with an image profile map.
219 % The format of the DestroyProfiles method is:
221 % void DestroyImageProfiles(Image *image)
223 % A description of each parameter follows:
225 % o image: the image.
228 MagickExport void DestroyImageProfiles(Image *image)
230 if (image->profiles != (SplayTreeInfo *) NULL)
231 image->profiles=DestroySplayTree((SplayTreeInfo *) image->profiles);
235 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
239 % G e t I m a g e P r o f i l e %
243 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
245 % GetImageProfile() gets a profile associated with an image by name.
247 % The format of the GetImageProfile method is:
249 % const StringInfo *GetImageProfile(const Image *image,const char *name)
251 % A description of each parameter follows:
253 % o image: the image.
255 % o name: the profile name.
258 MagickExport const StringInfo *GetImageProfile(const Image *image,
267 assert(image != (Image *) NULL);
268 assert(image->signature == MagickSignature);
269 if (image->debug != MagickFalse)
270 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
271 if (image->profiles == (SplayTreeInfo *) NULL)
272 return((StringInfo *) NULL);
273 (void) CopyMagickString(key,name,MaxTextExtent);
274 profile=(const StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
275 image->profiles,key);
280 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
284 % G e t N e x t I m a g e P r o f i l e %
288 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
290 % GetNextImageProfile() gets the next profile name for an image.
292 % The format of the GetNextImageProfile method is:
294 % char *GetNextImageProfile(const Image *image)
296 % A description of each parameter follows:
298 % o hash_info: the hash info.
301 MagickExport char *GetNextImageProfile(const Image *image)
303 assert(image != (Image *) NULL);
304 assert(image->signature == MagickSignature);
305 if (image->debug != MagickFalse)
306 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
307 if (image->profiles == (SplayTreeInfo *) NULL)
308 return((char *) NULL);
309 return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->profiles));
313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
317 % P r o f i l e I m a g e %
321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
323 % ProfileImage() associates, applies, or removes an ICM, IPTC, or generic
324 % profile with / to / from an image. If the profile is NULL, it is removed
325 % from the image otherwise added or applied. Use a name of '*' and a profile
326 % of NULL to remove all profiles from the image.
328 % ICC and ICM profiles are handled as follows: If the image does not have
329 % an associated color profile, the one you provide is associated with the
330 % image and the image pixels are not transformed. Otherwise, the colorspace
331 % transform defined by the existing and new profile are applied to the image
332 % pixels and the new profile is associated with the image.
334 % The format of the ProfileImage method is:
336 % MagickBooleanType ProfileImage(Image *image,const char *name,
337 % const void *datum,const size_t length,const MagickBooleanType clone)
339 % A description of each parameter follows:
341 % o image: the image.
343 % o name: Name of profile to add or remove: ICC, IPTC, or generic profile.
345 % o datum: the profile data.
347 % o length: the length of the profile.
349 % o clone: should be MagickFalse.
353 #if defined(MAGICKCORE_LCMS_DELEGATE)
355 static unsigned short **DestroyPixelThreadSet(unsigned short **pixels)
360 assert(pixels != (unsigned short **) NULL);
361 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
362 if (pixels[i] != (unsigned short *) NULL)
363 pixels[i]=(unsigned short *) RelinquishMagickMemory(pixels[i]);
364 pixels=(unsigned short **) RelinquishMagickMemory(pixels);
368 static unsigned short **AcquirePixelThreadSet(const size_t columns,
369 const size_t channels)
380 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
381 pixels=(unsigned short **) AcquireQuantumMemory(number_threads,
383 if (pixels == (unsigned short **) NULL)
384 return((unsigned short **) NULL);
385 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
386 for (i=0; i < (ssize_t) number_threads; i++)
388 pixels[i]=(unsigned short *) AcquireQuantumMemory(columns,channels*
390 if (pixels[i] == (unsigned short *) NULL)
391 return(DestroyPixelThreadSet(pixels));
396 static cmsHTRANSFORM *DestroyTransformThreadSet(cmsHTRANSFORM *transform)
401 assert(transform != (cmsHTRANSFORM *) NULL);
402 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
403 if (transform[i] != (cmsHTRANSFORM) NULL)
404 cmsDeleteTransform(transform[i]);
405 transform=(cmsHTRANSFORM *) RelinquishMagickMemory(transform);
409 static cmsHTRANSFORM *AcquireTransformThreadSet(Image *image,
410 const cmsHPROFILE source_profile,const cmsUInt32Number source_type,
411 const cmsHPROFILE target_profile,const cmsUInt32Number target_type,
412 const int intent,const cmsUInt32Number flags)
423 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
424 transform=(cmsHTRANSFORM *) AcquireQuantumMemory(number_threads,
426 if (transform == (cmsHTRANSFORM *) NULL)
427 return((cmsHTRANSFORM *) NULL);
428 (void) ResetMagickMemory(transform,0,number_threads*sizeof(*transform));
429 for (i=0; i < (ssize_t) number_threads; i++)
431 transform[i]=cmsCreateTransformTHR(image,source_profile,source_type,
432 target_profile,target_type,intent,flags);
433 if (transform[i] == (cmsHTRANSFORM) NULL)
434 return(DestroyTransformThreadSet(transform));
440 #if defined(MAGICKCORE_LCMS_DELEGATE)
441 #if defined(LCMS_VERSION) && (LCMS_VERSION >= 2000)
442 static void CMSExceptionHandler(cmsContext context,cmsUInt32Number severity,
454 cms_exception=(CMSExceptionInfo *) context;
455 image=cms_exception->image;
456 exception=cms_exception->exception;
457 if (image == (Image *) NULL)
459 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
460 "UnableToTransformColorspace","'%s'","unknown context");
463 if (image->debug != MagickFalse)
464 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%u, %s",
465 severity,message != (char *) NULL ? message : "no message");
466 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
467 "UnableToTransformColorspace","'%s'",image->filename);
470 static int CMSExceptionHandler(int severity,const char *message)
472 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%d, %s",
473 severity,message != (char *) NULL ? message : "no message");
479 MagickExport MagickBooleanType ProfileImage(Image *image,const char *name,
480 const void *datum,const size_t length,ExceptionInfo *exception)
482 #define ProfileImageTag "Profile/Image"
483 #define ThrowProfileException(severity,tag,context) \
485 if (source_profile != (cmsHPROFILE) NULL) \
486 (void) cmsCloseProfile(source_profile); \
487 if (target_profile != (cmsHPROFILE) NULL) \
488 (void) cmsCloseProfile(target_profile); \
489 ThrowBinaryException(severity,tag,context); \
498 assert(image != (Image *) NULL);
499 assert(image->signature == MagickSignature);
500 if (image->debug != MagickFalse)
501 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
502 assert(name != (const char *) NULL);
503 if ((datum == (const void *) NULL) || (length == 0))
516 Delete image profile(s).
518 names=ConstantString(name);
519 (void) SubstituteString(&names,","," ");
520 arguments=StringToArgv(names,&number_arguments);
521 names=DestroyString(names);
522 if (arguments == (char **) NULL)
524 ResetImageProfileIterator(image);
525 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
527 for (i=1; i < (ssize_t) number_arguments; i++)
529 if ((*arguments[i] == '!') &&
530 (LocaleCompare(name,arguments[i]+1) == 0))
532 if (GlobExpression(name,arguments[i],MagickTrue) != MagickFalse)
534 (void) DeleteImageProfile(image,name);
538 name=GetNextImageProfile(image);
540 for (i=0; i < (ssize_t) number_arguments; i++)
541 arguments[i]=DestroyString(arguments[i]);
542 arguments=(char **) RelinquishMagickMemory(arguments);
546 Add a ICC, IPTC, or generic profile to the image.
549 profile=AcquireStringInfo((size_t) length);
550 SetStringInfoDatum(profile,(unsigned char *) datum);
551 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
552 status=SetImageProfile(image,name,profile,exception);
558 icc_profile=GetImageProfile(image,"icc");
559 if ((icc_profile != (const StringInfo *) NULL) &&
560 (CompareStringInfo(icc_profile,profile) == 0))
565 value=GetImageProperty(image,"exif:ColorSpace",exception);
568 if (LocaleCompare(value,"1") != 0)
569 (void) SetsRGBImageProfile(image,exception);
570 value=GetImageProperty(image,"exif:InteroperabilityIndex",exception);
571 if (LocaleCompare(value,"R98.") != 0)
572 (void) SetsRGBImageProfile(image,exception);
573 value=GetImageProperty(image,"exif:InteroperabilityIndex",exception);
574 if (LocaleCompare(value,"R03.") != 0)
575 (void) SetAdobeRGB1998ImageProfile(image,exception);
577 icc_profile=GetImageProfile(image,"icc");
579 if ((icc_profile != (const StringInfo *) NULL) &&
580 (CompareStringInfo(icc_profile,profile) == 0))
582 profile=DestroyStringInfo(profile);
585 #if !defined(MAGICKCORE_LCMS_DELEGATE)
586 (void) ThrowMagickException(exception,GetMagickModule(),
587 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
588 "'%s' (LCMS)",image->filename);
598 Transform pixel colors as defined by the color profiles.
600 cmsSetLogErrorHandler(CMSExceptionHandler);
601 cms_exception.image=image;
602 cms_exception.exception=exception;
603 (void) cms_exception;
604 source_profile=cmsOpenProfileFromMemTHR(&cms_exception,
605 GetStringInfoDatum(profile),(cmsUInt32Number)
606 GetStringInfoLength(profile));
607 if (source_profile == (cmsHPROFILE) NULL)
608 ThrowBinaryException(ResourceLimitError,
609 "ColorspaceColorProfileMismatch",name);
610 if ((cmsGetDeviceClass(source_profile) != cmsSigLinkClass) &&
611 (icc_profile == (StringInfo *) NULL))
612 status=SetImageProfile(image,name,profile,exception);
622 cmsColorSpaceSignature
653 **restrict source_pixels,
654 **restrict target_pixels;
656 target_profile=(cmsHPROFILE) NULL;
657 if (icc_profile != (StringInfo *) NULL)
659 target_profile=source_profile;
660 source_profile=cmsOpenProfileFromMemTHR(&cms_exception,
661 GetStringInfoDatum(icc_profile),(cmsUInt32Number)
662 GetStringInfoLength(icc_profile));
663 if (source_profile == (cmsHPROFILE) NULL)
664 ThrowProfileException(ResourceLimitError,
665 "ColorspaceColorProfileMismatch",name);
667 switch (cmsGetColorSpace(source_profile))
671 source_colorspace=CMYKColorspace;
672 source_type=(cmsUInt32Number) TYPE_CMYK_16;
678 source_colorspace=GRAYColorspace;
679 source_type=(cmsUInt32Number) TYPE_GRAY_16;
685 source_colorspace=LabColorspace;
686 source_type=(cmsUInt32Number) TYPE_Lab_16;
692 source_colorspace=YUVColorspace;
693 source_type=(cmsUInt32Number) TYPE_YUV_16;
699 source_colorspace=sRGBColorspace;
700 source_type=(cmsUInt32Number) TYPE_RGB_16;
706 source_colorspace=XYZColorspace;
707 source_type=(cmsUInt32Number) TYPE_XYZ_16;
711 case cmsSigYCbCrData:
713 source_colorspace=YCbCrColorspace;
714 source_type=(cmsUInt32Number) TYPE_YCbCr_16;
720 source_colorspace=UndefinedColorspace;
721 source_type=(cmsUInt32Number) TYPE_RGB_16;
726 signature=cmsGetPCS(source_profile);
727 if (target_profile != (cmsHPROFILE) NULL)
728 signature=cmsGetColorSpace(target_profile);
733 target_colorspace=CMYKColorspace;
734 target_type=(cmsUInt32Number) TYPE_CMYK_16;
740 target_colorspace=LabColorspace;
741 target_type=(cmsUInt32Number) TYPE_Lab_16;
747 target_colorspace=GRAYColorspace;
748 target_type=(cmsUInt32Number) TYPE_GRAY_16;
754 target_colorspace=YUVColorspace;
755 target_type=(cmsUInt32Number) TYPE_YUV_16;
761 target_colorspace=sRGBColorspace;
762 target_type=(cmsUInt32Number) TYPE_RGB_16;
768 target_colorspace=XYZColorspace;
769 target_type=(cmsUInt32Number) TYPE_XYZ_16;
773 case cmsSigYCbCrData:
775 target_colorspace=YCbCrColorspace;
776 target_type=(cmsUInt32Number) TYPE_YCbCr_16;
782 target_colorspace=UndefinedColorspace;
783 target_type=(cmsUInt32Number) TYPE_RGB_16;
788 if ((source_colorspace == UndefinedColorspace) ||
789 (target_colorspace == UndefinedColorspace))
790 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
792 if ((source_colorspace == GRAYColorspace) &&
793 (IsImageGray(image,exception) == MagickFalse))
794 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
796 if ((source_colorspace == CMYKColorspace) &&
797 (image->colorspace != CMYKColorspace))
798 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
800 if ((source_colorspace == XYZColorspace) &&
801 (image->colorspace != XYZColorspace))
802 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
804 if ((source_colorspace == YCbCrColorspace) &&
805 (image->colorspace != YCbCrColorspace))
806 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
808 if ((source_colorspace != CMYKColorspace) &&
809 (source_colorspace != LabColorspace) &&
810 (source_colorspace != XYZColorspace) &&
811 (source_colorspace != YCbCrColorspace) &&
812 (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse))
813 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
815 switch (image->rendering_intent)
817 case AbsoluteIntent: intent=INTENT_ABSOLUTE_COLORIMETRIC; break;
818 case PerceptualIntent: intent=INTENT_PERCEPTUAL; break;
819 case RelativeIntent: intent=INTENT_RELATIVE_COLORIMETRIC; break;
820 case SaturationIntent: intent=INTENT_SATURATION; break;
821 default: intent=INTENT_PERCEPTUAL; break;
823 flags=cmsFLAGS_HIGHRESPRECALC;
824 #if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
825 if (image->black_point_compensation != MagickFalse)
826 flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
828 transform=AcquireTransformThreadSet(image,source_profile,
829 source_type,target_profile,target_type,intent,flags);
830 if (transform == (cmsHTRANSFORM *) NULL)
831 ThrowProfileException(ImageError,"UnableToCreateColorTransform",
834 Transform image as dictated by the source & target image profiles.
836 source_pixels=AcquirePixelThreadSet(image->columns,source_channels);
837 target_pixels=AcquirePixelThreadSet(image->columns,target_channels);
838 if ((source_pixels == (unsigned short **) NULL) ||
839 (target_pixels == (unsigned short **) NULL))
841 transform=DestroyTransformThreadSet(transform);
842 ThrowProfileException(ResourceLimitError,
843 "MemoryAllocationFailed",image->filename);
845 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
847 target_pixels=DestroyPixelThreadSet(target_pixels);
848 source_pixels=DestroyPixelThreadSet(source_pixels);
849 transform=DestroyTransformThreadSet(transform);
850 if (source_profile != (cmsHPROFILE) NULL)
851 (void) cmsCloseProfile(source_profile);
852 if (target_profile != (cmsHPROFILE) NULL)
853 (void) cmsCloseProfile(target_profile);
856 if (target_colorspace == CMYKColorspace)
857 (void) SetImageColorspace(image,target_colorspace,exception);
860 image_view=AcquireAuthenticCacheView(image,exception);
861 #if defined(MAGICKCORE_OPENMP_SUPPORT)
862 #pragma omp parallel for schedule(static,4) shared(status) \
863 dynamic_number_threads(image,image,image->rows,1)
865 for (y=0; y < (ssize_t) image->rows; y++)
868 id = GetOpenMPThreadId();
879 register unsigned short
882 if (status == MagickFalse)
884 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
886 if (q == (Quantum *) NULL)
892 for (x=0; x < (ssize_t) image->columns; x++)
894 *p++=ScaleQuantumToShort(GetPixelRed(image,q));
895 if (source_channels > 1)
897 *p++=ScaleQuantumToShort(GetPixelGreen(image,q));
898 *p++=ScaleQuantumToShort(GetPixelBlue(image,q));
900 if (source_channels > 3)
901 *p++=ScaleQuantumToShort(GetPixelBlack(image,q));
902 q+=GetPixelChannels(image);
904 cmsDoTransform(transform[id],source_pixels[id],target_pixels[id],
905 (unsigned int) image->columns);
907 q-=image->columns*GetPixelChannels(image);
908 for (x=0; x < (ssize_t) image->columns; x++)
910 if (target_channels == 1)
911 SetPixelGray(image,ScaleShortToQuantum(*p),q);
913 SetPixelRed(image,ScaleShortToQuantum(*p),q);
915 if (target_channels > 1)
917 SetPixelGreen(image,ScaleShortToQuantum(*p),q);
919 SetPixelBlue(image,ScaleShortToQuantum(*p),q);
922 if (target_channels > 3)
924 SetPixelBlack(image,ScaleShortToQuantum(*p),q);
927 q+=GetPixelChannels(image);
929 sync=SyncCacheViewAuthenticPixels(image_view,exception);
930 if (sync == MagickFalse)
932 if (image->progress_monitor != (MagickProgressMonitor) NULL)
937 #if defined(MAGICKCORE_OPENMP_SUPPORT)
938 #pragma omp critical (MagickCore_ProfileImage)
940 proceed=SetImageProgress(image,ProfileImageTag,progress++,
942 if (proceed == MagickFalse)
946 image_view=DestroyCacheView(image_view);
947 (void) SetImageColorspace(image,target_colorspace,exception);
952 image->type=image->alpha_trait != BlendPixelTrait ? TrueColorType :
958 image->type=image->alpha_trait != BlendPixelTrait ? ColorSeparationType :
959 ColorSeparationMatteType;
964 image->type=image->alpha_trait != BlendPixelTrait ? GrayscaleType :
971 target_pixels=DestroyPixelThreadSet(target_pixels);
972 source_pixels=DestroyPixelThreadSet(source_pixels);
973 transform=DestroyTransformThreadSet(transform);
974 if (cmsGetDeviceClass(source_profile) != cmsSigLinkClass)
975 status=SetImageProfile(image,name,profile,exception);
976 if (target_profile != (cmsHPROFILE) NULL)
977 (void) cmsCloseProfile(target_profile);
979 (void) cmsCloseProfile(source_profile);
983 profile=DestroyStringInfo(profile);
988 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
992 % R e m o v e I m a g e P r o f i l e %
996 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
998 % RemoveImageProfile() removes a named profile from the image and returns its
1001 % The format of the RemoveImageProfile method is:
1003 % void *RemoveImageProfile(Image *image,const char *name)
1005 % A description of each parameter follows:
1007 % o image: the image.
1009 % o name: the profile name.
1012 MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
1017 assert(image != (Image *) NULL);
1018 assert(image->signature == MagickSignature);
1019 if (image->debug != MagickFalse)
1020 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1021 if (image->profiles == (SplayTreeInfo *) NULL)
1022 return((StringInfo *) NULL);
1023 profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
1024 image->profiles,name);
1029 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1033 % R e s e t P r o f i l e I t e r a t o r %
1037 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1039 % ResetImageProfileIterator() resets the image profile iterator. Use it in
1040 % conjunction with GetNextImageProfile() to iterate over all the profiles
1041 % associated with an image.
1043 % The format of the ResetImageProfileIterator method is:
1045 % ResetImageProfileIterator(Image *image)
1047 % A description of each parameter follows:
1049 % o image: the image.
1052 MagickExport void ResetImageProfileIterator(const Image *image)
1054 assert(image != (Image *) NULL);
1055 assert(image->signature == MagickSignature);
1056 if (image->debug != MagickFalse)
1057 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1058 if (image->profiles == (SplayTreeInfo *) NULL)
1060 ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
1064 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1068 % S e t I m a g e P r o f i l e %
1072 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1074 % SetImageProfile() adds a named profile to the image. If a profile with the
1075 % same name already exists, it is replaced. This method differs from the
1076 % ProfileImage() method in that it does not apply CMS color profiles.
1078 % The format of the SetImageProfile method is:
1080 % MagickBooleanType SetImageProfile(Image *image,const char *name,
1081 % const StringInfo *profile)
1083 % A description of each parameter follows:
1085 % o image: the image.
1087 % o name: the profile name, for example icc, exif, and 8bim (8bim is the
1088 % Photoshop wrapper for iptc profiles).
1090 % o profile: A StringInfo structure that contains the named profile.
1094 static void *DestroyProfile(void *profile)
1096 return((void *) DestroyStringInfo((StringInfo *) profile));
1099 static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1100 unsigned char *quantum)
1106 static inline const unsigned char *ReadResourceBytes(const unsigned char *p,
1107 const ssize_t count,unsigned char *quantum)
1112 for (i=0; i < count; i++)
1117 static inline const unsigned char *ReadResourceLong(const unsigned char *p,
1120 *quantum=(size_t) (*p++ << 24);
1121 *quantum|=(size_t) (*p++ << 16);
1122 *quantum|=(size_t) (*p++ << 8);
1123 *quantum|=(size_t) (*p++ << 0);
1127 static inline const unsigned char *ReadResourceShort(const unsigned char *p,
1128 unsigned short *quantum)
1130 *quantum=(unsigned short) (*p++ << 8);
1131 *quantum|=(unsigned short) (*p++ << 0);
1135 static MagickBooleanType GetProfilesFromResourceBlock(Image *image,
1136 const StringInfo *resource_block,ExceptionInfo *exception)
1141 register const unsigned char
1159 datum=GetStringInfoDatum(resource_block);
1160 length=GetStringInfoLength(resource_block);
1161 for (p=datum; p < (datum+length-16); )
1163 if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1166 p=ReadResourceShort(p,&id);
1167 p=ReadResourceByte(p,&length_byte);
1169 if (((length_byte+1) & 0x01) != 0)
1171 if (p > (datum+length-4))
1173 p=ReadResourceLong(p,&count);
1174 if ((p > (datum+length-count)) || (count > length))
1186 p=ReadResourceShort(p,&resolution)+6;
1187 image->resolution.x=(double) resolution;
1188 p=ReadResourceShort(p,&resolution)+6;
1189 image->resolution.y=(double) resolution;
1197 profile=AcquireStringInfo(count);
1198 SetStringInfoDatum(profile,p);
1199 (void) SetImageProfile(image,"iptc",profile,exception);
1200 profile=DestroyStringInfo(profile);
1217 profile=AcquireStringInfo(count);
1218 SetStringInfoDatum(profile,p);
1219 (void) SetImageProfile(image,"icc",profile,exception);
1220 profile=DestroyStringInfo(profile);
1229 profile=AcquireStringInfo(count);
1230 SetStringInfoDatum(profile,p);
1231 (void) SetImageProfile(image,"exif",profile,exception);
1232 profile=DestroyStringInfo(profile);
1241 profile=AcquireStringInfo(count);
1242 SetStringInfoDatum(profile,p);
1243 (void) SetImageProfile(image,"xmp",profile,exception);
1244 profile=DestroyStringInfo(profile);
1254 if ((count & 0x01) != 0)
1260 MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
1261 const StringInfo *profile,ExceptionInfo *exception)
1265 property[MaxTextExtent];
1270 assert(image != (Image *) NULL);
1271 assert(image->signature == MagickSignature);
1272 if (image->debug != MagickFalse)
1273 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1274 if (image->profiles == (SplayTreeInfo *) NULL)
1275 image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
1277 (void) CopyMagickString(key,name,MaxTextExtent);
1278 status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1279 ConstantString(key),CloneStringInfo(profile));
1280 if ((status != MagickFalse) &&
1281 ((LocaleCompare(name,"iptc") == 0) || (LocaleCompare(name,"8bim") == 0)))
1282 (void) GetProfilesFromResourceBlock(image,profile,exception);
1284 Inject profile into image properties.
1286 (void) FormatLocaleString(property,MaxTextExtent,"%s:sans",name);
1287 (void) GetImageProperty(image,property,exception);
1292 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1296 % S y n c I m a g e P r o f i l e s %
1300 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1302 % SyncImageProfiles() synchronizes image properties with the image profiles.
1303 % Currently we only support updating the EXIF resolution and orientation.
1305 % The format of the SyncImageProfiles method is:
1307 % MagickBooleanType SyncImageProfiles(Image *image)
1309 % A description of each parameter follows:
1311 % o image: the image.
1315 static inline int ReadProfileByte(unsigned char **p,size_t *length)
1327 static inline unsigned short ReadProfileShort(const EndianType endian,
1328 unsigned char *buffer)
1333 if (endian == LSBEndian)
1335 value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
1336 return((unsigned short) (value & 0xffff));
1338 value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
1339 ((unsigned char *) buffer)[1]);
1340 return((unsigned short) (value & 0xffff));
1343 static inline size_t ReadProfileLong(const EndianType endian,
1344 unsigned char *buffer)
1349 if (endian == LSBEndian)
1351 value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
1352 (buffer[1] << 8 ) | (buffer[0]));
1353 return((size_t) (value & 0xffffffff));
1355 value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
1356 (buffer[2] << 8) | buffer[3]);
1357 return((size_t) (value & 0xffffffff));
1360 static inline void WriteProfileLong(const EndianType endian,
1361 const size_t value,unsigned char *p)
1366 if (endian == LSBEndian)
1368 buffer[0]=(unsigned char) value;
1369 buffer[1]=(unsigned char) (value >> 8);
1370 buffer[2]=(unsigned char) (value >> 16);
1371 buffer[3]=(unsigned char) (value >> 24);
1372 (void) CopyMagickMemory(p,buffer,4);
1375 buffer[0]=(unsigned char) (value >> 24);
1376 buffer[1]=(unsigned char) (value >> 16);
1377 buffer[2]=(unsigned char) (value >> 8);
1378 buffer[3]=(unsigned char) value;
1379 (void) CopyMagickMemory(p,buffer,4);
1382 static void WriteProfileShort(const EndianType endian,
1383 const unsigned short value,unsigned char *p)
1388 if (endian == LSBEndian)
1390 buffer[0]=(unsigned char) value;
1391 buffer[1]=(unsigned char) (value >> 8);
1392 (void) CopyMagickMemory(p,buffer,2);
1395 buffer[0]=(unsigned char) (value >> 8);
1396 buffer[1]=(unsigned char) value;
1397 (void) CopyMagickMemory(p,buffer,2);
1400 MagickPrivate MagickBooleanType SyncImageProfiles(Image *image)
1402 #define MaxDirectoryStack 16
1403 #define EXIF_DELIMITER "\n"
1404 #define EXIF_NUM_FORMATS 12
1405 #define TAG_EXIF_OFFSET 0x8769
1406 #define TAG_INTEROP_OFFSET 0xa005
1408 typedef struct _DirectoryInfo
1418 directory_stack[MaxDirectoryStack];
1434 format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1444 Set EXIF resolution tag.
1446 profile=(StringInfo *) GetImageProfile(image,"EXIF");
1447 if (profile == (StringInfo *) NULL)
1449 length=GetStringInfoLength(profile);
1450 exif=GetStringInfoDatum(profile);
1453 if (ReadProfileByte(&exif,&length) != 0x45)
1455 if (ReadProfileByte(&exif,&length) != 0x78)
1457 if (ReadProfileByte(&exif,&length) != 0x69)
1459 if (ReadProfileByte(&exif,&length) != 0x66)
1461 if (ReadProfileByte(&exif,&length) != 0x00)
1463 if (ReadProfileByte(&exif,&length) != 0x00)
1468 return(MagickFalse);
1469 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
1477 return(MagickFalse);
1478 if (ReadProfileShort(endian,exif+2) != 0x002a)
1479 return(MagickFalse);
1481 This the offset to the first IFD.
1483 offset=(ssize_t) ((int) ReadProfileLong(endian,exif+4));
1484 if ((offset < 0) || (size_t) offset >= length)
1485 return(MagickFalse);
1486 directory=exif+offset;
1494 directory=directory_stack[level].directory;
1495 entry=directory_stack[level].entry;
1498 Determine how many entries there are in the current IFD.
1500 number_entries=ReadProfileShort(endian,directory);
1501 for ( ; entry < number_entries; entry++)
1506 register unsigned char
1517 q=(unsigned char *) (directory+2+(12*entry));
1518 tag_value=(ssize_t) ReadProfileShort(endian,q);
1519 format=(ssize_t) ReadProfileShort(endian,q+2);
1520 if ((format-1) >= EXIF_NUM_FORMATS)
1522 components=(ssize_t) ((int) ReadProfileLong(endian,q+4));
1523 number_bytes=(size_t) components*format_bytes[format];
1524 if ((ssize_t) number_bytes < components)
1525 break; /* prevent overflow */
1526 if (number_bytes <= 4)
1534 The directory entry contains an offset.
1536 offset=(ssize_t) ((int) ReadProfileLong(endian,q+8));
1537 if ((size_t) (offset+number_bytes) > length)
1539 if (~length < number_bytes)
1540 continue; /* prevent overflow */
1541 p=(unsigned char *) (exif+offset);
1547 (void) WriteProfileLong(endian,(size_t) (image->resolution.x+0.5),p);
1548 (void) WriteProfileLong(endian,1UL,p+4);
1553 (void) WriteProfileLong(endian,(size_t) (image->resolution.y+0.5),p);
1554 (void) WriteProfileLong(endian,1UL,p+4);
1559 if (number_bytes == 4)
1561 (void) WriteProfileLong(endian,(size_t) image->orientation,p);
1564 (void) WriteProfileShort(endian,(unsigned short) image->orientation,
1570 if (number_bytes == 4)
1572 (void) WriteProfileLong(endian,(size_t) (image->units+1),p);
1575 (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
1581 if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
1586 offset=(ssize_t) ((int) ReadProfileLong(endian,p));
1587 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
1589 directory_stack[level].directory=directory;
1591 directory_stack[level].entry=entry;
1593 directory_stack[level].directory=exif+offset;
1594 directory_stack[level].entry=0;
1596 if ((directory+2+(12*number_entries)) > (exif+length))
1598 offset=(ssize_t) ((int) ReadProfileLong(endian,directory+2+(12*
1600 if ((offset != 0) && ((size_t) offset < length) &&
1601 (level < (MaxDirectoryStack-2)))
1603 directory_stack[level].directory=exif+offset;
1604 directory_stack[level].entry=0;
1611 } while (level > 0);