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.
Files changed (59) hide show
  1. package/README.md +28 -8
  2. package/THIRD_PARTY_NOTICES.md +167 -2
  3. package/dist/animation.d.ts +18 -8
  4. package/dist/animation.js +23 -2
  5. package/dist/codecs/av1/cdef.js +197 -0
  6. package/dist/codecs/av1/data.js +141 -0
  7. package/dist/codecs/av1/decode.js +1769 -0
  8. package/dist/codecs/av1/filmgrain.js +219 -0
  9. package/dist/codecs/av1/frame.js +114 -0
  10. package/dist/codecs/av1/ipred.js +491 -0
  11. package/dist/codecs/av1/itx.js +950 -0
  12. package/dist/codecs/av1/loopfilter.js +149 -0
  13. package/dist/codecs/av1/msac.js +161 -0
  14. package/dist/codecs/av1/obu.js +516 -0
  15. package/dist/codecs/av1/postfilter.js +28 -0
  16. package/dist/codecs/av1/restoration.js +193 -0
  17. package/dist/codecs/av1/tables.js +160 -0
  18. package/dist/codecs/avif.js +581 -0
  19. package/dist/codecs/bmp.js +317 -0
  20. package/dist/codecs/hdr.d.ts +25 -0
  21. package/dist/codecs/hdr.js +201 -0
  22. package/dist/codecs/ico.js +48 -0
  23. package/dist/codecs/jpeg.js +2 -1
  24. package/dist/codecs/jxl/bits.js +126 -0
  25. package/dist/codecs/jxl/blend.js +117 -0
  26. package/dist/codecs/jxl/color.js +116 -0
  27. package/dist/codecs/jxl/dct.js +404 -0
  28. package/dist/codecs/jxl/entropy.js +556 -0
  29. package/dist/codecs/jxl/features.js +325 -0
  30. package/dist/codecs/jxl/filters.js +119 -0
  31. package/dist/codecs/jxl/frame-header.js +195 -0
  32. package/dist/codecs/jxl/frame.js +319 -0
  33. package/dist/codecs/jxl/headers.js +249 -0
  34. package/dist/codecs/jxl/idct.js +484 -0
  35. package/dist/codecs/jxl/modular.js +688 -0
  36. package/dist/codecs/jxl/quant.js +219 -0
  37. package/dist/codecs/jxl/upsample.js +52 -0
  38. package/dist/codecs/jxl/vardct.js +472 -0
  39. package/dist/codecs/jxl.js +89 -0
  40. package/dist/codecs/netpbm.js +239 -0
  41. package/dist/codecs/qoi.js +150 -0
  42. package/dist/codecs/vp8-enc-dsp.js +932 -0
  43. package/dist/codecs/vp8-enc-tables.js +78 -0
  44. package/dist/codecs/vp8-enc.js +1930 -0
  45. package/dist/codecs/webp.js +36 -2
  46. package/dist/image-type.d.ts +15 -1
  47. package/dist/image-type.js +45 -2
  48. package/dist/image.d.ts +47 -6
  49. package/dist/image.js +49 -3
  50. package/dist/index.d.ts +3 -2
  51. package/dist/index.js +2 -2
  52. package/dist/render/hinting.js +26 -18
  53. package/dist/render/layout.js +1 -1
  54. package/dist/render/svg.js +1 -1
  55. package/dist/render/unicode.js +1 -9
  56. package/dist/util/bytes.js +9 -0
  57. package/dist/util/resample.js +4 -2
  58. package/dist/version.js +1 -1
  59. package/package.json +25 -2
@@ -0,0 +1,319 @@
1
+ import { BitReader } from "./bits.js";
2
+ import { frameHeader, toc, passShifts, FRAME_LF, FRAME_REGULAR, FRAME_SKIP_PROGRESSIVE, BLEND_REPLACE, BLEND_ADD, BLEND_BLEND, BLEND_MULADD, BLEND_MUL, FLAG_PATCHES, FLAG_SPLINES, FLAG_NOISE, FLAG_USE_LF_FRAME } from "./frame-header.js";
3
+ import { ModularImage, channel, readTree, decodeModular } from "./modular.js";
4
+ import { upsample } from "./upsample.js";
5
+ import { gaborish, epf } from "./filters.js";
6
+ import { VarDCT, readVarDCTGlobal } from "./vardct.js";
7
+ import { readPatches, applyPatches, readSplines, renderSplines, readNoise, renderNoise } from "./features.js";
8
+ import { performBlending, PB_REPLACE, PB_ADD, PB_BLEND_ABOVE, PB_ADD_ABOVE, PB_MUL } from "./blend.js";
9
+ const NUM_QUANT_TABLES = 17;
10
+ class Sections {
11
+ d;
12
+ t;
13
+ single = null;
14
+ constructor(d, t) {
15
+ this.d = d;
16
+ this.t = t;
17
+ if (t.offsets.length === 1)
18
+ this.single = new BitReader(d, t.offsets[0], t.offsets[0] + t.sizes[0]);
19
+ }
20
+ get(i) {
21
+ if (this.single)
22
+ return this.single;
23
+ const end = this.t.offsets[i] + this.t.sizes[i];
24
+ if (end > this.d.length)
25
+ throw new Error('JPEG XL: truncated data');
26
+ return new BitReader(this.d, this.t.offsets[i], end);
27
+ }
28
+ }
29
+ export function decodeFrame(d, r, state) {
30
+ const image = state.image;
31
+ const f = frameHeader(r, image);
32
+ const t = toc(r, f);
33
+ const sections = new Sections(d, t);
34
+ if ((f.isLast || f.duration > 0) && (f.type === FRAME_REGULAR || f.type === FRAME_SKIP_PROGRESSIVE)) {
35
+ state.visible++;
36
+ state.invisible = 0;
37
+ }
38
+ else
39
+ state.invisible++;
40
+ const useLfFrame = (f.flags & FLAG_USE_LF_FRAME) !== 0;
41
+ const lf = lfGlobal(sections.get(0), f, image);
42
+ const vardct = lf.vardct ? new VarDCT(f, lf.vardct, lf.lfDequant, image.bitDepth.bits) : null;
43
+ const lfGroupDim = f.groupDim * 8;
44
+ for (let g = 0; g < f.numLfGroups; g++) {
45
+ const gr = sections.get(1 + g);
46
+ if (vardct && !useLfFrame)
47
+ vardct.readLfCoefficients(gr, g, lf.tree);
48
+ const x = (g % f.lfGroupsPerRow) * lfGroupDim, y = Math.floor(g / f.lfGroupsPerRow) * lfGroupDim;
49
+ decodeModularGroup(gr, lf, f, x, y, lfGroupDim, 3, 1000, 1 + f.numLfGroups + g);
50
+ vardct?.readHfMetadata(gr, g, lf.tree);
51
+ }
52
+ if (vardct)
53
+ vardct.readHfGlobal(sections.get(1 + f.numLfGroups), lf.tree);
54
+ for (let p = 0; p < f.numPasses; p++) {
55
+ const [min, max] = passShifts(f, p);
56
+ for (let g = 0; g < f.numGroups; g++) {
57
+ const gr = sections.get(2 + f.numLfGroups + p * f.numGroups + g);
58
+ vardct?.readPassGroup(gr, p, g);
59
+ const x = (g % f.groupsPerRow) * f.groupDim, y = Math.floor(g / f.groupsPerRow) * f.groupDim;
60
+ decodeModularGroup(gr, lf, f, x, y, f.groupDim, min, max, 1 + 3 * f.numLfGroups + NUM_QUANT_TABLES + f.numGroups * p + g);
61
+ }
62
+ }
63
+ lf.modular.undo();
64
+ let planes = modularToPlanes(lf, f, image);
65
+ let sigma = null;
66
+ if (vardct) {
67
+ const lfFrame = useLfFrame ? state.lfFrames[f.lfLevel] : null;
68
+ if (useLfFrame && !lfFrame)
69
+ throw new Error('JPEG XL: no LF frame');
70
+ const xyb = vardct.render(image.quantBias, image.quantBiasNumerator, lfFrame ? lfFrame.channels.slice(0, 3) : null);
71
+ const color = xyb.map((plane, c) => {
72
+ const hs = vardct.hs[c], vs = vardct.vs[c], w = Math.ceil(f.sampleWidth / (1 << hs)), h = Math.ceil(f.sampleHeight / (1 << vs));
73
+ const out = crop(plane, vardct.stride(c), w, h);
74
+ return hs || vs ? chromaUpsample(out, w, h, f.sampleWidth, f.sampleHeight) : out;
75
+ });
76
+ planes = { ...planes, channels: [...color, ...planes.channels] };
77
+ sigma = vardct.sigma;
78
+ }
79
+ planes = filter(planes, f, image, sigma, vardct ? vardct.bw : 0);
80
+ const numColor = planes.channels.length - image.extraChannels.length;
81
+ if (lf.patches)
82
+ applyPatches(planes, numColor, lf.patches, state.references, image.extraChannels);
83
+ const baseX = lf.vardct ? lf.vardct.baseX : 0, baseB = lf.vardct ? lf.vardct.baseB : 1;
84
+ if (lf.splines && numColor === 3)
85
+ renderSplines(planes, lf.splines, baseX, baseB);
86
+ planes = upsamplePlanes(planes, f, image);
87
+ if (lf.noise && numColor === 3)
88
+ renderNoise(planes, lf.noise, f.groupDim, state.visible, state.invisible, baseX, baseB);
89
+ const frame = render(planes, f, state);
90
+ return { ...frame, end: t.end };
91
+ }
92
+ function crop(src, stride, w, h) {
93
+ if (stride === w && src.length === w * h)
94
+ return src;
95
+ const out = new Float32Array(w * h);
96
+ for (let y = 0; y < h; y++)
97
+ out.set(src.subarray(y * stride, y * stride + w), y * w);
98
+ return out;
99
+ }
100
+ function chromaUpsample(src, sw, sh, w, h) {
101
+ let cur = src, cw = sw;
102
+ if (sw < w) {
103
+ const out = new Float32Array(w * sh);
104
+ for (let y = 0; y < sh; y++) {
105
+ for (let x = 0; x < w; x++) {
106
+ const c = x >> 1, n = x & 1 ? Math.min(c + 1, sw - 1) : Math.max(c - 1, 0);
107
+ out[y * w + x] = 0.75 * cur[y * sw + c] + 0.25 * cur[y * sw + n];
108
+ }
109
+ }
110
+ cur = out;
111
+ cw = w;
112
+ }
113
+ if (sh < h) {
114
+ const out = new Float32Array(cw * h);
115
+ for (let y = 0; y < h; y++) {
116
+ const c = y >> 1, n = y & 1 ? Math.min(c + 1, sh - 1) : Math.max(c - 1, 0);
117
+ for (let x = 0; x < cw; x++)
118
+ out[y * cw + x] = 0.75 * cur[c * cw + x] + 0.25 * cur[n * cw + x];
119
+ }
120
+ cur = out;
121
+ }
122
+ return cur;
123
+ }
124
+ function lfGlobal(r, f, image) {
125
+ const patches = f.flags & FLAG_PATCHES ? readPatches(r, image.extraChannels, f.width, f.height) : null;
126
+ const splines = f.flags & FLAG_SPLINES ? readSplines(r, f.width, f.height) : null;
127
+ const noise = f.flags & FLAG_NOISE ? readNoise(r) : null;
128
+ const lfDequant = r.bool() ? [1 / 32, 1 / 4, 1 / 2] : [r.f16(), r.f16(), r.f16()];
129
+ if (lfDequant.some(v => v / 128 < 1e-8))
130
+ throw new Error('JPEG XL: invalid LF dequantization');
131
+ const vardct = f.modular ? null : readVarDCTGlobal(r);
132
+ const numExtra = image.extraChannels.length;
133
+ let tree = null;
134
+ if (r.bool())
135
+ tree = readTree(r, Math.min(1 << 22, 1024 + Math.floor(f.width * f.height * (f.encodedColorChannels + numExtra) / 16)));
136
+ const w = f.sampleWidth, h = f.sampleHeight, list = [];
137
+ if (f.modular) {
138
+ if (f.doYCbCr) {
139
+ const hs = f.jpegUpsampling.map(u => u === 1 || u === 2), vs = f.jpegUpsampling.map(u => u === 1 || u === 3);
140
+ const anyH = hs.some(Boolean), anyV = vs.some(Boolean);
141
+ for (let c = 0; c < 3; c++) {
142
+ const u = f.jpegUpsampling[c];
143
+ const hshift = anyH && (u === 0 || u === 3) ? 1 : 0, vshift = anyV && (u === 0 || u === 2) ? 1 : 0;
144
+ list.push(channel(Math.ceil(w / (1 << hshift)), Math.ceil(h / (1 << vshift)), hshift, vshift));
145
+ }
146
+ }
147
+ else
148
+ for (let c = 0; c < f.encodedColorChannels; c++)
149
+ list.push(channel(w, h));
150
+ }
151
+ const colorShift = Math.log2(f.upsampling);
152
+ for (const up of f.ecUpsampling) {
153
+ const shift = Math.log2(up) - colorShift;
154
+ list.push(channel(Math.ceil(f.upsampledWidth / up), Math.ceil(f.upsampledHeight / up), shift, shift));
155
+ }
156
+ const modular = new ModularImage(list, image.bitDepth.bits);
157
+ decodeModular(r, modular, 0, tree, f.groupDim, false);
158
+ return { patches, splines, noise, lfDequant, vardct, tree, modular };
159
+ }
160
+ function decodeModularGroup(r, lf, f, x0, y0, dim, minShift, maxShift, stream) {
161
+ const full = lf.modular.channels, nbMeta = lf.modular.nbMeta;
162
+ let c = nbMeta;
163
+ while (c < full.length && full[c].w <= f.groupDim && full[c].h <= f.groupDim)
164
+ c++;
165
+ const parts = [], channels = [];
166
+ for (; c < full.length; c++) {
167
+ const fc = full[c], shift = Math.min(fc.hshift, fc.vshift);
168
+ if (shift > maxShift || shift < minShift)
169
+ continue;
170
+ const x = x0 >> fc.hshift, y = y0 >> fc.vshift;
171
+ const w = Math.max(0, Math.min(dim >> fc.hshift, fc.w - x)), h = Math.max(0, Math.min(dim >> fc.vshift, fc.h - y));
172
+ if (!w || !h)
173
+ continue;
174
+ parts.push([fc, x, y]);
175
+ channels.push(channel(w, h, fc.hshift, fc.vshift));
176
+ }
177
+ if (!channels.length)
178
+ return;
179
+ const gi = new ModularImage(channels, lf.modular.bitDepth);
180
+ decodeModular(r, gi, stream, lf.tree, Infinity, true);
181
+ parts.forEach(([fc, x, y], i) => {
182
+ const g = gi.channels[i];
183
+ for (let row = 0; row < g.h; row++)
184
+ fc.data.set(g.data.subarray(row * g.w, (row + 1) * g.w), (y + row) * fc.w + x);
185
+ });
186
+ }
187
+ function modularToPlanes(lf, f, image) {
188
+ const ch = lf.modular.channels.slice(lf.modular.nbMeta);
189
+ const out = [];
190
+ const toFloat = (c, depth = image.bitDepth) => {
191
+ const o = new Float32Array(c.w * c.h);
192
+ if (depth.float)
193
+ for (let i = 0; i < o.length; i++)
194
+ o[i] = floatSample(c.data[i], depth.bits, depth.expBits);
195
+ else {
196
+ const scale = 1 / (2 ** depth.bits - 1);
197
+ for (let i = 0; i < o.length; i++)
198
+ o[i] = c.data[i] * scale;
199
+ }
200
+ return o;
201
+ };
202
+ if (!f.modular) {
203
+ image.extraChannels.forEach((ec, i) => out.push(toFloat(ch[i], ec.bitDepth)));
204
+ return { width: f.sampleWidth, height: f.sampleHeight, channels: out };
205
+ }
206
+ const numColor = f.encodedColorChannels;
207
+ if (image.xybEncoded) {
208
+ const [y, x, b] = ch;
209
+ const X = new Float32Array(y.w * y.h), Y = new Float32Array(y.w * y.h), B = new Float32Array(y.w * y.h);
210
+ const mx = lf.lfDequant[0] / 128, my = lf.lfDequant[1] / 128, mb = lf.lfDequant[2] / 128;
211
+ for (let i = 0; i < X.length; i++) {
212
+ X[i] = x.data[i] * mx;
213
+ Y[i] = y.data[i] * my;
214
+ B[i] = (b.data[i] + y.data[i]) * mb;
215
+ }
216
+ out.push(X, Y, B);
217
+ }
218
+ else
219
+ for (let c = 0; c < numColor; c++)
220
+ out.push(toFloat(ch[c]));
221
+ image.extraChannels.forEach((ec, i) => out.push(toFloat(ch[numColor + i], ec.bitDepth)));
222
+ return { width: f.sampleWidth, height: f.sampleHeight, channels: out };
223
+ }
224
+ function filter(p, f, image, sigma, sigmaStride = 0) {
225
+ if (!f.gab && !f.epf)
226
+ return p;
227
+ const numColor = p.channels.length - image.extraChannels.length;
228
+ let color = numColor === 1 ? [p.channels[0], p.channels[0], p.channels[0]] : p.channels.slice(0, 3);
229
+ if (f.gab)
230
+ color = gaborish(color, p.width, p.height, f.gab);
231
+ if (f.epf)
232
+ color = epf(color, p.width, p.height, f.epf, sigma, sigmaStride);
233
+ return { ...p, channels: [...color.slice(0, numColor), ...p.channels.slice(numColor)] };
234
+ }
235
+ function upsamplePlanes(p, f, image) {
236
+ const numColor = p.channels.length - image.extraChannels.length, W = f.upsampledWidth, H = f.upsampledHeight;
237
+ const channels = p.channels.map((c, i) => {
238
+ const up = i < numColor ? f.upsampling : f.ecUpsampling[i - numColor];
239
+ return up === 1 ? c : upsample(c, Math.ceil(W / up), Math.ceil(H / up), up, image, W, H);
240
+ });
241
+ return { width: W, height: H, channels };
242
+ }
243
+ const f32 = new Float32Array(1), u32 = new Uint32Array(f32.buffer);
244
+ function floatSample(v, bits, expBits) {
245
+ if (bits === 32) {
246
+ u32[0] = v;
247
+ return f32[0];
248
+ }
249
+ const mantissaBits = bits - expBits - 1, sign = (v >>> (bits - 1)) & 1;
250
+ const mantissa = v & ((1 << mantissaBits) - 1), exp = (v >>> mantissaBits) & ((1 << expBits) - 1), bias = (1 << (expBits - 1)) - 1;
251
+ let value;
252
+ if (exp === 0)
253
+ value = mantissa / 2 ** mantissaBits * 2 ** (1 - bias);
254
+ else if (exp === (1 << expBits) - 1)
255
+ value = mantissa ? NaN : Infinity;
256
+ else
257
+ value = (1 + mantissa / 2 ** mantissaBits) * 2 ** (exp - bias);
258
+ return sign ? -value : value;
259
+ }
260
+ function render(p, f, state) {
261
+ const image = state.image;
262
+ let planes = p;
263
+ if (f.type === FRAME_LF) {
264
+ state.lfFrames[f.lfLevel - 1] = planes;
265
+ return { header: f, canvas: null };
266
+ }
267
+ const canReference = !f.isLast && (f.duration === 0 || f.saveAsReference !== 0) && f.type !== FRAME_LF;
268
+ if (canReference && f.saveBeforeCT) {
269
+ state.references[f.saveAsReference] = planes;
270
+ state.referenceIsXYB[f.saveAsReference] = image.xybEncoded;
271
+ }
272
+ planes = state.output.transform(planes, f.doYCbCr);
273
+ const normal = f.type === FRAME_REGULAR || f.type === FRAME_SKIP_PROGRESSIVE;
274
+ const canvas = normal ? blend(planes, f, state) : planes;
275
+ if (canReference && !f.saveBeforeCT) {
276
+ state.references[f.saveAsReference] = canvas;
277
+ state.referenceIsXYB[f.saveAsReference] = false;
278
+ }
279
+ return { header: f, canvas: normal ? canvas : null };
280
+ }
281
+ const PB_MODES = [];
282
+ PB_MODES[BLEND_REPLACE] = PB_REPLACE;
283
+ PB_MODES[BLEND_ADD] = PB_ADD;
284
+ PB_MODES[BLEND_BLEND] = PB_BLEND_ABOVE;
285
+ PB_MODES[BLEND_MULADD] = PB_ADD_ABOVE;
286
+ PB_MODES[BLEND_MUL] = PB_MUL;
287
+ const blending = (b) => ({ mode: PB_MODES[b.mode], alpha: b.alphaChannel, clamp: b.clamp });
288
+ function blend(p, f, state) {
289
+ const image = state.image, W = image.width, H = image.height, numExtra = image.extraChannels.length;
290
+ const n = p.channels.length, numColor = n - numExtra;
291
+ const fullReplace = !f.haveCrop && f.blending.mode === BLEND_REPLACE && f.ecBlending.every(b => b.mode === BLEND_REPLACE);
292
+ if (fullReplace && p.width === W && p.height === H)
293
+ return p;
294
+ const source = (c) => state.references[c < numColor ? f.blending.source : f.ecBlending[c - numColor].source];
295
+ for (let c = 0; c < n; c++)
296
+ if (state.referenceIsXYB[c < numColor ? f.blending.source : f.ecBlending[c - numColor].source] && source(c))
297
+ throw new Error('JPEG XL: blending onto an XYB reference');
298
+ const out = [];
299
+ for (let c = 0; c < n; c++) {
300
+ const bg = source(c), o = new Float32Array(W * H);
301
+ if (bg) {
302
+ if (bg.width < W || bg.height < H)
303
+ throw new Error('JPEG XL: blending onto a cropped reference');
304
+ const plane = bg.channels[c] ?? bg.channels[0];
305
+ for (let y = 0; y < H; y++)
306
+ o.set(plane.subarray(y * bg.width, y * bg.width + W), y * W);
307
+ }
308
+ out.push(o);
309
+ }
310
+ const x0 = Math.max(f.x0, 0), y0 = Math.max(f.y0, 0), x1 = Math.min(f.x0 + p.width, W), y1 = Math.min(f.y0 + p.height, H);
311
+ if (x1 <= x0 || y1 <= y0)
312
+ return { width: W, height: H, channels: out };
313
+ const color = blending(f.blending), ec = f.ecBlending.map(blending);
314
+ for (let y = y0; y < y1; y++) {
315
+ const i = y * W + x0, j = (y - f.y0) * p.width + (x0 - f.x0);
316
+ performBlending(out, i, p.channels, j, out, i, x1 - x0, numColor, color, ec, image.extraChannels);
317
+ }
318
+ return { width: W, height: H, channels: out };
319
+ }
@@ -0,0 +1,249 @@
1
+ import { extensions, unpackSigned } from "./bits.js";
2
+ import { EntropyDecoder } from "./entropy.js";
3
+ export const EXTRA_ALPHA = 0, EXTRA_SPOT = 2, EXTRA_BLACK = 4;
4
+ export const CS_RGB = 0, CS_GREY = 1, CS_XYB = 2;
5
+ export const WP_D65 = 1, WP_CUSTOM = 2, WP_E = 10, WP_DCI = 11;
6
+ export const PRI_SRGB = 1, PRI_CUSTOM = 2, PRI_BT2100 = 9, PRI_P3 = 11;
7
+ export const TF_BT709 = 1, TF_UNKNOWN = 2, TF_LINEAR = 8, TF_SRGB = 13, TF_PQ = 16, TF_DCI = 17, TF_HLG = 18;
8
+ const SIZE_DIST = [[1, 9], [1, 13], [1, 18], [1, 30]];
9
+ function defaultWidth(ratio, height) {
10
+ switch (ratio) {
11
+ case 1: return height;
12
+ case 2: return Math.floor(height * 12 / 10);
13
+ case 3: return Math.floor(height * 4 / 3);
14
+ case 4: return Math.floor(height * 3 / 2);
15
+ case 5: return Math.floor(height * 16 / 9);
16
+ case 6: return Math.floor(height * 5 / 4);
17
+ default: return height * 2;
18
+ }
19
+ }
20
+ export function sizeHeader(r) {
21
+ const div8 = r.bool();
22
+ const height = div8 ? 8 * (1 + r.bits(5)) : r.u32(...SIZE_DIST);
23
+ const ratio = r.bits(3);
24
+ const width = ratio ? defaultWidth(ratio, height) : div8 ? 8 * (1 + r.bits(5)) : r.u32(...SIZE_DIST);
25
+ return [width, height];
26
+ }
27
+ function previewHeader(r) {
28
+ const div8 = r.bool();
29
+ const dist8 = [16, 32, [1, 5], [33, 9]];
30
+ const dist = [[1, 6], [65, 8], [321, 10], [1345, 12]];
31
+ const height = div8 ? 8 * r.u32(...dist8) : r.u32(...dist);
32
+ const ratio = r.bits(3);
33
+ const width = ratio ? defaultWidth(ratio, height) : div8 ? 8 * r.u32(...dist8) : r.u32(...dist);
34
+ return [width, height];
35
+ }
36
+ export function bitDepth(r) {
37
+ if (r.bool()) {
38
+ const bits = r.u32(32, 16, 24, [1, 6]), expBits = r.bits(4) + 1;
39
+ const mantissaBits = bits - expBits - 1;
40
+ if (expBits < 2 || expBits > 8 || mantissaBits < 2 || mantissaBits > 23)
41
+ throw new Error('JPEG XL: invalid float sample bit depth');
42
+ return { float: true, bits, expBits };
43
+ }
44
+ const bits = r.u32(8, 10, 12, [1, 6]);
45
+ if (bits > 31)
46
+ throw new Error('JPEG XL: invalid bit depth');
47
+ return { float: false, bits, expBits: 0 };
48
+ }
49
+ function name(r) {
50
+ const length = r.u32(0, [0, 4], [16, 5], [48, 10]);
51
+ r.skipBits(8 * length);
52
+ }
53
+ function extraChannel(r) {
54
+ if (r.bool())
55
+ return { type: EXTRA_ALPHA, bitDepth: { float: false, bits: 8, expBits: 0 }, dimShift: 0, alphaAssociated: false, spot: [] };
56
+ const type = r.enum();
57
+ const depth = bitDepth(r);
58
+ const dimShift = r.u32(0, 3, 4, [1, 3]);
59
+ name(r);
60
+ let alphaAssociated = false;
61
+ const spot = [];
62
+ if (type === EXTRA_ALPHA)
63
+ alphaAssociated = r.bool();
64
+ else if (type === EXTRA_SPOT)
65
+ for (let i = 0; i < 4; i++)
66
+ spot.push(r.f16());
67
+ else if (type === 5)
68
+ r.u32(1, [0, 2], [3, 4], [19, 8]);
69
+ else if (type > 6 && type !== 15 && type !== 16)
70
+ throw new Error('JPEG XL: invalid extra channel type');
71
+ return { type, bitDepth: depth, dimShift, alphaAssociated, spot };
72
+ }
73
+ function customXY(r) {
74
+ const d = [[0, 19], [524288, 19], [1048576, 20], [2097152, 21]];
75
+ return unpackSigned(r.u32(...d)) / 1e6;
76
+ }
77
+ export function colourEncoding(r) {
78
+ const c = {
79
+ wantICC: false, colourSpace: CS_RGB, whitePoint: WP_D65, white: [], primaries: PRI_SRGB, primariesXY: [],
80
+ transfer: TF_SRGB, gamma: -1, renderingIntent: 1
81
+ };
82
+ if (r.bool())
83
+ return c;
84
+ c.wantICC = r.bool();
85
+ c.colourSpace = r.enum();
86
+ if (c.colourSpace > 3)
87
+ throw new Error('JPEG XL: invalid color space');
88
+ if (c.wantICC)
89
+ return c;
90
+ if (c.colourSpace !== CS_XYB) {
91
+ c.whitePoint = r.enum();
92
+ if (c.whitePoint === WP_CUSTOM)
93
+ c.white = [customXY(r), customXY(r)];
94
+ else if (c.whitePoint !== WP_D65 && c.whitePoint !== WP_E && c.whitePoint !== WP_DCI)
95
+ throw new Error('JPEG XL: invalid white point');
96
+ }
97
+ if (c.colourSpace !== CS_XYB && c.colourSpace !== CS_GREY) {
98
+ c.primaries = r.enum();
99
+ if (c.primaries === PRI_CUSTOM)
100
+ for (let i = 0; i < 6; i++)
101
+ c.primariesXY.push(customXY(r));
102
+ else if (c.primaries !== PRI_SRGB && c.primaries !== PRI_BT2100 && c.primaries !== PRI_P3)
103
+ throw new Error('JPEG XL: invalid primaries');
104
+ }
105
+ if (r.bool()) {
106
+ c.gamma = r.bits(24) / 1e7;
107
+ c.transfer = -1;
108
+ }
109
+ else {
110
+ c.transfer = r.enum();
111
+ if (![TF_BT709, TF_UNKNOWN, TF_LINEAR, TF_SRGB, TF_PQ, TF_DCI, TF_HLG].includes(c.transfer))
112
+ throw new Error('JPEG XL: invalid transfer function');
113
+ }
114
+ c.renderingIntent = r.enum();
115
+ if (c.renderingIntent > 3)
116
+ throw new Error('JPEG XL: invalid rendering intent');
117
+ return c;
118
+ }
119
+ const f16s = (r, n) => Array.from({ length: n }, () => r.f16());
120
+ export function imageHeader(r) {
121
+ if (r.bits(16) !== 0xaff)
122
+ throw new Error('JPEG XL: invalid signature');
123
+ const [width, height] = sizeHeader(r);
124
+ const h = {
125
+ width, height, orientation: 1, preview: null, animation: null, bitDepth: { float: false, bits: 8, expBits: 0 },
126
+ modular16BitBuffers: true, extraChannels: [], xybEncoded: true,
127
+ colour: { wantICC: false, colourSpace: CS_RGB, whitePoint: WP_D65, white: [], primaries: PRI_SRGB, primariesXY: [], transfer: TF_SRGB, gamma: -1, renderingIntent: 1 },
128
+ toneMapping: { intensityTarget: 255, minNits: 0, relativeToMaxDisplay: false, linearBelow: 0 },
129
+ opsinInverse: [11.031566901960783, -9.866943921568629, -0.16462299647058826, -3.254147380392157, 4.418770392156863, -0.16462299647058826, -3.6588512862745097, 2.7129230470588235, 1.9459282392156863],
130
+ opsinBias: [-0.0037930732552754493, -0.0037930732552754493, -0.0037930732552754493],
131
+ quantBias: [1 - 0.05465007330715401, 1 - 0.07005449891748593, 1 - 0.049935103337343655],
132
+ quantBiasNumerator: 0.145, up2: UP2, up4: UP4, up8: UP8
133
+ };
134
+ const allDefault = r.bool();
135
+ if (!allDefault) {
136
+ const extraFields = r.bool();
137
+ if (extraFields) {
138
+ h.orientation = 1 + r.bits(3);
139
+ if (r.bool())
140
+ sizeHeader(r);
141
+ if (r.bool()) {
142
+ const [w, ph] = previewHeader(r);
143
+ h.preview = { width: w, height: ph };
144
+ }
145
+ if (r.bool()) {
146
+ h.animation = {
147
+ tpsNumerator: r.u32(100, 1000, [1, 10], [1, 30]), tpsDenominator: r.u32(1, 1001, [1, 8], [1, 10]),
148
+ numLoops: r.u32(0, [0, 3], [0, 16], [0, 32]), haveTimecodes: r.bool()
149
+ };
150
+ }
151
+ }
152
+ h.bitDepth = bitDepth(r);
153
+ h.modular16BitBuffers = r.bool();
154
+ const numExtra = r.u32(0, 1, [2, 4], [1, 12]);
155
+ if (numExtra > 256)
156
+ throw new Error('JPEG XL: too many extra channels');
157
+ for (let i = 0; i < numExtra; i++)
158
+ h.extraChannels.push(extraChannel(r));
159
+ h.xybEncoded = r.bool();
160
+ h.colour = colourEncoding(r);
161
+ if (extraFields && !r.bool()) {
162
+ h.toneMapping = { intensityTarget: r.f16(), minNits: r.f16(), relativeToMaxDisplay: r.bool(), linearBelow: r.f16() };
163
+ const t = h.toneMapping;
164
+ if (t.intensityTarget <= 0 || t.minNits < 0 || t.minNits > t.intensityTarget || t.linearBelow < 0 || (t.relativeToMaxDisplay && t.linearBelow > 1)) {
165
+ throw new Error('JPEG XL: invalid tone mapping');
166
+ }
167
+ }
168
+ extensions(r);
169
+ }
170
+ if (!r.bool()) {
171
+ if (h.xybEncoded && !r.bool()) {
172
+ h.opsinInverse = f16s(r, 9);
173
+ h.opsinBias = f16s(r, 3);
174
+ h.quantBias = f16s(r, 3);
175
+ h.quantBiasNumerator = r.f16();
176
+ }
177
+ const mask = r.bits(3);
178
+ if (mask & 1)
179
+ h.up2 = f16s(r, 15);
180
+ if (mask & 2)
181
+ h.up4 = f16s(r, 55);
182
+ if (mask & 4)
183
+ h.up8 = f16s(r, 210);
184
+ }
185
+ return h;
186
+ }
187
+ export function skipICC(r) {
188
+ const size = r.u64();
189
+ if (size > 1 << 28)
190
+ throw new Error('JPEG XL: ICC profile too large');
191
+ const dec = new EntropyDecoder(r, 41);
192
+ dec.begin(r);
193
+ const kind = (b) => b >= 97 && b <= 122 || b >= 65 && b <= 90 ? 0 : b >= 48 && b <= 57 || b === 46 || b === 44 ? 1 : -1;
194
+ let b1 = 0, b2 = 0;
195
+ for (let i = 0; i < size; i++) {
196
+ let ctx = 0;
197
+ if (i > 128) {
198
+ const k1 = kind(b1), k2 = kind(b2);
199
+ const p1 = k1 >= 0 ? k1 : b1 <= 1 ? 2 + b1 : b1 <= 15 ? 4 : b1 >= 241 && b1 <= 254 ? 5 : b1 === 255 ? 6 : 7;
200
+ const p2 = k2 >= 0 ? k2 : b2 <= 15 ? 2 : b2 >= 241 ? 3 : 4;
201
+ ctx = 1 + p1 + 8 * p2;
202
+ }
203
+ const b = dec.read(r, ctx);
204
+ if (b > 255)
205
+ throw new Error('JPEG XL: invalid ICC profile');
206
+ b2 = b1;
207
+ b1 = b;
208
+ }
209
+ dec.finish();
210
+ }
211
+ const UP2 = [
212
+ -0.01716200, -0.03452303, -0.04022174, -0.02921014, -0.00624645, 0.14111091, 0.28896755, 0.00278718, -0.01610267,
213
+ 0.56661550, 0.03777607, -0.01986694, -0.03144731, -0.01185068, -0.00213539
214
+ ];
215
+ const UP4 = [
216
+ -0.02419067, -0.03491987, -0.03693351, -0.03094285, -0.00529785, -0.01663432, -0.03556863, -0.03888905, -0.03516850,
217
+ -0.00989469, 0.23651958, 0.33392945, -0.01073543, -0.01313181, -0.03556694, 0.13048175, 0.40103025, 0.03951150,
218
+ -0.02077584, 0.46914198, -0.00209270, -0.01484589, -0.04064806, 0.18942530, 0.56279892, 0.06674400, -0.02335494,
219
+ -0.03551682, -0.00754830, -0.02267919, -0.02363578, 0.00315804, -0.03399098, -0.01359519, -0.00091653, -0.00335467,
220
+ -0.01163294, -0.01610294, -0.00974088, -0.00191622, -0.01095446, -0.03198464, -0.04455121, -0.02799790, -0.00645912,
221
+ 0.06390599, 0.22963888, 0.00630981, -0.01897349, 0.67537268, 0.08483369, -0.02534994, -0.02205197, -0.01667999,
222
+ -0.00384443
223
+ ];
224
+ const UP8 = [
225
+ -0.02928613, -0.03706353, -0.03783812, -0.03324558, -0.00447632, -0.02519406, -0.03752601, -0.03901508, -0.03663285,
226
+ -0.00646649, -0.02066407, -0.03838633, -0.04002101, -0.03900035, -0.00901973, -0.01626393, -0.03954148, -0.04046620,
227
+ -0.03979621, -0.01224485, 0.29895328, 0.35757708, -0.02447552, -0.01081748, -0.04314594, 0.23903219, 0.41119301,
228
+ -0.00573046, -0.01450239, -0.04246845, 0.17567618, 0.45220643, 0.02287757, -0.01936783, -0.03583255, 0.11572472,
229
+ 0.47416733, 0.06284440, -0.02685066, 0.42720050, -0.02248939, -0.01155273, -0.04562755, 0.28689496, 0.49093869,
230
+ -0.00007891, -0.01545926, -0.04562659, 0.21238920, 0.53980934, 0.03369474, -0.02070211, -0.03866988, 0.14229550,
231
+ 0.56593398, 0.08045181, -0.02888298, -0.03680918, -0.00542229, -0.02920477, -0.02788574, -0.02118180, -0.03942402,
232
+ -0.00775547, -0.02433614, -0.03193943, -0.02030828, -0.04044014, -0.01074016, -0.01930822, -0.03620399, -0.01974125,
233
+ -0.03919545, -0.01456093, -0.00045072, -0.00360110, -0.01020207, -0.01231907, -0.00638988, -0.00071592, -0.00279122,
234
+ -0.00957115, -0.01288327, -0.00730937, -0.00107783, -0.00210156, -0.00890705, -0.01317668, -0.00813895, -0.00153491,
235
+ -0.02128481, -0.04173044, -0.04831487, -0.03293190, -0.00525260, -0.01720322, -0.04052736, -0.05045706, -0.03607317,
236
+ -0.00738030, -0.01341764, -0.03965629, -0.05151616, -0.03814886, -0.01005819, 0.18968273, 0.33063684, -0.01300105,
237
+ -0.01372950, -0.04017465, 0.13727832, 0.36402234, 0.01027890, -0.01832107, -0.03365072, 0.08734506, 0.38194295,
238
+ 0.04338228, -0.02525993, 0.56408126, 0.00458352, -0.01648227, -0.04887868, 0.24585519, 0.62026135, 0.04314807,
239
+ -0.02213737, -0.04158014, 0.16637289, 0.65027023, 0.09621636, -0.03101388, -0.04082742, -0.00904519, -0.02790922,
240
+ -0.02117818, 0.00798662, -0.03995711, -0.01243427, -0.02231705, -0.02946266, 0.00992055, -0.03600283, -0.01684920,
241
+ -0.00111684, -0.00411204, -0.01297130, -0.01723725, -0.01022545, -0.00165306, -0.00313110, -0.01218016, -0.01763266,
242
+ -0.01125620, -0.00231663, -0.01374149, -0.03797620, -0.05142937, -0.03117307, -0.00581914, -0.01064003, -0.03608089,
243
+ -0.05272168, -0.03375670, -0.00795586, 0.09628104, 0.27129991, -0.00353779, -0.01734151, -0.03153981, 0.05686230,
244
+ 0.28500998, 0.02230594, -0.02374955, 0.68214326, 0.05018048, -0.02320852, -0.04383616, 0.18459474, 0.71517975,
245
+ 0.10805613, -0.03263677, -0.03637639, -0.01394373, -0.02511203, -0.01728636, 0.05407331, -0.02867568, -0.01893131,
246
+ -0.00240854, -0.00446511, -0.01636187, -0.02377053, -0.01522848, -0.00333334, -0.00819975, -0.02964169, -0.04499287,
247
+ -0.02745350, -0.00612408, 0.02727416, 0.19446600, 0.00159832, -0.02232473, 0.74982506, 0.11452620, -0.03348048,
248
+ -0.01605681, -0.02070339, -0.00458223
249
+ ];