sxiv

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

commit 7b91e10f225797cef26ce7094bf2ac6b94901b33
parent b2dbd2fed3c9cee72ac3e84036b6b08b169a376e
Author: Bert M√ľnnich <ber.t@posteo.de>
Date:   Sun, 28 Sep 2014 00:26:02 +0200

Added thumbnail zooming...

- Key mappings +/- are now general commands
- Use JPG as thumbnail cache file format instead of PNG
- Fixes issue #161

Diffstat:
README.md | 4++--
commands.c | 26++++++++++++++------------
commands.lst | 2+-
config.def.h | 12++++++------
sxiv.1 | 12++++++------
thumbs.c | 104++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
thumbs.h | 3+++
7 files changed, 100 insertions(+), 63 deletions(-)

diff --git a/README.md b/README.md @@ -101,6 +101,8 @@ of small previews is displayed, making it easy to choose an image to open. r Reload image D Remove image from file list and go to next image Ctrl-h,j,k,l Scroll one window width/height left/down/up/right + + Zoom in + - Zoom out m Mark/unmark current image M Reverse all image marks Ctrl-m Remove all image marks @@ -123,8 +125,6 @@ of small previews is displayed, making it easy to choose an image to open. h,j,k,l Scroll image 1/5 of window width/height or [count] pixels left/down/up/right (also with arrow keys) H,J,K,L Scroll to left/bottom/top/right image edge - + Zoom in - - Zoom out = Set zoom level to 100%, or [count]% w Set zoom level to 100%, but fit large images into window W Fit image to window diff --git a/commands.c b/commands.c @@ -193,6 +193,20 @@ bool cg_scroll_screen(arg_t a) return tns_scroll(&tns, dir, true); } +bool cg_zoom(arg_t a) +{ + long d = (long) a; + + if (mode == MODE_THUMB) + return tns_zoom(&tns, d); + else if (d > 0) + return img_zoom_in(&img); + else if (d < 0) + return img_zoom_out(&img); + else + return false; +} + bool cg_toggle_image_mark(arg_t a) { files[fileidx].marked = !files[fileidx].marked; @@ -386,18 +400,6 @@ bool ci_drag(arg_t a) return true; } -bool ci_zoom(arg_t a) -{ - long scale = (long) a; - - if (scale > 0) - return img_zoom_in(&img); - else if (scale < 0) - return img_zoom_out(&img); - else - return false; -} - bool ci_set_zoom(arg_t a) { return img_zoom(&img, (prefix ? prefix : (long) a) / 100.0); diff --git a/commands.lst b/commands.lst @@ -8,6 +8,7 @@ G_CMD(remove_image) G_CMD(first) G_CMD(n_or_last) G_CMD(scroll_screen) +G_CMD(zoom) G_CMD(toggle_image_mark) G_CMD(reverse_marks) G_CMD(unmark_all) @@ -20,7 +21,6 @@ I_CMD(toggle_animation) I_CMD(scroll) I_CMD(scroll_to_edge) I_CMD(drag) -I_CMD(zoom) I_CMD(set_zoom) I_CMD(fit_to_win) I_CMD(rotate) diff --git a/config.def.h b/config.def.h @@ -79,6 +79,10 @@ static const keymap_t keys[] = { { ControlMask, XK_Up, g_scroll_screen, (arg_t) DIR_UP }, { ControlMask, XK_l, g_scroll_screen, (arg_t) DIR_RIGHT }, { ControlMask, XK_Right, g_scroll_screen, (arg_t) DIR_RIGHT }, + { 0, XK_plus, g_zoom, (arg_t) +1 }, + { 0, XK_KP_Add, g_zoom, (arg_t) +1 }, + { 0, XK_minus, g_zoom, (arg_t) -1 }, + { 0, XK_KP_Subtract, g_zoom, (arg_t) -1 }, { 0, XK_m, g_toggle_image_mark, (arg_t) None }, { 0, XK_M, g_reverse_marks, (arg_t) None }, { ControlMask, XK_m, g_unmark_all, (arg_t) None }, @@ -119,10 +123,6 @@ static const keymap_t keys[] = { { 0, XK_J, i_scroll_to_edge, (arg_t) DIR_DOWN }, { 0, XK_K, i_scroll_to_edge, (arg_t) DIR_UP }, { 0, XK_L, i_scroll_to_edge, (arg_t) DIR_RIGHT }, - { 0, XK_plus, i_zoom, (arg_t) +1 }, - { 0, XK_KP_Add, i_zoom, (arg_t) +1 }, - { 0, XK_minus, i_zoom, (arg_t) -1 }, - { 0, XK_KP_Subtract, i_zoom, (arg_t) -1 }, { 0, XK_equal, i_set_zoom, (arg_t) 100 }, { 0, XK_w, i_fit_to_win, (arg_t) SCALE_DOWN }, { 0, XK_W, i_fit_to_win, (arg_t) SCALE_FIT }, @@ -153,8 +153,8 @@ static const button_t buttons[] = { { ShiftMask, 5, i_scroll, (arg_t) DIR_RIGHT }, { 0, 6, i_scroll, (arg_t) DIR_LEFT }, { 0, 7, i_scroll, (arg_t) DIR_RIGHT }, - { ControlMask, 4, i_zoom, (arg_t) +1 }, - { ControlMask, 5, i_zoom, (arg_t) -1 }, + { ControlMask, 4, g_zoom, (arg_t) +1 }, + { ControlMask, 5, g_zoom, (arg_t) -1 }, }; #endif diff --git a/sxiv.1 b/sxiv.1 @@ -140,6 +140,12 @@ Scroll up one screen height. .BR Ctrl-l ", " Ctrl-Right Scroll right one screen width. .TP +.BR + +Zoom in. +.TP +.B \- +Zoom out. +.TP .B m Mark/unmark the current image. .TP @@ -251,12 +257,6 @@ Scroll to top image edge. Scroll to right image edge. .SS Zooming .TP -.BR + -Zoom in. -.TP -.B \- -Zoom out. -.TP .B = Set zoom level to 100%, or .IR count %. diff --git a/thumbs.c b/thumbs.c @@ -17,7 +17,6 @@ */ #define _POSIX_C_SOURCE 200112L -#define _THUMBS_CONFIG #include <stdio.h> #include <stdlib.h> @@ -37,7 +36,7 @@ void exif_auto_orientate(const fileinfo_t*); #endif static char *cache_dir; -static const int thumb_dim = THUMB_SIZE + 10; +static const int thumb_size[] = { 32, 64, 96, 128, 160 }; char* tns_cache_filepath(const char *filepath) { @@ -51,7 +50,7 @@ char* tns_cache_filepath(const char *filepath) /* don't cache images inside the cache directory! */ len = strlen(cache_dir) + strlen(filepath) + 6; cfile = (char*) s_malloc(len); - snprintf(cfile, len, "%s/%s.png", cache_dir, filepath + 1); + snprintf(cfile, len, "%s/%s.jpg", cache_dir, filepath + 1); } return cfile; } @@ -79,21 +78,19 @@ Imlib_Image tns_cache_load(const char *filepath, bool *outdated) return im; } -void tns_cache_write(thumb_t *t, const fileinfo_t *file, bool force) +void tns_cache_write(Imlib_Image im, const char *filepath, bool force) { char *cfile, *dirend; struct stat cstats, fstats; struct utimbuf times; Imlib_Load_Error err = 0; - if (t == NULL || t->im == NULL) + if (im == NULL || filepath == NULL) return; - if (file == NULL || file->name == NULL || file->path == NULL) - return; - if (stat(file->path, &fstats) < 0) + if (stat(filepath, &fstats) < 0) return; - if ((cfile = tns_cache_filepath(file->path)) != NULL) { + if ((cfile = tns_cache_filepath(filepath)) != NULL) { if (force || stat(cfile, &cstats) < 0 || cstats.st_mtime != fstats.st_mtime) { @@ -103,8 +100,8 @@ void tns_cache_write(thumb_t *t, const fileinfo_t *file, bool force) *dirend = '/'; } if (err == 0) { - imlib_context_set_image(t->im); - imlib_image_set_format("png"); + imlib_context_set_image(im); + imlib_image_set_format("jpg"); imlib_save_image_with_error_return(cfile, &err); } if (err == 0) { @@ -173,6 +170,7 @@ void tns_init(tns_t *tns, const fileinfo_t *files, int cnt, int *sel, win_t *win tns->loadnext = tns->first = tns->end = tns->r_first = tns->r_end = 0; tns->sel = sel; tns->win = win; + tns->zl = 1; tns->dirty = false; if ((homedir = getenv("XDG_CACHE_HOME")) == NULL || homedir[0] == '\0') { @@ -211,11 +209,34 @@ void tns_free(tns_t *tns) cache_dir = NULL; } +Imlib_Image tns_scale_down(Imlib_Image im, int dim) +{ + int w, h; + float z, zw, zh; + + imlib_context_set_image(im); + w = imlib_image_get_width(); + h = imlib_image_get_height(); + zw = (float) dim / (float) w; + zh = (float) dim / (float) h; + z = MIN(zw, zh); + z = MIN(z, 1.0); + + if (z < 1.0) { + imlib_context_set_anti_alias(1); + im = imlib_create_cropped_scaled_image(0, 0, w, h, z * w, z * h); + if (im == NULL) + die("could not allocate memory"); + imlib_free_image_and_decache(); + } + return im; +} + bool tns_load(tns_t *tns, int n, bool force) { int w, h; bool cache_hit = false; - float z, zw, zh; + float zw, zh; thumb_t *t; Imlib_Image im = NULL; const fileinfo_t *file; @@ -301,31 +322,20 @@ bool tns_load(tns_t *tns, int n, bool force) return false; } } - imlib_context_set_image(im); - imlib_context_set_anti_alias(1); + if (!cache_hit) { #if HAVE_LIBEXIF - if (!cache_hit) + imlib_context_set_image(im); exif_auto_orientate(file); #endif + im = tns_scale_down(im, thumb_size[ARRLEN(thumb_size)-1]); + tns_cache_write(im, file->path, true); + } - w = imlib_image_get_width(); - h = imlib_image_get_height(); - zw = (float) THUMB_SIZE / (float) w; - zh = (float) THUMB_SIZE / (float) h; - z = MIN(zw, zh); - z = MIN(z, 1.0); - t->w = z * w; - t->h = z * h; - - t->im = imlib_create_cropped_scaled_image(0, 0, w, h, t->w, t->h); - if (t->im == NULL) - die("could not allocate memory"); - - imlib_free_image_and_decache(); - - if (!cache_hit) - tns_cache_write(t, file, true); + t->im = tns_scale_down(im, thumb_size[tns->zl]); + imlib_context_set_image(t->im); + t->w = imlib_image_get_width(); + t->h = imlib_image_get_height(); tns->dirty = true; return true; @@ -382,6 +392,7 @@ void tns_render(tns_t *tns) thumb_t *t; win_t *win; int i, cnt, r, x, y; + int thumb_dim; if (tns == NULL || tns->thumbs == NULL || tns->win == NULL) return; @@ -392,6 +403,7 @@ void tns_render(tns_t *tns) win_clear(win); imlib_context_set_drawable(win->buf.pm); + thumb_dim = thumb_size[tns->zl] + 10; tns->cols = MAX(1, win->w / thumb_dim); tns->rows = MAX(1, win->h / thumb_dim); @@ -422,8 +434,8 @@ void tns_render(tns_t *tns) 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; + t->x = x + (thumb_size[tns->zl] - t->w) / 2; + t->y = y + (thumb_size[tns->zl] - 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) @@ -550,6 +562,26 @@ bool tns_scroll(tns_t *tns, direction_t dir, bool screen) return tns->first != old; } +bool tns_zoom(tns_t *tns, int d) +{ + int i, oldzl; + + if (tns == NULL || tns->thumbs == NULL) + return false; + + oldzl = tns->zl; + tns->zl += -(d < 0) + (d > 0); + tns->zl = MAX(tns->zl, 0); + tns->zl = MIN(tns->zl, ARRLEN(thumb_size)-1); + + if (tns->zl != oldzl) { + for (i = 0; i < tns->cnt; i++) + tns_unload(tns, i); + tns->dirty = true; + } + return tns->zl != oldzl; +} + int tns_translate(tns_t *tns, int x, int y) { int n; @@ -559,8 +591,8 @@ int tns_translate(tns_t *tns, int x, int y) if (x < tns->x || y < tns->y) return -1; - n = tns->first + (y - tns->y) / thumb_dim * tns->cols + - (x - tns->x) / thumb_dim; + n = tns->first + (y - tns->y) / (thumb_size[tns->zl] + 10) * tns->cols + + (x - tns->x) / (thumb_size[tns->zl] + 10); if (n >= tns->cnt) n = -1; diff --git a/thumbs.h b/thumbs.h @@ -47,6 +47,7 @@ typedef struct { int y; int cols; int rows; + int zl; bool dirty; } tns_t; @@ -66,6 +67,8 @@ void tns_highlight(tns_t*, int, bool); bool tns_move_selection(tns_t*, direction_t, int); bool tns_scroll(tns_t*, direction_t, bool); +bool tns_zoom(tns_t*, int); + int tns_translate(tns_t*, int, int); #endif /* THUMBS_H */