Lumen uses two typefaces: Inter for UI copy and JetBrains Mono for the terminal and code surfaces. Both are rendered through the same path — a signed-distance field rasterizer that produces gamma-correct partial-coverage anti-aliasing at any scale. The SDF builder and sampler are now x86-verified. This closes sigil-os#15, the deferred razor-crisp font quality pass that was waiting on this seam.
What SDF rendering is and why it matters
A bitmap font atlas stores a rasterized image of each glyph at a fixed size. Scale it up and the pixels become visible; scale it down and fine strokes disappear. An SDF atlas stores something different: for each texel, the signed distance to the nearest edge of the glyph outline. Positive values are inside the glyph; negative values are outside; zero is exactly on the edge.
To render a glyph at any size, the GPU samples the SDF atlas at the appropriate UV coordinates and applies a threshold around zero. Pixels with distance > threshold are the glyph foreground colour; pixels with distance < −threshold are fully transparent; pixels in the range [−threshold, +threshold] get a coverage weight proportional to their distance from the edge — that's the anti-aliasing. One SDF atlas, correct rendering at 8pt or 128pt.
The threshold can also be varied to fake bold or italic from the same atlas — bold widens the positive region, italic shears the UV sample coordinates. This is how Lumen can serve multiple weights and styles from a single atlas per typeface.
sdf_build: generating the distance field
sdf_build(atlas, glyph_idx, bitmap_row) takes a single row of a bitmap glyph (8 bits, MSB-first) and writes 8 SDF texels into the atlas at the corresponding position. For each of the 8 texels, it computes the distance to the nearest edge by scanning outward in both directions:
for each texel i in 0..7:
inside = bit_i_is_set(bitmap_row)
d = 0
while d < MAX_DIST:
if bit at (i + d) or (i - d) crosses the inside/outside boundary:
break
d += 1
texel[i] = encode_distance(d, inside)
# inside: 128 + d*scale (positive half, capped at 255)
# outside: 128 - d*scale (negative half, floored at 0)
The encoding maps distance=0 (on the edge) to texel value 128, deep inside to values approaching 255, and deep outside to values approaching 0. The sampler threshold is applied symmetrically around 128.
Verified distance oracle
The test stages a half-on bitmap for the letter 'A': columns 0–3 are lit (inside), columns 4–7 are unlit (outside). This gives a clean inside/outside boundary at column 3.5 — exactly where the SDF values should transition through 128.
The edge sits between t3 (144, just inside) and t4 (112, just outside). A sampler threshold of ±16 around 128 produces smooth anti-aliasing across that transition. Texels deeper inside (t0=192, t1=176) or further outside (t7=64) are fully opaque or fully transparent respectively.
The GPU sampling path
The GPU path reads the SDF atlas from VRAM (allocated via gpu_tex), samples at sub-texel UV coordinates using bilinear interpolation between adjacent texels, and applies the threshold per-fragment. The result is smooth coverage at any scale with no aliasing artefacts.
On hardware without GPU acceleration (Pi 3 floor), the scalar path in core/sdf.sg performs the same sample-and-threshold operation in software. The coverage value becomes a blend weight applied to the foreground colour before writing to the framebuffer. The output is identical to the GPU path; only the throughput differs.
This dual-path structure follows the GPU-first standing rule: one rasterizer, two backends, the GPU path always preferred, the scalar path always present. The Lumen compositor calls the same ui_text_packed function regardless of which backend is active.
Lumen typography in practice
The 0.5.0 terminal shipped with ui_text_packed wired for anti-aliasing, but the SDF sampler was a stub — it produced correct output but without the signed-distance field quality. The bitmap glyph path (gglyph, verified in the GLYPH-PASS test) handled the boot splash and early UI text. With sdf_build and the sampler now x86-verified, the full SDF path is available for the Lumen font service to load Inter and JetBrains Mono atlases into.
The font load seam — reading the TTF, building the SDF atlas, uploading it to VRAM — is the remaining step before the terminal renders razor-crisp glyphs at any point size. That seam depends on the font binary being accessible via the VFS, which is now possible with the FS stack complete. The pieces are ready; the integration is the next pass.