1use imgref::Img;
2use rgb::RGBA8;
3
4use crate::error::{Error, Result};
5use crate::format::Format;
6
7use super::{Codec, EncodeOptions, ImageData};
8
9pub struct AvifCodec;
11
12impl Codec for AvifCodec {
13 fn format(&self) -> Format {
14 Format::Avif
15 }
16
17 fn decode(&self, data: &[u8]) -> Result<ImageData> {
18 let img = image::load_from_memory_with_format(data, image::ImageFormat::Avif)
19 .map_err(|e| Error::Decode(format!("avif decode: {e}")))?;
20
21 let rgba = img.to_rgba8();
22 let width = rgba.width();
23 let height = rgba.height();
24
25 Ok(ImageData::new(width, height, rgba.into_raw()))
26 }
27
28 fn encode(&self, image: &ImageData, options: &EncodeOptions) -> Result<Vec<u8>> {
29 let width = image.width as usize;
30 let height = image.height as usize;
31
32 let pixels: Vec<RGBA8> = image
34 .data
35 .chunks_exact(4)
36 .map(|px| RGBA8::new(px[0], px[1], px[2], px[3]))
37 .collect();
38
39 let buffer = Img::new(pixels.as_slice(), width, height);
40
41 let encoded = ravif::Encoder::new()
42 .with_quality(options.quality as f32)
43 .with_speed(6)
44 .encode_rgba(buffer)
45 .map_err(|e| Error::Encode(format!("ravif encode: {e}")))?;
46
47 Ok(encoded.avif_file)
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 fn create_test_image(width: u32, height: u32) -> ImageData {
56 let size = (width * height * 4) as usize;
57 let mut data = vec![0u8; size];
58 for y in 0..height {
59 for x in 0..width {
60 let i = ((y * width + x) * 4) as usize;
61 data[i] = (x * 255 / width) as u8; data[i + 1] = (y * 255 / height) as u8; data[i + 2] = 128; data[i + 3] = 255; }
66 }
67 ImageData::new(width, height, data)
68 }
69
70 #[test]
71 fn encode_produces_valid_avif() {
72 let codec = AvifCodec;
73 let image = create_test_image(64, 48);
74 let options = EncodeOptions { quality: 80 };
75
76 let encoded = codec.encode(&image, &options).expect("encode failed");
77
78 assert!(
80 encoded.len() >= 8,
81 "encoded data too short: {} bytes",
82 encoded.len()
83 );
84 assert_eq!(&encoded[4..8], b"ftyp", "missing AVIF ftyp box");
85 }
86
87 #[test]
88 fn encode_and_decode_roundtrip() {
89 let codec = AvifCodec;
90 let original = create_test_image(64, 48);
91 let options = EncodeOptions { quality: 80 };
92
93 let encoded = codec.encode(&original, &options).expect("encode failed");
94 let decoded = codec.decode(&encoded).expect("decode failed");
95
96 assert_eq!(decoded.width, original.width);
97 assert_eq!(decoded.height, original.height);
98 assert_eq!(
99 decoded.data.len(),
100 (decoded.width * decoded.height * 4) as usize
101 );
102 }
103}