[gdk-pixbuf] Add _new_from_bytes() and _read_pixels() API, handle read-only pixbufs
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gdk-pixbuf] Add _new_from_bytes() and _read_pixels() API, handle read-only pixbufs
- Date: Sat, 19 Jul 2014 03:02:31 +0000 (UTC)
commit 862e3890f54aaeb8589a6ee4c146f1fb50556004
Author: Colin Walters <walters verbum org>
Date: Sat Jul 5 11:15:20 2014 -0400
Add _new_from_bytes() and _read_pixels() API, handle read-only pixbufs
GdkPixbuf is an old API that predates introspection and GBytes. It
has some confusion around whether or not pixbuf data is mutable or
not. The _new_from_data() API takes a *const* pointer, but it's not
copied, and _get_pixels() returns a non-const copy of the same
pointer.
There are several cases where we get read-only data, such as a
GResource. For language bindings, _new_from_data() doesn't work
because the array may be a temporary copy only allocated for the call.
In order to support a clean _new_from_bytes() API, we need to add the
concept of a read-only pixbuf into the core. The fundamental hack
here is that _get_pixels() now causes an implicit copy.
For the cases where we don't want to copy, add a new
gdk_pixbuf_read_pixels() that returns a proper const pointer.
https://bugzilla.gnome.org/show_bug.cgi?id=732297
gdk-pixbuf/gdk-pixbuf-core.h | 10 ++
gdk-pixbuf/gdk-pixbuf-data.c | 50 +++++++++++-
gdk-pixbuf/gdk-pixbuf-private.h | 3 +
gdk-pixbuf/gdk-pixbuf-scale.c | 76 +++++++++++++----
gdk-pixbuf/gdk-pixbuf-util.c | 19 +++--
gdk-pixbuf/gdk-pixbuf.c | 116 ++++++++++++++++++++------
gdk-pixbuf/gdk-pixbuf.symbols | 2 +
tests/Makefile.am | 7 ++
tests/pixbuf-readonly-to-mutable.c | 159 ++++++++++++++++++++++++++++++++++++
9 files changed, 390 insertions(+), 52 deletions(-)
---
diff --git a/gdk-pixbuf/gdk-pixbuf-core.h b/gdk-pixbuf/gdk-pixbuf-core.h
index 025a8ae..9f11d87 100644
--- a/gdk-pixbuf/gdk-pixbuf-core.h
+++ b/gdk-pixbuf/gdk-pixbuf-core.h
@@ -241,6 +241,9 @@ gsize gdk_pixbuf_get_byte_length (const GdkPixbuf *pixbuf);
guchar *gdk_pixbuf_get_pixels_with_length (const GdkPixbuf *pixbuf,
guint *length);
+const guint8* gdk_pixbuf_read_pixels (const GdkPixbuf *pixbuf);
+
+
@@ -298,6 +301,13 @@ GdkPixbuf *gdk_pixbuf_new_from_data (const guchar *data,
GdkPixbufDestroyNotify destroy_fn,
gpointer destroy_fn_data);
+GdkPixbuf *gdk_pixbuf_new_from_bytes (GBytes *data,
+ GdkColorspace colorspace,
+ gboolean has_alpha,
+ int bits_per_sample,
+ int width, int height,
+ int rowstride);
+
GdkPixbuf *gdk_pixbuf_new_from_xpm_data (const char **data);
GdkPixbuf* gdk_pixbuf_new_from_inline (gint data_length,
const guint8 *data,
diff --git a/gdk-pixbuf/gdk-pixbuf-data.c b/gdk-pixbuf/gdk-pixbuf-data.c
index d9c2291..769ead3 100644
--- a/gdk-pixbuf/gdk-pixbuf-data.c
+++ b/gdk-pixbuf/gdk-pixbuf-data.c
@@ -41,7 +41,15 @@
*
* Creates a new #GdkPixbuf out of in-memory image data. Currently only RGB
* images with 8 bits per sample are supported.
- *
+ *
+ * Since you are providing a pre-allocated pixel buffer, you must also
+ * specify a way to free that data. This is done with a function of
+ * type #GdkPixbufDestroyNotify. When a pixbuf created with is
+ * finalized, your destroy notification function will be called, and
+ * it is its responsibility to free the pixel array.
+ *
+ * See also gdk_pixbuf_new_from_bytes().
+ *
* Return value: (transfer full): A newly-created #GdkPixbuf structure with a reference count of 1.
**/
GdkPixbuf *
@@ -75,3 +83,43 @@ gdk_pixbuf_new_from_data (const guchar *data, GdkColorspace colorspace, gboolean
return pixbuf;
}
+
+/**
+ * gdk_pixbuf_new_from_bytes:
+ * @data: Image data in 8-bit/sample packed format inside a #GBytes
+ * @colorspace: Colorspace for the image data
+ * @has_alpha: Whether the data has an opacity channel
+ * @bits_per_sample: Number of bits per sample
+ * @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
+ *
+ * Creates a new #GdkPixbuf out of in-memory readonly image data.
+ * Currently only RGB images with 8 bits per sample are supported.
+ * This is the #GBytes variant of gdk_pixbuf_new_from_data().
+ *
+ * Return value: (transfer full): A newly-created #GdkPixbuf structure with a reference count of 1.
+ * Since: 2.32
+ **/
+GdkPixbuf *
+gdk_pixbuf_new_from_bytes (GBytes *data, GdkColorspace colorspace, gboolean has_alpha,
+ int bits_per_sample, int width, int height, int rowstride)
+{
+ g_return_val_if_fail (data != NULL, NULL);
+ g_return_val_if_fail (colorspace == GDK_COLORSPACE_RGB, NULL);
+ g_return_val_if_fail (bits_per_sample == 8, NULL);
+ g_return_val_if_fail (width > 0, NULL);
+ g_return_val_if_fail (height > 0, NULL);
+ g_return_val_if_fail (g_bytes_get_size (data) >= width * height * (has_alpha ? 4 : 3), NULL);
+
+ return (GdkPixbuf*) g_object_new (GDK_TYPE_PIXBUF,
+ "pixel-bytes", data,
+ "colorspace", colorspace,
+ "n-channels", has_alpha ? 4 : 3,
+ "bits-per-sample", bits_per_sample,
+ "has-alpha", has_alpha ? TRUE : FALSE,
+ "width", width,
+ "height", height,
+ "rowstride", rowstride,
+ NULL);
+}
diff --git a/gdk-pixbuf/gdk-pixbuf-private.h b/gdk-pixbuf/gdk-pixbuf-private.h
index 7c88d43..e209694 100644
--- a/gdk-pixbuf/gdk-pixbuf-private.h
+++ b/gdk-pixbuf/gdk-pixbuf-private.h
@@ -73,6 +73,9 @@ struct _GdkPixbuf {
/* User data for the destroy notification function */
gpointer destroy_fn_data;
+ /* Replaces "pixels" member (and destroy notify) */
+ GBytes *bytes;
+
/* Do we have an alpha channel? */
guint has_alpha : 1;
};
diff --git a/gdk-pixbuf/gdk-pixbuf-scale.c b/gdk-pixbuf/gdk-pixbuf-scale.c
index d6d4392..4288c65 100644
--- a/gdk-pixbuf/gdk-pixbuf-scale.c
+++ b/gdk-pixbuf/gdk-pixbuf-scale.c
@@ -46,6 +46,8 @@
* given size, scale an original image to fit, and then return the
* new pixbuf.
*
+ * If the destination pixbuf was created from a readonly source, these
+ * operations will force a copy into a mutable buffer.
*
* Scaling and compositing functions take advantage of MMX hardware
* acceleration on systems where MMX is supported. If gdk-pixbuf is built
@@ -136,6 +138,9 @@ gdk_pixbuf_scale (const GdkPixbuf *src,
double scale_y,
GdkInterpType interp_type)
{
+ const guint8 *src_pixels;
+ guint8 *dest_pixels;
+
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);
@@ -144,8 +149,12 @@ gdk_pixbuf_scale (const GdkPixbuf *src,
offset_x = floor (offset_x + 0.5);
offset_y = floor (offset_y + 0.5);
- _pixops_scale (dest->pixels, dest->width, dest->height, dest->rowstride,
- dest->n_channels, dest->has_alpha, src->pixels, src->width,
+ /* Force an implicit copy */
+ dest_pixels = gdk_pixbuf_get_pixels (dest);
+ src_pixels = gdk_pixbuf_read_pixels (src);
+
+ _pixops_scale (dest_pixels, dest->width, dest->height, dest->rowstride,
+ dest->n_channels, dest->has_alpha, src_pixels, src->width,
src->height, src->rowstride, src->n_channels, src->has_alpha,
dest_x, dest_y, dest_width, dest_height, offset_x, offset_y,
scale_x, scale_y, (PixopsInterpType)interp_type);
@@ -193,6 +202,9 @@ gdk_pixbuf_composite (const GdkPixbuf *src,
GdkInterpType interp_type,
int overall_alpha)
{
+ const guint8 *src_pixels;
+ guint8 *dest_pixels;
+
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);
@@ -202,8 +214,12 @@ gdk_pixbuf_composite (const GdkPixbuf *src,
offset_x = floor (offset_x + 0.5);
offset_y = floor (offset_y + 0.5);
- _pixops_composite (dest->pixels, dest->width, dest->height, dest->rowstride,
- dest->n_channels, dest->has_alpha, src->pixels,
+ /* Force an implicit copy */
+ dest_pixels = gdk_pixbuf_get_pixels (dest);
+ src_pixels = gdk_pixbuf_read_pixels (src);
+
+ _pixops_composite (dest_pixels, dest->width, dest->height, dest->rowstride,
+ dest->n_channels, dest->has_alpha, src_pixels,
src->width, src->height, src->rowstride, src->n_channels,
src->has_alpha, dest_x, dest_y, dest_width, dest_height,
offset_x, offset_y, scale_x, scale_y,
@@ -260,6 +276,9 @@ gdk_pixbuf_composite_color (const GdkPixbuf *src,
guint32 color1,
guint32 color2)
{
+ const guint8 *src_pixels;
+ guint8 *dest_pixels;
+
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);
@@ -269,9 +288,13 @@ gdk_pixbuf_composite_color (const GdkPixbuf *src,
offset_x = floor (offset_x + 0.5);
offset_y = floor (offset_y + 0.5);
- _pixops_composite_color (dest->pixels, dest_width, dest_height,
+ /* Force an implicit copy */
+ dest_pixels = gdk_pixbuf_get_pixels (dest);
+ src_pixels = gdk_pixbuf_read_pixels (src);
+
+ _pixops_composite_color (dest_pixels, dest_width, dest_height,
dest->rowstride, dest->n_channels, dest->has_alpha,
- src->pixels, src->width, src->height,
+ src_pixels, src->width, src->height,
src->rowstride, src->n_channels, src->has_alpha,
dest_x, dest_y, dest_width, dest_height, offset_x,
offset_y, scale_x, scale_y,
@@ -392,10 +415,15 @@ GdkPixbuf *
gdk_pixbuf_rotate_simple (const GdkPixbuf *src,
GdkPixbufRotation angle)
{
+ const guint8 *src_pixels;
+ guint8 *dest_pixels;
GdkPixbuf *dest;
- guchar *p, *q;
+ const guchar *p;
+ guchar *q;
gint x, y;
+ src_pixels = gdk_pixbuf_read_pixels (src);
+
switch (angle % 360)
{
case 0:
@@ -410,12 +438,14 @@ gdk_pixbuf_rotate_simple (const GdkPixbuf *src,
if (!dest)
return NULL;
+ dest_pixels = gdk_pixbuf_get_pixels (dest);
+
for (y = 0; y < src->height; y++)
{
for (x = 0; x < src->width; x++)
{
- p = src->pixels + OFFSET (src, x, y);
- q = dest->pixels + OFFSET (dest, y, src->width - x - 1);
+ p = src_pixels + OFFSET (src, x, y);
+ q = dest_pixels + OFFSET (dest, y, src->width - x - 1);
memcpy (q, p, dest->n_channels);
}
}
@@ -429,12 +459,14 @@ gdk_pixbuf_rotate_simple (const GdkPixbuf *src,
if (!dest)
return NULL;
+ dest_pixels = gdk_pixbuf_get_pixels (dest);
+
for (y = 0; y < src->height; y++)
{
for (x = 0; x < src->width; x++)
{
- p = src->pixels + OFFSET (src, x, y);
- q = dest->pixels + OFFSET (dest, src->width - x - 1, src->height - y - 1);
+ p = src_pixels + OFFSET (src, x, y);
+ q = dest_pixels + OFFSET (dest, src->width - x - 1, src->height - y - 1);
memcpy (q, p, dest->n_channels);
}
}
@@ -448,12 +480,14 @@ gdk_pixbuf_rotate_simple (const GdkPixbuf *src,
if (!dest)
return NULL;
+ dest_pixels = gdk_pixbuf_get_pixels (dest);
+
for (y = 0; y < src->height; y++)
{
for (x = 0; x < src->width; x++)
{
- p = src->pixels + OFFSET (src, x, y);
- q = dest->pixels + OFFSET (dest, src->height - y - 1, x);
+ p = src_pixels + OFFSET (src, x, y);
+ q = dest_pixels + OFFSET (dest, src->height - y - 1, x);
memcpy (q, p, dest->n_channels);
}
}
@@ -485,8 +519,11 @@ GdkPixbuf *
gdk_pixbuf_flip (const GdkPixbuf *src,
gboolean horizontal)
{
+ const guint8 *src_pixels;
+ guint8 *dest_pixels;
GdkPixbuf *dest;
- guchar *p, *q;
+ const guchar *p;
+ guchar *q;
gint x, y;
dest = gdk_pixbuf_new (src->colorspace,
@@ -497,12 +534,15 @@ gdk_pixbuf_flip (const GdkPixbuf *src,
if (!dest)
return NULL;
+ dest_pixels = gdk_pixbuf_get_pixels (dest);
+ src_pixels = gdk_pixbuf_read_pixels (src);
+
if (!horizontal) /* flip vertical */
{
for (y = 0; y < dest->height; y++)
{
- p = src->pixels + OFFSET (src, 0, y);
- q = dest->pixels + OFFSET (dest, 0, dest->height - y - 1);
+ p = src_pixels + OFFSET (src, 0, y);
+ q = dest_pixels + OFFSET (dest, 0, dest->height - y - 1);
memcpy (q, p, dest->rowstride);
}
}
@@ -512,8 +552,8 @@ gdk_pixbuf_flip (const GdkPixbuf *src,
{
for (x = 0; x < dest->width; x++)
{
- p = src->pixels + OFFSET (src, x, y);
- q = dest->pixels + OFFSET (dest, dest->width - x - 1, y);
+ p = src_pixels + OFFSET (src, x, y);
+ q = dest_pixels + OFFSET (dest, dest->width - x - 1, y);
memcpy (q, p, dest->n_channels);
}
}
diff --git a/gdk-pixbuf/gdk-pixbuf-util.c b/gdk-pixbuf/gdk-pixbuf-util.c
index 6fbaa8e..63627e9 100644
--- a/gdk-pixbuf/gdk-pixbuf-util.c
+++ b/gdk-pixbuf/gdk-pixbuf-util.c
@@ -65,12 +65,16 @@ gdk_pixbuf_add_alpha (const GdkPixbuf *pixbuf,
{
GdkPixbuf *new_pixbuf;
int x, y;
+ const guint8 *src_pixels;
+ guint8 *ret_pixels;
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
g_return_val_if_fail (pixbuf->colorspace == GDK_COLORSPACE_RGB, NULL);
g_return_val_if_fail (pixbuf->n_channels == 3 || pixbuf->n_channels == 4, NULL);
g_return_val_if_fail (pixbuf->bits_per_sample == 8, NULL);
+ src_pixels = gdk_pixbuf_read_pixels (pixbuf);
+
if (pixbuf->has_alpha) {
new_pixbuf = gdk_pixbuf_copy (pixbuf);
if (!new_pixbuf)
@@ -85,12 +89,15 @@ gdk_pixbuf_add_alpha (const GdkPixbuf *pixbuf,
if (!new_pixbuf)
return NULL;
+ ret_pixels = gdk_pixbuf_get_pixels (new_pixbuf);
+
for (y = 0; y < pixbuf->height; y++) {
- guchar *src, *dest;
+ const guchar *src;
+ guchar *dest;
guchar tr, tg, tb;
- src = pixbuf->pixels + y * pixbuf->rowstride;
- dest = new_pixbuf->pixels + y * new_pixbuf->rowstride;
+ src = src_pixels + y * pixbuf->rowstride;
+ dest = ret_pixels + y * new_pixbuf->rowstride;
if (pixbuf->has_alpha) {
/* Just subst color, we already copied everything else */
@@ -208,9 +215,9 @@ gdk_pixbuf_saturate_and_pixelate(const GdkPixbuf *src,
} else {
int i, j, t;
int width, height, has_alpha, src_rowstride, dest_rowstride, bytes_per_pixel;
- guchar *src_line;
+ const guchar *src_line;
guchar *dest_line;
- guchar *src_pixel;
+ const guchar *src_pixel;
guchar *dest_pixel;
guchar intensity;
@@ -221,8 +228,8 @@ gdk_pixbuf_saturate_and_pixelate(const GdkPixbuf *src,
src_rowstride = gdk_pixbuf_get_rowstride (src);
dest_rowstride = gdk_pixbuf_get_rowstride (dest);
- src_line = gdk_pixbuf_get_pixels (src);
dest_line = gdk_pixbuf_get_pixels (dest);
+ src_line = gdk_pixbuf_read_pixels (src);
#define DARK_FACTOR 0.7
#define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
diff --git a/gdk-pixbuf/gdk-pixbuf.c b/gdk-pixbuf/gdk-pixbuf.c
index 6a907cc..1f5e5d4 100644
--- a/gdk-pixbuf/gdk-pixbuf.c
+++ b/gdk-pixbuf/gdk-pixbuf.c
@@ -88,18 +88,15 @@
* > gdk_pixbuf_unref() are deprecated in favour of g_object_ref()
* > and g_object_unref() resp.
*
- * Finalizing a pixbuf means to free its pixel
- * data and to free the #GdkPixbuf structure itself. Most of the
- * library functions that create #GdkPixbuf structures create the
- * pixel data by themselves and define the way it should be freed;
- * you do not need to worry about those. The only function that lets
- * you specify how to free the pixel data is
- * gdk_pixbuf_new_from_data(). Since you pass it a pre-allocated
- * pixel buffer, you must also specify a way to free that data. This
- * is done with a function of type #GdkPixbufDestroyNotify. When a
- * pixbuf created with gdk_pixbuf_new_from_data() is finalized, your
- * destroy notification function will be called, and it is its
- * responsibility to free the pixel array.
+ * Finalizing a pixbuf means to free its pixel data and to free the
+ * #GdkPixbuf structure itself. Most of the library functions that
+ * create #GdkPixbuf structures create the pixel data by themselves
+ * and define the way it should be freed; you do not need to worry
+ * about those.
+ *
+ * To provide preallocated pixel data, use
+ * gdk_pixbuf_new_from_bytes(). The gdk_pixbuf_new_from_data() API is
+ * an older variant that predates the existence of #GBytes.
*/
static void gdk_pixbuf_finalize (GObject *object);
@@ -123,7 +120,8 @@ enum
PROP_WIDTH,
PROP_HEIGHT,
PROP_ROWSTRIDE,
- PROP_PIXELS
+ PROP_PIXELS,
+ PROP_PIXEL_BYTES
};
static void gdk_pixbuf_icon_iface_init (GIconIface *iface);
@@ -241,6 +239,22 @@ gdk_pixbuf_class_init (GdkPixbufClass *klass)
P_("Pixels"),
P_("A pointer to the pixel data of the
pixbuf"),
PIXBUF_PARAM_FLAGS));
+
+ /**
+ * GdkPixbuf::pixel-bytes:
+ *
+ * If set, this pixbuf was created from read-only #GBytes.
+ * Replaces GdkPixbuf::pixels.
+ *
+ * Since: 2.32
+ */
+ g_object_class_install_property (object_class,
+ PROP_PIXEL_BYTES,
+ g_param_spec_boxed ("pixel-bytes",
+ P_("Pixel Bytes"),
+ P_("Readonly pixel data"),
+ G_TYPE_BYTES,
+ PIXBUF_PARAM_FLAGS));
}
static void
@@ -248,8 +262,10 @@ gdk_pixbuf_finalize (GObject *object)
{
GdkPixbuf *pixbuf = GDK_PIXBUF (object);
- if (pixbuf->destroy_fn)
+ if (pixbuf->pixels && pixbuf->destroy_fn)
(* pixbuf->destroy_fn) (pixbuf->pixels, pixbuf->destroy_fn_data);
+
+ g_clear_pointer (&pixbuf->bytes, g_bytes_unref);
G_OBJECT_CLASS (gdk_pixbuf_parent_class)->finalize (object);
}
@@ -475,7 +491,7 @@ gdk_pixbuf_copy (const GdkPixbuf *pixbuf)
if (!buf)
return NULL;
- memcpy (buf, pixbuf->pixels, size);
+ memcpy (buf, gdk_pixbuf_read_pixels (pixbuf), size);
return gdk_pixbuf_new_from_data (buf,
pixbuf->colorspace, pixbuf->has_alpha,
@@ -494,13 +510,15 @@ gdk_pixbuf_copy (const GdkPixbuf *pixbuf)
* @width: width of region in @src_pixbuf
* @height: height of region in @src_pixbuf
*
- * Creates a new pixbuf which represents a sub-region of
- * @src_pixbuf. The new pixbuf shares its pixels with the
- * original pixbuf, so writing to one affects both.
- * The new pixbuf holds a reference to @src_pixbuf, so
- * @src_pixbuf will not be finalized until the new pixbuf
- * is finalized.
- *
+ * Creates a new pixbuf which represents a sub-region of @src_pixbuf.
+ * The new pixbuf shares its pixels with the original pixbuf, so
+ * writing to one affects both. The new pixbuf holds a reference to
+ * @src_pixbuf, so @src_pixbuf will not be finalized until the new
+ * pixbuf is finalized.
+ *
+ * Note that if @src_pixbuf is read-only, this function will force it
+ * to be mutable.
+ *
* Return value: (transfer full): a new pixbuf
**/
GdkPixbuf*
@@ -516,7 +534,8 @@ gdk_pixbuf_new_subpixbuf (GdkPixbuf *src_pixbuf,
g_return_val_if_fail (GDK_IS_PIXBUF (src_pixbuf), NULL);
g_return_val_if_fail (src_x >= 0 && src_x + width <= src_pixbuf->width, NULL);
g_return_val_if_fail (src_y >= 0 && src_y + height <= src_pixbuf->height, NULL);
-
+
+ /* Note causes an implicit copy where src_pixbuf owns the data */
pixels = (gdk_pixbuf_get_pixels (src_pixbuf)
+ src_y * src_pixbuf->rowstride
+ src_x * src_pixbuf->n_channels);
@@ -617,13 +636,14 @@ gdk_pixbuf_get_bits_per_sample (const GdkPixbuf *pixbuf)
* Return value: (array): A pointer to the pixbuf's pixel data.
* Please see the section on [image data](image-data) for information
* about how the pixel data is stored in memory.
+ *
+ * This function will cause an implicit copy of the pixbuf data if the
+ * pixbuf was created from read-only data.
**/
guchar *
gdk_pixbuf_get_pixels (const GdkPixbuf *pixbuf)
{
- g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
-
- return pixbuf->pixels;
+ return gdk_pixbuf_get_pixels_with_length (pixbuf, NULL);
}
/**
@@ -637,6 +657,9 @@ gdk_pixbuf_get_pixels (const GdkPixbuf *pixbuf)
* pixel data. Please see the section on [image data](image-data)
* for information about how the pixel data is stored in memory.
*
+ * This function will cause an implicit copy of the pixbuf data if the
+ * pixbuf was created from read-only data.
+ *
* Rename to: gdk_pixbuf_get_pixels
*
* Since: 2.26
@@ -647,6 +670,13 @@ gdk_pixbuf_get_pixels_with_length (const GdkPixbuf *pixbuf,
{
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
+ if (pixbuf->bytes) {
+ GdkPixbuf *mut_pixbuf = (GdkPixbuf*)pixbuf;
+ gsize len;
+ mut_pixbuf->pixels = g_bytes_unref_to_data (pixbuf->bytes, &len);
+ mut_pixbuf->bytes = NULL;
+ }
+
if (length)
*length = gdk_pixbuf_get_byte_length (pixbuf);
@@ -654,6 +684,31 @@ gdk_pixbuf_get_pixels_with_length (const GdkPixbuf *pixbuf,
}
/**
+ * gdk_pixbuf_read_pixels:
+ * @pixbuf: A pixbuf
+ *
+ * Returns a read-only pointer to the raw pixel data; must not be
+ * modified. This function allows skipping the implicit copy that
+ * must be made if gdk_pixbuf_get_pixels() is called on a read-only
+ * pixbuf.
+ *
+ * Since: 2.32
+ */
+const guint8*
+gdk_pixbuf_read_pixels (const GdkPixbuf *pixbuf)
+{
+ g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
+
+ if (pixbuf->bytes) {
+ gsize len;
+ /* Ignore len; callers know the size via other variables */
+ return g_bytes_get_data (pixbuf->bytes, &len);
+ } else {
+ return pixbuf->pixels;
+ }
+}
+
+/**
* gdk_pixbuf_get_width:
* @pixbuf: A pixbuf.
*
@@ -762,7 +817,8 @@ gdk_pixbuf_fill (GdkPixbuf *pixbuf,
if (pixbuf->width == 0 || pixbuf->height == 0)
return;
- pixels = pixbuf->pixels;
+ /* Force an implicit copy */
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
r = (pixel & 0xff000000) >> 24;
g = (pixel & 0x00ff0000) >> 16;
@@ -931,6 +987,9 @@ gdk_pixbuf_set_property (GObject *object,
case PROP_PIXELS:
pixbuf->pixels = (guchar *) g_value_get_pointer (value);
break;
+ case PROP_PIXEL_BYTES:
+ pixbuf->bytes = g_value_dup_boxed (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -971,6 +1030,9 @@ gdk_pixbuf_get_property (GObject *object,
case PROP_PIXELS:
g_value_set_pointer (value, gdk_pixbuf_get_pixels (pixbuf));
break;
+ case PROP_PIXEL_BYTES:
+ g_value_set_boxed (value, pixbuf->bytes);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
diff --git a/gdk-pixbuf/gdk-pixbuf.symbols b/gdk-pixbuf/gdk-pixbuf.symbols
index 66ef07b..3f292e0 100644
--- a/gdk-pixbuf/gdk-pixbuf.symbols
+++ b/gdk-pixbuf/gdk-pixbuf.symbols
@@ -23,6 +23,7 @@ gdk_pixbuf_get_colorspace
gdk_pixbuf_get_has_alpha
gdk_pixbuf_get_height
gdk_pixbuf_get_n_channels
+gdk_pixbuf_read_pixels
gdk_pixbuf_get_pixels
gdk_pixbuf_get_pixels_with_length
gdk_pixbuf_get_byte_length
@@ -38,6 +39,7 @@ gdk_pixbuf_fill
#if IN_HEADER(GDK_PIXBUF_CORE_H)
#if IN_FILE(__GDK_PIXBUF_DATA_C__)
gdk_pixbuf_new_from_data
+gdk_pixbuf_new_from_bytes
#endif
#endif
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 80840c2..1c17877 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -32,6 +32,7 @@ test_programs = \
pixbuf-resource \
pixbuf-scale \
pixbuf-save \
+ pixbuf-readonly-to-mutable \
$(NULL)
dist_installed_test_data = \
@@ -78,6 +79,12 @@ pixbuf_save_SOURCES = \
test-common.h \
$(NULL)
+pixbuf_readonly_to_mutable_SOURCES = \
+ pixbuf-readonly-to-mutable.c \
+ test-common.c \
+ test-common.h \
+ $(NULL)
+
pixbuf_resource_SOURCES = \
pixbuf-resource.c \
test-common.c \
diff --git a/tests/pixbuf-readonly-to-mutable.c b/tests/pixbuf-readonly-to-mutable.c
new file mode 100644
index 0000000..8f81c87
--- /dev/null
+++ b/tests/pixbuf-readonly-to-mutable.c
@@ -0,0 +1,159 @@
+/* -*- Mode: C; c-basic-offset: 2; -*- */
+/* GdkPixbuf library - test loaders
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gdk-pixbuf/gdk-pixbuf.h"
+#include "test-common.h"
+#include <string.h>
+
+#ifdef G_OS_UNIX
+#include <sys/mman.h>
+#include <unistd.h>
+#endif
+
+#ifdef G_OS_UNIX
+typedef struct {
+ void *buf;
+ gsize len;
+} MappedBuf;
+
+static void
+destroy_buf_unmap (gpointer data)
+{
+ MappedBuf *buf = data;
+ int r;
+
+ r = munmap (buf->buf, buf->len);
+ g_assert_cmpint (r, ==, 0);
+ g_free (buf);
+}
+#endif
+
+static GdkPixbuf *
+get_readonly_pixbuf (void)
+{
+ GdkPixbuf *reference;
+ GdkPixbuf *result;
+ GBytes *bytes;
+ GError *error = NULL;
+
+ reference = gdk_pixbuf_new_from_file (g_test_get_filename (G_TEST_DIST, "test-image.png", NULL), &error);
+ g_assert_no_error (error);
+
+#ifdef G_OS_UNIX
+ {
+ MappedBuf *buf;
+ int pagesize;
+ int pages;
+ int r;
+ gsize pixlen;
+
+ pagesize = sysconf (_SC_PAGE_SIZE);
+ g_assert_cmpint (pagesize, >, 0);
+
+ pixlen = gdk_pixbuf_get_byte_length (reference);
+ pages = pixlen / pagesize + 1;
+
+ buf = g_new0 (MappedBuf, 1);
+ buf->len = pages * pagesize;
+ buf->buf = mmap (NULL, buf->len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ g_assert (buf->buf != NULL);
+
+ memcpy (buf->buf, gdk_pixbuf_get_pixels (reference), pixlen);
+
+ r = mprotect (buf->buf, buf->len, PROT_READ);
+ g_assert_cmpint (r, ==, 0);
+
+ bytes = g_bytes_new_with_free_func (buf->buf, buf->len, destroy_buf_unmap, buf);
+ }
+#else
+ bytes = g_bytes_new (gdk_pixbuf_get_pixels (reference), gdk_pixbuf_get_byte_length (reference));
+#endif
+
+ result = gdk_pixbuf_new_from_bytes (bytes,
+ gdk_pixbuf_get_colorspace (reference),
+ gdk_pixbuf_get_has_alpha (reference),
+ gdk_pixbuf_get_bits_per_sample (reference),
+ gdk_pixbuf_get_width (reference),
+ gdk_pixbuf_get_height (reference),
+ gdk_pixbuf_get_rowstride (reference));
+ g_object_unref (reference);
+ g_bytes_unref (bytes);
+
+ return result;
+}
+
+static void
+test_mutate_readonly (void)
+{
+ GdkPixbuf *src;
+ GdkPixbuf *dest;
+
+ if (!format_supported ("png"))
+ {
+ g_test_skip ("format not supported");
+ return;
+ }
+
+ src = get_readonly_pixbuf ();
+ gdk_pixbuf_scale (src, src,
+ 0, 0,
+ gdk_pixbuf_get_width (src) / 4,
+ gdk_pixbuf_get_height (src) / 4,
+ 0, 0, 0.5, 0.5,
+ GDK_INTERP_NEAREST);
+ g_object_unref (src);
+
+ src = get_readonly_pixbuf ();
+
+ dest = gdk_pixbuf_scale_simple (src,
+ gdk_pixbuf_get_width (src) / 4,
+ gdk_pixbuf_get_height (src) / 4,
+ GDK_INTERP_NEAREST);
+ g_object_unref (dest);
+
+ dest = gdk_pixbuf_composite_color_simple (src,
+ gdk_pixbuf_get_width (src) / 4,
+ gdk_pixbuf_get_height (src) / 4,
+ GDK_INTERP_NEAREST,
+ 128,
+ 8,
+ G_MAXUINT32,
+ G_MAXUINT32/2);
+ g_object_unref (dest);
+
+ dest = gdk_pixbuf_rotate_simple (src, 180);
+ g_object_unref (dest);
+
+ dest = gdk_pixbuf_flip (src, TRUE);
+ g_object_unref (dest);
+ dest = gdk_pixbuf_flip (src, FALSE);
+ g_object_unref (dest);
+
+ g_object_unref (src);
+}
+
+int
+main (int argc, char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/pixbuf/readonly/mutate", test_mutate_readonly);
+
+ return g_test_run ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]