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.
This commit is contained in:
TermiNexus committed 2026-10-27 21:00:07 +08:00
1 parent faab22c40e
commit 6a65d0a7f2
1 file changed
+25 -6
+25 -6
View File
@@ -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]