[librsvg: 1/2] implement hue-rotate filter
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg: 1/2] implement hue-rotate filter
- Date: Tue, 1 Jun 2021 18:49:05 +0000 (UTC)
commit 83e731b583b86ffe9fb813b9f23411c9e9d1b3b5
Author: John Ledbetter <john ledbetter gmail com>
Date: Tue Jun 1 11:15:51 2021 -0400
implement hue-rotate filter
hue-rotate() takes a single optional argument which is either a
literal 0 or an angle; the default value is zero degrees.
src/filter_func.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++
src/filters/color_matrix.rs | 50 ++++++++++++++++-----------------
tests/src/filters.rs | 20 ++++++++++++++
3 files changed, 111 insertions(+), 26 deletions(-)
---
diff --git a/src/filter_func.rs b/src/filter_func.rs
index 593d7c65..ea98731f 100644
--- a/src/filter_func.rs
+++ b/src/filter_func.rs
@@ -16,6 +16,7 @@
use cssparser::{Color, Parser, RGBA};
+use crate::angle::Angle;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::DrawingCtx;
use crate::error::*;
@@ -46,6 +47,7 @@ pub enum FilterFunction {
Contrast(Contrast),
DropShadow(DropShadow),
Grayscale(Grayscale),
+ HueRotate(HueRotate),
Invert(Invert),
Opacity(Opacity),
Saturate(Saturate),
@@ -95,6 +97,14 @@ pub struct Grayscale {
proportion: Option<f64>,
}
+/// Parameters for the `hue-rotate()` filter function
+///
+/// https://www.w3.org/TR/filter-effects/#funcdef-filter-huerotate
+#[derive(Debug, Clone, PartialEq)]
+pub struct HueRotate {
+ angle: Option<Angle>,
+}
+
/// Parameters for the `invert()` filter function
///
/// https://www.w3.org/TR/filter-effects/#funcdef-filter-invert
@@ -222,6 +232,13 @@ fn parse_grayscale<'i>(parser: &mut Parser<'i, '_>) -> Result<FilterFunction, Pa
Ok(FilterFunction::Grayscale(Grayscale { proportion }))
}
+#[allow(clippy::unnecessary_wraps)]
+fn parse_huerotate<'i>(parser: &mut Parser<'i, '_>) -> Result<FilterFunction, ParseError<'i>> {
+ let angle = parser.try_parse(|p| Angle::parse(p)).ok();
+
+ Ok(FilterFunction::HueRotate(HueRotate { angle }))
+}
+
#[allow(clippy::unnecessary_wraps)]
fn parse_invert<'i>(parser: &mut Parser<'i, '_>) -> Result<FilterFunction, ParseError<'i>> {
let proportion = parse_num_or_percentage_clamped(parser);
@@ -443,6 +460,27 @@ impl Grayscale {
}
}
+impl HueRotate {
+ fn to_filter_spec(&self, params: &NormalizeParams) -> FilterSpec {
+ let rads = self.angle.map(|a| a.radians()).unwrap_or(0.0);
+ let user_space_filter = Filter::default().to_user_space(params);
+
+ let huerotate = ResolvedPrimitive {
+ primitive: Primitive::default(),
+ params: PrimitiveParams::ColorMatrix(ColorMatrix {
+ matrix: ColorMatrix::hue_rotate_matrix(rads),
+ ..ColorMatrix::default()
+ }),
+ }
+ .into_user_space(params);
+
+ FilterSpec {
+ user_space_filter,
+ primitives: vec![huerotate],
+ }
+ }
+}
+
impl Invert {
fn to_filter_spec(&self, params: &NormalizeParams) -> FilterSpec {
let p = self.proportion.unwrap_or(1.0);
@@ -584,6 +622,7 @@ impl Parse for FilterFunction {
("contrast", &parse_contrast),
("drop-shadow", &parse_dropshadow),
("grayscale", &parse_grayscale),
+ ("hue-rotate", &parse_huerotate),
("invert", &parse_invert),
("opacity", &parse_opacity),
("saturate", &parse_saturate),
@@ -618,6 +657,7 @@ impl FilterFunction {
FilterFunction::Contrast(v) => Ok(v.to_filter_spec(¶ms)),
FilterFunction::DropShadow(v) => Ok(v.to_filter_spec(¶ms, values.color().0)),
FilterFunction::Grayscale(v) => Ok(v.to_filter_spec(¶ms)),
+ FilterFunction::HueRotate(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)),
@@ -755,6 +795,28 @@ mod tests {
);
}
+ #[test]
+ fn parses_huerotate() {
+ assert_eq!(
+ FilterFunction::parse_str("hue-rotate()").unwrap(),
+ FilterFunction::HueRotate(HueRotate { angle: None })
+ );
+
+ assert_eq!(
+ FilterFunction::parse_str("hue-rotate(0)").unwrap(),
+ FilterFunction::HueRotate(HueRotate {
+ angle: Some(Angle::new(0.0))
+ })
+ );
+
+ assert_eq!(
+ FilterFunction::parse_str("hue-rotate(128deg)").unwrap(),
+ FilterFunction::HueRotate(HueRotate {
+ angle: Some(Angle::from_degrees(128.0))
+ })
+ );
+ }
+
#[test]
fn parses_invert() {
assert_eq!(
@@ -865,6 +927,11 @@ mod tests {
assert!(FilterFunction::parse_str("grayscale(foo)").is_err());
}
+ #[test]
+ fn invalid_huerotate_yields_error() {
+ assert!(FilterFunction::parse_str("hue-rotate(foo)").is_err());
+ }
+
#[test]
fn invalid_invert_yields_error() {
assert!(FilterFunction::parse_str("invert(foo)").is_err());
diff --git a/src/filters/color_matrix.rs b/src/filters/color_matrix.rs
index 030d576b..ee372481 100644
--- a/src/filters/color_matrix.rs
+++ b/src/filters/color_matrix.rs
@@ -114,32 +114,7 @@ impl SetAttributes for FeColorMatrix {
}
OperationType::HueRotate => {
let degrees: f64 = attr.parse(value)?;
- let (sin, cos) = degrees.to_radians().sin_cos();
-
- let a = Matrix3::new(
- 0.213, 0.715, 0.072,
- 0.213, 0.715, 0.072,
- 0.213, 0.715, 0.072,
- );
-
- let b = Matrix3::new(
- 0.787, -0.715, -0.072,
- -0.213, 0.285, -0.072,
- -0.213, -0.715, 0.928,
- );
-
- let c = Matrix3::new(
- -0.213, -0.715, 0.928,
- 0.143, 0.140, -0.283,
- -0.787, 0.715, 0.072,
- );
-
- let top_left = a + b * cos + c * sin;
-
- let mut matrix = top_left.fixed_resize(0.0);
- matrix[(3, 3)] = 1.0;
- matrix[(4, 4)] = 1.0;
- matrix
+ ColorMatrix::hue_rotate_matrix(degrees.to_radians())
}
};
@@ -215,6 +190,29 @@ impl ColorMatrix {
bounds,
})
}
+
+ pub fn hue_rotate_matrix(radians: f64) -> Matrix5<f64> {
+ let (sin, cos) = radians.sin_cos();
+
+ let a = Matrix3::new(
+ 0.213, 0.715, 0.072, 0.213, 0.715, 0.072, 0.213, 0.715, 0.072,
+ );
+
+ let b = Matrix3::new(
+ 0.787, -0.715, -0.072, -0.213, 0.285, -0.072, -0.213, -0.715, 0.928,
+ );
+
+ let c = Matrix3::new(
+ -0.213, -0.715, 0.928, 0.143, 0.140, -0.283, -0.787, 0.715, 0.072,
+ );
+
+ let top_left = a + b * cos + c * sin;
+
+ let mut matrix = top_left.fixed_resize(0.0);
+ matrix[(3, 3)] = 1.0;
+ matrix[(4, 4)] = 1.0;
+ matrix
+ }
}
impl FilterEffect for FeColorMatrix {
diff --git a/tests/src/filters.rs b/tests/src/filters.rs
index a3e63767..b698bd13 100644
--- a/tests/src/filters.rs
+++ b/tests/src/filters.rs
@@ -253,6 +253,26 @@ test_compare_render_output!(
"##,
);
+test_compare_render_output!(
+ huerotate_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="green" filter="hue-rotate(128deg)"/>
+</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="hueRotate" values="128" />
+ </filter>
+ </defs>
+
+ <rect x="100" y="100" width="200" height="200" fill="green" filter="url(#filter)"/>
+</svg>
+"##,
+);
+
test_compare_render_output!(
invert_filter_func,
br##"<?xml version="1.0" encoding="UTF-8"?>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]