TODO 文本布局部分重构完成

This commit is contained in:
2025-06-30 17:28:24 +08:00
parent 060a18cd68
commit 95a5d7f60e
12 changed files with 347 additions and 128 deletions

View File

@@ -1,21 +1,35 @@
#pragma once
#include <concepts>
#include <iterator>
#include <list>
#include <optional>
#include <shared_mutex>
#include <stdexcept>
#include <unordered_map>
#include <utility>
template<typename Key, typename Value>
requires std::equality_comparable<Key> && std::movable<Value>
namespace lru_concept {
template<typename Key>
concept Hashable = requires(Key key) {
{ std::hash<Key>{}(key) } -> std::convertible_to<std::size_t>;
};
template<typename Value>
concept Movable = std::movable<Value> && !std::is_const_v<Value> && !std::is_reference_v<Value>;
}
template<lru_concept::Hashable Key, lru_concept::Movable Value>
requires std::movable<Value>
class lru_cache {
using key_value_pair_t = std::pair<Key, Value>;
using list_iterator = typename std::list<key_value_pair_t>::iterator;
using list_iterator = typename std::list<key_value_pair_t>::iterator;
std::size_t capacity_;
mutable std::list<key_value_pair_t> cache_list_;
std::size_t capacity_;
mutable std::list<key_value_pair_t> cache_list_;
std::unordered_map<Key, list_iterator> cache_map_;
mutable std::shared_mutex mutex_;
mutable std::shared_mutex mutex_;
public:
explicit lru_cache(std::size_t capacity) : capacity_(capacity) {
if (capacity_ == 0) {
@@ -23,25 +37,66 @@ public:
}
}
// 复制构造和赋值
lru_cache(const lru_cache& other) {
std::shared_lock lock(other.mutex_);
capacity_ = other.capacity_;
cache_list_ = other.cache_list_;
// 重建 map因为迭代器在复制后会失效
for (auto it = cache_list_.begin(); it != cache_list_.end(); ++it) {
cache_map_[it->first] = it;
}
}
lru_cache& operator=(const lru_cache& other) {
if (this != &other) {
std::unique_lock lock1(mutex_, std::defer_lock);
std::shared_lock lock2(other.mutex_, std::defer_lock);
std::lock(lock1, lock2);
capacity_ = other.capacity_;
cache_list_ = other.cache_list_;
cache_map_.clear();
for (auto it = cache_list_.begin(); it != cache_list_.end(); ++it) {
cache_map_[it->first] = it;
}
}
return *this;
}
// 移动构造和赋值
lru_cache(lru_cache&& other) noexcept : capacity_(0) {
std::unique_lock lock(other.mutex_);
capacity_ = std::exchange(other.capacity_, 0);
cache_list_ = std::move(other.cache_list_);
cache_map_ = std::move(other.cache_map_);
}
lru_cache& operator=(lru_cache&& other) noexcept {
if (this != &other) {
std::unique_lock lock1(mutex_, std::defer_lock);
std::unique_lock lock2(other.mutex_, std::defer_lock);
std::lock(lock1, lock2);
capacity_ = std::exchange(other.capacity_, 0);
cache_list_ = std::move(other.cache_list_);
cache_map_ = std::move(other.cache_map_);
}
return *this;
}
[[nodiscard]] std::optional<Value> get(const Key& key) const {
std::shared_lock lock(mutex_);
// 使用单次加锁,避免 TOCTOU 问题
std::unique_lock lock(mutex_);
auto it = cache_map_.find(key);
if (it == cache_map_.end()) {
return std::nullopt; // Key not found
return std::nullopt;
}
// 将访问的节点移到链表头部(最近使用)
// 注意: 这里需要修改列表, 所以实际上需要写锁
lock.unlock();
std::unique_lock write_lock(mutex_);
it = cache_map_.find(key);
if (it == cache_map_.end()) {
return std::nullopt; // Key not found
}
// 将节点移到链表头部
// 将访问的节点移到链表头部
cache_list_.splice(cache_list_.begin(), cache_list_, it->second);
return it->second->second;
}
@@ -60,15 +115,14 @@ public:
// 键不存在,插入新的键值对
if (cache_list_.size() >= capacity_) {
// 缓存已满,删除最近最少使用的项目
auto last = cache_list_.end();
--last; // 获取最后一个元素
// 缓存已满,删除最近最少使用的项目
auto last = std::prev(cache_list_.end());
cache_map_.erase(last->first);
cache_list_.pop_back();
}
cache_list_.emplace_front(std::pair(key, std::move(value)));
cache_map_[key] = cache_list_.begin();
cache_list_.emplace_front(key, std::move(value));
cache_map_.emplace(key, cache_list_.begin());
}
// 删除指定key
@@ -76,8 +130,8 @@ public:
std::unique_lock lock(mutex_);
auto it = cache_map_.find(key);
if (it == cache_map_.end()) {
return false; // Key not found
if (it == cache_map_.end()) { // 修复了语法错误
return false;
}
cache_list_.erase(it->second);
@@ -99,9 +153,7 @@ public:
}
// 获取缓存容量
[[nodiscard]] std::size_t capacity() const noexcept {
return capacity_;
}
[[nodiscard]] std::size_t capacity() const noexcept { return capacity_; }
// 检查缓存是否为空
[[nodiscard]] bool empty() const {
@@ -112,35 +164,101 @@ public:
// 检查是否包含指定key
[[nodiscard]] bool contains(const Key& key) const {
std::shared_lock lock(mutex_);
return cache_map_.contains(key);
return cache_map_.contains(key); // C++20 feature
}
// 范围for循环支持
auto begin() const {
std::shared_lock lock(mutex_);
return cache_list_.begin();
}
// 提供线程安全的迭代器包装
class const_iterator {
const lru_cache* cache_;
list_iterator iter_;
std::shared_lock<std::shared_mutex> lock_;
auto end() const {
std::shared_lock lock(mutex_);
return cache_list_.end();
}
public:
using iterator_category = std::forward_iterator_tag;
using value_type = key_value_pair_t;
using difference_type = std::ptrdiff_t;
using pointer = const key_value_pair_t*;
using reference = const key_value_pair_t&;
const_iterator(const lru_cache* cache, list_iterator iter) : cache_(cache), iter_(iter), lock_(cache->mutex_) {}
reference operator*() const { return *iter_; }
pointer operator->() const { return &(*iter_); }
const_iterator& operator++() {
++iter_;
return *this;
}
const_iterator operator++(int) {
const_iterator tmp = *this;
++(*this);
return tmp;
}
bool operator==(const const_iterator& other) const { return iter_ == other.iter_; }
bool operator!=(const const_iterator& other) const { return !(*this == other); }
};
// 线程安全的迭代器
const_iterator begin() const { return const_iterator(this, cache_list_.begin()); }
const_iterator end() const { return const_iterator(this, cache_list_.end()); }
// 调整缓存容量
void resize(std::size_t new_capacity) {
if (new_capacity == 0) {
if (new_capacity == 0) { // 修复了语法错误
throw std::invalid_argument("Cache capacity must be greater than 0");
}
std::unique_lock lock(mutex_);
while (cache_list_.size() > new_capacity) {
auto last = cache_list_.end();
--last;
auto last = std::prev(cache_list_.end());
cache_map_.erase(last->first);
cache_list_.pop_back();
}
capacity_ = new_capacity;
}
// 批量操作支持
template<typename InputIt>
void put_range(InputIt first, InputIt last) {
std::unique_lock lock(mutex_);
for (auto it = first; it != last; ++it) {
// 内部实现,避免重复加锁
auto map_it = cache_map_.find(it->first);
if (map_it != cache_map_.end()) {
map_it->second->second = it->second;
cache_list_.splice(cache_list_.begin(), cache_list_, map_it->second);
}
else {
if (cache_list_.size() >= capacity_) {
auto last_elem = std::prev(cache_list_.end());
cache_map_.erase(last_elem->first);
cache_list_.pop_back();
}
cache_list_.emplace_front(it->first, it->second);
cache_map_.emplace(it->first, cache_list_.begin());
}
}
}
// 获取缓存统计信息
struct cache_stats {
std::size_t size;
std::size_t capacity;
double fill_ratio;
};
[[nodiscard]] cache_stats get_stats() const {
std::shared_lock lock(mutex_);
std::size_t current_size = cache_list_.size();
return { .size = current_size,
.capacity = capacity_,
.fill_ratio = static_cast<double>(current_size) / capacity_ };
}
};

View File

@@ -126,6 +126,14 @@ std::shared_ptr<image_heap_t> stb_font_face_t::get_emoji_image(int32_t in_glyph_
return nullptr;
}
bool stb_font_face_t::has_glyph_index(uint32_t in_glyph_index) const {
if (in_glyph_index == 0) {
return false; // 0通常表示无效的字形索引
}
// 检查字形索引是否存在
return stbtt_IsGlyphEmpty(&font_info_, in_glyph_index) == 0;
}
uint32_t stb_font_face_t::find_glyph_index(uint32_t in_unicode_codepoint) const {
return stbtt_FindGlyphIndex(&font_info_, in_unicode_codepoint);
}

View File

@@ -11,6 +11,7 @@ public:
[[nodiscard]] font_v_metrics_t get_metrics_impl() const override;
[[nodiscard]] std::shared_ptr<image_heap_t> get_glyph_image(int32_t in_glyph_id) const override;
[[nodiscard]] std::shared_ptr<image_heap_t> get_emoji_image(int32_t in_glyph_id) const override;
virtual bool has_glyph_index(uint32_t in_glyph_index) const override;
[[nodiscard]] uint32_t find_glyph_index(uint32_t in_unicode_codepoint) const override;
[[nodiscard]] float get_kerning(uint32_t in_first_glyph_id, uint32_t in_second_glyph_id) const override;
[[nodiscard]] glyph_shaped_t make_shape_glyph(uint32_t in_glyph_id) const override;

View File

@@ -3,6 +3,7 @@
#include "emoji_detector.h"
#include "font_system.h"
#include "interface/font_interface.h"
#include "misc/log_util.h"
namespace text_layout_impl {
// 临时存储待处理字形的信息
@@ -212,8 +213,49 @@ void font_manager::append_layout_text(text_layout_t& in_layout,
in_layout.total_size = { total_width, total_height };
}
void text_layout_t::line_t::format() {
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);
}
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);
auto g_metrics = using_font->shape_glyph(glyph_index);
float kerning = 0.0f;
if (prev_g) {
kerning = using_font->get_kerning(prev_glyph_index, glyph_index);
}
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;
cursor_x += kerning + g_metrics.advance.x();
prev_g = &g;
prev_glyph_index = glyph_index;
}
line_width = cursor_x;
}
void text_layout_t::push_str(const std::u32string& str) {
@@ -223,54 +265,60 @@ void text_layout_t::push_str(const std::u32string& str) {
}
auto& cursor_y = last_cursor.y();
auto& cursor_x = last_cursor.x();
auto* current_line = &get_last_line();
const auto& metrics = primary_font->get_metrics();
const auto default_line_height = metrics.line_height;
auto end_line = [&](bool new_line) {
current_line->position_y = cursor_y;
current_line->format(primary_font, font_size);
if (new_line) {
cursor_y += current_line->get_line_height() * line_spacing;
current_line = &next_line();
}
};
for (const auto c : str) {
if (c == U'\n') {
cursor_x = 0.0f;
cursor_y += default_line_height * line_spacing;
total_size.y() += default_line_height * line_spacing;
end_line(true);
continue;
}
auto* line = &get_last_line();
bool at_line_start = line->empty();
auto glyph_info = text_layout_impl::get_glyph_info(c, primary_font);
// 如果没有找到有效的字体或字形,跳过
if (!glyph_info.font)
continue;
glyph_position_t p{};
p.codepoint = c;
current_line->add_glyph(p);
}
// 计算间距和位置
auto kerning = line->glyphs.empty()
? 0.0f
: primary_font->get_kerning(line->glyphs.back().glyph_index, glyph_info.glyph_index);
const auto next_x = cursor_x + kerning + glyph_info.metrics.advance.x();
end_line(false); // 确保最后一行也被格式化
// 检查换行
if (text_layout_impl::should_wrap_line(max_width, next_x, !line->empty())) {
line = &next_line();
kerning = 0.f;
}
line->max_ascent = std::max(line->max_ascent, metrics.ascent);
line->max_descent = std::max(line->max_descent, metrics.descent);
cursor_x += kerning;
glyph_position_t glyph_position;
glyph_position.is_emoji = glyph_info.is_emoji;
glyph_position.glyph_index = glyph_info.glyph_index;
glyph_position.position = { cursor_x + glyph_info.metrics.offset.x(),
cursor_y + glyph_info.metrics.offset.y() };
glyph_position.region = glyph_info.region;
line->add_glyph(glyph_position);
cursor_x += glyph_info.metrics.advance.x();
line->position_y = cursor_y;
line->line_width = cursor_x;
total_size.y() += cursor_y;
for (const auto& line: lines) {
total_size.x() = std::max(total_size.x(), line.line_width);
}
}
void text_layout_t::set_font_size(float size) {
if (font_size == size) {
return; // 如果字体大小没有变化,直接返回
}
font_size = size;
if (override_primary_font) {
override_primary_font->set_font_size(size);
}
total_size.setZero(); // 重置总大小
last_cursor.setZero(); // 重置光标位置
clear(); // 清除现有内容
}
void text_layout_t::set_primary_font(const font_face_ptr& font) {
override_primary_font = font;
clear(); // 清除现有内容
}
float text_layout_t::get_empty_line_height() const {
const auto font = override_primary_font ? override_primary_font : font_manager::instance().get_primary_font();
if (!font) {
return 0.0f; // 如果没有主字体返回0
}
font->set_font_size(font_size);
return font->get_metrics().line_height * line_spacing;
}

View File

@@ -21,30 +21,48 @@ struct text_layout_t {
}
struct glyph_position_t {
bool is_emoji; // 是否为表情符号
uint32_t glyph_index; // 字形索引
uint32_t codepoint;
Eigen::Vector2f position; // 屏幕位置
std::shared_ptr<atlas_region_t> region; // 纹理图集区域
};
struct line_t {
float max_ascent = 0.0f; // 最大上升高度
float max_descent = 0.0f;
float line_height = 0.0f; // 行高
float position_y = 0.0f; // 行的Y位置
float line_width = 0.0f; // 行的宽度
std::vector<glyph_position_t> glyphs;
auto get_line_size() const {
return Eigen::Vector2f(line_width, max_ascent + max_descent);
[[nodiscard]] auto get_line_height() const {
return line_height;
}
[[nodiscard]] auto get_line_size() const {
return Eigen::Vector2f(line_width, get_line_height());
}
void add_glyph(const glyph_position_t& glyph) {
glyphs.emplace_back(glyph);
}
bool empty() const {
[[nodiscard]] bool empty() const {
return glyphs.empty();
}
void format();
void format(const font_face_ptr& in_primary_font, float in_font_size);
};
line_t* get_line(size_t index) {
if (index < lines.size()) {
return &lines[index];
}
return nullptr;
}
[[nodiscard]] const line_t* get_line(size_t index) const {
if (index < lines.size()) {
return &lines[index];
}
return nullptr;
}
[[nodiscard]] size_t size() const {
return lines.size();
}
auto& get_last_line() {
return lines.back();
}
@@ -58,16 +76,35 @@ struct text_layout_t {
}
void push_str(const std::u32string& str);
void clear() {
lines.clear();
lines.emplace_back(); // 重新初始化第一行
total_size.setZero();
last_cursor.setZero();
}
void add_glyph(const glyph_position_t& glyph) {
auto& last_line = get_last_line();
last_line.add_glyph(glyph);
}
std::vector<line_t> lines; // 文本行列表
float max_width;
float line_spacing = 1.2f; // 行间距
Eigen::Vector2f total_size{ 0, 0 }; // 文本总尺寸
Eigen::Vector2f last_cursor{ 0, 0 }; // 最后光标位置
font_face_ptr override_primary_font; // 主字体
void set_font_size(float size);
void set_primary_font(const font_face_ptr& font);
float get_empty_line_height() const;
auto begin() { return lines.begin(); }
auto begin() const { return lines.begin(); }
auto end() { return lines.end(); }
auto end() const { return lines.end(); }
Eigen::Vector2f total_size{ 0, 0 }; // 文本总尺寸
Eigen::Vector2f last_cursor{ 0, 0 }; // 最后光标位置
private:
float max_width = 0.f;
float line_spacing = 1.2f; // 行间距
float font_size = 24.f; // 字体大小
float empty_line_height = 0.f; // 空行高度
std::vector<line_t> lines; // 文本行列表
font_face_ptr override_primary_font; // 主字体
};

View File

@@ -1,17 +1,5 @@
#include "font_interface.h"
template<>
struct std::hash<font_face_interface::glyph_key> {
size_t operator()(const font_face_interface::glyph_key& key) const noexcept {
// 根据 glyph_key 的成员变量计算 hash
// 示例(需要根据实际成员调整):
size_t h1 = std::hash<uint32_t>{}(key.glyph_id);
size_t h2 = std::hash<float>{}(key.font_size);
// 组合多个 hash 值
return h1 ^ (h2 << 1);
}
};
bool font_face_interface::load(const std::filesystem::path& in_path) {
font_data_ = mapped_file::create();
if (!font_data_->map_file(in_path)) {

View File

@@ -31,16 +31,30 @@ struct glyph_shaped_t {
rect_t<> rect; // 字形矩形区域
};
struct glyph_key {
uint32_t glyph_id;
float font_size;
bool operator==(const glyph_key& other) const { return glyph_id == other.glyph_id && font_size == other.font_size; }
};
// 为 glyph_key 提供 std::hash 特化
template<>
struct std::hash<glyph_key> {
std::size_t operator()(const glyph_key& key) const noexcept {
// 组合哈希值的常用方法
const std::size_t h1 = std::hash<uint32_t>{}(key.glyph_id);
const std::size_t h2 = std::hash<float>{}(key.font_size);
// 使用魔法数字组合哈希值,避免简单的异或
return h1 ^ (h2 << 1); // 或者使用 boost::hash_combine 的方法
// return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
}
};
class font_face_interface {
public:
struct glyph_key {
uint32_t glyph_id;
float font_size;
bool operator==(const glyph_key& other) const {
return glyph_id == other.glyph_id && font_size == other.font_size;
}
};
virtual ~font_face_interface() = default;
bool load(const std::filesystem::path& in_path);
@@ -66,10 +80,11 @@ public:
[[nodiscard]] virtual std::shared_ptr<image_heap_t> get_glyph_image(int32_t in_glyph_id) const = 0;
[[nodiscard]] virtual std::shared_ptr<image_heap_t> get_emoji_image(int32_t in_glyph_id) const = 0;
[[nodiscard]] virtual bool has_glyph_index(uint32_t in_glyph_index) const = 0;
[[nodiscard]] virtual uint32_t find_glyph_index(uint32_t in_unicode_codepoint) const = 0;
[[nodiscard]] virtual bool has_glyph(uint32_t in_unicode_codepoint) const { return find_glyph_index(in_unicode_codepoint) > 0; }
[[nodiscard]] virtual float get_kerning(uint32_t in_first_glyph_id, uint32_t in_second_glyph_id) const = 0;
[[nodiscard]] virtual float get_kerning(uint32_t in_first_glyph_id, uint32_t in_second_glyph_id) const = 0;
[[nodiscard]] glyph_shaped_t shape_glyph(uint32_t in_glyph_id) const {
if (in_glyph_id == 0) {
return {};

View File

@@ -279,14 +279,14 @@ void render_elements::make_text(const text_layout_t& in_layout, const Eigen::Vec
const auto& glyph_sampler = texture_sampler_builder::get_sampler(sampler_type::pixel_art);
const auto& emoji_sampler = texture_sampler_builder::get_sampler(sampler_type::anisotropic);
for (const auto& line : in_layout.lines) {
for (const auto& line : in_layout) {
for (const auto& position : line.glyphs) {
const auto& p = position;
const auto& region = p.region;
const auto& size = region.rect.size().cast<float>();
const auto& uv = region.uv_rect;
const auto& size = region->rect.size().cast<float>();
const auto& uv = region->uv_rect;
if (const auto& texture = region.texture.lock()) {
if (const auto& texture = region->texture.lock()) {
// 构建完整的批次键
batch_key new_key;
new_key.pipeline = position.is_emoji ? image_pipeline_ : text_pipeline_;

View File

@@ -46,7 +46,7 @@ void meditable_text_box::on_paint(mirage_paint_context& in_context) {
{ { 0.1f, 0.1f, 0.1f, 1.f } },
{ round_ }
);
auto text_y = (in_context.geo().get_local_size().y() - layout_.total_size.y()) / 2 + round_;
auto text_y = round_;
// 绘制文本
in_context.drawer().make_text(
layout_,
@@ -55,8 +55,9 @@ void meditable_text_box::on_paint(mirage_paint_context& in_context) {
in_context.geo()
);
if (is_focus()) {
const float line_height = layout_.get_last_line().line_height;
const auto& cursor_pos = layout_.last_cursor + Eigen::Vector2f(round_, round_);
const float line_height = layout_.get_last_line().get_line_height();
const float line_width = layout_.get_last_line().line_width;
const auto& cursor_pos = Eigen::Vector2f(line_width, layout_.get_last_line().position_y) + Eigen::Vector2f(round_, round_);
// 绘制光标
in_context.drawer().make_rounded_rect(
cursor_pos,
@@ -72,7 +73,7 @@ void meditable_text_box::on_paint(mirage_paint_context& in_context) {
auto meditable_text_box::compute_desired_size(float in_layout_scale_multiplier) const -> Eigen::Vector2f {
return no_warp_size_
.cwiseMax(layout_.total_size)
.cwiseMax(Eigen::Vector2f(0, layout_.get_last_line().line_height))
.cwiseMax(Eigen::Vector2f(0, layout_.get_empty_line_height()))
+ Eigen::Vector2f(0, round_ * 2);
}
@@ -144,7 +145,10 @@ void meditable_text_box::update_layout(const geometry_t& in_geometry, bool in_la
invalidate(invalidate_reason::layout);
}
layout_ = font_manager::instance().layout_text(temp_text_, font_, font_size_, max_width, line_spacing_);
// layout_ = font_manager::instance().layout_text(temp_text_, font_, font_size_, max_width, line_spacing_);
layout_.set_font_size(font_size_);
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;
if (in_layout_validate) {
invalidate(invalidate_reason::layout);