MagickCore 7.1.2-24
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/license/ %
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 else
1454 profile=DestroyStringInfo(profile);
1455 if (target_info.profile != (cmsHPROFILE) NULL)
1456 (void) cmsCloseProfile(target_info.profile);
1457 }
1458 (void) cmsCloseProfile(source_info.profile);
1459 cmsDeleteContext(cms_context);
1460 }
1461#endif
1462 }
1463 return(status);
1464}
1465
1466/*
1467%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1468% %
1469% %
1470% %
1471% R e m o v e I m a g e P r o f i l e %
1472% %
1473% %
1474% %
1475%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1476%
1477% RemoveImageProfile() removes a named profile from the image and returns its
1478% value.
1479%
1480% The format of the RemoveImageProfile method is:
1481%
1482% void *RemoveImageProfile(Image *image,const char *name)
1483%
1484% A description of each parameter follows:
1485%
1486% o image: the image.
1487%
1488% o name: the profile name.
1489%
1490*/
1491MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
1492{
1493 StringInfo
1494 *profile;
1495
1496 assert(image != (Image *) NULL);
1497 assert(image->signature == MagickCoreSignature);
1498 if (IsEventLogging() != MagickFalse)
1499 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1500 if (image->profiles == (SplayTreeInfo *) NULL)
1501 return((StringInfo *) NULL);
1502 WriteTo8BimProfile(image,name,(StringInfo *) NULL);
1503 profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
1504 image->profiles,name);
1505 return(profile);
1506}
1507
1508/*
1509%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1510% %
1511% %
1512% %
1513% R e s e t P r o f i l e I t e r a t o r %
1514% %
1515% %
1516% %
1517%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1518%
1519% ResetImageProfileIterator() resets the image profile iterator. Use it in
1520% conjunction with GetNextImageProfile() to iterate over all the profiles
1521% associated with an image.
1522%
1523% The format of the ResetImageProfileIterator method is:
1524%
1525% void ResetImageProfileIterator(const Image *image)
1526%
1527% A description of each parameter follows:
1528%
1529% o image: the image.
1530%
1531*/
1532MagickExport void ResetImageProfileIterator(const Image *image)
1533{
1534 assert(image != (Image *) NULL);
1535 assert(image->signature == MagickCoreSignature);
1536 if (IsEventLogging() != MagickFalse)
1537 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1538 if (image->profiles == (SplayTreeInfo *) NULL)
1539 return;
1540 ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
1541}
1542
1543/*
1544%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1545% %
1546% %
1547% %
1548% S e t I m a g e P r o f i l e %
1549% %
1550% %
1551% %
1552%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1553%
1554% SetImageProfile() adds a named profile to the image. If a profile with the
1555% same name already exists, it is replaced. This method differs from the
1556% ProfileImage() method in that it does not apply CMS color profiles.
1557%
1558% The format of the SetImageProfile method is:
1559%
1560% MagickBooleanType SetImageProfile(Image *image,const char *name,
1561% const StringInfo *profile)
1562%
1563% A description of each parameter follows:
1564%
1565% o image: the image.
1566%
1567% o name: the profile name, for example icc, exif, and 8bim (8bim is the
1568% Photoshop wrapper for iptc profiles).
1569%
1570% o profile: A StringInfo structure that contains the named profile.
1571%
1572*/
1573
1574static void *DestroyProfile(void *profile)
1575{
1576 return((void *) DestroyStringInfo((StringInfo *) profile));
1577}
1578
1579static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1580 unsigned char *quantum)
1581{
1582 *quantum=(*p++);
1583 return(p);
1584}
1585
1586static inline const unsigned char *ReadResourceLong(const unsigned char *p,
1587 unsigned int *quantum)
1588{
1589 *quantum=(unsigned int) (*p++) << 24;
1590 *quantum|=(unsigned int) (*p++) << 16;
1591 *quantum|=(unsigned int) (*p++) << 8;
1592 *quantum|=(unsigned int) (*p++);
1593 return(p);
1594}
1595
1596static inline const unsigned char *ReadResourceShort(const unsigned char *p,
1597 unsigned short *quantum)
1598{
1599 *quantum=(unsigned short) (*p++) << 8;
1600 *quantum|=(unsigned short) (*p++);
1601 return(p);
1602}
1603
1604static inline void WriteResourceLong(unsigned char *p,
1605 const unsigned int quantum)
1606{
1607 unsigned char
1608 buffer[4];
1609
1610 buffer[0]=(unsigned char) (quantum >> 24);
1611 buffer[1]=(unsigned char) (quantum >> 16);
1612 buffer[2]=(unsigned char) (quantum >> 8);
1613 buffer[3]=(unsigned char) quantum;
1614 (void) memcpy(p,buffer,4);
1615}
1616
1617static void WriteTo8BimProfile(Image *image,const char *name,
1618 const StringInfo *profile)
1619{
1620 const unsigned char
1621 *datum,
1622 *q;
1623
1624 const unsigned char
1625 *p;
1626
1627 size_t
1628 length;
1629
1630 StringInfo
1631 *profile_8bim;
1632
1633 ssize_t
1634 count;
1635
1636 unsigned char
1637 length_byte;
1638
1639 unsigned int
1640 value;
1641
1642 unsigned short
1643 id,
1644 profile_id;
1645
1646 if (LocaleCompare(name,"icc") == 0)
1647 profile_id=0x040f;
1648 else
1649 if (LocaleCompare(name,"iptc") == 0)
1650 profile_id=0x0404;
1651 else
1652 if (LocaleCompare(name,"xmp") == 0)
1653 profile_id=0x0424;
1654 else
1655 return;
1656 profile_8bim=(StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
1657 image->profiles,"8bim");
1658 if (profile_8bim == (StringInfo *) NULL)
1659 return;
1660 datum=GetStringInfoDatum(profile_8bim);
1661 length=GetStringInfoLength(profile_8bim);
1662 for (p=datum; p < (datum+length-16); )
1663 {
1664 q=p;
1665 if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1666 break;
1667 p+=(ptrdiff_t) 4;
1668 p=ReadResourceShort(p,&id);
1669 p=ReadResourceByte(p,&length_byte);
1670 p+=(ptrdiff_t) length_byte;
1671 if (((length_byte+1) & 0x01) != 0)
1672 p++;
1673 if (p > (datum+length-4))
1674 break;
1675 p=ReadResourceLong(p,&value);
1676 count=(ssize_t) value;
1677 if ((count & 0x01) != 0)
1678 count++;
1679 if ((count < 0) || (p > (datum+length-count)) || (count > (ssize_t) length))
1680 break;
1681 if (id != profile_id)
1682 p+=(ptrdiff_t) count;
1683 else
1684 {
1685 size_t
1686 extent,
1687 offset;
1688
1689 ssize_t
1690 extract_extent;
1691
1692 StringInfo
1693 *extract_profile;
1694
1695 extract_extent=0;
1696 extent=(size_t) ((datum+length)-(p+count));
1697 if (profile == (StringInfo *) NULL)
1698 {
1699 offset=(size_t) (q-datum);
1700 extract_profile=AcquireStringInfo(offset+extent);
1701 (void) memcpy(extract_profile->datum,datum,offset);
1702 }
1703 else
1704 {
1705 offset=(size_t) (p-datum);
1706 extract_extent=(ssize_t) profile->length;
1707 if ((extract_extent & 0x01) != 0)
1708 extract_extent++;
1709 extract_profile=AcquireStringInfo(offset+(size_t) extract_extent+
1710 extent);
1711 (void) memcpy(extract_profile->datum,datum,offset-4);
1712 WriteResourceLong(extract_profile->datum+offset-4,(unsigned int)
1713 profile->length);
1714 (void) memcpy(extract_profile->datum+offset,
1715 profile->datum,profile->length);
1716 }
1717 (void) memcpy(extract_profile->datum+offset+extract_extent,
1718 p+count,extent);
1719 (void) AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1720 ConstantString("8bim"),CloneStringInfo(extract_profile));
1721 extract_profile=DestroyStringInfo(extract_profile);
1722 break;
1723 }
1724 }
1725}
1726
1727static void GetProfilesFromResourceBlock(Image *image,
1728 const StringInfo *resource_block,ExceptionInfo *exception)
1729{
1730 const unsigned char
1731 *datum;
1732
1733 const unsigned char
1734 *p;
1735
1736 size_t
1737 length;
1738
1739 ssize_t
1740 count;
1741
1742 StringInfo
1743 *profile;
1744
1745 unsigned char
1746 length_byte;
1747
1748 unsigned int
1749 value;
1750
1751 unsigned short
1752 id;
1753
1754 datum=GetStringInfoDatum(resource_block);
1755 length=GetStringInfoLength(resource_block);
1756 for (p=datum; p < (datum+length-16); )
1757 {
1758 if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1759 break;
1760 p+=(ptrdiff_t) 4;
1761 p=ReadResourceShort(p,&id);
1762 p=ReadResourceByte(p,&length_byte);
1763 p+=(ptrdiff_t) length_byte;
1764 if (((length_byte+1) & 0x01) != 0)
1765 p++;
1766 if (p > (datum+length-4))
1767 break;
1768 p=ReadResourceLong(p,&value);
1769 count=(ssize_t) value;
1770 if ((p > (datum+length-count)) || (count > (ssize_t) length) ||
1771 (count <= 0))
1772 break;
1773 switch (id)
1774 {
1775 case 0x03ed:
1776 {
1777 unsigned int
1778 resolution;
1779
1780 unsigned short
1781 units;
1782
1783 /*
1784 Resolution.
1785 */
1786 if (count < 10)
1787 break;
1788 p=ReadResourceLong(p,&resolution);
1789 image->resolution.x=((double) resolution)/65536.0;
1790 p=ReadResourceShort(p,&units)+2;
1791 p=ReadResourceLong(p,&resolution)+4;
1792 image->resolution.y=((double) resolution)/65536.0;
1793 /*
1794 Values are always stored as pixels per inch.
1795 */
1796 if ((ResolutionType) units != PixelsPerCentimeterResolution)
1797 image->units=PixelsPerInchResolution;
1798 else
1799 {
1800 image->units=PixelsPerCentimeterResolution;
1801 image->resolution.x/=2.54;
1802 image->resolution.y/=2.54;
1803 }
1804 break;
1805 }
1806 case 0x0404:
1807 {
1808 /*
1809 IPTC profile.
1810 */
1811 profile=BlobToProfileStringInfo("iptc",p,(size_t) count,exception);
1812 if (profile != (StringInfo *) NULL)
1813 (void) SetImageProfileInternal(image,GetStringInfoName(profile),
1814 profile,MagickTrue,exception);
1815 p+=(ptrdiff_t) count;
1816 break;
1817 }
1818 case 0x040c:
1819 {
1820 /*
1821 Thumbnail.
1822 */
1823 p+=(ptrdiff_t) count;
1824 break;
1825 }
1826 case 0x040f:
1827 {
1828 /*
1829 ICC Profile.
1830 */
1831 profile=BlobToProfileStringInfo("icc",p,(size_t) count,exception);
1832 if (profile != (StringInfo *) NULL)
1833 (void) SetImageProfileInternal(image,GetStringInfoName(profile),
1834 profile,MagickTrue,exception);
1835 p+=(ptrdiff_t) count;
1836 break;
1837 }
1838 case 0x0422:
1839 {
1840 /*
1841 EXIF Profile.
1842 */
1843 profile=BlobToProfileStringInfo("exif",p,(size_t) count,exception);
1844 if (profile != (StringInfo *) NULL)
1845 (void) SetImageProfileInternal(image,GetStringInfoName(profile),
1846 profile,MagickTrue,exception);
1847 p+=(ptrdiff_t) count;
1848 break;
1849 }
1850 case 0x0424:
1851 {
1852 /*
1853 XMP Profile.
1854 */
1855 profile=BlobToProfileStringInfo("xmp",p,(size_t) count,exception);
1856 if (profile != (StringInfo *) NULL)
1857 (void) SetImageProfileInternal(image,GetStringInfoName(profile),
1858 profile,MagickTrue,exception);
1859 p+=(ptrdiff_t) count;
1860 break;
1861 }
1862 default:
1863 {
1864 p+=(ptrdiff_t) count;
1865 break;
1866 }
1867 }
1868 if ((count & 0x01) != 0)
1869 p++;
1870 }
1871}
1872
1873static void PatchCorruptProfile(const char *name,StringInfo *profile)
1874{
1875 unsigned char
1876 *p;
1877
1878 size_t
1879 length;
1880
1881 /*
1882 Detect corrupt profiles and if discovered, repair.
1883 */
1884 if (LocaleCompare(name,"xmp") == 0)
1885 {
1886 /*
1887 Remove garbage after xpacket end.
1888 */
1889 p=GetStringInfoDatum(profile);
1890 p=(unsigned char *) strstr((const char *) p,"<?xpacket end=\"w\"?>");
1891 if (p != (unsigned char *) NULL)
1892 {
1893 p+=(ptrdiff_t) 19;
1894 length=(size_t) (p-GetStringInfoDatum(profile));
1895 if (length != GetStringInfoLength(profile))
1896 {
1897 *p='\0';
1898 SetStringInfoLength(profile,length);
1899 }
1900 }
1901 return;
1902 }
1903 if (((LocaleCompare(name, "exif") == 0) || (LocaleCompare(name, "app1") == 0)) &&
1904 (GetStringInfoLength(profile) > 2))
1905 {
1906 /*
1907 Check if profile starts with byte order marker instead of Exif.
1908 */
1909 p=GetStringInfoDatum(profile);
1910 if ((LocaleNCompare((const char *) p,"MM",2) == 0) ||
1911 (LocaleNCompare((const char *) p,"II",2) == 0))
1912 {
1913 const unsigned char
1914 profile_start[] = "Exif\0\0";
1915
1916 StringInfo
1917 *exif_profile;
1918
1919 exif_profile=AcquireStringInfo(6);
1920 if (exif_profile != (StringInfo *) NULL)
1921 {
1922 SetStringInfoDatum(exif_profile,profile_start);
1923 ConcatenateStringInfo(exif_profile,profile);
1924 SetStringInfoLength(profile,GetStringInfoLength(exif_profile));
1925 SetStringInfo(profile,exif_profile);
1926 exif_profile=DestroyStringInfo(exif_profile);
1927 }
1928 }
1929 }
1930}
1931
1932static MagickBooleanType ValidateXMPProfile(Image *image,
1933 const StringInfo *profile,ExceptionInfo *exception)
1934{
1935#if defined(MAGICKCORE_XML_DELEGATE)
1936 xmlDocPtr
1937 document;
1938
1939 /*
1940 Validate XMP profile.
1941 */
1942 const char *artifact=GetImageArtifact(image,"xmp:validate");
1943 if (IsStringTrue(artifact) == MagickFalse)
1944 return(MagickTrue);
1945 document=xmlReadMemory((const char *) GetStringInfoDatum(profile),(int)
1946 GetStringInfoLength(profile),"xmp.xml",NULL,XML_PARSE_NOERROR |
1947 XML_PARSE_NOWARNING);
1948 if (document == (xmlDocPtr) NULL)
1949 {
1950 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
1951 "CorruptImageProfile","`%s' (XMP)",image->filename);
1952 return(MagickFalse);
1953 }
1954 xmlFreeDoc(document);
1955 return(MagickTrue);
1956#else
1957 (void) profile;
1958 (void) ThrowMagickException(exception,GetMagickModule(),
1959 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (XML)",
1960 image->filename);
1961 return(MagickFalse);
1962#endif
1963}
1964
1965static MagickBooleanType SetImageProfileInternal(Image *image,const char *name,
1966 StringInfo *profile,const MagickBooleanType recursive,
1967 ExceptionInfo *exception)
1968{
1969 char
1970 key[MagickPathExtent];
1971
1972 MagickBooleanType
1973 status;
1974
1975 size_t
1976 length;
1977
1978 assert(image != (Image *) NULL);
1979 assert(image->signature == MagickCoreSignature);
1980 assert(profile != (StringInfo *) NULL);
1981 assert(name != (const char *) NULL);
1982 if (IsEventLogging() != MagickFalse)
1983 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1984 length=GetStringInfoLength(profile);
1985 if ((length == 0) || (length > GetMaxProfileSize()))
1986 {
1987 if (length != 0)
1988 (void) ThrowMagickException(exception,GetMagickModule(),
1989 ResourceLimitWarning,"ProfileSizeExceedsLimit","`%llu'",
1990 (unsigned long long) length);
1991 profile=DestroyStringInfo(profile);
1992 return(MagickTrue);
1993 }
1994 PatchCorruptProfile(name,profile);
1995 if ((LocaleCompare(name,"xmp") == 0) &&
1996 (ValidateXMPProfile(image,profile,exception) == MagickFalse))
1997 {
1998 profile=DestroyStringInfo(profile);
1999 return(MagickTrue);
2000 }
2001 if (image->profiles == (SplayTreeInfo *) NULL)
2002 image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
2003 DestroyProfile);
2004 (void) CopyMagickString(key,name,MagickPathExtent);
2005 /*
2006 * When an app1 profile starts with an exif header then store it as an exif
2007 * profile instead. The PatchCorruptProfile method already ensures that the
2008 * profile starts with exif instead of MM or II.
2009 */
2010 if ((length > 4) && (LocaleCompare(key,"app1") == 0) &&
2011 (LocaleNCompare((const char *) GetStringInfoDatum(profile),"exif",4) == 0))
2012 (void) CopyMagickString(key,"exif",MagickPathExtent);
2013 else
2014 LocaleLower(key);
2015 status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
2016 ConstantString(key),profile);
2017 if (status == MagickFalse)
2018 profile=DestroyStringInfo(profile);
2019 else
2020 {
2021 if (LocaleCompare(key,"8bim") == 0)
2022 GetProfilesFromResourceBlock(image,profile,exception);
2023 else
2024 if (recursive == MagickFalse)
2025 WriteTo8BimProfile(image,key,profile);
2026 }
2027 return(status);
2028}
2029
2030MagickExport StringInfo *AcquireProfileStringInfo(const char *name,
2031 const size_t length,ExceptionInfo *exception)
2032{
2033 StringInfo
2034 *profile = (StringInfo *) NULL;
2035
2036 if (length > GetMaxProfileSize())
2037 (void) ThrowMagickException(exception,GetMagickModule(),
2038 ResourceLimitWarning,"ProfileSizeExceedsLimit","`%llu'",
2039 (unsigned long long) length);
2040 else
2041 {
2042 profile=AcquireStringInfo(length);
2043 SetStringInfoName(profile,name);
2044 }
2045 return(profile);
2046}
2047
2048MagickExport StringInfo *BlobToProfileStringInfo(const char *name,
2049 const void *blob,const size_t length,ExceptionInfo *exception)
2050{
2051 StringInfo
2052 *profile;
2053
2054 profile=AcquireProfileStringInfo(name,length,exception);
2055 if (profile != (const StringInfo *) NULL)
2056 (void) memcpy(profile->datum,blob,length);
2057 return(profile);
2058}
2059
2060MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
2061 const StringInfo *profile,ExceptionInfo *exception)
2062{
2063 StringInfo
2064 *clone_profile;
2065
2066 if (profile == (const StringInfo *) NULL)
2067 return(MagickFalse);
2068 clone_profile=CloneStringInfo(profile);
2069 return(SetImageProfileInternal(image,name,clone_profile,MagickFalse,
2070 exception));
2071}
2072
2073MagickExport MagickBooleanType SetImageProfilePrivate(Image *image,
2074 StringInfo *profile,ExceptionInfo *exception)
2075{
2076 if (profile == (const StringInfo *) NULL)
2077 return(MagickFalse);
2078 return(SetImageProfileInternal(image,GetStringInfoName(profile),profile,
2079 MagickFalse,exception));
2080}
2081
2082/*
2083%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2084% %
2085% %
2086% %
2087% S y n c I m a g e P r o f i l e s %
2088% %
2089% %
2090% %
2091%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2092%
2093% SyncImageProfiles() synchronizes image properties with the image profiles.
2094%
2095% The format of the SyncImageProfiles method is:
2096%
2097% void SyncImageProfiles(Image *image)
2098%
2099% A description of each parameter follows:
2100%
2101% o image: the image.
2102%
2103*/
2104
2105static inline int ReadProfileByte(unsigned char **p,size_t *length)
2106{
2107 int
2108 c;
2109
2110 if (*length < 1)
2111 return(EOF);
2112 c=(int) (*(*p)++);
2113 (*length)--;
2114 return(c);
2115}
2116
2117static inline signed short ReadProfileShort(const EndianType endian,
2118 unsigned char *buffer)
2119{
2120 union
2121 {
2122 unsigned int
2123 unsigned_value;
2124
2125 signed int
2126 signed_value;
2127 } quantum;
2128
2129 unsigned short
2130 value;
2131
2132 if (endian == LSBEndian)
2133 {
2134 value=(unsigned short) buffer[1] << 8;
2135 value|=(unsigned short) buffer[0];
2136 quantum.unsigned_value=value & 0xffff;
2137 return((signed short) quantum.signed_value);
2138 }
2139 value=(unsigned short) buffer[0] << 8;
2140 value|=(unsigned short) buffer[1];
2141 quantum.unsigned_value=value & 0xffff;
2142 return((signed short) quantum.signed_value);
2143}
2144
2145static inline signed int ReadProfileLong(const EndianType endian,
2146 unsigned char *buffer)
2147{
2148 union
2149 {
2150 unsigned int
2151 unsigned_value;
2152
2153 signed int
2154 signed_value;
2155 } quantum;
2156
2157 unsigned int
2158 value;
2159
2160 if (endian == LSBEndian)
2161 {
2162 value=(unsigned int) buffer[3] << 24;
2163 value|=(unsigned int) buffer[2] << 16;
2164 value|=(unsigned int) buffer[1] << 8;
2165 value|=(unsigned int) buffer[0];
2166 quantum.unsigned_value=value & 0xffffffff;
2167 return(quantum.signed_value);
2168 }
2169 value=(unsigned int) buffer[0] << 24;
2170 value|=(unsigned int) buffer[1] << 16;
2171 value|=(unsigned int) buffer[2] << 8;
2172 value|=(unsigned int) buffer[3];
2173 quantum.unsigned_value=value & 0xffffffff;
2174 return(quantum.signed_value);
2175}
2176
2177static inline signed int ReadProfileMSBLong(unsigned char **p,size_t *length)
2178{
2179 signed int
2180 value;
2181
2182 if (*length < 4)
2183 return(0);
2184 value=ReadProfileLong(MSBEndian,*p);
2185 (*length)-=4;
2186 *p+=4;
2187 return(value);
2188}
2189
2190static inline signed short ReadProfileMSBShort(unsigned char **p,
2191 size_t *length)
2192{
2193 signed short
2194 value;
2195
2196 if (*length < 2)
2197 return(0);
2198 value=ReadProfileShort(MSBEndian,*p);
2199 (*length)-=2;
2200 *p+=2;
2201 return(value);
2202}
2203
2204static inline void WriteProfileLong(const EndianType endian,
2205 const size_t value,unsigned char *p)
2206{
2207 unsigned char
2208 buffer[4];
2209
2210 if (endian == LSBEndian)
2211 {
2212 buffer[0]=(unsigned char) value;
2213 buffer[1]=(unsigned char) (value >> 8);
2214 buffer[2]=(unsigned char) (value >> 16);
2215 buffer[3]=(unsigned char) (value >> 24);
2216 (void) memcpy(p,buffer,4);
2217 return;
2218 }
2219 buffer[0]=(unsigned char) (value >> 24);
2220 buffer[1]=(unsigned char) (value >> 16);
2221 buffer[2]=(unsigned char) (value >> 8);
2222 buffer[3]=(unsigned char) value;
2223 (void) memcpy(p,buffer,4);
2224}
2225
2226static void WriteProfileShort(const EndianType endian,
2227 const unsigned short value,unsigned char *p)
2228{
2229 unsigned char
2230 buffer[2];
2231
2232 if (endian == LSBEndian)
2233 {
2234 buffer[0]=(unsigned char) value;
2235 buffer[1]=(unsigned char) (value >> 8);
2236 (void) memcpy(p,buffer,2);
2237 return;
2238 }
2239 buffer[0]=(unsigned char) (value >> 8);
2240 buffer[1]=(unsigned char) value;
2241 (void) memcpy(p,buffer,2);
2242}
2243
2244static void SyncExifProfile(const Image *image,unsigned char *exif,
2245 size_t length)
2246{
2247#define MaxDirectoryStack 16
2248#define EXIF_DELIMITER "\n"
2249#define EXIF_NUM_FORMATS 12
2250#define TAG_EXIF_OFFSET 0x8769
2251#define TAG_INTEROP_OFFSET 0xa005
2252
2253 typedef struct _DirectoryInfo
2254 {
2255 unsigned char
2256 *directory;
2257
2258 size_t
2259 entry;
2260 } DirectoryInfo;
2261
2262 DirectoryInfo
2263 directory_stack[MaxDirectoryStack] = { { 0, 0 } };
2264
2265 EndianType
2266 endian;
2267
2268 size_t
2269 entry,
2270 number_entries;
2271
2272 SplayTreeInfo
2273 *exif_resources;
2274
2275 ssize_t
2276 id,
2277 level,
2278 offset;
2279
2280 static int
2281 format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
2282
2283 unsigned char
2284 *directory;
2285
2286 if (length < 16)
2287 return;
2288 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
2289 if ((id != 0x4949) && (id != 0x4D4D))
2290 {
2291 while (length != 0)
2292 {
2293 if (ReadProfileByte(&exif,&length) != 0x45)
2294 continue;
2295 if (ReadProfileByte(&exif,&length) != 0x78)
2296 continue;
2297 if (ReadProfileByte(&exif,&length) != 0x69)
2298 continue;
2299 if (ReadProfileByte(&exif,&length) != 0x66)
2300 continue;
2301 if (ReadProfileByte(&exif,&length) != 0x00)
2302 continue;
2303 if (ReadProfileByte(&exif,&length) != 0x00)
2304 continue;
2305 break;
2306 }
2307 if (length < 16)
2308 return;
2309 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
2310 }
2311 endian=LSBEndian;
2312 if (id == 0x4949)
2313 endian=LSBEndian;
2314 else
2315 if (id == 0x4D4D)
2316 endian=MSBEndian;
2317 else
2318 return;
2319 if (ReadProfileShort(endian,exif+2) != 0x002a)
2320 return;
2321 /*
2322 This the offset to the first IFD.
2323 */
2324 offset=(ssize_t) ReadProfileLong(endian,exif+4);
2325 if ((offset < 0) || ((size_t) offset >= length))
2326 return;
2327 directory=exif+offset;
2328 level=0;
2329 entry=0;
2330 exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
2331 (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
2332 do
2333 {
2334 if (level > 0)
2335 {
2336 level--;
2337 directory=directory_stack[level].directory;
2338 entry=directory_stack[level].entry;
2339 }
2340 if ((directory < exif) || (directory > (exif+length-2)))
2341 break;
2342 /*
2343 Determine how many entries there are in the current IFD.
2344 */
2345 number_entries=(size_t) ReadProfileShort(endian,directory);
2346 for ( ; entry < number_entries; entry++)
2347 {
2348 int
2349 components;
2350
2351 unsigned char
2352 *p,
2353 *q;
2354
2355 size_t
2356 number_bytes;
2357
2358 ssize_t
2359 format,
2360 tag_value;
2361
2362 q=(unsigned char *) (directory+2+(12*entry));
2363 if (q > (exif+length-12))
2364 break; /* corrupt EXIF */
2365 if (GetValueFromSplayTree(exif_resources,q) == q)
2366 break;
2367 (void) AddValueToSplayTree(exif_resources,q,q);
2368 tag_value=(ssize_t) ReadProfileShort(endian,q);
2369 format=(ssize_t) ReadProfileShort(endian,q+2);
2370 if ((format < 0) || ((format-1) >= EXIF_NUM_FORMATS))
2371 break;
2372 components=(int) ReadProfileLong(endian,q+4);
2373 if (components < 0)
2374 break; /* corrupt EXIF */
2375 number_bytes=(size_t) components*(size_t) format_bytes[format];
2376 if ((ssize_t) number_bytes < components)
2377 break; /* prevent overflow */
2378 if (number_bytes <= 4)
2379 p=q+8;
2380 else
2381 {
2382 /*
2383 The directory entry contains an offset.
2384 */
2385 offset=(ssize_t) ReadProfileLong(endian,q+8);
2386 if ((offset < 0) ||
2387 ((size_t) (offset+(ssize_t) number_bytes) > length))
2388 continue;
2389 if (~length < number_bytes)
2390 continue; /* prevent overflow */
2391 p=(unsigned char *) (exif+offset);
2392 }
2393 switch (tag_value)
2394 {
2395 case 0x011a:
2396 {
2397 (void) WriteProfileLong(endian,(size_t) (image->resolution.x+0.5),p);
2398 if (number_bytes == 8)
2399 (void) WriteProfileLong(endian,1UL,p+4);
2400 break;
2401 }
2402 case 0x011b:
2403 {
2404 (void) WriteProfileLong(endian,(size_t) (image->resolution.y+0.5),p);
2405 if (number_bytes == 8)
2406 (void) WriteProfileLong(endian,1UL,p+4);
2407 break;
2408 }
2409 case 0x0112:
2410 {
2411 if (number_bytes == 4)
2412 {
2413 (void) WriteProfileLong(endian,(size_t) image->orientation,p);
2414 break;
2415 }
2416 (void) WriteProfileShort(endian,(unsigned short) image->orientation,
2417 p);
2418 break;
2419 }
2420 case 0x0128:
2421 {
2422 if (number_bytes == 4)
2423 {
2424 (void) WriteProfileLong(endian,((size_t) image->units)+1,p);
2425 break;
2426 }
2427 (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
2428 break;
2429 }
2430 default:
2431 break;
2432 }
2433 if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
2434 {
2435 offset=(ssize_t) ReadProfileLong(endian,p);
2436 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
2437 {
2438 directory_stack[level].directory=directory;
2439 entry++;
2440 directory_stack[level].entry=entry;
2441 level++;
2442 directory_stack[level].directory=exif+offset;
2443 directory_stack[level].entry=0;
2444 level++;
2445 if ((directory+2+(12*number_entries)) > (exif+length))
2446 break;
2447 offset=(ssize_t) ReadProfileLong(endian,directory+2+(12*
2448 number_entries));
2449 if ((offset != 0) && ((size_t) offset < length) &&
2450 (level < (MaxDirectoryStack-2)))
2451 {
2452 directory_stack[level].directory=exif+offset;
2453 directory_stack[level].entry=0;
2454 level++;
2455 }
2456 }
2457 break;
2458 }
2459 }
2460 } while (level > 0);
2461 exif_resources=DestroySplayTree(exif_resources);
2462 return;
2463}
2464
2465static void Sync8BimProfile(const Image *image,const StringInfo *profile)
2466{
2467 size_t
2468 length;
2469
2470 ssize_t
2471 count;
2472
2473 unsigned char
2474 *p;
2475
2476 unsigned short
2477 id;
2478
2479 length=GetStringInfoLength(profile);
2480 p=GetStringInfoDatum(profile);
2481 while (length != 0)
2482 {
2483 if (ReadProfileByte(&p,&length) != 0x38)
2484 continue;
2485 if (ReadProfileByte(&p,&length) != 0x42)
2486 continue;
2487 if (ReadProfileByte(&p,&length) != 0x49)
2488 continue;
2489 if (ReadProfileByte(&p,&length) != 0x4D)
2490 continue;
2491 if (length < 7)
2492 return;
2493 id=(unsigned short) ReadProfileMSBShort(&p,&length);
2494 count=(ssize_t) ReadProfileByte(&p,&length);
2495 if ((count >= (ssize_t) length) || (count < 0))
2496 return;
2497 p+=(ptrdiff_t) count;
2498 length-=(size_t) count;
2499 if ((*p & 0x01) == 0)
2500 (void) ReadProfileByte(&p,&length);
2501 count=(ssize_t) ReadProfileMSBLong(&p,&length);
2502 if ((count > (ssize_t) length) || (count < 0))
2503 return;
2504 if ((id == 0x3ED) && (count == 16))
2505 {
2506 if (image->units == PixelsPerCentimeterResolution)
2507 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToSsizeT(
2508 image->resolution.x*2.54*65536.0),p);
2509 else
2510 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToSsizeT(
2511 image->resolution.x*65536.0),p);
2512 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+4);
2513 if (image->units == PixelsPerCentimeterResolution)
2514 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToSsizeT(
2515 image->resolution.y*2.54*65536.0),p+8);
2516 else
2517 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToSsizeT(
2518 image->resolution.y*65536.0),p+8);
2519 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+12);
2520 }
2521 if (id == 0x0422)
2522 SyncExifProfile(image,p,(size_t) count);
2523 p+=(ptrdiff_t) count;
2524 length-=(size_t) count;
2525 }
2526 return;
2527}
2528
2529static void ReplaceXmpValue(StringInfo *profile,size_t start,size_t end,
2530 const char *value)
2531{
2532 char
2533 *datum;
2534
2535 size_t
2536 length,
2537 new_length,
2538 value_length;
2539
2540 length=GetStringInfoLength(profile);
2541 value_length=strlen(value);
2542 new_length=length-(end-start)+value_length;
2543 if (new_length > length)
2544 SetStringInfoLength(profile,new_length);
2545 datum=(char *) GetStringInfoDatum(profile);
2546 (void) memmove(datum+start+value_length,datum+end,length-end);
2547 (void) memcpy(datum+start,value,value_length);
2548 if (new_length < length)
2549 {
2550 SetStringInfoLength(profile,new_length);
2551 datum=(char *) GetStringInfoDatum(profile);
2552 *(datum+new_length)='\0';
2553 }
2554}
2555
2556static MagickBooleanType GetXmpOffsets(const StringInfo *profile,
2557 const char *tag,size_t *start,size_t *end)
2558{
2559 char
2560 *datum,
2561 *pos;
2562
2563 size_t
2564 length,
2565 tag_length;
2566
2567 datum=(char *) GetStringInfoDatum(profile);
2568 length=GetStringInfoLength(profile);
2569 pos=strstr(datum,tag);
2570 tag_length=strlen(tag);
2571 if ((pos == (char *) NULL) || ((pos-datum) < 1) || (*(pos-1) != '<') ||
2572 (((size_t) (pos-datum)+tag_length) > length) ||
2573 (*(pos+tag_length) != '>'))
2574 return(MagickFalse);
2575 *start=(size_t) (pos-datum)+tag_length+1;
2576 pos=strstr(datum+*start,"<");
2577 if (pos == (char *) NULL)
2578 return(MagickFalse);
2579 *end=(size_t) (pos-datum);
2580 return(MagickTrue);
2581}
2582
2583static void GetXmpNumeratorAndDenominator(double value,
2584 unsigned long *numerator,unsigned long *denominator)
2585{
2586 double
2587 df;
2588
2589 *numerator=0;
2590 *denominator=1;
2591 if (value <= MagickEpsilon)
2592 return;
2593 if (value > (double) MAGICK_ULONG_MAX)
2594 {
2595 *numerator = MAGICK_ULONG_MAX;
2596 *denominator = 1;
2597 return;
2598 }
2599 if (floor(value) == value)
2600 {
2601 *numerator = (unsigned long) value;
2602 *denominator = 1;
2603 return;
2604 }
2605 *numerator=1;
2606 df=1.0;
2607 while(fabs(df - value) > MagickEpsilon)
2608 {
2609 if (df < value)
2610 (*numerator)++;
2611 else
2612 {
2613 (*denominator)++;
2614 *numerator=(unsigned long) (value*(*denominator));
2615 }
2616 df=*numerator/(double)*denominator;
2617 }
2618}
2619
2620static void SyncXmpProfile(const Image *image,StringInfo *profile)
2621{
2622 char
2623 value[MagickPathExtent];
2624
2625 size_t
2626 end,
2627 start;
2628
2629 unsigned long
2630 denominator,
2631 numerator;
2632
2633 *value='\0';
2634 if (GetXmpOffsets(profile,"tiff:XResolution",&start,&end) != MagickFalse)
2635 {
2636 GetXmpNumeratorAndDenominator(image->resolution.x,&numerator,
2637 &denominator);
2638 (void) FormatLocaleString(value,MagickPathExtent,"%lu/%lu",numerator,
2639 denominator);
2640 ReplaceXmpValue(profile,start,end,value);
2641 }
2642 if (GetXmpOffsets(profile,"tiff:YResolution",&start,&end) != MagickFalse)
2643 {
2644 if ((fabs(image->resolution.x-image->resolution.y) > MagickEpsilon) ||
2645 (*value == '\0'))
2646 {
2647 GetXmpNumeratorAndDenominator(image->resolution.y,&numerator,
2648 &denominator);
2649 (void) FormatLocaleString(value,MagickPathExtent,"%lu/%lu",
2650 numerator,denominator);
2651 }
2652 ReplaceXmpValue(profile,start,end,value);
2653 }
2654 if (GetXmpOffsets(profile,"tiff:ResolutionUnit",&start,&end) != MagickFalse)
2655 {
2656 (void) FormatLocaleString(value,MagickPathExtent,"%d",
2657 ((int) image->units)+1);
2658 ReplaceXmpValue(profile,start,end,value);
2659 }
2660 if (GetXmpOffsets(profile,"tiff:Orientation",&start,&end) != MagickFalse)
2661 {
2662 (void) FormatLocaleString(value,MagickPathExtent,"%d",
2663 (int) image->orientation);
2664 ReplaceXmpValue(profile,start,end,value);
2665 }
2666}
2667
2668MagickPrivate void SyncImageProfiles(Image *image)
2669{
2670 StringInfo
2671 *profile;
2672
2673 profile=(StringInfo *) GetImageProfile(image,"8BIM");
2674 if (profile != (StringInfo *) NULL)
2675 Sync8BimProfile(image,profile);
2676 profile=(StringInfo *) GetImageProfile(image,"EXIF");
2677 if (profile != (StringInfo *) NULL)
2678 SyncExifProfile(image,GetStringInfoDatum(profile),GetStringInfoLength(
2679 profile));
2680 profile=(StringInfo *) GetImageProfile(image,"XMP");
2681 if (profile != (StringInfo *) NULL)
2682 SyncXmpProfile(image,profile);
2683}
2684
2685static void UpdateClipPath(unsigned char *blob,size_t length,
2686 const size_t old_columns,const size_t old_rows,
2687 const RectangleInfo *new_geometry)
2688{
2689 ssize_t
2690 i,
2691 knot_count,
2692 selector;
2693
2694 knot_count=0;
2695 while (length != 0)
2696 {
2697 selector=(ssize_t) ReadProfileMSBShort(&blob,&length);
2698 switch (selector)
2699 {
2700 case 0:
2701 case 3:
2702 {
2703 if (knot_count != 0)
2704 {
2705 blob+=24;
2706 length-=(size_t) MagickMin(length,24U);
2707 break;
2708 }
2709 /*
2710 Expected subpath length record.
2711 */
2712 knot_count=(ssize_t) ReadProfileMSBShort(&blob,&length);
2713 blob+=22;
2714 length-=(size_t) MagickMin(length,22);
2715 break;
2716 }
2717 case 1:
2718 case 2:
2719 case 4:
2720 case 5:
2721 {
2722 if (knot_count == 0)
2723 {
2724 /*
2725 Unexpected subpath knot.
2726 */
2727 blob+=24;
2728 length-=(size_t) MagickMin(length,24);
2729 break;
2730 }
2731 /*
2732 Add sub-path knot
2733 */
2734 for (i=0; i < 3; i++)
2735 {
2736 double
2737 x,
2738 y;
2739
2740 signed int
2741 xx,
2742 yy;
2743
2744 y=(double) ReadProfileMSBLong(&blob,&length);
2745 y=y*old_rows/4096.0/4096.0;
2746 y-=new_geometry->y;
2747 yy=(signed int) ((y*4096*4096)/new_geometry->height);
2748 WriteProfileLong(MSBEndian,(size_t) yy,blob-4);
2749 x=(double) ReadProfileMSBLong(&blob,&length);
2750 x=x*old_columns/4096.0/4096.0;
2751 x-=new_geometry->x;
2752 xx=(signed int) ((x*4096*4096)/new_geometry->width);
2753 WriteProfileLong(MSBEndian,(size_t) xx,blob-4);
2754 }
2755 knot_count--;
2756 break;
2757 }
2758 case 6:
2759 case 7:
2760 case 8:
2761 default:
2762 {
2763 blob+=24;
2764 length-=(size_t) MagickMin(length,24);
2765 break;
2766 }
2767 }
2768 }
2769}
2770
2771MagickPrivate void Update8BIMClipPath(const Image *image,
2772 const size_t old_columns,const size_t old_rows,
2773 const RectangleInfo *new_geometry)
2774{
2775 const StringInfo
2776 *profile;
2777
2778 size_t
2779 length;
2780
2781 ssize_t
2782 count,
2783 id;
2784
2785 unsigned char
2786 *info;
2787
2788 assert(image != (Image *) NULL);
2789 assert(new_geometry != (RectangleInfo *) NULL);
2790 profile=GetImageProfile(image,"8bim");
2791 if (profile == (StringInfo *) NULL)
2792 return;
2793 length=GetStringInfoLength(profile);
2794 info=GetStringInfoDatum(profile);
2795 while (length > 0)
2796 {
2797 if (ReadProfileByte(&info,&length) != (unsigned char) '8')
2798 continue;
2799 if (ReadProfileByte(&info,&length) != (unsigned char) 'B')
2800 continue;
2801 if (ReadProfileByte(&info,&length) != (unsigned char) 'I')
2802 continue;
2803 if (ReadProfileByte(&info,&length) != (unsigned char) 'M')
2804 continue;
2805 id=(ssize_t) ReadProfileMSBShort(&info,&length);
2806 count=(ssize_t) ReadProfileByte(&info,&length);
2807 if ((count != 0) && ((size_t) count <= length))
2808 {
2809 info+=count;
2810 length-=(size_t) count;
2811 }
2812 if ((count & 0x01) == 0)
2813 (void) ReadProfileByte(&info,&length);
2814 count=(ssize_t) ReadProfileMSBLong(&info,&length);
2815 if ((count < 0) || ((size_t) count > length))
2816 {
2817 length=0;
2818 continue;
2819 }
2820 if ((id > 1999) && (id < 2999))
2821 UpdateClipPath(info,(size_t) count,old_columns,old_rows,new_geometry);
2822 info+=count;
2823 length-=(size_t) MagickMin(length,(size_t) count);
2824 }
2825}