From 4309e2371dca6583e9fafb5e0c8661978fb6e63b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 12 Jul 2020 10:07:55 +0530 Subject: [PATCH] Simplify code by using supersampling for diagonal lines as well Wu antialiasing obviously performs better but since characters are rendered only once and are very small in pixel size, performance is irrelevant here. --- kitty/fonts/box_drawing.py | 100 +++++++++++-------------------------- 1 file changed, 28 insertions(+), 72 deletions(-) diff --git a/kitty/fonts/box_drawing.py b/kitty/fonts/box_drawing.py index f4359ba15..8b0f1eaec 100644 --- a/kitty/fonts/box_drawing.py +++ b/kitty/fonts/box_drawing.py @@ -162,18 +162,22 @@ def downsample(src: BufType, dest: BufType, dest_width: int, dest_height: int, f for y in range(dest_height): offset = dest_width * y for x in range(dest_width): - dest[offset + x] = average_intensity_in_src(x, y) + dest[offset + x] = min(255, dest[offset + x] + average_intensity_in_src(x, y)) def supersampled(supersample_factor: int = 4) -> Callable: # Anti-alias the drawing performed by the wrapped function by # using supersampling + class SSByteArray(bytearray): + supersample_factor = 1 + def create_wrapper(f: Callable) -> Callable: @wraps(f) def supersampled_wrapper(buf: BufType, width: int, height: int, *args: Any, **kw: Any) -> None: w, h = supersample_factor * width, supersample_factor * height - ssbuf = bytearray(w * h) + ssbuf = SSByteArray(w * h) + ssbuf.supersample_factor = supersample_factor f(ssbuf, w, h, *args, **kw) downsample(ssbuf, buf, width, height, factor=supersample_factor) return supersampled_wrapper @@ -227,85 +231,37 @@ def corner_triangle(buf: BufType, width: int, height: int, corner: str) -> None: fill_region(buf, width, height, xlimits) -def antialiased_1px_line(buf: BufType, width: int, height: int, p1: Tuple[int, int], p2: Tuple[int, int]) -> None: - # 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: int, y: int) -> Tuple[int, int]: - return y, x - else: - def p(x: int, y: int) -> Tuple[int, int]: - return x, y - - if x2 < x1: - x1, x2, y1, y2 = x2, x1, y2, y1 - - def fpart(x: float) -> float: - return x - int(x) - - def rfpart(x: float) -> float: - return 1 - fpart(x) - - def putpixel(p: Tuple[int, int], alpha: float) -> None: - x, y = p - off = int(x + y * width) - if 0 <= off < off_limit: - buf[off] = int(min(buf[off] + (alpha * 255), 255)) - - def draw_endpoint(pt: Tuple[int, int]) -> int: - 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)) - xend = draw_endpoint(p(*p2)) - - if xstart > xend: - xstart, xend = xend, xstart - xstart += 1 - - 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 antialiased_line(buf: BufType, width: int, height: int, p1: Tuple[int, int], p2: Tuple[int, int], level: int = 1) -> None: - 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 thick_line(buf: BufType, width: int, height: int, thickness_in_pixels: int, p1: Tuple[int, int], p2: Tuple[int, int]) -> None: + if p1[0] > p2[0]: + p1, p2 = p2, p1 + leq = line_equation(*p1, *p2) + for x in range(p1[0], p2[0] + 1): + if 0 <= x < width: + y_p = leq(x) + if thickness_in_pixels <= 1: + r = range(int(y_p), int(y_p) + 1) + else: + delta = thickness_in_pixels // 2 + r = range(int(y_p) - delta, int(y_p) + delta + (thickness_in_pixels % 2)) + for y in r: + if 0 <= y < height: + buf[x + y * width] = 255 +@supersampled() def cross_line(buf: BufType, width: int, height: int, left: bool = True, level: int = 1) -> None: 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, level=level) + supersample_factor = getattr(buf, 'supersample_factor') + thick_line(buf, width, height, supersample_factor * thickness(level), p1, p2) +@supersampled() def half_cross_line(buf: BufType, width: int, height: int, which: str = 'tl', level: int = 1) -> None: + supersample_factor = getattr(buf, 'supersample_factor') + thickness_in_pixels = thickness(level) * supersample_factor my = (height - 1) // 2 if which == 'tl': p1 = 0, 0 @@ -319,7 +275,7 @@ def half_cross_line(buf: BufType, width: int, height: int, which: str = 'tl', le else: p2 = width - 1, height - 1 p1 = 0, my - antialiased_line(buf, width, height, p1, p2, level=level) + thick_line(buf, width, height, thickness_in_pixels, p1, p2) BezierFunc = Callable[[float], float]