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