1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
<script lang="ts">
import { onDestroy } from 'svelte';
import { detect2dCanvasBlockOnCanvas } from './detect-canvas-block';
let { src, alt }: { src: string; alt: string } = $props();
let blobUrl = $state(undefined as undefined | string);
const blobify = async (image: HTMLImageElement) => {
if (!image.src.startsWith('blob:')) {
const canvas = document.createElement('canvas');
if (blobUrl) URL.revokeObjectURL(blobUrl);
const canvasOpts = {
desynchronized: true,
alpha: true,
willReadFrequently: false,
// change to unorm8 to use 8-bit colours (will break HDR images)
colorType: 'float16' as const,
// change to srgb if colours look off
colorSpace: 'display-p3' as const,
} as const;
const e = detect2dCanvasBlockOnCanvas(canvas, false, canvasOpts);
if (e) throw e;
try {
// See https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/naturalHeight
const cw = (canvas.width = image.naturalWidth);
const ch = (canvas.height = image.naturalHeight);
const ctx = canvas.getContext('2d', canvasOpts)!;
ctx.drawImage(image, 0, 0, cw, ch);
blobUrl = URL.createObjectURL(
await new Promise<Blob>((rs, rj) =>
canvas.toBlob((value) =>
value ? rs(value) : rj(new Error('No blob')),
),
),
);
} catch (error) {
console.error('Failed to get canvas data:', error);
}
}
};
onDestroy(() => {
if (blobUrl) URL.revokeObjectURL(blobUrl);
});
</script>
<img
class="pointer-events-none"
loading="lazy"
src={blobUrl ?? src}
{alt}
onload={(e) =>
blobify((e.currentTarget ?? e.target) as unknown as HTMLImageElement)}
/>
|