use super::*;
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn yuv_420_to_rgb_row(
y: &[u8],
u_half: &[u8],
v_half: &[u8],
rgb_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
yuv_420_to_rgb_or_rgba_row::<false, false>(
y, u_half, v_half, None, rgb_out, width, matrix, full_range,
);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn yuv_420_to_rgba_row(
y: &[u8],
u_half: &[u8],
v_half: &[u8],
rgba_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
yuv_420_to_rgb_or_rgba_row::<true, false>(
y, u_half, v_half, None, rgba_out, width, matrix, full_range,
);
}
#[allow(dead_code)]
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub(crate) fn yuv_420_to_rgba_with_alpha_src_row(
y: &[u8],
u_half: &[u8],
v_half: &[u8],
a_src: &[u8],
rgba_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
yuv_420_to_rgb_or_rgba_row::<true, true>(
y,
u_half,
v_half,
Some(a_src),
rgba_out,
width,
matrix,
full_range,
);
}
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub(crate) fn yuv_420_to_rgb_or_rgba_row<const ALPHA: bool, const ALPHA_SRC: bool>(
y: &[u8],
u_half: &[u8],
v_half: &[u8],
a_src: Option<&[u8]>,
out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
const { assert!(!ALPHA_SRC || ALPHA) };
debug_assert_eq!(width & 1, 0, "YUV 4:2:0 requires even width");
debug_assert!(y.len() >= width, "y row too short");
debug_assert!(u_half.len() >= width / 2, "u_half row too short");
debug_assert!(v_half.len() >= width / 2, "v_half row too short");
let bpp: usize = if ALPHA { 4 } else { 3 };
debug_assert!(out.len() >= width * bpp, "out row too short for {bpp}bpp");
if ALPHA_SRC {
debug_assert!(
a_src.as_ref().is_some_and(|s| s.len() >= width),
"a_src row too short"
);
}
let coeffs = Coefficients::for_matrix(matrix);
let (y_off, y_scale, c_scale) = range_params_n::<8, 8>(full_range);
const RND: i32 = 1 << 14;
let mut x = 0;
while x < width {
let c_idx = x / 2;
let u_d = ((u_half[c_idx] as i32 - 128) * c_scale + RND) >> 15;
let v_d = ((v_half[c_idx] as i32 - 128) * c_scale + RND) >> 15;
let r_chroma = (coeffs.r_u() * u_d + coeffs.r_v() * v_d + RND) >> 15;
let g_chroma = (coeffs.g_u() * u_d + coeffs.g_v() * v_d + RND) >> 15;
let b_chroma = (coeffs.b_u() * u_d + coeffs.b_v() * v_d + RND) >> 15;
let y0 = ((y[x] as i32 - y_off) * y_scale + RND) >> 15;
let r0 = clamp_u8(y0 + r_chroma);
let g0 = clamp_u8(y0 + g_chroma);
let b0 = clamp_u8(y0 + b_chroma);
out[x * bpp] = r0;
out[x * bpp + 1] = g0;
out[x * bpp + 2] = b0;
if ALPHA_SRC {
out[x * bpp + 3] = a_src.as_ref().unwrap()[x];
} else if ALPHA {
out[x * bpp + 3] = 0xFF;
}
let y1 = ((y[x + 1] as i32 - y_off) * y_scale + RND) >> 15;
let r1 = clamp_u8(y1 + r_chroma);
let g1 = clamp_u8(y1 + g_chroma);
let b1 = clamp_u8(y1 + b_chroma);
out[(x + 1) * bpp] = r1;
out[(x + 1) * bpp + 1] = g1;
out[(x + 1) * bpp + 2] = b1;
if ALPHA_SRC {
out[(x + 1) * bpp + 3] = a_src.as_ref().unwrap()[x + 1];
} else if ALPHA {
out[(x + 1) * bpp + 3] = 0xFF;
}
x += 2;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn yuv_410_to_rgb_row(
y: &[u8],
u_quarter: &[u8],
v_quarter: &[u8],
rgb_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
yuv_410_to_rgb_or_rgba_row::<false>(y, u_quarter, v_quarter, rgb_out, width, matrix, full_range);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn yuv_410_to_rgba_row(
y: &[u8],
u_quarter: &[u8],
v_quarter: &[u8],
rgba_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
yuv_410_to_rgb_or_rgba_row::<true>(y, u_quarter, v_quarter, rgba_out, width, matrix, full_range);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn yuv_410_to_rgb_or_rgba_row<const ALPHA: bool>(
y: &[u8],
u_quarter: &[u8],
v_quarter: &[u8],
out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
debug_assert_eq!(width & 3, 0, "YUV 4:1:0 requires width % 4 == 0");
debug_assert!(y.len() >= width, "y row too short");
debug_assert!(u_quarter.len() >= width / 4, "u_quarter row too short");
debug_assert!(v_quarter.len() >= width / 4, "v_quarter 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);
const RND: i32 = 1 << 14;
let mut x = 0;
while x < width {
let c_idx = x / 4;
let u_d = ((u_quarter[c_idx] as i32 - 128) * c_scale + RND) >> 15;
let v_d = ((v_quarter[c_idx] as i32 - 128) * c_scale + RND) >> 15;
let r_chroma = (coeffs.r_u() * u_d + coeffs.r_v() * v_d + RND) >> 15;
let g_chroma = (coeffs.g_u() * u_d + coeffs.g_v() * v_d + RND) >> 15;
let b_chroma = (coeffs.b_u() * u_d + coeffs.b_v() * v_d + RND) >> 15;
for k in 0..4 {
let yk = ((y[x + k] as i32 - y_off) * y_scale + RND) >> 15;
out[(x + k) * bpp] = clamp_u8(yk + r_chroma);
out[(x + k) * bpp + 1] = clamp_u8(yk + g_chroma);
out[(x + k) * bpp + 2] = clamp_u8(yk + b_chroma);
if ALPHA {
out[(x + k) * bpp + 3] = 0xFF;
}
}
x += 4;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn yuv_444_to_rgb_row(
y: &[u8],
u: &[u8],
v: &[u8],
rgb_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
yuv_444_to_rgb_or_rgba_row::<false, false>(y, u, v, None, rgb_out, width, matrix, full_range);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn yuv_444_to_rgba_row(
y: &[u8],
u: &[u8],
v: &[u8],
rgba_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
yuv_444_to_rgb_or_rgba_row::<true, false>(y, u, v, None, rgba_out, width, matrix, full_range);
}
#[allow(dead_code)]
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub(crate) fn yuv_444_to_rgba_with_alpha_src_row(
y: &[u8],
u: &[u8],
v: &[u8],
a_src: &[u8],
rgba_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
yuv_444_to_rgb_or_rgba_row::<true, true>(
y,
u,
v,
Some(a_src),
rgba_out,
width,
matrix,
full_range,
);
}
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub(crate) fn yuv_444_to_rgb_or_rgba_row<const ALPHA: bool, const ALPHA_SRC: bool>(
y: &[u8],
u: &[u8],
v: &[u8],
a_src: Option<&[u8]>,
out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
const { assert!(!ALPHA_SRC || ALPHA) };
debug_assert!(y.len() >= width, "y row too short");
debug_assert!(u.len() >= width, "u row too short");
debug_assert!(v.len() >= width, "v row too short");
let bpp: usize = if ALPHA { 4 } else { 3 };
debug_assert!(out.len() >= width * bpp, "out row too short for {bpp}bpp");
if ALPHA_SRC {
debug_assert!(
a_src.as_ref().is_some_and(|s| s.len() >= width),
"a_src row too short"
);
}
let coeffs = Coefficients::for_matrix(matrix);
let (y_off, y_scale, c_scale) = range_params_n::<8, 8>(full_range);
const RND: i32 = 1 << 14;
for x in 0..width {
let u_d = ((u[x] as i32 - 128) * c_scale + RND) >> 15;
let v_d = ((v[x] as i32 - 128) * c_scale + RND) >> 15;
let r_chroma = (coeffs.r_u() * u_d + coeffs.r_v() * v_d + RND) >> 15;
let g_chroma = (coeffs.g_u() * u_d + coeffs.g_v() * v_d + RND) >> 15;
let b_chroma = (coeffs.b_u() * u_d + coeffs.b_v() * v_d + RND) >> 15;
let y0 = ((y[x] as i32 - y_off) * y_scale + RND) >> 15;
out[x * bpp] = clamp_u8(y0 + r_chroma);
out[x * bpp + 1] = clamp_u8(y0 + g_chroma);
out[x * bpp + 2] = clamp_u8(y0 + b_chroma);
if ALPHA_SRC {
out[x * bpp + 3] = a_src.as_ref().unwrap()[x];
} else if ALPHA {
out[x * bpp + 3] = 0xFF;
}
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn yuv_411_to_rgb_row(
y: &[u8],
u_quarter: &[u8],
v_quarter: &[u8],
rgb_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
yuv_411_to_rgb_or_rgba_row::<false>(y, u_quarter, v_quarter, rgb_out, width, matrix, full_range);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn yuv_411_to_rgba_row(
y: &[u8],
u_quarter: &[u8],
v_quarter: &[u8],
rgba_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
yuv_411_to_rgb_or_rgba_row::<true>(y, u_quarter, v_quarter, rgba_out, width, matrix, full_range);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn yuv_411_to_rgb_or_rgba_row<const ALPHA: bool>(
y: &[u8],
u_quarter: &[u8],
v_quarter: &[u8],
out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
debug_assert!(y.len() >= width, "y row too short");
debug_assert!(
u_quarter.len() >= width.div_ceil(4),
"u_quarter row too short"
);
debug_assert!(
v_quarter.len() >= width.div_ceil(4),
"v_quarter 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);
const RND: i32 = 1 << 14;
let body_end = width & !3;
let mut x = 0;
while x < body_end {
let c_idx = x / 4;
let u_d = ((u_quarter[c_idx] as i32 - 128) * c_scale + RND) >> 15;
let v_d = ((v_quarter[c_idx] as i32 - 128) * c_scale + RND) >> 15;
let r_chroma = (coeffs.r_u() * u_d + coeffs.r_v() * v_d + RND) >> 15;
let g_chroma = (coeffs.g_u() * u_d + coeffs.g_v() * v_d + RND) >> 15;
let b_chroma = (coeffs.b_u() * u_d + coeffs.b_v() * v_d + RND) >> 15;
let mut k = 0;
while k < 4 {
let y_k = ((y[x + k] as i32 - y_off) * y_scale + RND) >> 15;
let pos = (x + k) * bpp;
out[pos] = clamp_u8(y_k + r_chroma);
out[pos + 1] = clamp_u8(y_k + g_chroma);
out[pos + 2] = clamp_u8(y_k + b_chroma);
if ALPHA {
out[pos + 3] = 0xFF;
}
k += 1;
}
x += 4;
}
if x < width {
let c_idx = x / 4; let u_d = ((u_quarter[c_idx] as i32 - 128) * c_scale + RND) >> 15;
let v_d = ((v_quarter[c_idx] as i32 - 128) * c_scale + RND) >> 15;
let r_chroma = (coeffs.r_u() * u_d + coeffs.r_v() * v_d + RND) >> 15;
let g_chroma = (coeffs.g_u() * u_d + coeffs.g_v() * v_d + RND) >> 15;
let b_chroma = (coeffs.b_u() * u_d + coeffs.b_v() * v_d + RND) >> 15;
while x < width {
let y_k = ((y[x] as i32 - y_off) * y_scale + RND) >> 15;
let pos = x * bpp;
out[pos] = clamp_u8(y_k + r_chroma);
out[pos + 1] = clamp_u8(y_k + g_chroma);
out[pos + 2] = clamp_u8(y_k + b_chroma);
if ALPHA {
out[pos + 3] = 0xFF;
}
x += 1;
}
}
}