From 39dc31e02183801b5661b2bff2df705a85b7bd20 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 26 Dec 2024 15:17:41 +0530 Subject: [PATCH] Render the octant characters using box drawing --- kitty/data-types.h | 2 +- kitty/decorations.c | 86 ++++++++++++++++++++++++++++++++++++++++++++ kitty/fonts.c | 3 ++ kitty_tests/fonts.py | 17 +++++++-- 4 files changed, 104 insertions(+), 4 deletions(-) diff --git a/kitty/data-types.h b/kitty/data-types.h index 578193c3a..e71bb9683 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -183,7 +183,7 @@ typedef struct ImageAnchorPosition { #define IGNORE_PEDANTIC_WARNINGS START_IGNORE_DIAGNOSTIC("-Wpedantic") #define END_IGNORE_PEDANTIC_WARNINGS END_IGNORE_DIAGNOSTIC -#define ALLOW_UNUSED_RESULT IGNORE_DIAGNOSTIC("-Wunused-result") +#define ALLOW_UNUSED_RESULT START_IGNORE_DIAGNOSTIC("-Wunused-result") #define END_ALLOW_UNUSED_RESULT END_IGNORE_DIAGNOSTIC #define START_ALLOW_CASE_RANGE IGNORE_PEDANTIC_WARNINGS #define END_ALLOW_CASE_RANGE END_IGNORE_PEDANTIC_WARNINGS diff --git a/kitty/decorations.c b/kitty/decorations.c index e49d872fe..6911d219b 100644 --- a/kitty/decorations.c +++ b/kitty/decorations.c @@ -790,6 +790,30 @@ inner_corner(Canvas *self, uint level, Corner corner) { draw_vline(self, y1, y2, self->width / 2 + (xd * hgap), level); } +static Range +fourth_range(uint size, uint which) { + uint thickness = max(1, size / 4); + uint block = thickness * 4; + if (block == size) return (Range){.start=thickness * which, .end=thickness * (which + 1)}; + if (block > size) { + uint start = min(which * thickness, minus(size, thickness)); + return (Range){.start=start, .end=start + thickness}; + } + uint extra = minus(size, block); + uint thicknesses[4] = {thickness, thickness, thickness, thickness}; + uint pos = 0; + if (extra) { +#define d(i) thicknesses[i]++; if (!--extra) goto done; + // ensures the thickness of first and last are least likely to be changed + d(1); d(2); d(3); d(0); +#undef d + } +done: + for (uint i = 0; i < which; i++) pos += thicknesses[i]; + return (Range){.start=pos, .end=pos + thicknesses[which]}; +} + + static Range eight_range(uint size, uint which) { uint thickness = max(1, size / 8); @@ -829,6 +853,67 @@ eight_bar(Canvas *self, uint which, bool horizontal) { } } + +static void +octant_segment(Canvas *self, uint8_t which, bool left) { + Range x_range = left ? (Range){0, self->width / 2} : (Range){self->width/2, self->width}; + Range y_range = fourth_range(self->height, which); + for (uint y = y_range.start; y < y_range.end; y++) { + uint offset = y * self->width; + memset(self->mask + offset + x_range.start, 255, minus(x_range.end, x_range.start)); + } +} + +static void +octant(Canvas *self, uint8_t which) { + enum flags { a = 1, b = 2, c = 4, d = 8, m = 16, n = 32, o = 64, p = 128 }; + static const enum flags mapping[230] = { + // 00 - 0f + b, b|m, a|b|m, n, a|n, a|m|n, b|n, a|b|n, b|m|n, c, a|c, c|m, a|c|m, a|b|c, b|c|m, a|b|c|m, + // 10 - 1f + c|n, a|c|n, c|m|n, a|c|m|n, b|c|n, a|b|c|n, b|c|m|n, a|b|c|m|n, o, a|o, m|o, a|m|o, b|o, a|b|o, b|m|o, a|b|m|o, + // 20 - 2f + a|n|o, m|n|o, a|m|n|o, b|n|o, a|b|n|o, b|m|n|o, a|b|m|n|o, c|o, a|c|o, c|m|o, a|c|m|o, b|c|o, a|b|c|o, b|c|m|o, a|b|c|m|o, c|n|o, + // 30 - 3f + a|c|n|o, c|m|n|o, a|c|m|n|o, b|c|n|o, a|b|c|n|o, b|c|m|n|o, a|d, d|m, a|d|m, b|d, a|b|d, b|d|m, a|b|d|m, d|n, a|d|n, d|m|n, + // 40 - 4f + a|d|m|n, b|d|n, a|b|d|n, b|d|m|n, a|b|d|m|n, a|c|d, c|d|m, a|c|d|m, b|c|d, b|c|d|m, a|b|c|d|m, c|d|n, a|c|d|n, a|c|d|m|n, b|c|d|n, a|b|c|d|n, + // 50 - 5f + b|c|d|m|n, d|o, a|d|o, d|m|o, a|d|m|o, b|d|o, a|b|d|o, b|d|m|o, a|b|d|m|o, d|n|o, a|d|n|o, d|m|n|o, a|d|m|n|o, b|d|n|o, a|b|d|n|o, b|d|m|n|o, + // 60 - 6f + ~(c|p), c|d|o, a|c|d|o, c|d|m|o, a|c|d|m|o, b|c|d|o, ~(m|n|p), b|c|d|m|o, ~(n|p), c|d|n|o, a|c|d|n|o, c|d|m|n|o, ~(b|p), b|c|d|n|o, ~(m|p), ~(a|p), + // 70 - 7f + ~p, a|p, m|p, a|m|p, b|p, a|b|p, b|m|p, a|b|m|p, n|p, a|n|p, m|n|p, a|m|n|p, b|n|p, a|b|n|p, b|m|n|p, ~(c|d|o), + // 80 - 8f + c|p, a|c|p, c|m|p, a|c|m|p, b|c|p, a|b|c|p, b|c|m|p, ~(d|n|o), c|n|p, a|c|n|p, c|m|n|p, ~(b|d|o), b|c|n|p, ~(d|m|o), ~(a|d|o), ~(d|o), + // 90 - 9f + a|o|p, m|o|p, a|m|o|p, b|o|p, b|m|o|p, a|b|m|o|p, n|o|p, a|n|o|p, a|m|n|o|p, b|n|o|p, a|b|n|o|p, b|m|n|o|p, c|o|p, a|c|o|p, c|m|o|p, a|c|m|o|p, + // a0 - af + b|c|o|p, a|b|c|o|p, b|c|m|o|p, ~(n|d), c|n|o|p, a|c|n|o|p, c|m|n|o|p, ~(b|d), b|c|n|o|p, ~(d|m), ~(a|d), ~d, a|d|p, d|m|p, a|d|m|p, b|d|p, + // b0 - bf + a|b|d|p, b|d|m|p, a|b|d|m|p, d|n|p, a|d|n|p, d|m|n|p, a|d|m|n|p, b|d|n|p, a|b|d|n|p, b|d|m|n|p, ~(c|o), c|d|p, a|c|d|p, c|d|m|p, a|c|d|m|p, b|c|d|p, + + // c0 -cf + a|b|c|d|p, b|c|d|m|p, ~(n|o), c|d|n|p, a|c|d|n|p, c|d|m|n|p, ~(b|o), b|c|d|n|p, ~(m|o), ~(a|o), ~o, d|o|p, a|d|o|p, d|m|o|p, a|d|m|o|p, b|d|o|p, + + // d0 - df + a|b|d|o|p, b|d|m|o|p, ~(c|n), d|n|o|p, a|d|n|o|p, d|m|n|o|p, ~(b|c), b|d|n|o|p, ~(c|m), ~(a|c), ~c, a|c|d|o|p, c|d|m|o|p, ~(b|n), b|c|d|o|p, ~(a|n), + // e0 - e5 + ~n, c|d|n|o|p, ~(b|m), ~b, ~m, ~a, + + }; + which = mapping[which]; + if (which & a) octant_segment(self, 0, true); + if (which & b) octant_segment(self, 1, true); + if (which & c) octant_segment(self, 2, true); + if (which & d) octant_segment(self, 3, true); + if (which & m) octant_segment(self, 0, false); + if (which & n) octant_segment(self, 1, false); + if (which & o) octant_segment(self, 2, false); + if (which & p) octant_segment(self, 3, false); + +} + static void eight_block(Canvas *self, int horizontal, ...) { va_list args; va_start(args, horizontal); @@ -1702,6 +1787,7 @@ START_ALLOW_CASE_RANGE case 0x1fb28 ... 0x1fb28 + 19: sextant(c, ch - 0x1fb00 + 3); break; case 0x1fb70 ... 0x1fb70 + 5: eight_bar(c, ch - 0x1fb6f, false); break; case 0x1fb76 ... 0x1fb76 + 5: eight_bar(c, ch - 0x1fb75, true); break; + case 0x1cd00 ... 0x1cde5: octant(c, ch - 0x1cd00); true; } free(canvas.holes); free(canvas.y_limits); free(ss.holes); free(ss.y_limits); diff --git a/kitty/fonts.c b/kitty/fonts.c index ca4adf21d..b4d7414cf 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -724,6 +724,7 @@ START_ALLOW_CASE_RANGE case 0xe0b0 ... 0xe0bf: case 0xe0d6 ... 0xe0d7: // powerline box drawing case 0xee00 ... 0xee0b: // fira code progress bar/spinner case 0x1fb00 ... 0x1fbae: // symbols for legacy computing + case 0x1cd00 ... 0x1cde5: // octants case 0xf5d0 ... 0xf60d: // branch drawing characters if (allow_use_of_box_fonts) return BOX_FONT; /* fallthrough */ @@ -761,6 +762,8 @@ START_ALLOW_CASE_RANGE return 0xf00 + ch - 0x2800; // IDs from 0xf00 to 0xfff case 0x1fb00 ... 0x1fbae: return 0x1000 + ch - 0x1fb00; // IDs from 0x1000 to 0x10ae + case 0x1cd00 ... 0x1cde5: + return 0x1100 + ch - 0x1cd00; // IDs from 0x1100 to 0x11e5 case 0xf5d0 ... 0xf60d: return 0x2000 + ch - 0xf5d0; // IDs from 0x2000 to 0x203d default: diff --git a/kitty_tests/fonts.py b/kitty_tests/fonts.py index 3cca30211..2c613a0ea 100644 --- a/kitty_tests/fonts.py +++ b/kitty_tests/fonts.py @@ -435,15 +435,24 @@ class Rendering(FontBaseTest): def test_chars(chars: str = '╌', sz: int = 128) -> None: # kitty +runpy "from kitty.fonts.box_drawing import test_chars; test_chars('XXX')" from kitty.fast_data_types import concat_cells, render_box_char, set_send_sprite_to_gpu - - from .render import display_bitmap, setup_for_testing + from kitty.fonts.render import display_bitmap, setup_for_testing if not chars: import sys chars = sys.argv[-1] + + def as_ord(x: str) -> int: + if x.lower().startswith('u+'): + return int(x[2:], 16) + return ord(x) + + if '...' in chars: + start, end = chars.partition('...')[::2] + chars = ''.join(map(chr, range(as_ord(start), as_ord(end)+1))) + with setup_for_testing('monospace', sz) as (_, width, height): try: for ch in chars: - nb = render_box_char(ord(ch), width, height) + nb = render_box_char(as_ord(ch), width, height) rgb_data = concat_cells(width, height, False, (nb,)) display_bitmap(rgb_data, width, height) print() @@ -1188,3 +1197,5 @@ box_chars = { # {{{ '🮬', '🮭', '🮮'} # }}} +for ch in range(0x1cd00, 0x1cde5+1): # octants + box_chars.add(chr(ch))