/** * 帧矩形与命名。见 spec §0.3、§3。 * 帧号 1 基、行优先——已由读取 00014.png 的网格内容与 11 个按钮的 FrameIndex 对照实证。 */ /** 帧号 → 源矩形。FrameIndex=0 按帧 1 处理(实测仅 2 个对象如此)。 */ export function frameRect(image, frameIndex) { const normalized = frameIndex === 0 ? 1 : frameIndex; const idx = normalized - 1; if (!Number.isInteger(idx) || idx < 0 || idx >= image.frame_all) { throw new RangeError( `帧号非法或越界: ${image.bmp} frameIndex=${frameIndex}(有效范围 1..${image.frame_all},0 视同 1)`, ); } return { x: (idx % image.w) * image.w1, y: Math.floor(idx / image.w) * image.h1, width: image.w1, height: image.h1, }; } /** 补零位数:保证字典序 = 帧序。 */ export function padWidth(frameCount) { return String(frameCount).length; } /** 00014.png + 帧 5/共 12 → 00014_05.png;单帧图原样返回。 */ export function frameFileName(bmp, frameIndex, frameCount) { if (frameCount <= 1) return bmp; const normalized = frameIndex === 0 ? 1 : frameIndex; if (!Number.isInteger(normalized) || normalized <= 0) { throw new RangeError( `帧号非法: ${bmp} frameIndex=${frameIndex}(有效范围 >= 1,0 视同 1)`, ); } const base = bmp.replace(/\.png$/i, ''); const n = String(normalized).padStart(padWidth(frameCount), '0'); return `${base}_${n}.png`; } /** 该图全部帧的文件名,下标 0 对应帧 1。 */ export function frameNames(image) { return Array.from({ length: image.frame_all }, (_, i) => frameFileName(image.bmp, i + 1, image.frame_all)); }