use core::marker::PhantomData;
#[allow(unused_imports)]
use std::vec::Vec;
use derive_more::{Display, IsVariant, TryUnwrap, Unwrap};
use thiserror::Error;
use crate::SourceFormat;
#[allow(unused_imports)]
use crate::PixelSink;
pub use mediaframe::{
frame::{WidthAlignment, WidthAlignmentRequirement},
source::{HsvFrame, HsvFrameMut, HsvPlane},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DimensionMismatch {
configured_w: usize,
configured_h: usize,
frame_w: u32,
frame_h: u32,
}
impl DimensionMismatch {
#[inline]
pub const fn new(configured_w: usize, configured_h: usize, frame_w: u32, frame_h: u32) -> Self {
Self {
configured_w,
configured_h,
frame_w,
frame_h,
}
}
#[inline]
pub const fn configured_w(&self) -> usize {
self.configured_w
}
#[inline]
pub const fn configured_h(&self) -> usize {
self.configured_h
}
#[inline]
pub const fn frame_w(&self) -> u32 {
self.frame_w
}
#[inline]
pub const fn frame_h(&self) -> u32 {
self.frame_h
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InsufficientBuffer {
expected: usize,
actual: usize,
}
impl InsufficientBuffer {
#[inline]
pub const fn new(expected: usize, actual: usize) -> Self {
Self { expected, actual }
}
#[inline]
pub const fn expected(&self) -> usize {
self.expected
}
#[inline]
pub const fn actual(&self) -> usize {
self.actual
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InsufficientHsvPlane {
which: HsvPlane,
expected: usize,
actual: usize,
}
impl InsufficientHsvPlane {
#[inline]
pub const fn new(which: HsvPlane, expected: usize, actual: usize) -> Self {
Self {
which,
expected,
actual,
}
}
#[inline]
pub const fn which(&self) -> HsvPlane {
self.which
}
#[inline]
pub const fn expected(&self) -> usize {
self.expected
}
#[inline]
pub const fn actual(&self) -> usize {
self.actual
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GeometryOverflow {
width: usize,
height: usize,
channels: usize,
}
impl GeometryOverflow {
#[inline]
pub const fn new(width: usize, height: usize, channels: usize) -> Self {
Self {
width,
height,
channels,
}
}
#[inline]
pub const fn width(&self) -> usize {
self.width
}
#[inline]
pub const fn height(&self) -> usize {
self.height
}
#[inline]
pub const fn channels(&self) -> usize {
self.channels
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RowShapeMismatch {
which: RowSlice,
row: usize,
expected: usize,
actual: usize,
}
impl RowShapeMismatch {
#[inline]
pub const fn new(which: RowSlice, row: usize, expected: usize, actual: usize) -> Self {
Self {
which,
row,
expected,
actual,
}
}
#[inline]
pub const fn which(&self) -> RowSlice {
self.which
}
#[inline]
pub const fn row(&self) -> usize {
self.row
}
#[inline]
pub const fn expected(&self) -> usize {
self.expected
}
#[inline]
pub const fn actual(&self) -> usize {
self.actual
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RowIndexOutOfRange {
row: usize,
configured_height: usize,
}
impl RowIndexOutOfRange {
#[inline]
pub const fn new(row: usize, configured_height: usize) -> Self {
Self {
row,
configured_height,
}
}
#[inline]
pub const fn row(&self) -> usize {
self.row
}
#[inline]
pub const fn configured_height(&self) -> usize {
self.configured_height
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, IsVariant, TryUnwrap, Unwrap, Error)]
#[non_exhaustive]
pub enum MixedSinkerError {
#[error(
"MixedSinker frame dimensions mismatch: configured {}x{} but got {}x{}",
.0.configured_w(), .0.configured_h(), .0.frame_w(), .0.frame_h()
)]
DimensionMismatch(DimensionMismatch),
#[error("MixedSinker insufficient rgb buffer: expected >= {} bytes, got {}", .0.expected(), .0.actual())]
InsufficientRgbBuffer(InsufficientBuffer),
#[error("MixedSinker insufficient rgb_u16 buffer: expected >= {} elements, got {}", .0.expected(), .0.actual())]
InsufficientRgbU16Buffer(InsufficientBuffer),
#[error("MixedSinker insufficient luma_u16 buffer: expected >= {} elements, got {}", .0.expected(), .0.actual())]
InsufficientLumaU16Buffer(InsufficientBuffer),
#[error("MixedSinker insufficient rgba buffer: expected >= {} bytes, got {}", .0.expected(), .0.actual())]
InsufficientRgbaBuffer(InsufficientBuffer),
#[error("MixedSinker insufficient rgba_u16 buffer: expected >= {} elements, got {}", .0.expected(), .0.actual())]
InsufficientRgbaU16Buffer(InsufficientBuffer),
#[error("MixedSinker insufficient rgb_f32 buffer: expected >= {} elements, got {}", .0.expected(), .0.actual())]
InsufficientRgbF32Buffer(InsufficientBuffer),
#[error("MixedSinker insufficient rgb_f16 buffer: expected >= {} elements, got {}", .0.expected(), .0.actual())]
InsufficientRgbF16Buffer(InsufficientBuffer),
#[error("MixedSinker insufficient rgba_f32 buffer: expected >= {} elements, got {}", .0.expected(), .0.actual())]
InsufficientRgbaF32Buffer(InsufficientBuffer),
#[error("MixedSinker insufficient rgba_f16 buffer: expected >= {} elements, got {}", .0.expected(), .0.actual())]
InsufficientRgbaF16Buffer(InsufficientBuffer),
#[error("MixedSinker insufficient xyz_f32 buffer: expected >= {} elements, got {}", .0.expected(), .0.actual())]
InsufficientXyzF32Buffer(InsufficientBuffer),
#[error("MixedSinker insufficient luma_f32 buffer: expected >= {} elements, got {}", .0.expected(), .0.actual())]
InsufficientLumaF32Buffer(InsufficientBuffer),
#[error("MixedSinker insufficient luma buffer: expected >= {} bytes, got {}", .0.expected(), .0.actual())]
InsufficientLumaBuffer(InsufficientBuffer),
#[error("MixedSinker insufficient hsv {:?} plane: expected >= {} bytes, got {}", .0.which(), .0.expected(), .0.actual())]
InsufficientHsvPlane(InsufficientHsvPlane),
#[error("MixedSinker frame size overflows usize: {} x {} x channels={}", .0.width(), .0.height(), .0.channels())]
GeometryOverflow(GeometryOverflow),
#[error(
"MixedSinker row shape mismatch at row {}: {} slice has {} elements, expected {}",
.0.row(), .0.which(), .0.actual(), .0.expected()
)]
RowShapeMismatch(RowShapeMismatch),
#[error(
"MixedSinker row index {} is out of range for configured height {}",
.0.row(), .0.configured_height()
)]
RowIndexOutOfRange(RowIndexOutOfRange),
#[error("MixedSinker configured width {} {}", .0.width(), .0.required())]
WidthAlignment(WidthAlignment),
}
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Hash, IsVariant)]
#[non_exhaustive]
pub enum RowSlice {
#[display("Y")]
Y,
#[display("U Half")]
UHalf,
#[display("V Half")]
VHalf,
#[display("U Quarter")]
UQuarter,
#[display("V Quarter")]
VQuarter,
#[display("UV Half")]
UvHalf,
#[display("VU Half")]
VuHalf,
#[display("U Full")]
UFull,
#[display("V Full")]
VFull,
#[display("A Full")]
AFull,
#[display("U Full 10")]
UFull10,
#[display("V Full 10")]
VFull10,
#[display("A Full 10")]
AFull10,
#[display("U Full 12")]
UFull12,
#[display("V Full 12")]
VFull12,
#[display("A Full 12")]
AFull12,
#[display("U Full 14")]
UFull14,
#[display("V Full 14")]
VFull14,
#[display("A Full 14")]
AFull14,
#[display("UV Full")]
UvFull,
#[display("VU Full")]
VuFull,
#[display("Y9")]
Y9,
#[display("U Half 9")]
UHalf9,
#[display("V Half 9")]
VHalf9,
#[display("U Full 9")]
UFull9,
#[display("V Full 9")]
VFull9,
#[display("A Full 9")]
AFull9,
#[display("Y10")]
Y10,
#[display("U Half 10")]
UHalf10,
#[display("V Half 10")]
VHalf10,
#[display("UV Half 10")]
UvHalf10,
#[display("Y12")]
Y12,
#[display("U Half 12")]
UHalf12,
#[display("V Half 12")]
VHalf12,
#[display("UV Half 12")]
UvHalf12,
#[display("Y14")]
Y14,
#[display("U Half 14")]
UHalf14,
#[display("V Half 14")]
VHalf14,
#[display("Y16")]
Y16,
#[display("U Half 16")]
UHalf16,
#[display("V Half 16")]
VHalf16,
#[display("UV Half 16")]
UvHalf16,
#[display("A Full 16")]
AFull16,
#[display("U Full 16")]
UFull16,
#[display("V Full 16")]
VFull16,
#[display("UV Full 10")]
UvFull10,
#[display("UV Full 12")]
UvFull12,
#[display("UV Full 16")]
UvFull16,
#[display("Bayer Above")]
BayerAbove,
#[display("Bayer Mid")]
BayerMid,
#[display("Bayer Below")]
BayerBelow,
#[display("Bayer16 Above")]
Bayer16Above,
#[display("Bayer16 Mid")]
Bayer16Mid,
#[display("Bayer16 Below")]
Bayer16Below,
#[display("Pal8 index row")]
Pal8IndexRow,
#[display("RGB565 packed")]
Rgb565Packed,
#[display("BGR565 packed")]
Bgr565Packed,
#[display("RGB555 packed")]
Rgb555Packed,
#[display("BGR555 packed")]
Bgr555Packed,
#[display("RGB444 packed")]
Rgb444Packed,
#[display("BGR444 packed")]
Bgr444Packed,
#[display("RGB packed")]
RgbPacked,
#[display("BGR packed")]
BgrPacked,
#[display("RGBA packed")]
RgbaPacked,
#[display("BGRA packed")]
BgraPacked,
#[display("ARGB packed")]
ArgbPacked,
#[display("ABGR packed")]
AbgrPacked,
#[display("XRGB packed")]
XrgbPacked,
#[display("RGBX packed")]
RgbxPacked,
#[display("XBGR packed")]
XbgrPacked,
#[display("BGRX packed")]
BgrxPacked,
#[display("X2RGB10 packed")]
X2Rgb10Packed,
#[display("X2BGR10 packed")]
X2Bgr10Packed,
#[display("YUYV422 packed")]
Yuyv422Packed,
#[display("UYVY422 packed")]
Uyvy422Packed,
#[display("YVYU422 packed")]
Yvyu422Packed,
#[display("UYYVYY411 packed")]
Uyyvyy411Packed,
#[display("V210 packed")]
V210Packed,
#[display("Y210 packed")]
Y210Packed,
#[display("Y212 packed")]
Y212Packed,
#[display("Y216 packed")]
Y216Packed,
#[display("V410 packed")]
V410Packed,
#[display("V30X packed")]
V30XPacked,
#[display("XV36 packed")]
Xv36Packed,
#[display("VUYA packed")]
VuyaPacked,
#[display("VUYX packed")]
VuyxPacked,
#[display("AYUV64 packed")]
Ayuv64Packed,
#[display("RGBF32 packed")]
RgbF32Packed,
#[display("RGBF16 packed")]
RgbF16Packed,
#[display("XYZ12 packed")]
Xyz12Packed,
#[display("G plane")]
GPlane,
#[display("B plane")]
BPlane,
#[display("R plane")]
RPlane,
#[display("GBR f32 plane")]
GbrF32Plane,
#[display("GBR f16 plane")]
GbrF16Plane,
#[display("RGB48 packed")]
Rgb48Packed,
#[display("BGR48 packed")]
Bgr48Packed,
#[display("RGBA64 packed")]
Rgba64Packed,
#[display("BGRA64 packed")]
Bgra64Packed,
}
pub struct MixedSinker<'a, F: SourceFormat> {
rgb: Option<&'a mut [u8]>,
rgb_u16: Option<&'a mut [u16]>,
rgb_f32: Option<&'a mut [f32]>,
rgb_f16: Option<&'a mut [half::f16]>,
rgba: Option<&'a mut [u8]>,
rgba_u16: Option<&'a mut [u16]>,
rgba_f32: Option<&'a mut [f32]>,
rgba_f16: Option<&'a mut [half::f16]>,
luma: Option<&'a mut [u8]>,
luma_u16: Option<&'a mut [u16]>,
luma_f32: Option<&'a mut [f32]>,
#[cfg(any(
feature = "bayer",
feature = "gbr",
feature = "gray",
feature = "mono",
feature = "rgb",
feature = "rgb-float",
feature = "rgb-legacy",
feature = "v210",
feature = "xyz",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-packed",
feature = "yuv-planar",
feature = "yuv-semi-planar",
feature = "yuva",
))]
hsv: Option<HsvFrameMut<'a>>,
#[cfg(feature = "xyz")]
xyz_f32: Option<&'a mut [f32]>,
width: usize,
height: usize,
#[cfg(any(
feature = "bayer",
feature = "gbr",
feature = "gray",
feature = "mono",
feature = "rgb",
feature = "rgb-float",
feature = "rgb-legacy",
feature = "v210",
feature = "xyz",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-packed",
feature = "yuv-planar",
feature = "yuv-semi-planar",
feature = "yuva",
))]
rgb_scratch: Vec<u8>,
simd: bool,
#[cfg(any(feature = "bayer", feature = "mono"))]
luma_coefficients_q8: (u32, u32, u32),
_fmt: PhantomData<F>,
}
#[derive(Debug, Clone, Copy, PartialEq, IsVariant)]
#[non_exhaustive]
pub enum LumaCoefficients {
Bt709,
Bt2020,
Bt601,
DciP3,
AcesAp1,
Custom(CustomLumaCoefficients),
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CustomLumaCoefficients {
r: f32,
g: f32,
b: f32,
}
impl CustomLumaCoefficients {
pub const MAX_COEFFICIENT: f32 = 10.0;
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn try_new(r: f32, g: f32, b: f32) -> Result<Self, LumaCoefficientsError> {
if !r.is_finite() {
return Err(LumaCoefficientsError::NonFinite {
channel: LumaChannel::R,
value: r,
});
}
if !g.is_finite() {
return Err(LumaCoefficientsError::NonFinite {
channel: LumaChannel::G,
value: g,
});
}
if !b.is_finite() {
return Err(LumaCoefficientsError::NonFinite {
channel: LumaChannel::B,
value: b,
});
}
if r < 0.0 {
return Err(LumaCoefficientsError::Negative {
channel: LumaChannel::R,
value: r,
});
}
if g < 0.0 {
return Err(LumaCoefficientsError::Negative {
channel: LumaChannel::G,
value: g,
});
}
if b < 0.0 {
return Err(LumaCoefficientsError::Negative {
channel: LumaChannel::B,
value: b,
});
}
if r > Self::MAX_COEFFICIENT {
return Err(LumaCoefficientsError::OutOfBounds {
channel: LumaChannel::R,
value: r,
max: Self::MAX_COEFFICIENT,
});
}
if g > Self::MAX_COEFFICIENT {
return Err(LumaCoefficientsError::OutOfBounds {
channel: LumaChannel::G,
value: g,
max: Self::MAX_COEFFICIENT,
});
}
if b > Self::MAX_COEFFICIENT {
return Err(LumaCoefficientsError::OutOfBounds {
channel: LumaChannel::B,
value: b,
max: Self::MAX_COEFFICIENT,
});
}
Ok(Self { r, g, b })
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(r: f32, g: f32, b: f32) -> Self {
match Self::try_new(r, g, b) {
Ok(c) => c,
Err(_) => panic!("invalid CustomLumaCoefficients (non-finite, negative, or out of range)"),
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn r(&self) -> f32 {
self.r
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn g(&self) -> f32 {
self.g
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn b(&self) -> f32 {
self.b
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, IsVariant)]
#[non_exhaustive]
pub enum LumaChannel {
R,
G,
B,
}
#[derive(Debug, Clone, Copy, PartialEq, IsVariant, Error)]
#[non_exhaustive]
pub enum LumaCoefficientsError {
#[error("CustomLumaCoefficients.{channel:?} is non-finite (got {value})")]
NonFinite {
channel: LumaChannel,
value: f32,
},
#[error("CustomLumaCoefficients.{channel:?} is negative (got {value})")]
Negative {
channel: LumaChannel,
value: f32,
},
#[error("CustomLumaCoefficients.{channel:?} = {value} exceeds the magnitude bound ({max})")]
OutOfBounds {
channel: LumaChannel,
value: f32,
max: f32,
},
}
impl LumaCoefficients {
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn to_q8(self) -> (u32, u32, u32) {
match self {
Self::Bt709 => (54, 183, 19),
Self::Bt2020 => (67, 174, 15),
Self::Bt601 => (77, 150, 29),
Self::DciP3 => (59, 177, 20),
Self::AcesAp1 => (70, 172, 14),
Self::Custom(c) => (
(c.r * 256.0 + 0.5) as u32,
(c.g * 256.0 + 0.5) as u32,
(c.b * 256.0 + 0.5) as u32,
),
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn try_custom(r: f32, g: f32, b: f32) -> Result<Self, LumaCoefficientsError> {
match CustomLumaCoefficients::try_new(r, g, b) {
Ok(c) => Ok(Self::Custom(c)),
Err(e) => Err(e),
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn custom(r: f32, g: f32, b: f32) -> Self {
Self::Custom(CustomLumaCoefficients::new(r, g, b))
}
}
impl Default for LumaCoefficients {
fn default() -> Self {
Self::Bt709
}
}
impl<F: SourceFormat> MixedSinker<'_, F> {
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn new(width: usize, height: usize) -> Self {
Self {
rgb: None,
rgb_u16: None,
rgb_f32: None,
rgb_f16: None,
rgba: None,
rgba_u16: None,
rgba_f32: None,
rgba_f16: None,
luma: None,
luma_u16: None,
luma_f32: None,
#[cfg(any(
feature = "bayer",
feature = "gbr",
feature = "gray",
feature = "mono",
feature = "rgb",
feature = "rgb-float",
feature = "rgb-legacy",
feature = "v210",
feature = "xyz",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-packed",
feature = "yuv-planar",
feature = "yuv-semi-planar",
feature = "yuva",
))]
hsv: None,
#[cfg(feature = "xyz")]
xyz_f32: None,
width,
height,
#[cfg(any(
feature = "bayer",
feature = "gbr",
feature = "gray",
feature = "mono",
feature = "rgb",
feature = "rgb-float",
feature = "rgb-legacy",
feature = "v210",
feature = "xyz",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-packed",
feature = "yuv-planar",
feature = "yuv-semi-planar",
feature = "yuva",
))]
rgb_scratch: Vec::new(),
simd: true,
#[cfg(any(feature = "bayer", feature = "mono"))]
luma_coefficients_q8: (54, 183, 19),
_fmt: PhantomData,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn produces_rgb(&self) -> bool {
self.rgb.is_some()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn produces_rgb_u16(&self) -> bool {
self.rgb_u16.is_some()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn produces_rgb_f32(&self) -> bool {
self.rgb_f32.is_some()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn produces_rgb_f16(&self) -> bool {
self.rgb_f16.is_some()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn produces_rgba_f32(&self) -> bool {
self.rgba_f32.is_some()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn produces_rgba_f16(&self) -> bool {
self.rgba_f16.is_some()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn produces_rgba(&self) -> bool {
self.rgba.is_some()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn produces_rgba_u16(&self) -> bool {
self.rgba_u16.is_some()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn produces_luma(&self) -> bool {
self.luma.is_some()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn produces_luma_u16(&self) -> bool {
self.luma_u16.is_some()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn produces_luma_f32(&self) -> bool {
self.luma_f32.is_some()
}
#[cfg(any(
feature = "bayer",
feature = "gbr",
feature = "gray",
feature = "mono",
feature = "rgb",
feature = "rgb-float",
feature = "rgb-legacy",
feature = "v210",
feature = "xyz",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-packed",
feature = "yuv-planar",
feature = "yuv-semi-planar",
feature = "yuva",
))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn produces_hsv(&self) -> bool {
self.hsv.is_some()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn width(&self) -> usize {
self.width
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn height(&self) -> usize {
self.height
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn simd(&self) -> bool {
self.simd
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn set_simd(&mut self, simd: bool) -> &mut Self {
self.simd = simd;
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_simd(mut self, simd: bool) -> Self {
self.set_simd(simd);
self
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn frame_elems(&self, channels: usize) -> Result<usize, MixedSinkerError> {
self
.width
.checked_mul(self.height)
.and_then(|n| n.checked_mul(channels))
.ok_or(MixedSinkerError::GeometryOverflow(GeometryOverflow::new(
self.width,
self.height,
channels,
)))
}
#[cfg(any(
feature = "gbr",
feature = "gray",
feature = "mono",
feature = "rgb",
feature = "rgb-float",
feature = "rgb-legacy",
feature = "v210",
feature = "xyz",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-packed",
feature = "yuv-planar",
feature = "yuv-semi-planar",
feature = "yuva",
))]
#[cfg_attr(not(tarpaulin), inline(always))]
fn frame_pixels(&self) -> Result<usize, MixedSinkerError> {
self
.width
.checked_mul(self.height)
.ok_or(MixedSinkerError::GeometryOverflow(GeometryOverflow::new(
self.width,
self.height,
1,
)))
}
}
impl<'a, F: SourceFormat> MixedSinker<'a, F> {
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn with_rgb(mut self, buf: &'a mut [u8]) -> Result<Self, MixedSinkerError> {
self.set_rgb(buf)?;
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn set_rgb(&mut self, buf: &'a mut [u8]) -> Result<&mut Self, MixedSinkerError> {
let expected = self.frame_elems(3)?;
if buf.len() < expected {
return Err(MixedSinkerError::InsufficientRgbBuffer(
InsufficientBuffer::new(expected, buf.len()),
));
}
self.rgb = Some(buf);
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn with_luma(mut self, buf: &'a mut [u8]) -> Result<Self, MixedSinkerError> {
self.set_luma(buf)?;
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn set_luma(&mut self, buf: &'a mut [u8]) -> Result<&mut Self, MixedSinkerError> {
let expected = self.frame_elems(1)?;
if buf.len() < expected {
return Err(MixedSinkerError::InsufficientLumaBuffer(
InsufficientBuffer::new(expected, buf.len()),
));
}
self.luma = Some(buf);
Ok(self)
}
#[cfg(any(
feature = "bayer",
feature = "gbr",
feature = "gray",
feature = "mono",
feature = "rgb",
feature = "rgb-float",
feature = "rgb-legacy",
feature = "v210",
feature = "xyz",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-packed",
feature = "yuv-planar",
feature = "yuv-semi-planar",
feature = "yuva",
))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn with_hsv(
mut self,
h: &'a mut [u8],
s: &'a mut [u8],
v: &'a mut [u8],
) -> Result<Self, MixedSinkerError> {
self.set_hsv(h, s, v)?;
Ok(self)
}
#[cfg(any(
feature = "bayer",
feature = "gbr",
feature = "gray",
feature = "mono",
feature = "rgb",
feature = "rgb-float",
feature = "rgb-legacy",
feature = "v210",
feature = "xyz",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-packed",
feature = "yuv-planar",
feature = "yuv-semi-planar",
feature = "yuva",
))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn set_hsv(
&mut self,
h: &'a mut [u8],
s: &'a mut [u8],
v: &'a mut [u8],
) -> Result<&mut Self, MixedSinkerError> {
let expected = self.frame_elems(1)?;
if h.len() < expected {
return Err(MixedSinkerError::InsufficientHsvPlane(
InsufficientHsvPlane::new(HsvPlane::H, expected, h.len()),
));
}
if s.len() < expected {
return Err(MixedSinkerError::InsufficientHsvPlane(
InsufficientHsvPlane::new(HsvPlane::S, expected, s.len()),
));
}
if v.len() < expected {
return Err(MixedSinkerError::InsufficientHsvPlane(
InsufficientHsvPlane::new(HsvPlane::V, expected, v.len()),
));
}
self.hsv = Some(HsvFrameMut::new(h, s, v));
Ok(self)
}
}
#[cfg(any(
feature = "bayer",
feature = "gbr",
feature = "gray",
feature = "mono",
feature = "rgb",
feature = "rgb-float",
feature = "rgb-legacy",
feature = "v210",
feature = "xyz",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-packed",
feature = "yuv-planar",
feature = "yuv-semi-planar",
feature = "yuva",
))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(super) fn check_dimensions_match(
configured_w: usize,
configured_h: usize,
frame_w: u32,
frame_h: u32,
) -> Result<(), MixedSinkerError> {
let fw = frame_w as usize;
let fh = frame_h as usize;
if fw != configured_w || fh != configured_h {
return Err(MixedSinkerError::DimensionMismatch(DimensionMismatch::new(
configured_w,
configured_h,
frame_w,
frame_h,
)));
}
Ok(())
}
#[cfg(any(
feature = "gbr",
feature = "gray",
feature = "mono",
feature = "rgb",
feature = "rgb-float",
feature = "rgb-legacy",
feature = "v210",
feature = "xyz",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-packed",
feature = "yuv-planar",
feature = "yuv-semi-planar",
feature = "yuva",
))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(super) fn rgba_plane_row_slice(
buf: &mut [u8],
one_plane_start: usize,
one_plane_end: usize,
width: usize,
height: usize,
) -> Result<&mut [u8], MixedSinkerError> {
let end = one_plane_end
.checked_mul(4)
.ok_or(MixedSinkerError::GeometryOverflow(GeometryOverflow::new(
width, height, 4,
)))?;
let start = one_plane_start * 4; Ok(&mut buf[start..end])
}
#[cfg(any(
feature = "gbr",
feature = "gray",
feature = "mono",
feature = "rgb",
feature = "rgb-float",
feature = "rgb-legacy",
feature = "v210",
feature = "xyz",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-planar",
feature = "yuva",
))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(super) fn rgba_u16_plane_row_slice(
buf: &mut [u16],
one_plane_start: usize,
one_plane_end: usize,
width: usize,
height: usize,
) -> Result<&mut [u16], MixedSinkerError> {
let end = one_plane_end
.checked_mul(4)
.ok_or(MixedSinkerError::GeometryOverflow(GeometryOverflow::new(
width, height, 4,
)))?;
let start = one_plane_start * 4; Ok(&mut buf[start..end])
}
#[cfg(any(
feature = "bayer",
feature = "gbr",
feature = "gray",
feature = "mono",
feature = "rgb",
feature = "rgb-float",
feature = "rgb-legacy",
feature = "v210",
feature = "xyz",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-packed",
feature = "yuv-planar",
feature = "yuv-semi-planar",
feature = "yuva",
))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(super) fn rgb_row_buf_or_scratch<'a>(
rgb: Option<&'a mut [u8]>,
rgb_scratch: &'a mut Vec<u8>,
one_plane_start: usize,
one_plane_end: usize,
width: usize,
height: usize,
) -> Result<&'a mut [u8], MixedSinkerError> {
match rgb {
Some(buf) => {
let end = one_plane_end
.checked_mul(3)
.ok_or(MixedSinkerError::GeometryOverflow(GeometryOverflow::new(
width, height, 3,
)))?;
let start = one_plane_start * 3;
Ok(&mut buf[start..end])
}
None => {
let row_bytes = width
.checked_mul(3)
.ok_or(MixedSinkerError::GeometryOverflow(GeometryOverflow::new(
width, height, 3,
)))?;
if rgb_scratch.len() < row_bytes {
rgb_scratch.resize(row_bytes, 0);
}
Ok(&mut rgb_scratch[..row_bytes])
}
}
}
#[cfg(any(feature = "bayer", feature = "mono"))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(super) fn rgb_row_to_luma_row(rgb: &[u8], luma: &mut [u8], coeffs_q8: (u32, u32, u32)) {
debug_assert!(
luma
.len()
.checked_mul(3)
.is_some_and(|need| rgb.len() >= need),
"rgb_row_to_luma_row: rgb.len()={} but need {} (= 3 x luma.len()={})",
rgb.len(),
luma.len().saturating_mul(3),
luma.len(),
);
let (cr, cg, cb) = coeffs_q8;
for (i, d) in luma.iter_mut().enumerate() {
let r = rgb[3 * i] as u32;
let g = rgb[3 * i + 1] as u32;
let b = rgb[3 * i + 2] as u32;
*d = ((cr * r + cg * g + cb * b + 128) >> 8).min(255) as u8;
}
}
#[cfg(feature = "mono")]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(super) fn rgb_row_to_luma_u16_row(
rgb: &[u8],
luma_u16: &mut [u16],
coeffs_q8: (u32, u32, u32),
) {
debug_assert!(
luma_u16
.len()
.checked_mul(3)
.is_some_and(|need| rgb.len() >= need),
"rgb_row_to_luma_u16_row: rgb.len()={} but need {} (= 3 x luma_u16.len()={})",
rgb.len(),
luma_u16.len().saturating_mul(3),
luma_u16.len(),
);
let (cr, cg, cb) = coeffs_q8;
for (i, dst) in luma_u16.iter_mut().enumerate() {
let r = rgb[3 * i] as u32;
let g = rgb[3 * i + 1] as u32;
let b = rgb[3 * i + 2] as u32;
let y = ((cr * r + cg * g + cb * b + 128) >> 8).min(255) as u16;
*dst = (y << 8) | y;
}
}
#[cfg(feature = "yuv-444-packed")]
mod ayuv64;
#[cfg(feature = "bayer")]
mod bayer;
#[cfg(feature = "gray")]
mod gray;
#[cfg(feature = "rgb-legacy")]
mod legacy_rgb;
#[cfg(feature = "mono")]
mod mono1bit;
#[cfg(feature = "rgb")]
mod packed_rgb_10bit;
#[cfg(feature = "rgb")]
mod packed_rgb_16bit;
#[cfg(feature = "rgb")]
mod packed_rgb_8bit;
#[cfg(feature = "rgb-float")]
mod packed_rgb_f16;
#[cfg(feature = "rgb-float")]
mod packed_rgb_float;
#[cfg(feature = "yuv-packed")]
mod packed_yuv_4_1_1;
#[cfg(feature = "yuv-packed")]
mod packed_yuv_8bit;
#[cfg(feature = "mono")]
mod pal8;
#[cfg(feature = "yuv-planar")]
mod planar_8bit;
#[cfg(feature = "gbr")]
mod planar_gbr_8bit;
#[cfg(feature = "gbr")]
mod planar_gbr_f16;
#[cfg(feature = "gbr")]
mod planar_gbr_float;
#[cfg(feature = "gbr")]
mod planar_gbr_high_bit;
#[cfg(feature = "yuv-semi-planar")]
mod semi_planar_8bit;
#[cfg(feature = "yuv-planar")]
mod subsampled_4_2_0_high_bit;
#[cfg(feature = "yuv-planar")]
mod subsampled_4_2_2_high_bit;
#[cfg(feature = "yuv-planar")]
mod subsampled_4_4_4_high_bit;
#[cfg(feature = "v210")]
mod v210;
#[cfg(feature = "yuv-444-packed")]
mod v30x;
#[cfg(feature = "yuv-444-packed")]
mod v410;
#[cfg(feature = "yuv-444-packed")]
mod vuya;
#[cfg(feature = "yuv-444-packed")]
mod vuyx;
#[cfg(feature = "yuv-444-packed")]
mod xv36;
#[cfg(feature = "xyz")]
mod xyz12;
#[cfg(feature = "y2xx")]
mod y210;
#[cfg(feature = "y2xx")]
mod y212;
#[cfg(feature = "y2xx")]
mod y216;
#[cfg(feature = "yuva")]
mod yuva_4_2_0;
#[cfg(feature = "yuva")]
mod yuva_4_2_2;
#[cfg(feature = "yuva")]
mod yuva_4_4_4;
#[cfg(all(test, feature = "std"))]
mod tests;
#[cfg(all(test, feature = "std"))]
mod api_smoke_tests {
use super::*;
#[cfg(feature = "v210")]
#[test]
fn mixed_sinker_default_does_not_produce_luma_u16() {
let sink: MixedSinker<'_, crate::source::V210> = MixedSinker::new(6, 1);
assert!(!sink.produces_luma_u16());
}
#[test]
fn luma_u16_buffer_too_short_error_displays() {
let err = MixedSinkerError::InsufficientLumaU16Buffer(InsufficientBuffer::new(100, 50));
let msg = format!("{err}");
assert!(msg.contains("100"));
assert!(msg.contains("50"));
}
}