[librsvg] gradient.rs: Fully move gradients to Rust. Yay!



commit 7c991a72cecaed2e8252682f5b6e3c1b74694f61
Author: Federico Mena Quintero <federico gnome org>
Date:   Tue Jun 6 17:16:44 2017 -0500

    gradient.rs: Fully move gradients to Rust.  Yay!
    
    We introduce NodeGradient, and move all the machinery to turn a node
    with children for color stops into our existing Gradient structure.

 rsvg-base.c          |    6 +-
 rsvg-cairo-draw.c    |  100 +---------------
 rsvg-paint-server.c  |  181 -----------------------------
 rsvg-paint-server.h  |   78 +------------
 rust/src/gradient.rs |  312 +++++++++++++++++++++++++++-----------------------
 rust/src/lib.rs      |    6 +-
 6 files changed, 186 insertions(+), 497 deletions(-)
---
diff --git a/rsvg-base.c b/rsvg-base.c
index 5205424..deb2d54 100644
--- a/rsvg-base.c
+++ b/rsvg-base.c
@@ -263,7 +263,7 @@ static const NodeCreator node_creators[] = {
     { "circle",              TRUE,  rsvg_node_circle_new },
     { "clipPath",            TRUE,  rsvg_new_clip_path },
     /* "color-profile",      FALSE, */
-    { "conicalGradient",     TRUE,  rsvg_new_radial_gradient },
+    { "conicalGradient",     TRUE,  rsvg_node_radial_gradient_new },
     /* "cursor",             FALSE, */
     { "defs",                TRUE,  rsvg_node_defs_new },
     /* "desc",               TRUE,  */
@@ -306,7 +306,7 @@ static const NodeCreator node_creators[] = {
     /* "hkern",              FALSE, */
     { "image",               TRUE,  rsvg_new_image },
     { "line",                TRUE,  rsvg_node_line_new },
-    { "linearGradient",      TRUE,  rsvg_new_linear_gradient },
+    { "linearGradient",      TRUE,  rsvg_node_linear_gradient_new },
     { "marker",              TRUE,  rsvg_node_marker_new },
     { "mask",                TRUE,  rsvg_new_mask },
     /* "metadata",           FALSE, */
@@ -317,7 +317,7 @@ static const NodeCreator node_creators[] = {
     { "pattern",             TRUE,  rsvg_node_pattern_new },
     { "polygon",             TRUE,  rsvg_node_polygon_new },
     { "polyline",            TRUE,  rsvg_node_polyline_new },
-    { "radialGradient",      TRUE,  rsvg_new_radial_gradient },
+    { "radialGradient",      TRUE,  rsvg_node_radial_gradient_new },
     { "rect",                TRUE,  rsvg_node_rect_new },
     /* "script",             FALSE, */
     /* "set",                FALSE, */
diff --git a/rsvg-cairo-draw.c b/rsvg-cairo-draw.c
index d240987..76abbb3 100644
--- a/rsvg-cairo-draw.c
+++ b/rsvg-cairo-draw.c
@@ -42,93 +42,6 @@
 #include <pango/pangocairo.h>
 #include <pango/pangofc-fontmap.h>
 
-static Gradient *
-linear_gradient_to_rust (RsvgNode *node)
-{
-    RsvgLinearGradient *linear;
-    Gradient *gradient;
-
-    g_assert (rsvg_node_get_type (node) == RSVG_NODE_TYPE_LINEAR_GRADIENT);
-    linear = rsvg_rust_cnode_get_impl (node);
-
-    gradient = gradient_linear_new (linear->hasx1 ? &linear->x1 : NULL,
-                                    linear->hasy1 ? &linear->y1 : NULL,
-                                    linear->hasx2 ? &linear->x2 : NULL,
-                                    linear->hasy2 ? &linear->y2 : NULL,
-                                    linear->hasbbox ? &linear->obj_bbox : NULL,
-                                    linear->hastransform ? &linear->affine : NULL,
-                                    linear->hasspread ? &linear->spread : NULL,
-                                    linear->fallback);
-
-    gradient_add_color_stops_from_node (gradient, node);
-
-    return gradient;
-}
-
-static Gradient *
-radial_gradient_to_rust (RsvgNode *node)
-{
-    RsvgRadialGradient *radial;
-    Gradient *gradient;
-
-    g_assert (rsvg_node_get_type (node) == RSVG_NODE_TYPE_RADIAL_GRADIENT);
-    radial = rsvg_rust_cnode_get_impl (node);
-
-    gradient = gradient_radial_new (radial->hascx ? &radial->cx : NULL,
-                                    radial->hascy ? &radial->cy : NULL,
-                                    radial->hasr  ? &radial->r  : NULL,
-                                    radial->hasfx ? &radial->fx : NULL,
-                                    radial->hasfy ? &radial->fy : NULL,
-                                    radial->hasbbox ? &radial->obj_bbox : NULL,
-                                    radial->hastransform ? &radial->affine : NULL,
-                                    radial->hasspread ? &radial->spread : NULL,
-                                    radial->fallback);
-
-    gradient_add_color_stops_from_node (gradient, node);
-
-    return gradient;
-}
-
-Gradient *
-rsvg_gradient_node_to_rust_gradient (RsvgNode *node)
-{
-    if (rsvg_node_get_type (node) == RSVG_NODE_TYPE_LINEAR_GRADIENT) {
-        return linear_gradient_to_rust (node);
-    } else if (rsvg_node_get_type (node) == RSVG_NODE_TYPE_RADIAL_GRADIENT) {
-        return radial_gradient_to_rust (node);
-    } else {
-        return NULL;
-    }
-}
-
-static void
-_set_source_rsvg_linear_gradient (RsvgDrawingCtx *ctx,
-                                  RsvgNode *node,
-                                  guint8 opacity, RsvgBbox bbox)
-{
-    Gradient *gradient;
-
-    gradient = linear_gradient_to_rust (node);
-
-    gradient_resolve_fallbacks_and_set_pattern (gradient, ctx, opacity, bbox);
-
-    gradient_destroy (gradient);
-}
-
-static void
-_set_source_rsvg_radial_gradient (RsvgDrawingCtx * ctx,
-                                  RsvgNode *node,
-                                  guint8 opacity, RsvgBbox bbox)
-{
-    Gradient *gradient;
-
-    gradient = radial_gradient_to_rust (node);
-
-    gradient_resolve_fallbacks_and_set_pattern (gradient, ctx, opacity, bbox);
-
-    gradient_destroy (gradient);
-}
-
 static void
 _set_source_rsvg_solid_color (RsvgDrawingCtx * ctx,
                               RsvgSolidColor * color, guint8 opacity, guint32 current_color)
@@ -169,12 +82,13 @@ _set_source_rsvg_paint_server (RsvgDrawingCtx * ctx,
         node = rsvg_drawing_ctx_acquire_node (ctx, ps->core.iri->iri_str);
         if (node == NULL) {
             use_alternate = TRUE;
-        } else if (rsvg_node_get_type (node) == RSVG_NODE_TYPE_LINEAR_GRADIENT) {
-            _set_source_rsvg_linear_gradient (ctx, node, opacity, bbox);
-            had_paint_server = TRUE;
-        } else if (rsvg_node_get_type (node) == RSVG_NODE_TYPE_RADIAL_GRADIENT) {
-            _set_source_rsvg_radial_gradient (ctx, node, opacity, bbox);
-            had_paint_server = TRUE;
+        } else if (rsvg_node_get_type (node) == RSVG_NODE_TYPE_LINEAR_GRADIENT
+                   || rsvg_node_get_type (node) == RSVG_NODE_TYPE_RADIAL_GRADIENT) {
+            if (gradient_resolve_fallbacks_and_set_pattern (node, ctx, opacity, bbox)) {
+                had_paint_server = TRUE;
+            } else {
+                use_alternate = TRUE;
+            }
         } else if (rsvg_node_get_type (node) == RSVG_NODE_TYPE_PATTERN) {
             if (pattern_resolve_fallbacks_and_set_pattern (node, ctx, bbox)) {
                 had_paint_server = TRUE;
diff --git a/rsvg-paint-server.c b/rsvg-paint-server.c
index 3e94ae2..ab418d9 100644
--- a/rsvg-paint-server.c
+++ b/rsvg-paint-server.c
@@ -208,184 +208,3 @@ rsvg_paint_server_unref (RsvgPaintServer * ps)
         g_free (ps);
     }
 }
-
-static void
-rsvg_paint_server_draw (RsvgNode *node, gpointer impl, RsvgDrawingCtx *ctx, int dominate)
-{
-    /* nothing; paint servers are handled specially */
-}
-
-static void
-rsvg_linear_gradient_set_atts (RsvgNode *node, gpointer impl, RsvgHandle *handle, RsvgPropertyBag *atts)
-{
-    RsvgLinearGradient *grad = impl;
-    const char *value;
-
-    if ((value = rsvg_property_bag_lookup (atts, "x1"))) {
-        grad->x1 = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
-        grad->hasx1 = TRUE;
-    }
-    if ((value = rsvg_property_bag_lookup (atts, "y1"))) {
-        grad->y1 = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
-        grad->hasy1 = TRUE;
-    }
-    if ((value = rsvg_property_bag_lookup (atts, "x2"))) {
-        grad->x2 = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
-        grad->hasx2 = TRUE;
-    }
-    if ((value = rsvg_property_bag_lookup (atts, "y2"))) {
-        grad->y2 = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
-        grad->hasy2 = TRUE;
-    }
-    if ((value = rsvg_property_bag_lookup (atts, "spreadMethod"))) {
-        if (!strcmp (value, "pad")) {
-            grad->spread = CAIRO_EXTEND_PAD;
-        } else if (!strcmp (value, "reflect")) {
-            grad->spread = CAIRO_EXTEND_REFLECT;
-        } else if (!strcmp (value, "repeat")) {
-            grad->spread = CAIRO_EXTEND_REPEAT;
-        }
-        grad->hasspread = TRUE;
-    }
-    g_free (grad->fallback);
-    grad->fallback = g_strdup (rsvg_property_bag_lookup (atts, "xlink:href"));
-    if ((value = rsvg_property_bag_lookup (atts, "gradientTransform"))) {
-        if (rsvg_parse_transform (&grad->affine, value)) {
-            grad->hastransform = TRUE;
-        } else {
-            rsvg_node_set_attribute_parse_error (node,
-                                                 "gradientTransform",
-                                                 "Invalid transformation");
-        }
-    }
-    if ((value = rsvg_property_bag_lookup (atts, "gradientUnits"))) {
-        if (!strcmp (value, "userSpaceOnUse"))
-            grad->obj_bbox = FALSE;
-        else if (!strcmp (value, "objectBoundingBox"))
-            grad->obj_bbox = TRUE;
-        grad->hasbbox = TRUE;
-    }
-}
-
-static void
-rsvg_linear_gradient_free (gpointer impl)
-{
-    RsvgLinearGradient *self = impl;
-
-    g_free (self->fallback);
-    g_free (self);
-}
-
-RsvgNode *
-rsvg_new_linear_gradient (const char *element_name, RsvgNode *parent)
-{
-    RsvgLinearGradient *grad = NULL;
-
-    grad = g_new0 (RsvgLinearGradient, 1);
-    cairo_matrix_init_identity (&grad->affine);
-    grad->x1 = rsvg_length_parse ("0", LENGTH_DIR_HORIZONTAL);
-    grad->y1 = grad->y2 = rsvg_length_parse ("0", LENGTH_DIR_VERTICAL);
-    grad->x2 = rsvg_length_parse ("1", LENGTH_DIR_HORIZONTAL);
-    grad->fallback = NULL;
-    grad->obj_bbox = TRUE;
-    grad->spread = CAIRO_EXTEND_PAD;
-    grad->hasx1 = grad->hasy1 = grad->hasx2 = grad->hasy2 = grad->hasbbox = grad->hasspread =
-        grad->hastransform = FALSE;
-
-    return rsvg_rust_cnode_new (RSVG_NODE_TYPE_LINEAR_GRADIENT,
-                                parent,
-                                rsvg_state_new (),
-                                grad,
-                                rsvg_linear_gradient_set_atts,
-                                rsvg_paint_server_draw,
-                                rsvg_linear_gradient_free);
-}
-
-static void
-rsvg_radial_gradient_set_atts (RsvgNode *node, gpointer impl, RsvgHandle *handle, RsvgPropertyBag *atts)
-{
-    RsvgRadialGradient *grad = impl;
-    const char *value;
-
-    if ((value = rsvg_property_bag_lookup (atts, "cx"))) {
-        grad->cx = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
-        grad->hascx = TRUE;
-        if (!grad->hasfx)
-            grad->fx = grad->cx;
-    }
-    if ((value = rsvg_property_bag_lookup (atts, "cy"))) {
-        grad->cy = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
-        grad->hascy = TRUE;
-        if (!grad->hasfy)
-            grad->fy = grad->cy;
-    }
-    if ((value = rsvg_property_bag_lookup (atts, "r"))) {
-        grad->r = rsvg_length_parse (value, LENGTH_DIR_BOTH);
-        grad->hasr = TRUE;
-    }
-    if ((value = rsvg_property_bag_lookup (atts, "fx"))) {
-        grad->fx = rsvg_length_parse (value, LENGTH_DIR_HORIZONTAL);
-        grad->hasfx = TRUE;
-    }
-    if ((value = rsvg_property_bag_lookup (atts, "fy"))) {
-        grad->fy = rsvg_length_parse (value, LENGTH_DIR_VERTICAL);
-        grad->hasfy = TRUE;
-    }
-    g_free (grad->fallback);
-    grad->fallback = g_strdup (rsvg_property_bag_lookup (atts, "xlink:href"));
-    if ((value = rsvg_property_bag_lookup (atts, "gradientTransform"))) {
-        if (rsvg_parse_transform (&grad->affine, value)) {
-            grad->hastransform = TRUE;
-        } else {
-            rsvg_node_set_attribute_parse_error (node,
-                                                 "gradientTransform",
-                                                 "Invalid transformation");
-        }
-    }
-    if ((value = rsvg_property_bag_lookup (atts, "spreadMethod"))) {
-        if (!strcmp (value, "pad"))
-            grad->spread = CAIRO_EXTEND_PAD;
-        else if (!strcmp (value, "reflect"))
-            grad->spread = CAIRO_EXTEND_REFLECT;
-        else if (!strcmp (value, "repeat"))
-            grad->spread = CAIRO_EXTEND_REPEAT;
-        grad->hasspread = TRUE;
-    }
-    if ((value = rsvg_property_bag_lookup (atts, "gradientUnits"))) {
-        if (!strcmp (value, "userSpaceOnUse"))
-            grad->obj_bbox = FALSE;
-        else if (!strcmp (value, "objectBoundingBox"))
-            grad->obj_bbox = TRUE;
-        grad->hasbbox = TRUE;
-    }
-}
-
-static void
-rsvg_radial_gradient_free (gpointer impl)
-{
-    RsvgRadialGradient *self = impl;
-
-    g_free (self->fallback);
-    g_free (self);
-}
-
-RsvgNode *
-rsvg_new_radial_gradient (const char *element_name, RsvgNode *parent)
-{
-    RsvgRadialGradient *grad = g_new0 (RsvgRadialGradient, 1);
-    cairo_matrix_init_identity (&grad->affine);
-    grad->obj_bbox = TRUE;
-    grad->spread = CAIRO_EXTEND_PAD;
-    grad->fallback = NULL;
-    grad->cx = grad->cy = grad->r = grad->fx = grad->fy = rsvg_length_parse ("0.5", LENGTH_DIR_BOTH);
-    grad->hascx = grad->hascy = grad->hasfx = grad->hasfy = grad->hasr = grad->hasbbox =
-        grad->hasspread = grad->hastransform = FALSE;
-
-    return rsvg_rust_cnode_new (RSVG_NODE_TYPE_RADIAL_GRADIENT,
-                                parent,
-                                rsvg_state_new (),
-                                grad,
-                                rsvg_radial_gradient_set_atts,
-                                rsvg_paint_server_draw,
-                                rsvg_radial_gradient_free);
-}
diff --git a/rsvg-paint-server.h b/rsvg-paint-server.h
index 7022ed4..5154f86 100644
--- a/rsvg-paint-server.h
+++ b/rsvg-paint-server.h
@@ -32,90 +32,24 @@
 
 G_BEGIN_DECLS 
 
-typedef struct _RsvgLinearGradient RsvgLinearGradient;
-typedef struct _RsvgRadialGradient RsvgRadialGradient;
 typedef struct _RsvgSolidColor RsvgSolidColor;
 
 typedef struct _RsvgPaintServer RsvgPaintServer;
 
-typedef struct _RsvgPSCtx RsvgPSCtx;
-
-struct _RsvgLinearGradient {
-    gboolean obj_bbox;
-    cairo_matrix_t affine; /* user space to actual at time of gradient def */
-    cairo_extend_t spread;
-    RsvgLength x1, y1, x2, y2;
-    gboolean hasx1;
-    gboolean hasy1;
-    gboolean hasx2;
-    gboolean hasy2;
-    gboolean hasbbox;
-    gboolean hastransform;
-    gboolean hasspread;
-    char *fallback;
-};
-
-struct _RsvgRadialGradient {
-    gboolean obj_bbox;
-    cairo_matrix_t affine; /* user space to actual at time of gradient def */
-    cairo_extend_t spread;
-    RsvgLength cx, cy, r, fx, fy;
-    gboolean hascx;
-    gboolean hascy;
-    gboolean hasr;
-    gboolean hasfx;
-    gboolean hasfy;
-    gboolean hasbbox;
-    gboolean hastransform;
-    gboolean hasspread;
-    char *fallback;
-};
-
-/* This is a Rust gradient from rust/src/gradient.rs */
-typedef struct _Gradient Gradient;
-
-/* Implemented in rust/src/gradient.rs */
-G_GNUC_INTERNAL
-Gradient *gradient_linear_new (RsvgLength     *x1,
-                               RsvgLength     *y1,
-                               RsvgLength     *x2,
-                               RsvgLength     *y2,
-                               gboolean       *obj_bbox,
-                               cairo_matrix_t *affine,
-                               cairo_extend_t *extend,
-                               const char     *fallback_name);
-
-/* Implemented in rust/src/gradient.rs */
-G_GNUC_INTERNAL
-Gradient *gradient_radial_new (RsvgLength     *cx,
-                               RsvgLength     *cy,
-                               RsvgLength     *r,
-                               RsvgLength     *fx,
-                               RsvgLength     *fy,
-                               gboolean       *obj_bbox,
-                               cairo_matrix_t *affine,
-                               cairo_extend_t *extend,
-                               const char     *fallback_name);
-
 /* Implemented in rust/src/gradient.rs */
 G_GNUC_INTERNAL
-void gradient_destroy (Gradient *gradient);
+RsvgNode *rsvg_node_linear_gradient_new (const char *element_name, RsvgNode *parent);
 
 /* Implemented in rust/src/gradient.rs */
 G_GNUC_INTERNAL
-void gradient_add_color_stops_from_node (Gradient *gradient,
-                                         RsvgNode *node);
+RsvgNode *rsvg_node_radial_gradient_new (const char *element_name, RsvgNode *parent);
 
 /* Implemented in rust/src/gradient.rs */
 G_GNUC_INTERNAL
-void gradient_resolve_fallbacks_and_set_pattern (Gradient       *gradient,
-                                                 RsvgDrawingCtx *draw_ctx,
-                                                 guint8          opacity,
-                                                 RsvgBbox        bbox);
-
-G_GNUC_INTERNAL
-Gradient *rsvg_gradient_node_to_rust_gradient (RsvgNode *node);
-
+gboolean gradient_resolve_fallbacks_and_set_pattern (RsvgNode       *node,
+                                                     RsvgDrawingCtx *draw_ctx,
+                                                     guint8          opacity,
+                                                     RsvgBbox        bbox);
 /* Implemented in rust/src/pattern.rs */
 G_GNUC_INTERNAL
 RsvgNode *rsvg_node_pattern_new (const char *element_name, RsvgNode *parent);
diff --git a/rust/src/gradient.rs b/rust/src/gradient.rs
index 269bd67..c298b36 100644
--- a/rust/src/gradient.rs
+++ b/rust/src/gradient.rs
@@ -3,12 +3,17 @@ use ::glib_sys;
 use ::glib::translate::*;
 use ::libc;
 
+use std::cell::RefCell;
+
 use bbox::*;
 use drawing_ctx;
 use drawing_ctx::RsvgDrawingCtx;
+use handle::RsvgHandle;
 use length::*;
 use node::*;
 use paint_server::*;
+use property_bag;
+use property_bag::*;
 use stop::*;
 use util::*;
 
@@ -55,6 +60,18 @@ pub struct Gradient {
     pub variant: GradientVariant
 }
 
+impl Default for GradientCommon {
+    fn default () -> GradientCommon {
+        GradientCommon {
+            units:    None,
+            affine:   None,
+            spread:   None,
+            fallback: None,
+            stops:    None,
+        }
+    }
+}
+
 // All of the Gradient's fields are Option<foo> values, because
 // those fields can be omitted in the SVG file.  We need to resolve
 // them to default values, or to fallback values that come from
@@ -79,20 +96,6 @@ macro_rules! fallback_to (
 );
 
 impl GradientCommon {
-    fn new (units:    Option<PaintServerUnits>,
-            affine:   Option<cairo::Matrix>,
-            spread:   Option<PaintServerSpread>,
-            fallback: Option<String>,
-            stops:    Option<Vec<ColorStop>>) -> GradientCommon {
-        GradientCommon {
-            units:    units,
-            affine:   affine,
-            spread:   spread,
-            fallback: fallback,
-            stops:    stops
-        }
-    }
-
     fn clone_stops (&self) -> Option<Vec<ColorStop>> {
         if let Some (ref stops) = self.stops {
             Some (stops.clone ())
@@ -188,6 +191,7 @@ impl GradientVariant {
         /* These are per the spec */
 
         match *self {
+            // https://www.w3.org/TR/SVG/pservers.html#LinearGradients
             GradientVariant::Linear { ref mut x1, ref mut y1, ref mut x2, ref mut y2 } => {
                 fallback_to! (*x1, Some (RsvgLength::parse ("0%", LengthDir::Horizontal).unwrap ()));
                 fallback_to! (*y1, Some (RsvgLength::parse ("0%", LengthDir::Vertical).unwrap ()));
@@ -195,6 +199,7 @@ impl GradientVariant {
                 fallback_to! (*y2, Some (RsvgLength::parse ("0%", LengthDir::Vertical).unwrap ()));
             },
 
+            // https://www.w3.org/TR/SVG/pservers.html#RadialGradients
             GradientVariant::Radial { ref mut cx, ref mut cy, ref mut r, ref mut fx, ref mut fy } => {
                 fallback_to! (*cx, Some (RsvgLength::parse ("50%", LengthDir::Horizontal).unwrap ()));
                 fallback_to! (*cy, Some (RsvgLength::parse ("50%", LengthDir::Vertical).unwrap ()));
@@ -232,13 +237,6 @@ impl GradientVariant {
 }
 
 impl Gradient {
-    fn new (common: GradientCommon, variant: GradientVariant) -> Gradient {
-        Gradient {
-            common: common,
-            variant: variant
-        }
-    }
-
     fn is_resolved (&self) -> bool {
         self.common.is_resolved () && self.variant.is_resolved ()
     }
@@ -253,6 +251,24 @@ impl Gradient {
         self.variant.resolve_from_fallback (&fallback.variant);
     }
 
+    fn add_color_stops_from_node (&mut self, node: &RsvgNode) {
+        assert! (node.get_type () == NodeType::LinearGradient || node.get_type () == 
NodeType::RadialGradient);
+
+        for child in &*node.children.borrow () {
+            if child.get_type () != NodeType::Stop {
+                continue; // just ignore this child; we are only interested in gradient stops
+            }
+
+            if child.get_result ().is_err () {
+                break; // don't add any more stops
+            }
+
+            child.with_impl (|stop: &NodeStop| {
+                self.add_color_stop (stop.get_offset (), stop.get_rgba ());
+            });
+        }
+    }
+
     fn add_color_stop (&mut self, offset: f64, rgba: u32) {
         self.common.add_color_stop (offset, rgba);
     }
@@ -283,21 +299,26 @@ impl Clone for Gradient {
 }
 
 trait FallbackSource {
-    fn get_fallback (&mut self, name: &str) -> Option<Box<Gradient>>;
+    fn get_fallback (&mut self, name: &str) -> Option<RsvgNode>;
 }
 
 fn resolve_gradient (gradient: &Gradient, fallback_source: &mut FallbackSource) -> Gradient {
     let mut result = gradient.clone ();
 
     while !result.is_resolved () {
-        let mut opt_fallback: Option<Box<Gradient>> = None;
+        let mut opt_fallback: Option<RsvgNode> = None;
 
         if let Some (ref fallback_name) = result.common.fallback {
             opt_fallback = fallback_source.get_fallback (&**fallback_name);
         }
 
-        if let Some (fallback_gradient) = opt_fallback {
-            result.resolve_from_fallback (&*fallback_gradient);
+        if let Some (fallback_node) = opt_fallback {
+            fallback_node.with_impl (|i: &NodeGradient| {
+                let mut fallback_gradient = i.gradient.borrow ().clone ();
+                fallback_gradient.add_color_stops_from_node (&fallback_node);
+
+                result.resolve_from_fallback (&fallback_gradient)
+            });
         } else {
             result.resolve_from_defaults ();
             break;
@@ -329,29 +350,22 @@ impl Drop for NodeFallbackSource {
     }
 }
 
-extern "C" {
-    fn rsvg_gradient_node_to_rust_gradient (node: *const RsvgNode) -> *mut Gradient;
-}
-
 impl FallbackSource for NodeFallbackSource {
-    fn get_fallback (&mut self, name: &str) -> Option<Box<Gradient>> {
+    fn get_fallback (&mut self, name: &str) -> Option<RsvgNode> {
         let fallback_node = drawing_ctx::acquire_node (self.draw_ctx, name);
 
         if fallback_node.is_null () {
             return None;
         }
 
-        self.acquired_nodes.push (fallback_node);
-
-        let raw_fallback_gradient = unsafe { rsvg_gradient_node_to_rust_gradient (fallback_node) };
-
-        if raw_fallback_gradient.is_null () {
+        let node: &RsvgNode = unsafe { & *fallback_node };
+        if !(node.get_type () == NodeType::LinearGradient || node.get_type () == NodeType::RadialGradient) {
             return None;
         }
 
-        let fallback_gradient = unsafe { Box::from_raw (raw_fallback_gradient) };
+        self.acquired_nodes.push (fallback_node);
 
-        return Some (fallback_gradient);
+        return Some (node.clone ());
     }
 }
 
@@ -386,8 +400,7 @@ fn set_common_on_pattern<P: cairo::Pattern + cairo::Gradient> (gradient: &Gradie
 fn set_linear_gradient_on_pattern (gradient: &Gradient,
                                    draw_ctx: *mut RsvgDrawingCtx,
                                    bbox:     &RsvgBbox,
-                                   opacity:  u8)
-{
+                                   opacity:  u8) -> bool {
     if let GradientVariant::Linear { x1, y1, x2, y2 } = gradient.variant {
         let units = gradient.common.units.unwrap ();
 
@@ -408,6 +421,8 @@ fn set_linear_gradient_on_pattern (gradient: &Gradient,
     } else {
         unreachable! ();
     }
+
+    true
 }
 
 /* SVG defines radial gradients as being inside a circle (cx, cy, radius).  The
@@ -467,7 +482,7 @@ fn fix_focus_point (mut fx: f64,
 fn set_radial_gradient_on_pattern (gradient: &Gradient,
                                    draw_ctx: *mut RsvgDrawingCtx,
                                    bbox:     &RsvgBbox,
-                                   opacity:  u8) {
+                                   opacity:  u8) -> bool {
     if let GradientVariant::Radial { cx, cy, r, fx, fy } = gradient.variant {
         let units = gradient.common.units.unwrap ();
 
@@ -493,145 +508,154 @@ fn set_radial_gradient_on_pattern (gradient: &Gradient,
     } else {
         unreachable! ();
     }
+
+    true
 }
 
 fn set_pattern_on_draw_context (gradient: &Gradient,
                                 draw_ctx: *mut RsvgDrawingCtx,
                                 opacity:  u8,
-                                bbox:     &RsvgBbox) {
+                                bbox:     &RsvgBbox) -> bool {
     assert! (gradient.is_resolved ());
 
     match gradient.variant {
         GradientVariant::Linear { .. } => {
-            set_linear_gradient_on_pattern (gradient, draw_ctx, bbox, opacity);
+            set_linear_gradient_on_pattern (gradient, draw_ctx, bbox, opacity)
         }
 
         GradientVariant::Radial { .. } => {
-            set_radial_gradient_on_pattern (gradient, draw_ctx, bbox, opacity);
+            set_radial_gradient_on_pattern (gradient, draw_ctx, bbox, opacity)
         }
     }
 }
 
-fn paint_server_units_from_gboolean (v: glib_sys::gboolean) -> PaintServerUnits {
-    if from_glib (v) {
-        PaintServerUnits::ObjectBoundingBox
-    } else {
-        PaintServerUnits::UserSpaceOnUse
-    }
+struct NodeGradient {
+    gradient: RefCell <Gradient>
 }
 
-/* All the arguments are pointers because they are in fact optional in
- * SVG.  We turn the arguments into Option<foo>: NULL into None, and
- * anything else into a Some().
- */
-#[no_mangle]
-pub unsafe extern fn gradient_linear_new (x1: *const RsvgLength,
-                                          y1: *const RsvgLength,
-                                          x2: *const RsvgLength,
-                                          y2: *const RsvgLength,
-                                          obj_bbox: *const glib_sys::gboolean,
-                                          affine: *const cairo::Matrix,
-                                          spread: *const cairo::enums::Extend,
-                                          fallback_name: *const libc::c_char) -> *mut Gradient {
-    let my_units         = { if obj_bbox.is_null ()      { None } else { Some 
(paint_server_units_from_gboolean (*obj_bbox)) } };
-    let my_affine        = { if affine.is_null ()        { None } else { Some (*affine) } };
-    let my_spread        = { if spread.is_null ()        { None } else { Some (PaintServerSpread (*spread)) 
} };
-    let my_fallback_name = { if fallback_name.is_null () { None } else { Some (String::from_glib_none 
(fallback_name)) } };
-
-    let my_x1 = { if x1.is_null () { None } else { Some (*x1) } };
-    let my_y1 = { if y1.is_null () { None } else { Some (*y1) } };
-    let my_x2 = { if x2.is_null () { None } else { Some (*x2) } };
-    let my_y2 = { if y2.is_null () { None } else { Some (*y2) } };
-
-    let gradient = Gradient::new (GradientCommon::new (my_units, my_affine, my_spread, my_fallback_name, 
None),
-                                  GradientVariant::Linear { x1: my_x1,
-                                                            y1: my_y1,
-                                                            x2: my_x2,
-                                                            y2: my_y2 });
-
-    let boxed_gradient = Box::new (gradient);
-
-    Box::into_raw (boxed_gradient)
-}
+impl NodeGradient {
+    fn new_linear () -> NodeGradient {
+        NodeGradient {
+            gradient: RefCell::new (Gradient {
+                common: GradientCommon::default (),
+                variant: GradientVariant::Linear {
+                    x1: None,
+                    y1: None,
+                    x2: None,
+                    y2: None
+                }
+            })
+        }
+    }
 
-#[no_mangle]
-pub unsafe extern fn gradient_radial_new (cx: *const RsvgLength,
-                                          cy: *const RsvgLength,
-                                          r:  *const RsvgLength,
-                                          fx: *const RsvgLength,
-                                          fy: *const RsvgLength,
-                                          obj_bbox: *const glib_sys::gboolean,
-                                          affine: *const cairo::Matrix,
-                                          spread: *const cairo::enums::Extend,
-                                          fallback_name: *const libc::c_char) -> *mut Gradient {
-    let my_units         = { if obj_bbox.is_null ()      { None } else { Some 
(paint_server_units_from_gboolean (*obj_bbox)) } };
-    let my_affine        = { if affine.is_null ()        { None } else { Some (*affine) } };
-    let my_spread        = { if spread.is_null ()        { None } else { Some (PaintServerSpread (*spread)) 
} };
-    let my_fallback_name = { if fallback_name.is_null () { None } else { Some (String::from_glib_none 
(fallback_name)) } };
-
-    let my_cx = { if cx.is_null () { None } else { Some (*cx) } };
-    let my_cy = { if cy.is_null () { None } else { Some (*cy) } };
-    let my_r  = { if r.is_null  () { None } else { Some (*r)  } };
-    let my_fx = { if fx.is_null () { None } else { Some (*fx) } };
-    let my_fy = { if fy.is_null () { None } else { Some (*fy) } };
-
-    let gradient = Gradient::new (GradientCommon::new (my_units, my_affine, my_spread, my_fallback_name, 
None),
-                                  GradientVariant::Radial { cx: my_cx,
-                                                            cy: my_cy,
-                                                            r:  my_r,
-                                                            fx: my_fx,
-                                                            fy: my_fy });
-
-    let boxed_gradient = Box::new (gradient);
-
-    Box::into_raw (boxed_gradient)
+    fn new_radial () -> NodeGradient {
+        NodeGradient {
+            gradient: RefCell::new (Gradient {
+                common: GradientCommon::default (),
+                variant: GradientVariant::Radial {
+                    cx: None,
+                    cy: None,
+                    r:  None,
+                    fx: None,
+                    fy: None
+                }
+            })
+        }
+    }
 }
 
-#[no_mangle]
-pub unsafe extern fn gradient_destroy (raw_gradient: *mut Gradient) {
-    assert! (!raw_gradient.is_null ());
+impl NodeTrait for NodeGradient {
+    fn set_atts (&self, node: &RsvgNode, _: *const RsvgHandle, pbag: *const RsvgPropertyBag) -> NodeResult {
+        let mut g = self.gradient.borrow_mut ();
 
-    let _ = Box::from_raw (raw_gradient);
-}
+        // Attributes common to linear and radial gradients
 
-#[no_mangle]
-pub extern fn gradient_add_color_stops_from_node (raw_gradient: *mut Gradient,
-                                                  raw_node:     *const RsvgNode) {
-    assert! (!raw_gradient.is_null ());
-    assert! (!raw_node.is_null ());
+        g.common.units    = property_bag::parse_or_none (pbag, "gradientUnits")?;
+        g.common.affine   = property_bag::transform_or_none (pbag, "gradientTransform")?;
+        g.common.spread   = property_bag::parse_or_none (pbag, "spreadMethod")?;
+        g.common.fallback = property_bag::lookup (pbag, "xlink:href");
 
-    let gradient: &mut Gradient = unsafe { &mut (*raw_gradient) };
-    let node: &RsvgNode = unsafe { & *raw_node };
+        // Attributes specific to each gradient type.  The defaults mandated by the spec
+        // are in GradientVariant::resolve_from_defaults()
 
-    for child in &*node.children.borrow () {
-        if child.get_type () != NodeType::Stop {
-            continue; // just ignore this child; we are only interested in gradient stops
-        }
+        match node.get_type () {
+            NodeType::LinearGradient => {
+                g.variant = GradientVariant::Linear {
+                    x1: property_bag::length_or_none (pbag, "x1", LengthDir::Horizontal)?,
+                    y1: property_bag::length_or_none (pbag, "y1", LengthDir::Vertical)?,
+                    x2: property_bag::length_or_none (pbag, "x2", LengthDir::Horizontal)?,
+                    y2: property_bag::length_or_none (pbag, "y2", LengthDir::Vertical)?
+                };
+            },
+
+            NodeType::RadialGradient => {
+                g.variant = GradientVariant::Radial {
+                    cx: property_bag::length_or_none (pbag, "cx", LengthDir::Horizontal)?,
+                    cy: property_bag::length_or_none (pbag, "cy", LengthDir::Vertical)?,
+                    r:  property_bag::length_or_none (pbag, "r",  LengthDir::Both)?,
+                    fx: property_bag::length_or_none (pbag, "fx", LengthDir::Horizontal)?,
+                    fy: property_bag::length_or_none (pbag, "fy", LengthDir::Vertical)?
+                };
+            },
 
-        if child.get_result ().is_err () {
-            break; // don't add any more stops
+            _ => unreachable! ()
         }
 
-        child.with_impl (|stop: &NodeStop| {
-            gradient.add_color_stop (stop.get_offset (), stop.get_rgba ());
-        });
+        Ok (())
+    }
+
+    fn draw (&self, _: &RsvgNode, _: *const RsvgDrawingCtx, _: i32) {
+        // nothing; paint servers are handled specially
+    }
+
+    fn get_c_impl (&self) -> *const RsvgCNodeImpl {
+        unreachable! ();
     }
 }
 
 #[no_mangle]
-pub extern fn gradient_resolve_fallbacks_and_set_pattern (raw_gradient: *mut Gradient,
-                                                          draw_ctx:     *mut RsvgDrawingCtx,
-                                                          opacity:      u8,
-                                                          bbox:         RsvgBbox) {
-    assert! (!raw_gradient.is_null ());
-    let gradient: &mut Gradient = unsafe { &mut (*raw_gradient) };
+pub extern fn rsvg_node_linear_gradient_new (_: *const libc::c_char, raw_parent: *const RsvgNode) -> *const 
RsvgNode {
+    boxed_node_new (NodeType::LinearGradient,
+                    raw_parent,
+                    Box::new (NodeGradient::new_linear ()))
+}
+
+#[no_mangle]
+pub extern fn rsvg_node_radial_gradient_new (_: *const libc::c_char, raw_parent: *const RsvgNode) -> *const 
RsvgNode {
+    boxed_node_new (NodeType::RadialGradient,
+                    raw_parent,
+                    Box::new (NodeGradient::new_radial ()))
+}
 
+fn resolve_fallbacks_and_set_pattern (gradient: &Gradient,
+                                      draw_ctx: *mut RsvgDrawingCtx,
+                                      opacity:  u8,
+                                      bbox:     RsvgBbox) -> bool {
     let mut fallback_source = NodeFallbackSource::new (draw_ctx);
 
     let resolved = resolve_gradient (gradient, &mut fallback_source);
 
-    set_pattern_on_draw_context (&resolved,
-                                 draw_ctx,
-                                 opacity,
-                                 &bbox);
+    set_pattern_on_draw_context (&resolved, draw_ctx, opacity, &bbox)
+}
+
+#[no_mangle]
+pub extern fn gradient_resolve_fallbacks_and_set_pattern (raw_node:     *const RsvgNode,
+                                                          draw_ctx:     *mut RsvgDrawingCtx,
+                                                          opacity:      u8,
+                                                          bbox:         RsvgBbox) -> glib_sys::gboolean {
+    assert! (!raw_node.is_null ());
+    let node: &RsvgNode = unsafe { & *raw_node };
+
+    assert! (node.get_type () == NodeType::LinearGradient || node.get_type () == NodeType::RadialGradient);
+
+    let mut did_set_gradient = false;
+
+    node.with_impl (|node_gradient: &NodeGradient| {
+        let mut gradient = node_gradient.gradient.borrow ().clone ();
+        gradient.add_color_stops_from_node (node);
+
+        did_set_gradient = resolve_fallbacks_and_set_pattern (&gradient, draw_ctx, opacity, bbox);
+    });
+
+    did_set_gradient.to_glib ()
 }
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index db675c7..c25a2f6 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -39,10 +39,8 @@ pub use color::{
 };
 
 pub use gradient::{
-    gradient_linear_new,
-    gradient_radial_new,
-    gradient_destroy,
-    gradient_add_color_stops_from_node,
+    rsvg_node_linear_gradient_new,
+    rsvg_node_radial_gradient_new,
     gradient_resolve_fallbacks_and_set_pattern
 };
 


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