From b4c759e2de429c20bf07c9e16162d154152014d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 2 Sep 2021 14:55:26 +0200 Subject: [PATCH 1/5] box-drawing: add braille characters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Render braille ourselves, instead of using font glyphs. Decoding a braille character is easy enough; there are 256 codepoints, represented by an 8-bit integer (i.e. subtract the Unicode codepoint offset, 0x2800, and you’re left with an integer in the range 0-255). Each bit corresponds to a dot. The first 6 bits represent the upper 6 dots, while the two last bits represent the fourth (and last) row of dots. The hard part is sizing the dots and the spacing between them. The aim is to have the spacing between the dots be the same size as the dots themselves, and to have the margins on each side be half the size of the dots. In a perfectly sized cell, this means two braille characters next to each other will be evenly spaced. This is however almost never the case. The layout logic currently: * Set dot size to either the width / 4, or height / 8, depending on which one is smallest. * Horizontal spacing is initialized to the width / 4 * Vertical spacing is initialized to the height / 8 * Horizontal margins are initialized to the horizontal spacing / 2 * Vertical margins are initialized to the vertical spacing / 2. Next, we calculate the number of “remaining” pixels. That is, if we add the left margin, two dots and the spacing between, how many pixels are left on the horizontal axis? These pixels are distributed in the following order (we “stop” as soon as we run out of pixels): * If the dot size is 0 (happens for very small font sizes), increase it to 1. * If the margins are 0, increase them to 1. * If we have enough pixels (need at 2 horizontal and 4 vertical), increase the dot size. * Increase spacing. * Increase margins. Closes #702 --- CHANGELOG.md | 2 ++ box-drawing.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++ render.c | 15 ++++++--- terminal.h | 7 ++-- 4 files changed, 105 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fef6c13f..0b0c2011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,8 @@ * PGO build scripts, in the `pgo` directory. See INSTALL.md - _Performance optimized, PGO_, for details (https://codeberg.org/dnkl/foot/issues/701). +* Braille characters (U+2800 - U+28FF) are now rendered by foot + itself (https://codeberg.org/dnkl/foot/issues/702). ### Changed diff --git a/box-drawing.c b/box-drawing.c index f246fd01..a1940392 100644 --- a/box-drawing.c +++ b/box-drawing.c @@ -1953,6 +1953,92 @@ draw_quadrant(struct buf *buf, wchar_t wc) quad_lower_right(buf); } +static void +draw_braille(struct buf *buf, wchar_t wc) +{ + int w = min(buf->width / 4, buf->height / 8); + int x_spacing = buf->width / 4; + int y_spacing = buf->height / 8; + int x_margin = x_spacing / 2; + int y_margin = y_spacing / 2; + + int x_pix_left = buf->width - 2 * x_margin - x_spacing - 2 * w; + int y_pix_left = buf->height - 2 * y_margin - 3 * y_spacing - 4 * w; + + LOG_DBG( + "braille: before adjusting: " + "cell: %dx%d, margin=%dx%d, spacing=%dx%d, width=%d, left=%dx%d", + buf->width, buf->height, x_margin, y_margin, x_spacing, y_spacing, + w, x_pix_left, y_pix_left); + + /* First, try hard to ensure the DOT width is non-zero */ + if (x_pix_left >= 2 && y_pix_left >= 4 && w == 0) { + w++; + x_pix_left -= 2; + y_pix_left -= 4; + } + + /* Second, prefer a non-zero margin */ + if (x_pix_left >= 2 && x_margin == 0) { x_margin = 1; x_pix_left -= 2; } + if (y_pix_left >= 2 && y_margin == 0) { y_margin = 1; y_pix_left -= 2; } + + if (x_pix_left >= 2 && y_pix_left >= 4) { + w++; + x_pix_left -= 2; + y_pix_left -= 4; + } + + if (x_pix_left >= 1) { x_spacing++; x_pix_left--; } + if (y_pix_left >= 3) { y_spacing++; y_pix_left -= 3; } + + if (x_pix_left >= 2) { x_margin++; x_pix_left -= 2; } + if (y_pix_left >= 2) { y_margin++; y_pix_left -= 2; } + + LOG_DBG( + "braille: after adjusting: " + "cell: %dx%d, margin=%dx%d, spacing=%dx%d, width=%d, left=%dx%d", + buf->width, buf->height, x_margin, y_margin, x_spacing, y_spacing, + w, x_pix_left, y_pix_left); + + xassert(x_pix_left <= 1 || y_pix_left <= 1); + xassert(2 * x_margin + 2 * w + x_spacing <= buf->width); + xassert(2 * y_margin + 4 * w + 3 * y_spacing <= buf->height); + + int x[2], y[4]; + x[0] = x_margin; + x[1] = x_margin + w + x_spacing; + y[0] = y_margin; + y[1] = y[0] + w + y_spacing; + y[2] = y[1] + w + y_spacing; + y[3] = y[2] + w + y_spacing; + + assert(wc >= 0x2800); + assert(wc <= 0x28ff); + uint8_t sym = wc - 0x2800; + + /* Left side */ + if (sym & 1) + rect(x[0], y[0], x[0] + w, y[0] + w); + if (sym & 2) + rect(x[0], y[1], x[0] + w, y[1] + w); + if (sym & 4) + rect(x[0], y[2], x[0] + w, y[2] + w); + + /* Right side */ + if (sym & 8) + rect(x[1], y[0], x[1] + w, y[0] + w); + if (sym & 16) + rect(x[1], y[1], x[1] + w, y[1] + w); + if (sym & 32) + rect(x[1], y[2], x[1] + w, y[2] + w); + + /* 8-dot patterns */ + if (sym & 64) + rect(x[0], y[3], x[0] + w, y[3] + w); + if (sym & 128) + rect(x[1], y[3], x[1] + w, y[3] + w); +} + static void sextant_upper_left(struct buf *buf) { @@ -2653,6 +2739,8 @@ draw_glyph(struct buf *buf, wchar_t wc) case 0x2595: draw_right_one_eighth_block(buf); break; case 0x2596 ... 0x259f: draw_quadrant(buf, wc); break; + case 0x2800 ... 0x28ff: draw_braille(buf, wc); break; + case 0x1fb00 ... 0x1fb3b: draw_sextant(buf, wc); break; case 0x1fb3c ... 0x1fb40: diff --git a/render.c b/render.c index cfb6f0bb..1cbff10d 100644 --- a/render.c +++ b/render.c @@ -515,6 +515,9 @@ render_cell(struct terminal *term, pixman_image_t *pix, /* Classic box drawings */ (base >= 0x2500 && base <= 0x259f) || + /* Braille */ + (base >= 0x2800 && base <= 0x28ff) || + /* * Unicode 13 "Symbols for Legacy Computing" * sub-ranges below. @@ -531,9 +534,12 @@ render_cell(struct terminal *term, pixman_image_t *pix, /* Box drawing characters */ size_t idx = base >= 0x1fb00 ? (base >= 0x1fb9a - ? base - 0x1fb9a + 300 - : base - 0x1fb00 + 160) - : base - 0x2500; + ? base - 0x1fb9a + 556 + : base - 0x1fb00 + 416) + : (base >= 0x2800 + ? base - 0x2800 + 160 + : base - 0x2500); + xassert(idx < ALEN(term->box_drawing)); if (likely(term->box_drawing[idx] != NULL)) @@ -541,7 +547,8 @@ render_cell(struct terminal *term, pixman_image_t *pix, else { mtx_lock(&term->render.workers.lock); - /* Parallel thread may have instantiated it while we took the lock */ + /* Other thread may have instantiated it while we + * aquired the lock */ if (term->box_drawing[idx] == NULL) term->box_drawing[idx] = box_drawing(term, base); mtx_unlock(&term->render.workers.lock); diff --git a/terminal.h b/terminal.h index 620678b1..bfb327e3 100644 --- a/terminal.h +++ b/terminal.h @@ -335,10 +335,11 @@ struct terminal { /* * 0-159: U+02500+0259F - * 160-299: U+1FB00-1FB8B - * 300-301: U+1FB9A-1FB9B + * 160-415: U+02800-028FF + * 416-555: U+1FB00-1FB8B + * 556-557: U+1FB9A-1FB9B */ - struct fcft_glyph *box_drawing[302]; + struct fcft_glyph *box_drawing[558]; bool is_sending_paste_data; ptmx_buffer_list_t ptmx_buffers; From 896825f50c685507797d66c8f0428c3fe8c2d2e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 3 Sep 2021 21:38:08 +0200 Subject: [PATCH 2/5] render: codespell: aquire -> acquire --- render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render.c b/render.c index 1cbff10d..684c86e4 100644 --- a/render.c +++ b/render.c @@ -548,7 +548,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, mtx_lock(&term->render.workers.lock); /* Other thread may have instantiated it while we - * aquired the lock */ + * acquired the lock */ if (term->box_drawing[idx] == NULL) term->box_drawing[idx] = box_drawing(term, base); mtx_unlock(&term->render.workers.lock); From ac2091f107cbcca546d9f62fc81c5a519aec67dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 5 Sep 2021 10:05:35 +0200 Subject: [PATCH 3/5] box-drawing: NOINLINE braille --- box-drawing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/box-drawing.c b/box-drawing.c index a1940392..1c694627 100644 --- a/box-drawing.c +++ b/box-drawing.c @@ -1953,7 +1953,7 @@ draw_quadrant(struct buf *buf, wchar_t wc) quad_lower_right(buf); } -static void +static void NOINLINE draw_braille(struct buf *buf, wchar_t wc) { int w = min(buf->width / 4, buf->height / 8); From 37b15adcd8306652ce21804cb6140d1361adc94f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 14 Sep 2021 09:50:49 +0200 Subject: [PATCH 4/5] =?UTF-8?q?term:=20turn=20=E2=80=98box-drawings?= =?UTF-8?q?=E2=80=99=20array=20into=20three=20dynamically=20allocated=20ar?= =?UTF-8?q?rays?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The box_drawings array is now quite large, and uses up ~4K when *empty*. This patch splits it up into three separate, dynamically allocated arrays; one for the traditional box+line drawing and block elements glyphs, one for braille, and one for the legacy computing symbols. When we need to render a glyph, the *entire* array (that it belongs to) is allocated. I.e this is one step closer to a dynamic glyph cache (like the one fcft uses), but doesn’t go all the way. This is especially nice for people with ‘box-drawings-uses-font-glyphs=yes’; for them, the custom glyphs now uses 3*8 bytes (for the three array pointers), instead of 4K. --- render.c | 60 +++++++++++++++++++++++++++++++----------------------- terminal.c | 42 +++++++++++++++++++++++++++++--------- terminal.h | 27 +++++++++++++++++------- 3 files changed, 87 insertions(+), 42 deletions(-) diff --git a/render.c b/render.c index 684c86e4..b9acc8e1 100644 --- a/render.c +++ b/render.c @@ -513,10 +513,12 @@ render_cell(struct terminal *term, pixman_image_t *pix, if (base != 0) { if (unlikely( /* Classic box drawings */ - (base >= 0x2500 && base <= 0x259f) || + (base >= GLYPH_BOX_DRAWING_FIRST && + base <= GLYPH_BOX_DRAWING_LAST) || /* Braille */ - (base >= 0x2800 && base <= 0x28ff) || + (base >= GLYPH_BRAILLE_FIRST && + base <= GLYPH_BRAILLE_LAST) || /* * Unicode 13 "Symbols for Legacy Computing" @@ -524,42 +526,50 @@ render_cell(struct terminal *term, pixman_image_t *pix, * * Note, the full range is U+1FB00 - U+1FBF9 */ - - /* Unicode 13 sextants */ - (base >= 0x1fb00 && base <= 0x1fb8b) || - (base >= 0x1fb9a && base <= 0x1fb9b)) && + (base >= GLYPH_LEGACY_FIRST && + base <= GLYPH_LEGACY_LAST)) && likely(!term->conf->box_drawings_uses_font_glyphs)) { - /* Box drawing characters */ - size_t idx = base >= 0x1fb00 - ? (base >= 0x1fb9a - ? base - 0x1fb9a + 556 - : base - 0x1fb00 + 416) - : (base >= 0x2800 - ? base - 0x2800 + 160 - : base - 0x2500); + struct fcft_glyph ***arr; + size_t count; + size_t idx; - xassert(idx < ALEN(term->box_drawing)); + if (base >= GLYPH_LEGACY_FIRST) { + arr = &term->custom_glyphs.legacy; + count = GLYPH_LEGACY_COUNT; + idx = base - GLYPH_LEGACY_FIRST; + } else if (base >= GLYPH_BRAILLE_FIRST) { + arr = &term->custom_glyphs.braille; + count = GLYPH_BRAILLE_COUNT; + idx = base - GLYPH_BRAILLE_FIRST; + } else { + arr = &term->custom_glyphs.box_drawing; + count = GLYPH_BOX_DRAWING_COUNT; + idx = base - GLYPH_BOX_DRAWING_FIRST; + } - if (likely(term->box_drawing[idx] != NULL)) - single = term->box_drawing[idx]; + if (unlikely(*arr == NULL)) + *arr = xcalloc(count, sizeof((*arr)[0])); + + if (likely((*arr)[idx] != NULL)) + single = (*arr)[idx]; else { mtx_lock(&term->render.workers.lock); /* Other thread may have instantiated it while we * acquired the lock */ - if (term->box_drawing[idx] == NULL) - term->box_drawing[idx] = box_drawing(term, base); + single = (*arr)[idx]; + if (likely(single == NULL)) + single = (*arr)[idx] = box_drawing(term, base); mtx_unlock(&term->render.workers.lock); - - single = term->box_drawing[idx]; - xassert(single != NULL); } - glyph_count = 1; - glyphs = &single; - cell_cols = single->cols; + if (single != NULL) { + glyph_count = 1; + glyphs = &single; + cell_cols = single->cols; + } } else if (base >= CELL_COMB_CHARS_LO && base <= CELL_COMB_CHARS_HI) diff --git a/terminal.c b/terminal.c index 0138aa15..e6837977 100644 --- a/terminal.c +++ b/terminal.c @@ -626,15 +626,28 @@ err_sem_destroy: } static void -free_box_drawing(struct fcft_glyph **box_drawing) +free_custom_glyph(struct fcft_glyph **glyph) { - if (*box_drawing == NULL) + if (*glyph == NULL) return; - free(pixman_image_get_data((*box_drawing)->pix)); - pixman_image_unref((*box_drawing)->pix); - free(*box_drawing); - *box_drawing = NULL; + free(pixman_image_get_data((*glyph)->pix)); + pixman_image_unref((*glyph)->pix); + free(*glyph); + *glyph = NULL; +} + +static void +free_custom_glyphs(struct fcft_glyph ***glyphs, size_t count) +{ + if (*glyphs == NULL) + return; + + for (size_t i = 0; i < count; i++) + free_custom_glyph(&(*glyphs)[i]); + + free(*glyphs); + *glyphs = NULL; } static bool @@ -647,8 +660,12 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4]) term->fonts[i] = fonts[i]; } - for (size_t i = 0; i < ALEN(term->box_drawing); i++) - free_box_drawing(&term->box_drawing[i]); + free_custom_glyphs( + &term->custom_glyphs.box_drawing, GLYPH_BOX_DRAWING_COUNT); + free_custom_glyphs( + &term->custom_glyphs.braille, GLYPH_BRAILLE_COUNT); + free_custom_glyphs( + &term->custom_glyphs.legacy, GLYPH_LEGACY_COUNT); const int old_cell_width = term->cell_width; const int old_cell_height = term->cell_height; @@ -1590,8 +1607,13 @@ term_destroy(struct terminal *term) for (size_t i = 0; i < 4; i++) free(term->font_sizes[i]); - for (size_t i = 0; i < ALEN(term->box_drawing); i++) - free_box_drawing(&term->box_drawing[i]); + + free_custom_glyphs( + &term->custom_glyphs.box_drawing, GLYPH_BOX_DRAWING_COUNT); + free_custom_glyphs( + &term->custom_glyphs.braille, GLYPH_BRAILLE_COUNT); + free_custom_glyphs( + &term->custom_glyphs.legacy, GLYPH_LEGACY_COUNT); free(term->search.buf); diff --git a/terminal.h b/terminal.h index bfb327e3..028b4844 100644 --- a/terminal.h +++ b/terminal.h @@ -333,13 +333,26 @@ struct terminal { int16_t font_y_ofs; enum fcft_subpixel font_subpixel; - /* - * 0-159: U+02500+0259F - * 160-415: U+02800-028FF - * 416-555: U+1FB00-1FB8B - * 556-557: U+1FB9A-1FB9B - */ - struct fcft_glyph *box_drawing[558]; + struct { + struct fcft_glyph **box_drawing; + struct fcft_glyph **braille; + struct fcft_glyph **legacy; + + #define GLYPH_BOX_DRAWING_FIRST 0x2500 + #define GLYPH_BOX_DRAWING_LAST 0x259F + #define GLYPH_BOX_DRAWING_COUNT \ + (GLYPH_BOX_DRAWING_LAST - GLYPH_BOX_DRAWING_FIRST + 1) + + #define GLYPH_BRAILLE_FIRST 0x2800 + #define GLYPH_BRAILLE_LAST 0x28FF + #define GLYPH_BRAILLE_COUNT \ + (GLYPH_BRAILLE_LAST - GLYPH_BRAILLE_FIRST + 1) + + #define GLYPH_LEGACY_FIRST 0x1FB00 + #define GLYPH_LEGACY_LAST 0x1FB9B + #define GLYPH_LEGACY_COUNT \ + (GLYPH_LEGACY_LAST - GLYPH_LEGACY_FIRST + 1) + } custom_glyphs; bool is_sending_paste_data; ptmx_buffer_list_t ptmx_buffers; From e426e77b1d3640868b521b65617525f76ab3b35c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 14 Sep 2021 10:20:07 +0200 Subject: [PATCH 5/5] box-drawing: braille: prefer increasing spacing over dot width Too large dots make them harder to distinguish, when the spacing between them is small. Prefer increasing the spacing, instead of increasing the dot size. This looks better at small font sizes in particular. --- box-drawing.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/box-drawing.c b/box-drawing.c index 1c694627..f1789f64 100644 --- a/box-drawing.c +++ b/box-drawing.c @@ -1962,8 +1962,8 @@ draw_braille(struct buf *buf, wchar_t wc) int x_margin = x_spacing / 2; int y_margin = y_spacing / 2; - int x_pix_left = buf->width - 2 * x_margin - x_spacing - 2 * w; - int y_pix_left = buf->height - 2 * y_margin - 3 * y_spacing - 4 * w; + int x_px_left = buf->width - 2 * x_margin - x_spacing - 2 * w; + int y_px_left = buf->height - 2 * y_margin - 3 * y_spacing - 4 * w; LOG_DBG( "braille: before adjusting: " @@ -1972,35 +1972,38 @@ draw_braille(struct buf *buf, wchar_t wc) w, x_pix_left, y_pix_left); /* First, try hard to ensure the DOT width is non-zero */ - if (x_pix_left >= 2 && y_pix_left >= 4 && w == 0) { + if (x_px_left >= 2 && y_px_left >= 4 && w == 0) { w++; - x_pix_left -= 2; - y_pix_left -= 4; + x_px_left -= 2; + y_px_left -= 4; } /* Second, prefer a non-zero margin */ - if (x_pix_left >= 2 && x_margin == 0) { x_margin = 1; x_pix_left -= 2; } - if (y_pix_left >= 2 && y_margin == 0) { y_margin = 1; y_pix_left -= 2; } + if (x_px_left >= 2 && x_margin == 0) { x_margin = 1; x_px_left -= 2; } + if (y_px_left >= 2 && y_margin == 0) { y_margin = 1; y_px_left -= 2; } - if (x_pix_left >= 2 && y_pix_left >= 4) { + /* Third, increase spacing */ + if (x_px_left >= 1) { x_spacing++; x_px_left--; } + if (y_px_left >= 3) { y_spacing++; y_px_left -= 3; } + + /* Fourth, margins (“spacing”, but on the sides) */ + if (x_px_left >= 2) { x_margin++; x_px_left -= 2; } + if (y_px_left >= 2) { y_margin++; y_px_left -= 2; } + + /* Last - increase dot width */ + if (x_px_left >= 2 && y_px_left >= 4) { w++; - x_pix_left -= 2; - y_pix_left -= 4; + x_px_left -= 2; + y_px_left -= 4; } - if (x_pix_left >= 1) { x_spacing++; x_pix_left--; } - if (y_pix_left >= 3) { y_spacing++; y_pix_left -= 3; } - - if (x_pix_left >= 2) { x_margin++; x_pix_left -= 2; } - if (y_pix_left >= 2) { y_margin++; y_pix_left -= 2; } - LOG_DBG( "braille: after adjusting: " "cell: %dx%d, margin=%dx%d, spacing=%dx%d, width=%d, left=%dx%d", buf->width, buf->height, x_margin, y_margin, x_spacing, y_spacing, w, x_pix_left, y_pix_left); - xassert(x_pix_left <= 1 || y_pix_left <= 1); + xassert(x_px_left <= 1 || y_px_left <= 1); xassert(2 * x_margin + 2 * w + x_spacing <= buf->width); xassert(2 * y_margin + 4 * w + 3 * y_spacing <= buf->height);