mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-03-24 09:05:48 -04:00
render: Allow cells to bleed into their neighbor
This patch adds a `confined` flag to each cell to track if the last rendered glyph bled into it's right neighbor. To keep things simple, bleeding into any other neighbor cell than the immediate right one is not allowed. This should cover most use cases. Before rendering a row we now do a prepass and mark all cells unclean that are affected by a bleeding neighbor. If there are consecutive bleeding cells, the whole group must be re-rendered even if only a single cell has changed. The patch also deprecates both old overflowing glyph options *allow-overflowing-double-width-glyphs* and *pua-double-width* in favor of a single new one named *overflowing-glyphs*.
This commit is contained in:
parent
9211ee694c
commit
91801ae55d
6 changed files with 71 additions and 75 deletions
11
CHANGELOG.md
11
CHANGELOG.md
|
|
@ -33,6 +33,9 @@
|
||||||
|
|
||||||
* `locked-title=no|yes` to `foot.ini`
|
* `locked-title=no|yes` to `foot.ini`
|
||||||
(https://codeberg.org/dnkl/foot/issues/386).
|
(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
|
### Changed
|
||||||
|
|
@ -45,6 +48,12 @@
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
* The `tweak.allow-overflowing-double-width-glyphs` and
|
||||||
|
`tweak.pua-double-width` options (which have been superseded by
|
||||||
|
`tweak.overflowing-glyphs`).
|
||||||
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* Glyph offset not being taken into account when applying
|
* Glyph offset not being taken into account when applying
|
||||||
|
|
@ -69,6 +78,8 @@
|
||||||
### Security
|
### Security
|
||||||
### Contributors
|
### Contributors
|
||||||
|
|
||||||
|
* [clktmr](https://codeberg.org/clktmr)
|
||||||
|
|
||||||
|
|
||||||
## 1.8.1
|
## 1.8.1
|
||||||
|
|
||||||
|
|
|
||||||
17
config.c
17
config.c
|
|
@ -2213,16 +2213,10 @@ parse_section_tweak(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (strcmp(key, "allow-overflowing-double-width-glyphs") == 0) {
|
else if (strcmp(key, "overflowing-glyphs") == 0) {
|
||||||
conf->tweak.allow_overflowing_double_width_glyphs = str_to_bool(value);
|
conf->tweak.overflowing_glyphs = str_to_bool(value);
|
||||||
if (!conf->tweak.allow_overflowing_double_width_glyphs)
|
if (!conf->tweak.overflowing_glyphs)
|
||||||
LOG_WARN("tweak: disabled overflowing double-width glyphs");
|
LOG_WARN("tweak: disabled overflowing 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, "damage-whole-window") == 0) {
|
else if (strcmp(key, "damage-whole-window") == 0) {
|
||||||
|
|
@ -2833,7 +2827,7 @@ config_load(struct config *conf, const char *conf_path,
|
||||||
|
|
||||||
.tweak = {
|
.tweak = {
|
||||||
.fcft_filter = FCFT_SCALING_FILTER_LANCZOS3,
|
.fcft_filter = FCFT_SCALING_FILTER_LANCZOS3,
|
||||||
.allow_overflowing_double_width_glyphs = true,
|
.overflowing_glyphs = true,
|
||||||
.grapheme_shaping = false,
|
.grapheme_shaping = false,
|
||||||
.grapheme_width_method = GRAPHEME_WIDTH_DOUBLE,
|
.grapheme_width_method = GRAPHEME_WIDTH_DOUBLE,
|
||||||
.delayed_render_lower_ns = 500000, /* 0.5ms */
|
.delayed_render_lower_ns = 500000, /* 0.5ms */
|
||||||
|
|
@ -2844,7 +2838,6 @@ config_load(struct config *conf, const char *conf_path,
|
||||||
.damage_whole_window = false,
|
.damage_whole_window = false,
|
||||||
.box_drawing_base_thickness = 0.04,
|
.box_drawing_base_thickness = 0.04,
|
||||||
.box_drawing_solid_shades = true,
|
.box_drawing_solid_shades = true,
|
||||||
.pua_double_width = false,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
.notifications = tll_init(),
|
.notifications = tll_init(),
|
||||||
|
|
|
||||||
3
config.h
3
config.h
|
|
@ -245,7 +245,7 @@ struct config {
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
enum fcft_scaling_filter fcft_filter;
|
enum fcft_scaling_filter fcft_filter;
|
||||||
bool allow_overflowing_double_width_glyphs;
|
bool overflowing_glyphs;
|
||||||
bool grapheme_shaping;
|
bool grapheme_shaping;
|
||||||
enum {GRAPHEME_WIDTH_WCSWIDTH, GRAPHEME_WIDTH_DOUBLE} grapheme_width_method;
|
enum {GRAPHEME_WIDTH_WCSWIDTH, GRAPHEME_WIDTH_DOUBLE} grapheme_width_method;
|
||||||
bool render_timer_osd;
|
bool render_timer_osd;
|
||||||
|
|
@ -256,7 +256,6 @@ struct config {
|
||||||
off_t max_shm_pool_size;
|
off_t max_shm_pool_size;
|
||||||
float box_drawing_base_thickness;
|
float box_drawing_base_thickness;
|
||||||
bool box_drawing_solid_shades;
|
bool box_drawing_solid_shades;
|
||||||
bool pua_double_width;
|
|
||||||
} tweak;
|
} tweak;
|
||||||
|
|
||||||
user_notifications_t notifications;
|
user_notifications_t notifications;
|
||||||
|
|
|
||||||
|
|
@ -847,34 +847,24 @@ any of these options.
|
||||||
|
|
||||||
Default: _lanczos3_.
|
Default: _lanczos3_.
|
||||||
|
|
||||||
*allow-overflowing-double-width-glyphs*
|
*overflowing-glyphs*
|
||||||
Boolean. When enabled, double width glyphs with a character width
|
Boolean. When enabled, glyphs wider than their cell(s) are allowed
|
||||||
of 1 are allowed to overflow into the neighbouring cell.
|
to render into one additional neighbouring cell.
|
||||||
|
|
||||||
One use case for this is fonts "icon" characters in the Unicode
|
One use case for this are fonts with wide italic characters that
|
||||||
private usage area, e.g. Nerd Fonts, or Powerline Fonts. Without
|
"bend" into the next cell. Without this option, such glyphs will
|
||||||
this option, such glyphs will appear "cut off".
|
appear "cut off".
|
||||||
|
|
||||||
Another use case are legacy emoji characters like *WHITE FROWNING
|
Another use case are fonts with "icon" characters in the Unicode
|
||||||
FACE*.
|
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
|
Note: might impact performance depending on the font used.
|
||||||
should be allowed to overflow.
|
Especially small font sizes can cause many overflowing glyphs
|
||||||
|
because of subpixel rendering.
|
||||||
See also: *pua-double-width*
|
|
||||||
|
|
||||||
Default: _yes_.
|
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*
|
*render-timer*
|
||||||
Enables a frame rendering timer, that prints the time it takes to
|
Enables a frame rendering timer, that prints the time it takes to
|
||||||
render each frame, in microseconds, either on-screen, to stderr,
|
render each frame, in microseconds, either on-screen, to stderr,
|
||||||
|
|
|
||||||
78
render.c
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
|
static int
|
||||||
render_cell(struct terminal *term, pixman_image_t *pix,
|
render_cell(struct terminal *term, pixman_image_t *pix,
|
||||||
struct row *row, int col, int row_no, bool has_cursor)
|
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;
|
return 0;
|
||||||
|
|
||||||
cell->attrs.clean = 1;
|
cell->attrs.clean = 1;
|
||||||
|
cell->attrs.confined = true;
|
||||||
|
|
||||||
int width = term->cell_width;
|
int width = term->cell_width;
|
||||||
int height = term->cell_height;
|
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));
|
cell_cols = max(1, min(cell_cols, cols_left));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hack!
|
* Determine cells that will bleed into their right neighbor and remember
|
||||||
*
|
* them for cleanup in the next frame.
|
||||||
* 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 isn’t the last column
|
|
||||||
* - *this* cells is followed by an empty cell, or a space
|
|
||||||
*/
|
*/
|
||||||
if (term->conf->tweak.allow_overflowing_double_width_glyphs &&
|
int render_width = cell_cols * width;
|
||||||
glyph_count > 0 &&
|
if (term->conf->tweak.overflowing_glyphs &&
|
||||||
cell_cols == 1 &&
|
glyph_count > 0)
|
||||||
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' '))
|
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
if (glyph_width > render_width) {
|
||||||
* Ensure the cell we’re overflowing into gets re-rendered, to
|
render_width = min(glyph_width, render_width + width);
|
||||||
* ensure it is erased if *this* cell is erased. Note that we
|
|
||||||
* do *not* mark the row as dirty - we don’t need to re-render
|
for (int i = 0; i < cell_cols; i++)
|
||||||
* the cell if nothing else on the row has changed.
|
row->cells[col + i].attrs.confined = false;
|
||||||
*/
|
}
|
||||||
row->cells[col].attrs.clean = 0;
|
|
||||||
row->cells[col + 1].attrs.clean = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pixman_region32_t clip;
|
pixman_region32_t clip;
|
||||||
pixman_region32_init_rect(
|
pixman_region32_init_rect(
|
||||||
&clip, x, y,
|
&clip, x, y,
|
||||||
cell_cols * term->cell_width, term->cell_height);
|
render_width, term->cell_height);
|
||||||
pixman_image_set_clip_region32(pix, &clip);
|
pixman_image_set_clip_region32(pix, &clip);
|
||||||
|
|
||||||
/* Background */
|
/* Background */
|
||||||
|
|
@ -771,6 +767,10 @@ static void
|
||||||
render_row(struct terminal *term, pixman_image_t *pix, struct row *row,
|
render_row(struct terminal *term, pixman_image_t *pix, struct row *row,
|
||||||
int row_no, int cursor_col)
|
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--)
|
for (int col = term->cols - 1; col >= 0; col--)
|
||||||
render_cell(term, pix, row, col, row_no, cursor_col == 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))
|
(last_col_needs_erase && last_col))
|
||||||
{
|
{
|
||||||
render_cell(term, pix, row, col, term_row_no, cursor_col == col);
|
render_cell(term, pix, row, col, term_row_no, cursor_col == col);
|
||||||
} else
|
} else {
|
||||||
cell->attrs.clean = 1;
|
cell->attrs.clean = 1;
|
||||||
|
cell->attrs.confined = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,12 @@ struct attributes {
|
||||||
uint32_t fg:24;
|
uint32_t fg:24;
|
||||||
|
|
||||||
bool clean:1;
|
bool clean:1;
|
||||||
|
bool confined:1;
|
||||||
bool have_fg:1;
|
bool have_fg:1;
|
||||||
bool have_bg:1;
|
bool have_bg:1;
|
||||||
uint32_t selected:2;
|
uint32_t selected:2;
|
||||||
bool url:1;
|
bool url:1;
|
||||||
uint32_t reserved:2;
|
uint32_t reserved:1;
|
||||||
uint32_t bg:24;
|
uint32_t bg:24;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(struct attributes) == 8, "VT attribute struct too large");
|
static_assert(sizeof(struct attributes) == 8, "VT attribute struct too large");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue