diff --git a/.gitignore b/.gitignore index 587077d..5abd834 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ fips-files/deploy/ CMakeUserPresets.json #(); + const auto& text_block = std::make_shared(); 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); diff --git a/src/mirage_render/font/atlas/bitmap_glyph_atlas.h b/src/mirage_render/font/atlas/bitmap_glyph_atlas.h new file mode 100644 index 0000000..122b5b3 --- /dev/null +++ b/src/mirage_render/font/atlas/bitmap_glyph_atlas.h @@ -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; + } +}; diff --git a/src/mirage_render/font/atlas/color_emoji_atlas.h b/src/mirage_render/font/atlas/color_emoji_atlas.h new file mode 100644 index 0000000..0d24fb8 --- /dev/null +++ b/src/mirage_render/font/atlas/color_emoji_atlas.h @@ -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; + } +}; diff --git a/src/mirage_render/font/atlas/font_atlas.h b/src/mirage_render/font/atlas/font_atlas.h new file mode 100644 index 0000000..d967ff5 --- /dev/null +++ b/src/mirage_render/font/atlas/font_atlas.h @@ -0,0 +1,149 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#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 atlas_; ///< 底层纹理图集对象 + std::unordered_map 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( + 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(); + } +}; diff --git a/src/mirage_render/font/atlas/font_atlas_manager.cpp b/src/mirage_render/font/atlas/font_atlas_manager.cpp new file mode 100644 index 0000000..dd94700 --- /dev/null +++ b/src/mirage_render/font/atlas/font_atlas_manager.cpp @@ -0,0 +1,71 @@ +#include "font_atlas_manager.h" + +std::optional 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; +} diff --git a/src/mirage_render/font/atlas/font_atlas_manager.h b/src/mirage_render/font/atlas/font_atlas_manager.h new file mode 100644 index 0000000..1928b01 --- /dev/null +++ b/src/mirage_render/font/atlas/font_atlas_manager.h @@ -0,0 +1,81 @@ +#pragma once +#include +#include +#include +#include + +#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 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 glyph_atlases_; ///< 普通字形图集列表 + std::vector 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}); +}; diff --git a/src/mirage_render/font/colr_cpal_emoji.h b/src/mirage_render/font/colr_cpal_emoji.h new file mode 100644 index 0000000..698bb47 --- /dev/null +++ b/src/mirage_render/font/colr_cpal_emoji.h @@ -0,0 +1,171 @@ +#pragma once +#include "stb_truetype.h" +#include +#include +#include +#include + +#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& 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 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; +} \ No newline at end of file diff --git a/src/mirage_render/font/font_atlas_system.h b/src/mirage_render/font/font_atlas_system.h deleted file mode 100644 index 37cb4c7..0000000 --- a/src/mirage_render/font/font_atlas_system.h +++ /dev/null @@ -1,218 +0,0 @@ -#pragma once -// font_atlas_system.h - 字形与表情图集管理 - -#include - -#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( - 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 atlas_; - std::unordered_map 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( - 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 get_or_create_emoji( - const std::vector& 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 atlas_; - std::unordered_map cached_emojis_; - - /** - * @brief 创建表情缓存键 - * @param in_sequence 表情序列 - * @param in_font_size 字体大小 - * @return 缓存键 - */ - static std::string create_emoji_key(const std::vector& 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(); - } -}; diff --git a/src/mirage_render/font/font_face.cpp b/src/mirage_render/font/font_face.cpp index 3b0425e..b276aa0 100644 --- a/src/mirage_render/font/font_face.cpp +++ b/src/mirage_render/font/font_face.cpp @@ -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 font_face_t::get_color_emoji_bitmap( - const std::vector& in_emoji_sequence, float in_font_size) const { - - if (!supports_color_emoji() || in_emoji_sequence.empty()) { +std::optional 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(in_font_size); - bitmap.height = static_cast(in_font_size); - bitmap.data.resize(bitmap.width * bitmap.height * 4, 255); + // TODO 读取彩色位图数据 + return get_colr_bitmap(glyph_index, in_font_size); +} + +std::optional font_face_t::get_colr_bitmap(int32_t in_glyph_index, float in_font_size) const { + const auto* data = static_cast(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 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) diff --git a/src/mirage_render/font/font_face.h b/src/mirage_render/font/font_face.h index e9fb9ad..0a0f82f 100644 --- a/src/mirage_render/font/font_face.h +++ b/src/mirage_render/font/font_face.h @@ -96,15 +96,16 @@ public: /** * @brief 获取彩色表情位图 - * @param in_emoji_sequence 表情序列 + * @param glyph_index 表情符号索引 * @param in_font_size 字体大小 * @return 可选的彩色位图 */ - [[nodiscard]] std::optional get_color_emoji_bitmap( - const std::vector& in_emoji_sequence, - float in_font_size) const; + [[nodiscard]] std::optional get_color_emoji_bitmap(int32_t glyph_index, + float in_font_size) const; const auto& get_font_info() const { return font_info_; } + + std::optional get_colr_bitmap(int32_t in_glyph_index, float in_font_size) const; private: stbtt_fontinfo font_info_{}; // stb_truetype字体信息 std::shared_ptr 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]; } /** diff --git a/src/mirage_render/font/font_system.cpp b/src/mirage_render/font/font_system.cpp index dca3880..2856905 100644 --- a/src/mirage_render/font/font_system.cpp +++ b/src/mirage_render/font/font_system.cpp @@ -1,115 +1,188 @@ #include "font_system.h" -text_layout_t font_manager::layout_text(const std::u32string& text, - const std::shared_ptr& 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::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 font_manager::get_or_create_glyph_by_index(uint32_t in_glyph_id, - std::shared_ptr 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(); + 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_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& 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::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 font_manager::get_or_create_glyph_by_index( + int32_t in_glyph_id, + std::shared_ptr 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); } diff --git a/src/mirage_render/font/font_system.h b/src/mirage_render/font/font_system.h index fa3954c..4df23d9 100644 --- a/src/mirage_render/font/font_system.h +++ b/src/mirage_render/font/font_system.h @@ -1,102 +1,84 @@ #pragma once #include #include +#include +#include +#include #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(); - if (!font->load_from_file(in_font_path)) { - return -1; - } + /** + * @brief 获取指定码点的最佳字体 + * + * 实现字体回退机制,按以下顺序尝试查找支持指定码点的字体: + * 1. 主字体 + * 2. 如果是表情符号,尝试表情符号字体 + * 3. 所有其他字体 + * 4. 回退到主字体 + * + * @param in_code_point Unicode码点 + * @return 最适合渲染该码点的字体 + */ + std::shared_ptr 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 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& 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& in_font, + float font_size, + float max_width = 0.0f, + float line_spacing = 1.2f); - std::optional get_or_create_glyph_by_index(uint32_t in_glyph_id, std::shared_ptr 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 get_or_create_glyph_by_index( + int32_t in_glyph_id, + std::shared_ptr 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> fonts_; - std::vector glyph_atlases_; - std::vector emoji_atlases_; + std::unordered_map> fonts_; ///< 字体ID到字体对象的映射 + font_atlas_manager atlas_manager_; ///< 图集管理器 - std::vector emoji_font_ids_; - int primary_font_id_ = -1; - int next_font_id_ = 0; -}; + std::vector emoji_font_ids_; ///< 表情符号字体ID列表 + int primary_font_id_ = -1; ///< 主字体ID + int next_font_id_ = 0; ///< 下一个可用的字体ID +}; \ No newline at end of file diff --git a/src/mirage_render/font/font_type.h b/src/mirage_render/font/font_type.h index 92e4e31..b9adb8b 100644 --- a/src/mirage_render/font/font_type.h +++ b/src/mirage_render/font/font_type.h @@ -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; // 字形尺寸 diff --git a/src/mirage_render/render/mirage_paint_context.cpp b/src/mirage_render/render/mirage_paint_context.cpp index 28754bc..4355bc8 100644 --- a/src/mirage_render/render/mirage_paint_context.cpp +++ b/src/mirage_render/render/mirage_paint_context.cpp @@ -37,7 +37,6 @@ void mirage_paint_context::reset() { /** * @brief 开始一个新的绘制帧 - * @param in_parent_widget 当前帧的父组件 * * 设置父组件状态,并通知渲染元素开始新的帧。 * 每个渲染帧的开始都应调用此函数。 diff --git a/src/mirage_render/render/render_elements.cpp b/src/mirage_render/render/render_elements.cpp index 6cc617e..413a7ab 100644 --- a/src/mirage_render/render/render_elements.cpp +++ b/src/mirage_render/render/render_elements.cpp @@ -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; diff --git a/src/mirage_widget/widget/leaf_widget/mtext_block.cpp b/src/mirage_widget/widget/leaf_widget/mtext_block.cpp index 6a403c8..ba1c75c 100644 --- a/src/mirage_widget/widget/leaf_widget/mtext_block.cpp +++ b/src/mirage_widget/widget/leaf_widget/mtext_block.cpp @@ -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_); }