[librsvg] (#471): Fix blurry markers when rendering at larger than 1:1 scale



commit 7af8239a30f139179011b4d3072ac69a7e573a27
Author: Federico Mena Quintero <federico gnome org>
Date:   Tue Jun 18 15:07:04 2019 -0500

    (#471): Fix blurry markers when rendering at larger than 1:1 scale
    
    Apparently I didn't understand that
    
      cairo_matrix.scale(x, y)
    
    is not the same as
    
      scale = cairo_matrix_init_scale(x, y);
      cairo_matrix = Matrix::multiply(cairo_matrix, scale)
    
    and we needed the latter.
    
    This includes a new test for rendering markers when the initial affine
    is not at 1:1 scale.
    
    This commit also makes the markers code modify the cr's transformation
    with its own methods, instead of messing with the affine by hand.

 librsvg_crate/tests/primitives.rs | 62 +++++++++++++++++++++++++++++++++++++++
 rsvg_internals/src/drawing_ctx.rs |  6 ++--
 rsvg_internals/src/marker.rs      | 60 +++++++++++++++++--------------------
 3 files changed, 92 insertions(+), 36 deletions(-)
---
diff --git a/librsvg_crate/tests/primitives.rs b/librsvg_crate/tests/primitives.rs
index 13f6f15a..c3941292 100644
--- a/librsvg_crate/tests/primitives.rs
+++ b/librsvg_crate/tests/primitives.rs
@@ -146,6 +146,68 @@ fn simple_opacity_with_scale() {
     );
 }
 
+#[test]
+// https://gitlab.gnome.org/GNOME/librsvg/issues/471
+fn markers_with_scale() {
+    let svg = load_svg(
+        br#"<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg";>
+    <marker id="marker1" refX="10" refY="10" markerWidth="20" markerHeight="20" orient="auto">
+        <path id="marker-path" d="M 20 10 L 0 16 V 4 Z" fill="blue" opacity="0.5"/>
+    </marker>
+    <path d="M 30 100 L 170 100"
+          fill="none" stroke="green"
+          marker-start="url(#marker1)" marker-end="url(#marker1)"/>
+</svg>
+
+"#,
+    );
+
+    let output_surf = render_to_viewport(
+        &svg,
+        SurfaceSize(800, 800),
+        |cr| {
+            cr.scale(4.0, 4.0);
+        },
+        cairo::Rectangle {
+            x: 0.0,
+            y: 0.0,
+            width: 200.0,
+            height: 200.0,
+        },
+    )
+    .unwrap();
+
+    let reference_surf = cairo::ImageSurface::create(cairo::Format::ARgb32, 800, 800).unwrap();
+
+    {
+        let cr = cairo::Context::new(&reference_surf);
+
+        cr.scale(4.0, 4.0);
+
+        cr.move_to(30.0, 100.0);
+        cr.line_to(170.0, 100.0);
+        cr.set_source_rgb(0.0, 0.5, 0.0);
+        cr.set_line_width(1.0);
+        cr.stroke();
+
+        for (x, y) in &[(30.0, 100.0), (170.0, 100.0)] {
+            cr.move_to(x + 20.0 - 10.0, y + 10.0 - 10.0);
+            cr.line_to(x + 0.0 - 10.0, y + 16.0 - 10.0);
+            cr.line_to(x + 0.0 - 10.0, y + 4.0 - 10.0);
+            cr.set_source_rgba(0.0, 0.0, 1.0, 0.5);
+            cr.fill();
+        }
+    }
+
+    let reference_surf = SharedImageSurface::new(reference_surf, SurfaceType::SRgb).unwrap();
+
+    compare_to_surface(
+        &output_surf,
+        &reference_surf,
+        "markers_with_scale",
+    );
+}
+
 #[test]
 fn opacity_inside_transformed_group() {
     let svg = load_svg(
diff --git a/rsvg_internals/src/drawing_ctx.rs b/rsvg_internals/src/drawing_ctx.rs
index ce0a6874..0bb1b2ca 100644
--- a/rsvg_internals/src/drawing_ctx.rs
+++ b/rsvg_internals/src/drawing_ctx.rs
@@ -879,6 +879,7 @@ impl DrawingCtx {
     }
 }
 
+#[derive(Debug)]
 pub struct CompositingAffines {
     pub outside_temporary_surface: cairo::Matrix,
     pub initial: cairo::Matrix,
@@ -907,9 +908,8 @@ impl CompositingAffines {
 
         let for_temporary_surface = if is_topmost_temporary_surface {
             let untransformed = cairo::Matrix::multiply(&current, &initial_inverse);
-            let mut scaled_to_temp_surface = untransformed;
-            scaled_to_temp_surface.scale(scale_x, scale_y);
-            scaled_to_temp_surface
+            let scale = cairo::Matrix::new(scale_x, 0.0, 0.0, scale_y, 0.0, 0.0);
+            cairo::Matrix::multiply(&untransformed, &scale)
         } else {
             current
         };
diff --git a/rsvg_internals/src/marker.rs b/rsvg_internals/src/marker.rs
index 39277f79..5e453621 100644
--- a/rsvg_internals/src/marker.rs
+++ b/rsvg_internals/src/marker.rs
@@ -140,49 +140,43 @@ impl NodeMarker {
             return Ok(());
         }
 
-        let cr = draw_ctx.get_cairo_context();
-
-        let mut affine = cr.get_matrix();
-
-        affine.translate(xpos, ypos);
-
-        let rotation = match self.orient.get() {
-            MarkerOrient::Auto => computed_angle,
-            MarkerOrient::Angle(a) => a,
-        };
+        draw_ctx.with_saved_cr(&mut |dc| {
+            let cr = dc.get_cairo_context();
 
-        affine.rotate(rotation.radians());
+            cr.translate(xpos, ypos);
 
-        if self.units.get() == MarkerUnits::StrokeWidth {
-            affine.scale(line_width, line_width);
-        }
+            let rotation = match self.orient.get() {
+                MarkerOrient::Auto => computed_angle,
+                MarkerOrient::Angle(a) => a,
+            };
 
-        let params = if let Some(vbox) = self.vbox.get() {
-            let (_, _, w, h) = self.aspect.get().compute(
-                &vbox,
-                &Rectangle::new(0.0, 0.0, marker_width, marker_height),
-            );
+            cr.rotate(rotation.radians());
 
-            if vbox.width.approx_eq_cairo(&0.0) || vbox.height.approx_eq_cairo(&0.0) {
-                return Ok(());
+            if self.units.get() == MarkerUnits::StrokeWidth {
+                cr.scale(line_width, line_width);
             }
 
-            affine.scale(w / vbox.width, h / vbox.height);
+            let params = if let Some(vbox) = self.vbox.get() {
+                let (_, _, w, h) = self.aspect.get().compute(
+                    &vbox,
+                    &Rectangle::new(0.0, 0.0, marker_width, marker_height),
+                );
 
-            draw_ctx.push_view_box(vbox.width, vbox.height)
-        } else {
-            draw_ctx.push_view_box(marker_width, marker_height)
-        };
+                if vbox.width.approx_eq_cairo(&0.0) || vbox.height.approx_eq_cairo(&0.0) {
+                    return Ok(());
+                }
 
-        affine.translate(
-            -self.ref_x.get().normalize(&values, &params),
-            -self.ref_y.get().normalize(&values, &params),
-        );
+                cr.scale(w / vbox.width, h / vbox.height);
 
-        draw_ctx.with_saved_cr(&mut |dc| {
-            let cr = dc.get_cairo_context();
+                dc.push_view_box(vbox.width, vbox.height)
+            } else {
+                dc.push_view_box(marker_width, marker_height)
+            };
 
-            cr.set_matrix(affine);
+            cr.translate(
+                -self.ref_x.get().normalize(&values, &params),
+                -self.ref_y.get().normalize(&values, &params),
+            );
 
             if !values.is_overflow() {
                 if let Some(vbox) = self.vbox.get() {


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