~noskcaj/ubuntu/saucy/imagemagick/lp1218248

« back to all changes in this revision

Viewing changes to .pc/0002-Fix-security-bug-685903-libmagick-5-Fails-an-asserti.patch/magick/profile.c

  • Committer: Package Import Robot
  • Author(s): Micah Gersten
  • Date: 2013-01-22 21:38:05 UTC
  • mfrom: (6.2.27 sid)
  • Revision ID: package-import@ubuntu.com-20130122213805-uoar6wqz8fc0dvvl
Tags: 8:6.7.7.10-5ubuntu1
* Merge from Debian unstable. (LP: #1079209) Remaining changes:
  - Make ufraw-batch (universe) a suggestion instead of a recommendation.
  - Don't set MAKEFLAGS in debian/rules; just pass it to the build.
  - Build-Depend on libtiff5-dev instead of libtiff-dev
  - Depend on fftw3-dev as it's in main, not fftw-dev.
  - Don't build depend on graphicsmagick-imagemagick-compat (universe)
  - Don't use graphicmagick's convert executable just to convert our
    svg into a menu xpm.  Instead, run the convert we build.
* Mark Vcs-* as XS-Debian-Vcs-*
  - update debian/control

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
3
%                                                                             %
 
4
%                                                                             %
 
5
%                                                                             %
 
6
%               PPPP   RRRR    OOO   FFFFF  IIIII  L      EEEEE               %
 
7
%               P   P  R   R  O   O  F        I    L      E                   %
 
8
%               PPPP   RRRR   O   O  FFF      I    L      EEE                 %
 
9
%               P      R R    O   O  F        I    L      E                   %
 
10
%               P      R  R    OOO   F      IIIII  LLLLL  EEEEE               %
 
11
%                                                                             %
 
12
%                                                                             %
 
13
%                       MagickCore Image Profile Methods                      %
 
14
%                                                                             %
 
15
%                              Software Design                                %
 
16
%                                John Cristy                                  %
 
17
%                                 July 1992                                   %
 
18
%                                                                             %
 
19
%                                                                             %
 
20
%  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
 
21
%  dedicated to making software imaging solutions freely available.           %
 
22
%                                                                             %
 
23
%  You may not use this file except in compliance with the License.  You may  %
 
24
%  obtain a copy of the License at                                            %
 
25
%                                                                             %
 
26
%    http://www.imagemagick.org/script/license.php                            %
 
27
%                                                                             %
 
28
%  Unless required by applicable law or agreed to in writing, software        %
 
29
%  distributed under the License is distributed on an "AS IS" BASIS,          %
 
30
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
 
31
%  See the License for the specific language governing permissions and        %
 
32
%  limitations under the License.                                             %
 
33
%                                                                             %
 
34
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
35
%
 
36
%
 
37
*/
 
38
 
 
39
/*
 
40
  Include declarations.
 
41
*/
 
42
#include "magick/studio.h"
 
43
#include "magick/cache.h"
 
44
#include "magick/color.h"
 
45
#include "magick/colorspace-private.h"
 
46
#include "magick/configure.h"
 
47
#include "magick/exception.h"
 
48
#include "magick/exception-private.h"
 
49
#include "magick/hashmap.h"
 
50
#include "magick/image.h"
 
51
#include "magick/memory_.h"
 
52
#include "magick/monitor.h"
 
53
#include "magick/monitor-private.h"
 
54
#include "magick/option.h"
 
55
#include "magick/profile.h"
 
56
#include "magick/property.h"
 
57
#include "magick/quantum.h"
 
58
#include "magick/quantum-private.h"
 
59
#include "magick/resource_.h"
 
60
#include "magick/splay-tree.h"
 
61
#include "magick/string_.h"
 
62
#include "magick/thread-private.h"
 
63
#include "magick/token.h"
 
64
#include "magick/utility.h"
 
65
#if defined(MAGICKCORE_LCMS_DELEGATE)
 
66
#if defined(MAGICKCORE_HAVE_LCMS_LCMS2_H)
 
67
#include <wchar.h>
 
68
#include <lcms/lcms2.h>
 
69
#elif defined(MAGICKCORE_HAVE_LCMS2_H)
 
70
#include <wchar.h>
 
71
#include "lcms2.h"
 
72
#elif defined(MAGICKCORE_HAVE_LCMS_LCMS_H)
 
73
#include <lcms/lcms.h>
 
74
#else
 
75
#include "lcms.h"
 
76
#endif
 
77
#endif
 
78
 
 
79
/*
 
80
  Define declarations.
 
81
*/
 
82
#if !defined(LCMS_VERSION) || (LCMS_VERSION < 2000)
 
83
#define cmsSigCmykData icSigCmykData
 
84
#define cmsSigGrayData icSigGrayData
 
85
#define cmsSigLabData icSigLabData
 
86
#define cmsSigLuvData icSigLuvData
 
87
#define cmsSigRgbData icSigRgbData
 
88
#define cmsSigXYZData icSigXYZData
 
89
#define cmsSigYCbCrData icSigYCbCrData
 
90
#define cmsSigLinkClass icSigLinkClass
 
91
#define cmsColorSpaceSignature icColorSpaceSignature
 
92
#define cmsUInt32Number  DWORD
 
93
#define cmsSetLogErrorHandler(handler)  cmsSetErrorHandler(handler)
 
94
#define cmsCreateTransformTHR(context,source_profile,source_type, \
 
95
  target_profile,target_type,intent,flags)  cmsCreateTransform(source_profile, \
 
96
  source_type,target_profile,target_type,intent,flags);
 
97
#define cmsOpenProfileFromMemTHR(context,profile,length) \
 
98
  cmsOpenProfileFromMem(profile,length)
 
99
#endif
 
100
 
 
101
/*
 
102
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
103
%                                                                             %
 
104
%                                                                             %
 
105
%                                                                             %
 
106
%   C l o n e I m a g e P r o f i l e s                                       %
 
107
%                                                                             %
 
108
%                                                                             %
 
109
%                                                                             %
 
110
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
111
%
 
112
%  CloneImageProfiles() clones one or more image profiles.
 
113
%
 
114
%  The format of the CloneImageProfiles method is:
 
115
%
 
116
%      MagickBooleanType CloneImageProfiles(Image *image,
 
117
%        const Image *clone_image)
 
118
%
 
119
%  A description of each parameter follows:
 
120
%
 
121
%    o image: the image.
 
122
%
 
123
%    o clone_image: the clone image.
 
124
%
 
125
*/
 
126
MagickExport MagickBooleanType CloneImageProfiles(Image *image,
 
127
  const Image *clone_image)
 
128
{
 
129
  assert(image != (Image *) NULL);
 
130
  assert(image->signature == MagickSignature);
 
131
  if (image->debug != MagickFalse)
 
132
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
 
133
  assert(clone_image != (const Image *) NULL);
 
134
  assert(clone_image->signature == MagickSignature);
 
135
  image->color_profile.length=clone_image->color_profile.length;
 
136
  image->color_profile.info=clone_image->color_profile.info;
 
137
  image->iptc_profile.length=clone_image->iptc_profile.length;
 
138
  image->iptc_profile.info=clone_image->iptc_profile.info;
 
139
  if (clone_image->profiles != (void *) NULL)
 
140
    image->profiles=CloneSplayTree((SplayTreeInfo *) clone_image->profiles,
 
141
      (void *(*)(void *)) ConstantString,(void *(*)(void *)) CloneStringInfo);
 
142
  return(MagickTrue);
 
143
}
 
144
 
 
145
/*
 
146
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
147
%                                                                             %
 
148
%                                                                             %
 
149
%                                                                             %
 
150
%   D e l e t e I m a g e P r o f i l e                                       %
 
151
%                                                                             %
 
152
%                                                                             %
 
153
%                                                                             %
 
154
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
155
%
 
156
%  DeleteImageProfile() deletes a profile from the image by its name.
 
157
%
 
158
%  The format of the DeleteImageProfile method is:
 
159
%
 
160
%      MagickBooleanTyupe DeleteImageProfile(Image *image,const char *name)
 
161
%
 
162
%  A description of each parameter follows:
 
163
%
 
164
%    o image: the image.
 
165
%
 
166
%    o name: the profile name.
 
167
%
 
168
*/
 
169
MagickExport MagickBooleanType DeleteImageProfile(Image *image,const char *name)
 
170
{
 
171
  assert(image != (Image *) NULL);
 
172
  assert(image->signature == MagickSignature);
 
173
  if (image->debug != MagickFalse)
 
174
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
 
175
  if (image->profiles == (SplayTreeInfo *) NULL)
 
176
    return(MagickFalse);
 
177
  if (LocaleCompare(name,"icc") == 0)
 
178
    {
 
179
      /*
 
180
        Continue to support deprecated color profile for now.
 
181
      */
 
182
      image->color_profile.length=0;
 
183
      image->color_profile.info=(unsigned char *) NULL;
 
184
    }
 
185
  if (LocaleCompare(name,"iptc") == 0)
 
186
    {
 
187
      /*
 
188
        Continue to support deprecated IPTC profile for now.
 
189
      */
 
190
      image->iptc_profile.length=0;
 
191
      image->iptc_profile.info=(unsigned char *) NULL;
 
192
    }
 
193
  return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->profiles,name));
 
194
}
 
195
 
 
196
/*
 
197
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
198
%                                                                             %
 
199
%                                                                             %
 
200
%                                                                             %
 
201
%   D e s t r o y I m a g e P r o f i l e s                                   %
 
202
%                                                                             %
 
203
%                                                                             %
 
204
%                                                                             %
 
205
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
206
%
 
207
%  DestroyImageProfiles() releases memory associated with an image profile map.
 
208
%
 
209
%  The format of the DestroyProfiles method is:
 
210
%
 
211
%      void DestroyImageProfiles(Image *image)
 
212
%
 
213
%  A description of each parameter follows:
 
214
%
 
215
%    o image: the image.
 
216
%
 
217
*/
 
218
MagickExport 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
*/
 
248
MagickExport const StringInfo *GetImageProfile(const Image *image,
 
249
  const char *name)
 
250
{
 
251
  char
 
252
    key[MaxTextExtent];
 
253
 
 
254
  const StringInfo
 
255
    *profile;
 
256
 
 
257
  assert(image != (Image *) NULL);
 
258
  assert(image->signature == MagickSignature);
 
259
  if (image->debug != MagickFalse)
 
260
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
 
261
  if (image->profiles == (SplayTreeInfo *) NULL)
 
262
    return((StringInfo *) NULL);
 
263
  (void) CopyMagickString(key,name,MaxTextExtent);
 
264
  profile=(const StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
 
265
    image->profiles,key);
 
266
  return(profile);
 
267
}
 
268
 
 
269
/*
 
270
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
271
%                                                                             %
 
272
%                                                                             %
 
273
%                                                                             %
 
274
%   G e t N e x t I m a g e P r o f i l e                                     %
 
275
%                                                                             %
 
276
%                                                                             %
 
277
%                                                                             %
 
278
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
279
%
 
280
%  GetNextImageProfile() gets the next profile name for an image.
 
281
%
 
282
%  The format of the GetNextImageProfile method is:
 
283
%
 
284
%      char *GetNextImageProfile(const Image *image)
 
285
%
 
286
%  A description of each parameter follows:
 
287
%
 
288
%    o hash_info: the hash info.
 
289
%
 
290
*/
 
291
MagickExport char *GetNextImageProfile(const Image *image)
 
292
{
 
293
  assert(image != (Image *) NULL);
 
294
  assert(image->signature == MagickSignature);
 
295
  if (image->debug != MagickFalse)
 
296
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
 
297
  if (image->profiles == (SplayTreeInfo *) NULL)
 
298
    return((char *) NULL);
 
299
  return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->profiles));
 
300
}
 
301
 
 
302
/*
 
303
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
304
%                                                                             %
 
305
%                                                                             %
 
306
%                                                                             %
 
307
%   P r o f i l e I m a g e                                                   %
 
308
%                                                                             %
 
309
%                                                                             %
 
310
%                                                                             %
 
311
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
312
%
 
313
%  ProfileImage() associates, applies, or removes an ICM, IPTC, or generic
 
314
%  profile with / to / from an image.  If the profile is NULL, it is removed
 
315
%  from the image otherwise added or applied.  Use a name of '*' and a profile
 
316
%  of NULL to remove all profiles from the image.
 
317
%
 
318
%  ICC and ICM profiles are handled as follows: If the image does not have
 
319
%  an associated color profile, the one you provide is associated with the
 
320
%  image and the image pixels are not transformed.  Otherwise, the colorspace
 
321
%  transform defined by the existing and new profile are applied to the image
 
322
%  pixels and the new profile is associated with the image.
 
323
%
 
324
%  The format of the ProfileImage method is:
 
325
%
 
326
%      MagickBooleanType ProfileImage(Image *image,const char *name,
 
327
%        const void *datum,const size_t length,const MagickBooleanType clone)
 
328
%
 
329
%  A description of each parameter follows:
 
330
%
 
331
%    o image: the image.
 
332
%
 
333
%    o name: Name of profile to add or remove: ICC, IPTC, or generic profile.
 
334
%
 
335
%    o datum: the profile data.
 
336
%
 
337
%    o length: the length of the profile.
 
338
%
 
339
%    o clone: should be MagickFalse.
 
340
%
 
341
*/
 
342
 
 
343
#if defined(MAGICKCORE_LCMS_DELEGATE)
 
344
 
 
345
static unsigned short **DestroyPixelThreadSet(unsigned short **pixels)
 
346
{
 
347
  register ssize_t
 
348
    i;
 
349
 
 
350
  assert(pixels != (unsigned short **) NULL);
 
351
  for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
 
352
    if (pixels[i] != (unsigned short *) NULL)
 
353
      pixels[i]=(unsigned short *) RelinquishMagickMemory(pixels[i]);
 
354
  pixels=(unsigned short **) RelinquishMagickMemory(pixels);
 
355
  return(pixels);
 
356
}
 
357
 
 
358
static unsigned short **AcquirePixelThreadSet(const size_t columns,
 
359
  const size_t channels)
 
360
{
 
361
  register ssize_t
 
362
    i;
 
363
 
 
364
  unsigned short
 
365
    **pixels;
 
366
 
 
367
  size_t
 
368
    number_threads;
 
369
 
 
370
  number_threads=GetOpenMPMaximumThreads();
 
371
  pixels=(unsigned short **) AcquireQuantumMemory(number_threads,
 
372
    sizeof(*pixels));
 
373
  if (pixels == (unsigned short **) NULL)
 
374
    return((unsigned short **) NULL);
 
375
  (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
 
376
  for (i=0; i < (ssize_t) number_threads; i++)
 
377
  {
 
378
    pixels[i]=(unsigned short *) AcquireQuantumMemory(columns,channels*
 
379
      sizeof(**pixels));
 
380
    if (pixels[i] == (unsigned short *) NULL)
 
381
      return(DestroyPixelThreadSet(pixels));
 
382
  }
 
383
  return(pixels);
 
384
}
 
385
 
 
386
static cmsHTRANSFORM *DestroyTransformThreadSet(cmsHTRANSFORM *transform)
 
387
{
 
388
  register ssize_t
 
389
    i;
 
390
 
 
391
  assert(transform != (cmsHTRANSFORM *) NULL);
 
392
  for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
 
393
    if (transform[i] != (cmsHTRANSFORM) NULL)
 
394
      cmsDeleteTransform(transform[i]);
 
395
  transform=(cmsHTRANSFORM *) RelinquishMagickMemory(transform);
 
396
  return(transform);
 
397
}
 
398
 
 
399
static cmsHTRANSFORM *AcquireTransformThreadSet(Image *image,
 
400
  const cmsHPROFILE source_profile,const cmsUInt32Number source_type,
 
401
  const cmsHPROFILE target_profile,const cmsUInt32Number target_type,
 
402
  const int intent,const cmsUInt32Number flags)
 
403
{
 
404
  cmsHTRANSFORM
 
405
    *transform;
 
406
 
 
407
  register ssize_t
 
408
    i;
 
409
 
 
410
  size_t
 
411
    number_threads;
 
412
 
 
413
  number_threads=GetOpenMPMaximumThreads();
 
414
  transform=(cmsHTRANSFORM *) AcquireQuantumMemory(number_threads,
 
415
    sizeof(*transform));
 
416
  if (transform == (cmsHTRANSFORM *) NULL)
 
417
    return((cmsHTRANSFORM *) NULL);
 
418
  (void) ResetMagickMemory(transform,0,number_threads*sizeof(*transform));
 
419
  for (i=0; i < (ssize_t) number_threads; i++)
 
420
  {
 
421
    transform[i]=cmsCreateTransformTHR(image,source_profile,source_type,
 
422
      target_profile,target_type,intent,flags);
 
423
    if (transform[i] == (cmsHTRANSFORM) NULL)
 
424
      return(DestroyTransformThreadSet(transform));
 
425
  }
 
426
  return(transform);
 
427
}
 
428
#endif
 
429
 
 
430
#if defined(MAGICKCORE_LCMS_DELEGATE)
 
431
#if defined(LCMS_VERSION) && (LCMS_VERSION >= 2000)
 
432
static void LCMSExceptionHandler(cmsContext context,cmsUInt32Number severity,
 
433
  const char *message)
 
434
{
 
435
  Image
 
436
    *image;
 
437
 
 
438
  (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%u, %s",
 
439
    severity,message != (char *) NULL ? message : "no message");
 
440
  image=(Image *) context;
 
441
  if (image != (Image *) NULL)
 
442
    (void) ThrowMagickException(&image->exception,GetMagickModule(),
 
443
      ImageWarning,"UnableToTransformColorspace","`%s'",image->filename);
 
444
 
 
445
}
 
446
#else
 
447
static int LCMSExceptionHandler(int severity,const char *message)
 
448
{
 
449
  (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%d, %s",
 
450
    severity,message != (char *) NULL ? message : "no message");
 
451
  return(1);
 
452
}
 
453
#endif
 
454
#endif
 
455
 
 
456
MagickExport MagickBooleanType ProfileImage(Image *image,const char *name,
 
457
  const void *datum,const size_t length,
 
458
  const MagickBooleanType magick_unused(clone))
 
459
{
 
460
#define ProfileImageTag  "Profile/Image"
 
461
#define ThrowProfileException(severity,tag,context) \
 
462
{ \
 
463
  if (source_profile != (cmsHPROFILE) NULL) \
 
464
    (void) cmsCloseProfile(source_profile); \
 
465
  if (target_profile != (cmsHPROFILE) NULL) \
 
466
    (void) cmsCloseProfile(target_profile); \
 
467
  ThrowBinaryException(severity,tag,context); \
 
468
}
 
469
 
 
470
  MagickBooleanType
 
471
    status;
 
472
 
 
473
  StringInfo
 
474
    *profile;
 
475
 
 
476
  assert(image != (Image *) NULL);
 
477
  assert(image->signature == MagickSignature);
 
478
  if (image->debug != MagickFalse)
 
479
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
 
480
  assert(name != (const char *) NULL);
 
481
  if ((datum == (const void *) NULL) || (length == 0))
 
482
    {
 
483
      char
 
484
        **arguments,
 
485
        *names;
 
486
 
 
487
      int
 
488
        number_arguments;
 
489
 
 
490
      register ssize_t
 
491
        i;
 
492
 
 
493
      /*
 
494
        Delete image profile(s).
 
495
      */
 
496
      names=ConstantString(name);
 
497
      (void) SubstituteString(&names,","," ");
 
498
      arguments=StringToArgv(names,&number_arguments);
 
499
      names=DestroyString(names);
 
500
      if (arguments == (char **) NULL)
 
501
        return(MagickTrue);
 
502
      ResetImageProfileIterator(image);
 
503
      for (name=GetNextImageProfile(image); name != (const char *) NULL; )
 
504
      {
 
505
        for (i=1; i < (ssize_t) number_arguments; i++)
 
506
        {
 
507
          if ((*arguments[i] == '!') &&
 
508
              (LocaleCompare(name,arguments[i]+1) == 0))
 
509
            break;
 
510
          if (GlobExpression(name,arguments[i],MagickTrue) != MagickFalse)
 
511
            {
 
512
              (void) DeleteImageProfile(image,name);
 
513
              ResetImageProfileIterator(image);
 
514
              break;
 
515
            }
 
516
        }
 
517
        name=GetNextImageProfile(image);
 
518
      }
 
519
      for (i=0; i < (ssize_t) number_arguments; i++)
 
520
        arguments[i]=DestroyString(arguments[i]);
 
521
      arguments=(char **) RelinquishMagickMemory(arguments);
 
522
      return(MagickTrue);
 
523
    }
 
524
  /*
 
525
    Add a ICC, IPTC, or generic profile to the image.
 
526
  */
 
527
  status=MagickTrue;
 
528
  profile=AcquireStringInfo((size_t) length);
 
529
  SetStringInfoDatum(profile,(unsigned char *) datum);
 
530
  if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
 
531
    status=SetImageProfile(image,name,profile);
 
532
  else
 
533
    {
 
534
      const StringInfo
 
535
        *icc_profile;
 
536
 
 
537
      icc_profile=GetImageProfile(image,"icc");
 
538
      if ((icc_profile != (const StringInfo *) NULL) &&
 
539
          (CompareStringInfo(icc_profile,profile) == 0))
 
540
        {
 
541
          const char
 
542
            *value;
 
543
 
 
544
          value=GetImageProperty(image,"exif:ColorSpace");
 
545
          (void) value;
 
546
          /* Future.
 
547
          if (LocaleCompare(value,"1") != 0)
 
548
            (void) SetsRGBImageProfile(image);
 
549
          value=GetImageProperty(image,"exif:InteroperabilityIndex");
 
550
          if (LocaleCompare(value,"R98.") != 0)
 
551
            (void) SetsRGBImageProfile(image);
 
552
          value=GetImageProperty(image,"exif:InteroperabilityIndex");
 
553
          if (LocaleCompare(value,"R03.") != 0)
 
554
            (void) SetAdobeRGB1998ImageProfile(image);
 
555
          */
 
556
          icc_profile=GetImageProfile(image,"icc");
 
557
        }
 
558
      if ((icc_profile != (const StringInfo *) NULL) &&
 
559
          (CompareStringInfo(icc_profile,profile) == 0))
 
560
        {
 
561
          profile=DestroyStringInfo(profile);
 
562
          return(MagickTrue);
 
563
        }
 
564
#if !defined(MAGICKCORE_LCMS_DELEGATE)
 
565
      (void) ThrowMagickException(&image->exception,GetMagickModule(),
 
566
        MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (LCMS)",
 
567
        image->filename);
 
568
#else
 
569
      {
 
570
        cmsHPROFILE
 
571
          source_profile;
 
572
 
 
573
        /*
 
574
          Transform pixel colors as defined by the color profiles.
 
575
        */
 
576
        cmsSetLogErrorHandler(LCMSExceptionHandler);
 
577
        source_profile=cmsOpenProfileFromMemTHR(image,
 
578
          GetStringInfoDatum(profile),(cmsUInt32Number)
 
579
          GetStringInfoLength(profile));
 
580
        if (source_profile == (cmsHPROFILE) NULL)
 
581
          ThrowBinaryException(ResourceLimitError,
 
582
            "ColorspaceColorProfileMismatch",name);
 
583
        if ((cmsGetDeviceClass(source_profile) != cmsSigLinkClass) &&
 
584
            (icc_profile == (StringInfo *) NULL))
 
585
          status=SetImageProfile(image,name,profile);
 
586
        else
 
587
          {
 
588
            CacheView
 
589
              *image_view;
 
590
 
 
591
            ColorspaceType
 
592
              source_colorspace,
 
593
              target_colorspace;
 
594
 
 
595
            cmsColorSpaceSignature
 
596
              signature;
 
597
 
 
598
            cmsHPROFILE
 
599
              target_profile;
 
600
 
 
601
            cmsHTRANSFORM
 
602
              *restrict transform;
 
603
 
 
604
            cmsUInt32Number
 
605
              flags,
 
606
              source_type,
 
607
              target_type;
 
608
 
 
609
            ExceptionInfo
 
610
              *exception;
 
611
 
 
612
            int
 
613
              intent;
 
614
 
 
615
            MagickBooleanType
 
616
              status;
 
617
 
 
618
            MagickOffsetType
 
619
              progress;
 
620
 
 
621
            size_t
 
622
              source_channels,
 
623
              target_channels;
 
624
 
 
625
            ssize_t
 
626
              y;
 
627
 
 
628
            unsigned short
 
629
              **restrict source_pixels,
 
630
              **restrict target_pixels;
 
631
 
 
632
            exception=(&image->exception);
 
633
            target_profile=(cmsHPROFILE) NULL;
 
634
            if (icc_profile != (StringInfo *) NULL)
 
635
              {
 
636
                target_profile=source_profile;
 
637
                source_profile=cmsOpenProfileFromMemTHR(image,
 
638
                  GetStringInfoDatum(icc_profile),(cmsUInt32Number)
 
639
                  GetStringInfoLength(icc_profile));
 
640
                if (source_profile == (cmsHPROFILE) NULL)
 
641
                  ThrowProfileException(ResourceLimitError,
 
642
                    "ColorspaceColorProfileMismatch",name);
 
643
              }
 
644
            switch (cmsGetColorSpace(source_profile))
 
645
            {
 
646
              case cmsSigCmykData:
 
647
              {
 
648
                source_colorspace=CMYKColorspace;
 
649
                source_type=(cmsUInt32Number) TYPE_CMYK_16;
 
650
                source_channels=4;
 
651
                break;
 
652
              }
 
653
              case cmsSigGrayData:
 
654
              {
 
655
                source_colorspace=GRAYColorspace;
 
656
                source_type=(cmsUInt32Number) TYPE_GRAY_16;
 
657
                source_channels=1;
 
658
                break;
 
659
              }
 
660
              case cmsSigLabData:
 
661
              {
 
662
                source_colorspace=LabColorspace;
 
663
                source_type=(cmsUInt32Number) TYPE_Lab_16;
 
664
                source_channels=3;
 
665
                break;
 
666
              }
 
667
              case cmsSigLuvData:
 
668
              {
 
669
                source_colorspace=YUVColorspace;
 
670
                source_type=(cmsUInt32Number) TYPE_YUV_16;
 
671
                source_channels=3;
 
672
                break;
 
673
              }
 
674
              case cmsSigRgbData:
 
675
              {
 
676
                source_colorspace=sRGBColorspace;
 
677
                source_type=(cmsUInt32Number) TYPE_RGB_16;
 
678
                source_channels=3;
 
679
                break;
 
680
              }
 
681
              case cmsSigXYZData:
 
682
              {
 
683
                source_colorspace=XYZColorspace;
 
684
                source_type=(cmsUInt32Number) TYPE_XYZ_16;
 
685
                source_channels=3;
 
686
                break;
 
687
              }
 
688
              case cmsSigYCbCrData:
 
689
              {
 
690
                source_colorspace=YCbCrColorspace;
 
691
                source_type=(cmsUInt32Number) TYPE_YCbCr_16;
 
692
                source_channels=3;
 
693
                break;
 
694
              }
 
695
              default:
 
696
              {
 
697
                source_colorspace=UndefinedColorspace;
 
698
                source_type=(cmsUInt32Number) TYPE_RGB_16;
 
699
                source_channels=3;
 
700
                break;
 
701
              }
 
702
            }
 
703
            signature=cmsGetPCS(source_profile);
 
704
            if (target_profile != (cmsHPROFILE) NULL)
 
705
              signature=cmsGetColorSpace(target_profile);
 
706
            switch (signature)
 
707
            {
 
708
              case cmsSigCmykData:
 
709
              {
 
710
                target_colorspace=CMYKColorspace;
 
711
                target_type=(cmsUInt32Number) TYPE_CMYK_16;
 
712
                target_channels=4;
 
713
                break;
 
714
              }
 
715
              case cmsSigLabData:
 
716
              {
 
717
                target_colorspace=LabColorspace;
 
718
                target_type=(cmsUInt32Number) TYPE_Lab_16;
 
719
                target_channels=3;
 
720
                break;
 
721
              }
 
722
              case cmsSigGrayData:
 
723
              {
 
724
                target_colorspace=GRAYColorspace;
 
725
                target_type=(cmsUInt32Number) TYPE_GRAY_16;
 
726
                target_channels=1;
 
727
                break;
 
728
              }
 
729
              case cmsSigLuvData:
 
730
              {
 
731
                target_colorspace=YUVColorspace;
 
732
                target_type=(cmsUInt32Number) TYPE_YUV_16;
 
733
                target_channels=3;
 
734
                break;
 
735
              }
 
736
              case cmsSigRgbData:
 
737
              {
 
738
                target_colorspace=sRGBColorspace;
 
739
                target_type=(cmsUInt32Number) TYPE_RGB_16;
 
740
                target_channels=3;
 
741
                break;
 
742
              }
 
743
              case cmsSigXYZData:
 
744
              {
 
745
                target_colorspace=XYZColorspace;
 
746
                target_type=(cmsUInt32Number) TYPE_XYZ_16;
 
747
                target_channels=3;
 
748
                break;
 
749
              }
 
750
              case cmsSigYCbCrData:
 
751
              {
 
752
                target_colorspace=YCbCrColorspace;
 
753
                target_type=(cmsUInt32Number) TYPE_YCbCr_16;
 
754
                target_channels=3;
 
755
                break;
 
756
              }
 
757
              default:
 
758
              {
 
759
                target_colorspace=UndefinedColorspace;
 
760
                target_type=(cmsUInt32Number) TYPE_RGB_16;
 
761
                target_channels=3;
 
762
                break;
 
763
              }
 
764
            }
 
765
            if ((source_colorspace == UndefinedColorspace) ||
 
766
                (target_colorspace == UndefinedColorspace))
 
767
              ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
 
768
                name);
 
769
             if ((source_colorspace == GRAYColorspace) &&
 
770
                 (IsGrayImage(image,exception) == MagickFalse))
 
771
              ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
 
772
                name);
 
773
             if ((source_colorspace == CMYKColorspace) &&
 
774
                 (image->colorspace != CMYKColorspace))
 
775
              ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
 
776
                name);
 
777
             if ((source_colorspace == XYZColorspace) &&
 
778
                 (image->colorspace != XYZColorspace))
 
779
              ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
 
780
                name);
 
781
             if ((source_colorspace == YCbCrColorspace) &&
 
782
                 (image->colorspace != YCbCrColorspace))
 
783
              ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
 
784
                name);
 
785
             if ((source_colorspace != CMYKColorspace) &&
 
786
                 (source_colorspace != GRAYColorspace) &&
 
787
                 (source_colorspace != LabColorspace) &&
 
788
                 (source_colorspace != XYZColorspace) &&
 
789
                 (source_colorspace != YCbCrColorspace) &&
 
790
                 (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse))
 
791
              ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
 
792
                name);
 
793
            switch (image->rendering_intent)
 
794
            {
 
795
              case AbsoluteIntent: intent=INTENT_ABSOLUTE_COLORIMETRIC; break;
 
796
              case PerceptualIntent: intent=INTENT_PERCEPTUAL; break;
 
797
              case RelativeIntent: intent=INTENT_RELATIVE_COLORIMETRIC; break;
 
798
              case SaturationIntent: intent=INTENT_SATURATION; break;
 
799
              default: intent=INTENT_PERCEPTUAL; break;
 
800
            }
 
801
            flags=cmsFLAGS_HIGHRESPRECALC;
 
802
#if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
 
803
            if (image->black_point_compensation != MagickFalse)
 
804
              flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
 
805
#endif
 
806
            transform=AcquireTransformThreadSet(image,source_profile,
 
807
              source_type,target_profile,target_type,intent,flags);
 
808
            if (transform == (cmsHTRANSFORM *) NULL)
 
809
              ThrowProfileException(ImageError,"UnableToCreateColorTransform",
 
810
                name);
 
811
            /*
 
812
              Transform image as dictated by the source & target image profiles.
 
813
            */
 
814
            source_pixels=AcquirePixelThreadSet(image->columns,source_channels);
 
815
            target_pixels=AcquirePixelThreadSet(image->columns,target_channels);
 
816
            if ((source_pixels == (unsigned short **) NULL) ||
 
817
                (target_pixels == (unsigned short **) NULL))
 
818
              {
 
819
                transform=DestroyTransformThreadSet(transform);
 
820
                ThrowProfileException(ResourceLimitError,
 
821
                  "MemoryAllocationFailed",image->filename);
 
822
              }
 
823
            if (SetImageStorageClass(image,DirectClass) == MagickFalse)
 
824
              {
 
825
                target_pixels=DestroyPixelThreadSet(target_pixels);
 
826
                source_pixels=DestroyPixelThreadSet(source_pixels);
 
827
                transform=DestroyTransformThreadSet(transform);
 
828
                if (source_profile != (cmsHPROFILE) NULL)
 
829
                  (void) cmsCloseProfile(source_profile);
 
830
                if (target_profile != (cmsHPROFILE) NULL)
 
831
                  (void) cmsCloseProfile(target_profile);
 
832
                return(MagickFalse);
 
833
              }
 
834
            if (target_colorspace == CMYKColorspace)
 
835
              (void) SetImageColorspace(image,target_colorspace);
 
836
            status=MagickTrue;
 
837
            progress=0;
 
838
            image_view=AcquireAuthenticCacheView(image,exception);
 
839
#if defined(MAGICKCORE_OPENMP_SUPPORT)
 
840
            #pragma omp parallel for schedule(static,4) shared(status) \
 
841
              dynamic_number_threads(image,image->columns,image->rows,1)
 
842
#endif
 
843
            for (y=0; y < (ssize_t) image->rows; y++)
 
844
            {
 
845
              const int
 
846
                id = GetOpenMPThreadId();
 
847
 
 
848
              MagickBooleanType
 
849
                sync;
 
850
 
 
851
              register IndexPacket
 
852
                *restrict indexes;
 
853
 
 
854
              register ssize_t
 
855
                x;
 
856
 
 
857
              register PixelPacket
 
858
                *restrict q;
 
859
 
 
860
              register unsigned short
 
861
                *p;
 
862
 
 
863
              if (status == MagickFalse)
 
864
                continue;
 
865
              q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
 
866
                exception);
 
867
              if (q == (PixelPacket *) NULL)
 
868
                {
 
869
                  status=MagickFalse;
 
870
                  continue;
 
871
                }
 
872
              indexes=GetCacheViewAuthenticIndexQueue(image_view);
 
873
              p=source_pixels[id];
 
874
              for (x=0; x < (ssize_t) image->columns; x++)
 
875
              {
 
876
                *p++=ScaleQuantumToShort(GetPixelRed(q));
 
877
                if (source_channels > 1)
 
878
                  {
 
879
                    *p++=ScaleQuantumToShort(GetPixelGreen(q));
 
880
                    *p++=ScaleQuantumToShort(GetPixelBlue(q));
 
881
                  }
 
882
                if (source_channels > 3)
 
883
                  *p++=ScaleQuantumToShort(GetPixelIndex(indexes+x));
 
884
                q++;
 
885
              }
 
886
              cmsDoTransform(transform[id],source_pixels[id],target_pixels[id],
 
887
                (unsigned int) image->columns);
 
888
              p=target_pixels[id];
 
889
              q-=image->columns;
 
890
              for (x=0; x < (ssize_t) image->columns; x++)
 
891
              {
 
892
                SetPixelRed(q,ScaleShortToQuantum(*p));
 
893
                SetPixelGreen(q,GetPixelRed(q));
 
894
                SetPixelBlue(q,GetPixelRed(q));
 
895
                p++;
 
896
                if (target_channels > 1)
 
897
                  {
 
898
                    SetPixelGreen(q,ScaleShortToQuantum(*p));
 
899
                    p++;
 
900
                    SetPixelBlue(q,ScaleShortToQuantum(*p));
 
901
                    p++;
 
902
                  }
 
903
                if (target_channels > 3)
 
904
                  {
 
905
                    SetPixelIndex(indexes+x,ScaleShortToQuantum(*p));
 
906
                    p++;
 
907
                  }
 
908
                q++;
 
909
              }
 
910
              sync=SyncCacheViewAuthenticPixels(image_view,exception);
 
911
              if (sync == MagickFalse)
 
912
                status=MagickFalse;
 
913
              if (image->progress_monitor != (MagickProgressMonitor) NULL)
 
914
                {
 
915
                  MagickBooleanType
 
916
                    proceed;
 
917
 
 
918
#if defined(MAGICKCORE_OPENMP_SUPPORT)
 
919
                  #pragma omp critical (MagickCore_ProfileImage)
 
920
#endif
 
921
                  proceed=SetImageProgress(image,ProfileImageTag,progress++,
 
922
                    image->rows);
 
923
                  if (proceed == MagickFalse)
 
924
                    status=MagickFalse;
 
925
                }
 
926
            }
 
927
            image_view=DestroyCacheView(image_view);
 
928
            (void) SetImageColorspace(image,target_colorspace);
 
929
            switch (signature)
 
930
            {
 
931
              case cmsSigRgbData:
 
932
              {
 
933
                image->type=image->matte == MagickFalse ? TrueColorType :
 
934
                  TrueColorMatteType;
 
935
                break;
 
936
              }
 
937
              case cmsSigCmykData:
 
938
              {
 
939
                image->type=image->matte == MagickFalse ? ColorSeparationType :
 
940
                  ColorSeparationMatteType;
 
941
                break;
 
942
              }
 
943
              case cmsSigGrayData:
 
944
              {
 
945
                image->type=image->matte == MagickFalse ? GrayscaleType :
 
946
                  GrayscaleMatteType;
 
947
                break;
 
948
              }
 
949
              default:
 
950
                break;
 
951
            }
 
952
            target_pixels=DestroyPixelThreadSet(target_pixels);
 
953
            source_pixels=DestroyPixelThreadSet(source_pixels);
 
954
            transform=DestroyTransformThreadSet(transform);
 
955
            if (cmsGetDeviceClass(source_profile) != cmsSigLinkClass)
 
956
              status=SetImageProfile(image,name,profile);
 
957
            if (target_profile != (cmsHPROFILE) NULL)
 
958
              (void) cmsCloseProfile(target_profile);
 
959
          }
 
960
        (void) cmsCloseProfile(source_profile);
 
961
      }
 
962
#endif
 
963
    }
 
964
  profile=DestroyStringInfo(profile);
 
965
  return(status);
 
966
}
 
967
 
 
968
/*
 
969
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
970
%                                                                             %
 
971
%                                                                             %
 
972
%                                                                             %
 
973
%   R e m o v e I m a g e P r o f i l e                                       %
 
974
%                                                                             %
 
975
%                                                                             %
 
976
%                                                                             %
 
977
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
978
%
 
979
%  RemoveImageProfile() removes a named profile from the image and returns its
 
980
%  value.
 
981
%
 
982
%  The format of the RemoveImageProfile method is:
 
983
%
 
984
%      void *RemoveImageProfile(Image *image,const char *name)
 
985
%
 
986
%  A description of each parameter follows:
 
987
%
 
988
%    o image: the image.
 
989
%
 
990
%    o name: the profile name.
 
991
%
 
992
*/
 
993
MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
 
994
{
 
995
  StringInfo
 
996
    *profile;
 
997
 
 
998
  assert(image != (Image *) NULL);
 
999
  assert(image->signature == MagickSignature);
 
1000
  if (image->debug != MagickFalse)
 
1001
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
 
1002
  if (image->profiles == (SplayTreeInfo *) NULL)
 
1003
    return((StringInfo *) NULL);
 
1004
  if (LocaleCompare(name,"icc") == 0)
 
1005
    {
 
1006
      /*
 
1007
        Continue to support deprecated color profile for now.
 
1008
      */
 
1009
      image->color_profile.length=0;
 
1010
      image->color_profile.info=(unsigned char *) NULL;
 
1011
    }
 
1012
  if (LocaleCompare(name,"iptc") == 0)
 
1013
    {
 
1014
      /*
 
1015
        Continue to support deprecated IPTC profile for now.
 
1016
      */
 
1017
      image->iptc_profile.length=0;
 
1018
      image->iptc_profile.info=(unsigned char *) NULL;
 
1019
    }
 
1020
  profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
 
1021
    image->profiles,name);
 
1022
  return(profile);
 
1023
}
 
1024
 
 
1025
/*
 
1026
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1027
%                                                                             %
 
1028
%                                                                             %
 
1029
%                                                                             %
 
1030
%   R e s e t P r o f i l e I t e r a t o r                                   %
 
1031
%                                                                             %
 
1032
%                                                                             %
 
1033
%                                                                             %
 
1034
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1035
%
 
1036
%  ResetImageProfileIterator() resets the image profile iterator.  Use it in
 
1037
%  conjunction with GetNextImageProfile() to iterate over all the profiles
 
1038
%  associated with an image.
 
1039
%
 
1040
%  The format of the ResetImageProfileIterator method is:
 
1041
%
 
1042
%      ResetImageProfileIterator(Image *image)
 
1043
%
 
1044
%  A description of each parameter follows:
 
1045
%
 
1046
%    o image: the image.
 
1047
%
 
1048
*/
 
1049
MagickExport void ResetImageProfileIterator(const Image *image)
 
1050
{
 
1051
  assert(image != (Image *) NULL);
 
1052
  assert(image->signature == MagickSignature);
 
1053
  if (image->debug != MagickFalse)
 
1054
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
 
1055
  if (image->profiles == (SplayTreeInfo *) NULL)
 
1056
    return;
 
1057
  ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
 
1058
}
 
1059
 
 
1060
/*
 
1061
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1062
%                                                                             %
 
1063
%                                                                             %
 
1064
%                                                                             %
 
1065
%   S e t I m a g e P r o f i l e                                             %
 
1066
%                                                                             %
 
1067
%                                                                             %
 
1068
%                                                                             %
 
1069
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1070
%
 
1071
%  SetImageProfile() adds a named profile to the image.  If a profile with the
 
1072
%  same name already exists, it is replaced.  This method differs from the
 
1073
%  ProfileImage() method in that it does not apply CMS color profiles.
 
1074
%
 
1075
%  The format of the SetImageProfile method is:
 
1076
%
 
1077
%      MagickBooleanType SetImageProfile(Image *image,const char *name,
 
1078
%        const StringInfo *profile)
 
1079
%
 
1080
%  A description of each parameter follows:
 
1081
%
 
1082
%    o image: the image.
 
1083
%
 
1084
%    o name: the profile name, for example icc, exif, and 8bim (8bim is the
 
1085
%      Photoshop wrapper for iptc profiles).
 
1086
%
 
1087
%    o profile: A StringInfo structure that contains the named profile.
 
1088
%
 
1089
*/
 
1090
 
 
1091
static void *DestroyProfile(void *profile)
 
1092
{
 
1093
  return((void *) DestroyStringInfo((StringInfo *) profile));
 
1094
}
 
1095
 
 
1096
static inline const unsigned char *ReadResourceByte(const unsigned char *p,
 
1097
  unsigned char *quantum)
 
1098
{
 
1099
  *quantum=(*p++);
 
1100
  return(p);
 
1101
}
 
1102
 
 
1103
static inline const unsigned char *ReadResourceBytes(const unsigned char *p,
 
1104
  const ssize_t count,unsigned char *quantum)
 
1105
{
 
1106
  register ssize_t
 
1107
    i;
 
1108
 
 
1109
  for (i=0; i < count; i++)
 
1110
    *quantum++=(*p++);
 
1111
  return(p);
 
1112
}
 
1113
 
 
1114
static inline const unsigned char *ReadResourceLong(const unsigned char *p,
 
1115
  size_t *quantum)
 
1116
{
 
1117
  *quantum=(size_t) (*p++ << 24);
 
1118
  *quantum|=(size_t) (*p++ << 16);
 
1119
  *quantum|=(size_t) (*p++ << 8);
 
1120
  *quantum|=(size_t) (*p++ << 0);
 
1121
  return(p);
 
1122
}
 
1123
 
 
1124
static inline const unsigned char *ReadResourceShort(const unsigned char *p,
 
1125
  unsigned short *quantum)
 
1126
{
 
1127
  *quantum=(unsigned short) (*p++ << 8);
 
1128
  *quantum|=(unsigned short) (*p++ << 0);
 
1129
  return(p);
 
1130
}
 
1131
 
 
1132
static MagickBooleanType GetProfilesFromResourceBlock(Image *image,
 
1133
  const StringInfo *resource_block)
 
1134
{
 
1135
  const unsigned char
 
1136
    *datum;
 
1137
 
 
1138
  register const unsigned char
 
1139
    *p;
 
1140
 
 
1141
  size_t
 
1142
    length;
 
1143
 
 
1144
  StringInfo
 
1145
    *profile;
 
1146
 
 
1147
  unsigned char
 
1148
    length_byte;
 
1149
 
 
1150
  size_t
 
1151
    count;
 
1152
 
 
1153
  unsigned short
 
1154
    id;
 
1155
 
 
1156
  datum=GetStringInfoDatum(resource_block);
 
1157
  length=GetStringInfoLength(resource_block);
 
1158
  for (p=datum; p < (datum+length-16); )
 
1159
  {
 
1160
    if (LocaleNCompare((char *) p,"8BIM",4) != 0)
 
1161
      break;
 
1162
    p+=4;
 
1163
    p=ReadResourceShort(p,&id);
 
1164
    p=ReadResourceByte(p,&length_byte);
 
1165
    p+=length_byte;
 
1166
    if (((length_byte+1) & 0x01) != 0)
 
1167
      p++;
 
1168
    if (p > (datum+length-4))
 
1169
      break;
 
1170
    p=ReadResourceLong(p,&count);
 
1171
    if ((p > (datum+length-count)) || (count > length))
 
1172
      break;
 
1173
    switch (id)
 
1174
    {
 
1175
      case 0x03ed:
 
1176
      {
 
1177
        unsigned short
 
1178
          resolution;
 
1179
 
 
1180
        /*
 
1181
          Resolution.
 
1182
        */
 
1183
        p=ReadResourceShort(p,&resolution)+6;
 
1184
        image->x_resolution=(double) resolution;
 
1185
        p=ReadResourceShort(p,&resolution)+6;
 
1186
        image->y_resolution=(double) resolution;
 
1187
        break;
 
1188
      }
 
1189
      case 0x0404:
 
1190
      {
 
1191
        /*
 
1192
          IPTC Profile
 
1193
        */
 
1194
        profile=AcquireStringInfo(count);
 
1195
        SetStringInfoDatum(profile,p);
 
1196
        (void) SetImageProfile(image,"iptc",profile);
 
1197
        profile=DestroyStringInfo(profile);
 
1198
        p+=count;
 
1199
        break;
 
1200
      }
 
1201
      case 0x040c:
 
1202
      {
 
1203
        /*
 
1204
          Thumbnail.
 
1205
        */
 
1206
        p+=count;
 
1207
        break;
 
1208
      }
 
1209
      case 0x040f:
 
1210
      {
 
1211
        /*
 
1212
          ICC Profile.
 
1213
        */
 
1214
        profile=AcquireStringInfo(count);
 
1215
        SetStringInfoDatum(profile,p);
 
1216
        (void) SetImageProfile(image,"icc",profile);
 
1217
        profile=DestroyStringInfo(profile);
 
1218
        p+=count;
 
1219
        break;
 
1220
      }
 
1221
      case 0x0422:
 
1222
      {
 
1223
        /*
 
1224
          EXIF Profile.
 
1225
        */
 
1226
        profile=AcquireStringInfo(count);
 
1227
        SetStringInfoDatum(profile,p);
 
1228
        (void) SetImageProfile(image,"exif",profile);
 
1229
        profile=DestroyStringInfo(profile);
 
1230
        p+=count;
 
1231
        break;
 
1232
      }
 
1233
      case 0x0424:
 
1234
      {
 
1235
        /*
 
1236
          XMP Profile.
 
1237
        */
 
1238
        profile=AcquireStringInfo(count);
 
1239
        SetStringInfoDatum(profile,p);
 
1240
        (void) SetImageProfile(image,"xmp",profile);
 
1241
        profile=DestroyStringInfo(profile);
 
1242
        p+=count;
 
1243
        break;
 
1244
      }
 
1245
      default:
 
1246
      {
 
1247
        p+=count;
 
1248
        break;
 
1249
      }
 
1250
    }
 
1251
    if ((count & 0x01) != 0)
 
1252
      p++;
 
1253
  }
 
1254
  return(MagickTrue);
 
1255
}
 
1256
 
 
1257
MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
 
1258
  const StringInfo *profile)
 
1259
{
 
1260
  char
 
1261
    key[MaxTextExtent],
 
1262
    property[MaxTextExtent];
 
1263
 
 
1264
  MagickBooleanType
 
1265
    status;
 
1266
 
 
1267
  assert(image != (Image *) NULL);
 
1268
  assert(image->signature == MagickSignature);
 
1269
  if (image->debug != MagickFalse)
 
1270
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
 
1271
  if (image->profiles == (SplayTreeInfo *) NULL)
 
1272
    image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
 
1273
      DestroyProfile);
 
1274
  (void) CopyMagickString(key,name,MaxTextExtent);
 
1275
  status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
 
1276
    ConstantString(key),CloneStringInfo(profile));
 
1277
  if ((status != MagickFalse) &&
 
1278
      ((LocaleCompare(name,"icc") == 0) || (LocaleCompare(name,"icm") == 0)))
 
1279
    {
 
1280
      const StringInfo
 
1281
        *icc_profile;
 
1282
 
 
1283
      /*
 
1284
        Continue to support deprecated color profile member.
 
1285
      */
 
1286
      icc_profile=GetImageProfile(image,name);
 
1287
      if (icc_profile != (const StringInfo *) NULL)
 
1288
        {
 
1289
          image->color_profile.length=GetStringInfoLength(icc_profile);
 
1290
          image->color_profile.info=GetStringInfoDatum(icc_profile);
 
1291
        }
 
1292
    }
 
1293
  if ((status != MagickFalse) &&
 
1294
      ((LocaleCompare(name,"iptc") == 0) || (LocaleCompare(name,"8bim") == 0)))
 
1295
    {
 
1296
      const StringInfo
 
1297
        *iptc_profile;
 
1298
 
 
1299
      /*
 
1300
        Continue to support deprecated IPTC profile member.
 
1301
      */
 
1302
      iptc_profile=GetImageProfile(image,name);
 
1303
      if (iptc_profile != (const StringInfo *) NULL)
 
1304
        {
 
1305
          image->iptc_profile.length=GetStringInfoLength(iptc_profile);
 
1306
          image->iptc_profile.info=GetStringInfoDatum(iptc_profile);
 
1307
        }
 
1308
      (void) GetProfilesFromResourceBlock(image,profile);
 
1309
    }
 
1310
  /*
 
1311
    Inject profile into image properties.
 
1312
  */
 
1313
  (void) FormatLocaleString(property,MaxTextExtent,"%s:sans",name);
 
1314
  (void) GetImageProperty(image,property);
 
1315
  return(status);
 
1316
}
 
1317
 
 
1318
/*
 
1319
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1320
%                                                                             %
 
1321
%                                                                             %
 
1322
%                                                                             %
 
1323
%   S y n c I m a g e P r o f i l e s                                         %
 
1324
%                                                                             %
 
1325
%                                                                             %
 
1326
%                                                                             %
 
1327
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1328
%
 
1329
%  SyncImageProfiles() synchronizes image properties with the image profiles.
 
1330
%  Currently we only support updating the EXIF resolution and orientation.
 
1331
%
 
1332
%  The format of the SyncImageProfiles method is:
 
1333
%
 
1334
%      MagickBooleanType SyncImageProfiles(Image *image)
 
1335
%
 
1336
%  A description of each parameter follows:
 
1337
%
 
1338
%    o image: the image.
 
1339
%
 
1340
*/
 
1341
 
 
1342
static inline int ReadProfileByte(unsigned char **p,size_t *length)
 
1343
{
 
1344
  int
 
1345
    c;
 
1346
 
 
1347
  if (*length < 1)
 
1348
    return(EOF);
 
1349
  c=(int) (*(*p)++);
 
1350
  (*length)--;
 
1351
  return(c);
 
1352
}
 
1353
 
 
1354
static inline unsigned short ReadProfileShort(const EndianType endian,
 
1355
  unsigned char *buffer)
 
1356
{
 
1357
  unsigned short
 
1358
    value;
 
1359
 
 
1360
  if (endian == MSBEndian)
 
1361
    {
 
1362
      value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
 
1363
        ((unsigned char *) buffer)[1]);
 
1364
      return((unsigned short) (value & 0xffff));
 
1365
    }
 
1366
  value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
 
1367
  return((unsigned short) (value & 0xffff));
 
1368
}
 
1369
 
 
1370
static inline size_t ReadProfileLong(const EndianType endian,
 
1371
  unsigned char *buffer)
 
1372
{
 
1373
  size_t
 
1374
    value;
 
1375
 
 
1376
  if (endian == MSBEndian)
 
1377
    {
 
1378
      value=(size_t) ((buffer[0] << 24) | (buffer[1] << 16) |
 
1379
        (buffer[2] << 8) | buffer[3]);
 
1380
      return((size_t) (value & 0xffffffff));
 
1381
    }
 
1382
  value=(size_t) ((buffer[3] << 24) | (buffer[2] << 16) |
 
1383
    (buffer[1] << 8 ) | (buffer[0]));
 
1384
  return((size_t) (value & 0xffffffff));
 
1385
}
 
1386
 
 
1387
static inline void WriteProfileLong(const EndianType endian,
 
1388
  const size_t value,unsigned char *p)
 
1389
{
 
1390
  unsigned char
 
1391
    buffer[4];
 
1392
 
 
1393
  if (endian == MSBEndian)
 
1394
    {
 
1395
      buffer[0]=(unsigned char) (value >> 24);
 
1396
      buffer[1]=(unsigned char) (value >> 16);
 
1397
      buffer[2]=(unsigned char) (value >> 8);
 
1398
      buffer[3]=(unsigned char) value;
 
1399
      (void) CopyMagickMemory(p,buffer,4);
 
1400
      return;
 
1401
    }
 
1402
  buffer[0]=(unsigned char) value;
 
1403
  buffer[1]=(unsigned char) (value >> 8);
 
1404
  buffer[2]=(unsigned char) (value >> 16);
 
1405
  buffer[3]=(unsigned char) (value >> 24);
 
1406
  (void) CopyMagickMemory(p,buffer,4);
 
1407
}
 
1408
 
 
1409
static void WriteProfileShort(const EndianType endian,
 
1410
  const unsigned short value,unsigned char *p)
 
1411
{
 
1412
  unsigned char
 
1413
    buffer[2];
 
1414
 
 
1415
  if (endian == MSBEndian)
 
1416
    {
 
1417
      buffer[0]=(unsigned char) (value >> 8);
 
1418
      buffer[1]=(unsigned char) value;
 
1419
      (void) CopyMagickMemory(p,buffer,2);
 
1420
      return;
 
1421
    }
 
1422
  buffer[0]=(unsigned char) value;
 
1423
  buffer[1]=(unsigned char) (value >> 8);
 
1424
  (void) CopyMagickMemory(p,buffer,2);
 
1425
}
 
1426
 
 
1427
MagickExport MagickBooleanType SyncImageProfiles(Image *image)
 
1428
{
 
1429
#define MaxDirectoryStack  16
 
1430
#define EXIF_DELIMITER  "\n"
 
1431
#define EXIF_NUM_FORMATS  12
 
1432
#define TAG_EXIF_OFFSET  0x8769
 
1433
#define TAG_INTEROP_OFFSET  0xa005
 
1434
 
 
1435
  typedef struct _DirectoryInfo
 
1436
  {
 
1437
    unsigned char
 
1438
      *directory;
 
1439
 
 
1440
    size_t
 
1441
      entry;
 
1442
  } DirectoryInfo;
 
1443
 
 
1444
  DirectoryInfo
 
1445
    directory_stack[MaxDirectoryStack];
 
1446
 
 
1447
  EndianType
 
1448
    endian;
 
1449
 
 
1450
  size_t
 
1451
    entry,
 
1452
    length,
 
1453
    number_entries;
 
1454
 
 
1455
  SplayTreeInfo
 
1456
    *exif_resources;
 
1457
 
 
1458
  ssize_t
 
1459
    id,
 
1460
    level,
 
1461
    offset;
 
1462
 
 
1463
  static int
 
1464
    format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
 
1465
 
 
1466
  StringInfo
 
1467
    *profile;
 
1468
 
 
1469
  unsigned char
 
1470
    *directory,
 
1471
    *exif;
 
1472
 
 
1473
  /*
 
1474
    Set EXIF resolution tag.
 
1475
  */
 
1476
  profile=(StringInfo *) GetImageProfile(image,"EXIF");
 
1477
  if (profile == (StringInfo *) NULL)
 
1478
    return(MagickTrue);
 
1479
  length=GetStringInfoLength(profile);
 
1480
  exif=GetStringInfoDatum(profile);
 
1481
  while (length != 0)
 
1482
  {
 
1483
    if (ReadProfileByte(&exif,&length) != 0x45)
 
1484
      continue;
 
1485
    if (ReadProfileByte(&exif,&length) != 0x78)
 
1486
      continue;
 
1487
    if (ReadProfileByte(&exif,&length) != 0x69)
 
1488
      continue;
 
1489
    if (ReadProfileByte(&exif,&length) != 0x66)
 
1490
      continue;
 
1491
    if (ReadProfileByte(&exif,&length) != 0x00)
 
1492
      continue;
 
1493
    if (ReadProfileByte(&exif,&length) != 0x00)
 
1494
      continue;
 
1495
    break;
 
1496
  }
 
1497
  if (length < 16)
 
1498
    return(MagickFalse);
 
1499
  id=(ssize_t) ReadProfileShort(LSBEndian,exif);
 
1500
  endian=LSBEndian;
 
1501
  if (id == 0x4949)
 
1502
    endian=LSBEndian;
 
1503
  else
 
1504
    if (id == 0x4D4D)
 
1505
      endian=MSBEndian;
 
1506
    else
 
1507
      return(MagickFalse);
 
1508
  if (ReadProfileShort(endian,exif+2) != 0x002a)
 
1509
    return(MagickFalse);
 
1510
  /*
 
1511
    This the offset to the first IFD.
 
1512
  */
 
1513
  offset=(ssize_t) ((int) ReadProfileLong(endian,exif+4));
 
1514
  if ((offset < 0) || ((size_t) offset >= length))
 
1515
    return(MagickFalse);
 
1516
  directory=exif+offset;
 
1517
  level=0;
 
1518
  entry=0;
 
1519
  exif_resources=NewSplayTree((int (*)(const void *,const void *)) NULL,
 
1520
    (void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
 
1521
  do
 
1522
  {
 
1523
    if (level > 0)
 
1524
      {
 
1525
        level--;
 
1526
        directory=directory_stack[level].directory;
 
1527
        entry=directory_stack[level].entry;
 
1528
      }
 
1529
    /*
 
1530
      Determine how many entries there are in the current IFD.
 
1531
    */
 
1532
    number_entries=ReadProfileShort(endian,directory);
 
1533
    for ( ; entry < number_entries; entry++)
 
1534
    {
 
1535
      register unsigned char
 
1536
        *p,
 
1537
        *q;
 
1538
 
 
1539
      size_t
 
1540
        number_bytes;
 
1541
 
 
1542
      ssize_t
 
1543
        components,
 
1544
        format,
 
1545
        tag_value;
 
1546
 
 
1547
      q=(unsigned char *) (directory+2+(12*entry));
 
1548
      if (GetValueFromSplayTree(exif_resources,q) == q)
 
1549
        break;
 
1550
      (void) AddValueToSplayTree(exif_resources,q,q);
 
1551
      tag_value=(ssize_t) ReadProfileShort(endian,q);
 
1552
      format=(ssize_t) ReadProfileShort(endian,q+2);
 
1553
      if ((format-1) >= EXIF_NUM_FORMATS)
 
1554
        break;
 
1555
      components=(ssize_t) ((int) ReadProfileLong(endian,q+4));
 
1556
      number_bytes=(size_t) components*format_bytes[format];
 
1557
      if ((ssize_t) number_bytes < components)
 
1558
        break;  /* prevent overflow */
 
1559
      if (number_bytes <= 4)
 
1560
        p=q+8;
 
1561
      else
 
1562
        {
 
1563
          ssize_t
 
1564
            offset;
 
1565
 
 
1566
          /*
 
1567
            The directory entry contains an offset.
 
1568
          */
 
1569
          offset=(ssize_t) ((int) ReadProfileLong(endian,q+8));
 
1570
          if ((ssize_t) (offset+number_bytes) < offset)
 
1571
            continue;  /* prevent overflow */
 
1572
          if ((size_t) (offset+number_bytes) > length)
 
1573
            continue;
 
1574
          p=(unsigned char *) (exif+offset);
 
1575
        }
 
1576
      switch (tag_value)
 
1577
      {
 
1578
        case 0x011a:
 
1579
        {
 
1580
          (void) WriteProfileLong(endian,(size_t) (image->x_resolution+0.5),p);
 
1581
          (void) WriteProfileLong(endian,1UL,p+4);
 
1582
          break;
 
1583
        }
 
1584
        case 0x011b:
 
1585
        {
 
1586
          (void) WriteProfileLong(endian,(size_t) (image->y_resolution+0.5),p);
 
1587
          (void) WriteProfileLong(endian,1UL,p+4);
 
1588
          break;
 
1589
        }
 
1590
        case 0x0112:
 
1591
        {
 
1592
          if (number_bytes == 4)
 
1593
            {
 
1594
              (void) WriteProfileLong(endian,(size_t) image->orientation,p);
 
1595
              break;
 
1596
            }
 
1597
          (void) WriteProfileShort(endian,(unsigned short) image->orientation,
 
1598
            p);
 
1599
          break;
 
1600
        }
 
1601
        case 0x0128:
 
1602
        {
 
1603
          if (number_bytes == 4)
 
1604
            {
 
1605
              (void) WriteProfileLong(endian,(size_t) (image->units+1),p);
 
1606
              break;
 
1607
            }
 
1608
          (void) WriteProfileShort(endian,(unsigned short) (image->units+1),p);
 
1609
          break;
 
1610
        }
 
1611
        default:
 
1612
          break;
 
1613
      }
 
1614
      if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
 
1615
        {
 
1616
          ssize_t
 
1617
            offset;
 
1618
 
 
1619
          offset=(ssize_t) ((int) ReadProfileLong(endian,p));
 
1620
          if (((size_t) offset < length) && (level < (MaxDirectoryStack-2)))
 
1621
            {
 
1622
              directory_stack[level].directory=directory;
 
1623
              entry++;
 
1624
              directory_stack[level].entry=entry;
 
1625
              level++;
 
1626
              directory_stack[level].directory=exif+offset;
 
1627
              directory_stack[level].entry=0;
 
1628
              level++;
 
1629
              if ((directory+2+(12*number_entries)) > (exif+length))
 
1630
                break;
 
1631
              offset=(ssize_t) ((int) ReadProfileLong(endian,directory+2+(12*
 
1632
                number_entries)));
 
1633
              if ((offset != 0) && ((size_t) offset < length) &&
 
1634
                  (level < (MaxDirectoryStack-2)))
 
1635
                {
 
1636
                  directory_stack[level].directory=exif+offset;
 
1637
                  directory_stack[level].entry=0;
 
1638
                  level++;
 
1639
                }
 
1640
            }
 
1641
          break;
 
1642
        }
 
1643
    }
 
1644
  } while (level > 0);
 
1645
  exif_resources=DestroySplayTree(exif_resources);
 
1646
  return(MagickTrue);
 
1647
}