]> 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             }
1005           else
1006             {
1007               source_profile=cmsOpenProfileFromMem(
1008                 GetStringInfoDatum(icc_profile),(cmsUInt32Number)
1009                 GetStringInfoLength(icc_profile));
1010               target_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
1011                 (cmsUInt32Number) GetStringInfoLength(profile));
1012               if ((source_profile == (cmsHPROFILE) NULL) ||
1013                   (target_profile == (cmsHPROFILE) NULL))
1014                 ThrowBinaryException(ResourceLimitError,
1015                   "ColorspaceColorProfileMismatch",name);
1016             }
1017           switch (cmsGetColorSpace(source_profile))
1018           {
1019             case cmsSigCmykData:
1020             {
1021               source_colorspace=CMYKColorspace;
1022               source_type=(cmsUInt32Number) TYPE_CMYK_16;
1023               source_channels=4;
1024               break;
1025             }
1026             case cmsSigGrayData:
1027             {
1028               source_colorspace=GRAYColorspace;
1029               source_type=(cmsUInt32Number) TYPE_GRAY_16;
1030               source_channels=1;
1031               break;
1032             }
1033             case cmsSigLabData:
1034             {
1035               source_colorspace=LabColorspace;
1036               source_type=(cmsUInt32Number) TYPE_Lab_16;
1037               source_channels=3;
1038               break;
1039             }
1040             case cmsSigLuvData:
1041             {
1042               source_colorspace=YUVColorspace;
1043               source_type=(cmsUInt32Number) TYPE_YUV_16;
1044               source_channels=3;
1045               break;
1046             }
1047             case cmsSigRgbData:
1048             {
1049               source_colorspace=RGBColorspace;
1050               source_type=(cmsUInt32Number) TYPE_RGB_16;
1051               source_channels=3;
1052               break;
1053             }
1054             case cmsSigXYZData:
1055             {
1056               source_colorspace=XYZColorspace;
1057               source_type=(cmsUInt32Number) TYPE_XYZ_16;
1058               source_channels=3;
1059               break;
1060             }
1061             case cmsSigYCbCrData:
1062             {
1063               source_colorspace=YCbCrColorspace;
1064               source_type=(cmsUInt32Number) TYPE_YCbCr_16;
1065               source_channels=3;
1066               break;
1067             }
1068             default:
1069             {
1070               source_colorspace=UndefinedColorspace;
1071               source_type=(cmsUInt32Number) TYPE_RGB_16;
1072               source_channels=3;
1073               break;
1074             }
1075           }
1076           target_colorspace=source_colorspace;
1077           target_type=source_type;
1078           target_channels=source_channels;
1079           if (target_profile != (cmsHPROFILE) NULL)
1080             {
1081               switch (cmsGetColorSpace(target_profile))
1082               {
1083                 case cmsSigCmykData:
1084                 {
1085                   target_colorspace=CMYKColorspace;
1086                   target_type=(cmsUInt32Number) TYPE_CMYK_16;
1087                   target_channels=4;
1088                   break;
1089                 }
1090                 case cmsSigLabData:
1091                 {
1092                   target_colorspace=LabColorspace;
1093                   target_type=(cmsUInt32Number) TYPE_Lab_16;
1094                   target_channels=3;
1095                   break;
1096                 }
1097                 case cmsSigGrayData:
1098                 {
1099                   target_colorspace=GRAYColorspace;
1100                   target_type=(cmsUInt32Number) TYPE_GRAY_16;
1101                   target_channels=1;
1102                   break;
1103                 }
1104                 case cmsSigLuvData:
1105                 {
1106                   target_colorspace=YUVColorspace;
1107                   target_type=(cmsUInt32Number) TYPE_YUV_16;
1108                   target_channels=3;
1109                   break;
1110                 }
1111                 case cmsSigRgbData:
1112                 {
1113                   target_colorspace=RGBColorspace;
1114                   target_type=(cmsUInt32Number) TYPE_RGB_16;
1115                   target_channels=3;
1116                   break;
1117                 }
1118                 case cmsSigXYZData:
1119                 {
1120                   target_colorspace=XYZColorspace;
1121                   target_type=(cmsUInt32Number) TYPE_XYZ_16;
1122                   target_channels=3;
1123                   break;
1124                 }
1125                 case cmsSigYCbCrData:
1126                 {
1127                   target_colorspace=YCbCrColorspace;
1128                   target_type=(cmsUInt32Number) TYPE_YCbCr_16;
1129                   target_channels=3;
1130                   break;
1131                 }
1132                 default:
1133                 {
1134                   target_colorspace=UndefinedColorspace;
1135                   target_type=(cmsUInt32Number) TYPE_RGB_16;
1136                   target_channels=3;
1137                   break;
1138                 }
1139               }
1140               if ((source_colorspace == UndefinedColorspace) ||
1141                   (target_colorspace == UndefinedColorspace))
1142                 ThrowProfileException(ImageError,
1143                   "ColorspaceColorProfileMismatch",name);
1144                if ((source_colorspace == GRAYColorspace) &&
1145                    (IsGrayImage(image,exception) == MagickFalse))
1146                 ThrowProfileException(ImageError,
1147                   "ColorspaceColorProfileMismatch",name);
1148                if ((source_colorspace == CMYKColorspace) &&
1149                    (image->colorspace != CMYKColorspace))
1150                 ThrowProfileException(ImageError,
1151                   "ColorspaceColorProfileMismatch",name);
1152                if ((source_colorspace == XYZColorspace) &&
1153                    (image->colorspace != XYZColorspace))
1154                 ThrowProfileException(ImageError,
1155                   "ColorspaceColorProfileMismatch",name);
1156                if ((source_colorspace == YCbCrColorspace) &&
1157                    (image->colorspace != YCbCrColorspace))
1158                 ThrowProfileException(ImageError,
1159                   "ColorspaceColorProfileMismatch",name);
1160                if ((source_colorspace != CMYKColorspace) &&
1161                    (source_colorspace != GRAYColorspace) &&
1162                    (source_colorspace != LabColorspace) &&
1163                    (source_colorspace != XYZColorspace) &&
1164                    (source_colorspace != YCbCrColorspace) &&
1165                    (image->colorspace != RGBColorspace))
1166                 ThrowProfileException(ImageError,
1167                   "ColorspaceColorProfileMismatch",name);
1168             }
1169           switch (image->rendering_intent)
1170           {
1171             case AbsoluteIntent: intent=INTENT_ABSOLUTE_COLORIMETRIC; break;
1172             case PerceptualIntent: intent=INTENT_PERCEPTUAL; break;
1173             case RelativeIntent: intent=INTENT_RELATIVE_COLORIMETRIC; break;
1174             case SaturationIntent: intent=INTENT_SATURATION; break;
1175             default: intent=INTENT_PERCEPTUAL; break;
1176           }
1177           flags=cmsFLAGS_HIGHRESPRECALC;
1178 #if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
1179           if (image->black_point_compensation != MagickFalse)
1180             flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
1181 #endif
1182           transform=AcquireTransformThreadSet(source_profile,source_type,
1183             target_profile,target_type,intent,flags);
1184           (void) cmsCloseProfile(source_profile);
1185           if (transform == (cmsHTRANSFORM *) NULL)
1186             ThrowBinaryException(ImageError,"UnableToCreateColorTransform",
1187               name);
1188           /*
1189             Transform image as dictated by the source and target image profiles.
1190           */
1191           length=(size_t) image->columns;
1192           source_pixels=AcquirePixelThreadSet(image->columns,source_channels);
1193           target_pixels=AcquirePixelThreadSet(image->columns,target_channels);
1194           if ((source_pixels == (unsigned short **) NULL) ||
1195               (target_pixels == (unsigned short **) NULL))
1196             {
1197               transform=DestroyTransformThreadSet(transform);
1198               (void) cmsCloseProfile(target_profile);
1199               ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
1200                 image->filename);
1201             }
1202           if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1203             {
1204               target_pixels=DestroyPixelThreadSet(target_pixels);
1205               source_pixels=DestroyPixelThreadSet(source_pixels);
1206               transform=DestroyTransformThreadSet(transform);
1207               (void) cmsCloseProfile(target_profile);
1208               return(MagickFalse);
1209             }
1210           if (target_colorspace == CMYKColorspace)
1211             (void) SetImageColorspace(image,target_colorspace);
1212           status=MagickTrue;
1213           progress=0;
1214           image_view=AcquireCacheView(image);
1215 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1216           #pragma omp parallel for schedule(dynamic,4) shared(status)
1217 #endif
1218           for (y=0; y < (long) image->rows; y++)
1219           {
1220             MagickBooleanType
1221               sync;
1222
1223             register IndexPacket
1224               *restrict indexes;
1225
1226             register long
1227               id,
1228               x;
1229
1230             register PixelPacket
1231               *restrict q;
1232
1233             register unsigned short
1234               *p;
1235
1236             if (status == MagickFalse)
1237               continue;
1238             q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1239               exception);
1240             if (q == (PixelPacket *) NULL)
1241               {
1242                 status=MagickFalse;
1243                 continue;
1244               }
1245             indexes=GetCacheViewAuthenticIndexQueue(image_view);
1246             id=GetOpenMPThreadId();
1247             p=source_pixels[id];
1248             for (x=0; x < (long) image->columns; x++)
1249             {
1250               *p++=ScaleQuantumToShort(q->red);
1251               if (source_channels > 1)
1252                 {
1253                   *p++=ScaleQuantumToShort(q->green);
1254                   *p++=ScaleQuantumToShort(q->blue);
1255                 }
1256               if (source_channels > 3)
1257                 *p++=ScaleQuantumToShort(indexes[x]);
1258               q++;
1259             }
1260             cmsDoTransform(transform[id],source_pixels[id],target_pixels[id],
1261               (unsigned int) image->columns);
1262             p=target_pixels[id];
1263             q-=image->columns;
1264             for (x=0; x < (long) image->columns; x++)
1265             {
1266               q->red=ScaleShortToQuantum(*p);
1267               q->green=q->red;
1268               q->blue=q->red;
1269               p++;
1270               if (target_channels > 1)
1271                 {
1272                   q->green=ScaleShortToQuantum(*p);
1273                   p++;
1274                   q->blue=ScaleShortToQuantum(*p);
1275                   p++;
1276                 }
1277               if (target_channels > 3)
1278                 {
1279                   indexes[x]=ScaleShortToQuantum(*p);
1280                   p++;
1281                 }
1282               q++;
1283             }
1284             sync=SyncCacheViewAuthenticPixels(image_view,exception);
1285             if (sync == MagickFalse)
1286               status=MagickFalse;
1287             if (image->progress_monitor != (MagickProgressMonitor) NULL)
1288               {
1289                 MagickBooleanType
1290                   proceed;
1291
1292 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1293   #pragma omp critical (MagickCore_ProfileImage)
1294 #endif
1295                 proceed=SetImageProgress(image,ProfileImageTag,progress++,
1296                   image->rows);
1297                 if (proceed == MagickFalse)
1298                   status=MagickFalse;
1299               }
1300           }
1301           image_view=DestroyCacheView(image_view);
1302           (void) SetImageColorspace(image,target_colorspace);
1303           switch (cmsGetColorSpace(target_profile))
1304           {
1305             case cmsSigRgbData:
1306             {
1307               image->type=image->matte == MagickFalse ? TrueColorType :
1308                 TrueColorMatteType;
1309               break;
1310             }
1311             case cmsSigCmykData:
1312             {
1313               image->type=image->matte == MagickFalse ? ColorSeparationType :
1314                 ColorSeparationMatteType;
1315               break;
1316             }
1317             case cmsSigGrayData:
1318             {
1319               image->type=image->matte == MagickFalse ? GrayscaleType :
1320                 GrayscaleMatteType;
1321               break;
1322             }
1323             default:
1324               break;
1325           }
1326           target_pixels=DestroyPixelThreadSet(target_pixels);
1327           source_pixels=DestroyPixelThreadSet(source_pixels);
1328           transform=DestroyTransformThreadSet(transform);
1329           (void) cmsCloseProfile(target_profile);
1330         }
1331       if (cmsGetDeviceClass(profile) != cmsSigLinkClass)
1332         status=SetImageProfile(image,name,profile);
1333 #endif
1334     }
1335   profile=DestroyStringInfo(profile);
1336   return(status);
1337 }
1338 \f
1339 /*
1340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1341 %                                                                             %
1342 %                                                                             %
1343 %                                                                             %
1344 %   R e m o v e I m a g e P r o f i l e                                       %
1345 %                                                                             %
1346 %                                                                             %
1347 %                                                                             %
1348 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1349 %
1350 %  RemoveImageProfile() removes a named profile from the image and returns its
1351 %  value.
1352 %
1353 %  The format of the RemoveImageProfile method is:
1354 %
1355 %      void *RemoveImageProfile(Image *image,const char *name)
1356 %
1357 %  A description of each parameter follows:
1358 %
1359 %    o image: the image.
1360 %
1361 %    o name: the profile name.
1362 %
1363 */
1364 MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
1365 {
1366   StringInfo
1367     *profile;
1368
1369   assert(image != (Image *) NULL);
1370   assert(image->signature == MagickSignature);
1371   if (image->debug != MagickFalse)
1372     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1373   if (image->profiles == (SplayTreeInfo *) NULL)
1374     return((StringInfo *) NULL);
1375   if (LocaleCompare(name,"icc") == 0)
1376     {
1377       /*
1378         Continue to support deprecated color profile for now.
1379       */
1380       image->color_profile.length=0;
1381       image->color_profile.info=(unsigned char *) NULL;
1382     }
1383   if (LocaleCompare(name,"iptc") == 0)
1384     {
1385       /*
1386         Continue to support deprecated IPTC profile for now.
1387       */
1388       image->iptc_profile.length=0;
1389       image->iptc_profile.info=(unsigned char *) NULL;
1390     }
1391   profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
1392     image->profiles,name);
1393   return(profile);
1394 }
1395 \f
1396 /*
1397 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1398 %                                                                             %
1399 %                                                                             %
1400 %                                                                             %
1401 %   R e s e t P r o f i l e I t e r a t o r                                   %
1402 %                                                                             %
1403 %                                                                             %
1404 %                                                                             %
1405 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1406 %
1407 %  ResetImageProfileIterator() resets the image profile iterator.  Use it in
1408 %  conjunction with GetNextImageProfile() to iterate over all the profiles
1409 %  associated with an image.
1410 %
1411 %  The format of the ResetImageProfileIterator method is:
1412 %
1413 %      ResetImageProfileIterator(Image *image)
1414 %
1415 %  A description of each parameter follows:
1416 %
1417 %    o image: the image.
1418 %
1419 */
1420 MagickExport void ResetImageProfileIterator(const Image *image)
1421 {
1422   assert(image != (Image *) NULL);
1423   assert(image->signature == MagickSignature);
1424   if (image->debug != MagickFalse)
1425     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1426   if (image->profiles == (SplayTreeInfo *) NULL)
1427     return;
1428   ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
1429 }
1430 \f
1431 /*
1432 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1433 %                                                                             %
1434 %                                                                             %
1435 %                                                                             %
1436 %   S e t I m a g e P r o f i l e                                             %
1437 %                                                                             %
1438 %                                                                             %
1439 %                                                                             %
1440 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1441 %
1442 %  SetImageProfile() adds a named profile to the image.  If a profile with the
1443 %  same name already exists, it is replaced.  This method differs from the
1444 %  ProfileImage() method in that it does not apply CMS color profiles.
1445 %
1446 %  The format of the SetImageProfile method is:
1447 %
1448 %      MagickBooleanType SetImageProfile(Image *image,const char *name,
1449 %        const StringInfo *profile)
1450 %
1451 %  A description of each parameter follows:
1452 %
1453 %    o image: the image.
1454 %
1455 %    o name: the profile name, for example icc, exif, and 8bim (8bim is the
1456 %      Photoshop wrapper for iptc profiles).
1457 %
1458 %    o profile: A StringInfo structure that contains the named profile.
1459 %
1460 */
1461
1462 static void *DestroyProfile(void *profile)
1463 {
1464   return((void *) DestroyStringInfo((StringInfo *) profile));
1465 }
1466
1467 static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1468   unsigned char *quantum)
1469 {
1470   *quantum=(*p++);
1471   return(p);
1472 }
1473
1474 static inline const unsigned char *ReadResourceBytes(const unsigned char *p,
1475   const ssize_t count,unsigned char *quantum)
1476 {
1477   register ssize_t
1478     i;
1479
1480   for (i=0; i < count; i++)
1481     *quantum++=(*p++);
1482   return(p);
1483 }
1484
1485 static inline const unsigned char *ReadResourceLong(const unsigned char *p,
1486   unsigned long *quantum)
1487 {
1488   *quantum=(unsigned long) (*p++ << 24);
1489   *quantum|=(unsigned long) (*p++ << 16);
1490   *quantum|=(unsigned long) (*p++ << 8);
1491   *quantum|=(unsigned long) (*p++ << 0);
1492   return(p);
1493 }
1494
1495 static inline const unsigned char *ReadResourceShort(const unsigned char *p,
1496   unsigned short *quantum)
1497 {
1498   *quantum=(unsigned short) (*p++ << 8);
1499   *quantum|=(unsigned short) (*p++ << 0);
1500   return(p);
1501 }
1502
1503 static MagickBooleanType GetProfilesFromResourceBlock(Image *image,
1504   const StringInfo *resource_block)
1505 {
1506   const unsigned char
1507     *datum;
1508
1509   register const unsigned char
1510     *p;
1511
1512   size_t
1513     length;
1514
1515   StringInfo
1516     *profile;
1517
1518   unsigned char
1519     length_byte;
1520
1521   unsigned long
1522     count;
1523
1524   unsigned short
1525     id;
1526
1527   datum=GetStringInfoDatum(resource_block);
1528   length=GetStringInfoLength(resource_block);
1529   for (p=datum; p < (datum+length-16); )
1530   {
1531     if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1532       break;
1533     p+=4;
1534     p=ReadResourceShort(p,&id);
1535     p=ReadResourceByte(p,&length_byte);
1536     p+=length_byte;
1537     if (((length_byte+1) & 0x01) != 0)
1538       p++;
1539     if (p > (datum+length-4))
1540       break;
1541     p=ReadResourceLong(p,&count);
1542     if ((p > (datum+length-count)) || (count > length))
1543       break;
1544     switch (id)
1545     {
1546       case 0x03ed:
1547       {
1548         unsigned short
1549           resolution;
1550
1551         /*
1552           Resolution.
1553         */
1554         p=ReadResourceShort(p,&resolution)+6;
1555         image->x_resolution=(double) resolution;
1556         p=ReadResourceShort(p,&resolution)+6;
1557         image->y_resolution=(double) resolution;
1558         break;
1559       }
1560       case 0x0404:
1561       {
1562         /*
1563           IPTC Profile
1564         */
1565         profile=AcquireStringInfo(count);
1566         SetStringInfoDatum(profile,p);
1567         (void) SetImageProfile(image,"iptc",profile);
1568         profile=DestroyStringInfo(profile);
1569         p+=count;
1570         break;
1571       }
1572       case 0x040c:
1573       {
1574         /*
1575           Thumbnail.
1576         */
1577         p+=count;
1578         break;
1579       }
1580       case 0x040f:
1581       {
1582         /*
1583           ICC Profile.
1584         */
1585         profile=AcquireStringInfo(count);
1586         SetStringInfoDatum(profile,p);
1587         (void) SetImageProfile(image,"icc",profile);
1588         profile=DestroyStringInfo(profile);
1589         p+=count;
1590         break;
1591       }
1592       case 0x0422:
1593       {
1594         /*
1595           EXIF Profile.
1596         */
1597         profile=AcquireStringInfo(count);
1598         SetStringInfoDatum(profile,p);
1599         (void) SetImageProfile(image,"exif",profile);
1600         profile=DestroyStringInfo(profile);
1601         p+=count;
1602         break;
1603       }
1604       case 0x0424:
1605       {
1606         /*
1607           XMP Profile.
1608         */
1609         profile=AcquireStringInfo(count);
1610         SetStringInfoDatum(profile,p);
1611         (void) SetImageProfile(image,"xmp",profile);
1612         profile=DestroyStringInfo(profile);
1613         p+=count;
1614         break;
1615       }
1616       default:
1617       {
1618         p+=count;
1619         break;
1620       }
1621     }
1622     if ((count & 0x01) != 0)
1623       p++;
1624   }
1625   return(MagickTrue);
1626 }
1627
1628 MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
1629   const StringInfo *profile)
1630 {
1631   char
1632     key[MaxTextExtent],
1633     property[MaxTextExtent];
1634
1635   MagickBooleanType
1636     status;
1637
1638   assert(image != (Image *) NULL);
1639   assert(image->signature == MagickSignature);
1640   if (image->debug != MagickFalse)
1641     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1642   if (image->profiles == (SplayTreeInfo *) NULL)
1643     image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
1644       DestroyProfile);
1645   (void) CopyMagickString(key,name,MaxTextExtent);
1646   status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1647     ConstantString(key),CloneStringInfo(profile));
1648   if ((status != MagickFalse) &&
1649       ((LocaleCompare(name,"icc") == 0) || (LocaleCompare(name,"icm") == 0)))
1650     {
1651       const StringInfo
1652         *icc_profile;
1653
1654       /*
1655         Continue to support deprecated color profile member.
1656       */
1657       icc_profile=GetImageProfile(image,name);
1658       if (icc_profile != (const StringInfo *) NULL)
1659         {
1660           image->color_profile.length=GetStringInfoLength(icc_profile);
1661           image->color_profile.info=GetStringInfoDatum(icc_profile);
1662         }
1663     }
1664   if ((status != MagickFalse) &&
1665       ((LocaleCompare(name,"iptc") == 0) || (LocaleCompare(name,"8bim") == 0)))
1666     {
1667       const StringInfo
1668         *iptc_profile;
1669
1670       /*
1671         Continue to support deprecated IPTC profile member.
1672       */
1673       iptc_profile=GetImageProfile(image,name);
1674       if (iptc_profile != (const StringInfo *) NULL)
1675         {
1676           image->iptc_profile.length=GetStringInfoLength(iptc_profile);
1677           image->iptc_profile.info=GetStringInfoDatum(iptc_profile);
1678         }
1679       (void) GetProfilesFromResourceBlock(image,profile);
1680     }
1681   /*
1682     Inject profile into image properties.
1683   */
1684   (void) FormatMagickString(property,MaxTextExtent,"%s:sans",name);
1685   (void) GetImageProperty(image,property);
1686   return(status);
1687 }
1688 \f
1689 /*
1690 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1691 %                                                                             %
1692 %                                                                             %
1693 %                                                                             %
1694 %   S y n c I m a g e P r o f i l e s                                         %
1695 %                                                                             %
1696 %                                                                             %
1697 %                                                                             %
1698 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1699 %
1700 %  SyncImageProfiles() synchronizes image properties with the image profiles.
1701 %  Currently we only support updating the EXIF resolution and orientation.
1702 %
1703 %  The format of the SyncImageProfiles method is:
1704 %
1705 %      MagickBooleanType SyncImageProfiles(Image *image)
1706 %
1707 %  A description of each parameter follows:
1708 %
1709 %    o image: the image.
1710 %
1711 */
1712
1713 static inline int ReadProfileByte(unsigned char **p,size_t *length)
1714 {
1715   int
1716     c;
1717
1718   if (*length < 1)
1719     return(EOF);
1720   c=(int) (*(*p)++);
1721   (*length)--;
1722   return(c);
1723 }
1724
1725 static inline unsigned short ReadProfileShort(const EndianType endian,
1726   unsigned char *buffer)
1727 {
1728   unsigned short
1729     value;
1730
1731   if (endian == MSBEndian)
1732     {
1733       value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
1734         ((unsigned char *) buffer)[1]);
1735       return((unsigned short) (value & 0xffff));
1736     }
1737   value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
1738   return((unsigned short) (value & 0xffff));
1739 }
1740
1741 static inline unsigned long ReadProfileLong(const EndianType endian,
1742   unsigned char *buffer)
1743 {
1744   unsigned long
1745     value;
1746
1747   if (endian == MSBEndian)
1748     {
1749       value=(unsigned long) ((buffer[0] << 24) | (buffer[1] << 16) |
1750         (buffer[2] << 8) | buffer[3]);
1751       return((unsigned long) (value & 0xffffffff));
1752     }
1753   value=(unsigned long) ((buffer[3] << 24) | (buffer[2] << 16) |
1754     (buffer[1] << 8 ) | (buffer[0]));
1755   return((unsigned long) (value & 0xffffffff));
1756 }
1757
1758 static inline void WriteProfileLong(const EndianType endian,
1759   const unsigned long value,unsigned char *p)
1760 {
1761   unsigned char
1762     buffer[4];
1763
1764   if (endian == MSBEndian)
1765     {
1766       buffer[0]=(unsigned char) (value >> 24);
1767       buffer[1]=(unsigned char) (value >> 16);
1768       buffer[2]=(unsigned char) (value >> 8);
1769       buffer[3]=(unsigned char) value;
1770       (void) CopyMagickMemory(p,buffer,4);
1771       return;
1772     }
1773   buffer[0]=(unsigned char) value;
1774   buffer[1]=(unsigned char) (value >> 8);
1775   buffer[2]=(unsigned char) (value >> 16);
1776   buffer[3]=(unsigned char) (value >> 24);
1777   (void) CopyMagickMemory(p,buffer,4);
1778 }
1779
1780 static void WriteProfileShort(const EndianType endian,
1781   const unsigned short value,unsigned char *p)
1782 {
1783   unsigned char
1784     buffer[2];
1785
1786   if (endian == MSBEndian)
1787     {
1788       buffer[0]=(unsigned char) (value >> 8);
1789       buffer[1]=(unsigned char) value;
1790       (void) CopyMagickMemory(p,buffer,2);
1791       return;
1792     }
1793   buffer[0]=(unsigned char) value;
1794   buffer[1]=(unsigned char) (value >> 8);
1795   (void) CopyMagickMemory(p,buffer,2);
1796 }
1797
1798 MagickExport MagickBooleanType SyncImageProfiles(Image *image)
1799 {
1800 #define MaxDirectoryStack  16
1801 #define EXIF_DELIMITER  "\n"
1802 #define EXIF_NUM_FORMATS  12
1803 #define TAG_EXIF_OFFSET  0x8769
1804 #define TAG_INTEROP_OFFSET  0xa005
1805
1806   typedef struct _DirectoryInfo
1807   {
1808     unsigned char
1809       *directory;
1810
1811     unsigned long
1812       entry;
1813   } DirectoryInfo;
1814
1815   DirectoryInfo
1816     directory_stack[MaxDirectoryStack];
1817
1818   EndianType
1819     endian;
1820
1821   long
1822     id,
1823     level;
1824
1825   size_t
1826     length;
1827
1828   ssize_t
1829     offset;
1830
1831   static int
1832     format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1833
1834   StringInfo
1835     *profile;
1836
1837   unsigned char
1838     *directory,
1839     *exif;
1840
1841   unsigned long
1842     entry,
1843     number_entries;
1844
1845   /*
1846     Set EXIF resolution tag.
1847   */
1848   profile=(StringInfo *) GetImageProfile(image,"EXIF");
1849   if (profile == (StringInfo *) NULL)
1850     return(MagickTrue);
1851   length=GetStringInfoLength(profile);
1852   exif=GetStringInfoDatum(profile);
1853   while (length != 0)
1854   {
1855     if (ReadProfileByte(&exif,&length) != 0x45)
1856       continue;
1857     if (ReadProfileByte(&exif,&length) != 0x78)
1858       continue;
1859     if (ReadProfileByte(&exif,&length) != 0x69)
1860       continue;
1861     if (ReadProfileByte(&exif,&length) != 0x66)
1862       continue;
1863     if (ReadProfileByte(&exif,&length) != 0x00)
1864       continue;
1865     if (ReadProfileByte(&exif,&length) != 0x00)
1866       continue;
1867     break;
1868   }
1869   if (length < 16)
1870     return(MagickFalse);
1871   id=(int) ReadProfileShort(LSBEndian,exif);
1872   endian=LSBEndian;
1873   if (id == 0x4949)
1874     endian=LSBEndian;
1875   else
1876     if (id == 0x4D4D)
1877       endian=MSBEndian;
1878     else
1879       return(MagickFalse);
1880   if (ReadProfileShort(endian,exif+2) != 0x002a)
1881     return(MagickFalse);
1882   /*
1883     This the offset to the first IFD.
1884   */
1885   offset=(ssize_t) ReadProfileLong(endian,exif+4);
1886   if ((size_t) offset >= length)
1887     return(MagickFalse);
1888   directory=exif+offset;
1889   level=0;
1890   entry=0;
1891   do
1892   {
1893     if (level > 0)
1894       {
1895         level--;
1896         directory=directory_stack[level].directory;
1897         entry=directory_stack[level].entry;
1898       }
1899     /*
1900       Determine how many entries there are in the current IFD.
1901     */
1902     number_entries=ReadProfileShort(endian,directory);
1903     for ( ; entry < number_entries; entry++)
1904     {
1905       long
1906         components,
1907         format,
1908         tag_value;
1909
1910       register unsigned char
1911         *p,
1912         *q;
1913
1914       size_t
1915         number_bytes;
1916
1917       q=(unsigned char *) (directory+2+(12*entry));
1918       tag_value=(long) ReadProfileShort(endian,q);
1919       format=(long) ReadProfileShort(endian,q+2);
1920       if ((format-1) >= EXIF_NUM_FORMATS)
1921         break;
1922       components=(long) ReadProfileLong(endian,q+4);
1923       number_bytes=(size_t) components*format_bytes[format];
1924       if (number_bytes <= 4)
1925         p=q+8;
1926       else
1927         {
1928           ssize_t
1929             offset;
1930
1931           /*
1932             The directory entry contains an offset.
1933           */
1934           offset=(ssize_t) ReadProfileLong(endian,q+8);
1935           if ((size_t) (offset+number_bytes) > length)
1936             continue;
1937           p=(unsigned char *) (exif+offset);
1938         }
1939       switch (tag_value)
1940       {
1941         case 0x011a:
1942         {
1943           (void) WriteProfileLong(endian,(unsigned long)
1944             (image->x_resolution+0.5),p);
1945           (void) WriteProfileLong(endian,1UL,p+4);
1946           break;
1947         }
1948         case 0x011b:
1949         {
1950           (void) WriteProfileLong(endian,(unsigned long)
1951             (image->y_resolution+0.5),p);
1952           (void) WriteProfileLong(endian,1UL,p+4);
1953           break;
1954         }
1955         case 0x0112:
1956         {
1957           (void) WriteProfileShort(endian,(unsigned short)
1958             image->orientation,p);
1959           break;
1960         }
1961         case 0x0128:
1962         {
1963           (void) WriteProfileShort(endian,(unsigned short)
1964             (image->units+1),p);
1965           break;
1966         }
1967         default:
1968           break;
1969       }
1970       if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
1971         {
1972           size_t
1973             offset;
1974
1975           offset=(size_t) ReadProfileLong(endian,p);
1976           if ((offset < length) && (level < (MaxDirectoryStack-2)))
1977             {
1978               directory_stack[level].directory=directory;
1979               entry++;
1980               directory_stack[level].entry=entry;
1981               level++;
1982               directory_stack[level].directory=exif+offset;
1983               directory_stack[level].entry=0;
1984               level++;
1985               if ((directory+2+(12*number_entries)) > (exif+length))
1986                 break;
1987               offset=(size_t) ReadProfileLong(endian,directory+2+(12*
1988                 number_entries));
1989               if ((offset != 0) && (offset < length) &&
1990                   (level < (MaxDirectoryStack-2)))
1991                 {
1992                   directory_stack[level].directory=exif+offset;
1993                   directory_stack[level].entry=0;
1994                   level++;
1995                 }
1996             }
1997           break;
1998         }
1999     }
2000   } while (level > 0);
2001   return(MagickTrue);
2002 }