feat(text): optional vendored stb_truetype backend for runtime external fonts
This commit is contained in:
1 parent
daff60e3f0
commit
14d664a246
12 files changed
+6511
-19
No files matched your search
@@ -8,11 +8,11 @@ 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
|
||||
/// There is no font file, FreeType or third-party rasteriser 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
|
||||
@@ -27,6 +27,12 @@ namespace plot::render {
|
||||
///
|
||||
/// A codepoint outside the coverage set draws the baked fallback box, so a label
|
||||
/// the atlas cannot spell is visible rather than silently blank.
|
||||
///
|
||||
/// This is the only backend the core library ships. The one that bakes a font
|
||||
/// file at run time belongs to the optional target -- `plot/text/true_type_font.hpp`,
|
||||
/// deliberately not part of `include/plot/plot.hpp` -- and no public header
|
||||
/// outside `plot/text/` names what rasterises it, which is the isolation that
|
||||
/// target exists to keep.
|
||||
class EmbeddedMonoFont final : public TextBackend {
|
||||
public:
|
||||
/// The size the atlas was baked at, in font pixels, and the coverage samples
|
||||
|
||||
@@ -77,6 +77,32 @@ class Renderer {
|
||||
/// `Unsupported`; anything the driver refuses is `GLFailure` plus its log.
|
||||
bool init(Status* st = nullptr) noexcept;
|
||||
|
||||
/// The face every frame this renderer draws uses: the embedded one until a host
|
||||
/// installs another here.
|
||||
|
||||
/// `init()` binds `render::EmbeddedMonoFont`, and `draw()` installs the binding
|
||||
/// on the plot it is given (replacing whatever a host set with
|
||||
/// `Plot::set_text_backend()`, which is how a plot with no renderer keeps its
|
||||
/// own), so this is the way to draw with a face of your own -- a
|
||||
/// `plot::text::TrueTypeFont` from the optional target, say, or any other
|
||||
/// `TextBackend`. Passing null restores the embedded face. The swap takes
|
||||
/// effect on the next frame, which re-reads the backend's metrics at
|
||||
/// `style().device(axis_font_px)` and re-uploads its atlas -- so nothing here
|
||||
/// has to know whether the replacement is a different face, the same face
|
||||
/// re-baked at another size, or a backend whose pixels changed: the revision
|
||||
/// comparison in `render::TextAtlas` decides, and a replacement whose atlas is
|
||||
/// already on the GPU under the same revision is uploaded anyway (see the
|
||||
/// implementation for why that is the safe direction).
|
||||
///
|
||||
/// The backend is borrowed, not owned: it must outlive the frames drawn with it,
|
||||
/// which is the same lifetime rule `Plot::set_text_backend()` states.
|
||||
void set_text_backend(const render::TextBackend* font) noexcept;
|
||||
|
||||
/// The backend the last frame was drawn with, or the embedded face when the
|
||||
/// host installed none. The renderer's own wrapper while a frame is in flight,
|
||||
/// so a caller reads this after `draw()` to see what the plot was handed.
|
||||
const render::TextBackend* text_backend() const noexcept { return font_; }
|
||||
|
||||
/// Starts a frame: records `fb_w` x `fb_h` for the projection, flips
|
||||
/// `viewport_px` (top-left origin, like every `Rect` in this library) into GL's
|
||||
/// bottom-left origin once, clears that region to `style().background` and
|
||||
@@ -387,10 +413,14 @@ class Renderer {
|
||||
/// numbers.
|
||||
void draw_legend(const Plot& plot, const PlotLayout& layout);
|
||||
|
||||
/// The built-in backend the renderer owns. Every use of it goes through the
|
||||
/// `TextBackend` interface -- `self`-typed nowhere -- so swapping it for
|
||||
/// another face is a change to this member alone.
|
||||
/// The built-in backend the renderer owns, and the face the frames draw with:
|
||||
/// `set_text_backend()` writes `font_`, which starts here and comes back here
|
||||
/// when a host passes null. Every use of the face goes through the
|
||||
/// `TextBackend` interface -- `self`-typed nowhere -- so a host's backend and
|
||||
/// this one are interchangeable, and the only thing that has to happen on a swap
|
||||
/// is the atlas re-upload `set_text_backend()` arranges.
|
||||
render::EmbeddedMonoFont default_font_;
|
||||
const render::TextBackend* font_ = &default_font_;
|
||||
AxisFont font_backend_;
|
||||
render::Style style_;
|
||||
|
||||
|
||||
Reference in new issue
Block a user