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,374 @@
1
+ /**
2
+ * Pure JavaScript GIF decoder implementation
3
+ * Supports GIF87a and GIF89a formats with LZW decompression
4
+ */
5
+ import { LZWDecoder } from "./lzw.js";
6
+ export class GIFDecoder {
7
+ constructor(data) {
8
+ Object.defineProperty(this, "data", {
9
+ enumerable: true,
10
+ configurable: true,
11
+ writable: true,
12
+ value: void 0
13
+ });
14
+ Object.defineProperty(this, "pos", {
15
+ enumerable: true,
16
+ configurable: true,
17
+ writable: true,
18
+ value: void 0
19
+ });
20
+ this.data = data;
21
+ this.pos = 0;
22
+ }
23
+ readByte() {
24
+ if (this.pos >= this.data.length) {
25
+ throw new Error("Unexpected end of GIF data");
26
+ }
27
+ return this.data[this.pos++];
28
+ }
29
+ readUint16LE() {
30
+ const low = this.readByte();
31
+ const high = this.readByte();
32
+ return low | (high << 8);
33
+ }
34
+ readBytes(count) {
35
+ if (this.pos + count > this.data.length) {
36
+ throw new Error("Unexpected end of GIF data");
37
+ }
38
+ const result = this.data.slice(this.pos, this.pos + count);
39
+ this.pos += count;
40
+ return result;
41
+ }
42
+ readColorTable(size) {
43
+ // Each color is 3 bytes (RGB)
44
+ return this.readBytes(size * 3);
45
+ }
46
+ readDataSubBlocks() {
47
+ const blocks = [];
48
+ while (true) {
49
+ const blockSize = this.readByte();
50
+ if (blockSize === 0)
51
+ break;
52
+ const blockData = this.readBytes(blockSize);
53
+ blocks.push(...blockData);
54
+ }
55
+ return new Uint8Array(blocks);
56
+ }
57
+ decode() {
58
+ // Verify GIF signature
59
+ const signature = this.readBytes(3);
60
+ const version = this.readBytes(3);
61
+ if (signature[0] !== 0x47 || signature[1] !== 0x49 || signature[2] !== 0x46 ||
62
+ (version[0] !== 0x38) ||
63
+ (version[1] !== 0x37 && version[1] !== 0x39) ||
64
+ (version[2] !== 0x61)) {
65
+ throw new Error("Invalid GIF signature");
66
+ }
67
+ // Read Logical Screen Descriptor
68
+ const width = this.readUint16LE();
69
+ const height = this.readUint16LE();
70
+ const packed = this.readByte();
71
+ const backgroundColorIndex = this.readByte();
72
+ const _aspectRatio = this.readByte();
73
+ const hasGlobalColorTable = (packed & 0x80) !== 0;
74
+ const _colorResolution = ((packed & 0x70) >> 4) + 1;
75
+ const _sortFlag = (packed & 0x08) !== 0;
76
+ const globalColorTableSize = 2 << (packed & 0x07);
77
+ let globalColorTable = null;
78
+ if (hasGlobalColorTable) {
79
+ globalColorTable = this.readColorTable(globalColorTableSize);
80
+ }
81
+ // Parse data stream
82
+ let imageWidth = 0;
83
+ let imageHeight = 0;
84
+ let imageLeft = 0;
85
+ let imageTop = 0;
86
+ let localColorTable = null;
87
+ let transparentColorIndex = null;
88
+ let interlaced = false;
89
+ while (this.pos < this.data.length) {
90
+ const separator = this.readByte();
91
+ if (separator === 0x21) {
92
+ // Extension
93
+ const label = this.readByte();
94
+ if (label === 0xf9) {
95
+ // Graphic Control Extension
96
+ const _blockSize = this.readByte();
97
+ const packed = this.readByte();
98
+ const hasTransparent = (packed & 0x01) !== 0;
99
+ const _delayTime = this.readUint16LE();
100
+ const transparentIndex = this.readByte();
101
+ const _terminator = this.readByte();
102
+ if (hasTransparent) {
103
+ transparentColorIndex = transparentIndex;
104
+ }
105
+ }
106
+ else {
107
+ // Skip other extensions
108
+ this.readDataSubBlocks();
109
+ }
110
+ }
111
+ else if (separator === 0x2c) {
112
+ // Image Descriptor
113
+ imageLeft = this.readUint16LE();
114
+ imageTop = this.readUint16LE();
115
+ imageWidth = this.readUint16LE();
116
+ imageHeight = this.readUint16LE();
117
+ const packed = this.readByte();
118
+ const hasLocalColorTable = (packed & 0x80) !== 0;
119
+ interlaced = (packed & 0x40) !== 0;
120
+ const localColorTableSize = 2 << (packed & 0x07);
121
+ if (hasLocalColorTable) {
122
+ localColorTable = this.readColorTable(localColorTableSize);
123
+ }
124
+ // Read image data
125
+ const minCodeSize = this.readByte();
126
+ const compressedData = this.readDataSubBlocks();
127
+ // Decompress using LZW
128
+ const decoder = new LZWDecoder(minCodeSize, compressedData);
129
+ const indexedData = decoder.decompress();
130
+ // Convert indexed to RGBA
131
+ const colorTable = localColorTable || globalColorTable;
132
+ if (!colorTable) {
133
+ throw new Error("No color table available");
134
+ }
135
+ return this.indexedToRGBA(indexedData, imageWidth, imageHeight, colorTable, transparentColorIndex, interlaced, width, height, imageLeft, imageTop, backgroundColorIndex);
136
+ }
137
+ else if (separator === 0x3b) {
138
+ // Trailer - end of GIF
139
+ break;
140
+ }
141
+ else if (separator === 0x00) {
142
+ // Skip null bytes
143
+ continue;
144
+ }
145
+ else {
146
+ throw new Error(`Unknown separator: 0x${separator.toString(16)}`);
147
+ }
148
+ }
149
+ throw new Error("No image data found in GIF");
150
+ }
151
+ /**
152
+ * Decode all frames from an animated GIF
153
+ * @returns Object with canvas dimensions and array of frames
154
+ */
155
+ decodeAllFrames() {
156
+ // Reset position
157
+ this.pos = 0;
158
+ // Verify GIF signature
159
+ const signature = this.readBytes(3);
160
+ const version = this.readBytes(3);
161
+ if (signature[0] !== 0x47 || signature[1] !== 0x49 || signature[2] !== 0x46 ||
162
+ (version[0] !== 0x38) ||
163
+ (version[1] !== 0x37 && version[1] !== 0x39) ||
164
+ (version[2] !== 0x61)) {
165
+ throw new Error("Invalid GIF signature");
166
+ }
167
+ // Read Logical Screen Descriptor
168
+ const width = this.readUint16LE();
169
+ const height = this.readUint16LE();
170
+ const packed = this.readByte();
171
+ const _backgroundColorIndex = this.readByte();
172
+ const _aspectRatio = this.readByte();
173
+ const hasGlobalColorTable = (packed & 0x80) !== 0;
174
+ // Color table size: 2^(n+1) where n is the 3 least significant bits
175
+ const globalColorTableSize = 2 << (packed & 0x07);
176
+ let globalColorTable = null;
177
+ if (hasGlobalColorTable) {
178
+ globalColorTable = this.readColorTable(globalColorTableSize);
179
+ }
180
+ const frames = [];
181
+ // Parse data stream for all frames
182
+ let transparentColorIndex = null;
183
+ let delayTime = 0;
184
+ let disposalMethod = 0;
185
+ while (this.pos < this.data.length) {
186
+ const separator = this.readByte();
187
+ if (separator === 0x21) {
188
+ // Extension
189
+ const label = this.readByte();
190
+ if (label === 0xf9) {
191
+ // Graphic Control Extension
192
+ const _blockSize = this.readByte();
193
+ const packed = this.readByte();
194
+ disposalMethod = (packed >> 2) & 0x07;
195
+ const hasTransparent = (packed & 0x01) !== 0;
196
+ delayTime = this.readUint16LE();
197
+ const transparentIndex = this.readByte();
198
+ const _terminator = this.readByte();
199
+ if (hasTransparent) {
200
+ transparentColorIndex = transparentIndex;
201
+ }
202
+ }
203
+ else {
204
+ // Skip other extensions
205
+ this.readDataSubBlocks();
206
+ }
207
+ }
208
+ else if (separator === 0x2c) {
209
+ // Image Descriptor
210
+ const imageLeft = this.readUint16LE();
211
+ const imageTop = this.readUint16LE();
212
+ const imageWidth = this.readUint16LE();
213
+ const imageHeight = this.readUint16LE();
214
+ const packed = this.readByte();
215
+ const hasLocalColorTable = (packed & 0x80) !== 0;
216
+ const interlaced = (packed & 0x40) !== 0;
217
+ // Color table size: 2^(n+1) where n is the 3 least significant bits
218
+ const localColorTableSize = 2 << (packed & 0x07);
219
+ let localColorTable = null;
220
+ if (hasLocalColorTable) {
221
+ localColorTable = this.readColorTable(localColorTableSize);
222
+ }
223
+ // Read image data
224
+ const minCodeSize = this.readByte();
225
+ const compressedData = this.readDataSubBlocks();
226
+ // Decompress using LZW
227
+ const decoder = new LZWDecoder(minCodeSize, compressedData);
228
+ const indexedData = decoder.decompress();
229
+ // Convert indexed to RGBA
230
+ const colorTable = localColorTable || globalColorTable;
231
+ if (!colorTable) {
232
+ throw new Error("No color table available");
233
+ }
234
+ // Deinterlace if necessary
235
+ const deinterlaced = interlaced
236
+ ? this.deinterlace(indexedData, imageWidth, imageHeight)
237
+ : indexedData;
238
+ // Create frame with just the image data (not full canvas)
239
+ const frameData = new Uint8Array(imageWidth * imageHeight * 4);
240
+ for (let y = 0; y < imageHeight; y++) {
241
+ for (let x = 0; x < imageWidth; x++) {
242
+ const srcIdx = y * imageWidth + x;
243
+ if (srcIdx >= deinterlaced.length)
244
+ continue;
245
+ const colorIndex = deinterlaced[srcIdx];
246
+ const dstIdx = (y * imageWidth + x) * 4;
247
+ if (transparentColorIndex !== null &&
248
+ colorIndex === transparentColorIndex) {
249
+ // Transparent pixel
250
+ frameData[dstIdx] = 0;
251
+ frameData[dstIdx + 1] = 0;
252
+ frameData[dstIdx + 2] = 0;
253
+ frameData[dstIdx + 3] = 0;
254
+ }
255
+ else {
256
+ // Copy color from color table
257
+ const colorOffset = colorIndex * 3;
258
+ if (colorOffset + 2 < colorTable.length) {
259
+ frameData[dstIdx] = colorTable[colorOffset];
260
+ frameData[dstIdx + 1] = colorTable[colorOffset + 1];
261
+ frameData[dstIdx + 2] = colorTable[colorOffset + 2];
262
+ frameData[dstIdx + 3] = 255;
263
+ }
264
+ }
265
+ }
266
+ }
267
+ frames.push({
268
+ width: imageWidth,
269
+ height: imageHeight,
270
+ left: imageLeft,
271
+ top: imageTop,
272
+ data: frameData,
273
+ delay: delayTime,
274
+ disposal: disposalMethod,
275
+ });
276
+ // Reset graphic control extension state
277
+ transparentColorIndex = null;
278
+ delayTime = 0;
279
+ disposalMethod = 0;
280
+ }
281
+ else if (separator === 0x3b) {
282
+ // Trailer - end of GIF
283
+ break;
284
+ }
285
+ else if (separator === 0x00) {
286
+ // Skip null bytes
287
+ continue;
288
+ }
289
+ else {
290
+ throw new Error(`Unknown separator: 0x${separator.toString(16)}`);
291
+ }
292
+ }
293
+ if (frames.length === 0) {
294
+ throw new Error("No image data found in GIF");
295
+ }
296
+ return {
297
+ width,
298
+ height,
299
+ frames,
300
+ };
301
+ }
302
+ indexedToRGBA(indexedData, imageWidth, imageHeight, colorTable, transparentColorIndex, interlaced, canvasWidth, canvasHeight, imageLeft, imageTop, backgroundColorIndex) {
303
+ // Create RGBA buffer for full canvas
304
+ const rgba = new Uint8Array(canvasWidth * canvasHeight * 4);
305
+ // Fill with background color
306
+ const bgR = colorTable[backgroundColorIndex * 3] || 0;
307
+ const bgG = colorTable[backgroundColorIndex * 3 + 1] || 0;
308
+ const bgB = colorTable[backgroundColorIndex * 3 + 2] || 0;
309
+ for (let i = 0; i < rgba.length; i += 4) {
310
+ rgba[i] = bgR;
311
+ rgba[i + 1] = bgG;
312
+ rgba[i + 2] = bgB;
313
+ rgba[i + 3] = 255;
314
+ }
315
+ // Deinterlace if necessary
316
+ const deinterlaced = interlaced
317
+ ? this.deinterlace(indexedData, imageWidth, imageHeight)
318
+ : indexedData;
319
+ // Copy image data to canvas
320
+ for (let y = 0; y < imageHeight; y++) {
321
+ for (let x = 0; x < imageWidth; x++) {
322
+ const srcIdx = y * imageWidth + x;
323
+ if (srcIdx >= deinterlaced.length)
324
+ continue;
325
+ const colorIndex = deinterlaced[srcIdx];
326
+ const canvasX = imageLeft + x;
327
+ const canvasY = imageTop + y;
328
+ if (canvasX >= canvasWidth || canvasY >= canvasHeight)
329
+ continue;
330
+ const dstIdx = (canvasY * canvasWidth + canvasX) * 4;
331
+ if (transparentColorIndex !== null && colorIndex === transparentColorIndex) {
332
+ // Transparent pixel
333
+ rgba[dstIdx + 3] = 0;
334
+ }
335
+ else {
336
+ // Copy color from color table
337
+ const colorOffset = colorIndex * 3;
338
+ if (colorOffset + 2 < colorTable.length) {
339
+ rgba[dstIdx] = colorTable[colorOffset];
340
+ rgba[dstIdx + 1] = colorTable[colorOffset + 1];
341
+ rgba[dstIdx + 2] = colorTable[colorOffset + 2];
342
+ rgba[dstIdx + 3] = 255;
343
+ }
344
+ }
345
+ }
346
+ }
347
+ return {
348
+ width: canvasWidth,
349
+ height: canvasHeight,
350
+ data: rgba,
351
+ };
352
+ }
353
+ deinterlace(data, width, height) {
354
+ const deinterlaced = new Uint8Array(data.length);
355
+ const passes = [
356
+ { start: 0, step: 8 }, // Pass 1: every 8th row, starting with row 0
357
+ { start: 4, step: 8 }, // Pass 2: every 8th row, starting with row 4
358
+ { start: 2, step: 4 }, // Pass 3: every 4th row, starting with row 2
359
+ { start: 1, step: 2 }, // Pass 4: every 2nd row, starting with row 1
360
+ ];
361
+ let srcIdx = 0;
362
+ for (const pass of passes) {
363
+ for (let y = pass.start; y < height; y += pass.step) {
364
+ for (let x = 0; x < width; x++) {
365
+ if (srcIdx >= data.length)
366
+ break;
367
+ const dstIdx = y * width + x;
368
+ deinterlaced[dstIdx] = data[srcIdx++];
369
+ }
370
+ }
371
+ }
372
+ return deinterlaced;
373
+ }
374
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Pure JavaScript GIF encoder implementation
3
+ * Supports GIF89a format with LZW compression
4
+ */
5
+ export declare class GIFEncoder {
6
+ private width;
7
+ private height;
8
+ private data;
9
+ constructor(width: number, height: number, data: Uint8Array);
10
+ private writeBytes;
11
+ private writeUint16LE;
12
+ private writeString;
13
+ /**
14
+ * Quantize a color channel value to a specified number of levels
15
+ * @param value - The input color channel value (0-255)
16
+ * @param levels - Number of quantization levels minus 1 (e.g., 7 for 8 levels)
17
+ * @param step - Step size for quantization (e.g., 255/7)
18
+ * @returns Quantized integer value
19
+ */
20
+ private quantizeChannel;
21
+ /**
22
+ * Quantize RGBA image to 256 colors using median cut algorithm
23
+ */
24
+ private quantize;
25
+ private nextPowerOf2;
26
+ private getBitsPerColor;
27
+ encode(): Uint8Array;
28
+ }
29
+ //# sourceMappingURL=gif_encoder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gif_encoder.d.ts","sourceRoot":"","sources":["../../../src/src/utils/gif_encoder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,IAAI,CAAa;gBAEb,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU;IAM3D,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,WAAW;IAMnB;;;;;;OAMG;IACH,OAAO,CAAC,eAAe;IAMvB;;OAEG;IACH,OAAO,CAAC,QAAQ;IA6GhB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,eAAe;IAQvB,MAAM,IAAI,UAAU;CA+ErB"}
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Pure JavaScript GIF encoder implementation
3
+ * Supports GIF89a format with LZW compression
4
+ */
5
+ import { LZWEncoder } from "./lzw.js";
6
+ export class GIFEncoder {
7
+ constructor(width, height, data) {
8
+ Object.defineProperty(this, "width", {
9
+ enumerable: true,
10
+ configurable: true,
11
+ writable: true,
12
+ value: void 0
13
+ });
14
+ Object.defineProperty(this, "height", {
15
+ enumerable: true,
16
+ configurable: true,
17
+ writable: true,
18
+ value: void 0
19
+ });
20
+ Object.defineProperty(this, "data", {
21
+ enumerable: true,
22
+ configurable: true,
23
+ writable: true,
24
+ value: void 0
25
+ });
26
+ this.width = width;
27
+ this.height = height;
28
+ this.data = data;
29
+ }
30
+ writeBytes(output, bytes) {
31
+ output.push(...bytes);
32
+ }
33
+ writeUint16LE(output, value) {
34
+ output.push(value & 0xff);
35
+ output.push((value >> 8) & 0xff);
36
+ }
37
+ writeString(output, str) {
38
+ for (let i = 0; i < str.length; i++) {
39
+ output.push(str.charCodeAt(i));
40
+ }
41
+ }
42
+ /**
43
+ * Quantize a color channel value to a specified number of levels
44
+ * @param value - The input color channel value (0-255)
45
+ * @param levels - Number of quantization levels minus 1 (e.g., 7 for 8 levels)
46
+ * @param step - Step size for quantization (e.g., 255/7)
47
+ * @returns Quantized integer value
48
+ */
49
+ quantizeChannel(value, levels, step) {
50
+ // First round to get quantization level (0-levels)
51
+ // Then multiply by step size and round to ensure integer result
52
+ return Math.round(Math.round(value * levels / 255) * step);
53
+ }
54
+ /**
55
+ * Quantize RGBA image to 256 colors using median cut algorithm
56
+ */
57
+ quantize() {
58
+ // Simple quantization: collect unique colors and build palette
59
+ const colorMap = new Map();
60
+ const colors = [];
61
+ // Color quantization parameters for 8-bit palette (256 colors)
62
+ // R/G: 8 levels (0-7) using 3 bits, B: 4 levels (0-3) using 2 bits (8*8*4=256)
63
+ const RG_LEVELS = 7; // Max level for R/G (8 total levels: 0-7)
64
+ const B_LEVELS = 3; // Max level for B (4 total levels: 0-3)
65
+ const rgStep = 255 / RG_LEVELS; // Step size for R/G quantization
66
+ const bStep = 255 / B_LEVELS; // Step size for B quantization
67
+ // Collect unique colors
68
+ for (let i = 0; i < this.data.length; i += 4) {
69
+ const r = this.data[i];
70
+ const g = this.data[i + 1];
71
+ const b = this.data[i + 2];
72
+ const key = `${r},${g},${b}`;
73
+ if (!colorMap.has(key) && colors.length < 256) {
74
+ colorMap.set(key, colors.length);
75
+ colors.push({ r, g, b });
76
+ }
77
+ }
78
+ // Track if color reduction was applied
79
+ let useColorReduction = false;
80
+ // If we have too many colors, use simple color reduction
81
+ if (colors.length >= 256) {
82
+ // Downsample colors to 256 by reducing color depth
83
+ colorMap.clear();
84
+ colors.length = 0;
85
+ useColorReduction = true;
86
+ for (let i = 0; i < this.data.length; i += 4) {
87
+ // Reduce color depth: 3 bits for R/G channels, 2 bits for B channel
88
+ // This gives us 8 bits total = 256 possible colors
89
+ const r = this.quantizeChannel(this.data[i], RG_LEVELS, rgStep);
90
+ const g = this.quantizeChannel(this.data[i + 1], RG_LEVELS, rgStep);
91
+ const b = this.quantizeChannel(this.data[i + 2], B_LEVELS, bStep);
92
+ const key = `${r},${g},${b}`;
93
+ if (!colorMap.has(key)) {
94
+ if (colors.length < 256) {
95
+ colorMap.set(key, colors.length);
96
+ colors.push({ r, g, b });
97
+ }
98
+ }
99
+ }
100
+ }
101
+ // Pad to power of 2
102
+ const paletteSize = Math.max(2, this.nextPowerOf2(colors.length));
103
+ while (colors.length < paletteSize) {
104
+ colors.push({ r: 0, g: 0, b: 0 });
105
+ }
106
+ // Create palette
107
+ const palette = new Uint8Array(colors.length * 3);
108
+ for (let i = 0; i < colors.length; i++) {
109
+ palette[i * 3] = colors[i].r;
110
+ palette[i * 3 + 1] = colors[i].g;
111
+ palette[i * 3 + 2] = colors[i].b;
112
+ }
113
+ // Create indexed data
114
+ const indexed = new Uint8Array(this.width * this.height);
115
+ for (let i = 0, j = 0; i < this.data.length; i += 4, j++) {
116
+ let r = this.data[i];
117
+ let g = this.data[i + 1];
118
+ let b = this.data[i + 2];
119
+ // Apply color reduction if it was used for building the palette
120
+ if (useColorReduction) {
121
+ r = this.quantizeChannel(r, RG_LEVELS, rgStep);
122
+ g = this.quantizeChannel(g, RG_LEVELS, rgStep);
123
+ b = this.quantizeChannel(b, B_LEVELS, bStep);
124
+ }
125
+ const key = `${r},${g},${b}`;
126
+ // Try fast O(1) lookup first
127
+ if (colorMap.has(key)) {
128
+ indexed[j] = colorMap.get(key);
129
+ }
130
+ else {
131
+ // Fallback: find closest color in palette (shouldn't happen often)
132
+ let minDist = Infinity;
133
+ let bestIdx = 0;
134
+ for (let k = 0; k < colors.length; k++) {
135
+ const dr = r - colors[k].r;
136
+ const dg = g - colors[k].g;
137
+ const db = b - colors[k].b;
138
+ const dist = dr * dr + dg * dg + db * db;
139
+ if (dist < minDist) {
140
+ minDist = dist;
141
+ bestIdx = k;
142
+ }
143
+ }
144
+ indexed[j] = bestIdx;
145
+ }
146
+ }
147
+ return { palette, indexed };
148
+ }
149
+ nextPowerOf2(n) {
150
+ let power = 1;
151
+ while (power < n) {
152
+ power *= 2;
153
+ }
154
+ return power;
155
+ }
156
+ getBitsPerColor(paletteSize) {
157
+ let bits = 1;
158
+ while ((1 << bits) < paletteSize) {
159
+ bits++;
160
+ }
161
+ return Math.max(2, bits);
162
+ }
163
+ encode() {
164
+ const output = [];
165
+ // Quantize image
166
+ const { palette, indexed } = this.quantize();
167
+ const paletteSize = palette.length / 3;
168
+ const bitsPerColor = this.getBitsPerColor(paletteSize);
169
+ // Header
170
+ this.writeString(output, "GIF89a");
171
+ // Logical Screen Descriptor
172
+ this.writeUint16LE(output, this.width);
173
+ this.writeUint16LE(output, this.height);
174
+ // Packed field:
175
+ // - Global Color Table Flag (1 bit): 1
176
+ // - Color Resolution (3 bits): bitsPerColor - 1
177
+ // - Sort Flag (1 bit): 0
178
+ // - Size of Global Color Table (3 bits): bitsPerColor - 1
179
+ const packed = 0x80 | ((bitsPerColor - 1) << 4) | (bitsPerColor - 1);
180
+ output.push(packed);
181
+ // Background Color Index
182
+ output.push(0);
183
+ // Pixel Aspect Ratio
184
+ output.push(0);
185
+ // Global Color Table
186
+ // The GCT size is 2^(n+1) where n is the value in the packed field
187
+ // So we need to write that many colors, padding if necessary
188
+ const gctSize = 1 << bitsPerColor;
189
+ const paddedPalette = new Uint8Array(gctSize * 3);
190
+ paddedPalette.set(palette);
191
+ this.writeBytes(output, paddedPalette);
192
+ // Image Descriptor
193
+ output.push(0x2c); // Image Separator
194
+ // Image position and dimensions
195
+ this.writeUint16LE(output, 0); // Left
196
+ this.writeUint16LE(output, 0); // Top
197
+ this.writeUint16LE(output, this.width);
198
+ this.writeUint16LE(output, this.height);
199
+ // Packed field:
200
+ // - Local Color Table Flag (1 bit): 0
201
+ // - Interlace Flag (1 bit): 0
202
+ // - Sort Flag (1 bit): 0
203
+ // - Reserved (2 bits): 0
204
+ // - Size of Local Color Table (3 bits): 0
205
+ output.push(0);
206
+ // LZW Minimum Code Size
207
+ const minCodeSize = Math.max(2, bitsPerColor);
208
+ output.push(minCodeSize);
209
+ // Compress image data with LZW
210
+ const encoder = new LZWEncoder(minCodeSize);
211
+ const compressed = encoder.compress(indexed);
212
+ // Write compressed data in sub-blocks (max 255 bytes per block)
213
+ for (let i = 0; i < compressed.length; i += 255) {
214
+ const blockSize = Math.min(255, compressed.length - i);
215
+ output.push(blockSize);
216
+ for (let j = 0; j < blockSize; j++) {
217
+ output.push(compressed[i + j]);
218
+ }
219
+ }
220
+ // Block Terminator
221
+ output.push(0);
222
+ // Trailer
223
+ output.push(0x3b);
224
+ return new Uint8Array(output);
225
+ }
226
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Basic baseline JPEG decoder implementation
3
+ * Supports baseline DCT JPEG images (the most common format)
4
+ *
5
+ * This is a simplified implementation that handles common JPEG files.
6
+ * For complex or non-standard JPEGs, the ImageDecoder API fallback is preferred.
7
+ */
8
+ export declare class JPEGDecoder {
9
+ private data;
10
+ private pos;
11
+ private width;
12
+ private height;
13
+ private components;
14
+ private qTables;
15
+ private dcTables;
16
+ private acTables;
17
+ private restartInterval;
18
+ private bitBuffer;
19
+ private bitCount;
20
+ constructor(data: Uint8Array);
21
+ decode(): Uint8Array;
22
+ private readMarker;
23
+ private readUint16;
24
+ private skipSegment;
25
+ private parseDQT;
26
+ private parseDHT;
27
+ private buildHuffmanTable;
28
+ private parseSOF;
29
+ private parseSOS;
30
+ private parseDRI;
31
+ private decodeScan;
32
+ private decodeBlock;
33
+ private decodeHuffman;
34
+ private readBit;
35
+ private receiveBits;
36
+ private idct;
37
+ private convertToRGB;
38
+ }
39
+ //# sourceMappingURL=jpeg_decoder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jpeg_decoder.d.ts","sourceRoot":"","sources":["../../../src/src/utils/jpeg_decoder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAkGH,qBAAa,WAAW;IACtB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,QAAQ,CAAa;gBAEjB,IAAI,EAAE,UAAU;IAI5B,MAAM,IAAI,UAAU;IAwDpB,OAAO,CAAC,UAAU;IAoBlB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,QAAQ;IAsBhB,OAAO,CAAC,QAAQ;IAgChB,OAAO,CAAC,iBAAiB;IA8BzB,OAAO,CAAC,QAAQ;IAmChB,OAAO,CAAC,QAAQ;IAkBhB,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,UAAU;IAiDlB,OAAO,CAAC,WAAW;IAiDnB,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,OAAO;IA2Bf,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,IAAI;IAqCZ,OAAO,CAAC,YAAY;CAsGrB"}