scroll: destroy scrolled out sixels before scroll is applied

The logic that breaks out of sixel loops does not work for rows that
has already wrapped around.

Thus, we need to destroy sixels that are about to be scrolled
out *before* we actually scroll.

Since this is the *only* time we destroy sixels (instead of
overwriting it), rename the sixel functions. And, since they now do a
very specific thing, they can be greatly simplified (and thus faster).
This commit is contained in:
Daniel Eklöf 2020-06-29 22:01:02 +02:00
parent a136987678
commit 62be729c45
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
4 changed files with 64 additions and 142 deletions

View file

@ -72,13 +72,12 @@
* Do not auto-resize a sixel image for which the cllent has specified
a size. This fixes an issue where an image would incorrectly
overflow into the cell row beneath.
* Erase scrolled out sixel image that crossed the scrollback wrap
around boundary.
* Text printed, or other sixel images drawn, on top of a sixel image
no longer erases the entire image, only the part(s) covered by the
new text or image.
* Sixel images being erased when printing text next to them.
* Sixel handling when resizing window.
* Sixel handling when scrollback wraps around.
### Security

186
sixel.c
View file

@ -87,26 +87,40 @@ sixel_erase(struct terminal *term, struct sixel *sixel)
sixel_destroy(sixel);
}
static int
rebase_row(const struct terminal *term, int abs_row)
{
int scrollback_start = term->grid->offset + term->rows;
int rebased_row = abs_row - scrollback_start + term->grid->num_rows;
rebased_row &= term->grid->num_rows - 1;
return rebased_row;
}
static bool
verify_sixel_list_order(const struct terminal *term)
{
#if defined(_DEBUG)
int prev_row = INT_MAX;
tll_foreach(term->grid->sixel_images, it) {
int row = rebase_row(term, it->item.pos.row + it->item.rows - 1);
assert(row < prev_row);
if (row >= prev_row)
return false;
prev_row = row;
}
#endif
return true;
}
static void
sixel_insert(struct terminal *term, struct sixel sixel)
{
const int scrollback_end
= (term->grid->offset + term->rows) & (term->grid->num_rows - 1);
const int end_row
= (sixel.pos.row + sixel.rows
- scrollback_end
+ term->grid->num_rows) & (term->grid->num_rows - 1);
assert(end_row >= 0 && end_row < term->grid->num_rows);
int end_row = rebase_row(term, sixel.pos.row + sixel.rows - 1);
tll_foreach(term->grid->sixel_images, it) {
const int e
= (it->item.pos.row + it->item.rows
- scrollback_end
+ term->grid->num_rows) & (term->grid->num_rows - 1);
assert(e >= 0 && e < term->grid->num_rows);
if (e < end_row) {
if (rebase_row(term, it->item.pos.row + it->item.rows - 1) < end_row) {
tll_insert_before(term->grid->sixel_images, it, sixel);
goto out;
}
@ -121,106 +135,44 @@ out:
LOG_DBG(" rows=%d+%d", it->item.pos.row, it->item.rows);
}
#else
;
verify_sixel_list_order(term);
#endif
}
/* Row numbers are absolute */
static void
sixel_delete_at_point(struct terminal *term, int row, int col)
{
assert(row >= 0);
assert(row < term->grid->num_rows);
assert(col < term->grid->num_cols);
if (likely(tll_length(term->grid->sixel_images) == 0))
return;
tll_foreach(term->grid->sixel_images, it) {
struct sixel *six = &it->item;
const int six_start = six->pos.row;
const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1);
/* We should never generate scrollback wrapping sixels */
assert(six_end >= six_start);
if (row >= six_start && row <= six_end) {
const int col_start = six->pos.col;
const int col_end = six->pos.col + six->cols;
if (col < 0 || (col >= col_start && col < col_end)) {
sixel_erase(term, six);
tll_remove(term->grid->sixel_images, it);
}
}
}
}
/* TODO: remove */
void
sixel_delete_at_row(struct terminal *term, int row)
sixel_scroll_up(struct terminal *term, int rows)
{
if (likely(tll_length(term->grid->sixel_images) == 0))
return;
sixel_delete_at_point(
term, (term->grid->offset + row) & (term->grid->num_rows - 1), -1);
}
/* Row numbers are absolute */
static void
_sixel_delete_in_range(struct terminal *term, int start, int end)
{
assert(end >= start);
assert(start >= 0);
assert(start < term->grid->num_rows);
assert(end >= 0);
assert(end < term->grid->num_rows);
tll_foreach(term->grid->sixel_images, it) {
tll_rforeach(term->grid->sixel_images, it) {
struct sixel *six = &it->item;
const int six_start = six->pos.row;
const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1);
/* We should never generate scrollback wrapping sixels */
assert(six_end >= six_start);
if ((start <= six_start && end >= six_start) || /* Crosses sixel start boundary */
(start <= six_end && end >= six_end) || /* Crosses sixel end boundary */
(start >= six_start && end <= six_end)) /* Fully within sixel range */
{
sixel_erase(term, six);
int six_start = rebase_row(term, six->pos.row);
if (six_start < rows) {
sixel_destroy(six);
tll_remove(term->grid->sixel_images, it);
}
} else
break;
}
verify_sixel_list_order(term);
}
void
sixel_delete_in_range(struct terminal *term, int _start, int _end)
sixel_scroll_down(struct terminal *term, int rows)
{
if (likely(tll_length(term->grid->sixel_images) == 0))
return;
assert(term->grid->num_rows >= rows);
if (_start == _end) {
/* Avoid expensive wrap calculation */
return sixel_delete_at_point(
term, (term->grid->offset + _start) & (term->grid->num_rows - 1), -1);
tll_foreach(term->grid->sixel_images, it) {
struct sixel *six = &it->item;
int six_end = rebase_row(term, six->pos.row + six->rows - 1);
if (six_end >= term->grid->num_rows - rows) {
sixel_destroy(six);
tll_remove(term->grid->sixel_images, it);
} else
break;
}
assert(_end >= _start);
const int lines = _end - _start + 1;
const int start = (term->grid->offset + _start) & (term->grid->num_rows - 1);
const int end = (start + lines - 1) & (term->grid->num_rows - 1);
const bool wraps = end < start;
if (wraps) {
int rows_to_wrap_around = term->grid->num_rows - start;
assert(lines - rows_to_wrap_around > 0);
_sixel_delete_in_range(term, start, term->grid->num_rows);
_sixel_delete_in_range(term, 0, lines - rows_to_wrap_around);
} else
_sixel_delete_in_range(term, start, end);
verify_sixel_list_order(term);
}
static void
@ -331,39 +283,24 @@ static void
_sixel_overwrite_by_rectangle(
struct terminal *term, int row, int col, int height, int width)
{
assert(row + height <= term->grid->num_rows);
assert(row >= 0);
assert(row + height <= term->grid->num_rows);
assert(col >= 0);
assert(col + width <= term->grid->num_cols);
/* We don't handle rectangle wrapping around */
assert(row + height <= term->grid->num_rows);
const int start = row;
const int end = row + height - 1;
const int scrollback_end
= (term->grid->offset + term->rows) & (term->grid->num_rows - 1);
const int grid_relative_start
= (start
- scrollback_end
+ term->grid->num_rows) & (term->grid->num_rows - 1);
const int scrollback_rel_start = rebase_row(term, start);
tll_foreach(term->grid->sixel_images, it) {
struct sixel *six = &it->item;
const int six_start = six->pos.row;
const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1);
const int six_scrollback_rel_end = rebase_row(term, six_end);
const int six_grid_relative_end =
(six_end
- scrollback_end
+ + term->grid->num_rows) & (term->grid->num_rows - 1);
if (six_grid_relative_end < grid_relative_start) {
if (six_scrollback_rel_end < scrollback_rel_start) {
/* All remaining sixels are *before* our rectangle */
break;
}
@ -424,14 +361,8 @@ sixel_overwrite_by_row(struct terminal *term, int _row, int col, int width)
if (likely(tll_length(term->grid->sixel_images) == 0))
return;
const int scrollback_end
= (term->grid->offset + term->rows) & (term->grid->num_rows - 1);
const int row = (term->grid->offset + _row) & (term->grid->num_rows - 1);
const int grid_relative_row
= (term->grid->offset + row
- scrollback_end
+ term->grid->num_rows) & (term->grid->num_rows - 1);
const int scrollback_rel_row = rebase_row(term, row);
tll_foreach(term->grid->sixel_images, it) {
struct sixel *six = &it->item;
@ -441,12 +372,9 @@ sixel_overwrite_by_row(struct terminal *term, int _row, int col, int width)
/* We should never generate scrollback wrapping sixels */
assert(six_end >= six_start);
const int six_grid_relative_end
= (six->pos.row + six->rows - 1
- scrollback_end
+ term->grid->num_rows) & (term->grid->num_rows - 1);
const int six_scrollback_rel_end = rebase_row(term, six_end);
if (six_grid_relative_end < grid_relative_row) {
if (six_scrollback_rel_end < scrollback_rel_row) {
/* All remaining sixels are *before* "our" row */
break;
}

11
sixel.h
View file

@ -12,15 +12,8 @@ void sixel_unhook(struct terminal *term);
void sixel_destroy(struct sixel *sixel);
/*
* Deletes all sixels that are touched by the specified row(s). Used
* when scrolling, to competely remove sixels that has either
* completely, or partly scrolled out of history.
*
* Row numbers are relative to the current grid offset.
*/
void sixel_delete_in_range(struct terminal *term, int row_start, int row_end);
void sixel_delete_at_row(struct terminal *term, int row);
void sixel_scroll_up(struct terminal *term, int rows);
void sixel_scroll_down(struct terminal *term, int rows);
/*
* Remove sixel data from the specified location. Used when printing

View file

@ -1779,6 +1779,8 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows
}
}
sixel_scroll_up(term, rows);
bool view_follows = term->grid->view == term->grid->offset;
term->grid->offset += rows;
term->grid->offset &= term->grid->num_rows - 1;
@ -1800,7 +1802,6 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows
for (int r = region.end - rows; r < region.end; r++)
erase_line(term, grid_row_and_alloc(term->grid, r));
sixel_delete_in_range(term, region.end - rows, region.end - 1);
term_damage_scroll(term, DAMAGE_SCROLL, region, rows);
term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row);
@ -1841,6 +1842,8 @@ term_scroll_reverse_partial(struct terminal *term,
}
}
sixel_scroll_down(term, rows);
bool view_follows = term->grid->view == term->grid->offset;
term->grid->offset -= rows;
while (term->grid->offset < 0)
@ -1867,7 +1870,6 @@ term_scroll_reverse_partial(struct terminal *term,
for (int r = region.start; r < region.start + rows; r++)
erase_line(term, grid_row_and_alloc(term->grid, r));
sixel_delete_in_range(term, region.start, region.start + rows - 1);
term_damage_scroll(term, DAMAGE_SCROLL_REVERSE, region, rows);
term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row);