summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Bergli Heier <snakebite@jvnv.net>2010-01-06 19:23:09 +0100
committerJon Bergli Heier <snakebite@jvnv.net>2010-01-06 19:23:09 +0100
commit90df6aec51309372260b02b3a9bd518d35da4f40 (patch)
tree55010b820ccfa3dadb9b59637db96224203830b4
parentb0f0ee3842bff277048316f9c006dd16188de6c4 (diff)
Implemented threaded preloading of images.
The number of preloads is currently hardcoded in preload.c, an option to change this will be added shortly.
-rw-r--r--preload.c281
-rw-r--r--preload.h22
-rw-r--r--wallpapers.c40
-rw-r--r--wallpapers.h2
-rw-r--r--window_main.c100
5 files changed, 402 insertions, 43 deletions
diff --git a/preload.c b/preload.c
new file mode 100644
index 0000000..281bdf6
--- /dev/null
+++ b/preload.c
@@ -0,0 +1,281 @@
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "preload.h"
+#include "wallpapers.h"
+
+guint preload_max = 6;
+
+static gboolean preload_thread_exit = FALSE;
+static GHashTable *hashtable = NULL;
+static GMutex *preload_hashtable_mutex = NULL, *preload_thread_mutex = NULL;
+static GThread *preload_thread = NULL;
+
+static gpointer preload_thread_func(gpointer);
+
+struct preload_thread_data_t {
+ GArray *array;
+ gint win_width, win_height;
+};
+
+static void preload_hash_table_free(gpointer data) {
+ struct preload_hash_table_value_t *pt;
+
+ pt = data;
+
+ g_object_unref(pt->pb);
+
+ g_free(data);
+}
+
+void preload_init() {
+ preload_hashtable_mutex = g_mutex_new();
+ preload_thread_mutex = g_mutex_new();
+
+ hashtable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, preload_hash_table_free);
+}
+
+void preload_free() {
+ if(preload_thread) {
+ preload_thread_exit = TRUE;
+ g_thread_join(preload_thread);
+ }
+
+ g_mutex_free(preload_hashtable_mutex);
+ g_mutex_free(preload_thread_mutex);
+
+ g_hash_table_destroy(hashtable);
+ preload_hashtable_mutex = NULL;
+}
+
+#define preload_index(i) (((i) / 2) + 1) * (((i) % 2 * (-2) + 1))
+
+static GArray *preload_prepare_data(GtkWidget *thumbview) {
+ GList *list;
+ GArray *array;
+ GtkTreePath *sel_path, *path;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GValue value = {0};
+ gchar *filename;
+ guint list_len;
+ gint selected_index, *indices, index;
+ gboolean b;
+
+ array = g_array_new(FALSE, FALSE, sizeof(gchar*));
+
+ list = gtk_icon_view_get_selected_items(GTK_ICON_VIEW(thumbview));
+
+ list_len = g_list_length(list);
+
+ if(list_len != 1) {
+ g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free(list);
+
+ return array;
+ }
+
+ sel_path = g_list_nth_data(list, 0);
+
+ indices = gtk_tree_path_get_indices(sel_path);
+ if(!indices) {
+ g_warning("preload_thread: Failed to get indices");
+
+ g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free(list);
+
+ return array;
+ }
+
+ selected_index = indices[0];
+
+ model = gtk_icon_view_get_model(GTK_ICON_VIEW(thumbview));
+
+ /* Make sure the selected pixbuf is also preloaded. */
+ if(gtk_tree_model_get_iter(model, &iter, sel_path)) {
+ gtk_tree_model_get_value(model, &iter, 1, &value);
+ filename = g_value_dup_string(&value);
+ g_value_unset(&value);
+
+ g_array_append_val(array, filename);
+ }
+
+ for(gint i = 0; i < preload_max; i++) {
+ /* Make sure the selected pixbuf is also preloaded. */
+ if(array->len == 0 && gtk_tree_model_get_iter(model, &iter, sel_path)) {
+ gtk_tree_model_get_value(model, &iter, 1, &value);
+ filename = g_value_dup_string(&value);
+ g_value_unset(&value);
+
+ g_array_append_val(array, filename);
+ }
+
+ index = selected_index + preload_index(i);
+ if(index < 0) {
+ continue;
+ }
+
+ path = gtk_tree_path_new_from_indices(index, -1);
+
+ if(gtk_tree_path_get_depth(path) != 1) {
+ /* Assume we're out of bounds. */
+ gtk_tree_path_free(path);
+ continue;
+ }
+
+ b = gtk_tree_model_get_iter(model, &iter, path);
+
+ gtk_tree_path_free(path);
+
+ if(b == FALSE)
+ continue;
+
+ gtk_tree_model_get_value(model, &iter, 1, &value);
+ filename = g_value_dup_string(&value);
+ g_value_unset(&value);
+
+ g_array_append_val(array, filename);
+ }
+
+ g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free(list);
+
+ return array;
+}
+
+void preload_start_thread(GtkWidget *thumbview, gint win_width, gint win_height) {
+ struct preload_thread_data_t *data;
+ GError *error = NULL;
+
+ if(preload_thread) {
+ preload_thread_exit = TRUE;
+ g_thread_join(preload_thread);
+ }
+
+ data = g_malloc(sizeof(struct preload_thread_data_t));
+
+ data->array = preload_prepare_data(thumbview);
+ data->win_width = win_width;
+ data->win_height = win_height;
+
+ preload_thread_exit = FALSE;
+ preload_thread = g_thread_create(preload_thread_func, data, TRUE, &error);
+
+ if(!preload_thread) {
+ g_error("%s", error->message);
+ g_error_free(error);
+ }
+}
+
+void preload_clear() {
+ g_hash_table_remove_all(hashtable);
+}
+
+static void preload_add(const gchar *filename, gpointer data) {
+ g_mutex_lock(preload_hashtable_mutex);
+
+ g_hash_table_replace(hashtable, g_strdup(filename), data);
+
+ g_mutex_unlock(preload_hashtable_mutex);
+}
+
+gpointer preload_get(const gchar *filename) {
+ gpointer data;
+
+ g_mutex_lock(preload_hashtable_mutex);
+
+ data = g_hash_table_lookup(hashtable, filename);
+
+ g_mutex_unlock(preload_hashtable_mutex);
+
+ return data;
+}
+
+inline static void add_pixbuf(const gchar *filename, gpointer _data) {
+ struct preload_thread_data_t *data;
+ struct preload_hash_table_value_t *value;
+ GdkPixbuf *orig, *pb;
+ GError *error = NULL;
+ gint width, height;
+ gdouble ratio;
+
+ /* Check if we already loaded this filename. */
+ if(preload_get(filename))
+ return;
+
+ orig = gdk_pixbuf_new_from_file(filename, &error);
+
+ if(!orig) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ data = _data;
+
+ pb = resize_pixbuf(orig, data->win_width, data->win_height, &width, &height, &ratio);
+
+ value = g_malloc(sizeof(struct preload_hash_table_value_t));
+
+ if(pb) {
+ value->pb = pb;
+ value->width = width;
+ value->height = height;
+ value->ratio = ratio;
+
+ g_object_unref(orig);
+ } else {
+ value->pb = orig;
+ value->width = 0;
+ value->height = 0;
+ value->ratio = 0;
+ }
+
+ preload_add(filename, value);
+}
+
+static gpointer preload_thread_func(gpointer _data) {
+ struct preload_thread_data_t *data;
+ GArray *array;
+ GList *keys;
+ gchar *added_name;
+ const gchar *key_name;
+ gboolean found;
+
+ data = _data;
+ array = data->array;
+
+ g_mutex_lock(preload_hashtable_mutex);
+ keys = g_hash_table_get_keys(hashtable);
+ g_mutex_unlock(preload_hashtable_mutex);
+
+ for(int i = 0; i < g_list_length(keys) && preload_thread_exit == FALSE; i++) {
+ found = FALSE;
+ key_name = g_list_nth_data(keys, i);
+ for(int j = 0; j < array->len && preload_thread_exit == FALSE; j++) {
+ added_name = g_array_index(array, gchar*, j);
+ if(g_strcmp0(key_name, added_name) == 0) {
+ found = TRUE;
+ break;
+ }
+ }
+ if(found == FALSE) {
+ g_mutex_lock(preload_hashtable_mutex);
+ g_hash_table_remove(hashtable, key_name);
+ g_mutex_unlock(preload_hashtable_mutex);
+ }
+ }
+
+ for(int i = 0; i < array->len && preload_thread_exit == FALSE; i++) {
+ added_name = g_array_index(array, gchar*, i);
+ add_pixbuf(added_name, data);
+ }
+
+ g_list_free(keys);
+ g_array_free(array, TRUE);
+
+ g_free(data);
+ preload_thread = NULL;
+ return NULL;
+}
diff --git a/preload.h b/preload.h
new file mode 100644
index 0000000..dbadbeb
--- /dev/null
+++ b/preload.h
@@ -0,0 +1,22 @@
+#ifndef _PRELOAD_H_
+#define _PRELOAD_H_
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+
+struct preload_hash_table_value_t {
+ GdkPixbuf *pb;
+ gint width, height;
+ gdouble ratio;
+};
+
+void preload_init();
+void preload_free();
+void preload_start_thread(GtkWidget*, gint, gint);
+void preload_clear();
+gpointer preload_get(const gchar*);
+
+extern guint preload_max;
+
+#endif
diff --git a/wallpapers.c b/wallpapers.c
index 56a72c6..fd893f3 100644
--- a/wallpapers.c
+++ b/wallpapers.c
@@ -118,3 +118,43 @@ void add_dir_recursive(const gchar *path, sqlite_uint64 parent, GtkStatusbar *st
g_free(msg);
gdk_threads_leave();
}
+
+GdkPixbuf *resize_pixbuf(GdkPixbuf *orig, gint win_width, gint win_height, gint *_width, gint *_height, gdouble *ratio) {
+ GdkPixbuf *pb;
+ gint img_width, img_height, width, height;
+ gdouble scalex, scaley, width_ratio, height_ratio;
+
+ img_width = gdk_pixbuf_get_width(orig);
+ img_height = gdk_pixbuf_get_height(orig);
+
+ /* do we need to resize? */
+ if(img_width > win_width || img_height > win_height) {
+ /* resize by width */
+ width_ratio = (gdouble)img_width / (gdouble)win_width;
+ height_ratio = (gdouble)img_height / (gdouble)win_height;
+ if(width_ratio > height_ratio) {
+ width = win_width;
+ height = (gint)(1.0 / width_ratio * img_height);
+ scalex = (gdouble)width / (gdouble)img_width;
+ scaley = (gdouble)height / (gdouble)img_height;
+ } else { /* resize by height */
+ height = win_height;
+ width = (gint)(1.0 / height_ratio * img_width);
+ scalex = (gdouble)width / (gdouble)img_width;
+ scaley = (gdouble)height / (gdouble)img_height;
+ }
+ if(width == 0 || height == 0) return NULL;
+ pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
+ gdk_pixbuf_scale(orig, pb, 0, 0, width, height, 0, 0, scalex, scaley, GDK_INTERP_BILINEAR);
+ } else {
+ width = img_width;
+ height = img_height;
+ pb = gdk_pixbuf_copy(orig);
+ }
+
+ *ratio = (gdouble)width / (gdouble)img_width;
+ *_width = width;
+ *_height = height;
+
+ return pb;
+}
diff --git a/wallpapers.h b/wallpapers.h
index ce4430c..049cba0 100644
--- a/wallpapers.h
+++ b/wallpapers.h
@@ -2,9 +2,11 @@
#define _WALLPAPERS_H_
#include <gtk/gtk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
#include "db.h"
void add_dir_recursive(const gchar*, sqlite_uint64, GtkStatusbar*);
+GdkPixbuf *resize_pixbuf(GdkPixbuf*, gint, gint, gint*, gint*, gdouble*);
#endif
diff --git a/window_main.c b/window_main.c
index 984a4e0..a550943 100644
--- a/window_main.c
+++ b/window_main.c
@@ -9,6 +9,7 @@
#include "thumbnails.h"
#include "walls_conf.h"
#include "window_tag.h"
+#include "preload.h"
struct wallpaper_t *cur_wall = NULL;
GdkPixbuf *orig_pixbuf = NULL;
@@ -113,58 +114,41 @@ static void set_resize_msg(gdouble scale) {
g_free(msg);
}
-static void resize_pixbuf() {
- GdkPixbuf *pb;
+static void set_image_pixbuf_resized(GdkPixbuf *pb, gint win_width, gint win_height, gint width, gint height, gdouble ratio) {
+ set_resize_msg(ratio);
+
+ gtk_widget_set_size_request(GTK_WIDGET(image), width, height);
+ gtk_widget_set_uposition(GTK_WIDGET(image), (width < win_width ? win_width / 2 - width / 2 : 0),
+ (height < win_height ? win_height / 2 - height / 2 : 0));
+ gtk_image_set_from_pixbuf(image, pb);
+
+ gtk_layout_set_size(GTK_LAYOUT(layout), win_width, win_height);
+}
+
+static void set_image_pixbuf() {
GdkWindow *window;
- gint win_width, win_height, img_width, img_height, width, height;
- gdouble scalex, scaley, width_ratio, height_ratio;
+ GdkPixbuf *pb;
+ gint win_width, win_height, width, height;
+ gdouble ratio;
- /* Skip if no pixbuf is loaded yet. */
- if(!orig_pixbuf) return;
+ if(!orig_pixbuf)
+ return;
window = gtk_widget_get_window(layout);
gdk_drawable_get_size(window, &win_width, &win_height);
- gtk_layout_set_size(GTK_LAYOUT(layout), win_width, win_height);
- img_width = gdk_pixbuf_get_width(orig_pixbuf);
- img_height = gdk_pixbuf_get_height(orig_pixbuf);
-
- /* do we need to resize? */
- if(img_width > win_width || img_height > win_height) {
- /* resize by width */
- width_ratio = (gdouble)img_width / (gdouble)win_width;
- height_ratio = (gdouble)img_height / (gdouble)win_height;
- if(width_ratio > height_ratio) {
- width = win_width;
- height = (gint)(1.0 / width_ratio * img_height);
- scalex = (gdouble)width / (gdouble)img_width;
- scaley = (gdouble)height / (gdouble)img_height;
- } else { /* resize by height */
- height = win_height;
- width = (gint)(1.0 / height_ratio * img_width);
- scalex = (gdouble)width / (gdouble)img_width;
- scaley = (gdouble)height / (gdouble)img_height;
- }
- if(width == 0 || height == 0) return;
- pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
- gdk_pixbuf_scale(orig_pixbuf, pb, 0, 0, width, height, 0, 0, scalex, scaley, GDK_INTERP_BILINEAR);
- } else {
- width = img_width;
- height = img_height;
- pb = gdk_pixbuf_copy(orig_pixbuf);
- }
+ pb = resize_pixbuf(orig_pixbuf, win_width, win_height, &width, &height, &ratio);
+ if(!pb)
+ return;
- gtk_widget_set_size_request(GTK_WIDGET(image), width, height);
- gtk_widget_set_uposition(GTK_WIDGET(image), (width < win_width ? win_width / 2 - width / 2 : 0), (height < win_height ? win_height / 2 - height / 2 : 0));
- gtk_image_set_from_pixbuf(image, pb);
+ set_image_pixbuf_resized(pb, win_width, win_height, width, height, ratio);
g_object_unref(pb);
-
- set_resize_msg((gdouble)width / (gdouble)img_width);
}
void on_window_size_allocate(GtkWidget *widget, GtkAllocation *allocation, gpointer user_data) {
if(orig_pixbuf && (allocation->width != last_width || allocation->height != last_height)) {
- resize_pixbuf();
+ preload_clear();
+ set_image_pixbuf();
last_width = allocation->width;
last_height = allocation->height;
}
@@ -176,7 +160,8 @@ static gboolean hpane_resize_idle_func(gpointer data) {
pos = gtk_paned_get_position(GTK_PANED(data));
if(pos != hpane_last_pos) {
- resize_pixbuf();
+ preload_clear();
+ set_image_pixbuf();
hpane_last_pos = pos;
return TRUE; /* Ensure that we'll keep checking until we're done resizing. */
}
@@ -208,11 +193,23 @@ static void load_pixbuf(const gchar *filepath) {
GError *error = NULL;
sqlite_uint64 wallid;
struct wallpaper_t *wall;
+ struct preload_hash_table_value_t *pt;
gchar *base;
+ GdkWindow *window;
+ gint win_width, win_height;
if(orig_pixbuf)
g_object_unref(orig_pixbuf);
- orig_pixbuf = gdk_pixbuf_new_from_file(filepath, &error);
+
+ pt = preload_get(filepath);
+ if(pt)
+ orig_pixbuf = gdk_pixbuf_copy(pt->pb);
+ else
+ orig_pixbuf = NULL;
+
+ if(!orig_pixbuf)
+ orig_pixbuf = gdk_pixbuf_new_from_file(filepath, &error);
+
if(!orig_pixbuf) {
g_warning("%s", error->message);
g_error_free(error);
@@ -246,7 +243,13 @@ static void load_pixbuf(const gchar *filepath) {
walls_set_window_title();
}
- resize_pixbuf();
+ if(pt) {
+ window = gtk_widget_get_window(layout);
+ gdk_drawable_get_size(window, &win_width, &win_height);
+
+ set_image_pixbuf_resized(orig_pixbuf, win_width, win_height, pt->width, pt->height, pt->ratio);
+ } else
+ set_image_pixbuf();
}
/**
@@ -326,6 +329,8 @@ void on_thumbview_selection_changed(GtkIconView *iconview, gpointer user_data) {
GtkTreePath *path;
GtkTreeModel *model;
GtkTreeIter iter;
+ GdkWindow *window;
+ gint win_width, win_height;
list = gtk_icon_view_get_selected_items(iconview);
if(list) {
@@ -341,6 +346,11 @@ void on_thumbview_selection_changed(GtkIconView *iconview, gpointer user_data) {
g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL);
g_list_free(list);
+
+ window = gtk_widget_get_window(layout);
+ gdk_drawable_get_size(window, &win_width, &win_height);
+
+ preload_start_thread(GTK_WIDGET(thumbview), win_width, win_height);
}
}
@@ -690,10 +700,14 @@ int gui_main(int argc, char **argv) {
gtk_widget_show_all(GTK_WIDGET(window));
+ preload_init();
+
gdk_threads_enter();
gtk_main();
gdk_threads_leave();
+ preload_free();
+
db_close();
conf_close();