use super::{
GeometryOverflow, InsufficientPlane, InsufficientStride, WidthAlignment, ZeroDimension,
};
use derive_more::{Display, IsVariant, TryUnwrap, Unwrap};
use thiserror::Error;
#[derive(Debug, Clone, Copy)]
pub struct Nv20Frame<'a, const BE: bool = false> {
y: &'a [u16],
uv: &'a [u16],
width: u32,
height: u32,
y_stride: u32,
uv_stride: u32,
}
pub type Nv20LeFrame<'a> = Nv20Frame<'a, false>;
pub type Nv20BeFrame<'a> = Nv20Frame<'a, true>;
impl<'a, const BE: bool> Nv20Frame<'a, BE> {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn try_new(
y: &'a [u16],
uv: &'a [u16],
width: u32,
height: u32,
y_stride: u32,
uv_stride: u32,
) -> Result<Self, Nv20FrameError> {
if width == 0 || height == 0 {
return Err(Nv20FrameError::ZeroDimension(ZeroDimension::new(
width, height,
)));
}
if width & 1 != 0 {
return Err(Nv20FrameError::WidthAlignment(WidthAlignment::odd(
width as usize,
)));
}
if y_stride < width {
return Err(Nv20FrameError::InsufficientYStride(
InsufficientStride::new(y_stride, width),
));
}
let uv_row_elems = width;
if uv_stride < uv_row_elems {
return Err(Nv20FrameError::InsufficientUvStride(
InsufficientStride::new(uv_stride, uv_row_elems),
));
}
if uv_stride & 1 != 0 {
return Err(Nv20FrameError::UvStrideOdd(Nv20UvStrideOdd::new(uv_stride)));
}
let y_min = match (y_stride as usize).checked_mul(height as usize) {
Some(v) => v,
None => {
return Err(Nv20FrameError::GeometryOverflow(GeometryOverflow::new(
y_stride, height,
)));
}
};
if y.len() < y_min {
return Err(Nv20FrameError::InsufficientYPlane(InsufficientPlane::new(
y_min,
y.len(),
)));
}
let uv_min = match (uv_stride as usize).checked_mul(height as usize) {
Some(v) => v,
None => {
return Err(Nv20FrameError::GeometryOverflow(GeometryOverflow::new(
uv_stride, height,
)));
}
};
if uv.len() < uv_min {
return Err(Nv20FrameError::InsufficientUvPlane(InsufficientPlane::new(
uv_min,
uv.len(),
)));
}
Ok(Self {
y,
uv,
width,
height,
y_stride,
uv_stride,
})
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(
y: &'a [u16],
uv: &'a [u16],
width: u32,
height: u32,
y_stride: u32,
uv_stride: u32,
) -> Self {
match Self::try_new(y, uv, width, height, y_stride, uv_stride) {
Ok(frame) => frame,
Err(_) => panic!("invalid Nv20Frame dimensions or plane lengths"),
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn try_new_checked(
y: &'a [u16],
uv: &'a [u16],
width: u32,
height: u32,
y_stride: u32,
uv_stride: u32,
) -> Result<Self, Nv20FrameError> {
let frame = Self::try_new(y, uv, width, height, y_stride, uv_stride)?;
const HIGH_MASK: u16 = 0xFC00;
let w = width as usize;
let h = height as usize;
let uv_w = w; for row in 0..h {
let start = row * y_stride as usize;
for (col, &s) in y[start..start + w].iter().enumerate() {
let logical = if BE { u16::from_be(s) } else { u16::from_le(s) };
if logical & HIGH_MASK != 0 {
return Err(Nv20FrameError::StrayHighBits(Nv20StrayHighBits::new(
Nv20FramePlane::Y,
start + col,
logical,
)));
}
}
}
for row in 0..h {
let start = row * uv_stride as usize;
for (col, &s) in uv[start..start + uv_w].iter().enumerate() {
let logical = if BE { u16::from_be(s) } else { u16::from_le(s) };
if logical & HIGH_MASK != 0 {
return Err(Nv20FrameError::StrayHighBits(Nv20StrayHighBits::new(
Nv20FramePlane::Uv,
start + col,
logical,
)));
}
}
}
Ok(frame)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn y(&self) -> &'a [u16] {
self.y
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn uv(&self) -> &'a [u16] {
self.uv
}
#[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 y_stride(&self) -> u32 {
self.y_stride
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn uv_stride(&self) -> u32 {
self.uv_stride
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn is_be(&self) -> bool {
BE
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display)]
pub enum Nv20FramePlane {
Y,
Uv,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, IsVariant, TryUnwrap, Unwrap, Error)]
#[non_exhaustive]
#[unwrap(ref, ref_mut)]
#[try_unwrap(ref, ref_mut)]
pub enum Nv20FrameError {
#[error(transparent)]
ZeroDimension(ZeroDimension),
#[error(transparent)]
WidthAlignment(WidthAlignment),
#[error(transparent)]
InsufficientYStride(InsufficientStride),
#[error(transparent)]
InsufficientUvStride(InsufficientStride),
#[error(
"uv_stride ({}) is odd; semi-planar interleaved UV requires an even u16-element stride", .0.uv_stride()
)]
UvStrideOdd(Nv20UvStrideOdd),
#[error(transparent)]
InsufficientYPlane(InsufficientPlane),
#[error(transparent)]
InsufficientUvPlane(InsufficientPlane),
#[error(transparent)]
GeometryOverflow(GeometryOverflow),
#[error(
"sample {:#06x} on plane {} at element {} has non-zero high 6 bits (not a valid NV20 low-bit-packed 10-bit sample)", .0.value(), .0.plane(), .0.index()
)]
StrayHighBits(Nv20StrayHighBits),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Nv20UvStrideOdd {
uv_stride: u32,
}
impl Nv20UvStrideOdd {
#[inline]
pub const fn new(uv_stride: u32) -> Self {
Self { uv_stride }
}
#[inline]
pub const fn uv_stride(&self) -> u32 {
self.uv_stride
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Nv20StrayHighBits {
plane: Nv20FramePlane,
index: usize,
value: u16,
}
impl Nv20StrayHighBits {
#[inline]
pub const fn new(plane: Nv20FramePlane, index: usize, value: u16) -> Self {
Self {
plane,
index,
value,
}
}
#[inline]
pub const fn plane(&self) -> Nv20FramePlane {
self.plane
}
#[inline]
pub const fn index(&self) -> usize {
self.index
}
#[inline]
pub const fn value(&self) -> u16 {
self.value
}
}