#![forbid(unsafe_code)]
#![allow(clippy::unreadable_literal)]
#![allow(clippy::items_after_statements)]
#![allow(clippy::unnecessary_wraps)]
#![allow(clippy::struct_excessive_bools)]
#![allow(clippy::identity_op)]
#![allow(clippy::range_plus_one)]
#![allow(clippy::needless_range_loop)]
#![allow(clippy::useless_conversion)]
#![allow(clippy::redundant_closure_for_method_calls)]
#![allow(clippy::single_match_else)]
#![allow(dead_code)]
#![allow(clippy::doc_markdown)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::match_same_arms)]
#![allow(clippy::similar_names)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_precision_loss)]
#![allow(clippy::cast_lossless)]
#![allow(clippy::cast_sign_loss)]
mod buffer;
mod cdef_apply;
mod deblock;
mod film_grain;
mod loop_filter;
mod output;
mod pipeline;
mod residual;
mod super_res;
pub use buffer::{BufferPool, FrameBuffer, PlaneBuffer, ReferenceFrameManager};
pub use cdef_apply::{CdefApplicator, CdefBlockConfig, CdefFilterResult};
pub use deblock::{DeblockFilter, DeblockParams, FilterStrength};
pub use film_grain::{FilmGrainParams, FilmGrainSynthesizer, GrainBlock};
pub use loop_filter::{EdgeFilter, FilterDirection, LoopFilterPipeline};
pub use output::{OutputConfig, OutputFormat, OutputFormatter};
pub use pipeline::{DecoderPipeline, FrameContext, PipelineConfig, PipelineStage, StageResult};
pub use residual::{ResidualBuffer, ResidualPlane};
pub use super_res::{SuperResConfig, SuperResUpscaler, UpscaleMethod};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ReconstructionError {
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("Buffer allocation failed: {0}")]
AllocationFailed(String),
#[error("Reference frame not available: index {0}")]
ReferenceNotAvailable(usize),
#[error("Pipeline stage '{stage}' failed: {message}")]
StageError {
stage: String,
message: String,
},
#[error("Invalid dimensions: {width}x{height}")]
InvalidDimensions {
width: u32,
height: u32,
},
#[error("Unsupported bit depth: {0}")]
UnsupportedBitDepth(u8),
#[error("Coefficient overflow at ({x}, {y})")]
CoefficientOverflow {
x: usize,
y: usize,
},
#[error("Filter parameter out of range: {name} = {value}")]
FilterParameterOutOfRange {
name: String,
value: i32,
},
#[error("Internal error: {0}")]
Internal(String),
}
pub type ReconstructResult<T> = Result<T, ReconstructionError>;
pub const MAX_BIT_DEPTH: u8 = 12;
pub const MIN_BIT_DEPTH: u8 = 8;
pub const MAX_FRAME_WIDTH: u32 = 16384;
pub const MAX_FRAME_HEIGHT: u32 = 16384;
pub const NUM_REF_FRAMES: usize = 8;
pub const MAX_SB_SIZE: usize = 128;
pub const MIN_SB_SIZE: usize = 64;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PlaneType {
Y,
U,
V,
}
impl PlaneType {
#[must_use]
pub const fn index(self) -> usize {
match self {
Self::Y => 0,
Self::U => 1,
Self::V => 2,
}
}
#[must_use]
pub const fn is_chroma(self) -> bool {
!matches!(self, Self::Y)
}
#[must_use]
pub const fn all() -> [Self; 3] {
[Self::Y, Self::U, Self::V]
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum ChromaSubsampling {
Cs444,
Cs422,
#[default]
Cs420,
Mono,
}
impl ChromaSubsampling {
#[must_use]
pub const fn ratios(self) -> (u32, u32) {
match self {
Self::Cs444 => (1, 1),
Self::Cs422 => (2, 1),
Self::Cs420 => (2, 2),
Self::Mono => (1, 1),
}
}
#[must_use]
pub const fn num_planes(self) -> usize {
match self {
Self::Mono => 1,
_ => 3,
}
}
#[must_use]
pub fn chroma_size(self, luma_width: u32, luma_height: u32) -> (u32, u32) {
let (h_ratio, v_ratio) = self.ratios();
(luma_width.div_ceil(h_ratio), luma_height.div_ceil(v_ratio))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_plane_type() {
assert_eq!(PlaneType::Y.index(), 0);
assert_eq!(PlaneType::U.index(), 1);
assert_eq!(PlaneType::V.index(), 2);
assert!(!PlaneType::Y.is_chroma());
assert!(PlaneType::U.is_chroma());
assert!(PlaneType::V.is_chroma());
}
#[test]
fn test_chroma_subsampling() {
assert_eq!(ChromaSubsampling::Cs444.ratios(), (1, 1));
assert_eq!(ChromaSubsampling::Cs422.ratios(), (2, 1));
assert_eq!(ChromaSubsampling::Cs420.ratios(), (2, 2));
assert_eq!(ChromaSubsampling::Cs420.num_planes(), 3);
assert_eq!(ChromaSubsampling::Mono.num_planes(), 1);
}
#[test]
fn test_chroma_size_calculation() {
let cs = ChromaSubsampling::Cs420;
assert_eq!(cs.chroma_size(1920, 1080), (960, 540));
assert_eq!(cs.chroma_size(1921, 1081), (961, 541));
}
#[test]
fn test_reconstruction_error_display() {
let err = ReconstructionError::InvalidInput("test".to_string());
assert_eq!(format!("{err}"), "Invalid input: test");
let err = ReconstructionError::ReferenceNotAvailable(3);
assert_eq!(format!("{err}"), "Reference frame not available: index 3");
let err = ReconstructionError::InvalidDimensions {
width: 0,
height: 100,
};
assert_eq!(format!("{err}"), "Invalid dimensions: 0x100");
}
#[test]
fn test_constants() {
assert_eq!(MAX_BIT_DEPTH, 12);
assert_eq!(MIN_BIT_DEPTH, 8);
assert_eq!(NUM_REF_FRAMES, 8);
assert_eq!(MAX_SB_SIZE, 128);
assert_eq!(MIN_SB_SIZE, 64);
}
}