render
Mojo module 🡭
render
Text rendering: font matching from font_discovery.mojo, glyph outlines and metrics from ttf.mojo via glyph_outline.mojo, and rasterization from fill_path_aa (path.mojo) under FillRule.NONZERO, the rule TrueType outlines are drawn with, which also puts every glyph on the exact-area rasterizer (canvas.aa_area). The glyph path is unhinted. Glyphs fill through the same fill_path_aa every other shape uses, so translucent text composites through set_pixel like any other fill.
Unrotated text goes through a glyph mask cache on the FontCache:
each (face, size, glyph, sub-pixel offset) is rasterized once, as
the sub-sample counts fill_path_aa’s sweep computes, and every later
occurrence composites the cached counts through the sweep’s own alpha
arithmetic (_composite_glyph_mask), so the pixels are the ones a
direct fill writes. The sub-pixel offset is the glyph origin’s
fractional part rounded to 1/64 px (_SUBPIXEL_STEPS): whole-pixel
anchors are unchanged, and a fractional anchor places each glyph
within 1/128 px of where the unrounded outline would go, well inside
the sweep’s 1/4 px sample spacing. Rotated text still fills each
glyph’s outline directly (#170).
draw_text’s (x, y) is the baseline’s left end for LEFT alignment, not
a top-left corner like fill_rect’s. CENTER/RIGHT shift each line
horizontally against that same anchor.
Rotation and multi-line share one code path with the single-line case.
_layout_block and draw_text’s render pass both walk each line’s glyphs
from a shared anchor-relative local layout. A rotated block, and a
block under a canvas transform that is a similarity, places each glyph
by mapping its pen position through one matrix and compositing a mask
cached at that orientation and scale (_draw_block_similarity); a
non-uniform scale or skew fills each mapped outline directly
(_draw_block_direct). At rotation=0.0 with one line the layout’s
cos=1/sin=0 leaves every point unchanged.
One shaping step (_shape_line) turns each line’s text into the glyph
sequence every pass then walks, which is what keeps measure_text and
draw_text from disagreeing. It splits the line into bidi runs
(bidi.visual_runs), shapes each run in logical order, and
concatenates the runs left to right with a right-to-left run’s glyphs
reversed. Shaping per run in logical order is what the order has to
be: joining and ligature formation are defined between the characters
typed either side of a letter, which in a right-to-left run are not
the ones drawn either side of it.
Shaping a run maps each character through cmap and applies the
font’s GSUB features for the run’s script (ttf.mojo). A Latin run
gets ccmp and liga, so “f” and “i” become the one “fi” glyph a
font that has it draws. An Arabic run additionally classifies each
letter’s contextual form from its logical neighbors
(joining.mojo) and enables that one of isol/init/medi/fina
on that one glyph, then runs rlig, liga and calt; so “بسم”
draws as initial beh, medial seen, final meem rather than three
isolated letters. ligatures=False skips substitution entirely and
lays out one glyph per character, which for Arabic is the isolated
forms – one flag rather than two, because joining and ligatures are
the same machinery and an Arabic font’s rlig is not optional once
the letters are joined.
Adjacent glyphs kern against each other in the same per-run pass,
through the font’s GPOS pair adjustment or kern table
(_apply_run_kerning). Kerning is between the substituted glyphs,
the order a shaper applies the two tables in: a pair adjustment
written for “f” does not apply across an “fi” ligature, because the
ligature is what sits on the line. The pair is looked up in logical
order, since that is the order a font states an adjustment for, and
the result rides on the glyph a pass reaches second
(_ShapedGlyph.kern_before) – for a right-to-left run that is the
first of the two logically, because reversing the run swaps which
one is drawn second. Kerning stops at a run boundary. The adjustment
moves the pen between two glyphs and nothing else, so the glyph mask
cache – which holds a glyph’s coverage, not its place on the line –
is untouched by it. kerning=False leaves every kern_before at zero,
restoring the plain sum of hmtx advances.
Shaping only substitutes among characters the primary face has glyphs
for, since a ligature is defined over glyphs of one font. Everything
else goes through font fallback (_resolve_glyph): a codepoint the
requested family has no real glyph for (glyph index 0, “.notdef”)
resolves through resolve_font_file_for_char. This package bundles no
fonts, so a CJK/Cyrillic/symbol character requested under a Latin-only
family renders through whatever installed font has it; one missing
everywhere degrades to the unconstrained best match. Fallback faces
cache alongside the primary face.
Two entry points draw the same layout differently. stroke_text hands
each glyph’s outline to stroke_path_aa instead of filling it, which
is a label that reads over a busy background. draw_text_on_path puts
the baseline on a curve: the string is laid out straight, then each
glyph is placed at its own arc length along the path
(_ArcLengthPath) and turned to the tangent there. Neither is a
different layout – both walk what _shape_line produced, so text on
a curve kerns and ligates exactly as straight text does.
FontSlant/FontWeight come from font_discovery.mojo and TextAlign from text_align.mojo, both re-exported here.
Structs
TextMetrics: A single line’s measured size.width/heightare its tight ink bounding box;advanceis the logical cursor-advance distance, which differs whenever leading/trailing whitespace contributes advance but no ink. TextAlign’s CENTER/RIGHT useadvance.TextBlockBounds: The axis-aligned bounding box draw_text’s ink would occupy for a given text/rotation/align/font, anchor-relative:x/yare the top-left corner relative to draw_text’s(x, y)anchor, and either can be negative (RIGHT-aligned text extends left of the anchor; a label rotated upward extends above it).x + width/y + heightis the bottom-right corner.TextLayout: Text already shaped, kerned, line-broken and aligned, ready to be measured or drawn without doing that work again.