收口字体渲染路径以降低 atlas 体积并直接输出 RGB565
将位图字体从 RGBA8888 atlas 改为 1-bit mask,避免继续为字体存储无效颜色信息。 生成端现在按 row-major、MSB-first 将 8 个像素打包进 1 个 uint8_t;运行时由 draw_text 提供目标颜色,并在 DrawContext 中直接把 bit=1 的像素写入 RGB565 framebuffer。同步补充了回归测试与文档,确保颜色替换、背景填充和裁剪语义保持稳定。 Constraint: 本次只收口字体路径,不改 sprite/tilemap 资源格式 Constraint: 运行时保持纯二值绘制语义,不引入 alpha blending 或抗锯齿 Rejected: 继续兼容 RGBA8888 字体输入 | 会保留双路径复杂度且无实际收益 Confidence: high Scope-risk: moderate Reversibility: clean Directive: 后续字体资源必须继续使用 row-major + MSB-first 的 1-bit mask;修改位序前先同步生成脚本、运行时读取和测试 Tested: cmake --build build-win --config Release Tested: build-win/Release/render_pipeline_tests.exe Tested: ctest -C Release --output-on-failure Tested: font_atlas_mask 字节数校验(2808 == ceil(208*108/8)) Not-tested: Demo/设备上的人工目视文字显示效果
This commit is contained in:
@@ -133,11 +133,9 @@ int main(int argc, char* argv[])
|
||||
Platform::SteadyTimeSource time_source;
|
||||
|
||||
RenderData::BitmapFont font;
|
||||
font.atlas = RenderData::Image(
|
||||
font_atlas_pixels,
|
||||
font_atlas_width,
|
||||
font_atlas_height,
|
||||
RenderData::PixelFormat::RGBA8888);
|
||||
font.mask_bits = font_atlas_mask;
|
||||
font.atlas_width = font_atlas_width;
|
||||
font.atlas_height = font_atlas_height;
|
||||
font.char_w = font_char_w;
|
||||
font.char_h = font_char_h;
|
||||
font.columns = font_columns;
|
||||
|
||||
@@ -325,31 +325,68 @@ namespace Core
|
||||
}
|
||||
}
|
||||
|
||||
void DrawContext::draw_font_mask_region(const RenderData::BitmapFont& font,
|
||||
int32_t src_x, int32_t src_y,
|
||||
int32_t src_w, int32_t src_h,
|
||||
int32_t dst_x, int32_t dst_y,
|
||||
uint16_t pixel)
|
||||
{
|
||||
Core::FramePixel* dst = static_cast<Core::FramePixel*>(frameBuffer->get_buffer());
|
||||
const int32_t fb_w = frameBuffer->get_width();
|
||||
|
||||
for (int32_t row = 0; row < src_h; ++row)
|
||||
{
|
||||
const int32_t atlas_row = src_y + row;
|
||||
const int32_t dst_y_abs = dst_y + row;
|
||||
Core::FramePixel* dst_row = dst + dst_y_abs * fb_w + dst_x;
|
||||
|
||||
for (int32_t col = 0; col < src_w; ++col)
|
||||
{
|
||||
const int32_t atlas_x = src_x + col;
|
||||
const int32_t bit_index = atlas_row * font.atlas_width + atlas_x;
|
||||
const uint8_t packed = font.mask_bits[bit_index >> 3];
|
||||
const uint8_t bit_mask = static_cast<uint8_t>(0x80u >> (bit_index & 7));
|
||||
if ((packed & bit_mask) == 0u) continue;
|
||||
dst_row[col] = pixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawContext::draw_text(const RenderData::BitmapFont& font, int32_t x, int32_t y,
|
||||
const RenderData::Color& color, const char* text)
|
||||
{
|
||||
if (!font.atlas.pixels || !text) return;
|
||||
if (!font.mask_bits || !text) return;
|
||||
|
||||
const uint32_t rgba = color.to_rgba();
|
||||
const int32_t cw = font.char_w;
|
||||
const int32_t ch = font.char_h;
|
||||
const int32_t atlas_w = font.atlas.width;
|
||||
const uint32_t* src = static_cast<const uint32_t*>(font.atlas.pixels);
|
||||
const int32_t atlas_w = font.atlas_width;
|
||||
const int32_t atlas_h = font.atlas_height;
|
||||
const int32_t screen_w = frameBuffer->get_width();
|
||||
const int32_t screen_h = frameBuffer->get_height();
|
||||
if (cw <= 0 || ch <= 0 || font.columns <= 0 || atlas_w <= 0 || atlas_h <= 0) return;
|
||||
|
||||
const Core::FramePixel glyph_pixel = Core::rgba_to_frame_pixel(color.to_rgba());
|
||||
|
||||
int32_t cursor_x = x;
|
||||
for (const char* p = text; *p; ++p)
|
||||
{
|
||||
const int32_t index = static_cast<int32_t>(*p) - font.first_char;
|
||||
if (index < 0) { cursor_x += cw; continue; }
|
||||
if (index < 0)
|
||||
{
|
||||
cursor_x += cw;
|
||||
continue;
|
||||
}
|
||||
|
||||
const int32_t col = index % font.columns;
|
||||
const int32_t row = index / font.columns;
|
||||
const int32_t src_x = col * cw;
|
||||
const int32_t src_y = row * ch;
|
||||
if (src_x < 0 || src_y < 0 || src_x + cw > atlas_w || src_y + ch > atlas_h)
|
||||
{
|
||||
cursor_x += cw;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 整字符在屏幕外则跳过
|
||||
if (cursor_x + cw <= 0 || cursor_x >= screen_w || y + ch <= 0 || y >= screen_h)
|
||||
{
|
||||
cursor_x += cw;
|
||||
@@ -362,18 +399,14 @@ namespace Core
|
||||
if (y + ch > screen_h) sy_end = screen_h - y;
|
||||
if (cursor_x < 0) sx_start = -cursor_x;
|
||||
if (cursor_x + cw > screen_w) sx_end = screen_w - cursor_x;
|
||||
|
||||
for (int32_t sy = sy_start; sy < sy_end; ++sy)
|
||||
{
|
||||
const uint32_t* atlas_row = src + (src_y + sy) * atlas_w;
|
||||
const int32_t dst_y_abs = y + sy;
|
||||
for (int32_t sx = sx_start; sx < sx_end; ++sx)
|
||||
{
|
||||
const uint32_t pixel = atlas_row[src_x + sx];
|
||||
if ((pixel & 0xFF) == 0) continue;
|
||||
frameBuffer->set_pixel_unsafe(cursor_x + sx, dst_y_abs, rgba);
|
||||
}
|
||||
}
|
||||
draw_font_mask_region(font,
|
||||
src_x + sx_start,
|
||||
src_y + sy_start,
|
||||
sx_end - sx_start,
|
||||
sy_end - sy_start,
|
||||
cursor_x + sx_start,
|
||||
y + sy_start,
|
||||
glyph_pixel);
|
||||
cursor_x += cw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,12 @@ namespace Core
|
||||
Rasterizer::Rasterizer* rasterizer;
|
||||
Rasterizer::TriangleRasterizer* triangleRasterizer;
|
||||
|
||||
void draw_font_mask_region(const RenderData::BitmapFont& font,
|
||||
int32_t src_x, int32_t src_y,
|
||||
int32_t src_w, int32_t src_h,
|
||||
int32_t dst_x, int32_t dst_y,
|
||||
uint16_t pixel);
|
||||
|
||||
public:
|
||||
DrawContext(int32_t width, int32_t height);
|
||||
~DrawContext();
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include "Image.h"
|
||||
|
||||
namespace RenderData
|
||||
{
|
||||
struct BitmapFont
|
||||
{
|
||||
Image atlas;
|
||||
const uint8_t* mask_bits;
|
||||
int32_t atlas_width;
|
||||
int32_t atlas_height;
|
||||
int32_t char_w;
|
||||
int32_t char_h;
|
||||
int32_t columns;
|
||||
int32_t first_char;
|
||||
|
||||
BitmapFont()
|
||||
: mask_bits(nullptr),
|
||||
atlas_width(0),
|
||||
atlas_height(0),
|
||||
char_w(0),
|
||||
char_h(0),
|
||||
columns(0),
|
||||
first_char(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user