feat(ui-migration): PNG 切分/重组与全量重组校验
切分可逆, 故「重组(切分(原图)) 的像素 === 原图像素」是对全部 1006 帧的完全验证, 成本近零。比较解码后的像素缓冲而非文件字节(编码参数不同会让同像素图字节不同)。 verify-frames CLI 对全部 139 张多帧图跑该校验, 任一失败退出码 1。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import { PNG } from 'pngjs';
|
||||
import { frameRect } from './frames.mjs';
|
||||
|
||||
/**
|
||||
* PNG 切分与重组。见 spec §4.2 ②。
|
||||
* 切分是可逆操作,故「重组(切分(原图)) 的像素 === 原图像素」是对全部帧的完全验证。
|
||||
*/
|
||||
|
||||
/** 切分整图 → 帧 Buffer 数组,下标 0 对应帧 1。 */
|
||||
export function splitSheet(sheetBuffer, image) {
|
||||
const sheet = PNG.sync.read(sheetBuffer);
|
||||
const out = [];
|
||||
for (let i = 1; i <= image.frame_all; i++) {
|
||||
const r = frameRect(image, i);
|
||||
const cell = new PNG({ width: r.width, height: r.height });
|
||||
PNG.bitblt(sheet, cell, r.x, r.y, r.width, r.height, 0, 0);
|
||||
out.push(PNG.sync.write(cell));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 把帧按原网格拼回整图。 */
|
||||
export function reassemble(frameBuffers, image) {
|
||||
const sheet = new PNG({ width: image.w * image.w1, height: image.h * image.h1 });
|
||||
frameBuffers.forEach((buf, i) => {
|
||||
const r = frameRect(image, i + 1);
|
||||
const cell = PNG.sync.read(buf);
|
||||
PNG.bitblt(cell, sheet, 0, 0, r.width, r.height, r.x, r.y);
|
||||
});
|
||||
return PNG.sync.write(sheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两张 PNG 的**解码像素数据**。
|
||||
* 不能比较文件字节——编码参数不同会让同像素的图产生不同字节。
|
||||
*/
|
||||
export function pixelsEqual(bufA, bufB) {
|
||||
const a = PNG.sync.read(bufA);
|
||||
const b = PNG.sync.read(bufB);
|
||||
if (a.width !== b.width || a.height !== b.height) return false;
|
||||
return Buffer.compare(a.data, b.data) === 0;
|
||||
}
|
||||
|
||||
/** 对一张多帧图做重组校验。 */
|
||||
export function verifySheet(sheetBuffer, image) {
|
||||
const sheet = PNG.sync.read(sheetBuffer);
|
||||
const expectW = image.w * image.w1;
|
||||
const expectH = image.h * image.h1;
|
||||
if (sheet.width !== expectW || sheet.height !== expectH) {
|
||||
return {
|
||||
ok: false,
|
||||
reason: `尺寸不符: 实际 ${sheet.width}x${sheet.height},按网格应为 ${expectW}x${expectH}`,
|
||||
};
|
||||
}
|
||||
const back = reassemble(splitSheet(sheetBuffer, image), image);
|
||||
if (!pixelsEqual(sheetBuffer, back)) {
|
||||
return { ok: false, reason: '重组后像素与原图不一致(切分逻辑有误)' };
|
||||
}
|
||||
return { ok: true };
|
||||
}
|
||||
Reference in New Issue
Block a user