[librsvg] Make filters render to a surface



commit 1b3d2ea55a1be67e0548bf76903ff905888e2e18
Author: Christian Persch <chpe gnome org>
Date:   Sat Nov 26 23:33:34 2011 +0100

    Make filters render to a surface
    
    ... instead of into a GdkPixbuf.

 rsvg-cairo-draw.c   |   70 +++++++++++++++++---------------------------------
 rsvg-cairo-render.c |    2 +-
 rsvg-cairo-render.h |    2 +-
 rsvg-filter.c       |   60 +++++++++++++++++++++++++++++++++++--------
 rsvg-filter.h       |    7 +++-
 5 files changed, 80 insertions(+), 61 deletions(-)
---
diff --git a/rsvg-cairo-draw.c b/rsvg-cairo-draw.c
index fe93c36..3054a9e 100644
--- a/rsvg-cairo-draw.c
+++ b/rsvg-cairo-draw.c
@@ -790,39 +790,26 @@ rsvg_cairo_push_render_stack (RsvgDrawingCtx * ctx)
         && !state->filter && !state->mask && !lateclip && (state->comp_op == CAIRO_OPERATOR_OVER)
         && (state->enable_background == RSVG_ENABLE_BACKGROUND_ACCUMULATE))
         return;
-    if (!state->filter)
+
+    if (!state->filter) {
         surface = cairo_surface_create_similar (cairo_get_target (render->cr),
                                                 CAIRO_CONTENT_COLOR_ALPHA,
                                                 render->width, render->height);
-    else {
-        guchar *pixels;
-        int rowstride = render->width * 4;
-        GdkPixbuf *pixbuf;
-
-        pixels = g_try_malloc0 (render->width * render->height * 4);
-        if (pixels == NULL)
-            return; /* not really correct, but the best we can do here */
-
-        /* The pixbuf takes ownership of @pixels */
-        pixbuf = gdk_pixbuf_new_from_data (pixels,
-                                           GDK_COLORSPACE_RGB,
-                                           TRUE,
-                                           8,
-                                           render->width,
-                                           render->height,
-                                           rowstride,
-                                           (GdkPixbufDestroyNotify) rsvg_pixmap_destroy,
-                                           NULL);
-        render->pixbuf_stack = g_list_prepend (render->pixbuf_stack, pixbuf);
-
-        surface = cairo_image_surface_create_for_data (pixels,
-                                                       CAIRO_FORMAT_ARGB32,
-                                                       render->width, render->height, rowstride);
-        /* Also keep a reference to the pixbuf which owns the pixels */
-        cairo_surface_set_user_data (surface, &surface_pixel_data_key,
-                                     g_object_ref (pixbuf),
-                                     (cairo_destroy_func_t) g_object_unref);
+    } else {
+        surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 
+                                              render->width, render->height);
+
+        /* The surface reference is owned by the child_cr created below and put on the cr_stack! */
+        render->surfaces_stack = g_list_prepend (render->surfaces_stack, surface);
+    }
+
+#if 0
+    if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
+        cairo_surface_destroy (surface);
+        return;
     }
+#endif
+
     child_cr = cairo_create (surface);
     cairo_surface_destroy (surface);
 
@@ -862,26 +849,17 @@ rsvg_cairo_pop_render_stack (RsvgDrawingCtx * ctx)
         return;
 
     if (state->filter) {
-        GdkPixbuf *pixbuf = render->pixbuf_stack->data;
-        GdkPixbuf *output;
-
-        render->pixbuf_stack = g_list_remove (render->pixbuf_stack, pixbuf);
-
+        cairo_surface_t *output;
 
-        output = rsvg_filter_render (state->filter, pixbuf, ctx, &render->bbox, "2103");
-        g_object_unref (pixbuf);
+        output = render->surfaces_stack->data;
+        render->surfaces_stack = g_list_delete_link (render->surfaces_stack, render->surfaces_stack);
 
-        surface = cairo_image_surface_create_for_data (gdk_pixbuf_get_pixels (output),
-                                                       CAIRO_FORMAT_ARGB32,
-                                                       gdk_pixbuf_get_width (output),
-                                                       gdk_pixbuf_get_height (output),
-                                                       gdk_pixbuf_get_rowstride (output));
-        cairo_surface_set_user_data (surface, &surface_pixel_data_key,
-                                     output,
-                                     (cairo_destroy_func_t) g_object_unref);
+        surface = rsvg_filter_render (state->filter, output, ctx, &render->bbox, "2103");
 
-    } else
+        /* Don't destroy the output surface, it's owned by child_cr */
+    } else {
         surface = cairo_get_target (child_cr);
+    }
 
     render->cr = (cairo_t *) render->cr_stack->data;
     render->cr_stack = g_list_delete_link (render->cr_stack, render->cr_stack);
@@ -903,8 +881,8 @@ rsvg_cairo_pop_render_stack (RsvgDrawingCtx * ctx)
         cairo_paint_with_alpha (render->cr, (double) state->opacity / 255.0);
     else
         cairo_paint (render->cr);
-    cairo_destroy (child_cr);
 
+    cairo_destroy (child_cr);
 
     rsvg_bbox_insert ((RsvgBbox *) render->bb_stack->data, &render->bbox);
 
diff --git a/rsvg-cairo-render.c b/rsvg-cairo-render.c
index afbfb5f..db80c48 100644
--- a/rsvg-cairo-render.c
+++ b/rsvg-cairo-render.c
@@ -73,7 +73,7 @@ rsvg_cairo_render_new (cairo_t * cr, double width, double height)
     cairo_render->cr = cr;
     cairo_render->cr_stack = NULL;
     cairo_render->bb_stack = NULL;
-    cairo_render->pixbuf_stack = NULL;
+    cairo_render->surfaces_stack = NULL;
 
     return cairo_render;
 }
diff --git a/rsvg-cairo-render.h b/rsvg-cairo-render.h
index e26b7d3..66cce63 100644
--- a/rsvg-cairo-render.h
+++ b/rsvg-cairo-render.h
@@ -47,7 +47,7 @@ struct _RsvgCairoRender {
 
     RsvgBbox bbox;
     GList *bb_stack;
-    GList *pixbuf_stack;
+    GList *surfaces_stack;
 };
 
 #define RSVG_CAIRO_RENDER(render) (_RSVG_RENDER_CIC ((render), RSVG_RENDER_TYPE_CAIRO, RsvgCairoRender))
diff --git a/rsvg-filter.c b/rsvg-filter.c
index 27f962e..dba9796 100644
--- a/rsvg-filter.c
+++ b/rsvg-filter.c
@@ -31,6 +31,7 @@
 #include "rsvg-image.h"
 #include "rsvg-css.h"
 #include "rsvg-cairo-render.h"
+
 #include <string.h>
 
 #include <math.h>
@@ -454,37 +455,63 @@ rsvg_filter_context_free (RsvgFilterContext * ctx)
     g_free (ctx);
 }
 
+static void
+unref_surface (guchar *pixels,
+               gpointer user_data)
+{
+  cairo_surface_t *surface = user_data;
+
+  cairo_surface_destroy (surface);
+}
+
 /**
  * rsvg_filter_render: Create a new pixbuf applied the filter.
  * @self: a pointer to the filter to use
- * @source: a pointer to the source pixbuf
+ * @source: the a #cairo_surface_t of type %CAIRO_SURFACE_TYPE_IMAGE
  * @context: the context
  *
  * This function will create a context for itself, set up the coordinate systems
  * execute all its little primatives and then clean up its own mess
+ * 
+ * Returns: (transfer full): a new #cairo_surface_t
  **/
-GdkPixbuf *
-rsvg_filter_render (RsvgFilter * self, GdkPixbuf * source,
-                    RsvgDrawingCtx * context, RsvgBbox * bounds, char *channelmap)
+cairo_surface_t *
+rsvg_filter_render (RsvgFilter *self,
+                    cairo_surface_t *source,
+                    RsvgDrawingCtx *context, 
+                    RsvgBbox *bounds, 
+                    char *channelmap)
 {
+    static const cairo_user_data_key_t surface_pixel_data_key;
     RsvgFilterContext *ctx;
     RsvgFilterPrimitive *current;
     guint i;
-    GdkPixbuf *out;
-
+    GdkPixbuf *in, *out;
+    cairo_surface_t *output;
+
+    g_return_val_if_fail (source != NULL, NULL);
+    g_return_val_if_fail (cairo_surface_get_type (source) == CAIRO_SURFACE_TYPE_IMAGE, NULL);
+
+    in = gdk_pixbuf_new_from_data (cairo_image_surface_get_data (source),
+                                   GDK_COLORSPACE_RGB,
+                                   cairo_image_surface_get_format (source) == CAIRO_FORMAT_ARGB32,
+                                   8,
+                                   cairo_image_surface_get_width (source),
+                                   cairo_image_surface_get_height (source),
+                                   cairo_image_surface_get_stride (source),
+                                   (GdkPixbufDestroyNotify) unref_surface,
+                                   cairo_surface_reference (source));
 
     ctx = g_new (RsvgFilterContext, 1);
     ctx->filter = self;
-    ctx->source = source;
+    ctx->source = in;
     ctx->bg = NULL;
     ctx->results = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, rsvg_filter_free_pair);
     ctx->ctx = context;
 
-    g_object_ref (source);
-
     rsvg_filter_fix_coordinate_system (ctx, rsvg_current_state (context), bounds);
 
-    ctx->lastresult.result = source;
+    ctx->lastresult.result = in;
     ctx->lastresult.Rused = 1;
     ctx->lastresult.Gused = 1;
     ctx->lastresult.Bused = 1;
@@ -506,7 +533,18 @@ rsvg_filter_render (RsvgFilter * self, GdkPixbuf * source,
 
     rsvg_filter_context_free (ctx);
 
-    return out;
+    output = cairo_image_surface_create_for_data (gdk_pixbuf_get_pixels (out),
+                                                  gdk_pixbuf_get_has_alpha (out) ? CAIRO_FORMAT_ARGB32 
+                                                                                 : CAIRO_FORMAT_RGB24,
+                                                  gdk_pixbuf_get_width (out),
+                                                  gdk_pixbuf_get_height (out),
+                                                  gdk_pixbuf_get_rowstride (out));
+    /* Also keep a reference to the pixbuf which owns the pixels */
+    cairo_surface_set_user_data (output, &surface_pixel_data_key,
+                                 out /* adopt */,
+                                 (cairo_destroy_func_t) g_object_unref);
+
+    return output;
 }
 
 /**
diff --git a/rsvg-filter.h b/rsvg-filter.h
index 6d2d848..75e9bb0 100644
--- a/rsvg-filter.h
+++ b/rsvg-filter.h
@@ -41,8 +41,11 @@ struct _RsvgFilter {
     RsvgFilterUnits primitiveunits;
 };
 
-GdkPixbuf   *rsvg_filter_render	    (RsvgFilter * self, GdkPixbuf * source,
-                                     RsvgDrawingCtx * context, RsvgBbox * dimentions, char *channelmap);
+cairo_surface_t *rsvg_filter_render (RsvgFilter *self,
+                                     cairo_surface_t *source,
+                                     RsvgDrawingCtx *context, 
+                                     RsvgBbox *dimentions, 
+                                     char *channelmap);
 
 RsvgNode    *rsvg_new_filter	    (void);
 RsvgFilter  *rsvg_filter_parse	    (const RsvgDefs * defs, const char *str);



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