优化代码可读性

This commit is contained in:
2025-06-23 17:55:55 +08:00
parent 3db251aac6
commit c9b4e6dccd
12 changed files with 216 additions and 208 deletions

View File

@@ -246,7 +246,7 @@ void destroy_font_system() {
FT_Done_FreeType(library_);
}
std::shared_ptr<font_face_interface> create_font_face(const std::filesystem::path& in_path) {
font_face_ptr create_font_face(const std::filesystem::path& in_path) {
auto font_face = std::make_shared<freetype_interface>();
if (!font_face->load(in_path)) {
return nullptr;

View File

@@ -178,7 +178,7 @@ void destroy_font_system() {
// stb_truetype不需要销毁
}
std::shared_ptr<font_face_interface> create_font_face(const std::filesystem::path& in_path) {
font_face_ptr create_font_face(const std::filesystem::path& in_path) {
auto font_face = std::make_shared<stb_font_face_t>();
if (!font_face->load(in_path)) {
return nullptr;

View File

@@ -2,7 +2,7 @@
#include "interface/font_interface.h"
char_key_t::char_key_t(uint32_t in_glyph_index, const std::shared_ptr<font_face_interface>& in_font_face,
char_key_t::char_key_t(uint32_t in_glyph_index, const font_face_ptr& in_font_face,
float in_font_size):
glyph_index(in_glyph_index),
font_face(in_font_face),

View File

@@ -31,10 +31,10 @@ struct glyph_atlas_result_t {
struct char_key_t {
uint32_t glyph_index;
std::shared_ptr<font_face_interface> font_face;
font_face_ptr font_face;
float font_size;
char_key_t(uint32_t in_glyph_index, const std::shared_ptr<font_face_interface>& in_font_face, float in_font_size);
char_key_t(uint32_t in_glyph_index, const font_face_ptr& in_font_face, float in_font_size);
bool operator==(const char_key_t& other) const {
// Implement comparison logic. Might involve comparing font pointers

View File

@@ -71,7 +71,7 @@ std::optional<atlas_region_t> find_or_create_in_atlases(
std::optional<atlas_region_t> font_atlas_manager::get_or_create_glyph(
const int32_t in_glyph_id,
const std::shared_ptr<font_face_interface>& in_font,
const font_face_ptr& in_font,
const float in_font_size,
const bool is_emoji) {
const char_key_t key(in_glyph_id, in_font, in_font_size);

View File

@@ -30,7 +30,7 @@ public:
*/
std::optional<atlas_region_t> get_or_create_glyph(
int32_t in_glyph_id,
const std::shared_ptr<font_face_interface>& in_font,
const font_face_ptr& in_font,
float in_font_size,
bool is_emoji);

View File

@@ -4,6 +4,74 @@
#include "emoji_detector.h"
namespace text_layout_impl {
void finalize_line_glyphs(text_layout_t& layout, const text_layout_t::line_info_t& line, float cursor_y) {
const float line_height = line.max_line_height;
const float content_height = line.max_ascent + line.max_descent;
const float baseline_y = cursor_y + line.max_ascent + (line_height - content_height) / 2.f;
for (const auto& pending: line.glyphs) {
const float size_y_diff =
pending.metrics.rect.size().y() - static_cast<float>(pending.region.rect.size().y());
auto& glyph = layout.glyphs.emplace_back();
glyph.is_emoji = pending.is_emoji;
glyph.glyph_index = pending.glyph_index;
glyph.position = { pending.start_x + pending.metrics.offset.x(),
baseline_y + pending.metrics.offset.y() + size_y_diff };
glyph.size = pending.metrics.rect.size();
glyph.region = pending.region;
}
}
void update_line_metrics(text_layout_t::line_info_t& line, const font_v_metrics_t& font_metrics, float line_spacing) {
line.max_ascent = std::max(line.max_ascent, font_metrics.ascent);
line.max_descent = std::max(line.max_descent, std::abs(font_metrics.descent));
line.max_line_height = std::max(line.max_line_height, font_metrics.line_height * line_spacing);
}
void add_pending_glyph(text_layout_t::line_info_t& line,
const font_manager::glyph_info_t& info,
float cursor_x) {
text_layout_t::pending_glyph_t pending;
pending.glyph_index = info.glyph_index;
pending.prev_glyph_id_for_kerning = line.prev_glyph_id;
pending.start_x = cursor_x;
pending.metrics = info.metrics;
pending.region = info.region;
pending.is_emoji = info.is_emoji;
pending.font = info.font;
line.glyphs.push_back(pending);
}
bool should_wrap_line(float max_width, float next_x, bool has_content) {
return max_width > 0 && next_x > max_width && has_content;
}
void handle_newline(bool& at_line_start,
text_layout_t::line_info_t& line,
float& total_height,
const std::function<void(bool)>& finish_line) {
if (at_line_start) {
total_height += line.max_line_height;
} else {
finish_line(true);
}
}
void finalize_layout(const text_layout_t::line_info_t& line,
bool at_line_start,
bool text_empty,
float& total_height,
float default_line_height,
const std::function<void(bool)>& finish_line) {
if (line.has_content) {
finish_line(false);
} else if (at_line_start && !text_empty) {
total_height += line.max_line_height;
}
}
} // namespace text_layout_impl
void font_manager::destroy() {
fonts_.clear();
emoji_font_ids_.clear();
@@ -44,11 +112,11 @@ int font_manager::add_font(const std::filesystem::path& in_font_path, const std:
return font_id;
}
std::shared_ptr<font_face_interface> font_manager::get_font_for_code_point(
const std::shared_ptr<font_face_interface>& in_custom_primary_font, const uint32_t in_code_point,
font_face_ptr font_manager::get_font_for_code_point(
const font_face_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_face_interface>& font) -> std::shared_ptr<font_face_interface> {
auto check_font = [&](const font_face_ptr& font) -> font_face_ptr {
if (!font)
return nullptr; // 跳过空指针
@@ -76,7 +144,7 @@ std::shared_ptr<font_face_interface> font_manager::get_font_for_code_point(
// 此过滤器排除了空指针字体和已检查过的主字体。
// 注意: 更复杂的过滤器可以基于 is_emoji 和字体元数据 (例如 is_emoji_font())
// 来优先选择特定类型的字体(例如,为表情符号优先选择表情字体)。
auto font_filter = [&](const std::shared_ptr<font_face_interface>& font) {
auto font_filter = [&](const font_face_ptr& font) {
// **确保字体有效且不是我们已经检查过的主字体**
if (is_emoji) {
return std::ranges::contains(emoji_font_ids_, font);
@@ -112,7 +180,7 @@ std::shared_ptr<font_face_interface> font_manager::get_font_for_code_point(
text_layout_t font_manager::layout_text(
const std::u32string& text,
const std::shared_ptr<font_face_interface>& in_font,
const font_face_ptr& in_font,
float font_size,
float max_width,
float line_spacing) {
@@ -122,198 +190,89 @@ text_layout_t font_manager::layout_text(
}
void font_manager::append_layout_text(
text_layout_t& in_layout,
const std::u32string& append_text,
const std::shared_ptr<font_face_interface>& in_font,
float font_size,
float max_width,
float line_spacing) {
in_layout.glyphs.reserve(in_layout.glyphs.size() + append_text.size());
text_layout_t& in_layout,
const std::u32string& append_text,
const font_face_ptr& in_font,
float font_size,
float max_width,
float line_spacing) {
in_layout.glyphs.reserve(in_layout.glyphs.size() + append_text.size());
const auto& primary_font = in_font ? in_font : get_primary_font();
assert(primary_font && "No valid font available");
auto& current_line = in_layout.last_line;
float total_width = 0.0f;
float total_height = 0.0f;
bool at_line_start = true; // 跟踪是否在行的开始位置
auto& current_line = in_layout.last_line;
float& cursor_y = in_layout.last_cursor.y();
float& cursor_x = in_layout.last_cursor.x();
float& cursor_y = in_layout.last_cursor.y();
float& cursor_x = in_layout.last_cursor.x();
float total_width = 0.0f;
float total_height = 0.0f;
bool at_line_start = true;
std::map<std::shared_ptr<font_face_interface>, font_v_metrics_t> font_metrics_cache;
// 缓存字体度量
auto font_metrics_cache = cache_font_metrics(font_size, primary_font);
const float default_line_height = font_metrics_cache[primary_font].line_height * line_spacing;
current_line.max_line_height = default_line_height;
// 设置所有可能用到的字体大小
for (const auto& font : fonts_ | std::views::values) {
if (!font)
continue;
font->set_font_size(font_size);
// 确保获取度量前字体大小已设置 (已在循环开始前完成)
font_metrics_cache[font] = font->get_metrics();
}
if (primary_font) {
primary_font->set_font_size(font_size);
font_metrics_cache[primary_font] = primary_font->get_metrics();
}
const float current_font_max_height = font_metrics_cache[primary_font].line_height * line_spacing;
current_line.max_line_height = current_font_max_height; // 初始化当前行的最大行高
/**
* @brief 完成当前行的布局并将字形添加到最终布局中
*/
// 完成当前行
auto finish_line = [&](bool next_line = true) {
at_line_start = true; // 标记下一行的开始
at_line_start = true;
text_layout_impl::finalize_line_glyphs(in_layout, current_line, cursor_y);
// --- 计算行垂直居中的基线 ---
// **1. 计算该行分配的总高度**
const float actual_line_height = current_line.max_line_height;
// **2. 计算该行内容的实际高度**
const float content_height = current_line.max_ascent + current_line.max_descent;
// **3. 计算垂直居中对齐的基线 Y 坐标**
// 目标是将 content_height 放在 actual_line_height 的中间。
// 行空间的中心点 Y = cursor_y + actual_line_height / 2.0f
// 内容空间的中心点相对于基线的偏移 = (max_ascent - max_descent) / 2.0f
// 所以 baseline_y + (max_ascent - max_descent) / 2.0f = cursor_y + actual_line_height / 2.0f
// baseline_y = cursor_y + (actual_line_height - max_ascent + max_descent) / 2.0f
// (注意:这里假设 ascent 为正, descent 为正)
const float baseline_y = cursor_y + current_line.max_ascent + (actual_line_height - content_height) / 2.f;
// --- 处理当前行的所有字形,计算最终位置 ---
for (const auto& pending_glyph : current_line.glyphs) {
const auto& metrics = pending_glyph.metrics;
const auto& region = pending_glyph.region;
// 计算Y轴差异 (确保从位图数据的实际顶部开始绘制)
const float size_y_diff = metrics.rect.size().y() - static_cast<float>(region.rect.size().y());
// 计算最终字形绘制坐标 (使用新的 baseline_y)
float final_x = pending_glyph.start_x + metrics.offset.x();
// **final_y = baseline_y + 字形相对于基线的偏移 + 高度差异**
float final_y = baseline_y + metrics.offset.y() + size_y_diff;
// 添加字形位置信息到最终布局
auto& glyph_position = in_layout.glyphs.emplace_back();
glyph_position.is_emoji = pending_glyph.is_emoji;
glyph_position.glyph_index = pending_glyph.glyph_index;
glyph_position.position = { final_x, final_y };
glyph_position.size = metrics.rect.size();
glyph_position.region = region;
}
// --- 更新总体宽度 ---
total_width = std::max(total_width, current_line.current_width);
// 增加行计数(只要调用了 finish_line 就说明有一行)
total_height += actual_line_height;
total_height += current_line.max_line_height;
// 移到下一行的顶部
if (next_line) {
cursor_y = total_height;
cursor_x = 0.0f;
// 重置行信息和上一个字形ID
current_line = text_layout_t::line_info_t{};
current_line.max_line_height = current_font_max_height; // 设置新行的默认行高
}
if (next_line) {
cursor_y = total_height;
cursor_x = 0.0f;
current_line = text_layout_t::line_info_t{};
current_line.max_line_height = default_line_height;
}
};
/**
* @brief 处理单个字符的布局(第一阶段:收集信息和换行判断)
* (此函数逻辑基本不变,主要负责收集信息给 finish_line 使用)
*/
auto process_character = [&](const char32_t c) {
if (c == U'\n') {
// 如果当前行没有内容但我们在行的开始,仍然需要计算这个空行
if (at_line_start) {
total_height += current_line.max_line_height;
} else {
finish_line();
}
return;
}
at_line_start = false; // 不再是行的开始
const bool is_emoji = emoji_detector::is_emoji(c);
uint32_t glyph_index = 0;
const auto& using_font = get_font_for_code_point(primary_font, c, &glyph_index);
if (!using_font)
return;
const auto& current_font_metrics = font_metrics_cache[using_font];
const auto& glyph_metrics = using_font->shape_glyph(glyph_index);
const auto& region_opt = get_or_create_glyph_by_index(glyph_metrics.glyph_index, using_font, font_size,
is_emoji);
if (!region_opt) {
current_line.prev_glyph_id = 0;
return;
}
const auto& region = *region_opt;
float kerning = 0.0f;
if (current_line.prev_glyph_id != 0) {
kerning = using_font->get_kerning(current_line.prev_glyph_id, glyph_index);
}
float estimated_next_x = cursor_x + kerning + glyph_metrics.advance.x();
// 检查自动换行
if (max_width > 0 &&
estimated_next_x > max_width &&
current_line.has_content) {
finish_line();
// !! 继续处理当前字符 c 在新行上 !!
kerning = 0.0f; // 新行首字符无 kerning
// line_cursor_x 和 prev_glyph_id 已在 finish_line 中重置为 0
}
current_line.has_content = true;
// **更新当前行的最大度量信息** (保持不变)
current_line.max_ascent = std::max(current_line.max_ascent, current_font_metrics.ascent);
// **确保 descent 是正值**
current_line.max_descent = std::max(current_line.max_descent, std::abs(current_font_metrics.descent));
current_line.max_line_height = std::max(current_line.max_line_height, current_font_metrics.line_height * line_spacing);
cursor_x += kerning;
text_layout_t::pending_glyph_t pending_glyph;
pending_glyph.glyph_index = glyph_index;
pending_glyph.prev_glyph_id_for_kerning = current_line.prev_glyph_id;
pending_glyph.start_x = cursor_x;
pending_glyph.metrics = glyph_metrics;
pending_glyph.region = region;
pending_glyph.is_emoji = is_emoji;
pending_glyph.font = using_font;
current_line.glyphs.push_back(pending_glyph);
// 更新行宽度
current_line.current_width = cursor_x + glyph_metrics.advance.x();
// 更新光标 X 位置
cursor_x += glyph_metrics.advance.x();
// 更新上一个字形索引
current_line.prev_glyph_id = glyph_index;
};
// --- 主处理循环 ---
// 处理每个字符
for (const char32_t c : append_text) {
process_character(c);
if (c == U'\n') {
text_layout_impl::handle_newline(at_line_start, current_line, total_height, finish_line);
continue;
}
at_line_start = false;
// 获取字形信息
auto glyph_info = get_glyph_info(c, primary_font, font_size);
if (!glyph_info.font) continue;
// 计算间距和位置
float kerning = current_line.prev_glyph_id ?
glyph_info.font->get_kerning(current_line.prev_glyph_id, glyph_info.glyph_index) : 0.0f;
float next_x = cursor_x + kerning + glyph_info.metrics.advance.x();
// 检查换行
if (text_layout_impl::should_wrap_line(max_width, next_x, current_line.has_content)) {
finish_line();
kerning = 0.0f;
}
// 更新行信息
text_layout_impl::update_line_metrics(current_line, font_metrics_cache[glyph_info.font], line_spacing);
// 添加待处理字形
cursor_x += kerning;
text_layout_impl::add_pending_glyph(current_line, glyph_info, cursor_x);
cursor_x += glyph_info.metrics.advance.x();
current_line.current_width = cursor_x;
current_line.prev_glyph_id = glyph_info.glyph_index;
current_line.has_content = true;
}
// 处理最后一行
if (current_line.has_content) {
finish_line(false);
} else if (at_line_start && !append_text.empty()) {
// 如果在行的开始位置且有处理过字符(比如文本以\n结尾需要计入这个空行
total_height += current_line.max_line_height;
}
text_layout_impl::finalize_layout(current_line, at_line_start, append_text.empty(),
total_height, default_line_height, finish_line);
// --- 设置最终布局尺寸 ---
// 使用公式: total_height = max(1, line_count) * line_height
in_layout.total_size = { total_width, total_height };
}
@@ -322,7 +281,7 @@ void font_manager::append_layout_text(
*/
std::optional<atlas_region_t> font_manager::get_or_create_glyph_by_index(
int32_t in_glyph_id,
const std::shared_ptr<font_face_interface>& in_font,
const font_face_ptr& in_font,
float in_font_size,
bool is_emoji) {
// 验证字体有效性
@@ -334,8 +293,42 @@ std::optional<atlas_region_t> font_manager::get_or_create_glyph_by_index(
}
font_manager::font_manager() {
// 初始化字体系统
if (!init_font_system()) {
throw std::runtime_error("无法初始化字体系统");
}
// 初始化字体系统
if (!init_font_system()) {
throw std::runtime_error("无法初始化字体系统");
}
}
std::map<font_face_ptr, font_v_metrics_t> font_manager::cache_font_metrics(float font_size, const font_face_ptr& primary_font) {
std::map<font_face_ptr, font_v_metrics_t> cache;
for (const auto& font: fonts_ | std::views::values) {
if (!font)
continue;
font->set_font_size(font_size);
cache[font] = font->get_metrics();
}
if (primary_font) {
primary_font->set_font_size(font_size);
cache[primary_font] = primary_font->get_metrics();
}
return cache;
}
font_manager::glyph_info_t font_manager::get_glyph_info(char32_t c, const font_face_ptr& primary_font, float font_size) {
glyph_info_t info;
info.is_emoji = emoji_detector::is_emoji(c);
info.font = get_font_for_code_point(primary_font, c, &info.glyph_index);
if (!info.font)
return info;
info.metrics = info.font->shape_glyph(info.glyph_index);
if (auto region_opt = get_or_create_glyph_by_index(info.metrics.glyph_index, info.font, font_size, info.is_emoji)) {
info.region = *region_opt;
info.valid = true;
}
return info;
}

View File

@@ -5,9 +5,10 @@
#include <unordered_map>
#include <optional>
#include <filesystem>
#include <ranges>
#include "atlas/font_atlas.h"
#include "atlas/font_atlas_manager.h"
#include <filesystem>
#include "font_type.h"
#include "interface/font_interface.h"
@@ -21,6 +22,15 @@
*/
class font_manager {
public:
struct glyph_info_t {
font_face_ptr font;
uint32_t glyph_index = 0;
glyph_shaped_t metrics;
atlas_region_t region;
bool is_emoji = false;
bool valid = false;
};
/**
* @brief 获取单例实例
*
@@ -81,21 +91,21 @@ public:
* @param in_code_point Unicode码点
* @return 最适合渲染该码点的字体
*/
std::shared_ptr<font_face_interface> get_font_for_code_point(uint32_t in_code_point,
font_face_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<font_face_interface> get_font_for_code_point(
const std::shared_ptr<font_face_interface>& in_custom_primary_font, uint32_t in_code_point,
font_face_ptr get_font_for_code_point(
const font_face_ptr& in_custom_primary_font, uint32_t in_code_point,
uint32_t* out_glyph_index = nullptr);
/**
* @brief 获取主字体
* @return 主字体
*/
std::shared_ptr<font_face_interface> get_primary_font() {
font_face_ptr get_primary_font() {
if (primary_font_id_ != -1 && fonts_.contains(primary_font_id_)) {
return fonts_[primary_font_id_];
}
@@ -122,7 +132,7 @@ public:
*/
text_layout_t layout_text(
const std::u32string& text,
const std::shared_ptr<font_face_interface>& in_font,
const font_face_ptr& in_font,
float font_size,
float max_width = 0.0f,
float line_spacing = 1.2f);
@@ -130,7 +140,7 @@ public:
void append_layout_text(
text_layout_t& in_layout,
const std::u32string& append_text,
const std::shared_ptr<font_face_interface>& in_font,
const font_face_ptr& in_font,
float font_size,
float max_width = 0.0f,
float line_spacing = 1.2f
@@ -149,7 +159,7 @@ public:
*/
std::optional<atlas_region_t> get_or_create_glyph_by_index(
int32_t in_glyph_id,
const std::shared_ptr<font_face_interface>& in_font,
const font_face_ptr& in_font,
float in_font_size,
bool is_emoji);
@@ -172,10 +182,13 @@ public:
private:
font_manager();
std::unordered_map<int32_t, std::shared_ptr<font_face_interface>> fonts_; ///< 字体ID到字体对象的映射
std::map<font_face_ptr, font_v_metrics_t> cache_font_metrics(float font_size, const font_face_ptr& primary_font);
glyph_info_t get_glyph_info(char32_t c, const font_face_ptr& primary_font, float font_size);
std::unordered_map<int32_t, font_face_ptr> fonts_; ///< 字体ID到字体对象的映射
font_atlas_manager atlas_manager_; ///< 图集管理器
std::vector<std::shared_ptr<font_face_interface>> emoji_font_ids_; ///< 表情符号字体ID列表
std::vector<font_face_ptr> emoji_font_ids_; ///< 表情符号字体ID列表
int32_t primary_font_id_ = -1; ///< 主字体ID
int32_t next_font_id_ = 0; ///< 下一个可用的字体ID
};

View File

@@ -126,7 +126,7 @@ struct text_layout_t {
glyph_shaped_t metrics; // 字形度量信息
atlas_region_t region; // 字形在图集中的区域
bool is_emoji; // 是否为表情符号
std::shared_ptr<font_face_interface> font; // 使用的字体
font_face_ptr font; // 使用的字体
};
struct line_info_t {

View File

@@ -62,6 +62,8 @@ private:
float font_size_ = 16.0f; // 字体大小
};
using font_face_ptr = std::shared_ptr<font_face_interface>;
bool init_font_system();
void destroy_font_system();
std::shared_ptr<font_face_interface> create_font_face(const std::filesystem::path& in_path);
font_face_ptr create_font_face(const std::filesystem::path& in_path);

View File

@@ -6,7 +6,7 @@ class font_face_interface;
struct editable_text_box_args {
WARG(std::u32string, text, {})
WARG(std::shared_ptr<font_face_interface>, font, {})
WARG(font_face_ptr, font, {})
WARG(float, font_size, 24.f)
WARG(float, line_spacing, 1.2f)
WARG(bool, warp_text, false)
@@ -45,5 +45,5 @@ private:
text_layout_t layout_{};
std::u32string text_; // 最终显示的文本
std::u32string temp_text_; // 当前输入的文本
std::shared_ptr<font_face_interface> font_;
font_face_ptr font_;
};

View File

@@ -6,7 +6,7 @@ class font_face_interface;
struct text_block_args {
WARG(std::u32string, text, {})
WARG(std::shared_ptr<font_face_interface>, font, {})
WARG(font_face_ptr, font, {})
WARG(float, font_size, 24.f)
WARG(float, line_spacing, 1.2f)
WARG(bool, warp_text, false)
@@ -22,7 +22,7 @@ public:
update_no_wrap_size();
}
void set_font(const std::shared_ptr<font_face_interface>& in_font) {
void set_font(const font_face_ptr& in_font) {
font_ = in_font;
update_no_wrap_size();
}
@@ -53,5 +53,5 @@ private:
float font_size_ = .0f;
float line_spacing_ = 1.f;
bool warp_text_ = false;
std::shared_ptr<font_face_interface> font_;
font_face_ptr font_;
};