From 59cb1ad1e0abeb03473f4f1a1568ea4995e75e7c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 18 Oct 2019 11:01:51 +0530 Subject: [PATCH 1/2] Implement special rendering for the diagonal line box drawing chars --- kitty/fonts/box_drawing.py | 66 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/kitty/fonts/box_drawing.py b/kitty/fonts/box_drawing.py index 7cd3e33cd..718773eb4 100644 --- a/kitty/fonts/box_drawing.py +++ b/kitty/fonts/box_drawing.py @@ -172,6 +172,69 @@ def triangle(buf, width, height, left=True): fill_region(buf, width, height, xlimits) +def antialiased_line(buf, width, height, p1, p2): + # Draw an antialiased line using the Wu algorithm + x1, y1 = p1 + x2, y2 = p2 + dx, dy = x2 - x1, y2 - y1 + off_limit = height * width + steep = abs(dx) < abs(dy) + + if steep: + x1, y1, x2, y2, dx, dy = y1, x1, y2, x2, dy, dx + + def p(x, y): + return y, x + else: + def p(x, y): + return x, y + + if x2 < x1: + x1, x2, y1, y2 = x2, x1, y2, y1 + + def _fpart(x): + return x - int(x) + + def _rfpart(x): + return 1 - _fpart(x) + + def putpixel(p, alpha): + x, y = p + off = int(x + y * width) + if 0 <= off < off_limit: + buf[off] = min(buf[off] + (alpha * 255), 255) + + def draw_endpoint(pt): + x, y = pt + xend = round(x) + yend = y + grad * (xend - x) + xgap = _rfpart(x + 0.5) + px, py = int(xend), int(yend) + putpixel(p(px, py), _rfpart(yend) * xgap) + putpixel(p(px, py+1), _fpart(yend) * xgap) + return px + + grad = dy/dx + intery = y1 + _rfpart(x1) * grad + + xstart = draw_endpoint(p(*p1)) + 1 + xend = draw_endpoint(p(*p2)) + + for x in range(xstart, xend): + y = int(intery) + putpixel(p(x, y), _rfpart(intery)) + putpixel(p(x, y+1), _fpart(intery)) + intery += grad + + +def cross_line(buf, width, height, left=True): + if left: + p1, p2 = (0, 0), (width - 1, height - 1) + else: + p1, p2 = (width - 1, 0), (0, height - 1) + antialiased_line(buf, width, height, p1, p2) + + def cubic_bezier(start, end, c1, c2): def bezier_eq(p0, p1, p2, p3): @@ -472,6 +535,9 @@ box_chars = { '╣': [p(inner_corner, which='tl'), p(inner_corner, which='bl'), p(dvline, only='right')], '╦': [p(inner_corner, which='bl'), p(inner_corner, which='br'), p(dhline, only='top')], '╩': [p(inner_corner, which='tl'), p(inner_corner, which='tr'), p(dhline, only='bottom')], + '╱': [p(cross_line, left=False)], + '╲': [cross_line], + '╳': [cross_line, p(cross_line, left=False)], '▀': [p(vblock, frac=1/2)], '▁': [p(vblock, frac=1/8, gravity='bottom')], '▂': [p(vblock, frac=1/4, gravity='bottom')], From 576b03c047c4f538bca3f5b1b3501e7a930f355d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 18 Oct 2019 13:19:04 +0530 Subject: [PATCH 2/2] Implement drawing thick anti-aliased lines And actually use it for the unicode diagonal box drawing chars and also the powerline angle separators --- kitty/fonts.c | 16 +++-------- kitty/fonts/box_drawing.py | 56 +++++++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/kitty/fonts.c b/kitty/fonts.c index f6fa2c9a9..8059685bd 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -554,11 +554,9 @@ START_ALLOW_CASE_RANGE case ' ': case '\t': return BLANK_FONT; - case 0x2500 ... 0x2570: + case 0x2500 ... 0x2573: case 0x2574 ... 0x259f: - case 0xe0b0: - case 0xe0b2: - case 0xe0b4: + case 0xe0b0 ... 0xe0b4: case 0xe0b6: return BOX_FONT; default: @@ -594,14 +592,8 @@ START_ALLOW_CASE_RANGE switch(ch) { case 0x2500 ... 0x259f: return ch - 0x2500; - case 0xe0b0: - return 0xfa; - case 0xe0b2: - return 0xfb; - case 0xe0b4: - return 0xfc; - case 0xe0b6: - return 0xfd; + case 0xe0b0 ... 0xe0d4: + return 0xa0 + ch - 0xe0b0; default: return 0xff; } diff --git a/kitty/fonts/box_drawing.py b/kitty/fonts/box_drawing.py index 718773eb4..88f050a96 100644 --- a/kitty/fonts/box_drawing.py +++ b/kitty/fonts/box_drawing.py @@ -172,7 +172,7 @@ def triangle(buf, width, height, left=True): fill_region(buf, width, height, xlimits) -def antialiased_line(buf, width, height, p1, p2): +def antialiased_1px_line(buf, width, height, p1, p2): # Draw an antialiased line using the Wu algorithm x1, y1 = p1 x2, y2 = p2 @@ -192,47 +192,75 @@ def antialiased_line(buf, width, height, p1, p2): if x2 < x1: x1, x2, y1, y2 = x2, x1, y2, y1 - def _fpart(x): + def fpart(x): return x - int(x) - def _rfpart(x): - return 1 - _fpart(x) + def rfpart(x): + return 1 - fpart(x) def putpixel(p, alpha): x, y = p off = int(x + y * width) if 0 <= off < off_limit: - buf[off] = min(buf[off] + (alpha * 255), 255) + buf[off] = int(min(buf[off] + (alpha * 255), 255)) def draw_endpoint(pt): x, y = pt xend = round(x) yend = y + grad * (xend - x) - xgap = _rfpart(x + 0.5) + xgap = rfpart(x + 0.5) px, py = int(xend), int(yend) - putpixel(p(px, py), _rfpart(yend) * xgap) - putpixel(p(px, py+1), _fpart(yend) * xgap) + putpixel(p(px, py), rfpart(yend) * xgap) + putpixel(p(px, py+1), fpart(yend) * xgap) return px grad = dy/dx - intery = y1 + _rfpart(x1) * grad + intery = y1 + rfpart(x1) * grad xstart = draw_endpoint(p(*p1)) + 1 xend = draw_endpoint(p(*p2)) for x in range(xstart, xend): y = int(intery) - putpixel(p(x, y), _rfpart(intery)) - putpixel(p(x, y+1), _fpart(intery)) + putpixel(p(x, y), rfpart(intery)) + putpixel(p(x, y+1), fpart(intery)) intery += grad -def cross_line(buf, width, height, left=True): +def antialiased_line(buf, width, height, p1, p2, level=1): + th = thickness(level) + if th < 2: + return antialiased_1px_line(buf, width, height, p1, p2) + (x1, y1), (x2, y2) = p1, p2 + dh = th // 2 + items = range(-dh, dh + (th % 2)) + for delta in items: + antialiased_1px_line(buf, width, height, (x1, y1 + delta), (x2, y2 + delta)) + + +def cross_line(buf, width, height, left=True, level=1): if left: p1, p2 = (0, 0), (width - 1, height - 1) else: p1, p2 = (width - 1, 0), (0, height - 1) - antialiased_line(buf, width, height, p1, p2) + antialiased_line(buf, width, height, p1, p2, level=level) + + +def half_cross_line(buf, width, height, which='tl', level=1): + my = (height - 1) // 2 + if which == 'tl': + p1 = 0, 0 + p2 = width - 1, my + elif which == 'bl': + p2 = 0, height - 1 + p1 = width - 1, my + elif which == 'tr': + p1 = width - 1, 0 + p2 = 0, my + else: + p2 = width - 1, height - 1 + p1 = 0, my + antialiased_line(buf, width, height, p1, p2, level=level) def cubic_bezier(start, end, c1, c2): @@ -522,6 +550,8 @@ box_chars = { '': [p(triangle, left=False)], '': [D], '': [p(D, left=False)], + '': [p(half_cross_line, which='tl'), p(half_cross_line, which='bl')], + '': [p(half_cross_line, which='tr'), p(half_cross_line, which='br')], '═': [dhline], '║': [dvline], '╞': [vline, p(half_dhline, which='right')],