use super::{
GeometryOverflow, InsufficientPlane, InsufficientStride, WidthAlignment, WidthOverflow,
ZeroDimension,
};
use derive_more::{IsVariant, TryUnwrap, Unwrap};
use thiserror::Error;
#[derive(Debug, Clone, Copy)]
pub struct V210Frame<'a, const BE: bool = false> {
v210: &'a [u8],
width: u32,
height: u32,
stride: u32,
}
pub type V210LeFrame<'a> = V210Frame<'a, false>;
pub type V210BeFrame<'a> = V210Frame<'a, true>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, IsVariant, TryUnwrap, Unwrap, Error)]
#[non_exhaustive]
#[unwrap(ref, ref_mut)]
#[try_unwrap(ref, ref_mut)]
pub enum V210FrameError {
#[error(transparent)]
ZeroDimension(ZeroDimension),
#[error(transparent)]
WidthAlignment(WidthAlignment),
#[error(transparent)]
InsufficientStride(InsufficientStride),
#[error(transparent)]
InsufficientPlane(InsufficientPlane),
#[error(transparent)]
GeometryOverflow(GeometryOverflow),
#[error(transparent)]
WidthOverflow(WidthOverflow),
}
impl<'a, const BE: bool> V210Frame<'a, BE> {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn try_new(
v210: &'a [u8],
width: u32,
height: u32,
stride: u32,
) -> Result<Self, V210FrameError> {
if width == 0 || height == 0 {
return Err(V210FrameError::ZeroDimension(ZeroDimension::new(
width, height,
)));
}
if !width.is_multiple_of(2) {
return Err(V210FrameError::WidthAlignment(WidthAlignment::odd(
width as usize,
)));
}
let words = width.div_ceil(6);
let min_stride = match words.checked_mul(16) {
Some(n) => n,
None => return Err(V210FrameError::WidthOverflow(WidthOverflow::new(width))),
};
if stride < min_stride {
return Err(V210FrameError::InsufficientStride(InsufficientStride::new(
stride, width,
)));
}
let plane_min = match (stride as usize).checked_mul(height as usize) {
Some(n) => n,
None => {
return Err(V210FrameError::GeometryOverflow(GeometryOverflow::new(
stride, height,
)));
}
};
if v210.len() < plane_min {
return Err(V210FrameError::InsufficientPlane(InsufficientPlane::new(
plane_min,
v210.len(),
)));
}
Ok(Self {
v210,
width,
height,
stride,
})
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(v210: &'a [u8], width: u32, height: u32, stride: u32) -> Self {
match Self::try_new(v210, width, height, stride) {
Ok(f) => f,
Err(e) => {
match e {
V210FrameError::ZeroDimension(_) => panic!("invalid V210Frame: zero dimension"),
V210FrameError::WidthAlignment(_) => panic!("invalid V210Frame: odd width"),
V210FrameError::InsufficientStride(_) => panic!("invalid V210Frame: stride too small"),
V210FrameError::InsufficientPlane(_) => panic!("invalid V210Frame: plane too short"),
V210FrameError::GeometryOverflow(_) => {
panic!("invalid V210Frame: geometry overflow")
}
V210FrameError::WidthOverflow(_) => panic!("invalid V210Frame: width overflow"),
}
}
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn v210(&self) -> &'a [u8] {
self.v210
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn width(&self) -> u32 {
self.width
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn height(&self) -> u32 {
self.height
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn stride(&self) -> u32 {
self.stride
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn is_be(&self) -> bool {
BE
}
}