cross-image 0.2.3 → 0.4.0

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 (105) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +292 -74
  3. package/esm/mod.d.ts +6 -4
  4. package/esm/mod.js +4 -2
  5. package/esm/src/formats/apng.d.ts +17 -5
  6. package/esm/src/formats/apng.js +104 -9
  7. package/esm/src/formats/ascii.d.ts +13 -3
  8. package/esm/src/formats/ascii.js +25 -1
  9. package/esm/src/formats/avif.d.ts +96 -0
  10. package/esm/src/formats/avif.js +607 -0
  11. package/esm/src/formats/bmp.d.ts +13 -3
  12. package/esm/src/formats/bmp.js +75 -2
  13. package/esm/src/formats/dng.d.ts +14 -2
  14. package/esm/src/formats/dng.js +27 -5
  15. package/esm/src/formats/gif.d.ts +18 -5
  16. package/esm/src/formats/gif.js +160 -14
  17. package/esm/src/formats/heic.d.ts +96 -0
  18. package/esm/src/formats/heic.js +608 -0
  19. package/esm/src/formats/ico.d.ts +13 -3
  20. package/esm/src/formats/ico.js +32 -4
  21. package/esm/src/formats/jpeg.d.ts +10 -3
  22. package/esm/src/formats/jpeg.js +99 -11
  23. package/esm/src/formats/pam.d.ts +13 -3
  24. package/esm/src/formats/pam.js +68 -2
  25. package/esm/src/formats/pcx.d.ts +13 -3
  26. package/esm/src/formats/pcx.js +47 -2
  27. package/esm/src/formats/png.d.ts +15 -3
  28. package/esm/src/formats/png.js +89 -2
  29. package/esm/src/formats/png_base.js +2 -5
  30. package/esm/src/formats/ppm.d.ts +13 -3
  31. package/esm/src/formats/ppm.js +36 -2
  32. package/esm/src/formats/tiff.d.ts +14 -18
  33. package/esm/src/formats/tiff.js +219 -20
  34. package/esm/src/formats/webp.d.ts +10 -3
  35. package/esm/src/formats/webp.js +103 -8
  36. package/esm/src/image.d.ts +20 -3
  37. package/esm/src/image.js +65 -21
  38. package/esm/src/types.d.ts +74 -4
  39. package/esm/src/utils/gif_decoder.d.ts +4 -1
  40. package/esm/src/utils/gif_decoder.js +91 -65
  41. package/esm/src/utils/image_processing.js +144 -70
  42. package/esm/src/utils/jpeg_decoder.d.ts +17 -4
  43. package/esm/src/utils/jpeg_decoder.js +448 -83
  44. package/esm/src/utils/jpeg_encoder.d.ts +15 -1
  45. package/esm/src/utils/jpeg_encoder.js +263 -24
  46. package/esm/src/utils/resize.js +51 -20
  47. package/esm/src/utils/tiff_deflate.d.ts +18 -0
  48. package/esm/src/utils/tiff_deflate.js +27 -0
  49. package/esm/src/utils/tiff_packbits.d.ts +24 -0
  50. package/esm/src/utils/tiff_packbits.js +90 -0
  51. package/esm/src/utils/webp_decoder.d.ts +3 -1
  52. package/esm/src/utils/webp_decoder.js +144 -63
  53. package/esm/src/utils/webp_encoder.js +5 -11
  54. package/package.json +18 -1
  55. package/script/mod.d.ts +6 -4
  56. package/script/mod.js +7 -3
  57. package/script/src/formats/apng.d.ts +17 -5
  58. package/script/src/formats/apng.js +104 -9
  59. package/script/src/formats/ascii.d.ts +13 -3
  60. package/script/src/formats/ascii.js +25 -1
  61. package/script/src/formats/avif.d.ts +96 -0
  62. package/script/src/formats/avif.js +611 -0
  63. package/script/src/formats/bmp.d.ts +13 -3
  64. package/script/src/formats/bmp.js +75 -2
  65. package/script/src/formats/dng.d.ts +14 -2
  66. package/script/src/formats/dng.js +27 -5
  67. package/script/src/formats/gif.d.ts +18 -5
  68. package/script/src/formats/gif.js +160 -14
  69. package/script/src/formats/heic.d.ts +96 -0
  70. package/script/src/formats/heic.js +612 -0
  71. package/script/src/formats/ico.d.ts +13 -3
  72. package/script/src/formats/ico.js +32 -4
  73. package/script/src/formats/jpeg.d.ts +10 -3
  74. package/script/src/formats/jpeg.js +99 -11
  75. package/script/src/formats/pam.d.ts +13 -3
  76. package/script/src/formats/pam.js +68 -2
  77. package/script/src/formats/pcx.d.ts +13 -3
  78. package/script/src/formats/pcx.js +47 -2
  79. package/script/src/formats/png.d.ts +15 -3
  80. package/script/src/formats/png.js +89 -2
  81. package/script/src/formats/png_base.js +2 -5
  82. package/script/src/formats/ppm.d.ts +13 -3
  83. package/script/src/formats/ppm.js +36 -2
  84. package/script/src/formats/tiff.d.ts +14 -18
  85. package/script/src/formats/tiff.js +219 -20
  86. package/script/src/formats/webp.d.ts +10 -3
  87. package/script/src/formats/webp.js +103 -8
  88. package/script/src/image.d.ts +20 -3
  89. package/script/src/image.js +64 -20
  90. package/script/src/types.d.ts +74 -4
  91. package/script/src/utils/gif_decoder.d.ts +4 -1
  92. package/script/src/utils/gif_decoder.js +91 -65
  93. package/script/src/utils/image_processing.js +144 -70
  94. package/script/src/utils/jpeg_decoder.d.ts +17 -4
  95. package/script/src/utils/jpeg_decoder.js +448 -83
  96. package/script/src/utils/jpeg_encoder.d.ts +15 -1
  97. package/script/src/utils/jpeg_encoder.js +263 -24
  98. package/script/src/utils/resize.js +51 -20
  99. package/script/src/utils/tiff_deflate.d.ts +18 -0
  100. package/script/src/utils/tiff_deflate.js +31 -0
  101. package/script/src/utils/tiff_packbits.d.ts +24 -0
  102. package/script/src/utils/tiff_packbits.js +94 -0
  103. package/script/src/utils/webp_decoder.d.ts +3 -1
  104. package/script/src/utils/webp_decoder.js +144 -63
  105. package/script/src/utils/webp_encoder.js +5 -11
@@ -1,4 +1,4 @@
1
- import type { ImageData, ImageFormat } from "../types.js";
1
+ import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata } from "../types.js";
2
2
  /**
3
3
  * PPM format handler
4
4
  * Implements the Netpbm PPM (Portable PixMap) format.
@@ -34,17 +34,27 @@ export declare class PPMFormat implements ImageFormat {
34
34
  * @param data Raw PPM image data
35
35
  * @returns Decoded image data with RGBA pixels
36
36
  */
37
- decode(data: Uint8Array): Promise<ImageData>;
37
+ decode(data: Uint8Array, _options?: ImageDecoderOptions): Promise<ImageData>;
38
38
  /**
39
39
  * Encode RGBA image data to PPM format (P6 binary)
40
40
  * Note: Alpha channel is ignored as PPM doesn't support transparency
41
41
  * @param imageData Image data to encode
42
42
  * @returns Encoded PPM image bytes
43
43
  */
44
- encode(imageData: ImageData): Promise<Uint8Array>;
44
+ encode(imageData: ImageData, _options?: unknown): Promise<Uint8Array>;
45
45
  /**
46
46
  * Check if a byte is whitespace (space, tab, CR, LF)
47
47
  */
48
48
  private isWhitespace;
49
+ /**
50
+ * Get the list of metadata fields supported by PPM format
51
+ */
52
+ getSupportedMetadata(): Array<keyof ImageMetadata>;
53
+ /**
54
+ * Extract metadata from PPM data without fully decoding the pixel data
55
+ * @param data Raw PPM data
56
+ * @returns Extracted metadata or undefined
57
+ */
58
+ extractMetadata(data: Uint8Array): Promise<ImageMetadata | undefined>;
49
59
  }
50
60
  //# sourceMappingURL=ppm.d.ts.map
@@ -59,7 +59,7 @@ class PPMFormat {
59
59
  * @param data Raw PPM image data
60
60
  * @returns Decoded image data with RGBA pixels
61
61
  */
62
- decode(data) {
62
+ decode(data, _options) {
63
63
  if (!this.canDecode(data)) {
64
64
  throw new Error("Invalid PPM signature");
65
65
  }
@@ -211,7 +211,7 @@ class PPMFormat {
211
211
  * @param imageData Image data to encode
212
212
  * @returns Encoded PPM image bytes
213
213
  */
214
- encode(imageData) {
214
+ encode(imageData, _options) {
215
215
  const { width, height, data } = imageData;
216
216
  // Validate input
217
217
  if (data.length !== width * height * 4) {
@@ -242,5 +242,39 @@ class PPMFormat {
242
242
  isWhitespace(byte) {
243
243
  return byte === 0x20 || byte === 0x09 || byte === 0x0a || byte === 0x0d;
244
244
  }
245
+ /**
246
+ * Get the list of metadata fields supported by PPM format
247
+ */
248
+ getSupportedMetadata() {
249
+ return []; // PPM format doesn't support metadata preservation
250
+ }
251
+ /**
252
+ * Extract metadata from PPM data without fully decoding the pixel data
253
+ * @param data Raw PPM data
254
+ * @returns Extracted metadata or undefined
255
+ */
256
+ extractMetadata(data) {
257
+ if (!this.canDecode(data)) {
258
+ return Promise.resolve(undefined);
259
+ }
260
+ const metadata = {
261
+ format: "ppm",
262
+ compression: "none",
263
+ frameCount: 1,
264
+ bitDepth: 8,
265
+ colorType: "rgb",
266
+ };
267
+ // PPM is always RGB, uncompressed, and typically 8-bit
268
+ // P3 is ASCII, P6 is binary
269
+ if (data[1] === 0x33) {
270
+ // '3'
271
+ metadata.compression = "none"; // ASCII encoding
272
+ }
273
+ else if (data[1] === 0x36) {
274
+ // '6'
275
+ metadata.compression = "none"; // Binary encoding
276
+ }
277
+ return Promise.resolve(metadata);
278
+ }
245
279
  }
246
280
  exports.PPMFormat = PPMFormat;
@@ -1,20 +1,9 @@
1
- import type { ImageData, ImageFormat, ImageMetadata, MultiFrameImageData } from "../types.js";
2
- /**
3
- * Options for TIFF encoding
4
- */
5
- export interface TIFFEncodeOptions {
6
- /** Compression method: "none" for uncompressed (default), "lzw" for LZW compression */
7
- compression?: "none" | "lzw";
8
- /** Encode as grayscale instead of RGB/RGBA */
9
- grayscale?: boolean;
10
- /** Encode as RGB without alpha channel (ignored if grayscale is true) */
11
- rgb?: boolean;
12
- }
1
+ import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata, MultiFrameImageData } from "../types.js";
13
2
  /**
14
3
  * TIFF format handler
15
- * Implements pure-JS TIFF decoder for uncompressed and LZW-compressed RGB/RGBA images
16
- * and encoder for uncompressed and LZW-compressed RGBA TIFFs. Falls back to ImageDecoder
17
- * for other compressed TIFFs (JPEG, PackBits, etc.)
4
+ * Implements pure-JS TIFF decoder for uncompressed, LZW, PackBits, and Deflate-compressed RGB/RGBA images
5
+ * and encoder for uncompressed, LZW, PackBits, and Deflate-compressed RGBA TIFFs. Falls back to ImageDecoder
6
+ * for JPEG-compressed TIFFs.
18
7
  * Supports multi-page TIFF files.
19
8
  */
20
9
  export declare class TIFFFormat implements ImageFormat {
@@ -38,12 +27,12 @@ export declare class TIFFFormat implements ImageFormat {
38
27
  * @param data Raw TIFF image data
39
28
  * @returns Decoded image data with RGBA pixels of first page
40
29
  */
41
- decode(data: Uint8Array): Promise<ImageData>;
30
+ decode(data: Uint8Array, _options?: ImageDecoderOptions): Promise<ImageData>;
42
31
  encode(imageData: ImageData, options?: unknown): Promise<Uint8Array>;
43
32
  /**
44
33
  * Decode all pages from a multi-page TIFF
45
34
  */
46
- decodeFrames(data: Uint8Array): Promise<MultiFrameImageData>;
35
+ decodeFrames(data: Uint8Array, _options?: ImageDecoderOptions): Promise<MultiFrameImageData>;
47
36
  /**
48
37
  * Encode multi-page TIFF
49
38
  */
@@ -65,7 +54,7 @@ export declare class TIFFFormat implements ImageFormat {
65
54
  private decodeUsingRuntime;
66
55
  private readString;
67
56
  /**
68
- * Pure JavaScript TIFF decoder for uncompressed and LZW-compressed RGB/RGBA images
57
+ * Pure JavaScript TIFF decoder for uncompressed, LZW, PackBits, and Deflate-compressed RGB/RGBA images
69
58
  * Returns null if the TIFF uses unsupported features
70
59
  */
71
60
  private decodePureJS;
@@ -79,5 +68,12 @@ export declare class TIFFFormat implements ImageFormat {
79
68
  * TIFF supports extensive EXIF metadata including GPS and InteropIFD
80
69
  */
81
70
  getSupportedMetadata(): Array<keyof ImageMetadata>;
71
+ /**
72
+ * Extract metadata from TIFF data without fully decoding the pixel data
73
+ * This quickly parses IFD entries to extract metadata
74
+ * @param data Raw TIFF data
75
+ * @returns Extracted metadata or undefined
76
+ */
77
+ extractMetadata(data: Uint8Array): Promise<ImageMetadata | undefined>;
82
78
  }
83
79
  //# sourceMappingURL=tiff.d.ts.map
@@ -2,14 +2,16 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TIFFFormat = void 0;
4
4
  const tiff_lzw_js_1 = require("../utils/tiff_lzw.js");
5
+ const tiff_packbits_js_1 = require("../utils/tiff_packbits.js");
6
+ const tiff_deflate_js_1 = require("../utils/tiff_deflate.js");
5
7
  const security_js_1 = require("../utils/security.js");
6
8
  // Constants for unit conversions
7
9
  const DEFAULT_DPI = 72;
8
10
  /**
9
11
  * TIFF format handler
10
- * Implements pure-JS TIFF decoder for uncompressed and LZW-compressed RGB/RGBA images
11
- * and encoder for uncompressed and LZW-compressed RGBA TIFFs. Falls back to ImageDecoder
12
- * for other compressed TIFFs (JPEG, PackBits, etc.)
12
+ * Implements pure-JS TIFF decoder for uncompressed, LZW, PackBits, and Deflate-compressed RGB/RGBA images
13
+ * and encoder for uncompressed, LZW, PackBits, and Deflate-compressed RGBA TIFFs. Falls back to ImageDecoder
14
+ * for JPEG-compressed TIFFs.
13
15
  * Supports multi-page TIFF files.
14
16
  */
15
17
  class TIFFFormat {
@@ -55,7 +57,7 @@ class TIFFFormat {
55
57
  * @param data Raw TIFF image data
56
58
  * @returns Decoded image data with RGBA pixels of first page
57
59
  */
58
- async decode(data) {
60
+ async decode(data, _options) {
59
61
  if (!this.canDecode(data)) {
60
62
  throw new Error("Invalid TIFF signature");
61
63
  }
@@ -131,7 +133,7 @@ class TIFFFormat {
131
133
  metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
132
134
  };
133
135
  }
134
- encode(imageData, options) {
136
+ async encode(imageData, options) {
135
137
  const { width, height, data, metadata } = imageData;
136
138
  const opts = options;
137
139
  const compression = opts?.compression ?? "none";
@@ -175,6 +177,16 @@ class TIFFFormat {
175
177
  pixelData = encoder.compress(sourceData);
176
178
  compressionCode = 5;
177
179
  }
180
+ else if (compression === "packbits") {
181
+ // PackBits compress the pixel data
182
+ pixelData = (0, tiff_packbits_js_1.packBitsCompress)(sourceData);
183
+ compressionCode = 32773;
184
+ }
185
+ else if (compression === "deflate") {
186
+ // Deflate compress the pixel data
187
+ pixelData = await (0, tiff_deflate_js_1.deflateCompress)(sourceData);
188
+ compressionCode = 8;
189
+ }
178
190
  else {
179
191
  // Uncompressed
180
192
  pixelData = sourceData;
@@ -332,7 +344,7 @@ class TIFFFormat {
332
344
  /**
333
345
  * Decode all pages from a multi-page TIFF
334
346
  */
335
- async decodeFrames(data) {
347
+ async decodeFrames(data, _options) {
336
348
  if (!this.canDecode(data)) {
337
349
  throw new Error("Invalid TIFF signature");
338
350
  }
@@ -395,7 +407,7 @@ class TIFFFormat {
395
407
  /**
396
408
  * Encode multi-page TIFF
397
409
  */
398
- encodeFrames(imageData, options) {
410
+ async encodeFrames(imageData, options) {
399
411
  const opts = options;
400
412
  const compression = opts?.compression ?? "none";
401
413
  if (imageData.frames.length === 0) {
@@ -420,6 +432,12 @@ class TIFFFormat {
420
432
  const encoder = new tiff_lzw_js_1.TIFFLZWEncoder();
421
433
  pixelData = encoder.compress(frame.data);
422
434
  }
435
+ else if (compression === "packbits") {
436
+ pixelData = (0, tiff_packbits_js_1.packBitsCompress)(frame.data);
437
+ }
438
+ else if (compression === "deflate") {
439
+ pixelData = await (0, tiff_deflate_js_1.deflateCompress)(frame.data);
440
+ }
423
441
  else {
424
442
  pixelData = frame.data;
425
443
  }
@@ -456,7 +474,19 @@ class TIFFFormat {
456
474
  this.writeIFDEntry(result, 0x0102, 3, 4, dataOffset);
457
475
  dataOffset += 8;
458
476
  // Compression
459
- const compressionCode = compression === "lzw" ? 5 : 1;
477
+ let compressionCode;
478
+ if (compression === "lzw") {
479
+ compressionCode = 5;
480
+ }
481
+ else if (compression === "packbits") {
482
+ compressionCode = 32773;
483
+ }
484
+ else if (compression === "deflate") {
485
+ compressionCode = 8;
486
+ }
487
+ else {
488
+ compressionCode = 1;
489
+ }
460
490
  this.writeIFDEntry(result, 0x0103, 3, 1, compressionCode);
461
491
  // PhotometricInterpretation
462
492
  this.writeIFDEntry(result, 0x0106, 3, 1, 2);
@@ -467,9 +497,19 @@ class TIFFFormat {
467
497
  // RowsPerStrip
468
498
  this.writeIFDEntry(result, 0x0116, 4, 1, frame.height);
469
499
  // StripByteCounts
470
- const pixelDataSize = compression === "lzw"
471
- ? new tiff_lzw_js_1.TIFFLZWEncoder().compress(frame.data).length
472
- : frame.data.length;
500
+ let pixelDataSize;
501
+ if (compression === "lzw") {
502
+ pixelDataSize = new tiff_lzw_js_1.TIFFLZWEncoder().compress(frame.data).length;
503
+ }
504
+ else if (compression === "packbits") {
505
+ pixelDataSize = (0, tiff_packbits_js_1.packBitsCompress)(frame.data).length;
506
+ }
507
+ else if (compression === "deflate") {
508
+ pixelDataSize = (await (0, tiff_deflate_js_1.deflateCompress)(frame.data)).length;
509
+ }
510
+ else {
511
+ pixelDataSize = frame.data.length;
512
+ }
473
513
  this.writeIFDEntry(result, 0x0117, 4, 1, pixelDataSize);
474
514
  // XResolution
475
515
  const xResOffset = dataOffset;
@@ -572,7 +612,7 @@ class TIFFFormat {
572
612
  const isLittleEndian = data[0] === 0x49;
573
613
  // Try pure JavaScript decoder first
574
614
  try {
575
- const pureJSResult = this.decodePureJSFromIFD(data, ifdOffset, width, height, isLittleEndian);
615
+ const pureJSResult = await this.decodePureJSFromIFD(data, ifdOffset, width, height, isLittleEndian);
576
616
  if (pureJSResult) {
577
617
  return pureJSResult;
578
618
  }
@@ -696,7 +736,7 @@ class TIFFFormat {
696
736
  async decodeUsingRuntime(data, width, height) {
697
737
  // Try pure JavaScript decoder first for uncompressed TIFFs
698
738
  try {
699
- const pureJSResult = this.decodePureJS(data, width, height);
739
+ const pureJSResult = await this.decodePureJS(data, width, height);
700
740
  if (pureJSResult) {
701
741
  return pureJSResult;
702
742
  }
@@ -734,10 +774,10 @@ class TIFFFormat {
734
774
  return new TextDecoder().decode(data.slice(offset, endIndex));
735
775
  }
736
776
  /**
737
- * Pure JavaScript TIFF decoder for uncompressed and LZW-compressed RGB/RGBA images
777
+ * Pure JavaScript TIFF decoder for uncompressed, LZW, PackBits, and Deflate-compressed RGB/RGBA images
738
778
  * Returns null if the TIFF uses unsupported features
739
779
  */
740
- decodePureJS(data, width, height) {
780
+ async decodePureJS(data, width, height) {
741
781
  // Validate minimum TIFF header size
742
782
  if (data.length < 8) {
743
783
  return null;
@@ -748,8 +788,9 @@ class TIFFFormat {
748
788
  const ifdOffset = this.readUint32(data, 4, isLittleEndian);
749
789
  // Check compression
750
790
  const compression = this.getIFDValue(data, ifdOffset, 0x0103, isLittleEndian);
751
- if (compression !== 1 && compression !== 5) {
752
- // Only support uncompressed (1) and LZW (5)
791
+ if (compression !== 1 && compression !== 5 && compression !== 8 &&
792
+ compression !== 32773) {
793
+ // Support: 1 = uncompressed, 5 = LZW, 8 = Deflate, 32773 = PackBits
753
794
  return null;
754
795
  }
755
796
  // Check photometric interpretation
@@ -791,6 +832,16 @@ class TIFFFormat {
791
832
  const decoder = new tiff_lzw_js_1.TIFFLZWDecoder(compressedData);
792
833
  pixelData = decoder.decompress();
793
834
  }
835
+ else if (compression === 32773) {
836
+ // PackBits compressed
837
+ const compressedData = data.slice(stripOffset, stripOffset + stripByteCount);
838
+ pixelData = (0, tiff_packbits_js_1.packBitsDecompress)(compressedData);
839
+ }
840
+ else if (compression === 8) {
841
+ // Deflate compressed
842
+ const compressedData = data.slice(stripOffset, stripOffset + stripByteCount);
843
+ pixelData = await (0, tiff_deflate_js_1.deflateDecompress)(compressedData);
844
+ }
794
845
  else {
795
846
  // Uncompressed
796
847
  pixelData = data.slice(stripOffset, stripOffset + stripByteCount);
@@ -842,11 +893,12 @@ class TIFFFormat {
842
893
  * Pure JavaScript TIFF decoder for a specific IFD
843
894
  * Returns null if the TIFF uses unsupported features
844
895
  */
845
- decodePureJSFromIFD(data, ifdOffset, width, height, isLittleEndian) {
896
+ async decodePureJSFromIFD(data, ifdOffset, width, height, isLittleEndian) {
846
897
  // Check compression
847
898
  const compression = this.getIFDValue(data, ifdOffset, 0x0103, isLittleEndian);
848
- if (compression !== 1 && compression !== 5) {
849
- // Only support uncompressed (1) and LZW (5)
899
+ if (compression !== 1 && compression !== 5 && compression !== 8 &&
900
+ compression !== 32773) {
901
+ // Support: 1 = uncompressed, 5 = LZW, 8 = Deflate, 32773 = PackBits
850
902
  return null;
851
903
  }
852
904
  // Check photometric interpretation
@@ -888,6 +940,16 @@ class TIFFFormat {
888
940
  const decoder = new tiff_lzw_js_1.TIFFLZWDecoder(compressedData);
889
941
  pixelData = decoder.decompress();
890
942
  }
943
+ else if (compression === 32773) {
944
+ // PackBits compressed
945
+ const compressedData = data.slice(stripOffset, stripOffset + stripByteCount);
946
+ pixelData = (0, tiff_packbits_js_1.packBitsDecompress)(compressedData);
947
+ }
948
+ else if (compression === 8) {
949
+ // Deflate compressed
950
+ const compressedData = data.slice(stripOffset, stripOffset + stripByteCount);
951
+ pixelData = await (0, tiff_deflate_js_1.deflateDecompress)(compressedData);
952
+ }
891
953
  else {
892
954
  // Uncompressed
893
955
  pixelData = data.slice(stripOffset, stripOffset + stripByteCount);
@@ -966,5 +1028,142 @@ class TIFFFormat {
966
1028
  "physicalHeight",
967
1029
  ];
968
1030
  }
1031
+ /**
1032
+ * Extract metadata from TIFF data without fully decoding the pixel data
1033
+ * This quickly parses IFD entries to extract metadata
1034
+ * @param data Raw TIFF data
1035
+ * @returns Extracted metadata or undefined
1036
+ */
1037
+ extractMetadata(data) {
1038
+ if (!this.canDecode(data)) {
1039
+ return Promise.resolve(undefined);
1040
+ }
1041
+ // Determine byte order
1042
+ const isLittleEndian = data[0] === 0x49 && data[1] === 0x49;
1043
+ if (!isLittleEndian && !(data[0] === 0x4d && data[1] === 0x4d)) {
1044
+ return Promise.resolve(undefined);
1045
+ }
1046
+ // Read IFD offset
1047
+ const ifdOffset = this.readUint32(data, 4, isLittleEndian);
1048
+ if (ifdOffset >= data.length) {
1049
+ return Promise.resolve(undefined);
1050
+ }
1051
+ // Get dimensions for DPI calculation
1052
+ const width = this.getIFDValue(data, ifdOffset, 0x0100, isLittleEndian);
1053
+ const height = this.getIFDValue(data, ifdOffset, 0x0101, isLittleEndian);
1054
+ if (!width || !height) {
1055
+ return Promise.resolve(undefined);
1056
+ }
1057
+ // Extract metadata from TIFF tags
1058
+ const metadata = {
1059
+ format: "tiff",
1060
+ frameCount: 1,
1061
+ bitDepth: 8,
1062
+ };
1063
+ // Get compression type
1064
+ const compression = this.getIFDValue(data, ifdOffset, 0x0103, isLittleEndian);
1065
+ if (compression === 1) {
1066
+ metadata.compression = "none";
1067
+ }
1068
+ else if (compression === 5) {
1069
+ metadata.compression = "lzw";
1070
+ }
1071
+ else if (compression === 7 || compression === 6) {
1072
+ metadata.compression = "jpeg";
1073
+ }
1074
+ else if (compression === 8) {
1075
+ metadata.compression = "deflate";
1076
+ }
1077
+ else if (compression === 32773) {
1078
+ metadata.compression = "packbits";
1079
+ }
1080
+ else if (compression) {
1081
+ metadata.compression = `unknown-${compression}`;
1082
+ }
1083
+ // Get bits per sample
1084
+ const bitsPerSample = this.getIFDValue(data, ifdOffset, 0x0102, isLittleEndian);
1085
+ if (bitsPerSample) {
1086
+ metadata.bitDepth = bitsPerSample;
1087
+ }
1088
+ // Get photometric interpretation for color type
1089
+ const photometric = this.getIFDValue(data, ifdOffset, 0x0106, isLittleEndian);
1090
+ const samplesPerPixel = this.getIFDValue(data, ifdOffset, 0x0115, isLittleEndian);
1091
+ if (photometric === 0 || photometric === 1) {
1092
+ metadata.colorType = "grayscale";
1093
+ }
1094
+ else if (photometric === 2) {
1095
+ if (samplesPerPixel === 3) {
1096
+ metadata.colorType = "rgb";
1097
+ }
1098
+ else if (samplesPerPixel === 4) {
1099
+ metadata.colorType = "rgba";
1100
+ }
1101
+ }
1102
+ else if (photometric === 3) {
1103
+ metadata.colorType = "indexed";
1104
+ }
1105
+ // Count IFDs (pages/frames) by following the chain
1106
+ let currentIfdOffset = ifdOffset;
1107
+ let frameCount = 0;
1108
+ while (currentIfdOffset > 0 && currentIfdOffset < data.length &&
1109
+ frameCount < 1000) {
1110
+ frameCount++;
1111
+ // Read number of entries in this IFD
1112
+ const numEntries = this.readUint16(data, currentIfdOffset, isLittleEndian);
1113
+ // Next IFD offset is after all entries (2 + numEntries * 12 bytes)
1114
+ const nextIfdOffsetPos = currentIfdOffset + 2 + (numEntries * 12);
1115
+ if (nextIfdOffsetPos + 4 > data.length)
1116
+ break;
1117
+ currentIfdOffset = this.readUint32(data, nextIfdOffsetPos, isLittleEndian);
1118
+ }
1119
+ metadata.frameCount = frameCount;
1120
+ // XResolution (0x011a) and YResolution (0x011b) for DPI
1121
+ const xResOffset = this.getIFDValue(data, ifdOffset, 0x011a, isLittleEndian);
1122
+ const yResOffset = this.getIFDValue(data, ifdOffset, 0x011b, isLittleEndian);
1123
+ if (xResOffset && xResOffset < data.length - 8) {
1124
+ const numerator = this.readUint32(data, xResOffset, isLittleEndian);
1125
+ const denominator = this.readUint32(data, xResOffset + 4, isLittleEndian);
1126
+ if (denominator > 0) {
1127
+ metadata.dpiX = Math.round(numerator / denominator);
1128
+ }
1129
+ }
1130
+ if (yResOffset && yResOffset < data.length - 8) {
1131
+ const numerator = this.readUint32(data, yResOffset, isLittleEndian);
1132
+ const denominator = this.readUint32(data, yResOffset + 4, isLittleEndian);
1133
+ if (denominator > 0) {
1134
+ metadata.dpiY = Math.round(numerator / denominator);
1135
+ }
1136
+ }
1137
+ // Calculate physical dimensions if DPI is available
1138
+ if (metadata.dpiX && metadata.dpiY) {
1139
+ metadata.physicalWidth = width / metadata.dpiX;
1140
+ metadata.physicalHeight = height / metadata.dpiY;
1141
+ }
1142
+ // ImageDescription (0x010e)
1143
+ const descOffset = this.getIFDValue(data, ifdOffset, 0x010e, isLittleEndian);
1144
+ if (descOffset && descOffset < data.length) {
1145
+ metadata.description = this.readString(data, descOffset);
1146
+ }
1147
+ // Artist (0x013b)
1148
+ const artistOffset = this.getIFDValue(data, ifdOffset, 0x013b, isLittleEndian);
1149
+ if (artistOffset && artistOffset < data.length) {
1150
+ metadata.author = this.readString(data, artistOffset);
1151
+ }
1152
+ // Copyright (0x8298)
1153
+ const copyrightOffset = this.getIFDValue(data, ifdOffset, 0x8298, isLittleEndian);
1154
+ if (copyrightOffset && copyrightOffset < data.length) {
1155
+ metadata.copyright = this.readString(data, copyrightOffset);
1156
+ }
1157
+ // DateTime (0x0132)
1158
+ const dateTimeOffset = this.getIFDValue(data, ifdOffset, 0x0132, isLittleEndian);
1159
+ if (dateTimeOffset && dateTimeOffset < data.length) {
1160
+ const dateStr = this.readString(data, dateTimeOffset);
1161
+ const match = dateStr.match(/^(\d{4}):(\d{2}):(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
1162
+ if (match) {
1163
+ metadata.creationDate = new Date(parseInt(match[1]), parseInt(match[2]) - 1, parseInt(match[3]), parseInt(match[4]), parseInt(match[5]), parseInt(match[6]));
1164
+ }
1165
+ }
1166
+ return Promise.resolve(Object.keys(metadata).length > 0 ? metadata : undefined);
1167
+ }
969
1168
  }
970
1169
  exports.TIFFFormat = TIFFFormat;
@@ -1,4 +1,4 @@
1
- import type { ImageData, ImageFormat, ImageMetadata, WebPEncodeOptions } from "../types.js";
1
+ import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata, WebPEncoderOptions } from "../types.js";
2
2
  /**
3
3
  * WebP format handler
4
4
  * Implements a basic WebP decoder and encoder
@@ -19,14 +19,14 @@ export declare class WebPFormat implements ImageFormat {
19
19
  * @param data Raw WebP image data
20
20
  * @returns Decoded image data with RGBA pixels
21
21
  */
22
- decode(data: Uint8Array): Promise<ImageData>;
22
+ decode(data: Uint8Array, settings?: ImageDecoderOptions): Promise<ImageData>;
23
23
  /**
24
24
  * Encode RGBA image data to WebP format
25
25
  * @param imageData Image data to encode
26
26
  * @param options Optional WebP encoding options
27
27
  * @returns Encoded WebP image bytes
28
28
  */
29
- encode(imageData: ImageData, options?: WebPEncodeOptions): Promise<Uint8Array>;
29
+ encode(imageData: ImageData, options?: WebPEncoderOptions): Promise<Uint8Array>;
30
30
  private readUint24LE;
31
31
  private decodeUsingRuntime;
32
32
  private parseEXIF;
@@ -42,5 +42,12 @@ export declare class WebPFormat implements ImageFormat {
42
42
  * Get the list of metadata fields supported by WebP format
43
43
  */
44
44
  getSupportedMetadata(): Array<keyof ImageMetadata>;
45
+ /**
46
+ * Extract metadata from WebP data without fully decoding the pixel data
47
+ * This quickly parses RIFF chunks to extract EXIF and XMP metadata
48
+ * @param data Raw WebP data
49
+ * @returns Extracted metadata or undefined
50
+ */
51
+ extractMetadata(data: Uint8Array): Promise<ImageMetadata | undefined>;
45
52
  }
46
53
  //# sourceMappingURL=webp.d.ts.map