From 90df6aec51309372260b02b3a9bd518d35da4f40 Mon Sep 17 00:00:00 2001 From: Jon Bergli Heier Date: Wed, 6 Jan 2010 19:23:09 +0100 Subject: Implemented threaded preloading of images. The number of preloads is currently hardcoded in preload.c, an option to change this will be added shortly. --- preload.c | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 preload.c (limited to 'preload.c') 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 +#include +#include + +#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; +} -- cgit v1.2.3