[librsvg/rustification] gradient.rs: Finish porting gradients to Rust! Yay!



commit 1c0b41dfd17ec286b47b84aaa47c3f2a3fc10bb1
Author: Federico Mena Quintero <federico gnome org>
Date:   Wed Nov 30 21:17:29 2016 -0600

    gradient.rs: Finish porting gradients to Rust!  Yay!

 rsvg-css.c           |    2 +-
 rsvg-paint-server.h  |   42 +++++++----
 rust/src/gradient.rs |  200 +++++++++++++++++++++++++++++++++++++++++++++-----
 rust/src/lib.rs      |    4 +-
 4 files changed, 210 insertions(+), 38 deletions(-)
---
diff --git a/rsvg-css.c b/rsvg-css.c
index 6df3bbf..4dd9761 100644
--- a/rsvg-css.c
+++ b/rsvg-css.c
@@ -25,8 +25,8 @@
 */
 
 #include "config.h"
-#include "rsvg-css.h"
 #include "rsvg-private.h"
+#include "rsvg-css.h"
 #include "rsvg-styles.h"
 
 #include <glib.h>
diff --git a/rsvg-paint-server.h b/rsvg-paint-server.h
index 379237a..a0e069d 100644
--- a/rsvg-paint-server.h
+++ b/rsvg-paint-server.h
@@ -29,7 +29,6 @@
 
 #include <glib.h>
 #include <cairo.h>
-#include "rsvg-defs.h"
 
 G_BEGIN_DECLS 
 
@@ -57,13 +56,13 @@ struct _RsvgLinearGradient {
     cairo_matrix_t affine; /* user space to actual at time of gradient def */
     cairo_extend_t spread;
     RsvgLength x1, y1, x2, y2;
-    int hasx1:1;
-    int hasy1:1;
-    int hasx2:1;
-    int hasy2:1;
-    int hasbbox:1;
-    int hastransform:1;
-    int hasspread:1;
+    gboolean hasx1;
+    gboolean hasy1;
+    gboolean hasx2;
+    gboolean hasy2;
+    gboolean hasbbox;
+    gboolean hastransform;
+    gboolean hasspread;
     char *fallback;
 };
 
@@ -73,14 +72,14 @@ struct _RsvgRadialGradient {
     cairo_matrix_t affine; /* user space to actual at time of gradient def */
     cairo_extend_t spread;
     RsvgLength cx, cy, r, fx, fy;
-    int hascx:1;
-    int hascy:1;
-    int hasr:1;
-    int hasfx:1;
-    int hasfy:1;
-    int hasbbox:1;
-    int hastransform:1;
-    int hasspread:1;
+    gboolean hascx;
+    gboolean hascy;
+    gboolean hasr;
+    gboolean hasfx;
+    gboolean hasfy;
+    gboolean hasbbox;
+    gboolean hastransform;
+    gboolean hasspread;
     char *fallback;
 };
 
@@ -113,11 +112,22 @@ Gradient *gradient_radial_new (RsvgLength     *cx,
 G_GNUC_INTERNAL
 void gradient_destroy (Gradient *gradient);
 
+/* Implemented in rust/src/gradient.rs */
 G_GNUC_INTERNAL
 void gradient_add_color_stop (Gradient *gradient,
                               double    offset,
                               guint32   rgba);
 
+/* 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);
+
 struct _RsvgPattern {
     RsvgNode super;
     gboolean obj_cbbox;
diff --git a/rust/src/gradient.rs b/rust/src/gradient.rs
index 42e3039..2aea433 100644
--- a/rust/src/gradient.rs
+++ b/rust/src/gradient.rs
@@ -4,12 +4,12 @@ extern crate cairo_sys;
 extern crate glib;
 
 use self::glib::translate::*;
-use self::cairo::Pattern;
 
 use length::*;
 
 use drawing_ctx;
 use drawing_ctx::RsvgDrawingCtx;
+use drawing_ctx::RsvgNode;
 
 use bbox::*;
 
@@ -141,6 +141,18 @@ fn clone_fallback_name (fallback: &Option<String>) -> Option<String> {
     }
 }
 
+impl Clone for GradientCommon {
+    fn clone (&self) -> Self {
+        GradientCommon {
+            obj_bbox: self.obj_bbox,
+            affine:   self.affine,
+            spread:   self.spread,
+            fallback: clone_fallback_name (&self.fallback),
+            stops:    self.clone_stops ()
+        }
+    }
+}
+
 impl GradientVariant {
     fn is_resolved (&self) -> bool {
         match *self {
@@ -250,17 +262,21 @@ impl Gradient {
     }
 }
 
+impl Clone for Gradient {
+    fn clone (&self) -> Self {
+        Gradient {
+            common: self.common.clone (),
+            variant: self.variant
+        }
+    }
+}
+
 trait FallbackSource {
-    fn get_fallback (&self, name: &str) -> Option<Gradient>;
+    fn get_fallback (&mut self, name: &str) -> Option<Gradient>;
 }
 
-fn resolve_gradient (gradient: &Gradient, fallback_source: &FallbackSource) -> Gradient {
-    let mut result = Gradient::new (GradientCommon::new (gradient.common.obj_bbox,
-                                                         gradient.common.affine,
-                                                         gradient.common.spread,
-                                                         clone_fallback_name (&gradient.common.fallback),
-                                                         gradient.common.clone_stops ()),
-                                    gradient.variant);
+fn resolve_gradient (gradient: &Gradient, fallback_source: &mut FallbackSource) -> Gradient {
+    let mut result = gradient.clone ();
 
     while !result.is_resolved () {
         let mut opt_fallback: Option<Gradient> = None;
@@ -280,11 +296,55 @@ fn resolve_gradient (gradient: &Gradient, fallback_source: &FallbackSource) -> G
     result
 }
 
-fn set_common_on_pattern (gradient: &Gradient,
-                          draw_ctx: &mut RsvgDrawingCtx,
-                          pattern:  &mut cairo::LinearGradient,
-                          bbox:     &RsvgBbox,
-                          opacity:  u8)
+struct NodeFallbackSource {
+    draw_ctx: *mut RsvgDrawingCtx,
+    acquired_nodes: Vec<*mut RsvgNode>
+}
+
+impl NodeFallbackSource {
+    fn new (draw_ctx: *mut RsvgDrawingCtx) -> NodeFallbackSource {
+        NodeFallbackSource {
+            draw_ctx: draw_ctx,
+            acquired_nodes: Vec::<*mut RsvgNode>::new ()
+        }
+    }
+}
+
+impl Drop for NodeFallbackSource {
+    fn drop (&mut self) {
+        while let Some (node) = self.acquired_nodes.pop () {
+            drawing_ctx::release_node (self.draw_ctx, node);
+        }
+    }
+}
+
+impl FallbackSource for NodeFallbackSource {
+    fn get_fallback (&mut self, name: &str) -> Option<Gradient> {
+        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 () {
+            return None;
+        }
+
+        let fallback_gradient: &mut Gradient = unsafe { &mut (*raw_fallback_gradient) };
+
+        return Some (fallback_gradient.clone ());
+    }
+}
+
+fn set_common_on_pattern<P: cairo::Pattern + cairo::Gradient> (gradient: &Gradient,
+                                                               draw_ctx: *mut RsvgDrawingCtx,
+                                                               pattern:  &mut P,
+                                                               bbox:     &RsvgBbox,
+                                                               opacity:  u8)
 {
     let cr = drawing_ctx::get_cairo_context (draw_ctx);
 
@@ -309,7 +369,7 @@ fn set_common_on_pattern (gradient: &Gradient,
 }
 
 fn set_linear_gradient_on_pattern (gradient: &Gradient,
-                                   draw_ctx: &mut RsvgDrawingCtx,
+                                   draw_ctx: *mut RsvgDrawingCtx,
                                    bbox:     &RsvgBbox,
                                    opacity:  u8)
 {
@@ -335,17 +395,95 @@ fn set_linear_gradient_on_pattern (gradient: &Gradient,
     }
 }
 
+/* SVG defines radial gradients as being inside a circle (cx, cy, radius).  The
+ * gradient projects out from a focus point (fx, fy), which is assumed to be
+ * inside the circle, to the edge of the circle.
+ *
+ * The description of https://www.w3.org/TR/SVG/pservers.html#RadialGradientElement
+ * states:
+ *
+ * If the point defined by ‘fx’ and ‘fy’ lies outside the circle defined by
+ * ‘cx’, ‘cy’ and ‘r’, then the user agent shall set the focal point to the
+ * intersection of the line from (‘cx’, ‘cy’) to (‘fx’, ‘fy’) with the circle
+ * defined by ‘cx’, ‘cy’ and ‘r’.
+ *
+ * So, let's do that!
+ */
+fn fix_focus_point (mut fx: f64,
+                    mut fy: f64,
+                    cx: f64,
+                    cy: f64,
+                    radius: f64) -> (f64, f64) {
+    /* Easy case first: the focus point is inside the circle */
+
+    if (fx - cx) * (fx - cx) + (fy - cy) * (fy - cy) <= radius * radius {
+        return (fx, fy);
+    }
+
+    /* Hard case: focus point is outside the circle.
+     *
+     * First, translate everything to the origin.
+     */
+
+    fx -= cx;
+    fy -= cy;
+
+    /* Find the vector from the origin to (fx, fy) */
+
+    let mut vx = fx;
+    let mut vy = fy;
+
+    /* Find the vector's magnitude */
+
+    let mag = (vx * vx + vy * vy).sqrt ();
+
+    /* Normalize the vector to have a magnitude equal to radius; (vx, vy) will now be on the edge of the 
circle */
+
+    let scale = mag / radius;
+
+    vx /= scale;
+    vy /= scale;
+
+    /* Translate back to (cx, cy) and we are done! */
+
+    (vx + cx, vy + cy)
+}
+
 fn set_radial_gradient_on_pattern (gradient: &Gradient,
-                                   draw_ctx: &mut RsvgDrawingCtx,
+                                   draw_ctx: *mut RsvgDrawingCtx,
                                    bbox:     &RsvgBbox,
                                    opacity:  u8) {
-    unimplemented! ();
+    if let GradientVariant::Radial { cx, cy, r, fx, fy } = gradient.variant {
+        let obj_bbox = gradient.common.obj_bbox.unwrap ();
+
+        if obj_bbox {
+            drawing_ctx::push_view_box (draw_ctx, 1.0, 1.0);
+        }
+
+        let n_cx = cx.as_ref ().unwrap ().normalize (draw_ctx);
+        let n_cy = cy.as_ref ().unwrap ().normalize (draw_ctx);
+        let n_r  =  r.as_ref ().unwrap ().normalize (draw_ctx);
+        let n_fx = fx.as_ref ().unwrap ().normalize (draw_ctx);
+        let n_fy = fy.as_ref ().unwrap ().normalize (draw_ctx);
+
+        let (new_fx, new_fy) = fix_focus_point (n_fx, n_fy, n_cx, n_cy, n_r);
+
+        let mut pattern = cairo::RadialGradient::new (new_fx, new_fy, 0.0, n_cx, n_cy, n_r);
+
+        if obj_bbox {
+            drawing_ctx::pop_view_box (draw_ctx);
+        }
+
+        set_common_on_pattern (gradient, draw_ctx, &mut pattern, bbox, opacity);
+    } else {
+        unreachable! ();
+    }
 }
 
 fn set_pattern_on_draw_context (gradient: &Gradient,
-                                draw_ctx: &mut RsvgDrawingCtx,
-                                bbox:     &RsvgBbox,
-                                opacity:  u8) {
+                                draw_ctx: *mut RsvgDrawingCtx,
+                                opacity:  u8,
+                                bbox:     &RsvgBbox) {
     assert! (gradient.is_resolved ());
 
     match gradient.variant {
@@ -446,3 +584,25 @@ pub extern fn gradient_add_color_stop (raw_gradient: *mut Gradient,
 
     gradient.add_color_stop (offset, rgba);
 }
+
+extern "C" {
+    fn rsvg_gradient_node_to_rust_gradient (node: *const RsvgNode) -> *mut Gradient;
+}
+
+#[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) };
+
+    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);
+}
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index 39b97a3..7238382 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -7,8 +7,10 @@ pub use bbox::{
 
 pub use gradient::{
     gradient_linear_new,
+    gradient_radial_new,
     gradient_destroy,
-    gradient_add_color_stop
+    gradient_add_color_stop,
+    gradient_resolve_fallbacks_and_set_pattern
 };
 
 pub use path_builder::{


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