-
Notifications
You must be signed in to change notification settings - Fork 123
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
1,385 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
package lilliput | ||
|
||
// #include "avif.hpp" | ||
import "C" | ||
|
||
import ( | ||
"io" | ||
"time" | ||
"unsafe" | ||
) | ||
|
||
// Types | ||
// ---------------------------------------- | ||
|
||
type avifDecoder struct { | ||
decoder C.avif_decoder | ||
mat C.opencv_mat | ||
buf []byte | ||
} | ||
|
||
type avifEncoder struct { | ||
encoder C.avif_encoder | ||
dstBuf []byte | ||
icc []byte | ||
bgColor uint32 | ||
frameIndex int | ||
hasFlushed bool | ||
} | ||
|
||
// Decoder Implementation | ||
// ---------------------------------------- | ||
|
||
func newAvifDecoder(buf []byte) (*avifDecoder, error) { | ||
mat := C.opencv_mat_create_from_data(C.int(len(buf)), 1, C.CV_8U, unsafe.Pointer(&buf[0]), C.size_t(len(buf))) | ||
if mat == nil { | ||
return nil, ErrBufTooSmall | ||
} | ||
|
||
decoder := C.avif_decoder_create(mat) | ||
if decoder == nil { | ||
return nil, ErrInvalidImage | ||
} | ||
|
||
return &avifDecoder{ | ||
decoder: decoder, | ||
mat: mat, | ||
buf: buf, | ||
}, nil | ||
} | ||
|
||
func (d *avifDecoder) Header() (*ImageHeader, error) { | ||
return &ImageHeader{ | ||
width: int(C.avif_decoder_get_width(d.decoder)), | ||
height: int(C.avif_decoder_get_height(d.decoder)), | ||
pixelType: PixelType(C.avif_decoder_get_pixel_type(d.decoder)), | ||
orientation: OrientationTopLeft, | ||
numFrames: int(C.avif_decoder_get_num_frames(d.decoder)), | ||
contentLength: len(d.buf), | ||
}, nil | ||
} | ||
|
||
func (d *avifDecoder) DecodeTo(f *Framebuffer) error { | ||
if f == nil { | ||
return io.EOF | ||
} | ||
|
||
if d.hasReachedEndOfFrames() { | ||
return io.EOF | ||
} | ||
|
||
h, err := d.Header() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = f.resizeMat(h.Width(), h.Height(), h.PixelType()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
ret := C.avif_decoder_decode(d.decoder, f.mat) | ||
if !ret { | ||
return ErrDecodingFailed | ||
} | ||
|
||
// Set frame properties | ||
f.duration = time.Duration(C.avif_decoder_get_frame_duration(d.decoder)) * time.Millisecond | ||
f.dispose = DisposeMethod(C.avif_decoder_get_frame_dispose(d.decoder)) | ||
f.blend = BlendMethod(C.avif_decoder_get_frame_blend(d.decoder)) | ||
f.xOffset = int(C.avif_decoder_get_frame_x_offset(d.decoder)) | ||
f.yOffset = int(C.avif_decoder_get_frame_y_offset(d.decoder)) | ||
|
||
return nil | ||
} | ||
|
||
// Decoder Helper Methods | ||
// ---------------------------------------- | ||
|
||
func (d *avifDecoder) hasReachedEndOfFrames() bool { | ||
return C.avif_decoder_has_more_frames(d.decoder) == 0 | ||
} | ||
|
||
func (d *avifDecoder) ICC() []byte { | ||
iccDst := make([]byte, 8192) | ||
iccLength := C.avif_decoder_get_icc(d.decoder, unsafe.Pointer(&iccDst[0]), C.size_t(cap(iccDst))) | ||
return iccDst[:iccLength] | ||
} | ||
|
||
func (d *avifDecoder) Description() string { | ||
return "AVIF" | ||
} | ||
|
||
func (d *avifDecoder) BackgroundColor() uint32 { | ||
return uint32(C.avif_decoder_get_bg_color(d.decoder)) | ||
} | ||
|
||
func (d *avifDecoder) Duration() time.Duration { | ||
return time.Duration(C.avif_decoder_get_total_duration(d.decoder)) * time.Millisecond | ||
} | ||
|
||
func (d *avifDecoder) LoopCount() int { | ||
return int(C.avif_decoder_get_loop_count(d.decoder)) | ||
} | ||
|
||
func (d *avifDecoder) IsAnimated() bool { | ||
return int(C.avif_decoder_get_num_frames(d.decoder)) > 1 | ||
} | ||
|
||
func (d *avifDecoder) HasSubtitles() bool { | ||
return false | ||
} | ||
|
||
func (d *avifDecoder) IsStreamable() bool { | ||
return false | ||
} | ||
|
||
func (d *avifDecoder) SkipFrame() error { | ||
return ErrSkipNotSupported | ||
} | ||
|
||
func (d *avifDecoder) Close() { | ||
C.avif_decoder_release(d.decoder) | ||
C.opencv_mat_release(d.mat) | ||
d.buf = nil | ||
} | ||
|
||
// Encoder Implementation | ||
// ---------------------------------------- | ||
|
||
func newAvifEncoder(decodedBy Decoder, dstBuf []byte) (*avifEncoder, error) { | ||
dstBuf = dstBuf[:1] | ||
icc := decodedBy.ICC() | ||
loopCount := decodedBy.LoopCount() | ||
bgColor := decodedBy.BackgroundColor() | ||
|
||
var enc C.avif_encoder | ||
if len(icc) > 0 { | ||
enc = C.avif_encoder_create(unsafe.Pointer(&dstBuf[0]), C.size_t(cap(dstBuf)), | ||
unsafe.Pointer(&icc[0]), C.size_t(len(icc)), C.int(loopCount)) | ||
} else { | ||
enc = C.avif_encoder_create(unsafe.Pointer(&dstBuf[0]), C.size_t(cap(dstBuf)), | ||
nil, 0, C.int(loopCount)) | ||
} | ||
if enc == nil { | ||
return nil, ErrBufTooSmall | ||
} | ||
|
||
return &avifEncoder{ | ||
encoder: enc, | ||
dstBuf: dstBuf, | ||
icc: icc, | ||
bgColor: bgColor, | ||
}, nil | ||
} | ||
|
||
func (e *avifEncoder) Encode(f *Framebuffer, opt map[int]int) ([]byte, error) { | ||
if e.hasFlushed { | ||
return nil, io.EOF | ||
} | ||
|
||
if f == nil { | ||
length := C.avif_encoder_flush(e.encoder) | ||
if length == 0 { | ||
return nil, ErrInvalidImage | ||
} | ||
|
||
e.hasFlushed = true | ||
return e.dstBuf[:length], nil | ||
} | ||
|
||
var optList []C.int | ||
var firstOpt *C.int | ||
for k, v := range opt { | ||
optList = append(optList, C.int(k)) | ||
optList = append(optList, C.int(v)) | ||
} | ||
if len(optList) > 0 { | ||
firstOpt = (*C.int)(unsafe.Pointer(&optList[0])) | ||
} | ||
|
||
frameDelayMs := int(f.duration.Milliseconds()) | ||
length := C.avif_encoder_write(e.encoder, f.mat, firstOpt, C.size_t(len(optList)), | ||
C.int(frameDelayMs), C.int(f.blend), C.int(f.dispose)) | ||
if length == 0 { | ||
return nil, ErrInvalidImage | ||
} | ||
|
||
e.frameIndex++ | ||
return nil, nil | ||
} | ||
|
||
func (e *avifEncoder) Close() { | ||
C.avif_encoder_release(e.encoder) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
#ifndef LILLIPUT_AVIF_HPP | ||
#define LILLIPUT_AVIF_HPP | ||
|
||
#include "opencv.hpp" | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
//---------------------- | ||
// Enums & Constants | ||
//---------------------- | ||
enum AvifBlendMode { | ||
AVIF_BLEND_ALPHA = 0, | ||
AVIF_BLEND_NONE = 1 | ||
}; | ||
|
||
enum AvifDisposeMode { | ||
AVIF_DISPOSE_NONE = 0, | ||
AVIF_DISPOSE_BACKGROUND = 1 | ||
}; | ||
|
||
enum AvifEncoderOptions { | ||
AVIF_QUALITY = 1, | ||
AVIF_SPEED = 2 | ||
}; | ||
|
||
//---------------------- | ||
// Type Definitions | ||
//---------------------- | ||
typedef struct avif_decoder_struct* avif_decoder; | ||
typedef struct avif_encoder_struct* avif_encoder; | ||
|
||
//---------------------- | ||
// Decoder Management | ||
//---------------------- | ||
avif_decoder avif_decoder_create(const opencv_mat buf); | ||
void avif_decoder_release(avif_decoder d); | ||
|
||
//---------------------- | ||
// Decoder Properties | ||
//---------------------- | ||
int avif_decoder_get_width(const avif_decoder d); | ||
int avif_decoder_get_height(const avif_decoder d); | ||
int avif_decoder_get_pixel_type(const avif_decoder d); | ||
int avif_decoder_get_num_frames(const avif_decoder d); | ||
uint32_t avif_decoder_get_duration(const avif_decoder d); | ||
uint32_t avif_decoder_get_loop_count(const avif_decoder d); | ||
size_t avif_decoder_get_icc(const avif_decoder d, void* buf, size_t buf_len); | ||
uint32_t avif_decoder_get_bg_color(const avif_decoder d); | ||
int avif_decoder_get_total_duration(const avif_decoder d); | ||
|
||
//---------------------- | ||
// Frame Properties | ||
//---------------------- | ||
int avif_decoder_get_frame_duration(const avif_decoder d); | ||
int avif_decoder_get_frame_dispose(const avif_decoder d); | ||
int avif_decoder_get_frame_blend(const avif_decoder d); | ||
int avif_decoder_get_frame_x_offset(const avif_decoder d); | ||
int avif_decoder_get_frame_y_offset(const avif_decoder d); | ||
|
||
//---------------------- | ||
// Frame Operations | ||
//---------------------- | ||
bool avif_decoder_decode(avif_decoder d, opencv_mat mat); | ||
int avif_decoder_has_more_frames(avif_decoder d); | ||
|
||
//---------------------- | ||
// Encoder Management | ||
//---------------------- | ||
avif_encoder avif_encoder_create(void* buf, size_t buf_len, const void* icc, size_t icc_len, int loop_count); | ||
void avif_encoder_release(avif_encoder e); | ||
|
||
//---------------------- | ||
// Encoder Operations | ||
//---------------------- | ||
size_t avif_encoder_write(avif_encoder e, const opencv_mat src, const int* opt, size_t opt_len, int delay_ms, int blend, int dispose); | ||
size_t avif_encoder_flush(avif_encoder e); | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif | ||
|
||
#endif |
Oops, something went wrong.