支持多字体加载
This commit is contained in:
parent
b6c5bdae77
commit
0937bb8a60
@ -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}, U"你好,世界!全是水群大师\n测试换行\n测试Unicode: 😀\nТест по русскому языку\nテスト日本語", 32);
|
||||
context.draw_string({0, 0}, U"你好,世界!全是水群大师\n测试换行\n测试Unicode: 😀\nТест по русскому языку\nテスト日本語", 32, {0, 0, 0, 1});
|
||||
|
||||
context.flush();
|
||||
|
||||
|
@ -6,8 +6,10 @@
|
||||
|
||||
aorii_text* text = nullptr;
|
||||
void renderer_context::init() {
|
||||
text = new aorii_text(2048);
|
||||
text->initialize(LR"(C:\Windows\Fonts\msyh.ttc)", 64);
|
||||
text = new aorii_text();
|
||||
// D:\Projects\aorii\JetBrainsMono-Regular.ttf
|
||||
text->initialize(LR"(HarmonyOS_Sans_SC_Regular.ttf)", 64);
|
||||
text->add_font(LR"(C:\Windows\Fonts\seguiemj.ttf)");
|
||||
// text->precache_common_characters();
|
||||
}
|
||||
|
||||
|
@ -25,18 +25,19 @@ const std::u32string aorii_text::COMMON_PUNCTUATION =
|
||||
const std::u32string aorii_text::COMMON_NUMBERS =
|
||||
U"0123456789";
|
||||
|
||||
aorii_text::aorii_text(const uint32_t in_texture_size) : font(nullptr), texture_array(nullptr), font_pixel_size(0),
|
||||
aorii_text::font_data::~font_data() {
|
||||
delete info;
|
||||
}
|
||||
|
||||
aorii_text::aorii_text() : texture_array(nullptr), font_pixel_size(0),
|
||||
space_width(0),
|
||||
ascent(0), descent(0),
|
||||
line_gap(0),
|
||||
scale(0),
|
||||
texture_size(in_texture_size), current_x(0), current_y(0),
|
||||
current_x(0), current_y(0),
|
||||
current_texture_index(0) {
|
||||
}
|
||||
|
||||
aorii_text::~aorii_text() {
|
||||
delete font;
|
||||
}
|
||||
aorii_text::~aorii_text() {}
|
||||
|
||||
bool aorii_text::init_freetype() {
|
||||
return true;
|
||||
@ -45,47 +46,84 @@ bool aorii_text::init_freetype() {
|
||||
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();
|
||||
bool aorii_text::initialize(const std::wstring& in_font_path, const float in_font_pixel_size) {
|
||||
font_pixel_size = in_font_pixel_size;
|
||||
font_file.map_file(in_font_path);
|
||||
|
||||
auto font_buffer = (uint8_t*)font_file.get_data();
|
||||
if (!stbtt_InitFont(font, font_buffer, stbtt_GetFontOffsetForIndex(font_buffer, 0))) {
|
||||
spdlog::error("Failed to initialize font");
|
||||
font_file.unmap();
|
||||
delete font;
|
||||
font = nullptr;
|
||||
// 预计需要的纹理数量(8张纹理几乎可以容纳所有字符)
|
||||
constexpr uint32_t expected_textures = 8;
|
||||
texture_array = aorii::get_renderer_raw()->create_texture_array(2048, 2048, expected_textures, texture_format::R8_UNORM, device_memory_type::gpu);
|
||||
if (!texture_array) {
|
||||
spdlog::error("无法创建字符缓冲纹理");
|
||||
return false;
|
||||
}
|
||||
|
||||
scale = stbtt_ScaleForPixelHeight(font, in_font_pixel_size);
|
||||
if (!add_font(in_font_path)) {
|
||||
spdlog::error("添加主要字体失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取字体的垂直度量
|
||||
stbtt_GetFontVMetrics(font, &ascent, &descent, &line_gap);
|
||||
stbtt_GetCodepointHMetrics(font, ' ', &space_width, nullptr);
|
||||
ascent *= scale;
|
||||
descent *= scale;
|
||||
line_gap *= scale;
|
||||
space_width *= scale;
|
||||
spdlog::info("Font vertical metrics:\n 上升距离: {}, 下降距离: {}, 行间距: {}", ascent, descent, line_gap);
|
||||
// 获取主字体的度量信息
|
||||
const auto& primary_font = fonts[0];
|
||||
stbtt_GetFontVMetrics(primary_font.info, &ascent, &descent, &line_gap);
|
||||
stbtt_GetCodepointHMetrics(primary_font.info, ' ', &space_width, nullptr);
|
||||
stbtt_GetCodepointHMetrics(primary_font.info, '\t', &tab_width, nullptr);
|
||||
|
||||
// Create initial texture atlas
|
||||
return create_new_texture_atlas();
|
||||
ascent *= primary_font.scale;
|
||||
descent *= primary_font.scale;
|
||||
line_gap *= primary_font.scale;
|
||||
space_width *= primary_font.scale;
|
||||
tab_width *= primary_font.scale;
|
||||
|
||||
spdlog::info("主字体度量信息:\n 上升距离: {}, 下降距离: {}, 行间距: {}", ascent, descent, line_gap);
|
||||
return true;
|
||||
}
|
||||
|
||||
float aorii_text::get_kerning(char32_t ch1, char32_t ch2) const {
|
||||
// 获取字距调整
|
||||
return stbtt_GetCodepointKernAdvance(font, ch1, ch2) * scale;
|
||||
font_data const* left_ch_font = nullptr;
|
||||
int g1 = 0, g2 = 0;
|
||||
|
||||
// 在所有字体中查找字符对的字距调整
|
||||
for (const auto& font : fonts) {
|
||||
g1 = stbtt_FindGlyphIndex(font.info, ch1);
|
||||
g2 = stbtt_FindGlyphIndex(font.info, ch2);
|
||||
|
||||
if (g1 != 0) {
|
||||
left_ch_font = &font;
|
||||
}
|
||||
if (g1 != 0 && g2 != 0) {
|
||||
return stbtt_GetGlyphKernAdvance(font.info, g1, g2) * font.scale;
|
||||
}
|
||||
}
|
||||
if (!left_ch_font)
|
||||
return 0.0f;
|
||||
|
||||
int x0, x1;
|
||||
stbtt_GetGlyphBox(left_ch_font->info, g1, &x0, nullptr, &x1, nullptr);
|
||||
return (x1 - x0) * left_ch_font->scale;
|
||||
}
|
||||
|
||||
bool aorii_text::create_new_texture_atlas() {
|
||||
// 预计需要的纹理数量(8张纹理几乎可以容纳所有字符)
|
||||
constexpr uint32_t expected_textures = 8;
|
||||
texture_array = aorii::get_renderer_raw()->create_texture_array(texture_size, texture_size, expected_textures, texture_format::R8_UNORM, device_memory_type::gpu);
|
||||
return texture_array != nullptr;
|
||||
bool aorii_text::add_font(const std::wstring& font_path) {
|
||||
font_data new_font{};
|
||||
new_font.info = new stbtt_fontinfo();
|
||||
|
||||
// 映射字体文件
|
||||
if (!new_font.file.map_file(font_path)) {
|
||||
delete new_font.info;
|
||||
spdlog::error("Failed to map font file");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto* font_buffer = static_cast<uint8_t*>(new_font.file.get_data());
|
||||
if (!stbtt_InitFont(new_font.info, font_buffer, stbtt_GetFontOffsetForIndex(font_buffer, 0))) {
|
||||
new_font.file.unmap();
|
||||
delete new_font.info;
|
||||
spdlog::error("Failed to initialize font");
|
||||
return false;
|
||||
}
|
||||
|
||||
new_font.scale = stbtt_ScaleForPixelHeight(new_font.info, font_pixel_size);
|
||||
fonts.emplace_back(std::move(new_font));
|
||||
return true;
|
||||
}
|
||||
|
||||
character_info* aorii_text::get_or_create_character(const char32_t ch) {
|
||||
@ -97,11 +135,6 @@ character_info* aorii_text::get_or_create_character(const char32_t ch) {
|
||||
}
|
||||
|
||||
bool aorii_text::precache_characters(const std::u32string& characters) {
|
||||
if (!font) {
|
||||
spdlog::error("Font not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建进度通知回调的函数指针类型
|
||||
using ProgressCallback = std::function<void(float progress, const wchar_t currentChar)>;
|
||||
|
||||
@ -142,10 +175,33 @@ 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 result = 0;
|
||||
for (const auto& font : fonts) {
|
||||
result += font.info->numGlyphs;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t aorii_text::get_texture_size() const {
|
||||
return texture_array->size().x();
|
||||
}
|
||||
|
||||
character_info* aorii_text::add_character_to_atlas(const char32_t ch) {
|
||||
int glyph_index = stbtt_FindGlyphIndex(font, ch);
|
||||
font_data const* current_font = nullptr;
|
||||
int glyph_index = 0;
|
||||
for (const auto& font : fonts) {
|
||||
if (glyph_index = stbtt_FindGlyphIndex(font.info, ch)) {
|
||||
current_font = &font;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!current_font) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto font = current_font->info;
|
||||
const uint32_t texture_size = get_texture_size();
|
||||
|
||||
// 获取SDF尺寸和位图
|
||||
int32_t width, height, x_offset, y_offset;
|
||||
constexpr int padding = 8;
|
||||
@ -154,7 +210,7 @@ character_info* aorii_text::add_character_to_atlas(const char32_t ch) {
|
||||
|
||||
auto* sdf_bitmap = stbtt_GetGlyphSDF(
|
||||
font,
|
||||
scale,
|
||||
current_font->scale,
|
||||
glyph_index,
|
||||
padding,
|
||||
on_edge_value,
|
||||
@ -204,7 +260,7 @@ character_info* aorii_text::add_character_to_atlas(const char32_t ch) {
|
||||
character_info info;
|
||||
info.tex_u = current_x / static_cast<float>(texture_size);
|
||||
info.tex_v = current_y / static_cast<float>(texture_size);
|
||||
info.advance = advance_width * scale;
|
||||
info.advance = advance_width * current_font->scale;
|
||||
info.tex_z = static_cast<float>(current_texture_index);
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
@ -217,4 +273,3 @@ character_info* aorii_text::add_character_to_atlas(const char32_t ch) {
|
||||
|
||||
return &(character_map[ch] = info);
|
||||
}
|
||||
|
||||
|
@ -22,25 +22,37 @@ struct character_info {
|
||||
};
|
||||
|
||||
class aorii_text {
|
||||
struct font_data {
|
||||
font_data() = default;
|
||||
font_data(font_data&& other) {
|
||||
file = std::move(other.file);
|
||||
|
||||
info = other.info;
|
||||
scale = other.scale;
|
||||
other.info = nullptr;
|
||||
other.scale = 0;
|
||||
}
|
||||
~font_data();
|
||||
|
||||
stbtt_fontinfo* info;
|
||||
mapped_file file;
|
||||
float scale;
|
||||
};
|
||||
public:
|
||||
aorii_text(uint32_t in_texture_size = 2048);
|
||||
aorii_text();
|
||||
~aorii_text();
|
||||
|
||||
static bool init_freetype();
|
||||
static void destroy_freetype();
|
||||
|
||||
bool initialize(const wchar_t* in_font_path, float in_font_pixel_size);
|
||||
bool initialize(const std::wstring& in_font_path, float in_font_pixel_size);
|
||||
bool add_font(const std::wstring& font_path);
|
||||
character_info* get_or_create_character(char32_t ch);
|
||||
// 空格宽度
|
||||
[[nodiscard]] int32_t get_space_width() const { return space_width; }
|
||||
// 上升距离
|
||||
[[nodiscard]] int32_t get_ascent() const { return ascent; }
|
||||
// 下降距离
|
||||
[[nodiscard]] int32_t get_descent() const { return descent; }
|
||||
// 行间距
|
||||
[[nodiscard]] int32_t get_line_gap() const { return line_gap; }
|
||||
// 纹理尺寸
|
||||
[[nodiscard]] uint32_t get_texture_size() const { return texture_size; }
|
||||
|
||||
// 字体像素大小
|
||||
[[nodiscard]] float get_font_pixel_size() const { return font_pixel_size; }
|
||||
|
||||
@ -57,20 +69,18 @@ public:
|
||||
[[nodiscard]] uint32_t get_font_char_count() const;
|
||||
|
||||
[[nodiscard]] renderer_texture_array* get_texture_array() const { return texture_array; }
|
||||
[[nodiscard]] uint32_t get_texture_size() const;
|
||||
|
||||
[[nodiscard]] float get_kerning(char32_t ch1, char32_t ch2) const;
|
||||
private:
|
||||
bool create_new_texture_atlas();
|
||||
character_info* add_character_to_atlas(char32_t ch);
|
||||
stbtt_fontinfo* font;
|
||||
mapped_file font_file;
|
||||
|
||||
std::vector<font_data> fonts;
|
||||
|
||||
renderer_texture_array* texture_array;
|
||||
float font_pixel_size;
|
||||
int32_t space_width, ascent, descent, line_gap;
|
||||
float scale;
|
||||
int32_t space_width, tab_width, ascent, descent, line_gap;
|
||||
|
||||
const uint32_t texture_size;
|
||||
uint32_t current_x;
|
||||
uint32_t current_y;
|
||||
uint32_t next_row_y = 0; // 跟踪当前行的最大高度
|
||||
|
Loading…
x
Reference in New Issue
Block a user