blob: d91fbc23ba12c1d12a1a9089af1e8a06bf7b016c [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% July 1992 %
18% %
19% %
cristyb56bb242014-11-25 17:12:48 +000020% Copyright 1999-2015 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/attribute.h"
44#include "MagickCore/cache.h"
45#include "MagickCore/color.h"
cristy510d06a2011-07-06 23:43:54 +000046#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000047#include "MagickCore/configure.h"
48#include "MagickCore/exception.h"
49#include "MagickCore/exception-private.h"
50#include "MagickCore/hashmap.h"
51#include "MagickCore/image.h"
52#include "MagickCore/memory_.h"
53#include "MagickCore/monitor.h"
54#include "MagickCore/monitor-private.h"
55#include "MagickCore/option.h"
cristy759ba912014-06-26 11:59:43 +000056#include "MagickCore/option-private.h"
cristy4c08aed2011-07-01 19:47:50 +000057#include "MagickCore/pixel-accessor.h"
58#include "MagickCore/profile.h"
cristy7832dc22011-09-05 01:21:53 +000059#include "MagickCore/profile-private.h"
cristy4c08aed2011-07-01 19:47:50 +000060#include "MagickCore/property.h"
61#include "MagickCore/quantum.h"
62#include "MagickCore/quantum-private.h"
cristyac245f82012-05-05 17:13:57 +000063#include "MagickCore/resource_.h"
cristy4c08aed2011-07-01 19:47:50 +000064#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"
cristy3ed852e2009-09-05 21:47:34 +000069#if defined(MAGICKCORE_LCMS_DELEGATE)
cristyd6ac47f2015-06-06 13:46:55 +000070#if defined(MAGICKCORE_HAVE_LCMS_LCMS2_H)
71#include <wchar.h>
72#include <lcms/lcms2.h>
cristyfa2b4c32015-07-01 13:12:01 +000073#else
cristyd09bcf92010-03-25 03:04:45 +000074#include <wchar.h>
75#include "lcms2.h"
cristy3ed852e2009-09-05 21:47:34 +000076#endif
77#endif
78
79/*
dirke9dbde12014-04-23 16:03:29 +000080 Forward declarations
81*/
82static MagickBooleanType
83 SetImageProfileInternal(Image *,const char *,const StringInfo *,
84 const MagickBooleanType,ExceptionInfo *);
85
86static void
dirk9f4f3542014-04-21 17:19:03 +000087 WriteTo8BimProfile(Image *,const char*,const StringInfo *);
cristyd09bcf92010-03-25 03:04:45 +000088
89/*
cristy092d71c2011-10-14 18:01:29 +000090 Typedef declarations
91*/
cristy3fac9ec2011-11-17 18:04:39 +000092struct _ProfileInfo
cristy092d71c2011-10-14 18:01:29 +000093{
94 char
95 *name;
96
97 size_t
98 length;
99
100 unsigned char
101 *info;
102
103 size_t
104 signature;
105};
cristyd0b7ee52011-10-21 11:19:02 +0000106
107typedef struct _CMSExceptionInfo
108{
109 Image
110 *image;
111
112 ExceptionInfo
113 *exception;
114} CMSExceptionInfo;
cristy092d71c2011-10-14 18:01:29 +0000115
116/*
cristy3ed852e2009-09-05 21:47:34 +0000117%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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);
cristye1c94d92015-06-28 12:16:33 +0000145 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000146 if (image->debug != MagickFalse)
147 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
148 assert(clone_image != (const Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000149 assert(clone_image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000150 if (clone_image->profiles != (void *) NULL)
cristy419e2042013-07-18 22:14:51 +0000151 {
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 }
cristy3ed852e2009-09-05 21:47:34 +0000157 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*/
cristy04390e72010-03-08 00:50:13 +0000184MagickExport MagickBooleanType DeleteImageProfile(Image *image,const char *name)
cristy3ed852e2009-09-05 21:47:34 +0000185{
186 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000187 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000188 if (image->debug != MagickFalse)
189 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
190 if (image->profiles == (SplayTreeInfo *) NULL)
191 return(MagickFalse);
dirk9f4f3542014-04-21 17:19:03 +0000192 WriteTo8BimProfile(image,name,(StringInfo *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000193 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{
cristy3ed852e2009-09-05 21:47:34 +0000251 const StringInfo
252 *profile;
253
254 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000255 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000256 if (image->debug != MagickFalse)
257 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
258 if (image->profiles == (SplayTreeInfo *) NULL)
259 return((StringInfo *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000260 profile=(const StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
dirk32428f42015-04-14 20:59:59 +0000261 image->profiles,name);
cristy3ed852e2009-09-05 21:47:34 +0000262 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);
cristye1c94d92015-06-28 12:16:33 +0000290 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000291 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)
cristy3ed852e2009-09-05 21:47:34 +0000340static unsigned short **DestroyPixelThreadSet(unsigned short **pixels)
341{
cristybb503372010-05-27 20:51:26 +0000342 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000343 i;
344
345 assert(pixels != (unsigned short **) NULL);
cristyac245f82012-05-05 17:13:57 +0000346 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
cristy3ed852e2009-09-05 21:47:34 +0000347 if (pixels[i] != (unsigned short *) NULL)
348 pixels[i]=(unsigned short *) RelinquishMagickMemory(pixels[i]);
cristyb41ee102010-10-04 16:46:15 +0000349 pixels=(unsigned short **) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +0000350 return(pixels);
351}
352
353static unsigned short **AcquirePixelThreadSet(const size_t columns,
354 const size_t channels)
355{
cristybb503372010-05-27 20:51:26 +0000356 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000357 i;
358
359 unsigned short
360 **pixels;
361
cristybb503372010-05-27 20:51:26 +0000362 size_t
cristy3ed852e2009-09-05 21:47:34 +0000363 number_threads;
364
cristy9357bdd2012-07-30 12:28:34 +0000365 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
cristyb41ee102010-10-04 16:46:15 +0000366 pixels=(unsigned short **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +0000367 sizeof(*pixels));
368 if (pixels == (unsigned short **) NULL)
369 return((unsigned short **) NULL);
370 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
cristybb503372010-05-27 20:51:26 +0000371 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +0000372 {
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{
cristybb503372010-05-27 20:51:26 +0000383 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000384 i;
385
386 assert(transform != (cmsHTRANSFORM *) NULL);
cristyac245f82012-05-05 17:13:57 +0000387 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
cristy3ed852e2009-09-05 21:47:34 +0000388 if (transform[i] != (cmsHTRANSFORM) NULL)
389 cmsDeleteTransform(transform[i]);
cristyb41ee102010-10-04 16:46:15 +0000390 transform=(cmsHTRANSFORM *) RelinquishMagickMemory(transform);
cristy3ed852e2009-09-05 21:47:34 +0000391 return(transform);
392}
393
cristy7632f502010-06-18 13:26:31 +0000394static cmsHTRANSFORM *AcquireTransformThreadSet(Image *image,
cristyd09bcf92010-03-25 03:04:45 +0000395 const cmsHPROFILE source_profile,const cmsUInt32Number source_type,
cristy20a78b22010-04-05 01:22:12 +0000396 const cmsHPROFILE target_profile,const cmsUInt32Number target_type,
397 const int intent,const cmsUInt32Number flags)
cristy3ed852e2009-09-05 21:47:34 +0000398{
399 cmsHTRANSFORM
400 *transform;
401
cristybb503372010-05-27 20:51:26 +0000402 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000403 i;
404
cristybb503372010-05-27 20:51:26 +0000405 size_t
cristy3ed852e2009-09-05 21:47:34 +0000406 number_threads;
407
cristy9357bdd2012-07-30 12:28:34 +0000408 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
cristyb41ee102010-10-04 16:46:15 +0000409 transform=(cmsHTRANSFORM *) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +0000410 sizeof(*transform));
411 if (transform == (cmsHTRANSFORM *) NULL)
412 return((cmsHTRANSFORM *) NULL);
413 (void) ResetMagickMemory(transform,0,number_threads*sizeof(*transform));
cristybb503372010-05-27 20:51:26 +0000414 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +0000415 {
cristy9a00c142014-04-05 13:38:34 +0000416 transform[i]=cmsCreateTransformTHR((cmsContext) image,source_profile,
417 source_type,target_profile,target_type,intent,flags);
cristy3ed852e2009-09-05 21:47:34 +0000418 if (transform[i] == (cmsHTRANSFORM) NULL)
419 return(DestroyTransformThreadSet(transform));
420 }
421 return(transform);
422}
423#endif
424
cristyd0b7ee52011-10-21 11:19:02 +0000425#if defined(MAGICKCORE_LCMS_DELEGATE)
cristyd0b7ee52011-10-21 11:19:02 +0000426static 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 image=cms_exception->image;
440 exception=cms_exception->exception;
441 if (image == (Image *) NULL)
442 {
443 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
cristyefe601c2013-01-05 17:51:12 +0000444 "UnableToTransformColorspace","`%s'","unknown context");
cristyd0b7ee52011-10-21 11:19:02 +0000445 return;
446 }
447 if (image->debug != MagickFalse)
448 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%u, %s",
449 severity,message != (char *) NULL ? message : "no message");
450 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
cristyefe601c2013-01-05 17:51:12 +0000451 "UnableToTransformColorspace","`%s'",image->filename);
cristyd0b7ee52011-10-21 11:19:02 +0000452}
cristyd0b7ee52011-10-21 11:19:02 +0000453#endif
454
cristy35e92062014-08-26 14:13:16 +0000455static MagickBooleanType SetsRGBImageProfile(Image *image,
456 ExceptionInfo *exception)
457{
458 static unsigned char
459 sRGBProfile[] =
460 {
461 0x00, 0x00, 0x0c, 0x8c, 0x61, 0x72, 0x67, 0x6c, 0x02, 0x20, 0x00, 0x00,
462 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20,
463 0x07, 0xde, 0x00, 0x01, 0x00, 0x06, 0x00, 0x16, 0x00, 0x0f, 0x00, 0x3a,
464 0x61, 0x63, 0x73, 0x70, 0x4d, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00,
465 0x49, 0x45, 0x43, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00,
466 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6,
467 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x61, 0x72, 0x67, 0x6c,
468 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
469 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
470 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
471 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
472 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x99,
473 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0xec, 0x00, 0x00, 0x00, 0x67,
474 0x64, 0x6d, 0x6e, 0x64, 0x00, 0x00, 0x02, 0x54, 0x00, 0x00, 0x00, 0x70,
475 0x64, 0x6d, 0x64, 0x64, 0x00, 0x00, 0x02, 0xc4, 0x00, 0x00, 0x00, 0x88,
476 0x74, 0x65, 0x63, 0x68, 0x00, 0x00, 0x03, 0x4c, 0x00, 0x00, 0x00, 0x0c,
477 0x76, 0x75, 0x65, 0x64, 0x00, 0x00, 0x03, 0x58, 0x00, 0x00, 0x00, 0x67,
478 0x76, 0x69, 0x65, 0x77, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x24,
479 0x6c, 0x75, 0x6d, 0x69, 0x00, 0x00, 0x03, 0xe4, 0x00, 0x00, 0x00, 0x14,
480 0x6d, 0x65, 0x61, 0x73, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x24,
481 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x00, 0x00, 0x14,
482 0x62, 0x6b, 0x70, 0x74, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x14,
483 0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x44, 0x00, 0x00, 0x00, 0x14,
484 0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x58, 0x00, 0x00, 0x00, 0x14,
485 0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x6c, 0x00, 0x00, 0x00, 0x14,
486 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
487 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
488 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
489 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
490 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36,
491 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x28, 0x45, 0x71, 0x75, 0x69, 0x76,
492 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x77, 0x77,
493 0x2e, 0x73, 0x72, 0x67, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x31, 0x39,
494 0x39, 0x38, 0x20, 0x48, 0x50, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c,
495 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
496 0x00, 0x3f, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31,
497 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x28, 0x45, 0x71, 0x75,
498 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77,
499 0x77, 0x77, 0x2e, 0x73, 0x72, 0x67, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x20,
500 0x31, 0x39, 0x39, 0x38, 0x20, 0x48, 0x50, 0x20, 0x70, 0x72, 0x6f, 0x66,
501 0x69, 0x6c, 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
502 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x72, 0x65, 0x61,
503 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x47, 0x72, 0x61, 0x65, 0x6d,
504 0x65, 0x20, 0x57, 0x2e, 0x20, 0x47, 0x69, 0x6c, 0x6c, 0x2e, 0x20, 0x52,
505 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f,
506 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
507 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x20, 0x4e, 0x6f, 0x20, 0x57,
508 0x61, 0x72, 0x72, 0x61, 0x6e, 0x74, 0x79, 0x2c, 0x20, 0x55, 0x73, 0x65,
509 0x20, 0x61, 0x74, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x6f, 0x77, 0x6e,
510 0x20, 0x72, 0x69, 0x73, 0x6b, 0x2e, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63,
511 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20,
512 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69,
513 0x65, 0x63, 0x2e, 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
514 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20, 0x68, 0x74, 0x74,
515 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x65, 0x63, 0x2e,
516 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
517 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
518 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
519 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
520 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e,
521 0x49, 0x45, 0x43, 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e,
522 0x31, 0x20, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47,
523 0x42, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61,
524 0x63, 0x65, 0x20, 0x2d, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00,
525 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x49, 0x45, 0x43,
526 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x44,
527 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47, 0x42, 0x20, 0x63,
528 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20,
529 0x2d, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
530 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
531 0x00, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x20, 0x00, 0x00, 0x00, 0x00,
532 0x43, 0x52, 0x54, 0x20, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00,
533 0x00, 0x00, 0x00, 0x0d, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36,
534 0x2d, 0x32, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
535 0x00, 0x00, 0x00, 0x0d, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36,
536 0x2d, 0x32, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
537 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
538 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
539 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
540 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
541 0x76, 0x69, 0x65, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xa4, 0x7c,
542 0x00, 0x14, 0x5f, 0x30, 0x00, 0x10, 0xce, 0x02, 0x00, 0x03, 0xed, 0xb2,
543 0x00, 0x04, 0x13, 0x0a, 0x00, 0x03, 0x5c, 0x67, 0x00, 0x00, 0x00, 0x01,
544 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x0a, 0x3d,
545 0x00, 0x50, 0x00, 0x00, 0x00, 0x57, 0x1e, 0xb8, 0x6d, 0x65, 0x61, 0x73,
546 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
547 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
548 0x00, 0x00, 0x02, 0x8f, 0x00, 0x00, 0x00, 0x02, 0x58, 0x59, 0x5a, 0x20,
549 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0x51, 0x00, 0x01, 0x00, 0x00,
550 0x00, 0x01, 0x16, 0xcc, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
551 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
552 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xa0,
553 0x00, 0x00, 0x38, 0xf5, 0x00, 0x00, 0x03, 0x90, 0x58, 0x59, 0x5a, 0x20,
554 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x97, 0x00, 0x00, 0xb7, 0x87,
555 0x00, 0x00, 0x18, 0xd9, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
556 0x00, 0x00, 0x24, 0x9f, 0x00, 0x00, 0x0f, 0x84, 0x00, 0x00, 0xb6, 0xc4,
557 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
558 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x14, 0x00, 0x19,
559 0x00, 0x1e, 0x00, 0x23, 0x00, 0x28, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x37,
560 0x00, 0x3b, 0x00, 0x40, 0x00, 0x45, 0x00, 0x4a, 0x00, 0x4f, 0x00, 0x54,
561 0x00, 0x59, 0x00, 0x5e, 0x00, 0x63, 0x00, 0x68, 0x00, 0x6d, 0x00, 0x72,
562 0x00, 0x77, 0x00, 0x7c, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8b, 0x00, 0x90,
563 0x00, 0x95, 0x00, 0x9a, 0x00, 0x9f, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xae,
564 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc1, 0x00, 0xc6, 0x00, 0xcb,
565 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xdb, 0x00, 0xe0, 0x00, 0xe5, 0x00, 0xeb,
566 0x00, 0xf0, 0x00, 0xf6, 0x00, 0xfb, 0x01, 0x01, 0x01, 0x07, 0x01, 0x0d,
567 0x01, 0x13, 0x01, 0x19, 0x01, 0x1f, 0x01, 0x25, 0x01, 0x2b, 0x01, 0x32,
568 0x01, 0x38, 0x01, 0x3e, 0x01, 0x45, 0x01, 0x4c, 0x01, 0x52, 0x01, 0x59,
569 0x01, 0x60, 0x01, 0x67, 0x01, 0x6e, 0x01, 0x75, 0x01, 0x7c, 0x01, 0x83,
570 0x01, 0x8b, 0x01, 0x92, 0x01, 0x9a, 0x01, 0xa1, 0x01, 0xa9, 0x01, 0xb1,
571 0x01, 0xb9, 0x01, 0xc1, 0x01, 0xc9, 0x01, 0xd1, 0x01, 0xd9, 0x01, 0xe1,
572 0x01, 0xe9, 0x01, 0xf2, 0x01, 0xfa, 0x02, 0x03, 0x02, 0x0c, 0x02, 0x14,
573 0x02, 0x1d, 0x02, 0x26, 0x02, 0x2f, 0x02, 0x38, 0x02, 0x41, 0x02, 0x4b,
574 0x02, 0x54, 0x02, 0x5d, 0x02, 0x67, 0x02, 0x71, 0x02, 0x7a, 0x02, 0x84,
575 0x02, 0x8e, 0x02, 0x98, 0x02, 0xa2, 0x02, 0xac, 0x02, 0xb6, 0x02, 0xc1,
576 0x02, 0xcb, 0x02, 0xd5, 0x02, 0xe0, 0x02, 0xeb, 0x02, 0xf5, 0x03, 0x00,
577 0x03, 0x0b, 0x03, 0x16, 0x03, 0x21, 0x03, 0x2d, 0x03, 0x38, 0x03, 0x43,
578 0x03, 0x4f, 0x03, 0x5a, 0x03, 0x66, 0x03, 0x72, 0x03, 0x7e, 0x03, 0x8a,
579 0x03, 0x96, 0x03, 0xa2, 0x03, 0xae, 0x03, 0xba, 0x03, 0xc7, 0x03, 0xd3,
580 0x03, 0xe0, 0x03, 0xec, 0x03, 0xf9, 0x04, 0x06, 0x04, 0x13, 0x04, 0x20,
581 0x04, 0x2d, 0x04, 0x3b, 0x04, 0x48, 0x04, 0x55, 0x04, 0x63, 0x04, 0x71,
582 0x04, 0x7e, 0x04, 0x8c, 0x04, 0x9a, 0x04, 0xa8, 0x04, 0xb6, 0x04, 0xc4,
583 0x04, 0xd3, 0x04, 0xe1, 0x04, 0xf0, 0x04, 0xfe, 0x05, 0x0d, 0x05, 0x1c,
584 0x05, 0x2b, 0x05, 0x3a, 0x05, 0x49, 0x05, 0x58, 0x05, 0x67, 0x05, 0x77,
585 0x05, 0x86, 0x05, 0x96, 0x05, 0xa6, 0x05, 0xb5, 0x05, 0xc5, 0x05, 0xd5,
586 0x05, 0xe5, 0x05, 0xf6, 0x06, 0x06, 0x06, 0x16, 0x06, 0x27, 0x06, 0x37,
587 0x06, 0x48, 0x06, 0x59, 0x06, 0x6a, 0x06, 0x7b, 0x06, 0x8c, 0x06, 0x9d,
588 0x06, 0xaf, 0x06, 0xc0, 0x06, 0xd1, 0x06, 0xe3, 0x06, 0xf5, 0x07, 0x07,
589 0x07, 0x19, 0x07, 0x2b, 0x07, 0x3d, 0x07, 0x4f, 0x07, 0x61, 0x07, 0x74,
590 0x07, 0x86, 0x07, 0x99, 0x07, 0xac, 0x07, 0xbf, 0x07, 0xd2, 0x07, 0xe5,
591 0x07, 0xf8, 0x08, 0x0b, 0x08, 0x1f, 0x08, 0x32, 0x08, 0x46, 0x08, 0x5a,
592 0x08, 0x6e, 0x08, 0x82, 0x08, 0x96, 0x08, 0xaa, 0x08, 0xbe, 0x08, 0xd2,
593 0x08, 0xe7, 0x08, 0xfb, 0x09, 0x10, 0x09, 0x25, 0x09, 0x3a, 0x09, 0x4f,
594 0x09, 0x64, 0x09, 0x79, 0x09, 0x8f, 0x09, 0xa4, 0x09, 0xba, 0x09, 0xcf,
595 0x09, 0xe5, 0x09, 0xfb, 0x0a, 0x11, 0x0a, 0x27, 0x0a, 0x3d, 0x0a, 0x54,
596 0x0a, 0x6a, 0x0a, 0x81, 0x0a, 0x98, 0x0a, 0xae, 0x0a, 0xc5, 0x0a, 0xdc,
597 0x0a, 0xf3, 0x0b, 0x0b, 0x0b, 0x22, 0x0b, 0x39, 0x0b, 0x51, 0x0b, 0x69,
598 0x0b, 0x80, 0x0b, 0x98, 0x0b, 0xb0, 0x0b, 0xc8, 0x0b, 0xe1, 0x0b, 0xf9,
599 0x0c, 0x12, 0x0c, 0x2a, 0x0c, 0x43, 0x0c, 0x5c, 0x0c, 0x75, 0x0c, 0x8e,
600 0x0c, 0xa7, 0x0c, 0xc0, 0x0c, 0xd9, 0x0c, 0xf3, 0x0d, 0x0d, 0x0d, 0x26,
601 0x0d, 0x40, 0x0d, 0x5a, 0x0d, 0x74, 0x0d, 0x8e, 0x0d, 0xa9, 0x0d, 0xc3,
602 0x0d, 0xde, 0x0d, 0xf8, 0x0e, 0x13, 0x0e, 0x2e, 0x0e, 0x49, 0x0e, 0x64,
603 0x0e, 0x7f, 0x0e, 0x9b, 0x0e, 0xb6, 0x0e, 0xd2, 0x0e, 0xee, 0x0f, 0x09,
604 0x0f, 0x25, 0x0f, 0x41, 0x0f, 0x5e, 0x0f, 0x7a, 0x0f, 0x96, 0x0f, 0xb3,
605 0x0f, 0xcf, 0x0f, 0xec, 0x10, 0x09, 0x10, 0x26, 0x10, 0x43, 0x10, 0x61,
606 0x10, 0x7e, 0x10, 0x9b, 0x10, 0xb9, 0x10, 0xd7, 0x10, 0xf5, 0x11, 0x13,
607 0x11, 0x31, 0x11, 0x4f, 0x11, 0x6d, 0x11, 0x8c, 0x11, 0xaa, 0x11, 0xc9,
608 0x11, 0xe8, 0x12, 0x07, 0x12, 0x26, 0x12, 0x45, 0x12, 0x64, 0x12, 0x84,
609 0x12, 0xa3, 0x12, 0xc3, 0x12, 0xe3, 0x13, 0x03, 0x13, 0x23, 0x13, 0x43,
610 0x13, 0x63, 0x13, 0x83, 0x13, 0xa4, 0x13, 0xc5, 0x13, 0xe5, 0x14, 0x06,
611 0x14, 0x27, 0x14, 0x49, 0x14, 0x6a, 0x14, 0x8b, 0x14, 0xad, 0x14, 0xce,
612 0x14, 0xf0, 0x15, 0x12, 0x15, 0x34, 0x15, 0x56, 0x15, 0x78, 0x15, 0x9b,
613 0x15, 0xbd, 0x15, 0xe0, 0x16, 0x03, 0x16, 0x26, 0x16, 0x49, 0x16, 0x6c,
614 0x16, 0x8f, 0x16, 0xb2, 0x16, 0xd6, 0x16, 0xfa, 0x17, 0x1d, 0x17, 0x41,
615 0x17, 0x65, 0x17, 0x89, 0x17, 0xae, 0x17, 0xd2, 0x17, 0xf7, 0x18, 0x1b,
616 0x18, 0x40, 0x18, 0x65, 0x18, 0x8a, 0x18, 0xaf, 0x18, 0xd5, 0x18, 0xfa,
617 0x19, 0x20, 0x19, 0x45, 0x19, 0x6b, 0x19, 0x91, 0x19, 0xb7, 0x19, 0xdd,
618 0x1a, 0x04, 0x1a, 0x2a, 0x1a, 0x51, 0x1a, 0x77, 0x1a, 0x9e, 0x1a, 0xc5,
619 0x1a, 0xec, 0x1b, 0x14, 0x1b, 0x3b, 0x1b, 0x63, 0x1b, 0x8a, 0x1b, 0xb2,
620 0x1b, 0xda, 0x1c, 0x02, 0x1c, 0x2a, 0x1c, 0x52, 0x1c, 0x7b, 0x1c, 0xa3,
621 0x1c, 0xcc, 0x1c, 0xf5, 0x1d, 0x1e, 0x1d, 0x47, 0x1d, 0x70, 0x1d, 0x99,
622 0x1d, 0xc3, 0x1d, 0xec, 0x1e, 0x16, 0x1e, 0x40, 0x1e, 0x6a, 0x1e, 0x94,
623 0x1e, 0xbe, 0x1e, 0xe9, 0x1f, 0x13, 0x1f, 0x3e, 0x1f, 0x69, 0x1f, 0x94,
624 0x1f, 0xbf, 0x1f, 0xea, 0x20, 0x15, 0x20, 0x41, 0x20, 0x6c, 0x20, 0x98,
625 0x20, 0xc4, 0x20, 0xf0, 0x21, 0x1c, 0x21, 0x48, 0x21, 0x75, 0x21, 0xa1,
626 0x21, 0xce, 0x21, 0xfb, 0x22, 0x27, 0x22, 0x55, 0x22, 0x82, 0x22, 0xaf,
627 0x22, 0xdd, 0x23, 0x0a, 0x23, 0x38, 0x23, 0x66, 0x23, 0x94, 0x23, 0xc2,
628 0x23, 0xf0, 0x24, 0x1f, 0x24, 0x4d, 0x24, 0x7c, 0x24, 0xab, 0x24, 0xda,
629 0x25, 0x09, 0x25, 0x38, 0x25, 0x68, 0x25, 0x97, 0x25, 0xc7, 0x25, 0xf7,
630 0x26, 0x27, 0x26, 0x57, 0x26, 0x87, 0x26, 0xb7, 0x26, 0xe8, 0x27, 0x18,
631 0x27, 0x49, 0x27, 0x7a, 0x27, 0xab, 0x27, 0xdc, 0x28, 0x0d, 0x28, 0x3f,
632 0x28, 0x71, 0x28, 0xa2, 0x28, 0xd4, 0x29, 0x06, 0x29, 0x38, 0x29, 0x6b,
633 0x29, 0x9d, 0x29, 0xd0, 0x2a, 0x02, 0x2a, 0x35, 0x2a, 0x68, 0x2a, 0x9b,
634 0x2a, 0xcf, 0x2b, 0x02, 0x2b, 0x36, 0x2b, 0x69, 0x2b, 0x9d, 0x2b, 0xd1,
635 0x2c, 0x05, 0x2c, 0x39, 0x2c, 0x6e, 0x2c, 0xa2, 0x2c, 0xd7, 0x2d, 0x0c,
636 0x2d, 0x41, 0x2d, 0x76, 0x2d, 0xab, 0x2d, 0xe1, 0x2e, 0x16, 0x2e, 0x4c,
637 0x2e, 0x82, 0x2e, 0xb7, 0x2e, 0xee, 0x2f, 0x24, 0x2f, 0x5a, 0x2f, 0x91,
638 0x2f, 0xc7, 0x2f, 0xfe, 0x30, 0x35, 0x30, 0x6c, 0x30, 0xa4, 0x30, 0xdb,
639 0x31, 0x12, 0x31, 0x4a, 0x31, 0x82, 0x31, 0xba, 0x31, 0xf2, 0x32, 0x2a,
640 0x32, 0x63, 0x32, 0x9b, 0x32, 0xd4, 0x33, 0x0d, 0x33, 0x46, 0x33, 0x7f,
641 0x33, 0xb8, 0x33, 0xf1, 0x34, 0x2b, 0x34, 0x65, 0x34, 0x9e, 0x34, 0xd8,
642 0x35, 0x13, 0x35, 0x4d, 0x35, 0x87, 0x35, 0xc2, 0x35, 0xfd, 0x36, 0x37,
643 0x36, 0x72, 0x36, 0xae, 0x36, 0xe9, 0x37, 0x24, 0x37, 0x60, 0x37, 0x9c,
644 0x37, 0xd7, 0x38, 0x14, 0x38, 0x50, 0x38, 0x8c, 0x38, 0xc8, 0x39, 0x05,
645 0x39, 0x42, 0x39, 0x7f, 0x39, 0xbc, 0x39, 0xf9, 0x3a, 0x36, 0x3a, 0x74,
646 0x3a, 0xb2, 0x3a, 0xef, 0x3b, 0x2d, 0x3b, 0x6b, 0x3b, 0xaa, 0x3b, 0xe8,
647 0x3c, 0x27, 0x3c, 0x65, 0x3c, 0xa4, 0x3c, 0xe3, 0x3d, 0x22, 0x3d, 0x61,
648 0x3d, 0xa1, 0x3d, 0xe0, 0x3e, 0x20, 0x3e, 0x60, 0x3e, 0xa0, 0x3e, 0xe0,
649 0x3f, 0x21, 0x3f, 0x61, 0x3f, 0xa2, 0x3f, 0xe2, 0x40, 0x23, 0x40, 0x64,
650 0x40, 0xa6, 0x40, 0xe7, 0x41, 0x29, 0x41, 0x6a, 0x41, 0xac, 0x41, 0xee,
651 0x42, 0x30, 0x42, 0x72, 0x42, 0xb5, 0x42, 0xf7, 0x43, 0x3a, 0x43, 0x7d,
652 0x43, 0xc0, 0x44, 0x03, 0x44, 0x47, 0x44, 0x8a, 0x44, 0xce, 0x45, 0x12,
653 0x45, 0x55, 0x45, 0x9a, 0x45, 0xde, 0x46, 0x22, 0x46, 0x67, 0x46, 0xab,
654 0x46, 0xf0, 0x47, 0x35, 0x47, 0x7b, 0x47, 0xc0, 0x48, 0x05, 0x48, 0x4b,
655 0x48, 0x91, 0x48, 0xd7, 0x49, 0x1d, 0x49, 0x63, 0x49, 0xa9, 0x49, 0xf0,
656 0x4a, 0x37, 0x4a, 0x7d, 0x4a, 0xc4, 0x4b, 0x0c, 0x4b, 0x53, 0x4b, 0x9a,
657 0x4b, 0xe2, 0x4c, 0x2a, 0x4c, 0x72, 0x4c, 0xba, 0x4d, 0x02, 0x4d, 0x4a,
658 0x4d, 0x93, 0x4d, 0xdc, 0x4e, 0x25, 0x4e, 0x6e, 0x4e, 0xb7, 0x4f, 0x00,
659 0x4f, 0x49, 0x4f, 0x93, 0x4f, 0xdd, 0x50, 0x27, 0x50, 0x71, 0x50, 0xbb,
660 0x51, 0x06, 0x51, 0x50, 0x51, 0x9b, 0x51, 0xe6, 0x52, 0x31, 0x52, 0x7c,
661 0x52, 0xc7, 0x53, 0x13, 0x53, 0x5f, 0x53, 0xaa, 0x53, 0xf6, 0x54, 0x42,
662 0x54, 0x8f, 0x54, 0xdb, 0x55, 0x28, 0x55, 0x75, 0x55, 0xc2, 0x56, 0x0f,
663 0x56, 0x5c, 0x56, 0xa9, 0x56, 0xf7, 0x57, 0x44, 0x57, 0x92, 0x57, 0xe0,
664 0x58, 0x2f, 0x58, 0x7d, 0x58, 0xcb, 0x59, 0x1a, 0x59, 0x69, 0x59, 0xb8,
665 0x5a, 0x07, 0x5a, 0x56, 0x5a, 0xa6, 0x5a, 0xf5, 0x5b, 0x45, 0x5b, 0x95,
666 0x5b, 0xe5, 0x5c, 0x35, 0x5c, 0x86, 0x5c, 0xd6, 0x5d, 0x27, 0x5d, 0x78,
667 0x5d, 0xc9, 0x5e, 0x1a, 0x5e, 0x6c, 0x5e, 0xbd, 0x5f, 0x0f, 0x5f, 0x61,
668 0x5f, 0xb3, 0x60, 0x05, 0x60, 0x57, 0x60, 0xaa, 0x60, 0xfc, 0x61, 0x4f,
669 0x61, 0xa2, 0x61, 0xf5, 0x62, 0x49, 0x62, 0x9c, 0x62, 0xf0, 0x63, 0x43,
670 0x63, 0x97, 0x63, 0xeb, 0x64, 0x40, 0x64, 0x94, 0x64, 0xe9, 0x65, 0x3d,
671 0x65, 0x92, 0x65, 0xe7, 0x66, 0x3d, 0x66, 0x92, 0x66, 0xe8, 0x67, 0x3d,
672 0x67, 0x93, 0x67, 0xe9, 0x68, 0x3f, 0x68, 0x96, 0x68, 0xec, 0x69, 0x43,
673 0x69, 0x9a, 0x69, 0xf1, 0x6a, 0x48, 0x6a, 0x9f, 0x6a, 0xf7, 0x6b, 0x4f,
674 0x6b, 0xa7, 0x6b, 0xff, 0x6c, 0x57, 0x6c, 0xaf, 0x6d, 0x08, 0x6d, 0x60,
675 0x6d, 0xb9, 0x6e, 0x12, 0x6e, 0x6b, 0x6e, 0xc4, 0x6f, 0x1e, 0x6f, 0x78,
676 0x6f, 0xd1, 0x70, 0x2b, 0x70, 0x86, 0x70, 0xe0, 0x71, 0x3a, 0x71, 0x95,
677 0x71, 0xf0, 0x72, 0x4b, 0x72, 0xa6, 0x73, 0x01, 0x73, 0x5d, 0x73, 0xb8,
678 0x74, 0x14, 0x74, 0x70, 0x74, 0xcc, 0x75, 0x28, 0x75, 0x85, 0x75, 0xe1,
679 0x76, 0x3e, 0x76, 0x9b, 0x76, 0xf8, 0x77, 0x56, 0x77, 0xb3, 0x78, 0x11,
680 0x78, 0x6e, 0x78, 0xcc, 0x79, 0x2a, 0x79, 0x89, 0x79, 0xe7, 0x7a, 0x46,
681 0x7a, 0xa5, 0x7b, 0x04, 0x7b, 0x63, 0x7b, 0xc2, 0x7c, 0x21, 0x7c, 0x81,
682 0x7c, 0xe1, 0x7d, 0x41, 0x7d, 0xa1, 0x7e, 0x01, 0x7e, 0x62, 0x7e, 0xc2,
683 0x7f, 0x23, 0x7f, 0x84, 0x7f, 0xe5, 0x80, 0x47, 0x80, 0xa8, 0x81, 0x0a,
684 0x81, 0x6b, 0x81, 0xcd, 0x82, 0x30, 0x82, 0x92, 0x82, 0xf4, 0x83, 0x57,
685 0x83, 0xba, 0x84, 0x1d, 0x84, 0x80, 0x84, 0xe3, 0x85, 0x47, 0x85, 0xab,
686 0x86, 0x0e, 0x86, 0x72, 0x86, 0xd7, 0x87, 0x3b, 0x87, 0x9f, 0x88, 0x04,
687 0x88, 0x69, 0x88, 0xce, 0x89, 0x33, 0x89, 0x99, 0x89, 0xfe, 0x8a, 0x64,
688 0x8a, 0xca, 0x8b, 0x30, 0x8b, 0x96, 0x8b, 0xfc, 0x8c, 0x63, 0x8c, 0xca,
689 0x8d, 0x31, 0x8d, 0x98, 0x8d, 0xff, 0x8e, 0x66, 0x8e, 0xce, 0x8f, 0x36,
690 0x8f, 0x9e, 0x90, 0x06, 0x90, 0x6e, 0x90, 0xd6, 0x91, 0x3f, 0x91, 0xa8,
691 0x92, 0x11, 0x92, 0x7a, 0x92, 0xe3, 0x93, 0x4d, 0x93, 0xb6, 0x94, 0x20,
692 0x94, 0x8a, 0x94, 0xf4, 0x95, 0x5f, 0x95, 0xc9, 0x96, 0x34, 0x96, 0x9f,
693 0x97, 0x0a, 0x97, 0x75, 0x97, 0xe0, 0x98, 0x4c, 0x98, 0xb8, 0x99, 0x24,
694 0x99, 0x90, 0x99, 0xfc, 0x9a, 0x68, 0x9a, 0xd5, 0x9b, 0x42, 0x9b, 0xaf,
695 0x9c, 0x1c, 0x9c, 0x89, 0x9c, 0xf7, 0x9d, 0x64, 0x9d, 0xd2, 0x9e, 0x40,
696 0x9e, 0xae, 0x9f, 0x1d, 0x9f, 0x8b, 0x9f, 0xfa, 0xa0, 0x69, 0xa0, 0xd8,
697 0xa1, 0x47, 0xa1, 0xb6, 0xa2, 0x26, 0xa2, 0x96, 0xa3, 0x06, 0xa3, 0x76,
698 0xa3, 0xe6, 0xa4, 0x56, 0xa4, 0xc7, 0xa5, 0x38, 0xa5, 0xa9, 0xa6, 0x1a,
699 0xa6, 0x8b, 0xa6, 0xfd, 0xa7, 0x6e, 0xa7, 0xe0, 0xa8, 0x52, 0xa8, 0xc4,
700 0xa9, 0x37, 0xa9, 0xa9, 0xaa, 0x1c, 0xaa, 0x8f, 0xab, 0x02, 0xab, 0x75,
701 0xab, 0xe9, 0xac, 0x5c, 0xac, 0xd0, 0xad, 0x44, 0xad, 0xb8, 0xae, 0x2d,
702 0xae, 0xa1, 0xaf, 0x16, 0xaf, 0x8b, 0xb0, 0x00, 0xb0, 0x75, 0xb0, 0xea,
703 0xb1, 0x60, 0xb1, 0xd6, 0xb2, 0x4b, 0xb2, 0xc2, 0xb3, 0x38, 0xb3, 0xae,
704 0xb4, 0x25, 0xb4, 0x9c, 0xb5, 0x13, 0xb5, 0x8a, 0xb6, 0x01, 0xb6, 0x79,
705 0xb6, 0xf0, 0xb7, 0x68, 0xb7, 0xe0, 0xb8, 0x59, 0xb8, 0xd1, 0xb9, 0x4a,
706 0xb9, 0xc2, 0xba, 0x3b, 0xba, 0xb5, 0xbb, 0x2e, 0xbb, 0xa7, 0xbc, 0x21,
707 0xbc, 0x9b, 0xbd, 0x15, 0xbd, 0x8f, 0xbe, 0x0a, 0xbe, 0x84, 0xbe, 0xff,
708 0xbf, 0x7a, 0xbf, 0xf5, 0xc0, 0x70, 0xc0, 0xec, 0xc1, 0x67, 0xc1, 0xe3,
709 0xc2, 0x5f, 0xc2, 0xdb, 0xc3, 0x58, 0xc3, 0xd4, 0xc4, 0x51, 0xc4, 0xce,
710 0xc5, 0x4b, 0xc5, 0xc8, 0xc6, 0x46, 0xc6, 0xc3, 0xc7, 0x41, 0xc7, 0xbf,
711 0xc8, 0x3d, 0xc8, 0xbc, 0xc9, 0x3a, 0xc9, 0xb9, 0xca, 0x38, 0xca, 0xb7,
712 0xcb, 0x36, 0xcb, 0xb6, 0xcc, 0x35, 0xcc, 0xb5, 0xcd, 0x35, 0xcd, 0xb5,
713 0xce, 0x36, 0xce, 0xb6, 0xcf, 0x37, 0xcf, 0xb8, 0xd0, 0x39, 0xd0, 0xba,
714 0xd1, 0x3c, 0xd1, 0xbe, 0xd2, 0x3f, 0xd2, 0xc1, 0xd3, 0x44, 0xd3, 0xc6,
715 0xd4, 0x49, 0xd4, 0xcb, 0xd5, 0x4e, 0xd5, 0xd1, 0xd6, 0x55, 0xd6, 0xd8,
716 0xd7, 0x5c, 0xd7, 0xe0, 0xd8, 0x64, 0xd8, 0xe8, 0xd9, 0x6c, 0xd9, 0xf1,
717 0xda, 0x76, 0xda, 0xfb, 0xdb, 0x80, 0xdc, 0x05, 0xdc, 0x8a, 0xdd, 0x10,
718 0xdd, 0x96, 0xde, 0x1c, 0xde, 0xa2, 0xdf, 0x29, 0xdf, 0xaf, 0xe0, 0x36,
719 0xe0, 0xbd, 0xe1, 0x44, 0xe1, 0xcc, 0xe2, 0x53, 0xe2, 0xdb, 0xe3, 0x63,
720 0xe3, 0xeb, 0xe4, 0x73, 0xe4, 0xfc, 0xe5, 0x84, 0xe6, 0x0d, 0xe6, 0x96,
721 0xe7, 0x1f, 0xe7, 0xa9, 0xe8, 0x32, 0xe8, 0xbc, 0xe9, 0x46, 0xe9, 0xd0,
722 0xea, 0x5b, 0xea, 0xe5, 0xeb, 0x70, 0xeb, 0xfb, 0xec, 0x86, 0xed, 0x11,
723 0xed, 0x9c, 0xee, 0x28, 0xee, 0xb4, 0xef, 0x40, 0xef, 0xcc, 0xf0, 0x58,
724 0xf0, 0xe5, 0xf1, 0x72, 0xf1, 0xff, 0xf2, 0x8c, 0xf3, 0x19, 0xf3, 0xa7,
725 0xf4, 0x34, 0xf4, 0xc2, 0xf5, 0x50, 0xf5, 0xde, 0xf6, 0x6d, 0xf6, 0xfb,
726 0xf7, 0x8a, 0xf8, 0x19, 0xf8, 0xa8, 0xf9, 0x38, 0xf9, 0xc7, 0xfa, 0x57,
727 0xfa, 0xe7, 0xfb, 0x77, 0xfc, 0x07, 0xfc, 0x98, 0xfd, 0x29, 0xfd, 0xba,
728 0xfe, 0x4b, 0xfe, 0xdc, 0xff, 0x6d, 0xff, 0xff
729 };
730
731 StringInfo
732 *profile;
733
734 MagickBooleanType
735 status;
736
737 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000738 assert(image->signature == MagickCoreSignature);
dirkc8c6dd52015-01-23 23:41:51 +0000739 if (GetImageProfile(image,"icc") != (const StringInfo *) NULL)
cristy35e92062014-08-26 14:13:16 +0000740 return(MagickFalse);
741 profile=AcquireStringInfo(sizeof(sRGBProfile));
742 SetStringInfoDatum(profile,sRGBProfile);
dirkc8c6dd52015-01-23 23:41:51 +0000743 status=SetImageProfile(image,"icc",profile,exception);
cristy35e92062014-08-26 14:13:16 +0000744 profile=DestroyStringInfo(profile);
745 return(status);
746}
747
cristy3ed852e2009-09-05 21:47:34 +0000748MagickExport MagickBooleanType ProfileImage(Image *image,const char *name,
cristy092d71c2011-10-14 18:01:29 +0000749 const void *datum,const size_t length,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000750{
751#define ProfileImageTag "Profile/Image"
752#define ThrowProfileException(severity,tag,context) \
753{ \
cristye1c67a82010-04-10 02:20:57 +0000754 if (source_profile != (cmsHPROFILE) NULL) \
755 (void) cmsCloseProfile(source_profile); \
cristya9eb12b2010-04-08 01:19:04 +0000756 if (target_profile != (cmsHPROFILE) NULL) \
757 (void) cmsCloseProfile(target_profile); \
cristy3ed852e2009-09-05 21:47:34 +0000758 ThrowBinaryException(severity,tag,context); \
759}
760
761 MagickBooleanType
762 status;
763
764 StringInfo
765 *profile;
766
767 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000768 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000769 if (image->debug != MagickFalse)
770 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
771 assert(name != (const char *) NULL);
772 if ((datum == (const void *) NULL) || (length == 0))
773 {
774 char
cristy53b6cc32014-06-25 13:13:33 +0000775 *next;
cristy3ed852e2009-09-05 21:47:34 +0000776
777 /*
778 Delete image profile(s).
779 */
cristy3ed852e2009-09-05 21:47:34 +0000780 ResetImageProfileIterator(image);
cristy53b6cc32014-06-25 13:13:33 +0000781 for (next=GetNextImageProfile(image); next != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +0000782 {
cristy53b6cc32014-06-25 13:13:33 +0000783 if (IsOptionMember(next,name) != MagickFalse)
784 {
dirk15518fa2014-08-22 16:52:59 +0000785 (void) DeleteImageProfile(image,next);
cristy53b6cc32014-06-25 13:13:33 +0000786 ResetImageProfileIterator(image);
787 }
cristy948fbb62014-07-01 22:11:27 +0000788 next=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +0000789 }
cristy3ed852e2009-09-05 21:47:34 +0000790 return(MagickTrue);
791 }
792 /*
793 Add a ICC, IPTC, or generic profile to the image.
794 */
cristy902d15b2010-04-07 19:44:01 +0000795 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +0000796 profile=AcquireStringInfo((size_t) length);
797 SetStringInfoDatum(profile,(unsigned char *) datum);
cristy902d15b2010-04-07 19:44:01 +0000798 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
cristyd15e6592011-10-15 00:13:06 +0000799 status=SetImageProfile(image,name,profile,exception);
cristy902d15b2010-04-07 19:44:01 +0000800 else
cristy3ed852e2009-09-05 21:47:34 +0000801 {
802 const StringInfo
803 *icc_profile;
804
805 icc_profile=GetImageProfile(image,"icc");
806 if ((icc_profile != (const StringInfo *) NULL) &&
807 (CompareStringInfo(icc_profile,profile) == 0))
808 {
809 const char
810 *value;
811
cristyd15e6592011-10-15 00:13:06 +0000812 value=GetImageProperty(image,"exif:ColorSpace",exception);
cristyd479bca2012-06-04 14:26:56 +0000813 (void) value;
cristy3ed852e2009-09-05 21:47:34 +0000814 if (LocaleCompare(value,"1") != 0)
cristyd15e6592011-10-15 00:13:06 +0000815 (void) SetsRGBImageProfile(image,exception);
816 value=GetImageProperty(image,"exif:InteroperabilityIndex",exception);
cristy3ed852e2009-09-05 21:47:34 +0000817 if (LocaleCompare(value,"R98.") != 0)
cristyd15e6592011-10-15 00:13:06 +0000818 (void) SetsRGBImageProfile(image,exception);
cristy35e92062014-08-26 14:13:16 +0000819 /* Future.
cristyd15e6592011-10-15 00:13:06 +0000820 value=GetImageProperty(image,"exif:InteroperabilityIndex",exception);
cristy3ed852e2009-09-05 21:47:34 +0000821 if (LocaleCompare(value,"R03.") != 0)
cristyd15e6592011-10-15 00:13:06 +0000822 (void) SetAdobeRGB1998ImageProfile(image,exception);
cristy751980d2012-06-03 23:18:35 +0000823 */
cristy3ed852e2009-09-05 21:47:34 +0000824 icc_profile=GetImageProfile(image,"icc");
825 }
826 if ((icc_profile != (const StringInfo *) NULL) &&
827 (CompareStringInfo(icc_profile,profile) == 0))
828 {
829 profile=DestroyStringInfo(profile);
830 return(MagickTrue);
831 }
832#if !defined(MAGICKCORE_LCMS_DELEGATE)
cristy09b22022011-10-26 14:11:30 +0000833 (void) ThrowMagickException(exception,GetMagickModule(),
cristye2c81ad2011-08-20 22:54:14 +0000834 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
anthonye5b39652012-04-21 05:37:29 +0000835 "'%s' (LCMS)",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000836#else
cristy8a20a2d2010-04-08 14:16:27 +0000837 {
cristy8a20a2d2010-04-08 14:16:27 +0000838 cmsHPROFILE
cristye1c67a82010-04-10 02:20:57 +0000839 source_profile;
cristy3ed852e2009-09-05 21:47:34 +0000840
cristyd0b7ee52011-10-21 11:19:02 +0000841 CMSExceptionInfo
842 cms_exception;
843
cristy8a20a2d2010-04-08 14:16:27 +0000844 /*
845 Transform pixel colors as defined by the color profiles.
846 */
cristyd0b7ee52011-10-21 11:19:02 +0000847 cmsSetLogErrorHandler(CMSExceptionHandler);
848 cms_exception.image=image;
849 cms_exception.exception=exception;
cristy5f95f4f2011-10-23 01:01:01 +0000850 (void) cms_exception;
cristy9a00c142014-04-05 13:38:34 +0000851 source_profile=cmsOpenProfileFromMemTHR((cmsContext) &cms_exception,
cristy71203402010-06-18 13:12:03 +0000852 GetStringInfoDatum(profile),(cmsUInt32Number)
853 GetStringInfoLength(profile));
cristy8a20a2d2010-04-08 14:16:27 +0000854 if (source_profile == (cmsHPROFILE) NULL)
855 ThrowBinaryException(ResourceLimitError,
856 "ColorspaceColorProfileMismatch",name);
cristyfbaaf3e2010-04-08 14:53:43 +0000857 if ((cmsGetDeviceClass(source_profile) != cmsSigLinkClass) &&
cristye1c67a82010-04-10 02:20:57 +0000858 (icc_profile == (StringInfo *) NULL))
cristyd15e6592011-10-15 00:13:06 +0000859 status=SetImageProfile(image,name,profile,exception);
cristye1c67a82010-04-10 02:20:57 +0000860 else
cristy3ed852e2009-09-05 21:47:34 +0000861 {
cristye1c67a82010-04-10 02:20:57 +0000862 CacheView
863 *image_view;
cristy8a20a2d2010-04-08 14:16:27 +0000864
cristye1c67a82010-04-10 02:20:57 +0000865 ColorspaceType
866 source_colorspace,
867 target_colorspace;
cristy8a20a2d2010-04-08 14:16:27 +0000868
cristye1c67a82010-04-10 02:20:57 +0000869 cmsColorSpaceSignature
870 signature;
cristy8a20a2d2010-04-08 14:16:27 +0000871
cristye1c67a82010-04-10 02:20:57 +0000872 cmsHPROFILE
873 target_profile;
cristy8a20a2d2010-04-08 14:16:27 +0000874
cristye1c67a82010-04-10 02:20:57 +0000875 cmsHTRANSFORM
876 *restrict transform;
cristy8a20a2d2010-04-08 14:16:27 +0000877
cristye1c67a82010-04-10 02:20:57 +0000878 cmsUInt32Number
879 flags,
880 source_type,
881 target_type;
882
cristye1c67a82010-04-10 02:20:57 +0000883 int
884 intent;
885
cristye1c67a82010-04-10 02:20:57 +0000886 MagickBooleanType
887 status;
888
cristy5f959472010-05-27 22:19:46 +0000889 MagickOffsetType
890 progress;
891
cristye1c67a82010-04-10 02:20:57 +0000892 size_t
cristye1c67a82010-04-10 02:20:57 +0000893 source_channels,
894 target_channels;
895
cristy5f959472010-05-27 22:19:46 +0000896 ssize_t
897 y;
898
cristye1c67a82010-04-10 02:20:57 +0000899 unsigned short
900 **restrict source_pixels,
901 **restrict target_pixels;
902
cristye1c67a82010-04-10 02:20:57 +0000903 target_profile=(cmsHPROFILE) NULL;
904 if (icc_profile != (StringInfo *) NULL)
905 {
906 target_profile=source_profile;
cristy9a00c142014-04-05 13:38:34 +0000907 source_profile=cmsOpenProfileFromMemTHR((cmsContext)
908 &cms_exception,GetStringInfoDatum(icc_profile),
909 (cmsUInt32Number) GetStringInfoLength(icc_profile));
cristye1c67a82010-04-10 02:20:57 +0000910 if (source_profile == (cmsHPROFILE) NULL)
911 ThrowProfileException(ResourceLimitError,
912 "ColorspaceColorProfileMismatch",name);
913 }
914 switch (cmsGetColorSpace(source_profile))
cristy8a20a2d2010-04-08 14:16:27 +0000915 {
cristye1c67a82010-04-10 02:20:57 +0000916 case cmsSigCmykData:
917 {
918 source_colorspace=CMYKColorspace;
919 source_type=(cmsUInt32Number) TYPE_CMYK_16;
920 source_channels=4;
921 break;
922 }
923 case cmsSigGrayData:
924 {
925 source_colorspace=GRAYColorspace;
926 source_type=(cmsUInt32Number) TYPE_GRAY_16;
927 source_channels=1;
928 break;
929 }
930 case cmsSigLabData:
931 {
932 source_colorspace=LabColorspace;
933 source_type=(cmsUInt32Number) TYPE_Lab_16;
934 source_channels=3;
935 break;
936 }
937 case cmsSigLuvData:
938 {
939 source_colorspace=YUVColorspace;
940 source_type=(cmsUInt32Number) TYPE_YUV_16;
941 source_channels=3;
942 break;
943 }
944 case cmsSigRgbData:
945 {
cristyc511e882012-04-16 21:11:14 +0000946 source_colorspace=sRGBColorspace;
cristye1c67a82010-04-10 02:20:57 +0000947 source_type=(cmsUInt32Number) TYPE_RGB_16;
948 source_channels=3;
949 break;
950 }
951 case cmsSigXYZData:
952 {
953 source_colorspace=XYZColorspace;
954 source_type=(cmsUInt32Number) TYPE_XYZ_16;
955 source_channels=3;
956 break;
957 }
958 case cmsSigYCbCrData:
959 {
960 source_colorspace=YCbCrColorspace;
961 source_type=(cmsUInt32Number) TYPE_YCbCr_16;
962 source_channels=3;
963 break;
964 }
965 default:
966 {
967 source_colorspace=UndefinedColorspace;
968 source_type=(cmsUInt32Number) TYPE_RGB_16;
969 source_channels=3;
970 break;
971 }
cristy8a20a2d2010-04-08 14:16:27 +0000972 }
cristye1c67a82010-04-10 02:20:57 +0000973 signature=cmsGetPCS(source_profile);
974 if (target_profile != (cmsHPROFILE) NULL)
975 signature=cmsGetColorSpace(target_profile);
976 switch (signature)
977 {
978 case cmsSigCmykData:
cristy8a20a2d2010-04-08 14:16:27 +0000979 {
cristye1c67a82010-04-10 02:20:57 +0000980 target_colorspace=CMYKColorspace;
981 target_type=(cmsUInt32Number) TYPE_CMYK_16;
982 target_channels=4;
983 break;
cristy8a20a2d2010-04-08 14:16:27 +0000984 }
cristye1c67a82010-04-10 02:20:57 +0000985 case cmsSigLabData:
cristy8a20a2d2010-04-08 14:16:27 +0000986 {
cristye1c67a82010-04-10 02:20:57 +0000987 target_colorspace=LabColorspace;
988 target_type=(cmsUInt32Number) TYPE_Lab_16;
989 target_channels=3;
990 break;
cristy8a20a2d2010-04-08 14:16:27 +0000991 }
cristye1c67a82010-04-10 02:20:57 +0000992 case cmsSigGrayData:
cristy8a20a2d2010-04-08 14:16:27 +0000993 {
cristye1c67a82010-04-10 02:20:57 +0000994 target_colorspace=GRAYColorspace;
995 target_type=(cmsUInt32Number) TYPE_GRAY_16;
996 target_channels=1;
997 break;
cristy8a20a2d2010-04-08 14:16:27 +0000998 }
cristye1c67a82010-04-10 02:20:57 +0000999 case cmsSigLuvData:
1000 {
1001 target_colorspace=YUVColorspace;
1002 target_type=(cmsUInt32Number) TYPE_YUV_16;
1003 target_channels=3;
1004 break;
1005 }
1006 case cmsSigRgbData:
1007 {
cristyc511e882012-04-16 21:11:14 +00001008 target_colorspace=sRGBColorspace;
cristye1c67a82010-04-10 02:20:57 +00001009 target_type=(cmsUInt32Number) TYPE_RGB_16;
1010 target_channels=3;
1011 break;
1012 }
1013 case cmsSigXYZData:
1014 {
1015 target_colorspace=XYZColorspace;
1016 target_type=(cmsUInt32Number) TYPE_XYZ_16;
1017 target_channels=3;
1018 break;
1019 }
1020 case cmsSigYCbCrData:
1021 {
1022 target_colorspace=YCbCrColorspace;
1023 target_type=(cmsUInt32Number) TYPE_YCbCr_16;
1024 target_channels=3;
1025 break;
1026 }
1027 default:
1028 {
1029 target_colorspace=UndefinedColorspace;
1030 target_type=(cmsUInt32Number) TYPE_RGB_16;
1031 target_channels=3;
1032 break;
1033 }
1034 }
1035 if ((source_colorspace == UndefinedColorspace) ||
1036 (target_colorspace == UndefinedColorspace))
1037 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
1038 name);
1039 if ((source_colorspace == GRAYColorspace) &&
dirkf1d85482015-04-06 00:36:00 +00001040 (SetImageGray(image,exception) == MagickFalse))
cristye1c67a82010-04-10 02:20:57 +00001041 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
1042 name);
1043 if ((source_colorspace == CMYKColorspace) &&
1044 (image->colorspace != CMYKColorspace))
1045 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
1046 name);
1047 if ((source_colorspace == XYZColorspace) &&
1048 (image->colorspace != XYZColorspace))
1049 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
1050 name);
1051 if ((source_colorspace == YCbCrColorspace) &&
1052 (image->colorspace != YCbCrColorspace))
1053 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
1054 name);
1055 if ((source_colorspace != CMYKColorspace) &&
cristye1c67a82010-04-10 02:20:57 +00001056 (source_colorspace != LabColorspace) &&
1057 (source_colorspace != XYZColorspace) &&
1058 (source_colorspace != YCbCrColorspace) &&
cristy3d9f5ba2012-06-26 13:37:31 +00001059 (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse))
cristye1c67a82010-04-10 02:20:57 +00001060 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
1061 name);
1062 switch (image->rendering_intent)
1063 {
1064 case AbsoluteIntent: intent=INTENT_ABSOLUTE_COLORIMETRIC; break;
1065 case PerceptualIntent: intent=INTENT_PERCEPTUAL; break;
1066 case RelativeIntent: intent=INTENT_RELATIVE_COLORIMETRIC; break;
1067 case SaturationIntent: intent=INTENT_SATURATION; break;
cristye540eb42012-04-16 12:50:34 +00001068 default: intent=INTENT_PERCEPTUAL; break;
cristye1c67a82010-04-10 02:20:57 +00001069 }
1070 flags=cmsFLAGS_HIGHRESPRECALC;
1071#if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
1072 if (image->black_point_compensation != MagickFalse)
1073 flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
1074#endif
cristy7632f502010-06-18 13:26:31 +00001075 transform=AcquireTransformThreadSet(image,source_profile,
1076 source_type,target_profile,target_type,intent,flags);
cristye1c67a82010-04-10 02:20:57 +00001077 if (transform == (cmsHTRANSFORM *) NULL)
1078 ThrowProfileException(ImageError,"UnableToCreateColorTransform",
1079 name);
1080 /*
1081 Transform image as dictated by the source & target image profiles.
1082 */
cristye1c67a82010-04-10 02:20:57 +00001083 source_pixels=AcquirePixelThreadSet(image->columns,source_channels);
1084 target_pixels=AcquirePixelThreadSet(image->columns,target_channels);
1085 if ((source_pixels == (unsigned short **) NULL) ||
1086 (target_pixels == (unsigned short **) NULL))
1087 {
1088 transform=DestroyTransformThreadSet(transform);
1089 ThrowProfileException(ResourceLimitError,
1090 "MemoryAllocationFailed",image->filename);
1091 }
cristy574cc262011-08-05 01:23:58 +00001092 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristye1c67a82010-04-10 02:20:57 +00001093 {
1094 target_pixels=DestroyPixelThreadSet(target_pixels);
1095 source_pixels=DestroyPixelThreadSet(source_pixels);
1096 transform=DestroyTransformThreadSet(transform);
1097 if (source_profile != (cmsHPROFILE) NULL)
1098 (void) cmsCloseProfile(source_profile);
1099 if (target_profile != (cmsHPROFILE) NULL)
1100 (void) cmsCloseProfile(target_profile);
1101 return(MagickFalse);
1102 }
1103 if (target_colorspace == CMYKColorspace)
cristy63240882011-08-05 19:05:27 +00001104 (void) SetImageColorspace(image,target_colorspace,exception);
cristye1c67a82010-04-10 02:20:57 +00001105 status=MagickTrue;
1106 progress=0;
cristy46ff2672012-12-14 15:32:26 +00001107 image_view=AcquireAuthenticCacheView(image,exception);
cristye1c67a82010-04-10 02:20:57 +00001108#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001109 #pragma omp parallel for schedule(static,4) shared(status) \
cristy5e6b2592012-12-19 14:08:11 +00001110 magick_threads(image,image,image->rows,1)
cristye1c67a82010-04-10 02:20:57 +00001111#endif
cristybb503372010-05-27 20:51:26 +00001112 for (y=0; y < (ssize_t) image->rows; y++)
cristy8a20a2d2010-04-08 14:16:27 +00001113 {
cristy5c9e6f22010-09-17 17:31:01 +00001114 const int
1115 id = GetOpenMPThreadId();
cristyad740052010-07-03 01:38:03 +00001116
cristy8a20a2d2010-04-08 14:16:27 +00001117 MagickBooleanType
cristye1c67a82010-04-10 02:20:57 +00001118 sync;
1119
cristybb503372010-05-27 20:51:26 +00001120 register ssize_t
cristye1c67a82010-04-10 02:20:57 +00001121 x;
1122
cristy4c08aed2011-07-01 19:47:50 +00001123 register Quantum
cristye1c67a82010-04-10 02:20:57 +00001124 *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);
cristyacd2ed22011-08-30 01:44:23 +00001133 if (q == (Quantum *) NULL)
cristye1c67a82010-04-10 02:20:57 +00001134 {
1135 status=MagickFalse;
1136 continue;
1137 }
cristye1c67a82010-04-10 02:20:57 +00001138 p=source_pixels[id];
cristybb503372010-05-27 20:51:26 +00001139 for (x=0; x < (ssize_t) image->columns; x++)
cristye1c67a82010-04-10 02:20:57 +00001140 {
cristy4c08aed2011-07-01 19:47:50 +00001141 *p++=ScaleQuantumToShort(GetPixelRed(image,q));
cristye1c67a82010-04-10 02:20:57 +00001142 if (source_channels > 1)
1143 {
cristy4c08aed2011-07-01 19:47:50 +00001144 *p++=ScaleQuantumToShort(GetPixelGreen(image,q));
1145 *p++=ScaleQuantumToShort(GetPixelBlue(image,q));
cristye1c67a82010-04-10 02:20:57 +00001146 }
1147 if (source_channels > 3)
cristy4c08aed2011-07-01 19:47:50 +00001148 *p++=ScaleQuantumToShort(GetPixelBlack(image,q));
cristyed231572011-07-14 02:18:59 +00001149 q+=GetPixelChannels(image);
cristye1c67a82010-04-10 02:20:57 +00001150 }
1151 cmsDoTransform(transform[id],source_pixels[id],target_pixels[id],
1152 (unsigned int) image->columns);
1153 p=target_pixels[id];
cristy4f7c4342014-12-07 15:55:04 +00001154 q-=GetPixelChannels(image)*image->columns;
cristybb503372010-05-27 20:51:26 +00001155 for (x=0; x < (ssize_t) image->columns; x++)
cristye1c67a82010-04-10 02:20:57 +00001156 {
cristyaf6eb932012-01-01 01:22:59 +00001157 if (target_channels == 1)
1158 SetPixelGray(image,ScaleShortToQuantum(*p),q);
1159 else
1160 SetPixelRed(image,ScaleShortToQuantum(*p),q);
cristye1c67a82010-04-10 02:20:57 +00001161 p++;
1162 if (target_channels > 1)
1163 {
cristy4c08aed2011-07-01 19:47:50 +00001164 SetPixelGreen(image,ScaleShortToQuantum(*p),q);
cristye1c67a82010-04-10 02:20:57 +00001165 p++;
cristy4c08aed2011-07-01 19:47:50 +00001166 SetPixelBlue(image,ScaleShortToQuantum(*p),q);
cristye1c67a82010-04-10 02:20:57 +00001167 p++;
1168 }
1169 if (target_channels > 3)
1170 {
cristy4c08aed2011-07-01 19:47:50 +00001171 SetPixelBlack(image,ScaleShortToQuantum(*p),q);
cristye1c67a82010-04-10 02:20:57 +00001172 p++;
1173 }
cristyed231572011-07-14 02:18:59 +00001174 q+=GetPixelChannels(image);
cristye1c67a82010-04-10 02:20:57 +00001175 }
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;
cristy8a20a2d2010-04-08 14:16:27 +00001183
1184#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001185 #pragma omp critical (MagickCore_ProfileImage)
cristy8a20a2d2010-04-08 14:16:27 +00001186#endif
cristye1c67a82010-04-10 02:20:57 +00001187 proceed=SetImageProgress(image,ProfileImageTag,progress++,
1188 image->rows);
1189 if (proceed == MagickFalse)
1190 status=MagickFalse;
1191 }
cristy8a20a2d2010-04-08 14:16:27 +00001192 }
cristye1c67a82010-04-10 02:20:57 +00001193 image_view=DestroyCacheView(image_view);
cristy63240882011-08-05 19:05:27 +00001194 (void) SetImageColorspace(image,target_colorspace,exception);
cristye1c67a82010-04-10 02:20:57 +00001195 switch (signature)
1196 {
1197 case cmsSigRgbData:
1198 {
cristy17f11b02014-12-20 19:37:04 +00001199 image->type=image->alpha_trait == UndefinedPixelTrait ?
cristydef23e52015-01-22 11:52:01 +00001200 TrueColorType : TrueColorAlphaType;
cristye1c67a82010-04-10 02:20:57 +00001201 break;
1202 }
1203 case cmsSigCmykData:
1204 {
cristy17f11b02014-12-20 19:37:04 +00001205 image->type=image->alpha_trait == UndefinedPixelTrait ?
cristydef23e52015-01-22 11:52:01 +00001206 ColorSeparationType : ColorSeparationAlphaType;
cristye1c67a82010-04-10 02:20:57 +00001207 break;
1208 }
1209 case cmsSigGrayData:
1210 {
cristy17f11b02014-12-20 19:37:04 +00001211 image->type=image->alpha_trait == UndefinedPixelTrait ?
cristydef23e52015-01-22 11:52:01 +00001212 GrayscaleType : GrayscaleAlphaType;
cristye1c67a82010-04-10 02:20:57 +00001213 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 (cmsGetDeviceClass(source_profile) != cmsSigLinkClass)
cristyd15e6592011-10-15 00:13:06 +00001222 status=SetImageProfile(image,name,profile,exception);
cristye1c67a82010-04-10 02:20:57 +00001223 if (target_profile != (cmsHPROFILE) NULL)
1224 (void) cmsCloseProfile(target_profile);
cristy8a20a2d2010-04-08 14:16:27 +00001225 }
cristyfbaaf3e2010-04-08 14:53:43 +00001226 (void) cmsCloseProfile(source_profile);
cristy8a20a2d2010-04-08 14:16:27 +00001227 }
cristy3ed852e2009-09-05 21:47:34 +00001228#endif
1229 }
cristy3ed852e2009-09-05 21:47:34 +00001230 profile=DestroyStringInfo(profile);
1231 return(status);
1232}
1233
1234/*
1235%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1236% %
1237% %
1238% %
1239% R e m o v e I m a g e P r o f i l e %
1240% %
1241% %
1242% %
1243%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1244%
1245% RemoveImageProfile() removes a named profile from the image and returns its
1246% value.
1247%
1248% The format of the RemoveImageProfile method is:
1249%
1250% void *RemoveImageProfile(Image *image,const char *name)
1251%
1252% A description of each parameter follows:
1253%
1254% o image: the image.
1255%
1256% o name: the profile name.
1257%
1258*/
1259MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
1260{
1261 StringInfo
1262 *profile;
1263
1264 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001265 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001266 if (image->debug != MagickFalse)
1267 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1268 if (image->profiles == (SplayTreeInfo *) NULL)
1269 return((StringInfo *) NULL);
dirk9f4f3542014-04-21 17:19:03 +00001270 WriteTo8BimProfile(image,name,(StringInfo *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001271 profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
1272 image->profiles,name);
1273 return(profile);
1274}
1275
1276/*
1277%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1278% %
1279% %
1280% %
1281% R e s e t P r o f i l e I t e r a t o r %
1282% %
1283% %
1284% %
1285%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1286%
1287% ResetImageProfileIterator() resets the image profile iterator. Use it in
1288% conjunction with GetNextImageProfile() to iterate over all the profiles
1289% associated with an image.
1290%
1291% The format of the ResetImageProfileIterator method is:
1292%
1293% ResetImageProfileIterator(Image *image)
1294%
1295% A description of each parameter follows:
1296%
1297% o image: the image.
1298%
1299*/
1300MagickExport void ResetImageProfileIterator(const Image *image)
1301{
1302 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001303 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001304 if (image->debug != MagickFalse)
1305 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1306 if (image->profiles == (SplayTreeInfo *) NULL)
1307 return;
1308 ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
1309}
1310
1311/*
1312%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1313% %
1314% %
1315% %
1316% S e t I m a g e P r o f i l e %
1317% %
1318% %
1319% %
1320%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1321%
1322% SetImageProfile() adds a named profile to the image. If a profile with the
1323% same name already exists, it is replaced. This method differs from the
1324% ProfileImage() method in that it does not apply CMS color profiles.
1325%
1326% The format of the SetImageProfile method is:
1327%
1328% MagickBooleanType SetImageProfile(Image *image,const char *name,
1329% const StringInfo *profile)
1330%
1331% A description of each parameter follows:
1332%
1333% o image: the image.
1334%
1335% o name: the profile name, for example icc, exif, and 8bim (8bim is the
1336% Photoshop wrapper for iptc profiles).
1337%
1338% o profile: A StringInfo structure that contains the named profile.
1339%
1340*/
1341
1342static void *DestroyProfile(void *profile)
1343{
1344 return((void *) DestroyStringInfo((StringInfo *) profile));
1345}
1346
1347static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1348 unsigned char *quantum)
1349{
1350 *quantum=(*p++);
1351 return(p);
1352}
1353
cristy3ed852e2009-09-05 21:47:34 +00001354static inline const unsigned char *ReadResourceLong(const unsigned char *p,
cristy3fb79582014-03-13 17:04:24 +00001355 unsigned int *quantum)
cristy3ed852e2009-09-05 21:47:34 +00001356{
cristybb503372010-05-27 20:51:26 +00001357 *quantum=(size_t) (*p++ << 24);
1358 *quantum|=(size_t) (*p++ << 16);
1359 *quantum|=(size_t) (*p++ << 8);
1360 *quantum|=(size_t) (*p++ << 0);
cristy3ed852e2009-09-05 21:47:34 +00001361 return(p);
1362}
1363
1364static inline const unsigned char *ReadResourceShort(const unsigned char *p,
1365 unsigned short *quantum)
1366{
1367 *quantum=(unsigned short) (*p++ << 8);
1368 *quantum|=(unsigned short) (*p++ << 0);
1369 return(p);
dirke9dbde12014-04-23 16:03:29 +00001370}static inline void WriteResourceLong(unsigned char *p,
1371 const unsigned int quantum)
1372{
1373 unsigned char
1374 buffer[4];
1375
1376 buffer[0]=(unsigned char) (quantum >> 24);
1377 buffer[1]=(unsigned char) (quantum >> 16);
1378 buffer[2]=(unsigned char) (quantum >> 8);
1379 buffer[3]=(unsigned char) quantum;
1380 (void) CopyMagickMemory(p,buffer,4);
1381}
1382
1383static void WriteTo8BimProfile(Image *image,const char *name,
dirk4b099fc2014-04-27 16:05:56 +00001384 const StringInfo *profile)
dirke9dbde12014-04-23 16:03:29 +00001385{
dirke9dbde12014-04-23 16:03:29 +00001386 const unsigned char
1387 *datum,
cristyb0689062014-08-14 11:37:55 +00001388 *q;
dirke9dbde12014-04-23 16:03:29 +00001389
1390 register const unsigned char
1391 *p;
1392
1393 size_t
1394 length;
1395
1396 StringInfo
dirk4b099fc2014-04-27 16:05:56 +00001397 *profile_8bim;
dirke9dbde12014-04-23 16:03:29 +00001398
1399 ssize_t
1400 count;
1401
1402 unsigned char
1403 length_byte;
1404
1405 unsigned int
1406 value;
1407
1408 unsigned short
dirk5bdcdb92014-04-27 04:07:50 +00001409 id,
1410 profile_id;
dirke9dbde12014-04-23 16:03:29 +00001411
dirk4b099fc2014-04-27 16:05:56 +00001412 if (LocaleCompare(name,"icc") == 0)
1413 profile_id=0x040f;
dirk5bdcdb92014-04-27 04:07:50 +00001414 else
cristyb0689062014-08-14 11:37:55 +00001415 if (LocaleCompare(name,"iptc") == 0)
1416 profile_id=0x0404;
1417 else
1418 if (LocaleCompare(name,"xmp") == 0)
1419 profile_id=0x0424;
1420 else
1421 return;
1422 profile_8bim=(StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
dirk4b099fc2014-04-27 16:05:56 +00001423 image->profiles,"8bim");
1424 if (profile_8bim == (StringInfo *) NULL)
dirke9dbde12014-04-23 16:03:29 +00001425 return;
dirk4b099fc2014-04-27 16:05:56 +00001426 datum=GetStringInfoDatum(profile_8bim);
1427 length=GetStringInfoLength(profile_8bim);
dirke9dbde12014-04-23 16:03:29 +00001428 for (p=datum; p < (datum+length-16); )
1429 {
cristyb0689062014-08-14 11:37:55 +00001430 q=p;
dirke9dbde12014-04-23 16:03:29 +00001431 if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1432 break;
1433 p+=4;
1434 p=ReadResourceShort(p,&id);
1435 p=ReadResourceByte(p,&length_byte);
1436 p+=length_byte;
1437 if (((length_byte+1) & 0x01) != 0)
1438 p++;
1439 if (p > (datum+length-4))
1440 break;
1441 p=ReadResourceLong(p,&value);
1442 count=(ssize_t) value;
dirke9dbde12014-04-23 16:03:29 +00001443 if ((count & 0x01) != 0)
1444 count++;
cristyb0689062014-08-14 11:37:55 +00001445 if ((p > (datum+length-count)) || (count > (ssize_t) length))
1446 break;
1447 if (id != profile_id)
1448 p+=count;
1449 else
dirke9dbde12014-04-23 16:03:29 +00001450 {
1451 size_t
cristyb0689062014-08-14 11:37:55 +00001452 extent,
1453 offset;
dirke9dbde12014-04-23 16:03:29 +00001454
1455 ssize_t
cristyb0689062014-08-14 11:37:55 +00001456 extract_count;
dirke9dbde12014-04-23 16:03:29 +00001457
1458 StringInfo
cristyb0689062014-08-14 11:37:55 +00001459 *extract_profile;
dirke9dbde12014-04-23 16:03:29 +00001460
cristyb0689062014-08-14 11:37:55 +00001461 extract_count=0;
1462 extent=(datum+length)-(p+count);
dirk4b099fc2014-04-27 16:05:56 +00001463 if (profile == (StringInfo *) NULL)
dirke9dbde12014-04-23 16:03:29 +00001464 {
cristyb0689062014-08-14 11:37:55 +00001465 offset=(q-datum);
1466 extract_profile=AcquireStringInfo(offset+extent);
1467 (void) CopyMagickMemory(extract_profile->datum,datum,offset);
dirke9dbde12014-04-23 16:03:29 +00001468 }
1469 else
1470 {
1471 offset=(p-datum);
cristyb0689062014-08-14 11:37:55 +00001472 extract_count=profile->length;
1473 if ((extract_count & 0x01) != 0)
1474 extract_count++;
1475 extract_profile=AcquireStringInfo(offset+extract_count+extent);
1476 (void) CopyMagickMemory(extract_profile->datum,datum,offset-4);
dirkb0d783f2014-08-31 10:48:05 +00001477 WriteResourceLong(extract_profile->datum+offset-4,
1478 (unsigned int)profile->length);
cristyb0689062014-08-14 11:37:55 +00001479 (void) CopyMagickMemory(extract_profile->datum+offset,
1480 profile->datum,profile->length);
dirke9dbde12014-04-23 16:03:29 +00001481 }
cristyb0689062014-08-14 11:37:55 +00001482 (void) CopyMagickMemory(extract_profile->datum+offset+extract_count,
1483 p+count,extent);
dirke9dbde12014-04-23 16:03:29 +00001484 (void) AddValueToSplayTree((SplayTreeInfo *) image->profiles,
cristyb0689062014-08-14 11:37:55 +00001485 ConstantString("8bim"),CloneStringInfo(extract_profile));
1486 extract_profile=DestroyStringInfo(extract_profile);
dirke9dbde12014-04-23 16:03:29 +00001487 break;
1488 }
dirke9dbde12014-04-23 16:03:29 +00001489 }
1490}
1491
dirk9f4f3542014-04-21 17:19:03 +00001492static void GetProfilesFromResourceBlock(Image *image,
cristyd15e6592011-10-15 00:13:06 +00001493 const StringInfo *resource_block,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001494{
1495 const unsigned char
1496 *datum;
1497
1498 register const unsigned char
1499 *p;
1500
1501 size_t
1502 length;
1503
cristy66aeb172014-03-13 17:06:31 +00001504 ssize_t
1505 count;
1506
cristy3ed852e2009-09-05 21:47:34 +00001507 StringInfo
1508 *profile;
1509
1510 unsigned char
1511 length_byte;
1512
cristy3fb79582014-03-13 17:04:24 +00001513 unsigned int
1514 value;
1515
cristy3ed852e2009-09-05 21:47:34 +00001516 unsigned short
1517 id;
1518
1519 datum=GetStringInfoDatum(resource_block);
1520 length=GetStringInfoLength(resource_block);
1521 for (p=datum; p < (datum+length-16); )
1522 {
1523 if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1524 break;
1525 p+=4;
1526 p=ReadResourceShort(p,&id);
1527 p=ReadResourceByte(p,&length_byte);
1528 p+=length_byte;
1529 if (((length_byte+1) & 0x01) != 0)
1530 p++;
1531 if (p > (datum+length-4))
1532 break;
cristy3fb79582014-03-13 17:04:24 +00001533 p=ReadResourceLong(p,&value);
1534 count=(ssize_t) value;
cristy759ba912014-06-26 11:59:43 +00001535 if ((p > (datum+length-count)) || (count > (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00001536 break;
1537 switch (id)
1538 {
1539 case 0x03ed:
1540 {
dirk3287f1d2014-03-11 20:38:36 +00001541 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00001542 resolution;
1543
cristy3fb79582014-03-13 17:04:24 +00001544 unsigned short
1545 units;
1546
cristy3ed852e2009-09-05 21:47:34 +00001547 /*
1548 Resolution.
1549 */
dirk3287f1d2014-03-11 20:38:36 +00001550 p=ReadResourceLong(p,&resolution);
1551 image->resolution.x=((double) resolution)/65536.0;
1552 p=ReadResourceShort(p,&units)+2;
1553 p=ReadResourceLong(p,&resolution)+4;
1554 image->resolution.y=((double) resolution)/65536.0;
cristy3fb79582014-03-13 17:04:24 +00001555 /*
1556 Values are always stored as pixels per inch.
1557 */
1558 if ((ResolutionType) units != PixelsPerCentimeterResolution)
1559 image->units=PixelsPerInchResolution;
1560 else
dirk3287f1d2014-03-11 20:38:36 +00001561 {
1562 image->units=PixelsPerCentimeterResolution;
1563 image->resolution.x/=2.54;
1564 image->resolution.y/=2.54;
1565 }
cristy3ed852e2009-09-05 21:47:34 +00001566 break;
1567 }
1568 case 0x0404:
1569 {
1570 /*
1571 IPTC Profile
1572 */
1573 profile=AcquireStringInfo(count);
1574 SetStringInfoDatum(profile,p);
dirk9f4f3542014-04-21 17:19:03 +00001575 (void) SetImageProfileInternal(image,"iptc",profile,MagickTrue,
1576 exception);
cristy3ed852e2009-09-05 21:47:34 +00001577 profile=DestroyStringInfo(profile);
1578 p+=count;
1579 break;
1580 }
1581 case 0x040c:
1582 {
1583 /*
1584 Thumbnail.
1585 */
1586 p+=count;
1587 break;
1588 }
1589 case 0x040f:
1590 {
1591 /*
1592 ICC Profile.
1593 */
1594 profile=AcquireStringInfo(count);
1595 SetStringInfoDatum(profile,p);
dirk9f4f3542014-04-21 17:19:03 +00001596 (void) SetImageProfileInternal(image,"icc",profile,MagickTrue,
1597 exception);
cristy3ed852e2009-09-05 21:47:34 +00001598 profile=DestroyStringInfo(profile);
1599 p+=count;
1600 break;
1601 }
1602 case 0x0422:
1603 {
1604 /*
1605 EXIF Profile.
1606 */
1607 profile=AcquireStringInfo(count);
1608 SetStringInfoDatum(profile,p);
dirk9f4f3542014-04-21 17:19:03 +00001609 (void) SetImageProfileInternal(image,"exif",profile,MagickTrue,
1610 exception);
cristy3ed852e2009-09-05 21:47:34 +00001611 profile=DestroyStringInfo(profile);
1612 p+=count;
1613 break;
1614 }
1615 case 0x0424:
1616 {
1617 /*
1618 XMP Profile.
1619 */
1620 profile=AcquireStringInfo(count);
1621 SetStringInfoDatum(profile,p);
dirk9f4f3542014-04-21 17:19:03 +00001622 (void) SetImageProfileInternal(image,"xmp",profile,MagickTrue,
1623 exception);
cristy3ed852e2009-09-05 21:47:34 +00001624 profile=DestroyStringInfo(profile);
1625 p+=count;
1626 break;
1627 }
1628 default:
1629 {
1630 p+=count;
1631 break;
1632 }
1633 }
1634 if ((count & 0x01) != 0)
1635 p++;
1636 }
cristy3ed852e2009-09-05 21:47:34 +00001637}
1638
dirk9f4f3542014-04-21 17:19:03 +00001639static MagickBooleanType SetImageProfileInternal(Image *image,const char *name,
1640 const StringInfo *profile,const MagickBooleanType recursive,
1641 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001642{
1643 char
cristy151b66d2015-04-15 10:50:31 +00001644 key[MagickPathExtent],
1645 property[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001646
1647 MagickBooleanType
1648 status;
1649
1650 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001651 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001652 if (image->debug != MagickFalse)
1653 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1654 if (image->profiles == (SplayTreeInfo *) NULL)
1655 image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
1656 DestroyProfile);
cristy151b66d2015-04-15 10:50:31 +00001657 (void) CopyMagickString(key,name,MagickPathExtent);
dirk9f4f3542014-04-21 17:19:03 +00001658 LocaleLower(key);
cristy3ed852e2009-09-05 21:47:34 +00001659 status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1660 ConstantString(key),CloneStringInfo(profile));
dirk9f4f3542014-04-21 17:19:03 +00001661 if (status != MagickFalse)
1662 {
1663 if (LocaleCompare(name,"8bim") == 0)
1664 GetProfilesFromResourceBlock(image,profile,exception);
dirke9dbde12014-04-23 16:03:29 +00001665 else if (recursive == MagickFalse)
dirk9f4f3542014-04-21 17:19:03 +00001666 WriteTo8BimProfile(image,name,profile);
1667 }
cristy3ed852e2009-09-05 21:47:34 +00001668 /*
1669 Inject profile into image properties.
1670 */
cristy151b66d2015-04-15 10:50:31 +00001671 (void) FormatLocaleString(property,MagickPathExtent,"%s:*",name);
cristyd15e6592011-10-15 00:13:06 +00001672 (void) GetImageProperty(image,property,exception);
cristy3ed852e2009-09-05 21:47:34 +00001673 return(status);
1674}
dirk9f4f3542014-04-21 17:19:03 +00001675
1676MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
1677 const StringInfo *profile,ExceptionInfo *exception)
1678{
1679 return(SetImageProfileInternal(image,name,profile,MagickFalse,exception));
1680}
cristy3ed852e2009-09-05 21:47:34 +00001681
1682/*
1683%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1684% %
1685% %
1686% %
1687% S y n c I m a g e P r o f i l e s %
1688% %
1689% %
1690% %
1691%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1692%
1693% SyncImageProfiles() synchronizes image properties with the image profiles.
1694% Currently we only support updating the EXIF resolution and orientation.
1695%
1696% The format of the SyncImageProfiles method is:
1697%
1698% MagickBooleanType SyncImageProfiles(Image *image)
1699%
1700% A description of each parameter follows:
1701%
1702% o image: the image.
1703%
1704*/
1705
1706static inline int ReadProfileByte(unsigned char **p,size_t *length)
1707{
1708 int
1709 c;
1710
1711 if (*length < 1)
1712 return(EOF);
1713 c=(int) (*(*p)++);
1714 (*length)--;
1715 return(c);
1716}
1717
1718static inline unsigned short ReadProfileShort(const EndianType endian,
1719 unsigned char *buffer)
1720{
1721 unsigned short
1722 value;
1723
cristy1715f792012-11-22 16:56:12 +00001724 if (endian == LSBEndian)
cristy3ed852e2009-09-05 21:47:34 +00001725 {
cristy1715f792012-11-22 16:56:12 +00001726 value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
cristy3ed852e2009-09-05 21:47:34 +00001727 return((unsigned short) (value & 0xffff));
1728 }
cristy1715f792012-11-22 16:56:12 +00001729 value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
1730 ((unsigned char *) buffer)[1]);
cristy3ed852e2009-09-05 21:47:34 +00001731 return((unsigned short) (value & 0xffff));
1732}
1733
cristy26c14ef2014-10-05 13:59:05 +00001734static inline unsigned int ReadProfileLong(const EndianType endian,
cristy3ed852e2009-09-05 21:47:34 +00001735 unsigned char *buffer)
1736{
cristy26c14ef2014-10-05 13:59:05 +00001737 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00001738 value;
1739
cristy1715f792012-11-22 16:56:12 +00001740 if (endian == LSBEndian)
cristy3ed852e2009-09-05 21:47:34 +00001741 {
cristy26c14ef2014-10-05 13:59:05 +00001742 value=(unsigned int) ((buffer[3] << 24) | (buffer[2] << 16) |
cristy1715f792012-11-22 16:56:12 +00001743 (buffer[1] << 8 ) | (buffer[0]));
cristy26c14ef2014-10-05 13:59:05 +00001744 return((unsigned int) (value & 0xffffffff));
cristy3ed852e2009-09-05 21:47:34 +00001745 }
cristy26c14ef2014-10-05 13:59:05 +00001746 value=(unsigned int) ((buffer[0] << 24) | (buffer[1] << 16) |
cristy1715f792012-11-22 16:56:12 +00001747 (buffer[2] << 8) | buffer[3]);
cristy26c14ef2014-10-05 13:59:05 +00001748 return((unsigned int) (value & 0xffffffff));
cristy3ed852e2009-09-05 21:47:34 +00001749}
1750
cristy26c14ef2014-10-05 13:59:05 +00001751static inline unsigned int ReadProfileMSBLong(unsigned char **p,size_t *length)
dirk5bfa82b2014-03-01 11:22:55 +00001752{
cristy26c14ef2014-10-05 13:59:05 +00001753 unsigned int
dirk5bfa82b2014-03-01 11:22:55 +00001754 value;
1755
1756 if (*length < 4)
1757 return(0);
dirk5bfa82b2014-03-01 11:22:55 +00001758 value=ReadProfileLong(MSBEndian,*p);
1759 (*length)-=4;
1760 *p+=4;
1761 return(value);
1762}
1763
1764static inline unsigned short ReadProfileMSBShort(unsigned char **p,
1765 size_t *length)
1766{
1767 unsigned short
1768 value;
1769
1770 if (*length < 2)
1771 return(0);
dirk5bfa82b2014-03-01 11:22:55 +00001772 value=ReadProfileShort(MSBEndian,*p);
1773 (*length)-=2;
1774 *p+=2;
1775 return(value);
1776}
1777
cristy3ed852e2009-09-05 21:47:34 +00001778static inline void WriteProfileLong(const EndianType endian,
cristybb503372010-05-27 20:51:26 +00001779 const size_t value,unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001780{
1781 unsigned char
1782 buffer[4];
1783
cristy1715f792012-11-22 16:56:12 +00001784 if (endian == LSBEndian)
cristy3ed852e2009-09-05 21:47:34 +00001785 {
cristy1715f792012-11-22 16:56:12 +00001786 buffer[0]=(unsigned char) value;
1787 buffer[1]=(unsigned char) (value >> 8);
1788 buffer[2]=(unsigned char) (value >> 16);
1789 buffer[3]=(unsigned char) (value >> 24);
cristy3ed852e2009-09-05 21:47:34 +00001790 (void) CopyMagickMemory(p,buffer,4);
1791 return;
1792 }
cristy1715f792012-11-22 16:56:12 +00001793 buffer[0]=(unsigned char) (value >> 24);
1794 buffer[1]=(unsigned char) (value >> 16);
1795 buffer[2]=(unsigned char) (value >> 8);
1796 buffer[3]=(unsigned char) value;
cristy3ed852e2009-09-05 21:47:34 +00001797 (void) CopyMagickMemory(p,buffer,4);
1798}
1799
1800static void WriteProfileShort(const EndianType endian,
1801 const unsigned short value,unsigned char *p)
1802{
1803 unsigned char
1804 buffer[2];
1805
cristy1715f792012-11-22 16:56:12 +00001806 if (endian == LSBEndian)
cristy3ed852e2009-09-05 21:47:34 +00001807 {
cristy1715f792012-11-22 16:56:12 +00001808 buffer[0]=(unsigned char) value;
1809 buffer[1]=(unsigned char) (value >> 8);
cristy3ed852e2009-09-05 21:47:34 +00001810 (void) CopyMagickMemory(p,buffer,2);
1811 return;
1812 }
cristy1715f792012-11-22 16:56:12 +00001813 buffer[0]=(unsigned char) (value >> 8);
1814 buffer[1]=(unsigned char) value;
cristy3ed852e2009-09-05 21:47:34 +00001815 (void) CopyMagickMemory(p,buffer,2);
1816}
1817
dirk5bfa82b2014-03-01 11:22:55 +00001818static MagickBooleanType Sync8BimProfile(Image *image,StringInfo *profile)
1819{
1820 size_t
dirk5bfa82b2014-03-01 11:22:55 +00001821 length;
1822
cristy26c14ef2014-10-05 13:59:05 +00001823 ssize_t
1824 count;
1825
dirk5bfa82b2014-03-01 11:22:55 +00001826 unsigned char
1827 *p;
1828
1829 unsigned short
1830 id;
1831
1832 length=GetStringInfoLength(profile);
1833 p=GetStringInfoDatum(profile);
cristy26c14ef2014-10-05 13:59:05 +00001834 while (length != 0)
dirk5bfa82b2014-03-01 11:22:55 +00001835 {
1836 if (ReadProfileByte(&p,&length) != 0x38)
1837 continue;
1838 if (ReadProfileByte(&p,&length) != 0x42)
1839 continue;
1840 if (ReadProfileByte(&p,&length) != 0x49)
1841 continue;
1842 if (ReadProfileByte(&p,&length) != 0x4D)
1843 continue;
1844 if (length < 7)
1845 return(MagickFalse);
1846 id=ReadProfileMSBShort(&p,&length);
cristy26c14ef2014-10-05 13:59:05 +00001847 count=(ssize_t) ReadProfileByte(&p,&length);
cristy02bcb982014-10-05 19:22:25 +00001848 if (count > (ssize_t) length)
dirk5bfa82b2014-03-01 11:22:55 +00001849 return(MagickFalse);
1850 p+=count;
1851 if ((*p & 0x01) == 0)
cristy26c14ef2014-10-05 13:59:05 +00001852 (void) ReadProfileByte(&p,&length);
1853 count=(ssize_t) ReadProfileMSBLong(&p,&length);
cristy02bcb982014-10-05 19:22:25 +00001854 if (count > (ssize_t) length)
dirk5bfa82b2014-03-01 11:22:55 +00001855 return(MagickFalse);
cristy26c14ef2014-10-05 13:59:05 +00001856 if ((id == 0x3ED) && (count == 16))
dirk5bfa82b2014-03-01 11:22:55 +00001857 {
dirk3287f1d2014-03-11 20:38:36 +00001858 if (image->units == PixelsPerCentimeterResolution)
1859 WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.x*2.54*
1860 65536.0),p);
1861 else
1862 WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.x*
1863 65536.0),p);
1864 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+4);
1865 if (image->units == PixelsPerCentimeterResolution)
1866 WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.y*2.54*
1867 65536.0),p+8);
1868 else
1869 WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.y*
1870 65536.0),p+8);
1871 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+12);
dirk5bfa82b2014-03-01 11:22:55 +00001872 }
1873 p+=count;
1874 length-=count;
1875 }
1876 return(MagickTrue);
1877}
1878
1879MagickBooleanType SyncExifProfile(Image *image,StringInfo *profile)
cristy3ed852e2009-09-05 21:47:34 +00001880{
1881#define MaxDirectoryStack 16
1882#define EXIF_DELIMITER "\n"
1883#define EXIF_NUM_FORMATS 12
1884#define TAG_EXIF_OFFSET 0x8769
1885#define TAG_INTEROP_OFFSET 0xa005
1886
1887 typedef struct _DirectoryInfo
1888 {
1889 unsigned char
1890 *directory;
1891
cristybb503372010-05-27 20:51:26 +00001892 size_t
cristy3ed852e2009-09-05 21:47:34 +00001893 entry;
1894 } DirectoryInfo;
1895
1896 DirectoryInfo
1897 directory_stack[MaxDirectoryStack];
1898
1899 EndianType
1900 endian;
1901
cristy3ed852e2009-09-05 21:47:34 +00001902 size_t
cristyba978e12010-09-12 20:26:50 +00001903 entry,
1904 length,
1905 number_entries;
cristy3ed852e2009-09-05 21:47:34 +00001906
cristy9d314ff2011-03-09 01:30:28 +00001907 ssize_t
1908 id,
cristy82544b92012-02-28 20:11:42 +00001909 level,
1910 offset;
cristy9d314ff2011-03-09 01:30:28 +00001911
cristy3ed852e2009-09-05 21:47:34 +00001912 static int
1913 format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1914
cristy3ed852e2009-09-05 21:47:34 +00001915 unsigned char
1916 *directory,
1917 *exif;
1918
cristy3ed852e2009-09-05 21:47:34 +00001919 /*
1920 Set EXIF resolution tag.
1921 */
cristy3ed852e2009-09-05 21:47:34 +00001922 length=GetStringInfoLength(profile);
1923 exif=GetStringInfoDatum(profile);
cristy3ed852e2009-09-05 21:47:34 +00001924 if (length < 16)
1925 return(MagickFalse);
cristycee97112010-05-28 00:44:52 +00001926 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
dirk0c558772014-03-01 17:38:22 +00001927 if ((id != 0x4949) && (id != 0x4D4D))
1928 {
1929 while (length != 0)
1930 {
1931 if (ReadProfileByte(&exif,&length) != 0x45)
1932 continue;
1933 if (ReadProfileByte(&exif,&length) != 0x78)
1934 continue;
1935 if (ReadProfileByte(&exif,&length) != 0x69)
1936 continue;
1937 if (ReadProfileByte(&exif,&length) != 0x66)
1938 continue;
1939 if (ReadProfileByte(&exif,&length) != 0x00)
1940 continue;
1941 if (ReadProfileByte(&exif,&length) != 0x00)
1942 continue;
1943 break;
1944 }
1945 if (length < 16)
1946 return(MagickFalse);
1947 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
1948 }
cristy3ed852e2009-09-05 21:47:34 +00001949 endian=LSBEndian;
1950 if (id == 0x4949)
1951 endian=LSBEndian;
1952 else
1953 if (id == 0x4D4D)
1954 endian=MSBEndian;
1955 else
1956 return(MagickFalse);
1957 if (ReadProfileShort(endian,exif+2) != 0x002a)
1958 return(MagickFalse);
1959 /*
1960 This the offset to the first IFD.
1961 */
cristy82544b92012-02-28 20:11:42 +00001962 offset=(ssize_t) ((int) ReadProfileLong(endian,exif+4));
cristy64de1572012-02-29 02:44:11 +00001963 if ((offset < 0) || (size_t) offset >= length)
cristy3ed852e2009-09-05 21:47:34 +00001964 return(MagickFalse);
1965 directory=exif+offset;
1966 level=0;
1967 entry=0;
1968 do
1969 {
1970 if (level > 0)
1971 {
1972 level--;
1973 directory=directory_stack[level].directory;
1974 entry=directory_stack[level].entry;
1975 }
cristy4a99eb92015-02-19 12:32:19 +00001976 if ((directory < exif) || (directory > (exif+length-2)))
1977 break;
cristy3ed852e2009-09-05 21:47:34 +00001978 /*
1979 Determine how many entries there are in the current IFD.
1980 */
1981 number_entries=ReadProfileShort(endian,directory);
1982 for ( ; entry < number_entries; entry++)
1983 {
cristyba978e12010-09-12 20:26:50 +00001984 int
1985 components;
1986
cristy3ed852e2009-09-05 21:47:34 +00001987 register unsigned char
1988 *p,
1989 *q;
1990
1991 size_t
1992 number_bytes;
1993
cristy9d314ff2011-03-09 01:30:28 +00001994 ssize_t
1995 format,
1996 tag_value;
1997
cristy3ed852e2009-09-05 21:47:34 +00001998 q=(unsigned char *) (directory+2+(12*entry));
cristybb503372010-05-27 20:51:26 +00001999 tag_value=(ssize_t) ReadProfileShort(endian,q);
2000 format=(ssize_t) ReadProfileShort(endian,q+2);
cristy3ed852e2009-09-05 21:47:34 +00002001 if ((format-1) >= EXIF_NUM_FORMATS)
2002 break;
cristy4d14d592012-04-03 12:58:43 +00002003 components=(ssize_t) ((int) ReadProfileLong(endian,q+4));
cristy3ed852e2009-09-05 21:47:34 +00002004 number_bytes=(size_t) components*format_bytes[format];
cristyd228c032012-06-03 15:01:15 +00002005 if ((ssize_t) number_bytes < components)
cristy4d14d592012-04-03 12:58:43 +00002006 break; /* prevent overflow */
cristy3ed852e2009-09-05 21:47:34 +00002007 if (number_bytes <= 4)
2008 p=q+8;
2009 else
2010 {
cristy82544b92012-02-28 20:11:42 +00002011 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002012 offset;
2013
2014 /*
2015 The directory entry contains an offset.
2016 */
cristy82544b92012-02-28 20:11:42 +00002017 offset=(ssize_t) ((int) ReadProfileLong(endian,q+8));
cristy3d8d1f12012-02-29 01:59:28 +00002018 if ((size_t) (offset+number_bytes) > length)
cristy3ed852e2009-09-05 21:47:34 +00002019 continue;
cristy3d8d1f12012-02-29 01:59:28 +00002020 if (~length < number_bytes)
2021 continue; /* prevent overflow */
cristy3ed852e2009-09-05 21:47:34 +00002022 p=(unsigned char *) (exif+offset);
2023 }
2024 switch (tag_value)
2025 {
2026 case 0x011a:
2027 {
cristy82544b92012-02-28 20:11:42 +00002028 (void) WriteProfileLong(endian,(size_t) (image->resolution.x+0.5),p);
cristy3ed852e2009-09-05 21:47:34 +00002029 (void) WriteProfileLong(endian,1UL,p+4);
2030 break;
2031 }
2032 case 0x011b:
2033 {
cristy82544b92012-02-28 20:11:42 +00002034 (void) WriteProfileLong(endian,(size_t) (image->resolution.y+0.5),p);
cristy3ed852e2009-09-05 21:47:34 +00002035 (void) WriteProfileLong(endian,1UL,p+4);
2036 break;
2037 }
2038 case 0x0112:
2039 {
cristy91698032012-04-06 17:02:52 +00002040 if (number_bytes == 4)
2041 {
2042 (void) WriteProfileLong(endian,(size_t) image->orientation,p);
2043 break;
2044 }
cristy82544b92012-02-28 20:11:42 +00002045 (void) WriteProfileShort(endian,(unsigned short) image->orientation,
2046 p);
cristy3ed852e2009-09-05 21:47:34 +00002047 break;
2048 }
2049 case 0x0128:
2050 {
cristy91698032012-04-06 17:02:52 +00002051 if (number_bytes == 4)
2052 {
2053 (void) WriteProfileLong(endian,(size_t) (image->units+1),p);
2054 break;
2055 }
cristy82544b92012-02-28 20:11:42 +00002056 (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
cristy3ed852e2009-09-05 21:47:34 +00002057 break;
2058 }
2059 default:
2060 break;
2061 }
2062 if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
2063 {
cristy82544b92012-02-28 20:11:42 +00002064 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002065 offset;
2066
cristy82544b92012-02-28 20:11:42 +00002067 offset=(ssize_t) ((int) ReadProfileLong(endian,p));
cristy3d8d1f12012-02-29 01:59:28 +00002068 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
cristy3ed852e2009-09-05 21:47:34 +00002069 {
2070 directory_stack[level].directory=directory;
2071 entry++;
2072 directory_stack[level].entry=entry;
2073 level++;
2074 directory_stack[level].directory=exif+offset;
2075 directory_stack[level].entry=0;
2076 level++;
2077 if ((directory+2+(12*number_entries)) > (exif+length))
2078 break;
cristy82544b92012-02-28 20:11:42 +00002079 offset=(ssize_t) ((int) ReadProfileLong(endian,directory+2+(12*
2080 number_entries)));
cristy3d8d1f12012-02-29 01:59:28 +00002081 if ((offset != 0) && ((size_t) offset < length) &&
cristy3ed852e2009-09-05 21:47:34 +00002082 (level < (MaxDirectoryStack-2)))
2083 {
2084 directory_stack[level].directory=exif+offset;
2085 directory_stack[level].entry=0;
2086 level++;
2087 }
2088 }
2089 break;
2090 }
2091 }
2092 } while (level > 0);
2093 return(MagickTrue);
2094}
dirk5bfa82b2014-03-01 11:22:55 +00002095
dirk11b14152014-03-01 19:06:16 +00002096MagickPrivate MagickBooleanType SyncImageProfiles(Image *image)
dirk5bfa82b2014-03-01 11:22:55 +00002097{
2098 MagickBooleanType
2099 status;
2100
2101 StringInfo
2102 *profile;
2103
2104 status=MagickTrue;
2105 profile=(StringInfo *) GetImageProfile(image,"8BIM");
2106 if (profile != (StringInfo *) NULL)
2107 if (Sync8BimProfile(image,profile) == MagickFalse)
2108 status=MagickFalse;
2109 profile=(StringInfo *) GetImageProfile(image,"EXIF");
2110 if (profile != (StringInfo *) NULL)
2111 if (SyncExifProfile(image,profile) == MagickFalse)
2112 status=MagickFalse;
2113 return(status);
cristy3fb79582014-03-13 17:04:24 +00002114}