Redraw SIXEL images before or after text depending on which was placed first in the cell.

This commit is contained in:
Michael Grant
2026-08-09 08:32:25 +01:00
parent 54d3f77c2c
commit b6dbf10057
7 changed files with 118 additions and 75 deletions

View File

@@ -50,6 +50,7 @@ struct sixel_image {
u_int *colours;
u_int ncolours;
u_int used_colours;
u_int p1;
u_int p2;
u_int dx;
@@ -356,7 +357,8 @@ sixel_parse_repeat(struct sixel_image *si, const char *cp, const char *end)
/* Parse SIXEL data into an indexed image. */
struct sixel_image *
sixel_parse(const char *buf, size_t len, u_int p2, u_int xpixel, u_int ypixel)
sixel_parse(const char *buf, size_t len, u_int p1, u_int p2, u_int xpixel,
u_int ypixel)
{
struct sixel_image *si;
const char *cp = buf, *end = buf + len;
@@ -370,6 +372,7 @@ sixel_parse(const char *buf, size_t len, u_int p2, u_int xpixel, u_int ypixel)
si = xcalloc (1, sizeof *si);
si->xpixel = xpixel;
si->ypixel = ypixel;
si->p1 = p1;
si->p2 = p2;
while (cp != end) {
@@ -609,18 +612,13 @@ sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
new = xcalloc (1, sizeof *si);
new->xpixel = xpixel;
new->ypixel = ypixel;
new->p1 = si->p1;
new->p2 = si->p2;
new->set_ra = si->set_ra;
/* subtract offset */
new->ra_x = si->ra_x > pox ? si->ra_x - pox : 0;
new->ra_y = si->ra_y > poy ? si->ra_y - poy : 0;
/* clamp to size */
new->ra_x = new->ra_x < psx ? new->ra_x : psx;
new->ra_y = new->ra_y < psy ? new->ra_y : psy;
/* resize */
new->ra_x = new->ra_x * xpixel / si->xpixel;
new->ra_y = new->ra_y * ypixel / si->ypixel;
/* The raster attributes describe the scaled output rectangle. */
new->ra_x = tsx;
new->ra_y = tsy;
new->used_colours = si->used_colours;
for (y = 0; y < tsy; y++) {
@@ -753,7 +751,7 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
len = 8192;
buf = xmalloc(len);
tmplen = xsnprintf(tmp, sizeof tmp, "\033P9;%uq", si->p2);
tmplen = xsnprintf(tmp, sizeof tmp, "\033P%u;%uq", si->p1, si->p2);
sixel_print_add(&buf, &len, &used, tmp, tmplen);
if (si->set_ra) {
@@ -1133,6 +1131,7 @@ sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells_x,
si = xcalloc(1, sizeof *si);
si->xpixel = xpixel;
si->ypixel = ypixel;
si->p1 = 9;
si->p2 = 1;
si->set_ra = 1;
si->ra_x = sx;
@@ -1417,7 +1416,8 @@ sixel_draw_rect(struct tty *tty, const struct image_rect *rectangle,
tty_cursor(tty, destination_x, destination_y);
tty->flags |= TTY_NOBLOCK;
tty_putn(tty, data, size, 0);
tty_invalidate(tty);
/* SIXEL moves the cursor, but does not change terminal attributes. */
tty->cx = tty->cy = UINT_MAX;
free(data);
}

33
image.c
View File

@@ -570,6 +570,11 @@ image_get_draw_cell(struct tty *tty, const struct grid_cell *gc,
GRID_FLAG_SELECTED);
return (1);
}
if (gc->flags & GRID_FLAG_IMAGE_DAMAGED) {
memcpy(out, gc, sizeof *out);
out->flags &= ~(GRID_FLAG_IMAGE|GRID_FLAG_IMAGE_DAMAGED);
return (0);
}
image_get_fallback_cell(tty, im, gc->image_x, gc->image_y, gc, out,
style_ctx);
return (0);
@@ -787,10 +792,11 @@ image_redraw_scroll(struct screen_write_ctx *ctx, __unused u_int lines)
image_redraw_all(ctx);
}
/* Draw the graphical image marker runs in one visible scene span. */
/* Draw a span's graphical image markers before or after its text. */
void
image_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py,
u_int nx, u_int atx, u_int aty, const struct tty_style_ctx *style_ctx)
u_int nx, u_int atx, u_int aty, int before,
const struct tty_style_ctx *style_ctx)
{
const struct image_backend *backend;
struct image_rect rectangle;
@@ -802,6 +808,8 @@ image_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py,
backend = tty->image_backend;
if (~backend->flags & IMAGE_BACKEND_GRAPHICAL)
return;
if (before && (~backend->flags & IMAGE_BACKEND_TEMPORAL))
return;
for (i = 0; i < nx; i += run) {
grid_view_get_cell(s->grid, px + i, py, &gc);
@@ -809,10 +817,12 @@ image_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py,
run = 1;
continue;
}
if ((backend->flags & IMAGE_BACKEND_TEMPORAL) &&
(gc.flags & GRID_FLAG_IMAGE_DAMAGED)) {
run = 1;
continue;
if (backend->flags & IMAGE_BACKEND_TEMPORAL) {
if ((before && (~gc.flags & GRID_FLAG_IMAGE_DAMAGED)) ||
(!before && (gc.flags & GRID_FLAG_IMAGE_DAMAGED))) {
run = 1;
continue;
}
}
im = image_find(gc.image_id);
if (im == NULL) {
@@ -821,10 +831,13 @@ image_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py,
}
for (run = 1; i + run < nx; run++) {
grid_view_get_cell(s->grid, px + i + run, py, &next);
if (~next.flags & GRID_FLAG_IMAGE ||
((backend->flags & IMAGE_BACKEND_TEMPORAL) &&
(next.flags & GRID_FLAG_IMAGE_DAMAGED)) ||
next.image_id != gc.image_id ||
if (~next.flags & GRID_FLAG_IMAGE)
break;
if ((backend->flags & IMAGE_BACKEND_TEMPORAL) &&
((before && (~next.flags & GRID_FLAG_IMAGE_DAMAGED)) ||
(!before && (next.flags & GRID_FLAG_IMAGE_DAMAGED))))
break;
if (next.image_id != gc.image_id ||
next.image_y != gc.image_y ||
next.image_x != gc.image_x + run)
break;

View File

@@ -2638,7 +2638,7 @@ input_dcs_dispatch(struct input_ctx *ictx)
#ifdef ENABLE_IMAGES
struct window *w;
struct sixel_image *si;
int p2;
int p1, p2;
#endif
if (wp == NULL)
@@ -2656,10 +2656,13 @@ input_dcs_dispatch(struct input_ctx *ictx)
w = wp->window;
if (input_split(ictx) != 0)
return (0);
p1 = input_get(ictx, 0, 0, 0);
if (p1 == -1)
p1 = 0;
p2 = input_get(ictx, 1, 0, 0);
if (p2 == -1)
p2 = 0;
si = sixel_parse(buf, len, p2, w->xpixel, w->ypixel);
si = sixel_parse(buf, len, p1, p2, w->xpixel, w->ypixel);
if (si != NULL)
screen_write_sixelimage(sctx, si, ictx->cell.cell.bg);
}

View File

@@ -386,10 +386,12 @@ recalculate_size(struct window *w, int now)
* got a resize scheduled, then use the new size; otherwise the old.
*/
if (w->flags & WINDOW_RESIZE) {
if (!now && changed && w->new_sx == sx && w->new_sy == sy)
if (!now && changed && w->new_sx == sx && w->new_sy == sy &&
w->new_xpixel == xpixel && w->new_ypixel == ypixel)
changed = 0;
} else {
if (!now && changed && w->sx == sx && w->sy == sy)
if (!now && changed && w->sx == sx && w->sy == sy &&
w->xpixel == xpixel && w->ypixel == ypixel)
changed = 0;
}

View File

@@ -99,6 +99,12 @@ enum redraw_span_type {
#define REDRAW_ALL 0x7fffffff
#define REDRAW_IS_ALL(flags) ((flags) == REDRAW_ALL)
enum redraw_image_phase {
REDRAW_IMAGES_BEFORE,
REDRAW_TEXT,
REDRAW_IMAGES_AFTER
};
/* UTF-8 isolate characters. */
#define REDRAW_START_ISOLATE "\342\201\246"
#define REDRAW_END_ISOLATE "\342\201\251"
@@ -1237,7 +1243,8 @@ redraw_get_scene(struct client *c)
/* Draw a pane span. */
static void
redraw_draw_pane_span(struct redraw_draw_ctx *dctx,
struct redraw_span *span, u_int x, u_int y, u_int n)
struct redraw_span *span, u_int x, u_int y, u_int n,
enum redraw_image_phase phase)
{
struct redraw_scene *scene = dctx->scene;
struct client *c = scene->c;
@@ -1255,10 +1262,14 @@ redraw_draw_pane_span(struct redraw_draw_ctx *dctx,
px = span->data.p.px + (x - span->x);
py = span->data.p.py;
tty_draw_line(tty, s, px, py, n, x, y, &style_ctx);
#ifdef ENABLE_IMAGES
image_draw_line(tty, s, px, py, n, x, y, &style_ctx);
if (phase != REDRAW_TEXT)
image_draw_line(tty, s, px, py, n, x, y,
phase == REDRAW_IMAGES_BEFORE, &style_ctx);
else
#endif
if (phase == REDRAW_TEXT)
tty_draw_line(tty, s, px, py, n, x, y, &style_ctx);
}
/* Get default border style for spans without a pane. */
@@ -1518,7 +1529,7 @@ redraw_draw_menu_span(struct redraw_draw_ctx *dctx,
*/
static void
redraw_draw_span(struct redraw_draw_ctx *dctx, struct redraw_span *span,
u_int y, u_int clip_x, u_int clip_n)
u_int y, u_int clip_x, u_int clip_n, enum redraw_image_phase phase)
{
struct redraw_scene *scene = dctx->scene;
struct redraw_span_data *data = &span->data;
@@ -1539,10 +1550,12 @@ redraw_draw_span(struct redraw_draw_ctx *dctx, struct redraw_span *span,
continue;
x = rr->px;
n = rr->nx;
if (phase != REDRAW_TEXT && type != REDRAW_SPAN_PANE)
continue;
switch (span->data.type) {
case REDRAW_SPAN_PANE:
redraw_draw_pane_span(dctx, span, x, y, n);
redraw_draw_pane_span(dctx, span, x, y, n, phase);
break;
case REDRAW_SPAN_BORDER:
case REDRAW_SPAN_EMPTY:
@@ -1607,27 +1620,29 @@ redraw_draw_pane_lines(struct redraw_draw_ctx *dctx, struct window_pane *wp,
}
#endif
for (y = top; y < bottom; y++) {
line = &scene->lines[y];
if (dctx->flags & REDRAW_STATUS_TOP)
cy = dctx->status_lines + y;
else
cy = y;
if (flags & REDRAW_PANE) {
spans = &line->spans[REDRAW_SPAN_PANE];
TAILQ_FOREACH(span, spans, entry) {
if (span->data.p.wp == wp) {
redraw_draw_span(dctx, span, cy,
span->x, span->width);
for (enum redraw_image_phase phase = REDRAW_IMAGES_BEFORE;
phase <= REDRAW_IMAGES_AFTER; phase++) {
for (y = top; y < bottom; y++) {
line = &scene->lines[y];
if (dctx->flags & REDRAW_STATUS_TOP)
cy = dctx->status_lines + y;
else
cy = y;
if (flags & REDRAW_PANE) {
spans = &line->spans[REDRAW_SPAN_PANE];
TAILQ_FOREACH(span, spans, entry) {
if (span->data.p.wp == wp)
redraw_draw_span(dctx, span, cy, span->x,
span->width, phase);
}
}
}
if (flags & REDRAW_PANE_SCROLLBAR) {
spans = &line->spans[REDRAW_SPAN_SCROLLBAR];
TAILQ_FOREACH(span, spans, entry) {
if (span->data.sb.wp == wp) {
redraw_draw_span(dctx, span, cy,
span->x, span->width);
if (phase == REDRAW_TEXT &&
(flags & REDRAW_PANE_SCROLLBAR)) {
spans = &line->spans[REDRAW_SPAN_SCROLLBAR];
TAILQ_FOREACH(span, spans, entry) {
if (span->data.sb.wp == wp)
redraw_draw_span(dctx, span, cy, span->x,
span->width, phase);
}
}
}
@@ -1644,13 +1659,17 @@ redraw_draw_lines(struct redraw_draw_ctx *dctx, int flags)
struct redraw_span *span;
u_int y, cy, type;
for (y = 0; y < scene->sy; y++) {
line = &scene->lines[y];
if (dctx->flags & REDRAW_STATUS_TOP)
cy = dctx->status_lines + y;
else
cy = y;
for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
for (enum redraw_image_phase phase = REDRAW_IMAGES_BEFORE;
phase <= REDRAW_IMAGES_AFTER; phase++) {
for (y = 0; y < scene->sy; y++) {
line = &scene->lines[y];
if (dctx->flags & REDRAW_STATUS_TOP)
cy = dctx->status_lines + y;
else
cy = y;
for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
if (phase != REDRAW_TEXT && type != REDRAW_SPAN_PANE)
continue;
if (!REDRAW_IS_ALL(flags)) {
switch (type) {
case REDRAW_SPAN_PANE:
@@ -1686,9 +1705,9 @@ redraw_draw_lines(struct redraw_draw_ctx *dctx, int flags)
}
}
spans = &line->spans[type];
TAILQ_FOREACH(span, spans, entry) {
redraw_draw_span(dctx, span, cy, span->x,
span->width);
TAILQ_FOREACH(span, spans, entry)
redraw_draw_span(dctx, span, cy, span->x, span->width,
phase);
}
}
}
@@ -1709,10 +1728,9 @@ redraw_draw_menu_lines(struct redraw_draw_ctx *dctx)
cy = dctx->status_lines + y;
else
cy = y;
TAILQ_FOREACH(span, &line->spans[REDRAW_SPAN_MENU], entry) {
redraw_draw_span(dctx, span, cy, span->x,
span->width);
}
TAILQ_FOREACH(span, &line->spans[REDRAW_SPAN_MENU], entry)
redraw_draw_span(dctx, span, cy, span->x, span->width,
REDRAW_TEXT);
}
}
@@ -2191,13 +2209,17 @@ redraw_draw_damage_rect(struct redraw_draw_ctx *dctx, u_int x, u_int y,
if (sx == 0 || sy == 0)
return;
for (yy = y; yy < y + sy; yy++) {
line = &scene->lines[yy];
if (dctx->flags & REDRAW_STATUS_TOP)
cy = dctx->status_lines + yy;
else
cy = yy;
for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
for (enum redraw_image_phase phase = REDRAW_IMAGES_BEFORE;
phase <= REDRAW_IMAGES_AFTER; phase++) {
for (yy = y; yy < y + sy; yy++) {
line = &scene->lines[yy];
if (dctx->flags & REDRAW_STATUS_TOP)
cy = dctx->status_lines + yy;
else
cy = yy;
for (type = 0; type < REDRAW_SPAN_TYPES; type++) {
if (phase != REDRAW_TEXT && type != REDRAW_SPAN_PANE)
continue;
spans = &line->spans[type];
TAILQ_FOREACH(span, spans, entry) {
clip_x = (span->x > x) ? span->x : x;
@@ -2212,14 +2234,15 @@ redraw_draw_damage_rect(struct redraw_draw_ctx *dctx, u_int x, u_int y,
redraw_damage_grow_span_clip(span, &clip_x,
&clip_end);
redraw_draw_span(dctx, span, cy, clip_x,
clip_end - clip_x);
if (type == REDRAW_SPAN_PANE) {
clip_end - clip_x, phase);
if (phase == REDRAW_TEXT && type == REDRAW_SPAN_PANE) {
redraw_damage_draw_pane_prompt(dctx,
span, cy, clip_x,
clip_end - clip_x);
}
}
}
}
}
}

5
tmux.h
View File

@@ -4279,7 +4279,7 @@ void image_tty_update(struct tty *);
void image_tty_geometry_changed(struct tty *);
void image_tty_free(struct tty *, int);
void image_draw_line(struct tty *, struct screen *, u_int, u_int,
u_int, u_int, u_int, const struct tty_style_ctx *);
u_int, u_int, u_int, int, const struct tty_style_ctx *);
void image_get_fallback_cell(struct tty *, struct image *, u_int,
u_int, const struct grid_cell *, struct grid_cell *,
const struct tty_style_ctx *);
@@ -4312,7 +4312,8 @@ void sixel_draw_rect(struct tty *,
const struct image_rect *, const struct tty_style_ctx *);
void sixel_free_output(struct tty *, int);
void sixel_geometry_changed(struct tty *);
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int, u_int);
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int, u_int,
u_int);
void sixel_free(struct sixel_image *);
void sixel_log(struct sixel_image *);
void sixel_size_in_cells(struct sixel_image *, u_int *, u_int *);

View File

@@ -729,6 +729,7 @@ tty_keys_winsz(struct tty *tty, const char *buf, size_t len, size_t *size)
char_y = (ypixel && tty->sy) ? ypixel / tty->sy : 0;
tty_set_size(tty, tty->sx, tty->sy, char_x, char_y);
tty_invalidate(tty);
recalculate_sizes();
tty->flags &= ~TTY_WINSIZEQUERY;
*size = end + 1;