use super::*;
#[cfg_attr(not(tarpaulin), inline(always))]
const fn extract_v30x(word: u32) -> (i32, i32, i32) {
let u = ((word >> 2) & 0x3FF) as i32;
let y = ((word >> 12) & 0x3FF) as i32;
let v = ((word >> 22) & 0x3FF) as i32;
(u, y, v)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn v30x_to_rgb_or_rgba_row<const ALPHA: bool>(
packed: &[u32],
out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
debug_assert!(packed.len() >= width, "packed row too short");
let bpp: usize = if ALPHA { 4 } else { 3 };
debug_assert!(out.len() >= width * bpp, "out row too short");
let coeffs = Coefficients::for_matrix(matrix);
let (y_off, y_scale, c_scale) = range_params_n::<10, 8>(full_range);
let bias = chroma_bias::<10>();
for (x, &word) in packed[..width].iter().enumerate() {
let (u, y, v) = extract_v30x(word);
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 = x * 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] = 0xFF;
}
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn v30x_to_rgb_u16_or_rgba_u16_row<const ALPHA: bool>(
packed: &[u32],
out: &mut [u16],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
debug_assert!(packed.len() >= width, "packed row too short");
let bpp: usize = if ALPHA { 4 } else { 3 };
debug_assert!(out.len() >= width * bpp, "out row too short");
let coeffs = Coefficients::for_matrix(matrix);
let (y_off, y_scale, c_scale) = range_params_n::<10, 10>(full_range);
let bias = chroma_bias::<10>();
let alpha_max: u16 = 0x3FF;
let out_max: i32 = 0x3FF;
for (x, &word) in packed[..width].iter().enumerate() {
let (u, y, v) = extract_v30x(word);
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 = x * bpp;
out[off] = (y_s + r_chroma).clamp(0, out_max) as u16;
out[off + 1] = (y_s + g_chroma).clamp(0, out_max) as u16;
out[off + 2] = (y_s + b_chroma).clamp(0, out_max) as u16;
if ALPHA {
out[off + 3] = alpha_max;
}
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn v30x_to_luma_row(packed: &[u32], out: &mut [u8], width: usize) {
debug_assert!(packed.len() >= width);
debug_assert!(out.len() >= width);
for x in 0..width {
let y = (packed[x] >> 12) & 0x3FF;
out[x] = (y >> 2) as u8;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn v30x_to_luma_u16_row(packed: &[u32], out: &mut [u16], width: usize) {
debug_assert!(packed.len() >= width);
debug_assert!(out.len() >= width);
for x in 0..width {
let y = (packed[x] >> 12) & 0x3FF;
out[x] = y as u16;
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
use crate::ColorMatrix;
fn pack_v30x(u: u32, y: u32, v: u32) -> u32 {
debug_assert!(u < 1024 && y < 1024 && v < 1024);
(v << 22) | (y << 12) | (u << 2)
}
#[test]
fn v30x_known_pattern_rgb() {
let p = vec![
pack_v30x(512, 64, 512),
pack_v30x(512, 64, 512),
pack_v30x(512, 940, 512), pack_v30x(512, 940, 512),
];
let mut out = vec![0u8; 4 * 3];
v30x_to_rgb_or_rgba_row::<false>(&p, &mut out, 4, ColorMatrix::Bt709, false);
assert_eq!(&out[0..3], &[0u8, 0, 0]);
assert_eq!(&out[3..6], &[0u8, 0, 0]);
assert_eq!(&out[6..9], &[255u8, 255, 255]);
assert_eq!(&out[9..12], &[255u8, 255, 255]);
}
#[test]
fn v30x_known_pattern_rgba_alpha_max() {
let p = vec![pack_v30x(512, 940, 512)];
let mut out = vec![0u8; 4];
v30x_to_rgb_or_rgba_row::<true>(&p, &mut out, 1, ColorMatrix::Bt709, false);
assert_eq!(out[3], 0xFF);
}
#[test]
fn v30x_luma_extract_u8() {
let p = vec![
pack_v30x(0, 0x3FF, 0), pack_v30x(0, 0x100, 0), ];
let mut out = vec![0u8; 2];
v30x_to_luma_row(&p, &mut out, 2);
assert_eq!(&out[..], &[0xFFu8, 0x40]);
}
#[test]
fn v30x_luma_extract_u16_low_bit_packed() {
let p = vec![pack_v30x(0, 0x3FF, 0), pack_v30x(0, 0x123, 0)];
let mut out = vec![0u16; 2];
v30x_to_luma_u16_row(&p, &mut out, 2);
assert_eq!(&out[..], &[0x3FFu16, 0x123]);
}
#[test]
fn v30x_known_pattern_rgba_u16_alpha_max() {
let p = vec![pack_v30x(512, 940, 512)];
let mut out = vec![0u16; 4];
v30x_to_rgb_u16_or_rgba_u16_row::<true>(&p, &mut out, 1, ColorMatrix::Bt709, false);
assert_eq!(out[3], 0x3FF);
}
}