图库: 门控模型接真缩解后端 + _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

+68 -8
View File
@@ -1,38 +1,98 @@
#!/usr/bin/env python3
"""make_mem_samples.py — 为 test_reader_membudget.c 生成 JPEG 夹具(M22)。
"""make_mem_samples.py — 为 test_reader_membudget.c 生成解码夹具(M22 / M27)。
用法:python3 make_mem_samples.py <输出目录>
依赖:Pillow。没有 Pillow 时测试自动 SKIP JPEG 断言(BMP/PNG 夹具由 C 直接写)。
依赖:Pillow。没有 Pillow 时测试自动 SKIP JPEG 相关断言(BMP/PNG 小夹具由 C 直接写)。
刻意用**结构化图案**而不是噪声:文件极小、解码路径与真实漫画页一致(白底 + 分镜框)。
page.jpg 1000x1500 baseline(1.5MP,真机崩溃页所在档位)
big4.jpg 2000x2000 baseline(4MP,手机照片档)
prog.jpg 1000x1500 progressive(SOF2:验证渐进识别 + 峰值模型)
M27 追加「6 类典型页」与「回退路径」夹具,用来断言门控模型与真缩解后端同口径:
典型(应全部走后端、不占位):
base2000.jpg 2000x3000 baseline
prog2000.jpg 2000x3000 progressive
restart2000.jpg 2000x3000 progressive + restart markers
page1600.png 1600x2400 PNG
page1600.bmp 1600x2400 BMP
mp4.jpg 2320x1740(4MP)
mp12.jpg 4000x3000(12MP)
回退(后端拒绝、按旧 stb 门控占位):
cmyk.jpg 2000x3000 CMYK(4 分量)
adam7.png 1600x2400 Adam7 交错
mono1.bmp 1600x2400 1bpp
M22 旧断言仍用:
page.jpg / big4.jpg / prog.jpg(1000x1500 / 2000x2000 / 1000x1500 progressive)
"""
import os
import struct
import sys
import zlib
from PIL import Image, ImageDraw
def pattern(w, h):
im = Image.new("RGB", (w, h), (248, 247, 244))
def pattern(w, h, mode="RGB"):
im = Image.new(mode, (w, h), (248, 247, 244) if mode == "RGB" else 200)
d = ImageDraw.Draw(im)
for k in range(0, min(w, h) - 200, 70):
d.rectangle([20 + k, 20 + k, 160 + k, 120 + k], outline=(10, 10, 12), width=3)
return im
def chunk(tag, data):
return (struct.pack(">I", len(data)) + tag + data +
struct.pack(">I", zlib.crc32(tag + data) & 0xFFFFFFFF))
def write_adam7(path, img):
"""手写 Adam7 交错 PNG(Pillow 不支持写交错)。RGB8。"""
w, h = img.size
px = img.load()
raw = bytearray()
passes = [(0, 0, 8, 8), (4, 0, 8, 8), (0, 4, 4, 8), (2, 0, 4, 4),
(0, 2, 2, 4), (1, 0, 2, 2), (0, 1, 1, 2)]
for (x0, y0, dx, dy) in passes:
xs = list(range(x0, w, dx))
ys = list(range(y0, h, dy))
if not xs or not ys:
continue
for y in ys:
raw.append(0)
for x in xs:
r, g, b = px[x, y][:3]
raw += bytes((r, g, b))
ihdr = struct.pack(">IIBBBBB", w, h, 8, 2, 0, 0, 1)
data = (b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", ihdr) +
chunk(b"IDAT", zlib.compress(bytes(raw))) + chunk(b"IEND", b""))
with open(path, "wb") as f:
f.write(data)
def main():
if len(sys.argv) != 2:
print(__doc__)
return 2
out = sys.argv[1]
os.makedirs(out, exist_ok=True)
# ---- M22 旧断言夹具 ----
pattern(1000, 1500).save(os.path.join(out, "page.jpg"), "JPEG", quality=60, subsampling=2)
pattern(2000, 2000).save(os.path.join(out, "big4.jpg"), "JPEG", quality=60, subsampling=2)
pattern(1000, 1500).save(os.path.join(out, "prog.jpg"), "JPEG", quality=60, subsampling=2,
progressive=True)
# ---- M27 6 类典型页 ----
big = pattern(2000, 3000)
big.save(os.path.join(out, "base2000.jpg"), "JPEG", quality=60, subsampling=2)
big.save(os.path.join(out, "prog2000.jpg"), "JPEG", quality=60, subsampling=2, progressive=True)
big.save(os.path.join(out, "restart2000.jpg"), "JPEG", quality=60, subsampling=2,
progressive=True, restart_marker_blocks=8)
pattern(1600, 2400).save(os.path.join(out, "page1600.png"), "PNG")
pattern(1600, 2400).save(os.path.join(out, "page1600.bmp"), "BMP")
pattern(2320, 1740).save(os.path.join(out, "mp4.jpg"), "JPEG", quality=60, subsampling=2)
pattern(4000, 3000).save(os.path.join(out, "mp12.jpg"), "JPEG", quality=60, subsampling=2)
# ---- M27 回退路径 ----
big.convert("CMYK").save(os.path.join(out, "cmyk.jpg"), "JPEG", quality=60)
write_adam7(os.path.join(out, "adam7.png"), pattern(1600, 2400))
pattern(1600, 2400).convert("1").save(os.path.join(out, "mono1.bmp"), "BMP")
return 0