From acdafee6763b5a8acf3f5ca3d57aa81514008745 Mon Sep 17 00:00:00 2001 From: Michael Aufreiter Date: Sat, 4 Apr 2026 23:36:39 +0200 Subject: [PATCH] Do resize via canvas, instead of @jsquash/resize --- package.json | 2 +- src/lib/client/asset_processor.js | 63 ++++++++++++++++++++----------- vite.config.js | 2 +- 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index 6d07e734..98bcb7bb 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "vite": "^8.0.1" }, "dependencies": { - "@jsquash/resize": "^2.1.1", + "@jsquash/webp": "^1.5.0", "@sveltejs/adapter-node": "^5.5.4", "nanoid": "^5.1.6", diff --git a/src/lib/client/asset_processor.js b/src/lib/client/asset_processor.js index 262de679..ee4e633d 100644 --- a/src/lib/client/asset_processor.js +++ b/src/lib/client/asset_processor.js @@ -1,5 +1,4 @@ import { encode as encodeWebP } from '@jsquash/webp'; -import resize from '@jsquash/resize'; const WEBP_QUALITY = 80; @@ -20,6 +19,42 @@ async function decode_to_image_data(blob) { return ctx.getImageData(0, 0, canvas.width, canvas.height); } +/** + * Convert ImageData to a PNG blob using canvas. + * + * @param {ImageData} image_data + * @returns {Promise} + */ +async function image_data_to_png_blob(image_data) { + const canvas = new OffscreenCanvas(image_data.width, image_data.height); + const ctx = canvas.getContext('2d'); + if (!ctx) throw new Error('Could not get OffscreenCanvas 2d context'); + ctx.putImageData(image_data, 0, 0); + return await canvas.convertToBlob({ type: 'image/png' }); +} + +/** + * Resize an image blob to a specific width using canvas, maintaining aspect ratio. + * + * @param {Blob} blob + * @param {number} target_width + * @returns {Promise} + */ +async function resize_blob_to_width(blob, target_width) { + const bitmap = await createImageBitmap(blob); + const scale = target_width / bitmap.width; + const target_height = Math.round(bitmap.height * scale); + const canvas = new OffscreenCanvas(target_width, target_height); + const ctx = canvas.getContext('2d'); + if (!ctx) { + bitmap.close(); + throw new Error('Could not get OffscreenCanvas 2d context'); + } + ctx.drawImage(bitmap, 0, 0, target_width, target_height); + bitmap.close(); + return ctx.getImageData(0, 0, target_width, target_height); +} + /** * Resize ImageData to fit within max_width, preserving aspect ratio. * Returns the original ImageData if it already fits. @@ -29,19 +64,11 @@ async function decode_to_image_data(blob) { * @returns {Promise} */ async function resize_to_fit(image_data, max_width) { - const { width, height } = image_data; + const { width } = image_data; if (width <= max_width) return image_data; - const new_width = max_width; - const new_height = Math.round((height * max_width) / width); - - return resize(image_data, { - width: new_width, - height: new_height, - method: 'lanczos3', - premultiply: true, - linearRGB: true - }); + const png_blob = await image_data_to_png_blob(image_data); + return await resize_blob_to_width(png_blob, max_width); } /** @@ -52,16 +79,8 @@ async function resize_to_fit(image_data, max_width) { * @returns {Promise} */ async function resize_to_width(image_data, target_width) { - const scale = target_width / image_data.width; - const target_height = Math.round(image_data.height * scale); - - return resize(image_data, { - width: target_width, - height: target_height, - method: 'lanczos3', - premultiply: true, - linearRGB: true - }); + const png_blob = await image_data_to_png_blob(image_data); + return await resize_blob_to_width(png_blob, target_width); } /** diff --git a/vite.config.js b/vite.config.js index bc2534be..0d734c12 100644 --- a/vite.config.js +++ b/vite.config.js @@ -5,7 +5,7 @@ import { defineConfig } from 'vite'; export default defineConfig({ plugins: [tailwindcss(), sveltekit()], optimizeDeps: { - exclude: ['@jsquash/webp', '@jsquash/resize'] + exclude: ['@jsquash/webp'] }, worker: { format: 'es'