载入中...
搜索中...
未找到
Font.cpp
浏览该文件的文档.
1#include "graphics/Font.h"
2
3#include "common/Exception.h"
4#include "font/FontData.h"
5#include "graphics/Graphics.h"
6#include "graphics/Texture.h"
7#include "image/ImageData.h"
8
9#include <algorithm>
10#include <cmath>
11#include <memory>
12#include <numeric>
13#include <vector>
14
15namespace eve::graphics {
16
17uint32_t nextCodepointUtf8(const std::string &text, size_t &i) {
18 if (i >= text.size()) return 0;
19 const auto *s = reinterpret_cast<const unsigned char *>(text.data() + i);
20 const size_t rem = text.size() - i;
21 unsigned char c0 = s[0];
22 if (c0 < 0x80) {
23 i += 1;
24 return c0;
25 }
26 if ((c0 & 0xE0) == 0xC0 && rem >= 2 && (s[1] & 0xC0) == 0x80) {
27 i += 2;
28 return ((c0 & 0x1F) << 6) | (s[1] & 0x3F);
29 }
30 if ((c0 & 0xF0) == 0xE0 && rem >= 3 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80) {
31 i += 3;
32 return ((c0 & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
33 }
34 if ((c0 & 0xF8) == 0xF0 && rem >= 4 && (s[1] & 0xC0) == 0x80 && (s[2] & 0xC0) == 0x80 &&
35 (s[3] & 0xC0) == 0x80) {
36 i += 4;
37 return ((c0 & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
38 }
39 i += 1;
40 return 0xFFFD;
41}
42
43namespace {
44
45std::vector<int> decodeCodepoints(const std::string &text) {
46 std::vector<int> out;
47 size_t i = 0;
48 while (i < text.size()) {
49 uint32_t cp = nextCodepointUtf8(text, i);
50 if (cp == 0) continue;
51 out.push_back(static_cast<int>(cp));
52 }
53 return out;
54}
55
56} // namespace
57
58std::string Font::defaultCharset() {
59 std::string s;
60 s.reserve(95);
61 for (int c = 0x20; c <= 0x7E; ++c) s.push_back(static_cast<char>(c));
62 return s;
63}
64
65Font::Font(Graphics *gfx, font::FontData *fontData, std::string charset) : data(fontData) {
66 if (gfx == nullptr) throw eve::Exception("Font: Graphics instance is null");
67 if (data == nullptr) throw eve::Exception("Font: FontData is null");
68
69 struct Raster {
70 int codepoint;
71 std::unique_ptr<image::ImageData> bitmap;
72 int bearingX, bearingY, advance;
73 };
74
75 std::vector<int> codepoints = decodeCodepoints(charset);
76 std::vector<Raster> rasters;
77 rasters.reserve(codepoints.size());
78
79 // De-dup while preserving first occurrence (charset may repeat codepoints).
80 std::unordered_map<int, bool> seen;
81 seen.reserve(codepoints.size());
82
83 long long totalArea = 0;
84 int maxGlyphSide = 1;
85 const int padding = 1;
86
87 for (int cp : codepoints) {
88 if (seen.count(cp)) continue;
89 seen[cp] = true;
90 if (!data->hasGlyph(cp)) continue;
91
92 std::unique_ptr<image::ImageData> bmp(data->newGlyphImageData(cp));
93 const int w = bmp ? bmp->getWidth() : 0;
94 const int h = bmp ? bmp->getHeight() : 0;
95 totalArea += static_cast<long long>(w + padding) * static_cast<long long>(h + padding);
96 maxGlyphSide = std::max({maxGlyphSide, w, h});
97
98 rasters.push_back({cp, std::move(bmp), data->getGlyphBearingX(cp), data->getGlyphBearingY(cp),
99 data->getGlyphAdvance(cp)});
100 }
101
102 if (rasters.empty()) {
103 // No requested codepoint decoded (e.g. empty charset) — still hand back a
104 // valid 1x1 texture so getTexture() is always safe to use.
105 image::ImageData blank(1, 1, "RGBA8");
106 atlas = gfx->newTexture(&blank);
107 return;
108 }
109
110 // Shelf-pack tallest-first into a width chosen from the total glyph area,
111 // then grow height as needed (no re-packing / no width growth pass).
112 int atlasWidth = std::max<int>(64, static_cast<int>(std::sqrt(static_cast<double>(totalArea)) * 1.2) + 1);
113 atlasWidth = std::max(atlasWidth, maxGlyphSide + 2 * padding);
114
115 std::vector<size_t> order(rasters.size());
116 std::iota(order.begin(), order.end(), 0);
117 std::sort(order.begin(), order.end(), [&](size_t a, size_t b) {
118 return rasters[a].bitmap->getHeight() > rasters[b].bitmap->getHeight();
119 });
120
121 struct Rect {
122 int x = 0, y = 0;
123 };
124 std::vector<Rect> placed(rasters.size());
125
126 int penX = padding, penY = padding, shelfH = 0;
127 int atlasHeight = padding;
128 for (size_t idx : order) {
129 const int w = rasters[idx].bitmap->getWidth();
130 const int h = rasters[idx].bitmap->getHeight();
131 if (penX + w + padding > atlasWidth) {
132 penX = padding;
133 penY += shelfH + padding;
134 shelfH = 0;
135 }
136 placed[idx] = {penX, penY};
137 shelfH = std::max(shelfH, h);
138 penX += w + padding;
139 atlasHeight = std::max(atlasHeight, penY + shelfH + padding);
140 }
141
142 image::ImageData atlasImage(atlasWidth, atlasHeight, "RGBA8");
143 glyphs.reserve(rasters.size());
144 for (size_t idx = 0; idx < rasters.size(); ++idx) {
145 const Raster &r = rasters[idx];
146 const int w = r.bitmap->getWidth();
147 const int h = r.bitmap->getHeight();
148
149 Glyph g;
150 g.width = w;
151 g.height = h;
152 g.bearingX = r.bearingX;
153 g.bearingY = r.bearingY;
154 g.advance = r.advance;
155
156 if (w > 0 && h > 0) {
157 atlasImage.paste(r.bitmap.get(), placed[idx].x, placed[idx].y, 0, 0, w, h);
158 g.u0 = static_cast<float>(placed[idx].x) / static_cast<float>(atlasWidth);
159 g.v0 = static_cast<float>(placed[idx].y) / static_cast<float>(atlasHeight);
160 g.u1 = static_cast<float>(placed[idx].x + w) / static_cast<float>(atlasWidth);
161 g.v1 = static_cast<float>(placed[idx].y + h) / static_cast<float>(atlasHeight);
162 }
163
164 glyphs.emplace(r.codepoint, g);
165 }
166
167 atlas = gfx->newTexture(&atlasImage);
168}
169
170Font::~Font() = default;
171
172float Font::getHeight() const { return data->getLineHeight(); }
173float Font::getAscent() const { return data->getAscent(); }
174float Font::getBaseline() const { return data->getBaseline(); }
175
176float Font::getWidth(const std::string &text) const { return data->getWidth(text); }
177
178bool Font::hasGlyph(int codepoint) const { return glyphs.find(codepoint) != glyphs.end(); }
179
180const Font::Glyph *Font::findGlyph(int codepoint) const {
181 auto it = glyphs.find(codepoint);
182 return it == glyphs.end() ? nullptr : &it->second;
183}
184
185} // namespace eve::graphics
int y
Definition Grass.cpp:135
int x
Definition Grass.cpp:135
int h
int w
uint32_t a
uint32_t b
uint32_t c
int idx
Light2D::Data * data
uint32_t s
Definition Weather.cpp:28
CPU-side decoded font face (FreeType FT_Face + owned font bytes). Does not upload to GPU — rasterize ...
Definition FontData.h:23
float getWidth(std::string text) const
Definition FontData.cpp:212
int getGlyphAdvance(int codepoint) const
Definition FontData.cpp:233
float getBaseline() const
Definition FontData.cpp:125
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
float getAscent() const
Definition FontData.cpp:106
float getLineHeight() const
Definition FontData.cpp:119
bool hasGlyph(int codepoint) const
字形 / 文本测量。
Definition FontData.cpp:151
float getAscent() const
Definition Font.cpp:173
Font(Graphics *gfx, font::FontData *data, std::string charset=defaultCharset())
Definition Font.cpp:65
static std::string defaultCharset()
Printable ASCII (0x20..0x7E), used when no explicit charset is given.
Definition Font.cpp:58
bool hasGlyph(int codepoint) const
Whether codepoint was rasterized into this Font's atlas.
Definition Font.cpp:178
float getBaseline() const
Distance from the top of a line to the baseline (== getAscent()).
Definition Font.cpp:174
const Glyph * findGlyph(int codepoint) const
Returns nullptr if codepoint isn't in this Font's atlas.
Definition Font.cpp:180
float getHeight() const
Line height in pixels at the FontData's decoded pixel size.
Definition Font.cpp:172
float getWidth(const std::string &text) const
Pixel width of text (UTF-8), including kerning; delegates to FontData.
Definition Font.cpp:176
virtual Texture * newTexture(int width, int height, const uint8_t *rgba, bool repeatU=false, bool repeatV=false)=0
Represents raw pixel data.
Definition ImageData.h:26
void paste(ImageData *src, int dx, int dy, int sx, int sy, int sw, int sh)
Paste part of one ImageData onto another. The subregion defined by the top-left corner (sx,...
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
Definition AnimTrail.h:6
uint32_t nextCodepointUtf8(const std::string &text, size_t &i)
Decodes one UTF-8 codepoint from text starting at byte offset i, advancing i past it....
Definition Font.cpp:17
Axis-aligned integer rectangle (screen/tile space).
Definition Math.h:46