render: Allow cells to bleed into their neighbor #592

Manually merged
dnkl merged 1 commits from clktmr/foot:allow-bleeding-glyphs into master 3 months ago
  1. 11
      CHANGELOG.md
  2. 17
      config.c
  3. 3
      config.h
  4. 34
      doc/foot.ini.5.scd
  5. 78
      render.c
  6. 3
      terminal.h

11
CHANGELOG.md

@ -33,6 +33,9 @@
* `locked-title=no|yes` to `foot.ini`
(https://codeberg.org/dnkl/foot/issues/386).
* `tweak.overflowing-glyphs` option, which can be enabled to fix rendering
issues with glyphs of any width that appear cut-off
(https://codeberg.org/dnkl/foot/issues/592).
### Changed
@ -45,6 +48,12 @@
### Deprecated
### Removed
* The `tweak.allow-overflowing-double-width-glyphs` and
`tweak.pua-double-width` options (which have been superseded by
`tweak.overflowing-glyphs`).
### Fixed
dnkl marked this conversation as resolved
Review

Last thing: I encourage you to add yourself to the "contributors" list. Really nice job!

Last thing: I encourage you to add yourself to the _"contributors_" list. Really nice job!
Review

Thanks! Really enjoyed exploring and hacking on such clean codebase. And you're doing a great job maintaining it, too.

Thanks! Really enjoyed exploring and hacking on such clean codebase. And you're doing a great job maintaining it, too.
* Glyph offset not being taken into account when applying
@ -69,6 +78,8 @@
### Security
### Contributors
* [clktmr](https://codeberg.org/clktmr)
## 1.8.1

17
config.c

@ -2213,16 +2213,10 @@ parse_section_tweak(
return false;
}
else if (strcmp(key, "allow-overflowing-double-width-glyphs") == 0) {
conf->tweak.allow_overflowing_double_width_glyphs = str_to_bool(value);
if (!conf->tweak.allow_overflowing_double_width_glyphs)
LOG_WARN("tweak: disabled overflowing double-width glyphs");
}
else if (strcmp(key, "pua-double-width") == 0) {
conf->tweak.pua_double_width = str_to_bool(value);
if (conf->tweak.pua_double_width)
LOG_WARN("tweak: PUA double width glyphs enabled");
else if (strcmp(key, "overflowing-glyphs") == 0) {
conf->tweak.overflowing_glyphs = str_to_bool(value);
if (!conf->tweak.overflowing_glyphs)
LOG_WARN("tweak: disabled overflowing glyphs");
}
else if (strcmp(key, "damage-whole-window") == 0) {
@ -2833,7 +2827,7 @@ config_load(struct config *conf, const char *conf_path,
.tweak = {
.fcft_filter = FCFT_SCALING_FILTER_LANCZOS3,
.allow_overflowing_double_width_glyphs = true,
.overflowing_glyphs = true,
.grapheme_shaping = false,
.grapheme_width_method = GRAPHEME_WIDTH_DOUBLE,
.delayed_render_lower_ns = 500000, /* 0.5ms */
@ -2844,7 +2838,6 @@ config_load(struct config *conf, const char *conf_path,
.damage_whole_window = false,
.box_drawing_base_thickness = 0.04,
.box_drawing_solid_shades = true,
.pua_double_width = false,
},
.notifications = tll_init(),

3
config.h

@ -245,7 +245,7 @@ struct config {
struct {
enum fcft_scaling_filter fcft_filter;
bool allow_overflowing_double_width_glyphs;
bool overflowing_glyphs;
bool grapheme_shaping;
dnkl marked this conversation as resolved
Review

Isn't there a PUA option here somewhere (that should be removed) as well?

Isn't there a PUA option here somewhere (that should be removed) as well?
Review

pua_double_width in line 259 is removed as well, or am I missing another one?

`pua_double_width` in line 259 is removed as well, or am I missing another one?
Review

No, you're right. I somehow missed it.

No, you're right. I somehow missed it.
enum {GRAPHEME_WIDTH_WCSWIDTH, GRAPHEME_WIDTH_DOUBLE} grapheme_width_method;
bool render_timer_osd;
@ -256,7 +256,6 @@ struct config {
off_t max_shm_pool_size;
float box_drawing_base_thickness;
bool box_drawing_solid_shades;
bool pua_double_width;
} tweak;
user_notifications_t notifications;

34
doc/foot.ini.5.scd

@ -847,34 +847,24 @@ any of these options.
Default: _lanczos3_.
*allow-overflowing-double-width-glyphs*
Boolean. When enabled, double width glyphs with a character width
of 1 are allowed to overflow into the neighbouring cell.
*overflowing-glyphs*
Boolean. When enabled, glyphs wider than their cell(s) are allowed
to render into one additional neighbouring cell.
One use case for this is fonts "icon" characters in the Unicode
private usage area, e.g. Nerd Fonts, or Powerline Fonts. Without
this option, such glyphs will appear "cut off".
One use case for this are fonts with wide italic characters that
"bend" into the next cell. Without this option, such glyphs will
appear "cut off".
Another use case are legacy emoji characters like *WHITE FROWNING
FACE*.
Another use case are fonts with "icon" characters in the Unicode
private usage area, e.g. Nerd Fonts, or Powerline Fonts and legacy
emoji characters like *WHITE FROWNING FACE*.
Note: this feature uses _heuristics_ to determine *which* glyphs
should be allowed to overflow.
See also: *pua-double-width*
Note: might impact performance depending on the font used.
Especially small font sizes can cause many overflowing glyphs
because of subpixel rendering.
Default: _yes_.
*pua-double-width*
Boolean. When enabled, Unicode code points from the private usage
area (PUA) are always considered to be double width, regardless of
the actual glyph width.
Ignored if *allow-overflowing-double-width-glyphs* has been
disabled.
Default: _no_.
*render-timer*
Enables a frame rendering timer, that prints the time it takes to
render each frame, in microseconds, either on-screen, to stderr,

78
render.c

@ -437,6 +437,20 @@ draw_cursor(const struct terminal *term, const struct cell *cell,
}
}
static inline void
render_cell_prepass(struct terminal *term, struct row *row, int col)
{
for (; col < term->cols - 1; col++) {
if (row->cells[col].attrs.confined ||
(row->cells[col].attrs.clean == row->cells[col + 1].attrs.clean)) {
break;
}
row->cells[col].attrs.clean = 0;
row->cells[col + 1].attrs.clean = 0;
}
}
static int
render_cell(struct terminal *term, pixman_image_t *pix,
struct row *row, int col, int row_no, bool has_cursor)
@ -446,6 +460,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
return 0;
cell->attrs.clean = 1;
cell->attrs.confined = true;
int width = term->cell_width;
int height = term->cell_height;
@ -597,51 +612,32 @@ render_cell(struct terminal *term, pixman_image_t *pix,
cell_cols = max(1, min(cell_cols, cols_left));
/*
* Hack!
*
* Deal with double-width glyphs for which wcwidth() returns
* 1. Typically Unicode private usage area characters,
* e.g. powerline, or nerd hack fonts.
*
* Users can enable a tweak option that lets this glyphs
* overflow/bleed into the neighbouring cell.
*
* We only apply this workaround if:
* - the user has explicitly enabled this feature
* - the *character* width is 1
* - the *glyph* width is at least 1.5 cells
* - the *glyph* width is less than 3 cells
* - *this* column isnt the last column
* - *this* cells is followed by an empty cell, or a space
* Determine cells that will bleed into their right neighbor and remember
* them for cleanup in the next frame.
*/
if (term->conf->tweak.allow_overflowing_double_width_glyphs &&
glyph_count > 0 &&
cell_cols == 1 &&
col < term->cols - 1 &&
((glyphs[0]->x + glyphs[0]->width >= term->cell_width * 15 / 10 &&
glyphs[0]->x + glyphs[0]->width < 3 * term->cell_width) ||
(term->conf->tweak.pua_double_width &&
((base >= 0x00e000 && base <= 0x00f8ff) ||
(base >= 0x0f0000 && base <= 0x0ffffd) ||
(base >= 0x100000 && base <= 0x10fffd)))) &&
(row->cells[col + 1].wc == 0 || row->cells[col + 1].wc == L' '))
int render_width = cell_cols * width;
if (term->conf->tweak.overflowing_glyphs &&
glyph_count > 0)
{
cell_cols = 2;
int glyph_width = 0, advance = 0;
for (size_t i = 0; i < glyph_count; i++) {
glyph_width = max(glyph_width,
advance + glyphs[i]->x + glyphs[i]->width);
advance += glyphs[i]->advance.x;
}
/*
* Ensure the cell were overflowing into gets re-rendered, to
* ensure it is erased if *this* cell is erased. Note that we
* do *not* mark the row as dirty - we dont need to re-render
* the cell if nothing else on the row has changed.
*/
row->cells[col].attrs.clean = 0;
row->cells[col + 1].attrs.clean = 0;
if (glyph_width > render_width) {
render_width = min(glyph_width, render_width + width);
for (int i = 0; i < cell_cols; i++)
row->cells[col + i].attrs.confined = false;
}
}
pixman_region32_t clip;
pixman_region32_init_rect(
&clip, x, y,
cell_cols * term->cell_width, term->cell_height);
render_width, term->cell_height);
pixman_image_set_clip_region32(pix, &clip);
/* Background */
@ -771,6 +767,10 @@ static void
render_row(struct terminal *term, pixman_image_t *pix, struct row *row,
int row_no, int cursor_col)
{
if (term->conf->tweak.overflowing_glyphs)
for (int col = term->cols - 1; col >= 0; col--)
render_cell_prepass(term, row, col);
for (int col = term->cols - 1; col >= 0; col--)
render_cell(term, pix, row, col, row_no, cursor_col == col);
}
@ -1192,8 +1192,10 @@ render_sixel(struct terminal *term, pixman_image_t *pix,
(last_col_needs_erase && last_col))
{
render_cell(term, pix, row, col, term_row_no, cursor_col == col);
} else
} else {
cell->attrs.clean = 1;
cell->attrs.confined = 1;
}
}
}
}

3
terminal.h

@ -42,11 +42,12 @@ struct attributes {
uint32_t fg:24;
bool clean:1;
bool confined:1;
bool have_fg:1;
bool have_bg:1;
uint32_t selected:2;
bool url:1;
uint32_t reserved:2;
uint32_t reserved:1;
uint32_t bg:24;
};
static_assert(sizeof(struct attributes) == 8, "VT attribute struct too large");

Loading…
Cancel
Save