use super::{
GeometryOverflow, InsufficientPlane, InsufficientStride, UnsupportedBits, ZeroDimension,
};
use derive_more::{IsVariant, TryUnwrap, Unwrap};
use thiserror::Error;
#[derive(Debug, Clone, Copy)]
pub struct BayerFrame<'a> {
data: &'a [u8],
width: u32,
height: u32,
stride: u32,
}
impl<'a> BayerFrame<'a> {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn try_new(
data: &'a [u8],
width: u32,
height: u32,
stride: u32,
) -> Result<Self, BayerFrameError> {
if width == 0 || height == 0 {
return Err(BayerFrameError::ZeroDimension(ZeroDimension::new(
width, height,
)));
}
if stride < width {
return Err(BayerFrameError::InsufficientStride(
InsufficientStride::new(stride, width),
));
}
let min = match (stride as usize).checked_mul(height as usize) {
Some(v) => v,
None => {
return Err(BayerFrameError::GeometryOverflow(GeometryOverflow::new(
stride, height,
)));
}
};
if data.len() < min {
return Err(BayerFrameError::InsufficientPlane(InsufficientPlane::new(
min,
data.len(),
)));
}
Ok(Self {
data,
width,
height,
stride,
})
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(data: &'a [u8], width: u32, height: u32, stride: u32) -> Self {
match Self::try_new(data, width, height, stride) {
Ok(frame) => frame,
Err(_) => panic!("invalid BayerFrame dimensions or plane length"),
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn data(&self) -> &'a [u8] {
self.data
}
#[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
}
}
#[derive(Debug, Clone, Copy)]
pub struct BayerFrame16<'a, const BITS: u32> {
data: &'a [u16],
width: u32,
height: u32,
stride: u32,
}
impl<'a, const BITS: u32> BayerFrame16<'a, BITS> {
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn try_new(
data: &'a [u16],
width: u32,
height: u32,
stride: u32,
) -> Result<Self, BayerFrame16Error> {
if BITS != 10 && BITS != 12 && BITS != 14 && BITS != 16 {
return Err(BayerFrame16Error::UnsupportedBits(UnsupportedBits::new(
BITS,
)));
}
if width == 0 || height == 0 {
return Err(BayerFrame16Error::ZeroDimension(ZeroDimension::new(
width, height,
)));
}
if stride < width {
return Err(BayerFrame16Error::InsufficientStride(
InsufficientStride::new(stride, width),
));
}
let min = match (stride as usize).checked_mul(height as usize) {
Some(v) => v,
None => {
return Err(BayerFrame16Error::GeometryOverflow(GeometryOverflow::new(
stride, height,
)));
}
};
if data.len() < min {
return Err(BayerFrame16Error::InsufficientPlane(
InsufficientPlane::new(min, data.len()),
));
}
let max_valid: u16 = ((1u32 << BITS) - 1) as u16;
let w = width as usize;
let h = height as usize;
for row in 0..h {
let start = row * stride as usize;
for (col, &s) in data[start..start + w].iter().enumerate() {
if s > max_valid {
return Err(BayerFrame16Error::SampleOutOfRange(
BayerSampleOutOfRange::new(start + col, s, max_valid),
));
}
}
}
Ok(Self {
data,
width,
height,
stride,
})
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn new(data: &'a [u16], width: u32, height: u32, stride: u32) -> Self {
match Self::try_new(data, width, height, stride) {
Ok(frame) => frame,
Err(_) => {
panic!("invalid BayerFrame16 dimensions, plane length, BITS value, or sample range")
}
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn data(&self) -> &'a [u16] {
self.data
}
#[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 bits(&self) -> u32 {
BITS
}
}
pub type Bayer10Frame<'a> = BayerFrame16<'a, 10>;
pub type Bayer12Frame<'a> = BayerFrame16<'a, 12>;
pub type Bayer14Frame<'a> = BayerFrame16<'a, 14>;
pub type Bayer16Frame<'a> = BayerFrame16<'a, 16>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, IsVariant, TryUnwrap, Unwrap, Error)]
#[non_exhaustive]
#[unwrap(ref, ref_mut)]
#[try_unwrap(ref, ref_mut)]
pub enum BayerFrameError {
#[error(transparent)]
ZeroDimension(ZeroDimension),
#[error(transparent)]
InsufficientStride(InsufficientStride),
#[error(transparent)]
InsufficientPlane(InsufficientPlane),
#[error(transparent)]
GeometryOverflow(GeometryOverflow),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, IsVariant, TryUnwrap, Unwrap, Error)]
#[non_exhaustive]
#[unwrap(ref, ref_mut)]
#[try_unwrap(ref, ref_mut)]
pub enum BayerFrame16Error {
#[error(transparent)]
UnsupportedBits(UnsupportedBits),
#[error(transparent)]
ZeroDimension(ZeroDimension),
#[error(transparent)]
InsufficientStride(InsufficientStride),
#[error(transparent)]
InsufficientPlane(InsufficientPlane),
#[error(transparent)]
GeometryOverflow(GeometryOverflow),
#[error("sample {} at element {} exceeds {} ((1 << BITS) - 1)", .0.value(), .0.index(), .0.max_valid())]
SampleOutOfRange(BayerSampleOutOfRange),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BayerSampleOutOfRange {
index: usize,
value: u16,
max_valid: u16,
}
impl BayerSampleOutOfRange {
#[inline]
pub const fn new(index: usize, value: u16, max_valid: u16) -> Self {
Self {
index,
value,
max_valid,
}
}
#[inline]
pub const fn index(&self) -> usize {
self.index
}
#[inline]
pub const fn value(&self) -> u16 {
self.value
}
#[inline]
pub const fn max_valid(&self) -> u16 {
self.max_valid
}
}