-
-
Notifications
You must be signed in to change notification settings - Fork 10
/
png.ts
71 lines (64 loc) · 2.09 KB
/
png.ts
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/* eslint-disable antfu/consistent-list-newline */
/**
* Encodes an RGBA image to a PNG data URI. RGB should not be premultiplied by A.
*
* @remarks
* This is optimized for speed and simplicity and does not optimize for size
* at all. This doesn't do any compression (all values are stored uncompressed).
*
* @see https://github.com/evanw/thumbhash
* @author Evan Wallace
* @license MIT
*/
export function rgbaToDataUri(
/** The width of the input image. Must be ≤100px. */
w: number,
/** The height of the input image. Must be ≤100px. */
h: number,
/** The pixels in the input image, row-by-row. Must have w*h*4 elements. */
rgba: ArrayLike<number>,
) {
const row = w * 4 + 1
const idat = 6 + h * (5 + row)
const bytes = [
137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0,
w >> 8, w & 255, 0, 0, h >> 8, h & 255, 8, 6, 0, 0, 0, 0, 0, 0, 0,
idat >>> 24, (idat >> 16) & 255, (idat >> 8) & 255, idat & 255,
73, 68, 65, 84, 120, 1,
]
const table = [
0, 498536548, 997073096, 651767980, 1994146192, 1802195444, 1303535960,
1342533948, -306674912, -267414716, -690576408, -882789492, -1687895376,
-2032938284, -1609899400, -1111625188,
]
let a = 1
let b = 0
for (let y = 0, i = 0, end = row - 1; y < h; y++, end += row - 1) {
bytes.push(y + 1 < h ? 0 : 1, row & 255, row >> 8, ~row & 255, (row >> 8) ^ 255, 0)
for (b = (b + a) % 65521; i < end; i++) {
const u = rgba[i] & 255
bytes.push(u)
a = (a + u) % 65521
b = (b + a) % 65521
}
}
bytes.push(
b >> 8, b & 255, a >> 8, a & 255, 0, 0, 0, 0,
0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130,
)
for (let [start, end] of [[12, 29], [37, 41 + idat]]) {
let c = ~0
for (let i = start; i < end; i++) {
c ^= bytes[i]
c = (c >>> 4) ^ table[c & 15]
c = (c >>> 4) ^ table[c & 15]
}
c = ~c
bytes[end++] = c >>> 24
bytes[end++] = (c >> 16) & 255
bytes[end++] = (c >> 8) & 255
bytes[end++] = c & 255
}
const base64 = globalThis.btoa(String.fromCharCode(...bytes))
return `data:image/png;base64,${base64}`
}