msdfgen
FreeBodyEngine.font.msdfgen
#
A real multi-channel signed distance field (MSDF) generator, implementing Chlumsky's technique (https://github.com/Chlumsky/msdfgen): each glyph outline's edges are colored into cyan/magenta/yellow (two of R/G/B each) so that sharp corners survive being packed into 3 channels, and the final alpha in a shader is reconstructed via median(r, g, b) - see engine_assets/text/text.fbfrag.
Scope, stated plainly: - Real quadratic and cubic Bezier curve reconstruction from FreeType outline tags (not the previous version's "every point is a straight line" bug). - Real per-channel signed distance and real nonzero/even-odd winding-rule sign (via ray casting over the actual reconstructed contours - not the previous version's "sign from the nearest edge's local cross product", which was wrong at concavities and multi-contour glyphs). - Curves are subdivided into short line segments for the distance computation specifically (still colored and corner-detected at the true curve level) so the whole raster can be vectorized with NumPy instead of a per-pixel Python loop - the previous implementation's per-pixel loop was already reported "extremely slow" even without doing this much extra math per pixel. This is indistinguishable from true analytic curve distance at normal text/UI sizes, but is a real, disclosed simplification from doing Newton's-method refinement against the exact curve at every pixel. - msdfgen's "error correction" pass (patching rare median-disagreement artifacts at very acute angles) is not implemented - a reasonable, documented limitation affecting only pathological glyph shapes.
CORNER_ANGLE_THRESHOLD = math.radians(15)
module-attribute
#
CURVE_SEGMENTS = 8
module-attribute
#
CYAN = (0, 1, 1)
module-attribute
#
MAGENTA = (1, 0, 1)
module-attribute
#
WHITE = (1, 1, 1)
module-attribute
#
YELLOW = (1, 1, 0)
module-attribute
#
Edge(kind, points)
#
One outline edge (a line, quadratic, or cubic Bezier segment)
between two on-curve points, tagged with the color (see WHITE/CYAN/
MAGENTA/YELLOW) that color_edges assigns it for MSDF channel
selection.
Stores the edge's kind and its control points (2 for "line", 3
for "quad", 4 for "cubic"); color defaults to WHITE until
color_edges assigns a real per-channel color.
color = WHITE
instance-attribute
#
kind = kind
instance-attribute
#
points = points
instance-attribute
#
direction_at_end()
#
Returns the unit tangent direction arriving at the edge's end point (from its last interior/control point). Falls back to +X for a degenerate zero-length final segment rather than dividing by zero.
direction_at_start()
#
Returns the unit tangent direction leaving the edge's start point (toward its first interior/control point). Falls back to +X for a degenerate zero-length initial segment rather than dividing by zero.
end()
#
Returns the edge's ending on-curve point - for "quad"/"cubic" edges this is the true curve endpoint, not an intermediate control point.
flatten(segments=CURVE_SEGMENTS)
#
Returns the edge's start point followed by segments interior
samples (used for distance + winding, not for edge coloring).
start()
#
Returns the edge's starting on-curve point.
color_edges(contour, threshold=CORNER_ANGLE_THRESHOLD)
#
Chlumsky's edge-coloring heuristic: cycle cyan/magenta/yellow at each detected corner (a sharp direction change between consecutive edges), so any two color-adjacent edges share exactly one channel. A contour with no corners (a fully smooth loop) gets one uniform color instead.
contour_to_edges(points, tags)
#
Reconstructs one contour's real line/quadratic/cubic edges from FreeType's point/tag arrays, honoring the standard TrueType convention that two consecutive quadratic off-curve points imply an on-curve midpoint between them.
generate_char(char, face, atlas_size, range_px=4.0)
#
Generates one glyph's MSDF bitmap plus its em-relative metrics. A
single scale (derived from the face's units_per_EM, not this glyph's
own bounding box) is used for every glyph in a font, so relative glyph
sizes are preserved across the whole atlas - the previous
implementation independently rescaled each glyph's own bounding box to
fill its cell, destroying relative sizing.
generate_msdf(contours, width, height, scale_vec, offset, range_px)
#
Rasterizes contours (each already colored via color_edges) into a
width x height MSDF. scale_vec/offset map outline-space
coordinates (FreeType convention: Y increases upward) to output-pixel
space (row 0 = top, Y increases downward) - scale_vec is
(scale, -scale) so the Y component of every transformed point is also
flipped; range_px is the SDF's output-texel spread on each side of the
true edge. Rectangular (not square) since generate_char() sizes this to
each glyph's own tight bounding box rather than a fixed per-font cell.
get_global_metadata(face, em_size)
#
Font-wide metrics, em-relative (divide FreeType's 26.6-fixed-point
values by em_size * 64). Must be called after set_char_size().
get_outline(face, char)
#
Loads char's outline from face and returns the raw FreeType
(points, tags, contours) triple for contour_to_edges to reconstruct
into real line/quadratic/cubic edges. Always loads with
FT_LOAD_NO_SCALE - see the inline comment below for why every caller
that consumes this module's output must agree on that convention.