Skip to content
Open
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,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/memorysafety/rav1d", 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 @@ -105,7 +105,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
98 changes: 45 additions & 53 deletions src/codecs/avif/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,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 @@ -33,8 +36,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,
limits: Limits,
Expand Down Expand Up @@ -87,16 +90,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 @@ -223,7 +226,7 @@ impl Default for Plane16View<'_> {

/// This is correct to transmute FFI data for Y plane and Alpha plane
fn transmute_y_plane16<'data>(
plane: &'data dav1d::Plane,
plane_ref: &'data [u8],
stride: u32,
width: u32,
height: u32,
Expand All @@ -232,7 +235,6 @@ fn transmute_y_plane16<'data>(
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 @@ -265,14 +267,13 @@ fn transmute_y_plane16<'data>(

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

Expand Down Expand Up @@ -330,46 +331,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 @@ -384,22 +371,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 All @@ -412,6 +399,9 @@ fn get_matrix(

impl<R: Read> ImageDecoder for AvifDecoder<R> {
fn set_limits(&mut self, limits: Limits) -> ImageResult<()> {
limits.check_support(&crate::LimitSupport::default())?;
let layout = self.prepare_image()?;
limits.check_layout_dimensions(&layout)?;
self.limits = limits;
Ok(())
}
Expand Down Expand Up @@ -453,8 +443,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 @@ -487,11 +477,11 @@ impl<R: Read> ImageDecoder for AvifDecoder<R> {
let ref_v = self.picture.plane(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 @@ -592,7 +582,7 @@ 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),
width,
height,
Expand All @@ -606,15 +596,15 @@ impl<R: Read> AvifDecoder<R> {

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),
width,
height,
&mut self.limits,
)?;
v_plane_view = transmute_chroma_plane16(
&v_dav1d_plane,
v_dav1d_plane,
self.picture.pixel_layout(),
self.picture.stride(PlanarImageComponent::V),
width,
Expand Down Expand Up @@ -723,7 +713,7 @@ impl<R: Read> AvifDecoder<R> {

let a_dav1d_plane = picture.plane(PlanarImageComponent::Y);
let a_plane_view = transmute_y_plane16(
&a_dav1d_plane,
a_dav1d_plane,
picture.stride(PlanarImageComponent::Y),
width,
height,
Expand Down Expand Up @@ -753,12 +743,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
30 changes: 15 additions & 15 deletions src/codecs/avif/ycgco.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,13 +286,13 @@ where
macro_rules! define_ycgco_half_chroma {
($name: ident, $invoker: ident, $storage: ident, $cn: expr, $bp: expr, $description: expr) => {
#[doc = concat!($description, "
# Arguments
* `image`: see [YuvPlanarImage]
* `rgb`: RGB image layout
* `range`: see [YuvIntensityRange]
* `matrix`: see [YuvStandardMatrix]")]

# Arguments

* `image`: see [YuvPlanarImage]
* `rgb`: RGB image layout
* `range`: see [YuvIntensityRange]
* `matrix`: see [YuvStandardMatrix]")]
pub(crate) fn $name(
image: YuvPlanarImage<$storage>,
rgb: &mut [$storage],
Expand Down Expand Up @@ -369,14 +369,14 @@ define_ycgco_half_chroma!(
macro_rules! define_ycgcg_full_chroma {
($name: ident, $storage: ident, $cn: expr, $bp: expr, $description: expr) => {
#[doc = concat!($description, "
# Arguments
* `image`: see [YuvPlanarImage]
* `rgba`: RGB image layout
* `range`: see [YuvIntensityRange]
* `matrix`: see [YuvStandardMatrix]
")]

# Arguments

* `image`: see [YuvPlanarImage]
* `rgba`: RGB image layout
* `range`: see [YuvIntensityRange]
* `matrix`: see [YuvStandardMatrix]
")]
pub(crate) fn $name(
image: YuvPlanarImage<$storage>,
rgba: &mut [$storage],
Expand Down
Loading