cross-image 0.1.2
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 -0
- package/README.md +606 -0
- package/esm/mod.d.ts +33 -0
- package/esm/mod.d.ts.map +1 -0
- package/esm/mod.js +31 -0
- package/esm/package.json +3 -0
- package/esm/src/formats/ascii.d.ts +27 -0
- package/esm/src/formats/ascii.d.ts.map +1 -0
- package/esm/src/formats/ascii.js +172 -0
- package/esm/src/formats/bmp.d.ts +19 -0
- package/esm/src/formats/bmp.d.ts.map +1 -0
- package/esm/src/formats/bmp.js +174 -0
- package/esm/src/formats/gif.d.ts +40 -0
- package/esm/src/formats/gif.d.ts.map +1 -0
- package/esm/src/formats/gif.js +385 -0
- package/esm/src/formats/jpeg.d.ts +18 -0
- package/esm/src/formats/jpeg.d.ts.map +1 -0
- package/esm/src/formats/jpeg.js +414 -0
- package/esm/src/formats/png.d.ts +33 -0
- package/esm/src/formats/png.d.ts.map +1 -0
- package/esm/src/formats/png.js +544 -0
- package/esm/src/formats/raw.d.ts +23 -0
- package/esm/src/formats/raw.d.ts.map +1 -0
- package/esm/src/formats/raw.js +98 -0
- package/esm/src/formats/tiff.d.ts +58 -0
- package/esm/src/formats/tiff.d.ts.map +1 -0
- package/esm/src/formats/tiff.js +791 -0
- package/esm/src/formats/webp.d.ts +22 -0
- package/esm/src/formats/webp.d.ts.map +1 -0
- package/esm/src/formats/webp.js +403 -0
- package/esm/src/image.d.ts +124 -0
- package/esm/src/image.d.ts.map +1 -0
- package/esm/src/image.js +320 -0
- package/esm/src/types.d.ts +167 -0
- package/esm/src/types.d.ts.map +1 -0
- package/esm/src/types.js +1 -0
- package/esm/src/utils/gif_decoder.d.ts +42 -0
- package/esm/src/utils/gif_decoder.d.ts.map +1 -0
- package/esm/src/utils/gif_decoder.js +374 -0
- package/esm/src/utils/gif_encoder.d.ts +29 -0
- package/esm/src/utils/gif_encoder.d.ts.map +1 -0
- package/esm/src/utils/gif_encoder.js +226 -0
- package/esm/src/utils/jpeg_decoder.d.ts +39 -0
- package/esm/src/utils/jpeg_decoder.d.ts.map +1 -0
- package/esm/src/utils/jpeg_decoder.js +580 -0
- package/esm/src/utils/jpeg_encoder.d.ts +33 -0
- package/esm/src/utils/jpeg_encoder.d.ts.map +1 -0
- package/esm/src/utils/jpeg_encoder.js +1017 -0
- package/esm/src/utils/lzw.d.ts +43 -0
- package/esm/src/utils/lzw.d.ts.map +1 -0
- package/esm/src/utils/lzw.js +309 -0
- package/esm/src/utils/resize.d.ts +9 -0
- package/esm/src/utils/resize.d.ts.map +1 -0
- package/esm/src/utils/resize.js +52 -0
- package/esm/src/utils/tiff_lzw.d.ts +44 -0
- package/esm/src/utils/tiff_lzw.d.ts.map +1 -0
- package/esm/src/utils/tiff_lzw.js +306 -0
- package/esm/src/utils/webp_decoder.d.ts +39 -0
- package/esm/src/utils/webp_decoder.d.ts.map +1 -0
- package/esm/src/utils/webp_decoder.js +493 -0
- package/esm/src/utils/webp_encoder.d.ts +72 -0
- package/esm/src/utils/webp_encoder.d.ts.map +1 -0
- package/esm/src/utils/webp_encoder.js +627 -0
- package/package.json +41 -0
- package/script/mod.d.ts +33 -0
- package/script/mod.d.ts.map +1 -0
- package/script/mod.js +43 -0
- package/script/package.json +3 -0
- package/script/src/formats/ascii.d.ts +27 -0
- package/script/src/formats/ascii.d.ts.map +1 -0
- package/script/src/formats/ascii.js +176 -0
- package/script/src/formats/bmp.d.ts +19 -0
- package/script/src/formats/bmp.d.ts.map +1 -0
- package/script/src/formats/bmp.js +178 -0
- package/script/src/formats/gif.d.ts +40 -0
- package/script/src/formats/gif.d.ts.map +1 -0
- package/script/src/formats/gif.js +389 -0
- package/script/src/formats/jpeg.d.ts +18 -0
- package/script/src/formats/jpeg.d.ts.map +1 -0
- package/script/src/formats/jpeg.js +451 -0
- package/script/src/formats/png.d.ts +33 -0
- package/script/src/formats/png.d.ts.map +1 -0
- package/script/src/formats/png.js +548 -0
- package/script/src/formats/raw.d.ts +23 -0
- package/script/src/formats/raw.d.ts.map +1 -0
- package/script/src/formats/raw.js +102 -0
- package/script/src/formats/tiff.d.ts +58 -0
- package/script/src/formats/tiff.d.ts.map +1 -0
- package/script/src/formats/tiff.js +795 -0
- package/script/src/formats/webp.d.ts +22 -0
- package/script/src/formats/webp.d.ts.map +1 -0
- package/script/src/formats/webp.js +440 -0
- package/script/src/image.d.ts +124 -0
- package/script/src/image.d.ts.map +1 -0
- package/script/src/image.js +324 -0
- package/script/src/types.d.ts +167 -0
- package/script/src/types.d.ts.map +1 -0
- package/script/src/types.js +2 -0
- package/script/src/utils/gif_decoder.d.ts +42 -0
- package/script/src/utils/gif_decoder.d.ts.map +1 -0
- package/script/src/utils/gif_decoder.js +378 -0
- package/script/src/utils/gif_encoder.d.ts +29 -0
- package/script/src/utils/gif_encoder.d.ts.map +1 -0
- package/script/src/utils/gif_encoder.js +230 -0
- package/script/src/utils/jpeg_decoder.d.ts +39 -0
- package/script/src/utils/jpeg_decoder.d.ts.map +1 -0
- package/script/src/utils/jpeg_decoder.js +584 -0
- package/script/src/utils/jpeg_encoder.d.ts +33 -0
- package/script/src/utils/jpeg_encoder.d.ts.map +1 -0
- package/script/src/utils/jpeg_encoder.js +1021 -0
- package/script/src/utils/lzw.d.ts +43 -0
- package/script/src/utils/lzw.d.ts.map +1 -0
- package/script/src/utils/lzw.js +314 -0
- package/script/src/utils/resize.d.ts +9 -0
- package/script/src/utils/resize.d.ts.map +1 -0
- package/script/src/utils/resize.js +56 -0
- package/script/src/utils/tiff_lzw.d.ts +44 -0
- package/script/src/utils/tiff_lzw.d.ts.map +1 -0
- package/script/src/utils/tiff_lzw.js +311 -0
- package/script/src/utils/webp_decoder.d.ts +39 -0
- package/script/src/utils/webp_decoder.d.ts.map +1 -0
- package/script/src/utils/webp_decoder.js +497 -0
- package/script/src/utils/webp_encoder.d.ts +72 -0
- package/script/src/utils/webp_encoder.d.ts.map +1 -0
- package/script/src/utils/webp_encoder.js +631 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,YAAY,EACV,YAAY,EACZ,aAAa,EACb,SAAS,EACT,WAAW,EACX,UAAU,EACV,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC"}
|
package/script/mod.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module @cross/image
|
|
4
|
+
*
|
|
5
|
+
* A pure JavaScript, dependency-free, cross-runtime image processing library.
|
|
6
|
+
* Supports reading, resizing, and saving common image formats (PNG, JPEG, WebP, GIF, TIFF, BMP, RAW).
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { Image } from "@cross/image";
|
|
11
|
+
*
|
|
12
|
+
* // Read an image
|
|
13
|
+
* const data = await Deno.readFile("input.png");
|
|
14
|
+
* const image = await Image.read(data);
|
|
15
|
+
*
|
|
16
|
+
* // Resize it
|
|
17
|
+
* image.resize({ width: 200, height: 200 });
|
|
18
|
+
*
|
|
19
|
+
* // Save as different format
|
|
20
|
+
* const output = await image.save("jpeg");
|
|
21
|
+
* await Deno.writeFile("output.jpg", output);
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.ASCIIFormat = exports.RAWFormat = exports.BMPFormat = exports.TIFFFormat = exports.GIFFormat = exports.WebPFormat = exports.JPEGFormat = exports.PNGFormat = exports.Image = void 0;
|
|
26
|
+
var image_js_1 = require("./src/image.js");
|
|
27
|
+
Object.defineProperty(exports, "Image", { enumerable: true, get: function () { return image_js_1.Image; } });
|
|
28
|
+
var png_js_1 = require("./src/formats/png.js");
|
|
29
|
+
Object.defineProperty(exports, "PNGFormat", { enumerable: true, get: function () { return png_js_1.PNGFormat; } });
|
|
30
|
+
var jpeg_js_1 = require("./src/formats/jpeg.js");
|
|
31
|
+
Object.defineProperty(exports, "JPEGFormat", { enumerable: true, get: function () { return jpeg_js_1.JPEGFormat; } });
|
|
32
|
+
var webp_js_1 = require("./src/formats/webp.js");
|
|
33
|
+
Object.defineProperty(exports, "WebPFormat", { enumerable: true, get: function () { return webp_js_1.WebPFormat; } });
|
|
34
|
+
var gif_js_1 = require("./src/formats/gif.js");
|
|
35
|
+
Object.defineProperty(exports, "GIFFormat", { enumerable: true, get: function () { return gif_js_1.GIFFormat; } });
|
|
36
|
+
var tiff_js_1 = require("./src/formats/tiff.js");
|
|
37
|
+
Object.defineProperty(exports, "TIFFFormat", { enumerable: true, get: function () { return tiff_js_1.TIFFFormat; } });
|
|
38
|
+
var bmp_js_1 = require("./src/formats/bmp.js");
|
|
39
|
+
Object.defineProperty(exports, "BMPFormat", { enumerable: true, get: function () { return bmp_js_1.BMPFormat; } });
|
|
40
|
+
var raw_js_1 = require("./src/formats/raw.js");
|
|
41
|
+
Object.defineProperty(exports, "RAWFormat", { enumerable: true, get: function () { return raw_js_1.RAWFormat; } });
|
|
42
|
+
var ascii_js_1 = require("./src/formats/ascii.js");
|
|
43
|
+
Object.defineProperty(exports, "ASCIIFormat", { enumerable: true, get: function () { return ascii_js_1.ASCIIFormat; } });
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ASCIIOptions, ImageData, ImageFormat } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* ASCII format handler
|
|
4
|
+
* Converts images to ASCII art text representation
|
|
5
|
+
*
|
|
6
|
+
* Format structure:
|
|
7
|
+
* - Magic bytes (6 bytes): "ASCII\n" (0x41 0x53 0x43 0x49 0x49 0x0A)
|
|
8
|
+
* - Options line: "width:W charset:C aspectRatio:A invert:I\n"
|
|
9
|
+
* - ASCII art text (UTF-8 encoded)
|
|
10
|
+
*
|
|
11
|
+
* Note: This format is primarily for encoding (image to ASCII).
|
|
12
|
+
* Decoding reconstructs a basic grayscale approximation.
|
|
13
|
+
*/
|
|
14
|
+
export declare class ASCIIFormat implements ImageFormat {
|
|
15
|
+
readonly name = "ascii";
|
|
16
|
+
readonly mimeType = "text/plain";
|
|
17
|
+
private readonly MAGIC_BYTES;
|
|
18
|
+
private readonly CHARSETS;
|
|
19
|
+
canDecode(data: Uint8Array): boolean;
|
|
20
|
+
decode(data: Uint8Array): Promise<ImageData>;
|
|
21
|
+
encode(imageData: ImageData, options?: ASCIIOptions): Promise<Uint8Array>;
|
|
22
|
+
/**
|
|
23
|
+
* Parse options from the options line
|
|
24
|
+
*/
|
|
25
|
+
private parseOptions;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=ascii.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ascii.d.ts","sourceRoot":"","sources":["../../../src/src/formats/ascii.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAExE;;;;;;;;;;;GAWG;AACH,qBAAa,WAAY,YAAW,WAAW;IAC7C,QAAQ,CAAC,IAAI,WAAW;IACxB,QAAQ,CAAC,QAAQ,gBAAgB;IAEjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAOzB;IAGH,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAOvB;IAEF,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO;IAcpC,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;IA+D5C,MAAM,CACJ,SAAS,EAAE,SAAS,EACpB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,UAAU,CAAC;IAoDtB;;OAEG;IACH,OAAO,CAAC,YAAY;CA6BrB"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ASCIIFormat = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* ASCII format handler
|
|
6
|
+
* Converts images to ASCII art text representation
|
|
7
|
+
*
|
|
8
|
+
* Format structure:
|
|
9
|
+
* - Magic bytes (6 bytes): "ASCII\n" (0x41 0x53 0x43 0x49 0x49 0x0A)
|
|
10
|
+
* - Options line: "width:W charset:C aspectRatio:A invert:I\n"
|
|
11
|
+
* - ASCII art text (UTF-8 encoded)
|
|
12
|
+
*
|
|
13
|
+
* Note: This format is primarily for encoding (image to ASCII).
|
|
14
|
+
* Decoding reconstructs a basic grayscale approximation.
|
|
15
|
+
*/
|
|
16
|
+
class ASCIIFormat {
|
|
17
|
+
constructor() {
|
|
18
|
+
Object.defineProperty(this, "name", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
configurable: true,
|
|
21
|
+
writable: true,
|
|
22
|
+
value: "ascii"
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(this, "mimeType", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
configurable: true,
|
|
27
|
+
writable: true,
|
|
28
|
+
value: "text/plain"
|
|
29
|
+
});
|
|
30
|
+
Object.defineProperty(this, "MAGIC_BYTES", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
configurable: true,
|
|
33
|
+
writable: true,
|
|
34
|
+
value: new Uint8Array([
|
|
35
|
+
0x41,
|
|
36
|
+
0x53,
|
|
37
|
+
0x43,
|
|
38
|
+
0x49,
|
|
39
|
+
0x49,
|
|
40
|
+
0x0A,
|
|
41
|
+
])
|
|
42
|
+
}); // "ASCII\n"
|
|
43
|
+
// Character sets ordered from darkest to lightest
|
|
44
|
+
Object.defineProperty(this, "CHARSETS", {
|
|
45
|
+
enumerable: true,
|
|
46
|
+
configurable: true,
|
|
47
|
+
writable: true,
|
|
48
|
+
value: {
|
|
49
|
+
simple: " .:-=+*#%@",
|
|
50
|
+
extended: " .'`^\",:;Il!i><~+_-?][}{1)(|\\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$",
|
|
51
|
+
blocks: " ░▒▓█",
|
|
52
|
+
detailed: " .`-_':,;^=+/\"|)\\<>)iv%xclrs{*}I?!][1taeo7zjLunT#JCwfy325Fh9kP6pqdbVOlS8X$KHEA4D3RZG0MQNWU&%B@",
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
canDecode(data) {
|
|
57
|
+
// Check if data starts with "ASCII\n"
|
|
58
|
+
if (data.length < 6) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return data[0] === this.MAGIC_BYTES[0] &&
|
|
62
|
+
data[1] === this.MAGIC_BYTES[1] &&
|
|
63
|
+
data[2] === this.MAGIC_BYTES[2] &&
|
|
64
|
+
data[3] === this.MAGIC_BYTES[3] &&
|
|
65
|
+
data[4] === this.MAGIC_BYTES[4] &&
|
|
66
|
+
data[5] === this.MAGIC_BYTES[5];
|
|
67
|
+
}
|
|
68
|
+
decode(data) {
|
|
69
|
+
if (!this.canDecode(data)) {
|
|
70
|
+
throw new Error("Invalid ASCII art signature");
|
|
71
|
+
}
|
|
72
|
+
// Convert to string
|
|
73
|
+
const text = new TextDecoder().decode(data);
|
|
74
|
+
const lines = text.split("\n");
|
|
75
|
+
if (lines.length < 2) {
|
|
76
|
+
throw new Error("Invalid ASCII art format");
|
|
77
|
+
}
|
|
78
|
+
// Parse options from second line
|
|
79
|
+
const optionsLine = lines[1];
|
|
80
|
+
const options = this.parseOptions(optionsLine);
|
|
81
|
+
// Get ASCII art content (skip magic line and options line)
|
|
82
|
+
const artLines = lines.slice(2).filter((line) => line.length > 0);
|
|
83
|
+
if (artLines.length === 0) {
|
|
84
|
+
throw new Error("No ASCII art content found");
|
|
85
|
+
}
|
|
86
|
+
// Calculate dimensions
|
|
87
|
+
const height = artLines.length;
|
|
88
|
+
const width = Math.max(...artLines.map((line) => line.length));
|
|
89
|
+
// Convert ASCII art back to image data
|
|
90
|
+
const imageData = new Uint8Array(width * height * 4);
|
|
91
|
+
const charset = this.CHARSETS[options.charset] || this.CHARSETS.simple;
|
|
92
|
+
for (let y = 0; y < height; y++) {
|
|
93
|
+
const line = artLines[y];
|
|
94
|
+
for (let x = 0; x < width; x++) {
|
|
95
|
+
const char = x < line.length ? line[x] : " ";
|
|
96
|
+
const charIndex = charset.indexOf(char);
|
|
97
|
+
// Calculate brightness (0-255)
|
|
98
|
+
let brightness;
|
|
99
|
+
if (charIndex === -1) {
|
|
100
|
+
brightness = 0; // Unknown character = black
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
brightness = Math.floor((charIndex / (charset.length - 1)) * 255);
|
|
104
|
+
}
|
|
105
|
+
if (options.invert) {
|
|
106
|
+
brightness = 255 - brightness;
|
|
107
|
+
}
|
|
108
|
+
const offset = (y * width + x) * 4;
|
|
109
|
+
imageData[offset] = brightness; // R
|
|
110
|
+
imageData[offset + 1] = brightness; // G
|
|
111
|
+
imageData[offset + 2] = brightness; // B
|
|
112
|
+
imageData[offset + 3] = 255; // A
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return Promise.resolve({ width, height, data: imageData });
|
|
116
|
+
}
|
|
117
|
+
encode(imageData, options = {}) {
|
|
118
|
+
const { width: targetWidth = 80, charset = "simple", aspectRatio = 0.5, invert = false, } = options;
|
|
119
|
+
// Get character set
|
|
120
|
+
const chars = this.CHARSETS[charset] || this.CHARSETS.simple;
|
|
121
|
+
// Calculate target height based on aspect ratio
|
|
122
|
+
const { width: imgWidth, height: imgHeight, data } = imageData;
|
|
123
|
+
const targetHeight = Math.floor((imgHeight / imgWidth) * targetWidth * aspectRatio);
|
|
124
|
+
// Build ASCII art
|
|
125
|
+
const lines = [];
|
|
126
|
+
for (let y = 0; y < targetHeight; y++) {
|
|
127
|
+
let line = "";
|
|
128
|
+
for (let x = 0; x < targetWidth; x++) {
|
|
129
|
+
// Map to source pixel
|
|
130
|
+
const srcX = Math.floor((x / targetWidth) * imgWidth);
|
|
131
|
+
const srcY = Math.floor((y / targetHeight) * imgHeight);
|
|
132
|
+
const offset = (srcY * imgWidth + srcX) * 4;
|
|
133
|
+
// Calculate grayscale value
|
|
134
|
+
const r = data[offset];
|
|
135
|
+
const g = data[offset + 1];
|
|
136
|
+
const b = data[offset + 2];
|
|
137
|
+
const gray = Math.floor(0.299 * r + 0.587 * g + 0.114 * b);
|
|
138
|
+
// Map to character
|
|
139
|
+
const brightness = invert ? 255 - gray : gray;
|
|
140
|
+
const charIndex = Math.floor((brightness / 255) * (chars.length - 1));
|
|
141
|
+
line += chars[charIndex];
|
|
142
|
+
}
|
|
143
|
+
lines.push(line);
|
|
144
|
+
}
|
|
145
|
+
// Create output with magic bytes and options
|
|
146
|
+
const optionsLine = `width:${targetWidth} charset:${charset} aspectRatio:${aspectRatio} invert:${invert}`;
|
|
147
|
+
const content = `ASCII\n${optionsLine}\n${lines.join("\n")}`;
|
|
148
|
+
return Promise.resolve(new TextEncoder().encode(content));
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Parse options from the options line
|
|
152
|
+
*/
|
|
153
|
+
parseOptions(line) {
|
|
154
|
+
const defaults = {
|
|
155
|
+
charset: "simple",
|
|
156
|
+
invert: false,
|
|
157
|
+
};
|
|
158
|
+
if (!line)
|
|
159
|
+
return defaults;
|
|
160
|
+
const parts = line.split(" ");
|
|
161
|
+
const options = { ...defaults };
|
|
162
|
+
for (const part of parts) {
|
|
163
|
+
const [key, value] = part.split(":");
|
|
164
|
+
if (key === "charset" && value) {
|
|
165
|
+
if (["simple", "extended", "blocks", "detailed"].includes(value)) {
|
|
166
|
+
options.charset = value;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else if (key === "invert" && value) {
|
|
170
|
+
options.invert = value === "true";
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return options;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
exports.ASCIIFormat = ASCIIFormat;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ImageData, ImageFormat } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* BMP format handler
|
|
4
|
+
* Implements a pure JavaScript BMP decoder and encoder
|
|
5
|
+
*/
|
|
6
|
+
export declare class BMPFormat implements ImageFormat {
|
|
7
|
+
readonly name = "bmp";
|
|
8
|
+
readonly mimeType = "image/bmp";
|
|
9
|
+
canDecode(data: Uint8Array): boolean;
|
|
10
|
+
decode(data: Uint8Array): Promise<ImageData>;
|
|
11
|
+
encode(imageData: ImageData): Promise<Uint8Array>;
|
|
12
|
+
private readUint16LE;
|
|
13
|
+
private readUint32LE;
|
|
14
|
+
private readInt32LE;
|
|
15
|
+
private writeUint16LE;
|
|
16
|
+
private writeUint32LE;
|
|
17
|
+
private writeInt32LE;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=bmp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bmp.d.ts","sourceRoot":"","sources":["../../../src/src/formats/bmp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAiB,MAAM,aAAa,CAAC;AAKzE;;;GAGG;AACH,qBAAa,SAAU,YAAW,WAAW;IAC3C,QAAQ,CAAC,IAAI,SAAS;IACtB,QAAQ,CAAC,QAAQ,eAAe;IAEhC,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO;IAMpC,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;IAwF5C,MAAM,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;IA8DjD,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,YAAY;CAGrB"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BMPFormat = void 0;
|
|
4
|
+
// Constants for unit conversions
|
|
5
|
+
const INCHES_PER_METER = 39.3701;
|
|
6
|
+
/**
|
|
7
|
+
* BMP format handler
|
|
8
|
+
* Implements a pure JavaScript BMP decoder and encoder
|
|
9
|
+
*/
|
|
10
|
+
class BMPFormat {
|
|
11
|
+
constructor() {
|
|
12
|
+
Object.defineProperty(this, "name", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
configurable: true,
|
|
15
|
+
writable: true,
|
|
16
|
+
value: "bmp"
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(this, "mimeType", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
configurable: true,
|
|
21
|
+
writable: true,
|
|
22
|
+
value: "image/bmp"
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
canDecode(data) {
|
|
26
|
+
// BMP signature: 'BM' (0x42 0x4D)
|
|
27
|
+
return data.length >= 2 &&
|
|
28
|
+
data[0] === 0x42 && data[1] === 0x4d;
|
|
29
|
+
}
|
|
30
|
+
decode(data) {
|
|
31
|
+
if (!this.canDecode(data)) {
|
|
32
|
+
throw new Error("Invalid BMP signature");
|
|
33
|
+
}
|
|
34
|
+
// Read BMP file header (14 bytes)
|
|
35
|
+
const _fileSize = this.readUint32LE(data, 2);
|
|
36
|
+
const dataOffset = this.readUint32LE(data, 10);
|
|
37
|
+
// Read DIB header (at least 40 bytes for BITMAPINFOHEADER)
|
|
38
|
+
const dibHeaderSize = this.readUint32LE(data, 14);
|
|
39
|
+
let width;
|
|
40
|
+
let height;
|
|
41
|
+
let bitDepth;
|
|
42
|
+
let compression;
|
|
43
|
+
const metadata = {};
|
|
44
|
+
if (dibHeaderSize >= 40) {
|
|
45
|
+
// BITMAPINFOHEADER or later
|
|
46
|
+
width = this.readInt32LE(data, 18);
|
|
47
|
+
height = this.readInt32LE(data, 22);
|
|
48
|
+
bitDepth = this.readUint16LE(data, 28);
|
|
49
|
+
compression = this.readUint32LE(data, 30);
|
|
50
|
+
// Read DPI information (pixels per meter)
|
|
51
|
+
const xPixelsPerMeter = this.readInt32LE(data, 38);
|
|
52
|
+
const yPixelsPerMeter = this.readInt32LE(data, 42);
|
|
53
|
+
if (xPixelsPerMeter > 0 && yPixelsPerMeter > 0) {
|
|
54
|
+
// Convert pixels per meter to DPI
|
|
55
|
+
metadata.dpiX = Math.round(xPixelsPerMeter / INCHES_PER_METER);
|
|
56
|
+
metadata.dpiY = Math.round(yPixelsPerMeter / INCHES_PER_METER);
|
|
57
|
+
metadata.physicalWidth = Math.abs(width) / metadata.dpiX;
|
|
58
|
+
metadata.physicalHeight = Math.abs(height) / metadata.dpiY;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
throw new Error("Unsupported BMP header format");
|
|
63
|
+
}
|
|
64
|
+
// Handle negative height (top-down bitmap)
|
|
65
|
+
const isTopDown = height < 0;
|
|
66
|
+
const absHeight = Math.abs(height);
|
|
67
|
+
// Only support uncompressed BMPs for now
|
|
68
|
+
if (compression !== 0) {
|
|
69
|
+
throw new Error(`Compressed BMP not supported (compression type: ${compression})`);
|
|
70
|
+
}
|
|
71
|
+
// Only support 24-bit and 32-bit BMPs
|
|
72
|
+
if (bitDepth !== 24 && bitDepth !== 32) {
|
|
73
|
+
throw new Error(`Unsupported bit depth: ${bitDepth}. Only 24 and 32-bit BMPs are supported.`);
|
|
74
|
+
}
|
|
75
|
+
// Calculate row size (must be multiple of 4 bytes)
|
|
76
|
+
const bytesPerPixel = bitDepth / 8;
|
|
77
|
+
const rowSize = Math.floor((bitDepth * width + 31) / 32) * 4;
|
|
78
|
+
// Read pixel data
|
|
79
|
+
const rgba = new Uint8Array(width * absHeight * 4);
|
|
80
|
+
for (let y = 0; y < absHeight; y++) {
|
|
81
|
+
const rowIndex = isTopDown ? y : (absHeight - 1 - y); // BMP stores bottom-to-top by default
|
|
82
|
+
const rowOffset = dataOffset + y * rowSize;
|
|
83
|
+
for (let x = 0; x < width; x++) {
|
|
84
|
+
const pixelOffset = rowOffset + x * bytesPerPixel;
|
|
85
|
+
const outIndex = (rowIndex * width + x) * 4;
|
|
86
|
+
// BMP stores pixels as BGR(A)
|
|
87
|
+
rgba[outIndex] = data[pixelOffset + 2]; // R
|
|
88
|
+
rgba[outIndex + 1] = data[pixelOffset + 1]; // G
|
|
89
|
+
rgba[outIndex + 2] = data[pixelOffset]; // B
|
|
90
|
+
rgba[outIndex + 3] = bitDepth === 32 ? data[pixelOffset + 3] : 255; // A
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return Promise.resolve({
|
|
94
|
+
width,
|
|
95
|
+
height: absHeight,
|
|
96
|
+
data: rgba,
|
|
97
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
encode(imageData) {
|
|
101
|
+
const { width, height, data, metadata } = imageData;
|
|
102
|
+
// Calculate sizes
|
|
103
|
+
const bytesPerPixel = 4; // We'll encode as 32-bit RGBA
|
|
104
|
+
const rowSize = Math.floor((32 * width + 31) / 32) * 4;
|
|
105
|
+
const pixelDataSize = rowSize * height;
|
|
106
|
+
const fileSize = 14 + 40 + pixelDataSize; // File header + DIB header + pixel data
|
|
107
|
+
const result = new Uint8Array(fileSize);
|
|
108
|
+
// Calculate DPI values
|
|
109
|
+
let xPixelsPerMeter = 2835; // Default 72 DPI
|
|
110
|
+
let yPixelsPerMeter = 2835;
|
|
111
|
+
if (metadata?.dpiX && metadata.dpiX > 0) {
|
|
112
|
+
xPixelsPerMeter = Math.round(metadata.dpiX * INCHES_PER_METER);
|
|
113
|
+
}
|
|
114
|
+
if (metadata?.dpiY && metadata.dpiY > 0) {
|
|
115
|
+
yPixelsPerMeter = Math.round(metadata.dpiY * INCHES_PER_METER);
|
|
116
|
+
}
|
|
117
|
+
// BMP File Header (14 bytes)
|
|
118
|
+
result[0] = 0x42; // 'B'
|
|
119
|
+
result[1] = 0x4d; // 'M'
|
|
120
|
+
this.writeUint32LE(result, 2, fileSize); // File size
|
|
121
|
+
this.writeUint32LE(result, 6, 0); // Reserved
|
|
122
|
+
this.writeUint32LE(result, 10, 54); // Offset to pixel data (14 + 40)
|
|
123
|
+
// DIB Header (BITMAPINFOHEADER - 40 bytes)
|
|
124
|
+
this.writeUint32LE(result, 14, 40); // DIB header size
|
|
125
|
+
this.writeInt32LE(result, 18, width); // Width
|
|
126
|
+
this.writeInt32LE(result, 22, height); // Height (positive = bottom-up)
|
|
127
|
+
this.writeUint16LE(result, 26, 1); // Planes
|
|
128
|
+
this.writeUint16LE(result, 28, 32); // Bits per pixel
|
|
129
|
+
this.writeUint32LE(result, 30, 0); // Compression (0 = uncompressed)
|
|
130
|
+
this.writeUint32LE(result, 34, pixelDataSize); // Image size
|
|
131
|
+
this.writeInt32LE(result, 38, xPixelsPerMeter); // X pixels per meter
|
|
132
|
+
this.writeInt32LE(result, 42, yPixelsPerMeter); // Y pixels per meter
|
|
133
|
+
this.writeUint32LE(result, 46, 0); // Colors in palette
|
|
134
|
+
this.writeUint32LE(result, 50, 0); // Important colors
|
|
135
|
+
// Write pixel data (bottom-to-top, BGR(A) format)
|
|
136
|
+
let offset = 54;
|
|
137
|
+
for (let y = height - 1; y >= 0; y--) {
|
|
138
|
+
for (let x = 0; x < width; x++) {
|
|
139
|
+
const srcIndex = (y * width + x) * 4;
|
|
140
|
+
result[offset++] = data[srcIndex + 2]; // B
|
|
141
|
+
result[offset++] = data[srcIndex + 1]; // G
|
|
142
|
+
result[offset++] = data[srcIndex]; // R
|
|
143
|
+
result[offset++] = data[srcIndex + 3]; // A
|
|
144
|
+
}
|
|
145
|
+
// Add padding to make row size multiple of 4
|
|
146
|
+
const padding = rowSize - width * bytesPerPixel;
|
|
147
|
+
for (let p = 0; p < padding; p++) {
|
|
148
|
+
result[offset++] = 0;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return Promise.resolve(result);
|
|
152
|
+
}
|
|
153
|
+
readUint16LE(data, offset) {
|
|
154
|
+
return data[offset] | (data[offset + 1] << 8);
|
|
155
|
+
}
|
|
156
|
+
readUint32LE(data, offset) {
|
|
157
|
+
return data[offset] | (data[offset + 1] << 8) |
|
|
158
|
+
(data[offset + 2] << 16) | (data[offset + 3] << 24);
|
|
159
|
+
}
|
|
160
|
+
readInt32LE(data, offset) {
|
|
161
|
+
const value = this.readUint32LE(data, offset);
|
|
162
|
+
return value > 0x7fffffff ? value - 0x100000000 : value;
|
|
163
|
+
}
|
|
164
|
+
writeUint16LE(data, offset, value) {
|
|
165
|
+
data[offset] = value & 0xff;
|
|
166
|
+
data[offset + 1] = (value >>> 8) & 0xff;
|
|
167
|
+
}
|
|
168
|
+
writeUint32LE(data, offset, value) {
|
|
169
|
+
data[offset] = value & 0xff;
|
|
170
|
+
data[offset + 1] = (value >>> 8) & 0xff;
|
|
171
|
+
data[offset + 2] = (value >>> 16) & 0xff;
|
|
172
|
+
data[offset + 3] = (value >>> 24) & 0xff;
|
|
173
|
+
}
|
|
174
|
+
writeInt32LE(data, offset, value) {
|
|
175
|
+
this.writeUint32LE(data, offset, value < 0 ? value + 0x100000000 : value);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
exports.BMPFormat = BMPFormat;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ImageData, ImageFormat, MultiFrameImageData } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* GIF format handler
|
|
4
|
+
* Now includes pure-JS implementation with custom LZW compression/decompression
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - LZW compression/decompression
|
|
8
|
+
* - Color quantization and palette generation for encoding
|
|
9
|
+
* - Interlacing support
|
|
10
|
+
* - Transparency support
|
|
11
|
+
* - Multi-frame animation support (decoding and encoding)
|
|
12
|
+
* - Falls back to runtime APIs when pure-JS fails
|
|
13
|
+
*/
|
|
14
|
+
export declare class GIFFormat implements ImageFormat {
|
|
15
|
+
readonly name = "gif";
|
|
16
|
+
readonly mimeType = "image/gif";
|
|
17
|
+
supportsMultipleFrames(): boolean;
|
|
18
|
+
canDecode(data: Uint8Array): boolean;
|
|
19
|
+
decode(data: Uint8Array): Promise<ImageData>;
|
|
20
|
+
private extractMetadata;
|
|
21
|
+
encode(imageData: ImageData): Promise<Uint8Array>;
|
|
22
|
+
/**
|
|
23
|
+
* Decode all frames from an animated GIF
|
|
24
|
+
*/
|
|
25
|
+
decodeFrames(data: Uint8Array): Promise<MultiFrameImageData>;
|
|
26
|
+
/**
|
|
27
|
+
* Encode multi-frame image data to animated GIF
|
|
28
|
+
* Note: Currently not implemented, will encode only first frame
|
|
29
|
+
*/
|
|
30
|
+
encodeFrames(imageData: MultiFrameImageData, _options?: unknown): Promise<Uint8Array>;
|
|
31
|
+
private mapDisposalMethod;
|
|
32
|
+
private readUint16LE;
|
|
33
|
+
private decodeUsingRuntime;
|
|
34
|
+
private readDataSubBlocks;
|
|
35
|
+
private parseComment;
|
|
36
|
+
private parseXMP;
|
|
37
|
+
private injectMetadata;
|
|
38
|
+
private createCommentText;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=gif.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gif.d.ts","sourceRoot":"","sources":["../../../src/src/formats/gif.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,WAAW,EAEX,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAIrB;;;;;;;;;;;GAWG;AACH,qBAAa,SAAU,YAAW,WAAW;IAC3C,QAAQ,CAAC,IAAI,SAAS;IACtB,QAAQ,CAAC,QAAQ,eAAe;IAEhC,sBAAsB,IAAI,OAAO;IAIjC,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO;IAS9B,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;IA2ClD,OAAO,CAAC,eAAe;IAwDjB,MAAM,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;IAyDvD;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAkC5D;;;OAGG;IACH,YAAY,CACV,SAAS,EAAE,mBAAmB,EAC9B,QAAQ,CAAC,EAAE,OAAO,GACjB,OAAO,CAAC,UAAU,CAAC;IAkBtB,OAAO,CAAC,iBAAiB;IAgBzB,OAAO,CAAC,YAAY;YAIN,kBAAkB;IAqChC,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,YAAY;IAmCpB,OAAO,CAAC,QAAQ;IAuBhB,OAAO,CAAC,cAAc;IAqDtB,OAAO,CAAC,iBAAiB;CAY1B"}
|