#[cfg(all(test, feature = "std"))]
use super::*;
#[cfg(all(test, feature = "std"))]
pub(super) fn solid_vuya_frame(width: u32, height: u32, v: u8, u: u8, y: u8, a: u8) -> Vec<u8> {
let quad = [v, u, y, a];
(0..(width as usize) * (height as usize))
.flat_map(|_| quad)
.collect()
}
#[cfg(all(test, feature = "std"))]
fn pack_yuva444p_to_vuya(
y: &[u8],
u: &[u8],
v: &[u8],
a: &[u8],
width: usize,
height: usize,
) -> Vec<u8> {
let mut out = vec![0u8; width * height * 4];
for row in 0..height {
for x in 0..width {
let i = row * width + x;
let off = i * 4;
out[off] = v[i];
out[off + 1] = u[i];
out[off + 2] = y[i];
out[off + 3] = a[i];
}
}
out
}
#[test]
#[cfg(all(test, feature = "std"))]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn vuya_with_rgb_smoke() {
let buf = solid_vuya_frame(4, 1, 128, 128, 128, 0);
let src = VuyaFrame::try_new(&buf, 4, 1, 16).unwrap();
let mut rgb = std::vec![0u8; 4 * 3];
let mut sink = MixedSinker::<Vuya>::new(4, 1).with_rgb(&mut rgb).unwrap();
vuya_to(&src, true, ColorMatrix::Bt709, &mut sink).unwrap();
for px in rgb.chunks(3) {
assert!(
px[0].abs_diff(128) <= 4,
"expected ~128, got R={}, G={}, B={}",
px[0],
px[1],
px[2],
);
assert_eq!(px[0], px[1], "R ≠ G");
assert_eq!(px[1], px[2], "G ≠ B");
}
}
#[test]
#[cfg(all(test, feature = "std"))]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn vuya_with_rgba_passes_source_alpha() {
let alphas: [u8; 4] = [0x00, 0x7F, 0xAB, 0xFF];
let mut buf = std::vec![0u8; 4 * 4];
for (i, &a) in alphas.iter().enumerate() {
buf[i * 4] = 128;
buf[i * 4 + 1] = 128;
buf[i * 4 + 2] = 128;
buf[i * 4 + 3] = a;
}
let src = VuyaFrame::try_new(&buf, 4, 1, 16).unwrap();
let mut rgba = std::vec![0u8; 4 * 4];
let mut sink = MixedSinker::<Vuya>::new(4, 1).with_rgba(&mut rgba).unwrap();
vuya_to(&src, true, ColorMatrix::Bt709, &mut sink).unwrap();
for (i, &expected_a) in alphas.iter().enumerate() {
assert_eq!(
rgba[i * 4 + 3],
expected_a,
"alpha mismatch at pixel {i}: expected {expected_a:#X}, got {:#X}",
rgba[i * 4 + 3],
);
}
}
#[test]
#[cfg(all(test, feature = "std"))]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn vuya_with_luma_extracts_y_byte() {
let buf = solid_vuya_frame(8, 2, 128, 128, 0xC0, 0xFF);
let src = VuyaFrame::try_new(&buf, 8, 2, 32).unwrap();
let mut luma = std::vec![0u8; 8 * 2];
let mut sink = MixedSinker::<Vuya>::new(8, 2).with_luma(&mut luma).unwrap();
vuya_to(&src, false, ColorMatrix::Bt709, &mut sink).unwrap();
assert!(
luma.iter().all(|&y| y == 0xC0),
"luma expected 0xC0, got {:?}",
&luma[..8]
);
}
#[test]
#[cfg(all(test, feature = "std"))]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn vuya_with_hsv_smoke() {
let buf = solid_vuya_frame(6, 2, 128, 128, 128, 0);
let src = VuyaFrame::try_new(&buf, 6, 2, 24).unwrap();
let n = 6 * 2;
let mut h = std::vec![0u8; n];
let mut s = std::vec![0u8; n];
let mut v = std::vec![0u8; n];
let mut sink = MixedSinker::<Vuya>::new(6, 2)
.with_hsv(&mut h, &mut s, &mut v)
.unwrap();
vuya_to(&src, true, ColorMatrix::Bt709, &mut sink).unwrap();
for &sat in &s {
assert_eq!(sat, 0, "gray must have S=0 in HSV");
}
}
#[test]
#[cfg(all(test, feature = "std"))]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn vuya_with_rgb_and_rgba_preserves_source_alpha() {
let width = 8usize;
let height = 1usize;
let mut packed = std::vec![0u8; width * 4];
for n in 0..width {
packed[n * 4] = 128; packed[n * 4 + 1] = 128; packed[n * 4 + 2] = 128; packed[n * 4 + 3] = (n as u8) * 32 + 1; }
let frame = VuyaFrame::try_new(&packed, width as u32, height as u32, (width * 4) as u32).unwrap();
let mut rgb = std::vec![0u8; width * height * 3];
let mut rgba = std::vec![0u8; width * height * 4];
let mut sinker = MixedSinker::<Vuya>::new(width, height)
.with_rgb(&mut rgb)
.unwrap()
.with_rgba(&mut rgba)
.unwrap();
vuya_to(&frame, true, ColorMatrix::Bt709, &mut sinker).unwrap();
for n in 0..width {
assert_eq!(
&rgb[n * 3..n * 3 + 3],
&rgba[n * 4..n * 4 + 3],
"pixel {n}: RGB and RGBA RGB-channels diverge (RGB={:?} RGBA={:?})",
&rgb[n * 3..n * 3 + 3],
&rgba[n * 4..n * 4 + 3]
);
let expected_alpha = (n as u8) * 32 + 1;
assert_eq!(
rgba[n * 4 + 3],
expected_alpha,
"pixel {n}: source α was discarded (got {}, expected {})",
rgba[n * 4 + 3],
expected_alpha
);
}
}
#[test]
#[cfg(all(test, feature = "std"))]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn vuya_simd_vs_scalar_parity_at_1922() {
let w = 1922usize;
let h = 2usize;
let mut buf = std::vec![0u8; w * h * 4];
pseudo_random_u8(&mut buf, 0xBEEF_DEAD);
let src = VuyaFrame::try_new(&buf, w as u32, h as u32, (w * 4) as u32).unwrap();
let mut rgb_simd = std::vec![0u8; w * h * 3];
let mut rgb_scalar = std::vec![0u8; w * h * 3];
let mut sink_simd = MixedSinker::<Vuya>::new(w, h)
.with_rgb(&mut rgb_simd)
.unwrap();
vuya_to(&src, false, ColorMatrix::Bt709, &mut sink_simd).unwrap();
let mut sink_scalar = MixedSinker::<Vuya>::new(w, h)
.with_rgb(&mut rgb_scalar)
.unwrap()
.with_simd(false);
vuya_to(&src, false, ColorMatrix::Bt709, &mut sink_scalar).unwrap();
assert_eq!(rgb_simd, rgb_scalar, "VUYA SIMD ≠ scalar at width {w}");
}
#[test]
#[cfg(all(test, feature = "std"))]
fn vuya_rgb_buffer_too_short_returns_error() {
let mut rgb = std::vec![0u8; 95];
let result = MixedSinker::<Vuya>::new(8, 4).with_rgb(&mut rgb);
let Err(err) = result else {
panic!("expected InsufficientRgbBuffer");
};
assert_eq!(
err,
MixedSinkerError::InsufficientRgbBuffer(InsufficientBuffer::new(96, 95))
);
}
#[test]
#[cfg(all(test, feature = "std"))]
fn vuya_rgba_buffer_too_short_returns_error() {
let mut rgba = std::vec![0u8; 90];
let result = MixedSinker::<Vuya>::new(6, 4).with_rgba(&mut rgba);
let Err(err) = result else {
panic!("expected InsufficientRgbaBuffer");
};
assert_eq!(
err,
MixedSinkerError::InsufficientRgbaBuffer(InsufficientBuffer::new(96, 90))
);
}
#[test]
#[cfg(all(test, feature = "std"))]
fn vuya_luma_buffer_too_short_returns_error() {
let mut luma = std::vec![0u8; 20];
let result = MixedSinker::<Vuya>::new(8, 3).with_luma(&mut luma);
let Err(err) = result else {
panic!("expected InsufficientLumaBuffer");
};
assert_eq!(
err,
MixedSinkerError::InsufficientLumaBuffer(InsufficientBuffer::new(24, 20))
);
}
#[test]
#[cfg(all(test, feature = "std"))]
fn vuya_hsv_buffer_too_short_returns_error() {
let mut h = std::vec![0u8; 15];
let mut s = std::vec![0u8; 16];
let mut v = std::vec![0u8; 16];
let result = MixedSinker::<Vuya>::new(4, 4).with_hsv(&mut h, &mut s, &mut v);
let Err(err) = result else {
panic!("expected InsufficientHsvPlane");
};
assert!(matches!(
&err,
MixedSinkerError::InsufficientHsvPlane(e)
if matches!(e.which(), HsvPlane::H) && e.expected() == 16 && e.actual() == 15
));
}
#[test]
#[cfg(all(test, feature = "std"))]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn vuya_planar_parity_with_yuva444p() {
let width = 64usize;
let height = 4usize;
let n = width * height;
let mut yp = std::vec![0u8; n];
let mut up = std::vec![0u8; n];
let mut vp = std::vec![0u8; n];
let mut ap = std::vec![0u8; n];
pseudo_random_u8(&mut yp, 0xC0FFEE_u32);
pseudo_random_u8(&mut up, 0xBADF00D_u32);
pseudo_random_u8(&mut vp, 0xFEEDFACE_u32);
pseudo_random_u8(&mut ap, 0xA1FA5EED_u32);
let planar = Yuva444pFrame::try_new(
&yp,
&up,
&vp,
&ap,
width as u32,
height as u32,
width as u32,
width as u32,
width as u32,
width as u32,
)
.unwrap();
let vuya_buf = pack_yuva444p_to_vuya(&yp, &up, &vp, &ap, width, height);
let packed_frame =
VuyaFrame::try_new(&vuya_buf, width as u32, height as u32, (width * 4) as u32).unwrap();
for full_range in [true, false] {
let mut p_rgb = std::vec![0u8; n * 3];
let mut x_rgb = std::vec![0u8; n * 3];
let mut p_sink = MixedSinker::<Yuva444p>::new(width, height)
.with_rgb(&mut p_rgb)
.unwrap();
yuva444p_to(&planar, full_range, ColorMatrix::Bt709, &mut p_sink).unwrap();
let mut x_sink = MixedSinker::<Vuya>::new(width, height)
.with_rgb(&mut x_rgb)
.unwrap();
vuya_to(&packed_frame, full_range, ColorMatrix::Bt709, &mut x_sink).unwrap();
assert_eq!(
p_rgb, x_rgb,
"VUYA ↔ Yuva444p u8 RGB diverges (full_range={full_range})"
);
let mut p_rgba = std::vec![0u8; n * 4];
let mut x_rgba = std::vec![0u8; n * 4];
let mut p_sink2 = MixedSinker::<Yuva444p>::new(width, height)
.with_rgba(&mut p_rgba)
.unwrap();
yuva444p_to(&planar, full_range, ColorMatrix::Bt709, &mut p_sink2).unwrap();
let mut x_sink2 = MixedSinker::<Vuya>::new(width, height)
.with_rgba(&mut x_rgba)
.unwrap();
vuya_to(&packed_frame, full_range, ColorMatrix::Bt709, &mut x_sink2).unwrap();
assert_eq!(
p_rgba, x_rgba,
"VUYA ↔ Yuva444p u8 RGBA diverges (source-alpha path, full_range={full_range})"
);
for (i, &src_a) in ap.iter().enumerate() {
let alpha_out = x_rgba[i * 4 + 3];
assert_eq!(
alpha_out, src_a,
"VUYA RGBA alpha at pixel {i}: expected {src_a:#X}, got {alpha_out:#X} \
(full_range={full_range})"
);
}
}
}
#[test]
#[cfg(all(test, feature = "std"))]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn vuya_strategy_a_plus_matches_independent_kernel() {
let width = 128usize;
let height = 4usize;
let mut packed = std::vec![0u8; width * height * 4];
pseudo_random_u8(&mut packed, 0xC0FFEE);
let frame = VuyaFrame::try_new(&packed, width as u32, height as u32, (width * 4) as u32).unwrap();
for full_range in [true, false] {
for matrix in [
ColorMatrix::Bt601,
ColorMatrix::Bt709,
ColorMatrix::Bt2020Ncl,
ColorMatrix::Smpte240m,
ColorMatrix::Fcc,
ColorMatrix::YCgCo,
] {
let mut sinker_rgb = std::vec![0u8; width * height * 3];
let mut sinker_rgba = std::vec![0u8; width * height * 4];
{
let mut sink = MixedSinker::<Vuya>::new(width, height)
.with_rgb(&mut sinker_rgb)
.unwrap()
.with_rgba(&mut sinker_rgba)
.unwrap();
vuya_to(&frame, full_range, matrix, &mut sink).unwrap();
}
let mut inline_rgba = std::vec![0u8; width * height * 4];
let mut inline_rgb = std::vec![0u8; width * height * 3];
for r in 0..height {
let row_off_packed = r * width * 4;
let row_off_rgb = r * width * 3;
let row_off_rgba = r * width * 4;
crate::row::scalar::vuya_to_rgb_row(
&packed[row_off_packed..row_off_packed + width * 4],
&mut inline_rgb[row_off_rgb..row_off_rgb + width * 3],
width,
matrix,
full_range,
);
crate::row::scalar::vuya_to_rgba_row(
&packed[row_off_packed..row_off_packed + width * 4],
&mut inline_rgba[row_off_rgba..row_off_rgba + width * 4],
width,
matrix,
full_range,
);
}
assert_eq!(
sinker_rgb, inline_rgb,
"VUYA A+ RGB diverges (range={full_range}, matrix={matrix:?})"
);
assert_eq!(
sinker_rgba, inline_rgba,
"VUYA A+ RGBA diverges from scalar inline-α (range={full_range}, matrix={matrix:?})"
);
}
}
}
#[test]
#[cfg(all(test, feature = "std"))]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn vuya_with_luma_u16_extracts_y_zero_extended() {
let width = 64usize;
let height = 4usize;
let n = width * height;
let mut packed = std::vec![0u8; n * 4];
pseudo_random_u8(&mut packed, 0xC0FFEE);
let frame = VuyaFrame::try_new(&packed, width as u32, height as u32, (width * 4) as u32).unwrap();
let mut luma = std::vec![0u16; n];
let mut sink = MixedSinker::<Vuya>::new(width, height)
.with_luma_u16(&mut luma)
.unwrap();
vuya_to(&frame, false, ColorMatrix::Bt709, &mut sink).unwrap();
let expected: std::vec::Vec<u16> = (0..n).map(|i| packed[i * 4 + 2] as u16).collect();
assert_eq!(luma, expected);
}
#[test]
#[cfg(all(test, feature = "std"))]
fn vuya_luma_u16_buffer_too_short_returns_err() {
let mut luma = std::vec![0u16; 4 * 4 - 1];
let result = MixedSinker::<Vuya>::new(4, 4).with_luma_u16(&mut luma);
let Err(err) = result else {
panic!("expected InsufficientLumaU16Buffer");
};
assert_eq!(
err,
MixedSinkerError::InsufficientLumaU16Buffer(InsufficientBuffer::new(16, 15)),
"unexpected error: {err:?}"
);
}
#[test]
#[cfg(all(test, feature = "std"))]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn vuya_strategy_a_plus_with_simd_false_uses_scalar_path() {
let width = 67usize; let height = 2usize;
let mut packed = std::vec![0u8; width * height * 4];
pseudo_random_u8(&mut packed, 0xFEED_BABE);
let frame = VuyaFrame::try_new(&packed, width as u32, height as u32, (width * 4) as u32).unwrap();
let mut simd_rgb = std::vec![0u8; width * height * 3];
let mut simd_rgba = std::vec![0u8; width * height * 4];
{
let mut sink = MixedSinker::<Vuya>::new(width, height)
.with_rgb(&mut simd_rgb)
.unwrap()
.with_rgba(&mut simd_rgba)
.unwrap();
vuya_to(&frame, false, ColorMatrix::Bt709, &mut sink).unwrap();
}
let mut scalar_rgb = std::vec![0u8; width * height * 3];
let mut scalar_rgba = std::vec![0u8; width * height * 4];
{
let mut sink = MixedSinker::<Vuya>::new(width, height)
.with_rgb(&mut scalar_rgb)
.unwrap()
.with_rgba(&mut scalar_rgba)
.unwrap()
.with_simd(false);
vuya_to(&frame, false, ColorMatrix::Bt709, &mut sink).unwrap();
}
assert_eq!(
simd_rgb, scalar_rgb,
"VUYA A+ RGB diverges between SIMD and with_simd(false) paths"
);
assert_eq!(
simd_rgba, scalar_rgba,
"VUYA A+ RGBA diverges between SIMD and with_simd(false) paths"
);
}