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