P1-1(内存安全):mj_huff_build 构造 9 位快表前校验规范码空间 (code + bits[l] > (1<<l) → 表非法),填表时再夹一道 <512 边界;mj_dht 失败 即返回 0 → 后端拒绝 → 回退 stb。此前过订阅 DHT(如「长度 2 的码 100 个」)会把 fast[512] 下标推到 12799,28~220 字节的畸形 JPEG 就栈溢出(ASan 实测 stack-buffer-overflow,非 ASan 直接 SIGSEGV/SIGBUS)。 回归:码长 1..4 × 符号数 3..255 共 1012 组畸形 DHT 全部后端拒绝、无崩溃; 「有效 JPEG + 注入坏 DHT」走拒绝 + 回退(不花屏)。 P1-2(能力):三处扫描循环的区间重同步统一到 mj_boundary,**只在 total == rst (编码器区间长度)时重同步**。原实现一见「over && marker∈D0..D7」就重同步,但熵码 位缓冲有最多 9 位前瞻,over 会在区间真正结束前 1~2 个 MCU 置位——提前重同步会丢掉 bitbuf 里本区间剩余块的有效比特,随即失步(渐进式 DC 扫描每块仅 ~1 bit,前瞻能跨过 整个 MCU)。这正是 Pillow restart_marker_blocks=1/2/3/4/5/8/16/64/200 的渐进式 100% 回退 stb(25.8MiB)的根因。另修 mj_restart 裸扫把 FF 00(数据字节 0xFF 的填充) 里的 00 误当 marker。 回归:9 个渐进式 + restart 夹具全部 BACKEND=1、峰值 1.86MiB(与无 restart 相同)、 与同编码无 restart 文件逐字节一致(mad=0.000);baseline + restart 仍逐字节一致。 附带(评审 P2 两项,改动极小且已验证): - PNG 8bit gray/RGB 的 tRNS 透明接通(8bit 采样值在 tRNS 低字节),与 stb 逐点 alpha 一致; - 拒绝 16bit 量化表(只属 12bit 精度;收下会让 F*qt 在 mj_accim 里 int32 溢出,UB)。 - meow_img_load_file_step 的 JPEG 走真缩解后端(PNG/BMP 暂留 stb:阅读器预算模型仍是 M22「整幅物化」口径,待 M27 同步后放开)。 验证:ASan 构建全 PASS;test_meow_img_stream 111 项全 PASS;test_meow_img_budget / test_meow_img_color 全 PASS;tuku run_tests.sh rc=0(zip 106 / cbz 296,含 membudget)、 run_webtoon_tests.sh rc=0;build_sdk.sh 零警告,MIPS32 rel2 目标码。 峰值不变(修复不增加分配):2000x3000 baseline/progressive 1.86MiB、PNG 1.33MiB。 体积:meow_jpeg.o 17,284B + meow_png_stream.o 8,456B = 25.1KB。
500 lines
19 KiB
C
500 lines
19 KiB
C
/*
|
||
* meow_png_stream.c — M23 真缩解后端(二):PNG 逐行 inflate + BMP 按行跳采样。
|
||
*
|
||
* 为什么不用 stb 的 PNG 路径:stb 先整幅 inflate + 反滤波成 raw,再展开成 RGBA 整幅,
|
||
* 最后才降采样——1600×2400 PNG 实测 +26.2MB。这里改成「按行流式」:
|
||
* - IDAT 边到边 inflate,一次只持有一行(filter 字节 + rowbytes)与上一行;
|
||
* - 反滤波后**只把需要的列**写进目标行(最近邻,与 meow_img_downscale 同口径);
|
||
* - 不需要的行整行丢弃(但仍要反滤波,filter 依赖上一行);
|
||
* - 峰值 ≈ 两行缓冲 + zlib 窗口(32KB) + 目标 RGBA,与源图分辨率无关。
|
||
* 支持:gray / gray+A / RGB / RGBA / 调色板,bit depth 1/2/4/8/16(16bit 取高字节降 8bit),
|
||
* tRNS 透明。不支持 → 返回 0 回退 stb:Adam7 交错、非 0 压缩/滤波方法、异常色型组合。
|
||
*
|
||
* BMP:文件流按目标行直接 seek 到源行(不读全图),行内跳列采样。支持 8bpp 调色板 /
|
||
* 16bpp RGB555 / 24bpp / 32bpp(BI_RGB);RLE、BITFIELDS、1/2/4bpp → 回退 stb。
|
||
*
|
||
* 许可证:MeowOS 自写(MIT,随 SDK 发布)。PNG/zlib 语义参考 RFC 2083 / zlib 手册;
|
||
* BMP 布局参考 stb_image 的解析口径(同为本仓已有依赖,行为对齐)。
|
||
*/
|
||
#include "meow_jpeg.h"
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <stdint.h>
|
||
|
||
/* ---------------- zlib ABI ---------------- */
|
||
#if defined(__has_include)
|
||
# if __has_include(<zlib.h>)
|
||
# include <zlib.h>
|
||
# define MZP_HAVE_H 1
|
||
# endif
|
||
#endif
|
||
#ifndef MZP_HAVE_H
|
||
/* 设备 musl sysroot 没有 zlib.h(meow_font.c 同此口径):按 libz.a 的 ABI 声明最小 API。
|
||
* 指针/unsigned long 交给编译器定尺寸,宿主 64 位与设备 32 位都成立。 */
|
||
typedef unsigned char mzp_Bytef;
|
||
typedef unsigned int mzp_uInt;
|
||
typedef unsigned long mzp_uLong;
|
||
typedef void *mzp_voidpf;
|
||
typedef struct mzp_stream_s {
|
||
mzp_Bytef *next_in; mzp_uInt avail_in; mzp_uLong total_in;
|
||
mzp_Bytef *next_out; mzp_uInt avail_out; mzp_uLong total_out;
|
||
char *msg;
|
||
void *state;
|
||
void *zalloc;
|
||
void *zfree;
|
||
mzp_voidpf opaque;
|
||
int data_type;
|
||
mzp_uLong adler;
|
||
mzp_uLong reserved;
|
||
} mzp_stream;
|
||
extern int inflateInit_(mzp_stream *, const char *, int);
|
||
extern int inflate(mzp_stream *, int);
|
||
extern int inflateEnd(mzp_stream *);
|
||
extern const char *zlibVersion(void);
|
||
# define z_stream mzp_stream
|
||
# define Z_OK 0
|
||
# define Z_STREAM_END 1
|
||
# define Z_NO_FLUSH 0
|
||
#endif
|
||
|
||
/* ---------------- 字节源 ---------------- */
|
||
typedef struct {
|
||
FILE *f;
|
||
const unsigned char *mem;
|
||
size_t memlen, mempos;
|
||
} png_src;
|
||
|
||
static int ps_read(png_src *s, void *dst, size_t n)
|
||
{
|
||
if (s->mem) {
|
||
if (s->mempos + n > s->memlen) return 0;
|
||
memcpy(dst, s->mem + s->mempos, n);
|
||
s->mempos += n;
|
||
return 1;
|
||
}
|
||
return fread(dst, 1, n, s->f) == n;
|
||
}
|
||
|
||
static int ps_skip(png_src *s, size_t n)
|
||
{
|
||
if (s->mem) {
|
||
if (s->mempos + n > s->memlen) return 0;
|
||
s->mempos += n;
|
||
return 1;
|
||
}
|
||
return fseek(s->f, (long)n, SEEK_CUR) == 0;
|
||
}
|
||
|
||
/* ================================================================== */
|
||
/* PNG */
|
||
/* ================================================================== */
|
||
typedef struct {
|
||
png_src src;
|
||
z_stream z;
|
||
int z_ready, z_end;
|
||
unsigned w, h;
|
||
int depth, color;
|
||
int bits; /* 每像素位数 */
|
||
size_t rowbytes;
|
||
unsigned char *line; /* 1 filter 字节 + rowbytes */
|
||
unsigned char *prev; /* 上一行的像素字节 */
|
||
unsigned char *cur; /* 本行的像素字节 */
|
||
size_t need, got;
|
||
unsigned char pal[256][3];
|
||
unsigned char trns_a[256]; /* 调色板透明度 */
|
||
int pal_n;
|
||
int have_trns;
|
||
unsigned trns[3]; /* gray / RGB 的 tRNS 值(已降到 8bit) */
|
||
unsigned char *out;
|
||
int tw, th;
|
||
int next_ty;
|
||
unsigned y;
|
||
} png_ctx;
|
||
|
||
/* 从行里取 px 列的 8bit 通道;返回通道数(写入 o[0..3] 的 RGB(A) 语义由调用方定)。 */
|
||
static void png_get_px(const png_ctx *c, const unsigned char *row, int x, unsigned char *o)
|
||
{
|
||
int d = c->depth;
|
||
if (d < 8) {
|
||
int per = 8 / d; /* 每字节像素数 */
|
||
int byte = x / per;
|
||
int shift = 8 - d * (x % per + 1);
|
||
int v = (row[byte] >> shift) & ((1 << d) - 1);
|
||
if (c->color == 3) { /* 调色板:子字节值即索引 */
|
||
o[0] = c->pal[v][0]; o[1] = c->pal[v][1]; o[2] = c->pal[v][2];
|
||
o[3] = c->trns_a[v];
|
||
} else {
|
||
unsigned char g = (unsigned char)(v * 255 / ((1 << d) - 1));
|
||
o[0] = o[1] = o[2] = g;
|
||
/* tRNS 存的是原始采样值(0..2^d-1),不是展开后的 0..255 */
|
||
o[3] = (c->color == 0 && c->have_trns && (unsigned)v == c->trns[0]) ? 0 : 255;
|
||
}
|
||
return;
|
||
}
|
||
if (d == 8) {
|
||
if (c->color == 0) { /* gray */
|
||
o[0] = o[1] = o[2] = row[x];
|
||
o[3] = (c->have_trns && row[x] == c->trns[0]) ? 0 : 255;
|
||
} else if (c->color == 2) { /* RGB */
|
||
o[0] = row[x * 3]; o[1] = row[x * 3 + 1]; o[2] = row[x * 3 + 2];
|
||
o[3] = (c->have_trns && row[x * 3] == c->trns[0] &&
|
||
row[x * 3 + 1] == c->trns[1] && row[x * 3 + 2] == c->trns[2]) ? 0 : 255;
|
||
} else if (c->color == 3) { /* palette */
|
||
int i = row[x];
|
||
o[0] = c->pal[i][0]; o[1] = c->pal[i][1]; o[2] = c->pal[i][2];
|
||
o[3] = c->trns_a[i];
|
||
} else if (c->color == 4) { /* gray + A */
|
||
o[0] = o[1] = o[2] = row[x * 2];
|
||
o[3] = row[x * 2 + 1];
|
||
} else { /* RGBA */
|
||
o[0] = row[x * 4]; o[1] = row[x * 4 + 1];
|
||
o[2] = row[x * 4 + 2]; o[3] = row[x * 4 + 3];
|
||
}
|
||
return;
|
||
}
|
||
/* 16bit:取高字节 */
|
||
if (c->color == 0) {
|
||
o[0] = o[1] = o[2] = row[x * 2];
|
||
o[3] = (c->have_trns && row[x * 2] == c->trns[0]) ? 0 : 255;
|
||
} else if (c->color == 2) {
|
||
o[0] = row[x * 6]; o[1] = row[x * 6 + 2]; o[2] = row[x * 6 + 4];
|
||
o[3] = (c->have_trns && row[x * 6] == c->trns[0] &&
|
||
row[x * 6 + 2] == c->trns[1] && row[x * 6 + 4] == c->trns[2]) ? 0 : 255;
|
||
} else if (c->color == 4) {
|
||
o[0] = o[1] = o[2] = row[x * 4];
|
||
o[3] = row[x * 4 + 2];
|
||
} else {
|
||
o[0] = row[x * 8]; o[1] = row[x * 8 + 2];
|
||
o[2] = row[x * 8 + 4]; o[3] = row[x * 8 + 6];
|
||
}
|
||
}
|
||
|
||
/* 一行反滤波完成后:按需写目标行。 */
|
||
static int png_emit_row(png_ctx *c)
|
||
{
|
||
unsigned char ft = c->line[0];
|
||
const unsigned char *src = c->line + 1;
|
||
unsigned char *dst = c->cur;
|
||
size_t x, bpp = (size_t)((c->bits + 7) / 8);
|
||
if (bpp < 1) bpp = 1;
|
||
if (ft > 4) return 0;
|
||
for (x = 0; x < c->rowbytes; x++) {
|
||
unsigned char left = (x >= bpp) ? dst[x - bpp] : 0;
|
||
unsigned char up = c->prev ? c->prev[x] : 0;
|
||
unsigned char ul = (c->prev && x >= bpp) ? c->prev[x - bpp] : 0;
|
||
unsigned char v = src[x];
|
||
switch (ft) {
|
||
case 0: dst[x] = v; break;
|
||
case 1: dst[x] = (unsigned char)(v + left); break;
|
||
case 2: dst[x] = (unsigned char)(v + up); break;
|
||
case 3: dst[x] = (unsigned char)(v + (((unsigned)left + (unsigned)up) >> 1)); break;
|
||
default: {
|
||
int p = (int)left + (int)up - (int)ul;
|
||
int pa = p - (int)left; if (pa < 0) pa = -pa;
|
||
int pb = p - (int)up; if (pb < 0) pb = -pb;
|
||
int pc = p - (int)ul; if (pc < 0) pc = -pc;
|
||
unsigned char pred = (pa <= pb && pa <= pc) ? left : (pb <= pc ? up : ul);
|
||
dst[x] = (unsigned char)(v + pred);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (c->next_ty < c->th) {
|
||
long sy = (long)c->next_ty * (long)c->h / c->th;
|
||
if (sy == (long)c->y) {
|
||
unsigned char *o = c->out + (size_t)c->next_ty * c->tw * 4;
|
||
int tx;
|
||
for (tx = 0; tx < c->tw; tx++) {
|
||
int sx = (int)((long)tx * (long)c->w / c->tw);
|
||
unsigned char px[4];
|
||
png_get_px(c, dst, sx, px);
|
||
o[tx * 4] = px[0]; o[tx * 4 + 1] = px[1];
|
||
o[tx * 4 + 2] = px[2]; o[tx * 4 + 3] = px[3];
|
||
}
|
||
c->next_ty++;
|
||
}
|
||
}
|
||
/* 交换 prev/cur(prev 直接成为反滤波基准) */
|
||
{
|
||
unsigned char *t = c->prev;
|
||
c->prev = c->cur;
|
||
c->cur = t;
|
||
}
|
||
c->y++;
|
||
return 1;
|
||
}
|
||
|
||
#define PNG_NEED_MORE 2
|
||
|
||
static int png_pump(png_ctx *c)
|
||
{
|
||
while (c->got < c->need) {
|
||
int ret;
|
||
c->z.next_out = c->line + c->got;
|
||
c->z.avail_out = (unsigned)(c->need - c->got);
|
||
ret = inflate(&c->z, Z_NO_FLUSH);
|
||
c->got = c->need - c->z.avail_out;
|
||
if (ret == Z_STREAM_END) c->z_end = 1;
|
||
else if (ret != Z_OK) return 0;
|
||
if (c->got < c->need && (c->z.avail_in == 0 || c->z_end))
|
||
return c->z_end ? 0 : PNG_NEED_MORE;
|
||
}
|
||
if (!png_emit_row(c)) return 0;
|
||
c->got = 0;
|
||
return 1;
|
||
}
|
||
|
||
static int png_bits_of(int color, int depth)
|
||
{
|
||
switch (color) {
|
||
case 0: return depth; /* gray */
|
||
case 2: return depth * 3; /* rgb */
|
||
case 3: return depth; /* palette */
|
||
case 4: return depth * 2; /* gray + a */
|
||
case 6: return depth * 4; /* rgba */
|
||
default: return 0;
|
||
}
|
||
}
|
||
|
||
static int png_depth_ok(int color, int depth)
|
||
{
|
||
if (color == 0) return depth == 1 || depth == 2 || depth == 4 || depth == 8 || depth == 16;
|
||
if (color == 3) return depth == 1 || depth == 2 || depth == 4 || depth == 8;
|
||
if (color == 2 || color == 4 || color == 6) return depth == 8 || depth == 16;
|
||
return 0;
|
||
}
|
||
|
||
int meow_png_decode_stream(FILE *f, const unsigned char *mem, size_t len,
|
||
int maxdim, int *tw, int *th, unsigned char **rgba)
|
||
{
|
||
png_ctx c;
|
||
unsigned char sig[8], hdr[8];
|
||
unsigned char ibuf[4096];
|
||
int have_ihdr = 0, i, ok = 0;
|
||
|
||
if (maxdim <= 0) return 0;
|
||
memset(&c, 0, sizeof c);
|
||
c.src.f = f;
|
||
c.src.mem = mem;
|
||
c.src.memlen = len;
|
||
if (!ps_read(&c.src, sig, 8)) return 0;
|
||
if (memcmp(sig, "\x89PNG\r\n\x1a\n", 8) != 0) return 0;
|
||
|
||
while (ps_read(&c.src, hdr, 8)) {
|
||
unsigned clen = ((unsigned)hdr[0] << 24) | ((unsigned)hdr[1] << 16) |
|
||
((unsigned)hdr[2] << 8) | hdr[3];
|
||
const unsigned char *type = hdr + 4;
|
||
if (clen > 0x7FFFFFFFu) return 0;
|
||
|
||
if (memcmp(type, "IHDR", 4) == 0) {
|
||
unsigned char d[13];
|
||
if (clen < 13 || have_ihdr) return 0;
|
||
if (!ps_read(&c.src, d, 13)) return 0;
|
||
c.w = ((unsigned)d[0] << 24) | ((unsigned)d[1] << 16) | ((unsigned)d[2] << 8) | d[3];
|
||
c.h = ((unsigned)d[4] << 24) | ((unsigned)d[5] << 16) | ((unsigned)d[6] << 8) | d[7];
|
||
c.depth = d[8];
|
||
c.color = d[9];
|
||
if (d[10] != 0 || d[11] != 0 || d[12] != 0) return 0; /* 只支持非交错 + 标准压缩/滤波 */
|
||
if (c.w == 0 || c.h == 0 || c.w > 65535u || c.h > 65535u) return 0;
|
||
if (!png_depth_ok(c.color, c.depth)) return 0;
|
||
c.bits = png_bits_of(c.color, c.depth);
|
||
c.rowbytes = ((size_t)c.w * c.bits + 7) / 8;
|
||
if (c.rowbytes == 0) return 0;
|
||
/* 小图不动:与 meow_img_load_file_dim 的「旧入口」判定一致 */
|
||
if ((long)(c.w > c.h ? c.w : c.h) <= maxdim) return 0;
|
||
if (clen > 13 && !ps_skip(&c.src, clen - 13)) return 0;
|
||
have_ihdr = 1;
|
||
c.need = c.rowbytes + 1;
|
||
c.line = (unsigned char *)malloc(c.need);
|
||
c.prev = (unsigned char *)malloc(c.rowbytes);
|
||
c.cur = (unsigned char *)malloc(c.rowbytes);
|
||
if (!c.line || !c.prev || !c.cur) goto done;
|
||
memset(c.prev, 0, c.rowbytes);
|
||
memset(c.cur, 0, c.rowbytes);
|
||
for (i = 0; i < 256; i++) c.trns_a[i] = 255;
|
||
meow_img_target_dims((int)c.w, (int)c.h, maxdim, &c.tw, &c.th);
|
||
c.out = (unsigned char *)malloc((size_t)c.tw * c.th * 4);
|
||
if (!c.out) goto done;
|
||
memset(c.out, 0, (size_t)c.tw * c.th * 4);
|
||
memset(&c.z, 0, sizeof c.z);
|
||
if (inflateInit_(&c.z, zlibVersion(), (int)sizeof c.z) != Z_OK) goto done;
|
||
c.z_ready = 1;
|
||
} else if (memcmp(type, "PLTE", 4) == 0) {
|
||
unsigned char d[256 * 3];
|
||
if (clen > sizeof d || clen % 3) return 0;
|
||
if (!ps_read(&c.src, d, clen)) return 0;
|
||
c.pal_n = (int)(clen / 3);
|
||
memcpy(c.pal, d, clen);
|
||
} else if (memcmp(type, "tRNS", 4) == 0) {
|
||
unsigned char d[256];
|
||
if (clen > sizeof d) return 0;
|
||
if (!ps_read(&c.src, d, clen)) return 0;
|
||
c.have_trns = 1;
|
||
if (c.color == 3) {
|
||
for (i = 0; i < (int)clen && i < 256; i++) c.trns_a[i] = d[i];
|
||
} else if (c.color == 0 && clen >= 2) {
|
||
/* 8bit/低位深:采样值存在低字节;16bit:与像素一样取高字节比较 */
|
||
c.trns[0] = (c.depth == 16) ? d[0] : d[1];
|
||
} else if (c.color == 2 && clen >= 6) {
|
||
if (c.depth == 16) {
|
||
c.trns[0] = d[0]; c.trns[1] = d[2]; c.trns[2] = d[4];
|
||
} else {
|
||
c.trns[0] = d[1]; c.trns[1] = d[3]; c.trns[2] = d[5];
|
||
}
|
||
}
|
||
} else if (memcmp(type, "IDAT", 4) == 0) {
|
||
unsigned remain = clen;
|
||
if (!have_ihdr || !c.out) return 0;
|
||
while (remain) {
|
||
unsigned n = remain > sizeof ibuf ? (unsigned)sizeof ibuf : remain;
|
||
if (!ps_read(&c.src, ibuf, n)) goto done;
|
||
remain -= n;
|
||
c.z.next_in = ibuf;
|
||
c.z.avail_in = n;
|
||
while (c.z.avail_in && c.next_ty < c.th) {
|
||
int r = png_pump(&c);
|
||
if (r == 0) goto done;
|
||
if (r == PNG_NEED_MORE) break;
|
||
}
|
||
}
|
||
} else if (memcmp(type, "IEND", 4) == 0) {
|
||
break;
|
||
} else {
|
||
if (clen && !ps_skip(&c.src, clen)) return 0;
|
||
}
|
||
/* 跳过 CRC */
|
||
{
|
||
unsigned char crc[4];
|
||
if (!ps_read(&c.src, crc, 4)) break;
|
||
}
|
||
if (c.out && c.next_ty >= c.th) break; /* 目标行都拿到了,不必读完 */
|
||
}
|
||
|
||
if (!have_ihdr || !c.out || c.next_ty < c.th) goto done;
|
||
if (c.color == 3 && c.pal_n <= 0) goto done;
|
||
*tw = c.tw;
|
||
*th = c.th;
|
||
*rgba = c.out;
|
||
c.out = NULL;
|
||
ok = 1;
|
||
done:
|
||
if (c.z_ready) inflateEnd(&c.z);
|
||
free(c.line);
|
||
free(c.prev);
|
||
free(c.cur);
|
||
free(c.out);
|
||
return ok;
|
||
}
|
||
|
||
/* ================================================================== */
|
||
/* BMP */
|
||
/* ================================================================== */
|
||
static unsigned le16(const unsigned char *p) { return (unsigned)p[0] | ((unsigned)p[1] << 8); }
|
||
static unsigned le32(const unsigned char *p)
|
||
{
|
||
return (unsigned)p[0] | ((unsigned)p[1] << 8) | ((unsigned)p[2] << 16) | ((unsigned)p[3] << 24);
|
||
}
|
||
|
||
int meow_bmp_decode_stream(FILE *f, const unsigned char *mem, size_t len,
|
||
int maxdim, int *tw, int *th, unsigned char **rgba)
|
||
{
|
||
png_src src;
|
||
unsigned char fh[14], dh[4];
|
||
unsigned char dib[1024];
|
||
unsigned dibsize, dataoff, bpp, compression, colors;
|
||
long w, h, rowsize;
|
||
int topdown, i;
|
||
unsigned char pal[256][3];
|
||
unsigned char *row = NULL, *out = NULL;
|
||
int tw_ = 0, th_ = 0, ok = 0;
|
||
|
||
if (maxdim <= 0) return 0;
|
||
memset(&src, 0, sizeof src);
|
||
src.f = f;
|
||
src.mem = mem;
|
||
src.memlen = len;
|
||
if (!ps_read(&src, fh, 14)) return 0;
|
||
if (fh[0] != 'B' || fh[1] != 'M') return 0;
|
||
dataoff = le32(fh + 10);
|
||
if (!ps_read(&src, dh, 4)) return 0;
|
||
dibsize = le32(dh);
|
||
if (dibsize < 40 || dibsize > sizeof dib) return 0; /* BITMAPCOREHEADER/OS2 → stb */
|
||
memcpy(dib, dh, 4);
|
||
if (!ps_read(&src, dib + 4, dibsize - 4)) return 0;
|
||
w = (long)(int)le32(dib + 4);
|
||
h = (long)(int)le32(dib + 8);
|
||
bpp = le16(dib + 14);
|
||
compression = le32(dib + 16);
|
||
colors = (dibsize >= 36) ? le32(dib + 32) : 0;
|
||
if (compression != 0) return 0; /* RLE/BITFIELDS → stb */
|
||
if (w <= 0 || h == 0) return 0;
|
||
if (bpp != 8 && bpp != 16 && bpp != 24 && bpp != 32) return 0;
|
||
topdown = h < 0;
|
||
if (h < 0) h = -h;
|
||
if ((long)(w > h ? w : h) <= maxdim) return 0; /* 小图走旧入口 */
|
||
|
||
if (bpp <= 8) {
|
||
unsigned n = colors ? colors : 256;
|
||
unsigned char e[4];
|
||
if (n > 256) n = 256;
|
||
for (i = 0; (unsigned)i < n; i++) {
|
||
if (!ps_read(&src, e, 4)) return 0;
|
||
pal[i][0] = e[2]; pal[i][1] = e[1]; pal[i][2] = e[0];
|
||
}
|
||
for (; i < 256; i++) pal[i][0] = pal[i][1] = pal[i][2] = 0;
|
||
}
|
||
|
||
rowsize = (w * bpp + 31) / 32 * 4;
|
||
meow_img_target_dims((int)w, (int)h, maxdim, &tw_, &th_);
|
||
row = (unsigned char *)malloc((size_t)rowsize);
|
||
out = (unsigned char *)malloc((size_t)tw_ * th_ * 4);
|
||
if (!row || !out) goto done;
|
||
|
||
for (int ty = 0; ty < th_; ty++) {
|
||
int sy = (int)((long)ty * h / th_);
|
||
long file_row = topdown ? sy : (h - 1 - sy);
|
||
long off = (long)dataoff + file_row * rowsize;
|
||
unsigned char *o = out + (size_t)ty * tw_ * 4;
|
||
if (src.mem) {
|
||
if (off < 0 || (size_t)off + (size_t)rowsize > src.memlen) goto done;
|
||
memcpy(row, src.mem + off, (size_t)rowsize);
|
||
} else {
|
||
if (fseek(src.f, off, SEEK_SET) != 0) goto done;
|
||
if (fread(row, 1, (size_t)rowsize, src.f) != (size_t)rowsize) goto done;
|
||
}
|
||
for (int tx = 0; tx < tw_; tx++) {
|
||
int sx = (int)((long)tx * w / tw_);
|
||
unsigned char *p = o + (size_t)tx * 4;
|
||
if (bpp == 24) {
|
||
const unsigned char *q = row + sx * 3;
|
||
p[0] = q[2]; p[1] = q[1]; p[2] = q[0]; p[3] = 255;
|
||
} else if (bpp == 32) {
|
||
const unsigned char *q = row + sx * 4;
|
||
p[0] = q[2]; p[1] = q[1]; p[2] = q[0]; p[3] = 255; /* BI_RGB:alpha 不作为语义 */
|
||
} else if (bpp == 16) {
|
||
unsigned v = le16(row + sx * 2);
|
||
unsigned r = (v >> 10) & 31, g = (v >> 5) & 31, b = v & 31;
|
||
p[0] = (unsigned char)((r << 3) | (r >> 2));
|
||
p[1] = (unsigned char)((g << 3) | (g >> 2));
|
||
p[2] = (unsigned char)((b << 3) | (b >> 2));
|
||
p[3] = 255;
|
||
} else {
|
||
unsigned idx = row[sx];
|
||
p[0] = pal[idx & 255][0]; p[1] = pal[idx & 255][1];
|
||
p[2] = pal[idx & 255][2]; p[3] = 255;
|
||
}
|
||
}
|
||
}
|
||
*tw = tw_;
|
||
*th = th_;
|
||
*rgba = out;
|
||
out = NULL;
|
||
ok = 1;
|
||
done:
|
||
free(row);
|
||
free(out);
|
||
return ok;
|
||
}
|