pub use filters::*;
use crate::compat::*;
use crate::pixels::InnerPixel;
use crate::{CpuExtensions, ImageView, ImageViewMut};
#[macro_use]
mod macros;
mod filters;
#[macro_use]
mod optimisations;
mod u8x4;
mod vertical_u8;
cfg_if::cfg_if! {
if #[cfg(not(feature = "only_u8x4"))] {
mod u8x1;
mod u8x2;
mod u8x3;
mod u16x1;
mod u16x2;
mod u16x3;
mod u16x4;
mod i32x1;
mod f32x1;
mod f32x2;
mod f32x3;
mod f32x4;
mod vertical_u16;
mod vertical_f32;
}
}
pub(crate) trait Convolution: InnerPixel {
fn horiz_convolution(
src_view: &impl ImageView<Pixel = Self>,
dst_view: &mut impl ImageViewMut<Pixel = Self>,
offset: u32,
coeffs: Coefficients,
cpu_extensions: CpuExtensions,
);
fn vert_convolution(
src_view: &impl ImageView<Pixel = Self>,
dst_view: &mut impl ImageViewMut<Pixel = Self>,
offset: u32,
coeffs: Coefficients,
cpu_extensions: CpuExtensions,
);
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct Bound {
pub start: u32,
pub size: u32,
}
#[derive(Debug, Clone, Default)]
pub(crate) struct Coefficients {
pub values: Vec<f64>,
pub window_size: usize,
pub bounds: Vec<Bound>,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct CoefficientsChunk<'a> {
pub start: u32,
pub values: &'a [f64],
}
impl Coefficients {
pub fn get_chunks(&self) -> Vec<CoefficientsChunk<'_>> {
let mut coeffs = self.values.as_slice();
let mut res = Vec::with_capacity(self.bounds.len());
for bound in &self.bounds {
let (left, right) = coeffs.split_at(self.window_size);
coeffs = right;
let size = bound.size as usize;
res.push(CoefficientsChunk {
start: bound.start,
values: &left[0..size],
});
}
res
}
}
pub(crate) fn precompute_coefficients(
in_size: u32,
in0: f64, in1: f64, out_size: u32,
filter: fn(f64) -> f64,
filter_support: f64,
adaptive_kernel_size: bool,
) -> Coefficients {
if in_size == 0 || out_size == 0 {
return Coefficients::default();
}
let scale = (in1 - in0) / out_size as f64;
if scale <= 0. {
return Coefficients::default();
}
let filter_scale = if adaptive_kernel_size {
scale.max(1.0)
} else {
1.0
};
let filter_radius = filter_support * filter_scale;
let window_size = filter_radius.ceil() as usize * 2 + 1;
let recip_filter_scale = 1.0 / filter_scale;
let count_of_coeffs = window_size * out_size as usize;
let mut coeffs: Vec<f64> = Vec::with_capacity(count_of_coeffs);
let mut bounds: Vec<Bound> = Vec::with_capacity(out_size as usize);
for out_x in 0..out_size {
let in_center = in0 + (out_x as f64 + 0.5) * scale;
let x_min = (in_center - filter_radius).floor().max(0.) as u32;
let x_max = (in_center + filter_radius).ceil().min(in_size as f64) as u32;
let cur_index = coeffs.len();
let mut ww: f64 = 0.0;
let center = in_center - 0.5;
let mut bound_start = x_min;
let mut bound_end = x_max;
for x in x_min..x_max {
let w: f64 = filter((x as f64 - center) * recip_filter_scale);
if x == bound_start && w == 0. {
bound_start += 1;
} else {
coeffs.push(w);
ww += w;
}
}
for &c in coeffs.iter().rev() {
if bound_end <= bound_start || c != 0. {
break;
}
bound_end -= 1;
}
if ww != 0.0 {
coeffs[cur_index..].iter_mut().for_each(|w| *w /= ww);
}
coeffs.resize(cur_index + window_size, 0.);
bounds.push(Bound {
start: bound_start,
size: bound_end - bound_start,
});
}
Coefficients {
values: coeffs,
window_size,
bounds,
}
}