Created
January 5, 2026 19:18
-
-
Save manjaroman2/8527246d05573004a7552d902733dbf6 to your computer and use it in GitHub Desktop.
Binäre Rasterisierung in Python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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