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,580 @@
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
+ // JPEG markers
9
+ const EOI = 0xFFD9; // End of Image
10
+ const SOS = 0xFFDA; // Start of Scan
11
+ const DQT = 0xFFDB; // Define Quantization Table
12
+ const DHT = 0xFFC4; // Define Huffman Table
13
+ const SOF0 = 0xFFC0; // Start of Frame (Baseline DCT)
14
+ const SOF2 = 0xFFC2; // Start of Frame (Progressive DCT)
15
+ const DRI = 0xFFDD; // Define Restart Interval
16
+ // Zigzag order for DCT coefficients
17
+ const ZIGZAG = [
18
+ 0,
19
+ 1,
20
+ 8,
21
+ 16,
22
+ 9,
23
+ 2,
24
+ 3,
25
+ 10,
26
+ 17,
27
+ 24,
28
+ 32,
29
+ 25,
30
+ 18,
31
+ 11,
32
+ 4,
33
+ 5,
34
+ 12,
35
+ 19,
36
+ 26,
37
+ 33,
38
+ 40,
39
+ 48,
40
+ 41,
41
+ 34,
42
+ 27,
43
+ 20,
44
+ 13,
45
+ 6,
46
+ 7,
47
+ 14,
48
+ 21,
49
+ 28,
50
+ 35,
51
+ 42,
52
+ 49,
53
+ 56,
54
+ 57,
55
+ 50,
56
+ 43,
57
+ 36,
58
+ 29,
59
+ 22,
60
+ 15,
61
+ 23,
62
+ 30,
63
+ 37,
64
+ 44,
65
+ 51,
66
+ 58,
67
+ 59,
68
+ 52,
69
+ 45,
70
+ 38,
71
+ 31,
72
+ 39,
73
+ 46,
74
+ 53,
75
+ 60,
76
+ 61,
77
+ 54,
78
+ 47,
79
+ 55,
80
+ 62,
81
+ 63,
82
+ ];
83
+ export class JPEGDecoder {
84
+ constructor(data) {
85
+ Object.defineProperty(this, "data", {
86
+ enumerable: true,
87
+ configurable: true,
88
+ writable: true,
89
+ value: void 0
90
+ });
91
+ Object.defineProperty(this, "pos", {
92
+ enumerable: true,
93
+ configurable: true,
94
+ writable: true,
95
+ value: 0
96
+ });
97
+ Object.defineProperty(this, "width", {
98
+ enumerable: true,
99
+ configurable: true,
100
+ writable: true,
101
+ value: 0
102
+ });
103
+ Object.defineProperty(this, "height", {
104
+ enumerable: true,
105
+ configurable: true,
106
+ writable: true,
107
+ value: 0
108
+ });
109
+ Object.defineProperty(this, "components", {
110
+ enumerable: true,
111
+ configurable: true,
112
+ writable: true,
113
+ value: []
114
+ });
115
+ Object.defineProperty(this, "qTables", {
116
+ enumerable: true,
117
+ configurable: true,
118
+ writable: true,
119
+ value: []
120
+ });
121
+ Object.defineProperty(this, "dcTables", {
122
+ enumerable: true,
123
+ configurable: true,
124
+ writable: true,
125
+ value: []
126
+ });
127
+ Object.defineProperty(this, "acTables", {
128
+ enumerable: true,
129
+ configurable: true,
130
+ writable: true,
131
+ value: []
132
+ });
133
+ Object.defineProperty(this, "restartInterval", {
134
+ enumerable: true,
135
+ configurable: true,
136
+ writable: true,
137
+ value: 0
138
+ });
139
+ Object.defineProperty(this, "bitBuffer", {
140
+ enumerable: true,
141
+ configurable: true,
142
+ writable: true,
143
+ value: 0
144
+ });
145
+ Object.defineProperty(this, "bitCount", {
146
+ enumerable: true,
147
+ configurable: true,
148
+ writable: true,
149
+ value: 0
150
+ });
151
+ this.data = data;
152
+ }
153
+ decode() {
154
+ // Verify JPEG signature
155
+ if (this.data.length < 2 || this.data[0] !== 0xFF || this.data[1] !== 0xD8) {
156
+ throw new Error("Invalid JPEG signature");
157
+ }
158
+ this.pos = 2;
159
+ // Parse markers
160
+ while (this.pos < this.data.length) {
161
+ const marker = this.readMarker();
162
+ if (marker === EOI) {
163
+ break;
164
+ }
165
+ else if (marker === SOS) {
166
+ this.parseSOS();
167
+ this.decodeScan();
168
+ break; // Stop after first scan for baseline JPEG
169
+ }
170
+ else if (marker === DQT) {
171
+ this.parseDQT();
172
+ }
173
+ else if (marker === DHT) {
174
+ this.parseDHT();
175
+ }
176
+ else if (marker === SOF0) {
177
+ this.parseSOF();
178
+ }
179
+ else if (marker === SOF2) {
180
+ throw new Error("Progressive JPEG not supported in pure JS decoder");
181
+ }
182
+ else if (marker === DRI) {
183
+ this.parseDRI();
184
+ }
185
+ else if (marker >= 0xFFE0 && marker <= 0xFFEF) {
186
+ // Skip APP markers
187
+ this.skipSegment();
188
+ }
189
+ else if (marker >= 0xFFC0 && marker <= 0xFFCF) {
190
+ // Other SOF markers
191
+ if (marker !== 0xFFC4 && marker !== 0xFFC8 && marker !== 0xFFCC) {
192
+ throw new Error(`Unsupported JPEG type: marker 0x${marker.toString(16)}`);
193
+ }
194
+ }
195
+ else {
196
+ // Skip unknown markers
197
+ if (this.pos < this.data.length) {
198
+ this.skipSegment();
199
+ }
200
+ }
201
+ }
202
+ if (this.width === 0 || this.height === 0) {
203
+ throw new Error("Failed to decode JPEG: invalid dimensions");
204
+ }
205
+ // Convert YCbCr to RGB
206
+ return this.convertToRGB();
207
+ }
208
+ readMarker() {
209
+ while (this.pos < this.data.length && this.data[this.pos] !== 0xFF) {
210
+ this.pos++;
211
+ }
212
+ if (this.pos >= this.data.length - 1) {
213
+ return EOI;
214
+ }
215
+ const byte1 = this.data[this.pos++];
216
+ let byte2 = this.data[this.pos++];
217
+ // Skip padding 0xFF bytes
218
+ while (byte2 === 0xFF && this.pos < this.data.length) {
219
+ byte2 = this.data[this.pos++];
220
+ }
221
+ return (byte1 << 8) | byte2;
222
+ }
223
+ readUint16() {
224
+ const value = (this.data[this.pos] << 8) | this.data[this.pos + 1];
225
+ this.pos += 2;
226
+ return value;
227
+ }
228
+ skipSegment() {
229
+ const length = this.readUint16();
230
+ this.pos += length - 2;
231
+ }
232
+ parseDQT() {
233
+ let length = this.readUint16() - 2;
234
+ while (length > 0) {
235
+ const info = this.data[this.pos++];
236
+ const tableId = info & 0x0F;
237
+ const precision = (info >> 4) & 0x0F;
238
+ if (precision !== 0) {
239
+ throw new Error("16-bit quantization tables not supported");
240
+ }
241
+ const table = new Array(64);
242
+ for (let i = 0; i < 64; i++) {
243
+ table[ZIGZAG[i]] = this.data[this.pos++];
244
+ }
245
+ this.qTables[tableId] = table;
246
+ length -= 65;
247
+ }
248
+ }
249
+ parseDHT() {
250
+ let length = this.readUint16() - 2;
251
+ while (length > 0) {
252
+ const info = this.data[this.pos++];
253
+ const tableId = info & 0x0F;
254
+ const tableClass = (info >> 4) & 0x0F;
255
+ const bits = new Array(16);
256
+ let numSymbols = 0;
257
+ for (let i = 0; i < 16; i++) {
258
+ bits[i] = this.data[this.pos++];
259
+ numSymbols += bits[i];
260
+ }
261
+ const huffVal = new Array(numSymbols);
262
+ for (let i = 0; i < numSymbols; i++) {
263
+ huffVal[i] = this.data[this.pos++];
264
+ }
265
+ const table = this.buildHuffmanTable(bits, huffVal);
266
+ if (tableClass === 0) {
267
+ this.dcTables[tableId] = table;
268
+ }
269
+ else {
270
+ this.acTables[tableId] = table;
271
+ }
272
+ length -= 17 + numSymbols;
273
+ }
274
+ }
275
+ buildHuffmanTable(bits, huffVal) {
276
+ const maxCode = new Array(16).fill(-1);
277
+ const minCode = new Array(16).fill(-1);
278
+ const valPtr = new Array(16).fill(-1);
279
+ const codes = new Map();
280
+ let code = 0;
281
+ let valIndex = 0;
282
+ for (let i = 0; i < 16; i++) {
283
+ if (bits[i] > 0) {
284
+ minCode[i] = code;
285
+ valPtr[i] = valIndex;
286
+ for (let j = 0; j < bits[i]; j++) {
287
+ codes.set((i << 16) | code, huffVal[valIndex]);
288
+ code++;
289
+ valIndex++;
290
+ }
291
+ maxCode[i] = code - 1;
292
+ code <<= 1;
293
+ }
294
+ else {
295
+ code <<= 1;
296
+ }
297
+ }
298
+ return { codes, maxCode, minCode, valPtr, huffVal };
299
+ }
300
+ parseSOF() {
301
+ const _length = this.readUint16();
302
+ const precision = this.data[this.pos++];
303
+ if (precision !== 8) {
304
+ throw new Error(`Unsupported precision: ${precision}`);
305
+ }
306
+ this.height = this.readUint16();
307
+ this.width = this.readUint16();
308
+ const numComponents = this.data[this.pos++];
309
+ if (numComponents !== 1 && numComponents !== 3) {
310
+ throw new Error(`Unsupported number of components: ${numComponents}`);
311
+ }
312
+ this.components = [];
313
+ for (let i = 0; i < numComponents; i++) {
314
+ const id = this.data[this.pos++];
315
+ const samplingFactor = this.data[this.pos++];
316
+ const qTable = this.data[this.pos++];
317
+ this.components.push({
318
+ id,
319
+ h: (samplingFactor >> 4) & 0x0F,
320
+ v: samplingFactor & 0x0F,
321
+ qTable,
322
+ dcTable: 0,
323
+ acTable: 0,
324
+ pred: 0,
325
+ blocks: [],
326
+ });
327
+ }
328
+ }
329
+ parseSOS() {
330
+ const _length = this.readUint16();
331
+ const numComponents = this.data[this.pos++];
332
+ for (let i = 0; i < numComponents; i++) {
333
+ const id = this.data[this.pos++];
334
+ const tables = this.data[this.pos++];
335
+ const component = this.components.find((c) => c.id === id);
336
+ if (component) {
337
+ component.dcTable = (tables >> 4) & 0x0F;
338
+ component.acTable = tables & 0x0F;
339
+ }
340
+ }
341
+ this.pos += 3; // Skip spectral selection and successive approximation
342
+ }
343
+ parseDRI() {
344
+ const _length = this.readUint16();
345
+ this.restartInterval = this.readUint16();
346
+ }
347
+ decodeScan() {
348
+ // Calculate MCU dimensions
349
+ const maxH = Math.max(...this.components.map((c) => c.h));
350
+ const maxV = Math.max(...this.components.map((c) => c.v));
351
+ const mcuWidth = Math.ceil(this.width / (8 * maxH));
352
+ const mcuHeight = Math.ceil(this.height / (8 * maxV));
353
+ // Initialize bit buffer
354
+ this.bitBuffer = 0;
355
+ this.bitCount = 0;
356
+ // Initialize blocks for each component
357
+ for (const component of this.components) {
358
+ const blocksAcross = Math.ceil(this.width * component.h / (8 * maxH));
359
+ const blocksDown = Math.ceil(this.height * component.v / (8 * maxV));
360
+ component.blocks = Array(blocksDown).fill(null).map(() => Array(blocksAcross).fill(null).map(() => new Array(64).fill(0)));
361
+ }
362
+ // Decode MCUs
363
+ try {
364
+ for (let mcuY = 0; mcuY < mcuHeight; mcuY++) {
365
+ for (let mcuX = 0; mcuX < mcuWidth; mcuX++) {
366
+ // Decode all components in this MCU
367
+ for (const component of this.components) {
368
+ for (let v = 0; v < component.v; v++) {
369
+ for (let h = 0; h < component.h; h++) {
370
+ const blockY = mcuY * component.v + v;
371
+ const blockX = mcuX * component.h + h;
372
+ if (blockY < component.blocks.length &&
373
+ blockX < component.blocks[0].length) {
374
+ this.decodeBlock(component, blockY, blockX);
375
+ }
376
+ }
377
+ }
378
+ }
379
+ }
380
+ }
381
+ }
382
+ catch (e) {
383
+ // If we run into issues during decoding, we may still have partial data
384
+ console.warn("JPEG decode warning:", e);
385
+ }
386
+ }
387
+ decodeBlock(component, blockY, blockX) {
388
+ const block = component.blocks[blockY][blockX];
389
+ // Decode DC coefficient
390
+ const dcTable = this.dcTables[component.dcTable];
391
+ if (!dcTable) {
392
+ throw new Error(`Missing DC table ${component.dcTable}`);
393
+ }
394
+ const dcLen = this.decodeHuffman(dcTable);
395
+ const dcDiff = dcLen > 0 ? this.receiveBits(dcLen) : 0;
396
+ component.pred += dcDiff;
397
+ block[0] = component.pred * this.qTables[component.qTable][0];
398
+ // Decode AC coefficients
399
+ const acTable = this.acTables[component.acTable];
400
+ if (!acTable) {
401
+ throw new Error(`Missing AC table ${component.acTable}`);
402
+ }
403
+ let k = 1;
404
+ while (k < 64) {
405
+ const rs = this.decodeHuffman(acTable);
406
+ const r = (rs >> 4) & 0x0F;
407
+ const s = rs & 0x0F;
408
+ if (s === 0) {
409
+ if (r === 15) {
410
+ k += 16;
411
+ }
412
+ else {
413
+ break; // EOB
414
+ }
415
+ }
416
+ else {
417
+ k += r;
418
+ if (k >= 64)
419
+ break;
420
+ block[ZIGZAG[k]] = this.receiveBits(s) *
421
+ this.qTables[component.qTable][ZIGZAG[k]];
422
+ k++;
423
+ }
424
+ }
425
+ // Perform IDCT
426
+ this.idct(block);
427
+ }
428
+ decodeHuffman(table) {
429
+ let code = 0;
430
+ for (let len = 0; len < 16; len++) {
431
+ code = (code << 1) | this.readBit();
432
+ if (table.minCode[len] !== -1 && code <= table.maxCode[len]) {
433
+ const index = table.valPtr[len] + (code - table.minCode[len]);
434
+ return table.huffVal[index];
435
+ }
436
+ }
437
+ throw new Error("Invalid Huffman code");
438
+ }
439
+ readBit() {
440
+ if (this.bitCount === 0) {
441
+ let byte = this.data[this.pos++];
442
+ // Handle byte stuffing (0xFF 0x00)
443
+ if (byte === 0xFF) {
444
+ const nextByte = this.data[this.pos];
445
+ if (nextByte === 0x00) {
446
+ this.pos++;
447
+ }
448
+ else if (nextByte >= 0xD0 && nextByte <= 0xD7) {
449
+ // Restart marker - reset DC predictors
450
+ this.pos++;
451
+ for (const component of this.components) {
452
+ component.pred = 0;
453
+ }
454
+ byte = this.data[this.pos++];
455
+ }
456
+ }
457
+ this.bitBuffer = byte;
458
+ this.bitCount = 8;
459
+ }
460
+ this.bitCount--;
461
+ return (this.bitBuffer >> this.bitCount) & 1;
462
+ }
463
+ receiveBits(n) {
464
+ let value = 0;
465
+ for (let i = 0; i < n; i++) {
466
+ value = (value << 1) | this.readBit();
467
+ }
468
+ // Convert from magnitude representation
469
+ if (value < (1 << (n - 1))) {
470
+ value = value - (1 << n) + 1;
471
+ }
472
+ return value;
473
+ }
474
+ idct(block) {
475
+ // Simplified 2D IDCT
476
+ // This is a basic implementation - not optimized
477
+ const temp = new Array(64);
478
+ // 1D IDCT on rows
479
+ for (let i = 0; i < 8; i++) {
480
+ const offset = i * 8;
481
+ for (let j = 0; j < 8; j++) {
482
+ let sum = 0;
483
+ for (let k = 0; k < 8; k++) {
484
+ const c = k === 0 ? 1 / Math.sqrt(2) : 1;
485
+ sum += c * block[offset + k] *
486
+ Math.cos((2 * j + 1) * k * Math.PI / 16);
487
+ }
488
+ temp[offset + j] = sum / 2;
489
+ }
490
+ }
491
+ // 1D IDCT on columns
492
+ for (let j = 0; j < 8; j++) {
493
+ for (let i = 0; i < 8; i++) {
494
+ let sum = 0;
495
+ for (let k = 0; k < 8; k++) {
496
+ const c = k === 0 ? 1 / Math.sqrt(2) : 1;
497
+ sum += c * temp[k * 8 + j] * Math.cos((2 * i + 1) * k * Math.PI / 16);
498
+ }
499
+ // Level shift and clamp
500
+ block[i * 8 + j] = Math.max(0, Math.min(255, Math.round(sum / 2 + 128)));
501
+ }
502
+ }
503
+ }
504
+ convertToRGB() {
505
+ const rgba = new Uint8Array(this.width * this.height * 4);
506
+ if (this.components.length === 1) {
507
+ // Grayscale
508
+ const y = this.components[0];
509
+ for (let row = 0; row < this.height; row++) {
510
+ for (let col = 0; col < this.width; col++) {
511
+ const blockRow = Math.floor(row / 8);
512
+ const blockCol = Math.floor(col / 8);
513
+ const blockY = row % 8;
514
+ const blockX = col % 8;
515
+ if (blockRow < y.blocks.length && blockCol < y.blocks[0].length) {
516
+ const value = y.blocks[blockRow][blockCol][blockY * 8 + blockX];
517
+ const offset = (row * this.width + col) * 4;
518
+ rgba[offset] = value;
519
+ rgba[offset + 1] = value;
520
+ rgba[offset + 2] = value;
521
+ rgba[offset + 3] = 255;
522
+ }
523
+ }
524
+ }
525
+ }
526
+ else {
527
+ // YCbCr to RGB
528
+ const [y, cb, cr] = this.components;
529
+ const maxH = Math.max(...this.components.map((c) => c.h));
530
+ const maxV = Math.max(...this.components.map((c) => c.v));
531
+ for (let row = 0; row < this.height; row++) {
532
+ for (let col = 0; col < this.width; col++) {
533
+ // Y component
534
+ const yBlockRow = Math.floor(row / 8);
535
+ const yBlockCol = Math.floor(col / 8);
536
+ const yBlockY = row % 8;
537
+ const yBlockX = col % 8;
538
+ let yVal = 0;
539
+ if (yBlockRow < y.blocks.length && yBlockCol < y.blocks[0].length) {
540
+ yVal = y.blocks[yBlockRow][yBlockCol][yBlockY * 8 + yBlockX];
541
+ }
542
+ // Cb and Cr components (may be subsampled)
543
+ // Scale pixel position by subsampling factor, then get block and within-block positions
544
+ const cbRow = Math.floor(row * cb.v / maxV);
545
+ const cbCol = Math.floor(col * cb.h / maxH);
546
+ const cbBlockRow = Math.floor(cbRow / 8);
547
+ const cbBlockCol = Math.floor(cbCol / 8);
548
+ const cbBlockY = cbRow % 8;
549
+ const cbBlockX = cbCol % 8;
550
+ let cbVal = 0;
551
+ if (cbBlockRow < cb.blocks.length && cbBlockCol < cb.blocks[0].length) {
552
+ cbVal = cb.blocks[cbBlockRow][cbBlockCol][cbBlockY * 8 + cbBlockX] -
553
+ 128;
554
+ }
555
+ const crRow = Math.floor(row * cr.v / maxV);
556
+ const crCol = Math.floor(col * cr.h / maxH);
557
+ const crBlockRow = Math.floor(crRow / 8);
558
+ const crBlockCol = Math.floor(crCol / 8);
559
+ const crBlockY = crRow % 8;
560
+ const crBlockX = crCol % 8;
561
+ let crVal = 0;
562
+ if (crBlockRow < cr.blocks.length && crBlockCol < cr.blocks[0].length) {
563
+ crVal = cr.blocks[crBlockRow][crBlockCol][crBlockY * 8 + crBlockX] -
564
+ 128;
565
+ }
566
+ // YCbCr to RGB conversion
567
+ const r = Math.max(0, Math.min(255, Math.round(yVal + 1.402 * crVal)));
568
+ const g = Math.max(0, Math.min(255, Math.round(yVal - 0.344136 * cbVal - 0.714136 * crVal)));
569
+ const b = Math.max(0, Math.min(255, Math.round(yVal + 1.772 * cbVal)));
570
+ const offset = (row * this.width + col) * 4;
571
+ rgba[offset] = r;
572
+ rgba[offset + 1] = g;
573
+ rgba[offset + 2] = b;
574
+ rgba[offset + 3] = 255;
575
+ }
576
+ }
577
+ }
578
+ return rgba;
579
+ }
580
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Basic baseline JPEG encoder implementation
3
+ * Produces valid baseline DCT JPEG images
4
+ *
5
+ * This is a simplified implementation focusing on correctness over performance.
6
+ * For production use with better quality/size, the OffscreenCanvas API is preferred.
7
+ */
8
+ export declare class JPEGEncoder {
9
+ private quality;
10
+ private luminanceQuantTable;
11
+ private chrominanceQuantTable;
12
+ private dcLuminanceHuffman;
13
+ private acLuminanceHuffman;
14
+ private dcChrominanceHuffman;
15
+ private acChrominanceHuffman;
16
+ constructor(quality?: number);
17
+ private initQuantizationTables;
18
+ private initHuffmanTables;
19
+ private buildHuffmanTable;
20
+ encode(width: number, height: number, rgba: Uint8Array, dpiX?: number, dpiY?: number): Uint8Array;
21
+ private writeAPP0;
22
+ private writeDQT;
23
+ private writeSOF0;
24
+ private writeDHT;
25
+ private writeHuffmanTable;
26
+ private writeSOS;
27
+ private encodeScan;
28
+ private encodeBlock;
29
+ private forwardDCT;
30
+ private encodeDC;
31
+ private encodeAC;
32
+ }
33
+ //# sourceMappingURL=jpeg_encoder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jpeg_encoder.d.ts","sourceRoot":"","sources":["../../../src/src/utils/jpeg_encoder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAgrBH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,mBAAmB,CAAgB;IAC3C,OAAO,CAAC,qBAAqB,CAAgB;IAC7C,OAAO,CAAC,kBAAkB,CAAgB;IAC1C,OAAO,CAAC,kBAAkB,CAAgB;IAC1C,OAAO,CAAC,oBAAoB,CAAgB;IAC5C,OAAO,CAAC,oBAAoB,CAAgB;gBAEhC,OAAO,GAAE,MAAW;IAMhC,OAAO,CAAC,sBAAsB;IAqB9B,OAAO,CAAC,iBAAiB;IAmBzB,OAAO,CAAC,iBAAiB;IAqBzB,MAAM,CACJ,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,EAChB,IAAI,SAAK,EACT,IAAI,SAAK,GACR,UAAU;IAiCb,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,QAAQ;IAkBhB,OAAO,CAAC,SAAS;IAwBjB,OAAO,CAAC,QAAQ;IAkChB,OAAO,CAAC,iBAAiB;IA8BzB,OAAO,CAAC,QAAQ;IAsBhB,OAAO,CAAC,UAAU;IA2ElB,OAAO,CAAC,WAAW;IA4BnB,OAAO,CAAC,UAAU;IA8BlB,OAAO,CAAC,QAAQ;IAsBhB,OAAO,CAAC,QAAQ;CAqCjB"}