cross-image 0.1.2

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 (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +606 -0
  3. package/esm/mod.d.ts +33 -0
  4. package/esm/mod.d.ts.map +1 -0
  5. package/esm/mod.js +31 -0
  6. package/esm/package.json +3 -0
  7. package/esm/src/formats/ascii.d.ts +27 -0
  8. package/esm/src/formats/ascii.d.ts.map +1 -0
  9. package/esm/src/formats/ascii.js +172 -0
  10. package/esm/src/formats/bmp.d.ts +19 -0
  11. package/esm/src/formats/bmp.d.ts.map +1 -0
  12. package/esm/src/formats/bmp.js +174 -0
  13. package/esm/src/formats/gif.d.ts +40 -0
  14. package/esm/src/formats/gif.d.ts.map +1 -0
  15. package/esm/src/formats/gif.js +385 -0
  16. package/esm/src/formats/jpeg.d.ts +18 -0
  17. package/esm/src/formats/jpeg.d.ts.map +1 -0
  18. package/esm/src/formats/jpeg.js +414 -0
  19. package/esm/src/formats/png.d.ts +33 -0
  20. package/esm/src/formats/png.d.ts.map +1 -0
  21. package/esm/src/formats/png.js +544 -0
  22. package/esm/src/formats/raw.d.ts +23 -0
  23. package/esm/src/formats/raw.d.ts.map +1 -0
  24. package/esm/src/formats/raw.js +98 -0
  25. package/esm/src/formats/tiff.d.ts +58 -0
  26. package/esm/src/formats/tiff.d.ts.map +1 -0
  27. package/esm/src/formats/tiff.js +791 -0
  28. package/esm/src/formats/webp.d.ts +22 -0
  29. package/esm/src/formats/webp.d.ts.map +1 -0
  30. package/esm/src/formats/webp.js +403 -0
  31. package/esm/src/image.d.ts +124 -0
  32. package/esm/src/image.d.ts.map +1 -0
  33. package/esm/src/image.js +320 -0
  34. package/esm/src/types.d.ts +167 -0
  35. package/esm/src/types.d.ts.map +1 -0
  36. package/esm/src/types.js +1 -0
  37. package/esm/src/utils/gif_decoder.d.ts +42 -0
  38. package/esm/src/utils/gif_decoder.d.ts.map +1 -0
  39. package/esm/src/utils/gif_decoder.js +374 -0
  40. package/esm/src/utils/gif_encoder.d.ts +29 -0
  41. package/esm/src/utils/gif_encoder.d.ts.map +1 -0
  42. package/esm/src/utils/gif_encoder.js +226 -0
  43. package/esm/src/utils/jpeg_decoder.d.ts +39 -0
  44. package/esm/src/utils/jpeg_decoder.d.ts.map +1 -0
  45. package/esm/src/utils/jpeg_decoder.js +580 -0
  46. package/esm/src/utils/jpeg_encoder.d.ts +33 -0
  47. package/esm/src/utils/jpeg_encoder.d.ts.map +1 -0
  48. package/esm/src/utils/jpeg_encoder.js +1017 -0
  49. package/esm/src/utils/lzw.d.ts +43 -0
  50. package/esm/src/utils/lzw.d.ts.map +1 -0
  51. package/esm/src/utils/lzw.js +309 -0
  52. package/esm/src/utils/resize.d.ts +9 -0
  53. package/esm/src/utils/resize.d.ts.map +1 -0
  54. package/esm/src/utils/resize.js +52 -0
  55. package/esm/src/utils/tiff_lzw.d.ts +44 -0
  56. package/esm/src/utils/tiff_lzw.d.ts.map +1 -0
  57. package/esm/src/utils/tiff_lzw.js +306 -0
  58. package/esm/src/utils/webp_decoder.d.ts +39 -0
  59. package/esm/src/utils/webp_decoder.d.ts.map +1 -0
  60. package/esm/src/utils/webp_decoder.js +493 -0
  61. package/esm/src/utils/webp_encoder.d.ts +72 -0
  62. package/esm/src/utils/webp_encoder.d.ts.map +1 -0
  63. package/esm/src/utils/webp_encoder.js +627 -0
  64. package/package.json +41 -0
  65. package/script/mod.d.ts +33 -0
  66. package/script/mod.d.ts.map +1 -0
  67. package/script/mod.js +43 -0
  68. package/script/package.json +3 -0
  69. package/script/src/formats/ascii.d.ts +27 -0
  70. package/script/src/formats/ascii.d.ts.map +1 -0
  71. package/script/src/formats/ascii.js +176 -0
  72. package/script/src/formats/bmp.d.ts +19 -0
  73. package/script/src/formats/bmp.d.ts.map +1 -0
  74. package/script/src/formats/bmp.js +178 -0
  75. package/script/src/formats/gif.d.ts +40 -0
  76. package/script/src/formats/gif.d.ts.map +1 -0
  77. package/script/src/formats/gif.js +389 -0
  78. package/script/src/formats/jpeg.d.ts +18 -0
  79. package/script/src/formats/jpeg.d.ts.map +1 -0
  80. package/script/src/formats/jpeg.js +451 -0
  81. package/script/src/formats/png.d.ts +33 -0
  82. package/script/src/formats/png.d.ts.map +1 -0
  83. package/script/src/formats/png.js +548 -0
  84. package/script/src/formats/raw.d.ts +23 -0
  85. package/script/src/formats/raw.d.ts.map +1 -0
  86. package/script/src/formats/raw.js +102 -0
  87. package/script/src/formats/tiff.d.ts +58 -0
  88. package/script/src/formats/tiff.d.ts.map +1 -0
  89. package/script/src/formats/tiff.js +795 -0
  90. package/script/src/formats/webp.d.ts +22 -0
  91. package/script/src/formats/webp.d.ts.map +1 -0
  92. package/script/src/formats/webp.js +440 -0
  93. package/script/src/image.d.ts +124 -0
  94. package/script/src/image.d.ts.map +1 -0
  95. package/script/src/image.js +324 -0
  96. package/script/src/types.d.ts +167 -0
  97. package/script/src/types.d.ts.map +1 -0
  98. package/script/src/types.js +2 -0
  99. package/script/src/utils/gif_decoder.d.ts +42 -0
  100. package/script/src/utils/gif_decoder.d.ts.map +1 -0
  101. package/script/src/utils/gif_decoder.js +378 -0
  102. package/script/src/utils/gif_encoder.d.ts +29 -0
  103. package/script/src/utils/gif_encoder.d.ts.map +1 -0
  104. package/script/src/utils/gif_encoder.js +230 -0
  105. package/script/src/utils/jpeg_decoder.d.ts +39 -0
  106. package/script/src/utils/jpeg_decoder.d.ts.map +1 -0
  107. package/script/src/utils/jpeg_decoder.js +584 -0
  108. package/script/src/utils/jpeg_encoder.d.ts +33 -0
  109. package/script/src/utils/jpeg_encoder.d.ts.map +1 -0
  110. package/script/src/utils/jpeg_encoder.js +1021 -0
  111. package/script/src/utils/lzw.d.ts +43 -0
  112. package/script/src/utils/lzw.d.ts.map +1 -0
  113. package/script/src/utils/lzw.js +314 -0
  114. package/script/src/utils/resize.d.ts +9 -0
  115. package/script/src/utils/resize.d.ts.map +1 -0
  116. package/script/src/utils/resize.js +56 -0
  117. package/script/src/utils/tiff_lzw.d.ts +44 -0
  118. package/script/src/utils/tiff_lzw.d.ts.map +1 -0
  119. package/script/src/utils/tiff_lzw.js +311 -0
  120. package/script/src/utils/webp_decoder.d.ts +39 -0
  121. package/script/src/utils/webp_decoder.d.ts.map +1 -0
  122. package/script/src/utils/webp_decoder.js +497 -0
  123. package/script/src/utils/webp_encoder.d.ts +72 -0
  124. package/script/src/utils/webp_encoder.d.ts.map +1 -0
  125. package/script/src/utils/webp_encoder.js +631 -0
@@ -0,0 +1,497 @@
1
+ "use strict";
2
+ /**
3
+ * WebP VP8L (Lossless) decoder implementation
4
+ *
5
+ * This module implements a pure JavaScript decoder for WebP lossless (VP8L) format.
6
+ * It supports:
7
+ * - Huffman coding (canonical Huffman codes)
8
+ * - LZ77 backward references for compression
9
+ * - Color cache for repeated colors
10
+ * - Simple and complex Huffman code tables
11
+ *
12
+ * Current limitations:
13
+ * - Does not support transforms (predictor, color, subtract green, color indexing)
14
+ * - Does not support meta Huffman codes
15
+ * - Does not support lossy WebP (VP8) format
16
+ *
17
+ * For images with transforms or lossy compression, the decoder will fall back
18
+ * to the runtime's ImageDecoder API if available.
19
+ *
20
+ * @see https://developers.google.com/speed/webp/docs/riff_container
21
+ * @see https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.WebPDecoder = void 0;
25
+ // Helper to read little-endian values
26
+ function readUint24LE(data, offset) {
27
+ return data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16);
28
+ }
29
+ function readUint32LE(data, offset) {
30
+ return data[offset] | (data[offset + 1] << 8) |
31
+ (data[offset + 2] << 16) | (data[offset + 3] << 24);
32
+ }
33
+ // Huffman code table
34
+ class HuffmanTable {
35
+ constructor() {
36
+ Object.defineProperty(this, "root", {
37
+ enumerable: true,
38
+ configurable: true,
39
+ writable: true,
40
+ value: void 0
41
+ });
42
+ Object.defineProperty(this, "singleSymbol", {
43
+ enumerable: true,
44
+ configurable: true,
45
+ writable: true,
46
+ value: void 0
47
+ }); // For tables with only one symbol
48
+ this.root = {};
49
+ }
50
+ addCode(symbol, code, codeLength) {
51
+ // Handle single symbol case (code length 0)
52
+ if (codeLength === 0) {
53
+ this.singleSymbol = symbol;
54
+ return;
55
+ }
56
+ let node = this.root;
57
+ for (let i = codeLength - 1; i >= 0; i--) {
58
+ const bit = (code >> i) & 1;
59
+ if (bit === 0) {
60
+ if (!node.left)
61
+ node.left = {};
62
+ node = node.left;
63
+ }
64
+ else {
65
+ if (!node.right)
66
+ node.right = {};
67
+ node = node.right;
68
+ }
69
+ }
70
+ node.symbol = symbol;
71
+ }
72
+ readSymbol(reader) {
73
+ // Handle single symbol case
74
+ if (this.singleSymbol !== undefined) {
75
+ return this.singleSymbol;
76
+ }
77
+ let node = this.root;
78
+ while (node.symbol === undefined) {
79
+ const bit = reader.readBits(1);
80
+ node = bit === 0 ? node.left : node.right;
81
+ if (!node) {
82
+ throw new Error("Invalid Huffman code");
83
+ }
84
+ }
85
+ return node.symbol;
86
+ }
87
+ }
88
+ class BitReader {
89
+ constructor(data, offset) {
90
+ Object.defineProperty(this, "data", {
91
+ enumerable: true,
92
+ configurable: true,
93
+ writable: true,
94
+ value: void 0
95
+ });
96
+ Object.defineProperty(this, "pos", {
97
+ enumerable: true,
98
+ configurable: true,
99
+ writable: true,
100
+ value: void 0
101
+ });
102
+ Object.defineProperty(this, "bitPos", {
103
+ enumerable: true,
104
+ configurable: true,
105
+ writable: true,
106
+ value: void 0
107
+ });
108
+ Object.defineProperty(this, "value", {
109
+ enumerable: true,
110
+ configurable: true,
111
+ writable: true,
112
+ value: void 0
113
+ });
114
+ this.data = data;
115
+ this.pos = offset;
116
+ this.bitPos = 0;
117
+ this.value = 0;
118
+ }
119
+ readBits(numBits) {
120
+ let result = 0;
121
+ for (let i = 0; i < numBits; i++) {
122
+ if (this.bitPos === 0) {
123
+ if (this.pos >= this.data.length) {
124
+ throw new Error("Unexpected end of data");
125
+ }
126
+ this.value = this.data[this.pos++];
127
+ this.bitPos = 8;
128
+ }
129
+ result |= ((this.value >> (this.bitPos - 1)) & 1) << i;
130
+ this.bitPos--;
131
+ }
132
+ return result;
133
+ }
134
+ getPosition() {
135
+ return this.pos;
136
+ }
137
+ // Read bytes aligned to byte boundary
138
+ readBytes(count) {
139
+ // Align to byte boundary
140
+ if (this.bitPos !== 0 && this.bitPos !== 8) {
141
+ this.bitPos = 0;
142
+ }
143
+ if (this.pos + count > this.data.length) {
144
+ throw new Error("Unexpected end of data");
145
+ }
146
+ const result = this.data.slice(this.pos, this.pos + count);
147
+ this.pos += count;
148
+ return result;
149
+ }
150
+ }
151
+ class WebPDecoder {
152
+ constructor(data) {
153
+ Object.defineProperty(this, "data", {
154
+ enumerable: true,
155
+ configurable: true,
156
+ writable: true,
157
+ value: void 0
158
+ });
159
+ this.data = data;
160
+ }
161
+ decode() {
162
+ // Verify WebP signature
163
+ if (this.data.length < 12 ||
164
+ this.data[0] !== 0x52 || this.data[1] !== 0x49 || // "RI"
165
+ this.data[2] !== 0x46 || this.data[3] !== 0x46 || // "FF"
166
+ this.data[8] !== 0x57 || this.data[9] !== 0x45 || // "WE"
167
+ this.data[10] !== 0x42 || this.data[11] !== 0x50 // "BP"
168
+ ) {
169
+ throw new Error("Invalid WebP signature");
170
+ }
171
+ let pos = 12; // Skip RIFF header
172
+ let width = 0;
173
+ let height = 0;
174
+ let imageData = null;
175
+ // Parse chunks
176
+ while (pos + 8 <= this.data.length) {
177
+ const chunkType = String.fromCharCode(this.data[pos], this.data[pos + 1], this.data[pos + 2], this.data[pos + 3]);
178
+ const chunkSize = readUint32LE(this.data, pos + 4);
179
+ pos += 8;
180
+ if (pos + chunkSize > this.data.length)
181
+ break;
182
+ const chunkData = this.data.slice(pos, pos + chunkSize);
183
+ if (chunkType === "VP8L") {
184
+ // Lossless format - we can decode this
185
+ const result = this.decodeVP8L(chunkData);
186
+ width = result.width;
187
+ height = result.height;
188
+ imageData = result.data;
189
+ break; // Stop after decoding image data
190
+ }
191
+ else if (chunkType === "VP8 ") {
192
+ // Lossy format - not supported in pure JS decoder
193
+ throw new Error("WebP lossy (VP8) format not supported in pure JS decoder");
194
+ }
195
+ else if (chunkType === "VP8X") {
196
+ // Extended format header
197
+ if (chunkData.length >= 10) {
198
+ width = readUint24LE(chunkData, 4) + 1;
199
+ height = readUint24LE(chunkData, 7) + 1;
200
+ }
201
+ }
202
+ pos += chunkSize;
203
+ // Chunks are padded to even length
204
+ if (chunkSize % 2 === 1)
205
+ pos++;
206
+ }
207
+ if (!imageData || width === 0 || height === 0) {
208
+ throw new Error("Failed to decode WebP: no valid image data found");
209
+ }
210
+ return { width, height, data: imageData };
211
+ }
212
+ decodeVP8L(data) {
213
+ // VP8L signature
214
+ if (data[0] !== 0x2f) {
215
+ throw new Error("Invalid VP8L signature");
216
+ }
217
+ const bits = readUint32LE(data, 1);
218
+ const width = (bits & 0x3fff) + 1;
219
+ const height = ((bits >> 14) & 0x3fff) + 1;
220
+ const alphaUsed = (bits >> 28) & 1;
221
+ const versionNumber = (bits >> 29) & 7;
222
+ if (versionNumber !== 0) {
223
+ throw new Error(`Unsupported VP8L version: ${versionNumber}`);
224
+ }
225
+ // Create bit reader starting after header
226
+ const reader = new BitReader(data, 5);
227
+ // Decode image data
228
+ // This is a simplified decoder that handles basic lossless WebP
229
+ try {
230
+ const rgba = this.decodeImageData(reader, width, height, alphaUsed);
231
+ return { width, height, data: rgba };
232
+ }
233
+ catch (error) {
234
+ throw new Error(`VP8L decoding failed: ${error}`);
235
+ }
236
+ }
237
+ decodeImageData(reader, width, height, alphaUsed) {
238
+ // Read transform info
239
+ const useTransforms = reader.readBits(1);
240
+ if (useTransforms) {
241
+ // For simplicity, we don't support transforms in this basic decoder
242
+ // Transforms include: predictor, color, subtract green, color indexing
243
+ throw new Error("WebP transforms not supported in basic decoder");
244
+ }
245
+ // Read color cache info
246
+ const useColorCache = reader.readBits(1) === 1;
247
+ let colorCacheBits = 0;
248
+ let colorCacheSize = 0;
249
+ if (useColorCache) {
250
+ colorCacheBits = reader.readBits(4);
251
+ if (colorCacheBits < 1 || colorCacheBits > 11) {
252
+ throw new Error("Invalid color cache bits");
253
+ }
254
+ colorCacheSize = 1 << colorCacheBits;
255
+ }
256
+ // Read Huffman codes
257
+ const huffmanTables = this.readHuffmanCodes(reader, useColorCache, colorCacheBits);
258
+ // Decode the image using Huffman codes
259
+ const pixelData = new Uint8Array(width * height * 4);
260
+ let pixelIndex = 0;
261
+ const numPixels = width * height;
262
+ // Color cache for repeated colors
263
+ const colorCache = new Uint32Array(colorCacheSize);
264
+ for (let i = 0; i < numPixels;) {
265
+ // Read green channel (which determines the code type)
266
+ const green = huffmanTables.green.readSymbol(reader);
267
+ if (green < 256) {
268
+ // Literal pixel
269
+ const red = huffmanTables.red.readSymbol(reader);
270
+ const blue = huffmanTables.blue.readSymbol(reader);
271
+ const alpha = alphaUsed !== 0
272
+ ? huffmanTables.alpha.readSymbol(reader)
273
+ : 255;
274
+ pixelData[pixelIndex++] = red;
275
+ pixelData[pixelIndex++] = green;
276
+ pixelData[pixelIndex++] = blue;
277
+ pixelData[pixelIndex++] = alpha;
278
+ // Add to color cache if enabled
279
+ if (useColorCache) {
280
+ const color = (alpha << 24) | (blue << 16) | (green << 8) | red;
281
+ colorCache[i % colorCacheSize] = color;
282
+ }
283
+ i++;
284
+ }
285
+ else if (green < 256 + 24) {
286
+ // Backward reference (LZ77)
287
+ const lengthSymbol = green - 256;
288
+ const length = this.getLength(lengthSymbol, reader);
289
+ const distancePrefix = huffmanTables.distance.readSymbol(reader);
290
+ const distance = this.getDistance(distancePrefix, reader);
291
+ // Copy pixels from earlier in the stream
292
+ const srcIndex = pixelIndex - distance * 4;
293
+ if (srcIndex < 0) {
294
+ throw new Error("Invalid backward reference");
295
+ }
296
+ for (let j = 0; j < length; j++) {
297
+ pixelData[pixelIndex++] = pixelData[srcIndex + j * 4];
298
+ pixelData[pixelIndex++] = pixelData[srcIndex + j * 4 + 1];
299
+ pixelData[pixelIndex++] = pixelData[srcIndex + j * 4 + 2];
300
+ pixelData[pixelIndex++] = pixelData[srcIndex + j * 4 + 3];
301
+ // Add to color cache
302
+ if (useColorCache) {
303
+ const color = (pixelData[pixelIndex - 1] << 24) |
304
+ (pixelData[pixelIndex - 2] << 16) |
305
+ (pixelData[pixelIndex - 3] << 8) |
306
+ pixelData[pixelIndex - 4];
307
+ colorCache[(i + j) % colorCacheSize] = color;
308
+ }
309
+ }
310
+ i += length;
311
+ }
312
+ else {
313
+ // Color cache reference
314
+ const cacheIndex = green - 256 - 24;
315
+ if (cacheIndex >= colorCacheSize) {
316
+ throw new Error("Invalid color cache index");
317
+ }
318
+ const color = colorCache[cacheIndex];
319
+ pixelData[pixelIndex++] = color & 0xff; // R
320
+ pixelData[pixelIndex++] = (color >> 8) & 0xff; // G
321
+ pixelData[pixelIndex++] = (color >> 16) & 0xff; // B
322
+ pixelData[pixelIndex++] = (color >> 24) & 0xff; // A
323
+ i++;
324
+ }
325
+ }
326
+ return pixelData;
327
+ }
328
+ readHuffmanCodes(reader, useColorCache, colorCacheBits) {
329
+ // Read meta Huffman codes
330
+ const useMetaHuffman = reader.readBits(1);
331
+ if (useMetaHuffman) {
332
+ // Meta Huffman is more complex - for now throw error
333
+ throw new Error("Meta Huffman codes not yet supported");
334
+ }
335
+ // Read the main Huffman codes
336
+ // There are 5 Huffman code groups: green, red, blue, alpha, distance
337
+ // But we read 4 + optional distance code
338
+ const numCodeGroups = reader.readBits(4) + 4;
339
+ const tables = {
340
+ green: new HuffmanTable(),
341
+ red: new HuffmanTable(),
342
+ blue: new HuffmanTable(),
343
+ alpha: new HuffmanTable(),
344
+ distance: new HuffmanTable(),
345
+ };
346
+ const tableArray = [
347
+ tables.green,
348
+ tables.red,
349
+ tables.blue,
350
+ tables.alpha,
351
+ tables.distance,
352
+ ];
353
+ for (let i = 0; i < numCodeGroups && i < 5; i++) {
354
+ this.readHuffmanCode(reader, tableArray[i], useColorCache, colorCacheBits, i === 0);
355
+ }
356
+ return tables;
357
+ }
358
+ readHuffmanCode(reader, table, useColorCache, colorCacheBits, isGreen) {
359
+ const simple = reader.readBits(1);
360
+ if (simple) {
361
+ // Simple code - directly specify 1 or 2 symbols
362
+ const numSymbols = reader.readBits(1) + 1;
363
+ const isFirstEightBits = reader.readBits(1);
364
+ const symbols = [];
365
+ for (let i = 0; i < numSymbols; i++) {
366
+ const symbolBits = isFirstEightBits
367
+ ? 1 + reader.readBits(7)
368
+ : reader.readBits(8);
369
+ symbols.push(symbolBits);
370
+ }
371
+ // Build simple Huffman table
372
+ if (numSymbols === 1) {
373
+ // Single symbol - 0 bits needed
374
+ table.addCode(symbols[0], 0, 0);
375
+ }
376
+ else {
377
+ // Two symbols - 1 bit each
378
+ table.addCode(symbols[0], 0, 1);
379
+ table.addCode(symbols[1], 1, 1);
380
+ }
381
+ }
382
+ else {
383
+ // Complex code - read code lengths
384
+ const maxSymbol = isGreen
385
+ ? (256 + 24 + (useColorCache ? (1 << colorCacheBits) : 0))
386
+ : 256;
387
+ const codeLengths = this.readCodeLengths(reader, maxSymbol);
388
+ this.buildHuffmanTable(table, codeLengths);
389
+ }
390
+ }
391
+ readCodeLengths(reader, maxSymbol) {
392
+ // Read code length codes (used to encode the actual code lengths)
393
+ const numCodeLengthCodes = reader.readBits(4) + 4;
394
+ const codeLengthCodeLengths = new Array(19).fill(0);
395
+ // Code length code order
396
+ const codeLengthCodeOrder = [
397
+ 17,
398
+ 18,
399
+ 0,
400
+ 1,
401
+ 2,
402
+ 3,
403
+ 4,
404
+ 5,
405
+ 16,
406
+ 6,
407
+ 7,
408
+ 8,
409
+ 9,
410
+ 10,
411
+ 11,
412
+ 12,
413
+ 13,
414
+ 14,
415
+ 15,
416
+ ];
417
+ for (let i = 0; i < numCodeLengthCodes; i++) {
418
+ codeLengthCodeLengths[codeLengthCodeOrder[i]] = reader.readBits(3);
419
+ }
420
+ // Build code length Huffman table
421
+ const codeLengthTable = new HuffmanTable();
422
+ this.buildHuffmanTable(codeLengthTable, codeLengthCodeLengths);
423
+ // Read actual code lengths
424
+ const codeLengths = new Array(maxSymbol).fill(0);
425
+ let i = 0;
426
+ while (i < maxSymbol) {
427
+ const code = codeLengthTable.readSymbol(reader);
428
+ if (code < 16) {
429
+ // Literal code length
430
+ codeLengths[i++] = code;
431
+ }
432
+ else if (code === 16) {
433
+ // Repeat previous code length 3-6 times
434
+ const repeatCount = reader.readBits(2) + 3;
435
+ const prevLength = i > 0 ? codeLengths[i - 1] : 0;
436
+ for (let j = 0; j < repeatCount && i < maxSymbol; j++) {
437
+ codeLengths[i++] = prevLength;
438
+ }
439
+ }
440
+ else if (code === 17) {
441
+ // Repeat 0 for 3-10 times
442
+ const repeatCount = reader.readBits(3) + 3;
443
+ i += repeatCount;
444
+ }
445
+ else if (code === 18) {
446
+ // Repeat 0 for 11-138 times
447
+ const repeatCount = reader.readBits(7) + 11;
448
+ i += repeatCount;
449
+ }
450
+ }
451
+ return codeLengths;
452
+ }
453
+ buildHuffmanTable(table, codeLengths) {
454
+ // Build canonical Huffman codes
455
+ const maxCodeLength = Math.max(...codeLengths);
456
+ const lengthCounts = new Array(maxCodeLength + 1).fill(0);
457
+ for (const length of codeLengths) {
458
+ if (length > 0) {
459
+ lengthCounts[length]++;
460
+ }
461
+ }
462
+ // Generate codes
463
+ let code = 0;
464
+ const nextCode = new Array(maxCodeLength + 1).fill(0);
465
+ for (let i = 1; i <= maxCodeLength; i++) {
466
+ code = (code + lengthCounts[i - 1]) << 1;
467
+ nextCode[i] = code;
468
+ }
469
+ // Assign codes to symbols
470
+ for (let symbol = 0; symbol < codeLengths.length; symbol++) {
471
+ const length = codeLengths[symbol];
472
+ if (length > 0) {
473
+ table.addCode(symbol, nextCode[length], length);
474
+ nextCode[length]++;
475
+ }
476
+ }
477
+ }
478
+ getLength(symbol, reader) {
479
+ // Length encoding for backward references
480
+ if (symbol < 4) {
481
+ return symbol + 1;
482
+ }
483
+ const extraBits = (symbol - 2) >> 1;
484
+ const base = ((2 + (symbol & 1)) << extraBits) + 1;
485
+ return base + reader.readBits(extraBits);
486
+ }
487
+ getDistance(symbol, reader) {
488
+ // Distance encoding for backward references
489
+ if (symbol < 4) {
490
+ return symbol + 1;
491
+ }
492
+ const extraBits = (symbol - 2) >> 1;
493
+ const base = ((2 + (symbol & 1)) << extraBits) + 1;
494
+ return base + reader.readBits(extraBits);
495
+ }
496
+ }
497
+ exports.WebPDecoder = WebPDecoder;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * WebP VP8L (Lossless) encoder implementation with quality-based quantization
3
+ *
4
+ * This module implements a pure JavaScript encoder for WebP lossless (VP8L) format.
5
+ * It supports:
6
+ * - Lossless encoding (quality=100) with Huffman coding
7
+ * - Lossy encoding (quality<100) using color quantization while still using VP8L format
8
+ * - Simple Huffman coding (1-2 symbols per channel)
9
+ * - Complex Huffman coding for channels with many unique values (3+ symbols)
10
+ * - Literal pixel encoding (no transforms applied)
11
+ *
12
+ * Current limitations:
13
+ * - Does not use transforms (predictor, color, subtract green, color indexing)
14
+ * - Does not use LZ77 backward references (planned for future)
15
+ * - Does not use color cache (planned for future)
16
+ * - Lossy mode uses simple quantization, not true VP8 lossy encoding
17
+ * - Intended as a fallback when OffscreenCanvas is not available
18
+ *
19
+ * This encoder produces valid WebP lossless files with optional quality-based
20
+ * color quantization for lossy compression. For true VP8 lossy encoding with
21
+ * better compression, use the runtime's OffscreenCanvas API when available.
22
+ *
23
+ * @see https://developers.google.com/speed/webp/docs/riff_container
24
+ * @see https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification
25
+ */
26
+ export declare class WebPEncoder {
27
+ private width;
28
+ private height;
29
+ private data;
30
+ private quality;
31
+ constructor(width: number, height: number, rgba: Uint8Array);
32
+ encode(quality?: number): Uint8Array;
33
+ private encodeVP8L;
34
+ private hasAlphaChannel;
35
+ /**
36
+ * Quantize image data based on quality setting
37
+ * Quality 100 = no quantization (lossless)
38
+ * Quality 1-99 = quantize colors to reduce bit depth
39
+ * This creates a "lossy" effect while still using VP8L format
40
+ */
41
+ private quantizeImageData;
42
+ private encodeImageData;
43
+ /**
44
+ * Write Huffman code for a channel (either simple or complex)
45
+ * Returns the Huffman codes for encoding pixels
46
+ */
47
+ private writeHuffmanCode;
48
+ /**
49
+ * Write a symbol using its Huffman code
50
+ */
51
+ private writeSymbol;
52
+ private writeSimpleHuffmanCode;
53
+ /**
54
+ * Calculate optimal code lengths for symbols using standard Huffman algorithm
55
+ * Returns an array where index is the symbol and value is the code length
56
+ */
57
+ private calculateCodeLengths;
58
+ /**
59
+ * Build canonical Huffman codes from code lengths
60
+ * Returns a map from symbol to {code, length}
61
+ */
62
+ private buildCanonicalCodes;
63
+ /**
64
+ * RLE encode code lengths using special codes 16, 17, 18
65
+ */
66
+ private rleEncodeCodeLengths;
67
+ /**
68
+ * Write complex Huffman code using code lengths
69
+ */
70
+ private writeComplexHuffmanCode;
71
+ }
72
+ //# sourceMappingURL=webp_encoder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webp_encoder.d.ts","sourceRoot":"","sources":["../../../src/src/utils/webp_encoder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AA2DH,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEZ,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU;IAO3D,MAAM,CAAC,OAAO,GAAE,MAAY,GAAG,UAAU;IAmDzC,OAAO,CAAC,UAAU;IAyBlB,OAAO,CAAC,eAAe;IAUvB;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAwCzB,OAAO,CAAC,eAAe;IA8EvB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA4BxB;;OAEG;IACH,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,sBAAsB;IA4B9B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAoF5B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAyC3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA8D5B;;OAEG;IACH,OAAO,CAAC,uBAAuB;CA6GhC"}