121 lines
3.8 KiB
C++
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;
|
|
}
|