Skip to content

Instantly share code, notes, and snippets.

@manjaroman2
Created January 5, 2026 19:18
Show Gist options
  • Select an option

  • Save manjaroman2/8527246d05573004a7552d902733dbf6 to your computer and use it in GitHub Desktop.

Select an option

Save manjaroman2/8527246d05573004a7552d902733dbf6 to your computer and use it in GitHub Desktop.
Binäre Rasterisierung in Python
import numpy as np
import matplotlib.pyplot as plt
from fontTools.ttLib import TTFont
from fontTools.pens.recordingPen import RecordingPen
# Font laden und Glyph-Konturen extrahieren
font = TTFont("DejaVuSerif.ttf")
glyph_set = font.getGlyphSet()
glyph = glyph_set["ampersand"] # oder: font.getBestCmap()[ord("&")]
pen = RecordingPen()
glyph.draw(pen)
def bezier_quadratic(p0, p1, p2, steps=10):
"""Quadratische Bézierkurve in Liniensegmente"""
t = np.linspace(0, 1, steps)[:, None]
return (1-t)**2 * p0 + 2*(1-t)*t * p1 + t**2 * p2
def bezier_cubic(p0, p1, p2, p3, steps=10):
"""Kubische Bézierkurve in Liniensegmente"""
t = np.linspace(0, 1, steps)[:, None]
return (1-t)**3*p0 + 3*(1-t)**2*t*p1 + 3*(1-t)*t**2*p2 + t**3*p3
# Konturen aus RecordingPen extrahieren
contours = []
current = []
pos = np.array([0.0, 0.0])
for op, args in pen.value:
if op == "moveTo":
if current:
contours.append(np.array(current))
current = [args[0]]
pos = np.array(args[0])
elif op == "lineTo":
current.append(args[0])
pos = np.array(args[0])
elif op == "qCurveTo":
# args = (off-curve*, on-curve) - letzter Punkt ist immer on-curve
points = [np.array(p) for p in args]
if len(points) == 1:
# Nur ein Punkt = Linie
current.append(points[0].tolist())
else:
off_curve = points[:-1]
end_point = points[-1]
p0 = pos
for i, ctrl in enumerate(off_curve):
if i == len(off_curve) - 1:
p2 = end_point
else:
p2 = (ctrl + off_curve[i + 1]) / 2 # impliziter on-curve
curve = bezier_quadratic(p0, ctrl, p2)
current.extend(curve[1:].tolist())
p0 = p2 # <- das fehlte effektiv
pos = np.array(args[-1])
elif op == "curveTo":
# Kubische Bézier (selten in TTF, häufig in OTF)
curve = bezier_cubic(pos, np.array(args[0]), np.array(args[1]), np.array(args[2]))
current.extend(curve[1:].tolist())
pos = np.array(args[-1])
elif op == "closePath":
if current:
contours.append(np.array(current))
current = []
# Alle Konturen zusammenfassen
all_points = np.vstack(contours)
min_xy = all_points.min(axis=0)
max_xy = all_points.max(axis=0)
W, H = 32, 32
margin = 4
def normalize(contour):
c = contour - min_xy
scale = min((W - 2*margin) / (max_xy[0] - min_xy[0]),
(H - 2*margin) / (max_xy[1] - min_xy[1]))
return c * scale + margin
contours = [normalize(c) for c in contours]
# Punkt-in-Polygon mit Even-Odd-Fill (alle Konturen)
def inside(x, y):
crossings = 0
for poly in contours:
n = len(poly)
for i in range(n):
x1, y1 = poly[i]
x2, y2 = poly[(i + 1) % n]
if (y1 > y) != (y2 > y):
t = (y - y1) / (y2 - y1)
if x < x1 + t * (x2 - x1):
crossings += 1
return crossings % 2 == 1
bitmap = np.zeros((H, W), dtype=np.uint8)
for y in range(H):
for x in range(W):
if inside(x + 0.5, y + 0.5):
bitmap[y, x] = 255
# Anzeige
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 8))
# Links: Outlines
ax1.imshow(np.zeros((H, W)), cmap="gray", origin="lower", vmin=0, vmax=255)
for contour in contours:
closed = np.vstack([contour, contour[0]])
ax1.plot(closed[:, 0], closed[:, 1], "r-", linewidth=1.0)
ax1.set_xlim(-0.5, W - 0.5)
ax1.set_ylim(-0.5, H - 0.5)
ax1.set_xticks(np.arange(-0.5, W, 1))
ax1.set_yticks(np.arange(-0.5, H, 1))
ax1.grid(color="lightgray", linewidth=0.3)
ax1.set_xticklabels([])
ax1.set_yticklabels([])
ax1.set_aspect("equal")
ax1.set_title("Outlines")
# Rechts: Bitmap
ax2.imshow(bitmap, cmap="gray", origin="lower")
ax2.set_xticks(np.arange(-0.5, W, 1))
ax2.set_yticks(np.arange(-0.5, H, 1))
ax2.grid(color="lightgray", linewidth=0.3)
ax2.set_xticklabels([])
ax2.set_yticklabels([])
ax2.set_title("Binary Rasterization")
plt.suptitle("Ampersand (&)")
plt.tight_layout()
plt.savefig("Ampersand_BinaryRaster.png", bbox_inches="tight", dpi=300)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment