imagescript 3.2.1 → 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.
- package/README.md +35 -10
- package/THIRD_PARTY_NOTICES.md +298 -2
- package/dist/animation.d.ts +25 -8
- package/dist/animation.js +44 -13
- package/dist/codecs/av1/cdef.js +197 -0
- package/dist/codecs/av1/data.js +141 -0
- package/dist/codecs/av1/decode.js +1769 -0
- package/dist/codecs/av1/filmgrain.js +219 -0
- package/dist/codecs/av1/frame.js +114 -0
- package/dist/codecs/av1/ipred.js +491 -0
- package/dist/codecs/av1/itx.js +950 -0
- package/dist/codecs/av1/loopfilter.js +149 -0
- package/dist/codecs/av1/msac.js +161 -0
- package/dist/codecs/av1/obu.js +516 -0
- package/dist/codecs/av1/postfilter.js +28 -0
- package/dist/codecs/av1/restoration.js +193 -0
- package/dist/codecs/av1/tables.js +160 -0
- package/dist/codecs/avif.js +581 -0
- package/dist/codecs/bmp.js +317 -0
- package/dist/codecs/crc.js +5 -0
- package/dist/codecs/gif.js +10 -35
- package/dist/codecs/hdr.d.ts +25 -0
- package/dist/codecs/hdr.js +201 -0
- package/dist/codecs/ico.js +48 -0
- package/dist/codecs/jpeg.js +11 -28
- package/dist/codecs/jxl/bits.js +126 -0
- package/dist/codecs/jxl/blend.js +117 -0
- package/dist/codecs/jxl/color.js +116 -0
- package/dist/codecs/jxl/dct.js +404 -0
- package/dist/codecs/jxl/entropy.js +556 -0
- package/dist/codecs/jxl/features.js +325 -0
- package/dist/codecs/jxl/filters.js +119 -0
- package/dist/codecs/jxl/frame-header.js +195 -0
- package/dist/codecs/jxl/frame.js +319 -0
- package/dist/codecs/jxl/headers.js +249 -0
- package/dist/codecs/jxl/idct.js +484 -0
- package/dist/codecs/jxl/modular.js +688 -0
- package/dist/codecs/jxl/quant.js +219 -0
- package/dist/codecs/jxl/upsample.js +52 -0
- package/dist/codecs/jxl/vardct.js +472 -0
- package/dist/codecs/jxl.js +89 -0
- package/dist/codecs/netpbm.js +239 -0
- package/dist/codecs/png.js +22 -10
- package/dist/codecs/qoi.js +150 -0
- package/dist/codecs/tga.js +3 -1
- package/dist/codecs/tiff.js +7 -2
- package/dist/codecs/vp8-enc-dsp.js +932 -0
- package/dist/codecs/vp8-enc-tables.js +78 -0
- package/dist/codecs/vp8-enc.js +1930 -0
- package/dist/codecs/vp8.js +3 -1
- package/dist/codecs/vp8l.js +126 -58
- package/dist/codecs/webp.js +47 -11
- package/dist/codecs/zlib.js +81 -69
- package/dist/frame.d.ts +1 -0
- package/dist/frame.js +4 -2
- package/dist/gif.d.ts +1 -0
- package/dist/gif.js +2 -2
- package/dist/image-type.d.ts +15 -1
- package/dist/image-type.js +45 -2
- package/dist/image.d.ts +89 -13
- package/dist/image.js +266 -113
- package/dist/index.d.ts +3 -2
- package/dist/index.js +4 -4
- package/dist/render/bidi.js +350 -0
- package/dist/render/buffer.js +362 -0
- package/dist/render/color.js +164 -0
- package/dist/render/colr.js +523 -0
- package/dist/render/font.js +173 -241
- package/dist/render/hinting.js +38 -22
- package/dist/render/layout.js +1553 -0
- package/dist/render/linebreak.js +210 -0
- package/dist/render/segment.js +106 -0
- package/dist/render/shape.js +984 -0
- package/dist/render/shapers/arabic.js +284 -0
- package/dist/render/shapers/hangul.js +142 -0
- package/dist/render/shapers/hebrew.js +49 -0
- package/dist/render/shapers/index.js +52 -0
- package/dist/render/shapers/indic.js +579 -0
- package/dist/render/shapers/khmer.js +104 -0
- package/dist/render/shapers/machine.js +129 -0
- package/dist/render/shapers/myanmar.js +121 -0
- package/dist/render/shapers/syllabic.js +100 -0
- package/dist/render/shapers/thai.js +125 -0
- package/dist/render/shapers/use.js +235 -0
- package/dist/render/svg.js +102 -23
- package/dist/render/text.js +373 -103
- package/dist/render/unicode-data.js +63 -0
- package/dist/render/unicode.js +50 -0
- package/dist/text-layout.d.ts +4 -1
- package/dist/text-layout.js +2 -2
- package/dist/util/alpha.js +43 -0
- package/dist/util/blur.js +20 -33
- package/dist/util/bytes.js +40 -0
- package/dist/util/limits.js +34 -0
- package/dist/util/options.js +7 -0
- package/dist/util/resample.js +126 -7
- package/dist/util/rotate.js +43 -0
- package/dist/version.js +1 -1
- package/licenses/HarfBuzz.txt +42 -0
- package/licenses/Unicode-3.0.txt +39 -0
- package/package.json +25 -2
package/dist/codecs/vp8.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { KF_BMODE_PROBS, COEFF_UPDATE_PROBS, DEFAULT_COEFF_PROBS, DC_QLOOKUP, AC_QLOOKUP, PCAT } from "./vp8-tables.js";
|
|
2
|
+
import { MAX_PIXELS, checkPixels } from "../util/limits.js";
|
|
2
3
|
class BoolDecoder {
|
|
3
4
|
data;
|
|
4
5
|
pos;
|
|
@@ -161,9 +162,10 @@ function readHeader(data) {
|
|
|
161
162
|
};
|
|
162
163
|
}
|
|
163
164
|
const clamp255 = (v) => v < 0 ? 0 : v > 255 ? 255 : v;
|
|
164
|
-
export function decode(data) {
|
|
165
|
+
export function decode(data, maxPixels = MAX_PIXELS) {
|
|
165
166
|
const { header: h, first: d } = readHeader(data);
|
|
166
167
|
const { width, height } = h;
|
|
168
|
+
checkPixels(width, height, maxPixels);
|
|
167
169
|
const mbw = (width + 15) >> 4, mbh = (height + 15) >> 4;
|
|
168
170
|
const ys = mbw * 16 + 1, cs = mbw * 8 + 1;
|
|
169
171
|
const Y = new Uint8Array(ys * (mbh * 16 + 1));
|
package/dist/codecs/vp8l.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* table of short distance codes. Copyright (c) 2024 IETF Trust and the persons identified as the document authors,
|
|
4
4
|
* Revised BSD License (see THIRD_PARTY_NOTICES.md)
|
|
5
5
|
*/
|
|
6
|
+
import { ByteWriter } from "../util/bytes.js";
|
|
7
|
+
import { MAX_PIXELS, checkPixels } from "../util/limits.js";
|
|
6
8
|
const SIGNATURE = 0x2f;
|
|
7
9
|
const CODE_LENGTH_ORDER = [17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
|
|
8
10
|
const DISTANCE_MAP = Int8Array.of(0, 1, 1, 0, 1, 1, -1, 1, 0, 2, 2, 0, 1, 2, -1, 2, 2, 1, -2, 1, 2, 2, -2, 2, 0, 3, 3, 0, 1, 3, -1, 3, 3, 1, -3, 1, 2, 3, -2, 3, 3, 2, -3, 2, 0, 4, 4, 0, 1, 4, -1, 4, 4, 1, -4, 1, 3, 3, -3, 3, 2, 4, -2, 4, 4, 2, -4, 2, 0, 5, 3, 4, -3, 4, 4, 3, -4, 3, 5, 0, 1, 5, -1, 5, 5, 1, -5, 1, 2, 5, -2, 5, 5, 2, -5, 2, 4, 4, -4, 4, 3, 5, -3, 5, 5, 3, -5, 3, 0, 6, 6, 0, 1, 6, -1, 6, 6, 1, -6, 1, 2, 6, -2, 6, 6, 2, -6, 2, 4, 5, -4, 5, 5, 4, -5, 4, 3, 6, -3, 6, 6, 3, -6, 3, 0, 7, 7, 0, 1, 7, -1, 7, 5, 5, -5, 5, 7, 1, -7, 1, 4, 6, -4, 6, 6, 4, -6, 4, 2, 7, -2, 7, 7, 2, -7, 2, 3, 7, -3, 7, 7, 3, -7, 3, 5, 6, -5, 6, 6, 5, -6, 5, 8, 0, 4, 7, -4, 7, 7, 4, -7, 4, 8, 1, 8, 2, 6, 6, -6, 6, 8, 3, 5, 7, -5, 7, 7, 5, -7, 5, 8, 4, 6, 7, -6, 7, 7, 6, -7, 6, 8, 5, 7, 7, -7, 7, 8, 6, 8, 7);
|
|
@@ -314,24 +316,82 @@ function select(l, t, tl) {
|
|
|
314
316
|
return pl < pt ? l : t;
|
|
315
317
|
}
|
|
316
318
|
const addPixels = (a, b) => ((a & 0xff00ff00) + (b & 0xff00ff00) & 0xff00ff00 | (a & 0x00ff00ff) + (b & 0x00ff00ff) & 0x00ff00ff) >>> 0;
|
|
317
|
-
function
|
|
318
|
-
const
|
|
319
|
+
function predictRun(mode, p, i, n, width, out, o) {
|
|
320
|
+
const restore = out === p;
|
|
321
|
+
const end = i + n;
|
|
319
322
|
switch (mode) {
|
|
320
|
-
case
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
case
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
case
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
case
|
|
333
|
-
|
|
334
|
-
|
|
323
|
+
case 1:
|
|
324
|
+
for (; i < end; i++, o++)
|
|
325
|
+
out[o] = restore ? addPixels(p[i], p[i - 1]) : p[i - 1];
|
|
326
|
+
return;
|
|
327
|
+
case 2:
|
|
328
|
+
for (; i < end; i++, o++)
|
|
329
|
+
out[o] = restore ? addPixels(p[i], p[i - width]) : p[i - width];
|
|
330
|
+
return;
|
|
331
|
+
case 3:
|
|
332
|
+
for (; i < end; i++, o++)
|
|
333
|
+
out[o] = restore ? addPixels(p[i], p[i - width + 1]) : p[i - width + 1];
|
|
334
|
+
return;
|
|
335
|
+
case 4:
|
|
336
|
+
for (; i < end; i++, o++)
|
|
337
|
+
out[o] = restore ? addPixels(p[i], p[i - width - 1]) : p[i - width - 1];
|
|
338
|
+
return;
|
|
339
|
+
case 5:
|
|
340
|
+
for (; i < end; i++, o++) {
|
|
341
|
+
const v = average2(average2(p[i - 1], p[i - width + 1]), p[i - width]);
|
|
342
|
+
out[o] = restore ? addPixels(p[i], v) : v;
|
|
343
|
+
}
|
|
344
|
+
return;
|
|
345
|
+
case 6:
|
|
346
|
+
for (; i < end; i++, o++) {
|
|
347
|
+
const v = average2(p[i - 1], p[i - width - 1]);
|
|
348
|
+
out[o] = restore ? addPixels(p[i], v) : v;
|
|
349
|
+
}
|
|
350
|
+
return;
|
|
351
|
+
case 7:
|
|
352
|
+
for (; i < end; i++, o++) {
|
|
353
|
+
const v = average2(p[i - 1], p[i - width]);
|
|
354
|
+
out[o] = restore ? addPixels(p[i], v) : v;
|
|
355
|
+
}
|
|
356
|
+
return;
|
|
357
|
+
case 8:
|
|
358
|
+
for (; i < end; i++, o++) {
|
|
359
|
+
const v = average2(p[i - width - 1], p[i - width]);
|
|
360
|
+
out[o] = restore ? addPixels(p[i], v) : v;
|
|
361
|
+
}
|
|
362
|
+
return;
|
|
363
|
+
case 9:
|
|
364
|
+
for (; i < end; i++, o++) {
|
|
365
|
+
const v = average2(p[i - width], p[i - width + 1]);
|
|
366
|
+
out[o] = restore ? addPixels(p[i], v) : v;
|
|
367
|
+
}
|
|
368
|
+
return;
|
|
369
|
+
case 10:
|
|
370
|
+
for (; i < end; i++, o++) {
|
|
371
|
+
const v = average2(average2(p[i - 1], p[i - width - 1]), average2(p[i - width], p[i - width + 1]));
|
|
372
|
+
out[o] = restore ? addPixels(p[i], v) : v;
|
|
373
|
+
}
|
|
374
|
+
return;
|
|
375
|
+
case 11:
|
|
376
|
+
for (; i < end; i++, o++) {
|
|
377
|
+
const v = select(p[i - 1], p[i - width], p[i - width - 1]);
|
|
378
|
+
out[o] = restore ? addPixels(p[i], v) : v;
|
|
379
|
+
}
|
|
380
|
+
return;
|
|
381
|
+
case 12:
|
|
382
|
+
for (; i < end; i++, o++) {
|
|
383
|
+
const v = clampAddSubtractFull(p[i - 1], p[i - width], p[i - width - 1]);
|
|
384
|
+
out[o] = restore ? addPixels(p[i], v) : v;
|
|
385
|
+
}
|
|
386
|
+
return;
|
|
387
|
+
case 13:
|
|
388
|
+
for (; i < end; i++, o++) {
|
|
389
|
+
const v = clampAddSubtractHalf(average2(p[i - 1], p[i - width]), p[i - width - 1]);
|
|
390
|
+
out[o] = restore ? addPixels(p[i], v) : v;
|
|
391
|
+
}
|
|
392
|
+
return;
|
|
393
|
+
default: for (; i < end; i++, o++)
|
|
394
|
+
out[o] = restore ? addPixels(p[i], 0xff000000) : 0xff000000;
|
|
335
395
|
}
|
|
336
396
|
}
|
|
337
397
|
function inversePredictor(p, width, height, bits, modes) {
|
|
@@ -343,10 +403,10 @@ function inversePredictor(p, width, height, bits, modes) {
|
|
|
343
403
|
const row = y * width;
|
|
344
404
|
p[row] = addPixels(p[row], p[row - width]);
|
|
345
405
|
const modeRow = (y >> bits) * blocksPerRow;
|
|
346
|
-
for (let x = 1; x < width;
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
406
|
+
for (let x = 1; x < width;) {
|
|
407
|
+
const end = Math.min(width, (x >> bits) + 1 << bits);
|
|
408
|
+
predictRun(modes[modeRow + (x >> bits)] >> 8 & 15, p, row + x, end - x, width, p, row + x);
|
|
409
|
+
x = end;
|
|
350
410
|
}
|
|
351
411
|
}
|
|
352
412
|
}
|
|
@@ -400,8 +460,9 @@ export function readHeader(data) {
|
|
|
400
460
|
throw new Error('VP8L: unsupported version');
|
|
401
461
|
return { width, height, hasAlpha };
|
|
402
462
|
}
|
|
403
|
-
export function decode(data) {
|
|
463
|
+
export function decode(data, maxPixels = MAX_PIXELS) {
|
|
404
464
|
const { width, height, hasAlpha } = readHeader(data);
|
|
465
|
+
checkPixels(width, height, maxPixels);
|
|
405
466
|
const reader = new BitReader(data, 5);
|
|
406
467
|
return { width, height, hasAlpha, argb: decodeStream(reader, width, height) };
|
|
407
468
|
}
|
|
@@ -446,26 +507,22 @@ export function decodeStream(reader, width, height) {
|
|
|
446
507
|
}
|
|
447
508
|
return pixels;
|
|
448
509
|
}
|
|
449
|
-
class BitWriter {
|
|
450
|
-
|
|
451
|
-
length = 0;
|
|
452
|
-
buffer = 0;
|
|
510
|
+
class BitWriter extends ByteWriter {
|
|
511
|
+
bits = 0;
|
|
453
512
|
count = 0;
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
513
|
+
constructor() {
|
|
514
|
+
super(1 << 16);
|
|
515
|
+
}
|
|
516
|
+
write(value, size) {
|
|
517
|
+
while (size > 0) {
|
|
518
|
+
const take = Math.min(size, 24 - this.count);
|
|
519
|
+
this.bits |= (value & (1 << take) - 1) << this.count;
|
|
458
520
|
this.count += take;
|
|
459
521
|
value >>>= take;
|
|
460
|
-
|
|
522
|
+
size -= take;
|
|
461
523
|
while (this.count >= 8) {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
grown.set(this.bytes);
|
|
465
|
-
this.bytes = grown;
|
|
466
|
-
}
|
|
467
|
-
this.bytes[this.length++] = this.buffer & 255;
|
|
468
|
-
this.buffer >>>= 8;
|
|
524
|
+
this.byte(this.bits & 255);
|
|
525
|
+
this.bits >>>= 8;
|
|
469
526
|
this.count -= 8;
|
|
470
527
|
}
|
|
471
528
|
}
|
|
@@ -473,7 +530,7 @@ class BitWriter {
|
|
|
473
530
|
finish() {
|
|
474
531
|
if (this.count > 0)
|
|
475
532
|
this.write(0, 8 - this.count);
|
|
476
|
-
return
|
|
533
|
+
return super.finish();
|
|
477
534
|
}
|
|
478
535
|
}
|
|
479
536
|
function codeLengths(counts, maxLength) {
|
|
@@ -686,11 +743,13 @@ function tokenize(pixels, width, cacheBits) {
|
|
|
686
743
|
const HASH_BITS = 15, CHAIN = 24, MAX_LENGTH = 4096, MIN_LENGTH = 3, WINDOW = (1 << 20) - 120;
|
|
687
744
|
const head = new Int32Array(1 << HASH_BITS).fill(-1);
|
|
688
745
|
const chain = new Int32Array(n);
|
|
689
|
-
const
|
|
746
|
+
const hashes = new Int32Array(n);
|
|
747
|
+
for (let i = 0; i + 1 < n; i++)
|
|
748
|
+
hashes[i] = Math.imul(Math.imul(pixels[i], 0x1e35a7bd) ^ pixels[i + 1], 0x9e3779b1) >>> 32 - HASH_BITS;
|
|
690
749
|
const insert = (i) => {
|
|
691
750
|
if (i + 1 >= n)
|
|
692
751
|
return;
|
|
693
|
-
const h =
|
|
752
|
+
const h = hashes[i];
|
|
694
753
|
chain[i] = head[h];
|
|
695
754
|
head[h] = i;
|
|
696
755
|
};
|
|
@@ -721,7 +780,7 @@ function tokenize(pixels, width, cacheBits) {
|
|
|
721
780
|
}
|
|
722
781
|
}
|
|
723
782
|
if (i + 1 < n && bestLength < MAX_LENGTH) {
|
|
724
|
-
for (let j = head[
|
|
783
|
+
for (let j = head[hashes[i]], k = 0; j >= 0 && k < CHAIN && i - j <= WINDOW; j = chain[j], k++) {
|
|
725
784
|
if (i - j >= bestDistance && (i + bestLength >= n || pixels[j + bestLength] !== pixels[i + bestLength]))
|
|
726
785
|
continue;
|
|
727
786
|
const l = matchLength(i, j);
|
|
@@ -810,38 +869,47 @@ function writeImage(w, pixels, width, isMain) {
|
|
|
810
869
|
}
|
|
811
870
|
}
|
|
812
871
|
const subPixels = (a, b) => ((a | 0x00ff00ff) - (b & 0xff00ff00) & 0xff00ff00 | (a | 0xff00ff00) - (b & 0x00ff00ff) & 0x00ff00ff) >>> 0;
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
return (c0 < 128 ? c0 : 256 - c0) + (c1 < 128 ? c1 : 256 - c1) + (c2 < 128 ? c2 : 256 - c2) + (c3 < 128 ? c3 : 256 - c3);
|
|
816
|
-
}
|
|
872
|
+
const BYTE_COST = Uint8Array.from({ length: 256 }, (_, c) => c < 128 ? c : 256 - c);
|
|
873
|
+
const residualCost = (r) => BYTE_COST[r & 255] + BYTE_COST[r >>> 8 & 255] + BYTE_COST[r >>> 16 & 255] + BYTE_COST[r >>> 24];
|
|
817
874
|
function applyPredictor(p, width, height, bits) {
|
|
818
875
|
const blocksX = subSize(width, bits), blocksY = subSize(height, bits);
|
|
819
876
|
const modes = new Uint32Array(blocksX * blocksY);
|
|
820
877
|
const size = 1 << bits;
|
|
821
|
-
const src = p.slice();
|
|
822
|
-
const
|
|
878
|
+
const src = p.slice(), predicted = new Uint32Array(size);
|
|
879
|
+
const predictRow = (mode, x0, x1, y) => {
|
|
880
|
+
const row = y * width;
|
|
881
|
+
if (y === 0) {
|
|
882
|
+
for (let x = x0; x < x1; x++)
|
|
883
|
+
predicted[x - x0] = x === 0 ? 0xff000000 : src[row + x - 1];
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
let x = x0;
|
|
887
|
+
if (x === 0)
|
|
888
|
+
predicted[x++] = src[row - width];
|
|
889
|
+
predictRun(mode, src, row + x, x1 - x, width, predicted, x - x0);
|
|
890
|
+
};
|
|
823
891
|
for (let by = 0; by < blocksY; by++)
|
|
824
892
|
for (let bx = 0; bx < blocksX; bx++) {
|
|
825
893
|
let best = 0, bestCost = Infinity;
|
|
826
894
|
const x0 = bx * size, y0 = by * size, x1 = Math.min(width, x0 + size), y1 = Math.min(height, y0 + size);
|
|
827
895
|
for (let mode = 0; mode < 14; mode++) {
|
|
828
896
|
let cost = 0;
|
|
829
|
-
for (let y = y0; y < y1 && cost < bestCost; y++)
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
cost += residualCost(subPixels(src[i],
|
|
833
|
-
|
|
897
|
+
for (let y = y0; y < y1 && cost < bestCost; y++) {
|
|
898
|
+
predictRow(mode, x0, x1, y);
|
|
899
|
+
for (let x = x0, i = y * width + x0; x < x1; x++, i++)
|
|
900
|
+
cost += residualCost(subPixels(src[i], predicted[x - x0]));
|
|
901
|
+
}
|
|
834
902
|
if (cost < bestCost) {
|
|
835
903
|
bestCost = cost;
|
|
836
904
|
best = mode;
|
|
837
905
|
}
|
|
838
906
|
}
|
|
839
907
|
modes[by * blocksX + bx] = (0xff000000 | best << 8) >>> 0;
|
|
840
|
-
for (let y = y0; y < y1; y++)
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
p[i] = subPixels(src[i],
|
|
844
|
-
|
|
908
|
+
for (let y = y0; y < y1; y++) {
|
|
909
|
+
predictRow(best, x0, x1, y);
|
|
910
|
+
for (let x = x0, i = y * width + x0; x < x1; x++, i++)
|
|
911
|
+
p[i] = subPixels(src[i], predicted[x - x0]);
|
|
912
|
+
}
|
|
845
913
|
}
|
|
846
914
|
return modes;
|
|
847
915
|
}
|
package/dist/codecs/webp.js
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import * as vp8l from "./vp8l.js";
|
|
6
6
|
import * as vp8 from "./vp8.js";
|
|
7
|
+
import { encodeVP8, encodeAlpha } from "./vp8-enc.js";
|
|
8
|
+
import { rgbaToYUV } from "./vp8-enc-dsp.js";
|
|
7
9
|
import { orientation as exifOrientation } from "./exif.js";
|
|
10
|
+
import { MAX_PIXELS, checkPixels } from "../util/limits.js";
|
|
8
11
|
const fourCC = (s) => s.charCodeAt(0) | s.charCodeAt(1) << 8 | s.charCodeAt(2) << 16 | s.charCodeAt(3) << 24;
|
|
9
12
|
const RIFF = fourCC('RIFF'), WEBP = fourCC('WEBP');
|
|
10
13
|
const VP8 = fourCC('VP8 '), VP8L = fourCC('VP8L'), VP8X = fourCC('VP8X');
|
|
@@ -32,13 +35,13 @@ function container(data) {
|
|
|
32
35
|
return chunks(data, 12, Math.min(data.length, 8 + size));
|
|
33
36
|
}
|
|
34
37
|
const u24 = (d, o) => d[o] | d[o + 1] << 8 | d[o + 2] << 16;
|
|
35
|
-
function decodeBitstream(list) {
|
|
38
|
+
function decodeBitstream(list, maxPixels) {
|
|
36
39
|
let alpha = null;
|
|
37
40
|
for (const chunk of list) {
|
|
38
41
|
if (chunk.type === ALPH)
|
|
39
42
|
alpha ??= chunk;
|
|
40
43
|
else if (chunk.type === VP8L) {
|
|
41
|
-
const { width, height, argb } = vp8l.decode(chunk.data);
|
|
44
|
+
const { width, height, argb } = vp8l.decode(chunk.data, maxPixels);
|
|
42
45
|
const pixels = new Uint8Array(width * height * 4);
|
|
43
46
|
const out = new DataView(pixels.buffer);
|
|
44
47
|
for (let i = 0; i < argb.length; i++) {
|
|
@@ -48,7 +51,7 @@ function decodeBitstream(list) {
|
|
|
48
51
|
return { width, height, pixels };
|
|
49
52
|
}
|
|
50
53
|
else if (chunk.type === VP8) {
|
|
51
|
-
const image = vp8.decode(chunk.data);
|
|
54
|
+
const image = vp8.decode(chunk.data, maxPixels);
|
|
52
55
|
if (alpha)
|
|
53
56
|
applyAlpha(image.pixels, image.width, image.height, alpha.data);
|
|
54
57
|
return image;
|
|
@@ -109,7 +112,7 @@ export function isAnimated(data) {
|
|
|
109
112
|
const header = list[0];
|
|
110
113
|
return header && header.type === VP8X && header.data.length >= 10 && (header.data[0] & 2) !== 0 && list.some(c => c.type === ANMF);
|
|
111
114
|
}
|
|
112
|
-
export function decode(data) {
|
|
115
|
+
export function decode(data, maxPixels = MAX_PIXELS) {
|
|
113
116
|
const list = container(data);
|
|
114
117
|
if (!list.length)
|
|
115
118
|
throw new Error('WebP: no image data');
|
|
@@ -118,11 +121,11 @@ export function decode(data) {
|
|
|
118
121
|
if (exif)
|
|
119
122
|
orientation = exifOrientation(exif.data);
|
|
120
123
|
if (list.some(c => c.type === ANMF)) {
|
|
121
|
-
const animation = decodeAnimation(data, true);
|
|
124
|
+
const animation = decodeAnimation(data, true, maxPixels);
|
|
122
125
|
const frame = composite(animation)[0];
|
|
123
126
|
return { width: animation.width, height: animation.height, pixels: frame.pixels, orientation };
|
|
124
127
|
}
|
|
125
|
-
const image = decodeBitstream(list);
|
|
128
|
+
const image = decodeBitstream(list, maxPixels);
|
|
126
129
|
if (list[0].type === VP8X && list[0].data.length >= 10) {
|
|
127
130
|
const width = u24(list[0].data, 4) + 1, height = u24(list[0].data, 7) + 1;
|
|
128
131
|
if (width !== image.width || height !== image.height)
|
|
@@ -130,16 +133,17 @@ export function decode(data) {
|
|
|
130
133
|
}
|
|
131
134
|
return { ...image, orientation };
|
|
132
135
|
}
|
|
133
|
-
export function decodeAnimation(data, onlyFirstFrame = false) {
|
|
136
|
+
export function decodeAnimation(data, onlyFirstFrame = false, maxPixels = MAX_PIXELS) {
|
|
134
137
|
const list = container(data);
|
|
135
138
|
const header = list[0];
|
|
136
139
|
if (!header || header.type !== VP8X || !list.some(c => c.type === ANMF)) {
|
|
137
|
-
const { width, height, pixels } = decodeBitstream(list);
|
|
140
|
+
const { width, height, pixels } = decodeBitstream(list, maxPixels);
|
|
138
141
|
return { width, height, loopCount: 0, background: 0, frames: [{ x: 0, y: 0, width, height, duration: 0, blend: false, dispose: false, pixels }] };
|
|
139
142
|
}
|
|
140
143
|
if (header.data.length < 10)
|
|
141
144
|
throw new Error('WebP: invalid VP8X chunk');
|
|
142
145
|
const width = u24(header.data, 4) + 1, height = u24(header.data, 7) + 1;
|
|
146
|
+
checkPixels(width, height, maxPixels, onlyFirstFrame ? 1 : list.filter(c => c.type === ANMF).length);
|
|
143
147
|
let loopCount = 0, background = 0;
|
|
144
148
|
const frames = [];
|
|
145
149
|
for (const chunk of list) {
|
|
@@ -152,7 +156,7 @@ export function decodeAnimation(data, onlyFirstFrame = false) {
|
|
|
152
156
|
const d = chunk.data;
|
|
153
157
|
if (d.length < 16)
|
|
154
158
|
throw new Error('WebP: invalid ANMF chunk');
|
|
155
|
-
const frame = decodeBitstream(chunks(d, 16, d.length));
|
|
159
|
+
const frame = decodeBitstream(chunks(d, 16, d.length), maxPixels);
|
|
156
160
|
frames.push({
|
|
157
161
|
x: u24(d, 0) * 2,
|
|
158
162
|
y: u24(d, 3) * 2,
|
|
@@ -216,6 +220,15 @@ function riffChunk(type, body) {
|
|
|
216
220
|
out.set(body, 8);
|
|
217
221
|
return out;
|
|
218
222
|
}
|
|
223
|
+
function concat(parts) {
|
|
224
|
+
const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
|
|
225
|
+
let offset = 0;
|
|
226
|
+
for (const p of parts) {
|
|
227
|
+
out.set(p, offset);
|
|
228
|
+
offset += p.length;
|
|
229
|
+
}
|
|
230
|
+
return out;
|
|
231
|
+
}
|
|
219
232
|
function riff(chunks) {
|
|
220
233
|
let size = 4;
|
|
221
234
|
for (const c of chunks)
|
|
@@ -255,7 +268,29 @@ export function encode(rgba, width, height, exif) {
|
|
|
255
268
|
set24(vp8x, 7, height - 1);
|
|
256
269
|
return riff([riffChunk('VP8X', vp8x), bitstream, riffChunk('EXIF', exif)]);
|
|
257
270
|
}
|
|
258
|
-
export function
|
|
271
|
+
export function encodeLossy(rgba, width, height, quality, exif, method = 4) {
|
|
272
|
+
const { alpha, chunks } = lossyChunks(rgba, width, height, quality, method);
|
|
273
|
+
if (!alpha && !exif)
|
|
274
|
+
return riff(chunks);
|
|
275
|
+
const vp8x = new Uint8Array(10);
|
|
276
|
+
vp8x[0] = (exif ? 8 : 0) | (alpha ? 16 : 0);
|
|
277
|
+
set24(vp8x, 4, width - 1);
|
|
278
|
+
set24(vp8x, 7, height - 1);
|
|
279
|
+
return riff([riffChunk('VP8X', vp8x), ...chunks, ...exif ? [riffChunk('EXIF', exif)] : []]);
|
|
280
|
+
}
|
|
281
|
+
function lossyChunks(rgba, width, height, quality, method) {
|
|
282
|
+
if (width > 16383 || height > 16383)
|
|
283
|
+
throw new RangeError('WebP: lossy images can be at most 16383 pixels wide and high');
|
|
284
|
+
const picture = rgbaToYUV(rgba, width, height);
|
|
285
|
+
const frame = encodeVP8(picture, quality, method);
|
|
286
|
+
const padded = new Uint8Array(frame.length + (frame.length & 1));
|
|
287
|
+
padded.set(frame);
|
|
288
|
+
const chunks = [riffChunk('VP8 ', padded)];
|
|
289
|
+
if (picture.a)
|
|
290
|
+
chunks.unshift(riffChunk('ALPH', encodeAlpha(picture.a, width, height, method)));
|
|
291
|
+
return { alpha: !!picture.a, chunks };
|
|
292
|
+
}
|
|
293
|
+
export function encodeAnimation(frames, width, height, plays, exif, quality, method = 4) {
|
|
259
294
|
if (!frames.length)
|
|
260
295
|
throw new Error('WebP: an animation needs at least one frame');
|
|
261
296
|
if (width > 1 << 24 || height > 1 << 24)
|
|
@@ -274,7 +309,8 @@ export function encodeAnimation(frames, width, height, plays, exif) {
|
|
|
274
309
|
for (const f of frames) {
|
|
275
310
|
if (f.x & 1 || f.y & 1)
|
|
276
311
|
throw new RangeError('WebP: frame offsets have to be even');
|
|
277
|
-
const bitstream = riffChunk('VP8L', vp8l.encode(toARGB(f.pixels, f.width * f.height), f.width, f.height))
|
|
312
|
+
const bitstream = quality === undefined ? riffChunk('VP8L', vp8l.encode(toARGB(f.pixels, f.width * f.height), f.width, f.height))
|
|
313
|
+
: concat(lossyChunks(f.pixels, f.width, f.height, quality, method).chunks);
|
|
278
314
|
const header = new Uint8Array(16);
|
|
279
315
|
set24(header, 0, f.x >> 1);
|
|
280
316
|
set24(header, 3, f.y >> 1);
|
package/dist/codecs/zlib.js
CHANGED
|
@@ -94,75 +94,73 @@ function inflate(dat, buf) {
|
|
|
94
94
|
return buf.subarray(0, 0);
|
|
95
95
|
const tbts = sl * 8;
|
|
96
96
|
let final = 0, pos = 0, bt = 0;
|
|
97
|
-
let lm = null, dm = null, lbt = 0, dbt = 0;
|
|
98
97
|
do {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
else if (s === 18) {
|
|
148
|
-
n = 11 + bits(dat, pos, 127);
|
|
149
|
-
pos += 7;
|
|
150
|
-
}
|
|
151
|
-
while (n--)
|
|
152
|
-
ldt[i++] = c;
|
|
98
|
+
let lm, dm, lbt, dbt;
|
|
99
|
+
final = bits(dat, pos, 1);
|
|
100
|
+
const type = bits(dat, pos + 1, 3);
|
|
101
|
+
pos += 3;
|
|
102
|
+
if (!type) {
|
|
103
|
+
const s = shft(pos) + 4, l = dat[s - 4] | dat[s - 3] << 8, t = s + l;
|
|
104
|
+
if (t > sl)
|
|
105
|
+
throw new Error('zlib: unexpected end of data');
|
|
106
|
+
if (bt < buf.length)
|
|
107
|
+
buf.set(dat.subarray(s, Math.min(t, s + buf.length - bt)), bt);
|
|
108
|
+
bt += l;
|
|
109
|
+
pos = t * 8;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
else if (type === 1) {
|
|
113
|
+
lm = flrm;
|
|
114
|
+
dm = fdrm;
|
|
115
|
+
lbt = 9;
|
|
116
|
+
dbt = 5;
|
|
117
|
+
}
|
|
118
|
+
else if (type === 2) {
|
|
119
|
+
const hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;
|
|
120
|
+
const tl = hLit + bits(dat, pos + 5, 31) + 1;
|
|
121
|
+
pos += 14;
|
|
122
|
+
const ldt = new u8(tl);
|
|
123
|
+
const clt = new u8(19);
|
|
124
|
+
for (let i = 0; i < hcLen; ++i)
|
|
125
|
+
clt[clim[i]] = bits(dat, pos + i * 3, 7);
|
|
126
|
+
pos += hcLen * 3;
|
|
127
|
+
const clb = max(clt), clbmsk = (1 << clb) - 1;
|
|
128
|
+
const clm = hMap(clt, clb, true);
|
|
129
|
+
for (let i = 0; i < tl;) {
|
|
130
|
+
const r = clm[bits(dat, pos, clbmsk)];
|
|
131
|
+
pos += r & 15;
|
|
132
|
+
const s = r >> 4;
|
|
133
|
+
if (s < 16)
|
|
134
|
+
ldt[i++] = s;
|
|
135
|
+
else {
|
|
136
|
+
let c = 0, n = 0;
|
|
137
|
+
if (s === 16) {
|
|
138
|
+
n = 3 + bits(dat, pos, 3);
|
|
139
|
+
pos += 2;
|
|
140
|
+
c = ldt[i - 1];
|
|
141
|
+
}
|
|
142
|
+
else if (s === 17) {
|
|
143
|
+
n = 3 + bits(dat, pos, 7);
|
|
144
|
+
pos += 3;
|
|
153
145
|
}
|
|
146
|
+
else if (s === 18) {
|
|
147
|
+
n = 11 + bits(dat, pos, 127);
|
|
148
|
+
pos += 7;
|
|
149
|
+
}
|
|
150
|
+
while (n--)
|
|
151
|
+
ldt[i++] = c;
|
|
154
152
|
}
|
|
155
|
-
const lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
|
|
156
|
-
lbt = max(lt);
|
|
157
|
-
dbt = max(dt);
|
|
158
|
-
lm = hMap(lt, lbt, true);
|
|
159
|
-
dm = hMap(dt, dbt, true);
|
|
160
153
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
154
|
+
const lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
|
|
155
|
+
lbt = max(lt);
|
|
156
|
+
dbt = max(dt);
|
|
157
|
+
lm = hMap(lt, lbt, true);
|
|
158
|
+
dm = hMap(dt, dbt, true);
|
|
165
159
|
}
|
|
160
|
+
else
|
|
161
|
+
throw new Error('zlib: invalid block type');
|
|
162
|
+
if (pos > tbts)
|
|
163
|
+
throw new Error('zlib: unexpected end of data');
|
|
166
164
|
const lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;
|
|
167
165
|
for (;;) {
|
|
168
166
|
const c = lm[bits16(dat, pos) & lms], sym = c >> 4;
|
|
@@ -173,10 +171,8 @@ function inflate(dat, buf) {
|
|
|
173
171
|
throw new Error('zlib: invalid length/literal');
|
|
174
172
|
if (sym < 256)
|
|
175
173
|
buf[bt++] = sym;
|
|
176
|
-
else if (sym === 256)
|
|
177
|
-
lm = null;
|
|
174
|
+
else if (sym === 256)
|
|
178
175
|
break;
|
|
179
|
-
}
|
|
180
176
|
else {
|
|
181
177
|
let add = sym - 254;
|
|
182
178
|
if (sym > 264) {
|
|
@@ -557,8 +553,8 @@ export function decompress(data, limit) {
|
|
|
557
553
|
throw new Error('zlib: preset dictionaries are not supported');
|
|
558
554
|
return inflate(data.subarray(2), new u8(limit));
|
|
559
555
|
}
|
|
560
|
-
const { process: runtime, DecompressionStream: NativeDecompression, Blob: NativeBlob } = globalThis;
|
|
561
|
-
const nodeZlib = (() => {
|
|
556
|
+
const { process: runtime, CompressionStream: NativeCompression, DecompressionStream: NativeDecompression, Blob: NativeBlob } = globalThis;
|
|
557
|
+
export const nodeZlib = (() => {
|
|
562
558
|
try {
|
|
563
559
|
return (runtime && runtime.getBuiltinModule && runtime.getBuiltinModule('node:zlib')) || null;
|
|
564
560
|
}
|
|
@@ -571,6 +567,22 @@ export async function compressAsync(data, level) {
|
|
|
571
567
|
level = Math.min(9, Math.max(0, level | 0));
|
|
572
568
|
if (nodeZlib)
|
|
573
569
|
return plain(nodeZlib.deflateSync(data, { level }));
|
|
570
|
+
if (NativeCompression && NativeBlob && level >= 1 && level <= 6) {
|
|
571
|
+
const reader = new NativeBlob([data]).stream().pipeThrough(new NativeCompression('deflate')).getReader();
|
|
572
|
+
const parts = [];
|
|
573
|
+
let length = 0;
|
|
574
|
+
for (;;) {
|
|
575
|
+
const { done, value } = await reader.read();
|
|
576
|
+
if (done || !value)
|
|
577
|
+
break;
|
|
578
|
+
parts.push(value);
|
|
579
|
+
length += value.length;
|
|
580
|
+
}
|
|
581
|
+
const out = new Uint8Array(length);
|
|
582
|
+
for (let i = 0, offset = 0; i < parts.length; offset += parts[i++].length)
|
|
583
|
+
out.set(parts[i], offset);
|
|
584
|
+
return out;
|
|
585
|
+
}
|
|
574
586
|
return compress(data, level);
|
|
575
587
|
}
|
|
576
588
|
export async function decompressAsync(data, limit) {
|
package/dist/frame.d.ts
CHANGED
|
@@ -52,6 +52,7 @@ export declare class Frame extends Image {
|
|
|
52
52
|
get disposalMode(): FrameDisposalModeName;
|
|
53
53
|
/** Sets the disposal mode by name or id (0-3) */
|
|
54
54
|
set disposalMode(disposalMode: FrameDisposalModeName | FrameDisposalModeId);
|
|
55
|
+
/** A short description for debugging, e.g. `Frame<64x64x100ms>` */
|
|
55
56
|
toString(): string;
|
|
56
57
|
/**
|
|
57
58
|
* Converts an Image instance to a Frame, cloning it in the process
|
package/dist/frame.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Image } from "./image.js";
|
|
2
|
-
import { isOptions } from "./util/options.js";
|
|
2
|
+
import { isOptions, finite } from "./util/options.js";
|
|
3
3
|
const DISPOSAL_MODES = ['any', 'keep', 'previous', 'background'];
|
|
4
4
|
export class Frame extends Image {
|
|
5
5
|
duration;
|
|
@@ -26,8 +26,10 @@ export class Frame extends Image {
|
|
|
26
26
|
}
|
|
27
27
|
constructor(width, height, durationOrOptions, xOffset, yOffset, disposalMode) {
|
|
28
28
|
const { duration = 100, xOffset: x = 0, yOffset: y = 0, disposalMode: disposal = Frame.DISPOSAL_KEEP } = isOptions(durationOrOptions) ? durationOrOptions : { duration: durationOrOptions, xOffset, yOffset, disposalMode };
|
|
29
|
-
if (
|
|
29
|
+
if (finite(duration, 'frame duration') < 0)
|
|
30
30
|
throw new RangeError('Invalid frame duration');
|
|
31
|
+
finite(x, 'x offset');
|
|
32
|
+
finite(y, 'y offset');
|
|
31
33
|
super(width, height);
|
|
32
34
|
this.duration = duration;
|
|
33
35
|
this.xOffset = x;
|
package/dist/gif.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare class GIF extends Animation {
|
|
|
15
15
|
* {@link AnimationOptions}
|
|
16
16
|
*/
|
|
17
17
|
constructor(frames: Frame[], loopCountOrOptions?: number | AnimationOptions);
|
|
18
|
+
/** A short description for debugging, e.g. `GIF<64x64x1000ms>` */
|
|
18
19
|
toString(): string;
|
|
19
20
|
/**
|
|
20
21
|
* Encodes the GIF
|