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