[librsvg: 5/8] Pass the color_interpolation_filters directly to FilterContext.get_input()




commit 99afdceda6fab95cae8c543dd0bc71fd69080c14
Author: Federico Mena Quintero <federico gnome org>
Date:   Wed Mar 10 20:43:39 2021 -0600

    Pass the color_interpolation_filters directly to FilterContext.get_input()
    
    There was a convoluted scheme to get the value of
    color_interpolation_filters in the main filters::render(), and hack it
    into the FilterContext with with_linear_rgb().
    
    It is easier if filter primitives request their inputs in the color
    space that they need.
    
    I've added comments where the spec explicitly says that a primitive
    should *not* use the value of the color_interpolation_filters
    property.
    
    I've also added a comment to morphology.rs, since the spec is not
    clear about it, but a) using the value from the ComputedValues makes a
    test fail; b) the original code has feMorphology's implementation of
    is_affected_by_color_interpolation_filters() set to false anyway.
    
    I've regenerated the reference for svg1.1/filters-displace-02-f.svg -
    it fails when the code works as per the spec (it didn't before, as it
    converted both `in` and `in2` to the color_interpolation_filters color
    space when it should only do that for `in2`, while `in` should remain
    in its original color space.  I can't make much sense of the
    difference between the new result and the original PNG from the SVG1.1
    test suite.

 src/filters/blend.rs                               |  12 ++++--
 src/filters/color_matrix.rs                        |  10 +++--
 src/filters/component_transfer.rs                  |   8 +++-
 src/filters/composite.rs                           |  12 ++++--
 src/filters/context.rs                             |  30 ++++---------
 src/filters/convolve_matrix.rs                     |  10 +++--
 src/filters/displacement_map.rs                    |  24 +++++++++--
 src/filters/gaussian_blur.rs                       |  10 +++--
 src/filters/lighting.rs                            |  17 ++++----
 src/filters/merge.rs                               |  35 ++++++++++++---
 src/filters/mod.rs                                 |  47 ++++++---------------
 src/filters/morphology.rs                          |  16 ++++++-
 src/filters/offset.rs                              |  15 ++++++-
 src/filters/tile.rs                                |  15 ++++++-
 .../reftests/svg1.1/filters-displace-02-f-ref.png  | Bin 7658 -> 7511 bytes
 15 files changed, 165 insertions(+), 96 deletions(-)
---
diff --git a/src/filters/blend.rs b/src/filters/blend.rs
index 9ff0d41d..8728a5d6 100755
--- a/src/filters/blend.rs
+++ b/src/filters/blend.rs
@@ -5,7 +5,7 @@ use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::element::{ElementResult, SetAttributes};
 use crate::error::*;
-use crate::node::Node;
+use crate::node::{CascadedValues, Node};
 use crate::parsers::{Parse, ParseValue};
 use crate::xml::Attributes;
 
@@ -75,13 +75,17 @@ impl SetAttributes for FeBlend {
 impl FilterRender for FeBlend {
     fn render(
         &self,
-        _node: &Node,
+        node: &Node,
         ctx: &FilterContext,
         acquired_nodes: &mut AcquiredNodes<'_>,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1)?;
-        let input_2 = ctx.get_input(acquired_nodes, draw_ctx, &self.in2)?;
+        let cascaded = CascadedValues::new_from_node(node);
+        let values = cascaded.get();
+        let cif = values.color_interpolation_filters();
+
+        let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1, cif)?;
+        let input_2 = ctx.get_input(acquired_nodes, draw_ctx, &self.in2, cif)?;
         let bounds = self
             .base
             .get_bounds(ctx)?
diff --git a/src/filters/color_matrix.rs b/src/filters/color_matrix.rs
index 9cdb0ca1..31ecdf2e 100644
--- a/src/filters/color_matrix.rs
+++ b/src/filters/color_matrix.rs
@@ -6,7 +6,7 @@ use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::element::{ElementResult, SetAttributes};
 use crate::error::*;
-use crate::node::Node;
+use crate::node::{CascadedValues, Node};
 use crate::parsers::{NumberList, NumberListLength, Parse, ParseValue};
 use crate::surface_utils::{
     iterators::Pixels, shared_surface::ExclusiveImageSurface, ImageSurfaceDataExt, Pixel,
@@ -142,12 +142,16 @@ impl SetAttributes for FeColorMatrix {
 impl FilterRender for FeColorMatrix {
     fn render(
         &self,
-        _node: &Node,
+        node: &Node,
         ctx: &FilterContext,
         acquired_nodes: &mut AcquiredNodes<'_>,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1)?;
+        let cascaded = CascadedValues::new_from_node(node);
+        let values = cascaded.get();
+        let cif = values.color_interpolation_filters();
+
+        let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1, cif)?;
         let bounds = self
             .base
             .get_bounds(ctx)?
diff --git a/src/filters/component_transfer.rs b/src/filters/component_transfer.rs
index 75d7a85e..62923bd7 100644
--- a/src/filters/component_transfer.rs
+++ b/src/filters/component_transfer.rs
@@ -7,7 +7,7 @@ use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::element::{Draw, Element, ElementResult, SetAttributes};
 use crate::error::*;
-use crate::node::{Node, NodeBorrow};
+use crate::node::{CascadedValues, Node, NodeBorrow};
 use crate::parsers::{NumberList, NumberListLength, Parse, ParseValue};
 use crate::surface_utils::{
     iterators::Pixels, shared_surface::ExclusiveImageSurface, ImageSurfaceDataExt, Pixel,
@@ -294,9 +294,13 @@ impl FilterRender for FeComponentTransfer {
         acquired_nodes: &mut AcquiredNodes<'_>,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
+        let cascaded = CascadedValues::new_from_node(node);
+        let values = cascaded.get();
+        let cif = values.color_interpolation_filters();
+
         let functions = get_parameters(node)?;
 
-        let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1)?;
+        let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1, cif)?;
         let bounds = self
             .base
             .get_bounds(ctx)?
diff --git a/src/filters/composite.rs b/src/filters/composite.rs
index 0929d571..ae92b180 100644
--- a/src/filters/composite.rs
+++ b/src/filters/composite.rs
@@ -5,7 +5,7 @@ use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::element::{ElementResult, SetAttributes};
 use crate::error::*;
-use crate::node::Node;
+use crate::node::{CascadedValues, Node};
 use crate::parsers::{Parse, ParseValue};
 use crate::xml::Attributes;
 
@@ -76,13 +76,17 @@ impl SetAttributes for FeComposite {
 impl FilterRender for FeComposite {
     fn render(
         &self,
-        _node: &Node,
+        node: &Node,
         ctx: &FilterContext,
         acquired_nodes: &mut AcquiredNodes<'_>,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1)?;
-        let input_2 = ctx.get_input(acquired_nodes, draw_ctx, &self.in2)?;
+        let cascaded = CascadedValues::new_from_node(node);
+        let values = cascaded.get();
+        let cif = values.color_interpolation_filters();
+
+        let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1, cif)?;
+        let input_2 = ctx.get_input(acquired_nodes, draw_ctx, &self.in2, cif)?;
         let bounds = self
             .base
             .get_bounds(ctx)?
diff --git a/src/filters/context.rs b/src/filters/context.rs
index c5bf35be..172ff894 100644
--- a/src/filters/context.rs
+++ b/src/filters/context.rs
@@ -8,6 +8,7 @@ use crate::drawing_ctx::DrawingCtx;
 use crate::filter::Filter;
 use crate::parsers::CustomIdent;
 use crate::properties::ComputedValues;
+use crate::property_defs::ColorInterpolationFilters;
 use crate::rect::{IRect, Rect};
 use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
 use crate::transform::Transform;
@@ -70,11 +71,6 @@ pub struct FilterContext {
     primitive_units: CoordUnits,
     /// The filter effects region.
     effects_region: Rect,
-    /// Whether the currently rendered filter primitive uses linear RGB for color operations.
-    ///
-    /// This affects `get_input()` and `store_result()` which should perform linearization and
-    /// unlinearization respectively when this is set to `true`.
-    processing_linear_rgb: bool,
 
     /// The filter element affine matrix.
     ///
@@ -174,7 +170,6 @@ impl FilterContext {
             fill_paint_surface: OnceCell::new(),
             primitive_units,
             effects_region,
-            processing_linear_rgb: false,
             _affine: affine,
             paffine,
         }
@@ -310,8 +305,6 @@ impl FilterContext {
     }
 
     /// Retrieves the filter input surface according to the SVG rules.
-    ///
-    /// Does not take `processing_linear_rgb` into account.
     fn get_input_raw(
         &self,
         acquired_nodes: &mut AcquiredNodes<'_>,
@@ -369,11 +362,14 @@ impl FilterContext {
     }
 
     /// Retrieves the filter input surface according to the SVG rules.
+    ///
+    /// The surface will be converted to the color space specified by `color_interpolation_filters`.
     pub fn get_input(
         &self,
         acquired_nodes: &mut AcquiredNodes<'_>,
         draw_ctx: &mut DrawingCtx,
         in_: &Input,
+        color_interpolation_filters: ColorInterpolationFilters,
     ) -> Result<FilterInput, FilterError> {
         let raw = self.get_input_raw(acquired_nodes, draw_ctx, in_)?;
 
@@ -386,11 +382,12 @@ impl FilterContext {
             }) => (surface, *bounds),
         };
 
-        let surface = if self.processing_linear_rgb {
-            surface.to_linear_rgb(bounds)
-        } else {
-            surface.to_srgb(bounds)
+        let surface = match color_interpolation_filters {
+            ColorInterpolationFilters::Auto => Ok(surface.clone()),
+            ColorInterpolationFilters::LinearRgb => surface.to_linear_rgb(bounds),
+            ColorInterpolationFilters::Srgb => surface.to_srgb(bounds),
         };
+
         surface
             .map_err(FilterError::CairoError)
             .map(|surface| match raw {
@@ -400,15 +397,6 @@ impl FilterContext {
                 }
             })
     }
-
-    /// Calls the given closure with linear RGB processing enabled.
-    #[inline]
-    pub fn with_linear_rgb<T, F: FnOnce(&mut FilterContext) -> T>(&mut self, f: F) -> T {
-        self.processing_linear_rgb = true;
-        let rv = f(self);
-        self.processing_linear_rgb = false;
-        rv
-    }
 }
 
 impl FilterInput {
diff --git a/src/filters/convolve_matrix.rs b/src/filters/convolve_matrix.rs
index 40449942..4d6723b1 100644
--- a/src/filters/convolve_matrix.rs
+++ b/src/filters/convolve_matrix.rs
@@ -6,7 +6,7 @@ use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::element::{ElementResult, SetAttributes};
 use crate::error::*;
-use crate::node::Node;
+use crate::node::{CascadedValues, Node};
 use crate::parsers::{
     NonNegative, NumberList, NumberListLength, NumberOptionalNumber, Parse, ParseValue,
 };
@@ -124,14 +124,18 @@ impl SetAttributes for FeConvolveMatrix {
 impl FilterRender for FeConvolveMatrix {
     fn render(
         &self,
-        _node: &Node,
+        node: &Node,
         ctx: &FilterContext,
         acquired_nodes: &mut AcquiredNodes<'_>,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
         #![allow(clippy::many_single_char_names)]
 
-        let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1)?;
+        let cascaded = CascadedValues::new_from_node(node);
+        let values = cascaded.get();
+        let cif = values.color_interpolation_filters();
+
+        let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1, cif)?;
         let mut bounds = self
             .base
             .get_bounds(ctx)?
diff --git a/src/filters/displacement_map.rs b/src/filters/displacement_map.rs
index 6bd557ed..42d00e55 100644
--- a/src/filters/displacement_map.rs
+++ b/src/filters/displacement_map.rs
@@ -5,8 +5,9 @@ use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::element::{ElementResult, SetAttributes};
 use crate::error::*;
-use crate::node::Node;
+use crate::node::{CascadedValues, Node};
 use crate::parsers::{Parse, ParseValue};
+use crate::property_defs::ColorInterpolationFilters;
 use crate::surface_utils::{iterators::Pixels, shared_surface::ExclusiveImageSurface};
 use crate::xml::Attributes;
 
@@ -73,13 +74,28 @@ impl SetAttributes for FeDisplacementMap {
 impl FilterRender for FeDisplacementMap {
     fn render(
         &self,
-        _node: &Node,
+        node: &Node,
         ctx: &FilterContext,
         acquired_nodes: &mut AcquiredNodes<'_>,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1)?;
-        let displacement_input = ctx.get_input(acquired_nodes, draw_ctx, &self.in2)?;
+        let cascaded = CascadedValues::new_from_node(node);
+        let values = cascaded.get();
+        let cif = values.color_interpolation_filters();
+
+        // https://www.w3.org/TR/filter-effects/#feDisplacementMapElement
+        // "The color-interpolation-filters property only applies to
+        // the in2 source image and does not apply to the in source
+        // image. The in source image must remain in its current color
+        // space.
+
+        let input_1 = ctx.get_input(
+            acquired_nodes,
+            draw_ctx,
+            &self.in1,
+            ColorInterpolationFilters::Auto,
+        )?;
+        let displacement_input = ctx.get_input(acquired_nodes, draw_ctx, &self.in2, cif)?;
         let bounds = self
             .base
             .get_bounds(ctx)?
diff --git a/src/filters/gaussian_blur.rs b/src/filters/gaussian_blur.rs
index 5fe342b2..a752255c 100644
--- a/src/filters/gaussian_blur.rs
+++ b/src/filters/gaussian_blur.rs
@@ -7,7 +7,7 @@ use nalgebra::{DMatrix, Dynamic, VecStorage};
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::element::{ElementResult, SetAttributes};
-use crate::node::Node;
+use crate::node::{CascadedValues, Node};
 use crate::parsers::{NonNegative, NumberOptionalNumber, ParseValue};
 use crate::rect::IRect;
 use crate::surface_utils::{
@@ -190,12 +190,16 @@ fn gaussian_blur(
 impl FilterRender for FeGaussianBlur {
     fn render(
         &self,
-        _node: &Node,
+        node: &Node,
         ctx: &FilterContext,
         acquired_nodes: &mut AcquiredNodes<'_>,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1)?;
+        let cascaded = CascadedValues::new_from_node(node);
+        let values = cascaded.get();
+        let cif = values.color_interpolation_filters();
+
+        let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1, cif)?;
         let bounds = self
             .base
             .get_bounds(ctx)?
diff --git a/src/filters/lighting.rs b/src/filters/lighting.rs
index 81132bd9..db97b2dd 100644
--- a/src/filters/lighting.rs
+++ b/src/filters/lighting.rs
@@ -461,7 +461,14 @@ macro_rules! impl_lighting_filter {
                 acquired_nodes: &mut AcquiredNodes<'_>,
                 draw_ctx: &mut DrawingCtx,
             ) -> Result<FilterResult, FilterError> {
-                let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1)?;
+                let light = Light::new(node)?;
+
+                let input_1 = ctx.get_input(
+                    acquired_nodes,
+                    draw_ctx,
+                    &self.in1,
+                    light.color_interpolation_filters,
+                )?;
                 let mut bounds = self
                     .base
                     .get_bounds(ctx)?
@@ -494,18 +501,12 @@ macro_rules! impl_lighting_filter {
 
                 let (ox, oy) = scale.unwrap_or((1.0, 1.0));
 
-                let light = Light::new(node)?;
-
                 let source = light.source.transform(ctx.paffine());
 
-                // The generated color values are in the color space determined by
-                // color-interpolation-filters.
-                let surface_type = SurfaceType::from(light.color_interpolation_filters);
-
                 let mut surface = ExclusiveImageSurface::new(
                     input_surface.width(),
                     input_surface.height(),
-                    surface_type,
+                    SurfaceType::from(light.color_interpolation_filters),
                 )?;
 
                 {
diff --git a/src/filters/merge.rs b/src/filters/merge.rs
index 0ba0d8b1..35c598f8 100644
--- a/src/filters/merge.rs
+++ b/src/filters/merge.rs
@@ -3,8 +3,9 @@ use markup5ever::{expanded_name, local_name, namespace_url, ns};
 use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::element::{Draw, Element, ElementResult, SetAttributes};
-use crate::node::{Node, NodeBorrow};
+use crate::node::{CascadedValues, Node, NodeBorrow};
 use crate::parsers::ParseValue;
+use crate::property_defs::ColorInterpolationFilters;
 use crate::rect::IRect;
 use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
 use crate::xml::Attributes;
@@ -21,6 +22,7 @@ pub struct FeMerge {
 #[derive(Clone, Debug, Default, PartialEq)]
 pub struct FeMergeNode {
     in1: Input,
+    color_interpolation_filters: ColorInterpolationFilters,
 }
 
 impl Default for FeMerge {
@@ -63,7 +65,12 @@ impl FeMergeNode {
         bounds: IRect,
         output_surface: Option<SharedImageSurface>,
     ) -> Result<SharedImageSurface, FilterError> {
-        let input = ctx.get_input(acquired_nodes, draw_ctx, &self.in1)?;
+        let input = ctx.get_input(
+            acquired_nodes,
+            draw_ctx,
+            &self.in1,
+            self.color_interpolation_filters,
+        )?;
 
         if output_surface.is_none() {
             return Ok(input.surface().clone());
@@ -89,7 +96,12 @@ impl FilterRender for FeMerge {
         // Compute the filter bounds, taking each feMergeNode's input into account.
         let mut bounds = self.base.get_bounds(ctx)?;
         for merge_node in &parameters {
-            let input = ctx.get_input(acquired_nodes, draw_ctx, &merge_node.in1)?;
+            let input = ctx.get_input(
+                acquired_nodes,
+                draw_ctx,
+                &merge_node.in1,
+                merge_node.color_interpolation_filters,
+            )?;
             bounds = bounds.add_input(&input);
         }
 
@@ -137,8 +149,15 @@ fn get_parameters(node: &Node) -> Result<Vec<FeMergeNode>, FilterError> {
             return Err(FilterError::ChildNodeInError);
         }
 
+        let cascaded = CascadedValues::new_from_node(&child);
+        let values = cascaded.get();
+        let color_interpolation_filters = values.color_interpolation_filters();
+
         if let Element::FeMergeNode(ref merge_node) = *elt {
-            merge_nodes.push(merge_node.element_impl.clone());
+            merge_nodes.push(FeMergeNode {
+                color_interpolation_filters,
+                ..merge_node.element_impl.clone()
+            });
         }
     }
 
@@ -158,7 +177,7 @@ mod tests {
   <filter id="filter">
     <feMerge id="merge">
       <feMergeNode in="SourceGraphic"/>
-      <feMergeNode in="SourceAlpha"/>
+      <feMergeNode in="SourceAlpha" color-interpolation-filters="sRGB"/>
     </feMerge>
   </filter>
 </svg>
@@ -173,10 +192,12 @@ mod tests {
             &params[..],
             vec![
                 FeMergeNode {
-                    in1: Input::SourceGraphic
+                    in1: Input::SourceGraphic,
+                    color_interpolation_filters: Default::default(),
                 },
                 FeMergeNode {
-                    in1: Input::SourceAlpha
+                    in1: Input::SourceAlpha,
+                    color_interpolation_filters: ColorInterpolationFilters::Srgb,
                 },
             ]
         );
diff --git a/src/filters/mod.rs b/src/filters/mod.rs
index e69b6d97..cdcb89f0 100644
--- a/src/filters/mod.rs
+++ b/src/filters/mod.rs
@@ -11,7 +11,7 @@ use crate::drawing_ctx::DrawingCtx;
 use crate::element::{Draw, ElementResult, SetAttributes};
 use crate::error::{ElementError, ParseError, RenderingError};
 use crate::length::*;
-use crate::node::{CascadedValues, Node, NodeBorrow};
+use crate::node::{Node, NodeBorrow};
 use crate::parsers::{CustomIdent, Parse, ParseValue};
 use crate::properties::ComputedValues;
 use crate::property_defs::ColorInterpolationFilters;
@@ -258,45 +258,24 @@ pub fn render(
             !in_error
         })
         // Keep only filter primitives (those that implement the Filter trait)
-        .filter(|c| c.borrow_element().as_filter_effect().is_some())
-        // Check if the node wants linear RGB.
-        .map(|c| {
-            let linear_rgb = {
-                let cascaded = CascadedValues::new_from_node(&c);
-                let values = cascaded.get();
+        .filter(|c| c.borrow_element().as_filter_effect().is_some());
 
-                values.color_interpolation_filters() == ColorInterpolationFilters::LinearRgb
-            };
-
-            (c, linear_rgb)
-        });
-
-    for (c, linear_rgb) in primitives {
+    for c in primitives {
         let elt = c.borrow_element();
         let filter = elt.as_filter_effect().unwrap();
 
-        let mut render = |filter_ctx: &mut FilterContext| {
-            if let Err(err) = filter
-                .render(&c, 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(status);
-                }
-            }
-
-            Ok(())
-        };
-
         let start = Instant::now();
 
-        if filter.is_affected_by_color_interpolation_filters() && linear_rgb {
-            filter_ctx.with_linear_rgb(render)?;
-        } else {
-            render(&mut filter_ctx)?;
+        if let Err(err) = filter
+            .render(&c, &mut 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();
diff --git a/src/filters/morphology.rs b/src/filters/morphology.rs
index c88efbea..ce135681 100644
--- a/src/filters/morphology.rs
+++ b/src/filters/morphology.rs
@@ -9,6 +9,7 @@ use crate::element::{ElementResult, SetAttributes};
 use crate::error::*;
 use crate::node::Node;
 use crate::parsers::{NonNegative, NumberOptionalNumber, Parse, ParseValue};
+use crate::property_defs::ColorInterpolationFilters;
 use crate::rect::IRect;
 use crate::surface_utils::{
     iterators::{PixelRectangle, Pixels},
@@ -74,7 +75,20 @@ impl FilterRender for FeMorphology {
         acquired_nodes: &mut AcquiredNodes<'_>,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1)?;
+        // Although https://www.w3.org/TR/filter-effects/#propdef-color-interpolation-filters does not 
mention
+        // feMorphology as being one of the primitives that does *not* use that property,
+        // the SVG1.1 test for filters-morph-01-f.svg fails if we pass the value from the ComputedValues 
here (that
+        // document does not specify the color-interpolation-filters property, so it defaults to linearRGB).
+        // So, we pass Auto, which will get resolved to SRGB, and that makes that test pass.
+        //
+        // I suppose erosion/dilation doesn't care about the color space of the source image?
+
+        let input_1 = ctx.get_input(
+            acquired_nodes,
+            draw_ctx,
+            &self.in1,
+            ColorInterpolationFilters::Auto,
+        )?;
         let bounds = self
             .base
             .get_bounds(ctx)?
diff --git a/src/filters/offset.rs b/src/filters/offset.rs
index 213a5c77..3b899d4b 100644
--- a/src/filters/offset.rs
+++ b/src/filters/offset.rs
@@ -5,6 +5,7 @@ use crate::drawing_ctx::DrawingCtx;
 use crate::element::{ElementResult, SetAttributes};
 use crate::node::Node;
 use crate::parsers::ParseValue;
+use crate::property_defs::ColorInterpolationFilters;
 use crate::xml::Attributes;
 
 use super::context::{FilterContext, FilterOutput, FilterResult};
@@ -55,7 +56,19 @@ impl FilterRender for FeOffset {
         acquired_nodes: &mut AcquiredNodes<'_>,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1)?;
+        // https://www.w3.org/TR/filter-effects/#ColorInterpolationFiltersProperty
+        //
+        // "Note: The color-interpolation-filters property just has an
+        // effect on filter operations. Therefore, it has no effect on
+        // filter primitives like feOffset"
+        //
+        // This is why we pass Auto here.
+        let input_1 = ctx.get_input(
+            acquired_nodes,
+            draw_ctx,
+            &self.in1,
+            ColorInterpolationFilters::Auto,
+        )?;
         let bounds = self
             .base
             .get_bounds(ctx)?
diff --git a/src/filters/tile.rs b/src/filters/tile.rs
index 986eeff7..7d086082 100644
--- a/src/filters/tile.rs
+++ b/src/filters/tile.rs
@@ -2,6 +2,7 @@ use crate::document::AcquiredNodes;
 use crate::drawing_ctx::DrawingCtx;
 use crate::element::{ElementResult, SetAttributes};
 use crate::node::Node;
+use crate::property_defs::ColorInterpolationFilters;
 use crate::xml::Attributes;
 
 use super::context::{FilterContext, FilterInput, FilterOutput, FilterResult};
@@ -39,7 +40,19 @@ impl FilterRender for FeTile {
         acquired_nodes: &mut AcquiredNodes<'_>,
         draw_ctx: &mut DrawingCtx,
     ) -> Result<FilterResult, FilterError> {
-        let input_1 = ctx.get_input(acquired_nodes, draw_ctx, &self.in1)?;
+        // https://www.w3.org/TR/filter-effects/#ColorInterpolationFiltersProperty
+        //
+        // "Note: The color-interpolation-filters property just has an
+        // effect on filter operations. Therefore, it has no effect on
+        // filter primitives like [...], feTile"
+        //
+        // This is why we pass Auto here.
+        let input_1 = ctx.get_input(
+            acquired_nodes,
+            draw_ctx,
+            &self.in1,
+            ColorInterpolationFilters::Auto,
+        )?;
 
         // feTile doesn't consider its inputs in the filter primitive subregion calculation.
         let bounds = self.base.get_bounds(ctx)?.into_irect(ctx, draw_ctx);
diff --git a/tests/fixtures/reftests/svg1.1/filters-displace-02-f-ref.png 
b/tests/fixtures/reftests/svg1.1/filters-displace-02-f-ref.png
index 784f35ce..f8790b9e 100644
Binary files a/tests/fixtures/reftests/svg1.1/filters-displace-02-f-ref.png and 
b/tests/fixtures/reftests/svg1.1/filters-displace-02-f-ref.png differ


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