[librsvg: 6/9] Have FilterValue resolve itself to a FilterSpec
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg: 6/9] Have FilterValue resolve itself to a FilterSpec
- Date: Tue, 27 Apr 2021 17:54:46 +0000 (UTC)
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]