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