refactor: move fit to size function

This commit is contained in:
2026-10-10 06:50:07 +05:00
parent dfb1f531fa
commit 0b929eced2
4 changed files with 44 additions and 40 deletions
@@ -1,10 +1,6 @@
import { fitWithinBytes } from "$lib/core/compress";
import { ToolError, type ErrorVars } from "$lib/core/errors";
import {
downloadBlob,
encode,
fitWithinBytes,
type OutputMime,
} from "$lib/core/io";
import { downloadBlob, encode, type OutputMime } from "$lib/core/io";
import { outputFormatByMime } from "$lib/output-formats";
import type { PixelImage } from "$lib/core/types";
import { execute } from "$lib/executor";
@@ -155,7 +151,7 @@ async function download(s: RunnerState, ctx: SchemaToolRunnerCtx) {
const limit = ctx.limitKb();
const quality = q !== undefined ? q / 100 : undefined;
const blob = limit
? await fitWithinBytes(s.result, out.mime, limit * 1024, quality)
? await fitWithinBytes(s.result, out.mime, limit * 1024, quality, encode)
: await encode(s.result, out.mime, quality);
downloadBlob(blob, `${ctx.pageSlug()}.${out.ext}`);
} catch (e) {
+34
View File
@@ -1,3 +1,7 @@
import type { OutputMime } from "./io";
import { quantizeImage } from "./quantize";
import type { PixelImage } from "./types";
/** Compression presets: level -> number of quantization colors. */
export const COMPRESSION_LEVELS = {
light: 192,
@@ -80,3 +84,33 @@ export async function findQualityWithin(
}
return ok;
}
export type EncodeFn = (
img: PixelImage,
mime: OutputMime,
quality?: number,
) => Promise<Blob>;
/** Encodes img to fit targetBytes (quality search for lossy, color count for
* PNG; BMP as-is). `encodeFn` injected (core/io.encode): policy stays DOM-free. */
export async function fitWithinBytes(
img: PixelImage,
mime: OutputMime,
targetBytes: number,
quality: number | undefined,
encodeFn: EncodeFn,
): Promise<Blob> {
if (mime === "image/bmp") return encodeFn(img, mime);
const initial = await encodeFn(img, mime, quality);
if (initial.size <= targetBytes) return initial;
if (mime === "image/png") {
const encodeSize = async (k: number) =>
(await encodeFn(quantizeImage(img, k).image, mime)).size;
const k = await findMaxColorsWithin(targetBytes, 256, encodeSize);
return encodeFn(quantizeImage(img, k).image, mime);
}
const encodeSize = async (q: number) =>
(await encodeFn(img, mime, q / 100)).size;
const q = await findQualityWithin(targetBytes, encodeSize);
return encodeFn(img, mime, q / 100);
}
-28
View File
@@ -1,7 +1,5 @@
import { encodeBmpBytes } from "./bmp";
import { findMaxColorsWithin, findQualityWithin } from "./compress";
import { ToolError } from "./errors";
import { quantizeImage } from "./quantize";
import { type PixelImage } from "./types";
export type OutputMime =
@@ -144,32 +142,6 @@ function canvasToBlob(
});
}
/**
* Encodes img to fit targetBytes: binary search over quality for lossy
* formats, over quantization colors for PNG. BMP has no compression knobs --
* returned as-is (the caller warns the user the limit does not apply).
*/
export async function fitWithinBytes(
img: PixelImage,
mime: OutputMime,
targetBytes: number,
quality?: number,
): Promise<Blob> {
if (mime === "image/bmp") return encode(img, mime);
const initial = await encode(img, mime, quality);
if (initial.size <= targetBytes) return initial;
if (mime === "image/png") {
const encodeSize = async (k: number) =>
(await encode(quantizeImage(img, k).image, mime)).size;
const k = await findMaxColorsWithin(targetBytes, 256, encodeSize);
return encode(quantizeImage(img, k).image, mime);
}
const encodeSize = async (q: number) =>
(await encode(img, mime, q / 100)).size;
const q = await findQualityWithin(targetBytes, encodeSize);
return encode(img, mime, q / 100);
}
export function downloadBlob(blob: Blob, filename: string): void {
const url = URL.createObjectURL(blob);
const a = document.createElement("a");