TODO: 布局代码

This commit is contained in:
Nanako 2024-12-26 18:51:07 +08:00
parent 0937bb8a60
commit 5abdc2fc1d
5 changed files with 183 additions and 118 deletions

View File

@ -9,14 +9,14 @@ bool dx_sdf_text_pipeline::init() {
const auto d3d_device = aorii::get_renderer<dx_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");

View File

@ -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;
}
}

View File

@ -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<uint8_t*>(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<measured_ch> 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<measured_ch> 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<float>(texture_size);
info.tex_v = current_y / static_cast<float>(texture_size);
info.advance = advance_width * current_font->scale;
info.tex_z = static_cast<float>(current_texture_index);
info.width = width;
info.height = height;
info.u_size = width / static_cast<float>(texture_size);
info.v_size = height / static_cast<float>(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);
}
}

View File

@ -1,42 +1,68 @@
#pragma once
#include "core/pixel_format/pixel.h"
#include <unordered_map>
#include "misc/mapped_file.h"
#include <unordered_map>
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<measured_ch> 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<font_data> 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<char32_t, character_info> character_map;
std::unordered_map<char32_t, ch_atlas_item> character_map;
// 常用字符集定义
static const std::u32string COMMON_ASCII; // ASCII字符

View File

@ -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;