Merge branch 'text-fg-override-contrast-threshold' of https://github.com/lmcnulty/kitty

This commit is contained in:
Kovid Goyal
2023-05-24 12:56:29 +05:30
9 changed files with 81 additions and 10 deletions

View File

@@ -1,6 +1,7 @@
#version GLSL_VERSION #version GLSL_VERSION
#define {WHICH_PROGRAM} #define {WHICH_PROGRAM}
#define NOT_TRANSPARENT #define NOT_TRANSPARENT
#define NO_FG_OVERRIDE
#if defined(SIMPLE) || defined(BACKGROUND) || defined(SPECIAL) #if defined(SIMPLE) || defined(BACKGROUND) || defined(SPECIAL)
#define NEEDS_BACKROUND #define NEEDS_BACKROUND
@@ -23,6 +24,7 @@ uniform sampler2DArray sprites;
uniform int text_old_gamma; uniform int text_old_gamma;
uniform float text_contrast; uniform float text_contrast;
uniform float text_gamma_adjustment; uniform float text_gamma_adjustment;
uniform float text_fg_override_threshold;
in float effective_text_alpha; in float effective_text_alpha;
in vec3 sprite_pos; in vec3 sprite_pos;
in vec3 underline_pos; in vec3 underline_pos;
@@ -130,6 +132,19 @@ float clamp_to_unit_float(float x) {
vec4 foreground_contrast(vec4 over, vec3 under) { vec4 foreground_contrast(vec4 over, vec3 under) {
float underL = dot(under, Y); float underL = dot(under, Y);
float overL = dot(over.rgb, Y); float overL = dot(over.rgb, Y);
#if defined(FG_OVERRIDE)
// If the difference in luminance is too small,
// force the foreground color to be black or white.
float diffL = abs(underL - overL);
float overrideLvl = (1.f - colored_sprite) * step(diffL, text_fg_override_threshold);
float originalLvl = 1.f - overrideLvl;
over.rgb = (
originalLvl * over.rgb +
overrideLvl * vec3(step(underL, 0.5f))
);
#endif
// Apply additional gamma-adjustment scaled by the luminance difference, the darker the foreground the more adjustment we apply. // 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 saturation. // A multiplicative contrast is also available to increase saturation.
over.a = clamp_to_unit_float(mix(over.a, pow(over.a, text_gamma_adjustment), (1 - overL + underL) * text_gamma_scaling) * text_contrast); over.a = clamp_to_unit_float(mix(over.a, pow(over.a, text_gamma_adjustment), (1 - overL + underL) * text_gamma_scaling) * text_contrast);

View File

@@ -249,7 +249,7 @@ light text on dark backgrounds thinner. It might also make some text appear like
the strokes are uneven. the strokes are uneven.
You can fine tune the actual contrast curve used for glyph composition by You can fine tune the actual contrast curve used for glyph composition by
specifying two space separated numbers for this setting. specifying up to two space-separated numbers for this setting.
The first number is the gamma adjustment, which controls the thickness of dark The first number is the gamma adjustment, which controls the thickness of dark
text on light backgrounds. Increasing the value will make text appear thicker. text on light backgrounds. Increasing the value will make text appear thicker.
@@ -269,6 +269,17 @@ Then adjust the second parameter until it looks good. Then switch to a light the
and adjust the first parameter until the perceived thickness matches the dark theme. and adjust the first parameter until the perceived thickness matches the dark theme.
''') ''')
opt('text_fg_override_threshold', 0,
ctype='!text_fg_override_threshold',
long_text='''
The minimum accepted difference in luminance between the foreground and background
color, below which kitty will override the foreground color. It is percentage
ranging from :code:`0` to :code:`100`. If the difference in luminance of the
foreground and background is below this threshold, the foreground color will be set
to white if the background is dark or black if the background is light. The default
value is :code:`0`.
''')
egr() # }}} egr() # }}}

View File

@@ -1284,6 +1284,9 @@ class Parser:
def text_composition_strategy(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: def text_composition_strategy(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
ans['text_composition_strategy'] = str(val) ans['text_composition_strategy'] = str(val)
def text_fg_override_threshold(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
ans['text_fg_override_threshold'] = str(val)
def touch_scroll_multiplier(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: def touch_scroll_multiplier(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
ans['touch_scroll_multiplier'] = float(val) ans['touch_scroll_multiplier'] = float(val)

View File

@@ -70,6 +70,19 @@ convert_from_opts_text_composition_strategy(PyObject *py_opts, Options *opts) {
Py_DECREF(ret); Py_DECREF(ret);
} }
static void
convert_from_python_text_fg_override_threshold(PyObject *val, Options *opts) {
text_fg_override_threshold(val, opts);
}
static void
convert_from_opts_text_fg_override_threshold(PyObject *py_opts, Options *opts) {
PyObject *ret = PyObject_GetAttrString(py_opts, "text_fg_override_threshold");
if (ret == NULL) return;
convert_from_python_text_fg_override_threshold(ret, opts);
Py_DECREF(ret);
}
static void static void
convert_from_python_cursor_shape(PyObject *val, Options *opts) { convert_from_python_cursor_shape(PyObject *val, Options *opts) {
opts->cursor_shape = PyLong_AsLong(val); opts->cursor_shape = PyLong_AsLong(val);
@@ -1070,6 +1083,8 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) {
if (PyErr_Occurred()) return false; if (PyErr_Occurred()) return false;
convert_from_opts_text_composition_strategy(py_opts, opts); convert_from_opts_text_composition_strategy(py_opts, opts);
if (PyErr_Occurred()) return false; if (PyErr_Occurred()) return false;
convert_from_opts_text_fg_override_threshold(py_opts, opts);
if (PyErr_Occurred()) return false;
convert_from_opts_cursor_shape(py_opts, opts); convert_from_opts_cursor_shape(py_opts, opts);
if (PyErr_Occurred()) return false; if (PyErr_Occurred()) return false;
convert_from_opts_cursor_beam_thickness(py_opts, opts); convert_from_opts_cursor_beam_thickness(py_opts, opts);

View File

@@ -192,18 +192,37 @@ text_composition_strategy(PyObject *val, Options *opts) {
else if (PyUnicode_CompareWithASCIIString(val, "legacy") == 0) { else if (PyUnicode_CompareWithASCIIString(val, "legacy") == 0) {
opts->text_old_gamma = true; opts->text_old_gamma = true;
} else { } else {
DECREF_AFTER_FUNCTION PyObject *parts = PyUnicode_Split(val, NULL, 1); DECREF_AFTER_FUNCTION PyObject *parts = PyUnicode_Split(val, NULL, 2);
if (PyList_GET_SIZE(parts) != 2) { PyErr_SetString(PyExc_ValueError, "text_rendering_strategy must be of the form number:number"); return; } int size = PyList_GET_SIZE(parts);
DECREF_AFTER_FUNCTION PyObject *ga = PyFloat_FromString(PyList_GET_ITEM(parts, 0)); if (size < 1 || 2 < size) { PyErr_SetString(PyExc_ValueError, "text_rendering_strategy must be of the form number:[number]"); return; }
if (PyErr_Occurred()) return;
opts->text_gamma_adjustment = MAX(0.01f, PyFloat_AsFloat(ga)); if (size > 0) {
DECREF_AFTER_FUNCTION PyObject *contrast = PyFloat_FromString(PyList_GET_ITEM(parts, 1)); DECREF_AFTER_FUNCTION PyObject *ga = PyFloat_FromString(PyList_GET_ITEM(parts, 0));
if (PyErr_Occurred()) return; if (PyErr_Occurred()) return;
opts->text_contrast = MAX(0.0f, PyFloat_AsFloat(contrast)); opts->text_gamma_adjustment = MAX(0.01f, PyFloat_AsFloat(ga));
opts->text_contrast = MIN(100.0f, opts->text_contrast); }
if (size > 1) {
DECREF_AFTER_FUNCTION PyObject *contrast = PyFloat_FromString(PyList_GET_ITEM(parts, 1));
if (PyErr_Occurred()) return;
opts->text_contrast = MAX(0.0f, PyFloat_AsFloat(contrast));
opts->text_contrast = MIN(100.0f, opts->text_contrast);
}
} }
} }
static void
text_fg_override_threshold(PyObject *val, Options *opts) {
if (!PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "text_fg_override_threshold must be a string"); return; }
opts->text_fg_override_threshold = 0.f;
DECREF_AFTER_FUNCTION PyObject *text_fg_override_threshold = PyFloat_FromString(val);
if (PyErr_Occurred()) return;
opts->text_fg_override_threshold = MAX(0.f, PyFloat_AsFloat(text_fg_override_threshold));
opts->text_fg_override_threshold = MIN(100.f, PyFloat_AsFloat(text_fg_override_threshold));
}
static char_type* static char_type*
list_of_chars(PyObject *chars) { list_of_chars(PyObject *chars) {
if (!PyUnicode_Check(chars)) { PyErr_SetString(PyExc_TypeError, "list_of_chars must be a string"); return NULL; } if (!PyUnicode_Check(chars)) { PyErr_SetString(PyExc_TypeError, "list_of_chars must be a string"); return NULL; }

View File

@@ -445,6 +445,7 @@ option_names = ( # {{{
'tab_title_template', 'tab_title_template',
'term', 'term',
'text_composition_strategy', 'text_composition_strategy',
'text_fg_override_threshold',
'touch_scroll_multiplier', 'touch_scroll_multiplier',
'undercurl_style', 'undercurl_style',
'update_check_interval', 'update_check_interval',
@@ -598,6 +599,7 @@ class Options:
tab_title_template: str = '{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title}' tab_title_template: str = '{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title}'
term: str = 'xterm-kitty' term: str = 'xterm-kitty'
text_composition_strategy: str = 'platform' text_composition_strategy: str = 'platform'
text_fg_override_threshold: str = '0'
touch_scroll_multiplier: float = 1.0 touch_scroll_multiplier: float = 1.0
undercurl_style: choices_for_undercurl_style = 'thin-sparse' undercurl_style: choices_for_undercurl_style = 'thin-sparse'
update_check_interval: float = 24.0 update_check_interval: float = 24.0

View File

@@ -576,6 +576,9 @@ set_cell_uniforms(float current_inactive_text_alpha, bool force) {
S(CELL_PROGRAM, text_contrast, text_contrast, 1f); S(CELL_FG_PROGRAM, text_contrast, text_contrast, 1f); 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); 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); S(CELL_PROGRAM, text_gamma_adjustment, text_gamma_adjustment, 1f); S(CELL_FG_PROGRAM, text_gamma_adjustment, text_gamma_adjustment, 1f);
float text_fg_override_threshold = OPT(text_fg_override_threshold) * 0.01f;
S(CELL_PROGRAM, text_fg_override_threshold, text_fg_override_threshold, 1f); S(CELL_FG_PROGRAM, text_fg_override_threshold, text_fg_override_threshold, 1f);
#undef S #undef S
#define SV(prog, name, num, val, type) { bind_program(prog); glUniform##type(glGetUniformLocation(program_id(prog), #name), num, val); } #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); SV(CELL_PROGRAM, gamma_lut, 256, srgb_lut, 1fv); SV(CELL_FG_PROGRAM, gamma_lut, 256, srgb_lut, 1fv);

View File

@@ -49,6 +49,7 @@ typedef struct {
char *bell_path, *bell_theme; char *bell_path, *bell_theme;
float background_opacity, dim_opacity; float background_opacity, dim_opacity;
float text_contrast, text_gamma_adjustment; float text_contrast, text_gamma_adjustment;
float text_fg_override_threshold;
bool text_old_gamma; bool text_old_gamma;
char *background_image, *default_window_logo; char *background_image, *default_window_logo;

View File

@@ -397,6 +397,8 @@ class LoadShaderPrograms:
DECORATION_MASK=DECORATION_MASK, DECORATION_MASK=DECORATION_MASK,
STRIKE_SPRITE_INDEX=NUM_UNDERLINE_STYLES + 1, STRIKE_SPRITE_INDEX=NUM_UNDERLINE_STYLES + 1,
) )
if get_options().text_fg_override_threshold != '0':
ff = ff.replace('#define NO_FG_OVERRIDE', '#define FG_OVERRIDE')
if semi_transparent: if semi_transparent:
vv = vv.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT') vv = vv.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT')
ff = ff.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT') ff = ff.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT')