@npio/internals
Version:
A free visual website editor, powered with your own SolidJS components.
111 lines (96 loc) • 2.49 kB
text/typescript
import sharp, { Sharp } from "sharp";
import { PassThrough, Readable } from "stream";
import { fileType, svgMime } from "../../media";
import { decode, encode } from "blurhash";
export const processImageUpload = function (params: {
contentType: string;
stream: Readable;
}) {
const type = fileType(params.contentType);
if (type !== "image") {
return {
stream: params.stream,
};
}
let sharpTransformer = sharp();
const isSvg = params.contentType === svgMime;
if (!isSvg) {
sharpTransformer = sharpTransformer.rotate();
}
const sharpStream = params.stream.pipe(sharpTransformer);
const outputStream = isSvg
? params.stream.pipe(new PassThrough())
: sharpStream;
const blurStream = isSvg ? sharpStream : sharpStream.clone();
const info = Promise.all([
sharpStream.metadata(),
sharpStream.stats(),
generateBlurhash(blurStream),
]).then(([meta, stats, { blurhash, dataUrl }]) => {
return {
meta,
stats,
blurhash,
dataUrl,
};
});
return {
stream: outputStream,
info,
};
};
const generateBlurhash = async (stream: Sharp) => {
const sharpStream = stream.rotate().raw().ensureAlpha();
const resized = await sharpStream
.resize(32)
.toBuffer({ resolveWithObject: true });
const xComponent = 4;
const yComponent = Math.round(
(xComponent / resized.info.width) * resized.info.height,
);
const blurhash = encode(
new Uint8ClampedArray(resized.data),
resized.info.width!,
resized.info.height!,
xComponent,
yComponent,
);
const dataUrl = await generateBlurDataUrl(
blurhash,
resized.info.width,
resized.info.height,
);
return { blurhash, dataUrl };
};
/**
* Source: https://github.com/woltapp/blurhash/issues/43#issuecomment-759112713
*/
const generateBlurDataUrl = async (
hash: string,
width: number,
height: number,
options = {
size: 10,
quality: 25,
},
) => {
const hashWidth = options?.size;
const hashHeight = Math.round(hashWidth * (height / width));
const pixels = decode(hash, hashWidth, hashHeight);
const resizedImageBuf = await sharp(Buffer.from(pixels), {
raw: {
channels: 4,
width: hashWidth,
height: hashHeight,
},
})
.modulate({
saturation: 1.2,
})
.jpeg({
overshootDeringing: true,
quality: options.quality,
})
.toBuffer();
return `data:image/jpeg;base64,${resizedImageBuf.toString("base64")}`;
};