Add typing to box_drawing

This commit is contained in:
Kovid Goyal 2020-03-08 21:30:50 +05:30
parent 9b973ef99c
commit 353db678a2
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 112 additions and 107 deletions

View File

@ -10,24 +10,26 @@
import math import math
from functools import partial as p from functools import partial as p
from itertools import repeat from itertools import repeat
from typing import cast, Callable from typing import (
Callable, Dict, Generator, Iterable, List, Optional, Sequence, Tuple, cast
)
scale = (0.001, 1., 1.5, 2.)
scale = (0.001, 1, 1.5, 2)
_dpi = 96.0 _dpi = 96.0
BufType = bytearray
def set_scale(new_scale): def set_scale(new_scale: Sequence[float]) -> None:
global scale global scale
scale = tuple(new_scale) scale = (new_scale[0], new_scale[1], new_scale[2], new_scale[3])
def thickness(level=1, horizontal=True): def thickness(level: int = 1, horizontal: bool = True) -> int:
pts = scale[level] pts = scale[level]
return int(math.ceil(pts * (_dpi / 72.0))) return int(math.ceil(pts * (_dpi / 72.0)))
def draw_hline(buf, width, x1, x2, y, level): def draw_hline(buf: BufType, width: int, x1: int, x2: int, y: int, level: int) -> None:
' Draw a horizontal line between [x1, x2) centered at y with the thickness given by level ' ' Draw a horizontal line between [x1, x2) centered at y with the thickness given by level '
sz = thickness(level=level, horizontal=False) sz = thickness(level=level, horizontal=False)
start = y - sz // 2 start = y - sz // 2
@ -37,7 +39,7 @@ def draw_hline(buf, width, x1, x2, y, level):
buf[offset + x] = 255 buf[offset + x] = 255
def draw_vline(buf, width, y1, y2, x, level): def draw_vline(buf: BufType, width: int, y1: int, y2: int, x: int, level: int) -> None:
' Draw a vertical line between [y1, y2) centered at x with the thickness given by level ' ' Draw a vertical line between [y1, y2) centered at x with the thickness given by level '
sz = thickness(level=level, horizontal=True) sz = thickness(level=level, horizontal=True)
start = x - sz // 2 start = x - sz // 2
@ -46,25 +48,25 @@ def draw_vline(buf, width, y1, y2, x, level):
buf[x + y * width] = 255 buf[x + y * width] = 255
def half_hline(buf, width, height, level=1, which='left', extend_by=0): def half_hline(buf: BufType, width: int, height: int, level: int = 1, which: str = 'left', extend_by: int = 0):
x1, x2 = (0, extend_by + width // 2) if which == 'left' else (width // 2 - extend_by, width) x1, x2 = (0, extend_by + width // 2) if which == 'left' else (width // 2 - extend_by, width)
draw_hline(buf, width, x1, x2, height // 2, level) draw_hline(buf, width, x1, x2, height // 2, level)
def half_vline(buf, width, height, level=1, which='top', extend_by=0): def half_vline(buf: BufType, width: int, height: int, level: int = 1, which: str = 'top', extend_by: int = 0):
y1, y2 = (0, height // 2 + extend_by) if which == 'top' else (height // 2 - extend_by, height) y1, y2 = (0, height // 2 + extend_by) if which == 'top' else (height // 2 - extend_by, height)
draw_vline(buf, width, y1, y2, width // 2, level) draw_vline(buf, width, y1, y2, width // 2, level)
def get_holes(sz, hole_sz, num): def get_holes(sz: int, hole_sz: int, num: int) -> List[Tuple[int, ...]]:
if num == 1: if num == 1:
pts = [sz // 2] pts = [sz // 2]
elif num == 2: elif num == 2:
ssz = (sz - 2 * hole_sz) // 3 ssz = (sz - 2 * hole_sz) // 3
pts = (ssz + hole_sz // 2, 2 * ssz + hole_sz // 2 + hole_sz) pts = [ssz + hole_sz // 2, 2 * ssz + hole_sz // 2 + hole_sz]
elif num == 3: elif num == 3:
ssz = (sz - 3 * hole_sz) // 4 ssz = (sz - 3 * hole_sz) // 4
pts = (ssz + hole_sz // 2, 2 * ssz + hole_sz // 2 + hole_sz, 3 * ssz + 2 * hole_sz + hole_sz // 2) pts = [ssz + hole_sz // 2, 2 * ssz + hole_sz // 2 + hole_sz, 3 * ssz + 2 * hole_sz + hole_sz // 2]
holes = [] holes = []
for c in pts: for c in pts:
holes.append(tuple(range(c - hole_sz // 2, c - hole_sz // 2 + hole_sz))) holes.append(tuple(range(c - hole_sz // 2, c - hole_sz // 2 + hole_sz)))
@ -74,7 +76,7 @@ def get_holes(sz, hole_sz, num):
hole_factor = 8 hole_factor = 8
def add_hholes(buf, width, height, level=1, num=1): def add_hholes(buf: BufType, width: int, height: int, level: int = 1, num: int = 1) -> None:
line_sz = thickness(level=level, horizontal=True) line_sz = thickness(level=level, horizontal=True)
hole_sz = width // hole_factor hole_sz = width // hole_factor
start = height // 2 - line_sz // 2 start = height // 2 - line_sz // 2
@ -86,7 +88,7 @@ def add_hholes(buf, width, height, level=1, num=1):
buf[offset + x] = 0 buf[offset + x] = 0
def add_vholes(buf, width, height, level=1, num=1): def add_vholes(buf: BufType, width: int, height: int, level: int = 1, num: int = 1) -> None:
line_sz = thickness(level=level, horizontal=False) line_sz = thickness(level=level, horizontal=False)
hole_sz = height // hole_factor hole_sz = height // hole_factor
start = width // 2 - line_sz // 2 start = width // 2 - line_sz // 2
@ -97,77 +99,77 @@ def add_vholes(buf, width, height, level=1, num=1):
buf[x + width * y] = 0 buf[x + width * y] = 0
def hline(*a, level=1): def hline(buf: BufType, width: int, height: int, level: int = 1) -> None:
half_hline(*a, level=level) half_hline(buf, width, height, level=level)
half_hline(*a, level=level, which='right') half_hline(buf, width, height, level=level, which='right')
def vline(*a, level=1): def vline(buf: BufType, width: int, height: int, level=1) -> None:
half_vline(*a, level=level) half_vline(buf, width, height, level=level)
half_vline(*a, level=level, which='bottom') half_vline(buf, width, height, level=level, which='bottom')
def hholes(*a, level=1, num=1): def hholes(buf: BufType, width: int, height: int, level=1, num=1) -> None:
hline(*a, level=level) hline(buf, width, height, level=level)
add_hholes(*a, level=level, num=num) add_hholes(buf, width, height, level=level, num=num)
def vholes(*a, level=1, num=1): def vholes(buf: BufType, width: int, height: int, level=1, num=1) -> None:
vline(*a, level=level) vline(buf, width, height, level=level)
add_vholes(*a, level=level, num=num) add_vholes(buf, width, height, level=level, num=num)
def corner(*a, hlevel=1, vlevel=1, which=None): def corner(buf: BufType, width: int, height: int, hlevel=1, vlevel=1, which=None) -> None:
wh = 'right' if which in '┌└' else 'left' wh = 'right' if which in '┌└' else 'left'
half_hline(*a, level=hlevel, which=wh, extend_by=thickness(vlevel, horizontal=True) // 2) half_hline(buf, width, height, level=hlevel, which=wh, extend_by=thickness(vlevel, horizontal=True) // 2)
wv = 'top' if which in '└┘' else 'bottom' wv = 'top' if which in '└┘' else 'bottom'
half_vline(*a, level=vlevel, which=wv) half_vline(buf, width, height, level=vlevel, which=wv)
def vert_t(*args, a=1, b=1, c=1, which=None): def vert_t(buf: BufType, width: int, height: int, a=1, b=1, c=1, which=None) -> None:
half_vline(*args, level=a, which='top') half_vline(buf, width, height, level=a, which='top')
half_hline(*args, level=b, which='left' if which == '' else 'right') half_hline(buf, width, height, level=b, which='left' if which == '' else 'right')
half_vline(*args, level=c, which='bottom') half_vline(buf, width, height, level=c, which='bottom')
def horz_t(*args, a=1, b=1, c=1, which=None): def horz_t(buf: BufType, width: int, height: int, a=1, b=1, c=1, which=None) -> None:
half_hline(*args, level=a, which='left') half_hline(buf, width, height, level=a, which='left')
half_hline(*args, level=b, which='right') half_hline(buf, width, height, level=b, which='right')
half_vline(*args, level=c, which='top' if which == '' else 'bottom') half_vline(buf, width, height, level=c, which='top' if which == '' else 'bottom')
def cross(*s, a=1, b=1, c=1, d=1): def cross(buf: BufType, width: int, height: int, a=1, b=1, c=1, d=1) -> None:
half_hline(*s, level=a) half_hline(buf, width, height, level=a)
half_hline(*s, level=b, which='right') half_hline(buf, width, height, level=b, which='right')
half_vline(*s, level=c) half_vline(buf, width, height, level=c)
half_vline(*s, level=d, which='bottom') half_vline(buf, width, height, level=d, which='bottom')
def fill_region(buf, width, height, xlimits): def fill_region(buf: BufType, width: int, height: int, xlimits: Iterable[Iterable[float]]) -> None:
for y in range(height): for y in range(height):
offset = y * width offset = y * width
for x, (upper, lower) in enumerate(xlimits): for x, (upper, lower) in enumerate(xlimits):
buf[x + offset] = 255 if upper <= y <= lower else 0 buf[x + offset] = 255 if upper <= y <= lower else 0
# Anti-alias the boundary, simple y-axis anti-aliasing # Anti-alias the boundary, simple y-axis anti-aliasing
for x, limits in enumerate(xlimits): for x, limits in enumerate(xlimits):
for y in limits: for yf in limits:
for ypx in range(int(math.floor(y)), int(math.ceil(y)) + 1): for ypx in range(int(math.floor(yf)), int(math.ceil(yf)) + 1):
if 0 <= ypx < height: if 0 <= ypx < height:
off = ypx * width + x off = ypx * width + x
buf[off] = min(255, buf[off] + int((1 - abs(y - ypx)) * 255)) buf[off] = min(255, buf[off] + int((1 - abs(yf - ypx)) * 255))
def line_equation(x1, y1, x2, y2): def line_equation(x1: int, y1: int, x2: int, y2: int) -> Callable[[int], float]:
m = (y2 - y1) / (x2 - x1) m = (y2 - y1) / (x2 - x1)
c = y1 - m * x1 c = y1 - m * x1
def y(x): def y(x: int) -> float:
return m * x + c return m * x + c
return y return y
def triangle(buf, width, height, left=True): def triangle(buf: BufType, width: int, height: int, left: bool = True) -> None:
ay1, by1, y2 = 0, height - 1, height // 2 ay1, by1, y2 = 0, height - 1, height // 2
if left: if left:
x1, x2 = 0, width - 1 x1, x2 = 0, width - 1
@ -179,23 +181,23 @@ def triangle(buf, width, height, left=True):
fill_region(buf, width, height, xlimits) fill_region(buf, width, height, xlimits)
def corner_triangle(buf, width, height, corner): def corner_triangle(buf: BufType, width: int, height: int, corner: str) -> None:
if corner == 'top-right' or corner == 'bottom-left': if corner == 'top-right' or corner == 'bottom-left':
diagonal_y = line_equation(0, 0, width - 1, height - 1) diagonal_y = line_equation(0, 0, width - 1, height - 1)
if corner == 'top-right': if corner == 'top-right':
xlimits = [(0, diagonal_y(x)) for x in range(width)] xlimits = [(0., diagonal_y(x)) for x in range(width)]
elif corner == 'bottom-left': elif corner == 'bottom-left':
xlimits = [(diagonal_y(x), height - 1) for x in range(width)] xlimits = [(diagonal_y(x), height - 1.) for x in range(width)]
else: else:
diagonal_y = line_equation(width - 1, 0, 0, height - 1) diagonal_y = line_equation(width - 1, 0, 0, height - 1)
if corner == 'top-left': if corner == 'top-left':
xlimits = [(0, diagonal_y(x)) for x in range(width)] xlimits = [(0., diagonal_y(x)) for x in range(width)]
elif corner == 'bottom-right': elif corner == 'bottom-right':
xlimits = [(diagonal_y(x), height - 1) for x in range(width)] xlimits = [(diagonal_y(x), height - 1.) for x in range(width)]
fill_region(buf, width, height, xlimits) fill_region(buf, width, height, xlimits)
def antialiased_1px_line(buf, width, height, p1, p2): 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 # Draw an antialiased line using the Wu algorithm
x1, y1 = p1 x1, y1 = p1
x2, y2 = p2 x2, y2 = p2
@ -206,28 +208,28 @@ def antialiased_1px_line(buf, width, height, p1, p2):
if steep: if steep:
x1, y1, x2, y2, dx, dy = y1, x1, y2, x2, dy, dx x1, y1, x2, y2, dx, dy = y1, x1, y2, x2, dy, dx
def p(x, y): def p(x: int, y: int) -> Tuple[int, int]:
return y, x return y, x
else: else:
def p(x, y): def p(x: int, y: int) -> Tuple[int, int]:
return x, y return x, y
if x2 < x1: if x2 < x1:
x1, x2, y1, y2 = x2, x1, y2, y1 x1, x2, y1, y2 = x2, x1, y2, y1
def fpart(x): def fpart(x: float) -> float:
return x - int(x) return x - int(x)
def rfpart(x): def rfpart(x: float) -> float:
return 1 - fpart(x) return 1 - fpart(x)
def putpixel(p, alpha): def putpixel(p: Tuple[int, int], alpha: float) -> None:
x, y = p x, y = p
off = int(x + y * width) off = int(x + y * width)
if 0 <= off < off_limit: if 0 <= off < off_limit:
buf[off] = int(min(buf[off] + (alpha * 255), 255)) buf[off] = int(min(buf[off] + (alpha * 255), 255))
def draw_endpoint(pt): def draw_endpoint(pt: Tuple[int, int]) -> int:
x, y = pt x, y = pt
xend = round(x) xend = round(x)
yend = y + grad * (xend - x) yend = y + grad * (xend - x)
@ -254,7 +256,7 @@ def antialiased_1px_line(buf, width, height, p1, p2):
intery += grad intery += grad
def antialiased_line(buf, width, height, p1, p2, level=1): def antialiased_line(buf: BufType, width: int, height: int, p1: Tuple[int, int], p2: Tuple[int, int], level: int = 1) -> None:
th = thickness(level) th = thickness(level)
if th < 2: if th < 2:
return antialiased_1px_line(buf, width, height, p1, p2) return antialiased_1px_line(buf, width, height, p1, p2)
@ -265,7 +267,7 @@ def antialiased_line(buf, width, height, p1, p2, level=1):
antialiased_1px_line(buf, width, height, (x1, y1 + delta), (x2, y2 + delta)) antialiased_1px_line(buf, width, height, (x1, y1 + delta), (x2, y2 + delta))
def cross_line(buf, width, height, left=True, level=1): def cross_line(buf: BufType, width: int, height: int, left: bool = True, level: int = 1) -> None:
if left: if left:
p1, p2 = (0, 0), (width - 1, height - 1) p1, p2 = (0, 0), (width - 1, height - 1)
else: else:
@ -273,7 +275,7 @@ def cross_line(buf, width, height, left=True, level=1):
antialiased_line(buf, width, height, p1, p2, level=level) antialiased_line(buf, width, height, p1, p2, level=level)
def half_cross_line(buf, width, height, which='tl', level=1): def half_cross_line(buf: BufType, width: int, height: int, which: str = 'tl', level: int = 1) -> None:
my = (height - 1) // 2 my = (height - 1) // 2
if which == 'tl': if which == 'tl':
p1 = 0, 0 p1 = 0, 0
@ -290,11 +292,14 @@ def half_cross_line(buf, width, height, which='tl', level=1):
antialiased_line(buf, width, height, p1, p2, level=level) antialiased_line(buf, width, height, p1, p2, level=level)
def cubic_bezier(start, end, c1, c2): BezierFunc = Callable[[float], float]
def bezier_eq(p0, p1, p2, p3):
def f(t): def cubic_bezier(start: Tuple[int, int], end: Tuple[int, int], c1: Tuple[int, int], c2: Tuple[int, int]) -> Tuple[BezierFunc, BezierFunc]:
def bezier_eq(p0: int, p1: int, p2: int, p3: int) -> BezierFunc:
def f(t: float) -> float:
tm1 = 1 - t tm1 = 1 - t
tm1_3 = tm1 * tm1 * tm1 tm1_3 = tm1 * tm1 * tm1
t_3 = t * t * t t_3 = t * t * t
@ -306,7 +311,7 @@ def cubic_bezier(start, end, c1, c2):
return bezier_x, bezier_y return bezier_x, bezier_y
def find_bezier_for_D(width, height): def find_bezier_for_D(width: int, height: int) -> int:
cx = last_cx = width - 1 cx = last_cx = width - 1
start = (0, 0) start = (0, 0)
end = (0, height - 1) end = (0, height - 1)
@ -320,12 +325,12 @@ def find_bezier_for_D(width, height):
cx += 1 cx += 1
def get_bezier_limits(bezier_x, bezier_y): def get_bezier_limits(bezier_x: BezierFunc, bezier_y: BezierFunc) -> Generator[Tuple[float, float], None, int]:
start_x = bezier_x(0) start_x = int(bezier_x(0))
max_x = int(bezier_x(0.5)) max_x = int(bezier_x(0.5))
last_t, t_limit = 0, 0.5 last_t, t_limit = 0., 0.5
def find_t_for_x(x, start_t): def find_t_for_x(x: int, start_t: float) -> float:
if abs(bezier_x(start_t) - x) < 0.1: if abs(bezier_x(start_t) - x) < 0.1:
return start_t return start_t
increment = t_limit - start_t increment = t_limit - start_t
@ -354,7 +359,7 @@ def get_bezier_limits(bezier_x, bezier_y):
yield upper, lower yield upper, lower
def D(buf, width, height, left=True): def D(buf: BufType, width: int, height: int, left: bool = True) -> None:
c1x = find_bezier_for_D(width, height) c1x = find_bezier_for_D(width, height)
start = (0, 0) start = (0, 0)
end = (0, height - 1) end = (0, height - 1)
@ -374,7 +379,7 @@ def D(buf, width, height, left=True):
buf[offset + dest_x] = mbuf[offset + src_x] buf[offset + dest_x] = mbuf[offset + src_x]
def half_dhline(buf, width, height, level=1, which='left', only=None): def half_dhline(buf: BufType, width: int, height: int, level: int = 1, which: str = 'left', only: Optional[str] = None) -> Tuple[int, int]:
x1, x2 = (0, width // 2) if which == 'left' else (width // 2, width) x1, x2 = (0, width // 2) if which == 'left' else (width // 2, width)
gap = thickness(level + 1, horizontal=False) gap = thickness(level + 1, horizontal=False)
if only != 'bottom': if only != 'bottom':
@ -384,7 +389,7 @@ def half_dhline(buf, width, height, level=1, which='left', only=None):
return height // 2 - gap, height // 2 + gap return height // 2 - gap, height // 2 + gap
def half_dvline(buf, width, height, level=1, which='top', only=None): def half_dvline(buf: BufType, width: int, height: int, level: int = 1, which: str = 'top', only: Optional[str] = None) -> Tuple[int, int]:
y1, y2 = (0, height // 2) if which == 'top' else (height // 2, height) y1, y2 = (0, height // 2) if which == 'top' else (height // 2, height)
gap = thickness(level + 1, horizontal=True) gap = thickness(level + 1, horizontal=True)
if only != 'right': if only != 'right':
@ -394,33 +399,33 @@ def half_dvline(buf, width, height, level=1, which='top', only=None):
return width // 2 - gap, width // 2 + gap return width // 2 - gap, width // 2 + gap
def dvline(*s, only=None, level=1): def dvline(buf: BufType, width: int, height: int, only=None, level=1):
half_dvline(*s, only=only, level=level) half_dvline(buf, width, height, only=only, level=level)
return half_dvline(*s, only=only, which='bottom', level=level) return half_dvline(buf, width, height, only=only, which='bottom', level=level)
def dhline(*s, only=None, level=1): def dhline(buf: BufType, width: int, height: int, only=None, level=1):
half_dhline(*s, only=only, level=level) half_dhline(buf, width, height, only=only, level=level)
return half_dhline(*s, only=only, which='bottom', level=level) return half_dhline(buf, width, height, only=only, which='bottom', level=level)
def dvcorner(*s, level=1, which=''): def dvcorner(buf: BufType, width: int, height: int, level=1, which=''):
hw = 'right' if which in '╒╘' else 'left' hw = 'right' if which in '╒╘' else 'left'
half_dhline(*s, which=hw) half_dhline(buf, width, height, which=hw)
vw = 'top' if which in '╘╛' else 'bottom' vw = 'top' if which in '╘╛' else 'bottom'
gap = thickness(level + 1, horizontal=False) gap = thickness(level + 1, horizontal=False)
half_vline(*s, which=vw, extend_by=gap // 2 + thickness(level, horizontal=False)) half_vline(buf, width, height, which=vw, extend_by=gap // 2 + thickness(level, horizontal=False))
def dhcorner(*s, level=1, which=''): def dhcorner(buf: BufType, width: int, height: int, level=1, which=''):
vw = 'top' if which in '╙╜' else 'bottom' vw = 'top' if which in '╙╜' else 'bottom'
half_dvline(*s, which=vw) half_dvline(buf, width, height, which=vw)
hw = 'right' if which in '╓╙' else 'left' hw = 'right' if which in '╓╙' else 'left'
gap = thickness(level + 1, horizontal=True) gap = thickness(level + 1, horizontal=True)
half_hline(*s, which=hw, extend_by=gap // 2 + thickness(level, horizontal=True)) half_hline(buf, width, height, which=hw, extend_by=gap // 2 + thickness(level, horizontal=True))
def dcorner(buf, width, height, level=1, which=''): def dcorner(buf: BufType, width: int, height: int, level: int = 1, which: str = '') -> None:
hw = 'right' if which in '╔╚' else 'left' hw = 'right' if which in '╔╚' else 'left'
vw = 'top' if which in '╚╝' else 'bottom' vw = 'top' if which in '╚╝' else 'bottom'
hgap = thickness(level + 1, horizontal=False) hgap = thickness(level + 1, horizontal=False)
@ -452,18 +457,18 @@ def dcorner(buf, width, height, level=1, which='╔'):
draw_vline(buf, width, y1, y2, width // 2 + xdelta, level) draw_vline(buf, width, y1, y2, width // 2 + xdelta, level)
def dpip(*a, level=1, which=''): def dpip(buf: BufType, width: int, height: int, level: int = 1, which: str = '') -> None:
if which in '╟╢': if which in '╟╢':
left, right = dvline(*a) left, right = dvline(buf, width, height)
x1, x2 = (0, left) if which == '' else (right, a[1]) x1, x2 = (0, left) if which == '' else (right, width)
draw_hline(a[0], a[1], x1, x2, a[2] // 2, level) draw_hline(buf, width, x1, x2, height // 2, level)
else: else:
top, bottom = dhline(*a) top, bottom = dhline(buf, width, height)
y1, y2 = (0, top) if which == '' else (bottom, a[2]) y1, y2 = (0, top) if which == '' else (bottom, height)
draw_vline(a[0], a[1], y1, y2, a[1] // 2, level) draw_vline(buf, width, y1, y2, width // 2, level)
def inner_corner(buf, width, height, which='tl', level=1): def inner_corner(buf: BufType, width: int, height: int, which: str = 'tl', level: int = 1) -> None:
hgap = thickness(level + 1, horizontal=True) hgap = thickness(level + 1, horizontal=True)
vgap = thickness(level + 1, horizontal=False) vgap = thickness(level + 1, horizontal=False)
vthick = thickness(level, horizontal=True) // 2 vthick = thickness(level, horizontal=True) // 2
@ -475,7 +480,7 @@ def inner_corner(buf, width, height, which='tl', level=1):
draw_vline(buf, width, y1, y2, width // 2 + (xd * hgap), level) draw_vline(buf, width, y1, y2, width // 2 + (xd * hgap), level)
def vblock(buf, width, height, frac=1, gravity='top'): def vblock(buf: BufType, width: int, height: int, frac: float = 1., gravity: str = 'top') -> None:
num_rows = min(height, round(frac * height)) num_rows = min(height, round(frac * height))
start = 0 if gravity == 'top' else height - num_rows start = 0 if gravity == 'top' else height - num_rows
for r in range(start, start + num_rows): for r in range(start, start + num_rows):
@ -484,7 +489,7 @@ def vblock(buf, width, height, frac=1, gravity='top'):
buf[c] = 255 buf[c] = 255
def hblock(buf, width, height, frac=1, gravity='left'): def hblock(buf: BufType, width: int, height: int, frac: float = 1., gravity: str = 'left') -> None:
num_cols = min(width, round(frac * width)) num_cols = min(width, round(frac * width))
start = 0 if gravity == 'left' else width - num_cols start = 0 if gravity == 'left' else width - num_cols
for r in range(height): for r in range(height):
@ -493,7 +498,7 @@ def hblock(buf, width, height, frac=1, gravity='left'):
buf[c] = 255 buf[c] = 255
def shade(buf, width, height, light=False, invert=False): def shade(buf: BufType, width: int, height: int, light: bool = False, invert: bool = False) -> None:
square_sz = max(1, width // 12) square_sz = max(1, width // 12)
number_of_rows = height // square_sz number_of_rows = height // square_sz
number_of_cols = width // square_sz number_of_cols = width // square_sz
@ -544,7 +549,7 @@ def quad(buf, width, height, x=0, y=0):
buf[off + c] = 255 buf[off + c] = 255
box_chars = { box_chars: Dict[str, List[Callable]] = {
'': [hline], '': [hline],
'': [p(hline, level=3)], '': [p(hline, level=3)],
'': [vline], '': [vline],
@ -660,7 +665,7 @@ for chars, func_ in (('╒╕╘╛', dvcorner), ('╓╖╙╜', dhcorner), ('
box_chars[ch] = [p(cast(Callable, func_), which=ch)] box_chars[ch] = [p(cast(Callable, func_), which=ch)]
def render_box_char(ch, buf, width, height, dpi=96.0): def render_box_char(ch: str, buf: BufType, width: int, height: int, dpi: float = 96.0) -> BufType:
global _dpi global _dpi
_dpi = dpi _dpi = dpi
for func in box_chars[ch]: for func in box_chars[ch]:
@ -668,7 +673,7 @@ def render_box_char(ch, buf, width, height, dpi=96.0):
return buf return buf
def render_missing_glyph(buf, width, height): def render_missing_glyph(buf: BufType, width: int, height: int) -> None:
hgap = thickness(level=0, horizontal=True) + 1 hgap = thickness(level=0, horizontal=True) + 1
vgap = thickness(level=0, horizontal=False) + 1 vgap = thickness(level=0, horizontal=False) + 1
draw_hline(buf, width, hgap, width - hgap + 1, vgap, 0) draw_hline(buf, width, hgap, width - hgap + 1, vgap, 0)
@ -677,7 +682,7 @@ def render_missing_glyph(buf, width, height):
draw_vline(buf, width, vgap, height - vgap + 1, width - hgap, 0) draw_vline(buf, width, vgap, height - vgap + 1, width - hgap, 0)
def test_char(ch, sz=48): def test_char(ch: str, sz: int = 48) -> None:
# kitty +runpy "from kitty.fonts.box_drawing import test_char; test_char('XXX')" # kitty +runpy "from kitty.fonts.box_drawing import test_char; test_char('XXX')"
from .render import display_bitmap, setup_for_testing from .render import display_bitmap, setup_for_testing
from kitty.fast_data_types import concat_cells, set_send_sprite_to_gpu from kitty.fast_data_types import concat_cells, set_send_sprite_to_gpu
@ -697,7 +702,7 @@ def test_char(ch, sz=48):
set_send_sprite_to_gpu(None) set_send_sprite_to_gpu(None)
def test_drawing(sz=48, family='monospace'): def test_drawing(sz: int = 48, family: str = 'monospace') -> None:
from .render import display_bitmap, setup_for_testing from .render import display_bitmap, setup_for_testing
from kitty.fast_data_types import concat_cells, set_send_sprite_to_gpu from kitty.fast_data_types import concat_cells, set_send_sprite_to_gpu

View File

@ -50,11 +50,11 @@ class Rendering(BaseTest):
def test_box_drawing(self): def test_box_drawing(self):
prerendered = len(self.sprites) prerendered = len(self.sprites)
s = self.create_screen(cols=len(box_chars), lines=1, scrollback=0) s = self.create_screen(cols=len(box_chars) + 1, lines=1, scrollback=0)
s.draw(''.join(box_chars)) s.draw(''.join(box_chars))
line = s.line(0) line = s.line(0)
test_render_line(line) test_render_line(line)
self.assertEqual(len(self.sprites), prerendered + len(box_chars)) self.assertEqual(len(self.sprites) - prerendered, len(box_chars))
def test_font_rendering(self): def test_font_rendering(self):
render_string('ab\u0347\u0305你好|\U0001F601|\U0001F64f|\U0001F63a|') render_string('ab\u0347\u0305你好|\U0001F601|\U0001F64f|\U0001F63a|')