#include #include #include #include "db.h" #include "thumbnails.h" GThread *thumb_thread = NULL; gboolean thumb_thread_exit = FALSE; inline static gchar *get_wall_thumb_name(const gchar *filepath) { GChecksum *ck; gchar *s; gchar *cksum; ck = g_checksum_new(G_CHECKSUM_MD5); s = g_strdup_printf("file://%s", filepath); g_checksum_update(ck, (const guchar*)s, -1); g_free(s); cksum = g_strdup_printf("%s/.thumbnails/normal/%s.png", g_get_home_dir(), g_checksum_get_string(ck)); g_checksum_free(ck); return cksum; } inline static time_t get_mtime(const gchar *filepath) { struct stat sb; if(g_stat(filepath, &sb) == 0) { return sb.st_mtime; } else { return 0; } } inline static gboolean is_thumb_modified(GdkPixbuf *pb, const gchar *filename, const gchar *thumbname) { time_t mtime, pb_mtime, thumb_mtime; const gchar *mtime_s; mtime = get_mtime(filename); mtime_s = gdk_pixbuf_get_option(pb, "tEXt::Thumb::MTime"); pb_mtime = g_ascii_strtoll(mtime_s, NULL, 10); /* Original file modified since thumbnail creation. */ if(mtime != pb_mtime) return TRUE; thumb_mtime = get_mtime(thumbname); /* Original file newer than thumbnail. */ if(mtime > thumb_mtime) return TRUE; return FALSE; } GdkPixbuf *get_thumbnail(const gchar *filepath) { GdkPixbuf *pb, *pb2; gint win_width, win_height, img_width, img_height, width, height; gdouble scalex, scaley, width_ratio, height_ratio; GError *error = NULL; gchar *thumbname, *mtime, *width_s, *height_s; thumbname = get_wall_thumb_name(filepath); if(g_access(thumbname, F_OK) == 0) { pb = gdk_pixbuf_new_from_file(thumbname, &error); if(pb && !is_thumb_modified(pb, filepath, thumbname)) { g_free(thumbname); return pb; } else if(pb) { g_object_unref(pb); } } pb = gdk_pixbuf_new_from_file(filepath, &error); if(!pb) { g_warning("%s", error->message); g_error_free(error); g_free(thumbname); return NULL; } img_width = gdk_pixbuf_get_width(pb); img_height = gdk_pixbuf_get_height(pb); win_width = win_height = 128; 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); } else { height = win_height; width = (gint)(1.0 / height_ratio * img_width); } scalex = (gdouble)width / (gdouble)img_width; scaley = (gdouble)height / (gdouble)img_height; pb2 = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height); gdk_pixbuf_scale(pb, pb2, 0, 0, width, height, 0, 0, scalex, scaley, GDK_INTERP_BILINEAR); g_object_unref(pb); error = NULL; mtime = g_strdup_printf("%lu", (unsigned long)get_mtime(filepath)); width_s = g_strdup_printf("%d", img_width); height_s = g_strdup_printf("%d", img_height); if(!gdk_pixbuf_save(pb2, thumbname, "png", &error, "tEXt::Thumb::URI", filepath, "tEXt::Thumb::MTime", mtime, "tEXt::Software", "walls", "tEXt::Thumb::Image::Width", width_s, "tEXt::Thumb::Image::Height", height_s, NULL)) { g_warning("%s", error->message); g_error_free(error); } g_free(mtime); g_free(width_s); g_free(height_s); g_free(thumbname); return pb2; } gpointer add_thumbs_thread(gpointer data) { GtkListStore *liststore; GtkTreeIter iter; GtkTreePath *path; int n; GValue value = {0}; GdkPixbuf *pb = NULL; const gchar *filepath; thumb_thread_exit = FALSE; liststore = GTK_LIST_STORE(data); if(!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(liststore), &iter)) { return NULL; } n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(liststore), NULL); for(int i = 0; i < n; i++) { if(thumb_thread_exit == TRUE) break; gtk_tree_model_get_value(GTK_TREE_MODEL(liststore), &iter, 1, &value); filepath = g_value_get_string(&value); pb = get_thumbnail(filepath); g_value_unset(&value); if(pb) { gtk_list_store_set(liststore, &iter, 0, pb, -1); path = gtk_tree_model_get_path(GTK_TREE_MODEL(liststore), &iter); if(path) gtk_tree_model_row_changed(GTK_TREE_MODEL(liststore), path, &iter); g_object_unref(pb); } gtk_tree_model_iter_next(GTK_TREE_MODEL(liststore), &iter); } thumb_thread = NULL; return NULL; }