[librsvg: 2/6] Use CSS paint-order when performing fill/stroke/marker operations




commit fbeb998c9f87d21a5f710c1fffbe3ff31be91a1d
Author: John Ledbetter <john ledbetter gmail com>
Date:   Wed Aug 26 14:56:40 2020 -0400

    Use CSS paint-order when performing fill/stroke/marker operations
    
    The paint-order css property allows specifying in which order
    fill, stroke, and marker operations should be performed;
    refactor DrawingCtx#draw_path to handle fill/stroke separately
    and to perform them in the order specified by the paint-order
    property.

 rsvg_internals/src/drawing_ctx.rs                | 148 ++++++++++++-----------
 tests/fixtures/reftests/svg2/paint-order-ref.png | Bin 0 -> 9505 bytes
 tests/fixtures/reftests/svg2/paint-order.svg     |   6 +
 3 files changed, 86 insertions(+), 68 deletions(-)
---
diff --git a/rsvg_internals/src/drawing_ctx.rs b/rsvg_internals/src/drawing_ctx.rs
index 8fbdfc58..e5cbf144 100644
--- a/rsvg_internals/src/drawing_ctx.rs
+++ b/rsvg_internals/src/drawing_ctx.rs
@@ -28,8 +28,8 @@ use crate::path_builder::*;
 use crate::pattern::{PatternContentUnits, PatternUnits, ResolvedPattern};
 use crate::properties::ComputedValues;
 use crate::property_defs::{
-    ClipRule, FillRule, MixBlendMode, Opacity, Overflow, ShapeRendering, StrokeDasharray,
-    StrokeLinecap, StrokeLinejoin, TextRendering,
+    ClipRule, FillRule, MixBlendMode, Opacity, Overflow, PaintTarget, ShapeRendering,
+    StrokeDasharray, StrokeLinecap, StrokeLinejoin, TextRendering,
 };
 use crate::rect::Rect;
 use crate::shapes::Markers;
@@ -1160,63 +1160,56 @@ impl DrawingCtx {
         }
     }
 
-    pub fn stroke_and_fill(
+    pub fn stroke(
         &mut self,
         cr: &cairo::Context,
         acquired_nodes: &mut AcquiredNodes,
         values: &ComputedValues,
-    ) -> Result<BoundingBox, RenderingError> {
-        cr.set_antialias(cairo::Antialias::from(values.shape_rendering()));
+        bbox: &BoundingBox,
+    ) -> Result<(), RenderingError> {
+        let current_color = values.color().0;
 
-        self.setup_cr_for_stroke(cr, values);
+        self.set_source_paint_server(
+            acquired_nodes,
+            &values.stroke().0,
+            values.stroke_opacity().0,
+            bbox,
+            current_color,
+            values,
+        )
+        .map(|had_paint_server| {
+            if had_paint_server {
+                cr.stroke();
+            }
+        })?;
 
-        // Update the bbox in the rendering context.  Below, we actually set the
-        // fill/stroke patterns on the cairo_t.  That process requires the
-        // rendering context to have an updated bbox; for example, for the
-        // coordinate system in patterns.
-        let bbox = compute_stroke_and_fill_box(cr, values);
+        Ok(())
+    }
 
+    pub fn fill(
+        &mut self,
+        cr: &cairo::Context,
+        acquired_nodes: &mut AcquiredNodes,
+        values: &ComputedValues,
+        bbox: &BoundingBox,
+    ) -> Result<(), RenderingError> {
         let current_color = values.color().0;
 
-        let res = self
-            .set_source_paint_server(
-                acquired_nodes,
-                &values.fill().0,
-                values.fill_opacity().0,
-                &bbox,
-                current_color,
-                values,
-            )
-            .map(|had_paint_server| {
-                if had_paint_server {
-                    if values.stroke().0 == PaintServer::None {
-                        cr.fill();
-                    } else {
-                        cr.fill_preserve();
-                    }
-                }
-            })
-            .and_then(|_| {
-                self.set_source_paint_server(
-                    acquired_nodes,
-                    &values.stroke().0,
-                    values.stroke_opacity().0,
-                    &bbox,
-                    current_color,
-                    values,
-                )
-                .map(|had_paint_server| {
-                    if had_paint_server {
-                        cr.stroke();
-                    }
-                })
-            });
-
-        // clear the path in case stroke == fill == None; otherwise
-        // we leave it around from computing the bounding box
-        cr.new_path();
+        self.set_source_paint_server(
+            acquired_nodes,
+            &values.fill().0,
+            values.fill_opacity().0,
+            bbox,
+            current_color,
+            values,
+        )
+        .map(|had_paint_server| {
+            if had_paint_server {
+                cr.fill();
+            }
+        })?;
 
-        res.map(|_: ()| bbox)
+        Ok(())
     }
 
     pub fn draw_path(
@@ -1228,31 +1221,50 @@ impl DrawingCtx {
         markers: Markers,
         clipping: bool,
     ) -> Result<BoundingBox, RenderingError> {
-        if !path.is_empty() {
-            let bbox =
-                self.with_discrete_layer(node, acquired_nodes, values, clipping, &mut |an, dc| {
-                    let cr = dc.cr.clone();
+        if path.is_empty() {
+            return Ok(self.empty_bbox());
+        }
+
+        self.with_discrete_layer(node, acquired_nodes, values, clipping, &mut |an, dc| {
+            let cr = dc.cr.clone();
+            let is_square_linecap = values.stroke_line_cap() == StrokeLinecap::Square;
 
-                    let is_square_linecap = values.stroke_line_cap() == StrokeLinecap::Square;
-                    path.to_cairo(&cr, is_square_linecap)?;
+            cr.set_antialias(cairo::Antialias::from(values.shape_rendering()));
+            dc.setup_cr_for_stroke(&cr, values);
 
-                    if clipping {
-                        cr.set_fill_rule(cairo::FillRule::from(values.clip_rule()));
-                        Ok(dc.empty_bbox())
-                    } else {
-                        cr.set_fill_rule(cairo::FillRule::from(values.fill_rule()));
-                        dc.stroke_and_fill(&cr, an, values)
-                    }
-                })?;
+            let bbox = if clipping {
+                cr.set_fill_rule(cairo::FillRule::from(values.clip_rule()));
+                path.to_cairo(&cr, is_square_linecap)?;
+                dc.empty_bbox()
+            } else {
+                cr.set_fill_rule(cairo::FillRule::from(values.fill_rule()));
+                path.to_cairo(&cr, is_square_linecap)?;
+                let bbox = compute_stroke_and_fill_box(&cr, &values);
+                cr.new_path();
+                bbox
+            };
 
-            if markers == Markers::Yes {
-                marker::render_markers_for_path(path, self, acquired_nodes, values, clipping)?;
+            for &target in &values.paint_order().targets {
+                match target {
+                    PaintTarget::Fill if !clipping => {
+                        path.to_cairo(&cr, is_square_linecap)?;
+                        dc.fill(&cr, an, values, &bbox)?;
+                        cr.new_path();
+                    }
+                    PaintTarget::Stroke if !clipping => {
+                        path.to_cairo(&cr, is_square_linecap)?;
+                        dc.stroke(&cr, an, values, &bbox)?;
+                        cr.new_path();
+                    }
+                    PaintTarget::Markers if markers == Markers::Yes => {
+                        marker::render_markers_for_path(path, dc, an, values, clipping)?;
+                    }
+                    _ => {}
+                }
             }
 
             Ok(bbox)
-        } else {
-            Ok(self.empty_bbox())
-        }
+        })
     }
 
     pub fn draw_image(
diff --git a/tests/fixtures/reftests/svg2/paint-order-ref.png 
b/tests/fixtures/reftests/svg2/paint-order-ref.png
new file mode 100644
index 00000000..b8194522
Binary files /dev/null and b/tests/fixtures/reftests/svg2/paint-order-ref.png differ
diff --git a/tests/fixtures/reftests/svg2/paint-order.svg b/tests/fixtures/reftests/svg2/paint-order.svg
new file mode 100644
index 00000000..74193bae
--- /dev/null
+++ b/tests/fixtures/reftests/svg2/paint-order.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; width="750" height="450">
+<rect height="300" width="1000" fill="#98fb98"/>
+<rect height="300" width="1000" y="300" fill="#87cefa"/>
+<path d="M 200 300 H 1000" stroke="#deb887" stroke-width="37.5"/>
+<path d="M -200 -100 a 100 100 0 0 1 100 -100 h 200 a 100 100 0 0 1 100 100 v 200 a 100 100 0 0 1 -100 100 h 
-200 a 100 100 0 0 1 -100 -100 z" stroke="#deb887" stroke-width="125" fill="white" paint-order="stroke fill" 
transform="matrix(0.6 0 0 0.6 200 300)"/>
+</svg>


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