MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
GlyphAtlas.cpp
Go to the documentation of this file.
1#include "GlyphAtlas.hpp"
2
4
6
7GlyphAtlas::GlyphAtlas(FontFace& face, uint32_t pixel_size, uint32_t atlas_size)
8 : m_face(face)
9 , m_pixel_size(pixel_size)
10 , m_atlas_size(atlas_size)
11{
12 m_texture = std::make_unique<Kakshya::TextureContainer>(
14
15 FT_Set_Pixel_Sizes(m_face.get_face(), 0, m_pixel_size);
16}
17
18const GlyphMetrics* GlyphAtlas::get_or_rasterize(FT_UInt glyph_index)
19{
20 auto it = m_cache.find(glyph_index);
21 if (it != m_cache.end()) {
22 return &it->second;
23 }
24
25 if (!rasterize(glyph_index)) {
26 return nullptr;
27 }
28
29 return &m_cache.at(glyph_index);
30}
31
33{
34 if (!m_face.is_loaded()) {
36 "GlyphAtlas::get_or_rasterize -- FontFace not loaded");
37 return nullptr;
38 }
39
40 const FT_UInt idx = FT_Get_Char_Index(m_face.get_face(), codepoint);
41 if (idx == 0) {
43 "GlyphAtlas: no glyph for codepoint U+{:04X}", static_cast<uint32_t>(codepoint));
44 return nullptr;
45 }
46
47 return get_or_rasterize(idx);
48}
49
50[[nodiscard]] uint32_t GlyphAtlas::line_height() const
51{
52 FT_Face face = m_face.get_face();
53 if (!face || face->size == nullptr) {
54 return m_pixel_size;
55 }
56 const int32_t h = (face->size->metrics.ascender - face->size->metrics.descender) >> 6;
57 return h > 0 ? static_cast<uint32_t>(h) : m_pixel_size;
58}
59
60[[nodiscard]] uint32_t GlyphAtlas::ascender() const
61{
62 FT_Face face = m_face.get_face();
63 if (!face || face->size == nullptr)
64 return m_pixel_size;
65 const int32_t a = face->size->metrics.ascender >> 6;
66 return a > 0 ? static_cast<uint32_t>(a) : m_pixel_size;
67}
68
69bool GlyphAtlas::rasterize(FT_UInt glyph_index)
70{
71 FT_Face face = m_face.get_face();
72
73 if (const FT_Error err = FT_Set_Pixel_Sizes(face, 0, m_pixel_size); err != 0) {
75 "FT_Set_Pixel_Sizes({}) failed: {}", m_pixel_size, static_cast<int>(err));
76 return false;
77 }
78
79 if (const FT_Error err = FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER); err != 0) {
81 "FT_Load_Glyph({}) failed: {}", glyph_index, static_cast<int>(err));
82 return false;
83 }
84
85 const FT_Bitmap& bmp = face->glyph->bitmap;
86 const uint32_t gw = bmp.width;
87 const uint32_t gh = bmp.rows;
88
89 constexpr uint32_t k_pad = 1;
90
91 if (m_cursor_x + gw + k_pad > m_atlas_size) {
92 m_cursor_x = 0;
93 m_cursor_y += m_shelf_height + k_pad;
95 }
96
97 if (m_cursor_y + gh + k_pad > m_atlas_size) {
99 "GlyphAtlas full at pixel_size={} atlas_size={}. "
100 "Construct with a larger atlas_size.",
102 return false;
103 }
104
105 const std::span<uint8_t> pixels = m_texture->pixel_bytes(0);
106 if (pixels.empty()) {
108 "GlyphAtlas: TextureContainer pixel_bytes returned empty span");
109 return false;
110 }
111
112 const uint32_t stride = m_atlas_size;
113
114 for (uint32_t row = 0; row < gh; ++row) {
115 const uint8_t* src = bmp.buffer + static_cast<size_t>(row * static_cast<uint32_t>(std::abs(bmp.pitch)));
116 uint8_t* dst = pixels.data() + static_cast<size_t>((m_cursor_y + row) * stride) + m_cursor_x;
117 std::memcpy(dst, src, gw);
118 }
119
120 const float inv = 1.F / static_cast<float>(m_atlas_size);
121
122 GlyphMetrics m;
123 m.uv_x0 = static_cast<float>(m_cursor_x) * inv;
124 m.uv_y0 = static_cast<float>(m_cursor_y) * inv;
125 m.uv_x1 = static_cast<float>(m_cursor_x + gw) * inv;
126 m.uv_y1 = static_cast<float>(m_cursor_y + gh) * inv;
127 m.bearing_x = face->glyph->bitmap_left;
128 m.bearing_y = face->glyph->bitmap_top;
129 m.width = gw;
130 m.height = gh;
131 m.advance_x = static_cast<int32_t>(face->glyph->advance.x >> 6);
132
133 m_cache.emplace(glyph_index, m);
134
135 m_cursor_x += gw + k_pad;
136 if (gh > m_shelf_height) {
137 m_shelf_height = gh;
138 }
139
140 m_dirty = true;
141 return true;
142}
143
144} // namespace MayaFlux::Portal::Text
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
const std::vector< float > * pixels
Definition Decoder.cpp:58
uint32_t h
Definition InkPress.cpp:28
size_t a
FT_Face get_face() const
Raw FT_Face handle for use by GlyphAtlas.
Definition FontFace.hpp:57
bool is_loaded() const
Returns true after a successful load() call.
Definition FontFace.hpp:51
Owns a single FT_Face loaded from a file path.
Definition FontFace.hpp:24
std::unique_ptr< Kakshya::TextureContainer > m_texture
uint32_t line_height() const
Line advance in pixels for this atlas's pixel_size.
const GlyphMetrics * get_or_rasterize(FT_UInt glyph_index)
Return metrics for a glyph, rasterizing it into the atlas if needed.
bool rasterize(FT_UInt glyph_index)
std::unordered_map< FT_UInt, GlyphMetrics > m_cache
uint32_t ascender() const
Ascender in pixels for this atlas's pixel_size.
uint32_t atlas_size() const
Atlas texture dimension (width == height == atlas_size).
GlyphAtlas(FontFace &face, uint32_t pixel_size, uint32_t atlas_size=512)
Construct an atlas for a specific face and pixel size.
Definition GlyphAtlas.cpp:7
@ API
API calls from external code.
@ Portal
High-level user-facing API layer.
int32_t bearing_x
Horizontal bearing in pixels (from FT_GlyphSlot).
float uv_x0
Left UV edge in atlas texture.
float uv_x1
Right UV edge in atlas texture.
uint32_t height
Glyph bitmap height in pixels.
int32_t advance_x
Horizontal advance in pixels (26.6 fixed-point >> 6).
uint32_t width
Glyph bitmap width in pixels.
float uv_y0
Top UV edge in atlas texture.
int32_t bearing_y
Vertical bearing in pixels (from FT_GlyphSlot).
float uv_y1
Bottom UV edge in atlas texture.
Per-glyph layout and UV data produced by GlyphAtlas.