[librsvg: 8/10] drawing_ctx: rename some methods to use the right naming convention



commit 9bddd4363572373f9e996d646c53e7cd33766535
Author: Paolo Borelli <pborelli gnome org>
Date:   Sun May 13 16:01:10 2018 +0200

    drawing_ctx: rename some methods to use the right naming convention
    
    Also move the pixbuf related functions to file-util.c, despite the
    name, this is where all the other pixbuf related function are.

 librsvg/filters/image.c           |    2 +-
 librsvg/rsvg-base.c               |   12 -
 librsvg/rsvg-drawing-ctx.c        | 1090 +++++++++++++++----------------------
 librsvg/rsvg-drawing-ctx.h        |   31 +-
 librsvg/rsvg-file-util.c          |  204 +++++++
 rsvg_internals/src/drawing_ctx.rs |   88 +--
 6 files changed, 712 insertions(+), 715 deletions(-)
---
diff --git a/librsvg/filters/image.c b/librsvg/filters/image.c
index d856dd5b..e8e09813 100644
--- a/librsvg/filters/image.c
+++ b/librsvg/filters/image.c
@@ -57,7 +57,7 @@ rsvg_filter_primitive_image_render_in (RsvgFilterPrimitiveImage *image, RsvgFilt
 
     rsvg_state_set_affine (rsvg_drawing_ctx_get_current_state (ctx), context->paffine);
 
-    result = rsvg_cairo_get_surface_of_node (ctx, drawable, context->width, context->height);
+    result = rsvg_drawing_ctx_get_surface_of_node (ctx, drawable, context->width, context->height);
 
     rsvg_drawing_ctx_release_node (ctx, drawable);
 
diff --git a/librsvg/rsvg-base.c b/librsvg/rsvg-base.c
index 9c958b56..14e2dc37 100644
--- a/librsvg/rsvg-base.c
+++ b/librsvg/rsvg-base.c
@@ -332,18 +332,6 @@ rsvg_cleanup (void)
     xmlCleanupParser ();
 }
 
-void
-rsvg_pop_discrete_layer (RsvgDrawingCtx *ctx, gboolean clipping)
-{
-    rsvg_cairo_pop_discrete_layer (ctx, clipping);
-}
-
-void
-rsvg_push_discrete_layer (RsvgDrawingCtx *ctx, gboolean clipping)
-{
-    rsvg_cairo_push_discrete_layer (ctx, clipping);
-}
-
 cairo_surface_t *
 rsvg_cairo_surface_new_from_href (RsvgHandle *handle,
                                   const char *href,
diff --git a/librsvg/rsvg-drawing-ctx.c b/librsvg/rsvg-drawing-ctx.c
index cec0e509..f7356f2c 100644
--- a/librsvg/rsvg-drawing-ctx.c
+++ b/librsvg/rsvg-drawing-ctx.c
@@ -52,137 +52,6 @@ void rsvg_cairo_add_clipping_rect (RsvgDrawingCtx *ctx,
                                    double w,
                                    double h);
 
-#ifdef HAVE_PANGOFT2
-static cairo_font_options_t *
-get_font_options_for_testing (void)
-{
-    cairo_font_options_t *options;
-
-    options = cairo_font_options_create ();
-    cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
-    cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_FULL);
-    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
-
-    return options;
-}
-
-static void
-set_font_options_for_testing (PangoContext *context)
-{
-    cairo_font_options_t *font_options;
-
-    font_options = get_font_options_for_testing ();
-    pango_cairo_context_set_font_options (context, font_options);
-    cairo_font_options_destroy (font_options);
-}
-
-static void
-create_font_config_for_testing (RsvgDrawingCtx *ctx)
-{
-    const char *font_paths[] = {
-        SRCDIR "/tests/resources/Roboto-Regular.ttf",
-        SRCDIR "/tests/resources/Roboto-Italic.ttf",
-        SRCDIR "/tests/resources/Roboto-Bold.ttf",
-        SRCDIR "/tests/resources/Roboto-BoldItalic.ttf",
-    };
-
-    int i;
-
-    if (ctx->font_config_for_testing != NULL)
-        return;
-
-    ctx->font_config_for_testing = FcConfigCreate ();
-
-    for (i = 0; i < G_N_ELEMENTS(font_paths); i++) {
-        if (!FcConfigAppFontAddFile (ctx->font_config_for_testing, (const FcChar8 *) font_paths[i])) {
-            g_error ("Could not load font file \"%s\" for tests; aborting", font_paths[i]);
-        }
-    }
-}
-
-static PangoFontMap *
-get_font_map_for_testing (RsvgDrawingCtx *ctx)
-{
-    create_font_config_for_testing (ctx);
-
-    if (ctx->font_map_for_testing == NULL) {
-        ctx->font_map_for_testing = pango_cairo_font_map_new_for_font_type (CAIRO_FONT_TYPE_FT);
-        pango_fc_font_map_set_config (PANGO_FC_FONT_MAP (ctx->font_map_for_testing),
-                                      ctx->font_config_for_testing);
-    }
-
-    return ctx->font_map_for_testing;
-}
-#endif
-
-PangoContext *
-rsvg_cairo_get_pango_context (RsvgDrawingCtx * ctx)
-{
-    PangoFontMap *fontmap;
-    PangoContext *context;
-    double dpi_y;
-
-#ifdef HAVE_PANGOFT2
-    if (ctx->is_testing) {
-        fontmap = get_font_map_for_testing (ctx);
-    } else {
-#endif
-        fontmap = pango_cairo_font_map_get_default ();
-#ifdef HAVE_PANGOFT2
-    }
-#endif
-
-    context = pango_font_map_create_context (fontmap);
-    pango_cairo_update_context (ctx->cr, context);
-
-    rsvg_drawing_ctx_get_dpi (ctx, NULL, &dpi_y);
-    pango_cairo_context_set_resolution (context, dpi_y);
-
-#ifdef HAVE_PANGOFT2
-    if (ctx->is_testing) {
-        set_font_options_for_testing (context);
-    }
-#endif
-
-    return context;
-}
-
-cairo_t *
-rsvg_cairo_get_cairo_context (RsvgDrawingCtx *ctx)
-{
-    return ctx->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)
-{
-    ctx->cr = cr;
-}
-
-gboolean
-rsvg_cairo_is_cairo_context_nested (RsvgDrawingCtx *ctx, cairo_t *cr)
-{
-    return cr != ctx->initial_cr;
-}
-
 static void
 rsvg_cairo_generate_mask (cairo_t * cr, RsvgNode *mask, RsvgDrawingCtx *ctx)
 {
@@ -387,136 +256,221 @@ rsvg_cairo_clip (RsvgDrawingCtx *ctx, RsvgNode *node_clip_path, RsvgBbox *bbox)
 }
 
 static void
-push_bounding_box (RsvgDrawingCtx *ctx)
+rsvg_cairo_transformed_image_bounding_box (cairo_matrix_t *affine,
+                                           double width, double height,
+                                           double *x0, double *y0, double *x1, double *y1)
 {
-    RsvgState *state;
-    cairo_matrix_t affine;
-    RsvgBbox *bbox, *ink_bbox;
+    double x00 = 0, x01 = 0, x10 = width, x11 = width;
+    double y00 = 0, y01 = height, y10 = 0, y11 = height;
+    double t;
 
-    state = rsvg_drawing_ctx_get_current_state (ctx);
+    /* transform the four corners of the image */
+    cairo_matrix_transform_point (affine, &x00, &y00);
+    cairo_matrix_transform_point (affine, &x01, &y01);
+    cairo_matrix_transform_point (affine, &x10, &y10);
+    cairo_matrix_transform_point (affine, &x11, &y11);
 
-    bbox = g_new0 (RsvgBbox, 1);
-    *bbox = ctx->bbox;
-    ctx->bb_stack = g_list_prepend (ctx->bb_stack, bbox);
+    /* find minimum and maximum coordinates */
+    t = x00  < x01 ? x00  : x01;
+    t = t < x10 ? t : x10;
+    *x0 = floor (t < x11 ? t : x11);
 
-    ink_bbox = g_new0 (RsvgBbox, 1);
-    *ink_bbox = ctx->ink_bbox;
-    ctx->ink_bb_stack = g_list_prepend (ctx->ink_bb_stack, ink_bbox);
+    t = y00  < y01 ? y00  : y01;
+    t = t < y10 ? t : y10;
+    *y0 = floor (t < y11 ? t : y11);
 
-    affine = rsvg_state_get_affine (state);
-    rsvg_bbox_init (&ctx->bbox, &affine);
-    rsvg_bbox_init (&ctx->ink_bbox, &affine);
+    t = x00  > x01 ? x00  : x01;
+    t = t > x10 ? t : x10;
+    *x1 = ceil (t > x11 ? t : x11);
+
+    t = y00  > y01 ? y00  : y01;
+    t = t > y10 ? t : y10;
+    *y1 = ceil (t > y11 ? t : y11);
 }
 
-static void
-rsvg_cairo_push_render_stack (RsvgDrawingCtx * ctx)
+RsvgDrawingCtx *
+rsvg_drawing_ctx_new (cairo_t *cr, RsvgHandle *handle)
 {
+    RsvgDimensionData data;
+    RsvgDrawingCtx *draw;
     RsvgState *state;
-    char *clip_path;
-    char *filter;
-    char *mask;
-    guint8 opacity;
-    cairo_operator_t comp_op;
-    RsvgEnableBackgroundType enable_background;
-    cairo_surface_t *surface;
-    cairo_t *child_cr;
-    gboolean lateclip = FALSE;
+    cairo_matrix_t affine;
+    cairo_matrix_t state_affine;
+    double bbx0, bby0, bbx1, bby1;
 
-    state = rsvg_drawing_ctx_get_current_state (ctx);
-    clip_path = rsvg_state_get_clip_path (state);
-    filter = rsvg_state_get_filter (state);
-    mask = rsvg_state_get_mask (state);
-    opacity = rsvg_state_get_opacity (state);
-    comp_op = rsvg_state_get_comp_op (state);
-    enable_background = rsvg_state_get_enable_background (state);
+    rsvg_handle_get_dimensions (handle, &data);
+    if (data.width == 0 || data.height == 0)
+        return NULL;
 
-    if (clip_path) {
-        RsvgNode *node;
-        node = rsvg_drawing_ctx_acquire_node_of_type (ctx, clip_path, RSVG_NODE_TYPE_CLIP_PATH);
-        if (node) {
-            switch (rsvg_node_clip_path_get_units (node)) {
-            case userSpaceOnUse:
-                rsvg_cairo_clip (ctx, node, NULL);
-                break;
-            case objectBoundingBox:
-                lateclip = TRUE;
-                break;
+    draw = g_new0 (RsvgDrawingCtx, 1);
 
-            default:
-                g_assert_not_reached ();
-                break;
-            }
+    cairo_get_matrix (cr, &affine);
 
-            rsvg_drawing_ctx_release_node (ctx, node);
-        }
+    /* find bounding box of image as transformed by the current cairo context
+     * The size of this bounding box determines the size of the intermediate
+     * surfaces allocated during drawing. */
+    rsvg_cairo_transformed_image_bounding_box (&affine,
+                                               data.width, data.height,
+                                               &bbx0, &bby0, &bbx1, &bby1);
 
-        g_free (clip_path);
-    }
+    draw->initial_cr = cr;
+    draw->cr = cr;
+    draw->cr_stack = NULL;
+    draw->surfaces_stack = NULL;
 
-    if (opacity == 0xFF
-        && !filter && !mask && !lateclip && (comp_op == CAIRO_OPERATOR_OVER)
-        && (enable_background == RSVG_ENABLE_BACKGROUND_ACCUMULATE))
-        return;
+    draw->offset_x = bbx0;
+    draw->offset_y = bby0;
+    draw->width = bbx1 - bbx0;
+    draw->height = bby1 - bby0;
 
-    g_free (mask);
+    draw->state = NULL;
 
-    if (!filter) {
-        surface = cairo_surface_create_similar (cairo_get_target (ctx->cr),
-                                                CAIRO_CONTENT_COLOR_ALPHA,
-                                                ctx->width, ctx->height);
-    } else {
-        surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-                                              ctx->width, ctx->height);
+    draw->defs = handle->priv->defs;
+    draw->dpi_x = handle->priv->dpi_x;
+    draw->dpi_y = handle->priv->dpi_y;
+    draw->vb.rect.width = data.em;
+    draw->vb.rect.height = data.ex;
+    draw->vb_stack = NULL;
+    draw->drawsub_stack = NULL;
+    draw->acquired_nodes = NULL;
+    draw->is_testing = handle->priv->is_testing;
 
-        /* The surface reference is owned by the child_cr created below and put on the cr_stack! */
-        ctx->surfaces_stack = g_list_prepend (ctx->surfaces_stack, surface);
+    rsvg_drawing_ctx_state_push (draw);
+    state = rsvg_drawing_ctx_get_current_state (draw);
 
-        g_free (filter);
-    }
+    state_affine = rsvg_state_get_affine (state);
 
-#if 0
-    if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
-        cairo_surface_destroy (surface);
-        return;
+    /* apply cairo transformation to our affine transform */
+    cairo_matrix_multiply (&state_affine, &affine, &state_affine);
+
+    /* scale according to size set by size_func callback */
+    cairo_matrix_init_scale (&affine, data.width / data.em, data.height / data.ex);
+    cairo_matrix_multiply (&state_affine, &affine, &state_affine);
+
+    /* adjust transform so that the corner of the bounding box above is
+     * at (0,0) - we compensate for this in _set_rsvg_affine() in
+     * rsvg-cairo-render.c and a few other places */
+    state_affine.x0 -= draw->offset_x;
+    state_affine.y0 -= draw->offset_y;
+
+    rsvg_bbox_init (&draw->bbox, &state_affine);
+    rsvg_bbox_init (&draw->ink_bbox, &state_affine);
+
+    rsvg_state_set_affine (state, state_affine);
+
+#ifdef HAVE_PANGOFT2
+    draw->font_config_for_testing = NULL;
+    draw->font_map_for_testing = NULL;
+#endif
+
+    return draw;
+}
+
+void
+rsvg_drawing_ctx_free (RsvgDrawingCtx *ctx)
+{
+    g_assert (ctx->cr_stack == NULL);
+    g_assert (ctx->surfaces_stack == NULL);
+
+    g_assert(ctx->state);
+    g_assert(rsvg_state_parent(ctx->state) == NULL);
+    rsvg_state_free (ctx->state);
+
+       g_slist_free_full (ctx->drawsub_stack, (GDestroyNotify) rsvg_node_unref);
+
+    g_warn_if_fail (ctx->acquired_nodes == NULL);
+    g_slist_free (ctx->acquired_nodes);
+
+    g_assert (ctx->bb_stack == NULL);
+    g_assert (ctx->ink_bb_stack == NULL);
+
+#ifdef HAVE_PANGOFT2
+    if (ctx->font_config_for_testing) {
+        FcConfigDestroy (ctx->font_config_for_testing);
+        ctx->font_config_for_testing = NULL;
+    }
+
+    if (ctx->font_map_for_testing) {
+        g_object_unref (ctx->font_map_for_testing);
+        ctx->font_map_for_testing = NULL;
     }
 #endif
 
-    child_cr = cairo_create (surface);
-    cairo_surface_destroy (surface);
+    g_free (ctx);
+}
 
-    ctx->cr_stack = g_list_prepend (ctx->cr_stack, ctx->cr);
-    ctx->cr = child_cr;
+cairo_t *
+rsvg_drawing_ctx_get_cairo_context (RsvgDrawingCtx *ctx)
+{
+    return ctx->cr;
+}
 
-    push_bounding_box (ctx);
+/* FIXME: Usage of this function is more less a hack.  Some code does this:
+ *
+ *   save_cr = rsvg_drawing_ctx_get_cairo_context (ctx);
+ *
+ *   some_surface = create_surface ();
+ *
+ *   cr = cairo_create (some_surface);
+ *
+ *   rsvg_drawing_ctx_set_cairo_context (ctx, cr);
+ *
+ *   ... draw with ctx but to that temporary surface
+ *
+ *   rsvg_drawing_ctx_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_drawing_ctx_set_cairo_context (RsvgDrawingCtx *ctx, cairo_t *cr)
+{
+    ctx->cr = cr;
+}
+
+gboolean
+rsvg_drawing_ctx_is_cairo_context_nested (RsvgDrawingCtx *ctx, cairo_t *cr)
+{
+    return cr != ctx->initial_cr;
+}
+
+RsvgState *
+rsvg_drawing_ctx_get_current_state (RsvgDrawingCtx *ctx)
+{
+    return ctx->state;
 }
 
 void
-rsvg_cairo_push_discrete_layer (RsvgDrawingCtx * ctx, gboolean clipping)
+rsvg_drawing_ctx_set_current_state (RsvgDrawingCtx *ctx, RsvgState *state)
 {
-    if (!clipping) {
-        cairo_save (ctx->cr);
-        rsvg_cairo_push_render_stack (ctx);
-    }
+    ctx->state = state;
 }
 
 static void
-pop_bounding_box (RsvgDrawingCtx *ctx)
+push_bounding_box (RsvgDrawingCtx *ctx)
 {
-    rsvg_bbox_insert ((RsvgBbox *) ctx->bb_stack->data, &ctx->bbox);
-    rsvg_bbox_insert ((RsvgBbox *) ctx->ink_bb_stack->data, &ctx->ink_bbox);
+    RsvgState *state;
+    cairo_matrix_t affine;
+    RsvgBbox *bbox, *ink_bbox;
 
-    ctx->bbox = *((RsvgBbox *) ctx->bb_stack->data);
-    ctx->ink_bbox = *((RsvgBbox *) ctx->ink_bb_stack->data);
+    state = rsvg_drawing_ctx_get_current_state (ctx);
 
-    g_free (ctx->bb_stack->data);
-    g_free (ctx->ink_bb_stack->data);
+    bbox = g_new0 (RsvgBbox, 1);
+    *bbox = ctx->bbox;
+    ctx->bb_stack = g_list_prepend (ctx->bb_stack, bbox);
 
-    ctx->bb_stack = g_list_delete_link (ctx->bb_stack, ctx->bb_stack);
-    ctx->ink_bb_stack = g_list_delete_link (ctx->ink_bb_stack, ctx->ink_bb_stack);
+    ink_bbox = g_new0 (RsvgBbox, 1);
+    *ink_bbox = ctx->ink_bbox;
+    ctx->ink_bb_stack = g_list_prepend (ctx->ink_bb_stack, ink_bbox);
+
+    affine = rsvg_state_get_affine (state);
+    rsvg_bbox_init (&ctx->bbox, &affine);
+    rsvg_bbox_init (&ctx->ink_bbox, &affine);
 }
 
 static void
-rsvg_cairo_pop_render_stack (RsvgDrawingCtx * ctx)
+rsvg_drawing_ctx_push_render_stack (RsvgDrawingCtx * ctx)
 {
     RsvgState *state;
     char *clip_path;
@@ -525,11 +479,9 @@ rsvg_cairo_pop_render_stack (RsvgDrawingCtx * ctx)
     guint8 opacity;
     cairo_operator_t comp_op;
     RsvgEnableBackgroundType enable_background;
-    cairo_t *child_cr = ctx->cr;
-    RsvgNode *lateclip = NULL;
-    cairo_surface_t *surface = NULL;
-    gboolean needs_destroy = FALSE;
-    double offset_x = 0, offset_y = 0;
+    cairo_surface_t *surface;
+    cairo_t *child_cr;
+    gboolean lateclip = FALSE;
 
     state = rsvg_drawing_ctx_get_current_state (ctx);
     clip_path = rsvg_state_get_clip_path (state);
@@ -541,14 +493,22 @@ rsvg_cairo_pop_render_stack (RsvgDrawingCtx * ctx)
 
     if (clip_path) {
         RsvgNode *node;
-
         node = rsvg_drawing_ctx_acquire_node_of_type (ctx, clip_path, RSVG_NODE_TYPE_CLIP_PATH);
         if (node) {
-            if (rsvg_node_clip_path_get_units (node) == objectBoundingBox) {
-                lateclip = node;
-            } else {
-                rsvg_drawing_ctx_release_node (ctx, node);
+            switch (rsvg_node_clip_path_get_units (node)) {
+            case userSpaceOnUse:
+                rsvg_cairo_clip (ctx, node, NULL);
+                break;
+            case objectBoundingBox:
+                lateclip = TRUE;
+                break;
+
+            default:
+                g_assert_not_reached ();
+                break;
             }
+
+            rsvg_drawing_ctx_release_node (ctx, node);
         }
 
         g_free (clip_path);
@@ -559,476 +519,176 @@ rsvg_cairo_pop_render_stack (RsvgDrawingCtx * ctx)
         && (enable_background == RSVG_ENABLE_BACKGROUND_ACCUMULATE))
         return;
 
-    surface = cairo_get_target (child_cr);
-
-    if (filter) {
-        RsvgNode *node;
-        cairo_surface_t *output;
-
-        output = ctx->surfaces_stack->data;
-        ctx->surfaces_stack = g_list_delete_link (ctx->surfaces_stack, ctx->surfaces_stack);
+    g_free (mask);
 
-        node = rsvg_drawing_ctx_acquire_node_of_type (ctx, filter, RSVG_NODE_TYPE_FILTER);
-        if (node) {
-            needs_destroy = TRUE;
-            surface = rsvg_filter_render (node, output, ctx, "2103");
-            rsvg_drawing_ctx_release_node (ctx, node);
+    if (!filter) {
+        surface = cairo_surface_create_similar (cairo_get_target (ctx->cr),
+                                                CAIRO_CONTENT_COLOR_ALPHA,
+                                                ctx->width, ctx->height);
+    } else {
+        surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                              ctx->width, ctx->height);
 
-            /* Don't destroy the output surface, it's owned by child_cr */
-        }
+        /* The surface reference is owned by the child_cr created below and put on the cr_stack! */
+        ctx->surfaces_stack = g_list_prepend (ctx->surfaces_stack, surface);
 
         g_free (filter);
     }
 
-    ctx->cr = (cairo_t *) ctx->cr_stack->data;
-    ctx->cr_stack = g_list_delete_link (ctx->cr_stack, ctx->cr_stack);
-
-    if (ctx->cr == ctx->initial_cr) {
-        rsvg_drawing_ctx_get_offset (ctx, &offset_x, &offset_y);
-    }
-
-    cairo_identity_matrix (ctx->cr);
-    cairo_set_source_surface (ctx->cr, surface, offset_x, offset_y);
-
-    if (lateclip) {
-        rsvg_cairo_clip (ctx, lateclip, &ctx->bbox);
-        rsvg_drawing_ctx_release_node (ctx, lateclip);
-    }
-
-    cairo_set_operator (ctx->cr, comp_op);
-
-    if (mask) {
-        RsvgNode *node;
-
-        node = rsvg_drawing_ctx_acquire_node_of_type (ctx, mask, RSVG_NODE_TYPE_MASK);
-        if (node) {
-            rsvg_cairo_generate_mask (ctx->cr, node, ctx);
-            rsvg_drawing_ctx_release_node (ctx, node);
-        }
-
-        g_free (mask);
-    } else if (opacity != 0xFF)
-        cairo_paint_with_alpha (ctx->cr, (double) opacity / 255.0);
-    else
-        cairo_paint (ctx->cr);
-
-    cairo_destroy (child_cr);
-
-    pop_bounding_box (ctx);
-
-    if (needs_destroy) {
-        cairo_surface_destroy (surface);
-    }
-}
-
-void
-rsvg_cairo_pop_discrete_layer (RsvgDrawingCtx * ctx, gboolean clipping)
-{
-    if (!clipping) {
-        rsvg_cairo_pop_render_stack (ctx);
-        cairo_restore (ctx->cr);
-    }
-}
-
-cairo_surface_t *
-rsvg_cairo_get_surface_of_node (RsvgDrawingCtx *ctx,
-                                RsvgNode *drawable,
-                                double width,
-                                double height)
-{
-    cairo_surface_t *surface;
-    cairo_t *cr;
-    cairo_t *save_cr = ctx->cr;
-    cairo_t *save_initial_cr = ctx->initial_cr;
-    double save_x = ctx->offset_x;
-    double save_y = ctx->offset_y;
-    double save_w = ctx->width;
-    double save_h = ctx->height;
-
-    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
-    if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
-        cairo_surface_destroy (surface);
-        return NULL;
-    }
-
-    ctx->cr = cairo_create (surface);
-    ctx->initial_cr = ctx->cr;
-    ctx->offset_x = 0;
-    ctx->offset_y = 0;
-    ctx->width = width;
-    ctx->height = height;
-
-    rsvg_drawing_ctx_draw_node_from_stack (ctx, drawable, 0, FALSE);
-
-    cairo_destroy (ctx->cr);
-    ctx->cr = save_cr;
-    ctx->initial_cr = save_initial_cr;
-    ctx->offset_x = save_x;
-    ctx->offset_y = save_y;
-    ctx->width = save_w;
-    ctx->height = save_h;
-
-    return surface;
-}
-
-cairo_surface_t *
-rsvg_cairo_surface_from_pixbuf (const GdkPixbuf *pixbuf)
-{
-    gint width, height, gdk_rowstride, n_channels, cairo_rowstride;
-    guchar *gdk_pixels, *cairo_pixels;
-    cairo_format_t format;
-    cairo_surface_t *surface;
-    int j;
-
-    if (pixbuf == NULL)
-        return NULL;
-
-    width = gdk_pixbuf_get_width (pixbuf);
-    height = gdk_pixbuf_get_height (pixbuf);
-    gdk_pixels = gdk_pixbuf_get_pixels (pixbuf);
-    gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
-    n_channels = gdk_pixbuf_get_n_channels (pixbuf);
-
-    if (n_channels == 3)
-        format = CAIRO_FORMAT_RGB24;
-    else
-        format = CAIRO_FORMAT_ARGB32;
-
-    surface = cairo_image_surface_create (format, width, height);
+#if 0
     if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
         cairo_surface_destroy (surface);
-        return NULL;
+        return;
     }
-
-    cairo_pixels = cairo_image_surface_get_data (surface);
-    cairo_rowstride = cairo_image_surface_get_stride (surface);
-
-    if (n_channels == 3) {
-        for (j = height; j; j--) {
-            guchar *p = gdk_pixels;
-            guchar *q = cairo_pixels;
-            guchar *end = p + 3 * width;
-
-            while (p < end) {
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
-                q[0] = p[2];
-                q[1] = p[1];
-                q[2] = p[0];
-#else
-                q[1] = p[0];
-                q[2] = p[1];
-                q[3] = p[2];
 #endif
-                p += 3;
-                q += 4;
-            }
-
-            gdk_pixels += gdk_rowstride;
-            cairo_pixels += cairo_rowstride;
-        }
-    } else {
-        for (j = height; j; j--) {
-            guchar *p = gdk_pixels;
-            guchar *q = cairo_pixels;
-            guchar *end = p + 4 * width;
-            guint t1, t2, t3;
-
-#define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END
-
-            while (p < end) {
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
-                MULT (q[0], p[2], p[3], t1);
-                MULT (q[1], p[1], p[3], t2);
-                MULT (q[2], p[0], p[3], t3);
-                q[3] = p[3];
-#else
-                q[0] = p[3];
-                MULT (q[1], p[0], p[3], t1);
-                MULT (q[2], p[1], p[3], t2);
-                MULT (q[3], p[2], p[3], t3);
-#endif
-
-                p += 4;
-                q += 4;
-            }
-
-#undef MULT
-            gdk_pixels += gdk_rowstride;
-            cairo_pixels += cairo_rowstride;
-        }
-    }
 
-    cairo_surface_mark_dirty (surface);
-    return surface;
-}
+    child_cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
 
-/* Copied from gtk+/gdk/gdkpixbuf-drawable.c, LGPL 2+.
- *
- * Copyright (C) 1999 Michael Zucchi
- *
- * Authors: Michael Zucchi <zucchi zedzone mmc com au>
- *          Cody Russell <bratsche dfw net>
- *          Federico Mena-Quintero <federico gimp org>
- */
+    ctx->cr_stack = g_list_prepend (ctx->cr_stack, ctx->cr);
+    ctx->cr = child_cr;
 
-static void
-convert_alpha (guchar *dest_data,
-               int     dest_stride,
-               guchar *src_data,
-               int     src_stride,
-               int     src_x,
-               int     src_y,
-               int     width,
-               int     height)
-{
-    int x, y;
-
-    src_data += src_stride * src_y + src_x * 4;
-
-    for (y = 0; y < height; y++) {
-        guint32 *src = (guint32 *) src_data;
-
-        for (x = 0; x < width; x++) {
-          guint alpha = src[x] >> 24;
-
-          if (alpha == 0) {
-              dest_data[x * 4 + 0] = 0;
-              dest_data[x * 4 + 1] = 0;
-              dest_data[x * 4 + 2] = 0;
-          } else {
-              dest_data[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
-              dest_data[x * 4 + 1] = (((src[x] & 0x00ff00) >>  8) * 255 + alpha / 2) / alpha;
-              dest_data[x * 4 + 2] = (((src[x] & 0x0000ff) >>  0) * 255 + alpha / 2) / alpha;
-          }
-          dest_data[x * 4 + 3] = alpha;
-      }
-
-      src_data += src_stride;
-      dest_data += dest_stride;
-    }
+    push_bounding_box (ctx);
 }
 
 static void
-convert_no_alpha (guchar *dest_data,
-                  int     dest_stride,
-                  guchar *src_data,
-                  int     src_stride,
-                  int     src_x,
-                  int     src_y,
-                  int     width,
-                  int     height)
-{
-    int x, y;
-
-    src_data += src_stride * src_y + src_x * 4;
-
-    for (y = 0; y < height; y++) {
-        guint32 *src = (guint32 *) src_data;
-
-        for (x = 0; x < width; x++) {
-            dest_data[x * 3 + 0] = src[x] >> 16;
-            dest_data[x * 3 + 1] = src[x] >>  8;
-            dest_data[x * 3 + 2] = src[x];
-        }
-
-        src_data += src_stride;
-        dest_data += dest_stride;
-    }
-}
-
-GdkPixbuf *
-rsvg_cairo_surface_to_pixbuf (cairo_surface_t *surface)
+pop_bounding_box (RsvgDrawingCtx *ctx)
 {
-    cairo_content_t content;
-    GdkPixbuf *dest;
-    int width, height;
+    rsvg_bbox_insert ((RsvgBbox *) ctx->bb_stack->data, &ctx->bbox);
+    rsvg_bbox_insert ((RsvgBbox *) ctx->ink_bb_stack->data, &ctx->ink_bbox);
 
-    /* General sanity checks */
-    g_assert (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE);
+    ctx->bbox = *((RsvgBbox *) ctx->bb_stack->data);
+    ctx->ink_bbox = *((RsvgBbox *) ctx->ink_bb_stack->data);
 
-    width = cairo_image_surface_get_width (surface);
-    height = cairo_image_surface_get_height (surface);
-    if (width == 0 || height == 0)
-        return NULL;
+    g_free (ctx->bb_stack->data);
+    g_free (ctx->ink_bb_stack->data);
 
-    content = cairo_surface_get_content (surface) | CAIRO_CONTENT_COLOR;
-    dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
-                          !!(content & CAIRO_CONTENT_ALPHA),
-                          8,
-                          width, height);
-
-    if (gdk_pixbuf_get_has_alpha (dest))
-      convert_alpha (gdk_pixbuf_get_pixels (dest),
-                    gdk_pixbuf_get_rowstride (dest),
-                    cairo_image_surface_get_data (surface),
-                    cairo_image_surface_get_stride (surface),
-                    0, 0,
-                    width, height);
-    else
-      convert_no_alpha (gdk_pixbuf_get_pixels (dest),
-                        gdk_pixbuf_get_rowstride (dest),
-                        cairo_image_surface_get_data (surface),
-                        cairo_image_surface_get_stride (surface),
-                        0, 0,
-                        width, height);
-
-    return dest;
+    ctx->bb_stack = g_list_delete_link (ctx->bb_stack, ctx->bb_stack);
+    ctx->ink_bb_stack = g_list_delete_link (ctx->ink_bb_stack, ctx->ink_bb_stack);
 }
 
 static void
-rsvg_cairo_transformed_image_bounding_box (cairo_matrix_t *affine,
-                                           double width, double height,
-                                           double *x0, double *y0, double *x1, double *y1)
+rsvg_drawing_ctx_pop_render_stack (RsvgDrawingCtx * ctx)
 {
-    double x00 = 0, x01 = 0, x10 = width, x11 = width;
-    double y00 = 0, y01 = height, y10 = 0, y11 = height;
-    double t;
-
-    /* transform the four corners of the image */
-    cairo_matrix_transform_point (affine, &x00, &y00);
-    cairo_matrix_transform_point (affine, &x01, &y01);
-    cairo_matrix_transform_point (affine, &x10, &y10);
-    cairo_matrix_transform_point (affine, &x11, &y11);
-
-    /* find minimum and maximum coordinates */
-    t = x00  < x01 ? x00  : x01;
-    t = t < x10 ? t : x10;
-    *x0 = floor (t < x11 ? t : x11);
-
-    t = y00  < y01 ? y00  : y01;
-    t = t < y10 ? t : y10;
-    *y0 = floor (t < y11 ? t : y11);
-
-    t = x00  > x01 ? x00  : x01;
-    t = t > x10 ? t : x10;
-    *x1 = ceil (t > x11 ? t : x11);
-
-    t = y00  > y01 ? y00  : y01;
-    t = t > y10 ? t : y10;
-    *y1 = ceil (t > y11 ? t : y11);
-}
-
-RsvgDrawingCtx *
-rsvg_drawing_ctx_new (cairo_t *cr, RsvgHandle *handle)
-{
-    RsvgDimensionData data;
-    RsvgDrawingCtx *draw;
     RsvgState *state;
-    cairo_matrix_t affine;
-    cairo_matrix_t state_affine;
-    double bbx0, bby0, bbx1, bby1;
-
-    rsvg_handle_get_dimensions (handle, &data);
-    if (data.width == 0 || data.height == 0)
-        return NULL;
-
-    draw = g_new0 (RsvgDrawingCtx, 1);
+    char *clip_path;
+    char *filter;
+    char *mask;
+    guint8 opacity;
+    cairo_operator_t comp_op;
+    RsvgEnableBackgroundType enable_background;
+    cairo_t *child_cr = ctx->cr;
+    RsvgNode *lateclip = NULL;
+    cairo_surface_t *surface = NULL;
+    gboolean needs_destroy = FALSE;
+    double offset_x = 0, offset_y = 0;
 
-    cairo_get_matrix (cr, &affine);
+    state = rsvg_drawing_ctx_get_current_state (ctx);
+    clip_path = rsvg_state_get_clip_path (state);
+    filter = rsvg_state_get_filter (state);
+    mask = rsvg_state_get_mask (state);
+    opacity = rsvg_state_get_opacity (state);
+    comp_op = rsvg_state_get_comp_op (state);
+    enable_background = rsvg_state_get_enable_background (state);
 
-    /* find bounding box of image as transformed by the current cairo context
-     * The size of this bounding box determines the size of the intermediate
-     * surfaces allocated during drawing. */
-    rsvg_cairo_transformed_image_bounding_box (&affine,
-                                               data.width, data.height,
-                                               &bbx0, &bby0, &bbx1, &bby1);
+    if (clip_path) {
+        RsvgNode *node;
 
-    draw->initial_cr = cr;
-    draw->cr = cr;
-    draw->cr_stack = NULL;
-    draw->surfaces_stack = NULL;
+        node = rsvg_drawing_ctx_acquire_node_of_type (ctx, clip_path, RSVG_NODE_TYPE_CLIP_PATH);
+        if (node) {
+            if (rsvg_node_clip_path_get_units (node) == objectBoundingBox) {
+                lateclip = node;
+            } else {
+                rsvg_drawing_ctx_release_node (ctx, node);
+            }
+        }
 
-    draw->offset_x = bbx0;
-    draw->offset_y = bby0;
-    draw->width = bbx1 - bbx0;
-    draw->height = bby1 - bby0;
+        g_free (clip_path);
+    }
 
-    draw->state = NULL;
+    if (opacity == 0xFF
+        && !filter && !mask && !lateclip && (comp_op == CAIRO_OPERATOR_OVER)
+        && (enable_background == RSVG_ENABLE_BACKGROUND_ACCUMULATE))
+        return;
 
-    draw->defs = handle->priv->defs;
-    draw->dpi_x = handle->priv->dpi_x;
-    draw->dpi_y = handle->priv->dpi_y;
-    draw->vb.rect.width = data.em;
-    draw->vb.rect.height = data.ex;
-    draw->vb_stack = NULL;
-    draw->drawsub_stack = NULL;
-    draw->acquired_nodes = NULL;
-    draw->is_testing = handle->priv->is_testing;
+    surface = cairo_get_target (child_cr);
 
-    rsvg_drawing_ctx_state_push (draw);
-    state = rsvg_drawing_ctx_get_current_state (draw);
+    if (filter) {
+        RsvgNode *node;
+        cairo_surface_t *output;
 
-    state_affine = rsvg_state_get_affine (state);
+        output = ctx->surfaces_stack->data;
+        ctx->surfaces_stack = g_list_delete_link (ctx->surfaces_stack, ctx->surfaces_stack);
 
-    /* apply cairo transformation to our affine transform */
-    cairo_matrix_multiply (&state_affine, &affine, &state_affine);
+        node = rsvg_drawing_ctx_acquire_node_of_type (ctx, filter, RSVG_NODE_TYPE_FILTER);
+        if (node) {
+            needs_destroy = TRUE;
+            surface = rsvg_filter_render (node, output, ctx, "2103");
+            rsvg_drawing_ctx_release_node (ctx, node);
 
-    /* scale according to size set by size_func callback */
-    cairo_matrix_init_scale (&affine, data.width / data.em, data.height / data.ex);
-    cairo_matrix_multiply (&state_affine, &affine, &state_affine);
+            /* Don't destroy the output surface, it's owned by child_cr */
+        }
 
-    /* adjust transform so that the corner of the bounding box above is
-     * at (0,0) - we compensate for this in _set_rsvg_affine() in
-     * rsvg-cairo-render.c and a few other places */
-    state_affine.x0 -= draw->offset_x;
-    state_affine.y0 -= draw->offset_y;
+        g_free (filter);
+    }
 
-    rsvg_bbox_init (&draw->bbox, &state_affine);
-    rsvg_bbox_init (&draw->ink_bbox, &state_affine);
+    ctx->cr = (cairo_t *) ctx->cr_stack->data;
+    ctx->cr_stack = g_list_delete_link (ctx->cr_stack, ctx->cr_stack);
 
-    rsvg_state_set_affine (state, state_affine);
+    if (ctx->cr == ctx->initial_cr) {
+        rsvg_drawing_ctx_get_offset (ctx, &offset_x, &offset_y);
+    }
 
-#ifdef HAVE_PANGOFT2
-    draw->font_config_for_testing = NULL;
-    draw->font_map_for_testing = NULL;
-#endif
+    cairo_identity_matrix (ctx->cr);
+    cairo_set_source_surface (ctx->cr, surface, offset_x, offset_y);
 
-    return draw;
-}
+    if (lateclip) {
+        rsvg_cairo_clip (ctx, lateclip, &ctx->bbox);
+        rsvg_drawing_ctx_release_node (ctx, lateclip);
+    }
 
-void
-rsvg_drawing_ctx_free (RsvgDrawingCtx *ctx)
-{
-    g_assert (ctx->cr_stack == NULL);
-    g_assert (ctx->surfaces_stack == NULL);
+    cairo_set_operator (ctx->cr, comp_op);
 
-    g_assert(ctx->state);
-    g_assert(rsvg_state_parent(ctx->state) == NULL);
-    rsvg_state_free (ctx->state);
+    if (mask) {
+        RsvgNode *node;
 
-       g_slist_free_full (ctx->drawsub_stack, (GDestroyNotify) rsvg_node_unref);
+        node = rsvg_drawing_ctx_acquire_node_of_type (ctx, mask, RSVG_NODE_TYPE_MASK);
+        if (node) {
+            rsvg_cairo_generate_mask (ctx->cr, node, ctx);
+            rsvg_drawing_ctx_release_node (ctx, node);
+        }
 
-    g_warn_if_fail (ctx->acquired_nodes == NULL);
-    g_slist_free (ctx->acquired_nodes);
+        g_free (mask);
+    } else if (opacity != 0xFF)
+        cairo_paint_with_alpha (ctx->cr, (double) opacity / 255.0);
+    else
+        cairo_paint (ctx->cr);
 
-    g_assert (ctx->bb_stack == NULL);
-    g_assert (ctx->ink_bb_stack == NULL);
+    cairo_destroy (child_cr);
 
-#ifdef HAVE_PANGOFT2
-    if (ctx->font_config_for_testing) {
-        FcConfigDestroy (ctx->font_config_for_testing);
-        ctx->font_config_for_testing = NULL;
-    }
+    pop_bounding_box (ctx);
 
-    if (ctx->font_map_for_testing) {
-        g_object_unref (ctx->font_map_for_testing);
-        ctx->font_map_for_testing = NULL;
+    if (needs_destroy) {
+        cairo_surface_destroy (surface);
     }
-#endif
-
-    g_free (ctx);
 }
 
-RsvgState *
-rsvg_drawing_ctx_get_current_state (RsvgDrawingCtx *ctx)
+void
+rsvg_drawing_ctx_push_discrete_layer (RsvgDrawingCtx *ctx, gboolean clipping)
 {
-    return ctx->state;
+    if (!clipping) {
+        cairo_save (ctx->cr);
+        rsvg_drawing_ctx_push_render_stack (ctx);
+    }
 }
 
 void
-rsvg_drawing_ctx_set_current_state (RsvgDrawingCtx *ctx, RsvgState *state)
+rsvg_drawing_ctx_pop_discrete_layer (RsvgDrawingCtx *ctx, gboolean clipping)
 {
-    ctx->state = state;
+    if (!clipping) {
+        rsvg_drawing_ctx_pop_render_stack (ctx);
+        cairo_restore (ctx->cr);
+    }
 }
 
 /*
@@ -1234,3 +894,139 @@ rsvg_drawing_ctx_get_dpi (RsvgDrawingCtx *ctx, double *out_dpi_x, double *out_dp
     if (out_dpi_y)
         *out_dpi_y = ctx->dpi_y;
 }
+
+#ifdef HAVE_PANGOFT2
+static cairo_font_options_t *
+get_font_options_for_testing (void)
+{
+    cairo_font_options_t *options;
+
+    options = cairo_font_options_create ();
+    cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
+    cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_FULL);
+    cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON);
+
+    return options;
+}
+
+static void
+set_font_options_for_testing (PangoContext *context)
+{
+    cairo_font_options_t *font_options;
+
+    font_options = get_font_options_for_testing ();
+    pango_cairo_context_set_font_options (context, font_options);
+    cairo_font_options_destroy (font_options);
+}
+
+static void
+create_font_config_for_testing (RsvgDrawingCtx *ctx)
+{
+    const char *font_paths[] = {
+        SRCDIR "/tests/resources/Roboto-Regular.ttf",
+        SRCDIR "/tests/resources/Roboto-Italic.ttf",
+        SRCDIR "/tests/resources/Roboto-Bold.ttf",
+        SRCDIR "/tests/resources/Roboto-BoldItalic.ttf",
+    };
+
+    int i;
+
+    if (ctx->font_config_for_testing != NULL)
+        return;
+
+    ctx->font_config_for_testing = FcConfigCreate ();
+
+    for (i = 0; i < G_N_ELEMENTS(font_paths); i++) {
+        if (!FcConfigAppFontAddFile (ctx->font_config_for_testing, (const FcChar8 *) font_paths[i])) {
+            g_error ("Could not load font file \"%s\" for tests; aborting", font_paths[i]);
+        }
+    }
+}
+
+static PangoFontMap *
+get_font_map_for_testing (RsvgDrawingCtx *ctx)
+{
+    create_font_config_for_testing (ctx);
+
+    if (ctx->font_map_for_testing == NULL) {
+        ctx->font_map_for_testing = pango_cairo_font_map_new_for_font_type (CAIRO_FONT_TYPE_FT);
+        pango_fc_font_map_set_config (PANGO_FC_FONT_MAP (ctx->font_map_for_testing),
+                                      ctx->font_config_for_testing);
+    }
+
+    return ctx->font_map_for_testing;
+}
+#endif
+
+PangoContext *
+rsvg_drawing_ctx_get_pango_context (RsvgDrawingCtx * ctx)
+{
+    PangoFontMap *fontmap;
+    PangoContext *context;
+    double dpi_y;
+
+#ifdef HAVE_PANGOFT2
+    if (ctx->is_testing) {
+        fontmap = get_font_map_for_testing (ctx);
+    } else {
+#endif
+        fontmap = pango_cairo_font_map_get_default ();
+#ifdef HAVE_PANGOFT2
+    }
+#endif
+
+    context = pango_font_map_create_context (fontmap);
+    pango_cairo_update_context (ctx->cr, context);
+
+    rsvg_drawing_ctx_get_dpi (ctx, NULL, &dpi_y);
+    pango_cairo_context_set_resolution (context, dpi_y);
+
+#ifdef HAVE_PANGOFT2
+    if (ctx->is_testing) {
+        set_font_options_for_testing (context);
+    }
+#endif
+
+    return context;
+}
+
+cairo_surface_t *
+rsvg_drawing_ctx_get_surface_of_node (RsvgDrawingCtx *ctx,
+                                      RsvgNode *drawable,
+                                      double width,
+                                      double height)
+{
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    cairo_t *save_cr = ctx->cr;
+    cairo_t *save_initial_cr = ctx->initial_cr;
+    double save_x = ctx->offset_x;
+    double save_y = ctx->offset_y;
+    double save_w = ctx->width;
+    double save_h = ctx->height;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+    if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
+        cairo_surface_destroy (surface);
+        return NULL;
+    }
+
+    ctx->cr = cairo_create (surface);
+    ctx->initial_cr = ctx->cr;
+    ctx->offset_x = 0;
+    ctx->offset_y = 0;
+    ctx->width = width;
+    ctx->height = height;
+
+    rsvg_drawing_ctx_draw_node_from_stack (ctx, drawable, 0, FALSE);
+
+    cairo_destroy (ctx->cr);
+    ctx->cr = save_cr;
+    ctx->initial_cr = save_initial_cr;
+    ctx->offset_x = save_x;
+    ctx->offset_y = save_y;
+    ctx->width = save_w;
+    ctx->height = save_h;
+
+    return surface;
+}
diff --git a/librsvg/rsvg-drawing-ctx.h b/librsvg/rsvg-drawing-ctx.h
index 843d2c31..c9735df6 100644
--- a/librsvg/rsvg-drawing-ctx.h
+++ b/librsvg/rsvg-drawing-ctx.h
@@ -61,15 +61,18 @@ struct RsvgDrawingCtx {
 };
 
 G_GNUC_INTERNAL
-void rsvg_pop_discrete_layer    (RsvgDrawingCtx *ctx, gboolean clipping);
+RsvgDrawingCtx *rsvg_drawing_ctx_new (cairo_t *cr, RsvgHandle *handle);
+
 G_GNUC_INTERNAL
-void rsvg_push_discrete_layer   (RsvgDrawingCtx *ctx, gboolean clipping);
+void rsvg_drawing_ctx_free (RsvgDrawingCtx *draw_ctx);
 
 G_GNUC_INTERNAL
-RsvgDrawingCtx *rsvg_drawing_ctx_new (cairo_t *cr, RsvgHandle *handle);
+cairo_t *rsvg_drawing_ctx_get_cairo_context (RsvgDrawingCtx *ctx);
+G_GNUC_INTERNAL
+void rsvg_drawing_ctx_set_cairo_context (RsvgDrawingCtx *ctx, cairo_t *cr);
 
 G_GNUC_INTERNAL
-void rsvg_drawing_ctx_free (RsvgDrawingCtx *draw_ctx);
+gboolean rsvg_drawing_ctx_is_cairo_context_nested (RsvgDrawingCtx *ctx, cairo_t *cr);
 
 G_GNUC_INTERNAL
 RsvgState *rsvg_drawing_ctx_get_current_state   (RsvgDrawingCtx * ctx);
@@ -117,24 +120,18 @@ G_GNUC_INTERNAL
 void rsvg_drawing_ctx_get_dpi (RsvgDrawingCtx *ctx, double *out_dpi_x, double *out_dpi_y);
 
 G_GNUC_INTERNAL
-PangoContext    *rsvg_cairo_get_pango_context    (RsvgDrawingCtx *ctx);
-
-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
-gboolean rsvg_cairo_is_cairo_context_nested (RsvgDrawingCtx *ctx, cairo_t *cr);
+PangoContext *rsvg_drawing_ctx_get_pango_context (RsvgDrawingCtx *ctx);
 
 G_GNUC_INTERNAL
-void         rsvg_cairo_push_discrete_layer        (RsvgDrawingCtx *ctx, gboolean clipping);
+void         rsvg_drawing_ctx_push_discrete_layer (RsvgDrawingCtx *ctx, gboolean clipping);
 G_GNUC_INTERNAL
-void         rsvg_cairo_pop_discrete_layer      (RsvgDrawingCtx *ctx, gboolean clipping);
+void         rsvg_drawing_ctx_pop_discrete_layer (RsvgDrawingCtx *ctx, gboolean clipping);
 
 G_GNUC_INTERNAL
-cairo_surface_t*rsvg_cairo_get_surface_of_node  (RsvgDrawingCtx *ctx, RsvgNode *drawable, 
-                                                 double width, double height);
+cairo_surface_t *rsvg_drawing_ctx_get_surface_of_node (RsvgDrawingCtx *ctx,
+                                                       RsvgNode *drawable,
+                                                       double width,
+                                                       double height);
 
 G_END_DECLS
 
diff --git a/librsvg/rsvg-file-util.c b/librsvg/rsvg-file-util.c
index c8de90c6..47eeec58 100644
--- a/librsvg/rsvg-file-util.c
+++ b/librsvg/rsvg-file-util.c
@@ -298,3 +298,207 @@ rsvg_pixbuf_from_file_at_max_size (const gchar * file_name,
 
     return rsvg_pixbuf_from_file_with_size_data (file_name, &data, error);
 }
+
+cairo_surface_t *
+rsvg_cairo_surface_from_pixbuf (const GdkPixbuf *pixbuf)
+{
+    gint width, height, gdk_rowstride, n_channels, cairo_rowstride;
+    guchar *gdk_pixels, *cairo_pixels;
+    cairo_format_t format;
+    cairo_surface_t *surface;
+    int j;
+
+    if (pixbuf == NULL)
+        return NULL;
+
+    width = gdk_pixbuf_get_width (pixbuf);
+    height = gdk_pixbuf_get_height (pixbuf);
+    gdk_pixels = gdk_pixbuf_get_pixels (pixbuf);
+    gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+    n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+
+    if (n_channels == 3)
+        format = CAIRO_FORMAT_RGB24;
+    else
+        format = CAIRO_FORMAT_ARGB32;
+
+    surface = cairo_image_surface_create (format, width, height);
+    if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
+        cairo_surface_destroy (surface);
+        return NULL;
+    }
+
+    cairo_pixels = cairo_image_surface_get_data (surface);
+    cairo_rowstride = cairo_image_surface_get_stride (surface);
+
+    if (n_channels == 3) {
+        for (j = height; j; j--) {
+            guchar *p = gdk_pixels;
+            guchar *q = cairo_pixels;
+            guchar *end = p + 3 * width;
+
+            while (p < end) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+                q[0] = p[2];
+                q[1] = p[1];
+                q[2] = p[0];
+#else
+                q[1] = p[0];
+                q[2] = p[1];
+                q[3] = p[2];
+#endif
+                p += 3;
+                q += 4;
+            }
+
+            gdk_pixels += gdk_rowstride;
+            cairo_pixels += cairo_rowstride;
+        }
+    } else {
+        for (j = height; j; j--) {
+            guchar *p = gdk_pixels;
+            guchar *q = cairo_pixels;
+            guchar *end = p + 4 * width;
+            guint t1, t2, t3;
+
+#define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END
+
+            while (p < end) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+                MULT (q[0], p[2], p[3], t1);
+                MULT (q[1], p[1], p[3], t2);
+                MULT (q[2], p[0], p[3], t3);
+                q[3] = p[3];
+#else
+                q[0] = p[3];
+                MULT (q[1], p[0], p[3], t1);
+                MULT (q[2], p[1], p[3], t2);
+                MULT (q[3], p[2], p[3], t3);
+#endif
+
+                p += 4;
+                q += 4;
+            }
+
+#undef MULT
+            gdk_pixels += gdk_rowstride;
+            cairo_pixels += cairo_rowstride;
+        }
+    }
+
+    cairo_surface_mark_dirty (surface);
+    return surface;
+}
+
+/* Copied from gtk+/gdk/gdkpixbuf-drawable.c, LGPL 2+.
+ *
+ * Copyright (C) 1999 Michael Zucchi
+ *
+ * Authors: Michael Zucchi <zucchi zedzone mmc com au>
+ *          Cody Russell <bratsche dfw net>
+ *          Federico Mena-Quintero <federico gimp org>
+ */
+
+static void
+convert_alpha (guchar *dest_data,
+               int     dest_stride,
+               guchar *src_data,
+               int     src_stride,
+               int     src_x,
+               int     src_y,
+               int     width,
+               int     height)
+{
+    int x, y;
+
+    src_data += src_stride * src_y + src_x * 4;
+
+    for (y = 0; y < height; y++) {
+        guint32 *src = (guint32 *) src_data;
+
+        for (x = 0; x < width; x++) {
+          guint alpha = src[x] >> 24;
+
+          if (alpha == 0) {
+              dest_data[x * 4 + 0] = 0;
+              dest_data[x * 4 + 1] = 0;
+              dest_data[x * 4 + 2] = 0;
+          } else {
+              dest_data[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
+              dest_data[x * 4 + 1] = (((src[x] & 0x00ff00) >>  8) * 255 + alpha / 2) / alpha;
+              dest_data[x * 4 + 2] = (((src[x] & 0x0000ff) >>  0) * 255 + alpha / 2) / alpha;
+          }
+          dest_data[x * 4 + 3] = alpha;
+      }
+
+      src_data += src_stride;
+      dest_data += dest_stride;
+    }
+}
+
+static void
+convert_no_alpha (guchar *dest_data,
+                  int     dest_stride,
+                  guchar *src_data,
+                  int     src_stride,
+                  int     src_x,
+                  int     src_y,
+                  int     width,
+                  int     height)
+{
+    int x, y;
+
+    src_data += src_stride * src_y + src_x * 4;
+
+    for (y = 0; y < height; y++) {
+        guint32 *src = (guint32 *) src_data;
+
+        for (x = 0; x < width; x++) {
+            dest_data[x * 3 + 0] = src[x] >> 16;
+            dest_data[x * 3 + 1] = src[x] >>  8;
+            dest_data[x * 3 + 2] = src[x];
+        }
+
+        src_data += src_stride;
+        dest_data += dest_stride;
+    }
+}
+
+GdkPixbuf *
+rsvg_cairo_surface_to_pixbuf (cairo_surface_t *surface)
+{
+    cairo_content_t content;
+    GdkPixbuf *dest;
+    int width, height;
+
+    /* General sanity checks */
+    g_assert (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE);
+
+    width = cairo_image_surface_get_width (surface);
+    height = cairo_image_surface_get_height (surface);
+    if (width == 0 || height == 0)
+        return NULL;
+
+    content = cairo_surface_get_content (surface) | CAIRO_CONTENT_COLOR;
+    dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+                          !!(content & CAIRO_CONTENT_ALPHA),
+                          8,
+                          width, height);
+
+    if (gdk_pixbuf_get_has_alpha (dest))
+      convert_alpha (gdk_pixbuf_get_pixels (dest),
+                    gdk_pixbuf_get_rowstride (dest),
+                    cairo_image_surface_get_data (surface),
+                    cairo_image_surface_get_stride (surface),
+                    0, 0,
+                    width, height);
+    else
+      convert_no_alpha (gdk_pixbuf_get_pixels (dest),
+                        gdk_pixbuf_get_rowstride (dest),
+                        cairo_image_surface_get_data (surface),
+                        cairo_image_surface_get_stride (surface),
+                        0, 0,
+                        width, height);
+
+    return dest;
+}
diff --git a/rsvg_internals/src/drawing_ctx.rs b/rsvg_internals/src/drawing_ctx.rs
index f0478ac8..f266a0dd 100644
--- a/rsvg_internals/src/drawing_ctx.rs
+++ b/rsvg_internals/src/drawing_ctx.rs
@@ -20,6 +20,20 @@ extern "C" {
     fn rsvg_drawing_ctx_get_current_state(draw_ctx: *const RsvgDrawingCtx) -> *mut RsvgState;
     fn rsvg_drawing_ctx_set_current_state(draw_ctx: *mut RsvgDrawingCtx, state: *mut RsvgState);
 
+    fn rsvg_drawing_ctx_get_cairo_context(
+        draw_ctx: *const RsvgDrawingCtx,
+    ) -> *mut cairo_sys::cairo_t;
+
+    fn rsvg_drawing_ctx_set_cairo_context(
+        draw_ctx: *const RsvgDrawingCtx,
+        cr: *const cairo_sys::cairo_t,
+    );
+
+    fn rsvg_drawing_ctx_is_cairo_context_nested(
+        draw_ctx: *const RsvgDrawingCtx,
+        cr: *const cairo_sys::cairo_t,
+    ) -> glib_sys::gboolean;
+
     fn rsvg_drawing_ctx_get_dpi(
         draw_ctx: *const RsvgDrawingCtx,
         out_dpi_x: *mut f64,
@@ -65,20 +79,42 @@ extern "C" {
         clipping: glib_sys::gboolean,
     );
 
-    fn rsvg_push_discrete_layer(draw_ctx: *const RsvgDrawingCtx, clipping: glib_sys::gboolean);
-    fn rsvg_pop_discrete_layer(draw_ctx: *const RsvgDrawingCtx, clipping: glib_sys::gboolean);
-
-    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_cairo_is_cairo_context_nested(
+    fn rsvg_drawing_ctx_get_pango_context(
         draw_ctx: *const RsvgDrawingCtx,
-        cr: *const cairo_sys::cairo_t,
-    ) -> glib_sys::gboolean;
+    ) -> *mut pango_sys::PangoContext;
 
-    fn rsvg_cairo_get_pango_context(
+    fn rsvg_drawing_ctx_push_discrete_layer(
         draw_ctx: *const RsvgDrawingCtx,
-    ) -> *mut pango_sys::PangoContext;
+        clipping: glib_sys::gboolean
+    );
+    fn rsvg_drawing_ctx_pop_discrete_layer(
+        draw_ctx: *const RsvgDrawingCtx,
+        clipping: glib_sys::gboolean
+    );
+}
+
+pub fn get_cairo_context(draw_ctx: *const RsvgDrawingCtx) -> cairo::Context {
+    unsafe {
+        let raw_cr = rsvg_drawing_ctx_get_cairo_context(draw_ctx);
+
+        cairo::Context::from_glib_none(raw_cr)
+    }
+}
+
+pub fn set_cairo_context(draw_ctx: *const RsvgDrawingCtx, cr: &cairo::Context) {
+    unsafe {
+        let raw_cr = cr.to_glib_none().0;
+
+        rsvg_drawing_ctx_set_cairo_context(draw_ctx, raw_cr);
+    }
+}
+
+pub fn is_cairo_context_nested(draw_ctx: *const RsvgDrawingCtx, cr: &cairo::Context) -> bool {
+    unsafe {
+        let raw_cr = cr.to_glib_none().0;
+
+        from_glib(rsvg_drawing_ctx_is_cairo_context_nested(draw_ctx, raw_cr))
+    }
 }
 
 pub fn get_dpi(draw_ctx: *const RsvgDrawingCtx) -> (f64, f64) {
@@ -225,37 +261,13 @@ pub fn state_reinherit_top(draw_ctx: *const RsvgDrawingCtx, state: &State, domin
 
 pub fn push_discrete_layer(draw_ctx: *const RsvgDrawingCtx, clipping: bool) {
     unsafe {
-        rsvg_push_discrete_layer(draw_ctx, clipping.to_glib());
+        rsvg_drawing_ctx_push_discrete_layer(draw_ctx, clipping.to_glib());
     }
 }
 
 pub fn pop_discrete_layer(draw_ctx: *const RsvgDrawingCtx, clipping: bool) {
     unsafe {
-        rsvg_pop_discrete_layer(draw_ctx, clipping.to_glib());
-    }
-}
-
-pub fn get_cairo_context(draw_ctx: *const RsvgDrawingCtx) -> cairo::Context {
-    unsafe {
-        let raw_cr = rsvg_cairo_get_cairo_context(draw_ctx);
-
-        cairo::Context::from_glib_none(raw_cr)
-    }
-}
-
-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 is_cairo_context_nested(draw_ctx: *const RsvgDrawingCtx, cr: &cairo::Context) -> bool {
-    unsafe {
-        let raw_cr = cr.to_glib_none().0;
-
-        from_glib(rsvg_cairo_is_cairo_context_nested(draw_ctx, raw_cr))
+        rsvg_drawing_ctx_pop_discrete_layer(draw_ctx, clipping.to_glib());
     }
 }
 
@@ -271,7 +283,7 @@ pub fn get_offset(draw_ctx: *const RsvgDrawingCtx) -> (f64, f64) {
 }
 
 pub fn get_pango_context(draw_ctx: *const RsvgDrawingCtx) -> pango::Context {
-    unsafe { from_glib_full(rsvg_cairo_get_pango_context(draw_ctx)) }
+    unsafe { from_glib_full(rsvg_drawing_ctx_get_pango_context(draw_ctx)) }
 }
 
 pub fn insert_bbox(draw_ctx: *const RsvgDrawingCtx, bbox: &RsvgBbox) {


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