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:
1 parent
90e28e7b7e
commit
c0f7a95719
10 files changed
+434
-92
No files matched your search
+98
-51
@@ -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;
|
||||
}
|
||||
|
||||
+5
-2
@@ -84,8 +84,11 @@ int meow_font_has_glyph(uint32_t cp);
|
||||
* meow_font_drawtext/width_text/line_height/... 在 meow_font_enable_multifont(1) 开启时
|
||||
* 逐字符分类 → 用该类别对应槽的字库取字形与度量;缺该类别字库/未加载 → 回退 slot 0。
|
||||
* 关闭(默认)时一律走 slot 0,行为与单字体完全一致(零额外开销)。
|
||||
* 同 family 去重、≤4 套上限由 meow_font_config_multifont() 统一处理(launcher 与 SDK 共用)。 */
|
||||
#define MEOW_MAX_SLOTS 5 /* slot0(全局) + ≤4 套类别字库;超出则截断并置 *truncated=1(调用方 WARN) */
|
||||
* 同 family 去重、类别字库 ≤3 套上限 + 打包面内存预算由 meow_font_config_multifont() 统一处理(launcher 与 SDK 共用)。 */
|
||||
#define MEOW_MAX_SLOTS 4 /* slot0(全局) + ≤3 套类别字库。真机 56MB:多字体每套字库都驻留整幅
|
||||
* 打包面(common≈4.2MiB / gbk≈16.6MiB),5 套太多会叠加 OOM;故硬上限
|
||||
* 收到 4,且 meow_font_config_multifont 另按 meow_font.c 内
|
||||
* MEOW_MULTIFONT_MEM_BUDGET 做「总打包面字节」预算(超出→新增类别回退全局)。 */
|
||||
|
||||
int meow_font_load_slot(int slot, const char *png_path, const char *idx_path); /* 0=成功,负值失败 */
|
||||
void meow_font_unload_slot(int slot);
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* test_budget.c — 宿主校验 meow_font_config_multifont 的「打包面内存预算」:
|
||||
* slot0=common(≈4.2MiB)+ 给 CJK 类别装 gbk(≈16.6MiB)→ 合计 > MEOW_MULTIFONT_MEM_BUDGET
|
||||
* → gbk 应被拒绝(distinct=1,truncated=1),防止大字库 + 多字体双驻留 OOM。
|
||||
* 用法:gcc -I. test_budget.c meow_font.c -lz -o test_budget
|
||||
* ./test_budget <common.png> <common.idx> <gbk.png> <gbk.idx>
|
||||
*/
|
||||
#include "meow_font.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(cond, msg) do { \
|
||||
if (!(cond)) { g_fail++; fprintf(stderr, "FAIL: %s\n", msg); } \
|
||||
else printf("ok : %s\n", msg); \
|
||||
} while (0)
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 5) { fprintf(stderr, "usage: %s <common.png> <common.idx> <gbk.png> <gbk.idx>\n", argv[0]); return 2; }
|
||||
int r = meow_font_load(argv[1], argv[2]); /* slot0 = common */
|
||||
CHECK(r == 0, "load slot0 common");
|
||||
meow_font_class_cfg cfg[MEOW_FCLS_COUNT];
|
||||
memset(cfg, 0, sizeof cfg);
|
||||
cfg[MEOW_FCLS_CJK].png = argv[3]; /* CJK 类别 → gbk 大字库 */
|
||||
cfg[MEOW_FCLS_CJK].idx = argv[4];
|
||||
int distinct = 0, truncated = 0;
|
||||
r = meow_font_config_multifont(1, argv[1], argv[2], cfg, &distinct, &truncated);
|
||||
CHECK(r == 0, "config_multifont ok");
|
||||
printf(" distinct=%d truncated=%d\n", distinct, truncated);
|
||||
/* slot0 common 4.2MiB + gbk 16.6MiB = 20.8MiB > 16MiB 预算 → gbk 被拒 → distinct 仅全局 */
|
||||
CHECK(distinct == 1, "distinct==1 (gbk rejected by MEM_BUDGET)");
|
||||
CHECK(truncated == 1, "truncated==1 (budget exceeded -> class fell back to global)");
|
||||
/* gbk 槽应未驻留:distinct==1(仅全局 common)即证明 gbk 未被 commit;
|
||||
* 注意 common 4k 亦含「中」,故不能以 has_glyph(中)==0 断言——改以「common 仍在」佐证。 */
|
||||
CHECK(meow_font_has_glyph('A') == 1, "has_glyph(A)==1 (common still there)");
|
||||
meow_font_unload();
|
||||
printf("\nRESULT: %s\n", g_fail ? "FAIL" : "PASS");
|
||||
return g_fail ? 1 : 0;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* test_mem_trace.c — MeowOS 字库加载内存驻留量测(宿主导出单测)。
|
||||
*
|
||||
* 目的:精确量化 meow_font_load / meow_font_unload / meow_font_reload_tier:
|
||||
* (1) load 过程中堆峰值(含 PNG 解码中间:文件缓冲 + idat + 原始扫描行 + 打包面);
|
||||
* (2) load 后稳态驻留(plane + glyphs);
|
||||
* (3) unload 后 live 归零(free 真正归还、无碎片驻留)。
|
||||
*
|
||||
* 机制:-Wl,--wrap=malloc/free/calloc/realloc 把 meow_font.c 的分配纳入统计。
|
||||
* __wrap_* 用「指针→字节数」记账表精确统计 live / peak(无论 libc 是否支持 usable_size)。
|
||||
*
|
||||
* 用法:
|
||||
* gcc -I. -O0 -g test_mem_trace.c meow_font.c -lz \
|
||||
* -Wl,--wrap=malloc -Wl,--wrap=free -Wl,--wrap=calloc -Wl,--wrap=realloc \
|
||||
* -o test_mem_trace
|
||||
* ./test_mem_trace <font.png> <font.idx> <tier>
|
||||
* 退出码 0=全过;非 0=某断言失败。
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include "meow_font.h"
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ---------------- 指针→大小 记账表 ---------------- */
|
||||
#define TREESZ 4096
|
||||
static struct { void *p; size_t sz; } g_tab[TREESZ];
|
||||
static size_t g_live = 0, g_peak = 0;
|
||||
static int g_tracking = 0;
|
||||
|
||||
static void note_add(void *p, size_t sz) {
|
||||
if (!g_tracking || !p) return;
|
||||
g_live += sz;
|
||||
if (g_live > g_peak) g_peak = g_live;
|
||||
for (int i = 0; i < TREESZ; i++) if (!g_tab[i].p) { g_tab[i].p = p; g_tab[i].sz = sz; return; }
|
||||
}
|
||||
static void note_sub(void *p) {
|
||||
if (!g_tracking || !p) return;
|
||||
for (int i = 0; i < TREESZ; i++) {
|
||||
if (g_tab[i].p == p) { g_live -= g_tab[i].sz; g_tab[i].p = NULL; g_tab[i].sz = 0; return; }
|
||||
}
|
||||
}
|
||||
static size_t note_size(void *p) {
|
||||
if (!p) return 0;
|
||||
for (int i = 0; i < TREESZ; i++) if (g_tab[i].p == p) return g_tab[i].sz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *__real_malloc(size_t n);
|
||||
void *__real_calloc(size_t n, size_t s);
|
||||
void *__real_realloc(void *p, size_t n);
|
||||
void __real_free(void *p);
|
||||
|
||||
void *__wrap_malloc(size_t n) { void *p = __real_malloc(n); if (p) note_add(p, n); return p; }
|
||||
void *__wrap_calloc(size_t n, size_t s) { void *p = __real_calloc(n, s); if (p) note_add(p, n * s); return p; }
|
||||
void *__wrap_realloc(void *p, size_t n) {
|
||||
if (!p) { void *np = __real_realloc(NULL, n); if (np) note_add(np, n); return np; }
|
||||
size_t old = note_size(p);
|
||||
void *np = __real_realloc(p, n);
|
||||
if (np) { note_sub(p); note_add(np, n); }
|
||||
else { /* realloc 失败:原指针仍有效,表项保留 */ }
|
||||
(void)old;
|
||||
return np;
|
||||
}
|
||||
void __wrap_free(void *p) { if (p) note_sub(p); __real_free(p); }
|
||||
|
||||
static void reset_peak(void) { g_peak = g_live; }
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(cond, msg) do { \
|
||||
if (!(cond)) { g_fail++; fprintf(stderr, "FAIL: %s (live=%zu peak=%zu)\n", msg, g_live, g_peak); } \
|
||||
else printf("ok : %s\n", msg); \
|
||||
} while (0)
|
||||
|
||||
static double MiB(size_t b) { return (double)b / 1048576.0; }
|
||||
static double KiB(size_t b) { return (double)b / 1024.0; }
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 3) { fprintf(stderr, "usage: %s <font.png> <font.idx>\n", argv[0]); return 2; }
|
||||
const char *png = argv[1], *idx = argv[2];
|
||||
|
||||
g_tracking = 1;
|
||||
reset_peak();
|
||||
printf("== meow_font 内存量测(精确指针记账)==\n");
|
||||
printf("png=%s idx=%s\n", png, idx);
|
||||
|
||||
int r = meow_font_load(png, idx);
|
||||
CHECK(r == 0, "meow_font_load ok");
|
||||
printf(" >> load 后稳态驻留 live = %.2f MiB (%.2f KiB)\n", MiB(g_live), KiB(g_live));
|
||||
printf(" >> load 期间堆峰值 peak = %.2f MiB (%.2f KiB)\n", MiB(g_peak), KiB(g_peak));
|
||||
|
||||
static uint16_t fb[480 * 320];
|
||||
meow_font_set_fb(480, 320, 480 * 2);
|
||||
printf(" drawtext smoke...\n");
|
||||
meow_font_drawtext(fb, 0, 0, "中文测试 Abc 123,", 0xFFFF, 28);
|
||||
meow_font_drawtext(fb, 0, 60, "试渲染第20号 face 中文", 0xF800, 20);
|
||||
printf(" drawtext 后 live = %.2f MiB\n", MiB(g_live));
|
||||
|
||||
/* 反复 load/unload 验证无泄漏累增(每次 load 后的 live 应一致,unload 后归零) */
|
||||
size_t live_after_load[3];
|
||||
for (int k = 0; k < 3; k++) {
|
||||
meow_font_unload();
|
||||
if (g_live != 0) { printf(" [iter %d] unload 后 live=%zu (非0! -> leak)\n", k, g_live); }
|
||||
meow_font_load(png, idx);
|
||||
live_after_load[k] = g_live;
|
||||
}
|
||||
printf(" 三次 load 后 live = %.2f / %.2f / %.2f MiB\n",
|
||||
MiB(live_after_load[0]), MiB(live_after_load[1]), MiB(live_after_load[2]));
|
||||
CHECK(live_after_load[0] == live_after_load[1] && live_after_load[1] == live_after_load[2],
|
||||
"三轮回 load/unload 稳定(无泄漏累增)");
|
||||
meow_font_unload();
|
||||
printf(" >> unload 后 live = %.2f MiB (峰值 %.2f MiB)\n", MiB(g_live), MiB(g_peak));
|
||||
CHECK(g_live == 0, "unload 后 live 归零(free 真正归还)");
|
||||
|
||||
printf("\nRESULT: %s\n", g_fail ? "FAIL" : "PASS");
|
||||
return g_fail ? 1 : 0;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* test_plane_dump.c — 宿主解码正确性校验:把 meow_font 解码出的打包像素面 dump 成二进制,
|
||||
* 供 Python 参考解码(zlib IDAT + 反滤波)逐字节比对,验证流式解码与参考解码完全一致。
|
||||
* 需以 -DMEOW_FONT_ENABLE_DEBUG 编译 meow_font.c 暴露 meow_font_debug_plane。
|
||||
*/
|
||||
#include "meow_font.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* 宿主调试钩子:定义于 meow_font.c(MEOW_FONT_ENABLE_DEBUG 下才编译);此测试自带声明。 */
|
||||
extern const unsigned char *meow_font_debug_plane(int slot, unsigned *w, unsigned *h,
|
||||
unsigned *depth, size_t *rowbytes);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 4) { fprintf(stderr, "usage: %s <font.png> <font.idx> <out.raw>\n", argv[0]); return 2; }
|
||||
int r = meow_font_load(argv[1], argv[2]);
|
||||
if (r != 0) { fprintf(stderr, "load failed %d\n", r); return 1; }
|
||||
unsigned w, h, depth; size_t rb;
|
||||
const unsigned char *p = meow_font_debug_plane(0, &w, &h, &depth, &rb);
|
||||
if (!p) { fprintf(stderr, "no plane\n"); return 1; }
|
||||
size_t total = (size_t)rb * h;
|
||||
FILE *f = fopen(argv[3], "wb");
|
||||
if (!f) { return 1; }
|
||||
/* 先写出几何头(小端),再写平面字节 */
|
||||
unsigned hdr[5] = { w, h, depth, (unsigned)rb, (unsigned)total };
|
||||
fwrite(hdr, sizeof(unsigned), 5, f);
|
||||
fwrite(p, 1, total, f);
|
||||
fclose(f);
|
||||
printf("dumped w=%u h=%u depth=%u rowbytes=%zu bytes=%zu\n", w, h, depth, rb, total);
|
||||
meow_font_unload();
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user