cross-image 0.2.0 → 0.2.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 (62) hide show
  1. package/README.md +41 -28
  2. package/esm/mod.d.ts +4 -1
  3. package/esm/mod.js +4 -1
  4. package/esm/src/formats/apng.d.ts +50 -0
  5. package/esm/src/formats/apng.js +364 -0
  6. package/esm/src/formats/bmp.d.ts +0 -6
  7. package/esm/src/formats/bmp.js +24 -47
  8. package/esm/src/formats/dng.js +4 -4
  9. package/esm/src/formats/gif.d.ts +0 -2
  10. package/esm/src/formats/gif.js +10 -16
  11. package/esm/src/formats/ico.d.ts +41 -0
  12. package/esm/src/formats/ico.js +214 -0
  13. package/esm/src/formats/pcx.js +1 -1
  14. package/esm/src/formats/png.d.ts +2 -21
  15. package/esm/src/formats/png.js +5 -429
  16. package/esm/src/formats/png_base.d.ts +108 -0
  17. package/esm/src/formats/png_base.js +487 -0
  18. package/esm/src/formats/ppm.d.ts +50 -0
  19. package/esm/src/formats/ppm.js +242 -0
  20. package/esm/src/formats/tiff.d.ts +4 -0
  21. package/esm/src/formats/tiff.js +163 -44
  22. package/esm/src/formats/webp.d.ts +0 -1
  23. package/esm/src/formats/webp.js +4 -7
  24. package/esm/src/image.d.ts +30 -0
  25. package/esm/src/image.js +62 -1
  26. package/esm/src/utils/byte_utils.d.ts +30 -0
  27. package/esm/src/utils/byte_utils.js +50 -0
  28. package/esm/src/utils/gif_encoder.d.ts +3 -2
  29. package/esm/src/utils/gif_encoder.js +115 -48
  30. package/esm/src/utils/image_processing.d.ts +43 -0
  31. package/esm/src/utils/image_processing.js +230 -0
  32. package/package.json +1 -1
  33. package/script/mod.d.ts +4 -1
  34. package/script/mod.js +8 -2
  35. package/script/src/formats/apng.d.ts +50 -0
  36. package/script/src/formats/apng.js +368 -0
  37. package/script/src/formats/bmp.d.ts +0 -6
  38. package/script/src/formats/bmp.js +24 -47
  39. package/script/src/formats/dng.js +4 -4
  40. package/script/src/formats/gif.d.ts +0 -2
  41. package/script/src/formats/gif.js +10 -16
  42. package/script/src/formats/ico.d.ts +41 -0
  43. package/script/src/formats/ico.js +218 -0
  44. package/script/src/formats/pcx.js +1 -1
  45. package/script/src/formats/png.d.ts +2 -21
  46. package/script/src/formats/png.js +5 -429
  47. package/script/src/formats/png_base.d.ts +108 -0
  48. package/script/src/formats/png_base.js +491 -0
  49. package/script/src/formats/ppm.d.ts +50 -0
  50. package/script/src/formats/ppm.js +246 -0
  51. package/script/src/formats/tiff.d.ts +4 -0
  52. package/script/src/formats/tiff.js +163 -44
  53. package/script/src/formats/webp.d.ts +0 -1
  54. package/script/src/formats/webp.js +4 -7
  55. package/script/src/image.d.ts +30 -0
  56. package/script/src/image.js +61 -0
  57. package/script/src/utils/byte_utils.d.ts +30 -0
  58. package/script/src/utils/byte_utils.js +58 -0
  59. package/script/src/utils/gif_encoder.d.ts +3 -2
  60. package/script/src/utils/gif_encoder.js +115 -48
  61. package/script/src/utils/image_processing.d.ts +43 -0
  62. package/script/src/utils/image_processing.js +235 -0
@@ -2,14 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PNGFormat = void 0;
4
4
  const security_js_1 = require("../utils/security.js");
5
- // Constants for unit conversions
6
- const INCHES_PER_METER = 39.3701;
5
+ const png_base_js_1 = require("./png_base.js");
7
6
  /**
8
7
  * PNG format handler
9
8
  * Implements a pure JavaScript PNG decoder and encoder
10
9
  */
11
- class PNGFormat {
10
+ class PNGFormat extends png_base_js_1.PNGBase {
12
11
  constructor() {
12
+ super(...arguments);
13
13
  /** Format name identifier */
14
14
  Object.defineProperty(this, "name", {
15
15
  enumerable: true,
@@ -134,435 +134,11 @@ class PNGFormat {
134
134
  chunks.push(new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10])); // PNG signature
135
135
  chunks.push(this.createChunk("IHDR", ihdr));
136
136
  // Add metadata chunks if available
137
- if (metadata) {
138
- // Add pHYs chunk for DPI information
139
- if (metadata.dpiX !== undefined || metadata.dpiY !== undefined) {
140
- const physChunk = this.createPhysChunk(metadata);
141
- chunks.push(this.createChunk("pHYs", physChunk));
142
- }
143
- // Add tEXt chunks for standard metadata
144
- if (metadata.title !== undefined) {
145
- chunks.push(this.createChunk("tEXt", this.createTextChunk("Title", metadata.title)));
146
- }
147
- if (metadata.author !== undefined) {
148
- chunks.push(this.createChunk("tEXt", this.createTextChunk("Author", metadata.author)));
149
- }
150
- if (metadata.description !== undefined) {
151
- chunks.push(this.createChunk("tEXt", this.createTextChunk("Description", metadata.description)));
152
- }
153
- if (metadata.copyright !== undefined) {
154
- chunks.push(this.createChunk("tEXt", this.createTextChunk("Copyright", metadata.copyright)));
155
- }
156
- // Add custom metadata fields
157
- if (metadata.custom) {
158
- for (const [key, value] of Object.entries(metadata.custom)) {
159
- chunks.push(this.createChunk("tEXt", this.createTextChunk(key, String(value))));
160
- }
161
- }
162
- // Add EXIF chunk for GPS data and creation date
163
- if (metadata.latitude !== undefined || metadata.longitude !== undefined ||
164
- metadata.creationDate !== undefined) {
165
- const exifChunk = this.createExifChunk(metadata);
166
- if (exifChunk) {
167
- chunks.push(this.createChunk("eXIf", exifChunk));
168
- }
169
- }
170
- }
137
+ this.addMetadataChunks(chunks, metadata);
171
138
  chunks.push(this.createChunk("IDAT", compressed));
172
139
  chunks.push(this.createChunk("IEND", new Uint8Array(0)));
173
140
  // Concatenate all chunks
174
- const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
175
- const result = new Uint8Array(totalLength);
176
- let offset = 0;
177
- for (const chunk of chunks) {
178
- result.set(chunk, offset);
179
- offset += chunk.length;
180
- }
181
- return result;
182
- }
183
- readUint32(data, offset) {
184
- return (data[offset] << 24) | (data[offset + 1] << 16) |
185
- (data[offset + 2] << 8) | data[offset + 3];
186
- }
187
- writeUint32(data, offset, value) {
188
- data[offset] = (value >>> 24) & 0xff;
189
- data[offset + 1] = (value >>> 16) & 0xff;
190
- data[offset + 2] = (value >>> 8) & 0xff;
191
- data[offset + 3] = value & 0xff;
192
- }
193
- concatenateChunks(chunks) {
194
- const totalLength = chunks.reduce((sum, chunk) => sum + chunk.data.length, 0);
195
- const result = new Uint8Array(totalLength);
196
- let offset = 0;
197
- for (const chunk of chunks) {
198
- result.set(chunk.data, offset);
199
- offset += chunk.data.length;
200
- }
201
- return result;
202
- }
203
- async inflate(data) {
204
- // Use DecompressionStream API (available in Deno, Node 17+, and browsers)
205
- const stream = new Response(data).body
206
- .pipeThrough(new DecompressionStream("deflate"));
207
- const decompressed = await new Response(stream).arrayBuffer();
208
- return new Uint8Array(decompressed);
209
- }
210
- async deflate(data) {
211
- // Use CompressionStream API (available in Deno, Node 17+, and browsers)
212
- const stream = new Response(data).body
213
- .pipeThrough(new CompressionStream("deflate"));
214
- const compressed = await new Response(stream).arrayBuffer();
215
- return new Uint8Array(compressed);
216
- }
217
- unfilterAndConvert(data, width, height, bitDepth, colorType) {
218
- const rgba = new Uint8Array(width * height * 4);
219
- const bytesPerPixel = this.getBytesPerPixel(colorType, bitDepth);
220
- const scanlineLength = width * bytesPerPixel;
221
- let dataPos = 0;
222
- const scanlines = [];
223
- for (let y = 0; y < height; y++) {
224
- const filterType = data[dataPos++];
225
- const scanline = new Uint8Array(scanlineLength);
226
- for (let x = 0; x < scanlineLength; x++) {
227
- scanline[x] = data[dataPos++];
228
- }
229
- this.unfilterScanline(scanline, y > 0 ? scanlines[y - 1] : null, filterType, bytesPerPixel);
230
- scanlines.push(scanline);
231
- // Convert to RGBA
232
- for (let x = 0; x < width; x++) {
233
- const outIdx = (y * width + x) * 4;
234
- if (colorType === 6) { // RGBA
235
- rgba[outIdx] = scanline[x * 4];
236
- rgba[outIdx + 1] = scanline[x * 4 + 1];
237
- rgba[outIdx + 2] = scanline[x * 4 + 2];
238
- rgba[outIdx + 3] = scanline[x * 4 + 3];
239
- }
240
- else if (colorType === 2) { // RGB
241
- rgba[outIdx] = scanline[x * 3];
242
- rgba[outIdx + 1] = scanline[x * 3 + 1];
243
- rgba[outIdx + 2] = scanline[x * 3 + 2];
244
- rgba[outIdx + 3] = 255;
245
- }
246
- else if (colorType === 0) { // Grayscale
247
- const gray = scanline[x];
248
- rgba[outIdx] = gray;
249
- rgba[outIdx + 1] = gray;
250
- rgba[outIdx + 2] = gray;
251
- rgba[outIdx + 3] = 255;
252
- }
253
- else {
254
- throw new Error(`Unsupported PNG color type: ${colorType}`);
255
- }
256
- }
257
- }
258
- return rgba;
259
- }
260
- unfilterScanline(scanline, prevLine, filterType, bytesPerPixel) {
261
- for (let x = 0; x < scanline.length; x++) {
262
- const left = x >= bytesPerPixel ? scanline[x - bytesPerPixel] : 0;
263
- const above = prevLine ? prevLine[x] : 0;
264
- const upperLeft = (x >= bytesPerPixel && prevLine)
265
- ? prevLine[x - bytesPerPixel]
266
- : 0;
267
- switch (filterType) {
268
- case 0: // None
269
- break;
270
- case 1: // Sub
271
- scanline[x] = (scanline[x] + left) & 0xff;
272
- break;
273
- case 2: // Up
274
- scanline[x] = (scanline[x] + above) & 0xff;
275
- break;
276
- case 3: // Average
277
- scanline[x] = (scanline[x] + Math.floor((left + above) / 2)) & 0xff;
278
- break;
279
- case 4: // Paeth
280
- scanline[x] =
281
- (scanline[x] + this.paethPredictor(left, above, upperLeft)) & 0xff;
282
- break;
283
- }
284
- }
285
- }
286
- paethPredictor(a, b, c) {
287
- const p = a + b - c;
288
- const pa = Math.abs(p - a);
289
- const pb = Math.abs(p - b);
290
- const pc = Math.abs(p - c);
291
- if (pa <= pb && pa <= pc)
292
- return a;
293
- if (pb <= pc)
294
- return b;
295
- return c;
296
- }
297
- filterData(data, width, height) {
298
- // Use filter type 0 (None) for simplicity
299
- const filtered = new Uint8Array(height * (1 + width * 4));
300
- let pos = 0;
301
- for (let y = 0; y < height; y++) {
302
- filtered[pos++] = 0; // Filter type: None
303
- for (let x = 0; x < width * 4; x++) {
304
- filtered[pos++] = data[y * width * 4 + x];
305
- }
306
- }
307
- return filtered;
308
- }
309
- getBytesPerPixel(colorType, bitDepth) {
310
- const bitsPerPixel = this.getBitsPerPixel(colorType, bitDepth);
311
- return Math.ceil(bitsPerPixel / 8);
312
- }
313
- getBitsPerPixel(colorType, bitDepth) {
314
- switch (colorType) {
315
- case 0: // Grayscale
316
- return bitDepth;
317
- case 2: // RGB
318
- return bitDepth * 3;
319
- case 3: // Palette
320
- return bitDepth;
321
- case 4: // Grayscale + Alpha
322
- return bitDepth * 2;
323
- case 6: // RGBA
324
- return bitDepth * 4;
325
- default:
326
- throw new Error(`Unknown color type: ${colorType}`);
327
- }
328
- }
329
- createChunk(type, data) {
330
- const chunk = new Uint8Array(12 + data.length);
331
- this.writeUint32(chunk, 0, data.length);
332
- chunk[4] = type.charCodeAt(0);
333
- chunk[5] = type.charCodeAt(1);
334
- chunk[6] = type.charCodeAt(2);
335
- chunk[7] = type.charCodeAt(3);
336
- chunk.set(data, 8);
337
- const crc = this.crc32(chunk.slice(4, 8 + data.length));
338
- this.writeUint32(chunk, 8 + data.length, crc);
339
- return chunk;
340
- }
341
- crc32(data) {
342
- let crc = 0xffffffff;
343
- for (let i = 0; i < data.length; i++) {
344
- crc ^= data[i];
345
- for (let j = 0; j < 8; j++) {
346
- crc = (crc & 1) ? (crc >>> 1) ^ 0xedb88320 : crc >>> 1;
347
- }
348
- }
349
- return (crc ^ 0xffffffff) >>> 0;
350
- }
351
- // Metadata parsing methods
352
- parsePhysChunk(data, metadata, width, height) {
353
- if (data.length < 9)
354
- return;
355
- const pixelsPerUnitX = this.readUint32(data, 0);
356
- const pixelsPerUnitY = this.readUint32(data, 4);
357
- const unit = data[8]; // 0 = unknown, 1 = meter
358
- if (unit === 1 && pixelsPerUnitX > 0 && pixelsPerUnitY > 0) {
359
- // Convert pixels per meter to DPI
360
- metadata.dpiX = Math.round(pixelsPerUnitX / INCHES_PER_METER);
361
- metadata.dpiY = Math.round(pixelsPerUnitY / INCHES_PER_METER);
362
- metadata.physicalWidth = width / metadata.dpiX;
363
- metadata.physicalHeight = height / metadata.dpiY;
364
- }
365
- }
366
- parseTextChunk(data, metadata) {
367
- // tEXt format: keyword\0text
368
- const nullIndex = data.indexOf(0);
369
- if (nullIndex === -1)
370
- return;
371
- const keyword = new TextDecoder().decode(data.slice(0, nullIndex));
372
- const text = new TextDecoder().decode(data.slice(nullIndex + 1));
373
- // Map standard keywords to metadata fields
374
- switch (keyword.toLowerCase()) {
375
- case "title":
376
- metadata.title = text;
377
- break;
378
- case "author":
379
- metadata.author = text;
380
- break;
381
- case "description":
382
- metadata.description = text;
383
- break;
384
- case "copyright":
385
- metadata.copyright = text;
386
- break;
387
- default:
388
- // Store as custom metadata
389
- if (!metadata.custom)
390
- metadata.custom = {};
391
- metadata.custom[keyword] = text;
392
- }
393
- }
394
- parseITxtChunk(data, metadata) {
395
- // iTXt format: keyword\0compressed_flag\0compression_method\0language\0translated_keyword\0text
396
- let pos = 0;
397
- const nullIndex = data.indexOf(0, pos);
398
- if (nullIndex === -1 || pos >= data.length)
399
- return;
400
- const keyword = new TextDecoder().decode(data.slice(pos, nullIndex));
401
- pos = nullIndex + 1;
402
- if (pos + 2 > data.length)
403
- return; // Need at least 2 bytes for flags
404
- const _compressionFlag = data[pos++];
405
- const _compressionMethod = data[pos++];
406
- const languageNullIndex = data.indexOf(0, pos);
407
- if (languageNullIndex === -1 || pos >= data.length)
408
- return;
409
- pos = languageNullIndex + 1;
410
- const translatedKeywordNullIndex = data.indexOf(0, pos);
411
- if (translatedKeywordNullIndex === -1 || pos >= data.length)
412
- return;
413
- pos = translatedKeywordNullIndex + 1;
414
- if (pos >= data.length)
415
- return; // No text data
416
- const text = new TextDecoder("utf-8").decode(data.slice(pos));
417
- // Map to metadata fields (same as tEXt)
418
- switch (keyword.toLowerCase()) {
419
- case "title":
420
- metadata.title = text;
421
- break;
422
- case "author":
423
- metadata.author = text;
424
- break;
425
- case "description":
426
- metadata.description = text;
427
- break;
428
- case "copyright":
429
- metadata.copyright = text;
430
- break;
431
- default:
432
- if (!metadata.custom)
433
- metadata.custom = {};
434
- metadata.custom[keyword] = text;
435
- }
436
- }
437
- parseExifChunk(data, metadata) {
438
- // Basic EXIF parsing for GPS and date
439
- // EXIF data starts with byte order marker
440
- if (data.length < 8)
441
- return;
442
- try {
443
- const byteOrder = String.fromCharCode(data[0], data[1]);
444
- const littleEndian = byteOrder === "II";
445
- // Skip to IFD0 offset
446
- const ifd0Offset = littleEndian
447
- ? data[4] | (data[5] << 8) | (data[6] << 16) | (data[7] << 24)
448
- : (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7];
449
- if (ifd0Offset + 2 > data.length)
450
- return;
451
- // Read number of IFD entries with bounds check
452
- const numEntries = littleEndian
453
- ? data[ifd0Offset] | (data[ifd0Offset + 1] << 8)
454
- : (data[ifd0Offset] << 8) | data[ifd0Offset + 1];
455
- // Parse IFD entries looking for GPS and DateTime tags
456
- for (let i = 0; i < numEntries; i++) {
457
- const entryOffset = ifd0Offset + 2 + i * 12;
458
- if (entryOffset + 12 > data.length)
459
- break;
460
- const tag = littleEndian
461
- ? data[entryOffset] | (data[entryOffset + 1] << 8)
462
- : (data[entryOffset] << 8) | data[entryOffset + 1];
463
- // DateTime tag (0x0132)
464
- if (tag === 0x0132) {
465
- const valueOffset = littleEndian
466
- ? data[entryOffset + 8] | (data[entryOffset + 9] << 8) |
467
- (data[entryOffset + 10] << 16) | (data[entryOffset + 11] << 24)
468
- : (data[entryOffset + 8] << 24) | (data[entryOffset + 9] << 16) |
469
- (data[entryOffset + 10] << 8) | data[entryOffset + 11];
470
- if (valueOffset < data.length) {
471
- const nullIndex = data.indexOf(0, valueOffset);
472
- if (nullIndex > valueOffset) {
473
- const dateStr = new TextDecoder().decode(data.slice(valueOffset, nullIndex));
474
- // Parse EXIF datetime format: "YYYY:MM:DD HH:MM:SS"
475
- const match = dateStr.match(/^(\d{4}):(\d{2}):(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
476
- if (match) {
477
- metadata.creationDate = new Date(parseInt(match[1]), parseInt(match[2]) - 1, parseInt(match[3]), parseInt(match[4]), parseInt(match[5]), parseInt(match[6]));
478
- }
479
- }
480
- }
481
- }
482
- }
483
- }
484
- catch (_e) {
485
- // Ignore EXIF parsing errors
486
- }
487
- }
488
- createPhysChunk(metadata) {
489
- const chunk = new Uint8Array(9);
490
- // Default to 72 DPI if not specified
491
- const dpiX = metadata.dpiX ?? 72;
492
- const dpiY = metadata.dpiY ?? 72;
493
- // Convert DPI to pixels per meter
494
- const pixelsPerMeterX = Math.round(dpiX * INCHES_PER_METER);
495
- const pixelsPerMeterY = Math.round(dpiY * INCHES_PER_METER);
496
- this.writeUint32(chunk, 0, pixelsPerMeterX);
497
- this.writeUint32(chunk, 4, pixelsPerMeterY);
498
- chunk[8] = 1; // Unit is meters
499
- return chunk;
500
- }
501
- createTextChunk(keyword, text) {
502
- const keywordBytes = new TextEncoder().encode(keyword);
503
- const textBytes = new TextEncoder().encode(text);
504
- const chunk = new Uint8Array(keywordBytes.length + 1 + textBytes.length);
505
- chunk.set(keywordBytes, 0);
506
- chunk[keywordBytes.length] = 0; // Null separator
507
- chunk.set(textBytes, keywordBytes.length + 1);
508
- return chunk;
509
- }
510
- createExifChunk(metadata) {
511
- // Create a minimal EXIF structure
512
- const entries = [];
513
- // Add DateTime if available
514
- if (metadata.creationDate) {
515
- const date = metadata.creationDate;
516
- const dateStr = `${date.getFullYear()}:${String(date.getMonth() + 1).padStart(2, "0")}:${String(date.getDate()).padStart(2, "0")} ${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}:${String(date.getSeconds()).padStart(2, "0")}\0`;
517
- entries.push({
518
- tag: 0x0132,
519
- type: 2, // ASCII
520
- value: new TextEncoder().encode(dateStr),
521
- });
522
- }
523
- if (entries.length === 0)
524
- return null;
525
- // Build minimal EXIF structure
526
- const exif = [];
527
- // Byte order marker (little endian)
528
- exif.push(0x49, 0x49); // "II"
529
- exif.push(0x2a, 0x00); // 42
530
- // Offset to IFD0 (8 bytes from start)
531
- exif.push(0x08, 0x00, 0x00, 0x00);
532
- // Number of IFD entries
533
- exif.push(entries.length & 0xff, (entries.length >> 8) & 0xff);
534
- // Calculate data offset (after all entries)
535
- let dataOffset = 8 + 2 + entries.length * 12 + 4;
536
- for (const entry of entries) {
537
- // Tag
538
- exif.push(entry.tag & 0xff, (entry.tag >> 8) & 0xff);
539
- // Type
540
- exif.push(entry.type & 0xff, (entry.type >> 8) & 0xff);
541
- // Count
542
- const count = entry.value.length;
543
- exif.push(count & 0xff, (count >> 8) & 0xff, (count >> 16) & 0xff, (count >> 24) & 0xff);
544
- // Value/Offset
545
- if (entry.value.length <= 4) {
546
- for (let i = 0; i < 4; i++) {
547
- exif.push(i < entry.value.length ? entry.value[i] : 0);
548
- }
549
- }
550
- else {
551
- exif.push(dataOffset & 0xff, (dataOffset >> 8) & 0xff, (dataOffset >> 16) & 0xff, (dataOffset >> 24) & 0xff);
552
- dataOffset += entry.value.length;
553
- }
554
- }
555
- // Next IFD offset (0 = no more IFDs)
556
- exif.push(0x00, 0x00, 0x00, 0x00);
557
- // Append data for entries that didn't fit in value field
558
- for (const entry of entries) {
559
- if (entry.value.length > 4) {
560
- for (const byte of entry.value) {
561
- exif.push(byte);
562
- }
563
- }
564
- }
565
- return new Uint8Array(exif);
141
+ return this.concatenateArrays(chunks);
566
142
  }
567
143
  }
568
144
  exports.PNGFormat = PNGFormat;
@@ -0,0 +1,108 @@
1
+ import type { ImageMetadata } from "../types.js";
2
+ /**
3
+ * Base class for PNG and APNG format handlers
4
+ * Contains shared utility methods for PNG chunk manipulation and metadata parsing
5
+ */
6
+ export declare abstract class PNGBase {
7
+ /**
8
+ * Read a 32-bit unsigned integer (big-endian)
9
+ */
10
+ protected readUint32(data: Uint8Array, offset: number): number;
11
+ /**
12
+ * Read a 16-bit unsigned integer (big-endian)
13
+ */
14
+ protected readUint16(data: Uint8Array, offset: number): number;
15
+ /**
16
+ * Write a 32-bit unsigned integer (big-endian)
17
+ */
18
+ protected writeUint32(data: Uint8Array, offset: number, value: number): void;
19
+ /**
20
+ * Write a 16-bit unsigned integer (big-endian)
21
+ */
22
+ protected writeUint16(data: Uint8Array, offset: number, value: number): void;
23
+ /**
24
+ * Decompress PNG data using deflate
25
+ */
26
+ protected inflate(data: Uint8Array): Promise<Uint8Array>;
27
+ /**
28
+ * Compress PNG data using deflate
29
+ */
30
+ protected deflate(data: Uint8Array): Promise<Uint8Array>;
31
+ /**
32
+ * Unfilter PNG scanlines and convert to RGBA
33
+ */
34
+ protected unfilterAndConvert(data: Uint8Array, width: number, height: number, bitDepth: number, colorType: number): Uint8Array;
35
+ /**
36
+ * Unfilter a single PNG scanline
37
+ */
38
+ protected unfilterScanline(scanline: Uint8Array, prevLine: Uint8Array | null, filterType: number, bytesPerPixel: number): void;
39
+ /**
40
+ * Paeth predictor for PNG filtering
41
+ */
42
+ protected paethPredictor(a: number, b: number, c: number): number;
43
+ /**
44
+ * Filter PNG data for encoding (using filter type 0 - None)
45
+ */
46
+ protected filterData(data: Uint8Array, width: number, height: number): Uint8Array;
47
+ /**
48
+ * Get bytes per pixel for a given color type and bit depth
49
+ */
50
+ protected getBytesPerPixel(colorType: number, bitDepth: number): number;
51
+ /**
52
+ * Get bits per pixel for a given color type and bit depth
53
+ */
54
+ protected getBitsPerPixel(colorType: number, bitDepth: number): number;
55
+ /**
56
+ * Create a PNG chunk with length, type, data, and CRC
57
+ */
58
+ protected createChunk(type: string, data: Uint8Array): Uint8Array;
59
+ /**
60
+ * Calculate CRC32 checksum
61
+ */
62
+ protected crc32(data: Uint8Array): number;
63
+ /**
64
+ * Parse pHYs (physical pixel dimensions) chunk
65
+ */
66
+ protected parsePhysChunk(data: Uint8Array, metadata: ImageMetadata, width: number, height: number): void;
67
+ /**
68
+ * Parse tEXt (text) chunk
69
+ */
70
+ protected parseTextChunk(data: Uint8Array, metadata: ImageMetadata): void;
71
+ /**
72
+ * Parse iTXt (international text) chunk
73
+ */
74
+ protected parseITxtChunk(data: Uint8Array, metadata: ImageMetadata): void;
75
+ /**
76
+ * Parse eXIf (EXIF) chunk
77
+ */
78
+ protected parseExifChunk(data: Uint8Array, metadata: ImageMetadata): void;
79
+ /**
80
+ * Create pHYs (physical pixel dimensions) chunk
81
+ */
82
+ protected createPhysChunk(metadata: ImageMetadata): Uint8Array;
83
+ /**
84
+ * Create tEXt (text) chunk
85
+ */
86
+ protected createTextChunk(keyword: string, text: string): Uint8Array;
87
+ /**
88
+ * Create eXIf (EXIF) chunk
89
+ */
90
+ protected createExifChunk(metadata: ImageMetadata): Uint8Array | null;
91
+ /**
92
+ * Concatenate multiple byte arrays into a single Uint8Array
93
+ */
94
+ protected concatenateChunks(chunks: {
95
+ type: string;
96
+ data: Uint8Array;
97
+ }[]): Uint8Array;
98
+ /**
99
+ * Concatenate multiple Uint8Arrays into a single Uint8Array
100
+ */
101
+ protected concatenateArrays(arrays: Uint8Array[]): Uint8Array;
102
+ /**
103
+ * Add metadata chunks to the chunks array
104
+ * Shared method to avoid duplication between PNG and APNG encoding
105
+ */
106
+ protected addMetadataChunks(chunks: Uint8Array[], metadata: ImageMetadata | undefined): void;
107
+ }
108
+ //# sourceMappingURL=png_base.d.ts.map