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