rapier-jxl 1.1.16 → 1.1.19

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/AGENTS.md CHANGED
@@ -1,16 +1,25 @@
1
1
  # For an agent adding Rapier JXL to an app
2
2
 
3
- 1. Install: `npm install rapier-jxl` (or copy the eleven `.mjs` files at this repository's root into the app; no build needed).
4
- 2. Import `encode` and `transcode` from `rapier-jxl` (or from `index.mjs`).
3
+ 1. Install: `npm install rapier-jxl`, or copy one file from this repository's root into the app: `rapier-jxl.min.mjs`
4
+ (the core), `lossless.min.mjs`, `jpeg.min.mjs` or `photo.min.mjs` (one checked function each), or the readable
5
+ `.mjs` files with `index.mjs` as the core entry and `photo.mjs` as the optional photo entry. No build needed.
6
+ 2. Import `encode` and `transcode` from `rapier-jxl` (the readable modules), `rapier-jxl/min` (the one minified
7
+ file), or `encodeLosslessRGBA` from `rapier-jxl/lossless` and `transcode` from `rapier-jxl/jpeg`.
5
8
  3. Encode pixels: `encode(rgba, width, height, {quality})`, `rgba` being straight (not premultiplied) RGBA bytes
6
9
  row by row, as `CanvasRenderingContext2D.getImageData` gives them; `quality` 100 (the default) is lossless,
7
- 1 to 99 lossy. The answer is a `Uint8Array` of JPEG XL codestream bytes: save it as `.jxl` or wrap it in a
8
- `Blob` of type `image/jxl`.
10
+ 1 to 99 lossy. Alpha is exact at every quality. For photographic VarDCT, import `encodePhotoRGBA` from
11
+ `rapier-jxl/photo` and call `encodePhotoRGBA(rgba, width, height, {quality: 90})`. It defaults to 90; 100 is
12
+ exact, including RGB beneath transparent pixels. Quality numbers across encoders are not equivalent PSNR.
13
+ Each answer is a `Uint8Array` of JPEG XL codestream bytes: save it as `.jxl` or wrap it in a `Blob` of type `image/jxl`.
9
14
  4. Carry a JPEG: `transcode(jpegBytes)` returns `{bytes, width, height, orientation}`; do not decode the JPEG
10
- first. If it throws `JXL_JPEG`, decode it (an `<img>` and a canvas) and call `encode` on the pixels.
11
- 5. Run it in a worker for anything larger than an icon; the README shows a complete one in eight lines.
15
+ first. If it throws `JXL_JPEG` (a form the carrier does not take, a colour profile other than sRGB, a file cut
16
+ short), decode it (an `<img>` and a canvas) and call `encode` on the pixels.
17
+ 5. Run it in a worker for anything larger than an icon. Calls are synchronous: cancel by terminating that worker,
18
+ disregard obsolete request IDs and start a replacement worker. A transferred input buffer is no longer owned
19
+ by the sender; keep a copy first if cancellation must retain the original pixels.
12
20
  6. Handle the five error codes (`JXL_INPUT`, `JXL_DIMENSIONS`, `JXL_SIZE`, `JXL_MEMORY`, `JXL_JPEG`); there are no
13
- others, and a call that returns has returned a stream every JPEG XL decoder reads.
21
+ others from the checked entries. The committed conformance cases are decoded by jxl-oxide and native libjxl;
22
+ this evidence does not prove every possible input. Keep original pixels until encoding and storage succeed.
14
23
  7. Show the result only where the browser decodes JPEG XL (`image/jxl` in `<picture>` with a fallback, or a
15
24
  feature test on a one-pixel stream); keep the original where it does not.
16
25
 
@@ -0,0 +1,68 @@
1
+ # Encoder payloads measured on 30 September 2026
2
+
3
+ Rapier JXL is the smallest JavaScript/WebAssembly JPEG XL encoder **among the published payloads measured here**.
4
+ This is a bounded survey, not proof of the smallest possible implementation. The alternatives implement different
5
+ subsets of JPEG XL. In particular, Rapier's JPEG carrier preserves coefficients but cannot reconstruct the original
6
+ JPEG file; libjxl's reversible JPEG mode can. Larger codecs also offer features Rapier does not, including HDR,
7
+ ICC profiles, animation, perceptual search and more compression tools.
8
+
9
+ | Encoder / entry | Version | Minified JS + WASM bytes | gzip bytes | Included capability |
10
+ | --- | --- | ---: | ---: | --- |
11
+ | Rapier JXL core | this release | 40,286 | 15,904 | 8-bit lossless RGBA, lossy modular with exact alpha, JPEG coefficients |
12
+ | Rapier JXL lossless | this release | 16,085 | 6,747 | 8-bit exact RGBA |
13
+ | Rapier JXL JPEG | this release | 30,624 | 12,274 | JPEG coefficients, orientation; no JPEG reconstruction |
14
+ | Rapier JXL photo | this release | 25,418 | 10,409 | 8-bit photographic VarDCT, exact alpha; q100 lossless |
15
+ | [jSquash](https://github.com/jamsinclair/jSquash/tree/main/packages/jxl) | 1.3.0 | 1,388,572 | 525,782 | 8-bit lossless/lossy RGBA |
16
+ | [Discourse's jSquash package](https://www.npmjs.com/package/@discourse/jxl) | 1.3.0 | 1,388,572 | 525,782 | Same measured encoder bytes as jSquash |
17
+ | [Lacinak's jSquash fork](https://github.com/kelaci/jSquash) | 1.3.0-kelaci.0 | 2,071,514 | 844,866 | Additional high bit-depth input options |
18
+ | [squoosh-kit](https://github.com/bnowak008/squoosh-kit/tree/main/packages/jxl) | 0.2.10 | 1,371,789 | 511,927 | 8-bit lossless/lossy RGBA |
19
+ | [icodec](https://github.com/Kaciras/icodec) | 0.6.0 | 2,474,954 | 900,957 | 8–16-bit lossless/lossy pixels |
20
+ | [Cornerstone libjxl](https://github.com/cornerstonejs/codecs/tree/main/packages/libjxl) | 1.1.1 | 2,575,008 | 936,242 | 1–16-bit grayscale/RGB; DICOM-oriented API |
21
+ | [jxl-wasm](https://github.com/saschanaz/jxl-wasm) | 0.7.0 | 2,533,758 | 961,538 | libjxl CLI, pixels and reversible JPEG |
22
+ | [jpeg-to-jxl](https://github.com/ChefJulio/jpeg-to-jxl) | 0.2.0 | 2,732,858 | 1,063,406 | Reversible JPEG recompression; includes decode/JPEG fallback |
23
+ | [Squoosh library](https://github.com/GoogleChromeLabs/squoosh/tree/dev/libsquoosh) | 0.5.3 | 1,615,596 | 539,210 | Shared multi-codec JavaScript and one JXL encoder WASM |
24
+
25
+ Bytes are decimal, not KiB. Every listed file, upstream URL, npm archive integrity and SHA-256 is in
26
+ [`public/encoder-sizes.json`](public/encoder-sizes.json). JavaScript is minified with Terser 5.51.2
27
+ (`compress.passes=2`, mangling, license comments retained); shipped WASM is unchanged. Each resource is gzipped
28
+ independently at level 9, then sizes are summed. These are delivery bytes, not npm tarball sizes. Rapier's
29
+ `sizes.json` records its independently optimized single-file builds and complete MIT banner.
30
+
31
+ The jSquash, fork, squoosh-kit and icodec rows count the low-level encoder factory and its WASM. Optional
32
+ convenience wrappers, threaded variants, workers and decoders are excluded. This favors those alternatives;
33
+ it does not pretend the entire npm package is needed to encode one picture. The Squoosh library has shared
34
+ JavaScript, so that file is included whole. The JPEG-to-JXL package also exposes reverse conversion from the
35
+ same WASM; it is not an encoder-only custom rebuild. Size measurements do not establish the other packages'
36
+ correctness or compression quality.
37
+
38
+ ## Reproduce
39
+
40
+ In a checkout of this public repository:
41
+
42
+ ```sh
43
+ npm install --ignore-scripts
44
+ node public/measure-encoders.mjs .encoder-size-cache encoder-sizes.json
45
+ ```
46
+
47
+ The script fetches only pinned npm archives from `public/encoders.json`, validates their integrity, reads the
48
+ listed members in memory and minifies their JavaScript. It never installs or executes downloaded competitor
49
+ code. The cache makes repeats offline. Rapier's own byte receipt is generated by its source stager, not by
50
+ this survey. For a standalone copy of the script, install `terser@5.51.2` first.
51
+
52
+ ## Found but not ranked
53
+
54
+ | Project | What was found | Why it is outside the numeric encoder ranking |
55
+ | --- | --- | --- |
56
+ | [jxl-oxide-wasm](https://github.com/tirr-c/jxl-oxide-wasm) 0.12.6 | 1,707,757 minified JS + WASM bytes; 612,548 gzip | Decoder only; cannot encode pixels or JPEGs |
57
+ | [jxl.js](https://github.com/niutech/jxl.js), jxl-rs-polyfill, TurboJXL, PureJsImage's JXL codec | Browser decoders | Decoding support is not encoding support |
58
+ | [libjxl](https://github.com/libjxl/libjxl/blob/main/doc/building_wasm.md) | WASM build instructions; official browser demo is a decoder | Encoder builds are represented by the measured libjxl-based packages; no single canonical encoder payload is published by this demo |
59
+ | [jixel](https://github.com/awxkee/jixel), revision `d84b45ff22d23895c76f58875358676d99e3081b` | Rust encoder, WASM-specific source; releases 0.3.2 and earlier have no downloadable WASM assets | No pinned public executable WASM package found; source bytes are not executable bytes |
60
+ | [libjxl-tiny](https://github.com/libjxl/libjxl-tiny), revision `8eae18172059d54f5c734ca814f23b96eebff859` | Small C++ encoder and WASM build instructions | No published WASM payload found; an unmeasured custom build cannot support a size ranking |
61
+ | [Hydrium](https://github.com/Traneptora/hydrium), revision `45227f35a222fadfd094a37325f937a6e9442c48` | Streaming C encoder; releases offer native executables | No JavaScript/WASM payload found |
62
+ | [Imazen jxl-encoder](https://github.com/imazen/jxl-encoder), revision `0d79a23e30da9504cd5f1e1b8a849cb930feb5fe` | Rust encoder and WASM benchmark example | No published WASM payload found |
63
+ | webcvt's jsquash-jxl adapter | Wrapper around `@jsquash/jxl` | Same encoder already measured; wrapper is additional application code |
64
+ | sharp/libvips packages and `cjxl` npm wrapper | Native binaries | Not JavaScript or WebAssembly encoder payloads |
65
+
66
+ Search covered npm's `jpeg-xl`, `jpegxl`, `jxl` and `jixel` results, upstream repositories and web searches for
67
+ JavaScript/WASM encoders. A package not found or not measured is not evidence for a universal record. If a
68
+ smaller usable encoder is published, it belongs in this table and the claim must change.
package/README.md CHANGED
@@ -2,16 +2,21 @@
2
2
 
3
3
  A JPEG XL encoder in pure JavaScript. No WebAssembly, no build step, no dependency, nothing fetched at run
4
4
  time. It is the encoder inside [Rapier](https://rapier.website), the single-file Markdown editor, offered on its
5
- own so any app can write JPEG XL pictures with the bytes it can afford: 24.7 KB gzipped
6
- (83.7 KB of readable source), MIT.
5
+ own so any app can write JPEG XL pictures with the bytes it can afford: one file of 40.3 kB,
6
+ 15.9 kB gzipped, MIT. The smallest JavaScript/WebAssembly JPEG XL encoder among the published
7
+ payloads [we measured](ENCODER-COMPARISON.md); the table explains the scope and how to reproduce it.
7
8
 
8
9
  - **Lossless.** Every pixel comes back as it went in. 8-bit grey, grey with alpha, RGB and RGBA.
9
10
  - **Lossy.** Quality 1 to 99, on libjxl's modular path (the Squeeze transform), for drawings, screenshots and
10
- pictures of few colours; a picture of few colours is answered exact when that is fewer bytes.
11
- - **A JPEG carried whole.** A JPEG's coefficients go into a JPEG XL frame untouched, the way libjxl transcodes one,
12
- so the picture decodes to the JPEG's own pixels at about a fifth fewer bytes, in one call, without decoding.
11
+ pictures of few colours; alpha stays exact. A picture of few colours is answered exact when that is fewer bytes.
12
+ - **Photographs, optionally.** Import `rapier-jxl/photo` for DCT8 photographic compression with exact alpha.
13
+ This separate module shares the JPEG carrier's VarDCT writer and adds no bytes to the core import.
14
+ - **A JPEG carried as its coefficients.** A JPEG's quantised DCT coefficients, quantisation tables, subsampling
15
+ and Exif orientation go into a JPEG XL frame the way libjxl transcodes them, so the picture decodes to the
16
+ JPEG's own pixels at about a fifth fewer bytes, in one call, without decoding. Not carried: the JPEG
17
+ reconstruction data (the JPEG file cannot be rebuilt from the stream), ICC, Exif beyond the orientation, XMP.
13
18
 
14
- Every browser that reads JPEG XL reads these streams; so do libjxl, jxl-oxide and everything built on them.
19
+ The retained corpus and seeded fuzz cases are decoded through jxl-oxide and native libjxl (below).
15
20
 
16
21
  ## Use it
17
22
 
@@ -34,14 +39,27 @@ bytes and returns a `Uint8Array` holding a bare JPEG XL codestream (the `.jxl` f
34
39
  takes a JPEG's bytes and returns `{bytes, width, height, orientation}`; the width and height are the picture's as
35
40
  shown (swapped when the Exif orientation turns it), and the orientation is kept in the JPEG XL header.
36
41
 
37
- Named entries for a tighter bundle: `encodeLosslessRGBA`, `encodeLossyRGBA`, `transcode`, and the raw
38
- `encodeLossless`, `encodeLossy`, `transcodeJPEG`, `parseJPEG`, `inspectPixels` beneath them. The whole module is
39
- the eleven `.mjs` files at the root of this repository: copy them into an app as they are, or install the package.
42
+ Four complete ES modules carry their MIT notice inside: `rapier-jxl.min.mjs` (core; `rapier-jxl/min`),
43
+ `lossless.min.mjs` (`encodeLosslessRGBA`, `LIMITS`; `rapier-jxl/lossless`), `jpeg.min.mjs` (`transcode`, `LIMITS`;
44
+ `rapier-jxl/jpeg`), and `photo.min.mjs` (`encodePhotoRGBA`, `LIMITS`; `rapier-jxl/photo`). The readable modules
45
+ are beside them, `index.mjs` the core entry and `photo.mjs` the optional photo entry. Copy a complete bundle
46
+ into an app, or install the package. The checked entries are `encode`, `encodeLosslessRGBA`,
47
+ `encodeLossyRGBA`, `encodePhotoRGBA` and `transcode`; the raw
48
+ `encodeLossless`, `encodeLossy`, `transcodeJPEG`, `parseJPEG` and `inspectPixels` beneath them take input the
49
+ checked entries have already admitted and may throw plain errors on anything else.
50
+
51
+ ```js
52
+ import {encodePhotoRGBA} from 'rapier-jxl/photo';
53
+ const photo = encodePhotoRGBA(data, width, height, {quality: 90});
54
+ ```
55
+
56
+ The photo entry defaults to quality 90; quality 100 is exact lossless. Its 1–99 range controls photographic
57
+ quantisation, while `encode` retains its artwork-oriented modular path. Quality numbers do not promise
58
+ identical PSNR between codecs or images. Neither lossy entry promises fewer bytes than lossless for every input.
40
59
 
41
60
  ### In a worker
42
61
 
43
- Encoding is synchronous and takes tens to hundreds of milliseconds on a large picture, so do it off the main
44
- thread. A complete worker is this:
62
+ Encoding is synchronous; large pictures can take seconds, so run it off the main thread. A complete worker is this:
45
63
 
46
64
  ```js
47
65
  // jxl-worker.mjs
@@ -55,33 +73,42 @@ self.onmessage = ({data: {id, op, ...ask}}) => {
55
73
  ```
56
74
 
57
75
  Post `{id, op: 'encode', data, width, height, quality}` or `{id, op: 'transcode', jpeg}`; receive `{id, ok: true,
58
- bytes, ...}` or `{id, ok: false, code, message}`, the bytes transferred, never copied.
76
+ bytes, ...}` or `{id, ok: false, code, message}`, the output bytes transferred. To cancel synchronous work,
77
+ terminate that dedicated worker and discard its request ID. A queued abort message cannot interrupt a
78
+ synchronous encode. Keep the input buffer in the caller if retry is needed; posting it without a transfer list
79
+ copies it, while transferring it gives ownership to the worker.
59
80
 
60
81
  ### Limits and errors
61
82
 
62
- One picture at a time, at most 16,384 pixels on a side, 24 million pixels, and a 16 MiB stream. Anything else is
63
- refused before any work with an `Error` whose `code` is one of `JXL_INPUT` (the arguments), `JXL_DIMENSIONS`,
64
- `JXL_SIZE`, `JXL_MEMORY` (the engine ran out of memory) or `JXL_JPEG` (a JPEG the carrier does not take:
65
- arithmetic coding, 12-bit, lossless, CMYK or a DNL height; decode it and encode its pixels instead). There is no
66
- other failure: a call either returns a stream every decoder reads or throws one of these.
83
+ One picture at a time, at most 16,384 pixels on a side, 24 million pixels, and a 16 MiB stream. The arguments are
84
+ read before any work, and a JPEG's header before a byte of it is allocated. A refusal is an `Error` whose `code`
85
+ is one of `JXL_INPUT` (the arguments), `JXL_DIMENSIONS`, `JXL_SIZE`, `JXL_MEMORY` (the engine ran out of memory)
86
+ or `JXL_JPEG`: a JPEG the carrier does not take (arithmetic coding, 12-bit, lossless, CMYK, a DNL height, a colour
87
+ profile other than sRGB), or a JPEG cut short or out of order, which is refused rather than carried with pixels
88
+ invented; decode such a JPEG and encode its pixels instead. A checked call returns a stream or throws one of these.
67
89
 
68
90
  ## Sizes
69
91
 
70
- | what | source | gzipped |
71
- | --- | --- | --- |
72
- | the whole module (the eleven files) | 83.7 KB | 24.7 KB |
73
- | lossless only (`encodeLosslessRGBA`) | 35.7 KB | 11.0 KB |
74
- | the JPEG carrier only (`transcode`) | 60.1 KB | 18.0 KB |
92
+ | file | bytes | gzip | Brotli |
93
+ | --- | ---: | ---: | ---: |
94
+ | `rapier-jxl.min.mjs`, core | 40,286 | 15,904 | 14,096 |
95
+ | `lossless.min.mjs`, `encodeLosslessRGBA` alone | 16,085 | 6,747 | 5,948 |
96
+ | `jpeg.min.mjs`, `transcode` alone | 30,624 | 12,274 | 10,866 |
97
+ | `photo.min.mjs`, photographic pixels | 25,418 | 10,409 | 9,209 |
98
+ | all readable modules, including photo | 103,193 | 31,376 | |
75
99
 
76
- Measured on this release's files by the script that stages this repository, gzip at level 9, before any
77
- minifier. A bundler that drops what you do not import lands between the rows.
100
+ Exact bytes of this release's files, measured by the script that stages this repository; `sizes.json` carries
101
+ their hashes and the tools (terser 5.51.2, Node v22.22.2; gzip at level 9, Brotli at quality 11). A minified
102
+ file keeps every check and every export it names, and is proved at staging to encode what the readable source
103
+ encodes; the readable modules' gzip is of their concatenation.
78
104
 
79
105
  What it writes, so a decoder's author knows what to expect: bare codestreams (no container box), 8-bit only,
80
- prefix codes only (never ANS), one frame, no preview, no animation, no ICC profile (sRGB is declared), no
81
- XYB, no chroma-from-luma, no filters. Lossless pictures use the modular mode with a palette of up to 512
82
- colours, the reversible YCoCg transform, the clamped-gradient predictor and one prefix code per channel, in
83
- groups of 256 by 256 pixels. Lossy pictures use the modular mode with the Squeeze transform. Carried JPEGs use
84
- the VarDCT mode with the JPEG's own quantisation tables as raw dequantisation matrices.
106
+ prefix codes only (never ANS), one frame, no preview, no animation, no ICC profile (sRGB is declared, and a JPEG
107
+ with another profile is refused), no XYB, no chroma-from-luma, no filters. Lossless pictures use modular mode,
108
+ comparing a palette of up to 512 colours with direct encoding by actual stream length, in groups of 256 by 256
109
+ pixels. Measured prediction and colour-transform choices keep smooth artwork and independent channels small.
110
+ Lossy artwork uses the Squeeze transform with exact alpha. Carried JPEGs use VarDCT with the JPEG's quantisation
111
+ tables as raw dequantisation matrices. The photo entry produces DCT8 coefficients from pixels for the same writer.
85
112
 
86
113
  ## Why it exists
87
114
 
@@ -96,11 +123,14 @@ encoder is this one: tell your agent to add `rapier-jxl` (see `AGENTS.md`) and i
96
123
 
97
124
  ## How it is checked
98
125
 
99
- Rapier's own tree holds the tests: every stream this encoder writes is decoded again through
100
- [jxl-oxide](https://github.com/tirr-c/jxl-oxide) and compared pixel by pixel (exact where exactness is promised,
101
- above 38 dB where it is not, one stream for every JPEG form of one picture), and the page's retained checks run
102
- the same before each release. This repository is republished from that tree at each release, so what is here has
103
- passed them.
126
+ Tests and deterministic structure-aware fuzzing live in `public/test/`, with small JPEG seeds and generated pixel
127
+ cases. Install the development dependencies and run `npm test`. The workflow requires both
128
+ [jxl-oxide](https://github.com/tirr-c/jxl-oxide) 0.12.6 and FFmpeg's native libjxl decoder; a missing native oracle
129
+ is explicitly reported locally and is a failure in CI. Cases compare exact pixels and alpha where promised,
130
+ lossy fidelity where relevant, accepted JPEG content, and refusal of malformed inputs. Staging also proves each
131
+ minified entry returns the same bytes as its readable source. The 30 September audit's 131 pixel cases and
132
+ 36 JPEG forms remain the acceptance baseline. These checks cover their inputs, not every possible codestream
133
+ or every decoder implementation.
104
134
 
105
135
  ## Licence
106
136
 
package/bits.mjs CHANGED
@@ -1,6 +1,11 @@
1
- // Rapier's JPEG XL encoder: the bit writer. MIT (LICENSE).
1
+ // Rapier's JPEG XL encoder: the bit writer, and the limits every part keeps. MIT (LICENSE).
2
2
  // JPEG XL packs bits least-significant first; `write` takes up to 32 bits at a time.
3
3
 
4
+ // What one call takes at most: the 16 MiB codestream, 24 million pixels, 16,384 on a side. The checked API refuses a
5
+ // larger ask before any work, the JPEG reader before it allocates a plane, and a writer that would grow past the
6
+ // stream's bound stops there instead of filling memory first.
7
+ export const LIMITS = Object.freeze({bytes: 16 * 1024 * 1024, pixels: 24_000_000, edge: 16384});
8
+
4
9
  export class BitWriter {
5
10
  constructor(capacity = 4096) {
6
11
  this.bytes = new Uint8Array(capacity);
@@ -31,7 +36,9 @@ export class BitWriter {
31
36
  }
32
37
  get bitLength() { return this.at * 8 + this.pending; }
33
38
  grow(need = 0) {
34
- const next = new Uint8Array(Math.max(this.bytes.length * 2, this.at + need + 16));
39
+ const cap = LIMITS.bytes + 64;
40
+ if (this.at + need + 16 > cap) throw Object.assign(new Error('The encoded picture exceeds 16 MiB.'), {code: 'JXL_SIZE'});
41
+ const next = new Uint8Array(Math.min(cap, Math.max(this.bytes.length * 2, this.at + need + 16)));
35
42
  next.set(this.bytes.subarray(0, this.at)); this.bytes = next;
36
43
  }
37
44
  // Appends another writer's bits at the current bit position.
package/entropy.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  // The AC coefficients of a VarDCT frame are coded in hundreds of contexts; the contexts are clustered into a few
3
3
  // dozen histograms by greedy merging (the cheapest entropy increase first), each histogram takes the hybrid
4
4
  // integer split that costs least on its own values, and a prefix code is built per histogram.
5
- import {buildCode, uintConfig, hybridToken} from './prefix.mjs';
5
+ import {buildCode, uintConfig, hybridToken, writeHybrid} from './prefix.mjs';
6
6
  import {floorLog2} from './bits.mjs';
7
7
 
8
8
  const CONFIGS = [uintConfig(0), uintConfig(1), uintConfig(2), uintConfig(3), uintConfig(4), uintConfig(4, 1, 1), uintConfig(5, 1, 1), uintConfig(2, 0, 1), uintConfig(3, 0, 1)];
@@ -31,9 +31,7 @@ export class TokenCounts {
31
31
  // The bits a set of contexts costs under a configuration: token entropy plus the raw bits.
32
32
  cost(contexts, config) {
33
33
  const freqs = this.tokens(contexts, config);
34
- let total = 0, bits = 0;
35
- for (const f of freqs) total += f;
36
- for (const f of freqs) if (f) bits += f * Math.log2(total / f);
34
+ let bits = entropyBits(freqs);
37
35
  for (const ctx of contexts) {
38
36
  const base = ctx * 64;
39
37
  for (let v = config.splitToken; v < 64; v++) if (this.small[base + v]) bits += this.small[base + v] * (floorLog2(v) - config.msb - config.lsb);
@@ -91,9 +89,7 @@ export function buildTokenCoding(counts, {maxClusters = 48, newClusterCost = 320
91
89
  });
92
90
  const write = (w, ctx, value) => {
93
91
  const histogram = histograms[contextMap[ctx]];
94
- hybridToken(histogram.config, value, slots);
95
- w.write(histogram.code.lengths[slots[0]], histogram.code.codes[slots[0]]);
96
- if (slots[1]) w.write(slots[1], slots[2]);
92
+ writeHybrid(w, histogram.code, histogram.config, value);
97
93
  };
98
94
  return {contextMap, histograms, write, bits};
99
95
  }
package/index.mjs CHANGED
@@ -6,10 +6,11 @@ import {inspectPixels, encodeLossless} from './lossless.mjs';
6
6
  import {encodeLossy} from './lossy.mjs';
7
7
  import {transcodeJPEG} from './vardct.mjs';
8
8
  import {parseJPEG} from './jpeg.mjs';
9
+ import {LIMITS} from './bits.mjs';
9
10
 
10
- // What one call takes at most: the 16 MiB codestream, 24 million pixels, 16,384 on a side. Larger asks are refused
11
- // with a coded error before any work, the way a decoder would refuse them after it.
12
- export const LIMITS = Object.freeze({bytes: 16 * 1024 * 1024, pixels: 24_000_000, edge: 16384});
11
+ // What one call takes at most (bits.mjs): the 16 MiB codestream, 24 million pixels, 16,384 on a side. Larger asks are
12
+ // refused with a coded error before any work, the way a decoder would refuse them after it.
13
+ export {LIMITS};
13
14
 
14
15
  const fault = (code, message) => Object.assign(new Error(message), {code});
15
16
  function admitSize(width, height) {
@@ -22,6 +23,9 @@ function admitPixels(data, width, height) {
22
23
  if (!(data instanceof Uint8Array) && !(data instanceof Uint8ClampedArray)) throw fault('JXL_INPUT', 'Pixels are a Uint8Array or Uint8ClampedArray of RGBA bytes.');
23
24
  if (data.length !== width * height * 4) throw fault('JXL_INPUT', 'Pixels are width * height * 4 bytes: straight (not premultiplied) RGBA, row by row.');
24
25
  }
26
+ function admitQuality(quality) {
27
+ if (typeof quality !== 'number' || !Number.isFinite(quality) || quality < 1 || quality > 100) throw fault('JXL_INPUT', 'Quality is a number from 1 to 100.');
28
+ }
25
29
  function answer(bytes) {
26
30
  if (bytes.length > LIMITS.bytes) throw fault('JXL_SIZE', 'The encoded picture exceeds 16 MiB.');
27
31
  return bytes;
@@ -32,36 +36,49 @@ function guard(work) {
32
36
  }
33
37
 
34
38
  // Exact: every pixel comes back as it went in. An opaque alpha is dropped, a grey picture keeps one channel,
35
- // up to 512 colours become a palette, colour goes through the reversible YCoCg transform.
39
+ // up to 512 colours may use a palette when its complete stream wins. Larger images sample reversible colour
40
+ // transforms and predictors; the selected direct candidate is still compared with the palette by actual bytes.
36
41
  export function encodeLosslessRGBA(data, width, height) {
37
42
  admitPixels(data, width, height);
38
43
  return guard(() => answer(encodeLossless(data, width, height, {shape: inspectPixels(data, width, height)})));
39
44
  }
40
45
 
41
- // Lossy modular at a quality from 1 to 99 (90 is libjxl's distance 1.0); a picture of few colours is answered
46
+ // Lossy modular at a quality from 1 to 99, using libjxl's quality-to-distance curve (not equivalent PSNR).
47
+ // Alpha stays exact. A picture of few colours is answered
42
48
  // exact when that is fewer bytes. Quality 100 is the lossless answer.
43
49
  export function encodeLossyRGBA(data, width, height, quality = 90) {
44
50
  admitPixels(data, width, height);
45
- if (!Number.isFinite(quality) || quality < 1 || quality > 100) throw fault('JXL_INPUT', 'Quality is a number from 1 to 100.');
51
+ admitQuality(quality);
46
52
  return guard(() => {
47
53
  const shape = inspectPixels(data, width, height);
48
54
  if (quality >= 100) return answer(encodeLossless(data, width, height, {shape}));
49
- let bytes = encodeLossy(data, width, height, {quality, shape});
50
- if (shape.palette) { const exact = encodeLossless(data, width, height, {shape}); if (exact.length <= bytes.length) bytes = exact; }
51
- return answer(bytes);
55
+ if (!shape.palette) return answer(encodeLossy(data, width, height, {quality, shape}));
56
+ // The exact stream of a few-colour picture is cheap to make (256-pixel sections), so it is made first: a lossy
57
+ // attempt that runs out of memory (its planes and Squeeze copies cost eight bytes a pixel and more) still answers.
58
+ const exact = encodeLossless(data, width, height, {shape});
59
+ let bytes;
60
+ try { bytes = encodeLossy(data, width, height, {quality, shape}); }
61
+ catch (error) { if (!(error instanceof RangeError) || error.code) throw error; bytes = exact; }
62
+ return answer(exact.length <= bytes.length ? exact : bytes);
52
63
  });
53
64
  }
54
65
 
55
- // One call for both: quality 100 (the default) is exact, anything lower is lossy.
56
- export function encode(data, width, height, {quality = 100} = {}) {
66
+ // One call for both: quality 100 (the default) is exact, anything lower is lossy. The options are read before any
67
+ // work: a quality that is not a number from 1 to 100 is JXL_INPUT, never a silent choice.
68
+ export function encode(data, width, height, options = {}) {
69
+ if (options === null || typeof options !== 'object') throw fault('JXL_INPUT', 'Options are an object: {quality}.');
70
+ const quality = options.quality === undefined ? 100 : options.quality;
71
+ admitQuality(quality);
57
72
  return quality >= 100 ? encodeLosslessRGBA(data, width, height) : encodeLossyRGBA(data, width, height, quality);
58
73
  }
59
74
 
60
- // A JPEG carried whole into JPEG XL: its coefficients, quantisation tables, colour and subsampling kept, only the
61
- // entropy coding changed, so the picture decodes to the JPEG's own pixels at about a fifth fewer bytes. Baseline,
62
- // extended sequential and progressive scans, with restarts; 8-bit; grey or YCbCr/RGB; an Exif orientation kept.
63
- // A JPEG this path does not take (arithmetic coding, 12-bit, lossless, CMYK, a DNL height) is refused as
64
- // JXL_JPEG; decode it and encode its pixels instead.
75
+ // A JPEG carried as its coefficients into JPEG XL: the quantised DCT coefficients, quantisation tables, colour and
76
+ // subsampling kept, only the entropy coding changed, so the picture decodes to the JPEG's own pixels at about a
77
+ // fifth fewer bytes. Baseline, extended sequential and progressive scans, with restarts; 8-bit; grey, YCbCr or RGB;
78
+ // an Exif orientation kept. Not carried: the JPEG reconstruction data (the JPEG file itself cannot be rebuilt from
79
+ // the stream), ICC, Exif beyond the orientation, XMP. A JPEG this path does not take (arithmetic coding, 12-bit,
80
+ // lossless, CMYK, a DNL height, a colour profile other than sRGB, a file cut short) is refused as JXL_JPEG; decode
81
+ // it and encode its pixels instead.
65
82
  export function transcode(jpeg) {
66
83
  if (!(jpeg instanceof Uint8Array) || !jpeg.length) throw fault('JXL_INPUT', 'A JPEG is a non-empty Uint8Array.');
67
84
  if (jpeg.length > LIMITS.bytes) throw fault('JXL_SIZE', 'A JPEG is at most 16 MiB.');
package/jpeg.min.mjs ADDED
@@ -0,0 +1,2 @@
1
+ /*! Rapier JXL 1.1.19 | 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
+ const t=Object.freeze({bytes:16777216,pixels:24e6,edge:16384});class e{constructor(t=4096){this.bytes=new Uint8Array(t),this.at=0,this.acc=0,this.pending=0}write(t,e){if(t>32||e<0||e>=2**t)throw new Error("bit write out of range: "+t+" bits, "+e);let n=this.acc+e*2**this.pending,o=this.pending+t;this.at+5>=this.bytes.length&&this.grow();const r=this.bytes;for(;o>=8;)r[this.at++]=255&n,n=Math.floor(n/256),o-=8;this.acc=n,this.pending=o}writeU32(t,e){for(let n=0;n<4;n++){const[o,r]=t[n];if(e>=r&&e-r<2**o)return this.write(2,n),void(o&&this.write(o,e-r))}throw new Error("U32 value out of range: "+e)}zeroPadToByte(){this.pending&&(this.at+1>=this.bytes.length&&this.grow(),this.bytes[this.at++]=this.acc,this.acc=0,this.pending=0)}get bitLength(){return 8*this.at+this.pending}grow(e=0){const n=t.bytes+64;if(this.at+e+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+e+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 e=0;e<t.at;e++)this.write(8,t.bytes[e]);else this.bytes.set(t.bytes.subarray(0,t.at),this.at),this.at+=t.at;t.pending&&this.write(t.pending,t.acc)}finish(){const t=this.at+(this.pending?1:0),e=new Uint8Array(t);return e.set(this.bytes.subarray(0,this.at)),this.pending&&(e[this.at]=this.acc),e}}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 i(t){if(0===t)return 0;const e=t<0?32768:0;if(!((t=Math.abs(t))<65520))throw new Error("half float out of range: "+t);let n=Math.floor(Math.log2(t)),o=t/2**n-1;n<-14&&(o=t/2**-14,n=-15);let r=Math.round(1024*o);return 1024===r&&(r=0,n++),e|n+15<<10|r}const s=[1,2,3,4,0,5,17,6,16,7,8,9,10,11,12,13,14,15],c=[2,4,3,2,2,4],a=[0,7,3,2,1,15];function f(t,e){const n=new Uint8Array(t.length),o=[];for(let e=0;e<t.length;e++)t[e]>0&&o.push(e);if(!o.length)return n;if(1===o.length)return n[o[0]]=1,n;for(let r=1;;r*=2){const i=o.map(e=>({w:Math.max(t[e],r),sym:e,l:null,r:null})).sort((t,e)=>t.w-e.w||t.sym-e.sym),s=[];let c=0,a=0;const f=()=>c<i.length&&(a>=s.length||i[c].w<=s[a].w)?i[c++]:s[a++];for(;i.length-c+(s.length-a)>1;){const t=f(),e=f();s.push({w:t.w+e.w,sym:-1,l:t,r:e})}const l=[[f(),0]];let h=0;for(;l.length;){const[t,e]=l.pop();t.sym>=0?(n[t.sym]=e,e>h&&(h=e)):l.push([t.l,e+1],[t.r,e+1])}if(h<=e)return n}}function l(t,e,n,o,r,i){const s=[];for(;s.push(o&r),o>>=i;)o--;for(let o=s.length-1;o>=0;o--)t.push(n),e.push(s[o])}function h(t,e){let n=0;for(let o=0;o<e;o++)n=n<<1|1&t,t>>=1;return n}function w(t){const e=new Uint32Array(17),n=new Uint32Array(17),o=new Uint16Array(t.length);for(const n of t)e[n]++;e[0]=0;for(let t=1,o=0;t<=16;t++)o=o+e[t-1]<<1,n[t]=o;for(let e=0;e<t.length;e++)t[e]&&(o[e]=h(n[t[e]]++,t[e]));return o}function u(t){const e=[];for(let n=0;n<t.length;n++)t[n]>0&&e.push(n);const n=e.length,o=e[n-1]+1||1,r=new Uint8Array(o);if(n<=4){e.sort((e,n)=>t[n]-t[e]||e-n);const i=4===n&&t[e[0]]>t[e[2]]+t[e[3]]?1:0,s=n<2?[0]:2===n?[1,1]:3===n?[1,2,2]:i?[1,2,3,3]:[2,2,2,2];return e.forEach((t,e)=>{r[t]=s[e]}),e.sort((t,e)=>r[t]-r[e]||t-e),{alphabetSize:o,lengths:r,codes:w(r),simple:n?e:null,treeSelect:i}}const i=f(t.subarray?t.subarray(0,o):t.slice(0,o),15);return r.set(i),{alphabetSize:o,lengths:r,codes:w(r),simple:null,treeSelect:0}}function p(t,e){if(e.alphabetSize<=1)return;if(e.simple){const n=o(e.alphabetSize-1)+1;t.write(2,1),t.write(2,e.simple.length-1);for(const o of e.simple)t.write(n,o);return void(4===e.simple.length&&t.write(1,e.treeSelect))}const n=e.lengths,r=[],i=[];let h=n.length-1;for(;h>0&&!n[h];)h--;let u=8;for(let t=0;t<=h;){const e=n[t];let o=1;for(;t+o<=h&&n[t+o]===e;)o++;if(0===e)if(o<3)for(let t=0;t<o;t++)r.push(0),i.push(0);else l(r,i,17,o-3,7,3);else{let t=o;if(e!==u&&(r.push(e),i.push(0),t--,u=e),t<3)for(let n=0;n<t;n++)r.push(e),i.push(0);else l(r,i,16,t-3,3,2)}t+=o}const p=new Uint32Array(18);for(const t of r)p[t]++;let g=0;for(const t of p)t&&g++;1===g&&(p[r[0]?0:1]=p[r[0]]);const m=f(p,5),d=w(m);let b=18;for(;b>0&&!m[s[b-1]];)b--;t.write(2,0);for(let e=0;e<b;e++){const n=m[s[e]];t.write(c[n],a[n])}for(let e=0;e<r.length;e++){const n=r[e];t.write(m[n],d[n]),16===n?t.write(2,i[e]):17===n&&t.write(3,i[e])}}function g(t,e=0,n=0){return{split:t,msb:e,lsb:n,splitToken:1<<t}}function m(t,e,n=15){t.write(r(n+1),e.split),e.split!==n&&(t.write(r(e.split+1),e.msb),t.write(r(e.split-e.msb+1),e.lsb))}function d(t,e,n){if(e<t.splitToken)return n[0]=e,n[1]=0,void(n[2]=0);const r=o(e),i=e-(1<<r),s=r-t.msb-t.lsb;n[0]=t.splitToken+(r-t.split<<t.msb+t.lsb|i>>r-t.msb<<t.lsb|i&(1<<t.lsb)-1),n[1]=s,n[2]=e>>t.lsb&(1<<s)-1}function b(t,e,n,o=0){d(t,e,y),n[o+y[0]]++}const y=[0,0,0];function M(t,e,n,o){d(n,o,y),t.write(e.lengths[y[0]],e.codes[y[0]]),y[1]&&t.write(y[1],y[2])}function x(t,e){let n=0;for(const t of e)n=Math.max(n,t);const o=n?r(n+1):0;if(o<=3){if(t.write(1,1),t.write(2,o),o)for(const n of e)t.write(o,n);return}t.write(1,0),t.write(1,0);const i=g(8),s=g(4),c=e.length>=64,a=new Uint32Array(c?257:n+1),f=[];for(let t=0;t<e.length;){let n=1;for(;c&&t+n<e.length&&e[t+n]===e[t];)n++;if(n>=8)f.push([e[t],n-1]),a[e[t]]++,b(s,n-1-7,a,224),t+=n;else{for(let o=0;o<n;o++)f.push([e[t],0]),a[e[t]]++;t+=n}}const l=u(a);if(!c){A(t,{contextMap:new Uint8Array(1),histograms:[{config:i,code:l}]});for(const[e]of f)M(t,l,i,e);return}const h=new Uint32Array(1);h[0]=1,A(t,{lz77:{minSymbol:224,minLength:7,lengthConfig:s},contextMap:new Uint8Array([1,0]),histograms:[{config:g(0),code:u(h)},{config:i,code:l}]});const w=[0,0,0];for(const[e,n]of f)if(M(t,l,i,e),n){d(s,n-7,w);const e=224+w[0];t.write(l.lengths[e],l.codes[e]),w[1]&&t.write(w[1],w[2])}}function A(t,{lz77:e=null,contextMap:n,histograms:r}){t.write(1,e?1:0),e&&(t.writeU32([[0,224],[0,512],[0,4096],[15,8]],e.minSymbol),t.writeU32([[0,3],[0,4],[2,5],[8,9]],e.minLength),m(t,e.lengthConfig,8)),n.length>1&&x(t,n),t.write(1,1);for(const e of r)m(t,e.config,15);for(const e of r){const n=e.code.alphabetSize-1;if(n){const e=o(n);t.write(1,1),t.write(4,e),t.write(e,n-(1<<e))}else t.write(1,0)}for(const e of r)p(t,e.code)}const v=Object.freeze({zero:0,left:1,top:2,average0:3,select:4,gradient:5,weighted:6,topRight:7,topLeft:8,leftLeft:9,average1:10,average2:11,average3:12,average4:13}),U=Object.freeze({minSymbol:224,minLength:7,lengthConfig:g(4)}),S=g(0),C=U.minSymbol+33;function k(t,e){const n=[[3,0],[6,8],[10,72],[13,1096]];if("rct"===e.type)t.write(2,0),t.writeU32(n,e.beginC),t.writeU32([[0,6],[2,0],[4,2],[6,10]],e.rctType??6);else if("palette"===e.type)t.write(2,1),t.writeU32(n,e.beginC),t.writeU32([[0,1],[0,3],[0,4],[13,1]],e.numC),t.writeU32([[8,0],[10,256],[12,1280],[16,5376]],e.nbColors),t.writeU32([[0,0],[8,1],[10,257],[16,1281]],e.nbDeltas??0),t.write(4,e.predictor??0);else{if("squeeze"!==e.type)throw new Error("unknown transform "+e.type);{t.write(2,2);const o=e.params??[];t.writeU32([[0,0],[4,1],[6,9],[8,41]],o.length);for(const e of o)t.write(1,e.horizontal?1:0),t.write(1,e.inPlace?1:0),t.writeU32(n,e.beginC),t.writeU32([[0,1],[0,2],[0,3],[4,4]],e.numC)}}}function I(t,e,o,r,i,s){const c=s.predictor,a=s.offset,f=s.multiplier,l=S,h=U.lengthConfig,w=[0,0,0],u=t?e.lengths:null,p=t?e.codes:null;let g=0;const m=n=>{t?(d(l,n,w),t.write(u[w[0]],p[w[0]]),w[1]&&t.write(w[1],w[2])):b(l,n,e)},y=()=>{if(g){if(g>=U.minLength+1){m(0);const n=g-U.minLength-1;if(t){d(h,n,w);const e=U.minSymbol+w[0];t.write(u[e],p[e]),w[1]&&t.write(w[1],w[2])}else b(h,n,e,U.minSymbol)}else for(let t=0;t<g;t++)m(0);g=0}},M=(t,e)=>{const r=o[t];let i;1===f?i=r-e-a:(i=Math.round((r-e-a)/f),o[t]=e+a+i*f),0!==i?(y(),m(n(i))):g++};for(let t=0,e=0;t<i;t++)for(let n=0;n<r;n++,e++){let i;if(0===c)i=0;else{const s=n?o[e-1]:t?o[e-r]:0,a=t?o[e-r]:s;if(1===c)i=s;else if(2===c)i=a;else if(3===c)i=(s+a)/2|0;else{const f=s+a-(n&&t?o[e-r-1]:s);if(5===c){const t=s<a?s:a,e=s<a?a:s;i=f<t?t:f>e?e:f}else{if(4!==c)throw new Error("predictor "+c+" is not coded here");i=Math.abs(f-s)<Math.abs(f-a)?s:a}}}M(e,i)}y()}const E=256;function q(t,e){e<=512?(t.write(2,0),t.write(9,e-1)):e<=8192?(t.write(2,1),t.write(13,e-1)):e<=1<<18?(t.write(2,2),t.write(18,e-1)):(t.write(2,3),t.write(30,e-1))}const z=[0,1024,17408,4211712],L=[12,16,24,32];const O=[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];function J(t,e="JXL_JPEG"){return Object.assign(new Error("JPEG: "+t),{code:e})}function P(t,e){const n=new Uint16Array(65536);let o=0,r=0;for(let i=1;i<=16;i++){for(let s=0;s<t[i-1];s++){const t=e[r++],s=o<<16-i,c=1<<16-i;if(s+c>=65536)throw J("bad Huffman table");n.fill(i<<8|t,s,s+c),o++}o<<=1}return n}function T(e){if(!(e.length>4)||255!==e[0]||216!==e[1])throw J("not a JPEG");const n=[],o=[[],[]],r=new Set,i={count:0,parts:[]};let s=null,c=0,a=-1,f=!1,l=1,h=2;const w=t=>e[t]<<8|e[t+1];for(;;){if(h>=e.length){if(s&&s.scans)break;throw J("truncated")}if(255!==e[h])throw J("marker expected");for(;255===e[h];)h++;const u=e[h++];if(217===u)break;if(216===u)throw J("a second start");if(u>=208&&u<=215)throw J("a restart marker outside a scan");if(1===u)continue;if(h+2>e.length)throw J("truncated");const p=w(h),g=h+2,m=h+p;if(p<2||m>e.length)throw J("bad segment");if(h=m,219===u)for(let t=g;t<m;){const o=e[t]>>4,r=15&e[t];if(o>1||r>3||t+1+(o?128:64)>m)throw J("bad quantisation table");t++;const i=new Int32Array(64);for(let n=0;n<64;n++)if(i[O[n]]=o?w(t):e[t],t+=o?2:1,!i[O[n]])throw J("a zero quantisation step");if(s?.progressive&&s.components.some(t=>t.tq===r&&t.quant&&t.coverage.some(t=>0!==t)&&t.quant.some((t,e)=>t!==i[e])))throw J("a quantisation table changed between progressive scans");n[r]=i}else if(196===u)for(let t=g;t<m;){const n=e[t]>>4,i=15&e[t];if(n>1||i>3||t+17>m)throw J("bad Huffman table");t++;const s=e.subarray(t,t+16);let c=0;for(const t of s)c+=t;if(c>256||t+16+c>m)throw J("bad Huffman table");const a=e.subarray(t+16,t+16+c);o[n][i]=P(s,a),0===n&&(a.some(t=>t>15)?r.add(i):r.delete(i)),t+=16+c}else if(192===u||193===u||194===u){if(s)throw J("two frames");if(m-g<6)throw J("bad frame header");if(8!==e[g])throw J("only 8-bit samples");const n=w(g+1),o=w(g+3),r=e[g+5];if(!o||!n)throw J("a height left to a DNL marker");if(1!==r&&3!==r)throw J("only grey or three components");if(m-g<6+3*r)throw J("bad frame header");if(o>t.edge||n>t.edge)throw J("a picture past "+t.edge+" pixels on a side","JXL_DIMENSIONS");if(o*n>t.pixels)throw J("a picture past "+t.pixels+" pixels","JXL_DIMENSIONS");const i=[];let c=0;for(let t=0,n=g+6;t<r;t++,n+=3){const t=e[n+1]>>4,o=15&e[n+1],r=e[n+2];if(t<1||t>4||o<1||o>4)throw J("bad sampling");if(r>3)throw J("bad quantisation table");if(i.some(t=>t.id===e[n]))throw J("two components with one id");c+=t*o,i.push({id:e[n],h:t,v:o,tq:r,pred:0,coverage:new Int8Array(64).fill(-1)})}if(c>10)throw J("bad sampling");const a=Math.max(...i.map(t=>t.h)),f=Math.max(...i.map(t=>t.v)),l=Math.ceil(o/(8*a)),h=Math.ceil(n/(8*f));for(const t of i)t.blocksW=Math.ceil(Math.ceil(o*t.h/a)/8),t.blocksH=Math.ceil(Math.ceil(n*t.v/f)/8),t.stride=l*t.h,t.rows=h*t.v,t.coeffs=new Int16Array(t.stride*t.rows*64);s={progressive:194===u,width:o,height:n,components:i,hmax:a,vmax:f,mcusX:l,mcusY:h,scans:0}}else{if(195===u||u>=197&&u<=199||u>=201&&u<=203||u>=205&&u<=207)throw J("lossless, hierarchical or arithmetic coding");if(221===u){if(p<4)throw J("bad segment");c=w(g)}else{if(220===u)throw J("a DNL marker");if(218===u){if(!s)throw J("a scan before the frame");const t=e[g],i=[];if(t<1||t>4||g+1+2*t+3>m)throw J("bad scan header");let a=g+1;for(let r=0;r<t;r++,a+=2){const t=s.components.find(t=>t.id===e[a]);if(!t)throw J("a scan of an unknown component");if(i.some(e=>e.component===t))throw J("a component twice in one scan");if(t.quant||(t.quant=n[t.tq]),!t.quant)throw J("a scan without its quantisation table");i.push({component:t,dc:o[0][e[a+1]>>4],ac:o[1][15&e[a+1]],dcSlot:e[a+1]>>4})}const f=e[a],l=e[a+1],w=e[a+2]>>4,u=15&e[a+2];if((!s.progressive||0===f&&0===w)&&i.some(t=>r.has(t.dcSlot)))throw J("bad Huffman table");h=N(e,m,s,i,f,l,w,u,c),s.scans++}else if(224===u)74===e[g]&&70===e[g+1]&&73===e[g+2]&&70===e[g+3]&&(f=!0);else if(225===u)1===l&&69===e[g]&&120===e[g+1]&&105===e[g+2]&&102===e[g+3]&&(l=D(e,g+6,m));else if(226===u){if(p>=16&&"ICC_PROFILE"===String.fromCharCode(...e.subarray(g,g+11))&&0===e[g+11]){const t=e[g+12],n=e[g+13];if(!t||!n||t>n||i.count&&i.count!==n||i.parts[t])throw J("a colour profile cut short");i.count=n,i.parts[t]=e.subarray(g+14,m)}}else 238===u&&p>=14&&65===e[g]&&100===e[g+1]&&111===e[g+2]&&98===e[g+3]&&101===e[g+4]&&(a=e[g+11])}}}if(!s||!s.scans)throw J("no picture");for(const t of s.components)for(let e=0;e<64;e++)if(0!==t.coverage[e])throw J(s.progressive?"a progressive JPEG whose scans do not finish every coefficient":"a component without its scan");if(i.count&&!function(t){const e=[];for(let n=1;n<=t.count;n++){if(!t.parts[n])throw J("a colour profile cut short");e.push(t.parts[n])}const n=e.reduce((t,e)=>t+e.length,0),o=new Uint8Array(n);for(let t=0,n=0;t<e.length;n+=e[t].length,t++)o.set(e[t],n);const r=t=>t+4<=n?(o[t]<<24|o[t+1]<<16|o[t+2]<<8|o[t+3])>>>0:0,i=t=>t+4<=n?String.fromCharCode(o[t],o[t+1],o[t+2],o[t+3]):"",s=r(128);if(n<132||132+12*s>n)return!1;for(let t=0;t<s;t++){const e=132+12*t;if("desc"!==i(e))continue;const s=r(e+4),c=r(e+8);if(s+c>n||c<16)return!1;let a="";if("desc"===i(s)){const t=Math.min(r(s+8),c-12);for(let e=0;e<t;e++)a+=String.fromCharCode(o[s+12+e])}else{if("mluc"!==i(s))return!1;{if(!r(s+8)||c<28)return!1;const t=r(s+20),e=s+r(s+24);if(e+t>n)return!1;for(let n=0;n+1<t;n+=2)a+=String.fromCharCode(o[e+n]<<8|o[e+n+1])}}return/sRGB|sGray|IEC ?61966/i.test(a)}return!1}(i))throw J("a colour profile other than sRGB (decode the picture and encode its pixels)");let u=!1;return 3!==s.components.length||f||(u=a>=0?0===a:82===s.components[0].id&&71===s.components[1].id&&66===s.components[2].id),{width:s.width,height:s.height,components:s.components,progressive:s.progressive,ycbcr:!u,orientation:l}}function D(t,e,n){const o=77===t[e]&&77===t[e+1];if(!o&&(73!==t[e]||73!==t[e+1]))return 1;const r=e=>e+2>n?0:o?t[e]<<8|t[e+1]:t[e]|t[e+1]<<8;if(42!==r(e+2))return 1;const i=e+(e=>e+4>n?0:o?(t[e]<<24|t[e+1]<<16|t[e+2]<<8|t[e+3])>>>0:(t[e]|t[e+1]<<8|t[e+2]<<16|t[e+3]<<24)>>>0)(e+4),s=r(i);for(let t=0,e=i+2;t<s&&e+12<=n;t++,e+=12){if(274!==r(e))continue;const t=r(e+8);return t>=1&&t<=8?t:1}return 1}function N(t,e,n,o,r,i,s,c,a){!function(t,e,n,o,r,i){if(!t.progressive){if(0!==n||63!==o||0!==r||0!==i)throw J("a bad spectral selection");for(const t of e){if(t.component.coverage.some(t=>-1!==t))throw J("a component coded twice");t.component.coverage.fill(0)}return}if(i>13||r&&r!==i+1)throw J("a bad successive approximation");if(0===n){if(0!==o)throw J("a bad spectral selection");for(const{component:t}of e){if(t.coverage[0]!==(r||-1))throw J(r?"a refinement scan out of order":"a DC scan twice");t.coverage[0]=i}return}if(1!==e.length)throw J("an interleaved AC scan");if(o>63||n>o)throw J("a bad spectral selection");const{coverage:s}=e[0].component;if(-1===s[0])throw J("an AC scan before the DC scan");for(let t=n;t<=o;t++){if(s[t]!==(r||-1))throw J(r?"a refinement scan out of order":"a band coded twice");s[t]=i}}(n,o,r,i,s,c);let f=e,l=0,h=0,w=0,u=!1;const p=()=>{for(;h<=24;){let e=0;if(u||f>=t.length)u=!0,w+=8;else if(e=t[f],255===e){const n=t[f+1];if(0===n)f+=2;else{if(255===n){f++;continue}u=!0,e=0,w+=8}}else f++;l=(l<<8|e)>>>0,h+=8}},g=()=>J("a scan cut short"),m=t=>{if(!t)return 0;if(p(),t>h-w)throw g();const e=l>>>h-t&(1<<t)-1;return h-=t,e},d=(t,e)=>t<1<<e-1?t-(1<<e)+1:t,b=t=>{if(!t)throw J("a scan without its Huffman table");p();const e=t[l>>>h-16&65535];if(!e)throw J("bad Huffman code");if(e>>8>h-w)throw g();return h-=e>>8,255&e},y=()=>{const e=h-w;if(e>=8)throw J("bytes left over in a scan");if(v)throw J("an end-of-band run past its interval");if(e&&(l>>>w&(1<<e)-1)!=(1<<e)-1)throw J("bad scan padding");for(;f+1<t.length&&255===t[f]&&255===t[f+1];)f++},M=e=>{if(y(),255!==t[f]||t[f+1]!==208+(7&e))throw J("a restart marker missing or out of order");f+=2,l=0,h=0,w=0,u=!1;for(const t of o)t.component.pred=0;v=0},x=(t,e)=>{if(e>11)throw J("a DC value out of range");const n=e?d(m(e),e):0;if(t.component.pred+=n,t.component.pred>2047||t.component.pred<-2048)throw J("a DC value out of range");return t.component.pred},A=(t,e)=>{if(t>10)throw J("an AC value out of range");const n=d(m(t),t)*(1<<e);if(n>1023||n<-1023)throw J("an AC value out of range");return n};let v=0;const U=1===o.length,S=n.progressive?0===r?0===s?(t,e,n)=>{const o=x(t,b(t.dc))*(1<<c);if(o>2047||o<-2048)throw J("a DC value out of range");e[n]=o}:(t,e,n)=>{m(1)&&(e[n]|=1<<c)}:0===s?(t,e,n)=>{if(v>0)v--;else for(let o=r;o<=i;){const r=b(t.ac),s=r>>4,a=15&r;if(a){if(o+=s,o>i)throw J("coefficients past the band");e[n+O[o]]=A(a,c),o++}else{if(s<15){v=(1<<s)-1+(s?m(s):0);break}if(o+=16,o>i+1)throw J("coefficients past the band")}}}:(t,e,n)=>{const o=1<<c,s=-1<<c;let a=r;if(v<=0)for(;a<=i;a++){const r=b(t.ac);let c=r>>4,f=0;const l=15&r;if(l){if(1!==l)throw J("a bad refinement code");f=m(1)?o:s}else if(15!==c){v=1<<c,c&&(v+=m(c));break}for(;a<=i;a++){const t=n+O[a];if(0!==e[t])m(1)&&0===(e[t]&o)&&(e[t]+=e[t]>=0?o:s);else if(--c<0)break}if(c>=0||f&&a>i)throw J("coefficients past the band");f&&(e[n+O[a]]=f)}if(v>0){for(;a<=i;a++){const t=n+O[a];0!==e[t]&&m(1)&&0===(e[t]&o)&&(e[t]+=e[t]>=0?o:s)}v--}}:(t,e,n)=>{e[n]=x(t,b(t.dc));for(let o=1;o<64;){const r=b(t.ac),i=r>>4,s=15&r;if(!s){if(15===i){if(o+=16,o>64)throw J("coefficients past the block");continue}if(i)throw J("an end-of-band run in a sequential scan");break}if(o+=i,o>63)throw J("coefficients past the block");e[n+O[o]]=A(s,0),o++}};for(const t of o)t.component.pred=0;const C=o[0].component,k=U?C.blocksW*C.blocksH:n.mcusX*n.mcusY;for(let t=0;t<k;t++)if(a&&t>0&&t%a===0&&M(t/a-1),U){const e=t/C.blocksW|0,n=t-e*C.blocksW;S(o[0],C.coeffs,64*(e*C.stride+n))}else{const e=t/n.mcusX|0,r=t-e*n.mcusX;for(const t of o){const n=t.component;for(let o=0;o<n.v;o++)for(let i=0;i<n.h;i++)S(t,n.coeffs,64*((e*n.v+o)*n.stride+r*n.h+i))}}if(y(),255===t[f]&&t[f+1]>=208&&t[f+1]<=215){if(!a||k%a||t[f+1]!==208+(k/a-1&7))throw J("a restart marker out of order");for(f+=2;f+1<t.length&&255===t[f]&&255===t[f+1];)f++}if(f<t.length&&!(255===t[f]&&f+1<t.length&&0!==t[f+1]&&255!==t[f+1]))throw J("bytes left over after a scan");return f}const X=[g(0),g(1),g(2),g(3),g(4),g(4,1,1),g(5,1,1),g(2,0,1),g(3,0,1)],G=g(4,1,1);class _{constructor(t){this.contexts=t,this.small=new Uint32Array(64*t),this.large=Array.from({length:t},()=>null),this.totals=new Float64Array(t)}add(t,e){if(this.totals[t]++,e<64)return void this.small[64*t+e]++;const n=this.large[t]||(this.large[t]=new Map);n.set(e,(n.get(e)||0)+1)}tokens(t,e,n=64){const o=new Uint32Array(n);for(const n of t){const t=64*n;for(let n=0;n<64;n++)this.small[t+n]&&(o[H(e,n)]+=this.small[t+n]);const r=this.large[n];if(r)for(const[t,n]of r)o[H(e,t)]+=n}return o}cost(t,e){let n=R(this.tokens(t,e));for(const r of t){const t=64*r;for(let r=e.splitToken;r<64;r++)this.small[t+r]&&(n+=this.small[t+r]*(o(r)-e.msb-e.lsb));const i=this.large[r];if(i)for(const[t,r]of i)n+=r*(o(t)-e.msb-e.lsb)}return n}}const B=[0,0,0];function H(t,e){return d(t,e,B),B[0]}function R(t){let e=0,n=0;for(const n of t)e+=n;for(const o of t)o&&(n+=o*Math.log2(e/o));return n}const j=[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],Z=[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],W=(()=>{const t=new Uint8Array(64);let e=1;for(let n=0;n<8;n++)for(let o=0;o<=n;o++){let r=o,i=n-o;n%2&&([r,i]=[i,r]),t[r<1&&i<1?0:e++]=8*i+r}for(let n=7;n>0;n--){const o=n-1;for(let n=0;n<=o;n++){let r=7-(o-n),i=7-n;o%2&&([r,i]=[i,r]),t[e++]=8*i+r}}return t.map(t=>(7&t)<<3|t>>3)})(),Y=(t,e,n)=>2*(Z[t]+j[e])+n;function F(t,e,n){const o=[];if(e>0){const n=new Uint32Array(2048);let r=0;for(const e of t)n[Math.min(2047,Math.max(0,e+1024))]++,r++;let i=0,s=r/(e+1);for(let t=0;t<2048&&o.length<e;t++)i+=n[t],i>s&&(o.push(t-1025),s=r*(o.length+1)/(e+1))}const r=o.length+1,i=new Uint8Array(39*r);for(let t=0;t<r;t++)for(let e=0;e<13;e++)i[(0+e)*r+t]=t,i[(13+e)*r+t]=n?r:r+(t>>1),i[(26+e)*r+t]=n?r:r+(r-1>>1)+1+(t>>1);let s=0;for(const t of i)s=Math.max(s,t+1);return{thresholds:o,numDc:r,map:i,numCtxs:s,bucketOf:t=>{let e=0;for(const n of o)t>n&&e++;return e},contextOf:(t,e)=>i[13*(t<2?1^t:2)*r+e],nonZero:(t,e)=>(Math.min(t,64)<8?Math.min(t,64):4+(Math.min(t,64)>>1))*s+e,zeroDensityOffset:t=>37*s+458*t,contexts:495*s}}function K(t,e,o){const r=e.map(()=>function(t,e=0,n=1){return{predictor:t,offset:e,multiplier:n,context:-1}}(o)),i=r.map(()=>new Uint32Array(C));e.forEach((t,e)=>I(null,i[e],t.data,t.w,t.h,r[e])),function(t,{useGlobalTree:e=!0,transforms:n=[]}={}){t.write(1,e?1:0),t.write(1,1),t.writeU32([[0,0],[0,1],[4,2],[8,18]],n.length);for(const e of n)k(t,e)}(t,{useGlobalTree:!1,transforms:[]});const s=function(t,e){const o=[e],r=[],i=[];for(const t of o){if(t.left){r.push(t.property+1,n(t.splitval)),o.push(t.left,t.right);continue}t.context=i.length,i.push(t);const e=1===t.multiplier?0:31-Math.clz32(t.multiplier&-t.multiplier);r.push(0,t.predictor,n(t.offset),e,(t.multiplier>>e)-1)}const s=new Uint32Array(64);for(const t of r)b(S,t,s);const c=u(s);A(t,{contextMap:new Uint8Array(6),histograms:[{config:S,code:c}]});const a=[0,0,0];for(const e of r)d(S,e,a),t.write(c.lengths[a[0]],c.codes[a[0]]),a[1]&&t.write(a[1],a[2]);return i}(t,function(t){return function(t,e){const n=(o,r)=>{if(o===r)return e?t[o].tree:t[o];const i=o+r>>1;return function(t,e,n,o){return{property:t,splitval:e,left:n,right:o}}(e,e?t[i].streamId:i,n(i+1,r),n(o,i))};return n(0,t.length-1)}(t,0)}(r)),c=function(t,e,n,o=t=>t.context){const r=new Uint8Array(e.length+1);for(const t of e)r[t.context]=o(t)+1;const i=new Uint32Array(2);i[1]=1;const s=[{config:S,code:u(i)}],c=Math.max(...r);for(let t=0;t<c;t++)s.push({config:S,code:u(n[t])});return A(t,{lz77:U,contextMap:r,histograms:s}),s}(t,s,i,t=>r.indexOf(t));e.forEach((e,n)=>I(t,c[n+1].code,e.data,e.w,e.h,r[n]))}function Q(t,o=T(t)){return function(t){const{width:o,height:s,components:c,alpha:a}=t,f=1===c.length,l=t.quantScale||1,h=t.ycbcr||f,w=(f?[0,0,0]:h?[1,0,2]:[0,1,2]).map(t=>c[t]),p=t=>1===t?0:2===t?1:-1,g=w.map(t=>p(t.h)),m=w.map(t=>p(t.v));if(g.includes(-1)||m.includes(-1))throw J("sampling factors other than one or two");const d=Math.max(...g),b=Math.max(...m),y=t=>d-g[t],U=t=>b-m[t];if(!h&&(d||b))throw J("an RGB JPEG with subsampling");const S=Math.ceil(o/(8<<d))<<d,C=Math.ceil(s/(8<<b))<<b,k=Math.ceil(o/E),I=Math.ceil(s/E),O=k*I,P=Math.ceil(S/256),T=P*Math.ceil(C/256),D=1===O,N=(t,e,n)=>{const o=w[t];return f&&1!==t?-1:64*(n*o.stride+e)},B=t=>{let e=1024;for(;t%e;)e>>=1;return e},H=w.map(t=>h?1:t.quant[0]/B(t.quant[0])),j=w.map(t=>h?0:1024/B(t.quant[0])),Z=new Int32Array(S*C);for(let t=0;t<C;t++)for(let e=0;e<S;e++)Z[t*S+e]=w[1].coeffs[N(1,e,t)]*H[1]+j[1];const Q=w[1].quant,V=(Q[8]+Q[16]+Q[24]+Q[32]+Q[40])/l,$=S*C,tt=$<256?0:Math.max(1,Math.min(7,r($)-r(V)-7));let et=F(Z,tt,f);const nt=new e(256);if(function(t,e,n,o,r,{xyb:i=!1,orientation:s=1}={}){t.write(16,2815),t.write(1,0),q(t,n),t.write(3,0),q(t,e),t.write(1,0);const c=1!==s;t.write(1,c?1:0),c&&(t.write(3,s-1),t.write(1,0),t.write(1,0),t.write(1,0)),t.write(1,0),t.write(2,0),t.write(1,1),r?(t.write(2,1),t.write(1,1)):t.write(2,0),t.write(1,i?1:0),3===o?t.write(1,1):(t.write(1,0),t.write(1,0),t.write(2,1),t.write(2,1),t.write(1,0),t.write(2,2),t.write(4,11),t.write(2,1)),c&&t.write(1,1),t.write(2,0),t.write(1,1),t.zeroPadToByte()}(nt,o,s,f?1:3,!!a,{orientation:t.orientation}),nt.write(1,0),nt.write(2,0),nt.write(1,0),nt.write(2,2),nt.write(8,111),nt.write(1,h?1:0),h)for(let t=0;t<3;t++)nt.write(2,0===g[t]&&0===m[t]?0:1===g[t]&&1===m[t]?1:1===g[t]?2:3);nt.write(2,0),a&&nt.write(2,0),nt.write(2,0),nt.write(1,0),nt.write(2,0),a&&nt.write(2,0),nt.write(1,1),nt.write(2,0),nt.write(1,0),nt.write(1,0),nt.write(2,0),nt.write(2,0),nt.write(2,0);const ot=[0,1,2].map(()=>new Int32Array(1024)),rt=(t,e)=>{const o=t%k*32,r=32*(t/k|0),i=Math.min(32,S-o),s=Math.min(32,C-r);for(let t=0;t<s;t++)for(let s=0;s<i;s++)for(const i of[1,0,2]){const c=y(i),a=U(i),f=s>>c,l=t>>a;if(f<<c!==s||l<<a!==t)continue;const h=N(i,(o>>c)+f,(r>>a)+l),u=w[i].coeffs;let p=0;if(h>=0)for(let t=1;t<64;t++)u[h+t]&&p++;const g=ot[i],m=0===f?0===l?32:g[32*(l-1)]:0===l?g[32*l+f-1]:g[32*(l-1)+f]+g[32*l+f-1]+1>>1;g[32*l+f]=p;const d=et.contextOf(i,et.bucketOf(Z[(r+t)*S+o+s]));e(et.nonZero(m,d),p);const b=et.zeroDensityOffset(d);let M=p>4?0:1,x=p;for(let t=1;t<64&&x;t++){const o=u[h+W[t]];e(b+Y(x,t,M),n(o)),M=o?1:0,x-=M}}},it=t=>{et=t;const e=new _(t.contexts);for(let t=0;t<O;t++)rt(t,(t,n)=>e.add(t,n));return function(t,{maxClusters:e=48,newClusterCost:n=320}={}){const o=[];for(let e=0;e<t.contexts;e++)t.totals[e]>0&&o.push(e);o.sort((e,n)=>t.totals[n]-t.totals[e]);const r=[];for(const i of o){const o=t.tokens([i],G),s=R(o);let c=-1,a=1/0;for(let t=0;t<r.length;t++){const e=r[t],n=new Uint32Array(64);for(let t=0;t<64;t++)n[t]=e.freqs[t]+o[t];const i=R(n)-e.bits-s;i<a&&(a=i,c=t)}if(c<0||r.length<e&&a>n)r.push({contexts:[i],freqs:o,bits:s});else{const t=r[c];t.contexts.push(i);for(let e=0;e<64;e++)t.freqs[e]+=o[e];t.bits=R(t.freqs)}}r.length||r.push({contexts:[],freqs:new Uint32Array(64),bits:0});const i=new Uint8Array(t.contexts);let s=2*t.contexts;const c=r.map((e,n)=>{for(const t of e.contexts)i[t]=n;let o=X[0],r=1/0;for(const n of X){const i=t.cost(e.contexts,n);i<r&&(r=i,o=n)}const c=u(t.tokens(e.contexts,o,128));return s+=r+3*c.alphabetSize+40,{config:o,code:c}});return{contextMap:i,histograms:c,write:(t,e,n)=>{const o=c[i[e]];M(t,o.code,o.config,n)},bits:s}}(e)};let st=it(et);if(tt>0){const t=et,e=it(F(Z,0,f));e.bits<=st.bits?st=e:et=t}const ct=Array.from({length:D?1:2+T+O},()=>null),at=t=>ct[D?0:t]||(ct[D?0:t]=new e(4096)),ft=at(0);ft.write(1,0);const lt=(t,e)=>{const n=t*E,r=e*E,i=Math.min(E,o-n),c=Math.min(E,s-r),f=new Int32Array(i*c);for(let t=0;t<c;t++)for(let e=0;e<i;e++)f[t*i+e]=a[4*((r+t)*o+n+e)+3];return{w:i,h:c,data:f}};for(let t=0;t<3;t++)ft.write(16,i(w[t].quant[0]/H[t]/(2040*l)*128));ft.write(2,3),ft.write(16,57343),ft.write(2,1),ft.write(5,0),ft.write(1,0),ft.write(4,0),ft.write(4,et.thresholds.length);for(const t of et.thresholds)ft.writeU32([[4,0],[8,16],[16,272],[32,65808]],n(t));ft.write(4,0),ft.write(4,0),x(ft,et.map),ft.write(1,0),ft.write(2,0),ft.write(16,0),ft.write(16,0),ft.write(8,128),ft.write(8,128),ft.write(1,0),a&&K(ft,[D?lt(0,0):{w:0,h:0,data:new Int32Array(0)}],v.gradient);for(let t=0;t<T;t++){const e=at(1+t),n=t%P*256,o=256*(t/P|0),i=Math.min(256,S-n),s=Math.min(256,C-o);e.write(2,0),K(e,[1,0,2].map(t=>{const e=i>>y(t),r=s>>U(t),c=new Int32Array(e*r),a=H[t],f=j[t];for(let i=0;i<r;i++)for(let r=0;r<e;r++){const s=N(t,(n>>y(t))+r,(o>>U(t))+i);c[i*e+r]=s<0?0:w[t].coeffs[s]*a+f}return{w:e,h:r,data:c}}),v.gradient);const c=i*s,a=r(c);a&&e.write(a,c-1);const f=i+7>>3,l=s+7>>3;K(e,[{w:f,h:l,data:new Int32Array(f*l)},{w:f,h:l,data:new Int32Array(f*l)},{w:c,h:2,data:new Int32Array(2*c)},{w:i,h:s,data:new Int32Array(c)}],v.zero)}const ht=at(1+T);ht.write(1,0),ht.write(3,7),ht.write(16,i(1/(2040*l))),K(ht,[0,1,2].map(t=>{const e=new Int32Array(64),n=w[t].quant;for(let t=0;t<8;t++)for(let o=0;o<8;o++)e[8*o+t]=n[8*t+o];return{w:8,h:8,data:e}}),v.gradient);for(let t=1;t<17;t++)ht.write(3,0);O>1&&ht.write(r(O),0),ht.write(2,2),A(ht,{contextMap:st.contextMap,histograms:st.histograms});for(let t=0;t<O;t++){const e=at(2+T+t);rt(t,(t,n)=>st.write(e,t,n)),a&&!D&&K(e,[lt(t%k,Math.floor(t/k))],v.gradient)}return function(t,e){const n=e.map(t=>t.length);!function(t,e){t.write(1,0),t.zeroPadToByte();for(const n of e){let e=0;for(;e<3&&n>=z[e+1];)e++;t.write(2,e),t.write(L[e]-2,n-z[e])}t.zeroPadToByte()}(t,n);const o=t.finish(),r=new Uint8Array(o.length+n.reduce((t,e)=>t+e,0));r.set(o);let i=o.length;for(const t of e)r.set(t,i),i+=t.length;return r}(nt,ct.map(t=>{const n=t||new e(16);return n.zeroPadToByte(),n.finish()}))}(o)}const V=(t,e)=>Object.assign(new Error(e),{code:t});function $(e){if(!(e instanceof Uint8Array&&e.length))throw V("JXL_INPUT","A JPEG is a non-empty Uint8Array.");if(e.length>t.bytes)throw V("JXL_SIZE","A JPEG is at most 16 MiB.");return function(t){try{return t()}catch(t){if(t instanceof RangeError&&!t.code)throw V("JXL_MEMORY","Not enough memory for this picture.");throw t}}(()=>{const n=T(e);!function(e,n){if(!Number.isInteger(e)||!Number.isInteger(n)||e<1||n<1)throw V("JXL_INPUT","A picture is at least one pixel wide and high, in whole pixels.");if(e>t.edge||n>t.edge)throw V("JXL_DIMENSIONS","A picture is at most "+t.edge+" pixels on a side.");if(e*n>t.pixels)throw V("JXL_DIMENSIONS","A picture is at most "+t.pixels.toLocaleString("en-US")+" pixels.")}(n.width,n.height);const o=function(e){if(e.length>t.bytes)throw V("JXL_SIZE","The encoded picture exceeds 16 MiB.");return e}(Q(e,n)),r=n.orientation>=5;return{bytes:o,width:r?n.height:n.width,height:r?n.width:n.height,orientation:n.orientation}})}export{$ as transcode,t as LIMITS};