]> granicus.if.org Git - imagemagick/blob - MagickCore/profile.c
(no commit message)
[imagemagick] / MagickCore / profile.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               PPPP   RRRR    OOO   FFFFF  IIIII  L      EEEEE               %
7 %               P   P  R   R  O   O  F        I    L      E                   %
8 %               PPPP   RRRR   O   O  FFF      I    L      EEE                 %
9 %               P      R R    O   O  F        I    L      E                   %
10 %               P      R  R    OOO   F      IIIII  LLLLL  EEEEE               %
11 %                                                                             %
12 %                                                                             %
13 %                       MagickCore Image Profile Methods                      %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   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 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/attribute.h"
44 #include "MagickCore/cache.h"
45 #include "MagickCore/color.h"
46 #include "MagickCore/colorspace-private.h"
47 #include "MagickCore/configure.h"
48 #include "MagickCore/exception.h"
49 #include "MagickCore/exception-private.h"
50 #include "MagickCore/hashmap.h"
51 #include "MagickCore/image.h"
52 #include "MagickCore/memory_.h"
53 #include "MagickCore/monitor.h"
54 #include "MagickCore/monitor-private.h"
55 #include "MagickCore/option.h"
56 #include "MagickCore/pixel-accessor.h"
57 #include "MagickCore/profile.h"
58 #include "MagickCore/profile-private.h"
59 #include "MagickCore/property.h"
60 #include "MagickCore/quantum.h"
61 #include "MagickCore/quantum-private.h"
62 #include "MagickCore/resource_.h"
63 #include "MagickCore/splay-tree.h"
64 #include "MagickCore/string_.h"
65 #include "MagickCore/thread-private.h"
66 #include "MagickCore/token.h"
67 #include "MagickCore/utility.h"
68 #if defined(MAGICKCORE_LCMS_DELEGATE)
69 #if defined(MAGICKCORE_HAVE_LCMS_LCMS2_H)
70 #include <wchar.h>
71 #include <lcms/lcms2.h>
72 #elif defined(MAGICKCORE_HAVE_LCMS2_H)
73 #include <wchar.h>
74 #include "lcms2.h"
75 #elif defined(MAGICKCORE_HAVE_LCMS_LCMS_H)
76 #include <lcms/lcms.h>
77 #else
78 #include "lcms.h"
79 #endif
80 #endif
81 \f
82 /*
83   Define declarations.
84 */
85 #if !defined(LCMS_VERSION) || (LCMS_VERSION < 2000)
86 #define cmsSigCmykData icSigCmykData
87 #define cmsSigGrayData icSigGrayData
88 #define cmsSigLabData icSigLabData
89 #define cmsSigLuvData icSigLuvData
90 #define cmsSigRgbData icSigRgbData
91 #define cmsSigXYZData icSigXYZData
92 #define cmsSigYCbCrData icSigYCbCrData
93 #define cmsSigLinkClass icSigLinkClass
94 #define cmsColorSpaceSignature icColorSpaceSignature
95 #define cmsUInt32Number  DWORD
96 #define cmsSetLogErrorHandler(handler)  cmsSetErrorHandler(handler)
97 #define cmsCreateTransformTHR(context,source_profile,source_type, \
98   target_profile,target_type,intent,flags)  cmsCreateTransform(source_profile, \
99   source_type,target_profile,target_type,intent,flags);
100 #define cmsOpenProfileFromMemTHR(context,profile,length) \
101   cmsOpenProfileFromMem(profile,length)
102 #endif
103 \f
104 /*
105   Typedef declarations
106 */
107 struct _ProfileInfo
108 {
109   char
110     *name;
111
112   size_t
113     length;
114
115   unsigned char
116     *info;
117
118   size_t
119     signature;
120 };
121
122 typedef struct _CMSExceptionInfo
123 {
124   Image
125     *image;
126
127   ExceptionInfo
128     *exception;
129 } CMSExceptionInfo;
130 \f
131 /*
132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
133 %                                                                             %
134 %                                                                             %
135 %                                                                             %
136 %   C l o n e I m a g e P r o f i l e s                                       %
137 %                                                                             %
138 %                                                                             %
139 %                                                                             %
140 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
141 %
142 %  CloneImageProfiles() clones one or more image profiles.
143 %
144 %  The format of the CloneImageProfiles method is:
145 %
146 %      MagickBooleanType CloneImageProfiles(Image *image,
147 %        const Image *clone_image)
148 %
149 %  A description of each parameter follows:
150 %
151 %    o image: the image.
152 %
153 %    o clone_image: the clone image.
154 %
155 */
156 MagickExport MagickBooleanType CloneImageProfiles(Image *image,
157   const Image *clone_image)
158 {
159   assert(image != (Image *) NULL);
160   assert(image->signature == MagickSignature);
161   if (image->debug != MagickFalse)
162     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
163   assert(clone_image != (const Image *) NULL);
164   assert(clone_image->signature == MagickSignature);
165   if (clone_image->profiles != (void *) NULL)
166     {
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 \f
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 */
199 MagickExport 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 \f
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 */
232 MagickExport void DestroyImageProfiles(Image *image)
233 {
234   if (image->profiles != (SplayTreeInfo *) NULL)
235     image->profiles=DestroySplayTree((SplayTreeInfo *) image->profiles);
236 }
237 \f
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 */
262 MagickExport 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 \f
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 */
305 MagickExport 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 \f
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
359 static 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
372 static 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
400 static 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
413 static cmsHTRANSFORM *AcquireTransformThreadSet(Image *image,
414   const cmsHPROFILE source_profile,const cmsUInt32Number source_type,
415   const cmsHPROFILE target_profile,const cmsUInt32Number target_type,
416   const int intent,const cmsUInt32Number flags)
417 {
418   cmsHTRANSFORM
419     *transform;
420
421   register ssize_t
422     i;
423
424   size_t
425     number_threads;
426
427   number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
428   transform=(cmsHTRANSFORM *) AcquireQuantumMemory(number_threads,
429     sizeof(*transform));
430   if (transform == (cmsHTRANSFORM *) NULL)
431     return((cmsHTRANSFORM *) NULL);
432   (void) ResetMagickMemory(transform,0,number_threads*sizeof(*transform));
433   for (i=0; i < (ssize_t) number_threads; i++)
434   {
435     transform[i]=cmsCreateTransformTHR(image,source_profile,source_type,
436       target_profile,target_type,intent,flags);
437     if (transform[i] == (cmsHTRANSFORM) NULL)
438       return(DestroyTransformThreadSet(transform));
439   }
440   return(transform);
441 }
442 #endif
443
444 #if defined(MAGICKCORE_LCMS_DELEGATE)
445 #if defined(LCMS_VERSION) && (LCMS_VERSION >= 2000)
446 static 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
474 static 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
483 MagickExport MagickBooleanType ProfileImage(Image *image,const char *name,
484   const void *datum,const size_t length,ExceptionInfo *exception)
485 {
486 #define ProfileImageTag  "Profile/Image"
487 #define ThrowProfileException(severity,tag,context) \
488 { \
489   if (source_profile != (cmsHPROFILE) NULL) \
490     (void) cmsCloseProfile(source_profile); \
491   if (target_profile != (cmsHPROFILE) NULL) \
492     (void) cmsCloseProfile(target_profile); \
493   ThrowBinaryException(severity,tag,context); \
494 }
495
496   MagickBooleanType
497     status;
498
499   StringInfo
500     *profile;
501
502   assert(image != (Image *) NULL);
503   assert(image->signature == MagickSignature);
504   if (image->debug != MagickFalse)
505     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
506   assert(name != (const char *) NULL);
507   if ((datum == (const void *) NULL) || (length == 0))
508     {
509       char
510         **arguments,
511         *names;
512
513       int
514         number_arguments;
515
516       register ssize_t
517         i;
518
519       /*
520         Delete image profile(s).
521       */
522       names=ConstantString(name);
523       (void) SubstituteString(&names,","," ");
524       arguments=StringToArgv(names,&number_arguments);
525       names=DestroyString(names);
526       if (arguments == (char **) NULL)
527         return(MagickTrue);
528       ResetImageProfileIterator(image);
529       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
530       {
531         for (i=1; i < (ssize_t) number_arguments; i++)
532         {
533           if ((*arguments[i] == '!') &&
534               (LocaleCompare(name,arguments[i]+1) == 0))
535             break;
536           if (GlobExpression(name,arguments[i],MagickTrue) != MagickFalse)
537             {
538               (void) DeleteImageProfile(image,name);
539               break;
540             }
541         }
542         name=GetNextImageProfile(image);
543       }
544       for (i=0; i < (ssize_t) number_arguments; i++)
545         arguments[i]=DestroyString(arguments[i]);
546       arguments=(char **) RelinquishMagickMemory(arguments);
547       return(MagickTrue);
548     }
549   /*
550     Add a ICC, IPTC, or generic profile to the image.
551   */
552   status=MagickTrue;
553   profile=AcquireStringInfo((size_t) length);
554   SetStringInfoDatum(profile,(unsigned char *) datum);
555   if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
556     status=SetImageProfile(image,name,profile,exception);
557   else
558     {
559       const StringInfo
560         *icc_profile;
561
562       icc_profile=GetImageProfile(image,"icc");
563       if ((icc_profile != (const StringInfo *) NULL) &&
564           (CompareStringInfo(icc_profile,profile) == 0))
565         {
566           const char
567             *value;
568
569           value=GetImageProperty(image,"exif:ColorSpace",exception);
570           (void) value;
571           /* Future.
572           if (LocaleCompare(value,"1") != 0)
573             (void) SetsRGBImageProfile(image,exception);
574           value=GetImageProperty(image,"exif:InteroperabilityIndex",exception);
575           if (LocaleCompare(value,"R98.") != 0)
576             (void) SetsRGBImageProfile(image,exception);
577           value=GetImageProperty(image,"exif:InteroperabilityIndex",exception);
578           if (LocaleCompare(value,"R03.") != 0)
579             (void) SetAdobeRGB1998ImageProfile(image,exception);
580           */
581           icc_profile=GetImageProfile(image,"icc");
582         }
583       if ((icc_profile != (const StringInfo *) NULL) &&
584           (CompareStringInfo(icc_profile,profile) == 0))
585         {
586           profile=DestroyStringInfo(profile);
587           return(MagickTrue);
588         }
589 #if !defined(MAGICKCORE_LCMS_DELEGATE)
590       (void) ThrowMagickException(exception,GetMagickModule(),
591         MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
592         "'%s' (LCMS)",image->filename);
593 #else
594       {
595         cmsHPROFILE
596           source_profile;
597
598         CMSExceptionInfo
599           cms_exception;
600
601         /*
602           Transform pixel colors as defined by the color profiles.
603         */
604         cmsSetLogErrorHandler(CMSExceptionHandler);
605         cms_exception.image=image;
606         cms_exception.exception=exception;
607         (void) cms_exception;
608         source_profile=cmsOpenProfileFromMemTHR(&cms_exception,
609           GetStringInfoDatum(profile),(cmsUInt32Number)
610           GetStringInfoLength(profile));
611         if (source_profile == (cmsHPROFILE) NULL)
612           ThrowBinaryException(ResourceLimitError,
613             "ColorspaceColorProfileMismatch",name);
614         if ((cmsGetDeviceClass(source_profile) != cmsSigLinkClass) &&
615             (icc_profile == (StringInfo *) NULL))
616           status=SetImageProfile(image,name,profile,exception);
617         else
618           {
619             CacheView
620               *image_view;
621
622             ColorspaceType
623               source_colorspace,
624               target_colorspace;
625
626             cmsColorSpaceSignature
627               signature;
628
629             cmsHPROFILE
630               target_profile;
631
632             cmsHTRANSFORM
633               *restrict transform;
634
635             cmsUInt32Number
636               flags,
637               source_type,
638               target_type;
639
640             int
641               intent;
642
643             MagickBooleanType
644               status;
645
646             MagickOffsetType
647               progress;
648
649             size_t
650               source_channels,
651               target_channels;
652
653             ssize_t
654               y;
655
656             unsigned short
657               **restrict source_pixels,
658               **restrict target_pixels;
659
660             target_profile=(cmsHPROFILE) NULL;
661             if (icc_profile != (StringInfo *) NULL)
662               {
663                 target_profile=source_profile;
664                 source_profile=cmsOpenProfileFromMemTHR(&cms_exception,
665                   GetStringInfoDatum(icc_profile),(cmsUInt32Number)
666                   GetStringInfoLength(icc_profile));
667                 if (source_profile == (cmsHPROFILE) NULL)
668                   ThrowProfileException(ResourceLimitError,
669                     "ColorspaceColorProfileMismatch",name);
670               }
671             switch (cmsGetColorSpace(source_profile))
672             {
673               case cmsSigCmykData:
674               {
675                 source_colorspace=CMYKColorspace;
676                 source_type=(cmsUInt32Number) TYPE_CMYK_16;
677                 source_channels=4;
678                 break;
679               }
680               case cmsSigGrayData:
681               {
682                 source_colorspace=GRAYColorspace;
683                 source_type=(cmsUInt32Number) TYPE_GRAY_16;
684                 source_channels=1;
685                 break;
686               }
687               case cmsSigLabData:
688               {
689                 source_colorspace=LabColorspace;
690                 source_type=(cmsUInt32Number) TYPE_Lab_16;
691                 source_channels=3;
692                 break;
693               }
694               case cmsSigLuvData:
695               {
696                 source_colorspace=YUVColorspace;
697                 source_type=(cmsUInt32Number) TYPE_YUV_16;
698                 source_channels=3;
699                 break;
700               }
701               case cmsSigRgbData:
702               {
703                 source_colorspace=sRGBColorspace;
704                 source_type=(cmsUInt32Number) TYPE_RGB_16;
705                 source_channels=3;
706                 break;
707               }
708               case cmsSigXYZData:
709               {
710                 source_colorspace=XYZColorspace;
711                 source_type=(cmsUInt32Number) TYPE_XYZ_16;
712                 source_channels=3;
713                 break;
714               }
715               case cmsSigYCbCrData:
716               {
717                 source_colorspace=YCbCrColorspace;
718                 source_type=(cmsUInt32Number) TYPE_YCbCr_16;
719                 source_channels=3;
720                 break;
721               }
722               default:
723               {
724                 source_colorspace=UndefinedColorspace;
725                 source_type=(cmsUInt32Number) TYPE_RGB_16;
726                 source_channels=3;
727                 break;
728               }
729             }
730             signature=cmsGetPCS(source_profile);
731             if (target_profile != (cmsHPROFILE) NULL)
732               signature=cmsGetColorSpace(target_profile);
733             switch (signature)
734             {
735               case cmsSigCmykData:
736               {
737                 target_colorspace=CMYKColorspace;
738                 target_type=(cmsUInt32Number) TYPE_CMYK_16;
739                 target_channels=4;
740                 break;
741               }
742               case cmsSigLabData:
743               {
744                 target_colorspace=LabColorspace;
745                 target_type=(cmsUInt32Number) TYPE_Lab_16;
746                 target_channels=3;
747                 break;
748               }
749               case cmsSigGrayData:
750               {
751                 target_colorspace=GRAYColorspace;
752                 target_type=(cmsUInt32Number) TYPE_GRAY_16;
753                 target_channels=1;
754                 break;
755               }
756               case cmsSigLuvData:
757               {
758                 target_colorspace=YUVColorspace;
759                 target_type=(cmsUInt32Number) TYPE_YUV_16;
760                 target_channels=3;
761                 break;
762               }
763               case cmsSigRgbData:
764               {
765                 target_colorspace=sRGBColorspace;
766                 target_type=(cmsUInt32Number) TYPE_RGB_16;
767                 target_channels=3;
768                 break;
769               }
770               case cmsSigXYZData:
771               {
772                 target_colorspace=XYZColorspace;
773                 target_type=(cmsUInt32Number) TYPE_XYZ_16;
774                 target_channels=3;
775                 break;
776               }
777               case cmsSigYCbCrData:
778               {
779                 target_colorspace=YCbCrColorspace;
780                 target_type=(cmsUInt32Number) TYPE_YCbCr_16;
781                 target_channels=3;
782                 break;
783               }
784               default:
785               {
786                 target_colorspace=UndefinedColorspace;
787                 target_type=(cmsUInt32Number) TYPE_RGB_16;
788                 target_channels=3;
789                 break;
790               }
791             }
792             if ((source_colorspace == UndefinedColorspace) ||
793                 (target_colorspace == UndefinedColorspace))
794               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
795                 name);
796              if ((source_colorspace == GRAYColorspace) &&
797                  (IsImageGray(image,exception) == MagickFalse))
798               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
799                 name);
800              if ((source_colorspace == CMYKColorspace) &&
801                  (image->colorspace != CMYKColorspace))
802               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
803                 name);
804              if ((source_colorspace == XYZColorspace) &&
805                  (image->colorspace != XYZColorspace))
806               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
807                 name);
808              if ((source_colorspace == YCbCrColorspace) &&
809                  (image->colorspace != YCbCrColorspace))
810               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
811                 name);
812              if ((source_colorspace != CMYKColorspace) &&
813                  (source_colorspace != LabColorspace) &&
814                  (source_colorspace != XYZColorspace) &&
815                  (source_colorspace != YCbCrColorspace) &&
816                  (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse))
817               ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
818                 name);
819             switch (image->rendering_intent)
820             {
821               case AbsoluteIntent: intent=INTENT_ABSOLUTE_COLORIMETRIC; break;
822               case PerceptualIntent: intent=INTENT_PERCEPTUAL; break;
823               case RelativeIntent: intent=INTENT_RELATIVE_COLORIMETRIC; break;
824               case SaturationIntent: intent=INTENT_SATURATION; break;
825               default: intent=INTENT_PERCEPTUAL; break;
826             }
827             flags=cmsFLAGS_HIGHRESPRECALC;
828 #if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
829             if (image->black_point_compensation != MagickFalse)
830               flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
831 #endif
832             transform=AcquireTransformThreadSet(image,source_profile,
833               source_type,target_profile,target_type,intent,flags);
834             if (transform == (cmsHTRANSFORM *) NULL)
835               ThrowProfileException(ImageError,"UnableToCreateColorTransform",
836                 name);
837             /*
838               Transform image as dictated by the source & target image profiles.
839             */
840             source_pixels=AcquirePixelThreadSet(image->columns,source_channels);
841             target_pixels=AcquirePixelThreadSet(image->columns,target_channels);
842             if ((source_pixels == (unsigned short **) NULL) ||
843                 (target_pixels == (unsigned short **) NULL))
844               {
845                 transform=DestroyTransformThreadSet(transform);
846                 ThrowProfileException(ResourceLimitError,
847                   "MemoryAllocationFailed",image->filename);
848               }
849             if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
850               {
851                 target_pixels=DestroyPixelThreadSet(target_pixels);
852                 source_pixels=DestroyPixelThreadSet(source_pixels);
853                 transform=DestroyTransformThreadSet(transform);
854                 if (source_profile != (cmsHPROFILE) NULL)
855                   (void) cmsCloseProfile(source_profile);
856                 if (target_profile != (cmsHPROFILE) NULL)
857                   (void) cmsCloseProfile(target_profile);
858                 return(MagickFalse);
859               }
860             if (target_colorspace == CMYKColorspace)
861               (void) SetImageColorspace(image,target_colorspace,exception);
862             status=MagickTrue;
863             progress=0;
864             image_view=AcquireAuthenticCacheView(image,exception);
865 #if defined(MAGICKCORE_OPENMP_SUPPORT)
866             #pragma omp parallel for schedule(static,4) shared(status) \
867               magick_threads(image,image,image->rows,1)
868 #endif
869             for (y=0; y < (ssize_t) image->rows; y++)
870             {
871               const int
872                 id = GetOpenMPThreadId();
873
874               MagickBooleanType
875                 sync;
876
877               register ssize_t
878                 x;
879
880               register Quantum
881                 *restrict q;
882
883               register unsigned short
884                 *p;
885
886               if (status == MagickFalse)
887                 continue;
888               q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
889                 exception);
890               if (q == (Quantum *) NULL)
891                 {
892                   status=MagickFalse;
893                   continue;
894                 }
895               p=source_pixels[id];
896               for (x=0; x < (ssize_t) image->columns; x++)
897               {
898                 *p++=ScaleQuantumToShort(GetPixelRed(image,q));
899                 if (source_channels > 1)
900                   {
901                     *p++=ScaleQuantumToShort(GetPixelGreen(image,q));
902                     *p++=ScaleQuantumToShort(GetPixelBlue(image,q));
903                   }
904                 if (source_channels > 3)
905                   *p++=ScaleQuantumToShort(GetPixelBlack(image,q));
906                 q+=GetPixelChannels(image);
907               }
908               cmsDoTransform(transform[id],source_pixels[id],target_pixels[id],
909                 (unsigned int) image->columns);
910               p=target_pixels[id];
911               q-=image->columns*GetPixelChannels(image);
912               for (x=0; x < (ssize_t) image->columns; x++)
913               {
914                 if (target_channels == 1)
915                   SetPixelGray(image,ScaleShortToQuantum(*p),q);
916                 else
917                   SetPixelRed(image,ScaleShortToQuantum(*p),q);
918                 p++;
919                 if (target_channels > 1)
920                   {
921                     SetPixelGreen(image,ScaleShortToQuantum(*p),q);
922                     p++;
923                     SetPixelBlue(image,ScaleShortToQuantum(*p),q);
924                     p++;
925                   }
926                 if (target_channels > 3)
927                   {
928                     SetPixelBlack(image,ScaleShortToQuantum(*p),q);
929                     p++;
930                   }
931                 q+=GetPixelChannels(image);
932               }
933               sync=SyncCacheViewAuthenticPixels(image_view,exception);
934               if (sync == MagickFalse)
935                 status=MagickFalse;
936               if (image->progress_monitor != (MagickProgressMonitor) NULL)
937                 {
938                   MagickBooleanType
939                     proceed;
940
941 #if defined(MAGICKCORE_OPENMP_SUPPORT)
942                   #pragma omp critical (MagickCore_ProfileImage)
943 #endif
944                   proceed=SetImageProgress(image,ProfileImageTag,progress++,
945                     image->rows);
946                   if (proceed == MagickFalse)
947                     status=MagickFalse;
948                 }
949             }
950             image_view=DestroyCacheView(image_view);
951             (void) SetImageColorspace(image,target_colorspace,exception);
952             switch (signature)
953             {
954               case cmsSigRgbData:
955               {
956                 image->type=image->alpha_trait != BlendPixelTrait ?
957                   TrueColorType : TrueColorMatteType;
958                 break;
959               }
960               case cmsSigCmykData:
961               {
962                 image->type=image->alpha_trait != BlendPixelTrait ?
963                   ColorSeparationType : ColorSeparationMatteType;
964                 break;
965               }
966               case cmsSigGrayData:
967               {
968                 image->type=image->alpha_trait != BlendPixelTrait ?
969                   GrayscaleType : GrayscaleMatteType;
970                 break;
971               }
972               default:
973                 break;
974             }
975             target_pixels=DestroyPixelThreadSet(target_pixels);
976             source_pixels=DestroyPixelThreadSet(source_pixels);
977             transform=DestroyTransformThreadSet(transform);
978             if (cmsGetDeviceClass(source_profile) != cmsSigLinkClass)
979               status=SetImageProfile(image,name,profile,exception);
980             if (target_profile != (cmsHPROFILE) NULL)
981               (void) cmsCloseProfile(target_profile);
982           }
983         (void) cmsCloseProfile(source_profile);
984       }
985 #endif
986     }
987   profile=DestroyStringInfo(profile);
988   return(status);
989 }
990 \f
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 */
1016 MagickExport 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 \f
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 */
1056 MagickExport 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 \f
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
1098 static void *DestroyProfile(void *profile)
1099 {
1100   return((void *) DestroyStringInfo((StringInfo *) profile));
1101 }
1102
1103 static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1104   unsigned char *quantum)
1105 {
1106   *quantum=(*p++);
1107   return(p);
1108 }
1109
1110 static 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
1121 static 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
1131 static 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
1139 static MagickBooleanType GetProfilesFromResourceBlock(Image *image,
1140   const StringInfo *resource_block,ExceptionInfo *exception)
1141 {
1142   const unsigned char
1143     *datum;
1144
1145   register const unsigned char
1146     *p;
1147
1148   size_t
1149     length;
1150
1151   StringInfo
1152     *profile;
1153
1154   unsigned char
1155     length_byte;
1156
1157   ssize_t
1158     count;
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
1283 MagickExport 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 \f
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
1338 static 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
1350 static 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
1366 static 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
1383 static 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
1398 static 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
1413 static 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
1435 static 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
1453 static 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
1512 MagickBooleanType 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
1727 MagickPrivate 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 }