YiffyPaintings/scripts/images.ts

128 lines
4.4 KiB
TypeScript
Raw Normal View History

2024-05-31 13:54:48 +00:00
import type { PathLike } from "fs";
import { access } from "fs/promises";
import { dirname, resolve } from "path";
import sharp, { type OverlayOptions } from "sharp";
2024-05-31 13:54:48 +00:00
import { parseArgs } from "util";
import { formatImage, makeKZ } from "./common";
2024-05-31 13:54:48 +00:00
import { readFile } from "fs/promises";
import { writeFile } from "fs/promises";
import { rm } from "fs/promises";
import { mkdir } from "fs/promises";
import { stat } from "fs/promises";
import { readdir } from "fs/promises";
const { values: args } = parseArgs({
args: Bun.argv,
options: {
images: {
type: "string",
short: "i",
default: "images/images.json"
},
imagedir: {
type: "string",
short: "d",
default: "images"
},
outdir: {
type: "string",
short: "o",
default: "data"
},
kzoutdir: {
type: "string",
short: "k",
default: "data-kz"
},
throw: {
type: "boolean",
short: "t",
default: false
}
},
strict: true,
allowPositionals: true
});
const dirExists = async(path: PathLike) => access(path).then(() => true, () => false);
const imagesPath = resolve(args.images ?? "images.json");
const imageDir = resolve(args.imagedir ?? dirname(imagesPath));
const outDir = resolve(args.outdir ?? "data");
const kzOutDir = resolve(args.kzoutdir ?? "data-kz");
const framePercent = 0.03125;
const frameColors = [0xA47627, 0xA45226, 0x944421, 0xAC581D, 0x8C341C, 0xAC641D, 0xAB6C25, 0xA44424, 0xAC572C, 0xAC4C24, 0xA87824];
const throwOnMissing = !!args.throw;
if (!await dirExists(imageDir)) {
console.error("Image directory %s does not exist.", imageDir);
process.exit(1);
}
const ap = (p: string) => resolve(p, "assets/minecraft/textures/painting");
// const ap = (p: string) => resolve(p, "../img");
2024-05-31 13:54:48 +00:00
await rm(`${outDir}/assets`, { recursive: true, force: true });
await rm(`${kzOutDir}/assets`, { recursive: true, force: true });
await mkdir(ap(outDir), { recursive: true });
await mkdir(ap(kzOutDir), { recursive: true });
interface Image {
animation?: { frametime?: number; };
frames?: { start: number; end?: number; } | number[];
2024-05-31 13:54:48 +00:00
kz: [x: number, y: number] | null;
name: string;
resize?: [width: number, height: number];
size: string;
}
interface Images {
images: Array<Image>;
sizes: Record<string, [width: number, height: number]>;
}
const images = await Bun.file(imagesPath).json() as Images;
const [baseWidth, baseHeight] = images.sizes["1x1"];
let kz = await makeKZ(baseWidth);
2024-05-31 13:54:48 +00:00
const kzCleanup: string[] = [];
for (const image of images.images) {
let img: string | string[];
if (await stat(`${imageDir}/${image.name}`).then(s => s.isDirectory(), () => false)) {
img = await readdir(`${imageDir}/${image.name}`).then(files => files.map(f => `${imageDir}/${image.name}/${f}`).sort());
} else {
const [file] = await Array.fromAsync(new Bun.Glob(`${image.name}.*`).scan({ onlyFiles: true, cwd: imageDir }));
if (!file) {
console.error("Image %s does not exist.", image.name);
if (throwOnMissing) {
process.exit(1);
}
continue;
}
img = `${imageDir}/${file}`
}
console.log("Processing %s", image.name);
const [resizeWidth, resizeHeight] = image.resize ?? [];
const { isImage, result, kzFile } = await formatImage(image.name, img, frameColors, framePercent, ...images.sizes[image.size], resizeWidth, resizeHeight, image.frames, image.kz !== null);
await result.toFile(`${ap(outDir)}/${image.name}.png`);
if (!isImage) {
await writeFile(`${ap(outDir)}/${image.name}.png.mcmeta`, JSON.stringify({ animation: image.animation ?? {} }));
}
if (image.kz && kzFile !== null) {
kz = await sharp(kz)
.composite([{
input: await sharp(kzFile === true ? `${ap(outDir)}/${image.name}.png` : kzFile).resize(images.sizes[image.size][0], images.sizes[image.size][1]).toBuffer(),
top: baseHeight * image.kz[1],
left: baseWidth * image.kz[0]
}])
.toBuffer();
if (kzFile !== true) {
kzCleanup.push(kzFile);
}
}
}
await sharp(kz).toFile(`${ap(kzOutDir)}/paintings_kristoffer_zetterstrand.png`);
for (const file of kzCleanup) {
await rm(file);
}