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