字体布局
This commit is contained in:
parent
6129e71db7
commit
5aafef4cfb
@ -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>();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
//-------------- 分割操作 --------------
|
||||
|
||||
/**
|
||||
|
@ -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 组件可见性和交互行为的位标志
|
||||
|
@ -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)
|
||||
|
||||
|
88
src/mirage_render/font/emoji_detector.h
Normal file
88
src/mirage_render/font/emoji_detector.h
Normal 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;
|
||||
}
|
||||
};
|
214
src/mirage_render/font/font_atlas_system.h
Normal file
214
src/mirage_render/font/font_atlas_system.h
Normal 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();
|
||||
}
|
||||
};
|
385
src/mirage_render/font/font_face.h
Normal file
385
src/mirage_render/font/font_face.h
Normal 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_;
|
||||
}
|
||||
};
|
110
src/mirage_render/font/font_system.h
Normal file
110
src/mirage_render/font/font_system.h
Normal 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;
|
||||
};
|
129
src/mirage_render/font/font_type.h
Normal file
129
src/mirage_render/font/font_type.h
Normal 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 }; ///< 文本边距
|
||||
};
|
150
src/mirage_render/font/mtsdf_generator.h
Normal file
150
src/mirage_render/font/mtsdf_generator.h
Normal 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)));
|
||||
}
|
||||
};
|
5079
src/mirage_render/font/stb_truetype.h
Normal file
5079
src/mirage_render/font/stb_truetype.h
Normal file
File diff suppressed because it is too large
Load Diff
1
src/mirage_render/font/stb_truetype_impl.cpp
Normal file
1
src/mirage_render/font/stb_truetype_impl.cpp
Normal file
@ -0,0 +1 @@
|
||||
|
@ -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();
|
||||
|
@ -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生成器
|
||||
|
Loading…
x
Reference in New Issue
Block a user