use std::io::Read;
use image::error::{DecodingError, ImageFormatHint};
use image::{ColorType, ImageError, ImageDecoder, ImageResult};
use crate::error::AvifError;
use crate::ffi;
use crate::info::BitDepth;
use crate::sys;
#[derive(Default)]
pub struct DecoderConfig {
pub threads: Option<u32>,
}
pub struct AvifDecoder<R: Read> {
decoder: *mut sys::avifDecoder,
_data: Vec<u8>,
config: DecoderConfig,
width: u32,
height: u32,
depth: u32,
alpha_present: bool,
_reader: std::marker::PhantomData<R>,
}
impl<R: Read> AvifDecoder<R> {
pub fn new(mut r: R) -> ImageResult<Self> {
let mut data = Vec::new();
r.read_to_end(&mut data).map_err(ImageError::IoError)?;
unsafe {
let decoder = sys::avifDecoderCreate();
if decoder.is_null() {
return Err(to_image_error(AvifError::DecoderInit("avifDecoderCreate returned null".into())));
}
(*decoder).codecChoice = sys::avifCodecChoice_AVIF_CODEC_CHOICE_DAV1D;
let res = sys::avifDecoderSetIOMemory(decoder, data.as_ptr(), data.len());
if !ffi::is_ok(res) {
sys::avifDecoderDestroy(decoder);
return Err(to_image_error(AvifError::DecoderInit(ffi::result_message(res))));
}
let res = sys::avifDecoderParse(decoder);
if !ffi::is_ok(res) {
sys::avifDecoderDestroy(decoder);
return Err(to_image_error(AvifError::Decode(ffi::result_message(res))));
}
let image = (*decoder).image;
Ok(Self {
decoder,
width: (*image).width,
height: (*image).height,
depth: (*image).depth,
alpha_present: (*decoder).alphaPresent == sys::AVIF_TRUE as sys::avifBool,
_data: data,
config: DecoderConfig::default(),
_reader: std::marker::PhantomData,
})
}
}
pub fn with_threads(mut self, threads: u32) -> Self {
self.config.threads = Some(threads);
self
}
pub fn bit_depth(&self) -> BitDepth {
match self.depth {
12 => BitDepth::Twelve,
10 => BitDepth::Ten,
_ => BitDepth::Eight,
}
}
fn channels(&self) -> usize {
if self.alpha_present { 4 } else { 3 }
}
fn sample_bytes(&self) -> usize {
if self.depth > 8 { 2 } else { 1 }
}
}
impl<R: Read> ImageDecoder for AvifDecoder<R> {
fn dimensions(&self) -> (u32, u32) {
(self.width, self.height)
}
fn color_type(&self) -> ColorType {
match (self.depth > 8, self.alpha_present) {
(false, false) => ColorType::Rgb8,
(false, true) => ColorType::Rgba8,
(true, false) => ColorType::Rgb16,
(true, true) => ColorType::Rgba16,
}
}
fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
let expected = self.width as usize * self.height as usize * self.channels() * self.sample_bytes();
if buf.len() != expected {
return Err(to_image_error(AvifError::Decode(format!(
"output buffer length {} does not match expected {expected}",
buf.len()
))));
}
unsafe {
(*self.decoder).maxThreads = self.config.threads.unwrap_or(0) as i32;
let res = sys::avifDecoderNextImage(self.decoder);
if !ffi::is_ok(res) {
return Err(to_image_error(AvifError::Decode(ffi::result_message(res))));
}
let image = (*self.decoder).image;
let mut rgb: sys::avifRGBImage = std::mem::zeroed();
sys::avifRGBImageSetDefaults(&mut rgb, image);
rgb.format = if self.alpha_present {
sys::avifRGBFormat_AVIF_RGB_FORMAT_RGBA
} else {
sys::avifRGBFormat_AVIF_RGB_FORMAT_RGB
};
rgb.depth = if self.depth > 8 { 16 } else { 8 };
rgb.pixels = buf.as_mut_ptr();
rgb.rowBytes = self.width * (self.channels() * self.sample_bytes()) as u32;
let res = sys::avifImageYUVToRGB(image, &mut rgb);
if !ffi::is_ok(res) {
return Err(to_image_error(AvifError::Decode(ffi::result_message(res))));
}
}
Ok(())
}
fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
self.read_image(buf)
}
}
impl<R: Read> Drop for AvifDecoder<R> {
fn drop(&mut self) {
unsafe { sys::avifDecoderDestroy(self.decoder) };
}
}
fn to_image_error(err: AvifError) -> ImageError {
ImageError::Decoding(DecodingError::new(ImageFormatHint::Name("AVIF".into()), err))
}