TODO: 布局代码
This commit is contained in:
parent
0937bb8a60
commit
5abdc2fc1d
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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字符
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user