diff --git a/src/mirage_render/CMakeLists.txt b/src/mirage_render/CMakeLists.txt index 1394169..fee375f 100644 --- a/src/mirage_render/CMakeLists.txt +++ b/src/mirage_render/CMakeLists.txt @@ -8,9 +8,17 @@ target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(${PROJECT_NAME} PUBLIC mirage_core sokol) option(MIRAGE_STB_IMAGE_LOADER "Use stb load image" ON) +option(MIRAGE_SVG_EMOJI "Use svg emoji" ON) if (MIRAGE_STB_IMAGE_LOADER) target_link_libraries(${PROJECT_NAME} PUBLIC mirage_stb_image_loader) + target_compile_definitions(${PROJECT_NAME} PUBLIC HAS_STB_IMAGE) +endif () +if (MIRAGE_SVG_EMOJI) + find_package(nanosvg REQUIRED) + find_package(ZLIB REQUIRED) + target_link_libraries(${PROJECT_NAME} PUBLIC NanoSVG::nanosvg NanoSVG::nanosvgrast ZLIB::ZLIB) + target_compile_definitions(${PROJECT_NAME} PUBLIC HAS_NANOSVG) endif () # 添加编译shader的自定义命令 diff --git a/src/mirage_render/font/font_face.cpp b/src/mirage_render/font/font_face.cpp index 7c78356..d6ba259 100644 --- a/src/mirage_render/font/font_face.cpp +++ b/src/mirage_render/font/font_face.cpp @@ -2,8 +2,11 @@ #define STB_TRUETYPE_IMPLEMENTATION #include "font_utils.h" +#include "font_renderer/cbdt_renderer.h" #include "font_renderer/colr_renderer.h" +#include "font_renderer/sbix_renderer.h" #include "font_renderer/standard_bitmap_renderer.h" +#include "font_renderer/svg_renderer.h" font_face_t::font_face_t() { // 延迟初始化渲染器,直到加载字体 @@ -247,12 +250,14 @@ void font_face_t::init_renderers() { bitmap_renderer_ = std::make_unique(); // 创建彩色表情渲染器 +#ifdef HAS_STB_IMAGE + emoji_renderers_.push_back(std::make_unique()); +#endif + emoji_renderers_.push_back(std::make_unique()); emoji_renderers_.push_back(std::make_unique()); - - // 未来可以在这里添加其他类型的彩色表情渲染器 - // emoji_renderers_.push_back(std::make_unique()); - // emoji_renderers_.push_back(std::make_unique()); - // emoji_renderers_.push_back(std::make_unique()); +#ifdef HAS_NANOSVG + emoji_renderers_.push_back(std::make_unique()); +#endif } bool font_face_t::load_font_data(const std::wstring& font_path) { diff --git a/src/mirage_render/font/font_renderer/cbdt_renderer.cpp b/src/mirage_render/font/font_renderer/cbdt_renderer.cpp new file mode 100644 index 0000000..0e8fd95 --- /dev/null +++ b/src/mirage_render/font/font_renderer/cbdt_renderer.cpp @@ -0,0 +1,295 @@ +#include "cbdt_renderer.h" +#include +#include + +bool cbdt_renderer_t::supports_font(const uint8_t* font_data, int font_offset) const { + // 检查CBDT和CBLC表是否同时存在 + return font::find_table_offset(font_data, font_offset, "CBDT") != 0 && + font::find_table_offset(font_data, font_offset, "CBLC") != 0; +} + +std::optional cbdt_renderer_t::render_emoji( + const stbtt_fontinfo& font_info, + const uint8_t* font_data, + int font_offset, + int32_t glyph_index, + float font_size) const { + + // 检查参数有效性 + if (glyph_index == 0 || font_size <= 0.0f) { + return std::nullopt; + } + + // 查找CBDT和CBLC表 + const uint32_t cbdt_offset = font::find_table_offset(font_data, font_offset, "CBDT"); + const uint32_t cblc_offset = font::find_table_offset(font_data, font_offset, "CBLC"); + + if (cbdt_offset == 0 || cblc_offset == 0) { + return std::nullopt; + } + + const uint8_t* cbdt_data = font_data + cbdt_offset; + const uint8_t* cblc_data = font_data + cblc_offset; + + // 查找字形位图 + auto bitmap_loc = find_bitmap(cblc_data, cbdt_data, glyph_index, font_size); + if (!bitmap_loc) { + return std::nullopt; + } + + // 解码位图数据 + return decode_bitmap(*bitmap_loc); +} + +std::optional cbdt_renderer_t::find_bitmap( + const uint8_t* cblc_data, + const uint8_t* cbdt_data, + int32_t glyph_index, + float target_size) const { + + // 解析CBLC表头部 + // 2字节:majorVersion + // 2字节:minorVersion + // 4字节:numSizes(bitmapSize数量) + const uint16_t major_version = font::read_u16(cblc_data); + const uint16_t minor_version = font::read_u16(cblc_data + 2); + const uint32_t num_sizes = font::read_u32(cblc_data + 4); + + if (num_sizes == 0) { + return std::nullopt; + } + + // 找到最佳匹配的bitmapSize(位图尺寸) + uint32_t best_size_index = 0; + uint16_t best_ppem = 0; + uint16_t target_ppem = static_cast(target_size); + + // bitmapSize数组 + const uint8_t* bitmapSize_array = cblc_data + 8; + + // 遍历所有bitmapSize,找到最接近目标尺寸的一个 + for (uint32_t i = 0; i < num_sizes; i++) { + const uint8_t* size_table = bitmapSize_array + i * 48; // 每个bitmapSize表48字节 + + // 读取当前bitmapSize的ppem(像素每em) + const uint16_t ppem_x = font::read_u16(size_table); + const uint16_t ppem_y = font::read_u16(size_table + 2); + const uint16_t ppem = std::max(ppem_x, ppem_y); // 通常两者相等 + + // 找到最接近的尺寸 + if (best_ppem == 0 || + (ppem >= target_ppem && ppem < best_ppem) || + (best_ppem < target_ppem && ppem > best_ppem)) { + best_ppem = ppem; + best_size_index = i; + } + } + + // 获取选中的bitmapSize数据 + const uint8_t* size_table = bitmapSize_array + best_size_index * 48; + + // 读取indexSubTableArray偏移和数量 + const uint32_t index_subtable_array_offset = font::read_u32(size_table + 8); + const uint32_t num_index_subtables = font::read_u32(size_table + 12); + + // 访问indexSubTableArray + const uint8_t* index_subtable_array = cblc_data + index_subtable_array_offset; + + // 查找包含目标字形的索引子表 + for (uint32_t i = 0; i < num_index_subtables; i++) { + const uint8_t* subtable_entry = index_subtable_array + i * 8; + + // 读取firstGlyphIndex和lastGlyphIndex + const uint16_t first_glyph = font::read_u16(subtable_entry); + const uint16_t last_glyph = font::read_u16(subtable_entry + 2); + + // 检查字形索引是否在范围内 + if (glyph_index >= first_glyph && glyph_index <= last_glyph) { + // 找到了包含目标字形的子表 + const uint32_t subtable_offset = font::read_u32(subtable_entry + 4); + const uint8_t* index_subtable = cblc_data + subtable_offset; + + // 读取索引子表头部 + const uint16_t index_format = font::read_u16(index_subtable); + const uint16_t image_format = font::read_u16(index_subtable + 2); + const uint32_t image_data_offset = font::read_u32(index_subtable + 4); + + // 根据索引格式查找位图数据 + uint32_t bitmap_offset = 0; + uint32_t bitmap_length = 0; + + // 处理不同的索引格式 + if (index_format == 1) { + // 格式1:只有一个字形范围,所有字形共享相同的位图度量 + const uint8_t* sbit_line = index_subtable + 8; + bitmap_offset = font::read_u32(sbit_line) + image_data_offset; + bitmap_length = font::read_u32(sbit_line + 4); + } else if (index_format == 2) { + // 格式2:图像大小相同,但每个字形有不同的位图 + const uint16_t image_size = font::read_u32(index_subtable + 8); + const uint32_t glyph_offset = (glyph_index - first_glyph) * image_size; + bitmap_offset = image_data_offset + glyph_offset; + bitmap_length = image_size; + } else if (index_format == 3) { + // 格式3:每个字形有偏移数组 + const uint16_t glyph_array_offset = 16 + (glyph_index - first_glyph) * 4; + bitmap_offset = font::read_u32(index_subtable + glyph_array_offset) + image_data_offset; + + // 下一个字形的偏移或表的结束位置确定长度 + uint32_t next_offset; + if (glyph_index < last_glyph) { + next_offset = font::read_u32(index_subtable + glyph_array_offset + 4) + image_data_offset; + } else { + // 估计最后一个字形的长度 + next_offset = bitmap_offset + 1024; // 假设最大长度 + } + bitmap_length = next_offset - bitmap_offset; + } else if (index_format == 4 || index_format == 5) { + // 格式4和5:更复杂的索引方式,这里简化实现 + // 实际情况中需要根据具体字体完善 + continue; + } + + // 如果找到了位图偏移 + if (bitmap_offset > 0) { + // 访问CBDT表中的位图数据 + const uint8_t* bitmap_data = cbdt_data + bitmap_offset; + + // 创建位图位置信息 + bitmap_location_t location; + + // 根据图像格式解析度量信息 + if (image_format == 17) { + // 小字形位图格式 (SmallGlyphMetrics) + const uint8_t height = font::read_u8(bitmap_data); + const uint8_t width = font::read_u8(bitmap_data + 1); + const int8_t bearing_x = font::read_i8(bitmap_data + 2); + const int8_t bearing_y = font::read_i8(bitmap_data + 3); + const uint8_t advance = font::read_u8(bitmap_data + 4); + + location.data = bitmap_data + 5; // 跳过SmallGlyphMetrics + location.data_format = image_format; + location.size = bitmap_length - 5; + location.width = width; + location.height = height; + location.bearing_x = bearing_x; + location.bearing_y = bearing_y; + location.bits_per_pixel = 32; // RGBA 8888 + + return location; + } + if (image_format == 18) { + // 大字形位图格式 (BigGlyphMetrics) + const uint8_t height = font::read_u8(bitmap_data); + const uint8_t width = font::read_u8(bitmap_data + 1); + const int8_t horiBearingX = font::read_i8(bitmap_data + 2); + const int8_t horiBearingY = font::read_i8(bitmap_data + 3); + const uint8_t horiAdvance = font::read_u8(bitmap_data + 4); + const int8_t vertBearingX = font::read_i8(bitmap_data + 5); + const int8_t vertBearingY = font::read_i8(bitmap_data + 6); + const uint8_t vertAdvance = font::read_u8(bitmap_data + 7); + + location.data = bitmap_data + 8; // 跳过BigGlyphMetrics + location.data_format = image_format; + location.size = bitmap_length - 8; + location.width = width; + location.height = height; + location.bearing_x = horiBearingX; + location.bearing_y = horiBearingY; + location.bits_per_pixel = 32; // RGBA 8888 + + return location; + } + if (image_format == 19) { + // 格式19:含调色板的位图 + const uint8_t height = font::read_u8(bitmap_data); + const uint8_t width = font::read_u8(bitmap_data + 1); + const int8_t bearing_x = font::read_i8(bitmap_data + 2); + const int8_t bearing_y = font::read_i8(bitmap_data + 3); + const uint8_t advance = font::read_u8(bitmap_data + 4); + + location.data = bitmap_data; // 包含完整头部 + location.data_format = image_format; + location.size = bitmap_length; + location.width = width; + location.height = height; + location.bearing_x = bearing_x; + location.bearing_y = bearing_y; + location.bits_per_pixel = 8; // 调色板索引 + + return location; + } + } + } + } + + // 未找到匹配的位图 + return std::nullopt; +} + +std::optional cbdt_renderer_t::decode_bitmap( + const bitmap_location_t& location) const { + + if (!location.data || location.size == 0) { + return std::nullopt; + } + + // 创建位图结构 + color_emoji_bitmap_t bitmap; + bitmap.width = location.width; + bitmap.height = location.height; + bitmap.x_offset = location.bearing_x; + bitmap.y_offset = location.bearing_y; + bitmap.data.resize(location.width * location.height * 4, 0); + + // 根据不同的格式解码位图 + if (location.data_format == 17 || location.data_format == 18) { + // 格式17/18: 直接RGBA位图 + if (location.size >= location.width * location.height * 4) { + std::memcpy(bitmap.data.data(), location.data, location.width * location.height * 4); + } else { + return std::nullopt; // 数据大小不足 + } + } else if (location.data_format == 19) { + // 格式19: 带调色板的位图 + const uint8_t* data = location.data; + + // 跳过SmallGlyphMetrics (5字节) + data += 5; + + const uint8_t num_palette_entries = font::read_u8(data); + const uint8_t color_ref_type = font::read_u8(data + 1); + + // 调色板数据 + const uint8_t* palette = data + 2; + + // 位图数据在调色板之后 + const uint8_t* image_data = palette + num_palette_entries * 4; + + // 如果是调色板索引格式 + if (color_ref_type == 1) { + for (uint16_t y = 0; y < location.height; y++) { + for (uint16_t x = 0; x < location.width; x++) { + const uint8_t index = image_data[y * location.width + x]; + if (index < num_palette_entries) { + const uint8_t* color = palette + index * 4; + const uint32_t pixel_offset = (y * location.width + x) * 4; + + bitmap.data[pixel_offset] = color[0]; // R + bitmap.data[pixel_offset + 1] = color[1]; // G + bitmap.data[pixel_offset + 2] = color[2]; // B + bitmap.data[pixel_offset + 3] = color[3]; // A + } + } + } + } else { + // 直接颜色格式 (不常见) + return std::nullopt; + } + } else { + // 不支持的格式 + return std::nullopt; + } + + return bitmap; +} diff --git a/src/mirage_render/font/font_renderer/cbdt_renderer.h b/src/mirage_render/font/font_renderer/cbdt_renderer.h new file mode 100644 index 0000000..28a525f --- /dev/null +++ b/src/mirage_render/font/font_renderer/cbdt_renderer.h @@ -0,0 +1,73 @@ +#pragma once +#include "font_renderer.h" +#include "font/font_utils.h" + +/** + * @class cbdt_renderer_t + * @brief 基于CBDT/CBLC表的彩色表情渲染器 + * + * 实现了Google CBDT/CBLC彩色字体格式的渲染,主要用于Android彩色表情 + * CBDT表存储位图数据,CBLC表存储位图位置信息 + */ +class cbdt_renderer_t : public color_emoji_renderer_t { +public: + /** + * @brief 检查字体是否支持CBDT/CBLC表 + * @param font_data 字体数据 + * @param font_offset 字体偏移 + * @return 是否支持 + */ + bool supports_font(const uint8_t* font_data, int font_offset) const override; + + /** + * @brief 渲染CBDT/CBLC彩色表情 + * @param font_info 字体信息 + * @param font_data 字体数据 + * @param font_offset 字体偏移 + * @param glyph_index 字形索引 + * @param font_size 字体大小 + * @return 彩色位图数据 + */ + std::optional render_emoji( + const stbtt_fontinfo& font_info, + const uint8_t* font_data, + int font_offset, + int32_t glyph_index, + float font_size) const override; + +private: + /** + * @brief 存储位图数据的位置和信息 + */ + struct bitmap_location_t { + const uint8_t* data = nullptr; // 位图数据起点 + uint32_t data_format = 0; // 数据格式 + uint32_t size = 0; // 数据大小 + uint16_t width = 0; // 位图宽度 + uint16_t height = 0; // 位图高度 + int16_t bearing_x = 0; // 水平原点偏移 + int16_t bearing_y = 0; // 垂直原点偏移 + uint8_t bits_per_pixel = 0; // 像素位深 + }; + + /** + * @brief 在CBLC/CBDT表中查找字形位图 + * @param cblc_data CBLC表数据 + * @param cbdt_data CBDT表数据 + * @param glyph_index 字形索引 + * @param target_size 目标尺寸 + * @return 位图位置信息 + */ + std::optional find_bitmap( + const uint8_t* cblc_data, + const uint8_t* cbdt_data, + int32_t glyph_index, + float target_size) const; + + /** + * @brief 解码CBDT格式位图 + * @param location 位图位置信息 + * @return 解码后的RGBA位图 + */ + std::optional decode_bitmap(const bitmap_location_t& location) const; +}; diff --git a/src/mirage_render/font/font_renderer/sbix_renderer.cpp b/src/mirage_render/font/font_renderer/sbix_renderer.cpp new file mode 100644 index 0000000..599f9f1 --- /dev/null +++ b/src/mirage_render/font/font_renderer/sbix_renderer.cpp @@ -0,0 +1,220 @@ +#include "sbix_renderer.h" +#ifdef HAS_STB_IMAGE +#include + +#include "stb_image.h" +#include "font/font_utils.h" + +bool sbix_renderer_t::supports_font(const uint8_t* font_data, int font_offset) const { + // 检查是否存在sbix表 + return font::find_table_offset(font_data, font_offset, "sbix") != 0; +} + +std::optional sbix_renderer_t::render_emoji( + const stbtt_fontinfo& font_info, + const uint8_t* font_data, + int font_offset, + int32_t glyph_index, + float font_size) const { + + // 检查参数有效性 + if (glyph_index == 0 || font_size <= 0.0f) { + return std::nullopt; + } + + // 查找sbix表 + const uint32_t sbix_offset = font::find_table_offset(font_data, font_offset, "sbix"); + if (sbix_offset == 0) { + return std::nullopt; + } + + const uint8_t* sbix_data = font_data + sbix_offset; + + // 查找最佳尺寸的位图 + auto bitmap_loc = find_best_bitmap(font_info, sbix_data, glyph_index, font_size); + if (!bitmap_loc) { + return std::nullopt; + } + + // 目前只支持PNG格式 + // sbix表中的graphic_type是4字节标记,通常为'png ' + if (bitmap_loc->graphic_type != 0x706E6720) { // 'png '的大端字节值 + return std::nullopt; + } + + // 解码PNG格式的位图 + auto bitmap = decode_png(bitmap_loc->data, bitmap_loc->size); + if (!bitmap) { + return std::nullopt; + } + + // 记录位图的原点偏移(可用于定位) + bitmap->x_offset = bitmap_loc->x_offset; + bitmap->y_offset = bitmap_loc->y_offset; + + return bitmap; +} + +std::optional sbix_renderer_t::find_best_bitmap( + const stbtt_fontinfo& font_info, + const uint8_t* sbix_data, + int32_t glyph_index, + float target_size) const { + + // 解析sbix表头部 + // 2字节:version + // 2字节:flags + // 4字节:numStrikes(trike数量,每个strike对应一个像素尺寸) + // 变长:strike偏移数组 + + const uint16_t version = font::read_u16(sbix_data); + const uint16_t flags = font::read_u16(sbix_data + 2); + const uint32_t num_strikes = font::read_u32(sbix_data + 4); + + if (num_strikes == 0) { + return std::nullopt; + } + + // 找到最佳匹配的strike(位图尺寸) + // 每一个strike包含一组相同尺寸的位图 + uint32_t best_strike_index = 0; + uint16_t best_ppem = 0; + uint16_t target_ppem = static_cast(target_size); + + // strike偏移数组 + const uint8_t* strike_offset_array = sbix_data + 8; + + // 遍历所有strike,找到最接近目标尺寸的strike + for (uint32_t i = 0; i < num_strikes; i++) { + const uint32_t strike_offset = font::read_u32(strike_offset_array + i * 4); + const uint8_t* strike_data = sbix_data + strike_offset; + + // 读取当前strike的ppem(像素每em) + const uint16_t ppem = font::read_u16(strike_data); + + // 找到最接近的尺寸 + if (best_ppem == 0 || + (ppem >= target_ppem && ppem < best_ppem) || + (best_ppem < target_ppem && ppem > best_ppem)) { + best_ppem = ppem; + best_strike_index = i; + } + } + + // 获取选中的strike数据 + const uint32_t strike_offset = font::read_u32(strike_offset_array + best_strike_index * 4); + const uint8_t* strike_data = sbix_data + strike_offset; + + // 读取strike头部 + // 2字节:ppem + // 2字节:ppi(每英寸像素数) + // 变长:glyphOffset数组(每个字形对应一个偏移) + + // 获取当前字体的最大字形ID + int num_glyphs = 0; + stbtt_GetFontBoundingBox(&font_info, nullptr, nullptr, nullptr, nullptr); // 确保已初始化 + if (font_info.numGlyphs > 0) { + num_glyphs = font_info.numGlyphs; + } else { + // 如果stbtt_fontinfo中没有,尝试从maxp表获取 + const uint32_t maxp_offset = font::find_table_offset( + reinterpret_cast(font_info.data), + 0, "maxp"); + if (maxp_offset) { + num_glyphs = font::read_u16(reinterpret_cast(font_info.data) + maxp_offset + 4); + } + + if (num_glyphs <= 0) { + return std::nullopt; // 无法确定字形数量 + } + } + + // 检查字形索引是否有效 + if (glyph_index >= num_glyphs) { + return std::nullopt; + } + + // 字形偏移数组 + const uint8_t* glyph_offset_array = strike_data + 4; + + // 获取当前字形的数据偏移和下一个字形的数据偏移 + const uint32_t glyph_offset = font::read_u32(glyph_offset_array + glyph_index * 4); + uint32_t next_glyph_offset; + + if (glyph_index + 1 < num_glyphs) { + next_glyph_offset = font::read_u32(glyph_offset_array + (glyph_index + 1) * 4); + } else { + // 如果是最后一个字形,使用strike的总大小作为边界 + const uint32_t next_strike_offset = (best_strike_index + 1 < num_strikes) ? + font::read_u32(strike_offset_array + (best_strike_index + 1) * 4) : + font::find_table_offset(reinterpret_cast(font_info.data), 0, "sbix") + font::read_u32(reinterpret_cast(font_info.data) + font::find_table_offset(reinterpret_cast(font_info.data), 0, "sbix") + 8); + + next_glyph_offset = next_strike_offset - strike_offset; + } + + // 如果当前字形没有位图数据或偏移无效 + if (glyph_offset >= next_glyph_offset || glyph_offset == 0) { + return std::nullopt; + } + + // 访问字形数据 + const uint8_t* glyph_data = strike_data + glyph_offset; + + // sbix字形记录格式: + // 2字节:originOffsetX(水平原点偏移) + // 2字节:originOffsetY(垂直原点偏移) + // 4字节:graphicType(图像格式,如'png ') + // 变长:图像数据 + + const int16_t x_offset = font::read_i16(glyph_data); + const int16_t y_offset = font::read_i16(glyph_data + 2); + const uint32_t graphic_type = font::read_u32(glyph_data + 4); + + // 计算位图数据大小 + const uint32_t bitmap_size = next_glyph_offset - glyph_offset - 8; + + // 返回位图位置信息 + bitmap_location_t location; + location.data = glyph_data + 8; // 跳过头部 + location.size = bitmap_size; + location.graphic_type = graphic_type; + location.x_offset = x_offset; + location.y_offset = y_offset; + + return location; +} + +std::optional sbix_renderer_t::decode_png( + const uint8_t* data, + size_t data_size) const { + + if (!data || data_size == 0) { + return std::nullopt; + } + + // 使用stb_image解码PNG数据 + int width, height, channels; + unsigned char* decoded = stbi_load_from_memory( + data, static_cast(data_size), + &width, &height, &channels, 4); // 强制转换为RGBA + + if (!decoded) { + return std::nullopt; + } + + // 创建并填充位图结构 + color_emoji_bitmap_t bitmap; + bitmap.width = width; + bitmap.height = height; + bitmap.data.resize(width * height * 4); + + // 复制解码后的数据 + std::memcpy(bitmap.data.data(), decoded, width * height * 4); + + // 释放stb_image分配的内存 + stbi_image_free(decoded); + + return bitmap; +} + +#endif diff --git a/src/mirage_render/font/font_renderer/sbix_renderer.h b/src/mirage_render/font/font_renderer/sbix_renderer.h new file mode 100644 index 0000000..d903f08 --- /dev/null +++ b/src/mirage_render/font/font_renderer/sbix_renderer.h @@ -0,0 +1,75 @@ +#pragma once +#include "font_renderer.h" + +#ifdef HAS_STB_IMAGE + +/** + * @class sbix_renderer_t + * @brief 基于sbix表的彩色表情渲染器 + * + * 实现了Apple sbix彩色字体格式的渲染,主要用于Apple Color Emoji字体 + * sbix表存储预渲染的位图表情,通常是PNG格式图像 + */ +class sbix_renderer_t : public color_emoji_renderer_t { +public: + /** + * @brief 检查字体是否支持sbix表 + * @param font_data 字体数据 + * @param font_offset 字体偏移 + * @return 是否支持 + */ + bool supports_font(const uint8_t* font_data, int font_offset) const override; + + /** + * @brief 渲染sbix彩色表情 + * @param font_info 字体信息 + * @param font_data 字体数据 + * @param font_offset 字体偏移 + * @param glyph_index 字形索引 + * @param font_size 字体大小 + * @return 彩色位图数据 + */ + std::optional render_emoji( + const stbtt_fontinfo& font_info, + const uint8_t* font_data, + int font_offset, + int32_t glyph_index, + float font_size) const override; + +private: + /** + * @brief 存储位图数据的位置和信息 + */ + struct bitmap_location_t { + const uint8_t* data = nullptr; // 位图数据起点 + uint32_t size = 0; // 数据大小 + uint32_t graphic_type = 0; // 数据格式 (如 'png ') + int16_t x_offset = 0; // 水平原点偏移 + int16_t y_offset = 0; // 垂直原点偏移 + }; + + /** + * @brief 在sbix表中查找最适合的位图 + * @param sbix_data sbix表数据 + * @param glyph_index 字形索引 + * @param target_size 目标尺寸 + * @return 位图位置信息 + */ + std::optional find_best_bitmap( + const stbtt_fontinfo& font_info, + const uint8_t* sbix_data, + int32_t glyph_index, + float target_size) const; + + /** + * @brief 解码PNG格式位图 + * @param data PNG数据 + * @param data_size 数据大小 + * @return 解码后的RGBA位图 + */ + std::optional decode_png( + const uint8_t* data, + size_t data_size) const; +}; + +#endif \ No newline at end of file diff --git a/src/mirage_render/font/font_renderer/svg_renderer.cpp b/src/mirage_render/font/font_renderer/svg_renderer.cpp new file mode 100644 index 0000000..aafddbb --- /dev/null +++ b/src/mirage_render/font/font_renderer/svg_renderer.cpp @@ -0,0 +1,169 @@ +#include "svg_renderer.h" + +#ifdef HAS_NANOSVG + +#include +#include // 用于解压缩SVGZ + +// 使用NanoSVG作为SVG解析和渲染器 +#define NANOSVG_IMPLEMENTATION +#include "nanosvg.h" +#define NANOSVGRAST_IMPLEMENTATION +#include "nanosvgrast.h" + +svg_renderer_t::svg_renderer_t() { + // 初始化任何需要的资源 +} + +svg_renderer_t::~svg_renderer_t() { + // 清理资源 +} + +bool svg_renderer_t::supports_font(const uint8_t* font_data, int font_offset) const { + // 检查字体是否包含SVG表 + return font::find_table_offset(font_data, font_offset, "SVG ") != 0; +} + +std::optional svg_renderer_t::render_emoji( + const stbtt_fontinfo& font_info, + const uint8_t* font_data, + int font_offset, + int32_t glyph_index, + float font_size) const { + + // 验证参数 + if (glyph_index == 0 || font_size <= 0.0f) { + return std::nullopt; + } + + // 使用stb_truetype提供的API获取SVG数据 + const char* svg_data = nullptr; + int result = stbtt_GetGlyphSVG(&font_info, glyph_index, &svg_data); + + if (result == 0 || svg_data == nullptr) { + return std::nullopt; // 没有找到SVG数据 + } + + // 计算渲染尺寸 + const float scale = stbtt_ScaleForPixelHeight(&font_info, font_size); + int x0, y0, x1, y1; + stbtt_GetGlyphBitmapBox(&font_info, glyph_index, scale, scale, &x0, &y0, &x1, &y1); + + int width = x1 - x0; + int height = y1 - y0; + + // 确保尺寸有效 + if (width <= 0 || height <= 0) { + width = static_cast(font_size); + height = static_cast(font_size); + } + + // 渲染SVG为位图 + return render_svg_to_bitmap(svg_data, width, height); +} + +std::optional svg_renderer_t::render_svg_to_bitmap( + const char* svg_data, + int width, + int height) const { + + if (!svg_data || width <= 0 || height <= 0) { + return std::nullopt; + } + + // 检查是否是SVGZ (gzip压缩的SVG) + const uint8_t* data_bytes = reinterpret_cast(svg_data); + bool is_compressed = data_bytes[0] == 0x1F && data_bytes[1] == 0x8B; + + // 准备SVG字符串 + std::string svg_str; + + if (is_compressed) { + // 获取压缩数据的长度(近似值,因为stbtt_GetGlyphSVG不返回长度) + size_t compressed_size = 0; + while (data_bytes[compressed_size]) { + compressed_size++; + // 安全检查,防止无限循环 + if (compressed_size > 10 * 1024 * 1024) { // 10MB限制 + return std::nullopt; + } + } + + // 解压缩SVGZ + z_stream zs = {0}; + if (inflateInit2(&zs, 15 + 32) != Z_OK) { + return std::nullopt; + } + + // 设置输入 + zs.avail_in = static_cast(compressed_size); + zs.next_in = const_cast(data_bytes); + + // 准备输出缓冲区 + const size_t buffer_size = 16384; + char buffer[buffer_size]; + + // 解压缩循环 + int ret; + do { + zs.avail_out = buffer_size; + zs.next_out = reinterpret_cast(buffer); + + ret = inflate(&zs, Z_NO_FLUSH); + if (ret == Z_STREAM_ERROR) { + inflateEnd(&zs); + return std::nullopt; + } + + svg_str.append(buffer, buffer_size - zs.avail_out); + } while (zs.avail_out == 0); + + inflateEnd(&zs); + } else { + // 直接使用未压缩的SVG数据 + svg_str = svg_data; + } + + // 使用NanoSVG解析SVG + NSVGimage* svg_image = nsvgParse(const_cast(svg_str.c_str()), "px", 96.0f); + if (!svg_image) { + return std::nullopt; + } + + // 创建栅格化器 + NSVGrasterizer* rast = nsvgCreateRasterizer(); + if (!rast) { + nsvgDelete(svg_image); + return std::nullopt; + } + + // 创建位图结构 + color_emoji_bitmap_t bitmap; + bitmap.width = width; + bitmap.height = height; + bitmap.data.resize(width * height * 4, 0); + + // 栅格化SVG到位图 + float scale_factor = std::min( + static_cast(width) / svg_image->width, + static_cast(height) / svg_image->height + ); + + nsvgRasterize( + rast, + svg_image, + 0, 0, + scale_factor, + bitmap.data.data(), + width, height, + width * 4 + ); + + // 清理资源 + nsvgDeleteRasterizer(rast); + nsvgDelete(svg_image); + + return bitmap; +} + +#endif \ No newline at end of file diff --git a/src/mirage_render/font/font_renderer/svg_renderer.h b/src/mirage_render/font/font_renderer/svg_renderer.h new file mode 100644 index 0000000..c9a9a6b --- /dev/null +++ b/src/mirage_render/font/font_renderer/svg_renderer.h @@ -0,0 +1,65 @@ +#pragma once +#include "font_renderer.h" +#include "font/font_utils.h" +#include + +#ifdef HAS_NANOSVG + +/** + * @class svg_renderer_t + * @brief 基于SVG表的彩色表情渲染器,使用stb_truetype提供的SVG接口 + * + * 实现了OpenType SVG彩色字体格式的渲染 + * SVG表存储矢量图形表情,支持无损缩放 + */ +class svg_renderer_t : public color_emoji_renderer_t { +public: + /** + * @brief 构造函数 + */ + svg_renderer_t(); + + /** + * @brief 析构函数 + */ + ~svg_renderer_t(); + + /** + * @brief 检查字体是否支持SVG表 + * @param font_data 字体数据 + * @param font_offset 字体偏移 + * @return 是否支持 + */ + bool supports_font(const uint8_t* font_data, int font_offset) const override; + + /** + * @brief 渲染SVG彩色表情 + * @param font_info 字体信息 + * @param font_data 字体数据 + * @param font_offset 字体偏移 + * @param glyph_index 字形索引 + * @param font_size 字体大小 + * @return 彩色位图数据 + */ + std::optional render_emoji( + const stbtt_fontinfo& font_info, + const uint8_t* font_data, + int font_offset, + int32_t glyph_index, + float font_size) const override; + +private: + /** + * @brief 渲染SVG为位图 + * @param svg_data SVG数据 + * @param width 目标宽度 + * @param height 目标高度 + * @return 渲染后的位图 + */ + std::optional render_svg_to_bitmap( + const char* svg_data, + int width, + int height) const; +}; + +#endif diff --git a/src/mirage_render/font/font_type.h b/src/mirage_render/font/font_type.h index b9adb8b..d54b87a 100644 --- a/src/mirage_render/font/font_type.h +++ b/src/mirage_render/font/font_type.h @@ -154,7 +154,9 @@ struct text_layout_t { * @brief 彩色表情位图 */ struct color_emoji_bitmap_t { - std::vector data; // RGBA像素数据 - int width = 0; // 宽度 - int height = 0; // 高度 + int width = 0; // 位图宽度 + int height = 0; // 位图高度 + int16_t x_offset = 0; // 水平原点偏移 + int16_t y_offset = 0; // 垂直原点偏移 + std::vector data; // RGBA数据,每个像素4字节 }; diff --git a/src/mirage_render/font/font_utils.h b/src/mirage_render/font/font_utils.h index fb5b607..f1a8d28 100644 --- a/src/mirage_render/font/font_utils.h +++ b/src/mirage_render/font/font_utils.h @@ -2,6 +2,20 @@ #include namespace font { + /** + * @brief 读取无符号8位整数 + * @param p 数据指针 + * @return 8位无符号整数 + */ + static uint8_t read_u8(const uint8_t* p) { return p[0]; } + + /** + * @brief 读取有符号8位整数 + * @param p 数据指针 + * @return 8位有符号整数 + */ + static int8_t read_i8(const uint8_t* p) { return static_cast(p[0]); } + /** * @brief 读取无符号短整型(2字节,大端序) * @param p 数据指针 @@ -9,6 +23,13 @@ namespace font { */ static uint16_t read_u16(const uint8_t* p) { return static_cast(p[0] << 8 | p[1]); } + /** + * @brief 读取有符号短整型(2字节,大端序) + * @param p 数据指针 + * @return 16位有符号整数 + */ + static int16_t read_i16(const uint8_t* p) { return static_cast(p[0] << 8 | p[1]); } + /** * @brief 读取无符号整型(4字节,大端序) * @param p 数据指针