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