diff options
| author | Ziv Scully <zivscully@gmail.com> | 2025-12-28 22:46:31 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-12-29 09:16:31 +0530 |
| commit | 8623dc3e38a2952fa87c1f6972b88d58e22f4c99 (patch) | |
| tree | 79caecd95706d956f5883ba54607d9ef13ff5f1b | |
| parent | 0cca3b6925da8e1e128259f6c63250cc99c96cb2 (diff) | |
feat(midnight-mode): add gamma correction and fix RGB bugs
Add gamma correction options for better contrast control in midnight mode:
- pdf-view-midnight-gamma: nonlinearly scale lightness (default 1.0)
- pdf-view-midnight-gamma-before-invert: control gamma application order
Bug fixes:
- Fix RGB normalization (255 not 256) - was causing incorrect background color
- Add missing black→foreground fast path (comment existed but code was missing)
Closes: #329
Author: @vizziv
| -rw-r--r-- | lisp/pdf-info.el | 4 | ||||
| -rw-r--r-- | lisp/pdf-view.el | 25 | ||||
| -rw-r--r-- | server/epdfinfo.c | 55 | ||||
| -rw-r--r-- | server/epdfinfo.h | 4 |
4 files changed, 77 insertions, 11 deletions
diff --git a/lisp/pdf-info.el b/lisp/pdf-info.el index db1dc7e..47d308d 100644 --- a/lisp/pdf-info.el +++ b/lisp/pdf-info.el @@ -572,6 +572,8 @@ interrupted." (cl-case key ((:render/printed) (setq value (equal value "1"))) + ((:render/gammabeforeinvert) + (setq value (equal value "1"))) ((:render/usecolors) (setq value (ignore-errors (let ((int-val (cl-parse-integer value))) @@ -1734,6 +1736,8 @@ Returns a list \(LEFT TOP RIGHT BOT\)." soptions)) ((:render/printed) (push (if value 1 0) soptions)) + ((:render/gammabeforeinvert) + (push (if value 1 0) soptions)) ((:render/usecolors) ;; 0 -> original color ;; 1 -> recolor document to grayscale mapping black to diff --git a/lisp/pdf-view.el b/lisp/pdf-view.el index e94762b..dc2e0be 100644 --- a/lisp/pdf-view.el +++ b/lisp/pdf-view.el @@ -133,6 +133,29 @@ Nevertheless, this seems to work well in most cases." :group 'pdf-view :type 'boolean) +(defcustom pdf-view-midnight-gamma 1.0 + "In midnight mode, nonlinearly scale lightness. + +Values less than 1 increase lightness, values more than 1 decrease +lightness (unless `pdf-view-midnight-gamma-before-invert' is non-nil, in +which reverses the effect direction)." + :group 'pdf-view + :type 'float) + +(defcustom pdf-view-midnight-gamma-before-invert nil + "In midnight mode, whether to scale lightness before inverting. + +If non-nil, this inverts the direction of the effect of +`pdf-view-midnight-gamma', i.e. values more than 1 increase lightness +instead of decreasing it. This option is provided because it results in +different behaviors near the ends of the lightness scale. For example, +if this option is nil (the default), then a gamma values less than 1 +significantly lighten colors very close to black. One gets a less +extreme effect by setting this option to non-nil and using gamma values +greater than 1." + :group 'pdf-view + :type 'boolean) + (defcustom pdf-view-change-page-hook nil "Hook run after changing to another page, but before displaying it. @@ -1339,6 +1362,8 @@ The colors are determined by the variable (pdf-info-setoptions :render/foreground (or (car pdf-view-midnight-colors) "black") :render/background (or (cdr pdf-view-midnight-colors) "white") + :render/gamma pdf-view-midnight-gamma + :render/gammabeforeinvert pdf-view-midnight-gamma-before-invert :render/usecolors (if pdf-view-midnight-invert ;; If midnight invert is enabled, pass "2" indicating diff --git a/server/epdfinfo.c b/server/epdfinfo.c index 29793d6..6ad59c6 100644 --- a/server/epdfinfo.c +++ b/server/epdfinfo.c @@ -483,7 +483,8 @@ static inline gboolean color_equal(struct color a, struct color b) static void image_recolor (cairo_surface_t * surface, const PopplerColor * fg, - const PopplerColor * bg, int usecolors) + const PopplerColor * bg, int usecolors, + double gamma, int gammabeforeinvert) { /* Performs one of two kinds of image recoloring depending on the value of usecolors: @@ -543,9 +544,9 @@ image_recolor (cairo_surface_t * surface, const PopplerColor * fg, { /* Careful. data color components blue, green, red. */ struct color rgb = { - .r = (double) data[2] / 256., - .g = (double) data[1] / 256., - .b = (double) data[0] / 256. + .r = (double) data[2] / 255., + .g = (double) data[1] / 255., + .b = (double) data[0] / 255. }; /* Linear interpolation between bg and fg based on the @@ -571,6 +572,7 @@ image_recolor (cairo_surface_t * surface, const PopplerColor * fg, white->background and black->foreground and have a single entry cache to speed up computation */ const struct color white = {.r = 1.0, .g = 1.0, .b = 1.0}; + const struct color black = {.r = 0.0, .g = 0.0, .b = 0.0}; struct color precomputed_rgb = white; struct color precomputed_inv_rgb = rgb_bg; @@ -578,8 +580,6 @@ image_recolor (cairo_surface_t * surface, const PopplerColor * fg, struct color oklab_fg = rgb2oklab(rgb_fg); struct color oklab_bg = rgb2oklab(rgb_bg); - const double oklab_diff_l = oklab_fg.l - oklab_bg.l; - unsigned int y; for (y = 0; y < page_height * rowstride; y += rowstride) { @@ -590,9 +590,9 @@ image_recolor (cairo_surface_t * surface, const PopplerColor * fg, { /* Careful. data color components blue, green, red. */ struct color rgb = { - .r = (double) data[2] / 256., - .g = (double) data[1] / 256., - .b = (double) data[0] / 256. + .r = (double) data[2] / 255., + .g = (double) data[1] / 255., + .b = (double) data[0] / 255. }; /* Convert to Oklab coordinates, invert perceived lightness, @@ -601,6 +601,10 @@ image_recolor (cairo_surface_t * surface, const PopplerColor * fg, { rgb = rgb_bg; } + else if (color_equal(black, rgb)) + { + rgb = rgb_fg; + } else if (color_equal(precomputed_rgb, rgb)) { rgb = precomputed_inv_rgb; @@ -613,7 +617,20 @@ image_recolor (cairo_surface_t * surface, const PopplerColor * fg, /* Invert the perceived lightness, and scales it */ double l = oklab.l; double inv_l = 1.0 - l; - oklab.l = oklab_bg.l + oklab_diff_l * inv_l; + + /* Nonlinearly scale lightness */ + if (gammabeforeinvert) + { + l = pow(l, gamma); + inv_l = 1.0 - l; + } + else + { + inv_l = pow(inv_l, gamma); + l = 1.0 - inv_l; + } + + oklab.l = oklab_bg.l * l + oklab_fg.l * inv_l; /* Have a and b parameters (which encode hue and saturation) start at the background value and interpolate up to @@ -706,7 +723,8 @@ image_render_page(PopplerDocument *pdf, PopplerPage *page, cairo_paint (cr); if (options && (options->usecolors)) - image_recolor (surface, &options->fg, &options->bg, options->usecolors); + image_recolor (surface, &options->fg, &options->bg, options->usecolors, + options->gamma, options->gammabeforeinvert); cairo_destroy (cr); @@ -1119,6 +1137,15 @@ command_arg_parse_arg (const epdfinfo_t *ctx, const char *arg, error_msg, "Expected 0 or 1:%s", arg); cmd_arg->value.flag = *arg == '1'; break; + case ARG_DOUBLE: + { + char *endptr; + double n = strtod (arg, &endptr); + cerror_if_not (! (*endptr), + error_msg, "Expected double (floating point): %s", arg); + cmd_arg->value.scalar = n; + } + break; case ARG_NONEMPTY_STRING: cerror_if_not (*arg, error_msg, "Non-empty string expected"); /* fall through */ @@ -1260,6 +1287,9 @@ command_arg_print(const command_arg_t *arg) case ARG_BOOL: printf ("%d", arg->value.flag ? 1 : 0); break; + case ARG_DOUBLE: + printf ("%f", arg->value.scalar); + break; case ARG_NONEMPTY_STRING: /* fall */ case ARG_STRING: print_response_string (arg->value.string, NONE); @@ -1311,6 +1341,7 @@ command_arg_type_size(command_arg_type_t type) case ARG_INVALID: return 0; case ARG_DOC: return sizeof (arg.value.doc); case ARG_BOOL: return sizeof (arg.value.flag); + case ARG_DOUBLE: return sizeof (arg.value.scalar); case ARG_NONEMPTY_STRING: /* fall */ case ARG_STRING: return sizeof (arg.value.string); case ARG_NATNUM: return sizeof (arg.value.natnum); @@ -3667,6 +3698,8 @@ const document_option_t document_options [] = DEC_DOPT (":render/printed", ARG_BOOL, render.printed), DEC_DOPT (":render/foreground", ARG_COLOR, render.fg), DEC_DOPT (":render/background", ARG_COLOR, render.bg), + DEC_DOPT (":render/gamma", ARG_DOUBLE, render.gamma), + DEC_DOPT (":render/gammabeforeinvert", ARG_BOOL, render.gammabeforeinvert), }; const command_arg_type_t cmd_getoptions_spec[] = diff --git a/server/epdfinfo.h b/server/epdfinfo.h index 542b577..ed6abd1 100644 --- a/server/epdfinfo.h +++ b/server/epdfinfo.h @@ -165,6 +165,7 @@ typedef enum ARG_INVALID = 0, ARG_DOC, ARG_BOOL, + ARG_DOUBLE, ARG_STRING, ARG_NONEMPTY_STRING, ARG_NATNUM, @@ -188,6 +189,8 @@ typedef struct PopplerColor bg, fg; gboolean usecolors; gboolean printed; + gboolean gammabeforeinvert; + gdouble gamma; } render_options_t; typedef struct @@ -214,6 +217,7 @@ typedef struct union { gboolean flag; + gdouble scalar; const char *string; long natnum; document_t *doc; |
