resvg/filter/
displacement_map.rs

1// Copyright 2020 the Resvg Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use super::{ImageRef, ImageRefMut};
5use usvg::filter::{ColorChannel, DisplacementMap};
6
7/// Applies a displacement map.
8///
9/// - `map` pixels should have a **unpremultiplied alpha**.
10/// - `src` pixels can have any alpha method.
11///
12/// `sx` and `sy` indicate canvas scale.
13///
14/// # Panics
15///
16/// When `src`, `map` and `dest` have different sizes.
17pub fn apply(
18    fe: &DisplacementMap,
19    sx: f32,
20    sy: f32,
21    src: ImageRef,
22    map: ImageRef,
23    dest: ImageRefMut,
24) {
25    assert!(src.width == map.width && src.width == dest.width);
26    assert!(src.height == map.height && src.height == dest.height);
27
28    let w = src.width as i32;
29    let h = src.height as i32;
30
31    let mut x: u32 = 0;
32    let mut y: u32 = 0;
33    for pixel in map.data.iter() {
34        let calc_offset = |channel| {
35            let c = match channel {
36                ColorChannel::B => pixel.b,
37                ColorChannel::G => pixel.g,
38                ColorChannel::R => pixel.r,
39                ColorChannel::A => pixel.a,
40            };
41
42            c as f32 / 255.0 - 0.5
43        };
44
45        let dx = calc_offset(fe.x_channel_selector());
46        let dy = calc_offset(fe.y_channel_selector());
47        let ox = (x as f32 + dx * sx * fe.scale()).round() as i32;
48        let oy = (y as f32 + dy * sy * fe.scale()).round() as i32;
49
50        // TODO: we should use some kind of anti-aliasing when offset is on a pixel border
51
52        if x < w as u32 && y < h as u32 && ox >= 0 && ox < w && oy >= 0 && oy < h {
53            let idx = (oy * w + ox) as usize;
54            let idx1 = (y * w as u32 + x) as usize;
55            dest.data[idx1] = src.data[idx];
56        }
57
58        x += 1;
59        if x == src.width {
60            x = 0;
61            y += 1;
62        }
63    }
64}