移除旧版本字符串布局函数
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user