水平布局

This commit is contained in:
Nanako 2025-04-04 12:24:14 +08:00
parent 2b923c2bb5
commit 09f56e6c57
12 changed files with 169 additions and 101 deletions

View File

@ -18,7 +18,7 @@ int main(int argc, char* argv[]) {
manager.add_font(L"C:/Windows/Fonts/seguiemj.ttf");
const auto& text_block = std::make_shared<mtext_block>();
text_block->set_text(U"Hello, World! 你好,世界!🥶");
text_block->set_text(U"Hello, World! 你好,世界!\n换行测试1111测试测试测试测试测试测试");
const auto& text_block2 = std::make_shared<mtext_block>();
text_block2->set_text(U"Hello, World!");

View File

@ -5,6 +5,7 @@
#include "freetype_interface.h"
#include "pixel.h"
#include "freetype/ftadvanc.h"
#include "freetype/ftcolor.h"
#include "freetype/ftglyph.h"
#include "freetype/ftlcdfil.h"
@ -24,13 +25,6 @@ void freetype_bitmap_deleter(image_heap_t* in_data) {
delete in_data;
}
std::string freetype_interface::get_font_name() const {
if (face_ == nullptr) {
return "unknown font";
}
return face_->family_name ? face_->family_name : "unknown font";
}
std::string freetype_interface::get_font_family() const {
if (face_ == nullptr) {
return "unknown font";
@ -51,9 +45,10 @@ bool freetype_interface::supports_color_emoji() const {
font_v_metrics_t freetype_interface::get_metrics() const {
font_v_metrics_t metrics{};
metrics.ascent = face_->ascender / 64.f;
metrics.descent = face_->descender / 64.f;
metrics.line_height = face_->size->metrics.height / 64.f;
// 使用当前字体大小下的度量信息
metrics.ascent = face_->size->metrics.ascender / 64.0f;
metrics.descent = -face_->size->metrics.descender / 64.0f; // FreeType中descent为负值
metrics.line_height = face_->size->metrics.height / 64.0f;
return metrics;
}
@ -116,6 +111,9 @@ uint32_t freetype_interface::find_glyph_index(uint32_t in_unicode_codepoint) con
}
float freetype_interface::get_kerning(uint32_t in_first_glyph_id, uint32_t in_second_glyph_id) const {
if (!FT_HAS_KERNING(face_)) {
return 0.0f;
}
FT_Vector kerning;
if (FT_Get_Kerning(face_, in_first_glyph_id, in_second_glyph_id, FT_KERNING_DEFAULT, &kerning) != 0) {
return 0.0f;
@ -126,23 +124,59 @@ float freetype_interface::get_kerning(uint32_t in_first_glyph_id, uint32_t in_se
glyph_shaped_t freetype_interface::shape_glyph(uint32_t in_glyph_id) const {
glyph_shaped_t out{};
FT_Load_Glyph(face_, in_glyph_id, FT_LOAD_NO_BITMAP);
FT_Glyph glyph;
if (FT_Get_Glyph(face_->glyph, &glyph) != 0) {
// 加载字形 - 使用 FT_LOAD_DEFAULT 而非 FT_LOAD_NO_BITMAP
if (FT_Load_Glyph(face_, in_glyph_id, FT_LOAD_DEFAULT | FT_LOAD_FORCE_AUTOHINT) != 0) {
return out;
}
FT_BBox bbox;
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &bbox);
// 获取字形度量
FT_GlyphSlot slot = face_->glyph;
// 设置基本信息
out.glyph_index = in_glyph_id;
out.advance.x() = static_cast<float>(face_->glyph->advance.x) / 64.0f;
out.advance.y() = static_cast<float>(face_->glyph->advance.y) / 64.0f;
out.offset.x() = static_cast<float>(face_->glyph->metrics.horiBearingX) / 64.0f;
out.offset.y() = static_cast<float>(face_->glyph->metrics.horiBearingY) / -64.0f;
out.rect.set_position({ bbox.xMin, bbox.yMin });
out.rect.set_size({ bbox.xMax - bbox.xMin, bbox.yMax - bbox.yMin });
FT_Done_Glyph(glyph);
// 计算字形的前进量 (26.6 固定小数点格式转为浮点数)
out.advance.x() = slot->advance.x >> 6;
out.advance.y() = slot->metrics.vertAdvance >> 6;
// 字形偏移量
out.offset.x() = static_cast<float>(slot->bitmap_left);
out.offset.y() = static_cast<float>(-slot->bitmap_top);
// 水平基线
out.hori_bearing.x() = static_cast<float>(slot->metrics.horiBearingX) / 64.0f;
out.hori_bearing.y() = static_cast<float>(slot->metrics.horiBearingY) / -64.0f;
// 垂直基线
out.vert_bearing.x() = static_cast<float>(slot->metrics.vertBearingX) / 64.0f;
out.vert_bearing.y() = static_cast<float>(slot->metrics.vertBearingY) / 64.0f;
// 字形矩形 - 直接使用位图尺寸
if (slot->format == FT_GLYPH_FORMAT_BITMAP) {
// 位图字形
out.rect.set_position({ 0, 0 });
out.rect.set_size({
static_cast<float>(slot->bitmap.width),
static_cast<float>(slot->bitmap.rows)
});
} else {
// 非位图字形 - 使用轮廓边界盒
FT_Glyph glyph;
if (FT_Get_Glyph(slot, &glyph) != 0) {
return out;
}
FT_BBox bbox;
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &bbox);
out.rect.set_position({ bbox.xMin, bbox.yMin });
out.rect.set_size({
static_cast<float>(bbox.xMax - bbox.xMin),
static_cast<float>(bbox.yMax - bbox.yMin)
});
FT_Done_Glyph(glyph);
}
return out;
}

View File

@ -4,7 +4,6 @@
class freetype_interface : public font_face_interface {
public:
[[nodiscard]] std::string get_font_name() const override;
[[nodiscard]] std::string get_font_family() const override;
[[nodiscard]] std::string get_font_style() const override;
[[nodiscard]] bool supports_color_emoji() const override;

View File

@ -16,38 +16,6 @@ void stb_truetype_deleter(image_heap_t* in_data) {
delete in_data;
}
std::string stb_font_face_t::get_font_name() const {
const auto* data = static_cast<const uint8_t*>(font_data_->get_data());
const int font_offset = stbtt_GetFontOffsetForIndex(data, 0);
if (font_offset < 0) {
return "unknown font";
}
uint32_t name_offset = font::find_table_offset(data, font_offset, "name");
if (name_offset == 0) {
return "unknown font";
}
const uint8_t* name_table = data + name_offset;
const uint16_t count = font::read_u16(name_table + 2);
const uint16_t string_offset = font::read_u16(name_table + 4);
for (uint16_t i = 0; i < count; ++i) {
const uint8_t* record = name_table + 6 + 12 * i;
const uint16_t name_id = font::read_u16(record + 6);
if (name_id == 4 || name_id == 1) {
const uint16_t length = font::read_u16(record + 8);
const uint16_t offset = font::read_u16(record + 10);
if (length > 0) {
const uint8_t* string_data = name_table + string_offset + offset;
std::string name(reinterpret_cast<const char*>(string_data), length);
return name;
}
}
}
return "unknown font";
}
std::string stb_font_face_t::get_font_family() const {
const auto* data = static_cast<const uint8_t*>(font_data_->get_data());
const int font_offset = stbtt_GetFontOffsetForIndex(data, 0);
@ -173,12 +141,16 @@ glyph_shaped_t stb_font_face_t::shape_glyph(uint32_t in_glyph_id) const {
glyph_shaped_t out{};
out.glyph_index = in_glyph_id;
out.advance.x() = (float)advance * scale_;
out.advance.y() = 0.0f;
out.offset.x() = (float)lsb * scale_;
out.offset.y() = y0;
out.advance.x() = (float)advance * scale_;
out.advance.y() = 0.0f;
out.rect.set_position({ x0, y0 });
out.rect.set_size({ x1 - x0, y1 - y0 });
out.hori_bearing.x() = (float)lsb * scale_;
out.hori_bearing.y() = y0;
out.vert_bearing.x() = 0.0f;
out.vert_bearing.y() = y0;
return out;
}

View File

@ -4,7 +4,6 @@
class stb_font_face_t : public font_face_interface {
public:
[[nodiscard]] std::string get_font_name() const override;
[[nodiscard]] std::string get_font_family() const override;
[[nodiscard]] std::string get_font_style() const override;

View File

@ -8,7 +8,7 @@ glyph_atlas_result_t bitmap_glyph_atlas::get_or_create_glyph(int32_t in_glyph_in
glyph_atlas_result_t result{};
// 创建缓存键
const auto& cache_key = create_cache_key(in_glyph_index, in_font->get_font_name(), in_font_size);
const auto& cache_key = create_cache_key(in_glyph_index, in_font->get_font_family(), in_font_size);
// 检查缓存,如果已存在则直接返回
auto cached_result = find_in_cache(cache_key);

View File

@ -8,7 +8,7 @@ glyph_atlas_result_t color_emoji_atlas::get_or_create_emoji(int32_t in_glyph_ind
glyph_atlas_result_t result{};
// 创建缓存键
const auto& cache_key = create_cache_key(in_glyph_index, in_font->get_font_name(), in_font_size);
const auto& cache_key = create_cache_key(in_glyph_index, in_font->get_font_family(), in_font_size);
// 检查缓存
auto cached_result = find_in_cache(cache_key);

View File

@ -76,25 +76,43 @@ text_layout_t font_manager::layout_text(
// 使用指定字体或主字体
const auto& primary_font = in_font ? in_font : get_primary_font();
assert(primary_font && "No valid font available");
primary_font->set_font_size(font_size);
// 初始化布局变量
float cursor_x = 0.f;
float cursor_y = 0.f;
float line_height = 0.f;
float cursor_x = 0.0f;
float cursor_y = 0.0f;
float width = 0.0f;
float height = 0.0f;
uint32_t prev_glyph_id = 0; // 上一个字形索引
float width = 0.f;
float height = 0.f;
// 当前行信息
struct LineInfo {
float height = 0.0f; // 行总高度
float ascent = 0.0f; // 最大上升距离
float descent = 0.0f; // 最大下降距离
bool has_content = false;
};
LineInfo current_line;
// 完成当前行的布局
auto finish_line = [&] {
// 更新总体尺寸
width = std::max(width, cursor_x);
height += line_height + line_spacing;
if (current_line.has_content) {
// 确保行高至少能容纳当前行的内容
float actual_line_height = current_line.ascent + current_line.descent;
current_line.height = std::max(current_line.height, actual_line_height);
// 移到下一行
cursor_y += line_height + line_spacing;
cursor_x = 0.0f;
line_height = 0.f;
// 更新总体尺寸
width = std::max(width, cursor_x);
height += current_line.height;
// 移到下一行
cursor_y += current_line.height + line_spacing;
cursor_x = 0.0f;
// 重置行信息
current_line = LineInfo{};
}
};
// 处理每个字符
@ -124,13 +142,12 @@ text_layout_t font_manager::layout_text(
// 使用新字体重新获取字形索引
glyph_index = using_font->find_glyph_index(c);
}
using_font->set_font_size(font_size);
const auto& v_metrics = primary_font->get_metrics();
line_height = std::max(line_height, v_metrics.line_height);
// 设置当前字体大小
using_font->set_font_size(font_size);
// 当前行的基线位置
const float baseline = cursor_y + v_metrics.baseline();
// 获取当前字体的度量信息
const auto& current_metrics = using_font->get_metrics();
// 获取字形度量信息
const auto& glyph_metrics = using_font->shape_glyph(glyph_index);
@ -142,38 +159,68 @@ text_layout_t font_manager::layout_text(
}
// 检查是否需要换行
if (max_width > 0 && cursor_x + glyph_metrics.advance.x() > max_width) { // 确保不是行首字符
if (max_width > 0 && cursor_x + glyph_metrics.advance.x() > max_width && current_line.has_content) {
finish_line();
}
// 标记当前行有内容
current_line.has_content = true;
// 更新当前行的度量信息
current_line.ascent = std::max(current_line.ascent, current_metrics.ascent);
current_line.descent = std::max(current_line.descent, current_metrics.descent);
// 特殊处理表情符号的高度
if (is_emoji) {
// 确保有足够的空间容纳表情符号
float emoji_height = glyph_metrics.rect.height() * 1.2f;
current_line.height = std::max(current_line.height, emoji_height);
} else {
// 使用字体的行高
current_line.height = std::max(current_line.height, current_metrics.line_height);
}
// 计算基线位置 - 使用当前行的上升距离
float baseline = cursor_y + current_line.ascent;
// 计算字形坐标
float x = cursor_x + glyph_metrics.offset.x() + glyph_metrics.rect.left();
float y = baseline + glyph_metrics.offset.y();
float x = cursor_x + glyph_metrics.hori_bearing.x();
float y = baseline + glyph_metrics.hori_bearing.y();
// 添加字形位置信息到布局
auto& glyph_position = layout.glyphs.emplace_back();
glyph_position.is_emoji = is_emoji;
glyph_position.glyph_index = glyph_index;
glyph_position.position = { x, y };
glyph_position.size = glyph_metrics.rect.size();
glyph_position.region = *region;
auto& glyph_position = layout.glyphs.emplace_back();
glyph_position.is_emoji = is_emoji;
glyph_position.glyph_index = glyph_index;
glyph_position.position = { x, y };
glyph_position.size = glyph_metrics.rect.size();
glyph_position.region = *region;
glyph_position.advance = glyph_metrics.advance.x();
glyph_position.left_bearing = glyph_metrics.hori_bearing.x();
glyph_position.hori_bearing_x = glyph_metrics.hori_bearing.x();
// 更新光标位置
cursor_x += glyph_metrics.advance.x() + glyph_metrics.rect.left();
cursor_x += glyph_metrics.advance.x(); // 添加字形间距
// cursor_x += glyph_metrics.hori_bearing.x() + glyph_metrics.rect.width();
// cursor_x += glyph_metrics.lsb - glyph_metrics.rsb; // 添加字形间距
if (prev_glyph_id != 0) {
// 添加字形间距
cursor_x += using_font->get_kerning(prev_glyph_id, glyph_index);
}
prev_glyph_id = glyph_index; // 更新上一个字形索引
}
finish_line();
// 处理最后一行
finish_line();
// 设置文本布局的总体尺寸
layout.total_size = {
width,
height // cursor_y已经包含了所有行的高度
};
layout.total_size = { width, height };
return layout;
}
/**
* @brief
*/

View File

@ -122,6 +122,10 @@ struct text_layout_t {
Eigen::Vector2f position; // 屏幕位置
Eigen::Vector2f size; // 字形尺寸
atlas_region_t region; // 纹理图集区域
float advance; // 前进量
float left_bearing; // 左侧间距
float hori_bearing_x; // 水平基线X坐标
};
std::vector<glyph_position_t> glyphs; // 所有字形位置

View File

@ -22,10 +22,12 @@ struct font_v_metrics_t {
};
struct glyph_shaped_t {
int32_t glyph_index; // Unicode码点
Eigen::Vector2f offset; // 相对位置偏移
Eigen::Vector2f advance; // 前进值
rect_t<> rect; // 字形矩形区域
int32_t glyph_index; // Unicode码点
Eigen::Vector2f offset; // 相对位置偏移
Eigen::Vector2f advance; // 前进值
Eigen::Vector2f hori_bearing; // 水平基线
Eigen::Vector2f vert_bearing; // 垂直基线
rect_t<> rect; // 字形矩形区域
};
class font_face_interface {
@ -36,7 +38,6 @@ public:
void set_font_size(float in_size) { font_size_ = in_size; on_set_font_size(in_size); }
[[nodiscard]] float get_font_size() const { return font_size_; }
[[nodiscard]] virtual std::string get_font_name() const = 0;
[[nodiscard]] virtual std::string get_font_family() const = 0;
[[nodiscard]] virtual std::string get_font_style() const = 0;

View File

@ -293,7 +293,19 @@ void render_elements::make_text(const text_layout_t& in_layout, const Eigen::Vec
ensure_batch_compatibility(new_key);
make_rect(real_pos, size, in_color, in_geometry, in_effect, {}, {}, {}, uv, in_rotation_radians, in_pivot, in_scale);
// make_wireframe(real_pos, size, in_geometry, 1.0f, in_color, in_rotation_radians, in_pivot, in_scale);
// Debug 绘制字形图元线框
// make_wireframe(real_pos, size, in_geometry, 1.0f, in_effect, { {1, 0, 0, 0.5 } }, in_rotation_radians, in_pivot, in_scale);
// Debug 绘制字形前进线
make_line(real_pos, real_pos - Eigen::Vector2f{ p.left_bearing, 0 }, in_geometry, 5.0f, in_effect, { {0, 1, 0, 0.5 } }, in_rotation_radians, in_pivot, in_scale);
// Debug 绘制字形advance线
Eigen::Vector2f advance_start = real_pos - Eigen::Vector2f{ p.left_bearing, -size.y() - 2 };
static linear_color random_advance_color = { 0, 1, 1, 1 };
// 随机颜色
random_advance_color.r = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
random_advance_color.g = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
random_advance_color.b = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
make_line(advance_start, advance_start + Eigen::Vector2f{ p.advance, 0 }, in_geometry, 4.0f, in_effect, { random_advance_color }, in_rotation_radians, in_pivot, in_scale);
}
}
// Eigen::Vector2f font_ascent_start = in_pos;

View File

@ -45,7 +45,7 @@ private:
std::u32string text_;
text_layout_t layout_{};
float font_size_ = 24.0f;
float font_size_ = 48.0f;
float line_spacing_ = 1.f;
float max_width_ = 0.0f;
std::shared_ptr<font_face_interface> font_;