One decoded color glyph: real advance width (font units, ttf_parser’s
own metric — NOT approximated from bitmap size) + a premultiplied RGBA8
bitmap (tiny_skia::Pixmap::decode_png’s own convention — the SAME one
rosace-render::image’s Image widget already decodes PNGs with, so
this reuses an already-consistent pixel-format contract, not a new one)
at whatever sbix strike size glyph_raster_image picked (nearest
available, not rescaled to the exact requested px — a named
simplification; see color_glyph_rgba’s doc).
Rasterized glyph metrics — deliberately the SAME shape/field names
fontdue::Metrics had (D127 rasterizer migration, 2026-08-03): every
consumer (canvas.rs’s CPU blit and GPU-atlas paths) reads .width/
.height/.xmin/.ymin/.advance_width off this — keeping the shape
identical meant the whole blit/atlas pipeline needed ZERO changes for
the fontdue -> swash swap, only this file (glyph PRODUCTION) changed.
An owned font face. swash::FontRef only ever BORROWS a byte slice —
it’s not meant to be stored long-term — so we keep the bytes ourselves
and reconstruct a FontRef on demand via [Self::as_ref], preserving
offset/key exactly as swash’s own doc comment on FontRef
recommends (a fresh FontRef::from_index call on every access would
mint a new CacheKey each time and defeat swash’s internal caching).
Text weight. Maps onto real font faces: SemiBold/Bold use the bold
face when one was found; Light/Regular/Medium use the regular face.
Before this existed the field was silently ignored — headings were never
actually bold.
The one glyph-placement walk (kerning, baseline, bearing) shared by the
CPU blit path (SkiaCanvas::draw_text_weighted) and the GPU atlas
collect path — they MUST agree glyph-for-glyph, so the math lives once.