pixbuf<->cairo_surface_t conversion



See bugs:

https://bugzilla.gnome.org/show_bug.cgi?id=395578
https://bugzilla.gnome.org/show_bug.cgi?id=491507

I'm attaching a patch which proposes some API to make GdkPixbuf
support the cairo RGB24/ARGB32 formats.
In this patch, there's no actual code to convert, it would come from
gdk/gdkcairo.c, I didn't mess with it yet.

The idea would be to have a second patch, which would let you set up a
pixbuf loader to load the new formats; the pixbuf loader would then
convert on the fly for backends that didn't have support for the new
formats, initially that'd be all of them. But we could give loaders a
"format hint" later.

Then on top of this, you could trivially write the convenience APIs
Owen mentions in https://bugzilla.gnome.org/show_bug.cgi?id=491507
The convenience APIs, however, require a cairo dependency. The first
patch I'd propose doesn't add a dependency but instead adds the Cairo
formats to the GdkPixbuf object, with the goal of enabling loaders to
load pixbufs in those formats.

Some disorganized thoughts on path forward and stuff mentioned in the bug:

* In 395578 Søren suggests Cairo and Pixman could be changed to
support the GdkPixbuf format. This seems like it makes a mess of Cairo
and Pixman for no really good reason - isn't the GdkPixbuf format kind
of dumb? Unless it has some other use. Currently cairo only has a
short list of formats that have some decent rationale to exist, it
doesn't try to be comprehensive, which is nice.

* convenience APIs using cairo surface would be handy, but they don't
deprecate GdkPixbuf at all, because they wouldn't provide in
particular GdkPixbufLoader or GdkPixbufAnimation.

* given a new API for GdkPixbuf as attached here, the convenience APIs
are really tiny, ultra-trivial functions (and could be made more so by
adding more with_format() flavors of gdk-pixbuf APIs). Adding a cairo
and thus X11 dep to gdk-pixbuf in order to support four, five-line
functions ... I don't know

* another way to provide cairo_surface_t convenience might be to have
pixbuf->surface as a standard lazily-created pixbuf field, and add
gdk_pixbuf_get_cairo_surface(). The idea would be that then you can
just pass a pixbuf around or use Loader and Animation just fine, and
the cairo_surface_t is just a little wrapper without its own pixel
buffer and without its own memory management, that is attached to the
pixbuf and can be used to draw. (In this scenario, arguably using
cairo_surface_t instead of pixbuf in new non-drawing-related GDK/GTK
APIs was a mistake, pixbuf should have been used since it's a GObject,
and surface only used for drawing)

* one long-term goal could be to deprecate the gdk-pixbuf.h API, but
keep its loader backends (and gradually refactor them to load directly
to cairo). Likely this involves new, cairo-centric GdkPixbufLoader and
GdkPixbufAnimation.

* (one downside of this goal is that cairo_surface_t is not a GObject
and can be a pain in the ass sometimes as a result.)

* GdkPixbufLoader and GdkPixbufAnimation are a mess to put in Cairo
itself because they wouldn't be GObjects either. Loader in particular
uses signals for example. Seems to point to a permanent "above cairo,
below gdk" image-loader library?

* I don't really know where Loader and Animation should go live. One
option is to add a separate "cairo centric" header in gdk-pixbuf
itself - could be a separate .so or not, but a separate header/API -
that was just a wrapper around gdk-pixbuf to start and would evolve to
drop it someday. The cairo-centric API would still use GLib though
perhaps, and just kind of s/pixbuf/surface/g in Loader and Animation?
I guess what I'm saying here is that it may be worth a separate lib or
header because it really isn't just four functions.

* A possibility would be to make the pixbufs in cairo format an
"internal only" feature of gdk-pixbuf, and export only the convenience
API; so apps would not see cairo-format pixbufs ever, they'd only see
cairo surfaces that happen to wrap those pixbufs.

* Seems also plausible to keep Loader and Animation as-is and
undeprecated, have a nice easy way to get a cairo surface from a
pixbuf, but start deprecating the other bits of gdk-pixbuf that do
duplicate cairo and aren't worth reimplementing with support for the
cairo pixbuf formats (e.g. gdk_pixbuf_scale).

* Loader and Animation are the real questions as far as what to do
with them, and where to put convenience APIs and what part of
gdk-pixbuf to deprecate would probably be obvious once Loader and
Animation were sorted out


While all this is up in the air, maybe there's some value to just
getting the format conversion code and pixbufs-in-cairo-format in
gdk-pixbuf, as a first step toward refactoring the pixbuf loader
backends to load in cairo format.

And while there may not be convenience functions at first, at least
the inconvenient functions are trivial instead of having to mess with
pixels.

Havoc
diff --git a/gdk-pixbuf/gdk-pixbuf-core.h b/gdk-pixbuf/gdk-pixbuf-core.h
index b256380..93d85e4 100644
--- a/gdk-pixbuf/gdk-pixbuf-core.h
+++ b/gdk-pixbuf/gdk-pixbuf-core.h
@@ -51,6 +51,41 @@ typedef enum {
 	GDK_COLORSPACE_RGB
 } GdkColorspace;
 
+/**
+ * GdkPixelFormat:
+ *
+ * Format of a pixbuf. Typically implies colorspace, number of
+ * channels, whether there's alpha, and so forth.
+ */
+typedef enum {
+        /* This first block of values matches cairo_format_t.
+         */
+        GDK_PIXEL_FORMAT_INVALID   = -1,
+        GDK_PIXEL_FORMAT_ARGB32    = 0,
+        GDK_PIXEL_FORMAT_RGB24     = 1,
+
+        /* Not supported yet, but in cairo.
+        GDK_PIXEL_FORMAT_A8        = 2,
+        GDK_PIXEL_FORMAT_A1        = 3,
+        GDK_PIXEL_FORMAT_RGB16_565 = 4,
+        */
+
+        /* Now we jump up to a higher value (to allow cairo_format_t
+         * to expand) and we have the "classic" gdk-pixbuf formats.
+         * These differ from cairo in three ways: they are not
+         * premultiplied, their byte order is fixed not native, and
+         * each pixel is always 32 bits (if no alpha, there's a pad
+         * byte).
+         *
+         * BE = Big Endian. These are named so the name includes
+         * all differences from the cairo formats, which should
+         * allow adding any other format while keeping consistent
+         * naming.
+         */
+        GDK_PIXEL_FORMAT_RGB24_BE_NO_PAD = 29,
+        GDK_PIXEL_FORMAT_RGBA32_BE_NO_PREMUL = 30
+} GdkPixelFormat;
+
 /* All of these are opaque structures */
 typedef struct _GdkPixbuf GdkPixbuf;
 
@@ -93,14 +128,15 @@ void       gdk_pixbuf_unref    (GdkPixbuf *pixbuf);
 
 /* GdkPixbuf accessors */
 
-GdkColorspace gdk_pixbuf_get_colorspace      (const GdkPixbuf *pixbuf);
-int           gdk_pixbuf_get_n_channels      (const GdkPixbuf *pixbuf);
-gboolean      gdk_pixbuf_get_has_alpha       (const GdkPixbuf *pixbuf);
-int           gdk_pixbuf_get_bits_per_sample (const GdkPixbuf *pixbuf);
-guchar       *gdk_pixbuf_get_pixels          (const GdkPixbuf *pixbuf);
-int           gdk_pixbuf_get_width           (const GdkPixbuf *pixbuf);
-int           gdk_pixbuf_get_height          (const GdkPixbuf *pixbuf);
-int           gdk_pixbuf_get_rowstride       (const GdkPixbuf *pixbuf);
+GdkColorspace  gdk_pixbuf_get_colorspace      (const GdkPixbuf *pixbuf);
+int            gdk_pixbuf_get_n_channels      (const GdkPixbuf *pixbuf);
+gboolean       gdk_pixbuf_get_has_alpha       (const GdkPixbuf *pixbuf);
+int            gdk_pixbuf_get_bits_per_sample (const GdkPixbuf *pixbuf);
+guchar        *gdk_pixbuf_get_pixels          (const GdkPixbuf *pixbuf);
+int            gdk_pixbuf_get_width           (const GdkPixbuf *pixbuf);
+int            gdk_pixbuf_get_height          (const GdkPixbuf *pixbuf);
+int            gdk_pixbuf_get_rowstride       (const GdkPixbuf *pixbuf);
+GdkPixelFormat gdk_pixbuf_get_pixel_format    (const GdkPixbuf *pixbuf);
 
 
 
@@ -108,6 +144,10 @@ int           gdk_pixbuf_get_rowstride       (const GdkPixbuf *pixbuf);
 GdkPixbuf *gdk_pixbuf_new (GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample,
 			   int width, int height);
 
+GdkPixbuf *gdk_pixbuf_new_with_format (GdkPixelFormat format,
+                                       int width, int height);
+
+
 /* Copy a pixbuf */
 
 GdkPixbuf *gdk_pixbuf_copy (const GdkPixbuf *pixbuf);
@@ -149,6 +189,13 @@ GdkPixbuf *gdk_pixbuf_new_from_data (const guchar *data,
 				     GdkPixbufDestroyNotify destroy_fn,
 				     gpointer destroy_fn_data);
 
+GdkPixbuf *gdk_pixbuf_new_from_data_with_format (const guchar *data,
+                                                 GdkPixelFormat format,
+                                                 int width, int height,
+                                                 int rowstride,
+                                                 GdkPixbufDestroyNotify destroy_fn,
+                                                 gpointer destroy_fn_data);
+
 GdkPixbuf *gdk_pixbuf_new_from_xpm_data (const char **data);
 GdkPixbuf* gdk_pixbuf_new_from_inline	(gint          data_length,
 					 const guint8 *data,
@@ -260,6 +307,16 @@ GdkPixbuf *gdk_pixbuf_apply_embedded_orientation (GdkPixbuf *src);
 G_CONST_RETURN gchar * gdk_pixbuf_get_option (GdkPixbuf   *pixbuf,
                                               const gchar *key);
 
+/* Convert to another format */
+void       gdk_pixbuf_convert_pixel_format          (const GdkPixbuf *src,
+                                                     GdkPixbuf       *dest,
+                                                     int              dest_x,
+                                                     int              dest_y,
+                                                     int              dest_width,
+                                                     int              dest_height);
+GdkPixbuf *gdk_pixbuf_copy_and_convert_pixel_format (const GdkPixbuf *src,
+                                                     GdkPixelFormat   format);
+
 
 G_END_DECLS
 
diff --git a/gdk-pixbuf/gdk-pixbuf-data.c b/gdk-pixbuf/gdk-pixbuf-data.c
index 87af0b9..041ec1c 100644
--- a/gdk-pixbuf/gdk-pixbuf-data.c
+++ b/gdk-pixbuf/gdk-pixbuf-data.c
@@ -49,7 +49,7 @@
 GdkPixbuf *
 gdk_pixbuf_new_from_data (const guchar *data, GdkColorspace colorspace, gboolean has_alpha,
 			  int bits_per_sample, int width, int height, int rowstride,
-	  GdkPixbufDestroyNotify destroy_fn, gpointer destroy_fn_data)
+                          GdkPixbufDestroyNotify destroy_fn, gpointer destroy_fn_data)
 {
 	GdkPixbuf *pixbuf;
 
@@ -61,17 +61,63 @@ gdk_pixbuf_new_from_data (const guchar *data, GdkColorspace colorspace, gboolean
 	g_return_val_if_fail (width > 0, NULL);
 	g_return_val_if_fail (height > 0, NULL);
 
-	pixbuf = g_object_new (GDK_TYPE_PIXBUF, 
-			       "colorspace", colorspace,
-			       "n-channels", has_alpha ? 4 : 3,
-			       "bits-per-sample", bits_per_sample,
-			       "has-alpha", has_alpha ? TRUE : FALSE,
+	pixbuf = gdk_pixbuf_new_from_data_with_format(data,
+                                                      has_alpha ? GDK_PIXEL_FORMAT_RGBA32_BE_NO_PREMUL :
+                                                      GDK_PIXEL_FORMAT_RGB24_BE_NO_PAD,
+                                                      width, height, rowstride,
+                                                      destroy_fn, destroy_fn_data);
+        g_assert (pixbuf->colorspace == colorspace);
+        g_assert (pixbuf->has_alpha == has_alpha);
+        g_assert (pixbuf->bits_per_sample == bits_per_sample);
+
+	return pixbuf;
+}
+
+/**
+ * gdk_pixbuf_new_from_data_with_format:
+ * @data: Image data in the given format
+ * @format: The format of the data
+ * @width: Width of the image in pixels, must be > 0
+ * @height: Height of the image in pixels, must be > 0
+ * @rowstride: Distance in bytes between row starts
+ * @destroy_fn: Function used to free the data when the pixbuf's reference count
+ * drops to zero, or %NULL if the data should not be freed
+ * @destroy_fn_data: Closure data to pass to the destroy notification function
+ *
+ * Creates a new #GdkPixbuf out of in-memory image data.
+ *
+ * Since: 2.22
+ * Return value: A newly-created #GdkPixbuf structure with a reference count of 1.
+ **/
+GdkPixbuf *
+gdk_pixbuf_new_from_data_with_format (const guchar *data,
+                                      GdkPixelFormat format,
+                                      int width, int height,
+                                      int rowstride,
+                                      GdkPixbufDestroyNotify destroy_fn,
+                                      gpointer destroy_fn_data)
+{
+	GdkPixbuf *pixbuf;
+        GdkPixelFormatAttributes attrs;
+
+	g_return_val_if_fail (data != NULL, NULL);
+	g_return_val_if_fail (width > 0, NULL);
+	g_return_val_if_fail (height > 0, NULL);
+
+        _gdk_pixel_format_get_attributes (format, &attrs);
+
+	pixbuf = g_object_new (GDK_TYPE_PIXBUF,
+			       "colorspace", attrs.colorspace,
+			       "n-channels", attrs.n_channels,
+			       "bits-per-sample", attrs.bits_per_sample,
+			       "has-alpha", attrs.has_alpha,
 			       "width", width,
 			       "height", height,
 			       "rowstride", rowstride,
+                               "format", format,
 			       "pixels", data,
 			       NULL);
-        
+
 	pixbuf->destroy_fn = destroy_fn;
 	pixbuf->destroy_fn_data = destroy_fn_data;
 
diff --git a/gdk-pixbuf/gdk-pixbuf-private.h b/gdk-pixbuf/gdk-pixbuf-private.h
index c060bd7..f6f7d6a 100644
--- a/gdk-pixbuf/gdk-pixbuf-private.h
+++ b/gdk-pixbuf/gdk-pixbuf-private.h
@@ -62,6 +62,9 @@ struct _GdkPixbuf {
 	/* Offset between rows */
 	int rowstride;
 
+        /* Format */
+        GdkPixelFormat format;
+
 	/* The pixel array */
 	guchar *pixels;
 
@@ -80,6 +83,18 @@ struct _GdkPixbufClass {
 
 };
 
+
+typedef struct {
+        GdkColorspace colorspace;
+        int bits_per_sample;
+        gboolean has_alpha;
+        int n_channels;
+        int bits_per_pixel;
+} GdkPixelFormatAttributes;
+
+void _gdk_pixel_format_get_attributes (GdkPixelFormat            format,
+                                       GdkPixelFormatAttributes *attrs);
+
 #ifdef GDK_PIXBUF_ENABLE_BACKEND
 
 gboolean _gdk_pixbuf_lock (GdkPixbufModule *image_module);
diff --git a/gdk-pixbuf/gdk-pixbuf-util.c b/gdk-pixbuf/gdk-pixbuf-util.c
index 4b29669..58a2801 100644
--- a/gdk-pixbuf/gdk-pixbuf-util.c
+++ b/gdk-pixbuf/gdk-pixbuf-util.c
@@ -348,3 +348,140 @@ gdk_pixbuf_gettext (const gchar *msgid)
 
         return g_dgettext (GETTEXT_PACKAGE, msgid);
 }
+
+void
+_gdk_pixel_format_get_attributes (GdkPixelFormat            format,
+                                  GdkPixelFormatAttributes *attrs)
+{
+        attrs->colorspace = GDK_COLORSPACE_RGB;
+        attrs->bits_per_sample = 8;
+
+        switch (format) {
+        case GDK_PIXEL_FORMAT_INVALID: /* avoid compiler warning */
+                g_assert_not_reached();
+                break;
+
+        case GDK_PIXEL_FORMAT_ARGB32:
+        case GDK_PIXEL_FORMAT_RGBA32_BE_NO_PREMUL:
+                attrs->n_channels = 4;
+                attrs->has_alpha = TRUE;
+                attrs->bits_per_pixel = 32;
+                break;
+
+        case GDK_PIXEL_FORMAT_RGB24:
+        case GDK_PIXEL_FORMAT_RGB24_BE_NO_PAD:
+                /* FIXME n_channels is conceptually 3 for
+                 * GDK_PIXEL_FORMAT_RGB24 but I know code uses it as a
+                 * pixel stride, not a conceptual number of color
+                 * channels, and that code will break due to the
+                 * padding byte. Not sure what to do with this.
+                 * Should n_channels be 3 or bits_per_pixel/8 ?
+                 */
+                attrs->n_channels = 3;
+                attrs->has_alpha = FALSE;
+                if (format == GDK_PIXEL_FORMAT_RGB24_BE_NO_PAD)
+                        attrs->bits_per_pixel = 24;
+                else
+                        attrs->bits_per_pixel = 32;
+                break;
+        }
+}
+
+/**
+ * gdk_pixbuf_copy_and_convert_pixel_format:
+ * @src: source image
+ * @format: format to convert to
+ *
+ * Copies a pixbuf to a new pixbuf with a different pixel format.
+ *
+ * Alpha cannot be added or removed as part of format conversion,
+ * i.e. you must convert between formats that both have or both lack
+ * alpha. See gdk_pixbuf_add_alpha() if you need to add alpha and
+ * gdk_pixbuf_composite() if you need to remove it.
+ *
+ * Note: at the moment, most operations on a #GdkPixbuf are only possible
+ * in the "classic" formats, #GDK_PIXEL_FORMAT_RGBA32_BE_NO_PREMUL and
+ * #GDK_PIXEL_FORMAT_RGB24_BE_NO_PAD. Other formats are primarily
+ * used with Cairo operations instead.
+ *
+ * Since: 2.22
+ *
+ * Return value: A newly-created pixbuf with a reference count of 1 or
+ * #NULL if not enough memory could be allocated for the image buffer.
+ */
+GdkPixbuf*
+gdk_pixbuf_copy_and_convert_pixel_format (const GdkPixbuf *src,
+                                          GdkPixelFormat   format)
+{
+        GdkPixbuf *dest;
+
+        g_return_val_if_fail (GDK_IS_PIXBUF (src), NULL);
+
+        if (src->format == format)
+                return gdk_pixbuf_copy (src);
+
+        dest = gdk_pixbuf_new_with_format (format,
+                                           gdk_pixbuf_get_width (src),
+                                           gdk_pixbuf_get_height (src));
+        if (dest == NULL)
+                return NULL;
+
+        gdk_pixbuf_convert_pixel_format (src,
+                                         dest,
+                                         0, 0,
+                                         gdk_pixbuf_get_width (src),
+                                         gdk_pixbuf_get_height (src));
+
+        return dest;
+}
+
+/**
+ * gdk_pixbuf_convert_pixel_format:
+ * @src: the source image
+ * @dest: the destination image
+ * @dest_x: x coord to write the pixels in dest
+ * @dest_y: y coord to write the pixels in dest
+ * @dest_width: width of pixels to write
+ * @dest_height: height of pixels to write
+ *
+ * This function copies the source pixbuf to a rectangle in the
+ * destination pixbuf, converting the pixel format at the same time.
+ * If the pixel formats are the same, the function just makes a copy.
+ *
+ * Alpha cannot be added or removed as part of format conversion,
+ * i.e. you must convert between formats that both have or both lack
+ * alpha. See gdk_pixbuf_add_alpha() if you need to add alpha and
+ * gdk_pixbuf_composite() if you need to remove it.
+ *
+ * The rectangle defined by @dest_x,@dest_y,@dest_width,@dest_height
+ * must be completely inside the destination pixbuf.
+ *
+ * If you want to copy from somewhere other than 0,0 on a source
+ * pixbuf, see gdk_pixbuf_new_subpixbuf().
+ *
+ * Note: at the moment, most operations on a #GdkPixbuf are only possible
+ * in the "classic" formats, #GDK_PIXEL_FORMAT_RGBA32_BE_NO_PREMUL and
+ * #GDK_PIXEL_FORMAT_RGB24_BE_NO_PAD. Other formats are primarily
+ * used with Cairo operations instead.
+ *
+ * Since: 2.22
+ */
+void
+gdk_pixbuf_convert_pixel_format (const GdkPixbuf *src,
+                                 GdkPixbuf       *dest,
+                                 int              dest_x,
+				 int              dest_y,
+				 int              dest_width,
+				 int              dest_height)
+{
+        g_return_if_fail (GDK_IS_PIXBUF (src));
+        g_return_if_fail (GDK_IS_PIXBUF (dest));
+        g_return_if_fail (dest_x >= 0 && dest_x + dest_width <= dest->width);
+        g_return_if_fail (dest_y >= 0 && dest_y + dest_height <= dest->height);
+
+        /* FIXME copy code from gdkcairo.c and implement its inverse.
+         * We would implement here four conversions:
+         * Cairo RGB to-from classic RGB, and
+         * Cairo ARGB to-from classic RGBA
+         */
+}
diff --git a/gdk-pixbuf/gdk-pixbuf.c b/gdk-pixbuf/gdk-pixbuf.c
index 824e290..431c6f4 100644
--- a/gdk-pixbuf/gdk-pixbuf.c
+++ b/gdk-pixbuf/gdk-pixbuf.c
@@ -59,6 +59,7 @@ enum
   PROP_WIDTH,
   PROP_HEIGHT,
   PROP_ROWSTRIDE,
+  PROP_FORMAT,
   PROP_PIXELS
 };
 
@@ -166,6 +167,21 @@ gdk_pixbuf_class_init (GdkPixbufClass *klass)
                                                            1,
                                                            PIXBUF_PARAM_FLAGS));
 
+        /**
+         * GdkPixbuf:format:
+         *
+         * How pixels are encoded; includes factors such as endianness, padding, and
+         * premultiplication in additon to colorspace, number of channels, etc.
+         */
+        g_object_class_install_property (object_class,
+                                         PROP_FORMAT,
+                                         g_param_spec_enum ("format",
+                                                            P_("Format"),
+                                                            P_("Encoding of pixels in the pixbuf"),
+                                                            GDK_TYPE_PIXEL_FORMAT,
+                                                            GDK_PIXEL_FORMAT_RGB24_BE_NO_PAD,
+                                                            PIXBUF_PARAM_FLAGS));
+
         g_object_class_install_property (object_class,
                                          PROP_PIXELS,
                                          g_param_spec_pointer ("pixels",
@@ -278,6 +294,59 @@ gdk_pixbuf_new (GdkColorspace colorspace,
 					 free_buffer, NULL);
 }
 
+
+
+/* Macros from Cairo. We always want to match Cairo (and Pixman)
+ * required stride alignment in the Cairo formats.
+ */
+#define CAIRO_STRIDE_ALIGNMENT (sizeof (guint32))
+#define CAIRO_STRIDE_FOR_WIDTH_BPP(w,bpp) \
+   ((((bpp)*(w)+7)/8 + CAIRO_STRIDE_ALIGNMENT-1) & -CAIRO_STRIDE_ALIGNMENT)
+
+/**
+ * gdk_pixbuf_new_with_format:
+ * @format: pixel format of the image buffer
+ * @width: Width of image in pixels, must be > 0
+ * @height: Height of image in pixels, must be > 0
+ *
+ * Creates a new #GdkPixbuf structure and allocates a buffer for it.  The
+ * buffer has an optimal rowstride.  Note that the buffer is not cleared;
+ * you will have to fill it completely yourself.
+ *
+ * Return value: A newly-created #GdkPixbuf with a reference count of 1, or
+ * %NULL if not enough memory could be allocated for the image buffer.
+ */
+GdkPixbuf *
+gdk_pixbuf_new_with_format (GdkPixelFormat format,
+                            int            width,
+                            int            height)
+{
+	guchar *buf;
+        int rowstride;
+        gsize bytes;
+        GdkPixelFormatAttributes attrs;
+
+	g_return_val_if_fail (width > 0, NULL);
+	g_return_val_if_fail (height > 0, NULL);
+
+        _gdk_pixel_format_get_attributes (format, &attrs);
+        rowstride = CAIRO_STRIDE_FOR_WIDTH_BPP (width, attrs.bits_per_pixel);
+        if (rowstride / (attrs.bits_per_pixel / 8) != width || rowstride + 3 < 0) /* overflow */
+                return NULL;
+
+        bytes = height * rowstride;
+        if (bytes / rowstride !=  height) /* overflow */
+                return NULL;
+
+	buf = g_try_malloc (bytes);
+	if (!buf)
+		return NULL;
+
+	return gdk_pixbuf_new_from_data_with_format (buf, format,
+                                                     width, height, rowstride,
+                                                     free_buffer, NULL);
+}
+
 /**
  * gdk_pixbuf_copy:
  * @pixbuf: A pixbuf.
@@ -296,6 +365,11 @@ gdk_pixbuf_copy (const GdkPixbuf *pixbuf)
 
 	g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
 
+        /* FIXME n_channels will be messed up for cairo's RGB24.  why
+         * are we doing the below code anyway? what's wrong with
+         * pixbuf->height * pixbuf->rowstride?
+         */
+
 	/* Calculate a semi-exact size.  Here we copy with full rowstrides;
 	 * maybe we should copy each row individually with the minimum
 	 * rowstride?
@@ -508,6 +582,36 @@ gdk_pixbuf_get_rowstride (const GdkPixbuf *pixbuf)
 	return pixbuf->rowstride;
 }
 
+/**
+ * gdk_pixbuf_get_pixel_format:
+ * @pixbuf: A pixbuf.
+ *
+ * Queries the pixel format of a pixbuf, which is a summary value
+ * generally implying the rowstride, number of channels, whether it
+ * has alpha, colorspace, and how the channels and alpha are encoded
+ * in a pixel.
+ *
+ * Historically, #GdkPixbuf would always be in one of the formats
+ * #GDK_PIXEL_FORMAT_RGBA32_BE_NO_PREMUL or
+ * #GDK_PIXEL_FORMAT_RGB24_BE_NO_PAD, and plenty of code will break on
+ * pixbufs not in these formats. By default, all constructors and
+ * loaders in the gdk-pixbuf library return only those formats.
+ * However, pixbufs can be converted to other formats with
+ * gdk_pixbuf_convert_pixel_format() or
+ * gdk_pixbuf_copy_and_convert_pixel_format(). After converting,
+ * only use the pixbuf with APIs that support the new format.
+ * The main reason to convert is to transfer a pixbuf to
+ * a cairo_surface_t and then use Cairo operations rather
+ * than pixbuf operations.
+ *
+ * Return value: Pixel format of a pixbuf
+ **/
+GdkPixelFormat
+gdk_pixbuf_get_pixel_format (const GdkPixbuf *pixbuf)
+{
+        return pixbuf->format;
+}
+
 
 
 /* General initialization hooks */


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