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