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