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+=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+=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+=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+=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 source_info.translate[1]=(-0.5);
1147 source_info.translate[2]=(-0.5);
1148 }
1149#if (MAGICKCORE_QUANTUM_DEPTH == 8)
1150 else
1151 source_info.type=(cmsUInt32Number) TYPE_Lab_8;
1152#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1153 else
1154 source_info.type=(cmsUInt32Number) TYPE_Lab_16;
1155#endif
1156 break;
1157 }
1158 case cmsSigRgbData:
1159 {
1160 source_info.colorspace=sRGBColorspace;
1161 if (highres != MagickFalse)
1162 source_info.type=(cmsUInt32Number) TYPE_RGB_DBL;
1163#if (MAGICKCORE_QUANTUM_DEPTH == 8)
1164 else
1165 source_info.type=(cmsUInt32Number) TYPE_RGB_8;
1166#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1167 else
1168 source_info.type=(cmsUInt32Number) TYPE_RGB_16;
1169#endif
1170 break;
1171 }
1172 case cmsSigXYZData:
1173 {
1174 source_info.colorspace=XYZColorspace;
1175 if (highres != MagickFalse)
1176 source_info.type=(cmsUInt32Number) TYPE_XYZ_DBL;
1177#if (MAGICKCORE_QUANTUM_DEPTH == 8)
1178 else
1179 source_info.type=(cmsUInt32Number) TYPE_XYZ_8;
1180#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1181 else
1182 source_info.type=(cmsUInt32Number) TYPE_XYZ_16;
1183#endif
1184 break;
1185 }
1186 default:
1187 ThrowProfileException(ImageError,
1188 "ColorspaceColorProfileMismatch",name);
1189 }
1190 signature=cmsGetPCS(source_info.profile);
1191 if (target_info.profile != (cmsHPROFILE) NULL)
1192 signature=cmsGetColorSpace(target_info.profile);
1193 SetLCMSInfoScale(&target_info,1.0);
1194 SetLCMSInfoTranslate(&target_info,0.0);
1195 target_info.channels=3;
1196 switch (signature)
1197 {
1198 case cmsSigCmykData:
1199 {
1200 target_info.colorspace=CMYKColorspace;
1201 target_info.channels=4;
1202 if (highres != MagickFalse)
1203 {
1204 target_info.type=(cmsUInt32Number) TYPE_CMYK_DBL;
1205 SetLCMSInfoScale(&target_info,0.01);
1206 }
1207#if (MAGICKCORE_QUANTUM_DEPTH == 8)
1208 else
1209 target_info.type=(cmsUInt32Number) TYPE_CMYK_8;
1210#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1211 else
1212 target_info.type=(cmsUInt32Number) TYPE_CMYK_16;
1213#endif
1214 break;
1215 }
1216 case cmsSigGrayData:
1217 {
1218 target_info.colorspace=GRAYColorspace;
1219 target_info.channels=1;
1220 if (highres != MagickFalse)
1221 target_info.type=(cmsUInt32Number) TYPE_GRAY_DBL;
1222#if (MAGICKCORE_QUANTUM_DEPTH == 8)
1223 else
1224 target_info.type=(cmsUInt32Number) TYPE_GRAY_8;
1225#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1226 else
1227 target_info.type=(cmsUInt32Number) TYPE_GRAY_16;
1228#endif
1229 break;
1230 }
1231 case cmsSigLabData:
1232 {
1233 target_info.colorspace=LabColorspace;
1234 if (highres != MagickFalse)
1235 {
1236 target_info.type=(cmsUInt32Number) TYPE_Lab_DBL;
1237 target_info.scale[0]=0.01;
1238 target_info.scale[1]=1/255.0;
1239 target_info.scale[2]=1/255.0;
1240 target_info.translate[1]=0.5;
1241 target_info.translate[2]=0.5;
1242 }
1243#if (MAGICKCORE_QUANTUM_DEPTH == 8)
1244 else
1245 target_info.type=(cmsUInt32Number) TYPE_Lab_8;
1246#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1247 else
1248 target_info.type=(cmsUInt32Number) TYPE_Lab_16;
1249#endif
1250 break;
1251 }
1252 case cmsSigRgbData:
1253 {
1254 target_info.colorspace=sRGBColorspace;
1255 if (highres != MagickFalse)
1256 target_info.type=(cmsUInt32Number) TYPE_RGB_DBL;
1257#if (MAGICKCORE_QUANTUM_DEPTH == 8)
1258 else
1259 target_info.type=(cmsUInt32Number) TYPE_RGB_8;
1260#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1261 else
1262 target_info.type=(cmsUInt32Number) TYPE_RGB_16;
1263#endif
1264 break;
1265 }
1266 case cmsSigXYZData:
1267 {
1268 target_info.colorspace=XYZColorspace;
1269 if (highres != MagickFalse)
1270 target_info.type=(cmsUInt32Number) TYPE_XYZ_DBL;
1271#if (MAGICKCORE_QUANTUM_DEPTH == 8)
1272 else
1273 target_info.type=(cmsUInt32Number) TYPE_XYZ_8;
1274#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1275 else
1276 source_info.type=(cmsUInt32Number) TYPE_XYZ_16;
1277#endif
1278 break;
1279 }
1280 default:
1281 ThrowProfileException(ImageError,
1282 "ColorspaceColorProfileMismatch",name);
1283 }
1284 switch (image->rendering_intent)
1285 {
1286 case AbsoluteIntent:
1287 {
1288 target_info.intent=INTENT_ABSOLUTE_COLORIMETRIC;
1289 break;
1290 }
1291 case PerceptualIntent:
1292 {
1293 target_info.intent=INTENT_PERCEPTUAL;
1294 break;
1295 }
1296 case RelativeIntent:
1297 {
1298 target_info.intent=INTENT_RELATIVE_COLORIMETRIC;
1299 break;
1300 }
1301 case SaturationIntent:
1302 {
1303 target_info.intent=INTENT_SATURATION;
1304 break;
1305 }
1306 default:
1307 {
1308 target_info.intent=INTENT_PERCEPTUAL;
1309 break;
1310 }
1311 }
1312 flags=cmsFLAGS_HIGHRESPRECALC;
1313#if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
1314 if (image->black_point_compensation != MagickFalse)
1315 flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
1316#endif
1317 transform=AcquireTransformTLS(&source_info,&target_info,flags,
1318 cms_context);
1319 if (transform == (cmsHTRANSFORM *) NULL)
1320 ThrowProfileException(ImageError,"UnableToCreateColorTransform",
1321 name);
1322 /*
1323 Transform image as dictated by the source & target image profiles.
1324 */
1325 source_info.pixels=AcquirePixelTLS(image->columns,
1326 source_info.channels,highres);
1327 target_info.pixels=AcquirePixelTLS(image->columns,
1328 target_info.channels,highres);
1329 if ((source_info.pixels == (void **) NULL) ||
1330 (target_info.pixels == (void **) NULL))
1331 {
1332 target_info.pixels=DestroyPixelTLS(target_info.pixels);
1333 source_info.pixels=DestroyPixelTLS(source_info.pixels);
1334 transform=DestroyTransformTLS(transform);
1335 ThrowProfileException(ResourceLimitError,
1336 "MemoryAllocationFailed",image->filename);
1337 }
1338 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1339 {
1340 target_info.pixels=DestroyPixelTLS(target_info.pixels);
1341 source_info.pixels=DestroyPixelTLS(source_info.pixels);
1342 transform=DestroyTransformTLS(transform);
1343 if (source_info.profile != (cmsHPROFILE) NULL)
1344 (void) cmsCloseProfile(source_info.profile);
1345 if (target_info.profile != (cmsHPROFILE) NULL)
1346 (void) cmsCloseProfile(target_info.profile);
1347 return(MagickFalse);
1348 }
1349 if (target_info.colorspace == CMYKColorspace)
1350 (void) SetImageColorspace(image,target_info.colorspace,exception);
1351 progress=0;
1352 image_view=AcquireAuthenticCacheView(image,exception);
1353#if defined(MAGICKCORE_OPENMP_SUPPORT)
1354 #pragma omp parallel for schedule(static) shared(status) \
1355 magick_number_threads(image,image,image->rows,1)
1356#endif
1357 for (y=0; y < (ssize_t) image->rows; y++)
1358 {
1359 const int
1360 id = GetOpenMPThreadId();
1361
1362 MagickBooleanType
1363 sync;
1364
1365 Quantum
1366 *magick_restrict q;
1367
1368 if (status == MagickFalse)
1369 continue;
1370 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1371 exception);
1372 if (q == (Quantum *) NULL)
1373 {
1374 status=MagickFalse;
1375 continue;
1376 }
1377 if (highres != MagickFalse)
1378 TransformDoublePixels(id,image,&source_info,&target_info,
1379 transform,q);
1380 else
1381 TransformQuantumPixels(id,image,&source_info,&target_info,
1382 transform,q);
1383 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1384 if (sync == MagickFalse)
1385 status=MagickFalse;
1386 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1387 {
1388 MagickBooleanType
1389 proceed;
1390
1391#if defined(MAGICKCORE_OPENMP_SUPPORT)
1392 #pragma omp atomic
1393#endif
1394 progress++;
1395 proceed=SetImageProgress(image,ProfileImageTag,progress,
1396 image->rows);
1397 if (proceed == MagickFalse)
1398 status=MagickFalse;
1399 }
1400 }
1401 image_view=DestroyCacheView(image_view);
1402 (void) SetImageColorspace(image,target_info.colorspace,exception);
1403 switch (signature)
1404 {
1405 case cmsSigRgbData:
1406 {
1407 image->type=image->alpha_trait == UndefinedPixelTrait ?
1408 TrueColorType : TrueColorAlphaType;
1409 break;
1410 }
1411 case cmsSigCmykData:
1412 {
1413 image->type=image->alpha_trait == UndefinedPixelTrait ?
1414 ColorSeparationType : ColorSeparationAlphaType;
1415 break;
1416 }
1417 case cmsSigGrayData:
1418 {
1419 image->type=image->alpha_trait == UndefinedPixelTrait ?
1420 GrayscaleType : GrayscaleAlphaType;
1421 break;
1422 }
1423 default:
1424 break;
1425 }
1426 target_info.pixels=DestroyPixelTLS(target_info.pixels);
1427 source_info.pixels=DestroyPixelTLS(source_info.pixels);
1428 transform=DestroyTransformTLS(transform);
1429 if ((status != MagickFalse) &&
1430 (cmsGetDeviceClass(source_info.profile) != cmsSigLinkClass))
1431 status=SetImageProfilePrivate(image,profile,exception);
1432 if (target_info.profile != (cmsHPROFILE) NULL)
1433 (void) cmsCloseProfile(target_info.profile);
1434 }
1435 (void) cmsCloseProfile(source_info.profile);
1436 cmsDeleteContext(cms_context);
1437 }
1438#endif
1439 }
1440 return(status);
1441}
1442
1443/*
1444%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1445% %
1446% %
1447% %
1448% R e m o v e I m a g e P r o f i l e %
1449% %
1450% %
1451% %
1452%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1453%
1454% RemoveImageProfile() removes a named profile from the image and returns its
1455% value.
1456%
1457% The format of the RemoveImageProfile method is:
1458%
1459% void *RemoveImageProfile(Image *image,const char *name)
1460%
1461% A description of each parameter follows:
1462%
1463% o image: the image.
1464%
1465% o name: the profile name.
1466%
1467*/
1468MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
1469{
1471 *profile;
1472
1473 assert(image != (Image *) NULL);
1474 assert(image->signature == MagickCoreSignature);
1475 if (IsEventLogging() != MagickFalse)
1476 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1477 if (image->profiles == (SplayTreeInfo *) NULL)
1478 return((StringInfo *) NULL);
1479 WriteTo8BimProfile(image,name,(StringInfo *) NULL);
1480 profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
1481 image->profiles,name);
1482 return(profile);
1483}
1484
1485/*
1486%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1487% %
1488% %
1489% %
1490% R e s e t P r o f i l e I t e r a t o r %
1491% %
1492% %
1493% %
1494%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1495%
1496% ResetImageProfileIterator() resets the image profile iterator. Use it in
1497% conjunction with GetNextImageProfile() to iterate over all the profiles
1498% associated with an image.
1499%
1500% The format of the ResetImageProfileIterator method is:
1501%
1502% ResetImageProfileIterator(Image *image)
1503%
1504% A description of each parameter follows:
1505%
1506% o image: the image.
1507%
1508*/
1509MagickExport void ResetImageProfileIterator(const Image *image)
1510{
1511 assert(image != (Image *) NULL);
1512 assert(image->signature == MagickCoreSignature);
1513 if (IsEventLogging() != MagickFalse)
1514 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1515 if (image->profiles == (SplayTreeInfo *) NULL)
1516 return;
1517 ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
1518}
1519
1520/*
1521%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1522% %
1523% %
1524% %
1525% S e t I m a g e P r o f i l e %
1526% %
1527% %
1528% %
1529%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1530%
1531% SetImageProfile() adds a named profile to the image. If a profile with the
1532% same name already exists, it is replaced. This method differs from the
1533% ProfileImage() method in that it does not apply CMS color profiles.
1534%
1535% The format of the SetImageProfile method is:
1536%
1537% MagickBooleanType SetImageProfile(Image *image,const char *name,
1538% const StringInfo *profile)
1539%
1540% A description of each parameter follows:
1541%
1542% o image: the image.
1543%
1544% o name: the profile name, for example icc, exif, and 8bim (8bim is the
1545% Photoshop wrapper for iptc profiles).
1546%
1547% o profile: A StringInfo structure that contains the named profile.
1548%
1549*/
1550
1551static void *DestroyProfile(void *profile)
1552{
1553 return((void *) DestroyStringInfo((StringInfo *) profile));
1554}
1555
1556static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1557 unsigned char *quantum)
1558{
1559 *quantum=(*p++);
1560 return(p);
1561}
1562
1563static inline const unsigned char *ReadResourceLong(const unsigned char *p,
1564 unsigned int *quantum)
1565{
1566 *quantum=(unsigned int) (*p++) << 24;
1567 *quantum|=(unsigned int) (*p++) << 16;
1568 *quantum|=(unsigned int) (*p++) << 8;
1569 *quantum|=(unsigned int) (*p++);
1570 return(p);
1571}
1572
1573static inline const unsigned char *ReadResourceShort(const unsigned char *p,
1574 unsigned short *quantum)
1575{
1576 *quantum=(unsigned short) (*p++) << 8;
1577 *quantum|=(unsigned short) (*p++);
1578 return(p);
1579}
1580
1581static inline void WriteResourceLong(unsigned char *p,
1582 const unsigned int quantum)
1583{
1584 unsigned char
1585 buffer[4];
1586
1587 buffer[0]=(unsigned char) (quantum >> 24);
1588 buffer[1]=(unsigned char) (quantum >> 16);
1589 buffer[2]=(unsigned char) (quantum >> 8);
1590 buffer[3]=(unsigned char) quantum;
1591 (void) memcpy(p,buffer,4);
1592}
1593
1594static void WriteTo8BimProfile(Image *image,const char *name,
1595 const StringInfo *profile)
1596{
1597 const unsigned char
1598 *datum,
1599 *q;
1600
1601 const unsigned char
1602 *p;
1603
1604 size_t
1605 length;
1606
1608 *profile_8bim;
1609
1610 ssize_t
1611 count;
1612
1613 unsigned char
1614 length_byte;
1615
1616 unsigned int
1617 value;
1618
1619 unsigned short
1620 id,
1621 profile_id;
1622
1623 if (LocaleCompare(name,"icc") == 0)
1624 profile_id=0x040f;
1625 else
1626 if (LocaleCompare(name,"iptc") == 0)
1627 profile_id=0x0404;
1628 else
1629 if (LocaleCompare(name,"xmp") == 0)
1630 profile_id=0x0424;
1631 else
1632 return;
1633 profile_8bim=(StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
1634 image->profiles,"8bim");
1635 if (profile_8bim == (StringInfo *) NULL)
1636 return;
1637 datum=GetStringInfoDatum(profile_8bim);
1638 length=GetStringInfoLength(profile_8bim);
1639 for (p=datum; p < (datum+length-16); )
1640 {
1641 q=p;
1642 if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1643 break;
1644 p+=4;
1645 p=ReadResourceShort(p,&id);
1646 p=ReadResourceByte(p,&length_byte);
1647 p+=length_byte;
1648 if (((length_byte+1) & 0x01) != 0)
1649 p++;
1650 if (p > (datum+length-4))
1651 break;
1652 p=ReadResourceLong(p,&value);
1653 count=(ssize_t) value;
1654 if ((count & 0x01) != 0)
1655 count++;
1656 if ((count < 0) || (p > (datum+length-count)) || (count > (ssize_t) length))
1657 break;
1658 if (id != profile_id)
1659 p+=count;
1660 else
1661 {
1662 size_t
1663 extent,
1664 offset;
1665
1666 ssize_t
1667 extract_extent;
1668
1670 *extract_profile;
1671
1672 extract_extent=0;
1673 extent=(size_t) ((datum+length)-(p+count));
1674 if (profile == (StringInfo *) NULL)
1675 {
1676 offset=(size_t) (q-datum);
1677 extract_profile=AcquireStringInfo(offset+extent);
1678 (void) memcpy(extract_profile->datum,datum,offset);
1679 }
1680 else
1681 {
1682 offset=(size_t) (p-datum);
1683 extract_extent=(ssize_t) profile->length;
1684 if ((extract_extent & 0x01) != 0)
1685 extract_extent++;
1686 extract_profile=AcquireStringInfo(offset+(size_t) extract_extent+
1687 extent);
1688 (void) memcpy(extract_profile->datum,datum,offset-4);
1689 WriteResourceLong(extract_profile->datum+offset-4,(unsigned int)
1690 profile->length);
1691 (void) memcpy(extract_profile->datum+offset,
1692 profile->datum,profile->length);
1693 }
1694 (void) memcpy(extract_profile->datum+offset+extract_extent,
1695 p+count,extent);
1696 (void) AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1697 ConstantString("8bim"),CloneStringInfo(extract_profile));
1698 extract_profile=DestroyStringInfo(extract_profile);
1699 break;
1700 }
1701 }
1702}
1703
1704static void GetProfilesFromResourceBlock(Image *image,
1705 const StringInfo *resource_block,ExceptionInfo *exception)
1706{
1707 const unsigned char
1708 *datum;
1709
1710 const unsigned char
1711 *p;
1712
1713 size_t
1714 length;
1715
1716 ssize_t
1717 count;
1718
1720 *profile;
1721
1722 unsigned char
1723 length_byte;
1724
1725 unsigned int
1726 value;
1727
1728 unsigned short
1729 id;
1730
1731 datum=GetStringInfoDatum(resource_block);
1732 length=GetStringInfoLength(resource_block);
1733 for (p=datum; p < (datum+length-16); )
1734 {
1735 if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1736 break;
1737 p+=4;
1738 p=ReadResourceShort(p,&id);
1739 p=ReadResourceByte(p,&length_byte);
1740 p+=length_byte;
1741 if (((length_byte+1) & 0x01) != 0)
1742 p++;
1743 if (p > (datum+length-4))
1744 break;
1745 p=ReadResourceLong(p,&value);
1746 count=(ssize_t) value;
1747 if ((p > (datum+length-count)) || (count > (ssize_t) length) ||
1748 (count <= 0))
1749 break;
1750 switch (id)
1751 {
1752 case 0x03ed:
1753 {
1754 unsigned int
1755 resolution;
1756
1757 unsigned short
1758 units;
1759
1760 /*
1761 Resolution.
1762 */
1763 if (count < 10)
1764 break;
1765 p=ReadResourceLong(p,&resolution);
1766 image->resolution.x=((double) resolution)/65536.0;
1767 p=ReadResourceShort(p,&units)+2;
1768 p=ReadResourceLong(p,&resolution)+4;
1769 image->resolution.y=((double) resolution)/65536.0;
1770 /*
1771 Values are always stored as pixels per inch.
1772 */
1773 if ((ResolutionType) units != PixelsPerCentimeterResolution)
1774 image->units=PixelsPerInchResolution;
1775 else
1776 {
1777 image->units=PixelsPerCentimeterResolution;
1778 image->resolution.x/=2.54;
1779 image->resolution.y/=2.54;
1780 }
1781 break;
1782 }
1783 case 0x0404:
1784 {
1785 /*
1786 IPTC profile.
1787 */
1788 profile=BlobToProfileStringInfo("iptc",p,(size_t) count,exception);
1789 if (profile != (StringInfo *) NULL)
1790 (void) SetImageProfileInternal(image,GetStringInfoName(profile),
1791 profile,MagickTrue,exception);
1792 p+=count;
1793 break;
1794 }
1795 case 0x040c:
1796 {
1797 /*
1798 Thumbnail.
1799 */
1800 p+=count;
1801 break;
1802 }
1803 case 0x040f:
1804 {
1805 /*
1806 ICC Profile.
1807 */
1808 profile=BlobToProfileStringInfo("icc",p,(size_t) count,exception);
1809 if (profile != (StringInfo *) NULL)
1810 (void) SetImageProfileInternal(image,GetStringInfoName(profile),
1811 profile,MagickTrue,exception);
1812 p+=count;
1813 break;
1814 }
1815 case 0x0422:
1816 {
1817 /*
1818 EXIF Profile.
1819 */
1820 profile=BlobToProfileStringInfo("exif",p,(size_t) count,exception);
1821 if (profile != (StringInfo *) NULL)
1822 (void) SetImageProfileInternal(image,GetStringInfoName(profile),
1823 profile,MagickTrue,exception);
1824 p+=count;
1825 break;
1826 }
1827 case 0x0424:
1828 {
1829 /*
1830 XMP Profile.
1831 */
1832 profile=BlobToProfileStringInfo("xmp",p,(size_t) count,exception);
1833 if (profile != (StringInfo *) NULL)
1834 (void) SetImageProfileInternal(image,GetStringInfoName(profile),
1835 profile,MagickTrue,exception);
1836 p+=count;
1837 break;
1838 }
1839 default:
1840 {
1841 p+=count;
1842 break;
1843 }
1844 }
1845 if ((count & 0x01) != 0)
1846 p++;
1847 }
1848}
1849
1850static void PatchCorruptProfile(const char *name,StringInfo *profile)
1851{
1852 unsigned char
1853 *p;
1854
1855 size_t
1856 length;
1857
1858 /*
1859 Detect corrupt profiles and if discovered, repair.
1860 */
1861 if (LocaleCompare(name,"xmp") == 0)
1862 {
1863 /*
1864 Remove garbage after xpacket end.
1865 */
1866 p=GetStringInfoDatum(profile);
1867 p=(unsigned char *) strstr((const char *) p,"<?xpacket end=\"w\"?>");
1868 if (p != (unsigned char *) NULL)
1869 {
1870 p+=19;
1871 length=(size_t) (p-GetStringInfoDatum(profile));
1872 if (length != GetStringInfoLength(profile))
1873 {
1874 *p='\0';
1875 SetStringInfoLength(profile,length);
1876 }
1877 }
1878 return;
1879 }
1880 if (LocaleCompare(name,"exif") == 0)
1881 {
1882 /*
1883 Check if profile starts with byte order marker instead of Exif.
1884 */
1885 p=GetStringInfoDatum(profile);
1886 if ((LocaleNCompare((const char *) p,"MM",2) == 0) ||
1887 (LocaleNCompare((const char *) p,"II",2) == 0))
1888 {
1889 const unsigned char
1890 profile_start[] = "Exif\0\0";
1891
1893 *exif_profile;
1894
1895 exif_profile=AcquireStringInfo(6);
1896 if (exif_profile != (StringInfo *) NULL)
1897 {
1898 SetStringInfoDatum(exif_profile,profile_start);
1899 ConcatenateStringInfo(exif_profile,profile);
1900 SetStringInfoLength(profile,GetStringInfoLength(exif_profile));
1901 SetStringInfo(profile,exif_profile);
1902 exif_profile=DestroyStringInfo(exif_profile);
1903 }
1904 }
1905 }
1906}
1907
1908static MagickBooleanType ValidateXMPProfile(Image *image,
1909 const StringInfo *profile,ExceptionInfo *exception)
1910{
1911#if defined(MAGICKCORE_XML_DELEGATE)
1912 xmlDocPtr
1913 document;
1914
1915 /*
1916 Validate XMP profile.
1917 */
1918 const char *artifact=GetImageArtifact(image,"xmp:validate");
1919 if (IsStringTrue(artifact) == MagickFalse)
1920 return(MagickTrue);
1921 document=xmlReadMemory((const char *) GetStringInfoDatum(profile),(int)
1922 GetStringInfoLength(profile),"xmp.xml",NULL,XML_PARSE_NOERROR |
1923 XML_PARSE_NOWARNING);
1924 if (document == (xmlDocPtr) NULL)
1925 {
1926 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1927 "CorruptImageProfile","`%s' (XMP)",image->filename);
1928 return(MagickFalse);
1929 }
1930 xmlFreeDoc(document);
1931 return(MagickTrue);
1932#else
1933 (void) profile;
1934 (void) ThrowMagickException(exception,GetMagickModule(),
1935 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (XML)",
1936 image->filename);
1937 return(MagickFalse);
1938#endif
1939}
1940
1941static MagickBooleanType SetImageProfileInternal(Image *image,const char *name,
1942 StringInfo *profile,const MagickBooleanType recursive,
1943 ExceptionInfo *exception)
1944{
1945 char
1946 key[MagickPathExtent];
1947
1948 MagickBooleanType
1949 status;
1950
1951 size_t
1952 length;
1953
1954 assert(image != (Image *) NULL);
1955 assert(image->signature == MagickCoreSignature);
1956 assert(profile != (StringInfo *) NULL);
1957 assert(name != (const char *) NULL);
1958 if (IsEventLogging() != MagickFalse)
1959 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1960 length=GetStringInfoLength(profile);
1961 if ((length == 0) || (length > GetMaxProfileSize()))
1962 {
1963 if (length != 0)
1964 (void) ThrowMagickException(exception,GetMagickModule(),
1965 ResourceLimitWarning,"ProfileSizeExceedsLimit","`%llu'",
1966 (unsigned long long) length);
1967 profile=DestroyStringInfo(profile);
1968 return(MagickTrue);
1969 }
1970 PatchCorruptProfile(name,profile);
1971 if ((LocaleCompare(name,"xmp") == 0) &&
1972 (ValidateXMPProfile(image,profile,exception) == MagickFalse))
1973 {
1974 profile=DestroyStringInfo(profile);
1975 return(MagickTrue);
1976 }
1977 if (image->profiles == (SplayTreeInfo *) NULL)
1978 image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
1979 DestroyProfile);
1980 (void) CopyMagickString(key,name,MagickPathExtent);
1981 LocaleLower(key);
1982 status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1983 ConstantString(key),profile);
1984 if (status == MagickFalse)
1985 profile=DestroyStringInfo(profile);
1986 else
1987 {
1988 if (LocaleCompare(name,"8bim") == 0)
1989 GetProfilesFromResourceBlock(image,profile,exception);
1990 else
1991 if (recursive == MagickFalse)
1992 WriteTo8BimProfile(image,name,profile);
1993 }
1994 return(status);
1995}
1996
1997MagickExport StringInfo *AcquireProfileStringInfo(const char *name,
1998 const size_t length,
1999 ExceptionInfo *exception)
2000{
2002 *profile = (StringInfo *) NULL;
2003
2004 if (length > GetMaxProfileSize())
2005 (void) ThrowMagickException(exception,GetMagickModule(),
2006 ResourceLimitWarning,"ProfileSizeExceedsLimit","`%llu'",
2007 (unsigned long long) length);
2008 else
2009 {
2010 profile=AcquireStringInfo(length);
2011 SetStringInfoName(profile,name);
2012 }
2013 return(profile);
2014}
2015
2016MagickExport StringInfo *BlobToProfileStringInfo(const char *name,
2017 const void *blob,const size_t length,ExceptionInfo *exception)
2018{
2020 *profile;
2021
2022 profile=AcquireProfileStringInfo(name,length,exception);
2023 if (profile != (const StringInfo *) NULL)
2024 (void) memcpy(profile->datum,blob,length);
2025 return(profile);
2026}
2027
2028MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
2029 const StringInfo *profile,ExceptionInfo *exception)
2030{
2032 *clone_profile;
2033
2034 if (profile == (const StringInfo *) NULL)
2035 return(MagickFalse);
2036 clone_profile=CloneStringInfo(profile);
2037 return(SetImageProfileInternal(image,name,clone_profile,MagickFalse,
2038 exception));
2039}
2040
2041MagickExport MagickBooleanType SetImageProfilePrivate(Image *image,
2042 StringInfo *profile,ExceptionInfo *exception)
2043{
2044 if (profile == (const StringInfo *) NULL)
2045 return(MagickFalse);
2046 return(SetImageProfileInternal(image,GetStringInfoName(profile),profile,
2047 MagickFalse,exception));
2048}
2049
2050/*
2051%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2052% %
2053% %
2054% %
2055% S y n c I m a g e P r o f i l e s %
2056% %
2057% %
2058% %
2059%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2060%
2061% SyncImageProfiles() synchronizes image properties with the image profiles.
2062%
2063% The format of the SyncImageProfiles method is:
2064%
2065% void SyncImageProfiles(Image *image)
2066%
2067% A description of each parameter follows:
2068%
2069% o image: the image.
2070%
2071*/
2072
2073static inline int ReadProfileByte(unsigned char **p,size_t *length)
2074{
2075 int
2076 c;
2077
2078 if (*length < 1)
2079 return(EOF);
2080 c=(int) (*(*p)++);
2081 (*length)--;
2082 return(c);
2083}
2084
2085static inline signed short ReadProfileShort(const EndianType endian,
2086 unsigned char *buffer)
2087{
2088 union
2089 {
2090 unsigned int
2091 unsigned_value;
2092
2093 signed int
2094 signed_value;
2095 } quantum;
2096
2097 unsigned short
2098 value;
2099
2100 if (endian == LSBEndian)
2101 {
2102 value=(unsigned short) buffer[1] << 8;
2103 value|=(unsigned short) buffer[0];
2104 quantum.unsigned_value=value & 0xffff;
2105 return(quantum.signed_value);
2106 }
2107 value=(unsigned short) buffer[0] << 8;
2108 value|=(unsigned short) buffer[1];
2109 quantum.unsigned_value=value & 0xffff;
2110 return(quantum.signed_value);
2111}
2112
2113static inline signed int ReadProfileLong(const EndianType endian,
2114 unsigned char *buffer)
2115{
2116 union
2117 {
2118 unsigned int
2119 unsigned_value;
2120
2121 signed int
2122 signed_value;
2123 } quantum;
2124
2125 unsigned int
2126 value;
2127
2128 if (endian == LSBEndian)
2129 {
2130 value=(unsigned int) buffer[3] << 24;
2131 value|=(unsigned int) buffer[2] << 16;
2132 value|=(unsigned int) buffer[1] << 8;
2133 value|=(unsigned int) buffer[0];
2134 quantum.unsigned_value=value & 0xffffffff;
2135 return(quantum.signed_value);
2136 }
2137 value=(unsigned int) buffer[0] << 24;
2138 value|=(unsigned int) buffer[1] << 16;
2139 value|=(unsigned int) buffer[2] << 8;
2140 value|=(unsigned int) buffer[3];
2141 quantum.unsigned_value=value & 0xffffffff;
2142 return(quantum.signed_value);
2143}
2144
2145static inline signed int ReadProfileMSBLong(unsigned char **p,size_t *length)
2146{
2147 signed int
2148 value;
2149
2150 if (*length < 4)
2151 return(0);
2152 value=ReadProfileLong(MSBEndian,*p);
2153 (*length)-=4;
2154 *p+=4;
2155 return(value);
2156}
2157
2158static inline signed short ReadProfileMSBShort(unsigned char **p,
2159 size_t *length)
2160{
2161 signed short
2162 value;
2163
2164 if (*length < 2)
2165 return(0);
2166 value=ReadProfileShort(MSBEndian,*p);
2167 (*length)-=2;
2168 *p+=2;
2169 return(value);
2170}
2171
2172static inline void WriteProfileLong(const EndianType endian,
2173 const size_t value,unsigned char *p)
2174{
2175 unsigned char
2176 buffer[4];
2177
2178 if (endian == LSBEndian)
2179 {
2180 buffer[0]=(unsigned char) value;
2181 buffer[1]=(unsigned char) (value >> 8);
2182 buffer[2]=(unsigned char) (value >> 16);
2183 buffer[3]=(unsigned char) (value >> 24);
2184 (void) memcpy(p,buffer,4);
2185 return;
2186 }
2187 buffer[0]=(unsigned char) (value >> 24);
2188 buffer[1]=(unsigned char) (value >> 16);
2189 buffer[2]=(unsigned char) (value >> 8);
2190 buffer[3]=(unsigned char) value;
2191 (void) memcpy(p,buffer,4);
2192}
2193
2194static void WriteProfileShort(const EndianType endian,
2195 const unsigned short value,unsigned char *p)
2196{
2197 unsigned char
2198 buffer[2];
2199
2200 if (endian == LSBEndian)
2201 {
2202 buffer[0]=(unsigned char) value;
2203 buffer[1]=(unsigned char) (value >> 8);
2204 (void) memcpy(p,buffer,2);
2205 return;
2206 }
2207 buffer[0]=(unsigned char) (value >> 8);
2208 buffer[1]=(unsigned char) value;
2209 (void) memcpy(p,buffer,2);
2210}
2211
2212static void SyncExifProfile(const Image *image,unsigned char *exif,
2213 size_t length)
2214{
2215#define MaxDirectoryStack 16
2216#define EXIF_DELIMITER "\n"
2217#define EXIF_NUM_FORMATS 12
2218#define TAG_EXIF_OFFSET 0x8769
2219#define TAG_INTEROP_OFFSET 0xa005
2220
2221 typedef struct _DirectoryInfo
2222 {
2223 unsigned char
2224 *directory;
2225
2226 size_t
2227 entry;
2228 } DirectoryInfo;
2229
2230 DirectoryInfo
2231 directory_stack[MaxDirectoryStack] = { { 0, 0 } };
2232
2233 EndianType
2234 endian;
2235
2236 size_t
2237 entry,
2238 number_entries;
2239
2241 *exif_resources;
2242
2243 ssize_t
2244 id,
2245 level,
2246 offset;
2247
2248 static int
2249 format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
2250
2251 unsigned char
2252 *directory;
2253
2254 if (length < 16)
2255 return;
2256 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
2257 if ((id != 0x4949) && (id != 0x4D4D))
2258 {
2259 while (length != 0)
2260 {
2261 if (ReadProfileByte(&exif,&length) != 0x45)
2262 continue;
2263 if (ReadProfileByte(&exif,&length) != 0x78)
2264 continue;
2265 if (ReadProfileByte(&exif,&length) != 0x69)
2266 continue;
2267 if (ReadProfileByte(&exif,&length) != 0x66)
2268 continue;
2269 if (ReadProfileByte(&exif,&length) != 0x00)
2270 continue;
2271 if (ReadProfileByte(&exif,&length) != 0x00)
2272 continue;
2273 break;
2274 }
2275 if (length < 16)
2276 return;
2277 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
2278 }
2279 endian=LSBEndian;
2280 if (id == 0x4949)
2281 endian=LSBEndian;
2282 else
2283 if (id == 0x4D4D)
2284 endian=MSBEndian;
2285 else
2286 return;
2287 if (ReadProfileShort(endian,exif+2) != 0x002a)
2288 return;
2289 /*
2290 This the offset to the first IFD.
2291 */
2292 offset=(ssize_t) ReadProfileLong(endian,exif+4);
2293 if ((offset < 0) || ((size_t) offset >= length))
2294 return;
2295 directory=exif+offset;
2296 level=0;
2297 entry=0;
2298 exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
2299 (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
2300 do
2301 {
2302 if (level > 0)
2303 {
2304 level--;
2305 directory=directory_stack[level].directory;
2306 entry=directory_stack[level].entry;
2307 }
2308 if ((directory < exif) || (directory > (exif+length-2)))
2309 break;
2310 /*
2311 Determine how many entries there are in the current IFD.
2312 */
2313 number_entries=(size_t) ReadProfileShort(endian,directory);
2314 for ( ; entry < number_entries; entry++)
2315 {
2316 int
2317 components;
2318
2319 unsigned char
2320 *p,
2321 *q;
2322
2323 size_t
2324 number_bytes;
2325
2326 ssize_t
2327 format,
2328 tag_value;
2329
2330 q=(unsigned char *) (directory+2+(12*entry));
2331 if (q > (exif+length-12))
2332 break; /* corrupt EXIF */
2333 if (GetValueFromSplayTree(exif_resources,q) == q)
2334 break;
2335 (void) AddValueToSplayTree(exif_resources,q,q);
2336 tag_value=(ssize_t) ReadProfileShort(endian,q);
2337 format=(ssize_t) ReadProfileShort(endian,q+2);
2338 if ((format < 0) || ((format-1) >= EXIF_NUM_FORMATS))
2339 break;
2340 components=(int) ReadProfileLong(endian,q+4);
2341 if (components < 0)
2342 break; /* corrupt EXIF */
2343 number_bytes=(size_t) components*(size_t) format_bytes[format];
2344 if ((ssize_t) number_bytes < components)
2345 break; /* prevent overflow */
2346 if (number_bytes <= 4)
2347 p=q+8;
2348 else
2349 {
2350 /*
2351 The directory entry contains an offset.
2352 */
2353 offset=(ssize_t) ReadProfileLong(endian,q+8);
2354 if ((offset < 0) ||
2355 ((size_t) (offset+(ssize_t) number_bytes) > length))
2356 continue;
2357 if (~length < number_bytes)
2358 continue; /* prevent overflow */
2359 p=(unsigned char *) (exif+offset);
2360 }
2361 switch (tag_value)
2362 {
2363 case 0x011a:
2364 {
2365 (void) WriteProfileLong(endian,(size_t) (image->resolution.x+0.5),p);
2366 if (number_bytes == 8)
2367 (void) WriteProfileLong(endian,1UL,p+4);
2368 break;
2369 }
2370 case 0x011b:
2371 {
2372 (void) WriteProfileLong(endian,(size_t) (image->resolution.y+0.5),p);
2373 if (number_bytes == 8)
2374 (void) WriteProfileLong(endian,1UL,p+4);
2375 break;
2376 }
2377 case 0x0112:
2378 {
2379 if (number_bytes == 4)
2380 {
2381 (void) WriteProfileLong(endian,(size_t) image->orientation,p);
2382 break;
2383 }
2384 (void) WriteProfileShort(endian,(unsigned short) image->orientation,
2385 p);
2386 break;
2387 }
2388 case 0x0128:
2389 {
2390 if (number_bytes == 4)
2391 {
2392 (void) WriteProfileLong(endian,((size_t) image->units)+1,p);
2393 break;
2394 }
2395 (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
2396 break;
2397 }
2398 default:
2399 break;
2400 }
2401 if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
2402 {
2403 offset=(ssize_t) ReadProfileLong(endian,p);
2404 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
2405 {
2406 directory_stack[level].directory=directory;
2407 entry++;
2408 directory_stack[level].entry=entry;
2409 level++;
2410 directory_stack[level].directory=exif+offset;
2411 directory_stack[level].entry=0;
2412 level++;
2413 if ((directory+2+(12*number_entries)) > (exif+length))
2414 break;
2415 offset=(ssize_t) ReadProfileLong(endian,directory+2+(12*
2416 number_entries));
2417 if ((offset != 0) && ((size_t) offset < length) &&
2418 (level < (MaxDirectoryStack-2)))
2419 {
2420 directory_stack[level].directory=exif+offset;
2421 directory_stack[level].entry=0;
2422 level++;
2423 }
2424 }
2425 break;
2426 }
2427 }
2428 } while (level > 0);
2429 exif_resources=DestroySplayTree(exif_resources);
2430 return;
2431}
2432
2433static void Sync8BimProfile(const Image *image,const StringInfo *profile)
2434{
2435 size_t
2436 length;
2437
2438 ssize_t
2439 count;
2440
2441 unsigned char
2442 *p;
2443
2444 unsigned short
2445 id;
2446
2447 length=GetStringInfoLength(profile);
2448 p=GetStringInfoDatum(profile);
2449 while (length != 0)
2450 {
2451 if (ReadProfileByte(&p,&length) != 0x38)
2452 continue;
2453 if (ReadProfileByte(&p,&length) != 0x42)
2454 continue;
2455 if (ReadProfileByte(&p,&length) != 0x49)
2456 continue;
2457 if (ReadProfileByte(&p,&length) != 0x4D)
2458 continue;
2459 if (length < 7)
2460 return;
2461 id=(unsigned short) ReadProfileMSBShort(&p,&length);
2462 count=(ssize_t) ReadProfileByte(&p,&length);
2463 if ((count >= (ssize_t) length) || (count < 0))
2464 return;
2465 p+=count;
2466 length-=(size_t) count;
2467 if ((*p & 0x01) == 0)
2468 (void) ReadProfileByte(&p,&length);
2469 count=(ssize_t) ReadProfileMSBLong(&p,&length);
2470 if ((count > (ssize_t) length) || (count < 0))
2471 return;
2472 if ((id == 0x3ED) && (count == 16))
2473 {
2474 if (image->units == PixelsPerCentimeterResolution)
2475 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2476 image->resolution.x*2.54*65536.0),p);
2477 else
2478 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2479 image->resolution.x*65536.0),p);
2480 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+4);
2481 if (image->units == PixelsPerCentimeterResolution)
2482 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2483 image->resolution.y*2.54*65536.0),p+8);
2484 else
2485 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2486 image->resolution.y*65536.0),p+8);
2487 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+12);
2488 }
2489 if (id == 0x0422)
2490 SyncExifProfile(image,p,(size_t) count);
2491 p+=count;
2492 length-=(size_t) count;
2493 }
2494 return;
2495}
2496
2497static void ReplaceXmpValue(StringInfo *profile,size_t start,size_t end,
2498 const char *value)
2499{
2500 char
2501 *datum;
2502
2503 size_t
2504 length,
2505 new_length,
2506 value_length;
2507
2508 length=GetStringInfoLength(profile);
2509 value_length=strlen(value);
2510 new_length=length-(end-start)+value_length;
2511 if (new_length > length)
2512 SetStringInfoLength(profile,new_length);
2513 datum=(char *) GetStringInfoDatum(profile);
2514 (void) memmove(datum+start+value_length,datum+end,length-end);
2515 (void) memcpy(datum+start,value,value_length);
2516 if (new_length < length)
2517 {
2518 SetStringInfoLength(profile,new_length);
2519 datum=(char *) GetStringInfoDatum(profile);
2520 *(datum+new_length)='\0';
2521 }
2522}
2523
2524static MagickBooleanType GetXmpOffsets(const StringInfo *profile,
2525 const char *tag,size_t *start,size_t *end)
2526{
2527 char
2528 *datum,
2529 *pos;
2530
2531 size_t
2532 length,
2533 tag_length;
2534
2535 datum=(char *) GetStringInfoDatum(profile);
2536 length=GetStringInfoLength(profile);
2537 pos=strstr(datum,tag);
2538 tag_length=strlen(tag);
2539 if ((pos == (char *) NULL) || ((pos-datum) < 1) || (*(pos-1) != '<') ||
2540 (((size_t) (pos-datum)+tag_length) > length) ||
2541 (*(pos+tag_length) != '>'))
2542 return(MagickFalse);
2543 *start=(size_t) (pos-datum)+tag_length+1;
2544 pos=strstr(datum+*start,"<");
2545 if (pos == (char *) NULL)
2546 return(MagickFalse);
2547 *end=(size_t) (pos-datum);
2548 return(MagickTrue);
2549}
2550
2551static void GetXmpNumeratorAndDenominator(double value,
2552 unsigned long *numerator,unsigned long *denominator)
2553{
2554 double
2555 df;
2556
2557 *numerator=0;
2558 *denominator=1;
2559 if (value <= MagickEpsilon)
2560 return;
2561 *numerator=1;
2562 df=1.0;
2563 while(fabs(df - value) > MagickEpsilon)
2564 {
2565 if (df < value)
2566 (*numerator)++;
2567 else
2568 {
2569 (*denominator)++;
2570 *numerator=(unsigned long) (value*(*denominator));
2571 }
2572 df=*numerator/(double)*denominator;
2573 }
2574}
2575
2576static void SyncXmpProfile(const Image *image,StringInfo *profile)
2577{
2578 char
2579 value[MagickPathExtent];
2580
2581 size_t
2582 end,
2583 start;
2584
2585 unsigned long
2586 denominator,
2587 numerator;
2588
2589 *value='\0';
2590 if (GetXmpOffsets(profile,"tiff:XResolution",&start,&end) != MagickFalse)
2591 {
2592 GetXmpNumeratorAndDenominator(image->resolution.x,&numerator,
2593 &denominator);
2594 (void) FormatLocaleString(value,MagickPathExtent,"%lu/%lu",numerator,
2595 denominator);
2596 ReplaceXmpValue(profile,start,end,value);
2597 }
2598 if (GetXmpOffsets(profile,"tiff:YResolution",&start,&end) != MagickFalse)
2599 {
2600 if ((fabs(image->resolution.x-image->resolution.y) > MagickEpsilon) ||
2601 (*value == '\0'))
2602 {
2603 GetXmpNumeratorAndDenominator(image->resolution.y,&numerator,
2604 &denominator);
2605 (void) FormatLocaleString(value,MagickPathExtent,"%lu/%lu",
2606 numerator,denominator);
2607 }
2608 ReplaceXmpValue(profile,start,end,value);
2609 }
2610 if (GetXmpOffsets(profile,"tiff:ResolutionUnit",&start,&end) != MagickFalse)
2611 {
2612 (void) FormatLocaleString(value,MagickPathExtent,"%d",
2613 ((int) image->units)+1);
2614 ReplaceXmpValue(profile,start,end,value);
2615 }
2616 if (GetXmpOffsets(profile,"tiff:Orientation",&start,&end) != MagickFalse)
2617 {
2618 (void) FormatLocaleString(value,MagickPathExtent,"%d",
2619 (int) image->orientation);
2620 ReplaceXmpValue(profile,start,end,value);
2621 }
2622}
2623
2624MagickPrivate void SyncImageProfiles(Image *image)
2625{
2627 *profile;
2628
2629 profile=(StringInfo *) GetImageProfile(image,"8BIM");
2630 if (profile != (StringInfo *) NULL)
2631 Sync8BimProfile(image,profile);
2632 profile=(StringInfo *) GetImageProfile(image,"EXIF");
2633 if (profile != (StringInfo *) NULL)
2634 SyncExifProfile(image,GetStringInfoDatum(profile),GetStringInfoLength(
2635 profile));
2636 profile=(StringInfo *) GetImageProfile(image,"XMP");
2637 if (profile != (StringInfo *) NULL)
2638 SyncXmpProfile(image,profile);
2639}
2640
2641static void UpdateClipPath(unsigned char *blob,size_t length,
2642 const size_t old_columns,const size_t old_rows,
2643 const RectangleInfo *new_geometry)
2644{
2645 ssize_t
2646 i,
2647 knot_count,
2648 selector;
2649
2650 knot_count=0;
2651 while (length != 0)
2652 {
2653 selector=(ssize_t) ReadProfileMSBShort(&blob,&length);
2654 switch (selector)
2655 {
2656 case 0:
2657 case 3:
2658 {
2659 if (knot_count != 0)
2660 {
2661 blob+=24;
2662 length-=(size_t) MagickMin(length,24U);
2663 break;
2664 }
2665 /*
2666 Expected subpath length record.
2667 */
2668 knot_count=(ssize_t) ReadProfileMSBShort(&blob,&length);
2669 blob+=22;
2670 length-=(size_t) MagickMin(length,22);
2671 break;
2672 }
2673 case 1:
2674 case 2:
2675 case 4:
2676 case 5:
2677 {
2678 if (knot_count == 0)
2679 {
2680 /*
2681 Unexpected subpath knot.
2682 */
2683 blob+=24;
2684 length-=(size_t) MagickMin(length,24);
2685 break;
2686 }
2687 /*
2688 Add sub-path knot
2689 */
2690 for (i=0; i < 3; i++)
2691 {
2692 double
2693 x,
2694 y;
2695
2696 signed int
2697 xx,
2698 yy;
2699
2700 y=(double) ReadProfileMSBLong(&blob,&length);
2701 y=y*old_rows/4096.0/4096.0;
2702 y-=new_geometry->y;
2703 yy=(signed int) ((y*4096*4096)/new_geometry->height);
2704 WriteProfileLong(MSBEndian,(size_t) yy,blob-4);
2705 x=(double) ReadProfileMSBLong(&blob,&length);
2706 x=x*old_columns/4096.0/4096.0;
2707 x-=new_geometry->x;
2708 xx=(signed int) ((x*4096*4096)/new_geometry->width);
2709 WriteProfileLong(MSBEndian,(size_t) xx,blob-4);
2710 }
2711 knot_count--;
2712 break;
2713 }
2714 case 6:
2715 case 7:
2716 case 8:
2717 default:
2718 {
2719 blob+=24;
2720 length-=(size_t) MagickMin(length,24);
2721 break;
2722 }
2723 }
2724 }
2725}
2726
2727MagickPrivate void Update8BIMClipPath(const Image *image,
2728 const size_t old_columns,const size_t old_rows,
2729 const RectangleInfo *new_geometry)
2730{
2731 const StringInfo
2732 *profile;
2733
2734 size_t
2735 length;
2736
2737 ssize_t
2738 count,
2739 id;
2740
2741 unsigned char
2742 *info;
2743
2744 assert(image != (Image *) NULL);
2745 assert(new_geometry != (RectangleInfo *) NULL);
2746 profile=GetImageProfile(image,"8bim");
2747 if (profile == (StringInfo *) NULL)
2748 return;
2749 length=GetStringInfoLength(profile);
2750 info=GetStringInfoDatum(profile);
2751 while (length > 0)
2752 {
2753 if (ReadProfileByte(&info,&length) != (unsigned char) '8')
2754 continue;
2755 if (ReadProfileByte(&info,&length) != (unsigned char) 'B')
2756 continue;
2757 if (ReadProfileByte(&info,&length) != (unsigned char) 'I')
2758 continue;
2759 if (ReadProfileByte(&info,&length) != (unsigned char) 'M')
2760 continue;
2761 id=(ssize_t) ReadProfileMSBShort(&info,&length);
2762 count=(ssize_t) ReadProfileByte(&info,&length);
2763 if ((count != 0) && ((size_t) count <= length))
2764 {
2765 info+=count;
2766 length-=(size_t) count;
2767 }
2768 if ((count & 0x01) == 0)
2769 (void) ReadProfileByte(&info,&length);
2770 count=(ssize_t) ReadProfileMSBLong(&info,&length);
2771 if ((count < 0) || ((size_t) count > length))
2772 {
2773 length=0;
2774 continue;
2775 }
2776 if ((id > 1999) && (id < 2999))
2777 UpdateClipPath(info,(size_t) count,old_columns,old_rows,new_geometry);
2778 info+=count;
2779 length-=(size_t) MagickMin(length,(size_t) count);
2780 }
2781}