[librsvg: 3/9] Implement `invert` filter function
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg: 3/9] Implement `invert` filter function
- Date: Wed, 26 May 2021 15:04:44 +0000 (UTC)
commit 637221b620cfeb73f7ac539bab6b35b1b83f17e4
Author: John Ledbetter <john ledbetter gmail com>
Date: Wed May 19 15:39:49 2021 -0400
Implement `invert` filter function
invert() accepts a single optional argument which is the proportion of
the transformation to apply. It's implemented as a
FeComponentTransfer filter.
See:
* https://www.w3.org/TR/filter-effects/#funcdef-filter-invert
* https://www.w3.org/TR/filter-effects/#invertEquivalent
src/filter_func.rs | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++-
tests/src/filters.rs | 24 ++++++++++++++++
2 files changed, 101 insertions(+), 1 deletion(-)
---
diff --git a/src/filter_func.rs b/src/filter_func.rs
index 4ac29065..34c9a2a6 100644
--- a/src/filter_func.rs
+++ b/src/filter_func.rs
@@ -19,6 +19,7 @@ use crate::{drawing_ctx::DrawingCtx, filters::component_transfer};
pub enum FilterFunction {
Blur(Blur),
Grayscale(Grayscale),
+ Invert(Invert),
Opacity(Opacity),
Saturate(Saturate),
Sepia(Sepia),
@@ -40,6 +41,14 @@ pub struct Grayscale {
proportion: Option<f64>,
}
+/// Parameters for the `invert()` filter function
+///
+/// https://www.w3.org/TR/filter-effects/#funcdef-filter-invert
+#[derive(Debug, Clone, PartialEq)]
+pub struct Invert {
+ proportion: Option<f64>,
+}
+
/// Parameters for the `opacity()` filter function
///
/// https://www.w3.org/TR/filter-effects/#funcdef-filter-opacity
@@ -114,6 +123,13 @@ fn parse_grayscale<'i>(parser: &mut Parser<'i, '_>) -> Result<FilterFunction, Pa
Ok(FilterFunction::Grayscale(Grayscale { proportion }))
}
+#[allow(clippy::unnecessary_wraps)]
+fn parse_invert<'i>(parser: &mut Parser<'i, '_>) -> Result<FilterFunction, ParseError<'i>> {
+ let proportion = parse_num_or_percentage_clamped(parser);
+
+ Ok(FilterFunction::Invert(Invert { proportion }))
+}
+
#[allow(clippy::unnecessary_wraps)]
fn parse_opacity<'i>(parser: &mut Parser<'i, '_>) -> Result<FilterFunction, ParseError<'i>> {
let proportion = parse_num_or_percentage_clamped(parser);
@@ -171,6 +187,44 @@ impl Grayscale {
}
}
+impl Invert {
+ fn to_filter_spec(&self, params: &NormalizeParams) -> FilterSpec {
+ let p = self.proportion.unwrap_or(1.0);
+ let user_space_filter = Filter::default().to_user_space(params);
+
+ let invert = ResolvedPrimitive {
+ primitive: Primitive::default(),
+ params: PrimitiveParams::ComponentTransfer(component_transfer::ComponentTransfer {
+ functions: component_transfer::Functions {
+ r: component_transfer::FeFuncR {
+ function_type: component_transfer::FunctionType::Table,
+ table_values: vec![p, 1.0 - p],
+ ..component_transfer::FeFuncR::default()
+ },
+ g: component_transfer::FeFuncG {
+ function_type: component_transfer::FunctionType::Table,
+ table_values: vec![p, 1.0 - p],
+ ..component_transfer::FeFuncG::default()
+ },
+ b: component_transfer::FeFuncB {
+ function_type: component_transfer::FunctionType::Table,
+ table_values: vec![p, 1.0 - p],
+ ..component_transfer::FeFuncB::default()
+ },
+ ..component_transfer::Functions::default()
+ },
+ ..component_transfer::ComponentTransfer::default()
+ }),
+ }
+ .into_user_space(params);
+
+ FilterSpec {
+ user_space_filter,
+ primitives: vec![invert],
+ }
+ }
+}
+
impl Opacity {
fn to_filter_spec(&self, params: &NormalizeParams) -> FilterSpec {
let p = self.proportion.unwrap_or(1.0);
@@ -179,7 +233,7 @@ impl Opacity {
let opacity = ResolvedPrimitive {
primitive: Primitive::default(),
params: PrimitiveParams::ComponentTransfer(component_transfer::ComponentTransfer {
- functions: crate::filters::component_transfer::Functions {
+ functions: component_transfer::Functions {
a: component_transfer::FeFuncA {
function_type: component_transfer::FunctionType::Table,
table_values: vec![0.0, p],
@@ -271,6 +325,7 @@ impl Parse for FilterFunction {
let fns: Vec<(&str, &dyn Fn(&mut Parser<'i, '_>) -> _)> = vec![
("blur", &parse_blur),
("grayscale", &parse_grayscale),
+ ("invert", &parse_invert),
("opacity", &parse_opacity),
("saturate", &parse_saturate),
("sepia", &parse_sepia),
@@ -301,6 +356,7 @@ impl FilterFunction {
match self {
FilterFunction::Blur(v) => Ok(v.to_filter_spec(¶ms)),
FilterFunction::Grayscale(v) => Ok(v.to_filter_spec(¶ms)),
+ FilterFunction::Invert(v) => Ok(v.to_filter_spec(¶ms)),
FilterFunction::Opacity(v) => Ok(v.to_filter_spec(¶ms)),
FilterFunction::Saturate(v) => Ok(v.to_filter_spec(¶ms)),
FilterFunction::Sepia(v) => Ok(v.to_filter_spec(¶ms)),
@@ -344,6 +400,21 @@ mod tests {
);
}
+ #[test]
+ fn parses_invert() {
+ assert_eq!(
+ FilterFunction::parse_str("invert()").unwrap(),
+ FilterFunction::Invert(Invert { proportion: None })
+ );
+
+ assert_eq!(
+ FilterFunction::parse_str("invert(50%)").unwrap(),
+ FilterFunction::Invert(Invert {
+ proportion: Some(0.50_f32.into()),
+ })
+ );
+ }
+
#[test]
fn parses_opacity() {
assert_eq!(
@@ -421,6 +492,11 @@ mod tests {
assert!(FilterFunction::parse_str("grayscale(foo)").is_err());
}
+ #[test]
+ fn invalid_invert_yields_error() {
+ assert!(FilterFunction::parse_str("invert(foo)").is_err());
+ }
+
#[test]
fn invalid_opacity_yields_error() {
assert!(FilterFunction::parse_str("opacity(foo)").is_err());
diff --git a/tests/src/filters.rs b/tests/src/filters.rs
index 868bbed4..7217f127 100644
--- a/tests/src/filters.rs
+++ b/tests/src/filters.rs
@@ -178,6 +178,30 @@ test_compare_render_output!(
"##,
);
+test_compare_render_output!(
+ invert_filter_func,
+ 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="invert(0.75)"/>
+</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">
+ <feComponentTransfer>
+ <feFuncR type="table" tableValues="0.75 0.25" />
+ <feFuncG type="table" tableValues="0.75 0.25" />
+ <feFuncB type="table" tableValues="0.75 0.25" />
+ </feComponentTransfer>
+ </filter>
+ </defs>
+
+ <rect x="100" y="100" width="200" height="200" fill="lime" filter="url(#filter)"/>
+</svg>
+"##,
+);
+
test_compare_render_output!(
opacity_filter_func,
br##"<?xml version="1.0" encoding="UTF-8"?>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]