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