Files
MeowOS-SDK/tuku/test/make_mem_samples.py
T
AskaEth 11e09af7a4 图库: 修 M27 评审 P1 门控旁路 + 三个 P2(r1)
P1-1:判定为 backend 的页不再无条件放行 + 后端中途失败不再静默回退 stb。
- meow_img_load_file_step_ex 增加 allow_stb_fallback 参数:0 = 只试后端,
  失败返回 NULL(坏图占位),绝不回落 stb。阅读器按 dec.path 传入。
- 旧行为实测:截断 12MP JPEG 18,403kB、3000×3000 24bpp 头-only BMP 36,056kB、
  非法位深 PNG 16,409kB(stb 都"成功"出图)。修复后:
  截断 JPEG 275kB→NULL、头-only BMP 908kB→NULL、非法位深 PNG 门控占位(0 分配)。

P2-1:PNG 支持矩阵补 png_depth_ok 同式(color 2/4/6 仅 8/16;color 3 无 16;
color 0 允许 1/2/4/8/16),不再出现「矩阵说支持、后端拒绝」。
P2-2:后端命中页也保留两道安全阀:MemAvailable<6MB 占位、估算 >12MB 硬上限占位。
P2-3:B8 指标改为「窗口稳定后只 reset 一次」,量整个 30 翻窗口内相对稳态基线的
最大存活增量(淘汰不再掩盖峰值),并打印稳态/驻留/增量。
P2-4:page_source.h 的估算精度口径限定为「后端命中/典型页」。

回归:
- B7c 新增截断 12MP JPEG / 24bpp 头-only BMP / 非法位深 PNG,断言 backend-only
  NULL 且峰值 ≤3MB、或 stb 门控占位;B7 典型页与 B7b 回退矩阵保持全绿。
- test_reader_many_pages 恢复 24bpp 头-only 夹具(1bpp 保留):24bpp 判 failed、
  1bpp 判 skipped;断言 failed=2 / skipped=1。
- run_tests.sh / run_webtoon_tests.sh EXIT=0;sdk/test_meow_img_stream ALL PASS;
  交叉构建(SDK + tuku)零警告。
2026-11-06 11:46:16 +08:00

135 lines
5.6 KiB
Python
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.
#!/usr/bin/env python3
"""make_mem_samples.py — 为 test_reader_membudget.c 生成解码夹具(M22 / M27)。
用法:python3 make_mem_samples.py <输出目录>
依赖:Pillow。没有 Pillow 时测试自动 SKIP JPEG 相关断言(BMP/PNG 小夹具由 C 直接写)。
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, 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")
# ---- M27 r1(P1-1):头部判支持但解码中途失败/后端拒绝的坏文件 ----
# 1) 截断的 12MP JPEG:SOF 仍在(probe/矩阵判 backend),熵段被腰斩 → 后端失败;
# 旧实现会静默回退 stb 并分配 ~18MB。
with open(os.path.join(out, "mp12.jpg"), "rb") as f:
blob = f.read(200000)
with open(os.path.join(out, "trunc2000.jpg"), "wb") as f:
f.write(blob)
# 2) 3000x3000 24bpp BMP,只有 54 字节头(无像素):矩阵判 backend,后端失败;
# 旧实现回退 stb 会 malloc 36MB 并"解出"一张全黑图。
hdr = bytearray(54)
hdr[0:2] = b"BM"
struct.pack_into("<I", hdr, 2, 54) # 谎报文件大小
struct.pack_into("<I", hdr, 10, 54) # 像素数据偏移
struct.pack_into("<I", hdr, 14, 40) # BITMAPINFOHEADER
struct.pack_into("<i", hdr, 18, 3000) # width
struct.pack_into("<i", hdr, 22, 3000) # height
struct.pack_into("<H", hdr, 26, 1) # planes
struct.pack_into("<H", hdr, 28, 24) # bpp
with open(os.path.join(out, "hdr3000.bmp"), "wb") as f:
f.write(hdr)
# 3) 非法位深 PNG(color=2 RGB + depth=1):后端 png_depth_ok 拒绝 → 必须判 stb;
# 旧矩阵只按 color 算位宽 → 误判 backend,失败后回退 stb 分配 ~16MB。
w, h = 1600, 2400
rowbytes = (w * 3 + 7) // 8 # 600 字节/行(depth=1 RGB)
raw = bytearray()
for y in range(h):
raw.append(0)
raw += bytes((x * 7 + y) & 0xFF for x in range(rowbytes))
ihdr = struct.pack(">IIBBBBB", w, h, 1, 2, 0, 0, 0)
data = (b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", ihdr) +
chunk(b"IDAT", zlib.compress(bytes(raw))) + chunk(b"IEND", b""))
with open(os.path.join(out, "baddepth.png"), "wb") as f:
f.write(data)
return 0
if __name__ == "__main__":
sys.exit(main())