SDK: 图片解码真缩解后端 —— JPEG DCT 域 / PNG 逐行 / BMP 跳行(M23)

stb 的 stbi_load_scaled 是「先整幅解、再重采样」:2000x3000 baseline 峰值 10.07MB、
渐进式 25.84MB、1600x2400 PNG 25.65MB,真机解码余量只有 ~4.2MB。新增解码后端在
解码期只算目标分辨率,峰值只与输出尺寸相关:

- meow_jpeg.c:自写纯 C JPEG 解码(baseline + progressive,Huffman)。按
  ceil(8*maxdim/长边) 选 N 点 DCT 缩解(N=1..7),基函数为「8 点 IDCT 按像素区间
  盒式平均」的 Q12 定点(DC 恒为 F00/8,N 之间亮度一致);系数边解边累加进分量平面,
  不留整幅系数阵列;渐进式跳过精细化扫描(Ah>0)、只解各频带首次扫描 —— 渐进式与
  baseline 内存足迹相同(这是 libjpeg 系列做不到的:它对渐进式必须整幅驻留 JBLOCK)。
  不支持项(4 分量 CMYK/YCCK、算术/无损/分层、12bit 精度)返回 0 回退 stb。
- meow_png_stream.c:libz 逐行 inflate + 反滤波,只把需要的列写进目标行;
  gray/gray+A/RGB/RGBA/调色板,1/2/4/8/16bit(16→8 取高字节);Adam7 等回退 stb。
  BMP 按目标行 seek + 行内跳列(8bpp 调色板/16bpp RGB555/24/32bpp),不读全图。
- meow_ui_img.c:load_file_dim / load_mem_dim 先进后端,失败/不支持回落 stb 旧路径
  (stbi_load_scaled + 精修);输出尺寸与 meow_img_downscale 同公式,对外语义不变。

宿主实测(--wrap malloc,maxdim=720,M23 前后同一夹具):
  2000x3000 baseline     10.07 → 1.86 MiB
  2000x3000 progressive  25.84 → 1.86 MiB
  1600x2400 PNG          25.65 → 1.33 MiB
  1600x2400 BMP          15.97 → 1.32 MiB
  4MP/8MP/12MP JPEG       9.67/13.37/20.12 → 2.30/2.20/2.29 MiB(不随分辨率线性增长)

sdk/test_meow_img_stream.c:峰值断言(≤2.5MiB)、与 stb 整幅+降采样的像素比对
(平均差 4.4-13.7/255,块均值结构相关 ≥0.88)、内存入口逐字节一致、回退矩阵
(CMYK/1bpp BMP/Adam7 PNG)、restart marker、缩解阶扫描 N=4/5/7、坏图容错。
build_sdk.sh 增加 meow_jpeg / meow_png_stream 两个 TU(MIPS32 -Os 零警告)。
体积:目标码 +24.9KB(meow_ui.a +25.5KB),picbrowser 静态二进制 +18.2KB。
This commit is contained in:
AskaEth committed 2026-11-05 21:33:54 +08:00
1 parent 88f3d861b0
commit d84dd949c7
8 files changed
+2320 -15

No files matched your search

+14 -5
View File
@@ -1,7 +1,8 @@
/*
* meow_ui_image.h — SDK 图片层(§4,B1)。
*
* 原 meow_ui.h 拆分的一部分(image 模块)。内部 stb_image(单头文件,静态编入 meow_ui.a)
* 原 meow_ui.h 拆分的一部分(image 模块)。解码后端:M23 真缩解(JPEG DCT 域 / PNG 逐行 /
* BMP 跳行,见 meow_jpeg.h)优先,回退 stb_image(单头文件,静态编入 meow_ui.a)。
* 解 PNG/JPEG/BMP,统一转 RGBA。meow_img 为不透明句柄(前向声明见 meow_ui_types.h)。
*/
#ifndef MEOW_UI_IMAGE_H
@@ -26,10 +27,18 @@ void meow_img_free(meow_img *img);
* App 可用它在加载前判断「图太大」并给用户提示,而不必先撞 OOM。 */
int meow_img_probe_file(const char *path, int *w, int *h, int *channels);
/* 追加(M12)预算解码:解码期受长边约束,返回图最长边 ≤ maxdim。
* JPEG:按 1/2、1/4、1/8 整数步长抽取解码(stbi_load_scaled),整幅原尺寸 RGBA 缓冲从不物化,
* 峰值 ≈ 亮度分量平面 + 缩解输出 + 单行缓冲;随后用 meow_img_downscale 精修到 maxdim。
* PNG/BMP 等 stb 无解码期缩放能力的格式:整幅解码后立即降采样(峰值同旧入口,见 M12 报告)。
/* 追加(M12/M23)预算解码:解码期受长边约束,返回图最长边 ≤ maxdim,尺寸与
* 「meow_img_load_file + meow_img_downscale(maxdim)」逐字节同口径(公式一致)。
*
* 实现(M23 起):先进真缩解后端(内部 meow_jpeg.c / meow_png_stream.c),解码期只算目标分辨率:
* JPEG:DCT 域缩解(每 8×8 块只解出 N×N 像素,N 由 8·maxdim/长边 取「不欠采样」的最小值),
* 渐进式只解到首个精细化扫描(首个 Ah>0)——低频带已完整,不再整幅驻留系数阵列;
* PNG :libz 逐行 inflate + 反滤波,按步长丢行丢列直写目标尺寸(gray/gray+A/RGB/RGBA/
* 调色板/16bit,16bit 取高字节);
* BMP :按目标行 seek 到源行 + 行内跳列(8bpp 调色板 / 16bpp RGB555 / 24bpp / 32bpp)。
* 峰值只与输出尺寸相关,不随源图分辨率/压缩体积线性增长(2000×3000 页 ≤2.5MB 量级)。
* 不支持的特性(CMYK/YCCK 4 分量 JPEG、算术编码、Adam7 交错 PNG、RLE/1-4bpp BMP、几乎等尺寸)
* 自动回退 stb 旧路径(meow_img_load_file + 缩解/降采样),对外语义完全不变。
* 小图(长边 ≤ maxdim)走旧路径,与原 API 完全一致;maxdim<=0 等价旧入口。失败返回 NULL。 */
meow_img *meow_img_load_file_dim(const char *path, int maxdim);
meow_img *meow_img_load_mem_dim(const void *buf, int len, int maxdim);