On Thu, Sep 2, 2010 at 7:24 PM, Havoc Pennington <h...@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 <mcla...@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 <mcla...@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
_______________________________________________ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list