1
//! Entry point for the CSS filters infrastructure.
2

            
3
use cssparser::{BasicParseError, Parser};
4
use markup5ever::{expanded_name, local_name, namespace_url, ns};
5
use std::rc::Rc;
6
use std::time::Instant;
7

            
8
use crate::bbox::BoundingBox;
9
use crate::document::AcquiredNodes;
10
use crate::drawing_ctx::DrawingCtx;
11
use crate::element::{set_attribute, ElementTrait};
12
use crate::error::{InternalRenderingError, ParseError};
13
use crate::filter::UserSpaceFilter;
14
use crate::length::*;
15
use crate::node::Node;
16
use crate::paint_server::UserSpacePaintSource;
17
use crate::parse_identifiers;
18
use crate::parsers::{CustomIdent, Parse, ParseValue};
19
use crate::properties::ColorInterpolationFilters;
20
use crate::rsvg_log;
21
use crate::session::Session;
22
use crate::surface_utils::{
23
    shared_surface::{SharedImageSurface, SurfaceType},
24
    EdgeMode,
25
};
26
use crate::transform::Transform;
27
use crate::xml::Attributes;
28

            
29
mod bounds;
30
use self::bounds::BoundsBuilder;
31

            
32
pub mod context;
33
use self::context::{FilterContext, FilterOutput, FilterResult};
34

            
35
mod error;
36
use self::error::FilterError;
37
pub use self::error::FilterResolveError;
38

            
39
/// A filter primitive interface.
40
pub trait FilterEffect: ElementTrait {
41
    fn resolve(
42
        &self,
43
        acquired_nodes: &mut AcquiredNodes<'_>,
44
        node: &Node,
45
    ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError>;
46
}
47

            
48
pub mod blend;
49
pub mod color_matrix;
50
pub mod component_transfer;
51
pub mod composite;
52
pub mod convolve_matrix;
53
pub mod displacement_map;
54
pub mod drop_shadow;
55
pub mod flood;
56
pub mod gaussian_blur;
57
pub mod image;
58
pub mod lighting;
59
pub mod merge;
60
pub mod morphology;
61
pub mod offset;
62
pub mod tile;
63
pub mod turbulence;
64

            
65
pub struct FilterSpec {
66
    pub name: String,
67
    pub user_space_filter: UserSpaceFilter,
68
    pub primitives: Vec<UserSpacePrimitive>,
69
}
70

            
71
/// Resolved parameters for each filter primitive.
72
///
73
/// These gather all the data that a primitive may need during rendering:
74
/// the `feFoo` element's attributes, any computed values from its properties,
75
/// and parameters extracted from the element's children (for example,
76
/// `feMerge` gathers info from its `feMergNode` children).
77
pub enum PrimitiveParams {
78
    Blend(blend::Blend),
79
    ColorMatrix(color_matrix::ColorMatrix),
80
    ComponentTransfer(component_transfer::ComponentTransfer),
81
    Composite(composite::Composite),
82
    ConvolveMatrix(convolve_matrix::ConvolveMatrix),
83
    DiffuseLighting(lighting::DiffuseLighting),
84
    DisplacementMap(displacement_map::DisplacementMap),
85
    Flood(flood::Flood),
86
    GaussianBlur(gaussian_blur::GaussianBlur),
87
    Image(image::Image),
88
    Merge(merge::Merge),
89
    Morphology(morphology::Morphology),
90
    Offset(offset::Offset),
91
    SpecularLighting(lighting::SpecularLighting),
92
    Tile(tile::Tile),
93
    Turbulence(turbulence::Turbulence),
94
}
95

            
96
impl PrimitiveParams {
97
    /// Returns a human-readable name for a primitive.
98
    #[rustfmt::skip]
99
    fn name(&self) -> &'static str {
100
        use PrimitiveParams::*;
101
        match self {
102
            Blend(..)             => "feBlend",
103
            ColorMatrix(..)       => "feColorMatrix",
104
            ComponentTransfer(..) => "feComponentTransfer",
105
            Composite(..)         => "feComposite",
106
            ConvolveMatrix(..)    => "feConvolveMatrix",
107
            DiffuseLighting(..)   => "feDiffuseLighting",
108
            DisplacementMap(..)   => "feDisplacementMap",
109
            Flood(..)             => "feFlood",
110
            GaussianBlur(..)      => "feGaussianBlur",
111
            Image(..)             => "feImage",
112
            Merge(..)             => "feMerge",
113
            Morphology(..)        => "feMorphology",
114
            Offset(..)            => "feOffset",
115
            SpecularLighting(..)  => "feSpecularLighting",
116
            Tile(..)              => "feTile",
117
            Turbulence(..)        => "feTurbulence",
118
        }
119
    }
120
}
121

            
122
/// The base filter primitive node containing common properties.
123
1652
#[derive(Default, Clone)]
124
pub struct Primitive {
125
826
    pub x: Option<Length<Horizontal>>,
126
826
    pub y: Option<Length<Vertical>>,
127
826
    pub width: Option<ULength<Horizontal>>,
128
826
    pub height: Option<ULength<Vertical>>,
129
826
    pub result: Option<CustomIdent>,
130
}
131

            
132
pub struct ResolvedPrimitive {
133
    pub primitive: Primitive,
134
    pub params: PrimitiveParams,
135
}
136

            
137
/// A fully resolved filter primitive in user-space coordinates.
138
pub struct UserSpacePrimitive {
139
    x: Option<f64>,
140
    y: Option<f64>,
141
    width: Option<f64>,
142
    height: Option<f64>,
143
    result: Option<CustomIdent>,
144

            
145
    params: PrimitiveParams,
146
}
147

            
148
/// An enumeration of possible inputs for a filter primitive.
149
1079
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
150
pub enum Input {
151
    #[default]
152
357
    Unspecified,
153
    SourceGraphic,
154
    SourceAlpha,
155
    BackgroundImage,
156
    BackgroundAlpha,
157
    FillPaint,
158
    StrokePaint,
159
91
    FilterOutput(CustomIdent),
160
}
161

            
162
impl Parse for Input {
163
218
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
164
218
        parser
165
218
            .try_parse(|p| {
166
                parse_identifiers!(
167
                    p,
168
                    "SourceGraphic" => Input::SourceGraphic,
169
                    "SourceAlpha" => Input::SourceAlpha,
170
                    "BackgroundImage" => Input::BackgroundImage,
171
                    "BackgroundAlpha" => Input::BackgroundAlpha,
172
                    "FillPaint" => Input::FillPaint,
173
                    "StrokePaint" => Input::StrokePaint,
174
                )
175
218
            })
176
82
            .or_else(|_: BasicParseError<'_>| {
177
82
                let ident = CustomIdent::parse(parser)?;
178
82
                Ok(Input::FilterOutput(ident))
179
82
            })
180
218
    }
181
}
182

            
183
impl ResolvedPrimitive {
184
432
    pub fn into_user_space(self, params: &NormalizeParams) -> UserSpacePrimitive {
185
453
        let x = self.primitive.x.map(|l| l.to_user(params));
186
455
        let y = self.primitive.y.map(|l| l.to_user(params));
187
454
        let width = self.primitive.width.map(|l| l.to_user(params));
188
453
        let height = self.primitive.height.map(|l| l.to_user(params));
189

            
190
432
        UserSpacePrimitive {
191
            x,
192
            y,
193
            width,
194
            height,
195
432
            result: self.primitive.result,
196
432
            params: self.params,
197
        }
198
432
    }
199
}
200

            
201
impl UserSpacePrimitive {
202
    /// Validates attributes and returns the `BoundsBuilder` for bounds computation.
203
    #[inline]
204
424
    fn get_bounds(&self, ctx: &FilterContext) -> BoundsBuilder {
205
424
        BoundsBuilder::new(self.x, self.y, self.width, self.height, ctx.paffine())
206
424
    }
207
}
208

            
209
impl Primitive {
210
390
    fn parse_standard_attributes(
211
        &mut self,
212
        attrs: &Attributes,
213
        session: &Session,
214
    ) -> (Input, Input) {
215
390
        let mut input_1 = Input::Unspecified;
216
390
        let mut input_2 = Input::Unspecified;
217

            
218
390
        for (attr, value) in attrs.iter() {
219
1094
            match attr.expanded() {
220
21
                expanded_name!("", "x") => set_attribute(&mut self.x, attr.parse(value), session),
221
23
                expanded_name!("", "y") => set_attribute(&mut self.y, attr.parse(value), session),
222
                expanded_name!("", "width") => {
223
22
                    set_attribute(&mut self.width, attr.parse(value), session)
224
                }
225
                expanded_name!("", "height") => {
226
21
                    set_attribute(&mut self.height, attr.parse(value), session)
227
                }
228
                expanded_name!("", "result") => {
229
90
                    set_attribute(&mut self.result, attr.parse(value), session)
230
                }
231
371
                expanded_name!("", "in") => set_attribute(&mut input_1, attr.parse(value), session),
232
                expanded_name!("", "in2") => {
233
279
                    set_attribute(&mut input_2, attr.parse(value), session)
234
234
                }
235
                _ => (),
236
82
            }
237
1094
        }
238

            
239
390
        (input_1, input_2)
240
390
    }
241

            
242
143
    pub fn parse_no_inputs(&mut self, attrs: &Attributes, session: &Session) {
243
143
        let (_, _) = self.parse_standard_attributes(attrs, session);
244
143
    }
245

            
246
202
    pub fn parse_one_input(&mut self, attrs: &Attributes, session: &Session) -> Input {
247
202
        let (input_1, _) = self.parse_standard_attributes(attrs, session);
248
202
        input_1
249
202
    }
250

            
251
45
    pub fn parse_two_inputs(&mut self, attrs: &Attributes, session: &Session) -> (Input, Input) {
252
45
        self.parse_standard_attributes(attrs, session)
253
45
    }
254
}
255

            
256
/// Applies a filter and returns the resulting surface.
257
301
pub fn render(
258
    filter: &FilterSpec,
259
    stroke_paint_source: Rc<UserSpacePaintSource>,
260
    fill_paint_source: Rc<UserSpacePaintSource>,
261
    source_surface: SharedImageSurface,
262
    acquired_nodes: &mut AcquiredNodes<'_>,
263
    draw_ctx: &mut DrawingCtx,
264
    transform: Transform,
265
    node_bbox: &BoundingBox,
266
) -> Result<SharedImageSurface, InternalRenderingError> {
267
301
    let session = draw_ctx.session().clone();
268

            
269
301
    FilterContext::new(
270
301
        &filter.user_space_filter,
271
301
        stroke_paint_source,
272
301
        fill_paint_source,
273
        &source_surface,
274
        transform,
275
301
        *node_bbox,
276
    )
277
596
    .and_then(|mut filter_ctx| {
278
        // the message has an unclosed parenthesis; we'll close it below.
279
        rsvg_log!(
280
295
            session,
281
            "(filter \"{}\" with effects_region={:?}",
282
            filter.name,
283
            filter_ctx.effects_region()
284
        );
285
718
        for user_space_primitive in &filter.primitives {
286
424
            let start = Instant::now();
287

            
288
424
            match render_primitive(user_space_primitive, &filter_ctx, acquired_nodes, draw_ctx) {
289
423
                Ok(output) => {
290
423
                    let elapsed = start.elapsed();
291
                    rsvg_log!(
292
423
                        session,
293
                        "(rendered filter primitive {} in {} seconds)",
294
                        user_space_primitive.params.name(),
295
                        elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9
296
                    );
297

            
298
423
                    filter_ctx.store_result(FilterResult {
299
423
                        name: user_space_primitive.result.clone(),
300
423
                        output,
301
                    });
302
423
                }
303

            
304
1
                Err(err) => {
305
                    rsvg_log!(
306
1
                        session,
307
                        "(filter primitive {} returned an error: {})",
308
                        user_space_primitive.params.name(),
309
                        err
310
                    );
311

            
312
                    // close the opening parenthesis from the message at the start of this function
313
1
                    rsvg_log!(session, ")");
314

            
315
                    // Exit early on Cairo errors. Continue rendering otherwise.
316
1
                    if let FilterError::CairoError(status) = err {
317
4
                        return Err(FilterError::CairoError(status));
318
                    }
319
1
                }
320
            }
321
        }
322

            
323
        // close the opening parenthesis from the message at the start of this function
324
295
        rsvg_log!(session, ")");
325

            
326
295
        Ok(filter_ctx.into_output()?)
327
297
    })
328
6
    .or_else(|err| match err {
329
        FilterError::CairoError(status) => {
330
            // Exit early on Cairo errors
331
            Err(InternalRenderingError::from(status))
332
        }
333

            
334
        _ => {
335
            // ignore other filter errors and just return an empty surface
336
6
            Ok(SharedImageSurface::empty(
337
6
                source_surface.width(),
338
6
                source_surface.height(),
339
6
                SurfaceType::AlphaOnly,
340
            )?)
341
6
        }
342
6
    })
343
301
}
344

            
345
#[rustfmt::skip]
346
424
fn render_primitive(
347
    primitive: &UserSpacePrimitive,
348
    ctx: &FilterContext,
349
    acquired_nodes: &mut AcquiredNodes<'_>,
350
    draw_ctx: &mut DrawingCtx,
351
) -> Result<FilterOutput, FilterError> {
352
    use PrimitiveParams::*;
353

            
354
424
    let bounds_builder = primitive.get_bounds(ctx);
355

            
356
    // Note that feDropShadow is not handled here.  When its FilterElement::resolve() is called,
357
    // it returns a series of lower-level primitives (flood, blur, offset, etc.) that make up
358
    // the drop-shadow effect.
359

            
360
424
    match primitive.params {
361
7
        Blend(ref p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
362
16
        ColorMatrix(ref p)       => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
363
18
        ComponentTransfer(ref p) => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
364
40
        Composite(ref p)         => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
365
32
        ConvolveMatrix(ref p)    => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
366
48
        DiffuseLighting(ref p)   => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
367
2
        DisplacementMap(ref p)   => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
368
34
        Flood(ref p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
369
46
        GaussianBlur(ref p)      => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
370
68
        Image(ref p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
371
23
        Merge(ref p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
372
5
        Morphology(ref p)        => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
373
24
        Offset(ref p)            => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
374
39
        SpecularLighting(ref p)  => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
375
2
        Tile(ref p)              => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
376
20
        Turbulence(ref p)        => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
377
    }
378
424
}
379

            
380
impl From<ColorInterpolationFilters> for SurfaceType {
381
107
    fn from(c: ColorInterpolationFilters) -> Self {
382
107
        match c {
383
104
            ColorInterpolationFilters::LinearRgb => SurfaceType::LinearRgb,
384
3
            _ => SurfaceType::SRgb,
385
        }
386
107
    }
387
}
388

            
389
impl Parse for EdgeMode {
390
16
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
391
16
        Ok(parse_identifiers!(
392
            parser,
393
            "duplicate" => EdgeMode::Duplicate,
394
            "wrap" => EdgeMode::Wrap,
395
            "none" => EdgeMode::None,
396
        )?)
397
16
    }
398
}