Mercurial > hg > forks > geeqie
changeset 2213:839d0e06e552
Merge branch 'ke' into ke-lua
* ke: (74 commits)
rename file_data_new_simple to file_data_new_group, filedata.c cleanup
recognize all registered extensions, even if they contain dots
sidecar_file_priority function is already called with extension as an argument
call notify functions in in an idle call
move the grouping code from file_data_new to filelist_read
separate function fortesting changed files without sidecars
consistent use of fd->extension
simplified grouping of sidecars
fixed filelist_sort_compare_filedata to not return 0 no different files
always check for sidecars by readdir
do not use FileData entries with empty path
updated copyright notice
added custom tiff loader
added long-term cache for keywords
set stereo.fixed default values to hdmi stereo standard
fixed stereo.fixed preferences
fixed stereo_temp_disable functionality
ignore raw image support reported by the pixbuf loader
fixed using DEBUG_N from multiple threads
Fix bug 3006812: ensure exif cache is initialized even when all files are unreadable.
...
author | Klaus Ethgen <Klaus@Ethgen.de> |
---|---|
date | Sat, 29 Oct 2011 23:41:36 +0100 |
parents | de06305391a1 (current diff) 1fa011010438 (diff) |
children | 58cd15125b23 |
files | configure.in src/Makefile.am src/main.c |
diffstat | 62 files changed, 5610 insertions(+), 2606 deletions(-) [+] |
line wrap: on
line diff
--- a/configure.in Fri Apr 22 11:29:35 2011 +0100 +++ b/configure.in Sat Oct 29 23:41:36 2011 +0100 @@ -291,6 +291,49 @@ AC_SUBST(LCMS_CFLAGS) AC_SUBST(LCMS_LIBS) +# libjpeg support +# ---------------------------------------------------------------------- + +AC_ARG_ENABLE([jpeg], + AC_HELP_STRING([--disable-jpeg], [disable direct jpeg support]), + [libjpeg=$enableval], [libjpeg=auto]) + +if test "x${libjpeg}" != "xno"; then + AC_CHECK_LIB(jpeg, jpeg_destroy_decompress, + HAVE_JPEG=yes + JPEG_LIBS=-ljpeg + AC_DEFINE(HAVE_JPEG, 1, [define to enable use of custom jpeg loader]), + HAVE_JPEG=no) +else + HAVE_JPEG=disabled +fi + +AM_CONDITIONAL(HAVE_JPEG, [test "x$HAVE_JPEG" = xyes]) +AC_SUBST(JPEG_CFLAGS) +AC_SUBST(JPEG_LIBS) + + +# libtiff support +# ---------------------------------------------------------------------- + +AC_ARG_ENABLE([tiff], + AC_HELP_STRING([--disable-tiff], [disable direct tiff support]), + [libtiff=$enableval], [libtiff=auto]) + +if test "x${libtiff}" != "xno"; then + AC_CHECK_LIB(tiff, TIFFClientOpen, + HAVE_TIFF=yes + TIFF_LIBS=-ltiff + AC_DEFINE(HAVE_TIFF, 1, [define to enable use of custom tiff loader]), + HAVE_TIFF=no) +else + HAVE_TIFF=disabled +fi + +AM_CONDITIONAL(HAVE_TIFF, [test "x$HAVE_TIFF" = xyes]) +AC_SUBST(TIFF_CFLAGS) +AC_SUBST(TIFF_LIBS) + # Exiv2 support # ---------------------------------------------------------------------- @@ -476,7 +519,7 @@ Gtk: $GTK_CFLAGS Glib: $GLIB_CFLAGS Thread: $GTHREAD_LIBS - Others: $LCMS_LIBS $EXIV2_LIBS $LIBCHAMPLAIN_LIBS $LIBCHAMPLAIN_GTK_LIBS $LUA_LIBS + Others: $JPEG_LIBS $TIFF_LIBS $LCMS_LIBS $EXIV2_LIBS $LIBCHAMPLAIN_LIBS $LIBCHAMPLAIN_GTK_LIBS $LUA_LIBS Localization: NLS support: $USE_NLS
--- a/po/POTFILES.in Fri Apr 22 11:29:35 2011 +0100 +++ b/po/POTFILES.in Sat Oct 29 23:41:36 2011 +0100 @@ -46,6 +46,7 @@ src/image.c src/image-load.c src/image-overlay.c +src/image_load_jpeg.c src/img-view.c src/layout.c src/layout_config.c
--- a/src/Makefile.am Fri Apr 22 11:29:35 2011 +0100 +++ b/src/Makefile.am Sat Oct 29 23:41:36 2011 +0100 @@ -162,10 +162,18 @@ image.h \ image-load.c \ image-load.h \ + image_load_gdk.c\ + image_load_gdk.h\ + image_load_jpeg.c\ + image_load_jpeg.h\ + image_load_tiff.c\ + image_load_tiff.h\ image-overlay.c \ image-overlay.h \ img-view.c \ img-view.h \ + jpeg_parser.c \ + jpeg_parser.h \ layout.c \ layout.h \ layout_config.c \ @@ -201,6 +209,8 @@ pan-view.h \ pixbuf-renderer.c \ pixbuf-renderer.h \ + renderer-tiles.c \ + renderer-tiles.h \ pixbuf_util.c \ pixbuf_util.h \ preferences.c \ @@ -246,7 +256,7 @@ window.h \ lua.c -geeqie_LDADD = $(GTK_LIBS) $(GLIB_LIBS) $(INTLLIBS) $(LCMS_LIBS) $(EXIV2_LIBS) $(LIBCHAMPLAIN_LIBS) $(LIBCHAMPLAIN_GTK_LIBS) $(LUA_LIBS) +geeqie_LDADD = $(GTK_LIBS) $(GLIB_LIBS) $(INTLLIBS) $(JPEG_LIBS) $(TIFF_LIBS) $(LCMS_LIBS) $(EXIV2_LIBS) $(LIBCHAMPLAIN_LIBS) $(LIBCHAMPLAIN_GTK_LIBS) $(LUA_LIBS) EXTRA_DIST = \ $(extra_SLIK)
--- a/src/bar_sort.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/bar_sort.c Sat Oct 29 23:41:36 2011 +0100 @@ -99,7 +99,7 @@ history_list_free_key(SORT_KEY_COLLECTIONS); bookmark_list_set_key(bookmarks, SORT_KEY_COLLECTIONS); - dir_fd = file_data_new_simple(get_collections_dir()); + dir_fd = file_data_new_dir(get_collections_dir()); filelist_read(dir_fd, &list, NULL); file_data_unref(dir_fd); @@ -205,18 +205,18 @@ GList *list; gchar *src_dir; - list = g_list_append(NULL, file_data_new_simple(sd->undo_dest)); + list = g_list_append(NULL, file_data_new_group(sd->undo_dest)); src_dir = remove_level_from_path(sd->undo_src); file_util_move_simple(list, src_dir, sd->lw->window); g_free(src_dir); } break; case BAR_SORT_COPY: - file_util_delete(file_data_new_simple(sd->undo_dest), NULL, button); + file_util_delete(file_data_new_group(sd->undo_dest), NULL, button); break; default: /* undo external command */ - file_util_delete(file_data_new_simple(sd->undo_dest), NULL, button); + file_util_delete(file_data_new_group(sd->undo_dest), NULL, button); break; } @@ -224,7 +224,7 @@ if (isfile(sd->undo_src)) { - layout_image_set_fd(sd->lw, file_data_new_simple(sd->undo_src)); + layout_image_set_fd(sd->lw, file_data_new_group(sd->undo_src)); } bar_sort_undo_set(sd, NULL, NULL, NULL); @@ -241,7 +241,7 @@ source = work->data; work = work->next; - collect_manager_remove(file_data_new_simple(source), sd->undo_dest); + collect_manager_remove(file_data_new_group(source), sd->undo_dest); } bar_sort_undo_set(sd, NULL, NULL, NULL);
--- a/src/cache_maint.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/cache_maint.c Sat Oct 29 23:41:36 2011 +0100 @@ -291,7 +291,7 @@ cache_folder = get_thumbnails_cache_dir(); } - dir_fd = file_data_new_simple(cache_folder); + dir_fd = file_data_new_dir(cache_folder); if (!filelist_read(dir_fd, NULL, &dlist)) { file_data_unref(dir_fd); @@ -370,7 +370,7 @@ base_length = strlen(homedir()) + strlen("/") + strlen(GQ_CACHE_RC_THUMB); base = g_strconcat(homedir(), "/", GQ_CACHE_RC_THUMB, dir, NULL); - dir_fd = file_data_new_simple(base); + dir_fd = file_data_new_dir(base); g_free(base); if (filelist_read(dir_fd, &flist, &dlist)) @@ -442,7 +442,7 @@ GList *work; cachedir = g_build_filename(dir, GQ_CACHE_LOCAL_THUMB, NULL); - cachedir_fd = file_data_new_simple(cachedir); + cachedir_fd = file_data_new_dir(cachedir); g_free(cachedir); filelist_read(cachedir_fd, &list, NULL); @@ -856,7 +856,7 @@ spinner_set_interval(cd->spinner, SPINNER_SPEED); - dir_fd = file_data_new_simple(path); + dir_fd = file_data_new_dir(path); cache_manager_render_folder(cd, dir_fd); file_data_unref(dir_fd); while (cache_manager_render_file(cd)); @@ -1062,21 +1062,21 @@ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(cd->progress), _("running...")); path = g_build_filename(homedir(), THUMB_FOLDER_GLOBAL, THUMB_FOLDER_NORMAL, NULL); - dir_fd = file_data_new_simple(path); + dir_fd = file_data_new_dir(path); filelist_read(dir_fd, &list, NULL); cd->list = list; file_data_unref(dir_fd); g_free(path); path = g_build_filename(homedir(), THUMB_FOLDER_GLOBAL, THUMB_FOLDER_LARGE, NULL); - dir_fd = file_data_new_simple(path); + dir_fd = file_data_new_dir(path); filelist_read(dir_fd, &list, NULL); cd->list = g_list_concat(cd->list, list); file_data_unref(dir_fd); g_free(path); path = g_build_filename(homedir(), THUMB_FOLDER_GLOBAL, THUMB_FOLDER_FAIL, NULL); - dir_fd = file_data_new_simple(path); + dir_fd = file_data_new_dir(path); filelist_read(dir_fd, &list, NULL); cd->list = g_list_concat(cd->list, list); file_data_unref(dir_fd);
--- a/src/collect-io.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/collect-io.c Sat Oct 29 23:41:36 2011 +0100 @@ -160,7 +160,7 @@ if (!flush) changed |= collect_manager_process_action(entry, &buf); - valid = (buf[0] == G_DIR_SEPARATOR && collection_add_check(cd, file_data_new_simple(buf), FALSE, TRUE)); + valid = (buf[0] == G_DIR_SEPARATOR && collection_add_check(cd, file_data_new_group(buf), FALSE, TRUE)); if (!valid) DEBUG_1("collection invalid file: %s", buf); total++; @@ -190,7 +190,7 @@ gchar *buf = NULL; while (collect_manager_process_action(entry, &buf)) { - collection_add_check(cd, file_data_new_simple(buf), FALSE, TRUE); + collection_add_check(cd, file_data_new_group(buf), FALSE, TRUE); changed = TRUE; g_free(buf); buf = NULL; @@ -673,7 +673,7 @@ GList *work; FileData *dir_fd; - dir_fd = file_data_new_simple(get_collections_dir()); + dir_fd = file_data_new_dir(get_collections_dir()); filelist_read(dir_fd, &list, NULL); file_data_unref(dir_fd);
--- a/src/debug.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/debug.c Sat Oct 29 23:41:36 2011 +0100 @@ -17,7 +17,7 @@ #include <glib/gprintf.h> - +GMutex *debug_mutex; /* * Logging functions */
--- a/src/debug.h Fri Apr 22 11:29:35 2011 +0100 +++ b/src/debug.h Sat Oct 29 23:41:36 2011 +0100 @@ -17,6 +17,8 @@ #define DOMAIN_DEBUG "debug" #define DOMAIN_INFO "info" +extern GMutex *debug_mutex; + gint log_domain_printf(const gchar *domain, const gchar *format, ...) G_GNUC_PRINTF(2, 3); #define log_printf(...) log_domain_printf(DOMAIN_INFO, __VA_ARGS__) @@ -37,9 +39,11 @@ gint debug_level = get_debug_level(); \ if (debug_level >= (n)) \ { \ + g_mutex_lock(debug_mutex); \ if (debug_level != 1) log_domain_printf(DOMAIN_DEBUG, "%s:%d: ", __FILE__, __LINE__); \ log_domain_printf(DOMAIN_DEBUG, __VA_ARGS__); \ log_domain_printf(DOMAIN_DEBUG, "\n"); \ + g_mutex_unlock(debug_mutex); \ } \ } while (0)
--- a/src/editors.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/editors.c Sat Oct 29 23:41:36 2011 +0100 @@ -135,6 +135,7 @@ {"image/x-xcf", ".xcf"}, {"image/x-xpixmap", ".xpm"}, {"image/x-x3f", ".x3f"}, + {"application/x-ptoptimizer-script", ".pto"}, {NULL, NULL}}; gint i, j;
--- a/src/exif-common.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/exif-common.c Sat Oct 29 23:41:36 2011 +0100 @@ -43,6 +43,7 @@ #include "format_raw.h" #include "ui_fileops.h" #include "cache.h" +#include "jpeg_parser.h" static gdouble exif_rational_to_double(ExifRational *r, gint sign) @@ -636,14 +637,20 @@ fd->exif = NULL; } +void exif_init_cache(void) +{ + assert(!exif_cache); + exif_cache = file_cache_new(exif_release_cb, 4); +} + ExifData *exif_read_fd(FileData *fd) { gchar *sidecar_path; + + if (!exif_cache) exif_init_cache(); if (!fd || !is_readable_file(fd->path)) return NULL; - if (!exif_cache) exif_cache = file_cache_new(exif_release_cb, 4); - if (file_cache_get(exif_cache, fd)) return fd->exif; /* CACHE_TYPE_XMP_METADATA file should exist only if the metadata are @@ -674,64 +681,6 @@ /* embedded icc in jpeg */ - -#define JPEG_MARKER 0xFF -#define JPEG_MARKER_SOI 0xD8 -#define JPEG_MARKER_EOI 0xD9 -#define JPEG_MARKER_APP1 0xE1 -#define JPEG_MARKER_APP2 0xE2 - -/* jpeg container format: - all data markers start with 0XFF - 2 byte long file start and end markers: 0xFFD8(SOI) and 0XFFD9(EOI) - 4 byte long data segment markers in format: 0xFFTTSSSSNNN... - FF: 1 byte standard marker identifier - TT: 1 byte data type - SSSS: 2 bytes in Motorola byte alignment for length of the data. - This value includes these 2 bytes in the count, making actual - length of NN... == SSSS - 2. - NNN.: the data in this segment - */ - -gboolean exif_jpeg_segment_find(guchar *data, guint size, - guchar app_marker, const gchar *magic, guint magic_len, - guint *seg_offset, guint *seg_length) -{ - guchar marker = 0; - guint offset = 0; - guint length = 0; - - while (marker != app_marker && - marker != JPEG_MARKER_EOI) - { - offset += length; - length = 2; - - if (offset + 2 >= size || - data[offset] != JPEG_MARKER) return FALSE; - - marker = data[offset + 1]; - if (marker != JPEG_MARKER_SOI && - marker != JPEG_MARKER_EOI) - { - if (offset + 4 >= size) return FALSE; - length += ((guint)data[offset + 2] << 8) + data[offset + 3]; - } - } - - if (marker == app_marker && - offset + length < size && - length >= 4 + magic_len && - memcmp(data + offset + 4, magic, magic_len) == 0) - { - *seg_offset = offset + 4; - *seg_length = length - 4; - return TRUE; - } - - return FALSE; -} - gboolean exif_jpeg_parse_color(ExifData *exif, guchar *data, guint size) { guint seg_offset = 0; @@ -746,7 +695,7 @@ TT = total number of ICC segments (TT in each ICC segment should match) */ - while (exif_jpeg_segment_find(data + seg_offset + seg_length, + while (jpeg_segment_find(data + seg_offset + seg_length, size - seg_offset - seg_length, JPEG_MARKER_APP2, "ICC_PROFILE\x00", 12,
--- a/src/exif.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/exif.c Sat Oct 29 23:41:36 2011 +0100 @@ -73,6 +73,7 @@ #include "main.h" #include "exif-int.h" +#include "jpeg_parser.h" #include "format_raw.h" #include "ui_fileops.h" @@ -1086,7 +1087,7 @@ return -2; } - if (exif_jpeg_segment_find(data, size, JPEG_MARKER_APP1, + if (jpeg_segment_find(data, size, JPEG_MARKER_APP1, "Exif\x00\x00", 6, &seg_offset, &seg_length)) {
--- a/src/exif.h Fri Apr 22 11:29:35 2011 +0100 +++ b/src/exif.h Sat Oct 29 23:41:36 2011 +0100 @@ -161,9 +161,6 @@ void exif_add_jpeg_color_profile(ExifData *exif, guchar *cp_data, guint cp_length); -gboolean exif_jpeg_segment_find(guchar *data, guint size, - guchar app_marker, const gchar *magic, guint magic_len, - guint *seg_offset, guint *seg_length); gboolean exif_jpeg_parse_color(ExifData *exif, guchar *data, guint size); /*raw support */
--- a/src/filedata.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/filedata.c Sat Oct 29 23:41:36 2011 +0100 @@ -27,8 +27,10 @@ static GHashTable *file_data_pool = NULL; static GHashTable *file_data_planned_change_hash = NULL; -static gint sidecar_file_priority(const gchar *path); -static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars, GHashTable *basename_hash); +static gint sidecar_file_priority(const gchar *extension); +static void file_data_check_sidecars(const GList *basename_list); +static FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd); + /* @@ -133,15 +135,10 @@ /* *----------------------------------------------------------------------------- - * file info struct + * changed files detection and notification *----------------------------------------------------------------------------- */ -FileData *file_data_merge_sidecar_files(FileData *target, FileData *source); -static void file_data_check_sidecars(FileData *fd, GHashTable *basename_hash); -FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd); - - void file_data_increment_version(FileData *fd) { fd->version++; @@ -153,74 +150,99 @@ } } -static gint file_data_sort_by_ext(gconstpointer a, gconstpointer b) +static gboolean file_data_check_changed_single_file(FileData *fd, struct stat *st) { - const FileData *fda = a; - const FileData *fdb = b; - - return strcmp(fdb->extension, fda->extension); + if (fd->size != st->st_size || + fd->date != st->st_mtime) + { + fd->size = st->st_size; + fd->date = st->st_mtime; + fd->mode = st->st_mode; + if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf); + fd->thumb_pixbuf = NULL; + file_data_increment_version(fd); + file_data_send_notification(fd, NOTIFY_REREAD); + return TRUE; + } + return FALSE; } -static GHashTable *file_data_basename_hash_new(void) -{ - return g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); -} - -static void file_data_basename_hash_insert(GHashTable *basename_hash, FileData *fd) +static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st) { - GList *list; - const gchar *ext = extension_from_path(fd->path); - gchar *basename = ext ? g_strndup(fd->path, ext - fd->path) : g_strdup(fd->path); - - list = g_hash_table_lookup(basename_hash, basename); + gboolean ret = FALSE; + GList *work; - if (!g_list_find(list, fd)) + ret = file_data_check_changed_single_file(fd, st); + + work = fd->sidecar_files; + while (work) { - list = g_list_insert_sorted(list, file_data_ref(fd), file_data_sort_by_ext); - g_hash_table_insert(basename_hash, basename, list); + FileData *sfd = work->data; + struct stat st; + work = work->next; + + if (!stat_utf8(sfd->path, &st)) + { + fd->size = 0; + fd->date = 0; + file_data_disconnect_sidecar_file(fd, sfd); + ret = TRUE; + continue; + } + + ret |= file_data_check_changed_files_recursive(sfd, &st); } - else - { - g_free(basename); - } + return ret; } -#if 0 -static void file_data_basename_hash_remove(GHashTable *basename_hash, FileData *fd) + +gboolean file_data_check_changed_files(FileData *fd) { - GList *list; - const gchar *ext = extension_from_path(fd->path); - gchar *basename = ext ? g_strndup(fd->path, ext - fd->path) : g_strdup(fd->path); - - list = g_hash_table_lookup(basename_hash, basename); + gboolean ret = FALSE; + struct stat st; - if (!g_list_find(list, fd)) return; - - list = g_list_remove(list, fd); - file_data_unref(fd); - - if (list) + if (fd->parent) fd = fd->parent; + + if (!stat_utf8(fd->path, &st)) { - g_hash_table_insert(basename_hash, basename, list); + GList *sidecars; + GList *work; + FileData *sfd = NULL; + + /* parent is missing, we have to rebuild whole group */ + ret = TRUE; + fd->size = 0; + fd->date = 0; + + /* file_data_disconnect_sidecar_file might delete the file, + we have to keep the reference to prevent this */ + sidecars = filelist_copy(fd->sidecar_files); + work = sidecars; + while (work) + { + sfd = work->data; + work = work->next; + + file_data_disconnect_sidecar_file(fd, sfd); + } + file_data_check_sidecars(sidecars); /* this will group the sidecars back together */ + /* now we can release the sidecars */ + filelist_free(sidecars); + file_data_send_notification(fd, NOTIFY_REREAD); } - else + else { - g_hash_table_remove(basename_hash, basename); - g_free(basename); + ret |= file_data_check_changed_files_recursive(fd, &st); } + + return ret; } -#endif - -static void file_data_basename_hash_remove_list(gpointer key, gpointer value, gpointer data) -{ - filelist_free((GList *)value); -} - -static void file_data_basename_hash_free(GHashTable *basename_hash) -{ - g_hash_table_foreach(basename_hash, file_data_basename_hash_remove_list, NULL); - g_hash_table_destroy(basename_hash); -} + +/* + *----------------------------------------------------------------------------- + * file name, extension, sorting, ... + *----------------------------------------------------------------------------- + */ static void file_data_set_collate_keys(FileData *fd) { @@ -292,102 +314,29 @@ return; } - fd->extension = extension_from_path(fd->path); + fd->extension = registered_extension_from_path(fd->path); if (fd->extension == NULL) { fd->extension = fd->name + strlen(fd->name); } - + + fd->sidecar_priority = sidecar_file_priority(fd->extension); file_data_set_collate_keys(fd); } -static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st) -{ - gboolean ret = FALSE; - GList *work; - - if (fd->size != st->st_size || - fd->date != st->st_mtime) - { - fd->size = st->st_size; - fd->date = st->st_mtime; - fd->mode = st->st_mode; - if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf); - fd->thumb_pixbuf = NULL; - file_data_increment_version(fd); - file_data_send_notification(fd, NOTIFY_REREAD); - ret = TRUE; - } - - work = fd->sidecar_files; - while (work) - { - FileData *sfd = work->data; - struct stat st; - work = work->next; - - if (!stat_utf8(sfd->path, &st)) - { - fd->size = 0; - fd->date = 0; - file_data_disconnect_sidecar_file(fd, sfd); - ret = TRUE; - continue; - } - - ret |= file_data_check_changed_files_recursive(sfd, &st); - } - return ret; -} - - -gboolean file_data_check_changed_files(FileData *fd) -{ - gboolean ret = FALSE; - struct stat st; - - if (fd->parent) fd = fd->parent; - - if (!stat_utf8(fd->path, &st)) - { - GList *sidecars; - GList *work; - FileData *sfd = NULL; - - /* parent is missing, we have to rebuild whole group */ - ret = TRUE; - fd->size = 0; - fd->date = 0; - - /* file_data_disconnect_sidecar_file might delete the file, - we have to keep the reference to prevent this */ - sidecars = filelist_copy(fd->sidecar_files); - work = sidecars; - while (work) - { - sfd = work->data; - work = work->next; - - file_data_disconnect_sidecar_file(fd, sfd); - } - if (sfd) file_data_check_sidecars(sfd, NULL); /* this will group the sidecars back together */ - /* now we can release the sidecars */ - filelist_free(sidecars); - file_data_send_notification(fd, NOTIFY_REREAD); - } - else - { - ret |= file_data_check_changed_files_recursive(fd, &st); - } - - return ret; -} - -static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars, GHashTable *basename_hash) +/* + *----------------------------------------------------------------------------- + * create or reuse Filedata + *----------------------------------------------------------------------------- + */ + +static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean disable_sidecars) { FileData *fd; - DEBUG_2("file_data_new: '%s' %d %d", path_utf8, check_sidecars, !!basename_hash); + DEBUG_2("file_data_new: '%s' %d", path_utf8, disable_sidecars); + + if (S_ISDIR(st->st_mode)) disable_sidecars = TRUE; if (!file_data_pool) file_data_pool = g_hash_table_new(g_str_hash, g_str_equal); @@ -412,19 +361,12 @@ if (fd) { gboolean changed; - if (basename_hash) - { - file_data_basename_hash_insert(basename_hash, fd); - if (check_sidecars) - file_data_check_sidecars(fd, basename_hash); - } + + if (disable_sidecars) file_data_disable_grouping(fd, TRUE); - if (fd->parent) - changed = file_data_check_changed_files(fd); - else - changed = file_data_check_changed_files_recursive(fd, st); - if (changed && check_sidecars && sidecar_file_priority(fd->extension)) - file_data_check_sidecars(fd, basename_hash); + + changed = file_data_check_changed_single_file(fd, st); + DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : ""); return fd; @@ -437,162 +379,24 @@ fd->mode = st->st_mode; fd->ref = 1; fd->magick = 0x12345678; + + if (disable_sidecars) fd->disable_grouping = TRUE; file_data_set_path(fd, path_utf8); /* set path, name, collate_key_*, original_path */ - if (basename_hash) file_data_basename_hash_insert(basename_hash, fd); - - if (check_sidecars) - file_data_check_sidecars(fd, basename_hash); return fd; } -/* extension must contain only ASCII characters */ -static GList *check_case_insensitive_ext(gchar *path) -{ - gchar *sl; - gchar *extl; - gint ext_len; - GList *list = NULL; - - sl = path_from_utf8(path); - - extl = strrchr(sl, '.'); - if (extl) - { - gint i, j; - extl++; /* the first char after . */ - ext_len = strlen(extl); - - for (i = 0; i < (1 << ext_len); i++) - { - struct stat st; - gboolean skip = FALSE; - for (j = 0; j < ext_len; j++) - { - if (i & (1 << (ext_len - 1 - j))) - { - extl[j] = g_ascii_tolower(extl[j]); - /* make sure the result does not contain duplicates */ - if (extl[j] == g_ascii_toupper(extl[j])) - { - /* no change, probably a number, we have already tested this combination */ - skip = TRUE; - break; - } - } - else - extl[j] = g_ascii_toupper(extl[j]); - } - if (skip) continue; - - if (stat(sl, &st) == 0) - { - list = g_list_prepend(list, file_data_new_local(sl, &st, FALSE, FALSE)); - } - } - } - g_free(sl); - - return list; -} - -static void file_data_check_sidecars(FileData *fd, GHashTable *basename_hash) -{ - gint base_len; - GString *fname; - FileData *parent_fd = NULL; - GList *work; - const GList *basename_list = NULL; - GList *group_list = NULL; - if (fd->disable_grouping || !sidecar_file_priority(fd->extension)) - return; - - base_len = fd->extension - fd->path; - fname = g_string_new_len(fd->path, base_len); - - if (basename_hash) - { - basename_list = g_hash_table_lookup(basename_hash, fname->str); - } - - - /* check for possible sidecar files; - the sidecar files created here are referenced only via fd->sidecar_files or fd->parent, - they have fd->ref set to 0 and file_data unref must chack and free them all together - (using fd->ref would cause loops and leaks) - */ - - /* find all possible sidecar files and order them according to sidecar_ext_get_list, - for case-only differences put lowercase first, - put the result to group_list - */ - work = sidecar_ext_get_list(); - while (work) - { - gchar *ext = work->data; - work = work->next; - - if (!basename_hash) - { - GList *new_list; - g_string_truncate(fname, base_len); - g_string_append(fname, ext); - new_list = check_case_insensitive_ext(fname->str); - group_list = g_list_concat(group_list, new_list); - } - else - { - const GList *work2 = basename_list; - - while (work2) - { - FileData *sfd = work2->data; - - if (g_ascii_strcasecmp(ext, sfd->extension) == 0) - { - group_list = g_list_append(group_list, file_data_ref(sfd)); - } - work2 = work2->next; - } - } - } - g_string_free(fname, TRUE); - - /* process the group list - the first one is the parent file, others are sidecars */ - work = group_list; - while (work) - { - FileData *new_fd = work->data; - work = work->next; - - if (new_fd->disable_grouping) - { - file_data_unref(new_fd); - continue; - } - - new_fd->ref--; /* do not use ref here */ - - if (!parent_fd) - parent_fd = new_fd; /* parent is the one with the highest prio, found first */ - else - file_data_merge_sidecar_files(parent_fd, new_fd); - } - g_list_free(group_list); -} - - -static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars, GHashTable *basename_hash) +static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean disable_sidecars) { gchar *path_utf8 = path_to_utf8(path); - FileData *ret = file_data_new(path_utf8, st, check_sidecars, basename_hash); + FileData *ret = file_data_new(path_utf8, st, disable_sidecars); g_free(path_utf8); return ret; } -FileData *file_data_new_simple(const gchar *path_utf8) +FileData *file_data_new_no_grouping(const gchar *path_utf8) { struct stat st; @@ -602,41 +406,31 @@ st.st_mtime = 0; } - return file_data_new(path_utf8, &st, TRUE, NULL); + return file_data_new(path_utf8, &st, TRUE); } -FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd) -{ - sfd->parent = target; - if (!g_list_find(target->sidecar_files, sfd)) - target->sidecar_files = g_list_prepend(target->sidecar_files, sfd); - file_data_increment_version(sfd); /* increments both sfd and target */ - return target; -} - - -FileData *file_data_merge_sidecar_files(FileData *target, FileData *source) +FileData *file_data_new_dir(const gchar *path_utf8) { - GList *work; - - file_data_add_sidecar_file(target, source); - - work = source->sidecar_files; - while (work) + struct stat st; + + if (!stat_utf8(path_utf8, &st)) { - FileData *sfd = work->data; - file_data_add_sidecar_file(target, sfd); - work = work->next; + st.st_size = 0; + st.st_mtime = 0; } - - g_list_free(source->sidecar_files); - source->sidecar_files = NULL; - - target->sidecar_files = filelist_sort(target->sidecar_files, SORT_NAME, TRUE); - - return target; + else + /* dir or non-existing yet */ + g_assert(S_ISDIR(st.st_mode)); + + return file_data_new(path_utf8, &st, TRUE); } +/* + *----------------------------------------------------------------------------- + * reference counting + *----------------------------------------------------------------------------- + */ + #ifdef DEBUG_FILEDATA FileData *file_data_ref_debug(const gchar *file, gint line, FileData *fd) #else @@ -664,6 +458,7 @@ g_assert(fd->magick == 0x12345678); g_assert(fd->ref == 0); + metadata_cache_free(fd); g_hash_table_remove(file_data_pool, fd->original_path); g_free(fd->path); @@ -732,7 +527,110 @@ } } -FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd) + + +/* + *----------------------------------------------------------------------------- + * sidecar file info struct + *----------------------------------------------------------------------------- + */ + +static gint file_data_sort_by_ext(gconstpointer a, gconstpointer b) +{ + const FileData *fda = a; + const FileData *fdb = b; + + if (fda->sidecar_priority < fdb->sidecar_priority) return -1; + if (fda->sidecar_priority > fdb->sidecar_priority) return 1; + + return strcmp(fdb->extension, fda->extension); +} + + +static gint sidecar_file_priority(const gchar *extension) +{ + gint i = 1; + GList *work; + + if (extension == NULL) + return 0; + + work = sidecar_ext_get_list(); + + while (work) { + gchar *ext = work->data; + + work = work->next; + if (g_ascii_strcasecmp(extension, ext) == 0) return i; + i++; + } + return 0; +} + +static FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd) +{ + sfd->parent = target; + if (!g_list_find(target->sidecar_files, sfd)) + target->sidecar_files = g_list_insert_sorted(target->sidecar_files, sfd, file_data_sort_by_ext); + file_data_increment_version(sfd); /* increments both sfd and target */ + return target; +} + + +static FileData *file_data_merge_sidecar_files(FileData *target, FileData *source) +{ + GList *work; + + file_data_add_sidecar_file(target, source); + + work = source->sidecar_files; + while (work) + { + FileData *sfd = work->data; + file_data_add_sidecar_file(target, sfd); + work = work->next; + } + + g_list_free(source->sidecar_files); + source->sidecar_files = NULL; + + return target; +} + +static void file_data_check_sidecars(const GList *basename_list) +{ + GList *work; + FileData *parent_fd; + if (!basename_list) return; + /* process the group list - the first one is the parent file, others are sidecars */ + parent_fd = basename_list->data; + work = basename_list->next; + while (work) + { + FileData *sfd = work->data; + work = work->next; + + file_data_merge_sidecar_files(parent_fd, sfd); + } + + /* there may be some sidecars that are already deleted - disconnect them */ + work = parent_fd->sidecar_files; + while (work) + { + FileData *sfd = work->data; + work = work->next; + + if (!g_list_find((GList *)basename_list, sfd)) + { + printf("removing unknown %s: %s \n", parent_fd->path, sfd->path); + file_data_disconnect_sidecar_file(parent_fd, sfd); + file_data_send_notification(sfd, NOTIFY_REREAD); + file_data_send_notification(parent_fd, NOTIFY_REREAD); + } + } +} + +static FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd) { sfd->parent = target; g_assert(g_list_find(target->sidecar_files, sfd)); @@ -778,7 +676,7 @@ file_data_disconnect_sidecar_file(fd, sfd); file_data_send_notification(sfd, NOTIFY_GROUPING); } - file_data_check_sidecars((FileData *)sidecar_files->data, FALSE); /* this will group the sidecars back together */ + file_data_check_sidecars(sidecar_files); /* this will group the sidecars back together */ filelist_free(sidecar_files); } else @@ -789,7 +687,7 @@ else { file_data_increment_version(fd); - file_data_check_sidecars(fd, FALSE); + /* file_data_check_sidecars call is not necessary - the file will be re-grouped on next dir read */ } file_data_send_notification(fd, NOTIFY_GROUPING); } @@ -809,105 +707,10 @@ } -/* compare name without extension */ -gint file_data_compare_name_without_ext(FileData *fd1, FileData *fd2) -{ - size_t len1 = fd1->extension - fd1->name; - size_t len2 = fd2->extension - fd2->name; - - if (len1 < len2) return -1; - if (len1 > len2) return 1; - - return strncmp(fd1->name, fd2->name, len1); /* FIXME: utf8 */ -} - -void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd) -{ - if (!fdci && fd) fdci = fd->change; - - if (!fdci) return; - - g_free(fdci->source); - g_free(fdci->dest); - - g_free(fdci); - - if (fd) fd->change = NULL; -} - -static gboolean file_data_can_write_directly(FileData *fd) -{ - return filter_name_is_writable(fd->extension); -} - -static gboolean file_data_can_write_sidecar(FileData *fd) -{ - return filter_name_allow_sidecar(fd->extension) && !filter_name_is_writable(fd->extension); -} - -gchar *file_data_get_sidecar_path(FileData *fd, gboolean existing_only) -{ - gchar *sidecar_path = NULL; - GList *work; - - if (!file_data_can_write_sidecar(fd)) return NULL; - - work = fd->parent ? fd->parent->sidecar_files : fd->sidecar_files; - while (work) - { - FileData *sfd = work->data; - work = work->next; - if (g_ascii_strcasecmp(sfd->extension, ".xmp") == 0) - { - sidecar_path = g_strdup(sfd->path); - break; - } - } - - if (!existing_only && !sidecar_path) - { - gchar *base = remove_extension_from_path(fd->path); - sidecar_path = g_strconcat(base, ".xmp", NULL); - g_free(base); - } - - return sidecar_path; -} - /* *----------------------------------------------------------------------------- - * sidecar file info struct - *----------------------------------------------------------------------------- - */ - - - -static gint sidecar_file_priority(const gchar *path) -{ - const gchar *extension = extension_from_path(path); - gint i = 1; - GList *work; - - if (extension == NULL) - return 0; - - work = sidecar_ext_get_list(); - - while (work) { - gchar *ext = work->data; - - work = work->next; - if (g_ascii_strcasecmp(extension, ext) == 0) return i; - i++; - } - return 0; -} - - -/* - *----------------------------------------------------------------------------- - * load file list + * filelist sorting *----------------------------------------------------------------------------- */ @@ -917,6 +720,7 @@ gint filelist_sort_compare_filedata(FileData *fa, FileData *fb) { + gint ret; if (!filelist_sort_ascend) { FileData *tmp = fa; @@ -940,7 +744,8 @@ break; #ifdef HAVE_STRVERSCMP case SORT_NUMBER: - return strverscmp(fa->name, fb->name); + ret = strverscmp(fa->name, fb->name); + if (ret != 0) return ret; break; #endif default: @@ -948,9 +753,16 @@ } if (options->file_sort.case_sensitive) - return strcmp(fa->collate_key_name, fb->collate_key_name); + ret = strcmp(fa->collate_key_name, fb->collate_key_name); else - return strcmp(fa->collate_key_name_nocase, fb->collate_key_name_nocase); + ret = strcmp(fa->collate_key_name_nocase, fb->collate_key_name_nocase); + + if (ret != 0) return ret; + + /* do not return 0 unless the files are really the same + file_data_pool ensures that original_path is unique + */ + return strcmp(fa->original_path, fb->original_path); } gint filelist_sort_compare_filedata_full(FileData *fa, FileData *fb, SortType method, gboolean ascend) @@ -989,6 +801,78 @@ return filelist_insert_sort_full(list, fd, method, ascend, (GCompareFunc) filelist_sort_file_cb); } +/* + *----------------------------------------------------------------------------- + * basename hash - grouping of sidecars in filelist + *----------------------------------------------------------------------------- + */ + + +static GHashTable *file_data_basename_hash_new(void) +{ + return g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); +} + +static GList * file_data_basename_hash_insert(GHashTable *basename_hash, FileData *fd) +{ + GList *list; + gchar *basename = g_strndup(fd->path, fd->extension - fd->path); + + list = g_hash_table_lookup(basename_hash, basename); + + if (!g_list_find(list, fd)) + { + list = g_list_insert_sorted(list, file_data_ref(fd), file_data_sort_by_ext); + g_hash_table_insert(basename_hash, basename, list); + } + else + { + g_free(basename); + } + return list; +} + +#if 0 +static void file_data_basename_hash_remove(GHashTable *basename_hash, FileData *fd) +{ + GList *list; + gchar *basename = g_strndup(fd->path, fd->extension - fd->path); + + list = g_hash_table_lookup(basename_hash, basename); + + if (!g_list_find(list, fd)) return; + + list = g_list_remove(list, fd); + file_data_unref(fd); + + if (list) + { + g_hash_table_insert(basename_hash, basename, list); + } + else + { + g_hash_table_remove(basename_hash, basename); + g_free(basename); + } +} +#endif + +static void file_data_basename_hash_remove_list(gpointer key, gpointer value, gpointer data) +{ + filelist_free((GList *)value); +} + +static void file_data_basename_hash_free(GHashTable *basename_hash) +{ + g_hash_table_foreach(basename_hash, file_data_basename_hash_remove_list, NULL); + g_hash_table_destroy(basename_hash); +} + +/* + *----------------------------------------------------------------------------- + * handling sidecars in filelist + *----------------------------------------------------------------------------- + */ static GList *filelist_filter_out_sidecars(GList *flist) { @@ -1010,6 +894,13 @@ return flist_filtered; } +static void file_data_basename_hash_to_sidecars(gpointer key, gpointer value, gpointer data) +{ + GList *basename_list = (GList *)value; + file_data_check_sidecars(basename_list); +} + + static gboolean is_hidden_file(const gchar *name) { if (name[0] != '.') return FALSE; @@ -1017,7 +908,13 @@ return TRUE; } -static gboolean filelist_read_real(FileData *dir_fd, GList **files, GList **dirs, gboolean follow_symlinks) +/* + *----------------------------------------------------------------------------- + * the main filelist function + *----------------------------------------------------------------------------- + */ + +static gboolean filelist_read_real(const gchar *dir_path, GList **files, GList **dirs, gboolean follow_symlinks) { DIR *dp; struct dirent *dir; @@ -1032,7 +929,7 @@ if (files) *files = NULL; if (dirs) *dirs = NULL; - pathl = path_from_utf8(dir_fd->path); + pathl = path_from_utf8(dir_path); if (!pathl) return FALSE; dp = opendir(pathl); @@ -1070,14 +967,19 @@ strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 && strcmp(name, THUMB_FOLDER_LOCAL) != 0) { - dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE, NULL)); + dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, TRUE)); } } else { if (files && filter_name_exists(name)) { - flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE, basename_hash)); + FileData *fd = file_data_new_local(filepath, &ent_sbuf, FALSE); + flist = g_list_prepend(flist, fd); + if (fd->sidecar_priority && !fd->disable_grouping) + { + file_data_basename_hash_insert(basename_hash, fd); + } } } } @@ -1094,24 +996,59 @@ closedir(dp); g_free(pathl); - if (basename_hash) file_data_basename_hash_free(basename_hash); if (dirs) *dirs = dlist; - if (files) *files = filelist_filter_out_sidecars(flist); + if (files) + { + g_hash_table_foreach(basename_hash, file_data_basename_hash_to_sidecars, NULL); + + *files = filelist_filter_out_sidecars(flist); + } + if (basename_hash) file_data_basename_hash_free(basename_hash); return TRUE; } gboolean filelist_read(FileData *dir_fd, GList **files, GList **dirs) { - return filelist_read_real(dir_fd, files, dirs, TRUE); + return filelist_read_real(dir_fd->path, files, dirs, TRUE); } gboolean filelist_read_lstat(FileData *dir_fd, GList **files, GList **dirs) { - return filelist_read_real(dir_fd, files, dirs, FALSE); + return filelist_read_real(dir_fd->path, files, dirs, FALSE); } +FileData *file_data_new_group(const gchar *path_utf8) +{ + gchar *dir; + struct stat st; + FileData *fd; + GList *files; + + if (!stat_utf8(path_utf8, &st)) + { + st.st_size = 0; + st.st_mtime = 0; + } + + if (S_ISDIR(st.st_mode)) + return file_data_new(path_utf8, &st, TRUE); + + dir = remove_level_from_path(path_utf8); + + filelist_read_real(dir, &files, NULL, TRUE); + + fd = g_hash_table_lookup(file_data_pool, path_utf8); + g_assert(fd); + file_data_ref(fd); + + filelist_free(files); + g_free(dir); + return fd; +} + + void filelist_free(GList *list) { GList *work; @@ -1159,7 +1096,7 @@ path = work->data; work = work->next; - new_list = g_list_prepend(new_list, file_data_new_simple(path)); + new_list = g_list_prepend(new_list, file_data_new_group(path)); } return g_list_reverse(new_list); @@ -1274,6 +1211,65 @@ return list; } +/* + *----------------------------------------------------------------------------- + * file modification support + *----------------------------------------------------------------------------- + */ + + +void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd) +{ + if (!fdci && fd) fdci = fd->change; + + if (!fdci) return; + + g_free(fdci->source); + g_free(fdci->dest); + + g_free(fdci); + + if (fd) fd->change = NULL; +} + +static gboolean file_data_can_write_directly(FileData *fd) +{ + return filter_name_is_writable(fd->extension); +} + +static gboolean file_data_can_write_sidecar(FileData *fd) +{ + return filter_name_allow_sidecar(fd->extension) && !filter_name_is_writable(fd->extension); +} + +gchar *file_data_get_sidecar_path(FileData *fd, gboolean existing_only) +{ + gchar *sidecar_path = NULL; + GList *work; + + if (!file_data_can_write_sidecar(fd)) return NULL; + + work = fd->parent ? fd->parent->sidecar_files : fd->sidecar_files; + while (work) + { + FileData *sfd = work->data; + work = work->next; + if (g_ascii_strcasecmp(sfd->extension, ".xmp") == 0) + { + sidecar_path = g_strdup(sfd->path); + break; + } + } + + if (!existing_only && !sidecar_path) + { + gchar *base = g_strndup(fd->path, fd->extension - fd->path); + sidecar_path = g_strconcat(base, ".xmp", NULL); + g_free(base); + } + + return sidecar_path; +} /* * marks and orientation @@ -2555,6 +2551,12 @@ implementation in view_file_list.c */ +typedef struct _NotifyIdleData NotifyIdleData; + +struct _NotifyIdleData { + FileData *fd; + NotifyType type; +}; typedef struct _NotifyData NotifyData; @@ -2628,17 +2630,29 @@ } -void file_data_send_notification(FileData *fd, NotifyType type) +gboolean file_data_send_notification_idle_cb(gpointer data) { + NotifyIdleData *nid = (NotifyIdleData *)data; GList *work = notify_func_list; while (work) { NotifyData *nd = (NotifyData *)work->data; - nd->func(fd, type, nd->data); + nd->func(nid->fd, nid->type, nd->data); work = work->next; } + file_data_unref(nid->fd); + g_free(nid); + return FALSE; +} + +void file_data_send_notification(FileData *fd, NotifyType type) +{ + NotifyIdleData *nid = g_new0(NotifyIdleData, 1); + nid->fd = file_data_ref(fd); + nid->type = type; + g_idle_add_full(G_PRIORITY_HIGH, file_data_send_notification_idle_cb, nid, NULL); } static GHashTable *file_data_monitor_pool = NULL;
--- a/src/filedata.h Fri Apr 22 11:29:35 2011 +0100 +++ b/src/filedata.h Sat Oct 29 23:41:36 2011 +0100 @@ -22,8 +22,14 @@ gchar *text_from_size_abrev(gint64 size); const gchar *text_from_time(time_t t); -/* this expects a utf-8 path */ -FileData *file_data_new_simple(const gchar *path_utf8); +/* scan for sidecar files - expensive */ +FileData *file_data_new_group(const gchar *path_utf8); + +/* should be used on helper files which can't have sidecars */ +FileData *file_data_new_no_grouping(const gchar *path_utf8); + +/* should be used on dirs */ +FileData *file_data_new_dir(const gchar *path_utf8); #ifdef DEBUG_FILEDATA FileData *file_data_ref_debug(const gchar *file, gint line, FileData *fd);
--- a/src/filefilter.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/filefilter.c Sat Oct 29 23:41:36 2011 +0100 @@ -188,6 +188,14 @@ work = work->next; name = gdk_pixbuf_format_get_name(format); + + if (strcmp(name, "Digital camera RAW") == 0) + { + DEBUG_1("Skipped '%s' from loader", name); + g_free(name); + continue; + } + desc = gdk_pixbuf_format_get_description(format); extensions = gdk_pixbuf_format_get_extensions(format); @@ -230,11 +238,16 @@ filter_add_if_missing("ico", "Icon file", ".ico;.cur", FORMAT_CLASS_IMAGE, TRUE, FALSE, FALSE); filter_add_if_missing("ras", "Raster", ".ras", FORMAT_CLASS_IMAGE, TRUE, FALSE, FALSE); filter_add_if_missing("svg", "Scalable Vector Graphics", ".svg", FORMAT_CLASS_IMAGE, TRUE, FALSE, FALSE); - + + /* special formats for stereo */ + filter_add_if_missing("jps", "Stereo side-by-side jpeg", ".jps", FORMAT_CLASS_IMAGE, TRUE, FALSE, TRUE); + filter_add_if_missing("mpo", "Stereo multi-image jpeg", ".mpo", FORMAT_CLASS_IMAGE, FALSE, TRUE, TRUE); + /* non-image files that might be desirable to show */ filter_add_if_missing("xmp", "XMP sidecar", ".xmp", FORMAT_CLASS_META, TRUE, FALSE, TRUE); filter_add_if_missing("gqv", GQ_APPNAME " image collection", GQ_COLLECTION_EXT, FORMAT_CLASS_META, FALSE, FALSE, TRUE); filter_add_if_missing("ufraw", "UFRaw ID file", ".ufraw", FORMAT_CLASS_META, FALSE, FALSE, TRUE); + filter_add_if_missing("pto", "Panorama script file", ".pto", FORMAT_CLASS_META, FALSE, FALSE, TRUE); /* These are the raw camera formats with embedded jpeg/exif. * (see format_raw.c and/or exiv2.cc) @@ -301,6 +314,20 @@ return list; } +static gint filter_sort_ext_len_cb(gconstpointer a, gconstpointer b) +{ + gchar *sa = (gchar *)a; + gchar *sb = (gchar *)b; + + gint len_a = strlen(sa); + gint len_b = strlen(sb); + + if (len_a > len_b) return -1; + if (len_a < len_b) return 1; + return 0; +} + + void filter_rebuild(void) { GList *work; @@ -361,10 +388,13 @@ } } + /* make sure registered_extension_from_path finds the longer match first */ + extension_list = g_list_sort(extension_list, filter_sort_ext_len_cb); sidecar_ext_parse(options->sidecar.ext); /* this must be updated after changed file extensions */ } -static gboolean filter_name_find(GList *filter, const gchar *name) +/* return the extension part of the name or NULL */ +static const gchar *filter_name_find(GList *filter, const gchar *name) { GList *work; guint ln; @@ -379,20 +409,23 @@ if (ln >= lf) { /* FIXME: utf8 */ - if (g_ascii_strncasecmp(name + ln - lf, filter, lf) == 0) return TRUE; + if (g_ascii_strncasecmp(name + ln - lf, filter, lf) == 0) return name + ln - lf; } work = work->next; } - return FALSE; + return NULL; } - +const gchar *registered_extension_from_path(const gchar *name) +{ + return filter_name_find(extension_list, name); +} gboolean filter_name_exists(const gchar *name) { if (!extension_list || options->file_filter.disable) return TRUE; - return filter_name_find(extension_list, name); + return !!filter_name_find(extension_list, name); } gboolean filter_file_class(const gchar *name, FileFormatClass file_class) @@ -403,17 +436,17 @@ return FALSE; } - return filter_name_find(file_class_extension_list[file_class], name); + return !!filter_name_find(file_class_extension_list[file_class], name); } gboolean filter_name_is_writable(const gchar *name) { - return filter_name_find(file_writable_list, name); + return !!filter_name_find(file_writable_list, name); } gboolean filter_name_allow_sidecar(const gchar *name) { - return filter_name_find(file_sidecar_list, name); + return !!filter_name_find(file_sidecar_list, name); } void filter_write_list(GString *outstr, gint indent)
--- a/src/filefilter.h Fri Apr 22 11:29:35 2011 +0100 +++ b/src/filefilter.h Sat Oct 29 23:41:36 2011 +0100 @@ -37,6 +37,7 @@ void filter_rebuild(void); GList *filter_to_list(const gchar *extensions); +const gchar *registered_extension_from_path(const gchar *name); gboolean filter_name_exists(const gchar *name); gboolean filter_file_class(const gchar *name, FileFormatClass file_class); gboolean filter_name_is_writable(const gchar *name);
--- a/src/fullscreen.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/fullscreen.c Sat Oct 29 23:41:36 2011 +0100 @@ -294,7 +294,7 @@ image_background_set_color_from_options(fs->imd, TRUE); image_set_delay_flip(fs->imd, options->fullscreen.clean_flip); image_auto_refresh_enable(fs->imd, fs->normal_imd->auto_refresh); - + if (options->fullscreen.clean_flip) { image_set_update_func(fs->imd, fullscreen_image_update_cb, fs); @@ -305,6 +305,10 @@ image_change_from_image(fs->imd, fs->normal_imd); + if (options->stereo.enable_fsmode) { + image_stereo_set(fs->imd, options->stereo.fsmode); + } + gtk_widget_show(fs->window); /* for hiding the mouse */ @@ -337,6 +341,11 @@ gdk_keyboard_ungrab(GDK_CURRENT_TIME); image_change_from_image(fs->normal_imd, fs->imd); + + if (options->stereo.enable_fsmode) { + image_stereo_set(fs->normal_imd, options->stereo.mode); + } + #ifdef HIDE_WINDOW_IN_FULLSCREEN gtk_widget_show(fs->normal_window); #endif
--- a/src/image-load.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/image-load.c Sat Oct 29 23:41:36 2011 +0100 @@ -13,6 +13,9 @@ #include "main.h" #include "image-load.h" +#include "image_load_gdk.h" +#include "image_load_jpeg.h" +#include "image_load_tiff.h" #include "exif.h" #include "filedata.h" @@ -315,7 +318,7 @@ /* the following functions may be executed in separate thread */ /* this function expects that il->data_mutex is locked by caller */ -static void image_loader_queue_delayed_erea_ready(ImageLoader *il, guint x, guint y, guint w, guint h) +static void image_loader_queue_delayed_area_ready(ImageLoader *il, guint x, guint y, guint w, guint h) { ImageLoaderAreaParam *par = g_new0(ImageLoaderAreaParam, 1); par->il = il; @@ -354,7 +357,7 @@ return; } - pb = gdk_pixbuf_loader_get_pixbuf(il->loader); + pb = il->backend.get_pixbuf(il->loader); if (pb == il->pixbuf) { @@ -362,6 +365,11 @@ return; } + if (g_ascii_strcasecmp(".jps", il->fd->extension) == 0) + { + g_object_set_data(G_OBJECT(pb), "stereo_data", GINT_TO_POINTER(STEREO_PIXBUF_CROSS)); + } + if (il->pixbuf) g_object_unref(il->pixbuf); il->pixbuf = pb; @@ -370,7 +378,7 @@ g_mutex_unlock(il->data_mutex); } -static void image_loader_area_updated_cb(GdkPixbufLoader *loader, +static void image_loader_area_updated_cb(gpointer loader, guint x, guint y, guint w, guint h, gpointer data) { @@ -387,14 +395,18 @@ g_mutex_lock(il->data_mutex); if (il->delay_area_ready) - image_loader_queue_delayed_erea_ready(il, x, y, w, h); + image_loader_queue_delayed_area_ready(il, x, y, w, h); else image_loader_emit_area_ready(il, x, y, w, h); + + if (il->stopping) il->backend.abort(il->loader); + g_mutex_unlock(il->data_mutex); } -static void image_loader_area_prepared_cb(GdkPixbufLoader *loader, gpointer data) +static void image_loader_area_prepared_cb(gpointer loader, gpointer data) { + ImageLoader *il = data; GdkPixbuf *pb; guchar *pix; size_t h, rs; @@ -403,7 +415,7 @@ http://bugzilla.gnome.org/show_bug.cgi?id=547669 http://bugzilla.gnome.org/show_bug.cgi?id=589334 */ - gchar *format = gdk_pixbuf_format_get_name(gdk_pixbuf_loader_get_format(loader)); + gchar *format = il->backend.get_format_name(loader); if (strcmp(format, "svg") == 0 || strcmp(format, "xpm") == 0) { @@ -413,7 +425,7 @@ g_free(format); - pb = gdk_pixbuf_loader_get_pixbuf(loader); + pb = il->backend.get_pixbuf(loader); h = gdk_pixbuf_get_height(pb); rs = gdk_pixbuf_get_rowstride(pb); @@ -423,11 +435,10 @@ } -static void image_loader_size_cb(GdkPixbufLoader *loader, +static void image_loader_size_cb(gpointer loader, gint width, gint height, gpointer data) { ImageLoader *il = data; - GdkPixbufFormat *format; gchar **mime_types; gboolean scale = FALSE; gint n; @@ -441,10 +452,7 @@ } g_mutex_unlock(il->data_mutex); - format = gdk_pixbuf_loader_get_format(loader); - if (!format) return; - - mime_types = gdk_pixbuf_format_get_mime_types(format); + mime_types = il->backend.get_format_mime_types(loader); n = 0; while (mime_types[n]) { @@ -478,7 +486,7 @@ if (nw < 1) nw = 1; } - gdk_pixbuf_loader_set_size(loader, nw, nh); + il->backend.set_size(loader, nw, nh); il->shrunk = TRUE; } g_mutex_unlock(il->data_mutex); @@ -493,9 +501,9 @@ if (il->loader) { /* some loaders do not have a pixbuf till close, order is important here */ - gdk_pixbuf_loader_close(il->loader, il->error ? NULL : &il->error); /* we are interested in the first error only */ + il->backend.close(il->loader, il->error ? NULL : &il->error); /* we are interested in the first error only */ image_loader_sync_pixbuf(il); - g_object_unref(G_OBJECT(il->loader)); + il->backend.free(il->loader); il->loader = NULL; } g_mutex_lock(il->data_mutex); @@ -506,14 +514,27 @@ static void image_loader_setup_loader(ImageLoader *il) { g_mutex_lock(il->data_mutex); - il->loader = gdk_pixbuf_loader_new(); +#ifdef HAVE_JPEG + if (il->bytes_total >= 2 && il->mapped_file[0] == 0xff && il->mapped_file[1] == 0xd8) + { + DEBUG_1("Using custom jpeg loader"); + image_loader_backend_set_jpeg(&il->backend); + } + else +#endif +#ifdef HAVE_TIFF + if (il->bytes_total >= 10 && + (memcmp(il->mapped_file, "MM\0*", 4) == 0 || + memcmp(il->mapped_file, "II*\0", 4) == 0)) + { + DEBUG_1("Using custom tiff loader"); + image_loader_backend_set_tiff(&il->backend); + } + else +#endif + image_loader_backend_set_default(&il->backend); - g_signal_connect(G_OBJECT(il->loader), "area_updated", - G_CALLBACK(image_loader_area_updated_cb), il); - g_signal_connect(G_OBJECT(il->loader), "size_prepared", - G_CALLBACK(image_loader_size_cb), il); - g_signal_connect(G_OBJECT(il->loader), "area_prepared", - G_CALLBACK(image_loader_area_prepared_cb), il); + il->loader = il->backend.loader_new(image_loader_area_updated_cb, image_loader_size_cb, image_loader_area_prepared_cb, il); g_mutex_unlock(il->data_mutex); } @@ -552,7 +573,7 @@ return FALSE; } - if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, il->mapped_file + il->bytes_read, b, &il->error))) + if (b < 0 || (b > 0 && !il->backend.write(il->loader, il->mapped_file + il->bytes_read, b, &il->error))) { image_loader_error(il); return FALSE; @@ -581,8 +602,17 @@ if (b < 1) return FALSE; image_loader_setup_loader(il); - - if (!gdk_pixbuf_loader_write(il->loader, il->mapped_file + il->bytes_read, b, &il->error)) + + g_assert(il->bytes_read == 0); + if (il->backend.load) { + b = il->bytes_total; + if (!il->backend.load(il->loader, il->mapped_file, b, &il->error)) + { + image_loader_stop_loader(il); + return FALSE; + } + } + else if (!il->backend.write(il->loader, il->mapped_file, b, &il->error)) { image_loader_stop_loader(il); return FALSE; @@ -591,10 +621,10 @@ il->bytes_read += b; /* read until size is known */ - while (il->loader && !gdk_pixbuf_loader_get_pixbuf(il->loader) && b > 0 && !image_loader_get_stopping(il)) + while (il->loader && !il->backend.get_pixbuf(il->loader) && b > 0 && !image_loader_get_stopping(il)) { b = MIN(il->read_buffer_size, il->bytes_total - il->bytes_read); - if (b < 0 || (b > 0 && !gdk_pixbuf_loader_write(il->loader, il->mapped_file + il->bytes_read, b, &il->error))) + if (b < 0 || (b > 0 && !il->backend.write(il->loader, il->mapped_file + il->bytes_read, b, &il->error))) { image_loader_stop_loader(il); return FALSE; @@ -952,16 +982,12 @@ gchar *image_loader_get_format(ImageLoader *il) { - GdkPixbufFormat *format; gchar **mimev; gchar *mime; if (!il || !il->loader) return NULL; - format = gdk_pixbuf_loader_get_format(il->loader); - if (!format) return NULL; - - mimev = gdk_pixbuf_format_get_mime_types(format); + mimev = il->backend.get_format_mime_types(il->loader); if (!mimev) return NULL; /* return first member of mimev, as GdkPixbufLoader has no way to tell us which exact one ? */
--- a/src/image-load.h Fri Apr 22 11:29:35 2011 +0100 +++ b/src/image-load.h Sat Oct 29 23:41:36 2011 +0100 @@ -16,6 +16,37 @@ #define TYPE_IMAGE_LOADER (image_loader_get_type()) +typedef void (*ImageLoaderBackendCbAreaPrepared)(gpointer loader, gpointer data); +typedef void (*ImageLoaderBackendCbSize)(gpointer loader, gint width, gint height, gpointer data); +typedef void (*ImageLoaderBackendCbAreaUpdated)(gpointer loader, guint x, guint y, guint w, guint h, gpointer data); + +typedef gpointer (*ImageLoaderBackendFuncLoaderNew)(ImageLoaderBackendCbAreaUpdated, ImageLoaderBackendCbSize, ImageLoaderBackendCbAreaPrepared, gpointer data); +typedef void (*ImageLoaderBackendFuncSetSize)(gpointer loader, int width, int height); +typedef gboolean (*ImageLoaderBackendFuncLoad)(gpointer loader, const guchar *buf, gsize count, GError **error); /* optional, load whole image at once */ +typedef gboolean (*ImageLoaderBackendFuncWrite)(gpointer loader, const guchar *buf, gsize count, GError **error); +typedef GdkPixbuf* (*ImageLoaderBackendFuncGetPixbuf)(gpointer loader); +typedef gboolean (*ImageLoaderBackendFuncClose)(gpointer loader, GError **error); +typedef void (*ImageLoaderBackendFuncAbort)(gpointer loader); +typedef void (*ImageLoaderBackendFuncFree)(gpointer loader); +typedef gchar* (*ImageLoaderBackendFuncGetFormatName)(gpointer loader); +typedef gchar** (*ImageLoaderBackendFuncGetFormatMimeTypes)(gpointer loader); + +typedef struct _ImageLoaderBackend ImageLoaderBackend; +struct _ImageLoaderBackend +{ + ImageLoaderBackendFuncLoaderNew loader_new; + ImageLoaderBackendFuncSetSize set_size; + ImageLoaderBackendFuncLoad load; + ImageLoaderBackendFuncWrite write; + ImageLoaderBackendFuncGetPixbuf get_pixbuf; + ImageLoaderBackendFuncClose close; + ImageLoaderBackendFuncAbort abort; + ImageLoaderBackendFuncFree free; + ImageLoaderBackendFuncGetFormatName get_format_name; + ImageLoaderBackendFuncGetFormatMimeTypes get_format_mime_types; +}; + + //typedef struct _ImageLoader ImageLoader; typedef struct _ImageLoaderClass ImageLoaderClass; @@ -41,8 +72,9 @@ guint idle_id; /* event source id */ gint idle_priority; - GdkPixbufLoader *loader; + gpointer *loader; GError *error; + ImageLoaderBackend backend; guint idle_done_id; /* event source id */ GList *area_param_list;
--- a/src/image.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/image.c Sat Oct 29 23:41:36 2011 +0100 @@ -1105,7 +1105,7 @@ void image_change_pixbuf(ImageWindow *imd, GdkPixbuf *pixbuf, gdouble zoom, gboolean lazy) { - + StereoPixbufData stereo_data = STEREO_PIXBUF_DEFAULT; /* read_exif and similar functions can actually notice that the file has changed and trigger a notification that removes the pixbuf from cache and unrefs it. Therefore we must ref it here before it is taken over by the renderer. */ @@ -1124,6 +1124,15 @@ } } + if (pixbuf) + { + stereo_data = imd->user_stereo; + if (stereo_data == STEREO_PIXBUF_DEFAULT) + { + stereo_data = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(pixbuf), "stereo_data")); + } + } + pixbuf_renderer_set_post_process_func((PixbufRenderer *)imd->pr, NULL, NULL, FALSE); if (imd->cm) { @@ -1133,12 +1142,13 @@ if (lazy) { - pixbuf_renderer_set_pixbuf_lazy((PixbufRenderer *)imd->pr, pixbuf, zoom, imd->orientation); + pixbuf_renderer_set_pixbuf_lazy((PixbufRenderer *)imd->pr, pixbuf, zoom, imd->orientation, stereo_data); } else { pixbuf_renderer_set_pixbuf((PixbufRenderer *)imd->pr, pixbuf, zoom); pixbuf_renderer_set_orientation((PixbufRenderer *)imd->pr, imd->orientation); + pixbuf_renderer_set_stereo_data((PixbufRenderer *)imd->pr, stereo_data); } if (pixbuf) g_object_unref(pixbuf); @@ -1263,6 +1273,8 @@ imd->orientation = source->orientation; imd->desaturate = source->desaturate; + imd->user_stereo = source->user_stereo; + pixbuf_renderer_move(PIXBUF_RENDERER(imd->pr), PIXBUF_RENDERER(source->pr)); if (imd->cm || imd->desaturate) @@ -1340,11 +1352,11 @@ if (vertical) { - zoom = (gdouble)pr->window_height / height; + zoom = (gdouble)pr->viewport_height / height; } else { - zoom = (gdouble)pr->window_width / width; + zoom = (gdouble)pr->viewport_width / width; } if (zoom < 1.0) @@ -1424,6 +1436,36 @@ return zoom; } +/* stereo */ +gint image_stereo_get(ImageWindow *imd) +{ + return pixbuf_renderer_stereo_get((PixbufRenderer *)imd->pr); +} + +void image_stereo_set(ImageWindow *imd, gint stereo_mode) +{ + DEBUG_1("Setting stereo mode %04x for imd %p", stereo_mode, imd); + pixbuf_renderer_stereo_set((PixbufRenderer *)imd->pr, stereo_mode); +} + +void image_stereo_swap(ImageWindow *imd) +{ + gint stereo_mode = pixbuf_renderer_stereo_get((PixbufRenderer *)imd->pr); + stereo_mode ^= PR_STEREO_SWAP; + pixbuf_renderer_stereo_set((PixbufRenderer *)imd->pr, stereo_mode); +} + +StereoPixbufData image_stereo_pixbuf_get(ImageWindow *imd) +{ + return imd->user_stereo; +} + +void image_stereo_pixbuf_set(ImageWindow *imd, StereoPixbufData stereo_mode) +{ + imd->user_stereo = stereo_mode; + image_reload(imd); +} + /* read ahead */ void image_prebuffer_set(ImageWindow *imd, FileData *fd) @@ -1671,6 +1713,12 @@ NULL); pixbuf_renderer_set_parent((PixbufRenderer *)imd->pr, (GtkWindow *)imd->top_window); + + image_stereo_set(imd, options->stereo.mode); + pixbuf_renderer_stereo_fixed_set((PixbufRenderer *)imd->pr, + options->stereo.fixed_w, options->stereo.fixed_h, + options->stereo.fixed_x1, options->stereo.fixed_y1, + options->stereo.fixed_x2, options->stereo.fixed_y2); } void image_options_sync(void)
--- a/src/image.h Fri Apr 22 11:29:35 2011 +0100 +++ b/src/image.h Sat Oct 29 23:41:36 2011 +0100 @@ -91,6 +91,14 @@ gchar *image_zoom_get_as_text(ImageWindow *imd); gdouble image_zoom_get_default(ImageWindow *imd); +/* stereo */ +gint image_stereo_get(ImageWindow *imd); +void image_stereo_set(ImageWindow *imd, gint stereo_mode); +void image_stereo_swap(ImageWindow *imd); + +StereoPixbufData image_stereo_pixbuf_get(ImageWindow *imd); +void image_stereo_pixbuf_set(ImageWindow *imd, StereoPixbufData stereo_mode); + /* read ahead, pass NULL to cancel */ void image_prebuffer_set(ImageWindow *imd, FileData *fd);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/image_load_gdk.c Sat Oct 29 23:41:36 2011 +0100 @@ -0,0 +1,62 @@ +/* + * Geeqie + * (C) 2004 John Ellis + * Copyright (C) 2008 - 2011 The Geeqie Team + * + * Author: Vladimir Nadvornik + * + * 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 "main.h" +#include "image-load.h" +#include "image_load_gdk.h" + + +static gchar* image_loader_gdk_get_format_name(gpointer loader) +{ + return gdk_pixbuf_format_get_name(gdk_pixbuf_loader_get_format(GDK_PIXBUF_LOADER(loader))); +} +static gchar** image_loader_gdk_get_format_mime_types(gpointer loader) +{ + return gdk_pixbuf_format_get_mime_types(gdk_pixbuf_loader_get_format(GDK_PIXBUF_LOADER(loader))); +} + +static gpointer image_loader_gdk_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data) +{ + GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); + + g_signal_connect(G_OBJECT(loader), "area_updated", G_CALLBACK(area_updated_cb), data); + g_signal_connect(G_OBJECT(loader), "size_prepared", G_CALLBACK(size_cb), data); + g_signal_connect(G_OBJECT(loader), "area_prepared", G_CALLBACK(area_prepared_cb), data); + return (gpointer) loader; +} + +static void image_loader_gdk_abort(gpointer loader) +{ +} + +static void image_loader_gdk_free(gpointer loader) +{ + g_object_unref(G_OBJECT(loader)); +} + +void image_loader_backend_set_default(ImageLoaderBackend *funcs) +{ + funcs->loader_new = image_loader_gdk_new; + funcs->set_size = (ImageLoaderBackendFuncSetSize) gdk_pixbuf_loader_set_size; + funcs->load = NULL; + funcs->write = (ImageLoaderBackendFuncWrite) gdk_pixbuf_loader_write; + funcs->get_pixbuf = (ImageLoaderBackendFuncGetPixbuf) gdk_pixbuf_loader_get_pixbuf; + funcs->close = (ImageLoaderBackendFuncClose) gdk_pixbuf_loader_close; + funcs->abort = image_loader_gdk_abort; + funcs->free = image_loader_gdk_free; + + funcs->get_format_name = image_loader_gdk_get_format_name; + funcs->get_format_mime_types = image_loader_gdk_get_format_mime_types; +} + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/image_load_gdk.h Sat Oct 29 23:41:36 2011 +0100 @@ -0,0 +1,19 @@ +/* + * Geeqie + * (C) 2004 John Ellis + * Copyright (C) 2008 - 2011 The Geeqie Team + * + * Author: Vladimir Nadvornik + * + * 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 IMAGE_LOAD_GDK_H +#define IMAGE_LOAD_GDK_H + +void image_loader_backend_set_default(ImageLoaderBackend *funcs); + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/image_load_jpeg.c Sat Oct 29 23:41:36 2011 +0100 @@ -0,0 +1,500 @@ +/* + * Geeqie + * (C) 2004 John Ellis + * Copyright (C) 2008 - 2011 The Geeqie Team + * + * Author: Vladimir Nadvornik + * + * 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! + */ + +/* based on code from GdkPixbuf library - JPEG image loader + * + * Copyright (C) 1999 Michael Zucchi + * Copyright (C) 1999 The Free Software Foundation + * + * Progressive loading code Copyright (C) 1999 Red Hat, Inc. + * + * Authors: Michael Zucchi <zucchi@zedzone.mmc.com.au> + * Federico Mena-Quintero <federico@gimp.org> + * Michael Fulbright <drmike@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#include "main.h" + +#include "image-load.h" +#include "image_load_jpeg.h" +#include "jpeg_parser.h" + +#ifdef HAVE_JPEG + +#include <setjmp.h> +#include <jpeglib.h> +#include <jerror.h> + +typedef struct _ImageLoaderJpeg ImageLoaderJpeg; +struct _ImageLoaderJpeg { + ImageLoaderBackendCbAreaUpdated area_updated_cb; + ImageLoaderBackendCbSize size_cb; + ImageLoaderBackendCbAreaPrepared area_prepared_cb; + + gpointer data; + + GdkPixbuf *pixbuf; + guint requested_width; + guint requested_height; + + gboolean abort; + gboolean stereo; + +}; + +/* error handler data */ +struct error_handler_data { + struct jpeg_error_mgr pub; + sigjmp_buf setjmp_buffer; + GError **error; +}; + +/* explode gray image data from jpeg library into rgb components in pixbuf */ +static void +explode_gray_into_buf (struct jpeg_decompress_struct *cinfo, + guchar **lines) +{ + gint i, j; + guint w; + + g_return_if_fail (cinfo != NULL); + g_return_if_fail (cinfo->output_components == 1); + g_return_if_fail (cinfo->out_color_space == JCS_GRAYSCALE); + + /* Expand grey->colour. Expand from the end of the + * memory down, so we can use the same buffer. + */ + w = cinfo->output_width; + for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) { + guchar *from, *to; + + from = lines[i] + w - 1; + to = lines[i] + (w - 1) * 3; + for (j = w - 1; j >= 0; j--) { + to[0] = from[0]; + to[1] = from[0]; + to[2] = from[0]; + to -= 3; + from--; + } + } +} + + +static void +convert_cmyk_to_rgb (struct jpeg_decompress_struct *cinfo, + guchar **lines) +{ + gint i, j; + + g_return_if_fail (cinfo != NULL); + g_return_if_fail (cinfo->output_components == 4); + g_return_if_fail (cinfo->out_color_space == JCS_CMYK); + + for (i = cinfo->rec_outbuf_height - 1; i >= 0; i--) { + guchar *p; + + p = lines[i]; + for (j = 0; j < cinfo->output_width; j++) { + int c, m, y, k; + c = p[0]; + m = p[1]; + y = p[2]; + k = p[3]; + if (cinfo->saw_Adobe_marker) { + p[0] = k*c / 255; + p[1] = k*m / 255; + p[2] = k*y / 255; + } + else { + p[0] = (255 - k)*(255 - c) / 255; + p[1] = (255 - k)*(255 - m) / 255; + p[2] = (255 - k)*(255 - y) / 255; + } + p[3] = 255; + p += 4; + } + } +} + + +static gpointer image_loader_jpeg_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data) +{ + ImageLoaderJpeg *loader = g_new0(ImageLoaderJpeg, 1); + + loader->area_updated_cb = area_updated_cb; + loader->size_cb = size_cb; + loader->area_prepared_cb = area_prepared_cb; + loader->data = data; + return (gpointer) loader; +} + +static void +fatal_error_handler (j_common_ptr cinfo) +{ + struct error_handler_data *errmgr; + char buffer[JMSG_LENGTH_MAX]; + + errmgr = (struct error_handler_data *) cinfo->err; + + /* Create the message */ + (* cinfo->err->format_message) (cinfo, buffer); + + /* broken check for *error == NULL for robustness against + * crappy JPEG library + */ + if (errmgr->error && *errmgr->error == NULL) { + g_set_error (errmgr->error, + GDK_PIXBUF_ERROR, + cinfo->err->msg_code == JERR_OUT_OF_MEMORY + ? GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY + : GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("Error interpreting JPEG image file (%s)"), + buffer); + } + + siglongjmp (errmgr->setjmp_buffer, 1); + + g_assert_not_reached (); +} + +static void +output_message_handler (j_common_ptr cinfo) +{ + /* This method keeps libjpeg from dumping crap to stderr */ + + /* do nothing */ +} + + +void image_loader_jpeg_read_scanline(struct jpeg_decompress_struct *cinfo, guchar **dptr, guint rowstride) +{ + guchar *lines[4]; + guchar **lptr; + gint i; + + lptr = lines; + for (i = 0; i < cinfo->rec_outbuf_height; i++) + { + *lptr++ = *dptr; + *dptr += rowstride; + } + + jpeg_read_scanlines (cinfo, lines, cinfo->rec_outbuf_height); + + switch (cinfo->out_color_space) + { + case JCS_GRAYSCALE: + explode_gray_into_buf (cinfo, lines); + break; + case JCS_RGB: + /* do nothing */ + break; + case JCS_CMYK: + convert_cmyk_to_rgb (cinfo, lines); + break; + default: + break; + } +} + + +static void init_source (j_decompress_ptr cinfo) {} +static boolean fill_input_buffer (j_decompress_ptr cinfo) +{ + ERREXIT(cinfo, JERR_INPUT_EMPTY); + return TRUE; +} +static void skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + struct jpeg_source_mgr* src = (struct jpeg_source_mgr*) cinfo->src; + + if (num_bytes > src->bytes_in_buffer) + { + ERREXIT(cinfo, JERR_INPUT_EOF); + } + else if (num_bytes > 0) + { + src->next_input_byte += (size_t) num_bytes; + src->bytes_in_buffer -= (size_t) num_bytes; + } +} +static void term_source (j_decompress_ptr cinfo) {} +static void set_mem_src (j_decompress_ptr cinfo, void* buffer, long nbytes) +{ + struct jpeg_source_mgr* src; + + if (cinfo->src == NULL) + { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) ( + (j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof(struct jpeg_source_mgr)); + } + + src = (struct jpeg_source_mgr*) cinfo->src; + src->init_source = init_source; + src->fill_input_buffer = fill_input_buffer; + src->skip_input_data = skip_input_data; + src->resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->term_source = term_source; + src->bytes_in_buffer = nbytes; + src->next_input_byte = (JOCTET*)buffer; +} + + +static gboolean image_loader_jpeg_load (gpointer loader, const guchar *buf, gsize count, GError **error) +{ + ImageLoaderJpeg *lj = (ImageLoaderJpeg *) loader; + struct jpeg_decompress_struct cinfo; + struct jpeg_decompress_struct cinfo2; + guchar *dptr, *dptr2; + guint rowstride; + guchar *stereo_buf2 = NULL; + guint stereo_length = 0; + + struct error_handler_data jerr; + + lj->stereo = FALSE; + + MPOData *mpo = jpeg_get_mpo_data(buf, count); + if (mpo && mpo->num_images > 1) + { + guint i; + gint idx1 = -1, idx2 = -1; + guint num2 = 1; + + for (i = 0; i < mpo->num_images; i++) + { + if (mpo->images[i].type_code == 0x20002) + { + if (mpo->images[i].MPIndividualNum == 1) + { + idx1 = i; + } + else if (mpo->images[i].MPIndividualNum > num2) + { + idx2 = i; + num2 = mpo->images[i].MPIndividualNum; + } + } + } + + if (idx1 >= 0 && idx2 >= 0) + { + lj->stereo = TRUE; + stereo_buf2 = (unsigned char *)buf + mpo->images[idx2].offset; + stereo_length = mpo->images[idx2].length; + buf = (unsigned char *)buf + mpo->images[idx1].offset; + count = mpo->images[idx1].length; + } + } + jpeg_mpo_data_free(mpo); + + /* setup error handler */ + cinfo.err = jpeg_std_error (&jerr.pub); + if (lj->stereo) cinfo2.err = jpeg_std_error (&jerr.pub); + jerr.pub.error_exit = fatal_error_handler; + jerr.pub.output_message = output_message_handler; + + jerr.error = error; + + + if (setjmp(jerr.setjmp_buffer)) + { + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object, close the input file, and return. + */ + jpeg_destroy_decompress(&cinfo); + if (lj->stereo) jpeg_destroy_decompress(&cinfo2); + return FALSE; + } + + jpeg_create_decompress(&cinfo); + + set_mem_src(&cinfo, (unsigned char *)buf, count); + + + jpeg_read_header(&cinfo, TRUE); + + if (lj->stereo) + { + jpeg_create_decompress(&cinfo2); + set_mem_src(&cinfo2, stereo_buf2, stereo_length); + jpeg_read_header(&cinfo2, TRUE); + + if (cinfo.image_width != cinfo2.image_width || + cinfo.image_height != cinfo2.image_height) + { + DEBUG_1("stereo data with different size"); + jpeg_destroy_decompress(&cinfo2); + lj->stereo = FALSE; + } + } + + + + lj->requested_width = lj->stereo ? cinfo.image_width * 2: cinfo.image_width; + lj->requested_height = cinfo.image_height; + lj->size_cb(loader, lj->requested_width, lj->requested_height, lj->data); + + cinfo.scale_num = 1; + for (cinfo.scale_denom = 2; cinfo.scale_denom <= 8; cinfo.scale_denom *= 2) { + jpeg_calc_output_dimensions(&cinfo); + if (cinfo.output_width < (lj->stereo ? lj->requested_width / 2 : lj->requested_width) || cinfo.output_height < lj->requested_height) { + cinfo.scale_denom /= 2; + break; + } + } + jpeg_calc_output_dimensions(&cinfo); + if (lj->stereo) + { + cinfo2.scale_num = cinfo.scale_num; + cinfo2.scale_denom = cinfo.scale_denom; + jpeg_calc_output_dimensions(&cinfo2); + jpeg_start_decompress(&cinfo2); + } + + + jpeg_start_decompress(&cinfo); + + + if (lj->stereo) + { + if (cinfo.output_width != cinfo2.output_width || + cinfo.output_height != cinfo2.output_height || + cinfo.out_color_components != cinfo2.out_color_components) + { + DEBUG_1("stereo data with different output size"); + jpeg_destroy_decompress(&cinfo2); + lj->stereo = FALSE; + } + } + + + lj->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, + cinfo.out_color_components == 4 ? TRUE : FALSE, + 8, lj->stereo ? cinfo.output_width * 2: cinfo.output_width, cinfo.output_height); + + if (!lj->pixbuf) + { + jpeg_destroy_decompress (&cinfo); + if (lj->stereo) jpeg_destroy_decompress (&cinfo2); + return 0; + } + if (lj->stereo) g_object_set_data(G_OBJECT(lj->pixbuf), "stereo_data", GINT_TO_POINTER(STEREO_PIXBUF_CROSS)); + lj->area_prepared_cb(loader, lj->data); + + rowstride = gdk_pixbuf_get_rowstride(lj->pixbuf); + dptr = gdk_pixbuf_get_pixels(lj->pixbuf); + dptr2 = gdk_pixbuf_get_pixels(lj->pixbuf) + ((cinfo.out_color_components == 4) ? 4 * cinfo.output_width : 3 * cinfo.output_width); + + + while (cinfo.output_scanline < cinfo.output_height && !lj->abort) + { + guint scanline = cinfo.output_scanline; + image_loader_jpeg_read_scanline(&cinfo, &dptr, rowstride); + lj->area_updated_cb(loader, 0, scanline, cinfo.output_width, cinfo.rec_outbuf_height, lj->data); + if (lj->stereo) + { + guint scanline = cinfo2.output_scanline; + image_loader_jpeg_read_scanline(&cinfo2, &dptr2, rowstride); + lj->area_updated_cb(loader, cinfo.output_width, scanline, cinfo2.output_width, cinfo2.rec_outbuf_height, lj->data); + } + } + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + if (lj->stereo) + { + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + } + + return TRUE; +} + +static void image_loader_jpeg_set_size(gpointer loader, int width, int height) +{ + ImageLoaderJpeg *lj = (ImageLoaderJpeg *) loader; + lj->requested_width = width; + lj->requested_height = height; +} + +static GdkPixbuf* image_loader_jpeg_get_pixbuf(gpointer loader) +{ + ImageLoaderJpeg *lj = (ImageLoaderJpeg *) loader; + return lj->pixbuf; +} + +static gchar* image_loader_jpeg_get_format_name(gpointer loader) +{ + return g_strdup("jpeg"); +} +static gchar** image_loader_jpeg_get_format_mime_types(gpointer loader) +{ + static gchar *mime[] = {"image/jpeg", NULL}; + return g_strdupv(mime); +} + +static gboolean image_loader_jpeg_close(gpointer loader, GError **error) +{ + return TRUE; +} + +static void image_loader_jpeg_abort(gpointer loader) +{ + ImageLoaderJpeg *lj = (ImageLoaderJpeg *) loader; + lj->abort = TRUE; +} + +static void image_loader_jpeg_free(gpointer loader) +{ + ImageLoaderJpeg *lj = (ImageLoaderJpeg *) loader; + if (lj->pixbuf) g_object_unref(lj->pixbuf); + g_free(lj); +} + + +void image_loader_backend_set_jpeg(ImageLoaderBackend *funcs) +{ + funcs->loader_new = image_loader_jpeg_new; + funcs->set_size = image_loader_jpeg_set_size; + funcs->load = image_loader_jpeg_load; + funcs->write = NULL; + funcs->get_pixbuf = image_loader_jpeg_get_pixbuf; + funcs->close = image_loader_jpeg_close; + funcs->abort = image_loader_jpeg_abort; + funcs->free = image_loader_jpeg_free; + + funcs->get_format_name = image_loader_jpeg_get_format_name; + funcs->get_format_mime_types = image_loader_jpeg_get_format_mime_types; +} + + + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/image_load_jpeg.h Sat Oct 29 23:41:36 2011 +0100 @@ -0,0 +1,21 @@ +/* + * Geeqie + * (C) 2004 John Ellis + * Copyright (C) 2008 - 2011 The Geeqie Team + * + * Author: Vladimir Nadvornik + * + * 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 IMAGE_LOAD_JPEG_H +#define IMAGE_LOAD_JPEG_H + +#ifdef HAVE_JPEG +void image_loader_backend_set_jpeg(ImageLoaderBackend *funcs); +#endif + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/image_load_tiff.c Sat Oct 29 23:41:36 2011 +0100 @@ -0,0 +1,422 @@ +/* + * Geeqie + * (C) 2004 John Ellis + * Copyright (C) 2008 - 2011 The Geeqie Team + * + * Author: Vladimir Nadvornik + * + * 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! + */ + +/* based on code from GdkPixbuf library - TIFF image loader + * + * Copyright (C) 1999 Mark Crichton + * Copyright (C) 1999 The Free Software Foundation + * + * Authors: Mark Crichton <crichton@gimp.org> + * Federico Mena-Quintero <federico@gimp.org> + * Jonathan Blandford <jrb@redhat.com> + * S�ren Sandmann <sandmann@daimi.au.dk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "main.h" + +#include "image-load.h" +#include "image_load_tiff.h" + +#ifdef HAVE_TIFF + +#include <tiffio.h> + +typedef struct _ImageLoaderTiff ImageLoaderTiff; +struct _ImageLoaderTiff { + ImageLoaderBackendCbAreaUpdated area_updated_cb; + ImageLoaderBackendCbSize size_cb; + ImageLoaderBackendCbAreaPrepared area_prepared_cb; + + gpointer data; + + GdkPixbuf *pixbuf; + guint requested_width; + guint requested_height; + + gboolean abort; + + const guchar *buffer; + toff_t used; + toff_t pos; +}; + +static void free_buffer (guchar *pixels, gpointer data) +{ + g_free (pixels); +} + +static tsize_t +tiff_load_read (thandle_t handle, tdata_t buf, tsize_t size) +{ + ImageLoaderTiff *context = (ImageLoaderTiff *)handle; + + if (context->pos + size > context->used) + return 0; + + memcpy (buf, context->buffer + context->pos, size); + context->pos += size; + return size; +} + +static tsize_t +tiff_load_write (thandle_t handle, tdata_t buf, tsize_t size) +{ + return -1; +} + +static toff_t +tiff_load_seek (thandle_t handle, toff_t offset, int whence) +{ + ImageLoaderTiff *context = (ImageLoaderTiff *)handle; + + switch (whence) { + case SEEK_SET: + if (offset > context->used) + return -1; + context->pos = offset; + break; + case SEEK_CUR: + if (offset + context->pos >= context->used) + return -1; + context->pos += offset; + break; + case SEEK_END: + if (offset + context->used > context->used) + return -1; + context->pos = context->used + offset; + break; + default: + return -1; + } + return context->pos; +} + +static int +tiff_load_close (thandle_t context) +{ + return 0; +} + +static toff_t +tiff_load_size (thandle_t handle) +{ + ImageLoaderTiff *context = (ImageLoaderTiff *)handle; + + return context->used; +} + +static int +tiff_load_map_file (thandle_t handle, tdata_t *buf, toff_t *size) +{ + ImageLoaderTiff *context = (ImageLoaderTiff *)handle; + + *buf = (tdata_t *) context->buffer; + *size = context->used; + + return 0; +} + +static void +tiff_load_unmap_file (thandle_t handle, tdata_t data, toff_t offset) +{ +} + +static gboolean image_loader_tiff_load (gpointer loader, const guchar *buf, gsize count, GError **error) +{ + ImageLoaderTiff *lt = (ImageLoaderTiff *) loader; + + TIFF *tiff; + guchar *pixels = NULL; + gint width, height, rowstride, bytes; + uint16 orientation = 0; + uint16 transform = 0; + uint32 rowsperstrip; + + lt->buffer = buf; + lt->used = count; + lt->pos = 0; + + TIFFSetWarningHandler(NULL); + + tiff = TIFFClientOpen ("libtiff-geeqie", "r", lt, + tiff_load_read, tiff_load_write, + tiff_load_seek, tiff_load_close, + tiff_load_size, + tiff_load_map_file, tiff_load_unmap_file); + if (!tiff) + { + DEBUG_1("Failed to open TIFF image"); + return FALSE; + } + + + + if (!TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width)) { + DEBUG_1("Could not get image width (bad TIFF file)"); + TIFFClose(tiff); + return FALSE; + } + + if (!TIFFGetField (tiff, TIFFTAG_IMAGELENGTH, &height)) { + DEBUG_1("Could not get image height (bad TIFF file)"); + TIFFClose(tiff); + return FALSE; + } + + if (width <= 0 || height <= 0) { + DEBUG_1("Width or height of TIFF image is zero"); + TIFFClose(tiff); + return FALSE; + } + + rowstride = width * 4; + if (rowstride / 4 != width) { /* overflow */ + DEBUG_1("Dimensions of TIFF image too large"); + TIFFClose(tiff); + return FALSE; + } + + bytes = height * rowstride; + if (bytes / rowstride != height) { /* overflow */ + DEBUG_1("Dimensions of TIFF image too large"); + TIFFClose(tiff); + return FALSE; + } + + lt->requested_width = width; + lt->requested_height = height; + lt->size_cb(loader, lt->requested_width, lt->requested_height, lt->data); + + pixels = g_try_malloc (bytes); + + if (!pixels) { + DEBUG_1("Insufficient memory to open TIFF file"); + TIFFClose(tiff); + return FALSE; + } + + lt->pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8, + width, height, rowstride, + free_buffer, NULL); + if (!lt->pixbuf) { + g_free (pixels); + DEBUG_1("Insufficient memory to open TIFF file"); + TIFFClose(tiff); + return FALSE; + } + + /* Set the "orientation" key associated with this image. libtiff + orientation handling is odd, so further processing is required + by higher-level functions based on this tag. If the embedded + orientation tag is 1-4, libtiff flips/mirrors the image as + required, and no client processing is required - so we report + no orientation. Orientations 5-8 require rotations which would + swap the width and height of the image. libtiff does not do this. + Instead it interprets orientations 5-8 the same as 1-4. + See http://bugzilla.remotesensing.org/show_bug.cgi?id=1548. + To correct for this, the client must apply the transform normally + used for orientation 5 to both orientations 5 and 7, and apply + the transform normally used for orientation 7 for both + orientations 6 and 8. Then everythings works out OK! */ + + TIFFGetField (tiff, TIFFTAG_ORIENTATION, &orientation); + + switch (orientation) { + case 5: + case 7: + transform = 5; + break; + case 6: + case 8: + transform = 7; + break; + default: + transform = 0; + break; + } + + + lt->area_prepared_cb(loader, lt->data); + + + + if( TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rowsperstrip) ) + { + /* read by strip */ + int row; + guchar *wrk_line = (guchar *)g_malloc(width * sizeof (uint32)); + + + for( row = 0; row < height; row += rowsperstrip ) + { + int rows_to_write, i_row; + + if (lt->abort) { + break; + } + + /* Read the strip into an RGBA array */ + if (!TIFFReadRGBAStrip(tiff, row, (uint32 *)(pixels + row * rowstride))) { + break; + } + + /* + * Figure out the number of scanlines actually in this strip. + */ + if( row + (int)rowsperstrip > height ) + rows_to_write = height - row; + else + rows_to_write = rowsperstrip; + + + + /* + * For some reason the TIFFReadRGBAStrip() function chooses the + * lower left corner as the origin. Vertically mirror scanlines. + */ + for( i_row = 0; i_row < rows_to_write / 2; i_row++ ) + { + guchar *top_line, *bottom_line; + + top_line = pixels + (row + i_row) * rowstride; + bottom_line = pixels + (row + rows_to_write - i_row - 1) * rowstride; + + memcpy(wrk_line, top_line, 4*width); + memcpy(top_line, bottom_line, 4*width); + memcpy(bottom_line, wrk_line, 4*width); + } + lt->area_updated_cb(loader, 0, row, width, rows_to_write, lt->data); + } + g_free(wrk_line); + } + else + { + /* fallback, tiled tiff */ + if (!TIFFReadRGBAImageOriented (tiff, width, height, (uint32 *)pixels, ORIENTATION_TOPLEFT, 1)) + { + TIFFClose(tiff); + return FALSE; + } + +#if G_BYTE_ORDER == G_BIG_ENDIAN + /* Turns out that the packing used by TIFFRGBAImage depends on + * the host byte order... + */ + while (pixels < pixbuf->pixels + bytes) + { + uint32 pixel = *(uint32 *)pixels; + int r = TIFFGetR(pixel); + int g = TIFFGetG(pixel); + int b = TIFFGetB(pixel); + int a = TIFFGetA(pixel); + *pixels++ = r; + *pixels++ = g; + *pixels++ = b; + *pixels++ = a; + } +#endif + + lt->area_updated_cb(loader, 0, 0, width, height, lt->data); + } + TIFFClose(tiff); + + return TRUE; +} + + +static gpointer image_loader_tiff_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data) +{ + ImageLoaderTiff *loader = g_new0(ImageLoaderTiff, 1); + + loader->area_updated_cb = area_updated_cb; + loader->size_cb = size_cb; + loader->area_prepared_cb = area_prepared_cb; + loader->data = data; + return (gpointer) loader; +} + + +static void image_loader_tiff_set_size(gpointer loader, int width, int height) +{ + ImageLoaderTiff *lt = (ImageLoaderTiff *) loader; + lt->requested_width = width; + lt->requested_height = height; +} + +static GdkPixbuf* image_loader_tiff_get_pixbuf(gpointer loader) +{ + ImageLoaderTiff *lt = (ImageLoaderTiff *) loader; + return lt->pixbuf; +} + +static gchar* image_loader_tiff_get_format_name(gpointer loader) +{ + return g_strdup("tiff"); +} +static gchar** image_loader_tiff_get_format_mime_types(gpointer loader) +{ + static gchar *mime[] = {"image/tiff", NULL}; + return g_strdupv(mime); +} + +static gboolean image_loader_tiff_close(gpointer loader, GError **error) +{ + return TRUE; +} + +static void image_loader_tiff_abort(gpointer loader) +{ + ImageLoaderTiff *lt = (ImageLoaderTiff *) loader; + lt->abort = TRUE; +} + +static void image_loader_tiff_free(gpointer loader) +{ + ImageLoaderTiff *lt = (ImageLoaderTiff *) loader; + if (lt->pixbuf) g_object_unref(lt->pixbuf); + g_free(lt); +} + + +void image_loader_backend_set_tiff(ImageLoaderBackend *funcs) +{ + funcs->loader_new = image_loader_tiff_new; + funcs->set_size = image_loader_tiff_set_size; + funcs->load = image_loader_tiff_load; + funcs->write = NULL; + funcs->get_pixbuf = image_loader_tiff_get_pixbuf; + funcs->close = image_loader_tiff_close; + funcs->abort = image_loader_tiff_abort; + funcs->free = image_loader_tiff_free; + + funcs->get_format_name = image_loader_tiff_get_format_name; + funcs->get_format_mime_types = image_loader_tiff_get_format_mime_types; +} + + + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/image_load_tiff.h Sat Oct 29 23:41:36 2011 +0100 @@ -0,0 +1,21 @@ +/* + * Geeqie + * (C) 2004 John Ellis + * Copyright (C) 2008 - 2011 The Geeqie Team + * + * Author: Vladimir Nadvornik + * + * 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 IMAGE_LOAD_TIFF_H +#define IMAGE_LOAD_TIFF_H + +#ifdef HAVE_TIFF +void image_loader_backend_set_tiff(ImageLoaderBackend *funcs); +#endif + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jpeg_parser.c Sat Oct 29 23:41:36 2011 +0100 @@ -0,0 +1,386 @@ +/* + * Geeqie + * (C) 2004 John Ellis + * Copyright (C) 2008 - 2011 The Geeqie Team + * + * Author: Vladimir Nadvornik + * + * 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 "main.h" +#include "jpeg_parser.h" + +gboolean jpeg_segment_find(guchar *data, guint size, + guchar app_marker, const gchar *magic, guint magic_len, + guint *seg_offset, guint *seg_length) +{ + guchar marker = 0; + guint offset = 0; + guint length = 0; + + while (marker != JPEG_MARKER_EOI) + { + offset += length; + length = 2; + + if (offset + 2 >= size || + data[offset] != JPEG_MARKER) return FALSE; + + marker = data[offset + 1]; + if (marker != JPEG_MARKER_SOI && + marker != JPEG_MARKER_EOI) + { + if (offset + 4 >= size) return FALSE; + length += ((guint)data[offset + 2] << 8) + data[offset + 3]; + + if (marker == app_marker && + offset + length < size && + length >= 4 + magic_len && + memcmp(data + offset + 4, magic, magic_len) == 0) + { + *seg_offset = offset + 4; + *seg_length = length - 4; + return TRUE; + } + } + } + return FALSE; +} + + +typedef enum { + TIFF_BYTE_ORDER_INTEL, + TIFF_BYTE_ORDER_MOTOROLA +} TiffByteOrder; + +#define TIFF_TIFD_OFFSET_TAG 0 +#define TIFF_TIFD_OFFSET_FORMAT 2 +#define TIFF_TIFD_OFFSET_COUNT 4 +#define TIFF_TIFD_OFFSET_DATA 8 +#define TIFF_TIFD_SIZE 12 + + + +guint16 tiff_byte_get_int16(guchar *f, TiffByteOrder bo) +{ + guint16 align_buf; + + memcpy(&align_buf, f, sizeof(guint16)); + + if (bo == TIFF_BYTE_ORDER_INTEL) + return GUINT16_FROM_LE(align_buf); + else + return GUINT16_FROM_BE(align_buf); +} + +guint32 tiff_byte_get_int32(guchar *f, TiffByteOrder bo) +{ + guint32 align_buf; + + memcpy(&align_buf, f, sizeof(guint32)); + + if (bo == TIFF_BYTE_ORDER_INTEL) + return GUINT32_FROM_LE(align_buf); + else + return GUINT32_FROM_BE(align_buf); +} + +void tiff_byte_put_int16(guchar *f, guint16 n, TiffByteOrder bo) +{ + guint16 align_buf; + + if (bo == TIFF_BYTE_ORDER_INTEL) + { + align_buf = GUINT16_TO_LE(n); + } + else + { + align_buf = GUINT16_TO_BE(n); + } + + memcpy(f, &align_buf, sizeof(guint16)); +} + +void tiff_byte_put_int32(guchar *f, guint32 n, TiffByteOrder bo) +{ + guint32 align_buf; + + if (bo == TIFF_BYTE_ORDER_INTEL) + { + align_buf = GUINT32_TO_LE(n); + } + else + { + align_buf = GUINT32_TO_BE(n); + } + + memcpy(f, &align_buf, sizeof(guint32)); +} + +gint tiff_directory_offset(guchar *data, const guint len, + guint *offset, TiffByteOrder *bo) +{ + if (len < 8) return FALSE; + + if (memcmp(data, "II", 2) == 0) + { + *bo = TIFF_BYTE_ORDER_INTEL; + } + else if (memcmp(data, "MM", 2) == 0) + { + *bo = TIFF_BYTE_ORDER_MOTOROLA; + } + else + { + return FALSE; + } + + if (tiff_byte_get_int16(data + 2, *bo) != 0x002A) + { + return FALSE; + } + + *offset = tiff_byte_get_int32(data + 4, *bo); + + return (*offset < len); +} + +typedef gint (* FuncParseIFDEntry)(guchar *tiff, guint offset, + guint size, TiffByteOrder bo, + gpointer data); + + +gint tiff_parse_IFD_table(guchar *tiff, guint offset, + guint size, TiffByteOrder bo, + guint *next_offset, + FuncParseIFDEntry parse_entry, gpointer data) +{ + guint count; + guint i; + guint next; + + + /* We should be able to read number of entries in IFD0) */ + if (size < offset + 2) return -1; + + count = tiff_byte_get_int16(tiff + offset, bo); + offset += 2; + /* Entries and next IFD offset must be readable */ + if (size < offset + count * TIFF_TIFD_SIZE + 4) return -1; + + for (i = 0; i < count; i++) + { + parse_entry(tiff, offset + i * TIFF_TIFD_SIZE, size, bo, data); + } + + next = tiff_byte_get_int32(tiff + offset + count * TIFF_TIFD_SIZE, bo); + if (next_offset) *next_offset = next; + + return 0; +} + +static gint mpo_parse_Index_IFD_entry(guchar *tiff, guint offset, + guint size, TiffByteOrder bo, + gpointer data) +{ + guint tag; + guint format; + guint count; + guint data_val; + guint data_offset; + guint data_length; + + MPOData *mpo = data; + + tag = tiff_byte_get_int16(tiff + offset + TIFF_TIFD_OFFSET_TAG, bo); + format = tiff_byte_get_int16(tiff + offset + TIFF_TIFD_OFFSET_FORMAT, bo); + count = tiff_byte_get_int32(tiff + offset + TIFF_TIFD_OFFSET_COUNT, bo); + data_val = tiff_byte_get_int32(tiff + offset + TIFF_TIFD_OFFSET_DATA, bo); + DEBUG_1(" tag %x format %x count %x data_val %x", tag, format, count, data_val); + + if (tag == 0xb000) + { + mpo->version = data_val; + DEBUG_1(" mpo version %x", mpo->version); + } + else if (tag == 0xb001) + { + mpo->num_images = data_val; + DEBUG_1(" num images %x", mpo->num_images); + } + else if (tag == 0xb002) + { + guint i; + data_offset = data_val; + data_length = count; + if (size < data_offset || size < data_offset + data_length) + { + return -1; + } + if (count != mpo->num_images * 16) + { + return -1; + } + + mpo->images = g_new0(MPOEntry, mpo->num_images); + + for (i = 0; i < mpo->num_images; i++) { + guint image_attr = tiff_byte_get_int32(tiff + data_offset + i * 16, bo); + mpo->images[i].type_code = image_attr & 0xffffff; + mpo->images[i].representative = !!(image_attr & 0x20000000); + mpo->images[i].dependent_child = !!(image_attr & 0x40000000); + mpo->images[i].dependent_parent = !!(image_attr & 0x80000000); + mpo->images[i].length = tiff_byte_get_int32(tiff + data_offset + i * 16 + 4, bo); + mpo->images[i].offset = tiff_byte_get_int32(tiff + data_offset + i * 16 + 8, bo); + mpo->images[i].dep1 = tiff_byte_get_int16(tiff + data_offset + i * 16 + 12, bo); + mpo->images[i].dep2 = tiff_byte_get_int16(tiff + data_offset + i * 16 + 14, bo); + + if (i == 0) + { + mpo->images[i].offset = 0; + } + else + { + mpo->images[i].offset += mpo->mpo_offset; + } + + DEBUG_1(" image %x %x %x", image_attr, mpo->images[i].length, mpo->images[i].offset); + } + } + + return 0; +} + +static gint mpo_parse_Attributes_IFD_entry(guchar *tiff, guint offset, + guint size, TiffByteOrder bo, + gpointer data) +{ + guint tag; + guint format; + guint count; + guint data_val; + guint data_offset; + guint data_length; + + MPOEntry *mpe = data; + + tag = tiff_byte_get_int16(tiff + offset + TIFF_TIFD_OFFSET_TAG, bo); + format = tiff_byte_get_int16(tiff + offset + TIFF_TIFD_OFFSET_FORMAT, bo); + count = tiff_byte_get_int32(tiff + offset + TIFF_TIFD_OFFSET_COUNT, bo); + data_val = tiff_byte_get_int32(tiff + offset + TIFF_TIFD_OFFSET_DATA, bo); + DEBUG_1(" tag %x format %x count %x data_val %x", tag, format, count, data_val); + + switch (tag) + { + case 0xb000: + mpe->MPFVersion = data_val; + DEBUG_1(" mpo version %x", data_val); + break; + case 0xb101: + mpe->MPIndividualNum = data_val; + DEBUG_1(" Individual Image Number %x", mpe->MPIndividualNum); + break; + case 0xb201: + mpe->PanOrientation = data_val; + break; +/* + +FIXME: +Panorama Scanning Orientation PanOrientation 45569 B201 LONG 1 +Panorama Horizontal Overlap PanOverlap_H 45570 B202 RATIONAL 1 +Panorama Vertical Overlap PanOverlap_V 45571 B203 RATIONAL 1 +Base Viewpoint Number BaseViewpointNum 45572 B204 LONG 1 +Convergence Angle ConvergenceAngle 45573 B205 SRATIONAL 1 +Baseline Length BaselineLength 45574 B206 RATIONAL 1 +Divergence Angle VerticalDivergence 45575 B207 SRATIONAL 1 +Horizontal Axis Distance AxisDistance_X 45576 B208 SRATIONAL 1 +Vertical Axis Distance AxisDistance_Y 45577 B209 SRATIONAL 1 +Collimation Axis Distance AxisDistance_Z 45578 B20A SRATIONAL 1 +Yaw Angle YawAngle 45579 B20B SRATIONAL 1 +Pitch Angle PitchAngle 45580 B20C SRATIONAL 1 +Roll Angle RollAngle 45581 B20D + */ + default: + break; + } + + return 0; +} + +MPOData *jpeg_get_mpo_data(guchar *data, guint size) +{ + guint seg_offset; + guint seg_size; + if (jpeg_segment_find(data, size, JPEG_MARKER_APP2, "MPF\x00", 4, &seg_offset, &seg_size) && seg_size >16) + { + guint offset; + guint next_offset; + TiffByteOrder bo; + MPOData *mpo; + guint i; + + DEBUG_1("mpo signature found at %x", seg_offset); + seg_offset += 4; + seg_size -= 4; + + if (!tiff_directory_offset(data + seg_offset, seg_size, &offset, &bo)) return NULL; + + mpo = g_new0(MPOData, 1); + mpo->mpo_offset = seg_offset; + + tiff_parse_IFD_table(data + seg_offset, offset , seg_size, bo, &next_offset, mpo_parse_Index_IFD_entry, (gpointer)mpo); + if (!mpo->images) mpo->num_images = 0; + + + for (i = 0; i < mpo->num_images; i++) + { + if (mpo->images[i].offset + mpo->images[i].length > size) + { + mpo->num_images = i; + DEBUG_1("MPO file truncated to %d valid images, %d %d", i, mpo->images[i].offset + mpo->images[i].length, size); + break; + } + } + + for (i = 0; i < mpo->num_images; i++) + { + if (i == 0) + { + offset = next_offset; + } + else + { + if (!jpeg_segment_find(data + mpo->images[i].offset, mpo->images[i].length, JPEG_MARKER_APP2, "MPF\x00", 4, &seg_offset, &seg_size) || seg_size <=16) + { + DEBUG_1("MPO image %d: MPO signature not found", i); + continue; + } + + seg_offset += 4; + seg_size -= 4; + if (!tiff_directory_offset(data + mpo->images[i].offset + seg_offset, seg_size, &offset, &bo)) + { + DEBUG_1("MPO image %d: invalid directory offset", i); + continue; + } + + } + tiff_parse_IFD_table(data + mpo->images[i].offset + seg_offset, offset , seg_size, bo, NULL, mpo_parse_Attributes_IFD_entry, (gpointer)&mpo->images[i]); + } + + return mpo; + } + return NULL; +} + +void jpeg_mpo_data_free(MPOData *mpo) +{ + if (mpo) + { + if (mpo->images) g_free(mpo->images); + g_free(mpo); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jpeg_parser.h Sat Oct 29 23:41:36 2011 +0100 @@ -0,0 +1,82 @@ +/* + * Geeqie + * (C) 2004 John Ellis + * Copyright (C) 2008 - 2010 The Geeqie Team + * + * Author: Vladimir Nadvornik + * + * 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 JPEG_PARSER_H +#define JPEG_PARSER_H + +#define JPEG_MARKER 0xFF +#define JPEG_MARKER_SOI 0xD8 +#define JPEG_MARKER_EOI 0xD9 +#define JPEG_MARKER_APP1 0xE1 +#define JPEG_MARKER_APP2 0xE2 + +/* jpeg container format: + all data markers start with 0XFF + 2 byte long file start and end markers: 0xFFD8(SOI) and 0XFFD9(EOI) + 4 byte long data segment markers in format: 0xFFTTSSSSNNN... + FF: 1 byte standard marker identifier + TT: 1 byte data type + SSSS: 2 bytes in Motorola byte alignment for length of the data. + This value includes these 2 bytes in the count, making actual + length of NN... == SSSS - 2. + NNN.: the data in this segment + */ + +gboolean jpeg_segment_find(guchar *data, guint size, + guchar app_marker, const gchar *magic, guint magic_len, + guint *seg_offset, guint *seg_length); + + +typedef struct _MPOData MPOData; +typedef struct _MPOEntry MPOEntry; + +struct _MPOEntry { + guint type_code; + gboolean representative; + gboolean dependent_child; + gboolean dependent_parent; + guint offset; + guint length; + guint dep1; + guint dep2; + + guint MPFVersion; + guint MPIndividualNum; + guint PanOrientation; + double PanOverlap_H; + double PanOverlap_V; + guint BaseViewpointNum; + double ConvergenceAngle; + double BaselineLength; + double VerticalDivergence; + double AxisDistance_X; + double AxisDistance_Y; + double AxisDistance_Z; + double YawAngle; + double PitchAngle; + double RollAngle; + +}; + + +struct _MPOData { + guint mpo_offset; + + guint version; + guint num_images; + MPOEntry *images; +}; + +MPOData* jpeg_get_mpo_data(guchar *data, guint size); +void jpeg_mpo_data_free(MPOData *mpo); + +#endif \ No newline at end of file
--- a/src/layout.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/layout.c Sat Oct 29 23:41:36 2011 +0100 @@ -851,7 +851,7 @@ if (!path) return FALSE; - fd = file_data_new_simple(path); + fd = file_data_new_group(path); ret = layout_set_fd(lw, fd); file_data_unref(fd); return ret; @@ -898,7 +898,7 @@ file_data_unregister_real_time_monitor(lw->dir_fd); file_data_unref(lw->dir_fd); } - lw->dir_fd = file_data_new_simple(base); + lw->dir_fd = file_data_new_dir(base); file_data_register_real_time_monitor(lw->dir_fd); g_free(base); }
--- a/src/layout_image.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/layout_image.c Sat Oct 29 23:41:36 2011 +0100 @@ -634,7 +634,7 @@ FileData *dir_fd; base = remove_level_from_path(fd->path); - dir_fd = file_data_new_simple(base); + dir_fd = file_data_new_dir(base); if (dir_fd != lw->dir_fd) { layout_set_fd(lw, dir_fd); @@ -902,7 +902,42 @@ return image_get_desaturate(lw->image); } +/* stereo */ +/* +gint layout_image_stereo_get(LayoutWindow *lw) +{ + if (!layout_valid(&lw)) return 0; + return image_stereo_get(lw->image); +} + +void layout_image_stereo_set(LayoutWindow *lw, gint stereo_mode) +{ + if (!layout_valid(&lw)) return; + + image_stereo_set(lw->image, stereo_mode); +} +void layout_image_stereo_swap(LayoutWindow *lw) +{ + if (!layout_valid(&lw)) return; + + image_stereo_swap(lw->image); +} +*/ + +gint layout_image_stereo_pixbuf_get(LayoutWindow *lw) +{ + if (!layout_valid(&lw)) return 0; + + return image_stereo_pixbuf_get(lw->image); +} + +void layout_image_stereo_pixbuf_set(LayoutWindow *lw, gint stereo_mode) +{ + if (!layout_valid(&lw)) return; + + image_stereo_pixbuf_set(lw->image, stereo_mode); +} const gchar *layout_image_get_path(LayoutWindow *lw) {
--- a/src/layout_image.h Fri Apr 22 11:29:35 2011 +0100 +++ b/src/layout_image.h Sat Oct 29 23:41:36 2011 +0100 @@ -55,6 +55,15 @@ void layout_image_set_desaturate(LayoutWindow *lw, gboolean desaturate); gboolean layout_image_get_desaturate(LayoutWindow *lw); +/* +gint layout_image_stereo_get(LayoutWindow *lw); +void layout_image_stereo_set(LayoutWindow *lw, gint stereo_mode); +*/ +void layout_image_stereo_swap(LayoutWindow *lw); + +gint layout_image_stereo_pixbuf_get(LayoutWindow *lw); +void layout_image_stereo_pixbuf_set(LayoutWindow *lw, gint stereo_mode); + void layout_image_next(LayoutWindow *lw); void layout_image_prev(LayoutWindow *lw); void layout_image_first(LayoutWindow *lw);
--- a/src/layout_util.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/layout_util.c Sat Oct 29 23:41:36 2011 +0100 @@ -836,6 +836,32 @@ layout_image_slideshow_pause_toggle(lw); } + +static void layout_menu_stereo_mode_next_cb(GtkAction *action, gpointer data) +{ + LayoutWindow *lw = data; + gint mode = layout_image_stereo_pixbuf_get(lw); + + /* 0->1, 1->2, 2->3, 3->1 - disable auto, then cycle */ + mode = mode % 3 + 1; + + GtkAction *radio = gtk_action_group_get_action(lw->action_group, "StereoAuto"); + radio_action_set_current_value(GTK_RADIO_ACTION(radio), mode); + + /* + this is called via fallback in layout_menu_stereo_mode_cb + layout_image_stereo_pixbuf_set(lw, mode); + */ + +} + +static void layout_menu_stereo_mode_cb(GtkRadioAction *action, GtkRadioAction *current, gpointer data) +{ + LayoutWindow *lw = data; + gint mode = gtk_radio_action_get_current_value(action); + layout_image_stereo_pixbuf_set(lw, mode); +} + static void layout_menu_help_cb(GtkAction *action, gpointer data) { LayoutWindow *lw = data; @@ -1038,7 +1064,7 @@ if (!path) return; /* Open previous path */ - dir_fd = file_data_new_simple(path); + dir_fd = file_data_new_dir(path); layout_set_fd(lw, dir_fd); file_data_unref(dir_fd); } @@ -1055,7 +1081,7 @@ if (path) { - FileData *dir_fd = file_data_new_simple(path); + FileData *dir_fd = file_data_new_dir(path); layout_set_fd(lw, dir_fd); file_data_unref(dir_fd); } @@ -1256,6 +1282,7 @@ { "ColorMenu", NULL, N_("_Color Management"), NULL, NULL, NULL }, { "ConnectZoomMenu", NULL, N_("_Connected Zoom"), NULL, NULL, NULL }, { "SplitMenu", NULL, N_("Spli_t"), NULL, NULL, NULL }, + { "StereoMenu", NULL, N_("Stere_o"), NULL, NULL, NULL }, { "OverlayMenu", NULL, N_("Image _Overlay"), NULL, NULL, NULL }, { "HelpMenu", NULL, N_("_Help"), NULL, NULL, NULL }, @@ -1355,6 +1382,7 @@ { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("About"), CB(layout_menu_about_cb) }, { "LogWindow", NULL, N_("_Log Window"), NULL, N_("Log Window"), CB(layout_menu_log_window_cb) }, { "ExifWin", NULL, N_("_Exif window"), "<control>E", N_("Exif window"), CB(layout_menu_bar_exif_cb) }, + { "StereoCycle", NULL, N_("_Cycle through stereo modes"), NULL, N_("Cycle through stereo modes"), CB(layout_menu_stereo_mode_next_cb) }, }; @@ -1413,6 +1441,13 @@ { "HistogramModeLog", NULL, N_("_Log Histogram"), NULL, N_("Log Histogram"), 1 }, }; +static GtkRadioActionEntry menu_stereo_mode_entries[] = { + { "StereoAuto", NULL, N_("_Auto"), NULL, N_("Stereo Auto"), STEREO_PIXBUF_DEFAULT }, + { "StereoSBS", NULL, N_("_Side by Side"), NULL, N_("Stereo Side by Side"), STEREO_PIXBUF_SBS }, + { "StereoCross", NULL, N_("_Cross"), NULL, N_("Stereo Cross"), STEREO_PIXBUF_CROSS }, + { "StereoOff", NULL, N_("_Off"), NULL, N_("Stereo Off"), STEREO_PIXBUF_NONE } +}; + #undef CB @@ -1551,6 +1586,14 @@ " <menuitem action='SplitQuad'/>" " <menuitem action='SplitSingle'/>" " </menu>" +" <menu action='StereoMenu'>" +" <menuitem action='StereoAuto'/>" +" <menuitem action='StereoSBS'/>" +" <menuitem action='StereoCross'/>" +" <menuitem action='StereoOff'/>" +" <separator/>" +" <menuitem action='StereoCycle'/>" +" </menu>" " <menu action='ColorMenu'>" " <menuitem action='UseColorProfiles'/>" " <menuitem action='UseImageProfile'/>" @@ -1915,6 +1958,9 @@ gtk_action_group_add_radio_actions(lw->action_group, menu_histogram_mode, G_N_ELEMENTS(menu_histogram_mode), 0, G_CALLBACK(layout_menu_histogram_mode_cb), lw); + gtk_action_group_add_radio_actions(lw->action_group, + menu_stereo_mode_entries, G_N_ELEMENTS(menu_stereo_mode_entries), + 0, G_CALLBACK(layout_menu_stereo_mode_cb), lw); lw->ui_manager = gtk_ui_manager_new(); @@ -2374,6 +2420,9 @@ action = gtk_action_group_get_action(lw->action_group, "ConnectZoomMenu"); gtk_action_set_sensitive(action, lw->split_mode != SPLIT_NONE); + action = gtk_action_group_get_action(lw->action_group, "StereoAuto"); + radio_action_set_current_value(GTK_RADIO_ACTION(action), layout_image_stereo_pixbuf_get(lw)); + layout_util_sync_color(lw); }
--- a/src/main.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/main.c Sat Oct 29 23:41:36 2011 +0100 @@ -126,7 +126,7 @@ { if (!*path) *path = remove_level_from_path(path_parsed); if (!*file) *file = g_strdup(path_parsed); - *list = g_list_prepend(*list, file_data_new_simple(path_parsed)); + *list = g_list_prepend(*list, file_data_new_group(path_parsed)); } } @@ -142,7 +142,7 @@ path_parsed = g_strdup(dir); parse_out_relatives(path_parsed); - dir_fd = file_data_new_simple(path_parsed); + dir_fd = file_data_new_dir(path_parsed); if (filelist_read(dir_fd, &files, NULL)) @@ -741,6 +741,8 @@ g_thread_init(NULL); gdk_threads_init(); gdk_threads_enter(); + debug_mutex = g_mutex_new(); + #endif /* init execution time counter (debug only) */
--- a/src/metadata.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/metadata.c Sat Oct 29 23:41:36 2011 +0100 @@ -62,6 +62,113 @@ static gboolean metadata_file_read(gchar *path, GList **keywords, gchar **comment); +/* + *------------------------------------------------------------------- + * long-term cache - keep keywords from whole dir in memory + *------------------------------------------------------------------- + */ + +/* fd->cached metadata list of lists + each particular list contains key as a first entry, then the values +*/ + +static void metadata_cache_update(FileData *fd, const gchar *key, const GList *values) +{ + GList *work; + + work = fd->cached_metadata; + while (work) + { + GList *entry = work->data; + gchar *entry_key = entry->data; + + if (strcmp(entry_key, key) == 0) + { + /* key found - just replace values */ + GList *old_values = entry->next; + entry->next = NULL; + old_values->prev = NULL; + string_list_free(old_values); + work->data = g_list_append(entry, string_list_copy(values)); + DEBUG_1("updated %s %s\n", key, fd->path); + return; + } + work = work->next; + } + + /* key not found - prepend new entry */ + fd->cached_metadata = g_list_prepend(fd->cached_metadata, + g_list_prepend(string_list_copy(values), g_strdup(key))); + DEBUG_1("added %s %s\n", key, fd->path); + +} + +static const GList *metadata_cache_get(FileData *fd, const gchar *key) +{ + GList *work; + + work = fd->cached_metadata; + while (work) + { + GList *entry = work->data; + gchar *entry_key = entry->data; + + if (strcmp(entry_key, key) == 0) + { + /* key found */ + DEBUG_1("found %s %s\n", key, fd->path); + return entry; + } + work = work->next; + } + return NULL; + DEBUG_1("not found %s %s\n", key, fd->path); +} + +static void metadata_cache_remove(FileData *fd, const gchar *key) +{ + GList *work; + + work = fd->cached_metadata; + while (work) + { + GList *entry = work->data; + gchar *entry_key = entry->data; + + if (strcmp(entry_key, key) == 0) + { + /* key found */ + string_list_free(entry); + fd->cached_metadata = g_list_delete_link(fd->cached_metadata, work); + DEBUG_1("removed %s %s\n", key, fd->path); + return; + } + work = work->next; + } + DEBUG_1("not removed %s %s\n", key, fd->path); +} + +void metadata_cache_free(FileData *fd) +{ + GList *work; + if (fd->cached_metadata) DEBUG_1("freed %s\n", fd->path); + + work = fd->cached_metadata; + while (work) + { + GList *entry = work->data; + string_list_free(entry); + + work = work->next; + } + g_list_free(fd->cached_metadata); + fd->cached_metadata = NULL; +} + + + + + /* *------------------------------------------------------------------- @@ -128,13 +235,18 @@ void metadata_notify_cb(FileData *fd, NotifyType type, gpointer data) { - if ((type & (NOTIFY_REREAD | NOTIFY_CHANGE)) && g_list_find(metadata_write_queue, fd)) + if (type & (NOTIFY_REREAD | NOTIFY_CHANGE)) { - DEBUG_1("Notify metadata: %s %04x", fd->path, type); - if (!isname(fd->path)) + metadata_cache_free(fd); + + if (g_list_find(metadata_write_queue, fd)) { - /* ignore deleted files */ - metadata_write_queue_remove(fd); + DEBUG_1("Notify metadata: %s %04x", fd->path, type); + if (!isname(fd->path)) + { + /* ignore deleted files */ + metadata_write_queue_remove(fd); + } } } } @@ -206,7 +318,7 @@ store the metadata in the cache) FIXME: this does not catch new sidecars created by independent external programs */ - file_data_unref(file_data_new_simple(fd->change->dest)); + file_data_unref(file_data_new_group(fd->change->dest)); if (success) metadata_legacy_delete(fd, fd->change->dest); return success; @@ -255,6 +367,9 @@ fd->modified_xmp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)string_list_free); } g_hash_table_insert(fd->modified_xmp, g_strdup(key), string_list_copy((GList *)values)); + + metadata_cache_remove(fd, key); + if (fd->exif) { exif_update_metadata(fd->exif, key, values); @@ -540,6 +655,7 @@ { ExifData *exif; GList *list = NULL; + const GList *cache_entry; if (!fd) return NULL; /* unwritten data overide everything */ @@ -549,6 +665,13 @@ if (list) return string_list_copy(list); } + + if (format == METADATA_PLAIN && strcmp(key, KEYWORD_KEY) == 0 + && (cache_entry = metadata_cache_get(fd, key))) + { + return string_list_copy(cache_entry->next); + } + /* Legacy metadata file is the primary source if it exists. Merging the lists does not make much sense, because the existence of @@ -558,8 +681,15 @@ */ if (strcmp(key, KEYWORD_KEY) == 0) { - if (metadata_legacy_read(fd, &list, NULL)) return list; - } + if (metadata_legacy_read(fd, &list, NULL)) + { + if (format == METADATA_PLAIN) + { + metadata_cache_update(fd, key, list); + } + return list; + } + } else if (strcmp(key, COMMENT_KEY) == 0) { gchar *comment = NULL; @@ -574,6 +704,12 @@ if (!exif) return NULL; list = exif_get_metadata(exif, key, format); exif_free_fd(fd, exif); + + if (format == METADATA_PLAIN && strcmp(key, KEYWORD_KEY) == 0) + { + metadata_cache_update(fd, key, list); + } + return list; }
--- a/src/metadata.h Fri Apr 22 11:29:35 2011 +0100 +++ b/src/metadata.h Sat Oct 29 23:41:36 2011 +0100 @@ -18,6 +18,8 @@ #define KEYWORD_KEY "Xmp.dc.subject" #define ORIENTATION_KEY "Xmp.tiff.Orientation" +void metadata_cache_free(FileData *fd); + gboolean metadata_write_queue_remove(FileData *fd); gboolean metadata_write_queue_remove_list(GList *list); gboolean metadata_write_perform(FileData *fd);
--- a/src/options.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/options.c Sat Oct 29 23:41:36 2011 +0100 @@ -124,6 +124,13 @@ options->tree_descend_subdirs = FALSE; options->update_on_time_change = TRUE; + + options->stereo.fixed_w = 1920; + options->stereo.fixed_h = 1080; + options->stereo.fixed_x1 = 0; + options->stereo.fixed_y1 = 0; + options->stereo.fixed_x2 = 0; + options->stereo.fixed_y2 = 1125; return options; }
--- a/src/options.h Fri Apr 22 11:29:35 2011 +0100 +++ b/src/options.h Sat Oct 29 23:41:36 2011 +0100 @@ -181,7 +181,30 @@ gboolean keywords_case_sensitive; gboolean write_orientation; } metadata; - + + /* Stereo */ + struct { + gint mode;; + gint fsmode; + gboolean enable_fsmode; + gint fixed_w, fixed_h; + gint fixed_x1, fixed_y1; + gint fixed_x2, fixed_y2; + struct { /* options in this struct are packed to mode and fsmode entries */ + gboolean mirror_right; + gboolean mirror_left; + gboolean flip_right; + gboolean flip_left; + gboolean swap; + gboolean temp_disable; + gboolean fs_mirror_right; + gboolean fs_mirror_left; + gboolean fs_flip_right; + gboolean fs_flip_left; + gboolean fs_swap; + gboolean fs_temp_disable; + } tmp; + } stereo; }; ConfOptions *options;
--- a/src/pan-calendar.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/pan-calendar.c Sat Oct 29 23:41:36 2011 +0100 @@ -333,7 +333,7 @@ */ g_snprintf(fake_path, sizeof(fake_path), "//%04d-%02d-%02d", year, month, day); - fd = file_data_new_simple(fake_path); + fd = file_data_new_no_grouping(fake_path); fd->date = dt; pi_day = pan_item_box_new(pw, fd, x, y, PAN_CAL_DAY_WIDTH, PAN_CAL_DAY_HEIGHT, PAN_CAL_DAY_BORDER,
--- a/src/pan-timeline.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/pan-timeline.c Sat Oct 29 23:41:36 2011 +0100 @@ -91,7 +91,7 @@ g_free(buf); y += pi->height; - pi_month = pan_item_box_new(pw, file_data_new_simple(fd->path), + pi_month = pan_item_box_new(pw, file_data_ref(fd), x, y, 0, 0, PAN_BOX_OUTLINE_THICKNESS, PAN_BOX_COLOR, PAN_BOX_ALPHA, @@ -133,7 +133,7 @@ y += pi->height; - pi_day = pan_item_box_new(pw, file_data_new_simple(fd->path), x, y, 0, 0, + pi_day = pan_item_box_new(pw, file_data_ref(fd), x, y, 0, 0, PAN_BOX_OUTLINE_THICKNESS, PAN_BOX_COLOR, PAN_BOX_ALPHA, PAN_BOX_OUTLINE_COLOR, PAN_BOX_OUTLINE_ALPHA);
--- a/src/pan-view.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/pan-view.c Sat Oct 29 23:41:36 2011 +0100 @@ -1604,7 +1604,7 @@ PAN_POPUP_BORDER_COLOR, PAN_POPUP_ALPHA); pan_item_set_key(pbox, "info"); - p = pan_item_image_new(pw, file_data_new_simple(pi->fd->path), + p = pan_item_image_new(pw, file_data_new_group(pi->fd->path), pbox->x + PREF_PAD_BORDER, pbox->y + PREF_PAD_BORDER, iw, ih); pan_item_set_key(p, "info"); pan_item_size_by_item(pbox, p, PREF_PAD_BORDER); @@ -2297,7 +2297,7 @@ } else { - FileData *dir_fd = file_data_new_simple(path); + FileData *dir_fd = file_data_new_dir(path); tab_completion_append_to_history(pw->path_entry, path); pan_layout_set_fd(pw, dir_fd);
--- a/src/pixbuf-renderer.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/pixbuf-renderer.c Sat Oct 29 23:41:36 2011 +0100 @@ -17,6 +17,7 @@ #include "main.h" #include "pixbuf-renderer.h" +#include "renderer-tiles.h" #include "intl.h" #include "layout.h" @@ -49,12 +50,6 @@ #endif -/* size to use when breaking up image pane for rendering */ -#define PR_TILE_SIZE 128 - -/* default size of tile cache (mb) */ -#define PR_CACHE_SIZE_DEFAULT 8 - /* default min and max zoom */ #define PR_ZOOM_MIN -32.0 #define PR_ZOOM_MAX 32.0 @@ -69,89 +64,11 @@ #define PR_SCROLLER_UPDATES_PER_SEC 30 #define PR_SCROLLER_DEAD_ZONE 6 -/* alpha channel checkerboard background (same as gimp) */ -#define PR_ALPHA_CHECK1 0x00999999 -#define PR_ALPHA_CHECK2 0x00666666 -#define PR_ALPHA_CHECK_SIZE 16 - /* when scaling image to below this size, use nearest pixel for scaling * (below about 4, the other scale types become slow generating their conversion tables) */ #define PR_MIN_SCALE_SIZE 8 -/* round A up/down to integer count of B */ -#define ROUND_UP(A,B) ((gint)(((A)+(B)-1)/(B))*(B)) -#define ROUND_DOWN(A,B) ((gint)(((A))/(B))*(B)) - -typedef enum { - TILE_RENDER_NONE = 0, /* do nothing */ - TILE_RENDER_AREA, /* render an area of the tile */ - TILE_RENDER_ALL /* render the whole tile */ -} ImageTileRenderType; - -typedef struct _ImageTile ImageTile; -typedef struct _QueueData QueueData; - -struct _ImageTile -{ - GdkPixmap *pixmap; /* off screen buffer */ - GdkPixbuf *pixbuf; /* pixbuf area for zooming */ - gint x; /* x offset into image */ - gint y; /* y offset into image */ - gint w; /* width that is visible (may be less if at edge of image) */ - gint h; /* height '' */ - - gboolean blank; - -/* render_todo: (explanation) - NONE do nothing - AREA render area of tile, usually only used when loading an image - note: will jump to an ALL if render_done is not ALL. - ALL render entire tile, if never done before w/ ALL, for expose events *only* -*/ - - ImageTileRenderType render_todo; /* what to do (see above) */ - ImageTileRenderType render_done; /* highest that has been done before on tile */ - - QueueData *qd; - QueueData *qd2; - - guint size; /* est. memory used by pixmap and pixbuf */ -}; - -struct _QueueData -{ - ImageTile *it; - gint x; - gint y; - gint w; - gint h; - gboolean new_data; -}; - -typedef struct _SourceTile SourceTile; -struct _SourceTile -{ - gint x; - gint y; - GdkPixbuf *pixbuf; - gboolean blank; -}; - -typedef struct _OverlayData OverlayData; -struct _OverlayData -{ - gint id; - - GdkPixbuf *pixbuf; - GdkWindow *window; - - gint x; - gint y; - - OverlayRendererFlags flags; -}; - enum { SIGNAL_ZOOM = 0, SIGNAL_CLICKED, @@ -206,32 +123,18 @@ GValue *value, GParamSpec *pspec); static gboolean pixbuf_renderer_expose(GtkWidget *widget, GdkEventExpose *event); -static void pr_render_complete_signal(PixbufRenderer *pr); - -static void pr_overlay_list_clear(PixbufRenderer *pr); static void pr_scroller_timer_set(PixbufRenderer *pr, gboolean start); -static void pr_border_draw(PixbufRenderer *pr, gint x, gint y, gint w, gint h); static void pr_source_tile_free_all(PixbufRenderer *pr); -static void pr_tile_free_all(PixbufRenderer *pr); -static void pr_tile_invalidate_region(PixbufRenderer *pr, gint x, gint y, gint w, gint h); -static gboolean pr_tile_is_visible(PixbufRenderer *pr, ImageTile *it); -static void pr_queue_clear(PixbufRenderer *pr); -static void pr_queue_merge(QueueData *parent, QueueData *qd); -static void pr_queue(PixbufRenderer *pr, gint x, gint y, gint w, gint h, - gint clamp, ImageTileRenderType render, gboolean new_data, gboolean only_existing); - -static void pr_redraw(PixbufRenderer *pr, gboolean new_data); static void pr_zoom_sync(PixbufRenderer *pr, gdouble zoom, PrZoomFlags flags, gint px, gint py); static void pr_signals_connect(PixbufRenderer *pr); static void pr_size_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data); -static void pr_hierarchy_changed_cb(GtkWidget *widget, GtkWidget *previous_toplevel, gpointer data); static void pixbuf_renderer_paint(PixbufRenderer *pr, GdkRectangle *area); -static gint pr_queue_draw_idle_cb(gpointer data); +static void pr_stereo_temp_disable(PixbufRenderer *pr, gboolean disable); /* @@ -507,21 +410,12 @@ pr->zoom = 1.0; pr->scale = 1.0; + pr->aspect_ratio = 1.0; pr->dither_quality = GDK_RGB_DITHER_NORMAL; pr->scroll_reset = PR_SCROLL_RESET_TOPLEFT; - pr->draw_idle_id = 0; - - pr->tile_width = PR_TILE_SIZE; - pr->tile_height = PR_TILE_SIZE; - - pr->tiles = NULL; - pr->tile_cache_size = 0; - - pr->tile_cache_max = PR_CACHE_SIZE_DEFAULT; - pr->scroller_id = 0; pr->scroller_overlay = -1; @@ -535,14 +429,17 @@ pr->norm_center_x = 0.5; pr->norm_center_y = 0.5; + + pr->stereo_mode = PR_STEREO_NONE; + + pr->renderer = (void *)renderer_tiles_new(pr); + + pr->renderer2 = NULL; gtk_widget_set_double_buffered(box, FALSE); g_signal_connect_after(G_OBJECT(box), "size_allocate", G_CALLBACK(pr_size_cb), pr); - g_signal_connect(G_OBJECT(pr), "hierarchy-changed", - G_CALLBACK(pr_hierarchy_changed_cb), pr); - pr_signals_connect(pr); } @@ -552,15 +449,13 @@ pr = PIXBUF_RENDERER(object); - pr_queue_clear(pr); - pr_tile_free_all(pr); + pr->renderer->free(pr->renderer); + if (pr->renderer2) pr->renderer2->free(pr->renderer2); if (pr->pixbuf) g_object_unref(pr->pixbuf); - if (pr->spare_tile) g_object_unref(pr->spare_tile); pr_scroller_timer_set(pr, FALSE); - pr_overlay_list_clear(pr); pr_source_tile_free_all(pr); } @@ -610,7 +505,7 @@ pr->complete = g_value_get_boolean(value); break; case PROP_CACHE_SIZE_DISPLAY: - pr->tile_cache_max = g_value_get_uint(value); +// pr->tile_cache_max = g_value_get_uint(value); break; case PROP_CACHE_SIZE_TILES: pr->source_tiles_cache_size = g_value_get_uint(value); @@ -676,7 +571,7 @@ g_value_set_boolean(value, pr->complete); break; case PROP_CACHE_SIZE_DISPLAY: - g_value_set_uint(value, pr->tile_cache_max); +// g_value_set_uint(value, pr->tile_cache_max); break; case PROP_CACHE_SIZE_TILES: g_value_set_uint(value, pr->source_tiles_cache_size); @@ -764,16 +659,7 @@ if (cursor) gdk_cursor_unref(cursor); } -static gint pixmap_calc_size(GdkPixmap *pixmap) -{ - gint w, h, d; - - d = gdk_drawable_get_depth(pixmap); - gdk_drawable_get_size(pixmap, &w, &h); - return w * h * (d / 8); -} - -static gboolean pr_clip_region(gint x, gint y, gint w, gint h, +gboolean pr_clip_region(gint x, gint y, gint w, gint h, gint clip_x, gint clip_y, gint clip_w, gint clip_h, gint *rx, gint *ry, gint *rw, gint *rh) { @@ -862,350 +748,33 @@ *------------------------------------------------------------------- */ -static void pr_overlay_get_position(PixbufRenderer *pr, OverlayData *od, - gint *x, gint *y, gint *w, gint *h) -{ - gint px, py, pw, ph; - - pw = gdk_pixbuf_get_width(od->pixbuf); - ph = gdk_pixbuf_get_height(od->pixbuf); - px = od->x; - py = od->y; - - if (od->flags & OVL_RELATIVE) - { - if (px < 0) px = pr->window_width - pw + px; - if (py < 0) py = pr->window_height - ph + py; - } - - if (x) *x = px; - if (y) *y = py; - if (w) *w = pw; - if (h) *h = ph; -} - -static void pr_overlay_init_window(PixbufRenderer *pr, OverlayData *od) -{ - gint px, py, pw, ph; - GdkWindowAttr attributes; - gint attributes_mask; - - pr_overlay_get_position(pr, od, &px, &py, &pw, &ph); - - attributes.window_type = GDK_WINDOW_CHILD; - attributes.wclass = GDK_INPUT_OUTPUT; - attributes.width = pw; - attributes.height = ph; - attributes.event_mask = GDK_EXPOSURE_MASK; - attributes_mask = 0; - - od->window = gdk_window_new(GTK_WIDGET(pr)->window, &attributes, attributes_mask); - gdk_window_set_user_data(od->window, pr); - gdk_window_move(od->window, px, py); - gdk_window_show(od->window); -} - -static void pr_overlay_draw(PixbufRenderer *pr, gint x, gint y, gint w, gint h, - ImageTile *it) -{ - GtkWidget *box; - GList *work; - - box = GTK_WIDGET(pr); - - work = pr->overlay_list; - while (work) - { - OverlayData *od; - gint px, py, pw, ph; - gint rx, ry, rw, rh; - - od = work->data; - work = work->next; - - if (!od->window) pr_overlay_init_window(pr, od); - - pr_overlay_get_position(pr, od, &px, &py, &pw, &ph); - if (pr_clip_region(x, y, w, h, px, py, pw, ph, &rx, &ry, &rw, &rh)) - { - if (!pr->overlay_buffer) - { - pr->overlay_buffer = gdk_pixmap_new(((GtkWidget *)pr)->window, pr->tile_width, pr->tile_height, -1); - } - - if (it) - { -#if GTK_CHECK_VERSION(2,20,0) - gdk_draw_drawable(pr->overlay_buffer, box->style->fg_gc[gtk_widget_get_state(box)], -#else - gdk_draw_drawable(pr->overlay_buffer, box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - it->pixmap, - rx - (pr->x_offset + (it->x - pr->x_scroll)), - ry - (pr->y_offset + (it->y - pr->y_scroll)), - 0, 0, rw, rh); - gdk_draw_pixbuf(pr->overlay_buffer, -#if GTK_CHECK_VERSION(2,20,0) - box->style->fg_gc[gtk_widget_get_state(box)], -#else - box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - od->pixbuf, - rx - px, ry - py, - 0, 0, rw, rh, - pr->dither_quality, rx, ry); -#if GTK_CHECK_VERSION(2,20,0) - gdk_draw_drawable(od->window, box->style->fg_gc[gtk_widget_get_state(box)], -#else - gdk_draw_drawable(od->window, box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - pr->overlay_buffer, - 0, 0, - rx - px, ry - py, rw, rh); - } - else - { - /* no ImageTile means region may be larger than our scratch buffer */ - gint sx, sy; - - for (sx = rx; sx < rx + rw; sx += pr->tile_width) - for (sy = ry; sy < ry + rh; sy += pr->tile_height) - { - gint sw, sh; - - sw = MIN(rx + rw - sx, pr->tile_width); - sh = MIN(ry + rh - sy, pr->tile_height); - - gdk_draw_rectangle(pr->overlay_buffer, -#if GTK_CHECK_VERSION(2,20,0) - box->style->bg_gc[gtk_widget_get_state(box)], TRUE, -#else - box->style->bg_gc[GTK_WIDGET_STATE(box)], TRUE, -#endif - 0, 0, sw, sh); - gdk_draw_pixbuf(pr->overlay_buffer, -#if GTK_CHECK_VERSION(2,20,0) - box->style->fg_gc[gtk_widget_get_state(box)], -#else - box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - od->pixbuf, - sx - px, sy - py, - 0, 0, sw, sh, - pr->dither_quality, sx, sy); -#if GTK_CHECK_VERSION(2,20,0) - gdk_draw_drawable(od->window, box->style->fg_gc[gtk_widget_get_state(box)], -#else - gdk_draw_drawable(od->window, box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - pr->overlay_buffer, - 0, 0, - sx - px, sy - py, sw, sh); - } - } - } - } -} - -static void pr_overlay_queue_draw(PixbufRenderer *pr, OverlayData *od) -{ - gint x, y, w, h; - - pr_overlay_get_position(pr, od, &x, &y, &w, &h); - pr_queue(pr, pr->x_scroll - pr->x_offset + x, - pr->y_scroll - pr->y_offset + y, - w, h, - FALSE, TILE_RENDER_ALL, FALSE, FALSE); - - pr_border_draw(pr, x, y, w, h); -} - -static void pr_overlay_queue_all(PixbufRenderer *pr) -{ - GList *work; - - work = pr->overlay_list; - while (work) - { - OverlayData *od = work->data; - work = work->next; - - pr_overlay_queue_draw(pr, od); - } -} - -static void pr_overlay_update_sizes(PixbufRenderer *pr) -{ - GList *work; - - work = pr->overlay_list; - while (work) - { - OverlayData *od = work->data; - work = work->next; - - if (!od->window) pr_overlay_init_window(pr, od); - - if (od->flags & OVL_RELATIVE) - { - gint x, y, w, h; - - pr_overlay_get_position(pr, od, &x, &y, &w, &h); - gdk_window_move_resize(od->window, x, y, w, h); - } - } -} - -static OverlayData *pr_overlay_find(PixbufRenderer *pr, gint id) -{ - GList *work; - - work = pr->overlay_list; - while (work) - { - OverlayData *od = work->data; - work = work->next; - - if (od->id == id) return od; - } - - return NULL; -} - gint pixbuf_renderer_overlay_add(PixbufRenderer *pr, GdkPixbuf *pixbuf, gint x, gint y, OverlayRendererFlags flags) { - OverlayData *od; - gint id; - - g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), -1); - g_return_val_if_fail(pixbuf != NULL, -1); - - id = 1; - while (pr_overlay_find(pr, id)) id++; - - od = g_new0(OverlayData, 1); - od->id = id; - od->pixbuf = pixbuf; - g_object_ref(G_OBJECT(od->pixbuf)); - od->x = x; - od->y = y; - od->flags = flags; - - pr_overlay_init_window(pr, od); - - pr->overlay_list = g_list_append(pr->overlay_list, od); - - pr_overlay_queue_draw(pr, od); - - return od->id; -} - -static void pr_overlay_free(PixbufRenderer *pr, OverlayData *od) -{ - pr->overlay_list = g_list_remove(pr->overlay_list, od); - - if (od->pixbuf) g_object_unref(G_OBJECT(od->pixbuf)); - if (od->window) gdk_window_destroy(od->window); - g_free(od); - - if (!pr->overlay_list && pr->overlay_buffer) - { - g_object_unref(pr->overlay_buffer); - pr->overlay_buffer = NULL; - } -} - -static void pr_overlay_list_clear(PixbufRenderer *pr) -{ - while (pr->overlay_list) - { - OverlayData *od; - - od = pr->overlay_list->data; - pr_overlay_free(pr, od); - } -} - -static void pr_overlay_list_reset_window(PixbufRenderer *pr) -{ - GList *work; - - if (pr->overlay_buffer) g_object_unref(pr->overlay_buffer); - pr->overlay_buffer = NULL; - - work = pr->overlay_list; - while (work) - { - OverlayData *od = work->data; - work = work->next; - if (od->window) gdk_window_destroy(od->window); - od->window = NULL; - } + /* let's assume both renderers returns the same value */ + if (pr->renderer2) pr->renderer2->overlay_add(pr->renderer2, pixbuf, x, y, flags); + return pr->renderer->overlay_add(pr->renderer, pixbuf, x, y, flags); } void pixbuf_renderer_overlay_set(PixbufRenderer *pr, gint id, GdkPixbuf *pixbuf, gint x, gint y) { - OverlayData *od; - - g_return_if_fail(IS_PIXBUF_RENDERER(pr)); - - od = pr_overlay_find(pr, id); - if (!od) return; - - if (pixbuf) - { - gint px, py, pw, ph; - - g_object_ref(G_OBJECT(pixbuf)); - g_object_unref(G_OBJECT(od->pixbuf)); - od->pixbuf = pixbuf; - - od->x = x; - od->y = y; - - if (!od->window) pr_overlay_init_window(pr, od); - - pr_overlay_queue_draw(pr, od); - pr_overlay_get_position(pr, od, &px, &py, &pw, &ph); - gdk_window_move_resize(od->window, px, py, pw, ph); - } - else - { - pr_overlay_queue_draw(pr, od); - pr_overlay_free(pr, od); - } + pr->renderer->overlay_set(pr->renderer, id, pixbuf, x, y); + if (pr->renderer2) pr->renderer2->overlay_set(pr->renderer2, id, pixbuf, x, y); } gboolean pixbuf_renderer_overlay_get(PixbufRenderer *pr, gint id, GdkPixbuf **pixbuf, gint *x, gint *y) { - OverlayData *od; - - g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), FALSE); - - od = pr_overlay_find(pr, id); - if (!od) return FALSE; - - if (pixbuf) *pixbuf = od->pixbuf; - if (x) *x = od->x; - if (y) *y = od->y; - - return TRUE; + if (pr->renderer2) pr->renderer2->overlay_get(pr->renderer2, id, pixbuf, x, y); + return pr->renderer->overlay_get(pr->renderer, id, pixbuf, x, y); } void pixbuf_renderer_overlay_remove(PixbufRenderer *pr, gint id) { - pixbuf_renderer_overlay_set(pr, id, NULL, 0, 0); + pr->renderer->overlay_set(pr->renderer, id, NULL, 0, 0); + if (pr->renderer2) pr->renderer2->overlay_set(pr->renderer2, id, NULL, 0, 0); } -static void pr_hierarchy_changed_cb(GtkWidget *widget, GtkWidget *previous_toplevel, gpointer data) -{ - PixbufRenderer *pr = data; - pr_overlay_list_reset_window(pr); -} - - /* *------------------------------------------------------------------- * scroller overlay @@ -1359,75 +928,10 @@ *------------------------------------------------------------------- */ -static void pr_border_draw(PixbufRenderer *pr, gint x, gint y, gint w, gint h) -{ - GtkWidget *box; - gint rx, ry, rw, rh; - - box = GTK_WIDGET(pr); - - if (!box->window) return; - - if (!pr->pixbuf && !pr->source_tiles_enabled) - { - if (pr_clip_region(x, y, w, h, - 0, 0, - pr->window_width, pr->window_height, - &rx, &ry, &rw, &rh)) - { - gdk_window_clear_area(box->window, rx, ry, rw, rh); - pr_overlay_draw(pr, rx, ry, rw, rh, NULL); - } - return; - } - - if (pr->vis_width < pr->window_width) - { - if (pr->x_offset > 0 && - pr_clip_region(x, y, w, h, - 0, 0, - pr->x_offset, pr->window_height, - &rx, &ry, &rw, &rh)) - { - gdk_window_clear_area(box->window, rx, ry, rw, rh); - pr_overlay_draw(pr, rx, ry, rw, rh, NULL); - } - if (pr->window_width - pr->vis_width - pr->x_offset > 0 && - pr_clip_region(x, y, w, h, - pr->x_offset + pr->vis_width, 0, - pr->window_width - pr->vis_width - pr->x_offset, pr->window_height, - &rx, &ry, &rw, &rh)) - { - gdk_window_clear_area(box->window, rx, ry, rw, rh); - pr_overlay_draw(pr, rx, ry, rw, rh, NULL); - } - } - if (pr->vis_height < pr->window_height) - { - if (pr->y_offset > 0 && - pr_clip_region(x, y, w, h, - pr->x_offset, 0, - pr->vis_width, pr->y_offset, - &rx, &ry, &rw, &rh)) - { - gdk_window_clear_area(box->window, rx, ry, rw, rh); - pr_overlay_draw(pr, rx, ry, rw, rh, NULL); - } - if (pr->window_height - pr->vis_height - pr->y_offset > 0 && - pr_clip_region(x, y, w, h, - pr->x_offset, pr->y_offset + pr->vis_height, - pr->vis_width, pr->window_height - pr->vis_height - pr->y_offset, - &rx, &ry, &rw, &rh)) - { - gdk_window_clear_area(box->window, rx, ry, rw, rh); - pr_overlay_draw(pr, rx, ry, rw, rh, NULL); - } - } -} - static void pr_border_clear(PixbufRenderer *pr) { - pr_border_draw(pr, 0, 0, pr->window_width, pr->window_height); + pr->renderer->border_clear(pr->renderer); + if (pr->renderer2) pr->renderer2->border_clear(pr->renderer2); } void pixbuf_renderer_set_color(PixbufRenderer *pr, GdkColor *color) @@ -1462,6 +966,15 @@ #endif } +static void pr_redraw(PixbufRenderer *pr, gboolean new_data) +{ + pr->renderer->queue_clear(pr->renderer); + pr->renderer->redraw(pr->renderer, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, new_data, FALSE); + if (pr->renderer2) { + pr->renderer2->queue_clear(pr->renderer2); + pr->renderer2->redraw(pr->renderer2, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, new_data, FALSE); + } +} /* *------------------------------------------------------------------- @@ -1508,10 +1021,14 @@ if (!st) return FALSE; - x1 = ROUND_DOWN(pr->x_scroll, pr->tile_width); - y1 = ROUND_DOWN(pr->y_scroll, pr->tile_height); - x2 = ROUND_UP(pr->x_scroll + pr->vis_width, pr->tile_width); - y2 = ROUND_UP(pr->y_scroll + pr->vis_height, pr->tile_height); +// x1 = ROUND_DOWN(pr->x_scroll, pr->tile_width); +// y1 = ROUND_DOWN(pr->y_scroll, pr->tile_height); +// x2 = ROUND_UP(pr->x_scroll + pr->vis_width, pr->tile_width); +// y2 = ROUND_UP(pr->y_scroll + pr->vis_height, pr->tile_height); + x1 = pr->x_scroll; + y1 = pr->y_scroll; + x2 = pr->x_scroll + pr->vis_width; + y2 = pr->y_scroll + pr->vis_height; return !((gdouble)st->x * pr->scale > (gdouble)x2 || (gdouble)(st->x + pr->source_tile_width) * pr->scale < (gdouble)x1 || @@ -1596,9 +1113,10 @@ st->blank = FALSE; } - pr_tile_invalidate_region(pr, st->x * pr->scale, st->y * pr->scale, + pr->renderer->invalidate_region(pr->renderer, st->x * pr->scale, st->y * pr->scale, pr->source_tile_width * pr->scale, pr->source_tile_height * pr->scale); - + if (pr->renderer2) pr->renderer2->invalidate_region(pr->renderer2, st->x * pr->scale, st->y * pr->scale, + pr->source_tile_width * pr->scale, pr->source_tile_height * pr->scale); return st; } @@ -1628,7 +1146,7 @@ return NULL; } -static GList *pr_source_tile_compute_region(PixbufRenderer *pr, gint x, gint y, gint w, gint h, gboolean request) +GList *pr_source_tile_compute_region(PixbufRenderer *pr, gint x, gint y, gint w, gint h, gboolean request) { gint x1, y1; GList *list = NULL; @@ -1683,134 +1201,16 @@ if (pr->func_tile_request && pr->func_tile_request(pr, rx, ry, rw, rh, pixbuf, pr->func_tile_data)) { - pr_tile_invalidate_region(pr, rx * pr->scale, ry * pr->scale, + pr->renderer->invalidate_region(pr->renderer, rx * pr->scale, ry * pr->scale, rw * pr->scale, rh * pr->scale); + if (pr->renderer2) pr->renderer2->invalidate_region(pr->renderer2, rx * pr->scale, ry * pr->scale, + rw * pr->scale, rh * pr->scale); } g_object_unref(pixbuf); } } } -static gboolean pr_source_tile_render(PixbufRenderer *pr, ImageTile *it, - gint x, gint y, gint w, gint h, - gboolean new_data, gboolean fast) -{ - GtkWidget *box; - GList *list; - GList *work; - gboolean draw = FALSE; - - box = GTK_WIDGET(pr); - - if (pr->zoom == 1.0 || pr->scale == 1.0) - { - list = pr_source_tile_compute_region(pr, it->x + x, it->y + y, w, h, TRUE); - work = list; - while (work) - { - SourceTile *st; - gint rx, ry, rw, rh; - - st = work->data; - work = work->next; - - if (pr_clip_region(st->x, st->y, pr->source_tile_width, pr->source_tile_height, - it->x + x, it->y + y, w, h, - &rx, &ry, &rw, &rh)) - { - if (st->blank) - { - gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE, - rx - st->x, ry - st->y, rw, rh); - } - else /* (pr->zoom == 1.0 || pr->scale == 1.0) */ - { - gdk_draw_pixbuf(it->pixmap, -#if GTK_CHECK_VERSION(2,20,0) - box->style->fg_gc[gtk_widget_get_state(box)], -#else - box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - st->pixbuf, - rx - st->x, ry - st->y, - rx - it->x, ry - it->y, - rw, rh, - pr->dither_quality, rx, ry); - } - } - } - } - else - { - gdouble scale_x, scale_y; - gint sx, sy, sw, sh; - - if (pr->image_width == 0 || pr->image_height == 0) return FALSE; - scale_x = (gdouble)pr->width / pr->image_width; - scale_y = (gdouble)pr->height / pr->image_height; - - sx = (gdouble)(it->x + x) / scale_x; - sy = (gdouble)(it->y + y) / scale_y; - sw = (gdouble)w / scale_x; - sh = (gdouble)h / scale_y; - - if (pr->width < PR_MIN_SCALE_SIZE || pr->height < PR_MIN_SCALE_SIZE) fast = TRUE; - -#if 0 - /* draws red over draw region, to check for leaks (regions not filled) */ - pixbuf_set_rect_fill(it->pixbuf, x, y, w, h, 255, 0, 0, 255); -#endif - - list = pr_source_tile_compute_region(pr, sx, sy, sw, sh, TRUE); - work = list; - while (work) - { - SourceTile *st; - gint rx, ry, rw, rh; - gint stx, sty, stw, sth; - - st = work->data; - work = work->next; - - stx = floor((gdouble)st->x * scale_x); - sty = floor((gdouble)st->y * scale_y); - stw = ceil((gdouble)(st->x + pr->source_tile_width) * scale_x) - stx; - sth = ceil((gdouble)(st->y + pr->source_tile_height) * scale_y) - sty; - - if (pr_clip_region(stx, sty, stw, sth, - it->x + x, it->y + y, w, h, - &rx, &ry, &rw, &rh)) - { - if (st->blank) - { - gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE, - rx - st->x, ry - st->y, rw, rh); - } - else - { - gdouble offset_x; - gdouble offset_y; - - /* may need to use unfloored stx,sty values here */ - offset_x = (gdouble)(stx - it->x); - offset_y = (gdouble)(sty - it->y); - - gdk_pixbuf_scale(st->pixbuf, it->pixbuf, rx - it->x, ry - it->y, rw, rh, - (gdouble) 0.0 + offset_x, - (gdouble) 0.0 + offset_y, - scale_x, scale_y, - (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality); - draw = TRUE; - } - } - } - } - - g_list_free(list); - - return draw; -} - void pixbuf_renderer_set_tiles(PixbufRenderer *pr, gint width, gint height, gint tile_width, gint tile_height, gint cache_size, PixbufRendererTileRequestFunc func_request, @@ -1912,259 +1312,69 @@ pr_zoom_sync(pr, zoom, flags, x, y); } + /* *------------------------------------------------------------------- - * display tiles + * signal emission *------------------------------------------------------------------- */ -static ImageTile *pr_tile_new(gint x, gint y, gint width, gint height) -{ - ImageTile *it; - - it = g_new0(ImageTile, 1); - - it->x = x; - it->y = y; - it->w = width; - it->h = height; - - it->render_done = TILE_RENDER_NONE; - - return it; -} - -static void pr_tile_free(ImageTile *it) +static void pr_update_signal(PixbufRenderer *pr) { - if (!it) return; - - if (it->pixbuf) g_object_unref(it->pixbuf); - if (it->pixmap) g_object_unref(it->pixmap); - - g_free(it); +#if 0 + log_printf("FIXME: send updated signal\n"); +#endif + DEBUG_1("%s pixbuf renderer updated - started drawing %p, img: %dx%d", get_exec_time(), pr, pr->image_width, pr->image_height); + pr->debug_updated = TRUE; } -static void pr_tile_free_all(PixbufRenderer *pr) +static void pr_zoom_signal(PixbufRenderer *pr) { - GList *work; - - work = pr->tiles; - while (work) - { - ImageTile *it; - - it = work->data; - work = work->next; - - pr_tile_free(it); - } - - g_list_free(pr->tiles); - pr->tiles = NULL; - pr->tile_cache_size = 0; + g_signal_emit(pr, signals[SIGNAL_ZOOM], 0, pr->zoom); } -static ImageTile *pr_tile_add(PixbufRenderer *pr, gint x, gint y) +static void pr_clicked_signal(PixbufRenderer *pr, GdkEventButton *bevent) { - ImageTile *it; - - it = pr_tile_new(x, y, pr->tile_width, pr->tile_height); - - if (it->x + it->w > pr->width) it->w = pr->width - it->x; - if (it->y + it->h > pr->height) it->h = pr->height - it->y; - - pr->tiles = g_list_prepend(pr->tiles, it); - pr->tile_cache_size += it->size; - - return it; + g_signal_emit(pr, signals[SIGNAL_CLICKED], 0, bevent); } -static void pr_tile_remove(PixbufRenderer *pr, ImageTile *it) +static void pr_scroll_notify_signal(PixbufRenderer *pr) { - if (it->qd) + g_signal_emit(pr, signals[SIGNAL_SCROLL_NOTIFY], 0); +} + +void pr_render_complete_signal(PixbufRenderer *pr) +{ + if (!pr->complete) { - QueueData *qd = it->qd; - - it->qd = NULL; - pr->draw_queue = g_list_remove(pr->draw_queue, qd); - g_free(qd); - } - - if (it->qd2) - { - QueueData *qd = it->qd2; - - it->qd2 = NULL; - pr->draw_queue_2pass = g_list_remove(pr->draw_queue_2pass, qd); - g_free(qd); + g_signal_emit(pr, signals[SIGNAL_RENDER_COMPLETE], 0); + g_object_set(G_OBJECT(pr), "complete", TRUE, NULL); } - - pr->tiles = g_list_remove(pr->tiles, it); - pr->tile_cache_size -= it->size; - - pr_tile_free(it); -} - -static void pr_tile_free_space(PixbufRenderer *pr, guint space, ImageTile *it) -{ - GList *work; - guint tile_max; - - work = g_list_last(pr->tiles); - - if (pr->source_tiles_enabled && pr->scale < 1.0) + if (pr->debug_updated) { - gint tiles; - - tiles = (pr->vis_width / pr->tile_width + 1) * (pr->vis_height / pr->tile_height + 1); - tile_max = MAX(tiles * pr->tile_width * pr->tile_height * 3, - (gint)((gdouble)pr->tile_cache_max * 1048576.0 * pr->scale)); - } - else - { - tile_max = pr->tile_cache_max * 1048576; - } - - while (work && pr->tile_cache_size + space > tile_max) - { - ImageTile *needle; - - needle = work->data; - work = work->prev; - if (needle != it && - ((!needle->qd && !needle->qd2) || !pr_tile_is_visible(pr, needle))) pr_tile_remove(pr, needle); + DEBUG_1("%s pixbuf renderer done %p", get_exec_time(), pr); + pr->debug_updated = FALSE; } } -static void pr_tile_invalidate_all(PixbufRenderer *pr) -{ - GList *work; - - work = pr->tiles; - while (work) - { - ImageTile *it; - - it = work->data; - work = work->next; - - it->render_done = TILE_RENDER_NONE; - it->render_todo = TILE_RENDER_ALL; - it->blank = FALSE; - - it->w = MIN(pr->tile_width, pr->width - it->x); - it->h = MIN(pr->tile_height, pr->height - it->y); - } -} - -static void pr_tile_invalidate_region(PixbufRenderer *pr, gint x, gint y, gint w, gint h) -{ - gint x1, x2; - gint y1, y2; - GList *work; - - x1 = ROUND_DOWN(x, pr->tile_width); - x2 = ROUND_UP(x + w, pr->tile_width); - - y1 = ROUND_DOWN(y, pr->tile_height); - y2 = ROUND_UP(y + h, pr->tile_height); - - work = pr->tiles; - while (work) - { - ImageTile *it; - - it = work->data; - work = work->next; - - if (it->x < x2 && it->x + it->w > x1 && - it->y < y2 && it->y + it->h > y1) - { - it->render_done = TILE_RENDER_NONE; - it->render_todo = TILE_RENDER_ALL; - } - } -} - -static ImageTile *pr_tile_get(PixbufRenderer *pr, gint x, gint y, gboolean only_existing) +static void pr_drag_signal(PixbufRenderer *pr, GdkEventButton *bevent) { - GList *work; - - work = pr->tiles; - while (work) - { - ImageTile *it; - - it = work->data; - if (it->x == x && it->y == y) - { - pr->tiles = g_list_delete_link(pr->tiles, work); - pr->tiles = g_list_prepend(pr->tiles, it); - return it; - } - - work = work->next; - } - - if (only_existing) return NULL; - - return pr_tile_add(pr, x, y); + g_signal_emit(pr, signals[SIGNAL_DRAG], 0, bevent); } -static void pr_tile_prepare(PixbufRenderer *pr, ImageTile *it) +static void pr_update_pixel_signal(PixbufRenderer *pr) { - if (!it->pixmap) - { - GdkPixmap *pixmap; - guint size; - - pixmap = gdk_pixmap_new(((GtkWidget *)pr)->window, pr->tile_width, pr->tile_height, -1); - - size = pixmap_calc_size(pixmap); - pr_tile_free_space(pr, size, it); - - it->pixmap = pixmap; - it->size += size; - pr->tile_cache_size += size; - } - - if ((pr->zoom != 1.0 || pr->source_tiles_enabled || (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf)) || - pr->orientation != EXIF_ORIENTATION_TOP_LEFT || pr->func_post_process) && !it->pixbuf) - { - GdkPixbuf *pixbuf; - guint size; -#if 0 -/* I don't think that we need a pixbuf with alpha channel here */ - if (pr->pixbuf) - { - pixbuf = gdk_pixbuf_new(gdk_pixbuf_get_colorspace(pr->pixbuf), - gdk_pixbuf_get_has_alpha(pr->pixbuf), - gdk_pixbuf_get_bits_per_sample(pr->pixbuf), - pr->tile_width, pr->tile_height); - } - else -#endif - { - pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, pr->tile_width, pr->tile_height); - } - - size = gdk_pixbuf_get_rowstride(pixbuf) * pr->tile_height; - pr_tile_free_space(pr, size, it); - - it->pixbuf = pixbuf; - it->size += size; - pr->tile_cache_size += size; - } + g_signal_emit(pr, signals[SIGNAL_UPDATE_PIXEL], 0); } /* *------------------------------------------------------------------- - * drawing + * sync and clamp *------------------------------------------------------------------- */ -static void pr_tile_coords_map_orientation(PixbufRenderer *pr, +void pr_tile_coords_map_orientation(gint orientation, gdouble tile_x, gdouble tile_y, /* coordinates of the tile */ gdouble image_w, gdouble image_h, gdouble tile_w, gdouble tile_h, @@ -2172,7 +1382,7 @@ { *res_x = tile_x; *res_y = tile_y; - switch (pr->orientation) + switch (orientation) { case EXIF_ORIENTATION_TOP_LEFT: /* normal -- nothing to do */ @@ -2215,7 +1425,7 @@ // log_printf("tile coord y:%f, ih:%d, th:%f ry:%f\n", tile_y, image_h, tile_h, *res_x); } -static void pr_tile_region_map_orientation(PixbufRenderer *pr, +void pr_tile_region_map_orientation(gint orientation, gint area_x, gint area_y, /* coordinates of the area inside tile */ gint tile_w, gint tile_h, gint area_w, gint area_h, @@ -2227,7 +1437,7 @@ *res_w = area_w; *res_h = area_h; - switch (pr->orientation) + switch (orientation) { case EXIF_ORIENTATION_TOP_LEFT: /* normal -- nothing to do */ @@ -2278,7 +1488,7 @@ // log_printf("inside y:%d, th:%d, ah:%d ry:%d\n", area_y, tile_h, area_h, *res_x); } -static void pr_coords_map_orientation_reverse(PixbufRenderer *pr, +void pr_coords_map_orientation_reverse(gint orientation, gint area_x, gint area_y, gint tile_w, gint tile_h, gint area_w, gint area_h, @@ -2290,7 +1500,7 @@ *res_w = area_w; *res_h = area_h; - switch (pr->orientation) + switch (orientation) { case EXIF_ORIENTATION_TOP_LEFT: /* normal -- nothing to do */ @@ -2341,906 +1551,6 @@ } -static GdkPixbuf *pr_get_spare_tile(PixbufRenderer *pr) -{ - if (!pr->spare_tile) pr->spare_tile = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, pr->tile_width, pr->tile_height); - return pr->spare_tile; -} - -#define COLOR_BYTES 3 /* rgb */ - -static void pr_tile_rotate_90_clockwise(PixbufRenderer *pr, GdkPixbuf **tile, gint x, gint y, gint w, gint h) -{ - GdkPixbuf *src = *tile; - GdkPixbuf *dest; - gint srs, drs; - guchar *s_pix, *d_pix; - guchar *sp, *dp; - guchar *ip, *spi, *dpi; - gint i, j; - gint tw = pr->tile_width; - - srs = gdk_pixbuf_get_rowstride(src); - s_pix = gdk_pixbuf_get_pixels(src); - spi = s_pix + (x * COLOR_BYTES); - - dest = pr_get_spare_tile(pr); - drs = gdk_pixbuf_get_rowstride(dest); - d_pix = gdk_pixbuf_get_pixels(dest); - dpi = d_pix + (tw - 1) * COLOR_BYTES; - - for (i = y; i < y + h; i++) - { - sp = spi + (i * srs); - ip = dpi - (i * COLOR_BYTES); - for (j = x; j < x + w; j++) - { - dp = ip + (j * drs); - memcpy(dp, sp, COLOR_BYTES); - sp += COLOR_BYTES; - } - } - - pr->spare_tile = src; - *tile = dest; -} - -static void pr_tile_rotate_90_counter_clockwise(PixbufRenderer *pr, GdkPixbuf **tile, gint x, gint y, gint w, gint h) -{ - GdkPixbuf *src = *tile; - GdkPixbuf *dest; - gint srs, drs; - guchar *s_pix, *d_pix; - guchar *sp, *dp; - guchar *ip, *spi, *dpi; - gint i, j; - gint th = pr->tile_height; - - srs = gdk_pixbuf_get_rowstride(src); - s_pix = gdk_pixbuf_get_pixels(src); - spi = s_pix + (x * COLOR_BYTES); - - dest = pr_get_spare_tile(pr); - drs = gdk_pixbuf_get_rowstride(dest); - d_pix = gdk_pixbuf_get_pixels(dest); - dpi = d_pix + (th - 1) * drs; - - for (i = y; i < y + h; i++) - { - sp = spi + (i * srs); - ip = dpi + (i * COLOR_BYTES); - for (j = x; j < x + w; j++) - { - dp = ip - (j * drs); - memcpy(dp, sp, COLOR_BYTES); - sp += COLOR_BYTES; - } - } - - pr->spare_tile = src; - *tile = dest; -} - -static void pr_tile_mirror_only(PixbufRenderer *pr, GdkPixbuf **tile, gint x, gint y, gint w, gint h) -{ - GdkPixbuf *src = *tile; - GdkPixbuf *dest; - gint srs, drs; - guchar *s_pix, *d_pix; - guchar *sp, *dp; - guchar *spi, *dpi; - gint i, j; - - gint tw = pr->tile_width; - - srs = gdk_pixbuf_get_rowstride(src); - s_pix = gdk_pixbuf_get_pixels(src); - spi = s_pix + (x * COLOR_BYTES); - - dest = pr_get_spare_tile(pr); - drs = gdk_pixbuf_get_rowstride(dest); - d_pix = gdk_pixbuf_get_pixels(dest); - dpi = d_pix + (tw - x - 1) * COLOR_BYTES; - - for (i = y; i < y + h; i++) - { - sp = spi + (i * srs); - dp = dpi + (i * drs); - for (j = 0; j < w; j++) - { - memcpy(dp, sp, COLOR_BYTES); - sp += COLOR_BYTES; - dp -= COLOR_BYTES; - } - } - - pr->spare_tile = src; - *tile = dest; -} - -static void pr_tile_mirror_and_flip(PixbufRenderer *pr, GdkPixbuf **tile, gint x, gint y, gint w, gint h) -{ - GdkPixbuf *src = *tile; - GdkPixbuf *dest; - gint srs, drs; - guchar *s_pix, *d_pix; - guchar *sp, *dp; - guchar *spi, *dpi; - gint i, j; - gint tw = pr->tile_width; - gint th = pr->tile_height; - - srs = gdk_pixbuf_get_rowstride(src); - s_pix = gdk_pixbuf_get_pixels(src); - spi = s_pix + (x * COLOR_BYTES); - - dest = pr_get_spare_tile(pr); - drs = gdk_pixbuf_get_rowstride(dest); - d_pix = gdk_pixbuf_get_pixels(dest); - dpi = d_pix + (th - 1) * drs + (tw - 1) * COLOR_BYTES; - - for (i = y; i < y + h; i++) - { - sp = s_pix + (i * srs) + (x * COLOR_BYTES); - dp = dpi - (i * drs) - (x * COLOR_BYTES); - for (j = 0; j < w; j++) - { - memcpy(dp, sp, COLOR_BYTES); - sp += COLOR_BYTES; - dp -= COLOR_BYTES; - } - } - - pr->spare_tile = src; - *tile = dest; -} - -static void pr_tile_flip_only(PixbufRenderer *pr, GdkPixbuf **tile, gint x, gint y, gint w, gint h) -{ - GdkPixbuf *src = *tile; - GdkPixbuf *dest; - gint srs, drs; - guchar *s_pix, *d_pix; - guchar *sp, *dp; - guchar *spi, *dpi; - gint i; - gint th = pr->tile_height; - - srs = gdk_pixbuf_get_rowstride(src); - s_pix = gdk_pixbuf_get_pixels(src); - spi = s_pix + (x * COLOR_BYTES); - - dest = pr_get_spare_tile(pr); - drs = gdk_pixbuf_get_rowstride(dest); - d_pix = gdk_pixbuf_get_pixels(dest); - dpi = d_pix + (th - 1) * drs + (x * COLOR_BYTES); - - for (i = y; i < y + h; i++) - { - sp = spi + (i * srs); - dp = dpi - (i * drs); - memcpy(dp, sp, w * COLOR_BYTES); - } - - pr->spare_tile = src; - *tile = dest; -} - -static void pr_tile_apply_orientation(PixbufRenderer *pr, GdkPixbuf **pixbuf, gint x, gint y, gint w, gint h) -{ - switch (pr->orientation) - { - case EXIF_ORIENTATION_TOP_LEFT: - /* normal -- nothing to do */ - break; - case EXIF_ORIENTATION_TOP_RIGHT: - /* mirrored */ - { - pr_tile_mirror_only(pr, pixbuf, x, y, w, h); - } - break; - case EXIF_ORIENTATION_BOTTOM_RIGHT: - /* upside down */ - { - pr_tile_mirror_and_flip(pr, pixbuf, x, y, w, h); - } - break; - case EXIF_ORIENTATION_BOTTOM_LEFT: - /* flipped */ - { - pr_tile_flip_only(pr, pixbuf, x, y, w, h); - } - break; - case EXIF_ORIENTATION_LEFT_TOP: - { - pr_tile_flip_only(pr, pixbuf, x, y, w, h); - pr_tile_rotate_90_clockwise(pr, pixbuf, x, pr->tile_height - y - h, w, h); - } - break; - case EXIF_ORIENTATION_RIGHT_TOP: - /* rotated -90 (270) */ - { - pr_tile_rotate_90_clockwise(pr, pixbuf, x, y, w, h); - } - break; - case EXIF_ORIENTATION_RIGHT_BOTTOM: - { - pr_tile_flip_only(pr, pixbuf, x, y, w, h); - pr_tile_rotate_90_counter_clockwise(pr, pixbuf, x, pr->tile_height - y - h, w, h); - } - break; - case EXIF_ORIENTATION_LEFT_BOTTOM: - /* rotated 90 */ - { - pr_tile_rotate_90_counter_clockwise(pr, pixbuf, x, y, w, h); - } - break; - default: - /* The other values are out of range */ - break; - } -} - - -static void pr_tile_render(PixbufRenderer *pr, ImageTile *it, - gint x, gint y, gint w, gint h, - gboolean new_data, gboolean fast) -{ - GtkWidget *box; - gboolean has_alpha; - gboolean draw = FALSE; - - if (it->render_todo == TILE_RENDER_NONE && it->pixmap && !new_data) return; - - if (it->render_done != TILE_RENDER_ALL) - { - x = 0; - y = 0; - w = it->w; - h = it->h; - if (!fast) it->render_done = TILE_RENDER_ALL; - } - else if (it->render_todo != TILE_RENDER_AREA) - { - if (!fast) it->render_todo = TILE_RENDER_NONE; - return; - } - - if (!fast) it->render_todo = TILE_RENDER_NONE; - - if (new_data) it->blank = FALSE; - - pr_tile_prepare(pr, it); - has_alpha = (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf)); - - box = GTK_WIDGET(pr); - - /* FIXME checker colors for alpha should be configurable, - * also should be drawn for blank = TRUE - */ - - if (it->blank) - { - /* no data, do fast rect fill */ - gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE, - 0, 0, it->w, it->h); - } - else if (pr->source_tiles_enabled) - { - draw = pr_source_tile_render(pr, it, x, y, w, h, new_data, fast); - } - else if (pr->zoom == 1.0 || pr->scale == 1.0) - { - - gdouble src_x, src_y; - gint pb_x, pb_y; - gint pb_w, pb_h; - pr_tile_coords_map_orientation(pr, it->x, it->y, - pr->image_width, pr->image_height, - pr->tile_width, pr->tile_height, - &src_x, &src_y); - pr_tile_region_map_orientation(pr, x, y, - pr->tile_width, pr->tile_height, - w, h, - &pb_x, &pb_y, - &pb_w, &pb_h); - - if (has_alpha) - { - gdk_pixbuf_composite_color(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h, - (gdouble) 0.0 - src_x, - (gdouble) 0.0 - src_y, - 1.0, 1.0, GDK_INTERP_NEAREST, - 255, it->x + pb_x, it->y + pb_y, - PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2); - pr_tile_apply_orientation(pr, &it->pixbuf, pb_x, pb_y, pb_w, pb_h); - draw = TRUE; - } - else - { - - - if (pr->orientation == EXIF_ORIENTATION_TOP_LEFT && !(pr->func_post_process && !(pr->post_process_slow && fast))) - { - /* faster, simple, base orientation, no postprocessing */ - gdk_draw_pixbuf(it->pixmap, -#if GTK_CHECK_VERSION(2,20,0) - box->style->fg_gc[gtk_widget_get_state(box)], -#else - box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - pr->pixbuf, - it->x + x, it->y + y, - x, y, - w, h, - pr->dither_quality, it->x + x, it->y + y); - } - else - { - gdk_pixbuf_copy_area(pr->pixbuf, - src_x + pb_x, src_y + pb_y, - pb_w, pb_h, - it->pixbuf, - pb_x, pb_y); - pr_tile_apply_orientation(pr, &it->pixbuf, pb_x, pb_y, pb_w, pb_h); - draw = TRUE; - } - } - } - else - { - gdouble scale_x, scale_y; - gdouble src_x, src_y; - gint pb_x, pb_y; - gint pb_w, pb_h; - - if (pr->image_width == 0 || pr->image_height == 0) return; - - scale_x = (gdouble)pr->width / pr->image_width; - scale_y = (gdouble)pr->height / pr->image_height; - - pr_tile_coords_map_orientation(pr, it->x, it->y, - pr->image_width * scale_x, pr->image_height * scale_y, - pr->tile_width, pr->tile_height, - &src_x, &src_y); - pr_tile_region_map_orientation(pr, x, y, - pr->tile_width, pr->tile_height, - w, h, - &pb_x, &pb_y, - &pb_w, &pb_h); - switch (pr->orientation) - { - gdouble tmp; - case EXIF_ORIENTATION_LEFT_TOP: - case EXIF_ORIENTATION_RIGHT_TOP: - case EXIF_ORIENTATION_RIGHT_BOTTOM: - case EXIF_ORIENTATION_LEFT_BOTTOM: - tmp = scale_x; - scale_x = scale_y; - scale_y = tmp; - break; - default: - /* nothing to do */ - break; - } - - /* HACK: The pixbuf scalers get kinda buggy(crash) with extremely - * small sizes for anything but GDK_INTERP_NEAREST - */ - if (pr->width < PR_MIN_SCALE_SIZE || pr->height < PR_MIN_SCALE_SIZE) fast = TRUE; - - if (!has_alpha) - { - gdk_pixbuf_scale(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h, - (gdouble) 0.0 - src_x, - (gdouble) 0.0 - src_y, - scale_x, scale_y, - (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality); - } - else - { - gdk_pixbuf_composite_color(pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h, - (gdouble) 0.0 - src_x, - (gdouble) 0.0 - src_y, - scale_x, scale_y, - (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality, - 255, it->x + pb_x, it->y + pb_y, - PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2); - } - pr_tile_apply_orientation(pr, &it->pixbuf, pb_x, pb_y, pb_w, pb_h); - draw = TRUE; - } - - if (draw && it->pixbuf && !it->blank) - { - - if (pr->func_post_process && !(pr->post_process_slow && fast)) - pr->func_post_process(pr, &it->pixbuf, x, y, w, h, pr->post_process_user_data); - - gdk_draw_pixbuf(it->pixmap, -#if GTK_CHECK_VERSION(2,20,0) - box->style->fg_gc[gtk_widget_get_state(box)], -#else - box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - it->pixbuf, - x, y, - x, y, - w, h, - pr->dither_quality, it->x + x, it->y + y); - } - -#if 0 - /* enable this line for debugging the edges of tiles */ - gdk_draw_rectangle(it->pixmap, box->style->white_gc, - FALSE, 0, 0, it->w, it->h); - gdk_draw_rectangle(it->pixmap, box->style->white_gc, - FALSE, x, y, w, h); -#endif -} - - -static void pr_tile_expose(PixbufRenderer *pr, ImageTile *it, - gint x, gint y, gint w, gint h, - gboolean new_data, gboolean fast) -{ - GtkWidget *box; - - pr_tile_render(pr, it, x, y, w, h, new_data, fast); - - box = GTK_WIDGET(pr); - -#if GTK_CHECK_VERSION(2,20,0) - gdk_draw_drawable(box->window, box->style->fg_gc[gtk_widget_get_state(box)], -#else - gdk_draw_drawable(box->window, box->style->fg_gc[GTK_WIDGET_STATE(box)], -#endif - it->pixmap, x, y, - pr->x_offset + (it->x - pr->x_scroll) + x, pr->y_offset + (it->y - pr->y_scroll) + y, w, h); - - if (pr->overlay_list) - { - pr_overlay_draw(pr, pr->x_offset + (it->x - pr->x_scroll) + x, - pr->y_offset + (it->y - pr->y_scroll) + y, - w, h, - it); - } -} - - -static gboolean pr_tile_is_visible(PixbufRenderer *pr, ImageTile *it) -{ - return (it->x + it->w >= pr->x_scroll && it->x < pr->x_scroll + pr->vis_width && - it->y + it->h >= pr->y_scroll && it->y < pr->y_scroll + pr->vis_height); -} - -/* - *------------------------------------------------------------------- - * draw queue - *------------------------------------------------------------------- - */ - -static gint pr_get_queued_area(GList *work) -{ - gint area = 0; - - while (work) - { - QueueData *qd = work->data; - area += qd->w * qd->h; - work = work->next; - } - return area; -} - - -static gboolean pr_queue_schedule_next_draw(PixbufRenderer *pr, gboolean force_set) -{ - gfloat percent; - gint visible_area = pr->vis_width * pr->vis_height; - - if (!pr->loading) - { - /* 2pass prio */ - DEBUG_2("redraw priority: 2pass"); - pr->draw_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, pr_queue_draw_idle_cb, pr, NULL); - return FALSE; - } - - if (visible_area == 0) - { - /* not known yet */ - percent = 100.0; - } - else - { - percent = 100.0 * pr_get_queued_area(pr->draw_queue) / visible_area; - } - - if (percent > 10.0) - { - /* we have enough data for starting intensive redrawing */ - DEBUG_2("redraw priority: high %.2f %%", percent); - pr->draw_idle_id = g_idle_add_full(GDK_PRIORITY_REDRAW, pr_queue_draw_idle_cb, pr, NULL); - return FALSE; - } - - if (percent < 1.0 || force_set) - { - /* queue is (almost) empty, wait 50 ms*/ - DEBUG_2("redraw priority: wait %.2f %%", percent); - pr->draw_idle_id = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 50, pr_queue_draw_idle_cb, pr, NULL); - return FALSE; - } - - /* keep the same priority as before */ - DEBUG_2("redraw priority: no change %.2f %%", percent); - return TRUE; -} - - -static gboolean pr_queue_draw_idle_cb(gpointer data) -{ - PixbufRenderer *pr = data; - QueueData *qd; - gboolean fast; - - - if ((!pr->pixbuf && !pr->source_tiles_enabled) || - (!pr->draw_queue && !pr->draw_queue_2pass) || - !pr->draw_idle_id) - { - pr_render_complete_signal(pr); - - pr->draw_idle_id = 0; - return FALSE; - } - - if (pr->draw_queue) - { - qd = pr->draw_queue->data; - fast = (pr->zoom_2pass && ((pr->zoom_quality != GDK_INTERP_NEAREST && pr->scale != 1.0) || pr->post_process_slow)); - } - else - { - if (pr->loading) - { - /* still loading, wait till done (also drops the higher priority) */ - - return pr_queue_schedule_next_draw(pr, FALSE); - } - - qd = pr->draw_queue_2pass->data; - fast = FALSE; - } - -#if GTK_CHECK_VERSION(2,20,0) - if (gtk_widget_get_realized(pr)) -#else - if (GTK_WIDGET_REALIZED(pr)) -#endif - { - if (pr_tile_is_visible(pr, qd->it)) - { - pr_tile_expose(pr, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast); - } - else if (qd->new_data) - { - /* if new pixel data, and we already have a pixmap, update the tile */ - qd->it->blank = FALSE; - if (qd->it->pixmap && qd->it->render_done == TILE_RENDER_ALL) - { - pr_tile_render(pr, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast); - } - } - } - - if (pr->draw_queue) - { - qd->it->qd = NULL; - pr->draw_queue = g_list_remove(pr->draw_queue, qd); - if (fast) - { - if (qd->it->qd2) - { - pr_queue_merge(qd->it->qd2, qd); - g_free(qd); - } - else - { - qd->it->qd2 = qd; - pr->draw_queue_2pass = g_list_append(pr->draw_queue_2pass, qd); - } - } - else - { - g_free(qd); - } - } - else - { - qd->it->qd2 = NULL; - pr->draw_queue_2pass = g_list_remove(pr->draw_queue_2pass, qd); - g_free(qd); - } - - if (!pr->draw_queue && !pr->draw_queue_2pass) - { - pr_render_complete_signal(pr); - - pr->draw_idle_id = 0; - return FALSE; - } - - return pr_queue_schedule_next_draw(pr, FALSE); -} - -static void pr_queue_list_free(GList *list) -{ - GList *work; - - work = list; - while (work) - { - QueueData *qd; - - qd = work->data; - work = work->next; - - qd->it->qd = NULL; - qd->it->qd2 = NULL; - g_free(qd); - } - - g_list_free(list); -} - -static void pr_queue_clear(PixbufRenderer *pr) -{ - pr_queue_list_free(pr->draw_queue); - pr->draw_queue = NULL; - - pr_queue_list_free(pr->draw_queue_2pass); - pr->draw_queue_2pass = NULL; - - if (pr->draw_idle_id) - { - g_source_remove(pr->draw_idle_id); - pr->draw_idle_id = 0; - } -} - -static void pr_queue_merge(QueueData *parent, QueueData *qd) -{ - if (parent->x + parent->w < qd->x + qd->w) - { - parent->w += (qd->x + qd->w) - (parent->x + parent->w); - } - if (parent->x > qd->x) - { - parent->w += parent->x - qd->x; - parent->x = qd->x; - } - - if (parent->y + parent->h < qd->y + qd->h) - { - parent->h += (qd->y + qd->h) - (parent->y + parent->h); - } - if (parent->y > qd->y) - { - parent->h += parent->y - qd->y; - parent->y = qd->y; - } - - parent->new_data |= qd->new_data; -} - -static gboolean pr_clamp_to_visible(PixbufRenderer *pr, gint *x, gint *y, gint *w, gint *h) -{ - gint nx, ny; - gint nw, nh; - gint vx, vy; - gint vw, vh; - - vw = pr->vis_width; - vh = pr->vis_height; - - vx = pr->x_scroll; - vy = pr->y_scroll; - - if (*x + *w < vx || *x > vx + vw || *y + *h < vy || *y > vy + vh) return FALSE; - - /* now clamp it */ - nx = CLAMP(*x, vx, vx + vw); - nw = CLAMP(*w - (nx - *x), 1, vw); - - ny = CLAMP(*y, vy, vy + vh); - nh = CLAMP(*h - (ny - *y), 1, vh); - - *x = nx; - *y = ny; - *w = nw; - *h = nh; - - return TRUE; -} - -static gboolean pr_queue_to_tiles(PixbufRenderer *pr, gint x, gint y, gint w, gint h, - gboolean clamp, ImageTileRenderType render, - gboolean new_data, gboolean only_existing) -{ - gint i, j; - gint x1, x2; - gint y1, y2; - - if (clamp && !pr_clamp_to_visible(pr, &x, &y, &w, &h)) return FALSE; - - x1 = ROUND_DOWN(x, pr->tile_width); - x2 = ROUND_UP(x + w, pr->tile_width); - - y1 = ROUND_DOWN(y, pr->tile_height); - y2 = ROUND_UP(y + h, pr->tile_height); - - for (j = y1; j <= y2; j += pr->tile_height) - { - for (i = x1; i <= x2; i += pr->tile_width) - { - ImageTile *it; - - it = pr_tile_get(pr, i, j, - (only_existing && - (i + pr->tile_width < pr->x_scroll || - i > pr->x_scroll + pr->vis_width || - j + pr->tile_height < pr->y_scroll || - j > pr->y_scroll + pr->vis_height))); - if (it) - { - QueueData *qd; - - if ((render == TILE_RENDER_ALL && it->render_done != TILE_RENDER_ALL) || - (render == TILE_RENDER_AREA && it->render_todo != TILE_RENDER_ALL)) - { - it->render_todo = render; - } - - qd = g_new(QueueData, 1); - qd->it = it; - qd->new_data = new_data; - - if (i < x) - { - qd->x = x - i; - } - else - { - qd->x = 0; - } - qd->w = x + w - i - qd->x; - if (qd->x + qd->w > pr->tile_width) qd->w = pr->tile_width - qd->x; - - if (j < y) - { - qd->y = y - j; - } - else - { - qd->y = 0; - } - qd->h = y + h - j - qd->y; - if (qd->y + qd->h > pr->tile_height) qd->h = pr->tile_height - qd->y; - - if (qd->w < 1 || qd->h < 1) - { - g_free(qd); - } - else if (it->qd) - { - pr_queue_merge(it->qd, qd); - g_free(qd); - } - else - { - it->qd = qd; - pr->draw_queue = g_list_append(pr->draw_queue, qd); - } - } - } - } - - return TRUE; -} - -static void pr_queue(PixbufRenderer *pr, gint x, gint y, gint w, gint h, - gboolean clamp, ImageTileRenderType render, - gboolean new_data, gboolean only_existing) -{ - gint nx, ny; - - nx = CLAMP(x, 0, pr->width - 1); - ny = CLAMP(y, 0, pr->height - 1); - w -= (nx - x); - h -= (ny - y); - w = CLAMP(w, 0, pr->width - nx); - h = CLAMP(h, 0, pr->height - ny); - if (w < 1 || h < 1) return; - - if (pr_queue_to_tiles(pr, nx, ny, w, h, clamp, render, new_data, only_existing) && - ((!pr->draw_queue && !pr->draw_queue_2pass) || !pr->draw_idle_id)) - { - if (pr->draw_idle_id) - { - g_source_remove(pr->draw_idle_id); - pr->draw_idle_id = 0; - } - pr_queue_schedule_next_draw(pr, TRUE); - } -} - -static void pr_redraw(PixbufRenderer *pr, gboolean new_data) -{ - pr_queue_clear(pr); - pr_queue(pr, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, new_data, FALSE); -} - -/* - *------------------------------------------------------------------- - * signal emission - *------------------------------------------------------------------- - */ - -static void pr_update_signal(PixbufRenderer *pr) -{ -#if 0 - log_printf("FIXME: send updated signal\n"); -#endif - DEBUG_1("%s pixbuf renderer updated - started drawing %p, img: %dx%d", get_exec_time(), pr, pr->image_width, pr->image_height); - pr->debug_updated = TRUE; -} - -static void pr_zoom_signal(PixbufRenderer *pr) -{ - g_signal_emit(pr, signals[SIGNAL_ZOOM], 0, pr->zoom); -} - -static void pr_clicked_signal(PixbufRenderer *pr, GdkEventButton *bevent) -{ - g_signal_emit(pr, signals[SIGNAL_CLICKED], 0, bevent); -} - -static void pr_scroll_notify_signal(PixbufRenderer *pr) -{ - g_signal_emit(pr, signals[SIGNAL_SCROLL_NOTIFY], 0); -} - -static void pr_render_complete_signal(PixbufRenderer *pr) -{ - if (!pr->complete) - { - g_signal_emit(pr, signals[SIGNAL_RENDER_COMPLETE], 0); - g_object_set(G_OBJECT(pr), "complete", TRUE, NULL); - } - if (pr->debug_updated) - { - DEBUG_1("%s pixbuf renderer done %p", get_exec_time(), pr); - pr->debug_updated = FALSE; - } -} - -static void pr_drag_signal(PixbufRenderer *pr, GdkEventButton *bevent) -{ - g_signal_emit(pr, signals[SIGNAL_DRAG], 0, bevent); -} - -static void pr_update_pixel_signal(PixbufRenderer *pr) -{ - g_signal_emit(pr, signals[SIGNAL_UPDATE_PIXEL], 0); -} - -/* - *------------------------------------------------------------------- - * sync and clamp - *------------------------------------------------------------------- - */ static void pixbuf_renderer_sync_scroll_center(PixbufRenderer *pr) { @@ -3253,13 +1563,13 @@ * of the "broken image" icon. */ - if (pr->width > pr->window_width) + if (pr->width > pr->viewport_width) { src_x = pr->x_scroll + pr->vis_width / 2; pr->norm_center_x = (gdouble)src_x / pr->width; } - if (pr->height > pr->window_height) + if (pr->height > pr->viewport_height) { src_y = pr->y_scroll + pr->vis_height / 2; pr->norm_center_y = (gdouble)src_y / pr->height; @@ -3313,25 +1623,25 @@ old_vw = pr->vis_width; old_vh = pr->vis_height; - if (pr->width < pr->window_width) + if (pr->width < pr->viewport_width) { pr->vis_width = pr->width; - pr->x_offset = (pr->window_width - pr->width) / 2; + pr->x_offset = (pr->viewport_width - pr->width) / 2; } else { - pr->vis_width = pr->window_width; + pr->vis_width = pr->viewport_width; pr->x_offset = 0; } - if (pr->height < pr->window_height) + if (pr->height < pr->viewport_height) { pr->vis_height = pr->height; - pr->y_offset = (pr->window_height - pr->height) / 2; + pr->y_offset = (pr->viewport_height - pr->height) / 2; } else { - pr->vis_height = pr->window_height; + pr->vis_height = pr->viewport_height; pr->y_offset = 0; } @@ -3383,15 +1693,15 @@ } else { - max_w = pr->window_width; - max_h = pr->window_height; + max_w = pr->viewport_width; + max_h = pr->viewport_height; } if ((pr->zoom_expand && !sizeable) || w > max_w || h > max_h) { - if ((gdouble)max_w / w > (gdouble)max_h / h) + if ((gdouble)max_w / w > (gdouble)max_h / h / pr->aspect_ratio) { - scale = (gdouble)max_h / h; + scale = (gdouble)max_h / h / pr->aspect_ratio; h = max_h; w = w * scale + 0.5; if (w > max_w) w = max_w; @@ -3400,7 +1710,7 @@ { scale = (gdouble)max_w / w; w = max_w; - h = h * scale + 0.5; + h = h * scale * pr->aspect_ratio + 0.5; if (h > max_h) h = max_h; } @@ -3424,13 +1734,13 @@ { scale = zoom; w = w * scale; - h = h * scale; + h = h * scale * pr->aspect_ratio; } else /* zoom out */ { scale = 1.0 / (0.0 - zoom); w = w * scale; - h = h * scale; + h = h * scale * pr->aspect_ratio; } invalid = (pr->width != w || pr->height != h); @@ -3442,7 +1752,8 @@ if (invalidate || invalid) { - pr_tile_invalidate_all(pr); + pr->renderer->invalidate_all(pr->renderer); + if (pr->renderer2) pr->renderer2->invalidate_all(pr->renderer2); if (!lazy) pr_redraw(pr, TRUE); } if (redrawn) *redrawn = (invalidate || invalid); @@ -3497,12 +1808,12 @@ case PR_SCROLL_RESET_NOCHANGE: /* maintain old scroll position */ pr->x_scroll = ((gdouble)pr->image_width * old_center_x * pr->scale) - pr->vis_width / 2; - pr->y_scroll = ((gdouble)pr->image_height * old_center_y * pr->scale) - pr->vis_height / 2; + pr->y_scroll = ((gdouble)pr->image_height * old_center_y * pr->scale * pr->aspect_ratio) - pr->vis_height / 2; break; case PR_SCROLL_RESET_CENTER: /* center new image */ pr->x_scroll = ((gdouble)pr->image_width / 2.0 * pr->scale) - pr->vis_width / 2; - pr->y_scroll = ((gdouble)pr->image_height / 2.0 * pr->scale) - pr->vis_height / 2; + pr->y_scroll = ((gdouble)pr->image_height / 2.0 * pr->scale * pr->aspect_ratio) - pr->vis_height / 2; break; case PR_SCROLL_RESET_TOPLEFT: default: @@ -3518,12 +1829,12 @@ if (center_point) { pr->x_scroll = old_cx / old_scale * pr->scale - (px - pr->x_offset); - pr->y_scroll = old_cy / old_scale * pr->scale - (py - pr->y_offset); + pr->y_scroll = old_cy / old_scale * pr->scale * pr->aspect_ratio - (py - pr->y_offset); } else { pr->x_scroll = old_cx / old_scale * pr->scale - (pr->vis_width / 2); - pr->y_scroll = old_cy / old_scale * pr->scale - (pr->vis_height / 2); + pr->y_scroll = old_cy / old_scale * pr->scale * pr->aspect_ratio - (pr->vis_height / 2); } } @@ -3537,7 +1848,8 @@ if (lazy) { - pr_queue_clear(pr); + pr->renderer->queue_clear(pr->renderer); + if (pr->renderer2) pr->renderer2->queue_clear(pr->renderer2); } else { @@ -3553,10 +1865,33 @@ { gboolean zoom_changed = FALSE; - if (pr->window_width == new_width && pr->window_height == new_height) return; + gint new_viewport_width = new_width; + gint new_viewport_height = new_height; + + if (!pr->stereo_temp_disable) + { + if (pr->stereo_mode & PR_STEREO_HORIZ) + { + new_viewport_width = new_width / 2; + } + else if (pr->stereo_mode & PR_STEREO_VERT) + { + new_viewport_height = new_height / 2; + } + else if (pr->stereo_mode & PR_STEREO_FIXED) + { + new_viewport_width = pr->stereo_fixed_width; + new_viewport_height = pr->stereo_fixed_height; + } + } + + if (pr->window_width == new_width && pr->window_height == new_height && + pr->viewport_width == new_viewport_width && pr->viewport_height == new_viewport_height) return; pr->window_width = new_width; pr->window_height = new_height; + pr->viewport_width = new_viewport_width; + pr->viewport_height = new_viewport_height; if (pr->zoom == 0.0) { @@ -3568,7 +1903,8 @@ pr_size_clamp(pr); pr_scroll_clamp(pr); - pr_overlay_update_sizes(pr); + pr->renderer->update_sizes(pr->renderer); + if (pr->renderer2) pr->renderer2->update_sizes(pr->renderer2); /* ensure scroller remains visible */ if (pr->scroller_overlay != -1) @@ -3622,15 +1958,13 @@ { gint x, y; - pr_border_draw(pr, area->x, area->y, area->width, area->height); - - x = MAX(0, (gint)area->x - pr->x_offset + pr->x_scroll); - y = MAX(0, (gint)area->y - pr->y_offset + pr->y_scroll); - - pr_queue(pr, x, y, - MIN((gint)area->width, pr->width - x), - MIN((gint)area->height, pr->height - y), - FALSE, TILE_RENDER_ALL, FALSE, FALSE); + pr->renderer->redraw(pr->renderer, area->x, area->y, area->width, area->height, + FALSE, TILE_RENDER_ALL, FALSE, FALSE); + if (pr->renderer2) + { + pr->renderer2->redraw(pr->renderer2, area->x, area->y, area->width, area->height, + FALSE, TILE_RENDER_ALL, FALSE, FALSE); + } } /* @@ -3643,7 +1977,6 @@ { gint old_x, old_y; gint x_off, y_off; - gint w, h; g_return_if_fail(IS_PIXBUF_RENDERER(pr)); @@ -3665,93 +1998,9 @@ x_off = pr->x_scroll - old_x; y_off = pr->y_scroll - old_y; - - w = pr->vis_width - abs(x_off); - h = pr->vis_height - abs(y_off); - - if (w < 1 || h < 1) - { - /* scrolled completely to new material */ - pr_queue(pr, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, FALSE, FALSE); - return; - } - else - { - gint x1, y1; - gint x2, y2; - GtkWidget *box; - GdkGC *gc; - GdkEvent *event; - - if (x_off < 0) - { - x1 = abs(x_off); - x2 = 0; - } - else - { - x1 = 0; - x2 = abs(x_off); - } - - if (y_off < 0) - { - y1 = abs(y_off); - y2 = 0; - } - else - { - y1 = 0; - y2 = abs(y_off); - } - - box = GTK_WIDGET(pr); - - gc = gdk_gc_new(box->window); - gdk_gc_set_exposures(gc, TRUE); - gdk_draw_drawable(box->window, gc, - box->window, - x2 + pr->x_offset, y2 + pr->y_offset, - x1 + pr->x_offset, y1 + pr->y_offset, w, h); - g_object_unref(gc); - - if (pr->overlay_list) - { - pr_overlay_queue_all(pr); - } - - w = pr->vis_width - w; - h = pr->vis_height - h; - - if (w > 0) - { - pr_queue(pr, - x_off > 0 ? pr->x_scroll + (pr->vis_width - w) : pr->x_scroll, pr->y_scroll, - w, pr->vis_height, TRUE, TILE_RENDER_ALL, FALSE, FALSE); - } - if (h > 0) - { - /* FIXME, to optimize this, remove overlap */ - pr_queue(pr, - pr->x_scroll, y_off > 0 ? pr->y_scroll + (pr->vis_height - h) : pr->y_scroll, - pr->vis_width, h, TRUE, TILE_RENDER_ALL, FALSE, FALSE); - } - - /* process exposures here, "expose_event" seems to miss a few with obstructed windows */ -#if ! GTK_CHECK_VERSION(2,18,0) - while ((event = gdk_event_get_graphics_expose(box->window)) != NULL) - { - pixbuf_renderer_paint(pr, &event->expose.area); - - if (event->expose.count == 0) - { - gdk_event_free(event); - break; - } - gdk_event_free(event); - } -#endif - } + + pr->renderer->scroll(pr->renderer, x_off, y_off); + if (pr->renderer2) pr->renderer2->scroll(pr->renderer2, x_off, y_off); } void pixbuf_renderer_scroll_to_point(PixbufRenderer *pr, gint x, gint y, @@ -3767,7 +2016,7 @@ ay = (gdouble)pr->vis_height * y_align; px = (gdouble)x * pr->scale - (pr->x_scroll + ax); - py = (gdouble)y * pr->scale - (pr->y_scroll + ay); + py = (gdouble)y * pr->scale * pr->aspect_ratio - (pr->y_scroll + ay); pixbuf_renderer_scroll(pr, px, py); } @@ -3929,7 +2178,7 @@ } #if GTK_CHECK_VERSION(2,20,0) - if (gdk_pointer_is_grabbed() && gtk_widget_has_grab(pr)) + if (gdk_pointer_is_grabbed() && gtk_widget_has_grab(GTK_WIDGET(pr))) #else if (gdk_pointer_is_grabbed() && GTK_WIDGET_HAS_GRAB(pr)) #endif @@ -3992,8 +2241,6 @@ G_CALLBACK(pr_mouse_release_cb), pr); g_signal_connect(G_OBJECT(pr), "leave_notify_event", G_CALLBACK(pr_mouse_leave_cb), pr); - g_signal_connect(G_OBJECT(pr), "hierarchy-changed", - G_CALLBACK(pr_hierarchy_changed_cb), pr); g_signal_connect(G_OBJECT(pr), "leave_notify_event", G_CALLBACK(pr_leave_notify_cb), pr); @@ -4008,11 +2255,138 @@ /* *------------------------------------------------------------------- + * stereo support + *------------------------------------------------------------------- + */ + +#define COLOR_BYTES 3 /* rgb */ +static void pr_create_anaglyph_RC(GdkPixbuf *pixbuf, GdkPixbuf *right, gint x, gint y, gint w, gint h) +{ + gint srs, drs; + guchar *s_pix, *d_pix; + guchar *sp, *dp; + guchar *spi, *dpi; + gint i, j; + + srs = gdk_pixbuf_get_rowstride(right); + s_pix = gdk_pixbuf_get_pixels(right); + spi = s_pix + (x * COLOR_BYTES); + + drs = gdk_pixbuf_get_rowstride(pixbuf); + d_pix = gdk_pixbuf_get_pixels(pixbuf); + dpi = d_pix + x * COLOR_BYTES; + + for (i = y; i < y + h; i++) + { + sp = spi + (i * srs); + dp = dpi + (i * drs); + for (j = 0; j < w; j++) + { + *dp = *sp; /* copy red channel */ + sp += COLOR_BYTES; + dp += COLOR_BYTES; + } + } +} + +static void pr_create_anaglyph_gray(GdkPixbuf *pixbuf, GdkPixbuf *right, gint x, gint y, gint w, gint h) +{ + gint srs, drs; + guchar *s_pix, *d_pix; + guchar *sp, *dp; + guchar *spi, *dpi; + gint i, j; + const double gc[3] = {0.299, 0.587, 0.114}; + + srs = gdk_pixbuf_get_rowstride(right); + s_pix = gdk_pixbuf_get_pixels(right); + spi = s_pix + (x * COLOR_BYTES); + + drs = gdk_pixbuf_get_rowstride(pixbuf); + d_pix = gdk_pixbuf_get_pixels(pixbuf); + dpi = d_pix + x * COLOR_BYTES; + + for (i = y; i < y + h; i++) + { + sp = spi + (i * srs); + dp = dpi + (i * drs); + for (j = 0; j < w; j++) + { + guchar g1 = dp[0] * gc[0] + dp[1] * gc[1] + dp[2] * gc[2]; + guchar g2 = sp[0] * gc[0] + sp[1] * gc[1] + sp[2] * gc[2]; + dp[0] = g2; /* red channel from sp */ + dp[1] = g1; /* green and blue from dp */ + dp[2] = g1; + sp += COLOR_BYTES; + dp += COLOR_BYTES; + } + } +} + +const double pr_dubois_matrix[3][6] = { + { 0.456, 0.500, 0.176, -0.043, -0.088, -0.002}, + {-0.040, -0.038, -0.016, 0.378, 0.734, -0.018}, + {-0.015, -0.021, -0.005, -0.072, -0.113, 1.226} + }; + +static void pr_create_anaglyph_dubois(GdkPixbuf *pixbuf, GdkPixbuf *right, gint x, gint y, gint w, gint h) +{ + gint srs, drs; + guchar *s_pix, *d_pix; + guchar *sp, *dp; + guchar *spi, *dpi; + gint i, j, k; + + srs = gdk_pixbuf_get_rowstride(right); + s_pix = gdk_pixbuf_get_pixels(right); + spi = s_pix + (x * COLOR_BYTES); + + drs = gdk_pixbuf_get_rowstride(pixbuf); + d_pix = gdk_pixbuf_get_pixels(pixbuf); + dpi = d_pix + x * COLOR_BYTES; + + for (i = y; i < y + h; i++) + { + sp = spi + (i * srs); + dp = dpi + (i * drs); + for (j = 0; j < w; j++) + { + double res[3]; + for (k = 0; k < 3; k++) + { + double *m = pr_dubois_matrix[k]; + res[k] = sp[0] * m[0] + sp[1] * m[1] + sp[2] * m[2] + dp[0] * m[3] + dp[1] * m[4] + dp[2] * m[5]; + if (res[k] < 0.0) res[k] = 0; + if (res[k] > 255.0) res[k] = 255.0; + } + dp[0] = res[0]; + dp[1] = res[1]; + dp[2] = res[2]; + sp += COLOR_BYTES; + dp += COLOR_BYTES; + } + } +} + +void pr_create_anaglyph(guint mode, GdkPixbuf *pixbuf, GdkPixbuf *right, gint x, gint y, gint w, gint h) +{ + if (mode & PR_STEREO_ANAGLYPH_RC) + pr_create_anaglyph_RC(pixbuf, right, x, y, w, h); + else if (mode & PR_STEREO_ANAGLYPH_GRAY) + pr_create_anaglyph_gray(pixbuf, right, x, y, w, h); + else if (mode & PR_STEREO_ANAGLYPH_DB) + pr_create_anaglyph_dubois(pixbuf, right, x, y, w, h); +} + +/* + *------------------------------------------------------------------- * public *------------------------------------------------------------------- */ static void pr_pixbuf_size_sync(PixbufRenderer *pr) { + pr->stereo_pixbuf_offset_left = 0; + pr->stereo_pixbuf_offset_right = 0; if (!pr->pixbuf) return; switch (pr->orientation) { @@ -4022,10 +2396,31 @@ case EXIF_ORIENTATION_LEFT_BOTTOM: pr->image_width = gdk_pixbuf_get_height(pr->pixbuf); pr->image_height = gdk_pixbuf_get_width(pr->pixbuf); + if (pr->stereo_data == STEREO_PIXBUF_SBS) + { + pr->image_height /= 2; + pr->stereo_pixbuf_offset_right = pr->image_height; + } + else if (pr->stereo_data == STEREO_PIXBUF_CROSS) + { + pr->image_height /= 2; + pr->stereo_pixbuf_offset_left = pr->image_height; + } + break; default: pr->image_width = gdk_pixbuf_get_width(pr->pixbuf); pr->image_height = gdk_pixbuf_get_height(pr->pixbuf); + if (pr->stereo_data == STEREO_PIXBUF_SBS) + { + pr->image_width /= 2; + pr->stereo_pixbuf_offset_right = pr->image_width; + } + else if (pr->stereo_data == STEREO_PIXBUF_CROSS) + { + pr->image_width /= 2; + pr->stereo_pixbuf_offset_left = pr->image_width; + } } } @@ -4055,7 +2450,8 @@ #endif { gdk_window_clear(box->window); - pr_overlay_draw(pr, 0, 0, pr->window_width, pr->window_height, NULL); + pr->renderer->overlay_draw(pr->renderer, 0, 0, pr->viewport_width, pr->viewport_height); + if (pr->renderer2) pr->renderer2->overlay_draw(pr->renderer2, 0, 0, pr->viewport_width, pr->viewport_height); } pr_update_signal(pr); @@ -4063,6 +2459,12 @@ return; } + if (pr->stereo_mode & PR_STEREO_TEMP_DISABLE) + { + gint disable = !pr->pixbuf || ! pr->stereo_data; + pr_stereo_temp_disable(pr, disable); + } + pr_pixbuf_size_sync(pr); pr_zoom_sync(pr, zoom, flags | PR_ZOOM_FORCE | PR_ZOOM_NEW, 0, 0); } @@ -4078,13 +2480,14 @@ pr_update_signal(pr); } -void pixbuf_renderer_set_pixbuf_lazy(PixbufRenderer *pr, GdkPixbuf *pixbuf, gdouble zoom, gint orientation) +void pixbuf_renderer_set_pixbuf_lazy(PixbufRenderer *pr, GdkPixbuf *pixbuf, gdouble zoom, gint orientation, StereoPixbufData stereo_data) { g_return_if_fail(IS_PIXBUF_RENDERER(pr)); pr_source_tile_unset(pr); pr->orientation = orientation; + pr->stereo_data = stereo_data; pr_set_pixbuf(pr, pixbuf, zoom, PR_ZOOM_LAZY); pr_update_signal(pr); @@ -4113,6 +2516,21 @@ return pr->orientation; } +void pixbuf_renderer_set_stereo_data(PixbufRenderer *pr, StereoPixbufData stereo_data) +{ + g_return_if_fail(IS_PIXBUF_RENDERER(pr)); + + pr->stereo_data = stereo_data; + + if (pr->stereo_mode & PR_STEREO_TEMP_DISABLE) + { + gint disable = !pr->pixbuf || ! pr->stereo_data; + pr_stereo_temp_disable(pr, disable); + } + pr_pixbuf_size_sync(pr); + pr_zoom_sync(pr, pr->zoom, PR_ZOOM_FORCE, 0, 0); +} + void pixbuf_renderer_set_post_process_func(PixbufRenderer *pr, PixbufRendererPostProcessFunc func, gpointer user_data, gboolean slow) { g_return_if_fail(IS_PIXBUF_RENDERER(pr)); @@ -4154,6 +2572,7 @@ pr->post_process_user_data = source->post_process_user_data; pr->post_process_slow = source->post_process_slow; pr->orientation = source->orientation; + pr->stereo_data = source->stereo_data; if (source->source_tiles_enabled) { @@ -4184,41 +2603,21 @@ pr->scroll_reset = scroll_reset; pixbuf_renderer_set_pixbuf(source, NULL, source->zoom); - pr_queue_clear(source); - pr_tile_free_all(source); +// pr_queue_clear(source); +// pr_tile_free_all(source); } -void pixbuf_renderer_area_changed(PixbufRenderer *pr, gint src_x, gint src_y, gint src_w, gint src_h) +void pixbuf_renderer_area_changed(PixbufRenderer *pr, gint x, gint y, gint w, gint h) { - gint x, y, width, height, x1, y1, x2, y2; - g_return_if_fail(IS_PIXBUF_RENDERER(pr)); - pr_coords_map_orientation_reverse(pr, - src_x, src_y, - pr->image_width, pr->image_height, - src_w, src_h, - &x, &y, - &width, &height); - if (pr->source_tiles_enabled) { - pr_source_tile_changed(pr, x, y, width, height); + pr_source_tile_changed(pr, x, y, w, h); } - if (pr->scale != 1.0 && pr->zoom_quality != GDK_INTERP_NEAREST) - { - /* increase region when using a zoom quality that may access surrounding pixels */ - y -= 1; - height += 2; - } - - x1 = (gint)floor((gdouble)x * pr->scale); - y1 = (gint)floor((gdouble)y * pr->scale); - x2 = (gint)ceil((gdouble)(x + width) * pr->scale); - y2 = (gint)ceil((gdouble)(y + height) * pr->scale); - - pr_queue(pr, x1, y1, x2 - x1, y2 - y1, FALSE, TILE_RENDER_AREA, TRUE, TRUE); + pr->renderer->area_changed(pr->renderer, x, y, w, h); + if (pr->renderer2) pr->renderer2->area_changed(pr->renderer2, x, y, w, h); } void pixbuf_renderer_zoom_adjust(PixbufRenderer *pr, gdouble increment) @@ -4276,6 +2675,85 @@ } } +static void pr_stereo_set(PixbufRenderer *pr) +{ + if (!pr->renderer) pr->renderer = (void *)renderer_tiles_new(pr); + + pr->renderer->stereo_set(pr->renderer, pr->stereo_mode & ~PR_STEREO_MIRROR_RIGHT & ~PR_STEREO_FLIP_RIGHT); + + if (pr->stereo_mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED)) + { + if (!pr->renderer2) pr->renderer2 = (void *)renderer_tiles_new(pr); + pr->renderer2->stereo_set(pr->renderer2, (pr->stereo_mode & ~PR_STEREO_MIRROR_LEFT & ~PR_STEREO_FLIP_LEFT) | PR_STEREO_RIGHT); + } + else + { + if (pr->renderer2) pr->renderer2->free(pr->renderer2); + pr->renderer2 = NULL; + } + if (pr->stereo_mode & PR_STEREO_HALF) + { + if (pr->stereo_mode & PR_STEREO_HORIZ) pr->aspect_ratio = 2.0; + else if (pr->stereo_mode & PR_STEREO_VERT) pr->aspect_ratio = 0.5; + else pr->aspect_ratio = 1.0; + } + else + { + pr->aspect_ratio = 1.0; + } +} + +void pixbuf_renderer_stereo_set(PixbufRenderer *pr, gint stereo_mode) +{ + gboolean redraw = !(pr->stereo_mode == stereo_mode) || pr->stereo_temp_disable; + pr->stereo_mode = stereo_mode; + if ((stereo_mode & PR_STEREO_TEMP_DISABLE) && pr->stereo_temp_disable) return; + + pr->stereo_temp_disable = FALSE; + + pr_stereo_set(pr); + + if (redraw) + { + pr_size_sync(pr, pr->window_width, pr->window_height); /* recalculate new viewport */ + pr_zoom_sync(pr, pr->zoom, PR_ZOOM_FORCE | PR_ZOOM_NEW, 0, 0); + } +} + +void pixbuf_renderer_stereo_fixed_set(PixbufRenderer *pr, gint width, gint height, gint x1, gint y1, gint x2, gint y2) +{ + pr->stereo_fixed_width = width; + pr->stereo_fixed_height = height; + pr->stereo_fixed_x_left = x1; + pr->stereo_fixed_y_left = y1; + pr->stereo_fixed_x_right = x2; + pr->stereo_fixed_y_right = y2; +} + +gint pixbuf_renderer_stereo_get(PixbufRenderer *pr) +{ + return pr->stereo_mode; +} + +static void pr_stereo_temp_disable(PixbufRenderer *pr, gboolean disable) +{ + if (pr->stereo_temp_disable == disable) return; + pr->stereo_temp_disable = disable; + if (disable) + { + if (!pr->renderer) pr->renderer = (void *)renderer_tiles_new(pr); + pr->renderer->stereo_set(pr->renderer, PR_STEREO_NONE); + if (pr->renderer2) pr->renderer2->free(pr->renderer2); + pr->renderer2 = NULL; + pr->aspect_ratio = 1.0; + } + else + { + pr_stereo_set(pr); + } + pr_size_sync(pr, pr->window_width, pr->window_height); /* recalculate new viewport */ +} + gboolean pixbuf_renderer_get_pixel_colors(PixbufRenderer *pr, gint x_pixel, gint y_pixel, gint *r_mouse, gint *g_mouse, gint *b_mouse) { @@ -4297,7 +2775,7 @@ if (!pb) return FALSE; - pr_tile_region_map_orientation(pr, + pr_tile_region_map_orientation(pr->orientation, x_pixel, y_pixel, pr->image_width, pr->image_height, 1, 1, /*single pixel */ @@ -4336,7 +2814,7 @@ } x_pixel = floor((gdouble)(pr->x_mouse - pr->x_offset + pr->x_scroll) / pr->scale); - y_pixel = floor((gdouble)(pr->y_mouse - pr->y_offset + pr->y_scroll) / pr->scale); + y_pixel = floor((gdouble)(pr->y_mouse - pr->y_offset + pr->y_scroll) / pr->scale / pr->aspect_ratio); x_pixel_clamped = CLAMP(x_pixel, 0, pr->image_width - 1); y_pixel_clamped = CLAMP(y_pixel, 0, pr->image_height - 1); @@ -4402,9 +2880,9 @@ } rect->x = (gint)((gdouble)pr->x_scroll / pr->scale); - rect->y = (gint)((gdouble)pr->y_scroll / pr->scale); + rect->y = (gint)((gdouble)pr->y_scroll / pr->scale / pr->aspect_ratio); rect->width = (gint)((gdouble)pr->vis_width / pr->scale); - rect->height = (gint)((gdouble)pr->vis_height / pr->scale); + rect->height = (gint)((gdouble)pr->vis_height / pr->scale / pr->aspect_ratio); return TRUE; }
--- a/src/pixbuf-renderer.h Fri Apr 22 11:29:35 2011 +0100 +++ b/src/pixbuf-renderer.h Sat Oct 29 23:41:36 2011 +0100 @@ -24,6 +24,24 @@ #define IS_PIXBUF_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_PIXBUF_RENDERER)) #define PIXBUF_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_PIXBUF_RENDERER, PixbufRendererClass)) +/* alpha channel checkerboard background (same as gimp) */ +#define PR_ALPHA_CHECK1 0x00999999 +#define PR_ALPHA_CHECK2 0x00666666 +#define PR_ALPHA_CHECK_SIZE 16 +/* when scaling image to below this size, use nearest pixel for scaling + * (below about 4, the other scale types become slow generating their conversion tables) + */ +#define PR_MIN_SCALE_SIZE 8 + +/* default size of tile cache (mb) */ +#define PR_CACHE_SIZE_DEFAULT 8 + +/* round A up/down to integer count of B */ +#define ROUND_UP(A,B) ((gint)(((A)+(B)-1)/(B))*(B)) +#define ROUND_DOWN(A,B) ((gint)(((A))/(B))*(B)) + + +typedef struct _RendererFuncs RendererFuncs; typedef struct _PixbufRenderer PixbufRenderer; typedef struct _PixbufRendererClass PixbufRendererClass; @@ -44,19 +62,58 @@ PR_SCROLL_RESET_COUNT, } PixbufRendererScrollResetType; +typedef enum { + TILE_RENDER_NONE = 0, /* do nothing */ + TILE_RENDER_AREA, /* render an area of the tile */ + TILE_RENDER_ALL /* render the whole tile */ +} ImageRenderType; + +typedef enum { + OVL_NORMAL = 0, + OVL_RELATIVE = 1 << 0, /* x,y coordinates are relative, negative values start bottom right */ + /* OVL_HIDE_ON_SCROLL = 1 << 1*/ /* hide temporarily when scrolling (not yet implemented) */ +} OverlayRendererFlags; + +struct _RendererFuncs +{ + void (*redraw)(void *renderer, gint x, gint y, gint w, gint h, + gint clamp, ImageRenderType render, gboolean new_data, gboolean only_existing); + void (*area_changed)(void *renderer, gint src_x, gint src_y, gint src_w, gint src_h); + void (*queue_clear)(void *renderer); + void (*border_clear)(void *renderer); + void (*invalidate_all)(void *renderer); + void (*invalidate_region)(void *renderer, gint x, gint y, gint w, gint h); + void (*scroll)(void *renderer, gint x_off, gint y_off); + void (*update_sizes)(void *renderer); + + gint (*overlay_add)(void *renderer, GdkPixbuf *pixbuf, gint x, gint y, OverlayRendererFlags flags); + void (*overlay_set)(void *renderer, gint id, GdkPixbuf *pixbuf, gint x, gint y); + gboolean (*overlay_get)(void *renderer, gint id, GdkPixbuf **pixbuf, gint *x, gint *y); + void (*overlay_draw)(void *renderer, gint x, gint y, gint w, gint h); + + void (*stereo_set)(void *renderer, gint stereo_mode); + + void (*free)(void *renderer); +}; + struct _PixbufRenderer { GtkEventBox eventbox; gint image_width; /* image actual dimensions (pixels) */ gint image_height; + gint stereo_pixbuf_offset_right; /* offset of the right part of the stereo image in pixbuf */ + gint stereo_pixbuf_offset_left; /* offset of the left part of the stereo image in pixbuf */ GdkPixbuf *pixbuf; gint window_width; /* allocated size of window (drawing area) */ gint window_height; - gint x_offset; /* offset of image start (non-zero when image < window) */ + gint viewport_width; /* allocated size of viewport (same as window for normal mode, half of window for SBS mode) */ + gint viewport_height; + + gint x_offset; /* offset of image start (non-zero when viewport < window) */ gint y_offset; gint x_mouse; /* coordinates of the mouse taken from GtkEvent */ @@ -82,6 +139,8 @@ gdouble zoom; /* zoom we want (0 is auto) */ gdouble scale; /* zoom we got (should never be 0) */ + gdouble aspect_ratio; /* screen pixel aspect ratio (2.0 for 3DTV SBS mode) */ + GdkInterpType zoom_quality; gboolean zoom_2pass; gboolean zoom_expand; @@ -102,20 +161,8 @@ gint autofit_limit_size; - gint tile_cache_max; /* max mb to use for offscreen buffer */ /*< private >*/ - - gint tile_width; - gint tile_height; - gint tile_cols; /* count of tile columns */ - GList *tiles; /* list of buffer tiles */ - gint tile_cache_size; /* allocated size of pixmaps/pixbufs */ - GList *draw_queue; /* list of areas to redraw */ - GList *draw_queue_2pass;/* list when 2 pass is enabled */ - - guint draw_idle_id; /* event source id */ - gboolean in_drag; gint drag_last_x; gint drag_last_y; @@ -151,12 +198,21 @@ gint scroller_xinc; gint scroller_yinc; - GList *overlay_list; - GdkPixmap *overlay_buffer; + gint orientation; - GdkPixbuf *spare_tile; - - gint orientation; + gint stereo_mode; + + StereoPixbufData stereo_data; + gboolean stereo_temp_disable; + gint stereo_fixed_width; + gint stereo_fixed_height; + gint stereo_fixed_x_left; + gint stereo_fixed_y_left; + gint stereo_fixed_x_right; + gint stereo_fixed_y_right; + + RendererFuncs *renderer; + RendererFuncs *renderer2; }; struct _PixbufRendererClass @@ -173,6 +229,8 @@ }; + + GType pixbuf_renderer_get_type(void); PixbufRenderer *pixbuf_renderer_new(void); @@ -185,7 +243,7 @@ void pixbuf_renderer_set_pixbuf(PixbufRenderer *pr, GdkPixbuf *pixbuf, gdouble zoom); /* same as pixbuf_renderer_set_pixbuf but waits with redrawing for pixbuf_renderer_area_changed */ -void pixbuf_renderer_set_pixbuf_lazy(PixbufRenderer *pr, GdkPixbuf *pixbuf, gdouble zoom, gint orientation); +void pixbuf_renderer_set_pixbuf_lazy(PixbufRenderer *pr, GdkPixbuf *pixbuf, gdouble zoom, gint orientation, StereoPixbufData stereo_data); GdkPixbuf *pixbuf_renderer_get_pixbuf(PixbufRenderer *pr); @@ -193,6 +251,9 @@ void pixbuf_renderer_set_orientation(PixbufRenderer *pr, gint orientation); gint pixbuf_renderer_get_orientation(PixbufRenderer *pr); +/* sets the format of stereo data in the input pixbuf */ +void pixbuf_renderer_set_stereo_data(PixbufRenderer *pr, StereoPixbufData stereo_data); + void pixbuf_renderer_set_post_process_func(PixbufRenderer *pr, PixbufRendererPostProcessFunc func, gpointer user_data, gboolean slow); /* display an on-request array of pixbuf tiles */ @@ -250,11 +311,6 @@ void pixbuf_renderer_set_color(PixbufRenderer *pr, GdkColor *color); /* overlay */ -typedef enum { - OVL_NORMAL = 0, - OVL_RELATIVE = 1 << 0, /* x,y coordinates are relative, negative values start bottom right */ - /* OVL_HIDE_ON_SCROLL = 1 << 1*/ /* hide temporarily when scrolling (not yet implemented) */ -} OverlayRendererFlags; gint pixbuf_renderer_overlay_add(PixbufRenderer *pr, GdkPixbuf *pixbuf, gint x, gint y, OverlayRendererFlags flags); @@ -265,9 +321,52 @@ gboolean pixbuf_renderer_get_mouse_position(PixbufRenderer *pr, gint *x_pixel, gint *y_pixel); /* x_pixel and y_pixel are the pixel coordinates \see pixbuf_renderer_get_mouse_position */ gboolean pixbuf_renderer_get_pixel_colors(PixbufRenderer *pr, gint x_pixel, gint y_pixel, - gint *r_mouse, gint *g_mouse, gint *b_mouse); + gint *r_mouse, gint *g_mouse, gint *b_mouse); void pixbuf_renderer_set_size_early(PixbufRenderer *pr, guint width, guint height); +/* stereo */ +void pixbuf_renderer_stereo_set(PixbufRenderer *pr, gint stereo_mode); +gint pixbuf_renderer_stereo_get(PixbufRenderer *pr); +void pixbuf_renderer_stereo_fixed_set(PixbufRenderer *pr, gint width, gint height, gint x1, gint y1, gint x2, gint y2); + +/* protected - for renderer use only*/ + +typedef struct _SourceTile SourceTile; +struct _SourceTile +{ + gint x; + gint y; + GdkPixbuf *pixbuf; + gboolean blank; +}; + + +gboolean pr_clip_region(gint x, gint y, gint w, gint h, + gint clip_x, gint clip_y, gint clip_w, gint clip_h, + gint *rx, gint *ry, gint *rw, gint *rh); +void pr_render_complete_signal(PixbufRenderer *pr); + +void pr_tile_coords_map_orientation(gint orientation, + gdouble tile_x, gdouble tile_y, /* coordinates of the tile */ + gdouble image_w, gdouble image_h, + gdouble tile_w, gdouble tile_h, + gdouble *res_x, gdouble *res_y); +void pr_tile_region_map_orientation(gint orientation, + gint area_x, gint area_y, /* coordinates of the area inside tile */ + gint tile_w, gint tile_h, + gint area_w, gint area_h, + gint *res_x, gint *res_y, + gint *res_w, gint *res_h); +void pr_coords_map_orientation_reverse(gint orientation, + gint area_x, gint area_y, + gint tile_w, gint tile_h, + gint area_w, gint area_h, + gint *res_x, gint *res_y, + gint *res_w, gint *res_h); + +GList *pr_source_tile_compute_region(PixbufRenderer *pr, gint x, gint y, gint w, gint h, gboolean request); + +void pr_create_anaglyph(guint mode, GdkPixbuf *pixbuf, GdkPixbuf *right, gint x, gint y, gint w, gint h); #endif /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/preferences.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/preferences.c Sat Oct 29 23:41:36 2011 +0100 @@ -305,6 +305,27 @@ options->metadata.confirm_on_dir_change = c_options->metadata.confirm_on_dir_change; options->metadata.keywords_case_sensitive = c_options->metadata.keywords_case_sensitive; options->metadata.write_orientation = c_options->metadata.write_orientation; + options->stereo.mode = (c_options->stereo.mode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) | + (c_options->stereo.tmp.mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) | + (c_options->stereo.tmp.flip_right ? PR_STEREO_FLIP_RIGHT : 0) | + (c_options->stereo.tmp.mirror_left ? PR_STEREO_MIRROR_LEFT : 0) | + (c_options->stereo.tmp.flip_left ? PR_STEREO_FLIP_LEFT : 0) | + (c_options->stereo.tmp.swap ? PR_STEREO_SWAP : 0) | + (c_options->stereo.tmp.temp_disable ? PR_STEREO_TEMP_DISABLE : 0); + options->stereo.fsmode = (c_options->stereo.fsmode & (PR_STEREO_HORIZ | PR_STEREO_VERT | PR_STEREO_FIXED | PR_STEREO_ANAGLYPH | PR_STEREO_HALF)) | + (c_options->stereo.tmp.fs_mirror_right ? PR_STEREO_MIRROR_RIGHT : 0) | + (c_options->stereo.tmp.fs_flip_right ? PR_STEREO_FLIP_RIGHT : 0) | + (c_options->stereo.tmp.fs_mirror_left ? PR_STEREO_MIRROR_LEFT : 0) | + (c_options->stereo.tmp.fs_flip_left ? PR_STEREO_FLIP_LEFT : 0) | + (c_options->stereo.tmp.fs_swap ? PR_STEREO_SWAP : 0) | + (c_options->stereo.tmp.fs_temp_disable ? PR_STEREO_TEMP_DISABLE : 0); + options->stereo.enable_fsmode = c_options->stereo.enable_fsmode; + options->stereo.fixed_w = c_options->stereo.fixed_w; + options->stereo.fixed_h = c_options->stereo.fixed_h; + options->stereo.fixed_x1 = c_options->stereo.fixed_x1; + options->stereo.fixed_y1 = c_options->stereo.fixed_y1; + options->stereo.fixed_x2 = c_options->stereo.fixed_x2; + options->stereo.fixed_y2 = c_options->stereo.fixed_y2; #ifdef DEBUG set_debug_level(debug_c); @@ -528,6 +549,96 @@ gtk_widget_show(combo); } +static void stereo_mode_menu_cb(GtkWidget *combo, gpointer data) +{ + gint *option = data; + + switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo))) + { + case 0: + default: + *option = PR_STEREO_NONE; + break; + case 1: + *option = PR_STEREO_ANAGLYPH_RC; + break; + case 2: + *option = PR_STEREO_ANAGLYPH_GRAY; + break; + case 3: + *option = PR_STEREO_ANAGLYPH_DB; + break; + case 4: + *option = PR_STEREO_HORIZ; + break; + case 5: + *option = PR_STEREO_HORIZ | PR_STEREO_HALF; + break; + case 6: + *option = PR_STEREO_VERT; + break; + case 7: + *option = PR_STEREO_VERT | PR_STEREO_HALF; + break; + case 8: + *option = PR_STEREO_FIXED; + break; + } +} + +static void add_stereo_mode_menu(GtkWidget *table, gint column, gint row, const gchar *text, + guint option, guint *option_c, gboolean add_fixed) +{ + GtkWidget *combo; + gint current = 0; + + *option_c = option; + + pref_table_label(table, column, row, text, 0.0); + + combo = gtk_combo_box_new_text(); + + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Single image")); + + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Anaglyph Red-Cyan")); + if (option & PR_STEREO_ANAGLYPH_RC) current = 1; + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Anaglyph Gray Red-Cyan")); + if (option & PR_STEREO_ANAGLYPH_GRAY) current = 2; + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Anaglyph Dubois")); + if (option & PR_STEREO_ANAGLYPH_DB) current = 3; + + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Side by Side")); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Side by Side Half size")); + if (option & PR_STEREO_HORIZ) + { + current = 4; + if (option & PR_STEREO_HALF) current = 5; + } + + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Top - Bottom")); + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Top - Bottom Half size")); + if (option & PR_STEREO_VERT) + { + current = 6; + if (option & PR_STEREO_HALF) current = 7; + } + + if (add_fixed) + { + gtk_combo_box_append_text(GTK_COMBO_BOX(combo), _("Fixed position")); + if (option & PR_STEREO_FIXED) current = 8; + } + + gtk_combo_box_set_active(GTK_COMBO_BOX(combo), current); + + g_signal_connect(G_OBJECT(combo), "changed", + G_CALLBACK(stereo_mode_menu_cb), option_c); + + gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1, + GTK_EXPAND | GTK_FILL, 0, 0, 0); + gtk_widget_show(combo); +} + static void filter_store_populate(void) { GList *work; @@ -1964,6 +2075,83 @@ #endif } +/* stereo tab */ +static void config_tab_stereo(GtkWidget *notebook) +{ + GtkWidget *vbox; + GtkWidget *group; + GtkWidget *group2; + GtkWidget *table; + GtkWidget *box; + GtkWidget *box2; + GtkWidget *fs_button; + vbox = scrolled_notebook_page(notebook, _("Stereo")); + + group = pref_group_new(vbox, FALSE, _("Windowed stereo mode"), GTK_ORIENTATION_VERTICAL); + + table = pref_table_new(group, 2, 1, FALSE, FALSE); + add_stereo_mode_menu(table, 0, 0, _("Windowed stereo mode"), options->stereo.mode, &c_options->stereo.mode, FALSE); + + table = pref_table_new(group, 2, 2, TRUE, FALSE); + box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, NULL); + pref_checkbox_new_int(box, _("Mirror left image"), + options->stereo.mode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.mirror_left); + box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, NULL); + pref_checkbox_new_int(box, _("Flip left image"), + options->stereo.mode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.flip_left); + box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, NULL); + pref_checkbox_new_int(box, _("Mirror right image"), + options->stereo.mode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.mirror_right); + box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, NULL); + pref_checkbox_new_int(box, _("Flip right image"), + options->stereo.mode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.flip_right); + pref_checkbox_new_int(group, _("Swap left and right images"), + options->stereo.mode & PR_STEREO_SWAP, &c_options->stereo.tmp.swap); + pref_checkbox_new_int(group, _("Disable stereo mode on single image source"), + options->stereo.mode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.temp_disable); + + group = pref_group_new(vbox, FALSE, _("Fullscreen stereo mode"), GTK_ORIENTATION_VERTICAL); + fs_button = pref_checkbox_new_int(group, _("Use different settings for fullscreen"), + options->stereo.enable_fsmode, &c_options->stereo.enable_fsmode); + box2 = pref_box_new(group, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE); + pref_checkbox_link_sensitivity(fs_button, box2); + table = pref_table_new(box2, 2, 1, FALSE, FALSE); + add_stereo_mode_menu(table, 0, 0, _("Fullscreen stereo mode"), options->stereo.fsmode, &c_options->stereo.fsmode, TRUE); + table = pref_table_new(box2, 2, 2, TRUE, FALSE); + box = pref_table_box(table, 0, 0, GTK_ORIENTATION_HORIZONTAL, NULL); + pref_checkbox_new_int(box, _("Mirror left image"), + options->stereo.fsmode & PR_STEREO_MIRROR_LEFT, &c_options->stereo.tmp.fs_mirror_left); + box = pref_table_box(table, 1, 0, GTK_ORIENTATION_HORIZONTAL, NULL); + pref_checkbox_new_int(box, _("Flip left image"), + options->stereo.fsmode & PR_STEREO_FLIP_LEFT, &c_options->stereo.tmp.fs_flip_left); + box = pref_table_box(table, 0, 1, GTK_ORIENTATION_HORIZONTAL, NULL); + pref_checkbox_new_int(box, _("Mirror right image"), + options->stereo.fsmode & PR_STEREO_MIRROR_RIGHT, &c_options->stereo.tmp.fs_mirror_right); + box = pref_table_box(table, 1, 1, GTK_ORIENTATION_HORIZONTAL, NULL); + pref_checkbox_new_int(box, _("Flip right image"), + options->stereo.fsmode & PR_STEREO_FLIP_RIGHT, &c_options->stereo.tmp.fs_flip_right); + pref_checkbox_new_int(box2, _("Swap left and right images"), + options->stereo.fsmode & PR_STEREO_SWAP, &c_options->stereo.tmp.fs_swap); + pref_checkbox_new_int(box2, _("Disable stereo mode on single image source"), + options->stereo.fsmode & PR_STEREO_TEMP_DISABLE, &c_options->stereo.tmp.fs_temp_disable); + + group2 = pref_group_new(box2, FALSE, _("Fixed position"), GTK_ORIENTATION_VERTICAL); + table = pref_table_new(group2, 5, 3, FALSE, FALSE); + pref_table_spin_new_int(table, 0, 0, _("Width"), NULL, + 1, 5000, 1, options->stereo.fixed_w, &c_options->stereo.fixed_w); + pref_table_spin_new_int(table, 3, 0, _("Height"), NULL, + 1, 5000, 1, options->stereo.fixed_h, &c_options->stereo.fixed_h); + pref_table_spin_new_int(table, 0, 1, _("Left X"), NULL, + 0, 5000, 1, options->stereo.fixed_x1, &c_options->stereo.fixed_x1); + pref_table_spin_new_int(table, 3, 1, _("Left Y"), NULL, + 0, 5000, 1, options->stereo.fixed_y1, &c_options->stereo.fixed_y1); + pref_table_spin_new_int(table, 0, 2, _("Right X"), NULL, + 0, 5000, 1, options->stereo.fixed_x2, &c_options->stereo.fixed_x2); + pref_table_spin_new_int(table, 3, 2, _("Right Y"), NULL, + 0, 5000, 1, options->stereo.fixed_y2, &c_options->stereo.fixed_y2); + +} + /* Main preferences window */ static void config_window_create(void) { @@ -2036,6 +2224,7 @@ config_tab_files(notebook); config_tab_metadata(notebook); config_tab_color(notebook); + config_tab_stereo(notebook); config_tab_behavior(notebook); gtk_widget_show(notebook);
--- a/src/rcfile.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/rcfile.c Sat Oct 29 23:41:36 2011 +0100 @@ -442,6 +442,15 @@ WRITE_NL(); WRITE_BOOL(*options, metadata.keywords_case_sensitive); WRITE_NL(); WRITE_BOOL(*options, metadata.write_orientation); + WRITE_NL(); WRITE_UINT(*options, stereo.mode); + WRITE_NL(); WRITE_UINT(*options, stereo.fsmode); + WRITE_NL(); WRITE_BOOL(*options, stereo.enable_fsmode); + WRITE_NL(); WRITE_UINT(*options, stereo.fixed_w); + WRITE_NL(); WRITE_UINT(*options, stereo.fixed_h); + WRITE_NL(); WRITE_UINT(*options, stereo.fixed_x1); + WRITE_NL(); WRITE_UINT(*options, stereo.fixed_y1); + WRITE_NL(); WRITE_UINT(*options, stereo.fixed_x2); + WRITE_NL(); WRITE_UINT(*options, stereo.fixed_y2); } static void write_color_profile(GString *outstr, gint indent) @@ -703,6 +712,16 @@ if (READ_BOOL(*options, metadata.keywords_case_sensitive)) continue; if (READ_BOOL(*options, metadata.write_orientation)) continue; + if (READ_UINT(*options, stereo.mode)) continue; + if (READ_UINT(*options, stereo.fsmode)) continue; + if (READ_BOOL(*options, stereo.enable_fsmode)) continue; + if (READ_UINT(*options, stereo.fixed_w)) continue; + if (READ_UINT(*options, stereo.fixed_h)) continue; + if (READ_UINT(*options, stereo.fixed_x1)) continue; + if (READ_UINT(*options, stereo.fixed_y1)) continue; + if (READ_UINT(*options, stereo.fixed_x2)) continue; + if (READ_UINT(*options, stereo.fixed_y2)) continue; + log_printf("unknown attribute %s = %s\n", option, value); }
--- a/src/remote.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/remote.c Sat Oct 29 23:41:36 2011 +0100 @@ -411,7 +411,7 @@ static void gr_slideshow_start_rec(const gchar *text, GIOChannel *channel, gpointer data) { GList *list; - FileData *dir_fd = file_data_new_simple(text); + FileData *dir_fd = file_data_new_dir(text); list = filelist_recursive(dir_fd); file_data_unref(dir_fd); if (!list) return; @@ -532,7 +532,7 @@ static void gr_get_sidecars(const gchar *text, GIOChannel *channel, gpointer data) { gchar *filename = expand_tilde(text); - FileData *fd = file_data_new_simple(filename); + FileData *fd = file_data_new_group(filename); GList *work; if (fd->parent) fd = fd->parent; @@ -555,7 +555,7 @@ static void gr_get_destination(const gchar *text, GIOChannel *channel, gpointer data) { gchar *filename = expand_tilde(text); - FileData *fd = file_data_new_simple(filename); + FileData *fd = file_data_new_group(filename); if (fd->change && fd->change->dest) { @@ -569,7 +569,7 @@ { gchar *filename = expand_tilde(text); - view_window_new(file_data_new_simple(filename)); + view_window_new(file_data_new_group(filename)); g_free(filename); } @@ -607,7 +607,7 @@ new = (!collection_get_first(remote_data->command_collection)); } - if (collection_add(remote_data->command_collection, file_data_new_simple(text), FALSE) && new) + if (collection_add(remote_data->command_collection, file_data_new_group(text), FALSE) && new) { layout_image_set_collection(NULL, remote_data->command_collection, collection_get_first(remote_data->command_collection)); @@ -736,6 +736,7 @@ } i++; } + printf_term(N_("\n All other command line parameters are used as plain files if they exists.\n")); } GList *remote_build_list(GList *list, gint argc, gchar *argv[], GList **errors) @@ -752,7 +753,7 @@ { list = g_list_append(list, argv[i]); } - else if (errors) + else if (errors && !isfile(argv[i])) { *errors = g_list_append(*errors, argv[i]); } @@ -762,6 +763,13 @@ return list; } +/** + * \param arg_exec Binary (argv0) + * \param remote_list Evaluated and recognized remote commands + * \param path The current path + * \param cmd_list List of all non collections in Path + * \param collection_list List of all collections in argv + */ void remote_control(const gchar *arg_exec, GList *remote_list, const gchar *path, GList *cmd_list, GList *collection_list) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/renderer-tiles.c Sat Oct 29 23:41:36 2011 +0100 @@ -0,0 +1,2157 @@ +/* + * Geeqie + * (C) 2006 John Ellis + * Copyright (C) 2008 - 2010 The Geeqie Team + * + * Author: John Ellis + * Author: Vladimir Nadvornik + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "main.h" +#include "pixbuf-renderer.h" +#include "renderer-tiles.h" + +#include "intl.h" +#include "layout.h" + +#include <gtk/gtk.h> + + +/* comment this out if not using this from within Geeqie + * defining GQ_BUILD does these things: + * - Sets the shift-click scroller pixbuf to a nice icon instead of a black box + */ +#define GQ_BUILD 1 + +#ifdef GQ_BUILD +#include "main.h" +#include "pixbuf_util.h" +#include "exif.h" +#else +typedef enum { + EXIF_ORIENTATION_UNKNOWN = 0, + EXIF_ORIENTATION_TOP_LEFT = 1, + EXIF_ORIENTATION_TOP_RIGHT = 2, + EXIF_ORIENTATION_BOTTOM_RIGHT = 3, + EXIF_ORIENTATION_BOTTOM_LEFT = 4, + EXIF_ORIENTATION_LEFT_TOP = 5, + EXIF_ORIENTATION_RIGHT_TOP = 6, + EXIF_ORIENTATION_RIGHT_BOTTOM = 7, + EXIF_ORIENTATION_LEFT_BOTTOM = 8 +} ExifOrientationType; +#endif + + +/* size to use when breaking up image pane for rendering */ +#define PR_TILE_SIZE 128 + +typedef struct _ImageTile ImageTile; +typedef struct _QueueData QueueData; + +struct _ImageTile +{ + GdkPixmap *pixmap; /* off screen buffer */ + GdkPixbuf *pixbuf; /* pixbuf area for zooming */ + gint x; /* x offset into image */ + gint y; /* y offset into image */ + gint w; /* width that is visible (may be less if at edge of image) */ + gint h; /* height '' */ + + gboolean blank; + +/* render_todo: (explanation) + NONE do nothing + AREA render area of tile, usually only used when loading an image + note: will jump to an ALL if render_done is not ALL. + ALL render entire tile, if never done before w/ ALL, for expose events *only* +*/ + + ImageRenderType render_todo; /* what to do (see above) */ + ImageRenderType render_done; /* highest that has been done before on tile */ + + QueueData *qd; + QueueData *qd2; + + guint size; /* est. memory used by pixmap and pixbuf */ +}; + +struct _QueueData +{ + ImageTile *it; + gint x; + gint y; + gint w; + gint h; + gboolean new_data; +}; + +typedef struct _OverlayData OverlayData; +struct _OverlayData +{ + gint id; + + GdkPixbuf *pixbuf; + GdkWindow *window; + + gint x; + gint y; + + OverlayRendererFlags flags; +}; + +typedef struct _RendererTiles RendererTiles; + +struct _RendererTiles +{ + RendererFuncs f; + PixbufRenderer *pr; + + gint tile_cache_max; /* max mb to use for offscreen buffer */ + + gint tile_width; + gint tile_height; + gint tile_cols; /* count of tile columns */ + GList *tiles; /* list of buffer tiles */ + gint tile_cache_size; /* allocated size of pixmaps/pixbufs */ + GList *draw_queue; /* list of areas to redraw */ + GList *draw_queue_2pass;/* list when 2 pass is enabled */ + + GList *overlay_list; + GdkPixmap *overlay_buffer; + + guint draw_idle_id; /* event source id */ + + GdkPixbuf *spare_tile; + + gint stereo_mode; + gint stereo_off_x; + gint stereo_off_y; + + gint x_scroll; /* allow local adjustment and mirroring */ + gint y_scroll; + +}; + + + +static void rt_border_draw(RendererTiles *rt, gint x, gint y, gint w, gint h); +static void rt_overlay_draw(RendererTiles *rt, gint x, gint y, gint w, gint h, ImageTile *it); + + +static void rt_tile_free_all(RendererTiles *rt); +static void rt_tile_invalidate_region(RendererTiles *rt, gint x, gint y, gint w, gint h); +static gboolean rt_tile_is_visible(RendererTiles *rt, ImageTile *it); +static void rt_queue_clear(RendererTiles *rt); +static void rt_queue_merge(QueueData *parent, QueueData *qd); +static void rt_queue(RendererTiles *rt, gint x, gint y, gint w, gint h, + gint clamp, ImageRenderType render, gboolean new_data, gboolean only_existing); + +static void rt_hierarchy_changed_cb(GtkWidget *widget, GtkWidget *previous_toplevel, gpointer data); +static gint rt_queue_draw_idle_cb(gpointer data); +static void renderer_redraw(void *renderer, gint x, gint y, gint w, gint h, + gint clamp, ImageRenderType render, gboolean new_data, gboolean only_existing); + +#define GET_RIGHT_PIXBUF_OFFSET(rt) \ + (( (rt->stereo_mode & PR_STEREO_RIGHT) && !(rt->stereo_mode & PR_STEREO_SWAP)) || \ + (!(rt->stereo_mode & PR_STEREO_RIGHT) && (rt->stereo_mode & PR_STEREO_SWAP)) ? \ + rt->pr->stereo_pixbuf_offset_right : rt->pr->stereo_pixbuf_offset_left ) + +#define GET_LEFT_PIXBUF_OFFSET(rt) \ + ((!(rt->stereo_mode & PR_STEREO_RIGHT) && !(rt->stereo_mode & PR_STEREO_SWAP)) || \ + ( (rt->stereo_mode & PR_STEREO_RIGHT) && (rt->stereo_mode & PR_STEREO_SWAP)) ? \ + rt->pr->stereo_pixbuf_offset_right : rt->pr->stereo_pixbuf_offset_left ) + + +static void rt_sync_scroll(RendererTiles *rt) +{ + PixbufRenderer *pr = rt->pr; + + rt->x_scroll = (rt->stereo_mode & PR_STEREO_MIRROR) ? + pr->width - pr->vis_width - pr->x_scroll + : pr->x_scroll; + + rt->y_scroll = (rt->stereo_mode & PR_STEREO_FLIP) ? + pr->height - pr->vis_height - pr->y_scroll + : pr->y_scroll; +} + +/* + *------------------------------------------------------------------- + * borders + *------------------------------------------------------------------- + */ + +static void rt_border_draw(RendererTiles *rt, gint x, gint y, gint w, gint h) +{ + PixbufRenderer *pr = rt->pr; + GtkWidget *box; + gint rx, ry, rw, rh; + + box = GTK_WIDGET(pr); + + if (!box->window) return; + + if (!pr->pixbuf && !pr->source_tiles_enabled) + { + if (pr_clip_region(x, y, w, h, + 0, 0, + pr->viewport_width, pr->viewport_height, + &rx, &ry, &rw, &rh)) + { + gdk_window_clear_area(box->window, rx + rt->stereo_off_x, ry + rt->stereo_off_y, rw, rh); + rt_overlay_draw(rt, rx, ry, rw, rh, NULL); + } + return; + } + + if (pr->vis_width < pr->viewport_width) + { + if (pr->x_offset > 0 && + pr_clip_region(x, y, w, h, + 0, 0, + pr->x_offset, pr->viewport_height, + &rx, &ry, &rw, &rh)) + { + gdk_window_clear_area(box->window, rx + rt->stereo_off_x, ry + rt->stereo_off_y, rw, rh); + rt_overlay_draw(rt, rx, ry, rw, rh, NULL); + } + if (pr->viewport_width - pr->vis_width - pr->x_offset > 0 && + pr_clip_region(x, y, w, h, + pr->x_offset + pr->vis_width, 0, + pr->viewport_width - pr->vis_width - pr->x_offset, pr->viewport_height, + &rx, &ry, &rw, &rh)) + { + gdk_window_clear_area(box->window, rx + rt->stereo_off_x, ry + rt->stereo_off_y, rw, rh); + rt_overlay_draw(rt, rx, ry, rw, rh, NULL); + } + } + if (pr->vis_height < pr->viewport_height) + { + if (pr->y_offset > 0 && + pr_clip_region(x, y, w, h, + pr->x_offset, 0, + pr->vis_width, pr->y_offset, + &rx, &ry, &rw, &rh)) + { + gdk_window_clear_area(box->window, rx + rt->stereo_off_x, ry + rt->stereo_off_y, rw, rh); + rt_overlay_draw(rt, rx, ry, rw, rh, NULL); + } + if (pr->viewport_height - pr->vis_height - pr->y_offset > 0 && + pr_clip_region(x, y, w, h, + pr->x_offset, pr->y_offset + pr->vis_height, + pr->vis_width, pr->viewport_height - pr->vis_height - pr->y_offset, + &rx, &ry, &rw, &rh)) + { + gdk_window_clear_area(box->window, rx + rt->stereo_off_x, ry + rt->stereo_off_y, rw, rh); + rt_overlay_draw(rt, rx, ry, rw, rh, NULL); + } + } +} + +static void rt_border_clear(RendererTiles *rt) +{ + PixbufRenderer *pr = rt->pr; + rt_border_draw(rt, 0, 0, pr->viewport_width, pr->viewport_height); +} + + +/* + *------------------------------------------------------------------- + * display tiles + *------------------------------------------------------------------- + */ + +static ImageTile *rt_tile_new(gint x, gint y, gint width, gint height) +{ + ImageTile *it; + + it = g_new0(ImageTile, 1); + + it->x = x; + it->y = y; + it->w = width; + it->h = height; + + it->render_done = TILE_RENDER_NONE; + + return it; +} + +static void rt_tile_free(ImageTile *it) +{ + if (!it) return; + + if (it->pixbuf) g_object_unref(it->pixbuf); + if (it->pixmap) g_object_unref(it->pixmap); + + g_free(it); +} + +static void rt_tile_free_all(RendererTiles *rt) +{ + GList *work; + + work = rt->tiles; + while (work) + { + ImageTile *it; + + it = work->data; + work = work->next; + + rt_tile_free(it); + } + + g_list_free(rt->tiles); + rt->tiles = NULL; + rt->tile_cache_size = 0; +} + +static ImageTile *rt_tile_add(RendererTiles *rt, gint x, gint y) +{ + PixbufRenderer *pr = rt->pr; + ImageTile *it; + + it = rt_tile_new(x, y, rt->tile_width, rt->tile_height); + + if (it->x + it->w > pr->width) it->w = pr->width - it->x; + if (it->y + it->h > pr->height) it->h = pr->height - it->y; + + rt->tiles = g_list_prepend(rt->tiles, it); + rt->tile_cache_size += it->size; + + return it; +} + +static void rt_tile_remove(RendererTiles *rt, ImageTile *it) +{ + if (it->qd) + { + QueueData *qd = it->qd; + + it->qd = NULL; + rt->draw_queue = g_list_remove(rt->draw_queue, qd); + g_free(qd); + } + + if (it->qd2) + { + QueueData *qd = it->qd2; + + it->qd2 = NULL; + rt->draw_queue_2pass = g_list_remove(rt->draw_queue_2pass, qd); + g_free(qd); + } + + rt->tiles = g_list_remove(rt->tiles, it); + rt->tile_cache_size -= it->size; + + rt_tile_free(it); +} + +static void rt_tile_free_space(RendererTiles *rt, guint space, ImageTile *it) +{ + PixbufRenderer *pr = rt->pr; + GList *work; + guint tile_max; + + work = g_list_last(rt->tiles); + + if (pr->source_tiles_enabled && pr->scale < 1.0) + { + gint tiles; + + tiles = (pr->vis_width / rt->tile_width + 1) * (pr->vis_height / rt->tile_height + 1); + tile_max = MAX(tiles * rt->tile_width * rt->tile_height * 3, + (gint)((gdouble)rt->tile_cache_max * 1048576.0 * pr->scale)); + } + else + { + tile_max = rt->tile_cache_max * 1048576; + } + + while (work && rt->tile_cache_size + space > tile_max) + { + ImageTile *needle; + + needle = work->data; + work = work->prev; + if (needle != it && + ((!needle->qd && !needle->qd2) || !rt_tile_is_visible(rt, needle))) rt_tile_remove(rt, needle); + } +} + +static void rt_tile_invalidate_all(RendererTiles *rt) +{ + PixbufRenderer *pr = rt->pr; + GList *work; + + work = rt->tiles; + while (work) + { + ImageTile *it; + + it = work->data; + work = work->next; + + it->render_done = TILE_RENDER_NONE; + it->render_todo = TILE_RENDER_ALL; + it->blank = FALSE; + + it->w = MIN(rt->tile_width, pr->width - it->x); + it->h = MIN(rt->tile_height, pr->height - it->y); + } +} + +static void rt_tile_invalidate_region(RendererTiles *rt, gint x, gint y, gint w, gint h) +{ + gint x1, x2; + gint y1, y2; + GList *work; + + x1 = ROUND_DOWN(x, rt->tile_width); + x2 = ROUND_UP(x + w, rt->tile_width); + + y1 = ROUND_DOWN(y, rt->tile_height); + y2 = ROUND_UP(y + h, rt->tile_height); + + work = rt->tiles; + while (work) + { + ImageTile *it; + + it = work->data; + work = work->next; + + if (it->x < x2 && it->x + it->w > x1 && + it->y < y2 && it->y + it->h > y1) + { + it->render_done = TILE_RENDER_NONE; + it->render_todo = TILE_RENDER_ALL; + } + } +} + +static ImageTile *rt_tile_get(RendererTiles *rt, gint x, gint y, gboolean only_existing) +{ + GList *work; + + work = rt->tiles; + while (work) + { + ImageTile *it; + + it = work->data; + if (it->x == x && it->y == y) + { + rt->tiles = g_list_delete_link(rt->tiles, work); + rt->tiles = g_list_prepend(rt->tiles, it); + return it; + } + + work = work->next; + } + + if (only_existing) return NULL; + + return rt_tile_add(rt, x, y); +} + +static gint pixmap_calc_size(GdkPixmap *pixmap) +{ + gint w, h, d; + + d = gdk_drawable_get_depth(pixmap); + gdk_drawable_get_size(pixmap, &w, &h); + return w * h * (d / 8); +} + +static void rt_tile_prepare(RendererTiles *rt, ImageTile *it) +{ + PixbufRenderer *pr = rt->pr; + if (!it->pixmap) + { + GdkPixmap *pixmap; + guint size; + + pixmap = gdk_pixmap_new(((GtkWidget *)pr)->window, rt->tile_width, rt->tile_height, -1); + + size = pixmap_calc_size(pixmap); + rt_tile_free_space(rt, size, it); + + it->pixmap = pixmap; + it->size += size; + rt->tile_cache_size += size; + } + + if (!it->pixbuf) + { + GdkPixbuf *pixbuf; + guint size; + pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, rt->tile_width, rt->tile_height); + + size = gdk_pixbuf_get_rowstride(pixbuf) * rt->tile_height; + rt_tile_free_space(rt, size, it); + + it->pixbuf = pixbuf; + it->size += size; + rt->tile_cache_size += size; + } +} + +/* + *------------------------------------------------------------------- + * overlays + *------------------------------------------------------------------- + */ + +static void rt_overlay_get_position(RendererTiles *rt, OverlayData *od, + gint *x, gint *y, gint *w, gint *h) +{ + PixbufRenderer *pr = rt->pr; + gint px, py, pw, ph; + + pw = gdk_pixbuf_get_width(od->pixbuf); + ph = gdk_pixbuf_get_height(od->pixbuf); + px = od->x; + py = od->y; + + if (od->flags & OVL_RELATIVE) + { + if (px < 0) px = pr->viewport_width - pw + px; + if (py < 0) py = pr->viewport_height - ph + py; + } + + if (x) *x = px; + if (y) *y = py; + if (w) *w = pw; + if (h) *h = ph; +} + +static void rt_overlay_init_window(RendererTiles *rt, OverlayData *od) +{ + PixbufRenderer *pr = rt->pr; + gint px, py, pw, ph; + GdkWindowAttr attributes; + gint attributes_mask; + + rt_overlay_get_position(rt, od, &px, &py, &pw, &ph); + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.width = pw; + attributes.height = ph; + attributes.event_mask = GDK_EXPOSURE_MASK; + attributes_mask = 0; + + od->window = gdk_window_new(GTK_WIDGET(pr)->window, &attributes, attributes_mask); + gdk_window_set_user_data(od->window, pr); + gdk_window_move(od->window, px + rt->stereo_off_x, py + rt->stereo_off_y); + gdk_window_show(od->window); +} + +static void rt_overlay_draw(RendererTiles *rt, gint x, gint y, gint w, gint h, + ImageTile *it) +{ + PixbufRenderer *pr = rt->pr; + GtkWidget *box; + GList *work; + + box = GTK_WIDGET(pr); + + work = rt->overlay_list; + while (work) + { + OverlayData *od; + gint px, py, pw, ph; + gint rx, ry, rw, rh; + + od = work->data; + work = work->next; + + if (!od->window) rt_overlay_init_window(rt, od); + + rt_overlay_get_position(rt, od, &px, &py, &pw, &ph); + if (pr_clip_region(x, y, w, h, px, py, pw, ph, &rx, &ry, &rw, &rh)) + { + if (!rt->overlay_buffer) + { + rt->overlay_buffer = gdk_pixmap_new(((GtkWidget *)pr)->window, rt->tile_width, rt->tile_height, -1); + } + + if (it) + { +#if GTK_CHECK_VERSION(2,20,0) + gdk_draw_drawable(rt->overlay_buffer, box->style->fg_gc[gtk_widget_get_state(box)], +#else + gdk_draw_drawable(rt->overlay_buffer, box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + it->pixmap, + rx - (pr->x_offset + (it->x - rt->x_scroll)), + ry - (pr->y_offset + (it->y - rt->y_scroll)), + 0, 0, rw, rh); + gdk_draw_pixbuf(rt->overlay_buffer, +#if GTK_CHECK_VERSION(2,20,0) + box->style->fg_gc[gtk_widget_get_state(box)], +#else + box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + od->pixbuf, + rx - px, ry - py, + 0, 0, rw, rh, + pr->dither_quality, rx, ry); +#if GTK_CHECK_VERSION(2,20,0) + gdk_draw_drawable(od->window, box->style->fg_gc[gtk_widget_get_state(box)], +#else + gdk_draw_drawable(od->window, box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + rt->overlay_buffer, + 0, 0, + rx - px, ry - py, rw, rh); + } + else + { + /* no ImageTile means region may be larger than our scratch buffer */ + gint sx, sy; + + for (sx = rx; sx < rx + rw; sx += rt->tile_width) + for (sy = ry; sy < ry + rh; sy += rt->tile_height) + { + gint sw, sh; + + sw = MIN(rx + rw - sx, rt->tile_width); + sh = MIN(ry + rh - sy, rt->tile_height); + + gdk_draw_rectangle(rt->overlay_buffer, +#if GTK_CHECK_VERSION(2,20,0) + box->style->bg_gc[gtk_widget_get_state(box)], TRUE, +#else + box->style->bg_gc[GTK_WIDGET_STATE(box)], TRUE, +#endif + 0, 0, sw, sh); + gdk_draw_pixbuf(rt->overlay_buffer, +#if GTK_CHECK_VERSION(2,20,0) + box->style->fg_gc[gtk_widget_get_state(box)], +#else + box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + od->pixbuf, + sx - px, sy - py, + 0, 0, sw, sh, + pr->dither_quality, sx, sy); +#if GTK_CHECK_VERSION(2,20,0) + gdk_draw_drawable(od->window, box->style->fg_gc[gtk_widget_get_state(box)], +#else + gdk_draw_drawable(od->window, box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + rt->overlay_buffer, + 0, 0, + sx - px, sy - py, sw, sh); + } + } + } + } +} + +static void rt_overlay_queue_draw(RendererTiles *rt, OverlayData *od, gint x1, gint y1, gint x2, gint y2) +{ + PixbufRenderer *pr = rt->pr; + gint x, y, w, h; + + rt_overlay_get_position(rt, od, &x, &y, &w, &h); + + /* add borders */ + x -= x1; + y -= y1; + w += x1 + x2; + h += y1 + y2; + + rt_queue(rt, rt->x_scroll - pr->x_offset + x, + rt->y_scroll - pr->y_offset + y, + w, h, + FALSE, TILE_RENDER_ALL, FALSE, FALSE); + + rt_border_draw(rt, x, y, w, h); +} + +static void rt_overlay_queue_all(RendererTiles *rt, gint x1, gint y1, gint x2, gint y2) +{ + GList *work; + + work = rt->overlay_list; + while (work) + { + OverlayData *od = work->data; + work = work->next; + + rt_overlay_queue_draw(rt, od, x1, y1, x2, y2); + } +} + +static void rt_overlay_update_sizes(RendererTiles *rt) +{ + GList *work; + + work = rt->overlay_list; + while (work) + { + OverlayData *od = work->data; + work = work->next; + + if (!od->window) rt_overlay_init_window(rt, od); + + if (od->flags & OVL_RELATIVE) + { + gint x, y, w, h; + + rt_overlay_get_position(rt, od, &x, &y, &w, &h); + gdk_window_move_resize(od->window, x + rt->stereo_off_x, y + rt->stereo_off_y, w, h); + } + } +} + +static OverlayData *rt_overlay_find(RendererTiles *rt, gint id) +{ + GList *work; + + work = rt->overlay_list; + while (work) + { + OverlayData *od = work->data; + work = work->next; + + if (od->id == id) return od; + } + + return NULL; +} + + +gint renderer_tiles_overlay_add(RendererTiles *rt, GdkPixbuf *pixbuf, gint x, gint y, + OverlayRendererFlags flags) +{ + PixbufRenderer *pr = rt->pr; + OverlayData *od; + gint id; + + g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), -1); + g_return_val_if_fail(pixbuf != NULL, -1); + + id = 1; + while (rt_overlay_find(rt, id)) id++; + + od = g_new0(OverlayData, 1); + od->id = id; + od->pixbuf = pixbuf; + g_object_ref(G_OBJECT(od->pixbuf)); + od->x = x; + od->y = y; + od->flags = flags; + + rt_overlay_init_window(rt, od); + + rt->overlay_list = g_list_append(rt->overlay_list, od); + + rt_overlay_queue_draw(rt, od, 0, 0, 0, 0); + + return od->id; +} + +static void rt_overlay_free(RendererTiles *rt, OverlayData *od) +{ + rt->overlay_list = g_list_remove(rt->overlay_list, od); + + if (od->pixbuf) g_object_unref(G_OBJECT(od->pixbuf)); + if (od->window) gdk_window_destroy(od->window); + g_free(od); + + if (!rt->overlay_list && rt->overlay_buffer) + { + g_object_unref(rt->overlay_buffer); + rt->overlay_buffer = NULL; + } +} + +static void rt_overlay_list_clear(RendererTiles *rt) +{ + while (rt->overlay_list) + { + OverlayData *od; + + od = rt->overlay_list->data; + rt_overlay_free(rt, od); + } +} + +static void rt_overlay_list_reset_window(RendererTiles *rt) +{ + GList *work; + + if (rt->overlay_buffer) g_object_unref(rt->overlay_buffer); + rt->overlay_buffer = NULL; + + work = rt->overlay_list; + while (work) + { + OverlayData *od = work->data; + work = work->next; + if (od->window) gdk_window_destroy(od->window); + od->window = NULL; + } +} + +void renderer_tiles_overlay_set(RendererTiles *rt, gint id, GdkPixbuf *pixbuf, gint x, gint y) +{ + PixbufRenderer *pr = rt->pr; + OverlayData *od; + + g_return_if_fail(IS_PIXBUF_RENDERER(pr)); + + od = rt_overlay_find(rt, id); + if (!od) return; + + if (pixbuf) + { + gint px, py, pw, ph; + + g_object_ref(G_OBJECT(pixbuf)); + g_object_unref(G_OBJECT(od->pixbuf)); + od->pixbuf = pixbuf; + + od->x = x; + od->y = y; + + if (!od->window) rt_overlay_init_window(rt, od); + + rt_overlay_queue_draw(rt, od, 0, 0, 0, 0); + rt_overlay_get_position(rt, od, &px, &py, &pw, &ph); + gdk_window_move_resize(od->window, px + rt->stereo_off_x, py + rt->stereo_off_y, pw, ph); + } + else + { + rt_overlay_queue_draw(rt, od, 0, 0, 0, 0); + rt_overlay_free(rt, od); + } +} + +gboolean renderer_tiles_overlay_get(RendererTiles *rt, gint id, GdkPixbuf **pixbuf, gint *x, gint *y) +{ + PixbufRenderer *pr = rt->pr; + OverlayData *od; + + g_return_val_if_fail(IS_PIXBUF_RENDERER(pr), FALSE); + + od = rt_overlay_find(rt, id); + if (!od) return FALSE; + + if (pixbuf) *pixbuf = od->pixbuf; + if (x) *x = od->x; + if (y) *y = od->y; + + return TRUE; +} + +static void rt_hierarchy_changed_cb(GtkWidget *widget, GtkWidget *previous_toplevel, gpointer data) +{ + RendererTiles *rt = data; + rt_overlay_list_reset_window(rt); +} + +/* + *------------------------------------------------------------------- + * drawing + *------------------------------------------------------------------- + */ + +static GdkPixbuf *rt_get_spare_tile(RendererTiles *rt) +{ + if (!rt->spare_tile) rt->spare_tile = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, rt->tile_width, rt->tile_height); + return rt->spare_tile; +} + +#define COLOR_BYTES 3 /* rgb */ + +static void rt_tile_rotate_90_clockwise(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h) +{ + GdkPixbuf *src = *tile; + GdkPixbuf *dest; + gint srs, drs; + guchar *s_pix, *d_pix; + guchar *sp, *dp; + guchar *ip, *spi, *dpi; + gint i, j; + gint tw = rt->tile_width; + + srs = gdk_pixbuf_get_rowstride(src); + s_pix = gdk_pixbuf_get_pixels(src); + spi = s_pix + (x * COLOR_BYTES); + + dest = rt_get_spare_tile(rt); + drs = gdk_pixbuf_get_rowstride(dest); + d_pix = gdk_pixbuf_get_pixels(dest); + dpi = d_pix + (tw - 1) * COLOR_BYTES; + + for (i = y; i < y + h; i++) + { + sp = spi + (i * srs); + ip = dpi - (i * COLOR_BYTES); + for (j = x; j < x + w; j++) + { + dp = ip + (j * drs); + memcpy(dp, sp, COLOR_BYTES); + sp += COLOR_BYTES; + } + } + + rt->spare_tile = src; + *tile = dest; +} + +static void rt_tile_rotate_90_counter_clockwise(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h) +{ + GdkPixbuf *src = *tile; + GdkPixbuf *dest; + gint srs, drs; + guchar *s_pix, *d_pix; + guchar *sp, *dp; + guchar *ip, *spi, *dpi; + gint i, j; + gint th = rt->tile_height; + + srs = gdk_pixbuf_get_rowstride(src); + s_pix = gdk_pixbuf_get_pixels(src); + spi = s_pix + (x * COLOR_BYTES); + + dest = rt_get_spare_tile(rt); + drs = gdk_pixbuf_get_rowstride(dest); + d_pix = gdk_pixbuf_get_pixels(dest); + dpi = d_pix + (th - 1) * drs; + + for (i = y; i < y + h; i++) + { + sp = spi + (i * srs); + ip = dpi + (i * COLOR_BYTES); + for (j = x; j < x + w; j++) + { + dp = ip - (j * drs); + memcpy(dp, sp, COLOR_BYTES); + sp += COLOR_BYTES; + } + } + + rt->spare_tile = src; + *tile = dest; +} + +static void rt_tile_mirror_only(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h) +{ + GdkPixbuf *src = *tile; + GdkPixbuf *dest; + gint srs, drs; + guchar *s_pix, *d_pix; + guchar *sp, *dp; + guchar *spi, *dpi; + gint i, j; + + gint tw = rt->tile_width; + + srs = gdk_pixbuf_get_rowstride(src); + s_pix = gdk_pixbuf_get_pixels(src); + spi = s_pix + (x * COLOR_BYTES); + + dest = rt_get_spare_tile(rt); + drs = gdk_pixbuf_get_rowstride(dest); + d_pix = gdk_pixbuf_get_pixels(dest); + dpi = d_pix + (tw - x - 1) * COLOR_BYTES; + + for (i = y; i < y + h; i++) + { + sp = spi + (i * srs); + dp = dpi + (i * drs); + for (j = 0; j < w; j++) + { + memcpy(dp, sp, COLOR_BYTES); + sp += COLOR_BYTES; + dp -= COLOR_BYTES; + } + } + + rt->spare_tile = src; + *tile = dest; +} + +static void rt_tile_mirror_and_flip(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h) +{ + GdkPixbuf *src = *tile; + GdkPixbuf *dest; + gint srs, drs; + guchar *s_pix, *d_pix; + guchar *sp, *dp; + guchar *spi, *dpi; + gint i, j; + gint tw = rt->tile_width; + gint th = rt->tile_height; + + srs = gdk_pixbuf_get_rowstride(src); + s_pix = gdk_pixbuf_get_pixels(src); + spi = s_pix + (x * COLOR_BYTES); + + dest = rt_get_spare_tile(rt); + drs = gdk_pixbuf_get_rowstride(dest); + d_pix = gdk_pixbuf_get_pixels(dest); + dpi = d_pix + (th - 1) * drs + (tw - 1) * COLOR_BYTES; + + for (i = y; i < y + h; i++) + { + sp = s_pix + (i * srs) + (x * COLOR_BYTES); + dp = dpi - (i * drs) - (x * COLOR_BYTES); + for (j = 0; j < w; j++) + { + memcpy(dp, sp, COLOR_BYTES); + sp += COLOR_BYTES; + dp -= COLOR_BYTES; + } + } + + rt->spare_tile = src; + *tile = dest; +} + +static void rt_tile_flip_only(RendererTiles *rt, GdkPixbuf **tile, gint x, gint y, gint w, gint h) +{ + GdkPixbuf *src = *tile; + GdkPixbuf *dest; + gint srs, drs; + guchar *s_pix, *d_pix; + guchar *sp, *dp; + guchar *spi, *dpi; + gint i; + gint th = rt->tile_height; + + srs = gdk_pixbuf_get_rowstride(src); + s_pix = gdk_pixbuf_get_pixels(src); + spi = s_pix + (x * COLOR_BYTES); + + dest = rt_get_spare_tile(rt); + drs = gdk_pixbuf_get_rowstride(dest); + d_pix = gdk_pixbuf_get_pixels(dest); + dpi = d_pix + (th - 1) * drs + (x * COLOR_BYTES); + + for (i = y; i < y + h; i++) + { + sp = spi + (i * srs); + dp = dpi - (i * drs); + memcpy(dp, sp, w * COLOR_BYTES); + } + + rt->spare_tile = src; + *tile = dest; +} + +static void rt_tile_apply_orientation(RendererTiles *rt, gint orientation, GdkPixbuf **pixbuf, gint x, gint y, gint w, gint h) +{ + switch (orientation) + { + case EXIF_ORIENTATION_TOP_LEFT: + /* normal -- nothing to do */ + break; + case EXIF_ORIENTATION_TOP_RIGHT: + /* mirrored */ + { + rt_tile_mirror_only(rt, pixbuf, x, y, w, h); + } + break; + case EXIF_ORIENTATION_BOTTOM_RIGHT: + /* upside down */ + { + rt_tile_mirror_and_flip(rt, pixbuf, x, y, w, h); + } + break; + case EXIF_ORIENTATION_BOTTOM_LEFT: + /* flipped */ + { + rt_tile_flip_only(rt, pixbuf, x, y, w, h); + } + break; + case EXIF_ORIENTATION_LEFT_TOP: + { + rt_tile_flip_only(rt, pixbuf, x, y, w, h); + rt_tile_rotate_90_clockwise(rt, pixbuf, x, rt->tile_height - y - h, w, h); + } + break; + case EXIF_ORIENTATION_RIGHT_TOP: + /* rotated -90 (270) */ + { + rt_tile_rotate_90_clockwise(rt, pixbuf, x, y, w, h); + } + break; + case EXIF_ORIENTATION_RIGHT_BOTTOM: + { + rt_tile_flip_only(rt, pixbuf, x, y, w, h); + rt_tile_rotate_90_counter_clockwise(rt, pixbuf, x, rt->tile_height - y - h, w, h); + } + break; + case EXIF_ORIENTATION_LEFT_BOTTOM: + /* rotated 90 */ + { + rt_tile_rotate_90_counter_clockwise(rt, pixbuf, x, y, w, h); + } + break; + default: + /* The other values are out of range */ + break; + } +} + +static gboolean rt_source_tile_render(RendererTiles *rt, ImageTile *it, + gint x, gint y, gint w, gint h, + gboolean new_data, gboolean fast) +{ + PixbufRenderer *pr = rt->pr; + GtkWidget *box; + GList *list; + GList *work; + gboolean draw = FALSE; + + box = GTK_WIDGET(pr); + + if (pr->zoom == 1.0 || pr->scale == 1.0) + { + list = pr_source_tile_compute_region(pr, it->x + x, it->y + y, w, h, TRUE); + work = list; + while (work) + { + SourceTile *st; + gint rx, ry, rw, rh; + + st = work->data; + work = work->next; + + if (pr_clip_region(st->x, st->y, pr->source_tile_width, pr->source_tile_height, + it->x + x, it->y + y, w, h, + &rx, &ry, &rw, &rh)) + { + if (st->blank) + { + gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE, + rx - st->x, ry - st->y, rw, rh); + } + else /* (pr->zoom == 1.0 || pr->scale == 1.0) */ + { + gdk_draw_pixbuf(it->pixmap, +#if GTK_CHECK_VERSION(2,20,0) + box->style->fg_gc[gtk_widget_get_state(box)], +#else + box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + st->pixbuf, + rx - st->x, ry - st->y, + rx - it->x, ry - it->y, + rw, rh, + pr->dither_quality, rx, ry); + } + } + } + } + else + { + gdouble scale_x, scale_y; + gint sx, sy, sw, sh; + + if (pr->image_width == 0 || pr->image_height == 0) return FALSE; + scale_x = (gdouble)pr->width / pr->image_width; + scale_y = (gdouble)pr->height / pr->image_height; + + sx = (gdouble)(it->x + x) / scale_x; + sy = (gdouble)(it->y + y) / scale_y; + sw = (gdouble)w / scale_x; + sh = (gdouble)h / scale_y; + + if (pr->width < PR_MIN_SCALE_SIZE || pr->height < PR_MIN_SCALE_SIZE) fast = TRUE; + +#if 0 + /* draws red over draw region, to check for leaks (regions not filled) */ + pixbuf_set_rect_fill(it->pixbuf, x, y, w, h, 255, 0, 0, 255); +#endif + + list = pr_source_tile_compute_region(pr, sx, sy, sw, sh, TRUE); + work = list; + while (work) + { + SourceTile *st; + gint rx, ry, rw, rh; + gint stx, sty, stw, sth; + + st = work->data; + work = work->next; + + stx = floor((gdouble)st->x * scale_x); + sty = floor((gdouble)st->y * scale_y); + stw = ceil((gdouble)(st->x + pr->source_tile_width) * scale_x) - stx; + sth = ceil((gdouble)(st->y + pr->source_tile_height) * scale_y) - sty; + + if (pr_clip_region(stx, sty, stw, sth, + it->x + x, it->y + y, w, h, + &rx, &ry, &rw, &rh)) + { + if (st->blank) + { + gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE, + rx - st->x, ry - st->y, rw, rh); + } + else + { + gdouble offset_x; + gdouble offset_y; + + /* may need to use unfloored stx,sty values here */ + offset_x = (gdouble)(stx - it->x); + offset_y = (gdouble)(sty - it->y); + + gdk_pixbuf_scale(st->pixbuf, it->pixbuf, rx - it->x, ry - it->y, rw, rh, + (gdouble) 0.0 + offset_x, + (gdouble) 0.0 + offset_y, + scale_x, scale_y, + (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality); + draw = TRUE; + } + } + } + } + + g_list_free(list); + + return draw; +} + +static void rt_tile_get_region(gboolean has_alpha, + const GdkPixbuf *src, GdkPixbuf *dest, + int pb_x, int pb_y, int pb_w, int pb_h, + double offset_x, double offset_y, double scale_x, double scale_y, + GdkInterpType interp_type, + int check_x, int check_y) +{ + if (!has_alpha) + { + if (scale_x == 1.0 && scale_y == 1.0) + { + gdk_pixbuf_copy_area(src, + -offset_x + pb_x, -offset_y + pb_y, + pb_w, pb_h, + dest, + pb_x, pb_y); + } + else + { + gdk_pixbuf_scale(src, dest, + pb_x, pb_y, pb_w, pb_h, + offset_x, + offset_y, + scale_x, scale_y, + interp_type); + } + } + else + { + gdk_pixbuf_composite_color(src, dest, + pb_x, pb_y, pb_w, pb_h, + offset_x, + offset_y, + scale_x, scale_y, + interp_type, + 255, check_x, check_y, + PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2); + } +} + + +static gint rt_get_orientation(RendererTiles *rt) +{ + PixbufRenderer *pr = rt->pr; + + gint orientation = pr->orientation; + static const gint mirror[] = {1, 2, 1, 4, 3, 6, 5, 8, 7}; + static const gint flip[] = {1, 4, 3, 2, 1, 8, 7, 6, 5}; + + if (rt->stereo_mode & PR_STEREO_MIRROR) orientation = mirror[orientation]; + if (rt->stereo_mode & PR_STEREO_FLIP) orientation = flip[orientation]; + return orientation; +} + + +static void rt_tile_render(RendererTiles *rt, ImageTile *it, + gint x, gint y, gint w, gint h, + gboolean new_data, gboolean fast) +{ + PixbufRenderer *pr = rt->pr; + GtkWidget *box; + gboolean has_alpha; + gboolean draw = FALSE; + gint orientation = rt_get_orientation(rt); + + if (it->render_todo == TILE_RENDER_NONE && it->pixmap && !new_data) return; + + if (it->render_done != TILE_RENDER_ALL) + { + x = 0; + y = 0; + w = it->w; + h = it->h; + if (!fast) it->render_done = TILE_RENDER_ALL; + } + else if (it->render_todo != TILE_RENDER_AREA) + { + if (!fast) it->render_todo = TILE_RENDER_NONE; + return; + } + + if (!fast) it->render_todo = TILE_RENDER_NONE; + + if (new_data) it->blank = FALSE; + + rt_tile_prepare(rt, it); + has_alpha = (pr->pixbuf && gdk_pixbuf_get_has_alpha(pr->pixbuf)); + + box = GTK_WIDGET(pr); + + /* FIXME checker colors for alpha should be configurable, + * also should be drawn for blank = TRUE + */ + + if (it->blank) + { + /* no data, do fast rect fill */ + gdk_draw_rectangle(it->pixmap, box->style->black_gc, TRUE, + 0, 0, it->w, it->h); + } + else if (pr->source_tiles_enabled) + { + draw = rt_source_tile_render(rt, it, x, y, w, h, new_data, fast); + } + else if ((pr->zoom == 1.0 || pr->scale == 1.0) && + pr->aspect_ratio == 1.0 && + !has_alpha && + orientation == EXIF_ORIENTATION_TOP_LEFT && + !(pr->func_post_process && !(pr->post_process_slow && fast)) && + !(rt->stereo_mode & PR_STEREO_ANAGLYPH)) + { + /* special case: faster, simple, scale 1.0, base orientation, no postprocessing */ + gdk_draw_pixbuf(it->pixmap, +#if GTK_CHECK_VERSION(2,20,0) + box->style->fg_gc[gtk_widget_get_state(box)], +#else + box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + pr->pixbuf, + it->x + x + GET_RIGHT_PIXBUF_OFFSET(rt), it->y + y, + x, y, + w, h, + pr->dither_quality, it->x + x, it->y + y); + } + else + { + gdouble scale_x, scale_y; + gdouble src_x, src_y; + gint pb_x, pb_y; + gint pb_w, pb_h; + + if (pr->image_width == 0 || pr->image_height == 0) return; + + scale_x = (gdouble)pr->width / pr->image_width; + scale_y = (gdouble)pr->height / pr->image_height; + + pr_tile_coords_map_orientation(orientation, it->x, it->y, + pr->width, pr->height, + rt->tile_width, rt->tile_height, + &src_x, &src_y); + pr_tile_region_map_orientation(orientation, x, y, + rt->tile_width, rt->tile_height, + w, h, + &pb_x, &pb_y, + &pb_w, &pb_h); + + switch (orientation) + { + gdouble tmp; + case EXIF_ORIENTATION_LEFT_TOP: + case EXIF_ORIENTATION_RIGHT_TOP: + case EXIF_ORIENTATION_RIGHT_BOTTOM: + case EXIF_ORIENTATION_LEFT_BOTTOM: + tmp = scale_x; + scale_x = scale_y; + scale_y = tmp; + break; + default: + /* nothing to do */ + break; + } + + /* HACK: The pixbuf scalers get kinda buggy(crash) with extremely + * small sizes for anything but GDK_INTERP_NEAREST + */ + if (pr->width < PR_MIN_SCALE_SIZE || pr->height < PR_MIN_SCALE_SIZE) fast = TRUE; + + rt_tile_get_region(has_alpha, + pr->pixbuf, it->pixbuf, pb_x, pb_y, pb_w, pb_h, + (gdouble) 0.0 - src_x - GET_RIGHT_PIXBUF_OFFSET(rt) * scale_x, + (gdouble) 0.0 - src_y, + scale_x, scale_y, + (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality, + it->x + pb_x, it->y + pb_y); + if (rt->stereo_mode & PR_STEREO_ANAGLYPH && + (pr->stereo_pixbuf_offset_right > 0 || pr->stereo_pixbuf_offset_left > 0)) + { + GdkPixbuf *right_pb = rt_get_spare_tile(rt); + rt_tile_get_region(has_alpha, + pr->pixbuf, right_pb, pb_x, pb_y, pb_w, pb_h, + (gdouble) 0.0 - src_x - GET_LEFT_PIXBUF_OFFSET(rt) * scale_x, + (gdouble) 0.0 - src_y, + scale_x, scale_y, + (fast) ? GDK_INTERP_NEAREST : pr->zoom_quality, + it->x + pb_x, it->y + pb_y); + pr_create_anaglyph(rt->stereo_mode, it->pixbuf, right_pb, pb_x, pb_y, pb_w, pb_h); + /* do not care about freeing spare_tile, it will be reused */ + } + rt_tile_apply_orientation(rt, orientation, &it->pixbuf, pb_x, pb_y, pb_w, pb_h); + draw = TRUE; + } + + if (draw && it->pixbuf && !it->blank) + { + + if (pr->func_post_process && !(pr->post_process_slow && fast)) + pr->func_post_process(pr, &it->pixbuf, x, y, w, h, pr->post_process_user_data); + + gdk_draw_pixbuf(it->pixmap, +#if GTK_CHECK_VERSION(2,20,0) + box->style->fg_gc[gtk_widget_get_state(box)], +#else + box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + it->pixbuf, + x, y, + x, y, + w, h, + pr->dither_quality, it->x + x, it->y + y); + } + +#if 0 + /* enable this line for debugging the edges of tiles */ + gdk_draw_rectangle(it->pixmap, box->style->white_gc, + FALSE, 0, 0, it->w, it->h); + gdk_draw_rectangle(it->pixmap, box->style->white_gc, + FALSE, x, y, w, h); +#endif +} + + +static void rt_tile_expose(RendererTiles *rt, ImageTile *it, + gint x, gint y, gint w, gint h, + gboolean new_data, gboolean fast) +{ + PixbufRenderer *pr = rt->pr; + GtkWidget *box; + + /* clamp to visible */ + if (it->x + x < rt->x_scroll) + { + w -= rt->x_scroll - it->x - x; + x = rt->x_scroll - it->x; + } + if (it->x + x + w > rt->x_scroll + pr->vis_width) + { + w = rt->x_scroll + pr->vis_width - it->x - x; + } + if (w < 1) return; + if (it->y + y < rt->y_scroll) + { + h -= rt->y_scroll - it->y - y; + y = rt->y_scroll - it->y; + } + if (it->y + y + h > rt->y_scroll + pr->vis_height) + { + h = rt->y_scroll + pr->vis_height - it->y - y; + } + if (h < 1) return; + + rt_tile_render(rt, it, x, y, w, h, new_data, fast); + + box = GTK_WIDGET(pr); + +#if GTK_CHECK_VERSION(2,20,0) + gdk_draw_drawable(box->window, box->style->fg_gc[gtk_widget_get_state(box)], +#else + gdk_draw_drawable(box->window, box->style->fg_gc[GTK_WIDGET_STATE(box)], +#endif + it->pixmap, x, y, + pr->x_offset + (it->x - rt->x_scroll) + x + rt->stereo_off_x, pr->y_offset + (it->y - rt->y_scroll) + y + rt->stereo_off_y, w, h); + + if (rt->overlay_list) + { + rt_overlay_draw(rt, pr->x_offset + (it->x - rt->x_scroll) + x, + pr->y_offset + (it->y - rt->y_scroll) + y, + w, h, + it); + } +} + + +static gboolean rt_tile_is_visible(RendererTiles *rt, ImageTile *it) +{ + PixbufRenderer *pr = rt->pr; + return (it->x + it->w >= rt->x_scroll && it->x < rt->x_scroll + pr->vis_width && + it->y + it->h >= rt->y_scroll && it->y < rt->y_scroll + pr->vis_height); +} + +/* + *------------------------------------------------------------------- + * draw queue + *------------------------------------------------------------------- + */ + +static gint rt_get_queued_area(GList *work) +{ + gint area = 0; + + while (work) + { + QueueData *qd = work->data; + area += qd->w * qd->h; + work = work->next; + } + return area; +} + + +static gboolean rt_queue_schedule_next_draw(RendererTiles *rt, gboolean force_set) +{ + PixbufRenderer *pr = rt->pr; + gfloat percent; + gint visible_area = pr->vis_width * pr->vis_height; + + if (!pr->loading) + { + /* 2pass prio */ + DEBUG_2("redraw priority: 2pass"); + rt->draw_idle_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, rt_queue_draw_idle_cb, rt, NULL); + return FALSE; + } + + if (visible_area == 0) + { + /* not known yet */ + percent = 100.0; + } + else + { + percent = 100.0 * rt_get_queued_area(rt->draw_queue) / visible_area; + } + + if (percent > 10.0) + { + /* we have enough data for starting intensive redrawing */ + DEBUG_2("redraw priority: high %.2f %%", percent); + rt->draw_idle_id = g_idle_add_full(GDK_PRIORITY_REDRAW, rt_queue_draw_idle_cb, rt, NULL); + return FALSE; + } + + if (percent < 1.0 || force_set) + { + /* queue is (almost) empty, wait 50 ms*/ + DEBUG_2("redraw priority: wait %.2f %%", percent); + rt->draw_idle_id = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 50, rt_queue_draw_idle_cb, rt, NULL); + return FALSE; + } + + /* keep the same priority as before */ + DEBUG_2("redraw priority: no change %.2f %%", percent); + return TRUE; +} + + +static gboolean rt_queue_draw_idle_cb(gpointer data) +{ + RendererTiles *rt = data; + PixbufRenderer *pr = rt->pr; + QueueData *qd; + gboolean fast; + + + if ((!pr->pixbuf && !pr->source_tiles_enabled) || + (!rt->draw_queue && !rt->draw_queue_2pass) || + !rt->draw_idle_id) + { + pr_render_complete_signal(pr); + + rt->draw_idle_id = 0; + return FALSE; + } + + if (rt->draw_queue) + { + qd = rt->draw_queue->data; + fast = (pr->zoom_2pass && ((pr->zoom_quality != GDK_INTERP_NEAREST && pr->scale != 1.0) || pr->post_process_slow)); + } + else + { + if (pr->loading) + { + /* still loading, wait till done (also drops the higher priority) */ + + return rt_queue_schedule_next_draw(rt, FALSE); + } + + qd = rt->draw_queue_2pass->data; + fast = FALSE; + } + +#if GTK_CHECK_VERSION(2,20,0) + if (gtk_widget_get_realized(pr)) +#else + if (GTK_WIDGET_REALIZED(pr)) +#endif + { + if (rt_tile_is_visible(rt, qd->it)) + { + rt_tile_expose(rt, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast); + } + else if (qd->new_data) + { + /* if new pixel data, and we already have a pixmap, update the tile */ + qd->it->blank = FALSE; + if (qd->it->pixmap && qd->it->render_done == TILE_RENDER_ALL) + { + rt_tile_render(rt, qd->it, qd->x, qd->y, qd->w, qd->h, qd->new_data, fast); + } + } + } + + if (rt->draw_queue) + { + qd->it->qd = NULL; + rt->draw_queue = g_list_remove(rt->draw_queue, qd); + if (fast) + { + if (qd->it->qd2) + { + rt_queue_merge(qd->it->qd2, qd); + g_free(qd); + } + else + { + qd->it->qd2 = qd; + rt->draw_queue_2pass = g_list_append(rt->draw_queue_2pass, qd); + } + } + else + { + g_free(qd); + } + } + else + { + qd->it->qd2 = NULL; + rt->draw_queue_2pass = g_list_remove(rt->draw_queue_2pass, qd); + g_free(qd); + } + + if (!rt->draw_queue && !rt->draw_queue_2pass) + { + pr_render_complete_signal(pr); + + rt->draw_idle_id = 0; + return FALSE; + } + + return rt_queue_schedule_next_draw(rt, FALSE); +} + +static void rt_queue_list_free(GList *list) +{ + GList *work; + + work = list; + while (work) + { + QueueData *qd; + + qd = work->data; + work = work->next; + + qd->it->qd = NULL; + qd->it->qd2 = NULL; + g_free(qd); + } + + g_list_free(list); +} + +static void rt_queue_clear(RendererTiles *rt) +{ + rt_queue_list_free(rt->draw_queue); + rt->draw_queue = NULL; + + rt_queue_list_free(rt->draw_queue_2pass); + rt->draw_queue_2pass = NULL; + + if (rt->draw_idle_id) + { + g_source_remove(rt->draw_idle_id); + rt->draw_idle_id = 0; + } + rt_sync_scroll(rt); +} + +static void rt_queue_merge(QueueData *parent, QueueData *qd) +{ + if (parent->x + parent->w < qd->x + qd->w) + { + parent->w += (qd->x + qd->w) - (parent->x + parent->w); + } + if (parent->x > qd->x) + { + parent->w += parent->x - qd->x; + parent->x = qd->x; + } + + if (parent->y + parent->h < qd->y + qd->h) + { + parent->h += (qd->y + qd->h) - (parent->y + parent->h); + } + if (parent->y > qd->y) + { + parent->h += parent->y - qd->y; + parent->y = qd->y; + } + + parent->new_data |= qd->new_data; +} + +static gboolean rt_clamp_to_visible(RendererTiles *rt, gint *x, gint *y, gint *w, gint *h) +{ + PixbufRenderer *pr = rt->pr; + gint nx, ny; + gint nw, nh; + gint vx, vy; + gint vw, vh; + + vw = pr->vis_width; + vh = pr->vis_height; + + vx = rt->x_scroll; + vy = rt->y_scroll; + + if (*x + *w < vx || *x > vx + vw || *y + *h < vy || *y > vy + vh) return FALSE; + + /* now clamp it */ + nx = CLAMP(*x, vx, vx + vw); + nw = CLAMP(*w - (nx - *x), 1, vw); + + ny = CLAMP(*y, vy, vy + vh); + nh = CLAMP(*h - (ny - *y), 1, vh); + + *x = nx; + *y = ny; + *w = nw; + *h = nh; + + return TRUE; +} + +static gboolean rt_queue_to_tiles(RendererTiles *rt, gint x, gint y, gint w, gint h, + gboolean clamp, ImageRenderType render, + gboolean new_data, gboolean only_existing) +{ + PixbufRenderer *pr = rt->pr; + gint i, j; + gint x1, x2; + gint y1, y2; + + if (clamp && !rt_clamp_to_visible(rt, &x, &y, &w, &h)) return FALSE; + + x1 = ROUND_DOWN(x, rt->tile_width); + x2 = ROUND_UP(x + w, rt->tile_width); + + y1 = ROUND_DOWN(y, rt->tile_height); + y2 = ROUND_UP(y + h, rt->tile_height); + + for (j = y1; j <= y2; j += rt->tile_height) + { + for (i = x1; i <= x2; i += rt->tile_width) + { + ImageTile *it; + + it = rt_tile_get(rt, i, j, + (only_existing && + (i + rt->tile_width < rt->x_scroll || + i > rt->x_scroll + pr->vis_width || + j + rt->tile_height < rt->y_scroll || + j > rt->y_scroll + pr->vis_height))); + if (it) + { + QueueData *qd; + + if ((render == TILE_RENDER_ALL && it->render_done != TILE_RENDER_ALL) || + (render == TILE_RENDER_AREA && it->render_todo != TILE_RENDER_ALL)) + { + it->render_todo = render; + } + + qd = g_new(QueueData, 1); + qd->it = it; + qd->new_data = new_data; + + if (i < x) + { + qd->x = x - i; + } + else + { + qd->x = 0; + } + qd->w = x + w - i - qd->x; + if (qd->x + qd->w > rt->tile_width) qd->w = rt->tile_width - qd->x; + + if (j < y) + { + qd->y = y - j; + } + else + { + qd->y = 0; + } + qd->h = y + h - j - qd->y; + if (qd->y + qd->h > rt->tile_height) qd->h = rt->tile_height - qd->y; + + if (qd->w < 1 || qd->h < 1) + { + g_free(qd); + } + else if (it->qd) + { + rt_queue_merge(it->qd, qd); + g_free(qd); + } + else + { + it->qd = qd; + rt->draw_queue = g_list_append(rt->draw_queue, qd); + } + } + } + } + + return TRUE; +} + +static void rt_queue(RendererTiles *rt, gint x, gint y, gint w, gint h, + gboolean clamp, ImageRenderType render, + gboolean new_data, gboolean only_existing) +{ + PixbufRenderer *pr = rt->pr; + gint nx, ny; + + rt_sync_scroll(rt); + + nx = CLAMP(x, 0, pr->width - 1); + ny = CLAMP(y, 0, pr->height - 1); + w -= (nx - x); + h -= (ny - y); + w = CLAMP(w, 0, pr->width - nx); + h = CLAMP(h, 0, pr->height - ny); + if (w < 1 || h < 1) return; + + if (rt_queue_to_tiles(rt, nx, ny, w, h, clamp, render, new_data, only_existing) && + ((!rt->draw_queue && !rt->draw_queue_2pass) || !rt->draw_idle_id)) + { + if (rt->draw_idle_id) + { + g_source_remove(rt->draw_idle_id); + rt->draw_idle_id = 0; + } + rt_queue_schedule_next_draw(rt, TRUE); + } +} + +static void rt_scroll(RendererTiles *rt, gint x_off, gint y_off) +{ + PixbufRenderer *pr = rt->pr; + + rt_sync_scroll(rt); + if (rt->stereo_mode & PR_STEREO_MIRROR) x_off = -x_off; + if (rt->stereo_mode & PR_STEREO_FLIP) y_off = -y_off; + + gint w = pr->vis_width - abs(x_off); + gint h = pr->vis_height - abs(y_off); + + if (w < 1 || h < 1) + { + /* scrolled completely to new material */ + rt_queue(rt, 0, 0, pr->width, pr->height, TRUE, TILE_RENDER_ALL, FALSE, FALSE); + return; + } + else + { + gint x1, y1; + gint x2, y2; + GtkWidget *box; + GdkGC *gc; + GdkEvent *event; + + if (x_off < 0) + { + x1 = abs(x_off); + x2 = 0; + } + else + { + x1 = 0; + x2 = abs(x_off); + } + + if (y_off < 0) + { + y1 = abs(y_off); + y2 = 0; + } + else + { + y1 = 0; + y2 = abs(y_off); + } + + box = GTK_WIDGET(pr); + + gc = gdk_gc_new(box->window); + gdk_gc_set_exposures(gc, TRUE); + gdk_draw_drawable(box->window, gc, + box->window, + x2 + pr->x_offset + rt->stereo_off_x, y2 + pr->y_offset + rt->stereo_off_y, + x1 + pr->x_offset + rt->stereo_off_x, y1 + pr->y_offset + rt->stereo_off_y, w, h); + g_object_unref(gc); + + rt_overlay_queue_all(rt, x2, y2, x1, y1); + + w = pr->vis_width - w; + h = pr->vis_height - h; + + if (w > 0) + { + rt_queue(rt, + x_off > 0 ? rt->x_scroll + (pr->vis_width - w) : rt->x_scroll, rt->y_scroll, + w, pr->vis_height, TRUE, TILE_RENDER_ALL, FALSE, FALSE); + } + if (h > 0) + { + /* FIXME, to optimize this, remove overlap */ + rt_queue(rt, + rt->x_scroll, y_off > 0 ? rt->y_scroll + (pr->vis_height - h) : rt->y_scroll, + pr->vis_width, h, TRUE, TILE_RENDER_ALL, FALSE, FALSE); + } + + /* process exposures here, "expose_event" seems to miss a few with obstructed windows */ +#if ! GTK_CHECK_VERSION(2,18,0) + while ((event = gdk_event_get_graphics_expose(box->window)) != NULL) + { + renderer_redraw((void *) rt, event->expose.area.x, event->expose.area.y, event->expose.area.width, event->expose.area.height, + FALSE, TILE_RENDER_ALL, FALSE, FALSE); + + if (event->expose.count == 0) + { + gdk_event_free(event); + break; + } + gdk_event_free(event); + } +#endif + } +} + +static void renderer_area_changed(void *renderer, gint src_x, gint src_y, gint src_w, gint src_h) +{ + RendererTiles *rt = (RendererTiles *)renderer; + PixbufRenderer *pr = rt->pr; + gint x, y, width, height, x1, y1, x2, y2; + + gint orientation = rt_get_orientation(rt); + pr_coords_map_orientation_reverse(orientation, + src_x - GET_RIGHT_PIXBUF_OFFSET(rt), src_y, + pr->image_width, pr->image_height, + src_w, src_h, + &x, &y, + &width, &height); + + if (pr->scale != 1.0 && pr->zoom_quality != GDK_INTERP_NEAREST) + { + /* increase region when using a zoom quality that may access surrounding pixels */ + y -= 1; + height += 2; + } + + x1 = (gint)floor((gdouble)x * pr->scale); + y1 = (gint)floor((gdouble)y * pr->scale * pr->aspect_ratio); + x2 = (gint)ceil((gdouble)(x + width) * pr->scale); + y2 = (gint)ceil((gdouble)(y + height) * pr->scale * pr->aspect_ratio); + + rt_queue(rt, x1, y1, x2 - x1, y2 - y1, FALSE, TILE_RENDER_AREA, TRUE, TRUE); +} + +static void renderer_redraw(void *renderer, gint x, gint y, gint w, gint h, + gint clamp, ImageRenderType render, gboolean new_data, gboolean only_existing) +{ + RendererTiles *rt = (RendererTiles *)renderer; + PixbufRenderer *pr = rt->pr; + + x -= rt->stereo_off_x; + y -= rt->stereo_off_y; + + rt_border_draw(rt, x, y, w, h); + + x = MAX(0, x - pr->x_offset + pr->x_scroll); + y = MAX(0, y - pr->y_offset + pr->y_scroll); + + rt_queue(rt, + x, y, + MIN(w, pr->width - x), + MIN(h, pr->height - y), + clamp, render, new_data, only_existing); +} + +static void renderer_queue_clear(void *renderer) +{ + rt_queue_clear((RendererTiles *)renderer); +} + +static void renderer_border_clear(void *renderer) +{ + rt_border_clear((RendererTiles *)renderer); +} + + +static void renderer_invalidate_all(void *renderer) +{ + rt_tile_invalidate_all((RendererTiles *)renderer); +} + +static void renderer_invalidate_region(void *renderer, gint x, gint y, gint w, gint h) +{ + rt_tile_invalidate_region((RendererTiles *)renderer, x, y, w, h); +} + +static void renderer_overlay_draw(void *renderer, gint x, gint y, gint w, gint h) +{ + rt_overlay_draw((RendererTiles *)renderer, x, y, w, h, NULL); +} + +static void renderer_update_sizes(void *renderer) +{ + RendererTiles *rt = (RendererTiles *)renderer; + + rt->stereo_off_x = 0; + rt->stereo_off_y = 0; + + if (rt->stereo_mode & PR_STEREO_RIGHT) + { + if (rt->stereo_mode & PR_STEREO_HORIZ) + { + rt->stereo_off_x = rt->pr->viewport_width; + } + else if (rt->stereo_mode & PR_STEREO_VERT) + { + rt->stereo_off_y = rt->pr->viewport_height; + } + else if (rt->stereo_mode & PR_STEREO_FIXED) + { + rt->stereo_off_x = rt->pr->stereo_fixed_x_right; + rt->stereo_off_y = rt->pr->stereo_fixed_y_right; + } + } + else + { + if (rt->stereo_mode & PR_STEREO_FIXED) + { + rt->stereo_off_x = rt->pr->stereo_fixed_x_left; + rt->stereo_off_y = rt->pr->stereo_fixed_y_left; + } + } + DEBUG_1("update size: %p %d %d %d %d", rt, rt->stereo_off_x, rt->stereo_off_y, rt->pr->viewport_width, rt->pr->viewport_height); + rt_sync_scroll(rt); + rt_overlay_update_sizes(rt); +} + +static void renderer_stereo_set(void *renderer, gint stereo_mode) +{ + RendererTiles *rt = (RendererTiles *)renderer; + + rt->stereo_mode = stereo_mode; +} + +static void renderer_free(void *renderer) +{ + RendererTiles *rt = (RendererTiles *)renderer; + rt_queue_clear(rt); + rt_tile_free_all(rt); + if (rt->spare_tile) g_object_unref(rt->spare_tile); + if (rt->overlay_buffer) g_object_unref(rt->overlay_buffer); + rt_overlay_list_clear(rt); + /* disconnect "hierarchy-changed" */ + g_signal_handlers_disconnect_matched(G_OBJECT(rt->pr), G_SIGNAL_MATCH_DATA, + 0, 0, 0, NULL, rt); + g_free(rt); +} + +RendererFuncs *renderer_tiles_new(PixbufRenderer *pr) +{ + RendererTiles *rt = g_new0(RendererTiles, 1); + + rt->pr = pr; + + rt->f.redraw = renderer_redraw; + rt->f.area_changed = renderer_area_changed; + rt->f.queue_clear = renderer_queue_clear; + rt->f.border_clear = renderer_border_clear; + rt->f.free = renderer_free; + rt->f.invalidate_all = renderer_invalidate_all; + rt->f.invalidate_region = renderer_invalidate_region; + rt->f.scroll = rt_scroll; + rt->f.update_sizes = renderer_update_sizes; + + + rt->f.overlay_add = renderer_tiles_overlay_add; + rt->f.overlay_set = renderer_tiles_overlay_set; + rt->f.overlay_get = renderer_tiles_overlay_get; + rt->f.overlay_draw = renderer_overlay_draw; + + rt->f.stereo_set = renderer_stereo_set; + + rt->tile_width = PR_TILE_SIZE; + rt->tile_height = PR_TILE_SIZE; + + rt->tiles = NULL; + rt->tile_cache_size = 0; + + rt->tile_cache_max = PR_CACHE_SIZE_DEFAULT; + + rt->draw_idle_id = 0; + + rt->stereo_mode = 0; + rt->stereo_off_x = 0; + rt->stereo_off_y = 0; + + g_signal_connect(G_OBJECT(pr), "hierarchy-changed", + G_CALLBACK(rt_hierarchy_changed_cb), rt); + + return (RendererFuncs *) rt; +} + + +/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/renderer-tiles.h Sat Oct 29 23:41:36 2011 +0100 @@ -0,0 +1,25 @@ +/* + * Geeqie + * (C) 2006 John Ellis + * Copyright (C) 2008 - 2010 The Geeqie Team + * + * 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 RENDERER_TILES_H +#define RENDERER_TILES_H + +#include <gtk/gtkeventbox.h> +#include <gtk/gtkwindow.h> + +#include <pixbuf-renderer.h> + + +RendererFuncs *renderer_tiles_new(PixbufRenderer *pr); + +#endif +/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/search.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/search.c Sat Oct 29 23:41:36 2011 +0100 @@ -2006,7 +2006,7 @@ path = fd->path + strlen(sd->search_dir_fd->path); if (path != fd->path) { - FileData *dir_fd = file_data_new_simple(path); + FileData *dir_fd = file_data_new_dir(path); success = filelist_read(dir_fd, &list, NULL); file_data_unref(dir_fd); } @@ -2124,7 +2124,7 @@ sd->search_similarity_cd = cache_sim_data_new(); } - sd->img_loader = image_loader_new(file_data_new_simple(sd->search_similarity_path)); + sd->img_loader = image_loader_new(file_data_new_group(sd->search_similarity_path)); g_signal_connect(G_OBJECT(sd->img_loader), "error", (GCallback)search_similarity_load_done_cb, sd); g_signal_connect(G_OBJECT(sd->img_loader), "done", (GCallback)search_similarity_load_done_cb, sd); if (image_loader_start(sd->img_loader)) @@ -2208,7 +2208,7 @@ if (isdir(path)) { file_data_unref(sd->search_dir_fd); - sd->search_dir_fd = file_data_new_simple(path); + sd->search_dir_fd = file_data_new_dir(path); tab_completion_append_to_history(sd->path_entry, sd->search_dir_fd->path); @@ -2227,7 +2227,7 @@ { /* search metadata */ file_data_unref(sd->search_dir_fd); - sd->search_dir_fd = file_data_new_simple(get_metadata_cache_dir()); + sd->search_dir_fd = file_data_new_dir(get_metadata_cache_dir()); search_start(sd); } else if (sd->search_type == SEARCH_MATCH_CONTAINS)
--- a/src/thumb.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/thumb.c Sat Oct 29 23:41:36 2011 +0100 @@ -27,7 +27,7 @@ static void thumb_loader_error_cb(ImageLoader *il, gpointer data); -static void thumb_loader_setup(ThumbLoader *tl, const gchar *path); +static void thumb_loader_setup(ThumbLoader *tl, FileData *fd); static GdkPixbuf *get_xv_thumbnail(gchar *thumb_filename, gint max_w, gint max_h); @@ -161,7 +161,7 @@ DEBUG_1("thumbnail size mismatch, regenerating: %s", tl->fd->path); tl->cache_hit = FALSE; - thumb_loader_setup(tl, tl->fd->path); + thumb_loader_setup(tl, tl->fd); g_signal_connect(G_OBJECT(tl->il), "done", (GCallback)thumb_loader_done_cb, tl); @@ -262,12 +262,10 @@ if (!tl->idle_done_id) tl->idle_done_id = g_idle_add(thumb_loader_done_delay_cb, tl); } -static void thumb_loader_setup(ThumbLoader *tl, const gchar *path) +static void thumb_loader_setup(ThumbLoader *tl, FileData *fd) { - FileData *fd = file_data_new_simple(path); image_loader_free(tl->il); tl->il = image_loader_new(fd); - file_data_unref(fd); image_loader_set_priority(tl->il, G_PRIORITY_LOW); /* this will speed up jpegs by up to 3x in some cases */ @@ -377,13 +375,15 @@ if (cache_path) { - thumb_loader_setup(tl, cache_path); + FileData *fd = file_data_new_no_grouping(cache_path); + thumb_loader_setup(tl, fd); + file_data_unref(fd); g_free(cache_path); tl->cache_hit = TRUE; } else { - thumb_loader_setup(tl, tl->fd->path); + thumb_loader_setup(tl, tl->fd); } g_signal_connect(G_OBJECT(tl->il), "done", (GCallback)thumb_loader_done_cb, tl); @@ -395,7 +395,7 @@ tl->cache_hit = FALSE; log_printf("%s", _("Thumbnail image in cache failed to load, trying to recreate.\n")); - thumb_loader_setup(tl, tl->fd->path); + thumb_loader_setup(tl, tl->fd); g_signal_connect(G_OBJECT(tl->il), "done", (GCallback)thumb_loader_done_cb, tl); if (image_loader_start(tl->il)) return TRUE; }
--- a/src/thumb_standard.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/thumb_standard.c Sat Oct 29 23:41:36 2011 +0100 @@ -66,7 +66,6 @@ static void thumb_loader_std_error_cb(ImageLoader *il, gpointer data); static gint thumb_loader_std_setup(ThumbLoaderStd *tl, FileData *fd); -static gint thumb_loader_std_setup_path(ThumbLoaderStd *tl, const gchar *path); ThumbLoaderStd *thumb_loader_std_new(gint width, gint height) @@ -514,10 +513,16 @@ if (!tl->thumb_path_local) { tl->thumb_path = thumb_loader_std_cache_path(tl, TRUE, NULL, FALSE); - if (isfile(tl->thumb_path) && thumb_loader_std_setup_path(tl, tl->thumb_path)) + if (isfile(tl->thumb_path)) { - tl->thumb_path_local = TRUE; - return TRUE; + FileData *fd = file_data_new_no_grouping(tl->thumb_path); + if (thumb_loader_std_setup(tl, fd)) + { + file_data_unref(fd); + tl->thumb_path_local = TRUE; + return TRUE; + } + file_data_unref(fd); } g_free(tl->thumb_path); @@ -629,14 +634,6 @@ return FALSE; } -static gboolean thumb_loader_std_setup_path(ThumbLoaderStd *tl, const gchar *path) -{ - FileData *fd = file_data_new_simple(path); - gboolean ret = thumb_loader_std_setup(tl, fd); - file_data_unref(fd); - return ret; -} - /* * Note: Currently local_cache only specifies where to save a _new_ thumb, if * a valid existing thumb is found anywhere the local thumb will not be created. @@ -689,7 +686,16 @@ tl->thumb_path_local = FALSE; found = isfile(tl->thumb_path); - if (found && thumb_loader_std_setup_path(tl, tl->thumb_path)) return TRUE; + if (found) + { + FileData *fd = file_data_new_no_grouping(tl->thumb_path); + if (thumb_loader_std_setup(tl, fd)) + { + file_data_unref(fd); + return TRUE; + } + file_data_unref(fd); + } if (thumb_loader_std_fail_check(tl) || !thumb_loader_std_next_source(tl, found)) @@ -876,7 +882,8 @@ tv->func_valid = func_valid; tv->data = data; - if (!thumb_loader_std_setup_path(tv->tl, thumb_path)) + FileData *fd = file_data_new_no_grouping(thumb_path); + if (!thumb_loader_std_setup(tv->tl, fd)) { tv->idle_id = g_idle_add(thumb_loader_std_thumb_file_validate_idle_cb, tv); } @@ -885,6 +892,7 @@ tv->idle_id = 0; } + file_data_unref(fd); return tv->tl; } @@ -974,9 +982,8 @@ tm->tl->cache_enable = TRUE; tm->tl->cache_hit = FALSE; tm->tl->cache_local = FALSE; - file_data_unref(tm->tl->fd); - tm->tl->fd = file_data_new_simple(tm->dest); + tm->tl->fd = file_data_new_group(tm->dest); tm->tl->source_mtime = strtol(mtime_str, NULL, 10); pathl = path_from_utf8(tm->tl->fd->path);
--- a/src/trash.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/trash.c Sat Oct 29 23:41:36 2011 +0100 @@ -37,7 +37,7 @@ gboolean warned = FALSE; FileData *dir_fd; - dir_fd = file_data_new_simple(options->file_ops.safe_delete_path); + dir_fd = file_data_new_dir(options->file_ops.safe_delete_path); if (!filelist_read(dir_fd, &list, NULL)) { file_data_unref(dir_fd);
--- a/src/typedefs.h Fri Apr 22 11:29:35 2011 +0100 +++ b/src/typedefs.h Sat Oct 29 23:41:36 2011 +0100 @@ -186,6 +186,38 @@ TOOLBAR_COUNT } ToolbarType; +typedef enum { + PR_STEREO_NONE = 0, /* do nothing */ + PR_STEREO_DUAL = 1 << 0, /* independent stereo buffers, for example nvidia opengl */ + PR_STEREO_FIXED = 1 << 1, /* custom position */ + PR_STEREO_HORIZ = 1 << 2, /* side by side */ + PR_STEREO_VERT = 1 << 3, /* above below */ + PR_STEREO_RIGHT = 1 << 4, /* render right buffer */ + PR_STEREO_ANAGLYPH_RC = 1 << 5, /* anaglyph red-cyan */ + PR_STEREO_ANAGLYPH_GRAY = 1 << 6, /* anaglyph gray red-cyan*/ + PR_STEREO_ANAGLYPH_DB = 1 << 7, /* anaglyph dubois*/ + PR_STEREO_ANAGLYPH = PR_STEREO_ANAGLYPH_RC | PR_STEREO_ANAGLYPH_GRAY | PR_STEREO_ANAGLYPH_DB, /* anaglyph mask */ + + PR_STEREO_MIRROR_LEFT = 1 << 8, /* mirror */ + PR_STEREO_FLIP_LEFT = 1 << 9, /* flip */ + + PR_STEREO_MIRROR_RIGHT = 1 << 10, /* mirror */ + PR_STEREO_FLIP_RIGHT = 1 << 11, /* flip */ + + PR_STEREO_MIRROR = PR_STEREO_MIRROR_LEFT | PR_STEREO_MIRROR_RIGHT, /* mirror mask*/ + PR_STEREO_FLIP = PR_STEREO_FLIP_LEFT | PR_STEREO_FLIP_RIGHT, /* flip mask*/ + PR_STEREO_SWAP = 1 << 12, /* swap left and right buffers */ + PR_STEREO_TEMP_DISABLE = 1 << 13, /* temporarily disable stereo mode if source image is not stereo */ + PR_STEREO_HALF = 1 << 14 +} PixbufRendererStereoMode; + +typedef enum { + STEREO_PIXBUF_DEFAULT = 0, + STEREO_PIXBUF_SBS = 1, + STEREO_PIXBUF_CROSS = 2, + STEREO_PIXBUF_NONE = 3 +} StereoPixbufData; + #define MAX_SPLIT_IMAGES 4 typedef struct _ImageLoader ImageLoader; @@ -440,6 +472,7 @@ gboolean delay_flip; gint orientation; gboolean desaturate; + gint user_stereo; }; #define FILEDATA_MARKS_SIZE 6 @@ -464,6 +497,7 @@ gint64 size; time_t date; mode_t mode; /* this is needed at least for notification in view_dir because it is preserved after the file/directory is deleted */ + gint sidecar_priority; guint marks; /* each bit represents one mark */ guint valid_marks; /* zero bit means that the corresponding mark needs to be reread */ @@ -488,6 +522,7 @@ ExifData *exif; GHashTable *modified_xmp; // hash table which contains unwritten xmp metadata in format: key->list of string values + GList *cached_metadata; }; struct _LayoutOptions
--- a/src/ui_fileops.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/ui_fileops.c Sat Oct 29 23:41:36 2011 +0100 @@ -490,8 +490,14 @@ /* set the dest file attributes to that of source (ignoring errors) */ - if (perms && chown(tl, st.st_uid, st.st_gid) < 0) ret = FALSE; - if (perms && chmod(tl, st.st_mode) < 0) ret = FALSE; + if (perms) + { + ret = chown(tl, st.st_uid, st.st_gid); + /* Ignores chown errors, while still doing chown + (so root still can copy files preserving ownership) */ + ret = TRUE; + if (chmod(tl, st.st_mode) < 0) ret = FALSE; + } tb.actime = st.st_atime; tb.modtime = st.st_mtime;
--- a/src/ui_misc.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/ui_misc.c Sat Oct 29 23:41:36 2011 +0100 @@ -722,6 +722,20 @@ return spin; } +GtkWidget *pref_table_spin_new_int(GtkWidget *table, gint column, gint row, + const gchar *text, const gchar *suffix, + gint min, gint max, gint step, + gint value, gint *value_var) +{ + *value_var = value; + return pref_table_spin(table, column, row, + text, suffix, + (gdouble)min, (gdouble)max, (gdouble)step, 0, + value, + G_CALLBACK(pref_spin_int_cb), value_var); +} + + #if ! GTK_CHECK_VERSION(2,12,0) static void pref_toolbar_destroy_cb(GtkWidget *widget, gpointer data)
--- a/src/ui_misc.h Fri Apr 22 11:29:35 2011 +0100 +++ b/src/ui_misc.h Sat Oct 29 23:41:36 2011 +0100 @@ -136,6 +136,11 @@ gdouble value, GCallback func, gpointer data); +GtkWidget *pref_table_spin_new_int(GtkWidget *table, gint column, gint row, + const gchar *text, const gchar *suffix, + gint min, gint max, gint step, + gint value, gint *value_var); + GtkWidget *pref_toolbar_new(GtkWidget *parent_box, GtkToolbarStyle style); GtkWidget *pref_toolbar_button(GtkWidget *toolbar,
--- a/src/utilops.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/utilops.c Sat Oct 29 23:41:36 2011 +0100 @@ -1074,7 +1074,7 @@ break; case UTILITY_TYPE_CREATE_FOLDER: file_data_unref(ud->dir_fd); - ud->dir_fd = file_data_new_simple(ud->dest_path); + ud->dir_fd = file_data_new_dir(ud->dest_path); break; case UTILITY_TYPE_DELETE: case UTILITY_TYPE_DELETE_LINK: @@ -2676,7 +2676,7 @@ g_free(buf); } - ud->dir_fd = file_data_new_simple(ud->dest_path); + ud->dir_fd = file_data_new_dir(ud->dest_path); ud->done_func = done_func; ud->done_data = done_data;
--- a/src/view_dir.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/view_dir.c Sat Oct 29 23:41:36 2011 +0100 @@ -241,7 +241,7 @@ ViewDir *vd = data; if (success) { - FileData *fd = file_data_new_simple(new_path); + FileData *fd = file_data_new_dir(new_path); GtkTreeIter iter; if (vd_find_row(vd, fd, &iter)) @@ -435,7 +435,7 @@ if (vd->select_func) { - FileData *fd = file_data_new_simple(path); + FileData *fd = file_data_new_dir(path); vd->select_func(vd, fd, vd->select_data); file_data_unref(fd); } @@ -561,7 +561,7 @@ break; case DIRVIEW_TREE: { - FileData *new_fd = file_data_new_simple(new_path); + FileData *new_fd = file_data_new_dir(new_path); fd = vdtree_populate_path(vd, new_fd, TRUE, TRUE); file_data_unref(new_fd); } @@ -1117,7 +1117,7 @@ if (vd->type == DIRVIEW_TREE) { GtkTreeIter iter; - FileData *base_fd = file_data_new_simple(base); + FileData *base_fd = file_data_new_dir(base); if (vd_find_row(vd, base_fd, &iter)) {
--- a/src/view_dir_list.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/view_dir_list.c Sat Oct 29 23:41:36 2011 +0100 @@ -159,7 +159,7 @@ if (strcmp(vd->dir_fd->path, G_DIR_SEPARATOR_S) != 0) { filepath = g_build_filename(vd->dir_fd->path, "..", NULL); - fd = file_data_new_simple(filepath); + fd = file_data_new_dir(filepath); VDLIST(vd)->list = g_list_prepend(VDLIST(vd)->list, fd); g_free(filepath); } @@ -167,7 +167,7 @@ if (options->file_filter.show_dot_directory) { filepath = g_build_filename(vd->dir_fd->path, ".", NULL); - fd = file_data_new_simple(filepath); + fd = file_data_new_dir(filepath); VDLIST(vd)->list = g_list_prepend(VDLIST(vd)->list, fd); g_free(filepath); }
--- a/src/view_dir_tree.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/view_dir_tree.c Sat Oct 29 23:41:36 2011 +0100 @@ -159,7 +159,7 @@ { if (!nd) return; - file_data_unref(nd->fd); + if (nd->fd) file_data_unref(nd->fd); g_free(nd); } @@ -281,7 +281,7 @@ NodeData *nd; gtk_tree_model_get(store, &iter, DIR_COLUMN_POINTER, &nd, -1); - if (strcmp(nd->fd->name, pd->name) == 0) + if (nd->fd && strcmp(nd->fd->name, pd->name) == 0) { fd = nd->fd; } @@ -419,7 +419,7 @@ /* all nodes are created with an "empty" node, so that the expander is shown * this is removed when the child is populated */ end = g_new0(NodeData, 1); - end->fd = file_data_new_simple(""); + end->fd = NULL; end->expanded = TRUE; gtk_tree_store_append(store, &empty, &child); @@ -462,7 +462,7 @@ if (nd->expanded) { - if (!isdir(nd->fd->path)) + if (!nd->fd || !isdir(nd->fd->path)) { if (vd->click_fd == nd->fd) vd->click_fd = NULL; if (vd->drop_fd == nd->fd) vd->drop_fd = NULL; @@ -495,16 +495,15 @@ if (target_fd->path[n] == G_DIR_SEPARATOR && target_fd->path[n+1] == '.') { gchar *name8; - struct stat sbuf; n++; while (target_fd->path[n] != '\0' && target_fd->path[n] != G_DIR_SEPARATOR) n++; name8 = g_strndup(target_fd->path, n); - if (stat_utf8(name8, &sbuf)) + if (isdir(name8)) { - list = g_list_prepend(list, file_data_new_simple(name8)); + list = g_list_prepend(list, file_data_new_dir(name8)); } g_free(name8); @@ -904,6 +903,10 @@ gtk_tree_model_get(store, a, DIR_COLUMN_POINTER, &nda, -1); gtk_tree_model_get(store, b, DIR_COLUMN_POINTER, &ndb, -1); + if (!nda->fd && !ndb->fd) return 0; + if (!nda->fd) return 1; + if (!ndb->fd) return -1; + if (options->file_sort.case_sensitive) return strcmp(nda->fd->collate_key_name, ndb->fd->collate_key_name); else @@ -922,7 +925,7 @@ FileData *fd; - fd = file_data_new_simple(path); + fd = file_data_new_dir(path); vdtree_add_by_data(vd, fd, NULL); vdtree_expand_by_data(vd, fd, TRUE);
--- a/src/view_file_list.c Fri Apr 22 11:29:35 2011 +0100 +++ b/src/view_file_list.c Sat Oct 29 23:41:36 2011 +0100 @@ -516,7 +516,7 @@ else { gchar *old_path = g_build_filename(vf->dir_fd->path, old, NULL); - FileData *fd = file_data_new_simple(old_path); /* get the fd from cache */ + FileData *fd = file_data_new_group(old_path); /* get the fd from cache */ file_util_rename_simple(fd, new_path, vf->listview); file_data_unref(fd); g_free(old_path);