mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-06-27 03:11:46 +00:00
Merge branch 'text-fg-override-contrast-threshold' of https://github.com/lmcnulty/kitty
This commit is contained in:
commit
7dd06cf134
9 changed files with 81 additions and 10 deletions
|
|
@ -1,6 +1,7 @@
|
|||
#version GLSL_VERSION
|
||||
#define {WHICH_PROGRAM}
|
||||
#define NOT_TRANSPARENT
|
||||
#define NO_FG_OVERRIDE
|
||||
|
||||
#if defined(SIMPLE) || defined(BACKGROUND) || defined(SPECIAL)
|
||||
#define NEEDS_BACKROUND
|
||||
|
|
@ -23,6 +24,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 +132,19 @@ 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 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.
|
||||
// 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);
|
||||
|
|
|
|||
|
|
@ -249,7 +249,7 @@ light text on dark backgrounds thinner. It might also make some text appear like
|
|||
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 two 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.
|
||||
|
|
@ -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.
|
||||
''')
|
||||
|
||||
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() # }}}
|
||||
|
||||
|
||||
|
|
|
|||
3
kitty/options/parse.py
generated
3
kitty/options/parse.py
generated
|
|
@ -1284,6 +1284,9 @@ class Parser:
|
|||
def text_composition_strategy(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
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:
|
||||
ans['touch_scroll_multiplier'] = float(val)
|
||||
|
||||
|
|
|
|||
15
kitty/options/to-c-generated.h
generated
15
kitty/options/to-c-generated.h
generated
|
|
@ -70,6 +70,19 @@ convert_from_opts_text_composition_strategy(PyObject *py_opts, Options *opts) {
|
|||
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
|
||||
convert_from_python_cursor_shape(PyObject *val, Options *opts) {
|
||||
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;
|
||||
convert_from_opts_text_composition_strategy(py_opts, opts);
|
||||
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);
|
||||
if (PyErr_Occurred()) return false;
|
||||
convert_from_opts_cursor_beam_thickness(py_opts, opts);
|
||||
|
|
|
|||
|
|
@ -192,18 +192,37 @@ 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 || 2 < size) { PyErr_SetString(PyExc_ValueError, "text_rendering_strategy must be of the form 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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*
|
||||
list_of_chars(PyObject *chars) {
|
||||
if (!PyUnicode_Check(chars)) { PyErr_SetString(PyExc_TypeError, "list_of_chars must be a string"); return NULL; }
|
||||
|
|
|
|||
2
kitty/options/types.py
generated
2
kitty/options/types.py
generated
|
|
@ -445,6 +445,7 @@ option_names = ( # {{{
|
|||
'tab_title_template',
|
||||
'term',
|
||||
'text_composition_strategy',
|
||||
'text_fg_override_threshold',
|
||||
'touch_scroll_multiplier',
|
||||
'undercurl_style',
|
||||
'update_check_interval',
|
||||
|
|
@ -598,6 +599,7 @@ class Options:
|
|||
tab_title_template: str = '{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title}'
|
||||
term: str = 'xterm-kitty'
|
||||
text_composition_strategy: str = 'platform'
|
||||
text_fg_override_threshold: str = '0'
|
||||
touch_scroll_multiplier: float = 1.0
|
||||
undercurl_style: choices_for_undercurl_style = 'thin-sparse'
|
||||
update_check_interval: float = 24.0
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ typedef struct {
|
|||
char *bell_path, *bell_theme;
|
||||
float background_opacity, dim_opacity;
|
||||
float text_contrast, text_gamma_adjustment;
|
||||
float text_fg_override_threshold;
|
||||
bool text_old_gamma;
|
||||
|
||||
char *background_image, *default_window_logo;
|
||||
|
|
|
|||
|
|
@ -397,6 +397,8 @@ class LoadShaderPrograms:
|
|||
DECORATION_MASK=DECORATION_MASK,
|
||||
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:
|
||||
vv = vv.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT')
|
||||
ff = ff.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue