彩色表情绘制第一版
This commit is contained in:
parent
727457b9f7
commit
f200fcba4a
1
.gitignore
vendored
1
.gitignore
vendored
@ -14,3 +14,4 @@ fips-files/deploy/
|
||||
CMakeUserPresets.json
|
||||
#<fips
|
||||
/tools/shader_path.txt
|
||||
/cache
|
||||
|
@ -10,9 +10,11 @@ int main(int argc, char* argv[]) {
|
||||
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");
|
||||
manager.add_font(L"C:/Windows/Fonts/seguiemj.ttf");
|
||||
|
||||
auto text_block = std::make_shared<mtext_block>();
|
||||
const auto& text_block = std::make_shared<mtext_block>();
|
||||
text_block->set_text(U"Hello, World! 你好,世界!😀");
|
||||
// text_block->set_text(U"😀");
|
||||
|
||||
const auto& window = mwindow::create({ 1024, 1024 }, L"Hello, World!");
|
||||
window->set_content(text_block);
|
||||
|
90
src/mirage_render/font/atlas/bitmap_glyph_atlas.h
Normal file
90
src/mirage_render/font/atlas/bitmap_glyph_atlas.h
Normal file
@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
#include "font_atlas.h"
|
||||
|
||||
/**
|
||||
* @class bitmap_glyph_atlas
|
||||
* @brief 位图字形图集管理器
|
||||
*
|
||||
* 管理普通字形的位图纹理图集,使用单通道(R8)格式存储字形位图。
|
||||
* 支持从字体中提取字形位图并将其添加到图集中。
|
||||
*/
|
||||
class bitmap_glyph_atlas : public atlas_base {
|
||||
public:
|
||||
/**
|
||||
* @brief 初始化位图字形图集
|
||||
*
|
||||
* @param in_atlas_size 图集的像素尺寸
|
||||
* @return 初始化是否成功
|
||||
*/
|
||||
bool initialize(const Eigen::Vector2i& in_atlas_size) override {
|
||||
return initialize_atlas(in_atlas_size, SG_PIXELFORMAT_R8);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取或创建字形的纹理区域
|
||||
*
|
||||
* 尝试从缓存中获取字形的纹理区域,如果不存在则从字体中提取位图并添加到图集中。
|
||||
*
|
||||
* @param in_glyph_index 字形的Unicode码点或字体特定索引
|
||||
* @param in_font 用于渲染字形的字体
|
||||
* @param in_font_size 字体大小
|
||||
* @return 操作结果,包含成功/失败状态和区域信息
|
||||
*/
|
||||
glyph_atlas_result_t get_or_create_glyph(
|
||||
int32_t in_glyph_index,
|
||||
const font_face_t& in_font,
|
||||
float in_font_size) {
|
||||
|
||||
glyph_atlas_result_t result{};
|
||||
|
||||
// 创建缓存键
|
||||
const auto& cache_key = create_cache_key(in_glyph_index, in_font.get_font_name(), in_font_size);
|
||||
|
||||
// 检查缓存,如果已存在则直接返回
|
||||
auto cached_result = find_in_cache(cache_key);
|
||||
if (cached_result.reason == glyph_atlas_reason_t::success) {
|
||||
return cached_result;
|
||||
}
|
||||
|
||||
// 获取字形形状 - 使用指定字体大小
|
||||
auto glyph_shape = in_font.get_glyph_shape_by_index(in_glyph_index, in_font_size);
|
||||
if (glyph_shape.empty()) {
|
||||
result.reason = glyph_atlas_reason_t::glyph_not_found;
|
||||
return result;
|
||||
}
|
||||
|
||||
// 使用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_glyph_index,
|
||||
&width, &height, &xoff, &yoff);
|
||||
|
||||
if (!bitmap || width == 0 || height == 0) {
|
||||
result.reason = glyph_atlas_reason_t::glyph_not_found;
|
||||
if (bitmap) {
|
||||
stbtt_FreeBitmap(bitmap, nullptr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 从图集分配空间,添加2像素的padding防止纹理采样时的边缘混合问题
|
||||
auto region = atlas_->allocate_region({ width, height }, { 2, 2 });
|
||||
if (!region) {
|
||||
stbtt_FreeBitmap(bitmap, nullptr);
|
||||
result.reason = glyph_atlas_reason_t::atlas_full;
|
||||
return result;
|
||||
}
|
||||
|
||||
// 更新图集,将位图数据上传到纹理
|
||||
atlas_->update_region(bitmap, width * height, region->rect);
|
||||
stbtt_FreeBitmap(bitmap, nullptr);
|
||||
|
||||
// 缓存结果以便未来复用
|
||||
add_to_cache(cache_key, *region);
|
||||
|
||||
result.reason = glyph_atlas_reason_t::success;
|
||||
result.region = *region;
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
75
src/mirage_render/font/atlas/color_emoji_atlas.h
Normal file
75
src/mirage_render/font/atlas/color_emoji_atlas.h
Normal file
@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
#include "font_atlas.h"
|
||||
|
||||
/**
|
||||
* @class color_emoji_atlas
|
||||
* @brief 彩色表情符号图集管理器
|
||||
*
|
||||
* 管理彩色表情符号的纹理图集,支持从字体中提取彩色表情符号的位图,
|
||||
* 并将其存储在RGBA格式的纹理图集中。
|
||||
*/
|
||||
class color_emoji_atlas : public atlas_base {
|
||||
public:
|
||||
/**
|
||||
* @brief 初始化表情符号图集
|
||||
*
|
||||
* @param in_atlas_size 图集的像素尺寸
|
||||
* @return 初始化是否成功
|
||||
*/
|
||||
bool initialize(const Eigen::Vector2i& in_atlas_size) override {
|
||||
return initialize_atlas(in_atlas_size, SG_PIXELFORMAT_RGBA8);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取或创建表情符号的纹理区域
|
||||
*
|
||||
* 尝试从缓存中获取表情符号的纹理区域,如果不存在则从字体中提取位图并添加到图集中。
|
||||
*
|
||||
* @param in_glyph_index 表情符号的码点或字体特定索引
|
||||
* @param in_font 用于渲染表情符号的字体
|
||||
* @param in_font_size 字体大小
|
||||
* @return 操作结果,包含成功/失败状态和区域信息
|
||||
*/
|
||||
glyph_atlas_result_t get_or_create_emoji(
|
||||
int32_t in_glyph_index,
|
||||
const font_face_t& in_font,
|
||||
float in_font_size) {
|
||||
|
||||
glyph_atlas_result_t result{};
|
||||
|
||||
// 创建缓存键
|
||||
const auto& cache_key = create_cache_key(in_glyph_index, in_font.get_font_name(), in_font_size);
|
||||
|
||||
// 检查缓存
|
||||
auto cached_result = find_in_cache(cache_key);
|
||||
if (cached_result.reason == glyph_atlas_reason_t::success) {
|
||||
return cached_result;
|
||||
}
|
||||
|
||||
// 获取彩色表情位图
|
||||
auto bitmap_opt = in_font.get_color_emoji_bitmap(in_glyph_index, in_font_size);
|
||||
if (!bitmap_opt) {
|
||||
result.reason = glyph_atlas_reason_t::glyph_not_found;
|
||||
return result;
|
||||
}
|
||||
|
||||
const auto& bitmap = *bitmap_opt;
|
||||
|
||||
// 从图集分配空间,添加2像素的padding防止纹理采样时的边缘混合问题
|
||||
const auto& region = atlas_->allocate_region({ bitmap.width, bitmap.height }, { 2, 2 });
|
||||
if (!region) {
|
||||
result.reason = glyph_atlas_reason_t::atlas_full;
|
||||
return result;
|
||||
}
|
||||
|
||||
// 更新图集,将位图数据上传到纹理
|
||||
atlas_->update_region(bitmap.data.data(), bitmap.data.size(), region->rect);
|
||||
|
||||
// 缓存结果以便未来复用
|
||||
add_to_cache(cache_key, *region);
|
||||
|
||||
result.reason = glyph_atlas_reason_t::success;
|
||||
result.region = *region;
|
||||
return result;
|
||||
}
|
||||
};
|
149
src/mirage_render/font/atlas/font_atlas.h
Normal file
149
src/mirage_render/font/atlas/font_atlas.h
Normal file
@ -0,0 +1,149 @@
|
||||
#pragma once
|
||||
|
||||
#include <Eigen/Eigen>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "font/font_face.h"
|
||||
#include "texture/atlas/texture2d_atlas.h"
|
||||
|
||||
/**
|
||||
* @enum glyph_atlas_reason_t
|
||||
* @brief 图集操作结果原因枚举
|
||||
*
|
||||
* 用于标识字形图集操作的结果状态,便于调用者了解操作失败的具体原因。
|
||||
*/
|
||||
enum class glyph_atlas_reason_t {
|
||||
success, ///< 操作成功完成
|
||||
glyph_not_found, ///< 未找到请求的字形
|
||||
atlas_full, ///< 图集空间已满,无法分配更多区域
|
||||
unknown ///< 未知原因导致的失败
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct glyph_atlas_result_t
|
||||
* @brief 图集操作结果结构体
|
||||
*
|
||||
* 封装了字形图集操作的完整结果信息,包括操作状态和分配的区域。
|
||||
*/
|
||||
struct glyph_atlas_result_t {
|
||||
glyph_atlas_reason_t reason = glyph_atlas_reason_t::unknown; ///< 操作结果原因
|
||||
atlas_region_t region; ///< 分配的区域信息(仅当操作成功时有效)
|
||||
};
|
||||
|
||||
/**
|
||||
* @class atlas_base
|
||||
* @brief 字形图集管理基类
|
||||
*
|
||||
* 提供字形图集管理的通用功能,包括图集初始化、区域分配和缓存管理。
|
||||
* 派生类需要实现具体的字形渲染和图集更新逻辑。
|
||||
*/
|
||||
class atlas_base {
|
||||
protected:
|
||||
std::shared_ptr<texture2d_atlas> atlas_; ///< 底层纹理图集对象
|
||||
std::unordered_map<std::string, atlas_region_t> cached_regions_; ///< 已缓存的区域映射表
|
||||
|
||||
/**
|
||||
* @brief 为字形创建唯一的缓存键
|
||||
*
|
||||
* @param in_glyph_index 字形的Unicode码点或字体特定索引
|
||||
* @param in_font_name 字体名称
|
||||
* @param in_font_size 字体大小
|
||||
* @return 唯一标识字形的缓存键字符串
|
||||
*/
|
||||
static std::string create_cache_key(uint32_t in_glyph_index, const std::string& in_font_name, float in_font_size) {
|
||||
std::stringstream ss;
|
||||
ss << std::hex << in_glyph_index << "_" << in_font_name << "_" << std::fixed
|
||||
<< std::setprecision(1) << in_font_size;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化底层纹理图集
|
||||
*
|
||||
* @param in_atlas_size 图集的像素尺寸
|
||||
* @param in_pixel_format 图集的像素格式
|
||||
* @return 初始化是否成功
|
||||
*/
|
||||
bool initialize_atlas(const Eigen::Vector2i& in_atlas_size, sg_pixel_format in_pixel_format) {
|
||||
atlas_ = std::make_shared<texture2d_atlas>(
|
||||
in_atlas_size,
|
||||
in_pixel_format,
|
||||
allocation_strategy_t::bin_packing // 使用二叉树装箱算法策略
|
||||
);
|
||||
|
||||
return atlas_ != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从缓存中查找字形区域
|
||||
*
|
||||
* @param in_cache_key 缓存键
|
||||
* @return 查找结果,包含成功/失败状态和区域信息
|
||||
*/
|
||||
glyph_atlas_result_t find_in_cache(const std::string& in_cache_key) {
|
||||
glyph_atlas_result_t result{};
|
||||
|
||||
auto it = cached_regions_.find(in_cache_key);
|
||||
if (it != cached_regions_.end()) {
|
||||
result.reason = glyph_atlas_reason_t::success;
|
||||
result.region = it->second;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将区域信息添加到缓存
|
||||
*
|
||||
* @param in_cache_key 缓存键
|
||||
* @param in_region 要缓存的区域
|
||||
*/
|
||||
void add_to_cache(const std::string& in_cache_key, const atlas_region_t& in_region) {
|
||||
cached_regions_[in_cache_key] = in_region;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 虚析构函数
|
||||
*
|
||||
* 确保正确释放派生类资源
|
||||
*/
|
||||
virtual ~atlas_base() = default;
|
||||
|
||||
/**
|
||||
* @brief 初始化图集
|
||||
*
|
||||
* @param in_atlas_size 图集的像素尺寸
|
||||
* @return 初始化是否成功
|
||||
*/
|
||||
virtual bool initialize(const Eigen::Vector2i& in_atlas_size) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取图集底层纹理
|
||||
*
|
||||
* @return 图集纹理对象
|
||||
*/
|
||||
[[nodiscard]] auto get_texture() const {
|
||||
return atlas_->get_texture();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取已缓存区域的数量
|
||||
*
|
||||
* @return 缓存的区域数量
|
||||
*/
|
||||
[[nodiscard]] size_t get_cached_regions_count() const {
|
||||
return cached_regions_.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除所有缓存的区域
|
||||
*/
|
||||
void clear_cache() {
|
||||
cached_regions_.clear();
|
||||
}
|
||||
};
|
71
src/mirage_render/font/atlas/font_atlas_manager.cpp
Normal file
71
src/mirage_render/font/atlas/font_atlas_manager.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include "font_atlas_manager.h"
|
||||
|
||||
std::optional<atlas_region_t> font_atlas_manager::get_or_create_glyph(
|
||||
int32_t in_glyph_id,
|
||||
const font_face_t& in_font,
|
||||
float in_font_size,
|
||||
bool is_emoji)
|
||||
{
|
||||
// 获取字形度量信息
|
||||
const auto& glyph = in_font.get_glyph_by_index(in_glyph_id, in_font_size);
|
||||
if (!glyph) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
glyph_atlas_result_t result{};
|
||||
result.reason = glyph_atlas_reason_t::atlas_full;
|
||||
|
||||
// **处理表情符号**
|
||||
if (is_emoji) {
|
||||
// 尝试从现有表情图集中获取
|
||||
for (auto& atlas : emoji_atlases_) {
|
||||
result = atlas.get_or_create_emoji(in_glyph_id, in_font, in_font_size);
|
||||
if (result.reason == glyph_atlas_reason_t::success) {
|
||||
return result.region;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果所有现有图集都已满,创建新的表情图集
|
||||
if (result.reason == glyph_atlas_reason_t::atlas_full) {
|
||||
auto& atlas = create_new_emoji_atlas();
|
||||
result = atlas.get_or_create_emoji(in_glyph_id, in_font, in_font_size);
|
||||
if (result.reason == glyph_atlas_reason_t::success) {
|
||||
return result.region;
|
||||
}
|
||||
}
|
||||
}
|
||||
// **处理普通字形**
|
||||
else {
|
||||
// 尝试从现有字形图集中获取
|
||||
for (auto& atlas : glyph_atlases_) {
|
||||
result = atlas.get_or_create_glyph(in_glyph_id, in_font, in_font_size);
|
||||
if (result.reason == glyph_atlas_reason_t::success) {
|
||||
return result.region;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果所有现有图集都已满,创建新的字形图集
|
||||
if (result.reason == glyph_atlas_reason_t::atlas_full) {
|
||||
auto& atlas = create_new_glyph_atlas();
|
||||
result = atlas.get_or_create_glyph(in_glyph_id, in_font, in_font_size);
|
||||
if (result.reason == glyph_atlas_reason_t::success) {
|
||||
return result.region;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果所有尝试都失败,返回空
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bitmap_glyph_atlas& font_atlas_manager::create_new_glyph_atlas(const Eigen::Vector2i& atlas_size) {
|
||||
auto& atlas = glyph_atlases_.emplace_back();
|
||||
atlas.initialize(atlas_size);
|
||||
return atlas;
|
||||
}
|
||||
|
||||
color_emoji_atlas& font_atlas_manager::create_new_emoji_atlas(const Eigen::Vector2i& atlas_size) {
|
||||
auto& atlas = emoji_atlases_.emplace_back();
|
||||
atlas.initialize(atlas_size);
|
||||
return atlas;
|
||||
}
|
81
src/mirage_render/font/atlas/font_atlas_manager.h
Normal file
81
src/mirage_render/font/atlas/font_atlas_manager.h
Normal file
@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <Eigen/Eigen>
|
||||
|
||||
#include "bitmap_glyph_atlas.h"
|
||||
#include "color_emoji_atlas.h"
|
||||
#include "font/font_face.h"
|
||||
|
||||
/**
|
||||
* @class font_atlas_manager
|
||||
* @brief 图集管理器 - 负责管理字形和表情符号的纹理图集
|
||||
*
|
||||
* 集中处理所有字形和表情符号的图集分配、创建和访问,
|
||||
* 使字体管理系统与底层纹理管理解耦。
|
||||
*/
|
||||
class font_atlas_manager {
|
||||
public:
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
font_atlas_manager() = default;
|
||||
|
||||
/**
|
||||
* @brief 获取或创建字形在图集中的区域
|
||||
*
|
||||
* @param in_glyph_id 字形ID
|
||||
* @param in_font 字体引用
|
||||
* @param in_font_size 字体大小
|
||||
* @param is_emoji 是否为表情符号
|
||||
* @return 字形在图集中的区域,失败返回nullopt
|
||||
*/
|
||||
std::optional<atlas_region_t> get_or_create_glyph(
|
||||
int32_t in_glyph_id,
|
||||
const font_face_t& in_font,
|
||||
float in_font_size,
|
||||
bool is_emoji);
|
||||
|
||||
/**
|
||||
* @brief 获取所有字形图集
|
||||
* @return 字形图集列表的常量引用
|
||||
*/
|
||||
[[nodiscard]] const auto& get_glyph_atlases() const {
|
||||
return glyph_atlases_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取所有表情图集
|
||||
* @return 表情图集列表的常量引用
|
||||
*/
|
||||
[[nodiscard]] const auto& get_emoji_atlases() const {
|
||||
return emoji_atlases_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除所有图集
|
||||
*/
|
||||
void clear() {
|
||||
glyph_atlases_.clear();
|
||||
emoji_atlases_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<bitmap_glyph_atlas> glyph_atlases_; ///< 普通字形图集列表
|
||||
std::vector<color_emoji_atlas> emoji_atlases_; ///< 表情符号图集列表
|
||||
|
||||
/**
|
||||
* @brief 创建新的字形图集
|
||||
* @param atlas_size 图集大小
|
||||
* @return 新创建的图集引用
|
||||
*/
|
||||
bitmap_glyph_atlas& create_new_glyph_atlas(const Eigen::Vector2i& atlas_size = {1024, 1024});
|
||||
|
||||
/**
|
||||
* @brief 创建新的表情图集
|
||||
* @param atlas_size 图集大小
|
||||
* @return 新创建的图集引用
|
||||
*/
|
||||
color_emoji_atlas& create_new_emoji_atlas(const Eigen::Vector2i& atlas_size = {1024, 1024});
|
||||
};
|
171
src/mirage_render/font/colr_cpal_emoji.h
Normal file
171
src/mirage_render/font/colr_cpal_emoji.h
Normal file
@ -0,0 +1,171 @@
|
||||
#pragma once
|
||||
#include "stb_truetype.h"
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "font_type.h"
|
||||
|
||||
class font_face_t;
|
||||
// 辅助函数:读取16位和32位无符号整数
|
||||
inline uint16_t read_u16(const uint8_t* data) {
|
||||
return (data[0] << 8) | data[1];
|
||||
}
|
||||
|
||||
inline uint32_t read_u32(const uint8_t* data) {
|
||||
return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
|
||||
}
|
||||
|
||||
// 读取COLR/CPAL彩色表情符号
|
||||
inline bool load_color_emoji(const std::shared_ptr<font_face_t>& in_font, uint32_t glyph_index, int size, color_emoji_bitmap_t& bitmap) {
|
||||
// 查找COLR和CPAL表
|
||||
uint32_t colr_offset, colr_length;
|
||||
uint32_t cpal_offset, cpal_length;
|
||||
|
||||
if (!find_table(font_data.data(), "COLR", colr_offset, colr_length) ||
|
||||
!find_table(font_data.data(), "CPAL", cpal_offset, cpal_length)) {
|
||||
std::cerr << "COLR or CPAL table not found" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 解析COLR表
|
||||
const uint8_t* colr_table = font_data.data() + colr_offset;
|
||||
uint16_t colr_version = read_u16(colr_table);
|
||||
uint16_t num_base_glyphs = read_u16(colr_table + 2);
|
||||
uint32_t base_glyph_records_offset = read_u32(colr_table + 4);
|
||||
uint32_t layer_records_offset = read_u32(colr_table + 8);
|
||||
uint16_t num_layers = read_u16(colr_table + 12);
|
||||
|
||||
// 查找基本字形记录
|
||||
const uint8_t* base_records = colr_table + base_glyph_records_offset;
|
||||
int target_record_index = -1;
|
||||
|
||||
for (uint16_t i = 0; i < num_base_glyphs; i++) {
|
||||
const uint8_t* record = base_records + i * 6;
|
||||
uint16_t base_glyph_id = read_u16(record);
|
||||
|
||||
if (base_glyph_id == glyph_index) {
|
||||
target_record_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (target_record_index == -1) {
|
||||
std::cerr << "Glyph not found in COLR table" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取图层信息
|
||||
const uint8_t* target_record = base_records + target_record_index * 6;
|
||||
uint16_t first_layer_index = read_u16(target_record + 2);
|
||||
uint16_t num_layers_for_glyph = read_u16(target_record + 4);
|
||||
|
||||
// 解析CPAL表
|
||||
const uint8_t* cpal_table = font_data.data() + cpal_offset;
|
||||
uint16_t num_palette_entries = read_u16(cpal_table + 2);
|
||||
uint16_t num_palettes = read_u16(cpal_table + 4);
|
||||
uint32_t color_records_offset = read_u32(cpal_table + 8);
|
||||
|
||||
// 使用第一个调色板
|
||||
const uint8_t* palette_indices = cpal_table + 12;
|
||||
uint16_t first_color_index = read_u16(palette_indices);
|
||||
|
||||
// 计算缩放比例
|
||||
float scale = stbtt_ScaleForPixelHeight(&font, size);
|
||||
|
||||
// 获取字形边界框
|
||||
int x0, y0, x1, y1;
|
||||
stbtt_GetGlyphBBox(&font, glyph_index, &x0, &y0, &x1, &y1);
|
||||
|
||||
// 计算位图尺寸
|
||||
int scaled_x0 = (int)(x0 * scale);
|
||||
int scaled_y0 = (int)(y0 * scale);
|
||||
int scaled_x1 = (int)(x1 * scale);
|
||||
int scaled_y1 = (int)(y1 * scale);
|
||||
|
||||
bitmap.width = scaled_x1 - scaled_x0;
|
||||
bitmap.height = scaled_y1 - scaled_y0;
|
||||
|
||||
// 确保位图尺寸有效
|
||||
if (bitmap.width <= 0 || bitmap.height <= 0) {
|
||||
bitmap.width = size;
|
||||
bitmap.height = size;
|
||||
}
|
||||
|
||||
// 初始化RGBA位图数据
|
||||
bitmap.data.resize(bitmap.width * bitmap.height * 4, 0);
|
||||
|
||||
// 渲染每一个图层
|
||||
const uint8_t* layer_records = colr_table + layer_records_offset;
|
||||
const uint8_t* color_records = cpal_table + color_records_offset;
|
||||
|
||||
for (uint16_t i = 0; i < num_layers_for_glyph; i++) {
|
||||
const uint8_t* layer = layer_records + (first_layer_index + i) * 4;
|
||||
uint16_t layer_glyph_id = read_u16(layer);
|
||||
uint16_t palette_entry_index = read_u16(layer + 2);
|
||||
|
||||
// 获取颜色
|
||||
uint16_t color_index = first_color_index + palette_entry_index;
|
||||
const uint8_t* color = color_records + color_index * 4;
|
||||
uint8_t blue = color[0];
|
||||
uint8_t green = color[1];
|
||||
uint8_t red = color[2];
|
||||
uint8_t alpha = color[3];
|
||||
|
||||
// 为图层创建临时位图
|
||||
int layer_x0, layer_y0, layer_x1, layer_y1;
|
||||
stbtt_GetGlyphBitmapBox(&font, layer_glyph_id, scale, scale,
|
||||
&layer_x0, &layer_y0, &layer_x1, &layer_y1);
|
||||
|
||||
int layer_width = layer_x1 - layer_x0;
|
||||
int layer_height = layer_y1 - layer_y0;
|
||||
|
||||
if (layer_width <= 0 || layer_height <= 0)
|
||||
continue;
|
||||
|
||||
// 渲染图层字形
|
||||
std::vector<uint8_t> layer_bitmap(layer_width * layer_height);
|
||||
stbtt_MakeGlyphBitmap(&font, layer_bitmap.data(),
|
||||
layer_width, layer_height, layer_width,
|
||||
scale, scale, layer_glyph_id);
|
||||
|
||||
// 计算图层在最终位图中的位置
|
||||
int offset_x = layer_x0 - scaled_x0;
|
||||
int offset_y = layer_y0 - scaled_y0;
|
||||
|
||||
// 将图层与颜色混合到最终位图
|
||||
for (int y = 0; y < layer_height; y++) {
|
||||
int dst_y = offset_y + y;
|
||||
if (dst_y < 0 || dst_y >= bitmap.height)
|
||||
continue;
|
||||
|
||||
for (int x = 0; x < layer_width; x++) {
|
||||
int dst_x = offset_x + x;
|
||||
if (dst_x < 0 || dst_x >= bitmap.width)
|
||||
continue;
|
||||
|
||||
uint8_t coverage = layer_bitmap[y * layer_width + x];
|
||||
if (coverage == 0)
|
||||
continue;
|
||||
|
||||
int dst_idx = (dst_y * bitmap.width + dst_x) * 4;
|
||||
|
||||
// Alpha混合
|
||||
float src_alpha = (coverage / 255.0f) * (alpha / 255.0f);
|
||||
float dst_alpha = bitmap.data[dst_idx + 3] / 255.0f;
|
||||
float out_alpha = src_alpha + dst_alpha * (1.0f - src_alpha);
|
||||
|
||||
if (out_alpha > 0) {
|
||||
bitmap.data[dst_idx + 0] = (uint8_t)((red * src_alpha + bitmap.data[dst_idx + 0] * dst_alpha * (1.0f - src_alpha)) / out_alpha);
|
||||
bitmap.data[dst_idx + 1] = (uint8_t)((green * src_alpha + bitmap.data[dst_idx + 1] * dst_alpha * (1.0f - src_alpha)) / out_alpha);
|
||||
bitmap.data[dst_idx + 2] = (uint8_t)((blue * src_alpha + bitmap.data[dst_idx + 2] * dst_alpha * (1.0f - src_alpha)) / out_alpha);
|
||||
}
|
||||
|
||||
bitmap.data[dst_idx + 3] = (uint8_t)(out_alpha * 255.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -1,218 +0,0 @@
|
||||
#pragma once
|
||||
// font_atlas_system.h - 字形与表情图集管理
|
||||
|
||||
#include <Eigen/Eigen>
|
||||
|
||||
#include "font_face.h"
|
||||
#include "texture/atlas/texture2d_atlas.h"
|
||||
|
||||
enum class glyph_atlas_reason_t {
|
||||
success,
|
||||
glyph_not_found,
|
||||
atlas_full,
|
||||
unknown
|
||||
};
|
||||
|
||||
struct glyph_atlas_result_t {
|
||||
glyph_atlas_reason_t reason;
|
||||
atlas_region_t region;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class mtsdf_glyph_atlas
|
||||
* @brief 管理MTSDF字形图集
|
||||
*/
|
||||
class mtsdf_glyph_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_R8,
|
||||
allocation_strategy_t::bin_packing
|
||||
);
|
||||
|
||||
return atlas_ != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取或创建MTSDF字形
|
||||
* @param in_glygh_index Unicode码点
|
||||
* @param in_font 字体
|
||||
* @param in_font_size 字体大小
|
||||
* @return 可选的图集区域
|
||||
*/
|
||||
glyph_atlas_result_t get_or_create_glyph(
|
||||
int32_t in_glygh_index,
|
||||
const font_face_t& in_font,
|
||||
float in_font_size) {
|
||||
|
||||
glyph_atlas_result_t result{};
|
||||
|
||||
// 创建缓存键
|
||||
const auto& cache_key = create_glyph_key(in_glygh_index, in_font.get_font_name(), in_font_size);
|
||||
|
||||
// 检查缓存
|
||||
auto it = cached_glyphs_.find(cache_key);
|
||||
if (it != cached_glyphs_.end()) {
|
||||
result.reason = glyph_atlas_reason_t::success;
|
||||
result.region = it->second;
|
||||
return result;
|
||||
}
|
||||
|
||||
// 获取字形形状 - 传递字体大小
|
||||
auto glyph_shape = in_font.get_glyph_shape_by_index(in_glygh_index, in_font_size);
|
||||
if (glyph_shape.empty()) {
|
||||
result.reason = glyph_atlas_reason_t::glyph_not_found;
|
||||
return result;
|
||||
}
|
||||
|
||||
// 使用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 }, { 2, 2 });
|
||||
if (!region) {
|
||||
result.reason = glyph_atlas_reason_t::atlas_full;
|
||||
return result;
|
||||
}
|
||||
|
||||
// 更新图集
|
||||
atlas_->update_region(bitmap, width * height, region->rect);
|
||||
|
||||
stbtt_FreeBitmap(bitmap, nullptr);
|
||||
|
||||
// 缓存结果
|
||||
cached_glyphs_[cache_key] = *region;
|
||||
|
||||
result.reason = glyph_atlas_reason_t::success;
|
||||
result.region = *region;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取图集纹理
|
||||
* @return 图集纹理
|
||||
*/
|
||||
[[nodiscard]] 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_glygh_index Unicode码点
|
||||
* @param in_font_name 字体名称
|
||||
* @param in_font_size 字体大小
|
||||
* @return 缓存键
|
||||
*/
|
||||
static std::string create_glyph_key(uint32_t in_glygh_index, const std::string& in_font_name, float in_font_size) {
|
||||
std::stringstream ss;
|
||||
ss << std::hex << in_glygh_index << "_" << 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<int32_t>& in_emoji_sequence,
|
||||
const 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;
|
||||
|
||||
// 从图集分配空间
|
||||
const auto& region = atlas_->allocate_region({ bitmap.width, bitmap.height }, { 2, 2 });
|
||||
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 图集纹理
|
||||
*/
|
||||
[[nodiscard]] 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 缓存键
|
||||
*/
|
||||
static std::string create_emoji_key(const std::vector<int32_t>& in_sequence, float in_font_size) {
|
||||
std::stringstream ss;
|
||||
for (const int32_t code : in_sequence) {
|
||||
ss << std::hex << code << "_";
|
||||
}
|
||||
ss << std::fixed << std::setprecision(1) << in_font_size;
|
||||
return ss.str();
|
||||
}
|
||||
};
|
@ -121,29 +121,162 @@ float font_face_t::get_scale_for_pixel_height(float in_font_size) const {
|
||||
return stbtt_ScaleForPixelHeight(&font_info_, in_font_size);
|
||||
}
|
||||
|
||||
std::optional<color_emoji_bitmap_t> font_face_t::get_color_emoji_bitmap(
|
||||
const std::vector<int32_t>& in_emoji_sequence, float in_font_size) const {
|
||||
|
||||
if (!supports_color_emoji() || in_emoji_sequence.empty()) {
|
||||
std::optional<color_emoji_bitmap_t> font_face_t::get_color_emoji_bitmap(int32_t glyph_index, float in_font_size) const {
|
||||
if (!supports_color_emoji()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// 获取主要码点的字形索引
|
||||
int32_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_t 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);
|
||||
// TODO 读取彩色位图数据
|
||||
|
||||
return get_colr_bitmap(glyph_index, in_font_size);
|
||||
}
|
||||
|
||||
std::optional<color_emoji_bitmap_t> font_face_t::get_colr_bitmap(int32_t in_glyph_index, float in_font_size) const {
|
||||
const auto* data = static_cast<const uint8_t*>(font_data_->get_data());
|
||||
int font_offset = stbtt_GetFontOffsetForIndex(data, 0);
|
||||
|
||||
auto colr_offset = find_table_offset(data, font_offset, "COLR");
|
||||
auto cpal_offset = find_table_offset(data, font_offset, "CPAL");
|
||||
if (colr_offset == 0 || cpal_offset == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const uint8_t* colr_data = data + colr_offset;
|
||||
const uint8_t* cpal_data = data + cpal_offset;
|
||||
|
||||
uint16_t colr_version = read_u16(colr_data);
|
||||
uint16_t num_base_glyphs = read_u16(colr_data + 2);
|
||||
uint32_t base_glyph_records_offset = read_u32(colr_data + 4);
|
||||
uint32_t layer_records_offset = read_u32(colr_data + 8);
|
||||
uint16_t num_layers = read_u16(colr_data + 12);
|
||||
|
||||
// 查找基本字形记录
|
||||
const uint8_t* base_records = colr_data + base_glyph_records_offset;
|
||||
int target_record_index = -1;
|
||||
|
||||
for (uint16_t i = 0; i < num_base_glyphs; i++) {
|
||||
const uint8_t* record = base_records + i * 6;
|
||||
const uint16_t base_glyph_id = read_u16(record);
|
||||
|
||||
if (base_glyph_id == in_glyph_index) {
|
||||
target_record_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (target_record_index == -1) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// 获取图层信息
|
||||
const uint8_t* target_record = base_records + target_record_index * 6;
|
||||
const uint16_t first_layer_index = read_u16(target_record + 2);
|
||||
const uint16_t num_layers_for_glyph = read_u16(target_record + 4);
|
||||
|
||||
// 解析CPAL表
|
||||
uint16_t num_palette_entries = read_u16(cpal_data + 2);
|
||||
uint16_t num_palettes = read_u16(cpal_data + 4);
|
||||
uint32_t color_records_offset = read_u32(cpal_data + 8);
|
||||
|
||||
// 使用第一个调色板
|
||||
const uint8_t* palette_indices = cpal_data + 12;
|
||||
const uint16_t first_color_index = read_u16(palette_indices);
|
||||
|
||||
// 计算缩放比例
|
||||
const float scale = stbtt_ScaleForPixelHeight(&font_info_, in_font_size);
|
||||
|
||||
// 获取字形边界框
|
||||
int x0, y0, x1, y1;
|
||||
stbtt_GetGlyphBitmapBox(&font_info_, in_glyph_index, scale, scale, &x0, &y0, &x1, &y1);
|
||||
|
||||
color_emoji_bitmap_t bitmap;
|
||||
bitmap.width = x1 - x0;
|
||||
bitmap.height = y1 - y0;
|
||||
|
||||
// 确保位图尺寸有效
|
||||
if (bitmap.width <= 0 || bitmap.height <= 0) {
|
||||
bitmap.width = 64;
|
||||
bitmap.height = 64;
|
||||
}
|
||||
|
||||
// 初始化RGBA位图数据
|
||||
bitmap.data.resize(bitmap.width * bitmap.height * 4, 0);
|
||||
|
||||
// 渲染每一个图层
|
||||
const uint8_t* layer_records = colr_data + layer_records_offset;
|
||||
const uint8_t* color_records = cpal_data + color_records_offset;
|
||||
|
||||
for (uint16_t i = 0; i < num_layers_for_glyph; i++) {
|
||||
const uint8_t* layer = layer_records + (first_layer_index + i) * 4;
|
||||
uint16_t layer_glyph_id = read_u16(layer);
|
||||
uint16_t palette_entry_index = read_u16(layer + 2);
|
||||
|
||||
// 获取颜色
|
||||
uint16_t color_index = first_color_index + palette_entry_index;
|
||||
const uint8_t* color = color_records + color_index * 4;
|
||||
uint8_t blue = color[0];
|
||||
uint8_t green = color[1];
|
||||
uint8_t red = color[2];
|
||||
uint8_t alpha = color[3];
|
||||
|
||||
// 为图层创建临时位图
|
||||
int layer_x0, layer_y0, layer_x1, layer_y1;
|
||||
stbtt_GetGlyphBitmapBox(&font_info_, layer_glyph_id, scale, scale,
|
||||
&layer_x0, &layer_y0, &layer_x1, &layer_y1);
|
||||
|
||||
int layer_width = layer_x1 - layer_x0;
|
||||
int layer_height = layer_y1 - layer_y0;
|
||||
|
||||
if (layer_width <= 0 || layer_height <= 0)
|
||||
continue;
|
||||
|
||||
// 渲染图层字形
|
||||
std::vector<uint8_t> layer_bitmap(layer_width * layer_height);
|
||||
stbtt_MakeGlyphBitmap(&font_info_, layer_bitmap.data(),
|
||||
layer_width, layer_height, layer_width,
|
||||
scale, scale, layer_glyph_id);
|
||||
|
||||
// 计算图层在最终位图中的位置
|
||||
int offset_x = layer_x0 - x0;
|
||||
int offset_y = layer_y0 - y0;
|
||||
|
||||
// 将图层与颜色混合到最终位图
|
||||
for (int y = 0; y < layer_height; y++) {
|
||||
int dst_y = offset_y + y;
|
||||
if (dst_y < 0 || dst_y >= bitmap.height)
|
||||
continue;
|
||||
|
||||
for (int x = 0; x < layer_width; x++) {
|
||||
int dst_x = offset_x + x;
|
||||
if (dst_x < 0 || dst_x >= bitmap.width)
|
||||
continue;
|
||||
|
||||
uint8_t coverage = layer_bitmap[y * layer_width + x];
|
||||
if (coverage == 0)
|
||||
continue;
|
||||
|
||||
int dst_idx = (dst_y * bitmap.width + dst_x) * 4;
|
||||
|
||||
// Alpha混合
|
||||
float src_alpha = (coverage / 255.0f) * (alpha / 255.0f);
|
||||
float dst_alpha = bitmap.data[dst_idx + 3] / 255.0f;
|
||||
float out_alpha = src_alpha + dst_alpha * (1.0f - src_alpha);
|
||||
|
||||
if (out_alpha > 0) {
|
||||
bitmap.data[dst_idx + 0] = (uint8_t)((red * src_alpha + bitmap.data[dst_idx + 0] * dst_alpha * (1.0f - src_alpha)) / out_alpha);
|
||||
bitmap.data[dst_idx + 1] = (uint8_t)((green * src_alpha + bitmap.data[dst_idx + 1] * dst_alpha * (1.0f - src_alpha)) / out_alpha);
|
||||
bitmap.data[dst_idx + 2] = (uint8_t)((blue * src_alpha + bitmap.data[dst_idx + 2] * dst_alpha * (1.0f - src_alpha)) / out_alpha);
|
||||
}
|
||||
|
||||
bitmap.data[dst_idx + 3] = (uint8_t)(out_alpha * 255.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
@ -159,13 +292,13 @@ bool font_face_t::load_font_data(const std::wstring& in_font_path) {
|
||||
}
|
||||
|
||||
uint32_t font_face_t::find_table_offset(const uint8_t* data, uint32_t fontstart, const char* tag) {
|
||||
int num_tables = read_ushort(data + fontstart + 4);
|
||||
uint32_t tabledir = fontstart + 12;
|
||||
const auto num_tables = read_u16(data + fontstart + 4);
|
||||
const uint32_t table_dir = fontstart + 12;
|
||||
|
||||
for (int i = 0; i < num_tables; ++i) {
|
||||
uint32_t loc = tabledir + 16 * i;
|
||||
const uint32_t loc = table_dir + 16 * i;
|
||||
if (check_tag(data + loc, tag)) {
|
||||
return read_ulong(data + loc + 8);
|
||||
return read_u32(data + loc + 8);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@ -189,17 +322,17 @@ void font_face_t::extract_font_name() {
|
||||
|
||||
// 名称表头部
|
||||
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);
|
||||
uint16_t count = read_u16(name_table + 2);
|
||||
uint16_t string_offset = read_u16(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);
|
||||
uint16_t platform_id = read_u16(record);
|
||||
uint16_t language_id = read_u16(record + 4);
|
||||
uint16_t name_id = read_u16(record + 6);
|
||||
uint16_t length = read_u16(record + 8);
|
||||
uint16_t offset = read_u16(record + 10);
|
||||
|
||||
// 优先使用Windows平台的英语完整名称
|
||||
bool is_english = (platform_id == 3 && language_id == 0x0409); // Windows English (US)
|
||||
|
@ -96,15 +96,16 @@ public:
|
||||
|
||||
/**
|
||||
* @brief 获取彩色表情位图
|
||||
* @param in_emoji_sequence 表情序列
|
||||
* @param glyph_index 表情符号索引
|
||||
* @param in_font_size 字体大小
|
||||
* @return 可选的彩色位图
|
||||
*/
|
||||
[[nodiscard]] std::optional<color_emoji_bitmap_t> get_color_emoji_bitmap(
|
||||
const std::vector<int32_t>& in_emoji_sequence,
|
||||
float in_font_size) const;
|
||||
[[nodiscard]] std::optional<color_emoji_bitmap_t> get_color_emoji_bitmap(int32_t glyph_index,
|
||||
float in_font_size) const;
|
||||
|
||||
const auto& get_font_info() const { return font_info_; }
|
||||
|
||||
std::optional<color_emoji_bitmap_t> get_colr_bitmap(int32_t in_glyph_index, float in_font_size) const;
|
||||
private:
|
||||
stbtt_fontinfo font_info_{}; // stb_truetype字体信息
|
||||
std::shared_ptr<mapped_file> font_data_; // 字体文件数据
|
||||
@ -143,8 +144,8 @@ private:
|
||||
* @param p 数据指针
|
||||
* @return 16位整数值
|
||||
*/
|
||||
static uint16_t read_ushort(const uint8_t* p) {
|
||||
return (uint16_t)(p[0] << 8) + p[1];
|
||||
static uint16_t read_u16(const uint8_t* p) {
|
||||
return p[0] << 8 | p[1];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,9 +153,11 @@ private:
|
||||
* @param p 数据指针
|
||||
* @return 32位整数值
|
||||
*/
|
||||
static uint32_t read_ulong(const uint8_t* p) {
|
||||
return ((uint32_t)p[0] << 24) + ((uint32_t)p[1] << 16) +
|
||||
((uint32_t)p[2] << 8) + p[3];
|
||||
static uint32_t read_u32(const uint8_t* p) {
|
||||
return (uint32_t)p[0] << 24 |
|
||||
(uint32_t)p[1] << 16 |
|
||||
(uint32_t)p[2] << 8 |
|
||||
p[3];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,115 +1,188 @@
|
||||
#include "font_system.h"
|
||||
|
||||
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;
|
||||
const float scale = in_font->get_scale_for_pixel_height(font_size);
|
||||
|
||||
// 布局变量
|
||||
float cursor_x = 0.0f;
|
||||
float cursor_y = 0.0f;
|
||||
const float line_height = font_size * line_spacing;
|
||||
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);
|
||||
// 获取字形度量
|
||||
const auto& glyph_metrics = in_font->get_glyph_by_index(glyph_index, font_size);
|
||||
|
||||
if (!glyph_metrics) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& region = get_or_create_glyph_by_index(glyph_metrics->glyph_index, in_font, font_size);
|
||||
if (!region) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查换行
|
||||
if (max_width > 0 &&
|
||||
cursor_x + glyph_metrics->advance > max_width &&
|
||||
cursor_x > 0) {
|
||||
cursor_x = 0.0f;
|
||||
cursor_y += line_height;
|
||||
baseline = cursor_y + ascent;
|
||||
}
|
||||
|
||||
// **计算最终位置**
|
||||
float glyph_x = cursor_x + glyph_metrics->bearing.x();
|
||||
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;
|
||||
glyph_position.position = { glyph_x, glyph_y };
|
||||
glyph_position.size = glyph_metrics->size;
|
||||
glyph_position.region = *region;
|
||||
|
||||
// 前进光标
|
||||
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;
|
||||
}
|
||||
|
||||
// 设置布局总体尺寸
|
||||
layout.total_size = {cursor_x, cursor_y + line_height};
|
||||
|
||||
return layout;
|
||||
void font_manager::destroy() {
|
||||
fonts_.clear();
|
||||
emoji_font_ids_.clear();
|
||||
atlas_manager_.clear();
|
||||
primary_font_id_ = -1;
|
||||
next_font_id_ = 0;
|
||||
}
|
||||
|
||||
std::optional<atlas_region_t> font_manager::get_or_create_glyph_by_index(uint32_t in_glyph_id,
|
||||
std::shared_ptr<font_face_t> in_font, float in_font_size) {
|
||||
|
||||
// 获取字体
|
||||
if (!in_font) {
|
||||
return std::nullopt;
|
||||
int font_manager::add_font(const std::wstring& in_font_path, const std::string& in_font_type) {
|
||||
auto font = std::make_shared<font_face_t>();
|
||||
if (!font->load_from_file(in_font_path)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 获取字形
|
||||
const auto& glyph = in_font->get_glyph_by_index(in_glyph_id, in_font_size);
|
||||
if (!glyph)
|
||||
return std::nullopt;
|
||||
font->set_font_type(in_font_type);
|
||||
int font_id = next_font_id_++;
|
||||
fonts_[font_id] = font;
|
||||
|
||||
glyph_atlas_result_t result{};
|
||||
result.reason = glyph_atlas_reason_t::atlas_full;
|
||||
// 尝试获取缓存
|
||||
for (auto& atlas : glyph_atlases_) {
|
||||
result = atlas.get_or_create_glyph(in_glyph_id, *in_font, in_font_size);
|
||||
if (result.reason == glyph_atlas_reason_t::glyph_not_found) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (result.reason == glyph_atlas_reason_t::success) {
|
||||
return result.region;
|
||||
}
|
||||
// 如果支持彩色表情或明确指定为表情字体,添加到表情字体列表
|
||||
if (font->supports_color_emoji() || in_font_type == "emoji") {
|
||||
emoji_font_ids_.push_back(font_id);
|
||||
}
|
||||
|
||||
if (result.reason == glyph_atlas_reason_t::atlas_full) {
|
||||
// 创建新的图集
|
||||
auto& atlas = glyph_atlases_.emplace_back();
|
||||
atlas.initialize({ 1024, 1024 });
|
||||
result = atlas.get_or_create_glyph(in_glyph_id, *in_font, in_font_size);
|
||||
if (result.reason == glyph_atlas_reason_t::success) {
|
||||
return result.region;
|
||||
}
|
||||
// 如果是第一个添加的字体,自动设为主字体
|
||||
if (primary_font_id_ == -1) {
|
||||
primary_font_id_ = font_id;
|
||||
}
|
||||
return std::nullopt;
|
||||
|
||||
return font_id;
|
||||
}
|
||||
|
||||
std::shared_ptr<font_face_t> font_manager::get_font_for_code_point(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 布局文本,将Unicode文本转换为可渲染的字形布局
|
||||
*/
|
||||
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;
|
||||
|
||||
// 使用指定字体或主字体
|
||||
const auto& primary_font = in_font ? in_font : get_primary_font();
|
||||
assert(primary_font && "No valid font available");
|
||||
|
||||
// 计算字体缩放比例
|
||||
const float scale = primary_font->get_scale_for_pixel_height(font_size);
|
||||
|
||||
// 初始化布局变量
|
||||
float cursor_x = 0.0f;
|
||||
float cursor_y = 0.0f;
|
||||
const float line_height = font_size * line_spacing;
|
||||
const float ascent = primary_font->get_ascent() * scale;
|
||||
float baseline = cursor_y + ascent;
|
||||
|
||||
int32_t last_index = 0; // 上一个字形索引,用于字距调整
|
||||
float min_y = std::numeric_limits<float>::max();
|
||||
|
||||
// 当前使用的字体,开始时使用主字体
|
||||
auto using_font = primary_font;
|
||||
|
||||
// 处理每个字符
|
||||
for (const auto& c : text) {
|
||||
// 检查是否是表情符号
|
||||
bool is_emoji = emoji_detector::is_emoji(c);
|
||||
|
||||
// 获取字形索引
|
||||
auto glyph_index = using_font->find_glyph_index(c);
|
||||
|
||||
// 如果当前字体不支持该字符,尝试查找支持该字符的其他字体
|
||||
if (glyph_index == 0) {
|
||||
using_font = get_font_for_code_point(c);
|
||||
if (!using_font) {
|
||||
continue; // 如果没有找到支持的字体,跳过该字符
|
||||
}
|
||||
|
||||
// 使用新字体重新获取字形索引
|
||||
glyph_index = using_font->find_glyph_index(c);
|
||||
}
|
||||
|
||||
// 获取字形度量信息
|
||||
const auto& glyph_metrics = using_font->get_glyph_by_index(glyph_index, font_size);
|
||||
if (!glyph_metrics) {
|
||||
continue; // 如果无法获取字形度量,跳过该字符
|
||||
}
|
||||
|
||||
// 获取或创建字形在图集中的区域
|
||||
const auto& region = get_or_create_glyph_by_index(glyph_metrics->glyph_index, using_font, font_size, is_emoji);
|
||||
if (!region) {
|
||||
continue; // 如果无法在图集中获取或创建字形,跳过该字符
|
||||
}
|
||||
|
||||
// 检查是否需要换行
|
||||
if (max_width > 0 &&
|
||||
cursor_x + glyph_metrics->advance > max_width &&
|
||||
cursor_x > 0) { // 确保不是行首字符
|
||||
cursor_x = 0.0f;
|
||||
cursor_y += line_height;
|
||||
baseline = cursor_y + ascent;
|
||||
}
|
||||
|
||||
// **计算字形位置**
|
||||
float glyph_x = cursor_x + glyph_metrics->bearing.x();
|
||||
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.is_emoji = is_emoji;
|
||||
glyph_position.glyph_index = glyph_index;
|
||||
glyph_position.position = { glyph_x, glyph_y };
|
||||
glyph_position.size = glyph_metrics->size;
|
||||
glyph_position.region = *region;
|
||||
|
||||
// 更新光标位置
|
||||
cursor_x += glyph_metrics->advance;
|
||||
|
||||
// 应用字距调整
|
||||
if (last_index > 0) {
|
||||
const float kern_advance = using_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& glyph : layout.glyphs) {
|
||||
glyph.position.y() -= min_y;
|
||||
}
|
||||
|
||||
// 设置文本布局的总体尺寸
|
||||
layout.total_size = { cursor_x, cursor_y + line_height };
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取或创建字形在图集中的区域
|
||||
*/
|
||||
std::optional<atlas_region_t> font_manager::get_or_create_glyph_by_index(
|
||||
int32_t in_glyph_id,
|
||||
std::shared_ptr<font_face_t> in_font,
|
||||
float in_font_size,
|
||||
bool is_emoji)
|
||||
{
|
||||
// 验证字体有效性
|
||||
if (!in_font) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return atlas_manager_.get_or_create_glyph(in_glyph_id, *in_font, in_font_size, is_emoji);
|
||||
}
|
||||
|
@ -1,102 +1,84 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <optional>
|
||||
|
||||
#include "emoji_detector.h"
|
||||
#include "font_atlas_system.h"
|
||||
#include "font_face.h"
|
||||
// font_system.h - 字体管理系统
|
||||
#include "atlas/bitmap_glyph_atlas.h"
|
||||
#include "atlas/color_emoji_atlas.h"
|
||||
#include "atlas/font_atlas.h"
|
||||
#include "atlas/font_atlas_manager.h"
|
||||
|
||||
/**
|
||||
* @class font_manager
|
||||
* @brief 管理多种字体并提供字体回退机制
|
||||
* @brief 字体管理系统
|
||||
*
|
||||
* 管理多种字体、提供字体回退机制,并处理文本布局和字形图集管理。
|
||||
* 实现为单例模式,确保全局只有一个字体管理实例。
|
||||
*/
|
||||
class font_manager {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
*
|
||||
* @return font_manager& 字体管理器的单例引用
|
||||
*/
|
||||
static font_manager& instance() {
|
||||
static font_manager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
fonts_.clear();
|
||||
emoji_font_ids_.clear();
|
||||
primary_font_id_ = -1;
|
||||
next_font_id_ = 0;
|
||||
/**
|
||||
* @brief 销毁所有资源
|
||||
*
|
||||
* 清理所有字体和图集资源,重置字体管理器状态。
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 添加字体
|
||||
*
|
||||
* 从文件加载字体并添加到字体管理系统中。
|
||||
* 如果字体支持彩色表情符号或指定为表情符号字体,将自动添加到表情符号字体列表。
|
||||
*
|
||||
* @param in_font_path 字体文件路径
|
||||
* @param in_font_type 字体类型(regular, bold, italic, emoji等)
|
||||
* @return 添加成功返回字体ID,失败返回-1
|
||||
*/
|
||||
int add_font(const std::wstring& in_font_path, const std::string& in_font_type = "regular");
|
||||
|
||||
/**
|
||||
* @brief 设置主字体
|
||||
*
|
||||
* @param in_font_id 要设为主字体的字体ID
|
||||
* @return 设置是否成功
|
||||
*/
|
||||
bool set_primary_font(int in_font_id) {
|
||||
if (fonts_.contains(in_font_id)) {
|
||||
primary_font_id_ = in_font_id;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加字体
|
||||
* @param in_font_path 字体文件路径
|
||||
* @param in_font_type 字体类型(regular, bold, italic, emoji等)
|
||||
* @return 字体ID
|
||||
*/
|
||||
int add_font(const std::wstring& 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;
|
||||
}
|
||||
/**
|
||||
* @brief 获取指定码点的最佳字体
|
||||
*
|
||||
* 实现字体回退机制,按以下顺序尝试查找支持指定码点的字体:
|
||||
* 1. 主字体
|
||||
* 2. 如果是表情符号,尝试表情符号字体
|
||||
* 3. 所有其他字体
|
||||
* 4. 回退到主字体
|
||||
*
|
||||
* @param in_code_point Unicode码点
|
||||
* @return 最适合渲染该码点的字体
|
||||
*/
|
||||
std::shared_ptr<font_face_t> get_font_for_code_point(uint32_t in_code_point);
|
||||
|
||||
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> get_font_for_code_point(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 主字体
|
||||
*/
|
||||
@ -111,31 +93,66 @@ public:
|
||||
* @brief 检查是否有彩色表情字体
|
||||
* @return 是否支持彩色表情
|
||||
*/
|
||||
bool has_color_emoji_font() const {
|
||||
[[nodiscard]] bool has_color_emoji_font() const {
|
||||
return !emoji_font_ids_.empty();
|
||||
}
|
||||
|
||||
text_layout_t layout_text(
|
||||
const std::u32string& text,
|
||||
const std::shared_ptr<font_face_t>& in_font,
|
||||
float font_size,
|
||||
float max_width = 0.0f,
|
||||
float line_spacing = 1.2f);
|
||||
/**
|
||||
* @brief 布局文本
|
||||
*
|
||||
* 将Unicode文本转换为可渲染的字形布局,包括位置计算和换行处理。
|
||||
*
|
||||
* @param text Unicode文本
|
||||
* @param in_font 指定字体,如为空则使用主字体
|
||||
* @param font_size 字体大小
|
||||
* @param max_width 最大宽度,超过此宽度会自动换行,0表示不限制宽度
|
||||
* @param line_spacing 行间距系数
|
||||
* @return 文本布局结果
|
||||
*/
|
||||
text_layout_t layout_text(
|
||||
const std::u32string& text,
|
||||
const std::shared_ptr<font_face_t>& in_font,
|
||||
float font_size,
|
||||
float max_width = 0.0f,
|
||||
float line_spacing = 1.2f);
|
||||
|
||||
std::optional<atlas_region_t> get_or_create_glyph_by_index(uint32_t in_glyph_id, std::shared_ptr<font_face_t> in_font, float in_font_size);
|
||||
/**
|
||||
* @brief 获取或创建字形
|
||||
*
|
||||
* 从图集中获取字形纹理区域,如果不存在则创建。
|
||||
*
|
||||
* @param in_glyph_id 字形ID
|
||||
* @param in_font 字体
|
||||
* @param in_font_size 字体大小
|
||||
* @param is_emoji 是否为表情符号
|
||||
* @return 字形在图集中的区域
|
||||
*/
|
||||
std::optional<atlas_region_t> get_or_create_glyph_by_index(
|
||||
int32_t in_glyph_id,
|
||||
std::shared_ptr<font_face_t> in_font,
|
||||
float in_font_size,
|
||||
bool is_emoji);
|
||||
|
||||
const auto& get_glyph_atlases() const {
|
||||
return glyph_atlases_;
|
||||
/**
|
||||
* @brief 获取所有字形图集
|
||||
* @return 字形图集列表的常量引用
|
||||
*/
|
||||
[[nodiscard]] const auto& get_glyph_atlases() const {
|
||||
return atlas_manager_.get_glyph_atlases();
|
||||
}
|
||||
const auto& get_emoji_atlases() const {
|
||||
return emoji_atlases_;
|
||||
|
||||
/**
|
||||
* @brief 获取所有表情图集
|
||||
* @return 表情图集列表的常量引用
|
||||
*/
|
||||
[[nodiscard]] const auto& get_emoji_atlases() const {
|
||||
return atlas_manager_.get_emoji_atlases();
|
||||
}
|
||||
private:
|
||||
std::unordered_map<int, std::shared_ptr<font_face_t>> fonts_;
|
||||
std::vector<mtsdf_glyph_atlas> glyph_atlases_;
|
||||
std::vector<color_emoji_atlas> emoji_atlases_;
|
||||
std::unordered_map<int, std::shared_ptr<font_face_t>> fonts_; ///< 字体ID到字体对象的映射
|
||||
font_atlas_manager atlas_manager_; ///< 图集管理器
|
||||
|
||||
std::vector<int> emoji_font_ids_;
|
||||
int primary_font_id_ = -1;
|
||||
int next_font_id_ = 0;
|
||||
};
|
||||
std::vector<int> emoji_font_ids_; ///< 表情符号字体ID列表
|
||||
int primary_font_id_ = -1; ///< 主字体ID
|
||||
int next_font_id_ = 0; ///< 下一个可用的字体ID
|
||||
};
|
@ -138,6 +138,7 @@ struct shaped_glyph_t {
|
||||
|
||||
struct text_layout_t {
|
||||
struct glyph_position_t {
|
||||
bool is_emoji; // 是否为表情符号
|
||||
int32_t glyph_index; // 字符码点
|
||||
Eigen::Vector2f position; // 屏幕位置
|
||||
Eigen::Vector2f size; // 字形尺寸
|
||||
|
@ -37,7 +37,6 @@ void mirage_paint_context::reset() {
|
||||
|
||||
/**
|
||||
* @brief 开始一个新的绘制帧
|
||||
* @param in_parent_widget 当前帧的父组件
|
||||
*
|
||||
* 设置父组件状态,并通知渲染元素开始新的帧。
|
||||
* 每个渲染帧的开始都应调用此函数。
|
||||
|
@ -259,7 +259,7 @@ void render_elements::make_text(const text_layout_t& in_layout, const Eigen::Vec
|
||||
if (auto texture = region.texture.lock()) {
|
||||
// 构建完整的批次键
|
||||
batch_key new_key;
|
||||
new_key.pipeline = text_pipeline_;
|
||||
new_key.pipeline = position.is_emoji ? image_pipeline_ : text_pipeline_;
|
||||
new_key.image = texture->get_image();
|
||||
new_key.sampler = *sampler;
|
||||
|
||||
|
@ -21,6 +21,5 @@ auto mtext_block::compute_desired_size(float in_layout_scale_multiplier) const -
|
||||
}
|
||||
|
||||
void mtext_block::update_layout() {
|
||||
const auto& using_font = font_ ? font_ : font_manager::instance().get_primary_font();
|
||||
layout_ = font_manager::instance().layout_text(text_, using_font, font_size_, max_width_, line_spacing_);
|
||||
layout_ = font_manager::instance().layout_text(text_, font_, font_size_, max_width_, line_spacing_);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user