Coverage Report

Created: 2026-05-24 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/coders/heif.c
Line
Count
Source
1
/*
2
% Copyright (C) 2023-2026 GraphicsMagick Group
3
%
4
% This program is covered by multiple licenses, which are described in
5
% Copyright.txt. You should have received a copy of Copyright.txt with this
6
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
7
%
8
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9
%                                                                             %
10
%                                                                             %
11
%                       H   H  EEEEE  I  FFFFF                                %
12
%                       H   H  E      I  F                                    %
13
%                       HHHHH  EEEEE  I  FFFF                                 %
14
%                       H   H  E      I  F                                    %
15
%                       H   H  EEEEE  I  F                                    %
16
%                                                                             %
17
%           Read HEIF/HEIC/AVIF image formats using libheif.                  %
18
%                                                                             %
19
%           Initial version contributed by Tobias Mark, 2021.                 %
20
%           Since developed by Bob Friesenhahn.                               %
21
%                                                                             %
22
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
23
* Status: Support for reading a single image.
24
* Ubuntu 22.04 LTS comes with 1.17.6 (Dec 20, 2023)!
25
* Debian 12 Backports provides 1.19.7 (March 3, 2025)
26
* Maybe limit support to version 1.17.6 (Dec 20, 2023) or later?
27
* But 1.20.0 (July 1, 2025) introduces support for sequences!
28
* Meanwhile, 1.14.0 is required to compile.
29
*
30
* Nokia technical publication on HEIF: https://nokiatech.github.io/heif/technical.html
31
*
32
* Yet to be done:
33
*
34
*   o Read/render HEIF tiles directly.
35
*   o Possibly support reading directly from file (heif_context_read_from_file())
36
*     rather than an in-memory BLOB.
37
*   o Support selecting to read HEIF depth image.
38
*   o Use HEIF transform properties to evaluate image orientation (requires 1.17).
39
*   o Provide user control over chroma upsampling options.
40
*   o Support reading sequences (requires 1.20).
41
*
42
*   o Write HEIF (HEIC, AVIF, more?)
43
*
44
* Definitions influencing the reader:
45
*
46
*  heif:ignore-transformations=yes/no (default no)
47
*
48
*    Determines if libheif will automatically rotate the image if it is not
49
*    right-side up.
50
*
51
*  heif:preserve-colorspace=yes/no (default no)
52
*
53
*    If image is stored in YCbCr, then return original YCbCr, but without the
54
*    benefit of applying an associated HEIF color profile.
55
*
56
*  heif:interleaved-rgb-decode=yes/no (default no)
57
*
58
*    RGB decoding can be done using individual planes (e.g. R, G, B, A), or
59
*    in an interleaved RGB(A) format.  This option controls libheif's image
60
*    decoding, but should not produce any change in the final result.
61
*/
62
63
#include "magick/studio.h"
64
#include "magick/attribute.h"
65
#include "magick/blob.h"
66
#include "magick/colormap.h"
67
#include "magick/log.h"
68
#include "magick/constitute.h"
69
#include "magick/magick.h"
70
#include "magick/monitor.h"
71
#include "magick/pixel_cache.h"
72
#include "magick/profile.h"
73
#include "magick/resource.h"
74
#include "magick/utility.h"
75
#include "magick/resource.h"
76
#include "magick/static.h"
77
78
#if defined(HasHEIF)
79
80
/* Set to 1 to enable the currently non-functional progress monitor callbacks */
81
#define HEIF_ENABLE_PROGRESS_MONITOR 0
82
83
#if defined(HAVE_LIBHEIF_HEIF_SEQUENCES_H)
84
/* Set to 1 to enable use of <libheif/heif_sequences.h> APIs */
85
#define HEIF_SUPPORT_SEQUENCES 1
86
#endif /* if defined(HAVE_LIBHEIF_HEIF_SEQUENCES_H) */
87
88
/* Set to 1 to enable RGB(A) decode in interleaved format */
89
#define HEIF_SUPPORT_INTERLEAVED 1
90
91
/*
92
  Libheif added LIBHEIF_MAKE_VERSION in 1.14.0 (October 31, 2021).
93
  Duplicate its definition so we can detect versions lower than
94
  1.14.0.
95
 */
96
#if !defined(LIBHEIF_HAVE_VERSION)
97
#define LIBHEIF_MAKE_VERSION(h, m, l) (((h) << 24 | (m) << 16 | (l) << 8) | 0)
98
#define LIBHEIF_HAVE_VERSION(h, m, l) (LIBHEIF_NUMERIC_VERSION >= LIBHEIF_MAKE_VERSION(h, m, l))
99
#endif /* if !defined(LIBHEIF_HAVE_VERSION) */
100
101
#if LIBHEIF_HAVE_VERSION(1,14,0)
102
#  error "Libheif must be at least 1.14.0!"
103
#endif
104
105
/* FIXME: It would be nice to use MagickSizeStrToInt64(), but it does
106
   not provide error status */
107
#define MagickAttributeToU32(image_info, magick, name, out_val)         \
108
972k
  {                                                                     \
109
972k
    const char *string_value;                                           \
110
972k
    if ((string_value = AccessDefinition(image_info,magick,name)))      \
111
972k
      {                                                                 \
112
0
        unsigned int ui_value;                                          \
113
0
        if (MagickAtoUIChk(string_value, &ui_value) != MagickFail)      \
114
0
          {                                                             \
115
0
            out_val = ui_value;                                         \
116
0
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),         \
117
0
                                  "security limits: %s = "              \
118
0
                                  "%" MAGICK_UINTMAX_F "u", name,       \
119
0
                                  (magick_uintmax_t) out_val);          \
120
0
          }                                                             \
121
0
      }                                                                 \
122
972k
  }
123
124
/* Rely on the compiler to remove unused code */
125
126
/* FIXME: It would be nice to use MagickSizeStrToInt64(), but it does
127
   not provide error status */
128
#define MagickAttributeToU64(image_info, magick, name, out_val)         \
129
389k
  {                                                                     \
130
389k
    const char *string_value;                                           \
131
389k
    if ((string_value = AccessDefinition(image_info,magick,name)))      \
132
389k
    {                                                                   \
133
0
      if (sizeof(long) == 64)                                           \
134
0
        {                                                               \
135
0
          unsigned long ul_value;                                       \
136
0
          if (MagickAtoULChk(string_value, &ul_value) != MagickFail)    \
137
0
            {                                                           \
138
0
              out_val = ul_value;                                       \
139
0
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),       \
140
0
                                    "security limits: %s = "            \
141
0
                                    "%" MAGICK_UINTMAX_F "u", name,     \
142
0
                                    (magick_uintmax_t) out_val);        \
143
0
            }                                                           \
144
0
        }                                                               \
145
0
      else                                                              \
146
0
        {                                                               \
147
0
          unsigned long long ull_value;                                 \
148
0
          if (MagickAtoULLChk(string_value, &ull_value) != MagickFail)  \
149
0
            {                                                           \
150
0
              out_val = ull_value;                                      \
151
0
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),       \
152
0
                                    "security limits: %s = "            \
153
0
                                    "%" MAGICK_UINTMAX_F "u", name,     \
154
0
                                    (magick_uintmax_t) out_val);        \
155
0
            }                                                           \
156
0
        }                                                               \
157
0
    }                                                                   \
158
389k
  }
159
160
#include <libheif/heif.h>
161
#if defined(HEIF_SUPPORT_SEQUENCES) && HEIF_SUPPORT_SEQUENCES
162
#include <libheif/heif_sequences.h>
163
#endif /* if defined(HEIF_SUPPORT_SEQUENCES) */
164
static MagickBool heif_initialized = MagickFalse;
165

166
/*
167
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
168
%                                                                             %
169
%                                                                             %
170
%                                                                             %
171
%   I s H E I F                                                               %
172
%                                                                             %
173
%                                                                             %
174
%                                                                             %
175
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
176
%
177
%  Method IsHEIF returns True if the image format type, identified by the
178
%  magick string, is supported by this HEIF reader.
179
%
180
%  The format of the IsHEIF  method is:
181
%
182
%      MagickBool IsHEIF(const unsigned char *magick,const size_t length)
183
%
184
%  A description of each parameter follows:
185
%
186
%    o status:  Method IsHEIF returns MagickTrue if the image format type is HEIF.
187
%
188
%    o magick: This string is generally the first few bytes of an image file
189
%      or blob.
190
%
191
%    o length: Specifies the length of the magick string.
192
%
193
%
194
*/
195
static MagickBool IsHEIF(const unsigned char *magick,const size_t length)
196
97.2k
{
197
97.2k
  enum heif_filetype_result
198
97.2k
    heif_filetype;
199
200
97.2k
  MagickBool
201
97.2k
    status = MagickFalse;
202
203
97.2k
  if ((length < 12) || (length > (size_t) INT_MAX))
204
88
    {
205
88
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
206
88
                            "Data is too short (%zu bytes)", length);
207
88
      return status;
208
88
    }
209
210
97.1k
  heif_filetype = heif_check_filetype(magick, (int) length);
211
97.1k
  switch (heif_filetype)
212
97.1k
    {
213
52.3k
    case heif_filetype_yes_supported:
214
52.3k
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
215
52.3k
                            "Data is HEIF (supported by libheif)");
216
52.3k
      status = MagickTrue;
217
52.3k
      break;
218
41.7k
    case heif_filetype_yes_unsupported:
219
41.7k
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
220
41.7k
                            "Data is HEIF (unsupported by libheif)");
221
41.7k
      status = MagickTrue;
222
41.7k
      break;
223
2.89k
    case heif_filetype_maybe:
224
2.89k
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
225
2.89k
                            "Data may be HEIF");
226
2.89k
      status = MagickTrue;
227
2.89k
      break;
228
166
    case heif_filetype_no:
229
166
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
230
166
                            "Data not HEIF");
231
166
      status = MagickFalse;
232
166
      break;
233
0
    default:
234
0
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
235
0
                            "Unexpected enum value %d",
236
0
                            (int) heif_filetype);
237
0
      status = MagickFalse;
238
97.1k
    }
239
97.1k
  return status;
240
97.1k
}
241
/*
242
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
243
%                                                                             %
244
%                                                                             %
245
%                                                                             %
246
%   R e a d H E I F I m a g e                                                 %
247
%                                                                             %
248
%                                                                             %
249
%                                                                             %
250
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
251
%
252
%  ReadHEIFImage() reads an image in the HEIF image format.
253
%
254
%  The format of the ReadHEIFImage method is:
255
%
256
%      Image *ReadHEIFImage(const ImageInfo *image_info,
257
%        ExceptionInfo *exception)
258
%
259
%  A description of each parameter follows:
260
%
261
%    o image_info: the image info.
262
%
263
%    o exception: return any errors or warnings in this structure.
264
%
265
*/
266
267
#define NATIVE_PLANAR_DECODE 1
268
269
#define HEIFReadCleanup()                                              \
270
97.2k
  do                                                                   \
271
97.2k
    {                                                                  \
272
97.2k
      MagickFreeResourceLimitedMemory(heif_item_id *,item_ids);        \
273
97.2k
      if (heif_image)                                                  \
274
97.2k
        heif_image_release(heif_image);                                \
275
97.2k
      if (heif_image_handle)                                           \
276
97.2k
        heif_image_handle_release(heif_image_handle);                  \
277
97.2k
      if (heif)                                                        \
278
97.2k
        heif_context_free(heif);                                       \
279
97.2k
      if (decode_options)                                              \
280
97.2k
        heif_decoding_options_free(decode_options);                    \
281
97.2k
      if (in_buf_allocated)                                            \
282
97.2k
        MagickFreeResourceLimitedMemory(unsigned char *,in_buf);       \
283
97.2k
    } while (0);
284
285
#define ThrowHEIFReaderException(code_,reason_,image_) \
286
2.83k
  do                                                   \
287
52.7k
    {                                                  \
288
52.7k
      HEIFReadCleanup();                               \
289
52.7k
      ThrowReaderException(code_,reason_,image_);      \
290
0
    } while (0);
291
292
/*
293
  Read metadata (Exif and XMP)
294
*/
295
static MagickPassFail ReadMetadata(struct heif_image_handle *heif_image_handle,
296
                                   Image *image, const MagickBool ignore_transformations)
297
45.6k
{
298
45.6k
  int
299
45.6k
    count,
300
45.6k
    i;
301
302
45.6k
  heif_item_id
303
45.6k
    *ids;
304
305
45.6k
  struct heif_error
306
45.6k
    err;
307
308
  /* Get number of metadata blocks attached to image */
309
45.6k
  count=heif_image_handle_get_number_of_metadata_blocks(heif_image_handle,
310
45.6k
                                                        /*type_filter*/ NULL);
311
45.6k
  if (count==0)
312
41.6k
    return MagickPass;
313
314
4.03k
  ids=MagickAllocateResourceLimitedArray(heif_item_id *,count,sizeof(*ids));
315
4.03k
  if (ids == (heif_item_id *) NULL)
316
4.03k
    ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed,(char *) NULL);
317
318
  /* Get list of metadata block ids */
319
4.03k
  count=heif_image_handle_get_list_of_metadata_block_IDs(heif_image_handle, NULL,
320
4.03k
                                                         ids,count);
321
322
  /* For each block id ... */
323
9.81k
  for (i=0; i < count; i++)
324
5.78k
    {
325
5.78k
      const char
326
5.78k
        *content_type,
327
5.78k
        *profile_name;
328
329
5.78k
      size_t
330
5.78k
        exif_pad = 0,
331
5.78k
        profile_size;
332
333
5.78k
      unsigned char
334
5.78k
        *profile;
335
336
      /* Access string indicating the type of the metadata (e.g. "Exif") */
337
5.78k
      profile_name=heif_image_handle_get_metadata_type(heif_image_handle,ids[i]);
338
339
      /* Access string indicating the content type */
340
5.78k
      content_type=heif_image_handle_get_metadata_content_type(heif_image_handle,ids[i]);
341
342
      /* Get the size of the raw metadata, as stored in the HEIF file */
343
5.78k
      profile_size=heif_image_handle_get_metadata_size(heif_image_handle,ids[i]);
344
345
5.78k
      if (image->logging)
346
5.78k
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
347
5.78k
                              "Profile \"%s\" with content type \"%s\""
348
5.78k
                              " and size %" MAGICK_SIZE_T_F "u bytes",
349
5.78k
                              profile_name ? profile_name : "(null)",
350
5.78k
                              content_type ? content_type : "(null)",
351
5.78k
                              (MAGICK_SIZE_T) profile_size);
352
353
5.78k
      if (NULL != profile_name && profile_size > 0)
354
4.42k
        {
355
4.42k
          if (strncmp(profile_name,"Exif",4) == 0)
356
4.10k
            exif_pad=2;
357
358
          /* Allocate memory for profile */
359
4.42k
          profile=MagickAllocateResourceLimitedArray(unsigned char*,profile_size+exif_pad,
360
4.42k
                                                     sizeof(*profile));
361
4.42k
          if (profile == (unsigned char*) NULL)
362
0
            {
363
0
              MagickFreeResourceLimitedMemory(heif_item_id *,ids);
364
0
              ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed,(char *) NULL);
365
0
            }
366
367
          /*
368
            Copy metadata into 'profile' buffer. For Exif data, you
369
            probably have to skip the first four bytes of the data,
370
            since they indicate the offset to the start of the TIFF
371
            header of the Exif data.
372
          */
373
4.42k
          err=heif_image_handle_get_metadata(heif_image_handle,ids[i],profile+exif_pad);
374
375
4.42k
          if (err.code != heif_error_Ok)
376
0
            {
377
0
              if (image->logging)
378
0
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
379
0
                                      "heif_image_handle_get_metadata() "
380
0
                                      "reports error %d, suberror %d, message \"%s\"",
381
0
                                      err.code, err.subcode, err.message);
382
0
              MagickFreeResourceLimitedMemory(unsigned char *,profile);
383
0
              MagickFreeResourceLimitedMemory(heif_item_id *,ids);
384
0
              ThrowBinaryException(CorruptImageError,
385
0
                                   AnErrorHasOccurredReadingFromFile,image->filename);
386
0
            }
387
388
4.42k
          if (strncmp(profile_name,"Exif",4) == 0 && profile_size > 4)
389
4.09k
            {
390
              /* Parse and skip offset to TIFF header */
391
4.09k
              unsigned char *p = profile;
392
4.09k
              magick_uint32_t offset;
393
394
              /* Big-endian offset decoding */
395
4.09k
              offset = (magick_uint32_t) p[exif_pad+0] << 24 |
396
4.09k
                (magick_uint32_t) p[exif_pad+1] << 16 |
397
4.09k
                (magick_uint32_t) p[exif_pad+2] << 8 |
398
4.09k
                (magick_uint32_t) p[exif_pad+3];
399
400
              /*
401
                If the TIFF header offset is not zero, then need to
402
                move the TIFF data forward to the correct offset.
403
              */
404
4.09k
              profile_size -= 4;
405
4.09k
              if (offset > 0 && offset < profile_size)
406
149
                {
407
149
                  profile_size -= offset;
408
409
                  /* Strip any EOI marker if payload starts with a JPEG marker */
410
149
                  if (profile_size > 2 &&
411
144
                      (memcmp(p+exif_pad+4,"\xff\xd8",2) == 0 ||
412
139
                       memcmp(p+exif_pad+4,"\xff\xe1",2) == 0) &&
413
8
                      memcmp(p+exif_pad+4+profile_size-2,"\xff\xd9",2) == 0)
414
0
                    profile_size -= 2;
415
416
149
                  (void) memmove(p+exif_pad+4,p+exif_pad+4+offset,profile_size);
417
149
                }
418
419
4.09k
              p[0]='E';
420
4.09k
              p[1]='x';
421
4.09k
              p[2]='i';
422
4.09k
              p[3]='f';
423
4.09k
              p[4]='\0';
424
4.09k
              p[5]='\0';
425
426
4.09k
              SetImageProfile(image,"EXIF",profile,profile_size+exif_pad+4);
427
428
              /*
429
                Retrieve image orientation from EXIF and store in
430
                image.
431
              */
432
4.09k
              {
433
4.09k
                const ImageAttribute
434
4.09k
                  *attribute;
435
436
4.09k
                attribute = GetImageAttribute(image,"EXIF:Orientation");
437
4.09k
                if ((attribute != (const ImageAttribute *) NULL) &&
438
173
                    (attribute->value != (char *) NULL))
439
173
                  {
440
173
                    int
441
173
                      orientation;
442
443
173
                    orientation=MagickAtoI(attribute->value);
444
173
                    if ((orientation > UndefinedOrientation) &&
445
56
                        (orientation <= LeftBottomOrientation))
446
47
                      image->orientation=(OrientationType) orientation;
447
173
                  }
448
4.09k
              }
449
4.09k
            }
450
332
          else
451
332
            {
452
332
              if (NULL != content_type && strncmp(content_type,"application/rdf+xml",19) == 0)
453
99
                SetImageProfile(image,"XMP",profile,profile_size);
454
332
            }
455
          /*
456
            Only apply Exif orientation if ignore-transformations is true
457
            since HEIF native transformations will handle orientation otherwise
458
          */
459
4.42k
          if (strncmp(profile_name,"Exif",4) == 0)
460
4.10k
            {
461
4.10k
              if (ignore_transformations)
462
0
                {
463
0
                  const ImageAttribute *attribute = GetImageAttribute(image,"EXIF:Orientation");
464
0
                  if (attribute && attribute->value)
465
0
                    {
466
0
                      SetImageAttribute(image,"EXIF:Orientation","1");
467
0
                      image->orientation = UndefinedOrientation;
468
0
                    }
469
0
                }
470
4.10k
            }
471
4.42k
          MagickFreeResourceLimitedMemory(unsigned char *,profile);
472
4.42k
        }
473
5.78k
    }
474
4.03k
  MagickFreeResourceLimitedMemory(heif_item_id *,ids);
475
4.03k
  return MagickPass;
476
4.03k
}
477
478
479
/*
480
  Read Color Profile
481
*/
482
static MagickPassFail ReadColorProfile(struct heif_image_handle *heif_image_handle,
483
                                       Image *image)
484
45.6k
{
485
45.6k
  struct heif_error
486
45.6k
    err;
487
488
45.6k
  enum heif_color_profile_type
489
45.6k
    profile_type; /* 4 chars encoded into enum by 'heif_fourcc()' */
490
491
45.6k
  unsigned char
492
45.6k
    *profile;
493
494
45.6k
  profile_type = heif_image_handle_get_color_profile_type(heif_image_handle);
495
496
45.6k
  if (heif_color_profile_type_not_present == profile_type)
497
27.3k
    return MagickPass;
498
499
18.3k
  if (image->logging && (heif_color_profile_type_not_present !=profile_type))
500
18.3k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
501
18.3k
                          "Found color profile of type \"%c%c%c%c\"",
502
18.3k
                          ((char) ((unsigned int) profile_type >> 24) & 0xff),
503
18.3k
                          ((char) ((unsigned int) profile_type >> 16) & 0xff),
504
18.3k
                          ((char) ((unsigned int) profile_type >> 8) & 0xff),
505
18.3k
                          ((char) ((unsigned int) profile_type) & 0xff));
506
507
18.3k
  if (heif_color_profile_type_prof == profile_type)
508
5.13k
    {
509
5.13k
      size_t profile_size;
510
511
5.13k
      profile_size = heif_image_handle_get_raw_color_profile_size(heif_image_handle);
512
513
5.13k
      if (image->logging)
514
5.13k
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
515
5.13k
                              "Reading ICC profile with size %" MAGICK_SIZE_T_F "u bytes...",
516
5.13k
                              (MAGICK_SIZE_T) profile_size);
517
518
5.13k
      if (profile_size > 0)
519
5.13k
        {
520
          /* Allocate 'profile' buffer for profile */
521
5.13k
          profile=MagickAllocateResourceLimitedArray(unsigned char*,profile_size,
522
5.13k
                                                     sizeof(*profile));
523
524
5.13k
          if (profile == (unsigned char*) NULL)
525
5.13k
            ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed,(char *) NULL);
526
527
          /* Copy ICC profile to 'profile' buffer */
528
5.13k
          err = heif_image_handle_get_raw_color_profile(heif_image_handle,
529
5.13k
                                                        profile);
530
5.13k
          if (err.code != heif_error_Ok)
531
0
            {
532
0
              if (image->logging)
533
0
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
534
0
                                      "heif_image_handle_get_raw_color_profile() "
535
0
                                      "reports error %d, suberror %d, message \"%s\"",
536
0
                                      err.code, err.subcode, err.message);
537
0
              MagickFreeResourceLimitedMemory(unsigned char *,profile);
538
0
              ThrowBinaryException(CorruptImageError,
539
0
                                   AnErrorHasOccurredReadingFromFile,image->filename);
540
541
0
            }
542
5.13k
          SetImageProfile(image,"ICM",profile,profile_size);
543
5.13k
          MagickFreeResourceLimitedMemory(unsigned char *,profile);
544
5.13k
          if (image->logging)
545
5.13k
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
546
5.13k
                                  "  done reading ICC profile");
547
5.13k
        }
548
5.13k
    }
549
18.3k
  return MagickPass;
550
18.3k
}
551
552
/*
553
  This progress monitor implementation is tentative since it is not invoked
554
555
  According to libheif issue 161
556
  (https://github.com/strukturag/libheif/issues/161) progress monitor
557
  does not actually work since the decoders it depends on do not
558
  support it.
559
560
  Libheif issue 546 (https://github.com/strukturag/libheif/pull/546)
561
  suggests changing the return type of on_progress and start_progress
562
  to "bool" so that one can implement cancellation support.
563
 */
564
typedef struct ProgressUserData_
565
{
566
  ExceptionInfo *exception;
567
  Image *image;
568
  enum heif_progress_step step;
569
  unsigned long int progress;
570
  unsigned long int max_progress;
571
572
} ProgressUserData;
573
574
#if HEIF_ENABLE_PROGRESS_MONITOR
575
/* Called when progress monitor starts.  The 'max_progress' parameter indicates the maximum value of progress */
576
static void start_progress(enum heif_progress_step step, int max_progress, void* progress_user_data)
577
{
578
  ProgressUserData *context= (ProgressUserData *) progress_user_data;
579
  Image *image=context->image;
580
  context->step = step;
581
  context->progress = 0;
582
  context->max_progress = max_progress;
583
  if (context->image->logging)
584
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
585
                          "start_progress: step=%d, max_progress=%d",step, max_progress);
586
  MagickMonitorFormatted(context->progress,context->max_progress,&image->exception,
587
                         "[%s] Loading image: %lux%lu...  ",
588
                         image->filename,
589
                         image->columns,image->rows);
590
}
591
592
/* Called for each step of progress.  The 'progress' parameter represents the progress within the span of 'max_progress' */
593
static void on_progress(enum heif_progress_step step, int progress, void* progress_user_data)
594
{
595
  ProgressUserData *context = (ProgressUserData *) progress_user_data;
596
  Image *image=context->image;
597
  context->step = step;
598
  context->progress = progress;
599
  if (context->image->logging)
600
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
601
                          "on_progress: step=%d, progress=%d",step, progress);
602
  MagickMonitorFormatted(context->progress,context->max_progress,&image->exception,
603
                         "[%s] Loading image: %lux%lu...  ",
604
                         image->filename,
605
                         image->columns,image->rows);
606
}
607
608
/* Called when progress monitor stops */
609
static void end_progress(enum heif_progress_step step, void* progress_user_data)
610
{
611
  ProgressUserData *context = (ProgressUserData *) progress_user_data;
612
  context->step = step;
613
  if (context->image->logging)
614
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
615
                          "end_progress: step=%d",step);
616
}
617
618
#endif /* if HEIF_ENABLE_PROGRESS_MONITOR */
619
620
/*
621
  Apply security limits
622
 */
623
static MagickPassFail apply_security_limits(const ImageInfo *image_info,
624
                                            struct heif_context *heif_context)
625
97.2k
{
626
97.2k
#if LIBHEIF_NUMERIC_VERSION >= LIBHEIF_MAKE_VERSION(1,19,0)
627
97.2k
  struct heif_security_limits
628
97.2k
    *security_limits;
629
630
97.2k
  magick_uintmax_t
631
97.2k
    memory_limit,
632
97.2k
    width_limit,
633
97.2k
    height_limit,
634
97.2k
    pixels_limit;
635
636
97.2k
  security_limits=heif_context_get_security_limits(heif_context);
637
638
97.2k
  memory_limit = (magick_uintmax_t) GetMagickResourceLimit(MemoryResource);
639
97.2k
  security_limits->max_memory_block_size = (uint64_t) memory_limit;
640
97.2k
  security_limits->max_total_memory = (uint64_t) memory_limit;
641
97.2k
  width_limit = (magick_uintmax_t) GetMagickResourceLimit(WidthResource);
642
97.2k
  if (width_limit > INT_MAX)
643
0
    width_limit = INT_MAX;
644
97.2k
  height_limit = (magick_uintmax_t) GetMagickResourceLimit(HeightResource);
645
97.2k
  if (height_limit > INT_MAX)
646
0
    height_limit = INT_MAX;
647
97.2k
  pixels_limit = (magick_uintmax_t) GetMagickResourceLimit(PixelsResource);
648
97.2k
  if ((width_limit < pixels_limit) && (height_limit < pixels_limit) &&
649
97.2k
      (width_limit*height_limit < pixels_limit))
650
25.6k
    pixels_limit = width_limit*height_limit;
651
652
97.2k
  security_limits->max_image_size_pixels = (uint64_t) pixels_limit;
653
654
  /*
655
    Version 1 interface has:
656
657
    uint64_t max_image_size_pixels;
658
    uint64_t max_number_of_tiles;
659
    uint32_t max_bayer_pattern_pixels;
660
    uint32_t max_items;
661
662
    uint32_t max_color_profile_size;
663
    uint64_t max_memory_block_size;
664
665
    uint32_t max_components;
666
667
    uint32_t max_iloc_extents_per_item;
668
    uint32_t max_size_entity_group;
669
670
    uint32_t max_children_per_box; // for all boxes that are not covered by other limits
671
672
    and the version 2 interface adds:
673
674
    uint64_t max_total_memory;
675
    uint32_t max_sample_description_box_entries;
676
    uint32_t max_sample_group_description_box_entries;
677
678
  */
679
  /* version 1 interface */
680
97.2k
  MagickAttributeToU64(image_info,"heif","max-image-size-pixels",
681
97.2k
                       security_limits->max_image_size_pixels);
682
683
97.2k
  MagickAttributeToU64(image_info,"heif","max-number-of-tiles",
684
97.2k
                       security_limits->max_number_of_tiles);
685
686
97.2k
  MagickAttributeToU32(image_info,"heif","max-bayer-pattern-pixels",
687
97.2k
                       security_limits->max_bayer_pattern_pixels);
688
689
97.2k
  MagickAttributeToU32(image_info,"heif","max-items",
690
97.2k
                       security_limits->max_items);
691
692
97.2k
  MagickAttributeToU32(image_info,"heif","max-color-profile-size",
693
97.2k
                       security_limits->max_color_profile_size);
694
695
97.2k
  MagickAttributeToU64(image_info,"heif","max-memory-block-size",
696
97.2k
                       security_limits->max_memory_block_size);
697
698
97.2k
  MagickAttributeToU32(image_info,"heif","max-components",
699
97.2k
                       security_limits->max_components);
700
701
97.2k
  MagickAttributeToU32(image_info,"heif","max-iloc-extents-per-item",
702
97.2k
                       security_limits->max_iloc_extents_per_item);
703
704
97.2k
  MagickAttributeToU32(image_info,"heif","max-size-entity-group",
705
97.2k
                       security_limits->max_size_entity_group);
706
707
97.2k
  MagickAttributeToU32(image_info,"heif","max-children-per-box",
708
97.2k
                       security_limits->max_children_per_box);
709
710
  /* version 2 interface */
711
712
  /* Added in 1.13.0, September 2, 2022 */
713
97.2k
  MagickAttributeToU64(image_info,"heif","max-total-memory",
714
97.2k
                       security_limits->max_total_memory);
715
716
  /* Added in 1.13.0, September 2, 2022 */
717
97.2k
  MagickAttributeToU32(image_info,"heif","max-sample-description-box-entries",
718
97.2k
                       security_limits->max_sample_description_box_entries);
719
720
  /* Added in 1.17.6? ?*/
721
97.2k
  MagickAttributeToU32(image_info,"heif","max-sample-group-description-box-entries",
722
97.2k
                       security_limits->max_sample_group_description_box_entries);
723
724
97.2k
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
725
97.2k
                        "security limits:"
726
                        /* version 1 interface */
727
97.2k
                        "  max_image_size_pixels = %" MAGICK_UINTMAX_F "u"","
728
97.2k
                        "  max_number_of_tiles = %" MAGICK_UINTMAX_F "u"","
729
97.2k
                        "  max_bayer_pattern_pixels = %" MAGICK_UINTMAX_F "u"","
730
97.2k
                        "  max_items = %" MAGICK_UINTMAX_F "u"","
731
97.2k
                        "  max_color_profile_size = %" MAGICK_UINTMAX_F "u"","
732
97.2k
                        "  max_memory_block_size = %" MAGICK_UINTMAX_F "u"","
733
97.2k
                        "  max_components = %" MAGICK_UINTMAX_F "u"","
734
97.2k
                        "  max_iloc_extents_per_item = %" MAGICK_UINTMAX_F "u"","
735
97.2k
                        "  max_size_entity_group = %" MAGICK_UINTMAX_F "u"","
736
97.2k
                        "  max_children_per_box = %" MAGICK_UINTMAX_F "u"","
737
                        /* version 2 interface */
738
97.2k
                        "  max_total_memory = %" MAGICK_UINTMAX_F "u"","
739
97.2k
                        "  max_sample_description_box_entries = %" MAGICK_UINTMAX_F "u"","
740
97.2k
                        "  max_sample_group_description_box_entries = %" MAGICK_UINTMAX_F "u",
741
                        /* version 1 interface */
742
97.2k
                        (magick_uintmax_t) security_limits->max_image_size_pixels,
743
97.2k
                        (magick_uintmax_t) security_limits->max_number_of_tiles,
744
97.2k
                        (magick_uintmax_t) security_limits->max_bayer_pattern_pixels,
745
97.2k
                        (magick_uintmax_t) security_limits->max_items,
746
97.2k
                        (magick_uintmax_t) security_limits->max_color_profile_size,
747
97.2k
                        (magick_uintmax_t) security_limits->max_memory_block_size,
748
97.2k
                        (magick_uintmax_t) security_limits->max_components,
749
97.2k
                        (magick_uintmax_t) security_limits->max_iloc_extents_per_item,
750
97.2k
                        (magick_uintmax_t) security_limits->max_size_entity_group,
751
97.2k
                        (magick_uintmax_t) security_limits->max_children_per_box,
752
                        /* version 2 interface */
753
97.2k
                        (magick_uintmax_t) security_limits->max_total_memory,
754
97.2k
                        (magick_uintmax_t) security_limits->max_sample_description_box_entries,
755
97.2k
                        (magick_uintmax_t) security_limits->max_sample_group_description_box_entries
756
97.2k
                        );
757
#else
758
  {
759
    /* DEPRECATED: Add an image size limit based on maximum_width ^ 2 */
760
    magick_int64_t width_limit = GetMagickResourceLimit(WidthResource);
761
    if (MagickResourceInfinity != width_limit)
762
      {
763
        if (width_limit > INT_MAX)
764
          width_limit =  INT_MAX;
765
        /* Added in libheif 1.4.0 */
766
        heif_context_set_maximum_image_size_limit(heif, (int) width_limit);
767
      }
768
  }
769
770
#endif /* if LIBHEIF_NUMERIC_VERSION >= LIBHEIF_MAKE_VERSION(1,19,0) */
771
772
97.2k
  return MagickPass;
773
97.2k
}
774
775
/*
776
  libheif/color-conversion/colorconversion.cc provides this to output to
777
  an ostream, but our code is not C++!
778
779
  std::ostream& operator<<(std::ostream& ostr, heif_colorspace c)
780
*/
781
static const char *HEIF_colorspace_to_string(const enum heif_colorspace c)
782
107k
{
783
107k
  const char *s = "unknown";
784
785
107k
  switch (c)
786
107k
    {
787
34
    case heif_colorspace_undefined:
788
34
      s = "undefined";
789
34
      break;
790
35.8k
    case heif_colorspace_YCbCr:
791
35.8k
      s = "YCbCr";
792
35.8k
      break;
793
63.7k
    case heif_colorspace_RGB:
794
63.7k
      s = "RGB";
795
63.7k
      break;
796
7.40k
    case heif_colorspace_monochrome:
797
7.40k
      s = "monochrome";
798
7.40k
      break;
799
0
    case heif_colorspace_nonvisual:
800
0
      s = "nonvisual";
801
0
      break;
802
0
#if LIBHEIF_HAVE_VERSION(1,21,3)
803
      /* Git commit cb6e524 added heif_colorspace_filter_array on 2/24/26 */
804
5
    case heif_colorspace_filter_array:
805
5
      s = "filter_array";
806
5
      break;
807
107k
#endif /* if LIBHEIF_HAVE_VERSION(1,21,3) */
808
107k
    }
809
810
107k
  return s;
811
107k
}
812
813
static ColorspaceType HEIF_colorspace_to_ColorspaceType(const enum heif_colorspace c)
814
15.7k
{
815
15.7k
  ColorspaceType t = UndefinedColorspace;
816
817
15.7k
  switch (c)
818
15.7k
    {
819
0
    case heif_colorspace_undefined:
820
0
      t = UndefinedColorspace;
821
0
      break;
822
0
    case heif_colorspace_YCbCr:
823
0
      t = YUVColorspace;
824
0
      break;
825
15.3k
    case heif_colorspace_RGB:
826
15.3k
      t = RGBColorspace;
827
15.3k
      break;
828
406
    case heif_colorspace_monochrome:
829
406
      t = GRAYColorspace;
830
406
      break;
831
0
    case heif_colorspace_nonvisual:
832
0
      t = UndefinedColorspace;
833
0
      break;
834
0
#if LIBHEIF_HAVE_VERSION(1,21,3)
835
1
    case heif_colorspace_filter_array:
836
1
      t = UndefinedColorspace;
837
1
      break;
838
15.7k
#endif /* if LIBHEIF_HAVE_VERSION(1,21,2) */
839
15.7k
    }
840
841
15.7k
  return t;
842
15.7k
}
843
844
static const char *HEIF_chroma_to_string(const enum heif_chroma c)
845
105k
{
846
105k
  const char *s = "unknown";
847
848
105k
  switch (c)
849
105k
    {
850
1.41k
    case heif_chroma_undefined:
851
1.41k
      s = "undefined";
852
1.41k
      break;
853
7.38k
    case heif_chroma_monochrome:
854
7.38k
      s = "monochrome";
855
7.38k
      break;
856
15.8k
    case heif_chroma_420:
857
15.8k
      s = "420";
858
15.8k
      break;
859
310
    case heif_chroma_422:
860
310
      s = "422";
861
310
      break;
862
81.0k
    case heif_chroma_444:
863
81.0k
      s = "444";
864
81.0k
      break;
865
0
    case heif_chroma_interleaved_RGB:
866
0
      s = "interleaved_RGB";
867
0
      break;
868
0
    case heif_chroma_interleaved_RGBA:
869
0
      s = "interleaved_RGBA";
870
0
      break;
871
0
    case heif_chroma_interleaved_RRGGBB_BE:
872
0
      s = "interleaved_RRGGBB_BE";
873
0
      break;
874
0
    case heif_chroma_interleaved_RRGGBBAA_BE:
875
0
      s = "interleaved_RRGGBBAA_BE";
876
0
      break;
877
0
    case heif_chroma_interleaved_RRGGBB_LE:
878
0
      s = "interleaved_RRGGBB_LE";
879
0
      break;
880
0
    case heif_chroma_interleaved_RRGGBBAA_LE:
881
0
      s = "interleaved_RRGGBBAA_LE";
882
0
      break;
883
105k
    }
884
885
105k
  return s;
886
105k
}
887
888
static const char *HEIF_channel_to_string(const enum heif_channel c)
889
43.4k
{
890
43.4k
  const char *s = "unknown";
891
892
43.4k
  switch (c)
893
43.4k
    {
894
756
    case heif_channel_Y:
895
756
      s = "Y";
896
756
      break;
897
0
    case heif_channel_Cb:
898
0
      s = "Cb";
899
0
      break;
900
0
    case heif_channel_Cr:
901
0
      s = "Cr";
902
0
      break;
903
14.2k
    case heif_channel_R:
904
14.2k
      s = "R";
905
14.2k
      break;
906
14.2k
    case heif_channel_G:
907
14.2k
      s = "G";
908
14.2k
      break;
909
14.2k
    case heif_channel_B:
910
14.2k
      s = "B";
911
14.2k
      break;
912
40
    case heif_channel_Alpha:
913
40
      s = "Alpha";
914
40
      break;
915
0
    case heif_channel_interleaved:
916
0
      s = "interleaved";
917
0
      break;
918
0
#if LIBHEIF_HAVE_VERSION(1,21,3)
919
0
    case heif_channel_filter_array:
920
      /* Git commit cb6e524 added heif_channel_filter_array on 2/24/26 */
921
0
      s = "filter_array";
922
0
      break;
923
0
#endif /* if LIBHEIF_HAVE_VERSION(1,21,3) */
924
0
    case heif_channel_depth:
925
0
      s = "channel_depth";
926
0
      break;
927
0
    case heif_channel_disparity:
928
0
      s = "disparity";
929
0
      break;
930
0
#if LIBHEIF_HAVE_VERSION(1,21,3)
931
      /* Added by changeset b4326c42 on 2/12/26 after v1.21.2 */
932
0
    case heif_channel_unknown:
933
0
      s = "unknown";
934
0
      break;
935
0
#endif /* if LIBHEIF_HAVE_VERSION(1,21,3) */
936
0
    default:
937
0
      s = "unknown";
938
0
      break;
939
43.4k
    }
940
941
43.4k
  return s;
942
43.4k
}
943
944
static MagickPassFail ReadHEIFImageFrame(heif_image_handle* heif_image_handle,
945
                                         struct heif_decoding_options *decode_options,
946
                                         Image *image,
947
                                         const ImageInfo *image_info,
948
                                         ExceptionInfo *exception)
949
46.1k
{
950
46.1k
#define HEIFReadImageFrameCleanup()             \
951
46.1k
  do                                            \
952
46.1k
    {                                           \
953
46.1k
      if (heif_image)                           \
954
46.1k
        heif_image_release(heif_image);         \
955
46.1k
    } while (0);
956
957
46.1k
#define ThrowHEIFThrowReadImageFrameException(code_,reason_,image_)     \
958
46.1k
  do                                                                    \
959
30.3k
    {                                                                   \
960
30.3k
      HEIFReadImageFrameCleanup();                                      \
961
30.3k
      if (code_ > exception->severity)                                  \
962
30.3k
        {                                                               \
963
30.3k
          ThrowException(&(image_)->exception,code_,reason_,(image_)->filename); \
964
30.3k
          CopyException(exception,&(image_)->exception);                \
965
30.3k
        }                                                               \
966
30.3k
      return(MagickFail);                                               \
967
30.3k
    } while (0);
968
969
46.1k
  struct heif_image
970
46.1k
    *heif_image = NULL;
971
972
46.1k
  PixelPacket
973
46.1k
    *q;
974
975
46.1k
  long
976
46.1k
    x,
977
46.1k
    y;
978
979
46.1k
  int
980
46.1k
    bits_per_pixel;
981
982
46.1k
  struct heif_error
983
46.1k
    heif_status;
984
985
#if HEIF_ENABLE_PROGRESS_MONITOR
986
  ProgressUserData
987
    progress_user_data;
988
#endif
989
990
46.1k
  enum heif_colorspace
991
46.1k
    heif_colorspace,
992
46.1k
    preferred_colorspace = heif_colorspace_undefined;
993
994
46.1k
  enum heif_chroma
995
46.1k
    preferred_chroma = heif_chroma_undefined;
996
997
  /*
998
    Note: Those values are preliminary but likely the upper bound
999
    The real image values might be rotated or cropped due to transformations
1000
1001
    heif_image_handle APIs are declared in heif_image_handle.h
1002
  */
1003
46.1k
  image->depth=8;
1004
46.1k
  bits_per_pixel=heif_image_handle_get_luma_bits_per_pixel(heif_image_handle);
1005
  /*
1006
    This function only works for YCbCr-based images.  We only use it
1007
    in ping mode since otherwise it screws up the depth in the Q8
1008
    build (due to scaling in libheif).
1009
  */
1010
46.1k
  if (image_info->ping)
1011
0
    {
1012
0
      if (image->logging)
1013
0
        if (bits_per_pixel < 0)
1014
0
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1015
0
                                "Image has undefined bit-depth!");
1016
0
      if (bits_per_pixel > 0)
1017
0
        image->depth=(unsigned int) bits_per_pixel;
1018
0
      if (image->logging)
1019
0
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1020
0
                              "Depth: %u", image->depth);
1021
0
    }
1022
1023
46.1k
  image->columns=heif_image_handle_get_width(heif_image_handle);
1024
46.1k
  image->rows=heif_image_handle_get_height(heif_image_handle);
1025
46.1k
  if (image->logging)
1026
46.1k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1027
46.1k
                          "Geometry: %lux%lu", image->columns, image->rows);
1028
46.1k
  image->matte=MagickFalse;
1029
46.1k
  if (heif_image_handle_has_alpha_channel(heif_image_handle))
1030
335
    image->matte=MagickTrue;
1031
46.1k
  if (image->logging)
1032
46.1k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1033
46.1k
                          "Matte: %s", image->matte ? "True" : "False");
1034
46.1k
  heif_status=heif_image_handle_get_preferred_decoding_colorspace(heif_image_handle,
1035
46.1k
                                                                  &preferred_colorspace,
1036
46.1k
                                                                  &preferred_chroma);
1037
46.1k
  if (heif_status.code != heif_error_Ok)
1038
440
    {
1039
440
      if (image->logging)
1040
440
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1041
440
                              "heif_image_handle_get_preferred_decoding_colorspace() "
1042
440
                              "reports error %d, suberror %d, message \"%s\"",
1043
440
                              heif_status.code, heif_status.subcode, heif_status.message);
1044
440
      ThrowHEIFThrowReadImageFrameException(CorruptImageError, AnErrorHasOccurredReadingFromFile, image);
1045
0
    }
1046
45.6k
  if (image->logging)
1047
45.6k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1048
45.6k
                          "Preferred colorspace: %s, "
1049
45.6k
                          "Preferred chroma: %s",
1050
45.6k
                          HEIF_colorspace_to_string(preferred_colorspace),
1051
45.6k
                          HEIF_chroma_to_string(preferred_chroma));
1052
1053
  /* Read EXIF and XMP profile */
1054
45.6k
  if (!ReadMetadata(heif_image_handle, image, decode_options->ignore_transformations))
1055
0
    {
1056
0
      CopyException(exception,&image->exception);
1057
0
      HEIFReadImageFrameCleanup();
1058
0
      return MagickFail;
1059
0
    }
1060
1061
  /* Read ICC profile */
1062
45.6k
  if (!ReadColorProfile(heif_image_handle, image))
1063
0
    {
1064
0
      CopyException(exception,&image->exception);
1065
0
      HEIFReadImageFrameCleanup();
1066
0
      return MagickFail;
1067
0
    }
1068
1069
  /*
1070
    When applying transformations the whole image has to be read to
1071
    get the real dimensions. We no longer read the whole image because
1072
    it makes 'identify' extremely slow for large files.
1073
1074
    The user should specify:
1075
1076
    -define heif:ignore-transformations=false
1077
1078
    in order to force reading much of the image to get fully accurate
1079
    information.
1080
  */
1081
45.6k
  if (image_info->ping && decode_options->ignore_transformations)
1082
0
    {
1083
0
      HEIFReadImageFrameCleanup();
1084
0
      if (image->logging)
1085
0
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1086
0
                              "Returning early without pixels due to ping mode...");
1087
0
      return MagickPass;
1088
0
    }
1089
1090
1091
  /*
1092
    Be prepared to handle images on per-plane basis. Upscale
1093
    subsampled chroma if necessary.
1094
  */
1095
45.6k
  if (image->logging)
1096
45.6k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1097
45.6k
                          "Decoding image...");
1098
1099
45.6k
  {
1100
45.6k
    enum heif_chroma heif_chroma_decode=preferred_chroma;
1101
45.6k
    enum heif_colorspace heif_colorspace_decode=preferred_colorspace;
1102
1103
    /* Influence whether to return original YBbCr or RGB */
1104
45.6k
    if (heif_colorspace_decode == heif_colorspace_YCbCr)
1105
35.8k
      {
1106
35.8k
        const char *string_value;
1107
35.8k
        MagickBool preserve_colorspace = MagickFalse;
1108
        /*
1109
          Use heif:preserve-colorspace=true or
1110
          heif:preserve-colorspace=1 to return the image in the
1111
          original colorspace.  This prevents the conversion of a
1112
          YCbCr image to RGB.  Note that libheif may automatically
1113
          apply a color profile when converting a YCbCr image to RGB,
1114
          and requesting original YCbCr defeats that.
1115
        */
1116
35.8k
        if ((string_value = AccessDefinition(image_info,"heif","preserve-colorspace")))
1117
0
          {
1118
0
            if ((LocaleCompare(string_value,"true") == 0) ||
1119
0
                (LocaleCompare(string_value,"yes") == 0) ||
1120
0
                (LocaleCompare(string_value,"1") == 0))
1121
0
              preserve_colorspace = MagickTrue;
1122
0
          }
1123
35.8k
        if (preserve_colorspace == MagickFalse)
1124
35.8k
          heif_colorspace_decode=heif_colorspace_RGB;
1125
1126
1127
        /* Upscale chroma if necessary */
1128
35.8k
        if ((heif_chroma_decode == heif_chroma_420) || (heif_chroma_decode == heif_chroma_422))
1129
16.1k
          {
1130
16.1k
            heif_chroma_decode=heif_chroma_444;
1131
16.1k
          }
1132
35.8k
      }
1133
45.6k
    if (heif_colorspace_decode == heif_colorspace_RGB)
1134
42.1k
      {
1135
42.1k
        const char *string_value;
1136
42.1k
        MagickBool interleaved_decode = MagickFalse;
1137
1138
42.1k
        if ((string_value = AccessDefinition(image_info,"heif","interleaved-rgb-decode")))
1139
0
          {
1140
0
            if ((LocaleCompare(string_value,"true") == 0) ||
1141
0
                (LocaleCompare(string_value,"yes") == 0) ||
1142
0
                (LocaleCompare(string_value,"1") == 0))
1143
0
              interleaved_decode = MagickTrue;
1144
0
          }
1145
1146
42.1k
        if (interleaved_decode)
1147
0
          {
1148
            /* Use interleaved mode */
1149
0
            if (image->matte)
1150
0
              {
1151
0
                if (bits_per_pixel > 8)
1152
0
                  {
1153
#if defined(WORDS_BIGENDIAN)
1154
                    heif_chroma_decode=heif_chroma_interleaved_RRGGBBAA_BE;
1155
#else
1156
0
                    heif_chroma_decode=heif_chroma_interleaved_RRGGBBAA_LE;
1157
0
#endif /* if defined(WORDS_BIGENDIAN) */
1158
0
                  }
1159
0
                else
1160
0
                  {
1161
0
                    heif_chroma_decode=heif_chroma_interleaved_RGBA;
1162
0
                  }
1163
0
              }
1164
0
            else
1165
0
              {
1166
0
                if (bits_per_pixel > 8)
1167
0
                  {
1168
#if defined(WORDS_BIGENDIAN)
1169
                    heif_chroma_decode=heif_chroma_interleaved_RRGGBB_BE;
1170
#else
1171
0
                    heif_chroma_decode=heif_chroma_interleaved_RRGGBB_LE;
1172
0
#endif/* if defined(WORDS_BIGENDIAN) */
1173
0
                  }
1174
0
                else
1175
0
                  {
1176
0
                    heif_chroma_decode=heif_chroma_interleaved_RGB;
1177
0
                  }
1178
0
              }
1179
0
          }
1180
42.1k
        else
1181
42.1k
          {
1182
            /* Use planar mode */
1183
42.1k
            heif_chroma_decode=heif_chroma_444;
1184
42.1k
          }
1185
42.1k
      }
1186
1187
45.6k
    if (image->logging && IsEventLogged(CoderEvent))
1188
0
      {
1189
0
#if 1
1190
        /* See libheif/api/libheif/heif_tiling.h */
1191
0
        heif_image_tiling tiling;
1192
0
        (void) memset(&tiling,0,sizeof(tiling));
1193
0
        heif_status=heif_image_handle_get_image_tiling(heif_image_handle, decode_options->ignore_transformations, &tiling);
1194
0
        if (heif_status.code != heif_error_Ok)
1195
0
          {
1196
0
            if (image->logging)
1197
0
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1198
0
                                    "heif_image_handle_get_image_tiling() "
1199
0
                                    "reports error %d, suberror %d, message \"%s\"",
1200
0
                                    heif_status.code, heif_status.subcode, heif_status.message);
1201
0
            ThrowHEIFThrowReadImageFrameException(CorruptImageError, AnErrorHasOccurredReadingFromFile, image);
1202
0
          }
1203
0
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1204
0
                              "Tiling: version %d, num_columns=%u, num_rows=%u,"
1205
0
                              " tile_width=%u, tile_height=%u, image_width=%u, image_height=%u,"
1206
0
                              " top_offset=%u, left_offset=%u, number_of_extra_dimensions=%u",
1207
0
                              tiling.version, tiling.num_columns, tiling.num_rows,
1208
0
                              tiling.tile_width, tiling.tile_height, tiling.image_width, tiling.image_height,
1209
0
                              tiling.top_offset, tiling.left_offset,
1210
0
                              tiling.number_of_extra_dimensions);
1211
0
#endif
1212
#if 0
1213
        /* From heif_dec.cc decode_image_tiles() */
1214
        for (uint32_t ty = 0; ty < tiling.num_rows; ty++)
1215
          for (uint32_t tx = 0; tx < tiling.num_columns; tx++)
1216
            {
1217
              /*
1218
                heif_image* image;
1219
                heif_error err;
1220
                err = heif_image_handle_decode_image_tile(handle,
1221
                &image,
1222
                encoder->colorspace(has_alpha),
1223
                encoder->chroma(has_alpha, bit_depth),
1224
                decode_options, tx, ty);
1225
                heif_error heif_image_handle_decode_image_tile(const heif_image_handle* in_handle,
1226
                heif_image** out_img,
1227
                enum heif_colorspace colorspace,
1228
                enum heif_chroma chroma,
1229
                const heif_decoding_options* options,
1230
                uint32_t tile_x, uint32_t tile_y);
1231
                heif_image_release(image);
1232
              */
1233
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1234
                                    "Tile %u,%u: (x1=%u y1=%u x2=%u y2=%u)",
1235
                                    tx, ty, tx*tiling.tile_width, ty*tiling.tile_height,
1236
                                    ((tx+1)*tiling.tile_width)-1, ((ty+1)*tiling.tile_height)-1);
1237
            }
1238
#endif
1239
0
      }
1240
1241
45.6k
    if (image->logging)
1242
45.6k
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1243
45.6k
                            "heif_decode_image: colorspace_decode=%s, chroma_decode=%s",
1244
45.6k
                            HEIF_colorspace_to_string(heif_colorspace_decode),
1245
45.6k
                            HEIF_chroma_to_string(heif_chroma_decode));
1246
45.6k
    heif_status=heif_decode_image(heif_image_handle,
1247
45.6k
                                  &heif_image,
1248
45.6k
                                  heif_colorspace_decode, /* Chose preferred colorspace */
1249
45.6k
                                  heif_chroma_decode,
1250
45.6k
                                  decode_options);
1251
45.6k
    if (heif_status.code != heif_error_Ok)
1252
29.9k
      {
1253
29.9k
        if (image->logging)
1254
29.9k
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1255
29.9k
                                "heif_decode_image() "
1256
29.9k
                                "reports error %d, suberror %d, message \"%s\"",
1257
29.9k
                                heif_status.code, heif_status.subcode, heif_status.message);
1258
29.9k
      }
1259
45.6k
  }
1260
45.6k
  if (heif_status.code == heif_error_Memory_allocation_error)
1261
45.1k
    ThrowHEIFThrowReadImageFrameException(ResourceLimitError,MemoryAllocationFailed,image);
1262
45.1k
  if (heif_status.code != heif_error_Ok)
1263
29.3k
    {
1264
29.3k
      ThrowHEIFThrowReadImageFrameException(CorruptImageError, AnErrorHasOccurredReadingFromFile, image);
1265
0
    }
1266
15.7k
  if (heif_image == (struct heif_image *) NULL)
1267
0
    {
1268
0
      if (image->logging)
1269
0
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1270
0
                              "heif_decode_image() reports heif_error_Ok but failed to return an image!");
1271
0
      ThrowHEIFThrowReadImageFrameException(CorruptImageError, AnErrorHasOccurredReadingFromFile, image);
1272
0
    }
1273
1274
  /*
1275
    Trace decoding warnings
1276
  */
1277
15.7k
  if (image->logging && IsEventLogged(CoderEvent))
1278
0
    {
1279
0
      int i;
1280
1281
0
      for (i = 0;; i++)
1282
0
        {
1283
0
          int n;
1284
0
          n = heif_image_get_decoding_warnings(heif_image,i,&heif_status, 1);
1285
0
          if (n == 0)
1286
0
            break;
1287
0
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1288
0
                                "Warning: %s", heif_status.message);
1289
0
        }
1290
0
    }
1291
1292
  /*
1293
    Update image properties
1294
  */
1295
15.7k
  image->columns=heif_image_get_primary_width(heif_image);
1296
15.7k
  image->rows=heif_image_get_primary_height(heif_image);
1297
15.7k
  if (image->logging)
1298
15.7k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1299
15.7k
                          "Geometry: %lux%lu",
1300
15.7k
                          image->columns,image->rows);
1301
15.7k
  heif_colorspace=heif_image_get_colorspace(heif_image);
1302
15.7k
  image->colorspace=HEIF_colorspace_to_ColorspaceType(heif_colorspace);
1303
15.7k
  if (image->logging)
1304
15.7k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1305
15.7k
                          "HEIF colorspace: %s",
1306
15.7k
                          HEIF_colorspace_to_string(heif_colorspace));
1307
1308
15.7k
  if (image_info->ping)
1309
0
    {
1310
0
      HEIFReadImageFrameCleanup();
1311
0
      if (image->logging)
1312
0
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1313
0
                              "Returning without pixels due to ping mode...");
1314
0
      return MagickPass;
1315
0
    }
1316
1317
15.7k
  if (CheckImagePixelLimits(image, exception) != MagickPass)
1318
1.11k
    {
1319
1.11k
      HEIFReadImageFrameCleanup();
1320
1.11k
      return MagickFail;
1321
1.11k
    }
1322
1323
14.6k
  {
1324
1325
14.6k
    typedef union
1326
14.6k
    {
1327
14.6k
      const uint8_t *u8;
1328
14.6k
      const uint16_t *u16;
1329
14.6k
    } magick_plane_t;
1330
1331
14.6k
    const char *
1332
14.6k
      heif_channel_str;
1333
1334
14.6k
    const char *
1335
14.6k
      heif_chroma_format_str;
1336
1337
14.6k
    enum heif_chroma
1338
14.6k
      heif_chroma_format;
1339
1340
14.6k
    heif_chroma_format = heif_image_get_chroma_format(heif_image);
1341
14.6k
    heif_chroma_format_str = HEIF_chroma_to_string(heif_chroma_format);
1342
1343
14.6k
    switch (heif_image_get_colorspace(heif_image))
1344
14.6k
      {
1345
0
      case heif_colorspace_YCbCr:
1346
0
        {
1347
          /*
1348
            Decode YCbCr(A) pixels
1349
          */
1350
1351
0
          static const enum heif_channel channels_YCbCrA[4] =
1352
0
            { heif_channel_Y, heif_channel_Cb, heif_channel_Cr, heif_channel_Alpha };
1353
1354
0
          magick_plane_t
1355
0
            planes[4];
1356
1357
0
          magick_plane_t
1358
0
            line[4];
1359
1360
0
          size_t
1361
0
            row_stride[4];
1362
1363
0
          int
1364
0
            channel_bits_per_pixel[4],
1365
0
            channel_bits_per_pixel_range[4];
1366
1367
0
          unsigned long
1368
0
            channel_max_value_given_bits[4];
1369
1370
0
          unsigned int
1371
0
            channel_index,
1372
0
            num_channels;
1373
1374
0
          enum heif_channel heif_channel;
1375
1376
0
          num_channels = image->matte ? 4 : 3;
1377
1378
0
          if (image->logging)
1379
0
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1380
0
                                  "Decoding %u planes for YCbCr colorspace (chroma %s)",
1381
0
                                  num_channels, heif_chroma_format_str);
1382
1383
0
          for (channel_index = 0; channel_index < num_channels; channel_index++)
1384
0
            {
1385
0
              heif_channel = channels_YCbCrA[channel_index];
1386
0
              heif_channel_str = HEIF_channel_to_string(heif_channel);
1387
0
              planes[channel_index].u8 = heif_image_get_plane_readonly2(heif_image,
1388
0
                                                                        heif_channel,
1389
0
                                                                        &row_stride[channel_index]);
1390
0
              if (planes[channel_index].u8 == (const uint8_t*) NULL)
1391
0
                {
1392
0
                  if (image->logging)
1393
0
                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1394
0
                                          "heif_image_get_plane_readonly2() returned NULL"
1395
0
                                          " for channel index %u (channel %s)!",
1396
0
                                          channel_index,heif_channel_str);
1397
0
                  ThrowHEIFThrowReadImageFrameException(CorruptImageError,
1398
0
                                                        AnErrorHasOccurredReadingFromFile, image);
1399
0
                }
1400
0
              channel_bits_per_pixel[channel_index] = heif_image_get_bits_per_pixel(heif_image,
1401
0
                                                                                    heif_channel);
1402
0
              channel_bits_per_pixel_range[channel_index] = heif_image_get_bits_per_pixel_range(heif_image,
1403
0
                                                                                                heif_channel);
1404
0
              channel_max_value_given_bits[channel_index] = MaxValueGivenBits(channel_bits_per_pixel_range[channel_index]);
1405
0
              if ((int) image->depth < channel_bits_per_pixel_range[channel_index])
1406
0
                image->depth=(unsigned int) channel_bits_per_pixel_range[channel_index];
1407
0
              if (image->logging)
1408
0
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1409
0
                                      "  channel %s row stride %zu (%d bits in %d bit quantum)",
1410
0
                                      heif_channel_str, row_stride[channel_index],
1411
0
                                      channel_bits_per_pixel_range[channel_index],
1412
0
                                      channel_bits_per_pixel[channel_index]);
1413
0
            }
1414
1415
          /* Transfer pixels to image, using row stride to find start of each row. */
1416
0
          if (image->logging)
1417
0
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1418
0
                                  "Transferring pixels to magick image...");
1419
0
          for (y=0; y < (long)image->rows; y++)
1420
0
            {
1421
0
              q=SetImagePixelsEx(image,0,y,image->columns,1,exception);
1422
0
              if (q == (PixelPacket *) NULL)
1423
0
                {
1424
0
                  HEIFReadImageFrameCleanup();
1425
0
                  return MagickFail;
1426
0
                }
1427
0
              for (channel_index = 0; channel_index < num_channels; channel_index++)
1428
0
                line[channel_index].u8=planes[channel_index].u8+y*row_stride[channel_index];
1429
0
              for (x=0; x < (long)image->columns; x++)
1430
0
                {
1431
0
                  for (channel_index = 0; channel_index < num_channels; channel_index++)
1432
0
                    {
1433
0
                      uint32_t
1434
0
                        sample = 0;
1435
1436
0
                      Quantum
1437
0
                        quantum=0;
1438
1439
                      /*
1440
                        Get sample
1441
                      */
1442
0
                      switch(channel_bits_per_pixel[channel_index])
1443
0
                        {
1444
0
                        case 8:
1445
0
                          sample = *line[channel_index].u8;
1446
0
                          line[channel_index].u8++;
1447
0
                          break;
1448
0
                        case 16:
1449
0
                          sample = *line[channel_index].u16;
1450
0
                          line[channel_index].u16++;
1451
0
                          break;
1452
0
                        }
1453
1454
                      /*
1455
                        Scale sample to quantum
1456
                      */
1457
0
                      if (channel_bits_per_pixel_range[channel_index] == 8)
1458
0
                        {
1459
0
                          quantum=ScaleCharToQuantum(sample);
1460
0
                        }
1461
0
                      else if (channel_bits_per_pixel_range[channel_index] < 8)
1462
0
                        {
1463
0
                          quantum=ScaleAnyToQuantum(sample,channel_max_value_given_bits[channel_index]);
1464
0
                        }
1465
0
                      else if (channel_bits_per_pixel_range[channel_index] == 16)
1466
0
                        {
1467
0
                          quantum=ScaleShortToQuantum(sample);
1468
0
                        }
1469
0
                      else if (channel_bits_per_pixel_range[channel_index] < 16)
1470
0
                        {
1471
0
                          quantum=ScaleAnyToQuantum(sample,channel_max_value_given_bits[channel_index]);
1472
0
                        }
1473
1474
                      /*
1475
                        Assign quantum to PixelPacket
1476
                      */
1477
0
                      switch (channel_index)
1478
0
                        {
1479
0
                        case 0:
1480
0
                          SetYSample(q,quantum);
1481
0
                          if (num_channels != 4)
1482
0
                            SetOpacitySample(q,OpaqueOpacity);
1483
0
                          break;
1484
0
                        case 1:
1485
0
                          SetCbSample(q,quantum);
1486
0
                          break;
1487
0
                        case 2:
1488
0
                          SetCrSample(q,quantum);
1489
0
                          break;
1490
0
                        case 3:
1491
0
                          SetOpacitySample(q,MaxRGB-quantum);
1492
0
                          break;
1493
0
                        }
1494
0
                    }
1495
0
                  q++;
1496
0
                }
1497
0
              if (!SyncImagePixelsEx(image,exception))
1498
0
                {
1499
0
                  HEIFReadImageFrameCleanup();
1500
0
                  return MagickFail;
1501
0
                }
1502
0
            }
1503
0
          if (image->logging)
1504
0
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1505
0
                                  "  done!");
1506
0
        }
1507
0
        break;
1508
14.2k
      case heif_colorspace_RGB:
1509
14.2k
        {
1510
14.2k
          if (heif_chroma_444 == heif_chroma_format)
1511
14.2k
            {
1512
              /*
1513
                Decode RGB(A) planar pixels
1514
              */
1515
14.2k
              static const enum heif_channel
1516
14.2k
                channels_RGBA[4] =
1517
14.2k
                { heif_channel_R, heif_channel_G, heif_channel_B, heif_channel_Alpha };
1518
1519
14.2k
              magick_plane_t
1520
14.2k
                planes[4];
1521
1522
14.2k
              magick_plane_t
1523
14.2k
                line[4];
1524
1525
14.2k
              size_t
1526
14.2k
                row_stride[4];
1527
1528
14.2k
              int
1529
14.2k
                channel_bits_per_pixel[4],
1530
14.2k
                channel_bits_per_pixel_range[4];
1531
1532
14.2k
              unsigned long
1533
14.2k
                channel_max_value_given_bits[4];
1534
1535
14.2k
              unsigned int
1536
14.2k
                channel_index,
1537
14.2k
                num_channels=0;
1538
1539
14.2k
              enum heif_channel
1540
14.2k
                heif_channel;
1541
1542
14.2k
              num_channels = image->matte ? 4 : 3;
1543
1544
14.2k
              if (image->logging)
1545
14.2k
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1546
14.2k
                                      "Decoding %u planes for RGB colorspace (chroma %s)",
1547
14.2k
                                      num_channels, heif_chroma_format_str);
1548
1549
56.9k
              for (channel_index = 0; channel_index < num_channels; channel_index++)
1550
42.7k
                {
1551
42.7k
                  heif_channel = channels_RGBA[channel_index];
1552
42.7k
                  heif_channel_str = HEIF_channel_to_string(heif_channel);
1553
42.7k
                  planes[channel_index].u8 = heif_image_get_plane_readonly2(heif_image,
1554
42.7k
                                                                            heif_channel,
1555
42.7k
                                                                            &row_stride[channel_index]);
1556
42.7k
                  if (planes[channel_index].u8 == (const uint8_t*) NULL)
1557
0
                    {
1558
0
                      if (image->logging)
1559
0
                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1560
0
                                              "heif_image_get_plane_readonly2() returned NULL"
1561
0
                                              " for channel index %u (channel %s)!",
1562
0
                                              channel_index,heif_channel_str);
1563
0
                      ThrowHEIFThrowReadImageFrameException(CorruptImageError,
1564
0
                                                            AnErrorHasOccurredReadingFromFile, image);
1565
0
                    }
1566
42.7k
                  channel_bits_per_pixel[channel_index] = heif_image_get_bits_per_pixel(heif_image,
1567
42.7k
                                                                                        heif_channel);
1568
42.7k
                  channel_bits_per_pixel_range[channel_index] = heif_image_get_bits_per_pixel_range(heif_image,
1569
42.7k
                                                                                                    heif_channel);
1570
42.7k
                  channel_max_value_given_bits[channel_index] = MaxValueGivenBits(channel_bits_per_pixel_range[channel_index]);
1571
42.7k
                  if (0 == channel_max_value_given_bits[channel_index])
1572
0
                    {
1573
0
                      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1574
0
                                            "MaxValueGivenBits(%d) returned 0!",
1575
0
                                            channel_bits_per_pixel_range[channel_index]);
1576
0
                      ThrowHEIFThrowReadImageFrameException(CorruptImageError,
1577
0
                                                            AnErrorHasOccurredReadingFromFile, image);
1578
0
                    }
1579
42.7k
                  if ((int) image->depth < channel_bits_per_pixel_range[channel_index])
1580
5.42k
                    image->depth=(unsigned int) channel_bits_per_pixel_range[channel_index];
1581
42.7k
                  if (image->logging)
1582
42.7k
                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1583
42.7k
                                          "  channel %s row stride %zu (%d bits in %d bit quantum)",
1584
42.7k
                                          heif_channel_str, row_stride[channel_index],
1585
42.7k
                                          channel_bits_per_pixel_range[channel_index],
1586
42.7k
                                          channel_bits_per_pixel[channel_index]);
1587
42.7k
                }
1588
1589
              /* Transfer pixels to image, using row stride to find start of each row. */
1590
14.2k
              if (image->logging)
1591
14.2k
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1592
14.2k
                                      "Transferring pixels to magick image...");
1593
3.03M
              for (y=0; y < (long)image->rows; y++)
1594
3.02M
                {
1595
3.02M
                  q=SetImagePixelsEx(image,0,y,image->columns,1,exception);
1596
3.02M
                  if (q == (PixelPacket *) NULL)
1597
4
                    {
1598
4
                      HEIFReadImageFrameCleanup();
1599
4
                      return MagickFail;
1600
4
                    }
1601
12.0M
                  for (channel_index = 0; channel_index < num_channels; channel_index++)
1602
9.07M
                    line[channel_index].u8=planes[channel_index].u8+y*row_stride[channel_index];
1603
1.87G
                  for (x=0; x < (long)image->columns; x++)
1604
1.87G
                    {
1605
7.48G
                      for (channel_index = 0; channel_index < num_channels; channel_index++)
1606
5.61G
                        {
1607
5.61G
                          uint32_t
1608
5.61G
                            sample = 0;
1609
1610
5.61G
                          Quantum
1611
5.61G
                            quantum=0;
1612
1613
                          /*
1614
                            Get sample
1615
                          */
1616
5.61G
                          switch(channel_bits_per_pixel[channel_index])
1617
5.61G
                            {
1618
3.76G
                            case 8:
1619
3.76G
                              sample = *line[channel_index].u8;
1620
3.76G
                              line[channel_index].u8++;
1621
3.76G
                              break;
1622
1.84G
                            case 16:
1623
1.84G
                              sample = *line[channel_index].u16;
1624
1.84G
                              line[channel_index].u16++;
1625
1.84G
                              break;
1626
5.61G
                            }
1627
1628
                          /*
1629
                            Scale sample to quantum
1630
                          */
1631
5.61G
                          if (channel_bits_per_pixel_range[channel_index] == 8)
1632
3.76G
                            {
1633
3.76G
                              quantum=ScaleCharToQuantum(sample);
1634
3.76G
                            }
1635
1.84G
                          else if (channel_bits_per_pixel_range[channel_index] < 8)
1636
21.6k
                            {
1637
21.6k
                              quantum=ScaleAnyToQuantum(sample,channel_max_value_given_bits[channel_index]);
1638
21.6k
                            }
1639
1.84G
                          else if (channel_bits_per_pixel_range[channel_index] == 16)
1640
71.9k
                            {
1641
71.9k
                              quantum=ScaleShortToQuantum(sample);
1642
71.9k
                            }
1643
1.84G
                          else if (channel_bits_per_pixel_range[channel_index] < 16)
1644
1.84G
                            {
1645
1.84G
                              quantum=ScaleAnyToQuantum(sample,channel_max_value_given_bits[channel_index]);
1646
1.84G
                            }
1647
1648
                          /*
1649
                            Assign quantum to PixelPacket
1650
                          */
1651
5.61G
                          switch (channel_index)
1652
5.61G
                            {
1653
1.87G
                            case 0:
1654
1.87G
                              SetRedSample(q,quantum);
1655
1.87G
                              if (num_channels != 4)
1656
1.87G
                                SetOpacitySample(q,OpaqueOpacity);
1657
1.87G
                              break;
1658
1.87G
                            case 1:
1659
1.87G
                              SetGreenSample(q,quantum);
1660
1.87G
                              break;
1661
1.87G
                            case 2:
1662
1.87G
                              SetBlueSample(q,quantum);
1663
1.87G
                              break;
1664
95.4k
                            case 3:
1665
95.4k
                              SetOpacitySample(q,MaxRGB-quantum);
1666
95.4k
                              break;
1667
5.61G
                            }
1668
5.61G
                        }
1669
1.87G
                      q++;
1670
1.87G
                    }
1671
3.02M
                  if (!SyncImagePixelsEx(image,exception))
1672
0
                    {
1673
0
                      HEIFReadImageFrameCleanup();
1674
0
                      return MagickFail;
1675
0
                    }
1676
3.02M
                }
1677
14.2k
              if (image->logging)
1678
14.2k
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1679
14.2k
                                      "  done!");
1680
14.2k
            } /* heif_chroma_444 */
1681
0
          else
1682
0
            {
1683
              /*
1684
                Decode RGB(A) interleaved pixels
1685
1686
                This requires set-up in advance to request the desired RGB(A) sub-format.
1687
1688
                heif_chroma_interleaved_RGB
1689
                heif_chroma_interleaved_RGBA
1690
                heif_chroma_interleaved_RRGGBB_BE
1691
                heif_chroma_interleaved_RRGGBBAA_BE
1692
                heif_chroma_interleaved_RRGGBB_LE
1693
                heif_chroma_interleaved_RRGGBBAA_LE
1694
              */
1695
1696
0
              typedef union
1697
0
              {
1698
0
                const uint8_t *u8;
1699
0
                const uint16_t *u16;
1700
0
              } magick_pixels_t;
1701
1702
0
              magick_pixels_t
1703
0
                pixels;
1704
1705
0
              size_t
1706
0
                row_stride;
1707
1708
0
              unsigned long
1709
0
                max_value_given_bits,
1710
0
                x,
1711
0
                y;
1712
1713
0
              int
1714
0
                bits_per_pixel,
1715
0
                bits_per_pixel_range;
1716
1717
0
              enum heif_channel
1718
0
                heif_channel;
1719
1720
0
              const MagickBool
1721
0
                matte = image->matte;
1722
1723
0
              if (image->logging)
1724
0
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1725
0
                                      "Chroma format: %s", heif_chroma_format_str);
1726
1727
0
              heif_channel = heif_channel_interleaved;
1728
1729
0
              heif_channel_str = HEIF_channel_to_string(heif_channel);
1730
1731
0
              bits_per_pixel = heif_image_get_bits_per_pixel(heif_image,
1732
0
                                                             heif_channel);
1733
0
              bits_per_pixel_range = heif_image_get_bits_per_pixel_range(heif_image,
1734
0
                                                                         heif_channel);
1735
1736
0
              max_value_given_bits = MaxValueGivenBits(bits_per_pixel_range);
1737
1738
0
              if (0 == max_value_given_bits)
1739
0
                {
1740
0
                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1741
0
                                        "MaxValueGivenBits(%d) returned 0!",
1742
0
                                        bits_per_pixel_range);
1743
0
                  ThrowHEIFThrowReadImageFrameException(CorruptImageError,
1744
0
                                                        AnErrorHasOccurredReadingFromFile, image);
1745
0
                }
1746
1747
0
              pixels.u8 = heif_image_get_plane_readonly2(heif_image,
1748
0
                                                         heif_channel,
1749
0
                                                         &row_stride);
1750
1751
0
              if (pixels.u8 == (const uint8_t*) NULL)
1752
0
                {
1753
0
                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1754
0
                                        "heif_image_get_plane_readonly2() returned NULL"
1755
0
                                        " (channel %s)!",heif_channel_str);
1756
0
                  ThrowHEIFThrowReadImageFrameException(CorruptImageError,
1757
0
                                                        AnErrorHasOccurredReadingFromFile, image);
1758
0
                }
1759
1760
              /* interleaved row stride 1536 (8 bits in 24 bit packet) */
1761
              /* interleaved row stride 3072 (16 bit samples in 48 bit packet) */
1762
0
              if (image->logging)
1763
0
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1764
0
                                      "  %s row stride %zu (%d bit samples in %d bit packet)",
1765
0
                                      heif_channel_str, row_stride,
1766
0
                                      bits_per_pixel_range,
1767
0
                                      bits_per_pixel);
1768
1769
0
              if (bits_per_pixel_range <= 8)
1770
0
                {
1771
0
                  const uint8_t
1772
0
                    *p;
1773
1774
0
                  for (y=0; y < image->rows; y++)
1775
0
                    {
1776
0
                      p=pixels.u8+y*row_stride;
1777
0
                      q=SetImagePixelsEx(image,0,y,image->columns,1,exception);
1778
0
                      if (q == (PixelPacket *) NULL)
1779
0
                        {
1780
0
                          HEIFReadImageFrameCleanup();
1781
0
                          return MagickFail;
1782
0
                        }
1783
0
                      if (bits_per_pixel_range == 8)
1784
0
                        {
1785
0
                          for (x=0; x < image->columns; x++)
1786
0
                            {
1787
0
                              SetRedSample(q,ScaleCharToQuantum(*p++));
1788
0
                              SetGreenSample(q,ScaleCharToQuantum(*p++));
1789
0
                              SetBlueSample(q,ScaleCharToQuantum(*p++));
1790
0
                              if (matte)
1791
0
                                SetOpacitySample(q,MaxRGB-ScaleCharToQuantum(*p++));
1792
0
                              else
1793
0
                                SetOpacitySample(q,OpaqueOpacity);
1794
0
                              q++;
1795
0
                            }
1796
0
                        }
1797
0
                      else
1798
0
                        {
1799
                          /* Maybe this case will never happen */
1800
0
                          for (x=0; x < image->columns; x++)
1801
0
                            {
1802
0
                              SetRedSample(q,ScaleAnyToQuantum(*p++,max_value_given_bits));
1803
0
                              SetGreenSample(q,ScaleAnyToQuantum(*p++,max_value_given_bits));
1804
0
                              SetBlueSample(q,ScaleAnyToQuantum(*p++,max_value_given_bits));
1805
0
                              if (matte)
1806
0
                                SetOpacitySample(q,MaxRGB-ScaleAnyToQuantum(*p++,max_value_given_bits));
1807
0
                              else
1808
0
                                SetOpacitySample(q,OpaqueOpacity);
1809
0
                              q++;
1810
0
                            }
1811
0
                        }
1812
0
                      if (!SyncImagePixelsEx(image,exception))
1813
0
                        {
1814
0
                          HEIFReadImageFrameCleanup();
1815
0
                          return MagickFail;
1816
0
                        }
1817
0
                    }
1818
0
                }
1819
0
              else if (bits_per_pixel_range <= 16)
1820
0
                {
1821
0
                  const uint16_t
1822
0
                    *p;
1823
1824
0
                  const size_t
1825
0
                    row_stride_uint16 = row_stride/sizeof(uint16_t);
1826
1827
0
                  for (y=0; y < image->rows; y++)
1828
0
                    {
1829
0
                      p=pixels.u16+y*row_stride_uint16;
1830
0
                      q=SetImagePixelsEx(image,0,y,image->columns,1,exception);
1831
0
                      if (q == (PixelPacket *) NULL)
1832
0
                        {
1833
0
                          HEIFReadImageFrameCleanup();
1834
0
                          return MagickFail;
1835
0
                        }
1836
0
                      if (bits_per_pixel_range == 16)
1837
0
                        {
1838
0
                          for (x=0; x < image->columns; x++)
1839
0
                            {
1840
0
                              SetRedSample(q,ScaleShortToQuantum(*p++));
1841
0
                              SetGreenSample(q,ScaleShortToQuantum(*p++));
1842
0
                              SetBlueSample(q,ScaleShortToQuantum(*p++));
1843
0
                              if (matte)
1844
0
                                SetOpacitySample(q,MaxRGB-ScaleShortToQuantum(*p++));
1845
0
                              else
1846
0
                                SetOpacitySample(q,OpaqueOpacity);
1847
0
                              q++;
1848
0
                            }
1849
0
                        }
1850
0
                      else
1851
0
                        {
1852
0
                          for (x=0; x < image->columns; x++)
1853
0
                            {
1854
0
                              SetRedSample(q,ScaleAnyToQuantum(*p++,max_value_given_bits));
1855
0
                              SetGreenSample(q,ScaleAnyToQuantum(*p++,max_value_given_bits));
1856
0
                              SetBlueSample(q,ScaleAnyToQuantum(*p++,max_value_given_bits));
1857
0
                              if (matte)
1858
0
                                SetOpacitySample(q,MaxRGB-ScaleAnyToQuantum(*p++,max_value_given_bits));
1859
0
                              else
1860
0
                                SetOpacitySample(q,OpaqueOpacity);
1861
0
                              q++;
1862
0
                            }
1863
0
                        }
1864
0
                      if (!SyncImagePixelsEx(image,exception))
1865
0
                        {
1866
0
                          HEIFReadImageFrameCleanup();
1867
0
                          return MagickFail;
1868
0
                        }
1869
0
                    }
1870
0
                }
1871
0
            }
1872
14.2k
          break;
1873
14.2k
        } /* case heif_colorspace_RGB */
1874
14.2k
      case heif_colorspace_monochrome:
1875
378
        {
1876
          /*
1877
            Decode Y(A) pixels
1878
          */
1879
378
          static const enum heif_channel channels_YA[4] =
1880
378
            { heif_channel_Y, heif_channel_Alpha };
1881
1882
378
          magick_plane_t
1883
378
            planes[2];
1884
1885
378
          magick_plane_t
1886
378
            line[2];
1887
1888
378
          size_t
1889
378
            row_stride[2];
1890
1891
378
          int
1892
378
            channel_bits_per_pixel[2];
1893
1894
378
          int
1895
378
            channel_bits_per_pixel_range[2];
1896
1897
378
          unsigned long
1898
378
            channel_max_value_given_bits[2];
1899
1900
378
          unsigned int
1901
378
            channel_index,
1902
378
            num_channels;
1903
1904
378
          enum heif_channel heif_channel;
1905
1906
378
          num_channels = image->matte ? 2 : 1;
1907
1908
378
          assert(heif_image_get_chroma_format(heif_image) == heif_chroma_monochrome);
1909
1910
378
          if (image->logging)
1911
378
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1912
378
                                  "Decoding %u plane%s for monochrome colorspace (chroma %s)",
1913
378
                                  num_channels, num_channels > 1 ? "s" : "", heif_chroma_format_str);
1914
1915
757
          for (channel_index = 0; channel_index < num_channels; channel_index++)
1916
379
            {
1917
379
              heif_channel = channels_YA[channel_index];
1918
379
              heif_channel_str = HEIF_channel_to_string(heif_channel);
1919
379
              planes[channel_index].u8 = heif_image_get_plane_readonly2(heif_image,
1920
379
                                                                        heif_channel,
1921
379
                                                                        &row_stride[channel_index]);
1922
379
              if (planes[channel_index].u8 == (const uint8_t*) NULL)
1923
0
                {
1924
0
                  if (image->logging)
1925
0
                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1926
0
                                          "heif_image_get_plane_readonly2() returned NULL"
1927
0
                                          " for channel index %u (channel %s)!",
1928
0
                                          channel_index,heif_channel_str);
1929
0
                  ThrowHEIFThrowReadImageFrameException(CorruptImageError,
1930
0
                                                        AnErrorHasOccurredReadingFromFile, image);
1931
0
                }
1932
379
              channel_bits_per_pixel[channel_index] = heif_image_get_bits_per_pixel(heif_image,
1933
379
                                                                                    heif_channel);
1934
379
              channel_bits_per_pixel_range[channel_index] = heif_image_get_bits_per_pixel_range(heif_image,
1935
379
                                                                                                heif_channel);
1936
379
              channel_max_value_given_bits[channel_index] = MaxValueGivenBits(channel_bits_per_pixel_range[channel_index]);
1937
379
              if (0 == channel_max_value_given_bits[channel_index])
1938
0
                {
1939
0
                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1940
0
                                        "MaxValueGivenBits(%d) returned 0!",
1941
0
                                        channel_bits_per_pixel_range[channel_index]);
1942
0
                  ThrowHEIFThrowReadImageFrameException(CorruptImageError,
1943
0
                                                        AnErrorHasOccurredReadingFromFile, image);
1944
0
                }
1945
379
              if ((int) image->depth < channel_bits_per_pixel_range[channel_index])
1946
25
                image->depth=(unsigned int) channel_bits_per_pixel_range[channel_index];
1947
379
              heif_channel_str = HEIF_channel_to_string(heif_channel);
1948
379
              if (image->logging)
1949
379
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1950
379
                                      "  channel %s row stride %zu (%d bits in %d bit quantum)",
1951
379
                                      heif_channel_str, row_stride[channel_index],
1952
379
                                      channel_bits_per_pixel_range[channel_index],
1953
379
                                      channel_bits_per_pixel[channel_index]);
1954
379
            }
1955
1956
          /* Transfer pixels to image, using row stride to find start of each row. */
1957
378
          if (image->logging)
1958
378
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1959
378
                                  "Transferring pixels to magick image...");
1960
97.8k
          for (y=0; y < (long)image->rows; y++)
1961
97.4k
            {
1962
97.4k
              q=SetImagePixelsEx(image,0,y,image->columns,1,exception);
1963
97.4k
              if (q == (PixelPacket *) NULL)
1964
0
                {
1965
0
                  HEIFReadImageFrameCleanup();
1966
0
                  return MagickFail;
1967
0
                }
1968
194k
              for (channel_index = 0; channel_index < num_channels; channel_index++)
1969
97.4k
                line[channel_index].u8=planes[channel_index].u8+y*row_stride[channel_index];
1970
26.3M
              for (x=0; x < (long)image->columns; x++)
1971
26.3M
                {
1972
52.6M
                  for (channel_index = 0; channel_index < num_channels; channel_index++)
1973
26.3M
                    {
1974
26.3M
                      uint32_t
1975
26.3M
                        sample = 0;
1976
1977
26.3M
                      Quantum
1978
26.3M
                        quantum=0;
1979
1980
                      /*
1981
                        Get sample
1982
                      */
1983
26.3M
                      switch(channel_bits_per_pixel[channel_index])
1984
26.3M
                        {
1985
16.7M
                        case 8:
1986
16.7M
                          sample = *line[channel_index].u8;
1987
16.7M
                          line[channel_index].u8++;
1988
16.7M
                          break;
1989
9.57M
                        case 16:
1990
9.57M
                          sample = *line[channel_index].u16;
1991
9.57M
                          line[channel_index].u16++;
1992
9.57M
                          break;
1993
26.3M
                        }
1994
1995
                      /*
1996
                        Scale sample to quantum
1997
                      */
1998
26.3M
                      if (channel_bits_per_pixel_range[channel_index] == 8)
1999
16.7M
                        {
2000
16.7M
                          quantum=ScaleCharToQuantum(sample);
2001
16.7M
                        }
2002
9.57M
                      else if (channel_bits_per_pixel_range[channel_index] < 8)
2003
600
                        {
2004
600
                          quantum=ScaleAnyToQuantum(sample,channel_max_value_given_bits[channel_index]);
2005
600
                        }
2006
9.57M
                      else if (channel_bits_per_pixel_range[channel_index] == 16)
2007
0
                        {
2008
0
                          quantum=ScaleShortToQuantum(sample);
2009
0
                        }
2010
9.57M
                      else if (channel_bits_per_pixel_range[channel_index] < 16)
2011
9.57M
                        {
2012
9.57M
                          quantum=ScaleAnyToQuantum(sample,channel_max_value_given_bits[channel_index]);
2013
9.57M
                        }
2014
2015
                      /*
2016
                        Assign quantum to PixelPacket
2017
                      */
2018
26.3M
                      switch (channel_index)
2019
26.3M
                        {
2020
26.3M
                        case 0:
2021
26.3M
                          SetGraySample(q,quantum);
2022
26.3M
                          if (num_channels != 2)
2023
26.2M
                            SetOpacitySample(q,OpaqueOpacity);
2024
26.3M
                          break;
2025
1.02k
                        case 1:
2026
1.02k
                          SetOpacitySample(q,MaxRGB-quantum);
2027
1.02k
                          break;
2028
26.3M
                        }
2029
26.3M
                    }
2030
26.3M
                  q++;
2031
26.3M
                }
2032
97.4k
              if (!SyncImagePixelsEx(image,exception))
2033
0
                {
2034
0
                  HEIFReadImageFrameCleanup();
2035
0
                  return MagickFail;
2036
0
                }
2037
97.4k
            }
2038
378
          if (image->logging)
2039
378
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2040
378
                                  "  done!");
2041
378
        }
2042
0
        break;
2043
0
      case heif_colorspace_nonvisual:
2044
        /* No visual channels */
2045
0
#if LIBHEIF_HAVE_VERSION(1,21,3)
2046
        /* Git commit cb6e524 added heif_colorspace_filter_array on 2/24/26 */
2047
1
      case heif_colorspace_filter_array:
2048
1
#endif /* if LIBHEIF_HAVE_VERSION(1,21,3) */
2049
1
      default:
2050
        /* Should never reach here. */
2051
1
        break;
2052
14.6k
      }
2053
14.6k
  }
2054
2055
14.6k
  HEIFReadImageFrameCleanup();
2056
2057
14.6k
  if (image->logging)
2058
14.6k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2059
14.6k
                          "Image depth: %u", image->depth);
2060
2061
14.6k
  return MagickPass;
2062
14.6k
}
2063
2064
static Image *ReadHEIFImage(const ImageInfo *image_info,
2065
                            ExceptionInfo *exception)
2066
97.2k
{
2067
97.2k
  Image
2068
97.2k
    *image;
2069
2070
97.2k
  struct heif_context
2071
97.2k
    *heif = NULL;
2072
2073
97.2k
  struct heif_image_handle
2074
97.2k
    *heif_image_handle = NULL;
2075
2076
97.2k
  struct heif_image
2077
97.2k
    *heif_image = NULL;
2078
2079
97.2k
  struct heif_decoding_options
2080
97.2k
    *decode_options = NULL;
2081
2082
97.2k
  const char
2083
97.2k
    *value;
2084
2085
97.2k
  unsigned char
2086
97.2k
    *in_buf = NULL;
2087
2088
97.2k
  heif_item_id
2089
97.2k
    *item_ids = NULL;
2090
2091
97.2k
  const char *
2092
97.2k
    magick;
2093
2094
97.2k
  size_t
2095
97.2k
    in_len = 0;
2096
2097
97.2k
  struct heif_error
2098
97.2k
    heif_status;
2099
2100
97.2k
  int
2101
97.2k
#if defined(HEIF_SUPPORT_SEQUENCES) && HEIF_SUPPORT_SEQUENCES
2102
97.2k
    has_sequence,
2103
97.2k
#endif /* if defined(HEIF_SUPPORT_SEQUENCES) && HEIF_SUPPORT_SEQUENCES */
2104
97.2k
    number_of_top_level_images;
2105
2106
97.2k
  char
2107
97.2k
    fourcc[5];
2108
2109
97.2k
  heif_item_id
2110
97.2k
    primary_image_id;
2111
2112
#if HEIF_ENABLE_PROGRESS_MONITOR
2113
  ProgressUserData
2114
    progress_user_data;
2115
#endif
2116
2117
97.2k
  MagickBool
2118
97.2k
    in_buf_allocated=MagickFalse;
2119
2120
97.2k
  MagickPassFail
2121
97.2k
    status;
2122
2123
97.2k
  assert(image_info != (const ImageInfo *) NULL);
2124
97.2k
  assert(image_info->signature == MagickSignature);
2125
97.2k
  assert(exception != (ExceptionInfo *) NULL);
2126
97.2k
  assert(exception->signature == MagickSignature);
2127
2128
97.2k
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2129
97.2k
                        "ImageInfo magick = %s", image_info->magick);
2130
  /*
2131
    Open image file.
2132
  */
2133
97.2k
  image=AllocateImage(image_info);
2134
97.2k
  if (image == (Image *) NULL)
2135
97.2k
    ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
2136
2137
97.2k
  if (OpenBlob(image_info,image,ReadBinaryBlobMode,exception) == MagickFail)
2138
97.2k
    ThrowReaderException(FileOpenError,UnableToOpenFile,image);
2139
2140
97.2k
  if (!heif_initialized)
2141
9
    {
2142
      /* heif_init() accepts a 'struct heif_init_params *' argument */
2143
9
      heif_init((struct heif_init_params *) NULL);
2144
9
      heif_initialized = MagickTrue;
2145
9
    }
2146
2147
2148
  /* Add decoding options support */
2149
97.2k
  decode_options = heif_decoding_options_alloc();
2150
97.2k
  if (decode_options == (struct heif_decoding_options*) NULL)
2151
97.2k
    ThrowHEIFReaderException(ResourceLimitError,MemoryAllocationFailed,image);
2152
2153
97.2k
  decode_options->ignore_transformations = image_info->ping ? MagickTrue : MagickFalse;
2154
97.2k
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2155
97.2k
                        "Initial ignore_transformations = %s",
2156
97.2k
                        decode_options->ignore_transformations ? "True" : "False");
2157
#if HEIF_ENABLE_PROGRESS_MONITOR
2158
  progress_user_data.exception = exception;
2159
  progress_user_data.image = image;
2160
  progress_user_data.max_progress = 0;
2161
  progress_user_data.progress = 0;
2162
#endif
2163
2164
#if HEIF_ENABLE_PROGRESS_MONITOR
2165
  decode_options->start_progress = start_progress;
2166
  decode_options->on_progress = on_progress;
2167
  decode_options->end_progress = end_progress;
2168
  decode_options->progress_user_data = &progress_user_data;
2169
#endif /* if HEIF_ENABLE_PROGRESS_MONITOR */
2170
2171
  /* version 2 decode options */
2172
2173
  /* Let libheif do the scaling from deep images if QuantumDepth == 8 */
2174
#if QuantumDepth == 8
2175
  decode_options->convert_hdr_to_8bit = 1;
2176
#endif /* if QuantumDepth == 8 */
2177
2178
2179
97.2k
  in_len=GetBlobSize(image);
2180
97.2k
  in_buf=GetBlobStreamData(image);
2181
97.2k
  if (in_buf == (unsigned char *) NULL)
2182
0
    {
2183
      /*
2184
        Read the whole input file into a memory blob
2185
      */
2186
0
      in_buf=MagickAllocateResourceLimitedArray(unsigned char *,in_len,sizeof(*in_buf));
2187
0
      if (in_buf == (unsigned char *) NULL)
2188
0
        ThrowHEIFReaderException(ResourceLimitError,MemoryAllocationFailed,image);
2189
0
      in_buf_allocated=MagickTrue;
2190
2191
0
      if (image->logging)
2192
0
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2193
0
                              "Reading file into in-memory blob...");
2194
0
      if (ReadBlob(image,in_len,in_buf) != in_len)
2195
0
        ThrowHEIFReaderException(CorruptImageError, UnexpectedEndOfFile, image);
2196
0
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2197
0
                            "  Done!");
2198
0
    }
2199
97.2k
  else
2200
97.2k
    {
2201
      /*
2202
        Accessing memory-mapped input file or blob memory directly.
2203
      */
2204
97.2k
      if (image->logging)
2205
97.2k
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2206
97.2k
                              "Input accessed as in-memory blob");
2207
97.2k
    }
2208
2209
  /* Init HEIF-Decoder handles */
2210
97.2k
  heif=heif_context_alloc();
2211
2212
  /* Apply security limits */
2213
97.2k
  (void) apply_security_limits(image_info,heif);
2214
2215
  /* Header sanity check */
2216
97.2k
  if (!IsHEIF(in_buf, in_len))
2217
97.0k
    ThrowHEIFReaderException(CorruptImageError, ImproperImageHeader, image);
2218
2219
  /* Test file header for format, and update image magick string */
2220
97.0k
  magick="HEIF";
2221
97.0k
  if (heif_has_compatible_brand(in_buf, in_len, "avci") == 1)
2222
798
    magick="AVCI";
2223
96.2k
  else if (heif_has_compatible_brand(in_buf, in_len, "avif") == 1)
2224
29.2k
    magick="AVIF";
2225
66.9k
  else if (heif_has_compatible_brand(in_buf, in_len, "heix") == 1)
2226
653
    magick="HEIC";
2227
66.3k
  else if (heif_has_compatible_brand(in_buf, in_len, "heic") == 1)
2228
12.8k
    magick="HEIC";
2229
53.4k
  else if (heif_has_compatible_brand(in_buf, in_len, "mp41") == 1)
2230
333
    magick="MP4";
2231
53.1k
  else if (heif_has_compatible_brand(in_buf, in_len, "mp42") == 1)
2232
165
    magick="MP4";
2233
97.0k
  (void) strlcpy(image->magick,magick,sizeof(image->magick));
2234
97.0k
  if (image->logging)
2235
97.0k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2236
97.0k
                          "Assigned magick: %s", magick);
2237
2238
  /*
2239
    Trace some information about the file
2240
  */
2241
97.0k
  if (image->logging)
2242
97.0k
    {
2243
97.0k
      {
2244
        /* Log main brand */
2245
97.0k
        (void) memset(fourcc,0,sizeof(fourcc));
2246
97.0k
        heif_brand_to_fourcc(heif_read_main_brand(in_buf, in_len), fourcc);
2247
97.0k
        fourcc[4]=0;
2248
97.0k
        if (fourcc[0] == 0)
2249
13.2k
          {
2250
13.2k
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2251
13.2k
                                  "Failed to read main brand!");
2252
13.2k
          }
2253
83.7k
        else
2254
83.7k
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2255
83.7k
                                "HEIF Brand: \"%s\"", fourcc);
2256
97.0k
      }
2257
97.0k
      {
2258
        /* Log compatible brands */
2259
97.0k
        heif_brand2* brands = NULL;
2260
97.0k
        int nBrands = 0;
2261
97.0k
        heif_status = heif_list_compatible_brands(in_buf, in_len, &brands, &nBrands);
2262
97.0k
        if (heif_status.code == heif_error_Ok)
2263
70.3k
          {
2264
70.3k
            char
2265
70.3k
              *compatible_brands;
2266
2267
70.3k
            const size_t
2268
70.3k
              compatible_brands_alloc = 6*(nBrands+1);
2269
2270
70.3k
            compatible_brands=MagickAllocateClearedMemory(char *,compatible_brands_alloc);
2271
70.3k
            if (compatible_brands == (char *) NULL)
2272
70.3k
              ThrowHEIFReaderException(ResourceLimitError,MemoryAllocationFailed,image);
2273
70.3k
            {
2274
70.3k
              int
2275
70.3k
                i;
2276
2277
300k
              for (i = 0; i < nBrands; i++)
2278
230k
                {
2279
230k
                  heif_brand_to_fourcc(brands[i], fourcc);
2280
230k
                  fourcc[4]=0;
2281
230k
                  if (i > 0)
2282
161k
                    (void) strlcat(compatible_brands,", ",compatible_brands_alloc);
2283
230k
                  (void) strlcat(compatible_brands,fourcc,compatible_brands_alloc);
2284
230k
                }
2285
70.3k
            }
2286
70.3k
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2287
70.3k
                                  "Compatible HEIF brands: %s", compatible_brands);
2288
70.3k
            MagickFreeMemory(compatible_brands);
2289
70.3k
          }
2290
26.6k
        else
2291
26.6k
          {
2292
26.6k
            if (image->logging)
2293
26.6k
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2294
26.6k
                                    "heif_list_compatible_brands() "
2295
26.6k
                                    "reports error %d, suberror %d, message \"%s\"",
2296
26.6k
                                    heif_status.code, heif_status.subcode, heif_status.message);
2297
26.6k
          }
2298
97.0k
        heif_free_list_of_compatible_brands(brands);
2299
97.0k
      }
2300
0
      {
2301
        /* Log MIME type */
2302
97.0k
        const char *mime_type = heif_get_file_mime_type(in_buf, in_len);
2303
97.0k
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2304
97.0k
                              "MIME Type: %s",
2305
97.0k
                              mime_type == (const char *) NULL ? "unknown" : mime_type);
2306
97.0k
      }
2307
97.0k
    } /* if (image->logging) */
2308
2309
97.0k
#if LIBHEIF_HAVE_VERSION(1,15,0)
2310
  /* Specify the maximum number of tile decoding threads used by libheif (default 4). */
2311
97.0k
  {
2312
97.0k
    unsigned int decoding_threads;
2313
#if defined(HAVE_OPENMP)
2314
    decoding_threads=(uint32_t) omp_get_max_threads();
2315
#else
2316
97.0k
    decoding_threads = 4;
2317
97.0k
#endif
2318
97.0k
    MagickAttributeToU32(image_info,"heif","tile-threads",decoding_threads);
2319
97.0k
    if (image->logging)
2320
97.0k
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2321
97.0k
                            "Using %u tile decoding threads...", decoding_threads);
2322
97.0k
    heif_context_set_max_decoding_threads(heif,decoding_threads);
2323
97.0k
  }
2324
97.0k
#endif /* LIBHEIF_HAVE_VERSION(1,15,0) */
2325
2326
  /*
2327
    Allow the user to enable transformations (e.g. the image might be rotated).
2328
  */
2329
97.0k
  if ((value=AccessDefinition(image_info,"heif","ignore-transformations")))
2330
0
    {
2331
0
      if (LocaleCompare(value,"FALSE") == 0)
2332
0
        decode_options->ignore_transformations = MagickFalse;
2333
0
      else if (LocaleCompare(value,"TRUE") == 0)
2334
0
        decode_options->ignore_transformations = MagickTrue;
2335
0
      if (image->logging)
2336
0
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2337
0
                              "Updated ignore-transformations: %s",
2338
0
                              decode_options->ignore_transformations ? "True" : "False");
2339
0
    }
2340
2341
2342
97.0k
  heif_status=heif_context_read_from_memory_without_copy(heif, in_buf, in_len, NULL);
2343
97.0k
  if (heif_status.code == heif_error_Unsupported_filetype
2344
94.8k
      || heif_status.code == heif_error_Unsupported_feature)
2345
94.4k
    ThrowHEIFReaderException(CoderError, ImageTypeNotSupported, image);
2346
94.4k
  if (heif_status.code != heif_error_Ok)
2347
48.3k
    {
2348
48.3k
      if (image->logging)
2349
48.3k
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2350
48.3k
                              "heif_context_read_from_memory_without_copy() "
2351
48.3k
                              "reports error %d, suberror %d, message \"%s\"",
2352
48.3k
                              heif_status.code, heif_status.subcode, heif_status.message);
2353
48.3k
      ThrowHEIFReaderException(CorruptImageError, AnErrorHasOccurredReadingFromFile, image);
2354
0
    }
2355
2356
46.0k
#if defined(HEIF_SUPPORT_SEQUENCES) && HEIF_SUPPORT_SEQUENCES
2357
46.0k
  has_sequence = heif_context_has_sequence(heif);
2358
46.0k
  if (image->logging)
2359
46.0k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2360
46.0k
                          "Has sequence: %c", has_sequence ? 'Y' : 'N');
2361
46.0k
#endif /* if defined(HEIF_SUPPORT_SEQUENCES) && HEIF_SUPPORT_SEQUENCES */
2362
2363
  /* Get the number of top level images */
2364
46.0k
  number_of_top_level_images=heif_context_get_number_of_top_level_images(heif);
2365
46.0k
  if (image->logging)
2366
46.0k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2367
46.0k
                          "Number of top level images: %d",
2368
46.0k
                          number_of_top_level_images);
2369
46.0k
  if (number_of_top_level_images <= 0)
2370
375
    {
2371
375
      if (image->logging)
2372
375
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2373
375
                              "heif_context_get_number_of_top_level_images() reports %d \"%s\"",
2374
375
                              number_of_top_level_images, heif_status.message);
2375
375
      ThrowHEIFReaderException(CorruptImageError, AnErrorHasOccurredReadingFromFile, image);
2376
0
    }
2377
2378
  /* Get the primary image id */
2379
45.7k
  heif_status=heif_context_get_primary_image_ID(heif,&primary_image_id);
2380
45.7k
  if (heif_status.code != heif_error_Ok)
2381
0
    {
2382
0
      if (image->logging)
2383
0
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2384
0
                              "heif_context_get_primary_image_ID() "
2385
0
                              "reports error %d, suberror %d, message \"%s\"",
2386
0
                              heif_status.code, heif_status.subcode, heif_status.message);
2387
0
      ThrowHEIFReaderException(CorruptImageError, AnErrorHasOccurredReadingFromFile, image);
2388
0
    }
2389
45.7k
  if (image->logging)
2390
45.7k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2391
45.7k
                          "Primary image id: %d", primary_image_id);
2392
2393
  /* Get a handle for the primary image id */
2394
45.7k
  heif_status=heif_context_get_image_handle(heif, primary_image_id, &heif_image_handle);
2395
45.7k
  if (heif_status.code == heif_error_Memory_allocation_error)
2396
45.7k
    ThrowHEIFReaderException(ResourceLimitError, MemoryAllocationFailed, image);
2397
45.7k
  if (heif_status.code != heif_error_Ok)
2398
1.14k
    {
2399
1.14k
      if (image->logging)
2400
1.14k
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2401
1.14k
                              "heif_context_get_image_handle() "
2402
1.14k
                              "reports error %d, suberror %d, message \"%s\"",
2403
1.14k
                              heif_status.code, heif_status.subcode, heif_status.message);
2404
1.14k
      ThrowHEIFReaderException(CorruptImageError, AnErrorHasOccurredReadingFromFile, image);
2405
0
    }
2406
2407
44.5k
  status=ReadHEIFImageFrame(heif_image_handle,decode_options,image,image_info,exception);
2408
2409
44.5k
  heif_image_handle_release(heif_image_handle);
2410
44.5k
  heif_image_handle=(struct heif_image_handle *) NULL;
2411
2412
  /*
2413
    Read any additional top-level images
2414
  */
2415
44.5k
  if ((status != MagickFail) && (number_of_top_level_images > 1))
2416
1.65k
    {
2417
1.65k
      int
2418
1.65k
        item_num;
2419
2420
1.65k
      item_ids=MagickAllocateResourceLimitedArray(heif_item_id *,number_of_top_level_images,
2421
1.65k
                                                  sizeof(*item_ids));
2422
1.65k
      if (item_ids == (heif_item_id *) NULL)
2423
1.65k
        ThrowHEIFReaderException(ResourceLimitError, MemoryAllocationFailed, image);
2424
2425
1.65k
      heif_context_get_list_of_top_level_image_IDs(heif,item_ids,number_of_top_level_images);
2426
2427
3.28k
      for (item_num=0; item_num < number_of_top_level_images; item_num++)
2428
3.28k
        {
2429
3.28k
          if (image->logging)
2430
3.28k
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2431
3.28k
                                  "Image index %d: image id %d",
2432
3.28k
                                  item_num, item_ids[item_num]);
2433
2434
          /* Skip the primary image we already read. Hopefully this
2435
             was also the first image in the top level image list. */
2436
3.28k
          if (item_ids[item_num] == primary_image_id)
2437
1.62k
            {
2438
1.62k
              if (image->logging)
2439
1.62k
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2440
1.62k
                                      "Item %d matches primary image id %d (skipping)",
2441
1.62k
                                      item_num, primary_image_id);
2442
1.62k
              continue;
2443
1.62k
            }
2444
2445
          /*
2446
            Allocate next image structure.
2447
          */
2448
1.65k
          AllocateNextImage(image_info,image);
2449
1.65k
          image=SyncNextImageInList(image);
2450
1.65k
          (void) strlcpy(image->magick,magick,sizeof(image->magick));
2451
2452
          /* Get HEIF image handle */
2453
1.65k
          heif_status=heif_context_get_image_handle(heif, item_ids[item_num],&heif_image_handle);
2454
1.65k
          if (heif_status.code == heif_error_Memory_allocation_error)
2455
1.65k
            ThrowHEIFReaderException(ResourceLimitError, MemoryAllocationFailed, image);
2456
1.65k
          if (heif_status.code != heif_error_Ok)
2457
118
            {
2458
118
              if (image->logging)
2459
118
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2460
118
                                      "heif_context_get_image_handle() "
2461
118
                                      "reports error %d, suberror %d, message \"%s\"",
2462
118
                                      heif_status.code, heif_status.subcode, heif_status.message);
2463
118
              ThrowHEIFReaderException(CorruptImageError, AnErrorHasOccurredReadingFromFile, image);
2464
0
            }
2465
2466
          /* Read the HEIF frame */
2467
1.53k
          status=ReadHEIFImageFrame(heif_image_handle,decode_options,image,image_info,exception);
2468
2469
          /* Release the HEIF image handle */
2470
1.53k
          heif_image_handle_release(heif_image_handle);
2471
1.53k
          heif_image_handle=(struct heif_image_handle *) NULL;
2472
2473
1.53k
          if (status == MagickFail)
2474
774
            break;
2475
2476
          /* Quit if no more scenes requested */
2477
761
          if (image_info->subrange != 0)
2478
761
            if (image->scene >= (image_info->subimage+image_info->subrange-1))
2479
761
              break;
2480
761
        }
2481
1.53k
      MagickFreeResourceLimitedMemory(heif_item_id *,item_ids);
2482
1.53k
    }
2483
2484
44.4k
  HEIFReadCleanup();
2485
44.4k
  CloseBlob(image);
2486
44.4k
  if (status == MagickFail)
2487
31.5k
    {
2488
31.5k
      DestroyImageList(image);
2489
31.5k
      image=(Image *) NULL;
2490
31.5k
    }
2491
2492
44.4k
  return image;
2493
44.5k
}
2494
2495
#endif /* HasHEIF */
2496
2497
/*
2498
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2499
%                                                                             %
2500
%                                                                             %
2501
%                                                                             %
2502
%   R e g i s t e r H E I F I m a g e                                         %
2503
%                                                                             %
2504
%                                                                             %
2505
%                                                                             %
2506
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2507
%
2508
%  Method RegisterHEIFImage adds attributes for the HEIF image format to
2509
%  the list of supported formats.  The attributes include the image format
2510
%  tag, a method to read and/or write the format and a brief
2511
%  description of the format.
2512
%
2513
%  The format of the RegisterHEIFImage method is:
2514
%
2515
%      RegisterHEIFImage(void)
2516
%
2517
*/
2518
static void GetHEIFDecoders(enum heif_compression_format compression_format,
2519
                            char *output_str,
2520
                            const size_t sizeof_output_str)
2521
36
{
2522
36
  const char
2523
36
    *decoder_description;
2524
2525
36
  int
2526
36
    decoder_count,
2527
36
    decoder_index;
2528
2529
36
#define MAX_HEIF_DECODERS 20
2530
2531
  /* heif_compression_format is declared in libheif/api/libheif/heif_context.h */
2532
36
  const heif_decoder_descriptor
2533
36
    *out_decoders[MAX_HEIF_DECODERS];
2534
2535
36
  decoder_count = heif_get_decoder_descriptors(compression_format,
2536
36
                                               out_decoders,
2537
36
                                               MAX_HEIF_DECODERS);
2538
36
  if (decoder_count > 0)
2539
36
    {
2540
36
      (void) strlcpy(output_str,"Decoders: ", sizeof_output_str);
2541
135
      for (decoder_index = 0 ; decoder_index < decoder_count; decoder_index++)
2542
99
        {
2543
99
          decoder_description = heif_decoder_descriptor_get_name(out_decoders[decoder_index]);
2544
99
          if (decoder_description != (const char *) NULL)
2545
99
            {
2546
99
              if (decoder_index != 0)
2547
63
                (void) strlcat(output_str,"; ",sizeof_output_str);
2548
99
              (void) strlcat(output_str,decoder_description,sizeof_output_str);
2549
99
            }
2550
99
        }
2551
36
    }
2552
36
}
2553
ModuleExport void RegisterHEIFImage(void)
2554
9
{
2555
9
#if defined(HasHEIF)
2556
9
  static char
2557
9
    version[20];
2558
2559
9
  static const MagickBool
2560
9
    need_seekable_stream=MagickFalse; /* Currently use in memory blob */
2561
2562
9
  MagickInfo
2563
9
    *entry;
2564
2565
  /* Get version information */
2566
9
  {
2567
9
    unsigned int
2568
9
      heif_major,
2569
9
      heif_minor,
2570
9
      heif_revision;
2571
2572
9
    int
2573
9
      encoder_version;
2574
2575
9
    encoder_version=heif_get_version_number();
2576
9
    heif_major=(encoder_version >> 16) & 0xff;
2577
9
    heif_minor=(encoder_version >> 8) & 0xff;
2578
9
    heif_revision=encoder_version & 0xff;
2579
9
    *version='\0';
2580
9
    (void) snprintf(version, sizeof(version),
2581
9
                    "heif v%u.%u.%u", heif_major,
2582
9
                    heif_minor, heif_revision);
2583
9
  }
2584
2585
  /* heif_compression_format is declared in libheif/api/libheif/heif_context.h */
2586
2587
  /* AVCI (H.264) */
2588
9
  if (heif_have_decoder_for_format(heif_compression_AVC)/* || */
2589
9
      /* heif_have_encoder_for_format(heif_compression_AVC) */)
2590
9
    {
2591
9
      static char
2592
9
        AVCNote[64] = "";
2593
2594
9
      GetHEIFDecoders(heif_compression_AVC,AVCNote,sizeof(AVCNote));
2595
2596
9
      entry=SetMagickInfo("AVCI");
2597
9
      entry->decoder=(DecoderHandler) ReadHEIFImage;
2598
9
      entry->magick=(MagickHandler) IsHEIF;
2599
9
      entry->description="Advanced Video Coding Image Format";
2600
9
      if (AVCNote[0] != '\0')
2601
9
        entry->note=AVCNote;
2602
9
      entry->adjoin=MagickFalse;
2603
9
      entry->seekable_stream=need_seekable_stream;
2604
9
      if (*version != '\0')
2605
9
        entry->version=version;
2606
9
      entry->module="HEIF";
2607
9
      entry->coder_class=PrimaryCoderClass;
2608
9
      entry->extension_treatment=ObeyExtensionTreatment;
2609
9
      (void) RegisterMagickInfo(entry);
2610
9
    }
2611
2612
  /* AVIF / heif_compression_AV1 */
2613
9
  if (heif_have_decoder_for_format(heif_compression_AV1)/* || */
2614
9
      /* heif_have_encoder_for_format(heif_compression_AV1) */)
2615
9
    {
2616
9
      static char
2617
9
        AV1Note[64] = "";
2618
2619
9
      GetHEIFDecoders(heif_compression_AV1,AV1Note,sizeof(AV1Note));
2620
9
      entry=SetMagickInfo("AVIF");
2621
9
      entry->decoder=(DecoderHandler) ReadHEIFImage;
2622
9
      entry->magick=(MagickHandler) IsHEIF;
2623
9
      entry->description="AV1 Image Format";
2624
9
      if (AV1Note[0] != '\0')
2625
9
        entry->note=AV1Note;
2626
9
      entry->adjoin=MagickFalse;
2627
9
      entry->seekable_stream=need_seekable_stream;
2628
9
      if (*version != '\0')
2629
9
        entry->version=version;
2630
9
      entry->module="HEIF";
2631
9
      entry->coder_class=PrimaryCoderClass;
2632
9
      entry->extension_treatment=ObeyExtensionTreatment;
2633
9
      (void) RegisterMagickInfo(entry);
2634
9
    }
2635
2636
  /*
2637
    This entry is used for anything which looks like HEIF, but is not
2638
    one of our directly supported subformats.
2639
  */
2640
9
  {
2641
9
    static char
2642
9
      HEIFNote[256] = "";
2643
2644
9
    GetHEIFDecoders(heif_compression_undefined,HEIFNote,sizeof(HEIFNote));
2645
2646
9
    entry=SetMagickInfo("HEIF");
2647
9
    entry->decoder=(DecoderHandler) ReadHEIFImage;
2648
9
    entry->magick=(MagickHandler) IsHEIF;
2649
9
    entry->description="High Efficiency Image Format";
2650
9
    if (HEIFNote[0] != '\0')
2651
9
      entry->note=HEIFNote;
2652
9
    entry->adjoin=MagickFalse;
2653
9
    entry->seekable_stream=need_seekable_stream;
2654
9
    if (*version != '\0')
2655
9
      entry->version=version;
2656
9
    entry->module="HEIF";
2657
9
    entry->coder_class=PrimaryCoderClass;
2658
9
    entry->extension_treatment=ObeyExtensionTreatment;
2659
9
    (void) RegisterMagickInfo(entry);
2660
9
  }
2661
2662
  /* HEIC (H.265) / heif_compression_HEVC */
2663
9
  if (heif_have_decoder_for_format(heif_compression_HEVC) /* || */
2664
9
      /* heif_have_encoder_for_format(heif_compression_HEVC) */)
2665
9
    {
2666
9
      static char
2667
9
        HEVCNote[64] = "";
2668
2669
9
      GetHEIFDecoders(heif_compression_AV1,HEVCNote,sizeof(HEVCNote));
2670
2671
9
      entry=SetMagickInfo("HEIC");
2672
9
      entry->decoder=(DecoderHandler) ReadHEIFImage;
2673
9
      entry->magick=(MagickHandler) IsHEIF;
2674
9
      entry->description="High-Efficiency Image Codec in HEIF";
2675
9
      if (HEVCNote[0] != '\0')
2676
9
        entry->note=HEVCNote;
2677
9
      entry->adjoin=MagickFalse;
2678
9
      entry->seekable_stream=need_seekable_stream;
2679
9
      if (*version != '\0')
2680
9
        entry->version=version;
2681
9
      entry->module="HEIF";
2682
9
      entry->coder_class=PrimaryCoderClass;
2683
9
      entry->extension_treatment=ObeyExtensionTreatment;
2684
9
      (void) RegisterMagickInfo(entry);
2685
2686
      /* Libheif supports some MP4 files */
2687
9
      entry=SetMagickInfo("MP4");
2688
9
      entry->decoder=(DecoderHandler) ReadHEIFImage;
2689
9
      entry->magick=(MagickHandler) IsHEIF;
2690
9
      entry->description="MPEG-4 Part 14 in HEIF";
2691
9
      if (HEVCNote[0] != '\0')
2692
9
        entry->note=HEVCNote;
2693
9
      entry->adjoin=MagickFalse;
2694
9
      entry->seekable_stream=need_seekable_stream;
2695
9
      if (*version != '\0')
2696
9
        entry->version=version;
2697
9
      entry->module="HEIF";
2698
9
      entry->coder_class=PrimaryCoderClass;
2699
9
      entry->extension_treatment=ObeyExtensionTreatment;
2700
9
      (void) RegisterMagickInfo(entry);
2701
9
    }
2702
2703
  /* MP4 (H.264, H.265) / heif_compression_HEVC */
2704
  /*
2705
    Since HEIF image sequences are very similar to MP4 video, libheif
2706
    can also read and write MP4 video (without audio) with all
2707
    supported codecs.
2708
2709
    heif_context_has_sequence() will return if input is a sequence.
2710
    Note that a file can contain still images and a sequence at the
2711
    same time.
2712
2713
    heif_compression_HEVC is H.265, heif_compression_AVC is H.264, and
2714
    heif_compression_AV1 is AV1
2715
2716
    video/mp4 or application/mp4
2717
  */
2718
#if 0
2719
  if (heif_have_decoder_for_format(heif_compression_HEVC) ||
2720
      heif_have_decoder_for_format(heif_compression_AVC) ||
2721
      heif_have_decoder_for_format(heif_compression_AV1))
2722
    {
2723
      static char
2724
        MP4Note[256] = "";
2725
2726
      /* FIXME: Needs to handle all MP4 codecs */
2727
      GetHEIFDecoders(heif_compression_AVC,MP4Note,sizeof(MP4Note));
2728
2729
      entry=SetMagickInfo("MP4");
2730
      entry->decoder=(DecoderHandler) ReadHEIFImage;
2731
      entry->magick=(MagickHandler) IsHEIF;
2732
      entry->description="MP4 video container (HVEC, AVC, or AV1 compression)";
2733
      if (MP4Note[0] != '\0')
2734
        entry->note=MP4Note;
2735
      entry->adjoin=MagickFalse;
2736
      entry->seekable_stream=need_seekable_stream;
2737
      if (*version != '\0')
2738
        entry->version=version;
2739
      entry->module="HEIF";
2740
      entry->coder_class=UnstableCoderClass;
2741
      entry->extension_treatment=HintExtensionTreatment;
2742
      (void) RegisterMagickInfo(entry);
2743
    }
2744
#endif
2745
2746
9
#endif /* HasHEIF */
2747
9
}
2748
2749
/*
2750
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2751
%                                                                             %
2752
%                                                                             %
2753
%                                                                             %
2754
%   U n r e g i s t e r H E I F I m a g e                                     %
2755
%                                                                             %
2756
%                                                                             %
2757
%                                                                             %
2758
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2759
%
2760
%  Method UnregisterHEIFImage removes format registrations made by the
2761
%  HEIF module from the list of supported formats.
2762
%
2763
%  The format of the UnregisterHEIFImage method is:
2764
%
2765
%      UnregisterHEIFImage(void)
2766
%
2767
*/
2768
ModuleExport void UnregisterHEIFImage(void)
2769
0
{
2770
0
#if defined(HasHEIF)
2771
0
  (void) UnregisterMagickInfo("AVCI");
2772
0
  (void) UnregisterMagickInfo("AVIF");
2773
0
  (void) UnregisterMagickInfo("HEIC");
2774
0
  (void) UnregisterMagickInfo("HEIF");
2775
0
  (void) UnregisterMagickInfo("MP4");
2776
0
  if (heif_initialized)
2777
0
    heif_deinit();
2778
0
#endif /* HasHEIF */
2779
0
}