Skip to main content

Module atlas

Module atlas 

Source
Expand description

Grid-packed glyph atlas layout shared by the GPU backends.

A GPU backend uploads every glyph of a FontChain once and then addresses one by a flat slot index that goes straight into an instance buffer. This module owns both halves of that: AtlasData is the CPU-side coverage buffer to upload, and GlyphAtlas is the char -> slot map to look up.

§Why a grid, not one glyph per layer

The obvious packing (one glyph per array-texture layer) caps a chain at the array-layer limit, which is 256 on the OpenGL 3.3 / GL ES 3.0 floor and on wgpu’s downlevel defaults. Packing a fixed ATLAS_COLSxATLAS_ROWS grid of glyphs into each layer instead means N glyphs need only ceil(N / 256) layers, which lifts the cap to MAX_SLOTS glyphs while still fitting inside that 256-layer minimum. A shader turns a slot back into its (layer, column, row) sub-rect.

§Coverage, not colour

AtlasData::coverage is one byte per texel: 0xFF where the glyph’s bit is set and 0 where it isn’t, meant for a single-channel (R8) texture. A backend samples it with nearest filtering and blends the cell’s foreground over its background by that coverage, so glyphs stay crisp at any integer scale and take the cell’s colours like any other glyph.

Those two values are the only ones that ever appear, and that is load-bearing rather than incidental. Coverage used as an alpha is the usual place text rendering goes wrong on colour space: a rasterizer’s partial coverage is a linear quantity, so interpolating between an sRGB-encoded foreground and background by it produces text that is too thin or too fat, and correcting for that is fiddly. The question does not arise for a bitmap font, because a blend factor of exactly 0 or exactly 1 selects one endpoint outright and every colour space agrees on the result.

Those two values being the only ones is an invariant of this module, not an accident of the current font sources, and every backend is entitled to rely on it. coverage_is_strictly_binary enforces it.

Anything that introduces partial coverage (an antialiased or grayscale-AA font source, multisampling, a non-integer render scale) reopens the colour-space question for all three backends simultaneously, and has to be a deliberate decision rather than a side effect. See docs/references/core/color-space.md.

§Examples

use retroglyph_window::atlas::GlyphAtlas;
use retroglyph_window::font::{FontChain, unscii16};

let atlas = GlyphAtlas::new(FontChain::from(unscii16::FONT), (8, 16));
// Unscii 16 is 256 CP437 glyphs, so it occupies exactly one 16x16 layer.
assert_eq!(atlas.slot_count(), 256);
assert_eq!(atlas.data().geometry.layers, 1);
// A character resolves to the slot its coverage was written to.
assert_eq!(atlas.resolve('A'), Some(u16::from(b'A')));

Structs§

AtlasData
The CPU-side coverage buffer for a whole atlas, grid-packed per AtlasGeometry.
AtlasGeometry
The packing of glyph cells into an array texture: a fixed ATLAS_COLSxATLAS_ROWS grid of cell_wxcell_h glyph cells per layer, across layers layers.
GlyphAtlas
A static FontChain plus the char -> slot map for its grid-packed atlas.

Constants§

ATLAS_COLS
Glyph columns packed into one array layer.
ATLAS_ROWS
Glyph rows packed into one array layer.
MAX_SLOTS
The number of slots the atlas can address, set by the u16 slot id an instance buffer carries.
SLOTS_PER_LAYER
Glyph slots per array layer (ATLAS_COLS * ATLAS_ROWS).

Functions§

addressable_glyphs
The number of atlas slots font occupies: its glyph count, capped at the 256 a u8 glyph index can address (see BitmapFont::rows).