cross-image 0.2.4 → 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 +507 -333
- package/esm/mod.d.ts +4 -4
- package/esm/mod.js +2 -2
- package/esm/src/formats/apng.d.ts +5 -5
- package/esm/src/formats/apng.js +7 -9
- package/esm/src/formats/ascii.d.ts +3 -3
- package/esm/src/formats/ascii.js +1 -1
- package/esm/src/formats/avif.d.ts +3 -3
- package/esm/src/formats/avif.js +7 -7
- package/esm/src/formats/bmp.d.ts +3 -3
- package/esm/src/formats/bmp.js +2 -2
- package/esm/src/formats/dng.d.ts +1 -1
- package/esm/src/formats/dng.js +1 -1
- package/esm/src/formats/gif.d.ts +4 -4
- package/esm/src/formats/gif.js +14 -10
- package/esm/src/formats/heic.d.ts +3 -3
- package/esm/src/formats/heic.js +7 -7
- package/esm/src/formats/ico.d.ts +3 -3
- package/esm/src/formats/ico.js +4 -4
- package/esm/src/formats/jpeg.d.ts +3 -3
- package/esm/src/formats/jpeg.js +23 -11
- package/esm/src/formats/pam.d.ts +3 -3
- package/esm/src/formats/pam.js +2 -2
- package/esm/src/formats/pcx.d.ts +3 -3
- package/esm/src/formats/pcx.js +2 -2
- package/esm/src/formats/png.d.ts +3 -3
- package/esm/src/formats/png.js +2 -2
- package/esm/src/formats/png_base.js +2 -5
- package/esm/src/formats/ppm.d.ts +3 -3
- package/esm/src/formats/ppm.js +2 -2
- package/esm/src/formats/tiff.d.ts +7 -18
- package/esm/src/formats/tiff.js +86 -21
- package/esm/src/formats/webp.d.ts +3 -3
- package/esm/src/formats/webp.js +11 -8
- package/esm/src/image.d.ts +11 -3
- package/esm/src/image.js +37 -21
- package/esm/src/types.d.ts +56 -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 +1 -1
- package/script/mod.d.ts +4 -4
- package/script/mod.js +2 -2
- package/script/src/formats/apng.d.ts +5 -5
- package/script/src/formats/apng.js +7 -9
- package/script/src/formats/ascii.d.ts +3 -3
- package/script/src/formats/ascii.js +1 -1
- package/script/src/formats/avif.d.ts +3 -3
- package/script/src/formats/avif.js +7 -7
- package/script/src/formats/bmp.d.ts +3 -3
- package/script/src/formats/bmp.js +2 -2
- package/script/src/formats/dng.d.ts +1 -1
- package/script/src/formats/dng.js +1 -1
- package/script/src/formats/gif.d.ts +4 -4
- package/script/src/formats/gif.js +14 -10
- package/script/src/formats/heic.d.ts +3 -3
- package/script/src/formats/heic.js +7 -7
- package/script/src/formats/ico.d.ts +3 -3
- package/script/src/formats/ico.js +4 -4
- package/script/src/formats/jpeg.d.ts +3 -3
- package/script/src/formats/jpeg.js +23 -11
- package/script/src/formats/pam.d.ts +3 -3
- package/script/src/formats/pam.js +2 -2
- package/script/src/formats/pcx.d.ts +3 -3
- package/script/src/formats/pcx.js +2 -2
- package/script/src/formats/png.d.ts +3 -3
- package/script/src/formats/png.js +2 -2
- package/script/src/formats/png_base.js +2 -5
- package/script/src/formats/ppm.d.ts +3 -3
- package/script/src/formats/ppm.js +2 -2
- package/script/src/formats/tiff.d.ts +7 -18
- package/script/src/formats/tiff.js +86 -21
- package/script/src/formats/webp.d.ts +3 -3
- package/script/src/formats/webp.js +11 -8
- package/script/src/image.d.ts +11 -3
- package/script/src/image.js +36 -20
- package/script/src/types.d.ts +56 -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
package/esm/mod.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
9
|
* ```ts
|
|
10
|
-
* import { Image } from "
|
|
10
|
+
* import { Image } from "jsr:@cross/image";
|
|
11
11
|
*
|
|
12
12
|
* // Decode an image
|
|
13
13
|
* const data = await Deno.readFile("input.png");
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
*
|
|
27
27
|
* @example
|
|
28
28
|
* ```ts
|
|
29
|
-
* import { Image } from "
|
|
29
|
+
* import { Image } from "jsr:@cross/image";
|
|
30
30
|
*
|
|
31
31
|
* // Create a blank canvas
|
|
32
32
|
* const canvas = Image.create(400, 300, 255, 255, 255);
|
|
@@ -43,13 +43,13 @@
|
|
|
43
43
|
* ```
|
|
44
44
|
*/
|
|
45
45
|
export { Image } from "./src/image.js";
|
|
46
|
-
export type {
|
|
46
|
+
export type { ASCIIEncoderOptions, FrameMetadata, ImageData, ImageDecoderOptions, ImageFormat, ImageFrame, ImageMetadata, JPEGEncoderOptions, MultiFrameImageData, ResizeOptions, TIFFEncoderOptions, WebPEncoderOptions, } from "./src/types.js";
|
|
47
47
|
export { PNGFormat } from "./src/formats/png.js";
|
|
48
48
|
export { APNGFormat } from "./src/formats/apng.js";
|
|
49
49
|
export { JPEGFormat } from "./src/formats/jpeg.js";
|
|
50
50
|
export { WebPFormat } from "./src/formats/webp.js";
|
|
51
51
|
export { GIFFormat } from "./src/formats/gif.js";
|
|
52
|
-
export {
|
|
52
|
+
export { TIFFFormat } from "./src/formats/tiff.js";
|
|
53
53
|
export { BMPFormat } from "./src/formats/bmp.js";
|
|
54
54
|
export { ICOFormat } from "./src/formats/ico.js";
|
|
55
55
|
export { DNGFormat } from "./src/formats/dng.js";
|
package/esm/mod.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
9
|
* ```ts
|
|
10
|
-
* import { Image } from "
|
|
10
|
+
* import { Image } from "jsr:@cross/image";
|
|
11
11
|
*
|
|
12
12
|
* // Decode an image
|
|
13
13
|
* const data = await Deno.readFile("input.png");
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
*
|
|
27
27
|
* @example
|
|
28
28
|
* ```ts
|
|
29
|
-
* import { Image } from "
|
|
29
|
+
* import { Image } from "jsr:@cross/image";
|
|
30
30
|
*
|
|
31
31
|
* // Create a blank canvas
|
|
32
32
|
* const canvas = Image.create(400, 300, 255, 255, 255);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ImageData, ImageFormat, ImageMetadata, MultiFrameImageData } from "../types.js";
|
|
1
|
+
import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata, MultiFrameImageData } from "../types.js";
|
|
2
2
|
import { PNGBase } from "./png_base.js";
|
|
3
3
|
/**
|
|
4
4
|
* APNG (Animated PNG) format handler
|
|
@@ -26,25 +26,25 @@ export declare class APNGFormat extends PNGBase implements ImageFormat {
|
|
|
26
26
|
* @param data Raw APNG image data
|
|
27
27
|
* @returns Decoded image data with RGBA pixels of first frame
|
|
28
28
|
*/
|
|
29
|
-
decode(data: Uint8Array): Promise<ImageData>;
|
|
29
|
+
decode(data: Uint8Array, settings?: ImageDecoderOptions): Promise<ImageData>;
|
|
30
30
|
/**
|
|
31
31
|
* Decode all frames from APNG image
|
|
32
32
|
* @param data Raw APNG image data
|
|
33
33
|
* @returns Decoded multi-frame image data
|
|
34
34
|
*/
|
|
35
|
-
decodeFrames(data: Uint8Array): Promise<MultiFrameImageData>;
|
|
35
|
+
decodeFrames(data: Uint8Array, _settings?: ImageDecoderOptions): Promise<MultiFrameImageData>;
|
|
36
36
|
/**
|
|
37
37
|
* Encode RGBA image data to APNG format (single frame)
|
|
38
38
|
* @param imageData Image data to encode
|
|
39
39
|
* @returns Encoded APNG image bytes
|
|
40
40
|
*/
|
|
41
|
-
encode(imageData: ImageData): Promise<Uint8Array>;
|
|
41
|
+
encode(imageData: ImageData, _options?: unknown): Promise<Uint8Array>;
|
|
42
42
|
/**
|
|
43
43
|
* Encode multi-frame image data to APNG format
|
|
44
44
|
* @param imageData Multi-frame image data to encode
|
|
45
45
|
* @returns Encoded APNG image bytes
|
|
46
46
|
*/
|
|
47
|
-
encodeFrames(imageData: MultiFrameImageData): Promise<Uint8Array>;
|
|
47
|
+
encodeFrames(imageData: MultiFrameImageData, _options?: unknown): Promise<Uint8Array>;
|
|
48
48
|
private decodeFrameData;
|
|
49
49
|
/**
|
|
50
50
|
* Get the list of metadata fields supported by APNG format
|
package/esm/src/formats/apng.js
CHANGED
|
@@ -67,8 +67,8 @@ export class APNGFormat extends PNGBase {
|
|
|
67
67
|
* @param data Raw APNG image data
|
|
68
68
|
* @returns Decoded image data with RGBA pixels of first frame
|
|
69
69
|
*/
|
|
70
|
-
async decode(data) {
|
|
71
|
-
const frames = await this.decodeFrames(data);
|
|
70
|
+
async decode(data, settings) {
|
|
71
|
+
const frames = await this.decodeFrames(data, settings);
|
|
72
72
|
const firstFrame = frames.frames[0];
|
|
73
73
|
return {
|
|
74
74
|
width: firstFrame.width,
|
|
@@ -82,7 +82,7 @@ export class APNGFormat extends PNGBase {
|
|
|
82
82
|
* @param data Raw APNG image data
|
|
83
83
|
* @returns Decoded multi-frame image data
|
|
84
84
|
*/
|
|
85
|
-
async decodeFrames(data) {
|
|
85
|
+
async decodeFrames(data, _settings) {
|
|
86
86
|
if (!this.canDecode(data)) {
|
|
87
87
|
throw new Error("Invalid APNG signature or missing acTL chunk");
|
|
88
88
|
}
|
|
@@ -169,9 +169,7 @@ export class APNGFormat extends PNGBase {
|
|
|
169
169
|
const delayDen = this.readUint16(chunk.data, 22);
|
|
170
170
|
const disposeOp = chunk.data[24];
|
|
171
171
|
const blendOp = chunk.data[25];
|
|
172
|
-
const delay = delayDen === 0
|
|
173
|
-
? delayNum * 10
|
|
174
|
-
: Math.round((delayNum / delayDen) * 1000);
|
|
172
|
+
const delay = delayDen === 0 ? delayNum * 10 : Math.round((delayNum / delayDen) * 1000);
|
|
175
173
|
let disposal = "none";
|
|
176
174
|
if (disposeOp === 1)
|
|
177
175
|
disposal = "background";
|
|
@@ -254,7 +252,7 @@ export class APNGFormat extends PNGBase {
|
|
|
254
252
|
* @param imageData Image data to encode
|
|
255
253
|
* @returns Encoded APNG image bytes
|
|
256
254
|
*/
|
|
257
|
-
encode(imageData) {
|
|
255
|
+
encode(imageData, _options) {
|
|
258
256
|
// For single frame, create a multi-frame with one frame
|
|
259
257
|
const multiFrame = {
|
|
260
258
|
width: imageData.width,
|
|
@@ -267,14 +265,14 @@ export class APNGFormat extends PNGBase {
|
|
|
267
265
|
}],
|
|
268
266
|
metadata: imageData.metadata,
|
|
269
267
|
};
|
|
270
|
-
return this.encodeFrames(multiFrame);
|
|
268
|
+
return this.encodeFrames(multiFrame, _options);
|
|
271
269
|
}
|
|
272
270
|
/**
|
|
273
271
|
* Encode multi-frame image data to APNG format
|
|
274
272
|
* @param imageData Multi-frame image data to encode
|
|
275
273
|
* @returns Encoded APNG image bytes
|
|
276
274
|
*/
|
|
277
|
-
async encodeFrames(imageData) {
|
|
275
|
+
async encodeFrames(imageData, _options) {
|
|
278
276
|
const { width, height, frames, metadata } = imageData;
|
|
279
277
|
if (frames.length === 0) {
|
|
280
278
|
throw new Error("No frames to encode");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ASCIIEncoderOptions, ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* ASCII format handler
|
|
4
4
|
* Converts images to ASCII art text representation
|
|
@@ -22,14 +22,14 @@ export declare class ASCIIFormat implements ImageFormat {
|
|
|
22
22
|
* @param data Raw ASCII art data
|
|
23
23
|
* @returns Decoded image data with grayscale RGBA pixels
|
|
24
24
|
*/
|
|
25
|
-
decode(data: Uint8Array): Promise<ImageData>;
|
|
25
|
+
decode(data: Uint8Array, _options?: ImageDecoderOptions): Promise<ImageData>;
|
|
26
26
|
/**
|
|
27
27
|
* Encode RGBA image data to ASCII art
|
|
28
28
|
* @param imageData Image data to encode
|
|
29
29
|
* @param options Optional ASCII encoding options
|
|
30
30
|
* @returns Encoded ASCII art as UTF-8 bytes
|
|
31
31
|
*/
|
|
32
|
-
encode(imageData: ImageData, options?:
|
|
32
|
+
encode(imageData: ImageData, options?: ASCIIEncoderOptions): Promise<Uint8Array>;
|
|
33
33
|
/**
|
|
34
34
|
* Parse options from the options line
|
|
35
35
|
*/
|
package/esm/src/formats/ascii.js
CHANGED
|
@@ -68,7 +68,7 @@ export class ASCIIFormat {
|
|
|
68
68
|
* @param data Raw ASCII art data
|
|
69
69
|
* @returns Decoded image data with grayscale RGBA pixels
|
|
70
70
|
*/
|
|
71
|
-
decode(data) {
|
|
71
|
+
decode(data, _options) {
|
|
72
72
|
if (!this.canDecode(data)) {
|
|
73
73
|
throw new Error("Invalid ASCII art signature");
|
|
74
74
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ImageData, ImageFormat, ImageMetadata } from "../types.js";
|
|
1
|
+
import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* AVIF format handler
|
|
4
4
|
* Supports AVIF images using runtime APIs (ImageDecoder/OffscreenCanvas)
|
|
@@ -21,7 +21,7 @@ export declare class AVIFFormat implements ImageFormat {
|
|
|
21
21
|
* @param data Raw AVIF image data
|
|
22
22
|
* @returns Decoded image data with RGBA pixels
|
|
23
23
|
*/
|
|
24
|
-
decode(data: Uint8Array): Promise<ImageData>;
|
|
24
|
+
decode(data: Uint8Array, settings?: ImageDecoderOptions): Promise<ImageData>;
|
|
25
25
|
/**
|
|
26
26
|
* Encode RGBA image data to AVIF format
|
|
27
27
|
* Uses runtime APIs (OffscreenCanvas) for encoding
|
|
@@ -32,7 +32,7 @@ export declare class AVIFFormat implements ImageFormat {
|
|
|
32
32
|
* @param imageData Image data to encode
|
|
33
33
|
* @returns Encoded AVIF image bytes
|
|
34
34
|
*/
|
|
35
|
-
encode(imageData: ImageData): Promise<Uint8Array>;
|
|
35
|
+
encode(imageData: ImageData, _options?: unknown): Promise<Uint8Array>;
|
|
36
36
|
/**
|
|
37
37
|
* Decode using runtime APIs
|
|
38
38
|
* @param data Raw AVIF data
|
package/esm/src/formats/avif.js
CHANGED
|
@@ -49,10 +49,13 @@ export class AVIFFormat {
|
|
|
49
49
|
* @param data Raw AVIF image data
|
|
50
50
|
* @returns Decoded image data with RGBA pixels
|
|
51
51
|
*/
|
|
52
|
-
async decode(data) {
|
|
52
|
+
async decode(data, settings) {
|
|
53
53
|
if (!this.canDecode(data)) {
|
|
54
54
|
throw new Error("Invalid AVIF signature");
|
|
55
55
|
}
|
|
56
|
+
if (settings?.runtimeDecoding === "never") {
|
|
57
|
+
throw new Error("AVIF decoding requires runtime APIs; set runtimeDecoding to 'prefer'");
|
|
58
|
+
}
|
|
56
59
|
// Extract metadata before decoding pixels
|
|
57
60
|
const metadata = await this.extractMetadata(data);
|
|
58
61
|
// Use runtime decoder
|
|
@@ -76,8 +79,8 @@ export class AVIFFormat {
|
|
|
76
79
|
* @param imageData Image data to encode
|
|
77
80
|
* @returns Encoded AVIF image bytes
|
|
78
81
|
*/
|
|
79
|
-
async encode(imageData) {
|
|
80
|
-
const { width, height, data, metadata } = imageData;
|
|
82
|
+
async encode(imageData, _options) {
|
|
83
|
+
const { width, height, data, metadata: _metadata } = imageData;
|
|
81
84
|
// Try to use runtime encoding if available
|
|
82
85
|
if (typeof OffscreenCanvas !== "undefined") {
|
|
83
86
|
try {
|
|
@@ -98,10 +101,7 @@ export class AVIFFormat {
|
|
|
98
101
|
// parsing and modifying the ISOBMFF container structure
|
|
99
102
|
// For now, we rely on the runtime encoder to preserve metadata
|
|
100
103
|
// if it was passed through the canvas
|
|
101
|
-
|
|
102
|
-
// Future enhancement: inject metadata into AVIF container
|
|
103
|
-
console.warn("AVIF metadata injection not yet implemented, metadata may be lost");
|
|
104
|
-
}
|
|
104
|
+
// Future enhancement: inject metadata into AVIF container
|
|
105
105
|
return encoded;
|
|
106
106
|
}
|
|
107
107
|
}
|
package/esm/src/formats/bmp.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ImageData, ImageFormat, ImageMetadata } from "../types.js";
|
|
1
|
+
import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* BMP format handler
|
|
4
4
|
* Implements a pure JavaScript BMP decoder and encoder
|
|
@@ -19,13 +19,13 @@ export declare class BMPFormat implements ImageFormat {
|
|
|
19
19
|
* @param data Raw BMP image data
|
|
20
20
|
* @returns Decoded image data with RGBA pixels
|
|
21
21
|
*/
|
|
22
|
-
decode(data: Uint8Array): Promise<ImageData>;
|
|
22
|
+
decode(data: Uint8Array, _options?: ImageDecoderOptions): Promise<ImageData>;
|
|
23
23
|
/**
|
|
24
24
|
* Encode RGBA image data to BMP format
|
|
25
25
|
* @param imageData Image data to encode
|
|
26
26
|
* @returns Encoded BMP image bytes
|
|
27
27
|
*/
|
|
28
|
-
encode(imageData: ImageData): Promise<Uint8Array>;
|
|
28
|
+
encode(imageData: ImageData, _options?: unknown): Promise<Uint8Array>;
|
|
29
29
|
/**
|
|
30
30
|
* Get the list of metadata fields supported by BMP format
|
|
31
31
|
*/
|
package/esm/src/formats/bmp.js
CHANGED
|
@@ -38,7 +38,7 @@ export class BMPFormat {
|
|
|
38
38
|
* @param data Raw BMP image data
|
|
39
39
|
* @returns Decoded image data with RGBA pixels
|
|
40
40
|
*/
|
|
41
|
-
decode(data) {
|
|
41
|
+
decode(data, _options) {
|
|
42
42
|
if (!this.canDecode(data)) {
|
|
43
43
|
throw new Error("Invalid BMP signature");
|
|
44
44
|
}
|
|
@@ -115,7 +115,7 @@ export class BMPFormat {
|
|
|
115
115
|
* @param imageData Image data to encode
|
|
116
116
|
* @returns Encoded BMP image bytes
|
|
117
117
|
*/
|
|
118
|
-
encode(imageData) {
|
|
118
|
+
encode(imageData, _options) {
|
|
119
119
|
const { width, height, data, metadata } = imageData;
|
|
120
120
|
// Calculate sizes
|
|
121
121
|
const bytesPerPixel = 4; // We'll encode as 32-bit RGBA
|
package/esm/src/formats/dng.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export declare class DNGFormat extends TIFFFormat {
|
|
|
22
22
|
* @param imageData Image data to encode
|
|
23
23
|
* @returns Encoded DNG image bytes
|
|
24
24
|
*/
|
|
25
|
-
encode(imageData: ImageData): Promise<Uint8Array>;
|
|
25
|
+
encode(imageData: ImageData, _options?: unknown): Promise<Uint8Array>;
|
|
26
26
|
/**
|
|
27
27
|
* Get the list of metadata fields supported by DNG format
|
|
28
28
|
* DNG is based on TIFF, so it supports all TIFF metadata fields
|
package/esm/src/formats/dng.js
CHANGED
|
@@ -74,7 +74,7 @@ export class DNGFormat extends TIFFFormat {
|
|
|
74
74
|
* @param imageData Image data to encode
|
|
75
75
|
* @returns Encoded DNG image bytes
|
|
76
76
|
*/
|
|
77
|
-
encode(imageData) {
|
|
77
|
+
encode(imageData, _options) {
|
|
78
78
|
const { width, height, data } = imageData;
|
|
79
79
|
// We'll create a Linear DNG (demosaiced RGB)
|
|
80
80
|
// This is very similar to a standard TIFF but with specific tags.
|
package/esm/src/formats/gif.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ImageData, ImageFormat, ImageMetadata, MultiFrameImageData } from "../types.js";
|
|
1
|
+
import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata, MultiFrameImageData } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* GIF format handler
|
|
4
4
|
* Now includes pure-JS implementation with custom LZW compression/decompression
|
|
@@ -32,18 +32,18 @@ export declare class GIFFormat implements ImageFormat {
|
|
|
32
32
|
* @param data Raw GIF image data
|
|
33
33
|
* @returns Decoded image data with RGBA pixels of first frame
|
|
34
34
|
*/
|
|
35
|
-
decode(data: Uint8Array): Promise<ImageData>;
|
|
35
|
+
decode(data: Uint8Array, settings?: ImageDecoderOptions): Promise<ImageData>;
|
|
36
36
|
private extractGIFMetadata;
|
|
37
37
|
/**
|
|
38
38
|
* Encode RGBA image data to GIF format (single frame)
|
|
39
39
|
* @param imageData Image data to encode
|
|
40
40
|
* @returns Encoded GIF image bytes
|
|
41
41
|
*/
|
|
42
|
-
encode(imageData: ImageData): Promise<Uint8Array>;
|
|
42
|
+
encode(imageData: ImageData, _options?: unknown): Promise<Uint8Array>;
|
|
43
43
|
/**
|
|
44
44
|
* Decode all frames from an animated GIF
|
|
45
45
|
*/
|
|
46
|
-
decodeFrames(data: Uint8Array): Promise<MultiFrameImageData>;
|
|
46
|
+
decodeFrames(data: Uint8Array, settings?: ImageDecoderOptions): Promise<MultiFrameImageData>;
|
|
47
47
|
/**
|
|
48
48
|
* Encode multi-frame image data to animated GIF
|
|
49
49
|
*/
|
package/esm/src/formats/gif.js
CHANGED
|
@@ -56,13 +56,16 @@ export class GIFFormat {
|
|
|
56
56
|
* @param data Raw GIF image data
|
|
57
57
|
* @returns Decoded image data with RGBA pixels of first frame
|
|
58
58
|
*/
|
|
59
|
-
async decode(data) {
|
|
59
|
+
async decode(data, settings) {
|
|
60
60
|
if (!this.canDecode(data)) {
|
|
61
61
|
throw new Error("Invalid GIF signature");
|
|
62
62
|
}
|
|
63
|
-
// Try pure-JS decoder first
|
|
63
|
+
// Try pure-JS decoder first with tolerant decoding enabled by default
|
|
64
64
|
try {
|
|
65
|
-
const decoder = new GIFDecoder(data
|
|
65
|
+
const decoder = new GIFDecoder(data, {
|
|
66
|
+
tolerantDecoding: settings?.tolerantDecoding ?? true,
|
|
67
|
+
onWarning: settings?.onWarning,
|
|
68
|
+
});
|
|
66
69
|
const result = decoder.decode();
|
|
67
70
|
// Validate dimensions for security (prevent integer overflow and heap exhaustion)
|
|
68
71
|
validateImageDimensions(result.width, result.height);
|
|
@@ -75,9 +78,8 @@ export class GIFFormat {
|
|
|
75
78
|
metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
|
|
76
79
|
};
|
|
77
80
|
}
|
|
78
|
-
catch (
|
|
81
|
+
catch (_error) {
|
|
79
82
|
// Fall back to runtime decoder if pure-JS fails
|
|
80
|
-
console.warn("Pure-JS GIF decoder failed, falling back to runtime:", error);
|
|
81
83
|
let pos = 6; // Skip "GIF89a" or "GIF87a"
|
|
82
84
|
const width = readUint16LE(data, pos);
|
|
83
85
|
pos += 2;
|
|
@@ -151,7 +153,7 @@ export class GIFFormat {
|
|
|
151
153
|
* @param imageData Image data to encode
|
|
152
154
|
* @returns Encoded GIF image bytes
|
|
153
155
|
*/
|
|
154
|
-
async encode(imageData) {
|
|
156
|
+
async encode(imageData, _options) {
|
|
155
157
|
const { width, height, data, metadata } = imageData;
|
|
156
158
|
// Try pure-JS encoder first
|
|
157
159
|
try {
|
|
@@ -164,9 +166,8 @@ export class GIFFormat {
|
|
|
164
166
|
}
|
|
165
167
|
return encoded;
|
|
166
168
|
}
|
|
167
|
-
catch (
|
|
169
|
+
catch (_error) {
|
|
168
170
|
// Fall back to runtime encoding if pure-JS fails
|
|
169
|
-
console.warn("Pure-JS GIF encoder failed, falling back to runtime:", error);
|
|
170
171
|
if (typeof OffscreenCanvas !== "undefined") {
|
|
171
172
|
try {
|
|
172
173
|
const canvas = new OffscreenCanvas(width, height);
|
|
@@ -199,12 +200,15 @@ export class GIFFormat {
|
|
|
199
200
|
/**
|
|
200
201
|
* Decode all frames from an animated GIF
|
|
201
202
|
*/
|
|
202
|
-
decodeFrames(data) {
|
|
203
|
+
decodeFrames(data, settings) {
|
|
203
204
|
if (!this.canDecode(data)) {
|
|
204
205
|
throw new Error("Invalid GIF signature");
|
|
205
206
|
}
|
|
206
207
|
try {
|
|
207
|
-
const decoder = new GIFDecoder(data
|
|
208
|
+
const decoder = new GIFDecoder(data, {
|
|
209
|
+
tolerantDecoding: settings?.tolerantDecoding ?? true,
|
|
210
|
+
onWarning: settings?.onWarning,
|
|
211
|
+
});
|
|
208
212
|
const result = decoder.decodeAllFrames();
|
|
209
213
|
// Extract metadata from comment extensions
|
|
210
214
|
const metadata = this.extractGIFMetadata(data);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ImageData, ImageFormat, ImageMetadata } from "../types.js";
|
|
1
|
+
import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* HEIC format handler
|
|
4
4
|
* Supports HEIC/HEIF images using runtime APIs (ImageDecoder/OffscreenCanvas)
|
|
@@ -21,7 +21,7 @@ export declare class HEICFormat implements ImageFormat {
|
|
|
21
21
|
* @param data Raw HEIC image data
|
|
22
22
|
* @returns Decoded image data with RGBA pixels
|
|
23
23
|
*/
|
|
24
|
-
decode(data: Uint8Array): Promise<ImageData>;
|
|
24
|
+
decode(data: Uint8Array, settings?: ImageDecoderOptions): Promise<ImageData>;
|
|
25
25
|
/**
|
|
26
26
|
* Encode RGBA image data to HEIC format
|
|
27
27
|
* Uses runtime APIs (OffscreenCanvas) for encoding
|
|
@@ -32,7 +32,7 @@ export declare class HEICFormat implements ImageFormat {
|
|
|
32
32
|
* @param imageData Image data to encode
|
|
33
33
|
* @returns Encoded HEIC image bytes
|
|
34
34
|
*/
|
|
35
|
-
encode(imageData: ImageData): Promise<Uint8Array>;
|
|
35
|
+
encode(imageData: ImageData, _options?: unknown): Promise<Uint8Array>;
|
|
36
36
|
/**
|
|
37
37
|
* Decode using runtime APIs
|
|
38
38
|
* @param data Raw HEIC data
|
package/esm/src/formats/heic.js
CHANGED
|
@@ -50,10 +50,13 @@ export class HEICFormat {
|
|
|
50
50
|
* @param data Raw HEIC image data
|
|
51
51
|
* @returns Decoded image data with RGBA pixels
|
|
52
52
|
*/
|
|
53
|
-
async decode(data) {
|
|
53
|
+
async decode(data, settings) {
|
|
54
54
|
if (!this.canDecode(data)) {
|
|
55
55
|
throw new Error("Invalid HEIC signature");
|
|
56
56
|
}
|
|
57
|
+
if (settings?.runtimeDecoding === "never") {
|
|
58
|
+
throw new Error("HEIC decoding requires runtime APIs; set runtimeDecoding to 'prefer'");
|
|
59
|
+
}
|
|
57
60
|
// Extract metadata before decoding pixels
|
|
58
61
|
const metadata = await this.extractMetadata(data);
|
|
59
62
|
// Use runtime decoder
|
|
@@ -77,8 +80,8 @@ export class HEICFormat {
|
|
|
77
80
|
* @param imageData Image data to encode
|
|
78
81
|
* @returns Encoded HEIC image bytes
|
|
79
82
|
*/
|
|
80
|
-
async encode(imageData) {
|
|
81
|
-
const { width, height, data, metadata } = imageData;
|
|
83
|
+
async encode(imageData, _options) {
|
|
84
|
+
const { width, height, data, metadata: _metadata } = imageData;
|
|
82
85
|
// Try to use runtime encoding if available
|
|
83
86
|
if (typeof OffscreenCanvas !== "undefined") {
|
|
84
87
|
try {
|
|
@@ -99,10 +102,7 @@ export class HEICFormat {
|
|
|
99
102
|
// parsing and modifying the ISOBMFF container structure
|
|
100
103
|
// For now, we rely on the runtime encoder to preserve metadata
|
|
101
104
|
// if it was passed through the canvas
|
|
102
|
-
|
|
103
|
-
// Future enhancement: inject metadata into HEIC container
|
|
104
|
-
console.warn("HEIC metadata injection not yet implemented, metadata may be lost");
|
|
105
|
-
}
|
|
105
|
+
// Future enhancement: inject metadata into HEIC container
|
|
106
106
|
return encoded;
|
|
107
107
|
}
|
|
108
108
|
}
|
package/esm/src/formats/ico.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ImageData, ImageFormat, ImageMetadata } from "../types.js";
|
|
1
|
+
import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* ICO format handler
|
|
4
4
|
* Implements a pure JavaScript ICO (Windows Icon) decoder and encoder
|
|
@@ -24,7 +24,7 @@ export declare class ICOFormat implements ImageFormat {
|
|
|
24
24
|
* @param data Raw ICO image data
|
|
25
25
|
* @returns Decoded image data with RGBA pixels
|
|
26
26
|
*/
|
|
27
|
-
decode(data: Uint8Array): Promise<ImageData>;
|
|
27
|
+
decode(data: Uint8Array, options?: ImageDecoderOptions): Promise<ImageData>;
|
|
28
28
|
/**
|
|
29
29
|
* Decode a DIB (Device Independent Bitmap) format
|
|
30
30
|
* This is a BMP without the 14-byte file header
|
|
@@ -36,7 +36,7 @@ export declare class ICOFormat implements ImageFormat {
|
|
|
36
36
|
* @param imageData Image data to encode
|
|
37
37
|
* @returns Encoded ICO image bytes
|
|
38
38
|
*/
|
|
39
|
-
encode(imageData: ImageData): Promise<Uint8Array>;
|
|
39
|
+
encode(imageData: ImageData, _options?: unknown): Promise<Uint8Array>;
|
|
40
40
|
/**
|
|
41
41
|
* Get the list of metadata fields supported by ICO format
|
|
42
42
|
*/
|
package/esm/src/formats/ico.js
CHANGED
|
@@ -49,7 +49,7 @@ export class ICOFormat {
|
|
|
49
49
|
* @param data Raw ICO image data
|
|
50
50
|
* @returns Decoded image data with RGBA pixels
|
|
51
51
|
*/
|
|
52
|
-
async decode(data) {
|
|
52
|
+
async decode(data, options) {
|
|
53
53
|
if (!this.canDecode(data)) {
|
|
54
54
|
throw new Error("Invalid ICO signature");
|
|
55
55
|
}
|
|
@@ -100,7 +100,7 @@ export class ICOFormat {
|
|
|
100
100
|
imageData[2] === 0x4e &&
|
|
101
101
|
imageData[3] === 0x47) {
|
|
102
102
|
// It's a PNG, decode it
|
|
103
|
-
return await this.pngFormat.decode(imageData);
|
|
103
|
+
return await this.pngFormat.decode(imageData, options);
|
|
104
104
|
}
|
|
105
105
|
// Otherwise, it's a BMP without the file header (DIB format)
|
|
106
106
|
return this.decodeDIB(imageData);
|
|
@@ -182,10 +182,10 @@ export class ICOFormat {
|
|
|
182
182
|
* @param imageData Image data to encode
|
|
183
183
|
* @returns Encoded ICO image bytes
|
|
184
184
|
*/
|
|
185
|
-
async encode(imageData) {
|
|
185
|
+
async encode(imageData, _options) {
|
|
186
186
|
const { width, height } = imageData;
|
|
187
187
|
// Encode the image as PNG
|
|
188
|
-
const pngData = await this.pngFormat.encode(imageData);
|
|
188
|
+
const pngData = await this.pngFormat.encode(imageData, undefined);
|
|
189
189
|
// Create ICO file structure
|
|
190
190
|
// ICONDIR (6 bytes) + ICONDIRENTRY (16 bytes) + PNG data
|
|
191
191
|
const icoSize = 6 + 16 + pngData.length;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ImageData, ImageFormat, ImageMetadata } from "../types.js";
|
|
1
|
+
import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata, JPEGEncoderOptions } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* JPEG format handler
|
|
4
4
|
* Implements a basic JPEG decoder and encoder
|
|
@@ -19,13 +19,13 @@ export declare class JPEGFormat implements ImageFormat {
|
|
|
19
19
|
* @param data Raw JPEG 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 JPEG format
|
|
25
25
|
* @param imageData Image data to encode
|
|
26
26
|
* @returns Encoded JPEG image bytes
|
|
27
27
|
*/
|
|
28
|
-
encode(imageData: ImageData): Promise<Uint8Array>;
|
|
28
|
+
encode(imageData: ImageData, options?: JPEGEncoderOptions): Promise<Uint8Array>;
|
|
29
29
|
private injectMetadata;
|
|
30
30
|
private decodeUsingRuntime;
|
|
31
31
|
private parseJFIF;
|
package/esm/src/formats/jpeg.js
CHANGED
|
@@ -37,7 +37,7 @@ export class JPEGFormat {
|
|
|
37
37
|
* @param data Raw JPEG image data
|
|
38
38
|
* @returns Decoded image data with RGBA pixels
|
|
39
39
|
*/
|
|
40
|
-
async decode(data) {
|
|
40
|
+
async decode(data, settings) {
|
|
41
41
|
if (!this.canDecode(data)) {
|
|
42
42
|
throw new Error("Invalid JPEG signature");
|
|
43
43
|
}
|
|
@@ -95,7 +95,7 @@ export class JPEGFormat {
|
|
|
95
95
|
validateImageDimensions(width, height);
|
|
96
96
|
// For a pure JS implementation, we'd need to implement full JPEG decoding
|
|
97
97
|
// which is very complex. Instead, we'll use the browser/runtime's decoder.
|
|
98
|
-
const rgba = await this.decodeUsingRuntime(data, width, height);
|
|
98
|
+
const rgba = await this.decodeUsingRuntime(data, width, height, settings);
|
|
99
99
|
return {
|
|
100
100
|
width,
|
|
101
101
|
height,
|
|
@@ -108,10 +108,13 @@ export class JPEGFormat {
|
|
|
108
108
|
* @param imageData Image data to encode
|
|
109
109
|
* @returns Encoded JPEG image bytes
|
|
110
110
|
*/
|
|
111
|
-
async encode(imageData) {
|
|
111
|
+
async encode(imageData, options) {
|
|
112
112
|
const { width, height, data, metadata } = imageData;
|
|
113
|
+
const requestedQuality = options?.quality;
|
|
114
|
+
const requestedProgressive = options?.progressive;
|
|
113
115
|
// Try to use runtime encoding if available (better quality)
|
|
114
|
-
|
|
116
|
+
// Note: progressive output is only supported by the pure-JS encoder.
|
|
117
|
+
if (!requestedProgressive && typeof OffscreenCanvas !== "undefined") {
|
|
115
118
|
try {
|
|
116
119
|
const canvas = new OffscreenCanvas(width, height);
|
|
117
120
|
const ctx = canvas.getContext("2d");
|
|
@@ -120,9 +123,12 @@ export class JPEGFormat {
|
|
|
120
123
|
const imgDataData = new Uint8ClampedArray(data);
|
|
121
124
|
imgData.data.set(imgDataData);
|
|
122
125
|
ctx.putImageData(imgData, 0, 0);
|
|
126
|
+
const quality = requestedQuality === undefined
|
|
127
|
+
? 0.9
|
|
128
|
+
: Math.max(1, Math.min(100, requestedQuality)) / 100;
|
|
123
129
|
const blob = await canvas.convertToBlob({
|
|
124
130
|
type: "image/jpeg",
|
|
125
|
-
quality
|
|
131
|
+
quality,
|
|
126
132
|
});
|
|
127
133
|
const arrayBuffer = await blob.arrayBuffer();
|
|
128
134
|
const encoded = new Uint8Array(arrayBuffer);
|
|
@@ -141,7 +147,10 @@ export class JPEGFormat {
|
|
|
141
147
|
const { JPEGEncoder } = await import("../utils/jpeg_encoder.js");
|
|
142
148
|
const dpiX = metadata?.dpiX ?? 72;
|
|
143
149
|
const dpiY = metadata?.dpiY ?? 72;
|
|
144
|
-
const encoder = new JPEGEncoder(
|
|
150
|
+
const encoder = new JPEGEncoder({
|
|
151
|
+
quality: requestedQuality,
|
|
152
|
+
progressive: requestedProgressive,
|
|
153
|
+
});
|
|
145
154
|
const encoded = encoder.encode(width, height, data, dpiX, dpiY);
|
|
146
155
|
// Add EXIF metadata if present
|
|
147
156
|
if (metadata && Object.keys(metadata).length > 0) {
|
|
@@ -185,9 +194,10 @@ export class JPEGFormat {
|
|
|
185
194
|
result.set(encoded.slice(pos), pos + app1.length);
|
|
186
195
|
return result;
|
|
187
196
|
}
|
|
188
|
-
async decodeUsingRuntime(data, _width, _height) {
|
|
197
|
+
async decodeUsingRuntime(data, _width, _height, settings) {
|
|
189
198
|
// Try to use ImageDecoder API if available (Deno, modern browsers)
|
|
190
|
-
if (
|
|
199
|
+
if (settings?.runtimeDecoding !== "never" &&
|
|
200
|
+
typeof ImageDecoder !== "undefined") {
|
|
191
201
|
try {
|
|
192
202
|
const decoder = new ImageDecoder({ data, type: "image/jpeg" });
|
|
193
203
|
const result = await decoder.decode();
|
|
@@ -202,15 +212,17 @@ export class JPEGFormat {
|
|
|
202
212
|
bitmap.close();
|
|
203
213
|
return new Uint8Array(imageData.data.buffer);
|
|
204
214
|
}
|
|
205
|
-
catch (
|
|
215
|
+
catch (_error) {
|
|
206
216
|
// ImageDecoder API failed, fall through to pure JS decoder
|
|
207
|
-
console.warn("JPEG decoding with ImageDecoder failed, using pure JS decoder:", error);
|
|
208
217
|
}
|
|
209
218
|
}
|
|
210
219
|
// Fallback to pure JavaScript decoder
|
|
211
220
|
try {
|
|
212
221
|
const { JPEGDecoder } = await import("../utils/jpeg_decoder.js");
|
|
213
|
-
const decoder = new JPEGDecoder(data
|
|
222
|
+
const decoder = new JPEGDecoder(data, {
|
|
223
|
+
tolerantDecoding: settings?.tolerantDecoding ?? true,
|
|
224
|
+
onWarning: settings?.onWarning,
|
|
225
|
+
});
|
|
214
226
|
return decoder.decode();
|
|
215
227
|
}
|
|
216
228
|
catch (error) {
|
package/esm/src/formats/pam.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ImageData, ImageFormat, ImageMetadata } from "../types.js";
|
|
1
|
+
import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* PAM format handler
|
|
4
4
|
* Implements the Netpbm PAM (Portable Arbitrary Map) format.
|
|
@@ -32,13 +32,13 @@ export declare class PAMFormat implements ImageFormat {
|
|
|
32
32
|
* @param data Raw PAM image data
|
|
33
33
|
* @returns Decoded image data with RGBA pixels
|
|
34
34
|
*/
|
|
35
|
-
decode(data: Uint8Array): Promise<ImageData>;
|
|
35
|
+
decode(data: Uint8Array, _options?: ImageDecoderOptions): Promise<ImageData>;
|
|
36
36
|
/**
|
|
37
37
|
* Encode RGBA image data to PAM format
|
|
38
38
|
* @param imageData Image data to encode
|
|
39
39
|
* @returns Encoded PAM image bytes
|
|
40
40
|
*/
|
|
41
|
-
encode(imageData: ImageData): Promise<Uint8Array>;
|
|
41
|
+
encode(imageData: ImageData, _options?: unknown): Promise<Uint8Array>;
|
|
42
42
|
/**
|
|
43
43
|
* Get the list of metadata fields supported by PAM format
|
|
44
44
|
*/
|