nsxiv95bc9b4
add brightness and contrast (#396)
* Imlib2 supports modifying gamma, brightness and contrast directly while sxiv only supports gamma. Makes sense to extend it to brightness and contrast as well. * Since color corrections need to be aware of each other, they have been refactored into one centralized function. * This also makes the code more hackable as it makes it easier to add more color correction functions without them interfering with each other.
+92 −35
7 files changed
- M commands.c +20 −7
- M commands.h +4 −0
- M config.def.h +11 −4
- M etc/nsxiv.1 +20 −0
- M image.c +29 −23
- M main.c +4 −0
- M nsxiv.h +4 −1
| @@ -222,15 +222,28 @@ bool cg_navigate_marked(arg_t n) | |||
| 222 | 222 | return navigate_to(new); | |
| 223 | 223 | } | |
| 224 | 224 | ||
| 225 | − | bool cg_change_gamma(arg_t d) | |
| 225 | + | static bool change_color_modifier(arg_t d, int *target) | |
| 226 | 226 | { | |
| 227 | − | if (img_change_gamma(&img, d * (prefix > 0 ? prefix : 1))) { | |
| 228 | − | if (mode == MODE_THUMB) | |
| 229 | − | tns.dirty = true; | |
| 230 | − | return true; | |
| 231 | − | } else { | |
| 227 | + | if (!img_change_color_modifier(&img, d * (prefix > 0 ? prefix : 1), target)) | |
| 232 | 228 | return false; | |
| 233 | − | } | |
| 229 | + | if (mode == MODE_THUMB) | |
| 230 | + | tns.dirty = true; | |
| 231 | + | return true; | |
| 232 | + | } | |
| 233 | + | ||
| 234 | + | bool cg_change_gamma(arg_t d) | |
| 235 | + | { | |
| 236 | + | return change_color_modifier(d, &img.gamma); | |
| 237 | + | } | |
| 238 | + | ||
| 239 | + | bool cg_change_brightness(arg_t d) | |
| 240 | + | { | |
| 241 | + | return change_color_modifier(d, &img.brightness); | |
| 242 | + | } | |
| 243 | + | ||
| 244 | + | bool cg_change_contrast(arg_t d) | |
| 245 | + | { | |
| 246 | + | return change_color_modifier(d, &img.contrast); | |
| 234 | 247 | } | |
| 235 | 248 | ||
| 236 | 249 | bool ci_navigate(arg_t n) | |
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | 5 | /* global */ | |
| 6 | 6 | bool cg_change_gamma(arg_t); | |
| 7 | + | bool cg_change_brightness(arg_t); | |
| 8 | + | bool cg_change_contrast(arg_t); | |
| 7 | 9 | bool cg_first(arg_t); | |
| 8 | 10 | bool cg_mark_range(arg_t); | |
| 9 | 11 | bool cg_n_or_last(arg_t); | |
| @@ -47,6 +49,8 @@ bool ct_select(arg_t); | |||
| 47 | 49 | #ifdef INCLUDE_MAPPINGS_CONFIG | |
| 48 | 50 | /* global */ | |
| 49 | 51 | #define g_change_gamma { cg_change_gamma, MODE_ALL } | |
| 52 | + | #define g_change_brightness { cg_change_brightness, MODE_ALL } | |
| 53 | + | #define g_change_contrast { cg_change_contrast, MODE_ALL } | |
| 50 | 54 | #define g_first { cg_first, MODE_ALL } | |
| 51 | 55 | #define g_mark_range { cg_mark_range, MODE_ALL } | |
| 52 | 56 | #define g_n_or_last { cg_n_or_last, MODE_ALL } | |
| @@ -33,11 +33,14 @@ static const float zoom_levels[] = { | |||
| 33 | 33 | /* default slideshow delay (in sec, overwritten via -S option): */ | |
| 34 | 34 | static const int SLIDESHOW_DELAY = 5; | |
| 35 | 35 | ||
| 36 | − | /* gamma correction: the user-visible ranges [-GAMMA_RANGE, 0] and | |
| 37 | − | * (0, GAMMA_RANGE] are mapped to the ranges [0, 1], and (1, GAMMA_MAX]. | |
| 36 | + | /* color correction: the user-visible ranges [-CC_STEPS, 0] and | |
| 37 | + | * (0, CC_STEPS] are mapped to the ranges [0, 1], and (1, *_MAX]. | |
| 38 | + | * Higher step count will have higher granulairy. | |
| 38 | 39 | */ | |
| 39 | − | static const double GAMMA_MAX = 10.0; | |
| 40 | − | static const int GAMMA_RANGE = 32; | |
| 40 | + | static const int CC_STEPS = 32; | |
| 41 | + | static const double GAMMA_MAX = 10.0; | |
| 42 | + | static const double BRIGHTNESS_MAX = 2.0; | |
| 43 | + | static const double CONTRAST_MAX = 4.0; | |
| 41 | 44 | ||
| 42 | 45 | /* command i_scroll pans image 1/PAN_FRACTION of screen width/height */ | |
| 43 | 46 | static const int PAN_FRACTION = 5; | |
| @@ -118,6 +121,10 @@ static const keymap_t keys[] = { | |||
| 118 | 121 | { 0, XK_braceleft, g_change_gamma, -1 }, | |
| 119 | 122 | { 0, XK_braceright, g_change_gamma, +1 }, | |
| 120 | 123 | { ControlMask, XK_g, g_change_gamma, 0 }, | |
| 124 | + | { ControlMask, XK_bracketright, g_change_brightness, +1 }, | |
| 125 | + | { ControlMask, XK_bracketleft, g_change_brightness, -1 }, | |
| 126 | + | { 0, XK_parenleft, g_change_contrast, -1 }, | |
| 127 | + | { 0, XK_parenright, g_change_contrast, +1 }, | |
| 121 | 128 | ||
| 122 | 129 | { 0, XK_h, t_move_sel, DIR_LEFT }, | |
| 123 | 130 | { 0, XK_Left, t_move_sel, DIR_LEFT }, | |
| @@ -209,6 +209,26 @@ steps. | |||
| 209 | 209 | .TP | |
| 210 | 210 | .B Ctrl-g | |
| 211 | 211 | Reset gamma correction. | |
| 212 | + | .TP | |
| 213 | + | .B [ | |
| 214 | + | Decrease brightness correction by | |
| 215 | + | .I count | |
| 216 | + | steps. | |
| 217 | + | .TP | |
| 218 | + | .B ] | |
| 219 | + | Increase brightness correction by | |
| 220 | + | .I count | |
| 221 | + | steps. | |
| 222 | + | .TP | |
| 223 | + | .B ( | |
| 224 | + | Decrease contrast by | |
| 225 | + | .I count | |
| 226 | + | steps. | |
| 227 | + | .TP | |
| 228 | + | .B ) | |
| 229 | + | Increase contrast by | |
| 230 | + | .I count | |
| 231 | + | steps. | |
| 212 | 232 | .SS Thumbnail mode | |
| 213 | 233 | The following keyboard commands are only available in thumbnail mode: | |
| 214 | 234 | .TP | |
| @@ -90,7 +90,9 @@ void img_init(img_t *img, win_t *win) | |||
| 90 | 90 | ||
| 91 | 91 | img->cmod = imlib_create_color_modifier(); | |
| 92 | 92 | imlib_context_set_color_modifier(img->cmod); | |
| 93 | − | img_change_gamma(img, options->gamma); | |
| 93 | + | img->brightness = 0; | |
| 94 | + | img->contrast = 0; | |
| 95 | + | img_change_color_modifier(img, options->gamma, &img->gamma); | |
| 94 | 96 | ||
| 95 | 97 | img->ss.on = options->slideshow > 0; | |
| 96 | 98 | img->ss.delay = options->slideshow > 0 ? options->slideshow : SLIDESHOW_DELAY * 10u; | |
| @@ -840,32 +842,36 @@ void img_toggle_antialias(img_t *img) | |||
| 840 | 842 | img->dirty = true; | |
| 841 | 843 | } | |
| 842 | 844 | ||
| 843 | − | bool img_change_gamma(img_t *img, int d) | |
| 845 | + | static double steps_to_range(int d, double max, double offset) | |
| 844 | 846 | { | |
| 845 | − | /* d < 0: decrease gamma | |
| 846 | − | * d = 0: reset gamma | |
| 847 | − | * d > 0: increase gamma | |
| 848 | − | */ | |
| 849 | − | int gamma; | |
| 850 | − | double range; | |
| 847 | + | return offset + d * ((d <= 0 ? 1.0 : (max - 1.0)) / CC_STEPS); | |
| 848 | + | } | |
| 851 | 849 | ||
| 852 | − | if (d == 0) | |
| 853 | − | gamma = 0; | |
| 854 | − | else | |
| 855 | − | gamma = MIN(MAX(img->gamma + d, -GAMMA_RANGE), GAMMA_RANGE); | |
| 850 | + | void img_update_color_modifiers(img_t *img) | |
| 851 | + | { | |
| 852 | + | assert(imlib_context_get_color_modifier() == img->cmod); | |
| 853 | + | imlib_reset_color_modifier(); | |
| 856 | 854 | ||
| 857 | − | if (img->gamma != gamma) { | |
| 858 | − | imlib_reset_color_modifier(); | |
| 859 | − | if (gamma) { | |
| 860 | − | range = gamma <= 0 ? 1.0 : GAMMA_MAX - 1.0; | |
| 861 | − | imlib_modify_color_modifier_gamma(1.0 + gamma * (range / GAMMA_RANGE)); | |
| 862 | − | } | |
| 863 | − | img->gamma = gamma; | |
| 864 | − | img->dirty = true; | |
| 865 | − | return true; | |
| 866 | − | } else { | |
| 855 | + | if (img->gamma != 0) | |
| 856 | + | imlib_modify_color_modifier_gamma(steps_to_range(img->gamma, GAMMA_MAX, 1.0)); | |
| 857 | + | if (img->brightness != 0) | |
| 858 | + | imlib_modify_color_modifier_brightness(steps_to_range(img->brightness, BRIGHTNESS_MAX, 0.0)); | |
| 859 | + | if (img->contrast != 0) | |
| 860 | + | imlib_modify_color_modifier_contrast(steps_to_range(img->contrast, CONTRAST_MAX, 1.0)); | |
| 861 | + | ||
| 862 | + | img->dirty = true; | |
| 863 | + | } | |
| 864 | + | ||
| 865 | + | bool img_change_color_modifier(img_t *img, int d, int *target) | |
| 866 | + | { | |
| 867 | + | int value = d == 0 ? 0 : MIN(MAX(*target + d, -CC_STEPS), CC_STEPS); | |
| 868 | + | ||
| 869 | + | if (*target == value) | |
| 867 | 870 | return false; | |
| 868 | − | } | |
| 871 | + | ||
| 872 | + | *target = value; | |
| 873 | + | img_update_color_modifiers(img); | |
| 874 | + | return true; | |
| 869 | 875 | } | |
| 870 | 876 | ||
| 871 | 877 | static bool img_frame_goto(img_t *img, int n) | |
| @@ -444,6 +444,10 @@ static void update_info(void) | |||
| 444 | 444 | } | |
| 445 | 445 | if (img.gamma) | |
| 446 | 446 | bar_put(r, "G%+d" BAR_SEP, img.gamma); | |
| 447 | + | if (img.brightness) | |
| 448 | + | bar_put(r, "B%+d" BAR_SEP, img.brightness); | |
| 449 | + | if (img.contrast) | |
| 450 | + | bar_put(r, "C%+d" BAR_SEP, img.contrast); | |
| 447 | 451 | bar_put(r, "%3d%%" BAR_SEP, (int) (img.zoom * 100.0)); | |
| 448 | 452 | if (img.multi.cnt > 0) { | |
| 449 | 453 | for (fn = 0, i = img.multi.cnt; i > 0; fn++, i /= 10); | |
| @@ -181,6 +181,8 @@ struct img { | |||
| 181 | 181 | ||
| 182 | 182 | Imlib_Color_Modifier cmod; | |
| 183 | 183 | int gamma; | |
| 184 | + | int brightness; | |
| 185 | + | int contrast; | |
| 184 | 186 | ||
| 185 | 187 | scalemode_t scalemode; | |
| 186 | 188 | float zoom; | |
| @@ -212,7 +214,8 @@ bool img_pan_edge(img_t*, direction_t); | |||
| 212 | 214 | void img_rotate(img_t*, degree_t); | |
| 213 | 215 | void img_flip(img_t*, flipdir_t); | |
| 214 | 216 | void img_toggle_antialias(img_t*); | |
| 215 | − | bool img_change_gamma(img_t*, int); | |
| 217 | + | void img_update_color_modifiers(img_t*); | |
| 218 | + | bool img_change_color_modifier(img_t*, int, int*); | |
| 216 | 219 | bool img_frame_navigate(img_t*, int); | |
| 217 | 220 | bool img_frame_animate(img_t*); | |
| 218 | 221 | Imlib_Image img_open(const fileinfo_t*); | |