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