render: underlines: improve the appearance of the 'dotted' style

Try to make the 'dotted' style appear more even, and less like each
cell is rendered separately (even though they are).

Algorithm:

Each dot is a square; it's sides are that of the font's line
thickness.

The spacing (gaps) between the dots is initially the same width as the
dots themselves.

This means the number of dots per cell is the cell width divided by
the dots' length/width, divided by two.

At this point, there may be "left-over" pixels.I.e. the widths of the
dots and the gaps between them may not add up to the width of the
cell.

These pixels are evenly (as possible) across the gaps.

There are still visual inaccuracies at small font sizes. This is
impossible to fix without changing the way underlines are rendered, to
render an entire line in one go. This is not something we want to do,
since it'll make styled underlines, for a specific cell/character,
look differently, depending on the surrounding context.
This commit is contained in:
Daniel Eklöf 2024-06-27 18:36:17 +02:00
parent 0d3f2f27e3
commit 19bf558e6c
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F

View file

@ -432,17 +432,43 @@ draw_styled_underline(const struct terminal *term, pixman_image_t *pix,
PIXMAN_OP_SRC, pix, color, 2, rects);
break;
}
case CURLY_DOTTED: {
const int ceil_w = cols * term->cell_width;
const int nrects = min(ceil_w / thickness / 2, 16);
pixman_rectangle16_t rects[16] = {0};
for (int i = 0; i < nrects; i++) {
case CURLY_DOTTED: {
/* Number of dots per cell */
int per_cell = (term->cell_width / thickness) / 2;
if (per_cell == 0)
per_cell = 1;
xassert(per_cell >= 1);
/* Spacing between dots; start with the same width as the dots
themselves, then widen them if necessary, to consume unused
pixels */
int spacing[per_cell];
for (int i = 0; i < per_cell; i++)
spacing[i] = thickness;
/* Pixels remaining at the end of the cell */
int remaining = term->cell_width - (per_cell * 2) * thickness;
/* Spread out the left-over pixels across the spacing between
the dots */
for (int i = 0; remaining > 0; i = (i + 1) % per_cell, remaining--)
spacing[i]++;
xassert(remaining <= 0);
pixman_rectangle16_t rects[per_cell];
int dot_x = x;
for (int i = 0; i < per_cell; i++) {
rects[i] = (pixman_rectangle16_t){
x + i * thickness * 2, y + y_ofs, thickness, thickness};
dot_x, y + y_ofs, thickness, thickness
};
dot_x += thickness + spacing[i];
}
pixman_image_fill_rectangles(PIXMAN_OP_SRC, pix, color, nrects, rects);
pixman_image_fill_rectangles(PIXMAN_OP_SRC, pix, color, per_cell, rects);
break;
}