summaryrefslogtreecommitdiff
path: root/rsvg_internals/src/filters/context.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rsvg_internals/src/filters/context.rs')
-rw-r--r--rsvg_internals/src/filters/context.rs61
1 files changed, 59 insertions, 2 deletions
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 {