sixel: re-scale images when the cell dimensions change

Before this patch, when the cell dimensions changed (i.e. when the
font size changes), sixel images were either removed (the new cell
dimensions are smaller than the old), or simply kept at their original
size (new cell dimensions are larger).

With this patch, sixels are instead resized. This means a
sixel *always* occupies the same number of rows and columns,
regardless of how much the font size is changed.

This is done by maintaining two sets of image data and pixman images,
as well as their dimensions. These two sets are the new ‘original’ and
‘scaled’ members of the sixel struct.

The "top-level" pixman image pointer, and the ‘width’ and ‘height’
members either point to the "original", or the "scaled" version.

They are invalidated as soon as the cell dimensions change. They, and
the ‘scaled’ image is updated on-demand (when we need to render a
sixel).

Note that the ‘scaled’ image is always NULL when the current cell
dimensions matches the ones used when emitting the sixel (to save
run-time memory).

Closes #1383
This commit is contained in:
Daniel Eklöf 2023-06-29 14:49:54 +02:00
parent 7a37e6891f
commit 49fb0cf359
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
7 changed files with 295 additions and 112 deletions

View file

@ -80,12 +80,14 @@
is explicitly added. is explicitly added.
* Default sixel aspect ratio is now 2:1 instead of 1:1. * Default sixel aspect ratio is now 2:1 instead of 1:1.
* Sixel images are no longer cropped to the last non-transparent row. * Sixel images are no longer cropped to the last non-transparent row.
* Sixel images are now re-scaled when the font size is changed
([#1383][1383]).
* `dpi-aware` now defaults to `no`, and the `auto` value has been * `dpi-aware` now defaults to `no`, and the `auto` value has been
removed. removed.
[1371]: https://codeberg.org/dnkl/foot/pulls/1371 [1371]: https://codeberg.org/dnkl/foot/pulls/1371
[1360]: https://codeberg.org/dnkl/foot/issues/1360 [1360]: https://codeberg.org/dnkl/foot/issues/1360
[1383]: https://codeberg.org/dnkl/foot/issues/1383
### Deprecated ### Deprecated

69
grid.c
View file

@ -255,27 +255,68 @@ grid_snapshot(const struct grid *grid)
} }
tll_foreach(grid->sixel_images, it) { tll_foreach(grid->sixel_images, it) {
int width = it->item.width; int original_width = it->item.original.width;
int height = it->item.height; int original_height = it->item.original.height;
pixman_image_t *pix = it->item.pix; pixman_image_t *original_pix = it->item.original.pix;
pixman_format_code_t pix_fmt = pixman_image_get_format(pix); pixman_format_code_t original_pix_fmt = pixman_image_get_format(original_pix);
int stride = stride_for_format_and_width(pix_fmt, width); int original_stride = stride_for_format_and_width(original_pix_fmt, original_width);
size_t size = stride * height; size_t original_size = original_stride * original_height;
void *new_data = xmalloc(size); void *new_original_data = xmalloc(original_size);
memcpy(new_data, it->item.data, size); memcpy(new_original_data, it->item.original.data, original_size);
pixman_image_t *new_pix = pixman_image_create_bits_no_clear( pixman_image_t *new_original_pix = pixman_image_create_bits_no_clear(
pix_fmt, width, height, new_data, stride); original_pix_fmt, original_width, original_height,
new_original_data, original_stride);
void *new_scaled_data = NULL;
pixman_image_t *new_scaled_pix = NULL;
int scaled_width = -1;
int scaled_height = -1;
if (it->item.scaled.data != NULL) {
scaled_width = it->item.scaled.width;
scaled_height = it->item.scaled.height;
pixman_image_t *scaled_pix = it->item.scaled.pix;
pixman_format_code_t scaled_pix_fmt = pixman_image_get_format(scaled_pix);
int scaled_stride = stride_for_format_and_width(scaled_pix_fmt, scaled_width);
size_t scaled_size = scaled_stride * scaled_height;
new_scaled_data = xmalloc(scaled_size);
memcpy(new_scaled_data, it->item.scaled.data, scaled_size);
new_scaled_pix = pixman_image_create_bits_no_clear(
scaled_pix_fmt, scaled_width, scaled_height, new_scaled_data,
scaled_stride);
}
struct sixel six = { struct sixel six = {
.data = new_data, .pix = (it->item.pix == it->item.original.pix
.pix = new_pix, ? new_original_pix
.width = width, : (it->item.pix == it->item.scaled.pix
.height = height, ? new_scaled_pix
: NULL)),
.width = it->item.width,
.height = it->item.height,
.rows = it->item.rows, .rows = it->item.rows,
.cols = it->item.cols, .cols = it->item.cols,
.pos = it->item.pos, .pos = it->item.pos,
.opaque = it->item.opaque,
.cell_width = it->item.cell_width,
.cell_height = it->item.cell_height,
.original = {
.data = new_original_data,
.pix = new_original_pix,
.width = original_width,
.height = original_height,
},
.scaled = {
.data = new_scaled_data,
.pix = new_scaled_pix,
.width = scaled_width,
.height = scaled_height,
},
}; };
tll_push_back(clone->sixel_images, six); tll_push_back(clone->sixel_images, six);

View file

@ -1160,6 +1160,10 @@ static void
render_sixel(struct terminal *term, pixman_image_t *pix, render_sixel(struct terminal *term, pixman_image_t *pix,
const struct coord *cursor, const struct sixel *sixel) const struct coord *cursor, const struct sixel *sixel)
{ {
xassert(sixel->pix != NULL);
xassert(sixel->width >= 0);
xassert(sixel->height >= 0);
const int view_end = (term->grid->view + term->rows - 1) & (term->grid->num_rows - 1); const int view_end = (term->grid->view + term->rows - 1) & (term->grid->num_rows - 1);
const bool last_row_needs_erase = sixel->height % term->cell_height != 0; const bool last_row_needs_erase = sixel->height % term->cell_height != 0;
const bool last_col_needs_erase = sixel->width % term->cell_width != 0; const bool last_col_needs_erase = sixel->width % term->cell_width != 0;
@ -1324,6 +1328,7 @@ render_sixel_images(struct terminal *term, pixman_image_t *pix,
break; break;
} }
sixel_sync_cache(term, &it->item);
render_sixel(term, pix, cursor, &it->item); render_sixel(term, pix, cursor, &it->item);
} }
} }

260
sixel.c
View file

@ -125,15 +125,34 @@ sixel_init(struct terminal *term, int p1, int p2, int p3)
return pan == 1 && pad == 1 ? &sixel_put_ar_11 : &sixel_put_generic; return pan == 1 && pad == 1 ? &sixel_put_ar_11 : &sixel_put_generic;
} }
static void
sixel_invalidate_cache(struct sixel *sixel)
{
if (sixel->scaled.pix != NULL)
pixman_image_unref(sixel->scaled.pix);
free(sixel->scaled.data);
sixel->scaled.pix = NULL;
sixel->scaled.data = NULL;
sixel->scaled.width = -1;
sixel->scaled.height = -1;
sixel->pix = NULL;
sixel->width = -1;
sixel->height = -1;
}
void void
sixel_destroy(struct sixel *sixel) sixel_destroy(struct sixel *sixel)
{ {
if (sixel->pix != NULL) sixel_invalidate_cache(sixel);
pixman_image_unref(sixel->pix);
free(sixel->data); if (sixel->original.pix != NULL)
sixel->pix = NULL; pixman_image_unref(sixel->original.pix);
sixel->data = NULL;
free(sixel->original.data);
sixel->original.pix = NULL;
sixel->original.data = NULL;
} }
void void
@ -396,10 +415,14 @@ blend_new_image_over_old(const struct terminal *term,
xassert(pix != NULL); xassert(pix != NULL);
xassert(opaque != NULL); xassert(opaque != NULL);
const int six_ofs_x = six->pos.col * term->cell_width; /*
const int six_ofs_y = six->pos.row * term->cell_height; * TODO: handle images being emitted with different cell dimensions
const int img_ofs_x = col * term->cell_width; */
const int img_ofs_y = row * term->cell_height;
const int six_ofs_x = six->pos.col * six->cell_width;
const int six_ofs_y = six->pos.row * six->cell_height;
const int img_ofs_x = col * six->cell_width;
const int img_ofs_y = row * six->cell_height;
const int img_width = pixman_image_get_width(*pix); const int img_width = pixman_image_get_width(*pix);
const int img_height = pixman_image_get_height(*pix); const int img_height = pixman_image_get_height(*pix);
@ -429,7 +452,7 @@ blend_new_image_over_old(const struct terminal *term,
*/ */
pixman_image_composite32( pixman_image_composite32(
PIXMAN_OP_OVER_REVERSE, PIXMAN_OP_OVER_REVERSE,
six->pix, NULL, *pix, six->original.pix, NULL, *pix,
box->x1 - six_ofs_x, box->y1 - six_ofs_y, box->x1 - six_ofs_x, box->y1 - six_ofs_y,
0, 0, 0, 0,
box->x1 - img_ofs_x, box->y1 - img_ofs_y, box->x1 - img_ofs_x, box->y1 - img_ofs_y,
@ -446,15 +469,15 @@ blend_new_image_over_old(const struct terminal *term,
* old image, or the next cell boundary, whichever comes * old image, or the next cell boundary, whichever comes
* first. * first.
*/ */
int bounding_x = six_ofs_x + six->width > img_ofs_x + img_width int bounding_x = six_ofs_x + six->original.width > img_ofs_x + img_width
? min( ? min(
six_ofs_x + six->width, six_ofs_x + six->original.width,
(box->x2 + term->cell_width - 1) / term->cell_width * term->cell_width) (box->x2 + six->cell_width - 1) / six->cell_width * six->cell_width)
: box->x2; : box->x2;
int bounding_y = six_ofs_y + six->height > img_ofs_y + img_height int bounding_y = six_ofs_y + six->original.height > img_ofs_y + img_height
? min( ? min(
six_ofs_y + six->height, six_ofs_y + six->original.height,
(box->y2 + term->cell_height - 1) / term->cell_height * term->cell_height) (box->y2 + six->cell_height - 1) / six->cell_height * six->cell_height)
: box->y2; : box->y2;
/* The required size of the new image */ /* The required size of the new image */
@ -494,7 +517,7 @@ blend_new_image_over_old(const struct terminal *term,
/* Copy the bottom tile of the old sixel image into the new pixmap */ /* Copy the bottom tile of the old sixel image into the new pixmap */
pixman_image_composite32( pixman_image_composite32(
PIXMAN_OP_SRC, PIXMAN_OP_SRC,
six->pix, NULL, pix2, six->original.pix, NULL, pix2,
box->x1 - six_ofs_x, box->y2 - six_ofs_y, box->x1 - six_ofs_x, box->y2 - six_ofs_y,
0, 0, 0, 0,
box->x1 - img_ofs_x, box->y2 - img_ofs_y, box->x1 - img_ofs_x, box->y2 - img_ofs_y,
@ -503,7 +526,7 @@ blend_new_image_over_old(const struct terminal *term,
/* Copy the right tile of the old sixel image into the new pixmap */ /* Copy the right tile of the old sixel image into the new pixmap */
pixman_image_composite32( pixman_image_composite32(
PIXMAN_OP_SRC, PIXMAN_OP_SRC,
six->pix, NULL, pix2, six->original.pix, NULL, pix2,
box->x2 - six_ofs_x, box->y1 - six_ofs_y, box->x2 - six_ofs_x, box->y1 - six_ofs_y,
0, 0, 0, 0,
box->x2 - img_ofs_x, box->y1 - img_ofs_y, box->x2 - img_ofs_x, box->y1 - img_ofs_y,
@ -577,14 +600,14 @@ sixel_overwrite(struct terminal *term, struct sixel *six,
pixman_region32_t six_rect; pixman_region32_t six_rect;
pixman_region32_init_rect( pixman_region32_init_rect(
&six_rect, &six_rect,
six->pos.col * term->cell_width, six->pos.row * term->cell_height, six->pos.col * six->cell_width, six->pos.row * six->cell_height,
six->width, six->height); six->original.width, six->original.height);
pixman_region32_t overwrite_rect; pixman_region32_t overwrite_rect;
pixman_region32_init_rect( pixman_region32_init_rect(
&overwrite_rect, &overwrite_rect,
col * term->cell_width, row * term->cell_height, col * six->cell_width, row * six->cell_height,
width * term->cell_width, height * term->cell_height); width * six->cell_width, height * six->cell_height);
#if defined(_DEBUG) #if defined(_DEBUG)
pixman_region32_t cell_intersection; pixman_region32_t cell_intersection;
@ -597,7 +620,6 @@ sixel_overwrite(struct terminal *term, struct sixel *six,
if (pix != NULL) if (pix != NULL)
blend_new_image_over_old(term, six, &six_rect, row, col, pix, opaque); blend_new_image_over_old(term, six, &six_rect, row, col, pix, opaque);
pixman_region32_t diff; pixman_region32_t diff;
pixman_region32_init(&diff); pixman_region32_init(&diff);
pixman_region32_subtract(&diff, &six_rect, &overwrite_rect); pixman_region32_subtract(&diff, &six_rect, &overwrite_rect);
@ -612,12 +634,12 @@ sixel_overwrite(struct terminal *term, struct sixel *six,
LOG_DBG("box #%d: x1=%d, y1=%d, x2=%d, y2=%d", i, LOG_DBG("box #%d: x1=%d, y1=%d, x2=%d, y2=%d", i,
boxes[i].x1, boxes[i].y1, boxes[i].x2, boxes[i].y2); boxes[i].x1, boxes[i].y1, boxes[i].x2, boxes[i].y2);
xassert(boxes[i].x1 % term->cell_width == 0); xassert(boxes[i].x1 % six->cell_width == 0);
xassert(boxes[i].y1 % term->cell_height == 0); xassert(boxes[i].y1 % six->cell_height == 0);
/* New image's position, in cells */ /* New image's position, in cells */
const int new_col = boxes[i].x1 / term->cell_width; const int new_col = boxes[i].x1 / six->cell_width;
const int new_row = boxes[i].y1 / term->cell_height; const int new_row = boxes[i].y1 / six->cell_height;
xassert(new_row < term->grid->num_rows); xassert(new_row < term->grid->num_rows);
@ -626,17 +648,17 @@ sixel_overwrite(struct terminal *term, struct sixel *six,
const int new_height = boxes[i].y2 - boxes[i].y1; const int new_height = boxes[i].y2 - boxes[i].y1;
uint32_t *new_data = xmalloc(new_width * new_height * sizeof(uint32_t)); uint32_t *new_data = xmalloc(new_width * new_height * sizeof(uint32_t));
const uint32_t *old_data = six->data; const uint32_t *old_data = six->original.data;
/* Pixel offsets into old image backing memory */ /* Pixel offsets into old image backing memory */
const int x_ofs = boxes[i].x1 - six->pos.col * term->cell_width; const int x_ofs = boxes[i].x1 - six->pos.col * six->cell_width;
const int y_ofs = boxes[i].y1 - six->pos.row * term->cell_height; const int y_ofs = boxes[i].y1 - six->pos.row * six->cell_height;
/* Copy image data, one row at a time */ /* Copy image data, one row at a time */
for (size_t j = 0; j < new_height; j++) { for (size_t j = 0; j < new_height; j++) {
memcpy( memcpy(
&new_data[(0 + j) * new_width], &new_data[(0 + j) * new_width],
&old_data[(y_ofs + j) * six->width + x_ofs], &old_data[(y_ofs + j) * six->original.width + x_ofs],
new_width * sizeof(uint32_t)); new_width * sizeof(uint32_t));
} }
@ -645,14 +667,27 @@ sixel_overwrite(struct terminal *term, struct sixel *six,
new_width, new_height, new_data, new_width * sizeof(uint32_t)); new_width, new_height, new_data, new_width * sizeof(uint32_t));
struct sixel new_six = { struct sixel new_six = {
.pix = NULL,
.width = -1,
.height = -1,
.pos = {.col = new_col, .row = new_row},
.cols = (new_width + six->cell_width - 1) / six->cell_width,
.rows = (new_height + six->cell_height - 1) / six->cell_height,
.opaque = six->opaque,
.cell_width = six->cell_width,
.cell_height = six->cell_height,
.original = {
.data = new_data, .data = new_data,
.pix = new_pix, .pix = new_pix,
.width = new_width, .width = new_width,
.height = new_height, .height = new_height,
.pos = {.col = new_col, .row = new_row}, },
.cols = (new_width + term->cell_width - 1) / term->cell_width, .scaled = {
.rows = (new_height + term->cell_height - 1) / term->cell_height, .data = NULL,
.opaque = six->opaque, .pix = NULL,
.width = -1,
.height = -1,
},
}; };
#if defined(_DEBUG) #if defined(_DEBUG)
@ -847,23 +882,94 @@ sixel_overwrite_at_cursor(struct terminal *term, int width)
void void
sixel_cell_size_changed(struct terminal *term) sixel_cell_size_changed(struct terminal *term)
{ {
struct grid *g = term->grid; tll_foreach(term->normal.sixel_images, it)
sixel_invalidate_cache(&it->item);
term->grid = &term->normal; tll_foreach(term->alt.sixel_images, it)
tll_foreach(term->normal.sixel_images, it) { sixel_invalidate_cache(&it->item);
struct sixel *six = &it->item;
six->rows = (six->height + term->cell_height - 1) / term->cell_height;
six->cols = (six->width + term->cell_width - 1) / term->cell_width;
} }
term->grid = &term->alt; void
tll_foreach(term->alt.sixel_images, it) { sixel_sync_cache(const struct terminal *term, struct sixel *six)
struct sixel *six = &it->item; {
six->rows = (six->height + term->cell_height - 1) / term->cell_height; if (six->pix != NULL) {
six->cols = (six->width + term->cell_width - 1) / term->cell_width; #if defined(_DEBUG)
if (six->cell_width == term->cell_width &&
six->cell_height == term->cell_height)
{
xassert(six->pix == six->original.pix);
xassert(six->width == six->original.width);
xassert(six->height == six->original.height);
xassert(six->scaled.data == NULL);
xassert(six->scaled.pix == NULL);
xassert(six->scaled.width < 0);
xassert(six->scaled.height < 0);
} else {
xassert(six->pix == six->scaled.pix);
xassert(six->width == six->scaled.width);
xassert(six->height == six->scaled.height);
xassert(six->scaled.data != NULL);
xassert(six->scaled.pix != NULL);
/* TODO: check ratio */
xassert(six->scaled.width >= 0);
xassert(six->scaled.height >= 0);
}
#endif
return;
} }
term->grid = g; /* Cache should be invalid */
xassert(six->scaled.data == NULL);
xassert(six->scaled.pix == NULL);
xassert(six->scaled.width < 0);
xassert(six->scaled.height < 0);
if (six->cell_width == term->cell_width &&
six->cell_height == term->cell_height)
{
six->pix = six->original.pix;
six->width = six->original.width;
six->height = six->original.height;
} else {
const double width_ratio = (double)term->cell_width / six->cell_width;
const double height_ratio = (double)term->cell_height / six->cell_height;
struct pixman_f_transform scale;
pixman_f_transform_init_scale(
&scale, 1. / width_ratio, 1. / height_ratio);
struct pixman_transform _scale;
pixman_transform_from_pixman_f_transform(&_scale, &scale);
pixman_image_set_transform(six->original.pix, &_scale);
pixman_image_set_filter(six->original.pix, PIXMAN_FILTER_BILINEAR, NULL, 0);
int scaled_width = (double)six->original.width * width_ratio;
int scaled_height = (double)six->original.height * height_ratio;
int scaled_stride = scaled_width * sizeof(uint32_t);
LOG_DBG("scaling sixel: %dx%d -> %dx%d",
six->original.width, six->original.height,
scaled_width, scaled_height);
uint8_t *scaled_data = xmalloc(scaled_height * scaled_stride);
pixman_image_t *scaled_pix = pixman_image_create_bits_no_clear(
PIXMAN_a8r8g8b8, scaled_width, scaled_height,
(uint32_t *)scaled_data, scaled_stride);
pixman_image_composite32(
PIXMAN_OP_SRC, six->original.pix, NULL, scaled_pix, 0, 0, 0, 0,
0, 0, scaled_width, scaled_height);
pixman_image_set_transform(six->original.pix, NULL);
six->scaled.data = scaled_data;
six->scaled.pix = six->pix = scaled_pix;
six->scaled.width = six->width = scaled_width;
six->scaled.height = six->height = scaled_height;
}
} }
void void
@ -926,14 +1032,15 @@ sixel_reflow_grid(struct terminal *term, struct grid *grid)
* allowed of course */ * allowed of course */
_sixel_overwrite_by_rectangle( _sixel_overwrite_by_rectangle(
term, six->pos.row, six->pos.col, six->rows, six->cols, term, six->pos.row, six->pos.col, six->rows, six->cols,
&it->item.pix, &it->item.opaque); &it->item.original.pix, &it->item.opaque);
if (it->item.data != pixman_image_get_data(it->item.pix)) { if (it->item.original.data != pixman_image_get_data(it->item.original.pix)) {
it->item.data = pixman_image_get_data(it->item.pix); it->item.original.data = pixman_image_get_data(it->item.original.pix);
it->item.width = pixman_image_get_width(it->item.pix); it->item.original.width = pixman_image_get_width(it->item.original.pix);
it->item.height = pixman_image_get_height(it->item.pix); it->item.original.height = pixman_image_get_height(it->item.original.pix);
it->item.cols = (it->item.width + term->cell_width - 1) / term->cell_width; it->item.cols = (it->item.original.width + it->item.cell_width - 1) / it->item.cell_width;
it->item.rows = (it->item.height + term->cell_height - 1) / term->cell_height; it->item.rows = (it->item.original.height + it->item.cell_height - 1) / it->item.cell_height;
sixel_invalidate_cache(&it->item);
} }
sixel_insert(term, it->item); sixel_insert(term, it->item);
@ -1027,13 +1134,27 @@ sixel_unhook(struct terminal *term)
} }
struct sixel image = { struct sixel image = {
.data = img_data, .pix = NULL,
.width = width, .width = -1,
.height = height, .height = -1,
.rows = (height + term->cell_height - 1) / term->cell_height, .rows = (height + term->cell_height - 1) / term->cell_height,
.cols = (width + term->cell_width - 1) / term->cell_width, .cols = (width + term->cell_width - 1) / term->cell_width,
.pos = (struct coord){start_col, cur_row}, .pos = (struct coord){start_col, cur_row},
.opaque = !term->sixel.transparent_bg, .opaque = !term->sixel.transparent_bg,
.cell_width = term->cell_width,
.cell_height = term->cell_height,
.original = {
.data = img_data,
.pix = NULL,
.width = width,
.height = height,
},
.scaled = {
.data = NULL,
.pix = NULL,
.width = -1,
.height = -1,
},
}; };
xassert(image.rows <= term->grid->num_rows); xassert(image.rows <= term->grid->num_rows);
@ -1044,8 +1165,9 @@ sixel_unhook(struct terminal *term)
image.width, image.height, image.width, image.height,
image.pos.row, image.pos.row + image.rows); image.pos.row, image.pos.row + image.rows);
image.pix = pixman_image_create_bits_no_clear( image.original.pix = pixman_image_create_bits_no_clear(
PIXMAN_a8r8g8b8, image.width, image.height, img_data, stride); PIXMAN_a8r8g8b8, image.original.width, image.original.height,
img_data, stride);
pixel_row_idx += height; pixel_row_idx += height;
pixel_rows_left -= height; pixel_rows_left -= height;
@ -1064,7 +1186,8 @@ sixel_unhook(struct terminal *term)
? max(0, image.rows - 1) ? max(0, image.rows - 1)
: image.rows; : image.rows;
xassert(rows_avail == 0 || image.height % term->cell_height == 0); xassert(rows_avail == 0 ||
image.original.height % term->cell_height == 0);
for (size_t i = 0; i < linefeed_count; i++) for (size_t i = 0; i < linefeed_count; i++)
term_linefeed(term); term_linefeed(term);
@ -1084,7 +1207,7 @@ sixel_unhook(struct terminal *term)
* higher up. * higher up.
*/ */
const int sixel_row_height = 6 * term->sixel.pan; const int sixel_row_height = 6 * term->sixel.pan;
const int sixel_rows = (image.height + sixel_row_height - 1) / sixel_row_height; const int sixel_rows = (image.original.height + sixel_row_height - 1) / sixel_row_height;
const int upper_pixel_last_sixel = (sixel_rows - 1) * sixel_row_height; const int upper_pixel_last_sixel = (sixel_rows - 1) * sixel_row_height;
const int term_rows = (upper_pixel_last_sixel + term->cell_height - 1) / term->cell_height; const int term_rows = (upper_pixel_last_sixel + term->cell_height - 1) / term->cell_height;
@ -1117,14 +1240,15 @@ sixel_unhook(struct terminal *term)
_sixel_overwrite_by_rectangle( _sixel_overwrite_by_rectangle(
term, image.pos.row, image.pos.col, image.rows, image.cols, term, image.pos.row, image.pos.col, image.rows, image.cols,
&image.pix, &image.opaque); &image.original.pix, &image.opaque);
if (image.data != pixman_image_get_data(image.pix)) { if (image.original.data != pixman_image_get_data(image.original.pix)) {
image.data = pixman_image_get_data(image.pix); image.original.data = pixman_image_get_data(image.original.pix);
image.width = pixman_image_get_width(image.pix); image.original.width = pixman_image_get_width(image.original.pix);
image.height = pixman_image_get_height(image.pix); image.original.height = pixman_image_get_height(image.original.pix);
image.cols = (image.width + term->cell_width - 1) / term->cell_width; image.cols = (image.original.width + image.cell_width - 1) / image.cell_width;
image.rows = (image.height + term->cell_height - 1) / term->cell_height; image.rows = (image.original.height + image.cell_height - 1) / image.cell_height;
sixel_invalidate_cache(&image);
} }
sixel_insert(term, image); sixel_insert(term, image);

View file

@ -20,6 +20,7 @@ void sixel_scroll_up(struct terminal *term, int rows);
void sixel_scroll_down(struct terminal *term, int rows); void sixel_scroll_down(struct terminal *term, int rows);
void sixel_cell_size_changed(struct terminal *term); void sixel_cell_size_changed(struct terminal *term);
void sixel_sync_cache(const struct terminal *term, struct sixel *sixel);
void sixel_reflow_grid(struct terminal *term, struct grid *grid); void sixel_reflow_grid(struct terminal *term, struct grid *grid);

View file

@ -763,9 +763,6 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4])
free_custom_glyphs( free_custom_glyphs(
&term->custom_glyphs.legacy, GLYPH_LEGACY_COUNT); &term->custom_glyphs.legacy, GLYPH_LEGACY_COUNT);
const int old_cell_width = term->cell_width;
const int old_cell_height = term->cell_height;
const struct config *conf = term->conf; const struct config *conf = term->conf;
const struct fcft_glyph *M = fcft_rasterize_char_utf32( const struct fcft_glyph *M = fcft_rasterize_char_utf32(
@ -792,28 +789,7 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4])
LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height); LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height);
if (term->cell_width < old_cell_width ||
term->cell_height < old_cell_height)
{
/*
* The cell size has decreased.
*
* This means sixels, which we cannot resize, no longer fit
* into their "allocated" grid space.
*
* To be able to fit them, we would have to change the grid
* content. Inserting empty lines _might_ seem acceptable, but
* we'd also need to insert empty columns, which would break
* existing layout completely.
*
* So we delete them.
*/
sixel_destroy_all(term);
} else if (term->cell_width != old_cell_width ||
term->cell_height != old_cell_height)
{
sixel_cell_size_changed(term); sixel_cell_size_changed(term);
}
/* Use force, since cell-width/height may have changed */ /* Use force, since cell-width/height may have changed */
render_resize_force(term, term->width / term->scale, term->height / term->scale); render_resize_force(term, term->width / term->scale, term->height / term->scale);

View file

@ -126,14 +126,48 @@ struct row {
}; };
struct sixel { struct sixel {
void *data; /*
* These three members reflect the "current", maybe scaled version
* of the image.
*
* The values will either be NULL/-1/-1, or match either the
* values in "original", or "scaled".
*
* They are typically reset when we need to invalidate the cached
* version (e.g. when the cell dimensions change).
*/
pixman_image_t *pix; pixman_image_t *pix;
int width; int width;
int height; int height;
int rows; int rows;
int cols; int cols;
struct coord pos; struct coord pos;
bool opaque; bool opaque;
/*
* We store the cell dimensions of the time the sixel was emitted.
*
* If the font size is changed, we rescale the image accordingly,
* to ensure it stays within its cell boundaries. scaled is a
* cached, rescaled version of data + pix.
*/
int cell_width;
int cell_height;
struct {
void *data;
pixman_image_t *pix;
int width;
int height;
} original;
struct {
void *data;
pixman_image_t *pix;
int width;
int height;
} scaled;
}; };
enum kitty_kbd_flags { enum kitty_kbd_flags {