profile.c revision 9357bdd9a30c3d65ef8812e45220f7552dc4376b
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-2012 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
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
82/*
83  Define declarations.
84*/
85#if !defined(LCMS_VERSION) || (LCMS_VERSION < 2000)
86#define cmsSigCmykData icSigCmykData
87#define cmsSigGrayData icSigGrayData
88#define cmsSigLabData icSigLabData
89#define cmsSigLuvData icSigLuvData
90#define cmsSigRgbData icSigRgbData
91#define cmsSigXYZData icSigXYZData
92#define cmsSigYCbCrData icSigYCbCrData
93#define cmsSigLinkClass icSigLinkClass
94#define cmsColorSpaceSignature icColorSpaceSignature
95#define cmsUInt32Number  DWORD
96#define cmsSetLogErrorHandler(handler)  cmsSetErrorHandler(handler)
97#define cmsCreateTransformTHR(context,source_profile,source_type, \
98  target_profile,target_type,intent,flags)  cmsCreateTransform(source_profile, \
99  source_type,target_profile,target_type,intent,flags);
100#define cmsOpenProfileFromMemTHR(context,profile,length) \
101  cmsOpenProfileFromMem(profile,length)
102#endif
103
104/*
105  Typedef declarations
106*/
107struct _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
122typedef struct _CMSExceptionInfo
123{
124  Image
125    *image;
126
127  ExceptionInfo
128    *exception;
129} CMSExceptionInfo;
130
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*/
156MagickExport 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
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*/
195MagickExport 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
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*/
228MagickExport void DestroyImageProfiles(Image *image)
229{
230  if (image->profiles != (SplayTreeInfo *) NULL)
231    image->profiles=DestroySplayTree((SplayTreeInfo *) image->profiles);
232}
233
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*/
258MagickExport 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
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*/
301MagickExport 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
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
355static 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
368static 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
396static 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
409static 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)
442static 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
470static 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
479MagickExport 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,exception);
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->matte == MagickFalse ? TrueColorType :
953                  TrueColorMatteType;
954                break;
955              }
956              case cmsSigCmykData:
957              {
958                image->type=image->matte == MagickFalse ? ColorSeparationType :
959                  ColorSeparationMatteType;
960                break;
961              }
962              case cmsSigGrayData:
963              {
964                image->type=image->matte == MagickFalse ? 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
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*/
1012MagickExport 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
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*/
1052MagickExport 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
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
1094static void *DestroyProfile(void *profile)
1095{
1096  return((void *) DestroyStringInfo((StringInfo *) profile));
1097}
1098
1099static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1100  unsigned char *quantum)
1101{
1102  *quantum=(*p++);
1103  return(p);
1104}
1105
1106static 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
1117static 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
1127static 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
1135static 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
1260MagickExport 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
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
1315static 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
1327static inline unsigned short ReadProfileShort(const EndianType endian,
1328  unsigned char *buffer)
1329{
1330  unsigned short
1331    value;
1332
1333  if (endian == MSBEndian)
1334    {
1335      value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
1336        ((unsigned char *) buffer)[1]);
1337      return((unsigned short) (value & 0xffff));
1338    }
1339  value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
1340  return((unsigned short) (value & 0xffff));
1341}
1342
1343static inline size_t ReadProfileLong(const EndianType endian,
1344  unsigned char *buffer)
1345{
1346  size_t
1347    value;
1348
1349  if (endian == MSBEndian)
1350    {
1351      value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
1352        (buffer[2] << 8) | buffer[3]);
1353      return((size_t) (value & 0xffffffff));
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
1360static 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 == MSBEndian)
1367    {
1368      buffer[0]=(unsigned char) (value >> 24);
1369      buffer[1]=(unsigned char) (value >> 16);
1370      buffer[2]=(unsigned char) (value >> 8);
1371      buffer[3]=(unsigned char) value;
1372      (void) CopyMagickMemory(p,buffer,4);
1373      return;
1374    }
1375  buffer[0]=(unsigned char) value;
1376  buffer[1]=(unsigned char) (value >> 8);
1377  buffer[2]=(unsigned char) (value >> 16);
1378  buffer[3]=(unsigned char) (value >> 24);
1379  (void) CopyMagickMemory(p,buffer,4);
1380}
1381
1382static void WriteProfileShort(const EndianType endian,
1383  const unsigned short value,unsigned char *p)
1384{
1385  unsigned char
1386    buffer[2];
1387
1388  if (endian == MSBEndian)
1389    {
1390      buffer[0]=(unsigned char) (value >> 8);
1391      buffer[1]=(unsigned char) value;
1392      (void) CopyMagickMemory(p,buffer,2);
1393      return;
1394    }
1395  buffer[0]=(unsigned char) value;
1396  buffer[1]=(unsigned char) (value >> 8);
1397  (void) CopyMagickMemory(p,buffer,2);
1398}
1399
1400MagickPrivate 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}
1614