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