diff --git a/kitty/cell_defines.glsl b/kitty/cell_defines.glsl index 9add9a638..ec6459548 100644 --- a/kitty/cell_defines.glsl +++ b/kitty/cell_defines.glsl @@ -25,5 +25,5 @@ #define ONLY_FOREGROUND #endif -// sRGB luminance values +// Linear space luminance values const vec3 Y = vec3(0.2126, 0.7152, 0.0722); diff --git a/kitty/cell_vertex.glsl b/kitty/cell_vertex.glsl index 3b46a48ab..2cc76486c 100644 --- a/kitty/cell_vertex.glsl +++ b/kitty/cell_vertex.glsl @@ -93,8 +93,7 @@ vec3 to_color(uint c, uint defval) { } vec3 resolve_dynamic_color(uint c, vec3 special_val, vec3 defval) { - float type = float(c & BYTE_MASK); - c >>= 8; + float type = float((c >> 24) & BYTE_MASK); #define q(which, val) one_if_equal_zero_otherwise(type, which) * val return ( q(COLOR_IS_RGB, color_to_vec(c)) + q(COLOR_IS_INDEX, color_to_vec(color_table[c & BYTE_MASK])) + @@ -103,7 +102,44 @@ vec3 resolve_dynamic_color(uint c, vec3 special_val, vec3 defval) { #undef q } -void resolve_extra_cursor_colors(out vec3 cursor_fg, out vec3 cursor_bg) { +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(vec3 a, vec3 b) { + return contrast_ratio(dot(a, Y), dot(b, Y)); +} + +struct ColorPair { + vec3 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(vec3 cell_bg, vec3 cell_fg) { + ColorPair cell = ColorPair(cell_fg, cell_bg), base = ColorPair(color_to_vec(default_fg), color_to_vec(bg_colors0)); + float cr = contrast_ratio(cell), br = contrast_ratio(base); + ColorPair higher_contrast_pair = if_less_than_pair(cr, br, base, cell); + return if_less_than_pair(cr, 2.5, higher_contrast_pair, cell); +} + +ColorPair resolve_extra_cursor_colors(vec3 cell_bg, vec3 cell_fg, ColorPair main_cursor) { + ColorPair ans = ColorPair( + resolve_dynamic_color(extra_cursor_bg, main_cursor.bg, main_cursor.bg), + resolve_dynamic_color(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(extra_cursor_bg & BYTE_MASK) - COLOR_IS_SPECIAL)), ans, special); } uvec3 to_sprite_coords(uint idx) { @@ -160,7 +196,7 @@ struct CellData { float has_cursor, has_block_cursor; uvec2 pos; uint cursor_fg_sprite_idx; - vec3 cursor_fg, cursor_bg; + ColorPair cursor; } cell_data; CellData set_vertex_position(vec3 cell_fg, vec3 cell_bg) { @@ -180,6 +216,7 @@ CellData set_vertex_position(vec3 cell_fg, vec3 cell_bg) { sprite_pos = to_sprite_pos(pos, sprite_idx[0] & SPRITE_INDEX_MASK); colored_sprite = float((sprite_idx[0] & SPRITE_COLORED_MASK) >> SPRITE_COLORED_SHIFT); #endif + // Cursor shape and colors float has_main_cursor = is_cursor(column, row); float multicursor_shape = float((is_selected >> 2) & 3u); float multicursor_uses_main_cursor_shape = float((is_selected >> 4) & BIT_MASK); @@ -187,9 +224,10 @@ CellData set_vertex_position(vec3 cell_fg, vec3 cell_bg) { float final_cursor_shape = if_one_then(has_main_cursor, 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); - vec3 cursor_fg = color_to_vec(main_cursor_fg); - vec3 cursor_bg = color_to_vec(main_cursor_bg); - return CellData(has_cursor, is_block_cursor, pos, cursor_shape_map[int(final_cursor_shape)], cursor_fg, cursor_bg); + ColorPair main_cursor = ColorPair(color_to_vec(main_cursor_bg), color_to_vec(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); + return CellData(has_cursor, is_block_cursor, pos, cursor_shape_map[int(final_cursor_shape)], cursor); } float background_opacity_for(uint bg, uint colorval, float opacity_if_matched) { // opacity_if_matched if bg == colorval else 1 @@ -226,8 +264,6 @@ vec3 fg_override(float under_luminance, float over_lumininace, vec3 under, vec3 #else -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); } vec3 fg_override(float under_luminance, float over_luminance, vec3 under, vec3 over) { @@ -295,8 +331,8 @@ void main() { underline_exclusion_pos = to_underline_exclusion_pos(); // Cursor - cursor_color_premult = vec4(cell_data.cursor_bg * cursor_opacity, cursor_opacity); - vec3 final_cursor_text_color = mix(foreground, cell_data.cursor_fg, cursor_opacity); + cursor_color_premult = vec4(cell_data.cursor.bg * cursor_opacity, cursor_opacity); + vec3 final_cursor_text_color = mix(foreground, cell_data.cursor.fg, cursor_opacity); foreground = if_one_then(cell_data.has_block_cursor, final_cursor_text_color, foreground); decoration_fg = if_one_then(cell_data.has_block_cursor, final_cursor_text_color, decoration_fg); cursor_pos = to_sprite_pos(cell_data.pos, cell_data.cursor_fg_sprite_idx * uint(cell_data.has_cursor)); @@ -318,7 +354,7 @@ void main() { // 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(use_cell_for_selection_bg, color_to_vec(fg_as_uint), color_to_vec(highlight_bg)), bg); - vec3 background_rgb = if_one_then(cell_data.has_block_cursor, mix(bg, cell_data.cursor_bg, cursor_opacity), bg); + vec3 background_rgb = if_one_then(cell_data.has_block_cursor, mix(bg, cell_data.cursor.bg, cursor_opacity), bg); background = background_rgb; // }}} diff --git a/kitty/shaders.c b/kitty/shaders.c index 2bbfe99b6..735cb722c 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -414,9 +414,8 @@ create_graphics_vao(void) { #define IS_SPECIAL_COLOR(name) (screen->color_profile->overridden.name.type == COLOR_IS_SPECIAL || (screen->color_profile->overridden.name.type == COLOR_NOT_SET && screen->color_profile->configured.name.type == COLOR_IS_SPECIAL)) static void -pick_cursor_color(Line *line, const ColorProfile *color_profile, color_type cell_fg, color_type cell_bg, index_type cell_color_x, color_type *cursor_fg, color_type *cursor_bg, color_type default_fg, color_type default_bg) { +pick_cursor_color(color_type cell_fg, color_type cell_bg, color_type *cursor_fg, color_type *cursor_bg, color_type default_fg, color_type default_bg) { ARGB32 fg, bg, dfg, dbg; - (void) line; (void) color_profile; (void) cell_color_x; fg.rgb = cell_fg; bg.rgb = cell_bg; *cursor_fg = cell_bg; *cursor_bg = cell_fg; double cell_contrast = rgb_contrast(fg, bg); @@ -515,8 +514,9 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, C }; } } + // If you change the following algorithm remember to change it in the cell shader for extra cursors too 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->main_cursor_fg, &rd->main_cursor_bg, rd->default_fg, rd->bg_colors0); + if (line_for_cursor) pick_cursor_color(cell_fg, cell_bg, &rd->main_cursor_fg, &rd->main_cursor_bg, rd->default_fg, rd->bg_colors0); else { rd->main_cursor_fg = rd->bg_colors0; rd->main_cursor_bg = rd->default_fg; } if (cell_bg == cell_fg) { rd->main_cursor_fg = rd->bg_colors0; rd->main_cursor_bg = rd->default_fg; diff --git a/kitty/utils.glsl b/kitty/utils.glsl index 708c7ff44..e758ffb5c 100644 --- a/kitty/utils.glsl +++ b/kitty/utils.glsl @@ -2,6 +2,8 @@ #define zero_or_one(x) step(1.f, x) // condition must be zero or one. When 1 thenval is returned otherwise elseval #define if_one_then(condition, thenval, elseval) mix(elseval, thenval, condition) +// a < b ? thenval : elseval +#define if_less_than(a, b, thenval, elseval) mix(thenval, elseval, step(b, a)) vec4 vec4_premul(vec3 rgb, float a) { return vec4(rgb * a, a);