use super::{
GeometryOverflow, InsufficientPlane, InsufficientStride, UnsupportedBits, WidthAlignment,
ZeroDimension,
};
use derive_more::{Display, IsVariant, TryUnwrap, Unwrap};
use thiserror::Error;
#[derive(Debug, Clone, Copy)]
pub struct PnFrame<'a, const BITS: u32, const BE: bool = false> {
y: &'a [u16],
uv: &'a [u16],
width: u32,
height: u32,
y_stride: u32,
uv_stride: u32,
}
impl<'a, const BITS: u32, const BE: bool> PnFrame<'a, BITS, 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, PnFrameError> {
if BITS != 10 && BITS != 12 && BITS != 16 {
return Err(PnFrameError::UnsupportedBits(UnsupportedBits::new(BITS)));
}
if width == 0 || height == 0 {
return Err(PnFrameError::ZeroDimension(ZeroDimension::new(
width, height,
)));
}
if width & 1 != 0 {
return Err(PnFrameError::WidthAlignment(WidthAlignment::odd(
width as usize,
)));
}
if y_stride < width {
return Err(PnFrameError::InsufficientYStride(InsufficientStride::new(
y_stride, width,
)));
}
let uv_row_elems = width;
if uv_stride < uv_row_elems {
return Err(PnFrameError::InsufficientUvStride(InsufficientStride::new(
uv_stride,
uv_row_elems,
)));
}
if uv_stride & 1 != 0 {
return Err(PnFrameError::UvStrideOdd(PnUvStrideOdd::new(uv_stride)));
}
let y_min = match (y_stride as usize).checked_mul(height as usize) {
Some(v) => v,
None => {
return Err(PnFrameError::GeometryOverflow(GeometryOverflow::new(
y_stride, height,
)));
}
};
if y.len() < y_min {
return Err(PnFrameError::InsufficientYPlane(InsufficientPlane::new(
y_min,
y.len(),
)));
}
let chroma_height = height.div_ceil(2);
let uv_min = match (uv_stride as usize).checked_mul(chroma_height as usize) {
Some(v) => v,
None => {
return Err(PnFrameError::GeometryOverflow(GeometryOverflow::new(
uv_stride,
chroma_height,
)));
}
};
if uv.len() < uv_min {
return Err(PnFrameError::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 PnFrame dimensions, plane lengths, or BITS value"),
}
}
#[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, PnFrameError> {
let frame = Self::try_new(y, uv, width, height, y_stride, uv_stride)?;
let low_bits = 16 - BITS;
let low_mask: u16 = ((1u32 << low_bits) - 1) as u16;
let w = width as usize;
let h = height as usize;
let uv_w = w; let chroma_h = height.div_ceil(2) as usize;
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 & low_mask != 0 {
return Err(PnFrameError::SampleLowBitsSet(PnSampleLowBitsSet::new(
PnFramePlane::Y,
start + col,
logical,
low_bits,
)));
}
}
}
for row in 0..chroma_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 & low_mask != 0 {
return Err(PnFrameError::SampleLowBitsSet(PnSampleLowBitsSet::new(
PnFramePlane::Uv,
start + col,
logical,
low_bits,
)));
}
}
}
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 bits(&self) -> u32 {
BITS
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn is_be(&self) -> bool {
BE
}
}
pub type P010Frame<'a> = PnFrame<'a, 10>;
pub type P012Frame<'a> = PnFrame<'a, 12>;
pub type P016Frame<'a> = PnFrame<'a, 16>;
pub type P010LeFrame<'a> = PnFrame<'a, 10, false>;
pub type P010BeFrame<'a> = PnFrame<'a, 10, true>;
pub type P012LeFrame<'a> = PnFrame<'a, 12, false>;
pub type P012BeFrame<'a> = PnFrame<'a, 12, true>;
pub type P016LeFrame<'a> = PnFrame<'a, 16, false>;
pub type P016BeFrame<'a> = PnFrame<'a, 16, true>;
#[derive(Debug, Clone, Copy)]
pub struct PnFrame422<'a, const BITS: u32, const BE: bool = false> {
y: &'a [u16],
uv: &'a [u16],
width: u32,
height: u32,
y_stride: u32,
uv_stride: u32,
}
impl<'a, const BITS: u32, const BE: bool> PnFrame422<'a, BITS, 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, PnFrameError> {
if BITS != 10 && BITS != 12 && BITS != 16 {
return Err(PnFrameError::UnsupportedBits(UnsupportedBits::new(BITS)));
}
if width == 0 || height == 0 {
return Err(PnFrameError::ZeroDimension(ZeroDimension::new(
width, height,
)));
}
if width & 1 != 0 {
return Err(PnFrameError::WidthAlignment(WidthAlignment::odd(
width as usize,
)));
}
if y_stride < width {
return Err(PnFrameError::InsufficientYStride(InsufficientStride::new(
y_stride, width,
)));
}
let uv_row_elems = width;
if uv_stride < uv_row_elems {
return Err(PnFrameError::InsufficientUvStride(InsufficientStride::new(
uv_stride,
uv_row_elems,
)));
}
if uv_stride & 1 != 0 {
return Err(PnFrameError::UvStrideOdd(PnUvStrideOdd::new(uv_stride)));
}
let y_min = match (y_stride as usize).checked_mul(height as usize) {
Some(v) => v,
None => {
return Err(PnFrameError::GeometryOverflow(GeometryOverflow::new(
y_stride, height,
)));
}
};
if y.len() < y_min {
return Err(PnFrameError::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(PnFrameError::GeometryOverflow(GeometryOverflow::new(
uv_stride, height,
)));
}
};
if uv.len() < uv_min {
return Err(PnFrameError::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 PnFrame422 dimensions, plane lengths, or BITS value"),
}
}
#[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, PnFrameError> {
let frame = Self::try_new(y, uv, width, height, y_stride, uv_stride)?;
if BITS == 16 {
return Ok(frame);
}
let low_bits = 16 - BITS;
let low_mask: u16 = ((1u32 << low_bits) - 1) as u16;
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 & low_mask != 0 {
return Err(PnFrameError::SampleLowBitsSet(PnSampleLowBitsSet::new(
PnFramePlane::Y,
start + col,
logical,
low_bits,
)));
}
}
}
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 & low_mask != 0 {
return Err(PnFrameError::SampleLowBitsSet(PnSampleLowBitsSet::new(
PnFramePlane::Uv,
start + col,
logical,
low_bits,
)));
}
}
}
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 bits(&self) -> u32 {
BITS
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn is_be(&self) -> bool {
BE
}
}
pub type P210Frame<'a> = PnFrame422<'a, 10>;
pub type P212Frame<'a> = PnFrame422<'a, 12>;
pub type P216Frame<'a> = PnFrame422<'a, 16>;
pub type P210LeFrame<'a> = PnFrame422<'a, 10, false>;
pub type P210BeFrame<'a> = PnFrame422<'a, 10, true>;
pub type P212LeFrame<'a> = PnFrame422<'a, 12, false>;
pub type P212BeFrame<'a> = PnFrame422<'a, 12, true>;
pub type P216LeFrame<'a> = PnFrame422<'a, 16, false>;
pub type P216BeFrame<'a> = PnFrame422<'a, 16, true>;
#[derive(Debug, Clone, Copy)]
pub struct PnFrame444<'a, const BITS: u32, const BE: bool = false> {
y: &'a [u16],
uv: &'a [u16],
width: u32,
height: u32,
y_stride: u32,
uv_stride: u32,
}
impl<'a, const BITS: u32, const BE: bool> PnFrame444<'a, BITS, 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, PnFrameError> {
if BITS != 10 && BITS != 12 && BITS != 16 {
return Err(PnFrameError::UnsupportedBits(UnsupportedBits::new(BITS)));
}
if width == 0 || height == 0 {
return Err(PnFrameError::ZeroDimension(ZeroDimension::new(
width, height,
)));
}
if y_stride < width {
return Err(PnFrameError::InsufficientYStride(InsufficientStride::new(
y_stride, width,
)));
}
let uv_row_elems = match width.checked_mul(2) {
Some(v) => v,
None => {
return Err(PnFrameError::GeometryOverflow(GeometryOverflow::new(
width, 2,
)));
}
};
if uv_stride < uv_row_elems {
return Err(PnFrameError::InsufficientUvStride(InsufficientStride::new(
uv_stride,
uv_row_elems,
)));
}
if uv_stride & 1 != 0 {
return Err(PnFrameError::UvStrideOdd(PnUvStrideOdd::new(uv_stride)));
}
let y_min = match (y_stride as usize).checked_mul(height as usize) {
Some(v) => v,
None => {
return Err(PnFrameError::GeometryOverflow(GeometryOverflow::new(
y_stride, height,
)));
}
};
if y.len() < y_min {
return Err(PnFrameError::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(PnFrameError::GeometryOverflow(GeometryOverflow::new(
uv_stride, height,
)));
}
};
if uv.len() < uv_min {
return Err(PnFrameError::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 PnFrame444 dimensions, plane lengths, or BITS value"),
}
}
#[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, PnFrameError> {
let frame = Self::try_new(y, uv, width, height, y_stride, uv_stride)?;
if BITS == 16 {
return Ok(frame);
}
let low_bits = 16 - BITS;
let low_mask: u16 = ((1u32 << low_bits) - 1) as u16;
let w = width as usize;
let h = height as usize;
let uv_w = 2 * 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 & low_mask != 0 {
return Err(PnFrameError::SampleLowBitsSet(PnSampleLowBitsSet::new(
PnFramePlane::Y,
start + col,
logical,
low_bits,
)));
}
}
}
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 & low_mask != 0 {
return Err(PnFrameError::SampleLowBitsSet(PnSampleLowBitsSet::new(
PnFramePlane::Uv,
start + col,
logical,
low_bits,
)));
}
}
}
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 bits(&self) -> u32 {
BITS
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn is_be(&self) -> bool {
BE
}
}
pub type P410Frame<'a> = PnFrame444<'a, 10>;
pub type P412Frame<'a> = PnFrame444<'a, 12>;
pub type P416Frame<'a> = PnFrame444<'a, 16>;
pub type P410LeFrame<'a> = PnFrame444<'a, 10, false>;
pub type P410BeFrame<'a> = PnFrame444<'a, 10, true>;
pub type P412LeFrame<'a> = PnFrame444<'a, 12, false>;
pub type P412BeFrame<'a> = PnFrame444<'a, 12, true>;
pub type P416LeFrame<'a> = PnFrame444<'a, 16, false>;
pub type P416BeFrame<'a> = PnFrame444<'a, 16, true>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display)]
pub enum PnFramePlane {
Y,
Uv,
}
pub type P010FramePlane = PnFramePlane;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, IsVariant, TryUnwrap, Unwrap, Error)]
#[non_exhaustive]
#[unwrap(ref, ref_mut)]
#[try_unwrap(ref, ref_mut)]
pub enum PnFrameError {
#[error(transparent)]
UnsupportedBits(UnsupportedBits),
#[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(PnUvStrideOdd),
#[error(transparent)]
InsufficientYPlane(InsufficientPlane),
#[error(transparent)]
InsufficientUvPlane(InsufficientPlane),
#[error(transparent)]
GeometryOverflow(GeometryOverflow),
#[error(
"sample {:#06x} on plane {} at element {} has non-zero low {} bits (not a valid Pn sample at the declared BITS)", .0.value(), .0.plane(), .0.index(), .0.low_bits()
)]
SampleLowBitsSet(PnSampleLowBitsSet),
}
pub type P010FrameError = PnFrameError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PnSampleLowBitsSet {
plane: PnFramePlane,
index: usize,
value: u16,
low_bits: u32,
}
impl PnSampleLowBitsSet {
#[inline]
pub const fn new(plane: PnFramePlane, index: usize, value: u16, low_bits: u32) -> Self {
Self {
plane,
index,
value,
low_bits,
}
}
#[inline]
pub const fn plane(&self) -> PnFramePlane {
self.plane
}
#[inline]
pub const fn index(&self) -> usize {
self.index
}
#[inline]
pub const fn value(&self) -> u16 {
self.value
}
#[inline]
pub const fn low_bits(&self) -> u32 {
self.low_bits
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PnUvStrideOdd {
uv_stride: u32,
}
impl PnUvStrideOdd {
#[inline]
pub const fn new(uv_stride: u32) -> Self {
Self { uv_stride }
}
#[inline]
pub const fn uv_stride(&self) -> u32 {
self.uv_stride
}
}