diff options
author | Ryan Lortie <desrt@moonpix.lan> | 2013-02-26 16:01:32 -0500 |
---|---|---|
committer | Ryan Lortie <desrt@desrt.ca> | 2013-04-21 16:39:14 -0400 |
commit | aa0f928631d7c3877633337960c10963fb6eaecd (patch) | |
tree | 390a033e7d659c04ff6e3ebc2b3b538a06f1e51d | |
parent | cb0d6bef7f241d21f6efd6af377cab3a33b7de09 (diff) | |
download | gdk-pixbuf-aa0f928631d7c3877633337960c10963fb6eaecd.tar.gz |
Fix GIcon implementation
The "new rules" for GIcon say that we must support serialisation via
g_icon_serialize() and loadability via GLoadableIcon, so implement both
of those.
Serialise GdkPixbuf by emitting a GVariant that will result in a
png-encoded GBytesIcon when deserialized. The GLoadableIcon interface
is similar: we return a stream that will read out as a png.
Test the serialisation by round-tripping an image through this process
and ensuring that it is pixel-perfect.
https://bugzilla.gnome.org/show_bug.cgi?id=688820
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | gdk-pixbuf/gdk-pixbuf.c | 104 | ||||
-rw-r--r-- | tests/Makefile.am | 6 | ||||
-rw-r--r-- | tests/pixbuf-icon-serialize.c | 60 | ||||
-rw-r--r-- | tests/test-image.png | bin | 0 -> 4314 bytes |
5 files changed, 168 insertions, 4 deletions
diff --git a/configure.ac b/configure.ac index 5efc1b6fd..204e23373 100644 --- a/configure.ac +++ b/configure.ac @@ -26,7 +26,7 @@ m4_define([gdk_pixbuf_binary_version], [2.10.0]) # required versions of other packages -m4_define([glib_required_version], [2.34.0]) +m4_define([glib_required_version], [2.37.0]) AC_INIT([gdk-pixbuf], [gdk_pixbuf_version], [http://bugzilla.gnome.org/enter_bug.cgi?product=gdk-pixbuf], diff --git a/gdk-pixbuf/gdk-pixbuf.c b/gdk-pixbuf/gdk-pixbuf.c index 3eaa91b57..0e13f27a2 100644 --- a/gdk-pixbuf/gdk-pixbuf.c +++ b/gdk-pixbuf/gdk-pixbuf.c @@ -135,10 +135,11 @@ enum }; static void gdk_pixbuf_icon_iface_init (GIconIface *iface); +static void gdk_pixbuf_loadable_icon_iface_init (GLoadableIconIface *iface); G_DEFINE_TYPE_WITH_CODE (GdkPixbuf, gdk_pixbuf, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_ICON, - gdk_pixbuf_icon_iface_init)) + G_IMPLEMENT_INTERFACE (G_TYPE_ICON, gdk_pixbuf_icon_iface_init) + G_IMPLEMENT_INTERFACE (G_TYPE_LOADABLE_ICON, gdk_pixbuf_loadable_icon_iface_init)) static void gdk_pixbuf_init (GdkPixbuf *pixbuf) @@ -292,11 +293,110 @@ gdk_pixbuf_unref (GdkPixbuf *pixbuf) g_object_unref (pixbuf); } +static GBytes * +gdk_pixbuf_make_bytes (GdkPixbuf *pixbuf, + GError **error) +{ + gchar *buffer; + gsize size; + + if (!gdk_pixbuf_save_to_buffer (pixbuf, &buffer, &size, "png", error, NULL)) + return NULL; + + return g_bytes_new_take (buffer, size); +} + +static GVariant * +gdk_pixbuf_serialize (GIcon *icon) +{ + GError *error = NULL; + GVariant *result; + GBytes *bytes; + + bytes = gdk_pixbuf_make_bytes (GDK_PIXBUF (icon), &error); + if (!bytes) + { + g_critical ("Unable to serialise GdkPixbuf to png (via g_icon_serialize()): %s", error->message); + g_error_free (error); + return NULL; + } + result = g_variant_new_from_bytes (G_VARIANT_TYPE_BYTESTRING, bytes, TRUE); + g_bytes_unref (bytes); + + return g_variant_new ("(sv)", "bytes", result); +} + +static GInputStream * +gdk_pixbuf_load (GLoadableIcon *icon, + int size, + char **type, + GCancellable *cancellable, + GError **error) +{ + GInputStream *stream; + GBytes *bytes; + + bytes = gdk_pixbuf_make_bytes (GDK_PIXBUF (icon), error); + if (!bytes) + return NULL; + + stream = g_memory_input_stream_new_from_bytes (bytes); + g_bytes_unref (bytes); + + if (type) + *type = g_strdup ("image/png"); + + return stream; +} + +static void +gdk_pixbuf_load_async (GLoadableIcon *icon, + int size, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (icon, cancellable, callback, user_data); + g_task_return_pointer (task, icon, NULL); + g_object_unref (task); +} + +static GInputStream * +gdk_pixbuf_load_finish (GLoadableIcon *icon, + GAsyncResult *res, + char **type, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (res, icon), NULL); + + if (!g_task_propagate_pointer (G_TASK (res), error)) + return NULL; + + return gdk_pixbuf_load (icon, 0, type, NULL, error); +} + +static void +gdk_pixbuf_loadable_icon_iface_init (GLoadableIconIface *iface) +{ + iface->load = gdk_pixbuf_load; + + /* In theory encoding a png could be time-consuming but we're talking + * about icons here, so assume it's probably going to be OK and handle + * the async variant of the call in-thread instead of having the + * default implementation dispatch it to a worker. + */ + iface->load_async = gdk_pixbuf_load_async; + iface->load_finish = gdk_pixbuf_load_finish; +} + static void gdk_pixbuf_icon_iface_init (GIconIface *iface) { iface->hash = (guint (*) (GIcon *)) g_direct_hash; iface->equal = (gboolean (*) (GIcon *, GIcon *)) g_direct_equal; + iface->serialize = gdk_pixbuf_serialize; } /* Used as the destroy notification function for gdk_pixbuf_new() */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 7129c4217..de12a12e3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -6,6 +6,8 @@ INCLUDES = \ $(GDK_PIXBUF_DEBUG_FLAGS) \ $(GDK_PIXBUF_DEP_CFLAGS) +AM_CFLAGS = '-DABS_SRCDIR="$(abs_srcdir)"' + DEPS = \ $(top_builddir)/gdk-pixbuf/libgdk_pixbuf-$(GDK_PIXBUF_API_VERSION).la @@ -18,13 +20,15 @@ noinst_PROGRAMS = \ pixbuf-lowmem \ pixbuf-randomly-modified \ pixbuf-random \ - pixbuf-threads + pixbuf-threads \ + pixbuf-icon-serialize pixbuf_read_LDADD = $(LDADDS) pixbuf_lowmem_LDADD = $(LDADDS) pixbuf_randomly_modified_LDADD = $(LDADDS) pixbuf_random_LDADD = $(LDADDS) pixbuf_threads_LDADD = $(LDADDS) $(GLIB_LIBS) +pixbuf_icon_serialize_LDADD = $(LDADDS) $(GLIB_LIBS) -include $(top_srcdir)/git.mk diff --git a/tests/pixbuf-icon-serialize.c b/tests/pixbuf-icon-serialize.c new file mode 100644 index 000000000..7d35a20af --- /dev/null +++ b/tests/pixbuf-icon-serialize.c @@ -0,0 +1,60 @@ +#include "config.h" +#include "gdk-pixbuf/gdk-pixbuf.h" +#include <string.h> +#include <glib.h> + +static void +test_serialize (void) +{ + GError *error = NULL; + GdkPixbuf *pixbuf; + GdkPixbuf *pixbuf2; + GVariant *data; + GIcon *icon; + GInputStream *stream; + + pixbuf = gdk_pixbuf_new_from_file (ABS_SRCDIR "/test-image.png", &error); + g_assert_no_error (error); + g_assert (pixbuf != NULL); + + /* turn it into a GVariant */ + data = g_icon_serialize (G_ICON (pixbuf)); + + /* back to a GIcon, but this will be a GBytesIcon, not GdkPixbuf */ + icon = g_icon_deserialize (data); + g_assert (G_IS_BYTES_ICON (icon)); + + /* but since that is a GLoadableIcon, we can load it again */ + stream = g_loadable_icon_load (G_LOADABLE_ICON (icon), 0, NULL, NULL, &error); + g_assert_no_error (error); + pixbuf2 = gdk_pixbuf_new_from_stream (stream, NULL, &error); + g_assert_no_error (error); + + /* make sure that the pixels are the same. + * our _serialize() uses png, so this should be perfect. + */ + { + guchar *pixels_a, *pixels_b; + guint len_a, len_b; + pixels_a = gdk_pixbuf_get_pixels_with_length (pixbuf, &len_a); + pixels_b = gdk_pixbuf_get_pixels_with_length (pixbuf2, &len_b); + g_assert (len_a == len_b); + g_assert (memcmp (pixels_a, pixels_b, len_a) == 0); + } + + g_object_unref (pixbuf2); + g_object_unref (pixbuf); + g_object_unref (stream); + g_variant_unref (data); + +} + +int +main (int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/pixbuf/icon/serialize", test_serialize); + + return g_test_run (); +} diff --git a/tests/test-image.png b/tests/test-image.png Binary files differnew file mode 100644 index 000000000..6974e57ac --- /dev/null +++ b/tests/test-image.png |