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