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