[recipes/image-download: 9/10] Rework GrImage with new loading APIs



commit 32bb11baf9d7d4c1fe388e6115b6d359088fc722
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Mar 5 18:14:08 2017 -0500

    Rework GrImage with new loading APIs

 src/gr-image.c |  312 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/gr-image.h |   30 +++++-
 2 files changed, 335 insertions(+), 7 deletions(-)
---
diff --git a/src/gr-image.c b/src/gr-image.c
index dd61b2a..5310c72 100644
--- a/src/gr-image.c
+++ b/src/gr-image.c
@@ -20,12 +20,21 @@
 
 #include "config.h"
 
+#include <libsoup/soup.h>
+
 #include "gr-image.h"
+#include "gr-utils.h"
+
 
 struct _GrImage
 {
         GObject parent_instance;
         char *path;
+
+        SoupSession *session;
+        SoupMessage *thumbnail_message;
+        SoupMessage *image_message;
+        GList *pending;
 };
 
 G_DEFINE_TYPE (GrImage, gr_image, G_TYPE_OBJECT)
@@ -33,9 +42,20 @@ G_DEFINE_TYPE (GrImage, gr_image, G_TYPE_OBJECT)
 static void
 gr_image_finalize (GObject *object)
 {
-        GrImage *image = GR_IMAGE (object);
+        GrImage *ri = GR_IMAGE (object);
 
-        g_free (image->path);
+        if (ri->thumbnail_message)
+                soup_session_cancel_message (ri->session,
+                                             ri->thumbnail_message,
+                                             SOUP_STATUS_CANCELLED);
+        g_clear_object (&ri->thumbnail_message);
+        if (ri->image_message)
+                soup_session_cancel_message (ri->session,
+                                             ri->image_message,
+                                             SOUP_STATUS_CANCELLED);
+        g_clear_object (&ri->image_message);
+        g_clear_object (&ri->session);
+        g_free (ri->path);
 
         G_OBJECT_CLASS (gr_image_parent_class)->finalize (object);
 }
@@ -54,11 +74,13 @@ gr_image_init (GrImage *image)
 }
 
 GrImage *
-gr_image_new (const char *path)
+gr_image_new (SoupSession *session,
+              const char  *path)
 {
         GrImage *image;
 
         image = g_object_new (GR_TYPE_IMAGE, NULL);
+        image->session = g_object_ref (session);
         gr_image_set_path (image, path);
 
         return image;
@@ -83,3 +105,287 @@ gr_image_array_new (void)
 {
         return g_ptr_array_new_with_free_func (g_object_unref);
 }
+
+static GdkPixbuf *
+load_pixbuf (const char *path,
+             int         width,
+             int         height,
+             gboolean    fit)
+{
+        GdkPixbuf *pixbuf;
+
+        if (fit)
+                pixbuf = load_pixbuf_fit_size (path, width, height, FALSE);
+        else
+                pixbuf = load_pixbuf_fill_size (path, width, height);
+
+        return pixbuf;
+}
+
+typedef struct {
+        GtkImage *image;
+        int width;
+        int height;
+        gboolean fit;
+        GCancellable *cancellable;
+        GrImageCallback callback;
+        gpointer data;
+} TaskData;
+
+static void
+task_data_free (gpointer data)
+{
+        TaskData *td = data;
+
+        g_clear_object (&td->cancellable);
+
+        g_free (td);
+}
+
+static char *
+get_image_url (const char *path)
+{
+        g_autofree char *basename = NULL;
+
+        basename = g_path_get_basename (path);
+        return g_strconcat ("http://mclasen.fedorapeople.org/recipes/images/";, basename, NULL);
+}
+
+static char *
+get_thumbnail_url (const char *path)
+{
+        g_autofree char *basename = NULL;
+
+        basename = g_path_get_basename (path);
+        return g_strconcat ("http://mclasen.fedorapeople.org/recipes/thumbnails/";, basename, NULL);
+}
+
+static char *
+get_image_cache_path (const char *path)
+{
+        char *filename;
+        g_autofree char *cache_dir = NULL;
+        g_autofree char *basename = NULL;
+
+        basename = g_path_get_basename (path);
+        filename = g_build_filename (g_get_user_cache_dir (), PACKAGE_NAME, "images", basename, NULL);
+        cache_dir = g_path_get_dirname (filename);
+        g_mkdir_with_parents (cache_dir, 0755);
+
+        return filename;
+}
+
+static char *
+get_thumbnail_cache_path (const char *path)
+{
+        char *filename;
+        g_autofree char *cache_dir = NULL;
+        g_autofree char *basename = NULL;
+
+        basename = g_path_get_basename (path);
+        filename = g_build_filename (g_get_user_cache_dir (), PACKAGE_NAME, "thumbnails", basename, NULL);
+        cache_dir = g_path_get_dirname (filename);
+        g_mkdir_with_parents (cache_dir, 0755);
+
+        return filename;
+}
+
+static void
+set_image (SoupSession *session,
+           SoupMessage *msg,
+           gpointer     data)
+{
+        GrImage *ri = data;
+        g_autofree char *cache_path = NULL;
+        GdkPixbuf *pixbuf;
+        GList *l;
+
+        l = ri->pending;
+        while (l) {
+                GList *next = l->next;
+                TaskData *td = l->data;
+
+                if (g_cancellable_is_cancelled (td->cancellable)) {
+                        ri->pending = g_list_remove (ri->pending, td);
+                        task_data_free (td);
+                }
+                l = next;
+        }
+
+        if (msg->status_code == SOUP_STATUS_CANCELLED || ri->session == NULL) {
+                g_debug ("Message cancelled");
+                goto error;
+        }
+
+        if (msg->status_code != SOUP_STATUS_OK) {
+                g_debug ("Status not ok: %d", msg->status_code);
+                goto out;
+        }
+
+        if (msg == ri->thumbnail_message) {
+                if (ri->image_message == NULL) // already got the image, ignore the thumbnail
+                        goto out;
+                cache_path = get_thumbnail_cache_path (ri->path);
+        }
+        else {
+                cache_path = get_image_cache_path (ri->path);
+        }
+
+        g_debug ("Saving image to %s", cache_path);
+        if (!g_file_set_contents (cache_path, msg->response_body->data, msg->response_body->length, NULL)) {
+                g_debug ("Saving image to %s failed", cache_path);
+                goto out;
+        }
+
+        g_debug ("Loading image for %s", ri->path);
+
+        for (l = ri->pending; l; l = l->next) {
+                TaskData *td = l->data;
+
+                if (msg == ri->thumbnail_message) {
+                        g_autoptr(GdkPixbuf) tmp = NULL;
+
+                        tmp = load_pixbuf (cache_path, 120, 120 * td->height / td->width, td->fit);
+
+                        pixbuf = gdk_pixbuf_scale_simple (tmp, td->width, td->height, GDK_INTERP_BILINEAR);
+                        pixbuf_blur (pixbuf, 5, 3);
+                }
+                else {
+                        pixbuf = load_pixbuf (cache_path, td->width, td->height, td->fit);
+                }
+                if (pixbuf)
+                        td->callback (ri, pixbuf, td->data);
+                else {
+                        g_message ("Failed to load %s", ri->path);
+                        break;
+                }
+        }
+
+out:
+        if (msg == ri->thumbnail_message)
+                g_clear_object (&ri->thumbnail_message);
+        else
+                g_clear_object (&ri->image_message);
+
+        if (ri->thumbnail_message || ri->image_message)
+                return;
+
+error:
+        g_list_free_full (ri->pending, task_data_free);
+        ri->pending = NULL;
+}
+
+void
+gr_image_load (GrImage         *ri,
+               int              width,
+               int              height,
+               gboolean         fit,
+               GCancellable    *cancellable,
+               GrImageCallback  callback,
+               gpointer         data)
+{
+        TaskData *td;
+        g_autofree char *cache_path = NULL;
+        g_autofree char *thumbnail_cache_path = NULL;
+        g_autoptr(GdkPixbuf) pixbuf = NULL;
+
+        if (ri->path[0] == '/') {
+                pixbuf = load_pixbuf (ri->path, width, height, fit);
+                if (pixbuf) {
+                        g_debug ("Use local image for %s", ri->path);
+                        callback (ri, pixbuf, data);
+                        return;
+                }
+        }
+
+        cache_path = get_image_cache_path (ri->path);
+        pixbuf = load_pixbuf (cache_path, width, height, fit);
+
+        if (pixbuf) {
+                g_debug ("Use cached image for %s", ri->path);
+                callback (ri, pixbuf, data);
+                return;
+        }
+
+        td = g_new0 (TaskData, 1);
+        td->width = width;
+        td->height = height;
+        td->fit = fit;
+        td->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+        td->callback = callback;
+        td->data = data;
+
+        ri->pending = g_list_prepend (ri->pending, td);
+
+        thumbnail_cache_path = get_thumbnail_cache_path (ri->path);
+        pixbuf = load_pixbuf (thumbnail_cache_path, 120, 120 * height / width, fit);
+        if (pixbuf) {
+                g_autoptr(GdkPixbuf) blurred = NULL;
+                g_debug ("Use cached thumbnail for %s", ri->path);
+
+                blurred = gdk_pixbuf_scale_simple (pixbuf, width, height, GDK_INTERP_BILINEAR);
+                pixbuf_blur (blurred, 5, 3);
+                callback (ri, blurred, data);
+        }
+        else if (width > 240 && ri->thumbnail_message == NULL) {
+                g_autofree char *url = NULL;
+                g_autoptr(SoupURI) base_uri = NULL;
+
+                url = get_thumbnail_url (ri->path);
+                base_uri = soup_uri_new (url);
+                ri->thumbnail_message = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri);
+                g_debug ("Load thumbnail for %s from %s", ri->path, url);
+                soup_session_queue_message (ri->session, g_object_ref (ri->thumbnail_message), set_image, 
ri);
+        }
+
+        if (ri->image_message == NULL) {
+                g_autofree char *url = NULL;
+                g_autoptr(SoupURI) base_uri = NULL;
+
+                url = get_image_url (ri->path);
+                base_uri = soup_uri_new (url);
+                ri->image_message = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri);
+                g_debug ("Load image for %s from %s", ri->path, url);
+                soup_session_queue_message (ri->session, g_object_ref (ri->image_message), set_image, ri);
+        }
+}
+
+void
+gr_image_set_pixbuf (GrImage   *ri,
+                     GdkPixbuf *pixbuf,
+                     gpointer   data)
+{
+        gtk_image_set_from_pixbuf (GTK_IMAGE (data), pixbuf);
+}
+
+GdkPixbuf  *
+gr_image_load_sync (GrImage   *ri,
+                    int        width,
+                    int        height,
+                    gboolean   fit)
+{
+        GdkPixbuf *pixbuf;
+
+        if (ri->path[0] == '/') {
+                pixbuf = load_pixbuf (ri->path, width, height, fit);
+        }
+        else {
+                g_autofree char *cache_path = NULL;
+
+                cache_path = get_image_cache_path (ri->path);
+                pixbuf = load_pixbuf (cache_path, width, height, fit);
+        }
+
+        if (!pixbuf) {
+                g_autoptr(GtkIconInfo) info = NULL;
+
+                info = gtk_icon_theme_lookup_icon (gtk_icon_theme_get_default (),
+                                                   "org.gnome.Recipes",
+                                                    256,
+                                                    GTK_ICON_LOOKUP_FORCE_SIZE);
+                pixbuf = load_pixbuf (gtk_icon_info_get_filename (info), width, height, fit);
+
+        }
+
+        return pixbuf;
+}
diff --git a/src/gr-image.h b/src/gr-image.h
index f438bef..eec924b 100644
--- a/src/gr-image.h
+++ b/src/gr-image.h
@@ -21,6 +21,7 @@
 #pragma once
 
 #include <gtk/gtk.h>
+#include <libsoup/soup.h>
 
 G_BEGIN_DECLS
 
@@ -28,10 +29,31 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GrImage, gr_image, GR, IMAGE, GObject)
 
-GrImage    *gr_image_new      (const char *path);
-void        gr_image_set_path (GrImage    *image,
-                               const char *path);
-const char *gr_image_get_path (GrImage    *image);
+GrImage    *gr_image_new         (SoupSession       *session,
+                                  const char        *path);
+void        gr_image_set_path    (GrImage           *image,
+                                  const char        *path);
+const char *gr_image_get_path    (GrImage           *image);
+GdkPixbuf  *gr_image_load_sync   (GrImage           *image,
+                                  int                 width,
+                                  int                 height,
+                                  gboolean            fit);
+
+typedef void (*GrImageCallback) (GrImage   *ri,
+                                 GdkPixbuf *pixbuf,
+                                 gpointer   data);
+
+void        gr_image_load        (GrImage            *ri,
+                                  int                 width,
+                                  int                 height,
+                                  gboolean            fit,
+                                  GCancellable       *cancellable,
+                                  GrImageCallback     callback,
+                                  gpointer            data);
+
+void        gr_image_set_pixbuf  (GrImage   *ri,
+                                  GdkPixbuf *pixbuf,
+                                  gpointer   data);
 
 GPtrArray *gr_image_array_new (void);
 


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