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 *,const 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 MagickBooleanType SetsRGBImageProfile(Image *image,
637 ExceptionInfo *exception)
638{
639 static unsigned char
640 sRGBProfile[] =
641 {
642 0x00, 0x00, 0x0c, 0x8c, 0x61, 0x72, 0x67, 0x6c, 0x02, 0x20, 0x00, 0x00,
643 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20,
644 0x07, 0xde, 0x00, 0x01, 0x00, 0x06, 0x00, 0x16, 0x00, 0x0f, 0x00, 0x3a,
645 0x61, 0x63, 0x73, 0x70, 0x4d, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00,
646 0x49, 0x45, 0x43, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00,
647 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6,
648 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x61, 0x72, 0x67, 0x6c,
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, 0x00,
652 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
653 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x99,
654 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0xec, 0x00, 0x00, 0x00, 0x67,
655 0x64, 0x6d, 0x6e, 0x64, 0x00, 0x00, 0x02, 0x54, 0x00, 0x00, 0x00, 0x70,
656 0x64, 0x6d, 0x64, 0x64, 0x00, 0x00, 0x02, 0xc4, 0x00, 0x00, 0x00, 0x88,
657 0x74, 0x65, 0x63, 0x68, 0x00, 0x00, 0x03, 0x4c, 0x00, 0x00, 0x00, 0x0c,
658 0x76, 0x75, 0x65, 0x64, 0x00, 0x00, 0x03, 0x58, 0x00, 0x00, 0x00, 0x67,
659 0x76, 0x69, 0x65, 0x77, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x24,
660 0x6c, 0x75, 0x6d, 0x69, 0x00, 0x00, 0x03, 0xe4, 0x00, 0x00, 0x00, 0x14,
661 0x6d, 0x65, 0x61, 0x73, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x24,
662 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x00, 0x00, 0x14,
663 0x62, 0x6b, 0x70, 0x74, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x14,
664 0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x44, 0x00, 0x00, 0x00, 0x14,
665 0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x58, 0x00, 0x00, 0x00, 0x14,
666 0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x6c, 0x00, 0x00, 0x00, 0x14,
667 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
668 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
669 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
670 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
671 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36,
672 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x28, 0x45, 0x71, 0x75, 0x69, 0x76,
673 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x77, 0x77,
674 0x2e, 0x73, 0x72, 0x67, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x31, 0x39,
675 0x39, 0x38, 0x20, 0x48, 0x50, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c,
676 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
677 0x00, 0x3f, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31,
678 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x28, 0x45, 0x71, 0x75,
679 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77,
680 0x77, 0x77, 0x2e, 0x73, 0x72, 0x67, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x20,
681 0x31, 0x39, 0x39, 0x38, 0x20, 0x48, 0x50, 0x20, 0x70, 0x72, 0x6f, 0x66,
682 0x69, 0x6c, 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
683 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x72, 0x65, 0x61,
684 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x47, 0x72, 0x61, 0x65, 0x6d,
685 0x65, 0x20, 0x57, 0x2e, 0x20, 0x47, 0x69, 0x6c, 0x6c, 0x2e, 0x20, 0x52,
686 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f,
687 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
688 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x20, 0x4e, 0x6f, 0x20, 0x57,
689 0x61, 0x72, 0x72, 0x61, 0x6e, 0x74, 0x79, 0x2c, 0x20, 0x55, 0x73, 0x65,
690 0x20, 0x61, 0x74, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x6f, 0x77, 0x6e,
691 0x20, 0x72, 0x69, 0x73, 0x6b, 0x2e, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63,
692 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20,
693 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69,
694 0x65, 0x63, 0x2e, 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
695 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20, 0x68, 0x74, 0x74,
696 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x65, 0x63, 0x2e,
697 0x63, 0x68, 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 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
701 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e,
702 0x49, 0x45, 0x43, 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e,
703 0x31, 0x20, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47,
704 0x42, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61,
705 0x63, 0x65, 0x20, 0x2d, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00,
706 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x49, 0x45, 0x43,
707 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x44,
708 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47, 0x42, 0x20, 0x63,
709 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20,
710 0x2d, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
711 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
712 0x00, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x20, 0x00, 0x00, 0x00, 0x00,
713 0x43, 0x52, 0x54, 0x20, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00,
714 0x00, 0x00, 0x00, 0x0d, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36,
715 0x2d, 0x32, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
716 0x00, 0x00, 0x00, 0x0d, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36,
717 0x2d, 0x32, 0x2e, 0x31, 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 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
722 0x76, 0x69, 0x65, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xa4, 0x7c,
723 0x00, 0x14, 0x5f, 0x30, 0x00, 0x10, 0xce, 0x02, 0x00, 0x03, 0xed, 0xb2,
724 0x00, 0x04, 0x13, 0x0a, 0x00, 0x03, 0x5c, 0x67, 0x00, 0x00, 0x00, 0x01,
725 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x0a, 0x3d,
726 0x00, 0x50, 0x00, 0x00, 0x00, 0x57, 0x1e, 0xb8, 0x6d, 0x65, 0x61, 0x73,
727 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
728 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
729 0x00, 0x00, 0x02, 0x8f, 0x00, 0x00, 0x00, 0x02, 0x58, 0x59, 0x5a, 0x20,
730 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0x51, 0x00, 0x01, 0x00, 0x00,
731 0x00, 0x01, 0x16, 0xcc, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
732 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
733 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xa0,
734 0x00, 0x00, 0x38, 0xf5, 0x00, 0x00, 0x03, 0x90, 0x58, 0x59, 0x5a, 0x20,
735 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x97, 0x00, 0x00, 0xb7, 0x87,
736 0x00, 0x00, 0x18, 0xd9, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
737 0x00, 0x00, 0x24, 0x9f, 0x00, 0x00, 0x0f, 0x84, 0x00, 0x00, 0xb6, 0xc4,
738 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
739 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x14, 0x00, 0x19,
740 0x00, 0x1e, 0x00, 0x23, 0x00, 0x28, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x37,
741 0x00, 0x3b, 0x00, 0x40, 0x00, 0x45, 0x00, 0x4a, 0x00, 0x4f, 0x00, 0x54,
742 0x00, 0x59, 0x00, 0x5e, 0x00, 0x63, 0x00, 0x68, 0x00, 0x6d, 0x00, 0x72,
743 0x00, 0x77, 0x00, 0x7c, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8b, 0x00, 0x90,
744 0x00, 0x95, 0x00, 0x9a, 0x00, 0x9f, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xae,
745 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc1, 0x00, 0xc6, 0x00, 0xcb,
746 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xdb, 0x00, 0xe0, 0x00, 0xe5, 0x00, 0xeb,
747 0x00, 0xf0, 0x00, 0xf6, 0x00, 0xfb, 0x01, 0x01, 0x01, 0x07, 0x01, 0x0d,
748 0x01, 0x13, 0x01, 0x19, 0x01, 0x1f, 0x01, 0x25, 0x01, 0x2b, 0x01, 0x32,
749 0x01, 0x38, 0x01, 0x3e, 0x01, 0x45, 0x01, 0x4c, 0x01, 0x52, 0x01, 0x59,
750 0x01, 0x60, 0x01, 0x67, 0x01, 0x6e, 0x01, 0x75, 0x01, 0x7c, 0x01, 0x83,
751 0x01, 0x8b, 0x01, 0x92, 0x01, 0x9a, 0x01, 0xa1, 0x01, 0xa9, 0x01, 0xb1,
752 0x01, 0xb9, 0x01, 0xc1, 0x01, 0xc9, 0x01, 0xd1, 0x01, 0xd9, 0x01, 0xe1,
753 0x01, 0xe9, 0x01, 0xf2, 0x01, 0xfa, 0x02, 0x03, 0x02, 0x0c, 0x02, 0x14,
754 0x02, 0x1d, 0x02, 0x26, 0x02, 0x2f, 0x02, 0x38, 0x02, 0x41, 0x02, 0x4b,
755 0x02, 0x54, 0x02, 0x5d, 0x02, 0x67, 0x02, 0x71, 0x02, 0x7a, 0x02, 0x84,
756 0x02, 0x8e, 0x02, 0x98, 0x02, 0xa2, 0x02, 0xac, 0x02, 0xb6, 0x02, 0xc1,
757 0x02, 0xcb, 0x02, 0xd5, 0x02, 0xe0, 0x02, 0xeb, 0x02, 0xf5, 0x03, 0x00,
758 0x03, 0x0b, 0x03, 0x16, 0x03, 0x21, 0x03, 0x2d, 0x03, 0x38, 0x03, 0x43,
759 0x03, 0x4f, 0x03, 0x5a, 0x03, 0x66, 0x03, 0x72, 0x03, 0x7e, 0x03, 0x8a,
760 0x03, 0x96, 0x03, 0xa2, 0x03, 0xae, 0x03, 0xba, 0x03, 0xc7, 0x03, 0xd3,
761 0x03, 0xe0, 0x03, 0xec, 0x03, 0xf9, 0x04, 0x06, 0x04, 0x13, 0x04, 0x20,
762 0x04, 0x2d, 0x04, 0x3b, 0x04, 0x48, 0x04, 0x55, 0x04, 0x63, 0x04, 0x71,
763 0x04, 0x7e, 0x04, 0x8c, 0x04, 0x9a, 0x04, 0xa8, 0x04, 0xb6, 0x04, 0xc4,
764 0x04, 0xd3, 0x04, 0xe1, 0x04, 0xf0, 0x04, 0xfe, 0x05, 0x0d, 0x05, 0x1c,
765 0x05, 0x2b, 0x05, 0x3a, 0x05, 0x49, 0x05, 0x58, 0x05, 0x67, 0x05, 0x77,
766 0x05, 0x86, 0x05, 0x96, 0x05, 0xa6, 0x05, 0xb5, 0x05, 0xc5, 0x05, 0xd5,
767 0x05, 0xe5, 0x05, 0xf6, 0x06, 0x06, 0x06, 0x16, 0x06, 0x27, 0x06, 0x37,
768 0x06, 0x48, 0x06, 0x59, 0x06, 0x6a, 0x06, 0x7b, 0x06, 0x8c, 0x06, 0x9d,
769 0x06, 0xaf, 0x06, 0xc0, 0x06, 0xd1, 0x06, 0xe3, 0x06, 0xf5, 0x07, 0x07,
770 0x07, 0x19, 0x07, 0x2b, 0x07, 0x3d, 0x07, 0x4f, 0x07, 0x61, 0x07, 0x74,
771 0x07, 0x86, 0x07, 0x99, 0x07, 0xac, 0x07, 0xbf, 0x07, 0xd2, 0x07, 0xe5,
772 0x07, 0xf8, 0x08, 0x0b, 0x08, 0x1f, 0x08, 0x32, 0x08, 0x46, 0x08, 0x5a,
773 0x08, 0x6e, 0x08, 0x82, 0x08, 0x96, 0x08, 0xaa, 0x08, 0xbe, 0x08, 0xd2,
774 0x08, 0xe7, 0x08, 0xfb, 0x09, 0x10, 0x09, 0x25, 0x09, 0x3a, 0x09, 0x4f,
775 0x09, 0x64, 0x09, 0x79, 0x09, 0x8f, 0x09, 0xa4, 0x09, 0xba, 0x09, 0xcf,
776 0x09, 0xe5, 0x09, 0xfb, 0x0a, 0x11, 0x0a, 0x27, 0x0a, 0x3d, 0x0a, 0x54,
777 0x0a, 0x6a, 0x0a, 0x81, 0x0a, 0x98, 0x0a, 0xae, 0x0a, 0xc5, 0x0a, 0xdc,
778 0x0a, 0xf3, 0x0b, 0x0b, 0x0b, 0x22, 0x0b, 0x39, 0x0b, 0x51, 0x0b, 0x69,
779 0x0b, 0x80, 0x0b, 0x98, 0x0b, 0xb0, 0x0b, 0xc8, 0x0b, 0xe1, 0x0b, 0xf9,
780 0x0c, 0x12, 0x0c, 0x2a, 0x0c, 0x43, 0x0c, 0x5c, 0x0c, 0x75, 0x0c, 0x8e,
781 0x0c, 0xa7, 0x0c, 0xc0, 0x0c, 0xd9, 0x0c, 0xf3, 0x0d, 0x0d, 0x0d, 0x26,
782 0x0d, 0x40, 0x0d, 0x5a, 0x0d, 0x74, 0x0d, 0x8e, 0x0d, 0xa9, 0x0d, 0xc3,
783 0x0d, 0xde, 0x0d, 0xf8, 0x0e, 0x13, 0x0e, 0x2e, 0x0e, 0x49, 0x0e, 0x64,
784 0x0e, 0x7f, 0x0e, 0x9b, 0x0e, 0xb6, 0x0e, 0xd2, 0x0e, 0xee, 0x0f, 0x09,
785 0x0f, 0x25, 0x0f, 0x41, 0x0f, 0x5e, 0x0f, 0x7a, 0x0f, 0x96, 0x0f, 0xb3,
786 0x0f, 0xcf, 0x0f, 0xec, 0x10, 0x09, 0x10, 0x26, 0x10, 0x43, 0x10, 0x61,
787 0x10, 0x7e, 0x10, 0x9b, 0x10, 0xb9, 0x10, 0xd7, 0x10, 0xf5, 0x11, 0x13,
788 0x11, 0x31, 0x11, 0x4f, 0x11, 0x6d, 0x11, 0x8c, 0x11, 0xaa, 0x11, 0xc9,
789 0x11, 0xe8, 0x12, 0x07, 0x12, 0x26, 0x12, 0x45, 0x12, 0x64, 0x12, 0x84,
790 0x12, 0xa3, 0x12, 0xc3, 0x12, 0xe3, 0x13, 0x03, 0x13, 0x23, 0x13, 0x43,
791 0x13, 0x63, 0x13, 0x83, 0x13, 0xa4, 0x13, 0xc5, 0x13, 0xe5, 0x14, 0x06,
792 0x14, 0x27, 0x14, 0x49, 0x14, 0x6a, 0x14, 0x8b, 0x14, 0xad, 0x14, 0xce,
793 0x14, 0xf0, 0x15, 0x12, 0x15, 0x34, 0x15, 0x56, 0x15, 0x78, 0x15, 0x9b,
794 0x15, 0xbd, 0x15, 0xe0, 0x16, 0x03, 0x16, 0x26, 0x16, 0x49, 0x16, 0x6c,
795 0x16, 0x8f, 0x16, 0xb2, 0x16, 0xd6, 0x16, 0xfa, 0x17, 0x1d, 0x17, 0x41,
796 0x17, 0x65, 0x17, 0x89, 0x17, 0xae, 0x17, 0xd2, 0x17, 0xf7, 0x18, 0x1b,
797 0x18, 0x40, 0x18, 0x65, 0x18, 0x8a, 0x18, 0xaf, 0x18, 0xd5, 0x18, 0xfa,
798 0x19, 0x20, 0x19, 0x45, 0x19, 0x6b, 0x19, 0x91, 0x19, 0xb7, 0x19, 0xdd,
799 0x1a, 0x04, 0x1a, 0x2a, 0x1a, 0x51, 0x1a, 0x77, 0x1a, 0x9e, 0x1a, 0xc5,
800 0x1a, 0xec, 0x1b, 0x14, 0x1b, 0x3b, 0x1b, 0x63, 0x1b, 0x8a, 0x1b, 0xb2,
801 0x1b, 0xda, 0x1c, 0x02, 0x1c, 0x2a, 0x1c, 0x52, 0x1c, 0x7b, 0x1c, 0xa3,
802 0x1c, 0xcc, 0x1c, 0xf5, 0x1d, 0x1e, 0x1d, 0x47, 0x1d, 0x70, 0x1d, 0x99,
803 0x1d, 0xc3, 0x1d, 0xec, 0x1e, 0x16, 0x1e, 0x40, 0x1e, 0x6a, 0x1e, 0x94,
804 0x1e, 0xbe, 0x1e, 0xe9, 0x1f, 0x13, 0x1f, 0x3e, 0x1f, 0x69, 0x1f, 0x94,
805 0x1f, 0xbf, 0x1f, 0xea, 0x20, 0x15, 0x20, 0x41, 0x20, 0x6c, 0x20, 0x98,
806 0x20, 0xc4, 0x20, 0xf0, 0x21, 0x1c, 0x21, 0x48, 0x21, 0x75, 0x21, 0xa1,
807 0x21, 0xce, 0x21, 0xfb, 0x22, 0x27, 0x22, 0x55, 0x22, 0x82, 0x22, 0xaf,
808 0x22, 0xdd, 0x23, 0x0a, 0x23, 0x38, 0x23, 0x66, 0x23, 0x94, 0x23, 0xc2,
809 0x23, 0xf0, 0x24, 0x1f, 0x24, 0x4d, 0x24, 0x7c, 0x24, 0xab, 0x24, 0xda,
810 0x25, 0x09, 0x25, 0x38, 0x25, 0x68, 0x25, 0x97, 0x25, 0xc7, 0x25, 0xf7,
811 0x26, 0x27, 0x26, 0x57, 0x26, 0x87, 0x26, 0xb7, 0x26, 0xe8, 0x27, 0x18,
812 0x27, 0x49, 0x27, 0x7a, 0x27, 0xab, 0x27, 0xdc, 0x28, 0x0d, 0x28, 0x3f,
813 0x28, 0x71, 0x28, 0xa2, 0x28, 0xd4, 0x29, 0x06, 0x29, 0x38, 0x29, 0x6b,
814 0x29, 0x9d, 0x29, 0xd0, 0x2a, 0x02, 0x2a, 0x35, 0x2a, 0x68, 0x2a, 0x9b,
815 0x2a, 0xcf, 0x2b, 0x02, 0x2b, 0x36, 0x2b, 0x69, 0x2b, 0x9d, 0x2b, 0xd1,
816 0x2c, 0x05, 0x2c, 0x39, 0x2c, 0x6e, 0x2c, 0xa2, 0x2c, 0xd7, 0x2d, 0x0c,
817 0x2d, 0x41, 0x2d, 0x76, 0x2d, 0xab, 0x2d, 0xe1, 0x2e, 0x16, 0x2e, 0x4c,
818 0x2e, 0x82, 0x2e, 0xb7, 0x2e, 0xee, 0x2f, 0x24, 0x2f, 0x5a, 0x2f, 0x91,
819 0x2f, 0xc7, 0x2f, 0xfe, 0x30, 0x35, 0x30, 0x6c, 0x30, 0xa4, 0x30, 0xdb,
820 0x31, 0x12, 0x31, 0x4a, 0x31, 0x82, 0x31, 0xba, 0x31, 0xf2, 0x32, 0x2a,
821 0x32, 0x63, 0x32, 0x9b, 0x32, 0xd4, 0x33, 0x0d, 0x33, 0x46, 0x33, 0x7f,
822 0x33, 0xb8, 0x33, 0xf1, 0x34, 0x2b, 0x34, 0x65, 0x34, 0x9e, 0x34, 0xd8,
823 0x35, 0x13, 0x35, 0x4d, 0x35, 0x87, 0x35, 0xc2, 0x35, 0xfd, 0x36, 0x37,
824 0x36, 0x72, 0x36, 0xae, 0x36, 0xe9, 0x37, 0x24, 0x37, 0x60, 0x37, 0x9c,
825 0x37, 0xd7, 0x38, 0x14, 0x38, 0x50, 0x38, 0x8c, 0x38, 0xc8, 0x39, 0x05,
826 0x39, 0x42, 0x39, 0x7f, 0x39, 0xbc, 0x39, 0xf9, 0x3a, 0x36, 0x3a, 0x74,
827 0x3a, 0xb2, 0x3a, 0xef, 0x3b, 0x2d, 0x3b, 0x6b, 0x3b, 0xaa, 0x3b, 0xe8,
828 0x3c, 0x27, 0x3c, 0x65, 0x3c, 0xa4, 0x3c, 0xe3, 0x3d, 0x22, 0x3d, 0x61,
829 0x3d, 0xa1, 0x3d, 0xe0, 0x3e, 0x20, 0x3e, 0x60, 0x3e, 0xa0, 0x3e, 0xe0,
830 0x3f, 0x21, 0x3f, 0x61, 0x3f, 0xa2, 0x3f, 0xe2, 0x40, 0x23, 0x40, 0x64,
831 0x40, 0xa6, 0x40, 0xe7, 0x41, 0x29, 0x41, 0x6a, 0x41, 0xac, 0x41, 0xee,
832 0x42, 0x30, 0x42, 0x72, 0x42, 0xb5, 0x42, 0xf7, 0x43, 0x3a, 0x43, 0x7d,
833 0x43, 0xc0, 0x44, 0x03, 0x44, 0x47, 0x44, 0x8a, 0x44, 0xce, 0x45, 0x12,
834 0x45, 0x55, 0x45, 0x9a, 0x45, 0xde, 0x46, 0x22, 0x46, 0x67, 0x46, 0xab,
835 0x46, 0xf0, 0x47, 0x35, 0x47, 0x7b, 0x47, 0xc0, 0x48, 0x05, 0x48, 0x4b,
836 0x48, 0x91, 0x48, 0xd7, 0x49, 0x1d, 0x49, 0x63, 0x49, 0xa9, 0x49, 0xf0,
837 0x4a, 0x37, 0x4a, 0x7d, 0x4a, 0xc4, 0x4b, 0x0c, 0x4b, 0x53, 0x4b, 0x9a,
838 0x4b, 0xe2, 0x4c, 0x2a, 0x4c, 0x72, 0x4c, 0xba, 0x4d, 0x02, 0x4d, 0x4a,
839 0x4d, 0x93, 0x4d, 0xdc, 0x4e, 0x25, 0x4e, 0x6e, 0x4e, 0xb7, 0x4f, 0x00,
840 0x4f, 0x49, 0x4f, 0x93, 0x4f, 0xdd, 0x50, 0x27, 0x50, 0x71, 0x50, 0xbb,
841 0x51, 0x06, 0x51, 0x50, 0x51, 0x9b, 0x51, 0xe6, 0x52, 0x31, 0x52, 0x7c,
842 0x52, 0xc7, 0x53, 0x13, 0x53, 0x5f, 0x53, 0xaa, 0x53, 0xf6, 0x54, 0x42,
843 0x54, 0x8f, 0x54, 0xdb, 0x55, 0x28, 0x55, 0x75, 0x55, 0xc2, 0x56, 0x0f,
844 0x56, 0x5c, 0x56, 0xa9, 0x56, 0xf7, 0x57, 0x44, 0x57, 0x92, 0x57, 0xe0,
845 0x58, 0x2f, 0x58, 0x7d, 0x58, 0xcb, 0x59, 0x1a, 0x59, 0x69, 0x59, 0xb8,
846 0x5a, 0x07, 0x5a, 0x56, 0x5a, 0xa6, 0x5a, 0xf5, 0x5b, 0x45, 0x5b, 0x95,
847 0x5b, 0xe5, 0x5c, 0x35, 0x5c, 0x86, 0x5c, 0xd6, 0x5d, 0x27, 0x5d, 0x78,
848 0x5d, 0xc9, 0x5e, 0x1a, 0x5e, 0x6c, 0x5e, 0xbd, 0x5f, 0x0f, 0x5f, 0x61,
849 0x5f, 0xb3, 0x60, 0x05, 0x60, 0x57, 0x60, 0xaa, 0x60, 0xfc, 0x61, 0x4f,
850 0x61, 0xa2, 0x61, 0xf5, 0x62, 0x49, 0x62, 0x9c, 0x62, 0xf0, 0x63, 0x43,
851 0x63, 0x97, 0x63, 0xeb, 0x64, 0x40, 0x64, 0x94, 0x64, 0xe9, 0x65, 0x3d,
852 0x65, 0x92, 0x65, 0xe7, 0x66, 0x3d, 0x66, 0x92, 0x66, 0xe8, 0x67, 0x3d,
853 0x67, 0x93, 0x67, 0xe9, 0x68, 0x3f, 0x68, 0x96, 0x68, 0xec, 0x69, 0x43,
854 0x69, 0x9a, 0x69, 0xf1, 0x6a, 0x48, 0x6a, 0x9f, 0x6a, 0xf7, 0x6b, 0x4f,
855 0x6b, 0xa7, 0x6b, 0xff, 0x6c, 0x57, 0x6c, 0xaf, 0x6d, 0x08, 0x6d, 0x60,
856 0x6d, 0xb9, 0x6e, 0x12, 0x6e, 0x6b, 0x6e, 0xc4, 0x6f, 0x1e, 0x6f, 0x78,
857 0x6f, 0xd1, 0x70, 0x2b, 0x70, 0x86, 0x70, 0xe0, 0x71, 0x3a, 0x71, 0x95,
858 0x71, 0xf0, 0x72, 0x4b, 0x72, 0xa6, 0x73, 0x01, 0x73, 0x5d, 0x73, 0xb8,
859 0x74, 0x14, 0x74, 0x70, 0x74, 0xcc, 0x75, 0x28, 0x75, 0x85, 0x75, 0xe1,
860 0x76, 0x3e, 0x76, 0x9b, 0x76, 0xf8, 0x77, 0x56, 0x77, 0xb3, 0x78, 0x11,
861 0x78, 0x6e, 0x78, 0xcc, 0x79, 0x2a, 0x79, 0x89, 0x79, 0xe7, 0x7a, 0x46,
862 0x7a, 0xa5, 0x7b, 0x04, 0x7b, 0x63, 0x7b, 0xc2, 0x7c, 0x21, 0x7c, 0x81,
863 0x7c, 0xe1, 0x7d, 0x41, 0x7d, 0xa1, 0x7e, 0x01, 0x7e, 0x62, 0x7e, 0xc2,
864 0x7f, 0x23, 0x7f, 0x84, 0x7f, 0xe5, 0x80, 0x47, 0x80, 0xa8, 0x81, 0x0a,
865 0x81, 0x6b, 0x81, 0xcd, 0x82, 0x30, 0x82, 0x92, 0x82, 0xf4, 0x83, 0x57,
866 0x83, 0xba, 0x84, 0x1d, 0x84, 0x80, 0x84, 0xe3, 0x85, 0x47, 0x85, 0xab,
867 0x86, 0x0e, 0x86, 0x72, 0x86, 0xd7, 0x87, 0x3b, 0x87, 0x9f, 0x88, 0x04,
868 0x88, 0x69, 0x88, 0xce, 0x89, 0x33, 0x89, 0x99, 0x89, 0xfe, 0x8a, 0x64,
869 0x8a, 0xca, 0x8b, 0x30, 0x8b, 0x96, 0x8b, 0xfc, 0x8c, 0x63, 0x8c, 0xca,
870 0x8d, 0x31, 0x8d, 0x98, 0x8d, 0xff, 0x8e, 0x66, 0x8e, 0xce, 0x8f, 0x36,
871 0x8f, 0x9e, 0x90, 0x06, 0x90, 0x6e, 0x90, 0xd6, 0x91, 0x3f, 0x91, 0xa8,
872 0x92, 0x11, 0x92, 0x7a, 0x92, 0xe3, 0x93, 0x4d, 0x93, 0xb6, 0x94, 0x20,
873 0x94, 0x8a, 0x94, 0xf4, 0x95, 0x5f, 0x95, 0xc9, 0x96, 0x34, 0x96, 0x9f,
874 0x97, 0x0a, 0x97, 0x75, 0x97, 0xe0, 0x98, 0x4c, 0x98, 0xb8, 0x99, 0x24,
875 0x99, 0x90, 0x99, 0xfc, 0x9a, 0x68, 0x9a, 0xd5, 0x9b, 0x42, 0x9b, 0xaf,
876 0x9c, 0x1c, 0x9c, 0x89, 0x9c, 0xf7, 0x9d, 0x64, 0x9d, 0xd2, 0x9e, 0x40,
877 0x9e, 0xae, 0x9f, 0x1d, 0x9f, 0x8b, 0x9f, 0xfa, 0xa0, 0x69, 0xa0, 0xd8,
878 0xa1, 0x47, 0xa1, 0xb6, 0xa2, 0x26, 0xa2, 0x96, 0xa3, 0x06, 0xa3, 0x76,
879 0xa3, 0xe6, 0xa4, 0x56, 0xa4, 0xc7, 0xa5, 0x38, 0xa5, 0xa9, 0xa6, 0x1a,
880 0xa6, 0x8b, 0xa6, 0xfd, 0xa7, 0x6e, 0xa7, 0xe0, 0xa8, 0x52, 0xa8, 0xc4,
881 0xa9, 0x37, 0xa9, 0xa9, 0xaa, 0x1c, 0xaa, 0x8f, 0xab, 0x02, 0xab, 0x75,
882 0xab, 0xe9, 0xac, 0x5c, 0xac, 0xd0, 0xad, 0x44, 0xad, 0xb8, 0xae, 0x2d,
883 0xae, 0xa1, 0xaf, 0x16, 0xaf, 0x8b, 0xb0, 0x00, 0xb0, 0x75, 0xb0, 0xea,
884 0xb1, 0x60, 0xb1, 0xd6, 0xb2, 0x4b, 0xb2, 0xc2, 0xb3, 0x38, 0xb3, 0xae,
885 0xb4, 0x25, 0xb4, 0x9c, 0xb5, 0x13, 0xb5, 0x8a, 0xb6, 0x01, 0xb6, 0x79,
886 0xb6, 0xf0, 0xb7, 0x68, 0xb7, 0xe0, 0xb8, 0x59, 0xb8, 0xd1, 0xb9, 0x4a,
887 0xb9, 0xc2, 0xba, 0x3b, 0xba, 0xb5, 0xbb, 0x2e, 0xbb, 0xa7, 0xbc, 0x21,
888 0xbc, 0x9b, 0xbd, 0x15, 0xbd, 0x8f, 0xbe, 0x0a, 0xbe, 0x84, 0xbe, 0xff,
889 0xbf, 0x7a, 0xbf, 0xf5, 0xc0, 0x70, 0xc0, 0xec, 0xc1, 0x67, 0xc1, 0xe3,
890 0xc2, 0x5f, 0xc2, 0xdb, 0xc3, 0x58, 0xc3, 0xd4, 0xc4, 0x51, 0xc4, 0xce,
891 0xc5, 0x4b, 0xc5, 0xc8, 0xc6, 0x46, 0xc6, 0xc3, 0xc7, 0x41, 0xc7, 0xbf,
892 0xc8, 0x3d, 0xc8, 0xbc, 0xc9, 0x3a, 0xc9, 0xb9, 0xca, 0x38, 0xca, 0xb7,
893 0xcb, 0x36, 0xcb, 0xb6, 0xcc, 0x35, 0xcc, 0xb5, 0xcd, 0x35, 0xcd, 0xb5,
894 0xce, 0x36, 0xce, 0xb6, 0xcf, 0x37, 0xcf, 0xb8, 0xd0, 0x39, 0xd0, 0xba,
895 0xd1, 0x3c, 0xd1, 0xbe, 0xd2, 0x3f, 0xd2, 0xc1, 0xd3, 0x44, 0xd3, 0xc6,
896 0xd4, 0x49, 0xd4, 0xcb, 0xd5, 0x4e, 0xd5, 0xd1, 0xd6, 0x55, 0xd6, 0xd8,
897 0xd7, 0x5c, 0xd7, 0xe0, 0xd8, 0x64, 0xd8, 0xe8, 0xd9, 0x6c, 0xd9, 0xf1,
898 0xda, 0x76, 0xda, 0xfb, 0xdb, 0x80, 0xdc, 0x05, 0xdc, 0x8a, 0xdd, 0x10,
899 0xdd, 0x96, 0xde, 0x1c, 0xde, 0xa2, 0xdf, 0x29, 0xdf, 0xaf, 0xe0, 0x36,
900 0xe0, 0xbd, 0xe1, 0x44, 0xe1, 0xcc, 0xe2, 0x53, 0xe2, 0xdb, 0xe3, 0x63,
901 0xe3, 0xeb, 0xe4, 0x73, 0xe4, 0xfc, 0xe5, 0x84, 0xe6, 0x0d, 0xe6, 0x96,
902 0xe7, 0x1f, 0xe7, 0xa9, 0xe8, 0x32, 0xe8, 0xbc, 0xe9, 0x46, 0xe9, 0xd0,
903 0xea, 0x5b, 0xea, 0xe5, 0xeb, 0x70, 0xeb, 0xfb, 0xec, 0x86, 0xed, 0x11,
904 0xed, 0x9c, 0xee, 0x28, 0xee, 0xb4, 0xef, 0x40, 0xef, 0xcc, 0xf0, 0x58,
905 0xf0, 0xe5, 0xf1, 0x72, 0xf1, 0xff, 0xf2, 0x8c, 0xf3, 0x19, 0xf3, 0xa7,
906 0xf4, 0x34, 0xf4, 0xc2, 0xf5, 0x50, 0xf5, 0xde, 0xf6, 0x6d, 0xf6, 0xfb,
907 0xf7, 0x8a, 0xf8, 0x19, 0xf8, 0xa8, 0xf9, 0x38, 0xf9, 0xc7, 0xfa, 0x57,
908 0xfa, 0xe7, 0xfb, 0x77, 0xfc, 0x07, 0xfc, 0x98, 0xfd, 0x29, 0xfd, 0xba,
909 0xfe, 0x4b, 0xfe, 0xdc, 0xff, 0x6d, 0xff, 0xff
910 };
911
913 *profile;
914
915 MagickBooleanType
916 status;
917
918 assert(image != (Image *) NULL);
919 assert(image->signature == MagickCoreSignature);
920 if (GetImageProfile(image,"icc") != (const StringInfo *) NULL)
921 return(MagickFalse);
922 profile=AcquireStringInfo(sizeof(sRGBProfile));
923 SetStringInfoDatum(profile,sRGBProfile);
924 status=SetImageProfile(image,"icc",profile,exception);
925 profile=DestroyStringInfo(profile);
926 return(status);
927}
928
929MagickExport MagickBooleanType ProfileImage(Image *image,const char *name,
930 const void *datum,const size_t length,ExceptionInfo *exception)
931{
932#define ProfileImageTag "Profile/Image"
933#ifndef TYPE_XYZ_8
934 #define TYPE_XYZ_8 (COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(1))
935#endif
936#define ThrowProfileException(severity,tag,context) \
937{ \
938 if (profile != (StringInfo *) NULL) \
939 profile=DestroyStringInfo(profile); \
940 if (cms_context != (cmsContext) NULL) \
941 cmsDeleteContext(cms_context); \
942 if (source_info.profile != (cmsHPROFILE) NULL) \
943 (void) cmsCloseProfile(source_info.profile); \
944 if (target_info.profile != (cmsHPROFILE) NULL) \
945 (void) cmsCloseProfile(target_info.profile); \
946 ThrowBinaryException(severity,tag,context); \
947}
948
949 MagickBooleanType
950 status;
951
953 *profile;
954
955 assert(image != (Image *) NULL);
956 assert(image->signature == MagickCoreSignature);
957 assert(name != (const char *) NULL);
958 if (IsEventLogging() != MagickFalse)
959 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
960 if ((datum == (const void *) NULL) || (length == 0))
961 {
962 char
963 *next;
964
965 /*
966 Delete image profile(s).
967 */
968 ResetImageProfileIterator(image);
969 for (next=GetNextImageProfile(image); next != (const char *) NULL; )
970 {
971 if (IsOptionMember(next,name) != MagickFalse)
972 {
973 (void) DeleteImageProfile(image,next);
974 ResetImageProfileIterator(image);
975 }
976 next=GetNextImageProfile(image);
977 }
978 return(MagickTrue);
979 }
980 /*
981 Add a ICC, IPTC, or generic profile to the image.
982 */
983 status=MagickTrue;
984 profile=AcquireStringInfo((size_t) length);
985 SetStringInfoDatum(profile,(unsigned char *) datum);
986 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
987 status=SetImageProfile(image,name,profile,exception);
988 else
989 {
990 const StringInfo
991 *icc_profile;
992
993 icc_profile=GetImageProfile(image,"icc");
994 if ((icc_profile != (const StringInfo *) NULL) &&
995 (CompareStringInfo(icc_profile,profile) == 0))
996 {
997 const char
998 *value;
999
1000 value=GetImageProperty(image,"exif:ColorSpace",exception);
1001 (void) value;
1002 if (LocaleCompare(value,"1") != 0)
1003 (void) SetsRGBImageProfile(image,exception);
1004 value=GetImageProperty(image,"exif:InteroperabilityIndex",exception);
1005 if (LocaleCompare(value,"R98.") != 0)
1006 (void) SetsRGBImageProfile(image,exception);
1007 icc_profile=GetImageProfile(image,"icc");
1008 }
1009 if ((icc_profile != (const StringInfo *) NULL) &&
1010 (CompareStringInfo(icc_profile,profile) == 0))
1011 {
1012 profile=DestroyStringInfo(profile);
1013 return(MagickTrue);
1014 }
1015#if !defined(MAGICKCORE_LCMS_DELEGATE)
1016 (void) ThrowMagickException(exception,GetMagickModule(),
1017 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
1018 "'%s' (LCMS)",image->filename);
1019#else
1020 {
1021 cmsContext
1022 cms_context;
1023
1025 cms_exception;
1026
1027 LCMSInfo
1028 source_info,
1029 target_info;
1030
1031 /*
1032 Transform pixel colors as defined by the color profiles.
1033 */
1034 cms_exception.image=image;
1035 cms_exception.exception=exception;
1036 cms_context=cmsCreateContext(NULL,&cms_exception);
1037 if (cms_context == (cmsContext) NULL)
1038 {
1039 profile=DestroyStringInfo(profile);
1040 ThrowBinaryException(ResourceLimitError,
1041 "ColorspaceColorProfileMismatch",name);
1042 }
1043 cmsSetLogErrorHandlerTHR(cms_context,CMSExceptionHandler);
1044 source_info.profile=cmsOpenProfileFromMemTHR(cms_context,
1045 GetStringInfoDatum(profile),(cmsUInt32Number)
1046 GetStringInfoLength(profile));
1047 if (source_info.profile == (cmsHPROFILE) NULL)
1048 {
1049 profile=DestroyStringInfo(profile);
1050 cmsDeleteContext(cms_context);
1051 ThrowBinaryException(ResourceLimitError,
1052 "ColorspaceColorProfileMismatch",name);
1053 }
1054 if ((cmsGetDeviceClass(source_info.profile) != cmsSigLinkClass) &&
1055 (icc_profile == (StringInfo *) NULL))
1056 status=SetImageProfile(image,name,profile,exception);
1057 else
1058 {
1059 CacheView
1060 *image_view;
1061
1062 cmsColorSpaceSignature
1063 signature;
1064
1065 cmsHTRANSFORM
1066 *magick_restrict transform;
1067
1068 cmsUInt32Number
1069 flags;
1070
1071 MagickBooleanType
1072 highres;
1073
1074 MagickOffsetType
1075 progress;
1076
1077 ssize_t
1078 y;
1079
1080 target_info.profile=(cmsHPROFILE) NULL;
1081 if (icc_profile != (StringInfo *) NULL)
1082 {
1083 target_info.profile=source_info.profile;
1084 source_info.profile=cmsOpenProfileFromMemTHR(cms_context,
1085 GetStringInfoDatum(icc_profile),(cmsUInt32Number)
1086 GetStringInfoLength(icc_profile));
1087 if (source_info.profile == (cmsHPROFILE) NULL)
1088 ThrowProfileException(ResourceLimitError,
1089 "ColorspaceColorProfileMismatch",name);
1090 }
1091 highres=MagickTrue;
1092#if !defined(MAGICKCORE_HDRI_SUPPORT) || (MAGICKCORE_QUANTUM_DEPTH > 16)
1093 {
1094 const char
1095 *artifact;
1096
1097 artifact=GetImageArtifact(image,"profile:highres-transform");
1098 if (IsStringFalse(artifact) != MagickFalse)
1099 highres=MagickFalse;
1100 }
1101#endif
1102 SetLCMSInfoScale(&source_info,1.0);
1103 SetLCMSInfoTranslate(&source_info,0.0);
1104 source_info.colorspace=sRGBColorspace;
1105 source_info.channels=3;
1106 switch (cmsGetColorSpace(source_info.profile))
1107 {
1108 case cmsSigCmykData:
1109 {
1110 source_info.colorspace=CMYKColorspace;
1111 source_info.channels=4;
1112 if (highres != MagickFalse)
1113 {
1114 source_info.type=(cmsUInt32Number) TYPE_CMYK_DBL;
1115 SetLCMSInfoScale(&source_info,100.0);
1116 }
1117#if (MAGICKCORE_QUANTUM_DEPTH == 8)
1118 else
1119 source_info.type=(cmsUInt32Number) TYPE_CMYK_8;
1120#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1121 else
1122 source_info.type=(cmsUInt32Number) TYPE_CMYK_16;
1123#endif
1124 break;
1125 }
1126 case cmsSigGrayData:
1127 {
1128 source_info.colorspace=GRAYColorspace;
1129 source_info.channels=1;
1130 if (highres != MagickFalse)
1131 source_info.type=(cmsUInt32Number) TYPE_GRAY_DBL;
1132#if (MAGICKCORE_QUANTUM_DEPTH == 8)
1133 else
1134 source_info.type=(cmsUInt32Number) TYPE_GRAY_8;
1135#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1136 else
1137 source_info.type=(cmsUInt32Number) TYPE_GRAY_16;
1138#endif
1139 break;
1140 }
1141 case cmsSigLabData:
1142 {
1143 source_info.colorspace=LabColorspace;
1144 if (highres != MagickFalse)
1145 {
1146 source_info.type=(cmsUInt32Number) TYPE_Lab_DBL;
1147 source_info.scale[0]=100.0;
1148 source_info.scale[1]=255.0;
1149 source_info.scale[2]=255.0;
1150 source_info.translate[1]=(-0.5);
1151 source_info.translate[2]=(-0.5);
1152 }
1153#if (MAGICKCORE_QUANTUM_DEPTH == 8)
1154 else
1155 source_info.type=(cmsUInt32Number) TYPE_Lab_8;
1156#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1157 else
1158 source_info.type=(cmsUInt32Number) TYPE_Lab_16;
1159#endif
1160 break;
1161 }
1162 case cmsSigRgbData:
1163 {
1164 source_info.colorspace=sRGBColorspace;
1165 if (highres != MagickFalse)
1166 source_info.type=(cmsUInt32Number) TYPE_RGB_DBL;
1167#if (MAGICKCORE_QUANTUM_DEPTH == 8)
1168 else
1169 source_info.type=(cmsUInt32Number) TYPE_RGB_8;
1170#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1171 else
1172 source_info.type=(cmsUInt32Number) TYPE_RGB_16;
1173#endif
1174 break;
1175 }
1176 case cmsSigXYZData:
1177 {
1178 source_info.colorspace=XYZColorspace;
1179 if (highres != MagickFalse)
1180 source_info.type=(cmsUInt32Number) TYPE_XYZ_DBL;
1181#if (MAGICKCORE_QUANTUM_DEPTH == 8)
1182 else
1183 source_info.type=(cmsUInt32Number) TYPE_XYZ_8;
1184#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1185 else
1186 source_info.type=(cmsUInt32Number) TYPE_XYZ_16;
1187#endif
1188 break;
1189 }
1190 default:
1191 ThrowProfileException(ImageError,
1192 "ColorspaceColorProfileMismatch",name);
1193 }
1194 signature=cmsGetPCS(source_info.profile);
1195 if (target_info.profile != (cmsHPROFILE) NULL)
1196 signature=cmsGetColorSpace(target_info.profile);
1197 SetLCMSInfoScale(&target_info,1.0);
1198 SetLCMSInfoTranslate(&target_info,0.0);
1199 target_info.channels=3;
1200 switch (signature)
1201 {
1202 case cmsSigCmykData:
1203 {
1204 target_info.colorspace=CMYKColorspace;
1205 target_info.channels=4;
1206 if (highres != MagickFalse)
1207 {
1208 target_info.type=(cmsUInt32Number) TYPE_CMYK_DBL;
1209 SetLCMSInfoScale(&target_info,0.01);
1210 }
1211#if (MAGICKCORE_QUANTUM_DEPTH == 8)
1212 else
1213 target_info.type=(cmsUInt32Number) TYPE_CMYK_8;
1214#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1215 else
1216 target_info.type=(cmsUInt32Number) TYPE_CMYK_16;
1217#endif
1218 break;
1219 }
1220 case cmsSigGrayData:
1221 {
1222 target_info.colorspace=GRAYColorspace;
1223 target_info.channels=1;
1224 if (highres != MagickFalse)
1225 target_info.type=(cmsUInt32Number) TYPE_GRAY_DBL;
1226#if (MAGICKCORE_QUANTUM_DEPTH == 8)
1227 else
1228 target_info.type=(cmsUInt32Number) TYPE_GRAY_8;
1229#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
1230 else
1231 target_info.type=(cmsUInt32Number) TYPE_GRAY_16;
1232#endif
1233 break;
1234 }
1235 case cmsSigLabData:
1236 {
1237 target_info.colorspace=LabColorspace;
1238 if (highres != MagickFalse)
1239 {
1240 target_info.type=(cmsUInt32Number) TYPE_Lab_DBL;
1241 target_info.scale[0]=0.01;
1242 target_info.scale[1]=1/255.0;
1243 target_info.scale[2]=1/255.0;
1244 target_info.translate[1]=0.5;
1245 target_info.translate[2]=0.5;
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=SetImageProfile(image,name,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 profile=DestroyStringInfo(profile);
1445 return(status);
1446}
1447
1448/*
1449%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1450% %
1451% %
1452% %
1453% R e m o v e I m a g e P r o f i l e %
1454% %
1455% %
1456% %
1457%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1458%
1459% RemoveImageProfile() removes a named profile from the image and returns its
1460% value.
1461%
1462% The format of the RemoveImageProfile method is:
1463%
1464% void *RemoveImageProfile(Image *image,const char *name)
1465%
1466% A description of each parameter follows:
1467%
1468% o image: the image.
1469%
1470% o name: the profile name.
1471%
1472*/
1473MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
1474{
1476 *profile;
1477
1478 assert(image != (Image *) NULL);
1479 assert(image->signature == MagickCoreSignature);
1480 if (IsEventLogging() != MagickFalse)
1481 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1482 if (image->profiles == (SplayTreeInfo *) NULL)
1483 return((StringInfo *) NULL);
1484 WriteTo8BimProfile(image,name,(StringInfo *) NULL);
1485 profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
1486 image->profiles,name);
1487 return(profile);
1488}
1489
1490/*
1491%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1492% %
1493% %
1494% %
1495% R e s e t P r o f i l e I t e r a t o r %
1496% %
1497% %
1498% %
1499%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1500%
1501% ResetImageProfileIterator() resets the image profile iterator. Use it in
1502% conjunction with GetNextImageProfile() to iterate over all the profiles
1503% associated with an image.
1504%
1505% The format of the ResetImageProfileIterator method is:
1506%
1507% ResetImageProfileIterator(Image *image)
1508%
1509% A description of each parameter follows:
1510%
1511% o image: the image.
1512%
1513*/
1514MagickExport void ResetImageProfileIterator(const Image *image)
1515{
1516 assert(image != (Image *) NULL);
1517 assert(image->signature == MagickCoreSignature);
1518 if (IsEventLogging() != MagickFalse)
1519 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1520 if (image->profiles == (SplayTreeInfo *) NULL)
1521 return;
1522 ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
1523}
1524
1525/*
1526%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1527% %
1528% %
1529% %
1530% S e t I m a g e P r o f i l e %
1531% %
1532% %
1533% %
1534%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1535%
1536% SetImageProfile() adds a named profile to the image. If a profile with the
1537% same name already exists, it is replaced. This method differs from the
1538% ProfileImage() method in that it does not apply CMS color profiles.
1539%
1540% The format of the SetImageProfile method is:
1541%
1542% MagickBooleanType SetImageProfile(Image *image,const char *name,
1543% const StringInfo *profile)
1544%
1545% A description of each parameter follows:
1546%
1547% o image: the image.
1548%
1549% o name: the profile name, for example icc, exif, and 8bim (8bim is the
1550% Photoshop wrapper for iptc profiles).
1551%
1552% o profile: A StringInfo structure that contains the named profile.
1553%
1554*/
1555
1556static void *DestroyProfile(void *profile)
1557{
1558 return((void *) DestroyStringInfo((StringInfo *) profile));
1559}
1560
1561static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1562 unsigned char *quantum)
1563{
1564 *quantum=(*p++);
1565 return(p);
1566}
1567
1568static inline const unsigned char *ReadResourceLong(const unsigned char *p,
1569 unsigned int *quantum)
1570{
1571 *quantum=(unsigned int) (*p++) << 24;
1572 *quantum|=(unsigned int) (*p++) << 16;
1573 *quantum|=(unsigned int) (*p++) << 8;
1574 *quantum|=(unsigned int) (*p++);
1575 return(p);
1576}
1577
1578static inline const unsigned char *ReadResourceShort(const unsigned char *p,
1579 unsigned short *quantum)
1580{
1581 *quantum=(unsigned short) (*p++) << 8;
1582 *quantum|=(unsigned short) (*p++);
1583 return(p);
1584}
1585
1586static inline void WriteResourceLong(unsigned char *p,
1587 const unsigned int quantum)
1588{
1589 unsigned char
1590 buffer[4];
1591
1592 buffer[0]=(unsigned char) (quantum >> 24);
1593 buffer[1]=(unsigned char) (quantum >> 16);
1594 buffer[2]=(unsigned char) (quantum >> 8);
1595 buffer[3]=(unsigned char) quantum;
1596 (void) memcpy(p,buffer,4);
1597}
1598
1599static void WriteTo8BimProfile(Image *image,const char *name,
1600 const StringInfo *profile)
1601{
1602 const unsigned char
1603 *datum,
1604 *q;
1605
1606 const unsigned char
1607 *p;
1608
1609 size_t
1610 length;
1611
1613 *profile_8bim;
1614
1615 ssize_t
1616 count;
1617
1618 unsigned char
1619 length_byte;
1620
1621 unsigned int
1622 value;
1623
1624 unsigned short
1625 id,
1626 profile_id;
1627
1628 if (LocaleCompare(name,"icc") == 0)
1629 profile_id=0x040f;
1630 else
1631 if (LocaleCompare(name,"iptc") == 0)
1632 profile_id=0x0404;
1633 else
1634 if (LocaleCompare(name,"xmp") == 0)
1635 profile_id=0x0424;
1636 else
1637 return;
1638 profile_8bim=(StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
1639 image->profiles,"8bim");
1640 if (profile_8bim == (StringInfo *) NULL)
1641 return;
1642 datum=GetStringInfoDatum(profile_8bim);
1643 length=GetStringInfoLength(profile_8bim);
1644 for (p=datum; p < (datum+length-16); )
1645 {
1646 q=p;
1647 if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1648 break;
1649 p+=4;
1650 p=ReadResourceShort(p,&id);
1651 p=ReadResourceByte(p,&length_byte);
1652 p+=length_byte;
1653 if (((length_byte+1) & 0x01) != 0)
1654 p++;
1655 if (p > (datum+length-4))
1656 break;
1657 p=ReadResourceLong(p,&value);
1658 count=(ssize_t) value;
1659 if ((count & 0x01) != 0)
1660 count++;
1661 if ((count < 0) || (p > (datum+length-count)) || (count > (ssize_t) length))
1662 break;
1663 if (id != profile_id)
1664 p+=count;
1665 else
1666 {
1667 size_t
1668 extent,
1669 offset;
1670
1671 ssize_t
1672 extract_extent;
1673
1675 *extract_profile;
1676
1677 extract_extent=0;
1678 extent=(size_t) ((datum+length)-(p+count));
1679 if (profile == (StringInfo *) NULL)
1680 {
1681 offset=(size_t) (q-datum);
1682 extract_profile=AcquireStringInfo(offset+extent);
1683 (void) memcpy(extract_profile->datum,datum,offset);
1684 }
1685 else
1686 {
1687 offset=(size_t) (p-datum);
1688 extract_extent=(ssize_t) profile->length;
1689 if ((extract_extent & 0x01) != 0)
1690 extract_extent++;
1691 extract_profile=AcquireStringInfo(offset+(size_t) extract_extent+
1692 extent);
1693 (void) memcpy(extract_profile->datum,datum,offset-4);
1694 WriteResourceLong(extract_profile->datum+offset-4,(unsigned int)
1695 profile->length);
1696 (void) memcpy(extract_profile->datum+offset,
1697 profile->datum,profile->length);
1698 }
1699 (void) memcpy(extract_profile->datum+offset+extract_extent,
1700 p+count,extent);
1701 (void) AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1702 ConstantString("8bim"),CloneStringInfo(extract_profile));
1703 extract_profile=DestroyStringInfo(extract_profile);
1704 break;
1705 }
1706 }
1707}
1708
1709static void GetProfilesFromResourceBlock(Image *image,
1710 const StringInfo *resource_block,ExceptionInfo *exception)
1711{
1712 const unsigned char
1713 *datum;
1714
1715 const unsigned char
1716 *p;
1717
1718 size_t
1719 length;
1720
1721 ssize_t
1722 count;
1723
1725 *profile;
1726
1727 unsigned char
1728 length_byte;
1729
1730 unsigned int
1731 value;
1732
1733 unsigned short
1734 id;
1735
1736 datum=GetStringInfoDatum(resource_block);
1737 length=GetStringInfoLength(resource_block);
1738 for (p=datum; p < (datum+length-16); )
1739 {
1740 if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1741 break;
1742 p+=4;
1743 p=ReadResourceShort(p,&id);
1744 p=ReadResourceByte(p,&length_byte);
1745 p+=length_byte;
1746 if (((length_byte+1) & 0x01) != 0)
1747 p++;
1748 if (p > (datum+length-4))
1749 break;
1750 p=ReadResourceLong(p,&value);
1751 count=(ssize_t) value;
1752 if ((p > (datum+length-count)) || (count > (ssize_t) length) ||
1753 (count <= 0))
1754 break;
1755 switch (id)
1756 {
1757 case 0x03ed:
1758 {
1759 unsigned int
1760 resolution;
1761
1762 unsigned short
1763 units;
1764
1765 /*
1766 Resolution.
1767 */
1768 if (count < 10)
1769 break;
1770 p=ReadResourceLong(p,&resolution);
1771 image->resolution.x=((double) resolution)/65536.0;
1772 p=ReadResourceShort(p,&units)+2;
1773 p=ReadResourceLong(p,&resolution)+4;
1774 image->resolution.y=((double) resolution)/65536.0;
1775 /*
1776 Values are always stored as pixels per inch.
1777 */
1778 if ((ResolutionType) units != PixelsPerCentimeterResolution)
1779 image->units=PixelsPerInchResolution;
1780 else
1781 {
1782 image->units=PixelsPerCentimeterResolution;
1783 image->resolution.x/=2.54;
1784 image->resolution.y/=2.54;
1785 }
1786 break;
1787 }
1788 case 0x0404:
1789 {
1790 /*
1791 IPTC profile.
1792 */
1793 profile=AcquireStringInfo((size_t) count);
1794 SetStringInfoDatum(profile,p);
1795 (void) SetImageProfileInternal(image,"iptc",profile,MagickTrue,
1796 exception);
1797 profile=DestroyStringInfo(profile);
1798 p+=count;
1799 break;
1800 }
1801 case 0x040c:
1802 {
1803 /*
1804 Thumbnail.
1805 */
1806 p+=count;
1807 break;
1808 }
1809 case 0x040f:
1810 {
1811 /*
1812 ICC Profile.
1813 */
1814 profile=AcquireStringInfo((size_t) count);
1815 SetStringInfoDatum(profile,p);
1816 (void) SetImageProfileInternal(image,"icc",profile,MagickTrue,
1817 exception);
1818 profile=DestroyStringInfo(profile);
1819 p+=count;
1820 break;
1821 }
1822 case 0x0422:
1823 {
1824 /*
1825 EXIF Profile.
1826 */
1827 profile=AcquireStringInfo((size_t) count);
1828 SetStringInfoDatum(profile,p);
1829 (void) SetImageProfileInternal(image,"exif",profile,MagickTrue,
1830 exception);
1831 profile=DestroyStringInfo(profile);
1832 p+=count;
1833 break;
1834 }
1835 case 0x0424:
1836 {
1837 /*
1838 XMP Profile.
1839 */
1840 profile=AcquireStringInfo((size_t) count);
1841 SetStringInfoDatum(profile,p);
1842 (void) SetImageProfileInternal(image,"xmp",profile,MagickTrue,
1843 exception);
1844 profile=DestroyStringInfo(profile);
1845 p+=count;
1846 break;
1847 }
1848 default:
1849 {
1850 p+=count;
1851 break;
1852 }
1853 }
1854 if ((count & 0x01) != 0)
1855 p++;
1856 }
1857}
1858
1859static void PatchCorruptProfile(const char *name,StringInfo *profile)
1860{
1861 unsigned char
1862 *p;
1863
1864 size_t
1865 length;
1866
1867 /*
1868 Detect corrupt profiles and if discovered, repair.
1869 */
1870 if (LocaleCompare(name,"xmp") == 0)
1871 {
1872 /*
1873 Remove garbage after xpacket end.
1874 */
1875 p=GetStringInfoDatum(profile);
1876 p=(unsigned char *) strstr((const char *) p,"<?xpacket end=\"w\"?>");
1877 if (p != (unsigned char *) NULL)
1878 {
1879 p+=19;
1880 length=(size_t) (p-GetStringInfoDatum(profile));
1881 if (length != GetStringInfoLength(profile))
1882 {
1883 *p='\0';
1884 SetStringInfoLength(profile,length);
1885 }
1886 }
1887 return;
1888 }
1889 if (LocaleCompare(name,"exif") == 0)
1890 {
1891 /*
1892 Check if profile starts with byte order marker instead of Exif.
1893 */
1894 p=GetStringInfoDatum(profile);
1895 if ((LocaleNCompare((const char *) p,"MM",2) == 0) ||
1896 (LocaleNCompare((const char *) p,"II",2) == 0))
1897 {
1898 const unsigned char
1899 profile_start[] = "Exif\0\0";
1900
1902 *exif_profile;
1903
1904 exif_profile=AcquireStringInfo(6);
1905 if (exif_profile != (StringInfo *) NULL)
1906 {
1907 SetStringInfoDatum(exif_profile,profile_start);
1908 ConcatenateStringInfo(exif_profile,profile);
1909 SetStringInfoLength(profile,GetStringInfoLength(exif_profile));
1910 SetStringInfo(profile,exif_profile);
1911 exif_profile=DestroyStringInfo(exif_profile);
1912 }
1913 }
1914 }
1915}
1916
1917static MagickBooleanType ValidateXMPProfile(Image *image,
1918 const StringInfo *profile,ExceptionInfo *exception)
1919{
1920#if defined(MAGICKCORE_XML_DELEGATE)
1921 xmlDocPtr
1922 document;
1923
1924 /*
1925 Validate XMP profile.
1926 */
1927 const char *artifact=GetImageArtifact(image,"xmp:validate");
1928 if (IsStringTrue(artifact) == MagickFalse)
1929 return(MagickTrue);
1930 document=xmlReadMemory((const char *) GetStringInfoDatum(profile),(int)
1931 GetStringInfoLength(profile),"xmp.xml",NULL,XML_PARSE_NOERROR |
1932 XML_PARSE_NOWARNING);
1933 if (document == (xmlDocPtr) NULL)
1934 {
1935 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1936 "CorruptImageProfile","`%s' (XMP)",image->filename);
1937 return(MagickFalse);
1938 }
1939 xmlFreeDoc(document);
1940 return(MagickTrue);
1941#else
1942 (void) profile;
1943 (void) ThrowMagickException(exception,GetMagickModule(),
1944 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (XML)",
1945 image->filename);
1946 return(MagickFalse);
1947#endif
1948}
1949
1950static MagickBooleanType SetImageProfileInternal(Image *image,const char *name,
1951 const StringInfo *profile,const MagickBooleanType recursive,
1952 ExceptionInfo *exception)
1953{
1954 char
1955 key[MagickPathExtent];
1956
1957 MagickBooleanType
1958 status;
1959
1961 *clone_profile;
1962
1963 assert(image != (Image *) NULL);
1964 assert(image->signature == MagickCoreSignature);
1965 if (IsEventLogging() != MagickFalse)
1966 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1967 clone_profile=CloneStringInfo(profile);
1968 PatchCorruptProfile(name,clone_profile);
1969 if ((LocaleCompare(name,"xmp") == 0) &&
1970 (ValidateXMPProfile(image,clone_profile,exception) == MagickFalse))
1971 {
1972 clone_profile=DestroyStringInfo(clone_profile);
1973 return(MagickTrue);
1974 }
1975 if (image->profiles == (SplayTreeInfo *) NULL)
1976 image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
1977 DestroyProfile);
1978 (void) CopyMagickString(key,name,MagickPathExtent);
1979 LocaleLower(key);
1980 status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1981 ConstantString(key),clone_profile);
1982 if (status != MagickFalse)
1983 {
1984 if (LocaleCompare(name,"8bim") == 0)
1985 GetProfilesFromResourceBlock(image,clone_profile,exception);
1986 else
1987 if (recursive == MagickFalse)
1988 WriteTo8BimProfile(image,name,clone_profile);
1989 }
1990 return(status);
1991}
1992
1993MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
1994 const StringInfo *profile,ExceptionInfo *exception)
1995{
1996 return(SetImageProfileInternal(image,name,profile,MagickFalse,exception));
1997}
1998
1999/*
2000%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2001% %
2002% %
2003% %
2004% S y n c I m a g e P r o f i l e s %
2005% %
2006% %
2007% %
2008%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2009%
2010% SyncImageProfiles() synchronizes image properties with the image profiles.
2011%
2012% The format of the SyncImageProfiles method is:
2013%
2014% void SyncImageProfiles(Image *image)
2015%
2016% A description of each parameter follows:
2017%
2018% o image: the image.
2019%
2020*/
2021
2022static inline int ReadProfileByte(unsigned char **p,size_t *length)
2023{
2024 int
2025 c;
2026
2027 if (*length < 1)
2028 return(EOF);
2029 c=(int) (*(*p)++);
2030 (*length)--;
2031 return(c);
2032}
2033
2034static inline signed short ReadProfileShort(const EndianType endian,
2035 unsigned char *buffer)
2036{
2037 union
2038 {
2039 unsigned int
2040 unsigned_value;
2041
2042 signed int
2043 signed_value;
2044 } quantum;
2045
2046 unsigned short
2047 value;
2048
2049 if (endian == LSBEndian)
2050 {
2051 value=(unsigned short) buffer[1] << 8;
2052 value|=(unsigned short) buffer[0];
2053 quantum.unsigned_value=value & 0xffff;
2054 return(quantum.signed_value);
2055 }
2056 value=(unsigned short) buffer[0] << 8;
2057 value|=(unsigned short) buffer[1];
2058 quantum.unsigned_value=value & 0xffff;
2059 return(quantum.signed_value);
2060}
2061
2062static inline signed int ReadProfileLong(const EndianType endian,
2063 unsigned char *buffer)
2064{
2065 union
2066 {
2067 unsigned int
2068 unsigned_value;
2069
2070 signed int
2071 signed_value;
2072 } quantum;
2073
2074 unsigned int
2075 value;
2076
2077 if (endian == LSBEndian)
2078 {
2079 value=(unsigned int) buffer[3] << 24;
2080 value|=(unsigned int) buffer[2] << 16;
2081 value|=(unsigned int) buffer[1] << 8;
2082 value|=(unsigned int) buffer[0];
2083 quantum.unsigned_value=value & 0xffffffff;
2084 return(quantum.signed_value);
2085 }
2086 value=(unsigned int) buffer[0] << 24;
2087 value|=(unsigned int) buffer[1] << 16;
2088 value|=(unsigned int) buffer[2] << 8;
2089 value|=(unsigned int) buffer[3];
2090 quantum.unsigned_value=value & 0xffffffff;
2091 return(quantum.signed_value);
2092}
2093
2094static inline signed int ReadProfileMSBLong(unsigned char **p,size_t *length)
2095{
2096 signed int
2097 value;
2098
2099 if (*length < 4)
2100 return(0);
2101 value=ReadProfileLong(MSBEndian,*p);
2102 (*length)-=4;
2103 *p+=4;
2104 return(value);
2105}
2106
2107static inline signed short ReadProfileMSBShort(unsigned char **p,
2108 size_t *length)
2109{
2110 signed short
2111 value;
2112
2113 if (*length < 2)
2114 return(0);
2115 value=ReadProfileShort(MSBEndian,*p);
2116 (*length)-=2;
2117 *p+=2;
2118 return(value);
2119}
2120
2121static inline void WriteProfileLong(const EndianType endian,
2122 const size_t value,unsigned char *p)
2123{
2124 unsigned char
2125 buffer[4];
2126
2127 if (endian == LSBEndian)
2128 {
2129 buffer[0]=(unsigned char) value;
2130 buffer[1]=(unsigned char) (value >> 8);
2131 buffer[2]=(unsigned char) (value >> 16);
2132 buffer[3]=(unsigned char) (value >> 24);
2133 (void) memcpy(p,buffer,4);
2134 return;
2135 }
2136 buffer[0]=(unsigned char) (value >> 24);
2137 buffer[1]=(unsigned char) (value >> 16);
2138 buffer[2]=(unsigned char) (value >> 8);
2139 buffer[3]=(unsigned char) value;
2140 (void) memcpy(p,buffer,4);
2141}
2142
2143static void WriteProfileShort(const EndianType endian,
2144 const unsigned short value,unsigned char *p)
2145{
2146 unsigned char
2147 buffer[2];
2148
2149 if (endian == LSBEndian)
2150 {
2151 buffer[0]=(unsigned char) value;
2152 buffer[1]=(unsigned char) (value >> 8);
2153 (void) memcpy(p,buffer,2);
2154 return;
2155 }
2156 buffer[0]=(unsigned char) (value >> 8);
2157 buffer[1]=(unsigned char) value;
2158 (void) memcpy(p,buffer,2);
2159}
2160
2161static void SyncExifProfile(const Image *image,unsigned char *exif,
2162 size_t length)
2163{
2164#define MaxDirectoryStack 16
2165#define EXIF_DELIMITER "\n"
2166#define EXIF_NUM_FORMATS 12
2167#define TAG_EXIF_OFFSET 0x8769
2168#define TAG_INTEROP_OFFSET 0xa005
2169
2170 typedef struct _DirectoryInfo
2171 {
2172 unsigned char
2173 *directory;
2174
2175 size_t
2176 entry;
2177 } DirectoryInfo;
2178
2179 DirectoryInfo
2180 directory_stack[MaxDirectoryStack] = { { 0, 0 } };
2181
2182 EndianType
2183 endian;
2184
2185 size_t
2186 entry,
2187 number_entries;
2188
2190 *exif_resources;
2191
2192 ssize_t
2193 id,
2194 level,
2195 offset;
2196
2197 static int
2198 format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
2199
2200 unsigned char
2201 *directory;
2202
2203 if (length < 16)
2204 return;
2205 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
2206 if ((id != 0x4949) && (id != 0x4D4D))
2207 {
2208 while (length != 0)
2209 {
2210 if (ReadProfileByte(&exif,&length) != 0x45)
2211 continue;
2212 if (ReadProfileByte(&exif,&length) != 0x78)
2213 continue;
2214 if (ReadProfileByte(&exif,&length) != 0x69)
2215 continue;
2216 if (ReadProfileByte(&exif,&length) != 0x66)
2217 continue;
2218 if (ReadProfileByte(&exif,&length) != 0x00)
2219 continue;
2220 if (ReadProfileByte(&exif,&length) != 0x00)
2221 continue;
2222 break;
2223 }
2224 if (length < 16)
2225 return;
2226 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
2227 }
2228 endian=LSBEndian;
2229 if (id == 0x4949)
2230 endian=LSBEndian;
2231 else
2232 if (id == 0x4D4D)
2233 endian=MSBEndian;
2234 else
2235 return;
2236 if (ReadProfileShort(endian,exif+2) != 0x002a)
2237 return;
2238 /*
2239 This the offset to the first IFD.
2240 */
2241 offset=(ssize_t) ReadProfileLong(endian,exif+4);
2242 if ((offset < 0) || ((size_t) offset >= length))
2243 return;
2244 directory=exif+offset;
2245 level=0;
2246 entry=0;
2247 exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
2248 (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
2249 do
2250 {
2251 if (level > 0)
2252 {
2253 level--;
2254 directory=directory_stack[level].directory;
2255 entry=directory_stack[level].entry;
2256 }
2257 if ((directory < exif) || (directory > (exif+length-2)))
2258 break;
2259 /*
2260 Determine how many entries there are in the current IFD.
2261 */
2262 number_entries=(size_t) ReadProfileShort(endian,directory);
2263 for ( ; entry < number_entries; entry++)
2264 {
2265 int
2266 components;
2267
2268 unsigned char
2269 *p,
2270 *q;
2271
2272 size_t
2273 number_bytes;
2274
2275 ssize_t
2276 format,
2277 tag_value;
2278
2279 q=(unsigned char *) (directory+2+(12*entry));
2280 if (q > (exif+length-12))
2281 break; /* corrupt EXIF */
2282 if (GetValueFromSplayTree(exif_resources,q) == q)
2283 break;
2284 (void) AddValueToSplayTree(exif_resources,q,q);
2285 tag_value=(ssize_t) ReadProfileShort(endian,q);
2286 format=(ssize_t) ReadProfileShort(endian,q+2);
2287 if ((format < 0) || ((format-1) >= EXIF_NUM_FORMATS))
2288 break;
2289 components=(int) ReadProfileLong(endian,q+4);
2290 if (components < 0)
2291 break; /* corrupt EXIF */
2292 number_bytes=(size_t) components*(size_t) format_bytes[format];
2293 if ((ssize_t) number_bytes < components)
2294 break; /* prevent overflow */
2295 if (number_bytes <= 4)
2296 p=q+8;
2297 else
2298 {
2299 /*
2300 The directory entry contains an offset.
2301 */
2302 offset=(ssize_t) ReadProfileLong(endian,q+8);
2303 if ((offset < 0) ||
2304 ((size_t) (offset+(ssize_t) number_bytes) > length))
2305 continue;
2306 if (~length < number_bytes)
2307 continue; /* prevent overflow */
2308 p=(unsigned char *) (exif+offset);
2309 }
2310 switch (tag_value)
2311 {
2312 case 0x011a:
2313 {
2314 (void) WriteProfileLong(endian,(size_t) (image->resolution.x+0.5),p);
2315 if (number_bytes == 8)
2316 (void) WriteProfileLong(endian,1UL,p+4);
2317 break;
2318 }
2319 case 0x011b:
2320 {
2321 (void) WriteProfileLong(endian,(size_t) (image->resolution.y+0.5),p);
2322 if (number_bytes == 8)
2323 (void) WriteProfileLong(endian,1UL,p+4);
2324 break;
2325 }
2326 case 0x0112:
2327 {
2328 if (number_bytes == 4)
2329 {
2330 (void) WriteProfileLong(endian,(size_t) image->orientation,p);
2331 break;
2332 }
2333 (void) WriteProfileShort(endian,(unsigned short) image->orientation,
2334 p);
2335 break;
2336 }
2337 case 0x0128:
2338 {
2339 if (number_bytes == 4)
2340 {
2341 (void) WriteProfileLong(endian,((size_t) image->units)+1,p);
2342 break;
2343 }
2344 (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
2345 break;
2346 }
2347 default:
2348 break;
2349 }
2350 if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
2351 {
2352 offset=(ssize_t) ReadProfileLong(endian,p);
2353 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
2354 {
2355 directory_stack[level].directory=directory;
2356 entry++;
2357 directory_stack[level].entry=entry;
2358 level++;
2359 directory_stack[level].directory=exif+offset;
2360 directory_stack[level].entry=0;
2361 level++;
2362 if ((directory+2+(12*number_entries)) > (exif+length))
2363 break;
2364 offset=(ssize_t) ReadProfileLong(endian,directory+2+(12*
2365 number_entries));
2366 if ((offset != 0) && ((size_t) offset < length) &&
2367 (level < (MaxDirectoryStack-2)))
2368 {
2369 directory_stack[level].directory=exif+offset;
2370 directory_stack[level].entry=0;
2371 level++;
2372 }
2373 }
2374 break;
2375 }
2376 }
2377 } while (level > 0);
2378 exif_resources=DestroySplayTree(exif_resources);
2379 return;
2380}
2381
2382static void Sync8BimProfile(const Image *image,const StringInfo *profile)
2383{
2384 size_t
2385 length;
2386
2387 ssize_t
2388 count;
2389
2390 unsigned char
2391 *p;
2392
2393 unsigned short
2394 id;
2395
2396 length=GetStringInfoLength(profile);
2397 p=GetStringInfoDatum(profile);
2398 while (length != 0)
2399 {
2400 if (ReadProfileByte(&p,&length) != 0x38)
2401 continue;
2402 if (ReadProfileByte(&p,&length) != 0x42)
2403 continue;
2404 if (ReadProfileByte(&p,&length) != 0x49)
2405 continue;
2406 if (ReadProfileByte(&p,&length) != 0x4D)
2407 continue;
2408 if (length < 7)
2409 return;
2410 id=(unsigned short) ReadProfileMSBShort(&p,&length);
2411 count=(ssize_t) ReadProfileByte(&p,&length);
2412 if ((count >= (ssize_t) length) || (count < 0))
2413 return;
2414 p+=count;
2415 length-=(size_t) count;
2416 if ((*p & 0x01) == 0)
2417 (void) ReadProfileByte(&p,&length);
2418 count=(ssize_t) ReadProfileMSBLong(&p,&length);
2419 if ((count > (ssize_t) length) || (count < 0))
2420 return;
2421 if ((id == 0x3ED) && (count == 16))
2422 {
2423 if (image->units == PixelsPerCentimeterResolution)
2424 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2425 image->resolution.x*2.54*65536.0),p);
2426 else
2427 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2428 image->resolution.x*65536.0),p);
2429 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+4);
2430 if (image->units == PixelsPerCentimeterResolution)
2431 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2432 image->resolution.y*2.54*65536.0),p+8);
2433 else
2434 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2435 image->resolution.y*65536.0),p+8);
2436 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+12);
2437 }
2438 if (id == 0x0422)
2439 SyncExifProfile(image,p,(size_t) count);
2440 p+=count;
2441 length-=(size_t) count;
2442 }
2443 return;
2444}
2445
2446static void ReplaceXmpValue(StringInfo *profile,size_t start,size_t end,
2447 const char *value)
2448{
2449 char
2450 *datum;
2451
2452 size_t
2453 length,
2454 new_length,
2455 value_length;
2456
2457 length=GetStringInfoLength(profile);
2458 value_length=strlen(value);
2459 new_length=length-(end-start)+value_length;
2460 if (new_length > length)
2461 SetStringInfoLength(profile,new_length);
2462 datum=(char *) GetStringInfoDatum(profile);
2463 (void) memmove(datum+start+value_length,datum+end,length-end);
2464 (void) memcpy(datum+start,value,value_length);
2465 if (new_length < length)
2466 {
2467 SetStringInfoLength(profile,new_length);
2468 datum=(char *) GetStringInfoDatum(profile);
2469 *(datum+new_length)='\0';
2470 }
2471}
2472
2473static MagickBooleanType GetXmpOffsets(const StringInfo *profile,
2474 const char *tag,size_t *start,size_t *end)
2475{
2476 char
2477 *datum,
2478 *pos;
2479
2480 size_t
2481 length,
2482 tag_length;
2483
2484 datum=(char *) GetStringInfoDatum(profile);
2485 length=GetStringInfoLength(profile);
2486 pos=strstr(datum,tag);
2487 tag_length=strlen(tag);
2488 if ((pos == (char *) NULL) || ((pos-datum) < 1) || (*(pos-1) != '<') ||
2489 (((size_t) (pos-datum)+tag_length) > length) ||
2490 (*(pos+tag_length) != '>'))
2491 return(MagickFalse);
2492 *start=(size_t) (pos-datum)+tag_length+1;
2493 pos=strstr(datum+*start,"<");
2494 if (pos == (char *) NULL)
2495 return(MagickFalse);
2496 *end=(size_t) (pos-datum);
2497 return(MagickTrue);
2498}
2499
2500static void GetXmpNumeratorAndDenominator(double value,
2501 unsigned long *numerator,unsigned long *denominator)
2502{
2503 double
2504 df;
2505
2506 *numerator=0;
2507 *denominator=1;
2508 if (value <= MagickEpsilon)
2509 return;
2510 *numerator=1;
2511 df=1.0;
2512 while(fabs(df - value) > MagickEpsilon)
2513 {
2514 if (df < value)
2515 (*numerator)++;
2516 else
2517 {
2518 (*denominator)++;
2519 *numerator=(unsigned long) (value*(*denominator));
2520 }
2521 df=*numerator/(double)*denominator;
2522 }
2523}
2524
2525static void SyncXmpProfile(const Image *image,StringInfo *profile)
2526{
2527 char
2528 value[MagickPathExtent];
2529
2530 size_t
2531 end,
2532 start;
2533
2534 unsigned long
2535 denominator,
2536 numerator;
2537
2538 *value='\0';
2539 if (GetXmpOffsets(profile,"tiff:XResolution",&start,&end) != MagickFalse)
2540 {
2541 GetXmpNumeratorAndDenominator(image->resolution.x,&numerator,
2542 &denominator);
2543 (void) FormatLocaleString(value,MagickPathExtent,"%lu/%lu",numerator,
2544 denominator);
2545 ReplaceXmpValue(profile,start,end,value);
2546 }
2547 if (GetXmpOffsets(profile,"tiff:YResolution",&start,&end) != MagickFalse)
2548 {
2549 if ((fabs(image->resolution.x-image->resolution.y) > MagickEpsilon) ||
2550 (*value == '\0'))
2551 {
2552 GetXmpNumeratorAndDenominator(image->resolution.y,&numerator,
2553 &denominator);
2554 (void) FormatLocaleString(value,MagickPathExtent,"%lu/%lu",
2555 numerator,denominator);
2556 }
2557 ReplaceXmpValue(profile,start,end,value);
2558 }
2559 if (GetXmpOffsets(profile,"tiff:ResolutionUnit",&start,&end) != MagickFalse)
2560 {
2561 (void) FormatLocaleString(value,MagickPathExtent,"%d",
2562 ((int) image->units)+1);
2563 ReplaceXmpValue(profile,start,end,value);
2564 }
2565 if (GetXmpOffsets(profile,"tiff:Orientation",&start,&end) != MagickFalse)
2566 {
2567 (void) FormatLocaleString(value,MagickPathExtent,"%d",
2568 (int) image->orientation);
2569 ReplaceXmpValue(profile,start,end,value);
2570 }
2571}
2572
2573MagickPrivate void SyncImageProfiles(Image *image)
2574{
2576 *profile;
2577
2578 profile=(StringInfo *) GetImageProfile(image,"8BIM");
2579 if (profile != (StringInfo *) NULL)
2580 Sync8BimProfile(image,profile);
2581 profile=(StringInfo *) GetImageProfile(image,"EXIF");
2582 if (profile != (StringInfo *) NULL)
2583 SyncExifProfile(image,GetStringInfoDatum(profile),GetStringInfoLength(
2584 profile));
2585 profile=(StringInfo *) GetImageProfile(image,"XMP");
2586 if (profile != (StringInfo *) NULL)
2587 SyncXmpProfile(image,profile);
2588}
2589
2590static void UpdateClipPath(unsigned char *blob,size_t length,
2591 const size_t old_columns,const size_t old_rows,
2592 const RectangleInfo *new_geometry)
2593{
2594 ssize_t
2595 i,
2596 knot_count,
2597 selector;
2598
2599 knot_count=0;
2600 while (length != 0)
2601 {
2602 selector=(ssize_t) ReadProfileMSBShort(&blob,&length);
2603 switch (selector)
2604 {
2605 case 0:
2606 case 3:
2607 {
2608 if (knot_count != 0)
2609 {
2610 blob+=24;
2611 length-=(size_t) MagickMin(length,24U);
2612 break;
2613 }
2614 /*
2615 Expected subpath length record.
2616 */
2617 knot_count=(ssize_t) ReadProfileMSBShort(&blob,&length);
2618 blob+=22;
2619 length-=(size_t) MagickMin(length,22);
2620 break;
2621 }
2622 case 1:
2623 case 2:
2624 case 4:
2625 case 5:
2626 {
2627 if (knot_count == 0)
2628 {
2629 /*
2630 Unexpected subpath knot.
2631 */
2632 blob+=24;
2633 length-=(size_t) MagickMin(length,24);
2634 break;
2635 }
2636 /*
2637 Add sub-path knot
2638 */
2639 for (i=0; i < 3; i++)
2640 {
2641 double
2642 x,
2643 y;
2644
2645 signed int
2646 xx,
2647 yy;
2648
2649 y=(double) ReadProfileMSBLong(&blob,&length);
2650 y=y*old_rows/4096.0/4096.0;
2651 y-=new_geometry->y;
2652 yy=(signed int) ((y*4096*4096)/new_geometry->height);
2653 WriteProfileLong(MSBEndian,(size_t) yy,blob-4);
2654 x=(double) ReadProfileMSBLong(&blob,&length);
2655 x=x*old_columns/4096.0/4096.0;
2656 x-=new_geometry->x;
2657 xx=(signed int) ((x*4096*4096)/new_geometry->width);
2658 WriteProfileLong(MSBEndian,(size_t) xx,blob-4);
2659 }
2660 knot_count--;
2661 break;
2662 }
2663 case 6:
2664 case 7:
2665 case 8:
2666 default:
2667 {
2668 blob+=24;
2669 length-=(size_t) MagickMin(length,24);
2670 break;
2671 }
2672 }
2673 }
2674}
2675
2676MagickPrivate void Update8BIMClipPath(const Image *image,
2677 const size_t old_columns,const size_t old_rows,
2678 const RectangleInfo *new_geometry)
2679{
2680 const StringInfo
2681 *profile;
2682
2683 size_t
2684 length;
2685
2686 ssize_t
2687 count,
2688 id;
2689
2690 unsigned char
2691 *info;
2692
2693 assert(image != (Image *) NULL);
2694 assert(new_geometry != (RectangleInfo *) NULL);
2695 profile=GetImageProfile(image,"8bim");
2696 if (profile == (StringInfo *) NULL)
2697 return;
2698 length=GetStringInfoLength(profile);
2699 info=GetStringInfoDatum(profile);
2700 while (length > 0)
2701 {
2702 if (ReadProfileByte(&info,&length) != (unsigned char) '8')
2703 continue;
2704 if (ReadProfileByte(&info,&length) != (unsigned char) 'B')
2705 continue;
2706 if (ReadProfileByte(&info,&length) != (unsigned char) 'I')
2707 continue;
2708 if (ReadProfileByte(&info,&length) != (unsigned char) 'M')
2709 continue;
2710 id=(ssize_t) ReadProfileMSBShort(&info,&length);
2711 count=(ssize_t) ReadProfileByte(&info,&length);
2712 if ((count != 0) && ((size_t) count <= length))
2713 {
2714 info+=count;
2715 length-=(size_t) count;
2716 }
2717 if ((count & 0x01) == 0)
2718 (void) ReadProfileByte(&info,&length);
2719 count=(ssize_t) ReadProfileMSBLong(&info,&length);
2720 if ((count < 0) || ((size_t) count > length))
2721 {
2722 length=0;
2723 continue;
2724 }
2725 if ((id > 1999) && (id < 2999))
2726 UpdateClipPath(info,(size_t) count,old_columns,old_rows,new_geometry);
2727 info+=count;
2728 length-=(size_t) MagickMin(length,(size_t) count);
2729 }
2730}