Files

207 lines
6.8 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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);
}