diff --git a/kitty/cell_fragment.glsl b/kitty/cell_fragment.glsl index 113b3c8c2..88080f359 100644 --- a/kitty/cell_fragment.glsl +++ b/kitty/cell_fragment.glsl @@ -23,6 +23,7 @@ uniform sampler2DArray sprites; uniform int text_old_gamma; uniform float text_contrast; uniform float text_gamma_adjustment; +uniform float text_fg_override_threshold; in float effective_text_alpha; in vec3 sprite_pos; in vec3 underline_pos; @@ -130,6 +131,16 @@ float clamp_to_unit_float(float x) { vec4 foreground_contrast(vec4 over, vec3 under) { float underL = dot(under, Y); float overL = dot(over.rgb, Y); + + // If the difference in luminance is too small, + // force the foreground color to be black or white. + float diffL = abs(underL - overL); + if (0.5 < underL && diffL < text_fg_override_threshold) { + over.rgb = vec3(0, 0, 0); + } else if (underL < 0.5 && diffL < text_fg_override_threshold) { + over.rgb = vec3(1, 1, 1); + } + // 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. over.a = clamp_to_unit_float(mix(over.a, pow(over.a, text_gamma_adjustment), (1 - overL + underL) * text_gamma_scaling) * text_contrast); diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 435e1c7cc..5288309f9 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -249,7 +249,7 @@ the strokes are uneven. You can fine tune the actual contrast curve used for glyph composition by -specifying two space separated numbers for this setting. +specifying up to three space-separated numbers for this setting. 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. @@ -263,6 +263,11 @@ ranging from :code:`0` to :code:`100`. The default value is :code:`0` on Linux and :code:`30` on macOS. +The third number is an override threshold. 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`. + If you wish to achieve similar looking thickness in light and dark themes, a good way to experiment is start by setting the value to :code:`1.0 0` and use a dark theme. Then adjust the second parameter until it looks good. Then switch to a light theme diff --git a/kitty/options/to-c.h b/kitty/options/to-c.h index fce4c8820..bbdd31285 100644 --- a/kitty/options/to-c.h +++ b/kitty/options/to-c.h @@ -183,7 +183,7 @@ static void text_composition_strategy(PyObject *val, Options *opts) { if (!PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "text_rendering_strategy must be a string"); return; } opts->text_old_gamma = false; - opts->text_gamma_adjustment = 1.0f; opts->text_contrast = 0.f; + opts->text_gamma_adjustment = 1.0f; opts->text_contrast = 0.f; opts->text_fg_override_threshold = 0.f; if (PyUnicode_CompareWithASCIIString(val, "platform") == 0) { #ifdef __APPLE__ opts->text_gamma_adjustment = 1.7f; opts->text_contrast = 30.f; @@ -192,15 +192,29 @@ text_composition_strategy(PyObject *val, Options *opts) { else if (PyUnicode_CompareWithASCIIString(val, "legacy") == 0) { opts->text_old_gamma = true; } else { - DECREF_AFTER_FUNCTION PyObject *parts = PyUnicode_Split(val, NULL, 1); - if (PyList_GET_SIZE(parts) != 2) { PyErr_SetString(PyExc_ValueError, "text_rendering_strategy must be of the form number:number"); return; } - DECREF_AFTER_FUNCTION PyObject *ga = PyFloat_FromString(PyList_GET_ITEM(parts, 0)); - if (PyErr_Occurred()) return; - opts->text_gamma_adjustment = MAX(0.01f, PyFloat_AsFloat(ga)); - 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); + DECREF_AFTER_FUNCTION PyObject *parts = PyUnicode_Split(val, NULL, 2); + int size = PyList_GET_SIZE(parts); + if (size < 1 || 3 < size) { PyErr_SetString(PyExc_ValueError, "text_rendering_strategy must be of the form number:[number]:[number]"); return; } + + if (size > 0) { + DECREF_AFTER_FUNCTION PyObject *ga = PyFloat_FromString(PyList_GET_ITEM(parts, 0)); + if (PyErr_Occurred()) return; + opts->text_gamma_adjustment = MAX(0.01f, PyFloat_AsFloat(ga)); + } + + 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); + } + + if (size > 2) { + DECREF_AFTER_FUNCTION PyObject *text_fg_override_threshold = PyFloat_FromString(PyList_GET_ITEM(parts, 2)); + 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)); + } } } diff --git a/kitty/shaders.c b/kitty/shaders.c index d9b8005f2..5d77d6bb4 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -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); 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); + + 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 #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); diff --git a/kitty/state.h b/kitty/state.h index 454aa90d0..08a4557c7 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -48,7 +48,7 @@ typedef struct { WindowTitleIn macos_show_window_title_in; char *bell_path, *bell_theme; float background_opacity, dim_opacity; - float text_contrast, text_gamma_adjustment; + float text_contrast, text_gamma_adjustment, text_fg_override_threshold; bool text_old_gamma; char *background_image, *default_window_logo;