[libhandy] avatar: Add hdy_avatar_draw_to_pixbuf_async



commit 7497ca34634adcea3b4a4c38fde0672b57a9bd1d
Author: Julian Sparber <julian sparber net>
Date:   Wed Feb 10 19:40:11 2021 +0100

    avatar: Add hdy_avatar_draw_to_pixbuf_async
    
    This function allows to draw an avatar to a pixbuf while loading the
    custom avatar async, if there isn't any custom avatar the async call
    will return quickly.

 debian/libhandy-1-0.symbols |   2 +
 src/hdy-avatar.c            | 138 ++++++++++++++++++++++++++++++++++++++++++++
 src/hdy-avatar.h            |  10 ++++
 tests/test-avatar.c         |  28 +++++++++
 4 files changed, 178 insertions(+)
---
diff --git a/debian/libhandy-1-0.symbols b/debian/libhandy-1-0.symbols
index c4f095ad..ad355761 100644
--- a/debian/libhandy-1-0.symbols
+++ b/debian/libhandy-1-0.symbols
@@ -19,6 +19,8 @@ libhandy-1.so.0 libhandy-1-0 #MINVER#
  hdy_application_window_get_type@LIBHANDY_1_0 0.80.0
  hdy_application_window_new@LIBHANDY_1_0 0.80.0
  hdy_avatar_draw_to_pixbuf@LIBHANDY_1_0 1.1.0
+ hdy_avatar_draw_to_pixbuf_async@LIBHANDY_1_0 1.1.0
+ hdy_avatar_draw_to_pixbuf_finish@LIBHANDY_1_0 1.1.0
  hdy_avatar_get_icon_name@LIBHANDY_1_0 0.85.0
  hdy_avatar_get_show_initials@LIBHANDY_1_0 0.80.0
  hdy_avatar_get_size@LIBHANDY_1_0 0.80.0
diff --git a/src/hdy-avatar.c b/src/hdy-avatar.c
index bacbf741..8e78172a 100644
--- a/src/hdy-avatar.c
+++ b/src/hdy-avatar.c
@@ -73,6 +73,17 @@ enum {
 };
 static GParamSpec *props[PROP_LAST_PROP];
 
+typedef struct {
+  gint size;
+  gint scale_factor;
+} SizeData;
+
+static void
+size_data_free (SizeData *data)
+{
+  g_slice_free (SizeData, data);
+}
+
 static void
 load_icon_async (GLoadableIcon       *icon,
                  gint                 size,
@@ -327,6 +338,27 @@ load_from_gicon_async_for_display_cb (GLoadableIcon *icon,
   }
 }
 
+static void
+load_from_gicon_async_for_export_cb (GLoadableIcon *icon,
+                                     GAsyncResult  *res,
+                                     gpointer      *user_data)
+{
+  GTask *task = G_TASK (user_data);
+  g_autoptr (GError) error = NULL;
+  g_autoptr (GdkPixbuf) pixbuf = NULL;
+
+  pixbuf = load_from_gicon_async_finish (res, &error);
+
+  if (!g_error_matches (error, HDY_AVATAR_ICON_ERROR, HDY_AVATAR_ICON_ERROR_EMPTY) &&
+      !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+    g_warning ("Failed to load icon: %s", error->message);
+  }
+
+  g_task_return_pointer (task,
+                         g_steal_pointer (&pixbuf),
+                         g_object_unref);
+}
+
 static void
 load_icon_async (GLoadableIcon       *icon,
                  gint                 size,
@@ -1224,6 +1256,112 @@ hdy_avatar_draw_to_pixbuf (HdyAvatar *self,
                                       bounds.height * scale_factor);
 }
 
+/**
+ * hdy_avatar_draw_to_pixbuf_async:
+ * @self: a #HdyAvatar
+ * @size: The size of the pixbuf
+ * @scale_factor: The scale factor
+ * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the avatar is generated
+ * @user_data: (closure): the data to pass to callback function
+
+ * Renders asynchronously @self into a pixbuf at @size and @scale_factor.
+ * This can be used to export the fallback avatar.
+ *
+ * Since: 1.1
+ */
+void
+hdy_avatar_draw_to_pixbuf_async (HdyAvatar           *self,
+                                 gint                 size,
+                                 gint                 scale_factor,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
+{
+  GTask *task = NULL;
+  gint scaled_size = size * scale_factor;
+  SizeData *data;
+
+  g_return_if_fail (HDY_IS_AVATAR (self));
+  g_return_if_fail (size > 0);
+  g_return_if_fail (scale_factor > 0);
+
+  data = g_slice_new (SizeData);
+  data->size = size;
+  data->scale_factor = scale_factor;
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, hdy_avatar_draw_to_pixbuf_async);
+  g_task_set_task_data (task, data, (GDestroyNotify) size_data_free);
+
+  if (get_icon (self) &&
+      (!self->round_image ||
+       gdk_pixbuf_get_width (self->round_image) != scaled_size ||
+       is_scaled (self->round_image)))
+    load_icon_async (get_icon (self),
+                     scaled_size,
+                     cancellable,
+                     (GAsyncReadyCallback) load_from_gicon_async_for_export_cb,
+                     task);
+  else
+    g_task_return_pointer (task, NULL, NULL);
+}
+
+/**
+ * hdy_avatar_draw_to_pixbuf_finish:
+ * @self: a #HdyAvatar
+ * @async_result: a #GAsyncResult
+ *
+ * Finishes an asynchronous draw of an avatar to a pixbuf.
+ *
+ * Returns: (transfer full): a #GdkPixbuf
+ *
+ * Since: 1.1
+ */
+GdkPixbuf *
+hdy_avatar_draw_to_pixbuf_finish (HdyAvatar    *self,
+                                  GAsyncResult *async_result)
+{
+  GTask *task;
+  g_autoptr (GdkPixbuf) pixbuf_from_icon = NULL;
+  g_autoptr (GdkPixbuf) custom_image = NULL;
+  g_autoptr (cairo_surface_t) surface = NULL;
+  g_autoptr (cairo_t) cr = NULL;
+  SizeData *data;
+  GtkStyleContext *context;
+  GtkAllocation bounds;
+
+  g_return_val_if_fail (G_IS_TASK (async_result), NULL);
+
+  task = G_TASK (async_result);
+
+  g_warn_if_fail (g_task_get_source_tag (task) == hdy_avatar_draw_to_pixbuf_async);
+
+  data = g_task_get_task_data (task);
+
+  context = gtk_widget_get_style_context (GTK_WIDGET (self));
+  gtk_render_background_get_clip (context, 0, 0, data->size, data->size, &bounds);
+
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                        bounds.width * data->scale_factor,
+                                        bounds.height * data->scale_factor);
+  cairo_surface_set_device_scale (surface, data->scale_factor, data->scale_factor);
+  cr = cairo_create (surface);
+
+  cairo_translate (cr, -bounds.x, -bounds.y);
+
+  pixbuf_from_icon = g_task_propagate_pointer (task, NULL);
+  custom_image = update_custom_image (get_icon (self),
+                                      pixbuf_from_icon,
+                                      NULL,
+                                      data->size * data->scale_factor);
+  draw_for_size (self, cr, custom_image, data->size, data->size, data->scale_factor);
+
+  return gdk_pixbuf_get_from_surface (surface, 0, 0,
+                                      bounds.width * data->scale_factor,
+                                      bounds.height * data->scale_factor);
+}
+
 /**
  * hdy_avatar_get_loadable_icon:
  * @self: a #HdyAvatar
diff --git a/src/hdy-avatar.h b/src/hdy-avatar.h
index 2d3d7cbe..b2c83489 100644
--- a/src/hdy-avatar.h
+++ b/src/hdy-avatar.h
@@ -74,6 +74,16 @@ GdkPixbuf   *hdy_avatar_draw_to_pixbuf      (HdyAvatar              *self,
                                              gint                    size,
                                              gint                    scale_factor);
 HDY_AVAILABLE_IN_1_1
+void hdy_avatar_draw_to_pixbuf_async        (HdyAvatar              *self,
+                                             gint                    size,
+                                             gint                    scale_factor,
+                                             GCancellable           *cancellable,
+                                             GAsyncReadyCallback     callback,
+                                             gpointer                user_data);
+HDY_AVAILABLE_IN_1_1
+GdkPixbuf *hdy_avatar_draw_to_pixbuf_finish (HdyAvatar              *self,
+                                             GAsyncResult           *async_result);
+HDY_AVAILABLE_IN_1_1
 GLoadableIcon *hdy_avatar_get_loadable_icon (HdyAvatar              *self);
 HDY_AVAILABLE_IN_1_1
 void           hdy_avatar_set_loadable_icon (HdyAvatar              *self,
diff --git a/tests/test-avatar.c b/tests/test-avatar.c
index b8475e03..edba33e9 100644
--- a/tests/test-avatar.c
+++ b/tests/test-avatar.c
@@ -215,6 +215,33 @@ test_hdy_avatar_draw_to_pixbuf (void)
   g_assert_cmpint (gdk_pixbuf_get_height (pixbuf), ==, TEST_SIZE * 2);
 }
 
+static void
+draw_to_pixbuf_async (HdyAvatar    *avatar,
+                      GAsyncResult *res,
+                      gpointer      user_data)
+{
+  g_autoptr (GdkPixbuf) pixbuf = hdy_avatar_draw_to_pixbuf_finish (avatar, res);
+
+  g_assert_cmpint (gdk_pixbuf_get_width (pixbuf), ==, TEST_SIZE * 2);
+  g_assert_cmpint (gdk_pixbuf_get_height (pixbuf), ==, TEST_SIZE * 2);
+  g_object_unref (avatar);
+}
+
+static void
+test_hdy_avatar_draw_to_pixbuf_async (void)
+{
+  HdyAvatar *avatar = NULL;
+
+  avatar = g_object_ref_sink (HDY_AVATAR (hdy_avatar_new (TEST_SIZE, NULL, TRUE)));
+
+  hdy_avatar_draw_to_pixbuf_async (avatar,
+                                   TEST_SIZE * 2,
+                                   1,
+                                   NULL,
+                                   (GAsyncReadyCallback) draw_to_pixbuf_async,
+                                   NULL);
+}
+
 static void
 test_hdy_avatar_loadable_icon (void)
 {
@@ -251,6 +278,7 @@ main (gint argc,
   g_test_add_func ("/Handy/Avatar/text", test_hdy_avatar_text);
   g_test_add_func ("/Handy/Avatar/size", test_hdy_avatar_size);
   g_test_add_func ("/Handy/Avatar/draw_to_pixbuf", test_hdy_avatar_draw_to_pixbuf);
+  g_test_add_func ("/Handy/Avatar/draw_to_pixbuf_async", test_hdy_avatar_draw_to_pixbuf_async);
   g_test_add_func ("/Handy/Avatar/loadable_icon", test_hdy_avatar_loadable_icon);
 
   return g_test_run ();


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