[librsvg/rustification] RsvgGradientStop: store whether the stop's values are valid



commit 4006aff6cfc1648660be27fb38b8bbb20ea813eb
Author: Federico Mena Quintero <federico gnome org>
Date:   Fri Nov 11 10:58:40 2016 -0600

    RsvgGradientStop: store whether the stop's values are valid
    
    The spec mandates that values for stop offsets are only plain numbers or
    percentages.  We used to handle this more or less implicitly by calling
    _rsvg_css_hand_normalize_length(), using the resulting value, and
    dropping the units.  Now we actually ensure that we get plain numbers
    or percentages, and eliminate this one use of _rsvg_css_hand_normalize_length().
    
    Now, RsvgGradientStop has an is_valid field that says whether the stop
    has valid units.  At rendering time, we can decide what to do about
    gradients with invalid stops:  currently we don't add invalid stops to
    the gradient, but we still render it up to the last valid stop -
    hopefully to make it easier to debug SVG files.
    
    Also, we now ensure that stop offsets are actually in nondecreasing
    order, per the spec.
    
    (I really want to get rid of _rsvg_css_hand_normalize_length(), but
    found these things along the way...)

 rsvg-cairo-draw.c   |   40 +++++++++++++++++++++++++++++++++++-----
 rsvg-paint-server.c |   24 +++++++++++++++++-------
 rsvg-paint-server.h |    1 +
 3 files changed, 53 insertions(+), 12 deletions(-)
---
diff --git a/rsvg-cairo-draw.c b/rsvg-cairo-draw.c
index d67b310..7ca45da 100644
--- a/rsvg-cairo-draw.c
+++ b/rsvg-cairo-draw.c
@@ -41,14 +41,25 @@
 
 #include <pango/pangocairo.h>
 
-static void
-_pattern_add_rsvg_color_stops (cairo_pattern_t * pattern,
-                               GPtrArray * stops, guint8 opacity)
+/* Adds the color stops from the array of child nodes (of type RSVG_NODE_TYPE_STOP)
+ * to a cairo_pattern_t.
+ *
+ * Returns true if the stops are all valid and conform to the SVG spec; false
+ * otherwise.  It is up to the caller to decide whether to render the gradient
+ * in this last case: it will have color stops added only up to the first invalid
+ * stop.
+ */
+static gboolean
+add_color_stops_for_gradient (cairo_pattern_t * pattern,
+                              GPtrArray * stops, guint8 opacity)
 {
     gsize i;
     RsvgGradientStop *stop;
     RsvgNode *node;
     guint32 rgba;
+    double last_offset;
+
+    last_offset = 0.0;
 
     for (i = 0; i < stops->len; i++) {
         node = (RsvgNode *) g_ptr_array_index (stops, i);
@@ -56,6 +67,15 @@ _pattern_add_rsvg_color_stops (cairo_pattern_t * pattern,
             continue;
 
         stop = (RsvgGradientStop *) node;
+
+        if (!stop->is_valid)
+            return FALSE;
+
+        if (stop->offset < last_offset)
+            return FALSE;
+
+        last_offset = stop->offset;
+
         rgba = stop->rgba;
         cairo_pattern_add_color_stop_rgba (pattern, stop->offset,
                                            ((rgba >> 24) & 0xff) / 255.0,
@@ -63,6 +83,8 @@ _pattern_add_rsvg_color_stops (cairo_pattern_t * pattern,
                                            ((rgba >> 8) & 0xff) / 255.0,
                                            (((rgba >> 0) & 0xff) * opacity) / 255.0 / 255.0);
     }
+
+    return TRUE;
 }
 
 static void
@@ -100,7 +122,11 @@ _set_source_rsvg_linear_gradient (RsvgDrawingCtx * ctx,
     cairo_pattern_set_matrix (pattern, &matrix);
     cairo_pattern_set_extend (pattern, linear->spread);
 
-    _pattern_add_rsvg_color_stops (pattern, linear->super.children, opacity);
+    /* We ignore the return value of add_color_stops_for_gradient(), which is
+     * whether the stops are conformant to the spec.  This is so that we can
+     * render a partially-generated gradient if some of the stops are invalid.
+     */
+    add_color_stops_for_gradient (pattern, linear->super.children, opacity);
 
     cairo_set_source (cr, pattern);
     cairo_pattern_destroy (pattern);
@@ -143,7 +169,11 @@ _set_source_rsvg_radial_gradient (RsvgDrawingCtx * ctx,
     cairo_pattern_set_matrix (pattern, &matrix);
     cairo_pattern_set_extend (pattern, radial->spread);
 
-    _pattern_add_rsvg_color_stops (pattern, radial->super.children, opacity);
+    /* We ignore the return value of add_color_stops_for_gradient(), which is
+     * whether the stops are conformant to the spec.  This is so that we can
+     * render a partially-generated gradient if some of the stops are invalid.
+     */
+    add_color_stops_for_gradient (pattern, radial->super.children, opacity);
 
     cairo_set_source (cr, pattern);
     cairo_pattern_destroy (pattern);
diff --git a/rsvg-paint-server.c b/rsvg-paint-server.c
index d2b705f..e03040f 100644
--- a/rsvg-paint-server.c
+++ b/rsvg-paint-server.c
@@ -149,7 +149,6 @@ rsvg_paint_server_unref (RsvgPaintServer * ps)
 static void
 rsvg_stop_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts)
 {
-    double offset = 0;
     gboolean is_current_color = FALSE;
     const char *value;
     RsvgGradientStop *stop;
@@ -161,13 +160,23 @@ rsvg_stop_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts)
         if ((value = rsvg_property_bag_lookup (atts, "offset"))) {
             /* either a number [0,1] or a percentage */
             RsvgLength length = rsvg_length_parse (value, LENGTH_DIR_BOTH);
-            offset = _rsvg_css_hand_normalize_length (&length, rsvg_dpi_percentage (ctx), 1., 0.);
 
-            if (offset < 0.)
-                offset = 0.;
-            else if (offset > 1.)
-                offset = 1.;
-            stop->offset = offset;
+            if (length.unit == LENGTH_UNIT_DEFAULT || length.unit == LENGTH_UNIT_PERCENT) {
+                double offset;
+
+                offset = length.length;
+
+                if (offset < 0.0)
+                    offset = 0.0;
+                else if (offset > 1.0)
+                    offset = 1.0;
+
+                stop->offset = offset;
+                stop->is_valid = TRUE;
+            } else {
+                /* Only default and percent values are allowed */
+                stop->is_valid = FALSE;
+            }
         }
         if ((value = rsvg_property_bag_lookup (atts, "style")))
             rsvg_parse_style (ctx, self->state, value);
@@ -198,6 +207,7 @@ rsvg_new_stop (void)
     stop->super.set_atts = rsvg_stop_set_atts;
     stop->offset = 0;
     stop->rgba = 0;
+    stop->is_valid = FALSE;
     return &stop->super;
 }
 
diff --git a/rsvg-paint-server.h b/rsvg-paint-server.h
index cb966c7..369ad4d 100644
--- a/rsvg-paint-server.h
+++ b/rsvg-paint-server.h
@@ -48,6 +48,7 @@ struct _RsvgGradientStop {
     RsvgNode super;
     double offset;
     guint32 rgba;
+    gboolean is_valid;
 };
 
 struct _RsvgLinearGradient {


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