MagickCore  6.9.13-46
Convert, Edit, Or Compose Bitmap Images
profile.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % PPPP RRRR OOO FFFFF IIIII L EEEEE %
7 % P P R R O O F I L E %
8 % PPPP RRRR O O FFF I L EEE %
9 % P R R O O F I L E %
10 % P R R OOO F IIIII LLLLL EEEEE %
11 % %
12 % %
13 % MagickCore Image Profile Methods %
14 % %
15 % Software Design %
16 % Cristy %
17 % July 1992 %
18 % %
19 % %
20 % Copyright 1999 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/license/ %
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 "magick/studio.h"
43 #include "magick/artifact.h"
44 #include "magick/attribute.h"
45 #include "magick/cache.h"
46 #include "magick/color.h"
47 #include "magick/colorspace-private.h"
48 #include "magick/configure.h"
49 #include "magick/exception.h"
50 #include "magick/exception-private.h"
51 #include "magick/hashmap.h"
52 #include "magick/image.h"
53 #include "magick/memory_.h"
54 #include "magick/monitor.h"
55 #include "magick/monitor-private.h"
56 #include "magick/option.h"
57 #include "magick/option-private.h"
58 #include "magick/profile.h"
59 #include "magick/property.h"
60 #include "magick/quantum.h"
61 #include "magick/quantum-private.h"
62 #include "magick/resource_.h"
63 #include "magick/splay-tree.h"
64 #include "magick/string_.h"
65 #include "magick/string-private.h"
66 #include "magick/thread-private.h"
67 #include "magick/token.h"
68 #include "magick/utility.h"
69 #if defined(MAGICKCORE_LCMS_DELEGATE)
70 #if defined(MAGICKCORE_HAVE_LCMS_LCMS2_H)
71 #include <wchar.h>
72 #include <lcms/lcms2.h>
73 #else
74 #include <wchar.h>
75 #include "lcms2.h"
76 #endif
77 #endif
78 #if defined(MAGICKCORE_XML_DELEGATE)
79 # include <libxml/parser.h>
80 # include <libxml/tree.h>
81 #endif
82 
83 /*
84  Forward declarations
85 */
86 static MagickBooleanType
87  SetImageProfileInternal(Image *,const char *,const StringInfo *,
88  const MagickBooleanType);
89 
90 static void
91  WriteTo8BimProfile(Image *,const char*,const StringInfo *);
92 
93 /*
94 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
95 % %
96 % %
97 % %
98 % C l o n e I m a g e P r o f i l e s %
99 % %
100 % %
101 % %
102 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
103 %
104 % CloneImageProfiles() clones one or more image profiles.
105 %
106 % The format of the CloneImageProfiles method is:
107 %
108 % MagickBooleanType CloneImageProfiles(Image *image,
109 % const Image *clone_image)
110 %
111 % A description of each parameter follows:
112 %
113 % o image: the image.
114 %
115 % o clone_image: the clone image.
116 %
117 */
118 
119 typedef char
120  *(*CloneKeyFunc)(const char *);
121 
122 typedef StringInfo
123  *(*CloneValueFunc)(const StringInfo *);
124 
125 static inline void *CloneProfileKey(void *key)
126 {
127  return((void *) ((CloneKeyFunc) ConstantString)((const char *) key));
128 }
129 
130 static inline void *CloneProfileValue(void *value)
131 {
132  return((void *) ((CloneValueFunc) CloneStringInfo)((const StringInfo *) value));
133 }
134 
135 MagickExport MagickBooleanType CloneImageProfiles(Image *image,
136  const Image *clone_image)
137 {
138  assert(image != (Image *) NULL);
139  assert(image->signature == MagickCoreSignature);
140  assert(clone_image != (const Image *) NULL);
141  assert(clone_image->signature == MagickCoreSignature);
142  if (IsEventLogging() != MagickFalse)
143  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
144  image->color_profile.length=clone_image->color_profile.length;
145  image->color_profile.info=clone_image->color_profile.info;
146  image->iptc_profile.length=clone_image->iptc_profile.length;
147  image->iptc_profile.info=clone_image->iptc_profile.info;
148  if (clone_image->profiles != (void *) NULL)
149  {
150  if (image->profiles != (void *) NULL)
151  DestroyImageProfiles(image);
152  image->profiles=CloneSplayTree((SplayTreeInfo *) clone_image->profiles,
153  CloneProfileKey,CloneProfileValue);
154  }
155  return(MagickTrue);
156 }
157 
158 /*
159 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
160 % %
161 % %
162 % %
163 % D e l e t e I m a g e P r o f i l e %
164 % %
165 % %
166 % %
167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
168 %
169 % DeleteImageProfile() deletes a profile from the image by its name.
170 %
171 % The format of the DeleteImageProfile method is:
172 %
173 % MagickBooleanType DeleteImageProfile(Image *image,const char *name)
174 %
175 % A description of each parameter follows:
176 %
177 % o image: the image.
178 %
179 % o name: the profile name.
180 %
181 */
182 MagickExport MagickBooleanType DeleteImageProfile(Image *image,const char *name)
183 {
184  assert(image != (Image *) NULL);
185  assert(image->signature == MagickCoreSignature);
186  if (image->debug != MagickFalse)
187  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
188  if (image->profiles == (SplayTreeInfo *) NULL)
189  return(MagickFalse);
190  if (LocaleCompare(name,"icc") == 0)
191  {
192  /*
193  Continue to support deprecated color profile for now.
194  */
195  image->color_profile.length=0;
196  image->color_profile.info=(unsigned char *) NULL;
197  }
198  if (LocaleCompare(name,"iptc") == 0)
199  {
200  /*
201  Continue to support deprecated IPTC profile for now.
202  */
203  image->iptc_profile.length=0;
204  image->iptc_profile.info=(unsigned char *) NULL;
205  }
206  WriteTo8BimProfile(image,name,(StringInfo *) NULL);
207  return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->profiles,name));
208 }
209 
210 /*
211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
212 % %
213 % %
214 % %
215 % D e s t r o y I m a g e P r o f i l e s %
216 % %
217 % %
218 % %
219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
220 %
221 % DestroyImageProfiles() releases memory associated with an image profile map.
222 %
223 % The format of the DestroyProfiles method is:
224 %
225 % void DestroyImageProfiles(Image *image)
226 %
227 % A description of each parameter follows:
228 %
229 % o image: the image.
230 %
231 */
232 MagickExport void DestroyImageProfiles(Image *image)
233 {
234  if (image->profiles != (SplayTreeInfo *) NULL)
235  image->profiles=DestroySplayTree((SplayTreeInfo *) image->profiles);
236 }
237 
238 /*
239 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
240 % %
241 % %
242 % %
243 % G e t I m a g e P r o f i l e %
244 % %
245 % %
246 % %
247 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
248 %
249 % GetImageProfile() gets a profile associated with an image by name.
250 %
251 % The format of the GetImageProfile method is:
252 %
253 % const StringInfo *GetImageProfile(const Image *image,const char *name)
254 %
255 % A description of each parameter follows:
256 %
257 % o image: the image.
258 %
259 % o name: the profile name.
260 %
261 */
262 MagickExport const StringInfo *GetImageProfile(const Image *image,
263  const char *name)
264 {
265  const StringInfo
266  *profile;
267 
268  assert(image != (Image *) NULL);
269  assert(image->signature == MagickCoreSignature);
270  if (image->debug != MagickFalse)
271  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
272  if (image->profiles == (SplayTreeInfo *) NULL)
273  return((StringInfo *) NULL);
274  profile=(const StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
275  image->profiles,name);
276  return(profile);
277 }
278 
279 /*
280 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
281 % %
282 % %
283 % %
284 % G e t N e x t I m a g e P r o f i l e %
285 % %
286 % %
287 % %
288 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
289 %
290 % GetNextImageProfile() gets the next profile name for an image.
291 %
292 % The format of the GetNextImageProfile method is:
293 %
294 % char *GetNextImageProfile(const Image *image)
295 %
296 % A description of each parameter follows:
297 %
298 % o hash_info: the hash info.
299 %
300 */
301 MagickExport char *GetNextImageProfile(const Image *image)
302 {
303  assert(image != (Image *) NULL);
304  assert(image->signature == MagickCoreSignature);
305  if (IsEventLogging() != MagickFalse)
306  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
307  if (image->profiles == (SplayTreeInfo *) NULL)
308  return((char *) NULL);
309  return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->profiles));
310 }
311 
312 /*
313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
314 % %
315 % %
316 % %
317 % P r o f i l e I m a g e %
318 % %
319 % %
320 % %
321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
322 %
323 % ProfileImage() associates, applies, or removes an ICM, IPTC, or generic
324 % profile with / to / from an image. If the profile is NULL, it is removed
325 % from the image otherwise added or applied. Use a name of '*' and a profile
326 % of NULL to remove all profiles from the image.
327 %
328 % ICC and ICM profiles are handled as follows: If the image does not have
329 % an associated color profile, the one you provide is associated with the
330 % image and the image pixels are not transformed. Otherwise, the colorspace
331 % transform defined by the existing and new profile are applied to the image
332 % pixels and the new profile is associated with the image.
333 %
334 % The format of the ProfileImage method is:
335 %
336 % MagickBooleanType ProfileImage(Image *image,const char *name,
337 % const void *datum,const size_t length,const MagickBooleanType clone)
338 %
339 % A description of each parameter follows:
340 %
341 % o image: the image.
342 %
343 % o name: Name of profile to add or remove: ICC, IPTC, or generic profile.
344 %
345 % o datum: the profile data.
346 %
347 % o length: the length of the profile.
348 %
349 % o clone: should be MagickFalse.
350 %
351 */
352 
353 #if defined(MAGICKCORE_LCMS_DELEGATE)
354 
355 typedef struct _LCMSInfo
356 {
357  ColorspaceType
358  colorspace;
359 
360  cmsUInt32Number
361  type;
362 
363  size_t
364  channels;
365 
366  cmsHPROFILE
367  profile;
368 
369  int
370  intent;
371 
372  double
373  **magick_restrict pixels,
374  scale[4],
375  translate[4];
376 } LCMSInfo;
377 
378 #if LCMS_VERSION < 2060
379 static void* cmsGetContextUserData(cmsContext ContextID)
380 {
381  return(ContextID);
382 }
383 
384 static cmsContext cmsCreateContext(void *magick_unused(Plugin),void *UserData)
385 {
386  magick_unreferenced(Plugin);
387  return((cmsContext) UserData);
388 }
389 
390 static void cmsSetLogErrorHandlerTHR(cmsContext magick_unused(ContextID),
391  cmsLogErrorHandlerFunction Fn)
392 {
393  magick_unreferenced(ContextID);
394  cmsSetLogErrorHandler(Fn);
395 }
396 
397 static void cmsDeleteContext(cmsContext magick_unused(ContextID))
398 {
399  magick_unreferenced(ContextID);
400 }
401 #endif
402 
403 static double **DestroyPixelTLS(double **pixels)
404 {
405  ssize_t
406  i;
407 
408  if (pixels == (double **) NULL)
409  return((double **) NULL);
410  for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
411  if (pixels[i] != (double *) NULL)
412  pixels[i]=(double *) RelinquishMagickMemory(pixels[i]);
413  pixels=(double **) RelinquishMagickMemory(pixels);
414  return(pixels);
415 }
416 
417 static double **AcquirePixelTLS(const size_t columns,const size_t channels)
418 {
419  double
420  **pixels;
421 
422  ssize_t
423  i;
424 
425  size_t
426  number_threads;
427 
428  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
429  pixels=(double **) AcquireQuantumMemory(number_threads,sizeof(*pixels));
430  if (pixels == (double **) NULL)
431  return((double **) NULL);
432  (void) memset(pixels,0,number_threads*sizeof(*pixels));
433  for (i=0; i < (ssize_t) number_threads; i++)
434  {
435  pixels[i]=(double *) AcquireQuantumMemory(columns,channels*
436  sizeof(**pixels));
437  if (pixels[i] == (double *) NULL)
438  return(DestroyPixelTLS(pixels));
439  }
440  return(pixels);
441 }
442 
443 static cmsHTRANSFORM *DestroyTransformTLS(cmsHTRANSFORM *transform)
444 {
445  ssize_t
446  i;
447 
448  assert(transform != (cmsHTRANSFORM *) NULL);
449  for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
450  if (transform[i] != (cmsHTRANSFORM) NULL)
451  cmsDeleteTransform(transform[i]);
452  transform=(cmsHTRANSFORM *) RelinquishMagickMemory(transform);
453  return(transform);
454 }
455 
456 static cmsHTRANSFORM *AcquireTransformTLS(const LCMSInfo *source_info,
457  const LCMSInfo *target_info,const cmsUInt32Number flags,
458  cmsContext cms_context)
459 {
460  cmsHTRANSFORM
461  *transform;
462 
463  ssize_t
464  i;
465 
466  size_t
467  number_threads;
468 
469  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
470  transform=(cmsHTRANSFORM *) AcquireQuantumMemory(number_threads,
471  sizeof(*transform));
472  if (transform == (cmsHTRANSFORM *) NULL)
473  return((cmsHTRANSFORM *) NULL);
474  (void) memset(transform,0,number_threads*sizeof(*transform));
475  for (i=0; i < (ssize_t) number_threads; i++)
476  {
477  transform[i]=cmsCreateTransformTHR(cms_context,source_info->profile,
478  source_info->type,target_info->profile,target_info->type,
479  target_info->intent,flags);
480  if (transform[i] == (cmsHTRANSFORM) NULL)
481  return(DestroyTransformTLS(transform));
482  }
483  return(transform);
484 }
485 
486 static void LCMSExceptionHandler(cmsContext context,cmsUInt32Number severity,
487  const char *message)
488 {
489  Image
490  *image;
491 
492  if (IsEventLogging() != MagickFalse)
493  (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%u, %s",
494  severity,message != (char *) NULL ? message : "no message");
495  image=(Image *) cmsGetContextUserData(context);
496  if (image != (Image *) NULL)
497  (void) ThrowMagickException(&image->exception,GetMagickModule(),
498  ImageWarning,"UnableToTransformColorspace","`%s'",image->filename);
499 }
500 
501 static inline void SetLCMSInfoTranslate(LCMSInfo *info,const double translate)
502 {
503  info->translate[0]=translate;
504  info->translate[1]=translate;
505  info->translate[2]=translate;
506  info->translate[3]=translate;
507 }
508 
509 static inline void SetLCMSInfoScale(LCMSInfo *info,const double scale)
510 {
511  info->scale[0]=scale;
512  info->scale[1]=scale;
513  info->scale[2]=scale;
514  info->scale[3]=scale;
515 }
516 #endif
517 
518 static MagickBooleanType SetsRGBImageProfile(Image *image)
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);
806  profile=DestroyStringInfo(profile);
807  return(status);
808 }
809 
810 MagickExport MagickBooleanType ProfileImage(Image *image,const char *name,
811  const void *datum,const size_t length,
812  const MagickBooleanType magick_unused(clone))
813 {
814 #define GetLCMSPixel(source_info,pixel,index) (source_info.scale[index]* \
815  ((QuantumScale*(MagickRealType) (pixel))+source_info.translate[index]))
816 #define ProfileImageTag "Profile/Image"
817 #define SetLCMSPixel(target_info,pixel,index) ClampToQuantum( \
818  target_info.scale[index]*(((MagickRealType) QuantumRange*pixel)+ \
819  target_info.translate[index]))
820 #define ThrowProfileException(severity,tag,context) \
821 { \
822  if (profile != (StringInfo *) NULL) \
823  profile=DestroyStringInfo(profile); \
824  if (cms_context != (cmsContext) NULL) \
825  cmsDeleteContext(cms_context); \
826  if (source_info.profile != (cmsHPROFILE) NULL) \
827  (void) cmsCloseProfile(source_info.profile); \
828  if (target_info.profile != (cmsHPROFILE) NULL) \
829  (void) cmsCloseProfile(target_info.profile); \
830  ThrowBinaryException(severity,tag,context); \
831 }
832 
833  MagickBooleanType
834  status;
835 
836  StringInfo
837  *profile;
838 
839  magick_unreferenced(clone);
840 
841  assert(image != (Image *) NULL);
842  assert(image->signature == MagickCoreSignature);
843  assert(name != (const char *) NULL);
844  if (IsEventLogging() != MagickFalse)
845  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
846  if ((datum == (const void *) NULL) || (length == 0))
847  {
848  char
849  *next;
850 
851  /*
852  Delete image profile(s).
853  */
854  ResetImageProfileIterator(image);
855  for (next=GetNextImageProfile(image); next != (const char *) NULL; )
856  {
857  if (IsOptionMember(next,name) != MagickFalse)
858  {
859  (void) DeleteImageProfile(image,next);
860  ResetImageProfileIterator(image);
861  }
862  next=GetNextImageProfile(image);
863  }
864  return(MagickTrue);
865  }
866  /*
867  Add a ICC, IPTC, or generic profile to the image.
868  */
869  status=MagickTrue;
870  profile=AcquireStringInfo((size_t) length);
871  SetStringInfoDatum(profile,(unsigned char *) datum);
872  if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
873  status=SetImageProfile(image,name,profile);
874  else
875  {
876  const StringInfo
877  *icc_profile;
878 
879  icc_profile=GetImageProfile(image,"icc");
880  if ((icc_profile != (const StringInfo *) NULL) &&
881  (CompareStringInfo(icc_profile,profile) == 0))
882  {
883  const char
884  *value;
885 
886  value=GetImageProperty(image,"exif:ColorSpace");
887  (void) value;
888  if (LocaleCompare(value,"1") != 0)
889  (void) SetsRGBImageProfile(image);
890  value=GetImageProperty(image,"exif:InteroperabilityIndex");
891  if (LocaleCompare(value,"R98.") != 0)
892  (void) SetsRGBImageProfile(image);
893  icc_profile=GetImageProfile(image,"icc");
894  }
895  if ((icc_profile != (const StringInfo *) NULL) &&
896  (CompareStringInfo(icc_profile,profile) == 0))
897  {
898  profile=DestroyStringInfo(profile);
899  return(MagickTrue);
900  }
901 #if !defined(MAGICKCORE_LCMS_DELEGATE)
902  (void) ThrowMagickException(&image->exception,GetMagickModule(),
903  MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (LCMS)",
904  image->filename);
905 #else
906  {
907  cmsContext
908  cms_context;
909 
910  LCMSInfo
911  source_info,
912  target_info;
913 
914  /*
915  Transform pixel colors as defined by the color profiles.
916  */
917  cms_context=cmsCreateContext(NULL,image);
918  if (cms_context == (cmsContext) NULL)
919  {
920  profile=DestroyStringInfo(profile);
921  ThrowBinaryImageException(ResourceLimitError,
922  "ColorspaceColorProfileMismatch",name);
923  }
924  cmsSetLogErrorHandlerTHR(cms_context,LCMSExceptionHandler);
925  source_info.profile=cmsOpenProfileFromMemTHR(cms_context,
926  GetStringInfoDatum(profile),(cmsUInt32Number)
927  GetStringInfoLength(profile));
928  if (source_info.profile == (cmsHPROFILE) NULL)
929  {
930  profile=DestroyStringInfo(profile);
931  cmsDeleteContext(cms_context);
932  ThrowBinaryImageException(ResourceLimitError,
933  "ColorspaceColorProfileMismatch",name);
934  }
935  if ((cmsGetDeviceClass(source_info.profile) != cmsSigLinkClass) &&
936  (icc_profile == (StringInfo *) NULL))
937  status=SetImageProfile(image,name,profile);
938  else
939  {
940  CacheView
941  *image_view;
942 
943  cmsColorSpaceSignature
944  signature;
945 
946  cmsHTRANSFORM
947  *magick_restrict transform;
948 
949  cmsUInt32Number
950  flags;
951 
953  *exception;
954 
955  MagickOffsetType
956  progress;
957 
958  ssize_t
959  y;
960 
961  exception=(&image->exception);
962  target_info.profile=(cmsHPROFILE) NULL;
963  if (icc_profile != (StringInfo *) NULL)
964  {
965  target_info.profile=source_info.profile;
966  source_info.profile=cmsOpenProfileFromMemTHR(cms_context,
967  GetStringInfoDatum(icc_profile),(cmsUInt32Number)
968  GetStringInfoLength(icc_profile));
969  if (source_info.profile == (cmsHPROFILE) NULL)
970  ThrowProfileException(ResourceLimitError,
971  "ColorspaceColorProfileMismatch",name);
972  }
973  SetLCMSInfoScale(&source_info,1.0);
974  SetLCMSInfoTranslate(&source_info,0.0);
975  source_info.colorspace=sRGBColorspace;
976  source_info.channels=3;
977  switch (cmsGetColorSpace(source_info.profile))
978  {
979  case cmsSigCmykData:
980  {
981  source_info.colorspace=CMYKColorspace;
982  source_info.channels=4;
983  source_info.type=(cmsUInt32Number) TYPE_CMYK_DBL;
984  SetLCMSInfoScale(&source_info,100.0);
985  break;
986  }
987  case cmsSigGrayData:
988  {
989  source_info.colorspace=GRAYColorspace;
990  source_info.channels=1;
991  source_info.type=(cmsUInt32Number) TYPE_GRAY_DBL;
992  break;
993  }
994  case cmsSigLabData:
995  {
996  source_info.colorspace=LabColorspace;
997  source_info.type=(cmsUInt32Number) TYPE_Lab_DBL;
998  source_info.scale[0]=100.0;
999  source_info.scale[1]=255.0;
1000  source_info.scale[2]=255.0;
1001 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1002  source_info.translate[1]=(-0.5);
1003  source_info.translate[2]=(-0.5);
1004 #endif
1005  break;
1006  }
1007  case cmsSigRgbData:
1008  {
1009  source_info.colorspace=sRGBColorspace;
1010  source_info.type=(cmsUInt32Number) TYPE_RGB_DBL;
1011  break;
1012  }
1013  case cmsSigXYZData:
1014  {
1015  source_info.colorspace=XYZColorspace;
1016  source_info.type=(cmsUInt32Number) TYPE_XYZ_DBL;
1017  break;
1018  }
1019  default:
1020  ThrowProfileException(ImageError,
1021  "ColorspaceColorProfileMismatch",name);
1022  }
1023  signature=cmsGetPCS(source_info.profile);
1024  if (target_info.profile != (cmsHPROFILE) NULL)
1025  signature=cmsGetColorSpace(target_info.profile);
1026  SetLCMSInfoScale(&target_info,1.0);
1027  SetLCMSInfoTranslate(&target_info,0.0);
1028  target_info.channels=3;
1029  switch (signature)
1030  {
1031  case cmsSigCmykData:
1032  {
1033  target_info.colorspace=CMYKColorspace;
1034  target_info.channels=4;
1035  target_info.type=(cmsUInt32Number) TYPE_CMYK_DBL;
1036  SetLCMSInfoScale(&target_info,0.01);
1037  break;
1038  }
1039  case cmsSigGrayData:
1040  {
1041  target_info.colorspace=GRAYColorspace;
1042  target_info.channels=1;
1043  target_info.type=(cmsUInt32Number) TYPE_GRAY_DBL;
1044  break;
1045  }
1046  case cmsSigLabData:
1047  {
1048  target_info.colorspace=LabColorspace;
1049  target_info.type=(cmsUInt32Number) TYPE_Lab_DBL;
1050  target_info.scale[0]=0.01;
1051  target_info.scale[1]=1/255.0;
1052  target_info.scale[2]=1/255.0;
1053 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1054  target_info.translate[1]=0.5;
1055  target_info.translate[2]=0.5;
1056 #endif
1057  break;
1058  }
1059  case cmsSigRgbData:
1060  {
1061  target_info.colorspace=sRGBColorspace;
1062  target_info.type=(cmsUInt32Number) TYPE_RGB_DBL;
1063  break;
1064  }
1065  case cmsSigXYZData:
1066  {
1067  target_info.colorspace=XYZColorspace;
1068  target_info.type=(cmsUInt32Number) TYPE_XYZ_DBL;
1069  break;
1070  }
1071  default:
1072  ThrowProfileException(ImageError,
1073  "ColorspaceColorProfileMismatch",name);
1074  }
1075  switch (image->rendering_intent)
1076  {
1077  case AbsoluteIntent:
1078  {
1079  target_info.intent=INTENT_ABSOLUTE_COLORIMETRIC;
1080  break;
1081  }
1082  case PerceptualIntent:
1083  {
1084  target_info.intent=INTENT_PERCEPTUAL;
1085  break;
1086  }
1087  case RelativeIntent:
1088  {
1089  target_info.intent=INTENT_RELATIVE_COLORIMETRIC;
1090  break;
1091  }
1092  case SaturationIntent:
1093  {
1094  target_info.intent=INTENT_SATURATION;
1095  break;
1096  }
1097  default:
1098  {
1099  target_info.intent=INTENT_PERCEPTUAL;
1100  break;
1101  }
1102  }
1103  flags=cmsFLAGS_HIGHRESPRECALC;
1104 #if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
1105  if (image->black_point_compensation != MagickFalse)
1106  flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
1107 #endif
1108  transform=AcquireTransformTLS(&source_info,&target_info,
1109  flags,cms_context);
1110  if (transform == (cmsHTRANSFORM *) NULL)
1111  ThrowProfileException(ImageError,"UnableToCreateColorTransform",
1112  name);
1113  /*
1114  Transform image as dictated by the source & target image profiles.
1115  */
1116  source_info.pixels=AcquirePixelTLS(image->columns,
1117  source_info.channels);
1118  target_info.pixels=AcquirePixelTLS(image->columns,
1119  target_info.channels);
1120  if ((source_info.pixels == (double **) NULL) ||
1121  (target_info.pixels == (double **) NULL))
1122  {
1123  target_info.pixels=DestroyPixelTLS(target_info.pixels);
1124  source_info.pixels=DestroyPixelTLS(source_info.pixels);
1125  transform=DestroyTransformTLS(transform);
1126  ThrowProfileException(ResourceLimitError,
1127  "MemoryAllocationFailed",image->filename);
1128  }
1129  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1130  {
1131  target_info.pixels=DestroyPixelTLS(target_info.pixels);
1132  source_info.pixels=DestroyPixelTLS(source_info.pixels);
1133  transform=DestroyTransformTLS(transform);
1134  profile=DestroyStringInfo(profile);
1135  if (source_info.profile != (cmsHPROFILE) NULL)
1136  (void) cmsCloseProfile(source_info.profile);
1137  if (target_info.profile != (cmsHPROFILE) NULL)
1138  (void) cmsCloseProfile(target_info.profile);
1139  return(MagickFalse);
1140  }
1141  if (target_info.colorspace == CMYKColorspace)
1142  (void) SetImageColorspace(image,target_info.colorspace);
1143  progress=0;
1144  image_view=AcquireAuthenticCacheView(image,exception);
1145 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1146  #pragma omp parallel for schedule(static) shared(status) \
1147  magick_number_threads(image,image,image->rows,1)
1148 #endif
1149  for (y=0; y < (ssize_t) image->rows; y++)
1150  {
1151  const int
1152  id = GetOpenMPThreadId();
1153 
1154  MagickBooleanType
1155  sync;
1156 
1157  IndexPacket
1158  *magick_restrict indexes;
1159 
1160  double
1161  *p;
1162 
1163  PixelPacket
1164  *magick_restrict q;
1165 
1166  ssize_t
1167  x;
1168 
1169  if (status == MagickFalse)
1170  continue;
1171  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1172  exception);
1173  if (q == (PixelPacket *) NULL)
1174  {
1175  status=MagickFalse;
1176  continue;
1177  }
1178  indexes=GetCacheViewAuthenticIndexQueue(image_view);
1179  p=source_info.pixels[id];
1180  for (x=0; x < (ssize_t) image->columns; x++)
1181  {
1182  *p++=GetLCMSPixel(source_info,GetPixelRed(q),0);
1183  if (source_info.channels > 1)
1184  {
1185  *p++=GetLCMSPixel(source_info,GetPixelGreen(q),1);
1186  *p++=GetLCMSPixel(source_info,GetPixelBlue(q),2);
1187  }
1188  if (source_info.channels > 3)
1189  {
1190  *p=GetLCMSPixel(source_info,0,3);
1191  if (indexes != (IndexPacket *) NULL)
1192  *p=GetLCMSPixel(source_info,GetPixelIndex(indexes+x),3);
1193  p++;
1194  }
1195  q++;
1196  }
1197  cmsDoTransform(transform[id],source_info.pixels[id],
1198  target_info.pixels[id],(unsigned int) image->columns);
1199  p=target_info.pixels[id];
1200  q-=image->columns;
1201  for (x=0; x < (ssize_t) image->columns; x++)
1202  {
1203  SetPixelRed(q,SetLCMSPixel(target_info,*p,0));
1204  SetPixelGreen(q,GetPixelRed(q));
1205  SetPixelBlue(q,GetPixelRed(q));
1206  p++;
1207  if (target_info.channels > 1)
1208  {
1209  SetPixelGreen(q,SetLCMSPixel(target_info,*p,1));
1210  p++;
1211  SetPixelBlue(q,SetLCMSPixel(target_info,*p,2));
1212  p++;
1213  }
1214  if (target_info.channels > 3)
1215  {
1216  if (indexes != (IndexPacket *) NULL)
1217  SetPixelIndex(indexes+x,SetLCMSPixel(target_info,*p,3));
1218  p++;
1219  }
1220  q++;
1221  }
1222  sync=SyncCacheViewAuthenticPixels(image_view,exception);
1223  if (sync == MagickFalse)
1224  status=MagickFalse;
1225  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1226  {
1227  MagickBooleanType
1228  proceed;
1229 
1230 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1231  #pragma omp atomic
1232 #endif
1233  progress++;
1234  proceed=SetImageProgress(image,ProfileImageTag,progress,
1235  image->rows);
1236  if (proceed == MagickFalse)
1237  status=MagickFalse;
1238  }
1239  }
1240  image_view=DestroyCacheView(image_view);
1241  (void) SetImageColorspace(image,target_info.colorspace);
1242  switch (signature)
1243  {
1244  case cmsSigRgbData:
1245  {
1246  image->type=image->matte == MagickFalse ? TrueColorType :
1247  TrueColorMatteType;
1248  break;
1249  }
1250  case cmsSigCmykData:
1251  {
1252  image->type=image->matte == MagickFalse ? ColorSeparationType :
1253  ColorSeparationMatteType;
1254  break;
1255  }
1256  case cmsSigGrayData:
1257  {
1258  image->type=image->matte == MagickFalse ? GrayscaleType :
1259  GrayscaleMatteType;
1260  break;
1261  }
1262  default:
1263  break;
1264  }
1265  target_info.pixels=DestroyPixelTLS(target_info.pixels);
1266  source_info.pixels=DestroyPixelTLS(source_info.pixels);
1267  transform=DestroyTransformTLS(transform);
1268  if ((status != MagickFalse) &&
1269  (cmsGetDeviceClass(source_info.profile) != cmsSigLinkClass))
1270  status=SetImageProfile(image,name,profile);
1271  if (target_info.profile != (cmsHPROFILE) NULL)
1272  (void) cmsCloseProfile(target_info.profile);
1273  }
1274  (void) cmsCloseProfile(source_info.profile);
1275  cmsDeleteContext(cms_context);
1276  }
1277 #endif
1278  }
1279  profile=DestroyStringInfo(profile);
1280  return(status);
1281 }
1282 
1283 /*
1284 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1285 % %
1286 % %
1287 % %
1288 % R e m o v e I m a g e P r o f i l e %
1289 % %
1290 % %
1291 % %
1292 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1293 %
1294 % RemoveImageProfile() removes a named profile from the image and returns its
1295 % value.
1296 %
1297 % The format of the RemoveImageProfile method is:
1298 %
1299 % void *RemoveImageProfile(Image *image,const char *name)
1300 %
1301 % A description of each parameter follows:
1302 %
1303 % o image: the image.
1304 %
1305 % o name: the profile name.
1306 %
1307 */
1308 MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
1309 {
1310  StringInfo
1311  *profile;
1312 
1313  assert(image != (Image *) NULL);
1314  assert(image->signature == MagickCoreSignature);
1315  if (IsEventLogging() != MagickFalse)
1316  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1317  if (image->profiles == (SplayTreeInfo *) NULL)
1318  return((StringInfo *) NULL);
1319  if (LocaleCompare(name,"icc") == 0)
1320  {
1321  /*
1322  Continue to support deprecated color profile for now.
1323  */
1324  image->color_profile.length=0;
1325  image->color_profile.info=(unsigned char *) NULL;
1326  }
1327  if (LocaleCompare(name,"iptc") == 0)
1328  {
1329  /*
1330  Continue to support deprecated IPTC profile for now.
1331  */
1332  image->iptc_profile.length=0;
1333  image->iptc_profile.info=(unsigned char *) NULL;
1334  }
1335  WriteTo8BimProfile(image,name,(StringInfo *) NULL);
1336  profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
1337  image->profiles,name);
1338  return(profile);
1339 }
1340 
1341 /*
1342 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1343 % %
1344 % %
1345 % %
1346 % R e s e t P r o f i l e I t e r a t o r %
1347 % %
1348 % %
1349 % %
1350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1351 %
1352 % ResetImageProfileIterator() resets the image profile iterator. Use it in
1353 % conjunction with GetNextImageProfile() to iterate over all the profiles
1354 % associated with an image.
1355 %
1356 % The format of the ResetImageProfileIterator method is:
1357 %
1358 % ResetImageProfileIterator(Image *image)
1359 %
1360 % A description of each parameter follows:
1361 %
1362 % o image: the image.
1363 %
1364 */
1365 MagickExport void ResetImageProfileIterator(const Image *image)
1366 {
1367  assert(image != (Image *) NULL);
1368  assert(image->signature == MagickCoreSignature);
1369  if (IsEventLogging() != MagickFalse)
1370  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1371  if (image->profiles == (SplayTreeInfo *) NULL)
1372  return;
1373  ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
1374 }
1375 
1376 /*
1377 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1378 % %
1379 % %
1380 % %
1381 % S e t I m a g e P r o f i l e %
1382 % %
1383 % %
1384 % %
1385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1386 %
1387 % SetImageProfile() adds a named profile to the image. If a profile with the
1388 % same name already exists, it is replaced. This method differs from the
1389 % ProfileImage() method in that it does not apply CMS color profiles.
1390 %
1391 % The format of the SetImageProfile method is:
1392 %
1393 % MagickBooleanType SetImageProfile(Image *image,const char *name,
1394 % const StringInfo *profile)
1395 %
1396 % A description of each parameter follows:
1397 %
1398 % o image: the image.
1399 %
1400 % o name: the profile name, for example icc, exif, and 8bim (8bim is the
1401 % Photoshop wrapper for iptc profiles).
1402 %
1403 % o profile: A StringInfo structure that contains the named profile.
1404 %
1405 */
1406 
1407 static void *DestroyProfile(void *profile)
1408 {
1409  return((void *) DestroyStringInfo((StringInfo *) profile));
1410 }
1411 
1412 static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1413  unsigned char *quantum)
1414 {
1415  *quantum=(*p++);
1416  return(p);
1417 }
1418 
1419 static inline const unsigned char *ReadResourceLong(const unsigned char *p,
1420  unsigned int *quantum)
1421 {
1422  *quantum=(unsigned int) (*p++) << 24;
1423  *quantum|=(unsigned int) (*p++) << 16;
1424  *quantum|=(unsigned int) (*p++) << 8;
1425  *quantum|=(unsigned int) (*p++);
1426  return(p);
1427 }
1428 
1429 static inline const unsigned char *ReadResourceShort(const unsigned char *p,
1430  unsigned short *quantum)
1431 {
1432  *quantum=(unsigned short) (*p++) << 8;
1433  *quantum|=(unsigned short) (*p++);
1434  return(p);
1435 }
1436 
1437 static inline void WriteResourceLong(unsigned char *p,
1438  const unsigned int quantum)
1439 {
1440  unsigned char
1441  buffer[4];
1442 
1443  buffer[0]=(unsigned char) (quantum >> 24);
1444  buffer[1]=(unsigned char) (quantum >> 16);
1445  buffer[2]=(unsigned char) (quantum >> 8);
1446  buffer[3]=(unsigned char) quantum;
1447  (void) memcpy(p,buffer,4);
1448 }
1449 
1450 static void WriteTo8BimProfile(Image *image,const char *name,
1451  const StringInfo *profile)
1452 {
1453 
1454  const unsigned char
1455  *datum,
1456  *q;
1457 
1458  const unsigned char
1459  *p;
1460 
1461  size_t
1462  length;
1463 
1464  StringInfo
1465  *profile_8bim;
1466 
1467  ssize_t
1468  count;
1469 
1470  unsigned char
1471  length_byte;
1472 
1473  unsigned int
1474  value;
1475 
1476  unsigned short
1477  id,
1478  profile_id;
1479 
1480  if (LocaleCompare(name,"icc") == 0)
1481  profile_id=0x040f;
1482  else
1483  if (LocaleCompare(name,"iptc") == 0)
1484  profile_id=0x0404;
1485  else
1486  if (LocaleCompare(name,"xmp") == 0)
1487  profile_id=0x0424;
1488  else
1489  return;
1490  profile_8bim=(StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
1491  image->profiles,"8bim");
1492  if (profile_8bim == (StringInfo *) NULL)
1493  return;
1494  datum=GetStringInfoDatum(profile_8bim);
1495  length=GetStringInfoLength(profile_8bim);
1496  for (p=datum; p < (datum+length-16); )
1497  {
1498  q=p;
1499  if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1500  break;
1501  p+=(ptrdiff_t) 4;
1502  p=ReadResourceShort(p,&id);
1503  p=ReadResourceByte(p,&length_byte);
1504  p+=(ptrdiff_t) length_byte;
1505  if (((length_byte+1) & 0x01) != 0)
1506  p++;
1507  if (p > (datum+length-4))
1508  break;
1509  p=ReadResourceLong(p,&value);
1510  count=(ssize_t) value;
1511  if ((count & 0x01) != 0)
1512  count++;
1513  if ((count < 0) || (p > (datum+length-count)) || (count > (ssize_t) length))
1514  break;
1515  if (id != profile_id)
1516  p+=(ptrdiff_t) count;
1517  else
1518  {
1519  size_t
1520  extent,
1521  offset;
1522 
1523  ssize_t
1524  extract_extent;
1525 
1526  StringInfo
1527  *extract_profile;
1528 
1529  extract_extent=0;
1530  extent=(datum+length)-(p+count);
1531  if (profile == (StringInfo *) NULL)
1532  {
1533  offset=(q-datum);
1534  extract_profile=AcquireStringInfo(offset+extent);
1535  (void) memcpy(extract_profile->datum,datum,offset);
1536  }
1537  else
1538  {
1539  offset=(p-datum);
1540  extract_extent=profile->length;
1541  if ((extract_extent & 0x01) != 0)
1542  extract_extent++;
1543  extract_profile=AcquireStringInfo(offset+extract_extent+extent);
1544  (void) memcpy(extract_profile->datum,datum,offset-4);
1545  WriteResourceLong(extract_profile->datum+offset-4,(unsigned int)
1546  profile->length);
1547  (void) memcpy(extract_profile->datum+offset,
1548  profile->datum,profile->length);
1549  }
1550  (void) memcpy(extract_profile->datum+offset+extract_extent,
1551  p+count,extent);
1552  (void) AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1553  ConstantString("8bim"),CloneStringInfo(extract_profile));
1554  extract_profile=DestroyStringInfo(extract_profile);
1555  break;
1556  }
1557  }
1558 }
1559 
1560 static void GetProfilesFromResourceBlock(Image *image,
1561  const StringInfo *resource_block)
1562 {
1563  const unsigned char
1564  *datum;
1565 
1566  const unsigned char
1567  *p;
1568 
1569  size_t
1570  length;
1571 
1572  ssize_t
1573  count;
1574 
1575  StringInfo
1576  *profile;
1577 
1578  unsigned char
1579  length_byte;
1580 
1581  unsigned int
1582  value;
1583 
1584  unsigned short
1585  id;
1586 
1587  datum=GetStringInfoDatum(resource_block);
1588  length=GetStringInfoLength(resource_block);
1589  for (p=datum; p < (datum+length-16); )
1590  {
1591  if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1592  break;
1593  p+=(ptrdiff_t) 4;
1594  p=ReadResourceShort(p,&id);
1595  p=ReadResourceByte(p,&length_byte);
1596  p+=(ptrdiff_t) length_byte;
1597  if (((length_byte+1) & 0x01) != 0)
1598  p++;
1599  if (p > (datum+length-4))
1600  break;
1601  p=ReadResourceLong(p,&value);
1602  count=(ssize_t) value;
1603  if ((p > (datum+length-count)) || (count > (ssize_t) length) ||
1604  (count <= 0))
1605  break;
1606  switch (id)
1607  {
1608  case 0x03ed:
1609  {
1610  unsigned int
1611  resolution;
1612 
1613  unsigned short
1614  units;
1615 
1616  /*
1617  Resolution.
1618  */
1619  if (count < 10)
1620  break;
1621  p=ReadResourceLong(p,&resolution);
1622  image->x_resolution=((double) resolution)/65536.0;
1623  p=ReadResourceShort(p,&units)+2;
1624  p=ReadResourceLong(p,&resolution)+4;
1625  image->y_resolution=((double) resolution)/65536.0;
1626  /*
1627  Values are always stored as pixels per inch.
1628  */
1629  if ((ResolutionType) units != PixelsPerCentimeterResolution)
1630  image->units=PixelsPerInchResolution;
1631  else
1632  {
1633  image->units=PixelsPerCentimeterResolution;
1634  image->x_resolution/=2.54;
1635  image->y_resolution/=2.54;
1636  }
1637  break;
1638  }
1639  case 0x0404:
1640  {
1641  /*
1642  IPTC Profile
1643  */
1644  profile=AcquireStringInfo(count);
1645  SetStringInfoDatum(profile,p);
1646  (void) SetImageProfileInternal(image,"iptc",profile,MagickTrue);
1647  profile=DestroyStringInfo(profile);
1648  p+=(ptrdiff_t) count;
1649  break;
1650  }
1651  case 0x040c:
1652  {
1653  /*
1654  Thumbnail.
1655  */
1656  p+=(ptrdiff_t) count;
1657  break;
1658  }
1659  case 0x040f:
1660  {
1661  /*
1662  ICC profile.
1663  */
1664  profile=AcquireStringInfo(count);
1665  SetStringInfoDatum(profile,p);
1666  (void) SetImageProfileInternal(image,"icc",profile,MagickTrue);
1667  profile=DestroyStringInfo(profile);
1668  p+=(ptrdiff_t) count;
1669  break;
1670  }
1671  case 0x0422:
1672  {
1673  /*
1674  EXIF Profile.
1675  */
1676  profile=AcquireStringInfo(count);
1677  SetStringInfoDatum(profile,p);
1678  (void) SetImageProfileInternal(image,"exif",profile,MagickTrue);
1679  profile=DestroyStringInfo(profile);
1680  p+=(ptrdiff_t) count;
1681  break;
1682  }
1683  case 0x0424:
1684  {
1685  /*
1686  XMP Profile.
1687  */
1688  profile=AcquireStringInfo(count);
1689  SetStringInfoDatum(profile,p);
1690  (void) SetImageProfileInternal(image,"xmp",profile,MagickTrue);
1691  profile=DestroyStringInfo(profile);
1692  p+=(ptrdiff_t) count;
1693  break;
1694  }
1695  default:
1696  {
1697  p+=(ptrdiff_t) count;
1698  break;
1699  }
1700  }
1701  if ((count & 0x01) != 0)
1702  p++;
1703  }
1704 }
1705 
1706 #if defined(MAGICKCORE_XML_DELEGATE)
1707 static MagickBooleanType ValidateXMPProfile(Image *image,
1708  const StringInfo *profile)
1709 {
1710  xmlDocPtr
1711  document;
1712 
1713  /*
1714  Parse XML profile.
1715  */
1716  const char *artifact=GetImageArtifact(image,"xmp:validate");
1717  if (IsStringTrue(artifact) == MagickFalse)
1718  return(MagickTrue);
1719  document=xmlReadMemory((const char *) GetStringInfoDatum(profile),(int)
1720  GetStringInfoLength(profile),"xmp.xml",NULL,XML_PARSE_NOERROR |
1721  XML_PARSE_NOWARNING);
1722  if (document == (xmlDocPtr) NULL)
1723  {
1724  (void) ThrowMagickException(&image->exception,GetMagickModule(),
1725  ImageWarning,"CorruptImageProfile","`%s' (XMP)",image->filename);
1726  return(MagickFalse);
1727  }
1728  xmlFreeDoc(document);
1729  return(MagickTrue);
1730 }
1731 #else
1732 static MagickBooleanType ValidateXMPProfile(Image *image,
1733  const StringInfo *profile)
1734 {
1735  (void) ThrowMagickException(&image->exception,GetMagickModule(),
1736  MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (XML)",
1737  image->filename);
1738  return(MagickFalse);
1739 }
1740 #endif
1741 
1742 static MagickBooleanType SetImageProfileInternal(Image *image,const char *name,
1743  const StringInfo *profile,const MagickBooleanType recursive)
1744 {
1745  char
1746  key[MaxTextExtent];
1747 
1748  MagickBooleanType
1749  status;
1750 
1751  assert(image != (Image *) NULL);
1752  assert(image->signature == MagickCoreSignature);
1753  if (IsEventLogging() != MagickFalse)
1754  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1755  if ((LocaleCompare(name,"xmp") == 0) &&
1756  (ValidateXMPProfile(image,profile) == MagickFalse))
1757  return(MagickTrue);
1758  if (image->profiles == (SplayTreeInfo *) NULL)
1759  image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
1760  DestroyProfile);
1761  (void) CopyMagickString(key,name,MaxTextExtent);
1762  LocaleLower(key);
1763  status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1764  ConstantString(key),CloneStringInfo(profile));
1765  if ((status != MagickFalse) &&
1766  ((LocaleCompare(name,"icc") == 0) || (LocaleCompare(name,"icm") == 0)))
1767  {
1768  const StringInfo
1769  *icc_profile;
1770 
1771  /*
1772  Continue to support deprecated color profile member.
1773  */
1774  icc_profile=GetImageProfile(image,name);
1775  if (icc_profile != (const StringInfo *) NULL)
1776  {
1777  image->color_profile.length=GetStringInfoLength(icc_profile);
1778  image->color_profile.info=GetStringInfoDatum(icc_profile);
1779  }
1780  }
1781  if ((status != MagickFalse) &&
1782  ((LocaleCompare(name,"iptc") == 0) || (LocaleCompare(name,"8bim") == 0)))
1783  {
1784  const StringInfo
1785  *iptc_profile;
1786 
1787  /*
1788  Continue to support deprecated IPTC profile member.
1789  */
1790  iptc_profile=GetImageProfile(image,name);
1791  if (iptc_profile != (const StringInfo *) NULL)
1792  {
1793  image->iptc_profile.length=GetStringInfoLength(iptc_profile);
1794  image->iptc_profile.info=GetStringInfoDatum(iptc_profile);
1795  }
1796  }
1797  if (status != MagickFalse)
1798  {
1799  if (LocaleCompare(name,"8bim") == 0)
1800  GetProfilesFromResourceBlock(image,profile);
1801  else
1802  if (recursive == MagickFalse)
1803  WriteTo8BimProfile(image,name,profile);
1804  }
1805  return(status);
1806 }
1807 
1808 MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
1809  const StringInfo *profile)
1810 {
1811  return(SetImageProfileInternal(image,name,profile,MagickFalse));
1812 }
1813 
1814 /*
1815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1816 % %
1817 % %
1818 % %
1819 % S y n c I m a g e P r o f i l e s %
1820 % %
1821 % %
1822 % %
1823 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1824 %
1825 % SyncImageProfiles() synchronizes image properties with the image profiles.
1826 % Currently we only support updating the EXIF resolution and orientation.
1827 %
1828 % The format of the SyncImageProfiles method is:
1829 %
1830 % MagickBooleanType SyncImageProfiles(Image *image)
1831 %
1832 % A description of each parameter follows:
1833 %
1834 % o image: the image.
1835 %
1836 */
1837 
1838 static inline int ReadProfileByte(unsigned char **p,size_t *length)
1839 {
1840  int
1841  c;
1842 
1843  if (*length < 1)
1844  return(EOF);
1845  c=(int) (*(*p)++);
1846  (*length)--;
1847  return(c);
1848 }
1849 
1850 static inline signed short ReadProfileShort(const EndianType endian,
1851  unsigned char *buffer)
1852 {
1853  union
1854  {
1855  unsigned int
1856  unsigned_value;
1857 
1858  signed int
1859  signed_value;
1860  } quantum;
1861 
1862  unsigned short
1863  value;
1864 
1865  if (endian == LSBEndian)
1866  {
1867  value=(unsigned short) buffer[1] << 8;
1868  value|=(unsigned short) buffer[0];
1869  quantum.unsigned_value=value & 0xffff;
1870  return(quantum.signed_value);
1871  }
1872  value=(unsigned short) buffer[0] << 8;
1873  value|=(unsigned short) buffer[1];
1874  quantum.unsigned_value=value & 0xffff;
1875  return(quantum.signed_value);
1876 }
1877 
1878 static inline signed int ReadProfileLong(const EndianType endian,
1879  unsigned char *buffer)
1880 {
1881  union
1882  {
1883  unsigned int
1884  unsigned_value;
1885 
1886  signed int
1887  signed_value;
1888  } quantum;
1889 
1890  unsigned int
1891  value;
1892 
1893  if (endian == LSBEndian)
1894  {
1895  value=(unsigned int) buffer[3] << 24;
1896  value|=(unsigned int) buffer[2] << 16;
1897  value|=(unsigned int) buffer[1] << 8;
1898  value|=(unsigned int) buffer[0];
1899  quantum.unsigned_value=value & 0xffffffff;
1900  return(quantum.signed_value);
1901  }
1902  value=(unsigned int) buffer[0] << 24;
1903  value|=(unsigned int) buffer[1] << 16;
1904  value|=(unsigned int) buffer[2] << 8;
1905  value|=(unsigned int) buffer[3];
1906  quantum.unsigned_value=value & 0xffffffff;
1907  return(quantum.signed_value);
1908 }
1909 
1910 static inline signed int ReadProfileMSBLong(unsigned char **p,size_t *length)
1911 {
1912  signed int
1913  value;
1914 
1915  if (*length < 4)
1916  return(0);
1917  value=ReadProfileLong(MSBEndian,*p);
1918  (*length)-=4;
1919  *p+=4;
1920  return(value);
1921 }
1922 
1923 static inline signed short ReadProfileMSBShort(unsigned char **p,
1924  size_t *length)
1925 {
1926  signed short
1927  value;
1928 
1929  if (*length < 2)
1930  return(0);
1931  value=ReadProfileShort(MSBEndian,*p);
1932  (*length)-=2;
1933  *p+=2;
1934  return(value);
1935 }
1936 
1937 static inline void WriteProfileLong(const EndianType endian,
1938  const size_t value,unsigned char *p)
1939 {
1940  unsigned char
1941  buffer[4];
1942 
1943  if (endian == LSBEndian)
1944  {
1945  buffer[0]=(unsigned char) value;
1946  buffer[1]=(unsigned char) (value >> 8);
1947  buffer[2]=(unsigned char) (value >> 16);
1948  buffer[3]=(unsigned char) (value >> 24);
1949  (void) memcpy(p,buffer,4);
1950  return;
1951  }
1952  buffer[0]=(unsigned char) (value >> 24);
1953  buffer[1]=(unsigned char) (value >> 16);
1954  buffer[2]=(unsigned char) (value >> 8);
1955  buffer[3]=(unsigned char) value;
1956  (void) memcpy(p,buffer,4);
1957 }
1958 
1959 static void WriteProfileShort(const EndianType endian,
1960  const unsigned short value,unsigned char *p)
1961 {
1962  unsigned char
1963  buffer[2];
1964 
1965  if (endian == LSBEndian)
1966  {
1967  buffer[0]=(unsigned char) value;
1968  buffer[1]=(unsigned char) (value >> 8);
1969  (void) memcpy(p,buffer,2);
1970  return;
1971  }
1972  buffer[0]=(unsigned char) (value >> 8);
1973  buffer[1]=(unsigned char) value;
1974  (void) memcpy(p,buffer,2);
1975 }
1976 
1977 static MagickBooleanType SyncExifProfile(const Image *image,unsigned char *exif,
1978  size_t length)
1979 {
1980 #define MaxDirectoryStack 16
1981 #define EXIF_DELIMITER "\n"
1982 #define EXIF_NUM_FORMATS 12
1983 #define TAG_EXIF_OFFSET 0x8769
1984 #define TAG_INTEROP_OFFSET 0xa005
1985 
1986  typedef struct _DirectoryInfo
1987  {
1988  unsigned char
1989  *directory;
1990 
1991  size_t
1992  entry;
1993  } DirectoryInfo;
1994 
1995  DirectoryInfo
1996  directory_stack[MaxDirectoryStack] = { { 0, 0 } };
1997 
1998  EndianType
1999  endian;
2000 
2001  size_t
2002  entry,
2003  number_entries;
2004 
2006  *exif_resources;
2007 
2008  ssize_t
2009  id,
2010  level,
2011  offset;
2012 
2013  static int
2014  format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
2015 
2016  unsigned char
2017  *directory;
2018 
2019  if (length < 16)
2020  return(MagickFalse);
2021  id=(ssize_t) ReadProfileShort(LSBEndian,exif);
2022  if ((id != 0x4949) && (id != 0x4D4D))
2023  {
2024  while (length != 0)
2025  {
2026  if (ReadProfileByte(&exif,&length) != 0x45)
2027  continue;
2028  if (ReadProfileByte(&exif,&length) != 0x78)
2029  continue;
2030  if (ReadProfileByte(&exif,&length) != 0x69)
2031  continue;
2032  if (ReadProfileByte(&exif,&length) != 0x66)
2033  continue;
2034  if (ReadProfileByte(&exif,&length) != 0x00)
2035  continue;
2036  if (ReadProfileByte(&exif,&length) != 0x00)
2037  continue;
2038  break;
2039  }
2040  if (length < 16)
2041  return(MagickFalse);
2042  id=(ssize_t) ReadProfileShort(LSBEndian,exif);
2043  }
2044  endian=LSBEndian;
2045  if (id == 0x4949)
2046  endian=LSBEndian;
2047  else
2048  if (id == 0x4D4D)
2049  endian=MSBEndian;
2050  else
2051  return(MagickFalse);
2052  if (ReadProfileShort(endian,exif+2) != 0x002a)
2053  return(MagickFalse);
2054  /*
2055  This the offset to the first IFD.
2056  */
2057  offset=(ssize_t) ReadProfileLong(endian,exif+4);
2058  if ((offset < 0) || ((size_t) offset >= length))
2059  return(MagickFalse);
2060  directory=exif+offset;
2061  level=0;
2062  entry=0;
2063  exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
2064  (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
2065  do
2066  {
2067  if (level > 0)
2068  {
2069  level--;
2070  directory=directory_stack[level].directory;
2071  entry=directory_stack[level].entry;
2072  }
2073  if ((directory < exif) || (directory > (exif+length-2)))
2074  break;
2075  /*
2076  Determine how many entries there are in the current IFD.
2077  */
2078  number_entries=ReadProfileShort(endian,directory);
2079  for ( ; entry < number_entries; entry++)
2080  {
2081  int
2082  components;
2083 
2084  unsigned char
2085  *p,
2086  *q;
2087 
2088  size_t
2089  number_bytes;
2090 
2091  ssize_t
2092  format,
2093  tag_value;
2094 
2095  q=(unsigned char *) (directory+2+(12*entry));
2096  if (q > (exif+length-12))
2097  break; /* corrupt EXIF */
2098  if (GetValueFromSplayTree(exif_resources,q) == q)
2099  break;
2100  (void) AddValueToSplayTree(exif_resources,q,q);
2101  tag_value=(ssize_t) ReadProfileShort(endian,q);
2102  format=(ssize_t) ReadProfileShort(endian,q+2);
2103  if ((format < 0) || ((format-1) >= EXIF_NUM_FORMATS))
2104  break;
2105  components=(int) ReadProfileLong(endian,q+4);
2106  if (components < 0)
2107  break; /* corrupt EXIF */
2108  number_bytes=(size_t) components*format_bytes[format];
2109  if ((ssize_t) number_bytes < components)
2110  break; /* prevent overflow */
2111  if (number_bytes <= 4)
2112  p=q+8;
2113  else
2114  {
2115  /*
2116  The directory entry contains an offset.
2117  */
2118  offset=(ssize_t) ReadProfileLong(endian,q+8);
2119  if ((offset < 0) || ((size_t) (offset+number_bytes) > length))
2120  continue;
2121  if (~length < number_bytes)
2122  continue; /* prevent overflow */
2123  p=(unsigned char *) (exif+offset);
2124  }
2125  switch (tag_value)
2126  {
2127  case 0x011a:
2128  {
2129  (void) WriteProfileLong(endian,(size_t) (image->x_resolution+0.5),p);
2130  if (number_bytes == 8)
2131  (void) WriteProfileLong(endian,1UL,p+4);
2132  break;
2133  }
2134  case 0x011b:
2135  {
2136  (void) WriteProfileLong(endian,(size_t) (image->y_resolution+0.5),p);
2137  if (number_bytes == 8)
2138  (void) WriteProfileLong(endian,1UL,p+4);
2139  break;
2140  }
2141  case 0x0112:
2142  {
2143  if (number_bytes == 4)
2144  {
2145  (void) WriteProfileLong(endian,(size_t) image->orientation,p);
2146  break;
2147  }
2148  (void) WriteProfileShort(endian,(unsigned short) image->orientation,
2149  p);
2150  break;
2151  }
2152  case 0x0128:
2153  {
2154  if (number_bytes == 4)
2155  {
2156  (void) WriteProfileLong(endian,((size_t) image->units)+1,p);
2157  break;
2158  }
2159  (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
2160  break;
2161  }
2162  default:
2163  break;
2164  }
2165  if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
2166  {
2167  offset=(ssize_t) ReadProfileLong(endian,p);
2168  if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
2169  {
2170  directory_stack[level].directory=directory;
2171  entry++;
2172  directory_stack[level].entry=entry;
2173  level++;
2174  directory_stack[level].directory=exif+offset;
2175  directory_stack[level].entry=0;
2176  level++;
2177  if ((directory+2+(12*number_entries)) > (exif+length))
2178  break;
2179  offset=(ssize_t) ReadProfileLong(endian,directory+2+(12*
2180  number_entries));
2181  if ((offset != 0) && ((size_t) offset < length) &&
2182  (level < (MaxDirectoryStack-2)))
2183  {
2184  directory_stack[level].directory=exif+offset;
2185  directory_stack[level].entry=0;
2186  level++;
2187  }
2188  }
2189  break;
2190  }
2191  }
2192  } while (level > 0);
2193  exif_resources=DestroySplayTree(exif_resources);
2194  return(MagickTrue);
2195 }
2196 
2197 static MagickBooleanType Sync8BimProfile(const Image *image,
2198  const StringInfo *profile)
2199 {
2200  size_t
2201  length;
2202 
2203  ssize_t
2204  count;
2205 
2206  unsigned char
2207  *p;
2208 
2209  unsigned short
2210  id;
2211 
2212  length=GetStringInfoLength(profile);
2213  p=GetStringInfoDatum(profile);
2214  while (length != 0)
2215  {
2216  if (ReadProfileByte(&p,&length) != 0x38)
2217  continue;
2218  if (ReadProfileByte(&p,&length) != 0x42)
2219  continue;
2220  if (ReadProfileByte(&p,&length) != 0x49)
2221  continue;
2222  if (ReadProfileByte(&p,&length) != 0x4D)
2223  continue;
2224  if (length < 7)
2225  return(MagickFalse);
2226  id=ReadProfileMSBShort(&p,&length);
2227  count=(ssize_t) ReadProfileByte(&p,&length);
2228  if ((count >= (ssize_t) length) || (count < 0))
2229  return(MagickFalse);
2230  p+=(ptrdiff_t) count;
2231  length-=count;
2232  if ((*p & 0x01) == 0)
2233  (void) ReadProfileByte(&p,&length);
2234  count=(ssize_t) ReadProfileMSBLong(&p,&length);
2235  if ((count > (ssize_t) length) || (count < 0))
2236  return(MagickFalse);
2237  if ((id == 0x3ED) && (count == 16))
2238  {
2239  if (image->units == PixelsPerCentimeterResolution)
2240  WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2241  image->x_resolution*2.54*65536.0),p);
2242  else
2243  WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2244  image->x_resolution*65536.0),p);
2245  WriteProfileShort(MSBEndian,(unsigned short) image->units,p+4);
2246  if (image->units == PixelsPerCentimeterResolution)
2247  WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2248  image->y_resolution*2.54*65536.0),p+8);
2249  else
2250  WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2251  image->y_resolution*65536.0),p+8);
2252  WriteProfileShort(MSBEndian,(unsigned short) image->units,p+12);
2253  }
2254  if (id == 0x0422)
2255  (void) SyncExifProfile(image,p,count);
2256  p+=(ptrdiff_t) count;
2257  length-=count;
2258  }
2259  return(MagickTrue);
2260 }
2261 
2262 MagickExport MagickBooleanType SyncImageProfiles(Image *image)
2263 {
2264  MagickBooleanType
2265  status;
2266 
2267  StringInfo
2268  *profile;
2269 
2270  status=MagickTrue;
2271  profile=(StringInfo *) GetImageProfile(image,"8BIM");
2272  if (profile != (StringInfo *) NULL)
2273  if (Sync8BimProfile(image,profile) == MagickFalse)
2274  status=MagickFalse;
2275  profile=(StringInfo *) GetImageProfile(image,"EXIF");
2276  if (profile != (StringInfo *) NULL)
2277  if (SyncExifProfile(image,GetStringInfoDatum(profile),
2278  GetStringInfoLength(profile)) == MagickFalse)
2279  status=MagickFalse;
2280  return(status);
2281 }
Definition: image.h:133