imagescript 3.2.0 → 3.3.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 +8 -3
- package/THIRD_PARTY_NOTICES.md +131 -0
- package/dist/animation.d.ts +7 -0
- package/dist/animation.js +22 -12
- package/dist/codecs/crc.js +5 -0
- package/dist/codecs/gif.js +30 -52
- package/dist/codecs/jpeg.js +9 -27
- package/dist/codecs/png.js +22 -10
- package/dist/codecs/tga.js +3 -1
- package/dist/codecs/tiff.js +7 -2
- package/dist/codecs/vp8.js +3 -1
- package/dist/codecs/vp8l.js +126 -58
- package/dist/codecs/webp.js +11 -9
- 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.d.ts +42 -7
- package/dist/image.js +224 -113
- 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 +12 -4
- 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 +101 -22
- package/dist/render/text.js +373 -103
- package/dist/render/unicode-data.js +63 -0
- package/dist/render/unicode.js +58 -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 +80 -5
- package/dist/util/bytes.js +31 -0
- package/dist/util/limits.js +34 -0
- package/dist/util/options.js +7 -0
- package/dist/util/resample.js +359 -79
- 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 +1 -1
package/dist/codecs/tiff.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { decompressAsync } from "./zlib.js";
|
|
2
2
|
import * as jpeg from "./jpeg.js";
|
|
3
|
+
import { MAX_PIXELS, checkPixels, checkBytes } from "../util/limits.js";
|
|
3
4
|
const TYPE_SIZES = { 1: 1, 2: 1, 3: 2, 4: 4, 5: 8, 6: 1, 7: 1, 8: 2, 9: 4, 10: 8, 11: 4, 12: 8, 13: 4, 16: 8, 17: 8, 18: 8 };
|
|
4
5
|
const TAGS = {
|
|
5
6
|
ImageWidth: 256,
|
|
@@ -28,7 +29,7 @@ const TAGS = {
|
|
|
28
29
|
ReferenceBlackWhite: 532
|
|
29
30
|
};
|
|
30
31
|
const COMPRESSION_NAMES = { 2: 'CCITT RLE', 3: 'CCITT fax 3', 4: 'CCITT fax 4', 6: 'old-style JPEG', 32809: 'ThunderScan', 34676: 'SGI LogLuv', 34677: 'SGI LogL' };
|
|
31
|
-
export async function decode(data) {
|
|
32
|
+
export async function decode(data, maxPixels = MAX_PIXELS) {
|
|
32
33
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
33
34
|
let little;
|
|
34
35
|
if (data[0] === 0x49 && data[1] === 0x49)
|
|
@@ -154,9 +155,13 @@ export async function decode(data) {
|
|
|
154
155
|
throw new Error('TIFF: missing image data');
|
|
155
156
|
return data.subarray(offset, Math.min(data.length, offset + count));
|
|
156
157
|
};
|
|
158
|
+
checkPixels(width, height, maxPixels);
|
|
159
|
+
checkPixels(segmentWidth, segmentHeight, Math.max(4 * width * height, 1 << 20));
|
|
160
|
+
checkBytes(Math.ceil(width * samplesPerPixel * bps / 8) * height, maxPixels);
|
|
157
161
|
const pixels = new Uint8Array(width * height * 4);
|
|
158
162
|
if (compression === 7) {
|
|
159
|
-
const
|
|
163
|
+
const jpegTables = tags.get(TAGS.JPEGTables);
|
|
164
|
+
const tables = jpegTables ? data.subarray(jpegTables.offset, jpegTables.offset + jpegTables.count) : null;
|
|
160
165
|
const transform = photometric === 6 ? 'ycc' : photometric === 2 ? 'rgb' : photometric === 5 ? 'cmyk' : null;
|
|
161
166
|
for (let s = 0; s < segmentsPerPlane; s++) {
|
|
162
167
|
let stream = segment(s);
|
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
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import * as vp8l from "./vp8l.js";
|
|
6
6
|
import * as vp8 from "./vp8.js";
|
|
7
7
|
import { orientation as exifOrientation } from "./exif.js";
|
|
8
|
+
import { MAX_PIXELS, checkPixels } from "../util/limits.js";
|
|
8
9
|
const fourCC = (s) => s.charCodeAt(0) | s.charCodeAt(1) << 8 | s.charCodeAt(2) << 16 | s.charCodeAt(3) << 24;
|
|
9
10
|
const RIFF = fourCC('RIFF'), WEBP = fourCC('WEBP');
|
|
10
11
|
const VP8 = fourCC('VP8 '), VP8L = fourCC('VP8L'), VP8X = fourCC('VP8X');
|
|
@@ -32,13 +33,13 @@ function container(data) {
|
|
|
32
33
|
return chunks(data, 12, Math.min(data.length, 8 + size));
|
|
33
34
|
}
|
|
34
35
|
const u24 = (d, o) => d[o] | d[o + 1] << 8 | d[o + 2] << 16;
|
|
35
|
-
function decodeBitstream(list) {
|
|
36
|
+
function decodeBitstream(list, maxPixels) {
|
|
36
37
|
let alpha = null;
|
|
37
38
|
for (const chunk of list) {
|
|
38
39
|
if (chunk.type === ALPH)
|
|
39
40
|
alpha ??= chunk;
|
|
40
41
|
else if (chunk.type === VP8L) {
|
|
41
|
-
const { width, height, argb } = vp8l.decode(chunk.data);
|
|
42
|
+
const { width, height, argb } = vp8l.decode(chunk.data, maxPixels);
|
|
42
43
|
const pixels = new Uint8Array(width * height * 4);
|
|
43
44
|
const out = new DataView(pixels.buffer);
|
|
44
45
|
for (let i = 0; i < argb.length; i++) {
|
|
@@ -48,7 +49,7 @@ function decodeBitstream(list) {
|
|
|
48
49
|
return { width, height, pixels };
|
|
49
50
|
}
|
|
50
51
|
else if (chunk.type === VP8) {
|
|
51
|
-
const image = vp8.decode(chunk.data);
|
|
52
|
+
const image = vp8.decode(chunk.data, maxPixels);
|
|
52
53
|
if (alpha)
|
|
53
54
|
applyAlpha(image.pixels, image.width, image.height, alpha.data);
|
|
54
55
|
return image;
|
|
@@ -109,7 +110,7 @@ export function isAnimated(data) {
|
|
|
109
110
|
const header = list[0];
|
|
110
111
|
return header && header.type === VP8X && header.data.length >= 10 && (header.data[0] & 2) !== 0 && list.some(c => c.type === ANMF);
|
|
111
112
|
}
|
|
112
|
-
export function decode(data) {
|
|
113
|
+
export function decode(data, maxPixels = MAX_PIXELS) {
|
|
113
114
|
const list = container(data);
|
|
114
115
|
if (!list.length)
|
|
115
116
|
throw new Error('WebP: no image data');
|
|
@@ -118,11 +119,11 @@ export function decode(data) {
|
|
|
118
119
|
if (exif)
|
|
119
120
|
orientation = exifOrientation(exif.data);
|
|
120
121
|
if (list.some(c => c.type === ANMF)) {
|
|
121
|
-
const animation = decodeAnimation(data, true);
|
|
122
|
+
const animation = decodeAnimation(data, true, maxPixels);
|
|
122
123
|
const frame = composite(animation)[0];
|
|
123
124
|
return { width: animation.width, height: animation.height, pixels: frame.pixels, orientation };
|
|
124
125
|
}
|
|
125
|
-
const image = decodeBitstream(list);
|
|
126
|
+
const image = decodeBitstream(list, maxPixels);
|
|
126
127
|
if (list[0].type === VP8X && list[0].data.length >= 10) {
|
|
127
128
|
const width = u24(list[0].data, 4) + 1, height = u24(list[0].data, 7) + 1;
|
|
128
129
|
if (width !== image.width || height !== image.height)
|
|
@@ -130,16 +131,17 @@ export function decode(data) {
|
|
|
130
131
|
}
|
|
131
132
|
return { ...image, orientation };
|
|
132
133
|
}
|
|
133
|
-
export function decodeAnimation(data, onlyFirstFrame = false) {
|
|
134
|
+
export function decodeAnimation(data, onlyFirstFrame = false, maxPixels = MAX_PIXELS) {
|
|
134
135
|
const list = container(data);
|
|
135
136
|
const header = list[0];
|
|
136
137
|
if (!header || header.type !== VP8X || !list.some(c => c.type === ANMF)) {
|
|
137
|
-
const { width, height, pixels } = decodeBitstream(list);
|
|
138
|
+
const { width, height, pixels } = decodeBitstream(list, maxPixels);
|
|
138
139
|
return { width, height, loopCount: 0, background: 0, frames: [{ x: 0, y: 0, width, height, duration: 0, blend: false, dispose: false, pixels }] };
|
|
139
140
|
}
|
|
140
141
|
if (header.data.length < 10)
|
|
141
142
|
throw new Error('WebP: invalid VP8X chunk');
|
|
142
143
|
const width = u24(header.data, 4) + 1, height = u24(header.data, 7) + 1;
|
|
144
|
+
checkPixels(width, height, maxPixels, onlyFirstFrame ? 1 : list.filter(c => c.type === ANMF).length);
|
|
143
145
|
let loopCount = 0, background = 0;
|
|
144
146
|
const frames = [];
|
|
145
147
|
for (const chunk of list) {
|
|
@@ -152,7 +154,7 @@ export function decodeAnimation(data, onlyFirstFrame = false) {
|
|
|
152
154
|
const d = chunk.data;
|
|
153
155
|
if (d.length < 16)
|
|
154
156
|
throw new Error('WebP: invalid ANMF chunk');
|
|
155
|
-
const frame = decodeBitstream(chunks(d, 16, d.length));
|
|
157
|
+
const frame = decodeBitstream(chunks(d, 16, d.length), maxPixels);
|
|
156
158
|
frames.push({
|
|
157
159
|
x: u24(d, 0) * 2,
|
|
158
160
|
y: u24(d, 3) * 2,
|
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
|
package/dist/gif.js
CHANGED
|
@@ -13,10 +13,10 @@ export class GIF extends Animation {
|
|
|
13
13
|
return this.encodeGIF(qualityOrOptions);
|
|
14
14
|
}
|
|
15
15
|
static async decode(input, onlyFirstFrameOrOptions = false) {
|
|
16
|
-
const
|
|
16
|
+
const { onlyFirstFrame = false, maxPixels } = isOptions(onlyFirstFrameOrOptions) ? onlyFirstFrameOrOptions : { onlyFirstFrame: onlyFirstFrameOrOptions };
|
|
17
17
|
const data = view(input);
|
|
18
18
|
if (!ImageType.isGIF(new DataView(data.buffer, data.byteOffset, data.byteLength)))
|
|
19
19
|
throw new Error('Unsupported image type');
|
|
20
|
-
return Animation.__fromGIF__(data,
|
|
20
|
+
return Animation.__fromGIF__(data, onlyFirstFrame, GIF, maxPixels);
|
|
21
21
|
}
|
|
22
22
|
}
|