Resize thumbnails in web worker
Some checks failed
Tests / tests (push) Has been cancelled

This commit is contained in:
Dustin Brett 2025-02-12 19:39:53 -08:00
parent cadbbb9f70
commit 933fe11b04
4 changed files with 87 additions and 1 deletions

View File

@ -27,6 +27,7 @@ import {
ICON_GIF_SECONDS,
IMAGE_FILE_EXTENSIONS,
MAX_ICON_SIZE,
MAX_THUMBNAIL_FILE_SIZE,
MOUNTED_FOLDER_ICON,
NEW_FOLDER_ICON,
ONE_TIME_PASSIVE_EVENT,
@ -45,6 +46,7 @@ import {
import shortcutCache from "public/.index/shortcutCache.json";
import {
blobToBase64,
bufferToBlob,
bufferToUrl,
getExtension,
getGifJs,
@ -52,6 +54,7 @@ import {
getMimeType,
isSafari,
isYouTubeUrl,
resizeImage,
} from "utils/functions";
type InternetShortcut = {
@ -651,7 +654,19 @@ export const getInfoWithExtension = (
{ signal, ...ONE_TIME_PASSIVE_EVENT }
);
imageIcon.decoding = "async";
imageIcon.src = bufferToUrl(contents, getMimeType(path));
const mimeType = getMimeType(path);
if (contents.length > MAX_THUMBNAIL_FILE_SIZE) {
resizeImage(
bufferToBlob(contents, mimeType),
MAX_ICON_SIZE
).then((resizedBlob) => {
imageIcon.src = URL.createObjectURL(resizedBlob);
});
} else {
imageIcon.src = bufferToUrl(contents, mimeType);
}
}
})
);

View File

@ -346,6 +346,8 @@ export const SUPPORTED_ICON_SIZES = [16, 32, 48, 96, 144];
export const MAX_ICON_SIZE = 144;
export const MAX_THUMBNAIL_FILE_SIZE = 1048576; // 1 MB
export const DEFAULT_TEXT_FILE_SAVE_PATH = `${DESKTOP_PATH}/Untitled.txt`;
export const DEFAULT_SCROLLBAR_WIDTH = 17;

View File

@ -37,6 +37,33 @@ export const bufferToUrl = (buffer: Buffer, mimeType?: string): string =>
? `data:${mimeType};base64,${window.btoa(buffer.toString())}`
: URL.createObjectURL(bufferToBlob(buffer, mimeType));
const RESIZE_IMAGE_TIMEOUT_SECONDS = 60;
export const resizeImage = async (
blob: Blob,
maxDimension: number
): Promise<Blob> =>
new Promise((resolve) => {
const worker = new Worker(
new URL("utils/resizeImage.worker", import.meta.url),
{ name: "Resize Image Worker" }
);
const timeoutHandle = setTimeout(() => {
resolve(blob);
worker.terminate();
}, RESIZE_IMAGE_TIMEOUT_SECONDS * 1000);
const canvas = document
.createElement("canvas")
.transferControlToOffscreen();
worker.addEventListener("message", ({ data }: { data: Blob }) => {
clearTimeout(timeoutHandle);
resolve(data instanceof Blob ? data : blob);
worker.terminate();
});
worker.postMessage({ blob, canvas, maxDimension }, [canvas]);
});
let dpi: number;
export const getDpi = (): number => {

View File

@ -0,0 +1,42 @@
type ResizeImageWorkerData = {
data: {
blob: Blob;
canvas: OffscreenCanvas;
maxDimension: number;
};
};
globalThis.addEventListener(
"message",
async ({
data: { blob, canvas, maxDimension },
}: ResizeImageWorkerData): Promise<void> => {
if (blob instanceof Blob && canvas instanceof OffscreenCanvas) {
const ctx = canvas.getContext("2d");
if (!ctx) return;
const bitmap = await createImageBitmap(blob);
if (!bitmap) return;
const offscreenCanvas = canvas;
const ratio = Math.min(
maxDimension / bitmap.width,
maxDimension / bitmap.height
);
const dw = Math.round(bitmap.width * ratio);
const dh = Math.round(bitmap.height * ratio);
offscreenCanvas.width = dw;
offscreenCanvas.height = dh;
ctx.drawImage(bitmap, 0, 0, dw, dh);
globalThis.postMessage(
await offscreenCanvas.convertToBlob({ type: "image/png" })
);
}
},
{ passive: true }
);