rapier-jxl 1.1.21 → 2.0.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/AGENTS.md +14 -10
- package/ENCODER-COMPARISON.md +6 -6
- package/README.md +75 -38
- package/admit.mjs +56 -0
- package/bits.mjs +30 -10
- package/effort.d.mts +11 -0
- package/effort.min.mjs +2 -0
- package/effort.mjs +217 -0
- package/entropy.mjs +16 -1
- package/frame.mjs +16 -9
- package/index.d.mts +13 -17
- package/index.mjs +30 -83
- package/jfif.mjs +407 -0
- package/jpeg.d.mts +14 -2
- package/jpeg.min.mjs +2 -2
- package/jpeg.mjs +34 -377
- package/lossless.mjs +33 -21
- package/lossy.mjs +41 -16
- package/package.json +9 -8
- package/photo.d.mts +6 -2
- package/photo.min.mjs +2 -2
- package/photo.mjs +73 -42
- package/public/examples/browser.html +4 -2
- package/public/examples/worker.mjs +34 -9
- package/rapier-jxl.min.mjs +2 -2
- package/sizes.json +179 -78
- package/vardct.mjs +30 -10
- package/weighted.mjs +79 -0
- package/writer.mjs +17 -0
- package/lossless.d.mts +0 -3
- package/lossless.min.mjs +0 -2
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
|
|
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
|
|
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, {
|
|
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
|
-
|
|
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 + (
|
|
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 <=
|
|
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 *
|
|
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 *
|
|
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(
|
|
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
|
-
|
|
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": "
|
|
4
|
-
"description": "A JPEG XL encoder in pure JavaScript
|
|
3
|
+
"version": "2.0.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,19 @@
|
|
|
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.
|
|
20
|
+
"default": "./jpeg.mjs"
|
|
25
21
|
},
|
|
26
22
|
"./photo": {
|
|
27
23
|
"types": "./photo.d.mts",
|
|
28
|
-
"default": "./photo.
|
|
24
|
+
"default": "./photo.mjs"
|
|
25
|
+
},
|
|
26
|
+
"./effort": {
|
|
27
|
+
"types": "./effort.d.mts",
|
|
28
|
+
"default": "./effort.mjs"
|
|
29
29
|
},
|
|
30
|
+
"./writer": "./writer.mjs",
|
|
30
31
|
"./package.json": "./package.json"
|
|
31
32
|
},
|
|
32
33
|
"files": [
|
package/photo.d.mts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
import type {Pixels, EncodeOptions, Limits} from './index.mjs';
|
|
2
|
+
import type {Pixels, EncodeOptions, Job, Limits} from './index.mjs';
|
|
3
|
+
export type {Pixels, Limits, EncodeOptions, ErrorCode, EncoderError, Job} from './index.mjs';
|
|
4
|
+
/** This door's limits, by its memory: the 16 MiB codestream, 40 million pixels, 16,384 on a side. */
|
|
3
5
|
export const LIMITS: Limits;
|
|
4
6
|
/** Photographs: DCT8 coefficients with exact alpha; quality 90 by default, 100 is exact. */
|
|
5
|
-
export function
|
|
7
|
+
export function encodePhoto(data: Pixels, width: number, height: number, options?: EncodeOptions): Uint8Array;
|
|
8
|
+
/** encodePhoto's work and bytes as steps; the call is admitted at once. */
|
|
9
|
+
export function encodePhotoSteps(data: Pixels, width: number, height: number, options?: EncodeOptions): Job;
|
package/photo.min.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/*! Rapier JXL
|
|
2
|
-
function t(t){return t>=0?2*t:2*-t-1}function n(t){return 31-Math.clz32(t)}function o(t){return t<=1?0:32-Math.clz32(t-1)}function r(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=Math.floor(Math.log2(t)),r=t/2**o-1;o<-14&&(r=t/2**-14,o=-15);let e=Math.round(1024*r);return 1024===e&&(e=0,o++),n|o+15<<10|e}function e(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),t:n,l:null,r:null})).sort((t,n)=>t.w-n.w||t.t-n.t),c=[];let f=0,i=0;const l=()=>f<s.length&&(i>=c.length||s[f].w<=c[i].w)?s[f++]:c[i++];for(;s.length-f+(c.length-i)>1;){const t=l(),n=l();c.push({w:t.w+n.w,t:-1,l:t,r:n})}const a=[[l(),0]];let h=0;for(;a.length;){const[t,n]=a.pop();t.t>=0?(o[t.t]=n,n>h&&(h=n)):a.push([t.l,n+1],[t.r,n+1])}if(h<=n)return o}}function s(t,n,o,r,e,s){const c=[];for(;c.push(r&e),r>>=s;)r--;for(let r=c.length-1;r>=0;r--)t.push(o),n.push(c[r])}function c(t,n){let o=0;for(let r=0;r<n;r++)o=o<<1|1&t,t>>=1;return o}function f(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]=c(o[t[n]]++,t[n]));return r}function i(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,s=o<=4?new Uint8Array(r):e(t,15).subarray(0,r);let c=0;if(o<=4){n.sort((n,o)=>t[o]-t[n]||n-o),c=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]:c?[1,2,3,3]:[2,2,2,2];n.forEach((t,n)=>{s[t]=r[n]}),n.sort((t,n)=>s[t]-s[n]||t-n)}return{o:r,i:s,u:f(s),M:o&&o<=4?n:null,p:c}}function l(t,o){if(o.o<=1)return;if(o.M){const r=n(o.o-1)+1;t.write(2,1),t.write(2,o.M.length-1);for(const n of o.M)t.write(r,n);return void(4===o.M.length&&t.write(1,o.p))}const r=o.i,c=[],i=[];let l=r.length-1;for(;l>0&&!r[l];)l--;let a=8;for(let t=0;t<=l;){const n=r[t];let o=1;for(;t+o<=l&&r[t+o]===n;)o++;let e=o;if(n&&n!==a&&(c.push(n),i.push(0),e--,a=n),e<3)for(let t=0;t<e;t++)c.push(n),i.push(0);else s(c,i,n?16:17,e-3,n?3:7,n?2:3);t+=o}const h=new Uint32Array(18);for(const t of c)h[t]++;let u=0;for(const t of h)t&&u++;1===u&&(h[c[0]?0:1]=h[c[0]]);const w=e(h,5),M=f(w);let y=18;for(;y>0&&!w[k[y-1]];)y--;t.write(2,0);for(let n=0;n<y;n++){const o=w[k[n]];t.write(z[o],B[o])}for(let n=0;n<c.length;n++){const o=c[n];t.write(w[o],M[o]),16===o?t.write(2,i[n]):17===o&&t.write(3,i[n])}}function a(t,n=0,o=0){return{split:t,A:n,m:o,U:1<<t}}function h(t,n,r=15){t.write(o(r+1),n.split),n.split!==r&&(t.write(o(n.split+1),n.A),t.write(o(n.split-n.A+1),n.m))}function u(t,o,r){if(o<t.U)return r[0]=o,r[1]=0,void(r[2]=0);const e=n(o),s=o-(1<<e),c=e-t.A-t.m;r[0]=t.U+(e-t.split<<t.A+t.m|s>>e-t.A<<t.m|s&(1<<t.m)-1),r[1]=c,r[2]=o>>t.m&(1<<c)-1}function w(t,n,o,r=0){u(t,n,F),o[r+F[0]]++}function M(t,n,o,r,e=0){u(o,r,F);const s=e+F[0];t.write(n.i[s],n.u[s]),F[1]&&t.write(F[1],F[2])}function y(t,n){let r=0;for(const t of n)r=Math.max(r,t);const e=r?o(r+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),c=a(4),f=n.length>=64,l=new Uint32Array(f?257:r+1),h=[];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)h.push([n[t],o-1]),l[n[t]]++,w(c,o-1-7,l,224),t+=o;else{for(let r=0;r<o;r++)h.push([n[t],0]),l[n[t]]++;t+=o}}const u=i(l);if(!f){p(t,{I:new Uint8Array(1),J:[{config:s,code:u}]});for(const[n]of h)M(t,u,s,n);return}const y=new Uint32Array(1);y[0]=1,p(t,{L:{T:224,minLength:7,X:c},I:new Uint8Array([1,0]),J:[{config:a(0),code:i(y)},{config:s,code:u}]});for(const[n,o]of h)M(t,u,s,n),o&&M(t,u,c,o-7,224)}function p(t,{L:o=null,I:r,J:e}){t.write(1,o?1:0),o&&(t.G([[0,224],[0,512],[0,4096],[15,8]],o.T),t.G([[0,3],[0,4],[2,5],[8,9]],o.minLength),h(t,o.X,8)),r.length>1&&y(t,r),t.write(1,1);for(const n of e)h(t,n.config,15);for(const o of e){const r=o.code.o-1;if(r){const o=n(r);t.write(1,1),t.write(4,o),t.write(o,r-(1<<o))}else t.write(1,0)}for(const n of e)l(t,n.code)}function A(t,n=0,o=1){return{C:t,offset:n,O:o,context:-1}}function d(t){return function(t,n){const o=(r,e)=>{if(r===e)return n?t[r].N:t[r];const s=r+e>>1;return function(t,n,o,r){return{P:t,S:n,left:o,right:r}}(n,n?t[s]._:s,o(s+1,e),o(r,s))};return o(0,t.length-1)}(t,0)}function g(n,o){const r=[o],e=[],s=[];for(const n of r){if(n.left){e.push(n.P+1,t(n.S)),r.push(n.left,n.right);continue}n.context=s.length,s.push(n);const o=1===n.O?0:31-Math.clz32(n.O&-n.O);e.push(0,n.C,t(n.offset),o,(n.O>>o)-1)}const c=new Uint32Array(64);for(const t of e)w(Q,t,c);const f=i(c);p(n,{I:new Uint8Array(6),J:[{config:Q,code:f}]});for(const t of e)M(n,f,Q,t);return s}function m(t,n){const o=[[3,0],[6,8],[10,72],[13,1096]];if("rct"===n.type)t.write(2,0),t.G(o,n.q),t.G([[0,6],[2,0],[4,2],[6,10]],n.j??6);else if("palette"===n.type)t.write(2,1),t.G(o,n.q),t.G([[0,1],[0,3],[0,4],[13,1]],n.k),t.G([[8,0],[10,256],[12,1280],[16,5376]],n.B),t.G([[0,0],[8,1],[10,257],[16,1281]],n.F??0),t.write(4,n.C??0);else{if("squeeze"!==n.type)throw new Error("unknown transform "+n.type);{t.write(2,2);const r=n.R??[];t.G([[0,0],[4,1],[6,9],[8,41]],r.length);for(const n of r)t.write(1,n.Z?1:0),t.write(1,n.D?1:0),t.G(o,n.q),t.G([[0,1],[0,2],[0,3],[4,4]],n.k)}}}function b(t,{Y:n=!0,H:o=[]}={}){t.write(1,n?1:0),t.write(1,1),t.G([[0,0],[0,1],[4,2],[8,18]],o.length);for(const n of o)m(t,n)}function U(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 c=[{config:Q,code:i(s)}],f=Math.max(...e);for(let t=0;t<f;t++)c.push({config:Q,code:i(o[t])});return p(t,{L:Y,I:e,J:c}),c}function x(n,o,r,e,s,c){const f=c.C,i=c.offset,l=c.O,a=Q,h=D;let u=0;const y=(t,r=a,e=0)=>{n?M(n,o,r,t,e):w(r,t,o,e)},p=()=>{if(u){if(u>=8)y(0),y(u-7-1,h,224);else for(let t=0;t<u;t++)y(0);u=0}},A=(n,o)=>{const e=r[n];let s;1===l?s=e-o-i:(s=Math.round((e-o-i)/l),r[n]=o+i+s*l),0!==s?(p(),y(t(s))):u++};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 c=o?r[n-1]:t?r[n-e]:0,i=t?r[n-e]:c;if(1===f)s=c;else if(2===f)s=i;else if(3===f)s=(c+i)/2|0;else{const l=c+i-(o&&t?r[n-e-1]:c);if(5===f){const t=c<i?c:i,n=c<i?i:c;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-c)<Math.abs(l-i)?c:i}}}A(n,s)}p()}function I(t,n){t.G([[9,1],[13,1],[18,1],[30,1]],n)}function E(t,n,o,r,e,{xyb:s=!1,orientation:c=1}={}){t.write(16,2815),t.write(1,0),I(t,o),t.write(3,0),I(t,n),t.write(1,0);const f=1!==c;t.write(1,f?1:0),f&&t.write(6,c-1),t.write(4,8),e?t.write(3,5):t.write(2,0),t.write(1,s?1:0),3===r?t.write(1,1):t.write(15,14100),f&&t.write(1,1),t.write(2,0),t.write(1,1),t.K()}function J(t,n){t.write(n?7:5,0),t.write(11,1)}function L(t,n){const o=n.map(t=>t.length);!function(t,n){t.write(1,0),t.K();for(const o of n)t.G([[10,0],[14,1024],[22,17408],[30,4211712]],o);t.K()}(t,o);const r=t.finish(),e=new Uint8Array(r.length+o.reduce((t,n)=>t+n,0));e.set(r);let s=r.length;for(const t of n)e.set(t,s),s+=t.length;return e}function T(t,n,o,{palette:r=!0}={}){const e=n*o;let s=!0,c=!0;for(let n=0,o=0;o<e&&(255===t[n+3]||(s=!1,c))&&(t[n]===t[n+1]&&t[n]===t[n+2]||(c=!1,s));o++,n+=4);const f=c?1:3,i=!s,l=f+(i?1:0),a=n=>c?i?t[n]+256*t[n+3]:t[n]:t[n]+256*t[n+1]+65536*t[n+2]+(i?16777216*t[n+3]:0),h=new Map;let u=null;for(let t=0,n=0;r&&n<e;n++,t+=4){const n=a(t);if(0!==n&&!h.has(n)&&(h.set(n,1),h.size>=2048))break}if(r&&h.size<2048){const t=[...h.keys()],n=(t,n)=>Math.floor(t/256**n)%256,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)),u={colours:[0,...t],index:r,key:a,byte:n}}return{colour:f,alpha:i,channels:l,palette:u}}function X(t,n,o,r){const{channels:e,palette:s}=r,c=function(t,n){const o=Math.ceil(t/H),r=Math.ceil(n/H);return{V:o,W:r,$:Math.ceil(t/2048),tt:Math.ceil(n/2048),nt:1===o&&1===r}}(n,o),f=s?2:e,a=Array.from({length:f},()=>A(Z)),h=d(a),u=Array.from({length:f},()=>new Int16Array(65536)),w=(o,r,c,f)=>{for(let i=0;i<f;i++){let f=4*((r+i)*n+o),l=i*c;for(let n=0;n<c;n++,f+=4,l++)if(s)u[0][l]=s.index.get(s.key(f));else if(e>=3){const n=t[f],o=t[f+1],r=t[f+2],s=n-r,c=r+(s>>1),i=o-c;u[0][l]=c+(i>>1),u[1][l]=s,u[2][l]=i,4===e&&(u[3][l]=t[f+3])}else u[0][l]=t[f],2===e&&(u[1][l]=t[f+3])}};let M=null;if(s){const t=s.colours.length;M=new Int16Array(t*e),s.colours.forEach((n,o)=>{for(let r=0;r<e;r++)M[r*t+o]=s.byte(n,r)})}const y=t=>a[s?c.nt?1:0:t],p=s?1:e,m=t=>{for(let r=0;r<c.W;r++)for(let e=0;e<c.V;e++){const s=e*H,f=r*H,i=Math.min(H,n-s),l=Math.min(H,o-f);w(s,f,i,l),t(r*c.V+e,i,l)}};if(!s){const t=t=>{const n=i(t),o=new j(128);l(o,n);let r=o.ot;for(let o=0;o<t.length;o++)t[o]&&(r+=t[o]*(n.i[o]+Math.max(0,o<224?o-1:o-236)));return r},r=Math.min(n,32),s=Math.min(o,32),c=[Z,R].map(t=>({C:t,rt:a.map(()=>new Uint32Array(257))}));for(const t of[0,.5,1]){w(Math.floor((n-r)*t),Math.floor((o-s)*t),r,s);for(const t of c)for(let n=0;n<e;n++)x(null,t.rt[n],u[n],r,s,A(t.C))}a.forEach((n,o)=>{const r=c.map(n=>t(n.rt[o])),e=r[1]<r[0]?1:0;n.C=c[e].C})}const I=s?[{type:"palette",q:0,k:e,B:s.colours.length}]:e>=3?[{type:"rct",q:0,j:6}]:[],T=a.map(()=>new Uint32Array(257)),X=t=>T[a.indexOf(t)];M&&x(null,X(a[0]),M,s.colours.length,e,a[0]),m((t,n,o)=>{for(let t=0;t<p;t++)x(null,X(y(t)),u[t],n,o,y(t))});const G=new j(256);E(G,n,o,r.colour,r.alpha),function(t,{alpha:n}){t.write(9,8),n&&t.write(2,0),t.write(2,1),J(t,n)}(G,{alpha:r.alpha});const C=new j(4096);C.write(1,1),C.write(1,1);const O=g(C,h),N=U(C,O,T,t=>a.indexOf(t)),P=t=>N[a.indexOf(t)+1].code;b(C,{Y:!0,H:I}),M&&x(C,P(a[0]),M,s.colours.length,e,a[0]);const S=[];if(c.nt)m((t,n,o)=>{for(let t=0;t<p;t++)x(C,P(y(t)),u[t],n,o,y(t))}),S.push(C.finish());else{S.push(C.finish());for(let t=0;t<c.$*c.tt+1;t++)S.push(new Uint8Array(0));m((t,n,o)=>{const r=new j(n*o*p+64);b(r,{Y:!0,H:[]});for(let t=0;t<p;t++)x(r,P(y(t)),u[t],n,o,y(t));S.push(r.finish())})}return L(G,S)}function G(t,n="JXL_JPEG"){return Object.assign(new Error("JPEG: "+t),{code:n})}function C(t,n){return u(t,n,$),$[0]}function O(t){let n=0,o=0;for(const o of t)n+=o;for(const r of t)r&&(o+=r*Math.log2(n/r));return o}function N(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,c=e/(n+1);for(let t=0;t<2048&&r.length<n;t++)s+=o[t],s>c&&(r.push(t-1025),c=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<tt;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 c=0;for(const t of s)c=Math.max(c,t+1);return{thresholds:r,et:e,map:s,st:c,ct:t=>{let n=0;for(const o of r)t>o&&n++;return n},ft:(t,n)=>s[(t<2?1^t:2)*tt*e+n],it:(t,n)=>(Math.min(t,64)<8?Math.min(t,64):4+(Math.min(t,64)>>1))*c+n,lt:t=>37*c+458*t,ht:495*c}}function P(t,n,o){const r=n.map(()=>A(o)),e=r.map(()=>new Uint32Array(257));n.forEach((t,n)=>x(null,e[n],t.data,t.w,t.h,r[n])),b(t,{Y:!1,H:[]});const s=g(t,d(r)),c=U(t,s,e,t=>r.indexOf(t));n.forEach((n,o)=>x(t,c[o+1].code,n.data,n.w,n.h,r[o]))}function S(t,n,o,r){let e=0;return e+=t[n]*ct[r],e+=t[n+o]*ct[r+1],e+=t[n+2*o]*ct[r+2],e+=t[n+3*o]*ct[r+3],e+=t[n+4*o]*ct[r+4],e+=t[n+5*o]*ct[r+5],e+=t[n+6*o]*ct[r+6],e+=t[n+7*o]*ct[r+7],e}function _(n,e,s,c={}){if(null===c||"object"!=typeof c)throw st("JXL_INPUT","Options are an object: {quality}.");const f=void 0===c.quality?90:c.quality;if("number"!=typeof f||!Number.isFinite(f)||f<1||f>100)throw st("JXL_INPUT","Quality is a number from 1 to 100.");if(!Number.isInteger(e)||!Number.isInteger(s)||e<1||s<1)throw st("JXL_INPUT","A picture has positive whole pixel dimensions.");if(e>v.edge||s>v.edge||e*s>v.pixels)throw st("JXL_DIMENSIONS","The picture exceeds the JPEG XL pixel limits.");if(!(n instanceof Uint8Array||n instanceof Uint8ClampedArray)||n.length!==e*s*4)throw st("JXL_INPUT","Pixels are width * height * 4 straight RGBA bytes.");try{const c=f>=100?function(t,n,o,{shape:r=T(t,n,o)}={}){let e,s;for(const c of r.palette?[r,{...r,palette:null}]:[r])try{const r=X(t,n,o,c);(!e||r.length<e.length)&&(e=r)}catch(t){if("JXL_SIZE"!==t.code)throw t;s=t}if(!e)throw s;return e}(n,e,s):function(n){const{width:e,height:s,components:c,alpha:f}=n,l=1===c.length,a=n.quantScale||1,h=n.ycbcr||l,u=(l?[0,0,0]:h?[1,0,2]:[0,1,2]).map(t=>c[t]),w=t=>1===t?0:2===t?1:-1,A=u.map(t=>w(t.h)),d=u.map(t=>w(t.v));if(A.includes(-1)||d.includes(-1))throw G("sampling factors other than one or two");const g=Math.max(...A),m=Math.max(...d),b=t=>g-A[t],U=t=>m-d[t];if(!h&&(g||m))throw G("an RGB JPEG with subsampling");const x=Math.ceil(e/(8<<g))<<g,I=Math.ceil(s/(8<<m))<<m,T=Math.ceil(e/H),X=Math.ceil(s/H),C=T*X,S=Math.ceil(x/256),_=S*Math.ceil(I/256),v=1===C,q=(t,n,o)=>{const r=u[t];return l&&1!==t?-1:64*(o*r.stride+n)},k=t=>{let n=1024;for(;t%n;)n>>=1;return n},z=u.map(t=>h?1:t.quant[0]/k(t.quant[0])),B=u.map(t=>h?0:1024/k(t.quant[0])),F=new Int32Array(x*I);for(let t=0;t<I;t++)for(let n=0;n<x;n++)F[t*x+n]=u[1].coeffs[q(1,n,t)]*z[1]+B[1];const R=u[1].quant,D=(R[8]+R[16]+R[24]+R[32]+R[40])/a,Y=x*I,Q=Y<256?0:Math.max(1,Math.min(7,o(Y)-o(D)-7));let $=N(F,Q,l);const tt=new j(256);if(E(tt,e,s,l?1:3,!!f,{orientation:n.orientation}),tt.write(1,0),tt.write(2,0),tt.write(1,0),tt.write(2,2),tt.write(8,111),tt.write(1,h?1:0),h)for(let t=0;t<3;t++)tt.write(2,0===A[t]&&0===d[t]?0:1===A[t]&&1===d[t]?1:1===A[t]?2:3);tt.write(2,0),f&&tt.write(2,0),J(tt,f);const nt=[0,1,2].map(()=>new Int32Array(1024)),ot=(n,o)=>{const r=n%T*32,e=32*(n/T|0),s=Math.min(32,x-r),c=Math.min(32,I-e);for(let n=0;n<c;n++)for(let c=0;c<s;c++)for(const s of[1,0,2]){const f=b(s),i=U(s),l=c>>f,a=n>>i;if(l<<f!==c||a<<i!==n)continue;const h=q(s,(r>>f)+l,(e>>i)+a),w=u[s].coeffs;let M=0;if(h>=0)for(let t=1;t<64;t++)w[h+t]&&M++;const y=nt[s],p=0===l?0===a?32:y[32*(a-1)]:0===a?y[32*a+l-1]:y[32*(a-1)+l]+y[32*a+l-1]+1>>1;y[32*a+l]=M;const A=$.ft(s,$.ct(F[(e+n)*x+r+c]));o($.it(p,A),M);const d=$.lt(A);let g=M>4?0:1,m=M;for(let n=1;n<64&&m;n++){const r=w[h+rt[n]];o(d+et(m,n,g),t(r)),g=r?1:0,m-=g}}},st=t=>{$=t;const n=new W(t.ht);for(let t=0;t<C;t++)ot(t,(t,o)=>n.add(t,o));return function(t,{maxClusters:n=48,newClusterCost:o=320}={}){const r=[];for(let n=0;n<t.ht;n++)t.ut[n]>0&&r.push(n);r.sort((n,o)=>t.ut[o]-t.ut[n]);const e=[];for(const s of r){const r=t.wt([s],V),c=O(r);let f=-1,i=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=O(o)-n.Mt-c;s<i&&(i=s,f=t)}if(f<0||e.length<n&&i>o)e.push({ht:[s],rt:r,Mt:c});else{const t=e[f];t.ht.push(s);for(let n=0;n<64;n++)t.rt[n]+=r[n];t.Mt=O(t.rt)}}e.length||e.push({ht:[],rt:new Uint32Array(64),Mt:0});const s=new Uint8Array(t.ht);let c=2*t.ht;const f=e.map((n,o)=>{for(const t of n.ht)s[t]=o;let r=K[0],e=1/0;for(const o of K){const s=t.yt(n.ht,o);s<e&&(e=s,r=o)}const f=i(t.wt(n.ht,r,128));return c+=e+3*f.o+40,{config:r,code:f}});return{I:s,J:f,write:(t,n,o)=>{const r=f[s[n]];M(t,r.code,r.config,o)},Mt:c}}(n)};let ct=st($);if(Q>0){const t=$,n=st(N(F,0,l));n.Mt<=ct.Mt?ct=n:$=t}const ft=Array.from({length:v?1:2+_+C},()=>null),it=t=>ft[v?0:t]||(ft[v?0:t]=new j(4096)),lt=it(0);lt.write(1,0);const at=(t,n)=>{const o=t*H,r=n*H,c=Math.min(H,e-o),i=Math.min(H,s-r),l=new Int32Array(c*i);for(let t=0;t<i;t++)for(let n=0;n<c;n++)l[t*c+n]=f[4*((r+t)*e+o+n)+3];return{w:c,h:i,data:l}};for(let t=0;t<3;t++)lt.write(16,r(u[t].quant[0]/z[t]/(2040*a)*128));lt.write(2,3),lt.write(16,57343),lt.write(2,1),lt.write(5,0),lt.write(1,0),lt.write(4,0),lt.write(4,$.thresholds.length);for(const n of $.thresholds)lt.G([[4,0],[8,16],[16,272],[32,65808]],t(n));lt.write(4,0),lt.write(4,0),y(lt,$.map),lt.write(1,0),lt.write(2,0),lt.write(16,0),lt.write(16,0),lt.write(8,128),lt.write(8,128),lt.write(1,0),f&&P(lt,[v?at(0,0):{w:0,h:0,data:new Int32Array(0)}],Z);for(let t=0;t<_;t++){const n=it(1+t),r=t%S*256,e=256*(t/S|0),s=Math.min(256,x-r),c=Math.min(256,I-e);n.write(2,0),P(n,[1,0,2].map(t=>{const n=s>>b(t),o=c>>U(t),f=new Int32Array(n*o),i=z[t],l=B[t];for(let s=0;s<o;s++)for(let o=0;o<n;o++){const c=q(t,(r>>b(t))+o,(e>>U(t))+s);f[s*n+o]=c<0?0:u[t].coeffs[c]*i+l}return{w:n,h:o,data:f}}),Z);const f=s*c,i=o(f);i&&n.write(i,f-1);const l=s+7>>3,a=c+7>>3;P(n,[{w:l,h:a,data:new Int32Array(l*a)},{w:l,h:a,data:new Int32Array(l*a)},{w:f,h:2,data:new Int32Array(2*f)},{w:s,h:c,data:new Int32Array(f)}],0)}const ht=it(1+_);ht.write(1,0),ht.write(3,7),ht.write(16,r(1/(2040*a))),P(ht,[0,1,2].map(t=>{const n=new Int32Array(64),o=u[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}}),Z);for(let t=1;t<17;t++)ht.write(3,0);C>1&&ht.write(o(C),0),ht.write(2,2),p(ht,{I:ct.I,J:ct.J});for(let t=0;t<C;t++){const n=it(2+_+t);ot(t,(t,o)=>ct.write(n,t,o)),f&&!v&&P(n,[at(t%T,Math.floor(t/T))],Z)}return L(tt,function(t){return t.map(t=>t?t.finish():new Uint8Array(0))}(ft))}(function(t,n,o,r){const e=Math.ceil(n/8),s=Math.ceil(o/8),c=r>=30?.1+.09*(100-r):53/3e3*r*r-1.15*r+25,f=[0,1,2].map(t=>({h:1,v:1,stride:e,rows:s,quant:Int32Array.from(ft,(n,o)=>Math.max(1,Math.round(16*c*(t?8:14)*(o?n:.5)))),coeffs:new Int16Array(e*s*64)})),i=new Float64Array(192),l=new Float64Array(64);for(let r=0;r<s;r++)for(let s=0;s<e;s++){for(let e=0;e<8;e++)for(let c=0;c<8;c++){const f=4*(Math.min(o-1,8*r+e)*n+Math.min(n-1,8*s+c)),l=t[f],a=t[f+1],h=t[f+2],u=8*e+c;i[u]=.299*l+.587*a+.114*h-128,i[64+u]=-.168735892*l-.331264108*a+.5*h,i[128+u]=.5*l-.418687589*a-.081312411*h}for(let t=0;t<3;t++){for(let n=0;n<8;n++)for(let o=0;o<8;o++)l[8*n+o]=S(i,64*t+8*n,1,8*o);const{coeffs:n,quant:o}=f[t],c=64*(r*e+s);for(let t=0;t<8;t++)for(let r=0;r<8;r++){const e=S(l,r,8,8*t),s=8*t+r;n[c+s]=Math.round(16*e/o[s])}}}let a=null;for(let n=3;n<t.length;n+=4)if(255!==t[n]){a=t;break}return{width:n,height:o,components:f,quantScale:16,ycbcr:!0,orientation:1,alpha:a}}(n,e,s,f));if(c.length>v.bytes)throw st("JXL_SIZE","The encoded picture exceeds 16 MiB.");return c}catch(t){if(t instanceof RangeError&&!t.code)throw st("JXL_MEMORY","Not enough memory for this picture.");throw t}}const v=Object.freeze({bytes:16777216,pixels:24e6,edge:16384}),q=Array.from({length:33},(t,n)=>2**n);class j{constructor(t=4096){this.bytes=new Uint8Array(t),this.at=0,this.At=0,this.pending=0}write(t,n){if(t>32||n<0||n>=(q[t]??2**t))throw new Error("bit write out of range: "+t+" bits, "+n);let o=this.At+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.At=o,this.pending=r}G(t,n){for(let o=0;o<4;o++){const[r,e]=t[o];if(n>=e&&n-e<2**r)return this.write(2,o),void(r&&this.write(r,n-e))}throw new Error("U32 value out of range: "+n)}K(){this.pending&&(this.at+1>=this.bytes.length&&this.grow(),this.bytes[this.at++]=this.At,this.At=0,this.pending=0)}get ot(){return 8*this.at+this.pending}grow(t=0){const n=v.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.At)}finish(){const t=this.at+(this.pending?1:0),n=this.bytes.slice(0,t);return this.pending&&(n[this.at]=this.At),n}}const k=[1,2,3,4,0,5,17,6,16,7,8,9,10,11,12,13,14,15],z=[2,4,3,2,2,4],B=[0,7,3,2,1,15],F=[0,0,0],R=3,Z=5,D=a(4),Y=Object.freeze({T:224,minLength:7,X:D}),Q=a(0),H=256,K=[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)],V=a(4,1,1);class W{constructor(t){this.ht=t,this.small=new Uint32Array(64*t),this.dt=Array.from({length:t},()=>null),this.ut=new Float64Array(t)}add(t,n){if(this.ut[t]++,n<64)return void this.small[64*t+n]++;const o=this.dt[t]||(this.dt[t]=new Map);o.set(n,(o.get(n)||0)+1)}wt(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[C(n,o)]+=this.small[t+o]);const e=this.dt[o];if(e)for(const[t,o]of e)r[C(n,t)]+=o}return r}yt(t,o){let r=O(this.wt(t,o));for(const e of t){const t=64*e;for(let e=o.U;e<64;e++)this.small[t+e]&&(r+=this.small[t+e]*(n(e)-o.A-o.m));const s=this.dt[e];if(s)for(const[t,e]of s)r+=e*(n(t)-o.A-o.m)}return r}}const $=[0,0,0],tt=13,nt=[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],ot=[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],rt=[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].map(t=>(7&t)<<3|t>>3),et=(t,n,o)=>2*(ot[t]+nt[n])+o,st=(t,n)=>Object.assign(new Error(n),{code:t}),ct=Float64Array.from({length:64},(t,n)=>(n<8?Math.SQRT1_2:1)*Math.cos((2*(7&n)+1)*(n>>3)*Math.PI/16)/2),ft=Float64Array.from({length:64},(t,n)=>1.3**Math.max(0,5*Math.hypot(7&n,n>>3)/Math.sqrt(98)-1));export{_ as encodePhotoRGBA,v as LIMITS};
|
|
1
|
+
/*! Rapier JXL 2.0.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,n){t.t([[9,1],[13,1],[18,1],[30,1]],n)}function i(t,n,o,r,e,{xyb:i=!1,orientation:c=1,colorSpace:f}={}){t.write(16,2815),t.write(1,0),s(t,o),t.write(3,0),s(t,n),t.write(1,0);const l=1!==c;t.write(1,l?1:0),l&&t.write(6,c-1),t.write(4,8),e?t.write(3,5):t.write(2,0),t.write(1,i?1:0),3===r&&"display-p3"===f?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 c(t,n){t.write(n?7:5,0),t.write(11,1)}function f(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=new Uint8Array(r.length+o.reduce((t,n)=>t+n,0));e.set(r);let s=r.length;for(const t of n)e.set(t,s),s+=t.length;return e}function l(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 c=0,f=0;const l=()=>c<s.length&&(f>=i.length||s[c].w<=i[f].w)?s[c++]:i[f++];for(;s.length-c+(i.length-f)>1;){const t=l(),n=l();i.push({w:t.w+n.w,i:-1,l:t,r:n})}const a=[[l(),0]];let h=0;for(;a.length;){const[t,n]=a.pop();t.i>=0?(o[t.i]=n,n>h&&(h=n)):a.push([t.l,n+1],[t.r,n+1])}if(h<=n)return o}}function a(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 h(t,n){let o=0;for(let r=0;r<n;r++)o=o<<1|1&t,t>>=1;return o}function u(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]=h(o[t[n]]++,t[n]));return r}function w(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):l(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,p:e,M:u(e),A:o&&o<=4?n:null,m:s}}function y(t,n){if(n.u<=1)return;if(n.A){const r=o(n.u-1)+1;t.write(2,1),t.write(2,n.A.length-1);for(const o of n.A)t.write(r,o);return void(4===n.A.length&&t.write(1,n.m))}const r=n.p,e=[],s=[];let i=r.length-1;for(;i>0&&!r[i];)i--;let c=8;for(let t=0;t<=i;){const n=r[t];let o=1;for(;t+o<=i&&r[t+o]===n;)o++;let f=o;if(n&&n!==c&&(e.push(n),s.push(0),f--,c=n),f<3)for(let t=0;t<f;t++)e.push(n),s.push(0);else a(e,s,n?16:17,f-3,n?3:7,n?2:3);t+=o}const f=new Uint32Array(18);for(const t of e)f[t]++;let h=0;for(const t of f)t&&h++;1===h&&(f[e[0]?0:1]=f[e[0]]);const w=l(f,5),y=u(w);let p=18;for(;p>0&&!w[Z[p-1]];)p--;t.write(2,0);for(let n=0;n<p;n++){const o=w[Z[n]];t.write(Y[o],Q[o])}for(let n=0;n<e.length;n++){const o=e[n];t.write(w[o],y[o]),16===o?t.write(2,s[n]):17===o&&t.write(3,s[n])}}function p(t,n=0,o=0){return{split:t,U:n,I:o,S:1<<t}}function M(t,n,o=15){t.write(r(o+1),n.split),n.split!==o&&(t.write(r(n.split+1),n.U),t.write(r(n.split-n.U+1),n.I))}function d(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.U-t.I;r[0]=t.S+(e-t.split<<t.U+t.I|s>>e-t.U<<t.I|s&(1<<t.I)-1),r[1]=i,r[2]=n>>t.I&(1<<i)-1}function A(t,n,o,r=0){d(t,n,H),o[r+H[0]]++}function g(t,n,o,r,e=0){d(o,r,H);const s=e+H[0];t.write(n.p[s],n.M[s]),H[1]&&t.write(H[1],H[2])}function b(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=p(8),i=p(4),c=n.length>=64,f=new Uint32Array(c?257:o+1),l=[];for(let t=0;t<n.length;){let o=1;for(;c&&t+o<n.length&&n[t+o]===n[t];)o++;if(o>=8)l.push([n[t],o-1]),f[n[t]]++,A(i,o-1-7,f,224),t+=o;else{for(let r=0;r<o;r++)l.push([n[t],0]),f[n[t]]++;t+=o}}const a=w(f);if(!c){m(t,{L:new Uint8Array(1),T:[{config:s,code:a}]});for(const[n]of l)g(t,a,s,n);return}const h=new Uint32Array(1);h[0]=1,m(t,{J:{X:224,minLength:7,N:i},L:new Uint8Array([1,0]),T:[{config:p(0),code:w(h)},{config:s,code:a}]});for(const[n,o]of l)g(t,a,s,n),o&&g(t,a,i,o-7,224)}function m(t,{J:n=null,L:r,T:e}){t.write(1,n?1:0),n&&(t.t([[0,224],[0,512],[0,4096],[15,8]],n.X),t.t([[0,3],[0,4],[2,5],[8,9]],n.minLength),M(t,n.N,8)),r.length>1&&b(t,r),t.write(1,1);for(const n of e)M(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)y(t,n.code)}function U(t,n=0,o=1){return{O:t,offset:n,_:o,context:-1}}function I(t){return function(t,n){const o=(r,e)=>{if(r===e)return n?t[r].C:t[r];const s=r+e>>1;return function(t,n,o,r){return{G:t,P: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 x(t,o){const r=[o],e=[],s=[];for(const t of r){if(t.left){e.push(t.G+1,n(t.P)),r.push(t.left,t.right);continue}t.context=s.length,s.push(t);const o=1===t._?0:31-Math.clz32(t._&-t._);e.push(0,t.O,n(t.offset),o,(t._>>o)-1)}const i=new Uint32Array(64);for(const t of e)A(tt,t,i);const c=w(i);m(t,{L:new Uint8Array(6),T:[{config:tt,code:c}]});for(const n of e)g(t,c,tt,n);return s}function S(t,n){const o=[[3,0],[6,8],[10,72],[13,1096]];if("rct"===n.type)t.write(2,0),t.t(o,n.j),t.t([[0,6],[2,0],[4,2],[6,10]],n.k??6);else if("palette"===n.type)t.write(2,1),t.t(o,n.j),t.t([[0,1],[0,3],[0,4],[13,1]],n.B),t.t([[8,0],[10,256],[12,1280],[16,5376]],n.F),t.t([[0,0],[8,1],[10,257],[16,1281]],n.R??0),t.write(4,n.O??0);else{if("squeeze"!==n.type)throw new Error("unknown transform "+n.type);{t.write(2,2);const r=n.D??[];t.t([[0,0],[4,1],[6,9],[8,41]],r.length);for(const n of r)t.write(1,n.Z?1:0),t.write(1,n.Y?1:0),t.t(o,n.j),t.t([[0,1],[0,2],[0,3],[4,4]],n.B)}}}function E(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)S(t,n)}function L(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:tt,code:w(s)}],c=Math.max(...e);for(let t=0;t<c;t++)i.push({config:tt,code:w(o[t])});return m(t,{J:$,L:e,T:i}),i}function T(t,o,r,e,s,i){const c=i.O,f=i.offset,l=i._,a=tt,h=W;let u=0;const w=(n,r=a,e=0)=>{t?g(t,o,r,n,e):A(r,n,o,e)},y=()=>{if(u){if(u>=8)w(0),w(u-7-1,h,224);else for(let t=0;t<u;t++)w(0);u=0}},p=(t,o)=>{const e=r[t];let s;1===l?s=e-o-f:(s=Math.round((e-o-f)/l),r[t]=o+f+s*l),0!==s?(y(),w(n(s))):u++};for(let t=0,n=0;t<s;t++)for(let o=0;o<e;o++,n++){let s;if(0===c)s=0;else{const i=o?r[n-1]:t?r[n-e]:0,f=t?r[n-e]:i;if(1===c)s=i;else if(2===c)s=f;else if(3===c)s=(i+f)/2|0;else{const l=i+f-(o&&t?r[n-e-1]:i);if(5===c){const t=i<f?i:f,n=i<f?f:i;s=l<t?t:l>n?n:l}else{if(4!==c)throw new Error("predictor "+c+" is not coded here");s=Math.abs(l-i)<Math.abs(l-f)?i:f}}}p(n,s)}y()}function J(t,n){return d(t,n,et),et[0]}function X(t){let n=0;for(t>=2&&t<4294967296&&(n=31-Math.clz32(t),t*=st[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 N(t){let n=0,o=0;for(const o of t)n+=o;for(const r of t)r&&(o+=r*X(n/r));return o}function O(t,n="JXL_JPEG"){return Object.assign(new Error("JPEG: "+t),{code:n})}function _(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<it;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,V:e,map:s,W:i,$:t=>{let n=0;for(const o of r)t>o&&n++;return n},tt:(t,n)=>s[(t<2?1^t:2)*it*e+n],nt:(t,n)=>(Math.min(t,64)<8?Math.min(t,64):4+(Math.min(t,64)>>1))*i+n,ot:t=>37*i+458*t,rt:495*i}}function C(t,n,o){const r=n.map(()=>U(o)),e=r.map(()=>new Uint32Array(257));n.forEach((t,n)=>T(null,e[n],t.data,t.w,t.h,r[n])),E(t,{H:!1,K:[]});const s=x(t,I(r)),i=L(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 G(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 c=i?1:3,f=!s,l=c+(f?1:0),a=n=>i?f?t[n]+256*t[n+3]:t[n]:t[n]+256*t[n+1]+65536*t[n+2]+(f?16777216*t[n+3]:0),h=new Map;let u=null;for(let t=0,n=0;r&&n<e;n++,t+=4){const n=a(t);if(0!==n&&!h.has(n)&&(h.set(n,1),h.size>=2048))break}if(r&&h.size<2048){const t=[...h.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)),u={colours:[0,...t],index:r,key:a,byte:n}}return{colour:c,alpha:f,channels:l,palette:u}}function*P(t,n,o,r,e){const{channels:s,palette:l}=r,a=function(t,n,o=256){const r=Math.ceil(t/o),e=Math.ceil(n/o);return{et:r,st:e,it:Math.ceil(t/(8*o)),ct:Math.ceil(n/(8*o)),ft:1===r&&1===e}}(n,o),h=l?2:s,u=Array.from({length:h},()=>U(V)),p=I(u),M=Array.from({length:h},()=>new Int16Array(65536)),d=(o,r,e,i)=>{for(let c=0;c<i;c++){let i=4*((r+c)*n+o),f=c*e;for(let n=0;n<e;n++,i+=4,f++)if(l)M[0][f]=l.index.get(l.key(i));else if(s>=3){const n=t[i],o=t[i+1],r=t[i+2],e=n-r,c=r+(e>>1),l=o-c;M[0][f]=c+(l>>1),M[1][f]=e,M[2][f]=l,4===s&&(M[3][f]=t[i+3])}else M[0][f]=t[i],2===s&&(M[1][f]=t[i+3])}};let A=null;if(l){const t=l.colours.length;A=new Int16Array(t*s),l.colours.forEach((n,o)=>{for(let r=0;r<s;r++)A[r*t+o]=l.byte(n,r)})}const g=t=>u[l?a.ft?1:0:t],b=l?1:s,m=a.et*a.st,S=2*m,J=(t,r)=>{const e=t%a.et*D,s=(t/a.et|0)*D,i=Math.min(D,n-e),c=Math.min(D,o-s);d(e,s,i,c),r(i,c)};let X=0;if(!l){const t=t=>{const n=w(t),o=new R(128);y(o,n);let r=o.lt;for(let o=0;o<t.length;o++)t[o]&&(r+=t[o]*(n.p[o]+Math.max(0,o<224?o-1:o-236)));return r},r=Math.min(n,32),e=Math.min(o,32),i=[V,K].map(t=>({O:t,ht:u.map(()=>new Uint32Array(257))}));for(const t of[0,.5,1]){d(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.ht[n],M[n],r,e,U(t.O))}u.forEach((n,o)=>{const r=i.map(n=>t(n.ht[o])),e=r[1]<r[0]?1:0;n.O=i[e].O})}const N=l?[{type:"palette",j:0,B:s,F:l.colours.length}]:s>=3?[{type:"rct",j:0,k:6}]:[],O=u.map(()=>new Uint32Array(257)),_=t=>O[u.indexOf(t)];A&&T(null,_(u[0]),A,l.colours.length,s,u[0]);for(let t=0;t<m;t++)J(t,(t,n)=>{for(let o=0;o<b;o++)T(null,_(g(o)),M[o],t,n,g(o))}),yield++X/S;const C=new R(256);i(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),c(t,n)}(C,{alpha:r.alpha});const G=new R(4096);G.write(1,1),G.write(1,1);const P=x(G,p),q=L(G,P,O,t=>u.indexOf(t)),v=t=>q[u.indexOf(t)+1].code;E(G,{H:!0,K:N}),A&&T(G,v(u[0]),A,l.colours.length,s,u[0]);const j=[];if(a.ft)J(0,(t,n)=>{for(let o=0;o<b;o++)T(G,v(g(o)),M[o],t,n,g(o))}),yield 1,j.push(G.finish());else{j.push(G.finish());for(let t=0;t<a.it*a.ct+1;t++)j.push(new Uint8Array(0));for(let t=0;t<m;t++)J(t,(t,n)=>{const o=new R(t*n*b+64);E(o,{H:!0,K:[]});for(let r=0;r<b;r++)T(o,v(g(r)),M[r],t,n,g(r));j.push(o.finish())}),yield++X/S}return f(C,j)}function q(t){try{return t()}catch(t){if(t instanceof RangeError&&!t.code)throw ht("JXL_MEMORY","Not enough memory for this picture.");throw t}}function v(t,n,o,r){let e=0;return e+=t[n]*ut[r],e+=t[n+o]*ut[r+1],e+=t[n+2*o]*ut[r+2],e+=t[n+3*o]*ut[r+3],e+=t[n+4*o]*ut[r+4],e+=t[n+5*o]*ut[r+5],e+=t[n+6*o]*ut[r+6],e+=t[n+7*o]*ut[r+7],e}function j(t,n,o,r){return function(t){let n;for(;!(n=t.next()).done;);return n.value}(k(t,n,o,r))}function k(o,s,l,a){const{quality:h,colorSpace:u}=function(t={},n=100){if(null===t||"object"!=typeof t)throw ht("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 ht("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 ht("JXL_INPUT","The colour space is srgb or display-p3.");return{quality:n,colorSpace:o}}(a,90);return function(t,n,o,r){if(function(t,n,o=z){if(!Number.isInteger(t)||!Number.isInteger(n)||t<1||n<1)throw ht("JXL_INPUT","A picture is at least one pixel wide and high, in whole pixels.");if(t>o.edge||n>o.edge)throw ht("JXL_DIMENSIONS","A picture is at most "+o.edge+" pixels on a side.");if(t*n>o.pixels)throw ht("JXL_DIMENSIONS","A picture is at most "+o.pixels.toLocaleString("en-US")+" pixels.")}(n,o,r),!(t instanceof Uint8Array||t instanceof Uint8ClampedArray))throw ht("JXL_INPUT","Pixels are a Uint8Array or Uint8ClampedArray of RGBA bytes.");if(t.length!==n*o*4)throw ht("JXL_INPUT","Pixels are width * height * 4 bytes: straight (not premultiplied) RGBA, row by row.")}(o,s,l,B),function(t){const n=function*(){let o,r;for(;!(o=q(()=>t.next(n.hurry))).done;)yield r=o.value;return r<1&&(yield 1),n.bytes=function(t){if(t.length>z.bytes)throw ht("JXL_SIZE","The encoded picture exceeds 16 MiB.");return t}(o.value)}();return n.bytes=null,n.hurry=!1,n}(function*(o,s,l,a,h){return a>=100?yield*function*(n,o,r,{shape:e=G(n,o,r),colorSpace:s}={}){let i,c;const f=e.palette?[e,{...e,palette:null}]:[e];for(let e=0;e<f.length;e++)try{const c=yield*t(P(n,o,r,f[e],s),e,f.length);(!i||c.length<i.length)&&(i=c)}catch(t){if("JXL_SIZE"!==t.code)throw t;c=t}if(!i)throw c;return i}(o,s,l,{colorSpace:h}):yield*t(function*(t){const{width:o,height:s,components:l,alpha:a}=t,h=1===l.length,u=t.quantScale||1;let y=1;for(;y/(2040*u)<1/16384;)y*=2;const p=t.ycbcr||h,M=(h?[0,0,0]:p?[1,0,2]:[0,1,2]).map(t=>l[t]),d=t=>1===t?0:2===t?1:-1,A=M.map(t=>d(t.h)),U=M.map(t=>d(t.v));if(A.includes(-1)||U.includes(-1))throw O("sampling factors other than one or two");const I=Math.max(...A),x=Math.max(...U),S=t=>I-A[t],E=t=>x-U[t];if(!p&&(I||x))throw O("an RGB JPEG with subsampling");const L=Math.ceil(o/(8<<I))<<I,T=Math.ceil(s/(8<<x))<<x,J=Math.ceil(o/D),X=Math.ceil(s/D),G=J*X,P=Math.ceil(L/256),q=P*Math.ceil(T/256),v=1===G,j=(t,n,o)=>{const r=M[t];return h&&1!==t?-1:64*(o*r.stride+n)},k=t=>{let n=1024;for(;t%n;)n>>=1;return n},z=M.map(t=>p?1:t.quant[0]/k(t.quant[0])),B=M.map(t=>p?0:1024/k(t.quant[0])),F=new Int32Array(L*T);for(let t=0;t<T;t++)for(let n=0;n<L;n++)F[t*L+n]=M[1].coeffs[j(1,n,t)]*z[1]+B[1];const Z=M[1].quant,Y=(Z[8]+Z[16]+Z[24]+Z[32]+Z[40])/u,Q=L*T,H=Q<256?0:Math.max(1,Math.min(7,r(Q)-r(Y)-7));let K=_(F,H,h);const W=new R(256);if(i(W,o,s,h?1:3,!!a,{orientation:t.orientation,colorSpace:t.colorSpace}),W.write(1,0),W.write(2,0),W.write(1,0),W.write(2,2),W.write(8,111),W.write(1,p?1:0),p)for(let t=0;t<3;t++)W.write(2,0===A[t]&&0===U[t]?0:1===A[t]&&1===U[t]?1:1===A[t]?2:3);W.write(2,0),a&&W.write(2,0),c(W,a);const $=[0,1,2].map(()=>new Int32Array(1024)),tt=(t,o)=>{const r=t%J*32,e=32*(t/J|0),s=Math.min(32,L-r),i=Math.min(32,T-e);for(let t=0;t<i;t++)for(let i=0;i<s;i++)for(const s of[1,0,2]){const c=S(s),f=E(s),l=i>>c,a=t>>f;if(l<<c!==i||a<<f!==t)continue;const h=j(s,(r>>c)+l,(e>>f)+a),u=M[s].coeffs;let w=0;if(h>=0)for(let t=1;t<64;t++)u[h+t]&&w++;const y=$[s],p=0===l?0===a?32:y[32*(a-1)]:0===a?y[32*a+l-1]:y[32*(a-1)+l]+y[32*a+l-1]+1>>1;y[32*a+l]=w;const d=K.tt(s,K.$(F[(e+t)*L+r+i]));o(K.nt(p,d),w);const A=K.ot(d);let g=w>4?0:1,b=w;for(let t=1;t<64&&b;t++){const r=u[h+lt[t]];o(A+at(b,t,g),n(r)),g=r?1:0,b-=g}}},et=(H>0?3:2)*G+q;let st=0;const it=function*(t){K=t;const n=new rt(t.rt);for(let t=0;t<G;t++)tt(t,(t,o)=>n.add(t,o)),yield++st/et;return function(t,{maxClusters:n=48,newClusterCost:o=320}={}){const r=[];for(let n=0;n<t.rt;n++)t.ut[n]>0&&r.push(n);r.sort((n,o)=>t.ut[o]-t.ut[n]);const e=[];for(const s of r){const r=t.wt([s],ot),i=N(r);let c=-1,f=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.ht[t]+r[t];const s=N(o)-n.yt-i;s<f&&(f=s,c=t)}if(c<0||e.length<n&&f>o)e.push({rt:[s],ht:r,yt:i});else{const t=e[c];t.rt.push(s);for(let n=0;n<64;n++)t.ht[n]+=r[n];t.yt=N(t.ht)}}e.length||e.push({rt:[],ht:new Uint32Array(64),yt:0});const s=new Uint8Array(t.rt);let i=2*t.rt;const c=e.map((n,o)=>{for(const t of n.rt)s[t]=o;let r=nt[0],e=1/0;for(const o of nt){const s=t.Mt(n.rt,o);s<e&&(e=s,r=o)}const c=w(t.wt(n.rt,r,128));return i+=e+3*c.u+40,{config:r,code:c}});return{L:s,T:c,write:(t,n,o)=>{const r=c[s[n]];g(t,r.code,r.config,o)},yt:i}}(n)};let ct=yield*it(K);if(H>0){const t=K,n=_(F,0,h),o=yield*it(n);o.yt<=ct.yt?ct=o:K=t}const ft=Array.from({length:v?1:2+q+G},()=>null),ht=t=>ft[v?0:t]||(ft[v?0:t]=new R(4096)),ut=ht(0);ut.write(1,0);const wt=(t,n)=>{const r=t*D,e=n*D,i=Math.min(D,o-r),c=Math.min(D,s-e),f=new Int32Array(i*c);for(let t=0;t<c;t++)for(let n=0;n<i;n++)f[t*i+n]=a[4*((e+t)*o+r+n)+3];return{w:i,h:c,data:f}};for(let t=0;t<3;t++)ut.write(16,e(M[t].quant[0]/z[t]/(2040*u)*128));ut.write(2,3),ut.write(16,57343),ut.write(2,1),ut.write(5,0),ut.write(1,0),ut.write(4,0),ut.write(4,K.thresholds.length);for(const t of K.thresholds)ut.t([[4,0],[8,16],[16,272],[32,65808]],n(t));ut.write(4,0),ut.write(4,0),b(ut,K.map),ut.write(1,0),ut.write(2,0),ut.write(16,0),ut.write(16,0),ut.write(8,128),ut.write(8,128),ut.write(1,0),a&&C(ut,[v?wt(0,0):{w:0,h:0,data:new Int32Array(0)}],V);for(let t=0;t<q;t++){const n=ht(1+t),o=t%P*256,e=256*(t/P|0),s=Math.min(256,L-o),i=Math.min(256,T-e);n.write(2,0),C(n,[1,0,2].map(t=>{const n=s>>S(t),r=i>>E(t),c=new Int32Array(n*r),f=z[t],l=B[t];for(let s=0;s<r;s++)for(let r=0;r<n;r++){const i=j(t,(o>>S(t))+r,(e>>E(t))+s);c[s*n+r]=i<0?0:M[t].coeffs[i]*f+l}return{w:n,h:r,data:c}}),V);const c=s*i,f=r(c);f&&n.write(f,c-1);const l=s+7>>3,a=i+7>>3,h=new Int32Array(2*c).fill(y-1,c);C(n,[{w:l,h:a,data:new Int32Array(l*a)},{w:l,h:a,data:new Int32Array(l*a)},{w:c,h:2,data:h},{w:s,h:i,data:new Int32Array(c)}],y>1?V:0),yield++st/et}const yt=ht(1+q);yt.write(1,0),yt.write(3,7),yt.write(16,e(y/(2040*u))),C(yt,[0,1,2].map(t=>{const n=new Int32Array(64),o=M[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}}),V);for(let t=1;t<17;t++)yt.write(3,0);G>1&&yt.write(r(G),0),yt.write(2,2),m(yt,{L:ct.L,T:ct.T});for(let t=0;t<G;t++){const n=ht(2+q+t);tt(t,(t,o)=>ct.write(n,t,o)),a&&!v&&C(n,[wt(t%J,Math.floor(t/J))],V),yield++st/et}return f(W,function(t){return t.map(t=>t?t.finish():new Uint8Array(0))}(ft))}(yield*t(function*(t,n,o,r,e){const s=Math.ceil(n/8),i=Math.ceil(o/8),c=r>=30?.1+.09*(100-r):53/3e3*r*r-1.15*r+25,f=[0,1,2].map(t=>({h:1,v:1,stride:s,rows:i,quant:Int32Array.from(wt,(n,o)=>Math.max(1,Math.round(16*c*(t?8:14)*(o?n:.5)))),coeffs:new Int16Array(s*i*64)})),l=new Float64Array(192),a=new Float64Array(64),h=(r,e)=>{for(let i=r;i<e;i++)for(let r=0;r<s;r++){for(let e=0;e<8;e++)for(let s=0;s<8;s++){const c=4*(Math.min(o-1,8*i+e)*n+Math.min(n-1,8*r+s)),f=t[c],a=t[c+1],h=t[c+2],u=8*e+s;l[u]=.299*f+.587*a+.114*h-128,l[64+u]=-.168735892*f-.331264108*a+.5*h,l[128+u]=.5*f-.418687589*a-.081312411*h}for(let t=0;t<3;t++){for(let n=0;n<8;n++)for(let o=0;o<8;o++)a[8*n+o]=v(l,64*t+8*n,1,8*o);const{coeffs:n,quant:o}=f[t],e=64*(i*s+r);for(let t=0;t<8;t++)for(let r=0;r<8;r++){const s=v(a,r,8,8*t),i=8*t+r;n[e+i]=Math.round(16*s/o[i])}}}};for(let t=0;t<i;t+=32)h(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:f,quantScale:16,ycbcr:!0,orientation:1,alpha:u,colorSpace:e}}(o,s,l,a,h),0,2)),1,2)}(o,s,l,h,u))}const z=Object.freeze({bytes:16777216,pixels:24e6,edge:16384}),B=Object.freeze({bytes:16777216,pixels:4e7,edge:16384}),F=[1];for(let t=1;t<=32;t++)F.push(2*F[t-1]);class R{constructor(t=4096){this.bytes=new Uint8Array(t),this.at=0,this.dt=0,this.pending=0}write(t,n){if(!(n>=0&&n<F[t]))throw new Error("bit write out of range: "+t+" bits, "+n);let o=this.dt+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.dt=o,this.pending=r}t(t,n){for(let o=0;o<4;o++){const[r,e]=t[o];if(n>=e&&n-e<F[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.dt,this.dt=0,this.pending=0)}get lt(){return 8*this.at+this.pending}grow(t=0){const n=z.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.dt)}finish(){const t=this.at+(this.pending?1:0),n=this.bytes.slice(0,t);return this.pending&&(n[this.at]=this.dt),n}}const D=256,Z=[1,2,3,4,0,5,17,6,16,7,8,9,10,11,12,13,14,15],Y=[2,4,3,2,2,4],Q=[0,7,3,2,1,15],H=[0,0,0],K=3,V=5,W=p(4),$=Object.freeze({X:224,minLength:7,N:W}),tt=p(0),nt=[p(0),p(1),p(2),p(3),p(4),p(4,1,1),p(5,1,1),p(2,0,1),p(3,0,1)],ot=p(4,1,1);class rt{constructor(t){this.rt=t,this.small=new Uint32Array(64*t),this.At=Array.from({length:t},()=>null),this.ut=new Float64Array(t)}add(t,n){if(this.ut[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)}wt(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[J(n,o)]+=this.small[t+o]);const e=this.At[o];if(e)for(const[t,o]of e)r[J(n,t)]+=o}return r}Mt(t,n){let r=N(this.wt(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.U-n.I));const s=this.At[e];if(s)for(const[t,e]of s)r+=e*(o(t)-n.U-n.I)}return r}}const et=[0,0,0],st=[1];for(let t=1;t<32;t++)st.push(st[t-1]/2);const it=13,ct=[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],ft=[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],lt=[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].map(t=>(7&t)<<3|t>>3),at=(t,n,o)=>2*(ft[t]+ct[n])+o,ht=(t,n)=>Object.assign(new Error(n),{code:t}),ut=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),wt=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});export{B as LIMITS,j as encodePhoto,k as encodePhotoSteps};
|
package/photo.mjs
CHANGED
|
@@ -1,15 +1,40 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
//
|
|
3
|
-
// carrier's contexts and prefix codes.
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
// Rapier JXL's photograph door: pixels through the carrier's VarDCT writer, sRGB to YCbCr, forward DCT8, quantisation,
|
|
3
|
+
// then the carrier's contexts and prefix codes. Its own door, so the core carries none of it.
|
|
4
|
+
import {PHOTO_LIMITS, complete, part} from './bits.mjs';
|
|
5
|
+
import {varDCTSteps} from './vardct.mjs';
|
|
6
|
+
import {losslessSteps} from './lossless.mjs';
|
|
7
|
+
import {admitOptions, admitPixels, job} from './admit.mjs';
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
//
|
|
12
|
-
|
|
9
|
+
// This door's limits (bits.mjs): 40 million pixels, its memory 6.5 bytes a pixel besides the picture at that size.
|
|
10
|
+
export {PHOTO_LIMITS as LIMITS};
|
|
11
|
+
|
|
12
|
+
// Neither table comes from Math.cos, Math.hypot or `**`, which each engine rounds its own way; the same pixels must
|
|
13
|
+
// give the same bytes everywhere. The DCT8 basis, (u ? 1 : √½) · cos((2x + 1) · u · π / 16) / 2 at u · 8 + x, is
|
|
14
|
+
// written out as V8 computed it (the cosines of the larger angles carry their rounding, and a coefficient on a
|
|
15
|
+
// rounding edge would move without it). The quantisation shape, 1.3 ** max(0, hypot(u, v) · 5 / √98 − 1) at
|
|
16
|
+
// v · 8 + u, is the exponential series of its logarithm: libjxl 0.7's DCT8 luma distance bands
|
|
17
|
+
// (lib/jxl/quant_weights.cc), the reciprocal weight growing with radial frequency; its integer steps are V8's at every
|
|
18
|
+
// quality by 0.001. Both chroma planes keep full resolution; their scale accounts for YCbCr's larger RGB error.
|
|
19
|
+
const COSINES = Float64Array.of(0.3535533905932738, 0.3535533905932738, 0.3535533905932738, 0.3535533905932738,
|
|
20
|
+
0.3535533905932738, 0.3535533905932738, 0.3535533905932738, 0.3535533905932738, 0.4903926402016152,
|
|
21
|
+
0.4157348061512726, 0.27778511650980114, 0.09754516100806417, -0.0975451610080641, -0.277785116509801,
|
|
22
|
+
-0.4157348061512727, -0.4903926402016152, 0.46193976625564337, 0.19134171618254492, -0.19134171618254486,
|
|
23
|
+
-0.46193976625564337, -0.4619397662556434, -0.19134171618254517, 0.191341716182545, 0.46193976625564326,
|
|
24
|
+
0.4157348061512726, -0.0975451610080641, -0.4903926402016152, -0.2777851165098011, 0.2777851165098009,
|
|
25
|
+
0.4903926402016152, 0.09754516100806439, -0.41573480615127256, 0.3535533905932738, -0.35355339059327373,
|
|
26
|
+
-0.35355339059327384, 0.3535533905932737, 0.35355339059327384, -0.35355339059327334, -0.35355339059327356,
|
|
27
|
+
0.3535533905932733, 0.27778511650980114, -0.4903926402016152, 0.09754516100806415, 0.4157348061512728,
|
|
28
|
+
-0.41573480615127256, -0.09754516100806401, 0.4903926402016153, -0.27778511650980076, 0.19134171618254492,
|
|
29
|
+
-0.4619397662556434, 0.46193976625564326, -0.19134171618254495, -0.19134171618254528, 0.46193976625564337,
|
|
30
|
+
-0.4619397662556432, 0.19134171618254478, 0.09754516100806417, -0.2777851165098011, 0.4157348061512728,
|
|
31
|
+
-0.4903926402016153, 0.4903926402016152, -0.4157348061512725, 0.27778511650980076, -0.09754516100806429);
|
|
32
|
+
const QUANT_SHAPE = Float64Array.from({length: 64}, (_, k) => {
|
|
33
|
+
const y = Math.max(0, Math.sqrt((k & 7) * (k & 7) + (k >> 3) * (k >> 3)) * 5 / Math.sqrt(98) - 1) * 0.26236426446749106;
|
|
34
|
+
let term = 1, sum = 1;
|
|
35
|
+
for (let n = 1; n < 30; n++) sum += term = term * y / n;
|
|
36
|
+
return sum;
|
|
37
|
+
});
|
|
13
38
|
|
|
14
39
|
// Eight terms in the original accumulation order, including the initial zero. The two separable passes
|
|
15
40
|
// share this fixed dot product without a loop or a changed floating-point sum.
|
|
@@ -26,49 +51,55 @@ function dot8(data, at, step, basis) {
|
|
|
26
51
|
return sum;
|
|
27
52
|
}
|
|
28
53
|
|
|
29
|
-
|
|
54
|
+
// A row of groups (32 block rows) per step.
|
|
55
|
+
function* coefficientSteps(data, width, height, quality, colorSpace) {
|
|
30
56
|
const stride = Math.ceil(width / 8), rows = Math.ceil(height / 8), quantScale = 16;
|
|
31
57
|
const distance = quality >= 30 ? 0.1 + (100 - quality) * 0.09 : 53 / 3000 * quality * quality - 23 / 20 * quality + 25;
|
|
32
58
|
const components = [0, 1, 2].map(c => ({h: 1, v: 1, stride, rows,
|
|
33
59
|
quant: Int32Array.from(QUANT_SHAPE, (shape, k) => Math.max(1, Math.round(quantScale * distance * (c ? 8 : 14) * (k ? shape : 0.5)))),
|
|
34
60
|
coeffs: new Int16Array(stride * rows * 64)}));
|
|
35
61
|
const block = new Float64Array(192), intermediate = new Float64Array(64);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
for (let c = 0; c < 3; c++) {
|
|
46
|
-
for (let y = 0; y < 8; y++) for (let u = 0; u < 8; u++) {
|
|
47
|
-
intermediate[y * 8 + u] = dot8(block, c * 64 + y * 8, 1, u * 8);
|
|
62
|
+
const band = (from, to) => {
|
|
63
|
+
for (let by = from; by < to; by++) for (let bx = 0; bx < stride; bx++) {
|
|
64
|
+
// Edge replication gives a complete transform without inventing a dark border in a partial block.
|
|
65
|
+
for (let y = 0; y < 8; y++) for (let x = 0; x < 8; x++) {
|
|
66
|
+
const at = (Math.min(height - 1, by * 8 + y) * width + Math.min(width - 1, bx * 8 + x)) * 4;
|
|
67
|
+
const r = data[at], g = data[at + 1], b = data[at + 2], k = y * 8 + x;
|
|
68
|
+
block[k] = 0.299 * r + 0.587 * g + 0.114 * b - 128;
|
|
69
|
+
block[64 + k] = -0.168735892 * r - 0.331264108 * g + 0.5 * b;
|
|
70
|
+
block[128 + k] = 0.5 * r - 0.418687589 * g - 0.081312411 * b;
|
|
48
71
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
72
|
+
for (let c = 0; c < 3; c++) {
|
|
73
|
+
for (let y = 0; y < 8; y++) for (let u = 0; u < 8; u++) {
|
|
74
|
+
intermediate[y * 8 + u] = dot8(block, c * 64 + y * 8, 1, u * 8);
|
|
75
|
+
}
|
|
76
|
+
const {coeffs, quant} = components[c], offset = (by * stride + bx) * 64;
|
|
77
|
+
for (let v = 0; v < 8; v++) for (let u = 0; u < 8; u++) {
|
|
78
|
+
const sum = dot8(intermediate, u, 8, v * 8), k = v * 8 + u;
|
|
79
|
+
coeffs[offset + k] = Math.round(sum * quantScale / quant[k]);
|
|
80
|
+
}
|
|
53
81
|
}
|
|
54
82
|
}
|
|
55
|
-
}
|
|
83
|
+
};
|
|
84
|
+
for (let by = 0; by < rows; by += 32) { band(by, Math.min(rows, by + 32)); yield Math.min(rows, by + 32) / rows; }
|
|
56
85
|
let alpha = null;
|
|
57
86
|
for (let i = 3; i < data.length; i += 4) if (data[i] !== 255) { alpha = data; break; }
|
|
58
|
-
return {width, height, components, quantScale, ycbcr: true, orientation: 1, alpha};
|
|
87
|
+
return {width, height, components, quantScale, ycbcr: true, orientation: 1, alpha, colorSpace};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Quality 90 by default, 1 to 99 photographic, on libjxl's quality-to-distance curve; 100 is the core's exact stream,
|
|
91
|
+
// including RGB beneath transparent pixels. Alpha is always exact; the colour space is declared as the core declares
|
|
92
|
+
// it. The options are read before any work.
|
|
93
|
+
export function encodePhoto(data, width, height, options) { return complete(encodePhotoSteps(data, width, height, options)); }
|
|
94
|
+
|
|
95
|
+
// The same work as a job (admit.mjs): the coefficients a row of groups per step, then the VarDCT writer's steps.
|
|
96
|
+
export function encodePhotoSteps(data, width, height, options) {
|
|
97
|
+
const {quality, colorSpace} = admitOptions(options, 90);
|
|
98
|
+
admitPixels(data, width, height, PHOTO_LIMITS);
|
|
99
|
+
return job(photoSteps(data, width, height, quality, colorSpace));
|
|
59
100
|
}
|
|
60
101
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const quality = options.quality === undefined ? 90 : options.quality;
|
|
65
|
-
if (typeof quality !== 'number' || !Number.isFinite(quality) || quality < 1 || quality > 100) throw photoFault('JXL_INPUT', 'Quality is a number from 1 to 100.');
|
|
66
|
-
if (!Number.isInteger(width) || !Number.isInteger(height) || width < 1 || height < 1) throw photoFault('JXL_INPUT', 'A picture has positive whole pixel dimensions.');
|
|
67
|
-
if (width > LIMITS.edge || height > LIMITS.edge || width * height > LIMITS.pixels) throw photoFault('JXL_DIMENSIONS', 'The picture exceeds the JPEG XL pixel limits.');
|
|
68
|
-
if (!(data instanceof Uint8Array) && !(data instanceof Uint8ClampedArray) || data.length !== width * height * 4) throw photoFault('JXL_INPUT', 'Pixels are width * height * 4 straight RGBA bytes.');
|
|
69
|
-
try {
|
|
70
|
-
const bytes = quality >= 100 ? encodeLossless(data, width, height) : encodeVarDCT(photoCoefficients(data, width, height, quality));
|
|
71
|
-
if (bytes.length > LIMITS.bytes) throw photoFault('JXL_SIZE', 'The encoded picture exceeds 16 MiB.');
|
|
72
|
-
return bytes;
|
|
73
|
-
} catch (error) { if (error instanceof RangeError && !error.code) throw photoFault('JXL_MEMORY', 'Not enough memory for this picture.'); throw error; }
|
|
102
|
+
function* photoSteps(data, width, height, quality, colorSpace) {
|
|
103
|
+
if (quality >= 100) return yield* losslessSteps(data, width, height, {colorSpace});
|
|
104
|
+
return yield* part(varDCTSteps(yield* part(coefficientSteps(data, width, height, quality, colorSpace), 0, 2)), 1, 2);
|
|
74
105
|
}
|
|
@@ -9,7 +9,9 @@ The result shows only in a browser that decodes JPEG XL; the size is reported ei
|
|
|
9
9
|
<p id="out"></p>
|
|
10
10
|
<img id="shown" alt="">
|
|
11
11
|
<script type="module">
|
|
12
|
-
import {encode
|
|
12
|
+
import {encode} from '../../index.mjs';
|
|
13
|
+
import {transcode} from '../../jpeg.mjs';
|
|
14
|
+
import {encodePhoto} from '../../photo.mjs';
|
|
13
15
|
const out = document.getElementById('out'), shown = document.getElementById('shown');
|
|
14
16
|
document.querySelector('input[type=file]').addEventListener('change', async event => {
|
|
15
17
|
const file = event.target.files[0];
|
|
@@ -27,7 +29,7 @@ document.querySelector('input[type=file]').addEventListener('change', async even
|
|
|
27
29
|
context.drawImage(image, 0, 0);
|
|
28
30
|
const {data, width, height} = context.getImageData(0, 0, image.width, image.height);
|
|
29
31
|
const quality = document.getElementById('lossy').checked ? Number(document.getElementById('q').value) : 100;
|
|
30
|
-
bytes = encode(data, width, height, {quality}); note = quality >= 100 ? 'lossless' : 'lossy, quality ' + quality;
|
|
32
|
+
bytes = quality >= 100 ? encode(data, width, height) : encodePhoto(data, width, height, {quality}); note = quality >= 100 ? 'lossless' : 'lossy, quality ' + quality;
|
|
31
33
|
}
|
|
32
34
|
const ms = Math.round(performance.now() - started);
|
|
33
35
|
out.textContent = `${file.name}: ${file.size.toLocaleString()} bytes in, ${bytes.length.toLocaleString()} bytes of JPEG XL out (${note}, ${ms} ms).`;
|
|
@@ -1,17 +1,42 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// A complete worker around Rapier JXL: post {id, op: 'encode', data, width, height, quality} or
|
|
3
|
-
//
|
|
4
|
-
|
|
2
|
+
// A complete worker around Rapier JXL: post {id, op: 'encode' or 'photo', data, width, height, quality} or
|
|
3
|
+
// {id, op: 'transcode', jpeg}; receive {id, progress} while it works, then {id, ok: true, bytes, width, height,
|
|
4
|
+
// orientation} or {id, ok: false, code, message}. {id, op: 'abort'} ends a request between steps. 'encode' takes an
|
|
5
|
+
// effort (the effort door; 1, its default, writes the core's bytes) and a deadline in milliseconds: past it the search
|
|
6
|
+
// is hurried and answers with the smallest stream written so far, never larger than effort 1's. Bytes are transferred.
|
|
7
|
+
import {encodeSteps} from '../../effort.mjs';
|
|
8
|
+
import {transcodeSteps} from '../../jpeg.mjs';
|
|
9
|
+
import {encodePhotoSteps} from '../../photo.mjs';
|
|
5
10
|
|
|
6
|
-
|
|
7
|
-
|
|
11
|
+
const running = new Set();
|
|
12
|
+
const turn = () => new Promise(resolve => setTimeout(resolve));
|
|
13
|
+
|
|
14
|
+
self.onmessage = async event => {
|
|
15
|
+
const ask = event.data || {}, {id, op} = ask;
|
|
16
|
+
if (op === 'abort') { running.delete(id); return; }
|
|
8
17
|
try {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
18
|
+
const job = op === 'encode' ? encodeSteps(ask.data, ask.width, ask.height, {quality: ask.quality, effort: ask.effort})
|
|
19
|
+
: op === 'photo' ? encodePhotoSteps(ask.data, ask.width, ask.height, {quality: ask.quality})
|
|
20
|
+
: op === 'transcode' ? transcodeSteps(ask.jpeg)
|
|
21
|
+
: null;
|
|
22
|
+
if (!job) throw Object.assign(new Error('unknown op ' + op), {code: 'JXL_INPUT'});
|
|
23
|
+
running.add(id);
|
|
24
|
+
let shown = performance.now();
|
|
25
|
+
const hurryAt = ask.deadline >= 0 ? shown + ask.deadline : Infinity;
|
|
26
|
+
for (const done of job) {
|
|
27
|
+
if (performance.now() >= hurryAt) job.hurry = true;
|
|
28
|
+
// Every 50 ms the worker takes its messages (an abort among them) and reports progress; leaving is the cancel.
|
|
29
|
+
if (performance.now() - shown < 50) continue;
|
|
30
|
+
self.postMessage({id, progress: done});
|
|
31
|
+
await turn();
|
|
32
|
+
if (!running.has(id)) return;
|
|
33
|
+
shown = performance.now();
|
|
34
|
+
}
|
|
35
|
+
running.delete(id);
|
|
36
|
+
const out = {bytes: job.bytes, width: job.width ?? ask.width, height: job.height ?? ask.height, orientation: job.orientation ?? 1};
|
|
13
37
|
self.postMessage({id, ok: true, ...out}, [out.bytes.buffer]);
|
|
14
38
|
} catch (error) {
|
|
39
|
+
running.delete(id);
|
|
15
40
|
self.postMessage({id, ok: false, code: error.code || 'JXL_ERROR', message: String(error.message || error)});
|
|
16
41
|
}
|
|
17
42
|
};
|