render: run the “overflowing glyphs” prepass *before* rendering sixels

This fixes an issue where the left-most column of a sixel was
“overwritten” by the cell content.

This patch also rewrites the prepass logic, to try to reduce the
number of loads performed.

The new logic loops each row from left to right, looking for dirty
cells. When a dirty cell is found, we first scan backwards, until we
find a non-overflowing cell. That cell is unaffected by the
overflowing cell we’re currently dealing with.

We can also stop as soon as we see a dirty cell, since that cell will
already have been dealt with.

Then, we scan forward, dirtying cells until we see a non-overflowing
cell. That first non-overflowing cell is also dirtied, but after that
we break.

The last loop, that scans forward, advances the same cell pointer used
in the outer loop.
This commit is contained in:
Daniel Eklöf 2021-08-10 18:33:18 +02:00
parent 2b14e28512
commit 41a8f2fc65
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
2 changed files with 70 additions and 18 deletions

View file

@ -74,6 +74,8 @@
scaling factor.
* Slow-to-terminate client applications causing other footclient instances to
freeze when closing a footclient window.
* Underlying cell content showing through in the left-most column of
sixels.
### Security

View file

@ -439,20 +439,6 @@ 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)
@ -767,10 +753,6 @@ 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);
}
@ -2452,6 +2434,74 @@ grid_render(struct terminal *term)
cursor.row &= term->grid->num_rows - 1;
}
if (term->conf->tweak.overflowing_glyphs) {
/*
* Pre-pass to dirty cells affected by overflowing glyphs.
*
* Given any two pair of cells where the first cell is
* overflowing into the second, *both* cells must be
* re-rendered if any one of them is dirty.
*
* Thus, given a string of overflowing glyphs, with a single
* dirty cell in the middle, we need to re-render the entire
* string.
*/
for (int r = 0; r < term->rows; r++) {
struct row *row = grid_row_in_view(term->grid, r);
if (!row->dirty)
continue;
/* Loop row from left to right, looking for dirty cells */
for (struct cell *cell = &row->cells[0];
cell < &row->cells[term->cols];
cell++)
{
if (cell->attrs.clean)
continue;
/*
* Cell is dirty, go back and dirty previous cells, if
* they are overflowing.
*
* As soon as we see a non-overflowing cell we can
* stop, since it isnt affecting the string of
* overflowing glyphs that follows it.
*
* As soon as we see a dirty cell, we can stop, since
* that means weve already handled it (remember the
* outer loop goes from left to right).
*/
for (struct cell *c = cell - 1; c >= &row->cells[0]; c--) {
if (c->attrs.confined)
break;
if (!c->attrs.clean)
break;
c->attrs.clean = false;
}
/*
* Now move forward, dirtying all cells until we hit a
* non-overflowing cell.
*
* Note that the first non-overflowing cell must be
* re-rendered as well, but any cell *after* that is
* unaffected by the string of overflowing glyphs
* were dealing with right now.
*
* For performance, this iterates the *outer* loops
* cell pointer - no point in re-checking all these
* glyphs again, in the outer loop.
*/
for (; cell < &row->cells[term->cols]; cell++) {
cell->attrs.clean = false;
if (cell->attrs.confined)
break;
}
}
}
}
render_sixel_images(term, buf->pix[0], &cursor);
if (term->render.workers.count > 0) {