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