From f0fc82f0987166b3fe4724e50da05ba611231c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 21 Feb 2020 23:40:35 +0100 Subject: [PATCH] sixel: wip: maintain a list of finished, and "active" sixel images In unhook, add the generated image to a list of finished sixel images, along with positioning and size information. When rendering, loop this list of images, and render the images (or parts of) that are visible. When scrolling, check if any part of the images cover the re-cycled lines, and if so, remove the *entire* image from the list. This means we have the following limitations: * The renderer always renders the whole (visible area of) the image(s). There are times when this isn't necessary - for example, when the image is scrolled inside the visible area. * It would be nice if we could crop the image when parts of it is scrolled out. --- render.c | 65 ++++++++++++++++++++++++++++++++++++++++++++---------- sixel.c | 44 +++++++++++++++++------------------- terminal.c | 39 ++++++++++++++++++++++++++++++++ terminal.h | 12 ++++++++-- 4 files changed, 122 insertions(+), 38 deletions(-) diff --git a/render.c b/render.c index a88aba17..9cad3012 100644 --- a/render.c +++ b/render.c @@ -816,25 +816,66 @@ grid_render(struct terminal *term) cols_updated * term->cell_width, term->cell_height); } - if (term->sixel.pix != NULL) { + tll_foreach(term->sixel_images, it) { + int first_visible_row = -1; + + if (view_end >= term->grid->view) { + /* Not wrapped */ + for (size_t i = it->item.pos.row; + i < it->item.pos.row + (it->item.height + term->cell_height - 1) / term->cell_height; + i++) + { + int row = i & (term->grid->num_rows - 1); + if (row >= term->grid->view && row <= view_end) { + first_visible_row = i; + break; + } + } + } else { + /* Wrapped */ + for (size_t i = it->item.pos.row; + i < it->item.pos.row + (it->item.height + term->cell_height - 1) / term->cell_height; + i++) + { + int row = i & (term->grid->num_rows - 1); + if (row >= term->grid->view || row <= view_end) { + first_visible_row = i; + break; + } + } + } + + if (first_visible_row < 0) + continue; + + const int first_img_row = first_visible_row - it->item.pos.row; + const int row = first_visible_row & (term->grid->num_rows - 1); + const int view_aligned = + (row - term->grid->view + term->grid->num_rows) & (term->grid->num_rows - 1); + + const int x = term->x_margin + it->item.pos.col * term->cell_width; + const int y = max(term->y_margin, term->y_margin + view_aligned * term->cell_height); + + const int width = min(it->item.width, term->width - x - term->x_margin); + const int height = min( + it->item.height - first_img_row * term->cell_height, + term->height - y - term->y_margin); + + //LOG_INFO("x=%d, y=%d, width=%d, height=%d", x, y, width, height); + pixman_image_composite( PIXMAN_OP_SRC, - term->sixel.pix, + it->item.pix, NULL, pix, + 0, first_img_row * term->cell_height, 0, 0, - 0, 0, - 0, 0, - term->sixel.max_col, - term->sixel.row * 6); + x, y, + width, height); + wl_surface_damage_buffer( term->window->surface, - 0, 0, term->sixel.max_col, term->sixel.row * 6); - - pixman_image_unref(term->sixel.pix); - free(term->sixel.image); - term->sixel.pix = NULL; - term->sixel.image = NULL; + x, y, width, height); } if (term->flash.active) { diff --git a/sixel.c b/sixel.c index 2ca783e5..f9c4c4cf 100644 --- a/sixel.c +++ b/sixel.c @@ -18,24 +18,9 @@ static size_t count; void sixel_init(struct terminal *term) { - if (term->sixel.pix != NULL) { - pixman_image_unref(term->sixel.pix); - free(term->sixel.image); - term->sixel.pix = NULL; - term->sixel.image = NULL; - } - assert(term->sixel.palette == NULL); assert(term->sixel.image == NULL); - if (term->sixel.image != NULL) { - if (term->sixel.pix != NULL) { - pixman_image_unref(term->sixel.pix); - term->sixel.pix = NULL; - } - free(term->sixel.image); - } - term->sixel.state = SIXEL_SIXEL; term->sixel.row = 0; term->sixel.col = 0; @@ -58,28 +43,39 @@ sixel_unhook(struct terminal *term) free(term->sixel.palette); term->sixel.palette = NULL; - if (term->sixel.pix != NULL) - pixman_image_unref(term->sixel.pix); - LOG_DBG("generating %dx%d pixman image", term->sixel.row * 6, term->sixel.max_col); - if (term->sixel.col > 0) { + if (term->sixel.col >= 0) { if (term->sixel.col > term->sixel.max_col) term->sixel.max_col = term->sixel.col; term->sixel.row++; term->sixel.col = 0; } - term->sixel.pix = pixman_image_create_bits_no_clear( + struct sixel image = { + .data = term->sixel.image, + .width = term->sixel.max_col, + .height = term->sixel.row * 6, + .pos = (struct coord){term->cursor.point.col, term->grid->offset + term->cursor.point.row}, + }; + + image.pix = pixman_image_create_bits_no_clear( PIXMAN_a8r8g8b8, - term->sixel.max_col, - term->sixel.row * 6, + image.width, image.height, term->sixel.image, IMAGE_WIDTH * sizeof(uint32_t)); - size_t lines = max(1, term->sixel.row * 6 / term->cell_height); + tll_push_back(term->sixel_images, image); + + term->sixel.image = NULL; + term->sixel.max_col = 0; + term->sixel.col = 0; + term->sixel.row = 0; + + const size_t lines = (image.height + term->cell_height - 1) / term->cell_height; for (size_t i = 0; i < lines; i++) term_linefeed(term); + term_formfeed(term); render_refresh(term); } @@ -101,7 +97,7 @@ sixel_add(struct terminal *term, uint32_t color, uint8_t sixel) sixel >>= 1; if (bit) { size_t idx = (term->sixel.row * 6 + i) * IMAGE_WIDTH + term->sixel.col; - term->sixel.image[idx] = 0x00 << 24 | color; + term->sixel.image[idx] = term->colors.alpha / 256 << 24 | color; } } diff --git a/terminal.c b/terminal.c index ba19b552..b80b9898 100644 --- a/terminal.c +++ b/terminal.c @@ -740,6 +740,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, .lower_fd = delay_lower_fd, .upper_fd = delay_upper_fd, }, + .sixel_images = tll_init(), .hold_at_exit = conf->hold_at_exit, .shutdown_cb = shutdown_cb, .shutdown_data = shutdown_data, @@ -993,6 +994,12 @@ term_destroy(struct terminal *term) tll_free(term->ptmx_buffer); tll_free(term->tab_stops); + tll_foreach(term->sixel_images, it) { + pixman_image_unref(it->item.pix); + free(it->item.data); + } + tll_free(term->sixel_images); + free(term->foot_exe); free(term->cwd); @@ -1521,6 +1528,21 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows erase_line(term, grid_row_and_alloc(term->grid, r)); if (selection_on_row_in_view(term, r)) selection_cancel(term); + + + tll_foreach(term->sixel_images, it) { + /* Make it simple - remove the entire image if it starts + * getting scrolled out */ + + int img_top_row = it->item.pos.row & (term->grid->num_rows - 1); + int new_row = (term->grid->offset + r) & (term->grid->num_rows - 1); + + if (img_top_row == new_row) { + pixman_image_unref(it->item.pix); + free(it->item.data); + tll_remove(term->sixel_images, it); + } + } } term_damage_scroll(term, DAMAGE_SCROLL, region, rows); @@ -1572,6 +1594,23 @@ term_scroll_reverse_partial(struct terminal *term, erase_line(term, grid_row_and_alloc(term->grid, r)); if (selection_on_row_in_view(term, r)) selection_cancel(term); + + tll_foreach(term->sixel_images, it) { + /* Make it simple - remove the entire image if it starts + * getting scrolled out */ + + /* TODO: untested */ + + int img_rows = (it->item.height + term->cell_height - 1) / term->cell_height; + int img_bottom_row = (it->item.pos.row + img_rows) & (term->grid->num_rows - 1); + int new_row = (term->grid->offset + r) & (term->grid->num_rows - 1); + + if (img_bottom_row == new_row) { + pixman_image_unref(it->item.pix); + free(it->item.data); + tll_remove(term->sixel_images, it); + } + } } term_damage_scroll(term, DAMAGE_SCROLL_REVERSE, region, rows); diff --git a/terminal.h b/terminal.h index 756b0554..69b94624 100644 --- a/terminal.h +++ b/terminal.h @@ -175,6 +175,14 @@ struct ptmx_buffer { size_t idx; }; +struct sixel { + void *data; + pixman_image_t *pix; + int width; + int height; + struct coord pos; +}; + struct terminal { struct fdm *fdm; const struct config *conf; @@ -357,10 +365,10 @@ struct terminal { unsigned int param; unsigned param_idx; - - pixman_image_t *pix; } sixel; + tll(struct sixel) sixel_images; + bool hold_at_exit; bool is_shutting_down; void (*shutdown_cb)(void *data, int exit_code);