diff --git a/.gitmodules b/.gitmodules index 764bea8..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "third_party/msdfgen"] - path = third_party/msdfgen - url = https://github.com/Chlumsky/msdfgen.git diff --git a/CMakeLists.txt b/CMakeLists.txt index acfc458..e7bc828 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,12 +30,6 @@ else () endif () add_definitions(-DMIRAGE_HDR_FORMAT=${MIRAGE_HDR_FORMAT} -DMIRAGE_PIXEL_FORMAT=${MIRAGE_PIXEL_FORMAT}) -set(MSDFGEN_USE_SKIA OFF CACHE BOOL "Use Skia for MSDFGen" FORCE) -set(MSDFGEN_USE_VCPKG OFF CACHE BOOL "Use VCPKG for MSDFGen" FORCE) -set(MSDFGEN_USE_OPENMP ON CACHE BOOL "Use OpenMP for MSDFGen" FORCE) -set(MSDFGEN_BUILD_STANDALONE ON CACHE BOOL "Build MSDFGen standalone" FORCE) -set(MSDFGEN_BUILD_STANDALONE ON CACHE BOOL "Build MSDFGen standalone" FORCE) - # 配置输出目录 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) @@ -54,7 +48,7 @@ else () add_definitions(-DDEBUG=0) endif () -add_subdirectory(third_party/msdfgen) +add_subdirectory(third_party/harfbuzz) add_subdirectory(src/sokol) add_subdirectory(src/mirage_render) add_subdirectory(src/mirage_image) diff --git a/example/src/main.cpp b/example/src/main.cpp index d2993af..cca6384 100644 --- a/example/src/main.cpp +++ b/example/src/main.cpp @@ -1,42 +1,24 @@ -// -// Created by Administrator on 25-2-26. -// - #include "mirage.h" #include "window/mwindow.h" -#include "widget/compound_widget/mbutton.h" #include "widget/panel_widget/mbox.h" -#include "pixel.h" -#include "misc/mapped_file/mapped_file.h" -#include "stb_image_loader.h" +#include "font/font_atlas_system.h" #include "font/font_face.h" -#include "texture/texture2d.h" +#include "font/font_system.h" #include "widget/widget_new.h" #include "widget/leaf_widget/mimage.h" +#include "widget/leaf_widget/mtext_block.h" int main(int argc, char* argv[]) { mirage_app::get().init(); + auto& manager = font_manager::instance(); + manager.add_font(L"C:/Windows/Fonts/msyh.ttc"); - { - font_face_t face; - face.load_from_file(L"C:/Windows/Fonts/segmdl2.ttf"); - auto name = face.get_font_name(); - auto color = face.supports_color_emoji(); - } + auto text_block = std::make_shared(); + text_block->set_text("Hello, World!"); - auto image2 = std::make_shared(); - image2->set_sampler(sampler_type::linear_clamp); - - const auto& window = mwindow::create({ 800, 600 }, L"Hello, World!"); - window->set_content( - mnew(mh_box) - - mslot(mh_box) - [ - image2 - ].stretch() - ); + const auto& window = mwindow::create({ 1024, 1024 }, L"Hello, World!"); + window->set_content(text_block); mirage_app::get().run(); return 0; diff --git a/src/mirage_core/misc/mirage_type.h b/src/mirage_core/misc/mirage_type.h index ef5f987..2839db5 100644 --- a/src/mirage_core/misc/mirage_type.h +++ b/src/mirage_core/misc/mirage_type.h @@ -164,6 +164,31 @@ struct rect_quad : std::array { } }; +//-------------- 特化类型 -------------- + +/** 表示矩形四个角颜色的类型 */ +using rect_color = rect_quad; + +/** 表示矩形四个角UV坐标的类型 */ +using rect_uv = rect_quad; + +/** 表示矩形四个角圆角半径的类型 */ +using rect_round = rect_quad; + +/** + * @brief 为rect_uv提供默认构造函数特化 + * + * 创建标准纹理坐标(0,0)到(1,1)的UV矩形。 + */ +template<> +inline rect_quad::rect_quad() : std::array{ + Eigen::Vector2f{ 0, 0 }, + Eigen::Vector2f{ 1, 0 }, + Eigen::Vector2f{ 0, 1 }, + Eigen::Vector2f{ 1, 1 } +} { +} + /** * @struct mirage_vertex_param_t * @brief 顶点参数结构,用于存储渲染顶点的额外参数 diff --git a/src/mirage_render/CMakeLists.txt b/src/mirage_render/CMakeLists.txt index 9228a13..1394169 100644 --- a/src/mirage_render/CMakeLists.txt +++ b/src/mirage_render/CMakeLists.txt @@ -5,7 +5,7 @@ retrieve_files(${CMAKE_CURRENT_SOURCE_DIR} SOURCE_FILES) add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES}) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(${PROJECT_NAME} PUBLIC mirage_core sokol msdfgen-core) +target_link_libraries(${PROJECT_NAME} PUBLIC mirage_core sokol) option(MIRAGE_STB_IMAGE_LOADER "Use stb load image" ON) diff --git a/src/mirage_render/font/font_atlas_system.h b/src/mirage_render/font/font_atlas_system.h index ea1a4d7..1072c50 100644 --- a/src/mirage_render/font/font_atlas_system.h +++ b/src/mirage_render/font/font_atlas_system.h @@ -5,13 +5,24 @@ #include "font_face.h" #include "texture/atlas/texture2d_atlas.h" -#include "font/mtsdf_generator.h" + +enum class glyph_atlas_reason_t { + success, + glyph_not_found, + atlas_full, + unknown +}; + +struct glyph_atlas_result_t { + glyph_atlas_reason_t reason; + atlas_region_t region; +}; /** - * @class MTSDFGlyphAtlas + * @class mtsdf_glyph_atlas * @brief 管理MTSDF字形图集 */ -class MTSDFGlyphAtlas { +class mtsdf_glyph_atlas { public: /** * @brief 初始化图集 @@ -21,7 +32,7 @@ public: bool initialize(const Eigen::Vector2i& in_atlas_size) { atlas_ = std::make_shared( in_atlas_size, - SG_PIXELFORMAT_RGBA8, + SG_PIXELFORMAT_R8, allocation_strategy_t::bin_packing ); @@ -30,74 +41,89 @@ public: /** * @brief 获取或创建MTSDF字形 - * @param in_code_point Unicode码点 + * @param in_glygh_index Unicode码点 * @param in_font 字体 - * @param in_generator MTSDF生成器 * @param in_font_size 字体大小 * @return 可选的图集区域 */ - std::optional get_or_create_glyph( - uint32_t in_code_point, - font_face_t& in_font, - mtsdf_generator& in_generator, + glyph_atlas_result_t get_or_create_glyph( + int32_t in_glygh_index, + const font_face_t& in_font, float in_font_size) { + glyph_atlas_result_t result{}; + // 创建缓存键 - const auto& cache_key = create_glyph_key(in_code_point, in_font.get_font_name(), in_font_size); + const auto& cache_key = create_glyph_key(in_glygh_index, in_font.get_font_name(), in_font_size); // 检查缓存 auto it = cached_glyphs_.find(cache_key); if (it != cached_glyphs_.end()) { - return it->second; + result.reason = glyph_atlas_reason_t::success; + result.region = it->second; + return result; } - // 获取字形形状 - auto glyph_shape = in_font.get_glyph_shape(in_code_point); - if (glyph_shape.empty()) { - return std::nullopt; - } + // 获取字形形状 - 传递字体大小 + auto glyph_shape = in_font.get_glyph_shape_by_index(in_glygh_index, in_font_size); + if (glyph_shape.empty()) { + result.reason = glyph_atlas_reason_t::glyph_not_found; + return result; + } // 获取字形度量 - const auto& glyph_metrics = in_font.get_glyph(in_code_point, in_font_size); + // const auto& glyph_metrics = in_font.get_glyph_by_index(in_glygh_index, in_font_size); // 计算MTSDF尺寸(添加4px内边距) - int padding = 4; - int width = static_cast(glyph_metrics.size.x()) + padding * 2; - int height = static_cast(glyph_metrics.size.y()) + padding * 2; + // const int padding = 4; + // int width = static_cast(glyph_metrics->size.x()) + padding * 2; + // int height = static_cast(glyph_metrics->size.y()) + padding * 2; // 确保最小尺寸 - width = std::max(width, 8); - height = std::max(height, 8); + // width = std::max(width, 8); + // height = std::max(height, 8); + + + // 生成MTSDF + // const auto& mtsdf_data = mtsdf_generator::generate_mtsdf( + // glyph_shape.data(), + // glyph_shape.size(), + // width, + // height, + // 3.0f // 范围值 + // ); + + // 使用stb_truetype生成MTSDF + float scale = in_font.get_scale_for_pixel_height(in_font_size); + int width, height, xoff, yoff; + auto bitmap = stbtt_GetGlyphBitmap(&in_font.get_font_info(), scale, scale, in_glygh_index, &width, &height, &xoff, &yoff); // 从图集分配空间 auto region = atlas_->allocate_region({width, height}); if (!region) { - return std::nullopt; + result.reason = glyph_atlas_reason_t::atlas_full; + return result; } - // 生成MTSDF - const auto& mtsdf_data = in_generator.generate_mtsdf( - glyph_shape.data(), - glyph_shape.size(), - width, - height, - 3.0f // 范围值 - ); - // 更新图集 - atlas_->update_region(mtsdf_data.data(), mtsdf_data.size(), region->rect); + atlas_->update_region(bitmap, width * height, region->rect); + + stbtt_FreeBitmap(bitmap, nullptr); // 缓存结果 cached_glyphs_[cache_key] = *region; - return *region; + result.reason = glyph_atlas_reason_t::success; + result.region = *region; + + return result; } /** * @brief 获取图集纹理 * @return 图集纹理 */ - auto get_texture() const { + [[nodiscard]] auto get_texture() const { return atlas_->get_texture(); } @@ -107,14 +133,14 @@ private: /** * @brief 创建字形缓存键 - * @param in_code_point Unicode码点 + * @param in_glygh_index Unicode码点 * @param in_font_name 字体名称 * @param in_font_size 字体大小 * @return 缓存键 */ - std::string create_glyph_key(uint32_t in_code_point, const std::string& in_font_name, float in_font_size) { + static std::string create_glyph_key(uint32_t in_glygh_index, const std::string& in_font_name, float in_font_size) { std::stringstream ss; - ss << std::hex << in_code_point << "_" << in_font_name << "_" << std::fixed + ss << std::hex << in_glygh_index << "_" << in_font_name << "_" << std::fixed << std::setprecision(1) << in_font_size; return ss.str(); } @@ -149,8 +175,8 @@ public: * @return 可选的图集区域 */ std::optional get_or_create_emoji( - const std::vector& in_emoji_sequence, - font_face_t& in_font, + const std::vector& in_emoji_sequence, + const font_face_t& in_font, float in_font_size) { // 创建缓存键 @@ -171,7 +197,7 @@ public: const auto& bitmap = *bitmap_opt; // 从图集分配空间 - auto region = atlas_->allocate_region({bitmap.width, bitmap.height}); + const auto& region = atlas_->allocate_region({bitmap.width, bitmap.height}); if (!region) { return std::nullopt; } @@ -189,7 +215,7 @@ public: * @brief 获取图集纹理 * @return 图集纹理 */ - auto get_texture() const { + [[nodiscard]] auto get_texture() const { return atlas_->get_texture(); } @@ -203,9 +229,9 @@ private: * @param in_font_size 字体大小 * @return 缓存键 */ - std::string create_emoji_key(const std::vector& in_sequence, float in_font_size) { + static std::string create_emoji_key(const std::vector& in_sequence, float in_font_size) { std::stringstream ss; - for (uint32_t code : in_sequence) { + for (const int32_t code : in_sequence) { ss << std::hex << code << "_"; } ss << std::fixed << std::setprecision(1) << in_font_size; diff --git a/src/mirage_render/font/font_face.cpp b/src/mirage_render/font/font_face.cpp new file mode 100644 index 0000000..a1ae997 --- /dev/null +++ b/src/mirage_render/font/font_face.cpp @@ -0,0 +1,276 @@ +#include "font/font_face.h" + +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" + +bool font_face_t::load_from_file(const std::wstring& in_font_path) { + // 读取字体文件 + if (!load_font_data(in_font_path)) { + return false; + } + + const auto font_data = (uint8_t*)font_data_->get_data(); + const auto font_data_size = font_data_->get_size(); + + // 初始化stb_truetype + const auto offset = stbtt_GetFontOffsetForIndex(font_data, 0); + if (!stbtt_InitFont(&font_info_, font_data, offset)) { + return false; + } + + // 检测字体是否支持彩色表情 + detect_color_tables(); + + return true; +} + +float font_face_t::get_ascent() const { + int ascent, descent, lineGap; + stbtt_GetFontVMetrics(&font_info_, &ascent, &descent, &lineGap); + return ascent; +} + +std::optional font_face_t::get_glyph(int32_t in_code_point, float in_font_size) const { + // 获取字形索引 + const auto glyph_index = stbtt_FindGlyphIndex(&font_info_, in_code_point); + if (glyph_index == 0) { + return {}; // 缺失字形 + } + + return get_glyph_by_index(glyph_index, in_font_size); +} + +std::optional font_face_t::get_glyph_by_index(int32_t in_glyph_index, float in_font_size) const { + glyph_t glyph; + glyph.glyph_index = in_glyph_index; + + // 计算缩放比例 + const auto scale = stbtt_ScaleForPixelHeight(&font_info_, in_font_size); + + // 获取水平度量 + int advance, lsb; + stbtt_GetGlyphHMetrics(&font_info_, in_glyph_index, &advance, &lsb); + + // 获取垂直度量 + int ascent, descent, line_gap; + stbtt_GetFontVMetrics(&font_info_, &ascent, &descent, &line_gap); + + // 获取字形边界 + int x0, y0, x1, y1; + stbtt_GetGlyphBitmapBox(&font_info_, in_glyph_index, scale, scale, &x0, &y0, &x1, &y1); + + // 设置字形数据 + glyph.size = {static_cast(x1 - x0), static_cast(y1 - y0)}; + glyph.bearing = {static_cast(lsb) * scale, static_cast(y0) }; + glyph.advance = static_cast(advance) * scale; + glyph.ascent = static_cast(ascent) * scale; + glyph.descent = static_cast(-descent) * scale; + glyph.line_gap = line_gap; + + return glyph; +} + +std::vector font_face_t::get_glyph_shape(int32_t in_code_point, float in_font_size) const { + // 获取字形索引 + int glyph_index = stbtt_FindGlyphIndex(&font_info_, in_code_point); + if (glyph_index == 0) { + return {}; // 缺失字形 + } + return get_glyph_shape_by_index(glyph_index, in_font_size); +} + +std::vector font_face_t::get_glyph_shape_by_index(int32_t in_glyph_index, float in_font_size) const { + // 计算缩放比例 - 添加这一行 + const float scale = stbtt_ScaleForPixelHeight(&font_info_, in_font_size); + + stbtt_vertex* vertices; + const auto num_vertices = stbtt_GetGlyphShape(&font_info_, in_glyph_index, &vertices); + + // 复制并转换顶点到向量 - 复制时应用缩放 + std::vector 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; +} + +float font_face_t::get_scale_for_pixel_height(float in_font_size) const { + return stbtt_ScaleForPixelHeight(&font_info_, in_font_size); +} + +std::optional font_face_t::get_color_emoji_bitmap( + const std::vector& in_emoji_sequence, float in_font_size) const { + + if (!supports_color_emoji() || in_emoji_sequence.empty()) { + return std::nullopt; + } + + // 获取主要码点的字形索引 + int32_t unicode_codepoint = in_emoji_sequence[0]; + int glyph_index = stbtt_FindGlyphIndex(&font_info_, unicode_codepoint); + + if (glyph_index == 0) { + return std::nullopt; + } + + color_emoji_bitmap_t bitmap; + + // 实际应用中需要根据字体格式(CBDT/CBLC、SBIX、COLR/CPAL、SVG)提取彩色位图 + // 简化实现,创建一个有色方块作为示例 + bitmap.width = static_cast(in_font_size); + bitmap.height = static_cast(in_font_size); + bitmap.data.resize(bitmap.width * bitmap.height * 4, 255); + + return bitmap; +} + +bool font_face_t::load_font_data(const std::wstring& in_font_path) { + font_data_ = mapped_file::create(); + if (!font_data_->map_file(in_font_path)) + return false; + + // 解析字体名称 + extract_font_name(); + + return true; +} + +uint32_t font_face_t::find_table_offset(const uint8_t* data, uint32_t fontstart, const char* tag) { + int num_tables = read_ushort(data + fontstart + 4); + uint32_t tabledir = fontstart + 12; + + for (int i = 0; i < num_tables; ++i) { + uint32_t loc = tabledir + 16 * i; + if (check_tag(data + loc, tag)) { + return read_ulong(data + loc + 8); + } + } + return 0; +} + +void font_face_t::extract_font_name() { + if (!font_data_ || !font_data_->get_data() || font_data_->get_size() == 0) { + font_name_ = "Unknown Font"; + return; + } + + const auto* data = static_cast(font_data_->get_data()); + int font_offset = stbtt_GetFontOffsetForIndex(data, 0); + + // 查找名称表 + uint32_t name_offset = find_table_offset(data, font_offset, "name"); + if (name_offset == 0) { + font_name_ = "Unknown Font"; + return; + } + + // 名称表头部 + const uint8_t* name_table = data + name_offset; + uint16_t count = read_ushort(name_table + 2); + uint16_t string_offset = read_ushort(name_table + 4); + + // 查找字体名称记录(偏好顺序:NameID 4=完整名称,NameID 1=字体族名称) + for (uint16_t i = 0; i < count; ++i) { + const uint8_t* record = name_table + 6 + 12 * i; + uint16_t platform_id = read_ushort(record); + uint16_t language_id = read_ushort(record + 4); + uint16_t name_id = read_ushort(record + 6); + uint16_t length = read_ushort(record + 8); + uint16_t offset = read_ushort(record + 10); + + // 优先使用Windows平台的英语完整名称 + bool is_english = (platform_id == 3 && language_id == 0x0409); // Windows English (US) + + if (name_id == 4 || (name_id == 1 && font_name_ == "Unknown Font")) { + // 获取字符串数据 + 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) { + uint16_t unicode = (string_data[j] << 8) | string_data[j + 1]; + + if (unicode < 0x80) { + name.push_back(static_cast(unicode)); + } else if (unicode < 0x800) { + name.push_back(static_cast(0xC0 | ((unicode >> 6) & 0x1F))); + name.push_back(static_cast(0x80 | (unicode & 0x3F))); + } else { + name.push_back(static_cast(0xE0 | ((unicode >> 12) & 0x0F))); + name.push_back(static_cast(0x80 | ((unicode >> 6) & 0x3F))); + name.push_back(static_cast(0x80 | (unicode & 0x3F))); + } + } + } + } else { // 假设为ASCII或其他单字节编码 + name = std::string(reinterpret_cast(string_data), length); + } + + if (!name.empty()) { + font_name_ = name; + + // 如果找到英语的完整名称,优先使用 + if (name_id == 4 && is_english) { + return; + } + } + } + } + + // 如果没找到有效名称,保持默认值 + if (font_name_.empty()) { + font_name_ = "Unknown Font"; + } +} + +void font_face_t::detect_color_tables() { + if (!font_data_ || !font_data_->get_data() || font_data_->get_size() == 0) { + return; + } + + const auto* data = static_cast(font_data_->get_data()); + const int font_offset = stbtt_GetFontOffsetForIndex(data, 0); + + // 检查表格是否存在 + auto table_exists = [&](const char* tag) -> bool { + const uint32_t offset = find_table_offset(data, font_offset, tag); + return offset != 0; + }; + + // 检查CBDT/CBLC表格 (Google/Android彩色表情) + has_cbdt_table_ = table_exists("CBDT") && table_exists("CBLC"); + + // 检查sbix表格 (Apple彩色表情) + has_sbix_table_ = table_exists("sbix"); + + // 检查COLR/CPAL表格 (Microsoft彩色表情) + has_colr_tables_ = table_exists("COLR") && table_exists("CPAL"); + + // 检查SVG表格 (OpenType SVG彩色表情) + has_svg_table_ = table_exists("SVG "); + + // 如果任一彩色表格存在,则标记为支持彩色 + has_color_table_ = has_cbdt_table_ || has_sbix_table_ || has_colr_tables_ || has_svg_table_; +} diff --git a/src/mirage_render/font/font_face.h b/src/mirage_render/font/font_face.h index 9456fbd..2546cc0 100644 --- a/src/mirage_render/font/font_face.h +++ b/src/mirage_render/font/font_face.h @@ -1,5 +1,4 @@ #pragma once -#include #include #include #include @@ -7,7 +6,6 @@ #include #include "font_type.h" -#define STB_TRUETYPE_IMPLEMENTATION #include "stb_truetype.h" #include "misc/mapped_file/mapped_file.h" @@ -22,23 +20,9 @@ public: * @param in_font_path 字体文件路径 * @return 是否成功加载 */ - bool load_from_file(const std::wstring& in_font_path) { - // 读取字体文件 - if (!load_font_data(in_font_path)) { - return false; - } + bool load_from_file(const std::wstring& in_font_path); - // 初始化stb_truetype - const int offset = stbtt_GetFontOffsetForIndex((uint8_t*)font_data_->get_data(), 0); - if (!stbtt_InitFont(&font_info_, (uint8_t*)font_data_->get_data(), offset)) { - return false; - } - - // 检测字体是否支持彩色表情 - detect_color_tables(); - - return true; - } + float get_ascent() const; /** * @brief 获取字形信息 @@ -46,78 +30,37 @@ public: * @param in_font_size 字体大小 * @return 字形信息 */ - glyph_t get_glyph(uint32_t in_code_point, float in_font_size) const { - glyph_t glyph; - glyph.code_point = in_code_point; + [[nodiscard]] std::optional get_glyph(int32_t in_code_point, float in_font_size) const; - // 获取字形索引 - int glyph_index = stbtt_FindGlyphIndex(&font_info_, in_code_point); - if (glyph_index == 0) { - return glyph; // 缺失字形 - } - - // 计算缩放比例 - float scale = stbtt_ScaleForPixelHeight(&font_info_, in_font_size); - - // 获取水平度量 - int advance, lsb; - stbtt_GetGlyphHMetrics(&font_info_, glyph_index, &advance, &lsb); - - // 获取垂直度量 - int ascent, descent, lineGap; - stbtt_GetFontVMetrics(&font_info_, &ascent, &descent, &lineGap); - - // 获取字形边界 - int x0, y0, x1, y1; - stbtt_GetGlyphBitmapBox(&font_info_, glyph_index, scale, scale, &x0, &y0, &x1, &y1); - - // 设置字形数据 - glyph.size = {static_cast(x1 - x0), static_cast(y1 - y0)}; - glyph.bearing = {static_cast(lsb) * scale, static_cast(y0)}; - glyph.advance = static_cast(advance) * scale; - glyph.ascent = static_cast(ascent) * scale; - glyph.descent = static_cast(-descent) * scale; - - return glyph; - } + [[nodiscard]] std::optional get_glyph_by_index(int32_t in_glyph_index, float in_font_size) const; /** * @brief 提取字形轮廓 * @param in_code_point Unicode码点 * @return 字形轮廓顶点 */ - std::vector get_glyph_shape(uint32_t in_code_point) const { - int glyph_index = stbtt_FindGlyphIndex(&font_info_, in_code_point); - if (glyph_index == 0) { - return {}; // 缺失字形 - } + [[nodiscard]] std::vector get_glyph_shape(int32_t in_code_point, float in_font_size) const; - stbtt_vertex* vertices; - const auto num_vertices = stbtt_GetGlyphShape(&font_info_, glyph_index, &vertices); + [[nodiscard]] std::vector get_glyph_shape_by_index(int32_t in_glyph_index, float in_font_size) const; - // 复制顶点到向量 - std::vector shape(vertices, vertices + num_vertices); - - // 释放stb分配的内存 - stbtt_FreeShape(&font_info_, vertices); - - return shape; - } + int32_t find_glyph_index(int32_t in_code_point) const { + return stbtt_FindGlyphIndex(&font_info_, in_code_point); + } /** * @brief 检查字体是否包含指定字形 * @param in_code_point Unicode码点 * @return 是否包含字形 */ - bool has_glyph(uint32_t in_code_point) const { - return stbtt_FindGlyphIndex(&font_info_, in_code_point) > 0; + [[nodiscard]] bool has_glyph(int32_t in_code_point) const { + return find_glyph_index(in_code_point) > 0; } /** * @brief 检查是否支持彩色表情 * @return 是否支持彩色表情 */ - bool supports_color_emoji() const { + [[nodiscard]] bool supports_color_emoji() const { return has_color_table_; } @@ -125,7 +68,7 @@ public: * @brief 获取字体名称 * @return 字体名称 */ - std::string get_font_name() const { + [[nodiscard]] std::string get_font_name() const { return font_name_; } @@ -133,7 +76,7 @@ public: * @brief 获取字体类型 * @return 字体类型 */ - std::string get_font_type() const { + [[nodiscard]] std::string get_font_type() const { return font_type_; } @@ -145,15 +88,7 @@ public: font_type_ = in_type; } - /** - * @struct color_emoji_bitmap - * @brief 彩色表情位图 - */ - struct color_emoji_bitmap { - std::vector data; // RGBA像素数据 - int width = 0; // 宽度 - int height = 0; // 高度 - }; + float get_scale_for_pixel_height(float in_font_size) const; /** * @brief 获取彩色表情位图 @@ -161,33 +96,11 @@ public: * @param in_font_size 字体大小 * @return 可选的彩色位图 */ - std::optional get_color_emoji_bitmap( - const std::vector& in_emoji_sequence, - float in_font_size) const { - - if (!supports_color_emoji() || in_emoji_sequence.empty()) { - return std::nullopt; - } - - // 获取主要码点的字形索引 - uint32_t unicode_codepoint = in_emoji_sequence[0]; - int glyph_index = stbtt_FindGlyphIndex(&font_info_, unicode_codepoint); - - if (glyph_index == 0) { - return std::nullopt; - } - - color_emoji_bitmap bitmap; - - // 实际应用中需要根据字体格式(CBDT/CBLC、SBIX、COLR/CPAL、SVG)提取彩色位图 - // 简化实现,创建一个有色方块作为示例 - bitmap.width = static_cast(in_font_size); - bitmap.height = static_cast(in_font_size); - bitmap.data.resize(bitmap.width * bitmap.height * 4, 255); - - return bitmap; - } + [[nodiscard]] std::optional get_color_emoji_bitmap( + const std::vector& in_emoji_sequence, + float in_font_size) const; + const auto& get_font_info() const { return font_info_; } private: stbtt_fontinfo font_info_{}; // stb_truetype字体信息 std::shared_ptr font_data_; // 字体文件数据 @@ -206,16 +119,7 @@ private: * @param in_font_path 字体文件路径 * @return 是否成功 */ - bool load_font_data(const std::wstring& in_font_path) { - font_data_ = mapped_file::create(); - if (!font_data_->map_file(in_font_path)) - return false; - - // 解析字体名称 - extract_font_name(); - - return true; - } + bool load_font_data(const std::wstring& in_font_path); /** * @brief 检查四字节标签是否匹配 @@ -223,7 +127,7 @@ private: * @param tag 标签字符串 * @return 是否匹配 */ - bool check_tag(const uint8_t* tag_data, const char* tag) const { + static bool check_tag(const uint8_t* tag_data, const char* tag) { return (tag_data[0] == tag[0] && tag_data[1] == tag[1] && tag_data[2] == tag[2] && @@ -235,7 +139,7 @@ private: * @param p 数据指针 * @return 16位整数值 */ - uint16_t read_ushort(const uint8_t* p) const { + static uint16_t read_ushort(const uint8_t* p) { return (uint16_t)(p[0] << 8) + p[1]; } @@ -244,7 +148,7 @@ private: * @param p 数据指针 * @return 32位整数值 */ - uint32_t read_ulong(const uint8_t* p) const { + static uint32_t read_ulong(const uint8_t* p) { return ((uint32_t)p[0] << 24) + ((uint32_t)p[1] << 16) + ((uint32_t)p[2] << 8) + p[3]; } @@ -256,130 +160,15 @@ private: * @param tag 表格标签 * @return 表格偏移量,如果不存在则为0 */ - uint32_t find_table_offset(const uint8_t* data, uint32_t fontstart, const char* tag) const { - int num_tables = read_ushort(data + fontstart + 4); - uint32_t tabledir = fontstart + 12; - - for (int i = 0; i < num_tables; ++i) { - uint32_t loc = tabledir + 16 * i; - if (check_tag(data + loc, tag)) { - return read_ulong(data + loc + 8); - } - } - return 0; - } + static uint32_t find_table_offset(const uint8_t* data, uint32_t fontstart, const char* tag); /** * @brief 从字体数据中提取字体名称 */ - void extract_font_name() { - if (!font_data_ || !font_data_->get_data() || font_data_->get_size() == 0) { - font_name_ = "Unknown Font"; - return; - } - - const uint8_t* data = static_cast(font_data_->get_data()); - int font_offset = stbtt_GetFontOffsetForIndex(data, 0); - - // 查找名称表 - uint32_t name_offset = find_table_offset(data, font_offset, "name"); - if (name_offset == 0) { - font_name_ = "Unknown Font"; - return; - } - - // 名称表头部 - const uint8_t* name_table = data + name_offset; - uint16_t count = read_ushort(name_table + 2); - uint16_t string_offset = read_ushort(name_table + 4); - - // 查找字体名称记录(偏好顺序:NameID 4=完整名称,NameID 1=字体族名称) - for (uint16_t i = 0; i < count; ++i) { - const uint8_t* record = name_table + 6 + 12 * i; - uint16_t platform_id = read_ushort(record); - uint16_t language_id = read_ushort(record + 4); - uint16_t name_id = read_ushort(record + 6); - uint16_t length = read_ushort(record + 8); - uint16_t offset = read_ushort(record + 10); - - // 优先使用Windows平台的英语完整名称 - bool is_english = (platform_id == 3 && language_id == 0x0409); // Windows English (US) - - if (name_id == 4 || (name_id == 1 && font_name_ == "Unknown Font")) { - // 获取字符串数据 - 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) { - uint16_t unicode = (string_data[j] << 8) | string_data[j + 1]; - - if (unicode < 0x80) { - name.push_back(static_cast(unicode)); - } else if (unicode < 0x800) { - name.push_back(static_cast(0xC0 | ((unicode >> 6) & 0x1F))); - name.push_back(static_cast(0x80 | (unicode & 0x3F))); - } else { - name.push_back(static_cast(0xE0 | ((unicode >> 12) & 0x0F))); - name.push_back(static_cast(0x80 | ((unicode >> 6) & 0x3F))); - name.push_back(static_cast(0x80 | (unicode & 0x3F))); - } - } - } - } else { // 假设为ASCII或其他单字节编码 - name = std::string(reinterpret_cast(string_data), length); - } - - if (!name.empty()) { - font_name_ = name; - - // 如果找到英语的完整名称,优先使用 - if (name_id == 4 && is_english) { - return; - } - } - } - } - - // 如果没找到有效名称,保持默认值 - if (font_name_.empty()) { - font_name_ = "Unknown Font"; - } - } + void extract_font_name(); /** * @brief 检测字体是否支持彩色表情 */ - void detect_color_tables() { - if (!font_data_ || !font_data_->get_data() || font_data_->get_size() == 0) { - return; - } - - const auto* data = static_cast(font_data_->get_data()); - const int font_offset = stbtt_GetFontOffsetForIndex(data, 0); - - // 检查表格是否存在 - auto table_exists = [&](const char* tag) -> bool { - const uint32_t offset = find_table_offset(data, font_offset, tag); - return offset != 0; - }; - - // 检查CBDT/CBLC表格 (Google/Android彩色表情) - has_cbdt_table_ = table_exists("CBDT") && table_exists("CBLC"); - - // 检查sbix表格 (Apple彩色表情) - has_sbix_table_ = table_exists("sbix"); - - // 检查COLR/CPAL表格 (Microsoft彩色表情) - has_colr_tables_ = table_exists("COLR") && table_exists("CPAL"); - - // 检查SVG表格 (OpenType SVG彩色表情) - has_svg_table_ = table_exists("SVG "); - - // 如果任一彩色表格存在,则标记为支持彩色 - has_color_table_ = has_cbdt_table_ || has_sbix_table_ || has_colr_tables_ || has_svg_table_; - } + void detect_color_tables(); }; diff --git a/src/mirage_render/font/font_system.cpp b/src/mirage_render/font/font_system.cpp new file mode 100644 index 0000000..1a629f7 --- /dev/null +++ b/src/mirage_render/font/font_system.cpp @@ -0,0 +1,98 @@ +#include "font_system.h" + +text_layout_t font_manager::layout_text(const std::string& text, + const std::shared_ptr& in_font, float font_size, + float max_width, + float line_spacing) { + text_layout_t layout; + float scale = in_font->get_scale_for_pixel_height(font_size); + + // 布局变量 + float cursor_x = 0.0f; + float cursor_y = 0.0f; + const float line_height = font_size * line_spacing; + const float ascent = in_font->get_ascent() * scale; + float baseline = cursor_y + ascent; + + // 处理每个字形 + for (const auto& c : text) { + const auto glyph_index = in_font->find_glyph_index(c); + // 获取字形度量 + const auto& glyph_metrics = in_font->get_glyph_by_index(glyph_index, font_size); + + if (!glyph_metrics) { + continue; + } + + const auto& region = get_or_create_glyph_by_index(glyph_metrics->glyph_index, in_font, font_size); + if (!region) { + continue; + } + + // 检查换行 + if (max_width > 0 && + cursor_x + glyph_metrics->advance > max_width && + cursor_x > 0) { + cursor_x = 0.0f; + cursor_y += line_height; + baseline = cursor_y + ascent; + } + + // **计算最终位置** + float glyph_x = cursor_x + glyph_metrics->bearing.x(); + float glyph_y = ascent - glyph_metrics->descent + glyph_metrics->line_gap + glyph_metrics->bearing.y(); + + auto& glyph_position = layout.glyphs.emplace_back(); + glyph_position.glyph_index = glyph_index; + glyph_position.position = { glyph_x, glyph_y }; + glyph_position.size = glyph_metrics->size; + glyph_position.region = *region; + + // 前进光标 + cursor_x += glyph_metrics->advance; + cursor_y += 0; // 通常为0,除非是垂直文本 + } + + // 设置布局总体尺寸 + layout.total_size = {cursor_x, cursor_y + line_height}; + + return layout; +} + +std::optional font_manager::get_or_create_glyph_by_index(uint32_t in_glyph_id, + std::shared_ptr in_font, float in_font_size) { + + // 获取字体 + if (!in_font) { + return std::nullopt; + } + + // 获取字形 + 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; + // 尝试获取缓存 + for (auto& atlas : glyph_atlases_) { + result = atlas.get_or_create_glyph(in_glyph_id, *in_font, in_font_size); + if (result.reason == glyph_atlas_reason_t::glyph_not_found) { + return std::nullopt; + } + if (result.reason == glyph_atlas_reason_t::success) { + return result.region; + } + } + + if (result.reason == glyph_atlas_reason_t::atlas_full) { + // 创建新的图集 + auto& atlas = glyph_atlases_.emplace_back(); + atlas.initialize({ 1024, 1024 }); + result = atlas.get_or_create_glyph(in_glyph_id, *in_font, in_font_size); + if (result.reason == glyph_atlas_reason_t::success) { + return result.region; + } + } + return std::nullopt; +} diff --git a/src/mirage_render/font/font_system.h b/src/mirage_render/font/font_system.h index aefbd11..025a603 100644 --- a/src/mirage_render/font/font_system.h +++ b/src/mirage_render/font/font_system.h @@ -3,6 +3,7 @@ #include #include "emoji_detector.h" +#include "font_atlas_system.h" #include "font_face.h" // font_system.h - 字体管理系统 @@ -12,13 +13,25 @@ */ class font_manager { public: + static font_manager& instance() { + static font_manager instance; + return instance; + } + + void destroy() { + fonts_.clear(); + emoji_font_ids_.clear(); + primary_font_id_ = -1; + next_font_id_ = 0; + } + /** * @brief 添加字体 * @param in_font_path 字体文件路径 * @param in_font_type 字体类型(regular, bold, italic, emoji等) * @return 字体ID */ - int addFont(const std::string& in_font_path, const std::string& in_font_type = "regular") { + int add_font(const std::wstring& in_font_path, const std::string& in_font_type = "regular") { auto font = std::make_shared(); if (!font->load_from_file(in_font_path)) { return -1; @@ -55,7 +68,7 @@ public: * @param in_code_point Unicode码点 * @return 匹配字体 */ - std::shared_ptr getFontForCodePoint(uint32_t in_code_point) { + std::shared_ptr get_font_for_code_point(uint32_t in_code_point) { // 主字体 auto primary = get_primary_font(); if (primary && primary->has_glyph(in_code_point)) { @@ -102,8 +115,26 @@ public: return !emoji_font_ids_.empty(); } + text_layout_t layout_text( + const std::string& text, + const std::shared_ptr& in_font, + float font_size, + float max_width = 0.0f, + float line_spacing = 1.2f); + + std::optional get_or_create_glyph_by_index(uint32_t in_glyph_id, std::shared_ptr in_font, float in_font_size); + + const auto& get_glyph_atlases() const { + return glyph_atlases_; + } + const auto& get_emoji_atlases() const { + return emoji_atlases_; + } private: std::unordered_map> fonts_; + std::vector glyph_atlases_; + std::vector emoji_atlases_; + std::vector emoji_font_ids_; int primary_font_id_ = -1; int next_font_id_ = 0; diff --git a/src/mirage_render/font/font_type.h b/src/mirage_render/font/font_type.h index d6acd32..92e4e31 100644 --- a/src/mirage_render/font/font_type.h +++ b/src/mirage_render/font/font_type.h @@ -32,12 +32,13 @@ enum class vertical_text_alignment_t { * @brief 字形基本信息 */ struct glyph_t { - uint32_t code_point = 0; ///< Unicode码点 - Eigen::Vector2f size; ///< 字形尺寸 - Eigen::Vector2f bearing; ///< 字形基线相对位置 - float advance = 0.0f; ///< 水平前进量 - float ascent = 0.0f; ///< 上升高度 - float descent = 0.0f; ///< 下降高度 + uint32_t glyph_index = 0; ///< 字形索引 + Eigen::Vector2f size; ///< 字形尺寸 + Eigen::Vector2f bearing; ///< 字形基线相对位置 + float advance = 0.0f; ///< 水平前进量 + float ascent = 0.0f; ///< 上升高度 + float descent = 0.0f; ///< 下降高度 + float line_gap = 0.0f; ///< 行间距 }; /** @@ -45,7 +46,7 @@ struct glyph_t { * @brief 已定位的字形 */ struct positioned_glyph_t { - uint32_t codePoint = 0; ///< Unicode码点 + uint32_t code_point = 0; ///< Unicode码点 Eigen::Vector2f position; ///< 屏幕位置 rect_t<> bounds; ///< 边界矩形 float advance = 0.0f; ///< 前进量 @@ -127,3 +128,32 @@ struct text_style_t { bool word_wrap = true; ///< 自动换行 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 { + int32_t glyph_index; // 字符码点 + Eigen::Vector2f position; // 屏幕位置 + Eigen::Vector2f size; // 字形尺寸 + atlas_region_t region; // 纹理图集区域 + }; + + std::vector glyphs; // 所有字形位置 + Eigen::Vector2f total_size; // 文本总尺寸 +}; + +/** + * @struct color_emoji_bitmap_t + * @brief 彩色表情位图 + */ +struct color_emoji_bitmap_t { + std::vector data; // RGBA像素数据 + int width = 0; // 宽度 + int height = 0; // 高度 +}; diff --git a/src/mirage_render/font/mtsdf_generator.h b/src/mirage_render/font/mtsdf_generator.h deleted file mode 100644 index 66485e6..0000000 --- a/src/mirage_render/font/mtsdf_generator.h +++ /dev/null @@ -1,150 +0,0 @@ -#pragma once -#include "msdfgen.h" -#include "stb_truetype.h" -// mtsdf_generator.h - MTSDF生成 - -/** - * @class mtsdf_generator - * @brief 使用msdfgen生成多通道有符号距离场 - */ -class mtsdf_generator { -public: - /** - * @brief 从字形轮廓生成MTSDF - * @param in_vertices stb_truetype顶点数组 - * @param in_num_vertices 顶点数量 - * @param in_width 输出宽度 - * @param in_height 输出高度 - * @param in_range 距离场范围 - * @return RGBA8像素数据 - */ - std::vector generate_mtsdf( - const stbtt_vertex* in_vertices, - size_t in_num_vertices, - int in_width, - int in_height, - float in_range) { - - // 创建Shape以存储字形轮廓 - msdfgen::Shape shape; - - // 处理顶点创建轮廓和边缘 - if (in_num_vertices > 0) { - msdfgen::Point2 contour_start; - msdfgen::Point2 current_point; - msdfgen::Contour* current_contour = nullptr; - - for (size_t i = 0; i < in_num_vertices; i++) { - const stbtt_vertex& v = in_vertices[i]; - - switch (v.type) { - case STBTT_vmove: - // 开始新轮廓 - current_contour = &shape.addContour(); - contour_start = msdfgen::Point2(v.x, v.y); - current_point = contour_start; - break; - - case STBTT_vline: - // 添加线段 - if (current_contour) { - current_contour->addEdge(msdfgen::EdgeHolder( - new msdfgen::LinearSegment(current_point, msdfgen::Point2(v.x, v.y)))); - current_point = msdfgen::Point2(v.x, v.y); - } - break; - - case STBTT_vcurve: - // 添加二次曲线段 - if (current_contour) { - current_contour->addEdge(msdfgen::EdgeHolder( - new msdfgen::QuadraticSegment( - current_point, - msdfgen::Point2(v.cx, v.cy), - msdfgen::Point2(v.x, v.y) - ))); - current_point = msdfgen::Point2(v.x, v.y); - } - break; - - case STBTT_vcubic: - // 添加三次曲线段 - if (current_contour) { - current_contour->addEdge(msdfgen::EdgeHolder( - new msdfgen::CubicSegment( - current_point, - msdfgen::Point2(v.cx, v.cy), - msdfgen::Point2(v.cx1, v.cy1), - msdfgen::Point2(v.x, v.y) - ))); - current_point = msdfgen::Point2(v.x, v.y); - } - break; - default: ; - } - } - - // 如果轮廓的最后一点不是起点,则闭合轮廓 - if (current_contour && current_point != contour_start) { - current_contour->addEdge(msdfgen::EdgeHolder(new msdfgen::LinearSegment(current_point, contour_start))); - } - } - - // 标准化形状(修正方向等) - shape.normalize(); - - // 创建输出位图(4通道浮点) - msdfgen::Bitmap bitmap(in_width, in_height); - - // 计算边界 - double l, b, r, t; - shape.bound(l, b, r, t); - - // 计算缩放以适应位图(带内边距) - double padding = 4.0; // 字形周围的像素内边距 - double scale_x = (in_width - 2 * padding) / (r - l > 0 ? r - l : 1); - double scale_y = (in_height - 2 * padding) / (t - b > 0 ? t - b : 1); - double scale = std::min(scale_x, scale_y); - - // 居中形状 - double tx = 0.5*(in_width - scale*(l + r)); - double ty = 0.5*(in_height - scale*(b + t)); - - // 设置错误修正配置 - msdfgen::ErrorCorrectionConfig error_correction_config; - - // 生成MTSDF - 使用4通道版本 - msdfgen::generateMTSDF(bitmap, shape, in_range, - msdfgen::Vector2(scale, scale), msdfgen::Vector2(tx, ty), - error_correction_config, true); // true启用重叠支持 - - // 转换为RGBA8 - std::vector result(in_width * in_height * 4); - for (int y = 0; y < in_height; y++) { - for (int x = 0; x < in_width; x++) { - const float* pixel = bitmap(x, y); - size_t index = (y * in_width + x) * 4; - - // 转换浮点通道到字节 - result[index + 0] = msdf_float_to_byte(pixel[0]); // 红色通道 - result[index + 1] = msdf_float_to_byte(pixel[1]); // 绿色通道 - result[index + 2] = msdf_float_to_byte(pixel[2]); // 蓝色通道 - result[index + 3] = msdf_float_to_byte(pixel[3]); // Alpha通道(完全不透明) - } - } - - return result; - } - -private: - /** - * @brief 将MSDF浮点值转换为字节 - * @param value 浮点值范围[-1, 1] - * @return 字节值范围[0, 255] - */ - static uint8_t msdf_float_to_byte(float value) { - // 将[-1, 1]映射到[0, 255] - const auto byte = static_cast((value * 0.5f + 0.5f) * 255.0f); - return static_cast(std::max(0, std::min(255, byte))); - } -}; diff --git a/src/mirage_render/font/stb_truetype_impl.cpp b/src/mirage_render/font/stb_truetype_impl.cpp deleted file mode 100644 index 8b13789..0000000 --- a/src/mirage_render/font/stb_truetype_impl.cpp +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/mirage_render/render/render_elements.cpp b/src/mirage_render/render/render_elements.cpp index 6c4db93..6cc617e 100644 --- a/src/mirage_render/render/render_elements.cpp +++ b/src/mirage_render/render/render_elements.cpp @@ -1,6 +1,9 @@ #include "render_elements.h" + +#include "font/font_system.h" #include "shaders/mirage_rounded_rect.hlsl.h" #include "shaders/mirage_image.hlsl.h" +#include "shaders/mirage_text.hlsl.h" template void compute_rect_vertices(const Eigen::MatrixBase& in_pos, @@ -208,7 +211,7 @@ void render_elements::make_rounded_rect(const Eigen::Vector2f& in_pos, const Eig void render_elements::make_image(const Eigen::Vector2f& in_pos, const Eigen::Vector2f& in_size, const geometry_t& in_geometry, const sg_image& in_image, sampler_type in_sampler_type, - const linear_color& in_color, const rect_uv& in_uv, float in_rotation_radians, const Eigen::Vector2f& in_pivot, + const rect_color& in_color, const rect_uv& in_uv, float in_rotation_radians, const Eigen::Vector2f& in_pivot, const Eigen::Vector2f& in_scale) { const auto& sampler = texture_sampler_builder::get_sampler(in_sampler_type); make_image(in_pos, @@ -225,7 +228,7 @@ void render_elements::make_image(const Eigen::Vector2f& in_pos, const Eigen::Vec void render_elements::make_image(const Eigen::Vector2f& in_pos, const Eigen::Vector2f& in_size, const geometry_t& in_geometry, const sg_image& in_image, const std::shared_ptr& in_sampler, - const linear_color& in_color, const rect_uv& in_uv, float in_rotation_radians, const Eigen::Vector2f& in_pivot, + const rect_color& in_color, const rect_uv& in_uv, float in_rotation_radians, const Eigen::Vector2f& in_pivot, const Eigen::Vector2f& in_scale) { if (!in_sampler) { return; @@ -242,6 +245,32 @@ void render_elements::make_image(const Eigen::Vector2f& in_pos, const Eigen::Vec make_rect(in_pos, in_size, { in_color }, in_geometry, {}, {}, {}, in_uv, in_rotation_radians, in_pivot, in_scale); } +void render_elements::make_text(const text_layout_t& in_layout, const Eigen::Vector2f& in_pos, + const Eigen::Vector2f& in_size, const geometry_t& in_geometry, const rect_color& in_color, + float in_rotation_radians, const Eigen::Vector2f& in_pivot, const Eigen::Vector2f& in_scale) { + const auto& sampler = texture_sampler_builder::get_sampler(sampler_type::linear_clamp); + + for (const auto& position : in_layout.glyphs) { + const auto& p = position; + const auto& region = p.region; + const auto& size = p.size; + const auto& uv = region.uv_rect; + + if (auto texture = region.texture.lock()) { + // 构建完整的批次键 + batch_key new_key; + new_key.pipeline = text_pipeline_; + new_key.image = texture->get_image(); + new_key.sampler = *sampler; + + const Eigen::Vector2f& real_pos = p.position.array() + in_pos.array(); + + ensure_batch_compatibility(new_key); + make_rect(real_pos, size, in_color, in_geometry, {}, {}, {}, uv, in_rotation_radians, in_pivot, in_scale); + } + } +} + // 确保缓冲区容量 void render_elements::ensure_buffer_capacity(uint32_t vertex_count, uint32_t index_count) { // 如果现有缓冲区不够大,重新创建 @@ -343,11 +372,15 @@ void render_elements::load_mirage_pipelines() { #else auto format = MIRAGE_PIXEL_FORMAT; #endif - auto rounded_rect_shader = sg_make_shader(get_mirage_rounded_rect_shader_desc()); - auto rounded_rect_pipeline_desc = get_mirage_rounded_rect_pipeline_desc(rounded_rect_shader, format, 1); + const auto rounded_rect_shader = sg_make_shader(get_mirage_rounded_rect_shader_desc()); + const auto rounded_rect_pipeline_desc = get_mirage_rounded_rect_pipeline_desc(rounded_rect_shader, format, 1); rounded_rect_pipeline_ = sg_make_pipeline(rounded_rect_pipeline_desc); - auto image_shader = sg_make_shader(get_mirage_image_shader_desc()); - auto image_pipeline_desc = get_mirage_image_pipeline_desc(image_shader, format, 1); + const auto image_shader = sg_make_shader(get_mirage_image_shader_desc()); + const auto image_pipeline_desc = get_mirage_image_pipeline_desc(image_shader, format, 1); image_pipeline_ = sg_make_pipeline(image_pipeline_desc); + + const auto font_shader = sg_make_shader(get_mirage_text_shader_desc()); + const auto font_pipeline_desc = get_mirage_text_pipeline_desc(font_shader, format, 1); + text_pipeline_ = sg_make_pipeline(font_pipeline_desc); } diff --git a/src/mirage_render/render/render_elements.h b/src/mirage_render/render/render_elements.h index 47dffd4..262b2b1 100644 --- a/src/mirage_render/render/render_elements.h +++ b/src/mirage_render/render/render_elements.h @@ -12,8 +12,10 @@ #include "sokol_gfx.h" #include "color.h" #include "texture_sampler.h" +#include "font/font_type.h" #include "misc/mirage_type.h" +class font_face_t; /** * @enum draw_effect * @brief 定义渲染效果类型 @@ -89,31 +91,6 @@ struct draw_batch { uint32_t index_count = 0; }; -//-------------- 特化类型 -------------- - -/** 表示矩形四个角颜色的类型 */ -using rect_color = rect_quad; - -/** 表示矩形四个角UV坐标的类型 */ -using rect_uv = rect_quad; - -/** 表示矩形四个角圆角半径的类型 */ -using rect_round = rect_quad; - -/** - * @brief 为rect_uv提供默认构造函数特化 - * - * 创建标准纹理坐标(0,0)到(1,1)的UV矩形。 - */ -template<> -inline rect_quad::rect_quad() : std::array{ - Eigen::Vector2f{ 0, 0 }, - Eigen::Vector2f{ 1, 0 }, - Eigen::Vector2f{ 0, 1 }, - Eigen::Vector2f{ 1, 1 } -} { -} - /** * @class render_elements * @brief 管理渲染资源和批处理渲染操作 @@ -267,7 +244,7 @@ public: const geometry_t& in_geometry, const sg_image& in_image, sampler_type in_sampler_type, - const linear_color& in_color = { 1, 1, 1, 1 }, + const rect_color& in_color = { { 1, 1, 1, 1 } }, const rect_uv& in_uv = {}, float in_rotation_radians = 0.0f, const Eigen::Vector2f& in_pivot = Eigen::Vector2f(0.5f, 0.5f), @@ -278,12 +255,21 @@ public: const geometry_t& in_geometry, const sg_image& in_image, const std::shared_ptr& in_sampler, - const linear_color& in_color = { 1, 1, 1, 1 }, + const rect_color& in_color = { { 1, 1, 1, 1 } }, const rect_uv& in_uv = {}, float in_rotation_radians = 0.0f, const Eigen::Vector2f& in_pivot = Eigen::Vector2f(0.5f, 0.5f), const Eigen::Vector2f& in_scale = Eigen::Vector2f(1.f, 1.f)); + void make_text(const text_layout_t& in_layout, + const Eigen::Vector2f& in_pos, + const Eigen::Vector2f& in_size, + const geometry_t& in_geometry, + const rect_color& in_color = { { 1, 1, 1, 1 } }, + float in_rotation_radians = 0.0f, + const Eigen::Vector2f& in_pivot = Eigen::Vector2f(0.5f, 0.5f), + const Eigen::Vector2f& in_scale = Eigen::Vector2f(1.f, 1.f)); + //-------------- 统计信息 -------------- /** @@ -402,4 +388,7 @@ private: /** 图片渲染管线 */ sg_pipeline image_pipeline_{}; + + /** 文本渲染管线 */ + sg_pipeline text_pipeline_{}; }; diff --git a/src/mirage_render/shaders/mirage_text.slang b/src/mirage_render/shaders/mirage_text.slang new file mode 100644 index 0000000..2760481 --- /dev/null +++ b/src/mirage_render/shaders/mirage_text.slang @@ -0,0 +1,35 @@ +#include "mirage_util.slang" + +cbuffer ParamBuffer +{ + matrix transform; +}; + +SamplerState textureSampler; +Texture2D texture; + +struct PSInput { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + float4 color : COLOR; +}; + +[shader("vertex")] +PSInput vertex_main(VSInput input) +{ + PSInput output; + + // 并通过常量缓冲区或结构化缓冲区传递 + output.position = mul(float4(input.position, 0.0, 1.0), transform); + output.uv = input.uv; + output.color = input.color; + + return output; +} + +[shader("pixel")] +float4 pixel_main(PSInput input) : SV_Target +{ + float4 color = texture.Sample(textureSampler, input.uv); + return float4(color.r) * input.color; +} diff --git a/src/mirage_render/texture/atlas/texture2d_atlas.cpp b/src/mirage_render/texture/atlas/texture2d_atlas.cpp index a1b136e..b7690dc 100644 --- a/src/mirage_render/texture/atlas/texture2d_atlas.cpp +++ b/src/mirage_render/texture/atlas/texture2d_atlas.cpp @@ -9,9 +9,12 @@ #include "texture/texture_map_utils.h" // 从知识库引用 #include +#include "font/font_type.h" + texture2d_atlas::texture2d_atlas(const Eigen::Vector2i& size, sg_pixel_format pixel_format, - allocation_strategy_t strategy) + allocation_strategy_t strategy) { + texture_ = std::make_shared(); // 创建底层纹理 rw_texture2d_data tex_data; tex_data.size = size; @@ -45,8 +48,8 @@ std::optional texture2d_atlas::allocate_region(const Eigen::Vect } // 分配成功,创建区域信息 - int region_id = next_region_id_++; - atlas_region_t region(*rect_opt, calculate_uv_rect(*rect_opt), region_id); + const int region_id = next_region_id_++; + atlas_region_t region(region_id, *rect_opt, texture_, calculate_uv_rect(*rect_opt)); // 存储区域信息 allocated_regions_[region.id] = region; @@ -109,10 +112,10 @@ void texture2d_atlas::update_region(const void* data, size_t data_size, const re } // 更新纹理区域 - auto map = texture_->map(texture_map_type::write); + texture_->sub_update({ data, data_size }, rect); // 使用texture_map_utils更新区域 - texture_map_utils::write_region(*map.get(), data, rect, texture_->get_bytes_per_pixel()); + // texture_map_utils::write_region(*map.get(), data, rect, texture_->get_bytes_per_pixel()); } float texture2d_atlas::get_free_space_ratio() const { @@ -139,15 +142,20 @@ std::vector texture2d_atlas::get_allocated_regions() const { return result; } -rect_t texture2d_atlas::calculate_uv_rect(const rect_t& rect) const { +rect_uv texture2d_atlas::calculate_uv_rect(const rect_t& rect) const { // 计算UV坐标 (归一化到0-1范围) - const Eigen::Vector2i texture_size = texture_->get_size(); - float left = static_cast(rect.left()) / texture_size.x(); - float top = static_cast(rect.top()) / texture_size.y(); - float width = static_cast(rect.width()) / texture_size.x(); - float height = static_cast(rect.height()) / texture_size.y(); + const Eigen::Vector2i& texture_size = texture_->get_size(); + const float left = static_cast(rect.left()) / texture_size.x(); + const float top = static_cast(rect.top()) / texture_size.y(); + const float right = static_cast(rect.right()) / texture_size.x(); + const float bottom = static_cast(rect.bottom()) / texture_size.y(); - return rect_t({left, top}, {width, height}); + const Eigen::Vector2f left_top(left, top); + const Eigen::Vector2f left_bottom(left, bottom); + const Eigen::Vector2f right_top(right, top); + const Eigen::Vector2f right_bottom(right, bottom); + + return { left_top, right_top, left_bottom, right_bottom }; } std::unique_ptr texture2d_atlas::create_allocator(allocation_strategy_t strategy, diff --git a/src/mirage_render/texture/atlas/texture2d_atlas.h b/src/mirage_render/texture/atlas/texture2d_atlas.h index 60befdb..e1ca218 100644 --- a/src/mirage_render/texture/atlas/texture2d_atlas.h +++ b/src/mirage_render/texture/atlas/texture2d_atlas.h @@ -106,7 +106,7 @@ public: * @param rect 像素坐标中的矩形 * @return UV坐标中的矩形 */ - rect_t calculate_uv_rect(const rect_t& rect) const; + rect_uv calculate_uv_rect(const rect_t& rect) const; private: /** diff --git a/src/mirage_render/texture/atlas/texture_atlas_types.h b/src/mirage_render/texture/atlas/texture_atlas_types.h index 1a3a321..9f36107 100644 --- a/src/mirage_render/texture/atlas/texture_atlas_types.h +++ b/src/mirage_render/texture/atlas/texture_atlas_types.h @@ -5,6 +5,7 @@ */ #include "geometry/rect.h" +class rw_texture2d; // 前向声明 class atlas_allocator; @@ -23,15 +24,18 @@ enum class allocation_strategy_t { * @brief 表示纹理图集中的一个已分配区域 */ struct atlas_region_t { - rect_t rect; ///< 像素坐标中的矩形 - rect_t uv_rect; ///< UV坐标中的矩形 (0-1范围) - int id; ///< 区域唯一标识符 + int id; ///< 区域唯一标识符 + rect_t rect; ///< 像素坐标中的矩形 + std::weak_ptr texture; ///< 关联的纹理 + rect_uv uv_rect; ///< UV坐标中的矩形 (0-1范围) // 构造函数 atlas_region_t() = default; - atlas_region_t(const rect_t& r, const rect_t& uv, const int region_id) : - rect(r), - uv_rect(uv), - id(region_id) {} + atlas_region_t(const int in_region_id, const rect_t& in_r, const std::shared_ptr& in_texture, + const rect_uv& in_uv) : + id(in_region_id), + rect(in_r), + texture(in_texture), + uv_rect(in_uv) {} }; diff --git a/src/mirage_render/texture/rw_texture2d.cpp b/src/mirage_render/texture/rw_texture2d.cpp index 3c73086..86493b4 100644 --- a/src/mirage_render/texture/rw_texture2d.cpp +++ b/src/mirage_render/texture/rw_texture2d.cpp @@ -4,101 +4,25 @@ #include "image_accessor_factory.h" -void rw_texture2d::create(const texture_data& in_data) { - sg_image_desc desc{}; - desc.type = SG_IMAGETYPE_2D; - desc.render_target = false; - desc.width = in_data.size.x(); - desc.height = in_data.size.y(); - desc.num_slices = 1; - desc.num_mipmaps = 1; - desc.usage = SG_USAGE_DYNAMIC; - desc.pixel_format = in_data.pixel_format; - desc.sample_count = 1; - desc.data = in_data.get_image_data(); - - create_from_desc(desc); -} - void rw_texture2d::update(const sg_image_data& in_data) { sg_update_image(get_image(), in_data); } -void rw_texture2d::sub_update(const sg_range& in_data, const rect_t& in_rect) { - // 验证输入参数 - if (!in_data.ptr || in_data.size == 0 || in_rect.width() <= 0 || in_rect.height() <= 0) { return; } - - // 获取像素格式信息 - const auto& pixel_info = sg_query_pixelformat(get_pixel_format()); - - // 计算源数据中一行的大小(字节数) - const int src_row_size = in_rect.width() * pixel_info.bytes_per_pixel; - - try { - // 映射纹理用于写入,现在返回的是unique_ptr - const auto mapped = map(texture_map_type::write_discard); - if (!mapped) { - return; // 映射失败 - } - - // 获取源数据的指针 - const auto src_ptr = static_cast(in_data.ptr); - - // 计算目标数据的起始位置 - std::byte* dst_ptr = mapped->data.data(); - - // 遍历矩形的每一行 - for (int y = 0; y < in_rect.height(); ++y) { - // 计算目标行在纹理数据中的位置 - std::byte* dst_row = dst_ptr + (in_rect.top() + y) * mapped->row_pitch + in_rect.left() * pixel_info. - bytes_per_pixel; - - // 计算源数据行的位置 - const std::byte* src_row = src_ptr + y * src_row_size; - - // 复制这一行的数据 - std::memcpy(dst_row, src_row, src_row_size); - } - } - catch (const std::exception& e) { - // 可以记录错误或处理异常 - std::cerr << "Error in sub_update: " << e.what() << std::endl; - } -} - void rw_texture2d::resize(const Eigen::Vector2i& in_size) { const auto& current_size = get_size(); // 如果大小相同,直接返回 if (current_size == in_size) { return; } - // 获取当前纹理的像素格式 - auto pixel_format = get_pixel_format(); - const auto& pixel_info = sg_query_pixelformat(pixel_format); - // 保存当前纹理数据(如果有有效数据) std::vector old_data; + Eigen::Vector2i old_size; if (is_valid() && current_size.x() > 0 && current_size.y() > 0) { try { // 映射当前纹理进行读取 - auto mapped = map(texture_map_type::read); - if (mapped) { - // 计算需要复制的行数和每行的字节数 - int copy_rows = std::min(current_size.y(), in_size.y()); - int copy_bytes_per_row = std::min(current_size.x(), in_size.x()) * pixel_info.bytes_per_pixel; - - // 为旧数据分配空间 - old_data.resize(copy_rows * copy_bytes_per_row); - - // 复制需要保留的数据 - for (int y = 0; y < copy_rows; ++y) { - std::memcpy(old_data.data() + y * copy_bytes_per_row, - mapped->data.data() + y * mapped->row_pitch, - copy_bytes_per_row); - } - } - // mapped离开作用域会自动解除映射 + old_data = read(); + old_size = current_size; } catch (const std::exception& e) { // 读取失败,继续创建空纹理 @@ -110,7 +34,6 @@ void rw_texture2d::resize(const Eigen::Vector2i& in_size) { sg_image_desc desc = get_image_desc(); desc.width = in_size.x(); desc.height = in_size.y(); - desc.pixel_format = pixel_format; // 清除旧的图像 clear(); @@ -118,26 +41,13 @@ void rw_texture2d::resize(const Eigen::Vector2i& in_size) { // 创建新的图像 create_from_desc(desc); - // 如果有旧数据,复制到新纹理 - if (!old_data.empty() && is_valid()) { - try { - // 映射新纹理进行写入 - if (const auto mapped = map(texture_map_type::write)) { - // 计算需要复制的行数和每行的字节数 - const auto copy_rows = std::min(current_size.y(), in_size.y()); - const auto copy_bytes_per_row = std::min(current_size.x(), in_size.x()) * pixel_info.bytes_per_pixel; + // 如果有旧数据,尝试恢复 + if (!old_data.empty() && old_size.x() > 0 && old_size.y() > 0) { + // 计算复制区域 + const auto copy_size = Eigen::Vector2i(std::min(old_size.x(), in_size.x()), std::min(old_size.y(), in_size.y())); + const auto copy_rect = rect_t(Eigen::Vector2i::Zero(), copy_size); - // 复制数据到新纹理 - for (int y = 0; y < copy_rows; ++y) { - std::memcpy(mapped->data.data() + y * mapped->row_pitch, - old_data.data() + y * copy_bytes_per_row, - copy_bytes_per_row); - } - } - } - catch (const std::exception& e) { - // 写入失败 - std::cerr << "Error writing to new texture: " << e.what() << std::endl; - } + // 更新纹理数据 + sub_update(SG_RANGE(old_data), copy_rect); } } diff --git a/src/mirage_render/texture/rw_texture2d.h b/src/mirage_render/texture/rw_texture2d.h index 4105a11..b336036 100644 --- a/src/mirage_render/texture/rw_texture2d.h +++ b/src/mirage_render/texture/rw_texture2d.h @@ -5,7 +5,7 @@ struct rw_texture2d_data : texture_data { void* data_ptr; virtual sg_image_data get_image_data() const override { const auto& pixel_info = sg_query_pixelformat(pixel_format); - sg_image_data data; + sg_image_data data{}; data.subimage[0][0].ptr = data_ptr; data.subimage[0][0].size = size.x() * size.y() * pixel_info.bytes_per_pixel; return data; @@ -14,8 +14,10 @@ struct rw_texture2d_data : texture_data { class rw_texture2d : public texture { public: - virtual void create(const texture_data& in_data) override; - virtual void update(const sg_image_data& in_data) override; - virtual void sub_update(const sg_range& in_data, const rect_t& in_rect) override; - virtual void resize(const Eigen::Vector2i& in_size) override; + void create(const texture_data& in_data) override; + void update(const sg_image_data& in_data) override; + void resize(const Eigen::Vector2i& in_size) override; + + void sub_update(const sg_range& in_data, const rect_t& in_rect, uint32_t src_row_pitch = 0); + std::vector read() const; }; diff --git a/src/mirage_render/texture/texture.h b/src/mirage_render/texture/texture.h index dbaa8cd..c3ba90c 100644 --- a/src/mirage_render/texture/texture.h +++ b/src/mirage_render/texture/texture.h @@ -21,9 +21,8 @@ public: virtual void create(const texture_data& in_data) = 0; virtual void update(const sg_image_data& in_data) = 0; - virtual void sub_update(const sg_range& in_data, const rect_t& in_rect) = 0; virtual void resize(const Eigen::Vector2i& in_size) = 0; - virtual std::unique_ptr map(texture_map_type in_type) const; + void clear(); diff --git a/src/mirage_render/texture/texture2d.cpp b/src/mirage_render/texture/texture2d.cpp index 9e22b7e..ff79935 100644 --- a/src/mirage_render/texture/texture2d.cpp +++ b/src/mirage_render/texture/texture2d.cpp @@ -25,10 +25,6 @@ void texture2d::update(const sg_image_data& in_data) { create_from_desc(desc); } -void texture2d::sub_update(const sg_range& in_data, const rect_t& in_rect) { - std::cerr << "texture2d can't sub_update!" << std::endl; -} - void texture2d::resize(const Eigen::Vector2i& in_size) { std::cerr << "texture2d can't resize!" << std::endl; } diff --git a/src/mirage_render/texture/texture2d.h b/src/mirage_render/texture/texture2d.h index 75d14cd..6dee793 100644 --- a/src/mirage_render/texture/texture2d.h +++ b/src/mirage_render/texture/texture2d.h @@ -6,7 +6,7 @@ struct texture2d_data : texture_data { void* data_ptr; virtual sg_image_data get_image_data() const override { const auto& pixel_info = sg_query_pixelformat(pixel_format); - sg_image_data data; + sg_image_data data{}; data.subimage[0][0].ptr = data_ptr; data.subimage[0][0].size = size.x() * size.y() * pixel_info.bytes_per_pixel; return data; @@ -23,6 +23,5 @@ class texture2d : public texture { public: virtual void create(const texture_data& in_data) override; virtual void update(const sg_image_data& in_data) override; - virtual void sub_update(const sg_range& in_data, const rect_t& in_rect) override; virtual void resize(const Eigen::Vector2i& in_size) override; }; diff --git a/src/mirage_render/texture/texture_map_types.h b/src/mirage_render/texture/texture_map_types.h index fd02f28..8ef91b6 100644 --- a/src/mirage_render/texture/texture_map_types.h +++ b/src/mirage_render/texture/texture_map_types.h @@ -14,11 +14,3 @@ struct texture_map { std::span data; // 数据 std::function unmap; // 取消映射函数,用于在销毁时调用 }; - -enum class texture_map_type { - read, - write, - read_write, - write_discard, - write_no_overwrite -}; diff --git a/src/mirage_render/texture/windows/rw_texture2d_d3d11.cpp b/src/mirage_render/texture/windows/rw_texture2d_d3d11.cpp new file mode 100644 index 0000000..3f62d98 --- /dev/null +++ b/src/mirage_render/texture/windows/rw_texture2d_d3d11.cpp @@ -0,0 +1,196 @@ +#include "texture/texture.h" +#include "sokol_gfx.h" +#include + +#include "render/windows/pixel_format_convert.h" +#include "texture/rw_texture2d.h" + +// 处理 D3D11 错误代码 +void handle_d3d_error(HRESULT hr, const char* operation) { + if (FAILED(hr)) { + std::string error_msg = std::string(operation) + ": "; + switch (hr) { + case E_INVALIDARG: + // 无效参数 + error_msg += "Invalid arguments"; + break; + case E_OUTOFMEMORY: + // 内存不足 + error_msg += "Out of memory"; + break; + case DXGI_ERROR_DEVICE_REMOVED: + // 设备移除 + error_msg += "Device removed"; + break; + case DXGI_ERROR_DEVICE_RESET: + // 设备重置 + error_msg += "Device reset"; + break; + default: + // 未知错误 + error_msg += "Unknown error (0x" + std::to_string(hr) + ")"; + break; + } + throw std::runtime_error(error_msg); + } +} + +void rw_texture2d::create(const texture_data& in_data) { + sg_image_desc desc{}; + desc.type = SG_IMAGETYPE_2D; + desc.render_target = false; + desc.width = in_data.size.x(); + desc.height = in_data.size.y(); + desc.num_slices = 1; + desc.num_mipmaps = 1; + desc.usage = _SG_USAGE_DEFAULT; + desc.pixel_format = in_data.pixel_format; + desc.sample_count = 1; + + auto d3d_device = (ID3D11Device*)sg_d3d11_device(); + auto d3d_context = (ID3D11DeviceContext*)sg_d3d11_device_context(); + + // 创建纹理 + ID3D11Texture2D* tex2d = nullptr; + D3D11_TEXTURE2D_DESC tex_desc{}; + tex_desc.Width = desc.width; + tex_desc.Height = desc.height; + tex_desc.MipLevels = 1; + tex_desc.ArraySize = 1; + tex_desc.Format = sg_pixel_format_to_dxgi(desc.pixel_format); + tex_desc.SampleDesc.Count = 1; + tex_desc.SampleDesc.Quality = 0; + tex_desc.Usage = D3D11_USAGE_DEFAULT; + tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + tex_desc.CPUAccessFlags = 0; + tex_desc.MiscFlags = 0; + + auto hr = d3d_device->CreateTexture2D(&tex_desc, nullptr, &tex2d); + handle_d3d_error(hr, "Failed to create texture"); + + // 创建视图 + ID3D11ShaderResourceView* srv = nullptr; + D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc{}; + srv_desc.Format = tex_desc.Format; + srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srv_desc.Texture2D.MostDetailedMip = 0; + srv_desc.Texture2D.MipLevels = 1; + + hr = d3d_device->CreateShaderResourceView(tex2d, &srv_desc, &srv); + handle_d3d_error(hr, "Failed to create shader resource view"); + + // 设置纹理 + desc.d3d11_texture = tex2d; + desc.d3d11_shader_resource_view = srv; + + create_from_desc(desc); + + const auto& image_data = in_data.get_image_data(); + if (image_data.subimage[0][0].ptr) + update(image_data); +} + +void rw_texture2d::sub_update(const sg_range& in_data, const rect_t& in_rect, uint32_t src_row_pitch) { + if (!is_valid()) { + // 无法更新无效纹理 + throw std::runtime_error("Cannot update invalid texture"); + } + + // 获取 D3D11 资源 + const auto& image_info = sg_d3d11_query_image_info(get_image()); + const auto& context = (ID3D11DeviceContext*)sg_d3d11_device_context(); + const auto& tex2d = (ID3D11Texture2D*)image_info.tex2d; + + if (!tex2d || !context) { + // 无法获取 D3D11 纹理或上下文 + throw std::runtime_error("Failed to get D3D11 texture or context"); + } + + // 获取纹理描述 + D3D11_TEXTURE2D_DESC desc; + tex2d->GetDesc(&desc); + + // 边界检查 + if (in_rect.left() < 0 || in_rect.top() < 0 || + in_rect.right() > (int32_t)desc.Width || + in_rect.bottom() > (int32_t)desc.Height) { + throw std::runtime_error("Update region out of texture bounds"); + } + + // 计算或使用提供的行间距 + uint32_t row_pitch = src_row_pitch; + if (row_pitch == 0) { + row_pitch = in_rect.width() * get_bytes_per_pixel(); + } + + // 检查数据大小是否足够 + if (in_data.size < (size_t)(in_rect.height() * row_pitch)) { + throw std::runtime_error("Insufficient data for update region"); + } + + // 使用UpdateSubresource更新纹理 + D3D11_BOX box; + box.left = in_rect.left(); + box.top = in_rect.top(); + box.front = 0; + box.right = in_rect.right(); + box.bottom = in_rect.bottom(); + box.back = 1; + + context->UpdateSubresource(tex2d, 0, &box, in_data.ptr, in_rect.width() * get_bytes_per_pixel(), 0); +} + +std::vector rw_texture2d::read() const { + // 获取纹理大小 + const auto& size = get_size(); + if (size.x() <= 0 || size.y() <= 0) { + // 无效的纹理大小 + throw std::runtime_error("Invalid texture size"); + } + + auto texture_info = sg_d3d11_query_image_info(get_image()); + auto tex2d = (ID3D11Texture2D*)texture_info.tex2d; + auto d3d_device = (ID3D11Device*)sg_d3d11_device(); + auto d3d_context = (ID3D11DeviceContext*)sg_d3d11_device_context(); + + // 获取纹理描述 + D3D11_TEXTURE2D_DESC desc{}; + tex2d->GetDesc(&desc); + + // 创建STAGING纹理 + D3D11_TEXTURE2D_DESC staging_desc = desc; + staging_desc.Usage = D3D11_USAGE_STAGING; + + ID3D11Texture2D* staging_tex = nullptr; + auto hr = d3d_device->CreateTexture2D(&staging_desc, nullptr, &staging_tex); + handle_d3d_error(hr, "Failed to create staging texture"); + + // 使用智能指针确保纹理始终被正确释放 + std::shared_ptr shared_staging_tex(staging_tex, [](ID3D11Texture2D* ptr) { ptr->Release(); }); + + // 复制纹理数据 + D3D11_BOX box{}; + box.left = 0; + box.top = 0; + box.front = 0; + box.right = size.x(); + box.bottom = size.y(); + box.back = 1; + + d3d_context->CopySubresourceRegion(staging_tex, 0, 0, 0, 0, tex2d, 0, &box); + + // 映射STAGING纹理 + D3D11_MAPPED_SUBRESOURCE mapped{}; + hr = d3d_context->Map(staging_tex, 0, D3D11_MAP_READ, 0, &mapped); + handle_d3d_error(hr, "Failed to map staging texture"); + + // 复制数据 + std::vector data(size.x() * size.y() * get_bytes_per_pixel()); + std::memcpy(data.data(), mapped.pData, data.size()); + + // 解除映射 + d3d_context->Unmap(staging_tex, 0); + + return data; +} + diff --git a/src/mirage_render/texture/windows/texture_d3d11.cpp b/src/mirage_render/texture/windows/texture_d3d11.cpp deleted file mode 100644 index 0a21497..0000000 --- a/src/mirage_render/texture/windows/texture_d3d11.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "texture/texture.h" -#include "sokol_gfx.h" -#include - -auto convert_map_type_to_d3d(texture_map_type in_type) { - switch (in_type) { - case texture_map_type::read: - return D3D11_MAP_READ; - case texture_map_type::write: - return D3D11_MAP_WRITE; - case texture_map_type::read_write: - return D3D11_MAP_READ_WRITE; - case texture_map_type::write_discard: - return D3D11_MAP_WRITE_DISCARD; - case texture_map_type::write_no_overwrite: - return D3D11_MAP_WRITE_NO_OVERWRITE; - default: - return D3D11_MAP_WRITE_DISCARD; - } -} - -// 验证映射类型与纹理描述是否兼容 -void validate_map_type(texture_map_type in_type, const D3D11_TEXTURE2D_DESC& desc) { - D3D11_MAP d3d_map_type = convert_map_type_to_d3d(in_type); - - // 检查读访问权限 - if ((d3d_map_type == D3D11_MAP_READ || d3d_map_type == D3D11_MAP_READ_WRITE) && !(desc.CPUAccessFlags & D3D11_CPU_ACCESS_READ)) { - // 纹理不支持 CPU 读取访问 - throw std::runtime_error("Texture does not support CPU read access"); - } - - // 检查写访问权限 - if ((d3d_map_type != D3D11_MAP_READ) && !(desc.CPUAccessFlags & D3D11_CPU_ACCESS_WRITE)) { - // 纹理不支持 CPU 写入访问 - throw std::runtime_error("Texture does not support CPU write access"); - } - - // 特殊检查:WRITE_DISCARD 仅适用于 DYNAMIC 用途的纹理 - if (d3d_map_type == D3D11_MAP_WRITE_DISCARD && desc.Usage != D3D11_USAGE_DYNAMIC) { - // WRITE_DISCARD 映射需要动态纹理 - throw std::runtime_error("WRITE_DISCARD mapping requires a dynamic texture"); - } -} - -// 处理 D3D11 错误代码 -void handle_d3d_error(HRESULT hr, const char* operation) { - if (FAILED(hr)) { - std::string error_msg = std::string(operation) + ": "; - switch (hr) { - case E_INVALIDARG: - // 无效参数 - error_msg += "Invalid arguments"; - break; - case E_OUTOFMEMORY: - // 内存不足 - error_msg += "Out of memory"; - break; - case DXGI_ERROR_DEVICE_REMOVED: - // 设备移除 - error_msg += "Device removed"; - break; - case DXGI_ERROR_DEVICE_RESET: - // 设备重置 - error_msg += "Device reset"; - break; - default: - // 未知错误 - error_msg += "Unknown error (0x" + std::to_string(hr) + ")"; - break; - } - throw std::runtime_error(error_msg); - } -} - -std::unique_ptr texture::map(texture_map_type in_type) const { - if (!is_valid()) { - // 无法映射无效纹理 - throw std::runtime_error("Cannot map invalid texture"); - } - - // 获取 D3D11 资源 - const auto& image_info = sg_d3d11_query_image_info(get_image()); - const auto& context = (ID3D11DeviceContext*) sg_d3d11_device_context(); - const auto& tex2d = (ID3D11Texture2D*) image_info.tex2d; - - if (!tex2d || !context) { - // 无法获取 D3D11 纹理或上下文 - throw std::runtime_error("Failed to get D3D11 texture or context"); - } - - // 获取纹理描述 - D3D11_TEXTURE2D_DESC desc; - tex2d->GetDesc(&desc); - - // 验证映射类型 - validate_map_type(in_type, desc); - - // 尝试映射纹理 - D3D11_MAPPED_SUBRESOURCE mapped; - const auto& d3d_map_type = convert_map_type_to_d3d(in_type); - auto hr = context->Map(tex2d, 0, d3d_map_type, 0, &mapped); - - // 处理映射错误 - handle_d3d_error(hr, "Failed to map texture"); - - // 使用智能指针确保纹理始终被正确解除映射 - std::shared_ptr shared_tex(tex2d, [](ID3D11Texture2D* ptr) {}); // 不增加引用计数 - std::shared_ptr shared_ctx(context, [](ID3D11DeviceContext* ptr) {}); // 不增加引用计数 - - // 设置映射结果 - auto map = std::make_unique(); - map->size = { desc.Width, desc.Height }; - map->data = { static_cast(mapped.pData), mapped.RowPitch * desc.Height }; - map->unmap = [shared_tex, shared_ctx] { - if (shared_tex && shared_ctx) { - shared_ctx->Unmap(shared_tex.get(), 0); - } - }; - map->row_pitch = mapped.RowPitch; - map->read_only = in_type == texture_map_type::read; - - return map; -} diff --git a/src/mirage_widget/widget/leaf_widget/mtext_block.cpp b/src/mirage_widget/widget/leaf_widget/mtext_block.cpp new file mode 100644 index 0000000..6a403c8 --- /dev/null +++ b/src/mirage_widget/widget/leaf_widget/mtext_block.cpp @@ -0,0 +1,26 @@ +// +// Created by 46944 on 25-3-28. +// + +#include "mtext_block.h" + +#include "font/font_system.h" + +void mtext_block::on_paint(mirage_paint_context& in_context) { + // 绘制文本 + in_context.drawer().make_text( + layout_, + {0, 0}, + in_context.geo().get_local_size(), + in_context.geo() + ); +} + +auto mtext_block::compute_desired_size(float in_layout_scale_multiplier) const -> Eigen::Vector2f { + return layout_.total_size; +} + +void mtext_block::update_layout() { + const auto& using_font = font_ ? font_ : font_manager::instance().get_primary_font(); + layout_ = font_manager::instance().layout_text(text_, using_font, font_size_, max_width_, line_spacing_); +} diff --git a/src/mirage_widget/widget/leaf_widget/mtext_block.h b/src/mirage_widget/widget/leaf_widget/mtext_block.h new file mode 100644 index 0000000..ce53bf2 --- /dev/null +++ b/src/mirage_widget/widget/leaf_widget/mtext_block.h @@ -0,0 +1,50 @@ +#pragma once +#include "font/font_type.h" +#include "widget/mleaf_widget.h" + +class mtext_block : public mleaf_widget { +public: + void on_paint(mirage_paint_context& in_context) override; + + void set_text(const std::string& in_text) { + text_ = in_text; + update_layout(); + } + + void set_font(const std::shared_ptr& in_font) { + font_ = in_font; + update_layout(); + } + + void set_font_size(float in_font_size) { + font_size_ = in_font_size; + update_layout(); + } + + void set_line_spacing(float in_line_spacing) { + line_spacing_ = in_line_spacing; + update_layout(); + } + + void set_max_width(float in_max_width) { + max_width_ = in_max_width; + update_layout(); + } + + const auto& get_text() const { return text_; } + const auto& get_font() const { return font_; } + auto get_font_size() const { return font_size_; } + auto get_line_spacing() const { return line_spacing_; } + auto get_max_width() const { return max_width_; } + + auto compute_desired_size(float in_layout_scale_multiplier) const -> Eigen::Vector2f override; +private: + void update_layout(); + + std::string text_; + text_layout_t layout_; + float font_size_ = 24.0f * 1.5f; + float line_spacing_ = 1.2f; + float max_width_ = 0.0f; + std::shared_ptr font_; +}; diff --git a/third_party/msdfgen b/third_party/msdfgen deleted file mode 160000 index 0388956..0000000 --- a/third_party/msdfgen +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 03889564a50452fa2e0b0a60973b5057001b391b