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

            
3
use crate::document::AcquiredNodes;
4
use crate::drawing_ctx::DrawingCtx;
5
use crate::element::{set_attribute, ElementTrait};
6
use crate::node::Node;
7
use crate::parsers::ParseValue;
8
use crate::properties::ColorInterpolationFilters;
9
use crate::rect::IRect;
10
use crate::rsvg_log;
11
use crate::session::Session;
12
use crate::xml::Attributes;
13

            
14
use super::bounds::BoundsBuilder;
15
use super::context::{FilterContext, FilterOutput};
16
use super::{
17
    FilterEffect, FilterError, FilterResolveError, Input, Primitive, PrimitiveParams,
18
    ResolvedPrimitive,
19
};
20

            
21
/// The `feOffset` filter primitive.
22
22
#[derive(Default)]
23
pub struct FeOffset {
24
22
    base: Primitive,
25
22
    params: Offset,
26
}
27

            
28
/// Resolved `feOffset` primitive for rendering.
29
90
#[derive(Clone, Default)]
30
pub struct Offset {
31
45
    pub in1: Input,
32
45
    pub dx: f64,
33
45
    pub dy: f64,
34
}
35

            
36
impl ElementTrait for FeOffset {
37
22
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
38
22
        self.params.in1 = self.base.parse_one_input(attrs, session);
39

            
40
85
        for (attr, value) in attrs.iter() {
41
63
            match attr.expanded() {
42
                expanded_name!("", "dx") => {
43
20
                    set_attribute(&mut self.params.dx, attr.parse(value), session)
44
                }
45
                expanded_name!("", "dy") => {
46
21
                    set_attribute(&mut self.params.dy, attr.parse(value), session)
47
                }
48
                _ => (),
49
            }
50
63
        }
51
22
    }
52
}
53

            
54
impl Offset {
55
24
    pub fn render(
56
        &self,
57
        bounds_builder: BoundsBuilder,
58
        ctx: &FilterContext,
59
        acquired_nodes: &mut AcquiredNodes<'_>,
60
        draw_ctx: &mut DrawingCtx,
61
    ) -> Result<FilterOutput, FilterError> {
62
        // https://www.w3.org/TR/filter-effects/#ColorInterpolationFiltersProperty
63
        //
64
        // "Note: The color-interpolation-filters property just has an
65
        // effect on filter operations. Therefore, it has no effect on
66
        // filter primitives like feOffset"
67
        //
68
        // This is why we pass Auto here.
69
24
        let input_1 = ctx.get_input(
70
            acquired_nodes,
71
            draw_ctx,
72
            &self.in1,
73
24
            ColorInterpolationFilters::Auto,
74
        )?;
75
24
        let bounds = bounds_builder.add_input(&input_1).compute(ctx).clipped;
76

            
77
24
        rsvg_log!(draw_ctx.session(), "(feOffset bounds={:?}", bounds);
78

            
79
24
        let (dx, dy) = ctx.paffine().transform_distance(self.dx, self.dy);
80

            
81
24
        let surface = input_1.surface().offset(bounds, dx, dy)?;
82

            
83
24
        let ibounds: IRect = bounds.into();
84

            
85
24
        Ok(FilterOutput {
86
24
            surface,
87
            bounds: ibounds,
88
        })
89
24
    }
90
}
91

            
92
impl FilterEffect for FeOffset {
93
23
    fn resolve(
94
        &self,
95
        _acquired_nodes: &mut AcquiredNodes<'_>,
96
        _node: &Node,
97
    ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> {
98
46
        Ok(vec![ResolvedPrimitive {
99
23
            primitive: self.base.clone(),
100
23
            params: PrimitiveParams::Offset(self.params.clone()),
101
        }])
102
23
    }
103
}