use bytemuck::{cast_slice, cast_slice_mut};
use crate::{DynamicImageRef, ImageRef, PixelType};
use super::geom::{geo_crop, geo_flip, geo_roi, geo_rot90};
use super::kernels::{
Demosaic, convert_inplace, debayer_into, luma_inplace, scale_inplace, scale_pixels_inplace,
};
use super::plan::{Step, StepKind};
use super::resample::geo_resize;
use super::spec::pixel_size;
use super::{ImageSpec, PipelineError};
pub(super) fn run_chain(
steps: &[Step],
coeffs: &[Box<[f64]>],
buf_a: &mut [f32],
buf_b: &mut [f32],
w: usize,
h: usize,
mut demosaic: Demosaic<'_>,
) -> Result<bool, PipelineError> {
let mut cur_a = true;
let (mut cw, mut ch) = (w, h);
for step in steps {
let n = cw * ch * step.in_channels as usize;
let bpp = step.in_channels as usize * pixel_size(step.in_pt)?;
match step.kind {
StepKind::Debayer(method) => {
let pat = step.bayer.expect("debayer step carries a pattern");
let (src, dst) = pick(&mut *buf_a, &mut *buf_b, cur_a);
debayer_into(src, dst, step.in_pt, cw, ch, pat, method, &mut demosaic)?;
cur_a = !cur_a;
}
StepKind::Convert => {
let buf = current(&mut *buf_a, &mut *buf_b, cur_a);
convert_inplace(buf, step.in_pt, step.out_pt, n)?;
}
StepKind::Scale { gain, offset } => {
let buf = current(&mut *buf_a, &mut *buf_b, cur_a);
scale_inplace(buf, step.in_pt, n, gain, offset)?;
}
StepKind::ScalePixels(factor) => {
let buf = current(&mut *buf_a, &mut *buf_b, cur_a);
scale_pixels_inplace(buf, step.in_pt, n, factor)?;
}
StepKind::Luma => {
if !step.luma_identity {
let cf = &coeffs[step.coeff_idx];
let buf = current(&mut *buf_a, &mut *buf_b, cur_a);
luma_inplace(buf, step.in_pt, step.in_channels as usize, n, cf)?;
}
}
StepKind::Crop { x, y, w: ow, h: oh } => {
let (src, dst) = pick(&mut *buf_a, &mut *buf_b, cur_a);
geo_crop(src, dst, cw, bpp, x, y, ow, oh);
cw = ow;
ch = oh;
cur_a = !cur_a;
}
StepKind::Roi { x, y, w: ow, h: oh } => {
let (src, dst) = pick(&mut *buf_a, &mut *buf_b, cur_a);
geo_roi(src, dst, cw, ch, bpp, x, y, ow, oh);
cw = ow;
ch = oh;
cur_a = !cur_a;
}
StepKind::Flip {
horizontal,
vertical,
} => {
let (src, dst) = pick(&mut *buf_a, &mut *buf_b, cur_a);
geo_flip(src, dst, cw, ch, bpp, horizontal, vertical);
cur_a = !cur_a;
}
StepKind::Rot90 { ccw } => {
let (src, dst) = pick(&mut *buf_a, &mut *buf_b, cur_a);
geo_rot90(src, dst, cw, ch, bpp, ccw);
std::mem::swap(&mut cw, &mut ch);
cur_a = !cur_a;
}
StepKind::Resize {
w: ow,
h: oh,
filter,
} => {
let (src, dst) = pick(&mut *buf_a, &mut *buf_b, cur_a);
geo_resize(
src,
dst,
cw,
ch,
step.in_channels as usize,
step.in_pt,
ow,
oh,
filter,
)?;
cw = ow;
ch = oh;
cur_a = !cur_a;
}
StepKind::Nop => {
}
}
}
Ok(cur_a)
}
fn halo_range(lo: usize, hi: usize, limit: usize, halo: usize, even: bool) -> (usize, usize) {
if halo == 0 && !even {
return (lo, hi);
}
let (mut a, mut b) = if even {
(
lo.saturating_sub(halo) & !1,
((hi + halo + 1) & !1).min(limit),
)
} else {
(lo.saturating_sub(halo), (hi + halo).min(limit))
};
let step = if even { 2 } else { 1 };
let min_span = 2 * halo + 3;
while b - a < min_span {
if b < limit {
b = (b + step).min(limit);
} else if a >= step {
a -= step;
} else {
break;
}
}
(a, b)
}
#[derive(Clone, Copy)]
pub(super) struct TileGeom {
pub(super) tile_rows: usize,
pub(super) tile_cols: usize, pub(super) halo: usize,
pub(super) even: bool,
}
#[allow(clippy::too_many_arguments)]
pub(super) fn fill_tiled(
steps: &[Step],
coeffs: &[Box<[f64]>],
in_spec: &ImageSpec,
out_spec: &ImageSpec,
frame: &[u8],
frame_stride: usize,
frame_off: usize,
out_buf: &mut [f32],
scratch_a: &mut [f32],
scratch_b: &mut [f32],
demosaic_scratch: &mut [f32],
geom: TileGeom,
parallel: bool,
) -> Result<(), PipelineError> {
let TileGeom {
tile_rows,
tile_cols,
halo,
even,
} = geom;
let w = in_spec.width;
let h = in_spec.height;
let cols = if tile_cols == 0 || tile_cols >= w {
w
} else {
tile_cols
};
let in_bpp = in_spec.bpp()?;
let out_bpp = out_spec.bpp()?;
let out_row = w * out_bpp;
let out_bytes = h * out_row;
let n_cols = w.div_ceil(cols);
let cap_a = scratch_a.len();
let cap_b = scratch_b.len();
let demo_len = demosaic_scratch.len();
let do_band = |band_idx: usize,
band: &mut [u8],
sa: &mut [f32],
sb: &mut [f32],
demo: &mut [f32]|
-> Result<(), PipelineError> {
let y0 = band_idx * tile_rows;
if y0 >= h {
return Ok(());
}
let y1 = (y0 + tile_rows).min(h);
let (ry0, ry1) = halo_range(y0, y1, h, halo, even);
let sh = ry1 - ry0;
for col in 0..n_cols {
let x0 = col * cols;
let x1 = (x0 + cols).min(w);
let (rx0, rx1) = halo_range(x0, x1, w, halo, even);
let sw = rx1 - rx0;
let sa_u8 = cast_slice_mut::<f32, u8>(&mut *sa);
let s_in_row = sw * in_bpp;
for r in 0..sh {
let src = frame_off + (ry0 + r) * frame_stride + rx0 * in_bpp;
sa_u8[r * s_in_row..r * s_in_row + s_in_row]
.copy_from_slice(&frame[src..src + s_in_row]);
}
let dm = if demo.is_empty() {
Demosaic::Alloc
} else {
Demosaic::Pooled(&mut *demo)
};
let cur_a = run_chain(steps, coeffs, &mut *sa, &mut *sb, sw, sh, dm)?;
let res: &[f32] = if cur_a { &*sa } else { &*sb };
let res_u8 = cast_slice::<f32, u8>(res);
let s_out_row = sw * out_bpp;
let y_skip = y0 - ry0;
if x0 == 0 && x1 == w && sw == w {
let take = (y1 - y0) * out_row;
let s0 = y_skip * s_out_row;
band[..take].copy_from_slice(&res_u8[s0..s0 + take]);
} else {
let x_skip_b = (x0 - rx0) * out_bpp;
let span_b = (x1 - x0) * out_bpp;
for r in 0..(y1 - y0) {
let d = r * out_row + x0 * out_bpp;
let s = (y_skip + r) * s_out_row + x_skip_b;
band[d..d + span_b].copy_from_slice(&res_u8[s..s + span_b]);
}
}
}
Ok(())
};
#[cfg(feature = "rayon")]
if parallel {
use rayon::prelude::*;
let out_u8 = &mut cast_slice_mut::<f32, u8>(out_buf)[..out_bytes];
return out_u8
.par_chunks_mut(tile_rows * out_row)
.enumerate()
.try_for_each_init(
|| {
(
vec![0.0f32; cap_a],
vec![0.0f32; cap_b],
vec![0.0f32; demo_len],
)
},
|(sa, sb, demo), (idx, band)| do_band(idx, band, sa, sb, demo),
);
}
#[cfg(not(feature = "rayon"))]
let _ = (parallel, cap_a, cap_b, demo_len);
let out_u8 = &mut cast_slice_mut::<f32, u8>(out_buf)[..out_bytes];
for (idx, band) in out_u8.chunks_mut(tile_rows * out_row).enumerate() {
do_band(
idx,
band,
&mut *scratch_a,
&mut *scratch_b,
&mut *demosaic_scratch,
)?;
}
Ok(())
}
fn pick<'x>(a: &'x mut [f32], b: &'x mut [f32], cur_a: bool) -> (&'x mut [f32], &'x mut [f32]) {
if cur_a { (a, b) } else { (b, a) }
}
pub(super) fn current<'x>(a: &'x mut [f32], b: &'x mut [f32], cur_a: bool) -> &'x mut [f32] {
if cur_a { a } else { b }
}
pub(super) fn view<'a>(
buf: &'a mut [f32],
spec: &ImageSpec,
) -> Result<DynamicImageRef<'a>, PipelineError> {
let n = spec.elems();
let (w, h) = (spec.width, spec.height);
let cs = spec.cspace.clone();
Ok(match spec.pixel_type.storage() {
PixelType::U8 => {
let d = &mut cast_slice_mut::<f32, u8>(buf)[..n];
DynamicImageRef::from(ImageRef::<u8>::create(d, w, h, cs)?)
}
PixelType::U16 => {
let d = &mut cast_slice_mut::<f32, u16>(buf)[..n];
let img = ImageRef::<u16>::create(d, w, h, cs)?
.with_bit_depth(spec.pixel_type.bit_depth());
DynamicImageRef::from(img)
}
PixelType::F32 => {
let d = &mut buf[..n];
DynamicImageRef::from(ImageRef::<f32>::create(d, w, h, cs)?)
}
other => return Err(PipelineError::UnsupportedPixelType(other)),
})
}