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