UNPKG

@npio/internals

Version:

A free visual website editor, powered with your own SolidJS components.

111 lines (96 loc) 2.49 kB
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")}`; };