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