Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ num-traits = { version = "0.2.0" }

# Optional dependencies
color_quant = { version = "1.1", optional = true }
dav1d = { version = "0.11", optional = true }
rav1d = { git = "https://github.com/leo030303/rav1d.git", branch = "add-rust-api", optional = true }
exr = { version = "1.74.0", default-features = false, optional = true }
gif = { version = "0.14.1", optional = true }
image-webp = { version = "0.2.0", optional = true }
Expand Down Expand Up @@ -98,7 +98,7 @@ webp = ["dep:image-webp"]
rayon = ["dep:rayon", "ravif?/threading", "exr?/rayon"] # Enables multi-threading
nasm = ["ravif?/asm"] # Enables use of nasm by rav1e (requires nasm to be installed)
color_quant = ["dep:color_quant"] # Enables color quantization
avif-native = ["dep:mp4parse", "dep:dav1d"] # Enable native dependency libdav1d
avif-native = ["dep:mp4parse", "dep:rav1d"] # Enable native dependency libdav1d
benchmarks = [] # Build some inline benchmarks. Useful only during development (requires nightly Rust)
serde = ["dep:serde"]

Expand Down
111 changes: 50 additions & 61 deletions src/codecs/avif/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ use crate::codecs::avif::ycgco::{
ycgco444_to_rgba8,
};
use crate::codecs::avif::yuv::*;
use dav1d::{PixelLayout, PlanarImageComponent};
use mp4parse::{read_avif, ImageMirror, ImageRotation, ParseStrictness};
use rav1d::rust_api::pixel::MatrixCoefficients;
use rav1d::rust_api::Picture as Rav1dPicture;
use rav1d::rust_api::PlanarImageComponent;
use rav1d::PixelLayout;

fn error_map<E: Into<Box<dyn Error + Send + Sync>>>(err: E) -> ImageError {
ImageError::Decoding(DecodingError::new(ImageFormat::Avif.into(), err))
Expand All @@ -32,8 +35,8 @@ fn error_map<E: Into<Box<dyn Error + Send + Sync>>>(err: E) -> ImageError {
/// Reads one image into the chosen input.
pub struct AvifDecoder<R> {
inner: PhantomData<R>,
picture: dav1d::Picture,
alpha_picture: Option<dav1d::Picture>,
picture: Rav1dPicture,
alpha_picture: Option<Rav1dPicture>,
icc_profile: Option<Vec<u8>>,
orientation: Orientation,
}
Expand Down Expand Up @@ -81,16 +84,16 @@ impl<R: Read> AvifDecoder<R> {
let ctx = read_avif(&mut r, ParseStrictness::Permissive).map_err(error_map)?;
let coded = ctx.primary_item_coded_data().unwrap_or_default();

let mut primary_decoder = dav1d::Decoder::new().map_err(error_map)?;
let mut primary_decoder = rav1d::rust_api::Decoder::new().map_err(error_map)?;
primary_decoder
.send_data(coded.to_vec(), None, None, None)
.send_data(coded.to_vec().into_boxed_slice(), None, None, None)
.map_err(error_map)?;
let picture = read_until_ready(&mut primary_decoder)?;
let alpha_item = ctx.alpha_item_coded_data().unwrap_or_default();
let alpha_picture = if !alpha_item.is_empty() {
let mut alpha_decoder = dav1d::Decoder::new().map_err(error_map)?;
let mut alpha_decoder = rav1d::rust_api::Decoder::new().map_err(error_map)?;
alpha_decoder
.send_data(alpha_item.to_vec(), None, None, None)
.send_data(alpha_item.to_vec().into_boxed_slice(), None, None, None)
.map_err(error_map)?;
Some(read_until_ready(&mut alpha_decoder)?)
} else {
Expand Down Expand Up @@ -193,15 +196,14 @@ impl Default for Plane16View<'_> {

/// This is correct to transmute FFI data for Y plane and Alpha plane
fn transmute_y_plane16(
plane: &dav1d::Plane,
plane_ref: &[u8],
stride: usize,
width: usize,
height: usize,
) -> Plane16View<'_> {
let mut y_plane_stride = stride >> 1;

let mut bind_y = vec![];
let plane_ref = plane.as_ref();

let mut shape_y_plane = || {
y_plane_stride = width;
Expand Down Expand Up @@ -233,13 +235,12 @@ fn transmute_y_plane16(

/// This is correct to transmute FFI data for Y plane and Alpha plane
fn transmute_chroma_plane16(
plane: &dav1d::Plane,
plane_ref: &[u8],
pixel_layout: PixelLayout,
stride: usize,
width: usize,
height: usize,
) -> Plane16View<'_> {
let plane_ref = plane.as_ref();
let mut chroma_plane_stride = stride >> 1;
let mut bind_chroma = vec![];

Expand Down Expand Up @@ -288,46 +289,32 @@ enum YuvMatrixStrategy {
}

/// Getting one of prebuilt matrix of fails
fn get_matrix(
david_matrix: dav1d::pixel::MatrixCoefficients,
) -> Result<YuvMatrixStrategy, ImageError> {
fn get_matrix(david_matrix: MatrixCoefficients) -> Result<YuvMatrixStrategy, ImageError> {
match david_matrix {
dav1d::pixel::MatrixCoefficients::Identity => Ok(YuvMatrixStrategy::Identity),
dav1d::pixel::MatrixCoefficients::BT709 => {
Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt709))
}
MatrixCoefficients::Identity => Ok(YuvMatrixStrategy::Identity),
MatrixCoefficients::BT709 => Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt709)),
// This is arguable, some applications prefer to go with Bt.709 as default,
// and some applications prefer Bt.601 as default.
// For ex. `Chrome` always prefer Bt.709 even for SD content
// However, nowadays standard should be Bt.709 for HD+ size otherwise Bt.601
dav1d::pixel::MatrixCoefficients::Unspecified => {
Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt709))
}
dav1d::pixel::MatrixCoefficients::Reserved => Err(ImageError::Unsupported(
MatrixCoefficients::Unspecified => Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt709)),
MatrixCoefficients::Reserved => Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Avif.into(),
UnsupportedErrorKind::GenericFeature(
"Using 'Reserved' color matrix is not supported".to_string(),
),
),
)),
dav1d::pixel::MatrixCoefficients::BT470M => {
Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt470_6))
}
dav1d::pixel::MatrixCoefficients::BT470BG => {
Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt601))
}
dav1d::pixel::MatrixCoefficients::ST170M => {
Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Smpte240))
}
dav1d::pixel::MatrixCoefficients::ST240M => {
Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Smpte240))
}
dav1d::pixel::MatrixCoefficients::YCgCo => Ok(YuvMatrixStrategy::CgCo),
dav1d::pixel::MatrixCoefficients::BT2020NonConstantLuminance => {
MatrixCoefficients::BT470M => Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt470_6)),
MatrixCoefficients::BT470BG => Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt601)),
MatrixCoefficients::ST170M => Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Smpte240)),
MatrixCoefficients::ST240M => Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Smpte240)),
MatrixCoefficients::YCgCo => Ok(YuvMatrixStrategy::CgCo),
MatrixCoefficients::BT2020NonConstantLuminance => {
Ok(YuvMatrixStrategy::KrKb(YuvStandardMatrix::Bt2020))
}
dav1d::pixel::MatrixCoefficients::BT2020ConstantLuminance => {
MatrixCoefficients::BT2020ConstantLuminance => {
// This matrix significantly differs from others because linearize values is required
// to compute Y instead of Y'.
// Actually it is almost everywhere is not implemented.
Expand All @@ -342,22 +329,22 @@ fn get_matrix(
),
))
}
dav1d::pixel::MatrixCoefficients::ST2085 => Err(ImageError::Unsupported(
MatrixCoefficients::ST2085 => Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Avif.into(),
UnsupportedErrorKind::GenericFeature("ST2085 matrix is not supported".to_string()),
),
)),
dav1d::pixel::MatrixCoefficients::ChromaticityDerivedConstantLuminance
| dav1d::pixel::MatrixCoefficients::ChromaticityDerivedNonConstantLuminance => Err(
MatrixCoefficients::ChromaticityDerivedConstantLuminance
| MatrixCoefficients::ChromaticityDerivedNonConstantLuminance => Err(
ImageError::Unsupported(UnsupportedError::from_format_and_kind(
ImageFormat::Avif.into(),
UnsupportedErrorKind::GenericFeature(
"Chromaticity Derived Luminance matrix is not supported".to_string(),
),
)),
),
dav1d::pixel::MatrixCoefficients::ICtCp => Err(ImageError::Unsupported(
MatrixCoefficients::ICtCp => Err(ImageError::Unsupported(
UnsupportedError::from_format_and_kind(
ImageFormat::Avif.into(),
UnsupportedErrorKind::GenericFeature(
Expand Down Expand Up @@ -407,8 +394,8 @@ impl<R: Read> ImageDecoder for AvifDecoder<R> {
}

let yuv_range = match self.picture.color_range() {
dav1d::pixel::YUVRange::Limited => YuvIntensityRange::Tv,
dav1d::pixel::YUVRange::Full => YuvIntensityRange::Pc,
rav1d::rust_api::pixel::YUVRange::Limited => YuvIntensityRange::Tv,
rav1d::rust_api::pixel::YUVRange::Full => YuvIntensityRange::Pc,
};

let matrix_strategy = get_matrix(self.picture.matrix_coefficients())?;
Expand Down Expand Up @@ -436,16 +423,16 @@ impl<R: Read> ImageDecoder for AvifDecoder<R> {
}

if bit_depth == 8 {
let ref_y = self.picture.plane(PlanarImageComponent::Y);
let ref_u = self.picture.plane(PlanarImageComponent::U);
let ref_v = self.picture.plane(PlanarImageComponent::V);
let ref_y = self.picture.plane_data(PlanarImageComponent::Y);
let ref_u = self.picture.plane_data(PlanarImageComponent::U);
let ref_v = self.picture.plane_data(PlanarImageComponent::V);

let image = YuvPlanarImage {
y_plane: ref_y.as_ref(),
y_plane: ref_y,
y_stride: self.picture.stride(PlanarImageComponent::Y) as usize,
u_plane: ref_u.as_ref(),
u_plane: ref_u,
u_stride: self.picture.stride(PlanarImageComponent::U) as usize,
v_plane: ref_v.as_ref(),
v_plane: ref_v,
v_stride: self.picture.stride(PlanarImageComponent::V) as usize,
width: width as usize,
height: height as usize,
Expand Down Expand Up @@ -494,7 +481,7 @@ impl<R: Read> ImageDecoder for AvifDecoder<R> {
}

let stride = picture.stride(PlanarImageComponent::Y) as usize;
let plane = picture.plane(PlanarImageComponent::Y);
let plane = picture.plane_data(PlanarImageComponent::Y);

for (buf, slice) in Iterator::zip(
buf.chunks_exact_mut(width as usize * 4),
Expand Down Expand Up @@ -536,7 +523,7 @@ impl<R: Read> AvifDecoder<R> {
yuv_range: YuvIntensityRange,
matrix_strategy: YuvMatrixStrategy,
) -> ImageResult<()> {
let y_dav1d_plane = self.picture.plane(PlanarImageComponent::Y);
let y_dav1d_plane = self.picture.plane_data(PlanarImageComponent::Y);

let (width, height) = (self.picture.width(), self.picture.height());
let bit_depth = self.picture.bit_depth();
Expand All @@ -547,27 +534,27 @@ impl<R: Read> AvifDecoder<R> {
// required criteria: bytemuck allows this align of this data, and stride must be dividable by 2

let y_plane_view = transmute_y_plane16(
&y_dav1d_plane,
y_dav1d_plane,
self.picture.stride(PlanarImageComponent::Y) as usize,
width as usize,
height as usize,
);

let u_dav1d_plane = self.picture.plane(PlanarImageComponent::U);
let v_dav1d_plane = self.picture.plane(PlanarImageComponent::V);
let u_dav1d_plane = self.picture.plane_data(PlanarImageComponent::U);
let v_dav1d_plane = self.picture.plane_data(PlanarImageComponent::V);
let mut u_plane_view = Plane16View::default();
let mut v_plane_view = Plane16View::default();

if self.picture.pixel_layout() != PixelLayout::I400 {
u_plane_view = transmute_chroma_plane16(
&u_dav1d_plane,
u_dav1d_plane,
self.picture.pixel_layout(),
self.picture.stride(PlanarImageComponent::U) as usize,
width as usize,
height as usize,
);
v_plane_view = transmute_chroma_plane16(
&v_dav1d_plane,
v_dav1d_plane,
self.picture.pixel_layout(),
self.picture.stride(PlanarImageComponent::V) as usize,
width as usize,
Expand Down Expand Up @@ -673,9 +660,9 @@ impl<R: Read> AvifDecoder<R> {
)));
}

let a_dav1d_plane = picture.plane(PlanarImageComponent::Y);
let a_dav1d_plane = picture.plane_data(PlanarImageComponent::Y);
let a_plane_view = transmute_y_plane16(
&a_dav1d_plane,
a_dav1d_plane,
picture.stride(PlanarImageComponent::Y) as usize,
width as usize,
height as usize,
Expand Down Expand Up @@ -704,12 +691,14 @@ impl<R: Read> AvifDecoder<R> {
/// `get_picture` and `send_pending_data` yield `Again` as a non-fatal error requesting more data is sent to the decoder
/// This ensures that in the case of `Again` all pending data is submitted
/// This should be called after `send_data` (which does not yield `Again` when called the first time)
fn read_until_ready(decoder: &mut dav1d::Decoder) -> ImageResult<dav1d::Picture> {
fn read_until_ready(
decoder: &mut rav1d::rust_api::Decoder,
) -> ImageResult<rav1d::rust_api::Picture> {
loop {
match decoder.get_picture() {
Err(dav1d::Error::Again) => match decoder.send_pending_data() {
Err(rav1d::Rav1dError::TryAgain) => match decoder.send_pending_data() {
Ok(()) => {}
Err(dav1d::Error::Again) => {}
Err(rav1d::Rav1dError::TryAgain) => {}
Err(e) => return Err(error_map(e)),
},
r => return r.map_err(error_map),
Expand Down
Loading