cross-image 0.2.2 → 0.2.4
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/README.md +333 -168
- package/esm/mod.d.ts +2 -0
- package/esm/mod.js +2 -0
- package/esm/src/formats/apng.d.ts +13 -1
- package/esm/src/formats/apng.js +97 -0
- package/esm/src/formats/ascii.d.ts +11 -1
- package/esm/src/formats/ascii.js +24 -0
- package/esm/src/formats/avif.d.ts +96 -0
- package/esm/src/formats/avif.js +607 -0
- package/esm/src/formats/bmp.d.ts +11 -1
- package/esm/src/formats/bmp.js +73 -0
- package/esm/src/formats/dng.d.ts +13 -1
- package/esm/src/formats/dng.js +26 -4
- package/esm/src/formats/gif.d.ts +15 -2
- package/esm/src/formats/gif.js +146 -4
- package/esm/src/formats/heic.d.ts +96 -0
- package/esm/src/formats/heic.js +608 -0
- package/esm/src/formats/ico.d.ts +11 -1
- package/esm/src/formats/ico.js +28 -0
- package/esm/src/formats/jpeg.d.ts +19 -1
- package/esm/src/formats/jpeg.js +709 -4
- package/esm/src/formats/pam.d.ts +11 -1
- package/esm/src/formats/pam.js +66 -0
- package/esm/src/formats/pcx.d.ts +11 -1
- package/esm/src/formats/pcx.js +45 -0
- package/esm/src/formats/png.d.ts +13 -1
- package/esm/src/formats/png.js +87 -0
- package/esm/src/formats/png_base.d.ts +8 -0
- package/esm/src/formats/png_base.js +176 -3
- package/esm/src/formats/ppm.d.ts +11 -1
- package/esm/src/formats/ppm.js +34 -0
- package/esm/src/formats/tiff.d.ts +13 -1
- package/esm/src/formats/tiff.js +165 -0
- package/esm/src/formats/webp.d.ts +16 -2
- package/esm/src/formats/webp.js +303 -62
- package/esm/src/image.d.ts +60 -0
- package/esm/src/image.js +253 -5
- package/esm/src/types.d.ts +59 -1
- package/esm/src/utils/image_processing.d.ts +55 -0
- package/esm/src/utils/image_processing.js +210 -0
- package/esm/src/utils/metadata/xmp.d.ts +52 -0
- package/esm/src/utils/metadata/xmp.js +325 -0
- package/esm/src/utils/resize.d.ts +4 -0
- package/esm/src/utils/resize.js +74 -0
- package/package.json +18 -1
- package/script/mod.d.ts +2 -0
- package/script/mod.js +5 -1
- package/script/src/formats/apng.d.ts +13 -1
- package/script/src/formats/apng.js +97 -0
- package/script/src/formats/ascii.d.ts +11 -1
- package/script/src/formats/ascii.js +24 -0
- package/script/src/formats/avif.d.ts +96 -0
- package/script/src/formats/avif.js +611 -0
- package/script/src/formats/bmp.d.ts +11 -1
- package/script/src/formats/bmp.js +73 -0
- package/script/src/formats/dng.d.ts +13 -1
- package/script/src/formats/dng.js +26 -4
- package/script/src/formats/gif.d.ts +15 -2
- package/script/src/formats/gif.js +146 -4
- package/script/src/formats/heic.d.ts +96 -0
- package/script/src/formats/heic.js +612 -0
- package/script/src/formats/ico.d.ts +11 -1
- package/script/src/formats/ico.js +28 -0
- package/script/src/formats/jpeg.d.ts +19 -1
- package/script/src/formats/jpeg.js +709 -4
- package/script/src/formats/pam.d.ts +11 -1
- package/script/src/formats/pam.js +66 -0
- package/script/src/formats/pcx.d.ts +11 -1
- package/script/src/formats/pcx.js +45 -0
- package/script/src/formats/png.d.ts +13 -1
- package/script/src/formats/png.js +87 -0
- package/script/src/formats/png_base.d.ts +8 -0
- package/script/src/formats/png_base.js +176 -3
- package/script/src/formats/ppm.d.ts +11 -1
- package/script/src/formats/ppm.js +34 -0
- package/script/src/formats/tiff.d.ts +13 -1
- package/script/src/formats/tiff.js +165 -0
- package/script/src/formats/webp.d.ts +16 -2
- package/script/src/formats/webp.js +303 -62
- package/script/src/image.d.ts +60 -0
- package/script/src/image.js +251 -3
- package/script/src/types.d.ts +59 -1
- package/script/src/utils/image_processing.d.ts +55 -0
- package/script/src/utils/image_processing.js +216 -0
- package/script/src/utils/metadata/xmp.d.ts +52 -0
- package/script/src/utils/metadata/xmp.js +333 -0
- package/script/src/utils/resize.d.ts +4 -0
- package/script/src/utils/resize.js +75 -0
package/README.md
CHANGED
|
@@ -1,168 +1,333 @@
|
|
|
1
|
-
# @cross/image
|
|
2
|
-
|
|
3
|
-
A pure JavaScript, dependency-free, cross-runtime image processing library for
|
|
4
|
-
Deno, Node.js, and Bun. Decode, encode, manipulate, and process images in
|
|
5
|
-
multiple formats including PNG, JPEG, WebP, GIF, and more—all without native
|
|
6
|
-
dependencies.
|
|
7
|
-
|
|
8
|
-
📚 **[Full Documentation](https://cross-image.56k.guru/)**
|
|
9
|
-
|
|
10
|
-
## Features
|
|
11
|
-
|
|
12
|
-
- 🚀 **Pure JavaScript** - No native dependencies
|
|
13
|
-
- 🔌 **Pluggable formats** - Easy to extend with custom formats
|
|
14
|
-
- 📦 **Cross-runtime** - Works on Deno, Node.js (18+), and Bun
|
|
15
|
-
- 🎨 **Multiple formats** - PNG, APNG, JPEG, WebP, GIF, TIFF, BMP, ICO, DNG,
|
|
16
|
-
PAM, PPM, PCX and
|
|
17
|
-
- ✂️ **Image manipulation** - Resize, crop, composite, and more
|
|
18
|
-
- 🎛️ **Image processing** - Chainable filters including `brightness`,
|
|
19
|
-
`contrast`, `saturation`, `exposure`, `blur`, `sharpen`, `sepia`, and
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
.
|
|
75
|
-
.
|
|
76
|
-
.
|
|
77
|
-
.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
await
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
import {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
await
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
|
108
|
-
|
|
|
109
|
-
|
|
|
110
|
-
|
|
|
111
|
-
|
|
|
112
|
-
|
|
|
113
|
-
|
|
|
114
|
-
|
|
|
115
|
-
|
|
|
116
|
-
|
|
|
117
|
-
|
|
|
118
|
-
|
|
|
119
|
-
|
|
|
120
|
-
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
1
|
+
# @cross/image
|
|
2
|
+
|
|
3
|
+
A pure JavaScript, dependency-free, cross-runtime image processing library for
|
|
4
|
+
Deno, Node.js, and Bun. Decode, encode, manipulate, and process images in
|
|
5
|
+
multiple formats including PNG, JPEG, WebP, GIF, and more—all without native
|
|
6
|
+
dependencies.
|
|
7
|
+
|
|
8
|
+
📚 **[Full Documentation](https://cross-image.56k.guru/)**
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- 🚀 **Pure JavaScript** - No native dependencies
|
|
13
|
+
- 🔌 **Pluggable formats** - Easy to extend with custom formats
|
|
14
|
+
- 📦 **Cross-runtime** - Works on Deno, Node.js (18+), and Bun
|
|
15
|
+
- 🎨 **Multiple formats** - PNG, APNG, JPEG, WebP, GIF, TIFF, BMP, ICO, DNG,
|
|
16
|
+
PAM, PPM, PCX, ASCII, HEIC, and AVIF support
|
|
17
|
+
- ✂️ **Image manipulation** - Resize, crop, composite, and more
|
|
18
|
+
- 🎛️ **Image processing** - Chainable filters including `brightness`,
|
|
19
|
+
`contrast`, `saturation`, `hue`, `exposure`, `blur`, `sharpen`, `sepia`, and
|
|
20
|
+
more
|
|
21
|
+
- 🖌️ **Drawing operations** - Create, fill, and manipulate pixels
|
|
22
|
+
- 🧩 **Multi-frame** - Decode/encode animated GIFs, APNGs and multi-page TIFFs
|
|
23
|
+
- 🔧 **Simple API** - Easy to use, intuitive interface
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
### Deno
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { Image } from "jsr:@cross/image";
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Node.js
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install cross-image
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { Image } from "cross-image";
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Bun
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install cross-image
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { Image } from "cross-image";
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
### Deno
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { Image } from "@cross/image";
|
|
59
|
+
|
|
60
|
+
// Decode an image (auto-detects format)
|
|
61
|
+
const data = await Deno.readFile("input.png");
|
|
62
|
+
const image = await Image.decode(data);
|
|
63
|
+
|
|
64
|
+
console.log(`Image size: ${image.width}x${image.height}`);
|
|
65
|
+
|
|
66
|
+
// Create a new blank image
|
|
67
|
+
const canvas = Image.create(800, 600, 255, 255, 255); // white background
|
|
68
|
+
|
|
69
|
+
// Composite the loaded image on top
|
|
70
|
+
canvas.composite(image, 50, 50);
|
|
71
|
+
|
|
72
|
+
// Apply image processing filters
|
|
73
|
+
canvas
|
|
74
|
+
.brightness(0.1)
|
|
75
|
+
.contrast(0.2)
|
|
76
|
+
.saturation(-0.1)
|
|
77
|
+
.blur(1)
|
|
78
|
+
.sharpen(0.3);
|
|
79
|
+
|
|
80
|
+
// Encode in a different format
|
|
81
|
+
const jpeg = await canvas.encode("jpeg");
|
|
82
|
+
await Deno.writeFile("output.jpg", jpeg);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Node.js
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { Image } from "cross-image";
|
|
89
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
90
|
+
|
|
91
|
+
// Read an image (auto-detects format)
|
|
92
|
+
const data = await readFile("input.png");
|
|
93
|
+
const image = await Image.decode(data);
|
|
94
|
+
|
|
95
|
+
console.log(`Image size: ${image.width}x${image.height}`);
|
|
96
|
+
|
|
97
|
+
// Resize the image
|
|
98
|
+
image.resize({ width: 800, height: 600 });
|
|
99
|
+
|
|
100
|
+
// Save in a different format
|
|
101
|
+
const jpeg = await image.encode("jpeg");
|
|
102
|
+
await writeFile("output.jpg", jpeg);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Supported Formats
|
|
106
|
+
|
|
107
|
+
| Format | Pure-JS | Notes |
|
|
108
|
+
| ------ | ----------- | ------------------------------------------------- |
|
|
109
|
+
| PNG | ✅ Full | Complete pure-JS implementation |
|
|
110
|
+
| APNG | ✅ Full | Animated PNG with multi-frame |
|
|
111
|
+
| BMP | ✅ Full | Complete pure-JS implementation |
|
|
112
|
+
| ICO | ✅ Full | Windows Icon format |
|
|
113
|
+
| GIF | ✅ Full | Animated GIF with multi-frame |
|
|
114
|
+
| DNG | ✅ Full | Linear DNG (Uncompressed RGBA) |
|
|
115
|
+
| PAM | ✅ Full | Netpbm PAM format |
|
|
116
|
+
| PPM | ✅ Full | Netpbm PPM format (P3/P6) |
|
|
117
|
+
| PCX | ✅ Full | ZSoft PCX (RLE compressed) |
|
|
118
|
+
| ASCII | ✅ Full | Text-based ASCII art |
|
|
119
|
+
| JPEG | ⚠️ Baseline | Pure-JS baseline DCT only |
|
|
120
|
+
| WebP | ⚠️ Lossless | Pure-JS lossless VP8L |
|
|
121
|
+
| TIFF | ⚠️ Basic | Pure-JS uncompressed, LZW, & grayscale |
|
|
122
|
+
| HEIC | 🔌 Runtime | Requires ImageDecoder/OffscreenCanvas API support |
|
|
123
|
+
| AVIF | 🔌 Runtime | Requires ImageDecoder/OffscreenCanvas API support |
|
|
124
|
+
|
|
125
|
+
See the
|
|
126
|
+
[full format support documentation](https://cross-image.56k.guru/formats/) for
|
|
127
|
+
detailed compatibility information.
|
|
128
|
+
|
|
129
|
+
## Metadata Support
|
|
130
|
+
|
|
131
|
+
@cross/image provides comprehensive EXIF 3.0 compliant metadata support for
|
|
132
|
+
image files, including camera information, GPS coordinates, and InteropIFD
|
|
133
|
+
compatibility markers.
|
|
134
|
+
|
|
135
|
+
### Supported Metadata Fields
|
|
136
|
+
|
|
137
|
+
**Basic Metadata:**
|
|
138
|
+
|
|
139
|
+
- `title`, `description`, `author`, `copyright`
|
|
140
|
+
- `creationDate` - Date/time image was created
|
|
141
|
+
|
|
142
|
+
**Camera Settings (JPEG, TIFF, WebP via XMP):**
|
|
143
|
+
|
|
144
|
+
- `cameraMake`, `cameraModel` - Camera manufacturer and model
|
|
145
|
+
- `lensMake`, `lensModel` - Lens information
|
|
146
|
+
- `iso` - ISO speed rating
|
|
147
|
+
- `exposureTime` - Shutter speed in seconds
|
|
148
|
+
- `fNumber` - Aperture (f-number)
|
|
149
|
+
- `focalLength` - Focal length in mm
|
|
150
|
+
- `flash`, `whiteBalance` - Capture settings
|
|
151
|
+
- `orientation` - Image orientation (1=normal, 3=180°, 6=90°CW, 8=90°CCW)
|
|
152
|
+
- `software` - Software used
|
|
153
|
+
- `userComment` - User notes
|
|
154
|
+
|
|
155
|
+
**GPS Coordinates (All EXIF formats: JPEG, PNG, WebP, TIFF):**
|
|
156
|
+
|
|
157
|
+
- `latitude`, `longitude` - GPS coordinates in decimal degrees
|
|
158
|
+
- Full microsecond precision with DMS (degrees-minutes-seconds) conversion
|
|
159
|
+
|
|
160
|
+
**DPI (JPEG, PNG, TIFF):**
|
|
161
|
+
|
|
162
|
+
- `dpiX`, `dpiY` - Dots per inch for printing
|
|
163
|
+
|
|
164
|
+
### EXIF 3.0 Specification Compliance
|
|
165
|
+
|
|
166
|
+
The library implements the EXIF 3.0 specification with:
|
|
167
|
+
|
|
168
|
+
- **50+ Exif Sub-IFD tags** for comprehensive camera metadata
|
|
169
|
+
- **30+ IFD0 tags** for image information
|
|
170
|
+
- **InteropIFD support** for format compatibility (R98/sRGB, R03/Adobe RGB,
|
|
171
|
+
THM/thumbnail)
|
|
172
|
+
- **GPS IFD** with proper coordinate conversion
|
|
173
|
+
- All EXIF data types (BYTE, ASCII, SHORT, LONG, RATIONAL, etc.)
|
|
174
|
+
|
|
175
|
+
### Example Usage
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
import { Image } from "@cross/image";
|
|
179
|
+
|
|
180
|
+
// Load an image
|
|
181
|
+
const data = await Deno.readFile("photo.jpg");
|
|
182
|
+
const image = await Image.decode(data);
|
|
183
|
+
|
|
184
|
+
// Set metadata
|
|
185
|
+
image.setMetadata({
|
|
186
|
+
author: "Jane Photographer",
|
|
187
|
+
copyright: "© 2024",
|
|
188
|
+
cameraMake: "Canon",
|
|
189
|
+
cameraModel: "EOS R5",
|
|
190
|
+
iso: 800,
|
|
191
|
+
exposureTime: 0.004, // 1/250s
|
|
192
|
+
fNumber: 2.8,
|
|
193
|
+
focalLength: 50,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Set GPS coordinates
|
|
197
|
+
image.setPosition(40.7128, -74.0060); // NYC
|
|
198
|
+
|
|
199
|
+
// Check what metadata a format supports
|
|
200
|
+
const jpegSupports = Image.getSupportedMetadata("jpeg");
|
|
201
|
+
console.log(jpegSupports); // Includes ISO, camera info, GPS, etc.
|
|
202
|
+
|
|
203
|
+
// Save with metadata
|
|
204
|
+
const jpeg = await image.save("jpeg");
|
|
205
|
+
await Deno.writeFile("output.jpg", jpeg);
|
|
206
|
+
|
|
207
|
+
// Metadata is preserved on reload!
|
|
208
|
+
const loaded = await Image.decode(jpeg);
|
|
209
|
+
console.log(loaded.metadata?.cameraMake); // "Canon"
|
|
210
|
+
console.log(loaded.getPosition()); // { latitude: 40.7128, longitude: -74.0060 }
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Extracting Metadata Without Decoding
|
|
214
|
+
|
|
215
|
+
For quickly reading metadata from images without the overhead of decoding pixel
|
|
216
|
+
data, use `Image.extractMetadata()`. This is particularly useful for:
|
|
217
|
+
|
|
218
|
+
- Reading EXIF data from large images or photos
|
|
219
|
+
- Extracting metadata from images with unsupported compression
|
|
220
|
+
- Building image catalogs or galleries
|
|
221
|
+
- Processing metadata in batch operations
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
import { Image } from "@cross/image";
|
|
225
|
+
|
|
226
|
+
// Extract metadata without decoding pixels
|
|
227
|
+
const data = await Deno.readFile("large-photo.jpg");
|
|
228
|
+
const metadata = await Image.extractMetadata(data);
|
|
229
|
+
|
|
230
|
+
console.log(metadata?.cameraMake); // "Canon"
|
|
231
|
+
console.log(metadata?.iso); // 800
|
|
232
|
+
console.log(metadata?.exposureTime); // 0.004
|
|
233
|
+
|
|
234
|
+
// Works with auto-detection
|
|
235
|
+
const metadata2 = await Image.extractMetadata(data); // Detects JPEG
|
|
236
|
+
|
|
237
|
+
// Or specify format explicitly
|
|
238
|
+
const metadata3 = await Image.extractMetadata(data, "jpeg");
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
This method is significantly faster than full decode when you only need
|
|
242
|
+
metadata, as it:
|
|
243
|
+
|
|
244
|
+
- Skips pixel data decompression
|
|
245
|
+
- Only parses metadata chunks/markers
|
|
246
|
+
- Returns `undefined` for unsupported formats
|
|
247
|
+
- Works with JPEG, PNG, WebP, TIFF, HEIC, and AVIF formats
|
|
248
|
+
|
|
249
|
+
### Format-Specific Support
|
|
250
|
+
|
|
251
|
+
Use `Image.getSupportedMetadata(format)` to check which fields are supported:
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
Image.getSupportedMetadata("jpeg"); // Full camera metadata + GPS (21 fields)
|
|
255
|
+
Image.getSupportedMetadata("tiff"); // Comprehensive EXIF + GPS + InteropIFD (23+ fields)
|
|
256
|
+
Image.getSupportedMetadata("png"); // DateTime, GPS, DPI, basic text (9 fields)
|
|
257
|
+
Image.getSupportedMetadata("webp"); // Enhanced XMP + GPS (15 fields - includes camera metadata!)
|
|
258
|
+
Image.getSupportedMetadata("heic"); // Full camera metadata + GPS (19 fields)
|
|
259
|
+
Image.getSupportedMetadata("avif"); // Full camera metadata + GPS (19 fields)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**Format Highlights:**
|
|
263
|
+
|
|
264
|
+
- **JPEG**: Most comprehensive EXIF support, including all camera settings and
|
|
265
|
+
GPS
|
|
266
|
+
- **TIFF**: Full EXIF 3.0 support with IFD structure, InteropIFD compatibility
|
|
267
|
+
- **WebP**: Enhanced XMP implementation with Dublin Core, EXIF, and TIFF
|
|
268
|
+
namespaces
|
|
269
|
+
- **PNG**: Basic EXIF support via eXIf chunk plus GPS coordinates
|
|
270
|
+
- **HEIC**: Full EXIF metadata extraction including camera settings, GPS, and
|
|
271
|
+
image info (runtime-dependent encoding)
|
|
272
|
+
- **AVIF**: Full EXIF metadata extraction including camera settings, GPS, and
|
|
273
|
+
image info (runtime-dependent encoding)
|
|
274
|
+
|
|
275
|
+
## Documentation
|
|
276
|
+
|
|
277
|
+
- **[API Reference](https://cross-image.56k.guru/api/)** - Complete API
|
|
278
|
+
documentation
|
|
279
|
+
- **[Format Support](https://cross-image.56k.guru/formats/)** - Supported
|
|
280
|
+
formats and specifications
|
|
281
|
+
- **[Image Processing](https://cross-image.56k.guru/processing/)** - Filters,
|
|
282
|
+
manipulation, and color adjustments
|
|
283
|
+
- [Filters](https://cross-image.56k.guru/processing/filters/) - Blur, sharpen,
|
|
284
|
+
and noise reduction
|
|
285
|
+
- [Manipulation](https://cross-image.56k.guru/processing/manipulation/) -
|
|
286
|
+
Resize, crop, composite, and draw
|
|
287
|
+
- [Color Adjustments](https://cross-image.56k.guru/processing/color-adjustments/) -
|
|
288
|
+
Brightness, contrast, saturation, and more
|
|
289
|
+
- **[Examples](https://cross-image.56k.guru/examples/)** - Practical examples
|
|
290
|
+
for common tasks
|
|
291
|
+
- [Decoding & Encoding](https://cross-image.56k.guru/examples/decoding-encoding/) -
|
|
292
|
+
Format-specific examples
|
|
293
|
+
- [Using Filters](https://cross-image.56k.guru/examples/filters/) - Filter
|
|
294
|
+
workflows and techniques
|
|
295
|
+
- [Manipulation](https://cross-image.56k.guru/examples/manipulation/) -
|
|
296
|
+
Resizing, cropping, and compositing
|
|
297
|
+
- [Multi-Frame Images](https://cross-image.56k.guru/examples/multi-frame/) -
|
|
298
|
+
Animated GIFs, APNGs, and TIFFs
|
|
299
|
+
- **[JPEG Implementation](https://cross-image.56k.guru/implementation/jpeg-implementation/)** -
|
|
300
|
+
Technical details for JPEG
|
|
301
|
+
- **[WebP Implementation](https://cross-image.56k.guru/implementation/webp-implementation/)** -
|
|
302
|
+
Technical details for WebP
|
|
303
|
+
- **[TIFF Implementation](https://cross-image.56k.guru/implementation/tiff-implementation/)** -
|
|
304
|
+
Technical details for TIFF
|
|
305
|
+
|
|
306
|
+
## Development
|
|
307
|
+
|
|
308
|
+
### Running Tests
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
deno test -A
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Linting and Formatting
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
deno fmt --check
|
|
318
|
+
deno lint
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Type Checking
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
deno check mod.ts
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## License
|
|
328
|
+
|
|
329
|
+
MIT License - see LICENSE file for details.
|
|
330
|
+
|
|
331
|
+
## Contributing
|
|
332
|
+
|
|
333
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
package/esm/mod.d.ts
CHANGED
|
@@ -57,4 +57,6 @@ export { PAMFormat } from "./src/formats/pam.js";
|
|
|
57
57
|
export { PCXFormat } from "./src/formats/pcx.js";
|
|
58
58
|
export { PPMFormat } from "./src/formats/ppm.js";
|
|
59
59
|
export { ASCIIFormat } from "./src/formats/ascii.js";
|
|
60
|
+
export { HEICFormat } from "./src/formats/heic.js";
|
|
61
|
+
export { AVIFFormat } from "./src/formats/avif.js";
|
|
60
62
|
//# sourceMappingURL=mod.d.ts.map
|
package/esm/mod.js
CHANGED
|
@@ -56,3 +56,5 @@ export { PAMFormat } from "./src/formats/pam.js";
|
|
|
56
56
|
export { PCXFormat } from "./src/formats/pcx.js";
|
|
57
57
|
export { PPMFormat } from "./src/formats/ppm.js";
|
|
58
58
|
export { ASCIIFormat } from "./src/formats/ascii.js";
|
|
59
|
+
export { HEICFormat } from "./src/formats/heic.js";
|
|
60
|
+
export { AVIFFormat } from "./src/formats/avif.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ImageData, ImageFormat, MultiFrameImageData } from "../types.js";
|
|
1
|
+
import type { ImageData, ImageFormat, ImageMetadata, MultiFrameImageData } from "../types.js";
|
|
2
2
|
import { PNGBase } from "./png_base.js";
|
|
3
3
|
/**
|
|
4
4
|
* APNG (Animated PNG) format handler
|
|
@@ -46,5 +46,17 @@ export declare class APNGFormat extends PNGBase implements ImageFormat {
|
|
|
46
46
|
*/
|
|
47
47
|
encodeFrames(imageData: MultiFrameImageData): Promise<Uint8Array>;
|
|
48
48
|
private decodeFrameData;
|
|
49
|
+
/**
|
|
50
|
+
* Get the list of metadata fields supported by APNG format
|
|
51
|
+
* Includes all PNG fields plus frame count
|
|
52
|
+
*/
|
|
53
|
+
getSupportedMetadata(): Array<keyof ImageMetadata>;
|
|
54
|
+
/**
|
|
55
|
+
* Extract metadata from APNG data without fully decoding the pixel data
|
|
56
|
+
* This quickly parses PNG chunks to extract metadata including frame count
|
|
57
|
+
* @param data Raw APNG data
|
|
58
|
+
* @returns Extracted metadata or undefined
|
|
59
|
+
*/
|
|
60
|
+
extractMetadata(data: Uint8Array): Promise<ImageMetadata | undefined>;
|
|
49
61
|
}
|
|
50
62
|
//# sourceMappingURL=apng.d.ts.map
|
package/esm/src/formats/apng.js
CHANGED
|
@@ -361,4 +361,101 @@ export class APNGFormat extends PNGBase {
|
|
|
361
361
|
const rgba = this.unfilterAndConvert(decompressed, width, height, bitDepth, colorType);
|
|
362
362
|
return rgba;
|
|
363
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* Get the list of metadata fields supported by APNG format
|
|
366
|
+
* Includes all PNG fields plus frame count
|
|
367
|
+
*/
|
|
368
|
+
getSupportedMetadata() {
|
|
369
|
+
return [
|
|
370
|
+
...super.getSupportedMetadata(),
|
|
371
|
+
"frameCount", // acTL chunk
|
|
372
|
+
];
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Extract metadata from APNG data without fully decoding the pixel data
|
|
376
|
+
* This quickly parses PNG chunks to extract metadata including frame count
|
|
377
|
+
* @param data Raw APNG data
|
|
378
|
+
* @returns Extracted metadata or undefined
|
|
379
|
+
*/
|
|
380
|
+
extractMetadata(data) {
|
|
381
|
+
if (!this.canDecode(data)) {
|
|
382
|
+
return Promise.resolve(undefined);
|
|
383
|
+
}
|
|
384
|
+
let pos = 8; // Skip PNG signature
|
|
385
|
+
let width = 0;
|
|
386
|
+
let height = 0;
|
|
387
|
+
let frameCount = 0;
|
|
388
|
+
const metadata = {
|
|
389
|
+
format: "apng",
|
|
390
|
+
compression: "deflate",
|
|
391
|
+
};
|
|
392
|
+
// Parse chunks for metadata only
|
|
393
|
+
while (pos < data.length) {
|
|
394
|
+
if (pos + 8 > data.length)
|
|
395
|
+
break;
|
|
396
|
+
const length = this.readUint32(data, pos);
|
|
397
|
+
pos += 4;
|
|
398
|
+
const type = String.fromCharCode(data[pos], data[pos + 1], data[pos + 2], data[pos + 3]);
|
|
399
|
+
pos += 4;
|
|
400
|
+
if (pos + length + 4 > data.length)
|
|
401
|
+
break;
|
|
402
|
+
const chunkData = data.slice(pos, pos + length);
|
|
403
|
+
pos += length;
|
|
404
|
+
pos += 4; // Skip CRC
|
|
405
|
+
if (type === "IHDR") {
|
|
406
|
+
width = this.readUint32(chunkData, 0);
|
|
407
|
+
height = this.readUint32(chunkData, 4);
|
|
408
|
+
// Parse bit depth and color type from IHDR
|
|
409
|
+
if (chunkData.length >= 9) {
|
|
410
|
+
metadata.bitDepth = chunkData[8];
|
|
411
|
+
const colorTypeCode = chunkData[9];
|
|
412
|
+
// PNG color types: 0=grayscale, 2=rgb, 3=indexed, 4=grayscale+alpha, 6=rgba
|
|
413
|
+
switch (colorTypeCode) {
|
|
414
|
+
case 0:
|
|
415
|
+
metadata.colorType = "grayscale";
|
|
416
|
+
break;
|
|
417
|
+
case 2:
|
|
418
|
+
metadata.colorType = "rgb";
|
|
419
|
+
break;
|
|
420
|
+
case 3:
|
|
421
|
+
metadata.colorType = "indexed";
|
|
422
|
+
break;
|
|
423
|
+
case 4:
|
|
424
|
+
metadata.colorType = "grayscale-alpha";
|
|
425
|
+
break;
|
|
426
|
+
case 6:
|
|
427
|
+
metadata.colorType = "rgba";
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
else if (type === "acTL") {
|
|
433
|
+
// Animation control chunk - contains frame count
|
|
434
|
+
if (chunkData.length >= 4) {
|
|
435
|
+
frameCount = this.readUint32(chunkData, 0);
|
|
436
|
+
metadata.frameCount = frameCount;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
else if (type === "pHYs") {
|
|
440
|
+
// Physical pixel dimensions
|
|
441
|
+
this.parsePhysChunk(chunkData, metadata, width, height);
|
|
442
|
+
}
|
|
443
|
+
else if (type === "tEXt") {
|
|
444
|
+
// Text chunk
|
|
445
|
+
this.parseTextChunk(chunkData, metadata);
|
|
446
|
+
}
|
|
447
|
+
else if (type === "iTXt") {
|
|
448
|
+
// International text chunk
|
|
449
|
+
this.parseITxtChunk(chunkData, metadata);
|
|
450
|
+
}
|
|
451
|
+
else if (type === "eXIf") {
|
|
452
|
+
// EXIF chunk
|
|
453
|
+
this.parseExifChunk(chunkData, metadata);
|
|
454
|
+
}
|
|
455
|
+
else if (type === "IEND") {
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return Promise.resolve(Object.keys(metadata).length > 0 ? metadata : undefined);
|
|
460
|
+
}
|
|
364
461
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ASCIIOptions, ImageData, ImageFormat } from "../types.js";
|
|
1
|
+
import type { ASCIIOptions, ImageData, ImageFormat, ImageMetadata } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* ASCII format handler
|
|
4
4
|
* Converts images to ASCII art text representation
|
|
@@ -34,5 +34,15 @@ export declare class ASCIIFormat implements ImageFormat {
|
|
|
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
|