profile.c revision 9a00c14cabed91238e6418c255871ed7d9e50a82
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((cmsContext) image,source_profile,
436      source_type,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((cmsContext) &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((cmsContext)
665                  &cms_exception,GetStringInfoDatum(icc_profile),
666                  (cmsUInt32Number) 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  unsigned int *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  ssize_t
1152    count;
1153
1154  StringInfo
1155    *profile;
1156
1157  unsigned char
1158    length_byte;
1159
1160   unsigned int
1161     value;
1162
1163  unsigned short
1164    id;
1165
1166  datum=GetStringInfoDatum(resource_block);
1167  length=GetStringInfoLength(resource_block);
1168  for (p=datum; p < (datum+length-16); )
1169  {
1170    if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1171      break;
1172    p+=4;
1173    p=ReadResourceShort(p,&id);
1174    p=ReadResourceByte(p,&length_byte);
1175    p+=length_byte;
1176    if (((length_byte+1) & 0x01) != 0)
1177      p++;
1178    if (p > (datum+length-4))
1179      break;
1180    p=ReadResourceLong(p,&value);
1181    count=(ssize_t) value;
1182    if ((p > (datum+length-count)) || (count > length))
1183      break;
1184    switch (id)
1185    {
1186      case 0x03ed:
1187      {
1188        unsigned int
1189          resolution;
1190
1191        unsigned short
1192          units;
1193
1194        /*
1195          Resolution.
1196        */
1197        p=ReadResourceLong(p,&resolution);
1198        image->resolution.x=((double) resolution)/65536.0;
1199        p=ReadResourceShort(p,&units)+2;
1200        p=ReadResourceLong(p,&resolution)+4;
1201        image->resolution.y=((double) resolution)/65536.0;
1202        /*
1203          Values are always stored as pixels per inch.
1204        */
1205        if ((ResolutionType) units != PixelsPerCentimeterResolution)
1206          image->units=PixelsPerInchResolution;
1207        else
1208          {
1209            image->units=PixelsPerCentimeterResolution;
1210            image->resolution.x/=2.54;
1211            image->resolution.y/=2.54;
1212          }
1213        break;
1214      }
1215      case 0x0404:
1216      {
1217        /*
1218          IPTC Profile
1219        */
1220        profile=AcquireStringInfo(count);
1221        SetStringInfoDatum(profile,p);
1222        (void) SetImageProfile(image,"iptc",profile,exception);
1223        profile=DestroyStringInfo(profile);
1224        p+=count;
1225        break;
1226      }
1227      case 0x040c:
1228      {
1229        /*
1230          Thumbnail.
1231        */
1232        p+=count;
1233        break;
1234      }
1235      case 0x040f:
1236      {
1237        /*
1238          ICC Profile.
1239        */
1240        profile=AcquireStringInfo(count);
1241        SetStringInfoDatum(profile,p);
1242        (void) SetImageProfile(image,"icc",profile,exception);
1243        profile=DestroyStringInfo(profile);
1244        p+=count;
1245        break;
1246      }
1247      case 0x0422:
1248      {
1249        /*
1250          EXIF Profile.
1251        */
1252        profile=AcquireStringInfo(count);
1253        SetStringInfoDatum(profile,p);
1254        (void) SetImageProfile(image,"exif",profile,exception);
1255        profile=DestroyStringInfo(profile);
1256        p+=count;
1257        break;
1258      }
1259      case 0x0424:
1260      {
1261        /*
1262          XMP Profile.
1263        */
1264        profile=AcquireStringInfo(count);
1265        SetStringInfoDatum(profile,p);
1266        (void) SetImageProfile(image,"xmp",profile,exception);
1267        profile=DestroyStringInfo(profile);
1268        p+=count;
1269        break;
1270      }
1271      default:
1272      {
1273        p+=count;
1274        break;
1275      }
1276    }
1277    if ((count & 0x01) != 0)
1278      p++;
1279  }
1280  return(MagickTrue);
1281}
1282
1283MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
1284  const StringInfo *profile,ExceptionInfo *exception)
1285{
1286  char
1287    key[MaxTextExtent],
1288    property[MaxTextExtent];
1289
1290  MagickBooleanType
1291    status;
1292
1293  assert(image != (Image *) NULL);
1294  assert(image->signature == MagickSignature);
1295  if (image->debug != MagickFalse)
1296    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1297  if (image->profiles == (SplayTreeInfo *) NULL)
1298    image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
1299      DestroyProfile);
1300  (void) CopyMagickString(key,name,MaxTextExtent);
1301  status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1302    ConstantString(key),CloneStringInfo(profile));
1303  if ((status != MagickFalse) &&
1304      ((LocaleCompare(name,"iptc") == 0) || (LocaleCompare(name,"8bim") == 0)))
1305    (void) GetProfilesFromResourceBlock(image,profile,exception);
1306  /*
1307    Inject profile into image properties.
1308  */
1309  (void) FormatLocaleString(property,MaxTextExtent,"%s:*",name);
1310  (void) GetImageProperty(image,property,exception);
1311  return(status);
1312}
1313
1314/*
1315%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1316%                                                                             %
1317%                                                                             %
1318%                                                                             %
1319%   S y n c I m a g e P r o f i l e s                                         %
1320%                                                                             %
1321%                                                                             %
1322%                                                                             %
1323%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1324%
1325%  SyncImageProfiles() synchronizes image properties with the image profiles.
1326%  Currently we only support updating the EXIF resolution and orientation.
1327%
1328%  The format of the SyncImageProfiles method is:
1329%
1330%      MagickBooleanType SyncImageProfiles(Image *image)
1331%
1332%  A description of each parameter follows:
1333%
1334%    o image: the image.
1335%
1336*/
1337
1338static inline int ReadProfileByte(unsigned char **p,size_t *length)
1339{
1340  int
1341    c;
1342
1343  if (*length < 1)
1344    return(EOF);
1345  c=(int) (*(*p)++);
1346  (*length)--;
1347  return(c);
1348}
1349
1350static inline unsigned short ReadProfileShort(const EndianType endian,
1351  unsigned char *buffer)
1352{
1353  unsigned short
1354    value;
1355
1356  if (endian == LSBEndian)
1357    {
1358      value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
1359      return((unsigned short) (value & 0xffff));
1360    }
1361  value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
1362    ((unsigned char *) buffer)[1]);
1363  return((unsigned short) (value & 0xffff));
1364}
1365
1366static inline size_t ReadProfileLong(const EndianType endian,
1367  unsigned char *buffer)
1368{
1369  size_t
1370    value;
1371
1372  if (endian == LSBEndian)
1373    {
1374      value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
1375        (buffer[1] << 8 ) | (buffer[0]));
1376      return((size_t) (value & 0xffffffff));
1377    }
1378  value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
1379    (buffer[2] << 8) | buffer[3]);
1380  return((size_t) (value & 0xffffffff));
1381}
1382
1383static inline size_t ReadProfileMSBLong(unsigned char **p,
1384  size_t *length)
1385{
1386  size_t
1387    value;
1388
1389  if (*length < 4)
1390    return(0);
1391
1392  value=ReadProfileLong(MSBEndian,*p);
1393  (*length)-=4;
1394  *p+=4;
1395  return(value);
1396}
1397
1398static inline unsigned short ReadProfileMSBShort(unsigned char **p,
1399  size_t *length)
1400{
1401  unsigned short
1402    value;
1403
1404  if (*length < 2)
1405    return(0);
1406
1407  value=ReadProfileShort(MSBEndian,*p);
1408  (*length)-=2;
1409  *p+=2;
1410  return(value);
1411}
1412
1413static inline void WriteProfileLong(const EndianType endian,
1414  const size_t value,unsigned char *p)
1415{
1416  unsigned char
1417    buffer[4];
1418
1419  if (endian == LSBEndian)
1420    {
1421      buffer[0]=(unsigned char) value;
1422      buffer[1]=(unsigned char) (value >> 8);
1423      buffer[2]=(unsigned char) (value >> 16);
1424      buffer[3]=(unsigned char) (value >> 24);
1425      (void) CopyMagickMemory(p,buffer,4);
1426      return;
1427    }
1428  buffer[0]=(unsigned char) (value >> 24);
1429  buffer[1]=(unsigned char) (value >> 16);
1430  buffer[2]=(unsigned char) (value >> 8);
1431  buffer[3]=(unsigned char) value;
1432  (void) CopyMagickMemory(p,buffer,4);
1433}
1434
1435static void WriteProfileShort(const EndianType endian,
1436  const unsigned short value,unsigned char *p)
1437{
1438  unsigned char
1439    buffer[2];
1440
1441  if (endian == LSBEndian)
1442    {
1443      buffer[0]=(unsigned char) value;
1444      buffer[1]=(unsigned char) (value >> 8);
1445      (void) CopyMagickMemory(p,buffer,2);
1446      return;
1447    }
1448  buffer[0]=(unsigned char) (value >> 8);
1449  buffer[1]=(unsigned char) value;
1450  (void) CopyMagickMemory(p,buffer,2);
1451}
1452
1453static MagickBooleanType Sync8BimProfile(Image *image,StringInfo *profile)
1454{
1455  size_t
1456    count,
1457    length;
1458
1459  unsigned char
1460    *p;
1461
1462  unsigned short
1463    id;
1464
1465  length=GetStringInfoLength(profile);
1466  p=GetStringInfoDatum(profile);
1467  while(length != 0)
1468  {
1469    if (ReadProfileByte(&p,&length) != 0x38)
1470      continue;
1471    if (ReadProfileByte(&p,&length) != 0x42)
1472      continue;
1473    if (ReadProfileByte(&p,&length) != 0x49)
1474      continue;
1475    if (ReadProfileByte(&p,&length) != 0x4D)
1476      continue;
1477    if (length < 7)
1478      return(MagickFalse);
1479    id=ReadProfileMSBShort(&p,&length);
1480    count=ReadProfileByte(&p,&length);
1481    if (count > length)
1482      return(MagickFalse);
1483    p+=count;
1484    if ((*p & 0x01) == 0)
1485      p++;
1486    count=ReadProfileMSBLong(&p,&length);
1487    if (count > length)
1488      return(MagickFalse);
1489    if (id == 0x3ED && count == 16)
1490      {
1491        if (image->units == PixelsPerCentimeterResolution)
1492          WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.x*2.54*
1493            65536.0),p);
1494        else
1495          WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.x*
1496            65536.0),p);
1497        WriteProfileShort(MSBEndian,(unsigned short) image->units,p+4);
1498        if (image->units == PixelsPerCentimeterResolution)
1499          WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.y*2.54*
1500            65536.0),p+8);
1501        else
1502          WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.y*
1503            65536.0),p+8);
1504        WriteProfileShort(MSBEndian,(unsigned short) image->units,p+12);
1505      }
1506    p+=count;
1507    length-=count;
1508  }
1509  return(MagickTrue);
1510}
1511
1512MagickBooleanType SyncExifProfile(Image *image,StringInfo *profile)
1513{
1514#define MaxDirectoryStack  16
1515#define EXIF_DELIMITER  "\n"
1516#define EXIF_NUM_FORMATS  12
1517#define TAG_EXIF_OFFSET  0x8769
1518#define TAG_INTEROP_OFFSET  0xa005
1519
1520  typedef struct _DirectoryInfo
1521  {
1522    unsigned char
1523      *directory;
1524
1525    size_t
1526      entry;
1527  } DirectoryInfo;
1528
1529  DirectoryInfo
1530    directory_stack[MaxDirectoryStack];
1531
1532  EndianType
1533    endian;
1534
1535  size_t
1536    entry,
1537    length,
1538    number_entries;
1539
1540  ssize_t
1541    id,
1542    level,
1543    offset;
1544
1545  static int
1546    format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1547
1548  unsigned char
1549    *directory,
1550    *exif;
1551
1552  /*
1553    Set EXIF resolution tag.
1554  */
1555  length=GetStringInfoLength(profile);
1556  exif=GetStringInfoDatum(profile);
1557  if (length < 16)
1558    return(MagickFalse);
1559  id=(ssize_t) ReadProfileShort(LSBEndian,exif);
1560  if ((id != 0x4949) && (id != 0x4D4D))
1561    {
1562      while (length != 0)
1563      {
1564        if (ReadProfileByte(&exif,&length) != 0x45)
1565          continue;
1566        if (ReadProfileByte(&exif,&length) != 0x78)
1567          continue;
1568        if (ReadProfileByte(&exif,&length) != 0x69)
1569          continue;
1570        if (ReadProfileByte(&exif,&length) != 0x66)
1571          continue;
1572        if (ReadProfileByte(&exif,&length) != 0x00)
1573          continue;
1574        if (ReadProfileByte(&exif,&length) != 0x00)
1575          continue;
1576        break;
1577      }
1578      if (length < 16)
1579        return(MagickFalse);
1580      id=(ssize_t) ReadProfileShort(LSBEndian,exif);
1581    }
1582  endian=LSBEndian;
1583  if (id == 0x4949)
1584    endian=LSBEndian;
1585  else
1586    if (id == 0x4D4D)
1587      endian=MSBEndian;
1588    else
1589      return(MagickFalse);
1590  if (ReadProfileShort(endian,exif+2) != 0x002a)
1591    return(MagickFalse);
1592  /*
1593    This the offset to the first IFD.
1594  */
1595  offset=(ssize_t) ((int) ReadProfileLong(endian,exif+4));
1596  if ((offset < 0) || (size_t) offset >= length)
1597    return(MagickFalse);
1598  directory=exif+offset;
1599  level=0;
1600  entry=0;
1601  do
1602  {
1603    if (level > 0)
1604      {
1605        level--;
1606        directory=directory_stack[level].directory;
1607        entry=directory_stack[level].entry;
1608      }
1609    /*
1610      Determine how many entries there are in the current IFD.
1611    */
1612    number_entries=ReadProfileShort(endian,directory);
1613    for ( ; entry < number_entries; entry++)
1614    {
1615      int
1616        components;
1617
1618      register unsigned char
1619        *p,
1620        *q;
1621
1622      size_t
1623        number_bytes;
1624
1625      ssize_t
1626        format,
1627        tag_value;
1628
1629      q=(unsigned char *) (directory+2+(12*entry));
1630      tag_value=(ssize_t) ReadProfileShort(endian,q);
1631      format=(ssize_t) ReadProfileShort(endian,q+2);
1632      if ((format-1) >= EXIF_NUM_FORMATS)
1633        break;
1634      components=(ssize_t) ((int) ReadProfileLong(endian,q+4));
1635      number_bytes=(size_t) components*format_bytes[format];
1636      if ((ssize_t) number_bytes < components)
1637        break;  /* prevent overflow */
1638      if (number_bytes <= 4)
1639        p=q+8;
1640      else
1641        {
1642          ssize_t
1643            offset;
1644
1645          /*
1646            The directory entry contains an offset.
1647          */
1648          offset=(ssize_t) ((int) ReadProfileLong(endian,q+8));
1649          if ((size_t) (offset+number_bytes) > length)
1650            continue;
1651          if (~length < number_bytes)
1652            continue;  /* prevent overflow */
1653          p=(unsigned char *) (exif+offset);
1654        }
1655      switch (tag_value)
1656      {
1657        case 0x011a:
1658        {
1659          (void) WriteProfileLong(endian,(size_t) (image->resolution.x+0.5),p);
1660          (void) WriteProfileLong(endian,1UL,p+4);
1661          break;
1662        }
1663        case 0x011b:
1664        {
1665          (void) WriteProfileLong(endian,(size_t) (image->resolution.y+0.5),p);
1666          (void) WriteProfileLong(endian,1UL,p+4);
1667          break;
1668        }
1669        case 0x0112:
1670        {
1671          if (number_bytes == 4)
1672            {
1673              (void) WriteProfileLong(endian,(size_t) image->orientation,p);
1674              break;
1675            }
1676          (void) WriteProfileShort(endian,(unsigned short) image->orientation,
1677            p);
1678          break;
1679        }
1680        case 0x0128:
1681        {
1682          if (number_bytes == 4)
1683            {
1684              (void) WriteProfileLong(endian,(size_t) (image->units+1),p);
1685              break;
1686            }
1687          (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
1688          break;
1689        }
1690        default:
1691          break;
1692      }
1693      if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
1694        {
1695          ssize_t
1696            offset;
1697
1698          offset=(ssize_t) ((int) ReadProfileLong(endian,p));
1699          if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
1700            {
1701              directory_stack[level].directory=directory;
1702              entry++;
1703              directory_stack[level].entry=entry;
1704              level++;
1705              directory_stack[level].directory=exif+offset;
1706              directory_stack[level].entry=0;
1707              level++;
1708              if ((directory+2+(12*number_entries)) > (exif+length))
1709                break;
1710              offset=(ssize_t) ((int) ReadProfileLong(endian,directory+2+(12*
1711                number_entries)));
1712              if ((offset != 0) && ((size_t) offset < length) &&
1713                  (level < (MaxDirectoryStack-2)))
1714                {
1715                  directory_stack[level].directory=exif+offset;
1716                  directory_stack[level].entry=0;
1717                  level++;
1718                }
1719            }
1720          break;
1721        }
1722    }
1723  } while (level > 0);
1724  return(MagickTrue);
1725}
1726
1727MagickPrivate MagickBooleanType SyncImageProfiles(Image *image)
1728{
1729  MagickBooleanType
1730    status;
1731
1732  StringInfo
1733    *profile;
1734
1735  status=MagickTrue;
1736  profile=(StringInfo *) GetImageProfile(image,"8BIM");
1737  if (profile != (StringInfo *) NULL)
1738    if (Sync8BimProfile(image,profile) == MagickFalse)
1739      status=MagickFalse;
1740  profile=(StringInfo *) GetImageProfile(image,"EXIF");
1741  if (profile != (StringInfo *) NULL)
1742    if (SyncExifProfile(image,profile) == MagickFalse)
1743      status=MagickFalse;
1744  return(status);
1745}
1746