Port rendering of scroll indicator

This commit is contained in:
Kovid Goyal
2025-08-04 14:40:25 +05:30
parent a9585d7324
commit 23b3c83a84
5 changed files with 80 additions and 48 deletions

View File

@@ -25,6 +25,7 @@ in vec4 cursor_color_premult;
in vec3 decoration_fg;
in float colored_sprite;
in float cell_has_default_bg;
in vec2 layer_pos;
out vec4 output_color;
@@ -91,9 +92,7 @@ vec4 adjust_foreground_contrast_with_background(vec4 text_fg, vec3 bg) {
}
vec4 layer_color(sampler2D s) {
// we need to clamp the co-ordinates as the blank texture has size 1x1
ivec2 pos = clamp(ivec2(gl_FragCoord.xy), ivec2(0), textureSize(s, 0)-1);
return texelFetch(s, pos, 0);
return texture(s, layer_pos);
}
#ifdef HAS_LAYERS

View File

@@ -50,6 +50,7 @@ out vec3 decoration_fg;
out float colored_sprite;
out float effective_text_alpha;
out float cell_has_default_bg;
out vec2 layer_pos; // position to sample layer texture at
// Utility functions {{{
@@ -167,10 +168,12 @@ CellData set_vertex_position() {
/* The position of this vertex, at a corner of the cell */
float left = -1.0 + column * dx;
float top = 1.0 - row * dy;
vec2 xpos = vec2(left, left + dx);
vec2 ypos = vec2(top, top - dy);
uvec2 pos = cell_pos_map[gl_VertexID];
gl_Position = vec4(xpos[pos.x], ypos[pos.y], 0, 1);
gl_Position = vec4(vec2(left, left + dx)[pos.x], vec2(top, top - dy)[pos.y], 0, 1);
// The position used to sample the layer textures
dx /= 2.0; dy /= 2.0;
left = 0 + column * dx; top = 1.0 - row * dy;
layer_pos = vec2(vec2(left, left + dx)[pos.x], vec2(top, top - dy)[pos.y]);
// The character sprite being rendered
sprite_pos = to_sprite_pos(pos, sprite_idx[0] & SPRITE_INDEX_MASK);
colored_sprite = float((sprite_idx[0] & SPRITE_COLORED_MASK) >> SPRITE_COLORED_SHIFT);

View File

@@ -102,8 +102,17 @@ typedef struct {
color_type cursor_bg;
struct {
struct { float intensity; color_type color; } visual_bell;
struct { float frac, alpha; color_type color; unsigned cell_height; } scroll_bar;
struct { float frac, alpha; color_type color; unsigned cell_height, cell_width; } scroll_bar;
} ui_layer;
struct {
int x;
} over_fg_layer;
struct {
int x;
} under_fg_layer;
struct {
int x;
} under_bg_layer;
} last_rendered;
bool is_dirty, scroll_changed, reload_all_gpu_data;
Cursor *cursor;

View File

@@ -13,6 +13,7 @@
#include "window_logo.h"
#include "srgb_gamma.h"
#include "uniforms_generated.h"
#include "png-reader.h"
/*
* TODO: for shader refactoring
@@ -684,8 +685,7 @@ get_visual_bell_intensity(Screen *screen) {
}
typedef struct UIRenderData {
unsigned screen_width, screen_height, cell_width, cell_height;
unsigned screen_width, screen_height, cell_width, cell_height, screen_left, screen_top;
} UIRenderData;
static void
@@ -708,6 +708,37 @@ draw_visual_bell_flash(GLfloat intensity, const color_type flash) {
glDisable(GL_BLEND);
}
static bool
draw_scroll_indicator(color_type bar_color, GLfloat alpha, float frac, UIRenderData ui) {
glEnable(GL_BLEND);
BLEND_PREMULT;
bind_program(TINT_PROGRAM);
#define C(shift) srgb_color((bar_color >> shift) & 0xFF) * alpha
glUniform4f(tint_program_layout.uniforms.tint_color, C(16), C(8), C(0), alpha);
#undef C
float bar_width = 0.5f * gl_size(ui.cell_width, ui.screen_width);
float bar_height = gl_size(ui.cell_height, ui.screen_height);
float bottom = -1.f + MAX(0, 2.f - bar_height) * frac;
glUniform4f(tint_program_layout.uniforms.edges, 1.f - bar_width, bottom + bar_height, 1.f, bottom);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisable(GL_BLEND);
return true;
}
static void
save_texture_as_png(GLuint texture_id, unsigned width, unsigned height, const char *filename) {
glBindTexture(GL_TEXTURE_2D, texture_id);
size_t sz = width * height * sizeof(uint32_t);
uint32_t* data = malloc(sz);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
const char *png = png_from_32bit_rgba(data, width, height, &sz, true);
if (!sz) fatal("Failed to save PNG to %s with error: %s", filename, png);
free(data);
FILE* file = fopen(filename, "wb");
fwrite(png, 1, sz, file);
fclose(file);
}
static bool
update_ui_layer(Screen *screen, UIRenderData ui, bool visual_bell_drawn, bool scrollback_indicator_drawn) {
@@ -719,9 +750,10 @@ update_ui_layer(Screen *screen, UIRenderData ui, bool visual_bell_drawn, bool sc
flash = !IS_SPECIAL_COLOR(highlight_bg) ? COLOR(visual_bell_color, highlight_bg) : COLOR(visual_bell_color, default_fg);
#undef COLOR
}
bool visual_bell_needs_redraw = intensity != last_ui.visual_bell.intensity || flash != last_ui.visual_bell.color;
last_ui.visual_bell.intensity = intensity;
last_ui.visual_bell.color = flash;
#define lvb last_ui.visual_bell
bool visual_bell_needs_redraw = intensity != lvb.intensity || flash != lvb.color;
lvb.intensity = intensity;
lvb.color = flash;
color_type bar_color = 0;
GLfloat bar_alpha = OPT(scrollback_indicator_opacity);
float bar_frac = 0;
@@ -729,18 +761,23 @@ update_ui_layer(Screen *screen, UIRenderData ui, bool visual_bell_drawn, bool sc
bar_color = colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.highlight_bg, screen->color_profile->configured.highlight_bg).rgb;
bar_frac = (float)screen->scrolled_by / (float)screen->historybuf->count;
}
bool scrollbar_needs_redraw = (last_ui.scroll_bar.frac != bar_frac || last_ui.scroll_bar.color != bar_color ||
last_ui.scroll_bar.cell_height != ui.cell_height || last_ui.scroll_bar.alpha != bar_alpha);
last_ui.scroll_bar.alpha = bar_alpha;
last_ui.scroll_bar.cell_height = ui.cell_height;
last_ui.scroll_bar.color = bar_color;
last_ui.scroll_bar.frac = bar_frac;
#define lsb last_ui.scroll_bar
bool scrollbar_needs_redraw = (lsb.frac != bar_frac || lsb.color != bar_color ||
lsb.cell_height != ui.cell_height || lsb.alpha != bar_alpha || lsb.cell_width != ui.cell_width);
lsb.alpha = bar_alpha; lsb.cell_height = ui.cell_height; lsb.cell_width = ui.cell_width; lsb.color = bar_color;
lsb.frac = bar_frac;
if (!scrollbar_needs_redraw && !visual_bell_needs_redraw) return false;
blank_canvas(0, 0); // clear the framebuffer
save_viewport_using_bottom_left_origin(0, 0, ui.screen_width, ui.screen_height);
if (visual_bell_drawn && intensity > 0) draw_visual_bell_flash(last_ui.visual_bell.intensity, last_ui.visual_bell.color);
if (visual_bell_drawn && intensity > 0) draw_visual_bell_flash(lvb.intensity, lvb.color);
if (scrollback_indicator_drawn) draw_scroll_indicator(bar_color, bar_alpha, bar_frac, ui);
restore_viewport();
if (0) save_texture_as_png(screen->textures.ui.id, ui.screen_width, ui.screen_height, "/tmp/ui.png");
return true;
#undef lsb
#undef lvb
#undef last_ui
}
@@ -755,7 +792,7 @@ draw_cells_with_layers(
#define USE_BLANK(which) { if (screen->textures.which.id) { free_texture(&screen->textures.which.id); } glBindTexture(GL_TEXTURE_2D, blank_texture); }
#define ENSURE_TEXTURE(which) \
has_layers = true; \
has_layers = true; bool texture_reset = false; \
if (screen->textures.which.width != ui.screen_width || screen->textures.which.height != ui.screen_height) { \
if (screen->textures.which.id) free_texture(&screen->textures.which.id); \
if (screen->textures.which.framebuffer_id) free_framebuffer(&screen->textures.which.framebuffer_id); \
@@ -773,6 +810,7 @@ draw_cells_with_layers(
glBindFramebuffer(GL_FRAMEBUFFER, screen->textures.which.framebuffer_id); \
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, screen->textures.which.id, 0); \
check_framebuffer_status_or_die(); \
texture_reset = true; \
} \
glBindTexture(GL_TEXTURE_2D, screen->textures.which.id); \
glBindFramebuffer(GL_FRAMEBUFFER, screen->textures.which.framebuffer_id); \
@@ -780,17 +818,20 @@ draw_cells_with_layers(
glActiveTexture(GL_TEXTURE0 + UNDER_BG_LAYER_UNIT);
if (grd.num_of_below_refs || has_background_image) {
ENSURE_TEXTURE(under_bg);
if (texture_reset) zero_at_ptr(&screen->last_rendered.under_bg_layer);
has_under_bg = 1.;
} else USE_BLANK(under_bg);
glActiveTexture(GL_TEXTURE0 + UNDER_FG_LAYER_UNIT);
if (grd.num_of_below_refs || wl) {
ENSURE_TEXTURE(under_fg);
if (texture_reset) zero_at_ptr(&screen->last_rendered.under_fg_layer);
} else USE_BLANK(under_fg);
glActiveTexture(GL_TEXTURE0 + OVER_FG_LAYER_UNIT);
if (grd.num_of_positive_refs) {
ENSURE_TEXTURE(over_fg);
if (texture_reset) zero_at_ptr(&screen->last_rendered.over_fg_layer);
} else USE_BLANK(over_fg);
glActiveTexture(GL_TEXTURE0 + UI_LAYER_UNIT);
@@ -798,6 +839,7 @@ draw_cells_with_layers(
bool scrollback_indicator_drawn = !(OPT(scrollback_indicator_opacity) <= 0 || screen->linebuf != screen->main_linebuf || !screen->scrolled_by);
if (visual_bell_drawn || scrollback_indicator_drawn) {
ENSURE_TEXTURE(ui);
if (texture_reset) zero_at_ptr(&screen->last_rendered.ui_layer);
update_ui_layer(screen, ui, visual_bell_drawn, scrollback_indicator_drawn);
} else USE_BLANK(ui);
#undef ENSURE_TEXTURE
@@ -823,30 +865,6 @@ draw_cells_with_layers(
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns);
}
static bool
draw_scroll_indicator(bool premult, Screen *screen, const CellRenderData *crd) {
glEnable(GL_BLEND);
if (premult) { BLEND_PREMULT } else { BLEND_ONTO_OPAQUE }
bind_program(TINT_PROGRAM);
const color_type bar_color = colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.highlight_bg, screen->color_profile->configured.highlight_bg).rgb;
GLfloat alpha = OPT(scrollback_indicator_opacity);
float frac = (float)screen->scrolled_by / (float)screen->historybuf->count;
const GLfloat bar_height = crd->gl.dy;
GLfloat bottom = (crd->gl.ystart - crd->gl.height);
bottom += MAX(0, crd->gl.height - bar_height) * frac;
#define C(shift) srgb_color((bar_color >> shift) & 0xFF) * premult_factor
GLfloat premult_factor = premult ? alpha : 1.0f;
glUniform4f(tint_program_layout.uniforms.tint_color, C(16), C(8), C(0), alpha);
#undef C
GLfloat width = 0.5f * crd->gl.dx;
GLfloat left = (GLfloat)(crd->gl.xstart + (screen->columns * crd->gl.dx - width));
glUniform4f(tint_program_layout.uniforms.edges, left, bottom, left + width, bottom + bar_height);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisable(GL_BLEND);
return true;
}
static float prev_inactive_text_alpha = -1;
static void
@@ -1071,10 +1089,13 @@ draw_cells(bool for_final_output, const WindowRenderData *srd, OSWindow *os_wind
has_underlying_image |= grd.num_of_below_refs > 0 || grd.num_of_negative_refs > 0;
(void)has_underlying_image;
bool is_semi_transparent = os_window->is_semi_transparent && min_bg_opacity < 1.;
UIRenderData ui = {srd->geometry.right - srd->geometry.left, srd->geometry.bottom - srd->geometry.top, os_window->fonts_data->fcm.cell_width, os_window->fonts_data->fcm.cell_height};
save_viewport_using_top_left_origin(srd->geometry.left, srd->geometry.top, ui.screen_width, ui.screen_height);
UIRenderData ui = {
srd->geometry.right - srd->geometry.left, srd->geometry.bottom - srd->geometry.top,
os_window->fonts_data->fcm.cell_width, os_window->fonts_data->fcm.cell_height,
srd->geometry.left, srd->geometry.top,
};
save_viewport_using_top_left_origin(ui.screen_left, ui.screen_top, ui.screen_width, ui.screen_height);
draw_cells_with_layers(for_final_output, os_window, screen, ui, is_semi_transparent, grd, wl);
draw_scroll_indicator(is_semi_transparent, screen, &crd);
if (window && screen->display_window_char) draw_window_number(os_window, screen, &crd, window);
if (OPT(show_hyperlink_targets) && window && screen->current_hyperlink_under_mouse.id && !is_mouse_hidden(os_window)) draw_hyperlink_target(os_window, screen, &crd, window);
free(scaled_render_data);

View File

@@ -1,5 +1,5 @@
uniform vec4 tint_color;
out vec4 color; // must be in linear space
out vec4 color; // must be in linear space and pre-multiplied
void main() {
color = tint_color;