1#![cfg_attr(feature = "simd", feature(portable_simd))]
49#![cfg_attr(test, feature(test))]
50
51use std::collections::VecDeque;
52#[cfg(any(doc, feature = "simd"))]
53use std::simd::{LaneCount, SupportedLaneCount};
54
55pub extern crate imgref;
56
57use imgref::ImgRefMut;
58
59#[cfg(test)]
60mod test;
61
62pub mod traits;
63pub mod iter;
64mod color;
65
66use traits::StackBlurrable;
67use iter::StackBlur;
68use color::Argb;
69
70pub fn blur<T, B: StackBlurrable>(
75 buffer: &mut ImgRefMut<T>,
76 radius: usize,
77 mut to_blurrable: impl FnMut(&T) -> B,
78 mut to_pixel: impl FnMut(B) -> T
79) {
80 use imgref_iter::traits::{ImgIter, ImgIterMut, ImgIterPtrMut};
81 use imgref_iter::iter::{IterWindows, IterWindowsPtrMut};
82
83 let mut ops = VecDeque::new();
84
85 let mut blur_windows = |writer: IterWindowsPtrMut<T>, reader: IterWindows<T>, mut ops: VecDeque<B>| {
90 for (write, read) in writer.zip(reader) {
91 let mut blur = StackBlur::new(read.map(&mut to_blurrable), radius, ops);
92 write.for_each(|place| unsafe { *place = to_pixel(blur.next().unwrap()) });
93 ops = blur.into_ops();
94 }
95
96 ops
97 };
98
99 let buffer_ptr = buffer.as_mut_ptr();
100 ops = blur_windows(unsafe { buffer_ptr.iter_rows_ptr_mut() }, buffer.iter_rows(), ops);
101 blur_windows(unsafe { buffer_ptr.iter_cols_ptr_mut() }, buffer.iter_cols(), ops);
102}
103
104#[cfg(any(doc, feature = "rayon"))]
109pub fn par_blur<T: Send + Sync, B: StackBlurrable + Send + Sync>(
110 buffer: &mut ImgRefMut<T>,
111 radius: usize,
112 to_blurrable: impl Fn(&T) -> B + Sync,
113 to_pixel: impl Fn(B) -> T + Sync
114) {
115 use imgref_iter::traits::{ImgIter, ImgIterMut, ImgIterPtrMut};
116 use imgref_iter::iter::{IterWindows, IterWindowsPtrMut};
117 #[cfg(not(doc))]
118 use rayon::iter::{ParallelBridge, ParallelIterator};
119
120 let mut opses = vec![Some(VecDeque::new()); rayon::current_num_threads()];
121 let opses_ptr = unsafe { unique::Unique::new_unchecked(opses.as_mut_ptr()) };
122
123 let par_blur_windows = |writer: IterWindowsPtrMut<T>, reader: IterWindows<T>| {
124 writer.zip(reader).par_bridge().for_each(|(write, read)| {
125 let ops_ref = unsafe { &mut *opses_ptr.as_ptr().add(rayon::current_thread_index().unwrap()) };
126 let ops = ops_ref.take().unwrap();
127 let mut blur = StackBlur::new(read.map(&to_blurrable), radius, ops);
128 write.for_each(|place| unsafe { *place = to_pixel(blur.next().unwrap()) });
129 ops_ref.replace(blur.into_ops());
130 });
131 };
132
133 let buffer_ptr = buffer.as_mut_ptr();
134 par_blur_windows(unsafe { buffer_ptr.iter_rows_ptr_mut() }, buffer.iter_rows());
135 par_blur_windows(unsafe { buffer_ptr.iter_cols_ptr_mut() }, buffer.iter_cols());
136}
137
138#[cfg(any(doc, feature = "simd"))]
143pub fn simd_blur<T, Bsimd: StackBlurrable, Bsingle: StackBlurrable, const LANES: usize>(
144 buffer: &mut ImgRefMut<T>,
145 radius: usize,
146 mut to_blurrable_simd: impl FnMut([&T; LANES]) -> Bsimd,
147 mut to_pixel_simd: impl FnMut(Bsimd) -> [T; LANES],
148 mut to_blurrable_single: impl FnMut(&T) -> Bsingle,
149 mut to_pixel_single: impl FnMut(Bsingle) -> T
150) where LaneCount<LANES>: SupportedLaneCount {
151 #[cfg(not(doc))]
152 use imgref_iter::traits::{ImgIterMut, ImgSimdIter, ImgSimdIterPtrMut};
153 #[cfg(not(doc))]
154 use imgref_iter::iter::{SimdIterWindow, SimdIterWindowPtrMut, SimdIterWindows, SimdIterWindowsPtrMut};
155
156 let mut ops_simd = VecDeque::new();
157 let mut ops_single = VecDeque::new();
158
159 let mut simd_blur_windows = |writer: SimdIterWindowsPtrMut<T, LANES>, reader: SimdIterWindows<T, LANES>, mut ops_simd: VecDeque<Bsimd>, mut ops_single: VecDeque<Bsingle>| {
160 for (write, read) in writer.zip(reader) {
161 match (write, read) {
162 (SimdIterWindowPtrMut::Simd(write), SimdIterWindow::Simd(read)) => {
163 let mut blur = StackBlur::new(read.map(&mut to_blurrable_simd), radius, ops_simd);
164 write.for_each(|place| place.into_iter().zip(to_pixel_simd(blur.next().unwrap())).for_each(|(place, pixel)| unsafe { *place = pixel }));
165 ops_simd = blur.into_ops();
166 }
167
168 (SimdIterWindowPtrMut::Single(write), SimdIterWindow::Single(read)) => {
169 let mut blur = StackBlur::new(read.map(&mut to_blurrable_single), radius, ops_single);
170 write.for_each(|place| unsafe { *place = to_pixel_single(blur.next().unwrap()) });
171 ops_single = blur.into_ops();
172 }
173
174 _ => unreachable!()
175 }
176 }
177
178 (ops_simd, ops_single)
179 };
180
181 let buffer_ptr = buffer.as_mut_ptr();
182 (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);
183 simd_blur_windows(unsafe { buffer_ptr.simd_iter_cols_ptr_mut::<LANES>() }, buffer.simd_iter_cols::<LANES>(), ops_simd, ops_single);
184}
185
186#[cfg(any(doc, all(feature = "rayon", feature = "simd")))]
191pub fn par_simd_blur<T: Send + Sync, Bsimd: StackBlurrable + Send + Sync, Bsingle: StackBlurrable + Send + Sync, const LANES: usize>(
192 buffer: &mut ImgRefMut<T>,
193 radius: usize,
194 to_blurrable_simd: impl Fn([&T; LANES]) -> Bsimd + Sync,
195 to_pixel_simd: impl Fn(Bsimd) -> [T; LANES] + Sync,
196 to_blurrable_single: impl Fn(&T) -> Bsingle + Sync,
197 to_pixel_single: impl Fn(Bsingle) -> T + Sync
198) where LaneCount<LANES>: SupportedLaneCount {
199 #[cfg(not(doc))]
200 use imgref_iter::traits::{ImgIterMut, ImgSimdIter, ImgSimdIterPtrMut};
201 #[cfg(not(doc))]
202 use rayon::iter::{ParallelBridge, ParallelIterator};
203 #[cfg(not(doc))]
204 use imgref_iter::iter::{SimdIterWindow, SimdIterWindowPtrMut, SimdIterWindows, SimdIterWindowsPtrMut};
205
206 let mut opses_simd = vec![Some(VecDeque::new()); rayon::current_num_threads()];
207 let opses_simd_ptr = unsafe { unique::Unique::new_unchecked(opses_simd.as_mut_ptr()) };
208
209 let mut opses_single = vec![Some(VecDeque::new()); rayon::current_num_threads()];
210 let opses_single_ptr = unsafe { unique::Unique::new_unchecked(opses_single.as_mut_ptr()) };
211
212 let par_simd_blur_windows = |writer: SimdIterWindowsPtrMut<T, LANES>, reader: SimdIterWindows<T, LANES>| {
213 writer.zip(reader).par_bridge().for_each(|(write, read)| match (write, read) {
214 (SimdIterWindowPtrMut::Simd(write), SimdIterWindow::Simd(read)) => {
215 let ops_ref = unsafe { &mut *opses_simd_ptr.as_ptr().add(rayon::current_thread_index().unwrap()) };
216 let ops = ops_ref.take().unwrap();
217 let mut blur = StackBlur::new(read.map(&to_blurrable_simd), radius, ops);
218 write.for_each(|place| place.into_iter().zip(to_pixel_simd(blur.next().unwrap())).for_each(|(place, pixel)| unsafe { *place = pixel }));
219 ops_ref.replace(blur.into_ops());
220 }
221
222 (SimdIterWindowPtrMut::Single(write), SimdIterWindow::Single(read)) => {
223 let ops_ref = unsafe { &mut *opses_single_ptr.as_ptr().add(rayon::current_thread_index().unwrap()) };
224 let ops = ops_ref.take().unwrap();
225 let mut blur = StackBlur::new(read.map(&to_blurrable_single), radius, ops);
226 write.for_each(|place| unsafe { *place = to_pixel_single(blur.next().unwrap()) });
227 ops_ref.replace(blur.into_ops());
228 }
229
230 _ => unreachable!()
231 });
232 };
233
234 let buffer_ptr = buffer.as_mut_ptr();
235 par_simd_blur_windows(unsafe { buffer_ptr.simd_iter_rows_ptr_mut::<LANES>() }, buffer.simd_iter_rows::<LANES>());
236 par_simd_blur_windows(unsafe { buffer_ptr.simd_iter_cols_ptr_mut::<LANES>() }, buffer.simd_iter_cols::<LANES>());
237}
238
239pub fn blur_argb(buffer: &mut ImgRefMut<u32>, radius: usize) {
246 blur(buffer, radius, |i| Argb::from_u32(*i), Argb::to_u32);
247}
248
249#[cfg(any(doc, feature = "blend-srgb"))]
256pub fn blur_srgb(buffer: &mut ImgRefMut<u32>, radius: usize) {
257 blur(buffer, radius, |i| Argb::from_u32_srgb(*i), Argb::to_u32_srgb);
258}
259
260#[cfg(any(doc, feature = "rayon"))]
267pub fn par_blur_argb(buffer: &mut ImgRefMut<u32>, radius: usize) {
268 par_blur(buffer, radius, |i| Argb::from_u32(*i), Argb::to_u32);
269}
270
271#[cfg(any(doc, all(feature = "rayon", feature = "blend-srgb")))]
278pub fn par_blur_srgb(buffer: &mut ImgRefMut<u32>, radius: usize) {
279 par_blur(buffer, radius, |i| Argb::from_u32_srgb(*i), Argb::to_u32_srgb);
280}
281
282#[cfg(any(doc, feature = "simd"))]
289pub fn simd_blur_argb<const LANES: usize>(buffer: &mut ImgRefMut<u32>, radius: usize) where LaneCount<LANES>: SupportedLaneCount {
290 simd_blur(buffer, radius,
291 |i: [&u32; LANES]| Argb::from_u32xN(i.map(u32::clone)), Argb::to_u32xN,
292 |i| Argb::from_u32(*i), Argb::to_u32
293 );
294}
295
296#[cfg(any(doc, all(feature = "simd", feature = "blend-srgb")))]
303pub fn simd_blur_srgb<const LANES: usize>(buffer: &mut ImgRefMut<u32>, radius: usize) where LaneCount<LANES>: SupportedLaneCount {
304 simd_blur(buffer, radius,
305 |i: [&u32; LANES]| Argb::from_u32xN_srgb(i.map(u32::clone)), Argb::to_u32xN_srgb,
306 |i| Argb::from_u32_srgb(*i), Argb::to_u32_srgb
307 );
308}
309
310#[cfg(any(doc, all(feature = "rayon", feature = "simd")))]
318pub fn par_simd_blur_argb<const LANES: usize>(buffer: &mut ImgRefMut<u32>, radius: usize) where LaneCount<LANES>: SupportedLaneCount {
319 par_simd_blur(buffer, radius,
320 |i: [&u32; LANES]| Argb::from_u32xN(i.map(u32::clone)), Argb::to_u32xN,
321 |i| Argb::from_u32(*i), Argb::to_u32
322 );
323}
324
325#[cfg(any(doc, all(feature = "rayon", feature = "simd", feature = "blend-srgb")))]
333pub fn par_simd_blur_srgb<const LANES: usize>(buffer: &mut ImgRefMut<u32>, radius: usize) where LaneCount<LANES>: SupportedLaneCount {
334 par_simd_blur(buffer, radius,
335 |i: [&u32; LANES]| Argb::from_u32xN_srgb(i.map(u32::clone)), Argb::to_u32xN_srgb,
336 |i| Argb::from_u32_srgb(*i), Argb::to_u32_srgb
337 );
338}