mirror of
https://github.com/tmux/tmux.git
synced 2026-10-15 16:04:34 +00:00
Image storage and helper changes from the Kitty additions.
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tmux.h"
|
||||
@@ -50,52 +51,47 @@ fallback_get_flags(__unused struct tty *tty)
|
||||
|
||||
/* Get the average colour and brightness of an area. */
|
||||
static int
|
||||
fallback_sample(struct image *im, uint64_t px, uint64_t py, uint64_t pw,
|
||||
uint64_t ph, u_char *brightness)
|
||||
fallback_sample(struct image *im, u_int px, u_int py, u_int pw, u_int ph,
|
||||
u_char *brightness)
|
||||
{
|
||||
const u_char *pixels, *p;
|
||||
uint64_t red = 0, green = 0, blue = 0, sum = 0, count;
|
||||
u_int w, h, cw, ch, x0, x1, y0, y1, x, y;
|
||||
size_t stride;
|
||||
struct image_geom *ig = image_get_geometry(im);
|
||||
struct image_bounds ib;
|
||||
const u_char *pixels, *p;
|
||||
double red = 0, green = 0, blue = 0, sum = 0;
|
||||
double wx, wy, weight, area;
|
||||
u_int x0, x1, y0, y1, x, y;
|
||||
size_t stride;
|
||||
|
||||
image_get_size(im, &w, &h);
|
||||
image_get_canvas_size(im, &cw, &ch);
|
||||
pixels = image_get_pixels(im, &stride, NULL);
|
||||
|
||||
x0 = px * cw / pw;
|
||||
x1 = ((px + 1) * cw + pw - 1) / pw;
|
||||
y0 = py * ch / ph;
|
||||
y1 = ((py + 1) * ch + ph - 1) / ph;
|
||||
if (x1 <= x0)
|
||||
x1 = x0 + 1;
|
||||
if (y1 <= y0)
|
||||
y1 = y0 + 1;
|
||||
if (x0 >= w || y0 >= h) {
|
||||
if (!image_get_bounds(ig, px, py, 1, 1, pw, ph, &ib)) {
|
||||
if (brightness != NULL)
|
||||
*brightness = 0;
|
||||
return (colour_join_rgb(0, 0, 0));
|
||||
}
|
||||
count = (uint64_t)(x1 - x0) * (y1 - y0);
|
||||
area = ib.area;
|
||||
pixels = image_get_pixels(im, &stride, NULL);
|
||||
|
||||
if (x1 > w)
|
||||
x1 = w;
|
||||
if (y1 > h)
|
||||
y1 = h;
|
||||
x0 = floor(ib.left);
|
||||
x1 = ceil(ib.right);
|
||||
y0 = floor(ib.top);
|
||||
y1 = ceil(ib.bottom);
|
||||
for (y = y0; y < y1; y++) {
|
||||
wy = fmin(y + 1, ib.bottom) - fmax(y, ib.top);
|
||||
for (x = x0; x < x1; x++) {
|
||||
p = pixels + y * stride + x * 4;
|
||||
wx = fmin(x + 1, ib.right) - fmax(x, ib.left);
|
||||
weight = wx * wy;
|
||||
|
||||
red += p[0] * p[3] / 255;
|
||||
green += p[1] * p[3] / 255;
|
||||
blue += p[2] * p[3] / 255;
|
||||
red += (p[0] * p[3] / 255) * weight;
|
||||
green += (p[1] * p[3] / 255) * weight;
|
||||
blue += (p[2] * p[3] / 255) * weight;
|
||||
|
||||
sum += ((2126ULL * p[0] + 7152ULL * p[1] +
|
||||
722ULL * p[2]) / 10000) * p[3] / 255;
|
||||
722ULL * p[2]) / 10000) * p[3] / 255 * weight;
|
||||
}
|
||||
}
|
||||
if (brightness != NULL)
|
||||
*brightness = sum / count;
|
||||
return (colour_join_rgb(red / count, green / count, blue / count));
|
||||
*brightness = sum / area;
|
||||
return (colour_join_rgb(red / area, green / area, blue / area));
|
||||
}
|
||||
|
||||
/* Set a cell to a character representing part of an image. */
|
||||
@@ -105,7 +101,9 @@ fallback_get_cell(struct tty *tty, struct image *im, u_int x, u_int y,
|
||||
{
|
||||
static const char a[] = " .:-=+*#%@";
|
||||
static const u_char bold_shades[8] = { 0, 1, 1, 2, 2, 3, 4, 4 };
|
||||
u_int ch, level, nlevels, sx, sy, colours;
|
||||
struct image_geom *ig = image_get_geometry(im);
|
||||
u_int ch, level, nlevels, colours;
|
||||
u_int sx = ig->sx, sy = ig->sy;
|
||||
u_char brightness;
|
||||
|
||||
if (out != gc)
|
||||
@@ -113,7 +111,6 @@ fallback_get_cell(struct tty *tty, struct image *im, u_int x, u_int y,
|
||||
out->flags &= GRID_FLAG_SELECTED;
|
||||
out->attr = 0;
|
||||
out->link = 0;
|
||||
image_get_size_in_cells(im, &sx, &sy);
|
||||
if (x >= sx || y >= sy) {
|
||||
utf8_set(&out->data, ' ');
|
||||
return;
|
||||
|
||||
50
image-grid.c
50
image-grid.c
@@ -354,25 +354,25 @@ 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_geom *ig;
|
||||
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)
|
||||
ig = image_get_geometry(im);
|
||||
if (src_x >= ig->sx || src_y >= ig->sy)
|
||||
return;
|
||||
|
||||
if (sx > gd->sx - x)
|
||||
sx = gd->sx - x;
|
||||
if (sx > image_sx - src_x)
|
||||
sx = image_sx - src_x;
|
||||
if (sx > ig->sx - src_x)
|
||||
sx = ig->sx - src_x;
|
||||
if (sy > rows - y)
|
||||
sy = rows - y;
|
||||
if (sy > image_sy - src_y)
|
||||
sy = image_sy - src_y;
|
||||
if (sy > ig->sy - src_y)
|
||||
sy = ig->sy - src_y;
|
||||
|
||||
plmt = image_placement_create(gd, im);
|
||||
for (row = 0; row < sy; row++) {
|
||||
@@ -739,16 +739,14 @@ static int
|
||||
image_cell_covers(struct image *new, u_int nx, u_int ny, struct image *old,
|
||||
u_int ox, u_int oy)
|
||||
{
|
||||
u_int npx, npy, nsx, nsy, opx, opy, osx, osy, xx, yy;
|
||||
u_int ncw, nch, ocw, och, ncx, ncy, ocx, ocy;
|
||||
const u_char *np, *op, *npixels, *opixels;
|
||||
size_t nstride, ostride;
|
||||
struct image_geom *ng = image_get_geometry(new);
|
||||
struct image_geom *og = image_get_geometry(old);
|
||||
u_int npx, npy, nsx, nsy, opx, opy, osx, osy, xx, yy;
|
||||
const u_char *np, *op, *npixels, *opixels;
|
||||
size_t nstride, ostride;
|
||||
|
||||
image_get_canvas_size(new, &ncw, &nch);
|
||||
image_get_canvas_size(old, &ocw, &och);
|
||||
image_get_size_in_cells(new, &ncx, &ncy);
|
||||
image_get_size_in_cells(old, &ocx, &ocy);
|
||||
if (ncw / ncx != ocw / ocx || nch / ncy != och / ocy)
|
||||
if (ng->canvas_width / ng->sx != og->canvas_width / og->sx ||
|
||||
ng->canvas_height / ng->sy != og->canvas_height / og->sy)
|
||||
return (0);
|
||||
image_get_pixel_area(new, nx, ny, 1, 1, &npx, &npy, &nsx, &nsy);
|
||||
image_get_pixel_area(old, ox, oy, 1, 1, &opx, &opy, &osx, &osy);
|
||||
@@ -838,10 +836,11 @@ image_extend_row(struct image_line *line, struct image_placement *plmt,
|
||||
void
|
||||
image_grid_resize_width(struct grid *gd, u_int new_sx)
|
||||
{
|
||||
struct image_geom *ig;
|
||||
struct image_placement *plmt;
|
||||
struct image_span *span;
|
||||
struct image_recover_row *rows;
|
||||
u_int image_sx, image_sy, from, to;
|
||||
u_int from, to;
|
||||
u_int nrows, i;
|
||||
|
||||
if (gd->images == NULL)
|
||||
@@ -850,11 +849,11 @@ image_grid_resize_width(struct grid *gd, u_int new_sx)
|
||||
from = plmt->recover_from;
|
||||
if (from == 0 || plmt->x >= new_sx)
|
||||
continue;
|
||||
image_get_size_in_cells(plmt->image, &image_sx, &image_sy);
|
||||
ig = image_get_geometry(plmt->image);
|
||||
|
||||
to = new_sx - plmt->x;
|
||||
if (to > image_sx)
|
||||
to = image_sx;
|
||||
if (to > ig->sx)
|
||||
to = ig->sx;
|
||||
if (to <= from)
|
||||
continue;
|
||||
|
||||
@@ -877,7 +876,7 @@ image_grid_resize_width(struct grid *gd, u_int new_sx)
|
||||
rows[i].source_y, from, to);
|
||||
}
|
||||
free(rows);
|
||||
plmt->recover_from = (to == image_sx ? 0 : to);
|
||||
plmt->recover_from = (to == ig->sx ? 0 : to);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -885,18 +884,17 @@ image_grid_resize_width(struct grid *gd, u_int new_sx)
|
||||
void
|
||||
image_write(struct screen_write_ctx *ctx, struct image *im, u_int bg)
|
||||
{
|
||||
struct image_geom *ig = image_get_geometry(im);
|
||||
struct screen *s = ctx->s;
|
||||
struct grid *gd = s->grid;
|
||||
struct image_placement *plmt;
|
||||
struct grid_line *gl;
|
||||
struct image_line *line;
|
||||
u_int cx = s->cx, cy = s->cy, y, sx, sy, lines;
|
||||
u_int image_sx;
|
||||
u_int cx = s->cx, cy = s->cy, y, lines;
|
||||
u_int sx = ig->sx, sy = ig->sy;
|
||||
u_int origin_y = 0, h_origin_y, region_height;
|
||||
u_int remaining, n;
|
||||
|
||||
image_get_size_in_cells(im, &sx, &sy);
|
||||
image_sx = sx;
|
||||
if (sx > screen_size_x(s) - cx)
|
||||
sx = screen_size_x(s) - cx;
|
||||
if (sx == 0)
|
||||
@@ -928,7 +926,7 @@ image_write(struct screen_write_ctx *ctx, struct image *im, u_int bg)
|
||||
sy -= origin_y;
|
||||
}
|
||||
plmt = image_placement_create(gd, im);
|
||||
if (sx < image_sx) {
|
||||
if (sx < ig->sx) {
|
||||
plmt->x = cx;
|
||||
plmt->recover_from = sx;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -165,17 +166,7 @@ struct sixel_rgb {
|
||||
|
||||
/* Pixels and dimensions of the image being encoded. */
|
||||
struct sixel_source {
|
||||
/* Source pixels and number of bytes between rows. */
|
||||
const u_char *pixels;
|
||||
size_t stride;
|
||||
|
||||
/* Source image and canvas sizes in pixels and size in cells. */
|
||||
u_int width;
|
||||
u_int height;
|
||||
u_int canvas_width;
|
||||
u_int canvas_height;
|
||||
u_int sx;
|
||||
u_int sy;
|
||||
struct image *image;
|
||||
|
||||
/* Destination canvas size in pixels. */
|
||||
u_int dst_w;
|
||||
@@ -636,9 +627,10 @@ sixel_colour_to_rgb(u_int c, u_char *r, u_char *g, u_char *b)
|
||||
static struct image *
|
||||
sixel_to_image(struct sixel_image *si)
|
||||
{
|
||||
struct image *im;
|
||||
u_char *pixels, *pixel, r, g, b;
|
||||
u_int x, y, c, sx, sy;
|
||||
struct image *im;
|
||||
struct image_geom ig = { 0 };
|
||||
u_char *pixels, *pixel, r, g, b;
|
||||
u_int x, y, c, sx, sy;
|
||||
|
||||
if ((uint64_t)si->x * si->y > IMAGE_SIZE_LIMIT / 4)
|
||||
return (NULL);
|
||||
@@ -669,8 +661,13 @@ sixel_to_image(struct sixel_image *si)
|
||||
pixel[3] = 255;
|
||||
}
|
||||
}
|
||||
im = image_create(si->x, si->y, sx * si->xpixel, sy * si->ypixel, sx,
|
||||
sy, pixels);
|
||||
ig.src_w = ig.scaled_width = si->x;
|
||||
ig.src_h = ig.scaled_height = si->y;
|
||||
ig.canvas_width = sx * si->xpixel;
|
||||
ig.canvas_height = sy * si->ypixel;
|
||||
ig.sx = sx;
|
||||
ig.sy = sy;
|
||||
im = image_create(&ig, pixels);
|
||||
if (im == NULL)
|
||||
free(pixels);
|
||||
return (im);
|
||||
@@ -1077,13 +1074,11 @@ sixel_clamp_colour(int colour)
|
||||
|
||||
/* Return a source pixel mapped to an output SIXEL pixel. */
|
||||
static const u_char *
|
||||
sixel_from_image_pixel(const struct sixel_source *src, u_int x, u_int y)
|
||||
sixel_from_image_pixel(struct sixel_source *src, u_int x, u_int y)
|
||||
{
|
||||
u_int px, py;
|
||||
u_int px = src->dst_x + x, py = src->dst_y + y;
|
||||
|
||||
px = ((uint64_t)src->dst_x + x) * src->canvas_width / src->dst_w;
|
||||
py = ((uint64_t)src->dst_y + y) * src->canvas_height / src->dst_h;
|
||||
return (src->pixels + py * src->stride + px * 4);
|
||||
return (image_get_pixel(src->image, px, py, src->dst_w, src->dst_h));
|
||||
}
|
||||
|
||||
/* Make a palette from the visible pixels in the output area. */
|
||||
@@ -1170,6 +1165,7 @@ sixel_make_image(struct image *im, u_int cx, u_int cy, u_int csx, u_int csy,
|
||||
struct sixel_image *si;
|
||||
struct sixel_rgb palette[SIXEL_PALETTE_SIZE];
|
||||
struct sixel_source src;
|
||||
struct image_geom *ig = image_get_geometry(im);
|
||||
const u_char *pixel;
|
||||
uint16_t *cache;
|
||||
int *current, *next, *tmp;
|
||||
@@ -1179,18 +1175,19 @@ sixel_make_image(struct image *im, u_int cx, u_int cy, u_int csx, u_int csy,
|
||||
uint64_t left, right, top, bottom;
|
||||
|
||||
/* Work out the requested cell crop in destination pixel coordinates. */
|
||||
src.pixels = image_get_pixels(im, &src.stride, NULL);
|
||||
image_get_size(im, &src.width, &src.height);
|
||||
image_get_canvas_size(im, &src.canvas_width, &src.canvas_height);
|
||||
image_get_size_in_cells(im, &src.sx, &src.sy);
|
||||
dst_w = (uint64_t)src.sx * cw;
|
||||
dst_h = (uint64_t)src.sy * ch;
|
||||
src.image = im;
|
||||
dst_w = (uint64_t)ig->sx * cw;
|
||||
dst_h = (uint64_t)ig->sy * ch;
|
||||
if (dst_w > UINT_MAX || dst_h > UINT_MAX)
|
||||
return (NULL);
|
||||
content_width = ((uint64_t)src.width * dst_w +
|
||||
src.canvas_width - 1) / src.canvas_width;
|
||||
content_height = ((uint64_t)src.height * dst_h +
|
||||
src.canvas_height - 1) / src.canvas_height;
|
||||
content_width = ceil((ig->canvas_xoff + ig->scaled_width) * dst_w /
|
||||
ig->canvas_width);
|
||||
content_height = ceil((ig->canvas_yoff + ig->scaled_height) * dst_h /
|
||||
ig->canvas_height);
|
||||
if (content_width > dst_w)
|
||||
content_width = dst_w;
|
||||
if (content_height > dst_h)
|
||||
content_height = dst_h;
|
||||
|
||||
/* Clip the requested cell rectangle to output pixel bounds. */
|
||||
left = (uint64_t)cx * cw;
|
||||
@@ -1313,8 +1310,9 @@ static struct sixel_image *
|
||||
sixel_get_image(struct image *im, u_int cw, u_int ch)
|
||||
{
|
||||
struct sixel_image *si;
|
||||
struct image_geom *ig = image_get_geometry(im);
|
||||
size_t size;
|
||||
u_int id = image_get_id(im), sx, sy;
|
||||
u_int id = image_get_id(im);
|
||||
struct sixel_item *cache, find = { .id = id, .cw = cw, .ch = ch };
|
||||
|
||||
cache = RB_FIND(sixel_tree, &sixel_tree, &find);
|
||||
@@ -1323,9 +1321,8 @@ sixel_get_image(struct image *im, u_int cw, u_int ch)
|
||||
TAILQ_INSERT_TAIL(&sixel_cache, cache, entry);
|
||||
return (cache->si);
|
||||
}
|
||||
image_get_size_in_cells(im, &sx, &sy);
|
||||
|
||||
si = sixel_make_image(im, 0, 0, sx, sy, cw, ch);
|
||||
si = sixel_make_image(im, 0, 0, ig->sx, ig->sy, cw, ch);
|
||||
if (si == NULL)
|
||||
return (NULL);
|
||||
|
||||
|
||||
296
image.c
296
image.c
@@ -20,6 +20,7 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -29,25 +30,11 @@
|
||||
struct image {
|
||||
u_int id;
|
||||
u_int references;
|
||||
u_int flags;
|
||||
#define IMAGE_FLAG_OWN_PIXELS 0x1
|
||||
struct image_geom geom;
|
||||
|
||||
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
|
||||
@@ -62,6 +49,8 @@ struct image {
|
||||
};
|
||||
RB_HEAD(images, image);
|
||||
static struct images images = RB_INITIALIZER(&images);
|
||||
|
||||
/* Next image ID. */
|
||||
static u_int image_next_id;
|
||||
|
||||
/* Compare images by IDs. */
|
||||
@@ -95,8 +84,7 @@ image_get_id(struct image *im)
|
||||
|
||||
/* 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)
|
||||
image_alloc(struct image_geom *ig, size_t stride, u_char *pixels)
|
||||
{
|
||||
struct image *im;
|
||||
u_int id;
|
||||
@@ -110,15 +98,10 @@ image_alloc(u_int w, u_int h, u_int cw, u_int ch, u_int sx, u_int sy,
|
||||
im = xcalloc(1, sizeof *im);
|
||||
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->order_id = id;
|
||||
im->geom = *ig;
|
||||
im->stride = stride;
|
||||
im->size = (h - 1) * stride + w * 4;
|
||||
im->size = (ig->src_h - 1) * stride + ig->src_w * 4;
|
||||
im->pixels = pixels;
|
||||
RB_INSERT(images, &images, im);
|
||||
return (im);
|
||||
@@ -126,107 +109,41 @@ image_alloc(u_int w, u_int h, u_int cw, u_int ch, u_int sx, u_int sy,
|
||||
|
||||
/* 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)
|
||||
image_create(struct image_geom *ig, u_char *pixels)
|
||||
{
|
||||
struct image *im;
|
||||
size_t stride = ig->src_w * 4;
|
||||
|
||||
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 ((uint64_t)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);
|
||||
return (image_create_with_parent(NULL, ig, stride, pixels));
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
u_char *pixels, *row, *src_row;
|
||||
u_int src_x, src_y;
|
||||
uint64_t x, y, off;
|
||||
|
||||
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 (pixels);
|
||||
}
|
||||
|
||||
/* Crop and resample an image. The view takes a reference to the image. */
|
||||
/* Create an image based on a parent image. */
|
||||
struct image *
|
||||
image_create_view(struct image *src, struct image_view *view)
|
||||
image_create_with_parent(struct image *parent, struct image_geom *ig,
|
||||
size_t stride, u_char *pixels)
|
||||
{
|
||||
struct image *im;
|
||||
u_char *pixels;
|
||||
u_int w, h;
|
||||
|
||||
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 ||
|
||||
(uint64_t)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)
|
||||
if (ig->src_w == 0 || ig->src_h == 0)
|
||||
return (NULL);
|
||||
if (ig->canvas_width == 0 || ig->canvas_height == 0)
|
||||
return (NULL);
|
||||
if (ig->sx == 0 || ig->sy == 0 || pixels == NULL)
|
||||
return (NULL);
|
||||
if ((uint64_t)ig->src_w * ig->src_h > IMAGE_SIZE_LIMIT / 4)
|
||||
return (NULL);
|
||||
if (ig->sx > USHRT_MAX || ig->sy > USHRT_MAX)
|
||||
return (NULL);
|
||||
if (ig->sx * ig->sy > IMAGE_SIZE_LIMIT || stride < ig->src_w * 4)
|
||||
return (NULL);
|
||||
if (ig->scaled_width <= 0 || ig->scaled_height <= 0)
|
||||
return (NULL);
|
||||
|
||||
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;
|
||||
im = image_alloc(ig, stride, pixels);
|
||||
im->parent = parent;
|
||||
if (parent != NULL) {
|
||||
im->order_id = parent->order_id;
|
||||
image_ref(parent);
|
||||
}
|
||||
im->parent = src;
|
||||
im->source_id = src->source_id;
|
||||
im->order_id = src->order_id;
|
||||
image_ref(src);
|
||||
return (im);
|
||||
}
|
||||
|
||||
@@ -247,47 +164,104 @@ image_free(struct image *im)
|
||||
fatalx("invalid image reference %u", im->id);
|
||||
if (--im->references == 0) {
|
||||
RB_REMOVE(images, &images, im);
|
||||
if (im->flags & IMAGE_FLAG_OWN_PIXELS)
|
||||
if (im->parent == NULL)
|
||||
free(im->pixels);
|
||||
if (im->parent != NULL)
|
||||
else
|
||||
image_free(im->parent);
|
||||
free(im);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get image size in pixels. */
|
||||
void
|
||||
image_get_size(struct image *im, u_int *w, u_int *h)
|
||||
/* Get the image geometry. */
|
||||
struct image_geom *
|
||||
image_get_geometry(struct image *im)
|
||||
{
|
||||
*w = im->width;
|
||||
*h = im->height;
|
||||
return (&im->geom);
|
||||
}
|
||||
|
||||
/* Get 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;
|
||||
}
|
||||
|
||||
/* Get 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;
|
||||
}
|
||||
|
||||
/* Get image pixels. */
|
||||
const u_char *
|
||||
/* Get the image pixels. */
|
||||
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);
|
||||
return (im->pixels + im->geom.src_y * im->stride + im->geom.src_x * 4);
|
||||
}
|
||||
|
||||
/* Convert an output X position to a source image position in pixels. */
|
||||
static double
|
||||
image_map_x(struct image_geom *ig, u_int x, u_int w)
|
||||
{
|
||||
double p;
|
||||
|
||||
p = (double)x * ig->canvas_width;
|
||||
p -= (double)ig->canvas_xoff * w;
|
||||
p *= ig->src_w;
|
||||
return (p / (w * ig->scaled_width));
|
||||
}
|
||||
|
||||
/* Convert an output Y position to a source image position in pixels. */
|
||||
static double
|
||||
image_map_y(struct image_geom *ig, u_int y, u_int h)
|
||||
{
|
||||
double p;
|
||||
|
||||
p = ((double)y * ig->canvas_height) - ((double)ig->canvas_yoff * h);
|
||||
return ((p * ig->src_h) / (h * ig->scaled_height));
|
||||
}
|
||||
|
||||
/* Find source image bounds for part of an output. */
|
||||
int
|
||||
image_get_bounds(struct image_geom *ig, u_int x, u_int y, u_int w, u_int h,
|
||||
u_int sx, u_int sy, struct image_bounds *ib)
|
||||
{
|
||||
if (x >= sx || y >= sy || w == 0 || h == 0)
|
||||
return (0);
|
||||
if (w > sx - x)
|
||||
w = sx - x;
|
||||
if (h > sy - y)
|
||||
h = sy - y;
|
||||
|
||||
ib->left = image_map_x(ig, x, sx);
|
||||
ib->top = image_map_y(ig, y, sy);
|
||||
ib->right = image_map_x(ig, x + w, sx);
|
||||
ib->bottom = image_map_y(ig, y + h, sy);
|
||||
if (ib->left >= ig->src_w ||
|
||||
ib->top >= ig->src_h ||
|
||||
ib->right <= 0 ||
|
||||
ib->bottom <= 0)
|
||||
return (0);
|
||||
ib->area = (ib->right - ib->left) * (ib->bottom - ib->top);
|
||||
|
||||
if (ib->left < 0)
|
||||
ib->left = 0;
|
||||
if (ib->top < 0)
|
||||
ib->top = 0;
|
||||
if (ib->right > ig->src_w)
|
||||
ib->right = ig->src_w;
|
||||
if (ib->bottom > ig->src_h)
|
||||
ib->bottom = ig->src_h;
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Map a pixel in the full output canvas to the original source pixels. */
|
||||
u_char *
|
||||
image_get_pixel(struct image *im, u_int x, u_int y, u_int w, u_int h)
|
||||
{
|
||||
static u_char empty[4];
|
||||
struct image_geom *ig = &im->geom;
|
||||
double px, py;
|
||||
u_int sx, sy;
|
||||
|
||||
px = image_map_x(ig, x, w);
|
||||
py = image_map_y(ig, y, h);
|
||||
if (px < 0 || py < 0 || px >= ig->src_w || py >= ig->src_h)
|
||||
return (empty);
|
||||
sx = px;
|
||||
sy = py;
|
||||
return (image_get_pixels(im, NULL, NULL) + sy * im->stride + sx * 4);
|
||||
}
|
||||
|
||||
/* Calculate the cell size. */
|
||||
@@ -303,41 +277,23 @@ image_size_in_cells(u_int w, u_int h, u_int xpixel, u_int ypixel, u_int *sx,
|
||||
*sy = h / ypixel + (h % ypixel != 0);
|
||||
}
|
||||
|
||||
/* Find area covered by a rectangle of cells in pixels. */
|
||||
/* Find the source pixels covered by a rectangle of placement cells. */
|
||||
void
|
||||
image_get_pixel_area(struct image *im, u_int x, u_int y, u_int w, u_int h,
|
||||
u_int *px, u_int *py, u_int *pw, u_int *ph)
|
||||
{
|
||||
u_int left, top, right, bottom;
|
||||
struct image_geom *ig;
|
||||
struct image_bounds ib;
|
||||
|
||||
*px = *py = *pw = *ph = 0;
|
||||
|
||||
if (im == NULL || x >= im->sx || y >= im->sy || w == 0 || h == 0)
|
||||
return;
|
||||
if (w > im->sx - x)
|
||||
w = im->sx - x;
|
||||
if (h > im->sy - y)
|
||||
h = im->sy - y;
|
||||
|
||||
left = (uint64_t)x * im->canvas_width / im->sx;
|
||||
top = (uint64_t)y * im->canvas_height / im->sy;
|
||||
right = ((uint64_t)(x + w) * im->canvas_width + im->sx - 1) / im->sx;
|
||||
bottom = ((uint64_t)(y + h) * im->canvas_height + im->sy - 1) / im->sy;
|
||||
|
||||
if (left >= im->width || top >= im->height)
|
||||
*px = *py = 0;
|
||||
else {
|
||||
if (right <= left)
|
||||
right = left + 1;
|
||||
if (bottom <= top)
|
||||
bottom = top + 1;
|
||||
if (right > im->width)
|
||||
right = im->width;
|
||||
if (bottom > im->height)
|
||||
bottom = im->height;
|
||||
*px = left;
|
||||
*py = top;
|
||||
*pw = right - left;
|
||||
*ph = bottom - top;
|
||||
if (im != NULL) {
|
||||
ig = &im->geom;
|
||||
if (image_get_bounds(ig, x, y, w, h, ig->sx, ig->sy, &ib)) {
|
||||
*px = floor(ib.left);
|
||||
*py = floor(ib.top);
|
||||
*pw = ceil(ib.right) - *px;
|
||||
*ph = ceil(ib.bottom) - *py;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,4 +105,16 @@ for nobr in (False, True):
|
||||
else:
|
||||
assert chars == ' \u2591\u2591\u2592\u2592\u2593\u2588\u2588', chars
|
||||
assert bold == [False, False, True, False, True, False, False, True], bold
|
||||
# Odd cell heights split a source pixel between the two fallback samples.
|
||||
with image_test(sys.argv[1]) as test:
|
||||
test.run('set-option', '-as', 'terminal-features', 'screen-256color:RGB')
|
||||
client = test.attach(graphical=False, ch=15)
|
||||
test.emit(sixel(8, 15, [(100, 0, 0), (0, 0, 100)],
|
||||
[y >= 8 for y in range(15) for x in range(8)]))
|
||||
test.redraw()
|
||||
terminal = Terminal(ch=15)
|
||||
terminal.feed(client.data)
|
||||
char, fg, bg, bold = terminal.cells[0, 0]
|
||||
assert char == '\u2584' and not bold, (char, bold)
|
||||
assert (fg, bg) == ((17, 0, 238), (255, 0, 0)), (fg, bg)
|
||||
PY
|
||||
|
||||
52
tmux-image.h
52
tmux-image.h
@@ -43,29 +43,40 @@ struct tty;
|
||||
/* Backend images move with terminal scrolling. */
|
||||
#define IMAGE_BACKEND_SCROLLS 0x2
|
||||
|
||||
/* 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;
|
||||
/* Source image and placement geometry. */
|
||||
struct image_geom {
|
||||
/* Position and size within the source RGBA pixels. */
|
||||
u_int src_x;
|
||||
u_int src_y;
|
||||
u_int src_w;
|
||||
u_int src_h;
|
||||
|
||||
/* Size after scaling, in pixels. */
|
||||
u_int scaled_width;
|
||||
u_int scaled_height;
|
||||
|
||||
/* Destination canvas size and offset in pixels. */
|
||||
/* Actual image size in pixels, including padding. */
|
||||
u_int canvas_width;
|
||||
u_int canvas_height;
|
||||
|
||||
/* Content offset within the canvas, in pixels. */
|
||||
u_int canvas_xoff;
|
||||
u_int canvas_yoff;
|
||||
|
||||
/* Destination size in terminal cells. */
|
||||
/* Scaled source size in pixels, excluding padding. */
|
||||
double scaled_width;
|
||||
double scaled_height;
|
||||
|
||||
/* Actual image size in cells, including padding. */
|
||||
u_int sx;
|
||||
u_int sy;
|
||||
};
|
||||
|
||||
/* Bounds within a source image, in pixels. */
|
||||
struct image_bounds {
|
||||
double left;
|
||||
double top;
|
||||
double right;
|
||||
double bottom;
|
||||
double area;
|
||||
};
|
||||
|
||||
/* A part of an image to draw. */
|
||||
struct image_rect {
|
||||
struct image *image;
|
||||
@@ -84,17 +95,18 @@ struct image_rect {
|
||||
};
|
||||
|
||||
/* 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_create(struct image_geom *, u_char *);
|
||||
struct image *image_create_with_parent(struct image *, struct image_geom *,
|
||||
size_t, u_char *);
|
||||
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 *);
|
||||
struct image_geom *image_get_geometry(struct image *);
|
||||
u_char *image_get_pixels(struct image *, size_t *, size_t *);
|
||||
int image_get_bounds(struct image_geom *, u_int, u_int, u_int,
|
||||
u_int, u_int, u_int, struct image_bounds *);
|
||||
u_char *image_get_pixel(struct image *, 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 *);
|
||||
void image_get_pixel_area(struct image *, u_int, u_int, u_int,
|
||||
|
||||
Reference in New Issue
Block a user