Add foreground override contrast threshold

This adds a parameter to text_composition_strategy that specifies
a percentage difference luminance below which the foreground
color will be overridden. The foreground color is set to white if
the background is dark or black if the background is light.

Many programs output colors that look good with the author's
terminal's color scheme but which are completely illegible with
other color schemes. This allows the user ensure that there is
always sufficient contrast to read the text on the screen.

Since we want existing configs to continue working, this also
makes it so that rather than taking exactly two parameters,
text_composition_strategy takes one--three parameters, using the
default values for those not specified.
This commit is contained in:
Luna McNulty 2023-05-18 11:06:00 -04:00
parent 9a0068e318
commit 05d94e8256
5 changed files with 45 additions and 12 deletions

View file

@ -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);

View file

@ -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

View file

@ -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));
}
}
}

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);
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);

View file

@ -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;