From cc37be615ccb19c73b9fdf44468c08586d7d2fe4 Mon Sep 17 00:00:00 2001 From: Nanako <469449812@qq.com> Date: Tue, 22 Apr 2025 00:49:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=96=87=E6=9C=AC=E5=B8=83?= =?UTF-8?q?=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .clang-format => .clang-format1 | 0 example/src/main.cpp | 33 ++-- src/mirage_render/CMakeLists.txt | 8 - src/mirage_render/src/font/font_system.cpp | 192 +++++++++++++-------- src/mirage_render/src/font/font_system.h | 189 ++++++++++---------- src/mirage_render/src/font/font_type.h | 14 +- 6 files changed, 237 insertions(+), 199 deletions(-) rename .clang-format => .clang-format1 (100%) diff --git a/.clang-format b/.clang-format1 similarity index 100% rename from .clang-format rename to .clang-format1 diff --git a/example/src/main.cpp b/example/src/main.cpp index 838404b..c887454 100644 --- a/example/src/main.cpp +++ b/example/src/main.cpp @@ -60,27 +60,27 @@ int main(int argc, char* argv[]) { ss << "license: " << license << "\n"; // const char*转换为std::u32string - const auto& config_info_str = utf8::utf8to32(ss.str()); + // const auto& config_info_str = utf8::utf8to32(ss.str()); // text_block->set_text(U"Hello, World! 你好,世界!\n换行测试1111,测试测试测试测试,测试测试😀🐵🙏 😃🐵🙏"); const auto& window = mwindow::create({ 800, 600 }, L"Hello, World!"); window->set_content( mnew(mv_box) [ - mslot(mv_box) - .horizontal_alignment(horizontal_alignment_t::left) - + mnew(mbutton) - [ - mslot(mbutton) - .margin({ 10 }) - .visibility(visibility_t::visible) - [ - mnew(mtext_block, - .text(config_info_str) - .font_size(24) - ) - ] - ], + // mslot(mv_box) + // .horizontal_alignment(horizontal_alignment_t::left) + // + mnew(mbutton) + // [ + // mslot(mbutton) + // .margin({ 10 }) + // .visibility(visibility_t::visible) + // [ + // mnew(mtext_block, + // .text(config_info_str) + // .font_size(24) + // ) + // ] + // ], mslot(mv_box) .horizontal_alignment(horizontal_alignment_t::right) @@ -91,7 +91,8 @@ int main(int argc, char* argv[]) { .visibility(visibility_t::visible) [ mnew(mtext_block, - .text(U"Hello, World!") + .text(U"Hello, World! 你好,世界!\n换行测试1111,测试测试测试测试,测试测试😀🐵🙏 😃🐵🙏") + // .text(U"😀🐵🙏😀🐵🙏") .font_size(24) ) ] diff --git a/src/mirage_render/CMakeLists.txt b/src/mirage_render/CMakeLists.txt index 70adf4b..4492f91 100644 --- a/src/mirage_render/CMakeLists.txt +++ b/src/mirage_render/CMakeLists.txt @@ -7,14 +7,6 @@ add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES}) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) target_link_libraries(${PROJECT_NAME} PUBLIC mirage_core sokol mirage_image) -option(MIRAGE_SVG_EMOJI "Use svg emoji" ON) -if (MIRAGE_SVG_EMOJI) - find_package(nanosvg REQUIRED) - find_package(ZLIB REQUIRED) - target_link_libraries(${PROJECT_NAME} PUBLIC NanoSVG::nanosvg NanoSVG::nanosvgrast ZLIB::ZLIB) - target_compile_definitions(${PROJECT_NAME} PUBLIC HAS_NANOSVG) -endif () - # 设置图像加载器 set(MIRAGE_IMAGE_LOAD_BACKEND "STB_IMAGE" CACHE STRING "选择图像加载器") set_property(CACHE MIRAGE_IMAGE_LOAD_BACKEND PROPERTY STRINGS "STB_IMAGE" "CUSTOM") diff --git a/src/mirage_render/src/font/font_system.cpp b/src/mirage_render/src/font/font_system.cpp index 5e93b09..17103eb 100644 --- a/src/mirage_render/src/font/font_system.cpp +++ b/src/mirage_render/src/font/font_system.cpp @@ -1,80 +1,113 @@ #include "font_system.h" +#include + #include "emoji_detector.h" void font_manager::destroy() { - fonts_.clear(); - emoji_font_ids_.clear(); - atlas_manager_.clear(); - primary_font_id_ = -1; - next_font_id_ = 0; - destroy_font_system(); + fonts_.clear(); + emoji_font_ids_.clear(); + atlas_manager_.clear(); + primary_font_id_ = -1; + next_font_id_ = 0; + destroy_font_system(); } void font_manager::load_default_font() { #if MIRAGE_PLATFORM_WINDOWS - add_font(L"C:/Windows/Fonts/msyh.ttc"); - add_font(L"C:/Windows/Fonts/seguiemj.ttf"); + add_font(L"C:/Windows/Fonts/msyh.ttc"); + add_font(L"C:/Windows/Fonts/seguiemj.ttf"); #else static_assert(false, "暂时不支持当前平台的默认字体加载"); #endif } int font_manager::add_font(const std::filesystem::path& in_font_path, const std::string& in_font_type) { - auto font = create_font_face(in_font_path); - if (!font) { - return -1; - } + const auto font = create_font_face(in_font_path); + if (!font) { + return -1; + } - int font_id = next_font_id_++; - fonts_[font_id] = font; + const auto font_id = next_font_id_++; + fonts_[font_id] = font; - // 如果支持彩色表情或明确指定为表情字体,添加到表情字体列表 - if (font->supports_color_emoji() || in_font_type == "emoji") { - emoji_font_ids_.push_back(font_id); - } + // 如果支持彩色表情或明确指定为表情字体,添加到表情字体列表 + if (font->supports_color_emoji() || in_font_type == "emoji") { + emoji_font_ids_.push_back(font); + } - // 如果是第一个添加的字体,自动设为主字体 - if (primary_font_id_ == -1) { - primary_font_id_ = font_id; - } + // 如果是第一个添加的字体,自动设为主字体 + if (primary_font_id_ == -1) { + primary_font_id_ = font_id; + } - return font_id; + return font_id; } std::shared_ptr font_manager::get_font_for_code_point( - std::shared_ptr in_custom_primary_font, uint32_t in_code_point, uint32_t* out_glyph_index) { - if (in_custom_primary_font) { - uint32_t glyph_id = in_custom_primary_font->find_glyph_index(in_code_point); - if (glyph_id > 0) { - if (out_glyph_index) - *out_glyph_index = glyph_id; - return in_custom_primary_font; - } - } + const std::shared_ptr& in_custom_primary_font, const uint32_t in_code_point, + uint32_t* out_glyph_index) { + // **辅助 Lambda:检查字体并更新字形索引** + auto check_font = [&](const std::shared_ptr& font) -> std::shared_ptr { + if (!font) + return nullptr; // 跳过空指针 - // 对表情符号使用表情符号字体 - if (emoji_detector::is_emoji(in_code_point)) { - for (int emoji_id : emoji_font_ids_) { - auto emoji_font = fonts_[emoji_id]; - if (emoji_font && emoji_font->has_glyph(in_code_point)) { - return emoji_font; - } - } - } + // **调用字体接口查找字形索引 (Assuming 0 means 'not found')** + const uint32_t glyph_id = font->find_glyph_index(in_code_point); + if (glyph_id > 0) { + // **如果找到字形,设置输出参数并返回字体** + if (out_glyph_index) { + *out_glyph_index = glyph_id; + } + return font; // 找到字形 + } + return nullptr; // 在此字体中未找到字形 + }; - // 尝试所有其他字体 - for (const auto& [id, font] : fonts_) { - uint32_t glyph_id = font->find_glyph_index(in_code_point); - if (id != primary_font_id_ && glyph_id > 0) { - if (out_glyph_index) - *out_glyph_index = glyph_id; - return font; - } - } + // **1. 尝试自定义主字体 (如果提供了)** + if (auto found_font = check_font(in_custom_primary_font)) { + return found_font; // 在主字体中找到,直接返回 + } - // 回退到主字体 - return in_custom_primary_font; + // 确定字符类型 + const bool is_emoji = emoji_detector::is_emoji(in_code_point); + + // **2. 定义字体过滤器** + // 此过滤器排除了空指针字体和已检查过的主字体。 + // 注意: 更复杂的过滤器可以基于 is_emoji 和字体元数据 (例如 is_emoji_font()) + // 来优先选择特定类型的字体(例如,为表情符号优先选择表情字体)。 + auto font_filter = [&](const std::shared_ptr& font) { + // **确保字体有效且不是我们已经检查过的主字体** + if (is_emoji) { + return std::ranges::contains(emoji_font_ids_, font); + } + return font != nullptr && font != in_custom_primary_font; + }; + + // **3. 遍历字体管理器中所有其他适用的字体** + for (const auto& font : std::views::values(fonts_) | std::views::filter(font_filter)) { + // **检查当前遍历到的字体** + if (auto found_font = check_font(font)) { + return found_font; // 在其他受管字体中找到,返回 + } + } + + // **4. 最终回退逻辑** + // 如果代码执行到这里,表示在主字体(如果提供)和所有其他过滤后的字体中都未找到该字形。 + // (可选) 在这里可以尝试一个全局默认回退字体: + // if (m_global_fallback_font) { + // if (auto found_font = check_font(m_global_fallback_font)) { + // return found_font; + // } + // } + + // **未能找到任何包含该字形的字体** + // **将输出字形索引设置为 0 (表示未找到)** + if (out_glyph_index) { + *out_glyph_index = 0; + } + // **返回 nullptr,表示没有找到合适的字体** + return nullptr; } text_layout_t font_manager::layout_text( @@ -82,8 +115,7 @@ text_layout_t font_manager::layout_text( const std::shared_ptr& in_font, float font_size, float max_width, - float line_spacing) -{ + float line_spacing) { text_layout_t layout; // 使用指定字体或主字体 @@ -92,18 +124,18 @@ text_layout_t font_manager::layout_text( primary_font->set_font_size(font_size); // 初始化布局变量 - float cursor_x = 0.0f; // 当前光标X位置 - float cursor_y = 0.0f; // 当前光标Y位置 - float width = 0.0f; // 总布局宽度 - float height = 0.0f; // 总布局高度 + float cursor_x = 0.0f; // 当前光标X位置 + float cursor_y = 0.0f; // 当前光标Y位置 + float width = 0.0f; // 总布局宽度 + float height = 0.0f; // 总布局高度 uint32_t prev_glyph_id = 0; // 上一个字形ID(用于字距调整) // 当前行信息 struct line_info { - float height = 0.0f; // 行总高度 - float ascent = 0.0f; // 最大上升距离 - float descent = 0.0f; // 最大下降距离 - float line_width = 0.0f; // 行宽度 + float height = 0.0f; // 行总高度 + float ascent = 0.0f; // 最大上升距离 + float descent = 0.0f; // 最大下降距离 + float line_width = 0.0f; // 行宽度 bool has_content = false; // 标记行是否有内容 }; @@ -127,6 +159,13 @@ text_layout_t font_manager::layout_text( } }; + // 设置字体大小 + for (const auto& font : fonts_ | std::views::values) { + font->set_font_size(font_size); + } + + std::map, font_v_metrics_t> font_metrics; + /** * @brief 处理单个字符的布局 * @@ -143,18 +182,19 @@ text_layout_t font_manager::layout_text( const bool is_emoji = emoji_detector::is_emoji(c); // 查找能够渲染此字符的字体 - uint32_t glyph_index = 0; + uint32_t glyph_index = 0; const auto& using_font = get_font_for_code_point(primary_font, c, &glyph_index); if (glyph_index == 0) { - return; // 如果没有找到支持的字体,跳过该字符 + return; // 如果没有找到支持的字体,跳过该字符 } - // 设置当前字体大小 - using_font->set_font_size(font_size); + if (!font_metrics.contains(using_font)) { + font_metrics[using_font] = using_font->get_metrics(); + } // 获取当前字体的度量信息 - const auto& current_metrics = using_font->get_metrics(); + const auto& current_metrics = font_metrics[using_font]; // 获取字形度量信息(形状、尺寸、间距等) const auto& glyph_metrics = using_font->shape_glyph(glyph_index); @@ -163,7 +203,7 @@ text_layout_t font_manager::layout_text( const auto& region = get_or_create_glyph_by_index(glyph_metrics.glyph_index, using_font, font_size, is_emoji); if (!region) { - return; // 如果无法获取或创建字形区域,跳过该字符 + return; // 如果无法获取或创建字形区域,跳过该字符 } // 检查是否需要自动换行 @@ -183,7 +223,8 @@ text_layout_t font_manager::layout_text( // 计算基线位置 - 基于当前行的上升距离 const auto baseline = cursor_y + current_line.ascent; - const auto size_y_diff = glyph_metrics.rect.size().y() - region->rect.size().y(); // 计算Y轴差异(字形位图高度与区域高度) + // 计算Y轴差异(字形位图高度与区域高度) + const auto size_y_diff = glyph_metrics.rect.size().y() - static_cast(region->rect.size().y()); // 计算字形绘制坐标 auto x = cursor_x + glyph_metrics.offset.x(); @@ -237,19 +278,18 @@ std::optional font_manager::get_or_create_glyph_by_index( int32_t in_glyph_id, const std::shared_ptr& in_font, float in_font_size, - bool is_emoji) -{ + bool is_emoji) { // 验证字体有效性 if (!in_font) { return std::nullopt; } - return atlas_manager_.get_or_create_glyph(in_glyph_id, in_font, in_font_size, is_emoji); + return atlas_manager_.get_or_create_glyph(in_glyph_id, in_font, in_font_size, is_emoji); } font_manager::font_manager() { - // 初始化字体系统 - if (!init_font_system()) { - throw std::runtime_error("无法初始化字体系统"); - } + // 初始化字体系统 + if (!init_font_system()) { + throw std::runtime_error("无法初始化字体系统"); + } } diff --git a/src/mirage_render/src/font/font_system.h b/src/mirage_render/src/font/font_system.h index bd3661a..24f9431 100644 --- a/src/mirage_render/src/font/font_system.h +++ b/src/mirage_render/src/font/font_system.h @@ -21,73 +21,77 @@ */ class font_manager { public: - /** - * @brief 获取单例实例 - * - * @return font_manager& 字体管理器的单例引用 - */ - static font_manager& instance() { - static font_manager instance; - return instance; - } + /** + * @brief 获取单例实例 + * + * @return font_manager& 字体管理器的单例引用 + */ + static font_manager& instance() { + static font_manager instance; + return instance; + } - /** - * @brief 销毁所有资源 - * - * 清理所有字体和图集资源,重置字体管理器状态。 - */ - void destroy(); + /** + * @brief 销毁所有资源 + * + * 清理所有字体和图集资源,重置字体管理器状态。 + */ + void destroy(); - /** - * @brief 加载操作系统默认字体 - */ - void load_default_font(); + /** + * @brief 加载操作系统默认字体 + */ + void load_default_font(); - /** - * @brief 添加字体 - * - * 从文件加载字体并添加到字体管理系统中。 - * 如果字体支持彩色表情符号或指定为表情符号字体,将自动添加到表情符号字体列表。 - * - * @param in_font_path 字体文件路径 - * @param in_font_type 字体类型(regular, bold, italic, emoji等) - * @return 添加成功返回字体ID,失败返回-1 - */ - int add_font(const std::filesystem::path& in_font_path, const std::string& in_font_type = "regular"); + /** + * @brief 添加字体 + * + * 从文件加载字体并添加到字体管理系统中。 + * 如果字体支持彩色表情符号或指定为表情符号字体,将自动添加到表情符号字体列表。 + * + * @param in_font_path 字体文件路径 + * @param in_font_type 字体类型(regular, bold, italic, emoji等) + * @return 添加成功返回字体ID,失败返回-1 + */ + int add_font(const std::filesystem::path& in_font_path, const std::string& in_font_type = "regular"); - /** - * @brief 设置主字体 - * - * @param in_font_id 要设为主字体的字体ID - * @return 设置是否成功 - */ - bool set_primary_font(int in_font_id) { - if (fonts_.contains(in_font_id)) { - primary_font_id_ = in_font_id; - return true; - } - return false; - } + /** + * @brief 设置主字体 + * + * @param in_font_id 要设为主字体的字体ID + * @return 设置是否成功 + */ + bool set_primary_font(int in_font_id) { + if (fonts_.contains(in_font_id)) { + primary_font_id_ = in_font_id; + return true; + } + return false; + } - /** - * @brief 获取指定码点的最佳字体 - * - * 实现字体回退机制,按以下顺序尝试查找支持指定码点的字体: - * 1. 主字体 - * 2. 如果是表情符号,尝试表情符号字体 - * 3. 所有其他字体 - * 4. 回退到主字体 - * - * @param in_code_point Unicode码点 - * @return 最适合渲染该码点的字体 - */ - std::shared_ptr get_font_for_code_point(uint32_t in_code_point, uint32_t* out_glyph_index = nullptr) { - auto primary = get_primary_font(); - return get_font_for_code_point(primary, in_code_point, out_glyph_index); - } - std::shared_ptr get_font_for_code_point(std::shared_ptr in_custom_primary_font, uint32_t in_code_point, uint32_t* out_glyph_index = nullptr); + /** + * @brief 获取指定码点的最佳字体 + * + * 实现字体回退机制,按以下顺序尝试查找支持指定码点的字体: + * 1. 主字体 + * 2. 如果是表情符号,尝试表情符号字体 + * 3. 所有其他字体 + * 4. 回退到主字体 + * + * @param in_code_point Unicode码点 + * @return 最适合渲染该码点的字体 + */ + std::shared_ptr get_font_for_code_point(uint32_t in_code_point, + uint32_t* out_glyph_index = nullptr) { + auto primary = get_primary_font(); + return get_font_for_code_point(primary, in_code_point, out_glyph_index); + } - /** + std::shared_ptr get_font_for_code_point( + const std::shared_ptr& in_custom_primary_font, uint32_t in_code_point, + uint32_t* out_glyph_index = nullptr); + + /** * @brief 获取主字体 * @return 主字体 */ @@ -106,16 +110,16 @@ public: return !emoji_font_ids_.empty(); } - /** - * @brief 计算文本布局,确定每个字形的位置和大小 - * - * @param text 要布局的Unicode文本 - * @param in_font 指定的字体(如果为nullptr则使用主字体) - * @param font_size 字体大小(像素单位) - * @param max_width 文本的最大宽度(用于自动换行,0表示不限制宽度) - * @param line_spacing 行间距因子(1.0表示标准行距) - * @return text_layout_t 包含所有字形位置和布局信息的结构 - */ + /** + * @brief 计算文本布局,确定每个字形的位置和大小 + * + * @param text 要布局的Unicode文本 + * @param in_font 指定的字体(如果为nullptr则使用主字体) + * @param font_size 字体大小(像素单位) + * @param max_width 文本的最大宽度(用于自动换行,0表示不限制宽度) + * @param line_spacing 行间距因子(1.0表示标准行距) + * @return text_layout_t 包含所有字形位置和布局信息的结构 + */ text_layout_t layout_text( const std::u32string& text, const std::shared_ptr& in_font, @@ -140,28 +144,29 @@ public: float in_font_size, bool is_emoji); - /** - * @brief 获取所有字形图集 - * @return 字形图集列表的常量引用 - */ - [[nodiscard]] const auto& get_glyph_atlases() const { - return atlas_manager_.get_glyph_atlases(); - } + /** + * @brief 获取所有字形图集 + * @return 字形图集列表的常量引用 + */ + [[nodiscard]] const auto& get_glyph_atlases() const { + return atlas_manager_.get_glyph_atlases(); + } + + /** + * @brief 获取所有表情图集 + * @return 表情图集列表的常量引用 + */ + [[nodiscard]] const auto& get_emoji_atlases() const { + return atlas_manager_.get_emoji_atlases(); + } - /** - * @brief 获取所有表情图集 - * @return 表情图集列表的常量引用 - */ - [[nodiscard]] const auto& get_emoji_atlases() const { - return atlas_manager_.get_emoji_atlases(); - } private: - font_manager(); + font_manager(); - std::unordered_map> fonts_; ///< 字体ID到字体对象的映射 - font_atlas_manager atlas_manager_; ///< 图集管理器 + std::unordered_map> fonts_; ///< 字体ID到字体对象的映射 + font_atlas_manager atlas_manager_; ///< 图集管理器 - std::vector emoji_font_ids_; ///< 表情符号字体ID列表 - int primary_font_id_ = -1; ///< 主字体ID - int next_font_id_ = 0; ///< 下一个可用的字体ID -}; \ No newline at end of file + std::vector> emoji_font_ids_; ///< 表情符号字体ID列表 + int32_t primary_font_id_ = -1; ///< 主字体ID + int32_t next_font_id_ = 0; ///< 下一个可用的字体ID +}; diff --git a/src/mirage_render/src/font/font_type.h b/src/mirage_render/src/font/font_type.h index 30d26e2..e2f1385 100644 --- a/src/mirage_render/src/font/font_type.h +++ b/src/mirage_render/src/font/font_type.h @@ -12,9 +12,9 @@ class texture2d; * @brief 文本水平对齐方式 */ enum class horizontal_text_alignment_t { - left, ///< 文本左对齐 + left, ///< 文本左对齐 center, ///< 文本居中对齐 - right ///< 文本右对齐 + right ///< 文本右对齐 }; /** @@ -32,7 +32,7 @@ enum class vertical_text_alignment_t { * @brief 已定位的字形 */ struct positioned_glyph_t { - uint32_t code_point = 0; ///< Unicode码点 + uint32_t code_point = 0; ///< Unicode码点 Eigen::Vector2f position; ///< 屏幕位置 rect_t<> bounds; ///< 边界矩形 float advance = 0.0f; ///< 前进量 @@ -91,9 +91,9 @@ struct text_line_t { * @brief 段落结构 */ struct text_paragraph_t { - std::vector lines; ///< 段落中的行 - flow_direction_t flow_direction; ///< 文本方向 - rect_t<> bounds; ///< 段落边界 + std::vector lines; ///< 段落中的行 + flow_direction_t flow_direction; ///< 文本方向 + rect_t<> bounds; ///< 段落边界 }; /** @@ -118,7 +118,7 @@ struct text_style_t { struct text_layout_t { struct glyph_position_t { bool is_emoji; // 是否为表情符号 - int32_t glyph_index; // 字符码点 + uint32_t glyph_index; // 字符码点 Eigen::Vector2f position; // 屏幕位置 Eigen::Vector2f size; // 字形尺寸 atlas_region_t region; // 纹理图集区域