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