[librsvg: 1/15] FilterContext: validate the transforms at creation time




commit 9ae570c8b6d28e4da30752a42dddac4b2c808847
Author: Federico Mena Quintero <federico gnome org>
Date:   Wed Apr 7 17:29:59 2021 -0500

    FilterContext: validate the transforms at creation time
    
    This will let us assume that the transforms are valid in the rest of
    the code.

 src/filters/context.rs |  16 ++++---
 src/filters/mod.rs     | 115 +++++++++++++++++++++++++------------------------
 2 files changed, 69 insertions(+), 62 deletions(-)
---
diff --git a/src/filters/context.rs b/src/filters/context.rs
index 54c38671..398e0565 100644
--- a/src/filters/context.rs
+++ b/src/filters/context.rs
@@ -107,11 +107,11 @@ impl FilterContext {
         computed_from_node_being_filtered: &ComputedValues,
         stroke_paint: UserSpacePaintSource,
         fill_paint: UserSpacePaintSource,
-        source_surface: SharedImageSurface,
+        source_surface: &SharedImageSurface,
         draw_ctx: &mut DrawingCtx,
         draw_transform: Transform,
         node_bbox: BoundingBox,
-    ) -> Self {
+    ) -> Result<Self, FilterError> {
         // The rect can be empty (for example, if the filter is applied to an empty group).
         // However, with userSpaceOnUse it's still possible to create images with a filter.
         let bbox_rect = node_bbox.rect.unwrap_or_default();
@@ -144,6 +144,12 @@ impl FilterContext {
             .post_transform(&draw_transform),
         };
 
+        if !(affine.is_invertible() && paffine.is_invertible()) {
+            return Err(FilterError::InvalidParameter(
+                "transform is not invertible".to_string(),
+            ));
+        }
+
         let effects_region = {
             let params = draw_ctx.push_coord_units(filter_units);
             let filter_rect = filter.get_rect(&computed_from_node_being_filtered, &params);
@@ -166,11 +172,11 @@ impl FilterContext {
             bbox.rect.unwrap()
         };
 
-        Self {
+        Ok(Self {
             computed_from_node_being_filtered: computed_from_node_being_filtered.clone(),
             stroke_paint,
             fill_paint,
-            source_surface,
+            source_surface: source_surface.clone(),
             last_result: None,
             previous_results: HashMap::new(),
             background_surface: OnceCell::new(),
@@ -180,7 +186,7 @@ impl FilterContext {
             effects_region,
             _affine: affine,
             paffine,
-        }
+        })
     }
 
     /// Returns the computed values from the node that referenced this filter.
diff --git a/src/filters/mod.rs b/src/filters/mod.rs
index ba8a6f5d..6efa7fc5 100644
--- a/src/filters/mod.rs
+++ b/src/filters/mod.rs
@@ -254,75 +254,76 @@ pub fn render(
         .resolve(acquired_nodes, values.fill_opacity().0, values.color().0)?
         .to_user_space(&node_bbox, draw_ctx, values);
 
-    let mut filter_ctx = FilterContext::new(
+    if let Ok(mut filter_ctx) = FilterContext::new(
         &filter,
         computed_from_node_being_filtered,
         stroke_paint_source,
         fill_paint_source,
-        source_surface,
+        &source_surface,
         draw_ctx,
         transform,
         node_bbox,
-    );
-
-    // If paffine is non-invertible, we won't draw anything. Also bbox combining in bounds
-    // computations will panic due to non-invertible martrix.
-    if !filter_ctx.paffine().is_invertible() {
-        return Ok(filter_ctx.into_output()?);
-    }
-
-    let primitives = filter_node
-        .children()
-        .filter(|c| c.is_element())
-        // Skip nodes in error.
-        .filter(|c| {
-            let in_error = c.borrow_element().is_in_error();
-
-            if in_error {
-                rsvg_log!("(ignoring filter primitive {} because it is in error)", c);
-            }
-
-            !in_error
-        })
-        // Keep only filter primitives (those that implement the Filter trait)
-        .filter(|c| c.borrow_element().as_filter_effect().is_some());
-
-    for c in primitives {
-        let elt = c.borrow_element();
-        let filter = elt.as_filter_effect().unwrap();
-
-        let start = Instant::now();
-
-        if let Err(err) = filter
-            .resolve(&c)
-            .and_then(|(primitive, params)| {
-                render_primitive(
-                    &primitive.resolve(),
-                    &params,
-                    &filter_ctx,
-                    acquired_nodes,
-                    draw_ctx,
-                )
+    ) {
+        let primitives = filter_node
+            .children()
+            .filter(|c| c.is_element())
+            // Skip nodes in error.
+            .filter(|c| {
+                let in_error = c.borrow_element().is_in_error();
+
+                if in_error {
+                    rsvg_log!("(ignoring filter primitive {} because it is in error)", c);
+                }
+
+                !in_error
             })
-            .and_then(|result| filter_ctx.store_result(result))
-        {
-            rsvg_log!("(filter primitive {} returned an error: {})", c, err);
-
-            // Exit early on Cairo errors. Continue rendering otherwise.
-            if let FilterError::CairoError(status) = err {
-                return Err(RenderingError::from(status));
+            // Keep only filter primitives (those that implement the Filter trait)
+            .filter(|c| c.borrow_element().as_filter_effect().is_some());
+
+        for c in primitives {
+            let elt = c.borrow_element();
+            let filter = elt.as_filter_effect().unwrap();
+
+            let start = Instant::now();
+
+            if let Err(err) = filter
+                .resolve(&c)
+                .and_then(|(primitive, params)| {
+                    render_primitive(
+                        &primitive.resolve(),
+                        &params,
+                        &filter_ctx,
+                        acquired_nodes,
+                        draw_ctx,
+                    )
+                })
+                .and_then(|result| filter_ctx.store_result(result))
+            {
+                rsvg_log!("(filter primitive {} returned an error: {})", c, err);
+
+                // Exit early on Cairo errors. Continue rendering otherwise.
+                if let FilterError::CairoError(status) = err {
+                    return Err(RenderingError::from(status));
+                }
             }
+
+            let elapsed = start.elapsed();
+            rsvg_log!(
+                "(rendered filter primitive {} in\n    {} seconds)",
+                c,
+                elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9
+            );
         }
 
-        let elapsed = start.elapsed();
-        rsvg_log!(
-            "(rendered filter primitive {} in\n    {} seconds)",
-            c,
-            elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9
-        );
+        Ok(filter_ctx.into_output()?)
+    } else {
+        // Ignore errors that happened when creating the FilterContext
+        Ok(SharedImageSurface::empty(
+            source_surface.width(),
+            source_surface.height(),
+            SurfaceType::AlphaOnly,
+        )?)
     }
-
-    Ok(filter_ctx.into_output()?)
 }
 
 #[rustfmt::skip]


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]