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
@@ -42,7 +42,7 @@ export class WebPFormat {
42
42
  * @param data Raw WebP image data
43
43
  * @returns Decoded image data with RGBA pixels
44
44
  */
45
- async decode(data) {
45
+ async decode(data, settings) {
46
46
  if (!this.canDecode(data)) {
47
47
  throw new Error("Invalid WebP signature");
48
48
  }
@@ -108,7 +108,7 @@ export class WebPFormat {
108
108
  validateImageDimensions(width, height);
109
109
  // For a pure JS implementation, we'd need to implement full WebP decoding
110
110
  // which is very complex. Instead, we'll use the browser/runtime's decoder.
111
- const rgba = await this.decodeUsingRuntime(data, width, height);
111
+ const rgba = await this.decodeUsingRuntime(data, width, height, settings);
112
112
  return {
113
113
  width,
114
114
  height,
@@ -171,9 +171,10 @@ export class WebPFormat {
171
171
  readUint24LE(data, offset) {
172
172
  return data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16);
173
173
  }
174
- async decodeUsingRuntime(data, _width, _height) {
174
+ async decodeUsingRuntime(data, _width, _height, settings) {
175
175
  // Try to use ImageDecoder API if available (Deno, modern browsers)
176
- if (typeof ImageDecoder !== "undefined") {
176
+ if (settings?.runtimeDecoding !== "never" &&
177
+ typeof ImageDecoder !== "undefined") {
177
178
  try {
178
179
  const decoder = new ImageDecoder({ data, type: "image/webp" });
179
180
  const result = await decoder.decode();
@@ -188,15 +189,17 @@ export class WebPFormat {
188
189
  bitmap.close();
189
190
  return new Uint8Array(imageData.data.buffer);
190
191
  }
191
- catch (error) {
192
+ catch (_error) {
192
193
  // ImageDecoder API failed, fall through to pure JS decoder
193
- console.warn("WebP decoding with ImageDecoder failed, using pure JS decoder:", error);
194
194
  }
195
195
  }
196
- // Fallback to pure JavaScript decoder (VP8L lossless only)
196
+ // Fallback to pure JavaScript decoder (VP8L lossless only) with tolerant mode
197
197
  try {
198
198
  const { WebPDecoder } = await import("../utils/webp_decoder.js");
199
- const decoder = new WebPDecoder(data);
199
+ const decoder = new WebPDecoder(data, {
200
+ tolerantDecoding: settings?.tolerantDecoding ?? true,
201
+ onWarning: settings?.onWarning,
202
+ });
200
203
  const result = decoder.decode();
201
204
  return result.data;
202
205
  }
@@ -567,4 +570,96 @@ export class WebPFormat {
567
570
  "focalLength",
568
571
  ];
569
572
  }
573
+ /**
574
+ * Extract metadata from WebP data without fully decoding the pixel data
575
+ * This quickly parses RIFF chunks to extract EXIF and XMP metadata
576
+ * @param data Raw WebP data
577
+ * @returns Extracted metadata or undefined
578
+ */
579
+ extractMetadata(data) {
580
+ if (!this.canDecode(data)) {
581
+ return Promise.resolve(undefined);
582
+ }
583
+ const metadata = {
584
+ format: "webp",
585
+ frameCount: 1,
586
+ bitDepth: 8,
587
+ };
588
+ let pos = 12; // Skip "RIFF" + size + "WEBP"
589
+ const readUint32LE = (data, offset) => {
590
+ return data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) |
591
+ (data[offset + 3] << 24);
592
+ };
593
+ const readUint16LE = (data, offset) => {
594
+ return data[offset] | (data[offset + 1] << 8);
595
+ };
596
+ // Parse chunks for metadata
597
+ while (pos + 8 <= data.length) {
598
+ const chunkType = String.fromCharCode(data[pos], data[pos + 1], data[pos + 2], data[pos + 3]);
599
+ const chunkSize = readUint32LE(data, pos + 4);
600
+ pos += 8;
601
+ // Stop if we've gone past the end
602
+ if (pos + chunkSize > data.length)
603
+ break;
604
+ const chunkData = data.slice(pos, pos + chunkSize);
605
+ if (chunkType === "VP8 ") {
606
+ // Lossy VP8 chunk
607
+ metadata.compression = "vp8";
608
+ metadata.colorType = "rgb";
609
+ }
610
+ else if (chunkType === "VP8L") {
611
+ // Lossless VP8L chunk
612
+ metadata.compression = "vp8l";
613
+ metadata.colorType = "rgba";
614
+ }
615
+ else if (chunkType === "VP8X") {
616
+ // Extended format chunk - contains animation info
617
+ if (chunkData.length >= 10) {
618
+ const flags = chunkData[0];
619
+ const _hasAnimation = (flags & 0x02) !== 0;
620
+ const hasAlpha = (flags & 0x10) !== 0;
621
+ if (hasAlpha) {
622
+ metadata.colorType = "rgba";
623
+ }
624
+ else {
625
+ metadata.colorType = "rgb";
626
+ }
627
+ // Animation is handled in ANIM chunk
628
+ }
629
+ }
630
+ else if (chunkType === "ANIM") {
631
+ // Animation parameters chunk
632
+ if (chunkData.length >= 6) {
633
+ // Background color at bytes 0-3
634
+ // Loop count at bytes 4-5
635
+ const _loopCount = readUint16LE(chunkData, 4);
636
+ // Note: Frame count is not directly in ANIM chunk, need to count ANMF chunks
637
+ // Reset frame count to 0 to start counting ANMF frames
638
+ metadata.frameCount = 0;
639
+ }
640
+ }
641
+ else if (chunkType === "ANMF") {
642
+ // Animation frame - count frames
643
+ if (metadata.frameCount !== undefined) {
644
+ metadata.frameCount++;
645
+ }
646
+ else {
647
+ metadata.frameCount = 1;
648
+ }
649
+ }
650
+ else if (chunkType === "EXIF") {
651
+ // EXIF metadata chunk
652
+ this.parseEXIF(chunkData, metadata);
653
+ }
654
+ else if (chunkType === "XMP ") {
655
+ // XMP metadata chunk
656
+ this.parseXMP(chunkData, metadata);
657
+ }
658
+ pos += chunkSize;
659
+ // Chunks are padded to even length
660
+ if (chunkSize % 2 === 1)
661
+ pos++;
662
+ }
663
+ return Promise.resolve(Object.keys(metadata).length > 0 ? metadata : undefined);
664
+ }
570
665
  }
@@ -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.
package/esm/src/image.js CHANGED
@@ -1,4 +1,4 @@
1
- import { resizeBicubic, resizeBilinear, resizeNearest, } from "./utils/resize.js";
1
+ import { resizeBicubic, resizeBilinear, resizeNearest } from "./utils/resize.js";
2
2
  import { adjustBrightness, adjustContrast, adjustExposure, adjustHue, adjustSaturation, boxBlur, composite, crop, fillRect, flipHorizontal, flipVertical, gaussianBlur, grayscale, invert, medianFilter, rotate180, rotate270, rotate90, sepia, sharpen, } from "./utils/image_processing.js";
3
3
  import { PNGFormat } from "./formats/png.js";
4
4
  import { APNGFormat } from "./formats/apng.js";
@@ -13,6 +13,8 @@ import { PAMFormat } from "./formats/pam.js";
13
13
  import { PCXFormat } from "./formats/pcx.js";
14
14
  import { PPMFormat } from "./formats/ppm.js";
15
15
  import { ASCIIFormat } from "./formats/ascii.js";
16
+ import { HEICFormat } from "./formats/heic.js";
17
+ import { AVIFFormat } from "./formats/avif.js";
16
18
  import { validateImageDimensions } from "./utils/security.js";
17
19
  /**
18
20
  * Main Image class for reading, manipulating, and saving images
@@ -156,26 +158,37 @@ export class Image {
156
158
  static getFormats() {
157
159
  return Image.formats;
158
160
  }
159
- /**
160
- * Decode an image from bytes
161
- * @param data Raw image data
162
- * @param format Optional format hint (e.g., "png", "jpeg", "webp")
163
- * @returns Image instance
164
- */
165
- static async decode(data, format) {
161
+ static async decode(data, formatOrOptions, options) {
162
+ // Backward-compatible overloads:
163
+ // - decode(data)
164
+ // - decode(data, "jpeg")
165
+ // New:
166
+ // - decode(data, { tolerantDecoding, onWarning, runtimeDecoding })
167
+ // - decode(data, "jpeg", { ...options })
168
+ if (typeof formatOrOptions === "string") {
169
+ return await Image.decodeWithSettings(data, formatOrOptions, options);
170
+ }
171
+ return await Image.decodeWithSettings(data, undefined, formatOrOptions);
172
+ }
173
+ static async decodeWithSettings(data, format, settings) {
166
174
  const image = new Image();
175
+ const normalizedSettings = {
176
+ tolerantDecoding: settings?.tolerantDecoding ?? true,
177
+ onWarning: settings?.onWarning,
178
+ runtimeDecoding: settings?.runtimeDecoding ?? "prefer",
179
+ };
167
180
  // Try specified format first
168
181
  if (format) {
169
182
  const handler = Image.formats.find((f) => f.name === format);
170
183
  if (handler && handler.canDecode(data)) {
171
- image.imageData = await handler.decode(data);
184
+ image.imageData = await handler.decode(data, normalizedSettings);
172
185
  return image;
173
186
  }
174
187
  }
175
188
  // Auto-detect format
176
189
  for (const handler of Image.formats) {
177
190
  if (handler.canDecode(data)) {
178
- image.imageData = await handler.decode(data);
191
+ image.imageData = await handler.decode(data, normalizedSettings);
179
192
  return image;
180
193
  }
181
194
  }
@@ -193,6 +206,30 @@ export class Image {
193
206
  }
194
207
  return formatHandler.getSupportedMetadata?.();
195
208
  }
209
+ /**
210
+ * Extract metadata from image data without fully decoding the pixel data
211
+ * This is useful for quickly reading EXIF, XMP, or other metadata from images
212
+ * that may have unsupported features or compression methods
213
+ * @param data Raw image data
214
+ * @param format Optional format hint (e.g., "png", "jpeg", "webp")
215
+ * @returns Metadata extracted from the image, or undefined if extraction fails or format is unsupported
216
+ */
217
+ static async extractMetadata(data, format) {
218
+ // Try specified format first
219
+ if (format) {
220
+ const handler = Image.formats.find((f) => f.name === format);
221
+ if (handler && handler.canDecode(data) && handler.extractMetadata) {
222
+ return await handler.extractMetadata(data);
223
+ }
224
+ }
225
+ // Auto-detect format
226
+ for (const handler of Image.formats) {
227
+ if (handler.canDecode(data) && handler.extractMetadata) {
228
+ return await handler.extractMetadata(data);
229
+ }
230
+ }
231
+ return undefined;
232
+ }
196
233
  /**
197
234
  * Read an image from bytes
198
235
  * @deprecated Use `decode()` instead. This method will be removed in a future version.
@@ -201,26 +238,31 @@ export class Image {
201
238
  * @returns Image instance
202
239
  */
203
240
  static read(data, format) {
204
- return Image.decode(data, format);
241
+ return format ? Image.decode(data, format) : Image.decode(data);
205
242
  }
206
- /**
207
- * Decode all frames from a multi-frame image (GIF animation, multi-page TIFF)
208
- * @param data Raw image data
209
- * @param format Optional format hint (e.g., "gif", "tiff")
210
- * @returns MultiFrameImageData with all frames
211
- */
212
- static async decodeFrames(data, format) {
243
+ static async decodeFrames(data, formatOrOptions, options) {
244
+ if (typeof formatOrOptions === "string") {
245
+ return await Image.decodeFramesWithSettings(data, formatOrOptions, options);
246
+ }
247
+ return await Image.decodeFramesWithSettings(data, undefined, formatOrOptions);
248
+ }
249
+ static async decodeFramesWithSettings(data, format, settings) {
250
+ const normalizedSettings = {
251
+ tolerantDecoding: settings?.tolerantDecoding ?? true,
252
+ onWarning: settings?.onWarning,
253
+ runtimeDecoding: settings?.runtimeDecoding ?? "prefer",
254
+ };
213
255
  // Try specified format first
214
256
  if (format) {
215
257
  const handler = Image.formats.find((f) => f.name === format);
216
258
  if (handler && handler.canDecode(data) && handler.decodeFrames) {
217
- return await handler.decodeFrames(data);
259
+ return await handler.decodeFrames(data, normalizedSettings);
218
260
  }
219
261
  }
220
262
  // Auto-detect format
221
263
  for (const handler of Image.formats) {
222
264
  if (handler.canDecode(data) && handler.decodeFrames) {
223
- return await handler.decodeFrames(data);
265
+ return await handler.decodeFrames(data, normalizedSettings);
224
266
  }
225
267
  }
226
268
  throw new Error("Unsupported or unrecognized multi-frame image format");
@@ -233,7 +275,7 @@ export class Image {
233
275
  * @returns MultiFrameImageData with all frames
234
276
  */
235
277
  static readFrames(data, format) {
236
- return Image.decodeFrames(data, format);
278
+ return format ? Image.decodeFrames(data, format) : Image.decodeFrames(data);
237
279
  }
238
280
  /**
239
281
  * Encode multi-frame image data to bytes in the specified format
@@ -841,5 +883,7 @@ Object.defineProperty(Image, "formats", {
841
883
  new PCXFormat(),
842
884
  new PPMFormat(),
843
885
  new ASCIIFormat(),
886
+ new HEICFormat(),
887
+ new AVIFFormat(),
844
888
  ]
845
889
  });
@@ -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 {};