use bytemuck::cast_slice_mut;
use crate::demosaic::{
ColorFilterArray, RasterMut, run_demosaic_imagedata, run_demosaic_imagedata_serial,
};
use crate::{
BayerPattern, ColorSpace, DemosaicMethod, Enlargeable, ImageRef, PixelStor, PixelType, U10,
U12, U14,
};
use super::spec::pixel_size;
use super::{PipelineError, ScaleFactor};
pub(super) enum Demosaic<'s> {
Alloc,
Pooled(&'s mut [f32]),
}
#[allow(clippy::too_many_arguments)]
pub(super) fn debayer_into(
src: &mut [f32],
dst: &mut [f32],
in_pt: PixelType,
w: usize,
h: usize,
pat: BayerPattern,
method: DemosaicMethod,
demosaic: &mut Demosaic<'_>,
) -> Result<(), PipelineError> {
match in_pt {
PixelType::U8 => debayer_typed::<u8>(
cast_slice_mut(src),
cast_slice_mut(dst),
w,
h,
pat,
method,
demosaic,
),
PixelType::U10 => debayer_typed::<U10>(
cast_slice_mut(src),
cast_slice_mut(dst),
w,
h,
pat,
method,
demosaic,
),
PixelType::U12 => debayer_typed::<U12>(
cast_slice_mut(src),
cast_slice_mut(dst),
w,
h,
pat,
method,
demosaic,
),
PixelType::U14 => debayer_typed::<U14>(
cast_slice_mut(src),
cast_slice_mut(dst),
w,
h,
pat,
method,
demosaic,
),
PixelType::U16 => debayer_typed::<u16>(
cast_slice_mut(src),
cast_slice_mut(dst),
w,
h,
pat,
method,
demosaic,
),
PixelType::F32 => debayer_typed::<f32>(src, dst, w, h, pat, method, demosaic),
}
}
#[allow(clippy::too_many_arguments)]
fn debayer_typed<T: PixelStor + Enlargeable + bytemuck::AnyBitPattern>(
src: &mut [T],
dst: &mut [T],
w: usize,
h: usize,
pat: BayerPattern,
method: DemosaicMethod,
demosaic: &mut Demosaic<'_>,
) -> Result<(), PipelineError> {
let elems = w * h;
let cfa: ColorFilterArray = ColorSpace::Bayer(pat).try_into()?;
let img = ImageRef::<T> {
data: &mut src[..elems],
len: elems,
width: w as u16,
height: h as u16,
cspace: ColorSpace::Bayer(pat),
bit_depth: None,
};
let mut raster = RasterMut::new(w, h, &mut dst[..w * h * 3]);
match demosaic {
Demosaic::Alloc => run_demosaic_imagedata(&img, cfa, method, &mut raster)?,
Demosaic::Pooled(pool) => {
let scratch: &mut [T] = cast_slice_mut(pool);
run_demosaic_imagedata_serial(&img, cfa, method, &mut raster, scratch)?
}
}
Ok(())
}
pub(super) fn convert_inplace(
buf: &mut [f32],
in_pt: PixelType,
out_pt: PixelType,
n: usize,
) -> Result<(), PipelineError> {
let is = pixel_size(in_pt)?;
let os = pixel_size(out_pt)?;
if in_pt == out_pt {
return Ok(());
}
let b = cast_slice_mut::<f32, u8>(buf);
let mut apply = |i: usize| {
let o = convert_one(&b[i * is..i * is + is], in_pt, out_pt);
b[i * os..i * os + os].copy_from_slice(&o[..os]);
};
if os <= is {
for i in 0..n {
apply(i);
}
} else {
for i in (0..n).rev() {
apply(i);
}
}
Ok(())
}
fn cast_to_bytes<T: PixelStor>(v: T, out_pt: PixelType) -> [u8; 4] {
let pad2 = |b: [u8; 2]| [b[0], b[1], 0, 0];
let pad1 = |b: u8| [b, 0, 0, 0];
match out_pt {
PixelType::U8 => pad1(v.cast_u8()),
PixelType::U16 => pad2(v.cast_u16().to_ne_bytes()),
PixelType::F32 => v.cast_f32().to_ne_bytes(),
_ => unreachable!("Op::Convert target is always a storage type"),
}
}
fn convert_one(src: &[u8], in_pt: PixelType, out_pt: PixelType) -> [u8; 4] {
let u16_at = |src: &[u8]| u16::from_ne_bytes([src[0], src[1]]);
match in_pt {
PixelType::U8 => cast_to_bytes(src[0], out_pt),
PixelType::U10 => cast_to_bytes(U10(u16_at(src)), out_pt),
PixelType::U12 => cast_to_bytes(U12(u16_at(src)), out_pt),
PixelType::U14 => cast_to_bytes(U14(u16_at(src)), out_pt),
PixelType::U16 => cast_to_bytes(u16_at(src), out_pt),
PixelType::F32 => {
cast_to_bytes(f32::from_ne_bytes([src[0], src[1], src[2], src[3]]), out_pt)
}
}
}
fn scale_typed<T: PixelStor + bytemuck::AnyBitPattern>(
buf: &mut [f32],
n: usize,
gain: f64,
offset: f64,
) {
for x in &mut cast_slice_mut::<f32, T>(buf)[..n] {
*x = T::from_f64((*x).to_f64() * gain + offset);
}
}
pub(super) fn scale_inplace(buf: &mut [f32], pt: PixelType, n: usize, gain: f64, offset: f64) {
match pt {
PixelType::U8 => scale_typed::<u8>(buf, n, gain, offset),
PixelType::U10 => scale_typed::<U10>(buf, n, gain, offset),
PixelType::U12 => scale_typed::<U12>(buf, n, gain, offset),
PixelType::U14 => scale_typed::<U14>(buf, n, gain, offset),
PixelType::U16 => scale_typed::<u16>(buf, n, gain, offset),
PixelType::F32 => scale_typed::<f32>(buf, n, gain, offset),
}
}
fn scale_pixels_rational_typed<T: PixelStor + bytemuck::AnyBitPattern>(
buf: &mut [f32],
n: usize,
num: i128,
den: i128,
) {
let lo = T::DEFAULT_MIN_VALUE.to_i64().unwrap() as i128;
let hi = T::DEFAULT_MAX_VALUE.to_i64().unwrap() as i128;
for x in &mut cast_slice_mut::<f32, T>(buf)[..n] {
let raw = x.to_i64().unwrap() as i128;
let v = rational_round(raw, num, den).clamp(lo, hi);
*x = <T as num_traits::NumCast>::from(v as i64).unwrap();
}
}
pub(super) fn scale_pixels_inplace(
buf: &mut [f32],
pt: PixelType,
n: usize,
factor: ScaleFactor,
) -> Result<(), PipelineError> {
let ScaleFactor::Rational { num, den } = factor else {
let ScaleFactor::Float(f) = factor else {
unreachable!()
};
scale_inplace(buf, pt, n, f, 0.0);
return Ok(());
};
if den == 0 {
return Err(PipelineError::BadScaleFactor);
}
let (num, den) = (num as i128, den as i128);
match pt {
PixelType::U8 => scale_pixels_rational_typed::<u8>(buf, n, num, den),
PixelType::U10 => scale_pixels_rational_typed::<U10>(buf, n, num, den),
PixelType::U12 => scale_pixels_rational_typed::<U12>(buf, n, num, den),
PixelType::U14 => scale_pixels_rational_typed::<U14>(buf, n, num, den),
PixelType::U16 => scale_pixels_rational_typed::<u16>(buf, n, num, den),
PixelType::F32 => scale_typed::<f32>(buf, n, num as f64 / den as f64, 0.0),
}
Ok(())
}
fn rational_round(x: i128, num: i128, den: i128) -> i128 {
let (mut p, mut q) = (x * num, den);
if q < 0 {
p = -p;
q = -q;
}
if p >= 0 {
(p + q / 2) / q
} else {
-((-p + q / 2) / q)
}
}
pub(super) fn luma_inplace(
buf: &mut [f32],
in_pt: PixelType,
channels: usize,
total: usize,
weights: &[f64],
) {
match in_pt {
PixelType::U8 => {
crate::coreimpls::run_luma(channels, total, cast_slice_mut::<f32, u8>(buf), weights)
}
PixelType::U10 => {
crate::coreimpls::run_luma(channels, total, cast_slice_mut::<f32, U10>(buf), weights)
}
PixelType::U12 => {
crate::coreimpls::run_luma(channels, total, cast_slice_mut::<f32, U12>(buf), weights)
}
PixelType::U14 => {
crate::coreimpls::run_luma(channels, total, cast_slice_mut::<f32, U14>(buf), weights)
}
PixelType::U16 => {
crate::coreimpls::run_luma(channels, total, cast_slice_mut::<f32, u16>(buf), weights)
}
PixelType::F32 => crate::coreimpls::run_luma(channels, total, buf, weights),
}
}