Files
Sylva/fonts/ttf/ttf.cpp
T
2026-06-06 10:31:20 +08:00

121 lines
3.8 KiB
C++

#include <fonts/ttf.h>
#include "ttf_internal.h"
#include <graphics/draw.h>
#include <memory/heap.h>
#include <serial.h>
// 逐字形临时缓冲区 — 静态分配以避免大中文字形的栈压力
static ttf_outline_t s_outline;
static ttf_seg_t s_segs[4096];
static SUINT8 s_coverage[256 * 256];
// 在屏幕 (px, py) 处渲染单个字形位图,py 为字形顶部(已从基线转换)
static void blit_glyph(SSINT32 px, SSINT32 py, SUINT32 w, SUINT32 h,
const SUINT8* coverage, SUINT32 N,
EFI_GRAPHICS_OUTPUT_BLT_PIXEL color)
{
SUINT32 scale = 255 / N;
for (SUINT32 y = 0; y < h; y++) {
SSINT32 sy = py + (SSINT32)y;
if (sy < 0) continue;
for (SUINT32 x = 0; x < w; x++) {
SSINT32 sx = px + (SSINT32)x;
if (sx < 0) continue;
SUINT8 c = coverage[y * w + x];
if (c == 0) continue;
SUINT8 a = (SUINT8)(c * scale);
draw_pixel_alpha(sx, sy, color, a);
}
}
}
// 在基线 (x, y) 处渲染单个码点,返回进宽(26.6 定点数)
static f26_6 render_codepoint(ttf_face_t* face, SSINT32 cp,
SSINT32 x, SSINT32 y, SUINT32 pixel_size,
EFI_GRAPHICS_OUTPUT_BLT_PIXEL color)
{
SUINT16 gid = ttf_cmap_lookup(face, cp);
if (!ttf_load_glyph(face, gid, pixel_size, &s_outline)) {
return 0; // composite / parse error
}
// Bitmap dims in pixels
SSINT32 bbox_w = s_outline.xmax - s_outline.xmin;
SSINT32 bbox_h = s_outline.ymax - s_outline.ymin;
if (bbox_w <= 0 || bbox_h <= 0) {
return s_outline.advance; // whitespace or zero-size
}
SUINT32 pw = (SUINT32)((bbox_w + 63) >> 6);
SUINT32 ph = (SUINT32)((bbox_h + 63) >> 6);
if (pw > 256 || ph > 256) {
return s_outline.advance; // too big for scratch
}
// Translate outline to bitmap-local: bx = fx - xmin, by = ymax - fy
ttf_outline_t local;
local.num_points = s_outline.num_points;
local.num_contours = s_outline.num_contours;
for (SUINT16 i = 0; i < s_outline.num_contours; i++) {
local.first[i] = s_outline.first[i];
local.last[i] = s_outline.last[i];
}
for (SUINT16 i = 0; i < s_outline.num_points; i++) {
local.x[i] = s_outline.x[i] - s_outline.xmin;
local.y[i] = s_outline.ymax - s_outline.y[i];
local.on_curve[i] = s_outline.on_curve[i];
}
local.xmin = 0;
local.ymin = 0;
local.xmax = bbox_w;
local.ymax = bbox_h;
SUINT32 n_segs = 0;
ttf_outline_to_segments(&local, s_segs, &n_segs);
// Clear coverage
for (SUINT32 i = 0; i < pw * ph; i++) s_coverage[i] = 0;
const SUINT32 N = 5; // subsamples per pixel row
ttf_rasterize(s_segs, n_segs, 0, 0, pw, ph, s_coverage, N);
// Screen origin of bitmap
SSINT32 px_screen = x + (s_outline.xmin >> 6);
SSINT32 py_screen = y - (s_outline.ymax >> 6);
blit_glyph(px_screen, py_screen, pw, ph, s_coverage, N, color);
return s_outline.advance;
}
SSINT32 ttf_draw_text(ttf_face_t* face, const char* utf8,
SSINT32 x, SSINT32 y, SUINT32 pixel_size,
EFI_GRAPHICS_OUTPUT_BLT_PIXEL color)
{
if (!face || !utf8) return 0;
const char* p = utf8;
f26_6 pen = 0;
while (*p) {
SSINT32 cp = ttf_utf8_decode(&p);
if (cp < 0) continue;
f26_6 adv = render_codepoint(face, cp,
x + (pen >> 6), y, pixel_size, color);
pen += adv;
}
return pen;
}
SSINT32 ttf_text_width(ttf_face_t* face, const char* utf8, SUINT32 pixel_size) {
if (!face || !utf8) return 0;
const char* p = utf8;
f26_6 pen = 0;
while (*p) {
SSINT32 cp = ttf_utf8_decode(&p);
if (cp < 0) continue;
SUINT16 gid = ttf_cmap_lookup(face, cp);
if (!ttf_load_glyph(face, gid, pixel_size, &s_outline)) continue;
pen += s_outline.advance;
}
return pen;
}