sharp by wasm

This commit is contained in:
seb
2026-10-28 01:46:51 +01:00
parent 1c5385061c
commit e7884c03ef
20 changed files with 530 additions and 364 deletions

View File

@@ -1,4 +1,4 @@
import sharp from 'sharp';
import { optimizeImage } from 'wasm-image-optimization';
import * as db from '../../db/posterminal.js';
const PIMAGE_DEFAULT_W = 200;
@@ -250,42 +250,63 @@ async function servePosImage(req, res, { isCimage = false } = {}) {
return res.status(304).end();
}
const imageSharp = sharp(image.buffer);
const meta = await optimizeImage({ image: image.buffer, format: 'none' });
const srcW = meta.originalWidth || targetW;
const srcH = meta.originalHeight || targetH;
const scale = Math.min(1, targetW / srcW, targetH / srcH);
const scaledW = Math.max(1, Math.round(srcW * scale));
const scaledH = Math.max(1, Math.round(srcH * scale));
const resizeOptions = isCimage
? {
fit: 'contain',
background: '#ffffff',
withoutEnlargement: true,
}
: {
fit: 'inside',
withoutEnlargement: true,
};
const scaledPng = await optimizeImage({
image: image.buffer,
width: scale < 1 ? scaledW : undefined,
height: scale < 1 ? scaledH : undefined,
fit: 'fill',
format: 'png',
});
let pipeline = imageSharp
.resize(targetW, targetH, resizeOptions)
.flatten({ background: '#ffffff' });
const canvasW = isCimage ? targetW : scaledPng.width;
const canvasH = isCimage ? targetH : scaledPng.height;
const offsetX = Math.round((canvasW - scaledPng.width) / 2);
const offsetY = Math.round((canvasH - scaledPng.height) / 2);
const b64 = Buffer.from(scaledPng.data).toString('base64');
const flatSvg =
`<svg xmlns="http://www.w3.org/2000/svg" width="${canvasW}" height="${canvasH}">` +
`<rect width="${canvasW}" height="${canvasH}" fill="#ffffff"/>` +
`<image href="data:image/png;base64,${b64}" x="${offsetX}" y="${offsetY}" width="${scaledPng.width}" height="${scaledPng.height}"/>` +
`</svg>`;
const accept = String(req.headers.accept || '');
const formatParam = typeof req.query.format === 'string' ? req.query.format.toLowerCase() : '';
let contentType = 'image/jpeg';
let targetFormat = 'jpeg';
let quality = 85;
let speed = 6;
if (formatParam === 'avif' || (!formatParam && accept.includes('image/avif'))) {
pipeline = pipeline.avif({ effort: 2 });
targetFormat = 'avif';
speed = 8;
contentType = 'image/avif';
} else if (formatParam === 'webp' || (!formatParam && accept.includes('image/webp'))) {
pipeline = pipeline.webp({ quality: 85 });
targetFormat = 'webp';
quality = 85;
contentType = 'image/webp';
} else if (formatParam === 'png') {
pipeline = pipeline.png();
targetFormat = 'png';
contentType = 'image/png';
} else {
pipeline = pipeline.jpeg({ quality: 85 });
targetFormat = 'jpeg';
quality = 85;
contentType = 'image/jpeg';
}
const buffer = await pipeline.toBuffer();
const encoded = await optimizeImage({
image: flatSvg,
format: targetFormat,
quality,
speed,
});
const buffer = Buffer.from(encoded.data);
res.setHeader('Content-Type', contentType);
res.setHeader('Content-Length', String(buffer.length));