支持多字体加载
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);
|
// 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}, 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();
|
context.flush();
|
||||||
|
|
||||||
|
@ -6,8 +6,10 @@
|
|||||||
|
|
||||||
aorii_text* text = nullptr;
|
aorii_text* text = nullptr;
|
||||||
void renderer_context::init() {
|
void renderer_context::init() {
|
||||||
text = new aorii_text(2048);
|
text = new aorii_text();
|
||||||
text->initialize(LR"(C:\Windows\Fonts\msyh.ttc)", 64);
|
// 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();
|
// text->precache_common_characters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,19 +25,20 @@ const std::u32string aorii_text::COMMON_PUNCTUATION =
|
|||||||
const std::u32string aorii_text::COMMON_NUMBERS =
|
const std::u32string aorii_text::COMMON_NUMBERS =
|
||||||
U"0123456789";
|
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() {
|
||||||
space_width(0),
|
delete info;
|
||||||
ascent(0), descent(0),
|
|
||||||
line_gap(0),
|
|
||||||
scale(0),
|
|
||||||
texture_size(in_texture_size), current_x(0), current_y(0),
|
|
||||||
current_texture_index(0) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
aorii_text::~aorii_text() {
|
aorii_text::aorii_text() : texture_array(nullptr), font_pixel_size(0),
|
||||||
delete font;
|
space_width(0),
|
||||||
|
ascent(0), descent(0),
|
||||||
|
line_gap(0),
|
||||||
|
current_x(0), current_y(0),
|
||||||
|
current_texture_index(0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aorii_text::~aorii_text() {}
|
||||||
|
|
||||||
bool aorii_text::init_freetype() {
|
bool aorii_text::init_freetype() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -45,47 +46,84 @@ bool aorii_text::init_freetype() {
|
|||||||
void aorii_text::destroy_freetype() {
|
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 std::wstring& 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();
|
|
||||||
font_pixel_size = 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();
|
// 预计需要的纹理数量(8张纹理几乎可以容纳所有字符)
|
||||||
if (!stbtt_InitFont(font, font_buffer, stbtt_GetFontOffsetForIndex(font_buffer, 0))) {
|
constexpr uint32_t expected_textures = 8;
|
||||||
spdlog::error("Failed to initialize font");
|
texture_array = aorii::get_renderer_raw()->create_texture_array(2048, 2048, expected_textures, texture_format::R8_UNORM, device_memory_type::gpu);
|
||||||
font_file.unmap();
|
if (!texture_array) {
|
||||||
delete font;
|
spdlog::error("无法创建字符缓冲纹理");
|
||||||
font = nullptr;
|
|
||||||
return false;
|
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);
|
const auto& primary_font = fonts[0];
|
||||||
stbtt_GetCodepointHMetrics(font, ' ', &space_width, nullptr);
|
stbtt_GetFontVMetrics(primary_font.info, &ascent, &descent, &line_gap);
|
||||||
ascent *= scale;
|
stbtt_GetCodepointHMetrics(primary_font.info, ' ', &space_width, nullptr);
|
||||||
descent *= scale;
|
stbtt_GetCodepointHMetrics(primary_font.info, '\t', &tab_width, nullptr);
|
||||||
line_gap *= scale;
|
|
||||||
space_width *= scale;
|
|
||||||
spdlog::info("Font vertical metrics:\n 上升距离: {}, 下降距离: {}, 行间距: {}", ascent, descent, line_gap);
|
|
||||||
|
|
||||||
// Create initial texture atlas
|
ascent *= primary_font.scale;
|
||||||
return create_new_texture_atlas();
|
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 {
|
float aorii_text::get_kerning(char32_t ch1, char32_t ch2) const {
|
||||||
// 获取字距调整
|
font_data const* left_ch_font = nullptr;
|
||||||
return stbtt_GetCodepointKernAdvance(font, ch1, ch2) * scale;
|
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() {
|
bool aorii_text::add_font(const std::wstring& font_path) {
|
||||||
// 预计需要的纹理数量(8张纹理几乎可以容纳所有字符)
|
font_data new_font{};
|
||||||
constexpr uint32_t expected_textures = 8;
|
new_font.info = new stbtt_fontinfo();
|
||||||
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;
|
// 映射字体文件
|
||||||
|
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) {
|
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) {
|
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)>;
|
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);
|
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) {
|
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尺寸和位图
|
// 获取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;
|
||||||
@ -154,7 +210,7 @@ character_info* aorii_text::add_character_to_atlas(const char32_t ch) {
|
|||||||
|
|
||||||
auto* sdf_bitmap = stbtt_GetGlyphSDF(
|
auto* sdf_bitmap = stbtt_GetGlyphSDF(
|
||||||
font,
|
font,
|
||||||
scale,
|
current_font->scale,
|
||||||
glyph_index,
|
glyph_index,
|
||||||
padding,
|
padding,
|
||||||
on_edge_value,
|
on_edge_value,
|
||||||
@ -204,7 +260,7 @@ character_info* aorii_text::add_character_to_atlas(const char32_t ch) {
|
|||||||
character_info info;
|
character_info info;
|
||||||
info.tex_u = current_x / static_cast<float>(texture_size);
|
info.tex_u = current_x / static_cast<float>(texture_size);
|
||||||
info.tex_v = current_y / 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.tex_z = static_cast<float>(current_texture_index);
|
||||||
info.width = width;
|
info.width = width;
|
||||||
info.height = height;
|
info.height = height;
|
||||||
@ -217,4 +273,3 @@ character_info* aorii_text::add_character_to_atlas(const char32_t ch) {
|
|||||||
|
|
||||||
return &(character_map[ch] = info);
|
return &(character_map[ch] = info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,25 +22,37 @@ struct character_info {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class aorii_text {
|
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:
|
public:
|
||||||
aorii_text(uint32_t in_texture_size = 2048);
|
aorii_text();
|
||||||
~aorii_text();
|
~aorii_text();
|
||||||
|
|
||||||
static bool init_freetype();
|
static bool init_freetype();
|
||||||
static void destroy_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);
|
character_info* get_or_create_character(char32_t ch);
|
||||||
// 空格宽度
|
|
||||||
[[nodiscard]] int32_t get_space_width() const { return space_width; }
|
[[nodiscard]] int32_t get_space_width() const { return space_width; }
|
||||||
// 上升距离
|
|
||||||
[[nodiscard]] int32_t get_ascent() const { return ascent; }
|
[[nodiscard]] int32_t get_ascent() const { return ascent; }
|
||||||
// 下降距离
|
|
||||||
[[nodiscard]] int32_t get_descent() const { return descent; }
|
[[nodiscard]] int32_t get_descent() const { return descent; }
|
||||||
// 行间距
|
|
||||||
[[nodiscard]] int32_t get_line_gap() const { return line_gap; }
|
[[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; }
|
[[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]] uint32_t get_font_char_count() const;
|
||||||
|
|
||||||
[[nodiscard]] renderer_texture_array* get_texture_array() const { return texture_array; }
|
[[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;
|
[[nodiscard]] float get_kerning(char32_t ch1, char32_t ch2) const;
|
||||||
private:
|
private:
|
||||||
bool create_new_texture_atlas();
|
|
||||||
character_info* add_character_to_atlas(char32_t ch);
|
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;
|
renderer_texture_array* texture_array;
|
||||||
float font_pixel_size;
|
float font_pixel_size;
|
||||||
int32_t space_width, ascent, descent, line_gap;
|
int32_t space_width, tab_width, ascent, descent, line_gap;
|
||||||
float scale;
|
|
||||||
|
|
||||||
const uint32_t texture_size;
|
|
||||||
uint32_t current_x;
|
uint32_t current_x;
|
||||||
uint32_t current_y;
|
uint32_t current_y;
|
||||||
uint32_t next_row_y = 0; // 跟踪当前行的最大高度
|
uint32_t next_row_y = 0; // 跟踪当前行的最大高度
|
||||||
|
Loading…
x
Reference in New Issue
Block a user