#![cfg_attr(feature = "simd", feature(portable_simd))]
#![cfg_attr(test, feature(test))]
use std::collections::VecDeque;
#[cfg(any(doc, feature = "simd"))]
use std::simd::{LaneCount, SupportedLaneCount};
pub extern crate imgref;
use imgref::ImgRefMut;
#[cfg(test)]
mod test;
pub mod traits;
pub mod iter;
mod color;
use traits::StackBlurrable;
use iter::StackBlur;
use color::Argb;
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::traits::{ImgIter, ImgIterMut, ImgIterPtrMut};
use imgref_iter::iter::{IterWindows, IterWindowsPtrMut};
let mut ops = VecDeque::new();
let mut blur_windows = |writer: IterWindowsPtrMut<T>, reader: IterWindows<T>, mut ops: VecDeque<B>| {
for (write, read) in writer.zip(reader) {
let mut blur = StackBlur::new(read.map(&mut to_blurrable), radius, ops);
write.for_each(|place| unsafe { *place = to_pixel(blur.next().unwrap()) });
ops = blur.into_ops();
}
ops
};
let buffer_ptr = buffer.as_mut_ptr();
ops = blur_windows(unsafe { buffer_ptr.iter_rows_ptr_mut() }, buffer.iter_rows(), ops);
blur_windows(unsafe { buffer_ptr.iter_cols_ptr_mut() }, buffer.iter_cols(), ops);
}
#[cfg(any(doc, feature = "rayon"))]
pub fn par_blur<T: Send + Sync, B: StackBlurrable + Send + Sync>(
buffer: &mut ImgRefMut<T>,
radius: usize,
to_blurrable: impl Fn(&T) -> B + Sync,
to_pixel: impl Fn(B) -> T + Sync
) {
use imgref_iter::traits::{ImgIter, ImgIterMut, ImgIterPtrMut};
use imgref_iter::iter::{IterWindows, IterWindowsPtrMut};
#[cfg(not(doc))]
use rayon::iter::{ParallelBridge, ParallelIterator};
let mut opses = vec![Some(VecDeque::new()); rayon::current_num_threads()];
let opses_ptr = unsafe { unique::Unique::new_unchecked(opses.as_mut_ptr()) };
let par_blur_windows = |writer: IterWindowsPtrMut<T>, reader: IterWindows<T>| {
writer.zip(reader).par_bridge().for_each(|(write, read)| {
let ops_ref = unsafe { &mut *opses_ptr.as_ptr().add(rayon::current_thread_index().unwrap()) };
let ops = ops_ref.take().unwrap();
let mut blur = StackBlur::new(read.map(&to_blurrable), radius, ops);
write.for_each(|place| unsafe { *place = to_pixel(blur.next().unwrap()) });
ops_ref.replace(blur.into_ops());
});
};
let buffer_ptr = buffer.as_mut_ptr();
par_blur_windows(unsafe { buffer_ptr.iter_rows_ptr_mut() }, buffer.iter_rows());
par_blur_windows(unsafe { buffer_ptr.iter_cols_ptr_mut() }, buffer.iter_cols());
}
#[cfg(any(doc, feature = "simd"))]
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
) where LaneCount<LANES>: SupportedLaneCount {
#[cfg(not(doc))]
use imgref_iter::traits::{ImgIterMut, ImgSimdIter, ImgSimdIterPtrMut};
#[cfg(not(doc))]
use imgref_iter::iter::{SimdIterWindow, SimdIterWindowPtrMut, SimdIterWindows, SimdIterWindowsPtrMut};
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, ops_simd);
write.for_each(|place| place.into_iter().zip(to_pixel_simd(blur.next().unwrap())).for_each(|(place, pixel)| unsafe { *place = pixel }));
ops_simd = blur.into_ops();
}
(SimdIterWindowPtrMut::Single(write), SimdIterWindow::Single(read)) => {
let mut blur = StackBlur::new(read.map(&mut to_blurrable_single), radius, ops_single);
write.for_each(|place| unsafe { *place = to_pixel_single(blur.next().unwrap()) });
ops_single = blur.into_ops();
}
_ => 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);
}
#[cfg(any(doc, all(feature = "rayon", feature = "simd")))]
pub fn par_simd_blur<T: Send + Sync, Bsimd: StackBlurrable + Send + Sync, Bsingle: StackBlurrable + Send + Sync, const LANES: usize>(
buffer: &mut ImgRefMut<T>,
radius: usize,
to_blurrable_simd: impl Fn([&T; LANES]) -> Bsimd + Sync,
to_pixel_simd: impl Fn(Bsimd) -> [T; LANES] + Sync,
to_blurrable_single: impl Fn(&T) -> Bsingle + Sync,
to_pixel_single: impl Fn(Bsingle) -> T + Sync
) where LaneCount<LANES>: SupportedLaneCount {
#[cfg(not(doc))]
use imgref_iter::traits::{ImgIterMut, ImgSimdIter, ImgSimdIterPtrMut};
#[cfg(not(doc))]
use rayon::iter::{ParallelBridge, ParallelIterator};
#[cfg(not(doc))]
use imgref_iter::iter::{SimdIterWindow, SimdIterWindowPtrMut, SimdIterWindows, SimdIterWindowsPtrMut};
let mut opses_simd = vec![Some(VecDeque::new()); rayon::current_num_threads()];
let opses_simd_ptr = unsafe { unique::Unique::new_unchecked(opses_simd.as_mut_ptr()) };
let mut opses_single = vec![Some(VecDeque::new()); rayon::current_num_threads()];
let opses_single_ptr = unsafe { unique::Unique::new_unchecked(opses_single.as_mut_ptr()) };
let par_simd_blur_windows = |writer: SimdIterWindowsPtrMut<T, LANES>, reader: SimdIterWindows<T, LANES>| {
writer.zip(reader).par_bridge().for_each(|(write, read)| match (write, read) {
(SimdIterWindowPtrMut::Simd(write), SimdIterWindow::Simd(read)) => {
let ops_ref = unsafe { &mut *opses_simd_ptr.as_ptr().add(rayon::current_thread_index().unwrap()) };
let ops = ops_ref.take().unwrap();
let mut blur = StackBlur::new(read.map(&to_blurrable_simd), radius, ops);
write.for_each(|place| place.into_iter().zip(to_pixel_simd(blur.next().unwrap())).for_each(|(place, pixel)| unsafe { *place = pixel }));
ops_ref.replace(blur.into_ops());
}
(SimdIterWindowPtrMut::Single(write), SimdIterWindow::Single(read)) => {
let ops_ref = unsafe { &mut *opses_single_ptr.as_ptr().add(rayon::current_thread_index().unwrap()) };
let ops = ops_ref.take().unwrap();
let mut blur = StackBlur::new(read.map(&to_blurrable_single), radius, ops);
write.for_each(|place| unsafe { *place = to_pixel_single(blur.next().unwrap()) });
ops_ref.replace(blur.into_ops());
}
_ => unreachable!()
});
};
let buffer_ptr = buffer.as_mut_ptr();
par_simd_blur_windows(unsafe { buffer_ptr.simd_iter_rows_ptr_mut::<LANES>() }, buffer.simd_iter_rows::<LANES>());
par_simd_blur_windows(unsafe { buffer_ptr.simd_iter_cols_ptr_mut::<LANES>() }, buffer.simd_iter_cols::<LANES>());
}
pub fn blur_argb(buffer: &mut ImgRefMut<u32>, radius: usize) {
blur(buffer, radius, |i| Argb::from_u32(*i), Argb::to_u32);
}
#[cfg(any(doc, feature = "blend-srgb"))]
pub fn blur_srgb(buffer: &mut ImgRefMut<u32>, radius: usize) {
blur(buffer, radius, |i| Argb::from_u32_srgb(*i), Argb::to_u32_srgb);
}
#[cfg(any(doc, feature = "rayon"))]
pub fn par_blur_argb(buffer: &mut ImgRefMut<u32>, radius: usize) {
par_blur(buffer, radius, |i| Argb::from_u32(*i), Argb::to_u32);
}
#[cfg(any(doc, all(feature = "rayon", feature = "blend-srgb")))]
pub fn par_blur_srgb(buffer: &mut ImgRefMut<u32>, radius: usize) {
par_blur(buffer, radius, |i| Argb::from_u32_srgb(*i), Argb::to_u32_srgb);
}
#[cfg(any(doc, feature = "simd"))]
pub fn simd_blur_argb<const LANES: usize>(buffer: &mut ImgRefMut<u32>, radius: usize) where LaneCount<LANES>: SupportedLaneCount {
simd_blur(buffer, radius,
|i: [&u32; LANES]| Argb::from_u32xN(i.map(u32::clone)), Argb::to_u32xN,
|i| Argb::from_u32(*i), Argb::to_u32
);
}
#[cfg(any(doc, all(feature = "simd", feature = "blend-srgb")))]
pub fn simd_blur_srgb<const LANES: usize>(buffer: &mut ImgRefMut<u32>, radius: usize) where LaneCount<LANES>: SupportedLaneCount {
simd_blur(buffer, radius,
|i: [&u32; LANES]| Argb::from_u32xN_srgb(i.map(u32::clone)), Argb::to_u32xN_srgb,
|i| Argb::from_u32_srgb(*i), Argb::to_u32_srgb
);
}
#[cfg(any(doc, all(feature = "rayon", feature = "simd")))]
pub fn par_simd_blur_argb<const LANES: usize>(buffer: &mut ImgRefMut<u32>, radius: usize) where LaneCount<LANES>: SupportedLaneCount {
par_simd_blur(buffer, radius,
|i: [&u32; LANES]| Argb::from_u32xN(i.map(u32::clone)), Argb::to_u32xN,
|i| Argb::from_u32(*i), Argb::to_u32
);
}
#[cfg(any(doc, all(feature = "rayon", feature = "simd", feature = "blend-srgb")))]
pub fn par_simd_blur_srgb<const LANES: usize>(buffer: &mut ImgRefMut<u32>, radius: usize) where LaneCount<LANES>: SupportedLaneCount {
par_simd_blur(buffer, radius,
|i: [&u32; LANES]| Argb::from_u32xN_srgb(i.map(u32::clone)), Argb::to_u32xN_srgb,
|i| Argb::from_u32_srgb(*i), Argb::to_u32_srgb
);
}