抽象字体接口

This commit is contained in:
Nanako 2025-04-02 00:07:53 +08:00
parent e966dadc70
commit d5465f1953
46 changed files with 691 additions and 2291 deletions

8
.gitmodules vendored

@ -1,6 +1,6 @@
[submodule "third_party/freetype"]
path = third_party/freetype
[submodule "src/mirage_render/font/freetype_font/freetype"]
path = src/mirage_render/font/freetype_font/freetype
url = https://github.com/freetype/freetype
[submodule "third_party/harfbuzz"]
path = third_party/harfbuzz
[submodule "src/mirage_render/font/freetype_font/harfbuzz"]
path = src/mirage_render/font/freetype_font/harfbuzz
url = https://github.com/harfbuzz/harfbuzz

@ -50,8 +50,6 @@ else ()
endif ()
add_subdirectory(src/sokol)
add_subdirectory(third_party/freetype)
add_subdirectory(third_party/harfbuzz)
add_subdirectory(src/mirage_render)
add_subdirectory(src/mirage_image)
add_subdirectory(src/mirage_core)

@ -3,7 +3,6 @@
#include "mirage.h"
#include "window/mwindow.h"
#include "font/font_system.h"
#include "font/font_renderer/colr_renderer.h"
#include "widget/widget_new.h"
#include "widget/compound_widget/mbutton.h"
#include "widget/leaf_widget/mtext_block.h"
@ -15,52 +14,6 @@
int main(int argc, char* argv[]) {
mirage_app::get().init();
FT_Library ft_library;
if (FT_Init_FreeType(&ft_library)) {
std::cerr << "Failed to initialize FreeType library" << std::endl;
return -1;
}
FT_Library_SetLcdFilter(ft_library, FT_LCD_FILTER_DEFAULT);
FT_Face ft_face;
if (FT_New_Face(ft_library, "C:/Windows/Fonts/msyh.ttc", 0, &ft_face)) {
std::cerr << "Failed to load font face" << std::endl;
return -1;
}
FT_Library_SetLcdFilterWeights(ft_library, nullptr);
FT_Library_SetLcdGeometry(ft_library, nullptr);
FT_Library_SetLcdFilter(ft_library, FT_LCD_FILTER_DEFAULT);
// 渲染一个字符
FT_Set_Pixel_Sizes(ft_face, 0, 24);
FT_Load_Char(ft_face, U'', FT_LOAD_RENDER);
FT_Render_Glyph(ft_face->glyph, FT_RENDER_MODE_LCD);
// 获取渲染的位图
FT_Bitmap bitmap = ft_face->glyph->bitmap;
if (bitmap.buffer) {
std::cout << "Bitmap width: " << bitmap.width << ", height: " << bitmap.rows << std::endl;
} else {
std::cerr << "Failed to get bitmap" << std::endl;
}
// 保存位图到文件
color_emoji_bitmap_t bitmap_;
// 将bitmap数据转换为color_emoji_bitmap_t格式
bitmap_.width = bitmap.width;
bitmap_.height = bitmap.rows;
bitmap_.data.resize(bitmap.width * bitmap.rows * 4); // RGBA格式
for (int y = 0; y < bitmap.rows; ++y) {
for (int x = 0; x < bitmap.width; ++x) {
int index = y * bitmap.width + x;
bitmap_.data[index * 4 + 0] = bitmap.buffer[index]; // R
bitmap_.data[index * 4 + 1] = bitmap.buffer[index]; // G
bitmap_.data[index * 4 + 2] = bitmap.buffer[index]; // B
bitmap_.data[index * 4 + 3] = bitmap.buffer[index]; // A
}
}
save_bitmap("output.bmp", bitmap_);
FT_Done_Face(ft_face);
auto& manager = font_manager::instance();
manager.add_font(L"C:/Users/46944/AppData/Local/Microsoft/Windows/Fonts/MapleMono-NF-CN-Regular.ttf");
manager.add_font(L"C:/Windows/Fonts/msyh.ttc");

@ -4,6 +4,7 @@
#include <iostream>
#include <thread>
#include "font/font_system.h"
#include "window/mwindow.h"
#include "misc/mirage_scoped_duration_timer.h"
#include "platform_window/platform_window.h"
@ -91,4 +92,5 @@ void mirage_app::run() {
texture_sampler_builder::clear();
render_context->cleanup();
delete render_context;
font_manager::instance().destroy();
}

@ -1,12 +1,11 @@
project(mirage_render)
set(SOURCE_FILES)
retrieve_files(${CMAKE_CURRENT_SOURCE_DIR}/src SOURCE_FILES)
add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(${PROJECT_NAME} PUBLIC mirage_core sokol freetype)
target_link_libraries(${PROJECT_NAME} PUBLIC mirage_core sokol freetype mirage_image)
option(MIRAGE_SVG_EMOJI "Use svg emoji" ON)
if (MIRAGE_SVG_EMOJI)
@ -16,13 +15,27 @@ if (MIRAGE_SVG_EMOJI)
target_compile_definitions(${PROJECT_NAME} PUBLIC HAS_NANOSVG)
endif ()
option(MIRAGE_STB_IMAGE_LOADER "Use stb load image" ON)
if (MIRAGE_STB_IMAGE_LOADER)
add_subdirectory(stb_image_loader)
#
set(MIRAGE_IMAGE_LOAD_BACKEND "STB_IMAGE" CACHE STRING "选择图像加载器")
set_property(CACHE MIRAGE_IMAGE_LOAD_BACKEND PROPERTY STRINGS "STB_IMAGE" "CUSTOM")
if (MIRAGE_IMAGE_LOAD_BACKEND STREQUAL "STB_IMAGE")
add_subdirectory(image/stb_image_loader)
target_link_libraries(${PROJECT_NAME} PUBLIC stb_image_loader)
target_compile_definitions(${PROJECT_NAME} PUBLIC HAS_STB_IMAGE)
endif ()
#
set(MIRAGE_FONT_BACKEND "STB_TRUETYPE" CACHE STRING "选择字体后端")
set_property(CACHE MIRAGE_FONT_BACKEND PROPERTY STRINGS "STB_TRUETYPE" "FREETYPE" "CUSTOM")
if (MIRAGE_FONT_BACKEND STREQUAL "STB_TRUETYPE")
add_subdirectory(font/stb_truetype_font)
target_link_libraries(${PROJECT_NAME} PUBLIC stb_truetype_font)
target_compile_definitions(${PROJECT_NAME} PUBLIC HAS_STB_TRUETYPE)
elseif (MIRAGE_FONT_BACKEND STREQUAL "FREETYPE")
add_subdirectory(font/freetype_font)
target_link_libraries(${PROJECT_NAME} PUBLIC freetype_font)
target_compile_definitions(${PROJECT_NAME} PUBLIC HAS_FREETYPE)
endif ()
# shader
add_mirage_shader_directory(${CMAKE_CURRENT_SOURCE_DIR}/shaders)

@ -0,0 +1,10 @@
project(freetype_font)
add_subdirectory(freetype)
add_subdirectory(harfbuzz)
set(SRC_FILES)
retrieve_files(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_FILES)
add_library(${PROJECT_NAME} STATIC ${SRC_FILES})
target_link_libraries(${PROJECT_NAME} PUBLIC freetype harfbuzz mirage_render)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)

@ -0,0 +1,163 @@
//
// Created by 46944 on 25-4-1.
//
#include "freetype_interface.h"
#include "freetype/ftglyph.h"
#include "interface/image_interface.h"
FT_Library library_;
void freetype_bitmap_deleter(image_heap_t* in_data) {
if (in_data == nullptr) {
return;
}
if (in_data->data != nullptr) {
const auto data = (uint8_t*)in_data->data;
delete[] data;
in_data->data = nullptr;
}
delete in_data;
}
std::string freetype_interface::get_font_name() const {
if (face_ == nullptr) {
return "unknown font";
}
return face_->family_name ? face_->family_name : "unknown font";
}
std::string freetype_interface::get_font_family() const {
if (face_ == nullptr) {
return "unknown font";
}
return face_->family_name ? face_->family_name : "unknown font";
}
std::string freetype_interface::get_font_style() const {
if (face_ == nullptr) {
return "unknown font";
}
return face_->style_name ? face_->style_name : "unknown font";
}
bool freetype_interface::supports_color_emoji() const {
return FT_HAS_COLOR(face_) || FT_HAS_SBIX(face_) || FT_HAS_SVG(face_);
}
font_v_metrics_t freetype_interface::get_metrics() const {
font_v_metrics_t metrics{};
metrics.ascent = face_->ascender;
metrics.descent = face_->descender;
metrics.line_gap = face_->height - (face_->ascender - face_->descender);
return metrics;
}
std::shared_ptr<image_heap_t> freetype_interface::get_glyph_image(int32_t in_glyph_id) const {
if (in_glyph_id == 0) {
return nullptr;
}
// 加载字形
FT_Load_Glyph(face_, in_glyph_id, FT_LOAD_RENDER);
if (face_->glyph->format != FT_GLYPH_FORMAT_BITMAP) {
return nullptr;
}
// 创建位图
// 这里使用了一个自定义的删除器来释放位图数据
std::shared_ptr<image_heap_t> image(new image_heap_t(), freetype_bitmap_deleter);
image->width = face_->glyph->bitmap.width;
image->height = face_->glyph->bitmap.rows;
image->pixel_format = SG_PIXELFORMAT_R8;
image->data = new uint8_t[image->width * image->height];
std::memcpy(image->data, face_->glyph->bitmap.buffer, image->width * image->height);
return image;
}
std::shared_ptr<image_heap_t> freetype_interface::get_emoji_image(int32_t in_glyph_id) const {
if (in_glyph_id == 0) {
return nullptr;
}
// 加载字形
FT_Load_Glyph(face_, in_glyph_id, FT_LOAD_COLOR);
if (face_->glyph->format != FT_GLYPH_FORMAT_BITMAP) {
return nullptr;
}
// 创建位图
// 这里使用了一个自定义的删除器来释放位图数据
std::shared_ptr<image_heap_t> image(new image_heap_t(), freetype_bitmap_deleter);
image->width = face_->glyph->bitmap.width;
image->height = face_->glyph->bitmap.rows;
image->pixel_format = SG_PIXELFORMAT_RGBA8;
image->data = new uint8_t[image->width * image->height * 4];
std::memcpy(image->data, face_->glyph->bitmap.buffer, image->width * image->height);
return image;
}
uint32_t freetype_interface::find_glyph_index(uint32_t in_unicode_codepoint) const {
return FT_Get_Char_Index(face_, in_unicode_codepoint);
}
float freetype_interface::get_kerning(uint32_t in_first_glyph_id, uint32_t in_second_glyph_id) const {
FT_Vector kerning;
if (FT_Get_Kerning(face_, in_first_glyph_id, in_second_glyph_id, FT_KERNING_DEFAULT, &kerning) != 0) {
return 0.0f;
}
return static_cast<float>(kerning.x) / 64.0f; // FreeType返回的单位是1/64像素
}
glyph_shaped_t freetype_interface::shape_glyph(uint32_t in_glyph_id) const {
glyph_shaped_t out{};
FT_Load_Glyph(face_, in_glyph_id, FT_LOAD_NO_BITMAP);
FT_Glyph glyph;
if (FT_Get_Glyph(face_->glyph, &glyph) != 0) {
return out;
}
FT_Done_Glyph(glyph);
out.glyph_index = in_glyph_id;
out.advance.x() = static_cast<float>(face_->glyph->advance.x) / 64.0f;
out.advance.y() = static_cast<float>(face_->glyph->advance.y) / 64.0f;
out.offset.x() = static_cast<float>(face_->glyph->metrics.horiBearingX) / 64.0f;
out.offset.y() = static_cast<float>(face_->glyph->metrics.horiBearingY) / 64.0f;
out.rect.set_position({ static_cast<int32_t>(face_->glyph->metrics.horiBearingX),
static_cast<int32_t>(face_->glyph->metrics.horiBearingY) });
out.rect.set_size({ static_cast<int32_t>(face_->glyph->metrics.width),
static_cast<int32_t>(face_->glyph->metrics.height) });
return out;
}
bool freetype_interface::on_load() {
return FT_New_Memory_Face(library_, font_data_->get_u8(), font_data_->get_size(), 0, &face_) == 0;
}
void freetype_interface::on_set_font_size(float in_size) {
font_face_interface::on_set_font_size(in_size);
FT_Set_Pixel_Sizes(face_, 0, static_cast<FT_UInt>(in_size));
}
bool init_font_system() {
if (FT_Init_FreeType(&library_)) {
return false;
}
return true;
}
void destroy_font_system() {
FT_Done_FreeType(library_);
}
std::shared_ptr<font_face_interface> create_font_face(const std::filesystem::path& in_path) {
auto font_face = std::make_shared<freetype_interface>();
if (!font_face->load(in_path)) {
return nullptr;
}
return font_face;
}

@ -0,0 +1,23 @@
#pragma once
#include "freetype/freetype.h"
#include "interface/font_interface.h"
class freetype_interface : public font_face_interface {
public:
[[nodiscard]] std::string get_font_name() const override;
[[nodiscard]] std::string get_font_family() const override;
[[nodiscard]] std::string get_font_style() const override;
[[nodiscard]] bool supports_color_emoji() const override;
[[nodiscard]] font_v_metrics_t get_metrics() const override;
[[nodiscard]] std::shared_ptr<image_heap_t> get_glyph_image(int32_t in_glyph_id) const override;
[[nodiscard]] std::shared_ptr<image_heap_t> get_emoji_image(int32_t in_glyph_id) const override;
[[nodiscard]] uint32_t find_glyph_index(uint32_t in_unicode_codepoint) const override;
[[nodiscard]] float get_kerning(uint32_t in_first_glyph_id, uint32_t in_second_glyph_id) const override;
[[nodiscard]] glyph_shaped_t shape_glyph(uint32_t in_glyph_id) const override;
protected:
bool on_load() override;
void on_set_font_size(float in_size) override;
protected:
FT_Face face_{};
};

@ -0,0 +1,8 @@
project(stb_truetype_font)
set(SRC_FILES)
retrieve_files(${CMAKE_CURRENT_SOURCE_DIR} SRC_FILES)
add_library(${PROJECT_NAME} STATIC ${SRC_FILES})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(${PROJECT_NAME} PUBLIC mirage_render)

@ -0,0 +1,212 @@
#include "stb_truetype_interface.h"
#include "font/font_utils.h"
#include "interface/image_interface.h"
void stb_truetype_deleter(image_heap_t* in_data) {
if (in_data == nullptr) {
return;
}
if (in_data->data != nullptr) {
stbtt_FreeBitmap((uint8_t*)in_data->data, nullptr);
}
delete in_data;
}
std::string stb_font_face_t::get_font_name() const {
const auto* data = static_cast<const uint8_t*>(font_data_->get_data());
const int font_offset = stbtt_GetFontOffsetForIndex(data, 0);
if (font_offset < 0) {
goto error;
}
uint32_t name_offset = font::find_table_offset(data, font_offset, "name");
if (name_offset == 0) {
goto error;
}
const uint8_t* name_table = data + name_offset;
const uint16_t count = font::read_u16(name_table + 2);
const uint16_t string_offset = font::read_u16(name_table + 4);
for (uint16_t i = 0; i < count; ++i) {
const uint8_t* record = name_table + 6 + 12 * i;
const uint16_t name_id = font::read_u16(record + 6);
if (name_id == 4 || name_id == 1) {
const uint16_t length = font::read_u16(record + 8);
const uint16_t offset = font::read_u16(record + 10);
if (length > 0) {
const uint8_t* string_data = name_table + string_offset + offset;
std::string name(reinterpret_cast<const char*>(string_data), length);
return name;
}
}
}
error:
return "unknown font";
}
std::string stb_font_face_t::get_font_family() const {
const auto* data = static_cast<const uint8_t*>(font_data_->get_data());
const int font_offset = stbtt_GetFontOffsetForIndex(data, 0);
if (font_offset < 0) {
goto error;
}
uint32_t name_offset = font::find_table_offset(data, font_offset, "name");
if (name_offset == 0) {
goto error;
}
const uint8_t* name_table = data + name_offset;
const uint16_t count = font::read_u16(name_table + 2);
const uint16_t string_offset = font::read_u16(name_table + 4);
for (uint16_t i = 0; i < count; ++i) {
const uint8_t* record = name_table + 6 + 12 * i;
const uint16_t name_id = font::read_u16(record + 6);
if (name_id == 1) {
const uint16_t length = font::read_u16(record + 8);
const uint16_t offset = font::read_u16(record + 10);
if (length > 0) {
const uint8_t* string_data = name_table + string_offset + offset;
std::string name(reinterpret_cast<const char*>(string_data), length);
return name;
}
}
}
error:
return "unknown font";
}
std::string stb_font_face_t::get_font_style() const {
const auto* data = static_cast<const uint8_t*>(font_data_->get_data());
const int font_offset = stbtt_GetFontOffsetForIndex(data, 0);
if (font_offset < 0) {
goto error;
}
uint32_t name_offset = font::find_table_offset(data, font_offset, "name");
if (name_offset == 0) {
goto error;
}
const uint8_t* name_table = data + name_offset;
const uint16_t count = font::read_u16(name_table + 2);
const uint16_t string_offset = font::read_u16(name_table + 4);
for (uint16_t i = 0; i < count; ++i) {
const uint8_t* record = name_table + 6 + 12 * i;
const uint16_t name_id = font::read_u16(record + 6);
if (name_id == 2) {
const uint16_t length = font::read_u16(record + 8);
const uint16_t offset = font::read_u16(record + 10);
if (length > 0) {
const uint8_t* string_data = name_table + string_offset + offset;
std::string name(reinterpret_cast<const char*>(string_data), length);
return name;
}
}
}
error:
return "unknown font";
}
bool stb_font_face_t::supports_color_emoji() const {
// stb_truetype不支持彩色表情符号
return false;
}
font_v_metrics_t stb_font_face_t::get_metrics() const {
int32_t ascent, descent, line_gap;
// 尝试使用OS2表中的度量这通常更准确
if (!stbtt_GetFontVMetricsOS2(&font_info_, &ascent, &descent, &line_gap)) {
// 如果失败使用默认的VMetrics
stbtt_GetFontVMetrics(&font_info_, &ascent, &descent, &line_gap);
}
font_v_metrics_t metrics{};
metrics.ascent = static_cast<float>(ascent) * scale_;
metrics.descent = static_cast<float>(descent) * scale_;
metrics.line_gap = static_cast<float>(line_gap) * scale_;
return metrics;
}
std::shared_ptr<image_heap_t> stb_font_face_t::get_glyph_image(int32_t in_glyph_id) const {
if (in_glyph_id == 0) {
return nullptr;
}
int32_t width, height, xoff, yoff;
// 获取字形图像
const auto bitmap = stbtt_GetGlyphBitmap(&font_info_, scale_, scale_, in_glyph_id, &width, &height, &xoff, &yoff);
// 创建位图
std::shared_ptr<image_heap_t> image(new image_heap_t(), stb_truetype_deleter);
image->width = width;
image->height = height;
image->pixel_format = SG_PIXELFORMAT_R8;
image->data = bitmap;
return image;
}
std::shared_ptr<image_heap_t> stb_font_face_t::get_emoji_image(int32_t in_glyph_id) const {
return nullptr;
}
uint32_t stb_font_face_t::find_glyph_index(uint32_t in_unicode_codepoint) const {
return stbtt_FindGlyphIndex(&font_info_, in_unicode_codepoint);
}
float stb_font_face_t::get_kerning(uint32_t in_first_glyph_id, uint32_t in_second_glyph_id) const {
return (float)stbtt_GetGlyphKernAdvance(&font_info_, in_first_glyph_id, in_second_glyph_id) * scale_;
}
glyph_shaped_t stb_font_face_t::shape_glyph(uint32_t in_glyph_id) const {
int32_t advance, lsb;
stbtt_GetGlyphHMetrics(&font_info_, in_glyph_id, &advance, &lsb);
// 获取字形的边界框
int32_t x0, y0, x1, y1;
stbtt_GetGlyphBox(&font_info_, in_glyph_id, &x0, &y0, &x1, &y1);
glyph_shaped_t out;
out.glyph_index = in_glyph_id;
out.advance.x() = (float)advance * scale_;
out.advance.y() = 0.0f;
out.offset.x() = (float)lsb * scale_;
out.offset.y() = 0.0f;
out.rect.set_position({ x0, y0 });
out.rect.set_size({ x1 - x0, y1 - y0 });
return out;
}
bool stb_font_face_t::on_load() {
const auto offset = stbtt_GetFontOffsetForIndex(font_data_->get_u8(), 0);
return stbtt_InitFont(&font_info_, font_data_->get_u8(), offset) != 0;
}
void stb_font_face_t::on_set_font_size(float in_size) {
font_face_interface::on_set_font_size(in_size);
scale_ = get_scale_for_pixel_height(in_size);
}
bool init_font_system() {
// stb_truetype不需要初始化
return true;
}
void destroy_font_system() {
// stb_truetype不需要销毁
}
std::shared_ptr<font_face_interface> create_font_face(const std::filesystem::path& in_path) {
auto font_face = std::make_shared<stb_font_face_t>();
if (!font_face->load(in_path)) {
return nullptr;
}
return font_face;
}

@ -0,0 +1,28 @@
#pragma once
#include "stb_truetype.h"
#include "interface/font_interface.h"
class stb_font_face_t : public font_face_interface {
public:
[[nodiscard]] std::string get_font_name() const override;
[[nodiscard]] std::string get_font_family() const override;
[[nodiscard]] std::string get_font_style() const override;
[[nodiscard]] bool supports_color_emoji() const override;
[[nodiscard]] font_v_metrics_t get_metrics() const override;
[[nodiscard]] std::shared_ptr<image_heap_t> get_glyph_image(int32_t in_glyph_id) const override;
[[nodiscard]] std::shared_ptr<image_heap_t> get_emoji_image(int32_t in_glyph_id) const override;
[[nodiscard]] uint32_t find_glyph_index(uint32_t in_unicode_codepoint) const override;
[[nodiscard]] float get_kerning(uint32_t in_first_glyph_id, uint32_t in_second_glyph_id) const override;
[[nodiscard]] glyph_shaped_t shape_glyph(uint32_t in_glyph_id) const override;
[[nodiscard]] float get_scale_for_pixel_height(float font_size) const {
return stbtt_ScaleForPixelHeight(&font_info_, font_size);
}
protected:
bool on_load() override;
void on_set_font_size(float in_size) override;
protected:
stbtt_fontinfo font_info_{};
float scale_{};
};

@ -0,0 +1,45 @@
#include "bitmap_glyph_atlas.h"
#include "interface/image_interface.h"
glyph_atlas_result_t bitmap_glyph_atlas::get_or_create_glyph(int32_t in_glyph_index,
const std::shared_ptr<font_face_interface>& 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;
}
in_font->set_font_size(in_font_size);
// 获取字形的位图数据
auto bitmap = in_font->get_glyph_image(in_glyph_index);
if (!bitmap) {
result.reason = glyph_atlas_reason_t::glyph_not_found;
return result;
}
// 从图集分配空间添加2像素的padding防止纹理采样时的边缘混合问题
auto region = atlas_->allocate_region(bitmap->get_size(), { 2, 2 });
if (!region) {
result.reason = glyph_atlas_reason_t::atlas_full;
return result;
}
// 更新图集,将位图数据上传到纹理
atlas_->update_region(bitmap->data, bitmap->get_data_size(), region->rect);
// 缓存结果以便未来复用
add_to_cache(cache_key, *region);
result.reason = glyph_atlas_reason_t::success;
result.region = *region;
return result;
}

@ -1,5 +1,6 @@
#pragma once
#include "font_atlas.h"
#include "interface/font_interface.h"
/**
* @class bitmap_glyph_atlas
@ -32,60 +33,6 @@ public:
*/
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);
// stbtt_GetGlyphSDF();
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;
}
const std::shared_ptr<font_face_interface>& in_font,
float in_font_size);
};

@ -0,0 +1,44 @@
#include "color_emoji_atlas.h"
#include "interface/image_interface.h"
glyph_atlas_result_t color_emoji_atlas::get_or_create_emoji(int32_t in_glyph_index,
const std::shared_ptr<font_face_interface>& 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;
}
in_font->set_font_size(in_font_size);
// 获取彩色表情位图
auto bitmap = in_font->get_emoji_image(in_glyph_index);
if (!bitmap) {
result.reason = glyph_atlas_reason_t::glyph_not_found;
return result;
}
// 从图集分配空间添加2像素的padding防止纹理采样时的边缘混合问题
const auto& region = atlas_->allocate_region(bitmap->get_size(), { 2, 2 });
if (!region) {
result.reason = glyph_atlas_reason_t::atlas_full;
return result;
}
// 更新图集,将位图数据上传到纹理
atlas_->update_region(bitmap->data, bitmap->get_data_size(), region->rect);
// 缓存结果以便未来复用
add_to_cache(cache_key, *region);
result.reason = glyph_atlas_reason_t::success;
result.region = *region;
return result;
}

@ -1,5 +1,6 @@
#pragma once
#include "font_atlas.h"
#include "interface/font_interface.h"
/**
* @class color_emoji_atlas
@ -32,44 +33,6 @@ public:
*/
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;
}
const std::shared_ptr<font_face_interface>& in_font,
float in_font_size);
};

@ -7,7 +7,6 @@
#include <iomanip>
#include <unordered_map>
#include "font/font_face.h"
#include "texture/atlas/texture2d_atlas.h"
/**

@ -2,16 +2,10 @@
std::optional<atlas_region_t> font_atlas_manager::get_or_create_glyph(
int32_t in_glyph_id,
const font_face_t& in_font,
const std::shared_ptr<font_face_interface>& 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;

@ -1,12 +1,9 @@
#pragma once
#include <vector>
#include <memory>
#include <optional>
#include <Eigen/Eigen>
#include "bitmap_glyph_atlas.h"
#include "color_emoji_atlas.h"
#include "font/font_face.h"
/**
* @class font_atlas_manager
@ -33,7 +30,7 @@ public:
*/
std::optional<atlas_region_t> get_or_create_glyph(
int32_t in_glyph_id,
const font_face_t& in_font,
const std::shared_ptr<font_face_interface>& in_font,
float in_font_size,
bool is_emoji);

@ -1,371 +0,0 @@
#include "font_face.h"
#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() {
// 延迟初始化渲染器,直到加载字体
}
bool font_face_t::load_from_file(const std::filesystem::path& font_path) {
// 读取字体文件数据
if (!load_font_data(font_path)) {
return false;
}
const auto* font_data = static_cast<const uint8_t*>(font_data_->get_data());
// 初始化stb_truetype
const auto offset = stbtt_GetFontOffsetForIndex(font_data, 0);
if (!stbtt_InitFont(&font_info_, font_data, offset)) {
return false;
}
// 初始化渲染器
init_renderers();
return true;
}
font_v_metrics_t font_face_t::get_v_metrics() const {
font_v_metrics_t metrics;
// 尝试使用OS2表中的度量这通常更准确
if (!stbtt_GetFontVMetricsOS2(&font_info_, &metrics.ascent, &metrics.descent, &metrics.line_gap)) {
// 如果失败使用默认的VMetrics
stbtt_GetFontVMetrics(&font_info_, &metrics.ascent, &metrics.descent, &metrics.line_gap);
}
return metrics;
}
float font_face_t::get_scale_for_pixel_height(float font_size) const {
return stbtt_ScaleForPixelHeight(&font_info_, font_size);
}
float font_face_t::get_line_height() const {
// 获取字体的行高
const auto v_metrics = get_v_metrics();
return static_cast<float>(v_metrics.ascent - v_metrics.descent + v_metrics.line_gap);
}
std::optional<glyph_t> font_face_t::get_glyph(int32_t code_point, float font_size) const {
// 获取字形索引
const auto glyph_index = find_glyph_index(code_point);
if (glyph_index == 0) {
return std::nullopt; // 缺失字形
}
return get_glyph_by_index(glyph_index, font_size);
}
std::optional<glyph_t> font_face_t::get_glyph_by_index(int32_t glyph_index, float font_size) const {
if (glyph_index == 0) {
return std::nullopt; // 无效字形索引
}
glyph_t glyph;
glyph.glyph_index = glyph_index;
// 计算缩放比例
const auto scale = get_scale_for_pixel_height(font_size);
// 获取水平度量
int advance, lsb;
stbtt_GetGlyphHMetrics(&font_info_, glyph_index, &advance, &lsb);
// 获取字形边界
int x0, y0, x1, y1;
stbtt_GetGlyphBitmapBox(&font_info_, glyph_index, scale, scale, &x0, &y0, &x1, &y1);
// 设置字形数据
glyph.rect.set_position({ x0, y0 });
glyph.rect.set_size({ x1 - x0, y1 - y0 });
glyph.lsb = static_cast<float>(lsb) * scale;
glyph.advance = static_cast<float>(advance) * scale;
return glyph;
}
int32_t font_face_t::find_glyph_index(int32_t code_point) const {
return stbtt_FindGlyphIndex(&font_info_, code_point);
}
float font_face_t::get_glyph_advance(int32_t glyph_index1, int32_t glyph_index2) const {
return static_cast<float>(stbtt_GetGlyphKernAdvance(&font_info_, glyph_index1, glyph_index2));
}
bool font_face_t::has_glyph(int32_t code_point) const {
return find_glyph_index(code_point) > 0;
}
std::vector<stbtt_vertex> font_face_t::get_glyph_shape(int32_t code_point, float font_size) const {
// 获取字形索引
const int glyph_index = find_glyph_index(code_point);
if (glyph_index == 0) {
return {}; // 缺失字形,返回空向量
}
return get_glyph_shape_by_index(glyph_index, font_size);
}
std::vector<stbtt_vertex> font_face_t::get_glyph_shape_by_index(int32_t glyph_index, float font_size) const {
if (glyph_index == 0) {
return {}; // 无效字形索引
}
// 计算缩放比例
const float scale = get_scale_for_pixel_height(font_size);
// 获取原始字形轮廓
stbtt_vertex* vertices;
const int num_vertices = stbtt_GetGlyphShape(&font_info_, glyph_index, &vertices);
if (num_vertices <= 0 || vertices == nullptr) {
return {}; // 无轮廓或获取失败
}
// 复制顶点并应用缩放
std::vector<stbtt_vertex> shape;
shape.reserve(num_vertices);
for (int i = 0; i < num_vertices; i++) {
stbtt_vertex v = vertices[i];
// 对所有坐标应用缩放
v.x *= scale;
v.y *= scale;
// 处理不同类型的曲线控制点
if (v.type == STBTT_vcurve) {
v.cx *= scale;
v.cy *= scale;
} else if (v.type == STBTT_vcubic) {
v.cx *= scale;
v.cy *= scale;
v.cx1 *= scale;
v.cy1 *= scale;
}
shape.push_back(v);
}
// 释放stb分配的内存
stbtt_FreeShape(&font_info_, vertices);
return shape;
}
std::optional<bitmap_t> font_face_t::get_glyph_bitmap(int32_t glyph_index, float font_size) const {
if (!bitmap_renderer_ || glyph_index == 0) {
return std::nullopt;
}
return bitmap_renderer_->render_bitmap(font_info_, glyph_index, font_size);
}
std::optional<bitmap_t> font_face_t::get_glyph_bitmap_by_code_point(int32_t code_point, float font_size) const {
const auto glyph_index = find_glyph_index(code_point);
if (glyph_index == 0) {
return std::nullopt;
}
return get_glyph_bitmap(glyph_index, font_size);
}
bool font_face_t::supports_color_emoji() const {
if (!font_data_ || !font_data_->get_data()) {
return false;
}
const auto* font_data = static_cast<const uint8_t*>(font_data_->get_data());
const int font_offset = stbtt_GetFontOffsetForIndex(font_data, 0);
if (font_offset < 0) {
return false;
}
for (const auto& renderer : emoji_renderers_) {
if (renderer->supports_font(font_data, font_offset)) {
return true;
}
}
return false;
}
std::optional<color_emoji_bitmap_t> font_face_t::get_color_emoji_bitmap(int32_t glyph_index, float font_size) const {
if (glyph_index == 0 || emoji_renderers_.empty()) {
return std::nullopt;
}
const auto* font_data = static_cast<const uint8_t*>(font_data_->get_data());
const int font_offset = stbtt_GetFontOffsetForIndex(font_data, 0);
if (font_offset < 0) {
return std::nullopt;
}
// 尝试每一个渲染器
for (const auto& renderer : emoji_renderers_) {
if (renderer->supports_font(font_data, font_offset)) {
auto bitmap = renderer->render_emoji(font_info_, font_data, font_offset, glyph_index, font_size);
if (bitmap) {
return bitmap;
}
}
}
return std::nullopt;
}
std::optional<color_emoji_bitmap_t> font_face_t::get_color_emoji_bitmap_by_code_point(int32_t code_point, float font_size) const {
const auto glyph_index = find_glyph_index(code_point);
if (glyph_index == 0) {
return std::nullopt;
}
return get_color_emoji_bitmap(glyph_index, font_size);
}
std::string font_face_t::get_font_name() const {
return font_name_;
}
std::string font_face_t::get_font_type() const {
return font_type_;
}
void font_face_t::set_font_type(const std::string& font_type) {
font_type_ = font_type;
}
const stbtt_fontinfo& font_face_t::get_font_info() const {
return font_info_;
}
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>());
#ifdef HAS_NANOSVG
emoji_renderers_.push_back(std::make_unique<svg_renderer_t>());
#endif
}
bool font_face_t::load_font_data(const std::filesystem::path& font_path) {
// 创建内存映射文件
font_data_ = mapped_file::create();
if (!font_data_ || !font_data_->map_file(font_path)) {
return false;
}
if (font_data_->get_size() == 0) {
return false;
}
// 提取字体名称
extract_font_name();
return true;
}
void font_face_t::extract_font_name() {
// 默认名称
font_name_ = "Unknown Font";
if (!font_data_ || !font_data_->get_data() || font_data_->get_size() == 0) {
return;
}
const auto* data = static_cast<const uint8_t*>(font_data_->get_data());
const int font_offset = stbtt_GetFontOffsetForIndex(data, 0);
if (font_offset < 0) {
return;
}
// 查找名称表
uint32_t name_offset = font::find_table_offset(data, font_offset, "name");
if (name_offset == 0) {
return;
}
// 解析名称表
const uint8_t* name_table = data + name_offset;
const uint16_t count = font::read_u16(name_table + 2);
const uint16_t string_offset = font::read_u16(name_table + 4);
// 字体名称优先级: 完整名称(4) > 字体族(1)
std::string family_name;
for (uint16_t i = 0; i < count; ++i) {
const uint8_t* record = name_table + 6 + 12 * i;
const uint16_t platform_id = font::read_u16(record);
const uint16_t encoding_id = font::read_u16(record + 2);
const uint16_t language_id = font::read_u16(record + 4);
const uint16_t name_id = font::read_u16(record + 6);
const uint16_t length = font::read_u16(record + 8);
const uint16_t offset = font::read_u16(record + 10);
// 是否为英语或首选语言
const bool is_english = (platform_id == 3 && language_id == 0x0409); // Windows 英语 (US)
if ((name_id == 4 || name_id == 1) && length > 0) {
const uint8_t* string_data = name_table + string_offset + offset;
std::string name;
// 处理不同平台的编码
if (platform_id == 3) { // Windows Unicode (UTF-16BE)
// 简单的UTF-16BE转UTF-8
for (uint16_t j = 0; j < length; j += 2) {
if (j + 1 < length) {
const uint16_t unicode = (string_data[j] << 8) | string_data[j + 1];
if (unicode < 0x80) {
name.push_back(static_cast<char>(unicode));
} else if (unicode < 0x800) {
name.push_back(static_cast<char>(0xC0 | ((unicode >> 6) & 0x1F)));
name.push_back(static_cast<char>(0x80 | (unicode & 0x3F)));
} else {
name.push_back(static_cast<char>(0xE0 | ((unicode >> 12) & 0x0F)));
name.push_back(static_cast<char>(0x80 | ((unicode >> 6) & 0x3F)));
name.push_back(static_cast<char>(0x80 | (unicode & 0x3F)));
}
}
}
} else { // 假设为ASCII或其他单字节编码
name = std::string(reinterpret_cast<const char*>(string_data), length);
}
if (!name.empty()) {
// 根据name_id和语言更新字体名称
if (name_id == 4) { // 完整名称
if (is_english) {
// 找到英语的完整名称,优先使用
font_name_ = name;
return;
} else if (font_name_ == "Unknown Font") {
// 如果还没有设置名称,使用非英语完整名称
font_name_ = name;
}
} else if (name_id == 1 && family_name.empty()) { // 字体族
// 保存字体族名称作为备选
family_name = name;
}
}
}
}
// 如果没有找到完整名称,但有字体族名称,则使用字体族
if (font_name_ == "Unknown Font" && !family_name.empty()) {
font_name_ = family_name;
}
}

@ -1,242 +0,0 @@
#pragma once
#include <string>
#include <vector>
#include <memory>
#include <optional>
#include <cstdint>
#include "font_type.h"
#include "stb_truetype.h"
#include "misc/mapped_file/mapped_file.h"
#include "font_renderer/font_renderer.h"
#include <filesystem>
/**
* @class font_face_t
* @brief
*
* TrueType/OpenType字体的访问
*/
class font_face_t {
public:
/**
* @brief
*/
font_face_t();
/**
* @brief
*/
~font_face_t() = default;
/**
* @brief
*/
font_face_t(const font_face_t&) = delete;
font_face_t& operator=(const font_face_t&) = delete;
//--------------------------------------------------------------------------
// 字体加载与初始化
//--------------------------------------------------------------------------
/**
* @brief
* @param font_path
* @return
*/
bool load_from_file(const std::filesystem::path& font_path);
//--------------------------------------------------------------------------
// 字体度量信息
//--------------------------------------------------------------------------
/**
* @brief baseline以上的高度
* @return
*/
font_v_metrics_t get_v_metrics() const;
/**
* @brief
* @param font_size
* @return
*/
float get_scale_for_pixel_height(float font_size) const;
/**
* @brief
* @return
*/
[[nodiscard]] float get_line_height() const;
//--------------------------------------------------------------------------
// 字形信息查询
//--------------------------------------------------------------------------
/**
* @brief Unicode码点获取字形信息
* @param code_point Unicode码点
* @param font_size
* @return
*/
[[nodiscard]] std::optional<glyph_t> get_glyph(int32_t code_point, float font_size) const;
/**
* @brief
* @param glyph_index
* @param font_size
* @return
*/
[[nodiscard]] std::optional<glyph_t> get_glyph_by_index(int32_t glyph_index, float font_size) const;
/**
* @brief
* @param code_point Unicode码点
* @return 0
*/
int32_t find_glyph_index(int32_t code_point) const;
/**
* @brief
* @param glyph_index1
* @param glyph_index2
* @return
*/
float get_glyph_advance(int32_t glyph_index1, int32_t glyph_index2) const;
/**
* @brief
* @param code_point Unicode码点
* @return
*/
[[nodiscard]] bool has_glyph(int32_t code_point) const;
//--------------------------------------------------------------------------
// 字形轮廓提取
//--------------------------------------------------------------------------
/**
* @brief Unicode码点获取字形轮廓
* @param code_point Unicode码点
* @param font_size
* @return
*/
[[nodiscard]] std::vector<stbtt_vertex> get_glyph_shape(int32_t code_point, float font_size) const;
/**
* @brief
* @param glyph_index
* @param font_size
* @return
*/
[[nodiscard]] std::vector<stbtt_vertex> get_glyph_shape_by_index(int32_t glyph_index, float font_size) const;
//--------------------------------------------------------------------------
// 位图渲染
//--------------------------------------------------------------------------
/**
* @brief
* @param glyph_index
* @param font_size
* @return
*/
[[nodiscard]] std::optional<bitmap_t> get_glyph_bitmap(int32_t glyph_index, float font_size) const;
/**
* @brief Unicode码点获取字形的灰度位图
* @param code_point Unicode码点
* @param font_size
* @return
*/
[[nodiscard]] std::optional<bitmap_t> get_glyph_bitmap_by_code_point(int32_t code_point, float font_size) const;
//--------------------------------------------------------------------------
// 彩色表情符号支持
//--------------------------------------------------------------------------
/**
* @brief
* @return
*/
[[nodiscard]] bool supports_color_emoji() const;
/**
* @brief
* @param glyph_index
* @param font_size
* @return
*/
[[nodiscard]] std::optional<color_emoji_bitmap_t> get_color_emoji_bitmap(int32_t glyph_index, float font_size) const;
/**
* @brief Unicode码点获取彩色表情符号位图
* @param code_point Unicode码点
* @param font_size
* @return
*/
[[nodiscard]] std::optional<color_emoji_bitmap_t> get_color_emoji_bitmap_by_code_point(int32_t code_point, float font_size) const;
//--------------------------------------------------------------------------
// 字体元数据
//--------------------------------------------------------------------------
/**
* @brief
* @return
*/
[[nodiscard]] std::string get_font_name() const;
/**
* @brief regular, bold, italic等
* @return
*/
[[nodiscard]] std::string get_font_type() const;
/**
* @brief
* @param font_type
*/
void set_font_type(const std::string& font_type);
/**
* @brief stb_truetype字体信息
* @return
*/
const stbtt_fontinfo& get_font_info() const;
private:
//--------------------------------------------------------------------------
// 内部数据成员
//--------------------------------------------------------------------------
stbtt_fontinfo font_info_{}; // stb_truetype字体信息
std::shared_ptr<mapped_file> font_data_; // 字体文件映射数据
std::string font_name_ = "Unknown"; // 字体名称
std::string font_type_ = "regular"; // 字体类型
// 渲染器
std::unique_ptr<bitmap_renderer_t> bitmap_renderer_; // 位图渲染器
std::vector<std::unique_ptr<color_emoji_renderer_t>> emoji_renderers_; // 表情渲染器集合
//--------------------------------------------------------------------------
// 内部辅助方法
//--------------------------------------------------------------------------
/**
* @brief
* @param font_path
* @return
*/
bool load_font_data(const std::filesystem::path& font_path);
/**
* @brief
*/
void extract_font_name();
/**
* @brief
*/
void init_renderers();
};

@ -1,295 +0,0 @@
#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;
}

@ -1,73 +0,0 @@
#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;
};

@ -1,351 +0,0 @@
#include "colr_renderer.h"
#include <fstream>
#include "font/font_utils.h"
#include "font/stb_truetype.h"
bool colr_renderer_t::supports_font(const uint8_t* font_data, int font_offset) const {
return font::find_table_offset(font_data, font_offset, "COLR") != 0 &&
font::find_table_offset(font_data, font_offset, "CPAL") != 0;
}
void save_bitmap(const std::string& filename, const color_emoji_bitmap_t& bitmap) {
// 检查输入有效性
if (bitmap.width <= 0 || bitmap.height <= 0 || bitmap.data.empty()) {
throw std::invalid_argument("Invalid bitmap data");
}
if (bitmap.data.size() < static_cast<size_t>(bitmap.width * bitmap.height * 4)) {
throw std::invalid_argument("Bitmap data size doesn't match dimensions");
}
#pragma pack(push, 1)
// BMP文件头结构
struct BITMAPFILEHEADER {
uint16_t bfType; // 文件类型,必须是"BM"0x4D42
uint32_t bfSize; // 文件大小
uint16_t bfReserved1; // 保留必须为0
uint16_t bfReserved2; // 保留必须为0
uint32_t bfOffBits; // 从文件开始到像素数据的偏移量
};
// BMP V4信息头结构**支持Alpha通道**
struct BITMAPV4HEADER {
uint32_t biSize; // 信息头大小
int32_t biWidth; // 图像宽度
int32_t biHeight; // 图像高度
uint16_t biPlanes; // 颜色平面数必须为1
uint16_t biBitCount; // 每个像素的位数
uint32_t biCompression; // 压缩方式
uint32_t biSizeImage; // 图像大小
int32_t biXPelsPerMeter; // 水平分辨率
int32_t biYPelsPerMeter; // 垂直分辨率
uint32_t biClrUsed; // 使用的颜色索引数
uint32_t biClrImportant; // 重要的颜色索引数
uint32_t bV4RedMask; // 红色掩码
uint32_t bV4GreenMask; // 绿色掩码
uint32_t bV4BlueMask; // 蓝色掩码
uint32_t bV4AlphaMask; // Alpha掩码
uint32_t bV4CSType; // 颜色空间类型
uint8_t bV4Endpoints[36]; // 端点
uint32_t bV4GammaRed; // 红色Gamma
uint32_t bV4GammaGreen; // 绿色Gamma
uint32_t bV4GammaBlue; // 蓝色Gamma
};
#pragma pack(pop)
// 常量定义
const uint32_t BI_BITFIELDS = 3; // 位域压缩
const uint16_t BMP_SIGNATURE = 0x4D42; // "BM"
// 计算文件大小
int rowSize = ((bitmap.width * 32 + 31) / 32) * 4; // 每行字节数必须是4的倍数
int dataSize = rowSize * bitmap.height;
int fileSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPV4HEADER) + dataSize;
// 准备文件头
BITMAPFILEHEADER fileHeader = {0};
fileHeader.bfType = BMP_SIGNATURE;
fileHeader.bfSize = fileSize;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPV4HEADER);
// 准备信息头
BITMAPV4HEADER infoHeader = {0};
infoHeader.biSize = sizeof(BITMAPV4HEADER);
infoHeader.biWidth = bitmap.width;
infoHeader.biHeight = bitmap.height; // **正值表示图像从下到上存储**
infoHeader.biPlanes = 1;
infoHeader.biBitCount = 32; // 32位每像素BGRA
infoHeader.biCompression = BI_BITFIELDS;
infoHeader.biSizeImage = dataSize;
infoHeader.biXPelsPerMeter = 0;
infoHeader.biYPelsPerMeter = 0;
infoHeader.biClrUsed = 0;
infoHeader.biClrImportant = 0;
infoHeader.bV4RedMask = 0x00FF0000; // R在第3字节
infoHeader.bV4GreenMask = 0x0000FF00; // G在第2字节
infoHeader.bV4BlueMask = 0x000000FF; // B在第1字节
infoHeader.bV4AlphaMask = 0xFF000000; // A在第4字节
infoHeader.bV4CSType = 0; // LCS_CALIBRATED_RGB
// 打开文件
std::ofstream file(filename, std::ios::binary);
if (!file) {
throw std::runtime_error("Failed to open file for writing: " + filename);
}
// 写入文件头
file.write(reinterpret_cast<const char*>(&fileHeader), sizeof(fileHeader));
// 写入信息头
file.write(reinterpret_cast<const char*>(&infoHeader), sizeof(infoHeader));
// 转换并写入像素数据
// **BMP存储顺序是从下到上从左到右且是BGRA而不是RGBA**
std::vector<uint8_t> bmpData(dataSize, 0); // 初始化为0
for (int y = 0; y < bitmap.height; y++) {
for (int x = 0; x < bitmap.width; x++) {
// 计算源数据和目标数据的索引
size_t srcIndex = (y * bitmap.width + x) * 4; // RGBA
size_t destIndex = ((bitmap.height - 1 - y) * rowSize) + (x * 4); // BGRA, 从下到上
// RGBA -> BGRA
bmpData[destIndex + 0] = bitmap.data[srcIndex + 2]; // B <- R
bmpData[destIndex + 1] = bitmap.data[srcIndex + 1]; // G <- G
bmpData[destIndex + 2] = bitmap.data[srcIndex + 0]; // R <- B
bmpData[destIndex + 3] = bitmap.data[srcIndex + 3]; // A <- A
}
}
// 写入像素数据
file.write(reinterpret_cast<const char*>(bmpData.data()), dataSize);
if (!file.good()) {
throw std::runtime_error("Error occurred while writing to file: " + filename);
}
}
std::optional<color_emoji_bitmap_t> colr_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) {
return std::nullopt;
}
// 查找COLR和CPAL表
const uint32_t colr_offset = font::find_table_offset(font_data, font_offset, "COLR");
const uint32_t cpal_offset = font::find_table_offset(font_data, font_offset, "CPAL");
if (colr_offset == 0 || cpal_offset == 0) {
return std::nullopt;
}
const uint8_t* colr_data = font_data + colr_offset;
const uint8_t* cpal_data = font_data + cpal_offset;
// 解析COLR表
const uint16_t colr_version = font::read_u16(colr_data);
const uint16_t num_base_glyphs = font::read_u16(colr_data + 2);
const uint32_t base_glyph_records_offset = font::read_u32(colr_data + 4);
const uint32_t layer_records_offset = font::read_u32(colr_data + 8);
const uint16_t num_layers = font::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 = font::read_u16(record);
if (base_glyph_id == 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 = font::read_u16(target_record + 2);
const uint16_t num_layers_for_glyph = font::read_u16(target_record + 4);
// 检查图层数量
if (num_layers_for_glyph == 0 || first_layer_index + num_layers_for_glyph > num_layers) {
return std::nullopt; // 无效的图层配置
}
// 解析CPAL表 - 为Windows字体特别处理
const uint16_t cpal_version = font::read_u16(cpal_data);
const uint16_t num_palette_entries = font::read_u16(cpal_data + 2);
const uint16_t num_palettes = font::read_u16(cpal_data + 4);
const uint32_t color_records_offset = font::read_u32(cpal_data + 8);
// Windows字体可能有类型选择器字段
uint16_t color_record_indices_offset = 12;
if (cpal_version > 0 && cpal_data + 12 < font_data + font_offset + color_records_offset) {
color_record_indices_offset = 14; // 调整CPAL格式偏移
}
// 使用第一个调色板
const uint8_t* palette_indices = cpal_data + color_record_indices_offset;
uint16_t first_color_index = 0;
if (num_palettes > 0) {
first_color_index = font::read_u16(palette_indices);
}
// 计算缩放比例
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);
// 创建位图结构
color_emoji_bitmap_t bitmap;
bitmap.width = x1 - x0;
bitmap.height = y1 - y0;
// 确保位图尺寸有效 - Windows字体可能需要额外的填充
if (bitmap.width <= 0 || bitmap.height <= 0) {
bitmap.width = static_cast<int>(font_size * 1.2); // 添加额外空间
bitmap.height = static_cast<int>(font_size * 1.2);
x0 = 0;
y0 = 0;
}
// 初始化RGBA位图数据
bitmap.data.resize(bitmap.width * bitmap.height * 4, 0);
// 渲染每一个图层 - Windows Segoe UI Emoji特别处理
const uint8_t* layer_records = colr_data + layer_records_offset;
const uint8_t* color_records = cpal_data + color_records_offset;
// 处理Microsoft特有的图层顺序
for (uint16_t i = 0; i < num_layers_for_glyph; i++) {
const uint8_t* layer = layer_records + (first_layer_index + i) * 4;
const uint16_t layer_glyph_id = font::read_u16(layer);
const uint16_t palette_entry_index = font::read_u16(layer + 2);
// 确保层索引有效
if (palette_entry_index >= num_palette_entries) {
continue;
}
// 获取颜色 - Windows字体可能有不同的颜色格式
const uint16_t color_index = first_color_index + palette_entry_index;
if (color_index >= num_palette_entries) {
continue;
}
const uint8_t* color = color_records + color_index * 4;
// Windows字体通常使用BGRA顺序
const uint8_t blue = color[0];
const uint8_t green = color[1];
const uint8_t red = color[2];
const uint8_t alpha = color[3];
// Windows字体可能会对特定颜色设置特殊意义
// 如果alpha为0微软通常将其视为"不透明黑色"
const uint8_t effective_alpha = (alpha == 0 && (red > 0 || green > 0 || blue > 0))
? 255 : alpha;
// 为图层创建临时位图
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);
const int layer_width = layer_x1 - layer_x0;
const int layer_height = layer_y1 - layer_y0;
if (layer_width <= 0 || layer_height <= 0) {
continue;
}
// 渲染图层字形的灰度位图
std::vector<uint8_t> layer_bitmap(layer_width * layer_height, 0);
// 使用更高精度的渲染方法 - 关键是对细节(如眼睛)的处理
stbtt_MakeGlyphBitmap(&font_info, layer_bitmap.data(),
layer_width, layer_height, layer_width,
scale, scale, layer_glyph_id);
// 测试保存BMP
save_bitmap(std::to_string(i) + ".bmp", bitmap);
// 计算图层在最终位图中的位置
const int offset_x = layer_x0 - x0;
const int offset_y = layer_y0 - y0;
// 将图层与颜色混合到最终位图 - Windows样式混合
for (int y = 0; y < layer_height; y++) {
const int dst_y = offset_y + y;
if (dst_y < 0 || dst_y >= bitmap.height) {
continue;
}
for (int x = 0; x < layer_width; x++) {
const int dst_x = offset_x + x;
if (dst_x < 0 || dst_x >= bitmap.width) {
continue;
}
const uint8_t coverage = layer_bitmap[y * layer_width + x];
if (coverage == 0) {
continue;
}
const int dst_idx = (dst_y * bitmap.width + dst_x) * 4;
// Windows风格的Alpha混合 - 这对眼睛等小细节很重要
const float src_alpha = (coverage / 255.0f) * (effective_alpha / 255.0f);
// 对于细节图层(如眼睛),增强其影响
float enhanced_alpha = src_alpha;
// if (palette_entry_index > 0 && src_alpha > 0) { // 非底色图层且有透明度
// enhanced_alpha = std::min(src_alpha * 1.2f, 1.0f); // 稍微增强
// }
const float dst_alpha = bitmap.data[dst_idx + 3] / 255.0f;
const float out_alpha = enhanced_alpha + dst_alpha * (1.0f - enhanced_alpha);
if (out_alpha > 0) {
// 特别处理黑色或暗色像素,这通常是眼睛等细节
if (red <= 64 && green <= 64 && blue <= 64 && enhanced_alpha > 0.2f) {
// 增强黑色细节的可见度
bitmap.data[dst_idx + 0] = red;
bitmap.data[dst_idx + 1] = green;
bitmap.data[dst_idx + 2] = blue;
bitmap.data[dst_idx + 3] = static_cast<uint8_t>(enhanced_alpha * 255.0f);
} else {
// 标准混合
const float src_factor = enhanced_alpha / out_alpha;
const float dst_factor = dst_alpha * (1.0f - enhanced_alpha) / out_alpha;
bitmap.data[dst_idx + 0] = static_cast<uint8_t>(
std::min(255.0f, red * src_factor + bitmap.data[dst_idx + 0] * dst_factor));
bitmap.data[dst_idx + 1] = static_cast<uint8_t>(
std::min(255.0f, green * src_factor + bitmap.data[dst_idx + 1] * dst_factor));
bitmap.data[dst_idx + 2] = static_cast<uint8_t>(
std::min(255.0f, blue * src_factor + bitmap.data[dst_idx + 2] * dst_factor));
bitmap.data[dst_idx + 3] = static_cast<uint8_t>(out_alpha * 255.0f);
}
}
}
}
}
return bitmap;
}

@ -1,38 +0,0 @@
#pragma once
#include "font_renderer.h"
#include "font/stb_truetype.h"
void save_bitmap(const std::string& filename, const color_emoji_bitmap_t& bitmap);
/**
* @class colr_renderer_t
* @brief COLR/CPAL表的彩色表情渲染器
*
* Microsoft COLR/CPAL彩色字体格式的渲染
*/
class colr_renderer_t : public color_emoji_renderer_t {
public:
/**
* @brief COLR/CPAL表
* @param font_data
* @param font_offset
* @return
*/
bool supports_font(const uint8_t* font_data, int font_offset) const override;
/**
* @brief COLR/CPAL彩色表情
* @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;
};

@ -1,72 +0,0 @@
#pragma once
#include <optional>
#include <vector>
#include <cstdint>
#include "font/font_type.h"
#include "font/stb_truetype.h"
/**
* @struct bitmap_t
* @brief
*/
struct bitmap_t {
int width = 0; // 位图宽度
int height = 0; // 位图高度
int bearing_x = 0; // 水平位置
int bearing_y = 0; // 垂直位置
std::vector<uint8_t> data; // 灰度数据每个像素1字节
};
/**
* @class bitmap_renderer_t
* @brief
*/
class bitmap_renderer_t {
public:
virtual ~bitmap_renderer_t() = default;
/**
* @brief
* @param font_info
* @param glyph_index
* @param font_size
* @return
*/
virtual std::optional<bitmap_t> render_bitmap(
const stbtt_fontinfo& font_info,
int32_t glyph_index,
float font_size) const = 0;
};
/**
* @class color_emoji_renderer_t
* @brief
*/
class color_emoji_renderer_t {
public:
virtual ~color_emoji_renderer_t() = default;
/**
* @brief
* @param font_data
* @param font_offset
* @return
*/
virtual bool supports_font(const uint8_t* font_data, int font_offset) const = 0;
/**
* @brief
* @param font_info
* @param font_data
* @param font_offset
* @param glyph_index
* @param font_size
* @return
*/
virtual 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 = 0;
};

@ -1,220 +0,0 @@
#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

@ -1,75 +0,0 @@
#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

@ -1,50 +0,0 @@
#include "standard_bitmap_renderer.h"
std::optional<bitmap_t> standard_bitmap_renderer_t::render_bitmap(
const stbtt_fontinfo& font_info,
int32_t glyph_index,
float font_size) const {
if (glyph_index == 0) {
return std::nullopt;
}
// 计算缩放比例
const float scale = stbtt_ScaleForPixelHeight(&font_info, font_size);
// 获取字形边界
int x0, y0, x1, y1;
stbtt_GetGlyphBitmapBoxSubpixel(&font_info,
glyph_index,
scale, scale,
0.5f, 0,
&x0, &y0, &x1, &y1);
const int width = x1 - x0;
const int height = y1 - y0;
// 确保位图尺寸有效
if (width <= 0 || height <= 0) {
return std::nullopt;
}
// 创建位图结构
bitmap_t bitmap;
bitmap.width = width;
bitmap.height = height;
bitmap.bearing_x = x0;
bitmap.bearing_y = y0;
bitmap.data.resize(width * height);
// 渲染字形
stbtt_MakeGlyphBitmapSubpixel(
&font_info,
bitmap.data.data(),
width, height, width,
scale, scale,
0.5f, 0,
glyph_index
);
return bitmap;
}

@ -1,21 +0,0 @@
#pragma once
#include "font_renderer.h"
/**
* @class standard_bitmap_renderer_t
* @brief 使stb_truetype实现的标准灰度位图渲染器
*/
class standard_bitmap_renderer_t : public bitmap_renderer_t {
public:
/**
* @brief
* @param font_info
* @param glyph_index
* @param font_size
* @return
*/
std::optional<bitmap_t> render_bitmap(
const stbtt_fontinfo& font_info,
int32_t glyph_index,
float font_size) const override;
};

@ -1,169 +0,0 @@
#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

@ -1,65 +0,0 @@
#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

@ -8,15 +8,15 @@ void font_manager::destroy() {
atlas_manager_.clear();
primary_font_id_ = -1;
next_font_id_ = 0;
destroy_font_system();
}
int font_manager::add_font(const std::filesystem::path& in_font_path, const std::string& in_font_type) {
auto font = std::make_shared<font_face_t>();
if (!font->load_from_file(in_font_path)) {
auto font = create_font_face(in_font_path);
if (!font) {
return -1;
}
font->set_font_type(in_font_type);
int font_id = next_font_id_++;
fonts_[font_id] = font;
@ -33,7 +33,7 @@ int font_manager::add_font(const std::filesystem::path& in_font_path, const std:
return font_id;
}
std::shared_ptr<font_face_t> font_manager::get_font_for_code_point(uint32_t in_code_point) {
std::shared_ptr<font_face_interface> 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)) {
@ -66,7 +66,7 @@ std::shared_ptr<font_face_t> font_manager::get_font_for_code_point(uint32_t in_c
*/
text_layout_t font_manager::layout_text(
const std::u32string& text,
const std::shared_ptr<font_face_t>& in_font,
const std::shared_ptr<font_face_interface>& in_font,
float font_size,
float max_width,
float line_spacing)
@ -77,19 +77,15 @@ text_layout_t font_manager::layout_text(
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 = primary_font->get_line_height() * scale + line_spacing;
float line_max_width = 0.0f;
const auto& v_metrics = primary_font->get_v_metrics();
const auto& v_metrics = primary_font->get_metrics();
float font_ascent = v_metrics.ascent * scale;
float font_descent = std::abs(v_metrics.descent * scale);
float font_ascent = v_metrics.ascent;
float font_descent = std::abs(v_metrics.descent);
int32_t last_index = 0; // 上一个字形索引,用于字距调整
float line_start_x = 0.0f; // 当前行的起始x坐标
@ -106,6 +102,7 @@ text_layout_t font_manager::layout_text(
baseline = cursor_y + font_ascent;
line_max_width = std::max(line_max_width, cursor_x);
const float line_height = v_metrics.line_height() + line_spacing;
// 移到下一行
cursor_y += line_height;
@ -113,8 +110,8 @@ text_layout_t font_manager::layout_text(
line_start_x = 0.0f;
// 重置为下一行的基线计算
font_ascent = v_metrics.ascent * scale;
font_descent = std::abs(v_metrics.descent * scale);
font_ascent = v_metrics.ascent;
font_descent = std::abs(v_metrics.descent);
baseline = cursor_y + font_ascent;
};
@ -144,45 +141,43 @@ text_layout_t font_manager::layout_text(
// 使用新字体重新获取字形索引
glyph_index = using_font->find_glyph_index(c);
}
using_font->set_font_size(font_size);
// 获取字形度量信息
const auto& glyph_metrics = using_font->get_glyph_by_index(glyph_index, font_size);
if (!glyph_metrics) {
continue; // 如果无法获取字形度量,跳过该字符
}
const auto& glyph_metrics = using_font->shape_glyph(glyph_index);
// 获取或创建字形在图集中的区域
const auto& region = get_or_create_glyph_by_index(glyph_metrics->glyph_index, using_font, font_size, is_emoji);
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 + glyph_metrics.advance.x() > max_width &&
cursor_x > line_start_x) { // 确保不是行首字符
finish_line();
}
// 计算字形坐标
float x = cursor_x + glyph_metrics->lsb + glyph_metrics->rect.left();
float x = cursor_x + glyph_metrics.offset.x() + glyph_metrics.rect.left();
// float y = cursor_y + font_ascent + glyph_metrics->rect.top() + font_descent;
float y = baseline + glyph_metrics->rect.top();
float y = baseline + glyph_metrics.rect.top();
// 添加字形位置信息到布局
auto& glyph_position = layout.glyphs.emplace_back();
glyph_position.is_emoji = is_emoji;
glyph_position.glyph_index = glyph_index;
glyph_position.position = { x, y };
glyph_position.size = glyph_metrics->rect.size();
glyph_position.size = glyph_metrics.rect.size();
glyph_position.region = *region;
// 更新光标位置
cursor_x += glyph_metrics->advance + glyph_metrics->rect.left();
cursor_x += glyph_metrics.advance.x() + glyph_metrics.rect.left();
// 应用字距调整
if (last_index > 0) {
const float kern_advance = using_font->get_glyph_advance(last_index, glyph_index) * scale;
const float kern_advance = using_font->get_kerning(last_index, glyph_index);
cursor_x += kern_advance;
}
@ -207,7 +202,7 @@ text_layout_t font_manager::layout_text(
*/
std::optional<atlas_region_t> font_manager::get_or_create_glyph_by_index(
int32_t in_glyph_id,
std::shared_ptr<font_face_t> in_font,
const std::shared_ptr<font_face_interface>& in_font,
float in_font_size,
bool is_emoji)
{
@ -216,5 +211,12 @@ std::optional<atlas_region_t> font_manager::get_or_create_glyph_by_index(
return std::nullopt;
}
return atlas_manager_.get_or_create_glyph(in_glyph_id, *in_font, in_font_size, is_emoji);
return atlas_manager_.get_or_create_glyph(in_glyph_id, in_font, in_font_size, is_emoji);
}
font_manager::font_manager() {
// 初始化字体系统
if (!init_font_system()) {
throw std::runtime_error("无法初始化字体系统");
}
}

@ -5,11 +5,13 @@
#include <unordered_map>
#include <optional>
#include "font_face.h"
#include "atlas/font_atlas.h"
#include "atlas/font_atlas_manager.h"
#include <filesystem>
#include "font_type.h"
#include "interface/font_interface.h"
/**
* @class font_manager
* @brief
@ -74,13 +76,13 @@ public:
* @param in_code_point Unicode码点
* @return
*/
std::shared_ptr<font_face_t> get_font_for_code_point(uint32_t in_code_point);
std::shared_ptr<font_face_interface> get_font_for_code_point(uint32_t in_code_point);
/**
* @brief
* @return
*/
std::shared_ptr<font_face_t> get_primary_font() {
std::shared_ptr<font_face_interface> get_primary_font() {
if (primary_font_id_ != -1 && fonts_.contains(primary_font_id_)) {
return fonts_[primary_font_id_];
}
@ -109,7 +111,7 @@ public:
*/
text_layout_t layout_text(
const std::u32string& text,
const std::shared_ptr<font_face_t>& in_font,
const std::shared_ptr<font_face_interface>& in_font,
float font_size,
float max_width = 0.0f,
float line_spacing = 1.2f);
@ -127,7 +129,7 @@ public:
*/
std::optional<atlas_region_t> get_or_create_glyph_by_index(
int32_t in_glyph_id,
std::shared_ptr<font_face_t> in_font,
const std::shared_ptr<font_face_interface>& in_font,
float in_font_size,
bool is_emoji);
@ -147,8 +149,10 @@ public:
return atlas_manager_.get_emoji_atlases();
}
private:
std::unordered_map<int, std::shared_ptr<font_face_t>> fonts_; ///< 字体ID到字体对象的映射
font_atlas_manager atlas_manager_; ///< 图集管理器
font_manager();
std::unordered_map<int, std::shared_ptr<font_face_interface>> fonts_; ///< 字体ID到字体对象的映射
font_atlas_manager atlas_manager_; ///< 图集管理器
std::vector<int> emoji_font_ids_; ///< 表情符号字体ID列表
int primary_font_id_ = -1; ///< 主字体ID

@ -27,17 +27,6 @@ enum class vertical_text_alignment_t {
bottom ///< 文本底部对齐
};
/**
* @struct glyph_t
* @brief
*/
struct glyph_t {
uint32_t glyph_index = 0; ///< 字形索引
rect_t<float> rect; ///< 字形矩形(相对于基线)
float lsb; ///< 左侧轴向边距
float advance = 0.0f; ///< 水平前进量
};
/**
* @struct positioned_glyph_t
* @brief
@ -126,13 +115,6 @@ struct text_style_t {
margin_t text_margin = { 0, 0, 0, 0 }; ///< 文本边距
};
struct shaped_glyph_t {
int32_t glyph_index; // Unicode码点
Eigen::Vector2f offset; // 相对位置偏移
Eigen::Vector2f advance; // 前进值
int cluster; // 字符簇索引
};
struct text_layout_t {
struct glyph_position_t {
bool is_emoji; // 是否为表情符号
@ -147,25 +129,3 @@ struct text_layout_t {
float ascent; // 上升高度
float baseline; // 基线位置
};
/**
* @struct color_emoji_bitmap_t
* @brief
*/
struct color_emoji_bitmap_t {
int width = 0; // 位图宽度
int height = 0; // 位图高度
int16_t x_offset = 0; // 水平原点偏移
int16_t y_offset = 0; // 垂直原点偏移
std::vector<uint8_t> data; // RGBA数据每个像素4字节
};
/**
* @struct font_v_metrics_t
* @brief
*/
struct font_v_metrics_t {
int32_t ascent; // 上升高度
int32_t descent; // 下降高度
int32_t line_gap; // 行间距
};

@ -0,0 +1,10 @@
#include "font_interface.h"
bool font_face_interface::load(const std::filesystem::path& in_path) {
font_data_ = mapped_file::create();
if (!font_data_->map_file(in_path)) {
return false;
}
return on_load();
}

@ -0,0 +1,63 @@
#pragma once
#include <memory>
#include <filesystem>
#include <string>
#include "geometry/rect.h"
#include "misc/mapped_file/mapped_file.h"
struct image_heap_t;
/**
* @struct font_v_metrics_t
* @brief
*/
struct font_v_metrics_t {
float ascent; // 上升高度
float descent; // 下降高度
float line_gap; // 行间距
[[nodiscard]] float line_height() const { return ascent - descent + line_gap; } // 行高
};
struct glyph_shaped_t {
int32_t glyph_index; // Unicode码点
Eigen::Vector2f offset; // 相对位置偏移
Eigen::Vector2f advance; // 前进值
rect_t<> rect; // 字形矩形区域
};
class font_face_interface {
public:
virtual ~font_face_interface() = default;
bool load(const std::filesystem::path& in_path);
void set_font_size(float in_size) { font_size_ = in_size; on_set_font_size(in_size); }
[[nodiscard]] float get_font_size() const { return font_size_; }
[[nodiscard]] virtual std::string get_font_name() const = 0;
[[nodiscard]] virtual std::string get_font_family() const = 0;
[[nodiscard]] virtual std::string get_font_style() const = 0;
[[nodiscard]] virtual bool supports_color_emoji() const = 0;
[[nodiscard]] virtual font_v_metrics_t get_metrics() const = 0;
[[nodiscard]] virtual std::shared_ptr<image_heap_t> get_glyph_image(int32_t in_glyph_id) const = 0;
[[nodiscard]] virtual std::shared_ptr<image_heap_t> get_emoji_image(int32_t in_glyph_id) const = 0;
[[nodiscard]] virtual uint32_t find_glyph_index(uint32_t in_unicode_codepoint) const = 0;
[[nodiscard]] virtual bool has_glyph(uint32_t in_unicode_codepoint) const { return find_glyph_index(in_unicode_codepoint) > 0; }
[[nodiscard]] virtual float get_kerning(uint32_t in_first_glyph_id, uint32_t in_second_glyph_id) const = 0;
[[nodiscard]] virtual glyph_shaped_t shape_glyph(uint32_t in_glyph_id) const = 0;
protected:
virtual bool on_load() = 0;
virtual void on_set_font_size(float in_size) {}
protected:
std::shared_ptr<mapped_file> font_data_; // 字体文件映射数据
private:
float font_size_ = 16.0f; // 字体大小
};
bool init_font_system();
void destroy_font_system();
std::shared_ptr<font_face_interface> create_font_face(const std::filesystem::path& in_path);

@ -2,20 +2,23 @@
#include <cstdint>
#include <span>
#include <filesystem>
#include <Eigen/Eigen>
#include "pixel.h"
#include "sokol_gfx.h"
#include "misc/mapped_file/mapped_file.h"
struct image_heap_t {
int32_t width; // 图像宽度
int32_t height; // 图像高度
uint32_t width; // 图像宽度
uint32_t height; // 图像高度
sg_pixel_format pixel_format; // 像素格式
void* data; // 图像数据指针
template<typename P>
image_accessor<P> make_accessor() {
return image_accessor<P>(data, width, height);
[[nodiscard]] Eigen::Vector2i get_size() const {
return { static_cast<int32_t>(width), static_cast<int32_t>(height) };
}
[[nodiscard]] size_t get_data_size() const {
const auto& pixel_info = sg_query_pixelformat(pixel_format);
return width * height * pixel_info.bytes_per_pixel;
}
};

@ -15,7 +15,6 @@
#include "font/font_type.h"
#include "misc/mirage_type.h"
class font_face_t;
/**
* @enum draw_effect
* @brief

@ -2,6 +2,8 @@
#include "font/font_type.h"
#include "widget/mleaf_widget.h"
class font_face_interface;
class mtext_block : public mleaf_widget {
public:
void on_paint(mirage_paint_context& in_context) override;
@ -11,7 +13,7 @@ public:
update_layout();
}
void set_font(const std::shared_ptr<font_face_t>& in_font) {
void set_font(const std::shared_ptr<font_face_interface>& in_font) {
font_ = in_font;
update_layout();
}
@ -46,5 +48,5 @@ private:
float font_size_ = 48.0f;
float line_spacing_ = 1.2f;
float max_width_ = 0.0f;
std::shared_ptr<font_face_t> font_;
std::shared_ptr<font_face_interface> font_;
};