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 %                                   Cristy                                    %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2021 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 %    https://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 
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/attribute.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/color.h"
47 #include "MagickCore/colorspace-private.h"
48 #include "MagickCore/configure.h"
49 #include "MagickCore/exception.h"
50 #include "MagickCore/exception-private.h"
51 #include "MagickCore/image.h"
52 #include "MagickCore/linked-list.h"
53 #include "MagickCore/memory_.h"
54 #include "MagickCore/monitor.h"
55 #include "MagickCore/monitor-private.h"
56 #include "MagickCore/option.h"
57 #include "MagickCore/option-private.h"
58 #include "MagickCore/pixel-accessor.h"
59 #include "MagickCore/profile.h"
60 #include "MagickCore/profile-private.h"
61 #include "MagickCore/property.h"
62 #include "MagickCore/quantum.h"
63 #include "MagickCore/quantum-private.h"
64 #include "MagickCore/resource_.h"
65 #include "MagickCore/splay-tree.h"
66 #include "MagickCore/string_.h"
67 #include "MagickCore/string-private.h"
68 #include "MagickCore/thread-private.h"
69 #include "MagickCore/token.h"
70 #include "MagickCore/utility.h"
71 #if defined(MAGICKCORE_LCMS_DELEGATE)
72 #if defined(MAGICKCORE_HAVE_LCMS_LCMS2_H)
73 #include <wchar.h>
74 #include <lcms/lcms2.h>
75 #else
76 #include <wchar.h>
77 #include "lcms2.h"
78 #endif
79 #endif
80 #if defined(MAGICKCORE_XML_DELEGATE)
81 #  if defined(MAGICKCORE_WINDOWS_SUPPORT)
82 #    if !defined(__MINGW32__)
83 #      include <win32config.h>
84 #    endif
85 #  endif
86 #  include <libxml/parser.h>
87 #  include <libxml/tree.h>
88 #endif
89 
90 /*
91   Forward declarations
92 */
93 static MagickBooleanType
94   SetImageProfileInternal(Image *,const char *,const StringInfo *,
95     const MagickBooleanType,ExceptionInfo *);
96 
97 static void
98   WriteTo8BimProfile(Image *,const char*,const StringInfo *);
99 
100 /*
101   Typedef declarations
102 */
103 struct _ProfileInfo
104 {
105   char
106     *name;
107 
108   size_t
109     length;
110 
111   unsigned char
112     *info;
113 
114   size_t
115     signature;
116 };
117 
118 typedef struct _CMSExceptionInfo
119 {
120   Image
121     *image;
122 
123   ExceptionInfo
124     *exception;
125 } CMSExceptionInfo;
126 
127 /*
128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
129 %                                                                             %
130 %                                                                             %
131 %                                                                             %
132 %   C l o n e I m a g e P r o f i l e s                                       %
133 %                                                                             %
134 %                                                                             %
135 %                                                                             %
136 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
137 %
138 %  CloneImageProfiles() clones one or more image profiles.
139 %
140 %  The format of the CloneImageProfiles method is:
141 %
142 %      MagickBooleanType CloneImageProfiles(Image *image,
143 %        const Image *clone_image)
144 %
145 %  A description of each parameter follows:
146 %
147 %    o image: the image.
148 %
149 %    o clone_image: the clone image.
150 %
151 */
CloneImageProfiles(Image * image,const Image * clone_image)152 MagickExport MagickBooleanType CloneImageProfiles(Image *image,
153   const Image *clone_image)
154 {
155   assert(image != (Image *) NULL);
156   assert(image->signature == MagickCoreSignature);
157   if (image->debug != MagickFalse)
158     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
159   assert(clone_image != (const Image *) NULL);
160   assert(clone_image->signature == MagickCoreSignature);
161   if (clone_image->profiles != (void *) NULL)
162     {
163       if (image->profiles != (void *) NULL)
164         DestroyImageProfiles(image);
165       image->profiles=CloneSplayTree((SplayTreeInfo *) clone_image->profiles,
166         (void *(*)(void *)) ConstantString,(void *(*)(void *)) CloneStringInfo);
167     }
168   return(MagickTrue);
169 }
170 
171 /*
172 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
173 %                                                                             %
174 %                                                                             %
175 %                                                                             %
176 %   D e l e t e I m a g e P r o f i l e                                       %
177 %                                                                             %
178 %                                                                             %
179 %                                                                             %
180 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
181 %
182 %  DeleteImageProfile() deletes a profile from the image by its name.
183 %
184 %  The format of the DeleteImageProfile method is:
185 %
186 %      MagickBooleanTyupe DeleteImageProfile(Image *image,const char *name)
187 %
188 %  A description of each parameter follows:
189 %
190 %    o image: the image.
191 %
192 %    o name: the profile name.
193 %
194 */
DeleteImageProfile(Image * image,const char * name)195 MagickExport MagickBooleanType DeleteImageProfile(Image *image,const char *name)
196 {
197   assert(image != (Image *) NULL);
198   assert(image->signature == MagickCoreSignature);
199   if (image->debug != MagickFalse)
200     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
201   if (image->profiles == (SplayTreeInfo *) NULL)
202     return(MagickFalse);
203   WriteTo8BimProfile(image,name,(StringInfo *) NULL);
204   return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->profiles,name));
205 }
206 
207 /*
208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
209 %                                                                             %
210 %                                                                             %
211 %                                                                             %
212 %   D e s t r o y I m a g e P r o f i l e s                                   %
213 %                                                                             %
214 %                                                                             %
215 %                                                                             %
216 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
217 %
218 %  DestroyImageProfiles() releases memory associated with an image profile map.
219 %
220 %  The format of the DestroyProfiles method is:
221 %
222 %      void DestroyImageProfiles(Image *image)
223 %
224 %  A description of each parameter follows:
225 %
226 %    o image: the image.
227 %
228 */
DestroyImageProfiles(Image * image)229 MagickExport void DestroyImageProfiles(Image *image)
230 {
231   if (image->profiles != (SplayTreeInfo *) NULL)
232     image->profiles=DestroySplayTree((SplayTreeInfo *) image->profiles);
233 }
234 
235 /*
236 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
237 %                                                                             %
238 %                                                                             %
239 %                                                                             %
240 %   G e t I m a g e P r o f i l e                                             %
241 %                                                                             %
242 %                                                                             %
243 %                                                                             %
244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
245 %
246 %  GetImageProfile() gets a profile associated with an image by name.
247 %
248 %  The format of the GetImageProfile method is:
249 %
250 %      const StringInfo *GetImageProfile(const Image *image,const char *name)
251 %
252 %  A description of each parameter follows:
253 %
254 %    o image: the image.
255 %
256 %    o name: the profile name.
257 %
258 */
GetImageProfile(const Image * image,const char * name)259 MagickExport const StringInfo *GetImageProfile(const Image *image,
260   const char *name)
261 {
262   const StringInfo
263     *profile;
264 
265   assert(image != (Image *) NULL);
266   assert(image->signature == MagickCoreSignature);
267   if (image->debug != MagickFalse)
268     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
269   if (image->profiles == (SplayTreeInfo *) NULL)
270     return((StringInfo *) NULL);
271   profile=(const StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
272     image->profiles,name);
273   return(profile);
274 }
275 
276 /*
277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
278 %                                                                             %
279 %                                                                             %
280 %                                                                             %
281 %   G e t N e x t I m a g e P r o f i l e                                     %
282 %                                                                             %
283 %                                                                             %
284 %                                                                             %
285 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
286 %
287 %  GetNextImageProfile() gets the next profile name for an image.
288 %
289 %  The format of the GetNextImageProfile method is:
290 %
291 %      char *GetNextImageProfile(const Image *image)
292 %
293 %  A description of each parameter follows:
294 %
295 %    o hash_info: the hash info.
296 %
297 */
GetNextImageProfile(const Image * image)298 MagickExport char *GetNextImageProfile(const Image *image)
299 {
300   assert(image != (Image *) NULL);
301   assert(image->signature == MagickCoreSignature);
302   if (image->debug != MagickFalse)
303     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
304   if (image->profiles == (SplayTreeInfo *) NULL)
305     return((char *) NULL);
306   return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->profiles));
307 }
308 
309 /*
310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
311 %                                                                             %
312 %                                                                             %
313 %                                                                             %
314 %   P r o f i l e I m a g e                                                   %
315 %                                                                             %
316 %                                                                             %
317 %                                                                             %
318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
319 %
320 %  ProfileImage() associates, applies, or removes an ICM, IPTC, or generic
321 %  profile with / to / from an image.  If the profile is NULL, it is removed
322 %  from the image otherwise added or applied.  Use a name of '*' and a profile
323 %  of NULL to remove all profiles from the image.
324 %
325 %  ICC and ICM profiles are handled as follows: If the image does not have
326 %  an associated color profile, the one you provide is associated with the
327 %  image and the image pixels are not transformed.  Otherwise, the colorspace
328 %  transform defined by the existing and new profile are applied to the image
329 %  pixels and the new profile is associated with the image.
330 %
331 %  The format of the ProfileImage method is:
332 %
333 %      MagickBooleanType ProfileImage(Image *image,const char *name,
334 %        const void *datum,const size_t length,const MagickBooleanType clone)
335 %
336 %  A description of each parameter follows:
337 %
338 %    o image: the image.
339 %
340 %    o name: Name of profile to add or remove: ICC, IPTC, or generic profile.
341 %
342 %    o datum: the profile data.
343 %
344 %    o length: the length of the profile.
345 %
346 %    o clone: should be MagickFalse.
347 %
348 */
349 
350 #if defined(MAGICKCORE_LCMS_DELEGATE)
351 
352 typedef struct _LCMSInfo
353 {
354   ColorspaceType
355     colorspace;
356 
357   cmsUInt32Number
358     type;
359 
360   size_t
361     channels;
362 
363   cmsHPROFILE
364     profile;
365 
366   int
367     intent;
368 
369   double
370     scale,
371     translate;
372 
373   void
374     **magick_restrict pixels;
375 } LCMSInfo;
376 
377 #if LCMS_VERSION < 2060
cmsGetContextUserData(cmsContext ContextID)378 static void* cmsGetContextUserData(cmsContext ContextID)
379 {
380   return(ContextID);
381 }
382 
cmsCreateContext(void * magick_unused (Plugin),void * UserData)383 static cmsContext cmsCreateContext(void *magick_unused(Plugin),void *UserData)
384 {
385   magick_unreferenced(Plugin);
386   return((cmsContext) UserData);
387 }
388 
cmsSetLogErrorHandlerTHR(cmsContext magick_unused (ContextID),cmsLogErrorHandlerFunction Fn)389 static void cmsSetLogErrorHandlerTHR(cmsContext magick_unused(ContextID),
390   cmsLogErrorHandlerFunction Fn)
391 {
392   magick_unreferenced(ContextID);
393   cmsSetLogErrorHandler(Fn);
394 }
395 
cmsDeleteContext(cmsContext magick_unused (ContextID))396 static void cmsDeleteContext(cmsContext magick_unused(ContextID))
397 {
398   magick_unreferenced(ContextID);
399 }
400 #endif
401 
DestroyPixelThreadSet(void ** pixels)402 static void **DestroyPixelThreadSet(void **pixels)
403 {
404   ssize_t
405     i;
406 
407   if (pixels == (void **) NULL)
408     return((void **) NULL);
409   for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
410     if (pixels[i] != (void *) NULL)
411       pixels[i]=RelinquishMagickMemory(pixels[i]);
412   pixels=(void **) RelinquishMagickMemory(pixels);
413   return(pixels);
414 }
415 
AcquirePixelThreadSet(const size_t columns,const size_t channels,MagickBooleanType highres)416 static void **AcquirePixelThreadSet(const size_t columns,
417   const size_t channels,MagickBooleanType highres)
418 {
419   ssize_t
420     i;
421 
422   size_t
423     number_threads;
424 
425   size_t
426     size;
427 
428   void
429     **pixels;
430 
431   number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
432   pixels=(void **) AcquireQuantumMemory(number_threads,sizeof(*pixels));
433   if (pixels == (void **) NULL)
434     return((void **) NULL);
435   (void) memset(pixels,0,number_threads*sizeof(*pixels));
436   size=sizeof(double);
437   if (highres == MagickFalse)
438     size=sizeof(Quantum);
439   for (i=0; i < (ssize_t) number_threads; i++)
440   {
441     pixels[i]=AcquireQuantumMemory(columns,channels*size);
442     if (pixels[i] == (void *) NULL)
443       return(DestroyPixelThreadSet(pixels));
444   }
445   return(pixels);
446 }
447 
DestroyTransformThreadSet(cmsHTRANSFORM * transform)448 static cmsHTRANSFORM *DestroyTransformThreadSet(cmsHTRANSFORM *transform)
449 {
450   ssize_t
451     i;
452 
453   assert(transform != (cmsHTRANSFORM *) NULL);
454   for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
455     if (transform[i] != (cmsHTRANSFORM) NULL)
456       cmsDeleteTransform(transform[i]);
457   transform=(cmsHTRANSFORM *) RelinquishMagickMemory(transform);
458   return(transform);
459 }
460 
AcquireTransformThreadSet(const LCMSInfo * source_info,const LCMSInfo * target_info,const cmsUInt32Number flags,cmsContext cms_context)461 static cmsHTRANSFORM *AcquireTransformThreadSet(const LCMSInfo *source_info,
462   const LCMSInfo *target_info,const cmsUInt32Number flags,
463   cmsContext cms_context)
464 {
465   cmsHTRANSFORM
466     *transform;
467 
468   ssize_t
469     i;
470 
471   size_t
472     number_threads;
473 
474   number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
475   transform=(cmsHTRANSFORM *) AcquireQuantumMemory(number_threads,
476     sizeof(*transform));
477   if (transform == (cmsHTRANSFORM *) NULL)
478     return((cmsHTRANSFORM *) NULL);
479   (void) memset(transform,0,number_threads*sizeof(*transform));
480   for (i=0; i < (ssize_t) number_threads; i++)
481   {
482     transform[i]=cmsCreateTransformTHR(cms_context,source_info->profile,
483       source_info->type,target_info->profile,target_info->type,
484       target_info->intent,flags);
485     if (transform[i] == (cmsHTRANSFORM) NULL)
486       return(DestroyTransformThreadSet(transform));
487   }
488   return(transform);
489 }
490 
CMSExceptionHandler(cmsContext context,cmsUInt32Number severity,const char * message)491 static void CMSExceptionHandler(cmsContext context,cmsUInt32Number severity,
492   const char *message)
493 {
494   CMSExceptionInfo
495     *cms_exception;
496 
497   ExceptionInfo
498     *exception;
499 
500   Image
501     *image;
502 
503   cms_exception=(CMSExceptionInfo *) cmsGetContextUserData(context);
504   if (cms_exception == (CMSExceptionInfo *) NULL)
505     return;
506   exception=cms_exception->exception;
507   if (exception == (ExceptionInfo *) NULL)
508     return;
509   image=cms_exception->image;
510   if (image == (Image *) NULL)
511     {
512       (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
513         "UnableToTransformColorspace","`%s'","unknown context");
514       return;
515     }
516   if (image->debug != MagickFalse)
517     (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%u, %s",
518       severity,message != (char *) NULL ? message : "no message");
519   (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
520     "UnableToTransformColorspace","`%s', %s (#%u)",image->filename,
521     message != (char *) NULL ? message : "no message",severity);
522 }
523 
TransformDoublePixels(const int id,const Image * image,const LCMSInfo * source_info,const LCMSInfo * target_info,const cmsHTRANSFORM * transform,Quantum * q)524 static void TransformDoublePixels(const int id,const Image* image,
525   const LCMSInfo *source_info,const LCMSInfo *target_info,
526   const cmsHTRANSFORM *transform,Quantum *q)
527 {
528 #define GetLCMSPixel(source_info,pixel) \
529   (source_info->scale*QuantumScale*(pixel)+source_info->translate)
530 #define SetLCMSPixel(target_info,pixel) \
531   ClampToQuantum(target_info->scale*QuantumRange*(pixel)+target_info->translate)
532 
533   double
534     *p;
535 
536   ssize_t
537     x;
538 
539   p=(double *) source_info->pixels[id];
540   for (x=0; x < (ssize_t) image->columns; x++)
541   {
542     *p++=GetLCMSPixel(source_info,GetPixelRed(image,q));
543     if (source_info->channels > 1)
544       {
545         *p++=GetLCMSPixel(source_info,GetPixelGreen(image,q));
546         *p++=GetLCMSPixel(source_info,GetPixelBlue(image,q));
547       }
548     if (source_info->channels > 3)
549       *p++=GetLCMSPixel(source_info,GetPixelBlack(image,q));
550     q+=GetPixelChannels(image);
551   }
552   cmsDoTransform(transform[id],source_info->pixels[id],
553     target_info->pixels[id],(unsigned int) image->columns);
554   p=(double *) target_info->pixels[id];
555   q-=GetPixelChannels(image)*image->columns;
556   for (x=0; x < (ssize_t) image->columns; x++)
557   {
558     if (target_info->channels == 1)
559       SetPixelGray(image,SetLCMSPixel(target_info,*p),q);
560     else
561       SetPixelRed(image,SetLCMSPixel(target_info,*p),q);
562     p++;
563     if (target_info->channels > 1)
564       {
565         SetPixelGreen(image,SetLCMSPixel(target_info,*p),q);
566         p++;
567         SetPixelBlue(image,SetLCMSPixel(target_info,*p),q);
568         p++;
569       }
570     if (target_info->channels > 3)
571       {
572         SetPixelBlack(image,SetLCMSPixel(target_info,*p),q);
573         p++;
574       }
575     q+=GetPixelChannels(image);
576   }
577 }
578 
TransformQuantumPixels(const int id,const Image * image,const LCMSInfo * source_info,const LCMSInfo * target_info,const cmsHTRANSFORM * transform,Quantum * q)579 static void TransformQuantumPixels(const int id,const Image* image,
580   const LCMSInfo *source_info,const LCMSInfo *target_info,
581   const cmsHTRANSFORM *transform,Quantum *q)
582 {
583   Quantum
584     *p;
585 
586   ssize_t
587     x;
588 
589   p=(Quantum *) source_info->pixels[id];
590   for (x=0; x < (ssize_t) image->columns; x++)
591   {
592     *p++=GetPixelRed(image,q);
593     if (source_info->channels > 1)
594       {
595         *p++=GetPixelGreen(image,q);
596         *p++=GetPixelBlue(image,q);
597       }
598     if (source_info->channels > 3)
599       *p++=GetPixelBlack(image,q);
600     q+=GetPixelChannels(image);
601   }
602   cmsDoTransform(transform[id],source_info->pixels[id],
603     target_info->pixels[id],(unsigned int) image->columns);
604   p=(Quantum *) target_info->pixels[id];
605   q-=GetPixelChannels(image)*image->columns;
606   for (x=0; x < (ssize_t) image->columns; x++)
607   {
608     if (target_info->channels == 1)
609       SetPixelGray(image,*p++,q);
610     else
611       SetPixelRed(image,*p++,q);
612     if (target_info->channels > 1)
613       {
614         SetPixelGreen(image,*p++,q);
615         SetPixelBlue(image,*p++,q);
616       }
617     if (target_info->channels > 3)
618       SetPixelBlack(image,*p++,q);
619     q+=GetPixelChannels(image);
620   }
621 }
622 #endif
623 
SetsRGBImageProfile(Image * image,ExceptionInfo * exception)624 static MagickBooleanType SetsRGBImageProfile(Image *image,
625   ExceptionInfo *exception)
626 {
627   static unsigned char
628     sRGBProfile[] =
629     {
630       0x00, 0x00, 0x0c, 0x8c, 0x61, 0x72, 0x67, 0x6c, 0x02, 0x20, 0x00, 0x00,
631       0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20,
632       0x07, 0xde, 0x00, 0x01, 0x00, 0x06, 0x00, 0x16, 0x00, 0x0f, 0x00, 0x3a,
633       0x61, 0x63, 0x73, 0x70, 0x4d, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00,
634       0x49, 0x45, 0x43, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00,
635       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6,
636       0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x61, 0x72, 0x67, 0x6c,
637       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
638       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
639       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
640       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
641       0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x99,
642       0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0xec, 0x00, 0x00, 0x00, 0x67,
643       0x64, 0x6d, 0x6e, 0x64, 0x00, 0x00, 0x02, 0x54, 0x00, 0x00, 0x00, 0x70,
644       0x64, 0x6d, 0x64, 0x64, 0x00, 0x00, 0x02, 0xc4, 0x00, 0x00, 0x00, 0x88,
645       0x74, 0x65, 0x63, 0x68, 0x00, 0x00, 0x03, 0x4c, 0x00, 0x00, 0x00, 0x0c,
646       0x76, 0x75, 0x65, 0x64, 0x00, 0x00, 0x03, 0x58, 0x00, 0x00, 0x00, 0x67,
647       0x76, 0x69, 0x65, 0x77, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x24,
648       0x6c, 0x75, 0x6d, 0x69, 0x00, 0x00, 0x03, 0xe4, 0x00, 0x00, 0x00, 0x14,
649       0x6d, 0x65, 0x61, 0x73, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x24,
650       0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x00, 0x00, 0x14,
651       0x62, 0x6b, 0x70, 0x74, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x14,
652       0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x44, 0x00, 0x00, 0x00, 0x14,
653       0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x58, 0x00, 0x00, 0x00, 0x14,
654       0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x6c, 0x00, 0x00, 0x00, 0x14,
655       0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
656       0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
657       0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
658       0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
659       0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36,
660       0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x28, 0x45, 0x71, 0x75, 0x69, 0x76,
661       0x61, 0x6c, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x77, 0x77,
662       0x2e, 0x73, 0x72, 0x67, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x31, 0x39,
663       0x39, 0x38, 0x20, 0x48, 0x50, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c,
664       0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
665       0x00, 0x3f, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31,
666       0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x28, 0x45, 0x71, 0x75,
667       0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77,
668       0x77, 0x77, 0x2e, 0x73, 0x72, 0x67, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x20,
669       0x31, 0x39, 0x39, 0x38, 0x20, 0x48, 0x50, 0x20, 0x70, 0x72, 0x6f, 0x66,
670       0x69, 0x6c, 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
671       0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x72, 0x65, 0x61,
672       0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x47, 0x72, 0x61, 0x65, 0x6d,
673       0x65, 0x20, 0x57, 0x2e, 0x20, 0x47, 0x69, 0x6c, 0x6c, 0x2e, 0x20, 0x52,
674       0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f,
675       0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
676       0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x20, 0x4e, 0x6f, 0x20, 0x57,
677       0x61, 0x72, 0x72, 0x61, 0x6e, 0x74, 0x79, 0x2c, 0x20, 0x55, 0x73, 0x65,
678       0x20, 0x61, 0x74, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x6f, 0x77, 0x6e,
679       0x20, 0x72, 0x69, 0x73, 0x6b, 0x2e, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63,
680       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20,
681       0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69,
682       0x65, 0x63, 0x2e, 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
683       0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20, 0x68, 0x74, 0x74,
684       0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x65, 0x63, 0x2e,
685       0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
686       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
687       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
688       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
689       0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e,
690       0x49, 0x45, 0x43, 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e,
691       0x31, 0x20, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47,
692       0x42, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61,
693       0x63, 0x65, 0x20, 0x2d, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00,
694       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x49, 0x45, 0x43,
695       0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x44,
696       0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47, 0x42, 0x20, 0x63,
697       0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20,
698       0x2d, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
699       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
700       0x00, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x20, 0x00, 0x00, 0x00, 0x00,
701       0x43, 0x52, 0x54, 0x20, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00,
702       0x00, 0x00, 0x00, 0x0d, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36,
703       0x2d, 0x32, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
704       0x00, 0x00, 0x00, 0x0d, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36,
705       0x2d, 0x32, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
706       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
707       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
708       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
709       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
710       0x76, 0x69, 0x65, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xa4, 0x7c,
711       0x00, 0x14, 0x5f, 0x30, 0x00, 0x10, 0xce, 0x02, 0x00, 0x03, 0xed, 0xb2,
712       0x00, 0x04, 0x13, 0x0a, 0x00, 0x03, 0x5c, 0x67, 0x00, 0x00, 0x00, 0x01,
713       0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x0a, 0x3d,
714       0x00, 0x50, 0x00, 0x00, 0x00, 0x57, 0x1e, 0xb8, 0x6d, 0x65, 0x61, 0x73,
715       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
716       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
717       0x00, 0x00, 0x02, 0x8f, 0x00, 0x00, 0x00, 0x02, 0x58, 0x59, 0x5a, 0x20,
718       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0x51, 0x00, 0x01, 0x00, 0x00,
719       0x00, 0x01, 0x16, 0xcc, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
720       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
721       0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xa0,
722       0x00, 0x00, 0x38, 0xf5, 0x00, 0x00, 0x03, 0x90, 0x58, 0x59, 0x5a, 0x20,
723       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x97, 0x00, 0x00, 0xb7, 0x87,
724       0x00, 0x00, 0x18, 0xd9, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
725       0x00, 0x00, 0x24, 0x9f, 0x00, 0x00, 0x0f, 0x84, 0x00, 0x00, 0xb6, 0xc4,
726       0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
727       0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x14, 0x00, 0x19,
728       0x00, 0x1e, 0x00, 0x23, 0x00, 0x28, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x37,
729       0x00, 0x3b, 0x00, 0x40, 0x00, 0x45, 0x00, 0x4a, 0x00, 0x4f, 0x00, 0x54,
730       0x00, 0x59, 0x00, 0x5e, 0x00, 0x63, 0x00, 0x68, 0x00, 0x6d, 0x00, 0x72,
731       0x00, 0x77, 0x00, 0x7c, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8b, 0x00, 0x90,
732       0x00, 0x95, 0x00, 0x9a, 0x00, 0x9f, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xae,
733       0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc1, 0x00, 0xc6, 0x00, 0xcb,
734       0x00, 0xd0, 0x00, 0xd5, 0x00, 0xdb, 0x00, 0xe0, 0x00, 0xe5, 0x00, 0xeb,
735       0x00, 0xf0, 0x00, 0xf6, 0x00, 0xfb, 0x01, 0x01, 0x01, 0x07, 0x01, 0x0d,
736       0x01, 0x13, 0x01, 0x19, 0x01, 0x1f, 0x01, 0x25, 0x01, 0x2b, 0x01, 0x32,
737       0x01, 0x38, 0x01, 0x3e, 0x01, 0x45, 0x01, 0x4c, 0x01, 0x52, 0x01, 0x59,
738       0x01, 0x60, 0x01, 0x67, 0x01, 0x6e, 0x01, 0x75, 0x01, 0x7c, 0x01, 0x83,
739       0x01, 0x8b, 0x01, 0x92, 0x01, 0x9a, 0x01, 0xa1, 0x01, 0xa9, 0x01, 0xb1,
740       0x01, 0xb9, 0x01, 0xc1, 0x01, 0xc9, 0x01, 0xd1, 0x01, 0xd9, 0x01, 0xe1,
741       0x01, 0xe9, 0x01, 0xf2, 0x01, 0xfa, 0x02, 0x03, 0x02, 0x0c, 0x02, 0x14,
742       0x02, 0x1d, 0x02, 0x26, 0x02, 0x2f, 0x02, 0x38, 0x02, 0x41, 0x02, 0x4b,
743       0x02, 0x54, 0x02, 0x5d, 0x02, 0x67, 0x02, 0x71, 0x02, 0x7a, 0x02, 0x84,
744       0x02, 0x8e, 0x02, 0x98, 0x02, 0xa2, 0x02, 0xac, 0x02, 0xb6, 0x02, 0xc1,
745       0x02, 0xcb, 0x02, 0xd5, 0x02, 0xe0, 0x02, 0xeb, 0x02, 0xf5, 0x03, 0x00,
746       0x03, 0x0b, 0x03, 0x16, 0x03, 0x21, 0x03, 0x2d, 0x03, 0x38, 0x03, 0x43,
747       0x03, 0x4f, 0x03, 0x5a, 0x03, 0x66, 0x03, 0x72, 0x03, 0x7e, 0x03, 0x8a,
748       0x03, 0x96, 0x03, 0xa2, 0x03, 0xae, 0x03, 0xba, 0x03, 0xc7, 0x03, 0xd3,
749       0x03, 0xe0, 0x03, 0xec, 0x03, 0xf9, 0x04, 0x06, 0x04, 0x13, 0x04, 0x20,
750       0x04, 0x2d, 0x04, 0x3b, 0x04, 0x48, 0x04, 0x55, 0x04, 0x63, 0x04, 0x71,
751       0x04, 0x7e, 0x04, 0x8c, 0x04, 0x9a, 0x04, 0xa8, 0x04, 0xb6, 0x04, 0xc4,
752       0x04, 0xd3, 0x04, 0xe1, 0x04, 0xf0, 0x04, 0xfe, 0x05, 0x0d, 0x05, 0x1c,
753       0x05, 0x2b, 0x05, 0x3a, 0x05, 0x49, 0x05, 0x58, 0x05, 0x67, 0x05, 0x77,
754       0x05, 0x86, 0x05, 0x96, 0x05, 0xa6, 0x05, 0xb5, 0x05, 0xc5, 0x05, 0xd5,
755       0x05, 0xe5, 0x05, 0xf6, 0x06, 0x06, 0x06, 0x16, 0x06, 0x27, 0x06, 0x37,
756       0x06, 0x48, 0x06, 0x59, 0x06, 0x6a, 0x06, 0x7b, 0x06, 0x8c, 0x06, 0x9d,
757       0x06, 0xaf, 0x06, 0xc0, 0x06, 0xd1, 0x06, 0xe3, 0x06, 0xf5, 0x07, 0x07,
758       0x07, 0x19, 0x07, 0x2b, 0x07, 0x3d, 0x07, 0x4f, 0x07, 0x61, 0x07, 0x74,
759       0x07, 0x86, 0x07, 0x99, 0x07, 0xac, 0x07, 0xbf, 0x07, 0xd2, 0x07, 0xe5,
760       0x07, 0xf8, 0x08, 0x0b, 0x08, 0x1f, 0x08, 0x32, 0x08, 0x46, 0x08, 0x5a,
761       0x08, 0x6e, 0x08, 0x82, 0x08, 0x96, 0x08, 0xaa, 0x08, 0xbe, 0x08, 0xd2,
762       0x08, 0xe7, 0x08, 0xfb, 0x09, 0x10, 0x09, 0x25, 0x09, 0x3a, 0x09, 0x4f,
763       0x09, 0x64, 0x09, 0x79, 0x09, 0x8f, 0x09, 0xa4, 0x09, 0xba, 0x09, 0xcf,
764       0x09, 0xe5, 0x09, 0xfb, 0x0a, 0x11, 0x0a, 0x27, 0x0a, 0x3d, 0x0a, 0x54,
765       0x0a, 0x6a, 0x0a, 0x81, 0x0a, 0x98, 0x0a, 0xae, 0x0a, 0xc5, 0x0a, 0xdc,
766       0x0a, 0xf3, 0x0b, 0x0b, 0x0b, 0x22, 0x0b, 0x39, 0x0b, 0x51, 0x0b, 0x69,
767       0x0b, 0x80, 0x0b, 0x98, 0x0b, 0xb0, 0x0b, 0xc8, 0x0b, 0xe1, 0x0b, 0xf9,
768       0x0c, 0x12, 0x0c, 0x2a, 0x0c, 0x43, 0x0c, 0x5c, 0x0c, 0x75, 0x0c, 0x8e,
769       0x0c, 0xa7, 0x0c, 0xc0, 0x0c, 0xd9, 0x0c, 0xf3, 0x0d, 0x0d, 0x0d, 0x26,
770       0x0d, 0x40, 0x0d, 0x5a, 0x0d, 0x74, 0x0d, 0x8e, 0x0d, 0xa9, 0x0d, 0xc3,
771       0x0d, 0xde, 0x0d, 0xf8, 0x0e, 0x13, 0x0e, 0x2e, 0x0e, 0x49, 0x0e, 0x64,
772       0x0e, 0x7f, 0x0e, 0x9b, 0x0e, 0xb6, 0x0e, 0xd2, 0x0e, 0xee, 0x0f, 0x09,
773       0x0f, 0x25, 0x0f, 0x41, 0x0f, 0x5e, 0x0f, 0x7a, 0x0f, 0x96, 0x0f, 0xb3,
774       0x0f, 0xcf, 0x0f, 0xec, 0x10, 0x09, 0x10, 0x26, 0x10, 0x43, 0x10, 0x61,
775       0x10, 0x7e, 0x10, 0x9b, 0x10, 0xb9, 0x10, 0xd7, 0x10, 0xf5, 0x11, 0x13,
776       0x11, 0x31, 0x11, 0x4f, 0x11, 0x6d, 0x11, 0x8c, 0x11, 0xaa, 0x11, 0xc9,
777       0x11, 0xe8, 0x12, 0x07, 0x12, 0x26, 0x12, 0x45, 0x12, 0x64, 0x12, 0x84,
778       0x12, 0xa3, 0x12, 0xc3, 0x12, 0xe3, 0x13, 0x03, 0x13, 0x23, 0x13, 0x43,
779       0x13, 0x63, 0x13, 0x83, 0x13, 0xa4, 0x13, 0xc5, 0x13, 0xe5, 0x14, 0x06,
780       0x14, 0x27, 0x14, 0x49, 0x14, 0x6a, 0x14, 0x8b, 0x14, 0xad, 0x14, 0xce,
781       0x14, 0xf0, 0x15, 0x12, 0x15, 0x34, 0x15, 0x56, 0x15, 0x78, 0x15, 0x9b,
782       0x15, 0xbd, 0x15, 0xe0, 0x16, 0x03, 0x16, 0x26, 0x16, 0x49, 0x16, 0x6c,
783       0x16, 0x8f, 0x16, 0xb2, 0x16, 0xd6, 0x16, 0xfa, 0x17, 0x1d, 0x17, 0x41,
784       0x17, 0x65, 0x17, 0x89, 0x17, 0xae, 0x17, 0xd2, 0x17, 0xf7, 0x18, 0x1b,
785       0x18, 0x40, 0x18, 0x65, 0x18, 0x8a, 0x18, 0xaf, 0x18, 0xd5, 0x18, 0xfa,
786       0x19, 0x20, 0x19, 0x45, 0x19, 0x6b, 0x19, 0x91, 0x19, 0xb7, 0x19, 0xdd,
787       0x1a, 0x04, 0x1a, 0x2a, 0x1a, 0x51, 0x1a, 0x77, 0x1a, 0x9e, 0x1a, 0xc5,
788       0x1a, 0xec, 0x1b, 0x14, 0x1b, 0x3b, 0x1b, 0x63, 0x1b, 0x8a, 0x1b, 0xb2,
789       0x1b, 0xda, 0x1c, 0x02, 0x1c, 0x2a, 0x1c, 0x52, 0x1c, 0x7b, 0x1c, 0xa3,
790       0x1c, 0xcc, 0x1c, 0xf5, 0x1d, 0x1e, 0x1d, 0x47, 0x1d, 0x70, 0x1d, 0x99,
791       0x1d, 0xc3, 0x1d, 0xec, 0x1e, 0x16, 0x1e, 0x40, 0x1e, 0x6a, 0x1e, 0x94,
792       0x1e, 0xbe, 0x1e, 0xe9, 0x1f, 0x13, 0x1f, 0x3e, 0x1f, 0x69, 0x1f, 0x94,
793       0x1f, 0xbf, 0x1f, 0xea, 0x20, 0x15, 0x20, 0x41, 0x20, 0x6c, 0x20, 0x98,
794       0x20, 0xc4, 0x20, 0xf0, 0x21, 0x1c, 0x21, 0x48, 0x21, 0x75, 0x21, 0xa1,
795       0x21, 0xce, 0x21, 0xfb, 0x22, 0x27, 0x22, 0x55, 0x22, 0x82, 0x22, 0xaf,
796       0x22, 0xdd, 0x23, 0x0a, 0x23, 0x38, 0x23, 0x66, 0x23, 0x94, 0x23, 0xc2,
797       0x23, 0xf0, 0x24, 0x1f, 0x24, 0x4d, 0x24, 0x7c, 0x24, 0xab, 0x24, 0xda,
798       0x25, 0x09, 0x25, 0x38, 0x25, 0x68, 0x25, 0x97, 0x25, 0xc7, 0x25, 0xf7,
799       0x26, 0x27, 0x26, 0x57, 0x26, 0x87, 0x26, 0xb7, 0x26, 0xe8, 0x27, 0x18,
800       0x27, 0x49, 0x27, 0x7a, 0x27, 0xab, 0x27, 0xdc, 0x28, 0x0d, 0x28, 0x3f,
801       0x28, 0x71, 0x28, 0xa2, 0x28, 0xd4, 0x29, 0x06, 0x29, 0x38, 0x29, 0x6b,
802       0x29, 0x9d, 0x29, 0xd0, 0x2a, 0x02, 0x2a, 0x35, 0x2a, 0x68, 0x2a, 0x9b,
803       0x2a, 0xcf, 0x2b, 0x02, 0x2b, 0x36, 0x2b, 0x69, 0x2b, 0x9d, 0x2b, 0xd1,
804       0x2c, 0x05, 0x2c, 0x39, 0x2c, 0x6e, 0x2c, 0xa2, 0x2c, 0xd7, 0x2d, 0x0c,
805       0x2d, 0x41, 0x2d, 0x76, 0x2d, 0xab, 0x2d, 0xe1, 0x2e, 0x16, 0x2e, 0x4c,
806       0x2e, 0x82, 0x2e, 0xb7, 0x2e, 0xee, 0x2f, 0x24, 0x2f, 0x5a, 0x2f, 0x91,
807       0x2f, 0xc7, 0x2f, 0xfe, 0x30, 0x35, 0x30, 0x6c, 0x30, 0xa4, 0x30, 0xdb,
808       0x31, 0x12, 0x31, 0x4a, 0x31, 0x82, 0x31, 0xba, 0x31, 0xf2, 0x32, 0x2a,
809       0x32, 0x63, 0x32, 0x9b, 0x32, 0xd4, 0x33, 0x0d, 0x33, 0x46, 0x33, 0x7f,
810       0x33, 0xb8, 0x33, 0xf1, 0x34, 0x2b, 0x34, 0x65, 0x34, 0x9e, 0x34, 0xd8,
811       0x35, 0x13, 0x35, 0x4d, 0x35, 0x87, 0x35, 0xc2, 0x35, 0xfd, 0x36, 0x37,
812       0x36, 0x72, 0x36, 0xae, 0x36, 0xe9, 0x37, 0x24, 0x37, 0x60, 0x37, 0x9c,
813       0x37, 0xd7, 0x38, 0x14, 0x38, 0x50, 0x38, 0x8c, 0x38, 0xc8, 0x39, 0x05,
814       0x39, 0x42, 0x39, 0x7f, 0x39, 0xbc, 0x39, 0xf9, 0x3a, 0x36, 0x3a, 0x74,
815       0x3a, 0xb2, 0x3a, 0xef, 0x3b, 0x2d, 0x3b, 0x6b, 0x3b, 0xaa, 0x3b, 0xe8,
816       0x3c, 0x27, 0x3c, 0x65, 0x3c, 0xa4, 0x3c, 0xe3, 0x3d, 0x22, 0x3d, 0x61,
817       0x3d, 0xa1, 0x3d, 0xe0, 0x3e, 0x20, 0x3e, 0x60, 0x3e, 0xa0, 0x3e, 0xe0,
818       0x3f, 0x21, 0x3f, 0x61, 0x3f, 0xa2, 0x3f, 0xe2, 0x40, 0x23, 0x40, 0x64,
819       0x40, 0xa6, 0x40, 0xe7, 0x41, 0x29, 0x41, 0x6a, 0x41, 0xac, 0x41, 0xee,
820       0x42, 0x30, 0x42, 0x72, 0x42, 0xb5, 0x42, 0xf7, 0x43, 0x3a, 0x43, 0x7d,
821       0x43, 0xc0, 0x44, 0x03, 0x44, 0x47, 0x44, 0x8a, 0x44, 0xce, 0x45, 0x12,
822       0x45, 0x55, 0x45, 0x9a, 0x45, 0xde, 0x46, 0x22, 0x46, 0x67, 0x46, 0xab,
823       0x46, 0xf0, 0x47, 0x35, 0x47, 0x7b, 0x47, 0xc0, 0x48, 0x05, 0x48, 0x4b,
824       0x48, 0x91, 0x48, 0xd7, 0x49, 0x1d, 0x49, 0x63, 0x49, 0xa9, 0x49, 0xf0,
825       0x4a, 0x37, 0x4a, 0x7d, 0x4a, 0xc4, 0x4b, 0x0c, 0x4b, 0x53, 0x4b, 0x9a,
826       0x4b, 0xe2, 0x4c, 0x2a, 0x4c, 0x72, 0x4c, 0xba, 0x4d, 0x02, 0x4d, 0x4a,
827       0x4d, 0x93, 0x4d, 0xdc, 0x4e, 0x25, 0x4e, 0x6e, 0x4e, 0xb7, 0x4f, 0x00,
828       0x4f, 0x49, 0x4f, 0x93, 0x4f, 0xdd, 0x50, 0x27, 0x50, 0x71, 0x50, 0xbb,
829       0x51, 0x06, 0x51, 0x50, 0x51, 0x9b, 0x51, 0xe6, 0x52, 0x31, 0x52, 0x7c,
830       0x52, 0xc7, 0x53, 0x13, 0x53, 0x5f, 0x53, 0xaa, 0x53, 0xf6, 0x54, 0x42,
831       0x54, 0x8f, 0x54, 0xdb, 0x55, 0x28, 0x55, 0x75, 0x55, 0xc2, 0x56, 0x0f,
832       0x56, 0x5c, 0x56, 0xa9, 0x56, 0xf7, 0x57, 0x44, 0x57, 0x92, 0x57, 0xe0,
833       0x58, 0x2f, 0x58, 0x7d, 0x58, 0xcb, 0x59, 0x1a, 0x59, 0x69, 0x59, 0xb8,
834       0x5a, 0x07, 0x5a, 0x56, 0x5a, 0xa6, 0x5a, 0xf5, 0x5b, 0x45, 0x5b, 0x95,
835       0x5b, 0xe5, 0x5c, 0x35, 0x5c, 0x86, 0x5c, 0xd6, 0x5d, 0x27, 0x5d, 0x78,
836       0x5d, 0xc9, 0x5e, 0x1a, 0x5e, 0x6c, 0x5e, 0xbd, 0x5f, 0x0f, 0x5f, 0x61,
837       0x5f, 0xb3, 0x60, 0x05, 0x60, 0x57, 0x60, 0xaa, 0x60, 0xfc, 0x61, 0x4f,
838       0x61, 0xa2, 0x61, 0xf5, 0x62, 0x49, 0x62, 0x9c, 0x62, 0xf0, 0x63, 0x43,
839       0x63, 0x97, 0x63, 0xeb, 0x64, 0x40, 0x64, 0x94, 0x64, 0xe9, 0x65, 0x3d,
840       0x65, 0x92, 0x65, 0xe7, 0x66, 0x3d, 0x66, 0x92, 0x66, 0xe8, 0x67, 0x3d,
841       0x67, 0x93, 0x67, 0xe9, 0x68, 0x3f, 0x68, 0x96, 0x68, 0xec, 0x69, 0x43,
842       0x69, 0x9a, 0x69, 0xf1, 0x6a, 0x48, 0x6a, 0x9f, 0x6a, 0xf7, 0x6b, 0x4f,
843       0x6b, 0xa7, 0x6b, 0xff, 0x6c, 0x57, 0x6c, 0xaf, 0x6d, 0x08, 0x6d, 0x60,
844       0x6d, 0xb9, 0x6e, 0x12, 0x6e, 0x6b, 0x6e, 0xc4, 0x6f, 0x1e, 0x6f, 0x78,
845       0x6f, 0xd1, 0x70, 0x2b, 0x70, 0x86, 0x70, 0xe0, 0x71, 0x3a, 0x71, 0x95,
846       0x71, 0xf0, 0x72, 0x4b, 0x72, 0xa6, 0x73, 0x01, 0x73, 0x5d, 0x73, 0xb8,
847       0x74, 0x14, 0x74, 0x70, 0x74, 0xcc, 0x75, 0x28, 0x75, 0x85, 0x75, 0xe1,
848       0x76, 0x3e, 0x76, 0x9b, 0x76, 0xf8, 0x77, 0x56, 0x77, 0xb3, 0x78, 0x11,
849       0x78, 0x6e, 0x78, 0xcc, 0x79, 0x2a, 0x79, 0x89, 0x79, 0xe7, 0x7a, 0x46,
850       0x7a, 0xa5, 0x7b, 0x04, 0x7b, 0x63, 0x7b, 0xc2, 0x7c, 0x21, 0x7c, 0x81,
851       0x7c, 0xe1, 0x7d, 0x41, 0x7d, 0xa1, 0x7e, 0x01, 0x7e, 0x62, 0x7e, 0xc2,
852       0x7f, 0x23, 0x7f, 0x84, 0x7f, 0xe5, 0x80, 0x47, 0x80, 0xa8, 0x81, 0x0a,
853       0x81, 0x6b, 0x81, 0xcd, 0x82, 0x30, 0x82, 0x92, 0x82, 0xf4, 0x83, 0x57,
854       0x83, 0xba, 0x84, 0x1d, 0x84, 0x80, 0x84, 0xe3, 0x85, 0x47, 0x85, 0xab,
855       0x86, 0x0e, 0x86, 0x72, 0x86, 0xd7, 0x87, 0x3b, 0x87, 0x9f, 0x88, 0x04,
856       0x88, 0x69, 0x88, 0xce, 0x89, 0x33, 0x89, 0x99, 0x89, 0xfe, 0x8a, 0x64,
857       0x8a, 0xca, 0x8b, 0x30, 0x8b, 0x96, 0x8b, 0xfc, 0x8c, 0x63, 0x8c, 0xca,
858       0x8d, 0x31, 0x8d, 0x98, 0x8d, 0xff, 0x8e, 0x66, 0x8e, 0xce, 0x8f, 0x36,
859       0x8f, 0x9e, 0x90, 0x06, 0x90, 0x6e, 0x90, 0xd6, 0x91, 0x3f, 0x91, 0xa8,
860       0x92, 0x11, 0x92, 0x7a, 0x92, 0xe3, 0x93, 0x4d, 0x93, 0xb6, 0x94, 0x20,
861       0x94, 0x8a, 0x94, 0xf4, 0x95, 0x5f, 0x95, 0xc9, 0x96, 0x34, 0x96, 0x9f,
862       0x97, 0x0a, 0x97, 0x75, 0x97, 0xe0, 0x98, 0x4c, 0x98, 0xb8, 0x99, 0x24,
863       0x99, 0x90, 0x99, 0xfc, 0x9a, 0x68, 0x9a, 0xd5, 0x9b, 0x42, 0x9b, 0xaf,
864       0x9c, 0x1c, 0x9c, 0x89, 0x9c, 0xf7, 0x9d, 0x64, 0x9d, 0xd2, 0x9e, 0x40,
865       0x9e, 0xae, 0x9f, 0x1d, 0x9f, 0x8b, 0x9f, 0xfa, 0xa0, 0x69, 0xa0, 0xd8,
866       0xa1, 0x47, 0xa1, 0xb6, 0xa2, 0x26, 0xa2, 0x96, 0xa3, 0x06, 0xa3, 0x76,
867       0xa3, 0xe6, 0xa4, 0x56, 0xa4, 0xc7, 0xa5, 0x38, 0xa5, 0xa9, 0xa6, 0x1a,
868       0xa6, 0x8b, 0xa6, 0xfd, 0xa7, 0x6e, 0xa7, 0xe0, 0xa8, 0x52, 0xa8, 0xc4,
869       0xa9, 0x37, 0xa9, 0xa9, 0xaa, 0x1c, 0xaa, 0x8f, 0xab, 0x02, 0xab, 0x75,
870       0xab, 0xe9, 0xac, 0x5c, 0xac, 0xd0, 0xad, 0x44, 0xad, 0xb8, 0xae, 0x2d,
871       0xae, 0xa1, 0xaf, 0x16, 0xaf, 0x8b, 0xb0, 0x00, 0xb0, 0x75, 0xb0, 0xea,
872       0xb1, 0x60, 0xb1, 0xd6, 0xb2, 0x4b, 0xb2, 0xc2, 0xb3, 0x38, 0xb3, 0xae,
873       0xb4, 0x25, 0xb4, 0x9c, 0xb5, 0x13, 0xb5, 0x8a, 0xb6, 0x01, 0xb6, 0x79,
874       0xb6, 0xf0, 0xb7, 0x68, 0xb7, 0xe0, 0xb8, 0x59, 0xb8, 0xd1, 0xb9, 0x4a,
875       0xb9, 0xc2, 0xba, 0x3b, 0xba, 0xb5, 0xbb, 0x2e, 0xbb, 0xa7, 0xbc, 0x21,
876       0xbc, 0x9b, 0xbd, 0x15, 0xbd, 0x8f, 0xbe, 0x0a, 0xbe, 0x84, 0xbe, 0xff,
877       0xbf, 0x7a, 0xbf, 0xf5, 0xc0, 0x70, 0xc0, 0xec, 0xc1, 0x67, 0xc1, 0xe3,
878       0xc2, 0x5f, 0xc2, 0xdb, 0xc3, 0x58, 0xc3, 0xd4, 0xc4, 0x51, 0xc4, 0xce,
879       0xc5, 0x4b, 0xc5, 0xc8, 0xc6, 0x46, 0xc6, 0xc3, 0xc7, 0x41, 0xc7, 0xbf,
880       0xc8, 0x3d, 0xc8, 0xbc, 0xc9, 0x3a, 0xc9, 0xb9, 0xca, 0x38, 0xca, 0xb7,
881       0xcb, 0x36, 0xcb, 0xb6, 0xcc, 0x35, 0xcc, 0xb5, 0xcd, 0x35, 0xcd, 0xb5,
882       0xce, 0x36, 0xce, 0xb6, 0xcf, 0x37, 0xcf, 0xb8, 0xd0, 0x39, 0xd0, 0xba,
883       0xd1, 0x3c, 0xd1, 0xbe, 0xd2, 0x3f, 0xd2, 0xc1, 0xd3, 0x44, 0xd3, 0xc6,
884       0xd4, 0x49, 0xd4, 0xcb, 0xd5, 0x4e, 0xd5, 0xd1, 0xd6, 0x55, 0xd6, 0xd8,
885       0xd7, 0x5c, 0xd7, 0xe0, 0xd8, 0x64, 0xd8, 0xe8, 0xd9, 0x6c, 0xd9, 0xf1,
886       0xda, 0x76, 0xda, 0xfb, 0xdb, 0x80, 0xdc, 0x05, 0xdc, 0x8a, 0xdd, 0x10,
887       0xdd, 0x96, 0xde, 0x1c, 0xde, 0xa2, 0xdf, 0x29, 0xdf, 0xaf, 0xe0, 0x36,
888       0xe0, 0xbd, 0xe1, 0x44, 0xe1, 0xcc, 0xe2, 0x53, 0xe2, 0xdb, 0xe3, 0x63,
889       0xe3, 0xeb, 0xe4, 0x73, 0xe4, 0xfc, 0xe5, 0x84, 0xe6, 0x0d, 0xe6, 0x96,
890       0xe7, 0x1f, 0xe7, 0xa9, 0xe8, 0x32, 0xe8, 0xbc, 0xe9, 0x46, 0xe9, 0xd0,
891       0xea, 0x5b, 0xea, 0xe5, 0xeb, 0x70, 0xeb, 0xfb, 0xec, 0x86, 0xed, 0x11,
892       0xed, 0x9c, 0xee, 0x28, 0xee, 0xb4, 0xef, 0x40, 0xef, 0xcc, 0xf0, 0x58,
893       0xf0, 0xe5, 0xf1, 0x72, 0xf1, 0xff, 0xf2, 0x8c, 0xf3, 0x19, 0xf3, 0xa7,
894       0xf4, 0x34, 0xf4, 0xc2, 0xf5, 0x50, 0xf5, 0xde, 0xf6, 0x6d, 0xf6, 0xfb,
895       0xf7, 0x8a, 0xf8, 0x19, 0xf8, 0xa8, 0xf9, 0x38, 0xf9, 0xc7, 0xfa, 0x57,
896       0xfa, 0xe7, 0xfb, 0x77, 0xfc, 0x07, 0xfc, 0x98, 0xfd, 0x29, 0xfd, 0xba,
897       0xfe, 0x4b, 0xfe, 0xdc, 0xff, 0x6d, 0xff, 0xff
898     };
899 
900   StringInfo
901     *profile;
902 
903   MagickBooleanType
904     status;
905 
906   assert(image != (Image *) NULL);
907   assert(image->signature == MagickCoreSignature);
908   if (GetImageProfile(image,"icc") != (const StringInfo *) NULL)
909     return(MagickFalse);
910   profile=AcquireStringInfo(sizeof(sRGBProfile));
911   SetStringInfoDatum(profile,sRGBProfile);
912   status=SetImageProfile(image,"icc",profile,exception);
913   profile=DestroyStringInfo(profile);
914   return(status);
915 }
916 
ProfileImage(Image * image,const char * name,const void * datum,const size_t length,ExceptionInfo * exception)917 MagickExport MagickBooleanType ProfileImage(Image *image,const char *name,
918   const void *datum,const size_t length,ExceptionInfo *exception)
919 {
920 #define ProfileImageTag  "Profile/Image"
921 #ifndef TYPE_XYZ_8
922   #define TYPE_XYZ_8 (COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(1))
923 #endif
924 #define ThrowProfileException(severity,tag,context) \
925 { \
926   if (profile != (StringInfo *) NULL) \
927      profile=DestroyStringInfo(profile); \
928   if (cms_context != (cmsContext) NULL) \
929     cmsDeleteContext(cms_context); \
930   if (source_info.profile != (cmsHPROFILE) NULL) \
931     (void) cmsCloseProfile(source_info.profile); \
932   if (target_info.profile != (cmsHPROFILE) NULL) \
933     (void) cmsCloseProfile(target_info.profile); \
934   ThrowBinaryException(severity,tag,context); \
935 }
936 
937   MagickBooleanType
938     status;
939 
940   StringInfo
941     *profile;
942 
943   assert(image != (Image *) NULL);
944   assert(image->signature == MagickCoreSignature);
945   if (image->debug != MagickFalse)
946     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
947   assert(name != (const char *) NULL);
948   if ((datum == (const void *) NULL) || (length == 0))
949     {
950       char
951         *next;
952 
953       /*
954         Delete image profile(s).
955       */
956       ResetImageProfileIterator(image);
957       for (next=GetNextImageProfile(image); next != (const char *) NULL; )
958       {
959         if (IsOptionMember(next,name) != MagickFalse)
960           {
961             (void) DeleteImageProfile(image,next);
962             ResetImageProfileIterator(image);
963           }
964         next=GetNextImageProfile(image);
965       }
966       return(MagickTrue);
967     }
968   /*
969     Add a ICC, IPTC, or generic profile to the image.
970   */
971   status=MagickTrue;
972   profile=AcquireStringInfo((size_t) length);
973   SetStringInfoDatum(profile,(unsigned char *) datum);
974   if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
975     status=SetImageProfile(image,name,profile,exception);
976   else
977     {
978       const StringInfo
979         *icc_profile;
980 
981       icc_profile=GetImageProfile(image,"icc");
982       if ((icc_profile != (const StringInfo *) NULL) &&
983           (CompareStringInfo(icc_profile,profile) == 0))
984         {
985           const char
986             *value;
987 
988           value=GetImageProperty(image,"exif:ColorSpace",exception);
989           (void) value;
990           if (LocaleCompare(value,"1") != 0)
991             (void) SetsRGBImageProfile(image,exception);
992           value=GetImageProperty(image,"exif:InteroperabilityIndex",exception);
993           if (LocaleCompare(value,"R98.") != 0)
994             (void) SetsRGBImageProfile(image,exception);
995           icc_profile=GetImageProfile(image,"icc");
996         }
997       if ((icc_profile != (const StringInfo *) NULL) &&
998           (CompareStringInfo(icc_profile,profile) == 0))
999         {
1000           profile=DestroyStringInfo(profile);
1001           return(MagickTrue);
1002         }
1003 #if !defined(MAGICKCORE_LCMS_DELEGATE)
1004       (void) ThrowMagickException(exception,GetMagickModule(),
1005         MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
1006         "'%s' (LCMS)",image->filename);
1007 #else
1008       {
1009         cmsContext
1010           cms_context;
1011 
1012         CMSExceptionInfo
1013           cms_exception;
1014 
1015         LCMSInfo
1016           source_info,
1017           target_info;
1018 
1019         /*
1020           Transform pixel colors as defined by the color profiles.
1021         */
1022         cms_exception.image=image;
1023         cms_exception.exception=exception;
1024         cms_context=cmsCreateContext(NULL,&cms_exception);
1025         if (cms_context == (cmsContext) NULL)
1026           ThrowBinaryException(ResourceLimitError,
1027             "ColorspaceColorProfileMismatch",name);
1028         cmsSetLogErrorHandlerTHR(cms_context,CMSExceptionHandler);
1029         source_info.profile=cmsOpenProfileFromMemTHR(cms_context,
1030           GetStringInfoDatum(profile),(cmsUInt32Number)
1031           GetStringInfoLength(profile));
1032         if (source_info.profile == (cmsHPROFILE) NULL)
1033           {
1034             cmsDeleteContext(cms_context);
1035             ThrowBinaryException(ResourceLimitError,
1036               "ColorspaceColorProfileMismatch",name);
1037           }
1038         if ((cmsGetDeviceClass(source_info.profile) != cmsSigLinkClass) &&
1039             (icc_profile == (StringInfo *) NULL))
1040           status=SetImageProfile(image,name,profile,exception);
1041         else
1042           {
1043             CacheView
1044               *image_view;
1045 
1046             cmsColorSpaceSignature
1047               signature;
1048 
1049             cmsHTRANSFORM
1050               *magick_restrict transform;
1051 
1052             cmsUInt32Number
1053               flags;
1054 
1055 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1056             const char
1057               *artifact;
1058 #endif
1059 
1060             MagickBooleanType
1061               highres;
1062 
1063             MagickOffsetType
1064               progress;
1065 
1066             ssize_t
1067               y;
1068 
1069             target_info.profile=(cmsHPROFILE) NULL;
1070             if (icc_profile != (StringInfo *) NULL)
1071               {
1072                 target_info.profile=source_info.profile;
1073                 source_info.profile=cmsOpenProfileFromMemTHR(cms_context,
1074                   GetStringInfoDatum(icc_profile),
1075                   (cmsUInt32Number) GetStringInfoLength(icc_profile));
1076                 if (source_info.profile == (cmsHPROFILE) NULL)
1077                   ThrowProfileException(ResourceLimitError,
1078                     "ColorspaceColorProfileMismatch",name);
1079               }
1080             highres=MagickTrue;
1081 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1082             artifact=GetImageArtifact(image,"profile:highres-transform");
1083             if (IsStringFalse(artifact) != MagickFalse)
1084               highres=MagickFalse;
1085 #endif
1086             source_info.scale=1.0;
1087             source_info.translate=0.0;
1088             source_info.colorspace=sRGBColorspace;
1089             source_info.channels=3;
1090             switch (cmsGetColorSpace(source_info.profile))
1091             {
1092               case cmsSigCmykData:
1093               {
1094                 source_info.colorspace=CMYKColorspace;
1095                 source_info.channels=4;
1096 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
1097                 if (highres == MagickFalse)
1098                   source_info.type=(cmsUInt32Number) TYPE_CMYK_8;
1099                 else
1100 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1101                 if (highres == MagickFalse)
1102                   source_info.type=(cmsUInt32Number) TYPE_CMYK_16;
1103                 else
1104 #endif
1105                   {
1106                     source_info.type=(cmsUInt32Number) TYPE_CMYK_DBL;
1107                     source_info.scale=100.0;
1108                   }
1109                 break;
1110               }
1111               case cmsSigGrayData:
1112               {
1113                 source_info.colorspace=GRAYColorspace;
1114                 source_info.channels=1;
1115 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
1116                 if (highres == MagickFalse)
1117                   source_info.type=(cmsUInt32Number) TYPE_GRAY_8;
1118                 else
1119 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1120                 if (highres == MagickFalse)
1121                   source_info.type=(cmsUInt32Number) TYPE_GRAY_16;
1122                 else
1123 #endif
1124                   source_info.type=(cmsUInt32Number) TYPE_GRAY_DBL;
1125                 break;
1126               }
1127               case cmsSigLabData:
1128               {
1129                 source_info.colorspace=LabColorspace;
1130 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
1131                 if (highres == MagickFalse)
1132                   source_info.type=(cmsUInt32Number) TYPE_Lab_8;
1133                 else
1134 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1135                 if (highres == MagickFalse)
1136                   source_info.type=(cmsUInt32Number) TYPE_Lab_16;
1137                 else
1138 #endif
1139                   {
1140                     source_info.type=(cmsUInt32Number) TYPE_Lab_DBL;
1141                     source_info.scale=100.0;
1142                     source_info.translate=(-0.5);
1143                   }
1144                 break;
1145               }
1146               case cmsSigRgbData:
1147               {
1148                 source_info.colorspace=sRGBColorspace;
1149 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
1150                 if (highres == MagickFalse)
1151                   source_info.type=(cmsUInt32Number) TYPE_RGB_8;
1152                 else
1153 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1154                 if (highres == MagickFalse)
1155                   source_info.type=(cmsUInt32Number) TYPE_RGB_16;
1156                 else
1157 #endif
1158                   source_info.type=(cmsUInt32Number) TYPE_RGB_DBL;
1159                 break;
1160               }
1161               case cmsSigXYZData:
1162               {
1163                 source_info.colorspace=XYZColorspace;
1164 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
1165                 if (highres == MagickFalse)
1166                   source_info.type=(cmsUInt32Number) TYPE_XYZ_8;
1167                 else
1168 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1169                 if (highres == MagickFalse)
1170                   source_info.type=(cmsUInt32Number) TYPE_XYZ_16;
1171                 else
1172 #endif
1173                   source_info.type=(cmsUInt32Number) TYPE_XYZ_DBL;
1174                 break;
1175               }
1176               default:
1177                 ThrowProfileException(ImageError,
1178                   "ColorspaceColorProfileMismatch",name);
1179             }
1180             signature=cmsGetPCS(source_info.profile);
1181             if (target_info.profile != (cmsHPROFILE) NULL)
1182               signature=cmsGetColorSpace(target_info.profile);
1183             target_info.scale=1.0;
1184             target_info.translate=0.0;
1185             target_info.channels=3;
1186             switch (signature)
1187             {
1188               case cmsSigCmykData:
1189               {
1190                 target_info.colorspace=CMYKColorspace;
1191                 target_info.channels=4;
1192 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
1193                 if (highres == MagickFalse)
1194                   target_info.type=(cmsUInt32Number) TYPE_CMYK_8;
1195                 else
1196 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1197                 if (highres == MagickFalse)
1198                   target_info.type=(cmsUInt32Number) TYPE_CMYK_16;
1199                 else
1200 #endif
1201                   {
1202                     target_info.type=(cmsUInt32Number) TYPE_CMYK_DBL;
1203                     target_info.scale=0.01;
1204                   }
1205                 break;
1206               }
1207               case cmsSigGrayData:
1208               {
1209                 target_info.colorspace=GRAYColorspace;
1210                 target_info.channels=1;
1211 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
1212                 if (highres == MagickFalse)
1213                   target_info.type=(cmsUInt32Number) TYPE_GRAY_8;
1214                 else
1215 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1216                 if (highres == MagickFalse)
1217                   target_info.type=(cmsUInt32Number) TYPE_GRAY_16;
1218                 else
1219 #endif
1220                   target_info.type=(cmsUInt32Number) TYPE_GRAY_DBL;
1221                 break;
1222               }
1223               case cmsSigLabData:
1224               {
1225                 target_info.colorspace=LabColorspace;
1226 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
1227                 if (highres == MagickFalse)
1228                   target_info.type=(cmsUInt32Number) TYPE_Lab_8;
1229                 else
1230 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1231                 if (highres == MagickFalse)
1232                   target_info.type=(cmsUInt32Number) TYPE_Lab_16;
1233                 else
1234 #endif
1235                   {
1236                     target_info.type=(cmsUInt32Number) TYPE_Lab_DBL;
1237                     target_info.scale=0.01;
1238                     target_info.translate=0.5;
1239                   }
1240                 break;
1241               }
1242               case cmsSigRgbData:
1243               {
1244                 target_info.colorspace=sRGBColorspace;
1245 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
1246                 if (highres == MagickFalse)
1247                   target_info.type=(cmsUInt32Number) TYPE_RGB_8;
1248                 else
1249 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1250                 if (highres == MagickFalse)
1251                   target_info.type=(cmsUInt32Number) TYPE_RGB_16;
1252                 else
1253 #endif
1254                   target_info.type=(cmsUInt32Number) TYPE_RGB_DBL;
1255                 break;
1256               }
1257               case cmsSigXYZData:
1258               {
1259                 target_info.colorspace=XYZColorspace;
1260 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
1261                 if (highres == MagickFalse)
1262                   target_info.type=(cmsUInt32Number) TYPE_XYZ_8;
1263                 else
1264 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1265                 if (highres == MagickFalse)
1266                   source_info.type=(cmsUInt32Number) TYPE_XYZ_16;
1267                 else
1268 #endif
1269                   target_info.type=(cmsUInt32Number) TYPE_XYZ_DBL;
1270                 break;
1271               }
1272               default:
1273                 ThrowProfileException(ImageError,
1274                   "ColorspaceColorProfileMismatch",name);
1275             }
1276             switch (image->rendering_intent)
1277             {
1278               case AbsoluteIntent:
1279               {
1280                 target_info.intent=INTENT_ABSOLUTE_COLORIMETRIC;
1281                 break;
1282               }
1283               case PerceptualIntent:
1284               {
1285                 target_info.intent=INTENT_PERCEPTUAL;
1286                 break;
1287               }
1288               case RelativeIntent:
1289               {
1290                 target_info.intent=INTENT_RELATIVE_COLORIMETRIC;
1291                 break;
1292               }
1293               case SaturationIntent:
1294               {
1295                 target_info.intent=INTENT_SATURATION;
1296                 break;
1297               }
1298               default:
1299               {
1300                 target_info.intent=INTENT_PERCEPTUAL;
1301                 break;
1302               }
1303             }
1304             flags=cmsFLAGS_HIGHRESPRECALC;
1305 #if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
1306             if (image->black_point_compensation != MagickFalse)
1307               flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
1308 #endif
1309             transform=AcquireTransformThreadSet(&source_info,&target_info,
1310               flags,cms_context);
1311             if (transform == (cmsHTRANSFORM *) NULL)
1312               ThrowProfileException(ImageError,"UnableToCreateColorTransform",
1313                 name);
1314             /*
1315               Transform image as dictated by the source & target image profiles.
1316             */
1317             source_info.pixels=AcquirePixelThreadSet(image->columns,
1318               source_info.channels,highres);
1319             target_info.pixels=AcquirePixelThreadSet(image->columns,
1320               target_info.channels,highres);
1321             if ((source_info.pixels == (void **) NULL) ||
1322                 (target_info.pixels == (void **) NULL))
1323               {
1324                 target_info.pixels=DestroyPixelThreadSet(target_info.pixels);
1325                 source_info.pixels=DestroyPixelThreadSet(source_info.pixels);
1326                 transform=DestroyTransformThreadSet(transform);
1327                 ThrowProfileException(ResourceLimitError,
1328                   "MemoryAllocationFailed",image->filename);
1329               }
1330             if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1331               {
1332                 target_info.pixels=DestroyPixelThreadSet(target_info.pixels);
1333                 source_info.pixels=DestroyPixelThreadSet(source_info.pixels);
1334                 transform=DestroyTransformThreadSet(transform);
1335                 if (source_info.profile != (cmsHPROFILE) NULL)
1336                   (void) cmsCloseProfile(source_info.profile);
1337                 if (target_info.profile != (cmsHPROFILE) NULL)
1338                   (void) cmsCloseProfile(target_info.profile);
1339                 return(MagickFalse);
1340               }
1341             if (target_info.colorspace == CMYKColorspace)
1342               (void) SetImageColorspace(image,target_info.colorspace,exception);
1343             progress=0;
1344             image_view=AcquireAuthenticCacheView(image,exception);
1345 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1346             #pragma omp parallel for schedule(static) shared(status) \
1347               magick_number_threads(image,image,image->rows,1)
1348 #endif
1349             for (y=0; y < (ssize_t) image->rows; y++)
1350             {
1351               const int
1352                 id = GetOpenMPThreadId();
1353 
1354               MagickBooleanType
1355                 sync;
1356 
1357               Quantum
1358                 *magick_restrict q;
1359 
1360               if (status == MagickFalse)
1361                 continue;
1362               q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1363                 exception);
1364               if (q == (Quantum *) NULL)
1365                 {
1366                   status=MagickFalse;
1367                   continue;
1368                 }
1369               if (highres != MagickFalse)
1370                 TransformDoublePixels(id,image,&source_info,&target_info,transform,q);
1371               else
1372                 TransformQuantumPixels(id,image,&source_info,&target_info,transform,q);
1373               sync=SyncCacheViewAuthenticPixels(image_view,exception);
1374               if (sync == MagickFalse)
1375                 status=MagickFalse;
1376               if (image->progress_monitor != (MagickProgressMonitor) NULL)
1377                 {
1378                   MagickBooleanType
1379                     proceed;
1380 
1381 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1382                   #pragma omp atomic
1383 #endif
1384                   progress++;
1385                   proceed=SetImageProgress(image,ProfileImageTag,progress,
1386                     image->rows);
1387                   if (proceed == MagickFalse)
1388                     status=MagickFalse;
1389                 }
1390             }
1391             image_view=DestroyCacheView(image_view);
1392             (void) SetImageColorspace(image,target_info.colorspace,exception);
1393             switch (signature)
1394             {
1395               case cmsSigRgbData:
1396               {
1397                 image->type=image->alpha_trait == UndefinedPixelTrait ?
1398                   TrueColorType : TrueColorAlphaType;
1399                 break;
1400               }
1401               case cmsSigCmykData:
1402               {
1403                 image->type=image->alpha_trait == UndefinedPixelTrait ?
1404                   ColorSeparationType : ColorSeparationAlphaType;
1405                 break;
1406               }
1407               case cmsSigGrayData:
1408               {
1409                 image->type=image->alpha_trait == UndefinedPixelTrait ?
1410                   GrayscaleType : GrayscaleAlphaType;
1411                 break;
1412               }
1413               default:
1414                 break;
1415             }
1416             target_info.pixels=DestroyPixelThreadSet(target_info.pixels);
1417             source_info.pixels=DestroyPixelThreadSet(source_info.pixels);
1418             transform=DestroyTransformThreadSet(transform);
1419             if ((status != MagickFalse) &&
1420                 (cmsGetDeviceClass(source_info.profile) != cmsSigLinkClass))
1421               status=SetImageProfile(image,name,profile,exception);
1422             if (target_info.profile != (cmsHPROFILE) NULL)
1423               (void) cmsCloseProfile(target_info.profile);
1424           }
1425         (void) cmsCloseProfile(source_info.profile);
1426         cmsDeleteContext(cms_context);
1427       }
1428 #endif
1429     }
1430   profile=DestroyStringInfo(profile);
1431   return(status);
1432 }
1433 
1434 /*
1435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1436 %                                                                             %
1437 %                                                                             %
1438 %                                                                             %
1439 %   R e m o v e I m a g e P r o f i l e                                       %
1440 %                                                                             %
1441 %                                                                             %
1442 %                                                                             %
1443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1444 %
1445 %  RemoveImageProfile() removes a named profile from the image and returns its
1446 %  value.
1447 %
1448 %  The format of the RemoveImageProfile method is:
1449 %
1450 %      void *RemoveImageProfile(Image *image,const char *name)
1451 %
1452 %  A description of each parameter follows:
1453 %
1454 %    o image: the image.
1455 %
1456 %    o name: the profile name.
1457 %
1458 */
RemoveImageProfile(Image * image,const char * name)1459 MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
1460 {
1461   StringInfo
1462     *profile;
1463 
1464   assert(image != (Image *) NULL);
1465   assert(image->signature == MagickCoreSignature);
1466   if (image->debug != MagickFalse)
1467     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1468   if (image->profiles == (SplayTreeInfo *) NULL)
1469     return((StringInfo *) NULL);
1470   WriteTo8BimProfile(image,name,(StringInfo *) NULL);
1471   profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
1472     image->profiles,name);
1473   return(profile);
1474 }
1475 
1476 /*
1477 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1478 %                                                                             %
1479 %                                                                             %
1480 %                                                                             %
1481 %   R e s e t P r o f i l e I t e r a t o r                                   %
1482 %                                                                             %
1483 %                                                                             %
1484 %                                                                             %
1485 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1486 %
1487 %  ResetImageProfileIterator() resets the image profile iterator.  Use it in
1488 %  conjunction with GetNextImageProfile() to iterate over all the profiles
1489 %  associated with an image.
1490 %
1491 %  The format of the ResetImageProfileIterator method is:
1492 %
1493 %      ResetImageProfileIterator(Image *image)
1494 %
1495 %  A description of each parameter follows:
1496 %
1497 %    o image: the image.
1498 %
1499 */
ResetImageProfileIterator(const Image * image)1500 MagickExport void ResetImageProfileIterator(const Image *image)
1501 {
1502   assert(image != (Image *) NULL);
1503   assert(image->signature == MagickCoreSignature);
1504   if (image->debug != MagickFalse)
1505     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1506   if (image->profiles == (SplayTreeInfo *) NULL)
1507     return;
1508   ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
1509 }
1510 
1511 /*
1512 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1513 %                                                                             %
1514 %                                                                             %
1515 %                                                                             %
1516 %   S e t I m a g e P r o f i l e                                             %
1517 %                                                                             %
1518 %                                                                             %
1519 %                                                                             %
1520 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1521 %
1522 %  SetImageProfile() adds a named profile to the image.  If a profile with the
1523 %  same name already exists, it is replaced.  This method differs from the
1524 %  ProfileImage() method in that it does not apply CMS color profiles.
1525 %
1526 %  The format of the SetImageProfile method is:
1527 %
1528 %      MagickBooleanType SetImageProfile(Image *image,const char *name,
1529 %        const StringInfo *profile)
1530 %
1531 %  A description of each parameter follows:
1532 %
1533 %    o image: the image.
1534 %
1535 %    o name: the profile name, for example icc, exif, and 8bim (8bim is the
1536 %      Photoshop wrapper for iptc profiles).
1537 %
1538 %    o profile: A StringInfo structure that contains the named profile.
1539 %
1540 */
1541 
DestroyProfile(void * profile)1542 static void *DestroyProfile(void *profile)
1543 {
1544   return((void *) DestroyStringInfo((StringInfo *) profile));
1545 }
1546 
ReadResourceByte(const unsigned char * p,unsigned char * quantum)1547 static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1548   unsigned char *quantum)
1549 {
1550   *quantum=(*p++);
1551   return(p);
1552 }
1553 
ReadResourceLong(const unsigned char * p,unsigned int * quantum)1554 static inline const unsigned char *ReadResourceLong(const unsigned char *p,
1555   unsigned int *quantum)
1556 {
1557   *quantum=(unsigned int) (*p++) << 24;
1558   *quantum|=(unsigned int) (*p++) << 16;
1559   *quantum|=(unsigned int) (*p++) << 8;
1560   *quantum|=(unsigned int) (*p++);
1561   return(p);
1562 }
1563 
ReadResourceShort(const unsigned char * p,unsigned short * quantum)1564 static inline const unsigned char *ReadResourceShort(const unsigned char *p,
1565   unsigned short *quantum)
1566 {
1567   *quantum=(unsigned short) (*p++) << 8;
1568   *quantum|=(unsigned short) (*p++);
1569   return(p);
1570 }
1571 
WriteResourceLong(unsigned char * p,const unsigned int quantum)1572 static inline void WriteResourceLong(unsigned char *p,
1573   const unsigned int quantum)
1574 {
1575   unsigned char
1576     buffer[4];
1577 
1578   buffer[0]=(unsigned char) (quantum >> 24);
1579   buffer[1]=(unsigned char) (quantum >> 16);
1580   buffer[2]=(unsigned char) (quantum >> 8);
1581   buffer[3]=(unsigned char) quantum;
1582   (void) memcpy(p,buffer,4);
1583 }
1584 
WriteTo8BimProfile(Image * image,const char * name,const StringInfo * profile)1585 static void WriteTo8BimProfile(Image *image,const char *name,
1586   const StringInfo *profile)
1587 {
1588   const unsigned char
1589     *datum,
1590     *q;
1591 
1592   const unsigned char
1593     *p;
1594 
1595   size_t
1596     length;
1597 
1598   StringInfo
1599     *profile_8bim;
1600 
1601   ssize_t
1602     count;
1603 
1604   unsigned char
1605     length_byte;
1606 
1607   unsigned int
1608     value;
1609 
1610   unsigned short
1611     id,
1612     profile_id;
1613 
1614   if (LocaleCompare(name,"icc") == 0)
1615     profile_id=0x040f;
1616   else
1617     if (LocaleCompare(name,"iptc") == 0)
1618       profile_id=0x0404;
1619     else
1620       if (LocaleCompare(name,"xmp") == 0)
1621         profile_id=0x0424;
1622       else
1623         return;
1624   profile_8bim=(StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
1625     image->profiles,"8bim");
1626   if (profile_8bim == (StringInfo *) NULL)
1627     return;
1628   datum=GetStringInfoDatum(profile_8bim);
1629   length=GetStringInfoLength(profile_8bim);
1630   for (p=datum; p < (datum+length-16); )
1631   {
1632     q=p;
1633     if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1634       break;
1635     p+=4;
1636     p=ReadResourceShort(p,&id);
1637     p=ReadResourceByte(p,&length_byte);
1638     p+=length_byte;
1639     if (((length_byte+1) & 0x01) != 0)
1640       p++;
1641     if (p > (datum+length-4))
1642       break;
1643     p=ReadResourceLong(p,&value);
1644     count=(ssize_t) value;
1645     if ((count & 0x01) != 0)
1646       count++;
1647     if ((count < 0) || (p > (datum+length-count)) || (count > (ssize_t) length))
1648       break;
1649     if (id != profile_id)
1650       p+=count;
1651     else
1652       {
1653         size_t
1654           extent,
1655           offset;
1656 
1657         ssize_t
1658           extract_extent;
1659 
1660         StringInfo
1661           *extract_profile;
1662 
1663         extract_extent=0;
1664         extent=(datum+length)-(p+count);
1665         if (profile == (StringInfo *) NULL)
1666           {
1667             offset=(q-datum);
1668             extract_profile=AcquireStringInfo(offset+extent);
1669             (void) memcpy(extract_profile->datum,datum,offset);
1670           }
1671         else
1672           {
1673             offset=(p-datum);
1674             extract_extent=profile->length;
1675             if ((extract_extent & 0x01) != 0)
1676               extract_extent++;
1677             extract_profile=AcquireStringInfo(offset+extract_extent+extent);
1678             (void) memcpy(extract_profile->datum,datum,offset-4);
1679             WriteResourceLong(extract_profile->datum+offset-4,(unsigned int)
1680               profile->length);
1681             (void) memcpy(extract_profile->datum+offset,
1682               profile->datum,profile->length);
1683           }
1684         (void) memcpy(extract_profile->datum+offset+extract_extent,
1685           p+count,extent);
1686         (void) AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1687           ConstantString("8bim"),CloneStringInfo(extract_profile));
1688         extract_profile=DestroyStringInfo(extract_profile);
1689         break;
1690       }
1691   }
1692 }
1693 
GetProfilesFromResourceBlock(Image * image,const StringInfo * resource_block,ExceptionInfo * exception)1694 static void GetProfilesFromResourceBlock(Image *image,
1695   const StringInfo *resource_block,ExceptionInfo *exception)
1696 {
1697   const unsigned char
1698     *datum;
1699 
1700   const unsigned char
1701     *p;
1702 
1703   size_t
1704     length;
1705 
1706   ssize_t
1707     count;
1708 
1709   StringInfo
1710     *profile;
1711 
1712   unsigned char
1713     length_byte;
1714 
1715   unsigned int
1716     value;
1717 
1718   unsigned short
1719     id;
1720 
1721   datum=GetStringInfoDatum(resource_block);
1722   length=GetStringInfoLength(resource_block);
1723   for (p=datum; p < (datum+length-16); )
1724   {
1725     if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1726       break;
1727     p+=4;
1728     p=ReadResourceShort(p,&id);
1729     p=ReadResourceByte(p,&length_byte);
1730     p+=length_byte;
1731     if (((length_byte+1) & 0x01) != 0)
1732       p++;
1733     if (p > (datum+length-4))
1734       break;
1735     p=ReadResourceLong(p,&value);
1736     count=(ssize_t) value;
1737     if ((p > (datum+length-count)) || (count > (ssize_t) length) || (count < 0))
1738       break;
1739     switch (id)
1740     {
1741       case 0x03ed:
1742       {
1743         unsigned int
1744           resolution;
1745 
1746         unsigned short
1747           units;
1748 
1749         /*
1750           Resolution.
1751         */
1752         if (count < 10)
1753           break;
1754         p=ReadResourceLong(p,&resolution);
1755         image->resolution.x=((double) resolution)/65536.0;
1756         p=ReadResourceShort(p,&units)+2;
1757         p=ReadResourceLong(p,&resolution)+4;
1758         image->resolution.y=((double) resolution)/65536.0;
1759         /*
1760           Values are always stored as pixels per inch.
1761         */
1762         if ((ResolutionType) units != PixelsPerCentimeterResolution)
1763           image->units=PixelsPerInchResolution;
1764         else
1765           {
1766             image->units=PixelsPerCentimeterResolution;
1767             image->resolution.x/=2.54;
1768             image->resolution.y/=2.54;
1769           }
1770         break;
1771       }
1772       case 0x0404:
1773       {
1774         /*
1775           IPTC Profile
1776         */
1777         profile=AcquireStringInfo(count);
1778         SetStringInfoDatum(profile,p);
1779         (void) SetImageProfileInternal(image,"iptc",profile,MagickTrue,
1780           exception);
1781         profile=DestroyStringInfo(profile);
1782         p+=count;
1783         break;
1784       }
1785       case 0x040c:
1786       {
1787         /*
1788           Thumbnail.
1789         */
1790         p+=count;
1791         break;
1792       }
1793       case 0x040f:
1794       {
1795         /*
1796           ICC Profile.
1797         */
1798         profile=AcquireStringInfo(count);
1799         SetStringInfoDatum(profile,p);
1800         (void) SetImageProfileInternal(image,"icc",profile,MagickTrue,
1801           exception);
1802         profile=DestroyStringInfo(profile);
1803         p+=count;
1804         break;
1805       }
1806       case 0x0422:
1807       {
1808         /*
1809           EXIF Profile.
1810         */
1811         profile=AcquireStringInfo(count);
1812         SetStringInfoDatum(profile,p);
1813         (void) SetImageProfileInternal(image,"exif",profile,MagickTrue,
1814           exception);
1815         profile=DestroyStringInfo(profile);
1816         p+=count;
1817         break;
1818       }
1819       case 0x0424:
1820       {
1821         /*
1822           XMP Profile.
1823         */
1824         profile=AcquireStringInfo(count);
1825         SetStringInfoDatum(profile,p);
1826         (void) SetImageProfileInternal(image,"xmp",profile,MagickTrue,
1827           exception);
1828         profile=DestroyStringInfo(profile);
1829         p+=count;
1830         break;
1831       }
1832       default:
1833       {
1834         p+=count;
1835         break;
1836       }
1837     }
1838     if ((count & 0x01) != 0)
1839       p++;
1840   }
1841 }
1842 
PatchCorruptProfile(const char * name,StringInfo * profile)1843 static void PatchCorruptProfile(const char *name,StringInfo *profile)
1844 {
1845   unsigned char
1846     *p;
1847 
1848   size_t
1849     length;
1850 
1851   /*
1852     Detect corrupt profiles and if discovered, repair.
1853   */
1854   if (LocaleCompare(name,"xmp") == 0)
1855     {
1856       /*
1857         Remove garbage after xpacket end.
1858       */
1859       p=GetStringInfoDatum(profile);
1860       p=(unsigned char *) strstr((const char *) p,"<?xpacket end=\"w\"?>");
1861       if (p != (unsigned char *) NULL)
1862         {
1863           p+=19;
1864           length=p-GetStringInfoDatum(profile);
1865           if (length != GetStringInfoLength(profile))
1866             {
1867               *p='\0';
1868               SetStringInfoLength(profile,length);
1869             }
1870         }
1871       return;
1872     }
1873   if (LocaleCompare(name,"exif") == 0)
1874     {
1875       /*
1876         Check if profile starts with byte order marker instead of Exif.
1877       */
1878       p=GetStringInfoDatum(profile);
1879       if ((LocaleNCompare((const char *) p,"MM",2) == 0) ||
1880           (LocaleNCompare((const char *) p,"II",2) == 0))
1881         {
1882           const unsigned char
1883             profile_start[] = "Exif\0\0";
1884 
1885           StringInfo
1886             *exif_profile;
1887 
1888           exif_profile=AcquireStringInfo(6);
1889           if (exif_profile != (StringInfo *) NULL)
1890             {
1891               SetStringInfoDatum(exif_profile,profile_start);
1892               ConcatenateStringInfo(exif_profile,profile);
1893               SetStringInfoLength(profile,GetStringInfoLength(exif_profile));
1894               SetStringInfo(profile,exif_profile);
1895               exif_profile=DestroyStringInfo(exif_profile);
1896             }
1897         }
1898     }
1899 }
1900 
1901 #if defined(MAGICKCORE_XML_DELEGATE)
ValidateXMPProfile(Image * image,const StringInfo * profile,ExceptionInfo * exception)1902 static MagickBooleanType ValidateXMPProfile(Image *image,
1903   const StringInfo *profile,ExceptionInfo *exception)
1904 {
1905   xmlDocPtr
1906     document;
1907 
1908   /*
1909     Parse XML profile.
1910   */
1911   document=xmlReadMemory((const char *) GetStringInfoDatum(profile),(int)
1912     GetStringInfoLength(profile),"xmp.xml",NULL,XML_PARSE_NOERROR |
1913     XML_PARSE_NOWARNING);
1914   if (document == (xmlDocPtr) NULL)
1915     {
1916       (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1917         "CorruptImageProfile","`%s' (XMP)",image->filename);
1918       return(MagickFalse);
1919     }
1920   xmlFreeDoc(document);
1921   return(MagickTrue);
1922 }
1923 #else
ValidateXMPProfile(Image * image,const StringInfo * profile,ExceptionInfo * exception)1924 static MagickBooleanType ValidateXMPProfile(Image *image,
1925   const StringInfo *profile,ExceptionInfo *exception)
1926 {
1927   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateWarning,
1928     "DelegateLibrarySupportNotBuiltIn","'%s' (XML)",image->filename);
1929   return(MagickFalse);
1930 }
1931 #endif
1932 
SetImageProfileInternal(Image * image,const char * name,const StringInfo * profile,const MagickBooleanType recursive,ExceptionInfo * exception)1933 static MagickBooleanType SetImageProfileInternal(Image *image,const char *name,
1934   const StringInfo *profile,const MagickBooleanType recursive,
1935   ExceptionInfo *exception)
1936 {
1937   char
1938     key[MagickPathExtent];
1939 
1940   MagickBooleanType
1941     status;
1942 
1943   StringInfo
1944     *clone_profile;
1945 
1946   assert(image != (Image *) NULL);
1947   assert(image->signature == MagickCoreSignature);
1948   if (image->debug != MagickFalse)
1949     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1950   clone_profile=CloneStringInfo(profile);
1951   PatchCorruptProfile(name,clone_profile);
1952   if ((LocaleCompare(name,"xmp") == 0) &&
1953       (ValidateXMPProfile(image,clone_profile,exception) == MagickFalse))
1954     {
1955       clone_profile=DestroyStringInfo(clone_profile);
1956       return(MagickTrue);
1957     }
1958   if (image->profiles == (SplayTreeInfo *) NULL)
1959     image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
1960       DestroyProfile);
1961   (void) CopyMagickString(key,name,MagickPathExtent);
1962   LocaleLower(key);
1963   status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1964     ConstantString(key),clone_profile);
1965   if (status != MagickFalse)
1966     {
1967       if (LocaleCompare(name,"8bim") == 0)
1968         GetProfilesFromResourceBlock(image,clone_profile,exception);
1969       else
1970         if (recursive == MagickFalse)
1971           WriteTo8BimProfile(image,name,clone_profile);
1972     }
1973   return(status);
1974 }
1975 
SetImageProfile(Image * image,const char * name,const StringInfo * profile,ExceptionInfo * exception)1976 MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
1977   const StringInfo *profile,ExceptionInfo *exception)
1978 {
1979   return(SetImageProfileInternal(image,name,profile,MagickFalse,exception));
1980 }
1981 
1982 /*
1983 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1984 %                                                                             %
1985 %                                                                             %
1986 %                                                                             %
1987 %   S y n c I m a g e P r o f i l e s                                         %
1988 %                                                                             %
1989 %                                                                             %
1990 %                                                                             %
1991 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1992 %
1993 %  SyncImageProfiles() synchronizes image properties with the image profiles.
1994 %  Currently we only support updating the EXIF resolution and orientation.
1995 %
1996 %  The format of the SyncImageProfiles method is:
1997 %
1998 %      MagickBooleanType SyncImageProfiles(Image *image)
1999 %
2000 %  A description of each parameter follows:
2001 %
2002 %    o image: the image.
2003 %
2004 */
2005 
ReadProfileByte(unsigned char ** p,size_t * length)2006 static inline int ReadProfileByte(unsigned char **p,size_t *length)
2007 {
2008   int
2009     c;
2010 
2011   if (*length < 1)
2012     return(EOF);
2013   c=(int) (*(*p)++);
2014   (*length)--;
2015   return(c);
2016 }
2017 
ReadProfileShort(const EndianType endian,unsigned char * buffer)2018 static inline signed short ReadProfileShort(const EndianType endian,
2019   unsigned char *buffer)
2020 {
2021   union
2022   {
2023     unsigned int
2024       unsigned_value;
2025 
2026     signed int
2027       signed_value;
2028   } quantum;
2029 
2030   unsigned short
2031     value;
2032 
2033   if (endian == LSBEndian)
2034     {
2035       value=(unsigned short) buffer[1] << 8;
2036       value|=(unsigned short) buffer[0];
2037       quantum.unsigned_value=value & 0xffff;
2038       return(quantum.signed_value);
2039     }
2040   value=(unsigned short) buffer[0] << 8;
2041   value|=(unsigned short) buffer[1];
2042   quantum.unsigned_value=value & 0xffff;
2043   return(quantum.signed_value);
2044 }
2045 
ReadProfileLong(const EndianType endian,unsigned char * buffer)2046 static inline signed int ReadProfileLong(const EndianType endian,
2047   unsigned char *buffer)
2048 {
2049   union
2050   {
2051     unsigned int
2052       unsigned_value;
2053 
2054     signed int
2055       signed_value;
2056   } quantum;
2057 
2058   unsigned int
2059     value;
2060 
2061   if (endian == LSBEndian)
2062     {
2063       value=(unsigned int) buffer[3] << 24;
2064       value|=(unsigned int) buffer[2] << 16;
2065       value|=(unsigned int) buffer[1] << 8;
2066       value|=(unsigned int) buffer[0];
2067       quantum.unsigned_value=value & 0xffffffff;
2068       return(quantum.signed_value);
2069     }
2070   value=(unsigned int) buffer[0] << 24;
2071   value|=(unsigned int) buffer[1] << 16;
2072   value|=(unsigned int) buffer[2] << 8;
2073   value|=(unsigned int) buffer[3];
2074   quantum.unsigned_value=value & 0xffffffff;
2075   return(quantum.signed_value);
2076 }
2077 
ReadProfileMSBLong(unsigned char ** p,size_t * length)2078 static inline signed int ReadProfileMSBLong(unsigned char **p,size_t *length)
2079 {
2080   signed int
2081     value;
2082 
2083   if (*length < 4)
2084     return(0);
2085   value=ReadProfileLong(MSBEndian,*p);
2086   (*length)-=4;
2087   *p+=4;
2088   return(value);
2089 }
2090 
ReadProfileMSBShort(unsigned char ** p,size_t * length)2091 static inline signed short ReadProfileMSBShort(unsigned char **p,
2092   size_t *length)
2093 {
2094   signed short
2095     value;
2096 
2097   if (*length < 2)
2098     return(0);
2099   value=ReadProfileShort(MSBEndian,*p);
2100   (*length)-=2;
2101   *p+=2;
2102   return(value);
2103 }
2104 
WriteProfileLong(const EndianType endian,const size_t value,unsigned char * p)2105 static inline void WriteProfileLong(const EndianType endian,
2106   const size_t value,unsigned char *p)
2107 {
2108   unsigned char
2109     buffer[4];
2110 
2111   if (endian == LSBEndian)
2112     {
2113       buffer[0]=(unsigned char) value;
2114       buffer[1]=(unsigned char) (value >> 8);
2115       buffer[2]=(unsigned char) (value >> 16);
2116       buffer[3]=(unsigned char) (value >> 24);
2117       (void) memcpy(p,buffer,4);
2118       return;
2119     }
2120   buffer[0]=(unsigned char) (value >> 24);
2121   buffer[1]=(unsigned char) (value >> 16);
2122   buffer[2]=(unsigned char) (value >> 8);
2123   buffer[3]=(unsigned char) value;
2124   (void) memcpy(p,buffer,4);
2125 }
2126 
WriteProfileShort(const EndianType endian,const unsigned short value,unsigned char * p)2127 static void WriteProfileShort(const EndianType endian,
2128   const unsigned short value,unsigned char *p)
2129 {
2130   unsigned char
2131     buffer[2];
2132 
2133   if (endian == LSBEndian)
2134     {
2135       buffer[0]=(unsigned char) value;
2136       buffer[1]=(unsigned char) (value >> 8);
2137       (void) memcpy(p,buffer,2);
2138       return;
2139     }
2140   buffer[0]=(unsigned char) (value >> 8);
2141   buffer[1]=(unsigned char) value;
2142   (void) memcpy(p,buffer,2);
2143 }
2144 
Sync8BimProfile(Image * image,StringInfo * profile)2145 static MagickBooleanType Sync8BimProfile(Image *image,StringInfo *profile)
2146 {
2147   size_t
2148     length;
2149 
2150   ssize_t
2151     count;
2152 
2153   unsigned char
2154     *p;
2155 
2156   unsigned short
2157     id;
2158 
2159   length=GetStringInfoLength(profile);
2160   p=GetStringInfoDatum(profile);
2161   while (length != 0)
2162   {
2163     if (ReadProfileByte(&p,&length) != 0x38)
2164       continue;
2165     if (ReadProfileByte(&p,&length) != 0x42)
2166       continue;
2167     if (ReadProfileByte(&p,&length) != 0x49)
2168       continue;
2169     if (ReadProfileByte(&p,&length) != 0x4D)
2170       continue;
2171     if (length < 7)
2172       return(MagickFalse);
2173     id=ReadProfileMSBShort(&p,&length);
2174     count=(ssize_t) ReadProfileByte(&p,&length);
2175     if ((count >= (ssize_t) length) || (count < 0))
2176       return(MagickFalse);
2177     p+=count;
2178     length-=count;
2179     if ((*p & 0x01) == 0)
2180       (void) ReadProfileByte(&p,&length);
2181     count=(ssize_t) ReadProfileMSBLong(&p,&length);
2182     if ((count > (ssize_t) length) || (count < 0))
2183       return(MagickFalse);
2184     if ((id == 0x3ED) && (count == 16))
2185       {
2186         if (image->units == PixelsPerCentimeterResolution)
2187           WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2188             image->resolution.x*2.54*65536.0),p);
2189         else
2190           WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2191             image->resolution.x*65536.0),p);
2192         WriteProfileShort(MSBEndian,(unsigned short) image->units,p+4);
2193         if (image->units == PixelsPerCentimeterResolution)
2194           WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2195             image->resolution.y*2.54*65536.0),p+8);
2196         else
2197           WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2198             image->resolution.y*65536.0),p+8);
2199         WriteProfileShort(MSBEndian,(unsigned short) image->units,p+12);
2200       }
2201     p+=count;
2202     length-=count;
2203   }
2204   return(MagickTrue);
2205 }
2206 
SyncExifProfile(Image * image,StringInfo * profile)2207 MagickBooleanType SyncExifProfile(Image *image,StringInfo *profile)
2208 {
2209 #define MaxDirectoryStack  16
2210 #define EXIF_DELIMITER  "\n"
2211 #define EXIF_NUM_FORMATS  12
2212 #define TAG_EXIF_OFFSET  0x8769
2213 #define TAG_INTEROP_OFFSET  0xa005
2214 
2215   typedef struct _DirectoryInfo
2216   {
2217     unsigned char
2218       *directory;
2219 
2220     size_t
2221       entry;
2222   } DirectoryInfo;
2223 
2224   DirectoryInfo
2225     directory_stack[MaxDirectoryStack];
2226 
2227   EndianType
2228     endian;
2229 
2230   size_t
2231     entry,
2232     length,
2233     number_entries;
2234 
2235   SplayTreeInfo
2236     *exif_resources;
2237 
2238   ssize_t
2239     id,
2240     level,
2241     offset;
2242 
2243   static int
2244     format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
2245 
2246   unsigned char
2247     *directory,
2248     *exif;
2249 
2250   /*
2251     Set EXIF resolution tag.
2252   */
2253   length=GetStringInfoLength(profile);
2254   exif=GetStringInfoDatum(profile);
2255   if (length < 16)
2256     return(MagickFalse);
2257   id=(ssize_t) ReadProfileShort(LSBEndian,exif);
2258   if ((id != 0x4949) && (id != 0x4D4D))
2259     {
2260       while (length != 0)
2261       {
2262         if (ReadProfileByte(&exif,&length) != 0x45)
2263           continue;
2264         if (ReadProfileByte(&exif,&length) != 0x78)
2265           continue;
2266         if (ReadProfileByte(&exif,&length) != 0x69)
2267           continue;
2268         if (ReadProfileByte(&exif,&length) != 0x66)
2269           continue;
2270         if (ReadProfileByte(&exif,&length) != 0x00)
2271           continue;
2272         if (ReadProfileByte(&exif,&length) != 0x00)
2273           continue;
2274         break;
2275       }
2276       if (length < 16)
2277         return(MagickFalse);
2278       id=(ssize_t) ReadProfileShort(LSBEndian,exif);
2279     }
2280   endian=LSBEndian;
2281   if (id == 0x4949)
2282     endian=LSBEndian;
2283   else
2284     if (id == 0x4D4D)
2285       endian=MSBEndian;
2286     else
2287       return(MagickFalse);
2288   if (ReadProfileShort(endian,exif+2) != 0x002a)
2289     return(MagickFalse);
2290   /*
2291     This the offset to the first IFD.
2292   */
2293   offset=(ssize_t) ReadProfileLong(endian,exif+4);
2294   if ((offset < 0) || ((size_t) offset >= length))
2295     return(MagickFalse);
2296   directory=exif+offset;
2297   level=0;
2298   entry=0;
2299   exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
2300     (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
2301   do
2302   {
2303     if (level > 0)
2304       {
2305         level--;
2306         directory=directory_stack[level].directory;
2307         entry=directory_stack[level].entry;
2308       }
2309     if ((directory < exif) || (directory > (exif+length-2)))
2310       break;
2311     /*
2312       Determine how many entries there are in the current IFD.
2313     */
2314     number_entries=ReadProfileShort(endian,directory);
2315     for ( ; entry < number_entries; entry++)
2316     {
2317       int
2318         components;
2319 
2320       unsigned char
2321         *p,
2322         *q;
2323 
2324       size_t
2325         number_bytes;
2326 
2327       ssize_t
2328         format,
2329         tag_value;
2330 
2331       q=(unsigned char *) (directory+2+(12*entry));
2332       if (q > (exif+length-12))
2333         break;  /* corrupt EXIF */
2334       if (GetValueFromSplayTree(exif_resources,q) == q)
2335         break;
2336       (void) AddValueToSplayTree(exif_resources,q,q);
2337       tag_value=(ssize_t) ReadProfileShort(endian,q);
2338       format=(ssize_t) ReadProfileShort(endian,q+2);
2339       if ((format < 0) || ((format-1) >= EXIF_NUM_FORMATS))
2340         break;
2341       components=(int) ReadProfileLong(endian,q+4);
2342       if (components < 0)
2343         break;  /* corrupt EXIF */
2344       number_bytes=(size_t) components*format_bytes[format];
2345       if ((ssize_t) number_bytes < components)
2346         break;  /* prevent overflow */
2347       if (number_bytes <= 4)
2348         p=q+8;
2349       else
2350         {
2351           /*
2352             The directory entry contains an offset.
2353           */
2354           offset=(ssize_t) ReadProfileLong(endian,q+8);
2355           if ((offset < 0) || ((size_t) (offset+number_bytes) > length))
2356             continue;
2357           if (~length < number_bytes)
2358             continue;  /* prevent overflow */
2359           p=(unsigned char *) (exif+offset);
2360         }
2361       switch (tag_value)
2362       {
2363         case 0x011a:
2364         {
2365           (void) WriteProfileLong(endian,(size_t) (image->resolution.x+0.5),p);
2366           if (number_bytes == 8)
2367             (void) WriteProfileLong(endian,1UL,p+4);
2368           break;
2369         }
2370         case 0x011b:
2371         {
2372           (void) WriteProfileLong(endian,(size_t) (image->resolution.y+0.5),p);
2373           if (number_bytes == 8)
2374             (void) WriteProfileLong(endian,1UL,p+4);
2375           break;
2376         }
2377         case 0x0112:
2378         {
2379           if (number_bytes == 4)
2380             {
2381               (void) WriteProfileLong(endian,(size_t) image->orientation,p);
2382               break;
2383             }
2384           (void) WriteProfileShort(endian,(unsigned short) image->orientation,
2385             p);
2386           break;
2387         }
2388         case 0x0128:
2389         {
2390           if (number_bytes == 4)
2391             {
2392               (void) WriteProfileLong(endian,(size_t) (image->units+1),p);
2393               break;
2394             }
2395           (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
2396           break;
2397         }
2398         default:
2399           break;
2400       }
2401       if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
2402         {
2403           offset=(ssize_t) ReadProfileLong(endian,p);
2404           if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
2405             {
2406               directory_stack[level].directory=directory;
2407               entry++;
2408               directory_stack[level].entry=entry;
2409               level++;
2410               directory_stack[level].directory=exif+offset;
2411               directory_stack[level].entry=0;
2412               level++;
2413               if ((directory+2+(12*number_entries)) > (exif+length))
2414                 break;
2415               offset=(ssize_t) ReadProfileLong(endian,directory+2+(12*
2416                 number_entries));
2417               if ((offset != 0) && ((size_t) offset < length) &&
2418                   (level < (MaxDirectoryStack-2)))
2419                 {
2420                   directory_stack[level].directory=exif+offset;
2421                   directory_stack[level].entry=0;
2422                   level++;
2423                 }
2424             }
2425           break;
2426         }
2427     }
2428   } while (level > 0);
2429   exif_resources=DestroySplayTree(exif_resources);
2430   return(MagickTrue);
2431 }
2432 
SyncImageProfiles(Image * image)2433 MagickPrivate MagickBooleanType SyncImageProfiles(Image *image)
2434 {
2435   MagickBooleanType
2436     status;
2437 
2438   StringInfo
2439     *profile;
2440 
2441   status=MagickTrue;
2442   profile=(StringInfo *) GetImageProfile(image,"8BIM");
2443   if (profile != (StringInfo *) NULL)
2444     if (Sync8BimProfile(image,profile) == MagickFalse)
2445       status=MagickFalse;
2446   profile=(StringInfo *) GetImageProfile(image,"EXIF");
2447   if (profile != (StringInfo *) NULL)
2448     if (SyncExifProfile(image,profile) == MagickFalse)
2449       status=MagickFalse;
2450   return(status);
2451 }
2452 
UpdateClipPath(unsigned char * blob,size_t length,const size_t old_columns,const size_t old_rows,const RectangleInfo * new_geometry)2453 static void UpdateClipPath(unsigned char *blob,size_t length,
2454   const size_t old_columns,const size_t old_rows,
2455   const RectangleInfo *new_geometry)
2456 {
2457   ssize_t
2458     i;
2459 
2460   ssize_t
2461     knot_count,
2462     selector;
2463 
2464   knot_count=0;
2465   while (length != 0)
2466   {
2467     selector=(ssize_t) ReadProfileMSBShort(&blob,&length);
2468     switch (selector)
2469     {
2470       case 0:
2471       case 3:
2472       {
2473         if (knot_count != 0)
2474           {
2475             blob+=24;
2476             length-=MagickMin(24,(ssize_t) length);
2477             break;
2478           }
2479         /*
2480           Expected subpath length record.
2481         */
2482         knot_count=(ssize_t) ReadProfileMSBShort(&blob,&length);
2483         blob+=22;
2484         length-=MagickMin(22,(ssize_t) length);
2485         break;
2486       }
2487       case 1:
2488       case 2:
2489       case 4:
2490       case 5:
2491       {
2492         if (knot_count == 0)
2493           {
2494             /*
2495               Unexpected subpath knot.
2496             */
2497             blob+=24;
2498             length-=MagickMin(24,(ssize_t) length);
2499             break;
2500           }
2501         /*
2502           Add sub-path knot
2503         */
2504         for (i=0; i < 3; i++)
2505         {
2506           double
2507             x,
2508             y;
2509 
2510           signed int
2511             xx,
2512             yy;
2513 
2514           y=(double) ReadProfileMSBLong(&blob,&length);
2515           y=y*old_rows/4096.0/4096.0;
2516           y-=new_geometry->y;
2517           yy=(signed int) ((y*4096*4096)/new_geometry->height);
2518           WriteProfileLong(MSBEndian,(size_t) yy,blob-4);
2519           x=(double) ReadProfileMSBLong(&blob,&length);
2520           x=x*old_columns/4096.0/4096.0;
2521           x-=new_geometry->x;
2522           xx=(signed int) ((x*4096*4096)/new_geometry->width);
2523           WriteProfileLong(MSBEndian,(size_t) xx,blob-4);
2524         }
2525         knot_count--;
2526         break;
2527       }
2528       case 6:
2529       case 7:
2530       case 8:
2531       default:
2532       {
2533         blob+=24;
2534         length-=MagickMin(24,(ssize_t) length);
2535         break;
2536       }
2537     }
2538   }
2539 }
2540 
Update8BIMClipPath(const Image * image,const size_t old_columns,const size_t old_rows,const RectangleInfo * new_geometry)2541 MagickPrivate void Update8BIMClipPath(const Image *image,
2542   const size_t old_columns,const size_t old_rows,
2543   const RectangleInfo *new_geometry)
2544 {
2545   const StringInfo
2546     *profile;
2547 
2548   size_t
2549     length;
2550 
2551   ssize_t
2552     count,
2553     id;
2554 
2555   unsigned char
2556     *info;
2557 
2558   assert(image != (Image *) NULL);
2559   assert(new_geometry != (RectangleInfo *) NULL);
2560   profile=GetImageProfile(image,"8bim");
2561   if (profile == (StringInfo *) NULL)
2562     return;
2563   length=GetStringInfoLength(profile);
2564   info=GetStringInfoDatum(profile);
2565   while (length > 0)
2566   {
2567     if (ReadProfileByte(&info,&length) != (unsigned char) '8')
2568       continue;
2569     if (ReadProfileByte(&info,&length) != (unsigned char) 'B')
2570       continue;
2571     if (ReadProfileByte(&info,&length) != (unsigned char) 'I')
2572       continue;
2573     if (ReadProfileByte(&info,&length) != (unsigned char) 'M')
2574       continue;
2575     id=(ssize_t) ReadProfileMSBShort(&info,&length);
2576     count=(ssize_t) ReadProfileByte(&info,&length);
2577     if ((count != 0) && ((size_t) count <= length))
2578       {
2579         info+=count;
2580         length-=count;
2581       }
2582     if ((count & 0x01) == 0)
2583       (void) ReadProfileByte(&info,&length);
2584     count=(ssize_t) ReadProfileMSBLong(&info,&length);
2585     if ((count < 0) || ((size_t) count > length))
2586       {
2587         length=0;
2588         continue;
2589       }
2590     if ((id > 1999) && (id < 2999))
2591       UpdateClipPath(info,(size_t) count,old_columns,old_rows,new_geometry);
2592     info+=count;
2593     length-=MagickMin(count,(ssize_t) length);
2594   }
2595 }
2596