Skip to main content

rsvg/filters/
displacement_map.rs

1use cssparser::Parser;
2use markup5ever::{expanded_name, local_name, ns};
3
4use crate::document::AcquiredNodes;
5use crate::element::{ElementTrait, set_attribute};
6use crate::error::*;
7use crate::node::{CascadedValues, Node};
8use crate::parse_identifiers;
9use crate::parsers::{Parse, ParseValue};
10use crate::properties::ColorInterpolationFilters;
11use crate::rect::IRect;
12use crate::session::Session;
13use crate::surface_utils::{iterators::Pixels, shared_surface::ExclusiveImageSurface};
14use crate::xml::Attributes;
15
16use super::bounds::BoundsBuilder;
17use super::context::{FilterContext, FilterOutput};
18use super::{
19    FilterEffect, FilterError, FilterResolveError, Input, InputRequirements, Primitive,
20    PrimitiveParams, ResolvedPrimitive,
21};
22
23/// Enumeration of the color channels the displacement map can source.
24#[derive(Default, Clone, Copy)]
25enum ColorChannel {
26    R,
27    G,
28    B,
29    #[default]
30    A,
31}
32
33/// The `feDisplacementMap` filter primitive.
34#[derive(Default)]
35pub struct FeDisplacementMap {
36    base: Primitive,
37    params: DisplacementMap,
38}
39
40/// Resolved `feDisplacementMap` primitive for rendering.
41#[derive(Clone, Default)]
42pub struct DisplacementMap {
43    in1: Input,
44    in2: Input,
45    scale: f64,
46    x_channel_selector: ColorChannel,
47    y_channel_selector: ColorChannel,
48    color_interpolation_filters: ColorInterpolationFilters,
49}
50
51impl ElementTrait for FeDisplacementMap {
52    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
53        let (in1, in2) = self.base.parse_two_inputs(attrs, session);
54        self.params.in1 = in1;
55        self.params.in2 = in2;
56
57        for (attr, value) in attrs.iter() {
58            match attr.expanded() {
59                expanded_name!("", "scale") => {
60                    set_attribute(&mut self.params.scale, attr.parse(value), session)
61                }
62                expanded_name!("", "xChannelSelector") => {
63                    set_attribute(
64                        &mut self.params.x_channel_selector,
65                        attr.parse(value),
66                        session,
67                    );
68                }
69                expanded_name!("", "yChannelSelector") => {
70                    set_attribute(
71                        &mut self.params.y_channel_selector,
72                        attr.parse(value),
73                        session,
74                    );
75                }
76                _ => (),
77            }
78        }
79    }
80}
81
82impl DisplacementMap {
83    pub fn render(
84        &self,
85        bounds_builder: BoundsBuilder,
86        ctx: &FilterContext,
87    ) -> Result<FilterOutput, FilterError> {
88        // https://www.w3.org/TR/filter-effects/#feDisplacementMapElement
89        // "The color-interpolation-filters property only applies to
90        // the in2 source image and does not apply to the in source
91        // image. The in source image must remain in its current color
92        // space.
93
94        let input_1 = ctx.get_input(&self.in1, ColorInterpolationFilters::Auto)?;
95        let displacement_input = ctx.get_input(&self.in2, self.color_interpolation_filters)?;
96        let bounds: IRect = bounds_builder
97            .add_input(&input_1)
98            .add_input(&displacement_input)
99            .compute(ctx)
100            .clipped
101            .into();
102
103        // Displacement map's values need to be non-premultiplied.
104        let displacement_surface = displacement_input.surface().unpremultiply(bounds)?;
105
106        let (sx, sy) = ctx.paffine().transform_distance(self.scale, self.scale);
107
108        let mut surface = ExclusiveImageSurface::new(
109            ctx.source_graphic().width(),
110            ctx.source_graphic().height(),
111            input_1.surface().surface_type(),
112        )?;
113
114        surface.draw(&mut |cr| {
115            for (x, y, displacement_pixel) in Pixels::within(&displacement_surface, bounds) {
116                let get_value = |channel| match channel {
117                    ColorChannel::R => displacement_pixel.r,
118                    ColorChannel::G => displacement_pixel.g,
119                    ColorChannel::B => displacement_pixel.b,
120                    ColorChannel::A => displacement_pixel.a,
121                };
122
123                let process = |x| f64::from(x) / 255.0 - 0.5;
124
125                let dx = process(get_value(self.x_channel_selector));
126                let dy = process(get_value(self.y_channel_selector));
127
128                let x = f64::from(x);
129                let y = f64::from(y);
130                let ox = sx * dx;
131                let oy = sy * dy;
132
133                // Doing this in a loop doesn't look too bad performance wise, and allows not to
134                // manually implement bilinear or other interpolation.
135                cr.rectangle(x, y, 1.0, 1.0);
136                cr.reset_clip();
137                cr.clip();
138
139                input_1.surface().set_as_source_surface(&cr, -ox, -oy)?;
140                cr.paint()?;
141            }
142
143            Ok(())
144        })?;
145
146        Ok(FilterOutput {
147            surface: surface.share()?,
148            bounds,
149        })
150    }
151
152    pub fn get_input_requirements(&self) -> InputRequirements {
153        self.in1
154            .get_requirements()
155            .fold(self.in2.get_requirements())
156    }
157}
158
159impl FilterEffect for FeDisplacementMap {
160    fn resolve(
161        &self,
162        _acquired_nodes: &mut AcquiredNodes<'_>,
163        node: &Node,
164    ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> {
165        let cascaded = CascadedValues::new_from_node(node);
166        let values = cascaded.get();
167
168        let mut params = self.params.clone();
169        params.color_interpolation_filters = values.color_interpolation_filters();
170
171        Ok(vec![ResolvedPrimitive {
172            primitive: self.base.clone(),
173            params: PrimitiveParams::DisplacementMap(params),
174        }])
175    }
176}
177
178impl Parse for ColorChannel {
179    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
180        Ok(parse_identifiers!(
181            parser,
182            "R" => ColorChannel::R,
183            "G" => ColorChannel::G,
184            "B" => ColorChannel::B,
185            "A" => ColorChannel::A,
186        )?)
187    }
188}