From 5abdc2fc1d6706cea13d1166df0eb9b69b9d56e6 Mon Sep 17 00:00:00 2001 From: Nanako <469449812@qq.com> Date: Thu, 26 Dec 2024 18:51:07 +0800 Subject: [PATCH] =?UTF-8?q?TODO:=20=E5=B8=83=E5=B1=80=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dx/pipeline/dx_sdf_text_pipeline.cpp | 6 +- .../core/renderer/renderer_context.cpp | 33 +--- src/renderer/core/renderer/renderer_text.cpp | 175 ++++++++++++------ src/renderer/core/renderer/renderer_text.h | 83 +++++---- src/renderer/shader/aorii_sdf_text.slang | 4 +- 5 files changed, 183 insertions(+), 118 deletions(-) diff --git a/src/renderer/backend/dx/pipeline/dx_sdf_text_pipeline.cpp b/src/renderer/backend/dx/pipeline/dx_sdf_text_pipeline.cpp index 129c073..997a51d 100644 --- a/src/renderer/backend/dx/pipeline/dx_sdf_text_pipeline.cpp +++ b/src/renderer/backend/dx/pipeline/dx_sdf_text_pipeline.cpp @@ -9,14 +9,14 @@ bool dx_sdf_text_pipeline::init() { const auto d3d_device = aorii::get_renderer()->get_d3d_device(); D3D11_SAMPLER_DESC sampler_desc = {}; - sampler_desc.Filter = D3D11_FILTER_ANISOTROPIC; + sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER; sampler_desc.MinLOD = 0; - sampler_desc.MaxLOD = 1; - sampler_desc.MaxAnisotropy = 4; + sampler_desc.MaxLOD = D3D11_FLOAT32_MAX; + sampler_desc.MaxAnisotropy = 1; d3d_device->CreateSamplerState(&sampler_desc, &sampler_state); loader.load("aorii_sdf_text_vertex_main", "aorii_sdf_text_pixel_main"); diff --git a/src/renderer/core/renderer/renderer_context.cpp b/src/renderer/core/renderer/renderer_context.cpp index bf5cbf5..3ace2d8 100644 --- a/src/renderer/core/renderer/renderer_context.cpp +++ b/src/renderer/core/renderer/renderer_context.cpp @@ -8,7 +8,7 @@ aorii_text* text = nullptr; void renderer_context::init() { text = new aorii_text(); // D:\Projects\aorii\JetBrainsMono-Regular.ttf - text->initialize(LR"(HarmonyOS_Sans_SC_Regular.ttf)", 64); + text->initialize(LR"(C:\Windows\Fonts\simhei.ttf)"); text->add_font(LR"(C:\Windows\Fonts\seguiemj.ttf)"); // text->precache_common_characters(); } @@ -46,39 +46,24 @@ void renderer_context::draw_string(const Eigen::Vector2f& in_pos, const std::u32 float cursor_x = in_pos.x(); float cursor_y = in_pos.y(); - const float scale = in_height / text->get_font_pixel_size(); - wchar_t last_char = 0; - for (const auto c : in_str) { - if (c == U'\n') { - cursor_x = in_pos.x(); - cursor_y += text->get_ascent() * scale; - continue; - } - if (c == U' ') { - cursor_x += text->get_space_width() * scale; - continue; - } - cursor_x += last_char ? text->get_kerning(last_char, c) : 0; + const auto& measure = text->measure_text(in_str, in_height); - const character_info* info = text->get_or_create_character(c); + for (const auto& mch: measure) { + const auto info = mch.item; if (!info) continue; - - // 根据in_height缩放字符大小 - const float y_offset = (info->y_offset + text->get_ascent()) * scale; - const Eigen::Vector2f size { info->width * scale, info->height * scale }; - const Eigen::Vector2f pos { cursor_x + info->x_offset * scale, cursor_y + y_offset }; - cursor_x += info->advance * scale; + // 使用整形坐标, 避免出现模糊 + const Eigen::Vector2f pos { cursor_x + mch.x_offset, cursor_y + mch.y_offset }; + const Eigen::Vector2f size { info->get_width() * mch.size_scale, info->get_height() * mch.size_scale }; aorii_vertex_param param{}; param.param_a1 = info->tex_u; param.param_a2 = info->tex_v; param.param_a3 = info->tex_z; - param.param_b1 = info->width / (float)text->get_texture_size(); - param.param_b2 = info->height / (float)text->get_texture_size(); + param.param_b1 = info->u_size; + param.param_b2 = info->v_size; make_rect(pos, size, in_color, 0, param); - last_char = c; } } diff --git a/src/renderer/core/renderer/renderer_text.cpp b/src/renderer/core/renderer/renderer_text.cpp index bf5257f..1620b95 100644 --- a/src/renderer/core/renderer/renderer_text.cpp +++ b/src/renderer/core/renderer/renderer_text.cpp @@ -26,18 +26,16 @@ const std::u32string aorii_text::COMMON_NUMBERS = U"0123456789"; aorii_text::font_data::~font_data() { - delete info; + delete font; } -aorii_text::aorii_text() : texture_array(nullptr), font_pixel_size(0), - space_width(0), - ascent(0), descent(0), - line_gap(0), +aorii_text::aorii_text() : texture_array(nullptr), current_x(0), current_y(0), current_texture_index(0) { } -aorii_text::~aorii_text() {} +aorii_text::~aorii_text() { +} bool aorii_text::init_freetype() { return true; @@ -46,9 +44,7 @@ bool aorii_text::init_freetype() { void aorii_text::destroy_freetype() { } -bool aorii_text::initialize(const std::wstring& in_font_path, const float in_font_pixel_size) { - font_pixel_size = in_font_pixel_size; - +bool aorii_text::initialize(const std::wstring& in_font_path) { // 预计需要的纹理数量(8张纹理几乎可以容纳所有字符) constexpr uint32_t expected_textures = 8; texture_array = aorii::get_renderer_raw()->create_texture_array(2048, 2048, expected_textures, texture_format::R8_UNORM, device_memory_type::gpu); @@ -62,19 +58,6 @@ bool aorii_text::initialize(const std::wstring& in_font_path, const float in_fon return false; } - // 获取主字体的度量信息 - const auto& primary_font = fonts[0]; - stbtt_GetFontVMetrics(primary_font.info, &ascent, &descent, &line_gap); - stbtt_GetCodepointHMetrics(primary_font.info, ' ', &space_width, nullptr); - stbtt_GetCodepointHMetrics(primary_font.info, '\t', &tab_width, nullptr); - - ascent *= primary_font.scale; - descent *= primary_font.scale; - line_gap *= primary_font.scale; - space_width *= primary_font.scale; - tab_width *= primary_font.scale; - - spdlog::info("主字体度量信息:\n 上升距离: {}, 下降距离: {}, 行间距: {}", ascent, descent, line_gap); return true; } @@ -84,54 +67,64 @@ float aorii_text::get_kerning(char32_t ch1, char32_t ch2) const { // 在所有字体中查找字符对的字距调整 for (const auto& font : fonts) { - g1 = stbtt_FindGlyphIndex(font.info, ch1); - g2 = stbtt_FindGlyphIndex(font.info, ch2); + g1 = stbtt_FindGlyphIndex(font.font, ch1); + g2 = stbtt_FindGlyphIndex(font.font, ch2); if (g1 != 0) { left_ch_font = &font; } if (g1 != 0 && g2 != 0) { - return stbtt_GetGlyphKernAdvance(font.info, g1, g2) * font.scale; + return stbtt_GetGlyphKernAdvance(font.font, g1, g2); } } if (!left_ch_font) return 0.0f; int x0, x1; - stbtt_GetGlyphBox(left_ch_font->info, g1, &x0, nullptr, &x1, nullptr); - return (x1 - x0) * left_ch_font->scale; + stbtt_GetGlyphBox(left_ch_font->font, g1, &x0, nullptr, &x1, nullptr); + return x1 - x0; } bool aorii_text::add_font(const std::wstring& font_path) { font_data new_font{}; - new_font.info = new stbtt_fontinfo(); + new_font.font = new stbtt_fontinfo(); // 映射字体文件 if (!new_font.file.map_file(font_path)) { - delete new_font.info; + delete new_font.font; spdlog::error("Failed to map font file"); return false; } const auto* font_buffer = static_cast(new_font.file.get_data()); - if (!stbtt_InitFont(new_font.info, font_buffer, stbtt_GetFontOffsetForIndex(font_buffer, 0))) { + if (!stbtt_InitFont(new_font.font, font_buffer, stbtt_GetFontOffsetForIndex(font_buffer, 0))) { new_font.file.unmap(); - delete new_font.info; + delete new_font.font; spdlog::error("Failed to initialize font"); return false; } - new_font.scale = stbtt_ScaleForPixelHeight(new_font.info, font_pixel_size); + int temp_space_width, temp_tab_width; + stbtt_GetCodepointHMetrics(new_font.font, ' ', &temp_space_width, nullptr); + stbtt_GetCodepointHMetrics(new_font.font, '\t', &temp_tab_width, nullptr); + new_font.space_width = temp_space_width; + new_font.tab_width = temp_tab_width; + + int ascent, descent, line_gap; + stbtt_GetFontVMetrics(new_font.font, &ascent, &descent, &line_gap); + new_font.ascent = ascent; + new_font.descent = descent; + new_font.line_gap = line_gap; + fonts.emplace_back(std::move(new_font)); return true; } -character_info* aorii_text::get_or_create_character(const char32_t ch) { +ch_atlas_item const* aorii_text::get_atlas_item(const char32_t ch) { if (const auto it = character_map.find(ch); it != character_map.end()) { return &it->second; } - // Try to add to current atlas - return add_character_to_atlas(ch); + return cache_ch_to_atlas(ch); } bool aorii_text::precache_characters(const std::u32string& characters) { @@ -147,14 +140,8 @@ bool aorii_text::precache_characters(const std::u32string& characters) { for (size_t i = 0; i < characters.length(); i++) { auto ch = characters[i]; - - // 检查字符是否已经缓存 - if (character_map.contains(ch)) { - continue; - } - - // 生成SDF数据 - add_character_to_atlas(ch); + if (!get_atlas_item(ch)) + spdlog::error("Failed to create character U+{:04X}", (uint32_t)ch); } return success; @@ -178,7 +165,7 @@ bool aorii_text::precache_common_characters() { uint32_t aorii_text::get_font_char_count() const { uint32_t result = 0; for (const auto& font : fonts) { - result += font.info->numGlyphs; + result += font.font->numGlyphs; } return result; } @@ -187,11 +174,71 @@ uint32_t aorii_text::get_texture_size() const { return texture_array->size().x(); } -character_info* aorii_text::add_character_to_atlas(const char32_t ch) { +std::vector aorii_text::measure_text(const std::u32string& text, float height) { + // 获取主字体 + font_data const& primary_font = fonts[0]; + const float scale = stbtt_ScaleForPixelHeight(primary_font.font, height); + const float scale_padding = padding * (height / pixel_height); + + // 基线和行高信息 + const int32_t ascent = primary_font.ascent; + const int32_t descent = primary_font.descent; + const int32_t line_gap = primary_font.line_gap * scale; + const int32_t line_height = height; + const float space_width = primary_font.space_width * scale; + const float tab_width = primary_font.tab_width * scale; + + std::vector result; + float x = 0; + float y = 0; + + char32_t prev_char = 0; + for (const auto& ch : text) { + if (ch == U' ') { + x += space_width; + continue; + } + if (ch == U'\t') { + x += tab_width; + continue; + } + if (ch == U'\n') { + x = 0; + y += line_height; + y += line_gap; + y -= scale_padding * 2; + continue; + } + + const auto item = get_atlas_item(ch); + measured_ch mch{}; + mch.item = item; + mch.x_offset = x; + mch.y_offset = y; + mch.size_scale = scale; + if (item) { + // 如果字符高度大于行高, 则缩放 + const float item_height = item->get_height() * scale; + if (item_height > line_height) { + mch.size_scale = line_height / item_height * scale; + } + + float test = line_height - item->get_height() * mch.size_scale; + mch.y_offset += test; + x += item->get_width() * mch.size_scale; + prev_char = ch; + } + result.push_back(mch); + } + return result; +} + +ch_atlas_item const* aorii_text::cache_ch_to_atlas(char32_t ch) { font_data const* current_font = nullptr; int glyph_index = 0; for (const auto& font : fonts) { - if (glyph_index = stbtt_FindGlyphIndex(font.info, ch)) { + glyph_index = stbtt_FindGlyphIndex(font.font, ch); + if (glyph_index) { current_font = &font; break; } @@ -199,18 +246,18 @@ character_info* aorii_text::add_character_to_atlas(const char32_t ch) { if (!current_font) { return nullptr; } - const auto font = current_font->info; + const auto font = current_font->font; const uint32_t texture_size = get_texture_size(); + constexpr uint8_t on_edge_value = 128; + const float pixel_dist_scale = (float)on_edge_value / padding; + const float scale = stbtt_ScaleForPixelHeight(font, pixel_height - padding * 2); + // 获取SDF尺寸和位图 int32_t width, height, x_offset, y_offset; - constexpr int padding = 8; - constexpr uint8_t on_edge_value = 128; - constexpr float pixel_dist_scale = 16.f; - auto* sdf_bitmap = stbtt_GetGlyphSDF( font, - current_font->scale, + scale, glyph_index, padding, on_edge_value, @@ -255,21 +302,35 @@ character_info* aorii_text::add_character_to_atlas(const char32_t ch) { // 获取字形度量信息 int advance_width, left_side_bearing; stbtt_GetGlyphHMetrics(font, glyph_index, &advance_width, &left_side_bearing); + int ch_x0, ch_y0, ch_x1, ch_y1; + stbtt_GetGlyphBox(font, glyph_index, &ch_x0, &ch_y0, &ch_x1, &ch_y1); + + // 将x_offset和y_offset转换到字体空间 + x_offset = x_offset / scale; + y_offset = y_offset / scale; // 创建并存储字符信息 - character_info info; + ch_atlas_item info{}; info.tex_u = current_x / static_cast(texture_size); info.tex_v = current_y / static_cast(texture_size); - info.advance = advance_width * current_font->scale; info.tex_z = static_cast(current_texture_index); - info.width = width; - info.height = height; + info.u_size = width / static_cast(texture_size); + info.v_size = height / static_cast(texture_size); + info.x_offset = x_offset; info.y_offset = y_offset; + info.left = ch_x0; + info.right = ch_x1; + info.top = ch_y0; + info.bottom = ch_y1; + + info.x_advance = advance_width; + info.left_side_bearing = left_side_bearing; + // 更新位置和下一行的y坐标 current_x += width; next_row_y = std::max(next_row_y, current_y + height); return &(character_map[ch] = info); -} +} \ No newline at end of file diff --git a/src/renderer/core/renderer/renderer_text.h b/src/renderer/core/renderer/renderer_text.h index f8d3b89..9a59d13 100644 --- a/src/renderer/core/renderer/renderer_text.h +++ b/src/renderer/core/renderer/renderer_text.h @@ -1,42 +1,68 @@ #pragma once - - #include "core/pixel_format/pixel.h" - -#include - #include "misc/mapped_file.h" +#include class renderer_texture_array; struct stbtt_fontinfo; -struct character_info { +struct ch_atlas_item { float tex_u; // U position in atlas float tex_v; // V position in atlas - float advance; // Advance width uint8_t tex_z; // Z position in texture array - uint8_t width; // Character width - uint8_t height; // Character height - int8_t x_offset; // X offset from baseline - int8_t y_offset; // Y offset from baseline + float u_size; // Texture width + float v_size; // Texture height + + int32_t x_offset; // X offset from baseline + int32_t y_offset; // Y offset from baseline + + int32_t left; + int32_t right; + int32_t top; + int32_t bottom; + int32_t get_width() const { return right - left; } + int32_t get_height() const { return bottom - top; } + + int32_t x_advance; + int32_t left_side_bearing; +}; + +struct measured_ch { + ch_atlas_item const* item; + float x_offset; + float y_offset; + float size_scale; }; class aorii_text { struct font_data { font_data() = default; - font_data(font_data&& other) { + font_data(font_data&& other) noexcept { file = std::move(other.file); - info = other.info; - scale = other.scale; - other.info = nullptr; - other.scale = 0; + font = other.font; + space_width = other.space_width; + tab_width = other.tab_width; + ascent = other.ascent; + descent = other.descent; + line_gap = other.line_gap; + + other.font = nullptr; + other.space_width = 0; + other.tab_width = 0; + other.ascent = 0; + other.descent = 0; + other.line_gap = 0; } ~font_data(); - stbtt_fontinfo* info; + stbtt_fontinfo* font{}; mapped_file file; - float scale; + int32_t space_width{}; + int32_t tab_width{}; + int32_t ascent{}; // 字体从基线到顶部的高度 + int32_t descent{}; // 基线到字体底部的高度 + int32_t line_gap{}; // 行间距 }; public: aorii_text(); @@ -45,16 +71,9 @@ public: static bool init_freetype(); static void destroy_freetype(); - bool initialize(const std::wstring& in_font_path, float in_font_pixel_size); + bool initialize(const std::wstring& in_font_path); bool add_font(const std::wstring& font_path); - character_info* get_or_create_character(char32_t ch); - [[nodiscard]] int32_t get_space_width() const { return space_width; } - [[nodiscard]] int32_t get_ascent() const { return ascent; } - [[nodiscard]] int32_t get_descent() const { return descent; } - [[nodiscard]] int32_t get_line_gap() const { return line_gap; } - - // 字体像素大小 - [[nodiscard]] float get_font_pixel_size() const { return font_pixel_size; } + ch_atlas_item const* get_atlas_item(char32_t ch); // 预缓存一组字符 bool precache_characters(const std::u32string& characters); @@ -72,21 +91,21 @@ public: [[nodiscard]] uint32_t get_texture_size() const; [[nodiscard]] float get_kerning(char32_t ch1, char32_t ch2) const; + [[nodiscard]] std::vector measure_text(const std::u32string& text, float height); private: - character_info* add_character_to_atlas(char32_t ch); - + ch_atlas_item const* cache_ch_to_atlas(char32_t ch); std::vector fonts; + const float pixel_height = 64; + const int padding = 5; renderer_texture_array* texture_array; - float font_pixel_size; - int32_t space_width, tab_width, ascent, descent, line_gap; uint32_t current_x; uint32_t current_y; uint32_t next_row_y = 0; // 跟踪当前行的最大高度 uint8_t current_texture_index; - std::unordered_map character_map; + std::unordered_map character_map; // 常用字符集定义 static const std::u32string COMMON_ASCII; // ASCII字符 diff --git a/src/renderer/shader/aorii_sdf_text.slang b/src/renderer/shader/aorii_sdf_text.slang index 8eba14e..029e92a 100644 --- a/src/renderer/shader/aorii_sdf_text.slang +++ b/src/renderer/shader/aorii_sdf_text.slang @@ -43,8 +43,8 @@ float4 pixel_main(PSInput input) : SV_Target { float2 uv = input.altas_uv + input.char_size * input.uv; float distance = atlas_texture.Sample(sampler_state, float3(uv, input.altas_index)).r; // return float4(distance, distance, distance, 1.0); - float range = 0.07; - float alpha = smoothstep(0.5 - range, 0.5 + range, distance); + float range = 0.2; + float alpha = smoothstep(0.4, 0.5, distance); float4 color = input.color; color.a *= alpha;