Ports ZIG/src/font/{Metrics,Atlas,Glyph}.zig and the whole sprite font
(font/sprite/**): box drawing, block elements, braille, branch/git
graph symbols, geometric shapes, powerline, and the symbols for legacy
computing (+ supplement, incl. the octants table).
Ghostty draws sprites with z2d; we use tiny-skia for path coverage,
composited onto an alpha8 canvas, while following the Zig geometry
(positions, thickness, rounding). Comptime discovery of draw functions
is replaced by explicit, overlap-checked codepoint range tables.
Upstream reference sheets are vendored in testdata/ and compared with a
tolerance: braille and U+1CD00 are pixel-identical; other sheets differ
only in antialiasing on thin diagonals/curves. Review renders are written
to target/sprite_sheets/.
The crate temporarily drops its config/unicode/terminal deps until the
rest of the font engine is ported (Phase 3).
Test Plan:
- cargo test -p ghostty-font (49 tests incl. reference sheet comparison)
- inspect target/sprite_sheets/review-all-18x36.png
4.7 KiB
4.7 KiB
ghostty-font — W7 (Metrics, Atlas, Glyph, sprite font)
Ported (Zig -> Rust, all under crates/ghostty-font/src/)
font/Metrics.zig->metrics.rs(Metrics,FaceMetrics,Modifier,ModifierSet = HashMap<Key, Modifier>,Key,Modifier::parse/parse_cli/format_entry/apply_{u32,i32,f64}).font/Atlas.zig->atlas.rs;font/Glyph.zig->glyph.rs.font/sprite.zig->sprite/mod.rs(Spriteenum, re-exports).font/sprite/Face.zig->sprite/face.rs;sprite/canvas.zig->sprite/canvas.rs.sprite/draw/{common,special,block,box,braille,branch,geometric_shapes,powerline,symbols_for_legacy_computing,symbols_for_legacy_computing_supplement}.zig->sprite/draw/*.rs(box.zig->box_.rssinceboxis a keyword-ish name).sprite/draw/octants.txtis copied next to the supplement module (loaded withinclude_str!, parsed lazily).
Design notes / deviations
- z2d replaced by
tiny-skia(coverage rasterizer only).Canvasowns an alpha8Vec<u8>; fills/strokes are rasterized into a scratch pixmap and compositedsrc_overwith the colour alpha, as z2d does for an opaque alpha8 pattern.Canvas::inner_stroke_pathreproduces the Zig fill-mask x double-width-stroke trick. Paths are built with the localPathtype (move_to/line_to/curve_to/close/arc); the padding translation is applied at raster time (like z2d'sstaticPathtransform). z2dContextis not needed (only used for arc/fill/stroke) and was folded intoPath::arc+fill_path/stroke_path. - Stroke miter limit 10 (z2d default) and miter joins.
- Comptime
draw<MIN>_<MAX>function discovery is replaced by explicitpub const RANGES: &[Range]tables in each draw module;face::ranges()sorts them and asserts non-overlap (equivalent of the Zig@compileError; covered byranges_sorted_and_non_overlapping). - Zig
assert(debug only) ->debug_assert!in braille (Zig asserts only in safe builds). NB: some odd cell sizes (e.g. 14x23) trip that assert in debug builds exactly as in Zig. The workspacereleaseprofile has debug-assertions on, so they apply there too;productionhas them off. Canvas::pixelignores out-of-bounds writes (z2dputPixeldoes as well).Box<T>insprite::canvasshadowsstd::boxed::Boxwhere imported.Colorisstruct Color(pub u8)(Zig non-exhaustive enum);Shadelikewise.Modifier::format_entry()returns just the value text (e.g."24%"); thea = ...\nconfig-entry formatting lives in ghostty-config.ModifierSetiteration order inMetrics::applyis theKeydeclaration order (Zig: hash-map order, unspecified).- Cargo.toml: removed deps on
ghostty-config,ghostty-unicode,ghostty-terminal(not used yet); addedtiny-skia,png,thiserror,log. - Atlas:
Atlas::newis infallible (no allocator);growis infallible;modified/resizedareAtomicUsize.
TODO(integration)
sprite::face::Presentationandsprite::face::RenderOptions(onlycell_width) are local stand-ins forfont.Presentation/font.face.RenderOptions; replace whenface.rs/main.rsof the font engine are ported.ghostty-configshould useModifier::parse_clifor theadjust-*options (andModifier::format_entry).
Tests
- Ported: Metrics 11/11 (the 2 formatConfig tests check the value text), Atlas 8/13, Sprite
start1/1, Face "render all sprites" 1/1. - Skipped (Zig-only): Atlas
grow OOM,init error,reserve error,grow error(allocator failure injection / tripwire), Atlas.Wasmhappy path(wasm).refAllDeclstests. - Added: canvas primitives, common helpers, braille layout, box/legacy smoke tests,
has_codepoint,ranges_sorted_and_non_overlapping,render_every_sprite_at_several_sizes(every codepoint x 5 cell sizes x cell_width None/1/2 throughrender_glyph, checks trimmed bounds/ink), cursor-bar offsets. - Reference comparison:
sprite_face_render_all_spritesrenders all 256-codepoint sheets at 18x36+4, 12x24+3, 11x21+2, 9x17+1 and compares with the upstream z2d reference PNGs (copied tocrates/ghostty-font/testdata/) with a tolerance: <= 4% of ink pixels may differ by more than 96/255. Measured: braille and U+1CD00 sheets are pixel-identical; other sheets differ only by antialiasing (typical max delta <= 64; a handful of thin diagonal-hatch pixels in U+1FB98/99 and branch/curve glyphs at 9x17 differ by up to ~200). - Review output (written by the tests):
target/sprite_sheets/—test-*.png(our sheets),diff-*.png(red = reference, green = ours),review-all-9x17.png,review-all-18x36.png(every sprite on one sheet).
Known gaps
- Pixel identity with z2d is not attempted (see above).
- Sprite
Faceis not yet wired intoCollection/SharedGrid(those modules come later).