公开仓 ~/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。
465 lines
19 KiB
C
465 lines
19 KiB
C
/* 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;
|
||
}
|