TODO: 多字体和字体缺失处理
This commit is contained in:
parent
e6a7254598
commit
c1e16b213e
@ -112,7 +112,7 @@ void dx_window::begin_frame() {
|
|||||||
// context.draw_line( { 600, 600 }, { mouse_x, mouse_y }, { 1, 0, 1, 1 }, thickness);
|
// 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);
|
// 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();
|
context.flush();
|
||||||
|
|
||||||
|
@ -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;
|
cursor_x += last_char ? text->get_kerning(last_char, c) : 0;
|
||||||
|
|
||||||
character_info info{};
|
const character_info* info = text->get_or_create_character(c);
|
||||||
text->get_or_create_character(c, info);
|
if (!info) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// 根据in_height缩放字符大小
|
// 根据in_height缩放字符大小
|
||||||
const float y_offset = (info.y_offset + text->get_ascent()) * 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 size { info->width * scale, info->height * scale };
|
||||||
const Eigen::Vector2f pos { cursor_x + info.x_offset * scale, cursor_y + y_offset };
|
const Eigen::Vector2f pos { cursor_x + info->x_offset * scale, cursor_y + y_offset };
|
||||||
cursor_x += info.advance * scale;
|
cursor_x += info->advance * scale;
|
||||||
|
|
||||||
aorii_vertex_param param{};
|
aorii_vertex_param param{};
|
||||||
param.param_a1 = info.tex_u;
|
param.param_a1 = info->tex_u;
|
||||||
param.param_a2 = info.tex_v;
|
param.param_a2 = info->tex_v;
|
||||||
param.param_a3 = info.tex_z;
|
param.param_a3 = info->tex_z;
|
||||||
param.param_b1 = info.width / (float)text->get_texture_size();
|
param.param_b1 = info->width / (float)text->get_texture_size();
|
||||||
param.param_b2 = info.height / (float)text->get_texture_size();
|
param.param_b2 = info->height / (float)text->get_texture_size();
|
||||||
|
|
||||||
make_rect(pos, size, in_color, 0, param);
|
make_rect(pos, size, in_color, 0, param);
|
||||||
last_char = c;
|
last_char = c;
|
||||||
|
@ -25,13 +25,13 @@ const std::wstring aorii_text::COMMON_PUNCTUATION =
|
|||||||
const std::wstring aorii_text::COMMON_NUMBERS =
|
const std::wstring aorii_text::COMMON_NUMBERS =
|
||||||
L"0123456789";
|
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),
|
texture_size(in_texture_size),
|
||||||
current_x(0), current_y(0), current_texture_index(0), scale(0) {
|
current_x(0), current_y(0), current_texture_index(0), scale(0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
aorii_text::~aorii_text() {
|
aorii_text::~aorii_text() {
|
||||||
delete font;
|
delete fonts;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool aorii_text::init_freetype() {
|
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) {
|
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));
|
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;
|
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");
|
spdlog::error("Failed to initialize font");
|
||||||
free(font_buffer);
|
free(font_buffer);
|
||||||
delete font;
|
delete font;
|
||||||
font = nullptr;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,8 +77,9 @@ bool aorii_text::initialize(const wchar_t* in_font_path, const float in_font_pix
|
|||||||
ascent *= scale;
|
ascent *= scale;
|
||||||
descent *= scale;
|
descent *= scale;
|
||||||
line_gap *= 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
|
// Create initial texture atlas
|
||||||
return create_new_texture_atlas();
|
return create_new_texture_atlas();
|
||||||
}
|
}
|
||||||
@ -96,26 +96,16 @@ bool aorii_text::create_new_texture_atlas() {
|
|||||||
return texture_array != nullptr;
|
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()) {
|
if (const auto it = character_map.find(ch); it != character_map.end()) {
|
||||||
out_info = it->second;
|
return &it->second;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载目标字符(以 '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
|
// Try to add to current atlas
|
||||||
out_info = add_character_to_atlas(glyph_index, ch);
|
return add_character_to_atlas(ch);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool aorii_text::precache_characters(const std::wstring& characters) {
|
bool aorii_text::precache_characters(const std::wstring& characters) {
|
||||||
if (!font) {
|
if (!fonts.size()) {
|
||||||
spdlog::error("Font not initialized");
|
spdlog::error("Font not initialized");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -138,14 +128,8 @@ bool aorii_text::precache_characters(const std::wstring& characters) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载目标字符(以 'A' 为例)
|
|
||||||
int glyph_index = stbtt_FindGlyphIndex(font, ch);
|
|
||||||
if (glyph_index == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成SDF数据
|
// 生成SDF数据
|
||||||
add_character_to_atlas(glyph_index, ch);
|
add_character_to_atlas(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
@ -166,31 +150,49 @@ bool aorii_text::precache_common_characters() {
|
|||||||
return precache_characters(all_common_chars);
|
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尺寸和位图
|
// 获取SDF尺寸和位图
|
||||||
int32_t width, height, x_offset, y_offset;
|
int32_t width, height, x_offset, y_offset;
|
||||||
constexpr int padding = 8;
|
constexpr int padding = 8;
|
||||||
constexpr uint8_t on_edge_value = 128;
|
constexpr uint8_t on_edge_value = 128;
|
||||||
constexpr float pixel_dist_scale = 16.f;
|
constexpr float pixel_dist_scale = 16.f;
|
||||||
|
|
||||||
auto* sdf_bitmap = stbtt_GetGlyphSDF(
|
uint8_t* sdf_bitmap = nullptr;
|
||||||
font,
|
const stbtt_fontinfo* font_info = nullptr;
|
||||||
scale,
|
for (const auto f: fonts) {
|
||||||
glyph_index,
|
const uint32_t glyph_index = stbtt_FindGlyphIndex(f, ch);
|
||||||
padding,
|
if (glyph_index == 0)
|
||||||
on_edge_value,
|
continue;
|
||||||
pixel_dist_scale,
|
sdf_bitmap = stbtt_GetGlyphSDF(
|
||||||
&width, &height,
|
f,
|
||||||
&x_offset, &y_offset
|
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) {
|
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 {
|
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) {
|
if (current_y + height > texture_size) {
|
||||||
current_texture_index++;
|
current_texture_index++;
|
||||||
if (current_texture_index >= texture_array->get_count()) {
|
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_x = 0;
|
||||||
current_y = 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;
|
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;
|
character_info info;
|
||||||
@ -237,6 +240,6 @@ character_info& aorii_text::add_character_to_atlas(int32_t glyph_index, const wc
|
|||||||
current_x += width;
|
current_x += width;
|
||||||
next_row_y = std::max(next_row_y, current_y + height);
|
next_row_y = std::max(next_row_y, current_y + height);
|
||||||
|
|
||||||
return character_map[ch] = info;
|
return &(character_map[ch] = info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ public:
|
|||||||
static void destroy_freetype();
|
static void destroy_freetype();
|
||||||
|
|
||||||
bool initialize(const wchar_t* in_font_path, float in_font_pixel_size);
|
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; }
|
[[nodiscard]] int32_t get_ascent() const { return ascent; }
|
||||||
// 下降距离
|
// 下降距离
|
||||||
@ -57,8 +57,8 @@ public:
|
|||||||
float get_kerning(wchar_t ch1, wchar_t ch2) const;
|
float get_kerning(wchar_t ch1, wchar_t ch2) const;
|
||||||
private:
|
private:
|
||||||
bool create_new_texture_atlas();
|
bool create_new_texture_atlas();
|
||||||
character_info& add_character_to_atlas(int32_t glyph_index, const wchar_t ch);
|
character_info* add_character_to_atlas(wchar_t ch);
|
||||||
stbtt_fontinfo* font;
|
std::vector<stbtt_fontinfo*> fonts;
|
||||||
|
|
||||||
renderer_texture_array* texture_array;
|
renderer_texture_array* texture_array;
|
||||||
const uint32_t texture_size;
|
const uint32_t texture_size;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user