载入中...
搜索中...
未找到
FontData.cpp
浏览该文件的文档.
1#include "font/FontData.h"
2
3#include "common/Exception.h"
4#include "image/ImageData.h"
5
6#include <ft2build.h>
7#include FT_FREETYPE_H
8#include FT_GLYPH_H
9
10#include <algorithm>
11#include <cstring>
12#include <utility>
13
14namespace eve {
15namespace font {
16
17namespace {
18
19FT_Library &ftLibrary() {
20 static struct Holder {
21 FT_Library lib = nullptr;
22 Holder() {
23 if (FT_Init_FreeType(&lib) != 0)
24 lib = nullptr;
25 }
26 ~Holder() {
27 if (lib)
28 FT_Done_FreeType(lib);
29 }
30 } holder;
31 return holder.lib;
32}
33
34// Decode one UTF-8 codepoint; advances `i`. Returns 0 on invalid / truncated.
35uint32_t nextCodepoint(const std::string &text, size_t &i) {
36 if (i >= text.size())
37 return 0;
38 const auto *s = reinterpret_cast<const unsigned char *>(text.data() + i);
39 const size_t rem = text.size() - i;
40 unsigned char c0 = s[0];
41 if (c0 < 0x80) {
42 i += 1;
43 return c0;
44 }
45 if ((c0 & 0xE0) == 0xC0 && rem >= 2 && (s[1] & 0xC0) == 0x80) {
46 i += 2;
47 return ((c0 & 0x1F) << 6) | (s[1] & 0x3F);
48 }
49 if ((c0 & 0xF0) == 0xE0 && rem >= 3 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80) {
50 i += 3;
51 return ((c0 & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
52 }
53 if ((c0 & 0xF8) == 0xF0 && rem >= 4 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80 &&
54 (s[3] & 0xC0) == 0x80) {
55 i += 4;
56 return ((c0 & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
57 }
58 i += 1;
59 return 0xFFFD;
60}
61
62} // namespace
63
64FontData::FontData(std::vector<uint8_t> fontBytes, int size, std::string uri)
65 : Resource(std::move(uri)), bytes(std::move(fontBytes)), pixelSize(size) {
66 if (bytes.empty())
67 throw eve::Exception("Cannot decode empty font data");
68 if (pixelSize <= 0)
69 throw eve::Exception("Font pixel size must be positive");
70
71 FT_Library lib = ftLibrary();
72 if (!lib)
73 throw eve::Exception("FreeType library init failed");
74
75 FT_Error err = FT_New_Memory_Face(lib, bytes.data(), static_cast<FT_Long>(bytes.size()), 0, &face);
76 if (err != 0 || !face)
77 throw eve::Exception("Could not decode font data (FreeType error %d)", static_cast<int>(err));
78
79 err = FT_Set_Pixel_Sizes(face, 0, static_cast<FT_UInt>(pixelSize));
80 if (err != 0) {
81 FT_Done_Face(face);
82 face = nullptr;
83 throw eve::Exception("Could not set font pixel size (FreeType error %d)", static_cast<int>(err));
84 }
85
86 glyphCache.resize(static_cast<size_t>(std::max<FT_Long>(face->num_glyphs, 1)));
87}
88
90 if (face) {
91 FT_Done_Face(face);
92 face = nullptr;
93 }
94}
95
96void FontData::adopt(eve::Resource &replacement) {
97 auto &other = static_cast<FontData &>(replacement);
98 std::swap(bytes, other.bytes);
99 std::swap(face, other.face);
100 std::swap(pixelSize, other.pixelSize);
101 std::swap(glyphCache, other.glyphCache);
102}
103
104int FontData::getSize() const { return pixelSize; }
105
106float FontData::getAscent() const {
107 if (!face)
108 return 0.f;
109 return static_cast<float>(face->size->metrics.ascender) / 64.f;
110}
111
112float FontData::getDescent() const {
113 if (!face)
114 return 0.f;
115 // FreeType descent is typically negative; expose as negative for layout.
116 return static_cast<float>(face->size->metrics.descender) / 64.f;
117}
118
120 if (!face)
121 return static_cast<float>(pixelSize);
122 return static_cast<float>(face->size->metrics.height) / 64.f;
123}
124
125float FontData::getBaseline() const { return getAscent(); }
126
127std::string FontData::getFamilyName() const {
128 if (!face || !face->family_name)
129 return {};
130 return face->family_name;
131}
132
133std::string FontData::getStyleName() const {
134 if (!face || !face->style_name)
135 return {};
136 return face->style_name;
137}
138
140 if (!face)
141 return 0;
142 return static_cast<int>(face->num_glyphs);
143}
144
145unsigned int FontData::glyphIndex(int codepoint) const {
146 if (!face || codepoint < 0)
147 return 0;
148 return FT_Get_Char_Index(face, static_cast<FT_ULong>(codepoint));
149}
150
151bool FontData::hasGlyph(int codepoint) const {
152 return glyphIndex(codepoint) != 0;
153}
154
155bool FontData::hasGlyphs(std::string text) const {
156 size_t i = 0;
157 while (i < text.size()) {
158 uint32_t cp = nextCodepoint(text, i);
159 if (cp == 0)
160 continue;
161 if (!hasGlyph(static_cast<int>(cp)))
162 return false;
163 }
164 return true;
165}
166
167float FontData::getKerning(int leftCodepoint, int rightCodepoint) const {
168 if (!face || !FT_HAS_KERNING(face))
169 return 0.f;
170 FT_UInt left = glyphIndex(leftCodepoint);
171 FT_UInt right = glyphIndex(rightCodepoint);
172 if (left == 0 || right == 0)
173 return 0.f;
174 FT_Vector delta;
175 if (FT_Get_Kerning(face, left, right, FT_KERNING_DEFAULT, &delta) != 0)
176 return 0.f;
177 return static_cast<float>(delta.x) / 64.f;
178}
179
180const FontData::GlyphCache &FontData::ensureGlyph(int codepoint) const {
181 static GlyphCache missing;
182 if (!face)
183 return missing;
184
185 FT_UInt index = glyphIndex(codepoint);
186
187 if (index >= glyphCache.size()) {
188 glyphCache.resize(index + 1);
189 }
190
191 GlyphCache &slot = glyphCache[index];
192 if (slot.loaded)
193 return slot;
194
195 FT_Error err = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT);
196 if (err != 0) {
197 slot.loaded = true;
198 slot.empty = true;
199 return slot;
200 }
201
202 slot.advance = static_cast<int>((face->glyph->advance.x + 32) >> 6);
203 slot.bearingX = face->glyph->bitmap_left;
204 slot.bearingY = face->glyph->bitmap_top;
205 slot.width = static_cast<int>((face->glyph->metrics.width + 63) >> 6);
206 slot.height = static_cast<int>((face->glyph->metrics.height + 63) >> 6);
207 slot.empty = (index == 0 && codepoint != 0);
208 slot.loaded = true;
209 return slot;
210}
211
212float FontData::getWidth(std::string text) const {
213 float width = 0.f;
214 int prev = -1;
215 size_t i = 0;
216 while (i < text.size()) {
217 uint32_t cp = nextCodepoint(text, i);
218 if (cp == 0)
219 continue;
220 int code = static_cast<int>(cp);
221 if (prev >= 0)
222 width += getKerning(prev, code);
223 width += static_cast<float>(ensureGlyph(code).advance);
224 prev = code;
225 }
226 return width;
227}
228
229int FontData::getGlyphWidth(int codepoint) const { return ensureGlyph(codepoint).width; }
230int FontData::getGlyphHeight(int codepoint) const { return ensureGlyph(codepoint).height; }
231int FontData::getGlyphBearingX(int codepoint) const { return ensureGlyph(codepoint).bearingX; }
232int FontData::getGlyphBearingY(int codepoint) const { return ensureGlyph(codepoint).bearingY; }
233int FontData::getGlyphAdvance(int codepoint) const { return ensureGlyph(codepoint).advance; }
234
236 if (!face)
237 return new image::ImageData(0, 0, "RGBA8");
238
239 FT_UInt index = glyphIndex(codepoint);
240 FT_Error err = FT_Load_Glyph(face, index, FT_LOAD_RENDER);
241 if (err != 0 || !face->glyph->bitmap.buffer || face->glyph->bitmap.width == 0 ||
242 face->glyph->bitmap.rows == 0) {
243 // Still refresh metric cache.
244 ensureGlyph(codepoint);
245 return new image::ImageData(0, 0, "RGBA8");
246 }
247
248 const FT_Bitmap &bm = face->glyph->bitmap;
249 const int w = static_cast<int>(bm.width);
250 const int h = static_cast<int>(bm.rows);
251
252 // Update cache with rendered metrics.
253 if (index >= glyphCache.size())
254 glyphCache.resize(index + 1);
255 GlyphCache &slot = glyphCache[index];
256 slot.width = w;
257 slot.height = h;
258 slot.bearingX = face->glyph->bitmap_left;
259 slot.bearingY = face->glyph->bitmap_top;
260 slot.advance = static_cast<int>((face->glyph->advance.x + 32) >> 6);
261 slot.empty = false;
262 slot.loaded = true;
263
264 auto *img = new image::ImageData(w, h, "RGBA8");
265 auto *dst = static_cast<unsigned char *>(img->getData());
266
267 if (bm.pixel_mode == FT_PIXEL_MODE_GRAY) {
268 for (int y = 0; y < h; ++y) {
269 const unsigned char *src = bm.buffer + y * bm.pitch;
270 unsigned char *row = dst + y * w * 4;
271 for (int x = 0; x < w; ++x) {
272 unsigned char a = src[x];
273 row[x * 4 + 0] = 255;
274 row[x * 4 + 1] = 255;
275 row[x * 4 + 2] = 255;
276 row[x * 4 + 3] = a;
277 }
278 }
279 } else if (bm.pixel_mode == FT_PIXEL_MODE_MONO) {
280 for (int y = 0; y < h; ++y) {
281 const unsigned char *src = bm.buffer + y * bm.pitch;
282 unsigned char *row = dst + y * w * 4;
283 for (int x = 0; x < w; ++x) {
284 unsigned char byte = src[x >> 3];
285 unsigned char a = (byte & (0x80 >> (x & 7))) ? 255 : 0;
286 row[x * 4 + 0] = 255;
287 row[x * 4 + 1] = 255;
288 row[x * 4 + 2] = 255;
289 row[x * 4 + 3] = a;
290 }
291 }
292 } else {
293 // Unsupported pixel mode — leave transparent.
294 std::memset(dst, 0, static_cast<size_t>(w) * static_cast<size_t>(h) * 4u);
295 }
296
297 return img;
298}
299
300} // namespace font
301} // namespace eve
int y
Definition Grass.cpp:135
int x
Definition Grass.cpp:135
int h
int w
uint32_t a
int width
uint32_t s
Definition Weather.cpp:28
Resource is a game object that is managed by the ResourceManager. It can be loaded from a file or gen...
Definition Resource.h:35
CPU-side decoded font face (FreeType FT_Face + owned font bytes). Does not upload to GPU — rasterize ...
Definition FontData.h:23
FontData(std::vector< uint8_t > bytes, int pixelSize, std::string uri="")
从字体字节创建指定像素尺寸的字体面。
Definition FontData.cpp:64
float getWidth(std::string text) const
Definition FontData.cpp:212
int getGlyphHeight(int codepoint) const
Definition FontData.cpp:230
int getGlyphAdvance(int codepoint) const
Definition FontData.cpp:233
float getBaseline() const
Definition FontData.cpp:125
std::string getFamilyName() const
字体元信息。
Definition FontData.cpp:127
bool hasGlyphs(std::string text) const
Definition FontData.cpp:155
int getGlyphBearingX(int codepoint) const
Definition FontData.cpp:231
image::ImageData * newGlyphImageData(int codepoint)
Rasterize one glyph to RGBA8 ImageData (RGB white, A from FreeType gray). Returns a 0x0 ImageData if ...
Definition FontData.cpp:235
int getGlyphBearingY(int codepoint) const
Definition FontData.cpp:232
void adopt(eve::Resource &replacement) override
Replace this face with replacement's (cache reload).
Definition FontData.cpp:96
float getKerning(int leftCodepoint, int rightCodepoint) const
Definition FontData.cpp:167
float getAscent() const
Definition FontData.cpp:106
int getSize() const
字号 / 度量信息。
Definition FontData.cpp:104
int getGlyphCount() const
Definition FontData.cpp:139
std::string getStyleName() const
Definition FontData.cpp:133
float getLineHeight() const
Definition FontData.cpp:119
float getDescent() const
Definition FontData.cpp:112
~FontData() override
Definition FontData.cpp:89
bool hasGlyph(int codepoint) const
字形 / 文本测量。
Definition FontData.cpp:151
int getGlyphWidth(int codepoint) const
单字形度量(像素)。
Definition FontData.cpp:229
Represents raw pixel data.
Definition ImageData.h:26
WidgetDesc text(std::string content, std::string id)
Static text label.
Definition Widget.cpp:235
Definition Build.cpp:11