use crate::{
ColorMatrix,
frame::{Gray8Frame, Gray16Frame, GrayNFrame, Grayf32Frame, Ya8Frame, Ya16Frame},
sinker::MixedSinker,
source::{
gray8_to, gray9_to, gray9_to_endian, gray10_to, gray10_to_endian, gray12_to, gray12_to_endian,
gray14_to, gray14_to_endian, gray16_to, gray16_to_endian, grayf32_to, grayf32_to_endian,
ya8_to, ya16_to, ya16_to_endian,
},
};
fn as_le_u16(host: &[u16]) -> std::vec::Vec<u16> {
host
.iter()
.map(|v| u16::from_ne_bytes(v.to_le_bytes()))
.collect()
}
fn as_le_f32(host: &[f32]) -> std::vec::Vec<f32> {
host
.iter()
.map(|v| f32::from_bits(u32::from_ne_bytes(v.to_bits().to_le_bytes())))
.collect()
}
const FR: bool = true;
const M: ColorMatrix = ColorMatrix::Bt709;
fn make_gray8_frame(data: &[u8], w: u32, h: u32) -> Gray8Frame<'_> {
Gray8Frame::new(data, w, h, w)
}
fn make_gray10_frame(data: &[u16], w: u32, h: u32) -> GrayNFrame<'_, 10> {
GrayNFrame::new(data, w, h, w)
}
fn make_gray16_frame(data: &[u16], w: u32, h: u32) -> Gray16Frame<'_> {
Gray16Frame::new(data, w, h, w)
}
#[test]
fn gray8_with_rgb_broadcasts_to_packed() {
let plane = [0u8, 64, 128, 255];
let frame = make_gray8_frame(&plane, 4, 1);
let mut rgb = std::vec![0u8; 4 * 3];
let mut sink = MixedSinker::<crate::source::Gray8>::new(4, 1)
.with_rgb(&mut rgb)
.unwrap();
gray8_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(rgb[0..3], [0, 0, 0]);
assert_eq!(rgb[3..6], [64, 64, 64]);
assert_eq!(rgb[6..9], [128, 128, 128]);
assert_eq!(rgb[9..12], [255, 255, 255]);
}
#[test]
fn gray8_with_rgba_alpha_is_0xff() {
let plane = [100u8; 4];
let frame = make_gray8_frame(&plane, 4, 1);
let mut rgba = std::vec![0u8; 4 * 4];
let mut sink = MixedSinker::<crate::source::Gray8>::new(4, 1)
.with_rgba(&mut rgba)
.unwrap();
gray8_to(&frame, FR, M, &mut sink).unwrap();
for i in 0..4 {
assert_eq!(rgba[i * 4 + 3], 0xFF, "pixel {i} alpha");
assert_eq!(rgba[i * 4], 100, "pixel {i} R");
}
}
#[test]
fn gray8_with_luma_copies_plane() {
let plane: Vec<u8> = (0..16u8).collect();
let frame = make_gray8_frame(&plane, 4, 4);
let mut luma = std::vec![0u8; 16];
let mut sink = MixedSinker::<crate::source::Gray8>::new(4, 4)
.with_luma(&mut luma)
.unwrap();
gray8_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(luma, plane);
}
#[test]
fn gray8_with_luma_u16_zero_extends() {
let plane = [0u8, 64, 128, 255];
let frame = make_gray8_frame(&plane, 4, 1);
let mut lu16 = std::vec![0u16; 4];
let mut sink = MixedSinker::<crate::source::Gray8>::new(4, 1)
.with_luma_u16(&mut lu16)
.unwrap();
gray8_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(lu16, [0, 64, 128, 255]);
}
#[test]
fn gray8_with_hsv_h_s_zero_v_equals_y() {
let plane = [50u8, 100, 200, 0];
let frame = make_gray8_frame(&plane, 4, 1);
let mut h = std::vec![0xFFu8; 4];
let mut s = std::vec![0xFFu8; 4];
let mut v = std::vec![0u8; 4];
let mut sink = MixedSinker::<crate::source::Gray8>::new(4, 1)
.with_hsv(&mut h, &mut s, &mut v)
.unwrap();
gray8_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(h, [0, 0, 0, 0], "H must be 0");
assert_eq!(s, [0, 0, 0, 0], "S must be 0");
assert_eq!(v, plane.as_slice(), "V must equal Y");
}
#[test]
fn gray10_with_rgb_masks_and_shifts() {
let plane = as_le_u16(&[512u16; 4]);
let frame = make_gray10_frame(&plane, 4, 1);
let mut rgb = std::vec![0u8; 12];
let mut sink = MixedSinker::<crate::source::Gray10>::new(4, 1)
.with_rgb(&mut rgb)
.unwrap();
gray10_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(rgb[0..3], [128, 128, 128]);
assert_eq!(rgb[3..6], [128, 128, 128]);
}
#[test]
fn gray10_with_luma_u16_masks_only() {
let plane = as_le_u16(&[0x0800u16, 0x03FFu16, 0x0200u16, 0x0001u16]);
let frame = make_gray10_frame(&plane, 4, 1);
let mut lu16 = std::vec![0u16; 4];
let mut sink = MixedSinker::<crate::source::Gray10>::new(4, 1)
.with_luma_u16(&mut lu16)
.unwrap();
gray10_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(lu16, [0x0000, 0x03FF, 0x0200, 0x0001]);
}
#[test]
fn gray16_with_rgb_shifts_to_u8() {
let plane = as_le_u16(&[0x8000u16, 0xFFFFu16, 0x0000u16, 0x0100u16]);
let frame = make_gray16_frame(&plane, 4, 1);
let mut rgb = std::vec![0u8; 12];
let mut sink = MixedSinker::<crate::source::Gray16>::new(4, 1)
.with_rgb(&mut rgb)
.unwrap();
gray16_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(rgb[0..3], [0x80, 0x80, 0x80]);
assert_eq!(rgb[3..6], [0xFF, 0xFF, 0xFF]);
assert_eq!(rgb[6..9], [0x00, 0x00, 0x00]);
assert_eq!(rgb[9..12], [0x01, 0x01, 0x01]);
}
#[test]
fn gray16_with_luma_u16_copies_plane() {
let intended: Vec<u16> = (0u16..16).map(|x| x * 4096).collect();
let plane = as_le_u16(&intended);
let frame = make_gray16_frame(&plane, 4, 4);
let mut lu16 = std::vec![0u16; 16];
let mut sink = MixedSinker::<crate::source::Gray16>::new(4, 4)
.with_luma_u16(&mut lu16)
.unwrap();
gray16_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(lu16, intended);
}
#[test]
fn gray16_with_rgba_u16_alpha_is_0xffff() {
let plane = as_le_u16(&[0x1234u16; 4]);
let frame = make_gray16_frame(&plane, 4, 1);
let mut rgba_u16 = std::vec![0u16; 16];
let mut sink = MixedSinker::<crate::source::Gray16>::new(4, 1)
.with_rgba_u16(&mut rgba_u16)
.unwrap();
gray16_to(&frame, FR, M, &mut sink).unwrap();
for i in 0..4 {
assert_eq!(rgba_u16[i * 4 + 3], 0xFFFF, "pixel {i} alpha");
assert_eq!(rgba_u16[i * 4], 0x1234, "pixel {i} R");
}
}
#[test]
fn gray9_walker_smoke_test() {
use crate::frame::GrayNFrame;
let plane = as_le_u16(&[100u16; 4]);
let frame: GrayNFrame<'_, 9> = GrayNFrame::new(&plane, 4, 1, 4);
let mut luma = std::vec![0u8; 4];
let mut sink = MixedSinker::<crate::source::Gray9>::new(4, 1)
.with_luma(&mut luma)
.unwrap();
gray9_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(luma, [50, 50, 50, 50]);
}
#[test]
fn gray12_walker_smoke_test() {
use crate::frame::GrayNFrame;
let plane = as_le_u16(&[0x0FFFu16; 4]);
let frame: GrayNFrame<'_, 12> = GrayNFrame::new(&plane, 4, 1, 4);
let mut luma = std::vec![0u8; 4];
let mut sink = MixedSinker::<crate::source::Gray12>::new(4, 1)
.with_luma(&mut luma)
.unwrap();
gray12_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(luma, [255, 255, 255, 255]);
}
#[test]
fn gray14_walker_smoke_test() {
use crate::frame::GrayNFrame;
let plane = as_le_u16(&[0x3FFFu16; 4]);
let frame: GrayNFrame<'_, 14> = GrayNFrame::new(&plane, 4, 1, 4);
let mut luma = std::vec![0u8; 4];
let mut sink = MixedSinker::<crate::source::Gray14>::new(4, 1)
.with_luma(&mut luma)
.unwrap();
gray14_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(luma, [255, 255, 255, 255]);
}
#[test]
fn gray8_limited_range_black_maps_to_zero() {
let plane = [16u8; 4];
let frame = make_gray8_frame(&plane, 4, 1);
let mut rgb = std::vec![0xFFu8; 12];
let mut sink = MixedSinker::<crate::source::Gray8>::new(4, 1)
.with_rgb(&mut rgb)
.unwrap();
gray8_to(&frame, false, M, &mut sink).unwrap();
for i in 0..4 {
assert_eq!(rgb[i * 3..i * 3 + 3], [0, 0, 0], "pixel {i}");
}
}
#[test]
fn gray8_limited_range_white_maps_to_255() {
let plane = [235u8; 4];
let frame = make_gray8_frame(&plane, 4, 1);
let mut rgb = std::vec![0u8; 12];
let mut sink = MixedSinker::<crate::source::Gray8>::new(4, 1)
.with_rgb(&mut rgb)
.unwrap();
gray8_to(&frame, false, M, &mut sink).unwrap();
for i in 0..4 {
assert_eq!(rgb[i * 3..i * 3 + 3], [255, 255, 255], "pixel {i}");
}
}
#[test]
fn gray8_limited_range_midpoint() {
let plane = [125u8; 4];
let frame = make_gray8_frame(&plane, 4, 1);
let mut rgb = std::vec![0u8; 12];
let mut sink = MixedSinker::<crate::source::Gray8>::new(4, 1)
.with_rgb(&mut rgb)
.unwrap();
gray8_to(&frame, false, M, &mut sink).unwrap();
for i in 0..4 {
assert_eq!(rgb[i * 3], 127, "pixel {i} R");
}
}
#[test]
fn gray8_limited_range_luma_passthrough_unchanged() {
let plane = [16u8, 235u8, 125u8, 0u8];
let frame = make_gray8_frame(&plane, 4, 1);
let mut luma = std::vec![0xAAu8; 4];
let mut sink = MixedSinker::<crate::source::Gray8>::new(4, 1)
.with_luma(&mut luma)
.unwrap();
gray8_to(&frame, false, M, &mut sink).unwrap();
assert_eq!(luma, [16, 235, 125, 0]);
}
#[test]
fn gray8_limited_range_rgba_alpha_is_0xff() {
let plane = [235u8; 4];
let frame = make_gray8_frame(&plane, 4, 1);
let mut rgba = std::vec![0u8; 16];
let mut sink = MixedSinker::<crate::source::Gray8>::new(4, 1)
.with_rgba(&mut rgba)
.unwrap();
gray8_to(&frame, false, M, &mut sink).unwrap();
for i in 0..4 {
assert_eq!(rgba[i * 4], 255, "pixel {i} R");
assert_eq!(rgba[i * 4 + 3], 0xFF, "pixel {i} alpha");
}
}
#[test]
fn gray8_limited_range_hsv_v_is_rescaled() {
let plane = [235u8; 4];
let frame = make_gray8_frame(&plane, 4, 1);
let mut h = std::vec![0xFFu8; 4];
let mut s = std::vec![0xFFu8; 4];
let mut v = std::vec![0u8; 4];
let mut sink = MixedSinker::<crate::source::Gray8>::new(4, 1)
.with_hsv(&mut h, &mut s, &mut v)
.unwrap();
gray8_to(&frame, false, M, &mut sink).unwrap();
assert_eq!(h, [0, 0, 0, 0], "H must be 0");
assert_eq!(s, [0, 0, 0, 0], "S must be 0");
assert_eq!(v, [255, 255, 255, 255], "V must be 255 for white");
}
#[test]
fn gray10_limited_range_black_and_white() {
use crate::frame::GrayNFrame;
let plane = as_le_u16(&[64u16, 940, 64, 940]);
let frame: GrayNFrame<'_, 10> = GrayNFrame::new(&plane, 4, 1, 4);
let mut rgb = std::vec![0x80u8; 12];
let mut sink = MixedSinker::<crate::source::Gray10>::new(4, 1)
.with_rgb(&mut rgb)
.unwrap();
gray10_to(&frame, false, M, &mut sink).unwrap();
assert_eq!(rgb[0..3], [0, 0, 0], "Y=64 → black");
assert_eq!(rgb[3..6], [255, 255, 255], "Y=940 → white");
assert_eq!(rgb[6..9], [0, 0, 0], "Y=64 → black");
assert_eq!(rgb[9..12], [255, 255, 255], "Y=940 → white");
}
#[test]
fn gray12_limited_range_black_and_white() {
use crate::frame::GrayNFrame;
let plane = as_le_u16(&[256u16, 3760, 256, 3760]);
let frame: GrayNFrame<'_, 12> = GrayNFrame::new(&plane, 4, 1, 4);
let mut rgb = std::vec![0x80u8; 12];
let mut sink = MixedSinker::<crate::source::Gray12>::new(4, 1)
.with_rgb(&mut rgb)
.unwrap();
gray12_to(&frame, false, M, &mut sink).unwrap();
assert_eq!(rgb[0..3], [0, 0, 0], "Y=256 → black");
assert_eq!(rgb[3..6], [255, 255, 255], "Y=3760 → white");
assert_eq!(rgb[6..9], [0, 0, 0], "Y=256 → black");
assert_eq!(rgb[9..12], [255, 255, 255], "Y=3760 → white");
}
#[test]
fn gray14_limited_range_black_and_white() {
use crate::frame::GrayNFrame;
let plane = as_le_u16(&[1024u16, 15040, 1024, 15040]);
let frame: GrayNFrame<'_, 14> = GrayNFrame::new(&plane, 4, 1, 4);
let mut rgb = std::vec![0x80u8; 12];
let mut sink = MixedSinker::<crate::source::Gray14>::new(4, 1)
.with_rgb(&mut rgb)
.unwrap();
gray14_to(&frame, false, M, &mut sink).unwrap();
assert_eq!(rgb[0..3], [0, 0, 0], "Y=1024 → black");
assert_eq!(rgb[3..6], [255, 255, 255], "Y=15040 → white");
assert_eq!(rgb[6..9], [0, 0, 0], "Y=1024 → black");
assert_eq!(rgb[9..12], [255, 255, 255], "Y=15040 → white");
}
#[test]
fn gray16_limited_range_black_and_white() {
let plane = as_le_u16(&[4096u16, 60160, 4096, 60160]);
let frame = make_gray16_frame(&plane, 4, 1);
let mut rgb = std::vec![0x80u8; 12];
let mut sink = MixedSinker::<crate::source::Gray16>::new(4, 1)
.with_rgb(&mut rgb)
.unwrap();
gray16_to(&frame, false, M, &mut sink).unwrap();
assert_eq!(rgb[0..3], [0, 0, 0], "Y=4096 → black");
assert_eq!(rgb[3..6], [255, 255, 255], "Y=60160 → white");
assert_eq!(rgb[6..9], [0, 0, 0], "Y=4096 → black");
assert_eq!(rgb[9..12], [255, 255, 255], "Y=60160 → white");
}
#[test]
fn gray16_limited_range_luma_passthrough_unchanged() {
let plane = as_le_u16(&[4096u16, 60160, 32768, 0]);
let frame = make_gray16_frame(&plane, 4, 1);
let mut lu16 = std::vec![0u16; 4];
let mut sink = MixedSinker::<crate::source::Gray16>::new(4, 1)
.with_luma_u16(&mut lu16)
.unwrap();
gray16_to(&frame, false, M, &mut sink).unwrap();
assert_eq!(lu16, [4096, 60160, 32768, 0]);
}
#[test]
fn gray16_limited_range_rgba_u16_alpha_is_0xffff() {
let plane = as_le_u16(&[4096u16; 4]);
let frame = make_gray16_frame(&plane, 4, 1);
let mut rgba_u16 = std::vec![0u16; 16];
let mut sink = MixedSinker::<crate::source::Gray16>::new(4, 1)
.with_rgba_u16(&mut rgba_u16)
.unwrap();
gray16_to(&frame, false, M, &mut sink).unwrap();
for i in 0..4 {
assert_eq!(rgba_u16[i * 4 + 3], 0xFFFF, "pixel {i} alpha");
}
}
#[test]
fn gray16_limited_range_rgba_u16_channels_rescale_at_boundaries() {
let plane = as_le_u16(&[4096u16, 60160u16, 65535u16, 0u16]);
let frame = make_gray16_frame(&plane, 4, 1);
let mut rgba_u16 = std::vec![0u16; 16];
let mut sink = MixedSinker::<crate::source::Gray16>::new(4, 1)
.with_rgba_u16(&mut rgba_u16)
.unwrap();
gray16_to(&frame, false, M, &mut sink).unwrap();
assert_eq!(&rgba_u16[0..3], &[0, 0, 0]);
assert_eq!(&rgba_u16[4..7], &[65535, 65535, 65535]);
assert_eq!(&rgba_u16[8..11], &[65535, 65535, 65535]);
assert_eq!(&rgba_u16[12..15], &[0, 0, 0]);
for i in 0..4 {
assert_eq!(rgba_u16[i * 4 + 3], 0xFFFF);
}
}
#[test]
fn gray16_limited_range_rgb_u16_channels_rescale_at_boundaries() {
let plane = as_le_u16(&[4096u16, 60160u16]);
let frame = make_gray16_frame(&plane, 2, 1);
let mut rgb_u16 = std::vec![0u16; 6];
let mut sink = MixedSinker::<crate::source::Gray16>::new(2, 1)
.with_rgb_u16(&mut rgb_u16)
.unwrap();
gray16_to(&frame, false, M, &mut sink).unwrap();
assert_eq!(&rgb_u16[0..3], &[0, 0, 0]);
assert_eq!(&rgb_u16[3..6], &[65535, 65535, 65535]);
}
#[test]
fn gray16_limited_range_hsv_v_is_rescaled() {
let plane = as_le_u16(&[60160u16; 4]); let frame = make_gray16_frame(&plane, 4, 1);
let mut h = std::vec![0xFFu8; 4];
let mut s = std::vec![0xFFu8; 4];
let mut v = std::vec![0u8; 4];
let mut sink = MixedSinker::<crate::source::Gray16>::new(4, 1)
.with_hsv(&mut h, &mut s, &mut v)
.unwrap();
gray16_to(&frame, false, M, &mut sink).unwrap();
assert_eq!(h, [0, 0, 0, 0], "H must be 0");
assert_eq!(s, [0, 0, 0, 0], "S must be 0");
assert_eq!(v, [255, 255, 255, 255], "V must be 255 for white");
}
#[test]
fn grayf32_with_luma_f32_passthrough() {
let intended: std::vec::Vec<f32> = std::vec![0.0, 0.25, 0.5, 0.75, 1.0, 1.5, -0.5, f32::NAN];
let plane = as_le_f32(&intended);
let frame = Grayf32Frame::new(&plane, 8, 1, 8);
let mut out = std::vec![0.0f32; 8];
let mut sink = MixedSinker::<crate::source::Grayf32>::new(8, 1)
.with_luma_f32(&mut out)
.unwrap();
grayf32_to(&frame, FR, M, &mut sink).unwrap();
for (i, (&a, &b)) in intended.iter().zip(out.iter()).enumerate() {
if a.is_nan() {
assert!(b.is_nan(), "pixel {i}: expected NaN");
} else {
assert_eq!(a, b, "pixel {i}");
}
}
}
#[test]
fn grayf32_with_rgb_f32_replicates_losslessly() {
let intended: std::vec::Vec<f32> = std::vec![0.25, 0.75, 1.5, -0.5];
let plane = as_le_f32(&intended);
let frame = Grayf32Frame::new(&plane, 4, 1, 4);
let mut out = std::vec![0.0f32; 4 * 3];
let mut sink = MixedSinker::<crate::source::Grayf32>::new(4, 1)
.with_rgb_f32(&mut out)
.unwrap();
grayf32_to(&frame, FR, M, &mut sink).unwrap();
for (x, &y) in intended.iter().enumerate() {
assert_eq!(out[x * 3], y, "pixel {x} R");
assert_eq!(out[x * 3 + 1], y, "pixel {x} G");
assert_eq!(out[x * 3 + 2], y, "pixel {x} B");
}
}
#[test]
fn grayf32_with_rgb_saturates() {
let intended: std::vec::Vec<f32> = std::vec![-0.5, 0.0, 0.5, 1.0, 1.5];
let plane = as_le_f32(&intended);
let frame = Grayf32Frame::new(&plane, 5, 1, 5);
let mut rgb = std::vec![0u8; 5 * 3];
let mut sink = MixedSinker::<crate::source::Grayf32>::new(5, 1)
.with_rgb(&mut rgb)
.unwrap();
grayf32_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(&rgb[0..3], &[0, 0, 0]); assert_eq!(&rgb[3..6], &[0, 0, 0]); assert_eq!(&rgb[6..9], &[128, 128, 128]); assert_eq!(&rgb[9..12], &[255, 255, 255]); assert_eq!(&rgb[12..15], &[255, 255, 255]); }
#[test]
fn grayf32_with_hsv_h0_s0_v_saturated() {
let intended: std::vec::Vec<f32> = std::vec![0.0, 0.5, 1.0];
let plane = as_le_f32(&intended);
let frame = Grayf32Frame::new(&plane, 3, 1, 3);
let mut h = std::vec![0xFFu8; 3];
let mut s = std::vec![0xFFu8; 3];
let mut v = std::vec![0u8; 3];
let mut sink = MixedSinker::<crate::source::Grayf32>::new(3, 1)
.with_hsv(&mut h, &mut s, &mut v)
.unwrap();
grayf32_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(h, [0, 0, 0]);
assert_eq!(s, [0, 0, 0]);
assert_eq!(v, [0, 128, 255]);
}
#[test]
fn grayf32_with_luma_u16_and_rgb_u16() {
let intended = std::vec![0.5f32];
let plane = as_le_f32(&intended);
let frame = Grayf32Frame::new(&plane, 1, 1, 1);
let mut lu16 = std::vec![0u16; 1];
let mut rgb_u16 = std::vec![0u16; 3];
let mut sink = MixedSinker::<crate::source::Grayf32>::new(1, 1)
.with_luma_u16(&mut lu16)
.unwrap()
.with_rgb_u16(&mut rgb_u16)
.unwrap();
grayf32_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(lu16[0], 32768);
assert_eq!(rgb_u16, [32768, 32768, 32768]);
}
#[test]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn grayf32_width_128_and_130_smoke() {
for &w in &[128usize, 130usize] {
let intended: std::vec::Vec<f32> = (0..w).map(|i| i as f32 / w as f32).collect();
let plane = as_le_f32(&intended);
let frame = Grayf32Frame::new(&plane, w as u32, 1, w as u32);
let mut rgb = std::vec![0u8; w * 3];
let mut luma_f32 = std::vec![0.0f32; w];
let mut sink = MixedSinker::<crate::source::Grayf32>::new(w, 1)
.with_rgb(&mut rgb)
.unwrap()
.with_luma_f32(&mut luma_f32)
.unwrap();
grayf32_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(rgb[0], 0, "w={w} first R");
assert_eq!(luma_f32[0], 0.0, "w={w} first luma_f32");
assert!(luma_f32[w - 1] > 0.9, "w={w} last luma_f32");
}
}
#[test]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn grayf32_sinker_le_encoded_frame_decodes_correctly() {
use crate::source::Grayf32;
let w = 16usize;
let h = 4usize;
let mut intended = std::vec![0.0f32; w * h];
for (i, v) in intended.iter_mut().enumerate() {
*v = match i % 4 {
0 => 0.5,
1 => 1.5,
2 => -0.25,
_ => 100.0,
};
}
let le_plane: std::vec::Vec<f32> = intended
.iter()
.map(|&v| f32::from_bits(v.to_bits().to_le()))
.collect();
let frame = Grayf32Frame::new(&le_plane, w as u32, h as u32, w as u32);
let mut luma_f32_out = std::vec![0.0f32; w * h];
{
let mut sink = MixedSinker::<Grayf32>::new(w, h)
.with_luma_f32(&mut luma_f32_out)
.unwrap();
grayf32_to(&frame, FR, M, &mut sink).unwrap();
}
assert_eq!(
luma_f32_out, intended,
"Grayf32 sinker failed to decode LE-encoded plane to host-native"
);
let mut rgb_f32_out = std::vec![0.0f32; w * h * 3];
{
let mut sink = MixedSinker::<Grayf32>::new(w, h)
.with_rgb_f32(&mut rgb_f32_out)
.unwrap();
grayf32_to(&frame, FR, M, &mut sink).unwrap();
}
for (x, &y) in intended.iter().enumerate() {
assert_eq!(rgb_f32_out[x * 3], y, "pixel {x} R diverges");
assert_eq!(rgb_f32_out[x * 3 + 1], y, "pixel {x} G diverges");
assert_eq!(rgb_f32_out[x * 3 + 2], y, "pixel {x} B diverges");
}
}
#[test]
fn ya8_with_rgb_and_rgba_strategy_a_plus() {
let packed: std::vec::Vec<u8> = std::vec![100, 200, 50, 150];
let frame = Ya8Frame::new(&packed, 2, 1, 4);
let mut rgb = std::vec![0u8; 6];
let mut rgba = std::vec![0u8; 8];
let mut sink = MixedSinker::<crate::source::Ya8>::new(2, 1)
.with_rgb(&mut rgb)
.unwrap()
.with_rgba(&mut rgba)
.unwrap();
ya8_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(&rgb[0..3], &[100, 100, 100]);
assert_eq!(&rgb[3..6], &[50, 50, 50]);
assert_eq!(&rgba[0..4], &[100, 100, 100, 200]);
assert_eq!(&rgba[4..8], &[50, 50, 50, 150]);
}
#[test]
fn ya8_standalone_rgba_source_alpha() {
let packed: std::vec::Vec<u8> = std::vec![77, 11, 88, 22];
let frame = Ya8Frame::new(&packed, 2, 1, 4);
let mut rgba = std::vec![0u8; 8];
let mut sink = MixedSinker::<crate::source::Ya8>::new(2, 1)
.with_rgba(&mut rgba)
.unwrap();
ya8_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(&rgba[0..4], &[77, 77, 77, 11]);
assert_eq!(&rgba[4..8], &[88, 88, 88, 22]);
}
#[test]
fn ya8_with_luma_u16_zero_extends() {
let packed: std::vec::Vec<u8> = std::vec![200, 50, 100, 25];
let frame = Ya8Frame::new(&packed, 2, 1, 4);
let mut lu16 = std::vec![0u16; 2];
let mut sink = MixedSinker::<crate::source::Ya8>::new(2, 1)
.with_luma_u16(&mut lu16)
.unwrap();
ya8_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(lu16, [200, 100]);
}
#[test]
fn ya8_with_hsv_h0_s0_v_y() {
let packed: std::vec::Vec<u8> = std::vec![200, 50, 100, 25];
let frame = Ya8Frame::new(&packed, 2, 1, 4);
let mut h = std::vec![0xFFu8; 2];
let mut s = std::vec![0xFFu8; 2];
let mut v = std::vec![0u8; 2];
let mut sink = MixedSinker::<crate::source::Ya8>::new(2, 1)
.with_hsv(&mut h, &mut s, &mut v)
.unwrap();
ya8_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(h, [0, 0]);
assert_eq!(s, [0, 0]);
assert_eq!(v, [200, 100]);
}
#[test]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn ya8_width_128_and_130_smoke() {
for &w in &[128usize, 130usize] {
let packed: std::vec::Vec<u8> = (0..w).flat_map(|i| [i as u8, (255 - i as u8)]).collect();
let frame = Ya8Frame::new(&packed, w as u32, 1, (w * 2) as u32);
let mut rgb = std::vec![0u8; w * 3];
let mut rgba = std::vec![0u8; w * 4];
let mut sink = MixedSinker::<crate::source::Ya8>::new(w, 1)
.with_rgb(&mut rgb)
.unwrap()
.with_rgba(&mut rgba)
.unwrap();
ya8_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(&rgb[0..3], &[0, 0, 0], "w={w}");
assert_eq!(&rgba[0..4], &[0, 0, 0, 255], "w={w}");
}
}
#[test]
fn ya16_with_rgba_u16_source_alpha() {
let packed = as_le_u16(&[0x8000u16, 0x4000]);
let frame = Ya16Frame::new(&packed, 1, 1, 2);
let mut rgba_u16 = std::vec![0u16; 4];
let mut luma_u16 = std::vec![0u16; 1];
let mut sink = MixedSinker::<crate::source::Ya16>::new(1, 1)
.with_rgba_u16(&mut rgba_u16)
.unwrap()
.with_luma_u16(&mut luma_u16)
.unwrap();
ya16_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(&rgba_u16, &[0x8000, 0x8000, 0x8000, 0x4000]);
assert_eq!(luma_u16[0], 0x8000);
}
#[test]
fn ya16_with_rgba_u8_source_alpha_shifted() {
let packed = as_le_u16(&[0x8000u16, 0x4000, 0xFFFF, 0x8000]);
let frame = Ya16Frame::new(&packed, 2, 1, 4);
let mut rgba = std::vec![0u8; 8];
let mut sink = MixedSinker::<crate::source::Ya16>::new(2, 1)
.with_rgba(&mut rgba)
.unwrap();
ya16_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(&rgba[0..4], &[0x80, 0x80, 0x80, 0x40]);
assert_eq!(&rgba[4..8], &[0xFF, 0xFF, 0xFF, 0x80]);
}
#[test]
fn ya16_with_rgb_and_rgba_strategy_a_plus() {
let packed = as_le_u16(&[0x8000u16, 0x4000, 0x2000, 0xC000]);
let frame = Ya16Frame::new(&packed, 2, 1, 4);
let mut rgb = std::vec![0u8; 6];
let mut rgba = std::vec![0u8; 8];
let mut sink = MixedSinker::<crate::source::Ya16>::new(2, 1)
.with_rgb(&mut rgb)
.unwrap()
.with_rgba(&mut rgba)
.unwrap();
ya16_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(&rgb[0..3], &[0x80, 0x80, 0x80]);
assert_eq!(&rgba[0..4], &[0x80, 0x80, 0x80, 0x40]);
assert_eq!(&rgb[3..6], &[0x20, 0x20, 0x20]);
assert_eq!(&rgba[4..8], &[0x20, 0x20, 0x20, 0xC0]);
}
#[test]
fn ya16_with_hsv_h0_s0_v_shifted() {
let packed = as_le_u16(&[0x8000u16, 0x4000, 0xFFFF, 0x0000]);
let frame = Ya16Frame::new(&packed, 2, 1, 4);
let mut h = std::vec![0xFFu8; 2];
let mut s = std::vec![0xFFu8; 2];
let mut v = std::vec![0u8; 2];
let mut sink = MixedSinker::<crate::source::Ya16>::new(2, 1)
.with_hsv(&mut h, &mut s, &mut v)
.unwrap();
ya16_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(h, [0, 0]);
assert_eq!(s, [0, 0]);
assert_eq!(v, [0x80, 0xFF]);
}
#[test]
fn ya16_combined_rgb_and_rgba_alpha_matches_standalone_le_encoded() {
let w: u32 = 8;
let h: u32 = 1;
let samples: [(u16, u16); 8] = [
(0x0000, 0xFFFF),
(0x8000, 0x4000),
(0xFFFF, 0x0000),
(0x1234, 0xABCD),
(0x00FF, 0xFF00),
(0x5A5A, 0xA5A5),
(0x7FFF, 0x8000),
(0xC000, 0x3FFF),
];
let le_encoded = |x: u16| -> u16 { u16::from_ne_bytes(x.to_le_bytes()) };
let packed: std::vec::Vec<u16> = samples
.iter()
.flat_map(|&(y, a)| [le_encoded(y), le_encoded(a)])
.collect();
let frame = Ya16Frame::new(&packed, w, h, w * 2);
let mut rgb_combined = std::vec![0u8; (w * h * 3) as usize];
let mut rgba_combined = std::vec![0u8; (w * h * 4) as usize];
{
let mut sink = MixedSinker::<crate::source::Ya16>::new(w as usize, h as usize)
.with_simd(false)
.with_rgb(&mut rgb_combined)
.unwrap()
.with_rgba(&mut rgba_combined)
.unwrap();
ya16_to(&frame, FR, M, &mut sink).unwrap();
}
let mut rgba_standalone = std::vec![0u8; (w * h * 4) as usize];
{
let mut sink = MixedSinker::<crate::source::Ya16>::new(w as usize, h as usize)
.with_simd(false)
.with_rgba(&mut rgba_standalone)
.unwrap();
ya16_to(&frame, FR, M, &mut sink).unwrap();
}
assert_eq!(
rgba_combined, rgba_standalone,
"combined (with_rgb+with_rgba) RGBA must equal standalone with_rgba"
);
}
#[test]
fn ya16_combined_rgb_u16_and_rgba_u16_alpha_matches_standalone_le_encoded() {
let w: u32 = 8;
let h: u32 = 1;
let samples: [(u16, u16); 8] = [
(0x0000, 0xFFFF),
(0x8000, 0x4000),
(0xFFFF, 0x0000),
(0x1234, 0xABCD),
(0x00FF, 0xFF00),
(0x5A5A, 0xA5A5),
(0x7FFF, 0x8000),
(0xC000, 0x3FFF),
];
let le_encoded = |x: u16| -> u16 { u16::from_ne_bytes(x.to_le_bytes()) };
let packed: std::vec::Vec<u16> = samples
.iter()
.flat_map(|&(y, a)| [le_encoded(y), le_encoded(a)])
.collect();
let frame = Ya16Frame::new(&packed, w, h, w * 2);
let mut rgb_combined = std::vec![0u16; (w * h * 3) as usize];
let mut rgba_combined = std::vec![0u16; (w * h * 4) as usize];
{
let mut sink = MixedSinker::<crate::source::Ya16>::new(w as usize, h as usize)
.with_simd(false)
.with_rgb_u16(&mut rgb_combined)
.unwrap()
.with_rgba_u16(&mut rgba_combined)
.unwrap();
ya16_to(&frame, FR, M, &mut sink).unwrap();
}
let mut rgba_standalone = std::vec![0u16; (w * h * 4) as usize];
{
let mut sink = MixedSinker::<crate::source::Ya16>::new(w as usize, h as usize)
.with_simd(false)
.with_rgba_u16(&mut rgba_standalone)
.unwrap();
ya16_to(&frame, FR, M, &mut sink).unwrap();
}
assert_eq!(
rgba_combined, rgba_standalone,
"combined (with_rgb_u16+with_rgba_u16) RGBA u16 must equal standalone"
);
}
#[test]
#[cfg_attr(
miri,
ignore = "SIMD-dispatched row kernels use intrinsics unsupported by Miri"
)]
fn ya16_width_128_and_130_smoke() {
for &w in &[128usize, 130usize] {
let intended: std::vec::Vec<u16> = (0..w)
.flat_map(|i| [(i as u16) << 8, (255u16 - i as u16) << 8])
.collect();
let packed = as_le_u16(&intended);
let frame = Ya16Frame::new(&packed, w as u32, 1, (w * 2) as u32);
let mut rgba = std::vec![0u8; w * 4];
let mut luma_u16 = std::vec![0u16; w];
let mut sink = MixedSinker::<crate::source::Ya16>::new(w, 1)
.with_rgba(&mut rgba)
.unwrap()
.with_luma_u16(&mut luma_u16)
.unwrap();
ya16_to(&frame, FR, M, &mut sink).unwrap();
assert_eq!(&rgba[0..4], &[0, 0, 0, 0xFF], "w={w} px0");
}
}
use crate::{
frame::{
Gray9BeFrame, Gray9LeFrame, Gray10BeFrame, Gray10LeFrame, Gray12BeFrame, Gray12LeFrame,
Gray14BeFrame, Gray14LeFrame, Gray16BeFrame, Gray16LeFrame, Grayf32BeFrame, Grayf32LeFrame,
Ya16BeFrame, Ya16LeFrame,
},
source::{Gray9, Gray10, Gray12, Gray14, Gray16, Grayf32, Ya16},
};
fn as_be_u16(host: &[u16]) -> std::vec::Vec<u16> {
host
.iter()
.map(|v| u16::from_ne_bytes(v.to_be_bytes()))
.collect()
}
fn as_be_f32(host: &[f32]) -> std::vec::Vec<f32> {
host
.iter()
.map(|v| f32::from_bits(u32::from_ne_bytes(v.to_bits().to_be_bytes())))
.collect()
}
macro_rules! gray_planar_u16_le_be_roundtrip_test {
(
label: $label:literal,
marker: $marker:ident,
le_frame: $le_frame:ident,
be_frame: $be_frame:ident,
walker_le: $walker_le:ident,
walker_be: $walker_be:ident,
intended: $intended:expr,
) => {{
let intended: std::vec::Vec<u16> = $intended;
let pix_le = as_le_u16(&intended);
let pix_be = as_be_u16(&intended);
#[cfg(miri)]
let modes: &[bool] = &[false];
#[cfg(not(miri))]
let modes: &[bool] = &[false, true];
for &use_simd in modes {
let frame_le = $le_frame::try_new(&pix_le, 16, 4, 16).unwrap();
let mut out_le = std::vec![0u8; 16 * 4 * 4];
let mut sink_le = MixedSinker::<$marker>::new(16, 4)
.with_simd(use_simd)
.with_rgba(&mut out_le)
.unwrap();
$walker_le(&frame_le, true, M, &mut sink_le).unwrap();
let frame_be = $be_frame::try_new(&pix_be, 16, 4, 16).unwrap();
let mut out_be = std::vec![0u8; 16 * 4 * 4];
let mut sink_be = MixedSinker::<$marker<true>>::new(16, 4)
.with_simd(use_simd)
.with_rgba(&mut out_be)
.unwrap();
$walker_be(&frame_be, true, M, &mut sink_be).unwrap();
assert_eq!(
out_le, out_be,
"{} LE/BE outputs diverge (simd={use_simd}) — `<const BE>` propagation broken",
$label,
);
}
}};
}
#[test]
fn gray9_le_be_roundtrip_byte_identical() {
gray_planar_u16_le_be_roundtrip_test! {
label: "Gray9",
marker: Gray9,
le_frame: Gray9LeFrame,
be_frame: Gray9BeFrame,
walker_le: gray9_to,
walker_be: gray9_to_endian,
intended: (0..16 * 4).map(|i| ((i * 7) as u16) & 0x01FF).collect(),
}
}
#[test]
fn gray10_le_be_roundtrip_byte_identical() {
gray_planar_u16_le_be_roundtrip_test! {
label: "Gray10",
marker: Gray10,
le_frame: Gray10LeFrame,
be_frame: Gray10BeFrame,
walker_le: gray10_to,
walker_be: gray10_to_endian,
intended: (0..16 * 4).map(|i| ((i * 11) as u16) & 0x03FF).collect(),
}
}
#[test]
fn gray12_le_be_roundtrip_byte_identical() {
gray_planar_u16_le_be_roundtrip_test! {
label: "Gray12",
marker: Gray12,
le_frame: Gray12LeFrame,
be_frame: Gray12BeFrame,
walker_le: gray12_to,
walker_be: gray12_to_endian,
intended: (0..16 * 4).map(|i| ((i * 17) as u16) & 0x0FFF).collect(),
}
}
#[test]
fn gray14_le_be_roundtrip_byte_identical() {
gray_planar_u16_le_be_roundtrip_test! {
label: "Gray14",
marker: Gray14,
le_frame: Gray14LeFrame,
be_frame: Gray14BeFrame,
walker_le: gray14_to,
walker_be: gray14_to_endian,
intended: (0..16 * 4).map(|i| ((i * 23) as u16) & 0x3FFF).collect(),
}
}
macro_rules! gray16_dual_output_le_be_roundtrip_test {
(
label: $label:literal,
marker: $marker:ident,
le_frame: $le_frame:ident,
be_frame: $be_frame:ident,
walker_le: $walker_le:ident,
walker_be: $walker_be:ident,
intended: $intended:expr,
) => {{
let intended: std::vec::Vec<u16> = $intended;
let pix_le = as_le_u16(&intended);
let pix_be = as_be_u16(&intended);
#[cfg(miri)]
let modes: &[bool] = &[false];
#[cfg(not(miri))]
let modes: &[bool] = &[false, true];
for &use_simd in modes {
let frame_le = $le_frame::try_new(&pix_le, 16, 4, 16).unwrap();
let mut out_le_rgba = std::vec![0u8; 16 * 4 * 4];
let mut out_le_luma_u16 = std::vec![0u16; 16 * 4];
let mut sink_le = MixedSinker::<$marker>::new(16, 4)
.with_simd(use_simd)
.with_rgba(&mut out_le_rgba)
.unwrap()
.with_luma_u16(&mut out_le_luma_u16)
.unwrap();
$walker_le(&frame_le, true, M, &mut sink_le).unwrap();
let frame_be = $be_frame::try_new(&pix_be, 16, 4, 16).unwrap();
let mut out_be_rgba = std::vec![0u8; 16 * 4 * 4];
let mut out_be_luma_u16 = std::vec![0u16; 16 * 4];
let mut sink_be = MixedSinker::<$marker<true>>::new(16, 4)
.with_simd(use_simd)
.with_rgba(&mut out_be_rgba)
.unwrap()
.with_luma_u16(&mut out_be_luma_u16)
.unwrap();
$walker_be(&frame_be, true, M, &mut sink_be).unwrap();
assert_eq!(
out_le_rgba, out_be_rgba,
"{} RGBA u8 LE/BE outputs diverge (simd={use_simd}) — `<const BE>` propagation broken",
$label,
);
assert_eq!(
out_le_luma_u16, out_be_luma_u16,
"{} luma u16 LE/BE outputs diverge (simd={use_simd}) — `<const BE>` propagation broken",
$label,
);
}
}};
}
#[test]
fn gray16_le_be_roundtrip_byte_identical() {
gray16_dual_output_le_be_roundtrip_test! {
label: "Gray16",
marker: Gray16,
le_frame: Gray16LeFrame,
be_frame: Gray16BeFrame,
walker_le: gray16_to,
walker_be: gray16_to_endian,
intended: (0..16 * 4)
.map(|i| match i % 4 {
0 => 0x1234,
1 => 0xABCD,
2 => 0x00FF,
_ => 0xFF00,
})
.collect(),
}
}
macro_rules! grayf32_le_be_roundtrip_test {
(
label: $label:literal,
marker: $marker:ident,
le_frame: $le_frame:ident,
be_frame: $be_frame:ident,
walker_le: $walker_le:ident,
walker_be: $walker_be:ident,
intended: $intended:expr,
) => {{
let intended: std::vec::Vec<f32> = $intended;
let pix_le = as_le_f32(&intended);
let pix_be = as_be_f32(&intended);
#[cfg(miri)]
let modes: &[bool] = &[false];
#[cfg(not(miri))]
let modes: &[bool] = &[false, true];
for &use_simd in modes {
let frame_le = $le_frame::try_new(&pix_le, 16, 4, 16).unwrap();
let mut out_le = std::vec![0u8; 16 * 4 * 4];
let mut sink_le = MixedSinker::<$marker>::new(16, 4)
.with_simd(use_simd)
.with_rgba(&mut out_le)
.unwrap();
$walker_le(&frame_le, true, M, &mut sink_le).unwrap();
let frame_be = $be_frame::try_new(&pix_be, 16, 4, 16).unwrap();
let mut out_be = std::vec![0u8; 16 * 4 * 4];
let mut sink_be = MixedSinker::<$marker<true>>::new(16, 4)
.with_simd(use_simd)
.with_rgba(&mut out_be)
.unwrap();
$walker_be(&frame_be, true, M, &mut sink_be).unwrap();
assert_eq!(
out_le, out_be,
"{} LE/BE outputs diverge (simd={use_simd}) — `<const BE>` propagation broken",
$label,
);
}
}};
}
#[test]
fn grayf32_le_be_roundtrip_byte_identical() {
grayf32_le_be_roundtrip_test! {
label: "Grayf32",
marker: Grayf32,
le_frame: Grayf32LeFrame,
be_frame: Grayf32BeFrame,
walker_le: grayf32_to,
walker_be: grayf32_to_endian,
intended: (0..16 * 4)
.map(|i| match i % 5 {
0 => 0.0f32,
1 => 0.25,
2 => 0.5,
3 => 0.75,
_ => 1.0,
})
.collect(),
}
}
macro_rules! ya16_le_be_roundtrip_test {
(
label: $label:literal,
marker: $marker:ident,
le_frame: $le_frame:ident,
be_frame: $be_frame:ident,
walker_le: $walker_le:ident,
walker_be: $walker_be:ident,
intended: $intended:expr,
) => {{
let intended: std::vec::Vec<u16> = $intended;
let pix_le = as_le_u16(&intended);
let pix_be = as_be_u16(&intended);
#[cfg(miri)]
let modes: &[bool] = &[false];
#[cfg(not(miri))]
let modes: &[bool] = &[false, true];
for &use_simd in modes {
let frame_le = $le_frame::try_new(&pix_le, 16, 4, 16 * 2).unwrap();
let mut out_le_rgba = std::vec![0u8; 16 * 4 * 4];
let mut out_le_rgba_u16 = std::vec![0u16; 16 * 4 * 4];
let mut sink_le = MixedSinker::<$marker>::new(16, 4)
.with_simd(use_simd)
.with_rgba(&mut out_le_rgba)
.unwrap()
.with_rgba_u16(&mut out_le_rgba_u16)
.unwrap();
$walker_le(&frame_le, true, M, &mut sink_le).unwrap();
let frame_be = $be_frame::try_new(&pix_be, 16, 4, 16 * 2).unwrap();
let mut out_be_rgba = std::vec![0u8; 16 * 4 * 4];
let mut out_be_rgba_u16 = std::vec![0u16; 16 * 4 * 4];
let mut sink_be = MixedSinker::<$marker<true>>::new(16, 4)
.with_simd(use_simd)
.with_rgba(&mut out_be_rgba)
.unwrap()
.with_rgba_u16(&mut out_be_rgba_u16)
.unwrap();
$walker_be(&frame_be, true, M, &mut sink_be).unwrap();
assert_eq!(
out_le_rgba, out_be_rgba,
"{} RGBA u8 LE/BE outputs diverge (simd={use_simd}) — `<const BE>` propagation broken",
$label,
);
assert_eq!(
out_le_rgba_u16, out_be_rgba_u16,
"{} RGBA u16 LE/BE outputs diverge (simd={use_simd}) — `<const BE>` propagation broken",
$label,
);
}
}};
}
#[test]
fn ya16_le_be_roundtrip_byte_identical() {
ya16_le_be_roundtrip_test! {
label: "Ya16",
marker: Ya16,
le_frame: Ya16LeFrame,
be_frame: Ya16BeFrame,
walker_le: ya16_to,
walker_be: ya16_to_endian,
intended: (0..16 * 4 * 2)
.map(|i| match i % 4 {
0 => 0x1234, 1 => 0xABCD, 2 => 0x00FF, _ => 0xFF00, })
.collect(),
}
}