cross-image 0.2.3 → 0.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 (105) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +292 -74
  3. package/esm/mod.d.ts +6 -4
  4. package/esm/mod.js +4 -2
  5. package/esm/src/formats/apng.d.ts +17 -5
  6. package/esm/src/formats/apng.js +104 -9
  7. package/esm/src/formats/ascii.d.ts +13 -3
  8. package/esm/src/formats/ascii.js +25 -1
  9. package/esm/src/formats/avif.d.ts +96 -0
  10. package/esm/src/formats/avif.js +607 -0
  11. package/esm/src/formats/bmp.d.ts +13 -3
  12. package/esm/src/formats/bmp.js +75 -2
  13. package/esm/src/formats/dng.d.ts +14 -2
  14. package/esm/src/formats/dng.js +27 -5
  15. package/esm/src/formats/gif.d.ts +18 -5
  16. package/esm/src/formats/gif.js +160 -14
  17. package/esm/src/formats/heic.d.ts +96 -0
  18. package/esm/src/formats/heic.js +608 -0
  19. package/esm/src/formats/ico.d.ts +13 -3
  20. package/esm/src/formats/ico.js +32 -4
  21. package/esm/src/formats/jpeg.d.ts +10 -3
  22. package/esm/src/formats/jpeg.js +99 -11
  23. package/esm/src/formats/pam.d.ts +13 -3
  24. package/esm/src/formats/pam.js +68 -2
  25. package/esm/src/formats/pcx.d.ts +13 -3
  26. package/esm/src/formats/pcx.js +47 -2
  27. package/esm/src/formats/png.d.ts +15 -3
  28. package/esm/src/formats/png.js +89 -2
  29. package/esm/src/formats/png_base.js +2 -5
  30. package/esm/src/formats/ppm.d.ts +13 -3
  31. package/esm/src/formats/ppm.js +36 -2
  32. package/esm/src/formats/tiff.d.ts +14 -18
  33. package/esm/src/formats/tiff.js +219 -20
  34. package/esm/src/formats/webp.d.ts +10 -3
  35. package/esm/src/formats/webp.js +103 -8
  36. package/esm/src/image.d.ts +20 -3
  37. package/esm/src/image.js +65 -21
  38. package/esm/src/types.d.ts +74 -4
  39. package/esm/src/utils/gif_decoder.d.ts +4 -1
  40. package/esm/src/utils/gif_decoder.js +91 -65
  41. package/esm/src/utils/image_processing.js +144 -70
  42. package/esm/src/utils/jpeg_decoder.d.ts +17 -4
  43. package/esm/src/utils/jpeg_decoder.js +448 -83
  44. package/esm/src/utils/jpeg_encoder.d.ts +15 -1
  45. package/esm/src/utils/jpeg_encoder.js +263 -24
  46. package/esm/src/utils/resize.js +51 -20
  47. package/esm/src/utils/tiff_deflate.d.ts +18 -0
  48. package/esm/src/utils/tiff_deflate.js +27 -0
  49. package/esm/src/utils/tiff_packbits.d.ts +24 -0
  50. package/esm/src/utils/tiff_packbits.js +90 -0
  51. package/esm/src/utils/webp_decoder.d.ts +3 -1
  52. package/esm/src/utils/webp_decoder.js +144 -63
  53. package/esm/src/utils/webp_encoder.js +5 -11
  54. package/package.json +18 -1
  55. package/script/mod.d.ts +6 -4
  56. package/script/mod.js +7 -3
  57. package/script/src/formats/apng.d.ts +17 -5
  58. package/script/src/formats/apng.js +104 -9
  59. package/script/src/formats/ascii.d.ts +13 -3
  60. package/script/src/formats/ascii.js +25 -1
  61. package/script/src/formats/avif.d.ts +96 -0
  62. package/script/src/formats/avif.js +611 -0
  63. package/script/src/formats/bmp.d.ts +13 -3
  64. package/script/src/formats/bmp.js +75 -2
  65. package/script/src/formats/dng.d.ts +14 -2
  66. package/script/src/formats/dng.js +27 -5
  67. package/script/src/formats/gif.d.ts +18 -5
  68. package/script/src/formats/gif.js +160 -14
  69. package/script/src/formats/heic.d.ts +96 -0
  70. package/script/src/formats/heic.js +612 -0
  71. package/script/src/formats/ico.d.ts +13 -3
  72. package/script/src/formats/ico.js +32 -4
  73. package/script/src/formats/jpeg.d.ts +10 -3
  74. package/script/src/formats/jpeg.js +99 -11
  75. package/script/src/formats/pam.d.ts +13 -3
  76. package/script/src/formats/pam.js +68 -2
  77. package/script/src/formats/pcx.d.ts +13 -3
  78. package/script/src/formats/pcx.js +47 -2
  79. package/script/src/formats/png.d.ts +15 -3
  80. package/script/src/formats/png.js +89 -2
  81. package/script/src/formats/png_base.js +2 -5
  82. package/script/src/formats/ppm.d.ts +13 -3
  83. package/script/src/formats/ppm.js +36 -2
  84. package/script/src/formats/tiff.d.ts +14 -18
  85. package/script/src/formats/tiff.js +219 -20
  86. package/script/src/formats/webp.d.ts +10 -3
  87. package/script/src/formats/webp.js +103 -8
  88. package/script/src/image.d.ts +20 -3
  89. package/script/src/image.js +64 -20
  90. package/script/src/types.d.ts +74 -4
  91. package/script/src/utils/gif_decoder.d.ts +4 -1
  92. package/script/src/utils/gif_decoder.js +91 -65
  93. package/script/src/utils/image_processing.js +144 -70
  94. package/script/src/utils/jpeg_decoder.d.ts +17 -4
  95. package/script/src/utils/jpeg_decoder.js +448 -83
  96. package/script/src/utils/jpeg_encoder.d.ts +15 -1
  97. package/script/src/utils/jpeg_encoder.js +263 -24
  98. package/script/src/utils/resize.js +51 -20
  99. package/script/src/utils/tiff_deflate.d.ts +18 -0
  100. package/script/src/utils/tiff_deflate.js +31 -0
  101. package/script/src/utils/tiff_packbits.d.ts +24 -0
  102. package/script/src/utils/tiff_packbits.js +94 -0
  103. package/script/src/utils/webp_decoder.d.ts +3 -1
  104. package/script/src/utils/webp_decoder.js +144 -63
  105. package/script/src/utils/webp_encoder.js +5 -11
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ /**
3
+ * PackBits compression/decompression for TIFF
4
+ * PackBits is a simple run-length encoding (RLE) scheme used in TIFF images
5
+ * Compression code: 32773
6
+ *
7
+ * Format:
8
+ * - Header byte n:
9
+ * - If n >= 0 and n <= 127: Copy the next n+1 bytes literally
10
+ * - If n >= -127 and n <= -1: Repeat the next byte -n+1 times
11
+ * - If n = -128: No operation (skip)
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.packBitsCompress = packBitsCompress;
15
+ exports.packBitsDecompress = packBitsDecompress;
16
+ /**
17
+ * Compress data using PackBits RLE
18
+ * @param data Uncompressed data
19
+ * @returns Compressed data
20
+ */
21
+ function packBitsCompress(data) {
22
+ const result = [];
23
+ let i = 0;
24
+ while (i < data.length) {
25
+ // Look for runs (repeated bytes)
26
+ let runLength = 1;
27
+ while (i + runLength < data.length &&
28
+ data[i + runLength] === data[i] &&
29
+ runLength < 128) {
30
+ runLength++;
31
+ }
32
+ // If we have a run of 2 or more, encode it as a run
33
+ if (runLength >= 2) {
34
+ result.push(-(runLength - 1) & 0xff); // Two's complement representation
35
+ result.push(data[i]);
36
+ i += runLength;
37
+ }
38
+ else {
39
+ // Look for literals (non-repeating bytes)
40
+ const literalStart = i;
41
+ let literalLength = 1;
42
+ while (i + literalLength < data.length &&
43
+ literalLength < 128) {
44
+ // Check if we're starting a run
45
+ if (i + literalLength + 1 < data.length &&
46
+ data[i + literalLength] === data[i + literalLength + 1]) {
47
+ // We found a run, stop the literal sequence here
48
+ break;
49
+ }
50
+ literalLength++;
51
+ }
52
+ result.push(literalLength - 1);
53
+ for (let j = 0; j < literalLength; j++) {
54
+ result.push(data[literalStart + j]);
55
+ }
56
+ i += literalLength;
57
+ }
58
+ }
59
+ return new Uint8Array(result);
60
+ }
61
+ /**
62
+ * Decompress PackBits RLE data
63
+ * @param data Compressed data
64
+ * @returns Decompressed data
65
+ */
66
+ function packBitsDecompress(data) {
67
+ const result = [];
68
+ let i = 0;
69
+ while (i < data.length) {
70
+ const header = data[i++];
71
+ if (header === 128) {
72
+ // No operation, skip
73
+ continue;
74
+ }
75
+ else if (header < 128) {
76
+ // Literal run: copy next (header + 1) bytes
77
+ const count = header + 1;
78
+ for (let j = 0; j < count && i < data.length; j++) {
79
+ result.push(data[i++]);
80
+ }
81
+ }
82
+ else {
83
+ // Repeated run: repeat next byte (257 - header) times
84
+ const count = 257 - header;
85
+ if (i < data.length) {
86
+ const value = data[i++];
87
+ for (let j = 0; j < count; j++) {
88
+ result.push(value);
89
+ }
90
+ }
91
+ }
92
+ }
93
+ return new Uint8Array(result);
94
+ }
@@ -19,9 +19,11 @@
19
19
  * @see https://developers.google.com/speed/webp/docs/riff_container
20
20
  * @see https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification
21
21
  */
22
+ import type { ImageDecoderOptions } from "../types.js";
22
23
  export declare class WebPDecoder {
23
24
  private data;
24
- constructor(data: Uint8Array);
25
+ private options;
26
+ constructor(data: Uint8Array, settings?: ImageDecoderOptions);
25
27
  decode(): {
26
28
  width: number;
27
29
  height: number;
@@ -96,7 +96,6 @@ class HuffmanTable {
96
96
  const bit = reader.readBits(1);
97
97
  node = bit === 0 ? node.left : node.right;
98
98
  if (!node) {
99
- // console.log("Invalid Huffman code - walked off tree");
100
99
  throw new Error("Invalid Huffman code");
101
100
  }
102
101
  }
@@ -167,14 +166,24 @@ class BitReader {
167
166
  }
168
167
  }
169
168
  class WebPDecoder {
170
- constructor(data) {
169
+ constructor(data, settings = {}) {
171
170
  Object.defineProperty(this, "data", {
172
171
  enumerable: true,
173
172
  configurable: true,
174
173
  writable: true,
175
174
  value: void 0
176
175
  });
176
+ Object.defineProperty(this, "options", {
177
+ enumerable: true,
178
+ configurable: true,
179
+ writable: true,
180
+ value: void 0
181
+ });
177
182
  this.data = data;
183
+ this.options = {
184
+ tolerantDecoding: settings.tolerantDecoding ?? true,
185
+ onWarning: settings.onWarning,
186
+ };
178
187
  }
179
188
  decode() {
180
189
  // Verify WebP signature
@@ -281,66 +290,142 @@ class WebPDecoder {
281
290
  const numPixels = width * height;
282
291
  // Color cache for repeated colors
283
292
  const colorCache = new Uint32Array(colorCacheSize);
284
- for (let i = 0; i < numPixels;) {
285
- // Read green channel (which determines the code type)
286
- const green = huffmanTables.green.readSymbol(reader);
287
- if (green < 256) {
288
- // Literal pixel
289
- const red = huffmanTables.red.readSymbol(reader);
290
- const blue = huffmanTables.blue.readSymbol(reader);
291
- const alpha = alphaUsed !== 0
292
- ? huffmanTables.alpha.readSymbol(reader)
293
- : 255;
294
- pixelData[pixelIndex++] = red;
295
- pixelData[pixelIndex++] = green;
296
- pixelData[pixelIndex++] = blue;
297
- pixelData[pixelIndex++] = alpha;
298
- // Add to color cache if enabled
299
- if (useColorCache) {
300
- const color = (alpha << 24) | (blue << 16) | (green << 8) | red;
301
- colorCache[i % colorCacheSize] = color;
293
+ if (this.options.tolerantDecoding) {
294
+ // Tolerant mode: continue decoding even if errors occur
295
+ try {
296
+ for (let i = 0; i < numPixels;) {
297
+ // Read green channel (which determines the code type)
298
+ const green = huffmanTables.green.readSymbol(reader);
299
+ if (green < 256) {
300
+ // Literal pixel
301
+ const red = huffmanTables.red.readSymbol(reader);
302
+ const blue = huffmanTables.blue.readSymbol(reader);
303
+ const alpha = alphaUsed !== 0 ? huffmanTables.alpha.readSymbol(reader) : 255;
304
+ pixelData[pixelIndex++] = red;
305
+ pixelData[pixelIndex++] = green;
306
+ pixelData[pixelIndex++] = blue;
307
+ pixelData[pixelIndex++] = alpha;
308
+ // Add to color cache if enabled
309
+ if (useColorCache) {
310
+ const color = (alpha << 24) | (blue << 16) | (green << 8) | red;
311
+ colorCache[i % colorCacheSize] = color;
312
+ }
313
+ i++;
314
+ }
315
+ else if (green < 256 + 24) {
316
+ // Backward reference (LZ77)
317
+ const lengthSymbol = green - 256;
318
+ const length = this.getLength(lengthSymbol, reader);
319
+ const distancePrefix = huffmanTables.distance.readSymbol(reader);
320
+ const distance = this.getDistance(distancePrefix, reader);
321
+ // Copy pixels from earlier in the stream
322
+ const srcIndex = pixelIndex - distance * 4;
323
+ if (srcIndex < 0) {
324
+ throw new Error("Invalid backward reference");
325
+ }
326
+ for (let j = 0; j < length; j++) {
327
+ pixelData[pixelIndex++] = pixelData[srcIndex + j * 4];
328
+ pixelData[pixelIndex++] = pixelData[srcIndex + j * 4 + 1];
329
+ pixelData[pixelIndex++] = pixelData[srcIndex + j * 4 + 2];
330
+ pixelData[pixelIndex++] = pixelData[srcIndex + j * 4 + 3];
331
+ // Add to color cache
332
+ if (useColorCache) {
333
+ const color = (pixelData[pixelIndex - 1] << 24) |
334
+ (pixelData[pixelIndex - 2] << 16) |
335
+ (pixelData[pixelIndex - 3] << 8) |
336
+ pixelData[pixelIndex - 4];
337
+ colorCache[(i + j) % colorCacheSize] = color;
338
+ }
339
+ }
340
+ i += length;
341
+ }
342
+ else {
343
+ // Color cache reference
344
+ const cacheIndex = green - 256 - 24;
345
+ if (cacheIndex >= colorCacheSize) {
346
+ throw new Error("Invalid color cache index");
347
+ }
348
+ const color = colorCache[cacheIndex];
349
+ pixelData[pixelIndex++] = color & 0xff; // R
350
+ pixelData[pixelIndex++] = (color >> 8) & 0xff; // G
351
+ pixelData[pixelIndex++] = (color >> 16) & 0xff; // B
352
+ pixelData[pixelIndex++] = (color >> 24) & 0xff; // A
353
+ i++;
354
+ }
302
355
  }
303
- i++;
304
356
  }
305
- else if (green < 256 + 24) {
306
- // Backward reference (LZ77)
307
- const lengthSymbol = green - 256;
308
- const length = this.getLength(lengthSymbol, reader);
309
- const distancePrefix = huffmanTables.distance.readSymbol(reader);
310
- const distance = this.getDistance(distancePrefix, reader);
311
- // Copy pixels from earlier in the stream
312
- const srcIndex = pixelIndex - distance * 4;
313
- if (srcIndex < 0) {
314
- throw new Error("Invalid backward reference");
357
+ catch (e) {
358
+ // Tolerant decoding: fill remaining pixels with gray (128, 128, 128, 255)
359
+ this.options.onWarning?.(`WebP VP8L: Partial decode at pixel ${pixelIndex / 4}/${numPixels}`, e);
360
+ while (pixelIndex < pixelData.length) {
361
+ pixelData[pixelIndex++] = 128; // R
362
+ pixelData[pixelIndex++] = 128; // G
363
+ pixelData[pixelIndex++] = 128; // B
364
+ pixelData[pixelIndex++] = 255; // A
315
365
  }
316
- for (let j = 0; j < length; j++) {
317
- pixelData[pixelIndex++] = pixelData[srcIndex + j * 4];
318
- pixelData[pixelIndex++] = pixelData[srcIndex + j * 4 + 1];
319
- pixelData[pixelIndex++] = pixelData[srcIndex + j * 4 + 2];
320
- pixelData[pixelIndex++] = pixelData[srcIndex + j * 4 + 3];
321
- // Add to color cache
366
+ }
367
+ }
368
+ else {
369
+ // Non-tolerant mode: throw on first error
370
+ for (let i = 0; i < numPixels;) {
371
+ // Read green channel (which determines the code type)
372
+ const green = huffmanTables.green.readSymbol(reader);
373
+ if (green < 256) {
374
+ // Literal pixel
375
+ const red = huffmanTables.red.readSymbol(reader);
376
+ const blue = huffmanTables.blue.readSymbol(reader);
377
+ const alpha = alphaUsed !== 0 ? huffmanTables.alpha.readSymbol(reader) : 255;
378
+ pixelData[pixelIndex++] = red;
379
+ pixelData[pixelIndex++] = green;
380
+ pixelData[pixelIndex++] = blue;
381
+ pixelData[pixelIndex++] = alpha;
382
+ // Add to color cache if enabled
322
383
  if (useColorCache) {
323
- const color = (pixelData[pixelIndex - 1] << 24) |
324
- (pixelData[pixelIndex - 2] << 16) |
325
- (pixelData[pixelIndex - 3] << 8) |
326
- pixelData[pixelIndex - 4];
327
- colorCache[(i + j) % colorCacheSize] = color;
384
+ const color = (alpha << 24) | (blue << 16) | (green << 8) | red;
385
+ colorCache[i % colorCacheSize] = color;
328
386
  }
387
+ i++;
329
388
  }
330
- i += length;
331
- }
332
- else {
333
- // Color cache reference
334
- const cacheIndex = green - 256 - 24;
335
- if (cacheIndex >= colorCacheSize) {
336
- throw new Error("Invalid color cache index");
389
+ else if (green < 256 + 24) {
390
+ // Backward reference (LZ77)
391
+ const lengthSymbol = green - 256;
392
+ const length = this.getLength(lengthSymbol, reader);
393
+ const distancePrefix = huffmanTables.distance.readSymbol(reader);
394
+ const distance = this.getDistance(distancePrefix, reader);
395
+ // Copy pixels from earlier in the stream
396
+ const srcIndex = pixelIndex - distance * 4;
397
+ if (srcIndex < 0) {
398
+ throw new Error("Invalid backward reference");
399
+ }
400
+ for (let j = 0; j < length; j++) {
401
+ pixelData[pixelIndex++] = pixelData[srcIndex + j * 4];
402
+ pixelData[pixelIndex++] = pixelData[srcIndex + j * 4 + 1];
403
+ pixelData[pixelIndex++] = pixelData[srcIndex + j * 4 + 2];
404
+ pixelData[pixelIndex++] = pixelData[srcIndex + j * 4 + 3];
405
+ // Add to color cache
406
+ if (useColorCache) {
407
+ const color = (pixelData[pixelIndex - 1] << 24) |
408
+ (pixelData[pixelIndex - 2] << 16) |
409
+ (pixelData[pixelIndex - 3] << 8) |
410
+ pixelData[pixelIndex - 4];
411
+ colorCache[(i + j) % colorCacheSize] = color;
412
+ }
413
+ }
414
+ i += length;
415
+ }
416
+ else {
417
+ // Color cache reference
418
+ const cacheIndex = green - 256 - 24;
419
+ if (cacheIndex >= colorCacheSize) {
420
+ throw new Error("Invalid color cache index");
421
+ }
422
+ const color = colorCache[cacheIndex];
423
+ pixelData[pixelIndex++] = color & 0xff; // R
424
+ pixelData[pixelIndex++] = (color >> 8) & 0xff; // G
425
+ pixelData[pixelIndex++] = (color >> 16) & 0xff; // B
426
+ pixelData[pixelIndex++] = (color >> 24) & 0xff; // A
427
+ i++;
337
428
  }
338
- const color = colorCache[cacheIndex];
339
- pixelData[pixelIndex++] = color & 0xff; // R
340
- pixelData[pixelIndex++] = (color >> 8) & 0xff; // G
341
- pixelData[pixelIndex++] = (color >> 16) & 0xff; // B
342
- pixelData[pixelIndex++] = (color >> 24) & 0xff; // A
343
- i++;
344
429
  }
345
430
  }
346
431
  return pixelData;
@@ -381,9 +466,7 @@ class WebPDecoder {
381
466
  const isFirstEightBits = reader.readBits(1);
382
467
  const symbols = [];
383
468
  for (let i = 0; i < numSymbols; i++) {
384
- const symbolBits = isFirstEightBits
385
- ? reader.readBits(8)
386
- : reader.readBits(1);
469
+ const symbolBits = isFirstEightBits ? reader.readBits(8) : reader.readBits(1);
387
470
  symbols.push(symbolBits);
388
471
  }
389
472
  // Build simple Huffman table
@@ -399,9 +482,7 @@ class WebPDecoder {
399
482
  }
400
483
  else {
401
484
  // Complex code - read code lengths
402
- const maxSymbol = isGreen
403
- ? (256 + 24 + (useColorCache ? (1 << colorCacheBits) : 0))
404
- : 256;
485
+ const maxSymbol = isGreen ? (256 + 24 + (useColorCache ? (1 << colorCacheBits) : 0)) : 256;
405
486
  const codeLengths = this.readCodeLengths(reader, maxSymbol);
406
487
  this.buildHuffmanTable(table, codeLengths);
407
488
  }
@@ -409,7 +490,7 @@ class WebPDecoder {
409
490
  readCodeLengths(reader, maxSymbol) {
410
491
  // Read code length codes (used to encode the actual code lengths)
411
492
  const numCodeLengthCodes = reader.readBits(4) + 4;
412
- const codeLengthCodeLengths = new Array(19).fill(0);
493
+ const codeLengthCodeLengths = new Uint8Array(19);
413
494
  // Code length code order
414
495
  const codeLengthCodeOrder = [
415
496
  17,
@@ -445,7 +526,7 @@ class WebPDecoder {
445
526
  const codeLengthTable = new HuffmanTable();
446
527
  this.buildHuffmanTable(codeLengthTable, codeLengthCodeLengths);
447
528
  // Read actual code lengths
448
- const codeLengths = new Array(maxSymbol).fill(0);
529
+ const codeLengths = new Uint8Array(maxSymbol);
449
530
  let i = 0;
450
531
  while (i < maxSymbol) {
451
532
  const code = codeLengthTable.readSymbol(reader);
@@ -372,7 +372,7 @@ class WebPEncoder {
372
372
  * Returns an array where index is the symbol and value is the code length
373
373
  */
374
374
  calculateCodeLengths(frequencies, maxSymbol, maxCodeLength = 15) {
375
- const codeLengths = new Array(maxSymbol).fill(0);
375
+ const codeLengths = new Uint8Array(maxSymbol);
376
376
  // Get symbols with non-zero frequencies
377
377
  const symbols = Array.from(frequencies.keys()).sort((a, b) => a - b);
378
378
  if (symbols.length === 0)
@@ -380,7 +380,6 @@ class WebPEncoder {
380
380
  // For a single symbol, use code length 1
381
381
  // (Canonical Huffman codes require length >= 1)
382
382
  if (symbols.length === 1) {
383
- // console.log(`Single symbol ${symbols[0]}, forcing length 1`);
384
383
  codeLengths[symbols[0]] = 1;
385
384
  return codeLengths;
386
385
  }
@@ -427,7 +426,6 @@ class WebPEncoder {
427
426
  // If tree is too deep, flatten frequencies and rebuild
428
427
  let attempts = 0;
429
428
  while (maxDepth > maxCodeLength && attempts < 5) {
430
- // console.log(`Tree too deep (${maxDepth} > ${maxCodeLength}), flattening...`);
431
429
  attempts++;
432
430
  // Add bias to frequencies to flatten the tree
433
431
  // Increase bias with each attempt
@@ -442,11 +440,8 @@ class WebPEncoder {
442
440
  maxDepth = 0;
443
441
  checkDepth(root, 0);
444
442
  }
445
- if (maxDepth > maxCodeLength) {
446
- console.warn(`Failed to reduce Huffman tree depth to ${maxCodeLength} (current: ${maxDepth})`);
447
- // Force hard limit by sorting and assigning lengths?
448
- // For now, let's just see if this is happening.
449
- }
443
+ // If tree depth couldn't be reduced, encoding may fail but we'll try anyway
444
+ // This is an internal limitation that doesn't affect most images
450
445
  // Calculate code lengths by traversing tree (iterative to avoid deep recursion)
451
446
  const stack = [{
452
447
  node: root,
@@ -488,7 +483,7 @@ class WebPEncoder {
488
483
  }
489
484
  }
490
485
  // Count symbols at each length
491
- const lengthCounts = new Array(maxLength + 1).fill(0);
486
+ const lengthCounts = new Uint32Array(maxLength + 1);
492
487
  for (let i = 0; i < codeLengths.length; i++) {
493
488
  if (codeLengths[i] > 0) {
494
489
  lengthCounts[codeLengths[i]]++;
@@ -496,7 +491,7 @@ class WebPEncoder {
496
491
  }
497
492
  // Generate first code for each length
498
493
  let code = 0;
499
- const nextCode = new Array(maxLength + 1).fill(0);
494
+ const nextCode = new Uint32Array(maxLength + 1);
500
495
  for (let len = 1; len <= maxLength; len++) {
501
496
  code = (code + lengthCounts[len - 1]) << 1;
502
497
  nextCode[len] = code;
@@ -628,7 +623,6 @@ class WebPEncoder {
628
623
  }
629
624
  numCodeLengthCodes = Math.max(4, numCodeLengthCodes);
630
625
  // Write number of code length codes
631
- // console.log(`Complex Huffman: numCodeLengthCodes=${numCodeLengthCodes}, rleEncoded.length=${rleEncoded.length}`);
632
626
  writer.writeBits(numCodeLengthCodes - 4, 4);
633
627
  // Write code length code lengths
634
628
  for (let i = 0; i < numCodeLengthCodes; i++) {