feat(render): anti-aliased embedded Adwaita Mono atlas, generated and committed
This commit is contained in:
1 parent
3ff7b9c978
commit
5ca3587325
17 files changed
+3422
-627
No files matched your search
@@ -1,264 +0,0 @@
|
||||
#include <plot/render/bitmap_font.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace plot::render {
|
||||
namespace {
|
||||
|
||||
// Glyph data and its provenance
|
||||
// -----------------------------
|
||||
// The 95 printable ASCII glyphs (slots 0..94, U+0020..U+007E) are the
|
||||
// `font8x8_basic` table of the font8x8 project by Daniel Hepper, released into
|
||||
// the public domain. It is itself based on Marcel Sondaar's public-domain 8x8
|
||||
// VGA font (via the IBM public-domain VGA fonts). Fetched on 2026-09-27 from
|
||||
// https://raw.githubusercontent.com/dhepper/font8x8/master/font8x8_basic.h
|
||||
// The glyphs the plot layer's SI labels need come from the same project:
|
||||
// U+00B0 DEGREE SIGN and U+00B5 MICRO SIGN from font8x8_ext_latin.h (which
|
||||
// covers U+00A0..U+00FF), and U+03A9 GREEK CAPITAL LETTER OMEGA from
|
||||
// font8x8_greek.h (which covers U+0390..U+03C9).
|
||||
// The missing-glyph box in the last slot is authored here: a hollow 8x8 square.
|
||||
// Nothing else in this table is hand-drawn, and none of it is a library: it is
|
||||
// public-domain data, so `plot_core` still links nothing third-party.
|
||||
//
|
||||
// Row 0 is the top row and bit 0 is the leftmost pixel, one byte per row.
|
||||
|
||||
constexpr u8 kGlyphBits[BitmapFont::kGlyphCount][BitmapFont::kGlyphPx] = {
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // [ 0] U+0020 space
|
||||
{0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // [ 1] U+0021 '!'
|
||||
{0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // [ 2] U+0022 '"'
|
||||
{0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // [ 3] U+0023 '#'
|
||||
{0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // [ 4] U+0024 '$'
|
||||
{0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // [ 5] U+0025 '%'
|
||||
{0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // [ 6] U+0026 '&'
|
||||
{0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // [ 7] U+0027 '''
|
||||
{0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // [ 8] U+0028 '('
|
||||
{0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // [ 9] U+0029 ')'
|
||||
{0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // [10] U+002A '*'
|
||||
{0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // [11] U+002B '+'
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // [12] U+002C ','
|
||||
{0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // [13] U+002D '-'
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // [14] U+002E '.'
|
||||
{0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // [15] U+002F '/'
|
||||
{0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // [16] U+0030 '0'
|
||||
{0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // [17] U+0031 '1'
|
||||
{0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // [18] U+0032 '2'
|
||||
{0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // [19] U+0033 '3'
|
||||
{0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // [20] U+0034 '4'
|
||||
{0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // [21] U+0035 '5'
|
||||
{0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // [22] U+0036 '6'
|
||||
{0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // [23] U+0037 '7'
|
||||
{0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // [24] U+0038 '8'
|
||||
{0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // [25] U+0039 '9'
|
||||
{0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // [26] U+003A ':'
|
||||
{0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // [27] U+003B ';'
|
||||
{0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // [28] U+003C '<'
|
||||
{0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // [29] U+003D '='
|
||||
{0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // [30] U+003E '>'
|
||||
{0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // [31] U+003F '?'
|
||||
{0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // [32] U+0040 '@'
|
||||
{0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // [33] U+0041 'A'
|
||||
{0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // [34] U+0042 'B'
|
||||
{0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // [35] U+0043 'C'
|
||||
{0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // [36] U+0044 'D'
|
||||
{0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // [37] U+0045 'E'
|
||||
{0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // [38] U+0046 'F'
|
||||
{0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // [39] U+0047 'G'
|
||||
{0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // [40] U+0048 'H'
|
||||
{0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // [41] U+0049 'I'
|
||||
{0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // [42] U+004A 'J'
|
||||
{0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // [43] U+004B 'K'
|
||||
{0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // [44] U+004C 'L'
|
||||
{0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // [45] U+004D 'M'
|
||||
{0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // [46] U+004E 'N'
|
||||
{0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // [47] U+004F 'O'
|
||||
{0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // [48] U+0050 'P'
|
||||
{0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // [49] U+0051 'Q'
|
||||
{0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // [50] U+0052 'R'
|
||||
{0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // [51] U+0053 'S'
|
||||
{0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // [52] U+0054 'T'
|
||||
{0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // [53] U+0055 'U'
|
||||
{0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // [54] U+0056 'V'
|
||||
{0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // [55] U+0057 'W'
|
||||
{0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // [56] U+0058 'X'
|
||||
{0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // [57] U+0059 'Y'
|
||||
{0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // [58] U+005A 'Z'
|
||||
{0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // [59] U+005B '['
|
||||
{0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // [60] U+005C '\'
|
||||
{0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // [61] U+005D ']'
|
||||
{0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // [62] U+005E '^'
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // [63] U+005F '_'
|
||||
{0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // [64] U+0060 '`'
|
||||
{0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // [65] U+0061 'a'
|
||||
{0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // [66] U+0062 'b'
|
||||
{0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // [67] U+0063 'c'
|
||||
{0x38, 0x30, 0x30, 0x3E, 0x33, 0x33, 0x6E, 0x00}, // [68] U+0064 'd'
|
||||
{0x00, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // [69] U+0065 'e'
|
||||
{0x1C, 0x36, 0x06, 0x0F, 0x06, 0x06, 0x0F, 0x00}, // [70] U+0066 'f'
|
||||
{0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // [71] U+0067 'g'
|
||||
{0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // [72] U+0068 'h'
|
||||
{0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // [73] U+0069 'i'
|
||||
{0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // [74] U+006A 'j'
|
||||
{0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // [75] U+006B 'k'
|
||||
{0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // [76] U+006C 'l'
|
||||
{0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // [77] U+006D 'm'
|
||||
{0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // [78] U+006E 'n'
|
||||
{0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // [79] U+006F 'o'
|
||||
{0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // [80] U+0070 'p'
|
||||
{0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // [81] U+0071 'q'
|
||||
{0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // [82] U+0072 'r'
|
||||
{0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // [83] U+0073 's'
|
||||
{0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // [84] U+0074 't'
|
||||
{0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // [85] U+0075 'u'
|
||||
{0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // [86] U+0076 'v'
|
||||
{0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // [87] U+0077 'w'
|
||||
{0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // [88] U+0078 'x'
|
||||
{0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // [89] U+0079 'y'
|
||||
{0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // [90] U+007A 'z'
|
||||
{0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // [91] U+007B '{'
|
||||
{0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // [92] U+007C '|'
|
||||
{0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // [93] U+007D '}'
|
||||
{0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // [94] U+007E '~'
|
||||
{0x1C, 0x36, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00}, // [95] U+00B0 degree
|
||||
{0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x03}, // [96] U+00B5 micro
|
||||
{0x3E, 0x63, 0x63, 0x63, 0x36, 0x36, 0x77, 0x00}, // [97] U+03A9 ohm
|
||||
{0x7E, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x00}, // [98] missing-glyph box, authored here
|
||||
};
|
||||
|
||||
/// Repacks the glyph-major table into the atlas image the text pipeline
|
||||
/// uploads: one 8x8 cell per slot, 255 for ink, straight alpha, no dilation.
|
||||
constexpr std::array<u8, BitmapFont::kAtlasWidth * BitmapFont::kAtlasHeight> pack_atlas() noexcept {
|
||||
std::array<u8, BitmapFont::kAtlasWidth * BitmapFont::kAtlasHeight> image{};
|
||||
for (u32 slot = 0; slot < BitmapFont::kGlyphCount; ++slot) {
|
||||
const u32 cell_x = (slot % BitmapFont::kAtlasCols) * BitmapFont::kGlyphPx;
|
||||
const u32 cell_y = (slot / BitmapFont::kAtlasCols) * BitmapFont::kGlyphPx;
|
||||
for (u32 y = 0; y < BitmapFont::kGlyphPx; ++y) {
|
||||
const u8 bits = kGlyphBits[slot][y];
|
||||
for (u32 x = 0; x < BitmapFont::kGlyphPx; ++x) {
|
||||
image[(cell_y + y) * BitmapFont::kAtlasWidth + cell_x + x] =
|
||||
static_cast<u8>(((bits >> x) & 1u) != 0u ? 0xFFu : 0x00u);
|
||||
}
|
||||
}
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
constexpr std::array<u8, BitmapFont::kAtlasWidth * BitmapFont::kAtlasHeight> kAtlasImage =
|
||||
pack_atlas();
|
||||
|
||||
/// Span of set bits in a glyph's rows: 0 for a blank glyph such as space.
|
||||
constexpr u32 ink_width(const u8* rows) noexcept {
|
||||
u32 lo = BitmapFont::kGlyphPx;
|
||||
u32 hi = 0;
|
||||
bool any = false;
|
||||
for (u32 y = 0; y < BitmapFont::kGlyphPx; ++y) {
|
||||
for (u32 x = 0; x < BitmapFont::kGlyphPx; ++x) {
|
||||
if (((rows[y] >> x) & 1u) != 0u) {
|
||||
if (x < lo) {
|
||||
lo = x;
|
||||
}
|
||||
if (x > hi) {
|
||||
hi = x;
|
||||
}
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return any ? hi - lo + 1u : 0u;
|
||||
}
|
||||
|
||||
/// The glyph table the pipeline stages from: one entry per slot, its rect at the
|
||||
/// slot's place in the grid and one cell big -- the cell `cell_uv()` declares,
|
||||
/// so a glyph's rect and the size the draw is told cannot disagree -- a zero
|
||||
/// bearing (the ink sits at the cell's top-left) and the ink-derived advance.
|
||||
/// Built from the same tables `pack_atlas()` packs.
|
||||
constexpr std::array<Glyph, BitmapFont::kGlyphCount> build_glyphs() noexcept {
|
||||
std::array<Glyph, BitmapFont::kGlyphCount> glyphs{};
|
||||
for (u32 slot = 0; slot < BitmapFont::kGlyphCount; ++slot) {
|
||||
const f32 u0 = static_cast<f32>(slot % BitmapFont::kAtlasCols) * BitmapFont::kCellUV.x;
|
||||
const f32 v0 = static_cast<f32>(slot / BitmapFont::kAtlasCols) * BitmapFont::kCellUV.y;
|
||||
const u32 ink = ink_width(kGlyphBits[slot]);
|
||||
const u32 spaced = ink + 1u; // one pixel of side bearing, floored at two
|
||||
glyphs[slot] = Glyph{u0, v0, u0 + BitmapFont::kCellUV.x, v0 + BitmapFont::kCellUV.y, 0.0f,
|
||||
0.0f, static_cast<f32>(spaced < 2u ? 2u : spaced)};
|
||||
}
|
||||
return glyphs;
|
||||
}
|
||||
|
||||
constexpr std::array<Glyph, BitmapFont::kGlyphCount> kGlyphs = build_glyphs();
|
||||
|
||||
} // namespace
|
||||
|
||||
const Glyph* BitmapFont::glyph_table() noexcept { return kGlyphs.data(); }
|
||||
|
||||
f32 BitmapFont::measure(const char* utf8) const {
|
||||
if (utf8 == nullptr) {
|
||||
return 0.0f;
|
||||
}
|
||||
f32 width = 0.0f;
|
||||
for (const char* p = utf8; *p != '\0';) {
|
||||
width += advance_codepoint(next_codepoint(p));
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
const Glyph* BitmapFont::glyph(u32 codepoint) const {
|
||||
// Every codepoint has a glyph: `glyph_index` maps whatever the font cannot
|
||||
// draw -- including `kInvalidCodepoint` -- to the visible box.
|
||||
return &glyph_table()[glyph_index(codepoint)];
|
||||
}
|
||||
|
||||
const u8* BitmapFont::atlas_pixels() const { return atlas_image(); }
|
||||
|
||||
u32 BitmapFont::atlas_width() const { return kAtlasWidth; }
|
||||
|
||||
u32 BitmapFont::atlas_height() const { return kAtlasHeight; }
|
||||
|
||||
u32 BitmapFont::revision() const { return 0u; }
|
||||
|
||||
f32 BitmapFont::height() const { return static_cast<f32>(kGlyphPx); }
|
||||
|
||||
f32 BitmapFont::ascent() const { return static_cast<f32>(kGlyphPx - 1u); }
|
||||
|
||||
f32 BitmapFont::advance(char c) const noexcept {
|
||||
return glyph_table()[glyph_index(static_cast<u32>(static_cast<unsigned char>(c)))].advance;
|
||||
}
|
||||
|
||||
f32 BitmapFont::advance_codepoint(u32 codepoint) const noexcept {
|
||||
return glyph_table()[glyph_index(codepoint)].advance;
|
||||
}
|
||||
|
||||
u32 BitmapFont::glyph_index(u32 codepoint) noexcept {
|
||||
if (codepoint >= kFirstChar && codepoint < kFirstChar + kAsciiGlyphs) {
|
||||
return codepoint - kFirstChar;
|
||||
}
|
||||
switch (codepoint) {
|
||||
case 0x00B0u:
|
||||
return kDegreeSlot;
|
||||
case 0x00B5u:
|
||||
return kMicroSlot;
|
||||
case 0x03A9u:
|
||||
return kOhmSlot;
|
||||
default:
|
||||
return kFallbackSlot;
|
||||
}
|
||||
}
|
||||
|
||||
u32 BitmapFont::glyph_rows(char) const noexcept { return kGlyphPx; }
|
||||
|
||||
const u8* BitmapFont::glyph_bitmap(char c) const noexcept {
|
||||
return glyph_bitmap_of(glyph_index(static_cast<u32>(static_cast<unsigned char>(c))));
|
||||
}
|
||||
|
||||
const u8* BitmapFont::glyph_bitmap_of(u32 slot) noexcept {
|
||||
return kGlyphBits[slot < kGlyphCount ? slot : kFallbackSlot];
|
||||
}
|
||||
|
||||
const u8* BitmapFont::atlas_image() noexcept { return kAtlasImage.data(); }
|
||||
|
||||
Vec2 BitmapFont::atlas_origin(u32 slot) noexcept {
|
||||
const u32 s = slot < kGlyphCount ? slot : kFallbackSlot;
|
||||
return Vec2{static_cast<f32>((s % kAtlasCols) * kGlyphPx),
|
||||
static_cast<f32>((s / kAtlasCols) * kGlyphPx)};
|
||||
}
|
||||
|
||||
} // namespace plot::render
|
||||
File diff suppressed because it is too large.
Load diff
+24
-16
@@ -88,23 +88,25 @@ f32 walk_text_clipped(Vec2 pen, const char* utf8, f32 budget_px, const render::T
|
||||
void warn_replaced_text_backend(const render::TextBackend* previous) noexcept {
|
||||
std::fprintf(stderr,
|
||||
"plot::Renderer::draw: replacing the Plot's TextBackend (%p) with the renderer's "
|
||||
"own bitmap font; a plot drawn by a Renderer measures the labels it draws.\n",
|
||||
"own embedded font; a plot drawn by a Renderer measures the labels it draws.\n",
|
||||
static_cast<const void*>(previous));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
void Renderer::AxisFont::reset(const render::BitmapFont& font, f32 requested_px) noexcept {
|
||||
void Renderer::AxisFont::reset(const render::TextBackend& font, f32 requested_px) noexcept {
|
||||
font_ = &font;
|
||||
const f32 native = static_cast<f32>(render::BitmapFont::kGlyphPx);
|
||||
// Whole multiples of the native cell only: a 1-bit glyph drawn at 1.5x lands
|
||||
// its strokes on uneven pixel counts, and the layout has to agree with what the
|
||||
// pipeline draws, so the requested size picks the nearest multiple.
|
||||
f32 steps = std::round(requested_px / native);
|
||||
if (!(steps >= 1.0f)) steps = 1.0f; // also catches NaN
|
||||
cell_px_ = steps * native;
|
||||
scale_ = cell_px_ / native;
|
||||
// The requested size is an em size, exactly as `em_px()` reports the size the
|
||||
// atlas was baked at, so the ratio between them is all the scaling there is:
|
||||
// an anti-aliased face is legible at any ratio, and a style asking for 12 px
|
||||
// from a 13 px bake gets 12/13 rather than a snapped whole multiple. A backend
|
||||
// that cannot say (`em_px()` at or below zero, or NaN) keeps a 1:1 scale rather
|
||||
// than dividing by it.
|
||||
const f32 baked = font.em_px();
|
||||
const f32 ratio = requested_px / baked;
|
||||
scale_ = (baked > 0.0f && ratio > 0.0f) ? ratio : 1.0f;
|
||||
cell_px_ = font.height() * scale_;
|
||||
}
|
||||
|
||||
f32 Renderer::AxisFont::measure(const char* utf8) const {
|
||||
@@ -119,6 +121,12 @@ f32 Renderer::AxisFont::ascent() const {
|
||||
return font_ != nullptr ? font_->ascent() * scale_ : 0.0f;
|
||||
}
|
||||
|
||||
f32 Renderer::AxisFont::em_px() const {
|
||||
// The em is the one metric that does not scale: it is the face's own unit, and
|
||||
// it is what the scale above was computed from.
|
||||
return font_ != nullptr ? font_->em_px() : 0.0f;
|
||||
}
|
||||
|
||||
const render::Glyph* Renderer::AxisFont::glyph(u32 codepoint) const {
|
||||
if (font_ == nullptr) {
|
||||
return nullptr; // nothing to draw with, so nothing to describe
|
||||
@@ -169,7 +177,7 @@ bool Renderer::init(Status* st) noexcept {
|
||||
const render::UploadMode mode =
|
||||
persistent ? render::UploadMode::PersistentMapped : render::UploadMode::Orphan;
|
||||
|
||||
font_backend_.reset(font_, style_.axis_font_px);
|
||||
font_backend_.reset(default_font_, style_.axis_font_px);
|
||||
|
||||
// The atlas is the backend's: `sync()` uploads whatever it exposes, at whatever
|
||||
// size it says, and restores the host's unpack alignment around the upload
|
||||
@@ -263,7 +271,7 @@ void Renderer::draw(Plot& plot) {
|
||||
// The plot reserves its label strips from TextBackend metrics, so it must
|
||||
// measure with the size the text pipeline draws with, and re-read the style
|
||||
// each frame so a changed axis_font_px takes effect on the next one.
|
||||
font_backend_.reset(font_, style_.axis_font_px);
|
||||
font_backend_.reset(default_font_, style_.axis_font_px);
|
||||
// The cell every glyph staged this frame is drawn in, declared from the backend
|
||||
// rather than left to whichever glyph is staged first: the instances carry rect
|
||||
// origins, and the draw carries this size (see TextBackend::cell_uv).
|
||||
@@ -280,7 +288,7 @@ void Renderer::draw(Plot& plot) {
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
// The renderer owns the text of the frames it draws: every label goes through
|
||||
// the text pipeline in `font_`, so the plot is handed this font's metrics
|
||||
// the text pipeline in `default_font_`, so the plot is handed this backend's metrics
|
||||
// whatever a host installed. A host backend is therefore replaced on the first
|
||||
// draw -- said out loud in a debug build, since its disappearance is otherwise
|
||||
// silent -- and a Plot nobody draws keeps it (see Plot::set_text_backend()).
|
||||
@@ -456,17 +464,17 @@ void Renderer::stage_xy(const f32* xy, u32 n, bool log_x, bool log_y) {
|
||||
|
||||
Vec2 Renderer::stage_text(Vec2 pos_px, const char* utf8) {
|
||||
// Text is staged in viewport pixels: the text program has no offset uniform.
|
||||
return glyphs_.add_text(utf8, to_viewport(pos_px), font_backend_.scale(), font_);
|
||||
return glyphs_.add_text(utf8, to_viewport(pos_px), font_backend_.scale(), default_font_);
|
||||
}
|
||||
|
||||
f32 Renderer::clipped_text_width(const char* utf8, f32 budget_px) const {
|
||||
return walk_text_clipped(Vec2{}, utf8, budget_px, font_, font_backend_.scale(),
|
||||
return walk_text_clipped(Vec2{}, utf8, budget_px, default_font_, font_backend_.scale(),
|
||||
[](Vec2, const render::Glyph&) {});
|
||||
}
|
||||
|
||||
f32 Renderer::stage_text_clipped(Vec2 pos_px, const char* utf8, f32 budget_px) {
|
||||
const f32 scale = font_backend_.scale();
|
||||
return walk_text_clipped(to_viewport(pos_px), utf8, budget_px, font_, scale,
|
||||
return walk_text_clipped(to_viewport(pos_px), utf8, budget_px, default_font_, scale,
|
||||
[this, scale](Vec2 pen, const render::Glyph& glyph) {
|
||||
glyphs_.add_glyph(pen, glyph, scale);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user