Coverage Report

Created: 2025-08-11 08:01

/src/graphicsmagick/magick/profile.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
% Copyright (C) 2003-2023 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
5
%
6
% This program is covered by multiple licenses, which are described in
7
% Copyright.txt. You should have received a copy of Copyright.txt with this
8
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9
%
10
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11
%                                                                             %
12
%                                                                             %
13
%                                                                             %
14
%              PPPP   RRRR    OOO   FFFFF  IIIII  L      EEEEE                %
15
%              P   P  R   R  O   O  F        I    L      E                    %
16
%              PPPP   RRRR   O   O  FFF      I    L      EEE                  %
17
%              P      R R    O   O  F        I    L      E                    %
18
%              P      R  R    OOO   F      IIIII  LLLLL  EEEEE                %
19
%                                                                             %
20
%                   GraphicsMagick Image Profile Methods                      %
21
%                                                                             %
22
%                                                                             %
23
%                              Software Design                                %
24
%                                John Cristy                                  %
25
%                               Bill Radcliffe                                %
26
%                              Bob Friesenhahn                                %
27
%                                                                             %
28
%                                                                             %
29
%                                                                             %
30
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31
%
32
%
33
%
34
*/
35

36
/*
37
  Include declarations.
38
*/
39
#include "magick/studio.h"
40
#include "magick/analyze.h"
41
#include "magick/color.h"
42
#include "magick/composite.h"
43
#include "magick/log.h"
44
#include "magick/map.h"
45
#include "magick/monitor.h"
46
#include "magick/omp_data_view.h"
47
#include "magick/pixel_iterator.h"
48
#include "magick/profile.h"
49
#include "magick/quantize.h"
50
#include "magick/resize.h"
51
#include "magick/transform.h"
52
#include "magick/utility.h"
53
#if defined(HasLCMS)
54
#  if defined(HAVE_LCMS2_LCMS2_H)
55
#    include <lcms2/lcms2.h>
56
#  elif defined(HAVE_LCMS2_H)
57
#    include <lcms2.h>
58
#  else
59
#    error "LCMS 2 header missing!"
60
#  endif
61
#endif
62

63
/*
64
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
65
%                                                                             %
66
%                                                                             %
67
%                                                                             %
68
%     A l l o c a t e I m a g e P r o f i l e I t e r a t o r                 %
69
%                                                                             %
70
%                                                                             %
71
%                                                                             %
72
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
73
%
74
%  AllocateImageProfileIterator allocates an iterator to traverse the
75
%  image profile list.  It is an error (i.e. will surely crash) to invoke
76
%  DeleteImageProfile() on the profile that the iterator is currently
77
%  referencing.  However, it is safe to delete a profile that the iterator
78
%  is not currently referencing. Inserting additional profiles does not
79
%  invalidate the current iterator.
80
%
81
%
82
%  The format of the AllocateImageProfileIterator method is:
83
%
84
%      ImageProfileIterator AllocateImageProfileIterator(const Image *image)
85
%
86
%  A description of each parameter follows:
87
%
88
%    o image: The image.
89
%
90
*/
91
MagickExport ImageProfileIterator
92
AllocateImageProfileIterator(const Image *image)
93
82.0k
{
94
82.0k
  if (!image->profiles)
95
74.5k
    return 0;
96
97
7.49k
  return (ImageProfileIterator) MagickMapAllocateIterator(image->profiles);
98
82.0k
}
99

100
/*
101
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
102
%                                                                             %
103
%                                                                             %
104
%                                                                             %
105
%     A p p e n d I m a g e P r o f i l e                                     %
106
%                                                                             %
107
%                                                                             %
108
%                                                                             %
109
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
110
%
111
%  AppendImageProfile adds a named profile to the image. If a profile with the
112
%  same name already exists, then the new profile data is appended to the
113
%  existing profile. If a null profile address is supplied, then an existing
114
%  profile is removed. The profile is copied into the image. Note that this
115
%  function does not execute CMS color profiles. Any existing CMS color
116
%  profile is simply added/updated. Use the ProfileImage() function in order
117
%  to execute a CMS color profile.
118
%
119
%  The format of the AppendImageProfile method is:
120
%
121
%      MaickPassFail AppendImageProfile(Image *image,const char *name,
122
%                                       const unsigned char *profile_chunk,
123
%                                       const size_t chunk_length)
124
%
125
%  A description of each parameter follows:
126
%
127
%    o image: The image.
128
%
129
%    o name: Profile name. Valid names are "8BIM", "ICM", "IPTC", XMP, or any
130
%                          unique text string.
131
%
132
%    o profile_chunk: Address of profile chunk to add or append. Pass zero
133
%               to remove an existing profile.
134
%
135
%    o length: The length of the profile chunk to add or append.
136
%
137
*/
138
MagickExport MagickPassFail
139
AppendImageProfile(Image *image,
140
                   const char *name,
141
                   const unsigned char *profile_chunk,
142
                   const size_t chunk_length)
143
0
{
144
0
  const unsigned char
145
0
    *existing_profile;
146
147
0
  size_t
148
0
    existing_length;
149
150
0
  MagickPassFail
151
0
    status;
152
153
0
  status=MagickFail;
154
0
  existing_length=0;
155
0
  existing_profile=(const unsigned char *) NULL;
156
0
  if (profile_chunk != (const unsigned char *) NULL)
157
0
    existing_profile=GetImageProfile(image,name,&existing_length);
158
159
0
  if ((profile_chunk == (const unsigned char *) NULL) ||
160
0
      (existing_profile == (const unsigned char *) NULL))
161
0
    {
162
0
      status=SetImageProfile(image,name,profile_chunk,chunk_length);
163
0
    }
164
0
  else
165
0
    {
166
0
      unsigned char
167
0
        *profile;
168
169
0
      size_t
170
0
        profile_length;
171
172
0
      profile_length=existing_length+chunk_length;
173
0
      if ((profile_length < existing_length) ||
174
0
          ((profile=MagickAllocateMemory(unsigned char *,(size_t) profile_length)) ==
175
0
           (unsigned char *) NULL))
176
0
        ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed,
177
0
                             (char *) NULL);
178
0
      (void) memcpy(profile,existing_profile,existing_length);
179
0
      (void) memcpy(profile+existing_length,profile_chunk,chunk_length);
180
0
      status=SetImageProfile(image,name,profile,profile_length);
181
0
      MagickFreeMemory(profile);
182
0
    }
183
184
0
  return status;
185
0
}
186

187
/*
188
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
189
%                                                                             %
190
%                                                                             %
191
%                                                                             %
192
%     D e a l l o c a t e I m a g e P r o f i l e I t e r a t o r             %
193
%                                                                             %
194
%                                                                             %
195
%                                                                             %
196
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
197
%
198
%  DeallocateImageProfileIterator deallocates an image profile iterator.
199
%
200
%  The format of the DeallocateImageProfileIterator method is:
201
%
202
%      void DeallocateImageProfileIterator(ImageProfileIterator profile_iterator)
203
%
204
%  A description of each parameter follows:
205
%
206
%    o profile_iterator: Profile iterator to deallocate.
207
%
208
*/
209
MagickExport void
210
DeallocateImageProfileIterator(ImageProfileIterator profile_iterator)
211
11.0k
{
212
11.0k
  if (profile_iterator != 0)
213
7.49k
    MagickMapDeallocateIterator((MagickMapIterator) profile_iterator);
214
11.0k
}
215

216
/*
217
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
218
%                                                                             %
219
%                                                                             %
220
%                                                                             %
221
%     D e l e t e I m a g e P r o f i l e                                     %
222
%                                                                             %
223
%                                                                             %
224
%                                                                             %
225
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
226
%
227
%  DeleteImageProfile removes a named profile from the image.
228
%
229
%  The format of the DeleteImageProfile method is:
230
%
231
%      unsigned int DeleteImageProfile(Image *image,const char *name)
232
%
233
%  A description of each parameter follows:
234
%
235
%    o image: The image.
236
%
237
%    o name: Profile name. Valid names are "8BIM", "ICM", & "IPTC" or a
238
%                          generic profile name.
239
%
240
*/
241
MagickExport MagickPassFail
242
DeleteImageProfile(Image *image,const char *name)
243
0
{
244
0
  return(SetImageProfile(image,name,0,0));
245
0
}
246

247
/*
248
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
249
%                                                                             %
250
%                                                                             %
251
%                                                                             %
252
%     G e t I m a g e P r o f i l e                                           %
253
%                                                                             %
254
%                                                                             %
255
%                                                                             %
256
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
257
%
258
%  GetImageProfile returns a pointer to the named image profile if it is
259
%  present. A null pointer is returned if the named profile is not present.
260
%
261
%  Older versions of this function stored profiles named "8BIM" and "IPTC"
262
%  in the same storage location.  This is no longer the case.  However,
263
%  GetImageProfile() will try the alternate name if the specifically
264
%  requested profile name is not available.
265
%
266
%  The format of the GetImageProfile method is:
267
%
268
%      const unsigned char *GetImageProfile(const Image* image,
269
%                             const char *name, size_t *length)
270
%
271
%  A description of each parameter follows:
272
%
273
%    o image: The image.
274
%
275
%    o name: Profile name. Valid names are "8BIM", "EXIF", "ICM", "IPTC",
276
%              "XMP" or any unique text string.
277
%
278
%    o length: Updated with profile length if profile is present.  Set to NULL
279
%              if length is not needed.
280
%
281
*/
282
MagickExport const unsigned char *
283
GetImageProfile(const Image* image, const char *name, size_t *length)
284
436k
{
285
436k
  const unsigned char
286
436k
    *profile = 0;
287
288
436k
  size_t
289
436k
    profile_length=0;
290
291
436k
  assert(image != (Image *) NULL);
292
436k
  assert(image->signature == MagickSignature);
293
436k
  assert(name != NULL);
294
295
436k
  if (length)
296
436k
    *length=0;
297
298
436k
  if (!image->profiles)
299
404k
    return 0;
300
301
31.9k
  profile=MagickMapAccessEntry(image->profiles,name,&profile_length);
302
303
31.9k
  if (!profile)
304
12.4k
    {
305
      /*
306
        Support common alias names and work-alikes.
307
      */
308
12.4k
      if (LocaleCompare("ICC",name) == 0)
309
23
        profile=MagickMapAccessEntry(image->profiles,"ICM",&profile_length);
310
12.4k
      else if (LocaleCompare("ICM",name) == 0)
311
178
        profile=MagickMapAccessEntry(image->profiles,"ICC",&profile_length);
312
12.2k
      else if (LocaleCompare("IPTC",name) == 0)
313
163
        profile=MagickMapAccessEntry(image->profiles,"8BIM",&profile_length);
314
12.0k
      else if (LocaleCompare("8BIM",name) == 0)
315
1.76k
        profile=MagickMapAccessEntry(image->profiles,"IPTC",&profile_length);
316
12.4k
    }
317
318
31.9k
  if (length)
319
31.9k
    *length=profile_length;
320
321
31.9k
  return profile;
322
436k
}
323

324
/*
325
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
326
%                                                                             %
327
%                                                                             %
328
%                                                                             %
329
%     N e x t I m a g e P r o f i l e                                         %
330
%                                                                             %
331
%                                                                             %
332
%                                                                             %
333
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
334
%
335
%  NextImageProfile iterates forward to the next image profile. The profile
336
%  name is returned along with the profile data, and length.  If there are
337
%  no more entries in the list, then MagickFail is returned.
338
%
339
%  The format of the AllocateImageProfileIterator method is:
340
%
341
%      MagickPassFail NextImageProfile(ImageProfileIterator profile_iterator,
342
%                             const char **name, const unsigned char **profile,
343
%                             size_t *length)
344
%
345
%  A description of each parameter follows:
346
%
347
%    o profile_iterator: Profile iterator.
348
%
349
%    o name: Address of pointer to update with address of name.
350
%
351
%    o profile: Address of pointer to update with location of profile data.
352
%
353
%    o length: Address of parameter to update with profile length.
354
%
355
*/
356
MagickExport MagickPassFail
357
NextImageProfile(ImageProfileIterator profile_iterator,
358
                 const char **name,
359
                 const unsigned char **profile,
360
                 size_t *length)
361
18.9k
{
362
18.9k
  MagickMapIterator
363
18.9k
    map_iterator;
364
365
18.9k
  MagickPassFail
366
18.9k
    status;
367
368
18.9k
  assert(name != (const char **) NULL);
369
18.9k
  assert(length != (size_t *) NULL);
370
371
18.9k
  if (profile_iterator == 0)
372
3.59k
    return (MagickFail);
373
374
15.3k
  map_iterator=(MagickMapIterator) profile_iterator;
375
15.3k
  status=MagickMapIterateNext(map_iterator,name);
376
15.3k
  if (status != MagickFail)
377
10.8k
    *profile=MagickMapDereferenceIterator(map_iterator,length);
378
15.3k
  return (status);
379
18.9k
}
380

381
/*
382
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
383
%                                                                             %
384
%                                                                             %
385
%                                                                             %
386
%   P r o f i l e I m a g e                                                   %
387
%                                                                             %
388
%                                                                             %
389
%                                                                             %
390
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
391
%
392
%  ProfileImage() adds, applies, or removes a ICM, IPTC, or generic profile
393
%  from an image.  If the profile is NULL, it is removed from the image
394
%  otherwise added (or applied).  Use a name of '*' and a profile of NULL to
395
%  remove all profiles from the image. Ownership of the profile is
396
%  transferred to GraphicsMagick (it should not be altered or deallocated)
397
%  unless the clone option is set to True.
398
%
399
%  ICC ICM profiles are a special case and are handled as follows:
400
%
401
%  If there is no ICM profile currently associated with the image, then
402
%  the profile is simply associated with the image and the image pixels
403
%  are not altered.
404
%
405
%  If there is already a ICM profile associated with the image, then
406
%  the colorspace transform described by the existing and new profiles
407
%  is applied to the image pixels, and the new profile is associated
408
%  with the image.
409
%
410
%  The format of the ProfileImage method is:
411
%
412
%      unsigned int ProfileImage(Image *image,const char *name,
413
%        unsigned char *profile,const size_t length,unsigned int clone)
414
%
415
%  A description of each parameter follows:
416
%
417
%    o image: The image.
418
%
419
%    o name: Name of profile to add or remove: ICM, IPTC, or generic profile.
420
%
421
%    o profile: The profile.  Can not be 'const' due to 'clone' option but
422
%             is treated as 'const' if 'clone' is set to MagickTrue.
423
%
424
%    o length: The length of the profile.
425
%
426
%    o clone: If set True, then copy the profile rather than taking
427
%             ownership of it.
428
%
429
%
430
*/
431
#if defined(HasLCMS)
432
433
typedef struct _ProfilePacket
434
{
435
  unsigned short
436
    red,
437
    green,
438
    blue,
439
    black;
440
} ProfilePacket;
441
442
typedef struct _TransformInfo
443
{
444
  Image           *image;             /* image handle */
445
  cmsHPROFILE     source_profile;     /* input profile */
446
  cmsHPROFILE     target_profile;     /* output profile */
447
  cmsUInt32Number source_type;        /* input pixel format */
448
  cmsUInt32Number target_type;        /* output pixel format */
449
  int             intent;             /* rendering intent */
450
  cmsUInt32Number flags;              /* create transform flags */
451
  ThreadViewDataSet *transform;       /* Thread-specific transforms */
452
  ColorspaceType  source_colorspace;  /* source image transform colorspace */
453
  ColorspaceType  target_colorspace;  /* target image transform colorspace */
454
  unsigned long   signature;          /* structure validation signature */
455
} TransformInfo;
456
457
static void
458
lcmsReplacementErrorHandler(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *ErrorText)
459
0
{
460
0
  TransformInfo
461
0
    *xform;
462
463
0
  ExceptionType
464
0
    type=TransformError;
465
466
0
  xform=(TransformInfo *) ContextID;
467
468
0
  switch(ErrorCode)
469
0
  {
470
0
  default:
471
0
    type=TransformWarning;
472
0
    break;
473
0
  }
474
475
0
  (void) LogMagickEvent(type,GetMagickModule(),"lcms: #%u, %s",
476
0
                        ErrorCode,(ErrorText != (char *) NULL) ? ErrorText : "No error text");
477
478
0
  if (xform != (TransformInfo *) NULL)
479
0
    {
480
0
      ThrowException2(&xform->image->exception,type,"UnableToTransformColorspace",
481
0
                     (ErrorText != (char *) NULL) ? ErrorText : "No error text");
482
0
    }
483
0
}
484
485
static MagickPassFail
486
ProfileImagePixels(void *mutable_data,         /* User provided mutable data */
487
                   const void *immutable_data, /* User provided immutable data */
488
                   Image * restrict image,               /* Modify image */
489
                   PixelPacket * restrict pixels,        /* Pixel row */
490
                   IndexPacket * restrict indexes,       /* Pixel row indexes */
491
                   const long npixels,         /* Number of pixels in row */
492
                   ExceptionInfo *exception)   /* Exception report */
493
0
{
494
0
  const TransformInfo
495
0
    *xform = (const TransformInfo *) immutable_data;
496
497
0
  register long
498
0
    i;
499
500
0
  cmsHTRANSFORM
501
0
    transform;
502
503
0
  const ColorspaceType
504
0
    source_colorspace = xform->source_colorspace;
505
506
0
  const ColorspaceType
507
0
    target_colorspace = xform->target_colorspace;
508
509
0
  ProfilePacket
510
0
    alpha,
511
0
    beta;
512
513
0
  ARG_NOT_USED(mutable_data);
514
0
  ARG_NOT_USED(exception);
515
516
0
  transform=(cmsHTRANSFORM) AccessThreadViewData(xform->transform);
517
518
  /*
519
    TODO: This may be optimized to use PixelPackets instead
520
    of moving PixelPacket components to and from ProfilePackets.
521
    The transform types below, then, must match #ifdef's in the
522
    PixelPacket struct definition and should be optimized
523
    based on Quantum size. Some (if not all?) YCbCr and LUV
524
    profiles are (TIFF) scanline oriented, so transforming
525
    one pixel at a time does not work for those profiles.
526
527
    Notice that the performance penalty of transforming only
528
    one pixel at a time is very small and probably not worth
529
    optimizing.
530
  */
531
532
0
  for (i=0; i < npixels; i++)
533
0
    {
534
0
      alpha.red=ScaleQuantumToShort(pixels[i].red);
535
0
      if (source_colorspace != GRAYColorspace)
536
0
        {
537
0
          alpha.green=ScaleQuantumToShort(pixels[i].green);
538
0
          alpha.blue=ScaleQuantumToShort(pixels[i].blue);
539
0
          if (source_colorspace == CMYKColorspace)
540
0
            alpha.black=ScaleQuantumToShort(pixels[i].opacity);
541
0
        }
542
0
      cmsDoTransform(transform,&alpha,&beta,1);
543
0
      pixels[i].red=ScaleShortToQuantum(beta.red);
544
0
      if (IsGrayColorspace(target_colorspace))
545
0
        {
546
0
          pixels[i].green=pixels[i].red;
547
0
          pixels[i].blue=pixels[i].red;
548
0
        }
549
0
      else
550
0
        {
551
0
          pixels[i].green=ScaleShortToQuantum(beta.green);
552
0
          pixels[i].blue=ScaleShortToQuantum(beta.blue);
553
0
        }
554
0
      if ((image->matte) && (NULL != indexes))
555
0
        {
556
0
          if ((source_colorspace == CMYKColorspace) &&
557
0
              (target_colorspace != CMYKColorspace))
558
0
            pixels[i].opacity=indexes[i];
559
0
          else
560
0
            if ((source_colorspace != CMYKColorspace) &&
561
0
                (target_colorspace == CMYKColorspace))
562
0
              indexes[i]=pixels[i].opacity;
563
0
        }
564
0
      if (target_colorspace == CMYKColorspace)
565
0
        pixels[i].opacity=ScaleShortToQuantum(beta.black);
566
0
    }
567
568
0
  return MagickPass;
569
0
}
570
571
static void MagickFreeCMSTransform(void * cmsTransformVoid)
572
0
{
573
0
  cmsHTRANSFORM
574
0
    cmsTransform=(cmsHTRANSFORM) cmsTransformVoid;
575
576
0
  if (cmsTransform != (cmsHTRANSFORM) NULL)
577
0
      cmsDeleteTransform(cmsTransform);
578
0
}
579
580
static const char *
581
PixelTypeToString(int pixel_type)
582
0
{
583
0
const char *
584
0
  result="";
585
586
0
  switch (pixel_type)
587
0
    {
588
0
    case PT_ANY:    result="ANY"   ; break;
589
0
    case PT_GRAY:   result="GRAY"  ; break;
590
0
    case PT_RGB:    result="RGB"   ; break;
591
0
    case PT_CMY:    result="CMY"   ; break;
592
0
    case PT_CMYK:   result="CMYK"  ; break;
593
0
    case PT_YCbCr:  result="YCbCr" ; break;
594
0
    case PT_YUV:    result="YUV (Lu'v')"; break;
595
0
    case PT_XYZ:    result="XYZ"   ; break;
596
0
    case PT_Lab:    result="Lab"   ; break;
597
0
    case PT_YUVK:   result="YUVK (Lu'v'K)" ; break;
598
0
    case PT_HSV:    result="HSV"   ; break;
599
0
    case PT_HLS:    result="HLS"   ; break;
600
0
    case PT_Yxy:    result="Yxy"   ; break;
601
602
#if defined(PT_HiFi)
603
    case PT_HiFi:   result="HiFi"  ; break;
604
#endif
605
#if defined(PT_HiFi7)
606
    case PT_HiFi7:  result="HiFi7" ; break;
607
#endif
608
#if defined(PT_HiFi8)
609
    case PT_HiFi8:  result="HiFi8" ; break;
610
#endif
611
#if defined(PT_HiFi9)
612
    case PT_HiFi9:  result="HiFi9" ; break;
613
#endif
614
#if defined(PT_HiFi10)
615
    case PT_HiFi10: result="HiFi10"; break;
616
#endif
617
#if defined(PT_HiFi11)
618
    case PT_HiFi11: result="HiFi11"; break;
619
#endif
620
#if defined(PT_HiFi12)
621
    case PT_HiFi12: result="HiFi12"; break;
622
#endif
623
#if defined(PT_HiFi13)
624
    case PT_HiFi13: result="HiFi13"; break;
625
#endif
626
#if defined(PT_HiFi14)
627
    case PT_HiFi14: result="HiFi14"; break;
628
#endif
629
#if defined(PT_HiFi15)
630
    case PT_HiFi15: result="HiFi15"; break;
631
#endif
632
633
0
#if defined(PT_MCH1)
634
0
    case PT_MCH1:   result="MCH1"  ; break;
635
0
#endif
636
0
#if defined(PT_MCH2)
637
0
    case PT_MCH2:   result="MCH2"  ; break;
638
0
#endif
639
0
#if defined(PT_MCH3)
640
0
    case PT_MCH3:   result="MCH3"  ; break;
641
0
#endif
642
0
#if defined(PT_MCH4)
643
0
    case PT_MCH4:   result="MCH4"  ; break;
644
0
#endif
645
0
#if defined(PT_MCH5)
646
0
    case PT_MCH5:   result="MCH5"  ; break;
647
0
#endif
648
0
#if defined(PT_MCH6)
649
0
    case PT_MCH6:   result="MCH6"  ; break;
650
0
#endif
651
0
#if defined(PT_MCH7)
652
0
    case PT_MCH7:   result="MCH7"  ; break;
653
0
#endif
654
0
#if defined(PT_MCH8)
655
0
    case PT_MCH8:   result="MCH8"  ; break;
656
0
#endif
657
0
#if defined(PT_MCH9)
658
0
    case PT_MCH9:   result="MCH9"  ; break;
659
0
#endif
660
0
#if defined(PT_MCH10)
661
0
    case PT_MCH10:  result="MCH10" ; break;
662
0
#endif
663
0
#if defined(PT_MCH11)
664
0
    case PT_MCH11:  result="MCH11" ; break;
665
0
#endif
666
0
#if defined(PT_MCH12)
667
0
    case PT_MCH12:  result="MCH12" ; break;
668
0
#endif
669
0
#if defined(PT_MCH13)
670
0
    case PT_MCH13:  result="MCH13" ; break;
671
0
#endif
672
0
#if defined(PT_MCH14)
673
0
    case PT_MCH14:  result="MCH14" ; break;
674
0
#endif
675
0
#if defined(PT_MCH15)
676
0
    case PT_MCH15:  result="MCH15" ; break;
677
0
#endif
678
0
#if defined(PT_LabV2)
679
0
    case PT_LabV2:  result="LabV2" ; break;
680
0
#endif
681
0
    }
682
683
0
  return result;
684
0
}
685
686
#endif /* defined(HasLCMS) */
687
688
0
#define ProfileImageText "[%s] Color Transform Pixels..."
689
MagickExport MagickPassFail
690
ProfileImage(Image *image,const char *name,unsigned char *profile,
691
             const size_t length,MagickBool clone)
692
0
{
693
0
  MagickPassFail
694
0
    status=MagickPass;
695
696
0
  assert(image != (Image *) NULL);
697
0
  assert(image->signature == MagickSignature);
698
0
  if (name == (const char *) NULL)
699
0
    ThrowBinaryException3(OptionError,NoProfileNameWasGiven,
700
0
                          UnableToAddOrRemoveProfile);
701
0
  if ((profile == (const unsigned char *) NULL) || (length == 0))
702
0
    {
703
      /*
704
        Remove an ICM, IPTC, or generic profile from the image.
705
      */
706
0
      char
707
0
        arg_string[MaxTextExtent],
708
0
        profile_remove[MaxTextExtent];
709
710
0
      const char
711
0
        *profile_name;
712
713
0
      size_t
714
0
        profile_length;
715
716
0
      const unsigned char *
717
0
        profile_info;
718
719
0
      ImageProfileIterator
720
0
        profile_iterator;
721
722
0
      char
723
0
        **argv;
724
725
0
      int
726
0
        argc;
727
728
0
      long
729
0
        i;
730
731
0
      (void) strlcpy(arg_string,name,sizeof(arg_string));
732
0
      LocaleUpper(arg_string);
733
0
      for (i=0; arg_string[i] != '\0'; i++)
734
0
        if (arg_string[i] == ',')
735
0
          arg_string[i] = ' ';
736
0
      argv=StringToArgv(arg_string,&argc);
737
0
      profile_iterator=AllocateImageProfileIterator(image);
738
0
      profile_remove[0]=0;
739
0
      while(NextImageProfile(profile_iterator,&profile_name,&profile_info,
740
0
                             &profile_length) != MagickFail)
741
0
        {
742
0
          if (strlen(profile_remove))
743
0
            {
744
0
              (void) DeleteImageProfile(image,profile_remove);
745
0
              profile_remove[0]=0;
746
0
            }
747
0
          for (i=1 ; i < argc ; i++)
748
0
            {
749
0
              if ((*argv[i] == '!') && (LocaleCompare(profile_name,argv[i]+1) == 0))
750
0
                break;
751
0
              if (GlobExpression(profile_name,argv[i]))
752
0
                {
753
0
                  (void) strlcpy(profile_remove,profile_name,sizeof(profile_remove));
754
0
                  break;
755
0
                }
756
0
            }
757
0
        }
758
0
      DeallocateImageProfileIterator(profile_iterator);
759
0
      if (strlen(profile_remove))
760
0
        (void) DeleteImageProfile(image,profile_remove);
761
762
0
      for(i=0; argv[i] != NULL; i++)
763
0
        MagickFreeMemory(argv[i]);
764
0
      MagickFreeMemory(argv);
765
766
0
      return(MagickPass);
767
0
    }
768
  /*
769
    Add a ICM, IPTC, or generic profile to the image.
770
  */
771
0
  if ((LocaleCompare("8bim",name) == 0) || (LocaleCompare("iptc",name) == 0))
772
0
    {
773
0
      if (clone)
774
0
        {
775
0
          (void) SetImageProfile(image,name,profile,length);
776
0
        }
777
0
      else
778
0
        {
779
0
          (void) SetImageProfile(image,name,profile,length);
780
0
          MagickFreeMemory(profile);
781
0
        }
782
0
      return(MagickPass);
783
0
    }
784
0
  if (LocaleCompare("icm",name) == 0)
785
0
    {
786
0
      const unsigned char
787
0
        *existing_profile;
788
789
0
      size_t
790
0
        existing_profile_length=0;
791
792
      /* Check for identical input and output profiles. Return on identity. */
793
0
      existing_profile=GetImageProfile(image,"ICM",&existing_profile_length);
794
795
0
      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
796
0
                            "New Profile: %lu bytes, Existing Profile: %lu bytes",
797
0
                            (unsigned long) length,
798
0
                            (unsigned long) existing_profile_length);
799
800
0
      if ((length != 0) && (length == existing_profile_length) &&
801
0
          (memcmp(existing_profile,profile,length) == 0))
802
0
        {
803
0
          return(MagickPass);
804
0
        }
805
806
      /* Convert to new colors if we have both an old and a new profile. */
807
0
      if ((existing_profile_length != 0) && (length != 0))
808
0
        {
809
0
#if defined(HasLCMS)
810
811
0
          TransformInfo
812
0
            xform;
813
814
0
          MagickBool
815
0
            transform_colormap;
816
817
          /*
818
            Transform pixel colors as defined by the color profiles.
819
          */
820
0
          (void) memset(&xform,0,sizeof(xform));
821
0
          xform.signature=MagickSignature;
822
0
          xform.image=image;
823
0
          cmsSetLogErrorHandler(lcmsReplacementErrorHandler);
824
825
0
          xform.source_profile=cmsOpenProfileFromMemTHR((cmsContext) &xform,
826
0
                                                        (unsigned char *) existing_profile,
827
0
                                                        (cmsUInt32Number) existing_profile_length);
828
0
          xform.target_profile=cmsOpenProfileFromMemTHR((cmsContext) &xform,
829
0
                                                        (unsigned char *) profile,
830
0
                                                        (cmsUInt32Number) length);
831
0
          if ((xform.source_profile == (cmsHPROFILE) NULL) ||
832
0
              (xform.target_profile == (cmsHPROFILE) NULL))
833
0
            ThrowBinaryException3(ResourceLimitError,UnableToManageColor,
834
0
                                  UnableToOpenColorProfile);
835
836
0
          switch (cmsGetColorSpace(xform.source_profile))
837
0
            {
838
0
            case cmsSigXYZData:
839
0
              {
840
0
                xform.source_colorspace=XYZColorspace;
841
0
                xform.source_type=TYPE_XYZ_16;
842
0
                break;
843
0
              }
844
0
            case cmsSigLabData:
845
0
              {
846
0
                xform.source_colorspace=LABColorspace;
847
0
                xform.source_type=TYPE_Lab_16;
848
0
                break;
849
0
              }
850
0
            case cmsSigCmykData:
851
0
              {
852
0
                xform.source_colorspace=CMYKColorspace;
853
0
                xform.source_type=TYPE_CMYK_16;
854
0
                break;
855
0
              }
856
0
            case cmsSigYCbCrData:
857
0
              {
858
0
                xform.source_colorspace=YCbCrColorspace;
859
0
                xform.source_type=TYPE_YCbCr_16;
860
0
                break;
861
0
              }
862
0
            case cmsSigLuvData:
863
0
              {
864
0
                xform.source_colorspace=YUVColorspace;
865
0
                xform.source_type=TYPE_YUV_16;
866
0
                break;
867
0
              }
868
0
            case cmsSigGrayData:
869
0
              {
870
0
                xform.source_colorspace=GRAYColorspace;
871
0
                xform.source_type=TYPE_GRAY_16;
872
0
                break;
873
0
              }
874
0
            case cmsSigRgbData:
875
0
              {
876
0
                xform.source_colorspace=RGBColorspace;
877
0
                xform.source_type=TYPE_RGB_16;
878
0
                break;
879
0
              }
880
0
            default:
881
0
              {
882
0
                xform.source_colorspace=UndefinedColorspace;
883
0
                xform.source_type=TYPE_RGB_16;
884
0
                break;
885
0
              }
886
0
            }
887
0
          switch (cmsGetColorSpace(xform.target_profile))
888
0
            {
889
0
            case cmsSigXYZData:
890
0
              {
891
0
                xform.target_colorspace=XYZColorspace;
892
0
                xform.target_type=TYPE_XYZ_16;
893
0
                break;
894
0
              }
895
0
            case cmsSigLabData:
896
0
              {
897
0
                xform.target_colorspace=LABColorspace;
898
0
                xform.target_type=TYPE_Lab_16;;
899
0
                break;
900
0
              }
901
0
            case cmsSigCmykData:
902
0
              {
903
0
                xform.target_colorspace=CMYKColorspace;
904
0
                xform.target_type=TYPE_CMYK_16;
905
0
                break;
906
0
              }
907
0
            case cmsSigYCbCrData:
908
0
              {
909
0
                xform.target_colorspace=YCbCrColorspace;
910
0
                xform.target_type=TYPE_YCbCr_16;
911
0
                break;
912
0
              }
913
0
            case cmsSigLuvData:
914
0
              {
915
0
                xform.target_colorspace=YUVColorspace;
916
0
                xform.target_type=TYPE_YUV_16;
917
0
                break;
918
0
              }
919
0
            case cmsSigGrayData:
920
0
              {
921
0
                xform.target_colorspace=GRAYColorspace;
922
0
                xform.target_type=TYPE_GRAY_16;
923
0
                break;
924
0
              }
925
0
            case cmsSigRgbData:
926
0
              {
927
0
                xform.target_colorspace=RGBColorspace;
928
0
                xform.target_type=TYPE_RGB_16;
929
0
                break;
930
0
              }
931
0
            default:
932
0
              {
933
0
                xform.target_colorspace=UndefinedColorspace;
934
0
                xform.target_type=TYPE_RGB_16;
935
0
                break;
936
0
              }
937
0
            }
938
939
          /* Colorspace undefined */
940
0
          if ((xform.source_colorspace == UndefinedColorspace) ||
941
0
              (xform.target_colorspace == UndefinedColorspace))
942
0
            {
943
0
              (void) cmsCloseProfile(xform.source_profile);
944
0
              (void) cmsCloseProfile(xform.target_profile);
945
0
              ThrowBinaryException3(ImageError,UnableToAssignProfile,
946
0
                                    ColorspaceColorProfileMismatch);
947
0
            }
948
          /* Gray colorspace */
949
0
          if (IsGrayColorspace(xform.source_colorspace) &&
950
0
              !IsGrayImage(image,&image->exception))
951
0
            {
952
0
              (void) cmsCloseProfile(xform.source_profile);
953
0
              (void) cmsCloseProfile(xform.target_profile);
954
0
              ThrowBinaryException3(ImageError,UnableToAssignProfile,
955
0
                                    ColorspaceColorProfileMismatch);
956
0
            }
957
          /* CMYK colorspace */
958
0
          if (IsCMYKColorspace(xform.source_colorspace) &&
959
0
              !IsCMYKColorspace(image->colorspace))
960
0
            {
961
0
              (void) cmsCloseProfile(xform.source_profile);
962
0
              (void) cmsCloseProfile(xform.target_profile);
963
0
              ThrowBinaryException3(ImageError,UnableToAssignProfile,
964
0
                                    ColorspaceColorProfileMismatch);
965
0
            }
966
          /* YCbCr colorspace */
967
0
          if (IsYCbCrColorspace(xform.source_colorspace) &&
968
0
              !IsYCbCrColorspace(image->colorspace))
969
0
            {
970
0
              (void) cmsCloseProfile(xform.source_profile);
971
0
              (void) cmsCloseProfile(xform.target_profile);
972
0
              ThrowBinaryException3(ImageError,UnableToAssignProfile,
973
0
                                    ColorspaceColorProfileMismatch);
974
0
            }
975
          /* Verify that source colorspace type is supported */
976
0
          if (!IsGrayColorspace(xform.source_colorspace) &&
977
0
              !IsCMYKColorspace(xform.source_colorspace) &&
978
0
              !IsLABColorspace(xform.source_colorspace) &&
979
0
              !IsYCbCrColorspace(xform.source_colorspace) &&
980
0
              !IsRGBColorspace(image->colorspace))
981
0
            {
982
0
              (void) cmsCloseProfile(xform.source_profile);
983
0
              (void) cmsCloseProfile(xform.target_profile);
984
0
              ThrowBinaryException3(ImageError,UnableToAssignProfile,
985
0
                                    ColorspaceColorProfileMismatch);
986
0
            }
987
988
0
          (void) LogMagickEvent(TransformEvent,GetMagickModule(),
989
0
                                "Source pixel format: COLORSPACE=%s SWAPFIRST=%d "
990
0
                                "FLAVOR=%d PLANAR=%d ENDIAN16=%d DOSWAP=%d "
991
0
                                "EXTRA=%d CHANNELS=%d BYTES=%d",
992
0
                                PixelTypeToString((int) T_COLORSPACE(xform.source_type)),
993
0
                                (int) T_SWAPFIRST(xform.source_type),
994
0
                                (int) T_FLAVOR(xform.source_type),
995
0
                                (int) T_PLANAR(xform.source_type),
996
0
                                (int) T_ENDIAN16(xform.source_type),
997
0
                                (int) T_DOSWAP(xform.source_type),
998
0
                                (int) T_EXTRA(xform.source_type),
999
0
                                (int) T_CHANNELS(xform.source_type),
1000
0
                                (int) T_BYTES(xform.source_type));
1001
1002
0
          (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1003
0
                                "Target pixel format: COLORSPACE=%s SWAPFIRST=%d "
1004
0
                                "FLAVOR=%d PLANAR=%d ENDIAN16=%d DOSWAP=%d "
1005
0
                                "EXTRA=%d CHANNELS=%d BYTES=%d",
1006
0
                                PixelTypeToString((int) T_COLORSPACE(xform.target_type)),
1007
0
                                (int) T_SWAPFIRST(xform.target_type),
1008
0
                                (int) T_FLAVOR(xform.target_type),
1009
0
                                (int) T_PLANAR(xform.target_type),
1010
0
                                (int) T_ENDIAN16(xform.target_type),
1011
0
                                (int) T_DOSWAP(xform.target_type),
1012
0
                                (int) T_EXTRA(xform.target_type),
1013
0
                                (int) T_CHANNELS(xform.target_type),
1014
0
                                (int) T_BYTES(xform.target_type));
1015
1016
0
          switch (image->rendering_intent)
1017
0
            {
1018
0
            case AbsoluteIntent:
1019
0
              xform.intent=INTENT_ABSOLUTE_COLORIMETRIC;
1020
0
              break;
1021
0
            case PerceptualIntent:
1022
0
              xform.intent=INTENT_PERCEPTUAL;
1023
0
              break;
1024
0
            case RelativeIntent:
1025
0
              xform.intent=INTENT_RELATIVE_COLORIMETRIC;
1026
0
              break;
1027
0
            case SaturationIntent:
1028
0
              xform.intent=INTENT_SATURATION;
1029
0
              break;
1030
0
            default:
1031
0
              xform.intent=INTENT_PERCEPTUAL;
1032
0
              break;
1033
0
            }
1034
1035
          /*
1036
            Transform just the colormap if the image is colormapped and we're
1037
            not transforming from gray to RGB/CMYK. A gray to RGB/CMYK
1038
            transformation must create a direct class image for the new image
1039
            to match the color profile even if the new image only has gray
1040
            colors. CMYK images are never color mapped.
1041
          */
1042
0
          transform_colormap=(image->storage_class == PseudoClass) &&
1043
0
            (xform.target_colorspace != CMYKColorspace) &&
1044
0
            ((xform.source_colorspace != GRAYColorspace) ||
1045
0
             (xform.source_colorspace == xform.target_colorspace));
1046
1047
          /* build pre-computed transforms? */
1048
0
          xform.flags=(transform_colormap ? cmsFLAGS_NOOPTIMIZE : 0);
1049
1050
0
          xform.transform=AllocateThreadViewDataSet(MagickFreeCMSTransform,
1051
0
                                                    image,&image->exception);
1052
0
          if (xform.transform == (ThreadViewDataSet *) NULL)
1053
0
            status=MagickFail;
1054
0
          if (status != MagickFail)
1055
0
            {
1056
0
              cmsHTRANSFORM
1057
0
                transform;
1058
1059
0
              unsigned int
1060
0
                index;
1061
1062
0
              for (index=0 ; index < GetThreadViewDataSetAllocatedViews(xform.transform); index++)
1063
0
                {
1064
0
                  transform=cmsCreateTransformTHR((cmsContext) &xform,/* transform handle */
1065
0
                                               xform.source_profile,  /* input profile */
1066
0
                                               xform.source_type,     /* input pixel format */
1067
0
                                               xform.target_profile,  /* output profile */
1068
0
                                               xform.target_type,     /* output pixel format */
1069
0
                                               xform.intent,          /* rendering intent */
1070
0
                                               xform.flags            /* pre-computed transforms? */
1071
0
                                               );
1072
0
                  if (transform == (cmsHTRANSFORM) NULL)
1073
0
                    {
1074
0
                      status=MagickFail;
1075
0
                      break;
1076
0
                    }
1077
0
                  AssignThreadViewData(xform.transform,index,transform);
1078
0
                }
1079
0
            }
1080
0
          (void) cmsCloseProfile(xform.source_profile);
1081
0
          (void) cmsCloseProfile(xform.target_profile);
1082
0
          if (status == MagickFail)
1083
0
            {
1084
0
              DestroyThreadViewDataSet(xform.transform);
1085
0
              ThrowBinaryException3(ResourceLimitError,UnableToManageColor,
1086
0
                                    UnableToCreateColorTransform);
1087
0
            }
1088
1089
0
          if (transform_colormap)
1090
0
            {
1091
0
              (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1092
0
                                    "Performing pseudo class color conversion");
1093
1094
0
              (void) ProfileImagePixels(NULL,
1095
0
                                        &xform,
1096
0
                                        image,
1097
0
                                        image->colormap,
1098
0
                                        (IndexPacket *) NULL,
1099
0
                                        image->colors,
1100
0
                                        &image->exception);
1101
1102
0
              (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1103
0
                                    "Completed pseudo class color conversion");
1104
0
              status &= SyncImage(image);
1105
0
            }
1106
0
          else
1107
0
            {
1108
0
              (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1109
0
                                    "Performing direct class color conversion");
1110
0
              if (image->storage_class == PseudoClass)
1111
0
                {
1112
0
                  status &= SyncImage(image);
1113
0
                  image->storage_class=DirectClass;
1114
0
                }
1115
0
              if (xform.target_colorspace == CMYKColorspace)
1116
0
                image->colorspace=xform.target_colorspace;
1117
1118
0
              status=PixelIterateMonoModify(ProfileImagePixels,
1119
0
                                            NULL,
1120
0
                                            ProfileImageText,
1121
0
                                            NULL,&xform,0,0,image->columns,image->rows,
1122
0
                                            image,&image->exception);
1123
1124
0
              (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1125
0
                                    "Completed direct class color conversion");
1126
1127
0
            }
1128
0
          image->colorspace=xform.target_colorspace;
1129
          /*
1130
            We can't be sure black and white stays exactly black and white
1131
            and that gray colors transform to gray colors.
1132
          */
1133
0
          image->is_grayscale=IsGrayColorspace(xform.target_colorspace);
1134
0
          image->is_monochrome=False;
1135
0
          DestroyThreadViewDataSet(xform.transform);
1136
1137
          /*
1138
            Throw away the old profile after conversion before we
1139
            assign a new one.
1140
          */
1141
0
          DeleteImageProfile(image,"ICM");
1142
#else
1143
          ThrowBinaryException(MissingDelegateError,LCMSLibraryIsNotAvailable,
1144
                               image->filename);
1145
#endif
1146
0
        }
1147
1148
      /*
1149
        TODO: If the image *did not* already have a color profile,
1150
        verify that the colorspace of the new profile is valid for the
1151
        colorspace of the image. If LCMS is not available we should
1152
        refuse to assign a new profile (just like we're refusing a
1153
        conversion above) as we can't be sure the assignment is valid.
1154
        We might be trying to assign a CMYK profile to an RGB image,
1155
        for instance.
1156
      */
1157
1158
0
      (void) SetImageProfile(image,"ICM",profile,length);
1159
0
      if (!clone)
1160
0
        MagickFreeMemory(profile);
1161
0
      return(status);
1162
0
    }
1163
1164
0
  status &= SetImageProfile(image,name,profile,length);
1165
0
  if (!clone)
1166
0
    MagickFreeMemory(profile);
1167
1168
0
  return(status);
1169
0
}
1170

1171
/*
1172
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1173
%                                                                             %
1174
%                                                                             %
1175
%                                                                             %
1176
%     S e t I m a g e P r o f i l e                                           %
1177
%                                                                             %
1178
%                                                                             %
1179
%                                                                             %
1180
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1181
%
1182
%  SetImageProfile adds a named profile to the image. If a profile with the
1183
%  same name already exists, then it is replaced. If a null profile address
1184
%  is supplied, then an existing profile is removed. The profile is copied
1185
%  into the image. Note that this function does not execute CMS color
1186
%  profiles. Any existing CMS color profile is simply replaced. Use the
1187
%  ProfileImage() function in order to execute a CMS color profile.
1188
%
1189
%  Older versions of this function stored profiles named "8BIM" and "IPTC"
1190
%  in the same storage location.  This is no longer the case.  However,
1191
%  GetImageProfile() will try the alternate name if the specifically
1192
%  requested profile name is not available.  Note that when trying to remove
1193
%  a profile, it may be necessary to remove both names in order for an
1194
%  "IPTC" profile to no longer be included in output file formats.
1195
%
1196
%  The format of the SetImageProfile method is:
1197
%
1198
%      unsigned int SetImageProfile(Image *image,const char *name,
1199
%                            const unsigned char *profile,const size_t length)
1200
%
1201
%  A description of each parameter follows:
1202
%
1203
%    o image: The image.
1204
%
1205
%    o name: Profile name. Valid names are "8BIM", EXIF, "ICM", "IPTC",
1206
%               XMP, or any unique text string.
1207
%
1208
%    o profile: Address of profile to add. Pass zero to remove an existing
1209
%               profile.
1210
%
1211
%    o length: The length of the profile.
1212
%
1213
*/
1214
MagickExport MagickPassFail
1215
SetImageProfile(Image *image,const char *name, const unsigned char *profile,
1216
                const size_t length)
1217
138k
{
1218
138k
  char
1219
138k
    ucase_name[MaxTextExtent];
1220
1221
138k
  unsigned int
1222
138k
    status = MagickPass;
1223
1224
138k
  assert(image != (Image *) NULL);
1225
138k
  assert(image->signature == MagickSignature);
1226
138k
  assert(name != NULL);
1227
1228
138k
  if (strlcpy(ucase_name,name,sizeof(ucase_name)) >= sizeof(ucase_name))
1229
0
    {
1230
0
      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1231
0
                            "Profile name too long! (%s)",name);
1232
0
      return MagickFail;
1233
0
    }
1234
138k
  LocaleUpper(ucase_name);
1235
1236
138k
  if ((profile == 0) && (image->profiles != 0))
1237
1
    {
1238
      /*
1239
        Remove existing entry.
1240
      */
1241
1
      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1242
1
                            "Removing %s profile",name);
1243
1
      status &= MagickMapRemoveEntry(image->profiles,name);
1244
1
    }
1245
138k
  else
1246
138k
    {
1247
      /*
1248
        Add or replace entry.
1249
      */
1250
138k
      if (image->profiles == 0)
1251
46.9k
        image->profiles=MagickMapAllocateMap(MagickMapCopyResourceLimitedBlob,
1252
46.9k
                                             MagickMapDeallocateResourceLimitedBlob);
1253
1254
138k
      if (image->profiles == 0)
1255
0
        ThrowBinaryException3(ResourceLimitError,MemoryAllocationFailed,
1256
138k
                              UnableToAddOrRemoveProfile);
1257
1258
138k
      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1259
138k
                            "Adding %s profile with length %ld bytes",name,
1260
138k
                            (unsigned long) length);
1261
138k
      if ((profile != 0) && (length != 0))
1262
138k
        {
1263
138k
          status &= MagickMapAddEntry(image->profiles,name,profile,length,
1264
138k
                                      &image->exception);
1265
138k
        }
1266
138k
    }
1267
138k
  return (status);
1268
138k
}