TODO: 多字体和字体缺失处理

This commit is contained in:
Nanako 2024-12-24 09:32:17 +08:00
parent e6a7254598
commit c1e16b213e
4 changed files with 63 additions and 58 deletions

View File

@ -112,7 +112,7 @@ void dx_window::begin_frame() {
// context.draw_line( { 600, 600 }, { mouse_x, mouse_y }, { 1, 0, 1, 1 }, thickness);
// if (test_texture) context.draw_texture({ 0.f, 0.f }, test_texture->size().cast<float>(), test_texture);
context.draw_string({0, 0}, L"你好,世界!全是水群大师\n测试换行", 32);
context.draw_string({0, 0}, L"你好,世界!全是水群大师\n测试换行\n测试日语: かわいい\n测试俄语: милый", 32);
context.flush();

View File

@ -54,21 +54,23 @@ void renderer_context::draw_string(const Eigen::Vector2f& in_pos, const std::wst
}
cursor_x += last_char ? text->get_kerning(last_char, c) : 0;
character_info info{};
text->get_or_create_character(c, info);
const character_info* info = text->get_or_create_character(c);
if (!info) {
continue;
}
// 根据in_height缩放字符大小
const float y_offset = (info.y_offset + text->get_ascent()) * scale;
const Eigen::Vector2f size { info.width * scale, info.height * scale };
const Eigen::Vector2f pos { cursor_x + info.x_offset * scale, cursor_y + y_offset };
cursor_x += info.advance * scale;
const float y_offset = (info->y_offset + text->get_ascent()) * scale;
const Eigen::Vector2f size { info->width * scale, info->height * scale };
const Eigen::Vector2f pos { cursor_x + info->x_offset * scale, cursor_y + y_offset };
cursor_x += info->advance * scale;
aorii_vertex_param param{};
param.param_a1 = info.tex_u;
param.param_a2 = info.tex_v;
param.param_a3 = info.tex_z;
param.param_b1 = info.width / (float)text->get_texture_size();
param.param_b2 = info.height / (float)text->get_texture_size();
param.param_a1 = info->tex_u;
param.param_a2 = info->tex_v;
param.param_a3 = info->tex_z;
param.param_b1 = info->width / (float)text->get_texture_size();
param.param_b2 = info->height / (float)text->get_texture_size();
make_rect(pos, size, in_color, 0, param);
last_char = c;

View File

@ -25,13 +25,13 @@ const std::wstring aorii_text::COMMON_PUNCTUATION =
const std::wstring aorii_text::COMMON_NUMBERS =
L"0123456789";
aorii_text::aorii_text(const uint32_t in_texture_size) : font(nullptr), texture_array(nullptr),
aorii_text::aorii_text(const uint32_t in_texture_size) : fonts(nullptr), texture_array(nullptr),
texture_size(in_texture_size),
current_x(0), current_y(0), current_texture_index(0), scale(0) {
}
aorii_text::~aorii_text() {
delete font;
delete fonts;
}
bool aorii_text::init_freetype() {
@ -44,7 +44,7 @@ void aorii_text::destroy_freetype() {
bool aorii_text::initialize(const wchar_t* in_font_path, const float in_font_pixel_size) {
const std::string font_path_a(in_font_path, in_font_path + wcslen(in_font_path));
font = new stbtt_fontinfo();
auto font = new stbtt_fontinfo();
font_pixel_size = in_font_pixel_size;
// 加载字体文件
@ -67,7 +67,6 @@ bool aorii_text::initialize(const wchar_t* in_font_path, const float in_font_pix
spdlog::error("Failed to initialize font");
free(font_buffer);
delete font;
font = nullptr;
return false;
}
@ -78,8 +77,9 @@ bool aorii_text::initialize(const wchar_t* in_font_path, const float in_font_pix
ascent *= scale;
descent *= scale;
line_gap *= scale;
spdlog::info("Font vertical metrics:\n 上升距离: {}, 下降距离: {}, 行间距: {}", ascent, descent, line_gap);
spdlog::info("Font vertical metrics: 上升距离: {}, 下降距离: {}, 行间距: {}", ascent, descent, line_gap);
fonts.push_back(font);
// Create initial texture atlas
return create_new_texture_atlas();
}
@ -96,26 +96,16 @@ bool aorii_text::create_new_texture_atlas() {
return texture_array != nullptr;
}
bool aorii_text::get_or_create_character(const wchar_t ch, character_info& out_info) {
character_info* aorii_text::get_or_create_character(const wchar_t ch) {
if (const auto it = character_map.find(ch); it != character_map.end()) {
out_info = it->second;
return true;
return &it->second;
}
// 加载目标字符(以 'A' 为例)
int glyph_index = stbtt_FindGlyphIndex(font, ch);
if (glyph_index == 0) {
printf("Failed to find glyph for character\n");
return false;
}
// Try to add to current atlas
out_info = add_character_to_atlas(glyph_index, ch);
return true;
return add_character_to_atlas(ch);
}
bool aorii_text::precache_characters(const std::wstring& characters) {
if (!font) {
if (!fonts.size()) {
spdlog::error("Font not initialized");
return false;
}
@ -138,14 +128,8 @@ bool aorii_text::precache_characters(const std::wstring& characters) {
continue;
}
// 加载目标字符(以 'A' 为例)
int glyph_index = stbtt_FindGlyphIndex(font, ch);
if (glyph_index == 0) {
continue;
}
// 生成SDF数据
add_character_to_atlas(glyph_index, ch);
add_character_to_atlas(ch);
}
return success;
@ -166,31 +150,49 @@ bool aorii_text::precache_common_characters() {
return precache_characters(all_common_chars);
}
uint32_t aorii_text::get_font_char_count() const { return font->numGlyphs; }
uint32_t aorii_text::get_font_char_count() const {
uint32_t char_count = 0;
for (auto& font : fonts) {
char_count += font->numGlyphs;
}
return char_count;
}
character_info& aorii_text::add_character_to_atlas(int32_t glyph_index, const wchar_t ch) {
character_info* aorii_text::add_character_to_atlas(const wchar_t ch) {
// 获取SDF尺寸和位图
int32_t width, height, x_offset, y_offset;
constexpr int padding = 8;
constexpr uint8_t on_edge_value = 128;
constexpr float pixel_dist_scale = 16.f;
auto* sdf_bitmap = stbtt_GetGlyphSDF(
font,
scale,
glyph_index,
padding,
on_edge_value,
pixel_dist_scale,
&width, &height,
&x_offset, &y_offset
);
uint8_t* sdf_bitmap = nullptr;
const stbtt_fontinfo* font_info = nullptr;
for (const auto f: fonts) {
const uint32_t glyph_index = stbtt_FindGlyphIndex(f, ch);
if (glyph_index == 0)
continue;
sdf_bitmap = stbtt_GetGlyphSDF(
f,
scale,
glyph_index,
padding,
on_edge_value,
pixel_dist_scale,
&width, &height,
&x_offset, &y_offset
);
if (sdf_bitmap) {
font_info = f;
break;
}
}
if (!sdf_bitmap) {
throw std::runtime_error("Failed to generate SDF for glyph");
spdlog::error("Failed to generate SDF for glyph {}", ch);
return nullptr;
}
ON_SCOPE_EXIT {
stbtt_FreeSDF(sdf_bitmap, font->userdata);
stbtt_FreeSDF(sdf_bitmap, font_info->userdata);
};
// 检查当前行是否有足够空间
@ -204,7 +206,8 @@ character_info& aorii_text::add_character_to_atlas(int32_t glyph_index, const wc
if (current_y + height > texture_size) {
current_texture_index++;
if (current_texture_index >= texture_array->get_count()) {
throw std::runtime_error("Texture array capacity exceeded");
spdlog::error("Texture array capacity exceeded");
return nullptr;
}
current_x = 0;
current_y = 0;
@ -220,7 +223,7 @@ character_info& aorii_text::add_character_to_atlas(int32_t glyph_index, const wc
// 获取字形度量信息
int advance_width, left_side_bearing;
stbtt_GetGlyphHMetrics(font, glyph_index, &advance_width, &left_side_bearing);
stbtt_GetGlyphHMetrics(font_info, glyph_index, &advance_width, &left_side_bearing);
// 创建并存储字符信息
character_info info;
@ -237,6 +240,6 @@ character_info& aorii_text::add_character_to_atlas(int32_t glyph_index, const wc
current_x += width;
next_row_y = std::max(next_row_y, current_y + height);
return character_map[ch] = info;
return &(character_map[ch] = info);
}

View File

@ -28,7 +28,7 @@ public:
static void destroy_freetype();
bool initialize(const wchar_t* in_font_path, float in_font_pixel_size);
bool get_or_create_character(wchar_t ch, character_info& out_info);
character_info* get_or_create_character(wchar_t ch);
// 上升距离
[[nodiscard]] int32_t get_ascent() const { return ascent; }
// 下降距离
@ -57,8 +57,8 @@ public:
float get_kerning(wchar_t ch1, wchar_t ch2) const;
private:
bool create_new_texture_atlas();
character_info& add_character_to_atlas(int32_t glyph_index, const wchar_t ch);
stbtt_fontinfo* font;
character_info* add_character_to_atlas(wchar_t ch);
std::vector<stbtt_fontinfo*> fonts;
renderer_texture_array* texture_array;
const uint32_t texture_size;