[recipes/image-download: 9/10] Rework GrImage with new loading APIs
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [recipes/image-download: 9/10] Rework GrImage with new loading APIs
- Date: Mon, 6 Mar 2017 00:34:26 +0000 (UTC)
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]