From 6a65d0a7f2e9784588bd3ee46f86ede8fb53e1ad Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 27 Oct 2026 21:00:07 +0800 Subject: [PATCH] fix(fonttools): scale Bezier subdivision to the pixel error Flatten each quadratic with a segment count derived from |p0 - 2*p1 + p2| in pixel space, targeting < 0.5 px error. The count grows with the rendered font size. All-off-curve contours now use midpoint quadratics instead of polygon edges. --- vpsdk/fonttools/fonttools.vp | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/vpsdk/fonttools/fonttools.vp b/vpsdk/fonttools/fonttools.vp index 1a9a041..bcbe479 100644 --- a/vpsdk/fonttools/fonttools.vp +++ b/vpsdk/fonttools/fonttools.vp @@ -176,10 +176,18 @@ def _add_edge(x0: float, y0: float, x1: float, y1: float): _edge_count += 1 def _quad(x0: float, y0: float, x1: float, y1: float, x2: float, y2: float): + # Maximum distance from the chord is |p0 - 2*p1 + p2| / 4. + # Choose n so the error per segment is below 0.5 px. + ddx: float = x0 - 2.0 * x1 + x2 + ddy: float = y0 - 2.0 * y1 + y2 + d: float = math.sqrtf(ddx * ddx + ddy * ddy) + n: int = int(math.floorf(math.sqrtf(d / 2.0))) + 1 + if n < 8: n = 8 + if n > 64: n = 64 px: float = x0 py: float = y0 - for s in range(1, 9): - t: float = float(s) / 8.0 + for s in range(1, n + 1): + t: float = float(s) / float(n) mt: float = 1.0 - t x: float = mt * mt * x0 + 2.0 * mt * t * x1 + t * t * x2 y: float = mt * mt * y0 + 2.0 * mt * t * y1 + t * t * y2 @@ -194,10 +202,21 @@ def _build_contour(start: int, end: int): first_on = i break if first_on < 0: - for i in range(start, end + 1): - j: int = i + 1 - if j > end: j = start - _add_edge(_px[i], _py[i], _px[j], _py[j]) + sx: float = (_px[end] + _px[start]) / 2.0 + sy: float = (_py[end] + _py[start]) / 2.0 + cx: float = sx + cy: float = sy + i: int = start + while i <= end: + nx: int = i + 1 + if nx > end: nx = start + ex: float = (_px[i] + _px[nx]) / 2.0 + ey: float = (_py[i] + _py[nx]) / 2.0 + _quad(cx, cy, _px[i], _py[i], ex, ey) + cx = ex + cy = ey + i += 1 + _add_edge(cx, cy, sx, sy) return cx: float = _px[first_on] cy: float = _py[first_on]