- 页槽从 RGBA 改成「已落框的 RGB565 位图」(2B/px):真机 480×853 页 1599KB → 800KB, 两页常驻 3.2MB → 1.6MB。解码仍在 worker 里产出 RGBA,当场转 565 并释放 RGBA。 - SDK 新增 565 原语(meow_ui_draw.h / meow_ui_gfx.c):meow_gfx_blit_rgb565 命中页每行 一次 memcpy(单次拷贝、无每帧重采样),缩放兜底走 565 最近邻。量化与 back_buf 用同一个 rgb565(),565 屏上落下的像素与旧 RGBA 路径逐像素一致(不引入色偏/新增色带)。 - 预算按 565 重算:单页像素上限沿用旧 3MB@RGBA 的同几何口径(=1.5MB@565), MR_RESIDENT_CAP = 3 槽 + 1 张在途 = 3.2MB;reader_estimate_peak 新增 convert_bytes (落槽暂存 2B/px)与 peak_bytes 一起参与门控,避免实测超预算。 - 结果队列(已解码、未落槽)字节计入总预算(pending_bytes),不再「偷吃」最多 2 页。 - CBZ meta() 改读条目 4KB 前缀(mz_read_prefix;deflate 解到 4KB 即停,输入缓冲走栈), 500 页排版不再触发 500 次全量解压。 - 测试:B3/B8 的 565 常驻与在途预算断言、CBZ 500 页 stored/deflate 前后对比、 P12 565 与 RGBA 路径的逐像素颜色对比(量化误差/RMSE/灰阶梯)。
96 lines
5.1 KiB
C
96 lines
5.1 KiB
C
/* zip.h — 纯 C zip 容器读取器(只依赖 libz 的 inflate/crc32 + stdio)。
|
||
*
|
||
* 定位:tuku 漫画阅读器的 CBZ/zip 容器层——开包、列目录、按需解压单条目。
|
||
* 不依赖 meow_ui.h / SDK / tuku 其它逻辑,宿主可裸测(见 tuku/test/test_zip.c)。
|
||
*
|
||
* 用法:
|
||
* mz_archive *z = NULL;
|
||
* int rc = mz_open("book.cbz", &z);
|
||
* if (rc != MZ_OK) { fprintf(stderr, "%s\n", mz_strerror(rc)); return; }
|
||
* for (int i = 0; i < mz_count(z); i++) {
|
||
* const mz_entry *e = mz_entry_at(z, i);
|
||
* if (mz_entry_is_dir(e) || !is_image_name(e->name)) continue;
|
||
* size_t n = 0;
|
||
* void *px = NULL;
|
||
* if (mz_read_alloc(z, e, &px, &n) == MZ_OK) { decode(px, n); free(px); }
|
||
* }
|
||
* mz_close(z);
|
||
*
|
||
* 线程约束:单个 mz_archive 不是线程安全的(共享 FILE* 位置与条目表只读);
|
||
* 多线程请各自 mz_open 自己的 archive,或由调用方加锁。模块内无全局可变状态。
|
||
*
|
||
* 内存:mz_open 只把 central directory 的条目元数据(条目表 + 名字副本)读进内存,
|
||
* 不展开条目数据。mz_read 解压到调用方缓冲(缓冲需 >= 该条目 uncomp_size);
|
||
* mz_read_alloc 只为单个条目分配一次。
|
||
*
|
||
* 安全:所有偏移/长度都按文件实际大小校验;越界、截断、坏 magic、坏 CRC 一律
|
||
* 返回错误码而不崩溃。条目名按原始字节透传(通常是 UTF-8,不做规范化/解码)。
|
||
* 名字可能是 ".."、绝对路径或反斜杠——调用方若把它落到文件系统,必须自行拒绝
|
||
* 越界路径。加密、zip64、分卷压缩包明确返回 MZ_ERR_UNSUPPORTED。
|
||
*
|
||
* 错误码均为负值;MZ_OK 为 0。mz_strerror 对所有已知码返回静态字符串。
|
||
*/
|
||
#ifndef TUKU_LOGIC_ZIP_ZIP_H
|
||
#define TUKU_LOGIC_ZIP_ZIP_H
|
||
|
||
#include <stddef.h>
|
||
#include <stdint.h>
|
||
|
||
enum {
|
||
MZ_OK = 0,
|
||
MZ_ERR_IO = -1, /* 文件读写失败(打开/seek/read) */
|
||
MZ_ERR_FORMAT = -2, /* 结构非法:坏 magic、central directory 不一致、流损坏 */
|
||
MZ_ERR_TRUNCATED = -3, /* 声明的偏移/长度越过文件尾(含局部头越界) */
|
||
MZ_ERR_UNSUPPORTED = -4, /* 加密 / zip64 / 分卷 / 非 stored-deflate 压缩方式 */
|
||
MZ_ERR_BUF = -5, /* 调用方缓冲小于条目 uncomp_size */
|
||
MZ_ERR_MEM = -6, /* 内存分配失败 */
|
||
MZ_ERR_ARG = -7, /* 空指针 / 越界 index 等调用错误 */
|
||
MZ_ERR_CRC = -8 /* 解压结果 CRC-32 与 central directory 不符 */
|
||
};
|
||
|
||
typedef struct mz_entry {
|
||
const char *name; /* NUL 结尾;内容为 zip 内原始字节,生命周期同 archive */
|
||
uint32_t name_len; /* 原始名字字节数(不含结尾 NUL,name 中可能有内嵌 NUL) */
|
||
uint16_t method; /* 0=stored, 8=deflate;其它值在 mz_read 时返回 UNSUPPORTED */
|
||
uint16_t flags; /* zip general purpose flags(bit0=加密,bit11=UTF-8 名) */
|
||
uint32_t crc; /* 条目内容 CRC-32 */
|
||
uint64_t comp_size; /* 压缩后字节数(archive 内) */
|
||
uint64_t uncomp_size; /* 原始字节数 */
|
||
uint64_t local_offset; /* local file header 在 archive 内的偏移 */
|
||
} mz_entry;
|
||
|
||
typedef struct mz_archive mz_archive;
|
||
|
||
/* 打开 zip。成功时 *out 为新 archive(mz_close 释放);失败时 *out == NULL。 */
|
||
int mz_open(const char *path, mz_archive **out);
|
||
void mz_close(mz_archive *z);
|
||
|
||
int mz_count(const mz_archive *z);
|
||
const mz_entry *mz_entry_at(const mz_archive *z, int index); /* 越界返回 NULL */
|
||
const mz_entry *mz_find(const mz_archive *z, const char *name); /* 精确 strcmp,找不到 NULL */
|
||
|
||
/* 目录条目(名字以 '/' 结尾)。注意 zip 常见但没有强制要求目录条目存在。 */
|
||
int mz_entry_is_dir(const mz_entry *e);
|
||
|
||
/* 解压单条目到 buf。buf_size 必须 >= e->uncomp_size,否则 MZ_ERR_BUF。
|
||
* 成功时 *out_size = uncomp_size(可为 0)。out_size 可为 NULL。
|
||
* 校验 CRC 与声明尺寸;失败时 buf 内容不可用。 */
|
||
int mz_read(mz_archive *z, const mz_entry *e, void *buf, size_t buf_size, size_t *out_size);
|
||
|
||
/* mz_read 的便捷版:malloc 恰好 uncomp_size 字节(0 长度条目分配 1 字节)。
|
||
* 成功时 *out_buf 由调用方 free。 */
|
||
int mz_read_alloc(mz_archive *z, const mz_entry *e, void **out_buf, size_t *out_size);
|
||
|
||
/* 只解压条目**前缀**到 buf(buf_size = 容量):stored 直接读前 buf_size 字节,
|
||
* deflate 在 inflate 出 buf_size 字节后即停(不整条目解压)。
|
||
* *out_size = 实际写入字节数(= min(buf_size, uncomp_size))。
|
||
* 用途:CBZ 排版只看图片头取宽高(PNG IHDR 33 字节 / JPEG SOF / BMP 头都在前 4KB)。
|
||
* 注意:没有读完整条目,因此**不校验整段 CRC**(只校验结构/长度),不要用它取内容。
|
||
* 出错语义与 mz_read 相同(越界/坏流/加密/zip64 一律返回错误码)。 */
|
||
int mz_read_prefix(mz_archive *z, const mz_entry *e, void *buf, size_t buf_size,
|
||
size_t *out_size);
|
||
|
||
const char *mz_strerror(int err);
|
||
|
||
#endif /* TUKU_LOGIC_ZIP_ZIP_H */
|