71 lines
2.6 KiB
TypeScript
71 lines
2.6 KiB
TypeScript
import { decode, encode } from 'blurhash';
|
|
|
|
type Req =
|
|
| { id: number; type: 'blurhashToBase64Image'; payload: { blurhash: string; width: number; height: number } }
|
|
| { id: number; type: 'base64ImageToBlurhash'; payload: { base64Image: string } };
|
|
|
|
type Res =
|
|
| { id: number; ok: true; data: string }
|
|
| { id: number; ok: false; error: string };
|
|
|
|
const toBase64 = async (blurhash: string, width: number, height: number): Promise<string> => {
|
|
const pixels = decode(blurhash, width, height);
|
|
const canvas = new OffscreenCanvas(width, height);
|
|
const ctx = canvas.getContext('2d');
|
|
if (!ctx) throw new Error('No 2d context');
|
|
const imageData = ctx.createImageData(width, height);
|
|
imageData.data.set(pixels);
|
|
ctx.putImageData(imageData, 0, 0);
|
|
const blob = await canvas.convertToBlob({ type: 'image/png' });
|
|
const buf = new Uint8Array(await blob.arrayBuffer());
|
|
let bin = '';
|
|
for (let i = 0; i < buf.length; i++) bin += String.fromCharCode(buf[i]);
|
|
return `data:image/png;base64,${btoa(bin)}`;
|
|
};
|
|
|
|
const toBlurhash = async (base64Image: string): Promise<string> => {
|
|
const src = base64Image?.trim();
|
|
if (!src) throw new Error('Empty image data');
|
|
|
|
const resp = await fetch(src);
|
|
const blob = await resp.blob();
|
|
if (!blob.size) throw new Error('Image fetch returned empty blob');
|
|
|
|
const bitmap = await createImageBitmap(blob);
|
|
const { width, height } = bitmap;
|
|
if (!width || !height) {
|
|
bitmap.close();
|
|
throw new Error(`Image has invalid size ${width}x${height}`);
|
|
}
|
|
|
|
const canvas = new OffscreenCanvas(width, height);
|
|
const ctx = canvas.getContext('2d');
|
|
if (!ctx) {
|
|
bitmap.close();
|
|
throw new Error('No 2d context');
|
|
}
|
|
|
|
ctx.drawImage(bitmap, 0, 0, width, height);
|
|
bitmap.close();
|
|
|
|
const imageData = ctx.getImageData(0, 0, width, height);
|
|
return encode(imageData.data, imageData.width, imageData.height, 4, 4);
|
|
};
|
|
|
|
self.onmessage = async (e: MessageEvent<Req>) => {
|
|
const { id, type, payload } = e.data;
|
|
const reply = (res: Res) => (self as unknown as Worker).postMessage(res);
|
|
try {
|
|
if (type === 'blurhashToBase64Image') {
|
|
const data = await toBase64(payload.blurhash, payload.width, payload.height);
|
|
reply({ id, ok: true, data });
|
|
} else if (type === 'base64ImageToBlurhash') {
|
|
const data = await toBlurhash(payload.base64Image);
|
|
reply({ id, ok: true, data });
|
|
} else {
|
|
throw new Error(`Unknown type ${type}`);
|
|
}
|
|
} catch (err: any) {
|
|
reply({ id, ok: false, error: String(err?.message ?? err) });
|
|
}
|
|
}; |