diff --git a/Makefile.am b/Makefile.am index 5a561819e..d79708ab6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -210,6 +210,7 @@ dist_tmux_SOURCES = \ style.c \ tmux-protocol.h \ tmux.c \ + tmux-image.h \ tmux.h \ tty-acs.c \ tty-draw.c \ @@ -251,7 +252,7 @@ endif # Enable image support. if ENABLE_IMAGES -dist_tmux_SOURCES += image.c image-sixel.c +dist_tmux_SOURCES += image.c image-grid.c image-sixel.c endif # Add bits for fuzzing if enabled. diff --git a/grid.c b/grid.c index 830929c0e..d6f67d0b7 100644 --- a/grid.c +++ b/grid.c @@ -308,6 +308,9 @@ grid_free_line(struct grid *gd, u_int py) { struct grid_line *gl = &gd->linedata[py]; +#ifdef ENABLE_IMAGES + image_grid_free_line(gd, gl); +#endif free(gl->celldata); free(gl->extddata); memset(gl, 0, sizeof *gl); @@ -347,6 +350,9 @@ void grid_destroy(struct grid *gd) { grid_free_lines(gd, 0, gd->hsize + gd->sy); +#ifdef ENABLE_IMAGES + image_grid_free(gd); +#endif free(gd->linedata); free(gd); } @@ -692,6 +698,9 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg) return; if (grid_check_y(gd, __func__, py + ny - 1) != 0) return; +#ifdef ENABLE_IMAGES + image_grid_remove(gd, px, py, nx, ny); +#endif for (yy = py; yy < py + ny; yy++) { gl = &gd->linedata[yy]; @@ -794,6 +803,9 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx, grid_expand_line(gd, py, px + nx, 8); grid_expand_line(gd, py, dx + nx, 8); +#ifdef ENABLE_IMAGES + image_grid_move_cells(gd, dx, px, py, nx); +#endif memmove(&gl->celldata[dx], &gl->celldata[px], nx * sizeof *gl->celldata); if (dx + nx > gl->cellused) @@ -1233,6 +1245,9 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, { struct grid_line *dstl, *srcl; u_int yy; +#ifdef ENABLE_IMAGES + u_int ody = dy, osy = sy; +#endif if (dy + ny > dst->hsize + dst->sy) ny = dst->hsize + dst->sy - dy; @@ -1245,6 +1260,9 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, dstl = &dst->linedata[dy]; memcpy(dstl, srcl, sizeof *dstl); +#ifdef ENABLE_IMAGES + dstl->images = NULL; +#endif if (srcl->cellsize != 0) { dstl->celldata = xreallocarray(NULL, srcl->cellsize, sizeof *dstl->celldata); @@ -1264,6 +1282,9 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, sy++; dy++; } +#ifdef ENABLE_IMAGES + image_grid_duplicate_lines(dst, ody, src, osy, ny); +#endif } /* Mark line as dead. */ @@ -1384,6 +1405,11 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy, break; line = yy + 1 + lines; +#ifdef ENABLE_IMAGES + if (image_grid_line_has_images(&gd->linedata[line])) + break; +#endif + /* Consume empty lines, including any markers. */ if (~gd->linedata[line].flags & GRID_LINE_WRAPPED) wrapped = 0; @@ -1453,8 +1479,7 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy, /* Remove the lines that were completely consumed. */ for (i = yy + 1; i < yy + 1 + lines; i++) { - free(gd->linedata[i].celldata); - free(gd->linedata[i].extddata); + grid_free_line(gd, i); grid_reflow_dead(&gd->linedata[i]); } @@ -1562,6 +1587,13 @@ grid_reflow(struct grid *gd, u_int sx) if (gl->flags & GRID_LINE_DEAD) continue; +#ifdef ENABLE_IMAGES + if (image_grid_line_has_images(gl)) { + grid_reflow_move(target, gl); + continue; + } +#endif + /* * Work out the width of this line. at is the point at which * the available width is hit, and width is the full line diff --git a/image-grid.c b/image-grid.c new file mode 100644 index 000000000..3c87505d2 --- /dev/null +++ b/image-grid.c @@ -0,0 +1,382 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2026 Michael Grant + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include + +#include "tmux.h" + +/* A run of cells on one grid line. */ +struct image_span { + u_int x; + u_int sx; + u_int source_x; + u_int source_y; + + struct image_line *line; + struct image_placement *placement; + + TAILQ_ENTRY(image_span) line_entry; + TAILQ_ENTRY(image_span) placement_entry; +}; +TAILQ_HEAD(image_spans, image_span); + +/* Spans on one grid line. */ +struct image_line { + struct image_spans spans; +}; + +/* A placement of an image, may be across multiple lines */ +struct image_placement { + struct image_store *store; + struct image *image; + struct image_spans spans; + + TAILQ_ENTRY(image_placement) entry; +}; +TAILQ_HEAD(image_placements, image_placement); + +/* Placements owned by one grid. */ +struct image_store { + struct image_placements placements; +}; + +/* A shared placements copied when duplicating lines. */ +struct image_copy_map { + struct image_placement *source; + struct image_placement *destination; +}; + +/* Part of a span waiting to be moved. */ +struct image_move { + struct image_placement *placement; + u_int x; + u_int sx; + u_int source_x; + u_int source_y; +}; + +/* Get the placement store for a grid. */ +static struct image_store * +image_store_get(struct grid *gd) +{ + if (gd->images == NULL) { + gd->images = xcalloc(1, sizeof *gd->images); + TAILQ_INIT(&gd->images->placements); + } + return (gd->images); +} + +/* Get the spans for a grid line. */ +static struct image_line * +image_line_get(struct grid_line *gl) +{ + if (gl->images == NULL) { + gl->images = xcalloc(1, sizeof *gl->images); + TAILQ_INIT(&gl->images->spans); + } + return (gl->images); +} + +/* Create a placement. Takes a reference to the image. */ +static struct image_placement * +image_placement_create(struct grid *gd, struct image *im) +{ + struct image_store *store = image_store_get(gd); + struct image_placement *plmt; + + plmt = xcalloc(1, sizeof *plmt); + plmt->store = store; + plmt->image = im; + TAILQ_INIT(&plmt->spans); + TAILQ_INSERT_TAIL(&store->placements, plmt, entry); + image_ref(im); + return (plmt); +} + +/* Free a placement after all its spans have gone. */ +static void +image_placement_free(struct image_placement *plmt) +{ + TAILQ_REMOVE(&plmt->store->placements, plmt, entry); + image_free(plmt->image); + free(plmt); +} + +/* Attach a span to both its grid line and its placement. */ +static void +image_span_add(struct image_line *line, struct image_placement *plmt, u_int x, + u_int sx, u_int src_x, u_int src_y) +{ + struct image_span *span; + + if (sx == 0) + return; + + span = xcalloc(1, sizeof *span); + span->x = x; + span->sx = sx; + span->source_x = src_x; + span->source_y = src_y; + span->line = line; + span->placement = plmt; + TAILQ_INSERT_TAIL(&line->spans, span, line_entry); + TAILQ_INSERT_TAIL(&plmt->spans, span, placement_entry); +} + +/* Detach and free one span. */ +static void +image_span_free(struct image_span *span) +{ + TAILQ_REMOVE(&span->line->spans, span, line_entry); + TAILQ_REMOVE(&span->placement->spans, span, placement_entry); + free(span); +} + +/* Free placements with no remaining spans. */ +static void +image_store_prune(struct image_store *store) +{ + struct image_placement *plmt, *next; + + if (store == NULL) + return; + TAILQ_FOREACH_SAFE(plmt, &store->placements, entry, next) { + if (TAILQ_EMPTY(&plmt->spans)) + image_placement_free(plmt); + } +} + +/* Add a source rectangle at absolute grid coordinates. */ +void +image_grid_add(struct grid *gd, struct image *im, u_int x, u_int y, + u_int src_x, u_int src_y, u_int sx, u_int sy) +{ + struct image_placement *plmt; + struct image_line *line; + u_int rows = gd->hsize + gd->sy, row; + u_int image_sx, image_sy; + + if (sx == 0 || sy == 0 || x >= gd->sx || y >= rows) + return; + image_get_size_in_cells(im, &image_sx, &image_sy); + if (src_x >= image_sx || src_y >= image_sy) + return; + + if (sx > gd->sx - x) + sx = gd->sx - x; + if (sx > image_sx - src_x) + sx = image_sx - src_x; + if (sy > rows - y) + sy = rows - y; + if (sy > image_sy - src_y) + sy = image_sy - src_y; + + plmt = image_placement_create(gd, im); + for (row = 0; row < sy; row++) { + line = image_line_get(&gd->linedata[y + row]); + image_span_add(line, plmt, x, sx, src_x, src_y + row); + } +} + +/* Remove the covered part of each span, retaining either side. */ +static void +image_line_remove(struct image_line *line, u_int x, u_int w) +{ + struct image_span *span, *next; + u_int end, span_end, right; + + if (line == NULL || w == 0) + return; + end = x + w; + if (end < x) + end = UINT_MAX; + TAILQ_FOREACH_SAFE(span, &line->spans, line_entry, next) { + span_end = span->x + span->sx; + if (span_end <= x || span->x >= end) + continue; + if (span->x < x && span_end > end) { + right = span_end - end; + span->sx = x - span->x; + image_span_add(line, span->placement, end, right, + span->source_x + end - span->x, span->source_y); + continue; + } + if (span->x < x) { + span->sx = x - span->x; + continue; + } + if (span_end > end) { + span->source_x += end - span->x; + span->sx = span_end - end; + span->x = end; + continue; + } + image_span_free(span); + } +} + +/* Remove a rectangle of image spans from a grid. */ +void +image_grid_remove(struct grid *gd, u_int x, u_int y, u_int sx, u_int sy) +{ + u_int rows = gd->hsize + gd->sy; + u_int row; + + if (gd->images == NULL || sx == 0 || sy == 0 || y >= rows) + return; + if (sy > rows - y) + sy = rows - y; + for (row = y; row < y + sy; row++) + image_line_remove(gd->linedata[row].images, x, sx); + image_store_prune(gd->images); +} + +/* Free all spans on a grid line. */ +void +image_grid_free_line(struct grid *gd, struct grid_line *gl) +{ + struct image_line *line = gl->images; + struct image_span *span, *next; + + if (line == NULL) + return; + + TAILQ_FOREACH_SAFE(span, &line->spans, line_entry, next) + image_span_free(span); + free(line); + + gl->images = NULL; + image_store_prune(gd->images); +} + +/* Free a grid's empty placement store. */ +void +image_grid_free(struct grid *gd) +{ + if (gd->images != NULL) { + image_store_prune(gd->images); + free(gd->images); + gd->images = NULL; + } +} + +/* Move the image spans belonging to a range of text cells. */ +void +image_grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) +{ + struct image_line *line; + struct image_span *span; + struct image_move *moves = NULL; + size_t n = 0, i; + u_int start, end, span_end; + + if (gd->images == NULL || + nx == 0 || + px == dx || + py >= gd->hsize + gd->sy) + return; + line = gd->linedata[py].images; + if (line == NULL) + return; + + end = px + nx; + TAILQ_FOREACH(span, &line->spans, line_entry) { + span_end = span->x + span->sx; + start = (span->x > px ? span->x : px); + if (start >= end || span_end <= px) + continue; + if (span_end > end) + span_end = end; + + moves = xreallocarray(moves, n + 1, sizeof *moves); + moves[n].placement = span->placement; + moves[n].x = dx + start - px; + moves[n].sx = span_end - start; + moves[n].source_x = span->source_x + start - span->x; + moves[n].source_y = span->source_y; + n++; + } + image_line_remove(line, px, nx); + image_line_remove(line, dx, nx); + for (i = 0; i < n; i++) { + image_span_add(line, moves[i].placement, moves[i].x, + moves[i].sx, moves[i].source_x, moves[i].source_y); + } + free(moves); + image_store_prune(gd->images); +} + +/* Map one source placement to one destination placement during a copy. */ +static struct image_placement * +image_copy_placement(struct grid *dst, struct image_placement *src, + struct image_copy_map **maps, u_int *nmaps) +{ + struct image_placement *plmt; + struct image_copy_map *map = *maps; + u_int i, n = *nmaps; + + for (i = 0; i < n; i++) { + if (map[i].source == src) + return (map[i].destination); + } + plmt = image_placement_create(dst, src->image); + + map = xreallocarray(map, n + 1, sizeof *map); + map[n].source = src; + map[n].destination = plmt; + + *maps = map; + *nmaps = n + 1; + return (plmt); +} + +/* Duplicate image spans and their placements between grid lines. */ +void +image_grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, + u_int sy, u_int ny) +{ + struct image_copy_map *maps = NULL; + struct image_line *src_line, *dst_line; + struct image_span *span; + struct image_placement *plmt; + u_int nmaps = 0, row; + + for (row = 0; row < ny; row++) { + src_line = src->linedata[sy + row].images; + if (src_line == NULL) + continue; + dst_line = image_line_get(&dst->linedata[dy + row]); + TAILQ_FOREACH(span, &src_line->spans, line_entry) { + plmt = image_copy_placement(dst, span->placement, &maps, + &nmaps); + image_span_add(dst_line, plmt, span->x, span->sx, + span->source_x, span->source_y); + } + } + free(maps); +} + +/* Check whether a grid line has any image spans. */ +int +image_grid_line_has_images(struct grid_line *gl) +{ + return (gl->images != NULL && !TAILQ_EMPTY(&gl->images->spans)); +} diff --git a/image.c b/image.c index 5613cf920..29875c71a 100644 --- a/image.c +++ b/image.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2026 Michael Grant * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,210 +19,289 @@ #include +#include #include #include #include "tmux.h" -static struct images all_images = TAILQ_HEAD_INITIALIZER(all_images); -static u_int all_images_count; -#define MAX_IMAGE_COUNT 20 +/* Maximum image size in bytes. */ +#define IMAGE_SIZE_LIMIT (64 * 1024 * 1024) -static void printflike(3, 4) -image_log(struct image *im, const char* from, const char* fmt, ...) +/* An image. */ +struct image { + u_int id; + u_int references; + u_int flags; +#define IMAGE_FLAG_OWN_PIXELS 0x1 + + struct image *parent; + u_int source_id; + u_int order_id; + + /* Size in cells. */ + u_int sx; + u_int sy; + + /* Size in pixels. */ + u_int width; + u_int height; + + /* Canvas size in pixels. */ + u_int canvas_width; + u_int canvas_height; + + /* + * RGBA pixels use four bytes each. Stride is the number of bytes + * between the start of each row. Size is the number of bytes + * containing all of this image's pixels - it may include pixels that + * are part of the parent image but not part of this image. + */ + size_t stride; + size_t size; + u_char *pixels; + + RB_ENTRY(image) entry; +}; +RB_HEAD(images, image); +static struct images images = RB_INITIALIZER(&images); +static u_int image_next_id; + +/* Compare images by IDs. */ +static int +image_cmp(struct image *a, struct image *b) { - va_list ap; - char s[128]; + if (a->id < b->id) + return (-1); + if (a->id > b->id) + return (1); + return (0); +} +RB_GENERATE_STATIC(images, image, entry, image_cmp); - if (log_get_level() == 0) - return; +/* Find an image by its ID. */ +struct image * +image_find(u_int id) +{ + struct image find; - if (fmt == NULL) { - log_debug("%s: %p (%ux%u %u,%u)", from, im, im->sx, im->sy, - im->px, im->py); - return; - } - - va_start(ap, fmt); - vsnprintf(s, sizeof s, fmt, ap); - va_end(ap); - - log_debug("%s: %p (%ux%u %u,%u): %s", from, im, im->sx, im->sy, - im->px, im->py, s); + find.id = id; + return (RB_FIND(images, &images, &find)); } -static void -image_free(struct image *im) +/* Return the ID of an image. */ +u_int +image_get_id(struct image *im) { - image_log(im, __func__, NULL); - - TAILQ_REMOVE(&all_images, im, all_entry); - all_images_count--; - - TAILQ_REMOVE(im->list, im, entry); - sixel_free(im->data); - free(im->fallback); - free(im); + return (im->id); } -int -image_free_all(struct screen *s) -{ - struct image *im, *im1; - int redraw = !TAILQ_EMPTY(&s->images); - - if (redraw) - log_debug ("%s", __func__); - TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) - image_free(im); - return (redraw); -} - -/* Create text placeholder for an image. */ -static void -image_fallback(char **ret, u_int sx, u_int sy) -{ - char *buf, *label; - u_int py, size, lsize; - - if (sy == 0) { - *ret = xstrdup(""); - return; - } - - /* Allocate first line. */ - lsize = xasprintf(&label, "SIXEL IMAGE (%ux%u)\r\n", sx, sy) + 1; - if (sx < lsize - 3) - size = lsize - 1; - else - size = sx + 2; - - /* Remaining lines. Every placeholder line has \r\n at the end. */ - size += (sx + 2) * (sy - 1) + 1; - *ret = buf = xmalloc(size); - - /* Render first line. */ - if (sx < lsize - 3) { - memcpy(buf, label, lsize); - buf += lsize - 1; - } else { - memcpy(buf, label, lsize - 3); - buf += lsize - 3; - memset(buf, '+', sx - lsize + 3); - buf += sx - lsize + 3; - snprintf(buf, 3, "\r\n"); - buf += 2; - } - - /* Remaining lines. */ - for (py = 1; py < sy; py++) { - memset(buf, '+', sx); - buf += sx; - snprintf(buf, 3, "\r\n"); - buf += 2; - } - - free(label); -} - -struct image* -image_store(struct screen *s, struct sixel_image *si) +/* Allocate an image and assign a unique ID. */ +static struct image * +image_alloc(u_int w, u_int h, u_int cw, u_int ch, u_int sx, u_int sy, + size_t stride, u_char *pixels) { struct image *im; + u_int id; + + do { + if (++image_next_id == 0) + image_next_id++; + id = image_next_id; + } while (image_find(id) != NULL); im = xcalloc(1, sizeof *im); - im->s = s; - im->data = si; - - im->px = s->cx; - im->py = s->cy; - sixel_size_in_cells(si, &im->sx, &im->sy); - - image_fallback(&im->fallback, im->sx, im->sy); - - image_log(im, __func__, NULL); - im->list = &s->images; - TAILQ_INSERT_TAIL(im->list, im, entry); - - TAILQ_INSERT_TAIL(&all_images, im, all_entry); - if (++all_images_count == MAX_IMAGE_COUNT) - image_free(TAILQ_FIRST(&all_images)); - + im->id = id; + im->references = 1; + im->source_id = im->order_id = id; + im->width = w; + im->height = h; + im->canvas_width = cw; + im->canvas_height = ch; + im->sx = sx; + im->sy = sy; + im->stride = stride; + im->size = (h - 1) * stride + w * 4; + im->pixels = pixels; + RB_INSERT(images, &images, im); return (im); } -int -image_check_line(struct screen *s, u_int py, u_int ny) +/* Create an image with the provided pixels. */ +struct image * +image_create(u_int w, u_int h, u_int cw, u_int ch, u_int sx, u_int sy, + u_char *pixels) { - struct image *im, *im1; - int redraw = 0, in; + struct image *im; - TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) { - in = (py + ny > im->py && py < im->py + im->sy); - image_log(im, __func__, "py=%u, ny=%u, in=%d", py, ny, in); - if (in) { - image_free(im); - redraw = 1; - } - } - return (redraw); + if (w == 0 || h == 0 || cw < w || ch < h) + return (NULL); + if (sx == 0 || sy == 0 || pixels == NULL) + return (NULL); + if ((uint64_t)w * h > IMAGE_SIZE_LIMIT / 4) + return (NULL); + if (sx > USHRT_MAX || sy > USHRT_MAX) + return (NULL); + if (sx * sy > IMAGE_SIZE_LIMIT) + return (NULL); + + im = image_alloc(w, h, cw, ch, sx, sy, w * 4, pixels); + im->flags |= IMAGE_FLAG_OWN_PIXELS; + return (im); } -int -image_check_area(struct screen *s, u_int px, u_int py, u_int nx, u_int ny) +/* Resample the crop into transparent, padded RGBA pixels. */ +static u_char * +image_view_resample(struct image *src, struct image_view *view, u_int w, + u_int h) { - struct image *im, *im1; - int redraw = 0, in; + u_char *pixels, *row, *src_row; + u_int src_x, src_y; + uint64_t x, y, off; - TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) { - in = (py < im->py + im->sy && - py + ny > im->py && - px < im->px + im->sx && - px + nx > im->px); - image_log(im, __func__, "py=%u, ny=%u, in=%d", py, ny, in); - if (in) { - image_free(im); - redraw = 1; + pixels = xcalloc(w * h, 4); + for (y = 0; y < view->scaled_height; y++) { + src_y = view->y + y * view->height / view->scaled_height; + src_row = src->pixels + src_y * src->stride + view->x * 4; + + off = ((y + view->canvas_yoff) * w + view->canvas_xoff) * 4; + row = pixels + off; + + if (view->scaled_width == view->width) + memcpy(row, src_row, view->width * 4); + else { + for (x = 0; x < view->scaled_width; x++) { + src_x = x * view->width / view->scaled_width; + memcpy(row + x * 4, src_row + src_x * 4, 4); + } } } - return (redraw); + return (pixels); } -int -image_scroll_up(struct screen *s, u_int lines) +/* Crop and resample an image. The view takes a reference to the image. */ +struct image * +image_create_view(struct image *src, struct image_view *view) { - struct image *im, *im1; - int redraw = 0; - u_int sx, sy; - struct sixel_image *new; + struct image *im; + u_char *pixels; + u_int w, h; - TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) { - if (im->py >= lines) { - image_log(im, __func__, "1, lines=%u", lines); - im->py -= lines; - redraw = 1; - continue; - } - if (im->py + im->sy <= lines) { - image_log(im, __func__, "2, lines=%u", lines); - image_free(im); - redraw = 1; - continue; - } - sx = im->sx; - sy = (im->py + im->sy) - lines; - image_log(im, __func__, "3, lines=%u, sy=%u", lines, sy); + if (src == NULL || + view == NULL || + view->x >= src->width || + view->y >= src->height || + view->width == 0 || + view->height == 0 || + view->scaled_width == 0 || + view->scaled_height == 0 || + view->sx == 0 || + view->sy == 0 || + view->width > src->width - view->x || + view->height > src->height - view->y || + view->sx > USHRT_MAX || + view->sy > USHRT_MAX || + view->sx * view->sy > IMAGE_SIZE_LIMIT || + view->canvas_xoff > view->canvas_width || + view->scaled_width > view->canvas_width - view->canvas_xoff || + view->canvas_yoff > view->canvas_height || + view->scaled_height > view->canvas_height - view->canvas_yoff) + return (NULL); - new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1); - sixel_free(im->data); - im->data = new; - - im->py = 0; - sixel_size_in_cells(im->data, &im->sx, &im->sy); - - free(im->fallback); - image_fallback(&im->fallback, im->sx, im->sy); - redraw = 1; + if (view->scaled_width == view->width && + view->scaled_height == view->height && + view->canvas_xoff == 0 && + view->canvas_yoff == 0) { + pixels = src->pixels + view->y * src->stride + view->x * 4; + im = image_alloc(view->width, view->height, view->canvas_width, + view->canvas_height, view->sx, view->sy, src->stride, + pixels); + } else { + w = view->canvas_xoff + view->scaled_width; + h = view->canvas_yoff + view->scaled_height; + if ((uint64_t)w * h > IMAGE_SIZE_LIMIT / 4) + return (NULL); + pixels = image_view_resample(src, view, w, h); + im = image_alloc(w, h, view->canvas_width, view->canvas_height, + view->sx, view->sy, w * 4, pixels); + im->flags |= IMAGE_FLAG_OWN_PIXELS; } - return (redraw); + im->parent = src; + im->source_id = src->source_id; + im->order_id = src->order_id; + image_ref(src); + return (im); +} + +/* Add a reference to an image. */ +void +image_ref(struct image *im) +{ + if (im->references == UINT_MAX) + fatalx("invalid image reference %u", im->id); + im->references++; +} + +/* Free an image. */ +void +image_free(struct image *im) +{ + if (im->references == 0) + fatalx("invalid image reference %u", im->id); + if (--im->references == 0) { + RB_REMOVE(images, &images, im); + if (im->flags & IMAGE_FLAG_OWN_PIXELS) + free(im->pixels); + if (im->parent != NULL) + image_free(im->parent); + free(im); + } +} + +/* Return image size in pixels. */ +void +image_get_size(struct image *im, u_int *w, u_int *h) +{ + *w = im->width; + *h = im->height; +} + +/* Return canvas size in pixels. */ +void +image_get_canvas_size(struct image *im, u_int *w, u_int *h) +{ + *w = im->canvas_width; + *h = im->canvas_height; +} + +/* Return image size in cells. */ +void +image_get_size_in_cells(struct image *im, u_int *sx, u_int *sy) +{ + *sx = im->sx; + *sy = im->sy; +} + +/* Return image pixels. */ +const u_char * +image_get_pixels(struct image *im, size_t *stride, size_t *size) +{ + if (stride != NULL) + *stride = im->stride; + if (size != NULL) + *size = im->size; + return (im->pixels); +} + +/* Calculate the cell size. */ +void +image_size_in_cells(u_int w, u_int h, u_int xpixel, u_int ypixel, u_int *sx, + u_int *sy) +{ + if (xpixel == 0) + xpixel = 8; + if (ypixel == 0) + ypixel = 16; + *sx = w / xpixel + (w % xpixel != 0); + *sy = h / ypixel + (h % ypixel != 0); } diff --git a/input.c b/input.c index 9bd03cf4e..f2f1db535 100644 --- a/input.c +++ b/input.c @@ -2625,11 +2625,6 @@ input_dcs_dispatch(struct input_ctx *ictx) const char prefix[] = "tmux;"; const u_int prefixlen = (sizeof prefix) - 1; long long allow_passthrough = 0; -#ifdef ENABLE_IMAGES - struct window *w; - struct sixel_image *si; - int p2; -#endif if (wp == NULL) oo = global_w_options; @@ -2641,20 +2636,6 @@ input_dcs_dispatch(struct input_ctx *ictx) return (0); } -#ifdef ENABLE_IMAGES - if (wp != NULL && buf[0] == 'q' && ictx->interm_len == 0) { - w = wp->window; - if (input_split(ictx) != 0) - return (0); - p2 = input_get(ictx, 1, 0, 0); - if (p2 == -1) - p2 = 0; - si = sixel_parse(buf, len, p2, w->xpixel, w->ypixel); - if (si != NULL) - screen_write_sixelimage(sctx, si, ictx->cell.cell.bg); - } -#endif - /* DCS sequences with intermediate byte '$' (includes DECRQSS). */ if (ictx->interm_len == 1 && ictx->interm_buf[0] == '$') { /* DECRQSS is DCS $ q Pt ST. */ diff --git a/screen-redraw.c b/screen-redraw.c index 9d9704f98..b50b6df65 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1921,15 +1921,6 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags) tty_reset(tty); -#ifdef ENABLE_IMAGES - if (wp != NULL) - tty_draw_images(c, wp); - else { - TAILQ_FOREACH(loop, &scene->w->panes, entry) - tty_draw_images(c, loop); - } -#endif - log_debug("%s: finished @%u redraw", c->name, scene->w->id); } diff --git a/screen-write.c b/screen-write.c index bc2cedd2c..b37dd6283 100644 --- a/screen-write.c +++ b/screen-write.c @@ -1471,11 +1471,6 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx) memcpy(&gc, &grid_default_cell, sizeof gc); utf8_set(&gc.data, 'E'); -#ifdef ENABLE_IMAGES - if (image_free_all(s) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - for (yy = 0; yy < screen_size_y(s); yy++) { for (xx = 0; xx < screen_size_x(s); xx++) grid_view_set_cell(s->grid, xx, yy, &gc); @@ -1518,11 +1513,6 @@ 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_IMAGES - if (image_check_line(s, s->cy, 1) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - screen_write_initctx(ctx, &ttyctx, 0, 1); ttyctx.bg = bg; @@ -1559,11 +1549,6 @@ 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_IMAGES - if (image_check_line(s, s->cy, 1) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - screen_write_initctx(ctx, &ttyctx, 0, 1); ttyctx.bg = bg; @@ -1600,11 +1585,6 @@ 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_IMAGES - if (image_check_line(s, s->cy, 1) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - screen_write_initctx(ctx, &ttyctx, 0, 1); ttyctx.bg = bg; @@ -1635,11 +1615,6 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) if (ny == 0) ny = 1; -#ifdef ENABLE_IMAGES - if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - if (s->cy < s->rupper || s->cy > s->rlower) { if (ny > sy - s->cy) ny = sy - s->cy; @@ -1703,11 +1678,6 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) if (ny == 0) ny = 1; -#ifdef ENABLE_IMAGES - if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - if (s->cy < s->rupper || s->cy > s->rlower) { if (ny > sy - s->cy) ny = sy - s->cy; @@ -1776,11 +1746,6 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) if (gl->cellsize == 0 && COLOUR_DEFAULT(bg)) return; -#ifdef ENABLE_IMAGES - if (image_check_line(s, s->cy, 1) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - flags = gl->flags & GRID_LINE_OSC133_FLAGS; memcpy(&od, &gl->osc133_data, sizeof od); grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); @@ -1815,11 +1780,6 @@ 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_IMAGES - if (image_check_line(s, s->cy, 1) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); ci->x = s->cx; @@ -1842,11 +1802,6 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) return; } -#ifdef ENABLE_IMAGES - if (image_check_line(s, s->cy, 1) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - if (s->cx > sx - 1) grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); else @@ -1896,11 +1851,6 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg) return; } -#ifdef ENABLE_IMAGES - if (image_free_all(s) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg); screen_write_collect_flush(ctx, 0, __func__); @@ -1948,9 +1898,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_IMAGES - int redraw = 0; -#endif u_int rupper = s->rupper, rlower = s->rlower; gl = grid_get_line(gd, gd->hsize + s->cy); @@ -1971,15 +1918,6 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg) return; } -#ifdef ENABLE_IMAGES - if (rlower == screen_size_y(s) - 1) - redraw = image_scroll_up(s, 1); - else - redraw = image_check_line(s, rupper, rlower - rupper); - if (redraw && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); screen_write_collect_scroll(ctx, bg); ctx->scrolled++; @@ -2003,11 +1941,6 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg) ctx->bg = bg; } -#ifdef ENABLE_IMAGES - if (image_scroll_up(s, lines) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - for (i = 0; i < lines; i++) { grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); screen_write_collect_scroll(ctx, bg); @@ -2032,11 +1965,6 @@ 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_IMAGES - if (image_free_all(s) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - for (i = 0; i < lines; i++) grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg); @@ -2073,11 +2001,6 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) struct visible_ranges *r; struct visible_range *ri; -#ifdef ENABLE_IMAGES - if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - screen_write_initctx(ctx, &ttyctx, 1, 1); ttyctx.bg = bg; @@ -2154,11 +2077,6 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg) struct visible_ranges *r; struct visible_range *ri; -#ifdef ENABLE_IMAGES - if (image_check_line(s, 0, s->cy - 1) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - screen_write_initctx(ctx, &ttyctx, 1, 1); ttyctx.bg = bg; @@ -2227,11 +2145,6 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) struct visible_ranges *r; struct visible_range *ri; -#ifdef ENABLE_IMAGES - if (image_free_all(s) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - screen_write_initctx(ctx, &ttyctx, 1, 1); ttyctx.bg = bg; @@ -2735,11 +2648,6 @@ screen_write_collect_end(struct screen_write_ctx *ctx) } } -#ifdef ENABLE_IMAGES - if (image_check_area(s, s->cx, s->cy, ci->used, 1) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; -#endif - grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x, ci->used); if (bnx != 0) @@ -3239,66 +3147,6 @@ screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len, tty_write(tty_cmd_rawstring, &ttyctx); } -#ifdef ENABLE_IMAGES -/* Write a SIXEL image. */ -void -screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si, - u_int bg) -{ - struct screen *s = ctx->s; - struct grid *gd = s->grid; - struct tty_ctx ttyctx; - u_int x, y, sx, sy, cx = s->cx, cy = s->cy, i, lines; - struct sixel_image *new; - - if (screen_size_y(s) == 1) - return; - - sixel_size_in_cells(si, &x, &y); - if (x > screen_size_x(s) || y > screen_size_y(s) - 1) { - if (x > screen_size_x(s) - cx) - sx = screen_size_x(s) - cx; - else - sx = x; - if (y > screen_size_y(s) - 1) - sy = screen_size_y(s) - 1; - else - sy = y; - new = sixel_scale(si, 0, 0, 0, y - sy, sx, sy, 1); - sixel_free(si); - if (new == NULL) - return; - sixel_size_in_cells(new, &x, &y); - si = new; - } - - sy = screen_size_y(s) - cy; - if (sy <= y) { - lines = y - sy + 1; - if (image_scroll_up(s, lines) && ctx->wp != NULL) - ctx->wp->flags |= PANE_REDRAW; - for (i = 0; i < lines; i++) { - grid_view_scroll_region_up(gd, 0, screen_size_y(s) - 1, - bg); - screen_write_collect_scroll(ctx, bg); - } - ctx->scrolled += lines; - if (lines > cy) - screen_write_cursormove(ctx, -1, 0, 0); - else - screen_write_cursormove(ctx, -1, cy - lines, 0); - } - screen_write_collect_flush(ctx, 0, __func__); - - screen_write_initctx(ctx, &ttyctx, 0, 0); - ttyctx.image = image_store(s, si); - - tty_write(tty_cmd_sixelimage, &ttyctx); - - screen_write_cursormove(ctx, 0, cy + y, 0); -} -#endif - /* Turn alternate screen on. */ void screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc, diff --git a/screen.c b/screen.c index e8d90d595..2a433aaf0 100644 --- a/screen.c +++ b/screen.c @@ -93,11 +93,6 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit) s->tabs = NULL; s->sel = NULL; -#ifdef ENABLE_IMAGES - TAILQ_INIT(&s->images); - TAILQ_INIT(&s->saved_images); -#endif - s->write_list = NULL; s->hyperlinks = NULL; @@ -129,11 +124,6 @@ screen_reinit(struct screen *s) screen_clear_selection(s); screen_free_titles(s); - -#ifdef ENABLE_IMAGES - image_free_all(s); -#endif - screen_set_progress_bar(s, PROGRESS_BAR_HIDDEN, 0); screen_reset_hyperlinks(s); } @@ -152,10 +142,6 @@ screen_reset_hyperlinks(struct screen *s) void screen_free(struct screen *s) { -#ifdef ENABLE_IMAGES - struct image *im; -#endif - free(s->sel); free(s->tabs); free(s->path); @@ -172,17 +158,6 @@ screen_free(struct screen *s) hyperlinks_free(s->hyperlinks); screen_free_titles(s); -#ifdef ENABLE_IMAGES - /* - * Images saved when entering the alternate screen stay linked in the - * global list; move them back so they are freed and unlinked here, or - * a later eviction would write through this freed screen. - */ - TAILQ_CONCAT(&s->images, &s->saved_images, entry); - TAILQ_FOREACH(im, &s->images, entry) - im->list = &s->images; - image_free_all(s); -#endif } /* Reset tabs to default, eight spaces apart. */ @@ -375,10 +350,6 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow, if (sy != screen_size_y(s)) screen_resize_y(s, sy, eat_empty, &cy); -#ifdef ENABLE_IMAGES - image_free_all(s); -#endif - if (reflow) screen_reflow(s, sx, &cx, &cy, cursor); @@ -690,10 +661,6 @@ int screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) { u_int sx, sy; -#ifdef ENABLE_IMAGES - struct image *im; -#endif - if (SCREEN_IS_ALTERNATE(s)) return 0; sx = screen_size_x(s); @@ -707,12 +674,6 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) } memcpy(&s->saved_cell, gc, sizeof s->saved_cell); -#ifdef ENABLE_IMAGES - TAILQ_CONCAT(&s->saved_images, &s->images, entry); - TAILQ_FOREACH(im, &s->saved_images, entry) - im->list = &s->saved_images; -#endif - grid_view_clear(s->grid, 0, 0, sx, sy, 8); s->saved_flags = s->grid->flags; @@ -726,10 +687,6 @@ int screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) { u_int sx = screen_size_x(s), sy = screen_size_y(s); -#ifdef ENABLE_IMAGES - struct image *im; -#endif - /* * If the current size is different, temporarily resize to the old size * before copying back. @@ -776,13 +733,6 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) grid_destroy(s->saved_grid); s->saved_grid = NULL; -#ifdef ENABLE_IMAGES - image_free_all(s); - TAILQ_CONCAT(&s->images, &s->saved_images, entry); - TAILQ_FOREACH(im, &s->images, entry) - im->list = &s->images; -#endif - if (s->cx > screen_size_x(s) - 1) s->cx = screen_size_x(s) - 1; if (s->cy > screen_size_y(s) - 1) diff --git a/tmux-image.h b/tmux-image.h new file mode 100644 index 000000000..61fb5598b --- /dev/null +++ b/tmux-image.h @@ -0,0 +1,99 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * Copyright (c) 2026 Michael Grant + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef TMUX_IMAGE_H +#define TMUX_IMAGE_H + +#ifdef ENABLE_IMAGES +#error image support is not ready yet + +struct grid; +struct grid_line; +struct image; +struct image_line; +struct image_store; +struct screen; +struct screen_write_ctx; +struct sixel_image; + +/* View of all or part of an image. */ +struct image_view { + /* Position and size in the image, in pixels. */ + u_int x; + u_int y; + u_int width; + u_int height; + + /* Size after scaling, in pixels. */ + u_int scaled_width; + u_int scaled_height; + + /* Destination canvas size and offset in pixels. */ + u_int canvas_width; + u_int canvas_height; + u_int canvas_xoff; + u_int canvas_yoff; + + /* Destination size in terminal cells. */ + u_int sx; + u_int sy; +}; + +/* image.c */ +struct image *image_create(u_int, u_int, u_int, u_int, u_int, u_int, + u_char *); +struct image *image_create_view(struct image *, struct image_view *); +struct image *image_find(u_int); +u_int image_get_id(struct image *); +void image_ref(struct image *); +void image_free(struct image *); +void image_get_size(struct image *, u_int *, u_int *); +void image_get_canvas_size(struct image *, u_int *, u_int *); +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 *); + +/* image-grid.c */ +void image_grid_add(struct grid *, struct image *, u_int, u_int, + u_int, u_int, u_int, u_int); +void image_grid_remove(struct grid *, u_int, u_int, u_int, u_int); +void image_grid_free_line(struct grid *, struct grid_line *); +void image_grid_free(struct grid *); +void image_grid_move_cells(struct grid *, u_int, u_int, u_int, + u_int); +void image_grid_duplicate_lines(struct grid *, u_int, struct grid *, + u_int, u_int); +int image_grid_line_has_images(struct grid_line *); + +/* image-sixel.c */ +#define SIXEL_COLOUR_REGISTERS 1024 +struct sixel_image *sixel_parse(const char *, size_t, 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 *); +struct sixel_image *sixel_scale(struct sixel_image *, u_int, u_int, u_int, + u_int, u_int, u_int, int); +char *sixel_print(struct sixel_image *, struct sixel_image *, + size_t *); +struct screen *sixel_to_screen(struct sixel_image *); + +#endif /* ENABLE_IMAGES */ + +#endif /* TMUX_IMAGE_H */ diff --git a/tmux.h b/tmux.h index 37a504ac8..743e89d38 100644 --- a/tmux.h +++ b/tmux.h @@ -19,11 +19,13 @@ #ifndef TMUX_H #define TMUX_H +#include #include #include #include #include +#include #include #include #include @@ -33,6 +35,7 @@ #endif #include "compat.h" +#include "tmux-image.h" #include "tmux-protocol.h" #include "xmalloc.h" @@ -77,10 +80,6 @@ struct screen_write_cline; struct screen_write_ctx; struct session; -#ifdef ENABLE_IMAGES -struct sixel_image; -#endif - struct tty_ctx; struct tty_code; struct tty_key; @@ -927,6 +926,9 @@ struct grid_line { u_int time; struct osc133_data osc133_data; u_short flags; +#ifdef ENABLE_IMAGES + struct image_line *images; +#endif }; /* Entire grid of cells. */ @@ -946,6 +948,9 @@ struct grid { u_int scroll_generation; struct grid_line *linedata; +#ifdef ENABLE_IMAGES + struct image_store *images; +#endif }; /* Virtual cursor in a grid. */ @@ -1037,26 +1042,6 @@ struct style { u_int link; }; -#ifdef ENABLE_IMAGES -/* Image. */ -struct image { - struct screen *s; - struct sixel_image *data; - char *fallback; - - u_int px; - u_int py; - u_int sx; - u_int sy; - - struct images *list; - TAILQ_ENTRY (image) entry; - - TAILQ_ENTRY (image) all_entry; -}; -TAILQ_HEAD(images, image); -#endif - /* Cursor style. */ enum screen_cursor_style { SCREEN_CURSOR_DEFAULT, @@ -1113,11 +1098,6 @@ struct screen { bitstr_t *tabs; struct screen_sel *sel; -#ifdef ENABLE_IMAGES - struct images images; - struct images saved_images; -#endif - struct screen_write_cline *write_list; struct hyperlinks *hyperlinks; @@ -1905,9 +1885,6 @@ struct tty_ctx { size_t size; } sel; -#ifdef ENABLE_IMAGES - struct image *image; -#endif }; /* @@ -3006,10 +2983,6 @@ void tty_cmd_scrolldown(struct tty *, const struct tty_ctx *); void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *); void tty_cmd_setselection(struct tty *, const struct tty_ctx *); void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); -#ifdef ENABLE_IMAGES -void tty_cmd_sixelimage(struct tty *, const struct tty_ctx *); -void tty_draw_images(struct client *, struct window_pane *); -#endif void tty_cmd_syncstart(struct tty *, const struct tty_ctx *); void tty_default_colours(struct grid_cell *, struct window_pane *, u_int *); @@ -3650,10 +3623,6 @@ void screen_write_setselection(struct screen_write_ctx *, const char *, u_char *, u_int); void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int, int); -#ifdef ENABLE_IMAGES -void screen_write_sixelimage(struct screen_write_ctx *, - struct sixel_image *, u_int); -#endif void screen_write_alternateon(struct screen_write_ctx *, struct grid_cell *, int); void screen_write_alternateoff(struct screen_write_ctx *, @@ -4238,27 +4207,6 @@ void spawn_editor_finish(struct window_pane *); /* regsub.c */ char *regsub(const char *, const char *, const char *, int); -#ifdef ENABLE_IMAGES -/* image.c */ -int image_free_all(struct screen *); -struct image *image_store(struct screen *, struct sixel_image *); -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); - -/* image-sixel.c */ -#define SIXEL_COLOUR_REGISTERS 1024 -struct sixel_image *sixel_parse(const char *, size_t, 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 *); -struct sixel_image *sixel_scale(struct sixel_image *, u_int, u_int, u_int, - u_int, u_int, u_int, int); -char *sixel_print(struct sixel_image *, struct sixel_image *, - size_t *); -struct screen *sixel_to_screen(struct sixel_image *); -#endif - /* server-acl.c */ #define SERVER_ACL_READONLY 0x1 #define SERVER_ACL_IS_GROUP 0x2 diff --git a/tty.c b/tty.c index 494e5634f..ad2853d42 100644 --- a/tty.c +++ b/tty.c @@ -68,11 +68,6 @@ static void tty_emulate_repeat(struct tty *, enum tty_code_code, enum tty_code_code, u_int); static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int); -#ifdef ENABLE_IMAGES -static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *), - struct client *, struct tty_ctx *); -#endif - #define tty_use_margin(tty) \ (tty->term->flags & TERM_DECSLRM) #define tty_full_width(tty, ctx) \ @@ -1460,60 +1455,6 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc) return (&new); } -#ifdef ENABLE_IMAGES -/* Update context for client. */ -static int -tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c) -{ - struct window_pane *wp = ttyctx->arg; - - if (c->session->curw->window != wp->window) - return (0); - if (wp->layout_cell == NULL) - return (0); - - if (tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy, &ttyctx->wsx, - &ttyctx->wsy)) - ttyctx->flags |= TTY_CTX_WINDOW_BIGGER; - else - ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER; - - ttyctx->yoff = ttyctx->ryoff = wp->yoff; - if (status_at_line(c) == 0) - ttyctx->yoff += status_line_size(c); - - return (1); -} - -void -tty_draw_images(struct client *c, struct window_pane *wp) -{ - struct image *im; - struct tty_ctx ttyctx; - - TAILQ_FOREACH(im, &wp->screen->images, entry) { - memset(&ttyctx, 0, sizeof ttyctx); - - /* Set the client independent properties. */ - ttyctx.ocx = im->px; - ttyctx.ocy = im->py; - - ttyctx.orlower = wp->screen->rlower; - ttyctx.orupper = wp->screen->rupper; - - ttyctx.xoff = ttyctx.rxoff = wp->xoff; - ttyctx.sx = wp->sx; - ttyctx.sy = wp->sy; - - ttyctx.image = im; - ttyctx.arg = wp; - ttyctx.set_client_cb = tty_set_client_cb; - ttyctx.flags |= TTY_CTX_INVISIBLE_PANES; - tty_write_one(tty_cmd_sixelimage, c, &ttyctx); - } -} -#endif - void tty_sync_start(struct tty *tty) { @@ -1588,19 +1529,6 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), } } -#ifdef ENABLE_IMAGES -/* Only write to the incoming tty instead of every client. */ -static void -tty_write_one(void (*cmdfn)(struct tty *, const struct tty_ctx *), - struct client *c, struct tty_ctx *ctx) -{ - if (ctx->set_client_cb == NULL) - return; - if ((ctx->set_client_cb(ctx, c)) == 1) - cmdfn(&c->tty, ctx); -} -#endif - void tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) { @@ -2034,58 +1962,6 @@ tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx) tty_invalidate(tty); } -#ifdef ENABLE_IMAGES -void -tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx) -{ - struct image *im = ctx->image; - struct sixel_image *si = im->data; - struct sixel_image *new; - char *data; - size_t size; - u_int cx = ctx->ocx, cy = ctx->ocy, sx, sy; - u_int i, j, x, y, rx, ry; - int fallback = 0; - - if ((~tty->term->flags & TERM_SIXEL) && - !tty_term_has(tty->term, TTYC_SXL)) - fallback = 1; - if (tty->xpixel == 0 || tty->ypixel == 0) - fallback = 1; - - sixel_size_in_cells(si, &sx, &sy); - log_debug("%s: image is %ux%u", __func__, sx, sy); - if (!tty_clamp_area(tty, ctx, cx, cy, sx, sy, &i, &j, &x, &y, &rx, &ry)) - return; - log_debug("%s: clamping to %u,%u-%u,%u", __func__, i, j, rx, ry); - - if (fallback == 1) { - data = xstrdup(im->fallback); - size = strlen(data); - } else { - new = sixel_scale(si, tty->xpixel, tty->ypixel, i, j, rx, ry, 0); - if (new == NULL) - return; - - data = sixel_print(new, si, &size); - } - if (data != NULL) { - log_debug("%s: %zu bytes: %s", __func__, size, data); - tty_region_off(tty); - tty_margin_off(tty); - tty_cursor(tty, x, y); - - tty->flags |= TTY_NOBLOCK; - tty_add(tty, data, size); - tty_invalidate(tty); - free(data); - } - - if (fallback == 0) - sixel_free(new); -} -#endif - void tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx) {