[librsvg] pattern.rs: New file; resolution of pattern fallbacks is now done in Rust



commit 198103a59c208a1562e101d89d90e7068d0018dd
Author: Federico Mena Quintero <federico gnome org>
Date:   Wed Feb 8 08:22:38 2017 -0600

    pattern.rs: New file; resolution of pattern fallbacks is now done in Rust

 rsvg-cairo-draw.c       |  178 +++++++++--------------------------------------
 rsvg-cairo-draw.h       |    2 +
 rsvg-paint-server.c     |  140 +++++++++----------------------------
 rsvg-paint-server.h     |   40 ++++++++++-
 rust/Cargo.toml         |    2 +
 rust/src/drawing_ctx.rs |   32 +++++++++
 rust/src/lib.rs         |    7 ++
 rust/src/pattern.rs     |  178 +++++++++++++++++++++++++++++++++++++----------
 8 files changed, 286 insertions(+), 293 deletions(-)
---
diff --git a/rsvg-cairo-draw.c b/rsvg-cairo-draw.c
index 0b51c39..3cc2f8c 100644
--- a/rsvg-cairo-draw.c
+++ b/rsvg-cairo-draw.c
@@ -169,155 +169,15 @@ _set_source_rsvg_solid_color (RsvgDrawingCtx * ctx,
 
 static void
 _set_source_rsvg_pattern (RsvgDrawingCtx * ctx,
-                          RsvgPattern * rsvg_pattern, guint8 opacity, RsvgBbox bbox)
+                          RsvgPattern * rsvg_pattern, RsvgBbox bbox)
 {
-    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
-    RsvgPattern local_pattern = *rsvg_pattern;
-    cairo_t *cr_render, *cr_pattern;
-    cairo_pattern_t *pattern;
-    cairo_surface_t *surface;
-    cairo_matrix_t matrix;
-    cairo_matrix_t affine, caffine, taffine;
-    double bbwscale, bbhscale, scwscale, schscale;
-    double patternw, patternh, patternx, patterny;
-    double scaled_width, scaled_height;
-    int pw, ph;
-
-    rsvg_pattern = &local_pattern;
-    rsvg_pattern_fix_fallback (ctx, rsvg_pattern);
-    cr_render = render->cr;
-
-    if (rsvg_pattern->obj_bbox)
-        rsvg_drawing_ctx_push_view_box (ctx, 1., 1.);
-
-    patternx = rsvg_length_normalize (&rsvg_pattern->x, ctx);
-    patterny = rsvg_length_normalize (&rsvg_pattern->y, ctx);
-    patternw = rsvg_length_normalize (&rsvg_pattern->width, ctx);
-    patternh = rsvg_length_normalize (&rsvg_pattern->height, ctx);
-
-    if (rsvg_pattern->obj_bbox)
-        rsvg_drawing_ctx_pop_view_box (ctx);
-
-    /* Work out the size of the rectangle so it takes into account the object bounding box */
-
-    if (rsvg_pattern->obj_bbox) {
-        bbwscale = bbox.rect.width;
-        bbhscale = bbox.rect.height;
-    } else {
-        bbwscale = 1.0;
-        bbhscale = 1.0;
-    }
-
-    cairo_matrix_multiply (&taffine, &rsvg_pattern->affine, &rsvg_current_state (ctx)->affine);
-
-    scwscale = sqrt (taffine.xx * taffine.xx + taffine.xy * taffine.xy);
-    schscale = sqrt (taffine.yx * taffine.yx + taffine.yy * taffine.yy);
+    Pattern *pattern;
 
-    pw = patternw * bbwscale * scwscale;
-    ph = patternh * bbhscale * schscale;
+    pattern = rsvg_pattern_node_to_rust_pattern ((RsvgNode *) rsvg_pattern);
 
-    scaled_width = patternw * bbwscale;
-    scaled_height = patternh * bbhscale;
-
-    if (fabs (scaled_width) < DBL_EPSILON || fabs (scaled_height) < DBL_EPSILON)
-        return;
-
-    scwscale = pw / scaled_width;
-    schscale = ph / scaled_height;
-
-    surface = cairo_surface_create_similar (cairo_get_target (cr_render),
-                                            CAIRO_CONTENT_COLOR_ALPHA, pw, ph);
-    cr_pattern = cairo_create (surface);
-
-    /* Create the pattern coordinate system */
-    if (rsvg_pattern->obj_bbox) {
-        /* subtract the pattern origin */
-        cairo_matrix_init_translate (&affine,
-                                     bbox.rect.x + patternx * bbox.rect.width,
-                                     bbox.rect.y + patterny * bbox.rect.height);
-    } else {
-        /* subtract the pattern origin */
-        cairo_matrix_init_translate (&affine, patternx, patterny);
-    }
-    /* Apply the pattern transform */
-    cairo_matrix_multiply (&affine, &affine, &rsvg_pattern->affine);
-
-    /* Create the pattern contents coordinate system */
-    if (rsvg_pattern->vbox.active) {
-        /* If there is a vbox, use that */
-        double w, h, x, y;
-        w = patternw * bbwscale;
-        h = patternh * bbhscale;
-        x = 0;
-        y = 0;
-        rsvg_aspect_ratio_compute (rsvg_pattern->preserve_aspect_ratio,
-                                   rsvg_pattern->vbox.rect.width,
-                                   rsvg_pattern->vbox.rect.height,
-                                   &x, &y, &w, &h);
-
-        x -= rsvg_pattern->vbox.rect.x * w / rsvg_pattern->vbox.rect.width;
-        y -= rsvg_pattern->vbox.rect.y * h / rsvg_pattern->vbox.rect.height;
-
-        cairo_matrix_init (&caffine,
-                           w / rsvg_pattern->vbox.rect.width,
-                           0,
-                           0,
-                           h / rsvg_pattern->vbox.rect.height,
-                           x,
-                           y);
-        rsvg_drawing_ctx_push_view_box (ctx, rsvg_pattern->vbox.rect.width, rsvg_pattern->vbox.rect.height);
-    } else if (rsvg_pattern->obj_cbbox) {
-        /* If coords are in terms of the bounding box, use them */
-        cairo_matrix_init_scale (&caffine, bbox.rect.width, bbox.rect.height);
-        rsvg_drawing_ctx_push_view_box (ctx, 1., 1.);
-    } else {
-        cairo_matrix_init_identity (&caffine);
-    }
-
-    if (scwscale != 1.0 || schscale != 1.0) {
-        cairo_matrix_t scalematrix;
-
-        cairo_matrix_init_scale (&scalematrix, scwscale, schscale);
-        cairo_matrix_multiply (&caffine, &caffine, &scalematrix);
-        cairo_matrix_init_scale (&scalematrix, 1. / scwscale, 1. / schscale);
-        cairo_matrix_multiply (&affine, &scalematrix, &affine);
-    }
+    pattern_resolve_fallbacks_and_set_pattern (pattern, ctx, bbox);
 
-    /* Draw to another surface */
-    render->cr = cr_pattern;
-
-    /* Set up transformations to be determined by the contents units */
-    rsvg_state_push (ctx);
-    rsvg_current_state (ctx)->personal_affine =
-            rsvg_current_state (ctx)->affine = caffine;
-
-    /* Draw everything */
-    _rsvg_node_draw_children ((RsvgNode *) rsvg_pattern, ctx, 2);
-    /* Return to the original coordinate system */
-    rsvg_state_pop (ctx);
-
-    /* Set the render to draw where it used to */
-    render->cr = cr_render;
-
-    pattern = cairo_pattern_create_for_surface (surface);
-    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
-
-    matrix = affine;
-    if (cairo_matrix_invert (&matrix) != CAIRO_STATUS_SUCCESS)
-      goto out;
-
-    cairo_pattern_set_matrix (pattern, &matrix);
-    cairo_pattern_set_filter (pattern, CAIRO_FILTER_BEST);
-
-    cairo_set_source (cr_render, pattern);
-
-    cairo_pattern_destroy (pattern);
-    cairo_destroy (cr_pattern);
-    cairo_surface_destroy (surface);
-
-  out:
-    if (rsvg_pattern->obj_cbbox || rsvg_pattern->vbox.active)
-        rsvg_drawing_ctx_pop_view_box (ctx);
+    pattern_destroy (pattern);
 }
 
 /* note: _set_source_rsvg_paint_server does not change cairo's CTM */
@@ -339,7 +199,7 @@ _set_source_rsvg_paint_server (RsvgDrawingCtx * ctx,
         else if (rsvg_node_type (node) == RSVG_NODE_TYPE_RADIAL_GRADIENT)
             _set_source_rsvg_radial_gradient (ctx, (RsvgRadialGradient *) node, opacity, bbox);
         else if (rsvg_node_type (node) == RSVG_NODE_TYPE_PATTERN)
-            _set_source_rsvg_pattern (ctx, (RsvgPattern *) node, opacity, bbox);
+            _set_source_rsvg_pattern (ctx, (RsvgPattern *) node, bbox);
 
         rsvg_drawing_ctx_release_node (ctx, node);
         break;
@@ -690,6 +550,32 @@ rsvg_cairo_get_cairo_context (RsvgDrawingCtx *ctx)
     return render->cr;
 }
 
+/* FIXME: Usage of this function is more less a hack.  Some code does this:
+ *
+ *   save_cr = rsvg_cairo_get_cairo_context (ctx);
+ *
+ *   some_surface = create_surface ();
+ *
+ *   cr = cairo_create (some_surface);
+ *
+ *   rsvg_cairo_set_cairo_context (ctx, cr);
+ *
+ *   ... draw with ctx but to that temporary surface
+ *
+ *   rsvg_cairo_set_cairo_context (ctx, save_cr);
+ *
+ * It would be better to have an explicit push/pop for the cairo_t, or
+ * pushing a temporary surface, or something that does not involve
+ * monkeypatching the cr directly.
+ */
+void
+rsvg_cairo_set_cairo_context (RsvgDrawingCtx *ctx, cairo_t *cr)
+{
+    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
+
+    render->cr = cr;
+}
+
 static void
 rsvg_cairo_generate_mask (cairo_t * cr, RsvgMask * self, RsvgDrawingCtx * ctx, RsvgBbox * bbox)
 {
diff --git a/rsvg-cairo-draw.h b/rsvg-cairo-draw.h
index aff07c6..9cac30d 100644
--- a/rsvg-cairo-draw.h
+++ b/rsvg-cairo-draw.h
@@ -46,6 +46,8 @@ void         rsvg_cairo_render_surface          (RsvgDrawingCtx *ctx, cairo_surf
                                                  double x, double y, double w, double h);
 G_GNUC_INTERNAL
 cairo_t *rsvg_cairo_get_cairo_context (RsvgDrawingCtx *ctx);
+G_GNUC_INTERNAL
+void rsvg_cairo_set_cairo_context (RsvgDrawingCtx *ctx, cairo_t *cr);
 
 G_GNUC_INTERNAL
 void         rsvg_cairo_push_discrete_layer        (RsvgDrawingCtx *ctx);
diff --git a/rsvg-paint-server.c b/rsvg-paint-server.c
index 0977be6..981c2de 100644
--- a/rsvg-paint-server.c
+++ b/rsvg-paint-server.c
@@ -502,127 +502,55 @@ rsvg_new_pattern (const char *element_name)
     return &pattern->super;
 }
 
-typedef const char * (* GetFallbackFn) (RsvgNode *node);
-typedef void (* ApplyFallbackFn) (RsvgNode *node, RsvgNode *fallback_node);
-
-/* Some SVG paint servers can reference a "parent" or "fallback" paint server
- * through the xlink:href attribute (for example,
- * http://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementHrefAttribute )
- * This is used to define a chain of properties to be resolved from each
- * fallback.
- */
-static void
-resolve_fallbacks (RsvgDrawingCtx *ctx,
-                   RsvgNode *data,
-                   RsvgNode *last_fallback,
-                   GetFallbackFn get_fallback,
-                   ApplyFallbackFn apply_fallback)
+Pattern *
+rsvg_pattern_node_to_rust_pattern (RsvgNode *node)
 {
-    RsvgNode *fallback;
-    const char *fallback_id;
-
-    fallback_id = get_fallback (last_fallback);
-    if (fallback_id == NULL)
-        return;
-    fallback = rsvg_drawing_ctx_acquire_node (ctx, fallback_id);
-    if (fallback == NULL)
-      return;
+    RsvgPattern *pnode;
+    Pattern *pattern;
 
-    apply_fallback (data, fallback);
-    resolve_fallbacks (ctx, data, fallback, get_fallback, apply_fallback);
-
-    rsvg_drawing_ctx_release_node (ctx, fallback);
-}
-
-static const char *
-pattern_get_fallback (RsvgNode *node)
-{
-    if (rsvg_node_type (node) == RSVG_NODE_TYPE_PATTERN) {
-        RsvgPattern *pattern = (RsvgPattern *) node;
-
-        return pattern->fallback;
-    } else
+    if (rsvg_node_type (node) != RSVG_NODE_TYPE_PATTERN)
         return NULL;
+
+    pnode = (RsvgPattern *) node;
+
+    pattern = pattern_new (pnode->hasx         ? &pnode->x : NULL,
+                           pnode->hasy         ? &pnode->y : NULL,
+                           pnode->haswidth     ? &pnode->width : NULL,
+                           pnode->hasheight    ? &pnode->height : NULL,
+                           pnode->hasbbox      ? &pnode->obj_bbox : NULL,
+                           pnode->hascbox      ? &pnode->obj_cbbox : NULL,
+                           pnode->hasvbox      ? &pnode->vbox : NULL,
+                           pnode->hastransform ? &pnode->affine : NULL,
+                           pnode->hasaspect    ? &pnode->preserve_aspect_ratio : NULL,
+                           pnode->fallback,
+                           node);
+
+    return pattern;
 }
 
 static gboolean
-has_children_foreach (RsvgNode *node, gpointer data)
+count_one_child_fn (RsvgNode *child, gpointer data)
 {
-    gboolean *has_child = data;
+    gboolean *has_children = data;
 
-    *has_child = TRUE;
-    return FALSE; /* stop since we found a child */
+    *has_children = TRUE;
+    return FALSE;
 }
 
 static gboolean
-has_children (RsvgNode *node)
+node_has_at_least_one_child (RsvgNode *node)
 {
-    gboolean has_child = FALSE;
+    gboolean has_children = FALSE;
 
-    rsvg_node_foreach_child (node, has_children_foreach, &has_child);
-    return has_child;
+    rsvg_node_foreach_child (node, count_one_child_fn, &has_children);
+    return has_children;
 }
 
-static void
-pattern_apply_fallback (RsvgNode *pattern_node, RsvgNode *fallback_node)
+gboolean
+rsvg_pattern_node_has_children (RsvgNode *node)
 {
-    RsvgPattern *pattern;
-    RsvgPattern *fallback;
-
-    g_assert (rsvg_node_type (pattern_node) == RSVG_NODE_TYPE_PATTERN);
-
-    if (rsvg_node_type (fallback_node) != RSVG_NODE_TYPE_PATTERN)
-        return;
-
-    pattern = (RsvgPattern *) pattern_node;
-    fallback = (RsvgPattern *) fallback_node;
-
-    if (!pattern->hasx && fallback->hasx) {
-        pattern->hasx = TRUE;
-        pattern->x = fallback->x;
-    }
-    if (!pattern->hasy && fallback->hasy) {
-        pattern->hasy = TRUE;
-        pattern->y = fallback->y;
-    }
-    if (!pattern->haswidth && fallback->haswidth) {
-        pattern->haswidth = TRUE;
-        pattern->width = fallback->width;
-    }
-    if (!pattern->hasheight && fallback->hasheight) {
-        pattern->hasheight = TRUE;
-        pattern->height = fallback->height;
-    }
-    if (!pattern->hastransform && fallback->hastransform) {
-        pattern->hastransform = TRUE;
-        pattern->affine = fallback->affine;
-    }
-    if (!pattern->hasvbox && fallback->hasvbox) {
-        pattern->vbox = fallback->vbox;
-    }
-    if (!pattern->hasaspect && fallback->hasaspect) {
-        pattern->hasaspect = TRUE;
-        pattern->preserve_aspect_ratio = fallback->preserve_aspect_ratio;
-    }
-    if (!pattern->hasbbox && fallback->hasbbox) {
-        pattern->hasbbox = TRUE;
-        pattern->obj_bbox = fallback->obj_bbox;
-    }
-    if (!pattern->hascbox && fallback->hascbox) {
-        pattern->hascbox = TRUE;
-        pattern->obj_cbbox = fallback->obj_cbbox;
-    }
-    if (!has_children (pattern_node) && has_children (fallback_node)) {
-        pattern->super.children = fallback->super.children;
-    }
-}
+    if (rsvg_node_type (node) != RSVG_NODE_TYPE_PATTERN)
+        return FALSE;
 
-void
-rsvg_pattern_fix_fallback (RsvgDrawingCtx *ctx, RsvgPattern * pattern)
-{
-    resolve_fallbacks (ctx,
-                       (RsvgNode *) pattern,
-                       (RsvgNode *) pattern,
-                       pattern_get_fallback,
-                       pattern_apply_fallback);
+    return node_has_at_least_one_child (node);
 }
diff --git a/rsvg-paint-server.h b/rsvg-paint-server.h
index 6d5ad2b..e260fba 100644
--- a/rsvg-paint-server.h
+++ b/rsvg-paint-server.h
@@ -83,6 +83,7 @@ struct _RsvgRadialGradient {
     char *fallback;
 };
 
+/* This is a Rust gradient from rust/src/gradient.rs */
 typedef struct _Gradient Gradient;
 
 /* Implemented in rust/src/gradient.rs */
@@ -148,6 +149,41 @@ struct _RsvgPattern {
     char *fallback;
 };
 
+/* This is a Rust pattern from rust/src/pattern.rs */
+typedef struct _Pattern Pattern;
+
+/* Implemented in rust/src/pattern.rs */
+G_GNUC_INTERNAL
+Pattern *
+pattern_new (RsvgLength     *x,
+             RsvgLength     *y,
+             RsvgLength     *width,
+             RsvgLength     *height,
+             gboolean       *obj_bbox,
+             gboolean       *obj_cbbox,
+             RsvgViewBox    *vbox,
+             cairo_matrix_t *affine,
+             guint32        *preserve_aspect_ratio,
+             const char     *fallback_name,
+             RsvgNode       *node);
+
+/* Implemented in rust/src/pattern.rs */
+G_GNUC_INTERNAL
+void pattern_destroy (Pattern *pattern);
+
+/* Implemented in rust/src/pattern.rs */
+G_GNUC_INTERNAL
+void pattern_resolve_fallbacks_and_set_pattern (Pattern        *pattern,
+                                                RsvgDrawingCtx *draw_ctx,
+                                                RsvgBbox        bbox);
+
+G_GNUC_INTERNAL
+Pattern *rsvg_pattern_node_to_rust_pattern (RsvgNode *node);
+
+G_GNUC_INTERNAL
+gboolean rsvg_pattern_node_has_children (RsvgNode *node);
+
+
 struct _RsvgSolidColor {
     gboolean currentcolor;
     guint32 argb;
@@ -189,9 +225,7 @@ G_GNUC_INTERNAL
 RsvgNode *rsvg_new_stop                (const char *element_name);
 G_GNUC_INTERNAL
 RsvgNode *rsvg_new_pattern      (const char *element_name);
-G_GNUC_INTERNAL
-void rsvg_pattern_fix_fallback          (RsvgDrawingCtx * ctx,
-                                         RsvgPattern * pattern);
+
 
 G_END_DECLS
 
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 6579336..4dc2d97 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -11,11 +11,13 @@ bitflags = ""
 #git = "https://github.com/gtk-rs/cairo.git";
 git = "https://github.com/federicomenaquintero/cairo.git";
 #git = "file:///home/federico/src/gtk-rs/cairo"
+#features = ["png"]
 
 [dependencies.cairo-rs]
 #git = "https://github.com/gtk-rs/cairo.git";
 git = "https://github.com/federicomenaquintero/cairo.git";
 #git = "file:///home/federico/src/gtk-rs/cairo"
+#features = ["png"]
 
 [dependencies.glib]
 git = "https://github.com/gtk-rs/glib";
diff --git a/rust/src/drawing_ctx.rs b/rust/src/drawing_ctx.rs
index 60d2900..d166cd6 100644
--- a/rust/src/drawing_ctx.rs
+++ b/rust/src/drawing_ctx.rs
@@ -40,6 +40,9 @@ extern "C" {
     fn rsvg_drawing_ctx_set_current_state_affine (draw_ctx: *const RsvgDrawingCtx,
                                                   affine:   *const cairo::Matrix);
 
+    fn rsvg_state_push (draw_ctx: *const RsvgDrawingCtx);
+    fn rsvg_state_pop (draw_ctx: *const RsvgDrawingCtx);
+
     fn rsvg_state_reinherit_top (draw_ctx: *const RsvgDrawingCtx,
                                  state: *mut RsvgState,
                                  dominate: libc::c_int);
@@ -48,6 +51,9 @@ extern "C" {
                                  builder: *const RsvgPathBuilder);
 
     fn rsvg_cairo_get_cairo_context (draw_ctx: *const RsvgDrawingCtx) -> *mut cairo_sys::cairo_t;
+    fn rsvg_cairo_set_cairo_context (draw_ctx: *const RsvgDrawingCtx, cr: *const cairo_sys::cairo_t);
+
+    fn _rsvg_node_draw_children (node: *const RsvgNode, draw_ctx: *const RsvgDrawingCtx, dominate: 
libc::c_int);
 }
 
 pub fn get_dpi (draw_ctx: *const RsvgDrawingCtx) -> (f64, f64) {
@@ -115,6 +121,14 @@ pub fn get_cairo_context (draw_ctx: *const RsvgDrawingCtx) -> cairo::Context {
     }
 }
 
+pub fn set_cairo_context (draw_ctx: *const RsvgDrawingCtx, cr: &cairo::Context) {
+    unsafe {
+        let raw_cr = cr.to_glib_none ().0;
+
+        rsvg_cairo_set_cairo_context (draw_ctx, raw_cr);
+    }
+}
+
 pub fn get_current_state_affine (draw_ctx: *const RsvgDrawingCtx) -> cairo::Matrix {
     unsafe {
         rsvg_drawing_ctx_get_current_state_affine (draw_ctx)
@@ -126,3 +140,21 @@ pub fn set_current_state_affine (draw_ctx: *const RsvgDrawingCtx, affine: cairo:
         rsvg_drawing_ctx_set_current_state_affine (draw_ctx, &affine);
     }
 }
+
+pub fn state_push (draw_ctx: *const RsvgDrawingCtx) {
+    unsafe {
+        rsvg_state_push (draw_ctx);
+    }
+}
+
+pub fn state_pop (draw_ctx: *const RsvgDrawingCtx) {
+    unsafe {
+        rsvg_state_pop (draw_ctx);
+    }
+}
+
+pub fn node_draw_children (draw_ctx: *const RsvgDrawingCtx, c_node: *const RsvgNode, dominate: libc::c_int) {
+    unsafe {
+        _rsvg_node_draw_children (c_node, draw_ctx, dominate);
+    }
+}
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index 37b787a..ef5012c 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -58,6 +58,12 @@ pub use viewbox::{
     RsvgViewBox
 };
 
+pub use pattern::{
+    pattern_new,
+    pattern_destroy,
+    pattern_resolve_fallbacks_and_set_pattern,
+};
+
 
 mod aspect_ratio;
 mod bbox;
@@ -69,6 +75,7 @@ mod marker;
 mod node;
 mod path_builder;
 mod path_parser;
+mod pattern;
 mod property_bag;
 mod state;
 mod shapes;
diff --git a/rust/src/pattern.rs b/rust/src/pattern.rs
index 0073671..e0fc383 100644
--- a/rust/src/pattern.rs
+++ b/rust/src/pattern.rs
@@ -3,8 +3,13 @@ extern crate cairo;
 extern crate cairo_sys;
 extern crate glib;
 
+use std::io;
+use std::io::prelude::*;
+use std::fs::File;
+
 use self::glib::translate::*;
 
+use aspect_ratio::*;
 use length::*;
 
 use drawing_ctx;
@@ -16,19 +21,33 @@ use util::*;
 use viewbox::*;
 
 use self::cairo::MatrixTrait;
-use self::cairo::enums::Content;
+use self::cairo::enums::*;
+use self::cairo::SurfacePattern;
+use self::cairo::Pattern as CairoPattern;
 
 pub struct Pattern {
     pub obj_bbox:              Option<bool>,
     pub obj_cbbox:             Option<bool>,
     pub vbox:                  Option<RsvgViewBox>,
-    pub preserve_aspect_ratio: Option<u32>,
+    pub preserve_aspect_ratio: Option<AspectRatio>,
     pub affine:                Option<cairo::Matrix>,
     pub fallback:              Option<String>,
     pub x:                     Option<RsvgLength>,
     pub y:                     Option<RsvgLength>,
     pub width:                 Option<RsvgLength>,
-    pub height:                Option<RsvgLength>
+    pub height:                Option<RsvgLength>,
+
+    // We just use c_node to see if the C implementation has children
+    pub c_node:                *const RsvgNode
+}
+
+extern "C" {
+    fn rsvg_pattern_node_to_rust_pattern (node: *const RsvgNode) -> *mut Pattern;
+    fn rsvg_pattern_node_has_children (node: *const RsvgNode) -> bool;
+}
+
+fn pattern_node_has_children (c_node: *const RsvgNode) -> bool {
+    unsafe { rsvg_pattern_node_has_children (c_node) }
 }
 
 impl Pattern {
@@ -41,20 +60,21 @@ impl Pattern {
             self.x.is_some () &&
             self.y.is_some () &&
             self.width.is_some () &&
-            self.height.is_some ()
-        // FIXME: which fallback contains the children?
+            self.height.is_some () &&
+            pattern_node_has_children (self.c_node)
     }
 
     fn resolve_from_defaults (&mut self) {
-        /* FIXME: check the spec */
         /* These are per the spec */
 
         if self.obj_bbox.is_none ()  { self.obj_bbox  = Some (true); }
         if self.obj_cbbox.is_none () { self.obj_cbbox = Some (false); }
         if self.vbox.is_none ()      { self.vbox      = Some (RsvgViewBox::new_inactive ()); }
 
-        // FIXME: this is RSVG_ASPECT_RATIO_XMID_YMID; use a constant, not a number.  Spec says "xMidYMid 
meet"
-        if self.preserve_aspect_ratio.is_none () { self.preserve_aspect_ratio = Some (1 << 4); }
+        if self.preserve_aspect_ratio.is_none () {
+            let aspect: AspectRatio = Default::default ();
+            self.preserve_aspect_ratio = Some (aspect);
+        }
 
         if self.affine.is_none ()    { self.affine    = Some (cairo::Matrix::identity ()); }
 
@@ -64,6 +84,9 @@ impl Pattern {
         if self.y.is_none ()         { self.y         = Some (RsvgLength::parse ("0", 
LengthDir::Horizontal)); }
         if self.width.is_none ()     { self.width     = Some (RsvgLength::parse ("0", 
LengthDir::Horizontal)); }
         if self.height.is_none ()    { self.height    = Some (RsvgLength::parse ("0", 
LengthDir::Horizontal)); }
+
+        // We don't resolve the children here - instead, we'll just
+        // NOP if there are no children at drawing time.
     }
 
     fn resolve_from_fallback (&mut self, fallback: &Pattern) {
@@ -83,6 +106,10 @@ impl Pattern {
         if self.fallback.is_none () {
             self.fallback = clone_fallback_name (&fallback.fallback);
         }
+
+        if !pattern_node_has_children (self.c_node) {
+            self.c_node = fallback.c_node;
+        }
     }
 }
 
@@ -99,6 +126,7 @@ impl Clone for Pattern {
             y:                     self.y,
             width:                 self.width,
             height:                self.height,
+            c_node:                self.c_node
         }
     }
 }
@@ -150,10 +178,6 @@ impl Drop for NodeFallbackSource {
     }
 }
 
-extern "C" {
-    fn rsvg_pattern_node_to_rust_pattern (node: *const RsvgNode) -> *mut Pattern;
-}
-
 impl FallbackSource for NodeFallbackSource {
     fn get_fallback (&mut self, name: &str) -> Option<Box<Pattern>> {
         let fallback_node = drawing_ctx::acquire_node (self.draw_ctx, name);
@@ -178,11 +202,14 @@ impl FallbackSource for NodeFallbackSource {
 
 fn set_pattern_on_draw_context (pattern: &Pattern,
                                 draw_ctx: *mut RsvgDrawingCtx,
-                                opacity:  u8,
                                 bbox:     &RsvgBbox) {
     assert! (pattern.is_resolved ());
 
-    let obj_bbox = pattern.obj_bbox.unwrap ();
+    let obj_bbox              = pattern.obj_bbox.unwrap ();
+    let obj_cbbox             = pattern.obj_cbbox.unwrap ();
+    let pattern_affine        = pattern.affine.unwrap ();
+    let vbox                  = pattern.vbox.unwrap ();
+    let preserve_aspect_ratio = pattern.preserve_aspect_ratio.unwrap ();
 
     if obj_bbox {
         drawing_ctx::push_view_box (draw_ctx, 1.0, 1.0);
@@ -210,13 +237,13 @@ fn set_pattern_on_draw_context (pattern: &Pattern,
         bbhscale = 1.0;
     }
 
-    let taffine = cairo::Matrix::multiply (&pattern.affine.unwrap (), &drawing_ctx::get_current_state_affine 
(draw_ctx));
+    let taffine = cairo::Matrix::multiply (&pattern_affine, &drawing_ctx::get_current_state_affine 
(draw_ctx));
 
     let mut scwscale = (taffine.xx * taffine.xx + taffine.xy * taffine.xy).sqrt ();
     let mut schscale = (taffine.yx * taffine.yx + taffine.yy * taffine.yy).sqrt ();
 
-    let pw = pattern_width * bbwscale * scwscale;
-    let ph = pattern_height * bbhscale * schscale;
+    let pw: i32 = (pattern_width * bbwscale * scwscale) as i32;
+    let ph: i32 = (pattern_height * bbhscale * schscale) as i32;
 
     let scaled_width = pattern_width * bbwscale;
     let scaled_height = pattern_height * bbhscale;
@@ -225,14 +252,8 @@ fn set_pattern_on_draw_context (pattern: &Pattern,
         return
     }
 
-    scwscale = pw / scaled_width;
-    schscale = ph / scaled_height;
-
-    let cr = drawing_ctx::get_cairo_context (draw_ctx);
-
-    let surface = cr.get_target ().create_similar (Content::ColorAlpha, pw as i32, ph as i32);
-
-    let cr_pattern = cairo::Context::new (&surface);
+    scwscale = pw as f64 / scaled_width;
+    schscale = ph as f64 / scaled_height;
 
     let mut affine: cairo::Matrix = cairo::Matrix::identity ();
 
@@ -245,17 +266,96 @@ fn set_pattern_on_draw_context (pattern: &Pattern,
     }
 
     // Apply the pattern transform
-    affine = cairo::Matrix::multiply (&affine, pattern.affine.as_ref ().unwrap ());
+    affine = cairo::Matrix::multiply (&affine, &pattern_affine);
+
+    let mut caffine: cairo::Matrix;
 
-    // Create the pattern contents coordinate system
-    if pattern.vbox.unwrap ().active {
+    let pushed_view_box: bool;
+
+        // Create the pattern contents coordinate system
+    if vbox.active {
         // If there is a vbox, use that
-        let w = pattern_width * bbwscale;
-        let h = pattern_height * bbhscale;
-        let mut x: f64 = 0.0;
-        let mut y: f64 = 0.0;
+        let (mut x, mut y, w, h) = preserve_aspect_ratio.compute (vbox.rect.width,
+                                                                  vbox.rect.height,
+                                                                  0.0,
+                                                                  0.0,
+                                                                  pattern_width * bbwscale,
+                                                                  pattern_height * bbhscale);
+
+        x -= vbox.rect.x * w / vbox.rect.width;
+        y -= vbox.rect.y * h / vbox.rect.height;
+
+        caffine = cairo::Matrix::new (w / vbox.rect.width,
+                                      0.0,
+                                      0.0,
+                                      h / vbox.rect.height,
+                                      x,
+                                      y);
+
+        drawing_ctx::push_view_box (draw_ctx, vbox.rect.width, vbox.rect.height);
+        pushed_view_box = true;
+    } else if obj_cbbox {
+        // If coords are in terms of the bounding box, use them
+
+        caffine = cairo::Matrix::identity ();
+        caffine.scale (bbox.rect.width, bbox.rect.height);
+
+        drawing_ctx::push_view_box (draw_ctx, 1.0, 1.0);
+        pushed_view_box = true;
+    } else {
+        caffine = cairo::Matrix::identity ();
+        pushed_view_box = false;
+    }
+
+    if scwscale != 1.0 || schscale != 1.0 {
+        let mut scalematrix = cairo::Matrix::identity ();
+        scalematrix.scale (scwscale, schscale);
+        caffine = cairo::Matrix::multiply (&caffine, &scalematrix);
+
+        scalematrix = cairo::Matrix::identity ();
+        scalematrix.scale (1.0 / scwscale, 1.0 / schscale);
+
+        affine = cairo::Matrix::multiply (&scalematrix, &affine);
+    }
+
+    // Draw to another surface
+
+    let cr_save = drawing_ctx::get_cairo_context (draw_ctx);
+    drawing_ctx::state_push (draw_ctx);
+
+    let surface = cr_save.get_target ().create_similar (Content::ColorAlpha, pw, ph);
+
+    let cr_pattern = cairo::Context::new (&surface);
+
+    drawing_ctx::set_cairo_context (draw_ctx, &cr_pattern);
+
+    // Set up transformations to be determined by the contents units
+    drawing_ctx::set_current_state_affine (draw_ctx, caffine);
+
+    // Draw everything
+    drawing_ctx::node_draw_children (draw_ctx, pattern.c_node, 2);
+
+    // Return to the original coordinate system and rendering context
+
+    drawing_ctx::state_pop (draw_ctx);
+    drawing_ctx::set_cairo_context (draw_ctx, &cr_save);
+
+    if pushed_view_box {
+        drawing_ctx::pop_view_box (draw_ctx);
     }
-    
+
+    // Set the final surface as a Cairo pattern into the Cairo context
+
+    let surface_pattern = SurfacePattern::create (&surface);
+    surface_pattern.set_extend (Extend::Repeat);
+
+    let mut matrix = affine;
+    matrix.invert ();
+
+    surface_pattern.set_matrix (matrix);
+    surface_pattern.set_filter (Filter::Best);
+
+    cr_save.set_source (&surface_pattern);
 }
 
 #[no_mangle]
@@ -268,7 +368,10 @@ pub unsafe extern fn pattern_new (x: *const RsvgLength,
                                   vbox: *const RsvgViewBox,
                                   affine: *const cairo::Matrix,
                                   preserve_aspect_ratio: *const u32,
-                                  fallback_name: *const libc::c_char) -> *mut Pattern {
+                                  fallback_name: *const libc::c_char,
+                                  c_node: *const RsvgNode) -> *mut Pattern {
+    assert! (!c_node.is_null ());
+
     let my_x         = { if x.is_null ()      { None } else { Some (*x) } };
     let my_y         = { if y.is_null ()      { None } else { Some (*y) } };
     let my_width     = { if width.is_null ()  { None } else { Some (*width) } };
@@ -280,7 +383,7 @@ pub unsafe extern fn pattern_new (x: *const RsvgLength,
 
     let my_affine    = { if affine.is_null () { None } else { Some (*affine) } };
 
-    let my_preserve_aspect_ratio = { if preserve_aspect_ratio.is_null () { None } else { Some 
(*preserve_aspect_ratio) } };
+    let my_preserve_aspect_ratio = { if preserve_aspect_ratio.is_null () { None } else { Some 
(AspectRatio::from_u32 (*preserve_aspect_ratio)) } };
 
     let my_fallback_name = { if fallback_name.is_null () { None } else { Some (String::from_glib_none 
(fallback_name)) } };
 
@@ -294,7 +397,8 @@ pub unsafe extern fn pattern_new (x: *const RsvgLength,
         x:                     my_x,
         y:                     my_y,
         width:                 my_width,
-        height:                my_height
+        height:                my_height,
+        c_node:                c_node
     };
 
     let boxed_pattern = Box::new (pattern);
@@ -312,7 +416,6 @@ pub unsafe extern fn pattern_destroy (raw_pattern: *mut Pattern) {
 #[no_mangle]
 pub extern fn pattern_resolve_fallbacks_and_set_pattern (raw_pattern: *mut Pattern,
                                                          draw_ctx:    *mut RsvgDrawingCtx,
-                                                         opacity:     u8,
                                                          bbox:        RsvgBbox) {
     assert! (!raw_pattern.is_null ());
     let pattern: &mut Pattern = unsafe { &mut (*raw_pattern) };
@@ -323,6 +426,5 @@ pub extern fn pattern_resolve_fallbacks_and_set_pattern (raw_pattern: *mut Patte
 
     set_pattern_on_draw_context (&resolved,
                                  draw_ctx,
-                                 opacity,
                                  &bbox);
 }


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