resvg/filter/
morphology.rs

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