summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2018-06-22 13:40:31 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2018-06-22 13:49:25 +0300
commite915da0935c8351d28a479520621561e49924662 (patch)
tree109a5ff26d72f8338cf5458f217f68b132b94d0e
parent2391a7444c19372c470ff12bb89f2e7dce92459b (diff)
downloadlibrsvg-e915da0935c8351d28a479520621561e49924662.tar.gz
Remove {un,}linearization boilerplate
-rw-r--r--rsvg_internals/src/filters/blend.rs57
-rw-r--r--rsvg_internals/src/filters/bounds.rs20
-rw-r--r--rsvg_internals/src/filters/component_transfer.rs29
-rw-r--r--rsvg_internals/src/filters/composite.rs62
-rw-r--r--rsvg_internals/src/filters/context.rs61
-rw-r--r--rsvg_internals/src/filters/ffi.rs65
-rw-r--r--rsvg_internals/src/filters/flood.rs5
-rw-r--r--rsvg_internals/src/filters/image.rs5
-rw-r--r--rsvg_internals/src/filters/merge.rs59
-rw-r--r--rsvg_internals/src/filters/mod.rs15
-rw-r--r--rsvg_internals/src/filters/offset.rs5
-rw-r--r--rsvg_internals/src/surface_utils/shared_surface.rs22
-rw-r--r--tests/fixtures/reftests/filter-composite-color-interpolation-filters-ref.pngbin7951 -> 7948 bytes
-rw-r--r--tests/fixtures/reftests/svg1.1/filters-composite-02-b-ref.pngbin15918 -> 15917 bytes
-rw-r--r--tests/fixtures/reftests/svg1.1/filters-image-04-f-ref.pngbin12642 -> 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<FilterResult, FilterError> {
+ fn render(&self, _node: &RsvgNode, ctx: &FilterContext) -> Result<FilterResult, FilterError> {
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<FilterResult, FilterError> {
+ fn render(&self, _node: &RsvgNode, ctx: &FilterContext) -> Result<FilterResult, FilterError> {
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<FilterInput> {
+ ///
+ /// Does not take `processing_linear_rgb` into account.
+ // TODO: pass the errors through.
+ fn get_input_raw(&self, in_: Option<&Input>) -> Option<FilterInput> {
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<FilterInput> {
+ 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, 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/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<T: Filter>(
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<T: Filter>() -> Self {
+ Self {
+ render: render::<T>,
+ 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<ImageSurface>,
) -> Result<ImageSurface, FilterError> {
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<FilterResult, FilterError>;
+
+ /// 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<Option<RsvgLength>>,
y: Cell<Option<RsvgLength>>,
@@ -77,7 +84,7 @@ impl Primitive {
#[inline]
fn new<T: Filter>() -> Primitive {
Primitive {
- render_function: render::<T>,
+ filter_function_pointers: FilterFunctionPointers::new::<T>(),
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<ImageSurface, cairo::Status> {
+ 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
--- a/tests/fixtures/reftests/filter-composite-color-interpolation-filters-ref.png
+++ b/tests/fixtures/reftests/filter-composite-color-interpolation-filters-ref.png
Binary files 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
--- a/tests/fixtures/reftests/svg1.1/filters-composite-02-b-ref.png
+++ b/tests/fixtures/reftests/svg1.1/filters-composite-02-b-ref.png
Binary files 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
--- a/tests/fixtures/reftests/svg1.1/filters-image-04-f-ref.png
+++ b/tests/fixtures/reftests/svg1.1/filters-image-04-f-ref.png
Binary files differ