文本布局系统

This commit is contained in:
Nanako 2025-03-28 03:14:21 +08:00
parent a19317b2f2
commit 727457b9f7
10 changed files with 69 additions and 57 deletions

View File

@ -48,7 +48,6 @@ else ()
add_definitions(-DDEBUG=0)
endif ()
add_subdirectory(third_party/harfbuzz)
add_subdirectory(src/sokol)
add_subdirectory(src/mirage_render)
add_subdirectory(src/mirage_image)

View File

@ -1,21 +1,18 @@
#include "mirage.h"
#include "window/mwindow.h"
#include "widget/panel_widget/mbox.h"
#include "font/font_atlas_system.h"
#include "font/font_face.h"
#include "font/font_system.h"
#include "widget/widget_new.h"
#include "widget/leaf_widget/mimage.h"
#include "widget/leaf_widget/mtext_block.h"
int main(int argc, char* argv[]) {
mirage_app::get().init();
auto& manager = font_manager::instance();
manager.add_font(L"C:/Users/46944/AppData/Local/Microsoft/Windows/Fonts/MapleMono-NF-CN-Regular.ttf");
manager.add_font(L"C:/Windows/Fonts/msyh.ttc");
auto text_block = std::make_shared<mtext_block>();
text_block->set_text("Hello, World!");
text_block->set_text(U"Hello, World! 你好,世界!😀");
const auto& window = mwindow::create({ 1024, 1024 }, L"Hello, World!");
window->set_content(text_block);

View File

@ -71,35 +71,13 @@ public:
return result;
}
// 获取字形度量
// const auto& glyph_metrics = in_font.get_glyph_by_index(in_glygh_index, in_font_size);
// 计算MTSDF尺寸(添加4px内边距)
// const int padding = 4;
// int width = static_cast<int>(glyph_metrics->size.x()) + padding * 2;
// int height = static_cast<int>(glyph_metrics->size.y()) + padding * 2;
// 确保最小尺寸
// width = std::max(width, 8);
// height = std::max(height, 8);
// 生成MTSDF
// const auto& mtsdf_data = mtsdf_generator::generate_mtsdf(
// glyph_shape.data(),
// glyph_shape.size(),
// width,
// height,
// 3.0f // 范围值
// );
// 使用stb_truetype生成MTSDF
// 使用stb_truetype生成位图
float scale = in_font.get_scale_for_pixel_height(in_font_size);
int width, height, xoff, yoff;
auto bitmap = stbtt_GetGlyphBitmap(&in_font.get_font_info(), scale, scale, in_glygh_index, &width, &height, &xoff, &yoff);
// 从图集分配空间
auto region = atlas_->allocate_region({width, height});
auto region = atlas_->allocate_region({ width, height }, { 2, 2 });
if (!region) {
result.reason = glyph_atlas_reason_t::atlas_full;
return result;
@ -197,7 +175,7 @@ public:
const auto& bitmap = *bitmap_opt;
// 从图集分配空间
const auto& region = atlas_->allocate_region({bitmap.width, bitmap.height});
const auto& region = atlas_->allocate_region({ bitmap.width, bitmap.height }, { 2, 2 });
if (!region) {
return std::nullopt;
}

View File

@ -53,7 +53,11 @@ std::optional<glyph_t> font_face_t::get_glyph_by_index(int32_t in_glyph_index, f
// 获取垂直度量
int ascent, descent, line_gap;
stbtt_GetFontVMetrics(&font_info_, &ascent, &descent, &line_gap);
// 尝试使用OS2表中的度量
if (!stbtt_GetFontVMetricsOS2(&font_info_, &ascent, &descent, &line_gap)) {
// 如果失败使用默认的VMetrics
stbtt_GetFontVMetrics(&font_info_, &ascent, &descent, &line_gap);
}
// 获取字形边界
int x0, y0, x1, y1;

View File

@ -47,6 +47,10 @@ public:
return stbtt_FindGlyphIndex(&font_info_, in_code_point);
}
float get_glyph_advance(int32_t in_glyph_index1, int32_t in_glyph_index2) const {
return stbtt_GetGlyphKernAdvance(&font_info_, in_glyph_index1, in_glyph_index2);
}
/**
* @brief
* @param in_code_point Unicode码点

View File

@ -1,11 +1,11 @@
#include "font_system.h"
text_layout_t font_manager::layout_text(const std::string& text,
const std::shared_ptr<font_face_t>& in_font, float font_size,
float max_width,
float line_spacing) {
text_layout_t font_manager::layout_text(const std::u32string& text,
const std::shared_ptr<font_face_t>& in_font, float font_size,
float max_width,
float line_spacing) {
text_layout_t layout;
float scale = in_font->get_scale_for_pixel_height(font_size);
const float scale = in_font->get_scale_for_pixel_height(font_size);
// 布局变量
float cursor_x = 0.0f;
@ -14,6 +14,9 @@ text_layout_t font_manager::layout_text(const std::string& text
const float ascent = in_font->get_ascent() * scale;
float baseline = cursor_y + ascent;
int32_t last_index = 0;
float min_y = std::numeric_limits<float>::max();
// 处理每个字形
for (const auto& c : text) {
const auto glyph_index = in_font->find_glyph_index(c);
@ -40,7 +43,7 @@ text_layout_t font_manager::layout_text(const std::string& text
// **计算最终位置**
float glyph_x = cursor_x + glyph_metrics->bearing.x();
float glyph_y = ascent - glyph_metrics->descent + glyph_metrics->line_gap + glyph_metrics->bearing.y();
float glyph_y = glyph_metrics->ascent - glyph_metrics->descent + glyph_metrics->line_gap + glyph_metrics->bearing.y();
auto& glyph_position = layout.glyphs.emplace_back();
glyph_position.glyph_index = glyph_index;
@ -51,6 +54,20 @@ text_layout_t font_manager::layout_text(const std::string& text
// 前进光标
cursor_x += glyph_metrics->advance;
cursor_y += 0; // 通常为0除非是垂直文本
if (last_index > 0) {
const float kern_advance = in_font->get_glyph_advance(last_index, glyph_index) * scale;
cursor_x += kern_advance;
}
last_index = glyph_index;
// 更新行的最小和最大Y坐标
min_y = std::min(min_y, glyph_y);
}
// 根据最小和最大Y坐标更新行的高度
for (auto& line : layout.glyphs) {
line.position.y() -= min_y;
}
// 设置布局总体尺寸

View File

@ -116,7 +116,7 @@ public:
}
text_layout_t layout_text(
const std::string& text,
const std::u32string& text,
const std::shared_ptr<font_face_t>& in_font,
float font_size,
float max_width = 0.0f,

View File

@ -36,25 +36,38 @@ std::optional<rect_t<int32_t>> texture2d_atlas::allocate(const Eigen::Vector2i&
return std::nullopt;
}
std::optional<atlas_region_t> texture2d_atlas::allocate_region(const Eigen::Vector2i& size) {
if (!allocator_) {
return std::nullopt;
}
std::optional<atlas_region_t> texture2d_atlas::allocate_region(const Eigen::Vector2i& size, const Eigen::Vector2i& padding) {
if (!allocator_) {
return std::nullopt;
}
// 使用分配器分配区域
auto rect_opt = allocator_->allocate(size);
if (!rect_opt) {
return std::nullopt; // 分配失败
}
// 计算包含padding的总尺寸
const Eigen::Vector2i& padded_size = size + padding * 2; // 每边加上padding
// 分配成功,创建区域信息
const int region_id = next_region_id_++;
atlas_region_t region(region_id, *rect_opt, texture_, calculate_uv_rect(*rect_opt));
// 使用分配器分配包含padding的区域
const auto& rect_opt = allocator_->allocate(padded_size);
if (!rect_opt) {
return std::nullopt; // 分配失败
}
// 存储区域信息
allocated_regions_[region.id] = region;
// 分配成功,创建区域信息
const int region_id = next_region_id_++;
return region;
// 获取分配的带padding的矩形
const auto& padded_rect = *rect_opt;
const Eigen::Vector2i& content_position = padded_rect.top_left() + padding;
// 计算内部有效区域不含padding
rect_t<int32_t> content_rect{ content_position, size };
// 创建region对象使用内容区域的UV坐标
atlas_region_t region(region_id, content_rect, texture_, calculate_uv_rect(content_rect));
// 存储区域信息
allocated_regions_[region.id] = region;
return region;
}
bool texture2d_atlas::free(const rect_t<int32_t>& rect) {

View File

@ -44,7 +44,7 @@ public:
* @param size
* @return std::nullopt
*/
std::optional<atlas_region_t> allocate_region(const Eigen::Vector2i& size);
std::optional<atlas_region_t> allocate_region(const Eigen::Vector2i& size, const Eigen::Vector2i& padding = {0, 0});
/**
* @brief

View File

@ -6,7 +6,7 @@ class mtext_block : public mleaf_widget {
public:
void on_paint(mirage_paint_context& in_context) override;
void set_text(const std::string& in_text) {
void set_text(const std::u32string& in_text) {
text_ = in_text;
update_layout();
}
@ -41,7 +41,7 @@ public:
private:
void update_layout();
std::string text_;
std::u32string text_;
text_layout_t layout_;
float font_size_ = 24.0f * 1.5f;
float line_spacing_ = 1.2f;