[librsvg/rustification] gradient.rs: Finish porting gradients to Rust! Yay!
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg/rustification] gradient.rs: Finish porting gradients to Rust! Yay!
- Date: Thu, 1 Dec 2016 03:20:13 +0000 (UTC)
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]