[gdk-pixbuf] Fix GIcon implementation



commit aa0f928631d7c3877633337960c10963fb6eaecd
Author: Ryan Lortie <desrt moonpix lan>
Date:   Tue Feb 26 16:01:32 2013 -0500

    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

 configure.ac                  |   2 +-
 gdk-pixbuf/gdk-pixbuf.c       | 104 +++++++++++++++++++++++++++++++++++++++++-
 tests/Makefile.am             |   6 ++-
 tests/pixbuf-icon-serialize.c |  60 ++++++++++++++++++++++++
 tests/test-image.png          | Bin 0 -> 4314 bytes
 5 files changed, 168 insertions(+), 4 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 5efc1b6..204e233 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 3eaa91b..0e13f27 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 7129c42..de12a12 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 0000000..7d35a20
--- /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 0000000..6974e57
Binary files /dev/null and b/tests/test-image.png differ


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