#[cfg(all(test, feature = "std"))]
use super::*;
#[cfg(all(test, feature = "std"))]
pub(super) fn solid_vuyx_frame(width: u32, height: u32, v: u8, u: u8, y: u8, x: u8) -> Vec<u8> {
let quad = [v, u, y, x];
(0..(width as usize) * (height as usize))
.flat_map(|_| quad)
.collect()
}
#[test]
#[cfg(all(test, feature = "std"))]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn vuyx_with_rgb_smoke() {
let buf = solid_vuyx_frame(4, 1, 128, 128, 128, 0);
let src = VuyxFrame::try_new(&buf, 4, 1, 16).unwrap();
let mut rgb = std::vec![0u8; 4 * 3];
let mut sink = MixedSinker::<Vuyx>::new(4, 1).with_rgb(&mut rgb).unwrap();
vuyx_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 vuyx_with_rgba_forces_alpha_max_with_zero_source() {
let buf = solid_vuyx_frame(8, 1, 128, 128, 128, 0x00);
let src = VuyxFrame::try_new(&buf, 8, 1, 32).unwrap();
let mut rgba = std::vec![0u8; 8 * 4];
let mut sink = MixedSinker::<Vuyx>::new(8, 1).with_rgba(&mut rgba).unwrap();
vuyx_to(&src, true, ColorMatrix::Bt709, &mut sink).unwrap();
for (i, px) in rgba.chunks(4).enumerate() {
assert_eq!(
px[3], 0xFF,
"pixel {i}: α must be 0xFF regardless of source X=0x00, got {:#X}",
px[3],
);
}
}
#[test]
#[cfg(all(test, feature = "std"))]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn vuyx_with_rgba_forces_alpha_max_with_random_source() {
let buf = solid_vuyx_frame(6, 2, 128, 128, 128, 0x42);
let src = VuyxFrame::try_new(&buf, 6, 2, 24).unwrap();
let n = 6 * 2;
let mut rgba = std::vec![0u8; n * 4];
let mut sink = MixedSinker::<Vuyx>::new(6, 2).with_rgba(&mut rgba).unwrap();
vuyx_to(&src, true, ColorMatrix::Bt709, &mut sink).unwrap();
for (i, px) in rgba.chunks(4).enumerate() {
assert_eq!(
px[3], 0xFF,
"pixel {i}: α must be 0xFF, source X=0x42 must be ignored, got {:#X}",
px[3],
);
}
}
#[test]
#[cfg(all(test, feature = "std"))]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn vuyx_with_luma_extracts_y_byte() {
let buf = solid_vuyx_frame(8, 2, 128, 128, 0xC0, 0xFF);
let src = VuyxFrame::try_new(&buf, 8, 2, 32).unwrap();
let mut luma = std::vec![0u8; 8 * 2];
let mut sink = MixedSinker::<Vuyx>::new(8, 2).with_luma(&mut luma).unwrap();
vuyx_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 vuyx_with_hsv_smoke() {
let buf = solid_vuyx_frame(6, 2, 128, 128, 128, 0);
let src = VuyxFrame::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::<Vuyx>::new(6, 2)
.with_hsv(&mut h, &mut s, &mut v)
.unwrap();
vuyx_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 vuyx_with_rgb_and_rgba_strategy_a_byte_identical() {
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] = 200; packed[n * 4 + 3] = (n as u8) * 30; }
let frame = VuyxFrame::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::<Vuyx>::new(width, height)
.with_rgb(&mut rgb)
.unwrap()
.with_rgba(&mut rgba)
.unwrap();
vuyx_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"
);
assert_eq!(
rgba[n * 4 + 3],
0xFF,
"pixel {n}: RGBA α must be 0xFF, got {:#X}",
rgba[n * 4 + 3],
);
}
}
#[test]
#[cfg(all(test, feature = "std"))]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn vuyx_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 = VuyxFrame::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::<Vuyx>::new(w, h)
.with_rgb(&mut rgb_simd)
.unwrap();
vuyx_to(&src, false, ColorMatrix::Bt709, &mut sink_simd).unwrap();
let mut sink_scalar = MixedSinker::<Vuyx>::new(w, h)
.with_rgb(&mut rgb_scalar)
.unwrap()
.with_simd(false);
vuyx_to(&src, false, ColorMatrix::Bt709, &mut sink_scalar).unwrap();
assert_eq!(rgb_simd, rgb_scalar, "VUYX SIMD ≠ scalar at width {w}");
}
#[test]
#[cfg(all(test, feature = "std"))]
fn vuyx_rgb_buffer_too_short_returns_error() {
let mut rgb = std::vec![0u8; 95];
let result = MixedSinker::<Vuyx>::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 vuyx_rgba_buffer_too_short_returns_error() {
let mut rgba = std::vec![0u8; 90];
let result = MixedSinker::<Vuyx>::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 vuyx_luma_buffer_too_short_returns_error() {
let mut luma = std::vec![0u8; 20];
let result = MixedSinker::<Vuyx>::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 vuyx_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::<Vuyx>::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 vuyx_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, 0xDEADBEEF);
let frame = VuyxFrame::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::<Vuyx>::new(width, height)
.with_luma_u16(&mut luma)
.unwrap();
vuyx_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 vuyx_luma_u16_buffer_too_short_returns_err() {
let mut luma = std::vec![0u16; 4 * 4 - 1];
let result = MixedSinker::<Vuyx>::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 vuyx_force_alpha_max_independent_of_source() {
let width = 16usize;
let height = 4usize;
let n = width * height;
let x_pattern: [u8; 8] = [0x00, 0x42, 0x99, 0xFF, 0x01, 0x7F, 0xAB, 0xCD];
let mut packed = std::vec![0u8; n * 4];
for i in 0..n {
packed[i * 4] = 128; packed[i * 4 + 1] = 128; packed[i * 4 + 2] = 128; packed[i * 4 + 3] = x_pattern[i % x_pattern.len()]; }
let src = VuyxFrame::try_new(&packed, width as u32, height as u32, (width * 4) as u32).unwrap();
let mut rgba = std::vec![0u8; n * 4];
let mut sink = MixedSinker::<Vuyx>::new(width, height)
.with_rgba(&mut rgba)
.unwrap();
vuyx_to(&src, true, ColorMatrix::Bt709, &mut sink).unwrap();
for i in 0..n {
let src_x = x_pattern[i % x_pattern.len()];
let out_a = rgba[i * 4 + 3];
assert_eq!(
out_a, 0xFF,
"pixel {i}: output α = {out_a:#X} but must be 0xFF (source X = {src_x:#X} must be ignored)"
);
}
}