mirror of
https://github.com/tmux/tmux.git
synced 2026-08-29 10:01:33 +00:00
Keep SIXEL palettes across redraw spans
This commit is contained in:
184
image-sixel.c
184
image-sixel.c
@@ -72,6 +72,22 @@ struct sixel_chunk {
|
||||
char *data;
|
||||
};
|
||||
|
||||
struct sixel_image_cache {
|
||||
u_int server_id;
|
||||
u_int xpixel;
|
||||
u_int ypixel;
|
||||
size_t size;
|
||||
uint64_t age;
|
||||
struct sixel_image *si;
|
||||
struct sixel_image_cache *next;
|
||||
};
|
||||
|
||||
struct sixel_output {
|
||||
struct sixel_image_cache *images;
|
||||
size_t size;
|
||||
uint64_t age;
|
||||
};
|
||||
|
||||
static int
|
||||
sixel_parse_expand_lines(struct sixel_image *si, u_int y)
|
||||
{
|
||||
@@ -499,6 +515,8 @@ sixel_to_image(struct sixel_image *si)
|
||||
sx, sy, pixels);
|
||||
if (im == NULL)
|
||||
free(pixels);
|
||||
else
|
||||
im->sixel = si;
|
||||
return (im);
|
||||
}
|
||||
#endif
|
||||
@@ -1124,21 +1142,175 @@ fail:
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static struct sixel_output *
|
||||
sixel_get_output(struct tty *tty)
|
||||
{
|
||||
struct sixel_output *so = tty->image_data;
|
||||
|
||||
if (so == NULL) {
|
||||
so = xcalloc(1, sizeof *so);
|
||||
tty->image_data = so;
|
||||
}
|
||||
return (so);
|
||||
}
|
||||
|
||||
static size_t
|
||||
sixel_image_size(struct sixel_image *si)
|
||||
{
|
||||
uint64_t size;
|
||||
|
||||
if ((uint64_t)si->x * si->y > SIZE_MAX / sizeof(uint16_t))
|
||||
return (0);
|
||||
size = (uint64_t)si->x * si->y * sizeof(uint16_t);
|
||||
if ((uint64_t)si->ncolours * sizeof *si->colours > SIZE_MAX - size)
|
||||
return (0);
|
||||
size += (uint64_t)si->ncolours * sizeof *si->colours;
|
||||
if (size > SIZE_MAX)
|
||||
return (0);
|
||||
return (size);
|
||||
}
|
||||
|
||||
static void
|
||||
sixel_remove_cache(struct sixel_output *so, struct sixel_image_cache **pp)
|
||||
{
|
||||
struct sixel_image_cache *cache = *pp;
|
||||
|
||||
*pp = cache->next;
|
||||
so->size -= cache->size;
|
||||
sixel_free(cache->si);
|
||||
free(cache);
|
||||
}
|
||||
|
||||
static void
|
||||
sixel_collect_images(struct sixel_output *so)
|
||||
{
|
||||
struct sixel_image_cache **pp, *cache;
|
||||
|
||||
for (pp = &so->images; (cache = *pp) != NULL; ) {
|
||||
if (image_find(cache->server_id) == NULL)
|
||||
sixel_remove_cache(so, pp);
|
||||
else
|
||||
pp = &cache->next;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sixel_free_output(struct tty *tty, __unused int send)
|
||||
{
|
||||
struct sixel_output *so = tty->image_data;
|
||||
struct sixel_image_cache *cache, *next;
|
||||
|
||||
if (so == NULL)
|
||||
return;
|
||||
for (cache = so->images; cache != NULL; cache = next) {
|
||||
next = cache->next;
|
||||
sixel_free(cache->si);
|
||||
free(cache);
|
||||
}
|
||||
free(so);
|
||||
tty->image_data = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
sixel_geometry_changed(struct tty *tty)
|
||||
{
|
||||
sixel_free_output(tty, !!(tty->flags & TTY_OPENED));
|
||||
}
|
||||
|
||||
static struct sixel_image *
|
||||
sixel_render_image(struct image *im, u_int xpixel, u_int ypixel)
|
||||
{
|
||||
/* Preserve SIXEL's original palette and indexed pixels when possible. */
|
||||
if (im->sixel != NULL)
|
||||
return (sixel_scale(im->sixel, xpixel, ypixel, 0, 0,
|
||||
im->sx, im->sy, 1));
|
||||
return (sixel_from_image(im, 0, 0, im->sx, im->sy, xpixel, ypixel));
|
||||
}
|
||||
|
||||
static struct sixel_image *
|
||||
sixel_get_image(struct tty *tty, struct image *im)
|
||||
{
|
||||
struct sixel_output *so = sixel_get_output(tty);
|
||||
struct sixel_image_cache **pp, *cache, **oldest;
|
||||
struct sixel_image *si;
|
||||
size_t size;
|
||||
|
||||
sixel_collect_images(so);
|
||||
for (cache = so->images; cache != NULL; cache = cache->next) {
|
||||
if (cache->server_id != im->id || cache->xpixel != tty->xpixel ||
|
||||
cache->ypixel != tty->ypixel)
|
||||
continue;
|
||||
cache->age = ++so->age;
|
||||
return (cache->si);
|
||||
}
|
||||
|
||||
si = sixel_render_image(im, tty->xpixel, tty->ypixel);
|
||||
if (si == NULL)
|
||||
return (NULL);
|
||||
size = sixel_image_size(si);
|
||||
if (size == 0 || size > IMAGE_SIZE_LIMIT) {
|
||||
/* The renderer still has a usable image, but it is not cacheable. */
|
||||
return (si);
|
||||
}
|
||||
while (so->size > IMAGE_SIZE_LIMIT - size) {
|
||||
oldest = NULL;
|
||||
for (pp = &so->images; (cache = *pp) != NULL;
|
||||
pp = &cache->next) {
|
||||
if (oldest == NULL || cache->age < (*oldest)->age)
|
||||
oldest = pp;
|
||||
}
|
||||
if (oldest == NULL)
|
||||
break;
|
||||
sixel_remove_cache(so, oldest);
|
||||
}
|
||||
cache = xcalloc(1, sizeof *cache);
|
||||
cache->server_id = im->id;
|
||||
cache->xpixel = tty->xpixel;
|
||||
cache->ypixel = tty->ypixel;
|
||||
cache->size = size;
|
||||
cache->age = ++so->age;
|
||||
cache->si = si;
|
||||
cache->next = so->images;
|
||||
so->images = cache;
|
||||
so->size += size;
|
||||
return (si);
|
||||
}
|
||||
|
||||
static int
|
||||
sixel_image_is_cached(struct tty *tty, struct sixel_image *si)
|
||||
{
|
||||
struct sixel_output *so = tty->image_data;
|
||||
struct sixel_image_cache *cache;
|
||||
|
||||
if (so == NULL)
|
||||
return (0);
|
||||
for (cache = so->images; cache != NULL; cache = cache->next) {
|
||||
if (cache->si == si)
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
sixel_draw_rectangle(struct tty *tty, const struct image_rectangle *rectangle,
|
||||
__unused const struct tty_style_ctx *style_ctx)
|
||||
{
|
||||
struct sixel_image *si;
|
||||
struct sixel_image *si, *crop;
|
||||
char *data;
|
||||
size_t size;
|
||||
|
||||
si = sixel_from_image(rectangle->image, rectangle->source_x,
|
||||
rectangle->source_y, rectangle->width, rectangle->height,
|
||||
tty->xpixel, tty->ypixel);
|
||||
si = sixel_get_image(tty, rectangle->image);
|
||||
if (si == NULL)
|
||||
return;
|
||||
data = sixel_print(si, NULL, &size);
|
||||
sixel_free(si);
|
||||
crop = sixel_scale(si, tty->xpixel, tty->ypixel,
|
||||
rectangle->source_x, rectangle->source_y, rectangle->width,
|
||||
rectangle->height, 1);
|
||||
if (!sixel_image_is_cached(tty, si))
|
||||
sixel_free(si);
|
||||
if (crop == NULL)
|
||||
return;
|
||||
data = sixel_print(crop, NULL, &size);
|
||||
sixel_free(crop);
|
||||
if (data == NULL)
|
||||
return;
|
||||
tty_region_off(tty);
|
||||
|
||||
5
image.c
5
image.c
@@ -50,7 +50,8 @@ static const struct image_backend image_backend_kitty = {
|
||||
kitty_draw_rectangle, kitty_free_output, kitty_geometry_changed
|
||||
};
|
||||
static const struct image_backend image_backend_sixel = {
|
||||
"sixel", IMAGE_BACKEND_GRAPHICAL, sixel_draw_rectangle, NULL, NULL
|
||||
"sixel", IMAGE_BACKEND_GRAPHICAL, sixel_draw_rectangle,
|
||||
sixel_free_output, sixel_geometry_changed
|
||||
};
|
||||
|
||||
static const struct image_backend *
|
||||
@@ -311,6 +312,8 @@ image_free(u_int id)
|
||||
free(im->pixels);
|
||||
else
|
||||
image_free(im->parent_id);
|
||||
if (im->sixel != NULL)
|
||||
sixel_free(im->sixel);
|
||||
free(im->cells);
|
||||
free(im);
|
||||
}
|
||||
|
||||
@@ -3029,9 +3029,10 @@ screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
|
||||
struct image *im;
|
||||
|
||||
im = sixel_to_image(si);
|
||||
sixel_free(si);
|
||||
if (im == NULL)
|
||||
if (im == NULL) {
|
||||
sixel_free(si);
|
||||
return;
|
||||
}
|
||||
image_write(ctx, im, bg);
|
||||
image_free(im->id);
|
||||
}
|
||||
|
||||
4
tmux.h
4
tmux.h
@@ -1070,6 +1070,8 @@ struct image {
|
||||
size_t stride;
|
||||
size_t size;
|
||||
u_char *pixels;
|
||||
/* Original indexed SIXEL data, if this image arrived as SIXEL. */
|
||||
struct sixel_image *sixel;
|
||||
struct image_cell *cells; /* lazily generated text samples */
|
||||
|
||||
RB_ENTRY(image) entry;
|
||||
@@ -4305,6 +4307,8 @@ void kitty_free_output(struct tty *, int);
|
||||
void kitty_geometry_changed(struct tty *);
|
||||
void sixel_draw_rectangle(struct tty *,
|
||||
const struct image_rectangle *, const struct tty_style_ctx *);
|
||||
void sixel_free_output(struct tty *, int);
|
||||
void sixel_geometry_changed(struct tty *);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
|
||||
Reference in New Issue
Block a user