1 | // Copyright 2020 the Resvg Authors |
2 | // SPDX-License-Identifier: Apache-2.0 OR MIT |
3 | |
4 | use super::{ImageRef, ImageRefMut}; |
5 | use 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. |
17 | pub 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 | } |
65 | |