Re: pixbuf<->cairo_surface_t conversion
- From: Matthias Clasen <matthias clasen gmail com>
- To: Havoc Pennington <hp pobox com>
- Cc: gtk-devel-list <gtk-devel-list gnome org>
- Subject: Re: pixbuf<->cairo_surface_t conversion
- Date: Fri, 3 Sep 2010 00:59:11 -0400
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]