diff options
| author | user <user@clank> | 2026-08-14 23:25:46 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-14 23:25:46 +0200 |
| commit | aa01b72770d83091a53a43722ecc9a527bc18931 (patch) | |
| tree | 04a35b6ba2566a154cc344ebc0e031941cbce873 /src/graphics.c | |
| parent | sl0ppty: carry the terminal's cell size, so images can have a size (diff) | |
| download | slosh-aa01b72770d83091a53a43722ecc9a527bc18931.tar.gz slosh-aa01b72770d83091a53a43722ecc9a527bc18931.tar.xz slosh-aa01b72770d83091a53a43722ecc9a527bc18931.zip | |
sl0ppty: re-emit what the program meant, not what it covers
Images rendered after the last commit, and were still wrong twice. Both
mistakes are invisible in a still picture, which is what every test was
looking at, and obvious the moment anything moves.
**The sub-cell offset was dropped.** A placement says where the image starts
*inside* its first cell (`X=`/`Y=`); that is what makes motion smooth rather
than a slideshow. We tracked the placement and re-emitted it without those,
so a bouncing logo moved in steps of a whole cell -- eight pixels across,
sixteen down.
**`c=`/`r=` were passed on unconditionally.** They mean *scale the image into
this many cells*, and the count a natural-size image happens to cover goes up
by one whenever it straddles one more boundary -- so the picture changed size
as it slid along. They are now sent only when the program asked for them,
which lib-vt reports as 0 when it did not.
Cropping splits the same way, and getting it wrong took a test failure to
notice: at natural size a lost cell costs the screen pixels the pane has left
(measured from the offset, not the cell edge, or an offset image hangs up to
a cell over the border); when the program asked to scale, a lost cell is
worth source/cells pixels and the crop is proportional. Cropping a 4-pixel
image scaled across 40 cells by screen pixels removes all of it.
gfx_place() took twenty positional arguments, half of them uint32_t pixel
quantities where a transposed pair compiles perfectly and lands the picture
slightly wrong -- the exact bug class this file exists for. It takes a struct
now.
`{"cmd":"graphics","format":"bytes"}` answers with the escape sequences the
client is actually sent. The model being right is not the same as the bytes
being right, and this whole episode was a lesson in asserting on the first
while the second was wrong. The new tests assert on the bytes.
Diffstat (limited to 'src/graphics.c')
| -rw-r--r-- | src/graphics.c | 49 |
1 files changed, 32 insertions, 17 deletions
diff --git a/src/graphics.c b/src/graphics.c index bf9aa1d..d3fc07c 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -127,11 +127,10 @@ static void transmit(graphics_t *g, gfx_image_t *img, uint32_t px_w, img->sent = true; } -void gfx_place(graphics_t *g, uint32_t pane, uint32_t src_id, uint64_t gen, - uint32_t place_id, uint16_t col, uint16_t row, uint16_t cols, - uint16_t rows, uint32_t sx, uint32_t sy, uint32_t sw, - uint32_t sh, uint32_t px_w, uint32_t px_h, int format, - int compression, const uint8_t *data, size_t data_len) { +void gfx_place(graphics_t *g, const gfx_req_t *req) { + if (!g || !req) return; + const uint32_t pane = req->pane, src_id = req->src_id; + const uint64_t gen = req->gen; gfx_image_t *img = img_find(g, pane, src_id); if (!img) { if (g->nimgs == g->imgcap) { @@ -147,9 +146,10 @@ void gfx_place(graphics_t *g, uint32_t pane, uint32_t src_id, uint64_t gen, img->sent = false; } if (!img->sent) - transmit(g, img, px_w, px_h, format, compression, data, data_len); + transmit(g, img, req->px_w, req->px_h, req->format, req->compression, + req->data, req->data_len); - uint32_t pid = place_id ? place_id : 1; + uint32_t pid = req->place_id ? req->place_id : 1; gfx_place_t *slot = NULL; for (size_t i = 0; i < g->nplaces; i++) if (g->places[i].out_id == img->out_id && g->places[i].place_id == pid) @@ -164,14 +164,18 @@ void gfx_place(graphics_t *g, uint32_t pane, uint32_t src_id, uint64_t gen, slot->out_id = img->out_id; slot->place_id = pid; } - slot->col = col; - slot->row = row; - slot->cols = cols; - slot->rows = rows; - slot->sx = sx; - slot->sy = sy; - slot->sw = sw; - slot->sh = sh; + slot->col = req->col; + slot->row = req->row; + slot->cols = req->cols; + slot->rows = req->rows; + slot->x_off = req->x_off; + slot->y_off = req->y_off; + slot->scale_cols = req->scale_cols; + slot->scale_rows = req->scale_rows; + slot->sx = req->sx; + slot->sy = req->sy; + slot->sw = req->sw; + slot->sh = req->sh; slot->live = true; } @@ -194,8 +198,19 @@ char *gfx_flush(graphics_t *g, size_t *out_len) { for (size_t i = 0; i < g->nplaces; i++) { gfx_place_t *p = &g->places[i]; out_fmt(g, "\x1b[%u;%uH", p->row + 1, p->col + 1); - out_fmt(g, "\x1b_Ga=p,q=2,C=1,i=%u,p=%u,c=%u,r=%u", p->out_id, p->place_id, - p->cols, p->rows); + out_fmt(g, "\x1b_Ga=p,q=2,C=1,i=%u,p=%u", p->out_id, p->place_id); + /* c=/r= mean *scale into this many cells*, so they are passed on only + * when the program asked for them. Sending the cell count a natural-size + * image happens to cover looks identical in a still picture and makes a + * moving one change size, because that count goes up by one whenever the + * image straddles one more cell boundary. */ + if (p->scale_cols || p->scale_rows) + out_fmt(g, ",c=%u,r=%u", p->scale_cols, p->scale_rows); + /* Where the image sits inside its first cell. Without these an image can + * only ever land on a cell boundary, so anything moving smoothly arrives + * in steps of a whole cell -- eight pixels across, sixteen down. */ + if (p->x_off) out_fmt(g, ",X=%u", p->x_off); + if (p->y_off) out_fmt(g, ",Y=%u", p->y_off); /* The source rectangle is what actually crops; c/r alone would scale. */ if (p->sw || p->sh) out_fmt(g, ",x=%u,y=%u,w=%u,h=%u", p->sx, p->sy, p->sw, p->sh); |
