1
use crate::document::AcquiredNodes;
2
use crate::drawing_ctx::DrawingCtx;
3
use crate::element::ElementTrait;
4
use crate::node::Node;
5
use crate::properties::ColorInterpolationFilters;
6
use crate::rect::IRect;
7
use crate::rsvg_log;
8
use crate::session::Session;
9
use crate::xml::Attributes;
10

            
11
use super::bounds::BoundsBuilder;
12
use super::context::{FilterContext, FilterInput, FilterOutput};
13
use super::{
14
    FilterEffect, FilterError, FilterResolveError, Input, Primitive, PrimitiveParams,
15
    ResolvedPrimitive,
16
};
17

            
18
/// The `feTile` filter primitive.
19
2
#[derive(Default)]
20
pub struct FeTile {
21
2
    base: Primitive,
22
2
    params: Tile,
23
}
24

            
25
/// Resolved `feTile` primitive for rendering.
26
8
#[derive(Clone, Default)]
27
pub struct Tile {
28
4
    in1: Input,
29
}
30

            
31
impl ElementTrait for FeTile {
32
2
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
33
2
        self.params.in1 = self.base.parse_one_input(attrs, session);
34
2
    }
35
}
36

            
37
impl Tile {
38
4
    pub fn render(
39
        &self,
40
        bounds_builder: BoundsBuilder,
41
        ctx: &FilterContext,
42
        acquired_nodes: &mut AcquiredNodes<'_>,
43
        draw_ctx: &mut DrawingCtx,
44
    ) -> Result<FilterOutput, FilterError> {
45
        // https://www.w3.org/TR/filter-effects/#ColorInterpolationFiltersProperty
46
        //
47
        // "Note: The color-interpolation-filters property just has an
48
        // effect on filter operations. Therefore, it has no effect on
49
        // filter primitives like [...], feTile"
50
        //
51
        // This is why we pass Auto here.
52
8
        let input_1 = ctx.get_input(
53
            acquired_nodes,
54
            draw_ctx,
55
            &self.in1,
56
4
            ColorInterpolationFilters::Auto,
57
        )?;
58

            
59
        // feTile doesn't consider its inputs in the filter primitive subregion calculation.
60
4
        let bounds: IRect = bounds_builder.compute(ctx).clipped.into();
61

            
62
2
        let surface = match input_1 {
63
            FilterInput::StandardInput(input_surface) => input_surface,
64
            FilterInput::PrimitiveOutput(FilterOutput {
65
2
                surface: input_surface,
66
2
                bounds: input_bounds,
67
            }) => {
68
3
                if input_bounds.is_empty() {
69
1
                    rsvg_log!(
70
1
                        draw_ctx.session(),
71
                        "(feTile with empty input_bounds; returning just the input surface)"
72
                    );
73

            
74
1
                    input_surface
75
                } else {
76
1
                    rsvg_log!(
77
1
                        draw_ctx.session(),
78
                        "(feTile bounds={:?}, input_bounds={:?})",
79
                        bounds,
80
                        input_bounds
81
                    );
82

            
83
1
                    let tile_surface = input_surface.tile(input_bounds)?;
84

            
85
1
                    ctx.source_graphic().paint_image_tiled(
86
1
                        bounds,
87
                        &tile_surface,
88
1
                        input_bounds.x0,
89
1
                        input_bounds.y0,
90
                    )?
91
1
                }
92
2
            }
93
        };
94

            
95
2
        Ok(FilterOutput { surface, bounds })
96
2
    }
97
}
98

            
99
impl FilterEffect for FeTile {
100
2
    fn resolve(
101
        &self,
102
        _acquired_nodes: &mut AcquiredNodes<'_>,
103
        _node: &Node,
104
    ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> {
105
4
        Ok(vec![ResolvedPrimitive {
106
2
            primitive: self.base.clone(),
107
2
            params: PrimitiveParams::Tile(self.params.clone()),
108
        }])
109
2
    }
110
}