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-2014 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)
167 if (image->profiles != (void *) NULL)
168 DestroyImageProfiles(image);
169 image->profiles=CloneSplayTree((SplayTreeInfo *) clone_image->profiles,
170 (void *(*)(void *)) ConstantString,(void *(*)(void *)) CloneStringInfo);
176 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
180 % D e l e t e I m a g e P r o f i l e %
184 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
186 % DeleteImageProfile() deletes a profile from the image by its name.
188 % The format of the DeleteImageProfile method is:
190 % MagickBooleanTyupe DeleteImageProfile(Image *image,const char *name)
192 % A description of each parameter follows:
194 % o image: the image.
196 % o name: the profile name.
199 MagickExport MagickBooleanType DeleteImageProfile(Image *image,const char *name)
201 assert(image != (Image *) NULL);
202 assert(image->signature == MagickSignature);
203 if (image->debug != MagickFalse)
204 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
205 if (image->profiles == (SplayTreeInfo *) NULL)
207 return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->profiles,name));
211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
215 % D e s t r o y I m a g e P r o f i l e s %
219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
221 % DestroyImageProfiles() releases memory associated with an image profile map.
223 % The format of the DestroyProfiles method is:
225 % void DestroyImageProfiles(Image *image)
227 % A description of each parameter follows:
229 % o image: the image.
232 MagickExport void DestroyImageProfiles(Image *image)
234 if (image->profiles != (SplayTreeInfo *) NULL)
235 image->profiles=DestroySplayTree((SplayTreeInfo *) image->profiles);
239 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
243 % G e t I m a g e P r o f i l e %
247 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
249 % GetImageProfile() gets a profile associated with an image by name.
251 % The format of the GetImageProfile method is:
253 % const StringInfo *GetImageProfile(const Image *image,const char *name)
255 % A description of each parameter follows:
257 % o image: the image.
259 % o name: the profile name.
262 MagickExport const StringInfo *GetImageProfile(const Image *image,
271 assert(image != (Image *) NULL);
272 assert(image->signature == MagickSignature);
273 if (image->debug != MagickFalse)
274 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
275 if (image->profiles == (SplayTreeInfo *) NULL)
276 return((StringInfo *) NULL);
277 (void) CopyMagickString(key,name,MaxTextExtent);
278 profile=(const StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
279 image->profiles,key);
284 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
288 % G e t N e x t I m a g e P r o f i l e %
292 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
294 % GetNextImageProfile() gets the next profile name for an image.
296 % The format of the GetNextImageProfile method is:
298 % char *GetNextImageProfile(const Image *image)
300 % A description of each parameter follows:
302 % o hash_info: the hash info.
305 MagickExport char *GetNextImageProfile(const Image *image)
307 assert(image != (Image *) NULL);
308 assert(image->signature == MagickSignature);
309 if (image->debug != MagickFalse)
310 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
311 if (image->profiles == (SplayTreeInfo *) NULL)
312 return((char *) NULL);
313 return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->profiles));
317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
321 % P r o f i l e I m a g e %
325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
327 % ProfileImage() associates, applies, or removes an ICM, IPTC, or generic
328 % profile with / to / from an image. If the profile is NULL, it is removed
329 % from the image otherwise added or applied. Use a name of '*' and a profile
330 % of NULL to remove all profiles from the image.
332 % ICC and ICM profiles are handled as follows: If the image does not have
333 % an associated color profile, the one you provide is associated with the
334 % image and the image pixels are not transformed. Otherwise, the colorspace
335 % transform defined by the existing and new profile are applied to the image
336 % pixels and the new profile is associated with the image.
338 % The format of the ProfileImage method is:
340 % MagickBooleanType ProfileImage(Image *image,const char *name,
341 % const void *datum,const size_t length,const MagickBooleanType clone)
343 % A description of each parameter follows:
345 % o image: the image.
347 % o name: Name of profile to add or remove: ICC, IPTC, or generic profile.
349 % o datum: the profile data.
351 % o length: the length of the profile.
353 % o clone: should be MagickFalse.
357 #if defined(MAGICKCORE_LCMS_DELEGATE)
359 static unsigned short **DestroyPixelThreadSet(unsigned short **pixels)
364 assert(pixels != (unsigned short **) NULL);
365 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
366 if (pixels[i] != (unsigned short *) NULL)
367 pixels[i]=(unsigned short *) RelinquishMagickMemory(pixels[i]);
368 pixels=(unsigned short **) RelinquishMagickMemory(pixels);
372 static unsigned short **AcquirePixelThreadSet(const size_t columns,
373 const size_t channels)
384 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
385 pixels=(unsigned short **) AcquireQuantumMemory(number_threads,
387 if (pixels == (unsigned short **) NULL)
388 return((unsigned short **) NULL);
389 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
390 for (i=0; i < (ssize_t) number_threads; i++)
392 pixels[i]=(unsigned short *) AcquireQuantumMemory(columns,channels*
394 if (pixels[i] == (unsigned short *) NULL)
395 return(DestroyPixelThreadSet(pixels));
400 static cmsHTRANSFORM *DestroyTransformThreadSet(cmsHTRANSFORM *transform)
405 assert(transform != (cmsHTRANSFORM *) NULL);
406 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
407 if (transform[i] != (cmsHTRANSFORM) NULL)
408 cmsDeleteTransform(transform[i]);
409 transform=(cmsHTRANSFORM *) RelinquishMagickMemory(transform);
413 static cmsHTRANSFORM *AcquireTransformThreadSet(Image *image,
414 const cmsHPROFILE source_profile,const cmsUInt32Number source_type,
415 const cmsHPROFILE target_profile,const cmsUInt32Number target_type,
416 const int intent,const cmsUInt32Number flags)
427 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
428 transform=(cmsHTRANSFORM *) AcquireQuantumMemory(number_threads,
430 if (transform == (cmsHTRANSFORM *) NULL)
431 return((cmsHTRANSFORM *) NULL);
432 (void) ResetMagickMemory(transform,0,number_threads*sizeof(*transform));
433 for (i=0; i < (ssize_t) number_threads; i++)
435 transform[i]=cmsCreateTransformTHR(image,source_profile,source_type,
436 target_profile,target_type,intent,flags);
437 if (transform[i] == (cmsHTRANSFORM) NULL)
438 return(DestroyTransformThreadSet(transform));
444 #if defined(MAGICKCORE_LCMS_DELEGATE)
445 #if defined(LCMS_VERSION) && (LCMS_VERSION >= 2000)
446 static void CMSExceptionHandler(cmsContext context,cmsUInt32Number severity,
458 cms_exception=(CMSExceptionInfo *) context;
459 image=cms_exception->image;
460 exception=cms_exception->exception;
461 if (image == (Image *) NULL)
463 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
464 "UnableToTransformColorspace","`%s'","unknown context");
467 if (image->debug != MagickFalse)
468 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%u, %s",
469 severity,message != (char *) NULL ? message : "no message");
470 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
471 "UnableToTransformColorspace","`%s'",image->filename);
474 static int CMSExceptionHandler(int severity,const char *message)
476 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%d, %s",
477 severity,message != (char *) NULL ? message : "no message");
483 MagickExport MagickBooleanType ProfileImage(Image *image,const char *name,
484 const void *datum,const size_t length,ExceptionInfo *exception)
486 #define ProfileImageTag "Profile/Image"
487 #define ThrowProfileException(severity,tag,context) \
489 if (source_profile != (cmsHPROFILE) NULL) \
490 (void) cmsCloseProfile(source_profile); \
491 if (target_profile != (cmsHPROFILE) NULL) \
492 (void) cmsCloseProfile(target_profile); \
493 ThrowBinaryException(severity,tag,context); \
502 assert(image != (Image *) NULL);
503 assert(image->signature == MagickSignature);
504 if (image->debug != MagickFalse)
505 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
506 assert(name != (const char *) NULL);
507 if ((datum == (const void *) NULL) || (length == 0))
520 Delete image profile(s).
522 names=ConstantString(name);
523 (void) SubstituteString(&names,","," ");
524 arguments=StringToArgv(names,&number_arguments);
525 names=DestroyString(names);
526 if (arguments == (char **) NULL)
528 ResetImageProfileIterator(image);
529 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
531 for (i=1; i < (ssize_t) number_arguments; i++)
533 if ((*arguments[i] == '!') &&
534 (LocaleCompare(name,arguments[i]+1) == 0))
536 if (GlobExpression(name,arguments[i],MagickTrue) != MagickFalse)
538 (void) DeleteImageProfile(image,name);
542 name=GetNextImageProfile(image);
544 for (i=0; i < (ssize_t) number_arguments; i++)
545 arguments[i]=DestroyString(arguments[i]);
546 arguments=(char **) RelinquishMagickMemory(arguments);
550 Add a ICC, IPTC, or generic profile to the image.
553 profile=AcquireStringInfo((size_t) length);
554 SetStringInfoDatum(profile,(unsigned char *) datum);
555 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
556 status=SetImageProfile(image,name,profile,exception);
562 icc_profile=GetImageProfile(image,"icc");
563 if ((icc_profile != (const StringInfo *) NULL) &&
564 (CompareStringInfo(icc_profile,profile) == 0))
569 value=GetImageProperty(image,"exif:ColorSpace",exception);
572 if (LocaleCompare(value,"1") != 0)
573 (void) SetsRGBImageProfile(image,exception);
574 value=GetImageProperty(image,"exif:InteroperabilityIndex",exception);
575 if (LocaleCompare(value,"R98.") != 0)
576 (void) SetsRGBImageProfile(image,exception);
577 value=GetImageProperty(image,"exif:InteroperabilityIndex",exception);
578 if (LocaleCompare(value,"R03.") != 0)
579 (void) SetAdobeRGB1998ImageProfile(image,exception);
581 icc_profile=GetImageProfile(image,"icc");
583 if ((icc_profile != (const StringInfo *) NULL) &&
584 (CompareStringInfo(icc_profile,profile) == 0))
586 profile=DestroyStringInfo(profile);
589 #if !defined(MAGICKCORE_LCMS_DELEGATE)
590 (void) ThrowMagickException(exception,GetMagickModule(),
591 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
592 "'%s' (LCMS)",image->filename);
602 Transform pixel colors as defined by the color profiles.
604 cmsSetLogErrorHandler(CMSExceptionHandler);
605 cms_exception.image=image;
606 cms_exception.exception=exception;
607 (void) cms_exception;
608 source_profile=cmsOpenProfileFromMemTHR(&cms_exception,
609 GetStringInfoDatum(profile),(cmsUInt32Number)
610 GetStringInfoLength(profile));
611 if (source_profile == (cmsHPROFILE) NULL)
612 ThrowBinaryException(ResourceLimitError,
613 "ColorspaceColorProfileMismatch",name);
614 if ((cmsGetDeviceClass(source_profile) != cmsSigLinkClass) &&
615 (icc_profile == (StringInfo *) NULL))
616 status=SetImageProfile(image,name,profile,exception);
626 cmsColorSpaceSignature
657 **restrict source_pixels,
658 **restrict target_pixels;
660 target_profile=(cmsHPROFILE) NULL;
661 if (icc_profile != (StringInfo *) NULL)
663 target_profile=source_profile;
664 source_profile=cmsOpenProfileFromMemTHR(&cms_exception,
665 GetStringInfoDatum(icc_profile),(cmsUInt32Number)
666 GetStringInfoLength(icc_profile));
667 if (source_profile == (cmsHPROFILE) NULL)
668 ThrowProfileException(ResourceLimitError,
669 "ColorspaceColorProfileMismatch",name);
671 switch (cmsGetColorSpace(source_profile))
675 source_colorspace=CMYKColorspace;
676 source_type=(cmsUInt32Number) TYPE_CMYK_16;
682 source_colorspace=GRAYColorspace;
683 source_type=(cmsUInt32Number) TYPE_GRAY_16;
689 source_colorspace=LabColorspace;
690 source_type=(cmsUInt32Number) TYPE_Lab_16;
696 source_colorspace=YUVColorspace;
697 source_type=(cmsUInt32Number) TYPE_YUV_16;
703 source_colorspace=sRGBColorspace;
704 source_type=(cmsUInt32Number) TYPE_RGB_16;
710 source_colorspace=XYZColorspace;
711 source_type=(cmsUInt32Number) TYPE_XYZ_16;
715 case cmsSigYCbCrData:
717 source_colorspace=YCbCrColorspace;
718 source_type=(cmsUInt32Number) TYPE_YCbCr_16;
724 source_colorspace=UndefinedColorspace;
725 source_type=(cmsUInt32Number) TYPE_RGB_16;
730 signature=cmsGetPCS(source_profile);
731 if (target_profile != (cmsHPROFILE) NULL)
732 signature=cmsGetColorSpace(target_profile);
737 target_colorspace=CMYKColorspace;
738 target_type=(cmsUInt32Number) TYPE_CMYK_16;
744 target_colorspace=LabColorspace;
745 target_type=(cmsUInt32Number) TYPE_Lab_16;
751 target_colorspace=GRAYColorspace;
752 target_type=(cmsUInt32Number) TYPE_GRAY_16;
758 target_colorspace=YUVColorspace;
759 target_type=(cmsUInt32Number) TYPE_YUV_16;
765 target_colorspace=sRGBColorspace;
766 target_type=(cmsUInt32Number) TYPE_RGB_16;
772 target_colorspace=XYZColorspace;
773 target_type=(cmsUInt32Number) TYPE_XYZ_16;
777 case cmsSigYCbCrData:
779 target_colorspace=YCbCrColorspace;
780 target_type=(cmsUInt32Number) TYPE_YCbCr_16;
786 target_colorspace=UndefinedColorspace;
787 target_type=(cmsUInt32Number) TYPE_RGB_16;
792 if ((source_colorspace == UndefinedColorspace) ||
793 (target_colorspace == UndefinedColorspace))
794 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
796 if ((source_colorspace == GRAYColorspace) &&
797 (IsImageGray(image,exception) == MagickFalse))
798 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
800 if ((source_colorspace == CMYKColorspace) &&
801 (image->colorspace != CMYKColorspace))
802 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
804 if ((source_colorspace == XYZColorspace) &&
805 (image->colorspace != XYZColorspace))
806 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
808 if ((source_colorspace == YCbCrColorspace) &&
809 (image->colorspace != YCbCrColorspace))
810 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
812 if ((source_colorspace != CMYKColorspace) &&
813 (source_colorspace != LabColorspace) &&
814 (source_colorspace != XYZColorspace) &&
815 (source_colorspace != YCbCrColorspace) &&
816 (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse))
817 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
819 switch (image->rendering_intent)
821 case AbsoluteIntent: intent=INTENT_ABSOLUTE_COLORIMETRIC; break;
822 case PerceptualIntent: intent=INTENT_PERCEPTUAL; break;
823 case RelativeIntent: intent=INTENT_RELATIVE_COLORIMETRIC; break;
824 case SaturationIntent: intent=INTENT_SATURATION; break;
825 default: intent=INTENT_PERCEPTUAL; break;
827 flags=cmsFLAGS_HIGHRESPRECALC;
828 #if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
829 if (image->black_point_compensation != MagickFalse)
830 flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
832 transform=AcquireTransformThreadSet(image,source_profile,
833 source_type,target_profile,target_type,intent,flags);
834 if (transform == (cmsHTRANSFORM *) NULL)
835 ThrowProfileException(ImageError,"UnableToCreateColorTransform",
838 Transform image as dictated by the source & target image profiles.
840 source_pixels=AcquirePixelThreadSet(image->columns,source_channels);
841 target_pixels=AcquirePixelThreadSet(image->columns,target_channels);
842 if ((source_pixels == (unsigned short **) NULL) ||
843 (target_pixels == (unsigned short **) NULL))
845 transform=DestroyTransformThreadSet(transform);
846 ThrowProfileException(ResourceLimitError,
847 "MemoryAllocationFailed",image->filename);
849 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
851 target_pixels=DestroyPixelThreadSet(target_pixels);
852 source_pixels=DestroyPixelThreadSet(source_pixels);
853 transform=DestroyTransformThreadSet(transform);
854 if (source_profile != (cmsHPROFILE) NULL)
855 (void) cmsCloseProfile(source_profile);
856 if (target_profile != (cmsHPROFILE) NULL)
857 (void) cmsCloseProfile(target_profile);
860 if (target_colorspace == CMYKColorspace)
861 (void) SetImageColorspace(image,target_colorspace,exception);
864 image_view=AcquireAuthenticCacheView(image,exception);
865 #if defined(MAGICKCORE_OPENMP_SUPPORT)
866 #pragma omp parallel for schedule(static,4) shared(status) \
867 magick_threads(image,image,image->rows,1)
869 for (y=0; y < (ssize_t) image->rows; y++)
872 id = GetOpenMPThreadId();
883 register unsigned short
886 if (status == MagickFalse)
888 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
890 if (q == (Quantum *) NULL)
896 for (x=0; x < (ssize_t) image->columns; x++)
898 *p++=ScaleQuantumToShort(GetPixelRed(image,q));
899 if (source_channels > 1)
901 *p++=ScaleQuantumToShort(GetPixelGreen(image,q));
902 *p++=ScaleQuantumToShort(GetPixelBlue(image,q));
904 if (source_channels > 3)
905 *p++=ScaleQuantumToShort(GetPixelBlack(image,q));
906 q+=GetPixelChannels(image);
908 cmsDoTransform(transform[id],source_pixels[id],target_pixels[id],
909 (unsigned int) image->columns);
911 q-=image->columns*GetPixelChannels(image);
912 for (x=0; x < (ssize_t) image->columns; x++)
914 if (target_channels == 1)
915 SetPixelGray(image,ScaleShortToQuantum(*p),q);
917 SetPixelRed(image,ScaleShortToQuantum(*p),q);
919 if (target_channels > 1)
921 SetPixelGreen(image,ScaleShortToQuantum(*p),q);
923 SetPixelBlue(image,ScaleShortToQuantum(*p),q);
926 if (target_channels > 3)
928 SetPixelBlack(image,ScaleShortToQuantum(*p),q);
931 q+=GetPixelChannels(image);
933 sync=SyncCacheViewAuthenticPixels(image_view,exception);
934 if (sync == MagickFalse)
936 if (image->progress_monitor != (MagickProgressMonitor) NULL)
941 #if defined(MAGICKCORE_OPENMP_SUPPORT)
942 #pragma omp critical (MagickCore_ProfileImage)
944 proceed=SetImageProgress(image,ProfileImageTag,progress++,
946 if (proceed == MagickFalse)
950 image_view=DestroyCacheView(image_view);
951 (void) SetImageColorspace(image,target_colorspace,exception);
956 image->type=image->alpha_trait != BlendPixelTrait ?
957 TrueColorType : TrueColorMatteType;
962 image->type=image->alpha_trait != BlendPixelTrait ?
963 ColorSeparationType : ColorSeparationMatteType;
968 image->type=image->alpha_trait != BlendPixelTrait ?
969 GrayscaleType : GrayscaleMatteType;
975 target_pixels=DestroyPixelThreadSet(target_pixels);
976 source_pixels=DestroyPixelThreadSet(source_pixels);
977 transform=DestroyTransformThreadSet(transform);
978 if (cmsGetDeviceClass(source_profile) != cmsSigLinkClass)
979 status=SetImageProfile(image,name,profile,exception);
980 if (target_profile != (cmsHPROFILE) NULL)
981 (void) cmsCloseProfile(target_profile);
983 (void) cmsCloseProfile(source_profile);
987 profile=DestroyStringInfo(profile);
992 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
996 % R e m o v e I m a g e P r o f i l e %
1000 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1002 % RemoveImageProfile() removes a named profile from the image and returns its
1005 % The format of the RemoveImageProfile method is:
1007 % void *RemoveImageProfile(Image *image,const char *name)
1009 % A description of each parameter follows:
1011 % o image: the image.
1013 % o name: the profile name.
1016 MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
1021 assert(image != (Image *) NULL);
1022 assert(image->signature == MagickSignature);
1023 if (image->debug != MagickFalse)
1024 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1025 if (image->profiles == (SplayTreeInfo *) NULL)
1026 return((StringInfo *) NULL);
1027 profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
1028 image->profiles,name);
1033 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1037 % R e s e t P r o f i l e I t e r a t o r %
1041 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1043 % ResetImageProfileIterator() resets the image profile iterator. Use it in
1044 % conjunction with GetNextImageProfile() to iterate over all the profiles
1045 % associated with an image.
1047 % The format of the ResetImageProfileIterator method is:
1049 % ResetImageProfileIterator(Image *image)
1051 % A description of each parameter follows:
1053 % o image: the image.
1056 MagickExport void ResetImageProfileIterator(const Image *image)
1058 assert(image != (Image *) NULL);
1059 assert(image->signature == MagickSignature);
1060 if (image->debug != MagickFalse)
1061 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1062 if (image->profiles == (SplayTreeInfo *) NULL)
1064 ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
1068 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1072 % S e t I m a g e P r o f i l e %
1076 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1078 % SetImageProfile() adds a named profile to the image. If a profile with the
1079 % same name already exists, it is replaced. This method differs from the
1080 % ProfileImage() method in that it does not apply CMS color profiles.
1082 % The format of the SetImageProfile method is:
1084 % MagickBooleanType SetImageProfile(Image *image,const char *name,
1085 % const StringInfo *profile)
1087 % A description of each parameter follows:
1089 % o image: the image.
1091 % o name: the profile name, for example icc, exif, and 8bim (8bim is the
1092 % Photoshop wrapper for iptc profiles).
1094 % o profile: A StringInfo structure that contains the named profile.
1098 static void *DestroyProfile(void *profile)
1100 return((void *) DestroyStringInfo((StringInfo *) profile));
1103 static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1104 unsigned char *quantum)
1110 static inline const unsigned char *ReadResourceBytes(const unsigned char *p,
1111 const ssize_t count,unsigned char *quantum)
1116 for (i=0; i < count; i++)
1121 static inline const unsigned char *ReadResourceLong(const unsigned char *p,
1122 unsigned int *quantum)
1124 *quantum=(size_t) (*p++ << 24);
1125 *quantum|=(size_t) (*p++ << 16);
1126 *quantum|=(size_t) (*p++ << 8);
1127 *quantum|=(size_t) (*p++ << 0);
1131 static inline const unsigned char *ReadResourceShort(const unsigned char *p,
1132 unsigned short *quantum)
1134 *quantum=(unsigned short) (*p++ << 8);
1135 *quantum|=(unsigned short) (*p++ << 0);
1139 static MagickBooleanType GetProfilesFromResourceBlock(Image *image,
1140 const StringInfo *resource_block,ExceptionInfo *exception)
1145 register const unsigned char
1166 datum=GetStringInfoDatum(resource_block);
1167 length=GetStringInfoLength(resource_block);
1168 for (p=datum; p < (datum+length-16); )
1170 if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1173 p=ReadResourceShort(p,&id);
1174 p=ReadResourceByte(p,&length_byte);
1176 if (((length_byte+1) & 0x01) != 0)
1178 if (p > (datum+length-4))
1180 p=ReadResourceLong(p,&value);
1181 count=(ssize_t) value;
1182 if ((p > (datum+length-count)) || (count > length))
1197 p=ReadResourceLong(p,&resolution);
1198 image->resolution.x=((double) resolution)/65536.0;
1199 p=ReadResourceShort(p,&units)+2;
1200 p=ReadResourceLong(p,&resolution)+4;
1201 image->resolution.y=((double) resolution)/65536.0;
1203 Values are always stored as pixels per inch.
1205 if ((ResolutionType) units != PixelsPerCentimeterResolution)
1206 image->units=PixelsPerInchResolution;
1209 image->units=PixelsPerCentimeterResolution;
1210 image->resolution.x/=2.54;
1211 image->resolution.y/=2.54;
1220 profile=AcquireStringInfo(count);
1221 SetStringInfoDatum(profile,p);
1222 (void) SetImageProfile(image,"iptc",profile,exception);
1223 profile=DestroyStringInfo(profile);
1240 profile=AcquireStringInfo(count);
1241 SetStringInfoDatum(profile,p);
1242 (void) SetImageProfile(image,"icc",profile,exception);
1243 profile=DestroyStringInfo(profile);
1252 profile=AcquireStringInfo(count);
1253 SetStringInfoDatum(profile,p);
1254 (void) SetImageProfile(image,"exif",profile,exception);
1255 profile=DestroyStringInfo(profile);
1264 profile=AcquireStringInfo(count);
1265 SetStringInfoDatum(profile,p);
1266 (void) SetImageProfile(image,"xmp",profile,exception);
1267 profile=DestroyStringInfo(profile);
1277 if ((count & 0x01) != 0)
1283 MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
1284 const StringInfo *profile,ExceptionInfo *exception)
1288 property[MaxTextExtent];
1293 assert(image != (Image *) NULL);
1294 assert(image->signature == MagickSignature);
1295 if (image->debug != MagickFalse)
1296 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1297 if (image->profiles == (SplayTreeInfo *) NULL)
1298 image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
1300 (void) CopyMagickString(key,name,MaxTextExtent);
1301 status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1302 ConstantString(key),CloneStringInfo(profile));
1303 if ((status != MagickFalse) &&
1304 ((LocaleCompare(name,"iptc") == 0) || (LocaleCompare(name,"8bim") == 0)))
1305 (void) GetProfilesFromResourceBlock(image,profile,exception);
1307 Inject profile into image properties.
1309 (void) FormatLocaleString(property,MaxTextExtent,"%s:*",name);
1310 (void) GetImageProperty(image,property,exception);
1315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1319 % S y n c I m a g e P r o f i l e s %
1323 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1325 % SyncImageProfiles() synchronizes image properties with the image profiles.
1326 % Currently we only support updating the EXIF resolution and orientation.
1328 % The format of the SyncImageProfiles method is:
1330 % MagickBooleanType SyncImageProfiles(Image *image)
1332 % A description of each parameter follows:
1334 % o image: the image.
1338 static inline int ReadProfileByte(unsigned char **p,size_t *length)
1350 static inline unsigned short ReadProfileShort(const EndianType endian,
1351 unsigned char *buffer)
1356 if (endian == LSBEndian)
1358 value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
1359 return((unsigned short) (value & 0xffff));
1361 value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
1362 ((unsigned char *) buffer)[1]);
1363 return((unsigned short) (value & 0xffff));
1366 static inline size_t ReadProfileLong(const EndianType endian,
1367 unsigned char *buffer)
1372 if (endian == LSBEndian)
1374 value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
1375 (buffer[1] << 8 ) | (buffer[0]));
1376 return((size_t) (value & 0xffffffff));
1378 value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
1379 (buffer[2] << 8) | buffer[3]);
1380 return((size_t) (value & 0xffffffff));
1383 static inline size_t ReadProfileMSBLong(unsigned char **p,
1392 value=ReadProfileLong(MSBEndian,*p);
1398 static inline unsigned short ReadProfileMSBShort(unsigned char **p,
1407 value=ReadProfileShort(MSBEndian,*p);
1413 static inline void WriteProfileLong(const EndianType endian,
1414 const size_t value,unsigned char *p)
1419 if (endian == LSBEndian)
1421 buffer[0]=(unsigned char) value;
1422 buffer[1]=(unsigned char) (value >> 8);
1423 buffer[2]=(unsigned char) (value >> 16);
1424 buffer[3]=(unsigned char) (value >> 24);
1425 (void) CopyMagickMemory(p,buffer,4);
1428 buffer[0]=(unsigned char) (value >> 24);
1429 buffer[1]=(unsigned char) (value >> 16);
1430 buffer[2]=(unsigned char) (value >> 8);
1431 buffer[3]=(unsigned char) value;
1432 (void) CopyMagickMemory(p,buffer,4);
1435 static void WriteProfileShort(const EndianType endian,
1436 const unsigned short value,unsigned char *p)
1441 if (endian == LSBEndian)
1443 buffer[0]=(unsigned char) value;
1444 buffer[1]=(unsigned char) (value >> 8);
1445 (void) CopyMagickMemory(p,buffer,2);
1448 buffer[0]=(unsigned char) (value >> 8);
1449 buffer[1]=(unsigned char) value;
1450 (void) CopyMagickMemory(p,buffer,2);
1453 static MagickBooleanType Sync8BimProfile(Image *image,StringInfo *profile)
1465 length=GetStringInfoLength(profile);
1466 p=GetStringInfoDatum(profile);
1469 if (ReadProfileByte(&p,&length) != 0x38)
1471 if (ReadProfileByte(&p,&length) != 0x42)
1473 if (ReadProfileByte(&p,&length) != 0x49)
1475 if (ReadProfileByte(&p,&length) != 0x4D)
1478 return(MagickFalse);
1479 id=ReadProfileMSBShort(&p,&length);
1480 count=ReadProfileByte(&p,&length);
1482 return(MagickFalse);
1484 if ((*p & 0x01) == 0)
1486 count=ReadProfileMSBLong(&p,&length);
1488 return(MagickFalse);
1489 if (id == 0x3ED && count == 16)
1491 if (image->units == PixelsPerCentimeterResolution)
1492 WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.x*2.54*
1495 WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.x*
1497 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+4);
1498 if (image->units == PixelsPerCentimeterResolution)
1499 WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.y*2.54*
1502 WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.y*
1504 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+12);
1512 MagickBooleanType SyncExifProfile(Image *image,StringInfo *profile)
1514 #define MaxDirectoryStack 16
1515 #define EXIF_DELIMITER "\n"
1516 #define EXIF_NUM_FORMATS 12
1517 #define TAG_EXIF_OFFSET 0x8769
1518 #define TAG_INTEROP_OFFSET 0xa005
1520 typedef struct _DirectoryInfo
1530 directory_stack[MaxDirectoryStack];
1546 format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1553 Set EXIF resolution tag.
1555 length=GetStringInfoLength(profile);
1556 exif=GetStringInfoDatum(profile);
1558 return(MagickFalse);
1559 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
1560 if ((id != 0x4949) && (id != 0x4D4D))
1564 if (ReadProfileByte(&exif,&length) != 0x45)
1566 if (ReadProfileByte(&exif,&length) != 0x78)
1568 if (ReadProfileByte(&exif,&length) != 0x69)
1570 if (ReadProfileByte(&exif,&length) != 0x66)
1572 if (ReadProfileByte(&exif,&length) != 0x00)
1574 if (ReadProfileByte(&exif,&length) != 0x00)
1579 return(MagickFalse);
1580 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
1589 return(MagickFalse);
1590 if (ReadProfileShort(endian,exif+2) != 0x002a)
1591 return(MagickFalse);
1593 This the offset to the first IFD.
1595 offset=(ssize_t) ((int) ReadProfileLong(endian,exif+4));
1596 if ((offset < 0) || (size_t) offset >= length)
1597 return(MagickFalse);
1598 directory=exif+offset;
1606 directory=directory_stack[level].directory;
1607 entry=directory_stack[level].entry;
1610 Determine how many entries there are in the current IFD.
1612 number_entries=ReadProfileShort(endian,directory);
1613 for ( ; entry < number_entries; entry++)
1618 register unsigned char
1629 q=(unsigned char *) (directory+2+(12*entry));
1630 tag_value=(ssize_t) ReadProfileShort(endian,q);
1631 format=(ssize_t) ReadProfileShort(endian,q+2);
1632 if ((format-1) >= EXIF_NUM_FORMATS)
1634 components=(ssize_t) ((int) ReadProfileLong(endian,q+4));
1635 number_bytes=(size_t) components*format_bytes[format];
1636 if ((ssize_t) number_bytes < components)
1637 break; /* prevent overflow */
1638 if (number_bytes <= 4)
1646 The directory entry contains an offset.
1648 offset=(ssize_t) ((int) ReadProfileLong(endian,q+8));
1649 if ((size_t) (offset+number_bytes) > length)
1651 if (~length < number_bytes)
1652 continue; /* prevent overflow */
1653 p=(unsigned char *) (exif+offset);
1659 (void) WriteProfileLong(endian,(size_t) (image->resolution.x+0.5),p);
1660 (void) WriteProfileLong(endian,1UL,p+4);
1665 (void) WriteProfileLong(endian,(size_t) (image->resolution.y+0.5),p);
1666 (void) WriteProfileLong(endian,1UL,p+4);
1671 if (number_bytes == 4)
1673 (void) WriteProfileLong(endian,(size_t) image->orientation,p);
1676 (void) WriteProfileShort(endian,(unsigned short) image->orientation,
1682 if (number_bytes == 4)
1684 (void) WriteProfileLong(endian,(size_t) (image->units+1),p);
1687 (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
1693 if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
1698 offset=(ssize_t) ((int) ReadProfileLong(endian,p));
1699 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
1701 directory_stack[level].directory=directory;
1703 directory_stack[level].entry=entry;
1705 directory_stack[level].directory=exif+offset;
1706 directory_stack[level].entry=0;
1708 if ((directory+2+(12*number_entries)) > (exif+length))
1710 offset=(ssize_t) ((int) ReadProfileLong(endian,directory+2+(12*
1712 if ((offset != 0) && ((size_t) offset < length) &&
1713 (level < (MaxDirectoryStack-2)))
1715 directory_stack[level].directory=exif+offset;
1716 directory_stack[level].entry=0;
1723 } while (level > 0);
1727 MagickPrivate MagickBooleanType SyncImageProfiles(Image *image)
1736 profile=(StringInfo *) GetImageProfile(image,"8BIM");
1737 if (profile != (StringInfo *) NULL)
1738 if (Sync8BimProfile(image,profile) == MagickFalse)
1740 profile=(StringInfo *) GetImageProfile(image,"EXIF");
1741 if (profile != (StringInfo *) NULL)
1742 if (SyncExifProfile(image,profile) == MagickFalse)