#pragma once #include #include #include namespace plot::render { /// The renderer's built-in text backend: an anti-aliased greyscale atlas of /// Adwaita Mono, baked at authoring time and committed as data. /// /// There is no font file, FreeType or stb_truetype behind this: the coverage /// bytes and the glyph table are generated by `tools/fonts/gen_atlas.py` into /// `src/render/embedded_mono_font.cpp`, so `plot_core` stays dependency-free and /// nothing rasterises at build or run time. That file carries the provenance -- /// source face, its version, the em size, the supersample factor and the /// coverage set -- and `gen_atlas.py --check` fails if it has drifted from its /// input. Adwaita Mono is SIL OFL 1.1 (a copy is committed at /// `licences/OFL-1.1.txt`); the class is named neutrally because OFL reserves /// the font name for modified redistributions. /// /// **One cell, every glyph.** The atlas is a fixed-cell grid -- `cell_uv()` is /// the contract, and the staging path asserts it per glyph. Each cell is the /// face's maximum advance wide and its ascent-to-descent extent tall, and every /// glyph is drawn into its cell with its bearing preserved, never trimmed to its /// ink. Because the face is monospaced the pen advances by the same amount for /// every glyph, which is the face's own `hmtx` advance and not an assumption. /// /// A codepoint outside the coverage set draws the baked fallback box, so a label /// the atlas cannot spell is visible rather than silently blank. class EmbeddedMonoFont final : public TextBackend { public: /// The size the atlas was baked at, in font pixels, and the coverage samples /// per texel. Repeated here so a caller can reason about the artwork without /// opening the generated table; the generated translation unit is the /// authority and pins the same values. static constexpr u32 kEmPx = 13; static constexpr u32 kSupersample = 4; /// The codepoint value `codepoint_of_slot()` reports for the fallback slot. It /// is deliberately not a Unicode scalar, so it can never collide with a /// codepoint the atlas really holds. static constexpr u32 kFallbackCodepoint = 0xFFFFFFFFu; EmbeddedMonoFont() noexcept = default; /// Width of `utf8` in font pixels: the sum of the glyphs' advances, zero for /// the empty string and for null. Allocates nothing. f32 measure(const char* utf8) const override; /// Cell height in font pixels: one line of text occupies this many pixels. f32 height() const override; /// Top of the cell to the baseline, in font pixels. f32 ascent() const override; /// `kEmPx`: the size the face was baked at. The renderer scales a run by /// `Style::axis_font_px / em_px()`, so the same backend draws the size the /// style asked for whatever it was baked at. f32 em_px() const override; /// The glyph for `codepoint`: its atlas cell as a normalised rect, no extra /// bearing -- the bearing is inside the cell's ink -- and its advance. Never /// null: anything outside the coverage set maps to the fallback box. const Glyph* glyph(u32 codepoint) const override; /// `(kCellW / atlas_width(), kCellH / atlas_height())`: the cell every glyph's /// rect is. Vec2 cell_uv() const override; /// The packed atlas, GL_R8-ready, rows of `atlas_width()` bytes, top row /// first, one coverage byte per texel. Never null. const u8* atlas_pixels() const override; u32 atlas_width() const override; u32 atlas_height() const override; /// Always zero: the atlas is a compile-time constant, so the renderer uploads /// it once and never re-uploads. u32 revision() const override; /// Advance of a decoded codepoint, in font pixels. A codepoint outside the /// coverage set reports the fallback box's advance. static f32 advance_codepoint(u32 codepoint) noexcept; /// Slot a codepoint maps to: printable ASCII is a direct index, the bakes SI /// and dash codepoints are a short switch, and everything else is the fallback /// box -- so a missing glyph is always visible, never blank. static u32 glyph_index(u32 codepoint) noexcept; /// Number of cells in the atlas, the fallback included. static u32 glyph_count() noexcept; /// The codepoint a slot was baked for, or `kFallbackCodepoint` for the /// fallback slot. Out-of-range slots report the fallback too. static u32 codepoint_of_slot(u32 slot) noexcept; /// The fixed cell size, in atlas texels. Every glyph's rect is exactly this. static u32 cell_width() noexcept; static u32 cell_height() noexcept; /// Texel origin of a slot's cell in the atlas; `glyph()` gives the same /// rectangle normalised, which is what the text pipeline stages. static Vec2 atlas_origin(u32 slot) noexcept; /// The packed atlas image: the bytes `atlas_pixels()` returns, as a /// compile-time table. Never null. static const u8* atlas_image() noexcept; }; } // namespace plot::render