Backport using uniform for text_fg_override_threshold from slang to legacy glsl

This commit is contained in:
Kovid Goyal 2026-07-03 12:46:20 +05:30
parent 47a238e4b3
commit 5fc2cf4230
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
9 changed files with 40 additions and 27 deletions

View file

@ -1,4 +1,3 @@
#define FG_OVERRIDE_THRESHOLD {FG_OVERRIDE_THRESHOLD}
#define FG_OVERRIDE_ALGO {FG_OVERRIDE_ALGO}
#define TEXT_NEW_GAMMA {TEXT_NEW_GAMMA}

View file

@ -25,6 +25,7 @@ uniform float gamma_lut[256];
uniform uint draw_bg_bitfield;
uniform usampler2D sprite_decorations_map;
uniform float row_offset;
uniform float fg_override_threshold;
// Have to use fixed locations here as all variants of the cell program share the same VAOs
layout(location=0) in uvec3 colors;
@ -268,7 +269,7 @@ vec3 fg_override(float under_luminance, float over_lumininace, vec3 under, vec3
// If the difference in luminance is too small,
// force the foreground color to be black or white.
float diff_luminance = abs(under_luminance - over_lumininace);
float override_level = (1.f - colored_sprite) * step(diff_luminance, FG_OVERRIDE_THRESHOLD);
float override_level = (1.f - colored_sprite) * step(diff_luminance, fg_override_threshold);
float original_level = 1.f - override_level;
return original_level * over + override_level * vec3(step(under_luminance, 0.5f));
}
@ -279,7 +280,7 @@ vec3 fg_override(float under_luminance, float over_luminance, vec3 under, vec3 o
float ratio = contrast_ratio(under_luminance, over_luminance);
vec3 diff = abs(under - over);
vec3 over_hsluv = rgbToHsluv(over);
const float min_contrast_ratio = FG_OVERRIDE_THRESHOLD;
const float min_contrast_ratio = fg_override_threshold;
float target_lum_a = clamp((under_luminance + 0.05f) * min_contrast_ratio - 0.05f, 0.f, 1.f);
float target_lum_b = clamp((under_luminance + 0.05f) / min_contrast_ratio - 0.05f, 0.f, 1.f);
vec3 result_a = clamp(hsluvToRgb(vec3(over_hsluv.x, over_hsluv.y, target_lum_a * 100.f)), 0.f, 1.f);

View file

@ -267,7 +267,7 @@ 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', option_type='text_fg_override_threshold', long_text='''
opt('text_fg_override_threshold', '0', option_type='text_fg_override_threshold', ctype='text_fg_override_threshold', long_text='''
A setting to prevent low contrast between foreground and background colors.
Useful when working with applications that use colors that do not contrast
well with your preferred color scheme. The default value is :code:`0`, which means no color overriding is performed.

View file

@ -122,6 +122,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) {
opts->text_fg_override_threshold = text_fg_override_threshold(val);
}
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);
@ -1520,6 +1533,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_shape_unfocused(py_opts, opts);

View file

@ -45,6 +45,16 @@ parse_ms_long_to_monotonic_t(PyObject *val) {
return ms_to_monotonic_t(PyLong_AsUnsignedLong(val));
}
static inline float
text_fg_override_threshold(PyObject *val) {
double ans = PyFloat_AsDouble(PyTuple_GET_ITEM(val, 0));
switch (PyUnicode_AsUTF8(PyTuple_GET_ITEM(val, 1))[0]) {
case '%': ans = MAX(0, MIN(ans, 100)) * 0.01; break;
default: ans = MAX(0, MIN(ans, 21)); break;
}
return (float)ans;
}
static inline WindowTitleIn
window_title_in(PyObject *title_in) {
const char *in = PyUnicode_AsUTF8(title_in);

View file

@ -681,7 +681,7 @@ class Options:
tab_separator: str = ''
tab_switch_strategy: choices_for_tab_switch_strategy = 'previous'
tab_title_max_length: int = 0
tab_title_template: str = '{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{tab.last_focused_progress_percent}{title}'
tab_title_template: str = '{fmt.fg.red}{bell_symbol}{activity_symbol}{secure_input_symbol}{fmt.fg.tab}{tab.last_focused_progress_percent}{title}'
term: str = 'xterm-kitty'
terminfo_type: choices_for_terminfo_type = 'path'
text_composition_strategy: str = 'platform'

View file

@ -1336,6 +1336,7 @@ call_cell_program(int program, const UIRenderData *ui, ssize_t vao_idx, bool for
bind_vao_uniform_buffer(vao_idx, color_table_buffer, COLOR_TABLE_BINDING_POINT);
glUniform1ui(cell_program_layouts[program].uniforms.draw_bg_bitfield, draw_bg_bitfield);
glUniform1f(cell_program_layouts[program].uniforms.row_offset, row_offset_for_screen(ui->screen));
glUniform1f(cell_program_layouts[program].uniforms.fg_override_threshold, OPT(text_fg_override_threshold));
if (for_final_output) glEnable(GL_FRAMEBUFFER_SRGB);
draw_quad(!for_final_output, render_lines_for_screen(ui->screen) * ui->screen->columns);
if (for_final_output) glDisable(GL_FRAMEBUFFER_SRGB);

View file

@ -5,7 +5,7 @@ import re
from collections.abc import Callable, Iterator
from functools import lru_cache, partial
from itertools import count
from typing import Any, Literal, NamedTuple, Optional
from typing import Any, Literal, Optional
from kitty.constants import read_kitty_resource
from kitty.fast_data_types import (
@ -136,14 +136,8 @@ class MultiReplacer:
null_replacer = MultiReplacer()
class TextFgOverrideThreshold(NamedTuple):
value: float = 0
unit: Literal['%', 'ratio'] = '%'
scaled_value: float = 0
class LoadShaderPrograms:
text_fg_override_threshold: TextFgOverrideThreshold = TextFgOverrideThreshold()
text_fg_override_threshold: tuple[float, Literal['%', 'ratio']] = 0, '%'
text_old_gamma: bool = False
cell_program_replacer: MultiReplacer = null_replacer
@ -151,8 +145,9 @@ class LoadShaderPrograms:
def needs_recompile(self) -> bool:
opts = get_options()
return (
opts.text_fg_override_threshold != (self.text_fg_override_threshold.value, self.text_fg_override_threshold.unit)
or (opts.text_composition_strategy == 'legacy') != self.text_old_gamma
bool(opts.text_fg_override_threshold[0]) != bool(self.text_fg_override_threshold[0]) or
opts.text_fg_override_threshold[1] != self.text_fg_override_threshold[1] or
(opts.text_composition_strategy == 'legacy') != self.text_old_gamma
)
def recompile_if_needed(self) -> None:
@ -162,15 +157,7 @@ class LoadShaderPrograms:
def __call__(self, allow_recompile: bool = False) -> None:
opts = get_options()
self.text_old_gamma = opts.text_composition_strategy == 'legacy'
text_fg_override_threshold: float = opts.text_fg_override_threshold[0]
match opts.text_fg_override_threshold[1]:
case '%':
text_fg_override_threshold = max(0, min(text_fg_override_threshold, 100.0)) * 0.01
case 'ratio':
text_fg_override_threshold = max(0, min(text_fg_override_threshold, 21.0))
self.text_fg_override_threshold = TextFgOverrideThreshold(
opts.text_fg_override_threshold[0], opts.text_fg_override_threshold[1], text_fg_override_threshold)
self.text_fg_override_threshold = opts.text_fg_override_threshold
cell = program_for('cell')
if self.cell_program_replacer is null_replacer:
@ -190,14 +177,13 @@ class LoadShaderPrograms:
)
def resolve_cell_defines(only_fg: int, only_bg: int, src: str) -> str:
algo = '1' if self.text_fg_override_threshold.unit == '%' else '2'
if not self.text_fg_override_threshold.scaled_value:
algo = '1' if self.text_fg_override_threshold[1] == '%' else '2'
if not self.text_fg_override_threshold[0]:
algo = '0'
r = self.cell_program_replacer.replacements
r['ONLY_FOREGROUND'] = str(only_fg)
r['ONLY_BACKGROUND'] = str(only_bg)
r['FG_OVERRIDE_ALGO'] = algo
r['FG_OVERRIDE_THRESHOLD'] = str(self.text_fg_override_threshold.scaled_value)
r['TEXT_NEW_GAMMA'] = '0' if self.text_old_gamma else '1'
return self.cell_program_replacer(src)
for prog, (only_fg, only_bg) in {

View file

@ -163,6 +163,7 @@ typedef struct Options {
double window_drag_tolerance;
bool generate_256_palette;
int drag_threshold;
float text_fg_override_threshold;
} Options;
typedef struct WindowLogoRenderData {