blob: cb363d444a8de31f03480b8b3bfbddf063f0029a [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% %
cristyfe676ee2013-11-18 13:03:38 +000020% Copyright 1999-2014 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)
cristyd09bcf92010-03-25 03:04:45 +000070#if defined(MAGICKCORE_HAVE_LCMS_LCMS2_H)
71#include <wchar.h>
72#include <lcms/lcms2.h>
73#elif defined(MAGICKCORE_HAVE_LCMS2_H)
74#include <wchar.h>
75#include "lcms2.h"
76#elif defined(MAGICKCORE_HAVE_LCMS_LCMS_H)
cristy3ed852e2009-09-05 21:47:34 +000077#include <lcms/lcms.h>
78#else
79#include "lcms.h"
80#endif
81#endif
82
83/*
cristy2110f4b2010-04-13 19:15:02 +000084 Define declarations.
85*/
cristy71203402010-06-18 13:12:03 +000086#if !defined(LCMS_VERSION) || (LCMS_VERSION < 2000)
cristy20a78b22010-04-05 01:22:12 +000087#define cmsSigCmykData icSigCmykData
88#define cmsSigGrayData icSigGrayData
89#define cmsSigLabData icSigLabData
90#define cmsSigLuvData icSigLuvData
91#define cmsSigRgbData icSigRgbData
92#define cmsSigXYZData icSigXYZData
93#define cmsSigYCbCrData icSigYCbCrData
cristy902d15b2010-04-07 19:44:01 +000094#define cmsSigLinkClass icSigLinkClass
cristybbf9c612010-04-08 13:37:47 +000095#define cmsColorSpaceSignature icColorSpaceSignature
cristy71203402010-06-18 13:12:03 +000096#define cmsUInt32Number DWORD
97#define cmsSetLogErrorHandler(handler) cmsSetErrorHandler(handler)
98#define cmsCreateTransformTHR(context,source_profile,source_type, \
99 target_profile,target_type,intent,flags) cmsCreateTransform(source_profile, \
100 source_type,target_profile,target_type,intent,flags);
101#define cmsOpenProfileFromMemTHR(context,profile,length) \
102 cmsOpenProfileFromMem(profile,length)
dirke9dbde12014-04-23 16:03:29 +0000103#endif
104
105/*
106 Forward declarations
107*/
108static MagickBooleanType
109 SetImageProfileInternal(Image *,const char *,const StringInfo *,
110 const MagickBooleanType,ExceptionInfo *);
111
112static void
dirk9f4f3542014-04-21 17:19:03 +0000113 WriteTo8BimProfile(Image *,const char*,const StringInfo *);
cristyd09bcf92010-03-25 03:04:45 +0000114
115/*
cristy092d71c2011-10-14 18:01:29 +0000116 Typedef declarations
117*/
cristy3fac9ec2011-11-17 18:04:39 +0000118struct _ProfileInfo
cristy092d71c2011-10-14 18:01:29 +0000119{
120 char
121 *name;
122
123 size_t
124 length;
125
126 unsigned char
127 *info;
128
129 size_t
130 signature;
131};
cristyd0b7ee52011-10-21 11:19:02 +0000132
133typedef struct _CMSExceptionInfo
134{
135 Image
136 *image;
137
138 ExceptionInfo
139 *exception;
140} CMSExceptionInfo;
cristy092d71c2011-10-14 18:01:29 +0000141
142/*
cristy3ed852e2009-09-05 21:47:34 +0000143%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
144% %
145% %
146% %
147% C l o n e I m a g e P r o f i l e s %
148% %
149% %
150% %
151%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
152%
153% CloneImageProfiles() clones one or more image profiles.
154%
155% The format of the CloneImageProfiles method is:
156%
157% MagickBooleanType CloneImageProfiles(Image *image,
158% const Image *clone_image)
159%
160% A description of each parameter follows:
161%
162% o image: the image.
163%
164% o clone_image: the clone image.
165%
166*/
167MagickExport MagickBooleanType CloneImageProfiles(Image *image,
168 const Image *clone_image)
169{
170 assert(image != (Image *) NULL);
171 assert(image->signature == MagickSignature);
172 if (image->debug != MagickFalse)
173 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
174 assert(clone_image != (const Image *) NULL);
175 assert(clone_image->signature == MagickSignature);
cristy3ed852e2009-09-05 21:47:34 +0000176 if (clone_image->profiles != (void *) NULL)
cristy419e2042013-07-18 22:14:51 +0000177 {
178 if (image->profiles != (void *) NULL)
179 DestroyImageProfiles(image);
180 image->profiles=CloneSplayTree((SplayTreeInfo *) clone_image->profiles,
181 (void *(*)(void *)) ConstantString,(void *(*)(void *)) CloneStringInfo);
182 }
cristy3ed852e2009-09-05 21:47:34 +0000183 return(MagickTrue);
184}
185
186/*
187%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
188% %
189% %
190% %
191% D e l e t e I m a g e P r o f i l e %
192% %
193% %
194% %
195%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
196%
197% DeleteImageProfile() deletes a profile from the image by its name.
198%
199% The format of the DeleteImageProfile method is:
200%
201% MagickBooleanTyupe DeleteImageProfile(Image *image,const char *name)
202%
203% A description of each parameter follows:
204%
205% o image: the image.
206%
207% o name: the profile name.
208%
209*/
cristy04390e72010-03-08 00:50:13 +0000210MagickExport MagickBooleanType DeleteImageProfile(Image *image,const char *name)
cristy3ed852e2009-09-05 21:47:34 +0000211{
212 assert(image != (Image *) NULL);
213 assert(image->signature == MagickSignature);
214 if (image->debug != MagickFalse)
215 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
216 if (image->profiles == (SplayTreeInfo *) NULL)
217 return(MagickFalse);
dirk9f4f3542014-04-21 17:19:03 +0000218 WriteTo8BimProfile(image,name,(StringInfo *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000219 return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->profiles,name));
220}
221
222/*
223%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
224% %
225% %
226% %
227% D e s t r o y I m a g e P r o f i l e s %
228% %
229% %
230% %
231%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
232%
233% DestroyImageProfiles() releases memory associated with an image profile map.
234%
235% The format of the DestroyProfiles method is:
236%
237% void DestroyImageProfiles(Image *image)
238%
239% A description of each parameter follows:
240%
241% o image: the image.
242%
243*/
244MagickExport void DestroyImageProfiles(Image *image)
245{
246 if (image->profiles != (SplayTreeInfo *) NULL)
247 image->profiles=DestroySplayTree((SplayTreeInfo *) image->profiles);
248}
249
250/*
251%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
252% %
253% %
254% %
255% G e t I m a g e P r o f i l e %
256% %
257% %
258% %
259%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
260%
261% GetImageProfile() gets a profile associated with an image by name.
262%
263% The format of the GetImageProfile method is:
264%
265% const StringInfo *GetImageProfile(const Image *image,const char *name)
266%
267% A description of each parameter follows:
268%
269% o image: the image.
270%
271% o name: the profile name.
272%
273*/
274MagickExport const StringInfo *GetImageProfile(const Image *image,
275 const char *name)
276{
277 char
278 key[MaxTextExtent];
279
280 const StringInfo
281 *profile;
282
283 assert(image != (Image *) NULL);
284 assert(image->signature == MagickSignature);
285 if (image->debug != MagickFalse)
286 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
287 if (image->profiles == (SplayTreeInfo *) NULL)
288 return((StringInfo *) NULL);
289 (void) CopyMagickString(key,name,MaxTextExtent);
290 profile=(const StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
291 image->profiles,key);
292 return(profile);
293}
294
295/*
296%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
297% %
298% %
299% %
300% G e t N e x t I m a g e P r o f i l e %
301% %
302% %
303% %
304%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
305%
306% GetNextImageProfile() gets the next profile name for an image.
307%
308% The format of the GetNextImageProfile method is:
309%
310% char *GetNextImageProfile(const Image *image)
311%
312% A description of each parameter follows:
313%
314% o hash_info: the hash info.
315%
316*/
317MagickExport char *GetNextImageProfile(const Image *image)
318{
319 assert(image != (Image *) NULL);
320 assert(image->signature == MagickSignature);
321 if (image->debug != MagickFalse)
322 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
323 if (image->profiles == (SplayTreeInfo *) NULL)
324 return((char *) NULL);
325 return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->profiles));
326}
327
328/*
329%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
330% %
331% %
332% %
333% P r o f i l e I m a g e %
334% %
335% %
336% %
337%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
338%
339% ProfileImage() associates, applies, or removes an ICM, IPTC, or generic
340% profile with / to / from an image. If the profile is NULL, it is removed
341% from the image otherwise added or applied. Use a name of '*' and a profile
342% of NULL to remove all profiles from the image.
343%
344% ICC and ICM profiles are handled as follows: If the image does not have
345% an associated color profile, the one you provide is associated with the
346% image and the image pixels are not transformed. Otherwise, the colorspace
347% transform defined by the existing and new profile are applied to the image
348% pixels and the new profile is associated with the image.
349%
350% The format of the ProfileImage method is:
351%
352% MagickBooleanType ProfileImage(Image *image,const char *name,
353% const void *datum,const size_t length,const MagickBooleanType clone)
354%
355% A description of each parameter follows:
356%
357% o image: the image.
358%
359% o name: Name of profile to add or remove: ICC, IPTC, or generic profile.
360%
361% o datum: the profile data.
362%
363% o length: the length of the profile.
364%
365% o clone: should be MagickFalse.
366%
367*/
368
369#if defined(MAGICKCORE_LCMS_DELEGATE)
370
371static unsigned short **DestroyPixelThreadSet(unsigned short **pixels)
372{
cristybb503372010-05-27 20:51:26 +0000373 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000374 i;
375
376 assert(pixels != (unsigned short **) NULL);
cristyac245f82012-05-05 17:13:57 +0000377 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
cristy3ed852e2009-09-05 21:47:34 +0000378 if (pixels[i] != (unsigned short *) NULL)
379 pixels[i]=(unsigned short *) RelinquishMagickMemory(pixels[i]);
cristyb41ee102010-10-04 16:46:15 +0000380 pixels=(unsigned short **) RelinquishMagickMemory(pixels);
cristy3ed852e2009-09-05 21:47:34 +0000381 return(pixels);
382}
383
384static unsigned short **AcquirePixelThreadSet(const size_t columns,
385 const size_t channels)
386{
cristybb503372010-05-27 20:51:26 +0000387 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000388 i;
389
390 unsigned short
391 **pixels;
392
cristybb503372010-05-27 20:51:26 +0000393 size_t
cristy3ed852e2009-09-05 21:47:34 +0000394 number_threads;
395
cristy9357bdd2012-07-30 12:28:34 +0000396 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
cristyb41ee102010-10-04 16:46:15 +0000397 pixels=(unsigned short **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +0000398 sizeof(*pixels));
399 if (pixels == (unsigned short **) NULL)
400 return((unsigned short **) NULL);
401 (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
cristybb503372010-05-27 20:51:26 +0000402 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +0000403 {
404 pixels[i]=(unsigned short *) AcquireQuantumMemory(columns,channels*
405 sizeof(**pixels));
406 if (pixels[i] == (unsigned short *) NULL)
407 return(DestroyPixelThreadSet(pixels));
408 }
409 return(pixels);
410}
411
412static cmsHTRANSFORM *DestroyTransformThreadSet(cmsHTRANSFORM *transform)
413{
cristybb503372010-05-27 20:51:26 +0000414 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000415 i;
416
417 assert(transform != (cmsHTRANSFORM *) NULL);
cristyac245f82012-05-05 17:13:57 +0000418 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
cristy3ed852e2009-09-05 21:47:34 +0000419 if (transform[i] != (cmsHTRANSFORM) NULL)
420 cmsDeleteTransform(transform[i]);
cristyb41ee102010-10-04 16:46:15 +0000421 transform=(cmsHTRANSFORM *) RelinquishMagickMemory(transform);
cristy3ed852e2009-09-05 21:47:34 +0000422 return(transform);
423}
424
cristy7632f502010-06-18 13:26:31 +0000425static cmsHTRANSFORM *AcquireTransformThreadSet(Image *image,
cristyd09bcf92010-03-25 03:04:45 +0000426 const cmsHPROFILE source_profile,const cmsUInt32Number source_type,
cristy20a78b22010-04-05 01:22:12 +0000427 const cmsHPROFILE target_profile,const cmsUInt32Number target_type,
428 const int intent,const cmsUInt32Number flags)
cristy3ed852e2009-09-05 21:47:34 +0000429{
430 cmsHTRANSFORM
431 *transform;
432
cristybb503372010-05-27 20:51:26 +0000433 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000434 i;
435
cristybb503372010-05-27 20:51:26 +0000436 size_t
cristy3ed852e2009-09-05 21:47:34 +0000437 number_threads;
438
cristy9357bdd2012-07-30 12:28:34 +0000439 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
cristyb41ee102010-10-04 16:46:15 +0000440 transform=(cmsHTRANSFORM *) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +0000441 sizeof(*transform));
442 if (transform == (cmsHTRANSFORM *) NULL)
443 return((cmsHTRANSFORM *) NULL);
444 (void) ResetMagickMemory(transform,0,number_threads*sizeof(*transform));
cristybb503372010-05-27 20:51:26 +0000445 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +0000446 {
cristy9a00c142014-04-05 13:38:34 +0000447 transform[i]=cmsCreateTransformTHR((cmsContext) image,source_profile,
448 source_type,target_profile,target_type,intent,flags);
cristy3ed852e2009-09-05 21:47:34 +0000449 if (transform[i] == (cmsHTRANSFORM) NULL)
450 return(DestroyTransformThreadSet(transform));
451 }
452 return(transform);
453}
454#endif
455
cristyd0b7ee52011-10-21 11:19:02 +0000456#if defined(MAGICKCORE_LCMS_DELEGATE)
457#if defined(LCMS_VERSION) && (LCMS_VERSION >= 2000)
458static void CMSExceptionHandler(cmsContext context,cmsUInt32Number severity,
459 const char *message)
460{
461 CMSExceptionInfo
462 *cms_exception;
463
464 ExceptionInfo
465 *exception;
466
467 Image
468 *image;
469
470 cms_exception=(CMSExceptionInfo *) context;
471 image=cms_exception->image;
472 exception=cms_exception->exception;
473 if (image == (Image *) NULL)
474 {
475 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
cristyefe601c2013-01-05 17:51:12 +0000476 "UnableToTransformColorspace","`%s'","unknown context");
cristyd0b7ee52011-10-21 11:19:02 +0000477 return;
478 }
479 if (image->debug != MagickFalse)
480 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%u, %s",
481 severity,message != (char *) NULL ? message : "no message");
482 (void) ThrowMagickException(exception,GetMagickModule(),ImageWarning,
cristyefe601c2013-01-05 17:51:12 +0000483 "UnableToTransformColorspace","`%s'",image->filename);
cristyd0b7ee52011-10-21 11:19:02 +0000484}
485#else
486static int CMSExceptionHandler(int severity,const char *message)
487{
488 (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%d, %s",
489 severity,message != (char *) NULL ? message : "no message");
490 return(1);
491}
492#endif
493#endif
494
cristy35e92062014-08-26 14:13:16 +0000495static MagickBooleanType SetsRGBImageProfile(Image *image,
496 ExceptionInfo *exception)
497{
498 static unsigned char
499 sRGBProfile[] =
500 {
501 0x00, 0x00, 0x0c, 0x8c, 0x61, 0x72, 0x67, 0x6c, 0x02, 0x20, 0x00, 0x00,
502 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20,
503 0x07, 0xde, 0x00, 0x01, 0x00, 0x06, 0x00, 0x16, 0x00, 0x0f, 0x00, 0x3a,
504 0x61, 0x63, 0x73, 0x70, 0x4d, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00,
505 0x49, 0x45, 0x43, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00,
506 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6,
507 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x61, 0x72, 0x67, 0x6c,
508 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
509 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
510 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
511 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
512 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x99,
513 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0xec, 0x00, 0x00, 0x00, 0x67,
514 0x64, 0x6d, 0x6e, 0x64, 0x00, 0x00, 0x02, 0x54, 0x00, 0x00, 0x00, 0x70,
515 0x64, 0x6d, 0x64, 0x64, 0x00, 0x00, 0x02, 0xc4, 0x00, 0x00, 0x00, 0x88,
516 0x74, 0x65, 0x63, 0x68, 0x00, 0x00, 0x03, 0x4c, 0x00, 0x00, 0x00, 0x0c,
517 0x76, 0x75, 0x65, 0x64, 0x00, 0x00, 0x03, 0x58, 0x00, 0x00, 0x00, 0x67,
518 0x76, 0x69, 0x65, 0x77, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x24,
519 0x6c, 0x75, 0x6d, 0x69, 0x00, 0x00, 0x03, 0xe4, 0x00, 0x00, 0x00, 0x14,
520 0x6d, 0x65, 0x61, 0x73, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x24,
521 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x04, 0x1c, 0x00, 0x00, 0x00, 0x14,
522 0x62, 0x6b, 0x70, 0x74, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x14,
523 0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x44, 0x00, 0x00, 0x00, 0x14,
524 0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x58, 0x00, 0x00, 0x00, 0x14,
525 0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x04, 0x6c, 0x00, 0x00, 0x00, 0x14,
526 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
527 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
528 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x08, 0x0c,
529 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
530 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36,
531 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x28, 0x45, 0x71, 0x75, 0x69, 0x76,
532 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77, 0x77, 0x77,
533 0x2e, 0x73, 0x72, 0x67, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x31, 0x39,
534 0x39, 0x38, 0x20, 0x48, 0x50, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c,
535 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
536 0x00, 0x3f, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31,
537 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x28, 0x45, 0x71, 0x75,
538 0x69, 0x76, 0x61, 0x6c, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x77,
539 0x77, 0x77, 0x2e, 0x73, 0x72, 0x67, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x20,
540 0x31, 0x39, 0x39, 0x38, 0x20, 0x48, 0x50, 0x20, 0x70, 0x72, 0x6f, 0x66,
541 0x69, 0x6c, 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
542 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x72, 0x65, 0x61,
543 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x47, 0x72, 0x61, 0x65, 0x6d,
544 0x65, 0x20, 0x57, 0x2e, 0x20, 0x47, 0x69, 0x6c, 0x6c, 0x2e, 0x20, 0x52,
545 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f,
546 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
547 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x20, 0x4e, 0x6f, 0x20, 0x57,
548 0x61, 0x72, 0x72, 0x61, 0x6e, 0x74, 0x79, 0x2c, 0x20, 0x55, 0x73, 0x65,
549 0x20, 0x61, 0x74, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, 0x6f, 0x77, 0x6e,
550 0x20, 0x72, 0x69, 0x73, 0x6b, 0x2e, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63,
551 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20,
552 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69,
553 0x65, 0x63, 0x2e, 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
554 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20, 0x68, 0x74, 0x74,
555 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x65, 0x63, 0x2e,
556 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
557 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
558 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
559 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
560 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e,
561 0x49, 0x45, 0x43, 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e,
562 0x31, 0x20, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47,
563 0x42, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61,
564 0x63, 0x65, 0x20, 0x2d, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00,
565 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x49, 0x45, 0x43,
566 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x44,
567 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47, 0x42, 0x20, 0x63,
568 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20,
569 0x2d, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
570 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
571 0x00, 0x00, 0x00, 0x00, 0x73, 0x69, 0x67, 0x20, 0x00, 0x00, 0x00, 0x00,
572 0x43, 0x52, 0x54, 0x20, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00,
573 0x00, 0x00, 0x00, 0x0d, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36,
574 0x2d, 0x32, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
575 0x00, 0x00, 0x00, 0x0d, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36,
576 0x2d, 0x32, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
577 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
578 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
579 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
580 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
581 0x76, 0x69, 0x65, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xa4, 0x7c,
582 0x00, 0x14, 0x5f, 0x30, 0x00, 0x10, 0xce, 0x02, 0x00, 0x03, 0xed, 0xb2,
583 0x00, 0x04, 0x13, 0x0a, 0x00, 0x03, 0x5c, 0x67, 0x00, 0x00, 0x00, 0x01,
584 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x0a, 0x3d,
585 0x00, 0x50, 0x00, 0x00, 0x00, 0x57, 0x1e, 0xb8, 0x6d, 0x65, 0x61, 0x73,
586 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
587 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
588 0x00, 0x00, 0x02, 0x8f, 0x00, 0x00, 0x00, 0x02, 0x58, 0x59, 0x5a, 0x20,
589 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf3, 0x51, 0x00, 0x01, 0x00, 0x00,
590 0x00, 0x01, 0x16, 0xcc, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
591 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
592 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xa0,
593 0x00, 0x00, 0x38, 0xf5, 0x00, 0x00, 0x03, 0x90, 0x58, 0x59, 0x5a, 0x20,
594 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x97, 0x00, 0x00, 0xb7, 0x87,
595 0x00, 0x00, 0x18, 0xd9, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
596 0x00, 0x00, 0x24, 0x9f, 0x00, 0x00, 0x0f, 0x84, 0x00, 0x00, 0xb6, 0xc4,
597 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
598 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x14, 0x00, 0x19,
599 0x00, 0x1e, 0x00, 0x23, 0x00, 0x28, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x37,
600 0x00, 0x3b, 0x00, 0x40, 0x00, 0x45, 0x00, 0x4a, 0x00, 0x4f, 0x00, 0x54,
601 0x00, 0x59, 0x00, 0x5e, 0x00, 0x63, 0x00, 0x68, 0x00, 0x6d, 0x00, 0x72,
602 0x00, 0x77, 0x00, 0x7c, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8b, 0x00, 0x90,
603 0x00, 0x95, 0x00, 0x9a, 0x00, 0x9f, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xae,
604 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc1, 0x00, 0xc6, 0x00, 0xcb,
605 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xdb, 0x00, 0xe0, 0x00, 0xe5, 0x00, 0xeb,
606 0x00, 0xf0, 0x00, 0xf6, 0x00, 0xfb, 0x01, 0x01, 0x01, 0x07, 0x01, 0x0d,
607 0x01, 0x13, 0x01, 0x19, 0x01, 0x1f, 0x01, 0x25, 0x01, 0x2b, 0x01, 0x32,
608 0x01, 0x38, 0x01, 0x3e, 0x01, 0x45, 0x01, 0x4c, 0x01, 0x52, 0x01, 0x59,
609 0x01, 0x60, 0x01, 0x67, 0x01, 0x6e, 0x01, 0x75, 0x01, 0x7c, 0x01, 0x83,
610 0x01, 0x8b, 0x01, 0x92, 0x01, 0x9a, 0x01, 0xa1, 0x01, 0xa9, 0x01, 0xb1,
611 0x01, 0xb9, 0x01, 0xc1, 0x01, 0xc9, 0x01, 0xd1, 0x01, 0xd9, 0x01, 0xe1,
612 0x01, 0xe9, 0x01, 0xf2, 0x01, 0xfa, 0x02, 0x03, 0x02, 0x0c, 0x02, 0x14,
613 0x02, 0x1d, 0x02, 0x26, 0x02, 0x2f, 0x02, 0x38, 0x02, 0x41, 0x02, 0x4b,
614 0x02, 0x54, 0x02, 0x5d, 0x02, 0x67, 0x02, 0x71, 0x02, 0x7a, 0x02, 0x84,
615 0x02, 0x8e, 0x02, 0x98, 0x02, 0xa2, 0x02, 0xac, 0x02, 0xb6, 0x02, 0xc1,
616 0x02, 0xcb, 0x02, 0xd5, 0x02, 0xe0, 0x02, 0xeb, 0x02, 0xf5, 0x03, 0x00,
617 0x03, 0x0b, 0x03, 0x16, 0x03, 0x21, 0x03, 0x2d, 0x03, 0x38, 0x03, 0x43,
618 0x03, 0x4f, 0x03, 0x5a, 0x03, 0x66, 0x03, 0x72, 0x03, 0x7e, 0x03, 0x8a,
619 0x03, 0x96, 0x03, 0xa2, 0x03, 0xae, 0x03, 0xba, 0x03, 0xc7, 0x03, 0xd3,
620 0x03, 0xe0, 0x03, 0xec, 0x03, 0xf9, 0x04, 0x06, 0x04, 0x13, 0x04, 0x20,
621 0x04, 0x2d, 0x04, 0x3b, 0x04, 0x48, 0x04, 0x55, 0x04, 0x63, 0x04, 0x71,
622 0x04, 0x7e, 0x04, 0x8c, 0x04, 0x9a, 0x04, 0xa8, 0x04, 0xb6, 0x04, 0xc4,
623 0x04, 0xd3, 0x04, 0xe1, 0x04, 0xf0, 0x04, 0xfe, 0x05, 0x0d, 0x05, 0x1c,
624 0x05, 0x2b, 0x05, 0x3a, 0x05, 0x49, 0x05, 0x58, 0x05, 0x67, 0x05, 0x77,
625 0x05, 0x86, 0x05, 0x96, 0x05, 0xa6, 0x05, 0xb5, 0x05, 0xc5, 0x05, 0xd5,
626 0x05, 0xe5, 0x05, 0xf6, 0x06, 0x06, 0x06, 0x16, 0x06, 0x27, 0x06, 0x37,
627 0x06, 0x48, 0x06, 0x59, 0x06, 0x6a, 0x06, 0x7b, 0x06, 0x8c, 0x06, 0x9d,
628 0x06, 0xaf, 0x06, 0xc0, 0x06, 0xd1, 0x06, 0xe3, 0x06, 0xf5, 0x07, 0x07,
629 0x07, 0x19, 0x07, 0x2b, 0x07, 0x3d, 0x07, 0x4f, 0x07, 0x61, 0x07, 0x74,
630 0x07, 0x86, 0x07, 0x99, 0x07, 0xac, 0x07, 0xbf, 0x07, 0xd2, 0x07, 0xe5,
631 0x07, 0xf8, 0x08, 0x0b, 0x08, 0x1f, 0x08, 0x32, 0x08, 0x46, 0x08, 0x5a,
632 0x08, 0x6e, 0x08, 0x82, 0x08, 0x96, 0x08, 0xaa, 0x08, 0xbe, 0x08, 0xd2,
633 0x08, 0xe7, 0x08, 0xfb, 0x09, 0x10, 0x09, 0x25, 0x09, 0x3a, 0x09, 0x4f,
634 0x09, 0x64, 0x09, 0x79, 0x09, 0x8f, 0x09, 0xa4, 0x09, 0xba, 0x09, 0xcf,
635 0x09, 0xe5, 0x09, 0xfb, 0x0a, 0x11, 0x0a, 0x27, 0x0a, 0x3d, 0x0a, 0x54,
636 0x0a, 0x6a, 0x0a, 0x81, 0x0a, 0x98, 0x0a, 0xae, 0x0a, 0xc5, 0x0a, 0xdc,
637 0x0a, 0xf3, 0x0b, 0x0b, 0x0b, 0x22, 0x0b, 0x39, 0x0b, 0x51, 0x0b, 0x69,
638 0x0b, 0x80, 0x0b, 0x98, 0x0b, 0xb0, 0x0b, 0xc8, 0x0b, 0xe1, 0x0b, 0xf9,
639 0x0c, 0x12, 0x0c, 0x2a, 0x0c, 0x43, 0x0c, 0x5c, 0x0c, 0x75, 0x0c, 0x8e,
640 0x0c, 0xa7, 0x0c, 0xc0, 0x0c, 0xd9, 0x0c, 0xf3, 0x0d, 0x0d, 0x0d, 0x26,
641 0x0d, 0x40, 0x0d, 0x5a, 0x0d, 0x74, 0x0d, 0x8e, 0x0d, 0xa9, 0x0d, 0xc3,
642 0x0d, 0xde, 0x0d, 0xf8, 0x0e, 0x13, 0x0e, 0x2e, 0x0e, 0x49, 0x0e, 0x64,
643 0x0e, 0x7f, 0x0e, 0x9b, 0x0e, 0xb6, 0x0e, 0xd2, 0x0e, 0xee, 0x0f, 0x09,
644 0x0f, 0x25, 0x0f, 0x41, 0x0f, 0x5e, 0x0f, 0x7a, 0x0f, 0x96, 0x0f, 0xb3,
645 0x0f, 0xcf, 0x0f, 0xec, 0x10, 0x09, 0x10, 0x26, 0x10, 0x43, 0x10, 0x61,
646 0x10, 0x7e, 0x10, 0x9b, 0x10, 0xb9, 0x10, 0xd7, 0x10, 0xf5, 0x11, 0x13,
647 0x11, 0x31, 0x11, 0x4f, 0x11, 0x6d, 0x11, 0x8c, 0x11, 0xaa, 0x11, 0xc9,
648 0x11, 0xe8, 0x12, 0x07, 0x12, 0x26, 0x12, 0x45, 0x12, 0x64, 0x12, 0x84,
649 0x12, 0xa3, 0x12, 0xc3, 0x12, 0xe3, 0x13, 0x03, 0x13, 0x23, 0x13, 0x43,
650 0x13, 0x63, 0x13, 0x83, 0x13, 0xa4, 0x13, 0xc5, 0x13, 0xe5, 0x14, 0x06,
651 0x14, 0x27, 0x14, 0x49, 0x14, 0x6a, 0x14, 0x8b, 0x14, 0xad, 0x14, 0xce,
652 0x14, 0xf0, 0x15, 0x12, 0x15, 0x34, 0x15, 0x56, 0x15, 0x78, 0x15, 0x9b,
653 0x15, 0xbd, 0x15, 0xe0, 0x16, 0x03, 0x16, 0x26, 0x16, 0x49, 0x16, 0x6c,
654 0x16, 0x8f, 0x16, 0xb2, 0x16, 0xd6, 0x16, 0xfa, 0x17, 0x1d, 0x17, 0x41,
655 0x17, 0x65, 0x17, 0x89, 0x17, 0xae, 0x17, 0xd2, 0x17, 0xf7, 0x18, 0x1b,
656 0x18, 0x40, 0x18, 0x65, 0x18, 0x8a, 0x18, 0xaf, 0x18, 0xd5, 0x18, 0xfa,
657 0x19, 0x20, 0x19, 0x45, 0x19, 0x6b, 0x19, 0x91, 0x19, 0xb7, 0x19, 0xdd,
658 0x1a, 0x04, 0x1a, 0x2a, 0x1a, 0x51, 0x1a, 0x77, 0x1a, 0x9e, 0x1a, 0xc5,
659 0x1a, 0xec, 0x1b, 0x14, 0x1b, 0x3b, 0x1b, 0x63, 0x1b, 0x8a, 0x1b, 0xb2,
660 0x1b, 0xda, 0x1c, 0x02, 0x1c, 0x2a, 0x1c, 0x52, 0x1c, 0x7b, 0x1c, 0xa3,
661 0x1c, 0xcc, 0x1c, 0xf5, 0x1d, 0x1e, 0x1d, 0x47, 0x1d, 0x70, 0x1d, 0x99,
662 0x1d, 0xc3, 0x1d, 0xec, 0x1e, 0x16, 0x1e, 0x40, 0x1e, 0x6a, 0x1e, 0x94,
663 0x1e, 0xbe, 0x1e, 0xe9, 0x1f, 0x13, 0x1f, 0x3e, 0x1f, 0x69, 0x1f, 0x94,
664 0x1f, 0xbf, 0x1f, 0xea, 0x20, 0x15, 0x20, 0x41, 0x20, 0x6c, 0x20, 0x98,
665 0x20, 0xc4, 0x20, 0xf0, 0x21, 0x1c, 0x21, 0x48, 0x21, 0x75, 0x21, 0xa1,
666 0x21, 0xce, 0x21, 0xfb, 0x22, 0x27, 0x22, 0x55, 0x22, 0x82, 0x22, 0xaf,
667 0x22, 0xdd, 0x23, 0x0a, 0x23, 0x38, 0x23, 0x66, 0x23, 0x94, 0x23, 0xc2,
668 0x23, 0xf0, 0x24, 0x1f, 0x24, 0x4d, 0x24, 0x7c, 0x24, 0xab, 0x24, 0xda,
669 0x25, 0x09, 0x25, 0x38, 0x25, 0x68, 0x25, 0x97, 0x25, 0xc7, 0x25, 0xf7,
670 0x26, 0x27, 0x26, 0x57, 0x26, 0x87, 0x26, 0xb7, 0x26, 0xe8, 0x27, 0x18,
671 0x27, 0x49, 0x27, 0x7a, 0x27, 0xab, 0x27, 0xdc, 0x28, 0x0d, 0x28, 0x3f,
672 0x28, 0x71, 0x28, 0xa2, 0x28, 0xd4, 0x29, 0x06, 0x29, 0x38, 0x29, 0x6b,
673 0x29, 0x9d, 0x29, 0xd0, 0x2a, 0x02, 0x2a, 0x35, 0x2a, 0x68, 0x2a, 0x9b,
674 0x2a, 0xcf, 0x2b, 0x02, 0x2b, 0x36, 0x2b, 0x69, 0x2b, 0x9d, 0x2b, 0xd1,
675 0x2c, 0x05, 0x2c, 0x39, 0x2c, 0x6e, 0x2c, 0xa2, 0x2c, 0xd7, 0x2d, 0x0c,
676 0x2d, 0x41, 0x2d, 0x76, 0x2d, 0xab, 0x2d, 0xe1, 0x2e, 0x16, 0x2e, 0x4c,
677 0x2e, 0x82, 0x2e, 0xb7, 0x2e, 0xee, 0x2f, 0x24, 0x2f, 0x5a, 0x2f, 0x91,
678 0x2f, 0xc7, 0x2f, 0xfe, 0x30, 0x35, 0x30, 0x6c, 0x30, 0xa4, 0x30, 0xdb,
679 0x31, 0x12, 0x31, 0x4a, 0x31, 0x82, 0x31, 0xba, 0x31, 0xf2, 0x32, 0x2a,
680 0x32, 0x63, 0x32, 0x9b, 0x32, 0xd4, 0x33, 0x0d, 0x33, 0x46, 0x33, 0x7f,
681 0x33, 0xb8, 0x33, 0xf1, 0x34, 0x2b, 0x34, 0x65, 0x34, 0x9e, 0x34, 0xd8,
682 0x35, 0x13, 0x35, 0x4d, 0x35, 0x87, 0x35, 0xc2, 0x35, 0xfd, 0x36, 0x37,
683 0x36, 0x72, 0x36, 0xae, 0x36, 0xe9, 0x37, 0x24, 0x37, 0x60, 0x37, 0x9c,
684 0x37, 0xd7, 0x38, 0x14, 0x38, 0x50, 0x38, 0x8c, 0x38, 0xc8, 0x39, 0x05,
685 0x39, 0x42, 0x39, 0x7f, 0x39, 0xbc, 0x39, 0xf9, 0x3a, 0x36, 0x3a, 0x74,
686 0x3a, 0xb2, 0x3a, 0xef, 0x3b, 0x2d, 0x3b, 0x6b, 0x3b, 0xaa, 0x3b, 0xe8,
687 0x3c, 0x27, 0x3c, 0x65, 0x3c, 0xa4, 0x3c, 0xe3, 0x3d, 0x22, 0x3d, 0x61,
688 0x3d, 0xa1, 0x3d, 0xe0, 0x3e, 0x20, 0x3e, 0x60, 0x3e, 0xa0, 0x3e, 0xe0,
689 0x3f, 0x21, 0x3f, 0x61, 0x3f, 0xa2, 0x3f, 0xe2, 0x40, 0x23, 0x40, 0x64,
690 0x40, 0xa6, 0x40, 0xe7, 0x41, 0x29, 0x41, 0x6a, 0x41, 0xac, 0x41, 0xee,
691 0x42, 0x30, 0x42, 0x72, 0x42, 0xb5, 0x42, 0xf7, 0x43, 0x3a, 0x43, 0x7d,
692 0x43, 0xc0, 0x44, 0x03, 0x44, 0x47, 0x44, 0x8a, 0x44, 0xce, 0x45, 0x12,
693 0x45, 0x55, 0x45, 0x9a, 0x45, 0xde, 0x46, 0x22, 0x46, 0x67, 0x46, 0xab,
694 0x46, 0xf0, 0x47, 0x35, 0x47, 0x7b, 0x47, 0xc0, 0x48, 0x05, 0x48, 0x4b,
695 0x48, 0x91, 0x48, 0xd7, 0x49, 0x1d, 0x49, 0x63, 0x49, 0xa9, 0x49, 0xf0,
696 0x4a, 0x37, 0x4a, 0x7d, 0x4a, 0xc4, 0x4b, 0x0c, 0x4b, 0x53, 0x4b, 0x9a,
697 0x4b, 0xe2, 0x4c, 0x2a, 0x4c, 0x72, 0x4c, 0xba, 0x4d, 0x02, 0x4d, 0x4a,
698 0x4d, 0x93, 0x4d, 0xdc, 0x4e, 0x25, 0x4e, 0x6e, 0x4e, 0xb7, 0x4f, 0x00,
699 0x4f, 0x49, 0x4f, 0x93, 0x4f, 0xdd, 0x50, 0x27, 0x50, 0x71, 0x50, 0xbb,
700 0x51, 0x06, 0x51, 0x50, 0x51, 0x9b, 0x51, 0xe6, 0x52, 0x31, 0x52, 0x7c,
701 0x52, 0xc7, 0x53, 0x13, 0x53, 0x5f, 0x53, 0xaa, 0x53, 0xf6, 0x54, 0x42,
702 0x54, 0x8f, 0x54, 0xdb, 0x55, 0x28, 0x55, 0x75, 0x55, 0xc2, 0x56, 0x0f,
703 0x56, 0x5c, 0x56, 0xa9, 0x56, 0xf7, 0x57, 0x44, 0x57, 0x92, 0x57, 0xe0,
704 0x58, 0x2f, 0x58, 0x7d, 0x58, 0xcb, 0x59, 0x1a, 0x59, 0x69, 0x59, 0xb8,
705 0x5a, 0x07, 0x5a, 0x56, 0x5a, 0xa6, 0x5a, 0xf5, 0x5b, 0x45, 0x5b, 0x95,
706 0x5b, 0xe5, 0x5c, 0x35, 0x5c, 0x86, 0x5c, 0xd6, 0x5d, 0x27, 0x5d, 0x78,
707 0x5d, 0xc9, 0x5e, 0x1a, 0x5e, 0x6c, 0x5e, 0xbd, 0x5f, 0x0f, 0x5f, 0x61,
708 0x5f, 0xb3, 0x60, 0x05, 0x60, 0x57, 0x60, 0xaa, 0x60, 0xfc, 0x61, 0x4f,
709 0x61, 0xa2, 0x61, 0xf5, 0x62, 0x49, 0x62, 0x9c, 0x62, 0xf0, 0x63, 0x43,
710 0x63, 0x97, 0x63, 0xeb, 0x64, 0x40, 0x64, 0x94, 0x64, 0xe9, 0x65, 0x3d,
711 0x65, 0x92, 0x65, 0xe7, 0x66, 0x3d, 0x66, 0x92, 0x66, 0xe8, 0x67, 0x3d,
712 0x67, 0x93, 0x67, 0xe9, 0x68, 0x3f, 0x68, 0x96, 0x68, 0xec, 0x69, 0x43,
713 0x69, 0x9a, 0x69, 0xf1, 0x6a, 0x48, 0x6a, 0x9f, 0x6a, 0xf7, 0x6b, 0x4f,
714 0x6b, 0xa7, 0x6b, 0xff, 0x6c, 0x57, 0x6c, 0xaf, 0x6d, 0x08, 0x6d, 0x60,
715 0x6d, 0xb9, 0x6e, 0x12, 0x6e, 0x6b, 0x6e, 0xc4, 0x6f, 0x1e, 0x6f, 0x78,
716 0x6f, 0xd1, 0x70, 0x2b, 0x70, 0x86, 0x70, 0xe0, 0x71, 0x3a, 0x71, 0x95,
717 0x71, 0xf0, 0x72, 0x4b, 0x72, 0xa6, 0x73, 0x01, 0x73, 0x5d, 0x73, 0xb8,
718 0x74, 0x14, 0x74, 0x70, 0x74, 0xcc, 0x75, 0x28, 0x75, 0x85, 0x75, 0xe1,
719 0x76, 0x3e, 0x76, 0x9b, 0x76, 0xf8, 0x77, 0x56, 0x77, 0xb3, 0x78, 0x11,
720 0x78, 0x6e, 0x78, 0xcc, 0x79, 0x2a, 0x79, 0x89, 0x79, 0xe7, 0x7a, 0x46,
721 0x7a, 0xa5, 0x7b, 0x04, 0x7b, 0x63, 0x7b, 0xc2, 0x7c, 0x21, 0x7c, 0x81,
722 0x7c, 0xe1, 0x7d, 0x41, 0x7d, 0xa1, 0x7e, 0x01, 0x7e, 0x62, 0x7e, 0xc2,
723 0x7f, 0x23, 0x7f, 0x84, 0x7f, 0xe5, 0x80, 0x47, 0x80, 0xa8, 0x81, 0x0a,
724 0x81, 0x6b, 0x81, 0xcd, 0x82, 0x30, 0x82, 0x92, 0x82, 0xf4, 0x83, 0x57,
725 0x83, 0xba, 0x84, 0x1d, 0x84, 0x80, 0x84, 0xe3, 0x85, 0x47, 0x85, 0xab,
726 0x86, 0x0e, 0x86, 0x72, 0x86, 0xd7, 0x87, 0x3b, 0x87, 0x9f, 0x88, 0x04,
727 0x88, 0x69, 0x88, 0xce, 0x89, 0x33, 0x89, 0x99, 0x89, 0xfe, 0x8a, 0x64,
728 0x8a, 0xca, 0x8b, 0x30, 0x8b, 0x96, 0x8b, 0xfc, 0x8c, 0x63, 0x8c, 0xca,
729 0x8d, 0x31, 0x8d, 0x98, 0x8d, 0xff, 0x8e, 0x66, 0x8e, 0xce, 0x8f, 0x36,
730 0x8f, 0x9e, 0x90, 0x06, 0x90, 0x6e, 0x90, 0xd6, 0x91, 0x3f, 0x91, 0xa8,
731 0x92, 0x11, 0x92, 0x7a, 0x92, 0xe3, 0x93, 0x4d, 0x93, 0xb6, 0x94, 0x20,
732 0x94, 0x8a, 0x94, 0xf4, 0x95, 0x5f, 0x95, 0xc9, 0x96, 0x34, 0x96, 0x9f,
733 0x97, 0x0a, 0x97, 0x75, 0x97, 0xe0, 0x98, 0x4c, 0x98, 0xb8, 0x99, 0x24,
734 0x99, 0x90, 0x99, 0xfc, 0x9a, 0x68, 0x9a, 0xd5, 0x9b, 0x42, 0x9b, 0xaf,
735 0x9c, 0x1c, 0x9c, 0x89, 0x9c, 0xf7, 0x9d, 0x64, 0x9d, 0xd2, 0x9e, 0x40,
736 0x9e, 0xae, 0x9f, 0x1d, 0x9f, 0x8b, 0x9f, 0xfa, 0xa0, 0x69, 0xa0, 0xd8,
737 0xa1, 0x47, 0xa1, 0xb6, 0xa2, 0x26, 0xa2, 0x96, 0xa3, 0x06, 0xa3, 0x76,
738 0xa3, 0xe6, 0xa4, 0x56, 0xa4, 0xc7, 0xa5, 0x38, 0xa5, 0xa9, 0xa6, 0x1a,
739 0xa6, 0x8b, 0xa6, 0xfd, 0xa7, 0x6e, 0xa7, 0xe0, 0xa8, 0x52, 0xa8, 0xc4,
740 0xa9, 0x37, 0xa9, 0xa9, 0xaa, 0x1c, 0xaa, 0x8f, 0xab, 0x02, 0xab, 0x75,
741 0xab, 0xe9, 0xac, 0x5c, 0xac, 0xd0, 0xad, 0x44, 0xad, 0xb8, 0xae, 0x2d,
742 0xae, 0xa1, 0xaf, 0x16, 0xaf, 0x8b, 0xb0, 0x00, 0xb0, 0x75, 0xb0, 0xea,
743 0xb1, 0x60, 0xb1, 0xd6, 0xb2, 0x4b, 0xb2, 0xc2, 0xb3, 0x38, 0xb3, 0xae,
744 0xb4, 0x25, 0xb4, 0x9c, 0xb5, 0x13, 0xb5, 0x8a, 0xb6, 0x01, 0xb6, 0x79,
745 0xb6, 0xf0, 0xb7, 0x68, 0xb7, 0xe0, 0xb8, 0x59, 0xb8, 0xd1, 0xb9, 0x4a,
746 0xb9, 0xc2, 0xba, 0x3b, 0xba, 0xb5, 0xbb, 0x2e, 0xbb, 0xa7, 0xbc, 0x21,
747 0xbc, 0x9b, 0xbd, 0x15, 0xbd, 0x8f, 0xbe, 0x0a, 0xbe, 0x84, 0xbe, 0xff,
748 0xbf, 0x7a, 0xbf, 0xf5, 0xc0, 0x70, 0xc0, 0xec, 0xc1, 0x67, 0xc1, 0xe3,
749 0xc2, 0x5f, 0xc2, 0xdb, 0xc3, 0x58, 0xc3, 0xd4, 0xc4, 0x51, 0xc4, 0xce,
750 0xc5, 0x4b, 0xc5, 0xc8, 0xc6, 0x46, 0xc6, 0xc3, 0xc7, 0x41, 0xc7, 0xbf,
751 0xc8, 0x3d, 0xc8, 0xbc, 0xc9, 0x3a, 0xc9, 0xb9, 0xca, 0x38, 0xca, 0xb7,
752 0xcb, 0x36, 0xcb, 0xb6, 0xcc, 0x35, 0xcc, 0xb5, 0xcd, 0x35, 0xcd, 0xb5,
753 0xce, 0x36, 0xce, 0xb6, 0xcf, 0x37, 0xcf, 0xb8, 0xd0, 0x39, 0xd0, 0xba,
754 0xd1, 0x3c, 0xd1, 0xbe, 0xd2, 0x3f, 0xd2, 0xc1, 0xd3, 0x44, 0xd3, 0xc6,
755 0xd4, 0x49, 0xd4, 0xcb, 0xd5, 0x4e, 0xd5, 0xd1, 0xd6, 0x55, 0xd6, 0xd8,
756 0xd7, 0x5c, 0xd7, 0xe0, 0xd8, 0x64, 0xd8, 0xe8, 0xd9, 0x6c, 0xd9, 0xf1,
757 0xda, 0x76, 0xda, 0xfb, 0xdb, 0x80, 0xdc, 0x05, 0xdc, 0x8a, 0xdd, 0x10,
758 0xdd, 0x96, 0xde, 0x1c, 0xde, 0xa2, 0xdf, 0x29, 0xdf, 0xaf, 0xe0, 0x36,
759 0xe0, 0xbd, 0xe1, 0x44, 0xe1, 0xcc, 0xe2, 0x53, 0xe2, 0xdb, 0xe3, 0x63,
760 0xe3, 0xeb, 0xe4, 0x73, 0xe4, 0xfc, 0xe5, 0x84, 0xe6, 0x0d, 0xe6, 0x96,
761 0xe7, 0x1f, 0xe7, 0xa9, 0xe8, 0x32, 0xe8, 0xbc, 0xe9, 0x46, 0xe9, 0xd0,
762 0xea, 0x5b, 0xea, 0xe5, 0xeb, 0x70, 0xeb, 0xfb, 0xec, 0x86, 0xed, 0x11,
763 0xed, 0x9c, 0xee, 0x28, 0xee, 0xb4, 0xef, 0x40, 0xef, 0xcc, 0xf0, 0x58,
764 0xf0, 0xe5, 0xf1, 0x72, 0xf1, 0xff, 0xf2, 0x8c, 0xf3, 0x19, 0xf3, 0xa7,
765 0xf4, 0x34, 0xf4, 0xc2, 0xf5, 0x50, 0xf5, 0xde, 0xf6, 0x6d, 0xf6, 0xfb,
766 0xf7, 0x8a, 0xf8, 0x19, 0xf8, 0xa8, 0xf9, 0x38, 0xf9, 0xc7, 0xfa, 0x57,
767 0xfa, 0xe7, 0xfb, 0x77, 0xfc, 0x07, 0xfc, 0x98, 0xfd, 0x29, 0xfd, 0xba,
768 0xfe, 0x4b, 0xfe, 0xdc, 0xff, 0x6d, 0xff, 0xff
769 };
770
771 StringInfo
772 *profile;
773
774 MagickBooleanType
775 status;
776
777 assert(image != (Image *) NULL);
778 assert(image->signature == MagickSignature);
779 if (GetImageProfile(image,"icm") != (const StringInfo *) NULL)
780 return(MagickFalse);
781 profile=AcquireStringInfo(sizeof(sRGBProfile));
782 SetStringInfoDatum(profile,sRGBProfile);
783 status=SetImageProfile(image,"icm",profile,exception);
784 profile=DestroyStringInfo(profile);
785 return(status);
786}
787
cristy3ed852e2009-09-05 21:47:34 +0000788MagickExport MagickBooleanType ProfileImage(Image *image,const char *name,
cristy092d71c2011-10-14 18:01:29 +0000789 const void *datum,const size_t length,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000790{
791#define ProfileImageTag "Profile/Image"
792#define ThrowProfileException(severity,tag,context) \
793{ \
cristye1c67a82010-04-10 02:20:57 +0000794 if (source_profile != (cmsHPROFILE) NULL) \
795 (void) cmsCloseProfile(source_profile); \
cristya9eb12b2010-04-08 01:19:04 +0000796 if (target_profile != (cmsHPROFILE) NULL) \
797 (void) cmsCloseProfile(target_profile); \
cristy3ed852e2009-09-05 21:47:34 +0000798 ThrowBinaryException(severity,tag,context); \
799}
800
801 MagickBooleanType
802 status;
803
804 StringInfo
805 *profile;
806
807 assert(image != (Image *) NULL);
808 assert(image->signature == MagickSignature);
809 if (image->debug != MagickFalse)
810 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
811 assert(name != (const char *) NULL);
812 if ((datum == (const void *) NULL) || (length == 0))
813 {
814 char
cristy53b6cc32014-06-25 13:13:33 +0000815 *next;
cristy3ed852e2009-09-05 21:47:34 +0000816
817 /*
818 Delete image profile(s).
819 */
cristy3ed852e2009-09-05 21:47:34 +0000820 ResetImageProfileIterator(image);
cristy53b6cc32014-06-25 13:13:33 +0000821 for (next=GetNextImageProfile(image); next != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +0000822 {
cristy53b6cc32014-06-25 13:13:33 +0000823 if (IsOptionMember(next,name) != MagickFalse)
824 {
dirk15518fa2014-08-22 16:52:59 +0000825 (void) DeleteImageProfile(image,next);
cristy53b6cc32014-06-25 13:13:33 +0000826 ResetImageProfileIterator(image);
827 }
cristy948fbb62014-07-01 22:11:27 +0000828 next=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +0000829 }
cristy3ed852e2009-09-05 21:47:34 +0000830 return(MagickTrue);
831 }
832 /*
833 Add a ICC, IPTC, or generic profile to the image.
834 */
cristy902d15b2010-04-07 19:44:01 +0000835 status=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +0000836 profile=AcquireStringInfo((size_t) length);
837 SetStringInfoDatum(profile,(unsigned char *) datum);
cristy902d15b2010-04-07 19:44:01 +0000838 if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
cristyd15e6592011-10-15 00:13:06 +0000839 status=SetImageProfile(image,name,profile,exception);
cristy902d15b2010-04-07 19:44:01 +0000840 else
cristy3ed852e2009-09-05 21:47:34 +0000841 {
842 const StringInfo
843 *icc_profile;
844
845 icc_profile=GetImageProfile(image,"icc");
846 if ((icc_profile != (const StringInfo *) NULL) &&
847 (CompareStringInfo(icc_profile,profile) == 0))
848 {
849 const char
850 *value;
851
cristyd15e6592011-10-15 00:13:06 +0000852 value=GetImageProperty(image,"exif:ColorSpace",exception);
cristyd479bca2012-06-04 14:26:56 +0000853 (void) value;
cristy3ed852e2009-09-05 21:47:34 +0000854 if (LocaleCompare(value,"1") != 0)
cristyd15e6592011-10-15 00:13:06 +0000855 (void) SetsRGBImageProfile(image,exception);
856 value=GetImageProperty(image,"exif:InteroperabilityIndex",exception);
cristy3ed852e2009-09-05 21:47:34 +0000857 if (LocaleCompare(value,"R98.") != 0)
cristyd15e6592011-10-15 00:13:06 +0000858 (void) SetsRGBImageProfile(image,exception);
cristy35e92062014-08-26 14:13:16 +0000859 /* Future.
cristyd15e6592011-10-15 00:13:06 +0000860 value=GetImageProperty(image,"exif:InteroperabilityIndex",exception);
cristy3ed852e2009-09-05 21:47:34 +0000861 if (LocaleCompare(value,"R03.") != 0)
cristyd15e6592011-10-15 00:13:06 +0000862 (void) SetAdobeRGB1998ImageProfile(image,exception);
cristy751980d2012-06-03 23:18:35 +0000863 */
cristy3ed852e2009-09-05 21:47:34 +0000864 icc_profile=GetImageProfile(image,"icc");
865 }
866 if ((icc_profile != (const StringInfo *) NULL) &&
867 (CompareStringInfo(icc_profile,profile) == 0))
868 {
869 profile=DestroyStringInfo(profile);
870 return(MagickTrue);
871 }
872#if !defined(MAGICKCORE_LCMS_DELEGATE)
cristy09b22022011-10-26 14:11:30 +0000873 (void) ThrowMagickException(exception,GetMagickModule(),
cristye2c81ad2011-08-20 22:54:14 +0000874 MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn",
anthonye5b39652012-04-21 05:37:29 +0000875 "'%s' (LCMS)",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000876#else
cristy8a20a2d2010-04-08 14:16:27 +0000877 {
cristy8a20a2d2010-04-08 14:16:27 +0000878 cmsHPROFILE
cristye1c67a82010-04-10 02:20:57 +0000879 source_profile;
cristy3ed852e2009-09-05 21:47:34 +0000880
cristyd0b7ee52011-10-21 11:19:02 +0000881 CMSExceptionInfo
882 cms_exception;
883
cristy8a20a2d2010-04-08 14:16:27 +0000884 /*
885 Transform pixel colors as defined by the color profiles.
886 */
cristyd0b7ee52011-10-21 11:19:02 +0000887 cmsSetLogErrorHandler(CMSExceptionHandler);
888 cms_exception.image=image;
889 cms_exception.exception=exception;
cristy5f95f4f2011-10-23 01:01:01 +0000890 (void) cms_exception;
cristy9a00c142014-04-05 13:38:34 +0000891 source_profile=cmsOpenProfileFromMemTHR((cmsContext) &cms_exception,
cristy71203402010-06-18 13:12:03 +0000892 GetStringInfoDatum(profile),(cmsUInt32Number)
893 GetStringInfoLength(profile));
cristy8a20a2d2010-04-08 14:16:27 +0000894 if (source_profile == (cmsHPROFILE) NULL)
895 ThrowBinaryException(ResourceLimitError,
896 "ColorspaceColorProfileMismatch",name);
cristyfbaaf3e2010-04-08 14:53:43 +0000897 if ((cmsGetDeviceClass(source_profile) != cmsSigLinkClass) &&
cristye1c67a82010-04-10 02:20:57 +0000898 (icc_profile == (StringInfo *) NULL))
cristyd15e6592011-10-15 00:13:06 +0000899 status=SetImageProfile(image,name,profile,exception);
cristye1c67a82010-04-10 02:20:57 +0000900 else
cristy3ed852e2009-09-05 21:47:34 +0000901 {
cristye1c67a82010-04-10 02:20:57 +0000902 CacheView
903 *image_view;
cristy8a20a2d2010-04-08 14:16:27 +0000904
cristye1c67a82010-04-10 02:20:57 +0000905 ColorspaceType
906 source_colorspace,
907 target_colorspace;
cristy8a20a2d2010-04-08 14:16:27 +0000908
cristye1c67a82010-04-10 02:20:57 +0000909 cmsColorSpaceSignature
910 signature;
cristy8a20a2d2010-04-08 14:16:27 +0000911
cristye1c67a82010-04-10 02:20:57 +0000912 cmsHPROFILE
913 target_profile;
cristy8a20a2d2010-04-08 14:16:27 +0000914
cristye1c67a82010-04-10 02:20:57 +0000915 cmsHTRANSFORM
916 *restrict transform;
cristy8a20a2d2010-04-08 14:16:27 +0000917
cristye1c67a82010-04-10 02:20:57 +0000918 cmsUInt32Number
919 flags,
920 source_type,
921 target_type;
922
cristye1c67a82010-04-10 02:20:57 +0000923 int
924 intent;
925
cristye1c67a82010-04-10 02:20:57 +0000926 MagickBooleanType
927 status;
928
cristy5f959472010-05-27 22:19:46 +0000929 MagickOffsetType
930 progress;
931
cristye1c67a82010-04-10 02:20:57 +0000932 size_t
cristye1c67a82010-04-10 02:20:57 +0000933 source_channels,
934 target_channels;
935
cristy5f959472010-05-27 22:19:46 +0000936 ssize_t
937 y;
938
cristye1c67a82010-04-10 02:20:57 +0000939 unsigned short
940 **restrict source_pixels,
941 **restrict target_pixels;
942
cristye1c67a82010-04-10 02:20:57 +0000943 target_profile=(cmsHPROFILE) NULL;
944 if (icc_profile != (StringInfo *) NULL)
945 {
946 target_profile=source_profile;
cristy9a00c142014-04-05 13:38:34 +0000947 source_profile=cmsOpenProfileFromMemTHR((cmsContext)
948 &cms_exception,GetStringInfoDatum(icc_profile),
949 (cmsUInt32Number) GetStringInfoLength(icc_profile));
cristye1c67a82010-04-10 02:20:57 +0000950 if (source_profile == (cmsHPROFILE) NULL)
951 ThrowProfileException(ResourceLimitError,
952 "ColorspaceColorProfileMismatch",name);
953 }
954 switch (cmsGetColorSpace(source_profile))
cristy8a20a2d2010-04-08 14:16:27 +0000955 {
cristye1c67a82010-04-10 02:20:57 +0000956 case cmsSigCmykData:
957 {
958 source_colorspace=CMYKColorspace;
959 source_type=(cmsUInt32Number) TYPE_CMYK_16;
960 source_channels=4;
961 break;
962 }
963 case cmsSigGrayData:
964 {
965 source_colorspace=GRAYColorspace;
966 source_type=(cmsUInt32Number) TYPE_GRAY_16;
967 source_channels=1;
968 break;
969 }
970 case cmsSigLabData:
971 {
972 source_colorspace=LabColorspace;
973 source_type=(cmsUInt32Number) TYPE_Lab_16;
974 source_channels=3;
975 break;
976 }
977 case cmsSigLuvData:
978 {
979 source_colorspace=YUVColorspace;
980 source_type=(cmsUInt32Number) TYPE_YUV_16;
981 source_channels=3;
982 break;
983 }
984 case cmsSigRgbData:
985 {
cristyc511e882012-04-16 21:11:14 +0000986 source_colorspace=sRGBColorspace;
cristye1c67a82010-04-10 02:20:57 +0000987 source_type=(cmsUInt32Number) TYPE_RGB_16;
988 source_channels=3;
989 break;
990 }
991 case cmsSigXYZData:
992 {
993 source_colorspace=XYZColorspace;
994 source_type=(cmsUInt32Number) TYPE_XYZ_16;
995 source_channels=3;
996 break;
997 }
998 case cmsSigYCbCrData:
999 {
1000 source_colorspace=YCbCrColorspace;
1001 source_type=(cmsUInt32Number) TYPE_YCbCr_16;
1002 source_channels=3;
1003 break;
1004 }
1005 default:
1006 {
1007 source_colorspace=UndefinedColorspace;
1008 source_type=(cmsUInt32Number) TYPE_RGB_16;
1009 source_channels=3;
1010 break;
1011 }
cristy8a20a2d2010-04-08 14:16:27 +00001012 }
cristye1c67a82010-04-10 02:20:57 +00001013 signature=cmsGetPCS(source_profile);
1014 if (target_profile != (cmsHPROFILE) NULL)
1015 signature=cmsGetColorSpace(target_profile);
1016 switch (signature)
1017 {
1018 case cmsSigCmykData:
cristy8a20a2d2010-04-08 14:16:27 +00001019 {
cristye1c67a82010-04-10 02:20:57 +00001020 target_colorspace=CMYKColorspace;
1021 target_type=(cmsUInt32Number) TYPE_CMYK_16;
1022 target_channels=4;
1023 break;
cristy8a20a2d2010-04-08 14:16:27 +00001024 }
cristye1c67a82010-04-10 02:20:57 +00001025 case cmsSigLabData:
cristy8a20a2d2010-04-08 14:16:27 +00001026 {
cristye1c67a82010-04-10 02:20:57 +00001027 target_colorspace=LabColorspace;
1028 target_type=(cmsUInt32Number) TYPE_Lab_16;
1029 target_channels=3;
1030 break;
cristy8a20a2d2010-04-08 14:16:27 +00001031 }
cristye1c67a82010-04-10 02:20:57 +00001032 case cmsSigGrayData:
cristy8a20a2d2010-04-08 14:16:27 +00001033 {
cristye1c67a82010-04-10 02:20:57 +00001034 target_colorspace=GRAYColorspace;
1035 target_type=(cmsUInt32Number) TYPE_GRAY_16;
1036 target_channels=1;
1037 break;
cristy8a20a2d2010-04-08 14:16:27 +00001038 }
cristye1c67a82010-04-10 02:20:57 +00001039 case cmsSigLuvData:
1040 {
1041 target_colorspace=YUVColorspace;
1042 target_type=(cmsUInt32Number) TYPE_YUV_16;
1043 target_channels=3;
1044 break;
1045 }
1046 case cmsSigRgbData:
1047 {
cristyc511e882012-04-16 21:11:14 +00001048 target_colorspace=sRGBColorspace;
cristye1c67a82010-04-10 02:20:57 +00001049 target_type=(cmsUInt32Number) TYPE_RGB_16;
1050 target_channels=3;
1051 break;
1052 }
1053 case cmsSigXYZData:
1054 {
1055 target_colorspace=XYZColorspace;
1056 target_type=(cmsUInt32Number) TYPE_XYZ_16;
1057 target_channels=3;
1058 break;
1059 }
1060 case cmsSigYCbCrData:
1061 {
1062 target_colorspace=YCbCrColorspace;
1063 target_type=(cmsUInt32Number) TYPE_YCbCr_16;
1064 target_channels=3;
1065 break;
1066 }
1067 default:
1068 {
1069 target_colorspace=UndefinedColorspace;
1070 target_type=(cmsUInt32Number) TYPE_RGB_16;
1071 target_channels=3;
1072 break;
1073 }
1074 }
1075 if ((source_colorspace == UndefinedColorspace) ||
1076 (target_colorspace == UndefinedColorspace))
1077 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
1078 name);
1079 if ((source_colorspace == GRAYColorspace) &&
cristy4c08aed2011-07-01 19:47:50 +00001080 (IsImageGray(image,exception) == MagickFalse))
cristye1c67a82010-04-10 02:20:57 +00001081 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
1082 name);
1083 if ((source_colorspace == CMYKColorspace) &&
1084 (image->colorspace != CMYKColorspace))
1085 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
1086 name);
1087 if ((source_colorspace == XYZColorspace) &&
1088 (image->colorspace != XYZColorspace))
1089 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
1090 name);
1091 if ((source_colorspace == YCbCrColorspace) &&
1092 (image->colorspace != YCbCrColorspace))
1093 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
1094 name);
1095 if ((source_colorspace != CMYKColorspace) &&
cristye1c67a82010-04-10 02:20:57 +00001096 (source_colorspace != LabColorspace) &&
1097 (source_colorspace != XYZColorspace) &&
1098 (source_colorspace != YCbCrColorspace) &&
cristy3d9f5ba2012-06-26 13:37:31 +00001099 (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse))
cristye1c67a82010-04-10 02:20:57 +00001100 ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
1101 name);
1102 switch (image->rendering_intent)
1103 {
1104 case AbsoluteIntent: intent=INTENT_ABSOLUTE_COLORIMETRIC; break;
1105 case PerceptualIntent: intent=INTENT_PERCEPTUAL; break;
1106 case RelativeIntent: intent=INTENT_RELATIVE_COLORIMETRIC; break;
1107 case SaturationIntent: intent=INTENT_SATURATION; break;
cristye540eb42012-04-16 12:50:34 +00001108 default: intent=INTENT_PERCEPTUAL; break;
cristye1c67a82010-04-10 02:20:57 +00001109 }
1110 flags=cmsFLAGS_HIGHRESPRECALC;
1111#if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
1112 if (image->black_point_compensation != MagickFalse)
1113 flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
1114#endif
cristy7632f502010-06-18 13:26:31 +00001115 transform=AcquireTransformThreadSet(image,source_profile,
1116 source_type,target_profile,target_type,intent,flags);
cristye1c67a82010-04-10 02:20:57 +00001117 if (transform == (cmsHTRANSFORM *) NULL)
1118 ThrowProfileException(ImageError,"UnableToCreateColorTransform",
1119 name);
1120 /*
1121 Transform image as dictated by the source & target image profiles.
1122 */
cristye1c67a82010-04-10 02:20:57 +00001123 source_pixels=AcquirePixelThreadSet(image->columns,source_channels);
1124 target_pixels=AcquirePixelThreadSet(image->columns,target_channels);
1125 if ((source_pixels == (unsigned short **) NULL) ||
1126 (target_pixels == (unsigned short **) NULL))
1127 {
1128 transform=DestroyTransformThreadSet(transform);
1129 ThrowProfileException(ResourceLimitError,
1130 "MemoryAllocationFailed",image->filename);
1131 }
cristy574cc262011-08-05 01:23:58 +00001132 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristye1c67a82010-04-10 02:20:57 +00001133 {
1134 target_pixels=DestroyPixelThreadSet(target_pixels);
1135 source_pixels=DestroyPixelThreadSet(source_pixels);
1136 transform=DestroyTransformThreadSet(transform);
1137 if (source_profile != (cmsHPROFILE) NULL)
1138 (void) cmsCloseProfile(source_profile);
1139 if (target_profile != (cmsHPROFILE) NULL)
1140 (void) cmsCloseProfile(target_profile);
1141 return(MagickFalse);
1142 }
1143 if (target_colorspace == CMYKColorspace)
cristy63240882011-08-05 19:05:27 +00001144 (void) SetImageColorspace(image,target_colorspace,exception);
cristye1c67a82010-04-10 02:20:57 +00001145 status=MagickTrue;
1146 progress=0;
cristy46ff2672012-12-14 15:32:26 +00001147 image_view=AcquireAuthenticCacheView(image,exception);
cristye1c67a82010-04-10 02:20:57 +00001148#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001149 #pragma omp parallel for schedule(static,4) shared(status) \
cristy5e6b2592012-12-19 14:08:11 +00001150 magick_threads(image,image,image->rows,1)
cristye1c67a82010-04-10 02:20:57 +00001151#endif
cristybb503372010-05-27 20:51:26 +00001152 for (y=0; y < (ssize_t) image->rows; y++)
cristy8a20a2d2010-04-08 14:16:27 +00001153 {
cristy5c9e6f22010-09-17 17:31:01 +00001154 const int
1155 id = GetOpenMPThreadId();
cristyad740052010-07-03 01:38:03 +00001156
cristy8a20a2d2010-04-08 14:16:27 +00001157 MagickBooleanType
cristye1c67a82010-04-10 02:20:57 +00001158 sync;
1159
cristybb503372010-05-27 20:51:26 +00001160 register ssize_t
cristye1c67a82010-04-10 02:20:57 +00001161 x;
1162
cristy4c08aed2011-07-01 19:47:50 +00001163 register Quantum
cristye1c67a82010-04-10 02:20:57 +00001164 *restrict q;
1165
1166 register unsigned short
1167 *p;
1168
1169 if (status == MagickFalse)
1170 continue;
1171 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1172 exception);
cristyacd2ed22011-08-30 01:44:23 +00001173 if (q == (Quantum *) NULL)
cristye1c67a82010-04-10 02:20:57 +00001174 {
1175 status=MagickFalse;
1176 continue;
1177 }
cristye1c67a82010-04-10 02:20:57 +00001178 p=source_pixels[id];
cristybb503372010-05-27 20:51:26 +00001179 for (x=0; x < (ssize_t) image->columns; x++)
cristye1c67a82010-04-10 02:20:57 +00001180 {
cristy4c08aed2011-07-01 19:47:50 +00001181 *p++=ScaleQuantumToShort(GetPixelRed(image,q));
cristye1c67a82010-04-10 02:20:57 +00001182 if (source_channels > 1)
1183 {
cristy4c08aed2011-07-01 19:47:50 +00001184 *p++=ScaleQuantumToShort(GetPixelGreen(image,q));
1185 *p++=ScaleQuantumToShort(GetPixelBlue(image,q));
cristye1c67a82010-04-10 02:20:57 +00001186 }
1187 if (source_channels > 3)
cristy4c08aed2011-07-01 19:47:50 +00001188 *p++=ScaleQuantumToShort(GetPixelBlack(image,q));
cristyed231572011-07-14 02:18:59 +00001189 q+=GetPixelChannels(image);
cristye1c67a82010-04-10 02:20:57 +00001190 }
1191 cmsDoTransform(transform[id],source_pixels[id],target_pixels[id],
1192 (unsigned int) image->columns);
1193 p=target_pixels[id];
cristyed231572011-07-14 02:18:59 +00001194 q-=image->columns*GetPixelChannels(image);
cristybb503372010-05-27 20:51:26 +00001195 for (x=0; x < (ssize_t) image->columns; x++)
cristye1c67a82010-04-10 02:20:57 +00001196 {
cristyaf6eb932012-01-01 01:22:59 +00001197 if (target_channels == 1)
1198 SetPixelGray(image,ScaleShortToQuantum(*p),q);
1199 else
1200 SetPixelRed(image,ScaleShortToQuantum(*p),q);
cristye1c67a82010-04-10 02:20:57 +00001201 p++;
1202 if (target_channels > 1)
1203 {
cristy4c08aed2011-07-01 19:47:50 +00001204 SetPixelGreen(image,ScaleShortToQuantum(*p),q);
cristye1c67a82010-04-10 02:20:57 +00001205 p++;
cristy4c08aed2011-07-01 19:47:50 +00001206 SetPixelBlue(image,ScaleShortToQuantum(*p),q);
cristye1c67a82010-04-10 02:20:57 +00001207 p++;
1208 }
1209 if (target_channels > 3)
1210 {
cristy4c08aed2011-07-01 19:47:50 +00001211 SetPixelBlack(image,ScaleShortToQuantum(*p),q);
cristye1c67a82010-04-10 02:20:57 +00001212 p++;
1213 }
cristyed231572011-07-14 02:18:59 +00001214 q+=GetPixelChannels(image);
cristye1c67a82010-04-10 02:20:57 +00001215 }
1216 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1217 if (sync == MagickFalse)
1218 status=MagickFalse;
1219 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1220 {
1221 MagickBooleanType
1222 proceed;
cristy8a20a2d2010-04-08 14:16:27 +00001223
1224#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001225 #pragma omp critical (MagickCore_ProfileImage)
cristy8a20a2d2010-04-08 14:16:27 +00001226#endif
cristye1c67a82010-04-10 02:20:57 +00001227 proceed=SetImageProgress(image,ProfileImageTag,progress++,
1228 image->rows);
1229 if (proceed == MagickFalse)
1230 status=MagickFalse;
1231 }
cristy8a20a2d2010-04-08 14:16:27 +00001232 }
cristye1c67a82010-04-10 02:20:57 +00001233 image_view=DestroyCacheView(image_view);
cristy63240882011-08-05 19:05:27 +00001234 (void) SetImageColorspace(image,target_colorspace,exception);
cristye1c67a82010-04-10 02:20:57 +00001235 switch (signature)
1236 {
1237 case cmsSigRgbData:
1238 {
cristye4f023a2013-02-27 21:01:50 +00001239 image->type=image->alpha_trait != BlendPixelTrait ?
1240 TrueColorType : TrueColorMatteType;
cristye1c67a82010-04-10 02:20:57 +00001241 break;
1242 }
1243 case cmsSigCmykData:
1244 {
cristye4f023a2013-02-27 21:01:50 +00001245 image->type=image->alpha_trait != BlendPixelTrait ?
1246 ColorSeparationType : ColorSeparationMatteType;
cristye1c67a82010-04-10 02:20:57 +00001247 break;
1248 }
1249 case cmsSigGrayData:
1250 {
cristye4f023a2013-02-27 21:01:50 +00001251 image->type=image->alpha_trait != BlendPixelTrait ?
1252 GrayscaleType : GrayscaleMatteType;
cristye1c67a82010-04-10 02:20:57 +00001253 break;
1254 }
1255 default:
1256 break;
1257 }
1258 target_pixels=DestroyPixelThreadSet(target_pixels);
1259 source_pixels=DestroyPixelThreadSet(source_pixels);
1260 transform=DestroyTransformThreadSet(transform);
1261 if (cmsGetDeviceClass(source_profile) != cmsSigLinkClass)
cristyd15e6592011-10-15 00:13:06 +00001262 status=SetImageProfile(image,name,profile,exception);
cristye1c67a82010-04-10 02:20:57 +00001263 if (target_profile != (cmsHPROFILE) NULL)
1264 (void) cmsCloseProfile(target_profile);
cristy8a20a2d2010-04-08 14:16:27 +00001265 }
cristyfbaaf3e2010-04-08 14:53:43 +00001266 (void) cmsCloseProfile(source_profile);
cristy8a20a2d2010-04-08 14:16:27 +00001267 }
cristy3ed852e2009-09-05 21:47:34 +00001268#endif
1269 }
cristy3ed852e2009-09-05 21:47:34 +00001270 profile=DestroyStringInfo(profile);
1271 return(status);
1272}
1273
1274/*
1275%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1276% %
1277% %
1278% %
1279% R e m o v e I m a g e P r o f i l e %
1280% %
1281% %
1282% %
1283%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1284%
1285% RemoveImageProfile() removes a named profile from the image and returns its
1286% value.
1287%
1288% The format of the RemoveImageProfile method is:
1289%
1290% void *RemoveImageProfile(Image *image,const char *name)
1291%
1292% A description of each parameter follows:
1293%
1294% o image: the image.
1295%
1296% o name: the profile name.
1297%
1298*/
1299MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
1300{
1301 StringInfo
1302 *profile;
1303
1304 assert(image != (Image *) NULL);
1305 assert(image->signature == MagickSignature);
1306 if (image->debug != MagickFalse)
1307 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1308 if (image->profiles == (SplayTreeInfo *) NULL)
1309 return((StringInfo *) NULL);
dirk9f4f3542014-04-21 17:19:03 +00001310 WriteTo8BimProfile(image,name,(StringInfo *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001311 profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
1312 image->profiles,name);
1313 return(profile);
1314}
1315
1316/*
1317%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1318% %
1319% %
1320% %
1321% R e s e t P r o f i l e I t e r a t o r %
1322% %
1323% %
1324% %
1325%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1326%
1327% ResetImageProfileIterator() resets the image profile iterator. Use it in
1328% conjunction with GetNextImageProfile() to iterate over all the profiles
1329% associated with an image.
1330%
1331% The format of the ResetImageProfileIterator method is:
1332%
1333% ResetImageProfileIterator(Image *image)
1334%
1335% A description of each parameter follows:
1336%
1337% o image: the image.
1338%
1339*/
1340MagickExport void ResetImageProfileIterator(const Image *image)
1341{
1342 assert(image != (Image *) NULL);
1343 assert(image->signature == MagickSignature);
1344 if (image->debug != MagickFalse)
1345 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1346 if (image->profiles == (SplayTreeInfo *) NULL)
1347 return;
1348 ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
1349}
1350
1351/*
1352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1353% %
1354% %
1355% %
1356% S e t I m a g e P r o f i l e %
1357% %
1358% %
1359% %
1360%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1361%
1362% SetImageProfile() adds a named profile to the image. If a profile with the
1363% same name already exists, it is replaced. This method differs from the
1364% ProfileImage() method in that it does not apply CMS color profiles.
1365%
1366% The format of the SetImageProfile method is:
1367%
1368% MagickBooleanType SetImageProfile(Image *image,const char *name,
1369% const StringInfo *profile)
1370%
1371% A description of each parameter follows:
1372%
1373% o image: the image.
1374%
1375% o name: the profile name, for example icc, exif, and 8bim (8bim is the
1376% Photoshop wrapper for iptc profiles).
1377%
1378% o profile: A StringInfo structure that contains the named profile.
1379%
1380*/
1381
1382static void *DestroyProfile(void *profile)
1383{
1384 return((void *) DestroyStringInfo((StringInfo *) profile));
1385}
1386
1387static inline const unsigned char *ReadResourceByte(const unsigned char *p,
1388 unsigned char *quantum)
1389{
1390 *quantum=(*p++);
1391 return(p);
1392}
1393
cristy3ed852e2009-09-05 21:47:34 +00001394static inline const unsigned char *ReadResourceLong(const unsigned char *p,
cristy3fb79582014-03-13 17:04:24 +00001395 unsigned int *quantum)
cristy3ed852e2009-09-05 21:47:34 +00001396{
cristybb503372010-05-27 20:51:26 +00001397 *quantum=(size_t) (*p++ << 24);
1398 *quantum|=(size_t) (*p++ << 16);
1399 *quantum|=(size_t) (*p++ << 8);
1400 *quantum|=(size_t) (*p++ << 0);
cristy3ed852e2009-09-05 21:47:34 +00001401 return(p);
1402}
1403
1404static inline const unsigned char *ReadResourceShort(const unsigned char *p,
1405 unsigned short *quantum)
1406{
1407 *quantum=(unsigned short) (*p++ << 8);
1408 *quantum|=(unsigned short) (*p++ << 0);
1409 return(p);
dirke9dbde12014-04-23 16:03:29 +00001410}static inline void WriteResourceLong(unsigned char *p,
1411 const unsigned int quantum)
1412{
1413 unsigned char
1414 buffer[4];
1415
1416 buffer[0]=(unsigned char) (quantum >> 24);
1417 buffer[1]=(unsigned char) (quantum >> 16);
1418 buffer[2]=(unsigned char) (quantum >> 8);
1419 buffer[3]=(unsigned char) quantum;
1420 (void) CopyMagickMemory(p,buffer,4);
1421}
1422
1423static void WriteTo8BimProfile(Image *image,const char *name,
dirk4b099fc2014-04-27 16:05:56 +00001424 const StringInfo *profile)
dirke9dbde12014-04-23 16:03:29 +00001425{
dirke9dbde12014-04-23 16:03:29 +00001426 const unsigned char
1427 *datum,
cristyb0689062014-08-14 11:37:55 +00001428 *q;
dirke9dbde12014-04-23 16:03:29 +00001429
1430 register const unsigned char
1431 *p;
1432
1433 size_t
1434 length;
1435
1436 StringInfo
dirk4b099fc2014-04-27 16:05:56 +00001437 *profile_8bim;
dirke9dbde12014-04-23 16:03:29 +00001438
1439 ssize_t
1440 count;
1441
1442 unsigned char
1443 length_byte;
1444
1445 unsigned int
1446 value;
1447
1448 unsigned short
dirk5bdcdb92014-04-27 04:07:50 +00001449 id,
1450 profile_id;
dirke9dbde12014-04-23 16:03:29 +00001451
dirk4b099fc2014-04-27 16:05:56 +00001452 if (LocaleCompare(name,"icc") == 0)
1453 profile_id=0x040f;
dirk5bdcdb92014-04-27 04:07:50 +00001454 else
cristyb0689062014-08-14 11:37:55 +00001455 if (LocaleCompare(name,"iptc") == 0)
1456 profile_id=0x0404;
1457 else
1458 if (LocaleCompare(name,"xmp") == 0)
1459 profile_id=0x0424;
1460 else
1461 return;
1462 profile_8bim=(StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
dirk4b099fc2014-04-27 16:05:56 +00001463 image->profiles,"8bim");
1464 if (profile_8bim == (StringInfo *) NULL)
dirke9dbde12014-04-23 16:03:29 +00001465 return;
dirk4b099fc2014-04-27 16:05:56 +00001466 datum=GetStringInfoDatum(profile_8bim);
1467 length=GetStringInfoLength(profile_8bim);
dirke9dbde12014-04-23 16:03:29 +00001468 for (p=datum; p < (datum+length-16); )
1469 {
cristyb0689062014-08-14 11:37:55 +00001470 q=p;
dirke9dbde12014-04-23 16:03:29 +00001471 if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1472 break;
1473 p+=4;
1474 p=ReadResourceShort(p,&id);
1475 p=ReadResourceByte(p,&length_byte);
1476 p+=length_byte;
1477 if (((length_byte+1) & 0x01) != 0)
1478 p++;
1479 if (p > (datum+length-4))
1480 break;
1481 p=ReadResourceLong(p,&value);
1482 count=(ssize_t) value;
dirke9dbde12014-04-23 16:03:29 +00001483 if ((count & 0x01) != 0)
1484 count++;
cristyb0689062014-08-14 11:37:55 +00001485 if ((p > (datum+length-count)) || (count > (ssize_t) length))
1486 break;
1487 if (id != profile_id)
1488 p+=count;
1489 else
dirke9dbde12014-04-23 16:03:29 +00001490 {
1491 size_t
cristyb0689062014-08-14 11:37:55 +00001492 extent,
1493 offset;
dirke9dbde12014-04-23 16:03:29 +00001494
1495 ssize_t
cristyb0689062014-08-14 11:37:55 +00001496 extract_count;
dirke9dbde12014-04-23 16:03:29 +00001497
1498 StringInfo
cristyb0689062014-08-14 11:37:55 +00001499 *extract_profile;
dirke9dbde12014-04-23 16:03:29 +00001500
cristyb0689062014-08-14 11:37:55 +00001501 extract_count=0;
1502 extent=(datum+length)-(p+count);
dirk4b099fc2014-04-27 16:05:56 +00001503 if (profile == (StringInfo *) NULL)
dirke9dbde12014-04-23 16:03:29 +00001504 {
cristyb0689062014-08-14 11:37:55 +00001505 offset=(q-datum);
1506 extract_profile=AcquireStringInfo(offset+extent);
1507 (void) CopyMagickMemory(extract_profile->datum,datum,offset);
dirke9dbde12014-04-23 16:03:29 +00001508 }
1509 else
1510 {
1511 offset=(p-datum);
cristyb0689062014-08-14 11:37:55 +00001512 extract_count=profile->length;
1513 if ((extract_count & 0x01) != 0)
1514 extract_count++;
1515 extract_profile=AcquireStringInfo(offset+extract_count+extent);
1516 (void) CopyMagickMemory(extract_profile->datum,datum,offset-4);
dirkb0d783f2014-08-31 10:48:05 +00001517 WriteResourceLong(extract_profile->datum+offset-4,
1518 (unsigned int)profile->length);
cristyb0689062014-08-14 11:37:55 +00001519 (void) CopyMagickMemory(extract_profile->datum+offset,
1520 profile->datum,profile->length);
dirke9dbde12014-04-23 16:03:29 +00001521 }
cristyb0689062014-08-14 11:37:55 +00001522 (void) CopyMagickMemory(extract_profile->datum+offset+extract_count,
1523 p+count,extent);
dirke9dbde12014-04-23 16:03:29 +00001524 (void) AddValueToSplayTree((SplayTreeInfo *) image->profiles,
cristyb0689062014-08-14 11:37:55 +00001525 ConstantString("8bim"),CloneStringInfo(extract_profile));
1526 extract_profile=DestroyStringInfo(extract_profile);
dirke9dbde12014-04-23 16:03:29 +00001527 break;
1528 }
dirke9dbde12014-04-23 16:03:29 +00001529 }
1530}
1531
dirk9f4f3542014-04-21 17:19:03 +00001532static void GetProfilesFromResourceBlock(Image *image,
cristyd15e6592011-10-15 00:13:06 +00001533 const StringInfo *resource_block,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001534{
1535 const unsigned char
1536 *datum;
1537
1538 register const unsigned char
1539 *p;
1540
1541 size_t
1542 length;
1543
cristy66aeb172014-03-13 17:06:31 +00001544 ssize_t
1545 count;
1546
cristy3ed852e2009-09-05 21:47:34 +00001547 StringInfo
1548 *profile;
1549
1550 unsigned char
1551 length_byte;
1552
cristy3fb79582014-03-13 17:04:24 +00001553 unsigned int
1554 value;
1555
cristy3ed852e2009-09-05 21:47:34 +00001556 unsigned short
1557 id;
1558
1559 datum=GetStringInfoDatum(resource_block);
1560 length=GetStringInfoLength(resource_block);
1561 for (p=datum; p < (datum+length-16); )
1562 {
1563 if (LocaleNCompare((char *) p,"8BIM",4) != 0)
1564 break;
1565 p+=4;
1566 p=ReadResourceShort(p,&id);
1567 p=ReadResourceByte(p,&length_byte);
1568 p+=length_byte;
1569 if (((length_byte+1) & 0x01) != 0)
1570 p++;
1571 if (p > (datum+length-4))
1572 break;
cristy3fb79582014-03-13 17:04:24 +00001573 p=ReadResourceLong(p,&value);
1574 count=(ssize_t) value;
cristy759ba912014-06-26 11:59:43 +00001575 if ((p > (datum+length-count)) || (count > (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00001576 break;
1577 switch (id)
1578 {
1579 case 0x03ed:
1580 {
dirk3287f1d2014-03-11 20:38:36 +00001581 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00001582 resolution;
1583
cristy3fb79582014-03-13 17:04:24 +00001584 unsigned short
1585 units;
1586
cristy3ed852e2009-09-05 21:47:34 +00001587 /*
1588 Resolution.
1589 */
dirk3287f1d2014-03-11 20:38:36 +00001590 p=ReadResourceLong(p,&resolution);
1591 image->resolution.x=((double) resolution)/65536.0;
1592 p=ReadResourceShort(p,&units)+2;
1593 p=ReadResourceLong(p,&resolution)+4;
1594 image->resolution.y=((double) resolution)/65536.0;
cristy3fb79582014-03-13 17:04:24 +00001595 /*
1596 Values are always stored as pixels per inch.
1597 */
1598 if ((ResolutionType) units != PixelsPerCentimeterResolution)
1599 image->units=PixelsPerInchResolution;
1600 else
dirk3287f1d2014-03-11 20:38:36 +00001601 {
1602 image->units=PixelsPerCentimeterResolution;
1603 image->resolution.x/=2.54;
1604 image->resolution.y/=2.54;
1605 }
cristy3ed852e2009-09-05 21:47:34 +00001606 break;
1607 }
1608 case 0x0404:
1609 {
1610 /*
1611 IPTC Profile
1612 */
1613 profile=AcquireStringInfo(count);
1614 SetStringInfoDatum(profile,p);
dirk9f4f3542014-04-21 17:19:03 +00001615 (void) SetImageProfileInternal(image,"iptc",profile,MagickTrue,
1616 exception);
cristy3ed852e2009-09-05 21:47:34 +00001617 profile=DestroyStringInfo(profile);
1618 p+=count;
1619 break;
1620 }
1621 case 0x040c:
1622 {
1623 /*
1624 Thumbnail.
1625 */
1626 p+=count;
1627 break;
1628 }
1629 case 0x040f:
1630 {
1631 /*
1632 ICC Profile.
1633 */
1634 profile=AcquireStringInfo(count);
1635 SetStringInfoDatum(profile,p);
dirk9f4f3542014-04-21 17:19:03 +00001636 (void) SetImageProfileInternal(image,"icc",profile,MagickTrue,
1637 exception);
cristy3ed852e2009-09-05 21:47:34 +00001638 profile=DestroyStringInfo(profile);
1639 p+=count;
1640 break;
1641 }
1642 case 0x0422:
1643 {
1644 /*
1645 EXIF Profile.
1646 */
1647 profile=AcquireStringInfo(count);
1648 SetStringInfoDatum(profile,p);
dirk9f4f3542014-04-21 17:19:03 +00001649 (void) SetImageProfileInternal(image,"exif",profile,MagickTrue,
1650 exception);
cristy3ed852e2009-09-05 21:47:34 +00001651 profile=DestroyStringInfo(profile);
1652 p+=count;
1653 break;
1654 }
1655 case 0x0424:
1656 {
1657 /*
1658 XMP Profile.
1659 */
1660 profile=AcquireStringInfo(count);
1661 SetStringInfoDatum(profile,p);
dirk9f4f3542014-04-21 17:19:03 +00001662 (void) SetImageProfileInternal(image,"xmp",profile,MagickTrue,
1663 exception);
cristy3ed852e2009-09-05 21:47:34 +00001664 profile=DestroyStringInfo(profile);
1665 p+=count;
1666 break;
1667 }
1668 default:
1669 {
1670 p+=count;
1671 break;
1672 }
1673 }
1674 if ((count & 0x01) != 0)
1675 p++;
1676 }
cristy3ed852e2009-09-05 21:47:34 +00001677}
1678
dirk9f4f3542014-04-21 17:19:03 +00001679static MagickBooleanType SetImageProfileInternal(Image *image,const char *name,
1680 const StringInfo *profile,const MagickBooleanType recursive,
1681 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001682{
1683 char
1684 key[MaxTextExtent],
1685 property[MaxTextExtent];
1686
1687 MagickBooleanType
1688 status;
1689
1690 assert(image != (Image *) NULL);
1691 assert(image->signature == MagickSignature);
1692 if (image->debug != MagickFalse)
1693 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1694 if (image->profiles == (SplayTreeInfo *) NULL)
1695 image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
1696 DestroyProfile);
1697 (void) CopyMagickString(key,name,MaxTextExtent);
dirk9f4f3542014-04-21 17:19:03 +00001698 LocaleLower(key);
cristy3ed852e2009-09-05 21:47:34 +00001699 status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
1700 ConstantString(key),CloneStringInfo(profile));
dirk9f4f3542014-04-21 17:19:03 +00001701 if (status != MagickFalse)
1702 {
1703 if (LocaleCompare(name,"8bim") == 0)
1704 GetProfilesFromResourceBlock(image,profile,exception);
dirke9dbde12014-04-23 16:03:29 +00001705 else if (recursive == MagickFalse)
dirk9f4f3542014-04-21 17:19:03 +00001706 WriteTo8BimProfile(image,name,profile);
1707 }
cristy3ed852e2009-09-05 21:47:34 +00001708 /*
1709 Inject profile into image properties.
1710 */
cristye9438e22014-01-06 23:39:37 +00001711 (void) FormatLocaleString(property,MaxTextExtent,"%s:*",name);
cristyd15e6592011-10-15 00:13:06 +00001712 (void) GetImageProperty(image,property,exception);
cristy3ed852e2009-09-05 21:47:34 +00001713 return(status);
1714}
dirk9f4f3542014-04-21 17:19:03 +00001715
1716MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
1717 const StringInfo *profile,ExceptionInfo *exception)
1718{
1719 return(SetImageProfileInternal(image,name,profile,MagickFalse,exception));
1720}
cristy3ed852e2009-09-05 21:47:34 +00001721
1722/*
1723%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1724% %
1725% %
1726% %
1727% S y n c I m a g e P r o f i l e s %
1728% %
1729% %
1730% %
1731%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1732%
1733% SyncImageProfiles() synchronizes image properties with the image profiles.
1734% Currently we only support updating the EXIF resolution and orientation.
1735%
1736% The format of the SyncImageProfiles method is:
1737%
1738% MagickBooleanType SyncImageProfiles(Image *image)
1739%
1740% A description of each parameter follows:
1741%
1742% o image: the image.
1743%
1744*/
1745
1746static inline int ReadProfileByte(unsigned char **p,size_t *length)
1747{
1748 int
1749 c;
1750
1751 if (*length < 1)
1752 return(EOF);
1753 c=(int) (*(*p)++);
1754 (*length)--;
1755 return(c);
1756}
1757
1758static inline unsigned short ReadProfileShort(const EndianType endian,
1759 unsigned char *buffer)
1760{
1761 unsigned short
1762 value;
1763
cristy1715f792012-11-22 16:56:12 +00001764 if (endian == LSBEndian)
cristy3ed852e2009-09-05 21:47:34 +00001765 {
cristy1715f792012-11-22 16:56:12 +00001766 value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
cristy3ed852e2009-09-05 21:47:34 +00001767 return((unsigned short) (value & 0xffff));
1768 }
cristy1715f792012-11-22 16:56:12 +00001769 value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
1770 ((unsigned char *) buffer)[1]);
cristy3ed852e2009-09-05 21:47:34 +00001771 return((unsigned short) (value & 0xffff));
1772}
1773
cristybb503372010-05-27 20:51:26 +00001774static inline size_t ReadProfileLong(const EndianType endian,
cristy3ed852e2009-09-05 21:47:34 +00001775 unsigned char *buffer)
1776{
cristybb503372010-05-27 20:51:26 +00001777 size_t
cristy3ed852e2009-09-05 21:47:34 +00001778 value;
1779
cristy1715f792012-11-22 16:56:12 +00001780 if (endian == LSBEndian)
cristy3ed852e2009-09-05 21:47:34 +00001781 {
cristy1715f792012-11-22 16:56:12 +00001782 value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
1783 (buffer[1] << 8 ) | (buffer[0]));
cristybb503372010-05-27 20:51:26 +00001784 return((size_t) (value & 0xffffffff));
cristy3ed852e2009-09-05 21:47:34 +00001785 }
cristy1715f792012-11-22 16:56:12 +00001786 value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
1787 (buffer[2] << 8) | buffer[3]);
cristybb503372010-05-27 20:51:26 +00001788 return((size_t) (value & 0xffffffff));
cristy3ed852e2009-09-05 21:47:34 +00001789}
1790
dirk5bfa82b2014-03-01 11:22:55 +00001791static inline size_t ReadProfileMSBLong(unsigned char **p,
1792 size_t *length)
1793{
1794 size_t
1795 value;
1796
1797 if (*length < 4)
1798 return(0);
1799
1800 value=ReadProfileLong(MSBEndian,*p);
1801 (*length)-=4;
1802 *p+=4;
1803 return(value);
1804}
1805
1806static inline unsigned short ReadProfileMSBShort(unsigned char **p,
1807 size_t *length)
1808{
1809 unsigned short
1810 value;
1811
1812 if (*length < 2)
1813 return(0);
1814
1815 value=ReadProfileShort(MSBEndian,*p);
1816 (*length)-=2;
1817 *p+=2;
1818 return(value);
1819}
1820
cristy3ed852e2009-09-05 21:47:34 +00001821static inline void WriteProfileLong(const EndianType endian,
cristybb503372010-05-27 20:51:26 +00001822 const size_t value,unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001823{
1824 unsigned char
1825 buffer[4];
1826
cristy1715f792012-11-22 16:56:12 +00001827 if (endian == LSBEndian)
cristy3ed852e2009-09-05 21:47:34 +00001828 {
cristy1715f792012-11-22 16:56:12 +00001829 buffer[0]=(unsigned char) value;
1830 buffer[1]=(unsigned char) (value >> 8);
1831 buffer[2]=(unsigned char) (value >> 16);
1832 buffer[3]=(unsigned char) (value >> 24);
cristy3ed852e2009-09-05 21:47:34 +00001833 (void) CopyMagickMemory(p,buffer,4);
1834 return;
1835 }
cristy1715f792012-11-22 16:56:12 +00001836 buffer[0]=(unsigned char) (value >> 24);
1837 buffer[1]=(unsigned char) (value >> 16);
1838 buffer[2]=(unsigned char) (value >> 8);
1839 buffer[3]=(unsigned char) value;
cristy3ed852e2009-09-05 21:47:34 +00001840 (void) CopyMagickMemory(p,buffer,4);
1841}
1842
1843static void WriteProfileShort(const EndianType endian,
1844 const unsigned short value,unsigned char *p)
1845{
1846 unsigned char
1847 buffer[2];
1848
cristy1715f792012-11-22 16:56:12 +00001849 if (endian == LSBEndian)
cristy3ed852e2009-09-05 21:47:34 +00001850 {
cristy1715f792012-11-22 16:56:12 +00001851 buffer[0]=(unsigned char) value;
1852 buffer[1]=(unsigned char) (value >> 8);
cristy3ed852e2009-09-05 21:47:34 +00001853 (void) CopyMagickMemory(p,buffer,2);
1854 return;
1855 }
cristy1715f792012-11-22 16:56:12 +00001856 buffer[0]=(unsigned char) (value >> 8);
1857 buffer[1]=(unsigned char) value;
cristy3ed852e2009-09-05 21:47:34 +00001858 (void) CopyMagickMemory(p,buffer,2);
1859}
1860
dirk5bfa82b2014-03-01 11:22:55 +00001861static MagickBooleanType Sync8BimProfile(Image *image,StringInfo *profile)
1862{
1863 size_t
1864 count,
1865 length;
1866
1867 unsigned char
1868 *p;
1869
1870 unsigned short
1871 id;
1872
1873 length=GetStringInfoLength(profile);
1874 p=GetStringInfoDatum(profile);
1875 while(length != 0)
1876 {
1877 if (ReadProfileByte(&p,&length) != 0x38)
1878 continue;
1879 if (ReadProfileByte(&p,&length) != 0x42)
1880 continue;
1881 if (ReadProfileByte(&p,&length) != 0x49)
1882 continue;
1883 if (ReadProfileByte(&p,&length) != 0x4D)
1884 continue;
1885 if (length < 7)
1886 return(MagickFalse);
1887 id=ReadProfileMSBShort(&p,&length);
1888 count=ReadProfileByte(&p,&length);
1889 if (count > length)
1890 return(MagickFalse);
1891 p+=count;
1892 if ((*p & 0x01) == 0)
1893 p++;
1894 count=ReadProfileMSBLong(&p,&length);
1895 if (count > length)
1896 return(MagickFalse);
1897 if (id == 0x3ED && count == 16)
1898 {
dirk3287f1d2014-03-11 20:38:36 +00001899 if (image->units == PixelsPerCentimeterResolution)
1900 WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.x*2.54*
1901 65536.0),p);
1902 else
1903 WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.x*
1904 65536.0),p);
1905 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+4);
1906 if (image->units == PixelsPerCentimeterResolution)
1907 WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.y*2.54*
1908 65536.0),p+8);
1909 else
1910 WriteProfileLong(MSBEndian, (unsigned int) (image->resolution.y*
1911 65536.0),p+8);
1912 WriteProfileShort(MSBEndian,(unsigned short) image->units,p+12);
dirk5bfa82b2014-03-01 11:22:55 +00001913 }
1914 p+=count;
1915 length-=count;
1916 }
1917 return(MagickTrue);
1918}
1919
1920MagickBooleanType SyncExifProfile(Image *image,StringInfo *profile)
cristy3ed852e2009-09-05 21:47:34 +00001921{
1922#define MaxDirectoryStack 16
1923#define EXIF_DELIMITER "\n"
1924#define EXIF_NUM_FORMATS 12
1925#define TAG_EXIF_OFFSET 0x8769
1926#define TAG_INTEROP_OFFSET 0xa005
1927
1928 typedef struct _DirectoryInfo
1929 {
1930 unsigned char
1931 *directory;
1932
cristybb503372010-05-27 20:51:26 +00001933 size_t
cristy3ed852e2009-09-05 21:47:34 +00001934 entry;
1935 } DirectoryInfo;
1936
1937 DirectoryInfo
1938 directory_stack[MaxDirectoryStack];
1939
1940 EndianType
1941 endian;
1942
cristy3ed852e2009-09-05 21:47:34 +00001943 size_t
cristyba978e12010-09-12 20:26:50 +00001944 entry,
1945 length,
1946 number_entries;
cristy3ed852e2009-09-05 21:47:34 +00001947
cristy9d314ff2011-03-09 01:30:28 +00001948 ssize_t
1949 id,
cristy82544b92012-02-28 20:11:42 +00001950 level,
1951 offset;
cristy9d314ff2011-03-09 01:30:28 +00001952
cristy3ed852e2009-09-05 21:47:34 +00001953 static int
1954 format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
1955
cristy3ed852e2009-09-05 21:47:34 +00001956 unsigned char
1957 *directory,
1958 *exif;
1959
cristy3ed852e2009-09-05 21:47:34 +00001960 /*
1961 Set EXIF resolution tag.
1962 */
cristy3ed852e2009-09-05 21:47:34 +00001963 length=GetStringInfoLength(profile);
1964 exif=GetStringInfoDatum(profile);
cristy3ed852e2009-09-05 21:47:34 +00001965 if (length < 16)
1966 return(MagickFalse);
cristycee97112010-05-28 00:44:52 +00001967 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
dirk0c558772014-03-01 17:38:22 +00001968 if ((id != 0x4949) && (id != 0x4D4D))
1969 {
1970 while (length != 0)
1971 {
1972 if (ReadProfileByte(&exif,&length) != 0x45)
1973 continue;
1974 if (ReadProfileByte(&exif,&length) != 0x78)
1975 continue;
1976 if (ReadProfileByte(&exif,&length) != 0x69)
1977 continue;
1978 if (ReadProfileByte(&exif,&length) != 0x66)
1979 continue;
1980 if (ReadProfileByte(&exif,&length) != 0x00)
1981 continue;
1982 if (ReadProfileByte(&exif,&length) != 0x00)
1983 continue;
1984 break;
1985 }
1986 if (length < 16)
1987 return(MagickFalse);
1988 id=(ssize_t) ReadProfileShort(LSBEndian,exif);
1989 }
cristy3ed852e2009-09-05 21:47:34 +00001990 endian=LSBEndian;
1991 if (id == 0x4949)
1992 endian=LSBEndian;
1993 else
1994 if (id == 0x4D4D)
1995 endian=MSBEndian;
1996 else
1997 return(MagickFalse);
1998 if (ReadProfileShort(endian,exif+2) != 0x002a)
1999 return(MagickFalse);
2000 /*
2001 This the offset to the first IFD.
2002 */
cristy82544b92012-02-28 20:11:42 +00002003 offset=(ssize_t) ((int) ReadProfileLong(endian,exif+4));
cristy64de1572012-02-29 02:44:11 +00002004 if ((offset < 0) || (size_t) offset >= length)
cristy3ed852e2009-09-05 21:47:34 +00002005 return(MagickFalse);
2006 directory=exif+offset;
2007 level=0;
2008 entry=0;
2009 do
2010 {
2011 if (level > 0)
2012 {
2013 level--;
2014 directory=directory_stack[level].directory;
2015 entry=directory_stack[level].entry;
2016 }
2017 /*
2018 Determine how many entries there are in the current IFD.
2019 */
2020 number_entries=ReadProfileShort(endian,directory);
2021 for ( ; entry < number_entries; entry++)
2022 {
cristyba978e12010-09-12 20:26:50 +00002023 int
2024 components;
2025
cristy3ed852e2009-09-05 21:47:34 +00002026 register unsigned char
2027 *p,
2028 *q;
2029
2030 size_t
2031 number_bytes;
2032
cristy9d314ff2011-03-09 01:30:28 +00002033 ssize_t
2034 format,
2035 tag_value;
2036
cristy3ed852e2009-09-05 21:47:34 +00002037 q=(unsigned char *) (directory+2+(12*entry));
cristybb503372010-05-27 20:51:26 +00002038 tag_value=(ssize_t) ReadProfileShort(endian,q);
2039 format=(ssize_t) ReadProfileShort(endian,q+2);
cristy3ed852e2009-09-05 21:47:34 +00002040 if ((format-1) >= EXIF_NUM_FORMATS)
2041 break;
cristy4d14d592012-04-03 12:58:43 +00002042 components=(ssize_t) ((int) ReadProfileLong(endian,q+4));
cristy3ed852e2009-09-05 21:47:34 +00002043 number_bytes=(size_t) components*format_bytes[format];
cristyd228c032012-06-03 15:01:15 +00002044 if ((ssize_t) number_bytes < components)
cristy4d14d592012-04-03 12:58:43 +00002045 break; /* prevent overflow */
cristy3ed852e2009-09-05 21:47:34 +00002046 if (number_bytes <= 4)
2047 p=q+8;
2048 else
2049 {
cristy82544b92012-02-28 20:11:42 +00002050 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002051 offset;
2052
2053 /*
2054 The directory entry contains an offset.
2055 */
cristy82544b92012-02-28 20:11:42 +00002056 offset=(ssize_t) ((int) ReadProfileLong(endian,q+8));
cristy3d8d1f12012-02-29 01:59:28 +00002057 if ((size_t) (offset+number_bytes) > length)
cristy3ed852e2009-09-05 21:47:34 +00002058 continue;
cristy3d8d1f12012-02-29 01:59:28 +00002059 if (~length < number_bytes)
2060 continue; /* prevent overflow */
cristy3ed852e2009-09-05 21:47:34 +00002061 p=(unsigned char *) (exif+offset);
2062 }
2063 switch (tag_value)
2064 {
2065 case 0x011a:
2066 {
cristy82544b92012-02-28 20:11:42 +00002067 (void) WriteProfileLong(endian,(size_t) (image->resolution.x+0.5),p);
cristy3ed852e2009-09-05 21:47:34 +00002068 (void) WriteProfileLong(endian,1UL,p+4);
2069 break;
2070 }
2071 case 0x011b:
2072 {
cristy82544b92012-02-28 20:11:42 +00002073 (void) WriteProfileLong(endian,(size_t) (image->resolution.y+0.5),p);
cristy3ed852e2009-09-05 21:47:34 +00002074 (void) WriteProfileLong(endian,1UL,p+4);
2075 break;
2076 }
2077 case 0x0112:
2078 {
cristy91698032012-04-06 17:02:52 +00002079 if (number_bytes == 4)
2080 {
2081 (void) WriteProfileLong(endian,(size_t) image->orientation,p);
2082 break;
2083 }
cristy82544b92012-02-28 20:11:42 +00002084 (void) WriteProfileShort(endian,(unsigned short) image->orientation,
2085 p);
cristy3ed852e2009-09-05 21:47:34 +00002086 break;
2087 }
2088 case 0x0128:
2089 {
cristy91698032012-04-06 17:02:52 +00002090 if (number_bytes == 4)
2091 {
2092 (void) WriteProfileLong(endian,(size_t) (image->units+1),p);
2093 break;
2094 }
cristy82544b92012-02-28 20:11:42 +00002095 (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
cristy3ed852e2009-09-05 21:47:34 +00002096 break;
2097 }
2098 default:
2099 break;
2100 }
2101 if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
2102 {
cristy82544b92012-02-28 20:11:42 +00002103 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002104 offset;
2105
cristy82544b92012-02-28 20:11:42 +00002106 offset=(ssize_t) ((int) ReadProfileLong(endian,p));
cristy3d8d1f12012-02-29 01:59:28 +00002107 if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
cristy3ed852e2009-09-05 21:47:34 +00002108 {
2109 directory_stack[level].directory=directory;
2110 entry++;
2111 directory_stack[level].entry=entry;
2112 level++;
2113 directory_stack[level].directory=exif+offset;
2114 directory_stack[level].entry=0;
2115 level++;
2116 if ((directory+2+(12*number_entries)) > (exif+length))
2117 break;
cristy82544b92012-02-28 20:11:42 +00002118 offset=(ssize_t) ((int) ReadProfileLong(endian,directory+2+(12*
2119 number_entries)));
cristy3d8d1f12012-02-29 01:59:28 +00002120 if ((offset != 0) && ((size_t) offset < length) &&
cristy3ed852e2009-09-05 21:47:34 +00002121 (level < (MaxDirectoryStack-2)))
2122 {
2123 directory_stack[level].directory=exif+offset;
2124 directory_stack[level].entry=0;
2125 level++;
2126 }
2127 }
2128 break;
2129 }
2130 }
2131 } while (level > 0);
2132 return(MagickTrue);
2133}
dirk5bfa82b2014-03-01 11:22:55 +00002134
dirk11b14152014-03-01 19:06:16 +00002135MagickPrivate MagickBooleanType SyncImageProfiles(Image *image)
dirk5bfa82b2014-03-01 11:22:55 +00002136{
2137 MagickBooleanType
2138 status;
2139
2140 StringInfo
2141 *profile;
2142
2143 status=MagickTrue;
2144 profile=(StringInfo *) GetImageProfile(image,"8BIM");
2145 if (profile != (StringInfo *) NULL)
2146 if (Sync8BimProfile(image,profile) == MagickFalse)
2147 status=MagickFalse;
2148 profile=(StringInfo *) GetImageProfile(image,"EXIF");
2149 if (profile != (StringInfo *) NULL)
2150 if (SyncExifProfile(image,profile) == MagickFalse)
2151 status=MagickFalse;
2152 return(status);
cristy3fb79582014-03-13 17:04:24 +00002153}