mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat: add support for JXL, Camera RAW, ICO, TGA, PSD, EXR, HDR image formats
Extends the platform to handle 7 new image format families alongside the existing AVIF support gap-fill. Uses the established HEIC decoder pattern (CLI decode → PNG → Sharp) for formats Sharp can't handle natively: Camera RAW via dcraw_emu/LibRaw, PSD/TGA/EXR/HDR via ImageMagick. JXL and ICO are Sharp-native. Adds server-side preview for non-browser-displayable formats and JXL as a new convert output target. All 27 validateImageBuffer callers updated with filename for extension-based format detection.
This commit is contained in:
@@ -9,6 +9,7 @@ import { autoOrient } from "../../lib/auto-orient.js";
|
||||
import { applyEffects } from "../../lib/bg-effects.js";
|
||||
import { isToolInstalled } from "../../lib/feature-status.js";
|
||||
import { validateImageBuffer } from "../../lib/file-validation.js";
|
||||
import { decodeToSharpCompat, needsCliDecode } from "../../lib/format-decoders.js";
|
||||
import { decodeHeic } from "../../lib/heic-converter.js";
|
||||
import { createWorkspace, getWorkspacePath } from "../../lib/workspace.js";
|
||||
import { updateSingleFileProgress } from "../progress.js";
|
||||
@@ -85,7 +86,7 @@ export function registerRemoveBackground(app: FastifyInstance) {
|
||||
return reply.status(400).send({ error: "No image file provided" });
|
||||
}
|
||||
|
||||
const validation = await validateImageBuffer(fileBuffer);
|
||||
const validation = await validateImageBuffer(fileBuffer, filename);
|
||||
if (!validation.valid) {
|
||||
return reply.status(400).send({ error: `Invalid image: ${validation.reason}` });
|
||||
}
|
||||
@@ -100,6 +101,13 @@ export function registerRemoveBackground(app: FastifyInstance) {
|
||||
if (ext) filename = `${filename.slice(0, -ext.length)}.png`;
|
||||
}
|
||||
|
||||
// Decode CLI-decoded formats (RAW, TGA, PSD, EXR, HDR)
|
||||
if (needsCliDecode(validation.format)) {
|
||||
fileBuffer = await decodeToSharpCompat(fileBuffer, validation.format);
|
||||
const ext = filename.match(/\.[^.]+$/)?.[0];
|
||||
if (ext) filename = `${filename.slice(0, -ext.length)}.png`;
|
||||
}
|
||||
|
||||
// Auto-orient to fix EXIF rotation
|
||||
fileBuffer = await autoOrient(fileBuffer);
|
||||
|
||||
@@ -177,6 +185,7 @@ export function registerRemoveBackground(app: FastifyInstance) {
|
||||
async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
let settingsRaw: string | null = null;
|
||||
let bgImageBuffer: Buffer | null = null;
|
||||
let bgFilename = "background";
|
||||
|
||||
try {
|
||||
const parts = request.parts();
|
||||
@@ -185,6 +194,7 @@ export function registerRemoveBackground(app: FastifyInstance) {
|
||||
const chunks: Buffer[] = [];
|
||||
for await (const chunk of part.file) chunks.push(chunk);
|
||||
bgImageBuffer = Buffer.concat(chunks);
|
||||
bgFilename = part.filename ?? "background";
|
||||
} else if (part.type === "field" && part.fieldname === "settings") {
|
||||
settingsRaw = part.value as string;
|
||||
}
|
||||
@@ -221,10 +231,13 @@ export function registerRemoveBackground(app: FastifyInstance) {
|
||||
|
||||
// Decode HEIC/HEIF background image if needed
|
||||
if (bgImageBuffer) {
|
||||
const bgValidation = await validateImageBuffer(bgImageBuffer);
|
||||
const bgValidation = await validateImageBuffer(bgImageBuffer, bgFilename);
|
||||
if (bgValidation.valid && bgValidation.format === "heif") {
|
||||
bgImageBuffer = await decodeHeic(bgImageBuffer);
|
||||
}
|
||||
if (bgValidation.valid && needsCliDecode(bgValidation.format)) {
|
||||
bgImageBuffer = await decodeToSharpCompat(bgImageBuffer, bgValidation.format);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply effects using cached mask + original
|
||||
|
||||
Reference in New Issue
Block a user