sxiv

simple X image viewer
git clone git://git.suckless.org/sxiv
Log | Files | Refs | LICENSE

commit eaa269b6cb486f83229cb0d3dc5f7e03d1c485bb
parent 52e56c892460e7d1f63ca557b705812f08d6d20c
Author: Bert M√ľnnich <ber.t@posteo.de>
Date:   Thu, 25 Sep 2014 20:57:24 +0200

Revised thumbnail loading...

- Only load the thumbnails that are currently visible in the window
- Unload thumbnails that are leaving the visible area
- Much less memory needed, but scrolling is now slower
- This also unintentionally fixes issue #86

Diffstat:
Makefile | 2+-
commands.c | 3++-
main.c | 33+++++++++++++--------------------
thumbs.c | 72++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
thumbs.h | 6+++---
5 files changed, 71 insertions(+), 45 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,4 +1,4 @@ -VERSION = git-20140911 +VERSION = git-20140926 PREFIX = /usr/local MANPREFIX = $(PREFIX)/share/man diff --git a/commands.c b/commands.c @@ -470,7 +470,8 @@ bool ct_reload_all(arg_t a) { tns_free(&tns); tns_init(&tns, files, filecnt, &fileidx, &win); - return false; + tns.dirty = true; + return true; } diff --git a/main.c b/main.c @@ -368,14 +368,13 @@ void update_info(void) return; mark = files[fileidx].marked ? "+ " : ""; if (mode == MODE_THUMB) { - if (tns.loadnext >= filecnt) { - n = snprintf(rt, rlen, "%s%0*d/%d", mark, fw, fileidx + 1, filecnt); - ow_info = true; - } else { - snprintf(lt, llen, "Loading... %0*d/%d", fw, tns.loadnext, filecnt); - rt[0] = '\0'; + if (tns.loadnext < tns.end) { + snprintf(lt, llen, "Loading... %0*d", fw, tns.loadnext); ow_info = false; + } else { + ow_info = true; } + n = snprintf(rt, rlen, "%s%0*d/%d", mark, fw, fileidx + 1, filecnt); } else { n = snprintf(rt, rlen, "%s", mark); if (img.ss.on) @@ -439,7 +438,7 @@ void reset_cursor(void) } } } else { - if (tns.loadnext < filecnt) + if (tns.loadnext < tns.end) cursor = CURSOR_WATCH; else cursor = CURSOR_ARROW; @@ -531,7 +530,7 @@ void run_key_handler(const char *key, unsigned int mask) memcmp(&oldst->st_mtime, &newst.st_mtime, sizeof(newst.st_mtime)) != 0) { if (tns.thumbs != NULL) { - tns.thumbs[finfo[i].fn].loaded = false; + tns_unload(&tns, finfo[i].fn); tns.loadnext = MIN(tns.loadnext, finfo[i].fn); } changed = true; @@ -669,29 +668,24 @@ void run(void) int xfd; fd_set fds; struct timeval timeout; - bool discard, reload, to_set; + bool discard, to_set; XEvent ev, nextev; set_timeout(redraw, 25, false); while (true) { - while (mode == MODE_THUMB && tns.loadnext < filecnt && + while (mode == MODE_THUMB && tns.loadnext < tns.end && XPending(win.env.dpy) == 0) { /* load thumbnails */ - reload = tns.loadnext != tns.cnt; set_timeout(redraw, TO_REDRAW_THUMBS, false); - if (tns_load(&tns, tns.loadnext, reload)) { - if (!reload) - tns.cnt++; - } else { + if (!tns_load(&tns, tns.loadnext, false)) { remove_file(tns.loadnext, false); - if (reload) - tns.dirty = true; + tns.dirty = true; } - while (tns.loadnext < filecnt && tns.thumbs[tns.loadnext].loaded) + while (tns.loadnext < tns.end && tns.thumbs[tns.loadnext].im != NULL) tns.loadnext++; - if (tns.loadnext >= filecnt) + if (tns.loadnext >= tns.end) redraw(); else check_timeouts(NULL); @@ -882,7 +876,6 @@ int main(int argc, char **argv) tns_init(&tns, files, filecnt, &fileidx, &win); while (!tns_load(&tns, 0, false)) remove_file(0, false); - tns.cnt = 1; } else { mode = MODE_IMAGE; tns.thumbs = NULL; diff --git a/thumbs.c b/thumbs.c @@ -56,7 +56,7 @@ char* tns_cache_filepath(const char *filepath) return cfile; } -Imlib_Image tns_cache_load(const char *filepath) +Imlib_Image tns_cache_load(const char *filepath, bool *outdated) { char *cfile; struct stat cstats, fstats; @@ -68,8 +68,12 @@ Imlib_Image tns_cache_load(const char *filepath) return NULL; if ((cfile = tns_cache_filepath(filepath)) != NULL) { - if (stat(cfile, &cstats) == 0 && cstats.st_mtime == fstats.st_mtime) - im = imlib_load_image(cfile); + if (stat(cfile, &cstats) == 0) { + if (cstats.st_mtime == fstats.st_mtime) + im = imlib_load_image(cfile); + else + *outdated = true; + } free(cfile); } return im; @@ -165,8 +169,8 @@ void tns_init(tns_t *tns, const fileinfo_t *files, int cnt, int *sel, win_t *win tns->thumbs = NULL; } tns->files = files; - tns->cap = cnt; - tns->cnt = tns->loadnext = tns->first = 0; + tns->cnt = cnt; + tns->loadnext = tns->first = tns->end = tns->r_first = tns->r_end = 0; tns->sel = sel; tns->win = win; tns->dirty = false; @@ -218,7 +222,7 @@ bool tns_load(tns_t *tns, int n, bool force) if (tns == NULL || tns->thumbs == NULL) return false; - if (n < 0 || n >= tns->cap) + if (n < 0 || n >= tns->cnt) return false; file = &tns->files[n]; if (file->name == NULL || file->path == NULL) @@ -231,7 +235,7 @@ bool tns_load(tns_t *tns, int n, bool force) imlib_free_image(); } - if (!force && (im = tns_cache_load(file->path)) != NULL) { + if (!force && (im = tns_cache_load(file->path, &force)) != NULL) { cache_hit = true; } else { #if HAVE_LIBEXIF @@ -323,11 +327,28 @@ bool tns_load(tns_t *tns, int n, bool force) if (!cache_hit) tns_cache_write(t, file, true); - t->loaded = true; tns->dirty = true; return true; } +void tns_unload(tns_t *tns, int n) +{ + thumb_t *t; + + if (tns == NULL || tns->thumbs == NULL) + return; + if (n < 0 || n >= tns->cnt) + return; + + t = &tns->thumbs[n]; + + if (t->im != NULL) { + imlib_context_set_image(t->im); + imlib_free_image(); + t->im = NULL; + } +} + void tns_check_view(tns_t *tns, bool scrolled) { int r; @@ -385,20 +406,31 @@ void tns_render(tns_t *tns) if (r > 0) cnt -= r % tns->cols; } - r = cnt % tns->cols ? 1 : 0; tns->x = x = (win->w - MIN(cnt, tns->cols) * thumb_dim) / 2 + 5; tns->y = y = (win->h - (cnt / tns->cols + r) * thumb_dim) / 2 + 5; + tns->loadnext = tns->cnt; + tns->end = tns->first + cnt; - for (i = 0; i < cnt; i++) { - t = &tns->thumbs[tns->first + i]; - t->x = x + (THUMB_SIZE - t->w) / 2; - t->y = y + (THUMB_SIZE - t->h) / 2; - imlib_context_set_image(t->im); - imlib_render_image_part_on_drawable_at_size(0, 0, t->w, t->h, - t->x, t->y, t->w, t->h); - if (tns->files[tns->first + i].marked) - tns_mark(tns, tns->first + i, true); + for (i = tns->r_first; i < tns->r_end; i++) { + if ((i < tns->first || i >= tns->end) && tns->thumbs[i].im != NULL) + tns_unload(tns, i); + } + tns->r_first = tns->first; + tns->r_end = tns->end; + + for (i = tns->first; i < tns->end; i++) { + t = &tns->thumbs[i]; + if (t->im != NULL) { + t->x = x + (THUMB_SIZE - t->w) / 2; + t->y = y + (THUMB_SIZE - t->h) / 2; + imlib_context_set_image(t->im); + imlib_render_image_on_drawable_at_size(t->x, t->y, t->w, t->h); + if (tns->files[i].marked) + tns_mark(tns, i, true); + } else { + tns->loadnext = MIN(tns->loadnext, i); + } if ((i + 1) % tns->cols == 0) { x = tns->x; y += thumb_dim; @@ -415,7 +447,7 @@ void tns_mark(tns_t *tns, int n, bool mark) if (tns == NULL || tns->thumbs == NULL || tns->win == NULL) return; - if (n >= 0 && n < tns->cnt) { + if (n >= 0 && n < tns->cnt && tns->thumbs[n].im != NULL) { win_t *win = tns->win; thumb_t *t = &tns->thumbs[n]; unsigned long col = win->fullscreen ? win->fscol : win->bgcol; @@ -440,7 +472,7 @@ void tns_highlight(tns_t *tns, int n, bool hl) if (tns == NULL || tns->thumbs == NULL || tns->win == NULL) return; - if (n >= 0 && n < tns->cnt) { + if (n >= 0 && n < tns->cnt && tns->thumbs[n].im != NULL) { win_t *win = tns->win; thumb_t *t = &tns->thumbs[n]; unsigned long col; diff --git a/thumbs.h b/thumbs.h @@ -31,16 +31,15 @@ typedef struct { int h; int x; int y; - bool loaded; } thumb_t; typedef struct { const fileinfo_t *files; thumb_t *thumbs; - int cap; int cnt; int loadnext; - int first; + int first, end; + int r_first, r_end; int *sel; win_t *win; @@ -58,6 +57,7 @@ void tns_init(tns_t*, const fileinfo_t*, int, int*, win_t*); void tns_free(tns_t*); bool tns_load(tns_t*, int, bool); +void tns_unload(tns_t*, int); void tns_render(tns_t*); void tns_mark(tns_t*, int, bool);