fix: rest of imagemagick formats

fixed over 35 formats in past few commits because i did a stupid (setting bit depth to "auto" apparently breaks 30 formats, but not all). also clamp sizes for certain formats
This commit is contained in:
Maya 2026-06-04 13:03:02 +03:00
parent 058f16af61
commit 125854414c
No known key found for this signature in database
2 changed files with 85 additions and 60 deletions

View File

@ -234,7 +234,7 @@ export class MagickConverter extends Converter {
default: "auto", default: "auto",
options: [ options: [
{ value: "auto", label: "Auto" }, { value: "auto", label: "Auto" },
{ value: "custom", label: "Custom" }, // { value: "custom", label: "Custom" },
{ value: "8", label: "8-bit" }, { value: "8", label: "8-bit" },
{ value: "16", label: "16-bit" }, { value: "16", label: "16-bit" },
{ value: "32", label: "32-bit" }, { value: "32", label: "32-bit" },

View File

@ -339,83 +339,95 @@ const magickConvert = async (
} }
} }
if (fmt === "ICO" && !singleSize) { const icons = ["ICO", "ICON", "CUR"]; // TODO: icns (1024x1024 max)
const standardSizes = [16, 24, 32, 48, 64, 128, 256, 512]; if (icons.includes(fmt)) {
if (singleSize) {
let desired = 0; // clamp to 256x256
if (resolution && resolution !== "auto") { clampRes(img, 256);
const actualResolution =
resolution === "custom"
? (conversionSettings.customResolution as string)
: resolution;
const [wsel, hsel] = (actualResolution || "")
.split("x")
.map((d: string) => parseInt(d) || 0);
desired = Math.max(wsel || 0, hsel || 0);
} else { } else {
desired = Math.max(img.width || 0, img.height || 0); // generate all standard sizes for icons
} const standardSizes = [16, 24, 32, 48, 64, 128, 256];
if (desired <= 0) desired = Math.max(...standardSizes); let desired = 0;
if (desired > Math.max(...standardSizes)) if (resolution && resolution !== "auto") {
desired = Math.max(...standardSizes); const actualResolution =
resolution === "custom"
? (conversionSettings.customResolution as string)
: resolution;
const [wsel, hsel] = (actualResolution || "")
.split("x")
.map((d: string) => parseInt(d) || 0);
desired = Math.max(wsel || 0, hsel || 0);
} else {
desired = Math.max(img.width || 0, img.height || 0);
}
const sizes = standardSizes.filter((s) => s <= desired); if (desired <= 0) desired = Math.max(...standardSizes);
if (sizes.length === 0) sizes.push(Math.min(...standardSizes)); if (desired > Math.max(...standardSizes))
desired = Math.max(...standardSizes);
const sourcePng = await new Promise<Uint8Array>((resolve) => { const sizes = standardSizes.filter((s) => s <= desired);
img.write(MagickFormat.Png, (o: Uint8Array) => if (sizes.length === 0) sizes.push(Math.min(...standardSizes));
resolve(structuredClone(o)),
);
});
console.log(`encoding sizes for ico: ${sizes.join(", ")}`); const sourcePng = await new Promise<Uint8Array>((resolve) => {
img.write(MagickFormat.Png, (o: Uint8Array) =>
return await new Promise<Uint8Array>((resolve) => { resolve(structuredClone(o)),
MagickImageCollection.use((collection) => {
for (const size of sizes) {
const variant = MagickImage.create(
sourcePng,
new MagickReadSettings({ format: MagickFormat.Png }),
);
const scale =
size / Math.max(variant.width, variant.height);
const newW = Math.max(1, Math.round(variant.width * scale));
const newH = Math.max(
1,
Math.round(variant.height * scale),
);
variant.resize(newW, newH);
collection.push(variant);
console.log(
`added size ${size}x${size} to MagickImageCollection`,
);
}
collection.write(
fmt as unknown as MagickFormat,
(o: Uint8Array) => {
resolve(structuredClone(o));
},
); );
}); });
});
} console.log(`encoding sizes for ico: ${sizes.join(", ")}`);
return await new Promise<Uint8Array>((resolve) => {
MagickImageCollection.use((collection) => {
for (const size of sizes) {
const variant = MagickImage.create(
sourcePng,
new MagickReadSettings({
format: MagickFormat.Png,
}),
);
const scale =
size / Math.max(variant.width, variant.height);
const newW = Math.max(
1,
Math.round(variant.width * scale),
);
const newH = Math.max(
1,
Math.round(variant.height * scale),
);
variant.resize(newW, newH);
collection.push(variant);
console.log(
`added size ${size}x${size} to MagickImageCollection`,
);
}
collection.write(
fmt as unknown as MagickFormat,
(o: Uint8Array) => {
resolve(structuredClone(o));
},
);
});
});
}
} else if (fmt === "RGF") clampRes(img, 254); // max 254x254
const result = await new Promise<Uint8Array>((resolve, reject) => { const result = await new Promise<Uint8Array>((resolve, reject) => {
try { try {
// quality, depth, colorSpace, transparency, metadata // quality, depth, colorSpace, transparency, metadata
const quality = conversionSettings.quality as number; const quality = conversionSettings.quality as number;
const bitDepth = conversionSettings.depth as number; const bitDepth = conversionSettings.depth;
const colorSpace = conversionSettings.colorSpace as string; const colorSpace = conversionSettings.colorSpace as string;
const transparency = conversionSettings.transparency as boolean; const transparency = conversionSettings.transparency as boolean;
const metadata = conversionSettings.metadata as boolean; const metadata = conversionSettings.metadata as boolean;
// magick-wasm automatically clamps (https://github.com/dlemstra/magick-wasm/blob/76fc6f2b0c0497d2ddc251bbf6174b4dc92ac3ea/src/magick-image.ts#L2480) // magick-wasm automatically clamps (https://github.com/dlemstra/magick-wasm/blob/76fc6f2b0c0497d2ddc251bbf6174b4dc92ac3ea/src/magick-image.ts#L2480)
if (quality) img.quality = quality; if (quality) img.quality = quality;
if (bitDepth) img.depth = bitDepth; if (bitDepth !== "auto") img.depth = bitDepth;
if (!metadata) img.strip(); if (!metadata) img.strip();
if (colorSpace) { if (colorSpace) {
switch (colorSpace) { switch (colorSpace) {
@ -462,6 +474,19 @@ const magickConvert = async (
return result; return result;
}; };
const clampRes = (img: IMagickImage, maxSize: number) => {
const w = img.width;
const h = img.height;
if (w > maxSize || h > maxSize) {
const scale = maxSize / Math.max(w, h);
const newW = Math.max(1, Math.round(w * scale));
const newH = Math.max(1, Math.round(h * scale));
img.resize(newW, newH);
}
};
onmessage = async (e) => { onmessage = async (e) => {
const message = e.data; const message = e.data;
try { try {