1/* 2 * GO interface to libheif 3 * Copyright (c) 2018 struktur AG, Dirk Farin <farin@struktur.de> 4 * 5 * This file is part of heif, an example application using libheif. 6 * 7 * heif is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version. 11 * 12 * heif is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with heif. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21package heic 22 23/* 24#cgo pkg-config: libheif 25#include <stdlib.h> 26#include <string.h> // We use 'memcpy' 27#include <libheif/heif.h> 28*/ 29import "C" 30 31import ( 32 "fmt" 33 "image" 34 "image/color" 35 "io" 36 "io/ioutil" 37 "runtime" 38 "unsafe" 39) 40 41func GetVersion() string { 42 return C.GoString(C.heif_get_version()) 43} 44 45type Compression C.enum_heif_compression_format 46 47const ( 48 CompressionUndefined = C.heif_compression_undefined 49 CompressionHEVC = C.heif_compression_HEVC 50 CompressionAV1 = C.heif_compression_AV1 51 CompressionAVC = C.heif_compression_AVC 52 CompressionJPEG = C.heif_compression_JPEG 53) 54 55type Chroma C.enum_heif_chroma 56 57const ( 58 ChromaUndefined = C.heif_chroma_undefined 59 ChromaMonochrome = C.heif_chroma_monochrome 60 Chroma420 = C.heif_chroma_420 61 Chroma422 = C.heif_chroma_422 62 Chroma444 = C.heif_chroma_444 63 ChromaInterleavedRGB = C.heif_chroma_interleaved_RGB 64 ChromaInterleavedRGBA = C.heif_chroma_interleaved_RGBA 65 ChromaInterleavedRRGGBBAA_BE = C.heif_chroma_interleaved_RRGGBBAA_BE 66 ChromaInterleavedRRGGBBAA_LE = C.heif_chroma_interleaved_RRGGBBAA_LE 67 ChromaInterleavedRRGGBB_BE = C.heif_chroma_interleaved_RRGGBB_BE 68 ChromaInterleavedRRGGBB_LE = C.heif_chroma_interleaved_RRGGBB_LE 69 70 ChromaInterleaved24Bit = C.heif_chroma_interleaved_24bit 71 ChromaInterleaved32Bit = C.heif_chroma_interleaved_32bit 72) 73 74type Colorspace C.enum_heif_colorspace 75 76const ( 77 ColorspaceUndefined = C.heif_colorspace_undefined 78 ColorspaceYCbCr = C.heif_colorspace_YCbCr 79 ColorspaceRGB = C.heif_colorspace_RGB 80 ColorspaceMonochrome = C.heif_colorspace_monochrome 81) 82 83type Channel C.enum_heif_channel 84 85const ( 86 ChannelY = C.heif_channel_Y 87 ChannelCb = C.heif_channel_Cb 88 ChannelCr = C.heif_channel_Cr 89 ChannelR = C.heif_channel_R 90 ChannelG = C.heif_channel_G 91 ChannelB = C.heif_channel_B 92 ChannelAlpha = C.heif_channel_Alpha 93 ChannelInterleaved = C.heif_channel_interleaved 94) 95 96type ProgressStep C.enum_heif_progress_step 97 98const ( 99 ProgressStepTotal = C.heif_progress_step_total 100 ProgressStepLoadTile = C.heif_progress_step_load_tile 101) 102 103type LosslessMode int 104 105const ( 106 LosslessModeDisabled LosslessMode = iota 107 LosslessModeEnabled 108) 109 110type LoggingLevel int 111 112const ( 113 LoggingLevelNone LoggingLevel = iota 114 LoggingLevelBasic 115 LoggingLevelAdvanced 116 LoggingLevelFull 117) 118 119// --- HeifError 120 121type ErrorCode C.enum_heif_error_code 122 123const ( 124 ErrorOK = C.heif_error_Ok 125 126 // Input file does not exist. 127 ErrorInputDoesNotExist = C.heif_error_Input_does_not_exist 128 129 // Error in input file. Corrupted or invalid content. 130 errorInvalidInput = C.heif_error_Invalid_input 131 132 // Input file type is not supported. 133 ErrorUnsupportedFiletype = C.heif_error_Unsupported_filetype 134 135 // Image requires an unsupported decoder feature. 136 ErrorUnsupportedFeature = C.heif_error_Unsupported_feature 137 138 // Library API has been used in an invalid way. 139 ErrorUsage = C.heif_error_Usage_error 140 141 // Could not allocate enough memory. 142 ErrorMemoryAllocation = C.heif_error_Memory_allocation_error 143 144 // The decoder plugin generated an error 145 ErrorDecoderPlugin = C.heif_error_Decoder_plugin_error 146 147 // The decoder plugin generated an error 148 ErrorEncoderPlugin = C.heif_error_Encoder_plugin_error 149 150 // Error during encoding or when writing to the output 151 ErrorEncoding = C.heif_error_Encoding_error 152 153 // Application has asked for a color profile type that does not exist 154 ErrorColorProfileDoesNotExist = C.heif_error_Color_profile_does_not_exist 155) 156 157type ErrorSubcode C.enum_heif_suberror_code 158 159const ( 160 // no further information available 161 SuberrorUnspecified = C.heif_suberror_Unspecified 162 163 // --- Invalid_input --- 164 165 // End of data reached unexpectedly. 166 SuberrorEndOfData = C.heif_suberror_End_of_data 167 168 // Size of box (defined in header) is wrong 169 SuberrorInvalidBoxSize = C.heif_suberror_Invalid_box_size 170 171 // Mandatory 'ftyp' box is missing 172 SuberrorNoFtypBox = C.heif_suberror_No_ftyp_box 173 174 SuberrorNoIdatBox = C.heif_suberror_No_idat_box 175 176 SuberrorNoMetaBox = C.heif_suberror_No_meta_box 177 178 SuberrorNoHdlrBox = C.heif_suberror_No_hdlr_box 179 180 SuberrorNoHvcCBox = C.heif_suberror_No_hvcC_box 181 182 SuberrorNoPitmBox = C.heif_suberror_No_pitm_box 183 184 SuberrorNoIpcoBox = C.heif_suberror_No_ipco_box 185 186 SuberrorNoIpmaBox = C.heif_suberror_No_ipma_box 187 188 SuberrorNoIlocBox = C.heif_suberror_No_iloc_box 189 190 SuberrorNoIinfBox = C.heif_suberror_No_iinf_box 191 192 SuberrorNoIprpBox = C.heif_suberror_No_iprp_box 193 194 SuberrorNoIrefBox = C.heif_suberror_No_iref_box 195 196 SuberrorNoPictHandler = C.heif_suberror_No_pict_handler 197 198 // An item property referenced in the 'ipma' box is not existing in the 'ipco' container. 199 SuberrorIpmaBoxReferencesNonexistingProperty = C.heif_suberror_Ipma_box_references_nonexisting_property 200 201 // No properties have been assigned to an item. 202 SuberrorNoPropertiesAssignedToItem = C.heif_suberror_No_properties_assigned_to_item 203 204 // Image has no (compressed) data 205 SuberrorNoItemData = C.heif_suberror_No_item_data 206 207 // Invalid specification of image grid (tiled image) 208 SuberrorInvalidGridData = C.heif_suberror_Invalid_grid_data 209 210 // Tile-images in a grid image are missing 211 SuberrorMissingGridImages = C.heif_suberror_Missing_grid_images 212 213 SuberrorNoAV1CBox = C.heif_suberror_No_av1C_box 214 215 SuberrorInvalidCleanAperture = C.heif_suberror_Invalid_clean_aperture 216 217 // Invalid specification of overlay image 218 SuberrorInvalidOverlayData = C.heif_suberror_Invalid_overlay_data 219 220 // Overlay image completely outside of visible canvas area 221 SuberrorOverlayImageOutsideOfCanvas = C.heif_suberror_Overlay_image_outside_of_canvas 222 223 SuberrorAuxiliaryImageTypeUnspecified = C.heif_suberror_Auxiliary_image_type_unspecified 224 225 SuberrorNoOrInvalidPrimaryItem = C.heif_suberror_No_or_invalid_primary_item 226 227 SuberrorNoInfeBox = C.heif_suberror_No_infe_box 228 229 SuberrorUnknownColorProfileType = C.heif_suberror_Unknown_color_profile_type 230 231 SuberrorWrongTileImageChromaFormat = C.heif_suberror_Wrong_tile_image_chroma_format 232 233 SuberrorInvalidFractionalNumber = C.heif_suberror_Invalid_fractional_number 234 235 SuberrorInvalidImageSize = C.heif_suberror_Invalid_image_size 236 237 // --- Memory_allocation_error --- 238 239 // A security limit preventing unreasonable memory allocations was exceeded by the input file. 240 // Please check whether the file is valid. If it is, contact us so that we could increase the 241 // security limits further. 242 SuberrorSecurityLimitExceeded = C.heif_suberror_Security_limit_exceeded 243 244 // --- Usage_error --- 245 246 // An item ID was used that is not present in the file. 247 SuberrorNonexistingItemReferenced = C.heif_suberror_Nonexisting_item_referenced // also used for Invalid_input 248 249 // An API argument was given a NULL pointer, which is not allowed for that function. 250 SuberrorNullPointerArgument = C.heif_suberror_Null_pointer_argument 251 252 // Image channel referenced that does not exist in the image 253 SuberrorNonexistingImageChannelReferenced = C.heif_suberror_Nonexisting_image_channel_referenced 254 255 // The version of the passed plugin is not supported. 256 SuberrorUnsupportedPluginVersion = C.heif_suberror_Unsupported_plugin_version 257 258 // The version of the passed writer is not supported. 259 SuberrorUnsupportedWriterVersion = C.heif_suberror_Unsupported_writer_version 260 261 // The given (encoder) parameter name does not exist. 262 SuberrorUnsupportedParameter = C.heif_suberror_Unsupported_parameter 263 264 // The value for the given parameter is not in the valid range. 265 SuberrorInvalidParameterValue = C.heif_suberror_Invalid_parameter_value 266 267 SuberrorInvalidPixiBox = C.heif_suberror_Invalid_pixi_box 268 269 // --- Unsupported_feature --- 270 271 // Image was coded with an unsupported compression method. 272 SuberrorUnsupportedCodec = C.heif_suberror_Unsupported_codec 273 274 // Image is specified in an unknown way, e.g. as tiled grid image (which is supported) 275 SuberrorUnsupportedImageType = C.heif_suberror_Unsupported_image_type 276 277 SuberrorUnsupportedDataVersion = C.heif_suberror_Unsupported_data_version 278 279 // The conversion of the source image to the requested chroma / colorspace is not supported. 280 SuberrorUnsupportedColorConversion = C.heif_suberror_Unsupported_color_conversion 281 282 SuberrorUnsupportedItemConstructionMethod = C.heif_suberror_Unsupported_item_construction_method 283 284 // --- Encoder_plugin_error --- 285 286 SuberrorUnsupportedBitDepth = C.heif_suberror_Unsupported_bit_depth 287 288 // --- Encoding_error --- 289 290 SuberrorCannotWriteOutputData = C.heif_suberror_Cannot_write_output_data 291) 292 293type HeifError struct { 294 Code ErrorCode 295 Subcode ErrorSubcode 296 Message string 297} 298 299func (e *HeifError) Error() string { 300 return e.Message 301} 302 303func convertHeifError(cerror C.struct_heif_error) error { 304 if cerror.code == ErrorOK { 305 return nil 306 } 307 308 return &HeifError{ 309 Code: ErrorCode(cerror.code), 310 Subcode: ErrorSubcode(cerror.subcode), 311 Message: C.GoString(cerror.message), 312 } 313} 314 315func convertItemIDs(ids []C.heif_item_id, count int) []int { 316 result := make([]int, count) 317 for i := 0; i < count; i++ { 318 result[i] = int(ids[i]) 319 } 320 return result 321} 322 323// --- Context 324 325type Context struct { 326 context *C.struct_heif_context 327} 328 329func NewContext() (*Context, error) { 330 ctx := &Context{ 331 context: C.heif_context_alloc(), 332 } 333 if ctx.context == nil { 334 return nil, fmt.Errorf("Could not allocate context") 335 } 336 337 runtime.SetFinalizer(ctx, freeHeifContext) 338 return ctx, nil 339} 340 341func freeHeifContext(c *Context) { 342 C.heif_context_free(c.context) 343 c.context = nil 344} 345 346func (c *Context) ReadFromFile(filename string) error { 347 c_filename := C.CString(filename) 348 defer C.free(unsafe.Pointer(c_filename)) 349 350 err := C.heif_context_read_from_file(c.context, c_filename, nil) 351 keepAlive(c) 352 return convertHeifError(err) 353} 354 355func (c *Context) ReadFromMemory(data []byte) error { 356 // TODO: Use reader API internally. 357 err := C.heif_context_read_from_memory(c.context, unsafe.Pointer(&data[0]), C.size_t(len(data)), nil) 358 keepAlive(c) 359 return convertHeifError(err) 360} 361 362type Encoder struct { 363 encoder *C.struct_heif_encoder 364 id string 365 name string 366} 367 368func (e *Encoder) ID() string { 369 return e.id 370} 371 372func (e *Encoder) Name() string { 373 return e.name 374} 375 376func (e *Encoder) SetQuality(q int) error { 377 err := C.heif_encoder_set_lossy_quality(e.encoder, C.int(q)) 378 keepAlive(e) 379 return convertHeifError(err) 380} 381 382func (e *Encoder) SetLossless(l LosslessMode) error { 383 err := C.heif_encoder_set_lossless(e.encoder, C.int(l)) 384 keepAlive(e) 385 return convertHeifError(err) 386} 387 388func (e *Encoder) SetLoggingLevel(l LoggingLevel) error { 389 err := C.heif_encoder_set_logging_level(e.encoder, C.int(l)) 390 keepAlive(e) 391 return convertHeifError(err) 392} 393 394func freeHeifEncoder(enc *Encoder) { 395 C.heif_encoder_release(enc.encoder) 396 enc.encoder = nil 397} 398 399func (c *Context) convertEncoderDescriptor(d *C.struct_heif_encoder_descriptor) (*Encoder, error) { 400 cid := C.heif_encoder_descriptor_get_id_name(d) 401 cname := C.heif_encoder_descriptor_get_name(d) 402 enc := &Encoder{ 403 id: C.GoString(cid), 404 name: C.GoString(cname), 405 } 406 err := C.heif_context_get_encoder(c.context, d, &enc.encoder) 407 keepAlive(c) 408 if err := convertHeifError(err); err != nil { 409 return nil, err 410 } 411 runtime.SetFinalizer(enc, freeHeifEncoder) 412 return enc, nil 413} 414 415func (c *Context) NewEncoder(compression Compression) (*Encoder, error) { 416 const max = 1 417 descriptors := make([]*C.struct_heif_encoder_descriptor, max) 418 num := int(C.heif_context_get_encoder_descriptors(c.context, uint32(compression), nil, &descriptors[0], C.int(max))) 419 keepAlive(c) 420 if num == 0 { 421 return nil, fmt.Errorf("no encoder for compression %v", compression) 422 } 423 return c.convertEncoderDescriptor(descriptors[0]) 424} 425 426func (c *Context) WriteToFile(filename string) error { 427 err := C.heif_context_write_to_file(c.context, C.CString(filename)) 428 keepAlive(c) 429 return convertHeifError(err) 430} 431 432func (c *Context) GetNumberOfTopLevelImages() int { 433 i := int(C.heif_context_get_number_of_top_level_images(c.context)) 434 keepAlive(c) 435 return i 436} 437 438func (c *Context) IsTopLevelImageID(ID int) bool { 439 ok := C.heif_context_is_top_level_image_ID(c.context, C.heif_item_id(ID)) != 0 440 keepAlive(c) 441 return ok 442} 443 444func (c *Context) GetListOfTopLevelImageIDs() []int { 445 num := int(C.heif_context_get_number_of_top_level_images(c.context)) 446 keepAlive(c) 447 if num == 0 { 448 return []int{} 449 } 450 451 origIDs := make([]C.heif_item_id, num) 452 C.heif_context_get_list_of_top_level_image_IDs(c.context, &origIDs[0], C.int(num)) 453 keepAlive(c) 454 return convertItemIDs(origIDs, num) 455} 456 457func (c *Context) GetPrimaryImageID() (int, error) { 458 var id C.heif_item_id 459 err := C.heif_context_get_primary_image_ID(c.context, &id) 460 keepAlive(c) 461 if err := convertHeifError(err); err != nil { 462 return 0, err 463 } 464 return int(id), nil 465} 466 467// --- ImageHandle 468 469type ImageHandle struct { 470 handle *C.struct_heif_image_handle 471} 472 473func freeHeifImageHandle(c *ImageHandle) { 474 C.heif_image_handle_release(c.handle) 475 c.handle = nil 476} 477 478func (c *Context) GetPrimaryImageHandle() (*ImageHandle, error) { 479 var handle ImageHandle 480 err := C.heif_context_get_primary_image_handle(c.context, &handle.handle) 481 keepAlive(c) 482 if err := convertHeifError(err); err != nil { 483 return nil, err 484 } 485 runtime.SetFinalizer(&handle, freeHeifImageHandle) 486 return &handle, convertHeifError(err) 487} 488 489func (c *Context) GetImageHandle(id int) (*ImageHandle, error) { 490 var handle ImageHandle 491 err := C.heif_context_get_image_handle(c.context, C.heif_item_id(id), &handle.handle) 492 keepAlive(c) 493 if err := convertHeifError(err); err != nil { 494 return nil, err 495 } 496 runtime.SetFinalizer(&handle, freeHeifImageHandle) 497 return &handle, nil 498} 499 500func (h *ImageHandle) IsPrimaryImage() bool { 501 ok := C.heif_image_handle_is_primary_image(h.handle) != 0 502 keepAlive(h) 503 return ok 504} 505 506func (h *ImageHandle) GetWidth() int { 507 i := int(C.heif_image_handle_get_width(h.handle)) 508 keepAlive(h) 509 return i 510} 511 512func (h *ImageHandle) GetHeight() int { 513 i := int(C.heif_image_handle_get_height(h.handle)) 514 keepAlive(h) 515 return i 516} 517 518func (h *ImageHandle) HasAlphaChannel() bool { 519 ok := C.heif_image_handle_has_alpha_channel(h.handle) != 0 520 keepAlive(h) 521 return ok 522} 523 524func (h *ImageHandle) HasDepthImage() bool { 525 ok := C.heif_image_handle_has_depth_image(h.handle) != 0 526 keepAlive(h) 527 return ok 528} 529 530func (h *ImageHandle) GetNumberOfDepthImages() int { 531 i := int(C.heif_image_handle_get_number_of_depth_images(h.handle)) 532 keepAlive(h) 533 return i 534} 535 536func (h *ImageHandle) GetListOfDepthImageIDs() []int { 537 num := int(C.heif_image_handle_get_number_of_depth_images(h.handle)) 538 keepAlive(h) 539 if num == 0 { 540 return []int{} 541 } 542 543 origIDs := make([]C.heif_item_id, num) 544 C.heif_image_handle_get_list_of_depth_image_IDs(h.handle, &origIDs[0], C.int(num)) 545 keepAlive(h) 546 return convertItemIDs(origIDs, num) 547} 548 549func (h *ImageHandle) GetDepthImageHandle(depth_image_id int) (*ImageHandle, error) { 550 var handle ImageHandle 551 err := C.heif_image_handle_get_depth_image_handle(h.handle, C.heif_item_id(depth_image_id), &handle.handle) 552 keepAlive(h) 553 if err := convertHeifError(err); err != nil { 554 return nil, err 555 } 556 557 runtime.SetFinalizer(&handle, freeHeifImageHandle) 558 return &handle, nil 559} 560 561func (h *ImageHandle) GetNumberOfThumbnails() int { 562 i := int(C.heif_image_handle_get_number_of_thumbnails(h.handle)) 563 keepAlive(h) 564 return i 565} 566 567func (h *ImageHandle) GetListOfThumbnailIDs() []int { 568 num := int(C.heif_image_handle_get_number_of_thumbnails(h.handle)) 569 keepAlive(h) 570 if num == 0 { 571 return []int{} 572 } 573 574 origIDs := make([]C.heif_item_id, num) 575 C.heif_image_handle_get_list_of_thumbnail_IDs(h.handle, &origIDs[0], C.int(num)) 576 keepAlive(h) 577 return convertItemIDs(origIDs, num) 578} 579 580func (h *ImageHandle) GetThumbnail(thumbnail_id int) (*ImageHandle, error) { 581 var handle ImageHandle 582 err := C.heif_image_handle_get_thumbnail(h.handle, C.heif_item_id(thumbnail_id), &handle.handle) 583 keepAlive(h) 584 runtime.SetFinalizer(&handle, freeHeifImageHandle) 585 return &handle, convertHeifError(err) 586} 587 588// TODO: EXIF metadata 589 590// --- Image 591 592type DecodingOptions struct { 593 options *C.struct_heif_decoding_options 594} 595 596func NewDecodingOptions() (*DecodingOptions, error) { 597 options := &DecodingOptions{ 598 options: C.heif_decoding_options_alloc(), 599 } 600 if options.options == nil { 601 return nil, fmt.Errorf("Could not allocate decoding options") 602 } 603 604 runtime.SetFinalizer(options, freeHeifDecodingOptions) 605 return options, nil 606} 607 608func freeHeifDecodingOptions(options *DecodingOptions) { 609 C.heif_decoding_options_free(options.options) 610 options.options = nil 611} 612 613type Image struct { 614 image *C.struct_heif_image 615} 616 617func NewImage(width, height int, colorspace Colorspace, chroma Chroma) (*Image, error) { 618 var image Image 619 err := C.heif_image_create(C.int(width), C.int(height), uint32(colorspace), uint32(chroma), &image.image) 620 if err := convertHeifError(err); err != nil { 621 return nil, err 622 } 623 runtime.SetFinalizer(&image, freeHeifImage) 624 return &image, nil 625} 626 627func freeHeifImage(image *Image) { 628 C.heif_image_release(image.image) 629 image.image = nil 630} 631 632func (h *ImageHandle) DecodeImage(colorspace Colorspace, chroma Chroma, options *DecodingOptions) (*Image, error) { 633 var image Image 634 635 var opt *C.struct_heif_decoding_options 636 if options != nil { 637 opt = options.options 638 } 639 640 err := C.heif_decode_image(h.handle, &image.image, uint32(colorspace), uint32(chroma), opt) 641 keepAlive(h) 642 if err := convertHeifError(err); err != nil { 643 return nil, err 644 } 645 646 runtime.SetFinalizer(&image, freeHeifImage) 647 return &image, nil 648} 649 650func (img *Image) GetColorspace() Colorspace { 651 cs := Colorspace(C.heif_image_get_colorspace(img.image)) 652 keepAlive(img) 653 return cs 654} 655 656func (img *Image) GetChromaFormat() Chroma { 657 c := Chroma(C.heif_image_get_chroma_format(img.image)) 658 keepAlive(img) 659 return c 660} 661 662func (img *Image) GetWidth(channel Channel) int { 663 i := int(C.heif_image_get_width(img.image, uint32(channel))) 664 keepAlive(img) 665 return i 666} 667 668func (img *Image) GetHeight(channel Channel) int { 669 i := int(C.heif_image_get_height(img.image, uint32(channel))) 670 keepAlive(img) 671 return i 672} 673 674func (img *Image) GetBitsPerPixel(channel Channel) int { 675 i := int(C.heif_image_get_bits_per_pixel(img.image, uint32(channel))) 676 keepAlive(img) 677 return i 678} 679 680func (img *Image) GetBitsPerPixelRange(channel Channel) int { 681 i := int(C.heif_image_get_bits_per_pixel_range(img.image, uint32(channel))) 682 keepAlive(img) 683 return i 684} 685 686func (img *Image) GetImage() (image.Image, error) { 687 var i image.Image 688 cf := img.GetChromaFormat() 689 switch cs := img.GetColorspace(); cs { 690 case ColorspaceYCbCr: 691 var subsample image.YCbCrSubsampleRatio 692 switch cf { 693 case Chroma420: 694 subsample = image.YCbCrSubsampleRatio420 695 case Chroma422: 696 subsample = image.YCbCrSubsampleRatio422 697 case Chroma444: 698 subsample = image.YCbCrSubsampleRatio444 699 default: 700 return nil, fmt.Errorf("Unsupported YCbCr chroma format: %v", cf) 701 } 702 y, err := img.GetPlane(ChannelY) 703 if err != nil { 704 return nil, err 705 } 706 cb, err := img.GetPlane(ChannelCb) 707 if err != nil { 708 return nil, err 709 } 710 cr, err := img.GetPlane(ChannelCr) 711 if err != nil { 712 return nil, err 713 } 714 i = &image.YCbCr{ 715 Y: y.Plane, 716 Cb: cb.Plane, 717 Cr: cr.Plane, 718 YStride: y.Stride, 719 CStride: cb.Stride, 720 SubsampleRatio: subsample, 721 Rect: image.Rectangle{ 722 Min: image.Point{ 723 X: 0, 724 Y: 0, 725 }, 726 Max: image.Point{ 727 X: img.GetWidth(ChannelY), 728 Y: img.GetHeight(ChannelY), 729 }, 730 }, 731 } 732 case ColorspaceRGB: 733 switch cf { 734 case Chroma444: 735 r, err := img.GetPlane(ChannelR) 736 if err != nil { 737 return nil, err 738 } 739 g, err := img.GetPlane(ChannelG) 740 if err != nil { 741 return nil, err 742 } 743 b, err := img.GetPlane(ChannelB) 744 if err != nil { 745 return nil, err 746 } 747 width := img.GetWidth(ChannelR) 748 height := img.GetHeight(ChannelR) 749 read_pos_r := 0 750 read_pos_g := 0 751 read_pos_b := 0 752 write_pos := 0 753 var rgba []byte 754 var stride int 755 if bpp := img.GetBitsPerPixelRange(ChannelR); bpp > 8 { 756 // NOTE: We only support the same bits per pixel on all components. 757 stride = width * 8 758 rgba = make([]byte, height*stride) 759 stride_add_r := r.Stride - width*2 760 stride_add_g := g.Stride - width*2 761 stride_add_b := b.Stride - width*2 762 if bpp == 16 { 763 for y := 0; y < height; y++ { 764 for x := 0; x < width; x++ { 765 rgba[write_pos] = r.Plane[read_pos_r] 766 rgba[write_pos+1] = r.Plane[read_pos_r+1] 767 rgba[write_pos+2] = g.Plane[read_pos_g] 768 rgba[write_pos+3] = g.Plane[read_pos_g+1] 769 rgba[write_pos+4] = b.Plane[read_pos_b] 770 rgba[write_pos+5] = b.Plane[read_pos_b+1] 771 rgba[write_pos+6] = 0xff 772 rgba[write_pos+7] = 0xff 773 read_pos_r += 2 774 read_pos_g += 2 775 read_pos_b += 2 776 write_pos += 8 777 } 778 read_pos_r += stride_add_r 779 read_pos_g += stride_add_g 780 read_pos_b += stride_add_b 781 } 782 } else { 783 for y := 0; y < height; y++ { 784 for x := 0; x < width; x++ { 785 r_value := (int16(r.Plane[read_pos_r+1]) << 8) | int16(r.Plane[read_pos_r]) 786 r_value = (r_value << (16 - uint(bpp))) | (r_value >> (2*uint(bpp) - 16)) 787 rgba[write_pos] = byte(r_value >> 8) 788 rgba[write_pos+1] = byte(r_value & 0xff) 789 g_value := (int16(g.Plane[read_pos_g+1]) << 8) | int16(g.Plane[read_pos_g]) 790 g_value = (g_value << (16 - uint(bpp))) | (g_value >> (2*uint(bpp) - 16)) 791 rgba[write_pos+2] = byte(g_value >> 8) 792 rgba[write_pos+3] = byte(g_value & 0xff) 793 b_value := (int16(b.Plane[read_pos_b+1]) << 8) | int16(b.Plane[read_pos_b]) 794 b_value = (b_value << (16 - uint(bpp))) | (b_value >> (2*uint(bpp) - 16)) 795 rgba[write_pos+4] = byte(b_value >> 8) 796 rgba[write_pos+5] = byte(b_value & 0xff) 797 rgba[write_pos+6] = 0xff 798 rgba[write_pos+7] = 0xff 799 read_pos_r += 2 800 read_pos_g += 2 801 read_pos_b += 2 802 write_pos += 8 803 } 804 read_pos_r += stride_add_r 805 read_pos_g += stride_add_g 806 read_pos_b += stride_add_b 807 } 808 } 809 810 i = &image.RGBA64{ 811 Pix: rgba, 812 Stride: stride, 813 Rect: image.Rectangle{ 814 Min: image.Point{ 815 X: 0, 816 Y: 0, 817 }, 818 Max: image.Point{ 819 X: width, 820 Y: height, 821 }, 822 }, 823 } 824 } else { 825 stride = width * 4 826 rgba = make([]byte, height*stride) 827 stride_add_r := r.Stride - width 828 stride_add_g := g.Stride - width 829 stride_add_b := b.Stride - width 830 for y := 0; y < height; y++ { 831 for x := 0; x < width; x++ { 832 rgba[write_pos] = r.Plane[read_pos_r] 833 rgba[write_pos+1] = g.Plane[read_pos_g] 834 rgba[write_pos+2] = b.Plane[read_pos_b] 835 rgba[write_pos+3] = 0xff 836 read_pos_r++ 837 read_pos_g++ 838 read_pos_b++ 839 write_pos += 4 840 } 841 read_pos_r += stride_add_r 842 read_pos_g += stride_add_g 843 read_pos_b += stride_add_b 844 } 845 846 i = &image.RGBA{ 847 Pix: rgba, 848 Stride: stride, 849 Rect: image.Rectangle{ 850 Min: image.Point{ 851 X: 0, 852 Y: 0, 853 }, 854 Max: image.Point{ 855 X: width, 856 Y: height, 857 }, 858 }, 859 } 860 } 861 case ChromaInterleavedRGB: 862 rgb, err := img.GetPlane(ChannelInterleaved) 863 if err != nil { 864 return nil, err 865 } 866 width := img.GetWidth(ChannelInterleaved) 867 height := img.GetHeight(ChannelInterleaved) 868 rgba := make([]byte, width*height*4) 869 read_pos := 0 870 write_pos := 0 871 stride_add := rgb.Stride - width*3 872 for y := 0; y < height; y++ { 873 for x := 0; x < width; x++ { 874 rgba[write_pos] = rgb.Plane[read_pos] 875 rgba[write_pos+1] = rgb.Plane[read_pos+1] 876 rgba[write_pos+2] = rgb.Plane[read_pos+2] 877 rgba[write_pos+3] = 0xff 878 read_pos += 3 879 write_pos += 4 880 } 881 read_pos += stride_add 882 } 883 i = &image.RGBA{ 884 Pix: rgba, 885 Stride: width * 4, 886 Rect: image.Rectangle{ 887 Min: image.Point{ 888 X: 0, 889 Y: 0, 890 }, 891 Max: image.Point{ 892 X: width, 893 Y: height, 894 }, 895 }, 896 } 897 case ChromaInterleavedRGBA: 898 rgba, err := img.GetPlane(ChannelInterleaved) 899 if err != nil { 900 return nil, err 901 } 902 i = &image.RGBA{ 903 Pix: rgba.Plane, 904 Stride: rgba.Stride, 905 Rect: image.Rectangle{ 906 Min: image.Point{ 907 X: 0, 908 Y: 0, 909 }, 910 Max: image.Point{ 911 X: img.GetWidth(ChannelInterleaved), 912 Y: img.GetHeight(ChannelInterleaved), 913 }, 914 }, 915 } 916 case ChromaInterleavedRRGGBB_BE: 917 rgb, err := img.GetPlane(ChannelInterleaved) 918 if err != nil { 919 return nil, err 920 } 921 width := img.GetWidth(ChannelInterleaved) 922 height := img.GetHeight(ChannelInterleaved) 923 rgba := make([]byte, width*height*8) 924 read_pos := 0 925 write_pos := 0 926 stride_add := rgb.Stride - width*6 927 if bpp := img.GetBitsPerPixelRange(ChannelInterleaved); bpp != 16 { 928 for y := 0; y < height; y++ { 929 for x := 0; x < width; x++ { 930 r_value := (int16(rgb.Plane[read_pos]) << 8) | int16(rgb.Plane[read_pos+1]) 931 r_value = (r_value << (16 - uint(bpp))) | (r_value >> (2*uint(bpp) - 16)) 932 rgba[write_pos] = byte(r_value >> 8) 933 rgba[write_pos+1] = byte(r_value & 0xff) 934 g_value := (int16(rgb.Plane[read_pos+2]) << 8) | int16(rgb.Plane[read_pos+3]) 935 g_value = (g_value << (16 - uint(bpp))) | (g_value >> (2*uint(bpp) - 16)) 936 rgba[write_pos+2] = byte(g_value >> 8) 937 rgba[write_pos+3] = byte(g_value & 0xff) 938 b_value := (int16(rgb.Plane[read_pos+4]) << 8) | int16(rgb.Plane[read_pos+5]) 939 b_value = (b_value << (16 - uint(bpp))) | (b_value >> (2*uint(bpp) - 16)) 940 rgba[write_pos+4] = byte(b_value >> 8) 941 rgba[write_pos+5] = byte(b_value & 0xff) 942 rgba[write_pos+6] = 0xff 943 rgba[write_pos+7] = 0xff 944 read_pos += 6 945 write_pos += 8 946 } 947 read_pos += stride_add 948 } 949 } else { 950 for y := 0; y < height; y++ { 951 for x := 0; x < width; x++ { 952 rgba[write_pos] = rgb.Plane[read_pos] 953 rgba[write_pos+1] = rgb.Plane[read_pos+1] 954 rgba[write_pos+2] = rgb.Plane[read_pos+2] 955 rgba[write_pos+3] = rgb.Plane[read_pos+3] 956 rgba[write_pos+4] = rgb.Plane[read_pos+4] 957 rgba[write_pos+5] = rgb.Plane[read_pos+5] 958 rgba[write_pos+6] = 0xff 959 rgba[write_pos+7] = 0xff 960 read_pos += 6 961 write_pos += 8 962 } 963 read_pos += stride_add 964 } 965 } 966 i = &image.RGBA64{ 967 Pix: rgba, 968 Stride: width * 4, 969 Rect: image.Rectangle{ 970 Min: image.Point{ 971 X: 0, 972 Y: 0, 973 }, 974 Max: image.Point{ 975 X: width, 976 Y: height, 977 }, 978 }, 979 } 980 case ChromaInterleavedRRGGBBAA_BE: 981 rgba, err := img.GetPlane(ChannelInterleaved) 982 if err != nil { 983 return nil, err 984 } 985 width := img.GetWidth(ChannelInterleaved) 986 height := img.GetHeight(ChannelInterleaved) 987 var plane []byte 988 if bpp := img.GetBitsPerPixelRange(ChannelInterleaved); bpp != 16 { 989 read_pos := 0 990 write_pos := 0 991 stride_add := rgba.Stride - width*8 992 plane = make([]byte, width*height*8) 993 for y := 0; y < height; y++ { 994 for x := 0; x < width; x++ { 995 r_value := (int16(rgba.Plane[read_pos]) << 8) | int16(rgba.Plane[read_pos+1]) 996 r_value = (r_value << (16 - uint(bpp))) | (r_value >> (2*uint(bpp) - 16)) 997 plane[write_pos] = byte(r_value >> 8) 998 plane[write_pos+1] = byte(r_value & 0xff) 999 g_value := (int16(rgba.Plane[read_pos+2]) << 8) | int16(rgba.Plane[read_pos+3]) 1000 g_value = (g_value << (16 - uint(bpp))) | (g_value >> (2*uint(bpp) - 16)) 1001 plane[write_pos+2] = byte(g_value >> 8) 1002 plane[write_pos+3] = byte(g_value & 0xff) 1003 b_value := (int16(rgba.Plane[read_pos+4]) << 8) | int16(rgba.Plane[read_pos+5]) 1004 b_value = (b_value << (16 - uint(bpp))) | (b_value >> (2*uint(bpp) - 16)) 1005 plane[write_pos+4] = byte(b_value >> 8) 1006 plane[write_pos+5] = byte(b_value & 0xff) 1007 a_value := (int16(rgba.Plane[read_pos+6]) << 8) | int16(rgba.Plane[read_pos+7]) 1008 a_value = (a_value << (16 - uint(bpp))) | (a_value >> (2*uint(bpp) - 16)) 1009 plane[write_pos+6] = byte(a_value >> 8) 1010 plane[write_pos+7] = byte(a_value & 0xff) 1011 read_pos += 8 1012 write_pos += 8 1013 } 1014 read_pos += stride_add 1015 } 1016 } else { 1017 plane = rgba.Plane 1018 } 1019 i = &image.RGBA64{ 1020 Pix: plane, 1021 Stride: rgba.Stride, 1022 Rect: image.Rectangle{ 1023 Min: image.Point{ 1024 X: 0, 1025 Y: 0, 1026 }, 1027 Max: image.Point{ 1028 X: width, 1029 Y: height, 1030 }, 1031 }, 1032 } 1033 default: 1034 return nil, fmt.Errorf("Unsupported RGB chroma format: %v", cf) 1035 } 1036 default: 1037 return nil, fmt.Errorf("Unsupported colorspace: %v", cs) 1038 } 1039 1040 return i, nil 1041} 1042 1043type ImageAccess struct { 1044 Plane []byte 1045 planePtr unsafe.Pointer 1046 Stride int 1047 height int 1048 1049 image *Image // need this reference to make sure the image is not GC'ed while we access it 1050} 1051 1052func (i *ImageAccess) setData(data []byte, stride int) { 1053 // Handle common case directly 1054 if stride == i.Stride { 1055 dstP := uintptr(i.planePtr) 1056 srcP := uintptr(unsafe.Pointer(&data[0])) 1057 C.memcpy(unsafe.Pointer(dstP), unsafe.Pointer(srcP), C.size_t(i.height*stride)) 1058 } else { 1059 for y := 0; y < i.height; y++ { 1060 dstP := uintptr(i.planePtr) + uintptr(y*i.Stride) 1061 srcP := uintptr(unsafe.Pointer(&data[0])) + uintptr(y*stride) 1062 C.memcpy(unsafe.Pointer(dstP), unsafe.Pointer(srcP), C.size_t(stride)) 1063 } 1064 } 1065 i.Plane = C.GoBytes(i.planePtr, C.int(i.height*i.Stride)) 1066} 1067 1068func (img *Image) GetPlane(channel Channel) (*ImageAccess, error) { 1069 height := C.heif_image_get_height(img.image, uint32(channel)) 1070 keepAlive(img) 1071 if height == -1 { 1072 return nil, fmt.Errorf("No such channel %v", channel) 1073 } 1074 1075 var stride C.int 1076 plane := C.heif_image_get_plane(img.image, uint32(channel), &stride) 1077 keepAlive(img) 1078 if plane == nil { 1079 return nil, fmt.Errorf("No such channel %v", channel) 1080 } 1081 1082 ptr := unsafe.Pointer(plane) 1083 size := stride * height 1084 access := &ImageAccess{ 1085 Plane: C.GoBytes(ptr, size), 1086 planePtr: ptr, 1087 Stride: int(stride), 1088 height: int(height), 1089 image: img, 1090 } 1091 return access, nil 1092} 1093 1094func (img *Image) NewPlane(channel Channel, width, height, depth int) (*ImageAccess, error) { 1095 err := C.heif_image_add_plane(img.image, uint32(channel), C.int(width), C.int(height), C.int(depth)) 1096 keepAlive(img) 1097 if err := convertHeifError(err); err != nil { 1098 return nil, err 1099 } 1100 return img.GetPlane(channel) 1101} 1102 1103func (img *Image) ScaleImage(width int, height int) (*Image, error) { 1104 var scaled_image Image 1105 err := C.heif_image_scale_image(img.image, &scaled_image.image, C.int(width), C.int(height), nil) 1106 keepAlive(img) 1107 if err := convertHeifError(err); err != nil { 1108 return nil, err 1109 } 1110 1111 runtime.SetFinalizer(&scaled_image, freeHeifImage) 1112 return &scaled_image, nil 1113} 1114 1115// --- High-level encoding API. 1116 1117type EncodingOptions struct { 1118 options *C.struct_heif_encoding_options 1119} 1120 1121func NewEncodingOptions() (*EncodingOptions, error) { 1122 options := &EncodingOptions{ 1123 options: C.heif_encoding_options_alloc(), 1124 } 1125 if options.options == nil { 1126 return nil, fmt.Errorf("Could not allocate encoding options") 1127 } 1128 1129 runtime.SetFinalizer(options, freeHeifEncodingOptions) 1130 return options, nil 1131} 1132 1133func freeHeifEncodingOptions(options *EncodingOptions) { 1134 C.heif_encoding_options_free(options.options) 1135 options.options = nil 1136} 1137 1138func imageFromRGBA(i *image.RGBA) (*Image, error) { 1139 min := i.Bounds().Min 1140 max := i.Bounds().Max 1141 w := max.X - min.X 1142 h := max.Y - min.Y 1143 1144 out, err := NewImage(w, h, ColorspaceRGB, ChromaInterleavedRGBA) 1145 if err != nil { 1146 return nil, fmt.Errorf("failed to create image: %v", err) 1147 } 1148 1149 p, err := out.NewPlane(ChannelInterleaved, w, h, 8) 1150 if err != nil { 1151 return nil, fmt.Errorf("failed to add plane: %v", err) 1152 } 1153 p.setData([]byte(i.Pix), w*4) 1154 1155 return out, nil 1156} 1157 1158func imageFromRGBA64(i *image.RGBA64) (*Image, error) { 1159 min := i.Bounds().Min 1160 max := i.Bounds().Max 1161 w := max.X - min.X 1162 h := max.Y - min.Y 1163 1164 out, err := NewImage(w, h, ColorspaceRGB, ChromaInterleavedRRGGBBAA_BE) 1165 if err != nil { 1166 return nil, fmt.Errorf("failed to create image: %v", err) 1167 } 1168 1169 p, err := out.NewPlane(ChannelInterleaved, w, h, 10) 1170 if err != nil { 1171 return nil, fmt.Errorf("failed to add plane: %v", err) 1172 } 1173 1174 pix := make([]byte, w*h*8) 1175 read_pos := 0 1176 write_pos := 0 1177 for y := 0; y < h; y++ { 1178 for x := 0; x < w; x++ { 1179 r := (uint16(i.Pix[read_pos]) << 8) | uint16(i.Pix[read_pos+1]) 1180 r = r >> 6 1181 pix[write_pos] = byte(r >> 8) 1182 pix[write_pos+1] = byte(r & 0xff) 1183 read_pos += 2 1184 g := (uint16(i.Pix[read_pos]) << 8) | uint16(i.Pix[read_pos+1]) 1185 g = g >> 6 1186 pix[write_pos+2] = byte(g >> 8) 1187 pix[write_pos+3] = byte(g & 0xff) 1188 read_pos += 2 1189 b := (uint16(i.Pix[read_pos]) << 8) | uint16(i.Pix[read_pos+1]) 1190 b = b >> 6 1191 pix[write_pos+4] = byte(b >> 8) 1192 pix[write_pos+5] = byte(b & 0xff) 1193 read_pos += 2 1194 a := (uint16(i.Pix[read_pos]) << 8) | uint16(i.Pix[read_pos+1]) 1195 a = a >> 6 1196 pix[write_pos+6] = byte(a >> 8) 1197 pix[write_pos+7] = byte(a & 0xff) 1198 pix[write_pos+6] = byte(a >> 8) 1199 pix[write_pos+7] = byte(a & 0xff) 1200 read_pos += 2 1201 write_pos += 8 1202 } 1203 } 1204 p.setData(pix, w*8) 1205 1206 return out, nil 1207} 1208 1209func imageFromGray(i *image.Gray) (*Image, error) { 1210 min := i.Bounds().Min 1211 max := i.Bounds().Max 1212 w := max.X - min.X 1213 h := max.Y - min.Y 1214 1215 out, err := NewImage(w, h, ColorspaceYCbCr, ChromaMonochrome) 1216 if err != nil { 1217 return nil, fmt.Errorf("failed to create image: %v", err) 1218 } 1219 1220 const depth = 8 1221 pY, err := out.NewPlane(ChannelY, w, h, depth) 1222 if err != nil { 1223 return nil, fmt.Errorf("failed to add Y plane: %v", err) 1224 } 1225 pY.setData([]byte(i.Pix), i.Stride) 1226 1227 return out, nil 1228} 1229 1230func imageFromYCbCr(i *image.YCbCr) (*Image, error) { 1231 min := i.Bounds().Min 1232 max := i.Bounds().Max 1233 w := max.X - min.X 1234 h := max.Y - min.Y 1235 1236 var cm Chroma 1237 switch sr := i.SubsampleRatio; sr { 1238 case image.YCbCrSubsampleRatio420: 1239 cm = Chroma420 1240 default: 1241 return nil, fmt.Errorf("unsupported subsample ratio: %s", sr.String()) 1242 } 1243 1244 out, err := NewImage(w, h, ColorspaceYCbCr, cm) 1245 if err != nil { 1246 return nil, fmt.Errorf("failed to create image: %v", err) 1247 } 1248 1249 const depth = 8 1250 pY, err := out.NewPlane(ChannelY, w, h, depth) 1251 if err != nil { 1252 return nil, fmt.Errorf("failed to add Y plane: %v", err) 1253 } 1254 pY.setData([]byte(i.Y), i.YStride) 1255 1256 // TODO: Might need to be updated for other SubsampleRatio values. 1257 halfW, halfH := (w+1)/2, (h+1)/2 1258 pCb, err := out.NewPlane(ChannelCb, halfW, halfH, depth) 1259 if err != nil { 1260 return nil, fmt.Errorf("failed to add Cb plane: %v", err) 1261 } 1262 pCb.setData([]byte(i.Cb), i.CStride) 1263 pCr, err := out.NewPlane(ChannelCr, halfW, halfH, depth) 1264 if err != nil { 1265 return nil, fmt.Errorf("failed to add Cr plane: %v", err) 1266 } 1267 pCr.setData([]byte(i.Cr), i.CStride) 1268 1269 return out, nil 1270} 1271 1272func EncodeFromImage(img image.Image, compression Compression, quality int, lossless LosslessMode, logging LoggingLevel) (*Context, error) { 1273 var out *Image 1274 1275 switch i := img.(type) { 1276 default: 1277 return nil, fmt.Errorf("unsupported image type: %T", i) 1278 case *image.RGBA: 1279 tmp, err := imageFromRGBA(i) 1280 if err != nil { 1281 return nil, fmt.Errorf("failed to create image: %v", err) 1282 } 1283 out = tmp 1284 case *image.RGBA64: 1285 tmp, err := imageFromRGBA64(i) 1286 if err != nil { 1287 return nil, fmt.Errorf("failed to create image: %v", err) 1288 } 1289 out = tmp 1290 case *image.Gray: 1291 tmp, err := imageFromGray(i) 1292 if err != nil { 1293 return nil, fmt.Errorf("failed to create image: %v", err) 1294 } 1295 out = tmp 1296 case *image.YCbCr: 1297 tmp, err := imageFromYCbCr(i) 1298 if err != nil { 1299 return nil, fmt.Errorf("failed to create image: %v", err) 1300 } 1301 out = tmp 1302 } 1303 1304 ctx, err := NewContext() 1305 if err != nil { 1306 return nil, fmt.Errorf("failed to create HEIF context: %v", err) 1307 } 1308 1309 enc, err := ctx.NewEncoder(compression) 1310 if err != nil { 1311 return nil, fmt.Errorf("failed to create encoder: %v", err) 1312 } 1313 1314 if err := enc.SetQuality(quality); err != nil { 1315 return nil, fmt.Errorf("failed to set quality: %v", err) 1316 } 1317 if err := enc.SetLossless(lossless); err != nil { 1318 return nil, fmt.Errorf("failed to set lossless mode: %v", err) 1319 } 1320 if err := enc.SetLoggingLevel(logging); err != nil { 1321 return nil, fmt.Errorf("failed to set logging level: %v", err) 1322 } 1323 1324 encOpts, err := NewEncodingOptions() 1325 if err != nil { 1326 return nil, fmt.Errorf("failed to get encoding options: %v", err) 1327 } 1328 1329 var handle ImageHandle 1330 err2 := C.heif_context_encode_image(ctx.context, out.image, enc.encoder, encOpts.options, &handle.handle) 1331 keepAlive(ctx) 1332 keepAlive(out) 1333 keepAlive(enc) 1334 keepAlive(encOpts) 1335 if err := convertHeifError(err2); err != nil { 1336 return nil, fmt.Errorf("failed to encode image: %v", err) 1337 } 1338 runtime.SetFinalizer(&handle, freeHeifImageHandle) 1339 1340 return ctx, nil 1341} 1342 1343// --- High-level decoding API, always decodes primary image (if present). 1344 1345func decodePrimaryImageFromReader(r io.Reader) (*ImageHandle, error) { 1346 ctx, err := NewContext() 1347 if err != nil { 1348 return nil, err 1349 } 1350 1351 data, err := ioutil.ReadAll(r) 1352 if err != nil { 1353 return nil, err 1354 } 1355 1356 if err := ctx.ReadFromMemory(data); err != nil { 1357 return nil, err 1358 } 1359 1360 handle, err := ctx.GetPrimaryImageHandle() 1361 if err != nil { 1362 return nil, err 1363 } 1364 1365 return handle, nil 1366} 1367 1368func decodeImage(r io.Reader) (image.Image, error) { 1369 handle, err := decodePrimaryImageFromReader(r) 1370 if err != nil { 1371 return nil, err 1372 } 1373 1374 img, err := handle.DecodeImage(ColorspaceUndefined, ChromaUndefined, nil) 1375 if err != nil { 1376 return nil, err 1377 } 1378 1379 return img.GetImage() 1380} 1381 1382func decodeConfig(r io.Reader) (image.Config, error) { 1383 var config image.Config 1384 handle, err := decodePrimaryImageFromReader(r) 1385 if err != nil { 1386 return config, err 1387 } 1388 1389 config = image.Config{ 1390 ColorModel: color.YCbCrModel, 1391 Width: handle.GetWidth(), 1392 Height: handle.GetHeight(), 1393 } 1394 return config, nil 1395} 1396 1397func init() { 1398 image.RegisterFormat("heif", "????ftypheic", decodeImage, decodeConfig) 1399 image.RegisterFormat("heif", "????ftypheim", decodeImage, decodeConfig) 1400 image.RegisterFormat("heif", "????ftypheis", decodeImage, decodeConfig) 1401 image.RegisterFormat("heif", "????ftypheix", decodeImage, decodeConfig) 1402 image.RegisterFormat("heif", "????ftyphevc", decodeImage, decodeConfig) 1403 image.RegisterFormat("heif", "????ftyphevm", decodeImage, decodeConfig) 1404 image.RegisterFormat("heif", "????ftyphevs", decodeImage, decodeConfig) 1405 image.RegisterFormat("heif", "????ftypmif1", decodeImage, decodeConfig) 1406 image.RegisterFormat("avif", "????ftypavif", decodeImage, decodeConfig) 1407 image.RegisterFormat("avif", "????ftypavis", decodeImage, decodeConfig) 1408} 1409