WIP checkpoint (M2.53→M2.54 工作区): 字体归MeowOS/fonts+SDK跟随+多字体(进行中)+boot板载+状态栏28+亮度/音量修复+文档刷新

This commit is contained in:
MeowOS Dev committed 2026-08-30 03:27:24 +08:00
1 parent 245bee2946
commit 064635b3c4
44 files changed
+2834 -308

No files matched your search

+238 -63
View File
@@ -11,6 +11,11 @@
*
* 绘制模型: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)。
*
* 多字体(M2.5x):槽位模型。slot 0 = meow_font_load() 装载的全局/默认字库(font.file);
* 额外字库经 meow_font_load_slot() 装载。meow_font_enable_multifont(1) 开启后,
* drawtext/width_text/line_height/... 逐字符 meow_font_class_of() 分类 → 用该类别
* 对应槽的字库取字形与度量;缺该类别/槽未载 → 回退 slot 0。关闭(默认)一律 slot 0。
*/
#include "meow_font.h"
@@ -71,17 +76,23 @@ typedef struct {
meow_glyph *glyphs; /* 按 cp 升序 */
} meow_face;
/* 一个槽 = 一套已装载的字库(一个 PNG plane + 若干 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;
int loaded; /* 是否已装载 */
} meow_font_slot;
static meow_state g;
/* framebuffer 几何(全局,作用于所有槽;绘制前 meow_font_set_fb 设置一次)。 */
static unsigned g_fb_w, g_fb_h, g_fb_pitch;
static meow_font_slot g_slots[MEOW_MAX_SLOTS];
static int g_multifont = 0; /* 0=单字体(默认) */
static int g_class_slot[MEOW_FCLS_COUNT]; /* 每字符类 → 槽;默认全 0(跟随全局) */
/* PNG 内部为大端;idx(自定格式)为小端。 */
static uint32_t be32(const unsigned char *p) {
@@ -220,15 +231,16 @@ static unsigned char *png_decode_gray(const unsigned char *png, size_t len,
return out;
}
/* ---------------- idx 解析 ---------------- */
static int load_idx(const unsigned char *buf, size_t len) {
/* ---------------- idx 解析(写入指定槽) ---------------- */
static int load_idx(const unsigned char *buf, size_t len, int slot) {
meow_font_slot *st = &g_slots[slot];
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 一致 */
if (depth != st->depth) return -2; /* 位深须与 PNG 一致 */
size_t face_off = 16;
meow_face *faces = (meow_face *)calloc((size_t)nfaces, sizeof(meow_face));
@@ -271,20 +283,21 @@ static int load_idx(const unsigned char *buf, size_t len) {
return -5;
}
}
g.faces = faces;
g.face_count = nfaces;
st->faces = faces;
st->face_count = nfaces;
return 0;
}
/* ---------------- 面/字形查找 ---------------- */
static meow_face *face_for_size(int size) {
if (g.face_count <= 0) return NULL;
/* ---------------- 面/字形查找(槽感知) ---------------- */
static meow_face *face_for_size(int slot, int size) {
meow_font_slot *st = &g_slots[slot];
if (st->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;
for (int i = 0; i < st->face_count; i++) {
int d = st->faces[i].px - size;
if (d < 0) d = -d;
if (best_delta < 0 || d < best_delta) { best_delta = d; best = &g.faces[i]; }
if (best_delta < 0 || d < best_delta) { best_delta = d; best = &st->faces[i]; }
}
return best;
}
@@ -300,11 +313,12 @@ static meow_glyph *find_glyph(meow_face *f, uint32_t cp) {
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];
/* ---------------- 像素采样(depth 感知,槽感知) ---------------- */
static inline unsigned sample_px(int slot, unsigned x, unsigned y) {
meow_font_slot *st = &g_slots[slot];
if (x >= st->plane_w || y >= st->plane_h) return 0;
if (st->depth == 8) return st->plane[(size_t)y * st->plane_w + x];
unsigned char b = st->plane[((size_t)y * st->plane_w + x) >> 1];
unsigned nh = (x & 1) ? (b & 0x0F) : (b >> 4);
return nh * 17;
}
@@ -320,10 +334,10 @@ static inline uint16_t blend565(uint16_t bg, uint16_t fg, unsigned a) {
return (uint16_t)((r << 11) | (gc << 5) | b);
}
/* ---------------- 字形 blit(含缩放、裁剪、alpha 混合) ---------------- */
static void blit_glyph(uint16_t *fb, meow_face *face, const meow_glyph *gr,
/* ---------------- 字形 blit(含缩放、裁剪、alpha 混合;槽感知) ---------------- */
static void blit_glyph(uint16_t *fb, int slot, 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 元素每行数 */
int stride = (int)(g_fb_pitch / 2); /* uint16 元素每行数 */
/* 目标尺寸 = 源尺寸 * scaleq / 4096 */
unsigned gw, gh;
int dx, dy;
@@ -343,15 +357,15 @@ static void blit_glyph(uint16_t *fb, meow_face *face, const meow_glyph *gr,
for (unsigned oy = 0; oy < gh; oy++) {
int ay = py + (int)oy;
if (ay < 0) continue;
if (ay >= (int)g.fb_h) break;
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;
if (ax >= (int)g_fb_w) break;
int sx = (int)((uint64_t)ox * sw / gw);
unsigned a = sample_px((unsigned)(base + sx),
unsigned a = sample_px(slot, (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;
@@ -386,13 +400,13 @@ static uint32_t advance_for_missing(meow_face *f) {
}
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);
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;
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 (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);
@@ -401,23 +415,86 @@ static void draw_tofu(uint16_t *fb, int x, int y, int w, int h, uint16_t color)
}
}
/* ---------------- 字符分类器(multi_font 路由依据) ---------------- */
int meow_font_class_of(uint32_t cp) {
/* 数字 */
if (cp >= '0' && cp <= '9') return MEOW_FCLS_NUM;
/* 拉丁字母(基本拉丁) */
if ((cp >= 'A' && cp <= 'Z') || (cp >= 'a' && cp <= 'z')) return MEOW_FCLS_LATIN;
/* 半角标点/空格/控制符(ASCII 非字母数字) */
if (cp < 0x80) return MEOW_FCLS_LATIN_SYM;
/* 全角 / 中文标点 */
if ((cp >= 0x3000 && cp <= 0x303F) || /* CJK 符号与标点(含全角空格 U+3000) */
(cp >= 0xFF00 && cp <= 0xFFEF) || /* 半/全角形式(全角数字字母符号) */
cp == 0x2014 || cp == 0x2018 || cp == 0x2019 || /* — ‘’ */
cp == 0x201C || cp == 0x201D || cp == 0x2026 || /* “” … */
cp == 0x00B7) /* · */
return MEOW_FCLS_CJK_SYM;
/* CJK 表意文字 + 扩展 + 兼容表意 */
if ((cp >= 0x4E00 && cp <= 0x9FFF) ||
(cp >= 0x3400 && cp <= 0x4DBF) ||
(cp >= 0x20000 && cp <= 0x2A6DF) ||
(cp >= 0x2A700 && cp <= 0x2B73F) ||
(cp >= 0x2B740 && cp <= 0x2B81F) ||
(cp >= 0x2B820 && cp <= 0x2CEAF) ||
(cp >= 0x2CEB0 && cp <= 0x2EBEF) ||
(cp >= 0x30000 && cp <= 0x3134F) ||
(cp >= 0xF900 && cp <= 0xFAFF) ||
(cp >= 0x2F800 && cp <= 0x2FA1F))
return MEOW_FCLS_CJK;
return MEOW_FCLS_OTHER;
}
/* ---------------- 路由:一个 codepoint 应使用的槽(multi off → slot0) ---------------- */
static int route_slot(uint32_t cp) {
if (!g_multifont) return 0;
int cls = meow_font_class_of(cp);
if (cls < 0 || cls >= MEOW_FCLS_COUNT) return 0;
int s = g_class_slot[cls];
if (s >= 0 && s < MEOW_MAX_SLOTS && g_slots[s].loaded) return s;
return 0; /* 缺该类别字库/未装载 → 回退全局 slot0 */
}
/* 取某码位在路由落点槽内、对应最近字号的面 + scaleq;无可用字库返回 NULL。
* 顺带带回落点槽(供 blit 采样;不需要者传 NULL)。 */
static meow_face *routed_face(uint32_t cp, int size, unsigned *scaleq, int *slot_out) {
int slot = route_slot(cp);
if (slot_out) *slot_out = slot;
meow_font_slot *st = &g_slots[slot];
if (!st->loaded) return NULL;
meow_face *f = face_for_size(slot, size);
if (!f) return NULL;
if (scaleq) *scaleq = (size == f->px) ? 4096u : (unsigned)(((uint64_t)size << 12) / (uint64_t)f->px);
return f;
}
/* ---------------- 公共 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;
g_fb_w = fb_w; g_fb_h = fb_h; g_fb_pitch = pitch_bytes;
}
void meow_font_unload_slot(int slot) {
if (slot < 0 || slot >= MEOW_MAX_SLOTS) return;
meow_font_slot *st = &g_slots[slot];
if (st->faces) {
for (int i = 0; i < st->face_count; i++) free(st->faces[i].glyphs);
free(st->faces);
}
free(st->plane);
st->faces = NULL; st->plane = NULL; st->face_count = 0;
st->plane_w = st->plane_h = 0; st->plane_rowbytes = 0; st->depth = 0;
st->loaded = 0;
}
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;
for (int i = 0; i < MEOW_MAX_SLOTS; i++) meow_font_unload_slot(i);
g_multifont = 0;
for (int i = 0; i < MEOW_FCLS_COUNT; i++) g_class_slot[i] = 0;
}
int meow_font_load(const char *png_path, const char *idx_path) {
meow_font_unload();
int meow_font_load_slot(int slot, const char *png_path, const char *idx_path) {
if (slot < 0 || slot >= MEOW_MAX_SLOTS) return -9;
meow_font_unload_slot(slot);
size_t plen = 0, ilen = 0;
unsigned char *png = read_file(png_path, &plen);
if (!png) return -1;
@@ -430,46 +507,91 @@ int meow_font_load(const char *png_path, const char *idx_path) {
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;
meow_font_slot *st = &g_slots[slot];
st->plane = plane;
st->depth = depth;
st->plane_w = w;
st->plane_h = h;
st->plane_rowbytes = rowbytes;
if (load_idx(idx, ilen) != 0) {
if (load_idx(idx, ilen, slot) != 0) {
free(idx);
meow_font_unload();
meow_font_unload_slot(slot);
return -3;
}
free(idx);
st->loaded = 1;
return 0;
}
int meow_font_load(const char *png_path, const char *idx_path) {
meow_font_unload(); /* 全局字库(重)装:清全部槽 + 复位多字体配置 */
return meow_font_load_slot(0, png_path, idx_path);
}
void meow_font_enable_multifont(int on) { g_multifont = on ? 1 : 0; }
void meow_font_set_class_slot(meow_fclass cls, int slot) {
if (cls < 0 || cls >= MEOW_FCLS_COUNT) return;
g_class_slot[cls] = (slot >= 0 && slot < MEOW_MAX_SLOTS && g_slots[slot].loaded) ? slot : 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;
int lo = g_multifont ? 0 : 0;
int hi = g_multifont ? MEOW_MAX_SLOTS : 1;
for (int s = lo; s < hi; s++) {
meow_font_slot *st = &g_slots[s];
if (!st->loaded) continue;
for (int i = 0; i < st->face_count; i++)
if (st->faces[i].px == size) return 1;
}
return 0;
}
int meow_font_has_glyph(uint32_t cp) {
int slot = route_slot(cp);
meow_font_slot *st = &g_slots[slot];
if (!st->loaded) return 0;
for (int i = 0; i < st->face_count; i++)
if (find_glyph(&st->faces[i], cp)) return 1;
return 0;
}
int meow_font_ascent(int size) {
meow_face *f = face_for_size(size);
if (g_multifont) {
int best = 0;
for (int s = 0; s < MEOW_MAX_SLOTS; s++) {
meow_face *f = face_for_size(s, size);
if (f && f->ascent > best) best = f->ascent;
}
return best;
}
meow_face *f = face_for_size(0, size);
return f ? f->ascent : 0;
}
int meow_font_line_height(int size) {
meow_face *f = face_for_size(size);
if (g_multifont) {
int best = 0;
for (int s = 0; s < MEOW_MAX_SLOTS; s++) {
meow_face *f = face_for_size(s, size);
if (f) { int h = f->ascent + f->descent; if (h > best) best = h; }
}
return best;
}
meow_face *f = face_for_size(0, 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;
unsigned scaleq;
meow_face *f = routed_face((uint32_t)cp, size, &scaleq, NULL);
if (!f) 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;
@@ -480,17 +602,18 @@ int meow_font_width_text(const char *utf8, int size) {
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;
unsigned scaleq;
int slot;
meow_face *f = routed_face((uint32_t)cp, size, &scaleq, &slot);
if (!f) 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);
blit_glyph(fb, slot, f, gr, x + pen, y, color, scaleq);
} else if (!gr) {
/* 缺字:画空心方块 */
unsigned adv = advance_for_missing(f);
@@ -511,22 +634,74 @@ void meow_font_drawtext_outline(uint16_t *fb, int x, int y, const char *utf8,
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);
/* 先将每个字形画 8 方向偏移描边,再画原色:逐字符处理以保证 advance 一致。 */
const unsigned char *p = (const unsigned char *)utf8;
int pen = 0;
while (*p) {
int cp = utf8_next(&p);
if (cp < 0) continue;
unsigned scaleq;
int slot;
meow_face *f = routed_face((uint32_t)cp, size, &scaleq, &slot);
if (!f) 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);
blit_glyph(fb, slot, 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);
unsigned adv = gr ? gr->advance : advance_for_missing(f);
pen += scaleq == 4096 ? (int)adv : (int)(((uint64_t)adv * scaleq) >> 12);
}
meow_font_drawtext(fb, x, y, utf8, color, size);
}
/* ---------------- 多字体配置(去重 + ≤4 套上限,launcher 与 SDK 共用) ---------------- */
int meow_font_config_multifont(int on,
const char *global_png, const char *global_idx,
const meow_font_class_cfg cfg[MEOW_FCLS_COUNT],
int *distinct, int *truncated) {
if (distinct) *distinct = 0;
if (truncated) *truncated = 0;
(void)global_idx; /* 全局字库已由 meow_font_load(slot0) 装载,idx 此处不重读 */
/* 复位映射(保留已装载槽;未开则纯单字体) */
g_multifont = 0;
for (int i = 0; i < MEOW_FCLS_COUNT; i++) g_class_slot[i] = 0;
int g0 = g_slots[0].loaded;
if (!on) { if (distinct) *distinct = g0 ? 1 : 0; return 0; }
/* 已装载的不同字库(png 路径去重)。首项 = 全局(slot0)。 */
const char *d_png[MEOW_MAX_SLOTS];
int d_slot[MEOW_MAX_SLOTS];
int d_n = 0;
d_png[0] = global_png ? global_png : "";
d_slot[0] = 0;
d_n = 1;
g_multifont = 1;
for (int c = 0; c < MEOW_FCLS_COUNT; c++) {
const char *png = cfg[c].png;
if (!png || !png[0]) continue; /* 空 = 跟随全局 */
int found = -1;
if (global_png && !strcmp(png, global_png)) found = 0;
for (int k = 0; k < d_n && found < 0; k++)
if (!strcmp(png, d_png[k])) found = d_slot[k]; /* 同 family 去重 */
if (found >= 0) { g_class_slot[c] = found; continue; }
/* 新的一套不同字体:上限 MEOW_MAX_SLOTS(含全局)→ 截断 + WARN */
if (d_n >= MEOW_MAX_SLOTS) {
if (truncated) *truncated = 1;
g_class_slot[c] = 0; /* 该类别回退全局 */
continue;
}
int slot = d_n; /* slot0 已被全局占用 */
const char *idx = cfg[c].idx ? cfg[c].idx : "";
if (meow_font_load_slot(slot, png, idx) != 0) { /* 装载失败 → 回退全局 */
g_class_slot[c] = 0;
continue;
}
d_png[d_n] = png; d_slot[d_n] = slot; d_n++;
g_class_slot[c] = slot;
}
if (distinct) *distinct = d_n;
return 0;
}