Skip to content

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.