imagescript 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +28 -8
  2. package/THIRD_PARTY_NOTICES.md +167 -2
  3. package/dist/animation.d.ts +18 -8
  4. package/dist/animation.js +23 -2
  5. package/dist/codecs/av1/cdef.js +197 -0
  6. package/dist/codecs/av1/data.js +141 -0
  7. package/dist/codecs/av1/decode.js +1769 -0
  8. package/dist/codecs/av1/filmgrain.js +219 -0
  9. package/dist/codecs/av1/frame.js +114 -0
  10. package/dist/codecs/av1/ipred.js +491 -0
  11. package/dist/codecs/av1/itx.js +950 -0
  12. package/dist/codecs/av1/loopfilter.js +149 -0
  13. package/dist/codecs/av1/msac.js +161 -0
  14. package/dist/codecs/av1/obu.js +516 -0
  15. package/dist/codecs/av1/postfilter.js +28 -0
  16. package/dist/codecs/av1/restoration.js +193 -0
  17. package/dist/codecs/av1/tables.js +160 -0
  18. package/dist/codecs/avif.js +581 -0
  19. package/dist/codecs/bmp.js +317 -0
  20. package/dist/codecs/hdr.d.ts +25 -0
  21. package/dist/codecs/hdr.js +201 -0
  22. package/dist/codecs/ico.js +48 -0
  23. package/dist/codecs/jpeg.js +2 -1
  24. package/dist/codecs/jxl/bits.js +126 -0
  25. package/dist/codecs/jxl/blend.js +117 -0
  26. package/dist/codecs/jxl/color.js +116 -0
  27. package/dist/codecs/jxl/dct.js +404 -0
  28. package/dist/codecs/jxl/entropy.js +556 -0
  29. package/dist/codecs/jxl/features.js +325 -0
  30. package/dist/codecs/jxl/filters.js +119 -0
  31. package/dist/codecs/jxl/frame-header.js +195 -0
  32. package/dist/codecs/jxl/frame.js +319 -0
  33. package/dist/codecs/jxl/headers.js +249 -0
  34. package/dist/codecs/jxl/idct.js +484 -0
  35. package/dist/codecs/jxl/modular.js +688 -0
  36. package/dist/codecs/jxl/quant.js +219 -0
  37. package/dist/codecs/jxl/upsample.js +52 -0
  38. package/dist/codecs/jxl/vardct.js +472 -0
  39. package/dist/codecs/jxl.js +89 -0
  40. package/dist/codecs/netpbm.js +239 -0
  41. package/dist/codecs/qoi.js +150 -0
  42. package/dist/codecs/vp8-enc-dsp.js +932 -0
  43. package/dist/codecs/vp8-enc-tables.js +78 -0
  44. package/dist/codecs/vp8-enc.js +1930 -0
  45. package/dist/codecs/webp.js +36 -2
  46. package/dist/image-type.d.ts +15 -1
  47. package/dist/image-type.js +45 -2
  48. package/dist/image.d.ts +47 -6
  49. package/dist/image.js +49 -3
  50. package/dist/index.d.ts +3 -2
  51. package/dist/index.js +2 -2
  52. package/dist/render/hinting.js +26 -18
  53. package/dist/render/layout.js +1 -1
  54. package/dist/render/svg.js +1 -1
  55. package/dist/render/unicode.js +1 -9
  56. package/dist/util/bytes.js +9 -0
  57. package/dist/util/resample.js +4 -2
  58. package/dist/version.js +1 -1
  59. package/package.json +25 -2
@@ -0,0 +1,126 @@
1
+ /*!
2
+ * The JPEG XL bit reader and the field types of its headers (ISO/IEC 18181-1), ported from jxl-oxide
3
+ * (crates/jxl-bitstream). Copyright (c) Wonwoo Choi, MIT License (see THIRD_PARTY_NOTICES.md)
4
+ */
5
+ export class BitReader {
6
+ d;
7
+ start;
8
+ end;
9
+ buf = 0;
10
+ n = 0;
11
+ pos;
12
+ constructor(d, start = 0, end = d.length) {
13
+ this.d = d;
14
+ this.start = start;
15
+ this.end = end;
16
+ this.pos = start;
17
+ }
18
+ get bitPosition() {
19
+ return (this.pos - this.start) * 8 - this.n;
20
+ }
21
+ refill() {
22
+ while (this.n <= 24) {
23
+ if (this.pos >= this.end) {
24
+ if (this.pos >= this.end + 8)
25
+ throw new Error('JPEG XL: truncated data');
26
+ this.pos++;
27
+ }
28
+ else
29
+ this.buf = (this.buf | this.d[this.pos++] << this.n) >>> 0;
30
+ this.n += 8;
31
+ }
32
+ }
33
+ peek(n) {
34
+ if (this.n < n)
35
+ this.refill();
36
+ return this.buf & ((1 << n) - 1);
37
+ }
38
+ skip(n) {
39
+ this.buf >>>= n;
40
+ this.n -= n;
41
+ }
42
+ bits(n) {
43
+ if (n > 24) {
44
+ const low = this.bits(16);
45
+ return low + this.bits(n - 16) * 65536;
46
+ }
47
+ if (this.n < n)
48
+ this.refill();
49
+ const v = this.buf & ((1 << n) - 1);
50
+ this.buf >>>= n;
51
+ this.n -= n;
52
+ return v;
53
+ }
54
+ bool() {
55
+ return this.bits(1) === 1;
56
+ }
57
+ u32(d0, d1, d2, d3) {
58
+ const sel = this.bits(2);
59
+ const d = sel === 0 ? d0 : sel === 1 ? d1 : sel === 2 ? d2 : d3;
60
+ return typeof d === 'number' ? d : d[0] + this.bits(d[1]);
61
+ }
62
+ u64() {
63
+ const sel = this.bits(2);
64
+ if (sel === 0)
65
+ return 0;
66
+ if (sel === 1)
67
+ return 1 + this.bits(4);
68
+ if (sel === 2)
69
+ return 17 + this.bits(8);
70
+ let value = this.bits(12), shift = 12;
71
+ while (this.bool()) {
72
+ if (shift === 60) {
73
+ value += this.bits(4) * 2 ** shift;
74
+ break;
75
+ }
76
+ value += this.bits(8) * 2 ** shift;
77
+ shift += 8;
78
+ }
79
+ if (value > Number.MAX_SAFE_INTEGER)
80
+ throw new Error('JPEG XL: value too large');
81
+ return value;
82
+ }
83
+ f16() {
84
+ const v = this.bits(16);
85
+ const sign = v & 0x8000 ? -1 : 1, exponent = v >> 10 & 31, mantissa = v & 1023;
86
+ if (exponent === 31)
87
+ throw new Error('JPEG XL: invalid float');
88
+ if (exponent === 0)
89
+ return sign * mantissa / 1024 / 16384;
90
+ return sign * (1 + mantissa / 1024) * 2 ** (exponent - 15);
91
+ }
92
+ enum() {
93
+ const v = this.u32(0, 1, [2, 4], [18, 6]);
94
+ if (v > 63)
95
+ throw new Error('JPEG XL: invalid enum value');
96
+ return v;
97
+ }
98
+ skipBits(n) {
99
+ while (n > 24) {
100
+ this.bits(24);
101
+ n -= 24;
102
+ }
103
+ this.bits(n);
104
+ }
105
+ zeroPadToByte() {
106
+ const pad = (8 - this.bitPosition % 8) % 8;
107
+ if (this.bits(pad) !== 0)
108
+ throw new Error('JPEG XL: non-zero padding');
109
+ }
110
+ get bytePosition() {
111
+ return this.start + Math.ceil(this.bitPosition / 8);
112
+ }
113
+ checkEnd() {
114
+ if (this.bitPosition > (this.end - this.start) * 8)
115
+ throw new Error('JPEG XL: truncated data');
116
+ }
117
+ }
118
+ export const unpackSigned = (v) => v % 2 ? -(v + 1) / 2 : v / 2;
119
+ export function extensions(r) {
120
+ const bits = r.u64();
121
+ let lengths = 0;
122
+ for (let i = 0; i < 64; i++)
123
+ if (Math.floor(bits / 2 ** i) % 2)
124
+ lengths += r.u64();
125
+ r.skipBits(lengths);
126
+ }
@@ -0,0 +1,117 @@
1
+ import { EXTRA_ALPHA } from "./headers.js";
2
+ export const PB_NONE = 0, PB_REPLACE = 1, PB_ADD = 2, PB_MUL = 3, PB_BLEND_ABOVE = 4, PB_BLEND_BELOW = 5, PB_ADD_ABOVE = 6, PB_ADD_BELOW = 7;
3
+ const clamp01 = (x) => x < 0 ? 0 : x > 1 ? 1 : x;
4
+ let rows = [];
5
+ export function performBlending(bg, bi, fg, fi, out, oi, n, numColor, color, ec, extra) {
6
+ const hasAlpha = extra.some(e => e.type === EXTRA_ALPHA), numEc = extra.length, nc = numColor + numEc;
7
+ if (rows.length < nc || rows[0].length < n)
8
+ rows = Array.from({ length: Math.max(nc, rows.length) }, () => new Float32Array(Math.max(n, rows[0]?.length ?? 0)));
9
+ for (let i = 0; i < numEc; i++) {
10
+ const c = numColor + i, e = ec[i], a = numColor + e.alpha, t = rows[c], B = bg[c], F = fg[c], BA = bg[a], FA = fg[a];
11
+ const isAlpha = c === a, premultiplied = extra[e.alpha].alphaAssociated;
12
+ switch (e.mode) {
13
+ case PB_ADD:
14
+ for (let k = 0; k < n; k++)
15
+ t[k] = B[bi + k] + F[fi + k];
16
+ break;
17
+ case PB_BLEND_ABOVE:
18
+ for (let k = 0; k < n; k++)
19
+ t[k] = alphaBlend1(B[bi + k], BA[bi + k], F[fi + k], FA[fi + k], isAlpha, premultiplied, e.clamp);
20
+ break;
21
+ case PB_BLEND_BELOW:
22
+ for (let k = 0; k < n; k++)
23
+ t[k] = alphaBlend1(F[fi + k], FA[fi + k], B[bi + k], BA[bi + k], isAlpha, premultiplied, e.clamp);
24
+ break;
25
+ case PB_ADD_ABOVE:
26
+ for (let k = 0; k < n; k++)
27
+ t[k] = isAlpha ? B[bi + k] : B[bi + k] + F[fi + k] * (e.clamp ? clamp01(FA[fi + k]) : FA[fi + k]);
28
+ break;
29
+ case PB_ADD_BELOW:
30
+ for (let k = 0; k < n; k++)
31
+ t[k] = isAlpha ? F[fi + k] : F[fi + k] + B[bi + k] * (e.clamp ? clamp01(BA[bi + k]) : BA[bi + k]);
32
+ break;
33
+ case PB_MUL:
34
+ for (let k = 0; k < n; k++)
35
+ t[k] = B[bi + k] * (e.clamp ? clamp01(F[fi + k]) : F[fi + k]);
36
+ break;
37
+ case PB_REPLACE:
38
+ t.set(F.subarray(fi, fi + n));
39
+ break;
40
+ default: t.set(B.subarray(bi, bi + n));
41
+ }
42
+ }
43
+ const a = numColor + color.alpha;
44
+ switch (color.mode) {
45
+ case PB_ADD:
46
+ for (let c = 0; c < numColor; c++) {
47
+ const t = rows[c], B = bg[c], F = fg[c];
48
+ for (let k = 0; k < n; k++)
49
+ t[k] = B[bi + k] + F[fi + k];
50
+ }
51
+ break;
52
+ case PB_ADD_ABOVE:
53
+ case PB_ADD_BELOW: {
54
+ const [lo, li, hi, hj] = color.mode === PB_ADD_ABOVE ? [bg, bi, fg, fi] : [fg, fi, bg, bi];
55
+ for (let c = 0; c < numColor; c++) {
56
+ const t = rows[c], L = lo[c], H = hi[c];
57
+ if (!hasAlpha) {
58
+ for (let k = 0; k < n; k++)
59
+ t[k] = bg[c][bi + k] + fg[c][fi + k];
60
+ continue;
61
+ }
62
+ const HA = hi[a];
63
+ for (let k = 0; k < n; k++)
64
+ t[k] = L[li + k] + H[hj + k] * (color.clamp ? clamp01(HA[hj + k]) : HA[hj + k]);
65
+ }
66
+ break;
67
+ }
68
+ case PB_BLEND_ABOVE:
69
+ case PB_BLEND_BELOW: {
70
+ if (!hasAlpha) {
71
+ for (let c = 0; c < numColor; c++)
72
+ rows[c].set(fg[c].subarray(fi, fi + n));
73
+ break;
74
+ }
75
+ const [lo, li, hi, hj] = color.mode === PB_BLEND_ABOVE ? [bg, bi, fg, fi] : [fg, fi, bg, bi];
76
+ const premultiplied = extra[color.alpha].alphaAssociated, HA = hi[a], LA = lo[a], ta = rows[a];
77
+ for (let k = 0; k < n; k++) {
78
+ const fa = color.clamp ? clamp01(HA[hj + k]) : HA[hj + k], ba = LA[li + k], na = 1 - (1 - fa) * (1 - ba);
79
+ if (premultiplied)
80
+ for (let c = 0; c < numColor; c++)
81
+ rows[c][k] = hi[c][hj + k] + lo[c][li + k] * (1 - fa);
82
+ else {
83
+ const rna = na > 0 ? 1 / na : 0;
84
+ for (let c = 0; c < numColor; c++)
85
+ rows[c][k] = (hi[c][hj + k] * fa + lo[c][li + k] * ba * (1 - fa)) * rna;
86
+ }
87
+ ta[k] = na;
88
+ }
89
+ break;
90
+ }
91
+ case PB_MUL:
92
+ for (let c = 0; c < numColor; c++) {
93
+ const t = rows[c], B = bg[c], F = fg[c];
94
+ for (let k = 0; k < n; k++)
95
+ t[k] = B[bi + k] * (color.clamp ? clamp01(F[fi + k]) : F[fi + k]);
96
+ }
97
+ break;
98
+ case PB_REPLACE:
99
+ for (let c = 0; c < numColor; c++)
100
+ rows[c].set(fg[c].subarray(fi, fi + n));
101
+ break;
102
+ default:
103
+ for (let c = 0; c < numColor; c++)
104
+ rows[c].set(bg[c].subarray(bi, bi + n));
105
+ }
106
+ for (let c = 0; c < nc; c++)
107
+ out[c].set(rows[c].subarray(0, n), oi);
108
+ }
109
+ function alphaBlend1(bg, bga, fg, fga, isAlpha, premultiplied, clamp) {
110
+ const fa = clamp ? fga : clamp01(fga);
111
+ if (isAlpha)
112
+ return 1 - (1 - fa) * (1 - bga);
113
+ if (premultiplied)
114
+ return fg + bg * (1 - fa);
115
+ const na = 1 - (1 - fa) * (1 - bga);
116
+ return (fg * fa + bg * bga * (1 - fa)) * (na > 0 ? 1 / na : 0);
117
+ }
@@ -0,0 +1,116 @@
1
+ import { TF_PQ, TF_HLG, PRI_BT2100, PRI_P3, EXTRA_ALPHA, EXTRA_SPOT, EXTRA_BLACK } from "./headers.js";
2
+ import { toSRGB, hdrToLinear, REFERENCE_WHITE } from "../hdr.js";
3
+ const srgbEncode = (v) => v <= 0.0031308 ? 12.92 * v : 1.055 * v ** (1 / 2.4) - 0.055;
4
+ export class ColorOutput {
5
+ image;
6
+ toneMapping;
7
+ linear;
8
+ hdrSignal;
9
+ scale;
10
+ constructor(image, toneMapping) {
11
+ this.image = image;
12
+ this.toneMapping = toneMapping;
13
+ const c = image.colour, hdr = !c.wantICC && (c.transfer === TF_PQ || c.transfer === TF_HLG);
14
+ this.linear = image.xybEncoded && (hdr || image.toneMapping.intensityTarget > 255);
15
+ this.hdrSignal = !image.xybEncoded && hdr;
16
+ this.scale = this.linear ? image.toneMapping.intensityTarget / REFERENCE_WHITE : 1;
17
+ }
18
+ transform(p, ycbcr) {
19
+ const image = this.image, n = p.width * p.height, ch = p.channels.slice();
20
+ if (ycbcr) {
21
+ const [cb, y, cr] = ch, r = new Float32Array(n), g = new Float32Array(n), b = new Float32Array(n);
22
+ for (let i = 0; i < n; i++) {
23
+ const Y = y[i] + 128 / 255;
24
+ r[i] = Y + 1.402 * cr[i];
25
+ g[i] = Y - 0.299 * 1.402 / 0.587 * cr[i] - 0.114 * 1.772 / 0.587 * cb[i];
26
+ b[i] = Y + 1.772 * cb[i];
27
+ }
28
+ ch.splice(0, 3, r, g, b);
29
+ }
30
+ else if (image.xybEncoded) {
31
+ const [x, y, b] = ch, m = image.opsinInverse, bias = image.opsinBias, cbrt = bias.map(Math.cbrt);
32
+ const k = 255 / image.toneMapping.intensityTarget * this.scale;
33
+ const R = new Float32Array(n), G = new Float32Array(n), B = new Float32Array(n);
34
+ for (let i = 0; i < n; i++) {
35
+ const gr = y[i] + x[i] - cbrt[0], gg = y[i] - x[i] - cbrt[1], gb = b[i] - cbrt[2];
36
+ const mr = gr * gr * gr + bias[0], mg = gg * gg * gg + bias[1], mb = gb * gb * gb + bias[2];
37
+ let r = (m[0] * mr + m[1] * mg + m[2] * mb) * k, g = (m[3] * mr + m[4] * mg + m[5] * mb) * k, bl = (m[6] * mr + m[7] * mg + m[8] * mb) * k;
38
+ if (!this.linear) {
39
+ r = srgbEncode(r);
40
+ g = srgbEncode(g);
41
+ bl = srgbEncode(bl);
42
+ }
43
+ R[i] = r;
44
+ G[i] = g;
45
+ B[i] = bl;
46
+ }
47
+ ch.splice(0, 3, R, G, B);
48
+ }
49
+ return { width: p.width, height: p.height, channels: ch };
50
+ }
51
+ toRGBA(p) {
52
+ const n = p.width * p.height, ch = p.channels, image = this.image;
53
+ const numColor = ch.length - image.extraChannels.length, grey = numColor === 1;
54
+ let [r, g, b] = grey ? [ch[0], ch[0], ch[0]] : ch;
55
+ const black = image.extraChannels.findIndex(e => e.type === EXTRA_BLACK);
56
+ if (black >= 0) {
57
+ const k = ch[numColor + black];
58
+ if (grey)
59
+ r = g = b = r.slice();
60
+ else
61
+ [r, g, b] = [r.slice(), g.slice(), b.slice()];
62
+ for (let i = 0; i < n; i++) {
63
+ r[i] *= k[i];
64
+ if (!grey) {
65
+ g[i] *= k[i];
66
+ b[i] *= k[i];
67
+ }
68
+ }
69
+ }
70
+ image.extraChannels.forEach((e, i) => {
71
+ if (e.type !== EXTRA_SPOT || grey)
72
+ return;
73
+ const s = ch[numColor + i], [sr, sg, sb, solidity] = e.spot;
74
+ if (r === ch[0])
75
+ [r, g, b] = [r.slice(), g.slice(), b.slice()];
76
+ for (let k = 0; k < n; k++) {
77
+ const mix = solidity * s[k];
78
+ r[k] = mix * sr + (1 - mix) * r[k];
79
+ g[k] = mix * sg + (1 - mix) * g[k];
80
+ b[k] = mix * sb + (1 - mix) * b[k];
81
+ }
82
+ });
83
+ const alphaIndex = image.extraChannels.findIndex(e => e.type === EXTRA_ALPHA);
84
+ const alpha = alphaIndex >= 0 ? ch[ch.length - image.extraChannels.length + alphaIndex] : null;
85
+ const premultiplied = alphaIndex >= 0 && image.extraChannels[alphaIndex].alphaAssociated;
86
+ let out;
87
+ if (this.linear || this.hdrSignal) {
88
+ const rgb = new Float32Array(3 * n);
89
+ for (let i = 0; i < n; i++) {
90
+ const a = premultiplied ? Math.max(alpha[i], 2 ** -26) : 1;
91
+ rgb[3 * i] = r[i] / a;
92
+ rgb[3 * i + 1] = g[i] / a;
93
+ rgb[3 * i + 2] = b[i] / a;
94
+ }
95
+ const c = image.colour;
96
+ if (this.hdrSignal)
97
+ hdrToLinear(rgb, c.transfer, c.primaries === PRI_BT2100 ? 9 : c.primaries === PRI_P3 ? 12 : 1);
98
+ out = toSRGB(rgb, this.toneMapping);
99
+ }
100
+ else {
101
+ out = new Uint8Array(4 * n);
102
+ const byte = (v) => v <= 0 ? 0 : v >= 1 ? 255 : Math.round(v * 255);
103
+ for (let i = 0; i < n; i++) {
104
+ const a = premultiplied ? Math.max(alpha[i], 2 ** -26) : 1;
105
+ out[4 * i] = byte(r[i] / a);
106
+ out[4 * i + 1] = byte(g[i] / a);
107
+ out[4 * i + 2] = byte(b[i] / a);
108
+ out[4 * i + 3] = 255;
109
+ }
110
+ }
111
+ if (alpha)
112
+ for (let i = 0; i < n; i++)
113
+ out[4 * i + 3] = alpha[i] <= 0 ? 0 : alpha[i] >= 1 ? 255 : Math.round(alpha[i] * 255);
114
+ return out;
115
+ }
116
+ }