#![feature(portable_simd, stmt_expr_attributes)]
#![cfg_attr(test, feature(test))]
use std::collections::VecDeque;
pub extern crate imgref;
use imgref::ImgRefMut;
#[cfg(test)]
mod test;
pub mod color;
pub mod iter;
pub mod traits;
use color::Argb;
use iter::StackBlur;
use traits::StackBlurrable;
pub fn blur<T, B: StackBlurrable>(
buffer: &mut ImgRefMut<T>,
radius: usize,
mut to_blurrable: impl FnMut(&T) -> B,
mut to_pixel: impl FnMut(B) -> T,
) {
use imgref_iter::iter::{IterWindows, IterWindowsPtrMut};
use imgref_iter::traits::{ImgIter, ImgIterMut, ImgIterPtrMut};
let mut ops = VecDeque::new();
let mut blur_windows = |writer: IterWindowsPtrMut<T>, reader: IterWindows<T>| {
for (write, read) in writer.zip(reader) {
let mut blur = StackBlur::new(read.map(&mut to_blurrable), radius, &mut ops);
write.for_each(|place| unsafe { *place = to_pixel(blur.next().unwrap()) });
}
};
let buffer_ptr = buffer.as_mut_ptr();
blur_windows(
unsafe { buffer_ptr.iter_rows_ptr_mut() },
buffer.iter_rows(),
);
blur_windows(
unsafe { buffer_ptr.iter_cols_ptr_mut() },
buffer.iter_cols(),
);
}
pub fn simd_blur<T, Bsimd: StackBlurrable, Bsingle: StackBlurrable, const LANES: usize>(
buffer: &mut ImgRefMut<T>,
radius: usize,
mut to_blurrable_simd: impl FnMut([&T; LANES]) -> Bsimd,
mut to_pixel_simd: impl FnMut(Bsimd) -> [T; LANES],
mut to_blurrable_single: impl FnMut(&T) -> Bsingle,
mut to_pixel_single: impl FnMut(Bsingle) -> T,
) {
#[cfg(not(doc))]
use imgref_iter::iter::{
SimdIterWindow, SimdIterWindowPtrMut, SimdIterWindows, SimdIterWindowsPtrMut,
};
#[cfg(not(doc))]
use imgref_iter::traits::{ImgIterMut, ImgSimdIter, ImgSimdIterPtrMut};
let mut ops_simd = VecDeque::new();
let mut ops_single = VecDeque::new();
let mut simd_blur_windows =
|writer: SimdIterWindowsPtrMut<T, LANES>,
reader: SimdIterWindows<T, LANES>,
mut ops_simd: VecDeque<Bsimd>,
mut ops_single: VecDeque<Bsingle>| {
for (write, read) in writer.zip(reader) {
match (write, read) {
(SimdIterWindowPtrMut::Simd(write), SimdIterWindow::Simd(read)) => {
let mut blur =
StackBlur::new(read.map(&mut to_blurrable_simd), radius, &mut ops_simd);
write.for_each(|place| {
place
.into_iter()
.zip(to_pixel_simd(blur.next().unwrap()))
.for_each(|(place, pixel)| unsafe { *place = pixel });
});
}
(SimdIterWindowPtrMut::Single(write), SimdIterWindow::Single(read)) => {
let mut blur = StackBlur::new(
read.map(&mut to_blurrable_single),
radius,
&mut ops_single,
);
write.for_each(|place| unsafe {
*place = to_pixel_single(blur.next().unwrap());
});
}
_ => unreachable!(),
}
}
(ops_simd, ops_single)
};
let buffer_ptr = buffer.as_mut_ptr();
(ops_simd, ops_single) = simd_blur_windows(
unsafe { buffer_ptr.simd_iter_rows_ptr_mut::<LANES>() },
buffer.simd_iter_rows::<LANES>(),
ops_simd,
ops_single,
);
simd_blur_windows(
unsafe { buffer_ptr.simd_iter_cols_ptr_mut::<LANES>() },
buffer.simd_iter_cols::<LANES>(),
ops_simd,
ops_single,
);
}
pub fn blur_argb(buffer: &mut ImgRefMut<u32>, radius: usize) {
blur(buffer, radius, |i| Argb::from(*i), Argb::into);
}
pub fn simd_blur_argb<const LANES: usize>(buffer: &mut ImgRefMut<u32>, radius: usize) {
simd_blur(
buffer,
radius,
|i: [&u32; LANES]| Argb::from(i.map(u32::clone)),
Argb::into,
|i| Argb::from(*i),
Argb::into,
);
}