Home | History | Annotate | Download | only in coders
      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-2019 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 
     85 #if defined(MAGICKCORE_HEIC_DELEGATE)
     86 
     87 /*
     88   Const declarations.
     89 */
     90 static const char *xmp_namespace = "http://ns.adobe.com/xap/1.0/ ";
     91 #define XmpNamespaceExtent 28
     92 
     93 /*
     95   Forward declarations.
     96 */
     97 
     98 #if !defined(MAGICKCORE_WINDOWS_SUPPORT)
     99 static MagickBooleanType
    100   WriteHEICImage(const ImageInfo *,Image *,ExceptionInfo *);
    101 #endif
    102 
    103 /*x
    105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    106 %                                                                             %
    107 %                                                                             %
    108 %                                                                             %
    109 %   R e a d H E I C I m a g e                                                 %
    110 %                                                                             %
    111 %                                                                             %
    112 %                                                                             %
    113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    114 %
    115 %  ReadHEICImage retrieves an image via a file descriptor, decodes the image,
    116 %  and returns it.  It allocates the memory necessary for the new Image
    117 %  structure and returns a pointer to the new image.
    118 %
    119 %  The format of the ReadHEICImage method is:
    120 %
    121 %      Image *ReadHEICImage(const ImageInfo *image_info,
    122 %        ExceptionInfo *exception)
    123 %
    124 %  A description of each parameter follows:
    125 %
    126 %    o image_info: the image info.
    127 %
    128 %    o exception: return any errors or warnings in this structure.
    129 %
    130 */
    131 static MagickBooleanType IsHeifSuccess(struct heif_error *error,Image *image,
    132   ExceptionInfo *exception)
    133 {
    134   if (error->code == 0)
    135     return(MagickTrue);
    136 
    137   ThrowBinaryException(CorruptImageError,error->message,image->filename);
    138 }
    139 
    140 static Image *ReadHEICImage(const ImageInfo *image_info,
    141   ExceptionInfo *exception)
    142 {
    143   heif_item_id
    144     exif_id;
    145 
    146   Image
    147     *image;
    148 
    149   int
    150     count,
    151     stride_y,
    152     stride_cb,
    153     stride_cr;
    154 
    155   MagickBooleanType
    156     status;
    157 
    158   MagickSizeType
    159     length;
    160 
    161   ssize_t
    162     y;
    163 
    164   struct heif_context
    165     *heif_context;
    166 
    167   struct heif_error
    168     error;
    169 
    170   struct heif_image
    171     *heif_image;
    172 
    173   struct heif_image_handle
    174     *image_handle;
    175 
    176   struct heif_decoding_options
    177     *decode_options;
    178 
    179   uint8_t
    180     *p_y,
    181     *p_cb,
    182     *p_cr;
    183 
    184   void
    185     *file_data;
    186 
    187   const char
    188     *option;
    189 
    190   /*
    191     Open image file.
    192   */
    193   assert(image_info != (const ImageInfo *) NULL);
    194   assert(image_info->signature == MagickCoreSignature);
    195   if (image_info->debug != MagickFalse)
    196     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
    197       image_info->filename);
    198   assert(exception != (ExceptionInfo *) NULL);
    199   assert(exception->signature == MagickCoreSignature);
    200   image=AcquireImage(image_info,exception);
    201   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
    202   if (status == MagickFalse)
    203     return(DestroyImageList(image));
    204   length=GetBlobSize(image);
    205   file_data=AcquireMagickMemory(length);
    206   if (file_data == (void *) NULL)
    207     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    208   if (ReadBlob(image,length,file_data) != (ssize_t) length)
    209     {
    210       file_data=RelinquishMagickMemory(file_data);
    211       ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
    212     }
    213   /*
    214     Decode HEIF file
    215   */
    216   heif_context=heif_context_alloc();
    217   error=heif_context_read_from_memory(heif_context,file_data,length,NULL);
    218   file_data=RelinquishMagickMemory(file_data);
    219   if (IsHeifSuccess(&error,image,exception) == MagickFalse)
    220     {
    221       heif_context_free(heif_context);
    222       return(DestroyImageList(image));
    223     }
    224   image_handle=(struct heif_image_handle *) NULL;
    225   error=heif_context_get_primary_image_handle(heif_context,&image_handle);
    226   if (IsHeifSuccess(&error,image,exception) == MagickFalse)
    227     {
    228       heif_context_free(heif_context);
    229       return(DestroyImageList(image));
    230     }
    231   /*
    232     Read Exif data from HEIC file
    233   */
    234   count=heif_image_handle_get_list_of_metadata_block_IDs(image_handle,"Exif",
    235     &exif_id,1);
    236   if (count > 0)
    237     {
    238       size_t
    239         exif_size;
    240 
    241       unsigned char
    242         *exif_buffer;
    243 
    244       exif_size=heif_image_handle_get_metadata_size(image_handle,exif_id);
    245       if (exif_size > GetBlobSize(image))
    246         {
    247           heif_image_handle_release(image_handle);
    248           heif_context_free(heif_context);
    249           ThrowReaderException(CorruptImageError,
    250             "InsufficientImageDataInFile");
    251         }
    252       exif_buffer=(unsigned char *) AcquireMagickMemory(exif_size);
    253       if (exif_buffer !=(unsigned char *) NULL)
    254         {
    255           error=heif_image_handle_get_metadata(image_handle,
    256             exif_id,exif_buffer);
    257           if (error.code == 0)
    258             {
    259               StringInfo
    260                 *profile;
    261 
    262               // The first 4 byte should be skipped since they indicate the
    263               // offset to the start of the TIFF header of the Exif data.
    264               profile=(StringInfo*) NULL;
    265               if (exif_size > 8)
    266                 profile=BlobToStringInfo(exif_buffer+4,exif_size-4);
    267               if (profile != (StringInfo*) NULL)
    268                 {
    269                   SetImageProfile(image,"exif",profile,exception);
    270                   profile=DestroyStringInfo(profile);
    271                 }
    272             }
    273         }
    274       exif_buffer=RelinquishMagickMemory(exif_buffer);
    275   }
    276   /*
    277     Set image size
    278    */
    279   image->depth=8;
    280   image->columns=(size_t) heif_image_handle_get_width(image_handle);
    281   image->rows=(size_t) heif_image_handle_get_height(image_handle);
    282   if (image_info->ping != MagickFalse)
    283     {
    284       image->colorspace=YCbCrColorspace;
    285       heif_image_handle_release(image_handle);
    286       heif_context_free(heif_context);
    287       return(GetFirstImageInList(image));
    288     }
    289   status=SetImageExtent(image,image->columns,image->rows,exception);
    290   if (status == MagickFalse)
    291     {
    292       heif_image_handle_release(image_handle);
    293       heif_context_free(heif_context);
    294       return(DestroyImageList(image));
    295     }
    296   /*
    297     Copy HEIF image into ImageMagick data structures
    298   */
    299   (void) SetImageColorspace(image,YCbCrColorspace,exception);
    300   decode_options=(struct heif_decoding_options *) NULL;
    301   option=GetImageOption(image_info,"heic:preserve-orientation");
    302   if (IsStringTrue(option) == MagickTrue)
    303     {
    304       decode_options=heif_decoding_options_alloc();
    305       decode_options->ignore_transformations=1;
    306     }
    307   else
    308     SetImageProperty(image,"exif:Orientation","1",exception);
    309   error=heif_decode_image(image_handle,&heif_image,heif_colorspace_YCbCr,
    310     heif_chroma_420,decode_options);
    311   if (decode_options != (struct heif_decoding_options *) NULL)
    312     {
    313       /* Correct the width and height of the image */
    314       image->columns=(size_t) heif_image_get_width(heif_image,heif_channel_Y);
    315       image->rows=(size_t) heif_image_get_height(heif_image,heif_channel_Y);
    316       status=SetImageExtent(image,image->columns,image->rows,exception);
    317       heif_decoding_options_free(decode_options);
    318     }
    319   if ((IsHeifSuccess(&error,image,exception) == MagickFalse) ||
    320       (status == MagickFalse))
    321     {
    322       heif_image_handle_release(image_handle);
    323       heif_context_free(heif_context);
    324       return(DestroyImageList(image));
    325     }
    326   p_y=heif_image_get_plane(heif_image,heif_channel_Y,&stride_y);
    327   p_cb=heif_image_get_plane(heif_image,heif_channel_Cb,&stride_cb);
    328   p_cr=heif_image_get_plane(heif_image,heif_channel_Cr,&stride_cr);
    329   for (y=0; y < (ssize_t) image->rows; y++)
    330   {
    331     Quantum
    332       *q;
    333 
    334     register ssize_t
    335       x;
    336 
    337     q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
    338     if (q == (Quantum *) NULL)
    339       break;
    340     for (x=0; x < (long) image->columns; x++)
    341     {
    342       SetPixelRed(image,ScaleCharToQuantum(p_y[y*stride_y + x]),q);
    343       SetPixelGreen(image,ScaleCharToQuantum(p_cb[(y/2)*stride_cb + x/2]),q);
    344       SetPixelBlue(image,ScaleCharToQuantum(p_cr[(y/2)*stride_cr + x/2]),q);
    345       q+=GetPixelChannels(image);
    346     }
    347     if (SyncAuthenticPixels(image,exception) == MagickFalse)
    348       break;
    349   }
    350   heif_image_release(heif_image);
    351   heif_image_handle_release(image_handle);
    352   heif_context_free(heif_context);
    353   return(GetFirstImageInList(image));
    354 }
    355 #endif
    356 
    357 /*
    358 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    359 %                                                                             %
    360 %                                                                             %
    361 %                                                                             %
    362 %   I s H E I C                                                               %
    363 %                                                                             %
    364 %                                                                             %
    365 %                                                                             %
    366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    367 %
    368 %  IsHEIC() returns MagickTrue if the image format type, identified by the
    369 %  magick string, is Heic.
    370 %
    371 %  The format of the IsHEIC method is:
    372 %
    373 %      MagickBooleanType IsHEIC(const unsigned char *magick,const size_t length)
    374 %
    375 %  A description of each parameter follows:
    376 %
    377 %    o magick: compare image format pattern against these bytes.
    378 %
    379 %    o length: Specifies the length of the magick string.
    380 %
    381 */
    382 static MagickBooleanType IsHEIC(const unsigned char *magick,const size_t length)
    383 {
    384   if (length < 12)
    385     return(MagickFalse);
    386   if (LocaleNCompare((const char *) magick+4,"ftyp",4) != 0)
    387   return(MagickFalse);
    388   if (LocaleNCompare((const char *) magick+8,"heic",4) == 0)
    389     return(MagickTrue);
    390   if (LocaleNCompare((const char *) magick+8,"heix",4) == 0)
    391     return(MagickTrue);
    392   if (LocaleNCompare((const char *) magick+8,"mif1",4) == 0)
    393     return(MagickTrue);
    394   return(MagickFalse);
    395 }
    396 
    397 /*
    398 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    399 %                                                                             %
    400 %                                                                             %
    401 %                                                                             %
    402 %   R e g i s t e r H E I C I m a g e                                         %
    403 %                                                                             %
    404 %                                                                             %
    405 %                                                                             %
    406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    407 %
    408 %  RegisterHEICImage() adds attributes for the HEIC image format to the list of
    409 %  supported formats.  The attributes include the image format tag, a method
    410 %  to read and/or write the format, whether the format supports the saving of
    411 %  more than one frame to the same file or blob, whether the format supports
    412 %  native in-memory I/O, and a brief description of the format.
    413 %
    414 %  The format of the RegisterHEICImage method is:
    415 %
    416 %      size_t RegisterHEICImage(void)
    417 %
    418 */
    419 ModuleExport size_t RegisterHEICImage(void)
    420 {
    421   MagickInfo
    422     *entry;
    423 
    424   entry=AcquireMagickInfo("HEIC","HEIC","High Efficiency Image Format");
    425 #if defined(MAGICKCORE_HEIC_DELEGATE)
    426   entry->decoder=(DecodeImageHandler *) ReadHEICImage;
    427 #if !defined(MAGICKCORE_WINDOWS_SUPPORT)
    428   entry->encoder=(EncodeImageHandler *) WriteHEICImage;
    429 #endif
    430 #endif
    431   entry->magick=(IsImageFormatHandler *) IsHEIC;
    432   entry->mime_type=ConstantString("image/x-heic");
    433 #if defined(LIBHEIF_VERSION)
    434   entry->version=ConstantString(LIBHEIF_VERSION);
    435 #endif
    436   entry->flags|=CoderDecoderSeekableStreamFlag;
    437   entry->flags^=CoderAdjoinFlag;
    438   (void) RegisterMagickInfo(entry);
    439   return(MagickImageCoderSignature);
    440 }
    441 
    442 /*
    443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    444 %                                                                             %
    445 %                                                                             %
    446 %                                                                             %
    447 %   U n r e g i s t e r H E I C I m a g e                                     %
    448 %                                                                             %
    449 %                                                                             %
    450 %                                                                             %
    451 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    452 %
    453 %  UnregisterHEICImage() removes format registrations made by the HEIC module
    454 %  from the list of supported formats.
    455 %
    456 %  The format of the UnregisterHEICImage method is:
    457 %
    458 %      UnregisterHEICImage(void)
    459 %
    460 */
    461 ModuleExport void UnregisterHEICImage(void)
    462 {
    463   (void) UnregisterMagickInfo("HEIC");
    464 }
    465 
    466 /*
    467 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    468 %                                                                             %
    469 %                                                                             %
    470 %                                                                             %
    471 %   W r i t e H E I C I m a g e                                               %
    472 %                                                                             %
    473 %                                                                             %
    474 %                                                                             %
    475 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    476 %
    477 %  WriteHEICImage() writes an HEIF image using the libheif library.
    478 %
    479 %  The format of the WriteHEICImage method is:
    480 %
    481 %      MagickBooleanType WriteHEICImage(const ImageInfo *image_info,
    482 %        Image *image)
    483 %
    484 %  A description of each parameter follows.
    485 %
    486 %    o image_info: the image info.
    487 %
    488 %    o image:  The image.
    489 %
    490 %    o exception:  return any errors or warnings in this structure.
    491 %
    492 */
    493 #if defined(MAGICKCORE_HEIC_DELEGATE) && !defined(MAGICKCORE_WINDOWS_SUPPORT)
    494 static void WriteProfile(struct heif_context* ctx,Image *image,
    495   ExceptionInfo *exception)
    496 {
    497   const char
    498     *name;
    499 
    500   const StringInfo
    501     *profile;
    502 
    503   MagickBooleanType
    504     iptc;
    505 
    506   register ssize_t
    507     i;
    508 
    509   size_t
    510     length;
    511 
    512   StringInfo
    513     *custom_profile;
    514 
    515   struct heif_error
    516     error;
    517 
    518   struct heif_image_handle
    519     *image_handle;
    520 
    521   /*Get image handle*/
    522   image_handle=(struct heif_image_handle *) NULL;
    523   error=heif_context_get_primary_image_handle(ctx,&image_handle);
    524   if (error.code != 0)
    525     return;
    526 
    527   /*
    528     Save image profile as a APP marker.
    529   */
    530   iptc=MagickFalse;
    531   custom_profile=AcquireStringInfo(65535L);
    532   ResetImageProfileIterator(image);
    533   for (name=GetNextImageProfile(image); name != (const char *) NULL; )
    534   {
    535     profile=GetImageProfile(image,name);
    536     length=GetStringInfoLength(profile);
    537 
    538     if (LocaleCompare(name,"EXIF") == 0)
    539       {
    540         length=GetStringInfoLength(profile);
    541         if (length > 65533L)
    542           {
    543             (void) ThrowMagickException(exception,GetMagickModule(),
    544               CoderWarning,"ExifProfileSizeExceedsLimit","`%s'",
    545               image->filename);
    546             length=65533L;
    547           }
    548           (void) heif_context_add_exif_metadata(ctx,image_handle,
    549             (void*) GetStringInfoDatum(profile),length);
    550       }
    551 
    552     if (LocaleCompare(name,"XMP") == 0)
    553       {
    554         StringInfo
    555           *xmp_profile;
    556 
    557         xmp_profile=StringToStringInfo(xmp_namespace);
    558         if (xmp_profile != (StringInfo *) NULL)
    559           {
    560             if (profile != (StringInfo *) NULL)
    561               ConcatenateStringInfo(xmp_profile,profile);
    562             GetStringInfoDatum(xmp_profile)[XmpNamespaceExtent]='\0';
    563             for (i=0; i < (ssize_t) GetStringInfoLength(xmp_profile); i+=65533L)
    564             {
    565               length=MagickMin(GetStringInfoLength(xmp_profile)-i,65533L);
    566               error=heif_context_add_XMP_metadata(ctx,image_handle,
    567                 (void*) (GetStringInfoDatum(xmp_profile)+i),length);
    568               if (error.code != 0)
    569                 break;
    570             }
    571             xmp_profile=DestroyStringInfo(xmp_profile);
    572           }
    573       }
    574     if (image->debug != MagickFalse)
    575       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
    576         "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
    577     name=GetNextImageProfile(image);
    578   }
    579   custom_profile=DestroyStringInfo(custom_profile);
    580   heif_image_handle_release(image_handle);
    581 }
    582 
    583 static struct heif_error heif_write_func(struct heif_context *ctx,const void* data,
    584   size_t size,void* userdata)
    585 {
    586   Image
    587     *image;
    588 
    589   struct heif_error
    590     error_ok;
    591 
    592   (void) ctx;
    593   image=(Image*) userdata;
    594   (void) WriteBlob(image,size,data);
    595   error_ok.code=heif_error_Ok;
    596   error_ok.subcode=heif_suberror_Unspecified;
    597   error_ok.message="ok";
    598   return(error_ok);
    599 }
    600 
    601 static MagickBooleanType WriteHEICImage(const ImageInfo *image_info,Image *image,
    602   ExceptionInfo *exception)
    603 {
    604   long
    605     x,
    606     y;
    607 
    608   MagickBooleanType
    609     status;
    610 
    611   MagickOffsetType
    612     scene;
    613 
    614   struct heif_context
    615     *heif_context;
    616 
    617   struct heif_encoder
    618     *heif_encoder;
    619 
    620   struct heif_image
    621     *heif_image;
    622 
    623   /*
    624     Open output image file.
    625   */
    626   assert(image_info != (const ImageInfo *) NULL);
    627   assert(image_info->signature == MagickCoreSignature);
    628   assert(image != (Image *) NULL);
    629   assert(image->signature == MagickCoreSignature);
    630   if (image->debug != MagickFalse)
    631     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    632   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
    633   if (status == MagickFalse)
    634     return(status);
    635   scene=0;
    636   heif_context=heif_context_alloc();
    637   heif_image=(struct heif_image*) NULL;
    638   heif_encoder=(struct heif_encoder*) NULL;
    639   do
    640   {
    641     const Quantum
    642       *p;
    643 
    644     int
    645       stride_y,
    646       stride_cb,
    647       stride_cr;
    648 
    649     struct heif_error
    650       error;
    651 
    652     struct heif_writer
    653       writer;
    654 
    655     uint8_t
    656       *p_y,
    657       *p_cb,
    658       *p_cr;
    659 
    660     /*
    661       Transform colorspace to YCbCr.
    662     */
    663     if (image->colorspace != YCbCrColorspace)
    664       status=TransformImageColorspace(image,YCbCrColorspace,exception);
    665     if (status == MagickFalse)
    666       break;
    667     /*
    668       Initialize HEIF encoder context
    669     */
    670     error=heif_image_create((int) image->columns,(int) image->rows,
    671       heif_colorspace_YCbCr,heif_chroma_420,&heif_image);
    672     status=IsHeifSuccess(&error,image,exception);
    673     if (status == MagickFalse)
    674       break;
    675     error=heif_image_add_plane(heif_image,heif_channel_Y,(int) image->columns,
    676       (int) image->rows,8);
    677     status=IsHeifSuccess(&error,image,exception);
    678     if (status == MagickFalse)
    679       break;
    680     error=heif_image_add_plane(heif_image,heif_channel_Cb,
    681       ((int) image->columns+1)/2,((int) image->rows+1)/2,8);
    682     status=IsHeifSuccess(&error,image,exception);
    683     if (status == MagickFalse)
    684       break;
    685     error=heif_image_add_plane(heif_image,heif_channel_Cr,
    686       ((int) image->columns+1)/2,((int) image->rows+1)/2,8);
    687     status=IsHeifSuccess(&error,image,exception);
    688     if (status == MagickFalse)
    689       break;
    690     p_y=heif_image_get_plane(heif_image,heif_channel_Y,&stride_y);
    691     p_cb=heif_image_get_plane(heif_image,heif_channel_Cb,&stride_cb);
    692     p_cr=heif_image_get_plane(heif_image,heif_channel_Cr,&stride_cr);
    693     /*
    694       Copy image to heif_image
    695     */
    696     for (y=0; y < (long) image->rows; y++)
    697     {
    698       p=GetVirtualPixels(image,0,y,image->columns,1,exception);
    699       if (p == (const Quantum *) NULL)
    700         {
    701           status=MagickFalse;
    702           break;
    703         }
    704       if ((y & 1)==0)
    705         {
    706           for (x=0; x < (long) image->columns; x+=2)
    707             {
    708               p_y[y*stride_y+x]=ScaleQuantumToChar(GetPixelRed(image,p));
    709               p_cb[y/2*stride_cb+x/2]=ScaleQuantumToChar(GetPixelGreen(image,
    710                 p));
    711               p_cr[y/2*stride_cr+x/2]=ScaleQuantumToChar(GetPixelBlue(image,
    712                 p));
    713               p+=GetPixelChannels(image);
    714 
    715               if (x+1 < (long) image->columns)
    716                 {
    717                   p_y[y*stride_y + x+1]=ScaleQuantumToChar(GetPixelRed(image,
    718                     p));
    719                   p+=GetPixelChannels(image);
    720                 }
    721             }
    722         }
    723       else
    724         {
    725           for (x=0; x < (long) image->columns; x++)
    726           {
    727             p_y[y*stride_y + x]=ScaleQuantumToChar(GetPixelRed(image,p));
    728             p+=GetPixelChannels(image);
    729           }
    730         }
    731       if (image->previous == (Image *) NULL)
    732         {
    733           status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
    734             image->rows);
    735           if (status == MagickFalse)
    736             break;
    737         }
    738     }
    739     if (status == MagickFalse)
    740       break;
    741     /*
    742       Code and actually write the HEIC image
    743     */
    744     error=heif_context_get_encoder_for_format(heif_context,
    745       heif_compression_HEVC,&heif_encoder);
    746     status=IsHeifSuccess(&error,image,exception);
    747     if (status == MagickFalse)
    748       break;
    749     if (image_info->quality != UndefinedCompressionQuality)
    750       {
    751         error=heif_encoder_set_lossy_quality(heif_encoder,
    752           (int) image_info->quality);
    753         status=IsHeifSuccess(&error,image,exception);
    754         if (status == MagickFalse)
    755           break;
    756       }
    757     error=heif_context_encode_image(heif_context,heif_image,heif_encoder,
    758       (const struct heif_encoding_options*) NULL,
    759       (struct heif_image_handle**) NULL);
    760     status=IsHeifSuccess(&error,image,exception);
    761     if (status == MagickFalse)
    762       break;
    763     writer.writer_api_version=1;
    764     writer.write=heif_write_func;
    765 
    766   	if (image->profiles != (void *) NULL)
    767     	WriteProfile(heif_context, image, exception);
    768 
    769     error=heif_context_write(heif_context,&writer,image);
    770     status=IsHeifSuccess(&error,image,exception);
    771     if (status == MagickFalse)
    772       break;
    773     if (GetNextImageInList(image) == (Image *) NULL)
    774         break;
    775     image=SyncNextImageInList(image);
    776     status=SetImageProgress(image,SaveImagesTag,scene,
    777       GetImageListLength(image));
    778     if (status == MagickFalse)
    779       break;
    780     heif_encoder_release(heif_encoder);
    781     heif_encoder=(struct heif_encoder*) NULL;
    782     heif_image_release(heif_image);
    783     heif_image=(struct heif_image*) NULL;
    784     scene++;
    785   } while (image_info->adjoin != MagickFalse);
    786 
    787   if (heif_encoder != (struct heif_encoder*) NULL)
    788     heif_encoder_release(heif_encoder);
    789   if (heif_image != (struct heif_image*) NULL)
    790     heif_image_release(heif_image);
    791   heif_context_free(heif_context);
    792 
    793   (void) CloseBlob(image);
    794   return(status);
    795 }
    796 #endif
    797