mirror of
https://github.com/tmux/tmux.git
synced 2026-08-04 06:28:35 +00:00
Refactor to centralize image-specific rendering and lifecycle code in image.c and the image-* backends.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -25,3 +25,4 @@ regress/logs/
|
||||
tags
|
||||
tmux
|
||||
tmux.1.*
|
||||
tools/image-ascii-test
|
||||
|
||||
@@ -5,7 +5,8 @@ CLEANFILES = tmux.1.mdoc tmux.1.man cmd-parse.c
|
||||
# Distribution tarball options.
|
||||
EXTRA_DIST = \
|
||||
CHANGES README COPYING example_tmux.conf \
|
||||
osdep-*.c mdoc2man.awk tmux.1
|
||||
osdep-*.c mdoc2man.awk tmux.1 \
|
||||
tools/generate-image-diacritics.sh tools/image-ascii-test.c
|
||||
dist_EXTRA_tmux_SOURCES = compat/*.[ch]
|
||||
|
||||
# Preprocessor flags.
|
||||
@@ -248,8 +249,7 @@ endif
|
||||
|
||||
# Enable image support.
|
||||
if ENABLE_IMAGES
|
||||
dist_tmux_SOURCES += image.c image-sixel.c image-kitty.c image-ascii.c \
|
||||
image-diacritics.h
|
||||
dist_tmux_SOURCES += image.c image-sixel.c image-kitty.c image-ascii.c
|
||||
endif
|
||||
|
||||
# Add bits for fuzzing if enabled.
|
||||
|
||||
@@ -18,51 +18,23 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
void
|
||||
image_make_ascii(struct image *im)
|
||||
image_get_text_cell(__unused struct tty *tty, struct image *im, u_int x,
|
||||
u_int y, const struct grid_cell *gc, struct grid_cell *out,
|
||||
__unused const struct tty_style_ctx *style_ctx)
|
||||
{
|
||||
static const char ramp[] = " .:-=+*#%@";
|
||||
const u_char *pixel;
|
||||
uint64_t total, count;
|
||||
u_int x, y, px, py, x0, x1, y0, y1, level;
|
||||
static const char ramp[] = " .:-=+*#%@";
|
||||
const struct image_cell *cell;
|
||||
u_int level = 0;
|
||||
|
||||
im->ascii = xcalloc((size_t)im->sx * im->sy, 1);
|
||||
for (y = 0; y < im->sy; y++) {
|
||||
y0 = ((uint64_t)y * im->height) / im->sy;
|
||||
y1 = ((uint64_t)(y + 1) * im->height) / im->sy;
|
||||
if (y1 <= y0)
|
||||
y1 = y0 + 1;
|
||||
for (x = 0; x < im->sx; x++) {
|
||||
x0 = ((uint64_t)x * im->width) / im->sx;
|
||||
x1 = ((uint64_t)(x + 1) * im->width) / im->sx;
|
||||
if (x1 <= x0)
|
||||
x1 = x0 + 1;
|
||||
|
||||
total = count = 0;
|
||||
for (py = y0; py < y1 && py < im->height; py++) {
|
||||
for (px = x0; px < x1 && px < im->width; px++) {
|
||||
pixel = im->pixels + py * im->stride + px * 4;
|
||||
total += ((2126ULL * pixel[0] +
|
||||
7152ULL * pixel[1] +
|
||||
722ULL * pixel[2]) / 10000) *
|
||||
pixel[3] / 255;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count == 0)
|
||||
continue;
|
||||
level = (total / count) * (sizeof ramp - 2) / 255;
|
||||
im->ascii[y * im->sx + x] = ramp[level];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
image_get_ascii(struct image *im, u_int x, u_int y)
|
||||
{
|
||||
if (im == NULL || x >= im->sx || y >= im->sy)
|
||||
return (" ");
|
||||
return (&im->ascii[y * im->sx + x]);
|
||||
memcpy(out, gc, sizeof *out);
|
||||
cell = image_get_cell(im, x, y);
|
||||
if (cell != NULL)
|
||||
level = cell->whole.brightness * (sizeof ramp - 2) / 255;
|
||||
utf8_set(&out->data, ramp[level]);
|
||||
out->flags &= ~GRID_FLAG_IMAGE;
|
||||
}
|
||||
|
||||
227
image-kitty.c
227
image-kitty.c
@@ -18,7 +18,6 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <png.h>
|
||||
#include <resolv.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -26,7 +25,7 @@
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
/* Generated from Kitty's Unicode 6.0 rowcolumn-diacritics.txt. */
|
||||
/* Generated from Unicode 6.0.0 by tools/generate-image-diacritics.sh. */
|
||||
static const uint32_t kitty_diacritics[] = {
|
||||
0x0305, 0x030D, 0x030E, 0x0310, 0x0312, 0x033D, 0x033E, 0x033F,
|
||||
0x0346, 0x034A, 0x034B, 0x034C, 0x0350, 0x0351, 0x0352, 0x0357,
|
||||
@@ -68,8 +67,6 @@ static const uint32_t kitty_diacritics[] = {
|
||||
0x1D244
|
||||
};
|
||||
|
||||
#define KITTY_IMAGE_LIMIT (64 * 1024 * 1024)
|
||||
|
||||
struct kitty_state {
|
||||
char action;
|
||||
char delete;
|
||||
@@ -100,14 +97,32 @@ struct kitty_context {
|
||||
struct kitty_source *sources;
|
||||
};
|
||||
|
||||
struct tty_image_cache {
|
||||
struct kitty_image_cache {
|
||||
u_int server_id;
|
||||
u_int kitty_id;
|
||||
u_int xpixel;
|
||||
u_int ypixel;
|
||||
struct tty_image_cache *next;
|
||||
struct kitty_image_cache *next;
|
||||
};
|
||||
|
||||
struct kitty_output {
|
||||
struct kitty_image_cache *images;
|
||||
u_int next_id;
|
||||
};
|
||||
|
||||
static struct kitty_output *
|
||||
kitty_get_output(struct tty *tty)
|
||||
{
|
||||
struct kitty_output *ko = tty->image_data;
|
||||
|
||||
if (ko == NULL) {
|
||||
ko = xcalloc(1, sizeof *ko);
|
||||
ko->next_id = arc4random_uniform(0xffff00) + 1;
|
||||
tty->image_data = ko;
|
||||
}
|
||||
return (ko);
|
||||
}
|
||||
|
||||
static size_t
|
||||
kitty_utf8(char *out, uint32_t value)
|
||||
{
|
||||
@@ -143,25 +158,38 @@ kitty_delete(struct tty *tty, u_int id)
|
||||
}
|
||||
|
||||
void
|
||||
kitty_images_free(struct tty *tty, int send)
|
||||
kitty_free_output(struct tty *tty, int send)
|
||||
{
|
||||
struct tty_image_cache *cache, *next;
|
||||
struct kitty_output *ko = tty->image_data;
|
||||
struct kitty_image_cache *cache, *next;
|
||||
|
||||
for (cache = tty->images; cache != NULL; cache = next) {
|
||||
if (ko == NULL)
|
||||
return;
|
||||
for (cache = ko->images; cache != NULL; cache = next) {
|
||||
next = cache->next;
|
||||
if (send)
|
||||
kitty_delete(tty, cache->kitty_id);
|
||||
free(cache);
|
||||
}
|
||||
tty->images = NULL;
|
||||
free(ko);
|
||||
tty->image_data = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
kitty_geometry_changed(struct tty *tty)
|
||||
{
|
||||
kitty_free_output(tty, !!(tty->flags & TTY_OPENED));
|
||||
}
|
||||
|
||||
static void
|
||||
kitty_images_collect(struct tty *tty)
|
||||
{
|
||||
struct tty_image_cache **pp, *cache;
|
||||
struct kitty_output *ko = tty->image_data;
|
||||
struct kitty_image_cache **pp, *cache;
|
||||
|
||||
for (pp = &tty->images; (cache = *pp) != NULL; ) {
|
||||
if (ko == NULL)
|
||||
return;
|
||||
for (pp = &ko->images; (cache = *pp) != NULL; ) {
|
||||
if (image_find(cache->server_id) != NULL) {
|
||||
pp = &cache->next;
|
||||
continue;
|
||||
@@ -183,23 +211,12 @@ kitty_place(struct tty *tty, struct image *im, u_int id)
|
||||
rows = im->sy - y;
|
||||
if (rows > nitems(kitty_diacritics))
|
||||
rows = nitems(kitty_diacritics);
|
||||
py = (uint64_t)y * im->height / im->sy;
|
||||
pheight = (uint64_t)(y + rows) * im->height / im->sy - py;
|
||||
if (pheight == 0) {
|
||||
py = im->height - 1;
|
||||
pheight = 1;
|
||||
}
|
||||
for (x = 0; x < im->sx; x += nitems(kitty_diacritics)) {
|
||||
columns = im->sx - x;
|
||||
if (columns > nitems(kitty_diacritics))
|
||||
columns = nitems(kitty_diacritics);
|
||||
px = (uint64_t)x * im->width / im->sx;
|
||||
pwidth = (uint64_t)(x + columns) * im->width /
|
||||
im->sx - px;
|
||||
if (pwidth == 0) {
|
||||
px = im->width - 1;
|
||||
pwidth = 1;
|
||||
}
|
||||
image_get_pixel_rectangle(im, x, y, columns, rows, &px,
|
||||
&py, &pwidth, &pheight);
|
||||
xsnprintf(control, sizeof control,
|
||||
"\033_Ga=p,U=1,i=%u,p=%u,x=%u,y=%u,w=%u,h=%u,"
|
||||
"c=%u,r=%u,q=2\033\\", id, placement++, px, py,
|
||||
@@ -209,16 +226,17 @@ kitty_place(struct tty *tty, struct image *im, u_int id)
|
||||
}
|
||||
}
|
||||
|
||||
static struct tty_image_cache *
|
||||
static struct kitty_image_cache *
|
||||
kitty_upload(struct tty *tty, struct image *im)
|
||||
{
|
||||
struct tty_image_cache *cache;
|
||||
struct kitty_output *ko = kitty_get_output(tty);
|
||||
struct kitty_image_cache *cache;
|
||||
char control[128], encoded[4097];
|
||||
size_t offset, size;
|
||||
int encodedlen;
|
||||
u_int id;
|
||||
|
||||
for (cache = tty->images; cache != NULL; cache = cache->next) {
|
||||
for (cache = ko->images; cache != NULL; cache = cache->next) {
|
||||
if (cache->server_id != im->id)
|
||||
continue;
|
||||
if (cache->xpixel == tty->xpixel &&
|
||||
@@ -230,11 +248,11 @@ kitty_upload(struct tty *tty, struct image *im)
|
||||
}
|
||||
if (cache == NULL) {
|
||||
cache = xcalloc(1, sizeof *cache);
|
||||
cache->next = tty->images;
|
||||
tty->images = cache;
|
||||
cache->next = ko->images;
|
||||
ko->images = cache;
|
||||
}
|
||||
do {
|
||||
id = ++tty->image_next_id & 0xffffff;
|
||||
id = ++ko->next_id & 0xffffff;
|
||||
} while (id == 0);
|
||||
cache->server_id = im->id;
|
||||
cache->kitty_id = id;
|
||||
@@ -278,7 +296,7 @@ kitty_placement(struct image *im, u_int x, u_int y)
|
||||
}
|
||||
|
||||
static void
|
||||
kitty_placeholder(struct tty *tty, struct tty_image_cache *cache,
|
||||
kitty_placeholder(struct tty *tty, struct kitty_image_cache *cache,
|
||||
struct image *im, u_int x, u_int y, u_int count)
|
||||
{
|
||||
char buf[8192], sgr[96];
|
||||
@@ -307,52 +325,38 @@ kitty_placeholder(struct tty *tty, struct tty_image_cache *cache,
|
||||
tty_puts(tty, "\033[39;59m");
|
||||
}
|
||||
|
||||
/*
|
||||
* Draw only the marker cells present in a scene-selected horizontal span.
|
||||
* Overlay and pane clipping have already been applied by screen-redraw.c.
|
||||
*/
|
||||
void
|
||||
kitty_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)
|
||||
kitty_draw_rectangle(struct tty *tty, const struct image_rectangle *rectangle,
|
||||
const struct tty_style_ctx *style_ctx)
|
||||
{
|
||||
struct grid_cell gc, next, draw_gc;
|
||||
struct image *im;
|
||||
struct tty_image_cache *cache;
|
||||
u_int i, run, tile;
|
||||
struct grid_cell draw_gc;
|
||||
struct kitty_image_cache *cache;
|
||||
struct image *im = rectangle->image;
|
||||
u_int x, y, run, available;
|
||||
|
||||
kitty_images_collect(tty);
|
||||
for (i = 0; i < nx; i += run) {
|
||||
grid_view_get_cell(s->grid, px + i, py, &gc);
|
||||
if (~gc.flags & GRID_FLAG_IMAGE) {
|
||||
run = 1;
|
||||
continue;
|
||||
cache = kitty_upload(tty, im);
|
||||
if (cache == NULL)
|
||||
return;
|
||||
memcpy(&draw_gc, &rectangle->cell, sizeof draw_gc);
|
||||
draw_gc.flags &= ~(GRID_FLAG_IMAGE|GRID_FLAG_SELECTED);
|
||||
utf8_set(&draw_gc.data, ' ');
|
||||
for (y = 0; y < rectangle->height; y++) {
|
||||
for (x = 0; x < rectangle->width; x += run) {
|
||||
available = nitems(kitty_diacritics) -
|
||||
(rectangle->source_x + x) %
|
||||
nitems(kitty_diacritics);
|
||||
run = rectangle->width - x;
|
||||
if (run > available)
|
||||
run = available;
|
||||
tty_cursor(tty, rectangle->destination_x + x,
|
||||
rectangle->destination_y + y);
|
||||
tty_attributes(tty, &draw_gc, style_ctx);
|
||||
kitty_placeholder(tty, cache, im,
|
||||
rectangle->source_x + x,
|
||||
rectangle->source_y + y, run);
|
||||
tty_reset(tty);
|
||||
}
|
||||
im = image_find(gc.image_id);
|
||||
if (im == NULL) {
|
||||
run = 1;
|
||||
continue;
|
||||
}
|
||||
tile = gc.image_x / nitems(kitty_diacritics);
|
||||
for (run = 1; i + run < nx; run++) {
|
||||
grid_view_get_cell(s->grid, px + i + run, py, &next);
|
||||
if (~next.flags & GRID_FLAG_IMAGE ||
|
||||
next.image_id != gc.image_id ||
|
||||
next.image_y != gc.image_y ||
|
||||
next.image_x != gc.image_x + run ||
|
||||
next.image_x / nitems(kitty_diacritics) != tile)
|
||||
break;
|
||||
}
|
||||
cache = kitty_upload(tty, im);
|
||||
if (cache == NULL)
|
||||
continue;
|
||||
tty_cursor(tty, atx + i, aty);
|
||||
memcpy(&draw_gc, &gc, sizeof draw_gc);
|
||||
draw_gc.flags &= ~(GRID_FLAG_IMAGE|GRID_FLAG_SELECTED);
|
||||
utf8_set(&draw_gc.data, ' ');
|
||||
tty_attributes(tty, &draw_gc, style_ctx);
|
||||
kitty_placeholder(tty, cache, im, gc.image_x, gc.image_y,
|
||||
run);
|
||||
tty_reset(tty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -535,8 +539,8 @@ kitty_source_get(struct kitty_context *kc, u_int id)
|
||||
static int
|
||||
kitty_append(struct kitty_state *ks, const u_char *buf, size_t len)
|
||||
{
|
||||
if (len > KITTY_IMAGE_LIMIT ||
|
||||
ks->encodedlen > KITTY_IMAGE_LIMIT - len)
|
||||
if (len > IMAGE_SIZE_LIMIT ||
|
||||
ks->encodedlen > IMAGE_SIZE_LIMIT - len)
|
||||
return (-1);
|
||||
ks->encoded = xrealloc(ks->encoded, ks->encodedlen + len + 1);
|
||||
memcpy(ks->encoded + ks->encodedlen, buf, len);
|
||||
@@ -545,54 +549,6 @@ kitty_append(struct kitty_state *ks, const u_char *buf, size_t len)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static u_char *
|
||||
kitty_base64(struct kitty_state *ks, size_t *size)
|
||||
{
|
||||
u_char *out;
|
||||
size_t needed;
|
||||
int result;
|
||||
|
||||
if (ks->encodedlen > (SIZE_MAX - 3) / 3 * 4)
|
||||
return (NULL);
|
||||
needed = (ks->encodedlen + 3) / 4 * 3;
|
||||
out = xmalloc(needed == 0 ? 1 : needed);
|
||||
result = b64_pton(ks->encoded, out, needed);
|
||||
if (result < 0) {
|
||||
free(out);
|
||||
return (NULL);
|
||||
}
|
||||
*size = result;
|
||||
return (out);
|
||||
}
|
||||
|
||||
static u_char *
|
||||
kitty_png(const u_char *data, size_t size, u_int *width, u_int *height)
|
||||
{
|
||||
png_image pi;
|
||||
u_char *pixels;
|
||||
|
||||
memset(&pi, 0, sizeof pi);
|
||||
pi.version = PNG_IMAGE_VERSION;
|
||||
if (!png_image_begin_read_from_memory(&pi, data, size))
|
||||
return (NULL);
|
||||
pi.format = PNG_FORMAT_RGBA;
|
||||
if (pi.width == 0 || pi.height == 0 ||
|
||||
PNG_IMAGE_SIZE(pi) > KITTY_IMAGE_LIMIT) {
|
||||
png_image_free(&pi);
|
||||
return (NULL);
|
||||
}
|
||||
pixels = xmalloc(PNG_IMAGE_SIZE(pi));
|
||||
if (!png_image_finish_read(&pi, NULL, pixels, 0, NULL)) {
|
||||
free(pixels);
|
||||
png_image_free(&pi);
|
||||
return (NULL);
|
||||
}
|
||||
*width = pi.width;
|
||||
*height = pi.height;
|
||||
png_image_free(&pi);
|
||||
return (pixels);
|
||||
}
|
||||
|
||||
static u_char *
|
||||
kitty_raw(struct kitty_state *ks, u_char *data, size_t size)
|
||||
{
|
||||
@@ -603,7 +559,7 @@ kitty_raw(struct kitty_state *ks, u_char *data, size_t size)
|
||||
|
||||
bytes = (ks->format == 24 ? 3 : 4);
|
||||
if (ks->width == 0 || ks->height == 0 ||
|
||||
(uint64_t)ks->width * ks->height * bytes > KITTY_IMAGE_LIMIT)
|
||||
(uint64_t)ks->width * ks->height * bytes > IMAGE_SIZE_LIMIT)
|
||||
return (NULL);
|
||||
expected = (size_t)ks->width * ks->height * bytes;
|
||||
raw = data;
|
||||
@@ -720,13 +676,14 @@ kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
if (ks->action != 'T' && ks->action != 't' && ks->action != 'q')
|
||||
goto fail;
|
||||
|
||||
decoded = kitty_base64(ks, &decodedlen);
|
||||
decoded = image_base64_decode(ks->encoded, ks->encodedlen,
|
||||
IMAGE_SIZE_LIMIT, &decodedlen);
|
||||
if (decoded == NULL)
|
||||
goto fail;
|
||||
if (ks->format == 100) {
|
||||
if (ks->compression == 'z') {
|
||||
if (ks->data_size == 0 ||
|
||||
ks->data_size > KITTY_IMAGE_LIMIT) {
|
||||
ks->data_size > IMAGE_SIZE_LIMIT) {
|
||||
free(decoded);
|
||||
goto fail;
|
||||
}
|
||||
@@ -746,7 +703,8 @@ kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
free(decoded);
|
||||
goto fail;
|
||||
}
|
||||
pixels = kitty_png(decoded, decodedlen, &ks->width, &ks->height);
|
||||
pixels = image_png_decode(decoded, decodedlen, IMAGE_SIZE_LIMIT,
|
||||
&ks->width, &ks->height);
|
||||
free(decoded);
|
||||
} else if (ks->format == 24 || ks->format == 32) {
|
||||
pixels = kitty_raw(ks, decoded, decodedlen);
|
||||
@@ -759,16 +717,11 @@ kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
if (pixels == NULL)
|
||||
goto fail;
|
||||
|
||||
sx = ks->columns;
|
||||
sy = ks->rows;
|
||||
if (xpixel == 0)
|
||||
xpixel = 8;
|
||||
if (ypixel == 0)
|
||||
ypixel = 16;
|
||||
if (sx == 0)
|
||||
sx = (ks->width + xpixel - 1) / xpixel;
|
||||
if (sy == 0)
|
||||
sy = (ks->height + ypixel - 1) / ypixel;
|
||||
image_size_in_cells(ks->width, ks->height, xpixel, ypixel, &sx, &sy);
|
||||
if (ks->columns != 0)
|
||||
sx = ks->columns;
|
||||
if (ks->rows != 0)
|
||||
sy = ks->rows;
|
||||
im = image_create(ks->width, ks->height, sx, sy, pixels);
|
||||
if (im == NULL)
|
||||
free(pixels);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -401,14 +402,7 @@ sixel_size_in_cells(struct sixel_image *si, u_int *x, u_int *y)
|
||||
si->xpixel = 8;
|
||||
if (si->ypixel == 0)
|
||||
si->ypixel = 16;
|
||||
if ((si->x % si->xpixel) == 0)
|
||||
*x = (si->x / si->xpixel);
|
||||
else
|
||||
*x = 1 + (si->x / si->xpixel);
|
||||
if ((si->y % si->ypixel) == 0)
|
||||
*y = (si->y / si->ypixel);
|
||||
else
|
||||
*y = 1 + (si->y / si->ypixel);
|
||||
image_size_in_cells(si->x, si->y, si->xpixel, si->ypixel, x, y);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
@@ -740,20 +734,27 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
|
||||
}
|
||||
|
||||
static struct sixel_image *
|
||||
sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells,
|
||||
u_int xpixel, u_int ypixel)
|
||||
sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells_x,
|
||||
u_int cells_y, u_int xpixel, u_int ypixel)
|
||||
{
|
||||
struct sixel_image *si;
|
||||
const u_char *pixel;
|
||||
u_int x, y, sx, sy, sourcex, sourcey, sourcey0;
|
||||
u_int sourcey1;
|
||||
u_int x, y, sx, sy, sourcex, sourcey;
|
||||
u_int sourcex0, sourcey0, sourcewidth, sourceheight;
|
||||
u_int r, g, b, colour, i;
|
||||
|
||||
sx = cells * xpixel;
|
||||
sy = ypixel;
|
||||
if ((uint64_t)cells_x * xpixel > UINT_MAX ||
|
||||
(uint64_t)cells_y * ypixel > UINT_MAX)
|
||||
return (NULL);
|
||||
sx = cells_x * xpixel;
|
||||
sy = cells_y * ypixel;
|
||||
if (sx == 0 || sy == 0 || sx > SIXEL_WIDTH_LIMIT ||
|
||||
sy > SIXEL_HEIGHT_LIMIT)
|
||||
return (NULL);
|
||||
image_get_pixel_rectangle(im, ox, oy, cells_x, cells_y, &sourcex0,
|
||||
&sourcey0, &sourcewidth, &sourceheight);
|
||||
if (sourcewidth == 0 || sourceheight == 0)
|
||||
return (NULL);
|
||||
|
||||
si = xcalloc(1, sizeof *si);
|
||||
si->xpixel = xpixel;
|
||||
@@ -772,16 +773,11 @@ sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells,
|
||||
}
|
||||
|
||||
for (y = 0; y < sy; y++) {
|
||||
sourcey0 = (uint64_t)oy * im->height / im->sy;
|
||||
sourcey1 = (uint64_t)(oy + 1) * im->height / im->sy;
|
||||
sourcey = sourcey0 +
|
||||
(uint64_t)y * (sourcey1 - sourcey0) / sy;
|
||||
sourcey = sourcey0 + (uint64_t)y * sourceheight / sy;
|
||||
if (sourcey >= im->height)
|
||||
sourcey = im->height - 1;
|
||||
for (x = 0; x < sx; x++) {
|
||||
sourcex = ((uint64_t)ox * im->width / im->sx) +
|
||||
(uint64_t)x * im->width * cells /
|
||||
((uint64_t)im->sx * sx);
|
||||
sourcex = sourcex0 + (uint64_t)x * sourcewidth / sx;
|
||||
if (sourcex >= im->width)
|
||||
sourcex = im->width - 1;
|
||||
pixel = im->pixels + sourcey * im->stride + sourcex * 4;
|
||||
@@ -801,51 +797,29 @@ sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells,
|
||||
}
|
||||
|
||||
void
|
||||
sixel_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py,
|
||||
u_int nx, u_int atx, u_int aty)
|
||||
sixel_draw_rectangle(struct tty *tty, const struct image_rectangle *rectangle,
|
||||
__unused const struct tty_style_ctx *style_ctx)
|
||||
{
|
||||
struct grid_cell gc, next;
|
||||
struct image *im;
|
||||
struct sixel_image *si;
|
||||
char *data;
|
||||
size_t size;
|
||||
u_int i, run;
|
||||
|
||||
for (i = 0; i < nx; i += run) {
|
||||
grid_view_get_cell(s->grid, px + i, py, &gc);
|
||||
if (~gc.flags & GRID_FLAG_IMAGE) {
|
||||
run = 1;
|
||||
continue;
|
||||
}
|
||||
im = image_find(gc.image_id);
|
||||
if (im == NULL) {
|
||||
run = 1;
|
||||
continue;
|
||||
}
|
||||
for (run = 1; i + run < nx; run++) {
|
||||
grid_view_get_cell(s->grid, px + i + run, py, &next);
|
||||
if (~next.flags & GRID_FLAG_IMAGE ||
|
||||
next.image_id != gc.image_id ||
|
||||
next.image_y != gc.image_y ||
|
||||
next.image_x != gc.image_x + run)
|
||||
break;
|
||||
}
|
||||
si = sixel_from_image(im, gc.image_x, gc.image_y, run,
|
||||
tty->xpixel, tty->ypixel);
|
||||
if (si == NULL)
|
||||
continue;
|
||||
data = sixel_print(si, NULL, &size);
|
||||
sixel_free(si);
|
||||
if (data == NULL)
|
||||
continue;
|
||||
tty_region_off(tty);
|
||||
tty_margin_off(tty);
|
||||
tty_cursor(tty, atx + i, aty);
|
||||
tty->flags |= TTY_NOBLOCK;
|
||||
tty_putn(tty, data, size, 0);
|
||||
tty_invalidate(tty);
|
||||
free(data);
|
||||
}
|
||||
si = sixel_from_image(rectangle->image, rectangle->source_x,
|
||||
rectangle->source_y, rectangle->width, rectangle->height,
|
||||
tty->xpixel, tty->ypixel);
|
||||
if (si == NULL)
|
||||
return;
|
||||
data = sixel_print(si, NULL, &size);
|
||||
sixel_free(si);
|
||||
if (data == NULL)
|
||||
return;
|
||||
tty_region_off(tty);
|
||||
tty_margin_off(tty);
|
||||
tty_cursor(tty, rectangle->destination_x, rectangle->destination_y);
|
||||
tty->flags |= TTY_NOBLOCK;
|
||||
tty_putn(tty, data, size, 0);
|
||||
tty_invalidate(tty);
|
||||
free(data);
|
||||
}
|
||||
|
||||
struct screen *
|
||||
|
||||
381
image.c
381
image.c
@@ -19,6 +19,9 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <png.h>
|
||||
#include <resolv.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -27,6 +30,88 @@
|
||||
static struct images images = RB_INITIALIZER(&images);
|
||||
static u_int image_next_id;
|
||||
|
||||
#define IMAGE_BACKEND_GRAPHICAL 0x1
|
||||
#define IMAGE_BACKEND_SCROLLS 0x2
|
||||
|
||||
struct image_backend {
|
||||
const char *name;
|
||||
int flags;
|
||||
void (*draw_rectangle)(struct tty *,
|
||||
const struct image_rectangle *, const struct tty_style_ctx *);
|
||||
void (*free)(struct tty *, int);
|
||||
void (*geometry_changed)(struct tty *);
|
||||
};
|
||||
|
||||
static const struct image_backend image_backend_ascii = {
|
||||
"ascii", IMAGE_BACKEND_SCROLLS, NULL, NULL, NULL
|
||||
};
|
||||
static const struct image_backend image_backend_kitty = {
|
||||
"kitty", IMAGE_BACKEND_GRAPHICAL|IMAGE_BACKEND_SCROLLS,
|
||||
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
|
||||
};
|
||||
|
||||
static const struct image_backend *
|
||||
image_tty_find_backend(struct tty *tty)
|
||||
{
|
||||
if (tty->term != NULL && tty->term->flags & TERM_KITTY)
|
||||
return (&image_backend_kitty);
|
||||
if (tty->term != NULL && tty->term->flags & TERM_SIXEL &&
|
||||
tty->xpixel != 0 && tty->ypixel != 0)
|
||||
return (&image_backend_sixel);
|
||||
return (&image_backend_ascii);
|
||||
}
|
||||
|
||||
void
|
||||
image_tty_update(struct tty *tty)
|
||||
{
|
||||
const struct image_backend *backend;
|
||||
|
||||
backend = image_tty_find_backend(tty);
|
||||
if (tty->image_backend == backend)
|
||||
return;
|
||||
|
||||
if (tty->image_backend != NULL && tty->image_backend->free != NULL)
|
||||
tty->image_backend->free(tty, !!(tty->flags & TTY_OPENED));
|
||||
tty->image_data = NULL;
|
||||
tty->image_backend = backend;
|
||||
log_debug("%s: %s image backend is %s", __func__,
|
||||
tty->client->name, backend->name);
|
||||
}
|
||||
|
||||
int
|
||||
image_tty_is_graphical(struct tty *tty)
|
||||
{
|
||||
image_tty_update(tty);
|
||||
return (!!(tty->image_backend->flags & IMAGE_BACKEND_GRAPHICAL));
|
||||
}
|
||||
|
||||
int
|
||||
image_tty_scrolls(struct tty *tty)
|
||||
{
|
||||
image_tty_update(tty);
|
||||
return (!!(tty->image_backend->flags & IMAGE_BACKEND_SCROLLS));
|
||||
}
|
||||
|
||||
void
|
||||
image_tty_geometry_changed(struct tty *tty)
|
||||
{
|
||||
image_tty_update(tty);
|
||||
if (tty->image_backend->geometry_changed != NULL)
|
||||
tty->image_backend->geometry_changed(tty);
|
||||
}
|
||||
|
||||
void
|
||||
image_tty_free(struct tty *tty, int send)
|
||||
{
|
||||
if (tty->image_backend != NULL && tty->image_backend->free != NULL)
|
||||
tty->image_backend->free(tty, send);
|
||||
tty->image_backend = NULL;
|
||||
tty->image_data = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
image_cmp(struct image *a, struct image *b)
|
||||
{
|
||||
@@ -38,6 +123,79 @@ image_cmp(struct image *a, struct image *b)
|
||||
}
|
||||
RB_GENERATE_STATIC(images, image, entry, image_cmp);
|
||||
|
||||
static void
|
||||
image_sample(struct image *im, uint64_t sample_x, uint64_t sample_y,
|
||||
uint64_t sample_columns, uint64_t sample_rows, struct image_sample *sample)
|
||||
{
|
||||
const u_char *pixel;
|
||||
uint64_t red = 0, green = 0, blue = 0, alpha = 0;
|
||||
uint64_t brightness = 0, count = 0;
|
||||
u_int x, y, x0, x1, y0, y1;
|
||||
|
||||
x0 = sample_x * im->width / sample_columns;
|
||||
x1 = (sample_x + 1) * im->width / sample_columns;
|
||||
y0 = sample_y * im->height / sample_rows;
|
||||
y1 = (sample_y + 1) * im->height / sample_rows;
|
||||
if (x1 <= x0)
|
||||
x1 = x0 + 1;
|
||||
if (y1 <= y0)
|
||||
y1 = y0 + 1;
|
||||
if (x1 > im->width)
|
||||
x1 = im->width;
|
||||
if (y1 > im->height)
|
||||
y1 = im->height;
|
||||
|
||||
for (y = y0; y < y1; y++) {
|
||||
for (x = x0; x < x1; x++) {
|
||||
pixel = im->pixels + y * im->stride + x * 4;
|
||||
red += pixel[0] * pixel[3] / 255;
|
||||
green += pixel[1] * pixel[3] / 255;
|
||||
blue += pixel[2] * pixel[3] / 255;
|
||||
alpha += pixel[3];
|
||||
brightness += ((2126ULL * pixel[0] +
|
||||
7152ULL * pixel[1] + 722ULL * pixel[2]) / 10000) *
|
||||
pixel[3] / 255;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count == 0)
|
||||
return;
|
||||
sample->red = red / count;
|
||||
sample->green = green / count;
|
||||
sample->blue = blue / count;
|
||||
sample->alpha = alpha / count;
|
||||
sample->brightness = brightness / count;
|
||||
}
|
||||
|
||||
static void
|
||||
image_make_cells(struct image *im)
|
||||
{
|
||||
struct image_cell *cell;
|
||||
uint64_t columns, rows;
|
||||
u_int x, y, sample_x, sample_y;
|
||||
|
||||
columns = (uint64_t)im->sx * IMAGE_SAMPLE_COLUMNS;
|
||||
rows = (uint64_t)im->sy * IMAGE_SAMPLE_ROWS;
|
||||
im->cells = xcalloc((size_t)im->sx * im->sy, sizeof *im->cells);
|
||||
for (y = 0; y < im->sy; y++) {
|
||||
for (x = 0; x < im->sx; x++) {
|
||||
cell = &im->cells[(size_t)y * im->sx + x];
|
||||
image_sample(im, x, y, im->sx, im->sy, &cell->whole);
|
||||
for (sample_y = 0; sample_y < IMAGE_SAMPLE_ROWS;
|
||||
sample_y++) {
|
||||
for (sample_x = 0;
|
||||
sample_x < IMAGE_SAMPLE_COLUMNS; sample_x++) {
|
||||
image_sample(im,
|
||||
(uint64_t)x * IMAGE_SAMPLE_COLUMNS + sample_x,
|
||||
(uint64_t)y * IMAGE_SAMPLE_ROWS + sample_y,
|
||||
columns, rows,
|
||||
&cell->samples[sample_y][sample_x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct image *
|
||||
image_find(u_int id)
|
||||
{
|
||||
@@ -56,7 +214,7 @@ image_create(u_int width, u_int height, u_int sx, u_int sy, u_char *pixels)
|
||||
return (NULL);
|
||||
if ((uint64_t)width * height * 4 > SIZE_MAX)
|
||||
return (NULL);
|
||||
if ((uint64_t)sx * sy > SIZE_MAX)
|
||||
if ((uint64_t)sx * sy > SIZE_MAX / sizeof *im->cells)
|
||||
return (NULL);
|
||||
|
||||
im = xcalloc(1, sizeof *im);
|
||||
@@ -74,7 +232,6 @@ image_create(u_int width, u_int height, u_int sx, u_int sy, u_char *pixels)
|
||||
im->stride = (size_t)width * 4;
|
||||
im->size = im->stride * height;
|
||||
im->pixels = pixels;
|
||||
image_make_ascii(im);
|
||||
|
||||
RB_INSERT(images, &images, im);
|
||||
log_debug("%s: image %u is %ux%u pixels, %ux%u cells", __func__,
|
||||
@@ -107,10 +264,20 @@ image_free(u_int id)
|
||||
log_debug("%s: freeing image %u", __func__, id);
|
||||
RB_REMOVE(images, &images, im);
|
||||
free(im->pixels);
|
||||
free(im->ascii);
|
||||
free(im->cells);
|
||||
free(im);
|
||||
}
|
||||
|
||||
const struct image_cell *
|
||||
image_get_cell(struct image *im, u_int x, u_int y)
|
||||
{
|
||||
if (im == NULL || x >= im->sx || y >= im->sy)
|
||||
return (NULL);
|
||||
if (im->cells == NULL)
|
||||
image_make_cells(im);
|
||||
return (&im->cells[(size_t)y * im->sx + x]);
|
||||
}
|
||||
|
||||
void
|
||||
image_set_cell(struct grid_cell *gc, struct image *im, u_int x, u_int y)
|
||||
{
|
||||
@@ -121,9 +288,118 @@ image_set_cell(struct grid_cell *gc, struct image *im, u_int x, u_int y)
|
||||
gc->image_y = y;
|
||||
}
|
||||
|
||||
/* Convert a cell-aligned image rectangle into source pixel coordinates. */
|
||||
void
|
||||
image_clear(struct screen *s, u_int id)
|
||||
image_get_pixel_rectangle(const struct image *im, u_int x, u_int y,
|
||||
u_int width, u_int height, u_int *px, u_int *py, u_int *pwidth,
|
||||
u_int *pheight)
|
||||
{
|
||||
u_int x1, y1;
|
||||
|
||||
*px = *py = *pwidth = *pheight = 0;
|
||||
if (im == NULL || x >= im->sx || y >= im->sy || width == 0 ||
|
||||
height == 0)
|
||||
return;
|
||||
if (width > im->sx - x)
|
||||
width = im->sx - x;
|
||||
if (height > im->sy - y)
|
||||
height = im->sy - y;
|
||||
|
||||
*px = (uint64_t)x * im->width / im->sx;
|
||||
*py = (uint64_t)y * im->height / im->sy;
|
||||
x1 = (uint64_t)(x + width) * im->width / im->sx;
|
||||
y1 = (uint64_t)(y + height) * im->height / im->sy;
|
||||
if (*px >= im->width)
|
||||
*px = im->width - 1;
|
||||
if (*py >= im->height)
|
||||
*py = im->height - 1;
|
||||
if (x1 <= *px)
|
||||
x1 = *px + 1;
|
||||
if (y1 <= *py)
|
||||
y1 = *py + 1;
|
||||
if (x1 > im->width)
|
||||
x1 = im->width;
|
||||
if (y1 > im->height)
|
||||
y1 = im->height;
|
||||
*pwidth = x1 - *px;
|
||||
*pheight = y1 - *py;
|
||||
}
|
||||
|
||||
void
|
||||
image_size_in_cells(u_int width, u_int height, u_int xpixel, u_int ypixel,
|
||||
u_int *sx, u_int *sy)
|
||||
{
|
||||
if (xpixel == 0)
|
||||
xpixel = 8;
|
||||
if (ypixel == 0)
|
||||
ypixel = 16;
|
||||
*sx = ((uint64_t)width + xpixel - 1) / xpixel;
|
||||
*sy = ((uint64_t)height + ypixel - 1) / ypixel;
|
||||
}
|
||||
|
||||
u_char *
|
||||
image_base64_decode(const char *data, size_t len, size_t limit, size_t *size)
|
||||
{
|
||||
char *copy;
|
||||
u_char *out;
|
||||
size_t needed;
|
||||
int result;
|
||||
|
||||
if (len > SIZE_MAX - 3)
|
||||
return (NULL);
|
||||
needed = (len + 3) / 4 * 3;
|
||||
if (needed > limit || needed > INT_MAX)
|
||||
return (NULL);
|
||||
|
||||
copy = xmalloc(len + 1);
|
||||
memcpy(copy, data, len);
|
||||
copy[len] = '\0';
|
||||
out = xmalloc(needed == 0 ? 1 : needed);
|
||||
result = b64_pton(copy, out, needed);
|
||||
free(copy);
|
||||
if (result < 0) {
|
||||
free(out);
|
||||
return (NULL);
|
||||
}
|
||||
*size = result;
|
||||
return (out);
|
||||
}
|
||||
|
||||
u_char *
|
||||
image_png_decode(const u_char *data, size_t size, size_t limit, u_int *width,
|
||||
u_int *height)
|
||||
{
|
||||
png_image pi;
|
||||
u_char *pixels;
|
||||
|
||||
if (size == 0 || size > limit)
|
||||
return (NULL);
|
||||
memset(&pi, 0, sizeof pi);
|
||||
pi.version = PNG_IMAGE_VERSION;
|
||||
if (!png_image_begin_read_from_memory(&pi, data, size))
|
||||
return (NULL);
|
||||
pi.format = PNG_FORMAT_RGBA;
|
||||
if (pi.width == 0 || pi.height == 0 ||
|
||||
(uint64_t)pi.width * pi.height * 4 > limit) {
|
||||
png_image_free(&pi);
|
||||
return (NULL);
|
||||
}
|
||||
pixels = xmalloc(PNG_IMAGE_SIZE(pi));
|
||||
if (!png_image_finish_read(&pi, NULL, pixels, 0, NULL)) {
|
||||
free(pixels);
|
||||
png_image_free(&pi);
|
||||
return (NULL);
|
||||
}
|
||||
*width = pi.width;
|
||||
*height = pi.height;
|
||||
png_image_free(&pi);
|
||||
return (pixels);
|
||||
}
|
||||
|
||||
void
|
||||
image_clear(struct screen_write_ctx *ctx, u_int id)
|
||||
{
|
||||
struct screen *s = ctx->s;
|
||||
struct grid *gd = s->grid;
|
||||
struct grid_cell gc;
|
||||
u_int x, y;
|
||||
@@ -132,24 +408,33 @@ image_clear(struct screen *s, u_int id)
|
||||
for (x = 0; x < gd->sx; x++) {
|
||||
grid_get_cell(gd, x, y, &gc);
|
||||
if ((gc.flags & GRID_FLAG_IMAGE) &&
|
||||
(id == 0 || gc.image_id == id))
|
||||
(id == 0 || gc.image_id == id)) {
|
||||
if (y >= gd->hsize)
|
||||
image_damage_area(ctx, x, y - gd->hsize,
|
||||
1, 1);
|
||||
grid_set_cell(gd, x, y, &grid_default_cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
static int
|
||||
image_check_area(struct screen *s, u_int px, u_int py, u_int nx, u_int ny)
|
||||
{
|
||||
struct grid_cell gc;
|
||||
u_int x, y, ex, ey;
|
||||
u_int sx = screen_size_x(s), sy = screen_size_y(s);
|
||||
|
||||
ex = px + nx;
|
||||
if (ex > screen_size_x(s))
|
||||
ex = screen_size_x(s);
|
||||
ey = py + ny;
|
||||
if (ey > screen_size_y(s))
|
||||
ey = screen_size_y(s);
|
||||
if (px >= sx || py >= sy || nx == 0 || ny == 0)
|
||||
return (0);
|
||||
if (nx > sx - px)
|
||||
ex = sx;
|
||||
else
|
||||
ex = px + nx;
|
||||
if (ny > sy - py)
|
||||
ey = sy;
|
||||
else
|
||||
ey = py + ny;
|
||||
for (y = py; y < ey; y++) {
|
||||
for (x = px; x < ex; x++) {
|
||||
grid_view_get_cell(s->grid, x, y, &gc);
|
||||
@@ -160,23 +445,72 @@ image_check_area(struct screen *s, u_int px, u_int py, u_int nx, u_int ny)
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
image_check_line(struct screen *s, u_int py, u_int ny)
|
||||
void
|
||||
image_damage_area(struct screen_write_ctx *ctx, u_int px, u_int py, u_int nx,
|
||||
u_int ny)
|
||||
{
|
||||
return (image_check_area(s, 0, py, screen_size_x(s), ny));
|
||||
if (ctx->wp != NULL && image_check_area(ctx->s, px, py, nx, ny))
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
}
|
||||
|
||||
int
|
||||
image_free_all(struct screen *s)
|
||||
void
|
||||
image_damage_all(struct screen_write_ctx *ctx)
|
||||
{
|
||||
return (image_check_area(s, 0, 0, screen_size_x(s),
|
||||
screen_size_y(s)));
|
||||
image_damage_area(ctx, 0, 0, screen_size_x(ctx->s),
|
||||
screen_size_y(ctx->s));
|
||||
}
|
||||
|
||||
int
|
||||
image_scroll_up(struct screen *s, __unused u_int lines)
|
||||
void
|
||||
image_damage_scroll(struct screen_write_ctx *ctx, __unused u_int lines)
|
||||
{
|
||||
return (image_free_all(s));
|
||||
image_damage_all(ctx);
|
||||
}
|
||||
|
||||
/* Draw the graphical image marker runs in one visible scene span. */
|
||||
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)
|
||||
{
|
||||
const struct image_backend *backend;
|
||||
struct image_rectangle rectangle;
|
||||
struct grid_cell gc, next;
|
||||
struct image *im;
|
||||
u_int i, run;
|
||||
|
||||
image_tty_update(tty);
|
||||
backend = tty->image_backend;
|
||||
if (~backend->flags & IMAGE_BACKEND_GRAPHICAL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < nx; i += run) {
|
||||
grid_view_get_cell(s->grid, px + i, py, &gc);
|
||||
if (~gc.flags & GRID_FLAG_IMAGE) {
|
||||
run = 1;
|
||||
continue;
|
||||
}
|
||||
im = image_find(gc.image_id);
|
||||
if (im == NULL) {
|
||||
run = 1;
|
||||
continue;
|
||||
}
|
||||
for (run = 1; i + run < nx; run++) {
|
||||
grid_view_get_cell(s->grid, px + i + run, py, &next);
|
||||
if (~next.flags & GRID_FLAG_IMAGE ||
|
||||
next.image_id != gc.image_id ||
|
||||
next.image_y != gc.image_y ||
|
||||
next.image_x != gc.image_x + run)
|
||||
break;
|
||||
}
|
||||
rectangle.image = im;
|
||||
memcpy(&rectangle.cell, &gc, sizeof rectangle.cell);
|
||||
rectangle.source_x = gc.image_x;
|
||||
rectangle.source_y = gc.image_y;
|
||||
rectangle.width = run;
|
||||
rectangle.height = 1;
|
||||
rectangle.destination_x = atx + i;
|
||||
rectangle.destination_y = aty;
|
||||
backend->draw_rectangle(tty, &rectangle, style_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -218,7 +552,6 @@ image_write(struct screen_write_ctx *ctx, struct image *im, u_int bg)
|
||||
grid_view_set_cell(gd, cx + x, cy + y, &gc);
|
||||
}
|
||||
}
|
||||
if (ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
image_damage_area(ctx, cx, cy, sx, sy);
|
||||
screen_write_cursormove(ctx, 0, cy + sy, 0);
|
||||
}
|
||||
|
||||
4
input.c
4
input.c
@@ -2833,7 +2833,7 @@ input_exit_apc(struct input_ctx *ictx)
|
||||
}
|
||||
if (im != NULL) {
|
||||
if (action == 'd')
|
||||
image_clear(sctx->s, im->id);
|
||||
image_clear(sctx, im->id);
|
||||
else
|
||||
image_write(sctx, im, ictx->cell.cell.bg);
|
||||
image_free(im->id);
|
||||
@@ -2841,7 +2841,7 @@ input_exit_apc(struct input_ctx *ictx)
|
||||
input_reply(ictx, 0, "\033_Gi=%u;OK\033\\",
|
||||
image_id);
|
||||
} else if (action == 'd' && image_id == 0) {
|
||||
image_clear(sctx->s, 0);
|
||||
image_clear(sctx, 0);
|
||||
} else if ((action == 't' || action == 'q') && quiet == 0)
|
||||
input_reply(ictx, 0, "\033_Gi=%u;OK\033\\", image_id);
|
||||
return;
|
||||
|
||||
@@ -54,13 +54,20 @@ $TMUX new-window -d "
|
||||
sleep 1
|
||||
[ "$($TMUX display-message -pt:2 '#{cursor_y}')" = 1 ] || exit 1
|
||||
|
||||
# PNG Kitty input uses the shared image decoder and canonical cell sizing.
|
||||
$TMUX new-window -d "
|
||||
printf '\033_Ga=T,q=2,f=100;iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAAGUExURf8AAP///0EdNBEAAAABYktHRAH/Ai3eAAAAB3RJTUUH6ggCDAECH324BwAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=\033\\'
|
||||
sleep 10"
|
||||
sleep 1
|
||||
[ "$($TMUX display-message -pt:3 '#{cursor_y}')" = 1 ] || exit 1
|
||||
|
||||
# SIXEL input reaches the same grid marker and copy-mode paths.
|
||||
$TMUX new-window -d "cat '$FIXTURE'; sleep 10"
|
||||
sleep 1
|
||||
[ "$($TMUX display-message -pt:3 '#{cursor_y}')" -gt 0 ] || exit 1
|
||||
$TMUX copy-mode -t:3 || exit 1
|
||||
$TMUX send-keys -t:3 -X history-top || exit 1
|
||||
$TMUX capture-pane -pt:3 >$TMP || exit 1
|
||||
[ "$($TMUX display-message -pt:4 '#{cursor_y}')" -gt 0 ] || exit 1
|
||||
$TMUX copy-mode -t:4 || exit 1
|
||||
$TMUX send-keys -t:4 -X history-top || exit 1
|
||||
$TMUX capture-pane -pt:4 >$TMP || exit 1
|
||||
|
||||
# A client without an image feature gets the brightness-based ASCII backend.
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
@@ -1126,11 +1126,7 @@ redraw_draw_pane_span(struct redraw_draw_ctx *dctx,
|
||||
py = span->data.p.py;
|
||||
tty_draw_line(tty, s, px, py, n, x, y, &style_ctx);
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (tty->term->flags & TERM_KITTY)
|
||||
kitty_draw_line(tty, s, px, py, n, x, y, &style_ctx);
|
||||
else if ((tty->term->flags & TERM_SIXEL) &&
|
||||
tty->xpixel != 0 && tty->ypixel != 0)
|
||||
sixel_draw_line(tty, s, px, py, n, x, y);
|
||||
image_draw_line(tty, s, px, py, n, x, y, &style_ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -1308,9 +1308,8 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
|
||||
memcpy(&gc, &grid_default_cell, sizeof gc);
|
||||
utf8_set(&gc.data, 'E');
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_free_all(s) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_damage_all(ctx);
|
||||
#endif
|
||||
|
||||
for (yy = 0; yy < screen_size_y(s); yy++) {
|
||||
@@ -1355,9 +1354,8 @@ screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
|
||||
if (s->cx > screen_size_x(s) - 1)
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_damage_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
||||
#endif
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 0, 1);
|
||||
@@ -1396,9 +1394,8 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
|
||||
if (s->cx > screen_size_x(s) - 1)
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_damage_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
||||
#endif
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 0, 1);
|
||||
@@ -1437,9 +1434,8 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
|
||||
if (s->cx > screen_size_x(s) - 1)
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_damage_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
||||
#endif
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 0, 1);
|
||||
@@ -1472,9 +1468,8 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
|
||||
if (ny == 0)
|
||||
ny = 1;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_damage_area(ctx, 0, s->cy, screen_size_x(s), sy - s->cy);
|
||||
#endif
|
||||
|
||||
if (s->cy < s->rupper || s->cy > s->rlower) {
|
||||
@@ -1540,9 +1535,8 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
|
||||
if (ny == 0)
|
||||
ny = 1;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_damage_area(ctx, 0, s->cy, screen_size_x(s), sy - s->cy);
|
||||
#endif
|
||||
|
||||
if (s->cy < s->rupper || s->cy > s->rlower) {
|
||||
@@ -1613,9 +1607,8 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
|
||||
if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_damage_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
||||
#endif
|
||||
|
||||
flags = gl->flags & GRID_LINE_OSC133_FLAGS;
|
||||
@@ -1652,9 +1645,8 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
|
||||
if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_damage_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
||||
#endif
|
||||
|
||||
grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
|
||||
@@ -1679,9 +1671,8 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_damage_area(ctx, 0, s->cy, screen_size_x(s), 1);
|
||||
#endif
|
||||
|
||||
if (s->cx > sx - 1)
|
||||
@@ -1733,9 +1724,8 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_free_all(s) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_damage_all(ctx);
|
||||
#endif
|
||||
|
||||
grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
|
||||
@@ -1785,9 +1775,6 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
|
||||
struct screen *s = ctx->s;
|
||||
struct grid *gd = s->grid;
|
||||
struct grid_line *gl;
|
||||
#ifdef ENABLE_SIXEL
|
||||
int redraw = 0;
|
||||
#endif
|
||||
u_int rupper = s->rupper, rlower = s->rlower;
|
||||
|
||||
gl = grid_get_line(gd, gd->hsize + s->cy);
|
||||
@@ -1808,13 +1795,12 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (rlower == screen_size_y(s) - 1)
|
||||
redraw = image_scroll_up(s, 1);
|
||||
image_damage_scroll(ctx, 1);
|
||||
else
|
||||
redraw = image_check_line(s, rupper, rlower - rupper);
|
||||
if (redraw && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
image_damage_area(ctx, 0, rupper, screen_size_x(s),
|
||||
rlower - rupper);
|
||||
#endif
|
||||
|
||||
grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
|
||||
@@ -1840,9 +1826,8 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
|
||||
ctx->bg = bg;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_scroll_up(s, lines) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_damage_scroll(ctx, lines);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < lines; i++) {
|
||||
@@ -1869,9 +1854,8 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
|
||||
else if (lines > s->rlower - s->rupper + 1)
|
||||
lines = s->rlower - s->rupper + 1;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_free_all(s) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_damage_all(ctx);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < lines; i++)
|
||||
@@ -1910,9 +1894,8 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
|
||||
struct visible_ranges *r;
|
||||
struct visible_range *ri;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_damage_area(ctx, 0, s->cy, screen_size_x(s), sy - s->cy);
|
||||
#endif
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 1, 1);
|
||||
@@ -1991,9 +1974,8 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
|
||||
struct visible_ranges *r;
|
||||
struct visible_range *ri;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_line(s, 0, s->cy - 1) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_damage_area(ctx, 0, 0, screen_size_x(s), s->cy + 1);
|
||||
#endif
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 1, 1);
|
||||
@@ -2064,9 +2046,8 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
|
||||
struct visible_ranges *r;
|
||||
struct visible_range *ri;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_free_all(s) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_damage_all(ctx);
|
||||
#endif
|
||||
|
||||
screen_write_initctx(ctx, &ttyctx, 1, 1);
|
||||
@@ -2531,9 +2512,8 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (image_check_area(s, s->cx, s->cy, ci->used, 1) && ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_damage_area(ctx, s->cx, s->cy, ci->used, 1);
|
||||
#endif
|
||||
|
||||
grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
|
||||
|
||||
5
screen.c
5
screen.c
@@ -126,11 +126,6 @@ screen_reinit(struct screen *s, int check)
|
||||
|
||||
screen_clear_selection(s);
|
||||
screen_free_titles(s);
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
image_free_all(s);
|
||||
#endif
|
||||
|
||||
screen_set_progress_bar(s, PROGRESS_BAR_HIDDEN, 0);
|
||||
screen_reset_hyperlinks(s);
|
||||
}
|
||||
|
||||
81
tmux.h
81
tmux.h
@@ -77,7 +77,8 @@ struct session;
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct image;
|
||||
struct tty_image_cache;
|
||||
struct image_backend;
|
||||
struct image_rectangle;
|
||||
#endif
|
||||
#ifdef ENABLE_SIXEL
|
||||
struct sixel_image;
|
||||
@@ -1037,6 +1038,23 @@ struct style {
|
||||
};
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* A protocol-neutral average of part of an image cell. RGB is premultiplied. */
|
||||
struct image_sample {
|
||||
u_char red;
|
||||
u_char green;
|
||||
u_char blue;
|
||||
u_char alpha;
|
||||
u_char brightness;
|
||||
};
|
||||
|
||||
/* Half blocks, quadrants and sextants all divide evenly into a 2 by 6 grid. */
|
||||
#define IMAGE_SAMPLE_COLUMNS 2
|
||||
#define IMAGE_SAMPLE_ROWS 6
|
||||
struct image_cell {
|
||||
struct image_sample whole;
|
||||
struct image_sample samples[IMAGE_SAMPLE_ROWS][IMAGE_SAMPLE_COLUMNS];
|
||||
};
|
||||
|
||||
/* Immutable protocol-neutral image placement. */
|
||||
struct image {
|
||||
u_int id;
|
||||
@@ -1048,11 +1066,25 @@ struct image {
|
||||
size_t stride;
|
||||
size_t size;
|
||||
u_char *pixels;
|
||||
char *ascii;
|
||||
struct image_cell *cells; /* lazily generated text samples */
|
||||
|
||||
RB_ENTRY(image) entry;
|
||||
};
|
||||
RB_HEAD(images, image);
|
||||
|
||||
/* A cell-aligned part of an image to draw at a terminal position. */
|
||||
struct image_rectangle {
|
||||
struct image *image;
|
||||
struct grid_cell cell;
|
||||
u_int source_x;
|
||||
u_int source_y;
|
||||
u_int width;
|
||||
u_int height;
|
||||
u_int destination_x;
|
||||
u_int destination_y;
|
||||
};
|
||||
|
||||
#define IMAGE_SIZE_LIMIT (64 * 1024 * 1024)
|
||||
#endif
|
||||
|
||||
/* Cursor style. */
|
||||
@@ -1843,8 +1875,8 @@ struct tty {
|
||||
struct event key_timer;
|
||||
struct tty_key *key_tree;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct tty_image_cache *images;
|
||||
u_int image_next_id;
|
||||
const struct image_backend *image_backend;
|
||||
void *image_data;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -4227,16 +4259,32 @@ struct image *image_create(u_int, u_int, u_int, u_int, u_char *);
|
||||
struct image *image_find(u_int);
|
||||
void image_ref(u_int);
|
||||
void image_free(u_int);
|
||||
int image_free_all(struct screen *);
|
||||
int image_check_line(struct screen *, u_int, u_int);
|
||||
int image_check_area(struct screen *, u_int, u_int, u_int, u_int);
|
||||
int image_scroll_up(struct screen *, u_int);
|
||||
void image_set_cell(struct grid_cell *, struct image *, u_int,
|
||||
u_int);
|
||||
void image_write(struct screen_write_ctx *, struct image *, u_int);
|
||||
void image_make_ascii(struct image *);
|
||||
const char *image_get_ascii(struct image *, u_int, u_int);
|
||||
void image_clear(struct screen *, u_int);
|
||||
void image_get_pixel_rectangle(const struct image *, u_int, u_int,
|
||||
u_int, u_int, u_int *, u_int *, u_int *, u_int *);
|
||||
void image_size_in_cells(u_int, u_int, u_int, u_int, u_int *,
|
||||
u_int *);
|
||||
u_char *image_base64_decode(const char *, size_t, size_t, size_t *);
|
||||
u_char *image_png_decode(const u_char *, size_t, size_t, u_int *,
|
||||
u_int *);
|
||||
void image_damage_area(struct screen_write_ctx *, u_int, u_int,
|
||||
u_int, u_int);
|
||||
void image_damage_all(struct screen_write_ctx *);
|
||||
void image_damage_scroll(struct screen_write_ctx *, u_int);
|
||||
int image_tty_is_graphical(struct tty *);
|
||||
int image_tty_scrolls(struct tty *);
|
||||
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 *);
|
||||
const struct image_cell *image_get_cell(struct image *, u_int, u_int);
|
||||
void image_get_text_cell(struct tty *, struct image *, u_int,
|
||||
u_int, const struct grid_cell *, struct grid_cell *,
|
||||
const struct tty_style_ctx *);
|
||||
void image_clear(struct screen_write_ctx *, u_int);
|
||||
#define KITTY_PARSE_ERROR -1
|
||||
#define KITTY_PARSE_OK 0
|
||||
#define KITTY_PARSE_MORE 1
|
||||
@@ -4244,11 +4292,12 @@ void image_clear(struct screen *, u_int);
|
||||
struct image *kitty_parse_image(void **, const u_char *, size_t, u_int,
|
||||
u_int, u_int *, u_int *, char *, int *);
|
||||
void kitty_free_state(void *);
|
||||
void kitty_draw_line(struct tty *, struct screen *, u_int, u_int,
|
||||
u_int, u_int, u_int, const struct tty_style_ctx *);
|
||||
void kitty_images_free(struct tty *, int);
|
||||
void sixel_draw_line(struct tty *, struct screen *, u_int, u_int,
|
||||
u_int, u_int, u_int);
|
||||
void kitty_draw_rectangle(struct tty *,
|
||||
const struct image_rectangle *, const struct tty_style_ctx *);
|
||||
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 *);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
|
||||
23
tty-draw.c
23
tty-draw.c
@@ -128,7 +128,6 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct grid_cell image_gc;
|
||||
struct image *im;
|
||||
const char *ascii;
|
||||
#endif
|
||||
struct grid_line *gl;
|
||||
u_int i, j, last_i, cx, ex, width;
|
||||
@@ -265,19 +264,19 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* Text terminals render image blocks as ASCII. */
|
||||
if (gc.flags & GRID_FLAG_IMAGE) {
|
||||
memcpy(&image_gc, &gc, sizeof image_gc);
|
||||
im = image_find(gc.image_id);
|
||||
if ((tty->term->flags & TERM_KITTY) ||
|
||||
((tty->term->flags & TERM_SIXEL) &&
|
||||
tty->xpixel != 0 &&
|
||||
tty->ypixel != 0)) {
|
||||
ascii = " ";
|
||||
if (image_tty_is_graphical(tty)) {
|
||||
memcpy(&image_gc, &gc,
|
||||
sizeof image_gc);
|
||||
utf8_set(&image_gc.data, ' ');
|
||||
image_gc.flags &=
|
||||
~GRID_FLAG_IMAGE;
|
||||
skip = 1;
|
||||
} else
|
||||
ascii = image_get_ascii(im,
|
||||
gc.image_x, gc.image_y);
|
||||
utf8_set(&image_gc.data, *ascii);
|
||||
image_gc.flags &= ~GRID_FLAG_IMAGE;
|
||||
} else {
|
||||
image_get_text_cell(tty, im,
|
||||
gc.image_x, gc.image_y, &gc,
|
||||
&image_gc, style_ctx);
|
||||
}
|
||||
if (skip)
|
||||
image_gc.flags &=
|
||||
~GRID_FLAG_SELECTED;
|
||||
|
||||
30
tty.c
30
tty.c
@@ -109,9 +109,6 @@ tty_init(struct tty *tty, struct client *c)
|
||||
tty->ccolour = -1;
|
||||
tty->fg = tty->bg = -1;
|
||||
tty->mouse_last_pane = -1;
|
||||
#ifdef ENABLE_IMAGES
|
||||
tty->image_next_id = arc4random_uniform(0xffff00) + 1;
|
||||
#endif
|
||||
|
||||
if (tcgetattr(c->fd, &tty->tio) != 0)
|
||||
return (-1);
|
||||
@@ -161,10 +158,19 @@ tty_resize(struct tty *tty)
|
||||
void
|
||||
tty_set_size(struct tty *tty, u_int sx, u_int sy, u_int xpixel, u_int ypixel)
|
||||
{
|
||||
#ifdef ENABLE_IMAGES
|
||||
int geometry_changed;
|
||||
|
||||
geometry_changed = (tty->xpixel != xpixel || tty->ypixel != ypixel);
|
||||
#endif
|
||||
tty->sx = sx;
|
||||
tty->sy = sy;
|
||||
tty->xpixel = xpixel;
|
||||
tty->ypixel = ypixel;
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (geometry_changed)
|
||||
image_tty_geometry_changed(tty);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -510,7 +516,7 @@ tty_close(struct tty *tty)
|
||||
|
||||
if (tty->flags & TTY_OPENED) {
|
||||
#ifdef ENABLE_IMAGES
|
||||
kitty_images_free(tty, 1);
|
||||
image_tty_free(tty, 1);
|
||||
#endif
|
||||
evbuffer_free(tty->in);
|
||||
event_del(&tty->event_in);
|
||||
@@ -529,7 +535,7 @@ tty_free(struct tty *tty)
|
||||
{
|
||||
tty_close(tty);
|
||||
#ifdef ENABLE_IMAGES
|
||||
kitty_images_free(tty, 0);
|
||||
image_tty_free(tty, 0);
|
||||
#endif
|
||||
|
||||
free(tty->r.ranges);
|
||||
@@ -542,6 +548,9 @@ tty_update_features(struct tty *tty)
|
||||
|
||||
if (tty_apply_features(tty->term, c->term_features))
|
||||
tty_term_apply_overrides(tty->term);
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_tty_update(tty);
|
||||
#endif
|
||||
|
||||
if (tty_use_margin(tty))
|
||||
tty_putcode(tty, TTYC_ENMG);
|
||||
@@ -2122,7 +2131,6 @@ tty_cell(struct tty *tty, const struct grid_cell *gc,
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct grid_cell image_gc;
|
||||
struct image *im;
|
||||
const char *ascii;
|
||||
#endif
|
||||
|
||||
/* Skip last character if terminal is stupid. */
|
||||
@@ -2146,15 +2154,11 @@ tty_cell(struct tty *tty, const struct grid_cell *gc,
|
||||
* selection; the unchanged image would then need to be drawn again.
|
||||
*/
|
||||
if (gc->flags & GRID_FLAG_IMAGE) {
|
||||
if ((tty->term->flags & TERM_KITTY) ||
|
||||
((tty->term->flags & TERM_SIXEL) &&
|
||||
tty->xpixel != 0 && tty->ypixel != 0))
|
||||
if (image_tty_is_graphical(tty))
|
||||
return;
|
||||
memcpy(&image_gc, gc, sizeof image_gc);
|
||||
im = image_find(gc->image_id);
|
||||
ascii = image_get_ascii(im, gc->image_x, gc->image_y);
|
||||
utf8_set(&image_gc.data, *ascii);
|
||||
image_gc.flags &= ~GRID_FLAG_IMAGE;
|
||||
image_get_text_cell(tty, im, gc->image_x, gc->image_y, gc,
|
||||
&image_gc, style_ctx);
|
||||
gc = &image_gc;
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user