]> 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 \f
104 /*
105   Typedef declarations
106 */
107 struct _ProfileInfo
108 {
109   char
110     *name;
111
112   size_t
113     length;
114
115   unsigned char
116     *info;
117
118   size_t
119     signature;
120 };
121
122 typedef struct _CMSExceptionInfo
123 {
124   Image
125     *image;
126
127   ExceptionInfo
128     *exception;
129 } CMSExceptionInfo;
130 \f
131 /*
132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
133 %                                                                             %
134 %                                                                             %
135 %                                                                             %
136 %   C l o n e I m a g e P r o f i l e s                                       %
137 %                                                                             %
138 %                                                                             %
139 %                                                                             %
140 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
141 %
142 %  CloneImageProfiles() clones one or more image profiles.
143 %
144 %  The format of the CloneImageProfiles method is:
145 %
146 %      MagickBooleanType CloneImageProfiles(Image *image,
147 %        const Image *clone_image)
148 %
149 %  A description of each parameter follows:
150 %
151 %    o image: the image.
152 %
153 %    o clone_image: the clone image.
154 %
155 */
156 MagickExport MagickBooleanType CloneImageProfiles(Image *image,
157   const Image *clone_image)
158 {
159   assert(image != (Image *) NULL);
160   assert(image->signature == MagickSignature);
161   if (image->debug != MagickFalse)
162     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
163   assert(clone_image != (const Image *) NULL);
164   assert(clone_image->signature == MagickSignature);
165   if (clone_image->profiles != (void *) NULL)
166     {
167       if (image->profiles != (void *) NULL)
168         DestroyImageProfiles(image);
169       image->profiles=CloneSplayTree((SplayTreeInfo *) clone_image->profiles,
170         (void *(*)(void *)) ConstantString,(void *(*)(void *)) CloneStringInfo);
171     }
172   return(MagickTrue);
173 }
174 \f
175 /*
176 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
177 %                                                                             %
178 %                                                                             %
179 %                                                                             %
180 %   D e l e t e I m a g e P r o f i l e                                       %
181 %                                                                             %
182 %                                                                             %
183 %                                                                             %
184 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
185 %
186 %  DeleteImageProfile() deletes a profile from the image by its name.
187 %
188 %  The format of the DeleteImageProfile method is:
189 %
190 %      MagickBooleanTyupe DeleteImageProfile(Image *image,const char *name)
191 %
192 %  A description of each parameter follows:
193 %
194 %    o image: the image.
195 %
196 %    o name: the profile name.
197 %
198 */
199 MagickExport MagickBooleanType DeleteImageProfile(Image *image,const char *name)
200 {
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)
206     return(MagickFalse);
207   return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->profiles,name));
208 }
209 \f
210 /*
211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
212 %                                                                             %
213 %                                                                             %
214 %                                                                             %
215 %   D e s t r o y I m a g e P r o f i l e s                                   %
216 %                                                                             %
217 %                                                                             %
218 %                                                                             %
219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
220 %
221 %  DestroyImageProfiles() releases memory associated with an image profile map.
222 %
223 %  The format of the DestroyProfiles method is:
224 %
225 %      void DestroyImageProfiles(Image *image)
226 %
227 %  A description of each parameter follows:
228 %
229 %    o image: the image.
230 %
231 */
232 MagickExport void DestroyImageProfiles(Image *image)
233 {
234   if (image->profiles != (SplayTreeInfo *) NULL)
235     image->profiles=DestroySplayTree((SplayTreeInfo *) image->profiles);
236 }
237 \f
238 /*
239 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
240 %                                                                             %
241 %                                                                             %
242 %                                                                             %
243 %   G e t I m a g e P r o f i l e                                             %
244 %                                                                             %
245 %                                                                             %
246 %                                                                             %
247 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
248 %
249 %  GetImageProfile() gets a profile associated with an image by name.
250 %
251 %  The format of the GetImageProfile method is:
252 %
253 %      const StringInfo *GetImageProfile(const Image *image,const char *name)
254 %
255 %  A description of each parameter follows:
256 %
257 %    o image: the image.
258 %
259 %    o name: the profile name.
260 %
261 */
262 MagickExport const StringInfo *GetImageProfile(const Image *image,
263   const char *name)
264 {
265   char
266     key[MaxTextExtent];
267
268   const StringInfo
269     *profile;
270
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);
280   return(profile);
281 }
282 \f
283 /*
284 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
285 %                                                                             %
286 %                                                                             %
287 %                                                                             %
288 %   G e t N e x t I m a g e P r o f i l e                                     %
289 %                                                                             %
290 %                                                                             %
291 %                                                                             %
292 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
293 %
294 %  GetNextImageProfile() gets the next profile name for an image.
295 %
296 %  The format of the GetNextImageProfile method is:
297 %
298 %      char *GetNextImageProfile(const Image *image)
299 %
300 %  A description of each parameter follows:
301 %
302 %    o hash_info: the hash info.
303 %
304 */
305 MagickExport char *GetNextImageProfile(const Image *image)
306 {
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));
314 }
315 \f
316 /*
317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
318 %                                                                             %
319 %                                                                             %
320 %                                                                             %
321 %   P r o f i l e I m a g e                                                   %
322 %                                                                             %
323 %                                                                             %
324 %                                                                             %
325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
326 %
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.
331 %
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.
337 %
338 %  The format of the ProfileImage method is:
339 %
340 %      MagickBooleanType ProfileImage(Image *image,const char *name,
341 %        const void *datum,const size_t length,const MagickBooleanType clone)
342 %
343 %  A description of each parameter follows:
344 %
345 %    o image: the image.
346 %
347 %    o name: Name of profile to add or remove: ICC, IPTC, or generic profile.
348 %
349 %    o datum: the profile data.
350 %
351 %    o length: the length of the profile.
352 %
353 %    o clone: should be MagickFalse.
354 %
355 */
356
357 #if defined(MAGICKCORE_LCMS_DELEGATE)
358
359 static unsigned short **DestroyPixelThreadSet(unsigned short **pixels)
360 {
361   register ssize_t
362     i;
363
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);
369   return(pixels);
370 }
371
372 static unsigned short **AcquirePixelThreadSet(const size_t columns,
373   const size_t channels)
374 {
375   register ssize_t
376     i;
377
378   unsigned short
379     **pixels;
380
381   size_t
382     number_threads;
383
384   number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
385   pixels=(unsigned short **) AcquireQuantumMemory(number_threads,
386     sizeof(*pixels));
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++)
391   {
392     pixels[i]=(unsigned short *) AcquireQuantumMemory(columns,channels*
393       sizeof(**pixels));
394     if (pixels[i] == (unsigned short *) NULL)
395       return(DestroyPixelThreadSet(pixels));
396   }
397   return(pixels);
398 }
399
400 static cmsHTRANSFORM *DestroyTransformThreadSet(cmsHTRANSFORM *transform)
401 {
402   register ssize_t
403     i;
404
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);
410   return(transform);
411 }
412
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)
417 {
418   cmsHTRANSFORM
419     *transform;
420
421   register ssize_t
422     i;
423
424   size_t
425     number_threads;
426
427   number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
428   transform=(cmsHTRANSFORM *) AcquireQuantumMemory(number_threads,
429     sizeof(*transform));
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++)
434   {
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));
439   }
440   return(transform);
441 }
442 #endif
443
444 #if defined(MAGICKCORE_LCMS_DELEGATE)
445 #if defined(LCMS_VERSION) && (LCMS_VERSION >= 2000)
446 static void CMSExceptionHandler(cmsContext context,cmsUInt32Number severity,
447   const char *message)
448 {
449   CMSExceptionInfo
450     *cms_exception;
451
452   ExceptionInfo
453     *exception;
454
455   Image
456     *image;
457
458   cms_exception=(CMSExceptionInfo *) context;
459   image=cms_exception->image;
460   exception=cms_exception->exception;
461   if (image == (Image *) NULL)
462     {
463       (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
464         "UnableToTransformColorspace","`%s'","unknown context");
465       return;
466     }
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);
472 }
473 #else
474 static int CMSExceptionHandler(int severity,const char *message)
475 {
476   (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%d, %s",
477     severity,message != (char *) NULL ? message : "no message");
478   return(1);
479 }
480 #endif
481 #endif
482
483 MagickExport MagickBooleanType ProfileImage(Image *image,const char *name,
484   const void *datum,const size_t length,ExceptionInfo *exception)
485 {
486 #define ProfileImageTag  "Profile/Image"
487 #define ThrowProfileException(severity,tag,context) \
488 { \
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); \
494 }
495
496   MagickBooleanType
497     status;
498
499   StringInfo
500     *profile;
501
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))
508     {
509       char
510         **arguments,
511         *names;
512
513       int
514         number_arguments;
515
516       register ssize_t
517         i;
518
519       /*
520         Delete image profile(s).
521       */
522       names=ConstantString(name);
523       (void) SubstituteString(&names,","," ");
524       arguments=StringToArgv(names,&number_arguments);
525       names=DestroyString(names);
526       if (arguments == (char **) NULL)
527         return(MagickTrue);
528       ResetImageProfileIterator(image);
529       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
530       {
531         for (i=1; i < (ssize_t) number_arguments; i++)
532         {
533           if ((*arguments[i] == '!') &&
534               (LocaleCompare(name,arguments[i]+1) == 0))
535             break;
536           if (GlobExpression(name,arguments[i],MagickTrue) != MagickFalse)
537             {
538               (void) DeleteImageProfile(image,name);
539               break;
540             }
541         }
542         name=GetNextImageProfile(image);
543       }
544       for (i=0; i < (ssize_t) number_arguments; i++)
545         arguments[i]=DestroyString(arguments[i]);
546       arguments=(char **) RelinquishMagickMemory(arguments);
547       return(MagickTrue);
548     }
549   /*
550     Add a ICC, IPTC, or generic profile to the image.
551   */
552   status=MagickTrue;
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);
557   else
558     {
559       const StringInfo
560         *icc_profile;
561
562       icc_profile=GetImageProfile(image,"icc");
563       if ((icc_profile != (const StringInfo *) NULL) &&
564           (CompareStringInfo(icc_profile,profile) == 0))
565         {
566           const char
567             *value;
568
569           value=GetImageProperty(image,"exif:ColorSpace",exception);
570           (void) value;
571           /* Future.
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);
580           */
581           icc_profile=GetImageProfile(image,"icc");
582         }
583       if ((icc_profile != (const StringInfo *) NULL) &&
584           (CompareStringInfo(icc_profile,profile) == 0))
585         {
586           profile=DestroyStringInfo(profile);
587           return(MagickTrue);
588         }
589 #if !defined(MAGICKCORE_LCMS_DELEGATE)
590       (void) ThrowMagickException(exception,GetMagickModule(),
591         MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
592         "'%s' (LCMS)",image->filename);
593 #else
594       {
595         cmsHPROFILE
596           source_profile;
597
598         CMSExceptionInfo
599           cms_exception;
600
601         /*
602           Transform pixel colors as defined by the color profiles.
603         */
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);
617         else
618           {
619             CacheView
620               *image_view;
621
622             ColorspaceType
623               source_colorspace,
624               target_colorspace;
625
626             cmsColorSpaceSignature
627               signature;
628
629             cmsHPROFILE
630               target_profile;
631
632             cmsHTRANSFORM
633               *restrict transform;
634
635             cmsUInt32Number
636               flags,
637               source_type,
638               target_type;
639
640             int
641               intent;
642
643             MagickBooleanType
644               status;
645
646             MagickOffsetType
647               progress;
648
649             size_t
650               source_channels,
651               target_channels;
652
653             ssize_t
654               y;
655
656             unsigned short
657               **restrict source_pixels,
658               **restrict target_pixels;
659
660             target_profile=(cmsHPROFILE) NULL;
661             if (icc_profile != (StringInfo *) NULL)
662               {
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);
670               }
671             switch (cmsGetColorSpace(source_profile))
672             {
673               case cmsSigCmykData:
674               {
675                 source_colorspace=CMYKColorspace;
676                 source_type=(cmsUInt32Number) TYPE_CMYK_16;
677                 source_channels=4;
678                 break;
679               }
680               case cmsSigGrayData:
681               {
682                 source_colorspace=GRAYColorspace;
683                 source_type=(cmsUInt32Number) TYPE_GRAY_16;
684                 source_channels=1;
685                 break;
686               }
687               case cmsSigLabData:
688               {
689                 source_colorspace=LabColorspace;
690                 source_type=(cmsUInt32Number) TYPE_Lab_16;
691                 source_channels=3;
692                 break;
693               }
694               case cmsSigLuvData:
695               {
696                 source_colorspace=YUVColorspace;
697                 source_type=(cmsUInt32Number) TYPE_YUV_16;
698                 source_channels=3;
699                 break;
700               }
701               case cmsSigRgbData:
702               {
703                 source_colorspace=sRGBColorspace;
704                 source_type=(cmsUInt32Number) TYPE_RGB_16;
705                 source_channels=3;
706                 break;
707               }
708               case cmsSigXYZData:
709               {
710                 source_colorspace=XYZColorspace;
711                 source_type=(cmsUInt32Number) TYPE_XYZ_16;
712                 source_channels=3;
713                 break;
714               }
715               case cmsSigYCbCrData:
716               {
717                 source_colorspace=YCbCrColorspace;
718                 source_type=(cmsUInt32Number) TYPE_YCbCr_16;
719                 source_channels=3;
720                 break;
721               }
722               default:
723               {
724                 source_colorspace=UndefinedColorspace;
725                 source_type=(cmsUInt32Number) TYPE_RGB_16;
726                 source_channels=3;
727                 break;
728               }
729             }
730             signature=cmsGetPCS(source_profile);
731             if (target_profile != (cmsHPROFILE) NULL)
732               signature=cmsGetColorSpace(target_profile);
733             switch (signature)
734             {
735               case cmsSigCmykData:
736               {
737                 target_colorspace=CMYKColorspace;
738                 target_type=(cmsUInt32Number) TYPE_CMYK_16;
739                 target_channels=4;
740                 break;
741               }
742               case cmsSigLabData:
743               {
744                 target_colorspace=LabColorspace;
745                 target_type=(cmsUInt32Number) TYPE_Lab_16;
746                 target_channels=3;
747                 break;
748               }
749               case cmsSigGrayData:
750               {
751                 target_colorspace=GRAYColorspace;
752                 target_type=(cmsUInt32Number) TYPE_GRAY_16;
753                 target_channels=1;
754                 break;
755               }
756               case cmsSigLuvData:
757               {
758                 target_colorspace=YUVColorspace;
759                 target_type=(cmsUInt32Number) TYPE_YUV_16;
760                 target_channels=3;
761                 break;
762               }
763               case cmsSigRgbData:
764               {
765                 target_colorspace=sRGBColorspace;
766                 target_type=(cmsUInt32Number) TYPE_RGB_16;
767                 target_channels=3;
768                 break;
769               }
770               case cmsSigXYZData:
771               {
772                 target_colorspace=XYZColorspace;
773                 target_type=(cmsUInt32Number) TYPE_XYZ_16;
774                 target_channels=3;
775                 break;
776               }
777               case cmsSigYCbCrData:
778               {
779                 target_colorspace=YCbCrColorspace;
780                 target_type=(cmsUInt32Number) TYPE_YCbCr_16;
781                 target_channels=3;
782                 break;
783               }
784               default:
785               {
786                 target_colorspace=UndefinedColorspace;
787                 target_type=(cmsUInt32Number) TYPE_RGB_16;
788                 target_channels=3;
789                 break;
790               }
791             }
792             if ((source_colorspace == UndefinedColorspace) ||
793                 (target_colorspace == UndefinedColorspace))
794               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
795                 name);
796              if ((source_colorspace == GRAYColorspace) &&
797                  (IsImageGray(image,exception) == MagickFalse))
798               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
799                 name);
800              if ((source_colorspace == CMYKColorspace) &&
801                  (image->colorspace != CMYKColorspace))
802               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
803                 name);
804              if ((source_colorspace == XYZColorspace) &&
805                  (image->colorspace != XYZColorspace))
806               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
807                 name);
808              if ((source_colorspace == YCbCrColorspace) &&
809                  (image->colorspace != YCbCrColorspace))
810               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
811                 name);
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",
818                 name);
819             switch (image->rendering_intent)
820             {
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;
826             }
827             flags=cmsFLAGS_HIGHRESPRECALC;
828 #if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
829             if (image->black_point_compensation != MagickFalse)
830               flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
831 #endif
832             transform=AcquireTransformThreadSet(image,source_profile,
833               source_type,target_profile,target_type,intent,flags);
834             if (transform == (cmsHTRANSFORM *) NULL)
835               ThrowProfileException(ImageError,"UnableToCreateColorTransform",
836                 name);
837             /*
838               Transform image as dictated by the source & target image profiles.
839             */
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))
844               {
845                 transform=DestroyTransformThreadSet(transform);
846                 ThrowProfileException(ResourceLimitError,
847                   "MemoryAllocationFailed",image->filename);
848               }
849             if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
850               {
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);
858                 return(MagickFalse);
859               }
860             if (target_colorspace == CMYKColorspace)
861               (void) SetImageColorspace(image,target_colorspace,exception);
862             status=MagickTrue;
863             progress=0;
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)
868 #endif
869             for (y=0; y < (ssize_t) image->rows; y++)
870             {
871               const int
872                 id = GetOpenMPThreadId();
873
874               MagickBooleanType
875                 sync;
876
877               register ssize_t
878                 x;
879
880               register Quantum
881                 *restrict q;
882
883               register unsigned short
884                 *p;
885
886               if (status == MagickFalse)
887                 continue;
888               q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
889                 exception);
890               if (q == (Quantum *) NULL)
891                 {
892                   status=MagickFalse;
893                   continue;
894                 }
895               p=source_pixels[id];
896               for (x=0; x < (ssize_t) image->columns; x++)
897               {
898                 *p++=ScaleQuantumToShort(GetPixelRed(image,q));
899                 if (source_channels > 1)
900                   {
901                     *p++=ScaleQuantumToShort(GetPixelGreen(image,q));
902                     *p++=ScaleQuantumToShort(GetPixelBlue(image,q));
903                   }
904                 if (source_channels > 3)
905                   *p++=ScaleQuantumToShort(GetPixelBlack(image,q));
906                 q+=GetPixelChannels(image);
907               }
908               cmsDoTransform(transform[id],source_pixels[id],target_pixels[id],
909                 (unsigned int) image->columns);
910               p=target_pixels[id];
911               q-=image->columns*GetPixelChannels(image);
912               for (x=0; x < (ssize_t) image->columns; x++)
913               {
914                 if (target_channels == 1)
915                   SetPixelGray(image,ScaleShortToQuantum(*p),q);
916                 else
917                   SetPixelRed(image,ScaleShortToQuantum(*p),q);
918                 p++;
919                 if (target_channels > 1)
920                   {
921                     SetPixelGreen(image,ScaleShortToQuantum(*p),q);
922                     p++;
923                     SetPixelBlue(image,ScaleShortToQuantum(*p),q);
924                     p++;
925                   }
926                 if (target_channels > 3)
927                   {
928                     SetPixelBlack(image,ScaleShortToQuantum(*p),q);
929                     p++;
930                   }
931                 q+=GetPixelChannels(image);
932               }
933               sync=SyncCacheViewAuthenticPixels(image_view,exception);
934               if (sync == MagickFalse)
935                 status=MagickFalse;
936               if (image->progress_monitor != (MagickProgressMonitor) NULL)
937                 {
938                   MagickBooleanType
939                     proceed;
940
941 #if defined(MAGICKCORE_OPENMP_SUPPORT)
942                   #pragma omp critical (MagickCore_ProfileImage)
943 #endif
944                   proceed=SetImageProgress(image,ProfileImageTag,progress++,
945                     image->rows);
946                   if (proceed == MagickFalse)
947                     status=MagickFalse;
948                 }
949             }
950             image_view=DestroyCacheView(image_view);
951             (void) SetImageColorspace(image,target_colorspace,exception);
952             switch (signature)
953             {
954               case cmsSigRgbData:
955               {
956                 image->type=image->alpha_trait != BlendPixelTrait ?
957                   TrueColorType : TrueColorMatteType;
958                 break;
959               }
960               case cmsSigCmykData:
961               {
962                 image->type=image->alpha_trait != BlendPixelTrait ?
963                   ColorSeparationType : ColorSeparationMatteType;
964                 break;
965               }
966               case cmsSigGrayData:
967               {
968                 image->type=image->alpha_trait != BlendPixelTrait ?
969                   GrayscaleType : GrayscaleMatteType;
970                 break;
971               }
972               default:
973                 break;
974             }
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);
982           }
983         (void) cmsCloseProfile(source_profile);
984       }
985 #endif
986     }
987   profile=DestroyStringInfo(profile);
988   return(status);
989 }
990 \f
991 /*
992 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
993 %                                                                             %
994 %                                                                             %
995 %                                                                             %
996 %   R e m o v e I m a g e P r o f i l e                                       %
997 %                                                                             %
998 %                                                                             %
999 %                                                                             %
1000 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1001 %
1002 %  RemoveImageProfile() removes a named profile from the image and returns its
1003 %  value.
1004 %
1005 %  The format of the RemoveImageProfile method is:
1006 %
1007 %      void *RemoveImageProfile(Image *image,const char *name)
1008 %
1009 %  A description of each parameter follows:
1010 %
1011 %    o image: the image.
1012 %
1013 %    o name: the profile name.
1014 %
1015 */
1016 MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
1017 {
1018   StringInfo
1019     *profile;
1020
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);
1029   return(profile);
1030 }
1031 \f
1032 /*
1033 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1034 %                                                                             %
1035 %                                                                             %
1036 %                                                                             %
1037 %   R e s e t P r o f i l e I t e r a t o r                                   %
1038 %                                                                             %
1039 %                                                                             %
1040 %                                                                             %
1041 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1042 %
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.
1046 %
1047 %  The format of the ResetImageProfileIterator method is:
1048 %
1049 %      ResetImageProfileIterator(Image *image)
1050 %
1051 %  A description of each parameter follows:
1052 %
1053 %    o image: the image.
1054 %
1055 */
1056 MagickExport void ResetImageProfileIterator(const Image *image)
1057 {
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)
1063     return;
1064   ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
1065 }
1066 \f
1067 /*
1068 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1069 %                                                                             %
1070 %                                                                             %
1071 %                                                                             %
1072 %   S e t I m a g e P r o f i l e                                             %
1073 %                                                                             %
1074 %                                                                             %
1075 %                                                                             %
1076 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1077 %
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.
1081 %
1082 %  The format of the SetImageProfile method is:
1083 %
1084 %      MagickBooleanType SetImageProfile(Image *image,const char *name,
1085 %        const StringInfo *profile)
1086 %
1087 %  A description of each parameter follows:
1088 %
1089 %    o image: the image.
1090 %
1091 %    o name: the profile name, for example icc, exif, and 8bim (8bim is the
1092 %      Photoshop wrapper for iptc profiles).
1093 %
1094 %    o profile: A StringInfo structure that contains the named profile.
1095 %
1096 */
1097
1098 static void *DestroyProfile(void *profile)
1099 {
1100   return((void *) DestroyStringInfo((StringInfo *) profile));
1101 }
1102
1103 static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1104   unsigned char *quantum)
1105 {
1106   *quantum=(*p++);
1107   return(p);
1108 }
1109
1110 static inline const unsigned char *ReadResourceBytes(const unsigned char *p,
1111   const ssize_t count,unsigned char *quantum)
1112 {
1113   register ssize_t
1114     i;
1115
1116   for (i=0; i < count; i++)
1117     *quantum++=(*p++);
1118   return(p);
1119 }
1120
1121 static inline const unsigned char *ReadResourceLong(const unsigned char *p,
1122   size_t *quantum)
1123 {
1124   *quantum=(size_t) (*p++ << 24);
1125   *quantum|=(size_t) (*p++ << 16);
1126   *quantum|=(size_t) (*p++ << 8);
1127   *quantum|=(size_t) (*p++ << 0);
1128   return(p);
1129 }
1130
1131 static inline const unsigned char *ReadResourceShort(const unsigned char *p,
1132   unsigned short *quantum)
1133 {
1134   *quantum=(unsigned short) (*p++ << 8);
1135   *quantum|=(unsigned short) (*p++ << 0);
1136   return(p);
1137 }
1138
1139 static MagickBooleanType GetProfilesFromResourceBlock(Image *image,
1140   const StringInfo *resource_block,ExceptionInfo *exception)
1141 {
1142   const unsigned char
1143     *datum;
1144
1145   register const unsigned char
1146     *p;
1147
1148   size_t
1149     length;
1150
1151   StringInfo
1152     *profile;
1153
1154   unsigned char
1155     length_byte;
1156
1157   size_t
1158     count;
1159
1160   unsigned short
1161     id;
1162
1163   datum=GetStringInfoDatum(resource_block);
1164   length=GetStringInfoLength(resource_block);
1165   for (p=datum; p < (datum+length-16); )
1166   {
1167     if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1168       break;
1169     p+=4;
1170     p=ReadResourceShort(p,&id);
1171     p=ReadResourceByte(p,&length_byte);
1172     p+=length_byte;
1173     if (((length_byte+1) & 0x01) != 0)
1174       p++;
1175     if (p > (datum+length-4))
1176       break;
1177     p=ReadResourceLong(p,&count);
1178     if ((p > (datum+length-count)) || (count > length))
1179       break;
1180     switch (id)
1181     {
1182       case 0x03ed:
1183       {
1184         unsigned short
1185           resolution;
1186
1187         /*
1188           Resolution.
1189         */
1190         p=ReadResourceShort(p,&resolution)+6;
1191         image->resolution.x=(double) resolution;
1192         p=ReadResourceShort(p,&resolution)+6;
1193         image->resolution.y=(double) resolution;
1194         break;
1195       }
1196       case 0x0404:
1197       {
1198         /*
1199           IPTC Profile
1200         */
1201         profile=AcquireStringInfo(count);
1202         SetStringInfoDatum(profile,p);
1203         (void) SetImageProfile(image,"iptc",profile,exception);
1204         profile=DestroyStringInfo(profile);
1205         p+=count;
1206         break;
1207       }
1208       case 0x040c:
1209       {
1210         /*
1211           Thumbnail.
1212         */
1213         p+=count;
1214         break;
1215       }
1216       case 0x040f:
1217       {
1218         /*
1219           ICC Profile.
1220         */
1221         profile=AcquireStringInfo(count);
1222         SetStringInfoDatum(profile,p);
1223         (void) SetImageProfile(image,"icc",profile,exception);
1224         profile=DestroyStringInfo(profile);
1225         p+=count;
1226         break;
1227       }
1228       case 0x0422:
1229       {
1230         /*
1231           EXIF Profile.
1232         */
1233         profile=AcquireStringInfo(count);
1234         SetStringInfoDatum(profile,p);
1235         (void) SetImageProfile(image,"exif",profile,exception);
1236         profile=DestroyStringInfo(profile);
1237         p+=count;
1238         break;
1239       }
1240       case 0x0424:
1241       {
1242         /*
1243           XMP Profile.
1244         */
1245         profile=AcquireStringInfo(count);
1246         SetStringInfoDatum(profile,p);
1247         (void) SetImageProfile(image,"xmp",profile,exception);
1248         profile=DestroyStringInfo(profile);
1249         p+=count;
1250         break;
1251       }
1252       default:
1253       {
1254         p+=count;
1255         break;
1256       }
1257     }
1258     if ((count & 0x01) != 0)
1259       p++;
1260   }
1261   return(MagickTrue);
1262 }
1263
1264 MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
1265   const StringInfo *profile,ExceptionInfo *exception)
1266 {
1267   char
1268     key[MaxTextExtent],
1269     property[MaxTextExtent];
1270
1271   MagickBooleanType
1272     status;
1273
1274   assert(image != (Image *) NULL);
1275   assert(image->signature == MagickSignature);
1276   if (image->debug != MagickFalse)
1277     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1278   if (image->profiles == (SplayTreeInfo *) NULL)
1279     image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
1280       DestroyProfile);
1281   (void) CopyMagickString(key,name,MaxTextExtent);
1282   status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1283     ConstantString(key),CloneStringInfo(profile));
1284   if ((status != MagickFalse) &&
1285       ((LocaleCompare(name,"iptc") == 0) || (LocaleCompare(name,"8bim") == 0)))
1286     (void) GetProfilesFromResourceBlock(image,profile,exception);
1287   /*
1288     Inject profile into image properties.
1289   */
1290   (void) FormatLocaleString(property,MaxTextExtent,"%s:*",name);
1291   (void) GetImageProperty(image,property,exception);
1292   return(status);
1293 }
1294 \f
1295 /*
1296 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1297 %                                                                             %
1298 %                                                                             %
1299 %                                                                             %
1300 %   S y n c I m a g e P r o f i l e s                                         %
1301 %                                                                             %
1302 %                                                                             %
1303 %                                                                             %
1304 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1305 %
1306 %  SyncImageProfiles() synchronizes image properties with the image profiles.
1307 %  Currently we only support updating the EXIF resolution and orientation.
1308 %
1309 %  The format of the SyncImageProfiles method is:
1310 %
1311 %      MagickBooleanType SyncImageProfiles(Image *image)
1312 %
1313 %  A description of each parameter follows:
1314 %
1315 %    o image: the image.
1316 %
1317 */
1318
1319 static inline int ReadProfileByte(unsigned char **p,size_t *length)
1320 {
1321   int
1322     c;
1323
1324   if (*length < 1)
1325     return(EOF);
1326   c=(int) (*(*p)++);
1327   (*length)--;
1328   return(c);
1329 }
1330
1331 static inline unsigned short ReadProfileShort(const EndianType endian,
1332   unsigned char *buffer)
1333 {
1334   unsigned short
1335     value;
1336
1337   if (endian == LSBEndian)
1338     {
1339       value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
1340       return((unsigned short) (value & 0xffff));
1341     }
1342   value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
1343     ((unsigned char *) buffer)[1]);
1344   return((unsigned short) (value & 0xffff));
1345 }
1346
1347 static inline size_t ReadProfileLong(const EndianType endian,
1348   unsigned char *buffer)
1349 {
1350   size_t
1351     value;
1352
1353   if (endian == LSBEndian)
1354     {
1355       value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
1356         (buffer[1] << 8 ) | (buffer[0]));
1357       return((size_t) (value & 0xffffffff));
1358     }
1359   value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
1360     (buffer[2] << 8) | buffer[3]);
1361   return((size_t) (value & 0xffffffff));
1362 }
1363
1364 static inline void WriteProfileLong(const EndianType endian,
1365   const size_t value,unsigned char *p)
1366 {
1367   unsigned char
1368     buffer[4];
1369
1370   if (endian == LSBEndian)
1371     {
1372       buffer[0]=(unsigned char) value;
1373       buffer[1]=(unsigned char) (value >> 8);
1374       buffer[2]=(unsigned char) (value >> 16);
1375       buffer[3]=(unsigned char) (value >> 24);
1376       (void) CopyMagickMemory(p,buffer,4);
1377       return;
1378     }
1379   buffer[0]=(unsigned char) (value >> 24);
1380   buffer[1]=(unsigned char) (value >> 16);
1381   buffer[2]=(unsigned char) (value >> 8);
1382   buffer[3]=(unsigned char) value;
1383   (void) CopyMagickMemory(p,buffer,4);
1384 }
1385
1386 static void WriteProfileShort(const EndianType endian,
1387   const unsigned short value,unsigned char *p)
1388 {
1389   unsigned char
1390     buffer[2];
1391
1392   if (endian == LSBEndian)
1393     {
1394       buffer[0]=(unsigned char) value;
1395       buffer[1]=(unsigned char) (value >> 8);
1396       (void) CopyMagickMemory(p,buffer,2);
1397       return;
1398     }
1399   buffer[0]=(unsigned char) (value >> 8);
1400   buffer[1]=(unsigned char) value;
1401   (void) CopyMagickMemory(p,buffer,2);
1402 }
1403
1404 MagickPrivate MagickBooleanType SyncImageProfiles(Image *image)
1405 {
1406 #define MaxDirectoryStack  16
1407 #define EXIF_DELIMITER  "\n"
1408 #define EXIF_NUM_FORMATS  12
1409 #define TAG_EXIF_OFFSET  0x8769
1410 #define TAG_INTEROP_OFFSET  0xa005
1411
1412   typedef struct _DirectoryInfo
1413   {
1414     unsigned char
1415       *directory;
1416
1417     size_t
1418       entry;
1419   } DirectoryInfo;
1420
1421   DirectoryInfo
1422     directory_stack[MaxDirectoryStack];
1423
1424   EndianType
1425     endian;
1426
1427   size_t
1428     entry,
1429     length,
1430     number_entries;
1431
1432   ssize_t
1433     id,
1434     level,
1435     offset;
1436
1437   static int
1438     format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1439
1440   StringInfo
1441     *profile;
1442
1443   unsigned char
1444     *directory,
1445     *exif;
1446
1447   /*
1448     Set EXIF resolution tag.
1449   */
1450   profile=(StringInfo *) GetImageProfile(image,"EXIF");
1451   if (profile == (StringInfo *) NULL)
1452     return(MagickTrue);
1453   length=GetStringInfoLength(profile);
1454   exif=GetStringInfoDatum(profile);
1455   while (length != 0)
1456   {
1457     if (ReadProfileByte(&exif,&length) != 0x45)
1458       continue;
1459     if (ReadProfileByte(&exif,&length) != 0x78)
1460       continue;
1461     if (ReadProfileByte(&exif,&length) != 0x69)
1462       continue;
1463     if (ReadProfileByte(&exif,&length) != 0x66)
1464       continue;
1465     if (ReadProfileByte(&exif,&length) != 0x00)
1466       continue;
1467     if (ReadProfileByte(&exif,&length) != 0x00)
1468       continue;
1469     break;
1470   }
1471   if (length < 16)
1472     return(MagickFalse);
1473   id=(ssize_t) ReadProfileShort(LSBEndian,exif);
1474   endian=LSBEndian;
1475   if (id == 0x4949)
1476     endian=LSBEndian;
1477   else
1478     if (id == 0x4D4D)
1479       endian=MSBEndian;
1480     else
1481       return(MagickFalse);
1482   if (ReadProfileShort(endian,exif+2) != 0x002a)
1483     return(MagickFalse);
1484   /*
1485     This the offset to the first IFD.
1486   */
1487   offset=(ssize_t) ((int) ReadProfileLong(endian,exif+4));
1488   if ((offset < 0) || (size_t) offset >= length)
1489     return(MagickFalse);
1490   directory=exif+offset;
1491   level=0;
1492   entry=0;
1493   do
1494   {
1495     if (level > 0)
1496       {
1497         level--;
1498         directory=directory_stack[level].directory;
1499         entry=directory_stack[level].entry;
1500       }
1501     /*
1502       Determine how many entries there are in the current IFD.
1503     */
1504     number_entries=ReadProfileShort(endian,directory);
1505     for ( ; entry < number_entries; entry++)
1506     {
1507       int
1508         components;
1509
1510       register unsigned char
1511         *p,
1512         *q;
1513
1514       size_t
1515         number_bytes;
1516
1517       ssize_t
1518         format,
1519         tag_value;
1520
1521       q=(unsigned char *) (directory+2+(12*entry));
1522       tag_value=(ssize_t) ReadProfileShort(endian,q);
1523       format=(ssize_t) ReadProfileShort(endian,q+2);
1524       if ((format-1) >= EXIF_NUM_FORMATS)
1525         break;
1526       components=(ssize_t) ((int) ReadProfileLong(endian,q+4));
1527       number_bytes=(size_t) components*format_bytes[format];
1528       if ((ssize_t) number_bytes < components)
1529         break;  /* prevent overflow */
1530       if (number_bytes <= 4)
1531         p=q+8;
1532       else
1533         {
1534           ssize_t
1535             offset;
1536
1537           /*
1538             The directory entry contains an offset.
1539           */
1540           offset=(ssize_t) ((int) ReadProfileLong(endian,q+8));
1541           if ((size_t) (offset+number_bytes) > length)
1542             continue;
1543           if (~length < number_bytes)
1544             continue;  /* prevent overflow */
1545           p=(unsigned char *) (exif+offset);
1546         }
1547       switch (tag_value)
1548       {
1549         case 0x011a:
1550         {
1551           (void) WriteProfileLong(endian,(size_t) (image->resolution.x+0.5),p);
1552           (void) WriteProfileLong(endian,1UL,p+4);
1553           break;
1554         }
1555         case 0x011b:
1556         {
1557           (void) WriteProfileLong(endian,(size_t) (image->resolution.y+0.5),p);
1558           (void) WriteProfileLong(endian,1UL,p+4);
1559           break;
1560         }
1561         case 0x0112:
1562         {
1563           if (number_bytes == 4)
1564             {
1565               (void) WriteProfileLong(endian,(size_t) image->orientation,p);
1566               break;
1567             }
1568           (void) WriteProfileShort(endian,(unsigned short) image->orientation,
1569             p);
1570           break;
1571         }
1572         case 0x0128:
1573         {
1574           if (number_bytes == 4)
1575             {
1576               (void) WriteProfileLong(endian,(size_t) (image->units+1),p);
1577               break;
1578             }
1579           (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
1580           break;
1581         }
1582         default:
1583           break;
1584       }
1585       if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
1586         {
1587           ssize_t
1588             offset;
1589
1590           offset=(ssize_t) ((int) ReadProfileLong(endian,p));
1591           if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
1592             {
1593               directory_stack[level].directory=directory;
1594               entry++;
1595               directory_stack[level].entry=entry;
1596               level++;
1597               directory_stack[level].directory=exif+offset;
1598               directory_stack[level].entry=0;
1599               level++;
1600               if ((directory+2+(12*number_entries)) > (exif+length))
1601                 break;
1602               offset=(ssize_t) ((int) ReadProfileLong(endian,directory+2+(12*
1603                 number_entries)));
1604               if ((offset != 0) && ((size_t) offset < length) &&
1605                   (level < (MaxDirectoryStack-2)))
1606                 {
1607                   directory_stack[level].directory=exif+offset;
1608                   directory_stack[level].entry=0;
1609                   level++;
1610                 }
1611             }
1612           break;
1613         }
1614     }
1615   } while (level > 0);
1616   return(MagickTrue);
1617 }