111 lines
5.1 KiB
C++
111 lines
5.1 KiB
C++
#pragma once
|
|
#include <plot/core/types.hpp>
|
|
#include <plot/math/vec2.hpp>
|
|
#include <plot/render/text.hpp>
|
|
|
|
namespace plot::render {
|
|
|
|
/// 8x8 bitmap font, the text backend of the renderer.
|
|
///
|
|
/// The glyphs are 1-bit, one byte per row, bit 0 the leftmost pixel; the table
|
|
/// and its provenance are in `src/render/bitmap_font.cpp`. Nothing here needs a
|
|
/// font file, FreeType or stb_truetype: `plot_core` stays dependency-free.
|
|
///
|
|
/// Metrics are proportional, not fixed width: an advance is the glyph's ink
|
|
/// width plus one pixel of spacing, floored at two pixels, derived from the
|
|
/// same bitmaps the atlas uploads, so a second width table cannot drift from the
|
|
/// artwork. Whitespace is a blank glyph with a nonzero advance, and every
|
|
/// codepoint without a glyph draws a visible box, so a missing glyph is obvious
|
|
/// rather than silently blank.
|
|
///
|
|
/// Metrics are in unscaled font pixels, one per glyph texel, and the atlas is a
|
|
/// grid of `kGlyphPx` cells: every glyph's rect is its own cell, so a run drawn
|
|
/// at `s` pixels per font pixel measures `measure(utf8) * s` and sits in a cell
|
|
/// of `s * kGlyphPx` pixels. `TextBatch::add_text` takes that factor and applies
|
|
/// it to the pen it advances.
|
|
class BitmapFont final : public TextBackend {
|
|
public:
|
|
// Atlas geometry: a grid of `kGlyphPx` cells whose width is a whole number of
|
|
// them. A glyph's rect is its own cell, normalised by these sizes, so the grid
|
|
// is all the text pipeline has to know about the atlas.
|
|
static constexpr u32 kGlyphPx = 8; ///< glyph cell edge, texels
|
|
static constexpr u32 kFirstChar = 32; ///< U+0020
|
|
static constexpr u32 kAsciiGlyphs = 95; ///< U+0020..U+007E
|
|
static constexpr u32 kDegreeSlot = kAsciiGlyphs; ///< U+00B0 DEGREE SIGN
|
|
static constexpr u32 kMicroSlot = kAsciiGlyphs + 1u; ///< U+00B5 MICRO SIGN
|
|
static constexpr u32 kOhmSlot = kAsciiGlyphs + 2u; ///< U+03A9 OHM
|
|
static constexpr u32 kFallbackSlot = kAsciiGlyphs + 3u; ///< missing-glyph box
|
|
static constexpr u32 kGlyphCount = kAsciiGlyphs + 4u;
|
|
static constexpr u32 kAtlasCols = 16;
|
|
static constexpr u32 kAtlasRows = 7;
|
|
static constexpr u32 kAtlasWidth = kAtlasCols * kGlyphPx; ///< 128 texels
|
|
static constexpr u32 kAtlasHeight = kAtlasRows * kGlyphPx; ///< 56 texels
|
|
static_assert(kAtlasCols * kAtlasRows >= kGlyphCount, "atlas too small for the glyph set");
|
|
|
|
BitmapFont() noexcept = default;
|
|
|
|
/// Width of `utf8`, the sum of the glyph advances: zero for the empty string
|
|
/// and for null. The ASCII path allocates nothing.
|
|
f32 measure(const char* utf8) const override;
|
|
|
|
/// Cell height in pixels (one glyph row of text occupies `height()` pixels).
|
|
f32 height() const override;
|
|
/// Top of the cell to the baseline, in pixels.
|
|
f32 ascent() const override;
|
|
|
|
/// Advance of one ASCII character in pixels. A byte that is not printable
|
|
/// ASCII reports the missing-glyph box, matching what `measure` gives that
|
|
/// byte on its own.
|
|
f32 advance(char c) const noexcept;
|
|
|
|
/// Advance of a decoded Unicode codepoint, in pixels.
|
|
f32 advance_codepoint(u32 codepoint) const noexcept;
|
|
|
|
/// Glyph slot for a codepoint: printable ASCII is its own slot, the three SI
|
|
/// codepoints `format_si` emits map to real glyphs, and anything else maps to
|
|
/// the visible missing-glyph box.
|
|
static u32 glyph_index(u32 codepoint) noexcept;
|
|
/// The glyph for `codepoint`: its atlas cell as a normalised rect (see
|
|
/// `Glyph`), a zero bearing and its ink-derived advance. Never null -- a
|
|
/// codepoint with no artwork maps to the visible missing-glyph box, which is
|
|
/// what a label outside the coverage set should show.
|
|
const Glyph* glyph(u32 codepoint) const override;
|
|
|
|
/// The packed atlas, as `TextBackend` sees it: the bytes `atlas_image()`
|
|
/// returns, `kAtlasWidth` by `kAtlasHeight`, GL_R8-ready. 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 it never changes and
|
|
/// the renderer uploads it once.
|
|
u32 revision() const override;
|
|
|
|
/// Rows in a glyph bitmap, always `kGlyphPx`.
|
|
u32 glyph_rows(char c) const noexcept;
|
|
|
|
/// The `kGlyphPx` rows of one ASCII character's bitmap, top row first.
|
|
const u8* glyph_bitmap(char c) const noexcept;
|
|
|
|
/// The `kGlyphPx` rows of a glyph slot.
|
|
static const u8* glyph_bitmap_of(u32 slot) noexcept;
|
|
|
|
/// The packed atlas image, ready for `plot::gl::Texture::upload_r8`: rows of
|
|
/// `kAtlasWidth` bytes, top row first, 255 for ink and 0 for background
|
|
/// (straight alpha, no dilation or gamma applied).
|
|
static const u8* atlas_image() noexcept;
|
|
|
|
/// Texel origin of a glyph slot in the atlas, in texels; `glyph()` gives the
|
|
/// same rectangle normalised, which is what the text pipeline stages.
|
|
static Vec2 atlas_origin(u32 slot) noexcept;
|
|
|
|
private:
|
|
/// Every slot's rect, bearing and advance, derived from the bitmaps above at
|
|
/// compile time. This replaced a per-instance `advances_` array holding the
|
|
/// same numbers, so the advances the pen uses and the ones `glyph()` publishes
|
|
/// cannot drift apart.
|
|
static const Glyph* glyph_table() noexcept;
|
|
};
|
|
|
|
} // namespace plot::render
|