rapier-jxl 1.1.21 → 2.1.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/lossless.mjs CHANGED
@@ -3,7 +3,7 @@
3
3
  // alpha is dropped and a grey picture keeps one channel; up to 2048 colours try a palette against direct encoded
4
4
  // cost. YCoCg decorrelates colour; samples choose gradient/average prediction; each channel takes one prefix code, zero runs
5
5
  // as LZ77 copies; groups of 256x256 pixels, each an independent section.
6
- import {BitWriter} from './bits.mjs';
6
+ import {BitWriter, complete, part} from './bits.mjs';
7
7
  import {writeImageHeader, writeModularFrameHeader, groupLayout, assembleCodestream, GROUP_DIM} from './frame.mjs';
8
8
  import {buildCode, writePrefixCode} from './prefix.mjs';
9
9
  import {AVERAGE_PREDICTOR, GRADIENT_PREDICTOR, ALPHABET, leaf, channelTree, writeTree, writeModularHeader, writeChannelHistograms, codeChannel} from './modular.mjs';
@@ -33,7 +33,7 @@ export function inspectPixels(rgba, width, height, {palette: wantPalette = true}
33
33
  }
34
34
  if (wantPalette && seen.size < MAX_PALETTE) {
35
35
  const entries = [...seen.keys()];
36
- const byte = (k, c) => Math.floor(k / 256 ** c) % 256;
36
+ const byte = (k, c) => (k >>> 8 * c) & 255;
37
37
  const weight = k => (0.299 * byte(k, 0) + 0.587 * byte(k, 1) + 0.114 * byte(k, 2) + 0.01) * (channels === 4 ? byte(k, 3) : 1);
38
38
  entries.sort((a, b) => weight(a) - weight(b));
39
39
  const index = new Map([[0, 0]]);
@@ -43,13 +43,17 @@ export function inspectPixels(rgba, width, height, {palette: wantPalette = true}
43
43
  return {colour, alpha, channels, palette};
44
44
  }
45
45
 
46
- export function encodeLossless(rgba, width, height, {shape = inspectPixels(rgba, width, height)} = {}) {
46
+ export function encodeLossless(rgba, width, height, options) { return complete(losslessSteps(rgba, width, height, options)); }
47
+
48
+ // The same, a group of one pass per step.
49
+ export function* losslessSteps(rgba, width, height, {shape = inspectPixels(rgba, width, height), colorSpace} = {}) {
47
50
  // The complete stream cost includes the palette, tree, histograms and group headers. A candidate that reaches
48
51
  // the stream limit must not hide a smaller valid representation of the same pixels.
49
52
  let best, oversized;
50
- for (const candidate of shape.palette ? [shape, {...shape, palette: null}] : [shape]) {
53
+ const candidates = shape.palette ? [shape, {...shape, palette: null}] : [shape];
54
+ for (let i = 0; i < candidates.length; i++) {
51
55
  try {
52
- const bytes = encodeLosslessPlan(rgba, width, height, candidate);
56
+ const bytes = yield* part(planSteps(rgba, width, height, candidates[i], colorSpace), i, candidates.length);
53
57
  if (!best || bytes.length < best.length) best = bytes;
54
58
  } catch (error) { if (error.code !== 'JXL_SIZE') throw error; oversized = error; }
55
59
  }
@@ -57,7 +61,7 @@ export function encodeLossless(rgba, width, height, {shape = inspectPixels(rgba,
57
61
  return best;
58
62
  }
59
63
 
60
- function encodeLosslessPlan(rgba, width, height, shape) {
64
+ function* planSteps(rgba, width, height, shape, colorSpace) {
61
65
  const {channels, palette} = shape;
62
66
  const layout = groupLayout(width, height);
63
67
  const streamChannels = palette ? 2 : channels; // the palette's meta channel and the index channel
@@ -91,13 +95,14 @@ function encodeLosslessPlan(rgba, width, height, shape) {
91
95
  const pictureLeaf = c => leaves[palette ? (layout.single ? 1 : 0) : c];
92
96
  const pictureChannels = palette ? 1 : channels;
93
97
 
94
- const forGroups = visit => {
95
- for (let gy = 0; gy < layout.groupsY; gy++) for (let gx = 0; gx < layout.groupsX; gx++) {
96
- const x0 = gx * GROUP_DIM, y0 = gy * GROUP_DIM, w = Math.min(GROUP_DIM, width - x0), h = Math.min(GROUP_DIM, height - y0);
97
- fill(x0, y0, w, h);
98
- visit(gy * layout.groupsX + gx, w, h);
99
- }
98
+ // A group's planes filled, then visited; each group of each pass is one step, row by row.
99
+ const groups = layout.groupsX * layout.groupsY, total = 2 * groups;
100
+ const group = (g, visit) => {
101
+ const x0 = g % layout.groupsX * GROUP_DIM, y0 = (g / layout.groupsX | 0) * GROUP_DIM, w = Math.min(GROUP_DIM, width - x0), h = Math.min(GROUP_DIM, height - y0);
102
+ fill(x0, y0, w, h);
103
+ visit(w, h);
100
104
  };
105
+ let done = 0;
101
106
 
102
107
  // Three small spatial samples choose gradient/average per channel. The same token code prices
103
108
  // both the prefix header and raw residual bits. Fixed YCoCg avoids choosing RGB from a sparse corner
@@ -128,11 +133,14 @@ function encodeLosslessPlan(rgba, width, height, shape) {
128
133
  // Pass one: token histograms per leaf.
129
134
  const freqs = leaves.map(() => new Uint32Array(ALPHABET)), freqOf = l => freqs[leaves.indexOf(l)];
130
135
  if (paletteRows) codeChannel(null, freqOf(leaves[0]), paletteRows, palette.colours.length, channels, leaves[0]);
131
- forGroups((g, w, h) => { for (let c = 0; c < pictureChannels; c++) codeChannel(null, freqOf(pictureLeaf(c)), planes[c], w, h, pictureLeaf(c)); });
136
+ for (let g = 0; g < groups; g++) {
137
+ group(g, (w, h) => { for (let c = 0; c < pictureChannels; c++) codeChannel(null, freqOf(pictureLeaf(c)), planes[c], w, h, pictureLeaf(c)); });
138
+ yield ++done / total;
139
+ }
132
140
 
133
141
  // Pass two: the sections.
134
142
  const header = new BitWriter(256);
135
- writeImageHeader(header, width, height, shape.colour, shape.alpha);
143
+ writeImageHeader(header, width, height, shape.colour, shape.alpha, {colorSpace});
136
144
  writeModularFrameHeader(header, {alpha: shape.alpha});
137
145
  const global = new BitWriter(4096);
138
146
  global.write(1, 1); // default DC quantisation
@@ -144,17 +152,21 @@ function encodeLosslessPlan(rgba, width, height, shape) {
144
152
  if (paletteRows) codeChannel(global, codeOf(leaves[0]), paletteRows, palette.colours.length, channels, leaves[0]);
145
153
  const sections = [];
146
154
  if (layout.single) {
147
- forGroups((g, w, h) => { for (let c = 0; c < pictureChannels; c++) codeChannel(global, codeOf(pictureLeaf(c)), planes[c], w, h, pictureLeaf(c)); });
155
+ group(0, (w, h) => { for (let c = 0; c < pictureChannels; c++) codeChannel(global, codeOf(pictureLeaf(c)), planes[c], w, h, pictureLeaf(c)); });
156
+ yield 1;
148
157
  sections.push(global.finish());
149
158
  } else {
150
159
  sections.push(global.finish());
151
160
  for (let i = 0; i < layout.dcGroupsX * layout.dcGroupsY + 1; i++) sections.push(new Uint8Array(0));
152
- forGroups((g, w, h) => {
153
- const section = new BitWriter(w * h * pictureChannels + 64);
154
- writeModularHeader(section, {useGlobalTree: true, transforms: []});
155
- for (let c = 0; c < pictureChannels; c++) codeChannel(section, codeOf(pictureLeaf(c)), planes[c], w, h, pictureLeaf(c));
156
- sections.push(section.finish());
157
- });
161
+ for (let g = 0; g < groups; g++) {
162
+ group(g, (w, h) => {
163
+ const section = new BitWriter(w * h * pictureChannels + 64);
164
+ writeModularHeader(section, {useGlobalTree: true, transforms: []});
165
+ for (let c = 0; c < pictureChannels; c++) codeChannel(section, codeOf(pictureLeaf(c)), planes[c], w, h, pictureLeaf(c));
166
+ sections.push(section.finish());
167
+ });
168
+ yield ++done / total;
169
+ }
158
170
  }
159
171
  return assembleCodestream(header, sections);
160
172
  }
package/lossy.mjs CHANGED
@@ -3,31 +3,47 @@
3
3
  // depth, then each squeezed channel quantised by a step that halves with every level (chroma coarser than luma),
4
4
  // carried as the multiplier of that channel's tree leaf so the decoder scales the residuals back. The finest
5
5
  // low-pass image is kept exact. Quality maps to libjxl's distance (90 is 1.0).
6
- import {BitWriter} from './bits.mjs';
7
- import {writeImageHeader, writeModularFrameHeader, groupLayout, finishSections, assembleCodestream, GROUP_DIM, DC_GROUP_DIM} from './frame.mjs';
6
+ import {BitWriter, complete} from './bits.mjs';
7
+ import {writeImageHeader, writeModularFrameHeader, groupLayout, finishSections, assembleCodestream} from './frame.mjs';
8
8
  import {ZERO_PREDICTOR, GRADIENT_PREDICTOR, ALPHABET, leaf, channelTree, streamTree, writeTree, writeModularHeader, writeChannelHistograms, codeChannel} from './modular.mjs';
9
9
  import {forwardSqueeze, defaultSqueezeParams} from './squeeze.mjs';
10
10
  import {inspectPixels} from './lossless.mjs';
11
11
 
12
12
  const QUALITY_FACTOR = 0.35, LUMA_FACTOR = 1.1;
13
+ // Groups of 1,024 pixels (group size shift 3), DC groups of 8,192: fewer sections and fewer bytes, and a picture up to
14
+ // 8,192 pixels on a side is one DC group, so no coarse Squeeze channel meets a DC group border inside it. A decoder
15
+ // that reads such a border from the wrong origin (jxl-rs 0.7.4, where one coarse tile holds several finer ones) reads
16
+ // these right. A picture within 256 pixels is one group either way and keeps the header of 256-pixel groups.
17
+ const SQUEEZE_GROUP = 1024, SQUEEZE_DC_GROUP = 8 * SQUEEZE_GROUP;
13
18
 
14
19
  // libjxl's JxlEncoderDistanceFromQuality.
15
20
  export function distanceFromQuality(quality) {
16
21
  return quality >= 100 ? 0 : quality >= 30 ? 0.1 + (100 - quality) * 0.09 : 53 / 3000 * quality * quality - 23 / 20 * quality + 25;
17
22
  }
18
23
 
24
+ // distance ** 1.2 in arithmetic every engine rounds alike (`**` does not): the fifth root by Newton's iteration from a
25
+ // fixed start, times the distance. Within three ulps of V8's power, and the same integer steps at every quality by 0.001.
26
+ function distancePower(distance) {
27
+ let root = 1;
28
+ for (let i = 0; i < 24; i++) root = (4 * root + distance / (root * root * root * root)) / 5;
29
+ return distance * root;
30
+ }
31
+
19
32
  // The quantisation step of a squeezed channel: `component` 0 luma, 1-2 chroma, 3 an extra channel.
20
33
  export function quantiserFor(component, hshift, vshift, distance) {
21
34
  let shift = Math.min(16, hshift + vshift);
22
35
  if (shift > 0) shift--;
23
36
  // Alpha is authored coverage: quantising its Squeeze residuals changes compositing even when RGB is close.
24
37
  if (component === 3) return 1;
25
- const base = 0.25 * distance ** 1.2, scale = 2 ** shift;
38
+ const base = 0.25 * distancePower(distance), scale = 1 << shift;
26
39
  const q = component === 1 || component === 2 ? base * QUALITY_FACTOR * Math.max(0.5, 1024 / scale) : base * QUALITY_FACTOR * LUMA_FACTOR * (163.84 / scale);
27
40
  return Math.max(1, Math.floor(q));
28
41
  }
29
42
 
30
- export function encodeLossy(rgba, width, height, {quality = 90, shape = inspectPixels(rgba, width, height, {palette: false})} = {}) {
43
+ export function encodeLossy(rgba, width, height, options) { return complete(lossySteps(rgba, width, height, options)); }
44
+
45
+ // The same as steps: Squeeze and the plan one step, then a section of each pass per step.
46
+ export function* lossySteps(rgba, width, height, {quality = 90, shape = inspectPixels(rgba, width, height, {palette: false}), colorSpace} = {}) {
31
47
  const distance = distanceFromQuality(quality);
32
48
  if (!(distance > 0)) throw new Error('lossy encoding needs a quality below 100');
33
49
  const {colour, alpha, channels: count} = shape;
@@ -42,20 +58,23 @@ export function encodeLossy(rgba, width, height, {quality = 90, shape = inspectP
42
58
  if (alpha) planes[count - 1][p] = rgba[i + 3];
43
59
  }
44
60
  const channels = planes.map((data, c) => ({w: width, h: height, hshift: 0, vshift: 0, data, component: colour === 3 ? c : c === 0 ? 0 : 3}));
45
- // Avoid empty chroma channels at the global/group boundary in a one-wide colour image.
46
- const params = width === 1 && height > GROUP_DIM && colour === 3 ? defaultSqueezeParams(channels).filter(p => !p.horizontal) : [];
61
+ // Avoid empty chroma channels at the global/group boundary in a one-wide colour image. Any other colour picture one
62
+ // pixel wide or high names its Squeeze steps, the decoder's defaults, instead of leaving them to the decoder: one that
63
+ // regenerates them (jxl-rs 0.7.4) leaves out the chroma steps an axis of one pixel makes empty, and misreads the rest.
64
+ const thin = width === 1 && height > SQUEEZE_GROUP && colour === 3;
65
+ const params = thin ? defaultSqueezeParams(channels).filter(p => !p.horizontal) : (width === 1 || height === 1) && colour === 3 ? defaultSqueezeParams(channels) : [];
47
66
  const squeezed = forwardSqueeze(channels, params.length ? params : undefined);
48
67
  const predictorOf = ch => ch.residual ? ZERO_PREDICTOR : GRADIENT_PREDICTOR;
49
- const quantiserOf = ch => quantiserFor(ch.component, ch.hshift + (params.length && ch.component > 0 && ch.component < 3 ? 1 : 0), ch.vshift, distance);
68
+ const quantiserOf = ch => quantiserFor(ch.component, ch.hshift + (thin && ch.component > 0 && ch.component < 3 ? 1 : 0), ch.vshift, distance);
50
69
  const transforms = [...(colour === 3 ? [{type: 'rct', beginC: 0, rctType: 6}] : []), {type: 'squeeze', params}];
51
70
 
52
71
  // The sections and the channel pieces each holds, in the decoder's order: the global section takes the channels
53
72
  // up to the first wider or taller than a group; DC groups take the rest with both shifts of three or more, AC
54
73
  // groups the rest; a piece is the group's rectangle at the channel's scale. A channel's number inside a section
55
74
  // is its place among that section's pieces, so every section gets its own subtree under the stream property.
56
- const layout = groupLayout(width, height), dcGroups = layout.dcGroupsX * layout.dcGroupsY, acGroups = layout.groupsX * layout.groupsY;
75
+ const layout = groupLayout(width, height, SQUEEZE_GROUP), dcGroups = layout.dcGroupsX * layout.dcGroupsY, acGroups = layout.groupsX * layout.groupsY;
57
76
  let globalCount = 0;
58
- while (globalCount < squeezed.length && squeezed[globalCount].w <= GROUP_DIM && squeezed[globalCount].h <= GROUP_DIM) globalCount++;
77
+ while (globalCount < squeezed.length && squeezed[globalCount].w <= SQUEEZE_GROUP && squeezed[globalCount].h <= SQUEEZE_GROUP) globalCount++;
59
78
  const rectOf = (ch, x0, y0, dim) => {
60
79
  const x = x0 >> ch.hshift, y = y0 >> ch.vshift;
61
80
  return {x, y, w: Math.min(ch.w, x + (dim >> ch.hshift)) - x, h: Math.min(ch.h, y + (dim >> ch.vshift)) - y};
@@ -68,7 +87,7 @@ export function encodeLossy(rgba, width, height, {quality = 90, shape = inspectP
68
87
  for (let c = globalCount; c < squeezed.length; c++) {
69
88
  const ch = squeezed[c];
70
89
  if (Math.min(ch.hshift, ch.vshift) < 3) continue;
71
- const rect = rectOf(ch, gx * DC_GROUP_DIM, gy * DC_GROUP_DIM, DC_GROUP_DIM);
90
+ const rect = rectOf(ch, gx * SQUEEZE_DC_GROUP, gy * SQUEEZE_DC_GROUP, SQUEEZE_DC_GROUP);
72
91
  if (rect.w > 0 && rect.h > 0) section.pieces.push({c, rect});
73
92
  }
74
93
  sections.push(section);
@@ -78,7 +97,7 @@ export function encodeLossy(rgba, width, height, {quality = 90, shape = inspectP
78
97
  for (let c = globalCount; c < squeezed.length; c++) {
79
98
  const ch = squeezed[c];
80
99
  if (Math.min(ch.hshift, ch.vshift) > 2) continue;
81
- const rect = rectOf(ch, gx * GROUP_DIM, gy * GROUP_DIM, GROUP_DIM);
100
+ const rect = rectOf(ch, gx * SQUEEZE_GROUP, gy * SQUEEZE_GROUP, SQUEEZE_GROUP);
82
101
  if (rect.w > 0 && rect.h > 0) section.pieces.push({c, rect});
83
102
  }
84
103
  sections.push(section);
@@ -92,7 +111,7 @@ export function encodeLossy(rgba, width, height, {quality = 90, shape = inspectP
92
111
  const tree = withPieces.length === 1 ? channelTree(withPieces[0].pieces.map(p => p.leaf))
93
112
  : streamTree(withPieces.map(section => ({streamId: section.streamId, tree: channelTree(section.pieces.map(p => p.leaf))})));
94
113
 
95
- const scratch = new Int16Array(GROUP_DIM * GROUP_DIM);
114
+ const scratch = new Int16Array(SQUEEZE_GROUP * SQUEEZE_GROUP);
96
115
  const dataOf = piece => {
97
116
  const ch = squeezed[piece.c], rect = piece.rect;
98
117
  if (rect.w === ch.w && rect.h === ch.h) return ch.data;
@@ -104,12 +123,17 @@ export function encodeLossy(rgba, width, height, {quality = 90, shape = inspectP
104
123
  // chroma residual channels that own no piece between channels that do.
105
124
  const owners = [...new Set(sections.flatMap(section => section.pieces.map(piece => piece.c)))].sort((a, b) => a - b);
106
125
  const histogramOf = new Map(owners.map((c, i) => [c, i]));
107
- const freqs = owners.map(() => new Uint32Array(ALPHABET));
108
- for (const section of sections) for (const piece of section.pieces) codeChannel(null, freqs[histogramOf.get(piece.c)], dataOf(piece), piece.rect.w, piece.rect.h, piece.leaf);
126
+ const freqs = owners.map(() => new Uint32Array(ALPHABET)), total = 1 + 2 * sections.length;
127
+ let done = 1;
128
+ yield done / total;
129
+ for (const section of sections) {
130
+ for (const piece of section.pieces) codeChannel(null, freqs[histogramOf.get(piece.c)], dataOf(piece), piece.rect.w, piece.rect.h, piece.leaf);
131
+ yield ++done / total;
132
+ }
109
133
 
110
134
  const header = new BitWriter(256);
111
- writeImageHeader(header, width, height, colour, alpha);
112
- writeModularFrameHeader(header, {alpha});
135
+ writeImageHeader(header, width, height, colour, alpha, {colorSpace});
136
+ writeModularFrameHeader(header, {alpha, shift: width > 256 || height > 256 ? 3 : 1});
113
137
  // Section order: DC global, DC groups, AC global (empty), AC groups.
114
138
  const writers = Array.from({length: layout.single ? 1 : 2 + dcGroups + acGroups}, () => null);
115
139
  const global = writers[0] = new BitWriter(65536);
@@ -125,6 +149,7 @@ export function encodeLossy(rgba, width, height, {quality = 90, shape = inspectP
125
149
  if (section.pieces.length) writeModularHeader(w, {useGlobalTree: true, transforms: []});
126
150
  }
127
151
  for (const piece of section.pieces) codeChannel(w, histograms[histogramOf.get(piece.c) + 1].code, dataOf(piece), piece.rect.w, piece.rect.h, piece.leaf);
152
+ yield ++done / total;
128
153
  }
129
154
  return assembleCodestream(header, finishSections(writers));
130
155
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rapier-jxl",
3
- "version": "1.1.21",
4
- "description": "A JPEG XL encoder in pure JavaScript: lossless, lossy, and a JPEG carried as its coefficients. One file of 36.3 kB, 15.0 kB gzipped. The encoder inside Rapier.",
3
+ "version": "2.1.0",
4
+ "description": "A JPEG XL encoder in pure JavaScript, lossless and lossy, in one file of 19.5 kB, 8.5 kB gzipped; JPEGs and photographs are their own doors. The encoder inside Rapier.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "./index.mjs",
@@ -15,18 +15,27 @@
15
15
  "types": "./index.d.mts",
16
16
  "default": "./rapier-jxl.min.mjs"
17
17
  },
18
- "./lossless": {
19
- "types": "./lossless.d.mts",
20
- "default": "./lossless.min.mjs"
21
- },
22
18
  "./jpeg": {
23
19
  "types": "./jpeg.d.mts",
24
- "default": "./jpeg.min.mjs"
20
+ "default": "./jpeg.mjs"
25
21
  },
26
22
  "./photo": {
27
23
  "types": "./photo.d.mts",
28
- "default": "./photo.min.mjs"
24
+ "default": "./photo.mjs"
25
+ },
26
+ "./jpeg-ans": {
27
+ "types": "./jpeg-ans.d.mts",
28
+ "default": "./jpeg-ans.mjs"
29
+ },
30
+ "./photo-ans": {
31
+ "types": "./photo-ans.d.mts",
32
+ "default": "./photo-ans.mjs"
33
+ },
34
+ "./effort": {
35
+ "types": "./effort.d.mts",
36
+ "default": "./effort.mjs"
29
37
  },
38
+ "./writer": "./writer.mjs",
30
39
  "./package.json": "./package.json"
31
40
  },
32
41
  "files": [
@@ -0,0 +1,3 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Same checked photographic surface; effort 2 also compares a completed ANS candidate.
3
+ export * from './photo.mjs';
@@ -0,0 +1,2 @@
1
+ /*! Rapier JXL 2.1.0 | MIT License Copyright (c) 2026 rapier.website Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
2
+ function*t(t,n,o){let r,e;for(;!(r=t.next(e)).done;)e=yield(n+r.value)/o;return r.value}function n(t){return t>=0?2*t:2*-t-1}function o(t){return 31-Math.clz32(t)}function r(t){return t<=1?0:32-Math.clz32(t-1)}function e(t){if(0===t)return 0;const n=t<0?32768:0;if(!((t=Math.abs(t))<65520))throw new Error("half float out of range: "+t);let o=0,r=t;for(;r>=2;)r/=2,o++;for(;r<1;)r*=2,o--;o<-14?(r=16384*t,o=-15):r-=1;let e=Math.round(1024*r);return 1024===e&&(e=0,o++),n|o+15<<10|e}function s(t){if(t>tt.bytes)throw et("JXL_SIZE","The encoded picture exceeds 16 MiB.")}function i(t){try{return t()}catch(t){if(t instanceof RangeError&&!t.code)throw et("JXL_MEMORY","Not enough memory for this picture.");throw t}}function f(t,n){t.t([[9,1],[13,1],[18,1],[30,1]],n)}function c(t,n,o,r,e,{xyb:s=!1,orientation:i=1,colorSpace:c}={}){t.write(16,2815),t.write(1,0),f(t,o),t.write(3,0),f(t,n),t.write(1,0);const l=1!==i;t.write(1,l?1:0),l&&t.write(6,i-1),t.write(4,8),e?t.write(3,5):t.write(2,0),t.write(1,s?1:0),3===r&&"display-p3"===c?t.write(21,903568):3===r?t.write(1,1):t.write(15,14100),l&&t.write(1,1),t.write(2,0),t.write(1,1),t.o()}function l(t,n){t.write(n?7:5,0),t.write(11,1)}function a(t,n){const o=n.map(t=>t.length);!function(t,n){t.write(1,0),t.o();for(const o of n)t.t([[10,0],[14,1024],[22,17408],[30,4211712]],o);t.o()}(t,o);const r=t.finish(),e=r.length+o.reduce((t,n)=>t+n,0);s(e);const i=new Uint8Array(e);i.set(r);let f=r.length;for(const t of n)i.set(t,f),f+=t.length;return i}function u(t,n){const o=new Uint8Array(t.length),r=[];for(let n=0;n<t.length;n++)t[n]>0&&r.push(n);if(!r.length)return o;if(1===r.length)return o[r[0]]=1,o;for(let e=1;;e*=2){const s=r.map(n=>({w:Math.max(t[n],e),i:n,l:null,r:null})).sort((t,n)=>t.w-n.w||t.i-n.i),i=[];let f=0,c=0;const l=()=>f<s.length&&(c>=i.length||s[f].w<=i[c].w)?s[f++]:i[c++];for(;s.length-f+(i.length-c)>1;){const t=l(),n=l();i.push({w:t.w+n.w,i:-1,l:t,r:n})}const a=[[l(),0]];let u=0;for(;a.length;){const[t,n]=a.pop();t.i>=0?(o[t.i]=n,n>u&&(u=n)):a.push([t.l,n+1],[t.r,n+1])}if(u<=n)return o}}function h(t,n,o,r,e,s){const i=[];for(;i.push(r&e),r>>=s;)r--;for(let r=i.length-1;r>=0;r--)t.push(o),n.push(i[r])}function y(t,n){let o=0;for(let r=0;r<n;r++)o=o<<1|1&t,t>>=1;return o}function w(t){const n=new Uint32Array(17),o=new Uint32Array(17),r=new Uint16Array(t.length);for(const o of t)n[o]++;n[0]=0;for(let t=1,r=0;t<=16;t++)r=r+n[t-1]<<1,o[t]=r;for(let n=0;n<t.length;n++)t[n]&&(r[n]=y(o[t[n]]++,t[n]));return r}function d(t){const n=[];for(let o=0;o<t.length;o++)t[o]>0&&n.push(o);const o=n.length,r=n[o-1]+1||1,e=o<=4?new Uint8Array(r):u(t,15).subarray(0,r);let s=0;if(o<=4){n.sort((n,o)=>t[o]-t[n]||n-o),s=4===o&&t[n[0]]>t[n[2]]+t[n[3]]?1:0;const r=o<2?[0]:2===o?[1,1]:3===o?[1,2,2]:s?[1,2,3,3]:[2,2,2,2];n.forEach((t,n)=>{e[t]=r[n]}),n.sort((t,n)=>e[t]-e[n]||t-n)}return{u:r,M:e,A:w(e),p:o&&o<=4?n:null,U:s}}function M(t,n){if(n.u<=1)return;if(n.p){const r=o(n.u-1)+1;t.write(2,1),t.write(2,n.p.length-1);for(const o of n.p)t.write(r,o);return void(4===n.p.length&&t.write(1,n.U))}const r=n.M,e=[],s=[];let i=r.length-1;for(;i>0&&!r[i];)i--;let f=8;for(let t=0;t<=i;){const n=r[t];let o=1;for(;t+o<=i&&r[t+o]===n;)o++;let c=o;if(n&&n!==f&&(e.push(n),s.push(0),c--,f=n),c<3)for(let t=0;t<c;t++)e.push(n),s.push(0);else h(e,s,n?16:17,c-3,n?3:7,n?2:3);t+=o}const c=new Uint32Array(18);for(const t of e)c[t]++;let l=0;for(const t of c)t&&l++;1===l&&(c[e[0]?0:1]=c[e[0]]);const a=u(c,5),y=w(a);let d=18;for(;d>0&&!a[it[d-1]];)d--;t.write(2,0);for(let n=0;n<d;n++){const o=a[it[n]];t.write(ft[o],ct[o])}for(let n=0;n<e.length;n++){const o=e[n];t.write(a[o],y[o]),16===o?t.write(2,s[n]):17===o&&t.write(3,s[n])}}function A(t,n=0,o=0){return{split:t,m:n,I:o,S:1<<t}}function p(t,n,o=15){t.write(r(o+1),n.split),n.split!==o&&(t.write(r(n.split+1),n.m),t.write(r(n.split-n.m+1),n.I))}function g(t,n,r){if(n<t.S)return r[0]=n,r[1]=0,void(r[2]=0);const e=o(n),s=n-(1<<e),i=e-t.m-t.I;r[0]=t.S+(e-t.split<<t.m+t.I|s>>e-t.m<<t.I|s&(1<<t.I)-1),r[1]=i,r[2]=n>>t.I&(1<<i)-1}function U(t,n,o,r=0){g(t,n,lt),o[r+lt[0]]++}function b(t,n,o,r,e=0){g(o,r,lt);const s=e+lt[0];t.write(n.M[s],n.A[s]),lt[1]&&t.write(lt[1],lt[2])}function m(t,n){let o=0;for(const t of n)o=Math.max(o,t);const e=o?r(o+1):0;if(e<=3){if(t.write(1,1),t.write(2,e),e)for(const o of n)t.write(e,o);return}t.write(1,0),t.write(1,0);const s=A(8),i=A(4),f=n.length>=64,c=new Uint32Array(f?257:o+1),l=[];for(let t=0;t<n.length;){let o=1;for(;f&&t+o<n.length&&n[t+o]===n[t];)o++;if(o>=8)l.push([n[t],o-1]),c[n[t]]++,U(i,o-1-7,c,224),t+=o;else{for(let r=0;r<o;r++)l.push([n[t],0]),c[n[t]]++;t+=o}}const a=d(c);if(!f){I(t,{L:new Uint8Array(1),J:[{config:s,code:a}]});for(const[n]of l)b(t,a,s,n);return}const u=new Uint32Array(1);u[0]=1,I(t,{X:{T:224,minLength:7,_:i},L:new Uint8Array([1,0]),J:[{config:A(0),code:d(u)},{config:s,code:a}]});for(const[n,o]of l)b(t,a,s,n),o&&b(t,a,i,o-7,224)}function I(t,{X:n=null,L:r,J:e}){t.write(1,n?1:0),n&&(t.t([[0,224],[0,512],[0,4096],[15,8]],n.T),t.t([[0,3],[0,4],[2,5],[8,9]],n.minLength),p(t,n._,8)),r.length>1&&m(t,r),t.write(1,1);for(const n of e)p(t,n.config,15);for(const n of e){const r=n.code.u-1;if(r){const n=o(r);t.write(1,1),t.write(4,n),t.write(n,r-(1<<n))}else t.write(1,0)}for(const n of e)M(t,n.code)}function x(t,n=0,o=1){return{N:t,offset:n,C:o,context:-1}}function S(t){return function(t,n){const o=(r,e)=>{if(r===e)return n?t[r].O:t[r];const s=r+e>>1;return function(t,n,o,r){return{P:t,F:n,left:o,right:r}}(n,n?t[s].q:s,o(s+1,e),o(r,s))};return o(0,t.length-1)}(t,0)}function E(t,o){const r=[o],e=[],s=[];for(const t of r){if(t.left){e.push(t.P+1,n(t.F)),r.push(t.left,t.right);continue}t.context=s.length,s.push(t);const o=1===t.C?0:31-Math.clz32(t.C&-t.C);e.push(0,t.N,n(t.offset),o,(t.C>>o)-1)}const i=new Uint32Array(64);for(const t of e)U(wt,t,i);const f=d(i);I(t,{L:new Uint8Array(6),J:[{config:wt,code:f}]});for(const n of e)b(t,f,wt,n);return s}function L(t,n){const o=[[3,0],[6,8],[10,72],[13,1096]];if("rct"===n.type)t.write(2,0),t.t(o,n.G),t.t([[0,6],[2,0],[4,2],[6,10]],n.j??6);else if("palette"===n.type)t.write(2,1),t.t(o,n.G),t.t([[0,1],[0,3],[0,4],[13,1]],n.k),t.t([[8,0],[10,256],[12,1280],[16,5376]],n.R),t.t([[0,0],[8,1],[10,257],[16,1281]],n.B??0),t.write(4,n.N??0);else{if("squeeze"!==n.type)throw new Error("unknown transform "+n.type);{t.write(2,2);const r=n.Z??[];t.t([[0,0],[4,1],[6,9],[8,41]],r.length);for(const n of r)t.write(1,n.D?1:0),t.write(1,n.Y?1:0),t.t(o,n.G),t.t([[0,1],[0,2],[0,3],[4,4]],n.k)}}}function J(t,{H:n=!0,K:o=[]}={}){t.write(1,n?1:0),t.write(1,1),t.t([[0,0],[0,1],[4,2],[8,18]],o.length);for(const n of o)L(t,n)}function X(t,n,o,r=t=>t.context){const e=new Uint8Array(n.length+1);for(const t of n)e[t.context]=r(t)+1;const s=new Uint32Array(2);s[1]=1;const i=[{config:wt,code:d(s)}],f=Math.max(...e);for(let t=0;t<f;t++)i.push({config:wt,code:d(o[t])});return I(t,{X:yt,L:e,J:i}),i}function T(t,o,r,e,s,i){const f=i.N,c=i.offset,l=i.C,a=wt,u=ht;let h=0;const y=(n,r=a,e=0)=>{t?b(t,o,r,n,e):U(r,n,o,e)},w=()=>{if(h){if(h>=8)y(0),y(h-7-1,u,224);else for(let t=0;t<h;t++)y(0);h=0}},d=(t,o)=>{const e=r[t];let s;1===l?s=e-o-c:(s=Math.round((e-o-c)/l),r[t]=o+c+s*l),0!==s?(w(),y(n(s))):h++};for(let t=0,n=0;t<s;t++)for(let o=0;o<e;o++,n++){let s;if(0===f)s=0;else{const i=o?r[n-1]:t?r[n-e]:0,c=t?r[n-e]:i;if(1===f)s=i;else if(2===f)s=c;else if(3===f)s=(i+c)/2|0;else{const l=i+c-(o&&t?r[n-e-1]:i);if(5===f){const t=i<c?i:c,n=i<c?c:i;s=l<t?t:l>n?n:l}else{if(4!==f)throw new Error("predictor "+f+" is not coded here");s=Math.abs(l-i)<Math.abs(l-c)?i:c}}}d(n,s)}w()}function _(t,n,o,{palette:r=!0}={}){const e=n*o;let s=!0,i=!0;for(let n=0,o=0;o<e&&(255===t[n+3]||(s=!1,i))&&(t[n]===t[n+1]&&t[n]===t[n+2]||(i=!1,s));o++,n+=4);const f=i?1:3,c=!s,l=f+(c?1:0),a=n=>i?c?t[n]+256*t[n+3]:t[n]:t[n]+256*t[n+1]+65536*t[n+2]+(c?16777216*t[n+3]:0),u=new Map;let h=null;for(let t=0,n=0;r&&n<e;n++,t+=4){const n=a(t);if(0!==n&&!u.has(n)&&(u.set(n,1),u.size>=2048))break}if(r&&u.size<2048){const t=[...u.keys()],n=(t,n)=>t>>>8*n&255,o=t=>(.299*n(t,0)+.587*n(t,1)+.114*n(t,2)+.01)*(4===l?n(t,3):1);t.sort((t,n)=>o(t)-o(n));const r=new Map([[0,0]]);t.forEach((t,n)=>r.set(t,n+1)),h={colours:[0,...t],index:r,key:a,byte:n}}return{colour:f,alpha:c,channels:l,palette:h}}function*N(t,n,o,r,e){const{channels:s,palette:i}=r,f=function(t,n,o=256){const r=Math.ceil(t/o),e=Math.ceil(n/o);return{V:r,W:e,$:Math.ceil(t/(8*o)),tt:Math.ceil(n/(8*o)),nt:1===r&&1===e}}(n,o),u=i?2:s,h=Array.from({length:u},()=>x(ut)),y=S(h),w=Array.from({length:u},()=>new Int16Array(65536)),A=(o,r,e,f)=>{for(let c=0;c<f;c++){let f=4*((r+c)*n+o),l=c*e;for(let n=0;n<e;n++,f+=4,l++)if(i)w[0][l]=i.index.get(i.key(f));else if(s>=3){const n=t[f],o=t[f+1],r=t[f+2],e=n-r,i=r+(e>>1),c=o-i;w[0][l]=i+(c>>1),w[1][l]=e,w[2][l]=c,4===s&&(w[3][l]=t[f+3])}else w[0][l]=t[f],2===s&&(w[1][l]=t[f+3])}};let p=null;if(i){const t=i.colours.length;p=new Int16Array(t*s),i.colours.forEach((n,o)=>{for(let r=0;r<s;r++)p[r*t+o]=i.byte(n,r)})}const g=t=>h[i?f.nt?1:0:t],U=i?1:s,b=f.V*f.W,m=2*b,I=(t,r)=>{const e=t%f.V*st,s=(t/f.V|0)*st,i=Math.min(st,n-e),c=Math.min(st,o-s);A(e,s,i,c),r(i,c)};let L=0;if(!i){const t=t=>{const n=d(t),o=new rt(128);M(o,n);let r=o.ot;for(let o=0;o<t.length;o++)t[o]&&(r+=t[o]*(n.M[o]+Math.max(0,o<224?o-1:o-236)));return r},r=Math.min(n,32),e=Math.min(o,32),i=[ut,at].map(t=>({N:t,rt:h.map(()=>new Uint32Array(257))}));for(const t of[0,.5,1]){A(Math.floor((n-r)*t),Math.floor((o-e)*t),r,e);for(const t of i)for(let n=0;n<s;n++)T(null,t.rt[n],w[n],r,e,x(t.N))}h.forEach((n,o)=>{const r=i.map(n=>t(n.rt[o])),e=r[1]<r[0]?1:0;n.N=i[e].N})}const _=i?[{type:"palette",G:0,k:s,R:i.colours.length}]:s>=3?[{type:"rct",G:0,j:6}]:[],N=h.map(()=>new Uint32Array(257)),v=t=>N[h.indexOf(t)];p&&T(null,v(h[0]),p,i.colours.length,s,h[0]);for(let t=0;t<b;t++)I(t,(t,n)=>{for(let o=0;o<U;o++)T(null,v(g(o)),w[o],t,n,g(o))}),yield++L/m;const C=new rt(256);c(C,n,o,r.colour,r.alpha,{colorSpace:e}),function(t,{alpha:n,shift:o=1}){t.write(9,8),n&&t.write(2,0),t.write(2,o),l(t,n)}(C,{alpha:r.alpha});const O=new rt(4096);O.write(1,1),O.write(1,1);const P=E(O,y),F=X(O,P,N,t=>h.indexOf(t)),q=t=>F[h.indexOf(t)+1].code;J(O,{H:!0,K:_}),p&&T(O,q(h[0]),p,i.colours.length,s,h[0]);const G=[];if(f.nt)I(0,(t,n)=>{for(let o=0;o<U;o++)T(O,q(g(o)),w[o],t,n,g(o))}),yield 1,G.push(O.finish());else{G.push(O.finish());for(let t=0;t<f.$*f.tt+1;t++)G.push(new Uint8Array(0));for(let t=0;t<b;t++)I(t,(t,n)=>{const o=new rt(t*n*U+64);J(o,{H:!0,K:[]});for(let r=0;r<U;r++)T(o,q(g(r)),w[r],t,n,g(r));G.push(o.finish())}),yield++L/m}return a(C,G)}function v(t,n,o,r){let e=0;return e+=t[n]*dt[r],e+=t[n+o]*dt[r+1],e+=t[n+2*o]*dt[r+2],e+=t[n+3*o]*dt[r+3],e+=t[n+4*o]*dt[r+4],e+=t[n+5*o]*dt[r+5],e+=t[n+6*o]*dt[r+6],e+=t[n+7*o]*dt[r+7],e}function C(t,n,o){const r=new Float64Array(192),e=new Float64Array(64),s=new Float64Array(192);return(i,f)=>{for(let e=0;e<8;e++)for(let s=0;s<8;s++){const c=4*(Math.min(o-1,8*f+e)*n+Math.min(n-1,8*i+s)),l=t[c],a=t[c+1],u=t[c+2],h=8*e+s;r[h]=.299*l+.587*a+.114*u-128,r[64+h]=-.168735892*l-.331264108*a+.5*u,r[128+h]=.5*l-.418687589*a-.081312411*u}for(let t=0;t<3;t++){for(let n=0;n<8;n++)for(let o=0;o<8;o++)e[8*n+o]=v(r,64*t+8*n,1,8*o);for(let n=0;n<8;n++)for(let o=0;o<8;o++)s[64*t+8*n+o]=v(e,o,8,8*n)}return s}}function*O(t,n,o,r,e){const s=Math.ceil(n/8),i=Math.ceil(o/8),f=r>=30?.1+.09*(100-r):53/3e3*r*r-1.15*r+25,c=[0,1,2].map(t=>({h:1,v:1,stride:s,rows:i,quant:Int32Array.from(Mt,(n,o)=>Math.max(1,Math.round(16*f*(t?18:10)*(o?n:.25)))),coeffs:new Int16Array(s*i*64)})),l=C(t,n,o),a=(t,n)=>{for(let o=t;o<n;o++)for(let t=0;t<s;t++){const n=l(t,o),r=64*(o*s+t);for(let t=0;t<3;t++)for(let o=0;o<64;o++)c[t].coeffs[r+o]=Math.round(16*n[64*t+o]/c[t].quant[o])}};for(let t=0;t<i;t+=32)a(t,Math.min(i,t+32)),yield Math.min(i,t+32)/i;let u=null;for(let n=3;n<t.length;n+=4)if(255!==t[n]){u=t;break}return{width:n,height:o,components:c,quantScale:16,ycbcr:!0,orientation:1,alpha:u,colorSpace:e}}function P(n,o,r,f,c){const{quality:l,colorSpace:a}=function(t={},n=100){if(null===t||"object"!=typeof t)throw et("JXL_INPUT","Options are an object: {quality, colorSpace}.");if(void 0!==t.quality&&(n=t.quality),"number"!=typeof n||!Number.isFinite(n)||n<1||n>100)throw et("JXL_INPUT","Quality is a number from 1 to 100.");const o=void 0===t.colorSpace?"srgb":t.colorSpace;if("srgb"!==o&&"display-p3"!==o)throw et("JXL_INPUT","The colour space is srgb or display-p3.");return{quality:n,colorSpace:o}}(f,90),u=function(t={}){if(null===t||"object"!=typeof t)throw et("JXL_INPUT","Options are an object.");const n=void 0===t.effort?1:t.effort;if(!Number.isInteger(n)||n<1||n>9)throw et("JXL_INPUT","Effort is a whole number from 1 to 9.");return n}(f);return function(t,n,o,r){if(function(t,n,o=tt){if(!Number.isInteger(t)||!Number.isInteger(n)||t<1||n<1)throw et("JXL_INPUT","A picture is at least one pixel wide and high, in whole pixels.");if(t>o.edge||n>o.edge)throw et("JXL_DIMENSIONS","A picture is at most "+o.edge+" pixels on a side.");if(t*n>o.pixels)throw et("JXL_DIMENSIONS","A picture is at most "+o.pixels.toLocaleString("en-US")+" pixels.")}(n,o,r),!(t instanceof Uint8Array||t instanceof Uint8ClampedArray))throw et("JXL_INPUT","Pixels are a Uint8Array or Uint8ClampedArray of RGBA bytes.");if(t.length!==n*o*4)throw et("JXL_INPUT","Pixels are width * height * 4 bytes: straight (not premultiplied) RGBA, row by row.")}(n,o,r,nt),function(t){const n=function*(){let o,r;for(;!(o=i(()=>t.next(n.hurry))).done;)yield r=o.value;return r<1&&(yield 1),n.bytes=(s((e=o.value).length),e);var e}();return n.bytes=null,n.hurry=!1,n}(function*(n,o,r,s,i,f,c){if(s>=100)return yield*function*(n,o,r,{shape:e=_(n,o,r),colorSpace:s}={}){let i,f;const c=e.palette?[e,{...e,palette:null}]:[e];for(let e=0;e<c.length;e++)try{const f=yield*t(N(n,o,r,c[e],s),e,c.length);(!i||f.length<i.length)&&(i=f)}catch(t){if("JXL_SIZE"!==t.code)throw t;f=t}if(!i)throw f;return i}(n,o,r,{colorSpace:i});if(f<5)return yield*t(c(yield*t(O(n,o,r,s,i),0,2),f),1,2);const l=yield*t(O(n,o,r,s,i),0,4);let a=yield*t(c(l,4),1,4);if(yield.5)return a;try{const o=yield*t(function*(t,n,o=0){const{width:r,height:s,components:i,quantScale:f}=n,c=i[0].stride,l=i[0].rows,a=c*l,u=At.length;if(t.byteLength+i.reduce((t,n)=>t+n.coeffs.byteLength,0)+Math.max(a*(10*u+2)+o,7*nt.bytes)>Ut)return null;const h=new Float64Array(a*u),y=new Uint16Array(a*u),w=C(t,r,s),d=i.map(t=>t.quant),M=i.map(t=>t.coeffs),A=At.map(t=>d.map(n=>Float64Array.from(n,n=>n*function(t){const n=e(t);let o=1+(1023&n)/1024,r=(n>>10)-15;for(;r<0;)o/=2,r++;for(;r>0;)o*=2,r--;return o}(4/(2040*f))*8*255/t))),p=new Int16Array(192),g=new Float64Array(3),U=new Int32Array(6),b=new Float64Array(6),m=new Float64Array(6);let I=0,x=0;const S=(t,n,o)=>{let r=0,e=0;const s=pt[o]*d[0][1]*d[0][1]/(f*f);for(let i=1;i<64;i++){if(!o){for(let r=0;r<3;r++){const s=M[r][64*n+i];g[r]=bt(s,r)*A[o][r][i]-t[64*r+i],s&&(e+=2+2*(32-Math.clz32(Math.abs(s))))}r+=mt(g[0],g[1],g[2]);continue}for(let n=0;n<3;n++){const r=t[64*n+i],e=A[o][n][i],s=Math.floor(r/e);for(let t=0;t<2;t++){const o=s+t;U[2*n+t]=o,m[2*n+t]=o?2+2*(32-Math.clz32(Math.abs(o))):0,b[2*n+t]=o<-32768||o>32767?1/0:bt(o,n)*e-r}}let f=0,c=1/0,l=0;for(let t=0;t<8;t++){const n=mt(b[1&t],b[2+(t>>1&1)],b[4+(t>>2)]),o=n+s*(m[1&t]+m[2+(t>>1&1)]+m[4+(t>>2)]);o<c&&(c=o,f=t,l=n)}if(c===1/0)return{error:1/0,rate:65535};r+=l;for(let t=0;t<3;t++){const n=U[2*t+(f>>t&1)];p[64*t+i]=n,n&&(e+=2+2*(32-Math.clz32(Math.abs(n))))}}return{error:r,rate:e}};for(let t=0;t<l;t++)for(let n=0;n<c;n++){const o=t*c+n,r=w(n,t);for(let t=0;t<u;t++){const n=S(r,o,t),e=o*u+t;h[e]=n.error,y[e]=n.rate,t||(I+=n.error,x+=n.rate)}if(((o+1)%1024==0||o+1===a)&&(yield(o+1)/(3*a)))return null}const E=new Uint8Array(a);let L=0;const J=function*(t,n){let o=0,r=0;for(let e=0;e<a;e++){const s=e*u;let i=0,f=h[s]+t*y[s];for(let n=1;n<u;n++){const o=h[s+n]+t*y[s+n];o<f&&(f=o,i=n)}if(o+=h[s+i],r+=y[s+i],n&&(E[e]=i),((e+1)%1024==0||e+1===a)&&(yield 1/3+(L+(e+1)/a)/147))return null}return L++,{error:o,rate:r}};let X=0,T=Math.max(1,I/Math.max(1,x));for(let t=0;t<24;t++){const t=yield*J(T,!1);if(!t)return null;if(t.error>I)break;T*=2}for(let t=0;t<24;t++){const t=(X+T)/2,n=yield*J(t,!1);if(!n)return null;n.error<=I?X=t:T=t}const _=yield*J(X,!0);if(!_||_.error>I||_.rate>=x||!E.some(t=>t))return null;const N=new Uint8Array(a);for(let t=0;t<l;t++)for(let n=0;n<c;n++){const o=t*c+n,r=E[o];if(N[o]=At[r],r){S(w(n,t),o,r);for(let t=0;t<3;t++)for(let n=1;n<64;n++)M[t][64*o+n]=p[64*t+n]}if(((o+1)%1024==0||o+1===a)&&(yield 2/3+(o+1)/(3*a)))return null}return{...n,quantFieldBase:4,quantFields:N,reconstructionError:{baseline:I,candidate:_.error}}}(n,l,a.byteLength),2,4);o&&(a=yield*t(c(o,4,a),3,4))}catch(t){if((!(t instanceof RangeError)||t.code)&&"JXL_SIZE"!==t.code)throw t}return a}(n,o,r,l,a,u,c))}function F(t,n){return g(t,n,Et),Et[0]}function q(t){let n=0;for(t>=2&&t<4294967296&&(n=31-Math.clz32(t),t*=Lt[n]);t>=1.4142135623730951;)t/=2,n++;for(;t<.7071067811865476;)t*=2,n--;const o=(t-1)/(t+1),r=o*o;let e=o,s=o;for(let t=3;t<30;t+=2)s+=(e*=r)/t;return n+2.885390081777927*s}function G(t){let n=0,o=0;for(const o of t)n+=o;for(const r of t)r&&(o+=r*q(n/r));return o}function j(t,{maxClusters:n=48,newClusterCost:o=320}={}){const r=[];for(let n=0;n<t.et;n++)t.st[n]>0&&r.push(n);r.sort((n,o)=>t.st[o]-t.st[n]);const e=[];for(const s of r){const r=t.it([s],xt),i=G(r);let f=-1,c=1/0;for(let t=0;t<e.length;t++){const n=e[t],o=new Uint32Array(64);for(let t=0;t<64;t++)o[t]=n.rt[t]+r[t];const s=G(o)-n.ft-i;s<c&&(c=s,f=t)}if(f<0||e.length<n&&c>o)e.push({et:[s],rt:r,ft:i});else{const t=e[f];t.et.push(s);for(let n=0;n<64;n++)t.rt[n]+=r[n];t.ft=G(t.rt)}}e.length||e.push({et:[],rt:new Uint32Array(64),ft:0});const s=new Uint8Array(t.et);let i=2*t.et;const f=e.map((n,o)=>{for(const t of n.et)s[t]=o;let r=It[0],e=1/0;for(const o of It){const s=t.ct(n.et,o);s<e&&(e=s,r=o)}const f=d(t.it(n.et,r,128));return i+=e+3*f.u+40,{config:r,code:f}});return{L:s,J:f,write:(t,n,o)=>{const r=f[s[n]];b(t,r.code,r.config,o)},ft:i}}function k(t,n="JXL_JPEG"){return Object.assign(new Error("JPEG: "+t),{code:n})}function R(t,n,o){const r=[];if(n>0){const o=new Uint32Array(2048);let e=0;for(const n of t)o[Math.min(2047,Math.max(0,n+1024))]++,e++;let s=0,i=e/(n+1);for(let t=0;t<2048&&r.length<n;t++)s+=o[t],s>i&&(r.push(t-1025),i=e*(r.length+1)/(n+1))}const e=r.length+1,s=new Uint8Array(39*e);for(let t=0;t<e;t++)for(let n=0;n<Xt;n++)s[(0+n)*e+t]=t,s[(13+n)*e+t]=o?e:e+(t>>1),s[(26+n)*e+t]=o?e:e+(e-1>>1)+1+(t>>1);let i=0;for(const t of s)i=Math.max(i,t+1);return{thresholds:r,lt:e,map:s,ut:i,ht:t=>{let n=0;for(const o of r)t>o&&n++;return n},yt:(t,n)=>s[(t<2?1^t:2)*Xt*e+n],wt:(t,n)=>(Math.min(t,64)<8?Math.min(t,64):4+(Math.min(t,64)>>1))*i+n,dt:t=>37*i+458*t,et:495*i}}function z(t,n,o){const r=n.map(()=>x(o)),e=r.map(()=>new Uint32Array(257));n.forEach((t,n)=>T(null,e[n],t.data,t.w,t.h,r[n])),J(t,{H:!1,K:[]});const s=E(t,S(r)),i=X(t,s,e,t=>r.indexOf(t));n.forEach((n,o)=>T(t,i[o+1].code,n.data,n.w,n.h,r[o]))}function*B(t,o={}){const{width:i,height:f,components:u,alpha:h}=t,y=1===u.length,w=t.quantScale||1;let M=t.quantFieldBase||1;for(;M/(2040*w)<1/16384;)M*=2;const p=t.ycbcr||y,g=y?[0,0,0]:p?[1,0,2]:[0,1,2],x=g.map(t=>u[t]),S=o.orders?g.map(t=>o.orders[t].map(t=>Nt[t])):[Nt,Nt,Nt],E=t=>1===t?0:2===t?1:-1,L=x.map(t=>E(t.h)),J=x.map(t=>E(t.v));if(L.includes(-1)||J.includes(-1))throw k("sampling factors other than one or two");const X=Math.max(...L),T=Math.max(...J),_=t=>X-L[t],N=t=>T-J[t];if(!p&&(X||T))throw k("an RGB JPEG with subsampling");const v=Math.ceil(i/(8<<X))<<X,C=Math.ceil(f/(8<<T))<<T,O=Math.ceil(i/st),P=Math.ceil(f/st),F=O*P,q=Math.ceil(v/256),G=q*Math.ceil(C/256),B=1===F,Z=(t,n,o)=>{const r=x[t];return y&&1!==t?-1:64*(o*r.stride+n)},D=t=>{let n=1024;for(;t%n;)n>>=1;return n},Y=x.map(t=>p?1:t.quant[0]/D(t.quant[0])),H=x.map(t=>p?0:1024/D(t.quant[0])),Q=new Int32Array(v*C);for(let t=0;t<C;t++)for(let n=0;n<v;n++)Q[t*v+n]=x[1].coeffs[Z(1,n,t)]*Y[1]+H[1];const K=x[1].quant,V=(K[8]+K[16]+K[24]+K[32]+K[40])/w,W=v*C,$=W<256?0:Math.max(1,Math.min(7,r(W)-r(V)-7));let tt=R(Q,$,y);const nt=new rt(256);if(c(nt,i,f,y?1:3,!!h,{orientation:t.orientation,colorSpace:t.colorSpace}),nt.write(1,0),nt.write(2,0),nt.write(1,0),nt.write(2,2),nt.write(8,111),nt.write(1,p?1:0),p)for(let t=0;t<3;t++)nt.write(2,0===L[t]&&0===J[t]?0:1===L[t]&&1===J[t]?1:1===L[t]?2:3);nt.write(2,0),h&&nt.write(2,0),l(nt,h);const ot=[0,1,2].map(()=>new Int32Array(1024)),et=(t,o)=>{const r=t%O*32,e=32*(t/O|0),s=Math.min(32,v-r),i=Math.min(32,C-e);for(let t=0;t<i;t++)for(let i=0;i<s;i++)for(const s of[1,0,2]){const f=_(s),c=N(s),l=i>>f,a=t>>c;if(l<<f!==i||a<<c!==t)continue;const u=Z(s,(r>>f)+l,(e>>c)+a),h=x[s].coeffs;let y=0;if(u>=0)for(let t=1;t<64;t++)h[u+t]&&y++;const w=ot[s],d=0===l?0===a?32:w[32*(a-1)]:0===a?w[32*a+l-1]:w[32*(a-1)+l]+w[32*a+l-1]+1>>1;w[32*a+l]=y;const M=tt.yt(s,tt.ht(Q[(e+t)*v+r+i]));o(tt.wt(d,M),y);const A=tt.dt(M);let p=y>4?0:1,g=y;for(let t=1;t<64&&g;t++){const r=h[u+S[s][t]];o(A+vt(g,t,p),n(r)),p=r?1:0,g-=p}}},it=($>0?3:2)*F+G;let ft=0;const ct=function*(t){tt=t;const n=new St(t.et);for(let t=0;t<F;t++)et(t,(t,o)=>n.add(t,o)),yield++ft/it;return(o.coding||j)(n,o.clusters)};let lt=yield*ct(tt);if($>0){const t=tt,n=R(Q,0,y),o=yield*ct(n);o.ft<=lt.ft?lt=o:tt=t}const at=Array.from({length:B?1:2+G+F},()=>null),ht=t=>at[B?0:t]||(at[B?0:t]=new rt(4096)),yt=new Uint32Array(at.length);let wt=0;const dt=t=>{t=B?0:t;const n=Math.ceil(at[t].ot/8);wt+=n-yt[t],yt[t]=n,s(wt)},Mt=ht(0);Mt.write(1,0);const At=(t,n)=>{const o=t*st,r=n*st,e=Math.min(st,i-o),s=Math.min(st,f-r),c=new Int32Array(e*s);for(let t=0;t<s;t++)for(let n=0;n<e;n++)c[t*e+n]=h[4*((r+t)*i+o+n)+3];return{w:e,h:s,data:c}};for(let t=0;t<3;t++)Mt.write(16,e(x[t].quant[0]/Y[t]/(2040*w)*128));Mt.write(2,3),Mt.write(16,57343),Mt.write(2,1),Mt.write(5,0),Mt.write(1,0),Mt.write(4,0),Mt.write(4,tt.thresholds.length);for(const t of tt.thresholds)Mt.t([[4,0],[8,16],[16,272],[32,65808]],n(t));Mt.write(4,0),Mt.write(4,0),m(Mt,tt.map),Mt.write(1,0),Mt.write(2,0),Mt.write(16,0),Mt.write(16,0),Mt.write(8,128),Mt.write(8,128),Mt.write(1,0),h&&z(Mt,[B?At(0,0):{w:0,h:0,data:new Int32Array(0)}],ut),dt(0);for(let n=0;n<G;n++){const o=ht(1+n),e=n%q*256,s=256*(n/q|0),i=Math.min(256,v-e),f=Math.min(256,C-s);o.write(2,0),z(o,[1,0,2].map(t=>{const n=i>>_(t),o=f>>N(t),r=new Int32Array(n*o),c=Y[t],l=H[t];for(let i=0;i<o;i++)for(let o=0;o<n;o++){const f=Z(t,(e>>_(t))+o,(s>>N(t))+i);r[i*n+o]=f<0?0:x[t].coeffs[f]*c+l}return{w:n,h:o,data:r}}),ut);const c=i*f,l=r(c);l&&o.write(l,c-1);const a=i+7>>3,u=f+7>>3,h=new Int32Array(2*c).fill(M-1,c);if(t.quantFields)for(let n=0;n<f;n++)for(let o=0;o<i;o++)h[c+n*i+o]=t.quantFields[(s+n)*v+e+o]-1;z(o,[{w:a,h:u,data:new Int32Array(a*u)},{w:a,h:u,data:new Int32Array(a*u)},{w:c,h:2,data:h},{w:i,h:f,data:new Int32Array(c)}],M>1?ut:0),dt(1+n),yield++ft/it}const pt=ht(1+G);pt.write(1,0),pt.write(3,7),pt.write(16,e(M/(2040*w))),z(pt,[0,1,2].map(t=>{const n=new Int32Array(64),o=x[t].quant;for(let t=0;t<8;t++)for(let r=0;r<8;r++)n[8*r+t]=o[8*t+r];return{w:8,h:8,data:n}}),ut);for(let t=1;t<17;t++)pt.write(3,0);if(F>1&&pt.write(r(F),0),o.orders){pt.t([[0,95],[0,19],[0,0],[13,0]],1);const t=[];for(const n of g){const r=Array.from({length:64},(t,n)=>n),e=o.orders[n].map(t=>{const n=r.indexOf(t);return r.splice(n,1),n});let s=64;for(;s>1&&!e[s-1];)s--;t.push(s-1,...e.slice(1,s))}const n=A(0),r=new Uint32Array(64);for(const o of t)U(n,o,r);const e=d(r);I(pt,{L:new Uint8Array(8),J:[{config:n,code:e}]});for(const o of t)b(pt,e,n,o)}else pt.write(2,2);lt.writeHistograms?lt.writeHistograms(pt):I(pt,{L:lt.L,J:lt.J}),dt(1+G);for(let t=0;t<F;t++){const n=ht(2+G+t);et(t,(t,o)=>lt.write(n,t,o)),lt.flush&&lt.flush(n),h&&!B&&z(n,[At(t%O,Math.floor(t/O))],ut),dt(2+G+t),yield++ft/it}return a(nt,function(t){return t.map(t=>t?t.finish():new Uint8Array(0))}(at))}function*Z(t,n,o){if(n<3&&!o)return yield*B(t);let r=o;try{const o=B(t);let e,s;for(;!(e=o.next()).done;)if(s=yield e.value/2,s&&r&&e.value<1)return o.return(),r;if((!r||e.value.length<r.length)&&(r=e.value),s||n<3)return r;const i=n<4?1:3,f=B(t,{clusters:{maxClusters:32,newClusterCost:160}});for(;!(e=f.next()).done;)if(s=yield.5+e.value/(2*i),s&&e.value<1)return f.return(),r;if(e.value.length<r.length&&(r=e.value),s||n<4)return r;const c=function*(t){const n=Jt.map(t=>(7&t)<<3|t>>3),o=t.map(()=>new Uint32Array(64)),r=t.reduce((t,n)=>t+n.coeffs.length,0);let e=0;for(let s=0;s<t.length;s++){const{coeffs:i}=t[s],f=o[s];for(let t=0;t<i.length;t+=65536){const o=Math.min(t+65536,i.length);for(let r=t;r<o;r+=64)for(let t=1;t<64;t++)i[r+n[t]]&&f[t]++;if(e+=o-t,yield e/r)return null}}return o.map(t=>[0,...Array.from({length:63},(t,n)=>n+1).sort((n,o)=>Math.floor(t[o]/8)-Math.floor(t[n]/8)||n-o)])}(t.components);for(;!(e=c.next()).done;)if(yield.5+(1+e.value)/6)return c.return(),r;const l=B(t,{orders:e.value});for(;!(e=l.next()).done;)if(s=yield.5+(2+e.value)/6,s&&e.value<1)return l.return(),r;return e.value.length<r.length?e.value:r}catch(t){if(!r||(!(t instanceof RangeError)||t.code)&&"JXL_SIZE"!==t.code)throw t;return r}}function D(t,n){if(!n)return void t.write(1,0);const r=o(n);t.write(1,1),t.write(3,r),t.write(r,n-(1<<r))}function Y(t){let n=0,o=0;for(let r=0;r<t.length;r++)n+=t[r],t[r]&&(o=r+1);const r=new Uint16Array(o||1);if(!n)return r[0]=4096,r;let e=0;for(let s=0;s<o;s++)t[s]&&(e+=r[s]=Math.max(1,Math.round(4096*t[s]/n)));for(;4096!==e;){const s=e<4096?1:-1;let i=-1,f=-1/0;for(let e=0;e<o;e++)if(t[e]&&(s>0||r[e]>1)){const o=s*(4096*t[e]-r[e]*n);o>f&&(i=e,f=o)}r[i]+=s,e+=s}return r}function H(t,n){const r=[];let e=0;for(let t=0;t<n.length;t++)n[t]&&r.push(t),n[t]>n[e]&&(e=t);if(r.length<=2){t.write(1,1),t.write(1,r.length-1);for(const n of r)D(t,n);return void(2===r.length&&t.write(12,n[r[0]]))}t.write(1,0),t.write(1,0),t.write(3,7),t.write(3,5),D(t,n.length-3);const s=new Uint8Array(n.length);let i=0;for(let t=0;t<n.length;t++)t!==e&&n[t]&&(s[t]=o(n[t])+1,i=Math.max(i,s[t]+(t<e?1:0)));s[e]=i;for(const n of s)t.write(Ct[n],Ot[n]);for(let o=0;o<n.length;o++)if(o!==e&&s[o]>1){const r=s[o]-1;t.write(r,n[o]-(1<<r))}}function Q(t,n){const o=1<<n,r=4096>>n,e=new Uint16Array(t.length+1),s=new Uint16Array(4096);let i=-1;for(let n=0;n<t.length;n++)e[n+1]=e[n]+t[n],4096===t[n]&&(i=n);if(i>=0){for(let t=0;t<4096;t++)s[t]=t;return{starts:e,reverse:s}}const f=new Int32Array(o),c=new Uint16Array(o),l=new Int32Array(o),a=[],u=[];f.set(t);for(let t=0;t<o;t++)f[t]<r?a.push(t):f[t]>r&&u.push(t);for(;u.length;){const t=u.pop(),n=a.pop(),o=r-f[n];f[t]-=o,c[n]=t,l[n]=f[t]-f[n],f[t]<r?a.push(t):f[t]>r&&u.push(t)}for(let t=0;t<o;t++)for(let n=0;n<r;n++){const o=n<f[t],i=o?t:c[t],a=o?n:n+l[t];s[e[i]+a]=t*r+n}return{starts:e,reverse:s}}function K(t,n){const o=j(t,n),e=o.J.map(()=>[]);for(let n=0;n<t.et;n++)t.st[n]&&e[o.L[n]].push(n);const s=o.J.map(({config:n},o)=>({config:n,counts:Y(t.it(e[o],n,256))})),i=Math.max(5,r(Math.max(...s.map(t=>t.counts.length))));for(const t of s)Object.assign(t,Q(t.counts,i));const f=new rt;f.write(1,0),o.L.length>1&&m(f,o.L),f.write(1,0),f.write(2,i-5);for(const t of s)p(f,t.config,i);for(const t of s)H(f,t.counts);const c=new Uint32Array(Pt),l=new Uint8Array(Pt),a=new Uint16Array(Pt),u=[0,0,0];let h=0;return{ft:o.ft,write:(t,n,r)=>{c[h]=r,l[h++]=o.L[n]},flush:t=>{let n=1245184;for(let t=h-1;t>=0;t--){const o=s[l[t]];g(o.config,c[t],u);const r=u[0],e=o.counts[r];c[t]=u[2],l[t]=u[1],n>>>20>=e&&(a[t]=65535&n,l[t]|=64,n>>>=16);const i=Math.floor(n/e);n=4096*i+o.reverse[o.starts[r]+n-i*e]}t.write(32,n);for(let n=0;n<h;n++){64&l[n]&&t.write(16,a[n]);const o=63&l[n];o&&t.write(o,c[n])}h=0},writeHistograms:t=>t.append(f)}}function*V(n,o,r){if(o<2)return yield*Z(n,o,r);let e=yield*t(Z(n,o,r),0,2);if(yield.5)return e;try{const t=B(n,{coding:K});let o;for(;!(o=t.next()).done;)if((yield.5+o.value/2)&&o.value<1)return t.return(),e;o.value.length<e.length&&(e=o.value)}catch(t){if((!(t instanceof RangeError)||t.code)&&"JXL_SIZE"!==t.code)throw t}return e}function W(t,n,o,r){return function(t){let n;for(;!(n=t.next()).done;);return n.value}($(t,n,o,r))}function $(t,n,o,r){return P(t,n,o,r,V)}const tt=Object.freeze({bytes:16777216,pixels:24e6,edge:16384}),nt=Object.freeze({bytes:16777216,pixels:4e7,edge:16384}),ot=[1];for(let t=1;t<=32;t++)ot.push(2*ot[t-1]);class rt{constructor(t=4096){this.bytes=new Uint8Array(t),this.at=0,this.Mt=0,this.pending=0}write(t,n){if(!(n>=0&&n<ot[t]))throw new Error("bit write out of range: "+t+" bits, "+n);let o=this.Mt+n*(1<<this.pending),r=this.pending+t;this.at+5>=this.bytes.length&&this.grow();const e=this.bytes;for(;r>=8;)e[this.at++]=255&o,o=Math.floor(o/256),r-=8;this.Mt=o,this.pending=r}t(t,n){for(let o=0;o<4;o++){const[r,e]=t[o];if(n>=e&&n-e<ot[r])return this.write(2,o),void(r&&this.write(r,n-e))}throw new Error("U32 value out of range: "+n)}o(){this.pending&&(this.at+1>=this.bytes.length&&this.grow(),this.bytes[this.at++]=this.Mt,this.Mt=0,this.pending=0)}get ot(){return 8*this.at+this.pending}grow(t=0){const n=tt.bytes+64;if(this.at+t+16>n)throw Object.assign(new Error("The encoded picture exceeds 16 MiB."),{code:"JXL_SIZE"});const o=new Uint8Array(Math.min(n,Math.max(2*this.bytes.length,this.at+t+16)));o.set(this.bytes.subarray(0,this.at)),this.bytes=o}append(t){if(this.at+t.at+8>=this.bytes.length&&this.grow(t.at+8),this.pending)for(let n=0;n<t.at;n++)this.write(8,t.bytes[n]);else this.bytes.set(t.bytes.subarray(0,t.at),this.at),this.at+=t.at;t.pending&&this.write(t.pending,t.Mt)}finish(){const t=this.at+(this.pending?1:0),n=this.bytes.slice(0,t);return this.pending&&(n[this.at]=this.Mt),n}}const et=(t,n)=>Object.assign(new Error(n),{code:t}),st=256,it=[1,2,3,4,0,5,17,6,16,7,8,9,10,11,12,13,14,15],ft=[2,4,3,2,2,4],ct=[0,7,3,2,1,15],lt=[0,0,0],at=3,ut=5,ht=A(4),yt=Object.freeze({T:224,minLength:7,_:ht}),wt=A(0),dt=Float64Array.of(.3535533905932738,.3535533905932738,.3535533905932738,.3535533905932738,.3535533905932738,.3535533905932738,.3535533905932738,.3535533905932738,.4903926402016152,.4157348061512726,.27778511650980114,.09754516100806417,-.0975451610080641,-.277785116509801,-.4157348061512727,-.4903926402016152,.46193976625564337,.19134171618254492,-.19134171618254486,-.46193976625564337,-.4619397662556434,-.19134171618254517,.191341716182545,.46193976625564326,.4157348061512726,-.0975451610080641,-.4903926402016152,-.2777851165098011,.2777851165098009,.4903926402016152,.09754516100806439,-.41573480615127256,.3535533905932738,-.35355339059327373,-.35355339059327384,.3535533905932737,.35355339059327384,-.35355339059327334,-.35355339059327356,.3535533905932733,.27778511650980114,-.4903926402016152,.09754516100806415,.4157348061512728,-.41573480615127256,-.09754516100806401,.4903926402016153,-.27778511650980076,.19134171618254492,-.4619397662556434,.46193976625564326,-.19134171618254495,-.19134171618254528,.46193976625564337,-.4619397662556432,.19134171618254478,.09754516100806417,-.2777851165098011,.4157348061512728,-.4903926402016153,.4903926402016152,-.4157348061512725,.27778511650980076,-.09754516100806429),Mt=Float64Array.from({length:64},(t,n)=>{const o=.26236426446749106*Math.max(0,5*Math.sqrt((7&n)*(7&n)+(n>>3)*(n>>3))/Math.sqrt(98)-1);let r=1,e=1;for(let t=1;t<30;t++)e+=r=r*o/t;return e}),At=[4,4,4,4,3,2,6,8],pt=[0,.125,.25,.5,.25,.25,.25,.25],gt=[.9299455010825141,.945349926692846,.9500648966626563],Ut=10.5*nt.pixels,bt=(t,n)=>Math.abs(t)<2?t*gt[n]:t-.145/t,mt=(t,n,o)=>{const r=t+1.402*o,e=t-.344136286*n-.714136286*o,s=t+1.772*n;return r*r+e*e+s*s},It=[A(0),A(1),A(2),A(3),A(4),A(4,1,1),A(5,1,1),A(2,0,1),A(3,0,1)],xt=A(4,1,1);class St{constructor(t){this.et=t,this.small=new Uint32Array(64*t),this.At=Array.from({length:t},()=>null),this.st=new Float64Array(t)}add(t,n){if(this.st[t]++,n<64)return void this.small[64*t+n]++;const o=this.At[t]||(this.At[t]=new Map);o.set(n,(o.get(n)||0)+1)}it(t,n,o=64){const r=new Uint32Array(o);for(const o of t){const t=64*o;for(let o=0;o<64;o++)this.small[t+o]&&(r[F(n,o)]+=this.small[t+o]);const e=this.At[o];if(e)for(const[t,o]of e)r[F(n,t)]+=o}return r}ct(t,n){let r=G(this.it(t,n));for(const e of t){const t=64*e;for(let e=n.S;e<64;e++)this.small[t+e]&&(r+=this.small[t+e]*(o(e)-n.m-n.I));const s=this.At[e];if(s)for(const[t,e]of s)r+=e*(o(t)-n.m-n.I)}return r}}const Et=[0,0,0],Lt=[1];for(let t=1;t<32;t++)Lt.push(Lt[t-1]/2);const Jt=[0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63],Xt=13,Tt=[0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15,16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,23,23,24,24,24,24,25,25,25,25,26,26,26,26,27,27,27,27,28,28,28,28,29,29,29,29,30,30,30,30],_t=[0,0,31,62,62,93,93,93,93,123,123,123,123,152,152,152,152,152,152,152,152,180,180,180,180,180,180,180,180,180,180,180,180,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206,206],Nt=Jt.map(t=>(7&t)<<3|t>>3),vt=(t,n,o)=>2*(_t[t]+Tt[n])+o,Ct=[5,4,4,4,4,4,3,3,3,3,3,6,7],Ot=[17,11,15,3,9,7,4,2,5,6,0,33,1],Pt=196608;export{nt as LIMITS,W as encodePhoto,$ as encodePhotoSteps};
package/photo-ans.mjs ADDED
@@ -0,0 +1,9 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Photographic pixels through one checked job owner; quality 100 remains exact.
3
+ import {PHOTO_LIMITS, complete} from './bits.mjs';
4
+ import {photoJob} from './photo-job.mjs';
5
+ import {coefficientAnsSteps} from './coefficient-ans.mjs';
6
+
7
+ export {PHOTO_LIMITS as LIMITS};
8
+ export function encodePhoto(data, width, height, options) { return complete(encodePhotoSteps(data, width, height, options)); }
9
+ export function encodePhotoSteps(data, width, height, options) { return photoJob(data, width, height, options, coefficientAnsSteps); }
package/photo-dct.mjs ADDED
@@ -0,0 +1,83 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Neither table comes from Math.cos, Math.hypot or `**`, which each engine rounds its own way; the same pixels must
3
+ // give the same bytes everywhere. The DCT8 basis, (u ? 1 : √½) · cos((2x + 1) · u · π / 16) / 2 at u · 8 + x, is
4
+ // written out as V8 computed it (the cosines of the larger angles carry their rounding, and a coefficient on a
5
+ // rounding edge would move without it). The quantisation shape, 1.3 ** max(0, hypot(u, v) · 5 / √98 − 1) at
6
+ // v · 8 + u, is the exponential series of its logarithm: libjxl 0.7's DCT8 luma distance bands
7
+ // (lib/jxl/quant_weights.cc), the reciprocal weight growing with radial frequency. Both chroma planes keep full
8
+ // resolution; coarser chroma AC steps reserve bits for luma, while finer DC steps preserve smooth gradients.
9
+ const COSINES = Float64Array.of(0.3535533905932738, 0.3535533905932738, 0.3535533905932738, 0.3535533905932738,
10
+ 0.3535533905932738, 0.3535533905932738, 0.3535533905932738, 0.3535533905932738, 0.4903926402016152,
11
+ 0.4157348061512726, 0.27778511650980114, 0.09754516100806417, -0.0975451610080641, -0.277785116509801,
12
+ -0.4157348061512727, -0.4903926402016152, 0.46193976625564337, 0.19134171618254492, -0.19134171618254486,
13
+ -0.46193976625564337, -0.4619397662556434, -0.19134171618254517, 0.191341716182545, 0.46193976625564326,
14
+ 0.4157348061512726, -0.0975451610080641, -0.4903926402016152, -0.2777851165098011, 0.2777851165098009,
15
+ 0.4903926402016152, 0.09754516100806439, -0.41573480615127256, 0.3535533905932738, -0.35355339059327373,
16
+ -0.35355339059327384, 0.3535533905932737, 0.35355339059327384, -0.35355339059327334, -0.35355339059327356,
17
+ 0.3535533905932733, 0.27778511650980114, -0.4903926402016152, 0.09754516100806415, 0.4157348061512728,
18
+ -0.41573480615127256, -0.09754516100806401, 0.4903926402016153, -0.27778511650980076, 0.19134171618254492,
19
+ -0.4619397662556434, 0.46193976625564326, -0.19134171618254495, -0.19134171618254528, 0.46193976625564337,
20
+ -0.4619397662556432, 0.19134171618254478, 0.09754516100806417, -0.2777851165098011, 0.4157348061512728,
21
+ -0.4903926402016153, 0.4903926402016152, -0.4157348061512725, 0.27778511650980076, -0.09754516100806429);
22
+ export {COSINES as dct8Basis};
23
+ export const QUANT_SHAPE = Float64Array.from({length: 64}, (_, k) => {
24
+ const y = Math.max(0, Math.sqrt((k & 7) * (k & 7) + (k >> 3) * (k >> 3)) * 5 / Math.sqrt(98) - 1) * 0.26236426446749106;
25
+ let term = 1, sum = 1;
26
+ for (let n = 1; n < 30; n++) sum += term = term * y / n;
27
+ return sum;
28
+ });
29
+
30
+ // Eight terms in the original accumulation order, including the initial zero. The two separable passes
31
+ // share this fixed dot product without a loop or a changed floating-point sum.
32
+ function dot8(data, at, step, basis) {
33
+ let sum = 0;
34
+ sum += data[at] * COSINES[basis];
35
+ sum += data[at + step] * COSINES[basis + 1];
36
+ sum += data[at + 2 * step] * COSINES[basis + 2];
37
+ sum += data[at + 3 * step] * COSINES[basis + 3];
38
+ sum += data[at + 4 * step] * COSINES[basis + 4];
39
+ sum += data[at + 5 * step] * COSINES[basis + 5];
40
+ sum += data[at + 6 * step] * COSINES[basis + 6];
41
+ sum += data[at + 7 * step] * COSINES[basis + 7];
42
+ return sum;
43
+ }
44
+
45
+ // Reused scratch for the baseline and the photographic quantisation search. A transform always visits its eight
46
+ // terms in the same order; edge replication agrees with the baseline's complete 8 by 8 blocks.
47
+ export function dctBlocks(data, width, height) {
48
+ const block = new Float64Array(192), intermediate = new Float64Array(64), sums = new Float64Array(192);
49
+ return (bx, by) => {
50
+ for (let y = 0; y < 8; y++) for (let x = 0; x < 8; x++) {
51
+ const at = (Math.min(height - 1, by * 8 + y) * width + Math.min(width - 1, bx * 8 + x)) * 4;
52
+ const r = data[at], g = data[at + 1], b = data[at + 2], k = y * 8 + x;
53
+ block[k] = 0.299 * r + 0.587 * g + 0.114 * b - 128;
54
+ block[64 + k] = -0.168735892 * r - 0.331264108 * g + 0.5 * b;
55
+ block[128 + k] = 0.5 * r - 0.418687589 * g - 0.081312411 * b;
56
+ }
57
+ for (let c = 0; c < 3; c++) {
58
+ for (let y = 0; y < 8; y++) for (let u = 0; u < 8; u++) intermediate[y * 8 + u] = dot8(block, c * 64 + y * 8, 1, u * 8);
59
+ for (let v = 0; v < 8; v++) for (let u = 0; u < 8; u++) sums[c * 64 + v * 8 + u] = dot8(intermediate, u, 8, v * 8);
60
+ }
61
+ return sums;
62
+ };
63
+ }
64
+
65
+ // A row of groups (32 block rows) per step.
66
+ export function* photoCoefficientSteps(data, width, height, quality, colorSpace) {
67
+ const stride = Math.ceil(width / 8), rows = Math.ceil(height / 8), quantScale = 16;
68
+ const distance = quality >= 30 ? 0.1 + (100 - quality) * 0.09 : 53 / 3000 * quality * quality - 23 / 20 * quality + 25;
69
+ const components = [0, 1, 2].map(c => ({h: 1, v: 1, stride, rows,
70
+ quant: Int32Array.from(QUANT_SHAPE, (shape, k) => Math.max(1, Math.round(quantScale * distance * (c ? 18 : 10) * (k ? shape : 0.25)))),
71
+ coeffs: new Int16Array(stride * rows * 64)}));
72
+ const transform = dctBlocks(data, width, height);
73
+ const band = (from, to) => {
74
+ for (let by = from; by < to; by++) for (let bx = 0; bx < stride; bx++) {
75
+ const sums = transform(bx, by), offset = (by * stride + bx) * 64;
76
+ for (let c = 0; c < 3; c++) for (let k = 0; k < 64; k++) components[c].coeffs[offset + k] = Math.round(sums[c * 64 + k] * quantScale / components[c].quant[k]);
77
+ }
78
+ };
79
+ for (let by = 0; by < rows; by += 32) { band(by, Math.min(rows, by + 32)); yield Math.min(rows, by + 32) / rows; }
80
+ let alpha = null;
81
+ for (let i = 3; i < data.length; i += 4) if (data[i] !== 255) { alpha = data; break; }
82
+ return {width, height, components, quantScale, ycbcr: true, orientation: 1, alpha, colorSpace};
83
+ }
package/photo-job.mjs ADDED
@@ -0,0 +1,27 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // One checked photograph job shared by the prefix and optional ANS doors.
3
+ import {PHOTO_LIMITS, part} from './bits.mjs';
4
+ import {admitEffort} from './effort-level.mjs';
5
+ import {losslessSteps} from './lossless.mjs';
6
+ import {admitOptions, admitPixels, job} from './admit.mjs';
7
+ import {photoCoefficientSteps} from './photo-dct.mjs';
8
+ import {quantisationSteps} from './photo-quant.mjs';
9
+
10
+ export function photoJob(data, width, height, options, coefficients) {
11
+ const {quality, colorSpace} = admitOptions(options, 90), effort = admitEffort(options);
12
+ admitPixels(data, width, height, PHOTO_LIMITS);
13
+ return job(photoSteps(data, width, height, quality, colorSpace, effort, coefficients));
14
+ }
15
+
16
+ function* photoSteps(data, width, height, quality, colorSpace, effort, encodeCoefficients) {
17
+ if (quality >= 100) return yield* losslessSteps(data, width, height, {colorSpace});
18
+ if (effort < 5) return yield* part(encodeCoefficients(yield* part(photoCoefficientSteps(data, width, height, quality, colorSpace), 0, 2), effort), 1, 2);
19
+ const coefficients = yield* part(photoCoefficientSteps(data, width, height, quality, colorSpace), 0, 4);
20
+ let best = yield* part(encodeCoefficients(coefficients, 4), 1, 4);
21
+ if (yield 0.5) return best;
22
+ try {
23
+ const chosen = yield* part(quantisationSteps(data, coefficients, best.byteLength), 2, 4);
24
+ if (chosen) best = yield* part(encodeCoefficients(chosen, 4, best), 3, 4);
25
+ } catch (error) { if (!(error instanceof RangeError && !error.code) && error.code !== 'JXL_SIZE') throw error; }
26
+ return best;
27
+ }