use super::*;
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn vuya_to_rgb_or_rgba_row<const ALPHA: bool, const ALPHA_SRC: bool>(
packed: &[u8],
out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
const { assert!(!ALPHA_SRC || ALPHA) };
debug_assert!(packed.len() >= width * 4, "packed row too short");
let bpp: usize = if ALPHA { 4 } else { 3 };
debug_assert!(out.len() >= width * bpp, "out row too short for {bpp}bpp");
let coeffs = Coefficients::for_matrix(matrix);
let (y_off, y_scale, c_scale) = range_params_n::<8, 8>(full_range);
let bias = chroma_bias::<8>();
for n in 0..width {
let base = n * 4;
let v = packed[base] as i32;
let u = packed[base + 1] as i32;
let y = packed[base + 2] as i32;
let a = packed[base + 3];
let u_d = q15_scale(u - bias, c_scale);
let v_d = q15_scale(v - bias, c_scale);
let r_chroma = q15_chroma(coeffs.r_u(), u_d, coeffs.r_v(), v_d);
let g_chroma = q15_chroma(coeffs.g_u(), u_d, coeffs.g_v(), v_d);
let b_chroma = q15_chroma(coeffs.b_u(), u_d, coeffs.b_v(), v_d);
let y_s = q15_scale(y - y_off, y_scale);
let off = n * bpp;
out[off] = clamp_u8(y_s + r_chroma);
out[off + 1] = clamp_u8(y_s + g_chroma);
out[off + 2] = clamp_u8(y_s + b_chroma);
if ALPHA {
out[off + 3] = if ALPHA_SRC { a } else { 0xFF };
}
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn vuya_to_rgb_row(
packed: &[u8],
rgb_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
vuya_to_rgb_or_rgba_row::<false, false>(packed, rgb_out, width, matrix, full_range);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn vuya_to_rgba_row(
packed: &[u8],
rgba_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
vuya_to_rgb_or_rgba_row::<true, true>(packed, rgba_out, width, matrix, full_range);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn vuyx_to_rgba_row(
packed: &[u8],
rgba_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
vuya_to_rgb_or_rgba_row::<true, false>(packed, rgba_out, width, matrix, full_range);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn vuya_to_luma_row(packed: &[u8], luma_out: &mut [u8], width: usize) {
debug_assert!(packed.len() >= width * 4, "packed row too short");
debug_assert!(luma_out.len() >= width, "luma row too short");
for n in 0..width {
luma_out[n] = packed[n * 4 + 2];
}
}
#[cfg_attr(not(any(feature = "std", feature = "alloc")), allow(dead_code))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn vuya_to_luma_u16_row(packed: &[u8], out: &mut [u16], width: usize) {
debug_assert!(packed.len() >= width * 4, "packed too short");
debug_assert!(out.len() >= width, "out too short");
for x in 0..width {
out[x] = packed[x * 4 + 2] as u16;
}
}
#[allow(dead_code)]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn vuyx_to_luma_u16_row(packed: &[u8], out: &mut [u16], width: usize) {
vuya_to_luma_u16_row(packed, out, width);
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
use crate::ColorMatrix;
fn pack_vuya(v: u8, u: u8, y: u8, a: u8) -> [u8; 4] {
[v, u, y, a]
}
#[test]
fn vuya_known_pattern_rgb_limited_range() {
let p_black = pack_vuya(128, 128, 16, 0);
let p_white = pack_vuya(128, 128, 235, 0);
let packed: Vec<u8> = [p_black, p_black, p_white, p_white]
.iter()
.flatten()
.copied()
.collect();
let mut out = vec![0u8; 4 * 3];
vuya_to_rgb_row(&packed, &mut out, 4, ColorMatrix::Bt709, false);
assert_eq!(&out[0..3], &[0u8, 0, 0], "black pixel 0");
assert_eq!(&out[3..6], &[0u8, 0, 0], "black pixel 1");
assert_eq!(&out[6..9], &[255u8, 255, 255], "white pixel 2");
assert_eq!(&out[9..12], &[255u8, 255, 255], "white pixel 3");
}
#[test]
fn vuya_rgba_passes_source_alpha() {
let p0 = pack_vuya(128, 128, 16, 0x42);
let p1 = pack_vuya(128, 128, 235, 0x99);
let packed: Vec<u8> = [p0, p1].iter().flatten().copied().collect();
let mut out = vec![0u8; 2 * 4];
vuya_to_rgba_row(&packed, &mut out, 2, ColorMatrix::Bt709, false);
assert_eq!(out[3], 0x42, "pixel 0 alpha");
assert_eq!(out[7], 0x99, "pixel 1 alpha");
}
#[test]
fn vuyx_rgba_forces_alpha_max_regardless_of_source() {
let p0 = pack_vuya(128, 128, 16, 0x42);
let p1 = pack_vuya(128, 128, 235, 0x99);
let packed: Vec<u8> = [p0, p1].iter().flatten().copied().collect();
let mut out = vec![0u8; 2 * 4];
vuyx_to_rgba_row(&packed, &mut out, 2, ColorMatrix::Bt709, false);
assert_eq!(out[3], 0xFF, "pixel 0 alpha should be 0xFF");
assert_eq!(out[7], 0xFF, "pixel 1 alpha should be 0xFF");
}
#[test]
fn vuya_luma_extract() {
let p0 = pack_vuya(0, 0, 0xFF, 0);
let p1 = pack_vuya(0, 0, 0x40, 0);
let packed: Vec<u8> = [p0, p1].iter().flatten().copied().collect();
let mut luma = vec![0u8; 2];
vuya_to_luma_row(&packed, &mut luma, 2);
assert_eq!(&luma[..], &[0xFFu8, 0x40]);
}
}