imagescript 3.3.0 → 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 +28 -8
- package/THIRD_PARTY_NOTICES.md +167 -2
- package/dist/animation.d.ts +18 -8
- package/dist/animation.js +23 -2
- 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/hdr.d.ts +25 -0
- package/dist/codecs/hdr.js +201 -0
- package/dist/codecs/ico.js +48 -0
- package/dist/codecs/jpeg.js +2 -1
- 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/qoi.js +150 -0
- 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/webp.js +36 -2
- package/dist/image-type.d.ts +15 -1
- package/dist/image-type.js +45 -2
- package/dist/image.d.ts +47 -6
- package/dist/image.js +49 -3
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -2
- package/dist/render/hinting.js +26 -18
- package/dist/render/layout.js +1 -1
- package/dist/render/svg.js +1 -1
- package/dist/render/unicode.js +1 -9
- package/dist/util/bytes.js +9 -0
- package/dist/util/resample.js +4 -2
- package/dist/version.js +1 -1
- package/package.json +25 -2
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { unpackSigned } from "./bits.js";
|
|
2
|
+
import { EntropyDecoder } from "./entropy.js";
|
|
3
|
+
import { EXTRA_ALPHA } from "./headers.js";
|
|
4
|
+
import { performBlending } from "./blend.js";
|
|
5
|
+
import { mirror } from "./upsample.js";
|
|
6
|
+
export function readPatches(r, extra, frameWidth, frameHeight) {
|
|
7
|
+
const alphas = extra.map((e, i) => e.type === EXTRA_ALPHA ? i : -1).filter(i => i >= 0);
|
|
8
|
+
const dec = new EntropyDecoder(r, 10);
|
|
9
|
+
dec.begin(r);
|
|
10
|
+
const maxRefs = Math.min(1 << 24, Math.floor(frameWidth * frameHeight / 16));
|
|
11
|
+
const numRefs = dec.read(r, 0);
|
|
12
|
+
if (numRefs > maxRefs)
|
|
13
|
+
throw new Error('JPEG XL: too many patches');
|
|
14
|
+
const patches = [];
|
|
15
|
+
let total = 0;
|
|
16
|
+
for (let p = 0; p < numRefs; p++) {
|
|
17
|
+
const ref = dec.read(r, 1);
|
|
18
|
+
if (ref >= 4)
|
|
19
|
+
throw new Error('JPEG XL: invalid patch reference');
|
|
20
|
+
const x0 = dec.read(r, 3), y0 = dec.read(r, 3), w = dec.read(r, 2) + 1, h = dec.read(r, 2) + 1, count = dec.read(r, 7) + 1;
|
|
21
|
+
total += count;
|
|
22
|
+
if (total > maxRefs * 4)
|
|
23
|
+
throw new Error('JPEG XL: too many patches');
|
|
24
|
+
const targets = [];
|
|
25
|
+
for (let i = 0; i < count; i++) {
|
|
26
|
+
let x, y;
|
|
27
|
+
if (i) {
|
|
28
|
+
x = targets[i - 1].x + unpackSigned(dec.read(r, 6));
|
|
29
|
+
y = targets[i - 1].y + unpackSigned(dec.read(r, 6));
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
x = dec.read(r, 4);
|
|
33
|
+
y = dec.read(r, 4);
|
|
34
|
+
}
|
|
35
|
+
const blending = [];
|
|
36
|
+
for (let c = 0; c <= extra.length; c++) {
|
|
37
|
+
const mode = dec.read(r, 5);
|
|
38
|
+
if (mode > 7)
|
|
39
|
+
throw new Error('JPEG XL: invalid patch blend mode');
|
|
40
|
+
const alpha = mode >= 4 && alphas.length >= 2 ? dec.read(r, 8) : alphas[0] ?? 0;
|
|
41
|
+
if (mode >= 4 && alpha >= extra.length)
|
|
42
|
+
throw new Error('JPEG XL: invalid patch alpha channel');
|
|
43
|
+
const clamp = mode >= 3 ? dec.read(r, 9) !== 0 : false;
|
|
44
|
+
blending.push({ mode, alpha, clamp });
|
|
45
|
+
}
|
|
46
|
+
targets.push({ x, y, blending });
|
|
47
|
+
}
|
|
48
|
+
patches.push({ ref, x0, y0, w, h, targets });
|
|
49
|
+
}
|
|
50
|
+
dec.finish();
|
|
51
|
+
return patches;
|
|
52
|
+
}
|
|
53
|
+
export function applyPatches(p, numColor, patches, references, extra) {
|
|
54
|
+
for (const patch of patches) {
|
|
55
|
+
const ref = references[patch.ref];
|
|
56
|
+
if (!ref)
|
|
57
|
+
throw new Error('JPEG XL: a patch of a missing reference');
|
|
58
|
+
if (patch.x0 + patch.w > ref.width || patch.y0 + patch.h > ref.height)
|
|
59
|
+
throw new Error('JPEG XL: a patch outside its reference');
|
|
60
|
+
for (const t of patch.targets) {
|
|
61
|
+
const x0 = Math.max(t.x, 0), x1 = Math.min(t.x + patch.w, p.width);
|
|
62
|
+
if (x1 <= x0)
|
|
63
|
+
continue;
|
|
64
|
+
for (let y = Math.max(t.y, 0); y < Math.min(t.y + patch.h, p.height); y++) {
|
|
65
|
+
const ri = (patch.y0 + y - t.y) * ref.width + patch.x0 + x0 - t.x, i = y * p.width + x0;
|
|
66
|
+
performBlending(p.channels, i, ref.channels, ri, p.channels, i, x1 - x0, numColor, t.blending[0], t.blending.slice(1), extra);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export function readSplines(r, frameWidth, frameHeight) {
|
|
72
|
+
const dec = new EntropyDecoder(r, 6);
|
|
73
|
+
dec.begin(r);
|
|
74
|
+
const pixels = frameWidth * frameHeight, maxPoints = Math.min(1 << 20, Math.floor(pixels / 2));
|
|
75
|
+
const n = dec.read(r, 2) + 1;
|
|
76
|
+
if (n > maxPoints)
|
|
77
|
+
throw new Error('JPEG XL: too many splines');
|
|
78
|
+
const starts = [];
|
|
79
|
+
for (let i = 0; i < n; i++) {
|
|
80
|
+
let x = dec.read(r, 1), y = dec.read(r, 1);
|
|
81
|
+
if (i) {
|
|
82
|
+
x = unpackSigned(x) + starts[2 * i - 2];
|
|
83
|
+
y = unpackSigned(y) + starts[2 * i - 1];
|
|
84
|
+
}
|
|
85
|
+
starts.push(x, y);
|
|
86
|
+
}
|
|
87
|
+
const quantAdjust = unpackSigned(dec.read(r, 0));
|
|
88
|
+
const splines = [];
|
|
89
|
+
let totalPoints = n;
|
|
90
|
+
for (let s = 0; s < n; s++) {
|
|
91
|
+
const count = dec.read(r, 3);
|
|
92
|
+
totalPoints += count;
|
|
93
|
+
if (totalPoints > maxPoints)
|
|
94
|
+
throw new Error('JPEG XL: too many spline points');
|
|
95
|
+
const points = [starts[2 * s], starts[2 * s + 1]];
|
|
96
|
+
let dx = 0, dy = 0;
|
|
97
|
+
for (let i = 0; i < count; i++) {
|
|
98
|
+
dx += unpackSigned(dec.read(r, 4));
|
|
99
|
+
dy += unpackSigned(dec.read(r, 4));
|
|
100
|
+
const x = points[points.length - 2] + dx, y = points[points.length - 1] + dy;
|
|
101
|
+
if (x === points[points.length - 2] && y === points[points.length - 1])
|
|
102
|
+
throw new Error('JPEG XL: identical successive spline points');
|
|
103
|
+
points.push(x, y);
|
|
104
|
+
}
|
|
105
|
+
const read32 = () => Int32Array.from({ length: 32 }, () => unpackSigned(dec.read(r, 5)));
|
|
106
|
+
const color = [read32(), read32(), read32()];
|
|
107
|
+
splines.push({ points, color, sigma: read32() });
|
|
108
|
+
}
|
|
109
|
+
dec.finish();
|
|
110
|
+
return { quantAdjust, splines };
|
|
111
|
+
}
|
|
112
|
+
const CHANNEL_WEIGHT = [0.0042, 0.075, 0.07, 0.3333];
|
|
113
|
+
const llround = (x) => x < 0 ? -Math.round(-x) : Math.round(x);
|
|
114
|
+
function continuousIDCT(dct, t) {
|
|
115
|
+
let sum = 0;
|
|
116
|
+
for (let i = 0; i < 32; i++)
|
|
117
|
+
sum += Math.SQRT2 * dct[i] * Math.cos(Math.PI / 32 * i * (t + 0.5));
|
|
118
|
+
return sum;
|
|
119
|
+
}
|
|
120
|
+
function erf(x) {
|
|
121
|
+
const ax = Math.abs(x);
|
|
122
|
+
const d = (((ax * 7.77394369e-02 + 2.05260015e-04) * ax + 2.32120216e-01) * ax + 2.77820801e-01) * ax + 1;
|
|
123
|
+
const inv = 1 / (d * d), r = 1 - inv * inv;
|
|
124
|
+
return x <= 0 ? -r : r;
|
|
125
|
+
}
|
|
126
|
+
function catmullRom(p) {
|
|
127
|
+
const n = p.length / 2;
|
|
128
|
+
if (n === 1)
|
|
129
|
+
return p.slice();
|
|
130
|
+
const pts = [2 * p[0] - p[2], 2 * p[1] - p[3], ...p, 2 * p[2 * n - 2] - p[2 * n - 4], 2 * p[2 * n - 1] - p[2 * n - 3]];
|
|
131
|
+
const out = [];
|
|
132
|
+
for (let s = 0; s + 3 < pts.length / 2; s++) {
|
|
133
|
+
const P = (k) => [pts[2 * (s + k)], pts[2 * (s + k) + 1]];
|
|
134
|
+
out.push(...P(1));
|
|
135
|
+
const d = [], t = [0];
|
|
136
|
+
for (let k = 0; k < 3; k++) {
|
|
137
|
+
const [ax, ay] = P(k), [bx, by] = P(k + 1);
|
|
138
|
+
d.push(Math.sqrt(Math.hypot(bx - ax, by - ay)));
|
|
139
|
+
t.push(t[k] + d[k]);
|
|
140
|
+
}
|
|
141
|
+
for (let i = 1; i < 16; i++) {
|
|
142
|
+
const tt = d[0] + i / 16 * d[1];
|
|
143
|
+
const a = [];
|
|
144
|
+
for (let k = 0; k < 3; k++) {
|
|
145
|
+
const [ax, ay] = P(k), [bx, by] = P(k + 1), m = (tt - t[k]) / d[k];
|
|
146
|
+
a.push([ax + m * (bx - ax), ay + m * (by - ay)]);
|
|
147
|
+
}
|
|
148
|
+
const b = [];
|
|
149
|
+
for (let k = 0; k < 2; k++) {
|
|
150
|
+
const m = (tt - t[k]) / (d[k] + d[k + 1]);
|
|
151
|
+
b.push([a[k][0] + m * (a[k + 1][0] - a[k][0]), a[k][1] + m * (a[k + 1][1] - a[k][1])]);
|
|
152
|
+
}
|
|
153
|
+
const m = (tt - t[1]) / d[1];
|
|
154
|
+
out.push(b[0][0] + m * (b[1][0] - b[0][0]), b[0][1] + m * (b[1][1] - b[0][1]));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
out.push(pts[pts.length - 4], pts[pts.length - 3]);
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
export function renderSplines(p, s, baseX, baseB) {
|
|
161
|
+
const inv = s.quantAdjust >= 0 ? 1 / (1 + 0.125 * s.quantAdjust) : 1 - 0.125 * s.quantAdjust;
|
|
162
|
+
const [cx, cy, cb] = p.channels, W = p.width, H = p.height;
|
|
163
|
+
for (const spline of s.splines) {
|
|
164
|
+
const color = [0, 1, 2].map(c => Float64Array.from(spline.color[c], (v, i) => v * (i ? 1 : Math.SQRT1_2) * CHANNEL_WEIGHT[c] * inv));
|
|
165
|
+
for (let i = 0; i < 32; i++) {
|
|
166
|
+
color[0][i] += baseX * color[1][i];
|
|
167
|
+
color[2][i] += baseB * color[1][i];
|
|
168
|
+
}
|
|
169
|
+
const sigmaDct = Float64Array.from(spline.sigma, (v, i) => v * (i ? 1 : Math.SQRT1_2) * CHANNEL_WEIGHT[3] * inv);
|
|
170
|
+
const curve = catmullRom(spline.points), points = [];
|
|
171
|
+
let px = curve[0], py = curve[1];
|
|
172
|
+
points.push(px, py, 1);
|
|
173
|
+
for (let next = 0; next < curve.length / 2;) {
|
|
174
|
+
let prevX = px, prevY = py, along = 0;
|
|
175
|
+
for (;;) {
|
|
176
|
+
if (next >= curve.length / 2) {
|
|
177
|
+
points.push(prevX, prevY, along);
|
|
178
|
+
next = Infinity;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
const nx = curve[2 * next], ny = curve[2 * next + 1], toNext = Math.sqrt((nx - prevX) ** 2 + (ny - prevY) ** 2);
|
|
182
|
+
if (along + toNext >= 1) {
|
|
183
|
+
const m = (1 - along) / toNext;
|
|
184
|
+
px = prevX + m * (nx - prevX);
|
|
185
|
+
py = prevY + m * (ny - prevY);
|
|
186
|
+
points.push(px, py, 1);
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
along += toNext;
|
|
190
|
+
prevX = nx;
|
|
191
|
+
prevY = ny;
|
|
192
|
+
next++;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const count = points.length / 3, arcLength = count - 2 + points[points.length - 1];
|
|
196
|
+
if (arcLength <= 0)
|
|
197
|
+
continue;
|
|
198
|
+
for (let k = 0; k < count; k++) {
|
|
199
|
+
const x = points[3 * k], y = points[3 * k + 1], intensity = points[3 * k + 2];
|
|
200
|
+
const progress = Math.min(1, k / arcLength);
|
|
201
|
+
const c = [0, 1, 2].map(ch => continuousIDCT(color[ch], 31 * progress));
|
|
202
|
+
const sigma = continuousIDCT(sigmaDct, 31 * progress);
|
|
203
|
+
if (!(Number.isFinite(sigma) && sigma !== 0 && Number.isFinite(1 / sigma) && Number.isFinite(intensity)))
|
|
204
|
+
continue;
|
|
205
|
+
let maxColor = 0.01;
|
|
206
|
+
for (let ch = 0; ch < 3; ch++)
|
|
207
|
+
maxColor = Math.max(maxColor, Math.abs(c[ch] * intensity));
|
|
208
|
+
const maxDistance = Math.sqrt(-2 * sigma * sigma * (Math.log(0.1) * 3 - Math.log(maxColor)));
|
|
209
|
+
const invSigma = 1 / sigma, k4 = 0.25 * sigma * intensity;
|
|
210
|
+
const y0 = Math.max(llround(y - maxDistance), 0), y1 = Math.min(llround(y + maxDistance) + 1, H);
|
|
211
|
+
const x0 = Math.max(llround(x - maxDistance), 0), x1 = Math.min(llround(x + maxDistance) + 1, W);
|
|
212
|
+
for (let yy = y0; yy < y1; yy++) {
|
|
213
|
+
const dy = yy - y;
|
|
214
|
+
for (let xx = x0; xx < x1; xx++) {
|
|
215
|
+
const dx = xx - x, distance = Math.sqrt(dx * dx + dy * dy);
|
|
216
|
+
const f = erf((distance * 0.5 + 0.353553391) * invSigma) - erf((distance * 0.5 - 0.353553391) * invSigma);
|
|
217
|
+
const local = k4 * f * f, i = yy * W + xx;
|
|
218
|
+
cx[i] += c[0] * local;
|
|
219
|
+
cy[i] += c[1] * local;
|
|
220
|
+
cb[i] += c[2] * local;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
export function readNoise(r) {
|
|
227
|
+
return Array.from({ length: 8 }, () => r.bits(10) / 1024);
|
|
228
|
+
}
|
|
229
|
+
const M64 = (1n << 64n) - 1n;
|
|
230
|
+
function splitMix64(z) {
|
|
231
|
+
z = ((z ^ (z >> 30n)) * 0xbf58476d1ce4e5b9n) & M64;
|
|
232
|
+
z = ((z ^ (z >> 27n)) * 0x94d049bb133111ebn) & M64;
|
|
233
|
+
return z ^ (z >> 31n);
|
|
234
|
+
}
|
|
235
|
+
class Xorshift128Plus {
|
|
236
|
+
s0 = new Uint32Array(16);
|
|
237
|
+
s1 = new Uint32Array(16);
|
|
238
|
+
constructor(seed1, seed2, seed3, seed4) {
|
|
239
|
+
const s0 = [splitMix64(((BigInt(seed1) << 32n) + BigInt(seed2) + 0x9e3779b97f4a7c15n) & M64)];
|
|
240
|
+
const s1 = [splitMix64(((BigInt(seed3) << 32n) + BigInt(seed4) + 0x9e3779b97f4a7c15n) & M64)];
|
|
241
|
+
for (let i = 1; i < 8; i++) {
|
|
242
|
+
s0.push(splitMix64(s0[i - 1]));
|
|
243
|
+
s1.push(splitMix64(s1[i - 1]));
|
|
244
|
+
}
|
|
245
|
+
for (let i = 0; i < 8; i++) {
|
|
246
|
+
this.s0[2 * i] = Number(s0[i] & 0xffffffffn);
|
|
247
|
+
this.s0[2 * i + 1] = Number(s0[i] >> 32n);
|
|
248
|
+
this.s1[2 * i] = Number(s1[i] & 0xffffffffn);
|
|
249
|
+
this.s1[2 * i + 1] = Number(s1[i] >> 32n);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
fill(out) {
|
|
253
|
+
for (let i = 0; i < 8; i++) {
|
|
254
|
+
let aLo = this.s0[2 * i], aHi = this.s0[2 * i + 1];
|
|
255
|
+
const bLo = this.s1[2 * i], bHi = this.s1[2 * i + 1];
|
|
256
|
+
const lo = (aLo + bLo) >>> 0;
|
|
257
|
+
out[2 * i] = lo;
|
|
258
|
+
out[2 * i + 1] = (aHi + bHi + (lo < aLo ? 1 : 0)) >>> 0;
|
|
259
|
+
this.s0[2 * i] = bLo;
|
|
260
|
+
this.s0[2 * i + 1] = bHi;
|
|
261
|
+
aHi = (aHi ^ ((aHi << 23) | (aLo >>> 9))) >>> 0;
|
|
262
|
+
aLo = (aLo ^ (aLo << 23)) >>> 0;
|
|
263
|
+
const a18Lo = ((aLo >>> 18) | (aHi << 14)) >>> 0, a18Hi = aHi >>> 18;
|
|
264
|
+
const b5Lo = ((bLo >>> 5) | (bHi << 27)) >>> 0, b5Hi = bHi >>> 5;
|
|
265
|
+
this.s1[2 * i] = (aLo ^ bLo ^ a18Lo ^ b5Lo) >>> 0;
|
|
266
|
+
this.s1[2 * i + 1] = (aHi ^ bHi ^ a18Hi ^ b5Hi) >>> 0;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const f32 = new Float32Array(1), u32 = new Uint32Array(f32.buffer);
|
|
271
|
+
export function renderNoise(p, lut, groupDim, visible, invisible, baseX, baseB) {
|
|
272
|
+
const W = p.width, H = p.height;
|
|
273
|
+
const rnd = [new Float32Array(W * H), new Float32Array(W * H), new Float32Array(W * H)], batch = new Uint32Array(16);
|
|
274
|
+
for (let ty = 0; ty < H; ty += groupDim) {
|
|
275
|
+
for (let tx = 0; tx < W; tx += groupDim) {
|
|
276
|
+
const rng = new Xorshift128Plus(visible, invisible, tx, ty), tw = Math.min(groupDim, W - tx);
|
|
277
|
+
for (const plane of rnd) {
|
|
278
|
+
for (let y = ty; y < Math.min(ty + groupDim, H); y++) {
|
|
279
|
+
for (let x = 0; x < tw; x += 16) {
|
|
280
|
+
rng.fill(batch);
|
|
281
|
+
for (let k = 0; k < 16 && x + k < tw; k++) {
|
|
282
|
+
u32[0] = (batch[k] >>> 9) | 0x3f800000;
|
|
283
|
+
plane[y * W + tx + x + k] = f32[0];
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const conv = rnd.map(src => {
|
|
291
|
+
const out = new Float32Array(W * H);
|
|
292
|
+
for (let y = 0; y < H; y++) {
|
|
293
|
+
for (let x = 0; x < W; x++) {
|
|
294
|
+
let others = 0;
|
|
295
|
+
for (let dy = -2; dy <= 2; dy++) {
|
|
296
|
+
const row = mirror(y + dy, H) * W;
|
|
297
|
+
for (let dx = -2; dx <= 2; dx++)
|
|
298
|
+
if (dx || dy)
|
|
299
|
+
others += src[row + mirror(x + dx, W)];
|
|
300
|
+
}
|
|
301
|
+
out[y * W + x] = others * 0.16 - src[y * W + x] * 3.84;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return out;
|
|
305
|
+
});
|
|
306
|
+
const strength = (v) => {
|
|
307
|
+
const scaled = Math.max(0, v * 6);
|
|
308
|
+
let floor = Math.floor(scaled), frac = scaled - floor;
|
|
309
|
+
if (scaled >= 7) {
|
|
310
|
+
floor = 6;
|
|
311
|
+
frac = 1;
|
|
312
|
+
}
|
|
313
|
+
const s = (lut[floor + 1] - lut[floor]) * frac + lut[floor];
|
|
314
|
+
return s < 0 ? 0 : s > 1 ? 1 : s;
|
|
315
|
+
};
|
|
316
|
+
const [X, Y, B] = p.channels;
|
|
317
|
+
for (let i = 0; i < W * H; i++) {
|
|
318
|
+
const sg = strength((Y[i] - X[i]) * 0.5), sr = strength((Y[i] + X[i]) * 0.5);
|
|
319
|
+
const r = conv[0][i] * 0.22, g = conv[1][i] * 0.22, c = conv[2][i] * 0.22;
|
|
320
|
+
const red = sr * (0.0078125 * r + 0.9921875 * c), green = sg * (0.0078125 * g + 0.9921875 * c), rg = red + green;
|
|
321
|
+
X[i] += baseX * rg + red - green;
|
|
322
|
+
Y[i] += rg;
|
|
323
|
+
B[i] += baseB * rg;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { mirror } from "./upsample.js";
|
|
2
|
+
export function gaborish(channels, w, h, weights) {
|
|
3
|
+
return channels.map((src, c) => {
|
|
4
|
+
const w0 = weights[2 * c], w1 = weights[2 * c + 1], norm = 1 / (1 + 4 * w0 + 4 * w1);
|
|
5
|
+
const out = new Float32Array(w * h);
|
|
6
|
+
for (let y = 0; y < h; y++) {
|
|
7
|
+
const t = mirror(y - 1, h) * w, m = y * w, b = mirror(y + 1, h) * w;
|
|
8
|
+
for (let x = 0; x < w; x++) {
|
|
9
|
+
const l = mirror(x - 1, w), r = mirror(x + 1, w);
|
|
10
|
+
out[m + x] = (src[m + x] + (src[t + x] + src[b + x] + src[m + l] + src[m + r]) * w0 + (src[t + l] + src[t + r] + src[b + l] + src[b + r]) * w1) * norm;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return out;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
const STEPS = [
|
|
17
|
+
{ u: [0, 1, 0, 2, 1, 0, 2, 0, 1, 1, -1, 1], plus: true },
|
|
18
|
+
{ u: [0, 1, 1, 0], plus: true },
|
|
19
|
+
{ u: [0, 1, 1, 0], plus: false }
|
|
20
|
+
];
|
|
21
|
+
const STRIP = 128;
|
|
22
|
+
function diffRow(i0, i1, i2, ry, ruy, xs, ux, d, o, pw, s0, s1, s2) {
|
|
23
|
+
const lo = Math.max(3, 3 - ux), hi = Math.min(pw - 3, pw - 3 - ux);
|
|
24
|
+
for (let px = 0; px < pw; px++) {
|
|
25
|
+
if (px === lo && lo < hi) {
|
|
26
|
+
for (let a = ry + px - 3, b = ruy + px - 3 + ux; px < hi; px++, a++, b++) {
|
|
27
|
+
d[o + px] = s0 * Math.abs(i0[b] - i0[a]) + s1 * Math.abs(i1[b] - i1[a]) + s2 * Math.abs(i2[b] - i2[a]);
|
|
28
|
+
}
|
|
29
|
+
if (px >= pw)
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
const a = ry + xs[px + 2], b = ruy + xs[px + 2 + ux];
|
|
33
|
+
d[o + px] = s0 * Math.abs(i0[b] - i0[a]) + s1 * Math.abs(i1[b] - i1[a]) + s2 * Math.abs(i2[b] - i2[a]);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function plusRow(d, sum, o, pw) {
|
|
37
|
+
for (let px = 1, k = o + 1; px < pw - 1; px++, k++)
|
|
38
|
+
sum[k] = d[k] + d[k - 1] + d[k + 1] + d[k - pw] + d[k + pw];
|
|
39
|
+
}
|
|
40
|
+
function outputRow(cur, out, y, w, sums, p0, pw, u, rows, xs, sigma, sigmaRow, sigmaModular, stepMul, borderMul) {
|
|
41
|
+
const [i0, i1, i2] = cur, [o0, o1, o2] = out, n = u.length / 2, yBorder = ((y + 1) & 6) === 0;
|
|
42
|
+
const innerRow = rows[0] === (y - 2) * w && rows[4] === (y + 2) * w;
|
|
43
|
+
const row = y * w;
|
|
44
|
+
for (let x = 0; x < w; x++) {
|
|
45
|
+
const i = row + x;
|
|
46
|
+
const sg = sigma ? sigma[sigmaRow + (x >> 3)] : sigmaModular;
|
|
47
|
+
if (sg < 0.3) {
|
|
48
|
+
o0[i] = i0[i];
|
|
49
|
+
o1[i] = i1[i];
|
|
50
|
+
o2[i] = i2[i];
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const sm = yBorder || (x & 7) === 0 || (x & 7) === 7 ? stepMul * borderMul : stepMul;
|
|
54
|
+
const negInvSigma = 6.6 * (Math.SQRT1_2 - 1) / sg * sm;
|
|
55
|
+
let sumW = 1, sum0 = i0[i], sum1 = i1[i], sum2 = i2[i];
|
|
56
|
+
const p = p0 + x, inner = innerRow && x >= 2 && x < w - 2;
|
|
57
|
+
for (let k = 0; k < n; k++) {
|
|
58
|
+
const ux = u[2 * k], uy = u[2 * k + 1], sum = sums[k];
|
|
59
|
+
let weight = 1 + sum[p] * negInvSigma;
|
|
60
|
+
if (weight > 0) {
|
|
61
|
+
const t = inner ? i + uy * w + ux : rows[2 + uy] + xs[x + 5 + ux];
|
|
62
|
+
sumW += weight;
|
|
63
|
+
sum0 += weight * i0[t];
|
|
64
|
+
sum1 += weight * i1[t];
|
|
65
|
+
sum2 += weight * i2[t];
|
|
66
|
+
}
|
|
67
|
+
weight = 1 + sum[p - uy * pw - ux] * negInvSigma;
|
|
68
|
+
if (weight > 0) {
|
|
69
|
+
const t = inner ? i - uy * w - ux : rows[2 - uy] + xs[x + 5 - ux];
|
|
70
|
+
sumW += weight;
|
|
71
|
+
sum0 += weight * i0[t];
|
|
72
|
+
sum1 += weight * i1[t];
|
|
73
|
+
sum2 += weight * i2[t];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
o0[i] = sum0 / sumW;
|
|
77
|
+
o1[i] = sum1 / sumW;
|
|
78
|
+
o2[i] = sum2 / sumW;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function epf(channels, w, h, e, sigma, bw) {
|
|
82
|
+
let cur = channels;
|
|
83
|
+
const steps = e.iters === 3 ? [0, 1, 2] : e.iters === 2 ? [1, 2] : [1];
|
|
84
|
+
const pw = w + 6, ph = STRIP + 6;
|
|
85
|
+
const diffs = Array.from({ length: 6 }, () => new Float64Array(pw * ph)), sums = Array.from({ length: 6 }, () => new Float64Array(pw * ph));
|
|
86
|
+
const xs = new Int32Array(w + 10);
|
|
87
|
+
for (let x = 0; x < xs.length; x++)
|
|
88
|
+
xs[x] = mirror(x - 5, w);
|
|
89
|
+
const rows = new Int32Array(5);
|
|
90
|
+
for (const step of steps) {
|
|
91
|
+
const out = [new Float32Array(w * h), new Float32Array(w * h), new Float32Array(w * h)];
|
|
92
|
+
const { u, plus } = STEPS[step], n = u.length / 2;
|
|
93
|
+
const stepMul = step === 0 ? e.pass0SigmaScale : step === 2 ? e.pass2SigmaScale : 1;
|
|
94
|
+
const [s0, s1, s2] = e.channelScale;
|
|
95
|
+
const [i0, i1, i2] = cur;
|
|
96
|
+
for (let y0 = 0; y0 < h; y0 += STRIP) {
|
|
97
|
+
const strip = Math.min(STRIP, h - y0);
|
|
98
|
+
for (let k = 0; k < n; k++) {
|
|
99
|
+
const ux = u[2 * k], uy = u[2 * k + 1], d = diffs[k], sum = sums[k];
|
|
100
|
+
for (let py = 0; py < strip + 6; py++) {
|
|
101
|
+
diffRow(i0, i1, i2, mirror(y0 + py - 3, h) * w, mirror(y0 + py - 3 + uy, h) * w, xs, ux, d, py * pw, pw, s0, s1, s2);
|
|
102
|
+
}
|
|
103
|
+
if (!plus)
|
|
104
|
+
sum.set(d);
|
|
105
|
+
else
|
|
106
|
+
for (let py = 1; py < strip + 5; py++)
|
|
107
|
+
plusRow(d, sum, py * pw, pw);
|
|
108
|
+
}
|
|
109
|
+
for (let ry = 0; ry < strip; ry++) {
|
|
110
|
+
const y = y0 + ry;
|
|
111
|
+
for (let k = 0; k < 5; k++)
|
|
112
|
+
rows[k] = mirror(y + k - 2, h) * w;
|
|
113
|
+
outputRow(cur, out, y, w, sums, (ry + 3) * pw + 3, pw, u, rows, xs, sigma, (y >> 3) * bw, e.sigmaForModular, stepMul, e.borderSadMul);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
cur = out;
|
|
117
|
+
}
|
|
118
|
+
return cur;
|
|
119
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { extensions, unpackSigned } from "./bits.js";
|
|
2
|
+
import { EntropyDecoder, readPermutation } from "./entropy.js";
|
|
3
|
+
export const FRAME_REGULAR = 0, FRAME_LF = 1, FRAME_REFERENCE = 2, FRAME_SKIP_PROGRESSIVE = 3;
|
|
4
|
+
export const BLEND_REPLACE = 0, BLEND_ADD = 1, BLEND_BLEND = 2, BLEND_MULADD = 3, BLEND_MUL = 4;
|
|
5
|
+
export const FLAG_NOISE = 1, FLAG_PATCHES = 2, FLAG_SPLINES = 16, FLAG_USE_LF_FRAME = 32, FLAG_SKIP_ADAPTIVE_LF_SMOOTHING = 128;
|
|
6
|
+
const DEFAULT_EPF = () => ({
|
|
7
|
+
iters: 2, sharpLut: [0, 1 / 7, 2 / 7, 3 / 7, 4 / 7, 5 / 7, 6 / 7, 1], channelScale: [40, 5, 3.5],
|
|
8
|
+
quantMul: 0.46, pass0SigmaScale: 0.9, pass2SigmaScale: 6.5, borderSadMul: 2 / 3, sigmaForModular: 1
|
|
9
|
+
});
|
|
10
|
+
const DEFAULT_GAB = [0.115169525, 0.061248592, 0.115169525, 0.061248592, 0.115169525, 0.061248592];
|
|
11
|
+
function blendingInfo(r, haveExtra, resets, mainMode = -1) {
|
|
12
|
+
const mode = r.u32(0, 1, 2, [3, 2]);
|
|
13
|
+
if (mode > 4)
|
|
14
|
+
throw new Error('JPEG XL: invalid blend mode');
|
|
15
|
+
const usesAlpha = haveExtra && (mode === BLEND_BLEND || mode === BLEND_MULADD);
|
|
16
|
+
const alphaChannel = usesAlpha ? r.u32(0, 1, 2, [3, 3]) : 0;
|
|
17
|
+
const clamp = usesAlpha || mode === BLEND_MUL ? r.bool() : false;
|
|
18
|
+
const source = resets(mainMode >= 0 ? mainMode : mode) ? 0 : r.bits(2);
|
|
19
|
+
return { mode, alphaChannel, clamp, source };
|
|
20
|
+
}
|
|
21
|
+
function epf(r, modular) {
|
|
22
|
+
const iters = r.bits(2);
|
|
23
|
+
if (!iters)
|
|
24
|
+
return null;
|
|
25
|
+
const e = DEFAULT_EPF();
|
|
26
|
+
e.iters = iters;
|
|
27
|
+
if (!modular && r.bool())
|
|
28
|
+
e.sharpLut = Array.from({ length: 8 }, () => r.f16());
|
|
29
|
+
if (r.bool()) {
|
|
30
|
+
e.channelScale = [r.f16(), r.f16(), r.f16()];
|
|
31
|
+
r.bits(32);
|
|
32
|
+
}
|
|
33
|
+
if (r.bool()) {
|
|
34
|
+
if (!modular)
|
|
35
|
+
e.quantMul = r.f16();
|
|
36
|
+
e.pass0SigmaScale = r.f16();
|
|
37
|
+
e.pass2SigmaScale = r.f16();
|
|
38
|
+
e.borderSadMul = r.f16();
|
|
39
|
+
}
|
|
40
|
+
if (modular)
|
|
41
|
+
e.sigmaForModular = r.f16();
|
|
42
|
+
return e;
|
|
43
|
+
}
|
|
44
|
+
export function frameHeader(r, image) {
|
|
45
|
+
const numExtra = image.extraChannels.length;
|
|
46
|
+
const allDefault = r.bool();
|
|
47
|
+
const type = allDefault ? FRAME_REGULAR : r.bits(2);
|
|
48
|
+
const modular = allDefault ? false : r.bool();
|
|
49
|
+
const flags = allDefault ? 0 : r.u64();
|
|
50
|
+
const doYCbCr = !allDefault && !image.xybEncoded ? r.bool() : false;
|
|
51
|
+
const useLfFrame = (flags & FLAG_USE_LF_FRAME) !== 0;
|
|
52
|
+
const grey = image.colour.colourSpace === 1;
|
|
53
|
+
const encodedColorChannels = modular && !doYCbCr && !image.xybEncoded && grey ? 1 : 3;
|
|
54
|
+
const jpegUpsampling = doYCbCr && !useLfFrame ? [r.bits(2), r.bits(2), r.bits(2)] : [0, 0, 0];
|
|
55
|
+
let upsampling = 1, ecUpsampling = image.extraChannels.map(e => 1 << e.dimShift);
|
|
56
|
+
if (!allDefault && !useLfFrame) {
|
|
57
|
+
upsampling = r.u32(1, 2, 4, 8);
|
|
58
|
+
ecUpsampling = image.extraChannels.map(e => r.u32(1, 2, 4, 8) << e.dimShift);
|
|
59
|
+
}
|
|
60
|
+
const groupSizeShift = modular ? r.bits(2) : 1;
|
|
61
|
+
let xqmScale = image.xybEncoded && !modular ? 3 : 2, bqmScale = 2;
|
|
62
|
+
if (!allDefault && image.xybEncoded && !modular) {
|
|
63
|
+
xqmScale = r.bits(3);
|
|
64
|
+
bqmScale = r.bits(3);
|
|
65
|
+
}
|
|
66
|
+
let numPasses = 1, passShift = [];
|
|
67
|
+
const downsample = [], lastPass = [];
|
|
68
|
+
if (!allDefault && type !== FRAME_REFERENCE) {
|
|
69
|
+
numPasses = r.u32(1, 2, 3, [4, 3]);
|
|
70
|
+
if (numPasses !== 1) {
|
|
71
|
+
const numDs = r.u32(0, 1, 2, [3, 1]);
|
|
72
|
+
if (numDs >= numPasses)
|
|
73
|
+
throw new Error('JPEG XL: invalid passes');
|
|
74
|
+
for (let i = 0; i < numPasses - 1; i++)
|
|
75
|
+
passShift.push(r.bits(2));
|
|
76
|
+
for (let i = 0; i < numDs; i++)
|
|
77
|
+
downsample.push(r.u32(1, 2, 4, 8));
|
|
78
|
+
for (let i = 0; i < numDs; i++)
|
|
79
|
+
lastPass.push(r.u32(0, 1, 2, [0, 3]));
|
|
80
|
+
}
|
|
81
|
+
else
|
|
82
|
+
passShift = [];
|
|
83
|
+
}
|
|
84
|
+
if (passShift.length < numPasses - 1)
|
|
85
|
+
passShift = new Array(numPasses - 1).fill(0);
|
|
86
|
+
const lfLevel = type === FRAME_LF ? 1 + r.bits(2) : 0;
|
|
87
|
+
const haveCrop = !allDefault && type !== FRAME_LF ? r.bool() : false;
|
|
88
|
+
let x0 = 0, y0 = 0, width = image.width, height = image.height;
|
|
89
|
+
if (haveCrop) {
|
|
90
|
+
const d = [[0, 8], [256, 11], [2304, 14], [18688, 30]];
|
|
91
|
+
if (type !== FRAME_REFERENCE) {
|
|
92
|
+
x0 = unpackSigned(r.u32(...d));
|
|
93
|
+
y0 = unpackSigned(r.u32(...d));
|
|
94
|
+
}
|
|
95
|
+
width = r.u32(...d);
|
|
96
|
+
height = r.u32(...d);
|
|
97
|
+
}
|
|
98
|
+
const resets = (mode) => mode === BLEND_REPLACE && (!haveCrop || (x0 <= 0 && y0 <= 0 && x0 + width >= image.width && y0 + height >= image.height));
|
|
99
|
+
const normal = type === FRAME_REGULAR || type === FRAME_SKIP_PROGRESSIVE;
|
|
100
|
+
let blending = { mode: BLEND_REPLACE, alphaChannel: 0, clamp: false, source: 0 };
|
|
101
|
+
const ecBlending = [];
|
|
102
|
+
if (!allDefault && normal) {
|
|
103
|
+
blending = blendingInfo(r, numExtra > 0, resets);
|
|
104
|
+
for (let i = 0; i < numExtra; i++)
|
|
105
|
+
ecBlending.push(blendingInfo(r, true, resets, blending.mode));
|
|
106
|
+
}
|
|
107
|
+
else
|
|
108
|
+
for (let i = 0; i < numExtra; i++)
|
|
109
|
+
ecBlending.push({ ...blending });
|
|
110
|
+
let duration = 0, isLast = type === FRAME_REGULAR, saveAsReference = 0;
|
|
111
|
+
if (!allDefault && normal) {
|
|
112
|
+
if (image.animation) {
|
|
113
|
+
duration = r.u32(0, 1, [0, 8], [0, 32]);
|
|
114
|
+
if (image.animation.haveTimecodes)
|
|
115
|
+
r.bits(32);
|
|
116
|
+
}
|
|
117
|
+
isLast = r.bool();
|
|
118
|
+
}
|
|
119
|
+
if (!allDefault && type !== FRAME_LF && !isLast)
|
|
120
|
+
saveAsReference = r.bits(2);
|
|
121
|
+
const resetsCanvas = resets(blending.mode);
|
|
122
|
+
let saveBeforeCT = !normal;
|
|
123
|
+
if (!allDefault && (type === FRAME_REFERENCE || (resetsCanvas && !isLast && (duration === 0 || saveAsReference !== 0) && type !== FRAME_LF)))
|
|
124
|
+
saveBeforeCT = r.bool();
|
|
125
|
+
let gab = DEFAULT_GAB, filter = DEFAULT_EPF();
|
|
126
|
+
if (!allDefault) {
|
|
127
|
+
r.skipBits(8 * r.u32(0, [0, 4], [16, 5], [48, 10]));
|
|
128
|
+
if (!r.bool()) {
|
|
129
|
+
gab = null;
|
|
130
|
+
if (r.bool()) {
|
|
131
|
+
gab = r.bool() ? Array.from({ length: 6 }, () => r.f16()) : DEFAULT_GAB;
|
|
132
|
+
for (let c = 0; c < 3; c++)
|
|
133
|
+
if (Math.abs(1 + 4 * (gab[2 * c] + gab[2 * c + 1])) < 1.1920929e-7)
|
|
134
|
+
throw new Error('JPEG XL: invalid Gaborish weights');
|
|
135
|
+
}
|
|
136
|
+
filter = epf(r, modular);
|
|
137
|
+
extensions(r);
|
|
138
|
+
}
|
|
139
|
+
extensions(r);
|
|
140
|
+
}
|
|
141
|
+
if (ecUpsampling.some(u => u < upsampling || u > 8))
|
|
142
|
+
throw new Error('JPEG XL: invalid extra channel upsampling');
|
|
143
|
+
const upsampledWidth = Math.ceil(width / 8 ** lfLevel), upsampledHeight = Math.ceil(height / 8 ** lfLevel);
|
|
144
|
+
const sampleWidth = Math.ceil(upsampledWidth / upsampling), sampleHeight = Math.ceil(upsampledHeight / upsampling);
|
|
145
|
+
const groupDim = 128 << groupSizeShift, groupsPerRow = Math.ceil(sampleWidth / groupDim), lfGroupsPerRow = Math.ceil(sampleWidth / (groupDim * 8));
|
|
146
|
+
return {
|
|
147
|
+
type, modular, flags, doYCbCr, encodedColorChannels, jpegUpsampling, upsampling, ecUpsampling, groupSizeShift, xqmScale, bqmScale,
|
|
148
|
+
numPasses, passShift, downsample, lastPass, lfLevel, haveCrop, x0, y0, width, height, blending, ecBlending, duration, isLast,
|
|
149
|
+
saveAsReference, resetsCanvas, saveBeforeCT, gab, epf: filter, upsampledWidth, upsampledHeight, sampleWidth, sampleHeight, groupDim, groupsPerRow,
|
|
150
|
+
numGroups: groupsPerRow * Math.ceil(sampleHeight / groupDim), lfGroupsPerRow, numLfGroups: lfGroupsPerRow * Math.ceil(sampleHeight / (groupDim * 8))
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
export function passShifts(f, pass) {
|
|
154
|
+
let max = 2, min = 3;
|
|
155
|
+
for (let i = 0;; i++) {
|
|
156
|
+
for (let j = 0; j < f.downsample.length; j++) {
|
|
157
|
+
if (i === f.lastPass[j])
|
|
158
|
+
min = [0, 0, 1, 0, 2, 0, 0, 0, 3][f.downsample[j]];
|
|
159
|
+
}
|
|
160
|
+
if (i === f.numPasses - 1)
|
|
161
|
+
min = 0;
|
|
162
|
+
if (i === pass)
|
|
163
|
+
return [min, max];
|
|
164
|
+
max = min - 1;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
export function toc(r, f) {
|
|
168
|
+
const single = f.numGroups === 1 && f.numPasses === 1;
|
|
169
|
+
const count = single ? 1 : 1 + f.numLfGroups + 1 + f.numGroups * f.numPasses;
|
|
170
|
+
if (count > 65536)
|
|
171
|
+
throw new Error('JPEG XL: too many sections');
|
|
172
|
+
let permutation = null;
|
|
173
|
+
if (r.bool()) {
|
|
174
|
+
const dec = new EntropyDecoder(r, 8);
|
|
175
|
+
dec.begin(r);
|
|
176
|
+
permutation = readPermutation(r, dec, count, 0);
|
|
177
|
+
dec.finish();
|
|
178
|
+
}
|
|
179
|
+
r.zeroPadToByte();
|
|
180
|
+
const sizes = [];
|
|
181
|
+
for (let i = 0; i < count; i++)
|
|
182
|
+
sizes.push(r.u32([0, 10], [1024, 14], [17408, 22], [4211712, 30]));
|
|
183
|
+
r.zeroPadToByte();
|
|
184
|
+
const offsets = [];
|
|
185
|
+
let at = r.bytePosition;
|
|
186
|
+
for (const size of sizes) {
|
|
187
|
+
offsets.push(at);
|
|
188
|
+
at += size;
|
|
189
|
+
}
|
|
190
|
+
if (permutation) {
|
|
191
|
+
const p = permutation;
|
|
192
|
+
return { offsets: p.map(i => offsets[i]), sizes: p.map(i => sizes[i]), end: at };
|
|
193
|
+
}
|
|
194
|
+
return { offsets, sizes, end: at };
|
|
195
|
+
}
|