-
Notifications
You must be signed in to change notification settings - Fork 39
Split size into separate width and height values #270
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,7 +16,8 @@ const BASE = path.resolve(__dirname, "../.."); | |
| const ANIM_ROOT = path.join(BASE, "extraction/raw/animations"); | ||
| const OUTPUT_ROOT = path.join(BASE, "backend/static/images/renders"); | ||
|
|
||
| const OUTPUT_SIZE = 512; | ||
| const OUTPUT_WIDTH = 512; | ||
| const OUTPUT_HEIGHT = 512; | ||
| const IDLE_NAMES = ["idle_loop", "idle", "Idle_loop", "Idle", "rest_idle", "rest_loop", "loop", "animation"]; | ||
| const SHADOW_NAMES = ["shadow", "shadow2", "shadow_v2", "ground", "ground_shadow"]; | ||
| const HIDDEN_SLOTS = ["smoketex", "smoke_tex", "smokeplacholder", "smoke_placeholder", "megatail", "megablade"]; | ||
|
|
@@ -36,7 +37,7 @@ function findAllSkels(dir) { | |
| const spineCorePath = path.join(__dirname, "node_modules/@esotericsoftware/spine-webgl/dist/iife/spine-webgl.js"); | ||
| const spineCoreCode = fs.readFileSync(spineCorePath, "utf-8"); | ||
|
|
||
| async function renderSkel(page, skelPath, outPath, outputSize) { | ||
| async function renderSkel(page, skelPath, outPath, outputWidth, outputHeight) { | ||
| const dir = path.dirname(skelPath); | ||
| const skelName = path.basename(skelPath, ".skel"); | ||
| const atlasPath = path.join(dir, skelName + ".atlas"); | ||
|
|
@@ -71,16 +72,16 @@ async function renderSkel(page, skelPath, outPath, outputSize) { | |
| }); | ||
|
|
||
| const result = await page.evaluate(async (params) => { | ||
| const { skelB64, atlasB64, textureData, outputSize, idleNames, shadowNames, hiddenSlots, spineCoreCode } = params; | ||
| const { skelB64, atlasB64, textureData, outputWidth, outputHeight, idleNames, shadowNames, hiddenSlots, spineCoreCode } = params; | ||
|
|
||
| if (!window.spine) { | ||
| eval(spineCoreCode.replace(/^"use strict";\s*var spine\s*=/, "window.spine =")); | ||
| } | ||
| const spine = window.spine; | ||
|
|
||
| const canvas = document.createElement("canvas"); | ||
| canvas.width = outputSize; | ||
| canvas.height = outputSize; | ||
| canvas.width = outputWidth; | ||
| canvas.height = outputHeight; | ||
| document.body.appendChild(canvas); | ||
|
|
||
| const gl = canvas.getContext("webgl2", { alpha: true, premultipliedAlpha: false, preserveDrawingBuffer: true }) | ||
|
|
@@ -175,17 +176,17 @@ async function renderSkel(page, skelPath, outPath, outputSize) { | |
| if (!isFinite(minX)) return { error: "no bounds" }; | ||
|
|
||
| const sw = maxX - minX, sh = maxY - minY; | ||
| const padding = outputSize * 0.04; | ||
| const avail = outputSize - padding * 2; | ||
| const padding = outputWidth * 0.04; | ||
| const avail = outputWidth - padding * 2; | ||
| const scale = Math.min(avail / sw, avail / sh); | ||
| const cx = (minX + maxX) / 2, cy = (minY + maxY) / 2; | ||
|
|
||
| mvp.ortho2d( | ||
| cx - outputSize / (2 * scale), cy - outputSize / (2 * scale), | ||
| outputSize / scale, outputSize / scale | ||
| cx - outputWidth / (2 * scale), cy - outputHeight / (2 * scale), | ||
| outputWidth / scale, outputHeight / scale | ||
| ); | ||
|
|
||
| gl.viewport(0, 0, outputSize, outputSize); | ||
| gl.viewport(0, 0, outputWidth, outputWidth); | ||
| gl.clearColor(0, 0, 0, 0); | ||
| gl.clear(gl.COLOR_BUFFER_BIT); | ||
| gl.enable(gl.BLEND); | ||
|
|
@@ -210,22 +211,22 @@ async function renderSkel(page, skelPath, outPath, outputSize) { | |
| batcher.end(); | ||
| shader.unbind(); | ||
|
|
||
| const pixels = new Uint8Array(outputSize * outputSize * 4); | ||
| gl.readPixels(0, 0, outputSize, outputSize, gl.RGBA, gl.UNSIGNED_BYTE, pixels); | ||
| const pixels = new Uint8Array(outputWidth * outputHeight * 4); | ||
| gl.readPixels(0, 0, outputWidth, outputHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels); | ||
|
|
||
| // Flip vertically | ||
| const flipped = new Uint8Array(pixels.length); | ||
| const rowSize = outputSize * 4; | ||
| for (let y = 0; y < outputSize; y++) { | ||
| flipped.set(pixels.subarray((outputSize - 1 - y) * rowSize, (outputSize - y) * rowSize), y * rowSize); | ||
| const rowSize = outputWidth * 4; | ||
| for (let y = 0; y < outputWidth; y++) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this and line R221 should be outputHeight, I think you're flipping y loop here? |
||
| flipped.set(pixels.subarray((outputWidth - 1 - y) * rowSize, (outputWidth - y) * rowSize), y * rowSize); | ||
| } | ||
|
|
||
| // Check if anything was actually rendered | ||
| let nonTransparent = 0; | ||
| for (let i = 3; i < flipped.length; i += 4) { | ||
| if (flipped[i] > 0) nonTransparent++; | ||
| } | ||
| if (nonTransparent < outputSize * outputSize * 0.001) { | ||
| if (nonTransparent < outputWidth * outputWidth * 0.001) { | ||
| return { error: "no bounds (blank render)" }; | ||
| } | ||
|
|
||
|
|
@@ -235,7 +236,7 @@ async function renderSkel(page, skelPath, outPath, outputSize) { | |
| size: `${sw.toFixed(0)}x${sh.toFixed(0)}`, | ||
| }; | ||
| }, { | ||
| skelB64, atlasB64, textureData, outputSize, | ||
| skelB64, atlasB64, textureData, outputWidth, outputHeight, | ||
| idleNames: IDLE_NAMES, shadowNames: SHADOW_NAMES, hiddenSlots: HIDDEN_SLOTS, spineCoreCode, | ||
| }); | ||
|
|
||
|
|
@@ -245,17 +246,17 @@ async function renderSkel(page, skelPath, outPath, outputSize) { | |
| fs.mkdirSync(path.dirname(outPath), { recursive: true }); | ||
|
|
||
| // Write PNG via node-canvas | ||
| const pngCanvas = createCanvas(outputSize, outputSize); | ||
| const pngCanvas = createCanvas(outputWidth, outputHeight); | ||
| const pngCtx = pngCanvas.getContext("2d"); | ||
| const imgData = pngCtx.createImageData(outputSize, outputSize); | ||
| const imgData = pngCtx.createImageData(outputWidth, outputHeight); | ||
| imgData.data.set(rawBuffer); | ||
| pngCtx.putImageData(imgData, 0, 0); | ||
| fs.writeFileSync(outPath, pngCanvas.toBuffer("image/png")); | ||
|
|
||
| // Write WebP via sharp | ||
| const webpPath = outPath.replace(/\.png$/, ".webp"); | ||
| const webpBuffer = await sharp(rawBuffer, { | ||
| raw: { width: outputSize, height: outputSize, channels: 4 }, | ||
| raw: { width: outputWidth, height: outputHeight, channels: 4 }, | ||
| }).webp({ quality: 90 }).toBuffer(); | ||
| fs.writeFileSync(webpPath, webpBuffer); | ||
|
|
||
|
|
@@ -291,7 +292,7 @@ async function main() { | |
| const outPath = path.join(OUTPUT_ROOT, relDir, skelName + ".png"); | ||
| const label = path.join(relDir, skelName); | ||
|
|
||
| const result = await renderSkel(page, skelPath, outPath, OUTPUT_SIZE); | ||
| const result = await renderSkel(page, skelPath, outPath, OUTPUT_WIDTH, OUTPUT_HEIGHT); | ||
| if (result.status === "ok") { | ||
| console.log(` OK ${label} (${result.size})`); | ||
| ok++; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,7 +53,8 @@ const HIDDEN_SLOTS = [ | |
| async function main() { | ||
| const skelDir = path.resolve(process.argv[2] || ""); | ||
| const outputPath = path.resolve(process.argv[3] || "output.gif"); | ||
| const outputSize = parseInt(process.argv[4] || "256"); | ||
| const outputWidth = parseInt(process.argv[4] || "256"); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This halves it, try *2ing it? same with r57
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if people do node render_gif.mjs 512 they'll get width 512 and height 256 i thinkmaybe try parseInt(process.argv[5] || |
||
| const outputHeight = parseInt(process.argv[5] || "256"); | ||
| const fpsArg = process.argv.find(a => a.startsWith("--fps=")); | ||
| const fps = fpsArg ? parseInt(fpsArg.split("=")[1]) : 20; | ||
| const whiteMode = process.argv.includes("--white"); | ||
|
|
@@ -83,7 +84,7 @@ async function main() { | |
| textureData[tf] = fs.readFileSync(path.join(skelDir, tf)).toString("base64"); | ||
| } | ||
|
|
||
| console.log(`Rendering ${skelName} as GIF at ${outputSize}x${outputSize}, ${fps}fps...`); | ||
| console.log(`Rendering ${skelName} as GIF at ${outputWidth}x${outputHeight}, ${fps}fps...`); | ||
| console.log(` Textures: ${textureFiles.join(", ")}`); | ||
|
|
||
| const browser = await chromium.launch({ headless: true, channel: "chrome" }); | ||
|
|
@@ -95,9 +96,9 @@ async function main() { | |
| if (isStreamFormat) { | ||
| fs.mkdirSync(framesDir, { recursive: true }); | ||
| await page.exposeFunction("__saveFrame", (idx, pixels) => { | ||
| const pngCanvas = createCanvas(outputSize, outputSize); | ||
| const pngCanvas = createCanvas(outputWidth, outputHeight); | ||
| const pCtx = pngCanvas.getContext("2d"); | ||
| const imgData = pCtx.createImageData(outputSize, outputSize); | ||
| const imgData = pCtx.createImageData(outputWidth, outputHeight); | ||
| imgData.data.set(new Uint8ClampedArray(pixels)); | ||
| pCtx.putImageData(imgData, 0, 0); | ||
| fs.writeFileSync(path.join(framesDir, `frame_${String(idx).padStart(4, "0")}.png`), pngCanvas.toBuffer("image/png")); | ||
|
|
@@ -108,7 +109,7 @@ async function main() { | |
| const spineCoreCode = fs.readFileSync(spineCorePath, "utf-8"); | ||
|
|
||
| const result = await page.evaluate(async (params) => { | ||
| const { skelB64, atlasB64, textureData, outputSize, fps, streamFrames, idleNames, shadowNames, hiddenSlots, whiteMode, skinName, animOverride, spineCoreCode } = params; | ||
| const { skelB64, atlasB64, textureData, outputWidth, outputHeight, fps, streamFrames, idleNames, shadowNames, hiddenSlots, whiteMode, skinName, animOverride, spineCoreCode } = params; | ||
|
|
||
| eval(spineCoreCode.replace(/^"use strict";\s*var spine\s*=/, "window.spine =")); | ||
| const spine = window.spine; | ||
|
|
@@ -249,14 +250,14 @@ async function main() { | |
| renderer.draw(batcher, skeleton); | ||
| batcher.end(); | ||
|
|
||
| const pixels = new Uint8Array(outputSize * outputSize * 4); | ||
| gl.readPixels(0, 0, outputSize, outputSize, gl.RGBA, gl.UNSIGNED_BYTE, pixels); | ||
| const pixels = new Uint8Array(outputWidth * outputHeight * 4); | ||
| gl.readPixels(0, 0, outputSize, outputHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels); | ||
|
|
||
| // Flip vertically | ||
| const flipped = new Uint8Array(outputSize * outputSize * 4); | ||
| const rowSize = outputSize * 4; | ||
| for (let row = 0; row < outputSize; row++) { | ||
| flipped.set(pixels.subarray((outputSize - 1 - row) * rowSize, (outputSize - row) * rowSize), row * rowSize); | ||
| const flipped = new Uint8Array(outputWidth * outputHeight * 4); | ||
| const rowSize = outputWidth * 4; | ||
| for (let row = 0; row < outputWidth; row++) { | ||
| flipped.set(pixels.subarray((outputWidth - 1 - row) * rowSize, (outputWidth - row) * rowSize), row * rowSize); | ||
| } | ||
|
|
||
| // White mode | ||
|
|
@@ -280,7 +281,7 @@ async function main() { | |
|
|
||
| return { frames: streamFrames ? [] : frames, frameCount, duration }; | ||
| }, { | ||
| skelB64, atlasB64, textureData, outputSize, fps, | ||
| skelB64, atlasB64, textureData, outputWidth, outputHeight, fps, | ||
| streamFrames: outputPath.endsWith(".webp") || outputPath.endsWith(".apng"), | ||
| idleNames: IDLE_NAMES, shadowNames: SHADOW_NAMES, hiddenSlots: HIDDEN_SLOTS, | ||
| whiteMode, skinName, animOverride, spineCoreCode, | ||
|
|
@@ -298,10 +299,10 @@ async function main() { | |
| if (!isStreamFormat) { | ||
| // Fallback: save frames from memory | ||
| fs.mkdirSync(tmpDir, { recursive: true }); | ||
| const pngCanvas2 = createCanvas(outputSize, outputSize); | ||
| const pngCanvas2 = createCanvas(outputWidth, outputHeight); | ||
| const pCtx2 = pngCanvas2.getContext("2d"); | ||
| for (let f = 0; f < result.frameCount; f++) { | ||
| const imgData = pCtx2.createImageData(outputSize, outputSize); | ||
| const imgData = pCtx2.createImageData(outputWidth, outputHeight); | ||
| imgData.data.set(new Uint8ClampedArray(result.frames[f])); | ||
| pCtx2.putImageData(imgData, 0, 0); | ||
| fs.writeFileSync(path.join(tmpDir, `frame_${String(f).padStart(4, "0")}.png`), pngCanvas2.toBuffer("image/png")); | ||
|
|
@@ -323,17 +324,17 @@ imgs[0].save('${outputPath}', save_all=True, append_images=imgs[1:], duration=${ | |
| fs.rmdirSync(tmpDir); | ||
| } else { | ||
| // Encode GIF | ||
| const encoder = new GIFEncoder(outputSize, outputSize, "neuquant", true); | ||
| const encoder = new GIFEncoder(outputWidth, outputHeight, "neuquant", true); | ||
| encoder.setDelay(Math.round(1000 / fps)); | ||
| encoder.setRepeat(0); | ||
| encoder.setTransparent(0x000000); | ||
| encoder.start(); | ||
|
|
||
| const gifCanvas = createCanvas(outputSize, outputSize); | ||
| const gifCanvas = createCanvas(outputWidth, outputHeight); | ||
| const ctx = gifCanvas.getContext("2d"); | ||
|
|
||
| for (let f = 0; f < result.frameCount; f++) { | ||
| const imgData = ctx.createImageData(outputSize, outputSize); | ||
| const imgData = ctx.createImageData(outputWidth, outputHeight); | ||
| imgData.data.set(new Uint8ClampedArray(result.frames[f])); | ||
| ctx.putImageData(imgData, 0, 0); | ||
| encoder.addFrame(ctx); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this may be a copy/paste error, second arg should be outputHeight