1
use cssparser::Parser;
2
use markup5ever::{expanded_name, local_name, namespace_url, ns};
3

            
4
use crate::document::AcquiredNodes;
5
use crate::drawing_ctx::DrawingCtx;
6
use crate::element::{set_attribute, ElementTrait};
7
use crate::error::*;
8
use crate::node::{CascadedValues, Node};
9
use crate::parse_identifiers;
10
use crate::parsers::{Parse, ParseValue};
11
use crate::properties::ColorInterpolationFilters;
12
use crate::rect::IRect;
13
use crate::session::Session;
14
use crate::surface_utils::{iterators::Pixels, shared_surface::ExclusiveImageSurface};
15
use crate::xml::Attributes;
16

            
17
use super::bounds::BoundsBuilder;
18
use super::context::{FilterContext, FilterOutput};
19
use super::{
20
    FilterEffect, FilterError, FilterResolveError, Input, Primitive, PrimitiveParams,
21
    ResolvedPrimitive,
22
};
23

            
24
/// Enumeration of the color channels the displacement map can source.
25
12
#[derive(Default, Clone, Copy)]
26
enum ColorChannel {
27
    R,
28
    G,
29
    B,
30
    #[default]
31
4
    A,
32
}
33

            
34
/// The `feDisplacementMap` filter primitive.
35
2
#[derive(Default)]
36
pub struct FeDisplacementMap {
37
2
    base: Primitive,
38
2
    params: DisplacementMap,
39
}
40

            
41
/// Resolved `feDisplacementMap` primitive for rendering.
42
8
#[derive(Clone, Default)]
43
pub struct DisplacementMap {
44
4
    in1: Input,
45
4
    in2: Input,
46
4
    scale: f64,
47
4
    x_channel_selector: ColorChannel,
48
4
    y_channel_selector: ColorChannel,
49
4
    color_interpolation_filters: ColorInterpolationFilters,
50
}
51

            
52
impl ElementTrait for FeDisplacementMap {
53
2
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
54
2
        let (in1, in2) = self.base.parse_two_inputs(attrs, session);
55
2
        self.params.in1 = in1;
56
2
        self.params.in2 = in2;
57

            
58
2
        for (attr, value) in attrs.iter() {
59
9
            match attr.expanded() {
60
                expanded_name!("", "scale") => {
61
2
                    set_attribute(&mut self.params.scale, attr.parse(value), session)
62
                }
63
                expanded_name!("", "xChannelSelector") => {
64
1
                    set_attribute(
65
1
                        &mut self.params.x_channel_selector,
66
1
                        attr.parse(value),
67
                        session,
68
                    );
69
                }
70
                expanded_name!("", "yChannelSelector") => {
71
2
                    set_attribute(
72
2
                        &mut self.params.y_channel_selector,
73
2
                        attr.parse(value),
74
                        session,
75
                    );
76
                }
77
                _ => (),
78
            }
79
9
        }
80
2
    }
81
}
82

            
83
impl DisplacementMap {
84
2
    pub fn render(
85
        &self,
86
        bounds_builder: BoundsBuilder,
87
        ctx: &FilterContext,
88
        acquired_nodes: &mut AcquiredNodes<'_>,
89
        draw_ctx: &mut DrawingCtx,
90
    ) -> Result<FilterOutput, FilterError> {
91
        // https://www.w3.org/TR/filter-effects/#feDisplacementMapElement
92
        // "The color-interpolation-filters property only applies to
93
        // the in2 source image and does not apply to the in source
94
        // image. The in source image must remain in its current color
95
        // space.
96

            
97
4
        let input_1 = ctx.get_input(
98
            acquired_nodes,
99
            draw_ctx,
100
            &self.in1,
101
2
            ColorInterpolationFilters::Auto,
102
        )?;
103
2
        let displacement_input = ctx.get_input(
104
            acquired_nodes,
105
            draw_ctx,
106
2
            &self.in2,
107
2
            self.color_interpolation_filters,
108
        )?;
109
2
        let bounds: IRect = bounds_builder
110
            .add_input(&input_1)
111
            .add_input(&displacement_input)
112
            .compute(ctx)
113
            .clipped
114
            .into();
115

            
116
        // Displacement map's values need to be non-premultiplied.
117
2
        let displacement_surface = displacement_input.surface().unpremultiply(bounds)?;
118

            
119
2
        let (sx, sy) = ctx.paffine().transform_distance(self.scale, self.scale);
120

            
121
2
        let mut surface = ExclusiveImageSurface::new(
122
2
            ctx.source_graphic().width(),
123
2
            ctx.source_graphic().height(),
124
2
            input_1.surface().surface_type(),
125
        )?;
126

            
127
6
        surface.draw(&mut |cr| {
128
109562
            for (x, y, displacement_pixel) in Pixels::within(&displacement_surface, bounds) {
129
328680
                let get_value = |channel| match channel {
130
43680
                    ColorChannel::R => displacement_pixel.r,
131
43680
                    ColorChannel::G => displacement_pixel.g,
132
                    ColorChannel::B => displacement_pixel.b,
133
131760
                    ColorChannel::A => displacement_pixel.a,
134
219120
                };
135

            
136
219120
                let process = |x| f64::from(x) / 255.0 - 0.5;
137

            
138
109560
                let dx = process(get_value(self.x_channel_selector));
139
109560
                let dy = process(get_value(self.y_channel_selector));
140

            
141
109560
                let x = f64::from(x);
142
109560
                let y = f64::from(y);
143
109560
                let ox = sx * dx;
144
109560
                let oy = sy * dy;
145

            
146
                // Doing this in a loop doesn't look too bad performance wise, and allows not to
147
                // manually implement bilinear or other interpolation.
148
109560
                cr.rectangle(x, y, 1.0, 1.0);
149
109560
                cr.reset_clip();
150
109560
                cr.clip();
151

            
152
109560
                input_1.surface().set_as_source_surface(&cr, -ox, -oy)?;
153
109562
                cr.paint()?;
154
            }
155

            
156
2
            Ok(())
157
2
        })?;
158

            
159
2
        Ok(FilterOutput {
160
2
            surface: surface.share()?,
161
2
            bounds,
162
        })
163
2
    }
164
}
165

            
166
impl FilterEffect for FeDisplacementMap {
167
2
    fn resolve(
168
        &self,
169
        _acquired_nodes: &mut AcquiredNodes<'_>,
170
        node: &Node,
171
    ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> {
172
2
        let cascaded = CascadedValues::new_from_node(node);
173
2
        let values = cascaded.get();
174

            
175
2
        let mut params = self.params.clone();
176
2
        params.color_interpolation_filters = values.color_interpolation_filters();
177

            
178
2
        Ok(vec![ResolvedPrimitive {
179
2
            primitive: self.base.clone(),
180
2
            params: PrimitiveParams::DisplacementMap(params),
181
        }])
182
2
    }
183
}
184

            
185
impl Parse for ColorChannel {
186
3
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
187
6
        Ok(parse_identifiers!(
188
            parser,
189
            "R" => ColorChannel::R,
190
            "G" => ColorChannel::G,
191
            "B" => ColorChannel::B,
192
            "A" => ColorChannel::A,
193
        )?)
194
3
    }
195
}