Re: pixbuf<->cairo_surface_t conversion



On Thu, Sep 2, 2010 at 7:24 PM, Havoc Pennington <hp pobox com> wrote:

> I was thinking about the problem of wasting memory, writing to one
> internal representation and not changing the other, etc. How about
> this: we only keep one representation around. If you get_pixels we
> drop the surface, if you get_cairo_surface we drop the old-style
> pixels.
>
> We deprecate get_pixels() which is the only call that can force the
> old-style representation to be created. If you do use get_pixels(),
> what's going to happen is that you do your pixel editing, and on the
> next paint gdk_pixbuf_get_cairo_surface() will force conversion back
> to cairo representation.
>

Here is an (untested) patch to implement this.
From 08eea58e9fb0d1b28392bca1d6ac320d3264bbb2 Mon Sep 17 00:00:00 2001
From: Matthias Clasen <mclasen redhat com>
Date: Fri, 3 Sep 2010 00:55:43 -0400
Subject: [PATCH] cairo_surface_t <-> GdkPixbuf conversion

---
 configure.ac                    |    2 +-
 gdk-pixbuf/gdk-pixbuf-cairo.h   |   39 +++++
 gdk-pixbuf/gdk-pixbuf-private.h |    4 +
 gdk-pixbuf/gdk-pixbuf.c         |  293 ++++++++++++++++++++++++++++++++++++++-
 gdk-pixbuf/gdk-pixbuf.h         |    1 +
 5 files changed, 336 insertions(+), 3 deletions(-)
 create mode 100644 gdk-pixbuf/gdk-pixbuf-cairo.h

diff --git a/configure.ac b/configure.ac
index 5bf8248..498432d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -903,7 +903,7 @@ if test $cross_compiling = yes; then
   fi
 fi
 
-GDK_PIXBUF_PACKAGES="gmodule-no-export-2.0 gobject-2.0 gio-2.0"
+GDK_PIXBUF_PACKAGES="gmodule-no-export-2.0 gobject-2.0 gio-2.0 cairo"
 GDK_PIXBUF_EXTRA_LIBS="$STATIC_LIB_DEPS $MATH_LIB $MEDIA_LIB"
 GDK_PIXBUF_EXTRA_CFLAGS=
 GDK_PIXBUF_DEP_LIBS="`$PKG_CONFIG --libs $GDK_PIXBUF_PACKAGES` $GDK_PIXBUF_EXTRA_LIBS"
diff --git a/gdk-pixbuf/gdk-pixbuf-cairo.h b/gdk-pixbuf/gdk-pixbuf-cairo.h
new file mode 100644
index 0000000..1e4b048
--- /dev/null
+++ b/gdk-pixbuf/gdk-pixbuf-cairo.h
@@ -0,0 +1,39 @@
+/* GdkPixbuf library - GdkPixbuf data structure
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * Author: Matthias Clasen <mclasen redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if defined(GDK_PIXBUF_DISABLE_SINGLE_INCLUDES) && !defined (GDK_PIXBUF_H_INSIDE) && !defined (GDK_PIXBUF_COMPILATION)
+#error "Only <gdk-pixbuf/gdk-pixbuf.h> can be included directly."
+#endif
+
+#ifndef GDK_PIXBUF_CAIRO_H
+#define GDK_PIXBUF_CAIRO_H
+
+#include <cairo/cairo.h>
+
+G_BEGIN_DECLS
+
+cairo_surface_t *gdk_pixbuf_get_cairo_surface      (const GdkPixbuf *pixbuf);
+GdkPixbuf       *gdk_pixbuf_new_from_cairo_surface (cairo_surface_t *surface);
+
+G_END_DECLS
+
+#endif  /* GDK_PIXBUF_CAIRO_H */
diff --git a/gdk-pixbuf/gdk-pixbuf-private.h b/gdk-pixbuf/gdk-pixbuf-private.h
index c060bd7..2ff22f2 100644
--- a/gdk-pixbuf/gdk-pixbuf-private.h
+++ b/gdk-pixbuf/gdk-pixbuf-private.h
@@ -31,6 +31,8 @@
 
 #include <glib-object.h>
 
+#include <cairo/cairo.h>
+
 #include "gdk-pixbuf-core.h"
 #include "gdk-pixbuf-io.h"
 #include "gdk-pixbuf-i18n.h"
@@ -73,6 +75,8 @@ struct _GdkPixbuf {
 
 	/* Do we have an alpha channel? */
 	guint has_alpha : 1;
+
+        cairo_surface_t *surface;
 };
 
 struct _GdkPixbufClass {
diff --git a/gdk-pixbuf/gdk-pixbuf.c b/gdk-pixbuf/gdk-pixbuf.c
index 824e290..5ea3e04 100644
--- a/gdk-pixbuf/gdk-pixbuf.c
+++ b/gdk-pixbuf/gdk-pixbuf.c
@@ -29,6 +29,8 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <cairo/cairo.h>
+
 #define GDK_PIXBUF_C_COMPILATION
 #include "gdk-pixbuf-private.h"
 #include "gdk-pixbuf-features.h"
@@ -178,10 +180,12 @@ static void
 gdk_pixbuf_finalize (GObject *object)
 {
         GdkPixbuf *pixbuf = GDK_PIXBUF (object);
-        
+
         if (pixbuf->destroy_fn)
                 (* pixbuf->destroy_fn) (pixbuf->pixels, pixbuf->destroy_fn_data);
-        
+        if (pixbuf->surface)
+                cairo_surface_destroy (pixbuf->surface);
+ 
         G_OBJECT_CLASS (gdk_pixbuf_parent_class)->finalize (object);
 }
 
@@ -441,6 +445,187 @@ gdk_pixbuf_get_bits_per_sample (const GdkPixbuf *pixbuf)
 	return pixbuf->bits_per_sample;
 }
 
+static void
+convert_surface_to_pixels (GdkPixbuf *pixbuf)
+{
+  gint width = pixbuf->width;
+  gint height = pixbuf->height;
+  gint n_channels = pixbuf->n_channels;
+  gint gdk_rowstride = pixbuf->rowstride;
+  guchar *gdk_pixels;
+  guchar *cairo_pixels;
+  gint cairo_rowstride;
+  cairo_format_t format;
+  gint j;
+
+  gdk_pixels = g_malloc (height * gdk_rowstride);
+
+  cairo_pixels = cairo_image_surface_get_data (pixbuf->surface);
+  cairo_rowstride = cairo_image_surface_get_stride (pixbuf->surface);
+  format = cairo_image_surface_get_format (pixbuf->surface);
+
+  if (format == CAIRO_FORMAT_ARGB32)
+    {
+      for (j = height; j; j--)
+        {
+          guchar *p = gdk_pixels;
+          guchar *q = cairo_pixels;
+
+          guchar *end = p + 4 * width;
+
+          while (p < end)
+            {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+              p[0] = q[2] / q[3] * 0xff;
+              p[1] = q[1] / q[3] * 0xff;
+              p[2] = q[0] / q[3] * 0xff;
+              p[3] = q[3];
+#else
+              p[0] = q[1] / q[0] * 0xff;
+              p[1] = q[2] / q[0] * 0xff;
+              p[2] = q[3] / q[0] * 0xff;
+              p[3] = q[0];
+#endif
+              p += 4;
+              q += 4;
+            }
+
+          gdk_pixels += gdk_rowstride;
+          cairo_pixels += cairo_rowstride;
+        }
+    }
+  else if (format == CAIRO_FORMAT_RGB24)
+    {
+      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
+              p[0] = q[2];
+              p[1] = q[1];
+              p[2] = q[0];
+#else
+              p[0] = q[1];
+              p[1] = q[2];
+              p[2] = q[3];
+#endif
+              p += 3;
+              q += 4;
+            }
+
+          gdk_pixels += gdk_rowstride;
+          cairo_pixels += cairo_rowstride;
+        }
+    }
+
+  cairo_surface_destroy (pixbuf->surface);
+  pixbuf->surface = NULL;
+
+  pixbuf->pixels = gdk_pixels;
+  pixbuf->destroy_fn = g_free;
+  pixbuf->destroy_fn_data = NULL;
+}
+
+static void
+convert_pixels_to_surface (GdkPixbuf *pixbuf)
+{
+  gint width = pixbuf->width;
+  gint height = pixbuf->height;
+  guchar *gdk_pixels = pixbuf->pixels;
+  int gdk_rowstride = pixbuf->rowstride;
+  int n_channels = pixbuf->n_channels;
+  int cairo_stride;
+  guchar *cairo_pixels;
+  cairo_format_t format;
+  cairo_surface_t *surface;
+  static const cairo_user_data_key_t key;
+  int j;
+
+  if (n_channels == 3)
+    format = CAIRO_FORMAT_RGB24;
+  else
+    format = CAIRO_FORMAT_ARGB32;
+
+  cairo_stride = cairo_format_stride_for_width (format, width);
+  cairo_pixels = g_malloc (height * cairo_stride);
+
+  surface = cairo_image_surface_create_for_data ((unsigned char *)cairo_pixels,
+                                                 format,
+                                                 width, height, cairo_stride);
+
+  cairo_surface_set_user_data (surface, &key,
+                               cairo_pixels, (cairo_destroy_func_t)g_free);
+  for (j = height; j; j--)
+    {
+      guchar *p = gdk_pixels;
+      guchar *q = cairo_pixels;
+
+      if (n_channels == 3)
+        {
+          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;
+            }
+        }
+      else
+        {
+          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_stride;
+    }
+
+  if (pixbuf->destroy_fn)
+    (* pixbuf->destroy_fn) (pixbuf->pixels, pixbuf->destroy_fn_data);
+
+  pixbuf->pixels = NULL;
+  pixbuf->destroy_fn = NULL;
+  pixbuf->destroy_fn_data = NULL;
+
+  pixbuf->surface = surface;
+}
+
 /**
  * gdk_pixbuf_get_pixels:
  * @pixbuf: A pixbuf.
@@ -456,10 +641,114 @@ gdk_pixbuf_get_pixels (const GdkPixbuf *pixbuf)
 {
 	g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
 
+        if (pixbuf->pixels == NULL) {
+                convert_surface_to_pixels (pixbuf);
+        }
+
 	return pixbuf->pixels;
 }
 
 /**
+ * gdk_pixbuf_get_cairo_surface:
+ * @pixbuf: a #GdkPixbuf
+ *
+ * Returns a cairo surface for the data @pixbuf.
+ *
+ * Note that GdkPixbuf only keeps one representation of the
+ * data around, so repeated calls to gdk_pixbuf_get_cairo_surface()
+ * and gdk_pixbuf_get_pixels() will cause the data to be converted
+ * back and forth between the 'classic' pixbuf format and a
+ * cairo surface.
+ *
+ * Returns: a cairo surface
+ */
+cairo_surface_t *
+gdk_pixbuf_get_cairo_surface (const GdkPixbuf *pixbuf)
+{
+        g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
+
+        if (pixbuf->surface == NULL) {
+                convert_pixels_to_surface (pixbuf);
+        }
+
+        return pixbuf->surface;
+}
+
+/**
+ * gdk_pixbuf_new_from_cairo_surface:
+ * @surface: a cairo surface
+ *
+ * Creates a GdkPixbuf from a cairo surface.
+ *
+ * Currently, only surfaces of type CAIRO_SURFACE_TYPE_IMAGE and
+ * format CAIRO_FORMAT_ARGB32 or CAIRO_FORMAT_RGB24 can be used.
+ *
+ * Returns: a newly created #GdkPixbuf
+ */
+GdkPixbuf *
+gdk_pixbuf_new_from_cairo_surface (cairo_surface_t *surface)
+{
+  int width;
+  int height;
+  int bits_per_sample;
+  int rowstride;
+  int n_channels;
+  GdkColorspace colorspace;
+  gboolean has_alpha;
+  GdkPixbuf *pixbuf;
+
+  pixbuf = NULL;
+
+  bits_per_sample = 8;
+  colorspace = GDK_COLORSPACE_RGB;
+
+  if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE)
+    {
+      width = cairo_image_surface_get_width (surface);
+      height = cairo_image_surface_get_height (surface);
+
+      switch (cairo_image_surface_get_format (surface))
+        {
+        case CAIRO_FORMAT_ARGB32:
+          has_alpha = TRUE;
+          n_channels = 4;
+          break;
+
+        case CAIRO_FORMAT_RGB24:
+          has_alpha = FALSE;
+          n_channels = 3;
+          break;
+
+        default:
+          g_warning ("can't convert cairo surface with format %d to GdkPixbuf", cairo_image_surface_get_format (surface));
+          goto out;
+        }
+
+      rowstride = width * n_channels;
+
+      pixbuf = g_object_new (GDK_TYPE_PIXBUF,
+                             "colorspace", colorspace,
+                             "n-channels", n_channels,
+                             "bits-per-sample", bits_per_sample,
+                             "has-alpha", has_alpha,
+                             "width", width,
+                             "height", height,
+                             "rowstride", rowstride,
+                             "pixels", NULL,
+                             NULL);
+
+      pixbuf->surface = cairo_surface_reference (surface);
+    }
+  else
+    g_warning ("can't convert cairo surface of type %d to GdkPixbuf", cairo_surface_get_type (surface));
+
+ out:
+
+  return pixbuf;
+}
+
+
+/**
  * gdk_pixbuf_get_width:
  * @pixbuf: A pixbuf.
  *
diff --git a/gdk-pixbuf/gdk-pixbuf.h b/gdk-pixbuf/gdk-pixbuf.h
index 9a30131..a6698d6 100644
--- a/gdk-pixbuf/gdk-pixbuf.h
+++ b/gdk-pixbuf/gdk-pixbuf.h
@@ -39,6 +39,7 @@
 #include <gdk-pixbuf/gdk-pixbuf-io.h>
 #include <gdk-pixbuf/gdk-pixbuf-loader.h>
 #include <gdk-pixbuf/gdk-pixbuf-enum-types.h>
+#include <gdk-pixbuf/gdk-pixbuf-cairo.h>
 
 #undef GDK_PIXBUF_H_INSIDE
 
-- 
1.7.2.2



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