mirror of
https://github.com/tmux/tmux.git
synced 2026-08-27 17:11:35 +00:00
Preserve SIXEL pixel dimensions
Retain the padded canonical pixel canvas separately from the image content. Scale only the populated portion of partial edge cells so an image keeps its exact raster size on the originating terminal while retaining its cell footprint.
This commit is contained in:
@@ -485,7 +485,13 @@ sixel_to_image(struct sixel_image *si)
|
||||
}
|
||||
}
|
||||
sixel_size_in_cells(si, &sx, &sy);
|
||||
im = image_create(si->x, si->y, sx, sy, pixels);
|
||||
if ((uint64_t)sx * si->xpixel > UINT_MAX ||
|
||||
(uint64_t)sy * si->ypixel > UINT_MAX) {
|
||||
free(pixels);
|
||||
return (NULL);
|
||||
}
|
||||
im = image_create(si->x, si->y, sx * si->xpixel, sy * si->ypixel,
|
||||
sx, sy, pixels);
|
||||
if (im == NULL)
|
||||
free(pixels);
|
||||
return (im);
|
||||
@@ -743,12 +749,29 @@ sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells_x,
|
||||
u_int x, y, sx, sy, sourcex, sourcey;
|
||||
u_int sourcex0, sourcey0, sourcewidth, sourceheight;
|
||||
u_int r, g, b, colour, i;
|
||||
uint64_t destination_width, destination_height;
|
||||
uint64_t content_width, content_height, x0, x1, y0, y1;
|
||||
|
||||
if ((uint64_t)cells_x * xpixel > UINT_MAX ||
|
||||
(uint64_t)cells_y * ypixel > UINT_MAX)
|
||||
destination_width = (uint64_t)im->sx * xpixel;
|
||||
destination_height = (uint64_t)im->sy * ypixel;
|
||||
if (destination_width > UINT_MAX || destination_height > UINT_MAX)
|
||||
return (NULL);
|
||||
sx = cells_x * xpixel;
|
||||
sy = cells_y * ypixel;
|
||||
content_width = ((uint64_t)im->width * destination_width +
|
||||
im->canvas_width - 1) / im->canvas_width;
|
||||
content_height = ((uint64_t)im->height * destination_height +
|
||||
im->canvas_height - 1) / im->canvas_height;
|
||||
x0 = (uint64_t)ox * xpixel;
|
||||
y0 = (uint64_t)oy * ypixel;
|
||||
x1 = ((uint64_t)ox + cells_x) * xpixel;
|
||||
y1 = ((uint64_t)oy + cells_y) * ypixel;
|
||||
if (x1 > content_width)
|
||||
x1 = content_width;
|
||||
if (y1 > content_height)
|
||||
y1 = content_height;
|
||||
if (x1 <= x0 || y1 <= y0)
|
||||
return (NULL);
|
||||
sx = x1 - x0;
|
||||
sy = y1 - y0;
|
||||
if (sx == 0 || sy == 0 || sx > SIXEL_WIDTH_LIMIT ||
|
||||
sy > SIXEL_HEIGHT_LIMIT)
|
||||
return (NULL);
|
||||
|
||||
45
image.c
45
image.c
@@ -132,14 +132,19 @@ image_sample(struct image *im, uint64_t sample_x, uint64_t sample_y,
|
||||
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;
|
||||
x0 = sample_x * im->canvas_width / sample_columns;
|
||||
x1 = ((sample_x + 1) * im->canvas_width + sample_columns - 1) /
|
||||
sample_columns;
|
||||
y0 = sample_y * im->canvas_height / sample_rows;
|
||||
y1 = ((sample_y + 1) * im->canvas_height + sample_rows - 1) /
|
||||
sample_rows;
|
||||
if (x1 <= x0)
|
||||
x1 = x0 + 1;
|
||||
if (y1 <= y0)
|
||||
y1 = y0 + 1;
|
||||
count = (uint64_t)(x1 - x0) * (y1 - y0);
|
||||
if (x0 >= im->width || y0 >= im->height)
|
||||
return;
|
||||
if (x1 > im->width)
|
||||
x1 = im->width;
|
||||
if (y1 > im->height)
|
||||
@@ -155,7 +160,6 @@ image_sample(struct image *im, uint64_t sample_x, uint64_t sample_y,
|
||||
brightness += ((2126ULL * pixel[0] +
|
||||
7152ULL * pixel[1] + 722ULL * pixel[2]) / 10000) *
|
||||
pixel[3] / 255;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count == 0)
|
||||
@@ -206,11 +210,13 @@ image_find(u_int id)
|
||||
}
|
||||
|
||||
struct image *
|
||||
image_create(u_int width, u_int height, u_int sx, u_int sy, u_char *pixels)
|
||||
image_create(u_int width, u_int height, u_int canvas_width,
|
||||
u_int canvas_height, u_int sx, u_int sy, u_char *pixels)
|
||||
{
|
||||
struct image *im;
|
||||
|
||||
if (width == 0 || height == 0 || sx == 0 || sy == 0 || pixels == NULL)
|
||||
if (width == 0 || height == 0 || canvas_width < width ||
|
||||
canvas_height < height || sx == 0 || sy == 0 || pixels == NULL)
|
||||
return (NULL);
|
||||
if ((uint64_t)width * height * 4 > SIZE_MAX)
|
||||
return (NULL);
|
||||
@@ -227,6 +233,8 @@ image_create(u_int width, u_int height, u_int sx, u_int sy, u_char *pixels)
|
||||
im->references = 1;
|
||||
im->width = width;
|
||||
im->height = height;
|
||||
im->canvas_width = canvas_width;
|
||||
im->canvas_height = canvas_height;
|
||||
im->sx = sx;
|
||||
im->sy = sy;
|
||||
im->stride = (size_t)width * 4;
|
||||
@@ -234,8 +242,9 @@ image_create(u_int width, u_int height, u_int sx, u_int sy, u_char *pixels)
|
||||
im->pixels = pixels;
|
||||
|
||||
RB_INSERT(images, &images, im);
|
||||
log_debug("%s: image %u is %ux%u pixels, %ux%u cells", __func__,
|
||||
im->id, width, height, sx, sy);
|
||||
log_debug("%s: image %u is %ux%u pixels on %ux%u canvas, "
|
||||
"%ux%u cells", __func__, im->id, width, height, canvas_width,
|
||||
canvas_height, sx, sy);
|
||||
return (im);
|
||||
}
|
||||
|
||||
@@ -305,14 +314,16 @@ image_get_pixel_rectangle(const struct image *im, u_int x, u_int y,
|
||||
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;
|
||||
*px = (uint64_t)x * im->canvas_width / im->sx;
|
||||
*py = (uint64_t)y * im->canvas_height / im->sy;
|
||||
x1 = ((uint64_t)(x + width) * im->canvas_width + im->sx - 1) /
|
||||
im->sx;
|
||||
y1 = ((uint64_t)(y + height) * im->canvas_height + im->sy - 1) /
|
||||
im->sy;
|
||||
if (*px >= im->width || *py >= im->height) {
|
||||
*px = *py = 0;
|
||||
return;
|
||||
}
|
||||
if (x1 <= *px)
|
||||
x1 = *px + 1;
|
||||
if (y1 <= *py)
|
||||
|
||||
@@ -69,10 +69,12 @@ $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.
|
||||
# A 26-pixel raster occupies two default 16-pixel cells, but the final cell
|
||||
# remains only partially filled instead of stretching the raster to 32 pixels.
|
||||
# A client without an image feature exposes this in the sampled text backend.
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 new-session -d -x 10 -y 4 "
|
||||
printf '\033_Ga=T,q=2,f=32,s=2,v=2,c=2,r=2;/wAA/wD/AP8AAP///////w==\033\\'
|
||||
printf '\033Pq\"1;1;26;26#0;2;100;100;100#0!26~-!26~-!26~-!26~-!26B\033\\'
|
||||
sleep 10" || exit 1
|
||||
$TMUX2 set -g status off || exit 1
|
||||
$TMUX new-session -d -x 10 -y 4 || exit 1
|
||||
@@ -80,9 +82,8 @@ $TMUX set -g status off || exit 1
|
||||
$TMUX send-keys -l "$TMUX2 attach-session" || exit 1
|
||||
$TMUX send-keys Enter || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E1 >$TMP || exit 1
|
||||
[ "$(sed -n 1p $TMP)" = ".*" ] || exit 1
|
||||
[ "$(sed -n 2p $TMP)" = " @" ] || exit 1
|
||||
$TMUX capture-pane -pS0 -E0 >$TMP || exit 1
|
||||
grep -q '^#=' $TMP || exit 1
|
||||
|
||||
# A selection redraw uses the single-cell path. It must leave ASCII image
|
||||
# cells visible (graphical clients skip these cells to avoid erasing pixels).
|
||||
@@ -92,8 +93,7 @@ $TMUX2 send-keys -X start-of-line || exit 1
|
||||
$TMUX2 send-keys -X begin-selection || exit 1
|
||||
$TMUX2 send-keys -X cursor-right || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E1 >$TMP || exit 1
|
||||
sed -n 1p $TMP | grep -q '^\.\*' || exit 1
|
||||
sed -n 2p $TMP | grep -q '^ @' || exit 1
|
||||
$TMUX capture-pane -pS0 -E0 >$TMP || exit 1
|
||||
grep -q '^#=' $TMP || exit 1
|
||||
|
||||
exit 0
|
||||
|
||||
5
tmux.h
5
tmux.h
@@ -1061,6 +1061,8 @@ struct image {
|
||||
u_int references;
|
||||
u_int width;
|
||||
u_int height;
|
||||
u_int canvas_width;
|
||||
u_int canvas_height;
|
||||
u_int sx;
|
||||
u_int sy;
|
||||
size_t stride;
|
||||
@@ -4255,7 +4257,8 @@ char *regsub(const char *, const char *, const char *, int);
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* image.c */
|
||||
struct image *image_create(u_int, u_int, u_int, u_int, u_char *);
|
||||
struct image *image_create(u_int, u_int, 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);
|
||||
|
||||
Reference in New Issue
Block a user