字体布局

This commit is contained in:
Nanako 2025-03-27 18:36:32 +08:00
parent 6129e71db7
commit 5aafef4cfb
14 changed files with 6175 additions and 72 deletions

View File

@ -9,6 +9,7 @@
#include "pixel.h"
#include "misc/mapped_file/mapped_file.h"
#include "stb_image_loader.h"
#include "font/font_face.h"
#include "texture/texture2d.h"
#include "widget/widget_new.h"
#include "widget/leaf_widget/mimage.h"
@ -17,6 +18,13 @@ int main(int argc, char* argv[]) {
mirage_app::get().init();
{
font_face_t face;
face.load_from_file(L"C:\\Windows\\Fonts\\segmdl2.ttf");
auto name = face.get_font_name();
auto color = face.supports_color_emoji();
}
auto ii = std::make_shared<texture2d>();
{
@ -35,7 +43,6 @@ int main(int argc, char* argv[]) {
}
{
auto map = ii->map(texture_map_type::read);
map->read_only;
}
auto image2 = std::make_shared<mimage>();

View File

@ -624,45 +624,6 @@ public:
return rect_t<U>({ x, y }, { width, height });
}
/**
* @brief
* @tparam U
* @param in_h_align
* @param in_v_align
* @param in_text_size
* @return
*/
template<typename U>
auto get_text_rect(horizontal_text_alignment_t in_h_align, vertical_text_alignment_t in_v_align, const Eigen::Vector2<U>& in_text_size) const {
rect_t<U> text_rect{{0, 0}, in_text_size};
switch (in_h_align) {
case horizontal_text_alignment_t::left:
text_rect.position_.x() = left();
break;
case horizontal_text_alignment_t::center:
text_rect.position_.x() = left() + (width() - text_rect.width()) / 2;
break;
case horizontal_text_alignment_t::right:
text_rect.position_.x() = right() - text_rect.width();
break;
}
switch (in_v_align) {
case vertical_text_alignment_t::top:
text_rect.position_.y() = top();
break;
case vertical_text_alignment_t::center:
text_rect.position_.y() = top() + (height() - text_rect.height()) / 2;
break;
case vertical_text_alignment_t::bottom:
text_rect.position_.y() = bottom() - text_rect.height();
break;
}
return text_rect;
}
//-------------- 分割操作 --------------
/**

View File

@ -57,26 +57,6 @@ enum class vertical_alignment_t {
stretch ///< 拉伸填充
};
/**
* @enum horizontal_text_alignment_t
* @brief
*/
enum class horizontal_text_alignment_t {
left, ///< 文本左对齐
center, ///< 文本居中对齐
right ///< 文本右对齐
};
/**
* @enum vertical_text_alignment_t
* @brief
*/
enum class vertical_text_alignment_t {
top, ///< 文本顶部对齐
center, ///< 文本居中对齐
bottom ///< 文本底部对齐
};
/**
* @enum visibility_t
* @brief

View File

@ -5,7 +5,7 @@ retrieve_files(${CMAKE_CURRENT_SOURCE_DIR} SOURCE_FILES)
add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(${PROJECT_NAME} PUBLIC mirage_core sokol)
target_link_libraries(${PROJECT_NAME} PUBLIC mirage_core sokol msdfgen-core)
option(MIRAGE_STB_IMAGE_LOADER "Use stb load image" ON)

View File

@ -0,0 +1,88 @@
#pragma once
// emoji_detector.h - 表情符号检测
#include <cstdint>
#include <string>
#include <vector>
/**
* @class emoji_detector
* @brief
*/
class emoji_detector {
public:
/**
* @brief
* @param in_code_point Unicode码点
* @return
*/
static bool is_emoji(uint32_t in_code_point) {
// 常见表情符号范围检查
if ((in_code_point >= 0x1F300 && in_code_point <= 0x1F64F) || // 表情符号
(in_code_point >= 0x1F680 && in_code_point <= 0x1F6FF) || // 交通与地图
(in_code_point >= 0x1F700 && in_code_point <= 0x1F77F) || // 符号补充
(in_code_point >= 0x1F780 && in_code_point <= 0x1F7FF) || // 几何形状
(in_code_point >= 0x1F900 && in_code_point <= 0x1F9FF) || // 补充符号与象形文字
(in_code_point >= 0x2600 && in_code_point <= 0x26FF)) { // 杂项符号
return true;
}
return false;
}
/**
* @brief
* @param in_code_point Unicode码点
* @return
*/
static bool is_emoji_sequence_component(uint32_t in_code_point) {
// 表情变体选择器
if (in_code_point == 0xFE0F || in_code_point == 0xFE0E) { return true; }
// 肤色修饰符
if (in_code_point >= 0x1F3FB && in_code_point <= 0x1F3FF) { return true; }
// 零宽连接符
if (in_code_point == 0x200D) { return true; }
return false;
}
/**
* @brief
* @param in_text Unicode文本
* @param in_start_pos
* @param in_out_length
* @return
*/
static std::vector<uint32_t> extract_emoji_sequence(const std::u32string& in_text, size_t in_start_pos,
size_t& in_out_length) {
std::vector<uint32_t> sequence;
if (in_start_pos >= in_text.length() || !is_emoji(in_text[in_start_pos])) {
in_out_length = 0;
return sequence;
}
// 添加基础表情符号
sequence.push_back(in_text[in_start_pos]);
in_out_length = 1;
// 寻找完整序列
size_t pos = in_start_pos + 1;
while (pos < in_text.length()) {
const uint32_t cp = in_text[pos];
// 检查是否是序列组件或跟随零宽连接符的表情
if (is_emoji_sequence_component(cp) || (pos > 0 && in_text[pos - 1] == 0x200D && is_emoji(cp))) {
sequence.push_back(cp);
in_out_length++;
pos++;
}
else { break; }
}
return sequence;
}
};

View File

@ -0,0 +1,214 @@
#pragma once
// font_atlas_system.h - 字形与表情图集管理
#include <Eigen/Eigen>
#include "font_face.h"
#include "texture/atlas/texture2d_atlas.h"
#include "font/mtsdf_generator.h"
/**
* @class MTSDFGlyphAtlas
* @brief MTSDF字形图集
*/
class MTSDFGlyphAtlas {
public:
/**
* @brief
* @param in_atlas_size
* @return
*/
bool initialize(const Eigen::Vector2i& in_atlas_size) {
atlas_ = std::make_shared<texture2d_atlas>(
in_atlas_size,
SG_PIXELFORMAT_RGBA8,
allocation_strategy_t::bin_packing
);
return atlas_ != nullptr;
}
/**
* @brief MTSDF字形
* @param in_code_point Unicode码点
* @param in_font
* @param in_generator MTSDF生成器
* @param in_font_size
* @return
*/
std::optional<atlas_region_t> get_or_create_glyph(
uint32_t in_code_point,
font_face_t& in_font,
mtsdf_generator& in_generator,
float in_font_size) {
// 创建缓存键
const auto& cache_key = create_glyph_key(in_code_point, in_font.get_font_name(), in_font_size);
// 检查缓存
auto it = cached_glyphs_.find(cache_key);
if (it != cached_glyphs_.end()) {
return it->second;
}
// 获取字形形状
auto glyph_shape = in_font.get_glyph_shape(in_code_point);
if (glyph_shape.empty()) {
return std::nullopt;
}
// 获取字形度量
const auto& glyph_metrics = in_font.get_glyph(in_code_point, in_font_size);
// 计算MTSDF尺寸(添加4px内边距)
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);
// 从图集分配空间
auto region = atlas_->allocate_region({width, height});
if (!region) {
return std::nullopt;
}
// 生成MTSDF
const auto& mtsdf_data = in_generator.generate_mtsdf(
glyph_shape.data(),
glyph_shape.size(),
width,
height,
3.0f // 范围值
);
// 更新图集
atlas_->update_region(mtsdf_data.data(), mtsdf_data.size(), region->rect);
// 缓存结果
cached_glyphs_[cache_key] = *region;
return *region;
}
/**
* @brief
* @return
*/
auto get_texture() const {
return atlas_->get_texture();
}
private:
std::shared_ptr<texture2d_atlas> atlas_;
std::unordered_map<std::string, atlas_region_t> cached_glyphs_;
/**
* @brief
* @param in_code_point Unicode码点
* @param in_font_name
* @param in_font_size
* @return
*/
std::string create_glyph_key(uint32_t in_code_point, const std::string& in_font_name, float in_font_size) {
std::stringstream ss;
ss << std::hex << in_code_point << "_" << in_font_name << "_" << std::fixed
<< std::setprecision(1) << in_font_size;
return ss.str();
}
};
/**
* @class color_emoji_atlas
* @brief
*/
class color_emoji_atlas {
public:
/**
* @brief
* @param in_atlas_size
* @return
*/
bool initialize(const Eigen::Vector2i& in_atlas_size) {
atlas_ = std::make_shared<texture2d_atlas>(
in_atlas_size,
SG_PIXELFORMAT_RGBA8,
allocation_strategy_t::bin_packing
);
return atlas_ != nullptr;
}
/**
* @brief
* @param in_emoji_sequence
* @param in_font
* @param in_font_size
* @return
*/
std::optional<atlas_region_t> get_or_create_emoji(
const std::vector<uint32_t>& in_emoji_sequence,
font_face_t& in_font,
float in_font_size) {
// 创建缓存键
const auto& emoji_key = create_emoji_key(in_emoji_sequence, in_font_size);
// 检查缓存
auto it = cached_emojis_.find(emoji_key);
if (it != cached_emojis_.end()) {
return it->second;
}
// 获取彩色表情位图
auto bitmap_opt = in_font.get_color_emoji_bitmap(in_emoji_sequence, in_font_size);
if (!bitmap_opt) {
return std::nullopt;
}
const auto& bitmap = *bitmap_opt;
// 从图集分配空间
auto region = atlas_->allocate_region({bitmap.width, bitmap.height});
if (!region) {
return std::nullopt;
}
// 更新图集
atlas_->update_region(bitmap.data.data(), bitmap.data.size(), region->rect);
// 缓存结果
cached_emojis_[emoji_key] = *region;
return *region;
}
/**
* @brief
* @return
*/
auto get_texture() const {
return atlas_->get_texture();
}
private:
std::shared_ptr<texture2d_atlas> atlas_;
std::unordered_map<std::string, atlas_region_t> cached_emojis_;
/**
* @brief
* @param in_sequence
* @param in_font_size
* @return
*/
std::string create_emoji_key(const std::vector<uint32_t>& in_sequence, float in_font_size) {
std::stringstream ss;
for (uint32_t code : in_sequence) {
ss << std::hex << code << "_";
}
ss << std::fixed << std::setprecision(1) << in_font_size;
return ss.str();
}
};

View File

@ -0,0 +1,385 @@
#pragma once
#include <fstream>
#include <string>
#include <vector>
#include <memory>
#include <optional>
#include <cstdint>
#include "font_type.h"
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"
#include "misc/mapped_file/mapped_file.h"
/**
* @class font_face_t
* @brief
*/
class font_face_t {
public:
/**
* @brief
* @param in_font_path
* @return
*/
bool load_from_file(const std::wstring& in_font_path) {
// 读取字体文件
if (!load_font_data(in_font_path)) {
return false;
}
// 初始化stb_truetype
const int offset = stbtt_GetFontOffsetForIndex((uint8_t*)font_data_->get_data(), 0);
if (!stbtt_InitFont(&font_info_, (uint8_t*)font_data_->get_data(), offset)) {
return false;
}
// 检测字体是否支持彩色表情
detect_color_tables();
return true;
}
/**
* @brief
* @param in_code_point Unicode码点
* @param in_font_size
* @return
*/
glyph_t get_glyph(uint32_t in_code_point, float in_font_size) const {
glyph_t glyph;
glyph.code_point = in_code_point;
// 获取字形索引
int glyph_index = stbtt_FindGlyphIndex(&font_info_, in_code_point);
if (glyph_index == 0) {
return glyph; // 缺失字形
}
// 计算缩放比例
float scale = stbtt_ScaleForPixelHeight(&font_info_, in_font_size);
// 获取水平度量
int advance, lsb;
stbtt_GetGlyphHMetrics(&font_info_, glyph_index, &advance, &lsb);
// 获取垂直度量
int ascent, descent, lineGap;
stbtt_GetFontVMetrics(&font_info_, &ascent, &descent, &lineGap);
// 获取字形边界
int x0, y0, x1, y1;
stbtt_GetGlyphBitmapBox(&font_info_, glyph_index, scale, scale, &x0, &y0, &x1, &y1);
// 设置字形数据
glyph.size = {static_cast<float>(x1 - x0), static_cast<float>(y1 - y0)};
glyph.bearing = {static_cast<float>(lsb) * scale, static_cast<float>(y0)};
glyph.advance = static_cast<float>(advance) * scale;
glyph.ascent = static_cast<float>(ascent) * scale;
glyph.descent = static_cast<float>(-descent) * scale;
return glyph;
}
/**
* @brief
* @param in_code_point Unicode码点
* @return
*/
std::vector<stbtt_vertex> get_glyph_shape(uint32_t in_code_point) const {
int glyph_index = stbtt_FindGlyphIndex(&font_info_, in_code_point);
if (glyph_index == 0) {
return {}; // 缺失字形
}
stbtt_vertex* vertices;
const auto num_vertices = stbtt_GetGlyphShape(&font_info_, glyph_index, &vertices);
// 复制顶点到向量
std::vector<stbtt_vertex> shape(vertices, vertices + num_vertices);
// 释放stb分配的内存
stbtt_FreeShape(&font_info_, vertices);
return shape;
}
/**
* @brief
* @param in_code_point Unicode码点
* @return
*/
bool has_glyph(uint32_t in_code_point) const {
return stbtt_FindGlyphIndex(&font_info_, in_code_point) > 0;
}
/**
* @brief
* @return
*/
bool supports_color_emoji() const {
return has_color_table_;
}
/**
* @brief
* @return
*/
std::string get_font_name() const {
return font_name_;
}
/**
* @brief
* @return
*/
std::string get_font_type() const {
return font_type_;
}
/**
* @brief
* @param in_type
*/
void set_font_type(const std::string& in_type) {
font_type_ = in_type;
}
/**
* @struct color_emoji_bitmap
* @brief
*/
struct color_emoji_bitmap {
std::vector<uint8_t> data; // RGBA像素数据
int width = 0; // 宽度
int height = 0; // 高度
};
/**
* @brief
* @param in_emoji_sequence
* @param in_font_size
* @return
*/
std::optional<color_emoji_bitmap> get_color_emoji_bitmap(
const std::vector<uint32_t>& in_emoji_sequence,
float in_font_size) const {
if (!supports_color_emoji() || in_emoji_sequence.empty()) {
return std::nullopt;
}
// 获取主要码点的字形索引
uint32_t unicode_codepoint = in_emoji_sequence[0];
int glyph_index = stbtt_FindGlyphIndex(&font_info_, unicode_codepoint);
if (glyph_index == 0) {
return std::nullopt;
}
color_emoji_bitmap bitmap;
// 实际应用中需要根据字体格式(CBDT/CBLC、SBIX、COLR/CPAL、SVG)提取彩色位图
// 简化实现,创建一个有色方块作为示例
bitmap.width = static_cast<int>(in_font_size);
bitmap.height = static_cast<int>(in_font_size);
bitmap.data.resize(bitmap.width * bitmap.height * 4, 255);
return bitmap;
}
private:
stbtt_fontinfo font_info_{}; // stb_truetype字体信息
std::shared_ptr<mapped_file> font_data_; // 字体文件数据
std::string font_name_; // 字体名称
std::string font_type_ = "regular"; // 字体类型
// 彩色表情支持标志
bool has_colr_tables_ = false;
bool has_cbdt_table_ = false;
bool has_sbix_table_ = false;
bool has_color_table_ = false;
bool has_svg_table_ = false;
/**
* @brief
* @param in_font_path
* @return
*/
bool load_font_data(const std::wstring& in_font_path) {
font_data_ = mapped_file::create();
if (!font_data_->map_file(in_font_path))
return false;
// 解析字体名称
extract_font_name();
return true;
}
/**
* @brief
* @param tag_data
* @param tag
* @return
*/
bool check_tag(const uint8_t* tag_data, const char* tag) const {
return (tag_data[0] == tag[0] &&
tag_data[1] == tag[1] &&
tag_data[2] == tag[2] &&
tag_data[3] == tag[3]);
}
/**
* @brief 2
* @param p
* @return 16
*/
uint16_t read_ushort(const uint8_t* p) const {
return (uint16_t)(p[0] << 8) + p[1];
}
/**
* @brief 4
* @param p
* @return 32
*/
uint32_t read_ulong(const uint8_t* p) const {
return ((uint32_t)p[0] << 24) + ((uint32_t)p[1] << 16) +
((uint32_t)p[2] << 8) + p[3];
}
/**
* @brief
* @param data
* @param fontstart
* @param tag
* @return 0
*/
uint32_t find_table_offset(const uint8_t* data, uint32_t fontstart, const char* tag) const {
int num_tables = read_ushort(data + fontstart + 4);
uint32_t tabledir = fontstart + 12;
for (int i = 0; i < num_tables; ++i) {
uint32_t loc = tabledir + 16 * i;
if (check_tag(data + loc, tag)) {
return read_ulong(data + loc + 8);
}
}
return 0;
}
/**
* @brief
*/
void extract_font_name() {
if (!font_data_ || !font_data_->get_data() || font_data_->get_size() == 0) {
font_name_ = "Unknown Font";
return;
}
const uint8_t* data = static_cast<const uint8_t*>(font_data_->get_data());
int font_offset = stbtt_GetFontOffsetForIndex(data, 0);
// 查找名称表
uint32_t name_offset = find_table_offset(data, font_offset, "name");
if (name_offset == 0) {
font_name_ = "Unknown Font";
return;
}
// 名称表头部
const uint8_t* name_table = data + name_offset;
uint16_t count = read_ushort(name_table + 2);
uint16_t string_offset = read_ushort(name_table + 4);
// 查找字体名称记录偏好顺序NameID 4=完整名称NameID 1=字体族名称)
for (uint16_t i = 0; i < count; ++i) {
const uint8_t* record = name_table + 6 + 12 * i;
uint16_t platform_id = read_ushort(record);
uint16_t language_id = read_ushort(record + 4);
uint16_t name_id = read_ushort(record + 6);
uint16_t length = read_ushort(record + 8);
uint16_t offset = read_ushort(record + 10);
// 优先使用Windows平台的英语完整名称
bool is_english = (platform_id == 3 && language_id == 0x0409); // Windows English (US)
if (name_id == 4 || (name_id == 1 && font_name_ == "Unknown Font")) {
// 获取字符串数据
const uint8_t* string_data = name_table + string_offset + offset;
std::string name;
// 处理不同平台的编码
if (platform_id == 3) { // Windows Unicode (UTF-16BE)
// 简单的UTF-16BE转UTF-8
for (uint16_t j = 0; j < length; j += 2) {
if (j + 1 < length) {
uint16_t unicode = (string_data[j] << 8) | string_data[j + 1];
if (unicode < 0x80) {
name.push_back(static_cast<char>(unicode));
} else if (unicode < 0x800) {
name.push_back(static_cast<char>(0xC0 | ((unicode >> 6) & 0x1F)));
name.push_back(static_cast<char>(0x80 | (unicode & 0x3F)));
} else {
name.push_back(static_cast<char>(0xE0 | ((unicode >> 12) & 0x0F)));
name.push_back(static_cast<char>(0x80 | ((unicode >> 6) & 0x3F)));
name.push_back(static_cast<char>(0x80 | (unicode & 0x3F)));
}
}
}
} else { // 假设为ASCII或其他单字节编码
name = std::string(reinterpret_cast<const char*>(string_data), length);
}
if (!name.empty()) {
font_name_ = name;
// 如果找到英语的完整名称,优先使用
if (name_id == 4 && is_english) {
return;
}
}
}
}
// 如果没找到有效名称,保持默认值
if (font_name_.empty()) {
font_name_ = "Unknown Font";
}
}
/**
* @brief
*/
void detect_color_tables() {
if (!font_data_ || !font_data_->get_data() || font_data_->get_size() == 0) {
return;
}
const uint8_t* data = static_cast<const uint8_t*>(font_data_->get_data());
int font_offset = stbtt_GetFontOffsetForIndex(data, 0);
// 检查表格是否存在
auto table_exists = [&](const char* tag) -> bool {
uint32_t offset = find_table_offset(data, font_offset, tag);
return offset != 0;
};
// 检查CBDT/CBLC表格 (Google/Android彩色表情)
has_cbdt_table_ = table_exists("CBDT") && table_exists("CBLC");
// 检查sbix表格 (Apple彩色表情)
has_sbix_table_ = table_exists("sbix");
// 检查COLR/CPAL表格 (Microsoft彩色表情)
has_colr_tables_ = table_exists("COLR") && table_exists("CPAL");
// 检查SVG表格 (OpenType SVG彩色表情)
has_svg_table_ = table_exists("SVG ");
// 如果任一彩色表格存在,则标记为支持彩色
has_color_table_ = has_cbdt_table_ || has_sbix_table_ || has_colr_tables_ || has_svg_table_;
}
};

View File

@ -0,0 +1,110 @@
#pragma once
#include <memory>
#include <string>
#include "emoji_detector.h"
#include "font_face.h"
// font_system.h - 字体管理系统
/**
* @class font_manager
* @brief 退
*/
class font_manager {
public:
/**
* @brief
* @param in_font_path
* @param in_font_type (regular, bold, italic, emoji等)
* @return ID
*/
int addFont(const std::string& in_font_path, const std::string& in_font_type = "regular") {
auto font = std::make_shared<font_face_t>();
if (!font->load_from_file(in_font_path)) {
return -1;
}
font->set_font_type(in_font_type);
int font_id = next_font_id_++;
fonts_[font_id] = font;
if (in_font_type == "emoji") {
emoji_font_ids_.push_back(font_id);
}
// 如果是第一个字体,自动设为主字体
if (primary_font_id_ == -1) {
primary_font_id_ = font_id;
}
return font_id;
}
/**
* @brief
* @param in_font_id ID
*/
void set_primary_font(int in_font_id) {
if (fonts_.contains(in_font_id)) {
primary_font_id_ = in_font_id;
}
}
/**
* @brief
* @param in_code_point Unicode码点
* @return
*/
std::shared_ptr<font_face_t> getFontForCodePoint(uint32_t in_code_point) {
// 主字体
auto primary = get_primary_font();
if (primary && primary->has_glyph(in_code_point)) {
return primary;
}
// 对表情使用表情字体
if (emoji_detector::is_emoji(in_code_point)) {
for (int emoji_id : emoji_font_ids_) {
auto emoji_font = fonts_[emoji_id];
if (emoji_font && emoji_font->has_glyph(in_code_point)) {
return emoji_font;
}
}
}
// 尝试所有其他字体
for (const auto& [id, font] : fonts_) {
if (id != primary_font_id_ && font->has_glyph(in_code_point)) {
return font;
}
}
// 回退到主字体
return primary;
}
/**
* @brief
* @return
*/
std::shared_ptr<font_face_t> get_primary_font() {
if (primary_font_id_ != -1 && fonts_.contains(primary_font_id_)) {
return fonts_[primary_font_id_];
}
return nullptr;
}
/**
* @brief
* @return
*/
bool has_color_emoji_font() const {
return !emoji_font_ids_.empty();
}
private:
std::unordered_map<int, std::shared_ptr<font_face_t>> fonts_;
std::vector<int> emoji_font_ids_;
int primary_font_id_ = -1;
int next_font_id_ = 0;
};

View File

@ -0,0 +1,129 @@
#pragma once
#include <vector>
#include "geometry/rect.h"
#include "misc/mirage_type.h"
#include "texture/atlas/texture_atlas_types.h"
class texture2d;
/**
* @enum horizontal_text_alignment_t
* @brief
*/
enum class horizontal_text_alignment_t {
left, ///< 文本左对齐
center, ///< 文本居中对齐
right ///< 文本右对齐
};
/**
* @enum vertical_text_alignment_t
* @brief
*/
enum class vertical_text_alignment_t {
top, ///< 文本顶部对齐
center, ///< 文本居中对齐
bottom ///< 文本底部对齐
};
/**
* @struct glyph_t
* @brief
*/
struct glyph_t {
uint32_t code_point = 0; ///< Unicode码点
Eigen::Vector2f size; ///< 字形尺寸
Eigen::Vector2f bearing; ///< 字形基线相对位置
float advance = 0.0f; ///< 水平前进量
float ascent = 0.0f; ///< 上升高度
float descent = 0.0f; ///< 下降高度
};
/**
* @struct positioned_glyph_t
* @brief
*/
struct positioned_glyph_t {
uint32_t codePoint = 0; ///< Unicode码点
Eigen::Vector2f position; ///< 屏幕位置
rect_t<> bounds; ///< 边界矩形
float advance = 0.0f; ///< 前进量
bool is_emoji = false; ///< 是否为表情符号
std::shared_ptr<texture2d> texture; ///< 字形纹理
atlas_region_t atlas_region; ///< 图集区域
std::vector<uint32_t> emoji_sequence; ///< 表情序列(对表情符号)
/**
* @brief MTSDF字形
* @param in_region
* @param in_mtsdf_texture MTSDF纹理
*/
void set_mtsdf_glyph(const atlas_region_t& in_region, const std::shared_ptr<texture2d>& in_mtsdf_texture) {
atlas_region = in_region;
texture = in_mtsdf_texture;
is_emoji = false;
}
/**
* @brief
* @param in_region
* @param in_emoji_texture
* @param in_sequence
*/
void set_emoji_glyph(const atlas_region_t& in_region, const std::shared_ptr<texture2d>& in_emoji_texture,
const std::vector<uint32_t>& in_sequence) {
atlas_region = in_region;
texture = in_emoji_texture;
is_emoji = true;
emoji_sequence = in_sequence;
}
};
/**
* @struct text_line_t
* @brief
*/
struct text_line_t {
std::vector<positioned_glyph_t> glyphs; ///< 字形数组
float y_position = 0.0f; ///< 行基线Y坐标
float ascent = 0.0f; ///< 行上升高度
float descent = 0.0f; ///< 行下降高度
float width = 0.0f; ///< 行宽度
float height = 0.0f; ///< 行高度
/**
* @brief
* @return
*/
rect_t<> get_bounds() const { return rect_t<>({ 0, y_position - ascent }, { width, ascent + descent }); }
};
/**
* @struct text_paragraph_t
* @brief
*/
struct text_paragraph_t {
std::vector<text_line_t> lines; ///< 段落中的行
flow_direction_t flow_direction; ///< 文本方向
rect_t<> bounds; ///< 段落边界
};
/**
* @struct text_style_t
* @brief
*/
struct text_style_t {
linear_color text_color = { 1, 1, 1, 1 }; ///< 文本颜色
float font_size = 16.0f; ///< 字体大小
float emoji_size = 24.0f; ///< 表情大小
float smoothing = 0.1f; ///< MTSDF平滑值
float character_spacing = 0.0f; ///< 字符间距
float line_spacing = 0.0f; ///< 行间距
float paragraph_spacing = 8.0f; ///< 段落间距
horizontal_text_alignment_t h_align = horizontal_text_alignment_t::left; ///< 水平对齐
vertical_text_alignment_t v_align = vertical_text_alignment_t::top; ///< 垂直对齐
flow_direction_t text_direction = flow_direction_t::left_to_right; ///< 文本方向
bool word_wrap = true; ///< 自动换行
margin_t text_margin = { 0, 0, 0, 0 }; ///< 文本边距
};

View File

@ -0,0 +1,150 @@
#pragma once
#include "msdfgen.h"
#include "stb_truetype.h"
// mtsdf_generator.h - MTSDF生成
/**
* @class mtsdf_generator
* @brief 使msdfgen生成多通道有符号距离场
*/
class mtsdf_generator {
public:
/**
* @brief MTSDF
* @param in_vertices stb_truetype顶点数组
* @param in_num_vertices
* @param in_width
* @param in_height
* @param in_range
* @return RGBA8像素数据
*/
std::vector<uint8_t> generate_mtsdf(
const stbtt_vertex* in_vertices,
size_t in_num_vertices,
int in_width,
int in_height,
float in_range) {
// 创建Shape以存储字形轮廓
msdfgen::Shape shape;
// 处理顶点创建轮廓和边缘
if (in_num_vertices > 0) {
msdfgen::Point2 contour_start;
msdfgen::Point2 current_point;
msdfgen::Contour* current_contour = nullptr;
for (size_t i = 0; i < in_num_vertices; i++) {
const stbtt_vertex& v = in_vertices[i];
switch (v.type) {
case STBTT_vmove:
// 开始新轮廓
current_contour = &shape.addContour();
contour_start = msdfgen::Point2(v.x, v.y);
current_point = contour_start;
break;
case STBTT_vline:
// 添加线段
if (current_contour) {
current_contour->addEdge(msdfgen::EdgeHolder(
new msdfgen::LinearSegment(current_point, msdfgen::Point2(v.x, v.y))));
current_point = msdfgen::Point2(v.x, v.y);
}
break;
case STBTT_vcurve:
// 添加二次曲线段
if (current_contour) {
current_contour->addEdge(msdfgen::EdgeHolder(
new msdfgen::QuadraticSegment(
current_point,
msdfgen::Point2(v.cx, v.cy),
msdfgen::Point2(v.x, v.y)
)));
current_point = msdfgen::Point2(v.x, v.y);
}
break;
case STBTT_vcubic:
// 添加三次曲线段
if (current_contour) {
current_contour->addEdge(msdfgen::EdgeHolder(
new msdfgen::CubicSegment(
current_point,
msdfgen::Point2(v.cx, v.cy),
msdfgen::Point2(v.cx1, v.cy1),
msdfgen::Point2(v.x, v.y)
)));
current_point = msdfgen::Point2(v.x, v.y);
}
break;
default: ;
}
}
// 如果轮廓的最后一点不是起点,则闭合轮廓
if (current_contour && current_point != contour_start) {
current_contour->addEdge(msdfgen::EdgeHolder(new msdfgen::LinearSegment(current_point, contour_start)));
}
}
// 标准化形状(修正方向等)
shape.normalize();
// 创建输出位图4通道浮点
msdfgen::Bitmap<float, 4> bitmap(in_width, in_height);
// 计算边界
double l, b, r, t;
shape.bound(l, b, r, t);
// 计算缩放以适应位图(带内边距)
double padding = 4.0; // 字形周围的像素内边距
double scale_x = (in_width - 2 * padding) / (r - l > 0 ? r - l : 1);
double scale_y = (in_height - 2 * padding) / (t - b > 0 ? t - b : 1);
double scale = std::min(scale_x, scale_y);
// 居中形状
double tx = 0.5*(in_width - scale*(l + r));
double ty = 0.5*(in_height - scale*(b + t));
// 设置错误修正配置
msdfgen::ErrorCorrectionConfig error_correction_config;
// 生成MTSDF - 使用4通道版本
msdfgen::generateMTSDF(bitmap, shape, in_range,
msdfgen::Vector2(scale, scale), msdfgen::Vector2(tx, ty),
error_correction_config, true); // true启用重叠支持
// 转换为RGBA8
std::vector<uint8_t> result(in_width * in_height * 4);
for (int y = 0; y < in_height; y++) {
for (int x = 0; x < in_width; x++) {
const float* pixel = bitmap(x, y);
size_t index = (y * in_width + x) * 4;
// 转换浮点通道到字节
result[index + 0] = msdf_float_to_byte(pixel[0]); // 红色通道
result[index + 1] = msdf_float_to_byte(pixel[1]); // 绿色通道
result[index + 2] = msdf_float_to_byte(pixel[2]); // 蓝色通道
result[index + 3] = msdf_float_to_byte(pixel[3]); // Alpha通道完全不透明
}
}
return result;
}
private:
/**
* @brief MSDF浮点值转换为字节
* @param value [-1, 1]
* @return [0, 255]
*/
static uint8_t msdf_float_to_byte(float value) {
// 将[-1, 1]映射到[0, 255]
const auto byte = static_cast<int>((value * 0.5f + 0.5f) * 255.0f);
return static_cast<uint8_t>(std::max(0, std::min(255, byte)));
}
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@

View File

@ -17,7 +17,7 @@ texture2d_atlas::texture2d_atlas(const Eigen::Vector2i& size, sg_pixel_format pi
tex_data.size = size;
tex_data.pixel_format = pixel_format;
tex_data.data_ptr = nullptr; // 初始为空
texture_.create(tex_data);
texture_->create(tex_data);
// 创建分配器
allocator_ = create_allocator(strategy, size);
@ -81,7 +81,7 @@ bool texture2d_atlas::free_region(int region_id) {
}
void texture2d_atlas::resize(const Eigen::Vector2i& new_size) {
texture_.resize(new_size);
texture_->resize(new_size);
if (allocator_) {
allocator_->resize(new_size);
}
@ -103,16 +103,16 @@ void texture2d_atlas::update_region(const void* data, size_t data_size, const re
// 确保区域在纹理范围内
if (rect.left() < 0 || rect.top() < 0 ||
rect.right() > texture_.get_size().x() ||
rect.bottom() > texture_.get_size().y()) {
rect.right() > texture_->get_size().x() ||
rect.bottom() > texture_->get_size().y()) {
return;
}
// 更新纹理区域
auto map = texture_.map(texture_map_type::write);
auto map = texture_->map(texture_map_type::write);
// 使用texture_map_utils更新区域
texture_map_utils::write_region(*map.get(), data, rect, texture_.get_bytes_per_pixel());
texture_map_utils::write_region(*map.get(), data, rect, texture_->get_bytes_per_pixel());
}
float texture2d_atlas::get_free_space_ratio() const {
@ -141,7 +141,7 @@ std::vector<atlas_region_t> texture2d_atlas::get_allocated_regions() const {
rect_t<float> texture2d_atlas::calculate_uv_rect(const rect_t<int32_t>& rect) const {
// 计算UV坐标 (归一化到0-1范围)
const Eigen::Vector2i texture_size = texture_.get_size();
const Eigen::Vector2i texture_size = texture_->get_size();
float left = static_cast<float>(rect.left()) / texture_size.x();
float top = static_cast<float>(rect.top()) / texture_size.y();
float width = static_cast<float>(rect.width()) / texture_size.x();

View File

@ -64,8 +64,7 @@ public:
/**
* @brief
*/
const rw_texture2d& get_texture() const { return texture_; }
rw_texture2d& get_texture() { return texture_; }
auto get_texture() const { return texture_; }
//-------------- 图集管理 --------------
/**
@ -88,7 +87,7 @@ public:
/**
* @brief
*/
Eigen::Vector2i get_size() const { return texture_.get_size(); }
Eigen::Vector2i get_size() const { return texture_->get_size(); }
/**
* @brief
@ -119,7 +118,7 @@ private:
std::unique_ptr<atlas_allocator> create_allocator(allocation_strategy_t strategy,
const Eigen::Vector2i& size);
rw_texture2d texture_; ///< 底层纹理
std::shared_ptr<rw_texture2d> texture_; ///< 底层纹理
std::unique_ptr<atlas_allocator> allocator_; ///< 区域分配器
std::unordered_map<int, atlas_region_t> allocated_regions_; ///< 存储已分配区域
int next_region_id_ = 1; ///< 区域ID生成器