Simplify and document the operation of the cell fragment shader

Also get rid of the unneccessary double bg_alpha followed by divide by
alpha step when rendering interleaved and premult
This commit is contained in:
Kovid Goyal 2019-06-12 10:22:47 +05:30
parent ae9df1ffac
commit a5aca35f1c
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 49 additions and 24 deletions

View File

@ -7,5 +7,5 @@ out vec4 color;
void main() {
color = texture(image, texcoord);
color = vec4(color.rgb / color.a, color.a);
color = vec4(color.rgb, color.a);
}

View File

@ -51,10 +51,45 @@ vec4 alpha_blend_premul(vec3 over, float over_alpha, vec3 under, float under_alp
float alpha = mix(under_alpha, 1.0f, over_alpha);
return vec4(premul_blend(over, over_alpha, under), alpha);
}
vec4 blend_onto_opaque_premul(vec3 over, float over_alpha, vec3 under) {
// same as alpha_blend_premul with under_alpha = 1 outputs a blended color
// with alpha 1 which is effectively pre-multiplied since alpha is 1
return vec4(premul_blend(over, over_alpha, under), 1.0);
}
// }}}
/*
* Explanation of rendering:
* There are a couple of cases, in order of increasing complexity:
* 1) Simple -- this path is used when there are either no images, or all images are
* drawn on top of text and the background is opaque. In this case, there is a single pass,
* of this shader with cell foreground and background colors blended directly.
* Expected output is a color premultiplied by alpha, with an alpha specified as well.
*
* 2) Interleaved -- this path is used if background is not opaque and there are images or
* if the background is opaque but there are images under text. Rendering happens in
* multiple passes drawing the background and foreground separately and blending.
*
* 2a) Opaque bg with images under text
* There are multiple passes, each pass is blended onto the previous using the opaque blend func (alpha, 1- alpha):
* 1) Draw only the background -- expected output is color with alpha 1
* 2) Draw the images that are supposed to be below text. This happens in the graphics shader
* 3) Draw the special cells (selection/cursor). Output is same as from step 1, with bg_alpha 1 for special cells and 0 otherwise
* 4) Draw the foreground -- expected output is color with alpha which is blended using the opaque blend func
* 5) Draw the images that are supposed to be above text again in the graphics shader
*
* 2b) Transparent bg with images
* First everything is rendered into a framebuffer, and then the framebauffer is blended onto
* the screen. The framebuffer is needed because it allows access to the background color pixels
* to blend with the image pixels. The steps are basically the same as for 2a.
*/
#ifdef NEEDS_FOREGROUND
vec4 calculate_foreground() {
// returns the effective foreground color in pre-multiplied form
vec4 text_fg = texture(sprites, sprite_pos);
vec3 fg = mix(foreground, text_fg.rgb, colored_sprite);
float text_alpha = text_fg.a;
@ -70,15 +105,12 @@ vec4 calculate_foreground() {
#endif
void main() {
#ifdef BACKGROUND
#ifdef SIMPLE
vec4 fg = calculate_foreground();
#ifdef TRANSPARENT
// bg_alpha is doubled to match rendering in the SIMPLE case
// and also rendering of the margin/padding, see https://github.com/kovidgoyal/kitty/pull/1291
// to test use background_opacity, window_margin_width and icat to display
// an image.
final_color = vec4(background.rgb * bg_alpha * bg_alpha, bg_alpha);
final_color = alpha_blend_premul(fg.rgb, fg.a, background.rgb * bg_alpha, bg_alpha);
#else
final_color = vec4(background.rgb, 1.0f);
final_color = blend_onto_opaque_premul(fg.rgb, fg.a, background.rgb);
#endif
#endif
@ -90,29 +122,22 @@ void main() {
#endif
#endif
#if defined(FOREGROUND) || defined(SIMPLE)
// FOREGROUND or SIMPLE
vec4 fg = calculate_foreground(); // pre-multiplied foreground
#ifdef BACKGROUND
#ifdef TRANSPARENT
final_color = vec4(background.rgb * bg_alpha, bg_alpha);
#else
final_color = vec4(background.rgb, 1.0f);
#endif
#endif
#ifdef FOREGROUND
// FOREGROUND
vec4 fg = calculate_foreground(); // pre-multiplied foreground
#ifdef TRANSPARENT
final_color = fg;
#else
final_color = vec4(fg.rgb / fg.a, fg.a);
#endif
#else
// SIMPLE
#ifdef TRANSPARENT
final_color = alpha_blend_premul(fg.rgb, fg.a, background * bg_alpha, bg_alpha);
final_color = vec4(final_color.rgb, final_color.a);
#else
// since background alpha is 1.0, it is effectively pre-multiplied
final_color = vec4(premul_blend(fg.rgb, fg.a, background), 1.0f);
final_color = vec4(final_color.rgb, final_color.a);
#endif
#endif
#endif
}

View File

@ -438,7 +438,7 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen
glDisable(GL_BLEND);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
// Now render the framebuffer to the screen reversing alpha pre-multiplication
// Now render the framebuffer to the screen
glEnable(GL_SCISSOR_TEST);
bind_program(BLIT_PROGRAM); bind_vertex_array(blit_vertex_array);
static bool blit_constants_set = false;