ohhhhhhhhhhh (exclude linux-extra-rootfs.img build artifact)
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
/* zhfont.c — 中文位图字库加载与渲染实现
|
||||
*
|
||||
* .dbf 格式(见 ../font/dbf.h):
|
||||
* header 14 字节 + count*12 字节索引(按 Unicode 码点升序)+ 字形数据。
|
||||
* 索引项 data_offset 相对文件头。
|
||||
*
|
||||
* 渲染:8bpp 灰度字形逐像素 du_blend(与 ASCII 渲染同路径,无字体缓存,
|
||||
* 每屏 <500 字在性能预算内,见桌面重构计划第七节)。
|
||||
*/
|
||||
/* 顺序敏感:ascii_font.h 声明 g_ascii(deshab_ui.h 的 du_draw_char 引用) */
|
||||
#include "ascii_font.h"
|
||||
#include "zhfont.h"
|
||||
#include "../font/dbf.h"
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
typedef long long i64;
|
||||
|
||||
/* ---- 字库状态 ---- */
|
||||
static zhfont_read_fn g_zh_read = 0;
|
||||
static const u8 *g_zh16 = 0;
|
||||
static const u8 *g_zh24 = 0;
|
||||
|
||||
/* ---- dbf 访问 ---- */
|
||||
|
||||
static const u8 *zh_dbf_get(const u8 *font, u32 cp, int *out_w, int *out_h) {
|
||||
const dbf_header *hdr = (const dbf_header *)font;
|
||||
const dbf_index_entry *idx = (const dbf_index_entry *)(font + 14);
|
||||
if (cp > 0xFFFF) return 0;
|
||||
int i = dbf_lookup((dbf_index_entry *)idx, hdr->count, (u16)cp);
|
||||
if (i < 0) return 0;
|
||||
*out_w = idx[i].width;
|
||||
*out_h = hdr->height;
|
||||
return font + idx[i].offset;
|
||||
}
|
||||
|
||||
/* 缺字占位:1px 空框 */
|
||||
static int zh_draw_missing(du_context *ctx, i64 x, i64 y, int w, int h, u32 fg) {
|
||||
du_rect_outline(ctx, x, y, w, h, fg, 1);
|
||||
return w;
|
||||
}
|
||||
|
||||
/* ---- 公开接口 ---- */
|
||||
|
||||
/* 头部静态校验(magic / bpp / 尺寸字段),不依赖文件大小 */
|
||||
static int zh_dbf_valid(const u8 *dst) {
|
||||
const dbf_header *hdr = (const dbf_header *)dst;
|
||||
if (!dst) return -1;
|
||||
if (hdr->magic[0] != 'D' || hdr->magic[1] != 'B' ||
|
||||
hdr->magic[2] != 'F' || hdr->magic[3] != 0x10) return -2;
|
||||
if (hdr->bpp != 8) return -3;
|
||||
if (hdr->count == 0 || hdr->height == 0 || hdr->width == 0) return -4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zh_load_dbf(const char *path, u8 *dst, u32 cap) {
|
||||
u32 sz = 0;
|
||||
if (!g_zh_read) return -1;
|
||||
if (g_zh_read(path, dst, cap, &sz) != 0) return -2;
|
||||
if (sz < 14) return -3;
|
||||
int rc = zh_dbf_valid(dst);
|
||||
if (rc != 0) return rc - 10;
|
||||
/* 越界防护:索引区与最大数据项必须在文件内 */
|
||||
{
|
||||
const dbf_header *hdr = (const dbf_header *)dst;
|
||||
const dbf_index_entry *last =
|
||||
(const dbf_index_entry *)(dst + 14 + (u64)(hdr->count - 1) * 12);
|
||||
if (14 + (u64)hdr->count * 12 > sz) return -7;
|
||||
if ((u64)last->offset + last->size > sz) return -8;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int zhfont_init(zhfont_read_fn read_to) {
|
||||
int rc;
|
||||
g_zh_read = read_to;
|
||||
rc = zh_load_dbf("system/font/simhei_16.dbf",
|
||||
(u8 *)ZHFONT16_ADDR, ZHFONT16_MAX);
|
||||
if (rc != 0) {
|
||||
g_zh16 = 0;
|
||||
return rc; /* -1=无读取器 -2=FAT32读失败 -3=过小 -7/-8=越界 -11..-13=头校验 */
|
||||
}
|
||||
g_zh16 = (const u8 *)ZHFONT16_ADDR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int zhfont_load24(void) {
|
||||
if (g_zh24) return 0;
|
||||
if (zh_load_dbf("system/font/simhei_24.dbf",
|
||||
(u8 *)ZHFONT24_ADDR, ZHFONT24_MAX) != 0) {
|
||||
return -1;
|
||||
}
|
||||
g_zh24 = (const u8 *)ZHFONT24_ADDR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int zhfont_ok(void) { return g_zh16 != 0; }
|
||||
int zhfont_ok24(void) { return g_zh24 != 0; }
|
||||
|
||||
void zhfont_attach16(const void *dbf_data) {
|
||||
/* 校验 magic 后挂接(数据由调用方保证生命周期) */
|
||||
if (dbf_data && zh_dbf_valid((const u8 *)dbf_data) == 0) g_zh16 = (const u8 *)dbf_data;
|
||||
else g_zh16 = 0;
|
||||
}
|
||||
|
||||
void zhfont_attach24(const void *dbf_data) {
|
||||
if (dbf_data && zh_dbf_valid((const u8 *)dbf_data) == 0) g_zh24 = (const u8 *)dbf_data;
|
||||
else g_zh24 = 0;
|
||||
}
|
||||
|
||||
unsigned int zh_utf8_decode(const char **s) {
|
||||
const u8 *p = (const u8 *)*s;
|
||||
u8 c = *p++;
|
||||
unsigned int cp;
|
||||
int extra;
|
||||
if (c < 0x80) { *s = (const char *)p; return c; }
|
||||
else if ((c & 0xE0) == 0xC0) { cp = c & 0x1F; extra = 1; }
|
||||
else if ((c & 0xF0) == 0xE0) { cp = c & 0x0F; extra = 2; }
|
||||
else if ((c & 0xF8) == 0xF0) { cp = c & 0x07; extra = 3; }
|
||||
else { (*s)++; return 0xFFFFFFFFu; } /* 无效首字节 */
|
||||
for (int i = 0; i < extra; i++) {
|
||||
u8 cc = *p;
|
||||
if ((cc & 0xC0) != 0x80) { (*s)++; return 0xFFFFFFFFu; }
|
||||
cp = (cp << 6) | (cc & 0x3F);
|
||||
p++;
|
||||
}
|
||||
*s = (const char *)p;
|
||||
return cp;
|
||||
}
|
||||
|
||||
int zh_draw_char(du_context *ctx, unsigned int codepoint,
|
||||
i64 x, i64 y, unsigned int fg) {
|
||||
if (!g_zh16) {
|
||||
/* 字库未加载:ASCII 走 Consolas,其余画空框 */
|
||||
if (codepoint >= 0x20 && codepoint < 0x7F) {
|
||||
du_draw_char(ctx, (char)codepoint, x, y, fg, 0);
|
||||
return (int)DU_ASCII_STEP;
|
||||
}
|
||||
return 16;
|
||||
}
|
||||
if (codepoint < 0x20) return 4; /* 控制字符:窄空隙 */
|
||||
int w, h;
|
||||
const u8 *g = zh_dbf_get(g_zh16, codepoint, &w, &h);
|
||||
if (!g) return zh_draw_missing(ctx, x, y, 16, 16, fg);
|
||||
for (int r = 0; r < h; r++) {
|
||||
for (int c = 0; c < w; c++) {
|
||||
u8 a = g[r * w + c];
|
||||
if (!a) continue;
|
||||
du_u32 bg = du_pixel_read(ctx, x + c, y + r);
|
||||
du_pixel(ctx, x + c, y + r,
|
||||
(a == 255) ? fg : du_blend(bg, fg, a));
|
||||
}
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
int zh_string_width(const char *utf8, int size24) {
|
||||
int total = 0;
|
||||
const u8 *font = size24 ? g_zh24 : g_zh16;
|
||||
if (!utf8) return 0;
|
||||
while (*utf8) {
|
||||
unsigned int cp = zh_utf8_decode(&utf8);
|
||||
if (cp == 0xFFFFFFFFu) { total += 4; continue; }
|
||||
if (cp < 0x20) { total += 4; continue; }
|
||||
int w, h;
|
||||
const u8 *g = font ? zh_dbf_get(font, cp, &w, &h) : 0;
|
||||
if (!g) w = size24 ? 24 : 16;
|
||||
total += w + 1; /* 1px 字间距 */
|
||||
}
|
||||
return total > 0 ? total - 1 : 0;
|
||||
}
|
||||
|
||||
int zh_line_height(int size24) { return size24 ? 26 : 18; }
|
||||
|
||||
int zh_draw_string(du_context *ctx, const char *utf8,
|
||||
i64 x, i64 y, unsigned int fg, int size24) {
|
||||
const u8 *font = size24 ? g_zh24 : g_zh16;
|
||||
i64 cx = x;
|
||||
if (!utf8) return 0;
|
||||
while (*utf8) {
|
||||
unsigned int cp = zh_utf8_decode(&utf8);
|
||||
if (cp == 0xFFFFFFFFu) { cx += 4; continue; }
|
||||
if (cp < 0x20) { cx += 4; continue; }
|
||||
if (cp == 0x20) { cx += (size24 ? 18 : 12); continue; }
|
||||
int w, h;
|
||||
const u8 *g = font ? zh_dbf_get(font, cp, &w, &h) : 0;
|
||||
if (!g) {
|
||||
cx += zh_draw_missing(ctx, cx, y, size24 ? 24 : 16,
|
||||
size24 ? 24 : 16, fg) + 1;
|
||||
continue;
|
||||
}
|
||||
for (int r = 0; r < h; r++) {
|
||||
for (int c = 0; c < w; c++) {
|
||||
u8 a = g[r * w + c];
|
||||
if (!a) continue;
|
||||
du_u32 bg = du_pixel_read(ctx, cx + c, y + r);
|
||||
du_pixel(ctx, cx + c, y + r,
|
||||
(a == 255) ? fg : du_blend(bg, fg, a));
|
||||
}
|
||||
}
|
||||
cx += w + 1;
|
||||
}
|
||||
return (int)(cx - x);
|
||||
}
|
||||
Reference in New Issue
Block a user