#[cfg_attr(not(tarpaulin), inline(always))]
fn round_ties_even_nonneg(x: f32) -> f32 {
if !x.is_finite() {
return 0.0;
}
let i = x as u32;
let frac = x - (i as f32);
if frac < 0.5 {
i as f32
} else if frac > 0.5 {
(i + 1) as f32
} else {
if i & 1 == 0 { i as f32 } else { (i + 1) as f32 }
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn f32_to_u8_clamped(v: f32) -> u8 {
let clamped = v.clamp(0.0, 1.0);
let scaled = clamped * 255.0;
round_ties_even_nonneg(scaled) as u8
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn f32_to_u16_clamped(v: f32) -> u16 {
let clamped = v.clamp(0.0, 1.0);
let scaled = clamped * 65535.0;
round_ties_even_nonneg(scaled) as u16
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn load_f32<const BE: bool>(rgb_in: &[f32], i: usize) -> f32 {
let bits = rgb_in[i].to_bits();
f32::from_bits(if BE {
u32::from_be(bits)
} else {
u32::from_le(bits)
})
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn load_f16<const BE: bool>(rgb_in: &[half::f16], i: usize) -> half::f16 {
let bits = rgb_in[i].to_bits();
half::f16::from_bits(if BE {
u16::from_be(bits)
} else {
u16::from_le(bits)
})
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn rgbf32_to_rgb_row<const BE: bool>(rgb_in: &[f32], rgb_out: &mut [u8], width: usize) {
debug_assert!(rgb_in.len() >= width * 3, "rgbf32 row too short");
debug_assert!(rgb_out.len() >= width * 3, "rgb_out row too short");
for x in 0..width {
let i = x * 3;
rgb_out[i] = f32_to_u8_clamped(load_f32::<BE>(rgb_in, i));
rgb_out[i + 1] = f32_to_u8_clamped(load_f32::<BE>(rgb_in, i + 1));
rgb_out[i + 2] = f32_to_u8_clamped(load_f32::<BE>(rgb_in, i + 2));
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn rgbf32_to_rgba_row<const BE: bool>(
rgb_in: &[f32],
rgba_out: &mut [u8],
width: usize,
) {
debug_assert!(rgb_in.len() >= width * 3, "rgbf32 row too short");
debug_assert!(rgba_out.len() >= width * 4, "rgba_out row too short");
for x in 0..width {
let s = x * 3;
let d = x * 4;
rgba_out[d] = f32_to_u8_clamped(load_f32::<BE>(rgb_in, s));
rgba_out[d + 1] = f32_to_u8_clamped(load_f32::<BE>(rgb_in, s + 1));
rgba_out[d + 2] = f32_to_u8_clamped(load_f32::<BE>(rgb_in, s + 2));
rgba_out[d + 3] = 0xFF;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn rgbf32_to_rgb_u16_row<const BE: bool>(
rgb_in: &[f32],
rgb_out: &mut [u16],
width: usize,
) {
debug_assert!(rgb_in.len() >= width * 3, "rgbf32 row too short");
debug_assert!(rgb_out.len() >= width * 3, "rgb_out row too short");
for x in 0..width {
let i = x * 3;
rgb_out[i] = f32_to_u16_clamped(load_f32::<BE>(rgb_in, i));
rgb_out[i + 1] = f32_to_u16_clamped(load_f32::<BE>(rgb_in, i + 1));
rgb_out[i + 2] = f32_to_u16_clamped(load_f32::<BE>(rgb_in, i + 2));
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn rgbf32_to_rgba_u16_row<const BE: bool>(
rgb_in: &[f32],
rgba_out: &mut [u16],
width: usize,
) {
debug_assert!(rgb_in.len() >= width * 3, "rgbf32 row too short");
debug_assert!(rgba_out.len() >= width * 4, "rgba_out row too short");
for x in 0..width {
let s = x * 3;
let d = x * 4;
rgba_out[d] = f32_to_u16_clamped(load_f32::<BE>(rgb_in, s));
rgba_out[d + 1] = f32_to_u16_clamped(load_f32::<BE>(rgb_in, s + 1));
rgba_out[d + 2] = f32_to_u16_clamped(load_f32::<BE>(rgb_in, s + 2));
rgba_out[d + 3] = 0xFFFF;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn rgbf32_to_rgb_f32_row<const BE: bool>(
rgb_in: &[f32],
rgb_out: &mut [f32],
width: usize,
) {
debug_assert!(rgb_in.len() >= width * 3, "rgbf32 row too short");
debug_assert!(rgb_out.len() >= width * 3, "rgb_f32_out row too short");
const HOST_NATIVE_BE: bool = cfg!(target_endian = "big");
if BE == HOST_NATIVE_BE {
rgb_out[..width * 3].copy_from_slice(&rgb_in[..width * 3]);
return;
}
for (dst, src) in rgb_out[..width * 3]
.iter_mut()
.zip(rgb_in[..width * 3].iter())
{
let bits = src.to_bits();
*dst = f32::from_bits(if BE {
u32::from_be(bits)
} else {
u32::from_le(bits)
});
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn rgbf16_to_rgb_row<const BE: bool>(
rgb_in: &[half::f16],
rgb_out: &mut [u8],
width: usize,
) {
debug_assert!(rgb_in.len() >= width * 3, "rgbf16 row too short");
debug_assert!(rgb_out.len() >= width * 3, "rgb_out row too short");
for x in 0..width {
let i = x * 3;
rgb_out[i] = f32_to_u8_clamped(load_f16::<BE>(rgb_in, i).to_f32());
rgb_out[i + 1] = f32_to_u8_clamped(load_f16::<BE>(rgb_in, i + 1).to_f32());
rgb_out[i + 2] = f32_to_u8_clamped(load_f16::<BE>(rgb_in, i + 2).to_f32());
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn rgbf16_to_rgba_row<const BE: bool>(
rgb_in: &[half::f16],
rgba_out: &mut [u8],
width: usize,
) {
debug_assert!(rgb_in.len() >= width * 3, "rgbf16 row too short");
debug_assert!(rgba_out.len() >= width * 4, "rgba_out row too short");
for x in 0..width {
let s = x * 3;
let d = x * 4;
rgba_out[d] = f32_to_u8_clamped(load_f16::<BE>(rgb_in, s).to_f32());
rgba_out[d + 1] = f32_to_u8_clamped(load_f16::<BE>(rgb_in, s + 1).to_f32());
rgba_out[d + 2] = f32_to_u8_clamped(load_f16::<BE>(rgb_in, s + 2).to_f32());
rgba_out[d + 3] = 0xFF;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn rgbf16_to_rgb_u16_row<const BE: bool>(
rgb_in: &[half::f16],
rgb_out: &mut [u16],
width: usize,
) {
debug_assert!(rgb_in.len() >= width * 3, "rgbf16 row too short");
debug_assert!(rgb_out.len() >= width * 3, "rgb_out row too short");
for x in 0..width {
let i = x * 3;
rgb_out[i] = f32_to_u16_clamped(load_f16::<BE>(rgb_in, i).to_f32());
rgb_out[i + 1] = f32_to_u16_clamped(load_f16::<BE>(rgb_in, i + 1).to_f32());
rgb_out[i + 2] = f32_to_u16_clamped(load_f16::<BE>(rgb_in, i + 2).to_f32());
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn rgbf16_to_rgba_u16_row<const BE: bool>(
rgb_in: &[half::f16],
rgba_out: &mut [u16],
width: usize,
) {
debug_assert!(rgb_in.len() >= width * 3, "rgbf16 row too short");
debug_assert!(rgba_out.len() >= width * 4, "rgba_out row too short");
for x in 0..width {
let s = x * 3;
let d = x * 4;
rgba_out[d] = f32_to_u16_clamped(load_f16::<BE>(rgb_in, s).to_f32());
rgba_out[d + 1] = f32_to_u16_clamped(load_f16::<BE>(rgb_in, s + 1).to_f32());
rgba_out[d + 2] = f32_to_u16_clamped(load_f16::<BE>(rgb_in, s + 2).to_f32());
rgba_out[d + 3] = 0xFFFF;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn rgbf16_to_rgb_f32_row<const BE: bool>(
rgb_in: &[half::f16],
rgb_out: &mut [f32],
width: usize,
) {
debug_assert!(rgb_in.len() >= width * 3, "rgbf16 row too short");
debug_assert!(rgb_out.len() >= width * 3, "rgb_f32_out row too short");
for (dst, src) in rgb_out[..width * 3]
.iter_mut()
.zip(rgb_in[..width * 3].iter())
{
let bits = src.to_bits();
let host_bits = if BE {
u16::from_be(bits)
} else {
u16::from_le(bits)
};
*dst = half::f16::from_bits(host_bits).to_f32();
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn rgbf16_to_rgb_f16_row<const BE: bool>(
rgb_in: &[half::f16],
rgb_out: &mut [half::f16],
width: usize,
) {
debug_assert!(rgb_in.len() >= width * 3, "rgbf16 row too short");
debug_assert!(rgb_out.len() >= width * 3, "rgb_f16_out row too short");
const HOST_NATIVE_BE: bool = cfg!(target_endian = "big");
if BE == HOST_NATIVE_BE {
rgb_out[..width * 3].copy_from_slice(&rgb_in[..width * 3]);
return;
}
for (dst, src) in rgb_out[..width * 3]
.iter_mut()
.zip(rgb_in[..width * 3].iter())
{
let bits = src.to_bits();
*dst = half::f16::from_bits(if BE {
u16::from_be(bits)
} else {
u16::from_le(bits)
});
}
}