]> granicus.if.org Git - imagemagick/blob - MagickCore/profile.c
(no commit message)
[imagemagick] / MagickCore / profile.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
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                 %
9 %               P      R R    O   O  F        I    L      E                   %
10 %               P      R  R    OOO   F      IIIII  LLLLL  EEEEE               %
11 %                                                                             %
12 %                                                                             %
13 %                       MagickCore Image Profile Methods                      %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
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.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
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)
70 #include <wchar.h>
71 #include <lcms/lcms2.h>
72 #elif defined(MAGICKCORE_HAVE_LCMS2_H)
73 #include <wchar.h>
74 #include "lcms2.h"
75 #elif defined(MAGICKCORE_HAVE_LCMS_LCMS_H)
76 #include <lcms/lcms.h>
77 #else
78 #include "lcms.h"
79 #endif
80 #endif
81 \f
82 /*
83   Define declarations.
84 */
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)
102 #endif
103
104 /*
105   Forward declarations
106 */
107 static MagickBooleanType
108   SetImageProfileInternal(Image *,const char *,const StringInfo *,
109     const MagickBooleanType,ExceptionInfo *);
110
111 static void
112   WriteTo8BimProfile(Image *,const char*,const StringInfo *);
113 \f
114 /*
115   Typedef declarations
116 */
117 struct _ProfileInfo
118 {
119   char
120     *name;
121
122   size_t
123     length;
124
125   unsigned char
126     *info;
127
128   size_t
129     signature;
130 };
131
132 typedef struct _CMSExceptionInfo
133 {
134   Image
135     *image;
136
137   ExceptionInfo
138     *exception;
139 } CMSExceptionInfo;
140 \f
141 /*
142 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
143 %                                                                             %
144 %                                                                             %
145 %                                                                             %
146 %   C l o n e I m a g e P r o f i l e s                                       %
147 %                                                                             %
148 %                                                                             %
149 %                                                                             %
150 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
151 %
152 %  CloneImageProfiles() clones one or more image profiles.
153 %
154 %  The format of the CloneImageProfiles method is:
155 %
156 %      MagickBooleanType CloneImageProfiles(Image *image,
157 %        const Image *clone_image)
158 %
159 %  A description of each parameter follows:
160 %
161 %    o image: the image.
162 %
163 %    o clone_image: the clone image.
164 %
165 */
166 MagickExport MagickBooleanType CloneImageProfiles(Image *image,
167   const Image *clone_image)
168 {
169   assert(image != (Image *) NULL);
170   assert(image->signature == MagickSignature);
171   if (image->debug != MagickFalse)
172     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
173   assert(clone_image != (const Image *) NULL);
174   assert(clone_image->signature == MagickSignature);
175   if (clone_image->profiles != (void *) NULL)
176     {
177       if (image->profiles != (void *) NULL)
178         DestroyImageProfiles(image);
179       image->profiles=CloneSplayTree((SplayTreeInfo *) clone_image->profiles,
180         (void *(*)(void *)) ConstantString,(void *(*)(void *)) CloneStringInfo);
181     }
182   return(MagickTrue);
183 }
184 \f
185 /*
186 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
187 %                                                                             %
188 %                                                                             %
189 %                                                                             %
190 %   D e l e t e I m a g e P r o f i l e                                       %
191 %                                                                             %
192 %                                                                             %
193 %                                                                             %
194 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
195 %
196 %  DeleteImageProfile() deletes a profile from the image by its name.
197 %
198 %  The format of the DeleteImageProfile method is:
199 %
200 %      MagickBooleanTyupe DeleteImageProfile(Image *image,const char *name)
201 %
202 %  A description of each parameter follows:
203 %
204 %    o image: the image.
205 %
206 %    o name: the profile name.
207 %
208 */
209 MagickExport MagickBooleanType DeleteImageProfile(Image *image,const char *name)
210 {
211   assert(image != (Image *) NULL);
212   assert(image->signature == MagickSignature);
213   if (image->debug != MagickFalse)
214     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
215   if (image->profiles == (SplayTreeInfo *) NULL)
216     return(MagickFalse);
217   WriteTo8BimProfile(image,name,(StringInfo *) NULL);
218   return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->profiles,name));
219 }
220 \f
221 /*
222 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
223 %                                                                             %
224 %                                                                             %
225 %                                                                             %
226 %   D e s t r o y I m a g e P r o f i l e s                                   %
227 %                                                                             %
228 %                                                                             %
229 %                                                                             %
230 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
231 %
232 %  DestroyImageProfiles() releases memory associated with an image profile map.
233 %
234 %  The format of the DestroyProfiles method is:
235 %
236 %      void DestroyImageProfiles(Image *image)
237 %
238 %  A description of each parameter follows:
239 %
240 %    o image: the image.
241 %
242 */
243 MagickExport void DestroyImageProfiles(Image *image)
244 {
245   if (image->profiles != (SplayTreeInfo *) NULL)
246     image->profiles=DestroySplayTree((SplayTreeInfo *) image->profiles);
247 }
248 \f
249 /*
250 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
251 %                                                                             %
252 %                                                                             %
253 %                                                                             %
254 %   G e t I m a g e P r o f i l e                                             %
255 %                                                                             %
256 %                                                                             %
257 %                                                                             %
258 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
259 %
260 %  GetImageProfile() gets a profile associated with an image by name.
261 %
262 %  The format of the GetImageProfile method is:
263 %
264 %      const StringInfo *GetImageProfile(const Image *image,const char *name)
265 %
266 %  A description of each parameter follows:
267 %
268 %    o image: the image.
269 %
270 %    o name: the profile name.
271 %
272 */
273 MagickExport const StringInfo *GetImageProfile(const Image *image,
274   const char *name)
275 {
276   char
277     key[MaxTextExtent];
278
279   const StringInfo
280     *profile;
281
282   assert(image != (Image *) NULL);
283   assert(image->signature == MagickSignature);
284   if (image->debug != MagickFalse)
285     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
286   if (image->profiles == (SplayTreeInfo *) NULL)
287     return((StringInfo *) NULL);
288   (void) CopyMagickString(key,name,MaxTextExtent);
289   profile=(const StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
290     image->profiles,key);
291   return(profile);
292 }
293 \f
294 /*
295 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
296 %                                                                             %
297 %                                                                             %
298 %                                                                             %
299 %   G e t N e x t I m a g e P r o f i l e                                     %
300 %                                                                             %
301 %                                                                             %
302 %                                                                             %
303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
304 %
305 %  GetNextImageProfile() gets the next profile name for an image.
306 %
307 %  The format of the GetNextImageProfile method is:
308 %
309 %      char *GetNextImageProfile(const Image *image)
310 %
311 %  A description of each parameter follows:
312 %
313 %    o hash_info: the hash info.
314 %
315 */
316 MagickExport char *GetNextImageProfile(const Image *image)
317 {
318   assert(image != (Image *) NULL);
319   assert(image->signature == MagickSignature);
320   if (image->debug != MagickFalse)
321     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
322   if (image->profiles == (SplayTreeInfo *) NULL)
323     return((char *) NULL);
324   return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->profiles));
325 }
326 \f
327 /*
328 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
329 %                                                                             %
330 %                                                                             %
331 %                                                                             %
332 %   P r o f i l e I m a g e                                                   %
333 %                                                                             %
334 %                                                                             %
335 %                                                                             %
336 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
337 %
338 %  ProfileImage() associates, applies, or removes an ICM, IPTC, or generic
339 %  profile with / to / from an image.  If the profile is NULL, it is removed
340 %  from the image otherwise added or applied.  Use a name of '*' and a profile
341 %  of NULL to remove all profiles from the image.
342 %
343 %  ICC and ICM profiles are handled as follows: If the image does not have
344 %  an associated color profile, the one you provide is associated with the
345 %  image and the image pixels are not transformed.  Otherwise, the colorspace
346 %  transform defined by the existing and new profile are applied to the image
347 %  pixels and the new profile is associated with the image.
348 %
349 %  The format of the ProfileImage method is:
350 %
351 %      MagickBooleanType ProfileImage(Image *image,const char *name,
352 %        const void *datum,const size_t length,const MagickBooleanType clone)
353 %
354 %  A description of each parameter follows:
355 %
356 %    o image: the image.
357 %
358 %    o name: Name of profile to add or remove: ICC, IPTC, or generic profile.
359 %
360 %    o datum: the profile data.
361 %
362 %    o length: the length of the profile.
363 %
364 %    o clone: should be MagickFalse.
365 %
366 */
367
368 #if defined(MAGICKCORE_LCMS_DELEGATE)
369
370 static unsigned short **DestroyPixelThreadSet(unsigned short **pixels)
371 {
372   register ssize_t
373     i;
374
375   assert(pixels != (unsigned short **) NULL);
376   for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
377     if (pixels[i] != (unsigned short *) NULL)
378       pixels[i]=(unsigned short *) RelinquishMagickMemory(pixels[i]);
379   pixels=(unsigned short **) RelinquishMagickMemory(pixels);
380   return(pixels);
381 }
382
383 static unsigned short **AcquirePixelThreadSet(const size_t columns,
384   const size_t channels)
385 {
386   register ssize_t
387     i;
388
389   unsigned short
390     **pixels;
391
392   size_t
393     number_threads;
394
395   number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
396   pixels=(unsigned short **) AcquireQuantumMemory(number_threads,
397     sizeof(*pixels));
398   if (pixels == (unsigned short **) NULL)
399     return((unsigned short **) NULL);
400   (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
401   for (i=0; i < (ssize_t) number_threads; i++)
402   {
403     pixels[i]=(unsigned short *) AcquireQuantumMemory(columns,channels*
404       sizeof(**pixels));
405     if (pixels[i] == (unsigned short *) NULL)
406       return(DestroyPixelThreadSet(pixels));
407   }
408   return(pixels);
409 }
410
411 static cmsHTRANSFORM *DestroyTransformThreadSet(cmsHTRANSFORM *transform)
412 {
413   register ssize_t
414     i;
415
416   assert(transform != (cmsHTRANSFORM *) NULL);
417   for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
418     if (transform[i] != (cmsHTRANSFORM) NULL)
419       cmsDeleteTransform(transform[i]);
420   transform=(cmsHTRANSFORM *) RelinquishMagickMemory(transform);
421   return(transform);
422 }
423
424 static cmsHTRANSFORM *AcquireTransformThreadSet(Image *image,
425   const cmsHPROFILE source_profile,const cmsUInt32Number source_type,
426   const cmsHPROFILE target_profile,const cmsUInt32Number target_type,
427   const int intent,const cmsUInt32Number flags)
428 {
429   cmsHTRANSFORM
430     *transform;
431
432   register ssize_t
433     i;
434
435   size_t
436     number_threads;
437
438   number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
439   transform=(cmsHTRANSFORM *) AcquireQuantumMemory(number_threads,
440     sizeof(*transform));
441   if (transform == (cmsHTRANSFORM *) NULL)
442     return((cmsHTRANSFORM *) NULL);
443   (void) ResetMagickMemory(transform,0,number_threads*sizeof(*transform));
444   for (i=0; i < (ssize_t) number_threads; i++)
445   {
446     transform[i]=cmsCreateTransformTHR((cmsContext) image,source_profile,
447       source_type,target_profile,target_type,intent,flags);
448     if (transform[i] == (cmsHTRANSFORM) NULL)
449       return(DestroyTransformThreadSet(transform));
450   }
451   return(transform);
452 }
453 #endif
454
455 #if defined(MAGICKCORE_LCMS_DELEGATE)
456 #if defined(LCMS_VERSION) && (LCMS_VERSION >= 2000)
457 static void CMSExceptionHandler(cmsContext context,cmsUInt32Number severity,
458   const char *message)
459 {
460   CMSExceptionInfo
461     *cms_exception;
462
463   ExceptionInfo
464     *exception;
465
466   Image
467     *image;
468
469   cms_exception=(CMSExceptionInfo *) context;
470   image=cms_exception->image;
471   exception=cms_exception->exception;
472   if (image == (Image *) NULL)
473     {
474       (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
475         "UnableToTransformColorspace","`%s'","unknown context");
476       return;
477     }
478   if (image->debug != MagickFalse)
479     (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%u, %s",
480       severity,message != (char *) NULL ? message : "no message");
481   (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
482     "UnableToTransformColorspace","`%s'",image->filename);
483 }
484 #else
485 static int CMSExceptionHandler(int severity,const char *message)
486 {
487   (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%d, %s",
488     severity,message != (char *) NULL ? message : "no message");
489   return(1);
490 }
491 #endif
492 #endif
493
494 MagickExport MagickBooleanType ProfileImage(Image *image,const char *name,
495   const void *datum,const size_t length,ExceptionInfo *exception)
496 {
497 #define ProfileImageTag  "Profile/Image"
498 #define ThrowProfileException(severity,tag,context) \
499 { \
500   if (source_profile != (cmsHPROFILE) NULL) \
501     (void) cmsCloseProfile(source_profile); \
502   if (target_profile != (cmsHPROFILE) NULL) \
503     (void) cmsCloseProfile(target_profile); \
504   ThrowBinaryException(severity,tag,context); \
505 }
506
507   MagickBooleanType
508     status;
509
510   StringInfo
511     *profile;
512
513   assert(image != (Image *) NULL);
514   assert(image->signature == MagickSignature);
515   if (image->debug != MagickFalse)
516     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
517   assert(name != (const char *) NULL);
518   if ((datum == (const void *) NULL) || (length == 0))
519     {
520       char
521         **arguments,
522         *names;
523
524       int
525         number_arguments;
526
527       register ssize_t
528         i;
529
530       /*
531         Delete image profile(s).
532       */
533       names=ConstantString(name);
534       (void) SubstituteString(&names,","," ");
535       arguments=StringToArgv(names,&number_arguments);
536       names=DestroyString(names);
537       if (arguments == (char **) NULL)
538         return(MagickTrue);
539       ResetImageProfileIterator(image);
540       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
541       {
542         for (i=1; i < (ssize_t) number_arguments; i++)
543         {
544           if ((*arguments[i] == '!') &&
545               (LocaleCompare(name,arguments[i]+1) == 0))
546             break;
547           if (GlobExpression(name,arguments[i],MagickTrue) != MagickFalse)
548             {
549               (void) DeleteImageProfile(image,name);
550               break;
551             }
552         }
553         name=GetNextImageProfile(image);
554       }
555       for (i=0; i < (ssize_t) number_arguments; i++)
556         arguments[i]=DestroyString(arguments[i]);
557       arguments=(char **) RelinquishMagickMemory(arguments);
558       return(MagickTrue);
559     }
560   /*
561     Add a ICC, IPTC, or generic profile to the image.
562   */
563   status=MagickTrue;
564   profile=AcquireStringInfo((size_t) length);
565   SetStringInfoDatum(profile,(unsigned char *) datum);
566   if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
567     status=SetImageProfile(image,name,profile,exception);
568   else
569     {
570       const StringInfo
571         *icc_profile;
572
573       icc_profile=GetImageProfile(image,"icc");
574       if ((icc_profile != (const StringInfo *) NULL) &&
575           (CompareStringInfo(icc_profile,profile) == 0))
576         {
577           const char
578             *value;
579
580           value=GetImageProperty(image,"exif:ColorSpace",exception);
581           (void) value;
582           /* Future.
583           if (LocaleCompare(value,"1") != 0)
584             (void) SetsRGBImageProfile(image,exception);
585           value=GetImageProperty(image,"exif:InteroperabilityIndex",exception);
586           if (LocaleCompare(value,"R98.") != 0)
587             (void) SetsRGBImageProfile(image,exception);
588           value=GetImageProperty(image,"exif:InteroperabilityIndex",exception);
589           if (LocaleCompare(value,"R03.") != 0)
590             (void) SetAdobeRGB1998ImageProfile(image,exception);
591           */
592           icc_profile=GetImageProfile(image,"icc");
593         }
594       if ((icc_profile != (const StringInfo *) NULL) &&
595           (CompareStringInfo(icc_profile,profile) == 0))
596         {
597           profile=DestroyStringInfo(profile);
598           return(MagickTrue);
599         }
600 #if !defined(MAGICKCORE_LCMS_DELEGATE)
601       (void) ThrowMagickException(exception,GetMagickModule(),
602         MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
603         "'%s' (LCMS)",image->filename);
604 #else
605       {
606         cmsHPROFILE
607           source_profile;
608
609         CMSExceptionInfo
610           cms_exception;
611
612         /*
613           Transform pixel colors as defined by the color profiles.
614         */
615         cmsSetLogErrorHandler(CMSExceptionHandler);
616         cms_exception.image=image;
617         cms_exception.exception=exception;
618         (void) cms_exception;
619         source_profile=cmsOpenProfileFromMemTHR((cmsContext) &cms_exception,
620           GetStringInfoDatum(profile),(cmsUInt32Number)
621           GetStringInfoLength(profile));
622         if (source_profile == (cmsHPROFILE) NULL)
623           ThrowBinaryException(ResourceLimitError,
624             "ColorspaceColorProfileMismatch",name);
625         if ((cmsGetDeviceClass(source_profile) != cmsSigLinkClass) &&
626             (icc_profile == (StringInfo *) NULL))
627           status=SetImageProfile(image,name,profile,exception);
628         else
629           {
630             CacheView
631               *image_view;
632
633             ColorspaceType
634               source_colorspace,
635               target_colorspace;
636
637             cmsColorSpaceSignature
638               signature;
639
640             cmsHPROFILE
641               target_profile;
642
643             cmsHTRANSFORM
644               *restrict transform;
645
646             cmsUInt32Number
647               flags,
648               source_type,
649               target_type;
650
651             int
652               intent;
653
654             MagickBooleanType
655               status;
656
657             MagickOffsetType
658               progress;
659
660             size_t
661               source_channels,
662               target_channels;
663
664             ssize_t
665               y;
666
667             unsigned short
668               **restrict source_pixels,
669               **restrict target_pixels;
670
671             target_profile=(cmsHPROFILE) NULL;
672             if (icc_profile != (StringInfo *) NULL)
673               {
674                 target_profile=source_profile;
675                 source_profile=cmsOpenProfileFromMemTHR((cmsContext)
676                   &cms_exception,GetStringInfoDatum(icc_profile),
677                   (cmsUInt32Number) GetStringInfoLength(icc_profile));
678                 if (source_profile == (cmsHPROFILE) NULL)
679                   ThrowProfileException(ResourceLimitError,
680                     "ColorspaceColorProfileMismatch",name);
681               }
682             switch (cmsGetColorSpace(source_profile))
683             {
684               case cmsSigCmykData:
685               {
686                 source_colorspace=CMYKColorspace;
687                 source_type=(cmsUInt32Number) TYPE_CMYK_16;
688                 source_channels=4;
689                 break;
690               }
691               case cmsSigGrayData:
692               {
693                 source_colorspace=GRAYColorspace;
694                 source_type=(cmsUInt32Number) TYPE_GRAY_16;
695                 source_channels=1;
696                 break;
697               }
698               case cmsSigLabData:
699               {
700                 source_colorspace=LabColorspace;
701                 source_type=(cmsUInt32Number) TYPE_Lab_16;
702                 source_channels=3;
703                 break;
704               }
705               case cmsSigLuvData:
706               {
707                 source_colorspace=YUVColorspace;
708                 source_type=(cmsUInt32Number) TYPE_YUV_16;
709                 source_channels=3;
710                 break;
711               }
712               case cmsSigRgbData:
713               {
714                 source_colorspace=sRGBColorspace;
715                 source_type=(cmsUInt32Number) TYPE_RGB_16;
716                 source_channels=3;
717                 break;
718               }
719               case cmsSigXYZData:
720               {
721                 source_colorspace=XYZColorspace;
722                 source_type=(cmsUInt32Number) TYPE_XYZ_16;
723                 source_channels=3;
724                 break;
725               }
726               case cmsSigYCbCrData:
727               {
728                 source_colorspace=YCbCrColorspace;
729                 source_type=(cmsUInt32Number) TYPE_YCbCr_16;
730                 source_channels=3;
731                 break;
732               }
733               default:
734               {
735                 source_colorspace=UndefinedColorspace;
736                 source_type=(cmsUInt32Number) TYPE_RGB_16;
737                 source_channels=3;
738                 break;
739               }
740             }
741             signature=cmsGetPCS(source_profile);
742             if (target_profile != (cmsHPROFILE) NULL)
743               signature=cmsGetColorSpace(target_profile);
744             switch (signature)
745             {
746               case cmsSigCmykData:
747               {
748                 target_colorspace=CMYKColorspace;
749                 target_type=(cmsUInt32Number) TYPE_CMYK_16;
750                 target_channels=4;
751                 break;
752               }
753               case cmsSigLabData:
754               {
755                 target_colorspace=LabColorspace;
756                 target_type=(cmsUInt32Number) TYPE_Lab_16;
757                 target_channels=3;
758                 break;
759               }
760               case cmsSigGrayData:
761               {
762                 target_colorspace=GRAYColorspace;
763                 target_type=(cmsUInt32Number) TYPE_GRAY_16;
764                 target_channels=1;
765                 break;
766               }
767               case cmsSigLuvData:
768               {
769                 target_colorspace=YUVColorspace;
770                 target_type=(cmsUInt32Number) TYPE_YUV_16;
771                 target_channels=3;
772                 break;
773               }
774               case cmsSigRgbData:
775               {
776                 target_colorspace=sRGBColorspace;
777                 target_type=(cmsUInt32Number) TYPE_RGB_16;
778                 target_channels=3;
779                 break;
780               }
781               case cmsSigXYZData:
782               {
783                 target_colorspace=XYZColorspace;
784                 target_type=(cmsUInt32Number) TYPE_XYZ_16;
785                 target_channels=3;
786                 break;
787               }
788               case cmsSigYCbCrData:
789               {
790                 target_colorspace=YCbCrColorspace;
791                 target_type=(cmsUInt32Number) TYPE_YCbCr_16;
792                 target_channels=3;
793                 break;
794               }
795               default:
796               {
797                 target_colorspace=UndefinedColorspace;
798                 target_type=(cmsUInt32Number) TYPE_RGB_16;
799                 target_channels=3;
800                 break;
801               }
802             }
803             if ((source_colorspace == UndefinedColorspace) ||
804                 (target_colorspace == UndefinedColorspace))
805               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
806                 name);
807              if ((source_colorspace == GRAYColorspace) &&
808                  (IsImageGray(image,exception) == MagickFalse))
809               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
810                 name);
811              if ((source_colorspace == CMYKColorspace) &&
812                  (image->colorspace != CMYKColorspace))
813               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
814                 name);
815              if ((source_colorspace == XYZColorspace) &&
816                  (image->colorspace != XYZColorspace))
817               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
818                 name);
819              if ((source_colorspace == YCbCrColorspace) &&
820                  (image->colorspace != YCbCrColorspace))
821               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
822                 name);
823              if ((source_colorspace != CMYKColorspace) &&
824                  (source_colorspace != LabColorspace) &&
825                  (source_colorspace != XYZColorspace) &&
826                  (source_colorspace != YCbCrColorspace) &&
827                  (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse))
828               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
829                 name);
830             switch (image->rendering_intent)
831             {
832               case AbsoluteIntent: intent=INTENT_ABSOLUTE_COLORIMETRIC; break;
833               case PerceptualIntent: intent=INTENT_PERCEPTUAL; break;
834               case RelativeIntent: intent=INTENT_RELATIVE_COLORIMETRIC; break;
835               case SaturationIntent: intent=INTENT_SATURATION; break;
836               default: intent=INTENT_PERCEPTUAL; break;
837             }
838             flags=cmsFLAGS_HIGHRESPRECALC;
839 #if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
840             if (image->black_point_compensation != MagickFalse)
841               flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
842 #endif
843             transform=AcquireTransformThreadSet(image,source_profile,
844               source_type,target_profile,target_type,intent,flags);
845             if (transform == (cmsHTRANSFORM *) NULL)
846               ThrowProfileException(ImageError,"UnableToCreateColorTransform",
847                 name);
848             /*
849               Transform image as dictated by the source & target image profiles.
850             */
851             source_pixels=AcquirePixelThreadSet(image->columns,source_channels);
852             target_pixels=AcquirePixelThreadSet(image->columns,target_channels);
853             if ((source_pixels == (unsigned short **) NULL) ||
854                 (target_pixels == (unsigned short **) NULL))
855               {
856                 transform=DestroyTransformThreadSet(transform);
857                 ThrowProfileException(ResourceLimitError,
858                   "MemoryAllocationFailed",image->filename);
859               }
860             if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
861               {
862                 target_pixels=DestroyPixelThreadSet(target_pixels);
863                 source_pixels=DestroyPixelThreadSet(source_pixels);
864                 transform=DestroyTransformThreadSet(transform);
865                 if (source_profile != (cmsHPROFILE) NULL)
866                   (void) cmsCloseProfile(source_profile);
867                 if (target_profile != (cmsHPROFILE) NULL)
868                   (void) cmsCloseProfile(target_profile);
869                 return(MagickFalse);
870               }
871             if (target_colorspace == CMYKColorspace)
872               (void) SetImageColorspace(image,target_colorspace,exception);
873             status=MagickTrue;
874             progress=0;
875             image_view=AcquireAuthenticCacheView(image,exception);
876 #if defined(MAGICKCORE_OPENMP_SUPPORT)
877             #pragma omp parallel for schedule(static,4) shared(status) \
878               magick_threads(image,image,image->rows,1)
879 #endif
880             for (y=0; y < (ssize_t) image->rows; y++)
881             {
882               const int
883                 id = GetOpenMPThreadId();
884
885               MagickBooleanType
886                 sync;
887
888               register ssize_t
889                 x;
890
891               register Quantum
892                 *restrict q;
893
894               register unsigned short
895                 *p;
896
897               if (status == MagickFalse)
898                 continue;
899               q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
900                 exception);
901               if (q == (Quantum *) NULL)
902                 {
903                   status=MagickFalse;
904                   continue;
905                 }
906               p=source_pixels[id];
907               for (x=0; x < (ssize_t) image->columns; x++)
908               {
909                 *p++=ScaleQuantumToShort(GetPixelRed(image,q));
910                 if (source_channels > 1)
911                   {
912                     *p++=ScaleQuantumToShort(GetPixelGreen(image,q));
913                     *p++=ScaleQuantumToShort(GetPixelBlue(image,q));
914                   }
915                 if (source_channels > 3)
916                   *p++=ScaleQuantumToShort(GetPixelBlack(image,q));
917                 q+=GetPixelChannels(image);
918               }
919               cmsDoTransform(transform[id],source_pixels[id],target_pixels[id],
920                 (unsigned int) image->columns);
921               p=target_pixels[id];
922               q-=image->columns*GetPixelChannels(image);
923               for (x=0; x < (ssize_t) image->columns; x++)
924               {
925                 if (target_channels == 1)
926                   SetPixelGray(image,ScaleShortToQuantum(*p),q);
927                 else
928                   SetPixelRed(image,ScaleShortToQuantum(*p),q);
929                 p++;
930                 if (target_channels > 1)
931                   {
932                     SetPixelGreen(image,ScaleShortToQuantum(*p),q);
933                     p++;
934                     SetPixelBlue(image,ScaleShortToQuantum(*p),q);
935                     p++;
936                   }
937                 if (target_channels > 3)
938                   {
939                     SetPixelBlack(image,ScaleShortToQuantum(*p),q);
940                     p++;
941                   }
942                 q+=GetPixelChannels(image);
943               }
944               sync=SyncCacheViewAuthenticPixels(image_view,exception);
945               if (sync == MagickFalse)
946                 status=MagickFalse;
947               if (image->progress_monitor != (MagickProgressMonitor) NULL)
948                 {
949                   MagickBooleanType
950                     proceed;
951
952 #if defined(MAGICKCORE_OPENMP_SUPPORT)
953                   #pragma omp critical (MagickCore_ProfileImage)
954 #endif
955                   proceed=SetImageProgress(image,ProfileImageTag,progress++,
956                     image->rows);
957                   if (proceed == MagickFalse)
958                     status=MagickFalse;
959                 }
960             }
961             image_view=DestroyCacheView(image_view);
962             (void) SetImageColorspace(image,target_colorspace,exception);
963             switch (signature)
964             {
965               case cmsSigRgbData:
966               {
967                 image->type=image->alpha_trait != BlendPixelTrait ?
968                   TrueColorType : TrueColorMatteType;
969                 break;
970               }
971               case cmsSigCmykData:
972               {
973                 image->type=image->alpha_trait != BlendPixelTrait ?
974                   ColorSeparationType : ColorSeparationMatteType;
975                 break;
976               }
977               case cmsSigGrayData:
978               {
979                 image->type=image->alpha_trait != BlendPixelTrait ?
980                   GrayscaleType : GrayscaleMatteType;
981                 break;
982               }
983               default:
984                 break;
985             }
986             target_pixels=DestroyPixelThreadSet(target_pixels);
987             source_pixels=DestroyPixelThreadSet(source_pixels);
988             transform=DestroyTransformThreadSet(transform);
989             if (cmsGetDeviceClass(source_profile) != cmsSigLinkClass)
990               status=SetImageProfile(image,name,profile,exception);
991             if (target_profile != (cmsHPROFILE) NULL)
992               (void) cmsCloseProfile(target_profile);
993           }
994         (void) cmsCloseProfile(source_profile);
995       }
996 #endif
997     }
998   profile=DestroyStringInfo(profile);
999   return(status);
1000 }
1001 \f
1002 /*
1003 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1004 %                                                                             %
1005 %                                                                             %
1006 %                                                                             %
1007 %   R e m o v e I m a g e P r o f i l e                                       %
1008 %                                                                             %
1009 %                                                                             %
1010 %                                                                             %
1011 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1012 %
1013 %  RemoveImageProfile() removes a named profile from the image and returns its
1014 %  value.
1015 %
1016 %  The format of the RemoveImageProfile method is:
1017 %
1018 %      void *RemoveImageProfile(Image *image,const char *name)
1019 %
1020 %  A description of each parameter follows:
1021 %
1022 %    o image: the image.
1023 %
1024 %    o name: the profile name.
1025 %
1026 */
1027 MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
1028 {
1029   StringInfo
1030     *profile;
1031
1032   assert(image != (Image *) NULL);
1033   assert(image->signature == MagickSignature);
1034   if (image->debug != MagickFalse)
1035     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1036   if (image->profiles == (SplayTreeInfo *) NULL)
1037     return((StringInfo *) NULL);
1038   WriteTo8BimProfile(image,name,(StringInfo *) NULL);
1039   profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
1040     image->profiles,name);
1041   return(profile);
1042 }
1043 \f
1044 /*
1045 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1046 %                                                                             %
1047 %                                                                             %
1048 %                                                                             %
1049 %   R e s e t P r o f i l e I t e r a t o r                                   %
1050 %                                                                             %
1051 %                                                                             %
1052 %                                                                             %
1053 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1054 %
1055 %  ResetImageProfileIterator() resets the image profile iterator.  Use it in
1056 %  conjunction with GetNextImageProfile() to iterate over all the profiles
1057 %  associated with an image.
1058 %
1059 %  The format of the ResetImageProfileIterator method is:
1060 %
1061 %      ResetImageProfileIterator(Image *image)
1062 %
1063 %  A description of each parameter follows:
1064 %
1065 %    o image: the image.
1066 %
1067 */
1068 MagickExport void ResetImageProfileIterator(const Image *image)
1069 {
1070   assert(image != (Image *) NULL);
1071   assert(image->signature == MagickSignature);
1072   if (image->debug != MagickFalse)
1073     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1074   if (image->profiles == (SplayTreeInfo *) NULL)
1075     return;
1076   ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
1077 }
1078 \f
1079 /*
1080 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1081 %                                                                             %
1082 %                                                                             %
1083 %                                                                             %
1084 %   S e t I m a g e P r o f i l e                                             %
1085 %                                                                             %
1086 %                                                                             %
1087 %                                                                             %
1088 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1089 %
1090 %  SetImageProfile() adds a named profile to the image.  If a profile with the
1091 %  same name already exists, it is replaced.  This method differs from the
1092 %  ProfileImage() method in that it does not apply CMS color profiles.
1093 %
1094 %  The format of the SetImageProfile method is:
1095 %
1096 %      MagickBooleanType SetImageProfile(Image *image,const char *name,
1097 %        const StringInfo *profile)
1098 %
1099 %  A description of each parameter follows:
1100 %
1101 %    o image: the image.
1102 %
1103 %    o name: the profile name, for example icc, exif, and 8bim (8bim is the
1104 %      Photoshop wrapper for iptc profiles).
1105 %
1106 %    o profile: A StringInfo structure that contains the named profile.
1107 %
1108 */
1109
1110 static void *DestroyProfile(void *profile)
1111 {
1112   return((void *) DestroyStringInfo((StringInfo *) profile));
1113 }
1114
1115 static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1116   unsigned char *quantum)
1117 {
1118   *quantum=(*p++);
1119   return(p);
1120 }
1121
1122 static inline const unsigned char *ReadResourceBytes(const unsigned char *p,
1123   const ssize_t count,unsigned char *quantum)
1124 {
1125   register ssize_t
1126     i;
1127
1128   for (i=0; i < count; i++)
1129     *quantum++=(*p++);
1130   return(p);
1131 }
1132
1133 static inline const unsigned char *ReadResourceLong(const unsigned char *p,
1134   unsigned int *quantum)
1135 {
1136   *quantum=(size_t) (*p++ << 24);
1137   *quantum|=(size_t) (*p++ << 16);
1138   *quantum|=(size_t) (*p++ << 8);
1139   *quantum|=(size_t) (*p++ << 0);
1140   return(p);
1141 }
1142
1143 static inline const unsigned char *ReadResourceShort(const unsigned char *p,
1144   unsigned short *quantum)
1145 {
1146   *quantum=(unsigned short) (*p++ << 8);
1147   *quantum|=(unsigned short) (*p++ << 0);
1148   return(p);
1149 }static inline void WriteResourceLong(unsigned char *p,
1150   const unsigned int quantum)
1151 {
1152   unsigned char
1153     buffer[4];
1154
1155   buffer[0]=(unsigned char) (quantum >> 24);
1156   buffer[1]=(unsigned char) (quantum >> 16);
1157   buffer[2]=(unsigned char) (quantum >> 8);
1158   buffer[3]=(unsigned char) quantum;
1159   (void) CopyMagickMemory(p,buffer,4);
1160 }
1161
1162 static void WriteTo8BimProfile(Image *image,const char *name,
1163   const StringInfo *profile)
1164 {
1165
1166   const unsigned char
1167     *datum,
1168     *s;
1169
1170   register const unsigned char
1171     *p;
1172
1173   size_t
1174     length;
1175
1176   StringInfo
1177     *profile_8bim;
1178
1179   ssize_t
1180     count;
1181
1182   unsigned char
1183     length_byte;
1184
1185   unsigned int
1186     value;
1187
1188   unsigned short
1189     id,
1190     profile_id;
1191
1192   if (LocaleCompare(name,"icc") == 0)
1193     profile_id=0x040f;
1194   else if (LocaleCompare(name,"iptc") == 0)
1195     profile_id=0x0404;
1196   else if (LocaleCompare(name,"xmp") == 0)
1197     profile_id=0x0424;
1198   else
1199     return;
1200   profile_8bim=(StringInfo *)GetValueFromSplayTree((SplayTreeInfo *)
1201     image->profiles,"8bim");
1202   if (profile_8bim == (StringInfo *) NULL)
1203     return;
1204   datum=GetStringInfoDatum(profile_8bim);
1205   length=GetStringInfoLength(profile_8bim);
1206   for (p=datum; p < (datum+length-16); )
1207   {
1208     s=p;
1209     if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1210       break;
1211     p+=4;
1212     p=ReadResourceShort(p,&id);
1213     p=ReadResourceByte(p,&length_byte);
1214     p+=length_byte;
1215     if (((length_byte+1) & 0x01) != 0)
1216       p++;
1217     if (p > (datum+length-4))
1218       break;
1219     p=ReadResourceLong(p,&value);
1220     count=(ssize_t) value;
1221     if ((p > (datum+length-count)) || (count > length))
1222       break;
1223     if ((count & 0x01) != 0)
1224       count++;
1225     if (id == profile_id)
1226       {
1227         size_t
1228           offset,
1229           rest;
1230
1231         ssize_t
1232           new_count;
1233
1234         StringInfo
1235           *new_profile;
1236
1237         new_count=0;
1238         rest=(datum+length)-(p+count);
1239         if (profile == (StringInfo *) NULL)
1240           {
1241             offset=(s-datum);
1242             new_profile=AcquireStringInfo(offset+rest);
1243             (void) CopyMagickMemory(new_profile->datum,datum,offset);
1244           }
1245         else
1246           {
1247             offset=(p-datum);
1248             new_count=profile->length;
1249             if ((new_count & 0x01) != 0)
1250               new_count++;
1251             new_profile=AcquireStringInfo(offset+new_count+rest);
1252             (void) CopyMagickMemory(new_profile->datum,datum,offset-4);
1253             WriteResourceLong(new_profile->datum+offset-4,profile->length);
1254             (void) CopyMagickMemory(new_profile->datum+offset,profile->datum,
1255               profile->length);
1256           }
1257         (void) CopyMagickMemory(new_profile->datum+offset+new_count,p+count,
1258           rest);
1259         (void) AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1260           ConstantString("8bim"),CloneStringInfo(new_profile));
1261         new_profile=DestroyStringInfo(new_profile);
1262         break;
1263       }
1264     else
1265       p+=count;
1266   }
1267 }
1268
1269 static void GetProfilesFromResourceBlock(Image *image,
1270   const StringInfo *resource_block,ExceptionInfo *exception)
1271 {
1272   const unsigned char
1273     *datum;
1274
1275   register const unsigned char
1276     *p;
1277
1278   size_t
1279     length;
1280
1281   ssize_t
1282     count;
1283
1284   StringInfo
1285     *profile;
1286
1287   unsigned char
1288     length_byte;
1289
1290    unsigned int
1291      value;
1292
1293   unsigned short
1294     id;
1295
1296   datum=GetStringInfoDatum(resource_block);
1297   length=GetStringInfoLength(resource_block);
1298   for (p=datum; p < (datum+length-16); )
1299   {
1300     if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1301       break;
1302     p+=4;
1303     p=ReadResourceShort(p,&id);
1304     p=ReadResourceByte(p,&length_byte);
1305     p+=length_byte;
1306     if (((length_byte+1) & 0x01) != 0)
1307       p++;
1308     if (p > (datum+length-4))
1309       break;
1310     p=ReadResourceLong(p,&value);
1311     count=(ssize_t) value;
1312     if ((p > (datum+length-count)) || (count > length))
1313       break;
1314     switch (id)
1315     {
1316       case 0x03ed:
1317       {
1318         unsigned int
1319           resolution;
1320
1321         unsigned short
1322           units;
1323
1324         /*
1325           Resolution.
1326         */
1327         p=ReadResourceLong(p,&resolution);
1328         image->resolution.x=((double) resolution)/65536.0;
1329         p=ReadResourceShort(p,&units)+2;
1330         p=ReadResourceLong(p,&resolution)+4;
1331         image->resolution.y=((double) resolution)/65536.0;
1332         /*
1333           Values are always stored as pixels per inch.
1334         */
1335         if ((ResolutionType) units != PixelsPerCentimeterResolution)
1336           image->units=PixelsPerInchResolution;
1337         else
1338           {
1339             image->units=PixelsPerCentimeterResolution;
1340             image->resolution.x/=2.54;
1341             image->resolution.y/=2.54;
1342           }
1343         break;
1344       }
1345       case 0x0404:
1346       {
1347         /*
1348           IPTC Profile
1349         */
1350         profile=AcquireStringInfo(count);
1351         SetStringInfoDatum(profile,p);
1352         (void) SetImageProfileInternal(image,"iptc",profile,MagickTrue,
1353           exception);
1354         profile=DestroyStringInfo(profile);
1355         p+=count;
1356         break;
1357       }
1358       case 0x040c:
1359       {
1360         /*
1361           Thumbnail.
1362         */
1363         p+=count;
1364         break;
1365       }
1366       case 0x040f:
1367       {
1368         /*
1369           ICC Profile.
1370         */
1371         profile=AcquireStringInfo(count);
1372         SetStringInfoDatum(profile,p);
1373         (void) SetImageProfileInternal(image,"icc",profile,MagickTrue,
1374           exception);
1375         profile=DestroyStringInfo(profile);
1376         p+=count;
1377         break;
1378       }
1379       case 0x0422:
1380       {
1381         /*
1382           EXIF Profile.
1383         */
1384         profile=AcquireStringInfo(count);
1385         SetStringInfoDatum(profile,p);
1386         (void) SetImageProfileInternal(image,"exif",profile,MagickTrue,
1387           exception);
1388         profile=DestroyStringInfo(profile);
1389         p+=count;
1390         break;
1391       }
1392       case 0x0424:
1393       {
1394         /*
1395           XMP Profile.
1396         */
1397         profile=AcquireStringInfo(count);
1398         SetStringInfoDatum(profile,p);
1399         (void) SetImageProfileInternal(image,"xmp",profile,MagickTrue,
1400           exception);
1401         profile=DestroyStringInfo(profile);
1402         p+=count;
1403         break;
1404       }
1405       default:
1406       {
1407         p+=count;
1408         break;
1409       }
1410     }
1411     if ((count & 0x01) != 0)
1412       p++;
1413   }
1414 }
1415
1416 static MagickBooleanType SetImageProfileInternal(Image *image,const char *name,
1417   const StringInfo *profile,const MagickBooleanType recursive,
1418   ExceptionInfo *exception)
1419 {
1420   char
1421     key[MaxTextExtent],
1422     property[MaxTextExtent];
1423
1424   MagickBooleanType
1425     status;
1426
1427   assert(image != (Image *) NULL);
1428   assert(image->signature == MagickSignature);
1429   if (image->debug != MagickFalse)
1430     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1431   if (image->profiles == (SplayTreeInfo *) NULL)
1432     image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
1433       DestroyProfile);
1434   (void) CopyMagickString(key,name,MaxTextExtent);
1435   LocaleLower(key);
1436   status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1437     ConstantString(key),CloneStringInfo(profile));
1438   if (status != MagickFalse)
1439     {
1440       if (LocaleCompare(name,"8bim") == 0)
1441         GetProfilesFromResourceBlock(image,profile,exception);
1442       else if (recursive == MagickFalse)
1443         WriteTo8BimProfile(image,name,profile);
1444     }
1445   /*
1446     Inject profile into image properties.
1447   */
1448   (void) FormatLocaleString(property,MaxTextExtent,"%s:*",name);
1449   (void) GetImageProperty(image,property,exception);
1450   return(status);
1451 }
1452
1453 MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
1454   const StringInfo *profile,ExceptionInfo *exception)
1455 {
1456   return(SetImageProfileInternal(image,name,profile,MagickFalse,exception));
1457 }
1458 \f
1459 /*
1460 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1461 %                                                                             %
1462 %                                                                             %
1463 %                                                                             %
1464 %   S y n c I m a g e P r o f i l e s                                         %
1465 %                                                                             %
1466 %                                                                             %
1467 %                                                                             %
1468 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1469 %
1470 %  SyncImageProfiles() synchronizes image properties with the image profiles.
1471 %  Currently we only support updating the EXIF resolution and orientation.
1472 %
1473 %  The format of the SyncImageProfiles method is:
1474 %
1475 %      MagickBooleanType SyncImageProfiles(Image *image)
1476 %
1477 %  A description of each parameter follows:
1478 %
1479 %    o image: the image.
1480 %
1481 */
1482
1483 static inline int ReadProfileByte(unsigned char **p,size_t *length)
1484 {
1485   int
1486     c;
1487
1488   if (*length < 1)
1489     return(EOF);
1490   c=(int) (*(*p)++);
1491   (*length)--;
1492   return(c);
1493 }
1494
1495 static inline unsigned short ReadProfileShort(const EndianType endian,
1496   unsigned char *buffer)
1497 {
1498   unsigned short
1499     value;
1500
1501   if (endian == LSBEndian)
1502     {
1503       value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
1504       return((unsigned short) (value & 0xffff));
1505     }
1506   value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
1507     ((unsigned char *) buffer)[1]);
1508   return((unsigned short) (value & 0xffff));
1509 }
1510
1511 static inline size_t ReadProfileLong(const EndianType endian,
1512   unsigned char *buffer)
1513 {
1514   size_t
1515     value;
1516
1517   if (endian == LSBEndian)
1518     {
1519       value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
1520         (buffer[1] << 8 ) | (buffer[0]));
1521       return((size_t) (value & 0xffffffff));
1522     }
1523   value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
1524     (buffer[2] << 8) | buffer[3]);
1525   return((size_t) (value & 0xffffffff));
1526 }
1527
1528 static inline size_t ReadProfileMSBLong(unsigned char **p,
1529   size_t *length)
1530 {
1531   size_t
1532     value;
1533
1534   if (*length < 4)
1535     return(0);
1536
1537   value=ReadProfileLong(MSBEndian,*p);
1538   (*length)-=4;
1539   *p+=4;
1540   return(value);
1541 }
1542
1543 static inline unsigned short ReadProfileMSBShort(unsigned char **p,
1544   size_t *length)
1545 {
1546   unsigned short
1547     value;
1548
1549   if (*length < 2)
1550     return(0);
1551
1552   value=ReadProfileShort(MSBEndian,*p);
1553   (*length)-=2;
1554   *p+=2;
1555   return(value);
1556 }
1557
1558 static inline void WriteProfileLong(const EndianType endian,
1559   const size_t value,unsigned char *p)
1560 {
1561   unsigned char
1562     buffer[4];
1563
1564   if (endian == LSBEndian)
1565     {
1566       buffer[0]=(unsigned char) value;
1567       buffer[1]=(unsigned char) (value >> 8);
1568       buffer[2]=(unsigned char) (value >> 16);
1569       buffer[3]=(unsigned char) (value >> 24);
1570       (void) CopyMagickMemory(p,buffer,4);
1571       return;
1572     }
1573   buffer[0]=(unsigned char) (value >> 24);
1574   buffer[1]=(unsigned char) (value >> 16);
1575   buffer[2]=(unsigned char) (value >> 8);
1576   buffer[3]=(unsigned char) value;
1577   (void) CopyMagickMemory(p,buffer,4);
1578 }
1579
1580 static void WriteProfileShort(const EndianType endian,
1581   const unsigned short value,unsigned char *p)
1582 {
1583   unsigned char
1584     buffer[2];
1585
1586   if (endian == LSBEndian)
1587     {
1588       buffer[0]=(unsigned char) value;
1589       buffer[1]=(unsigned char) (value >> 8);
1590       (void) CopyMagickMemory(p,buffer,2);
1591       return;
1592     }
1593   buffer[0]=(unsigned char) (value >> 8);
1594   buffer[1]=(unsigned char) value;
1595   (void) CopyMagickMemory(p,buffer,2);
1596 }
1597
1598 static MagickBooleanType Sync8BimProfile(Image *image,StringInfo *profile)
1599 {
1600   size_t
1601     count,
1602     length;
1603
1604   unsigned char
1605     *p;
1606
1607   unsigned short
1608     id;
1609
1610   length=GetStringInfoLength(profile);
1611   p=GetStringInfoDatum(profile);
1612   while(length != 0)
1613   {
1614     if (ReadProfileByte(&p,&length) != 0x38)
1615       continue;
1616     if (ReadProfileByte(&p,&length) != 0x42)
1617       continue;
1618     if (ReadProfileByte(&p,&length) != 0x49)
1619       continue;
1620     if (ReadProfileByte(&p,&length) != 0x4D)
1621       continue;
1622     if (length < 7)
1623       return(MagickFalse);
1624     id=ReadProfileMSBShort(&p,&length);
1625     count=ReadProfileByte(&p,&length);
1626     if (count > length)
1627       return(MagickFalse);
1628     p+=count;
1629     if ((*p & 0x01) == 0)
1630       p++;
1631     count=ReadProfileMSBLong(&p,&length);
1632     if (count > length)
1633       return(MagickFalse);
1634     if (id == 0x3ED && count == 16)
1635       {
1636         if (image->units == PixelsPerCentimeterResolution)
1637           WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.x*2.54*
1638             65536.0),p);
1639         else
1640           WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.x*
1641             65536.0),p);
1642         WriteProfileShort(MSBEndian,(unsigned short) image->units,p+4);
1643         if (image->units == PixelsPerCentimeterResolution)
1644           WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.y*2.54*
1645             65536.0),p+8);
1646         else
1647           WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.y*
1648             65536.0),p+8);
1649         WriteProfileShort(MSBEndian,(unsigned short) image->units,p+12);
1650       }
1651     p+=count;
1652     length-=count;
1653   }
1654   return(MagickTrue);
1655 }
1656
1657 MagickBooleanType SyncExifProfile(Image *image,StringInfo *profile)
1658 {
1659 #define MaxDirectoryStack  16
1660 #define EXIF_DELIMITER  "\n"
1661 #define EXIF_NUM_FORMATS  12
1662 #define TAG_EXIF_OFFSET  0x8769
1663 #define TAG_INTEROP_OFFSET  0xa005
1664
1665   typedef struct _DirectoryInfo
1666   {
1667     unsigned char
1668       *directory;
1669
1670     size_t
1671       entry;
1672   } DirectoryInfo;
1673
1674   DirectoryInfo
1675     directory_stack[MaxDirectoryStack];
1676
1677   EndianType
1678     endian;
1679
1680   size_t
1681     entry,
1682     length,
1683     number_entries;
1684
1685   ssize_t
1686     id,
1687     level,
1688     offset;
1689
1690   static int
1691     format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1692
1693   unsigned char
1694     *directory,
1695     *exif;
1696
1697   /*
1698     Set EXIF resolution tag.
1699   */
1700   length=GetStringInfoLength(profile);
1701   exif=GetStringInfoDatum(profile);
1702   if (length < 16)
1703     return(MagickFalse);
1704   id=(ssize_t) ReadProfileShort(LSBEndian,exif);
1705   if ((id != 0x4949) && (id != 0x4D4D))
1706     {
1707       while (length != 0)
1708       {
1709         if (ReadProfileByte(&exif,&length) != 0x45)
1710           continue;
1711         if (ReadProfileByte(&exif,&length) != 0x78)
1712           continue;
1713         if (ReadProfileByte(&exif,&length) != 0x69)
1714           continue;
1715         if (ReadProfileByte(&exif,&length) != 0x66)
1716           continue;
1717         if (ReadProfileByte(&exif,&length) != 0x00)
1718           continue;
1719         if (ReadProfileByte(&exif,&length) != 0x00)
1720           continue;
1721         break;
1722       }
1723       if (length < 16)
1724         return(MagickFalse);
1725       id=(ssize_t) ReadProfileShort(LSBEndian,exif);
1726     }
1727   endian=LSBEndian;
1728   if (id == 0x4949)
1729     endian=LSBEndian;
1730   else
1731     if (id == 0x4D4D)
1732       endian=MSBEndian;
1733     else
1734       return(MagickFalse);
1735   if (ReadProfileShort(endian,exif+2) != 0x002a)
1736     return(MagickFalse);
1737   /*
1738     This the offset to the first IFD.
1739   */
1740   offset=(ssize_t) ((int) ReadProfileLong(endian,exif+4));
1741   if ((offset < 0) || (size_t) offset >= length)
1742     return(MagickFalse);
1743   directory=exif+offset;
1744   level=0;
1745   entry=0;
1746   do
1747   {
1748     if (level > 0)
1749       {
1750         level--;
1751         directory=directory_stack[level].directory;
1752         entry=directory_stack[level].entry;
1753       }
1754     /*
1755       Determine how many entries there are in the current IFD.
1756     */
1757     number_entries=ReadProfileShort(endian,directory);
1758     for ( ; entry < number_entries; entry++)
1759     {
1760       int
1761         components;
1762
1763       register unsigned char
1764         *p,
1765         *q;
1766
1767       size_t
1768         number_bytes;
1769
1770       ssize_t
1771         format,
1772         tag_value;
1773
1774       q=(unsigned char *) (directory+2+(12*entry));
1775       tag_value=(ssize_t) ReadProfileShort(endian,q);
1776       format=(ssize_t) ReadProfileShort(endian,q+2);
1777       if ((format-1) >= EXIF_NUM_FORMATS)
1778         break;
1779       components=(ssize_t) ((int) ReadProfileLong(endian,q+4));
1780       number_bytes=(size_t) components*format_bytes[format];
1781       if ((ssize_t) number_bytes < components)
1782         break;  /* prevent overflow */
1783       if (number_bytes <= 4)
1784         p=q+8;
1785       else
1786         {
1787           ssize_t
1788             offset;
1789
1790           /*
1791             The directory entry contains an offset.
1792           */
1793           offset=(ssize_t) ((int) ReadProfileLong(endian,q+8));
1794           if ((size_t) (offset+number_bytes) > length)
1795             continue;
1796           if (~length < number_bytes)
1797             continue;  /* prevent overflow */
1798           p=(unsigned char *) (exif+offset);
1799         }
1800       switch (tag_value)
1801       {
1802         case 0x011a:
1803         {
1804           (void) WriteProfileLong(endian,(size_t) (image->resolution.x+0.5),p);
1805           (void) WriteProfileLong(endian,1UL,p+4);
1806           break;
1807         }
1808         case 0x011b:
1809         {
1810           (void) WriteProfileLong(endian,(size_t) (image->resolution.y+0.5),p);
1811           (void) WriteProfileLong(endian,1UL,p+4);
1812           break;
1813         }
1814         case 0x0112:
1815         {
1816           if (number_bytes == 4)
1817             {
1818               (void) WriteProfileLong(endian,(size_t) image->orientation,p);
1819               break;
1820             }
1821           (void) WriteProfileShort(endian,(unsigned short) image->orientation,
1822             p);
1823           break;
1824         }
1825         case 0x0128:
1826         {
1827           if (number_bytes == 4)
1828             {
1829               (void) WriteProfileLong(endian,(size_t) (image->units+1),p);
1830               break;
1831             }
1832           (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
1833           break;
1834         }
1835         default:
1836           break;
1837       }
1838       if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
1839         {
1840           ssize_t
1841             offset;
1842
1843           offset=(ssize_t) ((int) ReadProfileLong(endian,p));
1844           if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
1845             {
1846               directory_stack[level].directory=directory;
1847               entry++;
1848               directory_stack[level].entry=entry;
1849               level++;
1850               directory_stack[level].directory=exif+offset;
1851               directory_stack[level].entry=0;
1852               level++;
1853               if ((directory+2+(12*number_entries)) > (exif+length))
1854                 break;
1855               offset=(ssize_t) ((int) ReadProfileLong(endian,directory+2+(12*
1856                 number_entries)));
1857               if ((offset != 0) && ((size_t) offset < length) &&
1858                   (level < (MaxDirectoryStack-2)))
1859                 {
1860                   directory_stack[level].directory=exif+offset;
1861                   directory_stack[level].entry=0;
1862                   level++;
1863                 }
1864             }
1865           break;
1866         }
1867     }
1868   } while (level > 0);
1869   return(MagickTrue);
1870 }
1871
1872 MagickPrivate MagickBooleanType SyncImageProfiles(Image *image)
1873 {
1874   MagickBooleanType
1875     status;
1876
1877   StringInfo
1878     *profile;
1879
1880   status=MagickTrue;
1881   profile=(StringInfo *) GetImageProfile(image,"8BIM");
1882   if (profile != (StringInfo *) NULL)
1883     if (Sync8BimProfile(image,profile) == MagickFalse)
1884       status=MagickFalse;
1885   profile=(StringInfo *) GetImageProfile(image,"EXIF");
1886   if (profile != (StringInfo *) NULL)
1887     if (SyncExifProfile(image,profile) == MagickFalse)
1888       status=MagickFalse;
1889   return(status);
1890 }