Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
63 changes: 41 additions & 22 deletions src/lib/client/asset_processor.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { encode as encodeWebP } from '@jsquash/webp';
import resize from '@jsquash/resize';

const WEBP_QUALITY = 80;

Expand All @@ -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<Blob>}
*/
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<ImageData>}
*/
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.
Expand All @@ -29,19 +64,11 @@ async function decode_to_image_data(blob) {
* @returns {Promise<ImageData>}
*/
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);
}

/**
Expand All @@ -52,16 +79,8 @@ async function resize_to_fit(image_data, max_width) {
* @returns {Promise<ImageData>}
*/
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);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down