This commit is contained in:
Stan
2026-04-19 21:14:16 +02:00
parent 0c74a75126
commit 28d167f11f
42 changed files with 5681 additions and 55 deletions
+58
View File
@@ -0,0 +1,58 @@
/*
* Web Worker for image optimization (P1). Offloads the expensive bitmap decode,
* resize, and JPEG/PNG compression to a background thread so the UI remains
* responsive while processing large photos.
*
* Uses OffscreenCanvas which is available in Chrome 69+, Firefox 105+, and
* Safari 16.4+. The main module (images.js) detects support at runtime and
* falls back to the main thread for older browsers.
*/
function fitIntoBox(originalWidth, originalHeight, maxWidth, maxHeight) {
const ratio = Math.min(maxWidth / originalWidth, maxHeight / originalHeight, 1);
return {
width: Math.round(originalWidth * ratio),
height: Math.round(originalHeight * ratio)
};
}
self.onmessage = async (event) => {
const { id, file, imageRules } = event.data;
try {
const imageBitmap = await createImageBitmap(file);
const maxWidth = imageRules?.maxWidthPx || imageBitmap.width;
const maxHeight = imageRules?.maxHeightPx || imageBitmap.height;
const { width, height } = fitIntoBox(imageBitmap.width, imageBitmap.height, maxWidth, maxHeight);
const canvas = new OffscreenCanvas(width, height);
const context = canvas.getContext('2d');
context.drawImage(imageBitmap, 0, 0, width, height);
imageBitmap.close();
const targetMimeType = file.type === 'image/png' ? 'image/png' : 'image/jpeg';
const quality = Math.min(Math.max((imageRules?.jpegQuality || 82) / 100, 0.2), 0.95);
const blob = await canvas.convertToBlob({ type: targetMimeType, quality });
if (!blob) {
throw new Error(`Failed to optimize image: ${file.name}`);
}
if (imageRules?.maxFileSizeBytes && blob.size > imageRules.maxFileSizeBytes) {
throw new Error(`Optimized image still exceeds limit: ${file.name}`);
}
self.postMessage({
id,
result: {
blob,
width,
height,
extension: targetMimeType === 'image/png' ? 'png' : 'jpg'
}
});
} catch (error) {
self.postMessage({ id, error: error.message });
}
};