1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#![allow(clippy::many_single_char_names)]
use std::io;
pub use self::data::AvifData;
use libavif_sys as sys;
mod data;
pub struct RgbPixels {
rgb: sys::avifRGBImage,
}
impl RgbPixels {
pub fn width(&self) -> u32 {
self.rgb.width
}
pub fn height(&self) -> u32 {
self.rgb.height
}
pub fn pixel(&self, x: u32, y: u32) -> (u8, u8, u8, u8) {
assert!(x < self.width());
assert!(y < self.height());
unsafe {
let pixels = self.rgb.pixels;
let row_bytes = self.rgb.rowBytes as usize;
let rgb = pixels.add((4 * x as usize) + (row_bytes * y as usize));
let r = *rgb.add(0);
let g = *rgb.add(1);
let b = *rgb.add(2);
let a = *rgb.add(3);
(r, g, b, a)
}
}
}
impl Drop for RgbPixels {
fn drop(&mut self) {
unsafe {
sys::avifRGBImageFreePixels(&mut self.rgb as *mut sys::avifRGBImage);
}
}
}
pub fn is_avif(avif_bytes: &[u8]) -> bool {
let raw = sys::avifROData {
data: avif_bytes.as_ptr(),
size: avif_bytes.len(),
};
unsafe { sys::avifPeekCompatibleFileType(&raw) == 1 }
}
pub fn decode_rgb(avif_bytes: &[u8]) -> io::Result<RgbPixels> {
unsafe {
let mut raw = sys::avifROData {
data: avif_bytes.as_ptr(),
size: avif_bytes.len(),
};
let image = sys::avifImageCreateEmpty();
let decoder = sys::avifDecoderCreate();
let result = sys::avifDecoderRead(decoder, image, &mut raw);
sys::avifDecoderDestroy(decoder);
if result != sys::AVIF_RESULT_OK {
sys::avifImageDestroy(image);
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("result={}", result),
));
}
let mut rgb = sys::avifRGBImage::default();
let raw_rgb = &mut rgb as *mut sys::avifRGBImage;
sys::avifRGBImageSetDefaults(raw_rgb, image);
rgb.format = sys::AVIF_RGB_FORMAT_RGBA;
rgb.depth = 8;
sys::avifRGBImageAllocatePixels(raw_rgb);
sys::avifImageYUVToRGB(image, raw_rgb);
sys::avifImageDestroy(image);
Ok(RgbPixels { rgb })
}
}
pub fn encode_rgb8(width: u32, height: u32, rgb: &[u8]) -> io::Result<AvifData<'static>> {
let (stride, format) = if (width * height * 3) as usize == rgb.len() {
(3, sys::AVIF_RGB_FORMAT_RGB)
} else if (width * height * 4) as usize == rgb.len() {
(4, sys::AVIF_RGB_FORMAT_RGBA)
} else {
panic!("invalid rgb len")
};
unsafe {
let image = sys::avifImageCreate(width as _, height as _, 8, sys::AVIF_PIXEL_FORMAT_YUV444);
sys::avifImageAllocatePlanes(image, sys::AVIF_PLANES_YUV as _);
let mut rgb = sys::avifRGBImage {
width,
height,
depth: 8,
format,
pixels: rgb.as_ptr() as *mut u8,
rowBytes: stride * width,
};
sys::avifImageRGBToYUV(image, &mut rgb);
let mut encoder = sys::avifEncoderCreate();
(*encoder).maxThreads = 1;
(*encoder).minQuantizer = 5;
(*encoder).maxQuantizer = 40;
let mut raw = Default::default();
let result = sys::avifEncoderWrite(encoder, image, &mut raw);
sys::avifEncoderDestroy(encoder);
sys::avifImageDestroy(image);
if result != sys::AVIF_RESULT_OK {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("result={}", result),
));
}
Ok(AvifData::from(raw))
}
}