From e915da0935c8351d28a479520621561e49924662 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Fri, 22 Jun 2018 13:40:31 +0300 Subject: Remove {un,}linearization boilerplate --- rsvg_internals/src/filters/blend.rs | 57 ++++-------------- rsvg_internals/src/filters/bounds.rs | 20 ++----- rsvg_internals/src/filters/component_transfer.rs | 29 ++------- rsvg_internals/src/filters/composite.rs | 62 +++++--------------- rsvg_internals/src/filters/context.rs | 61 ++++++++++++++++++- rsvg_internals/src/filters/ffi.rs | 65 +++++++++++++++++---- rsvg_internals/src/filters/flood.rs | 5 ++ rsvg_internals/src/filters/image.rs | 5 ++ rsvg_internals/src/filters/merge.rs | 59 +++++++------------ rsvg_internals/src/filters/mod.rs | 15 +++-- rsvg_internals/src/filters/offset.rs | 5 ++ rsvg_internals/src/surface_utils/shared_surface.rs | 22 +++++++ ...r-composite-color-interpolation-filters-ref.png | Bin 7951 -> 7948 bytes .../reftests/svg1.1/filters-composite-02-b-ref.png | Bin 15918 -> 15917 bytes .../reftests/svg1.1/filters-image-04-f-ref.png | Bin 12642 -> 12582 bytes 15 files changed, 220 insertions(+), 185 deletions(-) diff --git a/rsvg_internals/src/filters/blend.rs b/rsvg_internals/src/filters/blend.rs index 8f1a401f..a5b47b76 100644 --- a/rsvg_internals/src/filters/blend.rs +++ b/rsvg_internals/src/filters/blend.rs @@ -1,6 +1,6 @@ use std::cell::{Cell, RefCell}; -use cairo::{self, ImageSurface}; +use cairo; use attributes::Attribute; use error::NodeError; @@ -8,8 +8,6 @@ use handle::RsvgHandle; use node::{NodeResult, NodeTrait, RsvgCNodeImpl, RsvgNode}; use parsers::ParseError; use property_bag::PropertyBag; -use srgb::{linearize_surface, unlinearize_surface}; -use state::ColorInterpolationFilters; use surface_utils::shared_surface::SharedImageSurface; use super::context::{FilterContext, FilterOutput, FilterResult}; @@ -74,7 +72,7 @@ impl NodeTrait for Blend { } impl Filter for Blend { - fn render(&self, node: &RsvgNode, ctx: &FilterContext) -> Result { + fn render(&self, _node: &RsvgNode, ctx: &FilterContext) -> Result { let input = make_result(self.base.get_input(ctx))?; let input_2 = make_result(ctx.get_input(self.in2.borrow().as_ref()))?; let bounds = self @@ -84,35 +82,10 @@ impl Filter for Blend { .add_input(&input_2) .into_irect(); - let cascaded = node.get_cascaded_values(); - let values = cascaded.get(); - - let input_surface = - if values.color_interpolation_filters == ColorInterpolationFilters::LinearRgb { - SharedImageSurface::new( - linearize_surface(input.surface(), bounds) - .map_err(FilterError::BadInputSurfaceStatus)?, - ).unwrap() - } else { - input.surface().clone() - }; - - let input_2_surface = - if values.color_interpolation_filters == ColorInterpolationFilters::LinearRgb { - SharedImageSurface::new( - linearize_surface(input_2.surface(), bounds) - .map_err(FilterError::BadInputSurfaceStatus)?, - ).unwrap() - } else { - input_2.surface().clone() - }; - - let output_surface = ImageSurface::create( - cairo::Format::ARgb32, - ctx.source_graphic().width(), - ctx.source_graphic().height(), - ).map_err(FilterError::OutputSurfaceCreation)?; - + let output_surface = input_2 + .surface() + .copy_surface(bounds) + .map_err(FilterError::OutputSurfaceCreation)?; { let cr = cairo::Context::new(&output_surface); cr.rectangle( @@ -123,22 +96,11 @@ impl Filter for Blend { ); cr.clip(); - input_2_surface.set_as_source_surface(&cr, 0f64, 0f64); - cr.paint(); - - input_surface.set_as_source_surface(&cr, 0f64, 0f64); + input.surface().set_as_source_surface(&cr, 0f64, 0f64); cr.set_operator(self.mode.get().into()); cr.paint(); } - let output_surface = - if values.color_interpolation_filters == ColorInterpolationFilters::LinearRgb { - unlinearize_surface(&SharedImageSurface::new(output_surface).unwrap(), bounds) - .map_err(FilterError::OutputSurfaceCreation)? - } else { - output_surface - }; - Ok(FilterResult { name: self.base.result.borrow().clone(), output: FilterOutput { @@ -147,6 +109,11 @@ impl Filter for Blend { }, }) } + + #[inline] + fn is_affected_by_color_interpolation_filters() -> bool { + true + } } impl Mode { diff --git a/rsvg_internals/src/filters/bounds.rs b/rsvg_internals/src/filters/bounds.rs index 5b85d654..54f3934e 100644 --- a/rsvg_internals/src/filters/bounds.rs +++ b/rsvg_internals/src/filters/bounds.rs @@ -83,12 +83,10 @@ impl<'a> BoundsBuilder<'a> { /// Returns the final pixel bounds. #[inline] pub fn into_irect(self) -> IRect { - let (mut bbox, needs_clipping) = self.apply_properties(); + let mut bbox = self.apply_properties(); - if needs_clipping { - let effects_region = self.ctx.effects_region(); - bbox.clip(&effects_region); - } + let effects_region = self.ctx.effects_region(); + bbox.clip(&effects_region); bbox.rect.unwrap().into() } @@ -98,11 +96,11 @@ impl<'a> BoundsBuilder<'a> { /// Used by feImage. #[inline] pub fn into_irect_without_clipping(self) -> IRect { - self.apply_properties().0.rect.unwrap().into() + self.apply_properties().rect.unwrap().into() } /// Applies the filter primitive properties. - fn apply_properties(mut self) -> (BoundingBox, bool) { + fn apply_properties(mut self) -> BoundingBox { if self.bbox.rect.is_none() || self.standard_input_was_referenced { // The default value is the filter effects region. let effects_region = self.ctx.effects_region(); @@ -113,8 +111,6 @@ impl<'a> BoundsBuilder<'a> { self.bbox.insert(&effects_region); } - let mut needs_clipping = false; - // If any of the properties were specified, we need to respect them. if self.x.is_some() || self.y.is_some() || self.width.is_some() || self.height.is_some() { self.ctx.with_primitive_units(|normalize| { @@ -135,15 +131,11 @@ impl<'a> BoundsBuilder<'a> { rect.height = normalize(&height); } }); - - // x, y, width, height, on the other hand, can exceed the filter effects region, so a - // clip is needed. - needs_clipping = true; } // Convert into the surface coordinate system. let mut bbox = BoundingBox::new(&cairo::Matrix::identity()); bbox.insert(&self.bbox); - (bbox, needs_clipping) + bbox } } diff --git a/rsvg_internals/src/filters/component_transfer.rs b/rsvg_internals/src/filters/component_transfer.rs index d21d6b84..883e303d 100644 --- a/rsvg_internals/src/filters/component_transfer.rs +++ b/rsvg_internals/src/filters/component_transfer.rs @@ -9,8 +9,6 @@ use handle::RsvgHandle; use node::{NodeResult, NodeTrait, NodeType, RsvgCNodeImpl, RsvgNode}; use parsers::{self, ListLength, NumberListError, ParseError}; use property_bag::PropertyBag; -use srgb::{linearize_surface, unlinearize_surface}; -use state::ColorInterpolationFilters; use surface_utils::{ iterators::Pixels, shared_surface::SharedImageSurface, @@ -282,19 +280,6 @@ impl Filter for ComponentTransfer { let input = make_result(self.base.get_input(ctx))?; let bounds = self.base.get_bounds(ctx).add_input(&input).into_irect(); - let cascaded = node.get_cascaded_values(); - let values = cascaded.get(); - - let input_surface = - if values.color_interpolation_filters == ColorInterpolationFilters::LinearRgb { - SharedImageSurface::new( - linearize_surface(input.surface(), bounds) - .map_err(FilterError::BadInputSurfaceStatus)?, - ).unwrap() - } else { - input.surface().clone() - }; - // Create the output surface. let mut output_surface = ImageSurface::create( cairo::Format::ARgb32, @@ -364,7 +349,7 @@ impl Filter for ComponentTransfer { { let mut output_data = output_surface.get_data().unwrap(); - for (x, y, pixel) in Pixels::new(&input_surface, bounds) { + for (x, y, pixel) in Pixels::new(input.surface(), bounds) { let alpha = f64::from(pixel.a) / 255f64; let new_alpha = compute_a(alpha); @@ -379,14 +364,6 @@ impl Filter for ComponentTransfer { } } - let output_surface = - if values.color_interpolation_filters == ColorInterpolationFilters::LinearRgb { - unlinearize_surface(&SharedImageSurface::new(output_surface).unwrap(), bounds) - .map_err(FilterError::OutputSurfaceCreation)? - } else { - output_surface - }; - Ok(FilterResult { name: self.base.result.borrow().clone(), output: FilterOutput { @@ -395,6 +372,10 @@ impl Filter for ComponentTransfer { }, }) } + + fn is_affected_by_color_interpolation_filters() -> bool { + true + } } impl FunctionType { diff --git a/rsvg_internals/src/filters/composite.rs b/rsvg_internals/src/filters/composite.rs index 719ae9a7..d4f136af 100644 --- a/rsvg_internals/src/filters/composite.rs +++ b/rsvg_internals/src/filters/composite.rs @@ -9,8 +9,6 @@ use handle::RsvgHandle; use node::{NodeResult, NodeTrait, RsvgCNodeImpl, RsvgNode}; use parsers::{self, parse, Parse}; use property_bag::PropertyBag; -use srgb::{linearize_surface, unlinearize_surface}; -use state::ColorInterpolationFilters; use surface_utils::{ iterators::Pixels, shared_surface::SharedImageSurface, @@ -102,7 +100,7 @@ impl NodeTrait for Composite { } impl Filter for Composite { - fn render(&self, node: &RsvgNode, ctx: &FilterContext) -> Result { + fn render(&self, _node: &RsvgNode, ctx: &FilterContext) -> Result { let input = make_result(self.base.get_input(ctx))?; let input_2 = make_result(ctx.get_input(self.in2.borrow().as_ref()))?; let bounds = self @@ -112,34 +110,11 @@ impl Filter for Composite { .add_input(&input_2) .into_irect(); - let cascaded = node.get_cascaded_values(); - let values = cascaded.get(); - - let input_surface = - if values.color_interpolation_filters == ColorInterpolationFilters::LinearRgb { - SharedImageSurface::new( - linearize_surface(input.surface(), bounds) - .map_err(FilterError::BadInputSurfaceStatus)?, - ).unwrap() - } else { - input.surface().clone() - }; - - let input_2_surface = - if values.color_interpolation_filters == ColorInterpolationFilters::LinearRgb { - SharedImageSurface::new( - linearize_surface(input_2.surface(), bounds) - .map_err(FilterError::BadInputSurfaceStatus)?, - ).unwrap() - } else { - input_2.surface().clone() - }; - let output_surface = if self.operator.get() == Operator::Arithmetic { let mut output_surface = ImageSurface::create( cairo::Format::ARgb32, - input_surface.width(), - input_surface.height(), + input.surface().width(), + input.surface().height(), ).map_err(FilterError::OutputSurfaceCreation)?; let output_stride = output_surface.get_stride() as usize; @@ -151,8 +126,8 @@ impl Filter for Composite { let k3 = self.k3.get(); let k4 = self.k4.get(); - for (x, y, pixel, pixel_2) in Pixels::new(&input_surface, bounds) - .map(|(x, y, p)| (x, y, p, input_2_surface.get_pixel(x, y))) + for (x, y, pixel, pixel_2) in Pixels::new(input.surface(), bounds) + .map(|(x, y, p)| (x, y, p, input_2.surface().get_pixel(x, y))) { let i1a = f64::from(pixel.a) / 255f64; let i2a = f64::from(pixel_2.a) / 255f64; @@ -186,11 +161,10 @@ impl Filter for Composite { output_surface } else { - let output_surface = ImageSurface::create( - cairo::Format::ARgb32, - ctx.source_graphic().width(), - ctx.source_graphic().height(), - ).map_err(FilterError::OutputSurfaceCreation)?; + let output_surface = input_2 + .surface() + .copy_surface(bounds) + .map_err(FilterError::OutputSurfaceCreation)?; let cr = cairo::Context::new(&output_surface); cr.rectangle( @@ -201,24 +175,13 @@ impl Filter for Composite { ); cr.clip(); - input_2_surface.set_as_source_surface(&cr, 0f64, 0f64); - cr.paint(); - - input_surface.set_as_source_surface(&cr, 0f64, 0f64); + input.surface().set_as_source_surface(&cr, 0f64, 0f64); cr.set_operator(self.operator.get().into()); cr.paint(); output_surface }; - let output_surface = - if values.color_interpolation_filters == ColorInterpolationFilters::LinearRgb { - unlinearize_surface(&SharedImageSurface::new(output_surface).unwrap(), bounds) - .map_err(FilterError::OutputSurfaceCreation)? - } else { - output_surface - }; - Ok(FilterResult { name: self.base.result.borrow().clone(), output: FilterOutput { @@ -227,6 +190,11 @@ impl Filter for Composite { }, }) } + + #[inline] + fn is_affected_by_color_interpolation_filters() -> bool { + true + } } impl Parse for Operator { diff --git a/rsvg_internals/src/filters/context.rs b/rsvg_internals/src/filters/context.rs index 22ae8d1c..16639772 100644 --- a/rsvg_internals/src/filters/context.rs +++ b/rsvg_internals/src/filters/context.rs @@ -15,6 +15,7 @@ use drawing_ctx::{self, RsvgDrawingCtx}; use length::RsvgLength; use node::{box_node, RsvgNode}; use paint_server::{self, PaintServer}; +use srgb::{linearize_surface, unlinearize_surface}; use surface_utils::{iterators::Pixels, shared_surface::SharedImageSurface, Pixel}; use unitinterval::UnitInterval; @@ -93,6 +94,11 @@ pub struct FilterContext { drawing_ctx: *mut RsvgDrawingCtx, /// The filter effects region. effects_region: BoundingBox, + /// 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. /// @@ -283,6 +289,7 @@ impl FilterContext { f64::from(width), f64::from(height), ), + processing_linear_rgb: false, affine, paffine, channelmap, @@ -403,7 +410,15 @@ impl FilterContext { /// Stores a filter primitive result into the context. #[inline] - pub fn store_result(&mut self, result: FilterResult) { + pub fn store_result(&mut self, mut result: FilterResult) { + // Unlinearize the surface if needed. + if self.processing_linear_rgb { + // TODO: unwrap() on unlinearize_surface() can panic if we run out of memory for Cairo. + result.output.surface = SharedImageSurface::new( + unlinearize_surface(&result.output.surface, result.output.bounds).unwrap(), + ).unwrap(); + } + if let Some(name) = result.name { self.previous_results.insert(name, result.output.clone()); } @@ -491,7 +506,10 @@ impl FilterContext { } /// Retrieves the filter input surface according to the SVG rules. - pub fn get_input(&self, in_: Option<&Input>) -> Option { + /// + /// Does not take `processing_linear_rgb` into account. + // TODO: pass the errors through. + fn get_input_raw(&self, in_: Option<&Input>) -> Option { if in_.is_none() { // No value => use the last result. // As per the SVG spec, if the filter primitive is the first in the chain, return the @@ -541,6 +559,45 @@ impl FilterContext { .map(FilterInput::PrimitiveOutput), } } + + /// Retrieves the filter input surface according to the SVG rules. + pub fn get_input(&self, in_: Option<&Input>) -> Option { + let raw = self.get_input_raw(in_); + + // Linearize the returned surface if needed. + if raw.is_some() && self.processing_linear_rgb { + let (surface, bounds) = match raw.as_ref().unwrap() { + FilterInput::StandardInput(ref surface) => { + (surface, self.effects_region().rect.unwrap().into()) + } + FilterInput::PrimitiveOutput(FilterOutput { + ref surface, + ref bounds, + }) => (surface, *bounds), + }; + + linearize_surface(surface, bounds) + .ok() + .map(|surface| SharedImageSurface::new(surface).unwrap()) + .map(|surface| match raw.as_ref().unwrap() { + FilterInput::StandardInput(_) => FilterInput::StandardInput(surface), + FilterInput::PrimitiveOutput(output) => { + FilterInput::PrimitiveOutput(FilterOutput { surface, ..*output }) + } + }) + } else { + raw + } + } + + /// Calls the given closure with linear RGB processing enabled. + #[inline] + pub fn with_linear_rgb 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/rsvg_internals/src/filters/ffi.rs b/rsvg_internals/src/filters/ffi.rs index 28d4b0eb..957b38eb 100644 --- a/rsvg_internals/src/filters/ffi.rs +++ b/rsvg_internals/src/filters/ffi.rs @@ -6,8 +6,8 @@ use libc::c_char; use drawing_ctx::RsvgDrawingCtx; use length::RsvgLength; -use node::{NodeType, RsvgCNodeImpl, RsvgNode}; -use state::{ComputedValues, RsvgComputedValues}; +use node::{NodeType, RsvgNode}; +use state::{ColorInterpolationFilters, ComputedValues, RsvgComputedValues}; use surface_utils::shared_surface::SharedImageSurface; use super::context::{FilterContext, RsvgFilterContext}; @@ -51,6 +51,27 @@ pub(super) fn render( node.with_impl(|filter: &T| filter.render(node, ctx)) } +/// The type of the `is_affected_by_color_interpolation_filters` function below. +pub(super) type IsAffectedByColorInterpFunctionType = fn() -> bool; + +/// Container for the filter function pointers. Needed to pass them around with C pointers. +#[derive(Clone, Copy)] +pub(super) struct FilterFunctionPointers { + render: RenderFunctionType, + is_affected_by_color_interpolation_filters: IsAffectedByColorInterpFunctionType, +} + +impl FilterFunctionPointers { + /// Creates a `FilterFunctionPointers` filled with pointers for `T`. + pub(super) fn new() -> Self { + Self { + render: render::, + is_affected_by_color_interpolation_filters: + T::is_affected_by_color_interpolation_filters, + } + } +} + /// Creates a new surface applied the filter. This function will create a context for itself, set up /// the coordinate systems execute all its little primitives and then clean up its own mess. pub fn filter_render( @@ -103,7 +124,17 @@ pub fn filter_render( && c.get_type() < NodeType::FilterPrimitiveLast }) .filter(|c| !c.is_in_error()) - .for_each(|mut c| match c.get_type() { + .map(|c| { + let linear_rgb = { + let cascaded = c.get_cascaded_values(); + let values = cascaded.get(); + + values.color_interpolation_filters == ColorInterpolationFilters::LinearRgb + }; + + (c, linear_rgb) + }) + .for_each(|(mut c, linear_rgb)| match c.get_type() { NodeType::FilterPrimitiveBlend | NodeType::FilterPrimitiveComponentTransfer | NodeType::FilterPrimitiveComposite @@ -111,23 +142,37 @@ pub fn filter_render( | NodeType::FilterPrimitiveImage | NodeType::FilterPrimitiveMerge | NodeType::FilterPrimitiveOffset => { - let render = unsafe { - *(&c.get_c_impl() as *const *const RsvgCNodeImpl as *const RenderFunctionType) + let pointers = unsafe { *(c.get_c_impl() as *const FilterFunctionPointers) }; + + let mut render = |filter_ctx: &mut FilterContext| { + match (pointers.render)(&c, filter_ctx) { + Ok(result) => filter_ctx.store_result(result), + Err(_) => { /* Do nothing for now */ } + } }; - match render(&c, &filter_ctx) { - Ok(result) => filter_ctx.store_result(result), - Err(_) => { /* Do nothing for now */ } + + if (pointers.is_affected_by_color_interpolation_filters)() && linear_rgb { + filter_ctx.with_linear_rgb(render); + } else { + render(&mut filter_ctx); } } _ => { let filter = unsafe { &mut *(c.get_c_impl() as *mut RsvgFilterPrimitive) }; - unsafe { + + let mut render = |filter_ctx: &mut FilterContext| unsafe { (filter.render.unwrap())( &mut c, &c.get_cascaded_values().get() as &ComputedValues as RsvgComputedValues, filter, - &mut filter_ctx, + filter_ctx, ); + }; + + if linear_rgb { + filter_ctx.with_linear_rgb(render); + } else { + render(&mut filter_ctx); } } }); diff --git a/rsvg_internals/src/filters/flood.rs b/rsvg_internals/src/filters/flood.rs index 251bffc3..a4c5d8c5 100644 --- a/rsvg_internals/src/filters/flood.rs +++ b/rsvg_internals/src/filters/flood.rs @@ -87,4 +87,9 @@ impl Filter for Flood { }, }) } + + #[inline] + fn is_affected_by_color_interpolation_filters() -> bool { + false + } } diff --git a/rsvg_internals/src/filters/image.rs b/rsvg_internals/src/filters/image.rs index 4fc1e2e7..af41ec43 100644 --- a/rsvg_internals/src/filters/image.rs +++ b/rsvg_internals/src/filters/image.rs @@ -234,4 +234,9 @@ impl Filter for Image { }, }) } + + #[inline] + fn is_affected_by_color_interpolation_filters() -> bool { + false + } } diff --git a/rsvg_internals/src/filters/merge.rs b/rsvg_internals/src/filters/merge.rs index b528145b..6d19e7ed 100644 --- a/rsvg_internals/src/filters/merge.rs +++ b/rsvg_internals/src/filters/merge.rs @@ -6,9 +6,6 @@ use attributes::Attribute; use handle::RsvgHandle; use node::{NodeResult, NodeTrait, NodeType, RsvgCNodeImpl, RsvgNode}; use property_bag::PropertyBag; -use srgb::{linearize_surface, unlinearize_surface}; -use state::ColorInterpolationFilters; -use state::ComputedValues; use surface_utils::shared_surface::SharedImageSurface; use super::context::{FilterContext, FilterOutput, FilterResult, IRect}; @@ -87,24 +84,16 @@ impl MergeNode { fn render( &self, ctx: &FilterContext, - values: &ComputedValues, bounds: IRect, output_surface: Option, ) -> Result { let input = make_result(ctx.get_input(self.in_.borrow().as_ref()))?; - let input_surface = - if values.color_interpolation_filters == ColorInterpolationFilters::LinearRgb { - SharedImageSurface::new( - linearize_surface(input.surface(), bounds) - .map_err(FilterError::BadInputSurfaceStatus)?, - ).unwrap() - } else { - input.surface().clone() - }; - if output_surface.is_none() { - return Ok(input_surface.into_image_surface()); + return input + .surface() + .copy_surface(bounds) + .map_err(FilterError::OutputSurfaceCreation); } let output_surface = output_surface.unwrap(); @@ -117,7 +106,7 @@ impl MergeNode { ); cr.clip(); - input_surface.set_as_source_surface(&cr, 0f64, 0f64); + input.surface().set_as_source_surface(&cr, 0f64, 0f64); cr.set_operator(cairo::Operator::Over); cr.paint(); @@ -139,37 +128,24 @@ impl Filter for Merge { } let bounds = bounds.into_irect(); - let cascaded = node.get_cascaded_values(); - let values = cascaded.get(); - // Now merge them all. let mut output_surface = None; for child in node .children() .filter(|c| c.get_type() == NodeType::FilterPrimitiveMergeNode) { - output_surface = Some( - child - .with_impl(move |c: &MergeNode| c.render(ctx, values, bounds, output_surface))?, - ); + output_surface = + Some(child.with_impl(move |c: &MergeNode| c.render(ctx, bounds, output_surface))?); } - let output_surface = output_surface - .map(|surface| { - if values.color_interpolation_filters == ColorInterpolationFilters::LinearRgb { - unlinearize_surface(&SharedImageSurface::new(surface).unwrap(), bounds) - } else { - Ok(surface) - } - }) - .unwrap_or_else(|| { - ImageSurface::create( - cairo::Format::ARgb32, - ctx.source_graphic().width(), - ctx.source_graphic().height(), - ) - }) - .map_err(FilterError::OutputSurfaceCreation)?; + let output_surface = match output_surface { + Some(surface) => surface, + None => ImageSurface::create( + cairo::Format::ARgb32, + ctx.source_graphic().width(), + ctx.source_graphic().height(), + ).map_err(FilterError::OutputSurfaceCreation)?, + }; Ok(FilterResult { name: self.base.result.borrow().clone(), @@ -179,4 +155,9 @@ impl Filter for Merge { }, }) } + + #[inline] + fn is_affected_by_color_interpolation_filters() -> bool { + true + } } diff --git a/rsvg_internals/src/filters/mod.rs b/rsvg_internals/src/filters/mod.rs index dab3e3c2..2edc51ed 100644 --- a/rsvg_internals/src/filters/mod.rs +++ b/rsvg_internals/src/filters/mod.rs @@ -44,12 +44,19 @@ trait Filter: NodeTrait { /// If this filter primitive can't be rendered for whatever reason (for instance, a required /// property hasn't been provided), an error is returned. fn render(&self, node: &RsvgNode, ctx: &FilterContext) -> Result; + + /// Returns `true` if this filter primitive is affected by the `color-interpolation-filters` + /// property. + /// + /// Primitives that do color blending (like `feComposite` or `feBlend`) should return `true` + /// here, whereas primitives that don't (like `feOffset`) should return `false`. + fn is_affected_by_color_interpolation_filters() -> bool; } /// The base filter primitive node containing common properties. struct Primitive { - // The purpose of this field is to pass this filter's render function to the C code. - render_function: RenderFunctionType, + // The purpose of this field is to pass this filter's function pointers to the C code. + filter_function_pointers: FilterFunctionPointers, x: Cell>, y: Cell>, @@ -77,7 +84,7 @@ impl Primitive { #[inline] fn new() -> Primitive { Primitive { - render_function: render::, + filter_function_pointers: FilterFunctionPointers::new::(), x: Cell::new(None), y: Cell::new(None), @@ -167,7 +174,7 @@ impl NodeTrait for Primitive { #[inline] fn get_c_impl(&self) -> *const RsvgCNodeImpl { // The code that deals with the return value is in ffi.rs. - self.render_function as *const RenderFunctionType as *const RsvgCNodeImpl + &self.filter_function_pointers as *const FilterFunctionPointers as *const RsvgCNodeImpl } } diff --git a/rsvg_internals/src/filters/offset.rs b/rsvg_internals/src/filters/offset.rs index 246fc7c5..680bb2b9 100644 --- a/rsvg_internals/src/filters/offset.rs +++ b/rsvg_internals/src/filters/offset.rs @@ -111,4 +111,9 @@ impl Filter for Offset { }, }) } + + #[inline] + fn is_affected_by_color_interpolation_filters() -> bool { + false + } } diff --git a/rsvg_internals/src/surface_utils/shared_surface.rs b/rsvg_internals/src/surface_utils/shared_surface.rs index 20de1886..b8737598 100644 --- a/rsvg_internals/src/surface_utils/shared_surface.rs +++ b/rsvg_internals/src/surface_utils/shared_surface.rs @@ -6,6 +6,8 @@ use cairo::{self, ImageSurface}; use cairo_sys; use glib::translate::{Stash, ToGlibPtr}; +use filters::context::IRect; + use super::Pixel; /// Wrapper for a Cairo image surface that allows shared access. @@ -125,6 +127,26 @@ impl SharedImageSurface { cr.set_source_surface(&self.surface, x, y); } + /// Returns a new `ImageSurface` with the same contents as the one stored in this + /// `SharedImageSurface` within the given bounds. + pub fn copy_surface(&self, bounds: IRect) -> Result { + let output_surface = ImageSurface::create(cairo::Format::ARgb32, self.width, self.height)?; + + let cr = cairo::Context::new(&output_surface); + cr.rectangle( + bounds.x0 as f64, + bounds.y0 as f64, + (bounds.x1 - bounds.x0) as f64, + (bounds.y1 - bounds.y0) as f64, + ); + cr.clip(); + + cr.set_source_surface(&self.surface, 0f64, 0f64); + cr.paint(); + + Ok(output_surface) + } + /// Returns a raw pointer to the underlying surface. /// /// # Safety diff --git a/tests/fixtures/reftests/filter-composite-color-interpolation-filters-ref.png b/tests/fixtures/reftests/filter-composite-color-interpolation-filters-ref.png index 3a04ef5a..de7d2c38 100644 Binary files a/tests/fixtures/reftests/filter-composite-color-interpolation-filters-ref.png and b/tests/fixtures/reftests/filter-composite-color-interpolation-filters-ref.png differ diff --git a/tests/fixtures/reftests/svg1.1/filters-composite-02-b-ref.png b/tests/fixtures/reftests/svg1.1/filters-composite-02-b-ref.png index 0cd2f40b..43492334 100644 Binary files a/tests/fixtures/reftests/svg1.1/filters-composite-02-b-ref.png and b/tests/fixtures/reftests/svg1.1/filters-composite-02-b-ref.png differ diff --git a/tests/fixtures/reftests/svg1.1/filters-image-04-f-ref.png b/tests/fixtures/reftests/svg1.1/filters-image-04-f-ref.png index ffe37305..670003f7 100644 Binary files a/tests/fixtures/reftests/svg1.1/filters-image-04-f-ref.png and b/tests/fixtures/reftests/svg1.1/filters-image-04-f-ref.png differ -- cgit v1.2.1