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