Files
MeowOS-For-R3P2/font/meow_font.c
T
MeowOS Dev 245bee2946 MeowOS for HiBy R3 Pro II — 公共发布版本(历史已重置)
• 全新单提交:清除含敏感信息的旧历史(sudo 口令、内网 IP、SSH 密钥路径、热点 SSID、设备 MAC)
• 本地开发文档/测试记录/构建脚本全部移入 gitignored 的 devdocs/ 与 scripts/build/(不入库)
• 保留:MeowOS 源码(launcher/watchdog/bootmenu/meow_svcd/SDK)、项目规范文档、mips-repo
增补组件:SDK 统一日志+通知 API、meow_svcd 服务管理器、mips-repo 软件仓库
2026-08-30 01:12:34 +08:00

533 lines
20 KiB
C
Raw 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.
/*
* meow_font.c — MeowOS 预渲染位图字体运行时(独立库,不绑定启动器)。
*
* 依赖:
* - libz(musl 静态)仅用于 inflate PNG 像素面。目标系统无 libpng,故模块内自带
* 最小 PNG 解码(灰度 non-interlaced,bit depth 4|8,filter 0-4)。
* - zlib 头文件不在 musl sysroot,故在文件底部声明 ABI 兼容的最小 API(z_stream 布局
* 与 libz.a 一致,已用 qemu-mipsel 验证)。
*
* 文件格式参见 gen/gen_font.py 注释(version=2,magic 'MFNI')。
*
* 绘制模型:drawtext(fb,x,y,utf8,color,size),(x,y)=行首升线顶部;baseline=y+ascent;
* 每字形 blit 于 (x+pen+dx, y+dy);pen 累加 advance。color 为 RGB565(R=bit15..11)。
*/
#include "meow_font.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* ---------------- 最小 zlib ABI 声明(与 libz.a 匹配,mipsel 32-bit unsigned long=4B) ------- */
typedef unsigned char mzf_Bytef;
typedef unsigned int mzf_uInt;
typedef unsigned long mzf_uLong;
typedef void *mzf_voidpf;
typedef unsigned int (*mzf_alloc_func)(unsigned int, unsigned int);
typedef void (*mzf_free_func)(unsigned int, unsigned int);
typedef struct mzf_stream_s {
mzf_Bytef *next_in;
mzf_uInt avail_in;
mzf_uLong total_in;
mzf_Bytef *next_out;
mzf_uInt avail_out;
mzf_uLong total_out;
char *msg;
void *state;
mzf_alloc_func zalloc;
mzf_free_func zfree;
mzf_voidpf opaque;
int data_type;
mzf_uLong adler;
mzf_uLong reserved;
} mzf_stream;
#define MZF_OK 0
#define MZF_STREAM_END 1
#define MZF_NO_FLUSH 0
#define MZF_FINISH 4
/* 函数符号名必须与 libz.a 一致(inflate 等);结构名仅 C 侧,ABI 由布局决定。 */
extern int inflateInit_(mzf_stream *, const char *, int);
extern int inflate(mzf_stream *, int);
extern int inflateEnd(mzf_stream *);
extern const char *zlibVersion(void);
/* ---------------- 内部数据结构 ---------------- */
typedef struct {
uint32_t cp;
uint16_t x, y, w, h, advance;
int8_t dx;
uint8_t dy;
} meow_glyph;
typedef struct {
int px; /* 原生字号 */
unsigned plane_x, plane_y; /* 在大画布(plane_w x plane_h)内的面左上角 */
unsigned plane_w, plane_h;
int ascent, descent; /* descent>=0 */
unsigned glyph_count;
meow_glyph *glyphs; /* 按 cp 升序 */
} meow_face;
typedef struct {
unsigned fb_w, fb_h, fb_pitch;
size_t plane_rowbytes;
unsigned depth; /* 4 或 8 */
unsigned plane_w, plane_h; /* 画布尺寸 */
unsigned char *plane; /* 打包像素面 */
int face_count;
meow_face *faces;
} meow_state;
static meow_state g;
/* PNG 内部为大端;idx(自定格式)为小端。 */
static uint32_t be32(const unsigned char *p) {
return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) |
((uint32_t)p[2] << 8) | (uint32_t)p[3];
}
static uint32_t le32(const unsigned char *p) {
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
}
/* ---------------- 内存读文件 ---------------- */
static unsigned char *read_file(const char *path, size_t *out_len) {
FILE *f = fopen(path, "rb");
if (!f) return NULL;
fseek(f, 0, SEEK_END);
long n = ftell(f);
fseek(f, 0, SEEK_SET);
if (n < 0) { fclose(f); return NULL; }
unsigned char *buf = (unsigned char *)malloc((size_t)n ? (size_t)n : 1);
if (!buf) { fclose(f); return NULL; }
size_t rd = fread(buf, 1, (size_t)n, f);
fclose(f);
if (rd != (size_t)n) { free(buf); return NULL; }
*out_len = (size_t)n;
return buf;
}
/* ---------------- zlib inflate ---------------- */
static int inflate_mem(const unsigned char *src, size_t srclen,
unsigned char *dst, size_t dstlen, size_t *out) {
mzf_stream s;
memset(&s, 0, sizeof(s));
s.next_in = (mzf_Bytef *)src;
s.avail_in = (mzf_uInt)srclen;
s.next_out = (mzf_Bytef *)dst;
s.avail_out = (mzf_uInt)dstlen;
s.zalloc = 0; s.zfree = 0; s.opaque = 0;
if (inflateInit_(&s, zlibVersion(), (int)sizeof(s)) != MZF_OK)
return -1;
int ret = inflate(&s, MZF_FINISH);
*out = (size_t)(dstlen - s.avail_out);
inflateEnd(&s);
return (ret == MZF_STREAM_END) ? 0 : -1;
}
/* ---------------- 最小 PNG 解码(灰度 non-interlaced) ---------------- */
/* 返回打包像素面(调用方释放),并输出 width/height/depth/plane_rowbytes。失败返回 NULL。 */
static unsigned char *png_decode_gray(const unsigned char *png, size_t len,
unsigned *out_w, unsigned *out_h,
unsigned *out_depth, size_t *out_rowbytes) {
if (len < 8 || memcmp(png, "\x89PNG\r\n\x1a\n", 8) != 0)
return NULL;
size_t i = 8;
unsigned w = 0, h = 0, depth = 0, color = 0, interlace = 1, compression = 1, filt = 1;
int have_ihdr = 0;
size_t idat_len = 0;
unsigned char *idat = NULL;
while (i + 8 <= len) {
uint32_t clen = be32(png + i);
const unsigned char *type = png + i + 4;
if (i + 8 + (size_t)clen > len) break;
if (memcmp(type, "IHDR", 4) == 0) {
if (clen < 13) { free(idat); return NULL; }
w = be32(png + i + 8);
h = be32(png + i + 12);
depth = png[i + 16];
color = png[i + 17];
compression = png[i + 18];
filt = png[i + 19];
interlace = png[i + 20];
have_ihdr = 1;
} else if (memcmp(type, "IDAT", 4) == 0) {
unsigned char *na = (unsigned char *)realloc(idat, idat_len + clen);
if (!na) { free(idat); return NULL; }
idat = na;
memcpy(idat + idat_len, png + i + 8, clen);
idat_len += clen;
} else if (memcmp(type, "IEND", 4) == 0) {
break;
}
i += 12 + (size_t)clen;
}
if (!have_ihdr || !idat ||
color != 0 || compression != 0 || filt != 0 || interlace != 0 ||
(depth != 4 && depth != 8) || w == 0 || h == 0) {
free(idat);
return NULL;
}
size_t rowbytes = ((size_t)w * depth + 7) / 8;
size_t scanlen = rowbytes + 1; /* 每扫描行 = 1 filter 字节 + rowbytes 像素 */
size_t rawlen = scanlen * h;
unsigned char *raw = (unsigned char *)malloc(rawlen);
if (!raw) { free(idat); return NULL; }
size_t outlen = 0;
if (inflate_mem(idat, idat_len, raw, rawlen, &outlen) != 0 || outlen != rawlen) {
free(raw); free(idat); return NULL;
}
free(idat);
/* 反滤波(bpp=1:灰度 depth 4|8) → out 为每行 rowbytes 的打包像素 */
size_t outlen_px = rowbytes * h;
unsigned char *out = (unsigned char *)malloc(outlen_px);
if (!out) { free(raw); return NULL; }
const unsigned bpp = 1;
for (size_t y = 0; y < h; y++) {
unsigned char ft = raw[y * scanlen];
const unsigned char *c = raw + y * scanlen + 1;
unsigned char *dst = out + y * rowbytes;
unsigned char *prev = (y == 0) ? NULL : out + (y - 1) * rowbytes;
for (size_t x = 0; x < rowbytes; x++) {
unsigned char left = (x >= bpp) ? dst[x - bpp] : 0;
unsigned char up = prev ? prev[x] : 0;
unsigned char ul = (prev && x >= bpp) ? prev[x - bpp] : 0;
unsigned char v = c[x];
switch (ft) {
case 0: dst[x] = v; break;
case 1: dst[x] = v + left; break;
case 2: dst[x] = v + up; break;
case 3: dst[x] = v + (((unsigned)left + (unsigned)up) >> 1); break;
case 4: {
int p = (int)left + (int)up - (int)ul;
int pa = p - (int)left; if (pa < 0) pa = -pa;
int pb = p - (int)up; if (pb < 0) pb = -pb;
int pc = p - (int)ul; if (pc < 0) pc = -pc;
unsigned char pred = (pa <= pb && pa <= pc) ? left : (pb <= pc ? up : ul);
dst[x] = v + pred;
break;
}
default: free(raw); free(out); return NULL;
}
}
}
free(raw);
*out_w = w; *out_h = h; *out_depth = depth; *out_rowbytes = rowbytes;
return out;
}
/* ---------------- idx 解析 ---------------- */
static int load_idx(const unsigned char *buf, size_t len) {
if (len < 16 || memcmp(buf, "MFNI", 4) != 0) return -1;
unsigned depth = buf[6];
int nfaces = buf[7];
unsigned png_w = le32(buf + 8), png_h = le32(buf + 12);
(void)png_w; (void)png_h;
if (nfaces < 1 || nfaces > 32) return -1;
if (depth != g.depth) return -2; /* 位深须与 PNG 一致 */
size_t face_off = 16;
meow_face *faces = (meow_face *)calloc((size_t)nfaces, sizeof(meow_face));
if (!faces) return -3;
for (int i = 0; i < nfaces; i++) {
const unsigned char *fh = buf + face_off + (size_t)i * 20;
meow_face *f = &faces[i];
f->px = fh[0] | (fh[1] << 8);
f->plane_x = fh[2] | (fh[3] << 8);
f->plane_y = fh[4] | (fh[5] << 8);
f->plane_w = fh[6] | (fh[7] << 8);
f->plane_h = fh[8] | (fh[9] << 8);
int16_t asc = (int16_t)(fh[10] | (fh[11] << 8));
int16_t dsc = (int16_t)(fh[12] | (fh[13] << 8));
f->ascent = asc;
f->descent = dsc;
f->glyph_count = fh[14] | (fh[15] << 8);
uint32_t goff = le32(fh + 16);
if (f->glyph_count > 0 && goff + (size_t)f->glyph_count * 16 <= len) {
f->glyphs = (meow_glyph *)malloc((size_t)f->glyph_count * sizeof(meow_glyph));
if (!f->glyphs) { for (int j = 0; j < i; j++) free(faces[j].glyphs); free(faces); return -4; }
for (unsigned k = 0; k < f->glyph_count; k++) {
const unsigned char *gchr = buf + goff + (size_t)k * 16;
meow_glyph *gr = &f->glyphs[k];
gr->cp = le32(gchr);
gr->x = gchr[4] | (gchr[5] << 8);
gr->y = gchr[6] | (gchr[7] << 8);
gr->w = gchr[8] | (gchr[9] << 8);
gr->h = gchr[10] | (gchr[11] << 8);
gr->advance = gchr[12] | (gchr[13] << 8);
gr->dx = (int8_t)gchr[14];
gr->dy = gchr[15];
}
} else if (f->glyph_count == 0) {
f->glyphs = NULL;
} else {
for (int j = 0; j < i; j++) free(faces[j].glyphs);
free(faces);
return -5;
}
}
g.faces = faces;
g.face_count = nfaces;
return 0;
}
/* ---------------- 面/字形查找 ---------------- */
static meow_face *face_for_size(int size) {
if (g.face_count <= 0) return NULL;
meow_face *best = NULL;
int best_delta = -1;
for (int i = 0; i < g.face_count; i++) {
int d = g.faces[i].px - size;
if (d < 0) d = -d;
if (best_delta < 0 || d < best_delta) { best_delta = d; best = &g.faces[i]; }
}
return best;
}
static meow_glyph *find_glyph(meow_face *f, uint32_t cp) {
unsigned lo = 0, hi = f->glyph_count;
while (lo < hi) {
unsigned mid = (lo + hi) >> 1;
uint32_t c = f->glyphs[mid].cp;
if (c == cp) return &f->glyphs[mid];
if (c < cp) lo = mid + 1; else hi = mid;
}
return NULL;
}
/* ---------------- 像素采样(depth 感知) ---------------- */
static inline unsigned sample_px(unsigned x, unsigned y) {
if (x >= g.plane_w || y >= g.plane_h) return 0;
if (g.depth == 8) return g.plane[(size_t)y * g.plane_w + x];
unsigned char b = g.plane[((size_t)y * g.plane_w + x) >> 1];
unsigned nh = (x & 1) ? (b & 0x0F) : (b >> 4);
return nh * 17;
}
/* ---------------- RGB565 alpha 混合 ---------------- */
static inline uint16_t blend565(uint16_t bg, uint16_t fg, unsigned a) {
unsigned inv = 255 - a;
unsigned br = (bg >> 11) & 31, bgc = (bg >> 5) & 63, bb = bg & 31;
unsigned fr = (fg >> 11) & 31, fgc = (fg >> 5) & 63, fb = fg & 31;
unsigned r = ((fr * a + br * inv) + 127) / 255;
unsigned gc = ((fgc * a + bgc * inv) + 127) / 255;
unsigned b = ((fb * a + bb * inv) + 127) / 255;
return (uint16_t)((r << 11) | (gc << 5) | b);
}
/* ---------------- 字形 blit(含缩放、裁剪、alpha 混合) ---------------- */
static void blit_glyph(uint16_t *fb, meow_face *face, const meow_glyph *gr,
int pen_x, int y, uint16_t color, unsigned scaleq) {
int stride = (int)(g.fb_pitch / 2); /* uint16 元素每行数 */
/* 目标尺寸 = 源尺寸 * scaleq / 4096 */
unsigned gw, gh;
int dx, dy;
if (scaleq == 4096) {
gw = gr->w; gh = gr->h;
dx = gr->dx; dy = gr->dy;
} else {
gw = (unsigned)(((uint64_t)gr->w * scaleq) >> 12);
gh = (unsigned)(((uint64_t)gr->h * scaleq) >> 12);
dx = (int)(((int64_t)gr->dx * scaleq) >> 12);
dy = (int)(((int64_t)gr->dy * scaleq) >> 12);
}
if (gw == 0 || gh == 0) return;
int px = pen_x + dx;
int py = y + dy;
unsigned sw = gr->w, sh = gr->h;
for (unsigned oy = 0; oy < gh; oy++) {
int ay = py + (int)oy;
if (ay < 0) continue;
if (ay >= (int)g.fb_h) break;
int sy = (int)((uint64_t)oy * sh / gh);
int base = face->plane_x + gr->x;
for (unsigned ox = 0; ox < gw; ox++) {
int ax = px + (int)ox;
if (ax < 0) continue;
if (ax >= (int)g.fb_w) break;
int sx = (int)((uint64_t)ox * sw / gw);
unsigned a = sample_px((unsigned)(base + sx),
(unsigned)(face->plane_y + gr->y + sy));
if (!a) continue;
size_t idx = (size_t)ay * (size_t)stride + (size_t)ax;
fb[idx] = blend565(fb[idx], color, a);
}
}
}
/* ---------------- UTF-8 解码 ---------------- */
static int utf8_next(const unsigned char **pp) {
const unsigned char *s = *pp;
unsigned char c = s[0];
if (c < 0x80) { *pp += 1; return c; }
int n;
uint32_t cp;
if ((c & 0xE0) == 0xC0) { n = 2; cp = c & 0x1F; }
else if ((c & 0xF0) == 0xE0) { n = 3; cp = c & 0x0F; }
else if ((c & 0xF8) == 0xF0) { n = 4; cp = c & 0x07; }
else { *pp += 1; return -1; }
for (int i = 1; i < n; i++) {
if ((s[i] & 0xC0) != 0x80) { *pp += 1; return -1; }
cp = (cp << 6) | (s[i] & 0x3F);
}
*pp += n;
return (int)cp;
}
static uint32_t advance_for_missing(meow_face *f) {
/* 缺字:用空格或 人 位 advance;找不到就用 fontSize/2 */
meow_glyph *sp = find_glyph(f, 0x20);
return sp ? sp->advance : (uint32_t)(f->px / 2);
}
static void draw_tofu(uint16_t *fb, int x, int y, int w, int h, uint16_t color) {
int stride = (int)(g.fb_pitch / 2);
for (int oy = 0; oy < h; oy++) {
int ay = y + oy;
if (ay < 0 || ay >= (int)g.fb_h) break;
for (int ox = 0; ox < w; ox++) {
int ax = x + ox;
if (ax < 0 || ax >= (int)g.fb_w) break;
if (oy == 0 || oy == h - 1 || ox == 0 || ox == w - 1) {
size_t idx = (size_t)ay * (size_t)stride + (size_t)ax;
fb[idx] = blend565(fb[idx], color, 255);
}
}
}
}
/* ---------------- 公共 API ---------------- */
void meow_font_set_fb(unsigned fb_w, unsigned fb_h, unsigned pitch_bytes) {
g.fb_w = fb_w; g.fb_h = fb_h; g.fb_pitch = pitch_bytes;
}
void meow_font_unload(void) {
if (g.faces) {
for (int i = 0; i < g.face_count; i++) free(g.faces[i].glyphs);
free(g.faces);
}
free(g.plane);
g.faces = NULL; g.plane = NULL; g.face_count = 0;
g.plane_w = g.plane_h = 0;
}
int meow_font_load(const char *png_path, const char *idx_path) {
meow_font_unload();
size_t plen = 0, ilen = 0;
unsigned char *png = read_file(png_path, &plen);
if (!png) return -1;
unsigned char *idx = read_file(idx_path, &ilen);
if (!idx) { free(png); return -1; }
unsigned w, h, depth;
size_t rowbytes;
unsigned char *plane = png_decode_gray(png, plen, &w, &h, &depth, &rowbytes);
free(png);
if (!plane) { free(idx); return -2; }
g.plane = plane;
g.depth = depth;
g.plane_w = w;
g.plane_h = h;
g.plane_rowbytes = rowbytes;
if (load_idx(idx, ilen) != 0) {
free(idx);
meow_font_unload();
return -3;
}
free(idx);
return 0;
}
int meow_font_has_size(int size) {
for (int i = 0; i < g.face_count; i++)
if (g.faces[i].px == size) return 1;
return 0;
}
int meow_font_ascent(int size) {
meow_face *f = face_for_size(size);
return f ? f->ascent : 0;
}
int meow_font_line_height(int size) {
meow_face *f = face_for_size(size);
return f ? (f->ascent + f->descent) : 0;
}
int meow_font_width_text(const char *utf8, int size) {
meow_face *f = face_for_size(size);
if (!f) return 0;
unsigned scaleq = (size == f->px) ? 4096u : (unsigned)(((uint64_t)size << 12) / (uint64_t)f->px);
const unsigned char *p = (const unsigned char *)utf8;
int total = 0;
while (*p) {
int cp = utf8_next(&p);
if (cp < 0) continue;
meow_glyph *gr = find_glyph(f, (uint32_t)cp);
unsigned adv = gr ? gr->advance : advance_for_missing(f);
if (scaleq == 4096) total += (int)adv;
else total += (int)(((uint64_t)adv * scaleq) >> 12);
}
return total;
}
void meow_font_drawtext(uint16_t *fb, int x, int y, const char *utf8,
uint16_t color, int size) {
meow_face *f = face_for_size(size);
if (!f || !g.plane) return;
unsigned scaleq = (size == f->px) ? 4096u : (unsigned)(((uint64_t)size << 12) / (uint64_t)f->px);
const unsigned char *p = (const unsigned char *)utf8;
int pen = 0;
while (*p) {
int cp = utf8_next(&p);
if (cp < 0) continue;
meow_glyph *gr = find_glyph(f, (uint32_t)cp);
if (gr && gr->w > 0 && gr->h > 0) {
blit_glyph(fb, f, gr, x + pen, y, color, scaleq);
} else if (!gr) {
/* 缺字:画空心方块 */
unsigned adv = advance_for_missing(f);
int tw, th;
if (scaleq == 4096) { tw = f->px / 2; th = f->ascent; }
else { tw = (int)(((uint64_t)(f->px / 2) * scaleq) >> 12); th = (int)(((uint64_t)f->ascent * scaleq) >> 12); }
draw_tofu(fb, x + pen + 1, y + 1, tw - 2, th - 2, color);
pen += scaleq == 4096 ? (int)adv : (int)(((uint64_t)adv * scaleq) >> 12);
continue;
}
int adv = gr ? gr->advance : (int)advance_for_missing(f);
pen += scaleq == 4096 ? adv : (int)(((uint64_t)adv * scaleq) >> 12);
}
}
void meow_font_drawtext_outline(uint16_t *fb, int x, int y, const char *utf8,
uint16_t color, uint16_t outline_color, int ol, int size) {
if (ol <= 0) { meow_font_drawtext(fb, x, y, utf8, color, size); return; }
static const int odx[8] = {-1,1,-1,1,0,0,-1,1};
static const int ody[8] = {-1,-1,1,1,-1,1,0,0};
/* 先将每个字形画 8 方向偏移描边,再画原色:逐字符处理以保证 advance 一致性。 */
meow_face *f = face_for_size(size);
if (!f || !g.plane) return;
unsigned scaleq = (size == f->px) ? 4096u : (unsigned)(((uint64_t)size << 12) / (uint64_t)f->px);
const unsigned char *p = (const unsigned char *)utf8;
int pen = 0;
while (*p) {
int cp = utf8_next(&p);
if (cp < 0) continue;
meow_glyph *gr = find_glyph(f, (uint32_t)cp);
if (gr && gr->w > 0 && gr->h > 0) {
for (int k = 0; k < 8; k++)
blit_glyph(fb, f, gr, x + pen + odx[k] * ol, y + ody[k] * ol, outline_color, scaleq);
}
int adv = gr ? gr->advance : (int)advance_for_missing(f);
pen += scaleq == 4096 ? adv : (int)(((uint64_t)adv * scaleq) >> 12);
}
meow_font_drawtext(fb, x, y, utf8, color, size);
}