diff --git a/kitty/shaders/background.slang b/kitty/shaders/background.slang new file mode 100644 index 000000000..9763cab40 --- /dev/null +++ b/kitty/shaders/background.slang @@ -0,0 +1,325 @@ +#language slang 2026 +// Copyright (C) 2026 Kovid Goyal +// Distributed under terms of the GPLv3 license. + +// https://github.com/shader-slang/slang/issues/11874 +// warnings-disable: 41012 + +module background; + +import utils; + +#define NUM_COLORS 256 + +// Inputs {{{ +public struct CellRenderData +{ + public float use_cell_bg_for_selection_fg, use_cell_fg_for_selection_fg, use_cell_for_selection_bg; + + public uint default_fg, highlight_fg, highlight_bg, main_cursor_fg, main_cursor_bg, url_color, url_style, inverted, extra_cursor_fg, extra_cursor_bg; + + public uint columns, lines, sprites_xnum, sprites_ynum, cursor_shape, cell_width, cell_height; + public uint cursor_x1, cursor_x2, cursor_y1, cursor_y2; + public float cursor_opacity, inactive_text_alpha, fg_override_threshold, row_offset, dim_opacity, blink_opacity; + + // must have unique entries with 0 being default_bg and unset being UINT32_MAX + public uint bg_colors0, bg_colors1, bg_colors2, bg_colors3, bg_colors4, bg_colors5, bg_colors6, bg_colors7; + public float bg_opacities0, bg_opacities1, bg_opacities2, bg_opacities3, bg_opacities4, bg_opacities5, bg_opacities6, bg_opacities7; +}; + +// Uniform Blocks (adjust binding slots as needed for your pipeline layout) +public ConstantBuffer crd; + +public struct ColorTable { + uint color_table[NUM_COLORS + MARK_MASK + MARK_MASK + 2]; +}; + +public ConstantBuffer ctb; +#define color_table ctb.color_table + +public struct GammaLUT { + float gamma_lut[256]; +}; + +public ConstantBuffer glt; +#define gamma_lut glt.gamma_lut +// + +static const int fg_index_map[3] = {0, 1, 0}; +static const uint2 cell_pos_map[4] = { + uint2(1u, 0u), // right, top + uint2(1u, 1u), // right, bottom + uint2(0u, 1u), // left, bottom + uint2(0u, 0u) // left, top +}; +static const uint cursor_shape_map[] = { // maps cursor shape to foreground sprite index + 0u, // NO_CURSOR + 0u, // BLOCK (this is rendered as background) + 2u, // BEAM + 3u, // UNDERLINE + 4u // UNFOCUSED +}; + +// }}} + +// Utility functions {{{ + +public static const uint BYTE_MASK = 0xFF; +public static const uint BIT_MASK = 1u; +// Linear space luminance values +public static const float3 Y = float3(0.2126, 0.7152, 0.0722); + +public float3 color_to_vec(uint c) { + uint r, g, b; + r = (c >> 16) & BYTE_MASK; + g = (c >> 8) & BYTE_MASK; + b = c & BYTE_MASK; + return float3(gamma_lut[r], gamma_lut[g], gamma_lut[b]); +} + +public float one_if_equal_zero_otherwise(float a, float b) { + return (1.0f - zero_or_one(abs(float(a) - float(b)))); +} + +// We need an integer variant to accommodate GPU driver bugs, see +// https://github.com/kovidgoyal/kitty/issues/9072 +public uint one_if_equal_zero_otherwise(int a, int b) { + return (1u - uint(zero_or_one(abs(float(a) - float(b))))); +} + +public uint one_if_equal_zero_otherwise(uint a, uint b) { + return (1u - uint(zero_or_one(abs(float(a) - float(b))))); +} + +public uint resolve_color(uint c, uint defval) { + int t = int(c & BYTE_MASK); + uint is_one = one_if_equal_zero_otherwise(t, 1); + uint is_two = one_if_equal_zero_otherwise(t, 2); + uint is_neither_one_nor_two = 1u - is_one - is_two; + return is_one * color_table[(c >> 8) & BYTE_MASK] + is_two * (c >> 8) + is_neither_one_nor_two * defval; +} + +public float3 to_color(uint c, uint defval) { + return color_to_vec(resolve_color(c, defval)); +} + +[ForceInline] +float3 q_func(float type_val, uint which, float3 val) { + return one_if_equal_zero_otherwise(type_val, float(which)) * val; +} + +float3 resolve_dynamic_color(uint c, float3 special_val, float3 defval) { + float type_val = float((c >> 24) & BYTE_MASK); + return ( + q_func(type_val, COLOR_IS_RGB, color_to_vec(c)) + + q_func(type_val, COLOR_IS_INDEX, color_to_vec(color_table[c & BYTE_MASK])) + + q_func(type_val, COLOR_IS_SPECIAL, special_val) + + q_func(type_val, COLOR_NOT_SET, defval) + ); +} + +public float contrast_ratio(float under_luminance, float over_luminance) { + return clamp((max(under_luminance, over_luminance) + 0.05f) / (min(under_luminance, over_luminance) + 0.05f), 1.f, 21.f); +} + +public float contrast_ratio(float3 a, float3 b) { + return contrast_ratio(dot(a, Y), dot(b, Y)); +} + +public struct ColorPair { + public float3 bg, fg; +}; + +float contrast_ratio(ColorPair a) { + return contrast_ratio(a.bg, a.fg); +} + +ColorPair if_less_than_pair(float a, float b, ColorPair thenval, ColorPair elseval) { + return ColorPair( + if_less_than(a, b, thenval.bg, elseval.bg), + if_less_than(a, b, thenval.fg, elseval.fg) + ); +} + +ColorPair if_one_then_pair(float condition, ColorPair thenval, ColorPair elseval) { + return ColorPair( + if_one_then(condition, thenval.bg, elseval.bg), + if_one_then(condition, thenval.fg, elseval.fg) + ); +} + +ColorPair resolve_extra_cursor_colors_for_special_cursor(float3 cell_bg, float3 cell_fg) { + ColorPair cell = ColorPair(cell_fg, cell_bg); + ColorPair base = ColorPair(color_to_vec(crd.default_fg), color_to_vec(crd.bg_colors0)); + float cr = contrast_ratio(cell); + float br = contrast_ratio(base); + ColorPair higher_contrast_pair = if_less_than_pair(cr, br, base, cell); + return if_less_than_pair(cr, 2.5f, higher_contrast_pair, cell); +} + +ColorPair resolve_extra_cursor_colors(float3 cell_bg, float3 cell_fg, ColorPair main_cursor) { + ColorPair ans = ColorPair( + resolve_dynamic_color(crd.extra_cursor_bg, main_cursor.bg, main_cursor.bg), + resolve_dynamic_color(crd.extra_cursor_fg, cell_bg, main_cursor.fg) + ); + ColorPair special = resolve_extra_cursor_colors_for_special_cursor(cell_bg, cell_fg); + return if_one_then_pair(zero_or_one(abs(float(crd.extra_cursor_bg & BYTE_MASK) - float(COLOR_IS_SPECIAL))), ans, special); +} + +uint is_cursor(uint x, uint y) { + uint clamped_x = clamp(x, crd.cursor_x1, crd.cursor_x2); + uint clamped_y = clamp(y, crd.cursor_y1, crd.cursor_y2); + return one_if_equal_zero_otherwise(x, clamped_x) * one_if_equal_zero_otherwise(y, clamped_y); +} + +float background_opacity_for(uint bg, uint colorval, float opacity_if_matched) { + float not_matched = step(1.0, abs(float(colorval) - float(bg))); + return not_matched + opacity_if_matched * (1.0 - not_matched); +} + +float calc_background_opacity(uint bg) { + return ( + background_opacity_for(bg, crd.bg_colors0, crd.bg_opacities0) * + background_opacity_for(bg, crd.bg_colors1, crd.bg_opacities1) * + background_opacity_for(bg, crd.bg_colors2, crd.bg_opacities2) * + background_opacity_for(bg, crd.bg_colors3, crd.bg_opacities3) * + background_opacity_for(bg, crd.bg_colors4, crd.bg_opacities4) * + background_opacity_for(bg, crd.bg_colors5, crd.bg_opacities5) * + background_opacity_for(bg, crd.bg_colors6, crd.bg_opacities6) * + background_opacity_for(bg, crd.bg_colors7, crd.bg_opacities7) + ); +} +// }}} + +public struct CellData { + public float has_cursor; + public float has_block_cursor; + public uint2 pos; + public uint cursor_fg_sprite_idx; + public ColorPair cursor; +}; + +// The complete per-cell background computation, shared by the cell shader and +// the padding shader. It computes the cell position and the final background +// color (accounting for marks, reverse video, selection and the block cursor), +// plus the intermediate data the cell foreground pass needs. +public struct BackgroundOutput { + public float4 position; + public float3 background_rgb; // final background color (linear, post cursor/selection) + public float bg_alpha; // final alpha, post special-cell opacity (pre draw_bg_bitfield mask) + public float cell_has_default_bg; + public float3 bg; // original background color, pre selection/cursor (used for selection fg) + public float3 foreground; // resolved foreground color, pre selection/override + public uint fg_as_uint; + public uint text_attrs; + public uint is_reversed; + public CellData cell_data; +}; + +public BackgroundOutput compute_background( + uint3 colors, uint2 sprite_idx, uint is_selected, uint instance_id, uint vertex_id +) { + BackgroundOutput bo; + + // set cell color indices {{{ + uint2 default_colors = uint2(crd.default_fg, crd.bg_colors0); + uint text_attrs = sprite_idx[1]; + uint is_reversed = ((text_attrs >> REVERSE_SHIFT) & BIT_MASK); + uint is_inverted = is_reversed + crd.inverted; + int fg_index = fg_index_map[is_inverted]; + int bg_index = 1 - fg_index; + int mark = int(text_attrs >> MARK_SHIFT) & MARK_MASK; + uint has_mark = uint(step(1, float(mark))); + uint bg_as_uint = resolve_color(colors[bg_index], default_colors[bg_index]); + bg_as_uint = has_mark * color_table[NUM_COLORS + mark - 1] + (BIT_MASK - has_mark) * bg_as_uint; + float cell_has_default_bg = 1.f - step(1.f, abs(float(bg_as_uint - crd.bg_colors0))); // 1 if has default bg else 0 + float3 bg = color_to_vec(bg_as_uint); + float3 cell_bg = bg; // preserve original bg for the selection foreground color + uint fg_as_uint = resolve_color(colors[fg_index], default_colors[fg_index]); + fg_as_uint = has_mark * color_table[NUM_COLORS + MARK_MASK + mark] + (1u - has_mark) * fg_as_uint; + float3 foreground = color_to_vec(fg_as_uint); + // }}} + + // Cell position {{{ + float dx = 2.0 / float(crd.columns); + float dy = 2.0 / float(crd.lines); + + // The current cell being rendered + uint row = instance_id / crd.columns; + uint column = instance_id - row * crd.columns; + + // The position of this vertex, at a corner of the cell + float left = -1.0 + float(column) * dx; + float top = 1.0 - (float(row) + crd.row_offset) * dy; + + uint2 pos = cell_pos_map[vertex_id]; + + float2 select_x = float2(left, left + dx); + float2 select_y = float2(top, top - dy); + bo.position = float4(select_x[pos.x], select_y[pos.y], 0.0, 1.0); + // }}} + + // Cursor shape and colors {{{ + float has_main_cursor = float(is_cursor(column, row)); + float multicursor_shape = float((is_selected >> 2) & 3u); + float multicursor_uses_main_cursor_shape = float((is_selected >> 4) & BIT_MASK); + multicursor_shape = if_one_then(multicursor_uses_main_cursor_shape, crd.cursor_shape, multicursor_shape); + + float final_cursor_shape = if_one_then(has_main_cursor, crd.cursor_shape, multicursor_shape); + float has_cursor = zero_or_one(final_cursor_shape); + float is_block_cursor = has_cursor * one_if_equal_zero_otherwise(final_cursor_shape, 1.0); + + ColorPair main_cursor; + main_cursor.bg = color_to_vec(crd.main_cursor_bg); + main_cursor.fg = color_to_vec(crd.main_cursor_fg); + + ColorPair extra_cursor = resolve_extra_cursor_colors(cell_bg, foreground, main_cursor); + ColorPair cursor = if_one_then_pair(has_main_cursor, main_cursor, extra_cursor); + + CellData cell_data; + cell_data.has_cursor = has_cursor; + cell_data.has_block_cursor = is_block_cursor; + cell_data.pos = pos; + cell_data.cursor_fg_sprite_idx = cursor_shape_map[int(final_cursor_shape)]; + cell_data.cursor = cursor; + // }}} + + // Background {{{ + float bg_alpha = calc_background_opacity(bg_as_uint); + // we use max so that opacity of the block cursor cell background goes from bg_alpha to 1 + float effective_cursor_opacity = max(crd.cursor_opacity, bg_alpha); + // is_special_cell is either 0 or 1 + float is_special_cell = cell_data.has_block_cursor + float(is_selected & BIT_MASK); + is_special_cell += float(is_reversed); // reverse video cells should be opaque as well + is_special_cell = zero_or_one(is_special_cell); + cell_has_default_bg = if_one_then(is_special_cell, 0., cell_has_default_bg); + + // special cells must always be fully opaque, otherwise leave bg_alpha untouched + bg_alpha = if_one_then(is_special_cell, 1.f, bg_alpha); + // Selection and cursor + bg_alpha = if_one_then(cell_data.has_block_cursor, effective_cursor_opacity, bg_alpha); + bg = if_one_then(float(is_selected & BIT_MASK), if_one_then(crd.use_cell_for_selection_bg, color_to_vec(fg_as_uint), color_to_vec(crd.highlight_bg)), bg); + float3 background_rgb = if_one_then(cell_data.has_block_cursor, lerp(bg, cell_data.cursor.bg, crd.cursor_opacity), bg); + // }}} + + bo.background_rgb = background_rgb; + bo.bg_alpha = bg_alpha; + bo.cell_has_default_bg = cell_has_default_bg; + bo.bg = cell_bg; + bo.foreground = foreground; + bo.fg_as_uint = fg_as_uint; + bo.text_attrs = text_attrs; + bo.is_reversed = is_reversed; + bo.cell_data = cell_data; + return bo; +} + +// Return the premultiplied background color for a cell, used by the padding +// shader to color padding regions to match their neighboring cell. This is the +// same as the cell background pass but without the draw_bg_bitfield masking, so +// the padding is always drawn. The premultiplied alpha ensures padding matches +// its neighboring cell under a semi-transparent OS window. +public float4 padding_background_premul(uint3 colors, uint2 sprite_idx, uint is_selected, uint instance_id) { + BackgroundOutput bo = compute_background(colors, sprite_idx, is_selected, instance_id, 0u); + return vec4_premul(bo.background_rgb, bo.bg_alpha); +} diff --git a/kitty/shaders/cell.slang b/kitty/shaders/cell.slang index 1b58381c6..4d36a1736 100644 --- a/kitty/shaders/cell.slang +++ b/kitty/shaders/cell.slang @@ -9,8 +9,7 @@ import utils; import hsluv; import alpha_blend; import linear2srgb; - -#define NUM_COLORS 256 +import background; extern static const uint FG_OVERRIDE_ALGO; extern static const bool TEXT_NEW_GAMMA; @@ -19,166 +18,34 @@ extern static const uint RENDER_MODE; static const bool ONLY_FOREGROUND = RENDER_MODE == 2; static const bool ONLY_BACKGROUND = RENDER_MODE == 1; -// Inputs {{{ -struct CellRenderData -{ - float use_cell_bg_for_selection_fg, use_cell_fg_for_selection_fg, use_cell_for_selection_bg; - - uint default_fg, highlight_fg, highlight_bg, main_cursor_fg, main_cursor_bg, url_color, url_style, inverted, extra_cursor_fg, extra_cursor_bg; - - uint columns, lines, sprites_xnum, sprites_ynum, cursor_shape, cell_width, cell_height; - uint cursor_x1, cursor_x2, cursor_y1, cursor_y2; - float cursor_opacity, inactive_text_alpha, fg_override_threshold, row_offset, dim_opacity, blink_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; -}; - -// Uniform Blocks (adjust binding slots as needed for your pipeline layout) -ConstantBuffer crd; - -struct ColorTable { - uint color_table[NUM_COLORS + MARK_MASK + MARK_MASK + 2]; -}; - -ConstantBuffer ctb; -#define color_table ctb.color_table - -struct GammaLUT { - float gamma_lut[256]; -}; - -ConstantBuffer glt; -#define gamma_lut glt.gamma_lut +// The background color computation, the cell render data, color table and gamma +// LUT as well as most color utility functions live in the background module so +// that they can be shared with the padding shader. uniform Sampler2D sprite_decorations_map; -// -static const int fg_index_map[3] = {0, 1, 0}; -static const uint2 cell_pos_map[4] = { - uint2(1u, 0u), // right, top - uint2(1u, 1u), // right, bottom - uint2(0u, 1u), // left, bottom - uint2(0u, 0u) // left, top -}; -static const uint cursor_shape_map[] = { // maps cursor shape to foreground sprite index - 0u, // NO_CURSOR - 0u, // BLOCK (this is rendered as background) - 2u, // BEAM - 3u, // UNDERLINE - 4u // UNFOCUSED -}; - -// }}} - -// Utility functions {{{ - -static const uint BYTE_MASK = 0xFF; static const uint SPRITE_INDEX_MASK = 0x7fffffff; static const uint SPRITE_COLORED_MASK = 0x80000000; static const uint SPRITE_COLORED_SHIFT = 31u; -static const uint BIT_MASK = 1u; -// Linear space luminance values -static const float3 Y = float3(0.2126, 0.7152, 0.0722); -float3 color_to_vec(uint c) { - uint r, g, b; - r = (c >> 16) & BYTE_MASK; - g = (c >> 8) & BYTE_MASK; - b = c & BYTE_MASK; - return float3(gamma_lut[r], gamma_lut[g], gamma_lut[b]); -} +struct VertexOutput { + float4 position : SV_Position; -float one_if_equal_zero_otherwise(float a, float b) { - return (1.0f - zero_or_one(abs(float(a) - float(b)))); -} - -// We need an integer variant to accommodate GPU driver bugs, see -// https://github.com/kovidgoyal/kitty/issues/9072 -uint one_if_equal_zero_otherwise(int a, int b) { - return (1u - uint(zero_or_one(abs(float(a) - float(b))))); -} - -uint one_if_equal_zero_otherwise(uint a, uint b) { - return (1u - uint(zero_or_one(abs(float(a) - float(b))))); -} - -uint resolve_color(uint c, uint defval) { - int t = int(c & BYTE_MASK); - uint is_one = one_if_equal_zero_otherwise(t, 1); - uint is_two = one_if_equal_zero_otherwise(t, 2); - uint is_neither_one_nor_two = 1u - is_one - is_two; - return is_one * color_table[(c >> 8) & BYTE_MASK] + is_two * (c >> 8) + is_neither_one_nor_two * defval; -} - -float3 to_color(uint c, uint defval) { - return color_to_vec(resolve_color(c, defval)); -} - -[ForceInline] -float3 q_func(float type_val, uint which, float3 val) { - return one_if_equal_zero_otherwise(type_val, float(which)) * val; -} - -float3 resolve_dynamic_color(uint c, float3 special_val, float3 defval) { - float type_val = float((c >> 24) & BYTE_MASK); - return ( - q_func(type_val, COLOR_IS_RGB, color_to_vec(c)) + - q_func(type_val, COLOR_IS_INDEX, color_to_vec(color_table[c & BYTE_MASK])) + - q_func(type_val, COLOR_IS_SPECIAL, special_val) + - q_func(type_val, COLOR_NOT_SET, defval) - ); -} - -float contrast_ratio(float under_luminance, float over_luminance) { - return clamp((max(under_luminance, over_luminance) + 0.05f) / (min(under_luminance, over_luminance) + 0.05f), 1.f, 21.f); -} - -float contrast_ratio(float3 a, float3 b) { - return contrast_ratio(dot(a, Y), dot(b, Y)); -} - -struct ColorPair { - float3 bg, fg; + float3 background; + float4 effective_background_premul; + float effective_text_alpha; + float3 sprite_pos; + float3 underline_pos; + float3 cursor_pos; + float3 strike_pos; + nointerpolation uint underline_exclusion_pos; + float3 cell_foreground; + float4 cursor_color_premult; + float3 decoration_fg; + float colored_sprite; }; -float contrast_ratio(ColorPair a) { - return contrast_ratio(a.bg, a.fg); -} - -ColorPair if_less_than_pair(float a, float b, ColorPair thenval, ColorPair elseval) { - return ColorPair( - if_less_than(a, b, thenval.bg, elseval.bg), - if_less_than(a, b, thenval.fg, elseval.fg) - ); -} - -ColorPair if_one_then_pair(float condition, ColorPair thenval, ColorPair elseval) { - return ColorPair( - if_one_then(condition, thenval.bg, elseval.bg), - if_one_then(condition, thenval.fg, elseval.fg) - ); -} - -ColorPair resolve_extra_cursor_colors_for_special_cursor(float3 cell_bg, float3 cell_fg) { - ColorPair cell = ColorPair(cell_fg, cell_bg); - ColorPair base = ColorPair(color_to_vec(crd.default_fg), color_to_vec(crd.bg_colors0)); - float cr = contrast_ratio(cell); - float br = contrast_ratio(base); - ColorPair higher_contrast_pair = if_less_than_pair(cr, br, base, cell); - return if_less_than_pair(cr, 2.5f, higher_contrast_pair, cell); -} - -ColorPair resolve_extra_cursor_colors(float3 cell_bg, float3 cell_fg, ColorPair main_cursor) { - ColorPair ans = ColorPair( - resolve_dynamic_color(crd.extra_cursor_bg, main_cursor.bg, main_cursor.bg), - resolve_dynamic_color(crd.extra_cursor_fg, cell_bg, main_cursor.fg) - ); - ColorPair special = resolve_extra_cursor_colors_for_special_cursor(cell_bg, cell_fg); - return if_one_then_pair(zero_or_one(abs(float(crd.extra_cursor_bg & BYTE_MASK) - float(COLOR_IS_SPECIAL))), ans, special); -} - +// Foreground utility functions {{{ uint3 to_sprite_coords(uint idx) { uint sprites_per_page = crd.sprites_xnum * crd.sprites_ynum; uint z = idx / sprites_per_page; @@ -224,119 +91,8 @@ uint2 get_decorations_indices(uint2 sprite_idx, uint in_url /* [0, 1] */, uint t uint has_underline = uint(step(0.5f, float(underline_style))); // [0, 1] return has_decorations * uint2(strike_idx, has_underline * (decorations_idx + underline_style)); } - -uint is_cursor(uint x, uint y) { - uint clamped_x = clamp(x, crd.cursor_x1, crd.cursor_x2); - uint clamped_y = clamp(y, crd.cursor_y1, crd.cursor_y2); - return one_if_equal_zero_otherwise(x, clamped_x) * one_if_equal_zero_otherwise(y, clamped_y); -} // }}} -struct CellData { - float has_cursor; - float has_block_cursor; - uint2 pos; - uint cursor_fg_sprite_idx; - ColorPair cursor; -}; - -struct VertexOutput { - float4 position : SV_Position; - - float3 background; - float4 effective_background_premul; - float effective_text_alpha; - float3 sprite_pos; - float3 underline_pos; - float3 cursor_pos; - float3 strike_pos; - nointerpolation uint underline_exclusion_pos; - float3 cell_foreground; - float4 cursor_color_premult; - float3 decoration_fg; - float colored_sprite; -}; - - -CellData set_vertex_position( - float3 cell_fg, - float3 cell_bg, - uint instance_id, - uint vertex_id, - uint2 sprite_idx, - uint is_selected, - inout VertexOutput vo, -) { - CellData cell_out; - float dx = 2.0 / float(crd.columns); - float dy = 2.0 / float(crd.lines); - - // The current cell being rendered - uint row = instance_id / crd.columns; - uint column = instance_id - row * crd.columns; - - // The position of this vertex, at a corner of the cell - float left = -1.0 + float(column) * dx; - float top = 1.0 - (float(row) + crd.row_offset) * dy; - - uint2 pos = cell_pos_map[vertex_id]; - - float2 select_x = float2(left, left + dx); - float2 select_y = float2(top, top - dy); - float posX = select_x[pos.x]; - float posY = select_y[pos.y]; - vo.position = float4(posX, posY, 0.0, 1.0); - - // The character sprite being rendered - if (!ONLY_BACKGROUND) { - vo.sprite_pos = to_sprite_pos(pos, sprite_idx[0] & SPRITE_INDEX_MASK); - vo.colored_sprite = float((sprite_idx[0] & SPRITE_COLORED_MASK) >> SPRITE_COLORED_SHIFT); - } - - // Cursor shape and colors - float has_main_cursor = float(is_cursor(column, row)); - float multicursor_shape = float((is_selected >> 2) & 3u); - float multicursor_uses_main_cursor_shape = float((is_selected >> 4) & BIT_MASK); - multicursor_shape = if_one_then(multicursor_uses_main_cursor_shape, crd.cursor_shape, multicursor_shape); - - float final_cursor_shape = if_one_then(has_main_cursor, crd.cursor_shape, multicursor_shape); - float has_cursor = zero_or_one(final_cursor_shape); - float is_block_cursor = has_cursor * one_if_equal_zero_otherwise(final_cursor_shape, 1.0); - - ColorPair main_cursor; - main_cursor.bg = color_to_vec(crd.main_cursor_bg); - main_cursor.fg = color_to_vec(crd.main_cursor_fg); - - ColorPair extra_cursor = resolve_extra_cursor_colors(cell_bg, cell_fg, main_cursor); - ColorPair cursor = if_one_then_pair(has_main_cursor, main_cursor, extra_cursor); - - cell_out.has_cursor = has_cursor; - cell_out.has_block_cursor = is_block_cursor; - cell_out.pos = pos; - cell_out.cursor_fg_sprite_idx = cursor_shape_map[int(final_cursor_shape)]; - cell_out.cursor = cursor; - - return cell_out; -} - -float background_opacity_for(uint bg, uint colorval, float opacity_if_matched) { - float not_matched = step(1.0, abs(float(colorval) - float(bg))); - return not_matched + opacity_if_matched * (1.0 - not_matched); -} - -float calc_background_opacity(uint bg) { - return ( - background_opacity_for(bg, crd.bg_colors0, crd.bg_opacities0) * - background_opacity_for(bg, crd.bg_colors1, crd.bg_opacities1) * - background_opacity_for(bg, crd.bg_colors2, crd.bg_opacities2) * - background_opacity_for(bg, crd.bg_colors3, crd.bg_opacities3) * - background_opacity_for(bg, crd.bg_colors4, crd.bg_opacities4) * - background_opacity_for(bg, crd.bg_colors5, crd.bg_opacities5) * - background_opacity_for(bg, crd.bg_colors6, crd.bg_opacities6) * - background_opacity_for(bg, crd.bg_colors7, crd.bg_opacities7) - ); -} - // Override foreground colors {{{ float3 fg_override_luminance(float colored_sprite, float under_luminance, float over_lumininace, float3 under, float3 over) { // If the difference in luminance is too small, @@ -382,35 +138,27 @@ VertexOutput vertex_main( ) { VertexOutput vo; - // set cell color indices {{{ - uint2 default_colors = uint2(crd.default_fg, crd.bg_colors0); - uint text_attrs = sprite_idx[1]; - uint is_reversed = ((text_attrs >> REVERSE_SHIFT) & BIT_MASK); - uint is_inverted = is_reversed + crd.inverted; - int fg_index = fg_index_map[is_inverted]; - int bg_index = 1 - fg_index; - int mark = int(text_attrs >> MARK_SHIFT) & MARK_MASK; - uint has_mark = uint(step(1, float(mark))); - uint bg_as_uint = resolve_color(colors[bg_index], default_colors[bg_index]); - bg_as_uint = has_mark * color_table[NUM_COLORS + mark - 1] + (BIT_MASK - has_mark) * bg_as_uint; - float cell_has_default_bg = 1.f - step(1.f, abs(float(bg_as_uint - crd.bg_colors0))); // 1 if has default bg else 0 - float3 bg = color_to_vec(bg_as_uint); - uint fg_as_uint = resolve_color(colors[fg_index], default_colors[fg_index]); - fg_as_uint = has_mark * color_table[NUM_COLORS + MARK_MASK + mark] + (1u - has_mark) * fg_as_uint; - float3 foreground = color_to_vec(fg_as_uint); - CellData cell_data = set_vertex_position(foreground, bg, instance_id, vertex_id, sprite_idx, is_selected, vo); - // }}} - + BackgroundOutput bo = compute_background(colors, sprite_idx, is_selected, instance_id, vertex_id); + vo.position = bo.position; + float3 background_rgb = bo.background_rgb; + float3 foreground = bo.foreground; + uint fg_as_uint = bo.fg_as_uint; + uint text_attrs = bo.text_attrs; + CellData cell_data = bo.cell_data; // Foreground {{{ if (!ONLY_BACKGROUND) { // background does not depend on foreground + // The character sprite being rendered + vo.sprite_pos = to_sprite_pos(cell_data.pos, sprite_idx[0] & SPRITE_INDEX_MASK); + vo.colored_sprite = float((sprite_idx[0] & SPRITE_COLORED_MASK) >> SPRITE_COLORED_SHIFT); + float has_dim = float((text_attrs >> DIM_SHIFT) & BIT_MASK), has_blink = float((text_attrs >> BLINK_SHIFT) & BIT_MASK); vo.effective_text_alpha = crd.inactive_text_alpha * if_one_then(has_dim, crd.dim_opacity, 1.0) * if_one_then( has_blink, crd.blink_opacity, 1.0); float in_url = float((is_selected >> 1) & BIT_MASK); vo.decoration_fg = if_one_then(in_url, color_to_vec(crd.url_color), to_color(colors[2], fg_as_uint)); // Selection - float3 selection_color = if_one_then(crd.use_cell_bg_for_selection_fg, bg, color_to_vec(crd.highlight_fg)); + float3 selection_color = if_one_then(crd.use_cell_bg_for_selection_fg, bo.bg, color_to_vec(crd.highlight_fg)); selection_color = if_one_then(crd.use_cell_fg_for_selection_fg, foreground, selection_color); foreground = if_one_then(float(is_selected & BIT_MASK), selection_color, foreground); vo.decoration_fg = if_one_then(float(is_selected & BIT_MASK), selection_color, vo.decoration_fg); @@ -429,24 +177,7 @@ VertexOutput vertex_main( } // }}} - // Background {{{ - float bg_alpha = calc_background_opacity(bg_as_uint); - // we use max so that opacity of the block cursor cell background goes from bg_alpha to 1 - float effective_cursor_opacity = max(crd.cursor_opacity, bg_alpha); - // is_special_cell is either 0 or 1 - float is_special_cell = cell_data.has_block_cursor + float(is_selected & BIT_MASK); - is_special_cell += float(is_reversed); // reverse video cells should be opaque as well - is_special_cell = zero_or_one(is_special_cell); - cell_has_default_bg = if_one_then(is_special_cell, 0., cell_has_default_bg); - - // special cells must always be fully opaque, otherwise leave bg_alpha untouched - bg_alpha = if_one_then(is_special_cell, 1.f, bg_alpha); - // Selection and cursor - bg_alpha = if_one_then(cell_data.has_block_cursor, effective_cursor_opacity, bg_alpha); - bg = if_one_then(float(is_selected & BIT_MASK), if_one_then(crd.use_cell_for_selection_bg, color_to_vec(fg_as_uint), color_to_vec(crd.highlight_bg)), bg); - float3 background_rgb = if_one_then(cell_data.has_block_cursor, lerp(bg, cell_data.cursor.bg, crd.cursor_opacity), bg); vo.background = background_rgb; - // }}} if (!ONLY_BACKGROUND && FG_OVERRIDE_ALGO > 0) { vo.decoration_fg = override_foreground_color(vo.decoration_fg, background_rgb, vo.colored_sprite); @@ -454,10 +185,10 @@ VertexOutput vertex_main( } if (!ONLY_FOREGROUND) { - float4 bgpremul = vec4_premul(background_rgb, bg_alpha); + float4 bgpremul = vec4_premul(background_rgb, bo.bg_alpha); // draw_bg_bitfield has bit 0 set to draw default bg cells and bit 1 set to draw non-default bg cells - float cell_has_non_default_bg = 1.f - cell_has_default_bg; - uint draw_bg_mask = uint(2.f * cell_has_non_default_bg + cell_has_default_bg); // 1 if has default bg else 2 + float cell_has_non_default_bg = 1.f - bo.cell_has_default_bg; + uint draw_bg_mask = uint(2.f * cell_has_non_default_bg + bo.cell_has_default_bg); // 1 if has default bg else 2 float draw_bg = step(0.5, float(draw_bg_bitfield & draw_bg_mask)); bgpremul *= draw_bg; vo.effective_background_premul = bgpremul; diff --git a/kitty/shaders/slang.py b/kitty/shaders/slang.py index d1600e7a2..1137eb63d 100644 --- a/kitty/shaders/slang.py +++ b/kitty/shaders/slang.py @@ -256,7 +256,7 @@ class SlangFile(NamedTuple): def defines(self) -> MappingProxyType[str, str]: ans = {} match os.path.basename(self.path): - case 'cell.slang': + case 'cell.slang' | 'background.slang': ans['MARK_MASK'] = str(MARK_MASK) ans['REVERSE_SHIFT'] = str(REVERSE) ans['STRIKE_SHIFT'] = str(STRIKETHROUGH)