1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5use super::ImageRefMut;
6use rgb::RGBA8;
7use usvg::filter::MorphologyOperator;
8
9/// Applies a morphology filter.
10///
11/// `src` pixels should have a **premultiplied alpha**.
12///
13/// # Allocations
14///
15/// This method will allocate a copy of the `src` image as a back buffer.
16pub fn apply(operator: MorphologyOperator, rx: f32, ry: f32, src: ImageRefMut) {
17 // No point in making matrix larger than image.
18 let columns = std::cmp::min(rx.ceil() as u32 * 2, src.width);
19 let rows = std::cmp::min(ry.ceil() as u32 * 2, src.height);
20 let target_x = (columns as f32 / 2.0).floor() as u32;
21 let target_y = (rows as f32 / 2.0).floor() as u32;
22
23 let width_max = src.width as i32 - 1;
24 let height_max = src.height as i32 - 1;
25
26 let mut buf = vec![RGBA8::default(); src.data.len()];
27 let mut buf = ImageRefMut::new(src.width, src.height, &mut buf);
28 let mut x = 0;
29 let mut y = 0;
30 for _ in src.data.iter() {
31 let mut new_p = RGBA8::default();
32 if operator == MorphologyOperator::Erode {
33 new_p.r = 255;
34 new_p.g = 255;
35 new_p.b = 255;
36 new_p.a = 255;
37 }
38
39 for oy in 0..rows {
40 for ox in 0..columns {
41 let tx = x as i32 - target_x as i32 + ox as i32;
42 let ty = y as i32 - target_y as i32 + oy as i32;
43
44 if tx < 0 || tx > width_max || ty < 0 || ty > height_max {
45 continue;
46 }
47
48 let p = src.pixel_at(tx as u32, ty as u32);
49 if operator == MorphologyOperator::Erode {
50 new_p.r = std::cmp::min(p.r, new_p.r);
51 new_p.g = std::cmp::min(p.g, new_p.g);
52 new_p.b = std::cmp::min(p.b, new_p.b);
53 new_p.a = std::cmp::min(p.a, new_p.a);
54 } else {
55 new_p.r = std::cmp::max(p.r, new_p.r);
56 new_p.g = std::cmp::max(p.g, new_p.g);
57 new_p.b = std::cmp::max(p.b, new_p.b);
58 new_p.a = std::cmp::max(p.a, new_p.a);
59 }
60 }
61 }
62
63 *buf.pixel_at_mut(x, y) = new_p;
64
65 x += 1;
66 if x == src.width {
67 x = 0;
68 y += 1;
69 }
70 }
71
72 // Do not use `mem::swap` because `data` referenced via FFI.
73 src.data.copy_from_slice(buf.data);
74}
75