1
use std::cell::OnceCell;
2
use std::collections::HashMap;
3
use std::rc::Rc;
4

            
5
use crate::bbox::BoundingBox;
6
use crate::coord_units::CoordUnits;
7
use crate::document::AcquiredNodes;
8
use crate::drawing_ctx::DrawingCtx;
9
use crate::filter::UserSpaceFilter;
10
use crate::paint_server::UserSpacePaintSource;
11
use crate::parsers::CustomIdent;
12
use crate::properties::ColorInterpolationFilters;
13
use crate::rect::{IRect, Rect};
14
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
15
use crate::transform::Transform;
16

            
17
use super::error::FilterError;
18
use super::Input;
19

            
20
/// A filter primitive output.
21
272
#[derive(Debug, Clone)]
22
pub struct FilterOutput {
23
    /// The surface after the filter primitive was applied.
24
272
    pub surface: SharedImageSurface,
25

            
26
    /// The filter primitive subregion.
27
272
    pub bounds: IRect,
28
}
29

            
30
/// A filter primitive result.
31
#[derive(Debug, Clone)]
32
pub struct FilterResult {
33
    /// The name of this result: the value of the `result` attribute.
34
    pub name: Option<CustomIdent>,
35

            
36
    /// The output.
37
    pub output: FilterOutput,
38
}
39

            
40
/// An input to a filter primitive.
41
#[derive(Debug, Clone)]
42
pub enum FilterInput {
43
    /// One of the standard inputs.
44
36
    StandardInput(SharedImageSurface),
45
    /// Output of another filter primitive.
46
    PrimitiveOutput(FilterOutput),
47
}
48

            
49
/// The filter rendering context.
50
pub struct FilterContext {
51
    /// Paint source for primitives which have an input value equal to `StrokePaint`.
52
    stroke_paint: Rc<UserSpacePaintSource>,
53
    /// Paint source for primitives which have an input value equal to `FillPaint`.
54
    fill_paint: Rc<UserSpacePaintSource>,
55

            
56
    /// The source graphic surface.
57
    source_surface: SharedImageSurface,
58
    /// Output of the last filter primitive.
59
    last_result: Option<FilterOutput>,
60
    /// Surfaces of the previous filter primitives by name.
61
    previous_results: HashMap<CustomIdent, FilterOutput>,
62

            
63
    /// Input surface for primitives that require an input of `BackgroundImage` or `BackgroundAlpha`. Computed lazily.
64
    background_surface: OnceCell<Result<SharedImageSurface, FilterError>>,
65

            
66
    // Input surface for primitives that require an input of `StrokePaint`, Computed lazily.
67
    stroke_paint_surface: OnceCell<Result<SharedImageSurface, FilterError>>,
68

            
69
    // Input surface for primitives that require an input of `FillPaint`, Computed lazily.
70
    fill_paint_surface: OnceCell<Result<SharedImageSurface, FilterError>>,
71

            
72
    /// Primtive units
73
    primitive_units: CoordUnits,
74
    /// The filter effects region.
75
    effects_region: Rect,
76

            
77
    /// The filter element affine matrix.
78
    ///
79
    /// If `filterUnits == userSpaceOnUse`, equal to the drawing context matrix, so, for example,
80
    /// if the target node is in a group with `transform="translate(30, 20)"`, this will be equal
81
    /// to a matrix that translates to 30, 20 (and does not scale). Note that the target node
82
    /// bounding box isn't included in the computations in this case.
83
    ///
84
    /// If `filterUnits == objectBoundingBox`, equal to the target node bounding box matrix
85
    /// multiplied by the drawing context matrix, so, for example, if the target node is in a group
86
    /// with `transform="translate(30, 20)"` and also has `x="1", y="1", width="50", height="50"`,
87
    /// this will be equal to a matrix that translates to 31, 21 and scales to 50, 50.
88
    ///
89
    /// This is to be used in conjunction with setting the viewbox size to account for the scaling.
90
    /// For `filterUnits == userSpaceOnUse`, the viewbox will have the actual resolution size, and
91
    /// for `filterUnits == objectBoundingBox`, the viewbox will have the size of 1, 1.
92
    _affine: Transform,
93

            
94
    /// The filter primitive affine matrix.
95
    ///
96
    /// See the comments for `_affine`, they largely apply here.
97
    paffine: Transform,
98
}
99

            
100
impl FilterContext {
101
    /// Creates a new `FilterContext`.
102
299
    pub fn new(
103
        filter: &UserSpaceFilter,
104
        stroke_paint: Rc<UserSpacePaintSource>,
105
        fill_paint: Rc<UserSpacePaintSource>,
106
        source_surface: &SharedImageSurface,
107
        draw_transform: Transform,
108
        node_bbox: BoundingBox,
109
    ) -> Result<Self, FilterError> {
110
        // The rect can be empty (for example, if the filter is applied to an empty group).
111
        // However, with userSpaceOnUse it's still possible to create images with a filter.
112
299
        let bbox_rect = node_bbox.rect.unwrap_or_default();
113

            
114
299
        let affine = match filter.filter_units {
115
11
            CoordUnits::UserSpaceOnUse => draw_transform,
116
288
            CoordUnits::ObjectBoundingBox => Transform::new_unchecked(
117
288
                bbox_rect.width(),
118
                0.0,
119
                0.0,
120
288
                bbox_rect.height(),
121
288
                bbox_rect.x0,
122
288
                bbox_rect.y0,
123
            )
124
            .post_transform(&draw_transform),
125
        };
126

            
127
299
        let paffine = match filter.primitive_units {
128
274
            CoordUnits::UserSpaceOnUse => draw_transform,
129
25
            CoordUnits::ObjectBoundingBox => Transform::new_unchecked(
130
25
                bbox_rect.width(),
131
                0.0,
132
                0.0,
133
25
                bbox_rect.height(),
134
25
                bbox_rect.x0,
135
25
                bbox_rect.y0,
136
            )
137
            .post_transform(&draw_transform),
138
        };
139

            
140
299
        if !(affine.is_invertible() && paffine.is_invertible()) {
141
6
            return Err(FilterError::InvalidParameter(
142
6
                "transform is not invertible".to_string(),
143
            ));
144
        }
145

            
146
        let effects_region = {
147
293
            let mut bbox = BoundingBox::new();
148
293
            let other_bbox = BoundingBox::new()
149
293
                .with_transform(affine)
150
293
                .with_rect(filter.rect);
151

            
152
            // At this point all of the previous viewbox and matrix business gets converted to pixel
153
            // coordinates in the final surface, because bbox is created with an identity transform.
154
293
            bbox.insert(&other_bbox);
155

            
156
            // Finally, clip to the width and height of our surface.
157
293
            let (width, height) = (source_surface.width(), source_surface.height());
158
293
            let rect = Rect::from_size(f64::from(width), f64::from(height));
159
293
            let other_bbox = BoundingBox::new().with_rect(rect);
160
293
            bbox.clip(&other_bbox);
161

            
162
293
            bbox.rect.unwrap()
163
        };
164

            
165
293
        Ok(Self {
166
293
            stroke_paint,
167
293
            fill_paint,
168
293
            source_surface: source_surface.clone(),
169
293
            last_result: None,
170
293
            previous_results: HashMap::new(),
171
293
            background_surface: OnceCell::new(),
172
293
            stroke_paint_surface: OnceCell::new(),
173
293
            fill_paint_surface: OnceCell::new(),
174
293
            primitive_units: filter.primitive_units,
175
            effects_region,
176
293
            _affine: affine,
177
293
            paffine,
178
        })
179
299
    }
180

            
181
    /// Returns the surface corresponding to the source graphic.
182
    #[inline]
183
510
    pub fn source_graphic(&self) -> &SharedImageSurface {
184
        &self.source_surface
185
510
    }
186

            
187
    /// Returns the surface corresponding to the background image snapshot.
188
10
    fn background_image(&self, draw_ctx: &DrawingCtx) -> Result<SharedImageSurface, FilterError> {
189
20
        let res = self.background_surface.get_or_init(|| {
190
20
            draw_ctx
191
10
                .get_snapshot(self.source_surface.width(), self.source_surface.height())
192
                .map_err(FilterError::Rendering)
193
10
        });
194

            
195
20
        res.as_ref().map(|s| s.clone()).map_err(|e| e.clone())
196
10
    }
197

            
198
    /// Returns a surface filled with the current stroke's paint, for `StrokePaint` inputs in primitives.
199
    ///
200
    /// Filter Effects 1: <https://www.w3.org/TR/filter-effects/#attr-valuedef-in-strokepaint>
201
6
    fn stroke_paint_image(
202
        &self,
203
        acquired_nodes: &mut AcquiredNodes<'_>,
204
        draw_ctx: &mut DrawingCtx,
205
    ) -> Result<SharedImageSurface, FilterError> {
206
12
        let res = self.stroke_paint_surface.get_or_init(|| {
207
12
            Ok(draw_ctx.get_paint_source_surface(
208
6
                self.source_surface.width(),
209
6
                self.source_surface.height(),
210
6
                acquired_nodes,
211
6
                &self.stroke_paint,
212
            )?)
213
6
        });
214

            
215
12
        res.as_ref().map(|s| s.clone()).map_err(|e| e.clone())
216
6
    }
217

            
218
    /// Returns a surface filled with the current fill's paint, for `FillPaint` inputs in primitives.
219
    ///
220
    /// Filter Effects 1: <https://www.w3.org/TR/filter-effects/#attr-valuedef-in-fillpaint>
221
8
    fn fill_paint_image(
222
        &self,
223
        acquired_nodes: &mut AcquiredNodes<'_>,
224
        draw_ctx: &mut DrawingCtx,
225
    ) -> Result<SharedImageSurface, FilterError> {
226
15
        let res = self.fill_paint_surface.get_or_init(|| {
227
14
            Ok(draw_ctx.get_paint_source_surface(
228
7
                self.source_surface.width(),
229
7
                self.source_surface.height(),
230
7
                acquired_nodes,
231
7
                &self.fill_paint,
232
            )?)
233
7
        });
234

            
235
16
        res.as_ref().map(|s| s.clone()).map_err(|e| e.clone())
236
8
    }
237

            
238
    /// Converts this `FilterContext` into the surface corresponding to the output of the filter
239
    /// chain.
240
    ///
241
    /// The returned surface is in the sRGB color space.
242
    // TODO: sRGB conversion should probably be done by the caller.
243
    #[inline]
244
293
    pub fn into_output(self) -> Result<SharedImageSurface, cairo::Error> {
245
293
        match self.last_result {
246
292
            Some(FilterOutput { surface, bounds }) => surface.to_srgb(bounds),
247
1
            None => SharedImageSurface::empty(
248
1
                self.source_surface.width(),
249
1
                self.source_surface.height(),
250
1
                SurfaceType::AlphaOnly,
251
            ),
252
        }
253
293
    }
254

            
255
    /// Stores a filter primitive result into the context.
256
    #[inline]
257
421
    pub fn store_result(&mut self, result: FilterResult) {
258
421
        if let Some(name) = result.name {
259
101
            self.previous_results.insert(name, result.output.clone());
260
        }
261

            
262
421
        self.last_result = Some(result.output);
263
421
    }
264

            
265
    /// Returns the paffine matrix.
266
    #[inline]
267
633
    pub fn paffine(&self) -> Transform {
268
633
        self.paffine
269
633
    }
270

            
271
    /// Returns the primitive units.
272
    #[inline]
273
    pub fn primitive_units(&self) -> CoordUnits {
274
        self.primitive_units
275
    }
276

            
277
    /// Returns the filter effects region.
278
    #[inline]
279
690
    pub fn effects_region(&self) -> Rect {
280
690
        self.effects_region
281
690
    }
282

            
283
    /// Get a filter primitive's default input as if its `in=\"...\"` were not specified.
284
    ///
285
    /// Per <https://drafts.fxtf.org/filter-effects/#element-attrdef-filter-primitive-in>,
286
    /// "References to non-existent results will be treated as if no result was
287
    /// specified".  That is, fall back to the last result in the filter chain, or if this
288
    /// is the first in the chain, just use SourceGraphic.
289
134
    fn get_unspecified_input(&self) -> FilterInput {
290
134
        if let Some(output) = self.last_result.as_ref() {
291
41
            FilterInput::PrimitiveOutput(output.clone())
292
        } else {
293
93
            FilterInput::StandardInput(self.source_graphic().clone())
294
        }
295
134
    }
296

            
297
    /// Retrieves the filter input surface according to the SVG rules.
298
424
    fn get_input_raw(
299
        &self,
300
        acquired_nodes: &mut AcquiredNodes<'_>,
301
        draw_ctx: &mut DrawingCtx,
302
        in_: &Input,
303
    ) -> Result<FilterInput, FilterError> {
304
424
        match *in_ {
305
134
            Input::Unspecified => Ok(self.get_unspecified_input()),
306

            
307
124
            Input::SourceGraphic => Ok(FilterInput::StandardInput(self.source_graphic().clone())),
308

            
309
24
            Input::SourceAlpha => self
310
                .source_graphic()
311
12
                .extract_alpha(self.effects_region().into())
312
                .map_err(FilterError::CairoError)
313
                .map(FilterInput::StandardInput),
314

            
315
7
            Input::BackgroundImage => self
316
                .background_image(draw_ctx)
317
                .map(FilterInput::StandardInput),
318

            
319
6
            Input::BackgroundAlpha => self
320
                .background_image(draw_ctx)
321
6
                .and_then(|surface| {
322
3
                    surface
323
3
                        .extract_alpha(self.effects_region().into())
324
                        .map_err(FilterError::CairoError)
325
3
                })
326
                .map(FilterInput::StandardInput),
327

            
328
8
            Input::FillPaint => self
329
                .fill_paint_image(acquired_nodes, draw_ctx)
330
                .map(FilterInput::StandardInput),
331

            
332
6
            Input::StrokePaint => self
333
                .stroke_paint_image(acquired_nodes, draw_ctx)
334
                .map(FilterInput::StandardInput),
335

            
336
130
            Input::FilterOutput(ref name) => {
337
130
                let input = match self.previous_results.get(name).cloned() {
338
130
                    Some(filter_output) => {
339
                        // Happy path: we found a previous primitive's named output, so pass it on.
340
130
                        FilterInput::PrimitiveOutput(filter_output)
341
130
                    }
342

            
343
                    None => {
344
                        // Fallback path: we didn't find a primitive's output by the
345
                        // specified name, so fall back to using an unspecified output.
346
                        // Per the spec, "References to non-existent results will be
347
                        // treated as if no result was specified." -
348
                        // https://drafts.fxtf.org/filter-effects/#element-attrdef-filter-primitive-in
349
                        self.get_unspecified_input()
350
                    }
351
                };
352

            
353
130
                Ok(input)
354
130
            }
355
        }
356
424
    }
357

            
358
    /// Retrieves the filter input surface according to the SVG rules.
359
    ///
360
    /// The surface will be converted to the color space specified by `color_interpolation_filters`.
361
424
    pub fn get_input(
362
        &self,
363
        acquired_nodes: &mut AcquiredNodes<'_>,
364
        draw_ctx: &mut DrawingCtx,
365
        in_: &Input,
366
        color_interpolation_filters: ColorInterpolationFilters,
367
    ) -> Result<FilterInput, FilterError> {
368
424
        let raw = self.get_input_raw(acquired_nodes, draw_ctx, in_)?;
369

            
370
        // Convert the input surface to the desired format.
371
848
        let (surface, bounds) = match raw {
372
253
            FilterInput::StandardInput(ref surface) => (surface, self.effects_region().into()),
373
            FilterInput::PrimitiveOutput(FilterOutput {
374
171
                ref surface,
375
171
                ref bounds,
376
171
            }) => (surface, *bounds),
377
        };
378

            
379
424
        let surface = match color_interpolation_filters {
380
33
            ColorInterpolationFilters::Auto => Ok(surface.clone()),
381
373
            ColorInterpolationFilters::LinearRgb => surface.to_linear_rgb(bounds),
382
18
            ColorInterpolationFilters::Srgb => surface.to_srgb(bounds),
383
        };
384

            
385
424
        surface
386
            .map_err(FilterError::CairoError)
387
848
            .map(|surface| match raw {
388
253
                FilterInput::StandardInput(_) => FilterInput::StandardInput(surface),
389
171
                FilterInput::PrimitiveOutput(ref output) => {
390
171
                    FilterInput::PrimitiveOutput(FilterOutput { surface, ..*output })
391
171
                }
392
424
            })
393
424
    }
394
}
395

            
396
impl FilterInput {
397
    /// Retrieves the surface from `FilterInput`.
398
    #[inline]
399
185057
    pub fn surface(&self) -> &SharedImageSurface {
400
370114
        match *self {
401
184924
            FilterInput::StandardInput(ref surface) => surface,
402
133
            FilterInput::PrimitiveOutput(FilterOutput { ref surface, .. }) => surface,
403
        }
404
185057
    }
405
}