#![cfg_attr(not(test), allow(dead_code))]
use crate::ColorMatrix;
#[inline(always)]
fn f32_to_u8(y: f32) -> u8 {
(y.clamp(0.0, 1.0) * 255.0 + 0.5) as u8
}
#[inline(always)]
fn f32_to_u16(y: f32) -> u16 {
(y.clamp(0.0, 1.0) * 65535.0 + 0.5) as u16
}
#[inline(always)]
fn f32_to_f16(y: f32) -> half::f16 {
half::f16::from_f32(y)
}
#[inline(always)]
fn load_f32<const BE: bool>(plane: &[f32], i: usize) -> f32 {
let raw = plane[i];
if BE {
f32::from_bits(u32::from_be(raw.to_bits()))
} else {
f32::from_bits(u32::from_le(raw.to_bits()))
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbrpf32_to_rgb_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
rgb_out: &mut [u8],
width: usize,
) {
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");
for x in 0..width {
let dst = x * 3;
rgb_out[dst] = f32_to_u8(load_f32::<BE>(r, x));
rgb_out[dst + 1] = f32_to_u8(load_f32::<BE>(g, x));
rgb_out[dst + 2] = f32_to_u8(load_f32::<BE>(b, x));
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbrpf32_to_rgba_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
rgba_out: &mut [u8],
width: usize,
) {
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");
for x in 0..width {
let dst = x * 4;
rgba_out[dst] = f32_to_u8(load_f32::<BE>(r, x));
rgba_out[dst + 1] = f32_to_u8(load_f32::<BE>(g, x));
rgba_out[dst + 2] = f32_to_u8(load_f32::<BE>(b, x));
rgba_out[dst + 3] = 0xFF;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbrpf32_to_rgb_u16_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
rgb_out: &mut [u16],
width: usize,
) {
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");
for x in 0..width {
let dst = x * 3;
rgb_out[dst] = f32_to_u16(load_f32::<BE>(r, x));
rgb_out[dst + 1] = f32_to_u16(load_f32::<BE>(g, x));
rgb_out[dst + 2] = f32_to_u16(load_f32::<BE>(b, x));
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbrpf32_to_rgba_u16_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
rgba_out: &mut [u16],
width: usize,
) {
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");
for x in 0..width {
let dst = x * 4;
rgba_out[dst] = f32_to_u16(load_f32::<BE>(r, x));
rgba_out[dst + 1] = f32_to_u16(load_f32::<BE>(g, x));
rgba_out[dst + 2] = f32_to_u16(load_f32::<BE>(b, x));
rgba_out[dst + 3] = 0xFFFF;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbrpf32_to_rgb_f32_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
rgb_out: &mut [f32],
width: usize,
) {
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");
for x in 0..width {
let dst = x * 3;
rgb_out[dst] = load_f32::<BE>(r, x);
rgb_out[dst + 1] = load_f32::<BE>(g, x);
rgb_out[dst + 2] = load_f32::<BE>(b, x);
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbrpf32_to_rgba_f32_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
rgba_out: &mut [f32],
width: usize,
) {
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");
for x in 0..width {
let dst = x * 4;
rgba_out[dst] = load_f32::<BE>(r, x);
rgba_out[dst + 1] = load_f32::<BE>(g, x);
rgba_out[dst + 2] = load_f32::<BE>(b, x);
rgba_out[dst + 3] = 1.0;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbrpf32_to_rgb_f16_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
rgb_out: &mut [half::f16],
width: usize,
) {
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");
for x in 0..width {
let dst = x * 3;
rgb_out[dst] = f32_to_f16(load_f32::<BE>(r, x));
rgb_out[dst + 1] = f32_to_f16(load_f32::<BE>(g, x));
rgb_out[dst + 2] = f32_to_f16(load_f32::<BE>(b, x));
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbrpf32_to_rgba_f16_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
rgba_out: &mut [half::f16],
width: usize,
) {
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 one_f16 = half::f16::from_f32(1.0);
for x in 0..width {
let dst = x * 4;
rgba_out[dst] = f32_to_f16(load_f32::<BE>(r, x));
rgba_out[dst + 1] = f32_to_f16(load_f32::<BE>(g, x));
rgba_out[dst + 2] = f32_to_f16(load_f32::<BE>(b, x));
rgba_out[dst + 3] = one_f16;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub(crate) fn gbrpf32_to_luma_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
luma_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
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_out row too short");
const CHUNK: usize = 64;
let mut scratch = [0u8; CHUNK * 3];
let mut offset = 0;
while offset < width {
let n = (width - offset).min(CHUNK);
gbrpf32_to_rgb_row::<BE>(
&g[offset..],
&b[offset..],
&r[offset..],
&mut scratch[..n * 3],
n,
);
super::rgb_to_luma_row(
&scratch[..n * 3],
&mut luma_out[offset..offset + n],
n,
matrix,
full_range,
);
offset += n;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub(crate) fn gbrpf32_to_luma_u16_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
luma_out: &mut [u16],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
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_out row too short");
const CHUNK: usize = 64;
let mut scratch = [0u8; CHUNK * 3];
let mut offset = 0;
while offset < width {
let n = (width - offset).min(CHUNK);
gbrpf32_to_rgb_row::<BE>(
&g[offset..],
&b[offset..],
&r[offset..],
&mut scratch[..n * 3],
n,
);
super::rgb_to_luma_u16_row(
&scratch[..n * 3],
&mut luma_out[offset..offset + n],
n,
matrix,
full_range,
);
offset += n;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbrpf32_to_hsv_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
h_out: &mut [u8],
s_out: &mut [u8],
v_out: &mut [u8],
width: usize,
) {
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!(h_out.len() >= width, "h_out row too short");
debug_assert!(s_out.len() >= width, "s_out row too short");
debug_assert!(v_out.len() >= width, "v_out row too short");
const CHUNK: usize = 64;
let mut scratch = [0u8; CHUNK * 3];
let mut offset = 0;
while offset < width {
let n = (width - offset).min(CHUNK);
gbrpf32_to_rgb_row::<BE>(
&g[offset..],
&b[offset..],
&r[offset..],
&mut scratch[..n * 3],
n,
);
super::rgb_to_hsv_row(
&scratch[..n * 3],
&mut h_out[offset..offset + n],
&mut s_out[offset..offset + n],
&mut v_out[offset..offset + n],
n,
);
offset += n;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbrapf32_to_rgba_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
a: &[f32],
rgba_out: &mut [u8],
width: usize,
) {
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");
for x in 0..width {
let dst = x * 4;
rgba_out[dst] = f32_to_u8(load_f32::<BE>(r, x));
rgba_out[dst + 1] = f32_to_u8(load_f32::<BE>(g, x));
rgba_out[dst + 2] = f32_to_u8(load_f32::<BE>(b, x));
rgba_out[dst + 3] = f32_to_u8(load_f32::<BE>(a, x));
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbrapf32_to_rgba_u16_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
a: &[f32],
rgba_out: &mut [u16],
width: usize,
) {
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");
for x in 0..width {
let dst = x * 4;
rgba_out[dst] = f32_to_u16(load_f32::<BE>(r, x));
rgba_out[dst + 1] = f32_to_u16(load_f32::<BE>(g, x));
rgba_out[dst + 2] = f32_to_u16(load_f32::<BE>(b, x));
rgba_out[dst + 3] = f32_to_u16(load_f32::<BE>(a, x));
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbrapf32_to_rgba_f32_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
a: &[f32],
rgba_out: &mut [f32],
width: usize,
) {
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");
for x in 0..width {
let dst = x * 4;
rgba_out[dst] = load_f32::<BE>(r, x);
rgba_out[dst + 1] = load_f32::<BE>(g, x);
rgba_out[dst + 2] = load_f32::<BE>(b, x);
rgba_out[dst + 3] = load_f32::<BE>(a, x);
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gbrapf32_to_rgba_f16_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
a: &[f32],
rgba_out: &mut [half::f16],
width: usize,
) {
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");
for x in 0..width {
let dst = x * 4;
rgba_out[dst] = f32_to_f16(load_f32::<BE>(r, x));
rgba_out[dst + 1] = f32_to_f16(load_f32::<BE>(g, x));
rgba_out[dst + 2] = f32_to_f16(load_f32::<BE>(b, x));
rgba_out[dst + 3] = f32_to_f16(load_f32::<BE>(a, x));
}
}
#[cfg(all(test, feature = "std"))]
mod tests;