[librsvg: 1/2] Implement vector-effect="non-scaling-stroke"




commit fe5ddfab380530536f61720af79f47ab06106b08
Author: Michael Howell <michael notriddle com>
Date:   Sat Sep 3 14:30:10 2022 -0700

    Implement vector-effect="non-scaling-stroke"
    
    Part-of: <https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/740>

 src/drawing_ctx.rs                                 | 40 +++++++++++++++++++---
 src/layout.rs                                      |  6 +++-
 src/properties.rs                                  |  3 +-
 src/property_defs.rs                               | 14 ++++++++
 .../885-vector-effect-non-scaling-stroke-ref.svg   |  4 +++
 .../885-vector-effect-non-scaling-stroke.svg       |  4 +++
 tests/src/reference.rs                             |  6 ++++
 7 files changed, 71 insertions(+), 6 deletions(-)
---
diff --git a/src/drawing_ctx.rs b/src/drawing_ctx.rs
index e7f0b3bfa..5cbb0a427 100644
--- a/src/drawing_ctx.rs
+++ b/src/drawing_ctx.rs
@@ -1275,7 +1275,12 @@ impl DrawingCtx {
                 cr.set_fill_rule(cairo::FillRule::from(shape.fill_rule));
 
                 path_helper.set()?;
-                let bbox = compute_stroke_and_fill_box(&cr, &shape.stroke, &shape.stroke_paint)?;
+                let bbox = compute_stroke_and_fill_box(
+                    &cr,
+                    &shape.stroke,
+                    &shape.stroke_paint,
+                    &dc.initial_viewport,
+                )?;
 
                 let stroke_paint = shape.stroke_paint.to_user_space(&bbox, view_params, values);
                 let fill_paint = shape.fill_paint.to_user_space(&bbox, view_params, values);
@@ -1292,7 +1297,17 @@ impl DrawingCtx {
 
                             PaintTarget::Stroke => {
                                 path_helper.set()?;
+                                let backup_matrix = if shape.stroke.non_scaling {
+                                    let matrix = cr.matrix();
+                                    cr.set_matrix(dc.initial_viewport.transform.into());
+                                    Some(matrix)
+                                } else {
+                                    None
+                                };
                                 dc.stroke(&cr, an, &stroke_paint)?;
+                                if let Some(matrix) = backup_matrix {
+                                    cr.set_matrix(matrix);
+                                }
                             }
 
                             PaintTarget::Markers => {
@@ -1420,8 +1435,12 @@ impl DrawingCtx {
             }
 
             path.to_cairo(&self.cr, false)?;
-            let bbox =
-                compute_stroke_and_fill_box(&self.cr, &span.stroke, &span.stroke_paint_source)?;
+            let bbox = compute_stroke_and_fill_box(
+                &self.cr,
+                &span.stroke,
+                &span.stroke_paint_source,
+                &self.initial_viewport,
+            )?;
             self.cr.new_path();
 
             if span.is_visible {
@@ -1968,6 +1987,7 @@ fn compute_stroke_and_fill_extents(
     cr: &cairo::Context,
     stroke: &Stroke,
     stroke_paint_source: &PaintSource,
+    initial_viewport: &Viewport,
 ) -> Result<PathExtents, RenderingError> {
     // Dropping the precision of cairo's bezier subdivision, yielding 2x
     // _rendering_ time speedups, are these rather expensive operations
@@ -2000,7 +2020,17 @@ fn compute_stroke_and_fill_extents(
     let stroke_extents = if !stroke.width.approx_eq_cairo(0.0)
         && !matches!(stroke_paint_source, PaintSource::None)
     {
+        let backup_matrix = if stroke.non_scaling {
+            let matrix = cr.matrix();
+            cr.set_matrix(initial_viewport.transform.into());
+            Some(matrix)
+        } else {
+            None
+        };
         let (x0, y0, x1, y1) = cr.stroke_extents()?;
+        if let Some(matrix) = backup_matrix {
+            cr.set_matrix(matrix);
+        }
         Some(Rect::new(x0, y0, x1, y1))
     } else {
         None
@@ -2026,8 +2056,10 @@ fn compute_stroke_and_fill_box(
     cr: &cairo::Context,
     stroke: &Stroke,
     stroke_paint_source: &PaintSource,
+    initial_viewport: &Viewport,
 ) -> Result<BoundingBox, RenderingError> {
-    let extents = compute_stroke_and_fill_extents(cr, stroke, stroke_paint_source)?;
+    let extents =
+        compute_stroke_and_fill_extents(cr, stroke, stroke_paint_source, initial_viewport)?;
 
     let ink_rect = match (extents.fill, extents.stroke) {
         (None, None) => None,
diff --git a/src/layout.rs b/src/layout.rs
index 4be72eb82..c82b47f90 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -18,7 +18,7 @@ use crate::properties::{
     ClipRule, ComputedValues, Direction, FillRule, Filter, FontFamily, FontStretch, FontStyle,
     FontVariant, FontWeight, Isolation, MixBlendMode, Opacity, Overflow, PaintOrder,
     ShapeRendering, StrokeDasharray, StrokeLinecap, StrokeLinejoin, StrokeMiterlimit,
-    TextDecoration, TextRendering, UnicodeBidi, XmlLang,
+    TextDecoration, TextRendering, UnicodeBidi, VectorEffect, XmlLang,
 };
 use crate::rect::Rect;
 use crate::session::Session;
@@ -62,6 +62,8 @@ pub struct Stroke {
     pub line_join: StrokeLinejoin,
     pub dash_offset: f64,
     pub dashes: Box<[f64]>,
+    // https://svgwg.org/svg2-draft/painting.html#non-scaling-stroke
+    pub non_scaling: bool,
 }
 
 /// Paths and basic shapes resolved to a path.
@@ -251,6 +253,7 @@ impl Stroke {
         let line_cap = values.stroke_line_cap();
         let line_join = values.stroke_line_join();
         let dash_offset = values.stroke_dashoffset().0.to_user(params);
+        let non_scaling = values.vector_effect() == VectorEffect::NonScalingStroke;
 
         let dashes = match values.stroke_dasharray() {
             StrokeDasharray(Dasharray::None) => Box::new([]),
@@ -267,6 +270,7 @@ impl Stroke {
             line_join,
             dash_offset,
             dashes,
+            non_scaling,
         }
     }
 }
diff --git a/src/properties.rs b/src/properties.rs
index 7c6c95a43..810b1ec7b 100644
--- a/src/properties.rs
+++ b/src/properties.rs
@@ -484,7 +484,6 @@ make_properties! {
         // "transform-box"            => (PresentationAttr::Yes, unimplemented),
         // "transform-origin"         => (PresentationAttr::Yes, unimplemented),
         "unicode-bidi"                => (PresentationAttr::Yes, unicode_bidi                : UnicodeBidi),
-        // "vector-effect"            => (PresentationAttr::Yes, unimplemented),
         "visibility"                  => (PresentationAttr::Yes, visibility                  : Visibility),
         // "white-space"              => (PresentationAttr::Yes, unimplemented),
         // "word-spacing"             => (PresentationAttr::Yes, unimplemented),
@@ -501,6 +500,7 @@ make_properties! {
         "mix-blend-mode"              => (PresentationAttr::No,  mix_blend_mode              : MixBlendMode),
         "paint-order"                 => (PresentationAttr::Yes, paint_order                 : PaintOrder),
         "text-orientation"            => (PresentationAttr::No,  text_orientation            : 
TextOrientation),
+        "vector-effect"               => (PresentationAttr::Yes, vector_effect               : VectorEffect),
     }
 
     // These are not properties, but presentation attributes.  However,
@@ -750,6 +750,7 @@ impl SpecifiedValues {
         compute!(TextRendering, text_rendering);
         compute!(TransformProperty, transform_property);
         compute!(UnicodeBidi, unicode_bidi);
+        compute!(VectorEffect, vector_effect);
         compute!(Visibility, visibility);
         compute!(Width, width);
         compute!(WritingMode, writing_mode);
diff --git a/src/property_defs.rs b/src/property_defs.rs
index 435e14127..0018bab2c 100644
--- a/src/property_defs.rs
+++ b/src/property_defs.rs
@@ -1153,6 +1153,20 @@ make_property!(
     "plaintext" => Plaintext,
 );
 
+make_property!(
+    /// `vector-effect` property.
+    ///
+    /// SVG2: <https://svgwg.org/svg2-draft/coords.html#VectorEffectProperty>
+    VectorEffect,
+    default: None,
+    inherits_automatically: false,
+
+    identifiers:
+    "none" => None,
+    "non-scaling-stroke" => NonScalingStroke,
+    // non-scaling-size, non-rotation, fixed-position not implemented
+);
+
 make_property!(
     /// `visibility` property.
     ///
diff --git a/tests/fixtures/reftests/bugs-reftests/885-vector-effect-non-scaling-stroke-ref.svg 
b/tests/fixtures/reftests/bugs-reftests/885-vector-effect-non-scaling-stroke-ref.svg
new file mode 100644
index 000000000..5fba0ad31
--- /dev/null
+++ b/tests/fixtures/reftests/bugs-reftests/885-vector-effect-non-scaling-stroke-ref.svg
@@ -0,0 +1,4 @@
+<svg width="500" height="500" xmlns="http://www.w3.org/2000/svg";>
+  <rect fill="none" stroke="green" stroke-width="25" width="100%" height="100%"/>
+  <rect x="0" y="25%" width="100%" height="50%" fill="red" stroke-width="25" stroke="blue"/>
+</svg>
diff --git a/tests/fixtures/reftests/bugs-reftests/885-vector-effect-non-scaling-stroke.svg 
b/tests/fixtures/reftests/bugs-reftests/885-vector-effect-non-scaling-stroke.svg
new file mode 100644
index 000000000..be3937036
--- /dev/null
+++ b/tests/fixtures/reftests/bugs-reftests/885-vector-effect-non-scaling-stroke.svg
@@ -0,0 +1,4 @@
+<svg width="500" height="500" xmlns="http://www.w3.org/2000/svg";>
+  <rect fill="none" stroke="green" stroke-width="25" width="100%" height="100%"/>
+  <rect x="0" y="250%" width="100%" height="500%" transform="scale(1 0.1)" fill="red" 
vector-effect="non-scaling-stroke" stroke-width="25" stroke="blue"/>
+</svg>
diff --git a/tests/src/reference.rs b/tests/src/reference.rs
index a00cb0cef..2234e130a 100644
--- a/tests/src/reference.rs
+++ b/tests/src/reference.rs
@@ -418,3 +418,9 @@ test_svg_reference!(
     "tests/fixtures/reftests/bugs-reftests/875-svg-use-width-height.svg",
     "tests/fixtures/reftests/bugs-reftests/875-svg-use-width-height-ref.svg"
 );
+
+test_svg_reference!(
+    bug_885_vector_effect_non_scaling_stroke,
+    "tests/fixtures/reftests/bugs-reftests/885-vector-effect-non-scaling-stroke.svg",
+    "tests/fixtures/reftests/bugs-reftests/885-vector-effect-non-scaling-stroke-ref.svg"
+);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]