From e78e86572ed6f273a7fc89d9aa6e7b24d87462e6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 19 Sep 2024 11:47:16 +0530 Subject: [PATCH] Prepare for allowing upto seven additional semi-transparent background colors --- kitty/cell_fragment.glsl | 2 +- kitty/cell_vertex.glsl | 69 ++++++++++++++++++++-------------- kitty/options/definition.py | 4 +- kitty/options/parse.py | 8 +++- kitty/options/to-c-generated.h | 15 ++++++++ kitty/options/to-c.h | 11 ++++++ kitty/options/types.py | 2 + kitty/options/utils.py | 17 +++++++++ kitty/shaders.c | 26 ++++++++----- kitty/state.h | 1 + 10 files changed, 114 insertions(+), 41 deletions(-) diff --git a/kitty/cell_fragment.glsl b/kitty/cell_fragment.glsl index eb9d6ae22..ceb0e39f0 100644 --- a/kitty/cell_fragment.glsl +++ b/kitty/cell_fragment.glsl @@ -187,7 +187,7 @@ void main() { #ifdef TRANSPARENT final_color = vec4_premul(background, bg_alpha); #else - final_color = vec4(background, draw_bg); + final_color = vec4(background, draw_bg * bg_alpha); #endif #endif diff --git a/kitty/cell_vertex.glsl b/kitty/cell_vertex.glsl index a8ca9c7e7..fd9b39328 100644 --- a/kitty/cell_vertex.glsl +++ b/kitty/cell_vertex.glsl @@ -4,13 +4,16 @@ // Inputs {{{ layout(std140) uniform CellRenderData { - float xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity, use_cell_bg_for_selection_fg, use_cell_fg_for_selection_fg, use_cell_for_selection_bg; + float xstart, ystart, dx, dy, sprite_dx, sprite_dy, use_cell_bg_for_selection_fg, use_cell_fg_for_selection_fg, use_cell_for_selection_bg; - uint default_fg, default_bg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted, second_transparent_bg; + uint default_fg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted, second_transparent_bg; uint xnum, ynum, cursor_fg_sprite_idx; float cursor_x, cursor_y, cursor_w, cursor_opacity; + // must have unique entries with 0 being default_bg and unset being UINT32_MAX + uint bg_colors0, bg_colors1, bg_colors2, bg_colors3, bg_colors4, bg_colors5, bg_colors6, bg_colors7; + float bg_opacities0, bg_opacities1, bg_opacities2, bg_opacities3, bg_opacities4, bg_opacities5, bg_opacities6, bg_opacities7; uint color_table[NUM_COLORS + MARK_MASK + MARK_MASK + 2]; }; #if (PHASE == PHASE_BACKGROUND) @@ -145,12 +148,30 @@ CellData set_vertex_position() { return CellData(has_cursor, has_cursor * is_block_cursor, pos); } +float background_opacity_for(uint bg, uint colorval, float opacity_if_matched) { // opacity_if_matched if bg == colorval else 1 + float not_matched = step(1.f, abs(float(colorval - bg))); // not_matched = 0 if bg == colorval else 1 + return not_matched + opacity_if_matched * (1.f - not_matched); +} + +float calc_background_opacity(uint bg) { + return ( + background_opacity_for(bg, bg_colors0, bg_opacities0) * + background_opacity_for(bg, bg_colors1, bg_opacities1) * + background_opacity_for(bg, bg_colors2, bg_opacities2) * + background_opacity_for(bg, bg_colors3, bg_opacities3) * + background_opacity_for(bg, bg_colors4, bg_opacities4) * + background_opacity_for(bg, bg_colors5, bg_opacities5) * + background_opacity_for(bg, bg_colors6, bg_opacities6) * + background_opacity_for(bg, bg_colors7, bg_opacities7) + ); +} + void main() { CellData cell_data = set_vertex_position(); // set cell color indices {{{ - uvec2 default_colors = uvec2(default_fg, default_bg); + uvec2 default_colors = uvec2(default_fg, bg_colors0); uint text_attrs = sprite_coords[3]; uint is_reversed = ((text_attrs >> REVERSE_SHIFT) & ONE); uint is_inverted = is_reversed + inverted; @@ -194,42 +215,34 @@ void main() { // }}} // Background {{{ - float bg_is_not_transparent = step(1, float(abs( - (bg_as_uint - default_colors[1]) * (bg_as_uint - second_transparent_bg) - ))); // bg_is_not_transparent = 0 if bg_as_uint in (default_colors[1], second_transparent_bg) else 1 +#if PHASE == PHASE_BOTH && !defined(TRANSPARENT) // fast case single pass opaque background + bg_alpha = 1; draw_bg = 1; - +#else + bg_alpha = calc_background_opacity(bg_as_uint); #if (PHASE == PHASE_BACKGROUND) // draw_bg_bitfield has bit 0 set to draw default bg cells and bit 1 set to draw non-default bg cells - uint draw_bg_mask = uint(2 * bg_is_not_transparent + (1 - bg_is_not_transparent)); - draw_bg = step(1, float(draw_bg_bitfield & draw_bg_mask)); + float cell_has_non_default_bg = step(1.f, abs(float(bg_as_uint - bg_colors0))); // 0 if has default bg else 1 + uint draw_bg_mask = uint(2.f * cell_has_non_default_bg + (1.f - cell_has_non_default_bg)); // 1 if has default bg else 2 + draw_bg = step(0.5, float(draw_bg_bitfield & draw_bg_mask)); +#else + draw_bg = 1; #endif - bg_alpha = 1.f; -#ifdef TRANSPARENT - // Set bg_alpha to background_opacity on cells that have the default background color - // Which means they must not have a block cursor or a selection or reverse video - // On other cells it should be 1. For the SPECIAL program it should be 1 on cells with - // selections/block cursor and 0 everywhere else. float is_special_cell = cell_data.has_block_cursor + float(is_selected & ONE); -#if (PHASE != PHASE_SPECIAL) - is_special_cell += bg_is_not_transparent + float(is_reversed); -#endif +#if PHASE == PHASE_SPECIAL + // Only special cells must be drawn and they must have bg_alpha 1 bg_alpha = step(0.5, is_special_cell); // bg_alpha = 1 if is_special_cell else 0 -#if (PHASE != PHASE_SPECIAL) - bg_alpha = bg_alpha + (1.0f - bg_alpha) * background_opacity; // bg_alpha = 1 if bg_alpha else background_opacity - bg_alpha *= draw_bg; // if not draw_bg: bg_alpha = 0 -#endif +#else + is_special_cell += float(is_reversed); // bg_alpha should be 1 for reverse video cells as well + is_special_cell = step(0.5, is_special_cell); // is_special_cell = 1 if is_special_cell else 0 + bg_alpha = bg_alpha * (1. - float(is_special_cell)) + is_special_cell; // bg_alpha = 1 if is_special_cell else bg_alpha #endif + bg_alpha *= draw_bg; +#endif // ends fast case #if else // Selection and cursor bg = choose_color(float(is_selected & ONE), choose_color(use_cell_for_selection_bg, color_to_vec(fg_as_uint), color_to_vec(highlight_bg)), bg); background = choose_color(cell_data.has_block_cursor, mix(bg, color_to_vec(cursor_bg), cursor_opacity), bg); -#if !defined(TRANSPARENT) && (PHASE == PHASE_SPECIAL) - float is_special_cell = cell_data.has_block_cursor + float(is_selected & ONE); - bg_alpha = step(0.5, is_special_cell); -#endif - // }}} - } diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 0ef2841c2..c54432108 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -1481,7 +1481,7 @@ theme with a background color in your editor, it will not be rendered as transparent. Instead you should change the default background color in your kitty config and not use a background color in the editor color scheme. Or use the escape codes to set the terminals default colors in a shell script to -launch your editor. See also :opt:`second_transparent_bg`. +launch your editor. See also :opt:`transparent_background_colors`. Be aware that using a value less than 1.0 is a (possibly significant) performance hit. When using a low value for this setting, it is desirable that you set the :opt:`background` color to a color the matches the @@ -1527,6 +1527,8 @@ opt('background_image_linear', 'no', long_text='When background image is scaled, whether linear interpolation should be used.' ) +opt('transparent_background_colors', '', option_type='transparent_background_colors', ctype='!transparent_background_colors') + opt('second_transparent_bg', 'none', option_type='to_color_or_none', long_text=''' When the background color matches this color, :opt:`background_opacity` is applied to it to render it as semi-transparent, just as for colors matching the main :opt:`background` color. diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 2132f13d7..5a0c685b9 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -19,8 +19,9 @@ from kitty.options.utils import ( shell_integration, store_multiple, symbol_map, tab_activity_symbol, tab_bar_edge, tab_bar_margin_height, tab_bar_min_tabs, tab_fade, tab_font_style, tab_separator, tab_title_template, titlebar_color, to_cursor_shape, to_cursor_unfocused_shape, to_font_size, - to_layout_names, to_modifiers, url_prefixes, url_style, visual_bell_duration, - visual_window_select_characters, window_border_width, window_logo_scale, window_size + to_layout_names, to_modifiers, transparent_background_colors, url_prefixes, url_style, + visual_bell_duration, visual_window_select_characters, window_border_width, window_logo_scale, + window_size ) @@ -1326,6 +1327,9 @@ class Parser: def touch_scroll_multiplier(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['touch_scroll_multiplier'] = float(val) + def transparent_background_colors(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + ans['transparent_background_colors'] = transparent_background_colors(val) + def undercurl_style(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_undercurl_style: diff --git a/kitty/options/to-c-generated.h b/kitty/options/to-c-generated.h index 949905b9c..1ba5235c5 100644 --- a/kitty/options/to-c-generated.h +++ b/kitty/options/to-c-generated.h @@ -850,6 +850,19 @@ convert_from_opts_background_image_linear(PyObject *py_opts, Options *opts) { Py_DECREF(ret); } +static void +convert_from_python_transparent_background_colors(PyObject *val, Options *opts) { + transparent_background_colors(val, opts); +} + +static void +convert_from_opts_transparent_background_colors(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "transparent_background_colors"); + if (ret == NULL) return; + convert_from_python_transparent_background_colors(ret, opts); + Py_DECREF(ret); +} + static void convert_from_python_dynamic_background_opacity(PyObject *val, Options *opts) { opts->dynamic_background_opacity = PyObject_IsTrue(val); @@ -1229,6 +1242,8 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) { if (PyErr_Occurred()) return false; convert_from_opts_background_image_linear(py_opts, opts); if (PyErr_Occurred()) return false; + convert_from_opts_transparent_background_colors(py_opts, opts); + if (PyErr_Occurred()) return false; convert_from_opts_dynamic_background_opacity(py_opts, opts); if (PyErr_Occurred()) return false; convert_from_opts_background_tint(py_opts, opts); diff --git a/kitty/options/to-c.h b/kitty/options/to-c.h index 1240ba678..f2ccb236a 100644 --- a/kitty/options/to-c.h +++ b/kitty/options/to-c.h @@ -441,6 +441,17 @@ window_logo_scale(PyObject *src, Options *opts) { opts->window_logo_scale.height = PyFloat_AsFloat(PyTuple_GET_ITEM(src, 1)); } +static void +transparent_background_colors(PyObject *src, Options *opts) { + memset(opts->transparent_background_colors, 0, sizeof(opts->transparent_background_colors)); + for (Py_ssize_t i = 0; i < MIN(PyTuple_GET_SIZE(src), (Py_ssize_t)arraysz(opts->transparent_background_colors)); i++) { + PyObject *e = PyTuple_GET_ITEM(src, i); + opts->transparent_background_colors[i].color = color_as_int(PyTuple_GET_ITEM(e, 0)); + opts->transparent_background_colors[i].opacity = PyFloat_AsFloat(PyTuple_GET_ITEM(e, 1)); + opts->transparent_background_colors[i].is_set = true; + } +} + static inline void resize_debounce_time(PyObject *src, Options *opts) { opts->resize_debounce_time.on_end = s_double_to_monotonic_t(PyFloat_AsDouble(PyTuple_GET_ITEM(src, 0))); diff --git a/kitty/options/types.py b/kitty/options/types.py index 067cc578e..2ae6b4d2e 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -443,6 +443,7 @@ option_names = ( # {{{ 'text_composition_strategy', 'text_fg_override_threshold', 'touch_scroll_multiplier', + 'transparent_background_colors', 'undercurl_style', 'underline_hyperlinks', 'update_check_interval', @@ -607,6 +608,7 @@ class Options: text_composition_strategy: str = 'platform' text_fg_override_threshold: float = 0.0 touch_scroll_multiplier: float = 1.0 + transparent_background_colors: typing.Tuple[typing.Tuple[kitty.fast_data_types.Color, float], ...] = () undercurl_style: choices_for_undercurl_style = 'thin-sparse' underline_hyperlinks: choices_for_underline_hyperlinks = 'hover' update_check_interval: float = 24.0 diff --git a/kitty/options/utils.py b/kitty/options/utils.py index 2a23447d3..9dbfa3352 100644 --- a/kitty/options/utils.py +++ b/kitty/options/utils.py @@ -1554,6 +1554,23 @@ def visual_bell_duration(spec: str) -> Tuple[float, EasingFunction, EasingFuncti return parse_animation(spec, interval=0.) +def transparent_background_colors(spec: str) -> Tuple[Tuple[Color, float], ...]: + if not spec: + return () + ans: list[tuple[Color, float]] = [] + seen: dict[Color, int] = {} + for part in spec.split(): + col, sep, alpha = part.partition('@') + c = to_color(col) + o = unit_float(alpha) if alpha else -1 + if (idx := seen.get(c)) is not None: + ans[idx] = c, o + continue + seen[c] = len(ans) + ans.append((c, o)) + return tuple(ans[:7]) + + def deprecated_hide_window_decorations_aliases(key: str, val: str, ans: Dict[str, Any]) -> None: if not hasattr(deprecated_hide_window_decorations_aliases, key): setattr(deprecated_hide_window_decorations_aliases, key, True) diff --git a/kitty/shaders.c b/kitty/shaders.c index a2ce99bd0..7d70d01b8 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -293,12 +293,14 @@ pick_cursor_color(Line *line, const ColorProfile *color_profile, color_type cell static void cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, const CellRenderData *crd, CursorRenderInfo *cursor, OSWindow *os_window) { struct GPUCellRenderData { - GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity, use_cell_bg_for_selection_fg, use_cell_fg_for_selection_color, use_cell_for_selection_bg; + GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy, use_cell_bg_for_selection_fg, use_cell_fg_for_selection_color, use_cell_for_selection_bg; - GLuint default_fg, default_bg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted, second_transparent_bg; + GLuint default_fg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted, second_transparent_bg; GLuint xnum, ynum, cursor_fg_sprite_idx; GLfloat cursor_x, cursor_y, cursor_w, cursor_opacity; + GLuint bg_colors0, bg_colors1, bg_colors2, bg_colors3, bg_colors4, bg_colors5, bg_colors6, bg_colors7; + GLfloat bg_opacities0, bg_opacities1, bg_opacities2, bg_opacities3, bg_opacities4, bg_opacities5, bg_opacities6, bg_opacities7; }; // Send the uniform data struct GPUCellRenderData *rd = (struct GPUCellRenderData*)map_vao_buffer(vao_idx, uniform_buffer, GL_WRITE_ONLY); @@ -307,8 +309,16 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c copy_color_table_to_buffer(cp, (GLuint*)rd, cell_program_layouts[CELL_PROGRAM].color_table.offset / sizeof(GLuint), cell_program_layouts[CELL_PROGRAM].color_table.stride / sizeof(GLuint)); } #define COLOR(name) colorprofile_to_color(cp, cp->overridden.name, cp->configured.name).rgb - rd->default_fg = COLOR(default_fg); rd->default_bg = COLOR(default_bg); + rd->default_fg = COLOR(default_fg); rd->highlight_fg = COLOR(highlight_fg); rd->highlight_bg = COLOR(highlight_bg); + rd->bg_colors0 = COLOR(default_bg); + rd->bg_opacities0 = os_window->is_semi_transparent ? os_window->background_opacity : 1.0f; +#define SETBG(which) if (OPT(transparent_background_colors)[which-1].is_set) { \ + rd->bg_colors##which = OPT(transparent_background_colors)[which-1].color; \ + rd->bg_opacities##which = OPT(transparent_background_colors)[which-1].opacity < 0 ? rd->bg_opacities0 : OPT(transparent_background_colors)[which-1].opacity; \ +} else { rd->bg_colors##which = UINT32_MAX; } + SETBG(1); SETBG(2); SETBG(3); SETBG(4); SETBG(5); SETBG(6); SETBG(7); +#undef SETBG // selection if (IS_SPECIAL_COLOR(highlight_fg)) { if (IS_SPECIAL_COLOR(highlight_bg)) { @@ -320,7 +330,6 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c rd->use_cell_bg_for_selection_fg = 0.f; rd->use_cell_fg_for_selection_color = 0.f; } rd->use_cell_for_selection_bg = IS_SPECIAL_COLOR(highlight_bg) ? 1. : 0.; - rd->second_transparent_bg = IS_SPECIAL_COLOR(second_transparent_bg) ? rd->default_bg : COLOR(second_transparent_bg); // Cursor position enum { BLOCK_IDX = 0, BEAM_IDX = NUM_UNDERLINE_STYLES + 3, UNDERLINE_IDX = NUM_UNDERLINE_STYLES + 4, UNFOCUSED_IDX = NUM_UNDERLINE_STYLES + 5 }; Line *line_for_cursor = NULL; @@ -338,7 +347,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c case CURSOR_HOLLOW: rd->cursor_fg_sprite_idx = UNFOCUSED_IDX; break; }; - color_type cell_fg = rd->default_fg, cell_bg = rd->default_bg; + color_type cell_fg = rd->default_fg, cell_bg = rd->bg_colors0; index_type cell_color_x = cursor->x; bool reversed = false; if (cursor->x < screen->columns && cursor->y < screen->lines) { @@ -352,10 +361,10 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c colors_for_cell(line_for_cursor, cp, &cell_color_x, &cell_fg, &cell_bg, &reversed); } if (IS_SPECIAL_COLOR(cursor_color)) { - if (line_for_cursor) pick_cursor_color(line_for_cursor, cp, cell_fg, cell_bg, cell_color_x, &rd->cursor_fg, &rd->cursor_bg, rd->default_fg, rd->default_bg); - else { rd->cursor_fg = rd->default_bg; rd->cursor_bg = rd->default_fg; } + if (line_for_cursor) pick_cursor_color(line_for_cursor, cp, cell_fg, cell_bg, cell_color_x, &rd->cursor_fg, &rd->cursor_bg, rd->default_fg, rd->bg_colors0); + else { rd->cursor_fg = rd->bg_colors0; rd->cursor_bg = rd->default_fg; } if (cell_bg == cell_fg) { - rd->cursor_fg = rd->default_bg; rd->cursor_bg = rd->default_fg; + rd->cursor_fg = rd->bg_colors0; rd->cursor_bg = rd->default_fg; } else { rd->cursor_fg = cell_bg; rd->cursor_bg = cell_fg; } } else { rd->cursor_bg = COLOR(cursor_color); @@ -375,7 +384,6 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c sprite_tracker_current_layout(os_window->fonts_data, &x, &y, &z); rd->sprite_dx = 1.0f / (float)x; rd->sprite_dy = 1.0f / (float)y; rd->inverted = screen_invert_colors(screen) ? 1 : 0; - rd->background_opacity = os_window->is_semi_transparent ? os_window->background_opacity : 1.0f; #undef COLOR rd->url_color = OPT(url_color); rd->url_style = OPT(url_style); diff --git a/kitty/state.h b/kitty/state.h index efae4d909..c87428907 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -119,6 +119,7 @@ typedef struct { } *entries; } font_features; struct { Animation *cursor, *visual_bell; } animation; + struct { color_type color; float opacity; bool is_set; } transparent_background_colors[8]; } Options; typedef struct WindowLogoRenderData {