TODO 文本布局部分重构完成
This commit is contained in:
2
src/mirage_config/third_party/tomlplusplus
vendored
2
src/mirage_config/third_party/tomlplusplus
vendored
Submodule src/mirage_config/third_party/tomlplusplus updated: 2f35c28a52...fea1d905f2
@@ -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_ };
|
||||
}
|
||||
};
|
||||
|
||||
Submodule src/mirage_render/font/freetype_font/freetype updated: 42a649be49...82090e67c2
Submodule src/mirage_render/font/freetype_font/harfbuzz updated: e1dcc454f1...9f83bbbe64
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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; // 主字体
|
||||
};
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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 {};
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user