抽象字体接口
This commit is contained in:
parent
e966dadc70
commit
d5465f1953
.gitmodulesCMakeLists.txt
example/src
src
mirage_app
mirage_render
CMakeLists.txt
font
freetype_font
stb_truetype_font
image/stb_image_loader
src
font
atlas
bitmap_glyph_atlas.cppbitmap_glyph_atlas.hcolor_emoji_atlas.cppcolor_emoji_atlas.hfont_atlas.hfont_atlas_manager.cppfont_atlas_manager.h
font_face.cppfont_face.hfont_renderer
cbdt_renderer.cppcbdt_renderer.hcolr_renderer.cppcolr_renderer.hfont_renderer.hsbix_renderer.cppsbix_renderer.hstandard_bitmap_renderer.cppstandard_bitmap_renderer.hsvg_renderer.cppsvg_renderer.h
font_system.cppfont_system.hfont_type.hinterface
render
mirage_widget/widget/leaf_widget
8
.gitmodules
vendored
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)
|
||||
|
10
src/mirage_render/font/freetype_font/CMakeLists.txt
Normal file
10
src/mirage_render/font/freetype_font/CMakeLists.txt
Normal file
@ -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)
|
163
src/mirage_render/font/freetype_font/src/freetype_interface.cpp
Normal file
163
src/mirage_render/font/freetype_font/src/freetype_interface.cpp
Normal file
@ -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_{};
|
||||
};
|
8
src/mirage_render/font/stb_truetype_font/CMakeLists.txt
Normal file
8
src/mirage_render/font/stb_truetype_font/CMakeLists.txt
Normal file
@ -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
src/mirage_render/src/font/stb_truetype.h → src/mirage_render/font/stb_truetype_font/stb_truetype.h
0
src/mirage_render/src/font/stb_truetype.h → src/mirage_render/font/stb_truetype_font/stb_truetype.h
@ -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_{};
|
||||
};
|
45
src/mirage_render/src/font/atlas/bitmap_glyph_atlas.cpp
Normal file
45
src/mirage_render/src/font/atlas/bitmap_glyph_atlas.cpp
Normal file
@ -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);
|
||||
};
|
||||
|
44
src/mirage_render/src/font/atlas/color_emoji_atlas.cpp
Normal file
44
src/mirage_render/src/font/atlas/color_emoji_atlas.cpp
Normal file
@ -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字节:numSizes(bitmapSize数量)
|
||||
const uint16_t major_version = font::read_u16(cblc_data);
|
||||
const uint16_t minor_version = font::read_u16(cblc_data + 2);
|
||||
const uint32_t num_sizes = font::read_u32(cblc_data + 4);
|
||||
|
||||
if (num_sizes == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// 找到最佳匹配的bitmapSize(位图尺寸)
|
||||
uint32_t best_size_index = 0;
|
||||
uint16_t best_ppem = 0;
|
||||
uint16_t target_ppem = static_cast<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字节:numStrikes(trike数量,每个strike对应一个像素尺寸)
|
||||
// 变长:strike偏移数组
|
||||
|
||||
const uint16_t version = font::read_u16(sbix_data);
|
||||
const uint16_t flags = font::read_u16(sbix_data + 2);
|
||||
const uint32_t num_strikes = font::read_u32(sbix_data + 4);
|
||||
|
||||
if (num_strikes == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// 找到最佳匹配的strike(位图尺寸)
|
||||
// 每一个strike包含一组相同尺寸的位图
|
||||
uint32_t best_strike_index = 0;
|
||||
uint16_t best_ppem = 0;
|
||||
uint16_t target_ppem = static_cast<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; // 行间距
|
||||
};
|
||||
|
10
src/mirage_render/src/interface/font_interface.cpp
Normal file
10
src/mirage_render/src/interface/font_interface.cpp
Normal file
@ -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();
|
||||
}
|
||||
|
63
src/mirage_render/src/interface/font_interface.h
Normal file
63
src/mirage_render/src/interface/font_interface.h
Normal file
@ -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_;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user