From bc82d4ee28b0ca0e86b096069b7a61cbb082a93b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 23 Jun 2020 21:07:12 +0200 Subject: [PATCH 01/11] sixel: wip: split up image being overwritten, rather than erasing it Instead of completely erasing a sixel image when it is being "overwritten" (text is printed somewhere within the image, or another sixel image is emitted within the first image), split it up into up to four pieces: 'above', 'below', 'to-the-left' and 'to-the-right'. This is currently very un-optimized, but seems to produce correct results. --- sixel.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++- sixel.h | 1 + terminal.c | 3 +- 3 files changed, 142 insertions(+), 2 deletions(-) diff --git a/sixel.c b/sixel.c index 2186795c..23ded128 100644 --- a/sixel.c +++ b/sixel.c @@ -154,10 +154,143 @@ sixel_delete_at_cursor(struct terminal *term) term, term->grid->cursor.point.row, term->grid->cursor.point.col); } +static void +sixel_split(struct terminal *term, struct sixel *six, int row, int col) +{ + assert(row >= six->pos.row); + assert(row < six->pos.row + six->rows); + assert(col >= six->pos.col); + assert(col < six->pos.col + six->cols); + + int rel_row = row - six->pos.row; + int rel_col = col - six->pos.col; + + if (rel_row > 0) { + struct sixel above = { + .width = six->width, + .height = rel_row * term->cell_height, + .rows = rel_row, + .cols = six->cols, + .pos = six->pos, + }; + above.data = malloc(above.width * above.height * sizeof(uint32_t)); + memcpy(above.data, six->data, above.width * above.height * sizeof(uint32_t)); + + above.pix = pixman_image_create_bits_no_clear( + PIXMAN_a8r8g8b8, + above.width, above.height, + above.data, above.width * sizeof(uint32_t)); + + tll_push_back(term->grid->sixel_images, above); + } + + if (rel_row + 1 < six->rows) { + struct sixel below = { + .width = six->width, + .height = six->height - (rel_row + 1) * term->cell_height, + .rows = six->rows - (rel_row + 1), + .cols = six->cols, + .pos = (struct coord){ + six->pos.col, + (six->pos.row + rel_row + 1) & (term->grid->num_rows - 1)}, + }; + below.data = malloc(below.width * below.height * sizeof(uint32_t)); + memcpy( + below.data, + &((const uint32_t *)six->data)[(rel_row + 1) * term->cell_height * six->width], + below.width * below.height * sizeof(uint32_t)); + below.pix = pixman_image_create_bits_no_clear( + PIXMAN_a8r8g8b8, + below.width, below.height, + below.data, below.width * sizeof(uint32_t)); + + tll_push_back(term->grid->sixel_images, below); + } + + if (rel_col > 0) { + struct sixel left = { + .width = rel_col * term->cell_width, + .height = min(term->cell_height, six->height - rel_row * term->cell_height), + .rows = 1, + .cols = rel_col, + .pos = (struct coord){six->pos.col, + (six->pos.row + rel_row) & (term->grid->num_rows - 1)}, + }; + left.data = malloc(left.width * left.height * sizeof(uint32_t)); + for (size_t i = 0; i < term->cell_height; i++) + memcpy( + &((uint32_t *)left.data)[i * left.width], + &((const uint32_t *)six->data)[(rel_row * term->cell_height + i) * six->width], + left.width * sizeof(uint32_t)); + left.pix = pixman_image_create_bits_no_clear( + PIXMAN_a8r8g8b8, + left.width, left.height, + left.data, left.width * sizeof(uint32_t)); + tll_push_back(term->grid->sixel_images, left); + + } + + if (rel_col + 1 < six->cols) { + struct sixel right = { + .width = six->width - (rel_col + 1) * term->cell_width, + .height = min(term->cell_height, six->height - rel_row * term->cell_height), + .rows = 1, + .cols = six->cols - (rel_col + 1), + .pos = (struct coord){six->pos.col + rel_col + 1, + (six->pos.row + rel_row) & (term->grid->num_rows - 1)}, + }; + right.data = malloc(right.width * right.height * sizeof(uint32_t)); + for (size_t i = 0; i < term->cell_height; i++) + memcpy( + &((uint32_t *)right.data)[i * right.width], + &((const uint32_t *)six->data)[(rel_row * term->cell_height + i) * six->width + (rel_col + 1) * term->cell_width], + right.width * sizeof(uint32_t)); + right.pix = pixman_image_create_bits_no_clear( + PIXMAN_a8r8g8b8, + right.width, right.height, + right.data, right.width * sizeof(uint32_t)); + tll_push_back(term->grid->sixel_images, right); + } +} + +void +sixel_split_at_point(struct terminal *term, int _row, int col) +{ + if (likely(tll_length(term->grid->sixel_images) == 0)) + return; + + const int row = (term->grid->offset + _row) & (term->grid->num_rows - 1); + + 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; + + 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 >= col_start && col < col_end) { + //sixel_erase(term, six); + sixel_split(term, six, row, col); + sixel_erase(term, six); + tll_remove(term->grid->sixel_images, it); + } + } + } +} + +void +sixel_split_at_cursor(struct terminal *term) +{ + sixel_split_at_point(term, term->grid->cursor.point.row, term->grid->cursor.point.col); +} + void sixel_unhook(struct terminal *term) { - sixel_delete_at_cursor(term); + //sixel_delete_at_cursor(term); struct sixel image = { .data = term->sixel.image.data, @@ -170,6 +303,11 @@ sixel_unhook(struct terminal *term) (term->grid->offset + term->grid->cursor.point.row) & (term->grid->num_rows - 1)}, }; + for (int row = 0; row < image.rows; row++) { + for (int col = 0; col < image.cols; col++) + sixel_split_at_point(term, term->grid->cursor.point.row + row, term->grid->cursor.point.col + col); + } + LOG_DBG("generating %dx%d pixman image", image.width, image.height); image.pix = pixman_image_create_bits_no_clear( diff --git a/sixel.h b/sixel.h index a10f14f2..2f3b0d86 100644 --- a/sixel.h +++ b/sixel.h @@ -15,6 +15,7 @@ void sixel_destroy(struct sixel *sixel); void sixel_delete_in_range(struct terminal *term, int start, int end); void sixel_delete_at_row(struct terminal *term, int _row); void sixel_delete_at_cursor(struct terminal *term); +void sixel_split_at_cursor(struct terminal *term); void sixel_colors_report_current(struct terminal *term); void sixel_colors_reset(struct terminal *term); diff --git a/terminal.c b/terminal.c index dd0c8c81..01fd50be 100644 --- a/terminal.c +++ b/terminal.c @@ -2381,7 +2381,8 @@ term_print(struct terminal *term, wchar_t wc, int width) print_linewrap(term); print_insert(term, width); - sixel_delete_at_cursor(term); + //sixel_delete_at_cursor(term); + sixel_split_at_cursor(term); /* *Must* get current cell *after* linewrap+insert */ struct row *row = term->grid->cur_row; From 32e70263f905ee58ba4026d57b1a5fec35c26352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 25 Jun 2020 17:30:51 +0200 Subject: [PATCH 02/11] wayland: use xdg_output's description if there's no 'model' --- wayland.c | 10 +++++++--- wayland.h | 7 ++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/wayland.c b/wayland.c index 5f99a309..ae7ead36 100644 --- a/wayland.c +++ b/wayland.c @@ -259,13 +259,15 @@ xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) { struct monitor *mon = data; - mon->name = strdup(name); + mon->name = name != NULL ? strdup(name) : NULL; } static void xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, const char *description) { + struct monitor *mon = data; + mon->description = description != NULL ? strdup(description) : NULL; } static const struct zxdg_output_v1_listener xdg_output_listener = { @@ -666,13 +668,14 @@ handle_global(void *data, struct wl_registry *registry, static void monitor_destroy(struct monitor *mon) { - free(mon->name); if (mon->xdg != NULL) zxdg_output_v1_destroy(mon->xdg); if (mon->output != NULL) wl_output_destroy(mon->output); free(mon->make); free(mon->model); + free(mon->name); + free(mon->description); } static void @@ -842,7 +845,8 @@ wayl_init(const struct config *conf, struct fdm *fdm) "%s: %dx%d+%dx%d@%dHz %s %.2f\" scale=%d PPI=%dx%d (physical) PPI=%dx%d (logical)", it->item.name, it->item.dim.px_real.width, it->item.dim.px_real.height, it->item.x, it->item.y, (int)round(it->item.refresh), - it->item.model, it->item.inch, it->item.scale, + it->item.model != NULL ? it->item.model : it->item.description, + it->item.inch, it->item.scale, it->item.ppi.real.x, it->item.ppi.real.y, it->item.ppi.scaled.x, it->item.ppi.scaled.y); } diff --git a/wayland.h b/wayland.h index 60f39454..ccc03ad3 100644 --- a/wayland.h +++ b/wayland.h @@ -19,7 +19,6 @@ struct monitor { struct wayland *wayl; struct wl_output *output; struct zxdg_output_v1 *xdg; - char *name; uint32_t wl_name; int x; @@ -63,8 +62,14 @@ struct monitor { float refresh; enum wl_output_subpixel subpixel; + /* From wl_output */ char *make; char *model; + + /* From xdg_output */ + char *name; + char *description; + float inch; /* e.g. 24" */ }; From f4fcdbf38b4a6f6041b5ebf709ba65d7a6394dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 27 Jun 2020 11:00:28 +0200 Subject: [PATCH 03/11] sixel: hopefully fix erasing of scrollback wrap-around sixels If a sixel image crossed the scrollback wrap around, the logic that detected whether a row, or a row range, intersected with the sixel was incorrect. --- CHANGELOG.md | 3 ++- sixel.c | 37 +++++++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26a15465..6adb6a76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,7 +67,8 @@ a size. This fixes an issue where an image would incorrectly overflow into the cell row beneath. * Window title always being set to "foot" on reset. - +* Erase scrolled out sixel image that crossed the scrollback wrap + around boundary. ### Security diff --git a/sixel.c b/sixel.c index 23ded128..ea7a9caa 100644 --- a/sixel.c +++ b/sixel.c @@ -98,13 +98,17 @@ sixel_delete_at_point(struct terminal *term, int _row, int col) const int row = (term->grid->offset + _row) & (term->grid->num_rows - 1); const int six_start = six->pos.row; - const int six_end = six_start + six->rows - 1; + const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1); - if (row >= six_start && row <= six_end) { + bool wraps = six_end < six_start; + + if ((!wraps && row >= six_start && row <= six_end) || + (wraps && (row >= six_start || row <= six_end))) + { const int col_start = six->pos.col; const int col_end = six->pos.col + six->cols; - if (col >= col_start && col < col_end) { + if (col < 0 || (col >= col_start && col < col_end)) { sixel_erase(term, six); tll_remove(term->grid->sixel_images, it); } @@ -115,7 +119,7 @@ sixel_delete_at_point(struct terminal *term, int _row, int col) void sixel_delete_at_row(struct terminal *term, int _row) { - sixel_delete_at_point(term, _row, INT_MAX); + sixel_delete_at_point(term, _row, -1); } void @@ -129,17 +133,30 @@ sixel_delete_in_range(struct terminal *term, int _start, int _end) if (_start == _end) return sixel_delete_at_row(term, _start); + const int start = (term->grid->offset + _start) & (term->grid->num_rows - 1); + const int end = (start + (_end - _start)) & (term->grid->num_rows - 1); + const bool wraps = end < start; + tll_foreach(term->grid->sixel_images, it) { struct sixel *six = &it->item; - const int start = (term->grid->offset + _start) & (term->grid->num_rows - 1); - const int end = start + (_end - _start); const int six_start = six->pos.row; - const int six_end = six_start + six->rows - 1; + const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1); + const bool six_wraps = 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 */ + if ((six_wraps == wraps && + ((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 */ + (six_wraps && !wraps && + ((start <= six_start && end >= six_start) || + (start <= six_end && end >= six_end) || + (start >= six_start || end <= six_end))) || + (!six_wraps && wraps && + ((six_start <= start && six_end >= end) || /* Sixel croses region start boundary */ + (six_start <= end && six_end >= end) || /* Sixel crosses region end boundary */ + (six_start >= start || six_end <= end))) /* Sixel is fully enclosed by region */ + ) { sixel_erase(term, six); tll_remove(term->grid->sixel_images, it); From dfc205e706ef261edcbae0e04e8ba5e1d225a886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 27 Jun 2020 12:45:22 +0200 Subject: [PATCH 04/11] sixel: add sixel_split_by_rectangle() This function splits a sixel image into up to four pieces. The four pieces correspond to the relative complement (set difference) of the sixel image and the specified rectangle. Use this function when (possibly) overwriting existing sixel images when generating a new one, i.e. in sixel_unhook(). --- sixel.c | 131 ++++++++++++++++++++++++++++++++------------------------ sixel.h | 1 - 2 files changed, 74 insertions(+), 58 deletions(-) diff --git a/sixel.c b/sixel.c index ea7a9caa..8869e46f 100644 --- a/sixel.c +++ b/sixel.c @@ -164,103 +164,102 @@ sixel_delete_in_range(struct terminal *term, int _start, int _end) } } -void -sixel_delete_at_cursor(struct terminal *term) -{ - sixel_delete_at_point( - term, term->grid->cursor.point.row, term->grid->cursor.point.col); -} - static void -sixel_split(struct terminal *term, struct sixel *six, int row, int col) +sixel_split(struct terminal *term, struct sixel *six, + int row, int col, int height, int width) { - assert(row >= six->pos.row); - assert(row < six->pos.row + six->rows); - assert(col >= six->pos.col); - assert(col < six->pos.col + six->cols); + int rel_above = max(row - six->pos.row, 0); + int rel_below = min(row + height - six->pos.row, six->rows); + int rel_left = max(col - six->pos.col, 0); + int rel_right = min(col + width - six->pos.col, six->cols); - int rel_row = row - six->pos.row; - int rel_col = col - six->pos.col; + assert(rel_above >= 0); + assert(rel_below >= 0); + assert(rel_left >= 0); + assert(rel_right >= 0); - if (rel_row > 0) { + LOG_DBG("SPLIT: six (%p): %dx%d-%dx%d, %dx%d-%dx%d, rel: above=%d, below=%d, left=%d, right=%d", + six, six->pos.row, six->pos.col, six->rows, six->cols, + row, col, height, width, + rel_above, rel_below, rel_left, rel_right); + + if (rel_above > 0) { struct sixel above = { .width = six->width, - .height = rel_row * term->cell_height, - .rows = rel_row, + .height = rel_above * term->cell_height, + .rows = rel_above, .cols = six->cols, .pos = six->pos, }; above.data = malloc(above.width * above.height * sizeof(uint32_t)); memcpy(above.data, six->data, above.width * above.height * sizeof(uint32_t)); - above.pix = pixman_image_create_bits_no_clear( PIXMAN_a8r8g8b8, above.width, above.height, above.data, above.width * sizeof(uint32_t)); - tll_push_back(term->grid->sixel_images, above); } - if (rel_row + 1 < six->rows) { + if (rel_below < six->rows) { struct sixel below = { .width = six->width, - .height = six->height - (rel_row + 1) * term->cell_height, - .rows = six->rows - (rel_row + 1), + .height = six->height - rel_below * term->cell_height, + .rows = six->rows - rel_below, .cols = six->cols, .pos = (struct coord){ six->pos.col, - (six->pos.row + rel_row + 1) & (term->grid->num_rows - 1)}, + (six->pos.row + rel_below) & (term->grid->num_rows - 1)}, }; below.data = malloc(below.width * below.height * sizeof(uint32_t)); memcpy( below.data, - &((const uint32_t *)six->data)[(rel_row + 1) * term->cell_height * six->width], + &((const uint32_t *)six->data)[rel_below * term->cell_height * six->width], below.width * below.height * sizeof(uint32_t)); below.pix = pixman_image_create_bits_no_clear( PIXMAN_a8r8g8b8, below.width, below.height, below.data, below.width * sizeof(uint32_t)); - tll_push_back(term->grid->sixel_images, below); } - if (rel_col > 0) { + if (rel_left > 0) { struct sixel left = { - .width = rel_col * term->cell_width, - .height = min(term->cell_height, six->height - rel_row * term->cell_height), + .width = rel_left * term->cell_width, + .height = min(term->cell_height, six->height - rel_above * term->cell_height), .rows = 1, - .cols = rel_col, - .pos = (struct coord){six->pos.col, - (six->pos.row + rel_row) & (term->grid->num_rows - 1)}, + .cols = rel_left, + .pos = (struct coord){ + six->pos.col, + (six->pos.row + rel_above) & (term->grid->num_rows - 1)}, }; left.data = malloc(left.width * left.height * sizeof(uint32_t)); for (size_t i = 0; i < term->cell_height; i++) memcpy( &((uint32_t *)left.data)[i * left.width], - &((const uint32_t *)six->data)[(rel_row * term->cell_height + i) * six->width], + &((const uint32_t *)six->data)[(rel_above * term->cell_height + i) * six->width], left.width * sizeof(uint32_t)); left.pix = pixman_image_create_bits_no_clear( PIXMAN_a8r8g8b8, left.width, left.height, left.data, left.width * sizeof(uint32_t)); tll_push_back(term->grid->sixel_images, left); - } - if (rel_col + 1 < six->cols) { + if (rel_right < six->cols) { struct sixel right = { - .width = six->width - (rel_col + 1) * term->cell_width, - .height = min(term->cell_height, six->height - rel_row * term->cell_height), + .width = six->width - rel_right * term->cell_width, + .height = min(term->cell_height, six->height - rel_above * term->cell_height), .rows = 1, - .cols = six->cols - (rel_col + 1), - .pos = (struct coord){six->pos.col + rel_col + 1, - (six->pos.row + rel_row) & (term->grid->num_rows - 1)}, + .cols = six->cols - rel_right, + .pos = (struct coord){ + six->pos.col + rel_right, + (six->pos.row + rel_above) & (term->grid->num_rows - 1)}, }; right.data = malloc(right.width * right.height * sizeof(uint32_t)); for (size_t i = 0; i < term->cell_height; i++) memcpy( &((uint32_t *)right.data)[i * right.width], - &((const uint32_t *)six->data)[(rel_row * term->cell_height + i) * six->width + (rel_col + 1) * term->cell_width], + &((const uint32_t *)six->data)[(rel_above * term->cell_height + i) * six->width + rel_right * term->cell_width], right.width * sizeof(uint32_t)); right.pix = pixman_image_create_bits_no_clear( PIXMAN_a8r8g8b8, @@ -270,27 +269,46 @@ sixel_split(struct terminal *term, struct sixel *six, int row, int col) } } -void -sixel_split_at_point(struct terminal *term, int _row, int col) +static void +sixel_split_by_rectangle(struct terminal *term, int _row, + int col, int height, int width) { if (likely(tll_length(term->grid->sixel_images) == 0)) return; - const int row = (term->grid->offset + _row) & (term->grid->num_rows - 1); + const int start = (term->grid->offset + _row) & (term->grid->num_rows - 1); + const int end = (start + height - 1) & (term->grid->num_rows - 1); + const bool wraps = end < 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; + const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1); + const bool six_wraps = six_end < six_start; - if (row >= six_start && row <= six_end) { + if ((six_wraps == wraps && + ((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 */ + (six_wraps && !wraps && + ((start <= six_start && end >= six_start) || + (start <= six_end && end >= six_end) || + (start >= six_start || end <= six_end))) || + (!six_wraps && wraps && + ((six_start <= start && six_end >= end) || /* Sixel croses region start boundary */ + (six_start <= end && six_end >= end) || /* Sixel crosses region end boundary */ + (six_start >= start || six_end <= end))) /* Sixel is fully enclosed by region */ + ) + { const int col_start = six->pos.col; - const int col_end = six->pos.col + six->cols; + const int col_end = six->pos.col + six->cols - 1; - if (col >= col_start && col < col_end) { - //sixel_erase(term, six); - sixel_split(term, six, row, col); + if ((col <= col_start && col + width - 1 >= col_start) || + (col <= col_end && col + width - 1 >= col_end) || + (col >= col_start && col + width - 1 <= col_end)) + { + sixel_split(term, six, start, col, height, width); sixel_erase(term, six); tll_remove(term->grid->sixel_images, it); } @@ -301,14 +319,13 @@ sixel_split_at_point(struct terminal *term, int _row, int col) void sixel_split_at_cursor(struct terminal *term) { - sixel_split_at_point(term, term->grid->cursor.point.row, term->grid->cursor.point.col); + sixel_split_by_rectangle( + term, term->grid->cursor.point.row, term->grid->cursor.point.col, 1, 1); } void sixel_unhook(struct terminal *term) { - //sixel_delete_at_cursor(term); - struct sixel image = { .data = term->sixel.image.data, .width = term->sixel.image.width, @@ -320,12 +337,12 @@ sixel_unhook(struct terminal *term) (term->grid->offset + term->grid->cursor.point.row) & (term->grid->num_rows - 1)}, }; - for (int row = 0; row < image.rows; row++) { - for (int col = 0; col < image.cols; col++) - sixel_split_at_point(term, term->grid->cursor.point.row + row, term->grid->cursor.point.col + col); - } + sixel_split_by_rectangle( + term, + term->grid->cursor.point.row, term->grid->cursor.point.col, + image.rows, image.cols); - LOG_DBG("generating %dx%d pixman image", image.width, image.height); + LOG_DBG("generating %dx%d pixman image at %d-%d", image.width, image.height, image.pos.row, image.pos.row + image.rows); image.pix = pixman_image_create_bits_no_clear( PIXMAN_a8r8g8b8, diff --git a/sixel.h b/sixel.h index 2f3b0d86..bb3b6cb0 100644 --- a/sixel.h +++ b/sixel.h @@ -14,7 +14,6 @@ void sixel_destroy(struct sixel *sixel); void sixel_delete_in_range(struct terminal *term, int start, int end); void sixel_delete_at_row(struct terminal *term, int _row); -void sixel_delete_at_cursor(struct terminal *term); void sixel_split_at_cursor(struct terminal *term); void sixel_colors_report_current(struct terminal *term); From d9b7a8572294f0bf950ef4944e2fc68edb1b5446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 27 Jun 2020 12:50:39 +0200 Subject: [PATCH 05/11] sixel: split: push front instead of back This reduces the burden on sixel_split_by_rectangle(), which would otherwise consider the newly added/splitted sixels. Since we already know they aren't covered by the specified rectangle, we can skip that. --- sixel.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sixel.c b/sixel.c index 8869e46f..f04a8d7e 100644 --- a/sixel.c +++ b/sixel.c @@ -197,7 +197,7 @@ sixel_split(struct terminal *term, struct sixel *six, PIXMAN_a8r8g8b8, above.width, above.height, above.data, above.width * sizeof(uint32_t)); - tll_push_back(term->grid->sixel_images, above); + tll_push_front(term->grid->sixel_images, above); } if (rel_below < six->rows) { @@ -219,7 +219,7 @@ sixel_split(struct terminal *term, struct sixel *six, PIXMAN_a8r8g8b8, below.width, below.height, below.data, below.width * sizeof(uint32_t)); - tll_push_back(term->grid->sixel_images, below); + tll_push_front(term->grid->sixel_images, below); } if (rel_left > 0) { @@ -242,7 +242,7 @@ sixel_split(struct terminal *term, struct sixel *six, PIXMAN_a8r8g8b8, left.width, left.height, left.data, left.width * sizeof(uint32_t)); - tll_push_back(term->grid->sixel_images, left); + tll_push_front(term->grid->sixel_images, left); } if (rel_right < six->cols) { @@ -265,7 +265,7 @@ sixel_split(struct terminal *term, struct sixel *six, PIXMAN_a8r8g8b8, right.width, right.height, right.data, right.width * sizeof(uint32_t)); - tll_push_back(term->grid->sixel_images, right); + tll_push_front(term->grid->sixel_images, right); } } From 37ceb65729beb221e92de8a8ae07c8f3d3e207b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 27 Jun 2020 13:18:54 +0200 Subject: [PATCH 06/11] sixel: split: make sure relative row/column values are bounded --- sixel.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sixel.c b/sixel.c index f04a8d7e..aaf5941e 100644 --- a/sixel.c +++ b/sixel.c @@ -168,10 +168,10 @@ static void sixel_split(struct terminal *term, struct sixel *six, int row, int col, int height, int width) { - int rel_above = max(row - six->pos.row, 0); - int rel_below = min(row + height - six->pos.row, six->rows); - int rel_left = max(col - six->pos.col, 0); - int rel_right = min(col + width - six->pos.col, six->cols); + int rel_above = min(max(row - six->pos.row, 0), six->rows); + int rel_below = max(min(row + height - six->pos.row, six->rows), 0); + int rel_left = min(max(col - six->pos.col, 0), six->cols); + int rel_right = max(min(col + width - six->pos.col, six->cols), 0); assert(rel_above >= 0); assert(rel_below >= 0); From 3715a37be9078fb2ab755adfcfa010d687322333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 27 Jun 2020 13:19:19 +0200 Subject: [PATCH 07/11] sixel: split_by_rectangle: bug: typo in boundary check --- sixel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sixel.c b/sixel.c index aaf5941e..2d04f742 100644 --- a/sixel.c +++ b/sixel.c @@ -296,7 +296,7 @@ sixel_split_by_rectangle(struct terminal *term, int _row, (start <= six_end && end >= six_end) || (start >= six_start || end <= six_end))) || (!six_wraps && wraps && - ((six_start <= start && six_end >= end) || /* Sixel croses region start boundary */ + ((six_start <= start && six_end >= start) || /* Sixel croses region start boundary */ (six_start <= end && six_end >= end) || /* Sixel crosses region end boundary */ (six_start >= start || six_end <= end))) /* Sixel is fully enclosed by region */ ) From e896c2fa5575120469e7571d590bf03e4f5c803c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 27 Jun 2020 13:56:13 +0200 Subject: [PATCH 08/11] sixel: refactor handling of wrap-around delete- and split regions Handle these on a higher abstraction level. The low level functions that detect sixel intersections now assume the specified rectangle (or line region) does *not* cross the wrap-around. This is ensured by detecting a wrap-around region before hand, and splitting it up into two, non wrapping regions. --- sixel.c | 138 ++++++++++++++++++++++++++++++++++++++++---------------- sixel.h | 5 +- 2 files changed, 102 insertions(+), 41 deletions(-) diff --git a/sixel.c b/sixel.c index 2d04f742..5f6fa7bd 100644 --- a/sixel.c +++ b/sixel.c @@ -87,16 +87,15 @@ sixel_erase(struct terminal *term, struct sixel *sixel) sixel_destroy(sixel); } +/* Row numbers are absolute */ static void -sixel_delete_at_point(struct terminal *term, int _row, int col) +sixel_delete_at_point(struct terminal *term, int row, int col) { if (likely(tll_length(term->grid->sixel_images) == 0)) return; tll_foreach(term->grid->sixel_images, it) { struct sixel *six = &it->item; - - const int row = (term->grid->offset + _row) & (term->grid->num_rows - 1); const int six_start = six->pos.row; const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1); @@ -117,25 +116,23 @@ sixel_delete_at_point(struct terminal *term, int _row, int col) } void -sixel_delete_at_row(struct terminal *term, int _row) +sixel_delete_at_row(struct terminal *term, int row) { - sixel_delete_at_point(term, _row, -1); + sixel_delete_at_point( + term, (term->grid->offset + row) & (term->grid->num_rows - 1), -1); } -void -sixel_delete_in_range(struct terminal *term, int _start, int _end) +/* Row numbers are absolute */ +static void +_sixel_delete_in_range(struct terminal *term, int start, int end) { - assert(_end >= _start); + assert(end >= start); if (likely(tll_length(term->grid->sixel_images) == 0)) return; - if (_start == _end) - return sixel_delete_at_row(term, _start); - - const int start = (term->grid->offset + _start) & (term->grid->num_rows - 1); - const int end = (start + (_end - _start)) & (term->grid->num_rows - 1); - const bool wraps = end < start; + if (start == end) + return sixel_delete_at_point(term, start, -1); tll_foreach(term->grid->sixel_images, it) { struct sixel *six = &it->item; @@ -144,18 +141,14 @@ sixel_delete_in_range(struct terminal *term, int _start, int _end) const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1); const bool six_wraps = six_end < six_start; - if ((six_wraps == wraps && + if ((!six_wraps && ((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 */ - (six_wraps && !wraps && + (six_wraps && ((start <= six_start && end >= six_start) || (start <= six_end && end >= six_end) || - (start >= six_start || end <= six_end))) || - (!six_wraps && wraps && - ((six_start <= start && six_end >= end) || /* Sixel croses region start boundary */ - (six_start <= end && six_end >= end) || /* Sixel crosses region end boundary */ - (six_start >= start || six_end <= end))) /* Sixel is fully enclosed by region */ + (start >= six_start || end <= six_end))) ) { sixel_erase(term, six); @@ -164,6 +157,24 @@ sixel_delete_in_range(struct terminal *term, int _start, int _end) } } +void +sixel_delete_in_range(struct terminal *term, int _start, int _end) +{ + 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); +} + static void sixel_split(struct terminal *term, struct sixel *six, int row, int col, int height, int width) @@ -269,36 +280,37 @@ sixel_split(struct terminal *term, struct sixel *six, } } +/* Row numbers are absolute */ static void -sixel_split_by_rectangle(struct terminal *term, int _row, - int col, int height, int width) +_sixel_split_by_rectangle( + struct terminal *term, int row, int col, int height, int width) { + assert(row + height <= term->grid->num_rows); + if (likely(tll_length(term->grid->sixel_images) == 0)) return; - const int start = (term->grid->offset + _row) & (term->grid->num_rows - 1); - const int end = (start + height - 1) & (term->grid->num_rows - 1); - const bool wraps = end < start; + /* We don't handle rectangle wrapping around */ + assert(row + height <= term->grid->num_rows); + + const int start = row; + const int end = row + height - 1; 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 bool six_wraps = six_end < six_start; + const bool six_wraps = six_end < six_start; /* TODO: do not generate sixels that wrap around */ - if ((six_wraps == wraps && + if ((!six_wraps && ((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 */ - (six_wraps && !wraps && + (six_wraps && ((start <= six_start && end >= six_start) || (start <= six_end && end >= six_end) || - (start >= six_start || end <= six_end))) || - (!six_wraps && wraps && - ((six_start <= start && six_end >= start) || /* Sixel croses region start boundary */ - (six_start <= end && six_end >= end) || /* Sixel crosses region end boundary */ - (six_start >= start || six_end <= end))) /* Sixel is fully enclosed by region */ + (start >= six_start || end <= six_end))) ) { const int col_start = six->pos.col; @@ -316,11 +328,61 @@ sixel_split_by_rectangle(struct terminal *term, int _row, } } +static void +sixel_split_by_rectangle( + struct terminal *term, int _row, int col, int height, int width) +{ + const int start = (term->grid->offset + _row) & (term->grid->num_rows - 1); + const int end = (start + height - 1) & (term->grid->num_rows - 1); + const bool wraps = end < start; + + if (wraps) { + int rows_to_wrap_around = term->grid->num_rows - start; + assert(height - rows_to_wrap_around > 0); + _sixel_split_by_rectangle(term, start, col, rows_to_wrap_around, width); + _sixel_split_by_rectangle(term, 0, col, height - rows_to_wrap_around, width); + } else + _sixel_split_by_rectangle(term, start, col, height, width); +} + +/* Row numbers are absolute */ +static void +sixel_split_at_point(struct terminal *term, int row, int col) +{ + assert(col >= 0); + + 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); + + bool wraps = six_end < six_start; + + if ((!wraps && row >= six_start && row <= six_end) || + (wraps && (row >= six_start || row <= six_end))) + { + const int col_start = six->pos.col; + const int col_end = six->pos.col + six->cols; + + if (col >= col_start && col < col_end) { + sixel_split(term, six, row, col, 1, 1); + sixel_erase(term, six); + tll_remove(term->grid->sixel_images, it); + } + } + } +} + void sixel_split_at_cursor(struct terminal *term) { - sixel_split_by_rectangle( - term, term->grid->cursor.point.row, term->grid->cursor.point.col, 1, 1); + sixel_split_at_point( + term, + (term->grid->offset + term->grid->cursor.point.row) & (term->grid->num_rows - 1), + term->grid->cursor.point.col); } void @@ -338,9 +400,7 @@ sixel_unhook(struct terminal *term) }; sixel_split_by_rectangle( - term, - term->grid->cursor.point.row, term->grid->cursor.point.col, - image.rows, image.cols); + term, term->grid->cursor.point.row, image.pos.col, image.rows, image.cols); LOG_DBG("generating %dx%d pixman image at %d-%d", image.width, image.height, image.pos.row, image.pos.row + image.rows); diff --git a/sixel.h b/sixel.h index bb3b6cb0..1d282e85 100644 --- a/sixel.h +++ b/sixel.h @@ -12,8 +12,9 @@ void sixel_unhook(struct terminal *term); void sixel_destroy(struct sixel *sixel); -void sixel_delete_in_range(struct terminal *term, int start, int end); -void sixel_delete_at_row(struct terminal *term, int _row); +/* Row numbers are relative to 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_split_at_cursor(struct terminal *term); void sixel_colors_report_current(struct terminal *term); From cc65002539e8969ec78c2dbc8299a76ebca2715b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 27 Jun 2020 14:19:08 +0200 Subject: [PATCH 09/11] sixel: never generate scrollback wrapping sixel images Instead, split them up into two (or more...). This makes the intersection detection logic *much* simpler. --- sixel.c | 149 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 96 insertions(+), 53 deletions(-) diff --git a/sixel.c b/sixel.c index 5f6fa7bd..01d221ab 100644 --- a/sixel.c +++ b/sixel.c @@ -91,6 +91,10 @@ sixel_erase(struct terminal *term, struct sixel *sixel) 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; @@ -99,11 +103,10 @@ sixel_delete_at_point(struct terminal *term, int row, int col) const int six_start = six->pos.row; const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1); - bool wraps = six_end < six_start; + /* We should never generate scrollback wrapping sixels */ + assert(six_end >= six_start); - if ((!wraps && row >= six_start && row <= six_end) || - (wraps && (row >= six_start || row <= six_end))) - { + if (row >= six_start && row <= six_end) { const int col_start = six->pos.col; const int col_end = six->pos.col + six->cols; @@ -127,6 +130,10 @@ 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); if (likely(tll_length(term->grid->sixel_images) == 0)) return; @@ -139,17 +146,13 @@ _sixel_delete_in_range(struct terminal *term, int start, int end) const int six_start = six->pos.row; const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1); - const bool six_wraps = six_end < six_start; - if ((!six_wraps && - ((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 */ - (six_wraps && - ((start <= six_start && end >= six_start) || - (start <= six_end && end >= six_end) || - (start >= six_start || end <= six_end))) - ) + /* 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); tll_remove(term->grid->sixel_images, it); @@ -179,6 +182,11 @@ static void sixel_split(struct terminal *term, struct sixel *six, int row, int col, int height, int width) { + assert(row >= 0); + assert(row + height <= term->grid->num_rows); + assert(col >= 0); + assert(col + width <= term->grid->num_cols); + int rel_above = min(max(row - six->pos.row, 0), six->rows); int rel_below = max(min(row + height - six->pos.row, six->rows), 0); int rel_left = min(max(col - six->pos.col, 0), six->cols); @@ -287,6 +295,11 @@ _sixel_split_by_rectangle( { 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); + if (likely(tll_length(term->grid->sixel_images) == 0)) return; @@ -301,17 +314,13 @@ _sixel_split_by_rectangle( const int six_start = six->pos.row; const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1); - const bool six_wraps = six_end < six_start; /* TODO: do not generate sixels that wrap around */ - if ((!six_wraps && - ((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 */ - (six_wraps && - ((start <= six_start && end >= six_start) || - (start <= six_end && end >= six_end) || - (start >= six_start || end <= six_end))) - ) + /* 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 */ { const int col_start = six->pos.col; const int col_end = six->pos.col + six->cols - 1; @@ -351,6 +360,11 @@ sixel_split_at_point(struct terminal *term, int row, int col) { assert(col >= 0); + assert(row >= 0); + assert(row < term->grid->num_rows); + assert(col >= 0); + assert(col < term->grid->num_cols); + if (likely(tll_length(term->grid->sixel_images) == 0)) return; @@ -359,11 +373,10 @@ sixel_split_at_point(struct terminal *term, int row, int col) const int six_start = six->pos.row; const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1); - bool wraps = six_end < six_start; + /* We should never generate scrollback wrapping sixels */ + assert(six_end >= six_start); - if ((!wraps && row >= six_start && row <= six_end) || - (wraps && (row >= six_start || row <= six_end))) - { + if (row >= six_start && row <= six_end) { const int col_start = six->pos.col; const int col_end = six->pos.col + six->cols; @@ -388,40 +401,70 @@ sixel_split_at_cursor(struct terminal *term) void sixel_unhook(struct terminal *term) { - struct sixel image = { - .data = term->sixel.image.data, - .width = term->sixel.image.width, - .height = term->sixel.image.height, - .rows = (term->sixel.image.height + term->cell_height - 1) / term->cell_height, - .cols = (term->sixel.image.width + term->cell_width - 1) / term->cell_width, - .pos = (struct coord){ - term->grid->cursor.point.col, - (term->grid->offset + term->grid->cursor.point.row) & (term->grid->num_rows - 1)}, - }; + int pixel_row_idx = 0; + int pixel_rows_left = term->sixel.image.height; + const int stride = term->sixel.image.width * sizeof(uint32_t); - sixel_split_by_rectangle( - term, term->grid->cursor.point.row, image.pos.col, image.rows, image.cols); + /* We do not allow sixels to cross the scrollback wrap-around, as + * this makes intersection calculations much more complicated */ + while (pixel_rows_left > 0) { + const struct coord *cursor = &term->grid->cursor.point; - LOG_DBG("generating %dx%d pixman image at %d-%d", image.width, image.height, image.pos.row, image.pos.row + image.rows); + const int cur_row = (term->grid->offset + cursor->row) & (term->grid->num_rows - 1); + const int rows_avail = term->grid->num_rows - cur_row; - image.pix = pixman_image_create_bits_no_clear( - PIXMAN_a8r8g8b8, - image.width, image.height, - term->sixel.image.data, - term->sixel.image.width * sizeof(uint32_t)); + const int pixel_rows_avail = rows_avail * term->cell_height; + + const int width = term->sixel.image.width; + const int height = min(pixel_rows_left, pixel_rows_avail); + + uint32_t *img_data; + if (pixel_row_idx == 0) + img_data = term->sixel.image.data; + else { + img_data = malloc(height * stride); + memcpy( + img_data, + &((uint8_t *)term->sixel.image.data)[pixel_row_idx * stride], + height * stride); + } + + struct sixel image = { + .data = img_data, + .width = width, + .height = height, + .rows = (height + term->cell_height - 1) / term->cell_height, + .cols = (width + term->cell_width - 1) / term->cell_width, + .pos = (struct coord){cursor->col, cur_row}, + }; + + sixel_split_by_rectangle( + term, cursor->row, image.pos.col, image.rows, image.cols); + + LOG_DBG("generating %dx%d pixman image at %d-%d", image.width, image.height, image.pos.row, image.pos.row + image.rows); + + image.pix = pixman_image_create_bits_no_clear( + PIXMAN_a8r8g8b8, + image.width, image.height, + img_data, stride); + + + for (size_t i = 0; i < image.rows; i++) + term_linefeed(term); + term_formfeed(term); + render_refresh(term); + + tll_push_back(term->grid->sixel_images, image); + + pixel_row_idx += height; + pixel_rows_left -= height; + } term->sixel.image.data = NULL; term->sixel.image.width = 0; term->sixel.image.height = 0; term->sixel.max_col = 0; term->sixel.pos = (struct coord){0, 0}; - - for (size_t i = 0; i < image.rows; i++) - term_linefeed(term); - term_formfeed(term); - render_refresh(term); - - tll_push_back(term->grid->sixel_images, image); } static unsigned From 8cdfd03c4b0402c2b6314e5fcfc939ad6b01c4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 27 Jun 2020 14:26:13 +0200 Subject: [PATCH 10/11] sixel: split: make pixman instantiation generic --- sixel.c | 61 +++++++++++++++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/sixel.c b/sixel.c index 01d221ab..e7e4ba8a 100644 --- a/sixel.c +++ b/sixel.c @@ -202,25 +202,22 @@ sixel_split(struct terminal *term, struct sixel *six, row, col, height, width, rel_above, rel_below, rel_left, rel_right); + struct sixel imgs[4] = {}; + if (rel_above > 0) { - struct sixel above = { + imgs[0] = (struct sixel){ .width = six->width, .height = rel_above * term->cell_height, .rows = rel_above, .cols = six->cols, .pos = six->pos, }; - above.data = malloc(above.width * above.height * sizeof(uint32_t)); - memcpy(above.data, six->data, above.width * above.height * sizeof(uint32_t)); - above.pix = pixman_image_create_bits_no_clear( - PIXMAN_a8r8g8b8, - above.width, above.height, - above.data, above.width * sizeof(uint32_t)); - tll_push_front(term->grid->sixel_images, above); + imgs[0].data = malloc(imgs[0].width * imgs[0].height * sizeof(uint32_t)); + memcpy(imgs[0].data, six->data, imgs[0].width * imgs[0].height * sizeof(uint32_t)); } if (rel_below < six->rows) { - struct sixel below = { + imgs[1] = (struct sixel){ .width = six->width, .height = six->height - rel_below * term->cell_height, .rows = six->rows - rel_below, @@ -229,20 +226,15 @@ sixel_split(struct terminal *term, struct sixel *six, six->pos.col, (six->pos.row + rel_below) & (term->grid->num_rows - 1)}, }; - below.data = malloc(below.width * below.height * sizeof(uint32_t)); + imgs[1].data = malloc(imgs[1].width * imgs[1].height * sizeof(uint32_t)); memcpy( - below.data, + imgs[1].data, &((const uint32_t *)six->data)[rel_below * term->cell_height * six->width], - below.width * below.height * sizeof(uint32_t)); - below.pix = pixman_image_create_bits_no_clear( - PIXMAN_a8r8g8b8, - below.width, below.height, - below.data, below.width * sizeof(uint32_t)); - tll_push_front(term->grid->sixel_images, below); + imgs[1].width * imgs[1].height * sizeof(uint32_t)); } if (rel_left > 0) { - struct sixel left = { + imgs[2] = (struct sixel){ .width = rel_left * term->cell_width, .height = min(term->cell_height, six->height - rel_above * term->cell_height), .rows = 1, @@ -251,21 +243,16 @@ sixel_split(struct terminal *term, struct sixel *six, six->pos.col, (six->pos.row + rel_above) & (term->grid->num_rows - 1)}, }; - left.data = malloc(left.width * left.height * sizeof(uint32_t)); + imgs[2].data = malloc(imgs[2].width * imgs[2].height * sizeof(uint32_t)); for (size_t i = 0; i < term->cell_height; i++) memcpy( - &((uint32_t *)left.data)[i * left.width], + &((uint32_t *)imgs[2].data)[i * imgs[2].width], &((const uint32_t *)six->data)[(rel_above * term->cell_height + i) * six->width], - left.width * sizeof(uint32_t)); - left.pix = pixman_image_create_bits_no_clear( - PIXMAN_a8r8g8b8, - left.width, left.height, - left.data, left.width * sizeof(uint32_t)); - tll_push_front(term->grid->sixel_images, left); + imgs[2].width * sizeof(uint32_t)); } if (rel_right < six->cols) { - struct sixel right = { + imgs[3] = (struct sixel){ .width = six->width - rel_right * term->cell_width, .height = min(term->cell_height, six->height - rel_above * term->cell_height), .rows = 1, @@ -274,17 +261,23 @@ sixel_split(struct terminal *term, struct sixel *six, six->pos.col + rel_right, (six->pos.row + rel_above) & (term->grid->num_rows - 1)}, }; - right.data = malloc(right.width * right.height * sizeof(uint32_t)); + imgs[3].data = malloc(imgs[3].width * imgs[3].height * sizeof(uint32_t)); for (size_t i = 0; i < term->cell_height; i++) memcpy( - &((uint32_t *)right.data)[i * right.width], + &((uint32_t *)imgs[3].data)[i * imgs[3].width], &((const uint32_t *)six->data)[(rel_above * term->cell_height + i) * six->width + rel_right * term->cell_width], - right.width * sizeof(uint32_t)); - right.pix = pixman_image_create_bits_no_clear( + imgs[3].width * sizeof(uint32_t)); + } + + for (size_t i = 0; i < sizeof(imgs) / sizeof(imgs[0]); i++) { + if (imgs[i].data == NULL) + continue; + + imgs[i].pix = pixman_image_create_bits_no_clear( PIXMAN_a8r8g8b8, - right.width, right.height, - right.data, right.width * sizeof(uint32_t)); - tll_push_front(term->grid->sixel_images, right); + imgs[i].width, imgs[i].height, + imgs[i].data, imgs[i].width * sizeof(uint32_t)); + tll_push_front(term->grid->sixel_images, imgs[i]); } } From f976df6f4d65a044fdd3a847b2f234eabb02fe9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 27 Jun 2020 14:27:49 +0200 Subject: [PATCH 11/11] changelog: sixel images can now be printed on --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6adb6a76..6d256668 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,10 @@ * Window title always being set to "foot" on reset. * 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. + ### Security