cross-image 0.2.4 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +615 -333
  3. package/esm/mod.d.ts +6 -4
  4. package/esm/mod.js +4 -2
  5. package/esm/src/formats/apng.d.ts +7 -5
  6. package/esm/src/formats/apng.js +15 -10
  7. package/esm/src/formats/ascii.d.ts +3 -3
  8. package/esm/src/formats/ascii.js +1 -1
  9. package/esm/src/formats/avif.d.ts +3 -3
  10. package/esm/src/formats/avif.js +17 -7
  11. package/esm/src/formats/bmp.d.ts +3 -3
  12. package/esm/src/formats/bmp.js +2 -2
  13. package/esm/src/formats/dng.d.ts +1 -1
  14. package/esm/src/formats/dng.js +1 -1
  15. package/esm/src/formats/gif.d.ts +5 -5
  16. package/esm/src/formats/gif.js +17 -13
  17. package/esm/src/formats/heic.d.ts +3 -3
  18. package/esm/src/formats/heic.js +17 -7
  19. package/esm/src/formats/ico.d.ts +3 -3
  20. package/esm/src/formats/ico.js +4 -4
  21. package/esm/src/formats/jpeg.d.ts +3 -3
  22. package/esm/src/formats/jpeg.js +23 -11
  23. package/esm/src/formats/pam.d.ts +3 -3
  24. package/esm/src/formats/pam.js +2 -2
  25. package/esm/src/formats/pcx.d.ts +3 -3
  26. package/esm/src/formats/pcx.js +2 -2
  27. package/esm/src/formats/png.d.ts +4 -3
  28. package/esm/src/formats/png.js +9 -3
  29. package/esm/src/formats/png_base.d.ts +42 -1
  30. package/esm/src/formats/png_base.js +200 -10
  31. package/esm/src/formats/ppm.d.ts +3 -3
  32. package/esm/src/formats/ppm.js +2 -2
  33. package/esm/src/formats/tiff.d.ts +7 -18
  34. package/esm/src/formats/tiff.js +162 -27
  35. package/esm/src/formats/webp.d.ts +3 -3
  36. package/esm/src/formats/webp.js +11 -8
  37. package/esm/src/image.d.ts +26 -3
  38. package/esm/src/image.js +66 -22
  39. package/esm/src/types.d.ts +122 -4
  40. package/esm/src/utils/base64.d.ts +32 -0
  41. package/esm/src/utils/base64.js +173 -0
  42. package/esm/src/utils/gif_decoder.d.ts +4 -1
  43. package/esm/src/utils/gif_decoder.js +91 -65
  44. package/esm/src/utils/gif_encoder.d.ts +3 -1
  45. package/esm/src/utils/gif_encoder.js +4 -2
  46. package/esm/src/utils/image_processing.d.ts +31 -0
  47. package/esm/src/utils/image_processing.js +232 -70
  48. package/esm/src/utils/jpeg_decoder.d.ts +17 -4
  49. package/esm/src/utils/jpeg_decoder.js +448 -83
  50. package/esm/src/utils/jpeg_encoder.d.ts +15 -1
  51. package/esm/src/utils/jpeg_encoder.js +263 -24
  52. package/esm/src/utils/resize.js +51 -20
  53. package/esm/src/utils/tiff_deflate.d.ts +18 -0
  54. package/esm/src/utils/tiff_deflate.js +27 -0
  55. package/esm/src/utils/tiff_packbits.d.ts +24 -0
  56. package/esm/src/utils/tiff_packbits.js +90 -0
  57. package/esm/src/utils/webp_decoder.d.ts +3 -1
  58. package/esm/src/utils/webp_decoder.js +144 -63
  59. package/esm/src/utils/webp_encoder.js +5 -11
  60. package/package.json +1 -1
  61. package/script/mod.d.ts +6 -4
  62. package/script/mod.js +13 -3
  63. package/script/src/formats/apng.d.ts +7 -5
  64. package/script/src/formats/apng.js +15 -10
  65. package/script/src/formats/ascii.d.ts +3 -3
  66. package/script/src/formats/ascii.js +1 -1
  67. package/script/src/formats/avif.d.ts +3 -3
  68. package/script/src/formats/avif.js +17 -7
  69. package/script/src/formats/bmp.d.ts +3 -3
  70. package/script/src/formats/bmp.js +2 -2
  71. package/script/src/formats/dng.d.ts +1 -1
  72. package/script/src/formats/dng.js +1 -1
  73. package/script/src/formats/gif.d.ts +5 -5
  74. package/script/src/formats/gif.js +17 -13
  75. package/script/src/formats/heic.d.ts +3 -3
  76. package/script/src/formats/heic.js +17 -7
  77. package/script/src/formats/ico.d.ts +3 -3
  78. package/script/src/formats/ico.js +4 -4
  79. package/script/src/formats/jpeg.d.ts +3 -3
  80. package/script/src/formats/jpeg.js +23 -11
  81. package/script/src/formats/pam.d.ts +3 -3
  82. package/script/src/formats/pam.js +2 -2
  83. package/script/src/formats/pcx.d.ts +3 -3
  84. package/script/src/formats/pcx.js +2 -2
  85. package/script/src/formats/png.d.ts +4 -3
  86. package/script/src/formats/png.js +9 -3
  87. package/script/src/formats/png_base.d.ts +42 -1
  88. package/script/src/formats/png_base.js +200 -10
  89. package/script/src/formats/ppm.d.ts +3 -3
  90. package/script/src/formats/ppm.js +2 -2
  91. package/script/src/formats/tiff.d.ts +7 -18
  92. package/script/src/formats/tiff.js +162 -27
  93. package/script/src/formats/webp.d.ts +3 -3
  94. package/script/src/formats/webp.js +11 -8
  95. package/script/src/image.d.ts +26 -3
  96. package/script/src/image.js +64 -20
  97. package/script/src/types.d.ts +122 -4
  98. package/script/src/utils/base64.d.ts +32 -0
  99. package/script/src/utils/base64.js +179 -0
  100. package/script/src/utils/gif_decoder.d.ts +4 -1
  101. package/script/src/utils/gif_decoder.js +91 -65
  102. package/script/src/utils/gif_encoder.d.ts +3 -1
  103. package/script/src/utils/gif_encoder.js +4 -2
  104. package/script/src/utils/image_processing.d.ts +31 -0
  105. package/script/src/utils/image_processing.js +236 -70
  106. package/script/src/utils/jpeg_decoder.d.ts +17 -4
  107. package/script/src/utils/jpeg_decoder.js +448 -83
  108. package/script/src/utils/jpeg_encoder.d.ts +15 -1
  109. package/script/src/utils/jpeg_encoder.js +263 -24
  110. package/script/src/utils/resize.js +51 -20
  111. package/script/src/utils/tiff_deflate.d.ts +18 -0
  112. package/script/src/utils/tiff_deflate.js +31 -0
  113. package/script/src/utils/tiff_packbits.d.ts +24 -0
  114. package/script/src/utils/tiff_packbits.js +94 -0
  115. package/script/src/utils/webp_decoder.d.ts +3 -1
  116. package/script/src/utils/webp_decoder.js +144 -63
  117. package/script/src/utils/webp_encoder.js +5 -11
package/README.md CHANGED
@@ -1,333 +1,615 @@
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.
1
+ # @cross/image
2
+
3
+ A pure JavaScript, dependency-free, cross-runtime image processing library for Deno, Node.js, and
4
+ Bun. Decode, encode, manipulate, and process images in multiple formats including PNG, JPEG, WebP,
5
+ GIF, and more—all without native dependencies.
6
+
7
+ 📚 **[Full Documentation](https://cross-image.56k.guru/)**
8
+
9
+ ## Features
10
+
11
+ - 🚀 **Pure JavaScript** - No native dependencies
12
+ - 🔌 **Pluggable formats** - Easy to extend with custom formats
13
+ - 📦 **Cross-runtime** - Works on Deno, Node.js (18+), and Bun
14
+ - 🎨 **Multiple formats** - PNG, APNG, JPEG, WebP, GIF, TIFF, BMP, ICO, DNG, PAM, PPM, PCX, ASCII,
15
+ HEIC, and AVIF support
16
+ - ✂️ **Image manipulation** - Resize, crop, composite, and more
17
+ - 🎛️ **Image processing** - Chainable filters including `brightness`, `contrast`, `saturation`,
18
+ `hue`, `exposure`, `blur`, `sharpen`, `sepia`, and more
19
+ - 🖌️ **Drawing operations** - Create, fill, and manipulate pixels
20
+ - 🧩 **Multi-frame** - Decode/encode animated GIFs, APNGs and multi-page TIFFs
21
+ - 🔧 **Simple API** - Easy to use, intuitive interface
22
+
23
+ ## Installation
24
+
25
+ ### Deno
26
+
27
+ ```ts
28
+ import { Image } from "jsr:@cross/image";
29
+ ```
30
+
31
+ ### Node.js
32
+
33
+ ```bash
34
+ npm install cross-image
35
+ ```
36
+
37
+ ```ts
38
+ import { Image } from "cross-image";
39
+ ```
40
+
41
+ ### Bun
42
+
43
+ ```bash
44
+ npm install cross-image
45
+ ```
46
+
47
+ ```ts
48
+ import { Image } from "cross-image";
49
+ ```
50
+
51
+ ## Quick Start
52
+
53
+ ### Deno
54
+
55
+ ```ts
56
+ import { Image } from "jsr:@cross/image";
57
+
58
+ // Decode an image (auto-detects format)
59
+ const data = await Deno.readFile("input.png");
60
+ const image = await Image.decode(data);
61
+
62
+ console.log(`Image size: ${image.width}x${image.height}`);
63
+
64
+ // Create a new blank image
65
+ const canvas = Image.create(800, 600, 255, 255, 255); // white background
66
+
67
+ // Composite the loaded image on top
68
+ canvas.composite(image, 50, 50);
69
+
70
+ // Apply image processing filters
71
+ canvas
72
+ .brightness(0.1)
73
+ .contrast(0.2)
74
+ .saturation(-0.1)
75
+ .blur(1)
76
+ .sharpen(0.3);
77
+
78
+ // Encode in a different format
79
+ const jpeg = await canvas.encode("jpeg");
80
+ await Deno.writeFile("output.jpg", jpeg);
81
+ ```
82
+
83
+ ### Node.js
84
+
85
+ ```ts
86
+ import { Image } from "cross-image";
87
+ import { readFile, writeFile } from "node:fs/promises";
88
+
89
+ // Read an image (auto-detects format)
90
+ const data = await readFile("input.png");
91
+ const image = await Image.decode(data);
92
+
93
+ console.log(`Image size: ${image.width}x${image.height}`);
94
+
95
+ // Resize the image
96
+ image.resize({ width: 800, height: 600 });
97
+
98
+ // Save in a different format
99
+ const jpeg = await image.encode("jpeg");
100
+ await writeFile("output.jpg", jpeg);
101
+ ```
102
+
103
+ ### Bun
104
+
105
+ ```ts
106
+ import { Image } from "cross-image";
107
+
108
+ // Read an image (auto-detects format)
109
+ const data = await Bun.file("input.png").arrayBuffer();
110
+ const image = await Image.decode(new Uint8Array(data));
111
+
112
+ console.log(`Image size: ${image.width}x${image.height}`);
113
+
114
+ // Resize the image and save
115
+ image.resize({ width: 800, height: 600 });
116
+ const jpeg = await image.encode("jpeg");
117
+ await Bun.write("output.jpg", jpeg);
118
+ ```
119
+
120
+ ## Supported Formats
121
+
122
+ | Format | Pure-JS | Notes |
123
+ | ------ | ------------------------- | ---------------------------------------------------------------------------------------------- |
124
+ | PNG | ✅ Full | Complete pure-JS implementation |
125
+ | APNG | ✅ Full | Animated PNG with multi-frame |
126
+ | BMP | Full | Complete pure-JS implementation |
127
+ | ICO | ✅ Full | Windows Icon format |
128
+ | GIF | ✅ Full | Animated GIF with multi-frame |
129
+ | DNG | ✅ Full | Linear DNG (Uncompressed RGBA) |
130
+ | PAM | ✅ Full | Netpbm PAM format |
131
+ | PPM | Full | Netpbm PPM format (P3/P6) |
132
+ | PCX | Full | ZSoft PCX (RLE compressed) |
133
+ | ASCII | ✅ Full | Text-based ASCII art |
134
+ | JPEG | ⚠️ Baseline & Progressive | Pure-JS baseline & progressive DCT: decode with spectral selection; encode with 2-scan (DC+AC) |
135
+ | WebP | ⚠️ Lossless | Pure-JS lossless VP8L |
136
+ | TIFF | ⚠️ Basic | Pure-JS uncompressed, LZW, PackBits, & Deflate; grayscale & RGB/RGBA |
137
+ | HEIC | 🔌 Runtime | Requires ImageDecoder/OffscreenCanvas API support |
138
+ | AVIF | 🔌 Runtime | Requires ImageDecoder/OffscreenCanvas API support |
139
+
140
+ See the [full format support documentation](https://cross-image.56k.guru/formats/) for detailed
141
+ compatibility information.
142
+
143
+ ## Advanced Usage
144
+
145
+ Most users should stick to the `Image` API (`Image.decode`, `image.encode`, etc.).
146
+
147
+ If you need to work with format handlers directly (e.g. `new PNGFormat()`) or register your own
148
+ `ImageFormat` implementation via `Image.registerFormat(...)`, see the API reference section
149
+ “Exported Format Classes”:
150
+
151
+ - https://cross-image.56k.guru/api/
152
+
153
+ ## JPEG Tolerant Decoding
154
+
155
+ The JPEG decoder includes a tolerant decoding mode (enabled by default) that gracefully handles
156
+ partially corrupted images or complex encoding patterns from mobile phone cameras. When enabled, the
157
+ decoder will continue processing even if some blocks fail to decode, filling failed blocks with
158
+ neutral values.
159
+
160
+ **Features:**
161
+
162
+ - **Enabled by default** - Handles real-world JPEGs from various devices
163
+ - **Progressive JPEG support** - Decodes both baseline and progressive JPEGs
164
+ - **Configurable** - Can be disabled for strict validation
165
+ - **Fault-tolerant** - Recovers partial image data instead of failing completely
166
+ - **Zero configuration** - Works automatically with the standard `Image.decode()` API
167
+
168
+ **When to use:**
169
+
170
+ - Mobile phone JPEGs with complex encoding patterns
171
+ - Progressive JPEG images from web sources
172
+ - Images from various camera manufacturers
173
+ - Partially corrupted JPEG files
174
+ - Production applications requiring maximum compatibility
175
+
176
+ **Example:**
177
+
178
+ ```typescript
179
+ import { Image } from "jsr:@cross/image";
180
+
181
+ const data = await Deno.readFile("mobile-photo.jpg");
182
+ // Default behavior - tolerant decoding enabled
183
+ const image = await Image.decode(data);
184
+
185
+ // Strict mode - fail fast on decode errors
186
+ const strictImage = await Image.decode(data, { tolerantDecoding: false });
187
+
188
+ // Optional: receive warnings during partial decode (pure-JS decoder paths)
189
+ const imageWithWarnings = await Image.decode(data, {
190
+ tolerantDecoding: true,
191
+ runtimeDecoding: "never",
192
+ onWarning: (message, details) => {
193
+ console.log(`JPEG Warning: ${message}`, details);
194
+ },
195
+ });
196
+ ```
197
+
198
+ **Note:** When using `Image.decode()`, the library automatically tries runtime-optimized decoders
199
+
200
+ ## CMYK Color Space Support
201
+
202
+ The library provides utilities for working with CMYK (Cyan, Magenta, Yellow, Key/Black) color space,
203
+ commonly used in professional printing and color manipulation.
204
+
205
+ ### Color Conversion Utilities
206
+
207
+ ```ts
208
+ import { cmykToRgb, rgbToCmyk } from "jsr:@cross/image";
209
+
210
+ // Convert RGB to CMYK
211
+ const [c, m, y, k] = rgbToCmyk(255, 0, 0); // Red
212
+ console.log({ c, m, y, k }); // { c: 0, m: 1, y: 1, k: 0 }
213
+
214
+ // Convert CMYK back to RGB
215
+ const [r, g, b] = cmykToRgb(c, m, y, k);
216
+ console.log({ r, g, b }); // { r: 255, g: 0, b: 0 }
217
+ ```
218
+
219
+ ### Image-Level CMYK Operations
220
+
221
+ ```ts
222
+ import { Image } from "jsr:@cross/image";
223
+
224
+ // Load an image and convert to CMYK
225
+ const data = await Deno.readFile("photo.jpg");
226
+ const image = await Image.decode(data);
227
+
228
+ // Get CMYK representation (Float32Array with 4 values per pixel)
229
+ const cmykData = image.toCMYK();
230
+
231
+ // Create an image from CMYK data
232
+ const restored = Image.fromCMYK(cmykData, image.width, image.height);
233
+
234
+ // Save the restored image
235
+ await Deno.writeFile("output.png", await restored.encode("png"));
236
+ ```
237
+
238
+ ### Batch Conversion
239
+
240
+ ```ts
241
+ import { cmykToRgba, rgbaToCmyk } from "jsr:@cross/image";
242
+
243
+ // Convert entire image data to CMYK
244
+ const rgbaData = new Uint8Array([255, 0, 0, 255]); // Red pixel
245
+ const cmykData = rgbaToCmyk(rgbaData);
246
+
247
+ // Convert CMYK data back to RGBA
248
+ const rgbaRestored = cmykToRgba(cmykData);
249
+ ```
250
+
251
+ **Use Cases:**
252
+
253
+ - Pre-press and print preparation workflows
254
+ - Color space conversion for professional printing
255
+ - Color analysis and manipulation in CMYK space
256
+ - Educational tools for understanding color models
257
+
258
+ ### CMYK TIFF Support
259
+
260
+ TIFF format has native support for CMYK images:
261
+
262
+ ```ts
263
+ import { Image } from "jsr:@cross/image";
264
+
265
+ // Decode CMYK TIFF (automatically converted to RGBA)
266
+ const cmykTiff = await Deno.readFile("cmyk-image.tif");
267
+ const image = await Image.decode(cmykTiff); // Automatic CMYK RGBA conversion
268
+
269
+ // Encode image as CMYK TIFF
270
+ const output = await image.encode("tiff", { cmyk: true });
271
+ await Deno.writeFile("output-cmyk.tif", output);
272
+
273
+ // CMYK works with all TIFF compression methods
274
+ const compressed = await image.encode("tiff", {
275
+ cmyk: true,
276
+ compression: "lzw", // or "packbits", "deflate", "none"
277
+ });
278
+ ```
279
+
280
+ **Benefits:**
281
+
282
+ - **Seamless workflow**: CMYK TIFFs decode automatically, no special handling needed
283
+ - **Print-ready output**: Generate CMYK TIFFs for professional printing workflows
284
+ - **Full compression support**: CMYK works with all TIFF compression methods
285
+ - **Industry standard**: TIFF is the preferred format for CMYK images in print production
286
+
287
+ ## Base64 / Data URLs
288
+
289
+ The library includes small utilities for working with base64 and `data:` URLs.
290
+
291
+ ```ts
292
+ import { Image, parseDataUrl, toDataUrl } from "jsr:@cross/image";
293
+
294
+ const image = Image.create(2, 2, 255, 0, 0);
295
+ const pngBytes = await image.encode("png");
296
+
297
+ const dataUrl = toDataUrl("image/png", pngBytes);
298
+ const parsed = parseDataUrl(dataUrl);
299
+
300
+ // parsed.bytes is a Uint8Array containing the PNG
301
+ const roundtrip = await Image.decode(parsed.bytes, "png");
302
+ console.log(roundtrip.width, roundtrip.height);
303
+ ```
304
+
305
+ (ImageDecoder API) first, falling back to the pure JS decoder with tolerant mode for maximum
306
+ compatibility.
307
+
308
+ ## Fault-Tolerant Decoding for Other Formats
309
+
310
+ In addition to JPEG, @cross/image provides fault-tolerant decoding for several other formats that
311
+ commonly encounter corruption or complex encoding patterns:
312
+
313
+ ### GIF Fault-Tolerant Decoding
314
+
315
+ The GIF decoder supports frame-level tolerance for animated GIFs. When enabled (default), corrupted
316
+ frames are skipped instead of causing complete decode failure.
317
+
318
+ **Features:**
319
+
320
+ - **Enabled by default** - Handles multi-frame GIFs with some corrupted frames
321
+ - **Frame-level recovery** - Skips bad frames, preserves good ones
322
+ - **LZW decompression errors** - Continues past compression errors
323
+
324
+ **Example:**
325
+
326
+ ```typescript
327
+ import { Image } from "jsr:@cross/image";
328
+
329
+ const data = await Deno.readFile("animated.gif");
330
+
331
+ // Tolerant mode (default) - skips corrupted frames
332
+ const tolerantFrames = await Image.decodeFrames(data);
333
+
334
+ // Strict mode - throws on first corrupted frame
335
+ const strictFrames = await Image.decodeFrames(data, {
336
+ tolerantDecoding: false,
337
+ });
338
+
339
+ // Optional: receive warnings when frames are skipped
340
+ const framesWithWarnings = await Image.decodeFrames(data, {
341
+ tolerantDecoding: true,
342
+ runtimeDecoding: "never",
343
+ onWarning: (message, details) => {
344
+ console.log(`GIF Warning: ${message}`, details);
345
+ },
346
+ });
347
+ ```
348
+
349
+ ### WebP Fault-Tolerant Decoding (VP8L Lossless)
350
+
351
+ The WebP VP8L (lossless) decoder supports pixel-level tolerance. When enabled (default), decoding
352
+ errors result in gray pixels for remaining data instead of complete failure.
353
+
354
+ **Features:**
355
+
356
+ - **Enabled by default** - Handles VP8L images with Huffman/LZ77 errors
357
+ - **Pixel-level recovery** - Fills remaining pixels with neutral gray
358
+ - **Huffman decode errors** - Continues past invalid codes
359
+
360
+ **Example:**
361
+
362
+ ```typescript
363
+ import { Image } from "jsr:@cross/image";
364
+
365
+ const data = await Deno.readFile("image.webp");
366
+
367
+ // Tolerant mode (default) - fills bad pixels with gray
368
+ const tolerantImage = await Image.decode(data);
369
+
370
+ // Strict mode - throws on first decode error
371
+ const strictImage = await Image.decode(data, { tolerantDecoding: false });
372
+
373
+ // Optional: receive warnings during partial decode
374
+ const imageWithWarnings = await Image.decode(data, {
375
+ tolerantDecoding: true,
376
+ runtimeDecoding: "never",
377
+ onWarning: (message, details) => {
378
+ console.log(`WebP Warning: ${message}`, details);
379
+ },
380
+ });
381
+ void imageWithWarnings;
382
+ ```
383
+
384
+ ### When to Use Fault-Tolerant Modes
385
+
386
+ **Use tolerant decoding (default) when:**
387
+
388
+ - Processing user-uploaded images from various sources
389
+ - Building production applications requiring maximum compatibility
390
+ - Handling images from mobile devices or cameras
391
+ - Recovering data from partially corrupted files
392
+ - Batch processing where some failures are acceptable
393
+
394
+ **Use strict decoding when:**
395
+
396
+ - Validating image file integrity
397
+ - Quality control in professional workflows
398
+ - Detecting file corruption explicitly
399
+ - Testing image encoder implementations
400
+
401
+ ### Warning Callbacks
402
+
403
+ Decoding APIs accept an optional `onWarning` callback that gets invoked when non-fatal issues occur
404
+ during decoding. This is useful for logging, monitoring, or debugging decoding issues without using
405
+ `console` methods.
406
+
407
+ **Example:**
408
+
409
+ ```typescript
410
+ import { Image } from "jsr:@cross/image";
411
+
412
+ const data = await Deno.readFile("input.webp");
413
+
414
+ const image = await Image.decode(data, {
415
+ tolerantDecoding: true,
416
+ runtimeDecoding: "never",
417
+ onWarning: (message, details) => {
418
+ // Log to your preferred logging system
419
+ myLogger.warn(message, details);
420
+ },
421
+ });
422
+ void image;
423
+ ```
424
+
425
+ The callback receives:
426
+
427
+ - `message`: A human-readable description of the warning
428
+ - `details`: Optional additional context (usually the error object)
429
+
430
+ ## Metadata Support
431
+
432
+ @cross/image provides comprehensive EXIF 3.0 compliant metadata support for image files, including
433
+ camera information, GPS coordinates, and InteropIFD compatibility markers.
434
+
435
+ ### Supported Metadata Fields
436
+
437
+ **Basic Metadata:**
438
+
439
+ - `title`, `description`, `author`, `copyright`
440
+ - `creationDate` - Date/time image was created
441
+
442
+ **Camera Settings (JPEG, TIFF, WebP via XMP):**
443
+
444
+ - `cameraMake`, `cameraModel` - Camera manufacturer and model
445
+ - `lensMake`, `lensModel` - Lens information
446
+ - `iso` - ISO speed rating
447
+ - `exposureTime` - Shutter speed in seconds
448
+ - `fNumber` - Aperture (f-number)
449
+ - `focalLength` - Focal length in mm
450
+ - `flash`, `whiteBalance` - Capture settings
451
+ - `orientation` - Image orientation (1=normal, 3=180°, 6=90°CW, 8=90°CCW)
452
+ - `software` - Software used
453
+ - `userComment` - User notes
454
+
455
+ **GPS Coordinates (All EXIF formats: JPEG, PNG, WebP, TIFF):**
456
+
457
+ - `latitude`, `longitude` - GPS coordinates in decimal degrees
458
+ - Full microsecond precision with DMS (degrees-minutes-seconds) conversion
459
+
460
+ **DPI (JPEG, PNG, TIFF):**
461
+
462
+ - `dpiX`, `dpiY` - Dots per inch for printing
463
+
464
+ ### EXIF 3.0 Specification Compliance
465
+
466
+ The library implements the EXIF 3.0 specification with:
467
+
468
+ - **50+ Exif Sub-IFD tags** for comprehensive camera metadata
469
+ - **30+ IFD0 tags** for image information
470
+ - **InteropIFD support** for format compatibility (R98/sRGB, R03/Adobe RGB, THM/thumbnail)
471
+ - **GPS IFD** with proper coordinate conversion
472
+ - All EXIF data types (BYTE, ASCII, SHORT, LONG, RATIONAL, etc.)
473
+
474
+ ### Example Usage
475
+
476
+ ```typescript
477
+ import { Image } from "jsr:@cross/image";
478
+
479
+ // Load an image
480
+ const data = await Deno.readFile("photo.jpg");
481
+ const image = await Image.decode(data);
482
+
483
+ // Set metadata
484
+ image.setMetadata({
485
+ author: "Jane Photographer",
486
+ copyright: "© 2024",
487
+ cameraMake: "Canon",
488
+ cameraModel: "EOS R5",
489
+ iso: 800,
490
+ exposureTime: 0.004, // 1/250s
491
+ fNumber: 2.8,
492
+ focalLength: 50,
493
+ });
494
+
495
+ // Set GPS coordinates
496
+ image.setPosition(40.7128, -74.0060); // NYC
497
+
498
+ // Check what metadata a format supports
499
+ const jpegSupports = Image.getSupportedMetadata("jpeg");
500
+ console.log(jpegSupports); // Includes ISO, camera info, GPS, etc.
501
+
502
+ // Save with metadata
503
+ const jpeg = await image.encode("jpeg");
504
+ await Deno.writeFile("output.jpg", jpeg);
505
+
506
+ // Metadata is preserved on reload!
507
+ const loaded = await Image.decode(jpeg);
508
+ console.log(loaded.metadata?.cameraMake); // "Canon"
509
+ console.log(loaded.getPosition()); // { latitude: 40.7128, longitude: -74.0060 }
510
+ ```
511
+
512
+ ### Extracting Metadata Without Decoding
513
+
514
+ For quickly reading metadata from images without the overhead of decoding pixel data, use
515
+ `Image.extractMetadata()`. This is particularly useful for:
516
+
517
+ - Reading EXIF data from large images or photos
518
+ - Extracting metadata from images with unsupported compression
519
+ - Building image catalogs or galleries
520
+ - Processing metadata in batch operations
521
+
522
+ ```typescript
523
+ import { Image } from "jsr:@cross/image";
524
+
525
+ // Extract metadata without decoding pixels
526
+ const data = await Deno.readFile("large-photo.jpg");
527
+ const metadata = await Image.extractMetadata(data);
528
+
529
+ console.log(metadata?.cameraMake); // "Canon"
530
+ console.log(metadata?.iso); // 800
531
+ console.log(metadata?.exposureTime); // 0.004
532
+
533
+ // Works with auto-detection
534
+ const metadata2 = await Image.extractMetadata(data); // Detects JPEG
535
+
536
+ // Or specify format explicitly
537
+ const metadata3 = await Image.extractMetadata(data, "jpeg");
538
+ ```
539
+
540
+ This method is significantly faster than full decode when you only need metadata, as it:
541
+
542
+ - Skips pixel data decompression
543
+ - Only parses metadata chunks/markers
544
+ - Returns `undefined` for unsupported formats
545
+ - Works with JPEG, PNG, WebP, TIFF, HEIC, and AVIF formats
546
+
547
+ ### Format-Specific Support
548
+
549
+ Use `Image.getSupportedMetadata(format)` to check which fields are supported:
550
+
551
+ ```typescript
552
+ Image.getSupportedMetadata("jpeg"); // Full camera metadata + GPS (21 fields)
553
+ Image.getSupportedMetadata("tiff"); // Comprehensive EXIF + GPS + InteropIFD (23+ fields)
554
+ Image.getSupportedMetadata("png"); // DateTime, GPS, DPI, basic text (9 fields)
555
+ Image.getSupportedMetadata("webp"); // Enhanced XMP + GPS (15 fields - includes camera metadata!)
556
+ Image.getSupportedMetadata("heic"); // Full camera metadata + GPS (19 fields)
557
+ Image.getSupportedMetadata("avif"); // Full camera metadata + GPS (19 fields)
558
+ ```
559
+
560
+ **Format Highlights:**
561
+
562
+ - **JPEG**: Most comprehensive EXIF support, including all camera settings and GPS
563
+ - **TIFF**: Full EXIF 3.0 support with IFD structure, InteropIFD compatibility
564
+ - **WebP**: Enhanced XMP implementation with Dublin Core, EXIF, and TIFF namespaces
565
+ - **PNG**: Basic EXIF support via eXIf chunk plus GPS coordinates
566
+ - **HEIC**: Full EXIF metadata extraction including camera settings, GPS, and image info
567
+ (runtime-dependent encoding)
568
+ - **AVIF**: Full EXIF metadata extraction including camera settings, GPS, and image info
569
+ (runtime-dependent encoding)
570
+
571
+ ## Documentation
572
+
573
+ - **[API Reference](https://cross-image.56k.guru/api/)** - Complete API documentation
574
+ - **[Format Support](https://cross-image.56k.guru/formats/)** - Supported formats and specifications
575
+ - **[Image Processing](https://cross-image.56k.guru/processing/)** - Filters, manipulation, and
576
+ color adjustments
577
+ - [Filters](https://cross-image.56k.guru/processing/filters/) - Blur, sharpen, and noise reduction
578
+ - [Manipulation](https://cross-image.56k.guru/processing/manipulation/) - Resize, crop, composite,
579
+ and draw
580
+ - [Color Adjustments](https://cross-image.56k.guru/processing/color-adjustments/) - Brightness,
581
+ contrast, saturation, and more
582
+ - **[Examples](https://cross-image.56k.guru/examples/)** - Practical examples for common tasks
583
+ - [Decoding & Encoding](https://cross-image.56k.guru/examples/decoding-encoding/) -
584
+ Format-specific examples
585
+ - [Using Filters](https://cross-image.56k.guru/examples/filters/) - Filter workflows and
586
+ techniques
587
+ - [Manipulation](https://cross-image.56k.guru/examples/manipulation/) - Resizing, cropping, and
588
+ compositing
589
+ - [Multi-Frame Images](https://cross-image.56k.guru/examples/multi-frame/) - Animated GIFs, APNGs,
590
+ and TIFFs
591
+ - **[JPEG Implementation](https://cross-image.56k.guru/implementation/jpeg-implementation/)** -
592
+ Technical details for JPEG
593
+ - **[WebP Implementation](https://cross-image.56k.guru/implementation/webp-implementation/)** -
594
+ Technical details for WebP
595
+ - **[TIFF Implementation](https://cross-image.56k.guru/implementation/tiff-implementation/)** -
596
+ Technical details for TIFF
597
+ - **[GIF Implementation](https://cross-image.56k.guru/implementation/gif-implementation/)** -
598
+ Technical details for GIF
599
+
600
+ ## Development
601
+
602
+ ```bash
603
+ deno task precommit
604
+ ```
605
+
606
+ CI validates cross-runtime compatibility (Deno, Bun, Node). See CONTRIBUTING.md for contributor
607
+ workflow details.
608
+
609
+ ## License
610
+
611
+ MIT License - see LICENSE file for details.
612
+
613
+ ## Contributing
614
+
615
+ Contributions are welcome! Please feel free to submit a Pull Request.