pub(crate) const FILTER_BITS: i32 = 7;
pub(crate) static WIENER_TAPS_MIN: [i32; 3] = [-5, -23, -17];
pub(crate) static WIENER_TAPS_MAX: [i32; 3] = [10, 8, 46];
pub(crate) static WIENER_TAPS_K: [i32; 3] = [1, 2, 3];
pub(crate) static WIENER_TAPS_MID: [i32; 3] = [3, -7, 15];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct WienerKernel {
pub(crate) taps: [i32; 7],
}
impl WienerKernel {
pub(crate) fn from_coded(c: [i32; 3]) -> Self {
let centre = (1 << FILTER_BITS) - 2 * (c[0] + c[1] + c[2]);
WienerKernel {
taps: [c[0], c[1], c[2], centre, c[2], c[1], c[0]],
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct WienerUnit {
pub h: [i32; 3],
pub v: [i32; 3],
}
#[inline]
fn inter_rounds(bd: u8) -> (i32, i32) {
let cs = (bd - 8) as i32;
(3 + cs, 11 - cs)
}
#[inline]
fn clamp3(v: i32, lo: i32, hi: i32) -> i32 {
v.max(lo).min(hi)
}
#[inline]
fn get(plane: &[i32], stride: usize, w: usize, h: usize, x: i32, y: i32) -> i32 {
let xx = x.clamp(0, w as i32 - 1) as usize;
let yy = y.clamp(0, h as i32 - 1) as usize;
plane[yy * stride + xx]
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn wiener_filter_rect(
dst: &mut [i32],
dst_y0: usize,
src: &[i32],
stride: usize,
w: usize,
h: usize,
x0: usize,
y0: usize,
rw: usize,
rh: usize,
ytop: usize,
ybot: usize,
hk: &WienerKernel,
vk: &WienerKernel,
bd: u8,
) {
let (round0, round1) = inter_rounds(bd);
let bdi = bd as i32;
let maxv = (1i32 << bd) - 1;
let offset = 1i32 << (bdi + FILTER_BITS - 1);
let limit = (1i32 << (bdi + 1 + FILTER_BITS - round0)) - 1;
let pad = 3usize;
let ih = rh + 2 * pad;
let mut inter = vec![0i32; ih * rw];
for r in 0..ih {
let raw_sy = y0 as i32 + r as i32 - pad as i32;
let sy = raw_sy.clamp(ytop as i32, ybot as i32);
for c in 0..rw {
let sx = x0 as i32 + c as i32;
let mut s = 0i32;
for t in 0..7 {
let px = get(src, stride, w, h, sx + t as i32 - 3, sy);
s += hk.taps[t] * px;
}
let v = clamp3((s + (1 << (round0 - 1)) + offset) >> round0, 0, limit);
inter[r * rw + c] = v;
}
}
let round1_offset = 1i32 << (round1 - 1);
let offset_correction = offset << (FILTER_BITS - round0);
for r in 0..rh {
for c in 0..rw {
let mut s = 0i32;
for t in 0..7 {
let iy = r as i32 + t as i32;
s += vk.taps[t] * inter[iy as usize * rw + c];
}
let v = (s - offset_correction + round1_offset) >> round1;
dst[(y0 + r - dst_y0) * stride + (x0 + c)] = v.clamp(0, maxv);
}
}
}
pub(crate) fn wiener_filter_plane(
dst: &mut [i32],
src: &[i32],
w: usize,
h: usize,
hk: &WienerKernel,
vk: &WienerKernel,
bd: u8,
) {
let mut ytop = 0usize;
while ytop < h {
let stripe_h = if ytop == 0 { 56 } else { 64 };
let ybot = (ytop + stripe_h).min(h);
let ctop = ytop.saturating_sub(2);
let cbot = (ybot + 2).min(h) - 1;
wiener_filter_rect(
dst,
0,
src,
w,
w,
h,
0,
ytop,
w,
ybot - ytop,
ctop,
cbot,
hk,
vk,
bd,
);
ytop = ybot;
}
}