1 
2 const KERNEL: [f32; 9] = [
3     0.095332, 0.118095, 0.095332,
4     0.118095, 0.146293, 0.118095,
5     0.095332, 0.118095, 0.095332,
6 ];
7 
8 #[cfg(target_os = "macos")]
9 mod mac {
10     use imgref::*;
11     use crate::ffi::vImage_Buffer;
12     use crate::ffi::vImagePixelCount;
13     use crate::ffi::vImageConvolve_PlanarF;
14     use crate::ffi::vImage_Flags::kvImageEdgeExtend;
15     use super::KERNEL;
16 
blur(src: ImgRef<'_, f32>, tmp: &mut [f32], mut dst: ImgRefMut<'_, f32>)17     pub fn blur(src: ImgRef<'_, f32>, tmp: &mut [f32], mut dst: ImgRefMut<'_, f32>) {
18         let srcbuf = vImage_Buffer {
19             width: src.width() as vImagePixelCount,
20             height: src.height() as vImagePixelCount,
21             rowBytes: src.stride() * std::mem::size_of::<f32>(),
22             data: src.buf().as_ptr(),
23         };
24         let mut dstbuf = vImage_Buffer {
25             width: dst.width() as vImagePixelCount,
26             height: dst.height() as vImagePixelCount,
27             rowBytes: dst.stride() * std::mem::size_of::<f32>(),
28             data: dst.buf_mut().as_mut_ptr(),
29         };
30 
31         do_blur(&srcbuf, tmp, &mut dstbuf, src.width(), src.height());
32     }
33 
blur_in_place(mut srcdst: ImgRefMut<'_, f32>, tmp: &mut [f32])34     pub fn blur_in_place(mut srcdst: ImgRefMut<'_, f32>, tmp: &mut [f32]) {
35         let srcbuf = vImage_Buffer {
36             width: srcdst.width() as vImagePixelCount,
37             height: srcdst.height() as vImagePixelCount,
38             rowBytes: srcdst.stride() * std::mem::size_of::<f32>(),
39             data: srcdst.buf().as_ptr(),
40         };
41         let mut dstbuf = vImage_Buffer {
42             width: srcdst.width() as vImagePixelCount,
43             height: srcdst.height() as vImagePixelCount,
44             rowBytes: srcdst.stride() * std::mem::size_of::<f32>(),
45             data: srcdst.buf_mut().as_mut_ptr(),
46         };
47 
48         do_blur(&srcbuf, tmp, &mut dstbuf, srcdst.width(), srcdst.height());
49     }
50 
do_blur(srcbuf: &vImage_Buffer<*const f32>, tmp: &mut [f32], dstbuf: &mut vImage_Buffer<*mut f32>, width: usize, height: usize)51     pub fn do_blur(srcbuf: &vImage_Buffer<*const f32>, tmp: &mut [f32], dstbuf: &mut vImage_Buffer<*mut f32>, width: usize, height: usize) {
52         assert_eq!(tmp.len(), width * height);
53 
54         unsafe {
55             let mut tmpwrbuf = vImage_Buffer {
56                 width: width as vImagePixelCount,
57                 height: height as vImagePixelCount,
58                 rowBytes: width * std::mem::size_of::<f32>(),
59                 data: tmp.as_mut_ptr(),
60             };
61             let res = vImageConvolve_PlanarF(srcbuf, &mut tmpwrbuf, std::ptr::null_mut(), 0, 0, KERNEL.as_ptr(), 3, 3, 0., kvImageEdgeExtend);
62             assert_eq!(0, res);
63 
64             let tmprbuf = vImage_Buffer {
65                 width: width as vImagePixelCount,
66                 height: height as vImagePixelCount,
67                 rowBytes: width * std::mem::size_of::<f32>(),
68                 data: tmp.as_ptr(),
69             };
70             let res = vImageConvolve_PlanarF(&tmprbuf, dstbuf, std::ptr::null_mut(), 0, 0, KERNEL.as_ptr(), 3, 3, 0., kvImageEdgeExtend);
71             assert_eq!(0, res);
72         }
73     }
74 }
75 
76 #[cfg(not(target_os = "macos"))]
77 mod portable {
78     use imgref::*;
79 
80     use super::KERNEL;
81     use std::cmp::min;
82 
83     #[inline]
do3f(prev: &[f32], curr: &[f32], next: &[f32], i: usize) -> f3284     fn do3f(prev: &[f32], curr: &[f32], next: &[f32], i: usize) -> f32 {
85         debug_assert!(i > 0);
86 
87         let c0 = i-1;
88         let c1 = i;
89         let c2 = i+1;
90 
91         unsafe {
92             prev.get_unchecked(c0)*KERNEL[0] + prev.get_unchecked(c1)*KERNEL[1] + prev.get_unchecked(c2)*KERNEL[2] +
93             curr.get_unchecked(c0)*KERNEL[3] + curr.get_unchecked(c1)*KERNEL[4] + curr.get_unchecked(c2)*KERNEL[5] +
94             next.get_unchecked(c0)*KERNEL[6] + next.get_unchecked(c1)*KERNEL[7] + next.get_unchecked(c2)*KERNEL[8]
95         }
96     }
97 
do3(prev: &[f32], curr: &[f32], next: &[f32], i: usize, width: usize) -> f3298     fn do3(prev: &[f32], curr: &[f32], next: &[f32], i: usize, width: usize) -> f32 {
99         let c0 = if i > 0 {i-1} else {0};
100         let c1 = i;
101         let c2 = min(i+1, width-1);
102 
103         prev[c0]*KERNEL[0] + prev[c1]*KERNEL[1] + prev[c2]*KERNEL[2] +
104         curr[c0]*KERNEL[3] + curr[c1]*KERNEL[4] + curr[c2]*KERNEL[5] +
105         next[c0]*KERNEL[6] + next[c1]*KERNEL[7] + next[c2]*KERNEL[8]
106     }
107 
blur(src: ImgRef<f32>, tmp: &mut [f32], dst: ImgRefMut<f32>)108     pub fn blur(src: ImgRef<f32>, tmp: &mut [f32], dst: ImgRefMut<f32>) {
109         {
110             let tmp_dst = ImgRefMut::new(tmp, dst.width(), dst.height());
111             do_blur(src, tmp_dst);
112         }
113         let tmp_src = ImgRef::new(tmp, src.width(), src.height());
114         do_blur(tmp_src, dst);
115     }
116 
do_blur(src: ImgRef<f32>, mut dst: ImgRefMut<f32>)117     pub fn do_blur(src: ImgRef<f32>, mut dst: ImgRefMut<f32>) {
118         assert_eq!(src.width(), dst.width());
119         assert_eq!(src.height(), dst.height());
120         assert!(src.width() > 0);
121         assert!(src.width() < 1<<24);
122         assert!(src.height() > 0);
123         assert!(src.height() < 1<<24);
124         debug_assert!(src.pixels().all(|p| p.is_finite()));
125 
126         let width = src.width();
127         let height = src.height();
128         let src_stride = src.stride();
129         let dst_stride = dst.stride();
130         let src = src.buf();
131         let dst = dst.buf_mut();
132 
133         let mut prev = &src[0..width];
134         let mut curr = prev;
135         let mut next = prev;
136         for y in 0..height {
137             prev = curr;
138             curr = next;
139             let next_start = (y+1)*src_stride;
140             next = if y+1 < height {&src[next_start..next_start+width]} else {curr};
141 
142             let dstrow = &mut dst[y*dst_stride..y*dst_stride+width];
143 
144             dstrow[0] = do3(prev, curr, next, 0, width);
145             for i in 1..width-1 {
146                 dstrow[i] = do3f(prev, curr, next, i);
147             }
148             if width > 1 {
149                 dstrow[width-1] = do3(prev, curr, next, width-1, width);
150             }
151         }
152     }
153 
blur_in_place(srcdst: ImgRefMut<f32>, tmp: &mut [f32])154     pub fn blur_in_place(srcdst: ImgRefMut<f32>, tmp: &mut [f32]) {
155         {
156             let tmp_dst = ImgRefMut::new(tmp, srcdst.width(), srcdst.height());
157             do_blur(srcdst.as_ref(), tmp_dst);
158         }
159         let tmp_src = ImgRef::new(tmp, srcdst.width(), srcdst.height());
160         do_blur(tmp_src, srcdst);
161     }
162 }
163 
164 
165 #[cfg(target_os = "macos")]
166 pub use self::mac::*;
167 
168 #[cfg(not(target_os = "macos"))]
169 pub use self::portable::*;
170 
171 #[cfg(test)]
172 use imgref::*;
173 
174 #[test]
blur_zero()175 fn blur_zero() {
176     let src = vec![0.25];
177 
178     let mut tmp = vec![-55.; 1];
179     let mut dst = vec![-99.; 1];
180 
181     let mut src2 = src.clone();
182 
183     blur(ImgRef::new(&src[..], 1,1), &mut tmp[..], ImgRefMut::new(&mut dst[..], 1, 1));
184     blur_in_place(ImgRefMut::new(&mut src2[..], 1, 1), &mut tmp[..]);
185 
186     assert_eq!(src2, dst);
187     assert!((0.25 - dst[0]).abs() < 0.00001);
188 }
189 
190 #[test]
blur_one()191 fn blur_one() {
192     blur_one_compare(Img::new(vec![
193         0.,0.,0.,0.,0.,
194         0.,0.,0.,0.,0.,
195         0.,0.,1.,0.,0.,
196         0.,0.,0.,0.,0.,
197         0.,0.,0.,0.,0.,
198     ], 5, 5));
199 }
200 
201 #[test]
blur_one_stride()202 fn blur_one_stride() {
203     let nan = 1./0.;
204     blur_one_compare(Img::new_stride(vec![
205         0.,0.,0.,0.,0., nan, -11.,
206         0.,0.,0.,0.,0., 333., nan,
207         0.,0.,1.,0.,0., nan, -11.,
208         0.,0.,0.,0.,0., 333., nan,
209         0.,0.,0.,0.,0., nan,
210     ], 5, 5, 7));
211 }
212 
213 #[cfg(test)]
blur_one_compare(src: ImgVec<f32>)214 fn blur_one_compare(src: ImgVec<f32>) {
215     let mut tmp = vec![-55.; 5*5];
216     let mut dst = vec![999.; 5*5];
217 
218     let mut src2 = src.clone();
219 
220     blur(src.as_ref(), &mut tmp[..], ImgRefMut::new(&mut dst[..], 5, 5));
221     blur_in_place(src2.as_mut(), &mut tmp[..]);
222 
223     assert_eq!(src2.pixels().collect::<Vec<_>>(), dst);
224 
225     assert!((1./110. - dst[0]).abs() < 0.0001, "{:?}", dst);
226     assert!((1./110. - dst[5*5-1]).abs() < 0.0001, "{:?}", dst);
227     assert!((0.11354011 - dst[2*5+2]).abs() < 0.0001);
228 }
229 
230 #[test]
blur_1x1()231 fn blur_1x1() {
232     let src = vec![1.];
233     let mut src2 = src.clone();
234 
235     let mut tmp = vec![-999.; 1];
236     let mut dst = vec![55.; 1];
237 
238     blur(ImgRef::new(&src[..], 1,1), &mut tmp[..], ImgRefMut::new(&mut dst[..], 1, 1));
239     blur_in_place(ImgRefMut::new(&mut src2[..], 1,1), &mut tmp[..]);
240 
241     assert!((dst[0] - 1.).abs() < 0.00001);
242     assert!((src2[0] - 1.).abs() < 0.00001);
243 }
244 
245 #[test]
blur_two()246 fn blur_two() {
247     let src = vec![
248     0.,1.,1.,1.,
249     1.,1.,1.,1.,
250     1.,1.,1.,1.,
251     1.,1.,1.,1.,
252     ];
253     let mut tmp = vec![-55.; 4*4];
254     let mut dst = vec![999.; 4*4];
255 
256     let mut src2 = src.clone();
257 
258     blur(ImgRef::new(&src[..], 4,4), &mut tmp[..], ImgRefMut::new(&mut dst[..], 4, 4));
259     blur_in_place(ImgRefMut::new(&mut src2[..], 4,4), &mut tmp[..]);
260 
261     assert_eq!(src2, dst);
262 
263     let z00 = 0.*KERNEL[0] + 0.*KERNEL[1] + 1.*KERNEL[2] +
264               0.*KERNEL[3] + 0.*KERNEL[4] + 1.*KERNEL[5] +
265               1.*KERNEL[6] + 1.*KERNEL[7] + 1.*KERNEL[8];
266     let z01 =                                   0.*KERNEL[0] + 1.*KERNEL[1] + 1.*KERNEL[2] +
267                                                 0.*KERNEL[3] + 1.*KERNEL[4] + 1.*KERNEL[5] +
268                                                 1.*KERNEL[6] + 1.*KERNEL[7] + 1.*KERNEL[8];
269 
270     let z10 = 0.*KERNEL[0] + 0.*KERNEL[1] + 1.*KERNEL[2] +
271               1.*KERNEL[3] + 1.*KERNEL[4] + 1.*KERNEL[5] +
272               1.*KERNEL[6] + 1.*KERNEL[7] + 1.*KERNEL[8];
273     let z11 =                                   0.*KERNEL[0] + 1.*KERNEL[1] + 1.*KERNEL[2] +
274                                                 1.*KERNEL[3] + 1.*KERNEL[4] + 1.*KERNEL[5] +
275                                                 1.*KERNEL[6] + 1.*KERNEL[7] + 1.*KERNEL[8];
276     let exp = z00*KERNEL[0] + z00*KERNEL[1] + z01*KERNEL[2] +
277               z00*KERNEL[3] + z00*KERNEL[4] + z01*KERNEL[5] +
278               z10*KERNEL[6] + z10*KERNEL[7] + z11*KERNEL[8];
279 
280     assert!((1. - dst[3]).abs() < 0.0001, "{}", dst[3]);
281     assert!((1. - dst[3 * 4]).abs() < 0.0001, "{}", dst[3 * 4]);
282     assert!((1. - dst[4 * 4 - 1]).abs() < 0.0001, "{}", dst[4 * 4 - 1]);
283     assert_eq!(exp, dst[0]);
284 }
285