mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-06 08:01:58 +02:00
Only apply linear2srgb to actual text pixels
Better fix for #6209 as it does not make background_opacity non-linear. Instead the linear2srgb() function is applied only to pixels that actually have text content.
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
#pragma kitty_include_shader <linear2srgb.glsl>
|
||||
|
||||
uniform sampler2D image;
|
||||
|
||||
in vec2 texcoord;
|
||||
@@ -8,5 +6,4 @@ out vec4 color;
|
||||
|
||||
void main() {
|
||||
color = texture(image, texcoord);
|
||||
color.a = linear2srgb(color.a);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#pragma kitty_include_shader <linear2srgb.glsl>
|
||||
|
||||
uniform uvec2 viewport;
|
||||
uniform uint colors[9];
|
||||
uniform float background_opacity, do_srgb_correction;
|
||||
uniform float background_opacity;
|
||||
uniform float tint_opacity, tint_premult;
|
||||
uniform float gamma_lut[256];
|
||||
in vec4 rect; // left, top, right, bottom
|
||||
@@ -46,6 +44,5 @@ void main() {
|
||||
color3 = is_window_bg * window_bg + (1. - is_window_bg) * color3;
|
||||
float final_opacity = is_default_bg * tint_opacity + (1. - is_default_bg) * background_opacity;
|
||||
float final_premult_opacity = is_default_bg * tint_premult + (1. - is_default_bg) * background_opacity;
|
||||
final_opacity = do_srgb_correction * linear2srgb(final_opacity) + (1. - do_srgb_correction) * final_opacity;
|
||||
color = vec4(color3 * final_premult_opacity, final_opacity);
|
||||
}
|
||||
|
||||
@@ -117,6 +117,7 @@ vec4 foreground_contrast(vec4 over, vec3 under) {
|
||||
return over;
|
||||
}
|
||||
|
||||
#ifdef TEXT_OLD_GAMMA
|
||||
vec4 foreground_contrast_incorrect(vec4 over, vec3 under) {
|
||||
// Simulation of gamma-incorrect blending
|
||||
float under_luminance = dot(under, Y);
|
||||
@@ -131,13 +132,17 @@ vec4 foreground_contrast_incorrect(vec4 over, vec3 under) {
|
||||
over.a = clamp_to_unit_float((srgb2linear(linear2srgb(over_lumininace) * over.a + linear2srgb(under_luminance) * (1.0f - over.a)) - under_luminance) / (over_lumininace - under_luminance));
|
||||
return over;
|
||||
}
|
||||
#endif
|
||||
|
||||
vec4 foreground_color() {
|
||||
vec4 load_text_foreground_color() {
|
||||
// For colored sprites use the color from the sprite rather than the text foreground
|
||||
// Return non-premultiplied foreground color
|
||||
vec4 text_fg = texture(sprites, sprite_pos);
|
||||
return vec4(mix(foreground, text_fg.rgb, colored_sprite), text_fg.a);
|
||||
}
|
||||
|
||||
vec4 foreground_with_decorations(vec4 text_fg) {
|
||||
vec4 calculate_premul_foreground_from_sprites(vec4 text_fg) {
|
||||
// Return premul foreground color from decorations (cursor, underline, strikethrough)
|
||||
float underline_alpha = texture(sprites, underline_pos).a;
|
||||
float strike_alpha = texture(sprites, strike_pos).a;
|
||||
float cursor_alpha = texture(sprites, cursor_pos).a;
|
||||
@@ -148,39 +153,41 @@ vec4 foreground_with_decorations(vec4 text_fg) {
|
||||
return mix(ans, cursor_color_vec, cursor_alpha);
|
||||
}
|
||||
|
||||
vec4 calculate_foreground() {
|
||||
// returns the effective foreground color in pre-multiplied form
|
||||
vec4 text_fg = foreground_color();
|
||||
return foreground_with_decorations(text_fg);
|
||||
}
|
||||
|
||||
vec4 calculate_foreground(vec3 bg) {
|
||||
vec4 adjust_foreground_contrast_with_background(vec4 text_fg, vec3 bg) {
|
||||
// When rendering on a background we can adjust the alpha channel contrast
|
||||
// to improve legibility based on the source and destination colors
|
||||
vec4 text_fg = foreground_color();
|
||||
#ifdef TEXT_OLD_GAMMA
|
||||
text_fg = foreground_contrast_incorrect(text_fg, bg);
|
||||
return foreground_contrast_incorrect(text_fg, bg);
|
||||
#else
|
||||
text_fg = foreground_contrast(text_fg, bg);
|
||||
return foreground_contrast(text_fg, bg);
|
||||
#endif
|
||||
return foreground_with_decorations(text_fg);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
float adjust_alpha_for_incorrect_blending_by_compositor(float text_fg_alpha, float final_alpha) {
|
||||
// Adjust the transparent alpha-channel to account for incorrect
|
||||
// gamma-blending performed by the compositor (true for at least wlroots, picom)
|
||||
// We have a linear alpha channel apply the sRGB curve to it once again to compensate
|
||||
// for the incorrect blending in the compositor.
|
||||
// We apply the correction only if there was actual text at this pixel, so as to not make
|
||||
// background_opacity non-linear
|
||||
// See https://github.com/kovidgoyal/kitty/issues/6209 for discussion.
|
||||
const float threshold = 0.000001;
|
||||
// linear2srgb(final_alpha) if text_fg_alpha >= threshold else final_alpha
|
||||
return mix(final_alpha, linear2srgb(final_alpha), step(threshold, text_fg_alpha));
|
||||
}
|
||||
|
||||
void main() {
|
||||
#ifdef SIMPLE
|
||||
vec4 fg = calculate_foreground(background);
|
||||
vec4 text_fg = load_text_foreground_color();
|
||||
text_fg = adjust_foreground_contrast_with_background(text_fg, background);
|
||||
vec4 text_fg_premul = calculate_premul_foreground_from_sprites(text_fg);
|
||||
#ifdef TRANSPARENT
|
||||
final_color = alpha_blend_premul(fg, vec4_premul(background, bg_alpha));
|
||||
|
||||
// Adjust the transparent alpha-channel to account for incorrect
|
||||
// gamma-blending performed by the compositor (true for at least wlroots,
|
||||
// picom, GNOME, MacOS).
|
||||
// This is the last pass:
|
||||
final_color.a = linear2srgb(final_color.a);
|
||||
final_color = alpha_blend_premul(text_fg_premul, vec4_premul(background, bg_alpha));
|
||||
final_color.a = adjust_alpha_for_incorrect_blending_by_compositor(text_fg_premul.a, final_color.a);
|
||||
#else
|
||||
final_color = alpha_blend_premul(fg, background);
|
||||
final_color = alpha_blend_premul(text_fg_premul, background);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -201,11 +208,12 @@ void main() {
|
||||
#endif
|
||||
|
||||
#ifdef FOREGROUND
|
||||
final_color = calculate_foreground(); // pre-multiplied foreground
|
||||
|
||||
// This is the last pass, called both with transparency and without but not in draw_cells_simple().
|
||||
// When transparent it is drawn into a framebuffer and linear2srgb() will be done
|
||||
// when blitting that framebuffer. When not transparent there is not need to do linear2srgb() anyway.
|
||||
vec4 text_fg = load_text_foreground_color();
|
||||
vec4 text_fg_premul = calculate_premul_foreground_from_sprites(text_fg);
|
||||
final_color = text_fg_premul;
|
||||
#ifdef TRANSPARENT
|
||||
final_color.a = adjust_alpha_for_incorrect_blending_by_compositor(text_fg_premul.a, final_color.a);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -988,7 +988,7 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, const ScreenRenderData *srd, float
|
||||
// }}}
|
||||
|
||||
// Borders {{{
|
||||
enum BorderUniforms { BORDER_viewport, BORDER_background_opacity, BORDER_tint_opacity, BORDER_tint_premult, BORDER_colors, BORDER_gamma_lut, BORDER_do_srgb_correction, NUM_BORDER_UNIFORMS };
|
||||
enum BorderUniforms { BORDER_viewport, BORDER_background_opacity, BORDER_tint_opacity, BORDER_tint_premult, BORDER_colors, BORDER_gamma_lut, NUM_BORDER_UNIFORMS };
|
||||
static GLint border_uniform_locations[NUM_BORDER_UNIFORMS] = {0};
|
||||
|
||||
static void
|
||||
@@ -998,7 +998,6 @@ init_borders_program(void) {
|
||||
SET_LOC(background_opacity)
|
||||
SET_LOC(tint_opacity)
|
||||
SET_LOC(tint_premult)
|
||||
SET_LOC(do_srgb_correction)
|
||||
SET_LOC(colors)
|
||||
SET_LOC(gamma_lut)
|
||||
#undef SET_LOC
|
||||
@@ -1022,7 +1021,6 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu
|
||||
float background_opacity = w->is_semi_transparent ? w->background_opacity: 1.0f;
|
||||
float tint_opacity = background_opacity;
|
||||
float tint_premult = background_opacity;
|
||||
float do_srgb_correction = 1.0f;
|
||||
if (has_bgimage(w)) {
|
||||
glEnable(GL_BLEND);
|
||||
BLEND_ONTO_OPAQUE;
|
||||
@@ -1031,7 +1029,6 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu
|
||||
background_opacity = 1.0f;
|
||||
tint_opacity = OPT(background_tint) * OPT(background_tint_gaps);
|
||||
tint_premult = w->is_semi_transparent ? OPT(background_tint) : 1.0f;
|
||||
do_srgb_correction = 0.0f;
|
||||
}
|
||||
|
||||
if (num_border_rects) {
|
||||
@@ -1053,7 +1050,6 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu
|
||||
glUniform1f(border_uniform_locations[BORDER_background_opacity], background_opacity);
|
||||
glUniform1f(border_uniform_locations[BORDER_tint_opacity], tint_opacity);
|
||||
glUniform1f(border_uniform_locations[BORDER_tint_premult], tint_premult);
|
||||
glUniform1f(border_uniform_locations[BORDER_do_srgb_correction], do_srgb_correction);
|
||||
glUniform2ui(border_uniform_locations[BORDER_viewport], viewport_width, viewport_height);
|
||||
glUniform1fv(border_uniform_locations[BORDER_gamma_lut], 256, srgb_lut);
|
||||
if (has_bgimage(w)) {
|
||||
|
||||
Reference in New Issue
Block a user