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