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