From 9e3bfb1eab2c7b1c7a1ec6021331fddc57e8a266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 21 Feb 2020 21:53:23 +0100 Subject: [PATCH 01/30] sixel: initial support This implements basic parsing of sixel data. Lots of limitations and temporary solutions as this is still work-in-progress: * Maximum image size hardcoded to 800x800 * No HLS color format support * Image is always rendered at 0x0 in the terminal --- dcs.c | 23 ++++- meson.build | 1 + render.c | 21 ++++ sixel.c | 284 ++++++++++++++++++++++++++++++++++++++++++++++++++++ sixel.h | 7 ++ terminal.h | 17 ++++ 6 files changed, 350 insertions(+), 3 deletions(-) create mode 100644 sixel.c create mode 100644 sixel.h diff --git a/dcs.c b/dcs.c index 048cd62e..f9e3c10c 100644 --- a/dcs.c +++ b/dcs.c @@ -3,6 +3,7 @@ #define LOG_MODULE "dcs" #define LOG_ENABLE_DBG 0 #include "log.h" +#include "sixel.h" #include "vt.h" static void @@ -35,9 +36,20 @@ dcs_hook(struct terminal *term, uint8_t final) assert(term->vt.dcs.data == NULL); assert(term->vt.dcs.size == 0); + assert(term->vt.dcs.put_handler == NULL); assert(term->vt.dcs.unhook_handler == NULL); switch (term->vt.private[0]) { + case 0: + switch (final) { + case 'q': + sixel_init(term); + term->vt.dcs.put_handler = &sixel_put; + term->vt.dcs.unhook_handler = &sixel_unhook; + break; + } + break; + case '=': switch (final) { case 's': @@ -75,9 +87,13 @@ void dcs_put(struct terminal *term, uint8_t c) { LOG_DBG("PUT: %c", c); - if (!ensure_size(term, term->vt.dcs.idx + 1)) - return; - term->vt.dcs.data[term->vt.dcs.idx++] = c; + if (term->vt.dcs.put_handler != NULL) + term->vt.dcs.put_handler(term, c); + else { + if (!ensure_size(term, term->vt.dcs.idx + 1)) + return; + term->vt.dcs.data[term->vt.dcs.idx++] = c; + } } void @@ -87,6 +103,7 @@ dcs_unhook(struct terminal *term) term->vt.dcs.unhook_handler(term); term->vt.dcs.unhook_handler = NULL; + term->vt.dcs.put_handler = NULL; free(term->vt.dcs.data); term->vt.dcs.data = NULL; diff --git a/meson.build b/meson.build index 2da5c65f..16483f51 100644 --- a/meson.build +++ b/meson.build @@ -117,6 +117,7 @@ executable( 'selection.c', 'selection.h', 'server.c', 'server.h', 'shm.c', 'shm.h', + 'sixel.c', 'sixel.h', 'slave.c', 'slave.h', 'terminal.c', 'terminal.h', 'tokenize.c', 'tokenize.h', diff --git a/render.c b/render.c index bb229288..a88aba17 100644 --- a/render.c +++ b/render.c @@ -816,6 +816,27 @@ grid_render(struct terminal *term) cols_updated * term->cell_width, term->cell_height); } + if (term->sixel.pix != NULL) { + pixman_image_composite( + PIXMAN_OP_SRC, + term->sixel.pix, + NULL, + pix, + 0, 0, + 0, 0, + 0, 0, + term->sixel.max_col, + term->sixel.row * 6); + 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; + } + if (term->flash.active) { /* Note: alpha is pre-computed in each color component */ /* TODO: dim while searching */ diff --git a/sixel.c b/sixel.c new file mode 100644 index 00000000..2ca783e5 --- /dev/null +++ b/sixel.c @@ -0,0 +1,284 @@ +#include "sixel.h" + +#include + +#define LOG_MODULE "sixel" +#define LOG_ENABLE_DBG 0 +#include "log.h" +#include "render.h" + +#define max(x, y) ((x) > (y) ? (x) : (y)) + +static const size_t COLOR_COUNT = 1024; +static const size_t IMAGE_WIDTH = 800; +static const size_t IMAGE_HEIGHT = 800; + +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; + term->sixel.color_idx = 0; + term->sixel.max_col = 0; + term->sixel.param = 0; + term->sixel.param_idx = 0; + memset(term->sixel.params, 0, sizeof(term->sixel.params)); + term->sixel.palette = calloc(COLOR_COUNT, sizeof(term->sixel.palette[0])); + term->sixel.image = calloc(IMAGE_WIDTH * IMAGE_HEIGHT, sizeof(term->sixel.image[0])); + + count = 0; + + /* TODO: default palette */ +} + +void +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 > 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( + PIXMAN_a8r8g8b8, + term->sixel.max_col, + term->sixel.row * 6, + term->sixel.image, + IMAGE_WIDTH * sizeof(uint32_t)); + + size_t lines = max(1, term->sixel.row * 6 / term->cell_height); + for (size_t i = 0; i < lines; i++) + term_linefeed(term); + render_refresh(term); +} + +static void +sixel_add(struct terminal *term, uint32_t color, uint8_t sixel) +{ + LOG_DBG("adding sixel %02hhx using color 0x%06x", sixel, color); + if (term->sixel.col >= IMAGE_WIDTH) { + LOG_WARN("column outside image width"); + return; + } + if (term->sixel.row >= IMAGE_HEIGHT) { + LOG_WARN("row outside image height"); + return; + } + + for (int i = 0; i < 6; i++) { + int bit = sixel & 1; + sixel >>= 1; + if (bit) { + size_t idx = (term->sixel.row * 6 + i) * IMAGE_WIDTH + term->sixel.col; + term->sixel.image[idx] = 0x00 << 24 | color; + } + } + + assert(sixel == 0); + + term->sixel.col++; +} + +static void +sixel_sixel(struct terminal *term, uint8_t c) +{ + switch (c) { + case '"': + term->sixel.state = SIXEL_RASTER; + term->sixel.param = 0; + term->sixel.param_idx = 0; + break; + + case '#': + term->sixel.state = SIXEL_COLOR; + term->sixel.color_idx = 0; + term->sixel.param = 0; + term->sixel.param_idx = 0; + break; + + case '$': + if (term->sixel.col > term->sixel.max_col) + term->sixel.max_col = term->sixel.col; + term->sixel.col = 0; + break; + + case '-': + if (term->sixel.col > term->sixel.max_col) + term->sixel.max_col = term->sixel.col; + term->sixel.row++; + term->sixel.col = 0; + break; + + case '!': + term->sixel.state = SIXEL_REPEAT; + term->sixel.param = 0; + term->sixel.param_idx = 0; + break; + + default: + if (c < '?' || c > '~') { + LOG_ERR("invalid sixel charactwer: '%c' at idx=%zu", c, count); + return; + } + + sixel_add(term, term->sixel.palette[term->sixel.color_idx], c - 63); + break; + } +} + +static void +sixel_repeat(struct terminal *term, uint8_t c) +{ + if (c >= '0' && c <= '9') { + term->sixel.param *= 10; + term->sixel.param += c - '0'; + } else { + LOG_DBG("repeating '%c' %u times", c, term->sixel.param); + term->sixel.state = SIXEL_SIXEL; + for (unsigned i = 0; i < term->sixel.param; i++) + sixel_sixel(term, c); + } +} + +static void +sixel_raster(struct terminal *term, uint8_t c) +{ + if (c >= '0' && c <= '9') { + term->sixel.param *= 10; + term->sixel.param += c - '0'; + } else { + if (term->sixel.param_idx < sizeof(term->sixel.params) / sizeof(term->sixel.params[0])) + term->sixel.params[term->sixel.param_idx++] = term->sixel.param; + + term->sixel.param = 0; + + if (c != ';') { + unsigned pan __attribute__((unused)) = term->sixel.params[0]; + unsigned pad __attribute__((unused)) = term->sixel.params[1]; + unsigned ph __attribute__((unused)) = term->sixel.params[2]; + unsigned pv __attribute__((unused)) = term->sixel.params[3]; + + LOG_DBG("pan=%u, pad=%u (aspect ratio = %u), size=%ux%u", + pan, pad, pan / pad, ph, pv); + } + + switch (c) { + case '#': term->sixel.state = SIXEL_COLOR; break; + case ';': term->sixel.state = SIXEL_RASTER; break; + default: term->sixel.state = SIXEL_SIXEL; sixel_sixel(term, c); break; + } + } +} + +static void +sixel_color(struct terminal *term, uint8_t c) +{ + if (c >= '0' && c <= '9') { + term->sixel.param *= 10; + term->sixel.param += c - '0'; + } else { + if (term->sixel.param < COLOR_COUNT) + term->sixel.color_idx = term->sixel.param; + else + term->sixel.color_idx = 0; + + term->sixel.param_idx = 0; + term->sixel.param = 0; + + switch (c) { + case '#': term->sixel.state = SIXEL_COLOR; break; + case ';': term->sixel.state = SIXEL_COLOR_SPEC; break; + default: term->sixel.state = SIXEL_SIXEL; sixel_sixel(term, c); break; + } + } +} + +static void +sixel_color_spec(struct terminal *term, uint8_t c) +{ + if (c >= '0' && c <= '9') { + term->sixel.param *= 10; + term->sixel.param += c - '0'; + } else { + if (term->sixel.param_idx < sizeof(term->sixel.params) / sizeof(term->sixel.params[0])) + term->sixel.params[term->sixel.param_idx++] = term->sixel.param; + + term->sixel.param = 0; + + if (c != ';') { + unsigned format = term->sixel.params[0]; + unsigned c1 = term->sixel.params[1]; + unsigned c2 = term->sixel.params[2]; + unsigned c3 = term->sixel.params[3]; + + if (format == 1) { + assert(false && "HLS color format not implemented"); + } else { + uint8_t r = 255 * c1 / 100; + uint8_t g = 255 * c2 / 100; + uint8_t b = 255 * c3 / 100; + + LOG_DBG("setting palette #%d = RGB %hhu/%hhu/%hhu", + term->sixel.color_idx, r, g, b); + + term->sixel.palette[term->sixel.color_idx] = r << 16 | g << 8 | b; + } + } + + switch (c) { + case '#': term->sixel.state = SIXEL_COLOR; break; + case ';': term->sixel.state = SIXEL_COLOR_SPEC; break; + default: term->sixel.state = SIXEL_SIXEL; sixel_sixel(term, c); break; + } + } +} + +void +sixel_put(struct terminal *term, uint8_t c) +{ + count++; + switch (c) { + case ' ': return; + case '\n': return; + case '\r': return; + } + + switch (term->sixel.state) { + case SIXEL_SIXEL: sixel_sixel(term, c); break; + case SIXEL_REPEAT: sixel_repeat(term, c); break; + case SIXEL_RASTER: sixel_raster(term, c); break; + case SIXEL_COLOR: sixel_color(term, c); break; + case SIXEL_COLOR_SPEC: sixel_color_spec(term, c); break; + } +} diff --git a/sixel.h b/sixel.h new file mode 100644 index 00000000..0cb24bfe --- /dev/null +++ b/sixel.h @@ -0,0 +1,7 @@ +#pragma once + +#include "terminal.h" + +void sixel_init(struct terminal *term); +void sixel_put(struct terminal *term, uint8_t c); +void sixel_unhook(struct terminal *term); diff --git a/terminal.h b/terminal.h index d9727b32..756b0554 100644 --- a/terminal.h +++ b/terminal.h @@ -131,6 +131,7 @@ struct vt { uint8_t *data; size_t size; size_t idx; + void (*put_handler)(struct terminal *term, uint8_t c); void (*unhook_handler)(struct terminal *term); } dcs; struct attributes attrs; @@ -344,6 +345,22 @@ struct terminal { int upper_fd; } delayed_render_timer; + struct { + enum { SIXEL_SIXEL, SIXEL_REPEAT, SIXEL_RASTER, SIXEL_COLOR, SIXEL_COLOR_SPEC} state; + int row; + int col; + int color_idx; + int max_col; + unsigned params[4]; + uint32_t *palette; + uint32_t *image; + + unsigned int param; + unsigned param_idx; + + pixman_image_t *pix; + } sixel; + bool hold_at_exit; bool is_shutting_down; void (*shutdown_cb)(void *data, int exit_code); From a1b5862db21ab0ae52ba2e8314be46432eac5225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 21 Feb 2020 23:35:43 +0100 Subject: [PATCH 02/30] scroll-up: ensure view is valid after adjusting an overshot scrollback When we scroll up, we need to ensure that we don't scroll too far, "past" the scrollback limit. I.e. we need to ensure we don't wrap around. The code did this. But, in certain scenarios, the resulting view points into uninitialized scrollback history. This happens when we haven't yet filled the entire scrollback, and scroll up enough lines to wrap around the scrollback. The old code would adjust the view for the wrap around, but doing so pointed the view at the not-yet utilized scrollback. --- commands.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/commands.c b/commands.c index 572dae68..53d0dfb3 100644 --- a/commands.c +++ b/commands.c @@ -55,6 +55,9 @@ cmd_scrollback_up(struct terminal *term, int rows) new_view = end + 1; } + while (term->grid->rows[new_view] == NULL) + new_view = (new_view + 1) % term->grid->num_rows; + #if defined(_DEBUG) for (int r = 0; r < term->rows; r++) assert(term->grid->rows[(new_view + r) % term->grid->num_rows] != NULL); 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 03/30] 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); From 564e8909f4c90320657a14812d9202dfeff53fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 21 Feb 2020 23:48:45 +0100 Subject: [PATCH 04/30] render: break out sixel rendering code --- render.c | 134 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 61 deletions(-) diff --git a/render.c b/render.c index 9cad3012..cd96ba24 100644 --- a/render.c +++ b/render.c @@ -519,6 +519,78 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf, } } +static void +render_sixel(struct terminal *term, pixman_image_t *pix, const struct sixel *sixel) +{ + int view_end = (term->grid->view + term->rows - 1) & (term->grid->num_rows - 1); + int first_visible_row = -1; + + if (view_end >= term->grid->view) { + /* Not wrapped */ + for (size_t i = sixel->pos.row; + i < sixel->pos.row + (sixel->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 = sixel->pos.row; + i < sixel->pos.row + (sixel->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) + return; + + const int first_img_row = first_visible_row - sixel->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 + sixel->pos.col * term->cell_width; + const int y = max(term->y_margin, term->y_margin + view_aligned * term->cell_height); + + const int width = min(sixel->width, term->width - x - term->x_margin); + const int height = min( + sixel->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, + sixel->pix, + NULL, + pix, + 0, first_img_row * term->cell_height, + 0, 0, + x, y, + width, height); + + wl_surface_damage_buffer( + term->window->surface, + x, y, width, height); +} + +static void +render_sixel_images(struct terminal *term, pixman_image_t *pix) +{ + tll_foreach(term->sixel_images, it) + render_sixel(term, pix, &it->item); +} + static void render_row(struct terminal *term, pixman_image_t *pix, struct row *row, int row_no) { @@ -816,67 +888,7 @@ grid_render(struct terminal *term) cols_updated * term->cell_width, term->cell_height); } - 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, - it->item.pix, - NULL, - pix, - 0, first_img_row * term->cell_height, - 0, 0, - x, y, - width, height); - - wl_surface_damage_buffer( - term->window->surface, - x, y, width, height); - } + render_sixel_images(term, pix); if (term->flash.active) { /* Note: alpha is pre-computed in each color component */ From 63140a68f516c8b4616bf6c726d23a75b73e5713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 00:05:25 +0100 Subject: [PATCH 05/30] sixel: calculate image height in (cell) rows --- render.c | 43 ++++++++++++++++++++++--------------------- sixel.c | 1 + terminal.c | 3 +-- terminal.h | 1 + 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/render.c b/render.c index cd96ba24..a4741b90 100644 --- a/render.c +++ b/render.c @@ -520,30 +520,23 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf, } static void -render_sixel(struct terminal *term, pixman_image_t *pix, const struct sixel *sixel) +render_sixel(struct terminal *term, pixman_image_t *pix, + const struct sixel *sixel) { int view_end = (term->grid->view + term->rows - 1) & (term->grid->num_rows - 1); int first_visible_row = -1; - if (view_end >= term->grid->view) { - /* Not wrapped */ - for (size_t i = sixel->pos.row; - i < sixel->pos.row + (sixel->height + term->cell_height - 1) / term->cell_height; - i++) - { - int row = i & (term->grid->num_rows - 1); + for (size_t i = sixel->pos.row; i < sixel->pos.row + sixel->rows; i++) { + int row = i & (term->grid->num_rows - 1); + + if (view_end >= term->grid->view) { + /* Not wrapped */ if (row >= term->grid->view && row <= view_end) { first_visible_row = i; break; } - } - } else { - /* Wrapped */ - for (size_t i = sixel->pos.row; - i < sixel->pos.row + (sixel->height + term->cell_height - 1) / term->cell_height; - i++) - { - int row = i & (term->grid->num_rows - 1); + } else { + /* Wrapped */ if (row >= term->grid->view || row <= view_end) { first_visible_row = i; break; @@ -554,20 +547,30 @@ render_sixel(struct terminal *term, pixman_image_t *pix, const struct sixel *six if (first_visible_row < 0) return; + /* First visible (0 based) row of the image */ const int first_img_row = first_visible_row - sixel->pos.row; + + /* Map first visible line to current grid view */ 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); + /* Translate row/column to x/y pixel values */ const int x = term->x_margin + sixel->pos.col * term->cell_width; - const int y = max(term->y_margin, term->y_margin + view_aligned * term->cell_height); + const int y = max( + term->y_margin, term->y_margin + view_aligned * term->cell_height); + /* Width/height, in pixels - and don't touch the window margins */ const int width = min(sixel->width, term->width - x - term->x_margin); const int height = min( sixel->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); + /* Verify we're not stepping outside the grid */ + assert(x >= term->x_margin); + assert(y >= term->y_margin); + assert(x + width <= term->width - term->x_margin); + assert(y + height <= term->height - term->y_margin); pixman_image_composite( PIXMAN_OP_SRC, @@ -579,9 +582,7 @@ render_sixel(struct terminal *term, pixman_image_t *pix, const struct sixel *six x, y, width, height); - wl_surface_damage_buffer( - term->window->surface, - x, y, width, height); + wl_surface_damage_buffer(term->window->surface, x, y, width, height); } static void diff --git a/sixel.c b/sixel.c index f9c4c4cf..0cfb2a24 100644 --- a/sixel.c +++ b/sixel.c @@ -56,6 +56,7 @@ sixel_unhook(struct terminal *term) .data = term->sixel.image, .width = term->sixel.max_col, .height = term->sixel.row * 6, + .rows = (term->sixel.row * 6 + term->cell_height - 1) / term->cell_height, .pos = (struct coord){term->cursor.point.col, term->grid->offset + term->cursor.point.row}, }; diff --git a/terminal.c b/terminal.c index b80b9898..c6c349c3 100644 --- a/terminal.c +++ b/terminal.c @@ -1601,8 +1601,7 @@ term_scroll_reverse_partial(struct terminal *term, /* 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 img_bottom_row = (it->item.pos.row + it->item.rows) & (term->grid->num_rows - 1); int new_row = (term->grid->offset + r) & (term->grid->num_rows - 1); if (img_bottom_row == new_row) { diff --git a/terminal.h b/terminal.h index 69b94624..bf113aa3 100644 --- a/terminal.h +++ b/terminal.h @@ -180,6 +180,7 @@ struct sixel { pixman_image_t *pix; int width; int height; + int rows; struct coord pos; }; From da76b8cbf85cdfd3caaf95e469db86a7bc1a2b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 00:10:42 +0100 Subject: [PATCH 06/30] sixel: ugly hack to remove over-written sixel images --- sixel.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sixel.c b/sixel.c index 0cfb2a24..240754f7 100644 --- a/sixel.c +++ b/sixel.c @@ -66,6 +66,14 @@ sixel_unhook(struct terminal *term) term->sixel.image, IMAGE_WIDTH * sizeof(uint32_t)); + tll_foreach(term->sixel_images, it) { + if (it->item.pos.row == image.pos.row) { + pixman_image_unref(it->item.pix); + free(it->item.data); + tll_remove(term->sixel_images, it); + } + } + tll_push_back(term->sixel_images, image); term->sixel.image = NULL; From 6f84b79810102708ec42a371e7a4a3446c741d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 00:19:38 +0100 Subject: [PATCH 07/30] sixel: always bump row on unhook --- sixel.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sixel.c b/sixel.c index 240754f7..4bb7a28e 100644 --- a/sixel.c +++ b/sixel.c @@ -45,12 +45,10 @@ sixel_unhook(struct terminal *term) LOG_DBG("generating %dx%d pixman image", term->sixel.row * 6, term->sixel.max_col); - 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; - } + if (term->sixel.col > term->sixel.max_col) + term->sixel.max_col = term->sixel.col; + term->sixel.row++; + term->sixel.col = 0; struct sixel image = { .data = term->sixel.image, From bb82b9fabc00e1fadca6fe630e91f07dbe1168b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 00:23:19 +0100 Subject: [PATCH 08/30] sixel: add sixel_destroy() --- sixel.c | 17 +++++++++++++---- sixel.h | 2 ++ terminal.c | 17 +++++++---------- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/sixel.c b/sixel.c index 4bb7a28e..fdad65a9 100644 --- a/sixel.c +++ b/sixel.c @@ -37,14 +37,22 @@ sixel_init(struct terminal *term) /* TODO: default palette */ } +void +sixel_destroy(struct sixel *sixel) +{ + pixman_image_unref(sixel->pix); + free(sixel->data); + + sixel->pix = NULL; + sixel->data = NULL; +} + void sixel_unhook(struct terminal *term) { free(term->sixel.palette); term->sixel.palette = NULL; - LOG_DBG("generating %dx%d pixman image", term->sixel.row * 6, term->sixel.max_col); - if (term->sixel.col > term->sixel.max_col) term->sixel.max_col = term->sixel.col; term->sixel.row++; @@ -58,6 +66,8 @@ sixel_unhook(struct terminal *term) .pos = (struct coord){term->cursor.point.col, term->grid->offset + term->cursor.point.row}, }; + LOG_DBG("generating %dx%d pixman image", image.width, image.height); + image.pix = pixman_image_create_bits_no_clear( PIXMAN_a8r8g8b8, image.width, image.height, @@ -66,8 +76,7 @@ sixel_unhook(struct terminal *term) tll_foreach(term->sixel_images, it) { if (it->item.pos.row == image.pos.row) { - pixman_image_unref(it->item.pix); - free(it->item.data); + sixel_destroy(&it->item); tll_remove(term->sixel_images, it); } } diff --git a/sixel.h b/sixel.h index 0cb24bfe..5933cf81 100644 --- a/sixel.h +++ b/sixel.h @@ -5,3 +5,5 @@ void sixel_init(struct terminal *term); void sixel_put(struct terminal *term, uint8_t c); void sixel_unhook(struct terminal *term); + +void sixel_destroy(struct sixel *sixel); diff --git a/terminal.c b/terminal.c index c6c349c3..cb7c08e9 100644 --- a/terminal.c +++ b/terminal.c @@ -19,12 +19,13 @@ #include "log.h" #include "async.h" +#include "config.h" #include "grid.h" #include "render.h" -#include "vt.h" #include "selection.h" -#include "config.h" +#include "sixel.h" #include "slave.h" +#include "vt.h" #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) @@ -994,10 +995,8 @@ 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_foreach(term->sixel_images, it) + sixel_destroy(&it->item); tll_free(term->sixel_images); free(term->foot_exe); @@ -1538,8 +1537,7 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows 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); + sixel_destroy(&it->item); tll_remove(term->sixel_images, it); } } @@ -1605,8 +1603,7 @@ term_scroll_reverse_partial(struct terminal *term, 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); + sixel_destroy(&it->item); tll_remove(term->sixel_images, it); } } From 00c6d4dde1346dc5f3bee394f147b8195c46a28c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 10:46:35 +0100 Subject: [PATCH 09/30] sixel: resize image buffer dynamically --- sixel.c | 103 +++++++++++++++++++++++++++++++++++++++++------------ terminal.h | 7 +++- 2 files changed, 86 insertions(+), 24 deletions(-) diff --git a/sixel.c b/sixel.c index fdad65a9..81dd980f 100644 --- a/sixel.c +++ b/sixel.c @@ -10,8 +10,6 @@ #define max(x, y) ((x) > (y) ? (x) : (y)) static const size_t COLOR_COUNT = 1024; -static const size_t IMAGE_WIDTH = 800; -static const size_t IMAGE_HEIGHT = 800; static size_t count; @@ -19,7 +17,7 @@ void sixel_init(struct terminal *term) { assert(term->sixel.palette == NULL); - assert(term->sixel.image == NULL); + assert(term->sixel.image.data == NULL); term->sixel.state = SIXEL_SIXEL; term->sixel.row = 0; @@ -30,7 +28,9 @@ sixel_init(struct terminal *term) term->sixel.param_idx = 0; memset(term->sixel.params, 0, sizeof(term->sixel.params)); term->sixel.palette = calloc(COLOR_COUNT, sizeof(term->sixel.palette[0])); - term->sixel.image = calloc(IMAGE_WIDTH * IMAGE_HEIGHT, sizeof(term->sixel.image[0])); + term->sixel.image.data = calloc(1 * 6, sizeof(term->sixel.image.data[0])); + term->sixel.image.width = 1; + term->sixel.image.height = 6; count = 0; @@ -59,7 +59,7 @@ sixel_unhook(struct terminal *term) term->sixel.col = 0; struct sixel image = { - .data = term->sixel.image, + .data = term->sixel.image.data, .width = term->sixel.max_col, .height = term->sixel.row * 6, .rows = (term->sixel.row * 6 + term->cell_height - 1) / term->cell_height, @@ -71,8 +71,8 @@ sixel_unhook(struct terminal *term) image.pix = pixman_image_create_bits_no_clear( PIXMAN_a8r8g8b8, image.width, image.height, - term->sixel.image, - IMAGE_WIDTH * sizeof(uint32_t)); + term->sixel.image.data, + term->sixel.image.width * sizeof(uint32_t)); tll_foreach(term->sixel_images, it) { if (it->item.pos.row == image.pos.row) { @@ -83,7 +83,9 @@ sixel_unhook(struct terminal *term) tll_push_back(term->sixel_images, image); - term->sixel.image = NULL; + term->sixel.image.data = NULL; + term->sixel.image.width = 0; + term->sixel.image.height = 0; term->sixel.max_col = 0; term->sixel.col = 0; term->sixel.row = 0; @@ -95,25 +97,79 @@ sixel_unhook(struct terminal *term) render_refresh(term); } +static bool +resize(struct terminal *term, int new_width, int new_height) +{ + LOG_DBG("resizing image: %dx%d -> %dx%d", + term->sixel.image.width, term->sixel.image.height, + new_width, new_height); + + uint32_t *old_data = term->sixel.image.data; + const int old_width = term->sixel.image.width; + const int old_height = term->sixel.image.height; + + assert(new_width >= old_width); + assert(new_height >= old_height); + + uint32_t *new_data = NULL; + + if (new_width == old_width) { + /* Width (and thus stride) is the same, so we can simply + * re-alloc the existing buffer */ + + new_data = realloc(old_data, new_width * new_height * sizeof(uint32_t)); + if (new_data == NULL) { + LOG_ERRNO("failed to reallocate sixel image buffer"); + return false; + } + + assert(new_height > old_height); + memset(&new_data[old_height * new_width], 0, (new_height - old_height) * new_width * sizeof(uint32_t)); + } else { + /* Width (and thus stride) change - need to allocate a new buffer */ + assert(new_width > old_width); + new_data = malloc(new_width * new_height * sizeof(uint32_t)); + + /* Copy old rows, and zero-initialize the tail of each row */ + for (int r = 0; r < old_height; r++) { + memcpy(&new_data[r * new_width], &old_data[r * old_width], old_width * sizeof(uint32_t)); + memset(&new_data[r * new_width + old_width], 0, (new_width - old_width) * sizeof(uint32_t)); + } + + /* Zero-initiailize new rows */ + for (int r = old_height; r < new_height; r++) + memset(&new_data[r * new_width], 0, new_width * sizeof(uint32_t)); + + free(old_data); + } + + assert(new_data != NULL); + term->sixel.image.data = new_data; + term->sixel.image.width = new_width; + term->sixel.image.height = new_height; + + return true; +} + static void sixel_add(struct terminal *term, uint32_t color, uint8_t sixel) { - LOG_DBG("adding sixel %02hhx using color 0x%06x", sixel, color); - if (term->sixel.col >= IMAGE_WIDTH) { - LOG_WARN("column outside image width"); - return; - } - if (term->sixel.row >= IMAGE_HEIGHT) { - LOG_WARN("row outside image height"); - return; + //LOG_DBG("adding sixel %02hhx using color 0x%06x", sixel, color); + + if (term->sixel.col >= term->sixel.image.width || + term->sixel.row * 6 >= term->sixel.image.height) + { + resize(term, + max(term->sixel.max_col, term->sixel.col + 1), + (term->sixel.row + 1) * 6); } - for (int i = 0; i < 6; i++) { - int bit = sixel & 1; - sixel >>= 1; - if (bit) { - size_t idx = (term->sixel.row * 6 + i) * IMAGE_WIDTH + term->sixel.col; - term->sixel.image[idx] = term->colors.alpha / 256 << 24 | color; + for (int i = 0; i < 6; i++, sixel >>= 1) { + if (sixel & 1) { + size_t pixel_row = term->sixel.row * 6 + i; + size_t stride = term->sixel.image.width; + size_t idx = pixel_row * stride + term->sixel.col; + term->sixel.image.data[idx] = term->colors.alpha / 256 << 24 | color; } } @@ -176,7 +232,8 @@ sixel_repeat(struct terminal *term, uint8_t c) term->sixel.param *= 10; term->sixel.param += c - '0'; } else { - LOG_DBG("repeating '%c' %u times", c, term->sixel.param); + //LOG_DBG("repeating '%c' %u times", c, term->sixel.param); + term->sixel.state = SIXEL_SIXEL; for (unsigned i = 0; i < term->sixel.param; i++) sixel_sixel(term, c); diff --git a/terminal.h b/terminal.h index bf113aa3..593e0a9f 100644 --- a/terminal.h +++ b/terminal.h @@ -362,7 +362,12 @@ struct terminal { int max_col; unsigned params[4]; uint32_t *palette; - uint32_t *image; + + struct { + uint32_t *data; + int width; + int height; + } image; unsigned int param; unsigned param_idx; From b03ffa3720b3a1d4e2b0b63f317287cf3afc58cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 10:46:54 +0100 Subject: [PATCH 10/30] sixel: resize image buffer after receiving a "raster" command --- sixel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sixel.c b/sixel.c index 81dd980f..b3bbdb65 100644 --- a/sixel.c +++ b/sixel.c @@ -260,6 +260,7 @@ sixel_raster(struct terminal *term, uint8_t c) LOG_DBG("pan=%u, pad=%u (aspect ratio = %u), size=%ux%u", pan, pad, pan / pad, ph, pv); + resize(term, ph, pv); } switch (c) { From ad5b2030f0094f9232af5170823ef66fdde7a625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 10:47:16 +0100 Subject: [PATCH 11/30] term: free sixel images on reset --- terminal.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/terminal.c b/terminal.c index cb7c08e9..3350c065 100644 --- a/terminal.c +++ b/terminal.c @@ -1121,6 +1121,10 @@ term_reset(struct terminal *term, bool hard) term->meta.esc_prefix = true; term->meta.eight_bit = true; + tll_foreach(term->sixel_images, it) + sixel_destroy(&it->item); + tll_free(term->sixel_images); + if (!hard) return; From 87193ff339df0e9808ef438239bfa84b4dcf1b7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 10:49:00 +0100 Subject: [PATCH 12/30] sixel: raster: only resize image buffer if necessary --- sixel.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sixel.c b/sixel.c index b3bbdb65..cfef67f3 100644 --- a/sixel.c +++ b/sixel.c @@ -260,7 +260,9 @@ sixel_raster(struct terminal *term, uint8_t c) LOG_DBG("pan=%u, pad=%u (aspect ratio = %u), size=%ux%u", pan, pad, pan / pad, ph, pv); - resize(term, ph, pv); + + if (ph >= term->sixel.image.height && pv >= term->sixel.image.width) + resize(term, ph, pv); } switch (c) { From 1b728dcac823193d96a490b7f7ee1acfa782da7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 10:50:05 +0100 Subject: [PATCH 13/30] sixel: rename state SIXEL_SIXEL -> SIXEL_GROUND --- sixel.c | 12 ++++++------ terminal.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sixel.c b/sixel.c index cfef67f3..29be361b 100644 --- a/sixel.c +++ b/sixel.c @@ -19,7 +19,7 @@ sixel_init(struct terminal *term) assert(term->sixel.palette == NULL); assert(term->sixel.image.data == NULL); - term->sixel.state = SIXEL_SIXEL; + term->sixel.state = SIXEL_GROUND; term->sixel.row = 0; term->sixel.col = 0; term->sixel.color_idx = 0; @@ -234,7 +234,7 @@ sixel_repeat(struct terminal *term, uint8_t c) } else { //LOG_DBG("repeating '%c' %u times", c, term->sixel.param); - term->sixel.state = SIXEL_SIXEL; + term->sixel.state = SIXEL_GROUND; for (unsigned i = 0; i < term->sixel.param; i++) sixel_sixel(term, c); } @@ -268,7 +268,7 @@ sixel_raster(struct terminal *term, uint8_t c) switch (c) { case '#': term->sixel.state = SIXEL_COLOR; break; case ';': term->sixel.state = SIXEL_RASTER; break; - default: term->sixel.state = SIXEL_SIXEL; sixel_sixel(term, c); break; + default: term->sixel.state = SIXEL_GROUND; sixel_sixel(term, c); break; } } } @@ -291,7 +291,7 @@ sixel_color(struct terminal *term, uint8_t c) switch (c) { case '#': term->sixel.state = SIXEL_COLOR; break; case ';': term->sixel.state = SIXEL_COLOR_SPEC; break; - default: term->sixel.state = SIXEL_SIXEL; sixel_sixel(term, c); break; + default: term->sixel.state = SIXEL_GROUND; sixel_sixel(term, c); break; } } } @@ -331,7 +331,7 @@ sixel_color_spec(struct terminal *term, uint8_t c) switch (c) { case '#': term->sixel.state = SIXEL_COLOR; break; case ';': term->sixel.state = SIXEL_COLOR_SPEC; break; - default: term->sixel.state = SIXEL_SIXEL; sixel_sixel(term, c); break; + default: term->sixel.state = SIXEL_GROUND; sixel_sixel(term, c); break; } } } @@ -347,7 +347,7 @@ sixel_put(struct terminal *term, uint8_t c) } switch (term->sixel.state) { - case SIXEL_SIXEL: sixel_sixel(term, c); break; + case SIXEL_GROUND: sixel_sixel(term, c); break; case SIXEL_REPEAT: sixel_repeat(term, c); break; case SIXEL_RASTER: sixel_raster(term, c); break; case SIXEL_COLOR: sixel_color(term, c); break; diff --git a/terminal.h b/terminal.h index 593e0a9f..c160c902 100644 --- a/terminal.h +++ b/terminal.h @@ -355,7 +355,7 @@ struct terminal { } delayed_render_timer; struct { - enum { SIXEL_SIXEL, SIXEL_REPEAT, SIXEL_RASTER, SIXEL_COLOR, SIXEL_COLOR_SPEC} state; + enum { SIXEL_GROUND, SIXEL_REPEAT, SIXEL_RASTER, SIXEL_COLOR, SIXEL_COLOR_SPEC} state; int row; int col; int color_idx; From f1936959603c7f1530c6ce72cfda1f64edcb0432 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 10:54:52 +0100 Subject: [PATCH 14/30] sixel: use a struct coord for current sixel position --- sixel.c | 46 ++++++++++++++++++++++------------------------ terminal.h | 22 +++++++++++----------- 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/sixel.c b/sixel.c index 29be361b..8751ac2a 100644 --- a/sixel.c +++ b/sixel.c @@ -20,8 +20,7 @@ sixel_init(struct terminal *term) assert(term->sixel.image.data == NULL); term->sixel.state = SIXEL_GROUND; - term->sixel.row = 0; - term->sixel.col = 0; + term->sixel.pos = (struct coord){0, 0}; term->sixel.color_idx = 0; term->sixel.max_col = 0; term->sixel.param = 0; @@ -53,16 +52,16 @@ sixel_unhook(struct terminal *term) free(term->sixel.palette); term->sixel.palette = NULL; - if (term->sixel.col > term->sixel.max_col) - term->sixel.max_col = term->sixel.col; - term->sixel.row++; - term->sixel.col = 0; + if (term->sixel.pos.col > term->sixel.max_col) + term->sixel.max_col = term->sixel.pos.col; + term->sixel.pos.row++; + term->sixel.pos.col = 0; struct sixel image = { .data = term->sixel.image.data, .width = term->sixel.max_col, - .height = term->sixel.row * 6, - .rows = (term->sixel.row * 6 + term->cell_height - 1) / term->cell_height, + .height = term->sixel.pos.row * 6, + .rows = (term->sixel.pos.row * 6 + term->cell_height - 1) / term->cell_height, .pos = (struct coord){term->cursor.point.col, term->grid->offset + term->cursor.point.row}, }; @@ -87,8 +86,7 @@ sixel_unhook(struct terminal *term) term->sixel.image.width = 0; term->sixel.image.height = 0; term->sixel.max_col = 0; - term->sixel.col = 0; - term->sixel.row = 0; + term->sixel.pos = (struct coord){0, 0}; const size_t lines = (image.height + term->cell_height - 1) / term->cell_height; for (size_t i = 0; i < lines; i++) @@ -156,26 +154,26 @@ sixel_add(struct terminal *term, uint32_t color, uint8_t sixel) { //LOG_DBG("adding sixel %02hhx using color 0x%06x", sixel, color); - if (term->sixel.col >= term->sixel.image.width || - term->sixel.row * 6 >= term->sixel.image.height) + if (term->sixel.pos.col >= term->sixel.image.width || + term->sixel.pos.row * 6 >= term->sixel.image.height) { resize(term, - max(term->sixel.max_col, term->sixel.col + 1), - (term->sixel.row + 1) * 6); + max(term->sixel.max_col, term->sixel.pos.col + 1), + (term->sixel.pos.row + 1) * 6); } for (int i = 0; i < 6; i++, sixel >>= 1) { if (sixel & 1) { - size_t pixel_row = term->sixel.row * 6 + i; + size_t pixel_row = term->sixel.pos.row * 6 + i; size_t stride = term->sixel.image.width; - size_t idx = pixel_row * stride + term->sixel.col; + size_t idx = pixel_row * stride + term->sixel.pos.col; term->sixel.image.data[idx] = term->colors.alpha / 256 << 24 | color; } } assert(sixel == 0); - term->sixel.col++; + term->sixel.pos.col++; } static void @@ -196,16 +194,16 @@ sixel_sixel(struct terminal *term, uint8_t c) break; case '$': - if (term->sixel.col > term->sixel.max_col) - term->sixel.max_col = term->sixel.col; - term->sixel.col = 0; + if (term->sixel.pos.col > term->sixel.max_col) + term->sixel.max_col = term->sixel.pos.col; + term->sixel.pos.col = 0; break; case '-': - if (term->sixel.col > term->sixel.max_col) - term->sixel.max_col = term->sixel.col; - term->sixel.row++; - term->sixel.col = 0; + if (term->sixel.pos.col > term->sixel.max_col) + term->sixel.max_col = term->sixel.pos.col; + term->sixel.pos.row++; + term->sixel.pos.col = 0; break; case '!': diff --git a/terminal.h b/terminal.h index c160c902..05b9b00b 100644 --- a/terminal.h +++ b/terminal.h @@ -356,21 +356,21 @@ struct terminal { struct { enum { SIXEL_GROUND, SIXEL_REPEAT, SIXEL_RASTER, SIXEL_COLOR, SIXEL_COLOR_SPEC} state; - int row; - int col; - int color_idx; - int max_col; - unsigned params[4]; - uint32_t *palette; + + struct coord pos; /* Current sixel coordinate */ + int color_idx; /* Current palette index */ + int max_col; /* Largest column index we've seen (aka the image width) */ + uint32_t *palette; /* Color palette */ struct { - uint32_t *data; - int width; - int height; + uint32_t *data; /* Raw image data, in ARGB */ + int width; /* Image width, in pixels */ + int height; /* Image height, in pixels */ } image; - unsigned int param; - unsigned param_idx; + unsigned params[4]; /* Collected parmaeters, for RASTER, COLOR_SPEC */ + unsigned int param; /* Currently collecting parameter, for RASTER, COLOR_SPEC and REPEAT */ + unsigned param_idx; /* Parameters seen */ } sixel; tll(struct sixel) sixel_images; From 762526485113ea711dcf322063b1f3c6dc3e753e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 11:30:30 +0100 Subject: [PATCH 15/30] sixel: clean up state machine Use proper state names, and implement the state machine using switch statements. --- sixel.c | 214 +++++++++++++++++++++++++++-------------------------- terminal.h | 11 ++- 2 files changed, 118 insertions(+), 107 deletions(-) diff --git a/sixel.c b/sixel.c index 8751ac2a..0656e4c1 100644 --- a/sixel.c +++ b/sixel.c @@ -7,7 +7,9 @@ #include "log.h" #include "render.h" +#define ALEN(v) (sizeof(v) / sizeof(v[0])) #define max(x, y) ((x) > (y) ? (x) : (y)) +#define min(x, y) ((x) < (y) ? (x) : (y)) static const size_t COLOR_COUNT = 1024; @@ -19,7 +21,7 @@ sixel_init(struct terminal *term) assert(term->sixel.palette == NULL); assert(term->sixel.image.data == NULL); - term->sixel.state = SIXEL_GROUND; + term->sixel.state = SIXEL_DECSIXEL; term->sixel.pos = (struct coord){0, 0}; term->sixel.color_idx = 0; term->sixel.max_col = 0; @@ -177,17 +179,23 @@ sixel_add(struct terminal *term, uint32_t color, uint8_t sixel) } static void -sixel_sixel(struct terminal *term, uint8_t c) +decsixel(struct terminal *term, uint8_t c) { switch (c) { case '"': - term->sixel.state = SIXEL_RASTER; + term->sixel.state = SIXEL_DECGRA; + term->sixel.param = 0; + term->sixel.param_idx = 0; + break; + + case '!': + term->sixel.state = SIXEL_DECGRI; term->sixel.param = 0; term->sixel.param_idx = 0; break; case '#': - term->sixel.state = SIXEL_COLOR; + term->sixel.state = SIXEL_DECGCI; term->sixel.color_idx = 0; term->sixel.param = 0; term->sixel.param_idx = 0; @@ -206,115 +214,118 @@ sixel_sixel(struct terminal *term, uint8_t c) term->sixel.pos.col = 0; break; - case '!': - term->sixel.state = SIXEL_REPEAT; - term->sixel.param = 0; - term->sixel.param_idx = 0; + case '?'...'~': + sixel_add(term, term->sixel.palette[term->sixel.color_idx], c - 63); + break; + + case ' ': + case '\n': + case '\r': break; default: - if (c < '?' || c > '~') { - LOG_ERR("invalid sixel charactwer: '%c' at idx=%zu", c, count); - return; - } - - sixel_add(term, term->sixel.palette[term->sixel.color_idx], c - 63); + LOG_WARN("invalid sixel charactwer: '%c' at idx=%zu", c, count); break; } } static void -sixel_repeat(struct terminal *term, uint8_t c) +decgra(struct terminal *term, uint8_t c) { - if (c >= '0' && c <= '9') { + switch (c) { + case '0'...'9': term->sixel.param *= 10; term->sixel.param += c - '0'; - } else { + break; + + case ';': + if (term->sixel.param_idx < ALEN(term->sixel.params)) + term->sixel.params[term->sixel.param_idx++] = term->sixel.param; + term->sixel.param = 0; + break; + + default: { + if (term->sixel.param_idx < ALEN(term->sixel.params)) + term->sixel.params[term->sixel.param_idx++] = term->sixel.param; + + int nparams = term->sixel.param_idx; + unsigned pan = nparams > 0 ? term->sixel.params[0] : 0; + unsigned pad = nparams > 1 ? term->sixel.params[1] : 0; + unsigned ph = nparams > 2 ? term->sixel.params[2] : 0; + unsigned pv = nparams > 3 ? term->sixel.params[3] : 0; + + pan = pan > 0 ? pan : 1; + pad = pad > 0 ? pad : 1; + + LOG_DBG("pan=%u, pad=%u (aspect ratio = %u), size=%ux%u", + pan, pad, pan / pad, ph, pv); + + if (ph >= term->sixel.image.height && pv >= term->sixel.image.width) + resize(term, ph, pv); + + term->sixel.state = SIXEL_DECSIXEL; + sixel_put(term, c); + break; + } + } +} + +static void +decgri(struct terminal *term, uint8_t c) +{ + switch (c) { + case '0'...'9': + term->sixel.param *= 10; + term->sixel.param += c - '0'; + break; + + default: //LOG_DBG("repeating '%c' %u times", c, term->sixel.param); - - term->sixel.state = SIXEL_GROUND; for (unsigned i = 0; i < term->sixel.param; i++) - sixel_sixel(term, c); + decsixel(term, c); + term->sixel.state = SIXEL_DECSIXEL; + break; } } static void -sixel_raster(struct terminal *term, uint8_t c) +decgci(struct terminal *term, uint8_t c) { - if (c >= '0' && c <= '9') { + switch (c) { + case '0'...'9': term->sixel.param *= 10; term->sixel.param += c - '0'; - } else { - if (term->sixel.param_idx < sizeof(term->sixel.params) / sizeof(term->sixel.params[0])) + break; + + case ';': + if (term->sixel.param_idx < ALEN(term->sixel.params)) + term->sixel.params[term->sixel.param_idx++] = term->sixel.param; + term->sixel.param = 0; + break; + + default: { + if (term->sixel.param_idx < ALEN(term->sixel.params)) term->sixel.params[term->sixel.param_idx++] = term->sixel.param; - term->sixel.param = 0; + int nparams = term->sixel.param_idx; - if (c != ';') { - unsigned pan __attribute__((unused)) = term->sixel.params[0]; - unsigned pad __attribute__((unused)) = term->sixel.params[1]; - unsigned ph __attribute__((unused)) = term->sixel.params[2]; - unsigned pv __attribute__((unused)) = term->sixel.params[3]; + if (nparams > 0) + term->sixel.color_idx = min(1 + term->sixel.params[0], COLOR_COUNT - 1); - LOG_DBG("pan=%u, pad=%u (aspect ratio = %u), size=%ux%u", - pan, pad, pan / pad, ph, pv); + if (nparams > 4) { + unsigned format = term->sixel.params[1]; + unsigned c1 = term->sixel.params[2]; + unsigned c2 = term->sixel.params[3]; + unsigned c3 = term->sixel.params[4]; - if (ph >= term->sixel.image.height && pv >= term->sixel.image.width) - resize(term, ph, pv); - } - - switch (c) { - case '#': term->sixel.state = SIXEL_COLOR; break; - case ';': term->sixel.state = SIXEL_RASTER; break; - default: term->sixel.state = SIXEL_GROUND; sixel_sixel(term, c); break; - } - } -} - -static void -sixel_color(struct terminal *term, uint8_t c) -{ - if (c >= '0' && c <= '9') { - term->sixel.param *= 10; - term->sixel.param += c - '0'; - } else { - if (term->sixel.param < COLOR_COUNT) - term->sixel.color_idx = term->sixel.param; - else - term->sixel.color_idx = 0; - - term->sixel.param_idx = 0; - term->sixel.param = 0; - - switch (c) { - case '#': term->sixel.state = SIXEL_COLOR; break; - case ';': term->sixel.state = SIXEL_COLOR_SPEC; break; - default: term->sixel.state = SIXEL_GROUND; sixel_sixel(term, c); break; - } - } -} - -static void -sixel_color_spec(struct terminal *term, uint8_t c) -{ - if (c >= '0' && c <= '9') { - term->sixel.param *= 10; - term->sixel.param += c - '0'; - } else { - if (term->sixel.param_idx < sizeof(term->sixel.params) / sizeof(term->sixel.params[0])) - term->sixel.params[term->sixel.param_idx++] = term->sixel.param; - - term->sixel.param = 0; - - if (c != ';') { - unsigned format = term->sixel.params[0]; - unsigned c1 = term->sixel.params[1]; - unsigned c2 = term->sixel.params[2]; - unsigned c3 = term->sixel.params[3]; - - if (format == 1) { + switch (format) { + case 1: { /* HLS */ + LOG_ERR("HLS color format not implemented"); assert(false && "HLS color format not implemented"); - } else { + break; + } + + case 2: { /* RGB */ uint8_t r = 255 * c1 / 100; uint8_t g = 255 * c2 / 100; uint8_t b = 255 * c3 / 100; @@ -323,32 +334,27 @@ sixel_color_spec(struct terminal *term, uint8_t c) term->sixel.color_idx, r, g, b); term->sixel.palette[term->sixel.color_idx] = r << 16 | g << 8 | b; + break; + } } } - switch (c) { - case '#': term->sixel.state = SIXEL_COLOR; break; - case ';': term->sixel.state = SIXEL_COLOR_SPEC; break; - default: term->sixel.state = SIXEL_GROUND; sixel_sixel(term, c); break; - } + term->sixel.state = SIXEL_DECSIXEL; + sixel_put(term, c); + break; + } } } void sixel_put(struct terminal *term, uint8_t c) { - count++; - switch (c) { - case ' ': return; - case '\n': return; - case '\r': return; + switch (term->sixel.state) { + case SIXEL_DECSIXEL: decsixel(term, c); break; + case SIXEL_DECGRA: decgra(term, c); break; + case SIXEL_DECGRI: decgri(term, c); break; + case SIXEL_DECGCI: decgci(term, c); break; } - switch (term->sixel.state) { - case SIXEL_GROUND: sixel_sixel(term, c); break; - case SIXEL_REPEAT: sixel_repeat(term, c); break; - case SIXEL_RASTER: sixel_raster(term, c); break; - case SIXEL_COLOR: sixel_color(term, c); break; - case SIXEL_COLOR_SPEC: sixel_color_spec(term, c); break; - } + count++; } diff --git a/terminal.h b/terminal.h index 05b9b00b..882abfba 100644 --- a/terminal.h +++ b/terminal.h @@ -355,7 +355,12 @@ struct terminal { } delayed_render_timer; struct { - enum { SIXEL_GROUND, SIXEL_REPEAT, SIXEL_RASTER, SIXEL_COLOR, SIXEL_COLOR_SPEC} state; + enum { + SIXEL_DECSIXEL, /* DECSIXEL body part ", $, -, ? ... ~ */ + SIXEL_DECGRA, /* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */ + SIXEL_DECGRI, /* DECGRI Graphics Repeat Introducer ! Pn Ch */ + SIXEL_DECGCI, /* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */ + } state; struct coord pos; /* Current sixel coordinate */ int color_idx; /* Current palette index */ @@ -368,8 +373,8 @@ struct terminal { int height; /* Image height, in pixels */ } image; - unsigned params[4]; /* Collected parmaeters, for RASTER, COLOR_SPEC */ - unsigned int param; /* Currently collecting parameter, for RASTER, COLOR_SPEC and REPEAT */ + unsigned params[5]; /* Collected parmaeters, for RASTER, COLOR_SPEC */ + unsigned param; /* Currently collecting parameter, for RASTER, COLOR_SPEC and REPEAT */ unsigned param_idx; /* Parameters seen */ } sixel; From a418521ced195562c00b49a7676d9396eaf8189f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 11:37:25 +0100 Subject: [PATCH 16/30] sixel: mention why we add one --- sixel.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sixel.c b/sixel.c index 0656e4c1..7e547822 100644 --- a/sixel.c +++ b/sixel.c @@ -309,8 +309,10 @@ decgci(struct terminal *term, uint8_t c) int nparams = term->sixel.param_idx; - if (nparams > 0) + if (nparams > 0) { + /* Add one, as we use idx==0 for background color (TODO) */ term->sixel.color_idx = min(1 + term->sixel.params[0], COLOR_COUNT - 1); + } if (nparams > 4) { unsigned format = term->sixel.params[1]; From a8dc61d0ffbe9f95ab2fb5d39474d66d5e57de2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 11:52:22 +0100 Subject: [PATCH 17/30] sixel: resize: initialize new rows/columns with the background color --- sixel.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/sixel.c b/sixel.c index 7e547822..f7966b80 100644 --- a/sixel.c +++ b/sixel.c @@ -29,10 +29,13 @@ sixel_init(struct terminal *term) term->sixel.param_idx = 0; memset(term->sixel.params, 0, sizeof(term->sixel.params)); term->sixel.palette = calloc(COLOR_COUNT, sizeof(term->sixel.palette[0])); - term->sixel.image.data = calloc(1 * 6, sizeof(term->sixel.image.data[0])); + term->sixel.image.data = malloc(1 * 6 * sizeof(term->sixel.image.data[0])); term->sixel.image.width = 1; term->sixel.image.height = 6; + for (size_t i = 0; i < 1 * 6; i++) + term->sixel.image.data[i] = term->colors.alpha / 256 << 24 | term->sixel.palette[0]; + count = 0; /* TODO: default palette */ @@ -124,25 +127,28 @@ resize(struct terminal *term, int new_width, int new_height) } assert(new_height > old_height); - memset(&new_data[old_height * new_width], 0, (new_height - old_height) * new_width * sizeof(uint32_t)); + } else { /* Width (and thus stride) change - need to allocate a new buffer */ assert(new_width > old_width); new_data = malloc(new_width * new_height * sizeof(uint32_t)); - /* Copy old rows, and zero-initialize the tail of each row */ + /* Copy old rows, and initialize new columns to background color */ for (int r = 0; r < old_height; r++) { memcpy(&new_data[r * new_width], &old_data[r * old_width], old_width * sizeof(uint32_t)); - memset(&new_data[r * new_width + old_width], 0, (new_width - old_width) * sizeof(uint32_t)); + + for (int c = old_width; c < new_width; c++) + new_data[r * new_width + c] = term->colors.alpha / 256 << 24 | term->sixel.palette[0]; } - - /* Zero-initiailize new rows */ - for (int r = old_height; r < new_height; r++) - memset(&new_data[r * new_width], 0, new_width * sizeof(uint32_t)); - free(old_data); } + /* Initialize new rows to background color */ + for (int r = old_height; r < new_height; r++) { + for (int c = 0; c < new_width; c++) + new_data[r * new_width + c] = term->colors.alpha / 256 << 24 | term->sixel.palette[0]; + } + assert(new_data != NULL); term->sixel.image.data = new_data; term->sixel.image.width = new_width; From 23d39e6f90bf57a4690f18312f32ef99eb665774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 11:52:48 +0100 Subject: [PATCH 18/30] sixel: finalize: use width/height from image, not row/col pos --- sixel.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/sixel.c b/sixel.c index f7966b80..7da45ec5 100644 --- a/sixel.c +++ b/sixel.c @@ -57,16 +57,11 @@ sixel_unhook(struct terminal *term) free(term->sixel.palette); term->sixel.palette = NULL; - if (term->sixel.pos.col > term->sixel.max_col) - term->sixel.max_col = term->sixel.pos.col; - term->sixel.pos.row++; - term->sixel.pos.col = 0; - struct sixel image = { .data = term->sixel.image.data, - .width = term->sixel.max_col, - .height = term->sixel.pos.row * 6, - .rows = (term->sixel.pos.row * 6 + term->cell_height - 1) / term->cell_height, + .width = term->sixel.image.width, + .height = term->sixel.image.height, + .rows = (term->sixel.image.height + term->cell_height - 1) / term->cell_height, .pos = (struct coord){term->cursor.point.col, term->grid->offset + term->cursor.point.row}, }; @@ -93,8 +88,7 @@ sixel_unhook(struct terminal *term) term->sixel.max_col = 0; term->sixel.pos = (struct coord){0, 0}; - const size_t lines = (image.height + term->cell_height - 1) / term->cell_height; - for (size_t i = 0; i < lines; i++) + for (size_t i = 0; i < image.rows; i++) term_linefeed(term); term_formfeed(term); render_refresh(term); From a9971f4c328a1cdeae59dc767b57f10c47224c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 12:16:19 +0100 Subject: [PATCH 19/30] csi: DA: we now implement sixel --- csi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csi.c b/csi.c index 3fc64ee7..ed237592 100644 --- a/csi.c +++ b/csi.c @@ -372,7 +372,7 @@ csi_dispatch(struct terminal *term, uint8_t final) * - 28 Rectangular editing. * - 29 ANSI text locator (i.e., DEC Locator mode). */ - const char *reply = "\033[?62;6;15;17;22c"; + const char *reply = "\033[?62;4;6;15;17;22c"; term_to_slave(term, reply, strlen(reply)); break; } From 5e65db3b070375abf4f32e62b0f05cc464ec28a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 14:01:20 +0100 Subject: [PATCH 20/30] sixel: use terminal's background color, not palette[0] --- sixel.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sixel.c b/sixel.c index 7da45ec5..69025669 100644 --- a/sixel.c +++ b/sixel.c @@ -34,7 +34,7 @@ sixel_init(struct terminal *term) term->sixel.image.height = 6; for (size_t i = 0; i < 1 * 6; i++) - term->sixel.image.data[i] = term->colors.alpha / 256 << 24 | term->sixel.palette[0]; + term->sixel.image.data[i] = term->colors.alpha / 256 << 24 | term->colors.bg; count = 0; @@ -132,7 +132,7 @@ resize(struct terminal *term, int new_width, int new_height) memcpy(&new_data[r * new_width], &old_data[r * old_width], old_width * sizeof(uint32_t)); for (int c = old_width; c < new_width; c++) - new_data[r * new_width + c] = term->colors.alpha / 256 << 24 | term->sixel.palette[0]; + new_data[r * new_width + c] = term->colors.alpha / 256 << 24 | term->colors.bg; } free(old_data); } @@ -140,7 +140,7 @@ resize(struct terminal *term, int new_width, int new_height) /* Initialize new rows to background color */ for (int r = old_height; r < new_height; r++) { for (int c = 0; c < new_width; c++) - new_data[r * new_width + c] = term->colors.alpha / 256 << 24 | term->sixel.palette[0]; + new_data[r * new_width + c] = term->colors.alpha / 256 << 24 | term->colors.bg; } assert(new_data != NULL); From 8e37a18083e8cdba1cfd4ee042d8f8edf7cfad39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 14:02:00 +0100 Subject: [PATCH 21/30] sixel: application configurable palette size (color count) This implements the CSI escapes for retrieving and (re)setting the palette size. --- csi.c | 25 +++++++++++++++++++++++++ sixel.c | 41 +++++++++++++++++++++++++++++++++++------ sixel.h | 7 +++++++ terminal.c | 3 +++ terminal.h | 3 +++ 5 files changed, 73 insertions(+), 6 deletions(-) diff --git a/csi.c b/csi.c index ed237592..d5c06905 100644 --- a/csi.c +++ b/csi.c @@ -15,6 +15,7 @@ #include "grid.h" #include "vt.h" #include "selection.h" +#include "sixel.h" #define min(x, y) ((x) < (y) ? (x) : (y)) @@ -1227,6 +1228,30 @@ csi_dispatch(struct terminal *term, uint8_t final) } break; + case 'S': { + unsigned target = vt_param_get(term, 0, 0); + unsigned operation = vt_param_get(term, 1, 0); + + switch (target) { + case 1: + switch (operation) { + case 1: sixel_colors_report_current(term); break; + case 2: sixel_colors_reset(term); break; + case 3: sixel_colors_set(term, vt_param_get(term, 2, 0)); break; + case 4: sixel_colors_report_max(term); + } + break; + + case 2: + break; + + case 3: + break; + + } + break; + } + default: UNHANDLED(); break; diff --git a/sixel.c b/sixel.c index 69025669..2536f467 100644 --- a/sixel.c +++ b/sixel.c @@ -11,7 +11,6 @@ #define max(x, y) ((x) > (y) ? (x) : (y)) #define min(x, y) ((x) < (y) ? (x) : (y)) -static const size_t COLOR_COUNT = 1024; static size_t count; @@ -20,6 +19,7 @@ sixel_init(struct terminal *term) { assert(term->sixel.palette == NULL); assert(term->sixel.image.data == NULL); + assert(term->sixel.palette_size <= SIXEL_MAX_COLORS); term->sixel.state = SIXEL_DECSIXEL; term->sixel.pos = (struct coord){0, 0}; @@ -28,7 +28,7 @@ sixel_init(struct terminal *term) term->sixel.param = 0; term->sixel.param_idx = 0; memset(term->sixel.params, 0, sizeof(term->sixel.params)); - term->sixel.palette = calloc(COLOR_COUNT, sizeof(term->sixel.palette[0])); + term->sixel.palette = calloc(term->sixel.palette_size, sizeof(term->sixel.palette[0])); term->sixel.image.data = malloc(1 * 6 * sizeof(term->sixel.image.data[0])); term->sixel.image.width = 1; term->sixel.image.height = 6; @@ -309,10 +309,8 @@ decgci(struct terminal *term, uint8_t c) int nparams = term->sixel.param_idx; - if (nparams > 0) { - /* Add one, as we use idx==0 for background color (TODO) */ - term->sixel.color_idx = min(1 + term->sixel.params[0], COLOR_COUNT - 1); - } + if (nparams > 0) + term->sixel.color_idx = min(term->sixel.params[0], term->sixel.palette_size - 1); if (nparams > 4) { unsigned format = term->sixel.params[1]; @@ -360,3 +358,34 @@ sixel_put(struct terminal *term, uint8_t c) count++; } + +void +sixel_colors_report_current(struct terminal *term) +{ + char reply[24]; + snprintf(reply, sizeof(reply), "\033[?1;0;%uS", term->sixel.palette_size); + term_to_slave(term, reply, strlen(reply)); +} + +void +sixel_colors_reset(struct terminal *term) +{ + term->sixel.palette_size = SIXEL_MAX_COLORS; + LOG_DBG("sixel palette size reset to %u", SIXEL_MAX_COLORS); +} + +void +sixel_colors_set(struct terminal *term, unsigned count) +{ + unsigned new_palette_size = min(max(2, count), SIXEL_MAX_COLORS); + term->sixel.palette_size = new_palette_size; + LOG_DBG("sixel palette size set to %u", new_palette_size); +} + +void +sixel_colors_report_max(struct terminal *term) +{ + char reply[24]; + snprintf(reply, sizeof(reply), "\033[?1;0;%uS", SIXEL_MAX_COLORS); + term_to_slave(term, reply, strlen(reply)); +} diff --git a/sixel.h b/sixel.h index 5933cf81..8e6d2949 100644 --- a/sixel.h +++ b/sixel.h @@ -2,8 +2,15 @@ #include "terminal.h" +#define SIXEL_MAX_COLORS 1024u + void sixel_init(struct terminal *term); void sixel_put(struct terminal *term, uint8_t c); void sixel_unhook(struct terminal *term); void sixel_destroy(struct sixel *sixel); + +void sixel_colors_report_current(struct terminal *term); +void sixel_colors_reset(struct terminal *term); +void sixel_colors_set(struct terminal *term, unsigned count); +void sixel_colors_report_max(struct terminal *term); diff --git a/terminal.c b/terminal.c index 3350c065..493a8ada 100644 --- a/terminal.c +++ b/terminal.c @@ -741,6 +741,9 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, .lower_fd = delay_lower_fd, .upper_fd = delay_upper_fd, }, + .sixel = { + .palette_size = SIXEL_MAX_COLORS, + }, .sixel_images = tll_init(), .hold_at_exit = conf->hold_at_exit, .shutdown_cb = shutdown_cb, diff --git a/terminal.h b/terminal.h index 882abfba..e0738057 100644 --- a/terminal.h +++ b/terminal.h @@ -376,6 +376,9 @@ struct terminal { unsigned params[5]; /* Collected parmaeters, for RASTER, COLOR_SPEC */ unsigned param; /* Currently collecting parameter, for RASTER, COLOR_SPEC and REPEAT */ unsigned param_idx; /* Parameters seen */ + + /* Application configurable */ + unsigned palette_size; } sixel; tll(struct sixel) sixel_images; From 25b09b909ca39cee6432ffd10e179336dbc178e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 21:03:24 +0100 Subject: [PATCH 22/30] sixel: application configurable image max width/height --- csi.c | 10 +++++--- sixel.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++--- sixel.h | 5 ++++ terminal.h | 4 ++- 4 files changed, 86 insertions(+), 8 deletions(-) diff --git a/csi.c b/csi.c index d5c06905..38d017f5 100644 --- a/csi.c +++ b/csi.c @@ -1243,11 +1243,13 @@ csi_dispatch(struct terminal *term, uint8_t final) break; case 2: + switch (operation) { + case 1: sixel_geometry_report_current(term); break; + case 2: sixel_geometry_reset(term); break; + case 3: sixel_geometry_set(term, vt_param_get(term, 2, 0), vt_param_get(term, 3, 0)); break; + case 4: sixel_geometry_report_max(term); + } break; - - case 3: - break; - } break; } diff --git a/sixel.c b/sixel.c index 2536f467..01c6987b 100644 --- a/sixel.c +++ b/sixel.c @@ -94,6 +94,22 @@ sixel_unhook(struct terminal *term) render_refresh(term); } +static unsigned +max_width(const struct terminal *term) +{ + return term->sixel.max_width == 0 + ? term->cols * term->cell_width + : term->sixel.max_width; +} + +static unsigned +max_height(const struct terminal *term) +{ + return term->sixel.max_height == 0 + ? term->rows * term->cell_height + : term->sixel.max_height; +} + static bool resize(struct terminal *term, int new_width, int new_height) { @@ -156,8 +172,14 @@ sixel_add(struct terminal *term, uint32_t color, uint8_t sixel) { //LOG_DBG("adding sixel %02hhx using color 0x%06x", sixel, color); + if (term->sixel.pos.col >= max_width(term) || + term->sixel.pos.row * 6 + 5 >= max_height(term)) + { + return; + } + if (term->sixel.pos.col >= term->sixel.image.width || - term->sixel.pos.row * 6 >= term->sixel.image.height) + term->sixel.pos.row * 6 + 5 >= term->sixel.image.height) { resize(term, max(term->sixel.max_col, term->sixel.pos.col + 1), @@ -174,7 +196,6 @@ sixel_add(struct terminal *term, uint32_t color, uint8_t sixel) } assert(sixel == 0); - term->sixel.pos.col++; } @@ -260,8 +281,11 @@ decgra(struct terminal *term, uint8_t c) LOG_DBG("pan=%u, pad=%u (aspect ratio = %u), size=%ux%u", pan, pad, pan / pad, ph, pv); - if (ph >= term->sixel.image.height && pv >= term->sixel.image.width) + if (ph >= term->sixel.image.height && pv >= term->sixel.image.width && + ph <= max_height(term) && pv <= max_width(term)) + { resize(term, ph, pv); + } term->sixel.state = SIXEL_DECSIXEL; sixel_put(term, c); @@ -365,6 +389,7 @@ sixel_colors_report_current(struct terminal *term) char reply[24]; snprintf(reply, sizeof(reply), "\033[?1;0;%uS", term->sixel.palette_size); term_to_slave(term, reply, strlen(reply)); + LOG_DBG("query response for current color count: %u", term->sixel.palette_size); } void @@ -388,4 +413,48 @@ sixel_colors_report_max(struct terminal *term) char reply[24]; snprintf(reply, sizeof(reply), "\033[?1;0;%uS", SIXEL_MAX_COLORS); term_to_slave(term, reply, strlen(reply)); + LOG_DBG("query response for max color count: %u", SIXEL_MAX_COLORS); +} + +void +sixel_geometry_report_current(struct terminal *term) +{ + char reply[64]; + snprintf(reply, sizeof(reply), "\033[?2;0;%u;%uS", + max_width(term), max_height(term)); + term_to_slave(term, reply, strlen(reply)); + + LOG_DBG("query response for current sixel geometry: %ux%u", + max_width(term), max_height(term)); +} + +void +sixel_geometry_reset(struct terminal *term) +{ + term->sixel.max_width = 0; + term->sixel.max_height = 0; + LOG_DBG("sixel geometry reset to %ux%u", max_width(term), max_height(term)); +} + +void +sixel_geometry_set(struct terminal *term, unsigned width, unsigned height) +{ + term->sixel.max_width = width; + term->sixel.max_height = height; + LOG_DBG("sixel geometry set to %ux%u", + term->sixel.max_width, term->sixel.max_height); +} + +void +sixel_geometry_report_max(struct terminal *term) +{ + unsigned max_width = term->cols * term->cell_width; + unsigned max_height = term->rows * term->cell_height; + + char reply[64]; + snprintf(reply, sizeof(reply), "\033[?2;0;%u;%uS", max_width, max_height); + term_to_slave(term, reply, strlen(reply)); + + LOG_DBG("query response for max sixel geometry: %ux%u", + max_width, max_height); } diff --git a/sixel.h b/sixel.h index 8e6d2949..dbce21c5 100644 --- a/sixel.h +++ b/sixel.h @@ -14,3 +14,8 @@ void sixel_colors_report_current(struct terminal *term); void sixel_colors_reset(struct terminal *term); void sixel_colors_set(struct terminal *term, unsigned count); void sixel_colors_report_max(struct terminal *term); + +void sixel_geometry_report_current(struct terminal *term); +void sixel_geometry_reset(struct terminal *term); +void sixel_geometry_set(struct terminal *term, unsigned width, unsigned height); +void sixel_geometry_report_max(struct terminal *term); diff --git a/terminal.h b/terminal.h index e0738057..5e491d29 100644 --- a/terminal.h +++ b/terminal.h @@ -378,7 +378,9 @@ struct terminal { unsigned param_idx; /* Parameters seen */ /* Application configurable */ - unsigned palette_size; + unsigned palette_size; /* Number of colors in palette */ + unsigned max_width; /* Maximum image width, in pixels */ + unsigned max_height; /* Maximum image height, in pixels */ } sixel; tll(struct sixel) sixel_images; From 7985cefd65d98f6ffd86c64c3bb866a0283e10d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 21:04:24 +0100 Subject: [PATCH 23/30] sixel: add comments to max_width()/max_height() --- sixel.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sixel.c b/sixel.c index 01c6987b..be21d263 100644 --- a/sixel.c +++ b/sixel.c @@ -97,6 +97,7 @@ sixel_unhook(struct terminal *term) static unsigned max_width(const struct terminal *term) { + /* foot extension - treat 0 to mean current terminal size */ return term->sixel.max_width == 0 ? term->cols * term->cell_width : term->sixel.max_width; @@ -105,6 +106,7 @@ max_width(const struct terminal *term) static unsigned max_height(const struct terminal *term) { + /* foot extension - treat 0 to mean current terminal size */ return term->sixel.max_height == 0 ? term->rows * term->cell_height : term->sixel.max_height; From f96eb5ff074dff8c9c9dce63886a94875604901c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 21:18:55 +0100 Subject: [PATCH 24/30] sixel: add HLS color format support --- meson.build | 1 + sixel-hls.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++ sixel-hls.h | 9 ++++ sixel.c | 7 +++- 4 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 sixel-hls.c create mode 100644 sixel-hls.h diff --git a/meson.build b/meson.build index 16483f51..21c23718 100644 --- a/meson.build +++ b/meson.build @@ -118,6 +118,7 @@ executable( 'server.c', 'server.h', 'shm.c', 'shm.h', 'sixel.c', 'sixel.h', + 'sixel-hls.c', 'sixel-hls.h', 'slave.c', 'slave.h', 'terminal.c', 'terminal.h', 'tokenize.c', 'tokenize.h', diff --git a/sixel-hls.c b/sixel-hls.c new file mode 100644 index 00000000..8b88b414 --- /dev/null +++ b/sixel-hls.c @@ -0,0 +1,117 @@ +// sixel.c (part of mintty) +// this function is derived from a part of graphics.c +// in Xterm pl#310 originally written by Ross Combs. +// +// Copyright 2013,2014 by Ross Combs +// +// All Rights Reserved +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name(s) of the above copyright +// holders shall not be used in advertising or otherwise to promote the +// sale, use or other dealings in this Software without prior written +// authorization. + +#include "sixel-hls.h" + +#define SIXEL_RGB(r, g, b) (((r) << 16) + ((g) << 8) + (b)) + +int +hls_to_rgb(int hue, int lum, int sat) +{ + double hs = (hue + 240) % 360; + double hv = hs / 360.0; + double lv = lum / 100.0; + double sv = sat / 100.0; + double c, x, m, c2; + double r1, g1, b1; + int r, g, b; + int hpi; + + if (sat == 0) { + r = g = b = lum * 255 / 100; + return SIXEL_RGB(r, g, b); + } + + if ((c2 = ((2.0 * lv) - 1.0)) < 0.0) { + c2 = -c2; + } + c = (1.0 - c2) * sv; + hpi = (int) (hv * 6.0); + x = (hpi & 1) ? c : 0.0; + m = lv - 0.5 * c; + + switch (hpi) { + case 0: + r1 = c; + g1 = x; + b1 = 0.0; + break; + case 1: + r1 = x; + g1 = c; + b1 = 0.0; + break; + case 2: + r1 = 0.0; + g1 = c; + b1 = x; + break; + case 3: + r1 = 0.0; + g1 = x; + b1 = c; + break; + case 4: + r1 = x; + g1 = 0.0; + b1 = c; + break; + case 5: + r1 = c; + g1 = 0.0; + b1 = x; + break; + default: + return SIXEL_RGB(255, 255, 255); + } + + r = (int) ((r1 + m) * 100.0 + 0.5); + g = (int) ((g1 + m) * 100.0 + 0.5); + b = (int) ((b1 + m) * 100.0 + 0.5); + + if (r < 0) { + r = 0; + } else if (r > 100) { + r = 100; + } + if (g < 0) { + g = 0; + } else if (g > 100) { + g = 100; + } + if (b < 0) { + b = 0; + } else if (b > 100) { + b = 100; + } + return SIXEL_RGB(r * 255 / 100, g * 255 / 100, b * 255 / 100); +} diff --git a/sixel-hls.h b/sixel-hls.h new file mode 100644 index 00000000..6d6b9f2f --- /dev/null +++ b/sixel-hls.h @@ -0,0 +1,9 @@ +#pragma once + +/* + * Primary color hues: + * blue: 0 degrees + * red: 120 degrees + * green: 240 degrees + */ +int hls_to_rgb(int hue, int lum, int sat); diff --git a/sixel.c b/sixel.c index be21d263..7b1c8646 100644 --- a/sixel.c +++ b/sixel.c @@ -6,6 +6,7 @@ #define LOG_ENABLE_DBG 0 #include "log.h" #include "render.h" +#include "sixel-hls.h" #define ALEN(v) (sizeof(v) / sizeof(v[0])) #define max(x, y) ((x) > (y) ? (x) : (y)) @@ -346,8 +347,10 @@ decgci(struct terminal *term, uint8_t c) switch (format) { case 1: { /* HLS */ - LOG_ERR("HLS color format not implemented"); - assert(false && "HLS color format not implemented"); + uint32_t rgb = hls_to_rgb(c1, c2, c3); + LOG_DBG("setting palette #%d = HLS %hhu/%hhu/%hhu (0x%06x)", + term->sixel.color_idx, c1, c2, c3, rgb); + term->sixel.palette[term->sixel.color_idx] = rgb; break; } From 05ff2a8a15d5be6b996a16e84ffed5b8b43b7d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 21:20:22 +0100 Subject: [PATCH 25/30] sixel: don't ever shrink the image --- sixel.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sixel.c b/sixel.c index 7b1c8646..15aeca43 100644 --- a/sixel.c +++ b/sixel.c @@ -184,9 +184,15 @@ sixel_add(struct terminal *term, uint32_t color, uint8_t sixel) if (term->sixel.pos.col >= term->sixel.image.width || term->sixel.pos.row * 6 + 5 >= term->sixel.image.height) { - resize(term, - max(term->sixel.max_col, term->sixel.pos.col + 1), - (term->sixel.pos.row + 1) * 6); + int width = max( + term->sixel.image.width, + max(term->sixel.max_col, term->sixel.pos.col + 1)); + + int height = max( + term->sixel.image.height, + (term->sixel.pos.row + 1) * 6)) + + resize(term, width, height);; } for (int i = 0; i < 6; i++, sixel >>= 1) { From 6e8c4149d2db6b400d6f47881a49ecaf9fb5c1a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 21:21:14 +0100 Subject: [PATCH 26/30] README: add sixel support to feature list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f67ac691..ce1f8d9f 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ * Scrollback search * Color emoji support * "Server" mode (one master process, many windows) +* [Sixel image support](https://en.wikipedia.org/wiki/Sixel) ## Non-features From 462229af7025a29f421d4acf657a71175bafb8c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 21:24:21 +0100 Subject: [PATCH 27/30] sixel: oops --- sixel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sixel.c b/sixel.c index 15aeca43..7d31dea4 100644 --- a/sixel.c +++ b/sixel.c @@ -190,9 +190,9 @@ sixel_add(struct terminal *term, uint32_t color, uint8_t sixel) int height = max( term->sixel.image.height, - (term->sixel.pos.row + 1) * 6)) + (term->sixel.pos.row + 1) * 6); - resize(term, width, height);; + resize(term, width, height); } for (int i = 0; i < 6; i++, sixel >>= 1) { From 80361ca04e12b65396d27fa1d981f6739861e453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 21:35:45 +0100 Subject: [PATCH 28/30] sixel: purge images at current cursor row When printing a character, or starting a new sixel image, purge all images that cover the cursor's current row. --- sixel.c | 25 ++++++++++++++++++------- sixel.h | 1 + terminal.c | 2 ++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/sixel.c b/sixel.c index 7d31dea4..db87844e 100644 --- a/sixel.c +++ b/sixel.c @@ -39,6 +39,8 @@ sixel_init(struct terminal *term) count = 0; + sixel_purge_at_cursor(term); + /* TODO: default palette */ } @@ -52,6 +54,22 @@ sixel_destroy(struct sixel *sixel) sixel->data = NULL; } +void +sixel_purge_at_cursor(struct terminal *term) +{ + const int row = term->grid->offset + term->cursor.point.row; + + tll_foreach(term->sixel_images, it) { + const int start = it->item.pos.row; + const int end = start + it->item.rows; + + if (row >= start && row < end) { + sixel_destroy(&it->item); + tll_remove(term->sixel_images, it); + } + } +} + void sixel_unhook(struct terminal *term) { @@ -74,13 +92,6 @@ sixel_unhook(struct terminal *term) term->sixel.image.data, term->sixel.image.width * sizeof(uint32_t)); - tll_foreach(term->sixel_images, it) { - if (it->item.pos.row == image.pos.row) { - sixel_destroy(&it->item); - tll_remove(term->sixel_images, it); - } - } - tll_push_back(term->sixel_images, image); term->sixel.image.data = NULL; diff --git a/sixel.h b/sixel.h index dbce21c5..c5c489af 100644 --- a/sixel.h +++ b/sixel.h @@ -9,6 +9,7 @@ void sixel_put(struct terminal *term, uint8_t c); void sixel_unhook(struct terminal *term); void sixel_destroy(struct sixel *sixel); +void sixel_purge_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 493a8ada..5c7b94e5 100644 --- a/terminal.c +++ b/terminal.c @@ -2128,6 +2128,8 @@ term_print(struct terminal *term, wchar_t wc, int width) print_linewrap(term); print_insert(term, width); + sixel_purge_at_cursor(term); + /* *Must* get current cell *after* linewrap+insert */ struct row *row = term->grid->cur_row; struct cell *cell = &row->cells[term->cursor.point.col]; From 270b7cc6f4a16e67fb65cccb43bb16a3e47c64ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 21:55:10 +0100 Subject: [PATCH 29/30] sixel: purge images in unhook, rather than in init This fixes an issue where the cursor was seen blinking in animated GIFs. --- sixel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sixel.c b/sixel.c index db87844e..19dd6069 100644 --- a/sixel.c +++ b/sixel.c @@ -39,8 +39,6 @@ sixel_init(struct terminal *term) count = 0; - sixel_purge_at_cursor(term); - /* TODO: default palette */ } @@ -76,6 +74,8 @@ sixel_unhook(struct terminal *term) free(term->sixel.palette); term->sixel.palette = NULL; + sixel_purge_at_cursor(term); + struct sixel image = { .data = term->sixel.image.data, .width = term->sixel.image.width, From 5c7fc3a0c7e231385c31ac52cd79946d0cb94feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 22 Feb 2020 22:04:28 +0100 Subject: [PATCH 30/30] README: add screenshot of sixel graphics --- README.md | 3 +++ doc/sixel-wow.png | Bin 0 -> 119970 bytes 2 files changed, 3 insertions(+) create mode 100644 doc/sixel-wow.png diff --git a/README.md b/README.md index ce1f8d9f..46cd07cc 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,9 @@ * "Server" mode (one master process, many windows) * [Sixel image support](https://en.wikipedia.org/wiki/Sixel) + ![wow](doc/sixel-wow.png "Sixel screenshot") + + ## Non-features diff --git a/doc/sixel-wow.png b/doc/sixel-wow.png new file mode 100644 index 0000000000000000000000000000000000000000..da481ac8dce811601d377086651bf0d5e62514be GIT binary patch literal 119970 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�bIpf++}=HfPWAto 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%0Dw`QB;&u3LZpoM$xo|8xE0 z;QB2e^VXd{#(ctXe@sF&zqR?<96`R@mtI#qd(USzulICG))%?G>?_Wszp!~`{B!?( znXi&H)hrA*rMkayBnz^iux^p7&Au9A`hDBhx~VS?-MCgYyZk$2&OKM|T6u*TD-YdB zSoZGT&4gvYn2)&&FW72Pe51VVhMT6(<=5vKRJN_#ko8VkrQ*AuYty#{bLO4ice%Jy z=6>Yve?fQe&)hs;{O>~klGLvIVlSevCtovj-_H^I_|R#Ouz&L9$L9avp|Af}U!FmD zk=W;-Hao1o|NZlQv4+KUwK?z4h3hly@rXb2P=_zQtc*{Aclz4-M_;Yr+wXb!X{O!D zZC{NUOs`EeE500lk1dfYoGo$DnT`I_+mlOeWX`9>E;X0bNt!GBac|VQTf0C0)p6RN z`{mdlqv+gor`E@Q+x)rwf6UAIGY|iMcW`^f&MUdkpP&8^Q6}biP4?aO$fT+l5tZ4_ zq9&Uiwyg|v%}@Va&wp#1ZOY5q9olhQj@RygyXojfeJ2LaoXxgJx@Wdt`tV8O-uXWq z4(Hp2v>pOWF7zNN6&={$ZEDu}|>u&g!lFf>EO9axX9ATe$Uaq{W|< zcW*E5Ud+i#(&m^nUd1b^7-GjcN52(Qka8Kf7pC_Rb=_@pJECn_1E2 zS3WN&l!*4bCcD;JZKBlnYqGDeMVH(BeaZT+;Mc;9^TU#C?_X4Z-Ocbr##x)oA*`I? z82{~dQPq3jxx&QcCv{K1q*Ty)znsBGKqp716r1nmA7Vn{COziq=Dq$N$REcXD%}toVj^w zw$-)YyBaf>Sh6lK|G4AwYzv>Ln~W36UvsTC+dom4A-Vff>TC6_3g^!i)Re5Wp7?L3 z{N=2_?W=s3#HY&dnUcBT`NeHT2X9u%S)5E&*1QE z6WfLGP_7r+I}<}M#__rezlzFBUwTXBdj66X{0@P8_G#W{aOUugpTA|=ymK8=+F$&h zJN*qfIPYhRbdY_7o?@WNcT@5EN=yo;RZpzGEp>faUhmy&67rw^yxfsvGY_U*7H<)4R3RXkV(cPSOdN$I_GUy`8l?Hu(A3eR?Q>H+y=icTJYIY?E?)bj`}&N0T{_;aC*s&53gRz4Do6PycQ6-TUS1 z;;PR^n#ZS1dc`T(*W`26NmI0zVcM>#f3ttC7j$aT9|Z>31J`kJ+|D%xCOr@J*SM+$@=-h1+C+gg71`qqRRo8QfUH8j7zc=al< z@W|rE?H|uO`W^8U3fVR>P-DyLZhrmuDoYQQ{@>UpXv6kTOJXcV-%fqQP_Xf$Y|OSZ*RE?X2)aJ= zoJx`2Wd3DVC-a||#9etQaOKAP@?ZXbwMRtP+U-i`d%fqN-gbtvoqsoPo&NXy#kWPj zEq7gBaL75Y_xzl=B`5Z6Is8P@(ugVCqxlR=*$WkVmtR`DUc63r%G7&3uYF5$ITO~r3l@*hJEJcZBLDe;xd!?e=pB<}mOA~j>X|Zc zxzt?un4I6-Kev?jFfDxZrLtn;p&e`z+g|hgzh0jFF!JtQyE)5_1z-LU^oPwq_1p}m zsjK!lJ!?y4G7evv&Crqm#4d>8)g8tko)=E$M~0?8XbXM2J?)2V?{vP$1y?egUzXh5 zYq(`zTyD?W75*D6zn<}W?&9JS{J~mB+1thCi=n}yj*g5)5`iu*CV@Q~N=mad+(ZNg zeMOYJIy%(41YKQRgpYP8DJ|+uauF1~=wdkHm0zi6Jhauuz48C~&kPI<44$rjF6*2U FngFqdq+kF5 literal 0 HcmV?d00001