summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Lortie <desrt@moonpix.lan>2013-02-26 16:01:32 -0500
committerRyan Lortie <desrt@desrt.ca>2013-04-21 16:39:14 -0400
commitaa0f928631d7c3877633337960c10963fb6eaecd (patch)
tree390a033e7d659c04ff6e3ebc2b3b538a06f1e51d
parentcb0d6bef7f241d21f6efd6af377cab3a33b7de09 (diff)
downloadgdk-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.ac2
-rw-r--r--gdk-pixbuf/gdk-pixbuf.c104
-rw-r--r--tests/Makefile.am6
-rw-r--r--tests/pixbuf-icon-serialize.c60
-rw-r--r--tests/test-image.pngbin0 -> 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
new file mode 100644
index 000000000..6974e57ac
--- /dev/null
+++ b/tests/test-image.png
Binary files differ