From a47cc05d4d3dabc3dd66bf82f0d6cc10ab06da91 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 3 Jul 2026 12:46:20 +0530 Subject: [PATCH] Backport using uniform for text_fg_override_threshold from slang to legacy glsl --- kitty/cell_defines.glsl | 1 - kitty/cell_vertex.glsl | 5 +++-- kitty/options/to-c-generated.h | 15 +++++++++++++++ kitty/options/to-c.h | 10 ++++++++++ kitty/shaders.c | 1 + kitty/shaders/legacy.py | 30 ++++++++---------------------- kitty/state.h | 1 + 7 files changed, 38 insertions(+), 25 deletions(-) diff --git a/kitty/cell_defines.glsl b/kitty/cell_defines.glsl index 23b1ffd07..2df43379d 100644 --- a/kitty/cell_defines.glsl +++ b/kitty/cell_defines.glsl @@ -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} diff --git a/kitty/cell_vertex.glsl b/kitty/cell_vertex.glsl index cabed1495..7c58706cb 100644 --- a/kitty/cell_vertex.glsl +++ b/kitty/cell_vertex.glsl @@ -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); diff --git a/kitty/options/to-c-generated.h b/kitty/options/to-c-generated.h index d209fb78d..5bb372e9e 100644 --- a/kitty/options/to-c-generated.h +++ b/kitty/options/to-c-generated.h @@ -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); @@ -1546,6 +1559,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); diff --git a/kitty/options/to-c.h b/kitty/options/to-c.h index 4a43e8d62..d4cc59046 100644 --- a/kitty/options/to-c.h +++ b/kitty/options/to-c.h @@ -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); diff --git a/kitty/shaders.c b/kitty/shaders.c index 8d9913642..4dd6cdeac 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -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); diff --git a/kitty/shaders/legacy.py b/kitty/shaders/legacy.py index 144b6cad9..7a10ba114 100644 --- a/kitty/shaders/legacy.py +++ b/kitty/shaders/legacy.py @@ -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 { diff --git a/kitty/state.h b/kitty/state.h index 6858ccaa1..4cbd1db46 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -164,6 +164,7 @@ typedef struct Options { double window_drag_tolerance; bool generate_256_palette; int drag_threshold; + float text_fg_override_threshold; } Options; typedef struct WindowLogoRenderData {