Compare commits

...

No commits in common. "master" and "419121c" have entirely different histories.

95 changed files with 161 additions and 495 deletions

106
README.md
View File

@ -1,118 +1,68 @@
# Yiffy Paintings # Yiffy Paintings
Replaces the default paintings with yiff. Supports 1.6+ Replaces the default paintings with yiff. Supports 1.6+
### Sources
* [alban](https://e621.net/posts/1101130)
* [aztec](https://e621.net/posts/537087)
* [aztec2](https://e621.net/posts/426790)
* [bomb](https://e621.net/posts/1680202)
* [burning_skull](https://e621.net/posts/773268)
* [bust](https://e621.net/posts/2008022)
* [courbet](https://e621.net/posts/1746859)
* [creebet](https://e621.net/posts/406661)
* [donkey_kong](https://e621.net/posts/1375064)
* [fighters](https://e621.net/posts/1881510)
* [graham](https://e621.net/posts/1533304)
* [kebab](https://e621.net/posts/1571695)
* [match](https://e621.net/posts/1352699)
* [pigscene](https://e621.net/posts/1518055)
* [plant](https://e621.net/posts/1194572)
* [pointer](https://e621.net/pools/8368)
* [pool](https://e621.net/posts/854139)
* [sea](https://e621.net/posts/2939687)
* [skeleton](https://e621.net/posts/979894)
* [skulls\_and\_roses](https://e621.net/posts/1599707)
* [stage](https://e621.net/posts/314664)
* [sunset](https://e621.net/posts/2333033)
* [void](https://e621.net/posts/704825)
* [wanderer](https://e621.net/posts/956366)
* [wasteland](https://e621.net/posts/464478)
* [wither](https://e621.net/posts/1683783)
### Examples ### Examples
<details> <details>
<summary>1x1</summary> <summary>1x1</summary>
* [kebab](https://e621.net/posts/1571695)
* [aztec](https://e621.net/posts/537087)
* [alban](https://e621.net/posts/1101130)
* [aztec2](https://e621.net/posts/426790)
* [bomb](https://e621.net/posts/1680202)
* [plant](https://e621.net/posts/1194572)
* [wasteland](https://e621.net/posts/464478)
* [meditative](https://e621.net/posts/3355209) (1.21+)
<img src="examples/1x1.png" width="80%"> <img src="examples/1x1.png" width="80%">
</details> </details>
<details> <details>
<summary>1x2</summary> <summary>1x2</summary>
* [wanderer](https://e621.net/posts/956366)
* [graham](https://e621.net/posts/1533304)
* [prairie_ride](https://e621.net/posts/4005374) (1.21+)
<img src="examples/1x2.png" width="80%"> <img src="examples/1x2.png" width="80%">
</details> </details>
<details> <details>
<summary>2x1</summary> <summary>2x1</summary>
* [pool](https://e621.net/posts/854139)
* [courbet](https://e621.net/posts/1746859)
* [sunset](https://e621.net/posts/2333033)
* [sea](https://e621.net/posts/2939687)
* [creebet](https://e621.net/posts/406661)
<img src="examples/2x1.png" width="80%"> <img src="examples/2x1.png" width="80%">
</details> </details>
<details> <details>
<summary>2x2</summary> <summary>2x2</summary>
* [match](https://e621.net/posts/1352699)
* [bust](https://e621.net/posts/2008022)
* [stage](https://e621.net/posts/314664)
* [void](https://e621.net/posts/704825)
* [skull\_and\_roses](https://e621.net/posts/1599707)
* [wither](https://e621.net/posts/1683783)
* [baroque](https://e621.net/posts/4524310) (1.21+)
* [humble](https://e621.net/posts/3293255) (1.21+)
* [earth](https://e621.net/posts/4394369) (1.19+)
* [wind](https://e621.net/posts/4025061) (1.19+)
* [fire](https://e621.net/posts/2984756) (1.19+)
* [water](https://e621.net/posts/2827630) (1.19+)
<img src="examples/2x2.png" width="80%"> <img src="examples/2x2.png" width="80%">
</details> </details>
<details>
<summary>3x3</summary>
* [bouquet](https://e621.net/posts/4420140) (1.21+)
* [cavebird](https://e621.net/posts/2433070) (1.21+)
* [cotan](https://e621.net/posts/2255060) (1.21+)
* [endboss](https://e621.net/posts/4543670) (1.21+)
* [fern](https://e621.net/posts/1339799) (1.21+)
* [owlemons](https://e621.net/posts/4623032) (1.21+)
* [sunflowers](https://e621.net/posts/4626501) (1.21+)
* [tides](https://e621.net/posts/4642340) (1.21+)
<img src="examples/3x3.png" width="80%">
</details>
<details>
<summary>3x4</summary>
* [backyard](https://e621.net/posts/4155856) (1.21+)
* [pond](https://e621.net/posts/3935565) (1.21+)
<img src="examples/3x4.png" width="80%">
</details>
<details> <details>
<summary>4x2</summary> <summary>4x2</summary>
* [fighters](https://e621.net/posts/1881510)
* [changing](https://e621.net/posts/3317590) (1.21+)
* [finding](https://e621.net/posts/1818640) (1.21+)
* [lowmist](https://e621.net/posts/1345861) (1.21+)
* [passage](https://e621.net/posts/4152001) (1.21+)
<img src="examples/4x2.png" width="80%"> <img src="examples/4x2.png" width="80%">
</details> </details>
<details> <details>
<summary>4x3</summary> <summary>4x3</summary>
* [skeleton](https://e621.net/posts/979894)
* [donkey_kong](https://e621.net/posts/1375064)
<img src="examples/4x3.png" width="80%"> <img src="examples/4x3.png" width="80%">
</details> </details>
<details> <details>
<summary>4x4</summary> <summary>4x4</summary>
* [burning_skull](https://e621.net/posts/773268)
* [pigscene](https://e621.net/posts/1518055)
* [pointer](https://e621.net/pools/8368)
* [unpacked](https://e621.net/posts/3543944) (1.21+)
* [orb](https://e621.net/posts/2626566) (1.21+)
<img src="examples/4x4.png" width="80%"> <img src="examples/4x4.png" width="80%">
</details> </details>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1 +0,0 @@
{"animation":{"frametime":3}}

Binary file not shown.

View File

@ -1 +0,0 @@
{"animation":{}}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1 +0,0 @@
{"animation":{}}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
examples/1x1.png (Stored with Git LFS)

Binary file not shown.

BIN
examples/1x2.png (Stored with Git LFS)

Binary file not shown.

BIN
examples/2x1.png (Stored with Git LFS)

Binary file not shown.

BIN
examples/2x2.png (Stored with Git LFS)

Binary file not shown.

BIN
examples/3x3.png (Stored with Git LFS)

Binary file not shown.

BIN
examples/3x4.png (Stored with Git LFS)

Binary file not shown.

BIN
examples/4x2.png (Stored with Git LFS)

Binary file not shown.

BIN
examples/4x3.png (Stored with Git LFS)

Binary file not shown.

BIN
examples/4x4.png (Stored with Git LFS)

Binary file not shown.

BIN
images/backyard.webm (Stored with Git LFS)

Binary file not shown.

BIN
images/baroque.webm (Stored with Git LFS)

Binary file not shown.

BIN
images/bouquet.png (Stored with Git LFS)

Binary file not shown.

BIN
images/cavebird.jpg (Stored with Git LFS)

Binary file not shown.

BIN
images/changing.png (Stored with Git LFS)

Binary file not shown.

BIN
images/cotan.png (Stored with Git LFS)

Binary file not shown.

BIN
images/earth.png (Stored with Git LFS)

Binary file not shown.

BIN
images/endboss.png (Stored with Git LFS)

Binary file not shown.

BIN
images/fern.jpg (Stored with Git LFS)

Binary file not shown.

BIN
images/finding.jpg (Stored with Git LFS)

Binary file not shown.

BIN
images/fire.png (Stored with Git LFS)

Binary file not shown.

BIN
images/humble.png (Stored with Git LFS)

Binary file not shown.

View File

@ -1,14 +1,14 @@
{ {
"sizes": { "sizes": {
"1x1": [320, 320], "1x1": [640, 640],
"1x2": [320, 640], "1x2": [640, 1280],
"2x1": [640, 320], "2x1": [1280, 640],
"2x2": [640, 640], "2x2": [1280, 1280],
"3x3": [960, 960], "3x3": [1920, 1920],
"3x4": [960, 1280], "3x4": [1920, 2560],
"4x2": [1280, 640], "4x2": [2560, 1280],
"4x3": [1280, 960], "4x3": [2560, 1920],
"4x4": [1280, 1280] "4x4": [2560, 2560]
}, },
"images": [ "images": [
{ {
@ -48,7 +48,7 @@
"kz": [6, 0] "kz": [6, 0]
}, },
{ {
"name": "meditative", "name": "meditation",
"size": "1x1", "size": "1x1",
"kz": null "kz": null
}, },
@ -81,22 +81,21 @@
"name": "wanderer", "name": "wanderer",
"size": "1x2", "size": "1x2",
"kz": [0, 4], "kz": [0, 4],
"resize": [320, 320], "resize": [640, 640],
"_comment": "For some reason this needs to be 1:1" "_comment": "For some reason this needs to be 1:1"
}, },
{ {
"name": "graham", "name": "graham",
"size": "1x2", "size": "1x2",
"kz": [1, 4], "kz": [1, 4],
"resize": [320, 320], "resize": [640, 640],
"frames": [1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58],
"_comment": "For some reason this needs to be 1:1" "_comment": "For some reason this needs to be 1:1"
}, },
{ {
"name": "prairie_ride", "name": "prairie_ride",
"size": "1x2", "size": "1x2",
"kz": null, "kz": null,
"resize": [320, 320], "resize": [640, 640],
"_comment": "For some reason this needs to be 1:1" "_comment": "For some reason this needs to be 1:1"
}, },
{ {
@ -157,9 +156,7 @@
{ {
"name": "baroque", "name": "baroque",
"size": "2x2", "size": "2x2",
"kz": null, "kz": null
"frames": { "start": 60, "end": 140 },
"resize": [320, 320]
}, },
{ {
"name": "humble", "name": "humble",
@ -190,7 +187,7 @@
"name": "pointer", "name": "pointer",
"size": "4x4", "size": "4x4",
"kz": [0, 12], "kz": [0, 12],
"resize": [320, 320], "resize": [640, 640],
"animation": { "frametime": 100 } "animation": { "frametime": 100 }
}, },
{ {
@ -202,8 +199,8 @@
"name": "burning_skull", "name": "burning_skull",
"size": "4x4", "size": "4x4",
"kz": [8, 12], "kz": [8, 12],
"resize": [320, 320], "resize": [640, 640],
"frames": { "start": 2805, "end": 2826 } "frames": [2805, 2826]
}, },
{ {
"name": "unpacked", "name": "unpacked",
@ -268,11 +265,7 @@
{ {
"name": "backyard", "name": "backyard",
"size": "3x4", "size": "3x4",
"kz": null, "kz": null
"resize": [640, 640],
"frames": [3, 6, 12, 15, 18],
"animation": { "frametime": 3 },
"_comment": "For some reason this needs to be 1:1"
}, },
{ {
"name": "pond", "name": "pond",

BIN
images/lowmist.png (Stored with Git LFS)

Binary file not shown.

BIN
images/meditative.gif (Stored with Git LFS)

Binary file not shown.

BIN
images/orb.png (Stored with Git LFS)

Binary file not shown.

BIN
images/owlemons.png (Stored with Git LFS)

Binary file not shown.

BIN
images/passage.png (Stored with Git LFS)

Binary file not shown.

BIN
images/pond.png (Stored with Git LFS)

Binary file not shown.

BIN
images/prairie_ride.png (Stored with Git LFS)

Binary file not shown.

BIN
images/sunflowers.jpg (Stored with Git LFS)

Binary file not shown.

View File

@ -1,23 +1,22 @@
{ {
"sizes": { "sizes": {
"1x1": [320, 320], "1x1": [640, 640],
"1x2": [320, 640], "1x2": [640, 1280],
"2x1": [640, 320], "2x1": [1280, 640],
"2x2": [640, 640], "2x2": [1280, 1280],
"3x3": [960, 960], "3x3": [1920, 1920],
"3x4": [960, 1280], "3x4": [1920, 2560],
"4x2": [1280, 640], "4x2": [2560, 1280],
"4x3": [1280, 960], "4x3": [2560, 1920],
"4x4": [1280, 1280] "4x4": [2560, 2560]
}, },
"images": [ "images": [
{ {
"name": "graham", "name": "burning_skull",
"size": "1x2", "size": "4x4",
"kz": [1, 4], "kz": [8, 12],
"resize": [320, 320], "resize": [620, 620],
"frames": [1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52, 55, 58], "frames": [2805, 2826]
"_comment": "For some reason this needs to be 1:1"
} }
] ]
} }

BIN
images/tides.png (Stored with Git LFS)

Binary file not shown.

BIN
images/unpacked.jpg (Stored with Git LFS)

Binary file not shown.

BIN
images/water.png (Stored with Git LFS)

Binary file not shown.

BIN
images/wind.png (Stored with Git LFS)

Binary file not shown.

View File

@ -18,8 +18,7 @@
["1.20", 15], ["1.20", 15],
["1.20.2", 18], ["1.20.2", 18],
["1.20.3", 26], ["1.20.3", 26],
["1.20.5", 32], ["1.20.5", 32]
["1.21", 34]
], ],
"exports": [], "exports": [],
"overrides": { "overrides": {

BIN
kz.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -1,67 +0,0 @@
1x1
- kebab
- aztec
- alban
- aztec2
- bomb
- plant
- wasteland
- meditative (1.21+)
1x2
- wanderer
- graham
- prairie_ride (1.21+)
2x1
- pool
- courbet
- sunset
- sea
- creebet
2x2
- match
- bust
- stage
- void
- skull_and_roses
- wither
- baroque (1.21+)
- humble (1.21+)
- earth (1.19+)
- wind (1.19+)
- fire (1.19+)
- water (1.19+)
3x3
- bouquet (1.21+)
- cavebird (1.21+)
- cotan (1.21+)
- endboss (1.21+)
- fern (1.21+)
- owlemons (1.21+)
- sunflowers (1.21+)
- tides (1.21+)
3x4
- backyard (1.21+)
- pond (1.21+)
4x2
- fighters
- changing (1.21+)
- finding (1.21+)
- lowmist (1.21+)
- passage (1.21+)
4x3
- skeleton
- donkey_kong
4x4
- pointer
- pigscene
- burning_skull
- unpacked (1.21+)
- orb (1.21+)

View File

@ -8,7 +8,7 @@ import { tmpdir } from "os";
import { basename } from "path"; import { basename } from "path";
import sharp, { type OverlayOptions, type Sharp } from "sharp"; import sharp, { type OverlayOptions, type Sharp } from "sharp";
export async function formatImage(name: string, image: string | string[], frameColors: number[], framePercent: number, width: number, height: number, resizeWidth?: number, resizeHeight?: number, frames?: { start: number; end?: number; } | number[], saveKz?: boolean) { export async function formatImage(name: string, image: string | string[], frameColors: number[], framePercent: number, width: number, height: number, resizeWidth?: number, resizeHeight?: number, frames?: [start: number, end: number], saveKz?: boolean) {
saveKz ??= false; saveKz ??= false;
const originalWidth = width, originalHeight = height; const originalWidth = width, originalHeight = height;
const originalFrameVH = Math.floor(Math.min(originalWidth, originalHeight) * framePercent); const originalFrameVH = Math.floor(Math.min(originalWidth, originalHeight) * framePercent);
@ -37,18 +37,11 @@ export async function formatImage(name: string, image: string | string[], frameC
let files: string[] = []; let files: string[] = [];
tmpDir = await mkdtemp(`${tmpdir()}/split-frames-`); tmpDir = await mkdtemp(`${tmpdir()}/split-frames-`);
if (!Array.isArray(image)) { if (!Array.isArray(image)) {
let select = ""; const [start = 0, end = null] = frames ?? [];
if (Array.isArray(frames)) {
select = frames.map(f => `eq(n\\,${f})`).join("+");
} else if (frames) {
select = `gte(n\\, ${frames.start})${frames.end ? `*lte(n\\,${frames.end}` : ""})`;
} else {
select = "n";
}
console.debug("Input file %s is not an image, assuming we need to extract frames.", name); console.debug("Input file %s is not an image, assuming we need to extract frames.", name);
await new Promise<void>((resolve) => { await new Promise<void>((resolve) => {
Ffmpeg(image) Ffmpeg(image)
.videoFilter(`scale=${inputWidth}:${inputHeight},select='${select}'`) .videoFilter(`scale=${inputWidth}:${inputHeight}${end !== null ? `,select='gte(n\\, ${start})*lte(n\\,${end})'` : ""}`)
.addOption("-vsync vfr") .addOption("-vsync vfr")
.output(`${tmpDir}/frame%04d.png`) .output(`${tmpDir}/frame%04d.png`)
.on("end", async() => { .on("end", async() => {
@ -63,12 +56,11 @@ export async function formatImage(name: string, image: string | string[], frameC
console.log("Frames extracted to %s, %d total for %s", tmpDir, files.length, name); console.log("Frames extracted to %s, %d total for %s", tmpDir, files.length, name);
resolve(); resolve();
}) })
.on("error", async(err) => { .on("error", async() => {
if (tmpDir) { if (tmpDir) {
await rm(tmpDir, { recursive: true }); await rm(tmpDir, { recursive: true });
} }
console.error("Failed to extract frames for %s.", name); console.error("Failed to extract frames for %s.", name);
console.error(err);
process.exit(1); process.exit(1);
}) })
.run() .run()
@ -200,62 +192,3 @@ async function createFrame(colors: number[], percent: number, width: number, hei
limitInputPixels: false limitInputPixels: false
}).composite(parts).png().toBuffer(); }).composite(parts).png().toBuffer();
} }
export const rgb = (hex: number) => ({ r: (hex >> 16) & 0xFF, g: (hex >> 8) & 0xFF, b: hex & 0xFF });
const kzBorderColor = rgb(0x6B3F7F)
const kzFillColor = rgb(0xD67FFF);
const plankSize = 4;
// the things I do to not have to make this a file
const plank = Buffer.from("iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAB3RJTUUH6AUfFCAKmCc95gAAATJJREFUKM9lUsFqwzAMlSXFXRYIYQRy6GX/tq/Zh/ZQGKUEOi9WpOyg1jWpL1Gen+X3/BS+vz6TCDxW2zQAoGZZ1et6JRF0UiSab1skUrMkklV9O4k4omZ+hv2TVfsuOC8SAQAh+paazbdtGu6/fLos9wrD4+a1aFht8+J0WZzAx4/Dq4ck0jZNEgEI820b+7sQQmRnF1TNXIyaubaxB5cKAKDK3rIdntTiyhtNQ2wrnM/XXGvd+Vlt25nkaYg19XzNfRdKGkV6yYFrxwAwDVHNPDhHXFWhcds0u05PiwCRaOyfD5BV+XzNq22urzjZZVLjew9VAlDdwz/z6kz0UfG3KsGVAStK+i74RDEhtojQiXetJ5QQk0gkotccAIBCWNQAlgPhosaYH1HI75+9v6F7+AdOcdbDy169/gAAAABJRU5ErkJggg==", "base64");
export async function makeKZ(squareSize: number, gridSize = 16) {
const kzBorderSize = squareSize * 0.0625;
const kzPlank = await sharp(plank).resize(squareSize, squareSize).toBuffer();
const plankStart = gridSize - plankSize;
const kzParts: OverlayOptions[] = [];
// i = top to bottom
// j = left to right
for (let i = 0; i < gridSize; i++) {
for (let j = 0; j < gridSize; j++) {
if (i < plankSize && j >= plankStart) {
kzParts.push({
input: kzPlank,
top: squareSize * i,
left: squareSize * j
});
} else {
kzParts.push({
input: {
create: {
width: kzBorderSize,
height: squareSize,
channels: 3,
background: kzBorderColor
}
},
top: squareSize * i,
left: squareSize * j
},{
input: {
create: {
width: squareSize,
height: kzBorderSize,
channels: 3,
background: kzBorderColor
}
},
top: squareSize * i,
left: squareSize * j
});
}
}
}
return sharp({
create: {
width: squareSize * 16,
height: squareSize * 16,
channels: 3,
background: kzFillColor
},
limitInputPixels: false
}).composite(kzParts).png().toBuffer();
}

View File

@ -1,9 +1,9 @@
import type { PathLike } from "fs"; import type { PathLike } from "fs";
import { access } from "fs/promises"; import { access } from "fs/promises";
import { dirname, resolve } from "path"; import { dirname, resolve } from "path";
import sharp, { type OverlayOptions } from "sharp"; import sharp from "sharp";
import { parseArgs } from "util"; import { parseArgs } from "util";
import { formatImage, makeKZ } from "./common"; import { formatImage } from "./common";
import { readFile } from "fs/promises"; import { readFile } from "fs/promises";
import { writeFile } from "fs/promises"; import { writeFile } from "fs/promises";
import { rm } from "fs/promises"; import { rm } from "fs/promises";
@ -34,6 +34,10 @@ const { values: args } = parseArgs({
short: "k", short: "k",
default: "data-kz" default: "data-kz"
}, },
kzfile: {
type: "string",
default: "kz.png"
},
throw: { throw: {
type: "boolean", type: "boolean",
short: "t", short: "t",
@ -48,6 +52,7 @@ const dirExists = async(path: PathLike) => access(path).then(() => true, () => f
const imagesPath = resolve(args.images ?? "images.json"); const imagesPath = resolve(args.images ?? "images.json");
const imageDir = resolve(args.imagedir ?? dirname(imagesPath)); const imageDir = resolve(args.imagedir ?? dirname(imagesPath));
const outDir = resolve(args.outdir ?? "data"); const outDir = resolve(args.outdir ?? "data");
const kzFile = resolve(args.kzfile ?? "frames/kz.png");
const kzOutDir = resolve(args.kzoutdir ?? "data-kz"); const kzOutDir = resolve(args.kzoutdir ?? "data-kz");
const framePercent = 0.03125; const framePercent = 0.03125;
const frameColors = [0xA47627, 0xA45226, 0x944421, 0xAC581D, 0x8C341C, 0xAC641D, 0xAB6C25, 0xA44424, 0xAC572C, 0xAC4C24, 0xA87824]; const frameColors = [0xA47627, 0xA45226, 0x944421, 0xAC581D, 0x8C341C, 0xAC641D, 0xAB6C25, 0xA44424, 0xAC572C, 0xAC4C24, 0xA87824];
@ -57,8 +62,12 @@ if (!await dirExists(imageDir)) {
process.exit(1); process.exit(1);
} }
if (!await Bun.file(kzFile).exists()) {
console.error("Kz file %s does not exist.", kzFile);
process.exit(1);
}
const ap = (p: string) => resolve(p, "assets/minecraft/textures/painting"); const ap = (p: string) => resolve(p, "assets/minecraft/textures/painting");
// const ap = (p: string) => resolve(p, "../img");
await rm(`${outDir}/assets`, { recursive: true, force: true }); await rm(`${outDir}/assets`, { recursive: true, force: true });
await rm(`${kzOutDir}/assets`, { recursive: true, force: true }); await rm(`${kzOutDir}/assets`, { recursive: true, force: true });
await mkdir(ap(outDir), { recursive: true }); await mkdir(ap(outDir), { recursive: true });
@ -66,7 +75,7 @@ await mkdir(ap(kzOutDir), { recursive: true });
interface Image { interface Image {
animation?: { frametime?: number; }; animation?: { frametime?: number; };
frames?: { start: number; end?: number; } | number[]; frames?: [start: number, end: number];
kz: [x: number, y: number] | null; kz: [x: number, y: number] | null;
name: string; name: string;
resize?: [width: number, height: number]; resize?: [width: number, height: number];
@ -81,8 +90,8 @@ interface Images {
const images = await Bun.file(imagesPath).json() as Images; const images = await Bun.file(imagesPath).json() as Images;
const [baseWidth, baseHeight] = images.sizes["1x1"]; const [baseWidth, baseHeight] = images.sizes["1x1"];
let kz = await makeKZ(baseWidth);
const kzCleanup: string[] = []; const kzCleanup: string[] = [];
let kz = await sharp(kzFile, { limitInputPixels: false }).toBuffer();
for (const image of images.images) { for (const image of images.images) {
let img: string | string[]; let img: string | string[];
if (await stat(`${imageDir}/${image.name}`).then(s => s.isDirectory(), () => false)) { if (await stat(`${imageDir}/${image.name}`).then(s => s.isDirectory(), () => false)) {