文本布局系统
This commit is contained in:
parent
a19317b2f2
commit
727457b9f7
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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码点
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
// 设置布局总体尺寸
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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 释放之前分配的纹理区域
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user