use super::*;
pub(super) fn solid_y210_frame(width: u32, height: u32, y: u16, u: u16, v: u16) -> Vec<u16> {
assert!(width.is_multiple_of(2), "Y210 requires even width");
let row_elems = (width as usize) * 2;
let mut buf = std::vec![0u16; row_elems * height as usize];
let y_msb = (y & 0x3FF) << 6;
let u_msb = (u & 0x3FF) << 6;
let v_msb = (v & 0x3FF) << 6;
for row in 0..height as usize {
let off = row * row_elems;
for q in 0..(width as usize / 2) {
let base = off + q * 4;
buf[base] = y_msb;
buf[base + 1] = u_msb;
buf[base + 2] = y_msb;
buf[base + 3] = v_msb;
}
}
buf
}
#[test]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn y210_luma_only_extracts_y_bytes_downshifted() {
let buf = solid_y210_frame(6, 8, 200, 512, 512); let src = Y210Frame::new(&buf, 6, 8, 12);
let mut luma = std::vec![0u8; 6 * 8];
let mut sink = MixedSinker::<Y210>::new(6, 8).with_luma(&mut luma).unwrap();
y210_to(&src, true, ColorMatrix::Bt601, &mut sink).unwrap();
assert!(luma.iter().all(|&y| y == 50), "luma {luma:?}");
}
#[test]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn y210_luma_u16_only_extracts_y_native_depth() {
let buf = solid_y210_frame(6, 8, 200, 512, 512);
let src = Y210Frame::new(&buf, 6, 8, 12);
let mut luma = std::vec![0u16; 6 * 8];
let mut sink = MixedSinker::<Y210>::new(6, 8)
.with_luma_u16(&mut luma)
.unwrap();
y210_to(&src, true, ColorMatrix::Bt601, &mut sink).unwrap();
assert!(luma.iter().all(|&y| y == 200), "luma_u16 {:?}", &luma[..16]);
}
#[test]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn y210_rgb_only_converts_gray_to_gray() {
let buf = solid_y210_frame(12, 4, 512, 512, 512);
let src = Y210Frame::new(&buf, 12, 4, 24);
let mut rgb = std::vec![0u8; 12 * 4 * 3];
let mut sink = MixedSinker::<Y210>::new(12, 4).with_rgb(&mut rgb).unwrap();
y210_to(&src, true, ColorMatrix::Bt601, &mut sink).unwrap();
for px in rgb.chunks(3) {
assert!(px[0].abs_diff(128) <= 1);
assert_eq!(px[0], px[1]);
assert_eq!(px[1], px[2]);
}
}
#[test]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn y210_rgba_only_converts_gray_to_gray_with_opaque_alpha() {
let buf = solid_y210_frame(12, 4, 512, 512, 512);
let src = Y210Frame::new(&buf, 12, 4, 24);
let mut rgba = std::vec![0u8; 12 * 4 * 4];
let mut sink = MixedSinker::<Y210>::new(12, 4)
.with_rgba(&mut rgba)
.unwrap();
y210_to(&src, true, ColorMatrix::Bt601, &mut sink).unwrap();
for px in rgba.chunks(4) {
assert_eq!(px[3], 0xFF);
}
}
#[test]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn y210_rgb_u16_only_converts_gray_to_gray_native_depth() {
let buf = solid_y210_frame(12, 4, 512, 512, 512);
let src = Y210Frame::new(&buf, 12, 4, 24);
let mut rgb = std::vec![0u16; 12 * 4 * 3];
let mut sink = MixedSinker::<Y210>::new(12, 4)
.with_rgb_u16(&mut rgb)
.unwrap();
y210_to(&src, true, ColorMatrix::Bt601, &mut sink).unwrap();
for px in rgb.chunks(3) {
assert!(px[0].abs_diff(512) <= 2, "expected ~512, got {}", px[0]);
}
}
#[test]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn y210_rgba_u16_alpha_is_max() {
let buf = solid_y210_frame(12, 4, 512, 512, 512);
let src = Y210Frame::new(&buf, 12, 4, 24);
let mut rgba = std::vec![0u16; 12 * 4 * 4];
let mut sink = MixedSinker::<Y210>::new(12, 4)
.with_rgba_u16(&mut rgba)
.unwrap();
y210_to(&src, true, ColorMatrix::Bt601, &mut sink).unwrap();
for px in rgba.chunks(4) {
assert_eq!(px[3], 1023);
}
}
#[test]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn y210_with_rgb_and_with_rgba_byte_identical_u8() {
let w = 12u32;
let h = 4u32;
let buf = solid_y210_frame(w, h, 700, 400, 600);
let src = Y210Frame::new(&buf, w, h, w * 2);
let mut rgb = std::vec![0u8; (w * h) as usize * 3];
let mut rgba = std::vec![0u8; (w * h) as usize * 4];
let mut sink = MixedSinker::<Y210>::new(w as usize, h as usize)
.with_rgb(&mut rgb)
.unwrap()
.with_rgba(&mut rgba)
.unwrap();
y210_to(&src, true, ColorMatrix::Bt601, &mut sink).unwrap();
for i in 0..(w * h) as usize {
assert_eq!(rgba[i * 4], rgb[i * 3]);
assert_eq!(rgba[i * 4 + 1], rgb[i * 3 + 1]);
assert_eq!(rgba[i * 4 + 2], rgb[i * 3 + 2]);
assert_eq!(rgba[i * 4 + 3], 0xFF);
}
}
#[test]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn y210_with_rgb_u16_and_with_rgba_u16_byte_identical() {
let w = 12u32;
let h = 4u32;
let buf = solid_y210_frame(w, h, 700, 400, 600);
let src = Y210Frame::new(&buf, w, h, w * 2);
let mut rgb = std::vec![0u16; (w * h) as usize * 3];
let mut rgba = std::vec![0u16; (w * h) as usize * 4];
let mut sink = MixedSinker::<Y210>::new(w as usize, h as usize)
.with_rgb_u16(&mut rgb)
.unwrap()
.with_rgba_u16(&mut rgba)
.unwrap();
y210_to(&src, true, ColorMatrix::Bt601, &mut sink).unwrap();
for i in 0..(w * h) as usize {
assert_eq!(rgba[i * 4], rgb[i * 3]);
assert_eq!(rgba[i * 4 + 1], rgb[i * 3 + 1]);
assert_eq!(rgba[i * 4 + 2], rgb[i * 3 + 2]);
assert_eq!(rgba[i * 4 + 3], 1023);
}
}
#[test]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn y210_with_simd_false_matches_with_simd_true() {
for w in [2usize, 4, 14, 16, 18, 30, 32, 34, 1920, 1922] {
let h = 2usize;
let mut buf = std::vec![0u16; w * 2 * h];
pseudo_random_u16_low_n_bits(&mut buf, 0xC0FFEE, 10);
for s in &mut buf {
*s <<= 6;
}
let src = Y210Frame::new(&buf, w as u32, h as u32, (w * 2) as u32);
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::<Y210>::new(w, h)
.with_rgb(&mut rgb_simd)
.unwrap();
let mut sink_scalar = MixedSinker::<Y210>::new(w, h)
.with_rgb(&mut rgb_scalar)
.unwrap()
.with_simd(false);
y210_to(&src, false, ColorMatrix::Bt709, &mut sink_simd).unwrap();
y210_to(&src, false, ColorMatrix::Bt709, &mut sink_scalar).unwrap();
assert_eq!(rgb_simd, rgb_scalar, "Y210 SIMD≠scalar at width {w}");
}
}
#[test]
fn y210_luma_u16_buffer_too_short_returns_err() {
let mut luma = std::vec![0u16; 6 * 7];
let result = MixedSinker::<Y210>::new(6, 8).with_luma_u16(&mut luma);
let Err(err) = result else {
panic!("expected InsufficientLumaU16Buffer");
};
assert_eq!(
err,
MixedSinkerError::InsufficientLumaU16Buffer(InsufficientBuffer::new(48, 42))
);
}
fn pack_yuv422p10_to_y210(
y: &[u16],
u: &[u16],
v: &[u16],
width: usize,
height: usize,
) -> Vec<u16> {
let cw = width / 2;
let mut out = std::vec![0u16; width * 2 * height];
for row in 0..height {
for c in 0..cw {
let off = row * width * 2 + c * 4;
out[off] = (y[row * width + 2 * c] << 6) & 0xFFC0;
out[off + 1] = (u[row * cw + c] << 6) & 0xFFC0;
out[off + 2] = (y[row * width + 2 * c + 1] << 6) & 0xFFC0;
out[off + 3] = (v[row * cw + c] << 6) & 0xFFC0;
}
}
out
}
#[test]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn y210_reconstructed_from_yuv422p10_matches_yuv422p10_to_rgb() {
let w = 16usize;
let h = 4usize;
let mut y_plane = std::vec![0u16; w * h];
let mut u_plane = std::vec![0u16; (w / 2) * h];
let mut v_plane = std::vec![0u16; (w / 2) * h];
pseudo_random_u16_low_n_bits(&mut y_plane, 0xC0FFEE, 10);
pseudo_random_u16_low_n_bits(&mut u_plane, 0xBADF00D, 10);
pseudo_random_u16_low_n_bits(&mut v_plane, 0xFEEDFACE, 10);
let planar = Yuv422p10Frame::new(
&y_plane,
&u_plane,
&v_plane,
w as u32,
h as u32,
w as u32,
(w / 2) as u32,
(w / 2) as u32,
);
let packed = pack_yuv422p10_to_y210(&y_plane, &u_plane, &v_plane, w, h);
let y210 = Y210Frame::new(&packed, w as u32, h as u32, (w * 2) as u32);
let mut rgb_planar = std::vec![0u8; w * h * 3];
let mut rgb_packed = std::vec![0u8; w * h * 3];
let mut s_planar = MixedSinker::<Yuv422p10>::new(w, h)
.with_rgb(&mut rgb_planar)
.unwrap();
let mut s_packed = MixedSinker::<Y210>::new(w, h)
.with_rgb(&mut rgb_packed)
.unwrap();
yuv422p10_to(&planar, false, ColorMatrix::Bt709, &mut s_planar).unwrap();
y210_to(&y210, false, ColorMatrix::Bt709, &mut s_packed).unwrap();
assert_eq!(rgb_planar, rgb_packed);
}
#[test]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn y210_matches_v210_with_same_logical_samples() {
let w = 12usize;
let h = 4usize;
let mut y_plane = std::vec![0u16; w * h];
let mut u_plane = std::vec![0u16; (w / 2) * h];
let mut v_plane = std::vec![0u16; (w / 2) * h];
pseudo_random_u16_low_n_bits(&mut y_plane, 0x12345, 10);
pseudo_random_u16_low_n_bits(&mut u_plane, 0x67890, 10);
pseudo_random_u16_low_n_bits(&mut v_plane, 0xABCDE, 10);
let v210_buf = super::v210::pack_yuv422p10_to_v210(&y_plane, &u_plane, &v_plane, w, h);
let v210_src = V210Frame::new(&v210_buf, w as u32, h as u32, ((w / 6) * 16) as u32);
let y210_buf = pack_yuv422p10_to_y210(&y_plane, &u_plane, &v_plane, w, h);
let y210_src = Y210Frame::new(&y210_buf, w as u32, h as u32, (w * 2) as u32);
let mut rgb_v210 = std::vec![0u8; w * h * 3];
let mut rgb_y210 = std::vec![0u8; w * h * 3];
let mut s_v210 = MixedSinker::<V210>::new(w, h)
.with_rgb(&mut rgb_v210)
.unwrap();
let mut s_y210 = MixedSinker::<Y210>::new(w, h)
.with_rgb(&mut rgb_y210)
.unwrap();
v210_to(&v210_src, true, ColorMatrix::Bt2020Ncl, &mut s_v210).unwrap();
y210_to(&y210_src, true, ColorMatrix::Bt2020Ncl, &mut s_y210).unwrap();
assert_eq!(rgb_v210, rgb_y210);
}
fn y210_as_be_u16(host: &[u16]) -> Vec<u16> {
host
.iter()
.map(|v| u16::from_ne_bytes(v.to_be_bytes()))
.collect()
}
#[test]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn y210_le_be_roundtrip_byte_identical() {
let intended = solid_y210_frame(8, 4, 600, 400, 700);
let pix_le = intended.clone();
let pix_be = y210_as_be_u16(&intended);
let frame_le = Y210LeFrame::try_new(&pix_le, 8, 4, 16).unwrap();
let mut out_le_rgba = std::vec![0u8; 8 * 4 * 4];
let mut out_le_luma_u16 = std::vec![0u16; 8 * 4];
let mut sink_le = MixedSinker::<Y210>::new(8, 4)
.with_simd(false)
.with_rgba(&mut out_le_rgba)
.unwrap()
.with_luma_u16(&mut out_le_luma_u16)
.unwrap();
y210_to(&frame_le, true, ColorMatrix::Bt709, &mut sink_le).unwrap();
let frame_be = Y210BeFrame::try_new(&pix_be, 8, 4, 16).unwrap();
let mut out_be_rgba = std::vec![0u8; 8 * 4 * 4];
let mut out_be_luma_u16 = std::vec![0u16; 8 * 4];
let mut sink_be = MixedSinker::<Y210<true>>::new(8, 4)
.with_simd(false)
.with_rgba(&mut out_be_rgba)
.unwrap()
.with_luma_u16(&mut out_be_luma_u16)
.unwrap();
y210_to_endian(&frame_be, true, ColorMatrix::Bt709, &mut sink_be).unwrap();
assert_eq!(
out_le_rgba, out_be_rgba,
"Y210 RGBA u8 LE/BE outputs diverge — `<const BE>` propagation broken"
);
assert_eq!(
out_le_luma_u16, out_be_luma_u16,
"Y210 luma u16 LE/BE outputs diverge — `<const BE>` propagation broken"
);
}