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
@@ -78,7 +78,7 @@ class WebPFormat {
78
78
  * @param data Raw WebP image data
79
79
  * @returns Decoded image data with RGBA pixels
80
80
  */
81
- async decode(data) {
81
+ async decode(data, settings) {
82
82
  if (!this.canDecode(data)) {
83
83
  throw new Error("Invalid WebP signature");
84
84
  }
@@ -144,7 +144,7 @@ class WebPFormat {
144
144
  (0, security_js_1.validateImageDimensions)(width, height);
145
145
  // For a pure JS implementation, we'd need to implement full WebP decoding
146
146
  // which is very complex. Instead, we'll use the browser/runtime's decoder.
147
- const rgba = await this.decodeUsingRuntime(data, width, height);
147
+ const rgba = await this.decodeUsingRuntime(data, width, height, settings);
148
148
  return {
149
149
  width,
150
150
  height,
@@ -207,9 +207,10 @@ class WebPFormat {
207
207
  readUint24LE(data, offset) {
208
208
  return data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16);
209
209
  }
210
- async decodeUsingRuntime(data, _width, _height) {
210
+ async decodeUsingRuntime(data, _width, _height, settings) {
211
211
  // Try to use ImageDecoder API if available (Deno, modern browsers)
212
- if (typeof ImageDecoder !== "undefined") {
212
+ if (settings?.runtimeDecoding !== "never" &&
213
+ typeof ImageDecoder !== "undefined") {
213
214
  try {
214
215
  const decoder = new ImageDecoder({ data, type: "image/webp" });
215
216
  const result = await decoder.decode();
@@ -224,15 +225,17 @@ class WebPFormat {
224
225
  bitmap.close();
225
226
  return new Uint8Array(imageData.data.buffer);
226
227
  }
227
- catch (error) {
228
+ catch (_error) {
228
229
  // ImageDecoder API failed, fall through to pure JS decoder
229
- console.warn("WebP decoding with ImageDecoder failed, using pure JS decoder:", error);
230
230
  }
231
231
  }
232
- // Fallback to pure JavaScript decoder (VP8L lossless only)
232
+ // Fallback to pure JavaScript decoder (VP8L lossless only) with tolerant mode
233
233
  try {
234
234
  const { WebPDecoder } = await Promise.resolve().then(() => __importStar(require("../utils/webp_decoder.js")));
235
- const decoder = new WebPDecoder(data);
235
+ const decoder = new WebPDecoder(data, {
236
+ tolerantDecoding: settings?.tolerantDecoding ?? true,
237
+ onWarning: settings?.onWarning,
238
+ });
236
239
  const result = decoder.decode();
237
240
  return result.data;
238
241
  }
@@ -603,5 +606,97 @@ class WebPFormat {
603
606
  "focalLength",
604
607
  ];
605
608
  }
609
+ /**
610
+ * Extract metadata from WebP data without fully decoding the pixel data
611
+ * This quickly parses RIFF chunks to extract EXIF and XMP metadata
612
+ * @param data Raw WebP data
613
+ * @returns Extracted metadata or undefined
614
+ */
615
+ extractMetadata(data) {
616
+ if (!this.canDecode(data)) {
617
+ return Promise.resolve(undefined);
618
+ }
619
+ const metadata = {
620
+ format: "webp",
621
+ frameCount: 1,
622
+ bitDepth: 8,
623
+ };
624
+ let pos = 12; // Skip "RIFF" + size + "WEBP"
625
+ const readUint32LE = (data, offset) => {
626
+ return data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) |
627
+ (data[offset + 3] << 24);
628
+ };
629
+ const readUint16LE = (data, offset) => {
630
+ return data[offset] | (data[offset + 1] << 8);
631
+ };
632
+ // Parse chunks for metadata
633
+ while (pos + 8 <= data.length) {
634
+ const chunkType = String.fromCharCode(data[pos], data[pos + 1], data[pos + 2], data[pos + 3]);
635
+ const chunkSize = readUint32LE(data, pos + 4);
636
+ pos += 8;
637
+ // Stop if we've gone past the end
638
+ if (pos + chunkSize > data.length)
639
+ break;
640
+ const chunkData = data.slice(pos, pos + chunkSize);
641
+ if (chunkType === "VP8 ") {
642
+ // Lossy VP8 chunk
643
+ metadata.compression = "vp8";
644
+ metadata.colorType = "rgb";
645
+ }
646
+ else if (chunkType === "VP8L") {
647
+ // Lossless VP8L chunk
648
+ metadata.compression = "vp8l";
649
+ metadata.colorType = "rgba";
650
+ }
651
+ else if (chunkType === "VP8X") {
652
+ // Extended format chunk - contains animation info
653
+ if (chunkData.length >= 10) {
654
+ const flags = chunkData[0];
655
+ const _hasAnimation = (flags & 0x02) !== 0;
656
+ const hasAlpha = (flags & 0x10) !== 0;
657
+ if (hasAlpha) {
658
+ metadata.colorType = "rgba";
659
+ }
660
+ else {
661
+ metadata.colorType = "rgb";
662
+ }
663
+ // Animation is handled in ANIM chunk
664
+ }
665
+ }
666
+ else if (chunkType === "ANIM") {
667
+ // Animation parameters chunk
668
+ if (chunkData.length >= 6) {
669
+ // Background color at bytes 0-3
670
+ // Loop count at bytes 4-5
671
+ const _loopCount = readUint16LE(chunkData, 4);
672
+ // Note: Frame count is not directly in ANIM chunk, need to count ANMF chunks
673
+ // Reset frame count to 0 to start counting ANMF frames
674
+ metadata.frameCount = 0;
675
+ }
676
+ }
677
+ else if (chunkType === "ANMF") {
678
+ // Animation frame - count frames
679
+ if (metadata.frameCount !== undefined) {
680
+ metadata.frameCount++;
681
+ }
682
+ else {
683
+ metadata.frameCount = 1;
684
+ }
685
+ }
686
+ else if (chunkType === "EXIF") {
687
+ // EXIF metadata chunk
688
+ this.parseEXIF(chunkData, metadata);
689
+ }
690
+ else if (chunkType === "XMP ") {
691
+ // XMP metadata chunk
692
+ this.parseXMP(chunkData, metadata);
693
+ }
694
+ pos += chunkSize;
695
+ // Chunks are padded to even length
696
+ if (chunkSize % 2 === 1)
697
+ pos++;
698
+ }
699
+ return Promise.resolve(Object.keys(metadata).length > 0 ? metadata : undefined);
700
+ }
606
701
  }
607
702
  exports.WebPFormat = WebPFormat;
@@ -1,4 +1,4 @@
1
- import type { ImageFormat, ImageMetadata, MultiFrameImageData, ResizeOptions } from "./types.js";
1
+ import type { ImageDecoderOptions, ImageFormat, ImageMetadata, MultiFrameImageData, ResizeOptions } from "./types.js";
2
2
  /**
3
3
  * Main Image class for reading, manipulating, and saving images
4
4
  */
@@ -78,13 +78,26 @@ export declare class Image {
78
78
  * @param format Optional format hint (e.g., "png", "jpeg", "webp")
79
79
  * @returns Image instance
80
80
  */
81
- static decode(data: Uint8Array, format?: string): Promise<Image>;
81
+ static decode(data: Uint8Array): Promise<Image>;
82
+ static decode(data: Uint8Array, format: string): Promise<Image>;
83
+ static decode(data: Uint8Array, format: string, options?: ImageDecoderOptions): Promise<Image>;
84
+ static decode(data: Uint8Array, options?: ImageDecoderOptions): Promise<Image>;
85
+ private static decodeWithSettings;
82
86
  /**
83
87
  * Get supported metadata fields for a specific format
84
88
  * @param format Format name (e.g., "jpeg", "png", "webp")
85
89
  * @returns Array of supported metadata field names, or undefined if format doesn't support metadata
86
90
  */
87
91
  static getSupportedMetadata(format: string): Array<keyof ImageMetadata> | undefined;
92
+ /**
93
+ * Extract metadata from image data without fully decoding the pixel data
94
+ * This is useful for quickly reading EXIF, XMP, or other metadata from images
95
+ * that may have unsupported features or compression methods
96
+ * @param data Raw image data
97
+ * @param format Optional format hint (e.g., "png", "jpeg", "webp")
98
+ * @returns Metadata extracted from the image, or undefined if extraction fails or format is unsupported
99
+ */
100
+ static extractMetadata(data: Uint8Array, format?: string): Promise<ImageMetadata | undefined>;
88
101
  /**
89
102
  * Read an image from bytes
90
103
  * @deprecated Use `decode()` instead. This method will be removed in a future version.
@@ -99,7 +112,11 @@ export declare class Image {
99
112
  * @param format Optional format hint (e.g., "gif", "tiff")
100
113
  * @returns MultiFrameImageData with all frames
101
114
  */
102
- static decodeFrames(data: Uint8Array, format?: string): Promise<MultiFrameImageData>;
115
+ static decodeFrames(data: Uint8Array): Promise<MultiFrameImageData>;
116
+ static decodeFrames(data: Uint8Array, format: string): Promise<MultiFrameImageData>;
117
+ static decodeFrames(data: Uint8Array, format: string, options?: ImageDecoderOptions): Promise<MultiFrameImageData>;
118
+ static decodeFrames(data: Uint8Array, options?: ImageDecoderOptions): Promise<MultiFrameImageData>;
119
+ private static decodeFramesWithSettings;
103
120
  /**
104
121
  * Read all frames from a multi-frame image (GIF animation, multi-page TIFF)
105
122
  * @deprecated Use `decodeFrames()` instead. This method will be removed in a future version.
@@ -16,6 +16,8 @@ const pam_js_1 = require("./formats/pam.js");
16
16
  const pcx_js_1 = require("./formats/pcx.js");
17
17
  const ppm_js_1 = require("./formats/ppm.js");
18
18
  const ascii_js_1 = require("./formats/ascii.js");
19
+ const heic_js_1 = require("./formats/heic.js");
20
+ const avif_js_1 = require("./formats/avif.js");
19
21
  const security_js_1 = require("./utils/security.js");
20
22
  /**
21
23
  * Main Image class for reading, manipulating, and saving images
@@ -159,26 +161,37 @@ class Image {
159
161
  static getFormats() {
160
162
  return Image.formats;
161
163
  }
162
- /**
163
- * Decode an image from bytes
164
- * @param data Raw image data
165
- * @param format Optional format hint (e.g., "png", "jpeg", "webp")
166
- * @returns Image instance
167
- */
168
- static async decode(data, format) {
164
+ static async decode(data, formatOrOptions, options) {
165
+ // Backward-compatible overloads:
166
+ // - decode(data)
167
+ // - decode(data, "jpeg")
168
+ // New:
169
+ // - decode(data, { tolerantDecoding, onWarning, runtimeDecoding })
170
+ // - decode(data, "jpeg", { ...options })
171
+ if (typeof formatOrOptions === "string") {
172
+ return await Image.decodeWithSettings(data, formatOrOptions, options);
173
+ }
174
+ return await Image.decodeWithSettings(data, undefined, formatOrOptions);
175
+ }
176
+ static async decodeWithSettings(data, format, settings) {
169
177
  const image = new Image();
178
+ const normalizedSettings = {
179
+ tolerantDecoding: settings?.tolerantDecoding ?? true,
180
+ onWarning: settings?.onWarning,
181
+ runtimeDecoding: settings?.runtimeDecoding ?? "prefer",
182
+ };
170
183
  // Try specified format first
171
184
  if (format) {
172
185
  const handler = Image.formats.find((f) => f.name === format);
173
186
  if (handler && handler.canDecode(data)) {
174
- image.imageData = await handler.decode(data);
187
+ image.imageData = await handler.decode(data, normalizedSettings);
175
188
  return image;
176
189
  }
177
190
  }
178
191
  // Auto-detect format
179
192
  for (const handler of Image.formats) {
180
193
  if (handler.canDecode(data)) {
181
- image.imageData = await handler.decode(data);
194
+ image.imageData = await handler.decode(data, normalizedSettings);
182
195
  return image;
183
196
  }
184
197
  }
@@ -196,6 +209,30 @@ class Image {
196
209
  }
197
210
  return formatHandler.getSupportedMetadata?.();
198
211
  }
212
+ /**
213
+ * Extract metadata from image data without fully decoding the pixel data
214
+ * This is useful for quickly reading EXIF, XMP, or other metadata from images
215
+ * that may have unsupported features or compression methods
216
+ * @param data Raw image data
217
+ * @param format Optional format hint (e.g., "png", "jpeg", "webp")
218
+ * @returns Metadata extracted from the image, or undefined if extraction fails or format is unsupported
219
+ */
220
+ static async extractMetadata(data, format) {
221
+ // Try specified format first
222
+ if (format) {
223
+ const handler = Image.formats.find((f) => f.name === format);
224
+ if (handler && handler.canDecode(data) && handler.extractMetadata) {
225
+ return await handler.extractMetadata(data);
226
+ }
227
+ }
228
+ // Auto-detect format
229
+ for (const handler of Image.formats) {
230
+ if (handler.canDecode(data) && handler.extractMetadata) {
231
+ return await handler.extractMetadata(data);
232
+ }
233
+ }
234
+ return undefined;
235
+ }
199
236
  /**
200
237
  * Read an image from bytes
201
238
  * @deprecated Use `decode()` instead. This method will be removed in a future version.
@@ -204,26 +241,31 @@ class Image {
204
241
  * @returns Image instance
205
242
  */
206
243
  static read(data, format) {
207
- return Image.decode(data, format);
244
+ return format ? Image.decode(data, format) : Image.decode(data);
208
245
  }
209
- /**
210
- * Decode all frames from a multi-frame image (GIF animation, multi-page TIFF)
211
- * @param data Raw image data
212
- * @param format Optional format hint (e.g., "gif", "tiff")
213
- * @returns MultiFrameImageData with all frames
214
- */
215
- static async decodeFrames(data, format) {
246
+ static async decodeFrames(data, formatOrOptions, options) {
247
+ if (typeof formatOrOptions === "string") {
248
+ return await Image.decodeFramesWithSettings(data, formatOrOptions, options);
249
+ }
250
+ return await Image.decodeFramesWithSettings(data, undefined, formatOrOptions);
251
+ }
252
+ static async decodeFramesWithSettings(data, format, settings) {
253
+ const normalizedSettings = {
254
+ tolerantDecoding: settings?.tolerantDecoding ?? true,
255
+ onWarning: settings?.onWarning,
256
+ runtimeDecoding: settings?.runtimeDecoding ?? "prefer",
257
+ };
216
258
  // Try specified format first
217
259
  if (format) {
218
260
  const handler = Image.formats.find((f) => f.name === format);
219
261
  if (handler && handler.canDecode(data) && handler.decodeFrames) {
220
- return await handler.decodeFrames(data);
262
+ return await handler.decodeFrames(data, normalizedSettings);
221
263
  }
222
264
  }
223
265
  // Auto-detect format
224
266
  for (const handler of Image.formats) {
225
267
  if (handler.canDecode(data) && handler.decodeFrames) {
226
- return await handler.decodeFrames(data);
268
+ return await handler.decodeFrames(data, normalizedSettings);
227
269
  }
228
270
  }
229
271
  throw new Error("Unsupported or unrecognized multi-frame image format");
@@ -236,7 +278,7 @@ class Image {
236
278
  * @returns MultiFrameImageData with all frames
237
279
  */
238
280
  static readFrames(data, format) {
239
- return Image.decodeFrames(data, format);
281
+ return format ? Image.decodeFrames(data, format) : Image.decodeFrames(data);
240
282
  }
241
283
  /**
242
284
  * Encode multi-frame image data to bytes in the specified format
@@ -845,5 +887,7 @@ Object.defineProperty(Image, "formats", {
845
887
  new pcx_js_1.PCXFormat(),
846
888
  new ppm_js_1.PPMFormat(),
847
889
  new ascii_js_1.ASCIIFormat(),
890
+ new heic_js_1.HEICFormat(),
891
+ new avif_js_1.AVIFFormat(),
848
892
  ]
849
893
  });
@@ -50,6 +50,16 @@ export interface ImageMetadata {
50
50
  software?: string;
51
51
  /** User comment / notes */
52
52
  userComment?: string;
53
+ /** Image file format (e.g., "png", "jpeg", "gif", "webp", "tiff") */
54
+ format?: string;
55
+ /** Compression algorithm used (e.g., "deflate", "lzw", "dct", "vp8", "vp8l", "none") */
56
+ compression?: string;
57
+ /** Number of frames in multi-frame images (e.g., animated GIFs, APNGs, multi-page TIFFs) */
58
+ frameCount?: number;
59
+ /** Bit depth per channel (e.g., 8, 16) */
60
+ bitDepth?: number;
61
+ /** Color type (e.g., "grayscale", "rgb", "rgba", "indexed", "grayscale-alpha") */
62
+ colorType?: string;
53
63
  /** Custom metadata fields */
54
64
  custom?: Record<string, string | number | boolean>;
55
65
  }
@@ -128,7 +138,7 @@ export interface ResizeOptions {
128
138
  /**
129
139
  * Options for ASCII art encoding
130
140
  */
131
- export interface ASCIIOptions {
141
+ export interface ASCIIEncoderOptions {
132
142
  /** Target width in characters (default: 80) */
133
143
  width?: number;
134
144
  /** Character set to use (default: "simple") */
@@ -138,10 +148,21 @@ export interface ASCIIOptions {
138
148
  /** Whether to invert brightness (default: false) */
139
149
  invert?: boolean;
140
150
  }
151
+ /**
152
+ * Options for TIFF encoding.
153
+ */
154
+ export interface TIFFEncoderOptions {
155
+ /** Compression method: "none" (default), "lzw", "packbits", or "deflate" */
156
+ compression?: "none" | "lzw" | "packbits" | "deflate";
157
+ /** Encode as grayscale instead of RGB/RGBA (default: false) */
158
+ grayscale?: boolean;
159
+ /** Encode as RGB without alpha channel (default: false, ignored if grayscale is true) */
160
+ rgb?: boolean;
161
+ }
141
162
  /**
142
163
  * Options for WebP encoding
143
164
  */
144
- export interface WebPEncodeOptions {
165
+ export interface WebPEncoderOptions {
145
166
  /**
146
167
  * Encoding quality (1-100, default: 90)
147
168
  * - 100 = lossless (VP8L)
@@ -154,6 +175,47 @@ export interface WebPEncodeOptions {
154
175
  */
155
176
  lossless?: boolean;
156
177
  }
178
+ /**
179
+ * Options for JPEG encoding.
180
+ */
181
+ export interface JPEGEncoderOptions {
182
+ /**
183
+ * Encoding quality (1-100, default depends on encoder backend).
184
+ */
185
+ quality?: number;
186
+ /**
187
+ * Enable progressive JPEG output when using the pure-JS encoder.
188
+ * Runtime encoders do not currently expose a progressive toggle.
189
+ */
190
+ progressive?: boolean;
191
+ }
192
+ /**
193
+ * Common options for decode APIs.
194
+ *
195
+ * These options are runtime-agnostic and can be passed to `Image.decode()` and
196
+ * `Image.decodeFrames()`.
197
+ */
198
+ export interface ImageDecoderOptions {
199
+ /**
200
+ * Controls tolerant decoding in the pure-JS decoders.
201
+ *
202
+ * - true (default): try to recover from corruption and continue
203
+ * - false: strict mode (fail fast)
204
+ */
205
+ tolerantDecoding?: boolean;
206
+ /**
207
+ * Optional warning callback used by pure-JS decoders when non-fatal issues are
208
+ * encountered.
209
+ */
210
+ onWarning?: (message: string, details?: unknown) => void;
211
+ /**
212
+ * Runtime decoder strategy.
213
+ *
214
+ * - "prefer" (default): try runtime decoders first (ImageDecoder/Canvas), then fall back to pure JS
215
+ * - "never": skip runtime decoders and use pure JS when available
216
+ */
217
+ runtimeDecoding?: "prefer" | "never";
218
+ }
157
219
  /**
158
220
  * Image format handler interface
159
221
  */
@@ -167,7 +229,7 @@ export interface ImageFormat {
167
229
  * @param data Raw image data
168
230
  * @returns Decoded image data
169
231
  */
170
- decode(data: Uint8Array): Promise<ImageData>;
232
+ decode(data: Uint8Array, options?: ImageDecoderOptions): Promise<ImageData>;
171
233
  /**
172
234
  * Encode image data to bytes
173
235
  * @param imageData Image data to encode
@@ -186,7 +248,7 @@ export interface ImageFormat {
186
248
  * @param data Raw image data
187
249
  * @returns Decoded multi-frame image data
188
250
  */
189
- decodeFrames?(data: Uint8Array): Promise<MultiFrameImageData>;
251
+ decodeFrames?(data: Uint8Array, options?: ImageDecoderOptions): Promise<MultiFrameImageData>;
190
252
  /**
191
253
  * Encode multi-frame image data to bytes (optional)
192
254
  * @param imageData Multi-frame image data to encode
@@ -203,5 +265,13 @@ export interface ImageFormat {
203
265
  * @returns Array of metadata field names that can be persisted
204
266
  */
205
267
  getSupportedMetadata?(): Array<keyof ImageMetadata>;
268
+ /**
269
+ * Extract metadata from image data without fully decoding the pixel data
270
+ * This is useful for quickly reading EXIF, XMP, or other metadata from images
271
+ * that may have unsupported features or compression methods
272
+ * @param data Raw image data
273
+ * @returns Metadata extracted from the image, or undefined if extraction fails
274
+ */
275
+ extractMetadata?(data: Uint8Array): Promise<ImageMetadata | undefined>;
206
276
  }
207
277
  //# sourceMappingURL=types.d.ts.map
@@ -2,6 +2,7 @@
2
2
  * Pure JavaScript GIF decoder implementation
3
3
  * Supports GIF87a and GIF89a formats with LZW decompression
4
4
  */
5
+ import type { ImageDecoderOptions } from "../types.js";
5
6
  interface GIFImage {
6
7
  width: number;
7
8
  height: number;
@@ -19,7 +20,8 @@ interface GIFFrame {
19
20
  export declare class GIFDecoder {
20
21
  private data;
21
22
  private pos;
22
- constructor(data: Uint8Array);
23
+ private options;
24
+ constructor(data: Uint8Array, settings?: ImageDecoderOptions);
23
25
  private readByte;
24
26
  private readUint16LE;
25
27
  private readBytes;
@@ -36,6 +38,7 @@ export declare class GIFDecoder {
36
38
  frames: GIFFrame[];
37
39
  };
38
40
  private indexedToRGBA;
41
+ private decodeFrame;
39
42
  private deinterlace;
40
43
  }
41
44
  export {};