diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | README.md | 5 | ||||
-rw-r--r-- | commands.c | 22 | ||||
-rw-r--r-- | commands.h | 1 | ||||
-rw-r--r-- | config.def.h | 11 | ||||
-rw-r--r-- | image.c | 54 | ||||
-rw-r--r-- | image.h | 5 | ||||
-rw-r--r-- | main.c | 13 | ||||
-rw-r--r-- | options.c | 17 | ||||
-rw-r--r-- | options.h | 1 | ||||
-rw-r--r-- | sxiv.1 | 27 | ||||
-rw-r--r-- | thumbs.c | 49 | ||||
-rw-r--r-- | thumbs.h | 1 |
13 files changed, 175 insertions, 33 deletions
@@ -1,4 +1,4 @@ -VERSION = git-20131021 +VERSION = git-20131114 PREFIX = /usr/local MANPREFIX = $(PREFIX)/share/man @@ -74,11 +74,12 @@ of small previews is displayed, making it easy to choose an image to open. -d Scale all images to 100%, but fit large images into window -F Use size-hints to make the window fixed/floating -f Start in fullscreen mode + -G GAMMA Set image gamma to GAMMA (-32..32) -g GEOMETRY Set window position and size (see section GEOMETRY SPECIFICATIONS of X(7)) -i Read file list from stdin - -n NUM Start at picture NUM -N NAME Set X window resource name to NAME + -n NUM Start at picture NUM -o Write list of marked files to stdout when quitting -q Be quiet, disable warnings -r Search given directories recursively for images @@ -108,7 +109,7 @@ of small previews is displayed, making it easy to choose an image to open. D Remove image from file list and go to next image m Mark/unmark current image - M Reverse marked images + M Reverse all image marks N Go [count] marked images forward P Go [count] marked images backward @@ -48,7 +48,6 @@ extern win_t win; extern fileinfo_t *files; extern int filecnt, fileidx; -extern int markcnt; extern int alternate; extern int prefix; @@ -61,7 +60,7 @@ bool it_quit(arg_t a) { unsigned int i; - if (options->to_stdout && markcnt > 0) { + if (options->to_stdout) { for (i = 0; i < filecnt; i++) { if (files[i].marked) printf("%s\n", files[i].name); @@ -247,7 +246,8 @@ bool it_toggle_image_mark(arg_t a) int sel = mode == MODE_IMAGE ? fileidx : tns.sel; files[sel].marked = !files[sel].marked; - markcnt += files[sel].marked ? 1 : -1; + if (mode == MODE_THUMB) + tns_mark(&tns, sel, files[sel].marked); return true; } @@ -255,11 +255,10 @@ bool it_reverse_marks(arg_t a) { int i, cnt = mode == MODE_IMAGE ? filecnt : tns.cnt; - for (i = 0; i < cnt; i++) { + for (i = 0; i < cnt; i++) files[i].marked = !files[i].marked; - markcnt += files[i].marked ? 1 : -1; - } - + if (mode == MODE_THUMB) + tns.dirty = true; return true; } @@ -486,6 +485,15 @@ bool i_toggle_antialias(arg_t a) } } +bool i_change_gamma(arg_t a) +{ + if (mode == MODE_IMAGE) { + return img_change_gamma(&img, (long) a); + } else { + return false; + } +} + bool it_toggle_alpha(arg_t a) { img.alpha = tns.alpha = !img.alpha; @@ -68,6 +68,7 @@ bool i_fit_to_img(arg_t); bool i_rotate(arg_t); bool i_flip(arg_t); bool i_toggle_antialias(arg_t); +bool i_change_gamma(arg_t); bool it_toggle_alpha(arg_t); bool it_open_with(arg_t); bool it_shell_cmd(arg_t); diff --git a/config.def.h b/config.def.h index c5e002f..b1e9c02 100644 --- a/config.def.h +++ b/config.def.h @@ -46,6 +46,12 @@ enum { GIF_LOOP = 0 /* endless loop [0/1] */ }; +/* gamma correction: the user-visible ranges [-GAMMA_RANGE, 0] and + * (0, GAMMA_RANGE] are mapped to the ranges [0, 1], and (1, GAMMA_MAX]. + * */ +static const double GAMMA_MAX = 10.0; +static const int GAMMA_RANGE = 32; + #endif #ifdef _THUMBS_CONFIG @@ -142,6 +148,11 @@ static const keymap_t keys[] = { { false, XK_a, i_toggle_antialias, (arg_t) None }, { false, XK_A, it_toggle_alpha, (arg_t) None }, + /* decrease/increase/reset gamma */ + { false, XK_braceleft, i_change_gamma, (arg_t) -1 }, + { false, XK_braceright, i_change_gamma, (arg_t) +1 }, + { true, XK_G, i_change_gamma, (arg_t) 0 }, + /* open current image with given program: */ { true, XK_g, it_open_with, (arg_t) "gimp" }, @@ -39,11 +39,27 @@ enum { MIN_GIF_DELAY = 25 }; float zoom_min; float zoom_max; -int zoomdiff(float z1, float z2) +static int zoomdiff(float z1, float z2) { return (int) (z1 * 1000.0 - z2 * 1000.0); } +static void img_apply_gamma(img_t *img) +{ + if (img == NULL || img->im == NULL || img->cmod == NULL) + return; + + if (img->gamma == 0) { + imlib_context_set_color_modifier(NULL); + } else { + double range = img->gamma <= 0 ? 1.0 : GAMMA_MAX - 1.0; + + imlib_context_set_color_modifier(img->cmod); + imlib_reset_color_modifier(); + imlib_modify_color_modifier_gamma(1.0 + img->gamma * (range / GAMMA_RANGE)); + } +} + void img_init(img_t *img, win_t *win) { zoom_min = zoom_levels[0] / 100.0; @@ -67,6 +83,9 @@ void img_init(img_t *img, win_t *win) img->alpha = !RENDER_WHITE_ALPHA; img->multi.cap = img->multi.cnt = 0; img->multi.animate = false; + + img->cmod = imlib_create_color_modifier(); + img->gamma = MIN(MAX(options->gamma, -GAMMA_RANGE), GAMMA_RANGE); } void exif_auto_orientate(const fileinfo_t *file) @@ -303,6 +322,8 @@ bool img_load(img_t *img, const fileinfo_t *file) img_load_gif(img, file); #endif + img_apply_gamma(img); + img->w = imlib_image_get_width(); img->h = imlib_image_get_height(); img->scalemode = options->scalemode; @@ -335,6 +356,9 @@ void img_close(img_t *img, bool decache) imlib_free_image(); img->im = NULL; } + + if (img->cmod) + imlib_context_set_color_modifier(NULL); } void img_check_pan(img_t *img, bool moved) @@ -706,6 +730,34 @@ void img_toggle_antialias(img_t *img) img->dirty = true; } +bool img_change_gamma(img_t *img, int d) +{ + /* d < 0: decrease gamma + * d = 0: reset gamma + * d > 0: increase gamma + */ + int gamma; + + if (img == NULL || img->im == NULL) + return false; + + if (d == 0) + gamma = 0; + else if (d < 0) + gamma = MAX(-GAMMA_RANGE, img->gamma - 1); + else + gamma = MIN(+GAMMA_RANGE, img->gamma + 1); + + if (img->gamma != gamma) { + img->gamma = gamma; + img_apply_gamma(img); + img->dirty = true; + return true; + } else { + return false; + } +} + bool img_frame_goto(img_t *img, int n) { if (img == NULL || img->im == NULL) @@ -55,6 +55,9 @@ typedef struct { bool aa; bool alpha; + Imlib_Color_Modifier cmod; + int gamma; + multi_img_t multi; } img_t; @@ -81,6 +84,8 @@ void img_flip(img_t*, flipdir_t); void img_toggle_antialias(img_t*); +bool img_change_gamma(img_t*, int); + bool img_frame_navigate(img_t*, int); bool img_frame_animate(img_t*, bool); @@ -65,7 +65,6 @@ win_t win; fileinfo_t *files; int filecnt, fileidx; -int markcnt; int alternate; int prefix; @@ -346,7 +345,10 @@ void update_info(void) ow_info = false; } } else { - n = snprintf(rt, rlen, "%s%3d%% | ", mark, (int) (img.zoom * 100.0)); + n = snprintf(rt, rlen, "%s", mark); + if (img.gamma != 0) + n += snprintf(rt + n, rlen - n, "G%+d | ", img.gamma); + n += snprintf(rt + n, rlen - n, "%3d%% | ", (int) (img.zoom * 100.0)); if (img.multi.cnt > 0) { for (fn = 0, i = img.multi.cnt; i > 0; fn++, i /= 10); n += snprintf(rt + n, rlen - n, "%0*d/%d | ", @@ -493,6 +495,13 @@ void on_buttonpress(XButtonEvent *bev) break; } break; + case Button3: + if ((sel = tns_translate(&tns, bev->x, bev->y)) >= 0) { + files[sel].marked = !files[sel].marked; + tns_mark(&tns, sel, files[sel].marked); + redraw(); + } + break; case Button4: case Button5: if (tns_scroll(&tns, bev->button == Button4 ? DIR_UP : DIR_DOWN, @@ -33,7 +33,7 @@ const options_t *options = (const options_t*) &_options; void print_usage(void) { - printf("usage: sxiv [-bcdFfhioqrstvZ] [-g GEOMETRY] [-n NUM] " + printf("usage: sxiv [-bcdFfhioqrstvZ] [-G GAMMA] [-g GEOMETRY] [-n NUM] " "[-N name] [-z ZOOM] FILES...\n"); } @@ -44,7 +44,8 @@ void print_version(void) void parse_options(int argc, char **argv) { - int opt, t; + int opt, t, gamma; + char *end; _options.from_stdin = false; _options.to_stdout = false; @@ -53,6 +54,7 @@ void parse_options(int argc, char **argv) _options.scalemode = SCALE_MODE; _options.zoom = 1.0; + _options.gamma = 0; _options.fixed_win = false; _options.fullscreen = false; @@ -64,7 +66,7 @@ void parse_options(int argc, char **argv) _options.thumb_mode = false; _options.clean_cache = false; - while ((opt = getopt(argc, argv, "bcdFfg:hin:N:oqrstvZz:")) != -1) { + while ((opt = getopt(argc, argv, "bcdFfG:g:hin:N:oqrstvZz:")) != -1) { switch (opt) { case '?': print_usage(); @@ -84,6 +86,15 @@ void parse_options(int argc, char **argv) case 'f': _options.fullscreen = true; break; + case 'G': + gamma = strtol(optarg, &end, 0); + if (*end != '\0') { + fprintf(stderr, "sxiv: invalid argument for option -G: %s\n", + optarg); + exit(EXIT_FAILURE); + } + _options.gamma = gamma; + break; case 'g': _options.geometry = optarg; break; @@ -34,6 +34,7 @@ typedef struct { /* image: */ scalemode_t scalemode; float zoom; + int gamma; /* window: */ bool fixed_win; @@ -4,12 +4,14 @@ sxiv \- Simple X Image Viewer .SH SYNOPSIS .B sxiv .RB [ \-bcdFfhiopqrstvZ ] +.RB [ \-G +.IR GAMMA ] .RB [ \-g .IR GEOMETRY ] -.RB [ \-n -.IR NUM ] .RB [ \-N .IR NAME ] +.RB [ \-n +.IR NUM ] .RB [ \-z .IR ZOOM ] .IR FILE ... @@ -44,17 +46,20 @@ size-hints to the window width/height. .B \-f Start in fullscreen mode. .TP +.BI "\-G " GAMMA +Set image gamma to GAMMA (-32..32). +.TP .BI "\-g " GEOMETRY Set window position and size. See section GEOMETRY SPECIFICATIONS of X(7) for more information on .IR GEOMETRY . .TP -.BI "\-n " NUM -Start at picture number NUM. -.TP .BI "\-N " NAME Set the resource name of sxiv's X window to NAME. .TP +.BI "\-n " NUM +Start at picture number NUM. +.TP .B \-h Print brief usage information to standard output and exit. .TP @@ -131,7 +136,7 @@ Remove current image from file list and go to next image. Mark/unmark the current image. .TP .B M -Reverse marked images. +Reverse all image marks. .TP .B N Go @@ -285,6 +290,16 @@ Flip image horizontally. .TP .B _ Flip image vertically. +.SS Gamma Correction +.TP +.B { +Decrease gamma. +.TP +.B } +Increase gamma. +.TP +.B Ctrl-G +Reset gamma. .SS Miscellaneous .TP .B a @@ -363,6 +363,8 @@ void tns_render(tns_t *tns) imlib_render_image_part_on_drawable_at_size(0, 0, t->w, t->h, t->x, t->y, t->w, t->h); + if (t->file->marked) + tns_mark(tns, tns->first + i, true); if ((i + 1) % tns->cols == 0) { x = tns->x; y += thumb_dim; @@ -374,20 +376,44 @@ void tns_render(tns_t *tns) tns_highlight(tns, tns->sel, true); } -void tns_highlight(tns_t *tns, int n, bool hl) +void tns_mark(tns_t *tns, int n, bool mark) { - thumb_t *t; - win_t *win; - int x, y; - unsigned long col; - if (tns == NULL || tns->thumbs == NULL || tns->win == NULL) return; - win = tns->win; + if (n >= 0 && n < tns->cnt) { + unsigned long col; + thumb_t *t = &tns->thumbs[n]; + win_t *win = tns->win; + int x = t->x, y = t->y, w = t->w, h = t->h; + + if (mark || n == tns->sel) + col = win->selcol; + else if (win->fullscreen) + col = win->fscol; + else + col = win->bgcol; + + win_draw_rect(win, win->pm, x - 4, y - 4, 8, 2, true, 0, col); + win_draw_rect(win, win->pm, x - 4, y - 4, 2, 8, true, 0, col); + win_draw_rect(win, win->pm, x + w - 4, y - 4, 8, 2, true, 0, col); + win_draw_rect(win, win->pm, x + w + 2, y - 4, 2, 8, true, 0, col); + win_draw_rect(win, win->pm, x - 4, y + h + 2, 8, 2, true, 0, col); + win_draw_rect(win, win->pm, x - 4, y + h - 4, 2, 8, true, 0, col); + win_draw_rect(win, win->pm, x + w - 4, y + h + 2, 8, 2, true, 0, col); + win_draw_rect(win, win->pm, x + w + 2, y + h - 4, 2, 8, true, 0, col); + } +} + +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) { - t = &tns->thumbs[n]; + unsigned long col; + thumb_t *t = &tns->thumbs[n]; + win_t *win = tns->win; if (hl) col = win->selcol; @@ -396,10 +422,11 @@ void tns_highlight(tns_t *tns, int n, bool hl) else col = win->bgcol; - x = t->x - (THUMB_SIZE - t->w) / 2; - y = t->y - (THUMB_SIZE - t->h) / 2; - win_draw_rect(win, win->pm, x - 3, y - 3, THUMB_SIZE + 6, THUMB_SIZE + 6, + win_draw_rect(win, win->pm, t->x - 3, t->y - 3, t->w + 6, t->h + 6, false, 2, col); + + if (!hl && t->file->marked) + tns_mark(tns, n, true); } } @@ -59,6 +59,7 @@ void tns_free(tns_t*); bool tns_load(tns_t*, int, const fileinfo_t*, bool, bool); void tns_render(tns_t*); +void tns_mark(tns_t*, int, bool); void tns_highlight(tns_t*, int, bool); bool tns_move_selection(tns_t*, direction_t, int); |