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