[librsvg: 8/9] feImage: detect circular references now that primitives are resolved early




commit 098d3e84457e072a636caec5fdf049811aaa9655
Author: Federico Mena Quintero <federico gnome org>
Date:   Wed Apr 21 19:29:29 2021 -0500

    feImage: detect circular references now that primitives are resolved early
    
    This introduces a `Source` enum, where the interesting cases are these
    for feImage:
    
        enum Source {
            Node(Node),
            ExternalImage(String),
        }
    
    In the `Node` case, we later use that node for
    `acquired_nodes.acquire_ref()` while rendering the feImage.  This
    makes it possible to catch circular references when there is a a
    feImage that references a node, that references a filter, that
    references the same node.

 src/filters/image.rs | 73 +++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 55 insertions(+), 18 deletions(-)
---
diff --git a/src/filters/image.rs b/src/filters/image.rs
index 9c677e24..c9ffac3c 100644
--- a/src/filters/image.rs
+++ b/src/filters/image.rs
@@ -26,18 +26,31 @@ pub struct FeImage {
     params: ImageParams,
 }
 
-/// Resolved `feImage` primitive for rendering.
 #[derive(Clone, Default)]
 struct ImageParams {
     aspect: AspectRatio,
     href: Option<String>,
 }
 
+/// Resolved `feImage` primitive for rendering.
 pub struct Image {
-    params: ImageParams,
+    aspect: AspectRatio,
+    source: Source,
     feimage_values: ComputedValues,
 }
 
+/// What a feImage references for rendering.
+enum Source {
+    /// Nothing is referenced; ignore the filter.
+    None,
+
+    /// Reference to a node.
+    Node(Node),
+
+    /// Reference to an external image.  This is just a URL.
+    ExternalImage(String),
+}
+
 impl Image {
     /// Renders the filter if the source is an existing node.
     fn render_node(
@@ -46,13 +59,8 @@ impl Image {
         acquired_nodes: &mut AcquiredNodes<'_>,
         draw_ctx: &mut DrawingCtx,
         bounds: Rect,
-        node_id: &NodeId,
+        referenced_node: &Node,
     ) -> Result<SharedImageSurface, FilterError> {
-        let acquired = acquired_nodes
-            .acquire(node_id)
-            .map_err(|_| FilterError::InvalidInput)?;
-        let referenced_node = acquired.get();
-
         // https://www.w3.org/TR/filter-effects/#feImageElement
         //
         // The filters spec says, "... otherwise [rendering a referenced object], the
@@ -89,7 +97,7 @@ impl Image {
             .lookup_image(url)
             .map_err(|_| FilterError::InvalidInput)?;
 
-        let rect = self.params.aspect.compute(
+        let rect = self.aspect.compute(
             &ViewBox::from(Rect::from_size(
                 f64::from(image.width()),
                 f64::from(image.height()),
@@ -138,14 +146,27 @@ impl Image {
     ) -> Result<FilterOutput, FilterError> {
         let bounds = bounds_builder.compute(ctx);
 
-        let href = self.params.href.as_ref().ok_or(FilterError::InvalidInput)?;
+        let surface = match &self.source {
+            Source::None => return Err(FilterError::InvalidInput),
+
+            Source::Node(node) => {
+                if let Ok(acquired) = acquired_nodes.acquire_ref(node) {
+                    self.render_node(
+                        ctx,
+                        acquired_nodes,
+                        draw_ctx,
+                        bounds.clipped,
+                        &acquired.get(),
+                    )?
+                } else {
+                    return Err(FilterError::InvalidInput);
+                }
+            }
 
-        let surface = if let Ok(node_id) = NodeId::parse(href) {
-            // if href has a fragment specified, render as a node
-            self.render_node(ctx, acquired_nodes, draw_ctx, bounds.clipped, &node_id)
-        } else {
-            self.render_external_image(ctx, acquired_nodes, draw_ctx, &bounds, href)
-        }?;
+            Source::ExternalImage(ref href) => {
+                self.render_external_image(ctx, acquired_nodes, draw_ctx, &bounds, href)?
+            }
+        };
 
         Ok(FilterOutput {
             surface,
@@ -157,16 +178,32 @@ impl Image {
 impl FilterEffect for FeImage {
     fn resolve(
         &self,
-        _acquired_nodes: &mut AcquiredNodes<'_>,
+        acquired_nodes: &mut AcquiredNodes<'_>,
         node: &Node,
     ) -> Result<ResolvedPrimitive, FilterResolveError> {
         let cascaded = CascadedValues::new_from_node(node);
         let feimage_values = cascaded.get().clone();
 
+        let source = match self.params.href {
+            None => Source::None,
+
+            Some(ref s) => {
+                if let Ok(node_id) = NodeId::parse(s) {
+                    acquired_nodes
+                        .acquire(&node_id)
+                        .map(|acquired| Source::Node(acquired.get().clone()))
+                        .unwrap_or(Source::None)
+                } else {
+                    Source::ExternalImage(s.to_string())
+                }
+            }
+        };
+
         Ok(ResolvedPrimitive {
             primitive: self.base.clone(),
             params: PrimitiveParams::Image(Image {
-                params: self.params.clone(),
+                aspect: self.params.aspect,
+                source,
                 feimage_values,
             }),
         })


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