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.
This commit is contained in:
parent
145ec86d3b
commit
4309e2371d
@ -162,18 +162,22 @@ def downsample(src: BufType, dest: BufType, dest_width: int, dest_height: int, f
|
|||||||
for y in range(dest_height):
|
for y in range(dest_height):
|
||||||
offset = dest_width * y
|
offset = dest_width * y
|
||||||
for x in range(dest_width):
|
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:
|
def supersampled(supersample_factor: int = 4) -> Callable:
|
||||||
# Anti-alias the drawing performed by the wrapped function by
|
# Anti-alias the drawing performed by the wrapped function by
|
||||||
# using supersampling
|
# using supersampling
|
||||||
|
|
||||||
|
class SSByteArray(bytearray):
|
||||||
|
supersample_factor = 1
|
||||||
|
|
||||||
def create_wrapper(f: Callable) -> Callable:
|
def create_wrapper(f: Callable) -> Callable:
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def supersampled_wrapper(buf: BufType, width: int, height: int, *args: Any, **kw: Any) -> None:
|
def supersampled_wrapper(buf: BufType, width: int, height: int, *args: Any, **kw: Any) -> None:
|
||||||
w, h = supersample_factor * width, supersample_factor * height
|
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)
|
f(ssbuf, w, h, *args, **kw)
|
||||||
downsample(ssbuf, buf, width, height, factor=supersample_factor)
|
downsample(ssbuf, buf, width, height, factor=supersample_factor)
|
||||||
return supersampled_wrapper
|
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)
|
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:
|
def thick_line(buf: BufType, width: int, height: int, thickness_in_pixels: int, p1: Tuple[int, int], p2: Tuple[int, int]) -> None:
|
||||||
# Draw an antialiased line using the Wu algorithm
|
if p1[0] > p2[0]:
|
||||||
x1, y1 = p1
|
p1, p2 = p2, p1
|
||||||
x2, y2 = p2
|
leq = line_equation(*p1, *p2)
|
||||||
dx, dy = x2 - x1, y2 - y1
|
for x in range(p1[0], p2[0] + 1):
|
||||||
off_limit = height * width
|
if 0 <= x < width:
|
||||||
steep = abs(dx) < abs(dy)
|
y_p = leq(x)
|
||||||
|
if thickness_in_pixels <= 1:
|
||||||
if steep:
|
r = range(int(y_p), int(y_p) + 1)
|
||||||
x1, y1, x2, y2, dx, dy = y1, x1, y2, x2, dy, dx
|
else:
|
||||||
|
delta = thickness_in_pixels // 2
|
||||||
def p(x: int, y: int) -> Tuple[int, int]:
|
r = range(int(y_p) - delta, int(y_p) + delta + (thickness_in_pixels % 2))
|
||||||
return y, x
|
for y in r:
|
||||||
else:
|
if 0 <= y < height:
|
||||||
def p(x: int, y: int) -> Tuple[int, int]:
|
buf[x + y * width] = 255
|
||||||
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))
|
|
||||||
|
|
||||||
|
|
||||||
|
@supersampled()
|
||||||
def cross_line(buf: BufType, width: int, height: int, left: bool = True, level: int = 1) -> None:
|
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:
|
||||||
p1, p2 = (width - 1, 0), (0, height - 1)
|
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:
|
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
|
my = (height - 1) // 2
|
||||||
if which == 'tl':
|
if which == 'tl':
|
||||||
p1 = 0, 0
|
p1 = 0, 0
|
||||||
@ -319,7 +275,7 @@ def half_cross_line(buf: BufType, width: int, height: int, which: str = 'tl', le
|
|||||||
else:
|
else:
|
||||||
p2 = width - 1, height - 1
|
p2 = width - 1, height - 1
|
||||||
p1 = 0, my
|
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]
|
BezierFunc = Callable[[float], float]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user