添加svg表情支持

This commit is contained in:
2025-03-28 13:45:15 +08:00
parent 2f9e76e4aa
commit 976994e85b
10 changed files with 941 additions and 8 deletions

View File

@@ -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的自定义命令

View File

@@ -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<standard_bitmap_renderer_t>();
// 创建彩色表情渲染器
#ifdef HAS_STB_IMAGE
emoji_renderers_.push_back(std::make_unique<sbix_renderer_t>());
#endif
emoji_renderers_.push_back(std::make_unique<cbdt_renderer_t>());
emoji_renderers_.push_back(std::make_unique<colr_renderer_t>());
// 未来可以在这里添加其他类型的彩色表情渲染器
// emoji_renderers_.push_back(std::make_unique<cbdt_renderer_t>());
// emoji_renderers_.push_back(std::make_unique<sbix_renderer_t>());
// emoji_renderers_.push_back(std::make_unique<svg_renderer_t>());
#ifdef HAS_NANOSVG
emoji_renderers_.push_back(std::make_unique<svg_renderer_t>());
#endif
}
bool font_face_t::load_font_data(const std::wstring& font_path) {

View File

@@ -0,0 +1,295 @@
#include "cbdt_renderer.h"
#include <algorithm>
#include <cstring>
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<color_emoji_bitmap_t> 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::bitmap_location_t> 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字节numSizesbitmapSize数量
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<uint16_t>(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<color_emoji_bitmap_t> 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;
}

View File

@@ -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<color_emoji_bitmap_t> 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<bitmap_location_t> 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<color_emoji_bitmap_t> decode_bitmap(const bitmap_location_t& location) const;
};

View File

@@ -0,0 +1,220 @@
#include "sbix_renderer.h"
#ifdef HAS_STB_IMAGE
#include <cstring>
#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<color_emoji_bitmap_t> 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::bitmap_location_t> 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字节numStrikestrike数量每个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<uint16_t>(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<const uint8_t*>(font_info.data),
0, "maxp");
if (maxp_offset) {
num_glyphs = font::read_u16(reinterpret_cast<const uint8_t*>(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<const uint8_t*>(font_info.data), 0, "sbix") + font::read_u32(reinterpret_cast<const uint8_t*>(font_info.data) + font::find_table_offset(reinterpret_cast<const uint8_t*>(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<color_emoji_bitmap_t> 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<int>(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

View File

@@ -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<color_emoji_bitmap_t> 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<bitmap_location_t> 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<color_emoji_bitmap_t> decode_png(
const uint8_t* data,
size_t data_size) const;
};
#endif

View File

@@ -0,0 +1,169 @@
#include "svg_renderer.h"
#ifdef HAS_NANOSVG
#include <cstring>
#include <zlib.h> // 用于解压缩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<color_emoji_bitmap_t> 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<int>(font_size);
height = static_cast<int>(font_size);
}
// 渲染SVG为位图
return render_svg_to_bitmap(svg_data, width, height);
}
std::optional<color_emoji_bitmap_t> 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<const uint8_t*>(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<uInt>(compressed_size);
zs.next_in = const_cast<uint8_t*>(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<uint8_t*>(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<char*>(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<float>(width) / svg_image->width,
static_cast<float>(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

View File

@@ -0,0 +1,65 @@
#pragma once
#include "font_renderer.h"
#include "font/font_utils.h"
#include <string>
#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<color_emoji_bitmap_t> 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<color_emoji_bitmap_t> render_svg_to_bitmap(
const char* svg_data,
int width,
int height) const;
};
#endif

View File

@@ -154,7 +154,9 @@ struct text_layout_t {
* @brief 彩色表情位图
*/
struct color_emoji_bitmap_t {
std::vector<uint8_t> 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<uint8_t> data; // RGBA数据每个像素4字节
};

View File

@@ -2,6 +2,20 @@
#include <cstdint>
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<int8_t>(p[0]); }
/**
* @brief 读取无符号短整型2字节大端序
* @param p 数据指针
@@ -9,6 +23,13 @@ namespace font {
*/
static uint16_t read_u16(const uint8_t* p) { return static_cast<uint16_t>(p[0] << 8 | p[1]); }
/**
* @brief 读取有符号短整型2字节大端序
* @param p 数据指针
* @return 16位有符号整数
*/
static int16_t read_i16(const uint8_t* p) { return static_cast<int16_t>(p[0] << 8 | p[1]); }
/**
* @brief 读取无符号整型4字节大端序
* @param p 数据指针