use super::{
GeometryOverflow, InsufficientBuffer, MixedSinker, MixedSinkerError, RowIndexOutOfRange,
RowShapeMismatch, RowSlice, WidthAlignment, check_dimensions_match, rgb_row_buf_or_scratch,
rgba_plane_row_slice, rgba_u16_plane_row_slice,
};
use crate::{PixelSink, row::*, source::*};
impl<'a> MixedSinker<'a, Yuva420p> {
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn with_rgba(mut self, buf: &'a mut [u8]) -> Result<Self, MixedSinkerError> {
self.set_rgba(buf)?;
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn set_rgba(&mut self, buf: &'a mut [u8]) -> Result<&mut Self, MixedSinkerError> {
let expected = self.frame_elems(4)?;
if buf.len() < expected {
return Err(MixedSinkerError::InsufficientRgbaBuffer(
InsufficientBuffer::new(expected, buf.len()),
));
}
self.rgba = Some(buf);
Ok(self)
}
}
impl Yuva420pSink for MixedSinker<'_, Yuva420p> {}
impl PixelSink for MixedSinker<'_, Yuva420p> {
type Input<'r> = Yuva420pRow<'r>;
type Error = MixedSinkerError;
fn begin_frame(&mut self, width: u32, height: u32) -> Result<(), Self::Error> {
if self.width & 1 != 0 {
return Err(MixedSinkerError::WidthAlignment(WidthAlignment::odd(
self.width,
)));
}
check_dimensions_match(self.width, self.height, width, height)
}
fn process(&mut self, row: Yuva420pRow<'_>) -> Result<(), Self::Error> {
let w = self.width;
let h = self.height;
let idx = row.row();
let use_simd = self.simd;
if w & 1 != 0 {
return Err(MixedSinkerError::WidthAlignment(WidthAlignment::odd(w)));
}
if row.y().len() != w {
return Err(MixedSinkerError::RowShapeMismatch(RowShapeMismatch::new(
RowSlice::Y,
idx,
w,
row.y().len(),
)));
}
if row.u_half().len() != w / 2 {
return Err(MixedSinkerError::RowShapeMismatch(RowShapeMismatch::new(
RowSlice::UHalf,
idx,
w / 2,
row.u_half().len(),
)));
}
if row.v_half().len() != w / 2 {
return Err(MixedSinkerError::RowShapeMismatch(RowShapeMismatch::new(
RowSlice::VHalf,
idx,
w / 2,
row.v_half().len(),
)));
}
if row.a().len() != w {
return Err(MixedSinkerError::RowShapeMismatch(RowShapeMismatch::new(
RowSlice::AFull,
idx,
w,
row.a().len(),
)));
}
if idx >= self.height {
return Err(MixedSinkerError::RowIndexOutOfRange(
RowIndexOutOfRange::new(idx, self.height),
));
}
let Self {
rgb,
rgba,
luma,
hsv,
rgb_scratch,
..
} = self;
let one_plane_start = idx * w;
let one_plane_end = one_plane_start + w;
if let Some(luma) = luma.as_deref_mut() {
luma[one_plane_start..one_plane_end].copy_from_slice(&row.y()[..w]);
}
let want_rgb = rgb.is_some();
let want_rgba = rgba.is_some();
let want_hsv = hsv.is_some();
let need_rgb_kernel = want_rgb || want_hsv;
if want_rgba && !need_rgb_kernel {
let rgba_buf = rgba.as_deref_mut().unwrap();
let rgba_row = rgba_plane_row_slice(rgba_buf, one_plane_start, one_plane_end, w, h)?;
yuva420p_to_rgba_row(
row.y(),
row.u_half(),
row.v_half(),
row.a(),
rgba_row,
w,
row.matrix(),
row.full_range(),
use_simd,
);
return Ok(());
}
if !need_rgb_kernel {
return Ok(());
}
let rgb_row = rgb_row_buf_or_scratch(
rgb.as_deref_mut(),
rgb_scratch,
one_plane_start,
one_plane_end,
w,
h,
)?;
yuv_420_to_rgb_row(
row.y(),
row.u_half(),
row.v_half(),
rgb_row,
w,
row.matrix(),
row.full_range(),
use_simd,
);
if let Some(hsv) = hsv.as_mut() {
let (h, s, v) = hsv.hsv();
rgb_to_hsv_row(
rgb_row,
&mut h[one_plane_start..one_plane_end],
&mut s[one_plane_start..one_plane_end],
&mut v[one_plane_start..one_plane_end],
w,
use_simd,
);
}
if want_rgba {
let rgba_buf = rgba.as_deref_mut().unwrap();
let rgba_row = rgba_plane_row_slice(rgba_buf, one_plane_start, one_plane_end, w, h)?;
expand_rgb_to_rgba_row(rgb_row, rgba_row, w);
crate::row::alpha_extract::copy_alpha_plane_u8(row.a(), rgba_row, w, use_simd);
}
Ok(())
}
}
impl<'a, const BE: bool> MixedSinker<'a, Yuva420p9<BE>> {
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn with_rgba(mut self, buf: &'a mut [u8]) -> Result<Self, MixedSinkerError> {
self.set_rgba(buf)?;
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn set_rgba(&mut self, buf: &'a mut [u8]) -> Result<&mut Self, MixedSinkerError> {
let expected = self.frame_elems(4)?;
if buf.len() < expected {
return Err(MixedSinkerError::InsufficientRgbaBuffer(
InsufficientBuffer::new(expected, buf.len()),
));
}
self.rgba = Some(buf);
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn with_rgba_u16(mut self, buf: &'a mut [u16]) -> Result<Self, MixedSinkerError> {
self.set_rgba_u16(buf)?;
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn set_rgba_u16(&mut self, buf: &'a mut [u16]) -> Result<&mut Self, MixedSinkerError> {
let expected = self.frame_elems(4)?;
if buf.len() < expected {
return Err(MixedSinkerError::InsufficientRgbaU16Buffer(
InsufficientBuffer::new(expected, buf.len()),
));
}
self.rgba_u16 = Some(buf);
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn with_rgb_u16(mut self, buf: &'a mut [u16]) -> Result<Self, MixedSinkerError> {
self.set_rgb_u16(buf)?;
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn set_rgb_u16(&mut self, buf: &'a mut [u16]) -> Result<&mut Self, MixedSinkerError> {
let expected = self.frame_elems(3)?;
if buf.len() < expected {
return Err(MixedSinkerError::InsufficientRgbU16Buffer(
InsufficientBuffer::new(expected, buf.len()),
));
}
self.rgb_u16 = Some(buf);
Ok(self)
}
}
impl<const BE: bool> Yuva420p9Sink<BE> for MixedSinker<'_, Yuva420p9<BE>> {}
impl<const BE: bool> PixelSink for MixedSinker<'_, Yuva420p9<BE>> {
type Input<'r> = Yuva420p9Row<'r>;
type Error = MixedSinkerError;
fn begin_frame(&mut self, width: u32, height: u32) -> Result<(), Self::Error> {
if self.width & 1 != 0 {
return Err(MixedSinkerError::WidthAlignment(WidthAlignment::odd(
self.width,
)));
}
check_dimensions_match(self.width, self.height, width, height)
}
fn process(&mut self, row: Yuva420p9Row<'_>) -> Result<(), Self::Error> {
yuva420p_high_bit_process::<9, BE, _, _, _, _>(
self,
row.row(),
row.y(),
row.u_half(),
row.v_half(),
row.a(),
row.matrix(),
row.full_range(),
RowSlice::Y9,
RowSlice::UHalf9,
RowSlice::VHalf9,
RowSlice::AFull9,
yuv420p9_to_rgb_row_endian,
yuv420p9_to_rgb_u16_row_endian,
yuva420p9_to_rgba_row_endian,
yuva420p9_to_rgba_u16_row_endian,
)
}
}
impl<'a, const BE: bool> MixedSinker<'a, Yuva420p10<BE>> {
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn with_rgba(mut self, buf: &'a mut [u8]) -> Result<Self, MixedSinkerError> {
self.set_rgba(buf)?;
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn set_rgba(&mut self, buf: &'a mut [u8]) -> Result<&mut Self, MixedSinkerError> {
let expected = self.frame_elems(4)?;
if buf.len() < expected {
return Err(MixedSinkerError::InsufficientRgbaBuffer(
InsufficientBuffer::new(expected, buf.len()),
));
}
self.rgba = Some(buf);
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn with_rgba_u16(mut self, buf: &'a mut [u16]) -> Result<Self, MixedSinkerError> {
self.set_rgba_u16(buf)?;
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn set_rgba_u16(&mut self, buf: &'a mut [u16]) -> Result<&mut Self, MixedSinkerError> {
let expected = self.frame_elems(4)?;
if buf.len() < expected {
return Err(MixedSinkerError::InsufficientRgbaU16Buffer(
InsufficientBuffer::new(expected, buf.len()),
));
}
self.rgba_u16 = Some(buf);
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn with_rgb_u16(mut self, buf: &'a mut [u16]) -> Result<Self, MixedSinkerError> {
self.set_rgb_u16(buf)?;
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn set_rgb_u16(&mut self, buf: &'a mut [u16]) -> Result<&mut Self, MixedSinkerError> {
let expected = self.frame_elems(3)?;
if buf.len() < expected {
return Err(MixedSinkerError::InsufficientRgbU16Buffer(
InsufficientBuffer::new(expected, buf.len()),
));
}
self.rgb_u16 = Some(buf);
Ok(self)
}
}
impl<const BE: bool> Yuva420p10Sink<BE> for MixedSinker<'_, Yuva420p10<BE>> {}
impl<const BE: bool> PixelSink for MixedSinker<'_, Yuva420p10<BE>> {
type Input<'r> = Yuva420p10Row<'r>;
type Error = MixedSinkerError;
fn begin_frame(&mut self, width: u32, height: u32) -> Result<(), Self::Error> {
if self.width & 1 != 0 {
return Err(MixedSinkerError::WidthAlignment(WidthAlignment::odd(
self.width,
)));
}
check_dimensions_match(self.width, self.height, width, height)
}
fn process(&mut self, row: Yuva420p10Row<'_>) -> Result<(), Self::Error> {
yuva420p_high_bit_process::<10, BE, _, _, _, _>(
self,
row.row(),
row.y(),
row.u_half(),
row.v_half(),
row.a(),
row.matrix(),
row.full_range(),
RowSlice::Y10,
RowSlice::UHalf10,
RowSlice::VHalf10,
RowSlice::AFull10,
yuv420p10_to_rgb_row_endian,
yuv420p10_to_rgb_u16_row_endian,
yuva420p10_to_rgba_row_endian,
yuva420p10_to_rgba_u16_row_endian,
)
}
}
impl<'a, const BE: bool> MixedSinker<'a, Yuva420p16<BE>> {
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn with_rgba(mut self, buf: &'a mut [u8]) -> Result<Self, MixedSinkerError> {
self.set_rgba(buf)?;
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn set_rgba(&mut self, buf: &'a mut [u8]) -> Result<&mut Self, MixedSinkerError> {
let expected = self.frame_elems(4)?;
if buf.len() < expected {
return Err(MixedSinkerError::InsufficientRgbaBuffer(
InsufficientBuffer::new(expected, buf.len()),
));
}
self.rgba = Some(buf);
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn with_rgba_u16(mut self, buf: &'a mut [u16]) -> Result<Self, MixedSinkerError> {
self.set_rgba_u16(buf)?;
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn set_rgba_u16(&mut self, buf: &'a mut [u16]) -> Result<&mut Self, MixedSinkerError> {
let expected = self.frame_elems(4)?;
if buf.len() < expected {
return Err(MixedSinkerError::InsufficientRgbaU16Buffer(
InsufficientBuffer::new(expected, buf.len()),
));
}
self.rgba_u16 = Some(buf);
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn with_rgb_u16(mut self, buf: &'a mut [u16]) -> Result<Self, MixedSinkerError> {
self.set_rgb_u16(buf)?;
Ok(self)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn set_rgb_u16(&mut self, buf: &'a mut [u16]) -> Result<&mut Self, MixedSinkerError> {
let expected = self.frame_elems(3)?;
if buf.len() < expected {
return Err(MixedSinkerError::InsufficientRgbU16Buffer(
InsufficientBuffer::new(expected, buf.len()),
));
}
self.rgb_u16 = Some(buf);
Ok(self)
}
}
impl<const BE: bool> Yuva420p16Sink<BE> for MixedSinker<'_, Yuva420p16<BE>> {}
impl<const BE: bool> PixelSink for MixedSinker<'_, Yuva420p16<BE>> {
type Input<'r> = Yuva420p16Row<'r>;
type Error = MixedSinkerError;
fn begin_frame(&mut self, width: u32, height: u32) -> Result<(), Self::Error> {
if self.width & 1 != 0 {
return Err(MixedSinkerError::WidthAlignment(WidthAlignment::odd(
self.width,
)));
}
check_dimensions_match(self.width, self.height, width, height)
}
fn process(&mut self, row: Yuva420p16Row<'_>) -> Result<(), Self::Error> {
yuva420p_high_bit_process::<16, BE, _, _, _, _>(
self,
row.row(),
row.y(),
row.u_half(),
row.v_half(),
row.a(),
row.matrix(),
row.full_range(),
RowSlice::Y16,
RowSlice::UHalf16,
RowSlice::VHalf16,
RowSlice::AFull16,
yuv420p16_to_rgb_row_endian,
yuv420p16_to_rgb_u16_row_endian,
yuva420p16_to_rgba_row_endian,
yuva420p16_to_rgba_u16_row_endian,
)
}
}
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
#[cfg_attr(not(tarpaulin), inline(always))]
fn yuva420p_high_bit_process<
const BITS: u32,
const BE: bool,
F: crate::SourceFormat,
RgbRowFn: Fn(&[u16], &[u16], &[u16], &mut [u8], usize, crate::ColorMatrix, bool, bool, bool),
RgbU16RowFn: Fn(&[u16], &[u16], &[u16], &mut [u16], usize, crate::ColorMatrix, bool, bool, bool),
RgbaRowFn: Fn(&[u16], &[u16], &[u16], &[u16], &mut [u8], usize, crate::ColorMatrix, bool, bool, bool),
>(
sinker: &mut MixedSinker<'_, F>,
idx: usize,
y_row: &[u16],
u_half_row: &[u16],
v_half_row: &[u16],
a_row: &[u16],
matrix: crate::ColorMatrix,
full_range: bool,
y_slice: RowSlice,
u_slice: RowSlice,
v_slice: RowSlice,
a_slice: RowSlice,
rgb_dispatch: RgbRowFn,
rgb_u16_dispatch: RgbU16RowFn,
rgba_dispatch: RgbaRowFn,
rgba_u16_dispatch: fn(
&[u16],
&[u16],
&[u16],
&[u16],
&mut [u16],
usize,
crate::ColorMatrix,
bool,
bool,
bool,
),
) -> Result<(), MixedSinkerError> {
let w = sinker.width;
let h = sinker.height;
let use_simd = sinker.simd;
if w & 1 != 0 {
return Err(MixedSinkerError::WidthAlignment(WidthAlignment::odd(w)));
}
if y_row.len() != w {
return Err(MixedSinkerError::RowShapeMismatch(RowShapeMismatch::new(
y_slice,
idx,
w,
y_row.len(),
)));
}
if u_half_row.len() != w / 2 {
return Err(MixedSinkerError::RowShapeMismatch(RowShapeMismatch::new(
u_slice,
idx,
w / 2,
u_half_row.len(),
)));
}
if v_half_row.len() != w / 2 {
return Err(MixedSinkerError::RowShapeMismatch(RowShapeMismatch::new(
v_slice,
idx,
w / 2,
v_half_row.len(),
)));
}
if a_row.len() != w {
return Err(MixedSinkerError::RowShapeMismatch(RowShapeMismatch::new(
a_slice,
idx,
w,
a_row.len(),
)));
}
if idx >= sinker.height {
return Err(MixedSinkerError::RowIndexOutOfRange(
RowIndexOutOfRange::new(idx, sinker.height),
));
}
let MixedSinker {
rgb,
rgb_u16,
rgba,
rgba_u16,
luma,
hsv,
rgb_scratch,
..
} = sinker;
let one_plane_start = idx * w;
let one_plane_end = one_plane_start + w;
if let Some(luma) = luma.as_deref_mut() {
let dst = &mut luma[one_plane_start..one_plane_end];
for (d, &s) in dst.iter_mut().zip(y_row.iter()) {
let logical = if BE { u16::from_be(s) } else { u16::from_le(s) };
*d = (logical >> (BITS - 8)) as u8;
}
}
let want_rgb_u16 = rgb_u16.is_some();
let want_rgba_u16 = rgba_u16.is_some();
if want_rgb_u16 {
let buf = rgb_u16.as_deref_mut().unwrap();
let rgb_plane_end = one_plane_end
.checked_mul(3)
.ok_or(MixedSinkerError::GeometryOverflow(GeometryOverflow::new(
w, h, 3,
)))?;
let rgb_plane_start = one_plane_start * 3;
let rgb_u16_row = &mut buf[rgb_plane_start..rgb_plane_end];
rgb_u16_dispatch(
y_row,
u_half_row,
v_half_row,
rgb_u16_row,
w,
matrix,
full_range,
use_simd,
BE,
);
if want_rgba_u16 {
let rgba_buf = rgba_u16.as_deref_mut().unwrap();
let rgba_u16_row = rgba_u16_plane_row_slice(rgba_buf, one_plane_start, one_plane_end, w, h)?;
expand_rgb_u16_to_rgba_u16_row::<BITS>(rgb_u16_row, rgba_u16_row, w);
crate::row::alpha_extract::copy_alpha_plane_u16::<BITS, BE>(a_row, rgba_u16_row, w, use_simd);
}
} else if want_rgba_u16 {
let rgba_buf = rgba_u16.as_deref_mut().unwrap();
let rgba_u16_row = rgba_u16_plane_row_slice(rgba_buf, one_plane_start, one_plane_end, w, h)?;
rgba_u16_dispatch(
y_row,
u_half_row,
v_half_row,
a_row,
rgba_u16_row,
w,
matrix,
full_range,
use_simd,
BE,
);
}
let want_rgb = rgb.is_some();
let want_rgba = rgba.is_some();
let want_hsv = hsv.is_some();
let need_rgb_kernel = want_rgb || want_hsv;
if want_rgba && !need_rgb_kernel {
let rgba_buf = rgba.as_deref_mut().unwrap();
let rgba_row = rgba_plane_row_slice(rgba_buf, one_plane_start, one_plane_end, w, h)?;
rgba_dispatch(
y_row, u_half_row, v_half_row, a_row, rgba_row, w, matrix, full_range, use_simd, BE,
);
return Ok(());
}
if !need_rgb_kernel {
return Ok(());
}
let rgb_row = rgb_row_buf_or_scratch(
rgb.as_deref_mut(),
rgb_scratch,
one_plane_start,
one_plane_end,
w,
h,
)?;
rgb_dispatch(
y_row, u_half_row, v_half_row, rgb_row, w, matrix, full_range, use_simd, BE,
);
if let Some(hsv) = hsv.as_mut() {
let (h, s, v) = hsv.hsv();
rgb_to_hsv_row(
rgb_row,
&mut h[one_plane_start..one_plane_end],
&mut s[one_plane_start..one_plane_end],
&mut v[one_plane_start..one_plane_end],
w,
use_simd,
);
}
if want_rgba {
let rgba_buf = rgba.as_deref_mut().unwrap();
let rgba_row = rgba_plane_row_slice(rgba_buf, one_plane_start, one_plane_end, w, h)?;
expand_rgb_to_rgba_row(rgb_row, rgba_row, w);
crate::row::alpha_extract::copy_alpha_plane_u16_to_u8::<BITS, BE>(a_row, rgba_row, w, use_simd);
}
Ok(())
}