feat(render): style, GLSL line/text pipelines, staging batches, bitmap font
This commit is contained in:
1 parent
9a086b7d69
commit
7cbc1c9955
22 files changed
+1535
No files matched your search
@@ -0,0 +1,101 @@
|
||||
#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, computed once when
|
||||
/// the font is constructed from the same bitmaps the atlas uploads. 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. The text pipeline
|
||||
/// draws a cell `u_px_range` pixels across, so a run at another size measures
|
||||
/// `measure(utf8) * u_px_range.x / kGlyphPx`; `TextBatch::add_text` takes that
|
||||
/// cell size and applies the same scale to the pen it advances.
|
||||
class BitmapFont final : public TextBackend {
|
||||
public:
|
||||
// Atlas geometry. It is part of the render pipeline's contract: the text
|
||||
// vertex shader expands a glyph cell by `u_px_range` pixels and reads the cell
|
||||
// at the instance's texel origin, so the atlas is a grid of 8x8 cells whose
|
||||
// width must stay a whole number of cells.
|
||||
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");
|
||||
|
||||
/// Reported by `next_codepoint` for a byte sequence that is not valid UTF-8.
|
||||
/// It is not a Unicode scalar value, so it can only map to the fallback box.
|
||||
static constexpr u32 kInvalidCodepoint = 0xFFFFFFFFu;
|
||||
|
||||
/// Decodes one UTF-8 sequence from `p` and advances `p` past it — one byte
|
||||
/// when the sequence is malformed, so a damaged label still terminates. The
|
||||
/// text batch and `measure` share it, which is what keeps a staged run exactly
|
||||
/// `measure`-wide.
|
||||
static u32 next_codepoint(const char*& p) noexcept;
|
||||
|
||||
BitmapFont() noexcept;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// 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; this is the per-instance `a_uv`
|
||||
/// the text pipeline reads.
|
||||
static Vec2 atlas_origin(u32 slot) noexcept;
|
||||
|
||||
private:
|
||||
f32 advances_[kGlyphCount];
|
||||
};
|
||||
|
||||
} // namespace plot::render
|
||||
Reference in new issue
Block a user