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:
@@ -73,6 +73,7 @@ const FORMAT_MAP: Record<string, string> = {
|
||||
heif: "avif",
|
||||
tiff: "tiff",
|
||||
gif: "gif",
|
||||
jxl: "jxl",
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,6 +8,18 @@ const MAGIC_BYTES: Array<{ bytes: number[]; offset: number; format: string }> =
|
||||
{ bytes: [0x49, 0x49, 0x2a, 0x00], offset: 0, format: "tiff" }, // Little-endian TIFF
|
||||
{ bytes: [0x4d, 0x4d, 0x00, 0x2a], offset: 0, format: "tiff" }, // Big-endian TIFF
|
||||
{ bytes: [0x42, 0x4d], offset: 0, format: "bmp" },
|
||||
// AVIF (ftyp box at offset 4, brand verified in detectByMagicBytes)
|
||||
{ bytes: [0x66, 0x74, 0x79, 0x70], offset: 4, format: "avif" },
|
||||
// JXL ISOBMFF container
|
||||
{ bytes: [0x00, 0x00, 0x00, 0x0c, 0x4a, 0x58, 0x4c, 0x20], offset: 0, format: "jxl" },
|
||||
// JXL raw codestream
|
||||
{ bytes: [0xff, 0x0a], offset: 0, format: "jxl" },
|
||||
// ICO
|
||||
{ bytes: [0x00, 0x00, 0x01, 0x00], offset: 0, format: "ico" },
|
||||
// PSD ("8BPS")
|
||||
{ bytes: [0x38, 0x42, 0x50, 0x53], offset: 0, format: "psd" },
|
||||
// OpenEXR
|
||||
{ bytes: [0x76, 0x2f, 0x31, 0x01], offset: 0, format: "exr" },
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -49,6 +61,12 @@ function detectByMagicBytes(buffer: Buffer): string {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// For ftyp, verify AVIF brand at bytes 8-11
|
||||
if (entry.format === "avif") {
|
||||
if (buffer.length < 12) continue;
|
||||
const brand = buffer.slice(8, 12).toString("ascii");
|
||||
if (brand !== "avif" && brand !== "avis") continue;
|
||||
}
|
||||
return entry.format;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ const FORMAT_MAP: Record<string, string> = {
|
||||
avif: "avif",
|
||||
tiff: "tiff",
|
||||
gif: "gif",
|
||||
jxl: "jxl" as const,
|
||||
};
|
||||
|
||||
export async function convert(image: Sharp, options: ConvertOptions): Promise<Sharp> {
|
||||
|
||||
@@ -17,7 +17,7 @@ export interface OperationResult {
|
||||
info: ImageInfo;
|
||||
}
|
||||
|
||||
export type OutputFormat = "jpg" | "png" | "webp" | "avif" | "tiff" | "gif" | "heic" | "heif";
|
||||
export type OutputFormat = "jpg" | "png" | "webp" | "avif" | "tiff" | "gif" | "heic" | "heif" | "jxl";
|
||||
|
||||
export interface ResizeOptions {
|
||||
width?: number;
|
||||
|
||||
@@ -12,6 +12,17 @@ const EXT_TO_MIME: Record<string, string> = {
|
||||
ico: "image/x-icon",
|
||||
heif: "image/heif",
|
||||
heic: "image/heic",
|
||||
jxl: "image/jxl",
|
||||
dng: "image/x-adobe-dng",
|
||||
cr2: "image/x-canon-cr2",
|
||||
nef: "image/x-nikon-nef",
|
||||
arw: "image/x-sony-arw",
|
||||
orf: "image/x-olympus-orf",
|
||||
rw2: "image/x-panasonic-rw2",
|
||||
tga: "image/x-tga",
|
||||
psd: "image/vnd.adobe.photoshop",
|
||||
exr: "image/x-exr",
|
||||
hdr: "image/vnd.radiance",
|
||||
};
|
||||
|
||||
const MIME_TO_EXT: Record<string, string> = {
|
||||
@@ -26,6 +37,17 @@ const MIME_TO_EXT: Record<string, string> = {
|
||||
"image/x-icon": "ico",
|
||||
"image/heif": "heif",
|
||||
"image/heic": "heic",
|
||||
"image/jxl": "jxl",
|
||||
"image/x-adobe-dng": "dng",
|
||||
"image/x-canon-cr2": "cr2",
|
||||
"image/x-nikon-nef": "nef",
|
||||
"image/x-sony-arw": "arw",
|
||||
"image/x-olympus-orf": "orf",
|
||||
"image/x-panasonic-rw2": "rw2",
|
||||
"image/x-tga": "tga",
|
||||
"image/vnd.adobe.photoshop": "psd",
|
||||
"image/x-exr": "exr",
|
||||
"image/vnd.radiance": "hdr",
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user