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