图库: 门控模型接真缩解后端 + _step 全格式后端(M27)

M23 交付了真缩解后端却「修好了用不上」:_step 只接 JPEG,而阅读器的峰值模型
仍是 stb 口径(PNG/BMP 按 W·H·4、JPEG 按缩解后整幅 RGBA),12MP/PNG 页全被判
超预算占位。本次把两处缝焊上:

- sdk/meow_ui_img.c:meow_img_load_file_step 的 JPEG-only 限制放开到 PNG/BMP;
  新增 meow_img_load_file_step_ex 回报实际路径(backend/stb),日志可核对预判。
- tuku/logic/reader/page_source.[ch]:新增 reader_estimate_peak(),成为峰值模型 +
  预算 + MemAvailable 门控的唯一实现(reader 不再另写一份)。
  * 后端命中(JPEG SOF0/1/2、≤3 分量、8bit、N=1..7;PNG 非交错/标准压缩/合法色型;
    BMP DIB≥40/BI_RGB/8·16·24·32bpp)→ 峰值 = 目标图 + 分量平面/行缓冲 + 固定开销,
    与源分辨率无关 → 直接放行,不门控;
  * 只有回退 stb(CMYK/算术编码、Adam7、RLE/1-4bpp、N≥8 小图等)才按 M22 stb 模型
    算峰值,超 3MB 或 MemAvailable <6MB 才占位。Adam7 另补 stb 的整幅暂存上界。
- reader:删掉本地 stb 模型/常量,统一调 reader_estimate_peak;日志加 path=backend|stb。
- 测试:6 类典型页(2000×3000 baseline/渐进/restart、1600×2400 PNG/BMP、4MP、12MP)
  断言全部 backend + 不占位 + 估算 ≥ 实测且 ≤1.5×(实测 1.03–1.08×);两条路径输出
  尺寸逐像素一致;CMYK/Adam7/1bpp 断言走 stb 并按旧门控占位;连翻 30 页无占位、
  单页分配峰值 ≤3MB;membudget 链接补 meow_ui_time.c 与 M23 两个解码 TU。
This commit is contained in:
AskaEth committed 2026-11-06 11:46:16 +08:00
1 parent 668629bda3
commit 1725af631b
9 files changed
+797 -195

No files matched your search

+52
View File
@@ -107,11 +107,63 @@ typedef struct mr_img_info {
int progressive; /* 1=渐进式 JPEG(SOF2/SOF6/SOF10/SOF14) */
int channels; /* 源通道数(JPEG 分量数 / PNG 颜色类型 / BMP 位深/8) */
long long planes; /* 见上:stb 中间面字节(不含最终 RGBA 输出) */
/* ---- 追加(M27):真缩解后端(M23)支持判定所需的头字段。
* 缺省 0 = 未知;判定按「不支持」处理(保守回退 stb 门控),绝不因缺字段误走后端。 ---- */
int depth; /* JPEG 精度 / PNG 位深 / BMP 位深(bpp) */
int color; /* PNG 颜色类型(0/2/3/4/6);非 PNG 为 0 */
int sof; /* JPEG SOF marker(0xC0/0xC1/0xC2 才可能走后端) */
int interlace; /* PNG 1=Adam7 交错(后端不支持) */
int compression; /* PNG 压缩法 / BMP BI_*(0 才可能走后端) */
int filter; /* PNG 滤波法(0 才可能走后端) */
int dib; /* BMP DIB 头长度(<40 后端不支持) */
int ncomp; /* JPEG 分量数(1..3;4=CMYK → 后端不支持) */
unsigned char comp_h[3], comp_v[3], comp_tq[3]; /* JPEG 采样因子/量化表号 */
} mr_img_info;
int mr_probe_info(const char *path, mr_img_info *info);
int mr_probe_info_mem(const void *buf, size_t len, mr_img_info *info);
/* ---- 追加(M27):解码峰值模型 + 门控的唯一实现 ----
* 阅读器(ui/reader)与页源(本层)都只调 reader_estimate_peak,不得各写一份模型。
* 一次调用同时回答三件事:
* path 走后端(MR_DECODE_PATH_BACKEND)还是回退 stb(MR_DECODE_PATH_STB);
* skip 是否占位(MR_DECODE_OK / SKIP_LOWMEM / SKIP_PEAK);
* peak_bytes + tw/th 预估峰值与产出尺寸(日志/测试直接对照实测)。
*
* 判定顺序(M27 核心设计):
* 1. 头部特征满足 M23 后端支持矩阵(JPEG SOF0/1/2、≤3 分量、8bit、N=1..7;
* PNG 非交错/标准压缩;BMP DIB≥40/BI_RGB/8·16·24·32bpp)→ **走后端**:
* 峰值 = 目标图字节 + 后端工作面 + 固定开销,与源分辨率无关 → 不占位(不做内存门控)。
* 2. 否则回退 stb:按 M22 的 stb 真实分配模型算峰值,超预算 → SKIP_PEAK;
* MemAvailable < 6MB → SKIP_LOWMEM。这两道门控只作用于回退路径。
*
* 参数:step 与 meow_img_load_file_step 同义(JPEG 档位提示);box_w/box_h 是阅读器
* 最终显示框;memfree_kb/memavail_kb 读不到时传 -1。out 必须非 NULL。
* 估算口径保证 ≥ 实测(--wrap malloc)且不超过实测 1.5×(见 tuku/test 断言)。 */
#define MR_DECODE_PATH_STB 0
#define MR_DECODE_PATH_BACKEND 1
#define MR_DECODE_OK 0
#define MR_DECODE_SKIP_LOWMEM 1
#define MR_DECODE_SKIP_PEAK 2
typedef struct mr_decode_decision {
int path; /* MR_DECODE_PATH_* */
int skip; /* MR_DECODE_OK / SKIP_* */
int step; /* 实际档位(<=1 按 1) */
int tw, th; /* 预估最终产出尺寸(含阅读器落框精修) */
long long peak_bytes; /* 预估峰值(字节,含固定开销) */
long long budget_bytes; /* 本次预算(字节;SKIP_PEAK 的比较基准) */
} mr_decode_decision;
void reader_estimate_peak(const mr_img_info *in, int step, int box_w, int box_h,
long memfree_kb, long memavail_kb, mr_decode_decision *out);
/* 预算策略(reader_estimate_peak 内部用的就是它):常态 3MB;MemFree 富余时放宽到
* min(12MB, MemFree-4MB);读不到 MemFree(<0)只用常态。暴露给日志/测试,避免复制。 */
long long reader_peak_budget_bytes(long memfree_kb);
/* ============================================================ */
/* 档案册(M11):.cbz / .zip 当一本书 */
/* ------------------------------------------------------------