线框着色器和线段着色器

This commit is contained in:
Nanako 2025-03-30 23:28:36 +08:00
parent 56917da0d1
commit 673b0a1478
12 changed files with 268 additions and 56 deletions

View File

@ -12,10 +12,11 @@ int main(int argc, char* argv[]) {
auto& manager = font_manager::instance();
manager.add_font(L"C:/Users/46944/AppData/Local/Microsoft/Windows/Fonts/MapleMono-NF-CN-Regular.ttf");
manager.add_font(L"C:/Windows/Fonts/msyh.ttc");
// manager.add_font(L"D:/Projects/noto-emoji-2.047/fonts/Noto-COLRv1.ttf");
manager.add_font(L"C:/Windows/Fonts/seguiemj.ttf");
const auto& text_block = std::make_shared<mtext_block>();
text_block->set_text(U"Hello, World! 你好,世界!😀");
text_block->set_text(U"Hello, World! 你好,世界!🥶😅😎");
const auto& text_block2 = std::make_shared<mtext_block>();
text_block2->set_text(U"Hello, World!");

View File

@ -163,6 +163,18 @@ public:
*/
const auto& size() const { return size_; }
/**
* @brief
* @return
*/
auto& position() { return position_; }
/**
* @brief
* @return
*/
auto& size() { return size_; }
/**
* @brief
* @return

View File

@ -58,6 +58,7 @@ public:
int width, height, xoff, yoff;
auto bitmap = stbtt_GetGlyphBitmap(&in_font.get_font_info(), scale, scale, in_glyph_index,
&width, &height, &xoff, &yoff);
// stbtt_GetGlyphSDF();
if (!bitmap || width == 0 || height == 0) {
result.reason = glyph_atlas_reason_t::glyph_not_found;

View File

@ -46,6 +46,12 @@ float font_face_t::get_scale_for_pixel_height(float font_size) const {
return stbtt_ScaleForPixelHeight(&font_info_, font_size);
}
float font_face_t::get_line_height() const {
// 获取字体的行高
const auto v_metrics = get_v_metrics();
return static_cast<float>(v_metrics.ascent - v_metrics.descent + v_metrics.line_gap);
}
std::optional<glyph_t> font_face_t::get_glyph(int32_t code_point, float font_size) const {
// 获取字形索引
const auto glyph_index = find_glyph_index(code_point);

View File

@ -62,6 +62,12 @@ public:
*/
float get_scale_for_pixel_height(float font_size) const;
/**
* @brief
* @return
*/
[[nodiscard]] float get_line_height() const;
//--------------------------------------------------------------------------
// 字形信息查询
//--------------------------------------------------------------------------

View File

@ -60,14 +60,29 @@ std::optional<color_emoji_bitmap_t> colr_renderer_t::render_emoji(
const uint16_t first_layer_index = font::read_u16(target_record + 2);
const uint16_t num_layers_for_glyph = font::read_u16(target_record + 4);
// 解析CPAL表
// 检查图层数量
if (num_layers_for_glyph == 0 || first_layer_index + num_layers_for_glyph > num_layers) {
return std::nullopt; // 无效的图层配置
}
// 解析CPAL表 - 为Windows字体特别处理
const uint16_t cpal_version = font::read_u16(cpal_data);
const uint16_t num_palette_entries = font::read_u16(cpal_data + 2);
const uint16_t num_palettes = font::read_u16(cpal_data + 4);
const uint32_t color_records_offset = font::read_u32(cpal_data + 8);
// Windows字体可能有类型选择器字段
uint16_t color_record_indices_offset = 12;
if (cpal_version > 0 && cpal_data + 12 < font_data + font_offset + color_records_offset) {
color_record_indices_offset = 14; // 调整CPAL格式偏移
}
// 使用第一个调色板
const uint8_t* palette_indices = cpal_data + 12;
const uint16_t first_color_index = font::read_u16(palette_indices);
const uint8_t* palette_indices = cpal_data + color_record_indices_offset;
uint16_t first_color_index = 0;
if (num_palettes > 0) {
first_color_index = font::read_u16(palette_indices);
}
// 计算缩放比例
const float scale = stbtt_ScaleForPixelHeight(&font_info, font_size);
@ -81,36 +96,50 @@ std::optional<color_emoji_bitmap_t> colr_renderer_t::render_emoji(
bitmap.width = x1 - x0;
bitmap.height = y1 - y0;
// 确保位图尺寸有效
// 确保位图尺寸有效 - Windows字体可能需要额外的填充
if (bitmap.width <= 0 || bitmap.height <= 0) {
bitmap.width = static_cast<int>(font_size);
bitmap.height = static_cast<int>(font_size);
bitmap.width = static_cast<int>(font_size * 1.2); // 添加额外空间
bitmap.height = static_cast<int>(font_size * 1.2);
x0 = 0;
y0 = 0;
}
// 初始化RGBA位图数据
bitmap.data.resize(bitmap.width * bitmap.height * 4, 0);
// 渲染每一个图层
// 渲染每一个图层 - Windows Segoe UI Emoji特别处理
const uint8_t* layer_records = colr_data + layer_records_offset;
const uint8_t* color_records = cpal_data + color_records_offset;
// 处理Microsoft特有的图层顺序
for (uint16_t i = 0; i < num_layers_for_glyph; i++) {
const uint8_t* layer = layer_records + (first_layer_index + i) * 4;
const uint16_t layer_glyph_id = font::read_u16(layer);
const uint16_t palette_entry_index = font::read_u16(layer + 2);
// 获取颜色
// 确保层索引有效
if (palette_entry_index >= num_palette_entries) {
continue;
}
// 获取颜色 - Windows字体可能有不同的颜色格式
const uint16_t color_index = first_color_index + palette_entry_index;
if (color_index >= num_palette_entries) {
continue; // 超出调色板范围
continue;
}
const uint8_t* color = color_records + color_index * 4;
// Windows字体通常使用BGRA顺序
const uint8_t blue = color[0];
const uint8_t green = color[1];
const uint8_t red = color[2];
const uint8_t alpha = color[3];
// Windows字体可能会对特定颜色设置特殊意义
// 如果alpha为0微软通常将其视为"不透明黑色"
const uint8_t effective_alpha = (alpha == 0 && (red > 0 || green > 0 || blue > 0))
? 255 : alpha;
// 为图层创建临时位图
int layer_x0, layer_y0, layer_x1, layer_y1;
stbtt_GetGlyphBitmapBox(&font_info, layer_glyph_id, scale, scale,
@ -120,11 +149,13 @@ std::optional<color_emoji_bitmap_t> colr_renderer_t::render_emoji(
const int layer_height = layer_y1 - layer_y0;
if (layer_width <= 0 || layer_height <= 0) {
continue; // 跳过无效尺寸的图层
continue;
}
// 渲染图层字形的灰度位图
std::vector<uint8_t> layer_bitmap(layer_width * layer_height, 0);
// 使用更高精度的渲染方法 - 关键是对细节(如眼睛)的处理
stbtt_MakeGlyphBitmap(&font_info, layer_bitmap.data(),
layer_width, layer_height, layer_width,
scale, scale, layer_glyph_id);
@ -133,7 +164,7 @@ std::optional<color_emoji_bitmap_t> colr_renderer_t::render_emoji(
const int offset_x = layer_x0 - x0;
const int offset_y = layer_y0 - y0;
// 将图层与颜色混合到最终位图
// 将图层与颜色混合到最终位图 - Windows样式混合
for (int y = 0; y < layer_height; y++) {
const int dst_y = offset_y + y;
if (dst_y < 0 || dst_y >= bitmap.height) {
@ -148,23 +179,45 @@ std::optional<color_emoji_bitmap_t> colr_renderer_t::render_emoji(
const uint8_t coverage = layer_bitmap[y * layer_width + x];
if (coverage == 0) {
continue; // 跳过透明像素
continue;
}
const int dst_idx = (dst_y * bitmap.width + dst_x) * 4;
// Alpha混合
const float src_alpha = (coverage / 255.0f) * (alpha / 255.0f);
const float dst_alpha = bitmap.data[dst_idx + 3] / 255.0f;
const float out_alpha = src_alpha + dst_alpha * (1.0f - src_alpha);
// Windows风格的Alpha混合 - 这对眼睛等小细节很重要
const float src_alpha = (coverage / 255.0f) * (effective_alpha / 255.0f);
if (out_alpha > 0) {
bitmap.data[dst_idx + 0] = static_cast<uint8_t>((red * src_alpha + bitmap.data[dst_idx + 0] * dst_alpha * (1.0f - src_alpha)) / out_alpha);
bitmap.data[dst_idx + 1] = static_cast<uint8_t>((green * src_alpha + bitmap.data[dst_idx + 1] * dst_alpha * (1.0f - src_alpha)) / out_alpha);
bitmap.data[dst_idx + 2] = static_cast<uint8_t>((blue * src_alpha + bitmap.data[dst_idx + 2] * dst_alpha * (1.0f - src_alpha)) / out_alpha);
// 对于细节图层(如眼睛),增强其影响
float enhanced_alpha = src_alpha;
if (palette_entry_index > 0 && src_alpha > 0) { // 非底色图层且有透明度
enhanced_alpha = std::min(src_alpha * 1.2f, 1.0f); // 稍微增强
}
bitmap.data[dst_idx + 3] = static_cast<uint8_t>(out_alpha * 255.0f);
const float dst_alpha = bitmap.data[dst_idx + 3] / 255.0f;
const float out_alpha = enhanced_alpha + dst_alpha * (1.0f - enhanced_alpha);
if (out_alpha > 0) {
// 特别处理黑色或暗色像素,这通常是眼睛等细节
if (red <= 64 && green <= 64 && blue <= 64 && enhanced_alpha > 0.2f) {
// 增强黑色细节的可见度
bitmap.data[dst_idx + 0] = red;
bitmap.data[dst_idx + 1] = green;
bitmap.data[dst_idx + 2] = blue;
bitmap.data[dst_idx + 3] = static_cast<uint8_t>(enhanced_alpha * 255.0f);
} else {
// 标准混合
const float src_factor = enhanced_alpha / out_alpha;
const float dst_factor = dst_alpha * (1.0f - enhanced_alpha) / out_alpha;
bitmap.data[dst_idx + 0] = static_cast<uint8_t>(
std::min(255.0f, red * src_factor + bitmap.data[dst_idx + 0] * dst_factor));
bitmap.data[dst_idx + 1] = static_cast<uint8_t>(
std::min(255.0f, green * src_factor + bitmap.data[dst_idx + 1] * dst_factor));
bitmap.data[dst_idx + 2] = static_cast<uint8_t>(
std::min(255.0f, blue * src_factor + bitmap.data[dst_idx + 2] * dst_factor));
bitmap.data[dst_idx + 3] = static_cast<uint8_t>(out_alpha * 255.0f);
}
}
}
}
}

View File

@ -81,13 +81,13 @@ text_layout_t font_manager::layout_text(
// 初始化布局变量
float cursor_x = 0.0f;
float cursor_y = 0.0f;
const float line_height = font_size * line_spacing;
const float line_height = primary_font->get_line_height() * scale + line_spacing;
float line_max_width = 0.0f;
const auto& v_metrics = primary_font->get_v_metrics();
float font_ascent = v_metrics.ascent * scale;
float line_max_descent = std::abs(v_metrics.descent * scale);
float font_descent = std::abs(v_metrics.descent * scale);
int32_t last_index = 0; // 上一个字形索引,用于字距调整
float line_start_x = 0.0f; // 当前行的起始x坐标
@ -112,10 +112,12 @@ text_layout_t font_manager::layout_text(
// 重置为下一行的基线计算
font_ascent = v_metrics.ascent * scale;
line_max_descent = std::abs(v_metrics.descent * scale);
font_descent = std::abs(v_metrics.descent * scale);
baseline = cursor_y + font_ascent;
};
layout.ascent = font_ascent;
// 处理每个字符
for (const auto& c : text) {
// 检查是否是换行符
@ -162,7 +164,8 @@ text_layout_t font_manager::layout_text(
// 计算字形坐标
float x = cursor_x + glyph_metrics->lsb + glyph_metrics->rect.left();
float y = cursor_y + font_ascent + glyph_metrics->rect.top();
// float y = cursor_y + font_ascent + glyph_metrics->rect.top() + font_descent;
float y = baseline + glyph_metrics->rect.top();
// 添加字形位置信息到布局
auto& glyph_position = layout.glyphs.emplace_back();

View File

@ -142,8 +142,10 @@ struct text_layout_t {
atlas_region_t region; // 纹理图集区域
};
std::vector<glyph_position_t> glyphs; // 所有字形位置
Eigen::Vector2f total_size; // 文本总尺寸
std::vector<glyph_position_t> glyphs; // 所有字形位置
Eigen::Vector2f total_size; // 文本总尺寸
float ascent; // 上升高度
float baseline = 0.0f; // 基线位置
};
/**

View File

@ -4,6 +4,8 @@
#include "shaders/mirage_rounded_rect.hlsl.h"
#include "shaders/mirage_image.hlsl.h"
#include "shaders/mirage_text.hlsl.h"
#include "shaders/mirage_wireframe.hlsl.h"
#include "shaders/mirage_line.hlsl.h"
template<typename Derived>
void compute_rect_vertices(const Eigen::MatrixBase<Derived>& in_pos,
@ -187,6 +189,7 @@ void render_elements::make_rect(
float in_rotation_radians,
const Eigen::Vector2f& in_pivot,
const Eigen::Vector2f& in_scale) {
// 从几何体转换到窗口坐标
const auto& pos = in_geometry.local_to_window(in_pos);
add_rect_to_batch(pos,
in_size,
@ -263,12 +266,87 @@ void render_elements::make_text(const text_layout_t& in_layout, const Eigen::Vec
new_key.image = texture->get_image();
new_key.sampler = *sampler;
const Eigen::Vector2f& real_pos = p.position.array() + in_pos.array();
Eigen::Vector2f real_pos = p.position.array() + in_pos.array();
// snap to pixel grid
real_pos.x() = std::round(real_pos.x());
real_pos.y() = std::round(real_pos.y());
ensure_batch_compatibility(new_key);
make_rect(real_pos, size, in_color, in_geometry, {}, {}, {}, uv, in_rotation_radians, in_pivot, in_scale);
// make_wireframe(real_pos, size, in_geometry, 1.0f, in_color, in_rotation_radians, in_pivot, in_scale);
}
}
Eigen::Vector2f font_ascent_start = in_pos;
font_ascent_start.y() += in_layout.ascent;
Eigen::Vector2f font_ascent_end = font_ascent_start;
font_ascent_end.x() += in_layout.total_size.x();
// 绘制文本的基线
make_line(font_ascent_start, font_ascent_end, in_geometry, 1.0f, in_color, in_rotation_radians, in_pivot, in_scale);
// 绘制文本的线框
make_wireframe(in_pos, in_size, in_geometry, 1.0f, in_color, in_rotation_radians, in_pivot, in_scale);
}
void render_elements::make_wireframe(const Eigen::Vector2f& in_pos, const Eigen::Vector2f& in_size,
const geometry_t& in_geometry, float in_line_width, const rect_color& in_color, float in_rotation_radians,
const Eigen::Vector2f& in_pivot, const Eigen::Vector2f& in_scale) {
set_pipeline(wireframe_pipeline_);
mirage_vertex_param_t param_a{};
param_a.x = in_line_width / in_size.x();
param_a.y = in_line_width / in_size.y();
make_rect(in_pos,
in_size,
{ in_color },
in_geometry,
param_a,
{},
{},
{},
in_rotation_radians,
in_pivot,
in_scale);
}
void render_elements::make_line(const Eigen::Vector2f& in_start, const Eigen::Vector2f& in_end,
const geometry_t& in_geometry, float in_thickness, const rect_color& in_color, float in_rotation_radians,
const Eigen::Vector2f& in_pivot, const Eigen::Vector2f& in_scale) {
set_pipeline(line_pipeline_);
auto rect = rect_t<>::bounding_box<float>({in_start, in_end});
if (rect.size().x() == 0 || rect.size().y() == 0) {
// 根据线段方向添加厚度
Eigen::Vector2f direction = (in_end - in_start).normalized();
Eigen::Vector2f normal = Eigen::Vector2f(-direction.y(), direction.x());
rect.position() -= normal * in_thickness * 0.5f;
rect.size() += normal * in_thickness;
}
mirage_vertex_param_t param_a{};
mirage_vertex_param_t param_b{};
param_a.x = in_thickness;
// 需要将线段的起始和结束点转换为窗口坐标, 因为着色器中使用的是窗口坐标
const auto& start_window = in_geometry.local_to_window(in_start);
const auto& end_window = in_geometry.local_to_window(in_end);
param_b.x = start_window.x();
param_b.y = start_window.y();
param_b.z = end_window.x();
param_b.w = end_window.y();
make_rect(
rect.position(),
rect.size(),
{ in_color },
in_geometry,
param_a,
param_b,
{},
{},
in_rotation_radians,
in_pivot,
in_scale
);
}
// 确保缓冲区容量
@ -383,4 +461,12 @@ void render_elements::load_mirage_pipelines() {
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);
const auto wireframe_shader = sg_make_shader(get_mirage_wireframe_shader_desc());
const auto wireframe_pipeline_desc = get_mirage_wireframe_pipeline_desc(wireframe_shader, format, 1);
wireframe_pipeline_ = sg_make_pipeline(wireframe_pipeline_desc);
const auto line_shader = sg_make_shader(get_mirage_line_shader_desc());
const auto line_pipeline_desc = get_mirage_line_pipeline_desc(line_shader, format, 1);
line_pipeline_ = sg_make_pipeline(line_pipeline_desc);
}

View File

@ -270,6 +270,24 @@ public:
const Eigen::Vector2f& in_pivot = Eigen::Vector2f(0.5f, 0.5f),
const Eigen::Vector2f& in_scale = Eigen::Vector2f(1.f, 1.f));
void make_wireframe(const Eigen::Vector2f& in_pos,
const Eigen::Vector2f& in_size,
const geometry_t& in_geometry,
float in_line_width,
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));
void make_line(const Eigen::Vector2f& in_start,
const Eigen::Vector2f& in_end,
const geometry_t& in_geometry,
float in_thickness,
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));
//-------------- 统计信息 --------------
/**
@ -391,4 +409,10 @@ private:
/** 文本渲染管线 */
sg_pipeline text_pipeline_{};
/** 线框渲染管线 */
sg_pipeline wireframe_pipeline_{};
/** 线条渲染管线 */
sg_pipeline line_pipeline_{};
};

View File

@ -0,0 +1,45 @@
#include "mirage_util.slang"
struct ParamBuffer
{
matrix transform;
};
ParameterBlock<ParamBuffer> param_buffer : register(b1);
struct PSInput {
float4 position : SV_POSITION; // 裁剪空间坐标
float4 color : COLOR; // 颜色
float2 lineWidth : TEXCOORD0; // 线段宽度
float2 uv : TEXCOORD1; // 纹理坐标
};
[shader("vertex")]
PSInput vertex_main(VSInput input)
{
PSInput output;
output.position = mul(float4(input.position, 0, 1.0), param_buffer.transform);
output.color = input.color;
output.lineWidth = input.param_a.xy;
output.uv = input.uv;
return output;
}
[shader("pixel")]
float4 pixel_main(PSInput input) : SV_TARGET
{
// 计算线段宽度和矩形大小
float2 lineWidth = input.lineWidth;
float2 uv = input.uv;
bool left = uv.x > lineWidth.x;
bool right = uv.x < 1 - lineWidth.x;
bool top = uv.y > lineWidth.y;
bool bottom = uv.y < 1 - lineWidth.y;
// 如果像素在矩形内部区域(同时在所有边界内),则丢弃它
if (left && right && top && bottom)
{
discard;
}
return input.color;
}

View File

@ -1,27 +0,0 @@
#include "mirage_util.slang"
struct ParamBuffer
{
matrix transform;
};
ParameterBlock<ParamBuffer> param_buffer : register(b1);
struct PSInput {
float4 position : SV_POSITION; // 裁剪空间坐标
float4 color : COLOR; // 颜色
};
[shader("vertex")]
PSInput vertex_main(VSInput input)
{
PSInput output;
output.position = mul(float4(input.position, 0, 1.0), param_buffer.transform);
output.color = input.color;
return output;
}
[shader("pixel")]
float4 pixel_main(PSInput input) : SV_TARGET
{
return input.color;
}