移除旧版本字符串布局函数

This commit is contained in:
2025-07-01 11:44:10 +08:00
parent 5d30162f78
commit b6979749f8
8 changed files with 82 additions and 262 deletions

View File

@@ -70,7 +70,7 @@ std::shared_ptr<atlas_region_t> find_or_create_in_atlases(
}
std::shared_ptr<atlas_region_t> font_atlas_manager::get_or_create_glyph(
const int32_t in_glyph_id,
const uint32_t in_glyph_id,
const font_face_ptr& in_font,
const float in_font_size,
const bool is_emoji) {

View File

@@ -29,7 +29,7 @@ public:
* @return 字形在图集中的区域失败返回nullopt
*/
std::shared_ptr<atlas_region_t> get_or_create_glyph(
int32_t in_glyph_id,
uint32_t in_glyph_id,
const font_face_ptr& in_font,
float in_font_size,
bool is_emoji);

View File

@@ -6,252 +6,72 @@
#include "misc/log_util.h"
namespace text_layout_impl {
// 临时存储待处理字形的信息
struct pending_glyph_t {
uint32_t glyph_index; // 字形索引
uint32_t prev_glyph_id_for_kerning; // 用于字距调整的上一个字形ID
float start_x; // 该字形开始绘制的相对X坐标应用字距调整前
glyph_shaped_t metrics; // 字形度量信息
std::shared_ptr<atlas_region_t> region; // 字形在图集中的区域
bool is_emoji; // 是否为表情符号
font_face_ptr font; // 使用的字体
};
struct line_info_t {
float max_ascent = 0.0f;
float max_descent = 0.0f; // 正值
float max_line_height = 0.0f;
float current_width = 0.0f;
bool has_content = false;
uint32_t prev_glyph_id = 0;
uint32_t line_index = 0; // 行索引
std::vector<pending_glyph_t> glyphs;
};
void finalize_line_glyphs(text_layout_t& layout, const 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());
text_layout_t::glyph_position_t p;
p.is_emoji = pending.is_emoji;
p.position = { pending.start_x + pending.metrics.offset.x(),
baseline_y + pending.metrics.offset.y() + size_y_diff };
p.region = pending.region;
layout.add_glyph(p);
}
}
void update_line_metrics(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(line_info_t& line, const glyph_info_t& info, float cursor_x) {
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(const bool& at_line_start,
const line_info_t& line,
float& cursor_y,
float& total_height,
const std::function<void(bool)>& finish_line) {
if (at_line_start) {
total_height += line.max_line_height;
cursor_y += line.max_line_height;
} else {
finish_line(true);
}
}
void finalize_layout(const 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;
}
}
glyph_info_t get_glyph_info(char32_t c, const font_face_ptr& primary_font) {
auto& font_mgr = font_manager::instance();
auto font_size = primary_font->get_font_size();
glyph_info_t info;
info.is_emoji = emoji_detector::is_emoji(c);
info.font = font_mgr.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 = font_mgr.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;
}
} // namespace text_layout_impl
void font_manager::append_layout_text(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) {
auto& last_line = in_layout.get_last_line();
last_line.glyphs.reserve(last_line.glyphs.size() + append_text.size());
const auto& primary_font = in_font ? in_font : get_primary_font();
if (!primary_font)
throw std::runtime_error("No valid primary font available");
text_layout_impl::line_info_t current_line{};
float cursor_x = 0, cursor_y = 0;
float total_width = 0.0f;
float total_height = 0.0f;
bool at_line_start = true;
// 缓存字体度量
auto font_metrics_cache = cache_font_metrics(font_size);
const float default_line_height = font_metrics_cache[primary_font].line_height * line_spacing;
current_line.max_line_height = default_line_height;
// 完成当前行
auto finish_line = [&](bool next_line = true) {
at_line_start = true;
text_layout_impl::finalize_line_glyphs(in_layout, current_line, cursor_y);
total_width = std::max(total_width, current_line.current_width);
total_height += current_line.max_line_height;
if (next_line) {
cursor_y = total_height;
cursor_x = 0.0f;
const auto last_index = current_line.line_index;
current_line = text_layout_impl::line_info_t{};
current_line.max_line_height = default_line_height;
current_line.line_index = last_index + 1; // 增加行索引
}
};
// 处理每个字符
for (const char32_t c: append_text) {
if (c == U'\n') {
text_layout_impl::handle_newline(at_line_start, current_line, cursor_y, total_height, finish_line);
continue;
}
at_line_start = false;
// 获取字形信息
auto glyph_info = text_layout_impl::get_glyph_info(c, primary_font);
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;
}
// 处理最后一行
text_layout_impl::finalize_layout(
current_line,
at_line_start,
append_text.empty(),
total_height,
default_line_height,
finish_line);
in_layout.total_size = { total_width, total_height };
}
void text_layout_t::line_t::format(const font_face_ptr& in_primary_font, float in_font_size) {
auto& font_mgr = font_manager::instance();
in_primary_font->set_font_size(in_font_size);
line_height = in_primary_font ? in_primary_font->get_metrics().line_height : font_mgr.get_primary_font()->get_metrics().line_height;
glyph_position_t* prev_g = nullptr;
uint32_t prev_glyph_index = 0;
float cursor_x = 0.0f;
for (auto& g : glyphs) {
uint32_t glyph_index;
font_face_ptr using_font = font_mgr.get_font_for_code_point(in_primary_font, g.codepoint, &glyph_index);
if (!using_font) {
log_warn("Glyph index {} not found in any font for codepoint U+{:04X}.", glyph_index, g.codepoint);
auto g = glyphs.begin();
while (g != glyphs.end()) {
uint32_t glyph_index = 0;
font_face_ptr using_font;
// 尝试找到一个可渲染的字形
do {
if (g == glyphs.end()) {
// 如果没有更多字形,跳出循环
using_font = nullptr;
break;
}
// 找一个能够渲染当前字形的字体
using_font = font_mgr.get_font_for_code_point(in_primary_font, g->codepoint, &glyph_index);
if (!using_font) {
log_warn("无法找到用于渲染 U+{:04X} 的字体", (uint32_t)g->codepoint);
// 移除当前字形并继续处理下一个
g = glyphs.erase(g);
}
}
while (!using_font);
// 没有找到任何可渲染字形, 跳出循环
if (!using_font)
break;
const auto is_emoji = emoji_detector::is_emoji(glyph_index);
const auto& metrics = using_font->get_metrics();
line_height = std::max(line_height, metrics.line_height);
g.is_emoji = is_emoji;
g.region = font_mgr.get_or_create_glyph_by_index(glyph_index, using_font, in_font_size, is_emoji);
g->is_emoji = is_emoji;
g->region = font_mgr.get_or_create_glyph_by_index(glyph_index, using_font, in_font_size, is_emoji);
auto g_metrics = using_font->shape_glyph(glyph_index);
float kerning = 0.0f;
if (prev_g) {
if (prev_glyph_index != 0) {
kerning = using_font->get_kerning(prev_glyph_index, glyph_index);
}
g.position.x() = cursor_x + kerning + g_metrics.offset.x();
g->position.x() = cursor_x + kerning + g_metrics.offset.x();
const float size_y_diff = g_metrics.rect.size().y() - static_cast<float>(g.region->rect.size().y());
const float baseline_y = position_y + metrics.ascent + (line_height - metrics.line_height) / 2.0f;
g.position.y() = baseline_y + g_metrics.offset.y() + size_y_diff - metrics.descent;
const float size_y_diff = g_metrics.rect.size().y() - static_cast<float>(g->region->rect.size().y());
const float baseline_y = position_y + metrics.ascent;
// g->position.y() = baseline_y + g_metrics.offset.y() + size_y_diff;
g->position.y() = baseline_y + g_metrics.offset.y() + size_y_diff - metrics.descent;
cursor_x += kerning + g_metrics.advance.x();
prev_g = &g;
prev_glyph_index = glyph_index;
++g;
}
line_width = cursor_x;
@@ -289,6 +109,7 @@ void text_layout_t::push_str(const std::u32string& str) {
end_line(false); // 确保最后一行也被格式化
total_size.y() += cursor_y;
total_size.y() += get_last_line().get_line_height();
for (const auto& line: lines) {
total_size.x() = std::max(total_size.x(), line.line_width);
}

View File

@@ -21,9 +21,9 @@ struct text_layout_t {
}
struct glyph_position_t {
bool is_emoji; // 是否为表情符号
uint32_t codepoint;
Eigen::Vector2f position; // 屏幕位置
std::shared_ptr<atlas_region_t> region; // 纹理图集区域
char32_t codepoint;
Eigen::Vector2f position; // 屏幕位置
std::shared_ptr<atlas_region_t> region; // 纹理图集区域
};
struct line_t {
@@ -118,6 +118,20 @@ struct text_layout_t {
void set_font_size(float size);
void set_primary_font(const font_face_ptr& font);
void set_line_spacing(float spacing) {
line_spacing = spacing;
}
void set_max_width(float width) {
max_width = width;
}
[[nodiscard]] float get_line_spacing() const {
return line_spacing;
}
[[nodiscard]] float get_font_size() const {
return font_size;
}
float get_empty_line_height() const;
auto begin() { return lines.begin(); }

View File

@@ -110,22 +110,11 @@ font_face_ptr font_manager::get_font_for_code_point(
return nullptr;
}
text_layout_t font_manager::layout_text(
const std::u32string& text,
const font_face_ptr& in_font,
float font_size,
float max_width,
float line_spacing) {
text_layout_t layout{};
append_layout_text(layout, text, in_font, font_size, max_width, line_spacing);
return layout;
}
/**
* @brief 获取或创建字形在图集中的区域
*/
std::shared_ptr<atlas_region_t> font_manager::get_or_create_glyph_by_index(
int32_t in_glyph_id,
uint32_t in_glyph_id,
const font_face_ptr& in_font,
float in_font_size,
bool is_emoji) {

View File

@@ -113,32 +113,6 @@ 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 包含所有字形位置和布局信息的结构
*/
text_layout_t layout_text(
const std::u32string& text,
const font_face_ptr& in_font,
float font_size,
float max_width = 0.0f,
float line_spacing = 1.2f);
void append_layout_text(
text_layout_t& in_layout,
const std::u32string& append_text,
const font_face_ptr& in_font,
float font_size,
float max_width = 0.0f,
float line_spacing = 1.2f
);
/**
* @brief 获取或创建字形
*
@@ -151,7 +125,7 @@ public:
* @return 字形在图集中的区域
*/
std::shared_ptr<atlas_region_t> get_or_create_glyph_by_index(
int32_t in_glyph_id,
uint32_t in_glyph_id,
const font_face_ptr& in_font,
float in_font_size,
bool is_emoji);

View File

@@ -1,5 +1,6 @@
#include "meditable_text_box.h"
#include "font/font_system.h"
#include "font/font_utils.h"
#include "window/mwindow.h"
void meditable_text_box::init() {
@@ -17,7 +18,10 @@ void meditable_text_box::setup_widget(const editable_text_box_args& in_args) {
line_spacing_ = in_args.line_spacing();
warp_text_ = in_args.warp_text();
text_changed_ = true; // 初始化时需要更新布局
layout_.set_primary_font(font_);
layout_.set_font_size(font_size_);
layout_.set_line_spacing(line_spacing_);
update_layout(geometry_);
}
@@ -287,7 +291,14 @@ void meditable_text_box::update_layout(const geometry_t& in_geometry, bool in_la
layout_.clear();
layout_.push_str(temp_text_);
no_warp_size_ = font_manager::instance().layout_text(temp_text_, font_, font_size_, 0, line_spacing_).total_size;
text_layout_t temp_layout{};
temp_layout.set_font_size(font_size_);
temp_layout.set_primary_font(font_);
temp_layout.set_line_spacing(line_spacing_);
temp_layout.set_max_width(max_width);
temp_layout.push_str(temp_text_);
no_warp_size_ = temp_layout.total_size;
if (in_layout_validate) {
invalidate(invalidate_reason::layout);

View File

@@ -42,11 +42,22 @@ void mtext_block::arrange_children(const geometry_t& in_allotted_geometry) {
invalidate(invalidate_reason::layout);
}
layout_ = font_manager::instance().layout_text(text_, font_, font_size_, max_width, line_spacing_);
layout_.set_font_size(font_size_);
layout_.set_primary_font(font_);
layout_.set_line_spacing(line_spacing_);
layout_.set_max_width(max_width);
layout_.clear();
layout_.push_str(text_);
}
void mtext_block::update_no_wrap_size() {
no_warp_size_ = font_manager::instance().layout_text(text_, font_, font_size_, 0, line_spacing_).total_size;
text_layout_t temp_layout{};
temp_layout.set_font_size(font_size_);
temp_layout.set_primary_font(font_);
temp_layout.set_line_spacing(line_spacing_);
temp_layout.push_str(text_);
no_warp_size_ = temp_layout.total_size;
// 如果没有任何内容, 将大小设置为0
if (no_warp_size_.x() == 0)
no_warp_size_.y() = 0;