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