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
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");
|
|
@@ -361,4 +359,101 @@ export class APNGFormat extends PNGBase {
|
|
|
361
359
|
const rgba = this.unfilterAndConvert(decompressed, width, height, bitDepth, colorType);
|
|
362
360
|
return rgba;
|
|
363
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* Get the list of metadata fields supported by APNG format
|
|
364
|
+
* Includes all PNG fields plus frame count
|
|
365
|
+
*/
|
|
366
|
+
getSupportedMetadata() {
|
|
367
|
+
return [
|
|
368
|
+
...super.getSupportedMetadata(),
|
|
369
|
+
"frameCount", // acTL chunk
|
|
370
|
+
];
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Extract metadata from APNG data without fully decoding the pixel data
|
|
374
|
+
* This quickly parses PNG chunks to extract metadata including frame count
|
|
375
|
+
* @param data Raw APNG data
|
|
376
|
+
* @returns Extracted metadata or undefined
|
|
377
|
+
*/
|
|
378
|
+
extractMetadata(data) {
|
|
379
|
+
if (!this.canDecode(data)) {
|
|
380
|
+
return Promise.resolve(undefined);
|
|
381
|
+
}
|
|
382
|
+
let pos = 8; // Skip PNG signature
|
|
383
|
+
let width = 0;
|
|
384
|
+
let height = 0;
|
|
385
|
+
let frameCount = 0;
|
|
386
|
+
const metadata = {
|
|
387
|
+
format: "apng",
|
|
388
|
+
compression: "deflate",
|
|
389
|
+
};
|
|
390
|
+
// Parse chunks for metadata only
|
|
391
|
+
while (pos < data.length) {
|
|
392
|
+
if (pos + 8 > data.length)
|
|
393
|
+
break;
|
|
394
|
+
const length = this.readUint32(data, pos);
|
|
395
|
+
pos += 4;
|
|
396
|
+
const type = String.fromCharCode(data[pos], data[pos + 1], data[pos + 2], data[pos + 3]);
|
|
397
|
+
pos += 4;
|
|
398
|
+
if (pos + length + 4 > data.length)
|
|
399
|
+
break;
|
|
400
|
+
const chunkData = data.slice(pos, pos + length);
|
|
401
|
+
pos += length;
|
|
402
|
+
pos += 4; // Skip CRC
|
|
403
|
+
if (type === "IHDR") {
|
|
404
|
+
width = this.readUint32(chunkData, 0);
|
|
405
|
+
height = this.readUint32(chunkData, 4);
|
|
406
|
+
// Parse bit depth and color type from IHDR
|
|
407
|
+
if (chunkData.length >= 9) {
|
|
408
|
+
metadata.bitDepth = chunkData[8];
|
|
409
|
+
const colorTypeCode = chunkData[9];
|
|
410
|
+
// PNG color types: 0=grayscale, 2=rgb, 3=indexed, 4=grayscale+alpha, 6=rgba
|
|
411
|
+
switch (colorTypeCode) {
|
|
412
|
+
case 0:
|
|
413
|
+
metadata.colorType = "grayscale";
|
|
414
|
+
break;
|
|
415
|
+
case 2:
|
|
416
|
+
metadata.colorType = "rgb";
|
|
417
|
+
break;
|
|
418
|
+
case 3:
|
|
419
|
+
metadata.colorType = "indexed";
|
|
420
|
+
break;
|
|
421
|
+
case 4:
|
|
422
|
+
metadata.colorType = "grayscale-alpha";
|
|
423
|
+
break;
|
|
424
|
+
case 6:
|
|
425
|
+
metadata.colorType = "rgba";
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
else if (type === "acTL") {
|
|
431
|
+
// Animation control chunk - contains frame count
|
|
432
|
+
if (chunkData.length >= 4) {
|
|
433
|
+
frameCount = this.readUint32(chunkData, 0);
|
|
434
|
+
metadata.frameCount = frameCount;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
else if (type === "pHYs") {
|
|
438
|
+
// Physical pixel dimensions
|
|
439
|
+
this.parsePhysChunk(chunkData, metadata, width, height);
|
|
440
|
+
}
|
|
441
|
+
else if (type === "tEXt") {
|
|
442
|
+
// Text chunk
|
|
443
|
+
this.parseTextChunk(chunkData, metadata);
|
|
444
|
+
}
|
|
445
|
+
else if (type === "iTXt") {
|
|
446
|
+
// International text chunk
|
|
447
|
+
this.parseITxtChunk(chunkData, metadata);
|
|
448
|
+
}
|
|
449
|
+
else if (type === "eXIf") {
|
|
450
|
+
// EXIF chunk
|
|
451
|
+
this.parseExifChunk(chunkData, metadata);
|
|
452
|
+
}
|
|
453
|
+
else if (type === "IEND") {
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return Promise.resolve(Object.keys(metadata).length > 0 ? metadata : undefined);
|
|
458
|
+
}
|
|
364
459
|
}
|
|
@@ -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,17 +22,27 @@ 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
|
*/
|
|
36
36
|
private parseOptions;
|
|
37
|
+
/**
|
|
38
|
+
* Get the list of metadata fields supported by ASCII format
|
|
39
|
+
*/
|
|
40
|
+
getSupportedMetadata(): Array<keyof ImageMetadata>;
|
|
41
|
+
/**
|
|
42
|
+
* Extract metadata from ASCII art data without fully decoding
|
|
43
|
+
* @param data Raw ASCII art data
|
|
44
|
+
* @returns Extracted metadata or undefined
|
|
45
|
+
*/
|
|
46
|
+
extractMetadata(data: Uint8Array): Promise<ImageMetadata | undefined>;
|
|
37
47
|
}
|
|
38
48
|
//# sourceMappingURL=ascii.d.ts.map
|
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
|
}
|
|
@@ -183,4 +183,28 @@ export class ASCIIFormat {
|
|
|
183
183
|
}
|
|
184
184
|
return options;
|
|
185
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Get the list of metadata fields supported by ASCII format
|
|
188
|
+
*/
|
|
189
|
+
getSupportedMetadata() {
|
|
190
|
+
return []; // ASCII art doesn't support metadata preservation
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Extract metadata from ASCII art data without fully decoding
|
|
194
|
+
* @param data Raw ASCII art data
|
|
195
|
+
* @returns Extracted metadata or undefined
|
|
196
|
+
*/
|
|
197
|
+
extractMetadata(data) {
|
|
198
|
+
if (!this.canDecode(data)) {
|
|
199
|
+
return Promise.resolve(undefined);
|
|
200
|
+
}
|
|
201
|
+
const metadata = {
|
|
202
|
+
format: "ascii",
|
|
203
|
+
compression: "none",
|
|
204
|
+
frameCount: 1,
|
|
205
|
+
bitDepth: 1, // ASCII is 1-bit (character or no character)
|
|
206
|
+
colorType: "grayscale",
|
|
207
|
+
};
|
|
208
|
+
return Promise.resolve(metadata);
|
|
209
|
+
}
|
|
186
210
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* AVIF format handler
|
|
4
|
+
* Supports AVIF images using runtime APIs (ImageDecoder/OffscreenCanvas)
|
|
5
|
+
* Note: Pure JavaScript encode/decode is not supported due to complexity
|
|
6
|
+
*/
|
|
7
|
+
export declare class AVIFFormat implements ImageFormat {
|
|
8
|
+
/** Format name identifier */
|
|
9
|
+
readonly name = "avif";
|
|
10
|
+
/** MIME type for AVIF images */
|
|
11
|
+
readonly mimeType = "image/avif";
|
|
12
|
+
/**
|
|
13
|
+
* Check if the given data is an AVIF image
|
|
14
|
+
* @param data Raw image data to check
|
|
15
|
+
* @returns true if data has AVIF signature
|
|
16
|
+
*/
|
|
17
|
+
canDecode(data: Uint8Array): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Decode AVIF image data to RGBA
|
|
20
|
+
* Uses runtime APIs (ImageDecoder) for decoding
|
|
21
|
+
* @param data Raw AVIF image data
|
|
22
|
+
* @returns Decoded image data with RGBA pixels
|
|
23
|
+
*/
|
|
24
|
+
decode(data: Uint8Array, settings?: ImageDecoderOptions): Promise<ImageData>;
|
|
25
|
+
/**
|
|
26
|
+
* Encode RGBA image data to AVIF format
|
|
27
|
+
* Uses runtime APIs (OffscreenCanvas) for encoding
|
|
28
|
+
*
|
|
29
|
+
* Note: Metadata injection is not currently implemented. Metadata may be lost during encoding
|
|
30
|
+
* as it would require parsing and modifying the ISOBMFF container structure.
|
|
31
|
+
*
|
|
32
|
+
* @param imageData Image data to encode
|
|
33
|
+
* @returns Encoded AVIF image bytes
|
|
34
|
+
*/
|
|
35
|
+
encode(imageData: ImageData, _options?: unknown): Promise<Uint8Array>;
|
|
36
|
+
/**
|
|
37
|
+
* Decode using runtime APIs
|
|
38
|
+
* @param data Raw AVIF data
|
|
39
|
+
* @returns Decoded image dimensions and pixel data
|
|
40
|
+
*/
|
|
41
|
+
private decodeUsingRuntime;
|
|
42
|
+
/**
|
|
43
|
+
* Parse EXIF metadata from AVIF data
|
|
44
|
+
*
|
|
45
|
+
* Note: This is a simplified implementation that searches for EXIF headers linearly.
|
|
46
|
+
* A full implementation would require navigating the ISOBMFF box structure to find
|
|
47
|
+
* the 'meta' box and then the 'Exif' item. This simplified approach may not work
|
|
48
|
+
* in all cases but is suitable for basic metadata extraction when runtime APIs are
|
|
49
|
+
* not available or as a fallback.
|
|
50
|
+
*
|
|
51
|
+
* @param data Raw AVIF data
|
|
52
|
+
* @param metadata Metadata object to populate
|
|
53
|
+
*/
|
|
54
|
+
private parseEXIF;
|
|
55
|
+
/**
|
|
56
|
+
* Parse TIFF-formatted EXIF data
|
|
57
|
+
* @param data EXIF data in TIFF format
|
|
58
|
+
* @param metadata Metadata object to populate
|
|
59
|
+
*/
|
|
60
|
+
private parseTIFFExif;
|
|
61
|
+
/**
|
|
62
|
+
* Parse Exif Sub-IFD for camera settings
|
|
63
|
+
* @param data EXIF data
|
|
64
|
+
* @param exifIfdOffset Offset to Exif Sub-IFD
|
|
65
|
+
* @param littleEndian Byte order
|
|
66
|
+
* @param metadata Metadata object to populate
|
|
67
|
+
*/
|
|
68
|
+
private parseExifSubIFD;
|
|
69
|
+
/**
|
|
70
|
+
* Parse GPS IFD for location data
|
|
71
|
+
* @param data EXIF data
|
|
72
|
+
* @param gpsIfdOffset Offset to GPS IFD
|
|
73
|
+
* @param littleEndian Byte order
|
|
74
|
+
* @param metadata Metadata object to populate
|
|
75
|
+
*/
|
|
76
|
+
private parseGPSIFD;
|
|
77
|
+
/**
|
|
78
|
+
* Read a rational value (numerator/denominator)
|
|
79
|
+
* @param data Data buffer
|
|
80
|
+
* @param offset Offset to rational
|
|
81
|
+
* @param littleEndian Byte order
|
|
82
|
+
* @returns Decimal value
|
|
83
|
+
*/
|
|
84
|
+
private readRational;
|
|
85
|
+
/**
|
|
86
|
+
* Get the list of metadata fields supported by AVIF format
|
|
87
|
+
*/
|
|
88
|
+
getSupportedMetadata(): Array<keyof ImageMetadata>;
|
|
89
|
+
/**
|
|
90
|
+
* Extract metadata from AVIF data without fully decoding the pixel data
|
|
91
|
+
* @param data Raw AVIF data
|
|
92
|
+
* @returns Extracted metadata or undefined
|
|
93
|
+
*/
|
|
94
|
+
extractMetadata(data: Uint8Array): Promise<ImageMetadata | undefined>;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=avif.d.ts.map
|