Files
MeowOS-For-R3P2/patch_v2/sdk/test_meow_img_budget.c
T
AskaEth e23bdca26a fix(sdk): 收编公开 SDK 仓独有改进(图片预算解码 + JPEG 解码期缩解),主仓成为唯一权威源
公开仓 ~/Proj/MeowOS-SDK 的 sdk/ 与主仓 patch_v2/sdk 无共同 git 历史(subtree split
重写了提交哈希,不做跨仓 merge/subtree pull),按内容逐文件裁决。公开仓侧的真增量只有
commit 21bffa1(图片预算解码)及其依赖;其余差异是公开仓的自包含打包(扁平 include/、
vendored 头与 meow_font.c)或主仓早已超越的旧快照。

一、取公开侧(纯追加,主仓该文件无独有改动)
- stb_image.h:新增 stbi_load_scaled / stbi_load_scaled_from_memory——JPEG 输出级按
  步长抽取行/列,整幅原尺寸输出缓冲从不物化;进升级 JPEG 各分量系数 IDCT 进像素平面后
  立即释放。scale<=1 与旧 API 等价,非 JPEG 忽略。主仓版本的 diff 全部是公开侧新增
  (无主仓独有 hunk),故整体替换。
- meow_ui_img.c:追加 img_pick_scale / img_finish_dim / meow_img_probe_file /
  meow_img_load_file_dim / meow_img_load_mem_dim。既有加载入口一字未改。
- include/meow_ui_image.h:追加上述三个公共声明。
- 新增 test_meow_img_budget.c + test_meow_img_budget_gen.py(--wrap malloc/free 打点
  统计瞬时峰值,B1-B9 覆盖矩阵)。

二、取主仓(公开侧为旧快照或自包含适配,并入会回退功能)
- meow_ui_ime.c / test_ime_fix.c:主仓 bug3 修复(IME 打开瞬间 200ms TAP 冷却窗 +
  drain/40ms/再 drain)及其回归用例为公开侧所无;公开侧同名文件不含该修复。
- meow_ui_input.c / meow_ui_internal.h:主仓 meow_input_touch_reset(drain 与叠加抑制
  进入沿共用)、FreeType 渲染/字形声明、meow_time_ms_from_parts。公开侧此三处无独有声明
  可并,取主仓即并集。
- meow_ui_gfx.c / meow_ui_sys.c / meow_cachegov.c:主仓时钟 ABI 根因修复(o32 time32,
  meow_ui_time.c 链接期接管 __clock_gettime*)已把这些文件的本地 now_ms 统一为
  meow_ui_time_ms();公开侧仍是各自 clock_gettime,回落即回退根因修复。
- meow_ui_dialog.c / meow_select.c:主仓 MD3 后续(对话框按钮胶囊 MEOW_SHAPE_FULL、
  bottom sheet 顶角 28)晚于公开侧快照,公开侧等于旧版。
- include/meow_ui_font.h:主仓多出 FreeType 段与 stdint 自洽 include,公开侧缺。
- build_sdk.sh / build_sdk_host.sh:主仓含 FreeType vendor 裁剪构建、meow_ui_time TU、
  launcher meow_font.a 打包;公开侧为自包含重写,不回流。
- 其余 test_*.c:差异仅为编译配方注释(公开仓用 -Iinclude、无 meow_ui_time.c、标注
  「需主仓组件故公开仓跳过」等),对主仓目录布局不成立,不改。

三、不引入(vendored 副本 / 打包产物)
- meow_font.c、include/meow_font.h、include/meow_ipc.h、include/meow_notif.h:公开仓
  为自包含 vendored 副本(meow_font.c 与主仓 font/meow_font.c 逐字节相同),主仓已有
  权威源,复制会制造双源。

裁决说明(两边都改过的文件):
- test_meow_ui_select.c:公开仓把 T1 探针从 x=10 改到 x=12(commit 432875d)。主仓 x=10
  在本树实测 6/6 PASS(容器卡左缘 8、文本左缘 24,x=10 已在卡内且避开圆角),无失败
  可修,故不跟进该改动,避免无谓 churn。
- 公开侧「meow_media_playctl」一类 API 名:仅出现在公开仓 vendored 头/文档,主仓
  sdk 无对应实现,未硬造。

四、验证(worktree / 分支 feat/sdk-absorb-public-deltas)
- build_sdk_host.sh --verify:0 warning / 0 error;成员清单 76 个(OBJS 32 +
  meow_font.o + freetype 43)一致,全部 ELF32 MIPS32 rel2 (o32)。
- nm 确认 meow_ui.a 导出 meow_img_probe_file / meow_img_load_file_dim /
  meow_img_load_mem_dim / stbi_load_scaled / stbi_load_scaled_from_memory。
- 宿主测试:对「未改动的 main SDK」跑同一套矩阵做基线对比,40 项同名结果逐项完全一致
  (22 PASS / 11 FAIL / 7 建链失败,失败项与失败计数一字不差);本提交新增
  test_meow_img_budget 为 PASS。补充按各测试正确配方单编:test_meow_img_color、
  test_meow_lyric、test_meow_lyric_view、test_meow_ui_ft(显式传入 MiSans 字体)、
  test_umbrella 均通过/0 warning。
- 既有失败全部是 Termux 环境问题,与本次合并无关(基线同样失败):/tmp 不可写导致
  mkdtemp/fopen/bind EACCES(config/vol/notif_open/notify_ipc/notify_integration/
  sb_collapse/overlay_suppress/input_keys),audiod 守护不在场(ui_audio_host),
  test_sdk_fixes 阻塞循环超时(基线同样 124)。
- hello_app 用新 meow_ui.a 重链成功(0 warning,静态 MIPS32r2),可执行文件
  471380→473636 字节(+2256,新解码路径)。

五、影响
meow_ui.a 内容变化(新增 4 个 MEOW_API + stb 解码路径改变)→ 需重编重链 hello / files /
reader / ncm 四 App 与 meow_launcherd。
2026-11-05 20:03:20 +08:00

465 lines
19 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.
/* test_meow_img_budget.c — M12 预算解码(meow_img_load_file_dim / _mem_dim)宿主验证。
*
* 覆盖矩阵(对齐 M12 任务 3/4):
* B1 探针:meow_img_probe_file 只读头取原始尺寸(8.32MP 图),坏图/不存在返回 0
* B2 小图不动:64x64 走旧路径,与 meow_img_load_file 逐字节相同;maxdim<=0 等价旧入口
* B3 长边约束:8.32MP baseline JPEG 在 maxdim=480/300/128/64 下结果两维都 ≤ maxdim,长宽比保持
* B4 峰值对比:同一 8.32MP JPEG「旧入口 vs 新入口」瞬时分配峰值(--wrap malloc 打点),
* 断言新入口峰值 < 旧入口 1/2;B4b 极端 maxdim=100;B4c finding 原始场景 1600x2400 页
* B5 内容正确:新入口结果与「旧入口整幅解码 + meow_img_downscale」网格采样比对(平均差阈值)
* B6 进升级:progressive JPEG 走同一缩解路径(峰值记录 + 长边约束)
* B7 内存入口:meow_img_load_mem_dim 与文件入口等价(峰值 < 旧内存入口 1/2)
* B8 坏图:坏 JPEG(魔数+垃圾)/ 空内存 / 不存在路径 → NULL(不崩)
* B9 PNG/BMP:尺寸约束成立,峰值如实记录(stb 无解码期缩放 → 与旧入口同级)
*
* 夹具(生成一次):
* python3 test_meow_img_budget_gen.py "$PREFIX/tmp/meow_img_budget"
* 编译(本 sdk 目录下;--wrap 用于给 malloc/free 家族打点):
* gcc -Wall -Wextra -I. -Iinclude -DMEOW_UI_HOST_TEST -o test_meow_img_budget \
* test_meow_img_budget.c meow_ui_img.c stb_image.c -lm \
* -Wl,--wrap=malloc -Wl,--wrap=calloc -Wl,--wrap=realloc -Wl,--wrap=free
* 运行:./test_meow_img_budget "$PREFIX/tmp/meow_img_budget" (exit 0=PASS / 1=FAIL)
* 夹具目录可省略:默认 $PREFIX/tmp/meow_img_budget;缺失时自动调 gen 脚本生成。
*/
#define _GNU_SOURCE
#include "meow_ui.h"
#include "meow_ui_internal.h" /* meow_gfx_blit_rgba 原型(本测试只给桩) */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/stat.h>
/* ------------------------------------------------------------------ */
/* 分配打点:--wrap malloc/free 家族,记录「存活字节」与峰值 */
/* ------------------------------------------------------------------ */
#define AHDR 32 /* 头部长度:保留原始大小,保证 16 字节对齐 */
static size_t g_live = 0;
static size_t g_peak = 0;
extern void *__real_malloc(size_t);
extern void *__real_calloc(size_t, size_t);
extern void *__real_realloc(void *, size_t);
extern void __real_free(void *);
static void acc_alloc(size_t n)
{
g_live += n;
if (g_live > g_peak) g_peak = g_live;
}
void *__wrap_malloc(size_t n)
{
size_t *h = (size_t *)__real_malloc(n + AHDR);
if (!h) return NULL;
h[0] = n;
acc_alloc(n);
return (char *)h + AHDR;
}
void *__wrap_calloc(size_t a, size_t b)
{
size_t n = a * b;
void *p = __wrap_malloc(n);
if (p) memset(p, 0, n);
return p;
}
void __wrap_free(void *p)
{
size_t *h;
if (!p) return;
h = (size_t *)((char *)p - AHDR);
g_live -= h[0];
__real_free(h);
}
void *__wrap_realloc(void *p, size_t n)
{
size_t old = p ? ((size_t *)((char *)p - AHDR))[0] : 0;
void *q = __wrap_malloc(n);
if (!q) return NULL;
if (p) {
memcpy(q, p, old < n ? old : n);
__wrap_free(p);
}
return q;
}
static size_t peak_reset(void) { g_peak = g_live; return g_live; }
static size_t peak_since(size_t base) { return g_peak - base; }
/* meow_ui_img.c 的绘制函数需要的 gfx 符号:本测试只测解码/内存,桩即可。 */
void meow_gfx_blit_rgba(int x, int y, int w, int h, const uint8_t *rgba, int alpha)
{
(void)x; (void)y; (void)w; (void)h; (void)rgba; (void)alpha;
}
/* ------------------------------------------------------------------ */
/* 断言与打印 */
/* ------------------------------------------------------------------ */
static int g_fail = 0;
#define CHECK(cond, name) do { \
if (cond) printf(" PASS %s\n", name); \
else { printf(" FAIL %s (%s:%d)\n", name, __FILE__, __LINE__); g_fail++; } } while (0)
static void peak_report(const char *tag, size_t bytes)
{
printf(" peak %-28s %8.2f MiB (%zu B)\n", tag, (double)bytes / (1024.0 * 1024.0), bytes);
}
/* ------------------------------------------------------------------ */
/* 路径与夹具 */
/* ------------------------------------------------------------------ */
static const char *g_dir = NULL;
static char *path_join(const char *name)
{
size_t n = strlen(g_dir) + strlen(name) + 2;
char *p = (char *)malloc(n);
if (!p) { printf("OOM\n"); exit(1); }
snprintf(p, n, "%s/%s", g_dir, name);
return p;
}
static int file_exists(const char *p)
{
struct stat st;
return stat(p, &st) == 0 && st.st_size > 0;
}
static void ensure_fixtures(void)
{
char *big = path_join("big.jpg");
if (file_exists(big)) { free(big); return; }
free(big);
printf("夹具缺失,尝试 python3 test_meow_img_budget_gen.py %s\n", g_dir);
{
char cmd[1024];
snprintf(cmd, sizeof cmd, "python3 test_meow_img_budget_gen.py '%s'", g_dir);
if (system(cmd) != 0)
printf(" 生成失败(缺 Pillow?)——请按头注释手动生成\n");
}
}
static uint8_t *read_whole(const char *p, size_t *len)
{
FILE *f = fopen(p, "rb");
uint8_t *buf;
long n;
if (!f) return NULL;
fseek(f, 0, SEEK_END);
n = ftell(f);
fseek(f, 0, SEEK_SET);
if (n <= 0) { fclose(f); return NULL; }
buf = (uint8_t *)malloc((size_t)n);
if (!buf) { fclose(f); return NULL; }
if (fread(buf, 1, (size_t)n, f) != (size_t)n) { free(buf); fclose(f); return NULL; }
fclose(f);
*len = (size_t)n;
return buf;
}
/* ------------------------------------------------------------------ */
/* 图像辅助 */
/* ------------------------------------------------------------------ */
static int nonblank(const meow_img *im)
{
int w = meow_img_w(im), h = meow_img_h(im);
const unsigned char *p = meow_img_pixels(im);
long sum = 0;
if (!p || w <= 0 || h <= 0) return 0;
for (long i = 0; i < (long)w * h; i += 97)
sum += p[i * 4] + p[i * 4 + 1] + p[i * 4 + 2];
return sum > 0;
}
/* 按比例网格采样比对两图(尺寸可不同):返回各通道平均绝对差。 */
static double mean_abs_diff(const meow_img *a, const meow_img *b, int grid)
{
int wa = meow_img_w(a), ha = meow_img_h(a);
int wb = meow_img_w(b), hb = meow_img_h(b);
const unsigned char *pa = meow_img_pixels(a), *pb = meow_img_pixels(b);
long acc = 0, cnt = 0;
if (!pa || !pb || wa <= 0 || hb <= 0) return 1e9;
for (int gy = 0; gy < grid; gy++) {
int ya = (int)((long)gy * ha / grid);
int yb = (int)((long)gy * hb / grid);
for (int gx = 0; gx < grid; gx++) {
int xa = (int)((long)gx * wa / grid);
int xb = (int)((long)gx * wb / grid);
const unsigned char *qa = pa + ((size_t)ya * wa + xa) * 4;
const unsigned char *qb = pb + ((size_t)yb * wb + xb) * 4;
for (int k = 0; k < 3; k++) {
int d = (int)qa[k] - (int)qb[k];
acc += d < 0 ? -d : d;
cnt++;
}
}
}
return cnt ? (double)acc / (double)cnt : 1e9;
}
/* ================================================================== */
int main(int argc, char **argv)
{
char *p_big, *p_prog, *p_small, *p_png, *p_bmp, *p_broken, *p_missing;
size_t peak_old, peak_new;
if (argc > 1) {
g_dir = argv[1];
} else {
/* Termux 的 /tmp 不可写:优先 $TMPDIR,其次 $PREFIX/tmp。 */
static char dflt[600];
const char *tmp = getenv("TMPDIR");
const char *pre = getenv("PREFIX");
if (tmp && *tmp) snprintf(dflt, sizeof dflt, "%s/meow_img_budget", tmp);
else if (pre && *pre) snprintf(dflt, sizeof dflt, "%s/tmp/meow_img_budget", pre);
else snprintf(dflt, sizeof dflt, "/tmp/meow_img_budget");
g_dir = dflt;
}
ensure_fixtures();
p_big = path_join("big.jpg");
p_prog = path_join("bigprog.jpg");
p_small = path_join("small.jpg");
p_png = path_join("big.png");
p_bmp = path_join("big.bmp");
p_broken = path_join("broken.jpg");
p_missing = path_join("no_such_file.jpg");
if (!file_exists(p_big)) {
printf("FATAL 夹具 %s 不存在(先跑 test_meow_img_budget_gen.py)\n", p_big);
return 1;
}
/* ---- B1 探针 ---- */
printf("[B1] 头探测(零像素内存)\n");
{
int w = 0, h = 0, c = 0;
CHECK(meow_img_probe_file(p_big, &w, &h, &c) == 1, "probe 成功");
CHECK(w == 3200 && h == 2600, "probe 原始尺寸 3200x2600");
CHECK(c == 3, "probe 通道数 3");
printf(" probe → %dx%d ch=%d\n", w, h, c);
CHECK(meow_img_probe_file(p_broken, NULL, NULL, NULL) == 0, "probe 截断图 → 0");
CHECK(meow_img_probe_file(p_missing, NULL, NULL, NULL) == 0, "probe 不存在 → 0");
CHECK(meow_img_probe_file(NULL, NULL, NULL, NULL) == 0, "probe NULL → 0");
}
/* ---- B2 小图不动 ---- */
printf("[B2] 小图不动 / maxdim<=0 等价旧入口\n");
{
meow_img *a = meow_img_load_file(p_small);
meow_img *b = meow_img_load_file_dim(p_small, 64);
meow_img *z = meow_img_load_file_dim(p_small, 0);
CHECK(a && b && z, "小图三条路径都成功");
CHECK(meow_img_w(b) == meow_img_w(a) && meow_img_h(b) == meow_img_h(a),
"maxdim=64 → 尺寸不变");
CHECK(b && a && meow_img_w(a) * meow_img_h(a) > 0 &&
memcmp(meow_img_pixels(a), meow_img_pixels(b),
(size_t)meow_img_w(a) * meow_img_h(a) * 4) == 0,
"小图逐字节与旧入口一致");
CHECK(z && a && memcmp(meow_img_pixels(a), meow_img_pixels(z),
(size_t)meow_img_w(a) * meow_img_h(a) * 4) == 0,
"maxdim=0 等价旧入口(逐字节)");
meow_img_free(a); meow_img_free(b); meow_img_free(z);
}
/* ---- B3 长边约束 ---- */
printf("[B3] 8.32MP baseline JPEG 长边约束\n");
{
static const int dims[] = { 480, 300, 128, 64 };
int wi = 0, hi = 0;
meow_img *full = meow_img_load_file(p_big);
CHECK(full != NULL, "旧入口解码成功");
if (full) { wi = meow_img_w(full); hi = meow_img_h(full); }
printf(" 原图 %dx%d (%.2f MP)\n", wi, hi, (double)wi * hi / 1e6);
for (size_t i = 0; i < sizeof dims / sizeof dims[0]; i++) {
meow_img *s = meow_img_load_file_dim(p_big, dims[i]);
char name[96];
snprintf(name, sizeof name, "maxdim=%d → 两维 ≤ maxdim 且非空", dims[i]);
CHECK(s && meow_img_w(s) <= dims[i] && meow_img_h(s) <= dims[i] && nonblank(s), name);
if (s) {
double ar0 = (double)wi / hi, ar1 = (double)meow_img_w(s) / meow_img_h(s);
CHECK(ar1 > ar0 * 0.98 && ar1 < ar0 * 1.02, "长宽比保持(±2%)");
printf(" maxdim=%-4d → %dx%d\n", dims[i], meow_img_w(s), meow_img_h(s));
}
meow_img_free(s);
}
meow_img_free(full);
}
/* ---- B4 峰值对比(JPEG) ---- */
printf("[B4] 瞬时分配峰值:旧入口 vs 新入口(同一 8.32MP JPEG)\n");
{
size_t base = peak_reset();
meow_img *old = meow_img_load_file(p_big);
peak_old = peak_since(base);
base = peak_reset();
meow_img *nw = meow_img_load_file_dim(p_big, 480);
peak_new = peak_since(base);
CHECK(old && nw, "两条路径都成功");
peak_report("旧入口 meow_img_load_file", peak_old);
peak_report("新入口 _dim(480)", peak_new);
printf(" 整幅 RGBA 理论值 = %.2f MiB;新/旧 = %.2f\n",
(double)meow_img_w(old) * meow_img_h(old) * 4 / (1024.0 * 1024.0),
(double)peak_new / (double)peak_old);
CHECK(peak_new * 2 < peak_old, "新入口峰值 < 旧入口 1/2(不物化整幅 RGBA)");
CHECK(peak_new < (size_t)20 * 1024 * 1024, "新入口峰值 < 20 MiB(8.32MP 页可加载)");
/* B5 内容正确性:新 vs 旧整幅 + downscale 精修 */
if (old && nw) {
meow_img *ref = meow_img_downscale(old, 480);
double d = mean_abs_diff(nw, ref ? ref : old, 64);
printf(" 与「旧入口+downscale」网格平均差 = %.2f\n", d);
CHECK(d < 20.0, "缩解内容结构一致(平均差 < 20)");
meow_img_free(ref);
}
meow_img_free(old);
meow_img_free(nw);
}
/* ---- B4b 极端 maxdim ---- */
printf("[B4b] maxdim=100(8 倍缩解档位 + 精修)\n");
{
size_t base = peak_reset();
meow_img *s = meow_img_load_file_dim(p_big, 100);
size_t peak = peak_since(base);
CHECK(s && meow_img_w(s) <= 100 && meow_img_h(s) <= 100 && nonblank(s), "结果 ≤100 且非空");
peak_report("_dim(100)", peak);
CHECK(peak * 2 < peak_old, "峰值仍 < 旧入口 1/2");
meow_img_free(s);
}
/* ---- B4c finding 原始场景 ---- */
printf("[B4c] finding 原始场景:1600x2400 漫画页\n");
{
char *p_page = path_join("page.jpg");
if (file_exists(p_page)) {
size_t base = peak_reset();
meow_img *old = meow_img_load_file(p_page);
size_t p_old = peak_since(base);
base = peak_reset();
meow_img *nw = meow_img_load_file_dim(p_page, 720);
size_t p_new = peak_since(base);
CHECK(old && nw, "1600x2400 两条路径都成功");
if (nw)
CHECK(meow_img_w(nw) <= 720 && meow_img_h(nw) <= 720 && nonblank(nw),
"结果 ≤720 且非空");
if (old)
printf(" 整幅 RGBA 理论值 = %.2f MiB\n",
(double)meow_img_w(old) * meow_img_h(old) * 4 / (1024.0 * 1024.0));
peak_report("旧入口 page.jpg", p_old);
peak_report("新入口 _dim(720)", p_new);
CHECK(p_new < (size_t)10 * 1024 * 1024, "新入口峰值 < 10 MiB(设备 ~14MB 口径内)");
meow_img_free(old);
meow_img_free(nw);
} else {
printf(" SKIP page.jpg(夹具缺失)\n");
}
free(p_page);
}
/* ---- B6 进升级 JPEG ---- */
printf("[B6] progressive JPEG(同一缩解路径)\n");
{
size_t base = peak_reset();
meow_img *old = meow_img_load_file(p_prog);
size_t p_old = peak_since(base);
base = peak_reset();
meow_img *nw = meow_img_load_file_dim(p_prog, 480);
size_t p_new = peak_since(base);
CHECK(old && nw, "进升级两条路径都成功");
CHECK(nw && meow_img_w(nw) <= 480 && meow_img_h(nw) <= 480 && nonblank(nw),
"进升级结果 ≤480 且非空");
peak_report("旧入口 progressive", p_old);
peak_report("新入口 _dim(480) progressive", p_new);
CHECK(p_new < p_old, "进升级峰值下降(系数阵列仍在扫描期驻留,见报告)");
meow_img_free(old);
meow_img_free(nw);
}
/* ---- B7 内存入口 ---- */
printf("[B7] 内存入口 meow_img_load_mem_dim(CBZ/档案页场景)\n");
{
size_t len = 0;
uint8_t *buf = read_whole(p_big, &len);
CHECK(buf != NULL, "读入 8.32MP JPEG 到内存");
if (buf) {
size_t base = peak_reset();
meow_img *old = meow_img_load_mem(buf, (int)len);
size_t p_old = peak_since(base);
base = peak_reset();
meow_img *nw = meow_img_load_mem_dim(buf, (int)len, 480);
size_t p_new = peak_since(base);
CHECK(old && nw, "内存入口两条路径都成功");
CHECK(nw && meow_img_w(nw) <= 480 && nonblank(nw), "内存入口结果 ≤480 且非空");
peak_report("旧 meow_img_load_mem", p_old);
peak_report("新 meow_img_load_mem_dim", p_new);
CHECK(p_new * 2 < p_old, "内存入口峰值 < 旧入口 1/2");
meow_img_free(old);
meow_img_free(nw);
free(buf);
}
}
/* ---- B8 坏图 ---- */
printf("[B8] 坏图容错\n");
{
size_t len = 0;
uint8_t *buf;
CHECK(meow_img_load_file_dim(p_broken, 480) == NULL, "截断 JPEG → NULL");
CHECK(meow_img_load_file_dim(p_missing, 480) == NULL, "不存在路径 → NULL");
CHECK(meow_img_load_file_dim(NULL, 480) == NULL, "NULL 路径 → NULL");
buf = read_whole(p_broken, &len);
CHECK(buf && meow_img_load_mem_dim(buf, (int)len, 480) == NULL, "截断 JPEG 内存入口 → NULL");
free(buf);
CHECK(meow_img_load_mem_dim("x", 3, 480) == NULL, "垃圾内存 → NULL");
}
/* ---- B9 PNG / BMP(stb 无解码期缩放,如实记录峰值) ---- */
printf("[B9] PNG / BMP:尺寸约束成立,峰值如实记录(stb 无解码期缩放)\n");
{
struct { const char *tag; const char *path; } fx[] = {
{ "PNG", p_png }, { "BMP", p_bmp },
};
for (size_t i = 0; i < sizeof fx / sizeof fx[0]; i++) {
if (!file_exists(fx[i].path)) {
printf(" SKIP %s(夹具缺失)\n", fx[i].tag);
continue;
}
{
size_t base = peak_reset();
meow_img *old = meow_img_load_file(fx[i].path);
size_t p_old = peak_since(base);
meow_img *nw;
char name[96];
base = peak_reset();
nw = meow_img_load_file_dim(fx[i].path, 480);
{
size_t p_new = peak_since(base);
snprintf(name, sizeof name, "%s _dim(480) 两维 ≤480 且非空", fx[i].tag);
CHECK(nw && meow_img_w(nw) <= 480 && meow_img_h(nw) <= 480 && nonblank(nw), name);
peak_report(fx[i].tag, p_old);
peak_report(" ↑ 新入口", p_new);
snprintf(name, sizeof name, "%s 峰值与旧入口同级(stb 无解码期缩放)", fx[i].tag);
CHECK(p_new <= p_old + p_old / 8, name);
if (old) printf(" %s 原图 %dx%d\n", fx[i].tag, meow_img_w(old), meow_img_h(old));
meow_img_free(nw);
}
meow_img_free(old);
}
}
}
if (g_fail) printf("\nRESULT: FAIL (%d)\n", g_fail);
else printf("\nRESULT: ALL PASS\n");
free(p_big); free(p_prog); free(p_small); free(p_png);
free(p_bmp); free(p_broken); free(p_missing);
return g_fail ? 1 : 0;
}