Implement underline exclusion

This commit is contained in:
Kovid Goyal 2024-12-16 08:35:32 +05:30
parent eb322913c3
commit 1707e603f3
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
6 changed files with 52 additions and 15 deletions

View file

@ -15,6 +15,7 @@ in vec3 sprite_pos;
in vec3 underline_pos;
in vec3 cursor_pos;
in vec3 strike_pos;
in vec3 underline_exclusion_pos;
in vec3 foreground;
in vec4 cursor_color_premult;
in vec3 decoration_fg;
@ -128,6 +129,8 @@ vec4 load_text_foreground_color() {
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 underline_exclusion = texture(sprites, underline_exclusion_pos).a;
underline_alpha *= 1.0f - underline_exclusion;
float strike_alpha = texture(sprites, strike_pos).a;
float cursor_alpha = texture(sprites, cursor_pos).a;
// Since strike and text are the same color, we simply add the alpha values

View file

@ -8,7 +8,7 @@ layout(std140) uniform CellRenderData {
uint default_fg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted;
uint xnum, ynum, sprites_xnum, sprites_ynum, cursor_fg_sprite_idx;
uint xnum, ynum, sprites_xnum, sprites_ynum, cursor_fg_sprite_idx, cell_height;
float cursor_x, cursor_y, cursor_w, cursor_opacity;
// must have unique entries with 0 being default_bg and unset being UINT32_MAX
@ -51,6 +51,7 @@ out vec3 underline_pos;
out vec3 cursor_pos;
out vec4 cursor_color_premult;
out vec3 strike_pos;
out vec3 underline_exclusion_pos;
out vec3 foreground;
out vec3 decoration_fg;
out float colored_sprite;
@ -108,9 +109,18 @@ vec3 to_sprite_pos(uvec2 pos, uint idx) {
uvec3 c = to_sprite_coords(idx);
vec2 s_xpos = vec2(c.x, float(c.x) + 1.0f) * (1.0f / float(sprites_xnum));
vec2 s_ypos = vec2(c.y, float(c.y) + 1.0f) * (1.0f / float(sprites_ynum));
uint texture_height_px = (cell_height + 1u) * sprites_ynum;
float row_height = 1.0f / float(texture_height_px);
s_ypos[1] -= row_height; // skip the decorations_exclude row
return vec3(s_xpos[pos.x], s_ypos[pos.y], c.z);
}
vec3 to_underline_exclusion_pos(uvec2 pos, vec3 sprite_pos) {
uvec3 c = to_sprite_coords(sprite_idx[0]);
float y = (float(c.y) + 1.0f) * (1.0f / float(sprites_ynum));
return vec3(sprite_pos.x, y, sprite_pos.z);
}
uint read_sprite_decorations_idx() {
int idx = int(sprite_idx[0] & SPRITE_INDEX_MASK);
ivec2 sz = textureSize(sprite_decorations_map, 0);
@ -232,6 +242,7 @@ void main() {
uvec2 decs = get_decorations_indices(uint(in_url), text_attrs);
strike_pos = to_sprite_pos(cell_data.pos, decs[0]);
underline_pos = to_sprite_pos(cell_data.pos, decs[1]);
underline_exclusion_pos = to_underline_exclusion_pos(cell_data.pos, sprite_pos);
// Cursor
cursor_color_premult = vec4(color_to_vec(cursor_bg) * cursor_opacity, cursor_opacity);

View file

@ -121,12 +121,13 @@ add_dashed_underline(uint8_t *buf, FontCellMetrics fcm) {
return ans;
}
static void
static unsigned
add_intensity(uint8_t *buf, unsigned x, unsigned y, uint8_t val, unsigned max_y, unsigned position, unsigned cell_width) {
y += position;
y = MIN(y, max_y);
unsigned idx = cell_width * y + x;
buf[idx] = MIN(255, buf[idx] + val);
return y;
}
DecorationGeometry
@ -152,10 +153,12 @@ add_curl_underline(uint8_t *buf, FontCellMetrics fcm) {
for (unsigned x = 0; x < fcm.cell_width; x++) {
double y = half_height * cos(x * xfactor);
unsigned y1 = (unsigned)floor(y - thickness), y2 = (unsigned)ceil(y);
unsigned i1 = (unsigned)(255. * fabs(y - floor(y)));
miny = MIN(miny, y1); maxy = MAX(maxy, y2);
add_intensity(buf, x, y1, 255 - i1, max_y, position, fcm.cell_width); // upper bound
add_intensity(buf, x, y2, i1, max_y, position, fcm.cell_width); // lower bound
unsigned intensity = (unsigned)(255. * fabs(y - floor(y)));
unsigned i1 = 255 - intensity, i2 = intensity;
unsigned yc = add_intensity(buf, x, y1, i1, max_y, position, fcm.cell_width); // upper bound
if (i1) { if (yc < miny) miny = yc; if (yc > maxy) maxy = yc; }
yc = add_intensity(buf, x, y2, i2, max_y, position, fcm.cell_width); // lower bound
if (i2) { if (yc < miny) miny = yc; if (yc > maxy) maxy = yc; }
// fill between upper and lower bound
for (unsigned t = 1; t <= thickness; t++) add_intensity(buf, x, y1 + t, 255, max_y, position, fcm.cell_width);
}

View file

@ -161,7 +161,7 @@ python_send_to_gpu(FontGroup *fg, sprite_index idx, pixel *buf) {
static void
ensure_canvas_can_fit(FontGroup *fg, unsigned cells, unsigned scale) {
#define cs(cells, scale) (sizeof(fg->canvas.buf[0]) * 3u * cells * fg->fcm.cell_width * fg->fcm.cell_height * scale * scale)
#define cs(cells, scale) (sizeof(fg->canvas.buf[0]) * 3u * cells * fg->fcm.cell_width * (fg->fcm.cell_height + 1) * scale * scale)
size_t size_in_bytes = cs(cells, scale);
if (size_in_bytes > fg->canvas.size_in_bytes) {
free(fg->canvas.buf);
@ -335,11 +335,28 @@ sprite_tracker_set_layout(GPUSpriteTracker *sprite_tracker, unsigned int cell_wi
sprite_tracker->x = 0; sprite_tracker->y = 0; sprite_tracker->z = 0;
}
static void
calculate_underline_exclusion_zones(pixel *buf, const FontGroup *fg, DecorationGeometry dg) {
pixel *ans = buf + fg->fcm.cell_height * fg->fcm.cell_width;
const unsigned bottom = MIN(dg.top + dg.height, fg->fcm.cell_height);
const unsigned thickness = MAX(1u, fg->fcm.underline_thickness / 1);
for (unsigned x = 0; x < fg->fcm.cell_width; x++) {
for (unsigned y = dg.top + 2; y < bottom && !ans[x]; y++) {
if ((buf[y * fg->fcm.cell_width + x] & 0x000000ff) > 0) {
unsigned start_x = x > thickness ? x - thickness : 0;
for (unsigned dx = start_x; dx < MIN(x + thickness, fg->fcm.cell_width); dx++) ans[dx] = 0xffffffff;
break;
}
}
}
}
static sprite_index
current_send_sprite_to_gpu(FontGroup *fg, pixel *buf, DecorationMetadata dec) {
sprite_index ans = current_sprite_index(&fg->sprite_tracker);
if (!do_increment(fg)) return 0;
if (python_send_to_gpu_impl) { python_send_to_gpu(fg, ans, buf); return ans; }
if (dec.underline_region.height) calculate_underline_exclusion_zones(buf, fg, dec.underline_region);
send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, ans, buf, dec.start_idx);
if (0) { printf("Sprite: %u dec_idx: %u\n", ans, dec.start_idx); display_rgba_data(buf, fg->fcm.cell_width, fg->fcm.cell_height); printf("\n"); }
return ans;
@ -832,7 +849,7 @@ apply_scale_to_font_group(FontGroup *fg, RunFont *rf) {
static pixel*
pointer_to_space_for_last_sprite(Canvas *canvas, FontCellMetrics fcm) {
return canvas->buf + (canvas->size_in_bytes / sizeof(canvas->buf[0]) - fcm.cell_width * fcm.cell_height);
return canvas->buf + (canvas->size_in_bytes / sizeof(canvas->buf[0]) - fcm.cell_width * (fcm.cell_height + 1));
}
static pixel*
@ -896,12 +913,14 @@ map_scaled_decoration_geometry(DecorationGeometry sdg, Region src, Region dest)
unsigned unscaled_top = dest.top + (scaled_top - src.top);
unsigned unscaled_bottom = unscaled_top + (scaled_bottom > scaled_top ? scaled_bottom - scaled_top : 0);
unscaled_bottom = MIN(unscaled_bottom, dest.bottom);
/*printf("111111 src: (%u, %u) dest: (%u, %u) sdg: (%u, %u) scaled: (%u, %u) unscaled: (%u, %u)\n",*/
/* src.top, src.bottom, dest.top, dest.bottom, sdg.top, sdg.top + sdg.height, scaled_top, scaled_bottom, unscaled_top, unscaled_bottom);*/
return (Region){.top=unscaled_top, .bottom=MAX(unscaled_top, unscaled_bottom)};
}
static void
render_scaled_decoration(FontCellMetrics unscaled_metrics, FontCellMetrics scaled_metrics, uint8_t *alpha_mask, pixel *output, Region src, Region dest) {
memset(output, 0, unscaled_metrics.cell_width * unscaled_metrics.cell_height * sizeof(output[0]));
memset(output, 0, unscaled_metrics.cell_width * (unscaled_metrics.cell_height + 1) * sizeof(output[0]));
unsigned src_limit = MIN(scaled_metrics.cell_height, src.bottom), dest_limit = MIN(unscaled_metrics.cell_height, dest.bottom);
unsigned cell_width = MIN(scaled_metrics.cell_width, unscaled_metrics.cell_width);
for (unsigned srcy = src.top, desty=dest.top; srcy < src_limit && desty < dest_limit; srcy++, desty++) {
@ -918,7 +937,7 @@ render_decorations(FontGroup *fg, Region src, Region dest, FontCellMetrics scale
const FontCellMetrics unscaled_metrics = fg->fcm;
scaled_metrics.cell_width = unscaled_metrics.cell_width;
RAII_ALLOC(uint8_t, alpha_mask, malloc(scaled_metrics.cell_height * scaled_metrics.cell_width));
RAII_ALLOC(pixel, buf, malloc(unscaled_metrics.cell_width * unscaled_metrics.cell_height * sizeof(pixel)));
RAII_ALLOC(pixel, buf, malloc(unscaled_metrics.cell_width * (unscaled_metrics.cell_height + 1) * sizeof(pixel)));
if (!alpha_mask || !buf) fatal("Out of memory");
sprite_index ans = 0;
bool is_underline = false; uint32_t underline_top = unscaled_metrics.cell_height, underline_bottom = 0;

View file

@ -173,7 +173,7 @@ realloc_sprite_texture(FONTS_DATA_HANDLE fg) {
sprite_tracker_current_layout(fg, &xnum, &ynum, &z);
znum = z + 1;
SpriteMap *sprite_map = (SpriteMap*)fg->sprite_map;
width = xnum * fg->fcm.cell_width; height = ynum * fg->fcm.cell_height;
width = xnum * fg->fcm.cell_width; height = ynum * (fg->fcm.cell_height + 1);
const GLenum texture_type = GL_TEXTURE_2D_ARRAY;
GLuint tex = setup_new_sprites_texture(texture_type);
glTexStorage3D(texture_type, 1, GL_SRGB8_ALPHA8, width, height, znum);
@ -223,8 +223,8 @@ send_sprite_to_gpu(FONTS_DATA_HANDLE fg, sprite_index idx, pixel *buf, sprite_in
glBindTexture(GL_TEXTURE_2D_ARRAY, sprite_map->texture_id);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
sprite_index_to_pos(idx, xnum, ynum, &x, &y, &z);
x *= fg->fcm.cell_width; y *= fg->fcm.cell_height;
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, x, y, z, fg->fcm.cell_width, fg->fcm.cell_height, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, buf);
x *= fg->fcm.cell_width; y *= (fg->fcm.cell_height + 1);
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, x, y, z, fg->fcm.cell_width, fg->fcm.cell_height + 1, 1, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, buf);
}
void
@ -368,7 +368,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c
GLuint default_fg, highlight_fg, highlight_bg, cursor_fg, cursor_bg, url_color, url_style, inverted;
GLuint xnum, ynum, sprites_xnum, sprites_ynum, cursor_fg_sprite_idx;
GLuint xnum, ynum, sprites_xnum, sprites_ynum, cursor_fg_sprite_idx, cell_height;
GLfloat cursor_x, cursor_y, cursor_w, cursor_opacity;
GLuint bg_colors0, bg_colors1, bg_colors2, bg_colors3, bg_colors4, bg_colors5, bg_colors6, bg_colors7;
GLfloat bg_opacities0, bg_opacities1, bg_opacities2, bg_opacities3, bg_opacities4, bg_opacities5, bg_opacities6, bg_opacities7;
@ -459,6 +459,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c
sprite_tracker_current_layout(os_window->fonts_data, &x, &y, &z);
rd->sprites_xnum = x; rd->sprites_ynum = y;
rd->inverted = screen_invert_colors(screen) ? 1 : 0;
rd->cell_height = os_window->fonts_data->fcm.cell_height;
#undef COLOR
rd->url_color = OPT(url_color); rd->url_style = OPT(url_style);

View file

@ -283,7 +283,7 @@ class Rendering(FontBaseTest):
def test_sprite_map(self):
sprite_map_set_limits(10, 3)
sprite_map_set_layout(5, 5)
sprite_map_set_layout(5, 4) # 4 because of underline_exclusion row
self.ae(test_sprite_position_increment(), (0, 0, 0))
self.ae(test_sprite_position_increment(), (1, 0, 0))
self.ae(test_sprite_position_increment(), (0, 1, 0))