MagickCore 6.9.13
Loading...
Searching...
No Matches
profile.c
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP RRRR OOO FFFFF IIIII L EEEEE %
7% P P R R O O F I L E %
8% PPPP RRRR O O FFF I L EEE %
9% P R R O O F I L E %
10% P R R OOO F IIIII LLLLL EEEEE %
11% %
12% %
13% MagickCore Image Profile Methods %
14% %
15% Software Design %
16% Cristy %
17% July 1992 %
18% %
19% %
20% Copyright 1999 ImageMagick Studio LLC, a non-profit organization %
21% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% https://imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
42#include "magick/studio.h"
43#include "magick/artifact.h"
44#include "magick/attribute.h"
45#include "magick/cache.h"
46#include "magick/color.h"
47#include "magick/colorspace-private.h"
48#include "magick/configure.h"
49#include "magick/exception.h"
50#include "magick/exception-private.h"
51#include "magick/hashmap.h"
52#include "magick/image.h"
53#include "magick/memory_.h"
54#include "magick/monitor.h"
55#include "magick/monitor-private.h"
56#include "magick/option.h"
57#include "magick/option-private.h"
58#include "magick/profile.h"
59#include "magick/property.h"
60#include "magick/quantum.h"
61#include "magick/quantum-private.h"
62#include "magick/resource_.h"
63#include "magick/splay-tree.h"
64#include "magick/string_.h"
65#include "magick/string-private.h"
66#include "magick/thread-private.h"
67#include "magick/token.h"
68#include "magick/utility.h"
69#if defined(MAGICKCORE_LCMS_DELEGATE)
70#if defined(MAGICKCORE_HAVE_LCMS_LCMS2_H)
71#include <wchar.h>
72#include <lcms/lcms2.h>
73#else
74#include <wchar.h>
75#include "lcms2.h"
76#endif
77#endif
78#if defined(MAGICKCORE_XML_DELEGATE)
79# if defined(MAGICKCORE_WINDOWS_SUPPORT)
80# if !defined(__MINGW32__)
81# include <win32config.h>
82# endif
83# endif
84# include <libxml/parser.h>
85# include <libxml/tree.h>
86#endif
87
88/*
89 Forward declarations
90*/
91static MagickBooleanType
92 SetImageProfileInternal(Image *,const char *,const StringInfo *,
93 const MagickBooleanType);
94
95static void
96 WriteTo8BimProfile(Image *,const char*,const StringInfo *);
97
98/*
99%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
100% %
101% %
102% %
103% C l o n e I m a g e P r o f i l e s %
104% %
105% %
106% %
107%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
108%
109% CloneImageProfiles() clones one or more image profiles.
110%
111% The format of the CloneImageProfiles method is:
112%
113% MagickBooleanType CloneImageProfiles(Image *image,
114% const Image *clone_image)
115%
116% A description of each parameter follows:
117%
118% o image: the image.
119%
120% o clone_image: the clone image.
121%
122*/
123MagickExport MagickBooleanType CloneImageProfiles(Image *image,
124 const Image *clone_image)
125{
126 assert(image != (Image *) NULL);
127 assert(image->signature == MagickCoreSignature);
128 assert(clone_image != (const Image *) NULL);
129 assert(clone_image->signature == MagickCoreSignature);
130 if (IsEventLogging() != MagickFalse)
131 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
132 image->color_profile.length=clone_image->color_profile.length;
133 image->color_profile.info=clone_image->color_profile.info;
134 image->iptc_profile.length=clone_image->iptc_profile.length;
135 image->iptc_profile.info=clone_image->iptc_profile.info;
136 if (clone_image->profiles != (void *) NULL)
137 {
138 if (image->profiles != (void *) NULL)
139 DestroyImageProfiles(image);
140 image->profiles=CloneSplayTree((SplayTreeInfo *) clone_image->profiles,
141 (void *(*)(void *)) ConstantString,(void *(*)(void *)) CloneStringInfo);
142 }
143 return(MagickTrue);
144}
145
146/*
147%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148% %
149% %
150% %
151% D e l e t e I m a g e P r o f i l e %
152% %
153% %
154% %
155%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
156%
157% DeleteImageProfile() deletes a profile from the image by its name.
158%
159% The format of the DeleteImageProfile method is:
160%
161% MagickBooleanType DeleteImageProfile(Image *image,const char *name)
162%
163% A description of each parameter follows:
164%
165% o image: the image.
166%
167% o name: the profile name.
168%
169*/
170MagickExport MagickBooleanType DeleteImageProfile(Image *image,const char *name)
171{
172 assert(image != (Image *) NULL);
173 assert(image->signature == MagickCoreSignature);
174 if (image->debug != MagickFalse)
175 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
176 if (image->profiles == (SplayTreeInfo *) NULL)
177 return(MagickFalse);
178 if (LocaleCompare(name,"icc") == 0)
179 {
180 /*
181 Continue to support deprecated color profile for now.
182 */
183 image->color_profile.length=0;
184 image->color_profile.info=(unsigned char *) NULL;
185 }
186 if (LocaleCompare(name,"iptc") == 0)
187 {
188 /*
189 Continue to support deprecated IPTC profile for now.
190 */
191 image->iptc_profile.length=0;
192 image->iptc_profile.info=(unsigned char *) NULL;
193 }
194 WriteTo8BimProfile(image,name,(StringInfo *) NULL);
195 return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->profiles,name));
196}
197
198/*
199%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
200% %
201% %
202% %
203% D e s t r o y I m a g e P r o f i l e s %
204% %
205% %
206% %
207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
208%
209% DestroyImageProfiles() releases memory associated with an image profile map.
210%
211% The format of the DestroyProfiles method is:
212%
213% void DestroyImageProfiles(Image *image)
214%
215% A description of each parameter follows:
216%
217% o image: the image.
218%
219*/
220MagickExport void DestroyImageProfiles(Image *image)
221{
222 if (image->profiles != (SplayTreeInfo *) NULL)
223 image->profiles=DestroySplayTree((SplayTreeInfo *) image->profiles);
224}
225
226/*
227%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
228% %
229% %
230% %
231% G e t I m a g e P r o f i l e %
232% %
233% %
234% %
235%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
236%
237% GetImageProfile() gets a profile associated with an image by name.
238%
239% The format of the GetImageProfile method is:
240%
241% const StringInfo *GetImageProfile(const Image *image,const char *name)
242%
243% A description of each parameter follows:
244%
245% o image: the image.
246%
247% o name: the profile name.
248%
249*/
250MagickExport const StringInfo *GetImageProfile(const Image *image,
251 const char *name)
252{
253 const StringInfo
254 *profile;
255
256 assert(image != (Image *) NULL);
257 assert(image->signature == MagickCoreSignature);
258 if (image->debug != MagickFalse)
259 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
260 if (image->profiles == (SplayTreeInfo *) NULL)
261 return((StringInfo *) NULL);
262 profile=(const StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
263 image->profiles,name);
264 return(profile);
265}
266
267/*
268%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
269% %
270% %
271% %
272% G e t N e x t I m a g e P r o f i l e %
273% %
274% %
275% %
276%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
277%
278% GetNextImageProfile() gets the next profile name for an image.
279%
280% The format of the GetNextImageProfile method is:
281%
282% char *GetNextImageProfile(const Image *image)
283%
284% A description of each parameter follows:
285%
286% o hash_info: the hash info.
287%
288*/
289MagickExport char *GetNextImageProfile(const Image *image)
290{
291 assert(image != (Image *) NULL);
292 assert(image->signature == MagickCoreSignature);
293 if (IsEventLogging() != MagickFalse)
294 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
295 if (image->profiles == (SplayTreeInfo *) NULL)
296 return((char *) NULL);
297 return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->profiles));
298}
299
300/*
301%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
302% %
303% %
304% %
305% P r o f i l e I m a g e %
306% %
307% %
308% %
309%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
310%
311% ProfileImage() associates, applies, or removes an ICM, IPTC, or generic
312% profile with / to / from an image. If the profile is NULL, it is removed
313% from the image otherwise added or applied. Use a name of '*' and a profile
314% of NULL to remove all profiles from the image.
315%
316% ICC and ICM profiles are handled as follows: If the image does not have
317% an associated color profile, the one you provide is associated with the
318% image and the image pixels are not transformed. Otherwise, the colorspace
319% transform defined by the existing and new profile are applied to the image
320% pixels and the new profile is associated with the image.
321%
322% The format of the ProfileImage method is:
323%
324% MagickBooleanType ProfileImage(Image *image,const char *name,
325% const void *datum,const size_t length,const MagickBooleanType clone)
326%
327% A description of each parameter follows:
328%
329% o image: the image.
330%
331% o name: Name of profile to add or remove: ICC, IPTC, or generic profile.
332%
333% o datum: the profile data.
334%
335% o length: the length of the profile.
336%
337% o clone: should be MagickFalse.
338%
339*/
340
341#if defined(MAGICKCORE_LCMS_DELEGATE)
342
343typedef struct _LCMSInfo
344{
345 ColorspaceType
346 colorspace;
347
348 cmsUInt32Number
349 type;
350
351 size_t
352 channels;
353
354 cmsHPROFILE
355 profile;
356
357 int
358 intent;
359
360 double
361 **magick_restrict pixels,
362 scale[4],
363 translate[4];
364} LCMSInfo;
365
366#if LCMS_VERSION < 2060
367static void* cmsGetContextUserData(cmsContext ContextID)
368{
369 return(ContextID);
370}
371
372static cmsContext cmsCreateContext(void *magick_unused(Plugin),void *UserData)
373{
374 magick_unreferenced(Plugin);
375 return((cmsContext) UserData);
376}
377
378static void cmsSetLogErrorHandlerTHR(cmsContext magick_unused(ContextID),
379 cmsLogErrorHandlerFunction Fn)
380{
381 magick_unreferenced(ContextID);
382 cmsSetLogErrorHandler(Fn);
383}
384
385static void cmsDeleteContext(cmsContext magick_unused(ContextID))
386{
387 magick_unreferenced(ContextID);
388}
389#endif
390
391static double **DestroyPixelTLS(double **pixels)
392{
393 ssize_t
394 i;
395
396 if (pixels == (double **) NULL)
397 return((double **) NULL);
398 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
399 if (pixels[i] != (double *) NULL)
400 pixels[i]=(double *) RelinquishMagickMemory(pixels[i]);
401 pixels=(double **) RelinquishMagickMemory(pixels);
402 return(pixels);
403}
404
405static double **AcquirePixelTLS(const size_t columns,
406 const size_t channels)
407{
408 double
409 **pixels;
410
411 ssize_t
412 i;
413
414 size_t
415 number_threads;
416
417 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
418 pixels=(double **) AcquireQuantumMemory(number_threads,sizeof(*pixels));
419 if (pixels == (double **) NULL)
420 return((double **) NULL);
421 (void) memset(pixels,0,number_threads*sizeof(*pixels));
422 for (i=0; i < (ssize_t) number_threads; i++)
423 {
424 pixels[i]=(double *) AcquireQuantumMemory(columns,channels*sizeof(**pixels));
425 if (pixels[i] == (double *) NULL)
426 return(DestroyPixelTLS(pixels));
427 }
428 return(pixels);
429}
430
431static cmsHTRANSFORM *DestroyTransformTLS(cmsHTRANSFORM *transform)
432{
433 ssize_t
434 i;
435
436 assert(transform != (cmsHTRANSFORM *) NULL);
437 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
438 if (transform[i] != (cmsHTRANSFORM) NULL)
439 cmsDeleteTransform(transform[i]);
440 transform=(cmsHTRANSFORM *) RelinquishMagickMemory(transform);
441 return(transform);
442}
443
444static cmsHTRANSFORM *AcquireTransformTLS(const LCMSInfo *source_info,
445 const LCMSInfo *target_info,const cmsUInt32Number flags,
446 cmsContext cms_context)
447{
448 cmsHTRANSFORM
449 *transform;
450
451 ssize_t
452 i;
453
454 size_t
455 number_threads;
456
457 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
458 transform=(cmsHTRANSFORM *) AcquireQuantumMemory(number_threads,
459 sizeof(*transform));
460 if (transform == (cmsHTRANSFORM *) NULL)
461 return((cmsHTRANSFORM *) NULL);
462 (void) memset(transform,0,number_threads*sizeof(*transform));
463 for (i=0; i < (ssize_t) number_threads; i++)
464 {
465 transform[i]=cmsCreateTransformTHR(cms_context,source_info->profile,
466 source_info->type,target_info->profile,target_info->type,
467 target_info->intent,flags);
468 if (transform[i] == (cmsHTRANSFORM) NULL)
469 return(DestroyTransformTLS(transform));
470 }
471 return(transform);
472}
473
474static void LCMSExceptionHandler(cmsContext context,cmsUInt32Number severity,
475 const char *message)
476{
477 Image
478 *image;
479
480 if (IsEventLogging() != MagickFalse)
481 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%u, %s",
482 severity,message != (char *) NULL ? message : "no message");
483 image=(Image *) cmsGetContextUserData(context);
484 if (image != (Image *) NULL)
485 (void) ThrowMagickException(&image->exception,GetMagickModule(),
486 ImageWarning,"UnableToTransformColorspace","`%s'",image->filename);
487}
488
489static inline void SetLCMSInfoTranslate(LCMSInfo *info,const double translate)
490{
491 info->translate[0]=translate;
492 info->translate[1]=translate;
493 info->translate[2]=translate;
494 info->translate[3]=translate;
495}
496
497static inline void SetLCMSInfoScale(LCMSInfo *info,const double scale)
498{
499 info->scale[0]=scale;
500 info->scale[1]=scale;
501 info->scale[2]=scale;
502 info->scale[3]=scale;
503}
504#endif
505
506static MagickBooleanType SetsRGBImageProfile(Image *image)
507{
508 static unsigned char
509 sRGBProfile[] =
510 {
511 0x00, 0x00, 0x0c, 0x8c, 0x61, 0x72, 0x67, 0x6c, 0x02, 0x20, 0x00, 0x00,
512 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20,
513 0x07, 0xde, 0x00, 0x01, 0x00, 0x06, 0x00, 0x16, 0x00, 0x0f, 0x00, 0x3a,
514 0x61, 0x63, 0x73, 0x70, 0x4d, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00,
515 0x49, 0x45, 0x43, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00,
516 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6,
517 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x61, 0x72, 0x67, 0x6c,
518 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
519 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
520 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
521 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
522 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x99,
523 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0xec, 0x00, 0x00, 0x00, 0x67,
524 0x64, 0x6d, 0x6e, 0x64, 0x00, 0x00, 0x02, 0x54, 0x00, 0x00, 0x00, 0x70,
525 0x64, 0x6d, 0x64, 0x64, 0x00, 0x00, 0x02, 0xc4, 0x00, 0x00, 0x00, 0x88,
526 0x74, 0x65, 0x63, 0x68, 0x00, 0x00, 0x03, 0x4c, 0x00, 0x00, 0x00, 0x0c,
527 0x76, 0x75, 0x65, 0x64, 0x00, 0x00, 0x03, 0x58, 0x00, 0x00, 0x00, 0x67,
528 0x76, 0x69, 0x65, 0x77, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x24,
529 0x6c, 0x75, 0x6d, 0x69, 0x00, 0x00, 0x03, 0xe4, 0x00, 0x00, 0x00, 0x14,
530 0x6d, 0x65, 0x61, 0x73, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x24,
531 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x00, 0x00, 0x14,
532 0x62, 0x6b, 0x70, 0x74, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x14,
533 0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x44, 0x00, 0x00, 0x00, 0x14,
534 0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x58, 0x00, 0x00, 0x00, 0x14,
535 0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x6c, 0x00, 0x00, 0x00, 0x14,
536 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
537 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
538 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
539 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
540 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36,
541 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x28, 0x45, 0x71, 0x75, 0x69, 0x76,
542 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x77, 0x77,
543 0x2e, 0x73, 0x72, 0x67, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x31, 0x39,
544 0x39, 0x38, 0x20, 0x48, 0x50, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c,
545 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
546 0x00, 0x3f, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31,
547 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x28, 0x45, 0x71, 0x75,
548 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77,
549 0x77, 0x77, 0x2e, 0x73, 0x72, 0x67, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x20,
550 0x31, 0x39, 0x39, 0x38, 0x20, 0x48, 0x50, 0x20, 0x70, 0x72, 0x6f, 0x66,
551 0x69, 0x6c, 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
552 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x72, 0x65, 0x61,
553 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x47, 0x72, 0x61, 0x65, 0x6d,
554 0x65, 0x20, 0x57, 0x2e, 0x20, 0x47, 0x69, 0x6c, 0x6c, 0x2e, 0x20, 0x52,
555 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f,
556 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
557 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x20, 0x4e, 0x6f, 0x20, 0x57,
558 0x61, 0x72, 0x72, 0x61, 0x6e, 0x74, 0x79, 0x2c, 0x20, 0x55, 0x73, 0x65,
559 0x20, 0x61, 0x74, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x6f, 0x77, 0x6e,
560 0x20, 0x72, 0x69, 0x73, 0x6b, 0x2e, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63,
561 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20,
562 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69,
563 0x65, 0x63, 0x2e, 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
564 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20, 0x68, 0x74, 0x74,
565 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x65, 0x63, 0x2e,
566 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
567 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
568 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
569 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
570 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e,
571 0x49, 0x45, 0x43, 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e,
572 0x31, 0x20, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47,
573 0x42, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61,
574 0x63, 0x65, 0x20, 0x2d, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00,
575 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x49, 0x45, 0x43,
576 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x44,
577 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47, 0x42, 0x20, 0x63,
578 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20,
579 0x2d, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
580 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
581 0x00, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x20, 0x00, 0x00, 0x00, 0x00,
582 0x43, 0x52, 0x54, 0x20, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00,
583 0x00, 0x00, 0x00, 0x0d, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36,
584 0x2d, 0x32, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
585 0x00, 0x00, 0x00, 0x0d, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36,
586 0x2d, 0x32, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
587 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
588 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
589 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
590 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
591 0x76, 0x69, 0x65, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xa4, 0x7c,
592 0x00, 0x14, 0x5f, 0x30, 0x00, 0x10, 0xce, 0x02, 0x00, 0x03, 0xed, 0xb2,
593 0x00, 0x04, 0x13, 0x0a, 0x00, 0x03, 0x5c, 0x67, 0x00, 0x00, 0x00, 0x01,
594 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x0a, 0x3d,
595 0x00, 0x50, 0x00, 0x00, 0x00, 0x57, 0x1e, 0xb8, 0x6d, 0x65, 0x61, 0x73,
596 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
597 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
598 0x00, 0x00, 0x02, 0x8f, 0x00, 0x00, 0x00, 0x02, 0x58, 0x59, 0x5a, 0x20,
599 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0x51, 0x00, 0x01, 0x00, 0x00,
600 0x00, 0x01, 0x16, 0xcc, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
601 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
602 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xa0,
603 0x00, 0x00, 0x38, 0xf5, 0x00, 0x00, 0x03, 0x90, 0x58, 0x59, 0x5a, 0x20,
604 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x97, 0x00, 0x00, 0xb7, 0x87,
605 0x00, 0x00, 0x18, 0xd9, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
606 0x00, 0x00, 0x24, 0x9f, 0x00, 0x00, 0x0f, 0x84, 0x00, 0x00, 0xb6, 0xc4,
607 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
608 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x14, 0x00, 0x19,
609 0x00, 0x1e, 0x00, 0x23, 0x00, 0x28, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x37,
610 0x00, 0x3b, 0x00, 0x40, 0x00, 0x45, 0x00, 0x4a, 0x00, 0x4f, 0x00, 0x54,
611 0x00, 0x59, 0x00, 0x5e, 0x00, 0x63, 0x00, 0x68, 0x00, 0x6d, 0x00, 0x72,
612 0x00, 0x77, 0x00, 0x7c, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8b, 0x00, 0x90,
613 0x00, 0x95, 0x00, 0x9a, 0x00, 0x9f, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xae,
614 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc1, 0x00, 0xc6, 0x00, 0xcb,
615 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xdb, 0x00, 0xe0, 0x00, 0xe5, 0x00, 0xeb,
616 0x00, 0xf0, 0x00, 0xf6, 0x00, 0xfb, 0x01, 0x01, 0x01, 0x07, 0x01, 0x0d,
617 0x01, 0x13, 0x01, 0x19, 0x01, 0x1f, 0x01, 0x25, 0x01, 0x2b, 0x01, 0x32,
618 0x01, 0x38, 0x01, 0x3e, 0x01, 0x45, 0x01, 0x4c, 0x01, 0x52, 0x01, 0x59,
619 0x01, 0x60, 0x01, 0x67, 0x01, 0x6e, 0x01, 0x75, 0x01, 0x7c, 0x01, 0x83,
620 0x01, 0x8b, 0x01, 0x92, 0x01, 0x9a, 0x01, 0xa1, 0x01, 0xa9, 0x01, 0xb1,
621 0x01, 0xb9, 0x01, 0xc1, 0x01, 0xc9, 0x01, 0xd1, 0x01, 0xd9, 0x01, 0xe1,
622 0x01, 0xe9, 0x01, 0xf2, 0x01, 0xfa, 0x02, 0x03, 0x02, 0x0c, 0x02, 0x14,
623 0x02, 0x1d, 0x02, 0x26, 0x02, 0x2f, 0x02, 0x38, 0x02, 0x41, 0x02, 0x4b,
624 0x02, 0x54, 0x02, 0x5d, 0x02, 0x67, 0x02, 0x71, 0x02, 0x7a, 0x02, 0x84,
625 0x02, 0x8e, 0x02, 0x98, 0x02, 0xa2, 0x02, 0xac, 0x02, 0xb6, 0x02, 0xc1,
626 0x02, 0xcb, 0x02, 0xd5, 0x02, 0xe0, 0x02, 0xeb, 0x02, 0xf5, 0x03, 0x00,
627 0x03, 0x0b, 0x03, 0x16, 0x03, 0x21, 0x03, 0x2d, 0x03, 0x38, 0x03, 0x43,
628 0x03, 0x4f, 0x03, 0x5a, 0x03, 0x66, 0x03, 0x72, 0x03, 0x7e, 0x03, 0x8a,
629 0x03, 0x96, 0x03, 0xa2, 0x03, 0xae, 0x03, 0xba, 0x03, 0xc7, 0x03, 0xd3,
630 0x03, 0xe0, 0x03, 0xec, 0x03, 0xf9, 0x04, 0x06, 0x04, 0x13, 0x04, 0x20,
631 0x04, 0x2d, 0x04, 0x3b, 0x04, 0x48, 0x04, 0x55, 0x04, 0x63, 0x04, 0x71,
632 0x04, 0x7e, 0x04, 0x8c, 0x04, 0x9a, 0x04, 0xa8, 0x04, 0xb6, 0x04, 0xc4,
633 0x04, 0xd3, 0x04, 0xe1, 0x04, 0xf0, 0x04, 0xfe, 0x05, 0x0d, 0x05, 0x1c,
634 0x05, 0x2b, 0x05, 0x3a, 0x05, 0x49, 0x05, 0x58, 0x05, 0x67, 0x05, 0x77,
635 0x05, 0x86, 0x05, 0x96, 0x05, 0xa6, 0x05, 0xb5, 0x05, 0xc5, 0x05, 0xd5,
636 0x05, 0xe5, 0x05, 0xf6, 0x06, 0x06, 0x06, 0x16, 0x06, 0x27, 0x06, 0x37,
637 0x06, 0x48, 0x06, 0x59, 0x06, 0x6a, 0x06, 0x7b, 0x06, 0x8c, 0x06, 0x9d,
638 0x06, 0xaf, 0x06, 0xc0, 0x06, 0xd1, 0x06, 0xe3, 0x06, 0xf5, 0x07, 0x07,
639 0x07, 0x19, 0x07, 0x2b, 0x07, 0x3d, 0x07, 0x4f, 0x07, 0x61, 0x07, 0x74,
640 0x07, 0x86, 0x07, 0x99, 0x07, 0xac, 0x07, 0xbf, 0x07, 0xd2, 0x07, 0xe5,
641 0x07, 0xf8, 0x08, 0x0b, 0x08, 0x1f, 0x08, 0x32, 0x08, 0x46, 0x08, 0x5a,
642 0x08, 0x6e, 0x08, 0x82, 0x08, 0x96, 0x08, 0xaa, 0x08, 0xbe, 0x08, 0xd2,
643 0x08, 0xe7, 0x08, 0xfb, 0x09, 0x10, 0x09, 0x25, 0x09, 0x3a, 0x09, 0x4f,
644 0x09, 0x64, 0x09, 0x79, 0x09, 0x8f, 0x09, 0xa4, 0x09, 0xba, 0x09, 0xcf,
645 0x09, 0xe5, 0x09, 0xfb, 0x0a, 0x11, 0x0a, 0x27, 0x0a, 0x3d, 0x0a, 0x54,
646 0x0a, 0x6a, 0x0a, 0x81, 0x0a, 0x98, 0x0a, 0xae, 0x0a, 0xc5, 0x0a, 0xdc,
647 0x0a, 0xf3, 0x0b, 0x0b, 0x0b, 0x22, 0x0b, 0x39, 0x0b, 0x51, 0x0b, 0x69,
648 0x0b, 0x80, 0x0b, 0x98, 0x0b, 0xb0, 0x0b, 0xc8, 0x0b, 0xe1, 0x0b, 0xf9,
649 0x0c, 0x12, 0x0c, 0x2a, 0x0c, 0x43, 0x0c, 0x5c, 0x0c, 0x75, 0x0c, 0x8e,
650 0x0c, 0xa7, 0x0c, 0xc0, 0x0c, 0xd9, 0x0c, 0xf3, 0x0d, 0x0d, 0x0d, 0x26,
651 0x0d, 0x40, 0x0d, 0x5a, 0x0d, 0x74, 0x0d, 0x8e, 0x0d, 0xa9, 0x0d, 0xc3,
652 0x0d, 0xde, 0x0d, 0xf8, 0x0e, 0x13, 0x0e, 0x2e, 0x0e, 0x49, 0x0e, 0x64,
653 0x0e, 0x7f, 0x0e, 0x9b, 0x0e, 0xb6, 0x0e, 0xd2, 0x0e, 0xee, 0x0f, 0x09,
654 0x0f, 0x25, 0x0f, 0x41, 0x0f, 0x5e, 0x0f, 0x7a, 0x0f, 0x96, 0x0f, 0xb3,
655 0x0f, 0xcf, 0x0f, 0xec, 0x10, 0x09, 0x10, 0x26, 0x10, 0x43, 0x10, 0x61,
656 0x10, 0x7e, 0x10, 0x9b, 0x10, 0xb9, 0x10, 0xd7, 0x10, 0xf5, 0x11, 0x13,
657 0x11, 0x31, 0x11, 0x4f, 0x11, 0x6d, 0x11, 0x8c, 0x11, 0xaa, 0x11, 0xc9,
658 0x11, 0xe8, 0x12, 0x07, 0x12, 0x26, 0x12, 0x45, 0x12, 0x64, 0x12, 0x84,
659 0x12, 0xa3, 0x12, 0xc3, 0x12, 0xe3, 0x13, 0x03, 0x13, 0x23, 0x13, 0x43,
660 0x13, 0x63, 0x13, 0x83, 0x13, 0xa4, 0x13, 0xc5, 0x13, 0xe5, 0x14, 0x06,
661 0x14, 0x27, 0x14, 0x49, 0x14, 0x6a, 0x14, 0x8b, 0x14, 0xad, 0x14, 0xce,
662 0x14, 0xf0, 0x15, 0x12, 0x15, 0x34, 0x15, 0x56, 0x15, 0x78, 0x15, 0x9b,
663 0x15, 0xbd, 0x15, 0xe0, 0x16, 0x03, 0x16, 0x26, 0x16, 0x49, 0x16, 0x6c,
664 0x16, 0x8f, 0x16, 0xb2, 0x16, 0xd6, 0x16, 0xfa, 0x17, 0x1d, 0x17, 0x41,
665 0x17, 0x65, 0x17, 0x89, 0x17, 0xae, 0x17, 0xd2, 0x17, 0xf7, 0x18, 0x1b,
666 0x18, 0x40, 0x18, 0x65, 0x18, 0x8a, 0x18, 0xaf, 0x18, 0xd5, 0x18, 0xfa,
667 0x19, 0x20, 0x19, 0x45, 0x19, 0x6b, 0x19, 0x91, 0x19, 0xb7, 0x19, 0xdd,
668 0x1a, 0x04, 0x1a, 0x2a, 0x1a, 0x51, 0x1a, 0x77, 0x1a, 0x9e, 0x1a, 0xc5,
669 0x1a, 0xec, 0x1b, 0x14, 0x1b, 0x3b, 0x1b, 0x63, 0x1b, 0x8a, 0x1b, 0xb2,
670 0x1b, 0xda, 0x1c, 0x02, 0x1c, 0x2a, 0x1c, 0x52, 0x1c, 0x7b, 0x1c, 0xa3,
671 0x1c, 0xcc, 0x1c, 0xf5, 0x1d, 0x1e, 0x1d, 0x47, 0x1d, 0x70, 0x1d, 0x99,
672 0x1d, 0xc3, 0x1d, 0xec, 0x1e, 0x16, 0x1e, 0x40, 0x1e, 0x6a, 0x1e, 0x94,
673 0x1e, 0xbe, 0x1e, 0xe9, 0x1f, 0x13, 0x1f, 0x3e, 0x1f, 0x69, 0x1f, 0x94,
674 0x1f, 0xbf, 0x1f, 0xea, 0x20, 0x15, 0x20, 0x41, 0x20, 0x6c, 0x20, 0x98,
675 0x20, 0xc4, 0x20, 0xf0, 0x21, 0x1c, 0x21, 0x48, 0x21, 0x75, 0x21, 0xa1,
676 0x21, 0xce, 0x21, 0xfb, 0x22, 0x27, 0x22, 0x55, 0x22, 0x82, 0x22, 0xaf,
677 0x22, 0xdd, 0x23, 0x0a, 0x23, 0x38, 0x23, 0x66, 0x23, 0x94, 0x23, 0xc2,
678 0x23, 0xf0, 0x24, 0x1f, 0x24, 0x4d, 0x24, 0x7c, 0x24, 0xab, 0x24, 0xda,
679 0x25, 0x09, 0x25, 0x38, 0x25, 0x68, 0x25, 0x97, 0x25, 0xc7, 0x25, 0xf7,
680 0x26, 0x27, 0x26, 0x57, 0x26, 0x87, 0x26, 0xb7, 0x26, 0xe8, 0x27, 0x18,
681 0x27, 0x49, 0x27, 0x7a, 0x27, 0xab, 0x27, 0xdc, 0x28, 0x0d, 0x28, 0x3f,
682 0x28, 0x71, 0x28, 0xa2, 0x28, 0xd4, 0x29, 0x06, 0x29, 0x38, 0x29, 0x6b,
683 0x29, 0x9d, 0x29, 0xd0, 0x2a, 0x02, 0x2a, 0x35, 0x2a, 0x68, 0x2a, 0x9b,
684 0x2a, 0xcf, 0x2b, 0x02, 0x2b, 0x36, 0x2b, 0x69, 0x2b, 0x9d, 0x2b, 0xd1,
685 0x2c, 0x05, 0x2c, 0x39, 0x2c, 0x6e, 0x2c, 0xa2, 0x2c, 0xd7, 0x2d, 0x0c,
686 0x2d, 0x41, 0x2d, 0x76, 0x2d, 0xab, 0x2d, 0xe1, 0x2e, 0x16, 0x2e, 0x4c,
687 0x2e, 0x82, 0x2e, 0xb7, 0x2e, 0xee, 0x2f, 0x24, 0x2f, 0x5a, 0x2f, 0x91,
688 0x2f, 0xc7, 0x2f, 0xfe, 0x30, 0x35, 0x30, 0x6c, 0x30, 0xa4, 0x30, 0xdb,
689 0x31, 0x12, 0x31, 0x4a, 0x31, 0x82, 0x31, 0xba, 0x31, 0xf2, 0x32, 0x2a,
690 0x32, 0x63, 0x32, 0x9b, 0x32, 0xd4, 0x33, 0x0d, 0x33, 0x46, 0x33, 0x7f,
691 0x33, 0xb8, 0x33, 0xf1, 0x34, 0x2b, 0x34, 0x65, 0x34, 0x9e, 0x34, 0xd8,
692 0x35, 0x13, 0x35, 0x4d, 0x35, 0x87, 0x35, 0xc2, 0x35, 0xfd, 0x36, 0x37,
693 0x36, 0x72, 0x36, 0xae, 0x36, 0xe9, 0x37, 0x24, 0x37, 0x60, 0x37, 0x9c,
694 0x37, 0xd7, 0x38, 0x14, 0x38, 0x50, 0x38, 0x8c, 0x38, 0xc8, 0x39, 0x05,
695 0x39, 0x42, 0x39, 0x7f, 0x39, 0xbc, 0x39, 0xf9, 0x3a, 0x36, 0x3a, 0x74,
696 0x3a, 0xb2, 0x3a, 0xef, 0x3b, 0x2d, 0x3b, 0x6b, 0x3b, 0xaa, 0x3b, 0xe8,
697 0x3c, 0x27, 0x3c, 0x65, 0x3c, 0xa4, 0x3c, 0xe3, 0x3d, 0x22, 0x3d, 0x61,
698 0x3d, 0xa1, 0x3d, 0xe0, 0x3e, 0x20, 0x3e, 0x60, 0x3e, 0xa0, 0x3e, 0xe0,
699 0x3f, 0x21, 0x3f, 0x61, 0x3f, 0xa2, 0x3f, 0xe2, 0x40, 0x23, 0x40, 0x64,
700 0x40, 0xa6, 0x40, 0xe7, 0x41, 0x29, 0x41, 0x6a, 0x41, 0xac, 0x41, 0xee,
701 0x42, 0x30, 0x42, 0x72, 0x42, 0xb5, 0x42, 0xf7, 0x43, 0x3a, 0x43, 0x7d,
702 0x43, 0xc0, 0x44, 0x03, 0x44, 0x47, 0x44, 0x8a, 0x44, 0xce, 0x45, 0x12,
703 0x45, 0x55, 0x45, 0x9a, 0x45, 0xde, 0x46, 0x22, 0x46, 0x67, 0x46, 0xab,
704 0x46, 0xf0, 0x47, 0x35, 0x47, 0x7b, 0x47, 0xc0, 0x48, 0x05, 0x48, 0x4b,
705 0x48, 0x91, 0x48, 0xd7, 0x49, 0x1d, 0x49, 0x63, 0x49, 0xa9, 0x49, 0xf0,
706 0x4a, 0x37, 0x4a, 0x7d, 0x4a, 0xc4, 0x4b, 0x0c, 0x4b, 0x53, 0x4b, 0x9a,
707 0x4b, 0xe2, 0x4c, 0x2a, 0x4c, 0x72, 0x4c, 0xba, 0x4d, 0x02, 0x4d, 0x4a,
708 0x4d, 0x93, 0x4d, 0xdc, 0x4e, 0x25, 0x4e, 0x6e, 0x4e, 0xb7, 0x4f, 0x00,
709 0x4f, 0x49, 0x4f, 0x93, 0x4f, 0xdd, 0x50, 0x27, 0x50, 0x71, 0x50, 0xbb,
710 0x51, 0x06, 0x51, 0x50, 0x51, 0x9b, 0x51, 0xe6, 0x52, 0x31, 0x52, 0x7c,
711 0x52, 0xc7, 0x53, 0x13, 0x53, 0x5f, 0x53, 0xaa, 0x53, 0xf6, 0x54, 0x42,
712 0x54, 0x8f, 0x54, 0xdb, 0x55, 0x28, 0x55, 0x75, 0x55, 0xc2, 0x56, 0x0f,
713 0x56, 0x5c, 0x56, 0xa9, 0x56, 0xf7, 0x57, 0x44, 0x57, 0x92, 0x57, 0xe0,
714 0x58, 0x2f, 0x58, 0x7d, 0x58, 0xcb, 0x59, 0x1a, 0x59, 0x69, 0x59, 0xb8,
715 0x5a, 0x07, 0x5a, 0x56, 0x5a, 0xa6, 0x5a, 0xf5, 0x5b, 0x45, 0x5b, 0x95,
716 0x5b, 0xe5, 0x5c, 0x35, 0x5c, 0x86, 0x5c, 0xd6, 0x5d, 0x27, 0x5d, 0x78,
717 0x5d, 0xc9, 0x5e, 0x1a, 0x5e, 0x6c, 0x5e, 0xbd, 0x5f, 0x0f, 0x5f, 0x61,
718 0x5f, 0xb3, 0x60, 0x05, 0x60, 0x57, 0x60, 0xaa, 0x60, 0xfc, 0x61, 0x4f,
719 0x61, 0xa2, 0x61, 0xf5, 0x62, 0x49, 0x62, 0x9c, 0x62, 0xf0, 0x63, 0x43,
720 0x63, 0x97, 0x63, 0xeb, 0x64, 0x40, 0x64, 0x94, 0x64, 0xe9, 0x65, 0x3d,
721 0x65, 0x92, 0x65, 0xe7, 0x66, 0x3d, 0x66, 0x92, 0x66, 0xe8, 0x67, 0x3d,
722 0x67, 0x93, 0x67, 0xe9, 0x68, 0x3f, 0x68, 0x96, 0x68, 0xec, 0x69, 0x43,
723 0x69, 0x9a, 0x69, 0xf1, 0x6a, 0x48, 0x6a, 0x9f, 0x6a, 0xf7, 0x6b, 0x4f,
724 0x6b, 0xa7, 0x6b, 0xff, 0x6c, 0x57, 0x6c, 0xaf, 0x6d, 0x08, 0x6d, 0x60,
725 0x6d, 0xb9, 0x6e, 0x12, 0x6e, 0x6b, 0x6e, 0xc4, 0x6f, 0x1e, 0x6f, 0x78,
726 0x6f, 0xd1, 0x70, 0x2b, 0x70, 0x86, 0x70, 0xe0, 0x71, 0x3a, 0x71, 0x95,
727 0x71, 0xf0, 0x72, 0x4b, 0x72, 0xa6, 0x73, 0x01, 0x73, 0x5d, 0x73, 0xb8,
728 0x74, 0x14, 0x74, 0x70, 0x74, 0xcc, 0x75, 0x28, 0x75, 0x85, 0x75, 0xe1,
729 0x76, 0x3e, 0x76, 0x9b, 0x76, 0xf8, 0x77, 0x56, 0x77, 0xb3, 0x78, 0x11,
730 0x78, 0x6e, 0x78, 0xcc, 0x79, 0x2a, 0x79, 0x89, 0x79, 0xe7, 0x7a, 0x46,
731 0x7a, 0xa5, 0x7b, 0x04, 0x7b, 0x63, 0x7b, 0xc2, 0x7c, 0x21, 0x7c, 0x81,
732 0x7c, 0xe1, 0x7d, 0x41, 0x7d, 0xa1, 0x7e, 0x01, 0x7e, 0x62, 0x7e, 0xc2,
733 0x7f, 0x23, 0x7f, 0x84, 0x7f, 0xe5, 0x80, 0x47, 0x80, 0xa8, 0x81, 0x0a,
734 0x81, 0x6b, 0x81, 0xcd, 0x82, 0x30, 0x82, 0x92, 0x82, 0xf4, 0x83, 0x57,
735 0x83, 0xba, 0x84, 0x1d, 0x84, 0x80, 0x84, 0xe3, 0x85, 0x47, 0x85, 0xab,
736 0x86, 0x0e, 0x86, 0x72, 0x86, 0xd7, 0x87, 0x3b, 0x87, 0x9f, 0x88, 0x04,
737 0x88, 0x69, 0x88, 0xce, 0x89, 0x33, 0x89, 0x99, 0x89, 0xfe, 0x8a, 0x64,
738 0x8a, 0xca, 0x8b, 0x30, 0x8b, 0x96, 0x8b, 0xfc, 0x8c, 0x63, 0x8c, 0xca,
739 0x8d, 0x31, 0x8d, 0x98, 0x8d, 0xff, 0x8e, 0x66, 0x8e, 0xce, 0x8f, 0x36,
740 0x8f, 0x9e, 0x90, 0x06, 0x90, 0x6e, 0x90, 0xd6, 0x91, 0x3f, 0x91, 0xa8,
741 0x92, 0x11, 0x92, 0x7a, 0x92, 0xe3, 0x93, 0x4d, 0x93, 0xb6, 0x94, 0x20,
742 0x94, 0x8a, 0x94, 0xf4, 0x95, 0x5f, 0x95, 0xc9, 0x96, 0x34, 0x96, 0x9f,
743 0x97, 0x0a, 0x97, 0x75, 0x97, 0xe0, 0x98, 0x4c, 0x98, 0xb8, 0x99, 0x24,
744 0x99, 0x90, 0x99, 0xfc, 0x9a, 0x68, 0x9a, 0xd5, 0x9b, 0x42, 0x9b, 0xaf,
745 0x9c, 0x1c, 0x9c, 0x89, 0x9c, 0xf7, 0x9d, 0x64, 0x9d, 0xd2, 0x9e, 0x40,
746 0x9e, 0xae, 0x9f, 0x1d, 0x9f, 0x8b, 0x9f, 0xfa, 0xa0, 0x69, 0xa0, 0xd8,
747 0xa1, 0x47, 0xa1, 0xb6, 0xa2, 0x26, 0xa2, 0x96, 0xa3, 0x06, 0xa3, 0x76,
748 0xa3, 0xe6, 0xa4, 0x56, 0xa4, 0xc7, 0xa5, 0x38, 0xa5, 0xa9, 0xa6, 0x1a,
749 0xa6, 0x8b, 0xa6, 0xfd, 0xa7, 0x6e, 0xa7, 0xe0, 0xa8, 0x52, 0xa8, 0xc4,
750 0xa9, 0x37, 0xa9, 0xa9, 0xaa, 0x1c, 0xaa, 0x8f, 0xab, 0x02, 0xab, 0x75,
751 0xab, 0xe9, 0xac, 0x5c, 0xac, 0xd0, 0xad, 0x44, 0xad, 0xb8, 0xae, 0x2d,
752 0xae, 0xa1, 0xaf, 0x16, 0xaf, 0x8b, 0xb0, 0x00, 0xb0, 0x75, 0xb0, 0xea,
753 0xb1, 0x60, 0xb1, 0xd6, 0xb2, 0x4b, 0xb2, 0xc2, 0xb3, 0x38, 0xb3, 0xae,
754 0xb4, 0x25, 0xb4, 0x9c, 0xb5, 0x13, 0xb5, 0x8a, 0xb6, 0x01, 0xb6, 0x79,
755 0xb6, 0xf0, 0xb7, 0x68, 0xb7, 0xe0, 0xb8, 0x59, 0xb8, 0xd1, 0xb9, 0x4a,
756 0xb9, 0xc2, 0xba, 0x3b, 0xba, 0xb5, 0xbb, 0x2e, 0xbb, 0xa7, 0xbc, 0x21,
757 0xbc, 0x9b, 0xbd, 0x15, 0xbd, 0x8f, 0xbe, 0x0a, 0xbe, 0x84, 0xbe, 0xff,
758 0xbf, 0x7a, 0xbf, 0xf5, 0xc0, 0x70, 0xc0, 0xec, 0xc1, 0x67, 0xc1, 0xe3,
759 0xc2, 0x5f, 0xc2, 0xdb, 0xc3, 0x58, 0xc3, 0xd4, 0xc4, 0x51, 0xc4, 0xce,
760 0xc5, 0x4b, 0xc5, 0xc8, 0xc6, 0x46, 0xc6, 0xc3, 0xc7, 0x41, 0xc7, 0xbf,
761 0xc8, 0x3d, 0xc8, 0xbc, 0xc9, 0x3a, 0xc9, 0xb9, 0xca, 0x38, 0xca, 0xb7,
762 0xcb, 0x36, 0xcb, 0xb6, 0xcc, 0x35, 0xcc, 0xb5, 0xcd, 0x35, 0xcd, 0xb5,
763 0xce, 0x36, 0xce, 0xb6, 0xcf, 0x37, 0xcf, 0xb8, 0xd0, 0x39, 0xd0, 0xba,
764 0xd1, 0x3c, 0xd1, 0xbe, 0xd2, 0x3f, 0xd2, 0xc1, 0xd3, 0x44, 0xd3, 0xc6,
765 0xd4, 0x49, 0xd4, 0xcb, 0xd5, 0x4e, 0xd5, 0xd1, 0xd6, 0x55, 0xd6, 0xd8,
766 0xd7, 0x5c, 0xd7, 0xe0, 0xd8, 0x64, 0xd8, 0xe8, 0xd9, 0x6c, 0xd9, 0xf1,
767 0xda, 0x76, 0xda, 0xfb, 0xdb, 0x80, 0xdc, 0x05, 0xdc, 0x8a, 0xdd, 0x10,
768 0xdd, 0x96, 0xde, 0x1c, 0xde, 0xa2, 0xdf, 0x29, 0xdf, 0xaf, 0xe0, 0x36,
769 0xe0, 0xbd, 0xe1, 0x44, 0xe1, 0xcc, 0xe2, 0x53, 0xe2, 0xdb, 0xe3, 0x63,
770 0xe3, 0xeb, 0xe4, 0x73, 0xe4, 0xfc, 0xe5, 0x84, 0xe6, 0x0d, 0xe6, 0x96,
771 0xe7, 0x1f, 0xe7, 0xa9, 0xe8, 0x32, 0xe8, 0xbc, 0xe9, 0x46, 0xe9, 0xd0,
772 0xea, 0x5b, 0xea, 0xe5, 0xeb, 0x70, 0xeb, 0xfb, 0xec, 0x86, 0xed, 0x11,
773 0xed, 0x9c, 0xee, 0x28, 0xee, 0xb4, 0xef, 0x40, 0xef, 0xcc, 0xf0, 0x58,
774 0xf0, 0xe5, 0xf1, 0x72, 0xf1, 0xff, 0xf2, 0x8c, 0xf3, 0x19, 0xf3, 0xa7,
775 0xf4, 0x34, 0xf4, 0xc2, 0xf5, 0x50, 0xf5, 0xde, 0xf6, 0x6d, 0xf6, 0xfb,
776 0xf7, 0x8a, 0xf8, 0x19, 0xf8, 0xa8, 0xf9, 0x38, 0xf9, 0xc7, 0xfa, 0x57,
777 0xfa, 0xe7, 0xfb, 0x77, 0xfc, 0x07, 0xfc, 0x98, 0xfd, 0x29, 0xfd, 0xba,
778 0xfe, 0x4b, 0xfe, 0xdc, 0xff, 0x6d, 0xff, 0xff
779 };
780
782 *profile;
783
784 MagickBooleanType
785 status;
786
787 assert(image != (Image *) NULL);
788 assert(image->signature == MagickCoreSignature);
789 if (GetImageProfile(image,"icc") != (const StringInfo *) NULL)
790 return(MagickFalse);
791 profile=AcquireStringInfo(sizeof(sRGBProfile));
792 SetStringInfoDatum(profile,sRGBProfile);
793 status=SetImageProfile(image,"icc",profile);
794 profile=DestroyStringInfo(profile);
795 return(status);
796}
797
798MagickExport MagickBooleanType ProfileImage(Image *image,const char *name,
799 const void *datum,const size_t length,
800 const MagickBooleanType magick_unused(clone))
801{
802#define GetLCMSPixel(source_info,pixel,index) (source_info.scale[index]* \
803 ((QuantumScale*(MagickRealType) (pixel))+source_info.translate[index]))
804#define ProfileImageTag "Profile/Image"
805#define SetLCMSPixel(target_info,pixel,index) ClampToQuantum( \
806 target_info.scale[index]*(((MagickRealType) QuantumRange*pixel)+ \
807 target_info.translate[index]))
808#define ThrowProfileException(severity,tag,context) \
809{ \
810 if (profile != (StringInfo *) NULL) \
811 profile=DestroyStringInfo(profile); \
812 if (cms_context != (cmsContext) NULL) \
813 cmsDeleteContext(cms_context); \
814 if (source_info.profile != (cmsHPROFILE) NULL) \
815 (void) cmsCloseProfile(source_info.profile); \
816 if (target_info.profile != (cmsHPROFILE) NULL) \
817 (void) cmsCloseProfile(target_info.profile); \
818 ThrowBinaryException(severity,tag,context); \
819}
820
821 MagickBooleanType
822 status;
823
825 *profile;
826
827 magick_unreferenced(clone);
828
829 assert(image != (Image *) NULL);
830 assert(image->signature == MagickCoreSignature);
831 assert(name != (const char *) NULL);
832 if (IsEventLogging() != MagickFalse)
833 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
834 if ((datum == (const void *) NULL) || (length == 0))
835 {
836 char
837 *next;
838
839 /*
840 Delete image profile(s).
841 */
842 ResetImageProfileIterator(image);
843 for (next=GetNextImageProfile(image); next != (const char *) NULL; )
844 {
845 if (IsOptionMember(next,name) != MagickFalse)
846 {
847 (void) DeleteImageProfile(image,next);
848 ResetImageProfileIterator(image);
849 }
850 next=GetNextImageProfile(image);
851 }
852 return(MagickTrue);
853 }
854 /*
855 Add a ICC, IPTC, or generic profile to the image.
856 */
857 status=MagickTrue;
858 profile=AcquireStringInfo((size_t) length);
859 SetStringInfoDatum(profile,(unsigned char *) datum);
860 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
861 status=SetImageProfile(image,name,profile);
862 else
863 {
864 const StringInfo
865 *icc_profile;
866
867 icc_profile=GetImageProfile(image,"icc");
868 if ((icc_profile != (const StringInfo *) NULL) &&
869 (CompareStringInfo(icc_profile,profile) == 0))
870 {
871 const char
872 *value;
873
874 value=GetImageProperty(image,"exif:ColorSpace");
875 (void) value;
876 if (LocaleCompare(value,"1") != 0)
877 (void) SetsRGBImageProfile(image);
878 value=GetImageProperty(image,"exif:InteroperabilityIndex");
879 if (LocaleCompare(value,"R98.") != 0)
880 (void) SetsRGBImageProfile(image);
881 icc_profile=GetImageProfile(image,"icc");
882 }
883 if ((icc_profile != (const StringInfo *) NULL) &&
884 (CompareStringInfo(icc_profile,profile) == 0))
885 {
886 profile=DestroyStringInfo(profile);
887 return(MagickTrue);
888 }
889#if !defined(MAGICKCORE_LCMS_DELEGATE)
890 (void) ThrowMagickException(&image->exception,GetMagickModule(),
891 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (LCMS)",
892 image->filename);
893#else
894 {
895 cmsContext
896 cms_context;
897
898 LCMSInfo
899 source_info,
900 target_info;
901
902 /*
903 Transform pixel colors as defined by the color profiles.
904 */
905 cms_context=cmsCreateContext(NULL,image);
906 if (cms_context == (cmsContext) NULL)
907 {
908 profile=DestroyStringInfo(profile);
909 ThrowBinaryImageException(ResourceLimitError,
910 "ColorspaceColorProfileMismatch",name);
911 }
912 cmsSetLogErrorHandlerTHR(cms_context,LCMSExceptionHandler);
913 source_info.profile=cmsOpenProfileFromMemTHR(cms_context,
914 GetStringInfoDatum(profile),(cmsUInt32Number)
915 GetStringInfoLength(profile));
916 if (source_info.profile == (cmsHPROFILE) NULL)
917 {
918 profile=DestroyStringInfo(profile);
919 cmsDeleteContext(cms_context);
920 ThrowBinaryImageException(ResourceLimitError,
921 "ColorspaceColorProfileMismatch",name);
922 }
923 if ((cmsGetDeviceClass(source_info.profile) != cmsSigLinkClass) &&
924 (icc_profile == (StringInfo *) NULL))
925 status=SetImageProfile(image,name,profile);
926 else
927 {
929 *image_view;
930
931 cmsColorSpaceSignature
932 signature;
933
934 cmsHTRANSFORM
935 *magick_restrict transform;
936
937 cmsUInt32Number
938 flags;
939
941 *exception;
942
943 MagickOffsetType
944 progress;
945
946 ssize_t
947 y;
948
949 exception=(&image->exception);
950 target_info.profile=(cmsHPROFILE) NULL;
951 if (icc_profile != (StringInfo *) NULL)
952 {
953 target_info.profile=source_info.profile;
954 source_info.profile=cmsOpenProfileFromMemTHR(cms_context,
955 GetStringInfoDatum(icc_profile),(cmsUInt32Number)
956 GetStringInfoLength(icc_profile));
957 if (source_info.profile == (cmsHPROFILE) NULL)
958 ThrowProfileException(ResourceLimitError,
959 "ColorspaceColorProfileMismatch",name);
960 }
961 SetLCMSInfoScale(&source_info,1.0);
962 SetLCMSInfoTranslate(&source_info,0.0);
963 source_info.colorspace=sRGBColorspace;
964 source_info.channels=3;
965 switch (cmsGetColorSpace(source_info.profile))
966 {
967 case cmsSigCmykData:
968 {
969 source_info.colorspace=CMYKColorspace;
970 source_info.channels=4;
971 source_info.type=(cmsUInt32Number) TYPE_CMYK_DBL;
972 SetLCMSInfoScale(&source_info,100.0);
973 break;
974 }
975 case cmsSigGrayData:
976 {
977 source_info.colorspace=GRAYColorspace;
978 source_info.channels=1;
979 source_info.type=(cmsUInt32Number) TYPE_GRAY_DBL;
980 break;
981 }
982 case cmsSigLabData:
983 {
984 source_info.colorspace=LabColorspace;
985 source_info.type=(cmsUInt32Number) TYPE_Lab_DBL;
986 source_info.scale[0]=100.0;
987 source_info.scale[1]=255.0;
988 source_info.scale[2]=255.0;
989#if !defined(MAGICKCORE_HDRI_SUPPORT)
990 source_info.translate[1]=(-0.5);
991 source_info.translate[2]=(-0.5);
992#endif
993 break;
994 }
995 case cmsSigRgbData:
996 {
997 source_info.colorspace=sRGBColorspace;
998 source_info.type=(cmsUInt32Number) TYPE_RGB_DBL;
999 break;
1000 }
1001 case cmsSigXYZData:
1002 {
1003 source_info.colorspace=XYZColorspace;
1004 source_info.type=(cmsUInt32Number) TYPE_XYZ_DBL;
1005 break;
1006 }
1007 default:
1008 ThrowProfileException(ImageError,
1009 "ColorspaceColorProfileMismatch",name);
1010 }
1011 signature=cmsGetPCS(source_info.profile);
1012 if (target_info.profile != (cmsHPROFILE) NULL)
1013 signature=cmsGetColorSpace(target_info.profile);
1014 SetLCMSInfoScale(&target_info,1.0);
1015 SetLCMSInfoTranslate(&target_info,0.0);
1016 target_info.channels=3;
1017 switch (signature)
1018 {
1019 case cmsSigCmykData:
1020 {
1021 target_info.colorspace=CMYKColorspace;
1022 target_info.channels=4;
1023 target_info.type=(cmsUInt32Number) TYPE_CMYK_DBL;
1024 SetLCMSInfoScale(&target_info,0.01);
1025 break;
1026 }
1027 case cmsSigGrayData:
1028 {
1029 target_info.colorspace=GRAYColorspace;
1030 target_info.channels=1;
1031 target_info.type=(cmsUInt32Number) TYPE_GRAY_DBL;
1032 break;
1033 }
1034 case cmsSigLabData:
1035 {
1036 target_info.colorspace=LabColorspace;
1037 target_info.type=(cmsUInt32Number) TYPE_Lab_DBL;
1038 target_info.scale[0]=0.01;
1039 target_info.scale[1]=1/255.0;
1040 target_info.scale[2]=1/255.0;
1041#if !defined(MAGICKCORE_HDRI_SUPPORT)
1042 target_info.translate[1]=0.5;
1043 target_info.translate[2]=0.5;
1044#endif
1045 break;
1046 }
1047 case cmsSigRgbData:
1048 {
1049 target_info.colorspace=sRGBColorspace;
1050 target_info.type=(cmsUInt32Number) TYPE_RGB_DBL;
1051 break;
1052 }
1053 case cmsSigXYZData:
1054 {
1055 target_info.colorspace=XYZColorspace;
1056 target_info.type=(cmsUInt32Number) TYPE_XYZ_DBL;
1057 break;
1058 }
1059 default:
1060 ThrowProfileException(ImageError,
1061 "ColorspaceColorProfileMismatch",name);
1062 }
1063 switch (image->rendering_intent)
1064 {
1065 case AbsoluteIntent:
1066 {
1067 target_info.intent=INTENT_ABSOLUTE_COLORIMETRIC;
1068 break;
1069 }
1070 case PerceptualIntent:
1071 {
1072 target_info.intent=INTENT_PERCEPTUAL;
1073 break;
1074 }
1075 case RelativeIntent:
1076 {
1077 target_info.intent=INTENT_RELATIVE_COLORIMETRIC;
1078 break;
1079 }
1080 case SaturationIntent:
1081 {
1082 target_info.intent=INTENT_SATURATION;
1083 break;
1084 }
1085 default:
1086 {
1087 target_info.intent=INTENT_PERCEPTUAL;
1088 break;
1089 }
1090 }
1091 flags=cmsFLAGS_HIGHRESPRECALC;
1092#if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
1093 if (image->black_point_compensation != MagickFalse)
1094 flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
1095#endif
1096 transform=AcquireTransformTLS(&source_info,&target_info,
1097 flags,cms_context);
1098 if (transform == (cmsHTRANSFORM *) NULL)
1099 ThrowProfileException(ImageError,"UnableToCreateColorTransform",
1100 name);
1101 /*
1102 Transform image as dictated by the source & target image profiles.
1103 */
1104 source_info.pixels=AcquirePixelTLS(image->columns,
1105 source_info.channels);
1106 target_info.pixels=AcquirePixelTLS(image->columns,
1107 target_info.channels);
1108 if ((source_info.pixels == (double **) NULL) ||
1109 (target_info.pixels == (double **) NULL))
1110 {
1111 target_info.pixels=DestroyPixelTLS(target_info.pixels);
1112 source_info.pixels=DestroyPixelTLS(source_info.pixels);
1113 transform=DestroyTransformTLS(transform);
1114 ThrowProfileException(ResourceLimitError,
1115 "MemoryAllocationFailed",image->filename);
1116 }
1117 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1118 {
1119 target_info.pixels=DestroyPixelTLS(target_info.pixels);
1120 source_info.pixels=DestroyPixelTLS(source_info.pixels);
1121 transform=DestroyTransformTLS(transform);
1122 profile=DestroyStringInfo(profile);
1123 if (source_info.profile != (cmsHPROFILE) NULL)
1124 (void) cmsCloseProfile(source_info.profile);
1125 if (target_info.profile != (cmsHPROFILE) NULL)
1126 (void) cmsCloseProfile(target_info.profile);
1127 return(MagickFalse);
1128 }
1129 if (target_info.colorspace == CMYKColorspace)
1130 (void) SetImageColorspace(image,target_info.colorspace);
1131 progress=0;
1132 image_view=AcquireAuthenticCacheView(image,exception);
1133#if defined(MAGICKCORE_OPENMP_SUPPORT)
1134 #pragma omp parallel for schedule(static) shared(status) \
1135 magick_number_threads(image,image,image->rows,1)
1136#endif
1137 for (y=0; y < (ssize_t) image->rows; y++)
1138 {
1139 const int
1140 id = GetOpenMPThreadId();
1141
1142 MagickBooleanType
1143 sync;
1144
1145 IndexPacket
1146 *magick_restrict indexes;
1147
1148 double
1149 *p;
1150
1152 *magick_restrict q;
1153
1154 ssize_t
1155 x;
1156
1157 if (status == MagickFalse)
1158 continue;
1159 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1160 exception);
1161 if (q == (PixelPacket *) NULL)
1162 {
1163 status=MagickFalse;
1164 continue;
1165 }
1166 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1167 p=source_info.pixels[id];
1168 for (x=0; x < (ssize_t) image->columns; x++)
1169 {
1170 *p++=GetLCMSPixel(source_info,GetPixelRed(q),0);
1171 if (source_info.channels > 1)
1172 {
1173 *p++=GetLCMSPixel(source_info,GetPixelGreen(q),1);
1174 *p++=GetLCMSPixel(source_info,GetPixelBlue(q),2);
1175 }
1176 if (source_info.channels > 3)
1177 {
1178 *p=GetLCMSPixel(source_info,0,3);
1179 if (indexes != (IndexPacket *) NULL)
1180 *p=GetLCMSPixel(source_info,GetPixelIndex(indexes+x),3);
1181 p++;
1182 }
1183 q++;
1184 }
1185 cmsDoTransform(transform[id],source_info.pixels[id],
1186 target_info.pixels[id],(unsigned int) image->columns);
1187 p=target_info.pixels[id];
1188 q-=image->columns;
1189 for (x=0; x < (ssize_t) image->columns; x++)
1190 {
1191 SetPixelRed(q,SetLCMSPixel(target_info,*p,0));
1192 SetPixelGreen(q,GetPixelRed(q));
1193 SetPixelBlue(q,GetPixelRed(q));
1194 p++;
1195 if (target_info.channels > 1)
1196 {
1197 SetPixelGreen(q,SetLCMSPixel(target_info,*p,1));
1198 p++;
1199 SetPixelBlue(q,SetLCMSPixel(target_info,*p,2));
1200 p++;
1201 }
1202 if (target_info.channels > 3)
1203 {
1204 if (indexes != (IndexPacket *) NULL)
1205 SetPixelIndex(indexes+x,SetLCMSPixel(target_info,*p,3));
1206 p++;
1207 }
1208 q++;
1209 }
1210 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1211 if (sync == MagickFalse)
1212 status=MagickFalse;
1213 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1214 {
1215 MagickBooleanType
1216 proceed;
1217
1218#if defined(MAGICKCORE_OPENMP_SUPPORT)
1219 #pragma omp atomic
1220#endif
1221 progress++;
1222 proceed=SetImageProgress(image,ProfileImageTag,progress,
1223 image->rows);
1224 if (proceed == MagickFalse)
1225 status=MagickFalse;
1226 }
1227 }
1228 image_view=DestroyCacheView(image_view);
1229 (void) SetImageColorspace(image,target_info.colorspace);
1230 switch (signature)
1231 {
1232 case cmsSigRgbData:
1233 {
1234 image->type=image->matte == MagickFalse ? TrueColorType :
1235 TrueColorMatteType;
1236 break;
1237 }
1238 case cmsSigCmykData:
1239 {
1240 image->type=image->matte == MagickFalse ? ColorSeparationType :
1241 ColorSeparationMatteType;
1242 break;
1243 }
1244 case cmsSigGrayData:
1245 {
1246 image->type=image->matte == MagickFalse ? GrayscaleType :
1247 GrayscaleMatteType;
1248 break;
1249 }
1250 default:
1251 break;
1252 }
1253 target_info.pixels=DestroyPixelTLS(target_info.pixels);
1254 source_info.pixels=DestroyPixelTLS(source_info.pixels);
1255 transform=DestroyTransformTLS(transform);
1256 if ((status != MagickFalse) &&
1257 (cmsGetDeviceClass(source_info.profile) != cmsSigLinkClass))
1258 status=SetImageProfile(image,name,profile);
1259 if (target_info.profile != (cmsHPROFILE) NULL)
1260 (void) cmsCloseProfile(target_info.profile);
1261 }
1262 (void) cmsCloseProfile(source_info.profile);
1263 cmsDeleteContext(cms_context);
1264 }
1265#endif
1266 }
1267 profile=DestroyStringInfo(profile);
1268 return(status);
1269}
1270
1271/*
1272%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1273% %
1274% %
1275% %
1276% R e m o v e I m a g e P r o f i l e %
1277% %
1278% %
1279% %
1280%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1281%
1282% RemoveImageProfile() removes a named profile from the image and returns its
1283% value.
1284%
1285% The format of the RemoveImageProfile method is:
1286%
1287% void *RemoveImageProfile(Image *image,const char *name)
1288%
1289% A description of each parameter follows:
1290%
1291% o image: the image.
1292%
1293% o name: the profile name.
1294%
1295*/
1296MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
1297{
1299 *profile;
1300
1301 assert(image != (Image *) NULL);
1302 assert(image->signature == MagickCoreSignature);
1303 if (IsEventLogging() != MagickFalse)
1304 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1305 if (image->profiles == (SplayTreeInfo *) NULL)
1306 return((StringInfo *) NULL);
1307 if (LocaleCompare(name,"icc") == 0)
1308 {
1309 /*
1310 Continue to support deprecated color profile for now.
1311 */
1312 image->color_profile.length=0;
1313 image->color_profile.info=(unsigned char *) NULL;
1314 }
1315 if (LocaleCompare(name,"iptc") == 0)
1316 {
1317 /*
1318 Continue to support deprecated IPTC profile for now.
1319 */
1320 image->iptc_profile.length=0;
1321 image->iptc_profile.info=(unsigned char *) NULL;
1322 }
1323 WriteTo8BimProfile(image,name,(StringInfo *) NULL);
1324 profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
1325 image->profiles,name);
1326 return(profile);
1327}
1328
1329/*
1330%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1331% %
1332% %
1333% %
1334% R e s e t P r o f i l e I t e r a t o r %
1335% %
1336% %
1337% %
1338%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1339%
1340% ResetImageProfileIterator() resets the image profile iterator. Use it in
1341% conjunction with GetNextImageProfile() to iterate over all the profiles
1342% associated with an image.
1343%
1344% The format of the ResetImageProfileIterator method is:
1345%
1346% ResetImageProfileIterator(Image *image)
1347%
1348% A description of each parameter follows:
1349%
1350% o image: the image.
1351%
1352*/
1353MagickExport void ResetImageProfileIterator(const Image *image)
1354{
1355 assert(image != (Image *) NULL);
1356 assert(image->signature == MagickCoreSignature);
1357 if (IsEventLogging() != MagickFalse)
1358 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1359 if (image->profiles == (SplayTreeInfo *) NULL)
1360 return;
1361 ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
1362}
1363
1364/*
1365%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1366% %
1367% %
1368% %
1369% S e t I m a g e P r o f i l e %
1370% %
1371% %
1372% %
1373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1374%
1375% SetImageProfile() adds a named profile to the image. If a profile with the
1376% same name already exists, it is replaced. This method differs from the
1377% ProfileImage() method in that it does not apply CMS color profiles.
1378%
1379% The format of the SetImageProfile method is:
1380%
1381% MagickBooleanType SetImageProfile(Image *image,const char *name,
1382% const StringInfo *profile)
1383%
1384% A description of each parameter follows:
1385%
1386% o image: the image.
1387%
1388% o name: the profile name, for example icc, exif, and 8bim (8bim is the
1389% Photoshop wrapper for iptc profiles).
1390%
1391% o profile: A StringInfo structure that contains the named profile.
1392%
1393*/
1394
1395static void *DestroyProfile(void *profile)
1396{
1397 return((void *) DestroyStringInfo((StringInfo *) profile));
1398}
1399
1400static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1401 unsigned char *quantum)
1402{
1403 *quantum=(*p++);
1404 return(p);
1405}
1406
1407static inline const unsigned char *ReadResourceLong(const unsigned char *p,
1408 unsigned int *quantum)
1409{
1410 *quantum=(unsigned int) (*p++) << 24;
1411 *quantum|=(unsigned int) (*p++) << 16;
1412 *quantum|=(unsigned int) (*p++) << 8;
1413 *quantum|=(unsigned int) (*p++);
1414 return(p);
1415}
1416
1417static inline const unsigned char *ReadResourceShort(const unsigned char *p,
1418 unsigned short *quantum)
1419{
1420 *quantum=(unsigned short) (*p++) << 8;
1421 *quantum|=(unsigned short) (*p++);
1422 return(p);
1423}
1424
1425static inline void WriteResourceLong(unsigned char *p,
1426 const unsigned int quantum)
1427{
1428 unsigned char
1429 buffer[4];
1430
1431 buffer[0]=(unsigned char) (quantum >> 24);
1432 buffer[1]=(unsigned char) (quantum >> 16);
1433 buffer[2]=(unsigned char) (quantum >> 8);
1434 buffer[3]=(unsigned char) quantum;
1435 (void) memcpy(p,buffer,4);
1436}
1437
1438static void WriteTo8BimProfile(Image *image,const char *name,
1439 const StringInfo *profile)
1440{
1441
1442 const unsigned char
1443 *datum,
1444 *q;
1445
1446 const unsigned char
1447 *p;
1448
1449 size_t
1450 length;
1451
1453 *profile_8bim;
1454
1455 ssize_t
1456 count;
1457
1458 unsigned char
1459 length_byte;
1460
1461 unsigned int
1462 value;
1463
1464 unsigned short
1465 id,
1466 profile_id;
1467
1468 if (LocaleCompare(name,"icc") == 0)
1469 profile_id=0x040f;
1470 else
1471 if (LocaleCompare(name,"iptc") == 0)
1472 profile_id=0x0404;
1473 else
1474 if (LocaleCompare(name,"xmp") == 0)
1475 profile_id=0x0424;
1476 else
1477 return;
1478 profile_8bim=(StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
1479 image->profiles,"8bim");
1480 if (profile_8bim == (StringInfo *) NULL)
1481 return;
1482 datum=GetStringInfoDatum(profile_8bim);
1483 length=GetStringInfoLength(profile_8bim);
1484 for (p=datum; p < (datum+length-16); )
1485 {
1486 q=p;
1487 if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1488 break;
1489 p+=(ptrdiff_t) 4;
1490 p=ReadResourceShort(p,&id);
1491 p=ReadResourceByte(p,&length_byte);
1492 p+=(ptrdiff_t) length_byte;
1493 if (((length_byte+1) & 0x01) != 0)
1494 p++;
1495 if (p > (datum+length-4))
1496 break;
1497 p=ReadResourceLong(p,&value);
1498 count=(ssize_t) value;
1499 if ((count & 0x01) != 0)
1500 count++;
1501 if ((count < 0) || (p > (datum+length-count)) || (count > (ssize_t) length))
1502 break;
1503 if (id != profile_id)
1504 p+=(ptrdiff_t) count;
1505 else
1506 {
1507 size_t
1508 extent,
1509 offset;
1510
1511 ssize_t
1512 extract_extent;
1513
1515 *extract_profile;
1516
1517 extract_extent=0;
1518 extent=(datum+length)-(p+count);
1519 if (profile == (StringInfo *) NULL)
1520 {
1521 offset=(q-datum);
1522 extract_profile=AcquireStringInfo(offset+extent);
1523 (void) memcpy(extract_profile->datum,datum,offset);
1524 }
1525 else
1526 {
1527 offset=(p-datum);
1528 extract_extent=profile->length;
1529 if ((extract_extent & 0x01) != 0)
1530 extract_extent++;
1531 extract_profile=AcquireStringInfo(offset+extract_extent+extent);
1532 (void) memcpy(extract_profile->datum,datum,offset-4);
1533 WriteResourceLong(extract_profile->datum+offset-4,(unsigned int)
1534 profile->length);
1535 (void) memcpy(extract_profile->datum+offset,
1536 profile->datum,profile->length);
1537 }
1538 (void) memcpy(extract_profile->datum+offset+extract_extent,
1539 p+count,extent);
1540 (void) AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1541 ConstantString("8bim"),CloneStringInfo(extract_profile));
1542 extract_profile=DestroyStringInfo(extract_profile);
1543 break;
1544 }
1545 }
1546}
1547
1548static void GetProfilesFromResourceBlock(Image *image,
1549 const StringInfo *resource_block)
1550{
1551 const unsigned char
1552 *datum;
1553
1554 const unsigned char
1555 *p;
1556
1557 size_t
1558 length;
1559
1560 ssize_t
1561 count;
1562
1564 *profile;
1565
1566 unsigned char
1567 length_byte;
1568
1569 unsigned int
1570 value;
1571
1572 unsigned short
1573 id;
1574
1575 datum=GetStringInfoDatum(resource_block);
1576 length=GetStringInfoLength(resource_block);
1577 for (p=datum; p < (datum+length-16); )
1578 {
1579 if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1580 break;
1581 p+=(ptrdiff_t) 4;
1582 p=ReadResourceShort(p,&id);
1583 p=ReadResourceByte(p,&length_byte);
1584 p+=(ptrdiff_t) length_byte;
1585 if (((length_byte+1) & 0x01) != 0)
1586 p++;
1587 if (p > (datum+length-4))
1588 break;
1589 p=ReadResourceLong(p,&value);
1590 count=(ssize_t) value;
1591 if ((p > (datum+length-count)) || (count > (ssize_t) length) ||
1592 (count <= 0))
1593 break;
1594 switch (id)
1595 {
1596 case 0x03ed:
1597 {
1598 unsigned int
1599 resolution;
1600
1601 unsigned short
1602 units;
1603
1604 /*
1605 Resolution.
1606 */
1607 if (count < 10)
1608 break;
1609 p=ReadResourceLong(p,&resolution);
1610 image->x_resolution=((double) resolution)/65536.0;
1611 p=ReadResourceShort(p,&units)+2;
1612 p=ReadResourceLong(p,&resolution)+4;
1613 image->y_resolution=((double) resolution)/65536.0;
1614 /*
1615 Values are always stored as pixels per inch.
1616 */
1617 if ((ResolutionType) units != PixelsPerCentimeterResolution)
1618 image->units=PixelsPerInchResolution;
1619 else
1620 {
1621 image->units=PixelsPerCentimeterResolution;
1622 image->x_resolution/=2.54;
1623 image->y_resolution/=2.54;
1624 }
1625 break;
1626 }
1627 case 0x0404:
1628 {
1629 /*
1630 IPTC Profile
1631 */
1632 profile=AcquireStringInfo(count);
1633 SetStringInfoDatum(profile,p);
1634 (void) SetImageProfileInternal(image,"iptc",profile,MagickTrue);
1635 profile=DestroyStringInfo(profile);
1636 p+=(ptrdiff_t) count;
1637 break;
1638 }
1639 case 0x040c:
1640 {
1641 /*
1642 Thumbnail.
1643 */
1644 p+=(ptrdiff_t) count;
1645 break;
1646 }
1647 case 0x040f:
1648 {
1649 /*
1650 ICC profile.
1651 */
1652 profile=AcquireStringInfo(count);
1653 SetStringInfoDatum(profile,p);
1654 (void) SetImageProfileInternal(image,"icc",profile,MagickTrue);
1655 profile=DestroyStringInfo(profile);
1656 p+=(ptrdiff_t) count;
1657 break;
1658 }
1659 case 0x0422:
1660 {
1661 /*
1662 EXIF Profile.
1663 */
1664 profile=AcquireStringInfo(count);
1665 SetStringInfoDatum(profile,p);
1666 (void) SetImageProfileInternal(image,"exif",profile,MagickTrue);
1667 profile=DestroyStringInfo(profile);
1668 p+=(ptrdiff_t) count;
1669 break;
1670 }
1671 case 0x0424:
1672 {
1673 /*
1674 XMP Profile.
1675 */
1676 profile=AcquireStringInfo(count);
1677 SetStringInfoDatum(profile,p);
1678 (void) SetImageProfileInternal(image,"xmp",profile,MagickTrue);
1679 profile=DestroyStringInfo(profile);
1680 p+=(ptrdiff_t) count;
1681 break;
1682 }
1683 default:
1684 {
1685 p+=(ptrdiff_t) count;
1686 break;
1687 }
1688 }
1689 if ((count & 0x01) != 0)
1690 p++;
1691 }
1692}
1693
1694#if defined(MAGICKCORE_XML_DELEGATE)
1695static MagickBooleanType ValidateXMPProfile(Image *image,
1696 const StringInfo *profile)
1697{
1698 xmlDocPtr
1699 document;
1700
1701 /*
1702 Parse XML profile.
1703 */
1704 const char *artifact=GetImageArtifact(image,"xmp:validate");
1705 if (IsStringTrue(artifact) == MagickFalse)
1706 return(MagickTrue);
1707 document=xmlReadMemory((const char *) GetStringInfoDatum(profile),(int)
1708 GetStringInfoLength(profile),"xmp.xml",NULL,XML_PARSE_NOERROR |
1709 XML_PARSE_NOWARNING);
1710 if (document == (xmlDocPtr) NULL)
1711 {
1712 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1713 ImageWarning,"CorruptImageProfile","`%s' (XMP)",image->filename);
1714 return(MagickFalse);
1715 }
1716 xmlFreeDoc(document);
1717 return(MagickTrue);
1718}
1719#else
1720static MagickBooleanType ValidateXMPProfile(Image *image,
1721 const StringInfo *profile)
1722{
1723 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1724 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (XML)",
1725 image->filename);
1726 return(MagickFalse);
1727}
1728#endif
1729
1730static MagickBooleanType SetImageProfileInternal(Image *image,const char *name,
1731 const StringInfo *profile,const MagickBooleanType recursive)
1732{
1733 char
1734 key[MaxTextExtent];
1735
1736 MagickBooleanType
1737 status;
1738
1739 assert(image != (Image *) NULL);
1740 assert(image->signature == MagickCoreSignature);
1741 if (IsEventLogging() != MagickFalse)
1742 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1743 if ((LocaleCompare(name,"xmp") == 0) &&
1744 (ValidateXMPProfile(image,profile) == MagickFalse))
1745 return(MagickTrue);
1746 if (image->profiles == (SplayTreeInfo *) NULL)
1747 image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
1748 DestroyProfile);
1749 (void) CopyMagickString(key,name,MaxTextExtent);
1750 LocaleLower(key);
1751 status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1752 ConstantString(key),CloneStringInfo(profile));
1753 if ((status != MagickFalse) &&
1754 ((LocaleCompare(name,"icc") == 0) || (LocaleCompare(name,"icm") == 0)))
1755 {
1756 const StringInfo
1757 *icc_profile;
1758
1759 /*
1760 Continue to support deprecated color profile member.
1761 */
1762 icc_profile=GetImageProfile(image,name);
1763 if (icc_profile != (const StringInfo *) NULL)
1764 {
1765 image->color_profile.length=GetStringInfoLength(icc_profile);
1766 image->color_profile.info=GetStringInfoDatum(icc_profile);
1767 }
1768 }
1769 if ((status != MagickFalse) &&
1770 ((LocaleCompare(name,"iptc") == 0) || (LocaleCompare(name,"8bim") == 0)))
1771 {
1772 const StringInfo
1773 *iptc_profile;
1774
1775 /*
1776 Continue to support deprecated IPTC profile member.
1777 */
1778 iptc_profile=GetImageProfile(image,name);
1779 if (iptc_profile != (const StringInfo *) NULL)
1780 {
1781 image->iptc_profile.length=GetStringInfoLength(iptc_profile);
1782 image->iptc_profile.info=GetStringInfoDatum(iptc_profile);
1783 }
1784 }
1785 if (status != MagickFalse)
1786 {
1787 if (LocaleCompare(name,"8bim") == 0)
1788 GetProfilesFromResourceBlock(image,profile);
1789 else
1790 if (recursive == MagickFalse)
1791 WriteTo8BimProfile(image,name,profile);
1792 }
1793 return(status);
1794}
1795
1796MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
1797 const StringInfo *profile)
1798{
1799 return(SetImageProfileInternal(image,name,profile,MagickFalse));
1800}
1801
1802/*
1803%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1804% %
1805% %
1806% %
1807% S y n c I m a g e P r o f i l e s %
1808% %
1809% %
1810% %
1811%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1812%
1813% SyncImageProfiles() synchronizes image properties with the image profiles.
1814% Currently we only support updating the EXIF resolution and orientation.
1815%
1816% The format of the SyncImageProfiles method is:
1817%
1818% MagickBooleanType SyncImageProfiles(Image *image)
1819%
1820% A description of each parameter follows:
1821%
1822% o image: the image.
1823%
1824*/
1825
1826static inline int ReadProfileByte(unsigned char **p,size_t *length)
1827{
1828 int
1829 c;
1830
1831 if (*length < 1)
1832 return(EOF);
1833 c=(int) (*(*p)++);
1834 (*length)--;
1835 return(c);
1836}
1837
1838static inline signed short ReadProfileShort(const EndianType endian,
1839 unsigned char *buffer)
1840{
1841 union
1842 {
1843 unsigned int
1844 unsigned_value;
1845
1846 signed int
1847 signed_value;
1848 } quantum;
1849
1850 unsigned short
1851 value;
1852
1853 if (endian == LSBEndian)
1854 {
1855 value=(unsigned short) buffer[1] << 8;
1856 value|=(unsigned short) buffer[0];
1857 quantum.unsigned_value=value & 0xffff;
1858 return(quantum.signed_value);
1859 }
1860 value=(unsigned short) buffer[0] << 8;
1861 value|=(unsigned short) buffer[1];
1862 quantum.unsigned_value=value & 0xffff;
1863 return(quantum.signed_value);
1864}
1865
1866static inline signed int ReadProfileLong(const EndianType endian,
1867 unsigned char *buffer)
1868{
1869 union
1870 {
1871 unsigned int
1872 unsigned_value;
1873
1874 signed int
1875 signed_value;
1876 } quantum;
1877
1878 unsigned int
1879 value;
1880
1881 if (endian == LSBEndian)
1882 {
1883 value=(unsigned int) buffer[3] << 24;
1884 value|=(unsigned int) buffer[2] << 16;
1885 value|=(unsigned int) buffer[1] << 8;
1886 value|=(unsigned int) buffer[0];
1887 quantum.unsigned_value=value & 0xffffffff;
1888 return(quantum.signed_value);
1889 }
1890 value=(unsigned int) buffer[0] << 24;
1891 value|=(unsigned int) buffer[1] << 16;
1892 value|=(unsigned int) buffer[2] << 8;
1893 value|=(unsigned int) buffer[3];
1894 quantum.unsigned_value=value & 0xffffffff;
1895 return(quantum.signed_value);
1896}
1897
1898static inline signed int ReadProfileMSBLong(unsigned char **p,size_t *length)
1899{
1900 signed int
1901 value;
1902
1903 if (*length < 4)
1904 return(0);
1905 value=ReadProfileLong(MSBEndian,*p);
1906 (*length)-=4;
1907 *p+=4;
1908 return(value);
1909}
1910
1911static inline signed short ReadProfileMSBShort(unsigned char **p,
1912 size_t *length)
1913{
1914 signed short
1915 value;
1916
1917 if (*length < 2)
1918 return(0);
1919 value=ReadProfileShort(MSBEndian,*p);
1920 (*length)-=2;
1921 *p+=2;
1922 return(value);
1923}
1924
1925static inline void WriteProfileLong(const EndianType endian,
1926 const size_t value,unsigned char *p)
1927{
1928 unsigned char
1929 buffer[4];
1930
1931 if (endian == LSBEndian)
1932 {
1933 buffer[0]=(unsigned char) value;
1934 buffer[1]=(unsigned char) (value >> 8);
1935 buffer[2]=(unsigned char) (value >> 16);
1936 buffer[3]=(unsigned char) (value >> 24);
1937 (void) memcpy(p,buffer,4);
1938 return;
1939 }
1940 buffer[0]=(unsigned char) (value >> 24);
1941 buffer[1]=(unsigned char) (value >> 16);
1942 buffer[2]=(unsigned char) (value >> 8);
1943 buffer[3]=(unsigned char) value;
1944 (void) memcpy(p,buffer,4);
1945}
1946
1947static void WriteProfileShort(const EndianType endian,
1948 const unsigned short value,unsigned char *p)
1949{
1950 unsigned char
1951 buffer[2];
1952
1953 if (endian == LSBEndian)
1954 {
1955 buffer[0]=(unsigned char) value;
1956 buffer[1]=(unsigned char) (value >> 8);
1957 (void) memcpy(p,buffer,2);
1958 return;
1959 }
1960 buffer[0]=(unsigned char) (value >> 8);
1961 buffer[1]=(unsigned char) value;
1962 (void) memcpy(p,buffer,2);
1963}
1964
1965static MagickBooleanType SyncExifProfile(const Image *image,unsigned char *exif,
1966 size_t length)
1967{
1968#define MaxDirectoryStack 16
1969#define EXIF_DELIMITER "\n"
1970#define EXIF_NUM_FORMATS 12
1971#define TAG_EXIF_OFFSET 0x8769
1972#define TAG_INTEROP_OFFSET 0xa005
1973
1974 typedef struct _DirectoryInfo
1975 {
1976 unsigned char
1977 *directory;
1978
1979 size_t
1980 entry;
1981 } DirectoryInfo;
1982
1983 DirectoryInfo
1984 directory_stack[MaxDirectoryStack] = { { 0, 0 } };
1985
1986 EndianType
1987 endian;
1988
1989 size_t
1990 entry,
1991 number_entries;
1992
1994 *exif_resources;
1995
1996 ssize_t
1997 id,
1998 level,
1999 offset;
2000
2001 static int
2002 format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
2003
2004 unsigned char
2005 *directory;
2006
2007 if (length < 16)
2008 return(MagickFalse);
2009 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
2010 if ((id != 0x4949) && (id != 0x4D4D))
2011 {
2012 while (length != 0)
2013 {
2014 if (ReadProfileByte(&exif,&length) != 0x45)
2015 continue;
2016 if (ReadProfileByte(&exif,&length) != 0x78)
2017 continue;
2018 if (ReadProfileByte(&exif,&length) != 0x69)
2019 continue;
2020 if (ReadProfileByte(&exif,&length) != 0x66)
2021 continue;
2022 if (ReadProfileByte(&exif,&length) != 0x00)
2023 continue;
2024 if (ReadProfileByte(&exif,&length) != 0x00)
2025 continue;
2026 break;
2027 }
2028 if (length < 16)
2029 return(MagickFalse);
2030 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
2031 }
2032 endian=LSBEndian;
2033 if (id == 0x4949)
2034 endian=LSBEndian;
2035 else
2036 if (id == 0x4D4D)
2037 endian=MSBEndian;
2038 else
2039 return(MagickFalse);
2040 if (ReadProfileShort(endian,exif+2) != 0x002a)
2041 return(MagickFalse);
2042 /*
2043 This the offset to the first IFD.
2044 */
2045 offset=(ssize_t) ReadProfileLong(endian,exif+4);
2046 if ((offset < 0) || ((size_t) offset >= length))
2047 return(MagickFalse);
2048 directory=exif+offset;
2049 level=0;
2050 entry=0;
2051 exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
2052 (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
2053 do
2054 {
2055 if (level > 0)
2056 {
2057 level--;
2058 directory=directory_stack[level].directory;
2059 entry=directory_stack[level].entry;
2060 }
2061 if ((directory < exif) || (directory > (exif+length-2)))
2062 break;
2063 /*
2064 Determine how many entries there are in the current IFD.
2065 */
2066 number_entries=ReadProfileShort(endian,directory);
2067 for ( ; entry < number_entries; entry++)
2068 {
2069 int
2070 components;
2071
2072 unsigned char
2073 *p,
2074 *q;
2075
2076 size_t
2077 number_bytes;
2078
2079 ssize_t
2080 format,
2081 tag_value;
2082
2083 q=(unsigned char *) (directory+2+(12*entry));
2084 if (q > (exif+length-12))
2085 break; /* corrupt EXIF */
2086 if (GetValueFromSplayTree(exif_resources,q) == q)
2087 break;
2088 (void) AddValueToSplayTree(exif_resources,q,q);
2089 tag_value=(ssize_t) ReadProfileShort(endian,q);
2090 format=(ssize_t) ReadProfileShort(endian,q+2);
2091 if ((format < 0) || ((format-1) >= EXIF_NUM_FORMATS))
2092 break;
2093 components=(int) ReadProfileLong(endian,q+4);
2094 if (components < 0)
2095 break; /* corrupt EXIF */
2096 number_bytes=(size_t) components*format_bytes[format];
2097 if ((ssize_t) number_bytes < components)
2098 break; /* prevent overflow */
2099 if (number_bytes <= 4)
2100 p=q+8;
2101 else
2102 {
2103 /*
2104 The directory entry contains an offset.
2105 */
2106 offset=(ssize_t) ReadProfileLong(endian,q+8);
2107 if ((offset < 0) || ((size_t) (offset+number_bytes) > length))
2108 continue;
2109 if (~length < number_bytes)
2110 continue; /* prevent overflow */
2111 p=(unsigned char *) (exif+offset);
2112 }
2113 switch (tag_value)
2114 {
2115 case 0x011a:
2116 {
2117 (void) WriteProfileLong(endian,(size_t) (image->x_resolution+0.5),p);
2118 if (number_bytes == 8)
2119 (void) WriteProfileLong(endian,1UL,p+4);
2120 break;
2121 }
2122 case 0x011b:
2123 {
2124 (void) WriteProfileLong(endian,(size_t) (image->y_resolution+0.5),p);
2125 if (number_bytes == 8)
2126 (void) WriteProfileLong(endian,1UL,p+4);
2127 break;
2128 }
2129 case 0x0112:
2130 {
2131 if (number_bytes == 4)
2132 {
2133 (void) WriteProfileLong(endian,(size_t) image->orientation,p);
2134 break;
2135 }
2136 (void) WriteProfileShort(endian,(unsigned short) image->orientation,
2137 p);
2138 break;
2139 }
2140 case 0x0128:
2141 {
2142 if (number_bytes == 4)
2143 {
2144 (void) WriteProfileLong(endian,((size_t) image->units)+1,p);
2145 break;
2146 }
2147 (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
2148 break;
2149 }
2150 default:
2151 break;
2152 }
2153 if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
2154 {
2155 offset=(ssize_t) ReadProfileLong(endian,p);
2156 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
2157 {
2158 directory_stack[level].directory=directory;
2159 entry++;
2160 directory_stack[level].entry=entry;
2161 level++;
2162 directory_stack[level].directory=exif+offset;
2163 directory_stack[level].entry=0;
2164 level++;
2165 if ((directory+2+(12*number_entries)) > (exif+length))
2166 break;
2167 offset=(ssize_t) ReadProfileLong(endian,directory+2+(12*
2168 number_entries));
2169 if ((offset != 0) && ((size_t) offset < length) &&
2170 (level < (MaxDirectoryStack-2)))
2171 {
2172 directory_stack[level].directory=exif+offset;
2173 directory_stack[level].entry=0;
2174 level++;
2175 }
2176 }
2177 break;
2178 }
2179 }
2180 } while (level > 0);
2181 exif_resources=DestroySplayTree(exif_resources);
2182 return(MagickTrue);
2183}
2184
2185static MagickBooleanType Sync8BimProfile(const Image *image,
2186 const StringInfo *profile)
2187{
2188 size_t
2189 length;
2190
2191 ssize_t
2192 count;
2193
2194 unsigned char
2195 *p;
2196
2197 unsigned short
2198 id;
2199
2200 length=GetStringInfoLength(profile);
2201 p=GetStringInfoDatum(profile);
2202 while (length != 0)
2203 {
2204 if (ReadProfileByte(&p,&length) != 0x38)
2205 continue;
2206 if (ReadProfileByte(&p,&length) != 0x42)
2207 continue;
2208 if (ReadProfileByte(&p,&length) != 0x49)
2209 continue;
2210 if (ReadProfileByte(&p,&length) != 0x4D)
2211 continue;
2212 if (length < 7)
2213 return(MagickFalse);
2214 id=ReadProfileMSBShort(&p,&length);
2215 count=(ssize_t) ReadProfileByte(&p,&length);
2216 if ((count >= (ssize_t) length) || (count < 0))
2217 return(MagickFalse);
2218 p+=(ptrdiff_t) count;
2219 length-=count;
2220 if ((*p & 0x01) == 0)
2221 (void) ReadProfileByte(&p,&length);
2222 count=(ssize_t) ReadProfileMSBLong(&p,&length);
2223 if ((count > (ssize_t) length) || (count < 0))
2224 return(MagickFalse);
2225 if ((id == 0x3ED) && (count == 16))
2226 {
2227 if (image->units == PixelsPerCentimeterResolution)
2228 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2229 image->x_resolution*2.54*65536.0),p);
2230 else
2231 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2232 image->x_resolution*65536.0),p);
2233 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+4);
2234 if (image->units == PixelsPerCentimeterResolution)
2235 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2236 image->y_resolution*2.54*65536.0),p+8);
2237 else
2238 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2239 image->y_resolution*65536.0),p+8);
2240 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+12);
2241 }
2242 if (id == 0x0422)
2243 (void) SyncExifProfile(image,p,count);
2244 p+=(ptrdiff_t) count;
2245 length-=count;
2246 }
2247 return(MagickTrue);
2248}
2249
2250MagickExport MagickBooleanType SyncImageProfiles(Image *image)
2251{
2252 MagickBooleanType
2253 status;
2254
2256 *profile;
2257
2258 status=MagickTrue;
2259 profile=(StringInfo *) GetImageProfile(image,"8BIM");
2260 if (profile != (StringInfo *) NULL)
2261 if (Sync8BimProfile(image,profile) == MagickFalse)
2262 status=MagickFalse;
2263 profile=(StringInfo *) GetImageProfile(image,"EXIF");
2264 if (profile != (StringInfo *) NULL)
2265 if (SyncExifProfile(image,GetStringInfoDatum(profile),
2266 GetStringInfoLength(profile)) == MagickFalse)
2267 status=MagickFalse;
2268 return(status);
2269}