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