From fbbecde2364457a1187501c23f5e1a2143616f7d Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Tue, 13 Oct 2026 22:43:16 +0100 Subject: [PATCH] SIXEL image placement. --- image-grid.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++- image-sixel.c | 9 ++-- image.c | 39 ++++++++++++++++++ tmux-image.h | 3 ++ 4 files changed, 157 insertions(+), 6 deletions(-) diff --git a/image-grid.c b/image-grid.c index 0873e9170..bf8e8bb38 100644 --- a/image-grid.c +++ b/image-grid.c @@ -196,7 +196,7 @@ image_grid_add(struct grid *gd, struct image *im, u_int x, u_int y, } } -/* Remove the covered part of each span, retaining either side. */ +/* Remove the covered part of a span. */ static void image_line_remove(struct image_line *line, u_int x, u_int w) { @@ -459,3 +459,113 @@ image_damage_screen(struct screen_write_ctx *ctx, u_int x, u_int y, u_int sx, if (ctx->flags & SCREEN_WRITE_INPUT) image_grid_remove(s->grid, x, s->grid->hsize + y, sx, sy); } + +/* Return whether an image cell has any visible pixels. */ +static int +image_cell_has_visible_pixels(struct image *im, u_int x, u_int y) +{ + u_int px, py, sx, sy, xx, yy; + const u_char *pixels; + size_t stride; + + image_get_pixel_area(im, x, y, 1, 1, &px, &py, &sx, &sy); + if (sx == 0 || sy == 0) + return (0); + pixels = image_get_pixels(im, &stride, NULL); + for (yy = py; yy < py + sy; yy++) { + for (xx = px; xx < px + sx; xx++) { + if (pixels[yy * stride + xx * 4 + 3] != 0) + return (1); + } + } + return (0); +} + +/* Attach the visible runs from one image row. */ +static void +image_add_row(struct image_line *line, struct image_placement *plmt, + u_int cx, u_int src_y, u_int sx) +{ + struct image *im = plmt->image; + u_int x, run; + + for (x = 0; x < sx; x += run) { + if (!image_cell_has_visible_pixels(im, x, src_y)) { + run = 1; + continue; + } + for (run = 1; x + run < sx; run++) { + if (!image_cell_has_visible_pixels(im, x + run, src_y)) + break; + } + image_span_add(line, plmt, cx + x, run, x, src_y); + } +} + +/* Place a SIXEL image at the cursor. */ +void +image_write(struct screen_write_ctx *ctx, struct image *im, u_int bg) +{ + struct screen *s = ctx->s; + struct grid *gd = s->grid; + struct image_placement *plmt; + struct grid_line *gl; + struct image_line *line; + u_int cx = s->cx, cy = s->cy, y, sx, sy, lines; + u_int origin_y = 0, h_origin_y, region_height; + u_int remaining, n; + + image_get_size_in_cells(im, &sx, &sy); + if (sx > screen_size_x(s) - cx) + sx = screen_size_x(s) - cx; + if (sx == 0) + return; + + /* Scroll images that move the cursor. */ + if (screen_size_y(s) - cy <= sy) { + lines = sy - (screen_size_y(s) - cy) + 1; + region_height = s->rlower - s->rupper + 1; + + remaining = lines; + while (remaining > 0) { + if (remaining < region_height) + n = remaining; + else + n = region_height; + screen_write_scrollup(ctx, n, bg); + remaining -= n; + } + + if (lines > cy) { + origin_y = lines - cy; + screen_write_cursormove(ctx, -1, 0, 0); + } else { + origin_y = 0; + screen_write_cursormove(ctx, -1, cy - lines, 0); + } + cy = s->cy; + sy -= origin_y; + } + plmt = image_placement_create(gd, im); + + /* Attach scrolled image rows to the available history lines. */ + h_origin_y = origin_y; + if (h_origin_y > gd->hsize) + h_origin_y = gd->hsize; + for (y = 0; y < h_origin_y; y++) { + gl = &gd->linedata[gd->hsize - h_origin_y + y]; + line = image_line_get(gl); + image_add_row(line, plmt, cx, origin_y - h_origin_y + y, sx); + } + + /* Attach visible image rows. */ + for (y = 0; y < sy; y++) { + line = image_line_get(&gd->linedata[gd->hsize + cy + y]); + image_add_row(line, plmt, cx, origin_y + y, sx); + } + + /* Schedule a redraw and advance the cursor past the image. */ + image_store_prune(gd->images); + image_redraw_area(ctx, cx, cy, sx, sy); + screen_write_cursormove(ctx, 0, cy + sy, 0); +} diff --git a/image-sixel.c b/image-sixel.c index 65de8ab20..c7fc56b3d 100644 --- a/image-sixel.c +++ b/image-sixel.c @@ -515,10 +515,10 @@ sixel_to_image(struct sixel_image *si) (uint64_t)sy * si->ypixel > UINT_MAX) return (NULL); - pixels = xcalloc((size_t)si->x * si->y, 4); + pixels = xcalloc(si->x * si->y, 4); for (y = 0; y < si->y; y++) { for (x = 0; x < si->x; x++) { - pixel = pixels + ((size_t)y * si->x + x) * 4; + pixel = pixels + (y * si->x + x) * 4; c = sixel_get_pixel(si, x, y); if (c == 0) { @@ -545,8 +545,7 @@ sixel_to_image(struct sixel_image *si) /* Write a SIXEL image. */ void -sixel_write(__unused struct screen_write_ctx *ctx, struct sixel_image *si, - __unused u_int bg) +sixel_write(struct screen_write_ctx *ctx, struct sixel_image *si, u_int bg) { struct image *im; @@ -554,6 +553,6 @@ sixel_write(__unused struct screen_write_ctx *ctx, struct sixel_image *si, sixel_free(si); if (im == NULL) return; - /* XXX image_write_sixel */ + image_write(ctx, im, bg); image_free(im); } diff --git a/image.c b/image.c index 47de935b9..5f7036612 100644 --- a/image.c +++ b/image.c @@ -302,3 +302,42 @@ image_size_in_cells(u_int w, u_int h, u_int xpixel, u_int ypixel, u_int *sx, *sx = w / xpixel + (w % xpixel != 0); *sy = h / ypixel + (h % ypixel != 0); } + +/* Find area covered by a rectangle of cells in pixels. */ +void +image_get_pixel_area(struct image *im, u_int x, u_int y, u_int w, u_int h, + u_int *px, u_int *py, u_int *pw, u_int *ph) +{ + u_int left, top, right, bottom; + + *px = *py = *pw = *ph = 0; + + if (im == NULL || x >= im->sx || y >= im->sy || w == 0 || h == 0) + return; + if (w > im->sx - x) + w = im->sx - x; + if (h > im->sy - y) + h = im->sy - y; + + left = (uint64_t)x * im->canvas_width / im->sx; + top = (uint64_t)y * im->canvas_height / im->sy; + right = ((uint64_t)(x + w) * im->canvas_width + im->sx - 1) / im->sx; + bottom = ((uint64_t)(y + h) * im->canvas_height + im->sy - 1) / im->sy; + + if (left >= im->width || top >= im->height) + *px = *py = 0; + else { + if (right <= left) + right = left + 1; + if (bottom <= top) + bottom = top + 1; + if (right > im->width) + right = im->width; + if (bottom > im->height) + bottom = im->height; + *px = left; + *py = top; + *pw = right - left; + *ph = bottom - top; + } +} diff --git a/tmux-image.h b/tmux-image.h index 5211bf3f6..2ac55c649 100644 --- a/tmux-image.h +++ b/tmux-image.h @@ -72,6 +72,8 @@ void image_get_size_in_cells(struct image *, u_int *, u_int *); const u_char *image_get_pixels(struct image *, size_t *, size_t *); void image_size_in_cells(u_int, u_int, u_int, u_int, u_int *, u_int *); +void image_get_pixel_area(struct image *, u_int, u_int, u_int, + u_int, u_int *, u_int *, u_int *, u_int *); /* image-grid.c */ void image_grid_add(struct grid *, struct image *, u_int, u_int, @@ -92,6 +94,7 @@ void image_redraw_all(struct screen_write_ctx *); void image_redraw_scroll(struct screen_write_ctx *); void image_damage_screen(struct screen_write_ctx *, u_int, u_int, u_int, u_int); +void image_write(struct screen_write_ctx *, struct image *, u_int); /* image-sixel.c */ #define SIXEL_COLOUR_REGISTERS 1024