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