2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % H H EEEEE IIIII CCCC %
10 % H H EEEEE IIIII CCCC %
13 % Read/Write Heic Image Format %
18 % Copyright 2018 Struktur AG %
23 % Copyright 2017-2018 YANDEX LLC. %
25 % You may not use this file except in compliance with the License. You may %
26 % obtain a copy of the License at %
28 % https://www.imagemagick.org/script/license.php %
30 % Unless required by applicable law or agreed to in writing, software %
31 % distributed under the License is distributed on an "AS IS" BASIS, %
32 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
33 % See the License for the specific language governing permissions and %
34 % limitations under the License. %
36 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/blob.h"
47 #include "MagickCore/blob-private.h"
48 #include "MagickCore/client.h"
49 #include "MagickCore/colorspace-private.h"
50 #include "MagickCore/property.h"
51 #include "MagickCore/display.h"
52 #include "MagickCore/exception.h"
53 #include "MagickCore/exception-private.h"
54 #include "MagickCore/image.h"
55 #include "MagickCore/image-private.h"
56 #include "MagickCore/list.h"
57 #include "MagickCore/magick.h"
58 #include "MagickCore/monitor.h"
59 #include "MagickCore/monitor-private.h"
60 #include "MagickCore/montage.h"
61 #include "MagickCore/transform.h"
62 #include "MagickCore/memory_.h"
63 #include "MagickCore/memory-private.h"
64 #include "MagickCore/option.h"
65 #include "MagickCore/pixel-accessor.h"
66 #include "MagickCore/quantum-private.h"
67 #include "MagickCore/static.h"
68 #include "MagickCore/string_.h"
69 #include "MagickCore/string-private.h"
70 #include "MagickCore/module.h"
71 #include "MagickCore/utility.h"
72 #if defined(MAGICKCORE_HEIC_DELEGATE)
73 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
76 #include <libheif/heif.h>
81 #if defined(MAGICKCORE_HEIC_DELEGATE)
83 #if !defined(MAGICKCORE_WINDOWS_SUPPORT)
84 static MagickBooleanType
85 WriteHEICImage(const ImageInfo *,Image *,ExceptionInfo *);
89 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93 % R e a d H E I C I m a g e %
97 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
99 % ReadHEICImage retrieves an image via a file descriptor, decodes the image,
100 % and returns it. It allocates the memory necessary for the new Image
101 % structure and returns a pointer to the new image.
103 % The format of the ReadHEICImage method is:
105 % Image *ReadHEICImage(const ImageInfo *image_info,
106 % ExceptionInfo *exception)
108 % A description of each parameter follows:
110 % o image_info: the image info.
112 % o exception: return any errors or warnings in this structure.
115 static MagickBooleanType IsHeifSuccess(struct heif_error *error,Image *image,
116 ExceptionInfo *exception)
118 if (error->code == 0)
121 ThrowBinaryException(CorruptImageError,error->message,image->filename);
124 static Image *ReadHEICImage(const ImageInfo *image_info,
125 ExceptionInfo *exception)
157 struct heif_image_handle
171 assert(image_info != (const ImageInfo *) NULL);
172 assert(image_info->signature == MagickCoreSignature);
173 if (image_info->debug != MagickFalse)
174 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
175 image_info->filename);
176 assert(exception != (ExceptionInfo *) NULL);
177 assert(exception->signature == MagickCoreSignature);
178 image=AcquireImage(image_info,exception);
179 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
180 if (status == MagickFalse)
181 return(DestroyImageList(image));
182 length=GetBlobSize(image);
183 file_data=AcquireMagickMemory(length);
184 if (file_data == (void *) NULL)
185 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
186 if (ReadBlob(image,length,file_data) != (ssize_t) length)
188 file_data=RelinquishMagickMemory(file_data);
189 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
194 heif_context=heif_context_alloc();
195 error=heif_context_read_from_memory(heif_context,file_data,length,NULL);
196 file_data=RelinquishMagickMemory(file_data);
197 if (IsHeifSuccess(&error,image,exception) == MagickFalse)
199 heif_context_free(heif_context);
200 return(DestroyImageList(image));
202 image_handle=(struct heif_image_handle *) NULL;
203 error=heif_context_get_primary_image_handle(heif_context,&image_handle);
204 if (IsHeifSuccess(&error,image,exception) == MagickFalse)
206 heif_context_free(heif_context);
207 return(DestroyImageList(image));
210 Read Exif data from HEIC file
212 count=heif_image_handle_get_list_of_metadata_block_IDs(image_handle,"Exif",
222 exif_size=heif_image_handle_get_metadata_size(image_handle,exif_id);
223 if (exif_size > GetBlobSize(image))
225 heif_image_handle_release(image_handle);
226 heif_context_free(heif_context);
227 ThrowReaderException(CorruptImageError,
228 "InsufficientImageDataInFile");
230 exif_buffer=AcquireMagickMemory(exif_size);
231 error=heif_image_handle_get_metadata(image_handle,exif_id,exif_buffer);
237 profile=BlobToStringInfo(exif_buffer,exif_size);
238 SetImageProfile(image,"exif",profile,exception);
239 profile=DestroyStringInfo(profile);
241 exif_buffer=RelinquishMagickMemory(exif_buffer);
247 image->columns=(size_t) heif_image_handle_get_width(image_handle);
248 image->rows=(size_t) heif_image_handle_get_height(image_handle);
249 if (image_info->ping != MagickFalse)
251 image->colorspace=YCbCrColorspace;
252 heif_image_handle_release(image_handle);
253 heif_context_free(heif_context);
254 return(GetFirstImageInList(image));
256 status=SetImageExtent(image,image->columns,image->rows,exception);
257 if (status == MagickFalse)
259 heif_image_handle_release(image_handle);
260 heif_context_free(heif_context);
261 return(DestroyImageList(image));
264 Copy HEIF image into ImageMagick data structures
266 (void) SetImageColorspace(image,YCbCrColorspace,exception);
267 error=heif_decode_image(image_handle,&heif_image,heif_colorspace_YCbCr,
268 heif_chroma_420,NULL);
269 if (IsHeifSuccess(&error,image,exception) == MagickFalse)
271 heif_image_handle_release(image_handle);
272 heif_context_free(heif_context);
273 return(DestroyImageList(image));
275 p_y=heif_image_get_plane(heif_image,heif_channel_Y,&stride_y);
276 p_cb=heif_image_get_plane(heif_image,heif_channel_Cb,&stride_cb);
277 p_cr=heif_image_get_plane(heif_image,heif_channel_Cr,&stride_cr);
278 for (y=0; y < (ssize_t) image->rows; y++)
286 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
287 if (q == (Quantum *) NULL)
289 for (x=0; x < (long) image->columns; x++)
291 SetPixelRed(image,ScaleCharToQuantum(p_y[y*stride_y + x]),q);
292 SetPixelGreen(image,ScaleCharToQuantum(p_cb[(y/2)*stride_cb + x/2]),q);
293 SetPixelBlue(image,ScaleCharToQuantum(p_cr[(y/2)*stride_cr + x/2]),q);
294 q+=GetPixelChannels(image);
296 if (SyncAuthenticPixels(image,exception) == MagickFalse)
299 heif_image_release(heif_image);
300 heif_image_handle_release(image_handle);
301 heif_context_free(heif_context);
302 return(GetFirstImageInList(image));
307 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
317 % IsHEIC() returns MagickTrue if the image format type, identified by the
318 % magick string, is Heic.
320 % The format of the IsHEIC method is:
322 % MagickBooleanType IsHEIC(const unsigned char *magick,const size_t length)
324 % A description of each parameter follows:
326 % o magick: compare image format pattern against these bytes.
328 % o length: Specifies the length of the magick string.
331 static MagickBooleanType IsHEIC(const unsigned char *magick,const size_t length)
335 if (LocaleNCompare((const char *) magick+8,"heic",4) == 0)
341 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
345 % R e g i s t e r H E I C I m a g e %
349 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
351 % RegisterHEICImage() adds attributes for the HEIC image format to the list of
352 % supported formats. The attributes include the image format tag, a method
353 % to read and/or write the format, whether the format supports the saving of
354 % more than one frame to the same file or blob, whether the format supports
355 % native in-memory I/O, and a brief description of the format.
357 % The format of the RegisterHEICImage method is:
359 % size_t RegisterHEICImage(void)
362 ModuleExport size_t RegisterHEICImage(void)
367 entry=AcquireMagickInfo("HEIC","HEIC","High Efficiency Image Format");
368 #if defined(MAGICKCORE_HEIC_DELEGATE)
369 entry->decoder=(DecodeImageHandler *) ReadHEICImage;
370 #if !defined(MAGICKCORE_WINDOWS_SUPPORT)
371 entry->encoder=(EncodeImageHandler *) WriteHEICImage;
374 entry->magick=(IsImageFormatHandler *) IsHEIC;
375 entry->mime_type=ConstantString("image/x-heic");
376 entry->flags|=CoderDecoderSeekableStreamFlag;
377 entry->flags^=CoderAdjoinFlag;
378 entry->flags^=CoderDecoderThreadSupportFlag;
379 (void) RegisterMagickInfo(entry);
380 return(MagickImageCoderSignature);
384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
388 % U n r e g i s t e r H E I C I m a g e %
392 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
394 % UnregisterHEICImage() removes format registrations made by the HEIC module
395 % from the list of supported formats.
397 % The format of the UnregisterHEICImage method is:
399 % UnregisterHEICImage(void)
402 ModuleExport void UnregisterHEICImage(void)
404 (void) UnregisterMagickInfo("HEIC");
409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
413 % W r i t e H E I C I m a g e %
417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
419 % WriteHEICImage() writes an HEIF image using the libheif library.
421 % The format of the WriteHEICImage method is:
423 % MagickBooleanType WriteHEICImage(const ImageInfo *image_info,
426 % A description of each parameter follows.
428 % o image_info: the image info.
430 % o image: The image.
432 % o exception: return any errors or warnings in this structure.
435 #if defined(MAGICKCORE_HEIC_DELEGATE) && !defined(MAGICKCORE_WINDOWS_SUPPORT)
436 static struct heif_error heif_write_func(struct heif_context *ctx,const void* data,
437 size_t size,void* userdata)
446 image=(Image*) userdata;
447 (void) WriteBlob(image,size,data);
448 error_ok.code=heif_error_Ok;
449 error_ok.subcode=heif_suberror_Unspecified;
450 error_ok.message="ok";
454 static MagickBooleanType WriteHEICImage(const ImageInfo *image_info,Image *image,
455 ExceptionInfo *exception)
477 Open output image file.
479 assert(image_info != (const ImageInfo *) NULL);
480 assert(image_info->signature == MagickCoreSignature);
481 assert(image != (Image *) NULL);
482 assert(image->signature == MagickCoreSignature);
483 if (image->debug != MagickFalse)
484 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
485 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
486 if (status == MagickFalse)
489 heif_context=heif_context_alloc();
490 heif_image=(struct heif_image*) NULL;
491 heif_encoder=(struct heif_encoder*) NULL;
514 Transform colorspace to YCbCr.
516 if (image->colorspace != YCbCrColorspace)
517 status=TransformImageColorspace(image,YCbCrColorspace,exception);
518 if (status == MagickFalse)
521 Initialize HEIF encoder context
523 error=heif_image_create((int) image->columns,(int) image->rows,
524 heif_colorspace_YCbCr,heif_chroma_420,&heif_image);
525 status=IsHeifSuccess(&error,image,exception);
526 if (status == MagickFalse)
528 error=heif_image_add_plane(heif_image,heif_channel_Y,(int) image->columns,
529 (int) image->rows,8);
530 status=IsHeifSuccess(&error,image,exception);
531 if (status == MagickFalse)
533 error=heif_image_add_plane(heif_image,heif_channel_Cb,
534 ((int) image->columns+1)/2,((int) image->rows+1)/2,8);
535 status=IsHeifSuccess(&error,image,exception);
536 if (status == MagickFalse)
538 error=heif_image_add_plane(heif_image,heif_channel_Cr,
539 ((int) image->columns+1)/2,((int) image->rows+1)/2,8);
540 status=IsHeifSuccess(&error,image,exception);
541 if (status == MagickFalse)
543 p_y=heif_image_get_plane(heif_image,heif_channel_Y,&stride_y);
544 p_cb=heif_image_get_plane(heif_image,heif_channel_Cb,&stride_cb);
545 p_cr=heif_image_get_plane(heif_image,heif_channel_Cr,&stride_cr);
547 Copy image to heif_image
549 for (y=0; y < (long) image->rows; y++)
551 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
552 if (p == (const Quantum *) NULL)
559 for (x=0; x < (long) image->columns; x+=2)
561 p_y[y*stride_y+x]=ScaleQuantumToChar(GetPixelRed(image,p));
562 p_cb[y/2*stride_cb+x/2]=ScaleQuantumToChar(GetPixelGreen(image,
564 p_cr[y/2*stride_cr+x/2]=ScaleQuantumToChar(GetPixelBlue(image,
566 p+=GetPixelChannels(image);
568 if (x+1 < image->columns)
570 p_y[y*stride_y + x+1]=ScaleQuantumToChar(GetPixelRed(image,
572 p+=GetPixelChannels(image);
578 for (x=0; x < (long) image->columns; x++)
580 p_y[y*stride_y + x]=ScaleQuantumToChar(GetPixelRed(image,p));
581 p+=GetPixelChannels(image);
584 if (image->previous == (Image *) NULL)
586 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
588 if (status == MagickFalse)
592 if (status == MagickFalse)
595 Code and actually write the HEIC image
597 error=heif_context_get_encoder_for_format(heif_context,
598 heif_compression_HEVC,&heif_encoder);
599 status=IsHeifSuccess(&error,image,exception);
600 if (status == MagickFalse)
602 if (image_info->quality != UndefinedCompressionQuality)
604 error=heif_encoder_set_lossy_quality(heif_encoder,
605 (int) image_info->quality);
606 status=IsHeifSuccess(&error,image,exception);
607 if (status == MagickFalse)
610 error=heif_context_encode_image(heif_context,heif_image,heif_encoder,
611 (const struct heif_encoding_options*) NULL,
612 (struct heif_image_handle**) NULL);
613 status=IsHeifSuccess(&error,image,exception);
614 if (status == MagickFalse)
616 writer.writer_api_version=1;
617 writer.write=heif_write_func;
618 error=heif_context_write(heif_context,&writer,image);
619 status=IsHeifSuccess(&error,image,exception);
620 if (status == MagickFalse)
622 if (GetNextImageInList(image) == (Image *) NULL)
624 image=SyncNextImageInList(image);
625 status=SetImageProgress(image,SaveImagesTag,scene,
626 GetImageListLength(image));
627 if (status == MagickFalse)
629 heif_encoder_release(heif_encoder);
630 heif_encoder=(struct heif_encoder*) NULL;
631 heif_image_release(heif_image);
632 heif_image=(struct heif_image*) NULL;
634 } while (image_info->adjoin != MagickFalse);
636 if (heif_encoder != (struct heif_encoder*) NULL)
637 heif_encoder_release(heif_encoder);
638 if (heif_image != (struct heif_image*) NULL)
639 heif_image_release(heif_image);
640 heif_context_free(heif_context);
642 (void) CloseBlob(image);