imagescript 3.2.1 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -10
- package/THIRD_PARTY_NOTICES.md +298 -2
- package/dist/animation.d.ts +25 -8
- package/dist/animation.js +44 -13
- package/dist/codecs/av1/cdef.js +197 -0
- package/dist/codecs/av1/data.js +141 -0
- package/dist/codecs/av1/decode.js +1769 -0
- package/dist/codecs/av1/filmgrain.js +219 -0
- package/dist/codecs/av1/frame.js +114 -0
- package/dist/codecs/av1/ipred.js +491 -0
- package/dist/codecs/av1/itx.js +950 -0
- package/dist/codecs/av1/loopfilter.js +149 -0
- package/dist/codecs/av1/msac.js +161 -0
- package/dist/codecs/av1/obu.js +516 -0
- package/dist/codecs/av1/postfilter.js +28 -0
- package/dist/codecs/av1/restoration.js +193 -0
- package/dist/codecs/av1/tables.js +160 -0
- package/dist/codecs/avif.js +581 -0
- package/dist/codecs/bmp.js +317 -0
- package/dist/codecs/crc.js +5 -0
- package/dist/codecs/gif.js +10 -35
- package/dist/codecs/hdr.d.ts +25 -0
- package/dist/codecs/hdr.js +201 -0
- package/dist/codecs/ico.js +48 -0
- package/dist/codecs/jpeg.js +11 -28
- package/dist/codecs/jxl/bits.js +126 -0
- package/dist/codecs/jxl/blend.js +117 -0
- package/dist/codecs/jxl/color.js +116 -0
- package/dist/codecs/jxl/dct.js +404 -0
- package/dist/codecs/jxl/entropy.js +556 -0
- package/dist/codecs/jxl/features.js +325 -0
- package/dist/codecs/jxl/filters.js +119 -0
- package/dist/codecs/jxl/frame-header.js +195 -0
- package/dist/codecs/jxl/frame.js +319 -0
- package/dist/codecs/jxl/headers.js +249 -0
- package/dist/codecs/jxl/idct.js +484 -0
- package/dist/codecs/jxl/modular.js +688 -0
- package/dist/codecs/jxl/quant.js +219 -0
- package/dist/codecs/jxl/upsample.js +52 -0
- package/dist/codecs/jxl/vardct.js +472 -0
- package/dist/codecs/jxl.js +89 -0
- package/dist/codecs/netpbm.js +239 -0
- package/dist/codecs/png.js +22 -10
- package/dist/codecs/qoi.js +150 -0
- package/dist/codecs/tga.js +3 -1
- package/dist/codecs/tiff.js +7 -2
- package/dist/codecs/vp8-enc-dsp.js +932 -0
- package/dist/codecs/vp8-enc-tables.js +78 -0
- package/dist/codecs/vp8-enc.js +1930 -0
- package/dist/codecs/vp8.js +3 -1
- package/dist/codecs/vp8l.js +126 -58
- package/dist/codecs/webp.js +47 -11
- package/dist/codecs/zlib.js +81 -69
- package/dist/frame.d.ts +1 -0
- package/dist/frame.js +4 -2
- package/dist/gif.d.ts +1 -0
- package/dist/gif.js +2 -2
- package/dist/image-type.d.ts +15 -1
- package/dist/image-type.js +45 -2
- package/dist/image.d.ts +89 -13
- package/dist/image.js +266 -113
- package/dist/index.d.ts +3 -2
- package/dist/index.js +4 -4
- package/dist/render/bidi.js +350 -0
- package/dist/render/buffer.js +362 -0
- package/dist/render/color.js +164 -0
- package/dist/render/colr.js +523 -0
- package/dist/render/font.js +173 -241
- package/dist/render/hinting.js +38 -22
- package/dist/render/layout.js +1553 -0
- package/dist/render/linebreak.js +210 -0
- package/dist/render/segment.js +106 -0
- package/dist/render/shape.js +984 -0
- package/dist/render/shapers/arabic.js +284 -0
- package/dist/render/shapers/hangul.js +142 -0
- package/dist/render/shapers/hebrew.js +49 -0
- package/dist/render/shapers/index.js +52 -0
- package/dist/render/shapers/indic.js +579 -0
- package/dist/render/shapers/khmer.js +104 -0
- package/dist/render/shapers/machine.js +129 -0
- package/dist/render/shapers/myanmar.js +121 -0
- package/dist/render/shapers/syllabic.js +100 -0
- package/dist/render/shapers/thai.js +125 -0
- package/dist/render/shapers/use.js +235 -0
- package/dist/render/svg.js +102 -23
- package/dist/render/text.js +373 -103
- package/dist/render/unicode-data.js +63 -0
- package/dist/render/unicode.js +50 -0
- package/dist/text-layout.d.ts +4 -1
- package/dist/text-layout.js +2 -2
- package/dist/util/alpha.js +43 -0
- package/dist/util/blur.js +20 -33
- package/dist/util/bytes.js +40 -0
- package/dist/util/limits.js +34 -0
- package/dist/util/options.js +7 -0
- package/dist/util/resample.js +126 -7
- package/dist/util/rotate.js +43 -0
- package/dist/version.js +1 -1
- package/licenses/HarfBuzz.txt +42 -0
- package/licenses/Unicode-3.0.txt +39 -0
- package/package.json +25 -2
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { MAX_PIXELS, checkPixels } from "../util/limits.js";
|
|
2
|
+
const BI_RGB = 0, BI_RLE8 = 1, BI_RLE4 = 2, BI_BITFIELDS = 3, BI_JPEG = 4, BI_PNG = 5, BI_ALPHABITFIELDS = 6;
|
|
3
|
+
const u16 = (d, o) => d[o] | d[o + 1] << 8;
|
|
4
|
+
const u32 = (d, o) => (d[o] | d[o + 1] << 8 | d[o + 2] << 16 | d[o + 3] << 24) >>> 0;
|
|
5
|
+
const i32 = (d, o) => d[o] | d[o + 1] << 8 | d[o + 2] << 16 | d[o + 3] << 24;
|
|
6
|
+
const WINDOWS_HEADERS = [40, 52, 56, 108, 124];
|
|
7
|
+
const isHeaderSize = (size) => size === 12 || WINDOWS_HEADERS.includes(size) || (size >= 16 && size <= 64 && size % 4 === 0);
|
|
8
|
+
export function isBMP(d) {
|
|
9
|
+
return d.length >= 26 && d[0] === 0x42 && d[1] === 0x4d && isHeaderSize(u32(d, 14));
|
|
10
|
+
}
|
|
11
|
+
function channel(mask) {
|
|
12
|
+
mask >>>= 0;
|
|
13
|
+
let shift = 0;
|
|
14
|
+
if (mask)
|
|
15
|
+
while (!(mask >>> shift & 1))
|
|
16
|
+
shift++;
|
|
17
|
+
const max = mask >>> shift;
|
|
18
|
+
let table = null;
|
|
19
|
+
if (max && max < 65536) {
|
|
20
|
+
table = new Uint8Array(max + 1);
|
|
21
|
+
for (let v = 0; v <= max; v++)
|
|
22
|
+
table[v] = Math.round(v * 255 / max);
|
|
23
|
+
}
|
|
24
|
+
return { mask, shift, max, table };
|
|
25
|
+
}
|
|
26
|
+
function sample(c, v) {
|
|
27
|
+
if (!c.max)
|
|
28
|
+
return 0;
|
|
29
|
+
const x = (v & c.mask) >>> c.shift;
|
|
30
|
+
return c.table ? c.table[x] : Math.round(x * 255 / c.max);
|
|
31
|
+
}
|
|
32
|
+
function readInfo(d, o) {
|
|
33
|
+
const size = u32(d, o);
|
|
34
|
+
if (!isHeaderSize(size))
|
|
35
|
+
throw new Error(`BMP: unknown header size ${size}`);
|
|
36
|
+
if (o + size > d.length)
|
|
37
|
+
throw new Error('BMP: truncated header');
|
|
38
|
+
if (size === 12) {
|
|
39
|
+
return { width: u16(d, o + 4), height: u16(d, o + 6), topDown: false, bits: u16(d, o + 10), compression: BI_RGB, headerEnd: o + 12, entryBytes: 3, colors: 0, masks: null };
|
|
40
|
+
}
|
|
41
|
+
const os2 = !WINDOWS_HEADERS.includes(size);
|
|
42
|
+
const width = i32(d, o + 4), rawHeight = i32(d, o + 8);
|
|
43
|
+
const bits = u16(d, o + 14);
|
|
44
|
+
let compression = size >= 20 ? u32(d, o + 16) : BI_RGB;
|
|
45
|
+
if (os2 && (compression === 3 || compression === 4))
|
|
46
|
+
throw new Error(`BMP: unsupported OS/2 compression (${compression === 3 ? 'Huffman 1D' : 'RLE24'})`);
|
|
47
|
+
const colors = size >= 36 ? u32(d, o + 32) : 0;
|
|
48
|
+
let masks = null;
|
|
49
|
+
let headerEnd = o + size;
|
|
50
|
+
if (compression === BI_BITFIELDS || compression === BI_ALPHABITFIELDS) {
|
|
51
|
+
if (size >= 52)
|
|
52
|
+
masks = [u32(d, o + 40), u32(d, o + 44), u32(d, o + 48), size >= 56 ? u32(d, o + 52) : 0];
|
|
53
|
+
else {
|
|
54
|
+
const count = compression === BI_ALPHABITFIELDS ? 4 : 3;
|
|
55
|
+
if (headerEnd + count * 4 > d.length)
|
|
56
|
+
throw new Error('BMP: truncated bit fields');
|
|
57
|
+
masks = [u32(d, headerEnd), u32(d, headerEnd + 4), u32(d, headerEnd + 8), count === 4 ? u32(d, headerEnd + 12) : 0];
|
|
58
|
+
headerEnd += count * 4;
|
|
59
|
+
}
|
|
60
|
+
compression = BI_BITFIELDS;
|
|
61
|
+
}
|
|
62
|
+
if (width <= 0 || rawHeight === 0 || rawHeight === -0x80000000)
|
|
63
|
+
throw new Error(`BMP: invalid dimensions ${width}x${rawHeight}`);
|
|
64
|
+
return { width, height: Math.abs(rawHeight), topDown: rawHeight < 0, bits, compression, headerEnd, entryBytes: 4, colors, masks };
|
|
65
|
+
}
|
|
66
|
+
function readPalette(d, info, end) {
|
|
67
|
+
const { bits, entryBytes, headerEnd } = info;
|
|
68
|
+
if (bits > 8)
|
|
69
|
+
return null;
|
|
70
|
+
let count = info.colors || 1 << bits;
|
|
71
|
+
count = Math.max(0, Math.min(count, Math.floor((Math.min(end, d.length) - headerEnd) / entryBytes)));
|
|
72
|
+
const palette = new Uint32Array(Math.max(count, 1 << bits));
|
|
73
|
+
palette.fill(0xff000000);
|
|
74
|
+
for (let i = 0; i < count; i++) {
|
|
75
|
+
const p = headerEnd + i * entryBytes;
|
|
76
|
+
palette[i] = (0xff000000 | d[p] << 16 | d[p + 1] << 8 | d[p + 2]) >>> 0;
|
|
77
|
+
}
|
|
78
|
+
return palette;
|
|
79
|
+
}
|
|
80
|
+
export function decodeDIB(d, o, pixelOffset, maxPixels, ico = false) {
|
|
81
|
+
const info = readInfo(d, o);
|
|
82
|
+
const { width, bits, compression, topDown } = info;
|
|
83
|
+
const height = ico ? info.height >> 1 : info.height;
|
|
84
|
+
if (!height)
|
|
85
|
+
throw new Error('BMP: invalid dimensions');
|
|
86
|
+
if (compression === BI_JPEG || compression === BI_PNG) {
|
|
87
|
+
const start = pixelOffset ?? info.headerEnd;
|
|
88
|
+
return { embedded: compression === BI_JPEG ? 'jpeg' : 'png', data: d.subarray(start) };
|
|
89
|
+
}
|
|
90
|
+
if (![1, 2, 4, 8, 16, 24, 32, 64].includes(bits))
|
|
91
|
+
throw new Error(`BMP: unsupported bit depth ${bits}`);
|
|
92
|
+
if (compression === BI_RLE8 && bits !== 8 || compression === BI_RLE4 && bits !== 4)
|
|
93
|
+
throw new Error('BMP: RLE compression of the wrong bit depth');
|
|
94
|
+
if (compression === BI_BITFIELDS && bits !== 16 && bits !== 32)
|
|
95
|
+
throw new Error('BMP: bit fields need 16 or 32 bit pixels');
|
|
96
|
+
if (compression > BI_PNG)
|
|
97
|
+
throw new Error(`BMP: unsupported compression ${compression}`);
|
|
98
|
+
checkPixels(width, height, maxPixels);
|
|
99
|
+
const palette = readPalette(d, info, pixelOffset ?? d.length);
|
|
100
|
+
const start = pixelOffset ?? info.headerEnd + (bits <= 8 ? info.colors || 1 << bits : info.colors) * info.entryBytes;
|
|
101
|
+
const pixels = new Uint8Array(width * height * 4);
|
|
102
|
+
const out = new Uint32Array(pixels.buffer);
|
|
103
|
+
const row = (y) => (topDown ? y : height - 1 - y) * width;
|
|
104
|
+
if (compression === BI_RLE8 || compression === BI_RLE4) {
|
|
105
|
+
decodeRLE(d, start, width, height, compression === BI_RLE4, palette, out, row);
|
|
106
|
+
return { width, height, pixels };
|
|
107
|
+
}
|
|
108
|
+
const stride = (width * bits + 31 >>> 5) * 4;
|
|
109
|
+
if (start + stride * (height - 1) + Math.ceil(width * bits / 8) > d.length)
|
|
110
|
+
throw new Error('BMP: truncated pixel data');
|
|
111
|
+
if (palette) {
|
|
112
|
+
const mask = (1 << bits) - 1;
|
|
113
|
+
for (let y = 0; y < height; y++) {
|
|
114
|
+
const r = start + y * stride, o = row(y);
|
|
115
|
+
for (let x = 0; x < width; x++) {
|
|
116
|
+
const bit = x * bits;
|
|
117
|
+
out[o + x] = palette[d[r + (bit >> 3)] >> (8 - bits - (bit & 7)) & mask];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else if (bits === 24) {
|
|
122
|
+
for (let y = 0; y < height; y++) {
|
|
123
|
+
const r = start + y * stride, o = row(y);
|
|
124
|
+
for (let x = 0; x < width; x++) {
|
|
125
|
+
const p = r + x * 3;
|
|
126
|
+
out[o + x] = (0xff000000 | d[p] << 16 | d[p + 1] << 8 | d[p + 2]) >>> 0;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else if (bits === 64) {
|
|
131
|
+
const srgb = (v) => {
|
|
132
|
+
const c = Math.min(1, Math.max(0, v / 8192));
|
|
133
|
+
return Math.round(255 * (c <= 0.0031308 ? 12.92 * c : 1.055 * c ** (1 / 2.4) - 0.055));
|
|
134
|
+
};
|
|
135
|
+
const s16 = (p) => d[p] | d[p + 1] << 8 << 16 >> 16;
|
|
136
|
+
for (let y = 0; y < height; y++) {
|
|
137
|
+
const r = start + y * stride, o = row(y) * 4;
|
|
138
|
+
for (let x = 0; x < width; x++) {
|
|
139
|
+
const p = r + x * 8, q = o + x * 4;
|
|
140
|
+
pixels[q] = srgb(s16(p + 4));
|
|
141
|
+
pixels[q + 1] = srgb(s16(p + 2));
|
|
142
|
+
pixels[q + 2] = srgb(s16(p));
|
|
143
|
+
pixels[q + 3] = Math.round(Math.min(1, Math.max(0, s16(p + 6) / 8192)) * 255);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
const masks = info.masks ?? (bits === 16 ? [0x7c00, 0x3e0, 0x1f, 0] : [0xff0000, 0xff00, 0xff, ico && bits === 32 ? 0xff000000 : 0]);
|
|
149
|
+
const [cr, cg, cb, ca] = masks.map(channel);
|
|
150
|
+
const fast = bits === 32 && cr.mask === 0xff0000 && cg.mask === 0xff00 && cb.mask === 0xff;
|
|
151
|
+
let alphaSeen = false;
|
|
152
|
+
for (let y = 0; y < height; y++) {
|
|
153
|
+
const r = start + y * stride, o = row(y);
|
|
154
|
+
for (let x = 0; x < width; x++) {
|
|
155
|
+
const v = bits === 16 ? u16(d, r + x * 2) : u32(d, r + x * 4);
|
|
156
|
+
const a = ca.max ? sample(ca, v) : 255;
|
|
157
|
+
if (ca.max && a)
|
|
158
|
+
alphaSeen = true;
|
|
159
|
+
out[o + x] = fast
|
|
160
|
+
? (a << 24 | (v & 0xff) << 16 | v & 0xff00 | v >> 16 & 0xff) >>> 0
|
|
161
|
+
: (a << 24 | sample(cb, v) << 16 | sample(cg, v) << 8 | sample(cr, v)) >>> 0;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (ca.max && !alphaSeen)
|
|
165
|
+
for (let i = 0; i < out.length; i++)
|
|
166
|
+
out[i] |= 0xff000000;
|
|
167
|
+
if (ico && bits === 32 && alphaSeen)
|
|
168
|
+
return { width, height, pixels };
|
|
169
|
+
}
|
|
170
|
+
if (ico) {
|
|
171
|
+
const maskStart = start + stride * height, maskStride = (width + 31 >>> 5) * 4;
|
|
172
|
+
if (maskStart + maskStride * (height - 1) + Math.ceil(width / 8) <= d.length) {
|
|
173
|
+
for (let y = 0; y < height; y++) {
|
|
174
|
+
const r = maskStart + y * maskStride, o = row(y);
|
|
175
|
+
for (let x = 0; x < width; x++)
|
|
176
|
+
if (d[r + (x >> 3)] >> (7 - (x & 7)) & 1)
|
|
177
|
+
out[o + x] = 0;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return { width, height, pixels };
|
|
182
|
+
}
|
|
183
|
+
function decodeRLE(d, o, width, height, four, palette, out, row) {
|
|
184
|
+
let x = 0, y = 0;
|
|
185
|
+
const put = (index) => {
|
|
186
|
+
if (x < width && y < height)
|
|
187
|
+
out[row(y) + x] = palette[index];
|
|
188
|
+
x++;
|
|
189
|
+
};
|
|
190
|
+
while (y < height) {
|
|
191
|
+
if (o + 1 >= d.length)
|
|
192
|
+
throw new Error('BMP: truncated RLE data');
|
|
193
|
+
const n = d[o], v = d[o + 1];
|
|
194
|
+
o += 2;
|
|
195
|
+
if (n) {
|
|
196
|
+
for (let i = 0; i < n; i++)
|
|
197
|
+
put(four ? (i & 1 ? v & 15 : v >> 4) : v);
|
|
198
|
+
}
|
|
199
|
+
else if (v === 0) {
|
|
200
|
+
x = 0;
|
|
201
|
+
y++;
|
|
202
|
+
}
|
|
203
|
+
else if (v === 1) {
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
else if (v === 2) {
|
|
207
|
+
if (o + 1 >= d.length)
|
|
208
|
+
throw new Error('BMP: truncated RLE data');
|
|
209
|
+
x += d[o];
|
|
210
|
+
y += d[o + 1];
|
|
211
|
+
o += 2;
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
const bytes = four ? v + 1 >> 1 : v;
|
|
215
|
+
if (o + bytes > d.length)
|
|
216
|
+
throw new Error('BMP: truncated RLE data');
|
|
217
|
+
for (let i = 0; i < v; i++)
|
|
218
|
+
put(four ? (i & 1 ? d[o + (i >> 1)] & 15 : d[o + (i >> 1)] >> 4) : d[o + i]);
|
|
219
|
+
o += bytes + (bytes & 1);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
export function decode(d, maxPixels = MAX_PIXELS) {
|
|
224
|
+
if (!isBMP(d))
|
|
225
|
+
throw new Error('BMP: invalid header');
|
|
226
|
+
const offset = u32(d, 10);
|
|
227
|
+
return decodeDIB(d, 14, offset >= 26 && offset < d.length ? offset : null, maxPixels);
|
|
228
|
+
}
|
|
229
|
+
export function encode(rgba, width, height) {
|
|
230
|
+
if (width < 1 || height < 1)
|
|
231
|
+
throw new RangeError('BMP: dimensions must be at least 1');
|
|
232
|
+
const count = width * height;
|
|
233
|
+
if (rgba.byteOffset % 4)
|
|
234
|
+
rgba = rgba.slice(0, count * 4);
|
|
235
|
+
const pixels = new Uint32Array(rgba.buffer, rgba.byteOffset, count);
|
|
236
|
+
let opaque = true;
|
|
237
|
+
for (let i = 3; i < count * 4; i += 4)
|
|
238
|
+
if (rgba[i] !== 255) {
|
|
239
|
+
opaque = false;
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
let colors = null;
|
|
243
|
+
if (opaque) {
|
|
244
|
+
colors = new Map();
|
|
245
|
+
for (let i = 0; i < count && colors; i++) {
|
|
246
|
+
if (!colors.has(pixels[i])) {
|
|
247
|
+
if (colors.size === 256)
|
|
248
|
+
colors = null;
|
|
249
|
+
else
|
|
250
|
+
colors.set(pixels[i], colors.size);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const bits = colors ? colors.size <= 2 ? 1 : colors.size <= 16 ? 4 : 8 : opaque ? 24 : 32;
|
|
255
|
+
const headerSize = bits === 32 ? 108 : 40;
|
|
256
|
+
const paletteSize = colors ? (1 << bits) * 4 : 0;
|
|
257
|
+
const stride = (width * bits + 31 >>> 5) * 4;
|
|
258
|
+
const offset = 14 + headerSize + paletteSize;
|
|
259
|
+
const size = offset + stride * height;
|
|
260
|
+
if (size > 0xffffffff)
|
|
261
|
+
throw new RangeError('BMP: image too large');
|
|
262
|
+
const out = new Uint8Array(size);
|
|
263
|
+
const view = new DataView(out.buffer);
|
|
264
|
+
out[0] = 0x42;
|
|
265
|
+
out[1] = 0x4d;
|
|
266
|
+
view.setUint32(2, size, true);
|
|
267
|
+
view.setUint32(10, offset, true);
|
|
268
|
+
view.setUint32(14, headerSize, true);
|
|
269
|
+
view.setInt32(18, width, true);
|
|
270
|
+
view.setInt32(22, height, true);
|
|
271
|
+
view.setUint16(26, 1, true);
|
|
272
|
+
view.setUint16(28, bits, true);
|
|
273
|
+
view.setUint32(30, bits === 32 ? BI_BITFIELDS : BI_RGB, true);
|
|
274
|
+
view.setUint32(34, stride * height, true);
|
|
275
|
+
view.setUint32(38, 2835, true);
|
|
276
|
+
view.setUint32(42, 2835, true);
|
|
277
|
+
if (bits === 32) {
|
|
278
|
+
view.setUint32(54, 0x00ff0000, true);
|
|
279
|
+
view.setUint32(58, 0x0000ff00, true);
|
|
280
|
+
view.setUint32(62, 0x000000ff, true);
|
|
281
|
+
view.setUint32(66, 0xff000000, true);
|
|
282
|
+
view.setUint32(70, 0x73524742, true);
|
|
283
|
+
}
|
|
284
|
+
if (colors)
|
|
285
|
+
for (const [color, index] of colors) {
|
|
286
|
+
const p = 14 + headerSize + index * 4;
|
|
287
|
+
out[p] = color >> 16 & 255;
|
|
288
|
+
out[p + 1] = color >> 8 & 255;
|
|
289
|
+
out[p + 2] = color & 255;
|
|
290
|
+
}
|
|
291
|
+
for (let y = 0; y < height; y++) {
|
|
292
|
+
const r = offset + (height - 1 - y) * stride, s = y * width * 4;
|
|
293
|
+
if (colors) {
|
|
294
|
+
const map = colors;
|
|
295
|
+
for (let x = 0; x < width; x++)
|
|
296
|
+
out[r + (x * bits >> 3)] |= map.get(pixels[y * width + x]) << (8 - bits - (x * bits & 7));
|
|
297
|
+
}
|
|
298
|
+
else if (bits === 24) {
|
|
299
|
+
for (let x = 0; x < width; x++) {
|
|
300
|
+
const p = r + x * 3, q = s + x * 4;
|
|
301
|
+
out[p] = rgba[q + 2];
|
|
302
|
+
out[p + 1] = rgba[q + 1];
|
|
303
|
+
out[p + 2] = rgba[q];
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
for (let x = 0; x < width; x++) {
|
|
308
|
+
const p = r + x * 4, q = s + x * 4;
|
|
309
|
+
out[p] = rgba[q + 2];
|
|
310
|
+
out[p + 1] = rgba[q + 1];
|
|
311
|
+
out[p + 2] = rgba[q];
|
|
312
|
+
out[p + 3] = rgba[q + 3];
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return out;
|
|
317
|
+
}
|
package/dist/codecs/crc.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Slice-by-16 CRC-32, derived from "Fast CRC32 in JavaScript" (https://gist.github.com/101arrowz/e58695f7ccfdf74f60ba22018093edea)
|
|
3
3
|
* Copyright (c) 2021 Arjun Barrett, MIT License (see THIRD_PARTY_NOTICES.md)
|
|
4
4
|
*/
|
|
5
|
+
import { nodeZlib } from "./zlib.js";
|
|
5
6
|
const TABLE = new Int32Array(16 * 256);
|
|
6
7
|
for (let n = 0; n < 256; n++) {
|
|
7
8
|
let c = n;
|
|
@@ -14,7 +15,11 @@ for (let n = 0; n < 256; n++) {
|
|
|
14
15
|
for (let k = 256; k < TABLE.length; k += 256)
|
|
15
16
|
c = TABLE[k + n] = TABLE[c & 0xff] ^ c >>> 8;
|
|
16
17
|
}
|
|
18
|
+
const native = nodeZlib && nodeZlib.crc32 ? nodeZlib.crc32.bind(nodeZlib) : null;
|
|
19
|
+
const NATIVE_MIN = 64;
|
|
17
20
|
export function crc32(buffer) {
|
|
21
|
+
if (native && buffer.length >= NATIVE_MIN)
|
|
22
|
+
return native(buffer) >>> 0;
|
|
18
23
|
let crc = -1, i = 0;
|
|
19
24
|
for (const end = buffer.length - 16; i <= end; i += 16) {
|
|
20
25
|
crc = TABLE[3840 + ((buffer[i] ^ crc) & 0xff)] ^ TABLE[3584 + ((buffer[i + 1] ^ crc >> 8) & 0xff)]
|
package/dist/codecs/gif.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { ByteWriter } from "../util/bytes.js";
|
|
1
2
|
import { quantize } from "./quantize.js";
|
|
3
|
+
import { MAX_PIXELS, PixelBudget, checkPixels } from "../util/limits.js";
|
|
2
4
|
const DISPOSAL_TO_GIF = [0, 1, 3, 2];
|
|
3
5
|
const DISPOSAL_FROM_GIF = [0, 1, 3, 2, 0, 0, 0, 0];
|
|
4
6
|
const MAX_CODE = 4095;
|
|
5
7
|
const UNEXPECTED_END = new Error('GIF: unexpected end of data');
|
|
6
8
|
export function encode({ width, height, loopCount = -1, frames }, quality = 95, comments = []) {
|
|
7
|
-
const out = new
|
|
9
|
+
const out = new GIFWriter();
|
|
8
10
|
out.bytes(0x47, 0x49, 0x46, 0x38, 0x39, 0x61);
|
|
9
11
|
out.u16(width);
|
|
10
12
|
out.u16(height);
|
|
@@ -47,7 +49,7 @@ export function encode({ width, height, loopCount = -1, frames }, quality = 95,
|
|
|
47
49
|
return out.finish();
|
|
48
50
|
}
|
|
49
51
|
function lzwEncode(indices, minCodeSize) {
|
|
50
|
-
const out = new
|
|
52
|
+
const out = new GIFWriter(indices.length);
|
|
51
53
|
const clear = 1 << minCodeSize;
|
|
52
54
|
const eoi = clear + 1;
|
|
53
55
|
const dictionary = new Int16Array(4096 * 256);
|
|
@@ -110,7 +112,7 @@ function writeSubBlocks(out, data) {
|
|
|
110
112
|
}
|
|
111
113
|
out.byte(0x00);
|
|
112
114
|
}
|
|
113
|
-
export function decode(data, onlyFirstFrame = false) {
|
|
115
|
+
export function decode(data, onlyFirstFrame = false, maxPixels = MAX_PIXELS) {
|
|
114
116
|
const reader = new ByteReader(data);
|
|
115
117
|
const signature = reader.string(6);
|
|
116
118
|
if (signature !== 'GIF87a' && signature !== 'GIF89a')
|
|
@@ -123,6 +125,7 @@ export function decode(data, onlyFirstFrame = false) {
|
|
|
123
125
|
const frames = [];
|
|
124
126
|
let loopCount = 0;
|
|
125
127
|
let control = null;
|
|
128
|
+
const budget = new PixelBudget(maxPixels);
|
|
126
129
|
try {
|
|
127
130
|
parse();
|
|
128
131
|
}
|
|
@@ -130,6 +133,7 @@ export function decode(data, onlyFirstFrame = false) {
|
|
|
130
133
|
if (err !== UNEXPECTED_END)
|
|
131
134
|
throw err;
|
|
132
135
|
}
|
|
136
|
+
checkPixels(width, height, maxPixels, Math.max(1, frames.length));
|
|
133
137
|
return { width, height, loopCount, frames };
|
|
134
138
|
function parse() {
|
|
135
139
|
while (reader.offset < data.length) {
|
|
@@ -183,6 +187,7 @@ export function decode(data, onlyFirstFrame = false) {
|
|
|
183
187
|
const table = localTable || globalTable;
|
|
184
188
|
const minCodeSize = reader.u8();
|
|
185
189
|
const compressed = readSubBlocks(reader);
|
|
190
|
+
budget.take(fw, fh);
|
|
186
191
|
const indices = lzwDecode(compressed, minCodeSize, fw * fh);
|
|
187
192
|
const { dispose = 0, delay = 0, transparentIndex = -1 } = control || {};
|
|
188
193
|
control = null;
|
|
@@ -376,42 +381,12 @@ class ByteReader {
|
|
|
376
381
|
return String.fromCharCode(...this.slice(length));
|
|
377
382
|
}
|
|
378
383
|
}
|
|
379
|
-
class ByteWriter {
|
|
380
|
-
buffer;
|
|
381
|
-
length;
|
|
382
|
-
constructor(capacity = 1024) {
|
|
383
|
-
this.buffer = new Uint8Array(Math.max(16, capacity));
|
|
384
|
-
this.length = 0;
|
|
385
|
-
}
|
|
386
|
-
reserve(size) {
|
|
387
|
-
if (this.length + size <= this.buffer.length)
|
|
388
|
-
return;
|
|
389
|
-
const buffer = new Uint8Array(Math.max(this.buffer.length * 2, this.length + size));
|
|
390
|
-
buffer.set(this.buffer.subarray(0, this.length));
|
|
391
|
-
this.buffer = buffer;
|
|
392
|
-
}
|
|
393
|
-
byte(value) {
|
|
394
|
-
this.reserve(1);
|
|
395
|
-
this.buffer[this.length++] = value;
|
|
396
|
-
}
|
|
397
|
-
bytes(...values) {
|
|
398
|
-
for (const value of values)
|
|
399
|
-
this.byte(value);
|
|
400
|
-
}
|
|
384
|
+
class GIFWriter extends ByteWriter {
|
|
401
385
|
u16(value) {
|
|
402
|
-
this.
|
|
403
|
-
this.byte(value >> 8 & 0xff);
|
|
386
|
+
this.bytes(value & 0xff, value >> 8 & 0xff);
|
|
404
387
|
}
|
|
405
388
|
string(value) {
|
|
406
389
|
for (let i = 0; i < value.length; i++)
|
|
407
390
|
this.byte(value.charCodeAt(i));
|
|
408
391
|
}
|
|
409
|
-
array(values) {
|
|
410
|
-
this.reserve(values.length);
|
|
411
|
-
this.buffer.set(values, this.length);
|
|
412
|
-
this.length += values.length;
|
|
413
|
-
}
|
|
414
|
-
finish() {
|
|
415
|
-
return this.buffer.slice(0, this.length);
|
|
416
|
-
}
|
|
417
392
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** How linear light brighter than white becomes 8 bit sRGB: tone mapped by Reinhard's operator, or clamped */
|
|
2
|
+
export type ToneMapping = 'reinhard' | 'clamp';
|
|
3
|
+
/**
|
|
4
|
+
* Linear light RGB (3 values per pixel) as sRGB RGBA. Reinhard's extended operator ("Photographic Tone Reproduction
|
|
5
|
+
* for Digital Images", 2002, equation 4) without its automatic exposure, which makes dark scenes like nights grey:
|
|
6
|
+
* the luminance L becomes L (1 + L / Lw²) / (1 + L), the brightest pixel's (at least 1) Lw white, and the channels are
|
|
7
|
+
* scaled by that, keeping their hue; images no brighter than white stay as they are
|
|
8
|
+
*/
|
|
9
|
+
export declare function toSRGB(rgb: Float32Array, toneMapping: ToneMapping): Uint8Array;
|
|
10
|
+
/** the white of SDR content in HDR (BT.2408, cd/m²): linear light 1 */
|
|
11
|
+
export declare const REFERENCE_WHITE = 203;
|
|
12
|
+
/**
|
|
13
|
+
* Non-linear PQ (SMPTE ST 2084, transfer 16) or HLG (BT.2100, 18) RGB (3 values per pixel, primaries as CICP codes) as
|
|
14
|
+
* linear light (in place) with BT.709 primaries, 1 being the reference white (203 cd/m²): HLG is displayed as on a
|
|
15
|
+
* 1000 cd/m² display (BT.2100's OOTF, gamma 1.2)
|
|
16
|
+
*/
|
|
17
|
+
export declare function hdrToLinear(rgb: Float32Array, transfer: number, primaries: number): Float32Array;
|
|
18
|
+
/** Whether the data starts like a Radiance HDR */
|
|
19
|
+
export declare function isHDR(d: Uint8Array): boolean;
|
|
20
|
+
/** Decodes a Radiance HDR into RGBA */
|
|
21
|
+
export declare function decode(d: Uint8Array, maxPixels?: number, toneMapping?: ToneMapping): {
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
pixels: Uint8Array;
|
|
25
|
+
};
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { MAX_PIXELS, checkPixels } from "../util/limits.js";
|
|
2
|
+
function linearToSRGB(v) {
|
|
3
|
+
if (!(v > 0))
|
|
4
|
+
return 0;
|
|
5
|
+
if (v >= 1)
|
|
6
|
+
return 255;
|
|
7
|
+
return Math.round(255 * (v <= 0.0031308 ? 12.92 * v : 1.055 * v ** (1 / 2.4) - 0.055));
|
|
8
|
+
}
|
|
9
|
+
export function toSRGB(rgb, toneMapping) {
|
|
10
|
+
const count = rgb.length / 3;
|
|
11
|
+
const luminance = (i) => 0.2126 * rgb[i] + 0.7152 * rgb[i + 1] + 0.0722 * rgb[i + 2];
|
|
12
|
+
let white = 1;
|
|
13
|
+
if (toneMapping === 'reinhard')
|
|
14
|
+
for (let i = 0; i < rgb.length; i += 3)
|
|
15
|
+
white = Math.max(white, luminance(i));
|
|
16
|
+
const white2 = white * white;
|
|
17
|
+
const pixels = new Uint8Array(count * 4);
|
|
18
|
+
for (let i = 0, p = 0; i < rgb.length; i += 3, p += 4) {
|
|
19
|
+
let scale = 1;
|
|
20
|
+
if (white > 1) {
|
|
21
|
+
const l = Math.max(0, luminance(i));
|
|
22
|
+
scale = (1 + l / white2) / (1 + l);
|
|
23
|
+
}
|
|
24
|
+
pixels[p] = linearToSRGB(rgb[i] * scale);
|
|
25
|
+
pixels[p + 1] = linearToSRGB(rgb[i + 1] * scale);
|
|
26
|
+
pixels[p + 2] = linearToSRGB(rgb[i + 2] * scale);
|
|
27
|
+
pixels[p + 3] = 255;
|
|
28
|
+
}
|
|
29
|
+
return pixels;
|
|
30
|
+
}
|
|
31
|
+
const PRIMARIES_BT2020 = 9, PRIMARIES_P3 = 12;
|
|
32
|
+
export const REFERENCE_WHITE = 203;
|
|
33
|
+
export function hdrToLinear(rgb, transfer, primaries) {
|
|
34
|
+
const m1 = 2610 / 16384, m2 = 2523 / 4096 * 128, c1 = 3424 / 4096, c2 = 2413 / 4096 * 32, c3 = 2392 / 4096 * 32;
|
|
35
|
+
const pq = (v) => {
|
|
36
|
+
const e = (v <= 0 ? 0 : v >= 1 ? 1 : v) ** (1 / m2);
|
|
37
|
+
return 10000 / REFERENCE_WHITE * (Math.max(e - c1, 0) / (c2 - c3 * e)) ** (1 / m1);
|
|
38
|
+
};
|
|
39
|
+
const a = 0.17883277, b = 1 - 4 * a, c = 0.5 - a * Math.log(4 * a);
|
|
40
|
+
const hlg = (v) => {
|
|
41
|
+
const e = v <= 0 ? 0 : v >= 1 ? 1 : v;
|
|
42
|
+
return e <= 0.5 ? e * e / 3 : (Math.exp((e - c) / a) + b) / 12;
|
|
43
|
+
};
|
|
44
|
+
const matrix = primaries === PRIMARIES_BT2020 ? [1.660491, -0.587641, -0.072850, -0.124550, 1.132900, -0.008349, -0.018151, -0.100579, 1.118730] :
|
|
45
|
+
primaries === PRIMARIES_P3 ? [1.224940, -0.224940, 0, -0.042057, 1.042057, 0, -0.019638, -0.078636, 1.098274] : null;
|
|
46
|
+
for (let i = 0; i < rgb.length; i += 3) {
|
|
47
|
+
let r, g, bl;
|
|
48
|
+
if (transfer === 16) {
|
|
49
|
+
r = pq(rgb[i]);
|
|
50
|
+
g = pq(rgb[i + 1]);
|
|
51
|
+
bl = pq(rgb[i + 2]);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
r = hlg(rgb[i]);
|
|
55
|
+
g = hlg(rgb[i + 1]);
|
|
56
|
+
bl = hlg(rgb[i + 2]);
|
|
57
|
+
const ys = 0.2627 * r + 0.6780 * g + 0.0593 * bl;
|
|
58
|
+
const gain = ys > 0 ? 1000 / REFERENCE_WHITE * ys ** 0.2 : 0;
|
|
59
|
+
r *= gain;
|
|
60
|
+
g *= gain;
|
|
61
|
+
bl *= gain;
|
|
62
|
+
}
|
|
63
|
+
if (matrix) {
|
|
64
|
+
rgb[i] = matrix[0] * r + matrix[1] * g + matrix[2] * bl;
|
|
65
|
+
rgb[i + 1] = matrix[3] * r + matrix[4] * g + matrix[5] * bl;
|
|
66
|
+
rgb[i + 2] = matrix[6] * r + matrix[7] * g + matrix[8] * bl;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
rgb[i] = r;
|
|
70
|
+
rgb[i + 1] = g;
|
|
71
|
+
rgb[i + 2] = bl;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return rgb;
|
|
75
|
+
}
|
|
76
|
+
const ascii = (d, start, end) => String.fromCharCode(...d.subarray(start, end));
|
|
77
|
+
export function isHDR(d) {
|
|
78
|
+
const start = ascii(d, 0, 10);
|
|
79
|
+
return start === '#?RADIANCE' || start.startsWith('#?RGBE');
|
|
80
|
+
}
|
|
81
|
+
const XYZ_TO_RGB = (() => {
|
|
82
|
+
const inverse = ([a, b, c, d, e, f, g, h, i]) => {
|
|
83
|
+
const det = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g);
|
|
84
|
+
return [e * i - f * h, c * h - b * i, b * f - c * e, f * g - d * i, a * i - c * g, c * d - a * f, d * h - e * g, b * g - a * h, a * e - b * d].map(v => v / det);
|
|
85
|
+
};
|
|
86
|
+
const [r, g, b] = [[0.640, 0.330], [0.290, 0.600], [0.150, 0.060]].map(([x, y]) => [x / y, 1, (1 - x - y) / y]);
|
|
87
|
+
const primaries = [r[0], g[0], b[0], r[1], g[1], b[1], r[2], g[2], b[2]];
|
|
88
|
+
const inv = inverse(primaries);
|
|
89
|
+
const scale = [0, 1, 2].map(row => inv[row * 3] + inv[row * 3 + 1] + inv[row * 3 + 2]);
|
|
90
|
+
return inverse(primaries.map((v, i) => v * scale[i % 3]));
|
|
91
|
+
})();
|
|
92
|
+
export function decode(d, maxPixels = MAX_PIXELS, toneMapping = 'reinhard') {
|
|
93
|
+
if (!isHDR(d))
|
|
94
|
+
throw new Error('HDR: invalid header');
|
|
95
|
+
let o = 0, xyz = false;
|
|
96
|
+
for (;;) {
|
|
97
|
+
let end = o;
|
|
98
|
+
while (end < d.length && d[end] !== 0x0a)
|
|
99
|
+
end++;
|
|
100
|
+
if (end >= d.length)
|
|
101
|
+
throw new Error('HDR: truncated header');
|
|
102
|
+
const line = ascii(d, o, end);
|
|
103
|
+
o = end + 1;
|
|
104
|
+
if (!line)
|
|
105
|
+
break;
|
|
106
|
+
if (line.startsWith('FORMAT=')) {
|
|
107
|
+
const format = line.slice(7).trim();
|
|
108
|
+
if (format !== '32-bit_rle_rgbe' && format !== '32-bit_rle_xyze')
|
|
109
|
+
throw new Error(`HDR: unsupported format ${format}`);
|
|
110
|
+
xyz = format === '32-bit_rle_xyze';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
let end = o;
|
|
114
|
+
while (end < d.length && d[end] !== 0x0a)
|
|
115
|
+
end++;
|
|
116
|
+
const resolution = /^([-+])([XY]) (\d+) ([-+])([XY]) (\d+)$/.exec(ascii(d, o, Math.min(end, o + 64)).trim());
|
|
117
|
+
if (!resolution || resolution[2] === resolution[5])
|
|
118
|
+
throw new Error('HDR: invalid resolution');
|
|
119
|
+
o = end + 1;
|
|
120
|
+
const [, sign1, axis1, n1, sign2, , n2] = resolution;
|
|
121
|
+
const lines = +n1, length = +n2;
|
|
122
|
+
const columns = axis1 === 'X';
|
|
123
|
+
const width = columns ? lines : length, height = columns ? length : lines;
|
|
124
|
+
if (!width || !height)
|
|
125
|
+
throw new Error(`HDR: invalid dimensions ${width}x${height}`);
|
|
126
|
+
checkPixels(width, height, maxPixels);
|
|
127
|
+
const rgb = new Float32Array(width * height * 3);
|
|
128
|
+
const line = new Uint8Array(length * 4);
|
|
129
|
+
for (let s = 0; s < lines; s++) {
|
|
130
|
+
o = readLine(d, o, line, length);
|
|
131
|
+
for (let i = 0; i < length; i++) {
|
|
132
|
+
const x = columns ? (sign1 === '+' ? s : width - 1 - s) : sign2 === '+' ? i : width - 1 - i;
|
|
133
|
+
const y = columns ? (sign2 === '-' ? i : height - 1 - i) : sign1 === '-' ? s : height - 1 - s;
|
|
134
|
+
const p = (y * width + x) * 3, e = line[i * 4 + 3];
|
|
135
|
+
if (e) {
|
|
136
|
+
const f = 2 ** (e - 136);
|
|
137
|
+
let r = line[i * 4] * f, g = line[i * 4 + 1] * f, b = line[i * 4 + 2] * f;
|
|
138
|
+
if (xyz) {
|
|
139
|
+
const m = XYZ_TO_RGB;
|
|
140
|
+
[r, g, b] = [m[0] * r + m[1] * g + m[2] * b, m[3] * r + m[4] * g + m[5] * b, m[6] * r + m[7] * g + m[8] * b];
|
|
141
|
+
}
|
|
142
|
+
rgb[p] = r;
|
|
143
|
+
rgb[p + 1] = g;
|
|
144
|
+
rgb[p + 2] = b;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return { width, height, pixels: toSRGB(rgb, toneMapping) };
|
|
149
|
+
}
|
|
150
|
+
function readLine(d, o, line, length) {
|
|
151
|
+
if (o + 4 > d.length)
|
|
152
|
+
throw new Error('HDR: truncated image data');
|
|
153
|
+
if (length >= 8 && length < 0x8000 && d[o] === 2 && d[o + 1] === 2 && !(d[o + 2] & 0x80)) {
|
|
154
|
+
if ((d[o + 2] << 8 | d[o + 3]) !== length)
|
|
155
|
+
throw new Error('HDR: scan line of the wrong length');
|
|
156
|
+
o += 4;
|
|
157
|
+
for (let c = 0; c < 4; c++) {
|
|
158
|
+
for (let i = 0; i < length;) {
|
|
159
|
+
if (o >= d.length)
|
|
160
|
+
throw new Error('HDR: truncated image data');
|
|
161
|
+
let n = d[o++];
|
|
162
|
+
if (n > 128) {
|
|
163
|
+
n -= 128;
|
|
164
|
+
if (i + n > length || o >= d.length)
|
|
165
|
+
throw new Error('HDR: invalid run');
|
|
166
|
+
const v = d[o++];
|
|
167
|
+
for (; n; n--)
|
|
168
|
+
line[i++ * 4 + c] = v;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
if (!n || i + n > length || o + n > d.length)
|
|
172
|
+
throw new Error('HDR: invalid run');
|
|
173
|
+
for (; n; n--)
|
|
174
|
+
line[i++ * 4 + c] = d[o++];
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return o;
|
|
179
|
+
}
|
|
180
|
+
for (let i = 0, shift = 0; i < length;) {
|
|
181
|
+
if (o + 4 > d.length)
|
|
182
|
+
throw new Error('HDR: truncated image data');
|
|
183
|
+
if (d[o] === 1 && d[o + 1] === 1 && d[o + 2] === 1) {
|
|
184
|
+
if (!i)
|
|
185
|
+
throw new Error('HDR: repetition without a pixel');
|
|
186
|
+
const n = d[o + 3] * 2 ** shift;
|
|
187
|
+
if (i + n > length)
|
|
188
|
+
throw new Error('HDR: invalid run');
|
|
189
|
+
for (let k = 0; k < n; k++, i++)
|
|
190
|
+
line.copyWithin(i * 4, i * 4 - 4, i * 4);
|
|
191
|
+
shift += 8;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
line.set(d.subarray(o, o + 4), i * 4);
|
|
195
|
+
i++;
|
|
196
|
+
shift = 0;
|
|
197
|
+
}
|
|
198
|
+
o += 4;
|
|
199
|
+
}
|
|
200
|
+
return o;
|
|
201
|
+
}
|