feat: additional contrast on text as a function of luminance difference

This commit is contained in:
Martin Wernstål 2023-02-01 17:29:28 +01:00
parent e64affe3f7
commit 3676e6651d
7 changed files with 120 additions and 1 deletions

View File

@ -20,6 +20,9 @@ in float bg_alpha;
#ifdef NEEDS_FOREGROUND
uniform sampler2DArray sprites;
uniform int text_old_gamma;
uniform float text_contrast;
uniform float text_gamma_adjustment;
in float effective_text_alpha;
in vec3 sprite_pos;
in vec3 underline_pos;
@ -100,6 +103,8 @@ vec4 vec4_premul(vec4 rgba) {
#ifdef NEEDS_FOREGROUND
// sRGB luminance values
const vec3 Y = vec3(0.2126, 0.7152, 0.0722);
// Scaling factor for the extra text-alpha adjustment for luminance-difference.
const float text_gamma_scaling = 0.5;
float linear2srgb(float x) {
// Approximation of linear-to-sRGB conversion
@ -116,6 +121,15 @@ float clamp(float x) {
return max(min(x, 1.0f), 0.0f);
}
vec4 foreground_contrast(vec4 over, vec3 under) {
float underL = dot(under, Y);
float overL = dot(over.rgb, Y);
// Apply additional gamma-adjustment scaled by the luminance difference, the darker the foreground the more adjustment we apply.
// A multiplicative contrast is also available to increase sasturation.
over.a = clamp(mix(over.a, pow(over.a, text_gamma_adjustment), (1 - overL + underL) * text_gamma_scaling) * text_contrast);
return over;
}
vec4 foreground_contrast_incorrect(vec4 over, vec3 under) {
// Simulation of gamma-incorrect blending
float underL = dot(under, Y);
@ -150,8 +164,10 @@ vec4 calculate_foreground() {
return foreground_with_decorations(text_fg);
}
vec4 calculate_foreground(vec3 bg) {
// When rendering on a background we can adjust the alpha channel contrast
// to improve legibility based on the source and destination colors
vec4 text_fg = foreground_color();
text_fg = foreground_contrast_incorrect(text_fg, bg);
text_fg = mix(foreground_contrast(text_fg, bg), foreground_contrast_incorrect(text_fg, bg), text_old_gamma);
return foreground_with_decorations(text_fg);
}

View File

@ -230,6 +230,41 @@ The style with which undercurls are rendered. This option takes the form
:code:`(thin|thick)-(sparse|dense)`. Thin and thick control the thickness of the
undercurl. Sparse and dense control how often the curl oscillates. With sparse
the curl will peak once per character, with dense twice.
'''
)
opt('text_old_gamma', 'no',
option_type='to_bool', ctype='bool',
long_text='''
If to simulate the old gamma-incorrect blending for the text alpha-channel, this
will make some text appear like the strokes are uneven. Dark text on bright backgrounds
will also look thicker while lighter text on darker backgrounds will look thinner.
'''
)
opt('text_gamma_adjustment', '1.0',
option_type='positive_float', ctype='float',
long_text='''
This setting adjusts the thickness of darker text on lighter backgrounds. Increasing the value
setting will make the text appear thicker while decreasing the value will make it thinner. It
can compensate for some fonts looking too-thin when using the gamma-correct alpha blending.
The result is scaled based on the luminance difference between the background and the foreground.
Dark text on light backgrounds receive the full impact of the curve while light text on dark
backgrounds are affected very little.
Range: >=0.01
MacOS: 1.7
'''
)
opt('text_contrast', '0',
option_type='positive_float', ctype='float',
long_text='''
Additional multiplicative text contrast as a percentage, will saturate and cause jagged edges if set too high.
Range: >=0.0
MacOS: 30
'''
)
egr() # }}}

View File

@ -1278,6 +1278,15 @@ class Parser:
def term(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
ans['term'] = str(val)
def text_contrast(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
ans['text_contrast'] = positive_float(val)
def text_gamma_adjustment(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
ans['text_gamma_adjustment'] = positive_float(val)
def text_old_gamma(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
ans['text_old_gamma'] = to_bool(val)
def touch_scroll_multiplier(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
ans['touch_scroll_multiplier'] = float(val)

View File

@ -57,6 +57,45 @@ convert_from_opts_modify_font(PyObject *py_opts, Options *opts) {
Py_DECREF(ret);
}
static void
convert_from_python_text_old_gamma(PyObject *val, Options *opts) {
opts->text_old_gamma = PyObject_IsTrue(val);
}
static void
convert_from_opts_text_old_gamma(PyObject *py_opts, Options *opts) {
PyObject *ret = PyObject_GetAttrString(py_opts, "text_old_gamma");
if (ret == NULL) return;
convert_from_python_text_old_gamma(ret, opts);
Py_DECREF(ret);
}
static void
convert_from_python_text_gamma_adjustment(PyObject *val, Options *opts) {
opts->text_gamma_adjustment = PyFloat_AsFloat(val);
}
static void
convert_from_opts_text_gamma_adjustment(PyObject *py_opts, Options *opts) {
PyObject *ret = PyObject_GetAttrString(py_opts, "text_gamma_adjustment");
if (ret == NULL) return;
convert_from_python_text_gamma_adjustment(ret, opts);
Py_DECREF(ret);
}
static void
convert_from_python_text_contrast(PyObject *val, Options *opts) {
opts->text_contrast = PyFloat_AsFloat(val);
}
static void
convert_from_opts_text_contrast(PyObject *py_opts, Options *opts) {
PyObject *ret = PyObject_GetAttrString(py_opts, "text_contrast");
if (ret == NULL) return;
convert_from_python_text_contrast(ret, opts);
Py_DECREF(ret);
}
static void
convert_from_python_cursor_shape(PyObject *val, Options *opts) {
opts->cursor_shape = PyLong_AsLong(val);
@ -1042,6 +1081,12 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) {
if (PyErr_Occurred()) return false;
convert_from_opts_modify_font(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_text_old_gamma(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_text_gamma_adjustment(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_text_contrast(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_cursor_shape(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_cursor_beam_thickness(py_opts, opts);

View File

@ -443,6 +443,9 @@ option_names = ( # {{{
'tab_title_max_length',
'tab_title_template',
'term',
'text_contrast',
'text_gamma_adjustment',
'text_old_gamma',
'touch_scroll_multiplier',
'undercurl_style',
'update_check_interval',
@ -594,6 +597,9 @@ class Options:
tab_title_max_length: int = 0
tab_title_template: str = '{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title}'
term: str = 'xterm-kitty'
text_contrast: float = 0
text_gamma_adjustment: float = 1.0
text_old_gamma: bool = False
touch_scroll_multiplier: float = 1.0
undercurl_style: choices_for_undercurl_style = 'thin-sparse'
update_check_interval: float = 24.0

View File

@ -569,6 +569,12 @@ set_cell_uniforms(float current_inactive_text_alpha, bool force) {
S(CELL_PROGRAM, sprites, SPRITE_MAP_UNIT, 1i); S(CELL_FG_PROGRAM, sprites, SPRITE_MAP_UNIT, 1i);
S(CELL_PROGRAM, dim_opacity, OPT(dim_opacity), 1f); S(CELL_FG_PROGRAM, dim_opacity, OPT(dim_opacity), 1f);
S(CELL_BG_PROGRAM, defaultbg, OPT(background), 1f);
int text_old_gamma = OPT(text_old_gamma) ? 1 : 0;
S(CELL_PROGRAM, text_old_gamma, text_old_gamma, 1i); S(CELL_FG_PROGRAM, text_old_gamma, text_old_gamma, 1i);
float text_contrast = 1.0f + OPT(text_contrast) * 0.01f;
S(CELL_PROGRAM, text_contrast, text_contrast, 1f); S(CELL_FG_PROGRAM, text_contrast, text_contrast, 1f);
float text_gamma_adjustment = OPT(text_gamma_adjustment) < 0.01f ? 1.0f : 1.0f / OPT(text_gamma_adjustment);
S(CELL_PROGRAM, text_gamma_adjustment, text_gamma_adjustment, 1f); S(CELL_FG_PROGRAM, text_gamma_adjustment, text_gamma_adjustment, 1f);
#undef S
#define SV(prog, name, num, val, type) { bind_program(prog); glUniform##type(glGetUniformLocation(program_id(prog), #name), num, val); }
SV(CELL_PROGRAM, gamma_lut, 256, srgb_lut, 1fv); SV(CELL_FG_PROGRAM, gamma_lut, 256, srgb_lut, 1fv);

View File

@ -47,6 +47,8 @@ typedef struct {
WindowTitleIn macos_show_window_title_in;
char *bell_path;
float background_opacity, dim_opacity;
float text_contrast, text_gamma_adjustment;
bool text_old_gamma;
char *background_image, *default_window_logo;
BackgroundImageLayout background_image_layout;