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.
- package/LICENSE +21 -21
- package/README.md +292 -74
- package/esm/mod.d.ts +6 -4
- package/esm/mod.js +4 -2
- package/esm/src/formats/apng.d.ts +17 -5
- package/esm/src/formats/apng.js +104 -9
- package/esm/src/formats/ascii.d.ts +13 -3
- package/esm/src/formats/ascii.js +25 -1
- package/esm/src/formats/avif.d.ts +96 -0
- package/esm/src/formats/avif.js +607 -0
- package/esm/src/formats/bmp.d.ts +13 -3
- package/esm/src/formats/bmp.js +75 -2
- package/esm/src/formats/dng.d.ts +14 -2
- package/esm/src/formats/dng.js +27 -5
- package/esm/src/formats/gif.d.ts +18 -5
- package/esm/src/formats/gif.js +160 -14
- package/esm/src/formats/heic.d.ts +96 -0
- package/esm/src/formats/heic.js +608 -0
- package/esm/src/formats/ico.d.ts +13 -3
- package/esm/src/formats/ico.js +32 -4
- package/esm/src/formats/jpeg.d.ts +10 -3
- package/esm/src/formats/jpeg.js +99 -11
- package/esm/src/formats/pam.d.ts +13 -3
- package/esm/src/formats/pam.js +68 -2
- package/esm/src/formats/pcx.d.ts +13 -3
- package/esm/src/formats/pcx.js +47 -2
- package/esm/src/formats/png.d.ts +15 -3
- package/esm/src/formats/png.js +89 -2
- package/esm/src/formats/png_base.js +2 -5
- package/esm/src/formats/ppm.d.ts +13 -3
- package/esm/src/formats/ppm.js +36 -2
- package/esm/src/formats/tiff.d.ts +14 -18
- package/esm/src/formats/tiff.js +219 -20
- package/esm/src/formats/webp.d.ts +10 -3
- package/esm/src/formats/webp.js +103 -8
- package/esm/src/image.d.ts +20 -3
- package/esm/src/image.js +65 -21
- package/esm/src/types.d.ts +74 -4
- package/esm/src/utils/gif_decoder.d.ts +4 -1
- package/esm/src/utils/gif_decoder.js +91 -65
- package/esm/src/utils/image_processing.js +144 -70
- package/esm/src/utils/jpeg_decoder.d.ts +17 -4
- package/esm/src/utils/jpeg_decoder.js +448 -83
- package/esm/src/utils/jpeg_encoder.d.ts +15 -1
- package/esm/src/utils/jpeg_encoder.js +263 -24
- package/esm/src/utils/resize.js +51 -20
- package/esm/src/utils/tiff_deflate.d.ts +18 -0
- package/esm/src/utils/tiff_deflate.js +27 -0
- package/esm/src/utils/tiff_packbits.d.ts +24 -0
- package/esm/src/utils/tiff_packbits.js +90 -0
- package/esm/src/utils/webp_decoder.d.ts +3 -1
- package/esm/src/utils/webp_decoder.js +144 -63
- package/esm/src/utils/webp_encoder.js +5 -11
- package/package.json +18 -1
- package/script/mod.d.ts +6 -4
- package/script/mod.js +7 -3
- package/script/src/formats/apng.d.ts +17 -5
- package/script/src/formats/apng.js +104 -9
- package/script/src/formats/ascii.d.ts +13 -3
- package/script/src/formats/ascii.js +25 -1
- package/script/src/formats/avif.d.ts +96 -0
- package/script/src/formats/avif.js +611 -0
- package/script/src/formats/bmp.d.ts +13 -3
- package/script/src/formats/bmp.js +75 -2
- package/script/src/formats/dng.d.ts +14 -2
- package/script/src/formats/dng.js +27 -5
- package/script/src/formats/gif.d.ts +18 -5
- package/script/src/formats/gif.js +160 -14
- package/script/src/formats/heic.d.ts +96 -0
- package/script/src/formats/heic.js +612 -0
- package/script/src/formats/ico.d.ts +13 -3
- package/script/src/formats/ico.js +32 -4
- package/script/src/formats/jpeg.d.ts +10 -3
- package/script/src/formats/jpeg.js +99 -11
- package/script/src/formats/pam.d.ts +13 -3
- package/script/src/formats/pam.js +68 -2
- package/script/src/formats/pcx.d.ts +13 -3
- package/script/src/formats/pcx.js +47 -2
- package/script/src/formats/png.d.ts +15 -3
- package/script/src/formats/png.js +89 -2
- package/script/src/formats/png_base.js +2 -5
- package/script/src/formats/ppm.d.ts +13 -3
- package/script/src/formats/ppm.js +36 -2
- package/script/src/formats/tiff.d.ts +14 -18
- package/script/src/formats/tiff.js +219 -20
- package/script/src/formats/webp.d.ts +10 -3
- package/script/src/formats/webp.js +103 -8
- package/script/src/image.d.ts +20 -3
- package/script/src/image.js +64 -20
- package/script/src/types.d.ts +74 -4
- package/script/src/utils/gif_decoder.d.ts +4 -1
- package/script/src/utils/gif_decoder.js +91 -65
- package/script/src/utils/image_processing.js +144 -70
- package/script/src/utils/jpeg_decoder.d.ts +17 -4
- package/script/src/utils/jpeg_decoder.js +448 -83
- package/script/src/utils/jpeg_encoder.d.ts +15 -1
- package/script/src/utils/jpeg_encoder.js +263 -24
- package/script/src/utils/resize.js +51 -20
- package/script/src/utils/tiff_deflate.d.ts +18 -0
- package/script/src/utils/tiff_deflate.js +31 -0
- package/script/src/utils/tiff_packbits.d.ts +24 -0
- package/script/src/utils/tiff_packbits.js +94 -0
- package/script/src/utils/webp_decoder.d.ts +3 -1
- package/script/src/utils/webp_decoder.js +144 -63
- 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 (
|
|
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 (
|
|
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;
|
package/script/src/image.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
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/script/src/image.js
CHANGED
|
@@ -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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
static async
|
|
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
|
});
|
package/script/src/types.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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 {};
|