use super::bits_mask;
#[inline(always)]
fn broadcast_u8_to_rgb(y: u8, out: &mut [u8], x: usize) {
let i = x * 3;
out[i] = y;
out[i + 1] = y;
out[i + 2] = y;
}
#[inline(always)]
fn broadcast_u8_to_rgba(y: u8, out: &mut [u8], x: usize) {
let i = x * 4;
out[i] = y;
out[i + 1] = y;
out[i + 2] = y;
out[i + 3] = 0xFF;
}
#[inline(always)]
fn broadcast_u16_to_rgb(y: u16, out: &mut [u16], x: usize) {
let i = x * 3;
out[i] = y;
out[i + 1] = y;
out[i + 2] = y;
}
#[inline(always)]
fn broadcast_u16_to_rgba(y: u16, alpha: u16, out: &mut [u16], x: usize) {
let i = x * 4;
out[i] = y;
out[i + 1] = y;
out[i + 2] = y;
out[i + 3] = alpha;
}
#[inline(always)]
fn limited_to_full_u8(y: u8) -> u8 {
let y = y as i32;
let rescaled = (y - 16) * 255 + 109; let result = rescaled / 219;
result.clamp(0, 255) as u8
}
#[inline(always)]
fn limited_n_to_full_u8<const BITS: u32>(y: u16) -> u8 {
let black = 16i32 << (BITS - 8);
let range = 219i32 << (BITS - 8);
let y = y as i32;
let rescaled = (y - black) * 255 + range / 2;
let result = rescaled / range;
result.clamp(0, 255) as u8
}
#[inline(always)]
fn limited_n_to_full_u16<const BITS: u32>(y: u16) -> u16 {
let black = 16i64 << (BITS - 8);
let range = 219i64 << (BITS - 8);
let max_native = ((1u64 << BITS) - 1) as i64;
let y = y as i64;
let rescaled = (y - black) * max_native + range / 2;
let result = rescaled / range;
result.clamp(0, max_native) as u16
}
#[inline(always)]
fn limited_16_to_full_u8(y: u16) -> u8 {
limited_n_to_full_u8::<16>(y)
}
#[inline(always)]
fn limited_16_to_full_u16(y: u16) -> u16 {
limited_n_to_full_u16::<16>(y)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray8_to_rgb_row(y_plane: &[u8], out: &mut [u8], width: usize, full_range: bool) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(out.len() >= width * 3, "out too short");
for (x, &y) in y_plane[..width].iter().enumerate() {
let y_out = if full_range { y } else { limited_to_full_u8(y) };
broadcast_u8_to_rgb(y_out, out, x);
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray8_to_rgba_row(y_plane: &[u8], out: &mut [u8], width: usize, full_range: bool) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(out.len() >= width * 4, "out too short");
for (x, &y) in y_plane[..width].iter().enumerate() {
let y_out = if full_range { y } else { limited_to_full_u8(y) };
broadcast_u8_to_rgba(y_out, out, x);
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray8_to_hsv_row(
y_plane: &[u8],
h_out: &mut [u8],
s_out: &mut [u8],
v_out: &mut [u8],
width: usize,
full_range: bool,
) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(h_out.len() >= width, "H out too short");
debug_assert!(s_out.len() >= width, "S out too short");
debug_assert!(v_out.len() >= width, "V out too short");
for (x, &y) in y_plane[..width].iter().enumerate() {
h_out[x] = 0;
s_out[x] = 0;
v_out[x] = if full_range { y } else { limited_to_full_u8(y) };
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray_n_to_rgb_row<const BITS: u32, const BE: bool>(
y_plane: &[u16],
out: &mut [u8],
width: usize,
full_range: bool,
) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(out.len() >= width * 3, "out too short");
let mask = bits_mask::<BITS>();
let shift = BITS - 8;
for (x, &raw) in y_plane[..width].iter().enumerate() {
let raw = if BE {
u16::from_be(raw)
} else {
u16::from_le(raw)
};
let masked = raw & mask;
let y8 = if full_range {
(masked >> shift) as u8
} else {
limited_n_to_full_u8::<BITS>(masked)
};
broadcast_u8_to_rgb(y8, out, x);
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray_n_to_rgba_row<const BITS: u32, const BE: bool>(
y_plane: &[u16],
out: &mut [u8],
width: usize,
full_range: bool,
) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(out.len() >= width * 4, "out too short");
let mask = bits_mask::<BITS>();
let shift = BITS - 8;
for (x, &raw) in y_plane[..width].iter().enumerate() {
let raw = if BE {
u16::from_be(raw)
} else {
u16::from_le(raw)
};
let masked = raw & mask;
let y8 = if full_range {
(masked >> shift) as u8
} else {
limited_n_to_full_u8::<BITS>(masked)
};
broadcast_u8_to_rgba(y8, out, x);
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray_n_to_rgb_u16_row<const BITS: u32, const BE: bool>(
y_plane: &[u16],
out: &mut [u16],
width: usize,
full_range: bool,
) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(out.len() >= width * 3, "out too short");
let mask = bits_mask::<BITS>();
for (x, &raw) in y_plane[..width].iter().enumerate() {
let raw = if BE {
u16::from_be(raw)
} else {
u16::from_le(raw)
};
let masked = raw & mask;
let y_out = if full_range {
masked
} else {
limited_n_to_full_u16::<BITS>(masked)
};
broadcast_u16_to_rgb(y_out, out, x);
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray_n_to_rgba_u16_row<const BITS: u32, const BE: bool>(
y_plane: &[u16],
out: &mut [u16],
width: usize,
full_range: bool,
) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(out.len() >= width * 4, "out too short");
let mask = bits_mask::<BITS>();
let alpha = mask; for (x, &raw) in y_plane[..width].iter().enumerate() {
let raw = if BE {
u16::from_be(raw)
} else {
u16::from_le(raw)
};
let masked = raw & mask;
let y_out = if full_range {
masked
} else {
limited_n_to_full_u16::<BITS>(masked)
};
broadcast_u16_to_rgba(y_out, alpha, out, x);
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray_n_to_luma_row<const BITS: u32, const BE: bool>(
y_plane: &[u16],
out: &mut [u8],
width: usize,
) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(out.len() >= width, "out too short");
let mask = bits_mask::<BITS>();
let shift = BITS - 8;
for (out_byte, &raw) in out[..width].iter_mut().zip(y_plane[..width].iter()) {
let raw = if BE {
u16::from_be(raw)
} else {
u16::from_le(raw)
};
*out_byte = ((raw & mask) >> shift) as u8;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray_n_to_luma_u16_row<const BITS: u32, const BE: bool>(
y_plane: &[u16],
out: &mut [u16],
width: usize,
) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(out.len() >= width, "out too short");
let mask = bits_mask::<BITS>();
for (out_el, &raw) in out[..width].iter_mut().zip(y_plane[..width].iter()) {
let raw = if BE {
u16::from_be(raw)
} else {
u16::from_le(raw)
};
*out_el = raw & mask;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray_n_to_hsv_row<const BITS: u32, const BE: bool>(
y_plane: &[u16],
h_out: &mut [u8],
s_out: &mut [u8],
v_out: &mut [u8],
width: usize,
full_range: bool,
) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(h_out.len() >= width, "H out too short");
debug_assert!(s_out.len() >= width, "S out too short");
debug_assert!(v_out.len() >= width, "V out too short");
let mask = bits_mask::<BITS>();
let shift = BITS - 8;
for (x, &raw) in y_plane[..width].iter().enumerate() {
let raw = if BE {
u16::from_be(raw)
} else {
u16::from_le(raw)
};
let masked = raw & mask;
h_out[x] = 0;
s_out[x] = 0;
v_out[x] = if full_range {
(masked >> shift) as u8
} else {
limited_n_to_full_u8::<BITS>(masked)
};
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray16_to_rgb_row<const BE: bool>(
y_plane: &[u16],
out: &mut [u8],
width: usize,
full_range: bool,
) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(out.len() >= width * 3, "out too short");
for (x, &raw) in y_plane[..width].iter().enumerate() {
let raw = if BE {
u16::from_be(raw)
} else {
u16::from_le(raw)
};
let y8 = if full_range {
(raw >> 8) as u8
} else {
limited_16_to_full_u8(raw)
};
broadcast_u8_to_rgb(y8, out, x);
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray16_to_rgba_row<const BE: bool>(
y_plane: &[u16],
out: &mut [u8],
width: usize,
full_range: bool,
) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(out.len() >= width * 4, "out too short");
for (x, &raw) in y_plane[..width].iter().enumerate() {
let raw = if BE {
u16::from_be(raw)
} else {
u16::from_le(raw)
};
let y8 = if full_range {
(raw >> 8) as u8
} else {
limited_16_to_full_u8(raw)
};
broadcast_u8_to_rgba(y8, out, x);
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray16_to_rgb_u16_row<const BE: bool>(
y_plane: &[u16],
out: &mut [u16],
width: usize,
full_range: bool,
) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(out.len() >= width * 3, "out too short");
for (x, &raw) in y_plane[..width].iter().enumerate() {
let raw = if BE {
u16::from_be(raw)
} else {
u16::from_le(raw)
};
let y_out = if full_range {
raw
} else {
limited_16_to_full_u16(raw)
};
broadcast_u16_to_rgb(y_out, out, x);
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray16_to_rgba_u16_row<const BE: bool>(
y_plane: &[u16],
out: &mut [u16],
width: usize,
full_range: bool,
) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(out.len() >= width * 4, "out too short");
for (x, &raw) in y_plane[..width].iter().enumerate() {
let raw = if BE {
u16::from_be(raw)
} else {
u16::from_le(raw)
};
let y_out = if full_range {
raw
} else {
limited_16_to_full_u16(raw)
};
broadcast_u16_to_rgba(y_out, 0xFFFF, out, x);
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray16_to_luma_row<const BE: bool>(y_plane: &[u16], out: &mut [u8], width: usize) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(out.len() >= width, "out too short");
for (out_byte, &raw) in out[..width].iter_mut().zip(y_plane[..width].iter()) {
let raw = if BE {
u16::from_be(raw)
} else {
u16::from_le(raw)
};
*out_byte = (raw >> 8) as u8;
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray16_to_luma_u16_row<const BE: bool>(
y_plane: &[u16],
out: &mut [u16],
width: usize,
) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(out.len() >= width, "out too short");
for (o, &raw) in out[..width].iter_mut().zip(y_plane[..width].iter()) {
*o = if BE {
u16::from_be(raw)
} else {
u16::from_le(raw)
};
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn gray16_to_hsv_row<const BE: bool>(
y_plane: &[u16],
h_out: &mut [u8],
s_out: &mut [u8],
v_out: &mut [u8],
width: usize,
full_range: bool,
) {
debug_assert!(y_plane.len() >= width, "y_plane too short");
debug_assert!(h_out.len() >= width, "H out too short");
debug_assert!(s_out.len() >= width, "S out too short");
debug_assert!(v_out.len() >= width, "V out too short");
for (x, &raw) in y_plane[..width].iter().enumerate() {
let raw = if BE {
u16::from_be(raw)
} else {
u16::from_le(raw)
};
h_out[x] = 0;
s_out[x] = 0;
v_out[x] = if full_range {
(raw >> 8) as u8
} else {
limited_16_to_full_u8(raw)
};
}
}
#[cfg(all(test, feature = "std"))]
mod tests;