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