* cache-loader.[ch]: New utility to load cache-able data.
* cache.[ch]: Add embedded (exif) date caching.
* pan-view.c: Use new cache loading mechanism. Add exif date support
to timeline and calendar view.
* src/Makefile.am: Add cache-loader.[c,h].
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
##### an offical release when making enhancements and translation updates. #####
+Wed Apr 13 18:16:14 2005 John Ellis <johne@verizon.net>
+
+ * cache-loader.[ch]: New utility to load cache-able data.
+ * cache.[ch]: Add embedded (exif) date caching.
+ * pan-view.c: Use new cache loading mechanism. Add exif date support
+ to timeline and calendar view.
+ * src/Makefile.am: Add cache-loader.[c,h].
+
Tue Apr 12 07:59:20 2005 John Ellis <johne@verizon.net>
* pan-view.c: Use mostly neutral (gray) colors in the pan view to avoid
----------------------------------------------
> pixbuf-renderer.c:
- d> fix two pass render from corrupting it->qd pointer (need one pointer for each queue?).
- d> fix image_change_from_image (to do this need a pixbuf_renderer_move_image).
- d> fix broken zoom out drawing when using source tiles.
- d> fix 2pass zoom when using source tiles and zoomed out (not always rendering second pass)
- d> pixbuf_renderer_move should be clearing the tiles of the source image since they are no longer
- valid and are then wasting memory (for example when full screen is active).
+ > tile dispose order is slightly incorrect, furthest ones from current position should be dropped first
+
> image.c:
- d> need to keep a list of ImageWindows and provide function to sync options to each object.
d> test and probably fix delay_flip.
> UPDATE: works as before (pre pixbuf-renderer), but should be fixed to provide a single redraw
by pre-rendering any scaled tiles that are visible before signaling 'render_complete'.
> make this a g_object with signals for completed, changed, etc.
- d> fix region computation rounding when updating scaled image in 'area ready' signal.
> fix delay flip 'completed' signal (full screen busy cursor is not always turned off)
- d> fix slow loading of images when zoomed out - bug could be poor clamping to visible in PixbufRenderer.
+
+ > cache-load.c:
+ > should honor enable_thumbnails setting
> work on pan view:
> Pick a better keyboard shortcut than Control + J :)
- d> Fix occasional redraw bugs when zoomed out.
- d> Fix occasional odd requests for non-visible tiles when zoomed out (related to above?).
- d> Fix slowness in image.c with huge grid size by changing use of pre-allocated tile array
- to on-demand tile allocation (can this be implemented like source tiles?).
- OR: use an array so that we do not need to walk a GList of pre-allocated tile containers
w> Fix search scrolling to try to center image and info popup.
> Fix info popup location to opposing horizontal side when near edge of grid.
> Find something to do with middle mouse clicks.
> search should highlight all matching images (with toggle?)
> should non-thumbnail images have a drop shadow?
d> optimize pixbuf_draw_line (line endpoints should clip to draw region before draw loop)
+ > introduced bug (looks like clamp or rounding error)
d> optimize pixbuf_draw_triangle
+ > introduced bug (round error?)
> does new pixbuf_draw_triangle contain line edge rounding error?
- d> move pixbuf_draw_* stuff into pixbuf_util.c
+ > check ref counting of image when redrawing after finish loading
+ > speed up sorting image.. stage when sorting/merging cache list
+
> time line view:
- > allow use of file date or EXIF (embedded) date.
+ w> allow use of file date or EXIF (embedded) date.
> allow horizontal _or_ vertical orientation.
> calendar view:
bar_sort.h \
cache.c \
cache.h \
+ cache-loader.c \
+ cache-loader.h \
cache_maint.c \
cache_maint.h \
cellrenderericon.c \
--- /dev/null
+/*
+ * GQview
+ * (C) 2005 John Ellis
+ *
+ * Author: John Ellis
+ *
+ * This software is released under the GNU General Public License (GNU GPL).
+ * Please read the included file COPYING for more information.
+ * This software comes with no warranty of any kind, use at your own risk!
+ */
+
+#include "gqview.h"
+#include "cache-loader.h"
+
+#include "exif.h"
+#include "md5-util.h"
+#include "ui_fileops.h"
+
+
+static gboolean cache_loader_process(CacheLoader *cl);
+
+
+static void cache_loader_done_cb(ImageLoader *il, gpointer data)
+{
+ CacheLoader *cl = data;
+
+ cache_loader_process(cl);
+}
+
+static void cache_loader_error_cb(ImageLoader *il, gpointer data)
+{
+ CacheLoader *cl = data;
+
+ cl->error = TRUE;
+ cache_loader_done_cb(il, data);
+}
+
+static gboolean cache_loader_process(CacheLoader *cl)
+{
+ if (cl->todo_mask & CACHE_LOADER_SIMILARITY &&
+ !cl->cd->similarity)
+ {
+ GdkPixbuf *pixbuf;
+
+ if (!cl->il && !cl->error)
+ {
+ cl->il = image_loader_new(cl->path);
+ image_loader_set_error_func(cl->il, cache_loader_error_cb, cl);
+ if (image_loader_start(cl->il, cache_loader_done_cb, cl))
+ {
+ return FALSE;
+ }
+
+ cl->error = TRUE;
+ }
+
+ pixbuf = image_loader_get_pixbuf(cl->il);
+ if (pixbuf)
+ {
+ if (!cl->error)
+ {
+ ImageSimilarityData *sim;
+
+ sim = image_sim_new_from_pixbuf(pixbuf);
+ cache_sim_data_set_similarity(cl->cd, sim);
+ image_sim_free(sim);
+
+ cl->done_mask |= CACHE_LOADER_SIMILARITY;
+ }
+
+ /* we have the dimensions via pixbuf */
+ if (!cl->cd->dimensions)
+ {
+ cache_sim_data_set_dimensions(cl->cd, gdk_pixbuf_get_width(pixbuf),
+ gdk_pixbuf_get_height(pixbuf));
+ if (cl->todo_mask & CACHE_LOADER_DIMENSIONS)
+ {
+ cl->todo_mask &= ~CACHE_LOADER_DIMENSIONS;
+ cl->done_mask |= CACHE_LOADER_DIMENSIONS;
+ }
+ }
+ }
+
+ image_loader_free(cl->il);
+ cl->il = NULL;
+
+ cl->todo_mask &= ~CACHE_LOADER_SIMILARITY;
+ }
+ else if (cl->todo_mask & CACHE_LOADER_DIMENSIONS &&
+ !cl->cd->dimensions)
+ {
+ if (!cl->error &&
+ image_load_dimensions(cl->path, &cl->cd->width, &cl->cd->height))
+ {
+ cl->cd->dimensions = TRUE;
+ cl->done_mask |= CACHE_LOADER_DIMENSIONS;
+ }
+ else
+ {
+ cl->error = TRUE;
+ }
+
+ cl->todo_mask &= ~CACHE_LOADER_DIMENSIONS;
+ }
+ else if (cl->todo_mask & CACHE_LOADER_MD5SUM &&
+ !cl->cd->have_md5sum)
+ {
+ if (md5_get_digest_from_file_utf8(cl->path, cl->cd->md5sum))
+ {
+ cl->cd->have_md5sum = TRUE;
+ cl->done_mask |= CACHE_LOADER_MD5SUM;
+ }
+ else
+ {
+ cl->error = TRUE;
+ }
+
+ cl->todo_mask &= ~CACHE_LOADER_MD5SUM;
+ }
+ else if (cl->todo_mask & CACHE_LOADER_DATE &&
+ !cl->cd->have_date)
+ {
+ time_t date = -1;
+ ExifData *exif;
+
+ exif = exif_read(cl->path);
+ if (exif)
+ {
+ gchar *text;
+
+ text = exif_get_data_as_text(exif, "fDateTime");
+ if (text)
+ {
+ struct tm t = { 0 };
+
+ if (sscanf(text, "%d:%d:%d %d:%d:%d", &t.tm_year, &t.tm_mon, &t.tm_mday,
+ &t.tm_hour, &t.tm_min, &t.tm_sec) == 6)
+ {
+ t.tm_year -= 1900;
+ t.tm_mon -= 1;
+ date = mktime(&t);
+ }
+ g_free(text);
+ }
+ exif_free(exif);
+ }
+
+ cl->cd->date = date;
+ cl->cd->have_date = TRUE;
+
+ cl->done_mask |= CACHE_LOADER_DATE;
+ cl->todo_mask &= ~CACHE_LOADER_DATE;
+ }
+ else
+ {
+ /* done, save then call done function */
+ if (enable_thumb_caching &&
+ cl->done_mask != CACHE_LOADER_NONE)
+ {
+ gchar *base;
+ mode_t mode = 0755;
+
+ base = cache_get_location(CACHE_TYPE_SIM, cl->path, FALSE, &mode);
+ if (cache_ensure_dir_exists(base, mode))
+ {
+ g_free(cl->cd->path);
+ cl->cd->path = cache_get_location(CACHE_TYPE_SIM, cl->path, TRUE, NULL);
+ if (cache_sim_data_save(cl->cd))
+ {
+ filetime_set(cl->cd->path, filetime(cl->path));
+ }
+ }
+ g_free(base);
+ }
+
+ cl->idle_id = -1;
+
+ if (cl->done_func)
+ {
+ cl->done_func(cl, cl->error, cl->done_data);
+ }
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean cache_loader_idle_cb(gpointer data)
+{
+ CacheLoader *cl = data;
+
+ return cache_loader_process(cl);
+}
+
+CacheLoader *cache_loader_new(const gchar *path, CacheDataType load_mask,
+ CacheLoaderDoneFunc done_func, gpointer done_data)
+{
+ CacheLoader *cl;
+ gchar *found;
+
+ if (!path || !isfile(path)) return NULL;
+
+ cl = g_new0(CacheLoader, 1);
+ cl->path = g_strdup(path);
+
+ cl->done_func = done_func;
+ cl->done_data = done_data;
+
+ found = cache_find_location(CACHE_TYPE_SIM, path);
+ if (found && filetime(found) == filetime(path))
+ {
+ cl->cd = cache_sim_data_load(found);
+ }
+ g_free(found);
+
+ if (!cl->cd) cl->cd = cache_sim_data_new();
+
+ cl->todo_mask = load_mask;
+ cl->done_mask = CACHE_LOADER_NONE;
+
+ cl->il = NULL;
+ cl->idle_id = g_idle_add(cache_loader_idle_cb, cl);
+
+ cl->error = FALSE;
+
+ return cl;
+}
+
+void cache_loader_free(CacheLoader *cl)
+{
+ if (!cl) return;
+
+ if (cl->idle_id != -1)
+ {
+ g_source_remove(cl->idle_id);
+ cl->idle_id = -1;
+ }
+
+ image_loader_free(cl->il);
+ cache_sim_data_free(cl->cd);
+
+ g_free(cl->path);
+ g_free(cl);
+}
+
--- /dev/null
+/*
+ * GQview
+ * (C) 2005 John Ellis
+ *
+ * Author: John Ellis
+ *
+ * This software is released under the GNU General Public License (GNU GPL).
+ * Please read the included file COPYING for more information.
+ * This software comes with no warranty of any kind, use at your own risk!
+ */
+
+
+#ifndef CACHE_LOADER_H
+#define CACHE_LOADER_H
+
+
+#include "cache.h"
+#include "image-load.h"
+
+
+typedef struct _CacheLoader CacheLoader;
+
+typedef void (* CacheLoaderDoneFunc)(CacheLoader *cl, gint error, gpointer data);
+
+
+typedef enum {
+ CACHE_LOADER_NONE = 0,
+ CACHE_LOADER_DIMENSIONS = 1 << 0,
+ CACHE_LOADER_DATE = 1 << 1,
+ CACHE_LOADER_MD5SUM = 1 << 2,
+ CACHE_LOADER_SIMILARITY = 1 << 3
+} CacheDataType;
+
+struct _CacheLoader {
+ gchar *path;
+ CacheData *cd;
+
+ CacheDataType todo_mask;
+ CacheDataType done_mask;
+
+ CacheLoaderDoneFunc done_func;
+ gpointer done_data;
+
+ gint error;
+
+ ImageLoader *il;
+ gint idle_id;
+};
+
+
+CacheLoader *cache_loader_new(const gchar *path, CacheDataType load_mask,
+ CacheLoaderDoneFunc done_func, gpointer done_data);
+
+void cache_loader_free(CacheLoader *cl);
+
+
+#endif
+
+
* SIMcache
* #coment
* Dimensions=[<width> x <height>]
+ * Date=[<value in time_t format, or -1 if no embedded date>]
* Checksum=[<value>]
* MD5sum=[<32 character ascii text digest>]
* SimilarityGrid[32 x 32]=<3072 bytes of data (1024 pixels in RGB format, 1 pixel is 24bits)>
CacheData *cd;
cd = g_new0(CacheData, 1);
- cd->dimensions = FALSE;
- cd->similarity = FALSE;
+ cd->date = -1;
return cd;
}
return TRUE;
}
+static gint cache_sim_write_date(FILE *f, CacheData *cd)
+{
+ if (!f || !cd || !cd->have_date) return FALSE;
+
+ fprintf(f, "Date=[%ld]\n", cd->date);
+
+ return TRUE;
+}
+
static gint cache_sim_write_checksum(FILE *f, CacheData *cd)
{
if (!f || !cd || !cd->have_checksum) return FALSE;
fprintf(f, "SIMcache\n#%s %s\n", PACKAGE, VERSION);
cache_sim_write_dimensions(f, cd);
+ cache_sim_write_date(f, cd);
cache_sim_write_checksum(f, cd);
cache_sim_write_md5sum(f, cd);
cache_sim_write_similarity(f, cd);
return FALSE;
}
+static gint cache_sim_read_date(FILE *f, char *buf, int s, CacheData *cd)
+{
+ if (!f || !buf || !cd) return FALSE;
+
+ if (s < 4 || strncmp("Date", buf, 4) != 0) return FALSE;
+
+ if (fseek(f, - s, SEEK_CUR) == 0)
+ {
+ char b;
+ char buf[1024];
+ gint p = 0;
+
+ b = 'X';
+ while (b != '[')
+ {
+ if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
+ }
+ while (b != ']' && p < 1023)
+ {
+ if (fread(&b, sizeof(b), 1, f) != 1) return FALSE;
+ buf[p] = b;
+ p++;
+ }
+
+ while (b != '\n')
+ {
+ if (fread(&b, sizeof(b), 1, f) != 1) break;
+ }
+
+ buf[p] = '\0';
+ cd->date = strtol(buf, NULL, 10);
+
+ cd->have_date = TRUE;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static gint cache_sim_read_checksum(FILE *f, char *buf, int s, CacheData *cd)
{
if (!f || !buf || !cd) return FALSE;
{
if (!cache_sim_read_comment(f, buf, s, cd) &&
!cache_sim_read_dimensions(f, buf, s, cd) &&
+ !cache_sim_read_date(f, buf, s, cd) &&
!cache_sim_read_checksum(f, buf, s, cd) &&
!cache_sim_read_md5sum(f, buf, s, cd) &&
!cache_sim_read_similarity(f, buf, s, cd))
cd->dimensions = TRUE;
}
+void cache_sim_data_set_date(CacheData *cd, time_t date)
+{
+ if (!cd) return;
+
+ cd->date = date;
+ cd->have_date = TRUE;
+}
+
void cache_sim_data_set_checksum(CacheData *cd, long checksum)
{
if (!cd) return;
gchar *path;
gint width;
gint height;
+ time_t date;
long checksum;
guchar md5sum[16];
ImageSimilarityData *sim;
gint dimensions;
+ gint have_date;
gint have_checksum;
gint have_md5sum;
gint similarity;
CacheData *cache_sim_data_load(const gchar *path);
void cache_sim_data_set_dimensions(CacheData *cd, gint w, gint h);
+void cache_sim_data_set_date(CacheData *cd, time_t date);
void cache_sim_data_set_checksum(CacheData *cd, long checksum);
void cache_sim_data_set_md5sum(CacheData *cd, guchar digest[16]);
void cache_sim_data_set_similarity(CacheData *cd, ImageSimilarityData *sd);
#include "pan-view.h"
#include "cache.h"
+#include "cache-loader.h"
#include "dnd.h"
#include "editors.h"
#include "filelist.h"
#define PAN_PREF_GROUP "pan_view_options"
#define PAN_PREF_HIDE_WARNING "hide_performance_warning"
+#define SORT_BY_EXIF_DATE 1
+
typedef enum {
LAYOUT_TIMELINE = 0,
gint cache_count;
gint cache_total;
gint cache_tick;
+ CacheLoader *cache_cl;
ImageLoader *il;
ThumbLoader *tl;
static GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
+static void pan_window_layout_update_idle(PanWindow *pw);
+
static GtkWidget *pan_popup_menu(PanWindow *pw);
static void pan_fullscreen_toggle(PanWindow *pw, gint force_off);
pw->cache_count = 0;
pw->cache_total = 0;
pw->cache_tick = 0;
+
+ cache_loader_free(pw->cache_cl);
+ pw->cache_cl = NULL;
}
static void pan_cache_fill(PanWindow *pw, const gchar *path)
pw->cache_total = g_list_length(pw->cache_todo);
}
+static void pan_cache_step_done_cb(CacheLoader *cl, gint error, gpointer data)
+{
+ PanWindow *pw = data;
+
+ if (pw->cache_list)
+ {
+ PanCacheData *pc;
+ pc = pw->cache_list->data;
+
+ if (!pc->cd)
+ {
+ pc->cd = cl->cd;
+ cl->cd = NULL;
+ }
+ }
+
+ cache_loader_free(cl);
+ pw->cache_cl = NULL;
+
+ pan_window_layout_update_idle(pw);
+}
+
static gint pan_cache_step(PanWindow *pw)
{
FileData *fd;
PanCacheData *pc;
- CacheData *cd = NULL;
+ CacheDataType load_mask;
- if (!pw->cache_todo) return FALSE;
+ if (!pw->cache_todo) return TRUE;
fd = pw->cache_todo->data;
pw->cache_todo = g_list_remove(pw->cache_todo, fd);
+#if 0
if (enable_thumb_caching)
{
gchar *found;
pw->cache_tick = 9;
}
-
+#endif
pc = g_new0(PanCacheData, 1);
memcpy(pc, fd, sizeof(FileData));
g_free(fd);
- pc->cd = cd;
+ pc->cd = NULL;
pw->cache_list = g_list_prepend(pw->cache_list, pc);
- return TRUE;
+ cache_loader_free(pw->cache_cl);
+
+ load_mask = CACHE_LOADER_NONE;
+ if (pw->size > LAYOUT_SIZE_THUMB_LARGE) load_mask |= CACHE_LOADER_DIMENSIONS;
+ if (SORT_BY_EXIF_DATE) load_mask |= CACHE_LOADER_DATE;
+ pw->cache_cl = cache_loader_new(((FileData *)pc)->path, load_mask,
+ pan_cache_step_done_cb, pw);
+ return (pw->cache_cl == NULL);
+}
+
+static void pan_cache_sync_date(PanWindow *pw, GList *list)
+{
+ GList *haystack;
+ GList *work;
+
+ haystack = g_list_copy(pw->cache_list);
+
+ work = list;
+ while (work)
+ {
+ FileData *fd;
+ GList *needle;
+
+ fd = work->data;
+ work = work->next;
+
+ needle = haystack;
+ while (needle)
+ {
+ PanCacheData *pc;
+ gchar *path;
+
+ pc = needle->data;
+ path = ((FileData *)pc)->path;
+ if (path && strcmp(path, fd->path) == 0)
+ {
+ GList *tmp;
+
+ if (pc->cd && pc->cd->have_date && pc->cd->date >= 0)
+ {
+ fd->date = pc->cd->date;
+ }
+
+ tmp = needle;
+ needle = needle->next;
+ haystack = g_list_delete_link(haystack, tmp);
+ }
+ else
+ {
+ needle = needle->next;
+ }
+ }
+ }
}
/*
list = pan_window_layout_list(path, SORT_NONE, TRUE);
list = filelist_sort(list, SORT_TIME, TRUE);
+ if (pw->cache_list && SORT_BY_EXIF_DATE)
+ {
+ pan_cache_sync_date(pw, list);
+ list = filelist_sort(list, SORT_TIME, TRUE);
+ }
+
day_max = 0;
count = 0;
tc = 0;
list = pan_window_layout_list(path, SORT_NONE, TRUE);
list = filelist_sort(list, SORT_TIME, TRUE);
+ if (pw->cache_list && SORT_BY_EXIF_DATE)
+ {
+ pan_cache_sync_date(pw, list);
+ list = filelist_sort(list, SORT_TIME, TRUE);
+ }
+
*width = PAN_FOLDER_BOX_BORDER * 2;
*height = PAN_FOLDER_BOX_BORDER * 2;
pi = pan_item_new_text(pw, x, y, buf,
TEXT_ATTR_BOLD | TEXT_ATTR_HEADING,
PAN_TEXT_COLOR, 255);
+ g_free(buf);
y += pi->height;
pi_month = pan_item_new_box(pw, file_data_new_simple(fd->path),
gint scroll_x;
gint scroll_y;
- if (pw->size > LAYOUT_SIZE_THUMB_LARGE)
+ if (pw->size > LAYOUT_SIZE_THUMB_LARGE ||
+ (SORT_BY_EXIF_DATE && (pw->layout == LAYOUT_TIMELINE || pw->layout == LAYOUT_CALENDAR)))
{
if (!pw->cache_list && !pw->cache_todo)
{
return TRUE;
}
}
- if (pan_cache_step(pw))
+ if (pw->cache_todo)
{
pw->cache_count++;
pw->cache_tick++;
pw->cache_tick = 0;
}
- return TRUE;
+ if (pan_cache_step(pw)) return TRUE;
+
+ pw->idle_id = -1;
+ return FALSE;
}
}
pan_window_message(pw, NULL);
pw->idle_id = -1;
-
return FALSE;
}
{
if (pw->idle_id == -1)
{
- pan_window_message(pw, _("Sorting images..."));
pw->idle_id = g_idle_add(pan_window_layout_update_idle_cb, pw);
}
}
+static void pan_window_layout_update(PanWindow *pw)
+{
+ pan_window_message(pw, _("Sorting images..."));
+ pan_window_layout_update_idle(pw);
+}
+
/*
*-----------------------------------------------------------------------------
* pan window keyboard
PanWindow *pw = data;
pw->layout = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
- pan_window_layout_update_idle(pw);
+ pan_window_layout_update(pw);
}
static void pan_window_layout_size_cb(GtkWidget *combo, gpointer data)
PanWindow *pw = data;
pw->size = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
- pan_window_layout_update_idle(pw);
+ pan_window_layout_update(pw);
}
static void pan_window_entry_activate_cb(const gchar *new_text, gpointer data)
warning_dialog(_("Folder not found"),
_("The entered path is not a folder"),
GTK_STOCK_DIALOG_WARNING, pw->path_entry);
- return;
}
+ else
+ {
+ tab_completion_append_to_history(pw->path_entry, path);
- tab_completion_append_to_history(pw->path_entry, path);
+ g_free(pw->path);
+ pw->path = g_strdup(path);
- g_free(pw->path);
- pw->path = g_strdup(path);
+ pan_window_layout_update(pw);
+ }
- pan_window_layout_update_idle(pw);
+ g_free(path);
}
static void pan_window_entry_change_cb(GtkWidget *combo, gpointer data)
gtk_widget_destroy(pw->window);
pan_window_items_free(pw);
+ pan_cache_free(pw);
g_free(pw->path);
gtk_window_set_default_size(GTK_WINDOW(pw->window), PAN_WINDOW_DEFAULT_WIDTH, PAN_WINDOW_DEFAULT_HEIGHT);
- pan_window_layout_update_idle(pw);
+ pan_window_layout_update(pw);
gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
gtk_widget_show(pw->window);
g_free(pw->path);
pw->path = g_strdup(path);
- pan_window_layout_update_idle(pw);
+ pan_window_layout_update(pw);
}
path_list_free(list);