refactor(render): TextBackend owns its atlas, and staged glyphs carry a rotation basis
This commit is contained in:
1 parent
90e17a0a11
commit
e074f57ddf
23 files changed
+1073
-227
No files matched your search
+61
-19
@@ -1,6 +1,23 @@
|
||||
#include <plot/render/primitives.hpp>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace plot::render {
|
||||
namespace {
|
||||
|
||||
/// `axis` as a unit vector: the cell's local +x, which is what the instance and
|
||||
/// the shader both take (see `TextInstance`). A zero, infinite or NaN direction
|
||||
/// means "unrotated" -- a caller's arithmetic slip must not turn a label into NaN
|
||||
/// geometry, and a basis the shader trusts to be unit must be one.
|
||||
Vec2 unit_axis(Vec2 axis) noexcept {
|
||||
const f32 len = axis.length();
|
||||
if (std::isfinite(len) && len > 0.0f) {
|
||||
return axis / len;
|
||||
}
|
||||
return Vec2{1.0f, 0.0f};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// ----------------------------------------------------------------- LineBatch
|
||||
|
||||
@@ -42,31 +59,56 @@ void LineBatch::add_polyline_xy(const f32* xy, u32 n) {
|
||||
|
||||
// ----------------------------------------------------------------- TextBatch
|
||||
|
||||
void TextBatch::begin() noexcept { v_.clear(); }
|
||||
|
||||
void TextBatch::clear() noexcept { v_.clear(); }
|
||||
|
||||
void TextBatch::add_glyph(Vec2 pos_px, u32 slot) {
|
||||
const Vec2 origin = BitmapFont::atlas_origin(slot);
|
||||
v_.push_back(pos_px.x);
|
||||
v_.push_back(pos_px.y);
|
||||
v_.push_back(origin.x);
|
||||
v_.push_back(origin.y);
|
||||
void TextBatch::begin() noexcept {
|
||||
v_.clear();
|
||||
cell_uv_ = Vec2{};
|
||||
}
|
||||
|
||||
Vec2 TextBatch::add_text(Vec2 pen_px, const char* utf8, const BitmapFont& font, f32 px_per_cell) {
|
||||
void TextBatch::clear() noexcept {
|
||||
v_.clear();
|
||||
cell_uv_ = Vec2{};
|
||||
}
|
||||
|
||||
void TextBatch::add_cell(Vec2 pen_px, const Glyph& glyph, f32 px, Vec2 unit_ux) {
|
||||
// The cell's own axes: the run direction and its perpendicular, which is the
|
||||
// cell's +v axis -- the one kTextVS builds the quad from. The bearing is in
|
||||
// font pixels, so it scales with the run and follows the same axes: a glyph
|
||||
// keeps its place inside a rotated run instead of drifting along screen +y.
|
||||
const Vec2 uy{-unit_ux.y, unit_ux.x};
|
||||
const Vec2 corner = pen_px + unit_ux * (glyph.xoff * px) + uy * (glyph.yoff * px);
|
||||
v_.push_back(corner.x);
|
||||
v_.push_back(corner.y);
|
||||
v_.push_back(glyph.u0);
|
||||
v_.push_back(glyph.v0);
|
||||
v_.push_back(unit_ux.x);
|
||||
v_.push_back(unit_ux.y);
|
||||
// One cell for the whole batch: every glyph a batch stages comes from the same
|
||||
// atlas, so the rect the shader reads is the one staged here (see
|
||||
// glyph_cell_uv()).
|
||||
cell_uv_ = Vec2{glyph.u1 - glyph.u0, glyph.v1 - glyph.v0};
|
||||
}
|
||||
|
||||
void TextBatch::add_glyph(Vec2 pen_px, const Glyph& glyph, f32 px, Vec2 axis) {
|
||||
add_cell(pen_px, glyph, px, unit_axis(axis));
|
||||
}
|
||||
|
||||
Vec2 TextBatch::add_text(const char* utf8, Vec2 pen_px, f32 px, const TextBackend& font,
|
||||
Vec2 axis) {
|
||||
if (utf8 == nullptr) {
|
||||
return pen_px;
|
||||
}
|
||||
const f32 scale = px_per_cell / static_cast<f32>(BitmapFont::kGlyphPx);
|
||||
const Vec2 ux = unit_axis(axis);
|
||||
for (const char* p = utf8; *p != '\0';) {
|
||||
// Same codepoints `measure` sums and the same advances, scaled by the
|
||||
// on-screen cell size: a run staged here is exactly what the plot layer
|
||||
// measured, times the scale it draws at, so a label never overruns the
|
||||
// space that was reserved for it.
|
||||
const u32 codepoint = BitmapFont::next_codepoint(p);
|
||||
add_glyph(pen_px, BitmapFont::glyph_index(codepoint));
|
||||
pen_px.x += font.advance_codepoint(codepoint) * scale;
|
||||
// The backend's own codepoints and the backend's own advances -- the same
|
||||
// numbers `measure()` sums -- scaled by `px` and measured along the basis,
|
||||
// so a run staged here is exactly as long as the plot layer measured it and
|
||||
// ends where the space reserved for the label ends.
|
||||
const Glyph* glyph = font.glyph(next_codepoint(p));
|
||||
if (glyph == nullptr) {
|
||||
continue; // no artwork for this codepoint: nothing to draw, nothing to advance
|
||||
}
|
||||
add_cell(pen_px, *glyph, px, ux);
|
||||
pen_px = pen_px + ux * (glyph->advance * px);
|
||||
}
|
||||
return pen_px;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user