[librsvg: 6/9] Have FilterValue resolve itself to a FilterSpec




commit 5f4287a1e7049890a8e39eeba9f145621aa6e8c5
Author: Federico Mena Quintero <federico gnome org>
Date:   Wed Apr 21 18:38:47 2021 -0500

    Have FilterValue resolve itself to a FilterSpec
    
    This lets us rearrange a lot of code, and hopefully make DrawingCtx
    simpler.
    
    One test fails, render-crash/recursive-feimage.svg.  This will be
    fixed in the next commit.

 src/drawing_ctx.rs   | 100 +++++++++---------------------------------
 src/filter.rs        |  68 ++++++++++++++++++++++++++++-
 src/filters/error.rs |   5 +++
 src/filters/mod.rs   | 120 ++++++++++++++++++++++++---------------------------
 4 files changed, 149 insertions(+), 144 deletions(-)
---
diff --git a/src/drawing_ctx.rs b/src/drawing_ctx.rs
index bed6aafc..2bafca39 100644
--- a/src/drawing_ctx.rs
+++ b/src/drawing_ctx.rs
@@ -17,8 +17,7 @@ use crate::document::{AcquiredNodes, NodeId};
 use crate::dpi::Dpi;
 use crate::element::Element;
 use crate::error::{AcquireError, ImplementationLimit, RenderingError};
-use crate::filter::FilterValue;
-use crate::filters;
+use crate::filters::{self, FilterSpec};
 use crate::float_eq_cairo::ApproxEqCairo;
 use crate::gradient::{GradientVariant, SpreadMethod, UserSpaceGradient};
 use crate::marker;
@@ -702,10 +701,7 @@ impl DrawingCtx {
                 // Set temporary surface as source
 
                 saved_cr.draw_ctx.cr.set_matrix(affines.compositing.into());
-                saved_cr
-                    .draw_ctx
-                    .cr
-                    .set_source_surface(&source_surface, 0.0, 0.0);
+                source_surface.set_as_source_surface(&saved_cr.draw_ctx.cr, 0.0, 0.0);
 
                 // Clip
 
@@ -894,7 +890,7 @@ impl DrawingCtx {
         node_name: &str,
         values: &ComputedValues,
         node_bbox: BoundingBox,
-    ) -> Result<cairo::Surface, RenderingError> {
+    ) -> Result<SharedImageSurface, RenderingError> {
         let stroke_paint_source = Rc::new(
             values
                 .stroke()
@@ -915,89 +911,35 @@ impl DrawingCtx {
             Filter::None => surface_to_filter,
             Filter::List(filter_list) => {
                 if filter_list.is_applicable(&node_name, acquired_nodes) {
-                    filter_list.iter().try_fold(
-                        surface_to_filter,
-                        |surface, filter| -> Result<_, RenderingError> {
-                            let FilterValue::Url(f) = filter;
-                            self.run_filter(
-                                acquired_nodes,
-                                &f,
+                    if let Ok(specs) = filter_list
+                        .iter()
+                        .map(|filter_value| {
+                            filter_value.to_filter_spec(acquired_nodes, self, node_name)
+                        })
+                        .collect::<Result<Vec<FilterSpec>, _>>()
+                    {
+                        specs.iter().try_fold(surface_to_filter, |surface, spec| {
+                            filters::render(
+                                &spec,
                                 stroke_paint_source.clone(),
                                 fill_paint_source.clone(),
-                                &node_name,
                                 surface,
+                                acquired_nodes,
+                                self,
+                                self.get_transform(),
                                 node_bbox,
                             )
-                        },
-                    )?
+                        })?
+                    } else {
+                        surface_to_filter
+                    }
                 } else {
                     surface_to_filter
                 }
             }
         };
 
-        let img_surface = surface.into_image_surface()?;
-
-        // turn ImageSurface into a Surface
-        Ok((*img_surface).clone())
-    }
-
-    fn run_filter(
-        &mut self,
-        acquired_nodes: &mut AcquiredNodes<'_>,
-        filter_uri: &NodeId,
-        stroke_paint_source: Rc<UserSpacePaintSource>,
-        fill_paint_source: Rc<UserSpacePaintSource>,
-        node_name: &str,
-        surface_to_filter: SharedImageSurface,
-        node_bbox: BoundingBox,
-    ) -> Result<SharedImageSurface, RenderingError> {
-        // TODO: since we check is_applicable before we get here, these checks are redundant
-        // do we want to remove them and directly grab the filter node? or keep for future error
-        // handling?
-        match acquired_nodes.acquire(filter_uri) {
-            Ok(acquired) => {
-                let node = acquired.get();
-
-                let element = node.borrow_element();
-
-                match *element {
-                    Element::Filter(_) => {
-                        if element.is_in_error() {
-                            return Ok(surface_to_filter);
-                        }
-
-                        return filters::render(
-                            &node,
-                            stroke_paint_source,
-                            fill_paint_source,
-                            surface_to_filter,
-                            acquired_nodes,
-                            self,
-                            self.get_transform(),
-                            node_bbox,
-                        );
-                    }
-                    _ => {
-                        rsvg_log!(
-                            "element {} will not be filtered since \"{}\" is not a filter",
-                            node_name,
-                            filter_uri,
-                        );
-                    }
-                }
-            }
-            _ => {
-                rsvg_log!(
-                    "element {} will not be filtered since its filter \"{}\" was not found",
-                    node_name,
-                    filter_uri,
-                );
-            }
-        }
-
-        // Non-existing filters must act as null filters (an empty surface is returned).
-        Ok(surface_to_filter)
+        Ok(surface)
     }
 
     fn set_gradient(self: &mut DrawingCtx, gradient: &UserSpaceGradient) {
diff --git a/src/filter.rs b/src/filter.rs
index b1d26ba3..58fe87aa 100644
--- a/src/filter.rs
+++ b/src/filter.rs
@@ -6,9 +6,10 @@ use std::slice::Iter;
 
 use crate::coord_units::CoordUnits;
 use crate::document::{AcquiredNodes, NodeId};
-use crate::drawing_ctx::ViewParams;
+use crate::drawing_ctx::{DrawingCtx, ViewParams};
 use crate::element::{Draw, Element, ElementResult, SetAttributes};
 use crate::error::ValueErrorKind;
+use crate::filters::{extract_filter_from_filter_node, FilterResolveError, FilterSpec};
 use crate::length::*;
 use crate::node::NodeBorrow;
 use crate::parsers::{Parse, ParseValue};
@@ -107,6 +108,71 @@ pub enum FilterValue {
     // TODO: add functions from https://www.w3.org/TR/filter-effects-1/#filter-functions
 }
 
+impl FilterValue {
+    pub fn to_filter_spec(
+        &self,
+        acquired_nodes: &mut AcquiredNodes<'_>,
+        draw_ctx: &DrawingCtx,
+        node_being_filtered_name: &str,
+    ) -> Result<FilterSpec, FilterResolveError> {
+        match *self {
+            FilterValue::Url(ref node_id) => filter_spec_from_filter_node(
+                acquired_nodes,
+                draw_ctx,
+                node_id,
+                node_being_filtered_name,
+            ),
+        }
+    }
+}
+
+fn filter_spec_from_filter_node(
+    acquired_nodes: &mut AcquiredNodes<'_>,
+    draw_ctx: &DrawingCtx,
+    node_id: &NodeId,
+    node_being_filtered_name: &str,
+) -> Result<FilterSpec, FilterResolveError> {
+    acquired_nodes
+        .acquire(node_id)
+        .map_err(|e| {
+            rsvg_log!(
+                "element {} will not be filtered with \"{}\": {}",
+                node_being_filtered_name,
+                node_id,
+                e
+            );
+            FilterResolveError::ReferenceToNonFilterElement
+        })
+        .and_then(|acquired| {
+            let node = acquired.get();
+            let element = node.borrow_element();
+
+            match *element {
+                Element::Filter(_) => {
+                    if element.is_in_error() {
+                        rsvg_log!(
+                            "element {} will not be filtered since its filter \"{}\" is in error",
+                            node_being_filtered_name,
+                            node_id,
+                        );
+                        Err(FilterResolveError::ChildNodeInError)
+                    } else {
+                        extract_filter_from_filter_node(node, draw_ctx)
+                    }
+                }
+
+                _ => {
+                    rsvg_log!(
+                        "element {} will not be filtered since \"{}\" is not a filter",
+                        node_being_filtered_name,
+                        node_id,
+                    );
+                    Err(FilterResolveError::ReferenceToNonFilterElement)
+                }
+            }
+        })
+}
+
 #[derive(Debug, Clone, PartialEq)]
 pub struct FilterValueList(Vec<FilterValue>);
 
diff --git a/src/filters/error.rs b/src/filters/error.rs
index 9a56d8fe..9fe58420 100644
--- a/src/filters/error.rs
+++ b/src/filters/error.rs
@@ -25,6 +25,8 @@ pub enum FilterError {
 /// Errors that can occur while resolving a `FilterSpec`.
 #[derive(Debug)]
 pub enum FilterResolveError {
+    /// An `uri(#foo)` reference does not point to a `<filter>` element.
+    ReferenceToNonFilterElement,
     /// A lighting filter has none or multiple light sources.
     InvalidLightSourceCount,
     /// Child node was in error.
@@ -52,6 +54,9 @@ impl fmt::Display for FilterError {
 impl fmt::Display for FilterResolveError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
+            FilterResolveError::ReferenceToNonFilterElement => {
+                write!(f, "reference to a non-filter element")
+            }
             FilterResolveError::InvalidLightSourceCount => write!(f, "invalid light source count"),
             FilterResolveError::ChildNodeInError => write!(f, "child node was in error"),
         }
diff --git a/src/filters/mod.rs b/src/filters/mod.rs
index 73482656..acf0ab68 100644
--- a/src/filters/mod.rs
+++ b/src/filters/mod.rs
@@ -29,7 +29,8 @@ pub mod context;
 use self::context::{FilterContext, FilterOutput, FilterResult};
 
 mod error;
-use self::error::{FilterError, FilterResolveError};
+use self::error::FilterError;
+pub use self::error::FilterResolveError;
 
 /// A filter primitive interface.
 pub trait FilterEffect: SetAttributes + Draw {
@@ -316,7 +317,7 @@ pub fn extract_filter_from_filter_node(
 
 /// Applies a filter and returns the resulting surface.
 pub fn render(
-    filter_node: &Node,
+    filter: &FilterSpec,
     stroke_paint_source: Rc<UserSpacePaintSource>,
     fill_paint_source: Rc<UserSpacePaintSource>,
     source_surface: SharedImageSurface,
@@ -325,74 +326,65 @@ pub fn render(
     transform: Transform,
     node_bbox: BoundingBox,
 ) -> Result<SharedImageSurface, RenderingError> {
-    extract_filter_from_filter_node(filter_node, draw_ctx)
-        .map_err(|e| {
-            FilterError::InvalidParameter(format!("error when creating filter spec: {}", e))
-        })
-        .and_then(|filter| {
-            let filter_ctx = FilterContext::new(
-                &filter.user_space_filter,
-                stroke_paint_source,
-                fill_paint_source,
-                &source_surface,
-                transform,
-                node_bbox,
-            )?;
-
-            Ok((filter, filter_ctx))
-        })
-        .and_then(|(filter, mut filter_ctx)| {
-            for user_space_primitive in &filter.primitives {
-                let start = Instant::now();
-
-                match render_primitive(&user_space_primitive, &filter_ctx, acquired_nodes, draw_ctx)
-                {
-                    Ok(output) => {
-                        let elapsed = start.elapsed();
-                        rsvg_log!(
-                            "(rendered filter primitive {} in\n    {} seconds)",
-                            user_space_primitive.params.name(),
-                            elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9
-                        );
-
-                        filter_ctx.store_result(FilterResult {
-                            name: user_space_primitive.result.clone(),
-                            output,
-                        });
-                    }
+    FilterContext::new(
+        &filter.user_space_filter,
+        stroke_paint_source,
+        fill_paint_source,
+        &source_surface,
+        transform,
+        node_bbox,
+    )
+    .and_then(|mut filter_ctx| {
+        for user_space_primitive in &filter.primitives {
+            let start = Instant::now();
+
+            match render_primitive(&user_space_primitive, &filter_ctx, acquired_nodes, draw_ctx) {
+                Ok(output) => {
+                    let elapsed = start.elapsed();
+                    rsvg_log!(
+                        "(rendered filter primitive {} in\n    {} seconds)",
+                        user_space_primitive.params.name(),
+                        elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9
+                    );
 
-                    Err(err) => {
-                        rsvg_log!(
-                            "(filter primitive {} returned an error: {})",
-                            user_space_primitive.params.name(),
-                            err
-                        );
-
-                        // Exit early on Cairo errors. Continue rendering otherwise.
-                        if let FilterError::CairoError(status) = err {
-                            return Err(FilterError::CairoError(status));
-                        }
+                    filter_ctx.store_result(FilterResult {
+                        name: user_space_primitive.result.clone(),
+                        output,
+                    });
+                }
+
+                Err(err) => {
+                    rsvg_log!(
+                        "(filter primitive {} returned an error: {})",
+                        user_space_primitive.params.name(),
+                        err
+                    );
+
+                    // Exit early on Cairo errors. Continue rendering otherwise.
+                    if let FilterError::CairoError(status) = err {
+                        return Err(FilterError::CairoError(status));
                     }
                 }
             }
+        }
 
-            Ok(filter_ctx.into_output()?)
-        })
-        .or_else(|err| match err {
-            FilterError::CairoError(status) => {
-                // Exit early on Cairo errors
-                Err(RenderingError::from(status))
-            }
+        Ok(filter_ctx.into_output()?)
+    })
+    .or_else(|err| match err {
+        FilterError::CairoError(status) => {
+            // Exit early on Cairo errors
+            Err(RenderingError::from(status))
+        }
 
-            _ => {
-                // ignore other filter errors and just return an empty surface
-                Ok(SharedImageSurface::empty(
-                    source_surface.width(),
-                    source_surface.height(),
-                    SurfaceType::AlphaOnly,
-                )?)
-            }
-        })
+        _ => {
+            // ignore other filter errors and just return an empty surface
+            Ok(SharedImageSurface::empty(
+                source_surface.width(),
+                source_surface.height(),
+                SurfaceType::AlphaOnly,
+            )?)
+        }
+    })
 }
 
 #[rustfmt::skip]


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