1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % H H EEEEE IIIII CCCC %
7 % H H E I C %
8 % HHHHH EEE I C %
9 % H H E I C %
10 % H H EEEEE IIIII CCCC %
11 % %
12 % %
13 % Read/Write Heic Image Format %
14 % %
15 % Dirk Farin %
16 % April 2018 %
17 % %
18 % Copyright 2018 Struktur AG %
19 % %
20 % Anton Kortunov %
21 % December 2017 %
22 % %
23 % Copyright 2017-2018 YANDEX LLC. %
24 % %
25 % Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
26 % dedicated to making software imaging solutions freely available. %
27 % %
28 % You may not use this file except in compliance with the License. You may %
29 % obtain a copy of the License at %
30 % %
31 % https://imagemagick.org/script/license.php %
32 % %
33 % Unless required by applicable law or agreed to in writing, software %
34 % distributed under the License is distributed on an "AS IS" BASIS, %
35 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
36 % See the License for the specific language governing permissions and %
37 % limitations under the License. %
38 % %
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40 %
41 %
42 */
43
44 /*
45 Include declarations.
46 */
47 #include "MagickCore/studio.h"
48 #include "MagickCore/artifact.h"
49 #include "MagickCore/blob.h"
50 #include "MagickCore/blob-private.h"
51 #include "MagickCore/client.h"
52 #include "MagickCore/colorspace-private.h"
53 #include "MagickCore/property.h"
54 #include "MagickCore/display.h"
55 #include "MagickCore/exception.h"
56 #include "MagickCore/exception-private.h"
57 #include "MagickCore/image.h"
58 #include "MagickCore/image-private.h"
59 #include "MagickCore/list.h"
60 #include "MagickCore/magick.h"
61 #include "MagickCore/monitor.h"
62 #include "MagickCore/monitor-private.h"
63 #include "MagickCore/montage.h"
64 #include "MagickCore/transform.h"
65 #include "MagickCore/distort.h"
66 #include "MagickCore/memory_.h"
67 #include "MagickCore/memory-private.h"
68 #include "MagickCore/option.h"
69 #include "MagickCore/pixel-accessor.h"
70 #include "MagickCore/quantum-private.h"
71 #include "MagickCore/static.h"
72 #include "MagickCore/string_.h"
73 #include "MagickCore/string-private.h"
74 #include "MagickCore/module.h"
75 #include "MagickCore/utility.h"
76 #if defined(MAGICKCORE_HEIC_DELEGATE)
77 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
78 #include <heif.h>
79 #else
80 #include <libheif/heif.h>
81 #endif
82 #endif
83
84 #if defined(MAGICKCORE_HEIC_DELEGATE)
85 /*
86 Define declarations.
87 */
88 #define XmpNamespaceExtent 28
89
90 /*
91 Const declarations.
92 */
93 static const char
94 xmp_namespace[] = "http://ns.adobe.com/xap/1.0/ ";
95
96 /*
97 Forward declarations.
98 */
99 static MagickBooleanType
100 WriteHEICImage(const ImageInfo *,Image *,ExceptionInfo *);
101
102 /*x
103 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
104 % %
105 % %
106 % %
107 % R e a d H E I C I m a g e %
108 % %
109 % %
110 % %
111 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
112 %
113 % ReadHEICImage retrieves an image via a file descriptor, decodes the image,
114 % and returns it. It allocates the memory necessary for the new Image
115 % structure and returns a pointer to the new image.
116 %
117 % The format of the ReadHEICImage method is:
118 %
119 % Image *ReadHEICImage(const ImageInfo *image_info,
120 % ExceptionInfo *exception)
121 %
122 % A description of each parameter follows:
123 %
124 % o image_info: the image info.
125 %
126 % o exception: return any errors or warnings in this structure.
127 %
128 */
129
IsHeifSuccess(Image * image,struct heif_error * error,ExceptionInfo * exception)130 static MagickBooleanType IsHeifSuccess(Image *image,struct heif_error *error,
131 ExceptionInfo *exception)
132 {
133 if (error->code == 0)
134 return(MagickTrue);
135 ThrowBinaryException(CorruptImageError,error->message,image->filename);
136 }
137
ReadHEICColorProfile(Image * image,struct heif_image_handle * image_handle,ExceptionInfo * exception)138 static MagickBooleanType ReadHEICColorProfile(Image *image,
139 struct heif_image_handle *image_handle,ExceptionInfo *exception)
140 {
141 size_t
142 length;
143
144 /*
145 Read color profile.
146 */
147 #if LIBHEIF_NUMERIC_VERSION >= 0x01040000
148 length=heif_image_handle_get_raw_color_profile_size(image_handle);
149 if (length > 0)
150 {
151 unsigned char
152 *color_buffer;
153
154 if ((MagickSizeType) length > GetBlobSize(image))
155 ThrowBinaryException(CorruptImageError,"InsufficientImageDataInFile",
156 image->filename);
157 color_buffer=(unsigned char *) AcquireQuantumMemory(1,length);
158 if (color_buffer != (unsigned char *) NULL)
159 {
160 struct heif_error
161 error;
162
163 error=heif_image_handle_get_raw_color_profile(image_handle,
164 color_buffer);
165 if (error.code == 0)
166 {
167 StringInfo
168 *profile;
169
170 profile=BlobToStringInfo(color_buffer,length);
171 if (profile != (StringInfo*) NULL)
172 {
173 (void) SetImageProfile(image,"icc",profile,exception);
174 profile=DestroyStringInfo(profile);
175 }
176 }
177 }
178 color_buffer=(unsigned char *) RelinquishMagickMemory(color_buffer);
179 }
180 #endif
181 return(MagickTrue);
182 }
183
ReadHEICExifProfile(Image * image,struct heif_image_handle * image_handle,ExceptionInfo * exception)184 static MagickBooleanType ReadHEICExifProfile(Image *image,
185 struct heif_image_handle *image_handle,ExceptionInfo *exception)
186 {
187 heif_item_id
188 exif_id;
189
190 int
191 count;
192
193 /*
194 Read Exif profile.
195 */
196 count=heif_image_handle_get_list_of_metadata_block_IDs(image_handle,"Exif",
197 &exif_id,1);
198 if (count > 0)
199 {
200 size_t
201 exif_size;
202
203 unsigned char
204 *exif_buffer;
205
206 exif_size=heif_image_handle_get_metadata_size(image_handle,exif_id);
207 if ((MagickSizeType) exif_size > GetBlobSize(image))
208 ThrowBinaryException(CorruptImageError,"InsufficientImageDataInFile",
209 image->filename);
210 exif_buffer=(unsigned char *) AcquireQuantumMemory(1,exif_size);
211 if (exif_buffer != (unsigned char *) NULL)
212 {
213 struct heif_error
214 error;
215
216 error=heif_image_handle_get_metadata(image_handle,
217 exif_id,exif_buffer);
218 if (error.code == 0)
219 {
220 StringInfo
221 *profile;
222
223 /*
224 Skip first 4 bytes, the offset to the TIFF header.
225 */
226 profile=(StringInfo*) NULL;
227 if (exif_size > 8)
228 profile=BlobToStringInfo(exif_buffer+4,(size_t) exif_size-4);
229 if (profile != (StringInfo*) NULL)
230 {
231 (void) SetImageProfile(image,"exif",profile,exception);
232 profile=DestroyStringInfo(profile);
233 }
234 }
235 }
236 exif_buffer=(unsigned char *) RelinquishMagickMemory(exif_buffer);
237 }
238 return(MagickTrue);
239 }
240
HEICSkipImage(const ImageInfo * image_info,Image * image)241 static inline MagickBooleanType HEICSkipImage(const ImageInfo *image_info,
242 Image *image)
243 {
244 if (image_info->number_scenes == 0)
245 return(MagickFalse);
246 if (image->scene == 0)
247 return(MagickFalse);
248 if (image->scene < image_info->scene)
249 return(MagickTrue);
250 if (image->scene > image_info->scene+image_info->number_scenes-1)
251 return(MagickTrue);
252 return(MagickFalse);
253 }
254
ReadHEICImageByID(const ImageInfo * image_info,Image * image,struct heif_image_handle * image_handle,ExceptionInfo * exception)255 static MagickBooleanType ReadHEICImageByID(const ImageInfo *image_info,
256 Image *image,struct heif_image_handle *image_handle,
257 ExceptionInfo *exception)
258 {
259 const uint8_t
260 *p;
261
262 int
263 stride = 0;
264
265 MagickBooleanType
266 preserve_orientation,
267 status;
268
269 ssize_t
270 y;
271
272 struct heif_decoding_options
273 *decode_options;
274
275 struct heif_error
276 error;
277
278 struct heif_image
279 *heif_image;
280
281 /*
282 Read HEIC image from container.
283 */
284 image->columns=(size_t) heif_image_handle_get_width(image_handle);
285 image->rows=(size_t) heif_image_handle_get_height(image_handle);
286 image->depth=8;
287 #if LIBHEIF_NUMERIC_VERSION > 0x01040000
288 {
289 int
290 bits_per_pixel;
291
292 bits_per_pixel=heif_image_handle_get_luma_bits_per_pixel(image_handle);
293 if (bits_per_pixel != -1)
294 image->depth=(size_t) bits_per_pixel;
295 }
296 #endif
297 if (heif_image_handle_has_alpha_channel(image_handle))
298 image->alpha_trait=BlendPixelTrait;
299 preserve_orientation=IsStringTrue(GetImageOption(image_info,
300 "heic:preserve-orientation"));
301 if (preserve_orientation == MagickFalse)
302 (void) SetImageProperty(image,"exif:Orientation","1",exception);
303 if (ReadHEICColorProfile(image,image_handle,exception) == MagickFalse)
304 return(MagickFalse);
305 if (ReadHEICExifProfile(image,image_handle,exception) == MagickFalse)
306 return(MagickFalse);
307 if (image_info->ping != MagickFalse)
308 return(MagickTrue);
309 if (HEICSkipImage(image_info,image) != MagickFalse)
310 return(MagickTrue);
311 status=SetImageExtent(image,image->columns,image->rows,exception);
312 if (status == MagickFalse)
313 return(MagickFalse);
314 decode_options=heif_decoding_options_alloc();
315 #if LIBHEIF_NUMERIC_VERSION > 0x01070000
316 decode_options->convert_hdr_to_8bit=1;
317 #endif
318 if (preserve_orientation == MagickTrue)
319 decode_options->ignore_transformations=1;
320 error=heif_decode_image(image_handle,&heif_image,heif_colorspace_RGB,
321 image->alpha_trait != UndefinedPixelTrait ? heif_chroma_interleaved_RGBA :
322 heif_chroma_interleaved_RGB,decode_options);
323 heif_decoding_options_free(decode_options);
324 if (IsHeifSuccess(image,&error,exception) == MagickFalse)
325 return(MagickFalse);
326 image->columns=(size_t) heif_image_get_width(heif_image,
327 heif_channel_interleaved);
328 image->rows=(size_t) heif_image_get_height(heif_image
329 ,heif_channel_interleaved);
330 status=SetImageExtent(image,image->columns,image->rows,exception);
331 if (status == MagickFalse)
332 {
333 heif_image_release(heif_image);
334 return(MagickFalse);
335 }
336 p=heif_image_get_plane_readonly(heif_image,heif_channel_interleaved,&stride);
337 stride-=(int) (image->columns * (image->alpha_trait != UndefinedPixelTrait ?
338 4 : 3));
339 for (y=0; y < (ssize_t) image->rows; y++)
340 {
341 Quantum
342 *q;
343
344 ssize_t
345 x;
346
347 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
348 if (q == (Quantum *) NULL)
349 break;
350 for (x=0; x < (ssize_t) image->columns; x++)
351 {
352 SetPixelRed(image,ScaleCharToQuantum((unsigned char) *(p++)),q);
353 SetPixelGreen(image,ScaleCharToQuantum((unsigned char) *(p++)),q);
354 SetPixelBlue(image,ScaleCharToQuantum((unsigned char) *(p++)),q);
355 if (image->alpha_trait != UndefinedPixelTrait)
356 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *(p++)),q);
357 q+=GetPixelChannels(image);
358 }
359 p+=stride;
360 if (SyncAuthenticPixels(image,exception) == MagickFalse)
361 break;
362 }
363 heif_image_release(heif_image);
364 return(MagickTrue);
365 }
366
ReadHEICImage(const ImageInfo * image_info,ExceptionInfo * exception)367 static Image *ReadHEICImage(const ImageInfo *image_info,
368 ExceptionInfo *exception)
369 {
370 heif_item_id
371 *image_ids,
372 primary_image_id;
373
374 Image
375 *image;
376
377 MagickBooleanType
378 status;
379
380 size_t
381 count,
382 length;
383
384 struct heif_context
385 *heif_context;
386
387 struct heif_error
388 error;
389
390 struct heif_image_handle
391 *image_handle;
392
393 void
394 *file_data;
395
396 /*
397 Open image file.
398 */
399 assert(image_info != (const ImageInfo *) NULL);
400 assert(image_info->signature == MagickCoreSignature);
401 if (image_info->debug != MagickFalse)
402 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
403 image_info->filename);
404 assert(exception != (ExceptionInfo *) NULL);
405 assert(exception->signature == MagickCoreSignature);
406 image=AcquireImage(image_info,exception);
407 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
408 if (status == MagickFalse)
409 return(DestroyImageList(image));
410 if (GetBlobSize(image) > (MagickSizeType) MAGICK_SSIZE_MAX)
411 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
412 length=(size_t) GetBlobSize(image);
413 file_data=AcquireMagickMemory(length);
414 if (file_data == (void *) NULL)
415 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
416 if (ReadBlob(image,length,file_data) != (ssize_t) length)
417 {
418 file_data=RelinquishMagickMemory(file_data);
419 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
420 }
421 #if LIBHEIF_NUMERIC_VERSION >= 0x010b0000
422 if (heif_has_compatible_brand(file_data,(int) length,"avif") != MagickFalse)
423 (void) CopyMagickString(image->magick,"AVIF",MagickPathExtent);
424 #endif
425 /*
426 Decode HEIF image.
427 */
428 heif_context=heif_context_alloc();
429 error=heif_context_read_from_memory_without_copy(heif_context,file_data,
430 length,NULL);
431 if (IsHeifSuccess(image,&error,exception) == MagickFalse)
432 {
433 heif_context_free(heif_context);
434 file_data=RelinquishMagickMemory(file_data);
435 return(DestroyImageList(image));
436 }
437 error=heif_context_get_primary_image_ID(heif_context,&primary_image_id);
438 if (IsHeifSuccess(image,&error,exception) == MagickFalse)
439 {
440 heif_context_free(heif_context);
441 file_data=RelinquishMagickMemory(file_data);
442 return(DestroyImageList(image));
443 }
444 error=heif_context_get_image_handle(heif_context,primary_image_id,
445 &image_handle);
446 if (IsHeifSuccess(image,&error,exception) == MagickFalse)
447 {
448 heif_context_free(heif_context);
449 file_data=RelinquishMagickMemory(file_data);
450 return(DestroyImageList(image));
451 }
452 status=ReadHEICImageByID(image_info,image,image_handle,exception);
453 image_ids=(heif_item_id *) NULL;
454 count=(size_t) heif_context_get_number_of_top_level_images(heif_context);
455 if ((status != MagickFalse) && (count > 1))
456 {
457 size_t
458 i;
459
460 image_ids=(heif_item_id *) AcquireQuantumMemory((size_t) count,
461 sizeof(*image_ids));
462 if (image_ids == (heif_item_id *) NULL)
463 {
464 heif_image_handle_release(image_handle);
465 heif_context_free(heif_context);
466 file_data=RelinquishMagickMemory(file_data);
467 return(DestroyImageList(image));
468 }
469 (void) heif_context_get_list_of_top_level_image_IDs(heif_context,
470 image_ids,(int) count);
471 for (i=0; i < count; i++)
472 {
473 if (image_ids[i] == primary_image_id)
474 continue;
475 /*
476 Allocate next image structure.
477 */
478 AcquireNextImage(image_info,image,exception);
479 if (GetNextImageInList(image) == (Image *) NULL)
480 {
481 status=MagickFalse;
482 break;
483 }
484 image=SyncNextImageInList(image);
485 error=heif_context_get_image_handle(heif_context,primary_image_id,
486 &image_handle);
487 if (IsHeifSuccess(image,&error,exception) == MagickFalse)
488 {
489 status=MagickFalse;
490 break;
491 }
492 status=ReadHEICImageByID(image_info,image,image_handle,exception);
493 if (status == MagickFalse)
494 break;
495 if (image_info->number_scenes != 0)
496 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
497 break;
498 }
499 }
500 heif_image_handle_release(image_handle);
501 error=heif_context_get_image_handle(heif_context,primary_image_id,
502 &image_handle);
503 if (IsHeifSuccess(image,&error,exception) == MagickFalse)
504 {
505 heif_context_free(heif_context);
506 file_data=RelinquishMagickMemory(file_data);
507 return(DestroyImageList(image));
508 }
509 if (heif_image_handle_has_depth_image(image_handle) != 0)
510 {
511 heif_item_id
512 depth_id;
513
514 int
515 number_images;
516
517 /*
518 Read depth image.
519 */
520 number_images=heif_image_handle_get_list_of_depth_image_IDs(image_handle,
521 &depth_id,1);
522 if (number_images > 0)
523 {
524 struct heif_image_handle
525 *depth_handle;
526
527 error=heif_image_handle_get_depth_image_handle(image_handle,depth_id,
528 &depth_handle);
529 if (IsHeifSuccess(image,&error,exception) != MagickFalse)
530 {
531 AcquireNextImage(image_info,image,exception);
532 if (GetNextImageInList(image) == (Image *) NULL)
533 status=MagickFalse;
534 image=SyncNextImageInList(image);
535 status=ReadHEICImageByID(image_info,image,depth_handle,
536 exception);
537 heif_image_handle_release(depth_handle);
538 }
539 }
540 }
541 heif_image_handle_release(image_handle);
542 if (image_ids != (heif_item_id *) NULL)
543 (void) RelinquishMagickMemory(image_ids);
544 heif_context_free(heif_context);
545 file_data=RelinquishMagickMemory(file_data);
546 if (status == MagickFalse)
547 return(DestroyImageList(image));
548 return(GetFirstImageInList(image));
549 }
550 #endif
551
552 /*
553 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
554 % %
555 % %
556 % %
557 % I s H E I C %
558 % %
559 % %
560 % %
561 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
562 %
563 % IsHEIC() returns MagickTrue if the image format type, identified by the
564 % magick string, is Heic.
565 %
566 % The format of the IsHEIC method is:
567 %
568 % MagickBooleanType IsHEIC(const unsigned char *magick,const size_t length)
569 %
570 % A description of each parameter follows:
571 %
572 % o magick: compare image format pattern against these bytes.
573 %
574 % o length: Specifies the length of the magick string.
575 %
576 */
IsHEIC(const unsigned char * magick,const size_t length)577 static MagickBooleanType IsHEIC(const unsigned char *magick,const size_t length)
578 {
579 if (length < 12)
580 return(MagickFalse);
581 if (LocaleNCompare((const char *) magick+4,"ftyp",4) != 0)
582 return(MagickFalse);
583 if (LocaleNCompare((const char *) magick+8,"avif",4) == 0)
584 return(MagickTrue);
585 if (LocaleNCompare((const char *) magick+8,"heic",4) == 0)
586 return(MagickTrue);
587 if (LocaleNCompare((const char *) magick+8,"heix",4) == 0)
588 return(MagickTrue);
589 if (LocaleNCompare((const char *) magick+8,"mif1",4) == 0)
590 return(MagickTrue);
591 return(MagickFalse);
592 }
593
594 /*
595 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
596 % %
597 % %
598 % %
599 % R e g i s t e r H E I C I m a g e %
600 % %
601 % %
602 % %
603 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
604 %
605 % RegisterHEICImage() adds attributes for the HEIC image format to the list of
606 % supported formats. The attributes include the image format tag, a method
607 % to read and/or write the format, whether the format supports the saving of
608 % more than one frame to the same file or blob, whether the format supports
609 % native in-memory I/O, and a brief description of the format.
610 %
611 % The format of the RegisterHEICImage method is:
612 %
613 % size_t RegisterHEICImage(void)
614 %
615 */
RegisterHEICImage(void)616 ModuleExport size_t RegisterHEICImage(void)
617 {
618 MagickInfo
619 *entry;
620
621 entry=AcquireMagickInfo("HEIC","HEIC","High Efficiency Image Format");
622 #if defined(MAGICKCORE_HEIC_DELEGATE)
623 entry->decoder=(DecodeImageHandler *) ReadHEICImage;
624 #if LIBHEIF_NUMERIC_VERSION >= 0x01030000
625 if (heif_have_encoder_for_format(heif_compression_HEVC))
626 entry->encoder=(EncodeImageHandler *) WriteHEICImage;
627 #else
628 entry->encoder=(EncodeImageHandler *) WriteHEICImage;
629 #endif
630 #endif
631 entry->magick=(IsImageFormatHandler *) IsHEIC;
632 entry->mime_type=ConstantString("image/heic");
633 #if defined(LIBHEIF_VERSION)
634 entry->version=ConstantString(LIBHEIF_VERSION);
635 #endif
636 entry->flags|=CoderDecoderSeekableStreamFlag;
637 (void) RegisterMagickInfo(entry);
638 entry=AcquireMagickInfo("HEIC","HEIF","High Efficiency Image Format");
639 #if defined(MAGICKCORE_HEIC_DELEGATE)
640 entry->decoder=(DecodeImageHandler *) ReadHEICImage;
641 #if LIBHEIF_NUMERIC_VERSION >= 0x01030000
642 if (heif_have_encoder_for_format(heif_compression_HEVC))
643 entry->encoder=(EncodeImageHandler *) WriteHEICImage;
644 #else
645 entry->encoder=(EncodeImageHandler *) WriteHEICImage;
646 #endif
647 #endif
648 entry->magick=(IsImageFormatHandler *) IsHEIC;
649 entry->mime_type=ConstantString("image/heif");
650 #if defined(LIBHEIF_VERSION)
651 entry->version=ConstantString(LIBHEIF_VERSION);
652 #endif
653 entry->flags|=CoderDecoderSeekableStreamFlag;
654 (void) RegisterMagickInfo(entry);
655 #if LIBHEIF_NUMERIC_VERSION > 0x01060200
656 entry=AcquireMagickInfo("HEIC","AVIF","AV1 Image File Format");
657 #if defined(MAGICKCORE_HEIC_DELEGATE)
658 if (heif_have_decoder_for_format(heif_compression_AV1))
659 entry->decoder=(DecodeImageHandler *) ReadHEICImage;
660 if (heif_have_encoder_for_format(heif_compression_AV1))
661 entry->encoder=(EncodeImageHandler *) WriteHEICImage;
662 #endif
663 entry->magick=(IsImageFormatHandler *) IsHEIC;
664 entry->mime_type=ConstantString("image/avif");
665 #if defined(LIBHEIF_VERSION)
666 entry->version=ConstantString(LIBHEIF_VERSION);
667 #endif
668 entry->flags|=CoderDecoderSeekableStreamFlag;
669 (void) RegisterMagickInfo(entry);
670 #endif
671 return(MagickImageCoderSignature);
672 }
673
674 /*
675 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
676 % %
677 % %
678 % %
679 % U n r e g i s t e r H E I C I m a g e %
680 % %
681 % %
682 % %
683 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
684 %
685 % UnregisterHEICImage() removes format registrations made by the HEIC module
686 % from the list of supported formats.
687 %
688 % The format of the UnregisterHEICImage method is:
689 %
690 % UnregisterHEICImage(void)
691 %
692 */
UnregisterHEICImage(void)693 ModuleExport void UnregisterHEICImage(void)
694 {
695 #if LIBHEIF_NUMERIC_VERSION > 0x01060200
696 (void) UnregisterMagickInfo("AVIF");
697 #endif
698 (void) UnregisterMagickInfo("HEIC");
699 (void) UnregisterMagickInfo("HEIF");
700 }
701
702 /*
703 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
704 % %
705 % %
706 % %
707 % W r i t e H E I C I m a g e %
708 % %
709 % %
710 % %
711 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
712 %
713 % WriteHEICImage() writes an HEIF image using the libheif library.
714 %
715 % The format of the WriteHEICImage method is:
716 %
717 % MagickBooleanType WriteHEICImage(const ImageInfo *image_info,
718 % Image *image)
719 %
720 % A description of each parameter follows.
721 %
722 % o image_info: the image info.
723 %
724 % o image: The image.
725 %
726 % o exception: return any errors or warnings in this structure.
727 %
728 */
729
730 #if defined(MAGICKCORE_HEIC_DELEGATE)
731 #if LIBHEIF_NUMERIC_VERSION >= 0x01030000
WriteProfile(struct heif_context * context,Image * image,ExceptionInfo * exception)732 static void WriteProfile(struct heif_context *context,Image *image,
733 ExceptionInfo *exception)
734 {
735 const char
736 *name;
737
738 const StringInfo
739 *profile;
740
741 ssize_t
742 i;
743
744 size_t
745 length;
746
747 struct heif_error
748 error;
749
750 struct heif_image_handle
751 *image_handle;
752
753 /*
754 Get image handle.
755 */
756 image_handle=(struct heif_image_handle *) NULL;
757 error=heif_context_get_primary_image_handle(context,&image_handle);
758 if (error.code != 0)
759 return;
760 /*
761 Save image profile as a APP marker.
762 */
763 ResetImageProfileIterator(image);
764 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
765 {
766 profile=GetImageProfile(image,name);
767 length=GetStringInfoLength(profile);
768 if (LocaleCompare(name,"EXIF") == 0)
769 {
770 length=GetStringInfoLength(profile);
771 if (length > 65533L)
772 {
773 (void) ThrowMagickException(exception,GetMagickModule(),
774 CoderWarning,"ExifProfileSizeExceedsLimit","`%s'",
775 image->filename);
776 length=65533L;
777 }
778 (void) heif_context_add_exif_metadata(context,image_handle,
779 (void*) GetStringInfoDatum(profile),(int) length);
780 }
781 if (LocaleCompare(name,"XMP") == 0)
782 {
783 StringInfo
784 *xmp_profile;
785
786 xmp_profile=StringToStringInfo(xmp_namespace);
787 if (xmp_profile != (StringInfo *) NULL)
788 {
789 if (profile != (StringInfo *) NULL)
790 ConcatenateStringInfo(xmp_profile,profile);
791 GetStringInfoDatum(xmp_profile)[XmpNamespaceExtent]='\0';
792 for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
793 {
794 length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
795 error=heif_context_add_XMP_metadata(context,image_handle,
796 (void*) (GetStringInfoDatum(xmp_profile)+i),(int) length);
797 if (error.code != 0)
798 break;
799 }
800 xmp_profile=DestroyStringInfo(xmp_profile);
801 }
802 }
803 if (image->debug != MagickFalse)
804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
805 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
806 name=GetNextImageProfile(image);
807 }
808 heif_image_handle_release(image_handle);
809 }
810 #endif
811
heif_write_func(struct heif_context * context,const void * data,size_t size,void * userdata)812 static struct heif_error heif_write_func(struct heif_context *context,
813 const void* data,size_t size,void* userdata)
814 {
815 Image
816 *image;
817
818 struct heif_error
819 error_ok;
820
821 (void) context;
822 image=(Image*) userdata;
823 (void) WriteBlob(image,size,(const unsigned char *) data);
824 error_ok.code=heif_error_Ok;
825 error_ok.subcode=heif_suberror_Unspecified;
826 error_ok.message="ok";
827 return(error_ok);
828 }
829
WriteHEICImageYCbCr(Image * image,struct heif_image * heif_image,ExceptionInfo * exception)830 static MagickBooleanType WriteHEICImageYCbCr(Image *image,
831 struct heif_image *heif_image,ExceptionInfo *exception)
832 {
833 int
834 p_y,
835 p_cb,
836 p_cr;
837
838 MagickBooleanType
839 status;
840
841 ssize_t
842 y;
843
844 struct heif_error
845 error;
846
847 uint8_t
848 *q_y,
849 *q_cb,
850 *q_cr;
851
852 status=MagickTrue;
853 error=heif_image_add_plane(heif_image,heif_channel_Y,(int) image->columns,
854 (int) image->rows,8);
855 status=IsHeifSuccess(image,&error,exception);
856 if (status == MagickFalse)
857 return(status);
858 error=heif_image_add_plane(heif_image,heif_channel_Cb,
859 ((int) image->columns+1)/2,((int) image->rows+1)/2,8);
860 status=IsHeifSuccess(image,&error,exception);
861 if (status == MagickFalse)
862 return(status);
863 error=heif_image_add_plane(heif_image,heif_channel_Cr,
864 ((int) image->columns+1)/2,((int) image->rows+1)/2,8);
865 status=IsHeifSuccess(image,&error,exception);
866 if (status == MagickFalse)
867 return(status);
868 q_y=heif_image_get_plane(heif_image,heif_channel_Y,&p_y);
869 q_cb=heif_image_get_plane(heif_image,heif_channel_Cb,&p_cb);
870 q_cr=heif_image_get_plane(heif_image,heif_channel_Cr,&p_cr);
871 /*
872 Copy image to heif_image
873 */
874 for (y=0; y < (ssize_t) image->rows; y++)
875 {
876 const Quantum
877 *p;
878
879 ssize_t
880 x;
881
882 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
883 if (p == (const Quantum *) NULL)
884 {
885 status=MagickFalse;
886 break;
887 }
888 if ((y & 0x01) != 0)
889 for (x=0; x < (ssize_t) image->columns; x++)
890 {
891 q_y[y*p_y+x]=ScaleQuantumToChar(GetPixelRed(image,p));
892 p+=GetPixelChannels(image);
893 }
894 else
895 for (x=0; x < (ssize_t) image->columns; x+=2)
896 {
897 q_y[y*p_y+x]=ScaleQuantumToChar(GetPixelRed(image,p));
898 q_cb[y/2*p_cb+x/2]=ScaleQuantumToChar(GetPixelGreen(image,p));
899 q_cr[y/2*p_cr+x/2]=ScaleQuantumToChar(GetPixelBlue(image,p));
900 p+=GetPixelChannels(image);
901 if ((x+1) < (ssize_t) image->columns)
902 {
903 q_y[y*p_y+x+1]=ScaleQuantumToChar(GetPixelRed(image,p));
904 p+=GetPixelChannels(image);
905 }
906 }
907 if (image->previous == (Image *) NULL)
908 {
909 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
910 image->rows);
911 if (status == MagickFalse)
912 break;
913 }
914 }
915 return(status);
916 }
917
WriteHEICImageRGBA(Image * image,struct heif_image * heif_image,ExceptionInfo * exception)918 static MagickBooleanType WriteHEICImageRGBA(Image *image,
919 struct heif_image *heif_image,ExceptionInfo *exception)
920 {
921 MagickBooleanType
922 status;
923
924 ssize_t
925 y;
926
927 const Quantum
928 *p;
929
930 int
931 stride;
932
933 struct heif_error
934 error;
935
936 uint8_t
937 *q,
938 *plane;
939
940 status=MagickTrue;
941 error=heif_image_add_plane(heif_image,heif_channel_interleaved,
942 (int) image->columns,(int) image->rows,8);
943 status=IsHeifSuccess(image,&error,exception);
944 if (status == MagickFalse)
945 return status;
946 plane=heif_image_get_plane(heif_image,heif_channel_interleaved,&stride);
947 /*
948 Copy image to heif_image
949 */
950 for (y=0; y < (ssize_t) image->rows; y++)
951 {
952 ssize_t
953 x;
954
955 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
956 if (p == (const Quantum *) NULL)
957 {
958 status=MagickFalse;
959 break;
960 }
961 q=plane+(y*stride);
962 for (x=0; x < (ssize_t) image->columns; x++)
963 {
964 *(q++)=ScaleQuantumToChar(GetPixelRed(image,p));
965 *(q++)=ScaleQuantumToChar(GetPixelGreen(image,p));
966 *(q++)=ScaleQuantumToChar(GetPixelBlue(image,p));
967 if (image->alpha_trait != UndefinedPixelTrait)
968 *(q++)=ScaleQuantumToChar(GetPixelAlpha(image,p));
969
970 p+=GetPixelChannels(image);
971 }
972 if (image->previous == (Image *) NULL)
973 {
974 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
975 image->rows);
976 if (status == MagickFalse)
977 break;
978 }
979 }
980 return(status);
981 }
982
WriteHEICImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)983 static MagickBooleanType WriteHEICImage(const ImageInfo *image_info,
984 Image *image,ExceptionInfo *exception)
985 {
986 MagickBooleanType
987 status;
988
989 MagickOffsetType
990 scene;
991
992 struct heif_context
993 *heif_context;
994
995 struct heif_encoder
996 *heif_encoder;
997
998 struct heif_error
999 error;
1000
1001 struct heif_image
1002 *heif_image;
1003
1004 struct heif_writer
1005 writer;
1006
1007 #if LIBHEIF_NUMERIC_VERSION > 0x01060200
1008 MagickBooleanType
1009 encode_avif;
1010 #endif
1011
1012 /*
1013 Open output image file.
1014 */
1015 assert(image_info != (const ImageInfo *) NULL);
1016 assert(image_info->signature == MagickCoreSignature);
1017 assert(image != (Image *) NULL);
1018 assert(image->signature == MagickCoreSignature);
1019 if (image->debug != MagickFalse)
1020 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1021 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1022 if (status == MagickFalse)
1023 return(status);
1024 scene=0;
1025 heif_context=heif_context_alloc();
1026 heif_image=(struct heif_image*) NULL;
1027 heif_encoder=(struct heif_encoder*) NULL;
1028 #if LIBHEIF_NUMERIC_VERSION > 0x01060200
1029 encode_avif=(LocaleCompare(image_info->magick,"AVIF") == 0) ?
1030 MagickTrue : MagickFalse;
1031 #endif
1032 do
1033 {
1034 #if LIBHEIF_NUMERIC_VERSION >= 0x01040000
1035 const StringInfo
1036 *profile;
1037 #endif
1038
1039 enum heif_colorspace
1040 colorspace;
1041
1042 enum heif_chroma
1043 chroma;
1044
1045 MagickBooleanType
1046 lossless;
1047
1048 colorspace=heif_colorspace_YCbCr;
1049 lossless=image_info->quality == 100 ? MagickTrue : MagickFalse;
1050 chroma=lossless ? heif_chroma_444 : heif_chroma_420;
1051
1052
1053 /*
1054 Get encoder for the specified format.
1055 */
1056 #if LIBHEIF_NUMERIC_VERSION > 0x01060200
1057 if (encode_avif != MagickFalse)
1058 {
1059 error=heif_context_get_encoder_for_format(heif_context,
1060 heif_compression_AV1,&heif_encoder);
1061 if (IssRGBCompatibleColorspace(image->colorspace) != MagickFalse)
1062 {
1063 colorspace=heif_colorspace_RGB;
1064 chroma=(image->alpha_trait == UndefinedPixelTrait) ?
1065 heif_chroma_interleaved_RGB : heif_chroma_interleaved_RGBA;
1066 }
1067 }
1068 else
1069 #endif
1070 error=heif_context_get_encoder_for_format(heif_context,
1071 heif_compression_HEVC,&heif_encoder);
1072 status=IsHeifSuccess(image,&error,exception);
1073 if (status == MagickFalse)
1074 break;
1075 if ((colorspace == heif_colorspace_YCbCr) &&
1076 (image->colorspace != YCbCrColorspace))
1077 {
1078 status=TransformImageColorspace(image,YCbCrColorspace,exception);
1079 if (status == MagickFalse)
1080 break;
1081 }
1082 /*
1083 Initialize HEIF encoder context.
1084 */
1085 error=heif_image_create((int) image->columns,(int) image->rows,colorspace,
1086 chroma,&heif_image);
1087 status=IsHeifSuccess(image,&error,exception);
1088 if (status == MagickFalse)
1089 break;
1090
1091 #if LIBHEIF_NUMERIC_VERSION >= 0x01040000
1092 profile=GetImageProfile(image,"icc");
1093 if (profile != (StringInfo *) NULL)
1094 (void) heif_image_set_raw_color_profile(heif_image,"prof",
1095 GetStringInfoDatum(profile),GetStringInfoLength(profile));
1096 #endif
1097 if (colorspace == heif_colorspace_YCbCr)
1098 status=WriteHEICImageYCbCr(image,heif_image,exception);
1099 else
1100 status=WriteHEICImageRGBA(image,heif_image,exception);
1101 if (status == MagickFalse)
1102 break;
1103
1104 /*
1105 Code and actually write the HEIC image
1106 */
1107 if (lossless != MagickFalse)
1108 error=heif_encoder_set_lossless(heif_encoder, 1);
1109 else if (image_info->quality != UndefinedCompressionQuality)
1110 error=heif_encoder_set_lossy_quality(heif_encoder,(int)
1111 image_info->quality);
1112
1113 status=IsHeifSuccess(image,&error,exception);
1114 if (status == MagickFalse)
1115 break;
1116
1117 #if LIBHEIF_NUMERIC_VERSION > 0x01060200
1118 if (encode_avif != MagickFalse)
1119 {
1120 const char
1121 *option;
1122
1123 option=GetImageOption(image_info,"heic:speed");
1124 if (option != (char *) NULL)
1125 {
1126 error=heif_encoder_set_parameter(heif_encoder,"speed",option);
1127 status=IsHeifSuccess(image,&error,exception);
1128 if (status == MagickFalse)
1129 break;
1130 }
1131
1132 option=GetImageOption(image_info,"heic:chroma");
1133 if (option != (char *) NULL)
1134 {
1135 error=heif_encoder_set_parameter(heif_encoder,"chroma",option);
1136 status=IsHeifSuccess(image,&error,exception);
1137 if (status == MagickFalse)
1138 break;
1139 }
1140 }
1141 #endif
1142
1143 error=heif_context_encode_image(heif_context,heif_image,heif_encoder,
1144 (const struct heif_encoding_options *) NULL,
1145 (struct heif_image_handle **) NULL);
1146 status=IsHeifSuccess(image,&error,exception);
1147 if (status == MagickFalse)
1148 break;
1149
1150 #if LIBHEIF_NUMERIC_VERSION >= 0x01030000
1151 if (image->profiles != (void *) NULL)
1152 WriteProfile(heif_context,image,exception);
1153 #endif
1154 if (GetNextImageInList(image) == (Image *) NULL)
1155 break;
1156 image=SyncNextImageInList(image);
1157 status=SetImageProgress(image,SaveImagesTag,scene,
1158 GetImageListLength(image));
1159 if (status == MagickFalse)
1160 break;
1161 heif_encoder_release(heif_encoder);
1162 heif_encoder=(struct heif_encoder*) NULL;
1163 heif_image_release(heif_image);
1164 heif_image=(struct heif_image*) NULL;
1165 scene++;
1166 } while (image_info->adjoin != MagickFalse);
1167 if (status != MagickFalse)
1168 {
1169 writer.writer_api_version=1;
1170 writer.write=heif_write_func;
1171 #if LIBHEIF_NUMERIC_VERSION >= 0x01030000
1172 if (image->profiles != (void *) NULL)
1173 WriteProfile(heif_context,image,exception);
1174 #endif
1175 error=heif_context_write(heif_context,&writer,image);
1176 status=IsHeifSuccess(image,&error,exception);
1177 }
1178 if (heif_encoder != (struct heif_encoder*) NULL)
1179 heif_encoder_release(heif_encoder);
1180 if (heif_image != (struct heif_image*) NULL)
1181 heif_image_release(heif_image);
1182 heif_context_free(heif_context);
1183 (void) CloseBlob(image);
1184 return(status);
1185 }
1186 #endif
1187