feat(font+sys): 字库内存纪律(agent-215)+sysui接管launcher监管(agent-216)

215: meow_font.c 流式分块解码(gbk峰值40→23.5MB, 逐字节校验一致) + 多字体槽5→4+16MB驻留预算; 量化: gbk稳态驻留17.3MB vs common 4.2MB(OOM主因); meow_font.a→67113c03 meow_ui.a→223d5401
216: bootmenu让位(sysui在则start launcher让位, 不在则exec兜底) + S70预置launcher.conf enabled=1(+旧设备升级迁移); launcher崩溃可自动拉起; bootmenu 4441822d / S70 e435affc / S90 e0ace111
216注: S70早启动与选单共存为真机重点观察项
This commit is contained in:
MeowOS committed 2026-08-31 13:28:07 +08:00
1 parent 90e28e7b7e
commit c0f7a95719
10 files changed
+434 -92

No files matched your search

+98 -51
View File
@@ -122,26 +122,13 @@ static unsigned char *read_file(const char *path, size_t *out_len) {
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。 */
/* ---------------- 最小 PNG 解码(灰度 non-interlaced) ----------------
* 流式/分块解码,消除「整幅原始扫描行 raw + 整段 idat 拷贝」双份驻留:
* - idat 不再另外 malloc 拷贝,直接以 png 文件缓冲内 IDAT 区间作 zlib 输入;
* - 逐行 inflate 到单行小缓冲 rowbuf(rowbytes+1 字节),随即反滤波写入打包面 out;
* 不再分配整幅 raw(= 扫描行数×(rowbytes+1))——对大字库这是 6-16MB 级的一次性峰值。
* 大字库 gbk 解码面本身很大(行数×rowbytes),流式后 load 峰值 ≈ 文件缓冲 + idx + 打包面,
* 而非 文件缓冲 + idat + raw + 打包面。*/
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) {
@@ -150,14 +137,14 @@ static unsigned char *png_decode_gray(const unsigned char *png, size_t len,
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;
size_t idat_off = 0, idat_len = 0; /* IDAT 在 png 内的连续区间(偏移/长度) */
int have_idat = 0;
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; }
if (clen < 13) return NULL;
w = be32(png + i + 8);
h = be32(png + i + 12);
depth = png[i + 16];
@@ -167,43 +154,61 @@ static unsigned char *png_decode_gray(const unsigned char *png, size_t len,
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;
if (!have_idat) { idat_off = i + 8; idat_len = clen; have_idat = 1; }
else {
/* 要求 IDAT 连续(合法 PNG/单段压缩满足;gen_font.py 亦单段) */
if (idat_off + idat_len != i + 8) return NULL;
idat_len += clen;
}
} else if (memcmp(type, "IEND", 4) == 0) {
break;
}
i += 12 + (size_t)clen;
}
if (!have_ihdr || !idat ||
if (!have_ihdr || !have_idat ||
color != 0 || compression != 0 || filt != 0 || interlace != 0 ||
(depth != 4 && depth != 8) || w == 0 || h == 0) {
free(idat);
(depth != 4 && depth != 8) || w == 0 || h == 0)
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;
size_t rawlen = scanlen * h; /* 期望的解压总量(校验用,不据此分配) */
unsigned char *out = (unsigned char *)malloc(rowbytes * h);
if (!out) return NULL;
unsigned char *rowbuf = (unsigned char *)malloc(scanlen);
if (!rowbuf) { free(out); return NULL; }
mzf_stream s;
memset(&s, 0, sizeof(s));
s.next_in = (mzf_Bytef *)(png + idat_off);
s.avail_in = (mzf_uInt)idat_len;
s.zalloc = 0; s.zfree = 0; s.opaque = 0;
if (inflateInit_(&s, zlibVersion(), (int)sizeof(s)) != MZF_OK) {
free(out); free(rowbuf); 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;
size_t produced = 0;
int ok = 1, stream_end = 0;
for (unsigned y = 0; y < h; y++) {
/* 逐行填满 rowbuf(zlib 可能分批产出,内层循环补足到 scanlen) */
size_t filled = 0;
while (filled < scanlen) {
s.next_out = rowbuf + filled;
s.avail_out = (mzf_uInt)(scanlen - filled);
int ret = inflate(&s, MZF_NO_FLUSH);
size_t got = (scanlen - filled) - s.avail_out;
filled += got;
if (ret == MZF_STREAM_END) { stream_end = 1; break; }
if (ret != MZF_OK || got == 0) { ok = 0; break; }
}
if (!ok || filled != scanlen) { ok = 0; break; }
/* 反滤波(bpp=1:灰度 depth 4|8)→ out 为每行 rowbytes 的打包像素 */
unsigned char ft = rowbuf[0];
const unsigned char *c = rowbuf + 1;
unsigned char *dst = out + (size_t)y * rowbytes;
unsigned char *prev = (y == 0) ? NULL : out + (size_t)(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;
@@ -223,11 +228,17 @@ static unsigned char *png_decode_gray(const unsigned char *png, size_t len,
dst[x] = v + pred;
break;
}
default: free(raw); free(out); return NULL;
default: ok = 0; break;
}
}
if (!ok) break;
produced += scanlen;
if (stream_end) break;
}
free(raw);
inflateEnd(&s);
free(rowbuf);
if (!ok || produced != rawlen) { free(out); return NULL; }
*out_w = w; *out_h = h; *out_depth = depth; *out_rowbytes = rowbytes;
return out;
}
@@ -493,6 +504,18 @@ void meow_font_unload(void) {
for (int i = 0; i < MEOW_FCLS_COUNT; i++) g_class_slot[i] = 0;
}
#ifdef MEOW_FONT_ENABLE_DEBUG
/* 宿主调试/校验钩子:取某槽已装载(打包)像素面 + 几何。仅宿主单测用,
* mipsel 构建不含 MEOW_FONT_ENABLE_DEBUG,无此符号、零开销。 */
const unsigned char *meow_font_debug_plane(int slot, unsigned *w, unsigned *h,
unsigned *depth, size_t *rowbytes) {
meow_font_slot *st = &g_slots[slot];
if (!st->loaded) return NULL;
*w = st->plane_w; *h = st->plane_h; *depth = st->depth; *rowbytes = st->plane_rowbytes;
return st->plane;
}
#endif
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);
@@ -672,7 +695,23 @@ void meow_font_drawtext_outline(uint16_t *fb, int x, int y, const char *utf8,
meow_font_drawtext(fb, x, y, utf8, color, size);
}
/* ---------------- 多字体配置(去重 + ≤4 套上限,launcher 与 SDK 共用) ---------------- */
/* ---------------- 多字体配置(去重 + ≤3 套上限 + 内存预算,launcher 与 SDK 共用) ---------------- */
/* 多字体全部槽(含 slot0)「打包像素面」总字节预算:超出时新增类别字库被拒、回退全局并用
* *truncated=1 通知调用方 WARN。设定依据(真机 56MB,launcher+App 常态化紧张):
* slot0 若为 gbk 大字库,其 plane 已 ≈16.6MiB(>预算)→ 自动拒绝任何额外类别字库,
* 杜绝「大字库 + 多字体」双驻留 OOM;slot0 为 common(≈4.2MiB)时允许 1-3 个额外常用类
* (+小类拉丁/数字等),封顶后单进程字库总驻留可控制在 ~16MiB 内。
* 值 16 MiB:56MiB 总内存 − launcher 基线(估 26-28MiB)− 应用运行时 → 字库红利带。 */
#define MEOW_MULTIFONT_MEM_BUDGET (16u * 1024 * 1024)
static size_t mfont_total_plane_bytes(void) {
size_t t = 0;
for (int i = 0; i < MEOW_MAX_SLOTS; i++) {
meow_font_slot *st = &g_slots[i];
if (st->loaded) t += (size_t)st->plane_rowbytes * st->plane_h;
}
return t;
}
int meow_font_config_multifont(int on,
const char *global_png, const char *global_idx,
const meow_font_class_cfg cfg[MEOW_FCLS_COUNT],
@@ -716,6 +755,14 @@ int meow_font_config_multifont(int on,
g_class_slot[c] = 0;
continue;
}
/* 内存预算:装载成功后,若全部槽 plane 总字节超预算 → 卸载该新槽 + 回退全局 + 截断通知。
* 防「大字库 slot0 + 多个大字库类别字库」同时驻留(真机 56MB 会 OOM)。 */
if (mfont_total_plane_bytes() > MEOW_MULTIFONT_MEM_BUDGET) {
meow_font_unload_slot(slot);
if (truncated) *truncated = 1;
g_class_slot[c] = 0;
continue;
}
d_png[d_n] = png; d_slot[d_n] = slot; d_n++;
g_class_slot[c] = slot;
}