feat(nitro): convert imported SWF bundles to WebP Lossless
Gitea Actions Runner Test / test-job (push) Successful in 1s
CI / check (push) Successful in 31s
CI / tests-unit (push) Successful in 1m59s
CI / tests-integration (push) Successful in 2m19s
CI / tests-ui (push) Successful in 2m56s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 2m18s
Gitea Actions Runner Test / test-job (push) Successful in 1s
CI / check (push) Successful in 31s
CI / tests-unit (push) Successful in 1m59s
CI / tests-integration (push) Successful in 2m19s
CI / tests-ui (push) Successful in 2m56s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 2m18s
Newly converted .nitro bundles now store their spritesheet as WebP VP8L instead of PNG, so imports land much smaller without changing a single pixel. The texture member and spritesheet.meta.image are both labelled from the actual bytes, never from a caller's assumption. - encode through sharp with lossless and exact, so colour hidden under alpha 0 survives; this mirrors ImageSharp's TransparentColorMode.Preserve - detect PNG/WebP by magic bytes and reject anything the client cannot render, on create, download and upload paths - keep the source format when deriving size-32 sheets, scaling composites and editing metadata, so existing bundles are never silently rewritten - report fidelity in the studio: the compression panel re-encodes with the same options the importer uses, so it cannot drift and invent false warnings, and shows PNG/WebP size estimates convertSwfToNitro and buildSpritesheet are now async, so the worker, the main-thread fallback and every import call site await them. PNG stays supported for existing bundles and icon sidecars are untouched.
This commit is contained in:
1 parent
420210ffa0
commit
17de94d984
22 files changed
+1118
-125
No files matched your search
@@ -199,7 +199,10 @@ export const POST = withAdmin(
|
||||
});
|
||||
try {
|
||||
const swfBuffer = await fs.readFile(swfPath);
|
||||
const conversion = convertSwfToNitro(swfBuffer, baseClassname);
|
||||
const conversion = await convertSwfToNitro(
|
||||
swfBuffer,
|
||||
baseClassname,
|
||||
);
|
||||
await fs.writeFile(nitroPath, conversion.bundle);
|
||||
|
||||
// Build FurnitureData entry if missing
|
||||
|
||||
@@ -104,13 +104,16 @@ export const PUT = withAdmin(
|
||||
|
||||
const saved = await withFurniDataLock(async () => {
|
||||
const existingBuffer = await fs.readFile(nitroPath);
|
||||
const { png, json: currentMetadata } = parseNitroBundle(existingBuffer);
|
||||
const { texture, json: currentMetadata } =
|
||||
parseNitroBundle(existingBuffer);
|
||||
if (
|
||||
JSON.stringify(currentMetadata.catalogStudio) !==
|
||||
JSON.stringify(metadata.catalogStudio)
|
||||
)
|
||||
return false;
|
||||
const newBundle = createNitroBundle(metadata, png, base);
|
||||
// `texture` keeps its original encoding, so editing metadata never
|
||||
// changes the bundle's texture format.
|
||||
const newBundle = createNitroBundle(metadata, texture, base);
|
||||
await fs.writeFile(nitroPath, newBundle);
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -775,7 +775,7 @@ export const PATCH = withAdmin(
|
||||
// Convert SWF → Nitro + update FurnitureData.json
|
||||
try {
|
||||
const swfBuffer = await fs.readFile(swfPath);
|
||||
const conversion = convertSwfToNitro(swfBuffer, baseClassname);
|
||||
const conversion = await convertSwfToNitro(swfBuffer, baseClassname);
|
||||
await fs.writeFile(nitroPath, conversion.bundle);
|
||||
|
||||
// Add to FurnitureData.json if not already present
|
||||
|
||||
@@ -4,6 +4,7 @@ import sharp from "sharp";
|
||||
import { apiError, apiOk } from "@/lib/api";
|
||||
import { withAdmin } from "@/lib/api-handler";
|
||||
import {
|
||||
buildNitroCompressionReport,
|
||||
inspectNitroScales,
|
||||
object,
|
||||
resolveNitroFrame,
|
||||
@@ -21,10 +22,11 @@ export const GET = withAdmin(
|
||||
const base = String(classname).split("*")[0],
|
||||
{ nitroDir } = await getFurniAssetDirs();
|
||||
try {
|
||||
const { json, png } = parseNitroBundle(
|
||||
const parsed = parseNitroBundle(
|
||||
await fs.readFile(path.join(nitroDir, `${base}.nitro`)),
|
||||
);
|
||||
const image = await sharp(png).metadata();
|
||||
const { json, texture } = parsed;
|
||||
const image = await sharp(texture).metadata();
|
||||
if (!image.width || !image.height)
|
||||
return apiError("Invalid bundle texture", 422);
|
||||
const asset = request.nextUrl.searchParams.get("asset");
|
||||
@@ -33,7 +35,7 @@ export const GET = withAdmin(
|
||||
return apiError("Sprite not found", 404);
|
||||
const resolved = resolveNitroFrame(json, asset),
|
||||
rect = object(resolved.frame.frame);
|
||||
let pipeline = sharp(png).extract({
|
||||
let pipeline = sharp(texture).extract({
|
||||
left: Number(rect.x),
|
||||
top: Number(rect.y),
|
||||
width: Number(rect.w),
|
||||
@@ -51,7 +53,10 @@ export const GET = withAdmin(
|
||||
});
|
||||
}
|
||||
return apiOk({
|
||||
report: inspectNitroScales(json, image.width, image.height),
|
||||
report: {
|
||||
...inspectNitroScales(json, image.width, image.height),
|
||||
compression: await buildNitroCompressionReport(parsed),
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === "ENOENT")
|
||||
|
||||
Reference in new issue
Block a user