[librsvg: 2/3] Implement Sepia filter
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg: 2/3] Implement Sepia filter
- Date: Wed, 12 May 2021 01:35:02 +0000 (UTC)
commit 28d9eeea97a61864b1f95c05992ce56e90436eca
Author: John Ledbetter <john ledbetter gmail com>
Date: Fri May 7 10:34:12 2021 -0400
Implement Sepia filter
The sepia filter takes a single optional argument, which is the
proportion of sepia to apply. This can be provided as either a value
between 0.0 and 1.0, or a percentage 0-100%.
The default value if none is given is 1.0. If a value greater than
1.0 is provided, it is treated as 1.0. If a value less than zero is
given, the default value is used.
See:
* https://www.w3.org/TR/filter-effects/#sepiaEquivalent
* https://www.w3.org/TR/filter-effects/#funcdef-filter-sepia
src/filter_func.rs | 103 +++++++++++++++++++++++++++++++++++++++++++-
src/filters/color_matrix.rs | 6 +--
tests/src/filters.rs | 61 ++++++++++++++++++++++++++
3 files changed, 165 insertions(+), 5 deletions(-)
---
diff --git a/src/filter_func.rs b/src/filter_func.rs
index fe9f75fa..a0ebeea1 100644
--- a/src/filter_func.rs
+++ b/src/filter_func.rs
@@ -1,6 +1,5 @@
use cssparser::Parser;
-use crate::coord_units::CoordUnits;
use crate::drawing_ctx::DrawingCtx;
use crate::error::*;
use crate::filter::Filter;
@@ -9,8 +8,9 @@ use crate::filters::{
ResolvedPrimitive,
};
use crate::length::*;
-use crate::parsers::Parse;
+use crate::parsers::{NumberOrPercentage, Parse};
use crate::properties::ComputedValues;
+use crate::{coord_units::CoordUnits, filters::color_matrix::ColorMatrix};
/// CSS Filter functions from the Filter Effects Module Level 1
///
@@ -18,6 +18,7 @@ use crate::properties::ComputedValues;
#[derive(Debug, Clone, PartialEq)]
pub enum FilterFunction {
Blur(Blur),
+ Sepia(Sepia),
}
/// Parameters for the `blur()` filter function
@@ -28,6 +29,14 @@ pub struct Blur {
std_deviation: Option<Length<Both>>,
}
+/// Parameters for the `sepia()` filter function
+///
+/// https://www.w3.org/TR/filter-effects/#funcdef-filter-sepia
+#[derive(Debug, Clone, PartialEq)]
+pub struct Sepia {
+ proportion: Option<f64>,
+}
+
fn parse_function<'i, F>(
parser: &mut Parser<'i, '_>,
name: &str,
@@ -55,6 +64,17 @@ fn parse_blur<'i>(parser: &mut Parser<'i, '_>) -> Result<FilterFunction, ParseEr
}))
}
+#[allow(clippy::unnecessary_wraps)]
+fn parse_sepia<'i>(parser: &mut Parser<'i, '_>) -> Result<FilterFunction, ParseError<'i>> {
+ let proportion = match parser.try_parse(|p| NumberOrPercentage::parse(p)) {
+ Ok(NumberOrPercentage { value }) if value < 0.0 => None,
+ Ok(NumberOrPercentage { value }) => Some(value.clamp(0.0, 1.0)),
+ Err(_) => None,
+ };
+
+ Ok(FilterFunction::Sepia(Sepia { proportion }))
+}
+
impl Blur {
fn to_filter_spec(&self, params: &NormalizeParams) -> FilterSpec {
// The 0.0 default is from the spec
@@ -78,6 +98,39 @@ impl Blur {
}
}
+impl Sepia {
+ #[rustfmt::skip]
+ fn matrix(&self) -> nalgebra::Matrix5<f64> {
+ let p = self.proportion.unwrap_or(1.0);
+
+ nalgebra::Matrix5::new(
+ 0.393 + 0.607 * (1.0 - p), 0.769 - 0.769 * (1.0 - p), 0.189 - 0.189 * (1.0 - p), 0.0, 0.0,
+ 0.349 - 0.349 * (1.0 - p), 0.686 + 0.314 * (1.0 - p), 0.168 - 0.168 * (1.0 - p), 0.0, 0.0,
+ 0.272 - 0.272 * (1.0 - p), 0.534 - 0.534 * (1.0 - p), 0.131 + 0.869 * (1.0 - p), 0.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0, 1.0,
+ )
+ }
+
+ fn to_filter_spec(&self, params: &NormalizeParams) -> FilterSpec {
+ let user_space_filter = Filter::default().to_user_space(params);
+
+ let sepia = ResolvedPrimitive {
+ primitive: Primitive::default(),
+ params: PrimitiveParams::ColorMatrix(ColorMatrix {
+ matrix: self.matrix(),
+ ..ColorMatrix::default()
+ }),
+ }
+ .into_user_space(params);
+
+ FilterSpec {
+ user_space_filter,
+ primitives: vec![sepia],
+ }
+ }
+}
+
impl Parse for FilterFunction {
fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
let loc = parser.current_source_location();
@@ -86,6 +139,10 @@ impl Parse for FilterFunction {
return Ok(func);
}
+ if let Ok(func) = parser.try_parse(|p| parse_function(p, "sepia", parse_sepia)) {
+ return Ok(func);
+ }
+
return Err(loc.new_custom_error(ValueErrorKind::parse_error("expected filter function")));
}
}
@@ -104,6 +161,7 @@ impl FilterFunction {
match self {
FilterFunction::Blur(v) => Ok(v.to_filter_spec(¶ms)),
+ FilterFunction::Sepia(v) => Ok(v.to_filter_spec(¶ms)),
}
}
}
@@ -129,9 +187,50 @@ mod tests {
);
}
+ #[test]
+ fn parses_sepia() {
+ assert_eq!(
+ FilterFunction::parse_str("sepia()").unwrap(),
+ FilterFunction::Sepia(Sepia { proportion: None })
+ );
+
+ assert_eq!(
+ FilterFunction::parse_str("sepia(80%)").unwrap(),
+ FilterFunction::Sepia(Sepia {
+ proportion: Some(0.80_f32.into())
+ })
+ );
+
+ assert_eq!(
+ FilterFunction::parse_str("sepia(0.52)").unwrap(),
+ FilterFunction::Sepia(Sepia {
+ proportion: Some(0.52_f32.into())
+ })
+ );
+
+ // values > 1.0 should be clamped to 1.0
+ assert_eq!(
+ FilterFunction::parse_str("sepia(1.5)").unwrap(),
+ FilterFunction::Sepia(Sepia {
+ proportion: Some(1.0)
+ })
+ );
+
+ // negative numbers are invalid.
+ assert_eq!(
+ FilterFunction::parse_str("sepia(-1)").unwrap(),
+ FilterFunction::Sepia(Sepia { proportion: None }),
+ );
+ }
+
#[test]
fn invalid_blur_yields_error() {
assert!(FilterFunction::parse_str("blur(foo)").is_err());
assert!(FilterFunction::parse_str("blur(42 43)").is_err());
}
+
+ #[test]
+ fn invalid_sepia_yields_error() {
+ assert!(FilterFunction::parse_str("sepia(foo)").is_err());
+ }
}
diff --git a/src/filters/color_matrix.rs b/src/filters/color_matrix.rs
index ac5f937b..030d576b 100644
--- a/src/filters/color_matrix.rs
+++ b/src/filters/color_matrix.rs
@@ -44,9 +44,9 @@ pub struct FeColorMatrix {
/// Resolved `feColorMatrix` primitive for rendering.
#[derive(Clone)]
pub struct ColorMatrix {
- in1: Input,
- matrix: Matrix5<f64>,
- color_interpolation_filters: ColorInterpolationFilters,
+ pub in1: Input,
+ pub matrix: Matrix5<f64>,
+ pub color_interpolation_filters: ColorInterpolationFilters,
}
impl Default for ColorMatrix {
diff --git a/tests/src/filters.rs b/tests/src/filters.rs
index fce32516..ca4a8fa4 100644
--- a/tests/src/filters.rs
+++ b/tests/src/filters.rs
@@ -155,3 +155,64 @@ fn blur_filter_func() {
.compare(&output_surf)
.evaluate(&output_surf, "blur_filter_func");
}
+
+#[test]
+fn sepia_filter_func() {
+ // Create an element with a filter function, and compare it to the
+ // supposed equivalent using the <filter> element.
+ let svg = load_svg(
+ br##"<?xml version="1.0" encoding="UTF-8"?>
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400"
height="400">
+ <rect x="100" y="100" width="200" height="200" fill="lime" filter="sepia(0.75)"/>
+</svg>
+"##,
+ ).unwrap();
+
+ let output_surf = render_document(
+ &svg,
+ SurfaceSize(400, 400),
+ |_| (),
+ cairo::Rectangle {
+ x: 0.0,
+ y: 0.0,
+ width: 400.0,
+ height: 400.0,
+ },
+ )
+ .unwrap();
+
+ let reference = load_svg(
+ br##"<?xml version="1.0" encoding="UTF-8"?>
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400"
height="400">
+ <defs>
+ <filter id="filter">
+ <feColorMatrix type="matrix"
+ values="0.5447500000000001 0.57675 0.14175 0 0
+ 0.26175 0.7645000000000001 0.126 0 0
+ 0.20400000000000001 0.4005 0.34825 0 0
+ 0 0 0 1 0"/>
+ </filter>
+ </defs>
+
+ <rect x="100" y="100" width="200" height="200" fill="lime" filter="url(#filter)"/>
+</svg>
+"##,
+ ).unwrap();
+
+ let reference_surf = render_document(
+ &reference,
+ SurfaceSize(400, 400),
+ |_| (),
+ cairo::Rectangle {
+ x: 0.0,
+ y: 0.0,
+ width: 400.0,
+ height: 400.0,
+ },
+ )
+ .unwrap();
+
+ Reference::from_surface(reference_surf.into_image_surface().unwrap())
+ .compare(&output_surf)
+ .evaluate(&output_surf, "sepia_filter_func");
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]