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 source_info.translate[1]=(-0.5);
990 source_info.translate[2]=(-0.5);
991 break;
992 }
993 case cmsSigRgbData:
994 {
995 source_info.colorspace=sRGBColorspace;
996 source_info.type=(cmsUInt32Number) TYPE_RGB_DBL;
997 break;
998 }
999 case cmsSigXYZData:
1000 {
1001 source_info.colorspace=XYZColorspace;
1002 source_info.type=(cmsUInt32Number) TYPE_XYZ_DBL;
1003 break;
1004 }
1005 default:
1006 ThrowProfileException(ImageError,
1007 "ColorspaceColorProfileMismatch",name);
1008 }
1009 signature=cmsGetPCS(source_info.profile);
1010 if (target_info.profile != (cmsHPROFILE) NULL)
1011 signature=cmsGetColorSpace(target_info.profile);
1012 SetLCMSInfoScale(&target_info,1.0);
1013 SetLCMSInfoTranslate(&target_info,0.0);
1014 target_info.channels=3;
1015 switch (signature)
1016 {
1017 case cmsSigCmykData:
1018 {
1019 target_info.colorspace=CMYKColorspace;
1020 target_info.channels=4;
1021 target_info.type=(cmsUInt32Number) TYPE_CMYK_DBL;
1022 SetLCMSInfoScale(&target_info,0.01);
1023 break;
1024 }
1025 case cmsSigGrayData:
1026 {
1027 target_info.colorspace=GRAYColorspace;
1028 target_info.channels=1;
1029 target_info.type=(cmsUInt32Number) TYPE_GRAY_DBL;
1030 break;
1031 }
1032 case cmsSigLabData:
1033 {
1034 target_info.colorspace=LabColorspace;
1035 target_info.type=(cmsUInt32Number) TYPE_Lab_DBL;
1036 target_info.scale[0]=0.01;
1037 target_info.scale[1]=1/255.0;
1038 target_info.scale[2]=1/255.0;
1039 target_info.translate[1]=0.5;
1040 target_info.translate[2]=0.5;
1041 break;
1042 }
1043 case cmsSigRgbData:
1044 {
1045 target_info.colorspace=sRGBColorspace;
1046 target_info.type=(cmsUInt32Number) TYPE_RGB_DBL;
1047 break;
1048 }
1049 case cmsSigXYZData:
1050 {
1051 target_info.colorspace=XYZColorspace;
1052 target_info.type=(cmsUInt32Number) TYPE_XYZ_DBL;
1053 break;
1054 }
1055 default:
1056 ThrowProfileException(ImageError,
1057 "ColorspaceColorProfileMismatch",name);
1058 }
1059 switch (image->rendering_intent)
1060 {
1061 case AbsoluteIntent:
1062 {
1063 target_info.intent=INTENT_ABSOLUTE_COLORIMETRIC;
1064 break;
1065 }
1066 case PerceptualIntent:
1067 {
1068 target_info.intent=INTENT_PERCEPTUAL;
1069 break;
1070 }
1071 case RelativeIntent:
1072 {
1073 target_info.intent=INTENT_RELATIVE_COLORIMETRIC;
1074 break;
1075 }
1076 case SaturationIntent:
1077 {
1078 target_info.intent=INTENT_SATURATION;
1079 break;
1080 }
1081 default:
1082 {
1083 target_info.intent=INTENT_PERCEPTUAL;
1084 break;
1085 }
1086 }
1087 flags=cmsFLAGS_HIGHRESPRECALC;
1088#if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
1089 if (image->black_point_compensation != MagickFalse)
1090 flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
1091#endif
1092 transform=AcquireTransformTLS(&source_info,&target_info,
1093 flags,cms_context);
1094 if (transform == (cmsHTRANSFORM *) NULL)
1095 ThrowProfileException(ImageError,"UnableToCreateColorTransform",
1096 name);
1097 /*
1098 Transform image as dictated by the source & target image profiles.
1099 */
1100 source_info.pixels=AcquirePixelTLS(image->columns,
1101 source_info.channels);
1102 target_info.pixels=AcquirePixelTLS(image->columns,
1103 target_info.channels);
1104 if ((source_info.pixels == (double **) NULL) ||
1105 (target_info.pixels == (double **) NULL))
1106 {
1107 target_info.pixels=DestroyPixelTLS(target_info.pixels);
1108 source_info.pixels=DestroyPixelTLS(source_info.pixels);
1109 transform=DestroyTransformTLS(transform);
1110 ThrowProfileException(ResourceLimitError,
1111 "MemoryAllocationFailed",image->filename);
1112 }
1113 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1114 {
1115 target_info.pixels=DestroyPixelTLS(target_info.pixels);
1116 source_info.pixels=DestroyPixelTLS(source_info.pixels);
1117 transform=DestroyTransformTLS(transform);
1118 profile=DestroyStringInfo(profile);
1119 if (source_info.profile != (cmsHPROFILE) NULL)
1120 (void) cmsCloseProfile(source_info.profile);
1121 if (target_info.profile != (cmsHPROFILE) NULL)
1122 (void) cmsCloseProfile(target_info.profile);
1123 return(MagickFalse);
1124 }
1125 if (target_info.colorspace == CMYKColorspace)
1126 (void) SetImageColorspace(image,target_info.colorspace);
1127 progress=0;
1128 image_view=AcquireAuthenticCacheView(image,exception);
1129#if defined(MAGICKCORE_OPENMP_SUPPORT)
1130 #pragma omp parallel for schedule(static) shared(status) \
1131 magick_number_threads(image,image,image->rows,1)
1132#endif
1133 for (y=0; y < (ssize_t) image->rows; y++)
1134 {
1135 const int
1136 id = GetOpenMPThreadId();
1137
1138 MagickBooleanType
1139 sync;
1140
1141 IndexPacket
1142 *magick_restrict indexes;
1143
1144 double
1145 *p;
1146
1148 *magick_restrict q;
1149
1150 ssize_t
1151 x;
1152
1153 if (status == MagickFalse)
1154 continue;
1155 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1156 exception);
1157 if (q == (PixelPacket *) NULL)
1158 {
1159 status=MagickFalse;
1160 continue;
1161 }
1162 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1163 p=source_info.pixels[id];
1164 for (x=0; x < (ssize_t) image->columns; x++)
1165 {
1166 *p++=GetLCMSPixel(source_info,GetPixelRed(q),0);
1167 if (source_info.channels > 1)
1168 {
1169 *p++=GetLCMSPixel(source_info,GetPixelGreen(q),1);
1170 *p++=GetLCMSPixel(source_info,GetPixelBlue(q),2);
1171 }
1172 if (source_info.channels > 3)
1173 {
1174 *p=GetLCMSPixel(source_info,0,3);
1175 if (indexes != (IndexPacket *) NULL)
1176 *p=GetLCMSPixel(source_info,GetPixelIndex(indexes+x),3);
1177 p++;
1178 }
1179 q++;
1180 }
1181 cmsDoTransform(transform[id],source_info.pixels[id],
1182 target_info.pixels[id],(unsigned int) image->columns);
1183 p=target_info.pixels[id];
1184 q-=image->columns;
1185 for (x=0; x < (ssize_t) image->columns; x++)
1186 {
1187 SetPixelRed(q,SetLCMSPixel(target_info,*p,0));
1188 SetPixelGreen(q,GetPixelRed(q));
1189 SetPixelBlue(q,GetPixelRed(q));
1190 p++;
1191 if (target_info.channels > 1)
1192 {
1193 SetPixelGreen(q,SetLCMSPixel(target_info,*p,1));
1194 p++;
1195 SetPixelBlue(q,SetLCMSPixel(target_info,*p,2));
1196 p++;
1197 }
1198 if (target_info.channels > 3)
1199 {
1200 if (indexes != (IndexPacket *) NULL)
1201 SetPixelIndex(indexes+x,SetLCMSPixel(target_info,*p,3));
1202 p++;
1203 }
1204 q++;
1205 }
1206 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1207 if (sync == MagickFalse)
1208 status=MagickFalse;
1209 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1210 {
1211 MagickBooleanType
1212 proceed;
1213
1214#if defined(MAGICKCORE_OPENMP_SUPPORT)
1215 #pragma omp atomic
1216#endif
1217 progress++;
1218 proceed=SetImageProgress(image,ProfileImageTag,progress,
1219 image->rows);
1220 if (proceed == MagickFalse)
1221 status=MagickFalse;
1222 }
1223 }
1224 image_view=DestroyCacheView(image_view);
1225 (void) SetImageColorspace(image,target_info.colorspace);
1226 switch (signature)
1227 {
1228 case cmsSigRgbData:
1229 {
1230 image->type=image->matte == MagickFalse ? TrueColorType :
1231 TrueColorMatteType;
1232 break;
1233 }
1234 case cmsSigCmykData:
1235 {
1236 image->type=image->matte == MagickFalse ? ColorSeparationType :
1237 ColorSeparationMatteType;
1238 break;
1239 }
1240 case cmsSigGrayData:
1241 {
1242 image->type=image->matte == MagickFalse ? GrayscaleType :
1243 GrayscaleMatteType;
1244 break;
1245 }
1246 default:
1247 break;
1248 }
1249 target_info.pixels=DestroyPixelTLS(target_info.pixels);
1250 source_info.pixels=DestroyPixelTLS(source_info.pixels);
1251 transform=DestroyTransformTLS(transform);
1252 if ((status != MagickFalse) &&
1253 (cmsGetDeviceClass(source_info.profile) != cmsSigLinkClass))
1254 status=SetImageProfile(image,name,profile);
1255 if (target_info.profile != (cmsHPROFILE) NULL)
1256 (void) cmsCloseProfile(target_info.profile);
1257 }
1258 (void) cmsCloseProfile(source_info.profile);
1259 cmsDeleteContext(cms_context);
1260 }
1261#endif
1262 }
1263 profile=DestroyStringInfo(profile);
1264 return(status);
1265}
1266
1267/*
1268%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1269% %
1270% %
1271% %
1272% R e m o v e I m a g e P r o f i l e %
1273% %
1274% %
1275% %
1276%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1277%
1278% RemoveImageProfile() removes a named profile from the image and returns its
1279% value.
1280%
1281% The format of the RemoveImageProfile method is:
1282%
1283% void *RemoveImageProfile(Image *image,const char *name)
1284%
1285% A description of each parameter follows:
1286%
1287% o image: the image.
1288%
1289% o name: the profile name.
1290%
1291*/
1292MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
1293{
1295 *profile;
1296
1297 assert(image != (Image *) NULL);
1298 assert(image->signature == MagickCoreSignature);
1299 if (IsEventLogging() != MagickFalse)
1300 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1301 if (image->profiles == (SplayTreeInfo *) NULL)
1302 return((StringInfo *) NULL);
1303 if (LocaleCompare(name,"icc") == 0)
1304 {
1305 /*
1306 Continue to support deprecated color profile for now.
1307 */
1308 image->color_profile.length=0;
1309 image->color_profile.info=(unsigned char *) NULL;
1310 }
1311 if (LocaleCompare(name,"iptc") == 0)
1312 {
1313 /*
1314 Continue to support deprecated IPTC profile for now.
1315 */
1316 image->iptc_profile.length=0;
1317 image->iptc_profile.info=(unsigned char *) NULL;
1318 }
1319 WriteTo8BimProfile(image,name,(StringInfo *) NULL);
1320 profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
1321 image->profiles,name);
1322 return(profile);
1323}
1324
1325/*
1326%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1327% %
1328% %
1329% %
1330% R e s e t P r o f i l e I t e r a t o r %
1331% %
1332% %
1333% %
1334%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1335%
1336% ResetImageProfileIterator() resets the image profile iterator. Use it in
1337% conjunction with GetNextImageProfile() to iterate over all the profiles
1338% associated with an image.
1339%
1340% The format of the ResetImageProfileIterator method is:
1341%
1342% ResetImageProfileIterator(Image *image)
1343%
1344% A description of each parameter follows:
1345%
1346% o image: the image.
1347%
1348*/
1349MagickExport void ResetImageProfileIterator(const Image *image)
1350{
1351 assert(image != (Image *) NULL);
1352 assert(image->signature == MagickCoreSignature);
1353 if (IsEventLogging() != MagickFalse)
1354 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1355 if (image->profiles == (SplayTreeInfo *) NULL)
1356 return;
1357 ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
1358}
1359
1360/*
1361%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1362% %
1363% %
1364% %
1365% S e t I m a g e P r o f i l e %
1366% %
1367% %
1368% %
1369%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1370%
1371% SetImageProfile() adds a named profile to the image. If a profile with the
1372% same name already exists, it is replaced. This method differs from the
1373% ProfileImage() method in that it does not apply CMS color profiles.
1374%
1375% The format of the SetImageProfile method is:
1376%
1377% MagickBooleanType SetImageProfile(Image *image,const char *name,
1378% const StringInfo *profile)
1379%
1380% A description of each parameter follows:
1381%
1382% o image: the image.
1383%
1384% o name: the profile name, for example icc, exif, and 8bim (8bim is the
1385% Photoshop wrapper for iptc profiles).
1386%
1387% o profile: A StringInfo structure that contains the named profile.
1388%
1389*/
1390
1391static void *DestroyProfile(void *profile)
1392{
1393 return((void *) DestroyStringInfo((StringInfo *) profile));
1394}
1395
1396static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1397 unsigned char *quantum)
1398{
1399 *quantum=(*p++);
1400 return(p);
1401}
1402
1403static inline const unsigned char *ReadResourceLong(const unsigned char *p,
1404 unsigned int *quantum)
1405{
1406 *quantum=(unsigned int) (*p++) << 24;
1407 *quantum|=(unsigned int) (*p++) << 16;
1408 *quantum|=(unsigned int) (*p++) << 8;
1409 *quantum|=(unsigned int) (*p++);
1410 return(p);
1411}
1412
1413static inline const unsigned char *ReadResourceShort(const unsigned char *p,
1414 unsigned short *quantum)
1415{
1416 *quantum=(unsigned short) (*p++) << 8;
1417 *quantum|=(unsigned short) (*p++);
1418 return(p);
1419}
1420
1421static inline void WriteResourceLong(unsigned char *p,
1422 const unsigned int quantum)
1423{
1424 unsigned char
1425 buffer[4];
1426
1427 buffer[0]=(unsigned char) (quantum >> 24);
1428 buffer[1]=(unsigned char) (quantum >> 16);
1429 buffer[2]=(unsigned char) (quantum >> 8);
1430 buffer[3]=(unsigned char) quantum;
1431 (void) memcpy(p,buffer,4);
1432}
1433
1434static void WriteTo8BimProfile(Image *image,const char *name,
1435 const StringInfo *profile)
1436{
1437
1438 const unsigned char
1439 *datum,
1440 *q;
1441
1442 const unsigned char
1443 *p;
1444
1445 size_t
1446 length;
1447
1449 *profile_8bim;
1450
1451 ssize_t
1452 count;
1453
1454 unsigned char
1455 length_byte;
1456
1457 unsigned int
1458 value;
1459
1460 unsigned short
1461 id,
1462 profile_id;
1463
1464 if (LocaleCompare(name,"icc") == 0)
1465 profile_id=0x040f;
1466 else
1467 if (LocaleCompare(name,"iptc") == 0)
1468 profile_id=0x0404;
1469 else
1470 if (LocaleCompare(name,"xmp") == 0)
1471 profile_id=0x0424;
1472 else
1473 return;
1474 profile_8bim=(StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
1475 image->profiles,"8bim");
1476 if (profile_8bim == (StringInfo *) NULL)
1477 return;
1478 datum=GetStringInfoDatum(profile_8bim);
1479 length=GetStringInfoLength(profile_8bim);
1480 for (p=datum; p < (datum+length-16); )
1481 {
1482 q=p;
1483 if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1484 break;
1485 p+=4;
1486 p=ReadResourceShort(p,&id);
1487 p=ReadResourceByte(p,&length_byte);
1488 p+=length_byte;
1489 if (((length_byte+1) & 0x01) != 0)
1490 p++;
1491 if (p > (datum+length-4))
1492 break;
1493 p=ReadResourceLong(p,&value);
1494 count=(ssize_t) value;
1495 if ((count & 0x01) != 0)
1496 count++;
1497 if ((count < 0) || (p > (datum+length-count)) || (count > (ssize_t) length))
1498 break;
1499 if (id != profile_id)
1500 p+=count;
1501 else
1502 {
1503 size_t
1504 extent,
1505 offset;
1506
1507 ssize_t
1508 extract_extent;
1509
1511 *extract_profile;
1512
1513 extract_extent=0;
1514 extent=(datum+length)-(p+count);
1515 if (profile == (StringInfo *) NULL)
1516 {
1517 offset=(q-datum);
1518 extract_profile=AcquireStringInfo(offset+extent);
1519 (void) memcpy(extract_profile->datum,datum,offset);
1520 }
1521 else
1522 {
1523 offset=(p-datum);
1524 extract_extent=profile->length;
1525 if ((extract_extent & 0x01) != 0)
1526 extract_extent++;
1527 extract_profile=AcquireStringInfo(offset+extract_extent+extent);
1528 (void) memcpy(extract_profile->datum,datum,offset-4);
1529 WriteResourceLong(extract_profile->datum+offset-4,(unsigned int)
1530 profile->length);
1531 (void) memcpy(extract_profile->datum+offset,
1532 profile->datum,profile->length);
1533 }
1534 (void) memcpy(extract_profile->datum+offset+extract_extent,
1535 p+count,extent);
1536 (void) AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1537 ConstantString("8bim"),CloneStringInfo(extract_profile));
1538 extract_profile=DestroyStringInfo(extract_profile);
1539 break;
1540 }
1541 }
1542}
1543
1544static void GetProfilesFromResourceBlock(Image *image,
1545 const StringInfo *resource_block)
1546{
1547 const unsigned char
1548 *datum;
1549
1550 const unsigned char
1551 *p;
1552
1553 size_t
1554 length;
1555
1556 ssize_t
1557 count;
1558
1560 *profile;
1561
1562 unsigned char
1563 length_byte;
1564
1565 unsigned int
1566 value;
1567
1568 unsigned short
1569 id;
1570
1571 datum=GetStringInfoDatum(resource_block);
1572 length=GetStringInfoLength(resource_block);
1573 for (p=datum; p < (datum+length-16); )
1574 {
1575 if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1576 break;
1577 p+=4;
1578 p=ReadResourceShort(p,&id);
1579 p=ReadResourceByte(p,&length_byte);
1580 p+=length_byte;
1581 if (((length_byte+1) & 0x01) != 0)
1582 p++;
1583 if (p > (datum+length-4))
1584 break;
1585 p=ReadResourceLong(p,&value);
1586 count=(ssize_t) value;
1587 if ((p > (datum+length-count)) || (count > (ssize_t) length) ||
1588 (count <= 0))
1589 break;
1590 switch (id)
1591 {
1592 case 0x03ed:
1593 {
1594 unsigned int
1595 resolution;
1596
1597 unsigned short
1598 units;
1599
1600 /*
1601 Resolution.
1602 */
1603 if (count < 10)
1604 break;
1605 p=ReadResourceLong(p,&resolution);
1606 image->x_resolution=((double) resolution)/65536.0;
1607 p=ReadResourceShort(p,&units)+2;
1608 p=ReadResourceLong(p,&resolution)+4;
1609 image->y_resolution=((double) resolution)/65536.0;
1610 /*
1611 Values are always stored as pixels per inch.
1612 */
1613 if ((ResolutionType) units != PixelsPerCentimeterResolution)
1614 image->units=PixelsPerInchResolution;
1615 else
1616 {
1617 image->units=PixelsPerCentimeterResolution;
1618 image->x_resolution/=2.54;
1619 image->y_resolution/=2.54;
1620 }
1621 break;
1622 }
1623 case 0x0404:
1624 {
1625 /*
1626 IPTC Profile
1627 */
1628 profile=AcquireStringInfo(count);
1629 SetStringInfoDatum(profile,p);
1630 (void) SetImageProfileInternal(image,"iptc",profile,MagickTrue);
1631 profile=DestroyStringInfo(profile);
1632 p+=count;
1633 break;
1634 }
1635 case 0x040c:
1636 {
1637 /*
1638 Thumbnail.
1639 */
1640 p+=count;
1641 break;
1642 }
1643 case 0x040f:
1644 {
1645 /*
1646 ICC profile.
1647 */
1648 profile=AcquireStringInfo(count);
1649 SetStringInfoDatum(profile,p);
1650 (void) SetImageProfileInternal(image,"icc",profile,MagickTrue);
1651 profile=DestroyStringInfo(profile);
1652 p+=count;
1653 break;
1654 }
1655 case 0x0422:
1656 {
1657 /*
1658 EXIF Profile.
1659 */
1660 profile=AcquireStringInfo(count);
1661 SetStringInfoDatum(profile,p);
1662 (void) SetImageProfileInternal(image,"exif",profile,MagickTrue);
1663 profile=DestroyStringInfo(profile);
1664 p+=count;
1665 break;
1666 }
1667 case 0x0424:
1668 {
1669 /*
1670 XMP Profile.
1671 */
1672 profile=AcquireStringInfo(count);
1673 SetStringInfoDatum(profile,p);
1674 (void) SetImageProfileInternal(image,"xmp",profile,MagickTrue);
1675 profile=DestroyStringInfo(profile);
1676 p+=count;
1677 break;
1678 }
1679 default:
1680 {
1681 p+=count;
1682 break;
1683 }
1684 }
1685 if ((count & 0x01) != 0)
1686 p++;
1687 }
1688}
1689
1690#if defined(MAGICKCORE_XML_DELEGATE)
1691static MagickBooleanType ValidateXMPProfile(Image *image,
1692 const StringInfo *profile)
1693{
1694 xmlDocPtr
1695 document;
1696
1697 /*
1698 Parse XML profile.
1699 */
1700 const char *artifact=GetImageArtifact(image,"xmp:validate");
1701 if (IsStringTrue(artifact) == MagickFalse)
1702 return(MagickTrue);
1703 document=xmlReadMemory((const char *) GetStringInfoDatum(profile),(int)
1704 GetStringInfoLength(profile),"xmp.xml",NULL,XML_PARSE_NOERROR |
1705 XML_PARSE_NOWARNING);
1706 if (document == (xmlDocPtr) NULL)
1707 {
1708 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1709 ImageWarning,"CorruptImageProfile","`%s' (XMP)",image->filename);
1710 return(MagickFalse);
1711 }
1712 xmlFreeDoc(document);
1713 return(MagickTrue);
1714}
1715#else
1716static MagickBooleanType ValidateXMPProfile(Image *image,
1717 const StringInfo *profile)
1718{
1719 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1720 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","'%s' (XML)",
1721 image->filename);
1722 return(MagickFalse);
1723}
1724#endif
1725
1726static MagickBooleanType SetImageProfileInternal(Image *image,const char *name,
1727 const StringInfo *profile,const MagickBooleanType recursive)
1728{
1729 char
1730 key[MaxTextExtent];
1731
1732 MagickBooleanType
1733 status;
1734
1735 assert(image != (Image *) NULL);
1736 assert(image->signature == MagickCoreSignature);
1737 if (IsEventLogging() != MagickFalse)
1738 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1739 if ((LocaleCompare(name,"xmp") == 0) &&
1740 (ValidateXMPProfile(image,profile) == MagickFalse))
1741 return(MagickTrue);
1742 if (image->profiles == (SplayTreeInfo *) NULL)
1743 image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
1744 DestroyProfile);
1745 (void) CopyMagickString(key,name,MaxTextExtent);
1746 LocaleLower(key);
1747 status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1748 ConstantString(key),CloneStringInfo(profile));
1749 if ((status != MagickFalse) &&
1750 ((LocaleCompare(name,"icc") == 0) || (LocaleCompare(name,"icm") == 0)))
1751 {
1752 const StringInfo
1753 *icc_profile;
1754
1755 /*
1756 Continue to support deprecated color profile member.
1757 */
1758 icc_profile=GetImageProfile(image,name);
1759 if (icc_profile != (const StringInfo *) NULL)
1760 {
1761 image->color_profile.length=GetStringInfoLength(icc_profile);
1762 image->color_profile.info=GetStringInfoDatum(icc_profile);
1763 }
1764 }
1765 if ((status != MagickFalse) &&
1766 ((LocaleCompare(name,"iptc") == 0) || (LocaleCompare(name,"8bim") == 0)))
1767 {
1768 const StringInfo
1769 *iptc_profile;
1770
1771 /*
1772 Continue to support deprecated IPTC profile member.
1773 */
1774 iptc_profile=GetImageProfile(image,name);
1775 if (iptc_profile != (const StringInfo *) NULL)
1776 {
1777 image->iptc_profile.length=GetStringInfoLength(iptc_profile);
1778 image->iptc_profile.info=GetStringInfoDatum(iptc_profile);
1779 }
1780 }
1781 if (status != MagickFalse)
1782 {
1783 if (LocaleCompare(name,"8bim") == 0)
1784 GetProfilesFromResourceBlock(image,profile);
1785 else
1786 if (recursive == MagickFalse)
1787 WriteTo8BimProfile(image,name,profile);
1788 }
1789 return(status);
1790}
1791
1792MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
1793 const StringInfo *profile)
1794{
1795 return(SetImageProfileInternal(image,name,profile,MagickFalse));
1796}
1797
1798/*
1799%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1800% %
1801% %
1802% %
1803% S y n c I m a g e P r o f i l e s %
1804% %
1805% %
1806% %
1807%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1808%
1809% SyncImageProfiles() synchronizes image properties with the image profiles.
1810% Currently we only support updating the EXIF resolution and orientation.
1811%
1812% The format of the SyncImageProfiles method is:
1813%
1814% MagickBooleanType SyncImageProfiles(Image *image)
1815%
1816% A description of each parameter follows:
1817%
1818% o image: the image.
1819%
1820*/
1821
1822static inline int ReadProfileByte(unsigned char **p,size_t *length)
1823{
1824 int
1825 c;
1826
1827 if (*length < 1)
1828 return(EOF);
1829 c=(int) (*(*p)++);
1830 (*length)--;
1831 return(c);
1832}
1833
1834static inline signed short ReadProfileShort(const EndianType endian,
1835 unsigned char *buffer)
1836{
1837 union
1838 {
1839 unsigned int
1840 unsigned_value;
1841
1842 signed int
1843 signed_value;
1844 } quantum;
1845
1846 unsigned short
1847 value;
1848
1849 if (endian == LSBEndian)
1850 {
1851 value=(unsigned short) buffer[1] << 8;
1852 value|=(unsigned short) buffer[0];
1853 quantum.unsigned_value=value & 0xffff;
1854 return(quantum.signed_value);
1855 }
1856 value=(unsigned short) buffer[0] << 8;
1857 value|=(unsigned short) buffer[1];
1858 quantum.unsigned_value=value & 0xffff;
1859 return(quantum.signed_value);
1860}
1861
1862static inline signed int ReadProfileLong(const EndianType endian,
1863 unsigned char *buffer)
1864{
1865 union
1866 {
1867 unsigned int
1868 unsigned_value;
1869
1870 signed int
1871 signed_value;
1872 } quantum;
1873
1874 unsigned int
1875 value;
1876
1877 if (endian == LSBEndian)
1878 {
1879 value=(unsigned int) buffer[3] << 24;
1880 value|=(unsigned int) buffer[2] << 16;
1881 value|=(unsigned int) buffer[1] << 8;
1882 value|=(unsigned int) buffer[0];
1883 quantum.unsigned_value=value & 0xffffffff;
1884 return(quantum.signed_value);
1885 }
1886 value=(unsigned int) buffer[0] << 24;
1887 value|=(unsigned int) buffer[1] << 16;
1888 value|=(unsigned int) buffer[2] << 8;
1889 value|=(unsigned int) buffer[3];
1890 quantum.unsigned_value=value & 0xffffffff;
1891 return(quantum.signed_value);
1892}
1893
1894static inline signed int ReadProfileMSBLong(unsigned char **p,size_t *length)
1895{
1896 signed int
1897 value;
1898
1899 if (*length < 4)
1900 return(0);
1901 value=ReadProfileLong(MSBEndian,*p);
1902 (*length)-=4;
1903 *p+=4;
1904 return(value);
1905}
1906
1907static inline signed short ReadProfileMSBShort(unsigned char **p,
1908 size_t *length)
1909{
1910 signed short
1911 value;
1912
1913 if (*length < 2)
1914 return(0);
1915 value=ReadProfileShort(MSBEndian,*p);
1916 (*length)-=2;
1917 *p+=2;
1918 return(value);
1919}
1920
1921static inline void WriteProfileLong(const EndianType endian,
1922 const size_t value,unsigned char *p)
1923{
1924 unsigned char
1925 buffer[4];
1926
1927 if (endian == LSBEndian)
1928 {
1929 buffer[0]=(unsigned char) value;
1930 buffer[1]=(unsigned char) (value >> 8);
1931 buffer[2]=(unsigned char) (value >> 16);
1932 buffer[3]=(unsigned char) (value >> 24);
1933 (void) memcpy(p,buffer,4);
1934 return;
1935 }
1936 buffer[0]=(unsigned char) (value >> 24);
1937 buffer[1]=(unsigned char) (value >> 16);
1938 buffer[2]=(unsigned char) (value >> 8);
1939 buffer[3]=(unsigned char) value;
1940 (void) memcpy(p,buffer,4);
1941}
1942
1943static void WriteProfileShort(const EndianType endian,
1944 const unsigned short value,unsigned char *p)
1945{
1946 unsigned char
1947 buffer[2];
1948
1949 if (endian == LSBEndian)
1950 {
1951 buffer[0]=(unsigned char) value;
1952 buffer[1]=(unsigned char) (value >> 8);
1953 (void) memcpy(p,buffer,2);
1954 return;
1955 }
1956 buffer[0]=(unsigned char) (value >> 8);
1957 buffer[1]=(unsigned char) value;
1958 (void) memcpy(p,buffer,2);
1959}
1960
1961static MagickBooleanType SyncExifProfile(const Image *image,unsigned char *exif,
1962 size_t length)
1963{
1964#define MaxDirectoryStack 16
1965#define EXIF_DELIMITER "\n"
1966#define EXIF_NUM_FORMATS 12
1967#define TAG_EXIF_OFFSET 0x8769
1968#define TAG_INTEROP_OFFSET 0xa005
1969
1970 typedef struct _DirectoryInfo
1971 {
1972 unsigned char
1973 *directory;
1974
1975 size_t
1976 entry;
1977 } DirectoryInfo;
1978
1979 DirectoryInfo
1980 directory_stack[MaxDirectoryStack] = { { 0, 0 } };
1981
1982 EndianType
1983 endian;
1984
1985 size_t
1986 entry,
1987 number_entries;
1988
1990 *exif_resources;
1991
1992 ssize_t
1993 id,
1994 level,
1995 offset;
1996
1997 static int
1998 format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1999
2000 unsigned char
2001 *directory;
2002
2003 if (length < 16)
2004 return(MagickFalse);
2005 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
2006 if ((id != 0x4949) && (id != 0x4D4D))
2007 {
2008 while (length != 0)
2009 {
2010 if (ReadProfileByte(&exif,&length) != 0x45)
2011 continue;
2012 if (ReadProfileByte(&exif,&length) != 0x78)
2013 continue;
2014 if (ReadProfileByte(&exif,&length) != 0x69)
2015 continue;
2016 if (ReadProfileByte(&exif,&length) != 0x66)
2017 continue;
2018 if (ReadProfileByte(&exif,&length) != 0x00)
2019 continue;
2020 if (ReadProfileByte(&exif,&length) != 0x00)
2021 continue;
2022 break;
2023 }
2024 if (length < 16)
2025 return(MagickFalse);
2026 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
2027 }
2028 endian=LSBEndian;
2029 if (id == 0x4949)
2030 endian=LSBEndian;
2031 else
2032 if (id == 0x4D4D)
2033 endian=MSBEndian;
2034 else
2035 return(MagickFalse);
2036 if (ReadProfileShort(endian,exif+2) != 0x002a)
2037 return(MagickFalse);
2038 /*
2039 This the offset to the first IFD.
2040 */
2041 offset=(ssize_t) ReadProfileLong(endian,exif+4);
2042 if ((offset < 0) || ((size_t) offset >= length))
2043 return(MagickFalse);
2044 directory=exif+offset;
2045 level=0;
2046 entry=0;
2047 exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
2048 (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
2049 do
2050 {
2051 if (level > 0)
2052 {
2053 level--;
2054 directory=directory_stack[level].directory;
2055 entry=directory_stack[level].entry;
2056 }
2057 if ((directory < exif) || (directory > (exif+length-2)))
2058 break;
2059 /*
2060 Determine how many entries there are in the current IFD.
2061 */
2062 number_entries=ReadProfileShort(endian,directory);
2063 for ( ; entry < number_entries; entry++)
2064 {
2065 int
2066 components;
2067
2068 unsigned char
2069 *p,
2070 *q;
2071
2072 size_t
2073 number_bytes;
2074
2075 ssize_t
2076 format,
2077 tag_value;
2078
2079 q=(unsigned char *) (directory+2+(12*entry));
2080 if (q > (exif+length-12))
2081 break; /* corrupt EXIF */
2082 if (GetValueFromSplayTree(exif_resources,q) == q)
2083 break;
2084 (void) AddValueToSplayTree(exif_resources,q,q);
2085 tag_value=(ssize_t) ReadProfileShort(endian,q);
2086 format=(ssize_t) ReadProfileShort(endian,q+2);
2087 if ((format < 0) || ((format-1) >= EXIF_NUM_FORMATS))
2088 break;
2089 components=(int) ReadProfileLong(endian,q+4);
2090 if (components < 0)
2091 break; /* corrupt EXIF */
2092 number_bytes=(size_t) components*format_bytes[format];
2093 if ((ssize_t) number_bytes < components)
2094 break; /* prevent overflow */
2095 if (number_bytes <= 4)
2096 p=q+8;
2097 else
2098 {
2099 /*
2100 The directory entry contains an offset.
2101 */
2102 offset=(ssize_t) ReadProfileLong(endian,q+8);
2103 if ((offset < 0) || ((size_t) (offset+number_bytes) > length))
2104 continue;
2105 if (~length < number_bytes)
2106 continue; /* prevent overflow */
2107 p=(unsigned char *) (exif+offset);
2108 }
2109 switch (tag_value)
2110 {
2111 case 0x011a:
2112 {
2113 (void) WriteProfileLong(endian,(size_t) (image->x_resolution+0.5),p);
2114 if (number_bytes == 8)
2115 (void) WriteProfileLong(endian,1UL,p+4);
2116 break;
2117 }
2118 case 0x011b:
2119 {
2120 (void) WriteProfileLong(endian,(size_t) (image->y_resolution+0.5),p);
2121 if (number_bytes == 8)
2122 (void) WriteProfileLong(endian,1UL,p+4);
2123 break;
2124 }
2125 case 0x0112:
2126 {
2127 if (number_bytes == 4)
2128 {
2129 (void) WriteProfileLong(endian,(size_t) image->orientation,p);
2130 break;
2131 }
2132 (void) WriteProfileShort(endian,(unsigned short) image->orientation,
2133 p);
2134 break;
2135 }
2136 case 0x0128:
2137 {
2138 if (number_bytes == 4)
2139 {
2140 (void) WriteProfileLong(endian,((size_t) image->units)+1,p);
2141 break;
2142 }
2143 (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
2144 break;
2145 }
2146 default:
2147 break;
2148 }
2149 if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
2150 {
2151 offset=(ssize_t) ReadProfileLong(endian,p);
2152 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
2153 {
2154 directory_stack[level].directory=directory;
2155 entry++;
2156 directory_stack[level].entry=entry;
2157 level++;
2158 directory_stack[level].directory=exif+offset;
2159 directory_stack[level].entry=0;
2160 level++;
2161 if ((directory+2+(12*number_entries)) > (exif+length))
2162 break;
2163 offset=(ssize_t) ReadProfileLong(endian,directory+2+(12*
2164 number_entries));
2165 if ((offset != 0) && ((size_t) offset < length) &&
2166 (level < (MaxDirectoryStack-2)))
2167 {
2168 directory_stack[level].directory=exif+offset;
2169 directory_stack[level].entry=0;
2170 level++;
2171 }
2172 }
2173 break;
2174 }
2175 }
2176 } while (level > 0);
2177 exif_resources=DestroySplayTree(exif_resources);
2178 return(MagickTrue);
2179}
2180
2181static MagickBooleanType Sync8BimProfile(const Image *image,
2182 const StringInfo *profile)
2183{
2184 size_t
2185 length;
2186
2187 ssize_t
2188 count;
2189
2190 unsigned char
2191 *p;
2192
2193 unsigned short
2194 id;
2195
2196 length=GetStringInfoLength(profile);
2197 p=GetStringInfoDatum(profile);
2198 while (length != 0)
2199 {
2200 if (ReadProfileByte(&p,&length) != 0x38)
2201 continue;
2202 if (ReadProfileByte(&p,&length) != 0x42)
2203 continue;
2204 if (ReadProfileByte(&p,&length) != 0x49)
2205 continue;
2206 if (ReadProfileByte(&p,&length) != 0x4D)
2207 continue;
2208 if (length < 7)
2209 return(MagickFalse);
2210 id=ReadProfileMSBShort(&p,&length);
2211 count=(ssize_t) ReadProfileByte(&p,&length);
2212 if ((count >= (ssize_t) length) || (count < 0))
2213 return(MagickFalse);
2214 p+=count;
2215 length-=count;
2216 if ((*p & 0x01) == 0)
2217 (void) ReadProfileByte(&p,&length);
2218 count=(ssize_t) ReadProfileMSBLong(&p,&length);
2219 if ((count > (ssize_t) length) || (count < 0))
2220 return(MagickFalse);
2221 if ((id == 0x3ED) && (count == 16))
2222 {
2223 if (image->units == PixelsPerCentimeterResolution)
2224 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2225 image->x_resolution*2.54*65536.0),p);
2226 else
2227 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2228 image->x_resolution*65536.0),p);
2229 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+4);
2230 if (image->units == PixelsPerCentimeterResolution)
2231 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2232 image->y_resolution*2.54*65536.0),p+8);
2233 else
2234 WriteProfileLong(MSBEndian,(unsigned int) CastDoubleToLong(
2235 image->y_resolution*65536.0),p+8);
2236 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+12);
2237 }
2238 if (id == 0x0422)
2239 (void) SyncExifProfile(image,p,count);
2240 p+=count;
2241 length-=count;
2242 }
2243 return(MagickTrue);
2244}
2245
2246MagickExport MagickBooleanType SyncImageProfiles(Image *image)
2247{
2248 MagickBooleanType
2249 status;
2250
2252 *profile;
2253
2254 status=MagickTrue;
2255 profile=(StringInfo *) GetImageProfile(image,"8BIM");
2256 if (profile != (StringInfo *) NULL)
2257 if (Sync8BimProfile(image,profile) == MagickFalse)
2258 status=MagickFalse;
2259 profile=(StringInfo *) GetImageProfile(image,"EXIF");
2260 if (profile != (StringInfo *) NULL)
2261 if (SyncExifProfile(image,GetStringInfoDatum(profile),
2262 GetStringInfoLength(profile)) == MagickFalse)
2263 status=MagickFalse;
2264 return(status);
2265}