#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbr_to_rgb_high_bit_row<const BITS: u32, const BE: bool>(
g: &[u16],
b: &[u16],
r: &[u16],
rgb_out: &mut [u8],
width: usize,
) {
const {
assert!(
matches!(BITS, 9 | 10 | 12 | 14 | 16),
"BITS must be one of 9, 10, 12, 14, or 16"
)
};
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(rgb_out.len() >= width * 3, "rgb_out row too short");
let mask: u16 = ((1u32 << BITS) - 1) as u16;
let shift = BITS - 8;
for x in 0..width {
let r_raw = if BE {
u16::from_be(r[x])
} else {
u16::from_le(r[x])
};
let g_raw = if BE {
u16::from_be(g[x])
} else {
u16::from_le(g[x])
};
let b_raw = if BE {
u16::from_be(b[x])
} else {
u16::from_le(b[x])
};
let r_val = r_raw & mask;
let g_val = g_raw & mask;
let b_val = b_raw & mask;
let dst = x * 3;
rgb_out[dst] = (r_val >> shift) as u8;
rgb_out[dst + 1] = (g_val >> shift) as u8;
rgb_out[dst + 2] = (b_val >> shift) as u8;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbr_to_rgb_u16_high_bit_row<const BITS: u32, const BE: bool>(
g: &[u16],
b: &[u16],
r: &[u16],
rgb_u16_out: &mut [u16],
width: usize,
) {
const {
assert!(
matches!(BITS, 9 | 10 | 12 | 14 | 16),
"BITS must be one of 9, 10, 12, 14, or 16"
)
};
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(rgb_u16_out.len() >= width * 3, "rgb_u16_out row too short");
let mask: u16 = ((1u32 << BITS) - 1) as u16;
for x in 0..width {
let r_raw = if BE {
u16::from_be(r[x])
} else {
u16::from_le(r[x])
};
let g_raw = if BE {
u16::from_be(g[x])
} else {
u16::from_le(g[x])
};
let b_raw = if BE {
u16::from_be(b[x])
} else {
u16::from_le(b[x])
};
let r_val = r_raw & mask;
let g_val = g_raw & mask;
let b_val = b_raw & mask;
let dst = x * 3;
rgb_u16_out[dst] = r_val;
rgb_u16_out[dst + 1] = g_val;
rgb_u16_out[dst + 2] = b_val;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbr_to_rgba_opaque_high_bit_row<const BITS: u32, const BE: bool>(
g: &[u16],
b: &[u16],
r: &[u16],
rgba_out: &mut [u8],
width: usize,
) {
const {
assert!(
matches!(BITS, 9 | 10 | 12 | 14 | 16),
"BITS must be one of 9, 10, 12, 14, or 16"
)
};
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(rgba_out.len() >= width * 4, "rgba_out row too short");
let mask: u16 = ((1u32 << BITS) - 1) as u16;
let shift = BITS - 8;
for x in 0..width {
let r_raw = if BE {
u16::from_be(r[x])
} else {
u16::from_le(r[x])
};
let g_raw = if BE {
u16::from_be(g[x])
} else {
u16::from_le(g[x])
};
let b_raw = if BE {
u16::from_be(b[x])
} else {
u16::from_le(b[x])
};
let r_val = r_raw & mask;
let g_val = g_raw & mask;
let b_val = b_raw & mask;
let dst = x * 4;
rgba_out[dst] = (r_val >> shift) as u8;
rgba_out[dst + 1] = (g_val >> shift) as u8;
rgba_out[dst + 2] = (b_val >> shift) as u8;
rgba_out[dst + 3] = 0xFF;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbr_to_rgba_opaque_u16_high_bit_row<const BITS: u32, const BE: bool>(
g: &[u16],
b: &[u16],
r: &[u16],
rgba_u16_out: &mut [u16],
width: usize,
) {
const {
assert!(
matches!(BITS, 9 | 10 | 12 | 14 | 16),
"BITS must be one of 9, 10, 12, 14, or 16"
)
};
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(
rgba_u16_out.len() >= width * 4,
"rgba_u16_out row too short"
);
let mask: u16 = ((1u32 << BITS) - 1) as u16;
let opaque: u16 = mask;
for x in 0..width {
let r_raw = if BE {
u16::from_be(r[x])
} else {
u16::from_le(r[x])
};
let g_raw = if BE {
u16::from_be(g[x])
} else {
u16::from_le(g[x])
};
let b_raw = if BE {
u16::from_be(b[x])
} else {
u16::from_le(b[x])
};
let r_val = r_raw & mask;
let g_val = g_raw & mask;
let b_val = b_raw & mask;
let dst = x * 4;
rgba_u16_out[dst] = r_val;
rgba_u16_out[dst + 1] = g_val;
rgba_u16_out[dst + 2] = b_val;
rgba_u16_out[dst + 3] = opaque;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbra_to_rgba_high_bit_row<const BITS: u32, const BE: bool>(
g: &[u16],
b: &[u16],
r: &[u16],
a: &[u16],
rgba_out: &mut [u8],
width: usize,
) {
const {
assert!(
matches!(BITS, 10 | 12 | 14 | 16),
"BITS must be one of 10, 12, 14, or 16 (FFmpeg has no GBRAP9)"
)
};
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(a.len() >= width, "a row too short");
debug_assert!(rgba_out.len() >= width * 4, "rgba_out row too short");
let mask: u16 = ((1u32 << BITS) - 1) as u16;
let shift = BITS - 8;
for x in 0..width {
let r_raw = if BE {
u16::from_be(r[x])
} else {
u16::from_le(r[x])
};
let g_raw = if BE {
u16::from_be(g[x])
} else {
u16::from_le(g[x])
};
let b_raw = if BE {
u16::from_be(b[x])
} else {
u16::from_le(b[x])
};
let a_raw = if BE {
u16::from_be(a[x])
} else {
u16::from_le(a[x])
};
let r_val = r_raw & mask;
let g_val = g_raw & mask;
let b_val = b_raw & mask;
let a_val = a_raw & mask;
let dst = x * 4;
rgba_out[dst] = (r_val >> shift) as u8;
rgba_out[dst + 1] = (g_val >> shift) as u8;
rgba_out[dst + 2] = (b_val >> shift) as u8;
rgba_out[dst + 3] = (a_val >> shift) as u8;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbra_to_rgba_u16_high_bit_row<const BITS: u32, const BE: bool>(
g: &[u16],
b: &[u16],
r: &[u16],
a: &[u16],
rgba_u16_out: &mut [u16],
width: usize,
) {
const {
assert!(
matches!(BITS, 10 | 12 | 14 | 16),
"BITS must be one of 10, 12, 14, or 16 (FFmpeg has no GBRAP9)"
)
};
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(a.len() >= width, "a row too short");
debug_assert!(
rgba_u16_out.len() >= width * 4,
"rgba_u16_out row too short"
);
let mask: u16 = ((1u32 << BITS) - 1) as u16;
for x in 0..width {
let r_raw = if BE {
u16::from_be(r[x])
} else {
u16::from_le(r[x])
};
let g_raw = if BE {
u16::from_be(g[x])
} else {
u16::from_le(g[x])
};
let b_raw = if BE {
u16::from_be(b[x])
} else {
u16::from_le(b[x])
};
let a_raw = if BE {
u16::from_be(a[x])
} else {
u16::from_le(a[x])
};
let r_val = r_raw & mask;
let g_val = g_raw & mask;
let b_val = b_raw & mask;
let a_val = a_raw & mask;
let dst = x * 4;
rgba_u16_out[dst] = r_val;
rgba_u16_out[dst + 1] = g_val;
rgba_u16_out[dst + 2] = b_val;
rgba_u16_out[dst + 3] = a_val;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbr_to_luma_u16_high_bit_row<const BITS: u32, const BE: bool>(
g: &[u16],
b: &[u16],
r: &[u16],
luma_out: &mut [u16],
width: usize,
matrix: crate::ColorMatrix,
full_range: bool,
) {
const {
assert!(
matches!(BITS, 9 | 10 | 12 | 14 | 16),
"BITS must be one of 9, 10, 12, 14, or 16"
)
};
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(luma_out.len() >= width, "luma row too short");
let (k_r, k_g, k_b) = super::luma_coefficients_q15(matrix);
let k_r = k_r as i64;
let k_g = k_g as i64;
let k_b = k_b as i64;
const RND: i64 = 1 << 14;
let native_max: u16 = ((1u32 << BITS) - 1) as u16;
let mask: u16 = native_max;
if full_range {
for x in 0..width {
let r_raw = if BE {
u16::from_be(r[x])
} else {
u16::from_le(r[x])
};
let g_raw = if BE {
u16::from_be(g[x])
} else {
u16::from_le(g[x])
};
let b_raw = if BE {
u16::from_be(b[x])
} else {
u16::from_le(b[x])
};
let rv = (r_raw & mask) as i64;
let gv = (g_raw & mask) as i64;
let bv = (b_raw & mask) as i64;
let y = ((k_r * rv + k_g * gv + k_b * bv + RND) >> 15) as i32;
luma_out[x] = y.clamp(0, native_max as i32) as u16;
}
} else {
let y_off = (16i64) << (BITS - 8);
let range = (219i64) << (BITS - 8);
let native_max_i64 = native_max as i64;
let y_max = (235i64) << (BITS - 8);
let y_min = y_off;
for x in 0..width {
let r_raw = if BE {
u16::from_be(r[x])
} else {
u16::from_le(r[x])
};
let g_raw = if BE {
u16::from_be(g[x])
} else {
u16::from_le(g[x])
};
let b_raw = if BE {
u16::from_be(b[x])
} else {
u16::from_le(b[x])
};
let rv = (r_raw & mask) as i64;
let gv = (g_raw & mask) as i64;
let bv = (b_raw & mask) as i64;
let y_full = (k_r * rv + k_g * gv + k_b * bv + RND) >> 15;
let y_full_clamped = y_full.clamp(0, native_max_i64);
let y_lim = y_off + (y_full_clamped * range + native_max_i64 / 2) / native_max_i64;
luma_out[x] = y_lim.clamp(y_min, y_max) as u16;
}
}
}
#[cfg(all(test, feature = "std"))]
mod tests;