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