cross-image 0.4.0 → 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.
- package/README.md +109 -1
- package/esm/mod.d.ts +3 -1
- package/esm/mod.js +2 -0
- package/esm/src/formats/apng.d.ts +5 -3
- package/esm/src/formats/apng.js +11 -4
- package/esm/src/formats/avif.d.ts +2 -2
- package/esm/src/formats/avif.js +11 -1
- package/esm/src/formats/gif.d.ts +3 -3
- package/esm/src/formats/gif.js +4 -4
- package/esm/src/formats/heic.d.ts +2 -2
- package/esm/src/formats/heic.js +11 -1
- package/esm/src/formats/png.d.ts +3 -2
- package/esm/src/formats/png.js +8 -2
- package/esm/src/formats/png_base.d.ts +42 -1
- package/esm/src/formats/png_base.js +198 -5
- package/esm/src/formats/tiff.js +76 -6
- package/esm/src/image.d.ts +15 -0
- package/esm/src/image.js +29 -1
- package/esm/src/types.d.ts +66 -0
- package/esm/src/utils/base64.d.ts +32 -0
- package/esm/src/utils/base64.js +173 -0
- package/esm/src/utils/gif_encoder.d.ts +3 -1
- package/esm/src/utils/gif_encoder.js +4 -2
- package/esm/src/utils/image_processing.d.ts +31 -0
- package/esm/src/utils/image_processing.js +88 -0
- package/package.json +1 -1
- package/script/mod.d.ts +3 -1
- package/script/mod.js +11 -1
- package/script/src/formats/apng.d.ts +5 -3
- package/script/src/formats/apng.js +11 -4
- package/script/src/formats/avif.d.ts +2 -2
- package/script/src/formats/avif.js +11 -1
- package/script/src/formats/gif.d.ts +3 -3
- package/script/src/formats/gif.js +4 -4
- package/script/src/formats/heic.d.ts +2 -2
- package/script/src/formats/heic.js +11 -1
- package/script/src/formats/png.d.ts +3 -2
- package/script/src/formats/png.js +8 -2
- package/script/src/formats/png_base.d.ts +42 -1
- package/script/src/formats/png_base.js +198 -5
- package/script/src/formats/tiff.js +76 -6
- package/script/src/image.d.ts +15 -0
- package/script/src/image.js +28 -0
- package/script/src/types.d.ts +66 -0
- package/script/src/utils/base64.d.ts +32 -0
- package/script/src/utils/base64.js +179 -0
- package/script/src/utils/gif_encoder.d.ts +3 -1
- package/script/src/utils/gif_encoder.js +4 -2
- package/script/src/utils/image_processing.d.ts +31 -0
- package/script/src/utils/image_processing.js +92 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base64 utilities.
|
|
3
|
+
*
|
|
4
|
+
* Designed to work across Deno, Node.js, and Bun without external dependencies.
|
|
5
|
+
*/
|
|
6
|
+
const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
7
|
+
const DECODE_TABLE = (() => {
|
|
8
|
+
const table = new Uint8Array(256);
|
|
9
|
+
table.fill(0xff);
|
|
10
|
+
for (let i = 0; i < BASE64_ALPHABET.length; i++) {
|
|
11
|
+
table[BASE64_ALPHABET.charCodeAt(i)] = i;
|
|
12
|
+
}
|
|
13
|
+
return table;
|
|
14
|
+
})();
|
|
15
|
+
function stripWhitespace(input) {
|
|
16
|
+
// Avoid regex to keep this fast and allocation-light.
|
|
17
|
+
let out = "";
|
|
18
|
+
for (let i = 0; i < input.length; i++) {
|
|
19
|
+
const code = input.charCodeAt(i);
|
|
20
|
+
// Space, tab, CR, LF
|
|
21
|
+
if (code === 0x20 || code === 0x09 || code === 0x0d || code === 0x0a)
|
|
22
|
+
continue;
|
|
23
|
+
out += input[i];
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
function normalizeBase64(input) {
|
|
28
|
+
// Accept URL-safe alphabet in decode paths.
|
|
29
|
+
let s = stripWhitespace(input.trim());
|
|
30
|
+
if (s.length === 0)
|
|
31
|
+
return s;
|
|
32
|
+
// Map base64url to base64.
|
|
33
|
+
if (s.includes("-") || s.includes("_")) {
|
|
34
|
+
s = s.replaceAll("-", "+").replaceAll("_", "/");
|
|
35
|
+
}
|
|
36
|
+
const remainder = s.length % 4;
|
|
37
|
+
if (remainder === 1) {
|
|
38
|
+
throw new Error("Invalid base64: length must not be 1 (mod 4)");
|
|
39
|
+
}
|
|
40
|
+
if (remainder === 2)
|
|
41
|
+
s += "==";
|
|
42
|
+
if (remainder === 3)
|
|
43
|
+
s += "=";
|
|
44
|
+
return s;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Encode bytes into a standard Base64 string.
|
|
48
|
+
*/
|
|
49
|
+
export function encodeBase64(bytes) {
|
|
50
|
+
if (bytes.length === 0)
|
|
51
|
+
return "";
|
|
52
|
+
const outLen = Math.ceil(bytes.length / 3) * 4;
|
|
53
|
+
const out = new Array(outLen);
|
|
54
|
+
let i = 0;
|
|
55
|
+
let o = 0;
|
|
56
|
+
for (; i + 2 < bytes.length; i += 3) {
|
|
57
|
+
const n = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
|
|
58
|
+
out[o++] = BASE64_ALPHABET[(n >> 18) & 63];
|
|
59
|
+
out[o++] = BASE64_ALPHABET[(n >> 12) & 63];
|
|
60
|
+
out[o++] = BASE64_ALPHABET[(n >> 6) & 63];
|
|
61
|
+
out[o++] = BASE64_ALPHABET[n & 63];
|
|
62
|
+
}
|
|
63
|
+
const remaining = bytes.length - i;
|
|
64
|
+
if (remaining === 1) {
|
|
65
|
+
const n = bytes[i] << 16;
|
|
66
|
+
out[o++] = BASE64_ALPHABET[(n >> 18) & 63];
|
|
67
|
+
out[o++] = BASE64_ALPHABET[(n >> 12) & 63];
|
|
68
|
+
out[o++] = "=";
|
|
69
|
+
out[o++] = "=";
|
|
70
|
+
}
|
|
71
|
+
else if (remaining === 2) {
|
|
72
|
+
const n = (bytes[i] << 16) | (bytes[i + 1] << 8);
|
|
73
|
+
out[o++] = BASE64_ALPHABET[(n >> 18) & 63];
|
|
74
|
+
out[o++] = BASE64_ALPHABET[(n >> 12) & 63];
|
|
75
|
+
out[o++] = BASE64_ALPHABET[(n >> 6) & 63];
|
|
76
|
+
out[o++] = "=";
|
|
77
|
+
}
|
|
78
|
+
return out.join("");
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Decode a Base64 (or Base64URL) string into bytes.
|
|
82
|
+
*
|
|
83
|
+
* - Whitespace is ignored.
|
|
84
|
+
* - Missing padding is tolerated.
|
|
85
|
+
* - `-`/`_` are accepted as URL-safe variants.
|
|
86
|
+
*/
|
|
87
|
+
export function decodeBase64(base64) {
|
|
88
|
+
const s = normalizeBase64(base64);
|
|
89
|
+
if (s.length === 0)
|
|
90
|
+
return new Uint8Array(0);
|
|
91
|
+
let padding = 0;
|
|
92
|
+
if (s.endsWith("=="))
|
|
93
|
+
padding = 2;
|
|
94
|
+
else if (s.endsWith("="))
|
|
95
|
+
padding = 1;
|
|
96
|
+
const quadCount = s.length / 4;
|
|
97
|
+
const outLen = quadCount * 3 - padding;
|
|
98
|
+
const out = new Uint8Array(outLen);
|
|
99
|
+
let o = 0;
|
|
100
|
+
for (let i = 0; i < s.length; i += 4) {
|
|
101
|
+
const c0 = s.charCodeAt(i);
|
|
102
|
+
const c1 = s.charCodeAt(i + 1);
|
|
103
|
+
const c2 = s.charCodeAt(i + 2);
|
|
104
|
+
const c3 = s.charCodeAt(i + 3);
|
|
105
|
+
const v0 = DECODE_TABLE[c0];
|
|
106
|
+
const v1 = DECODE_TABLE[c1];
|
|
107
|
+
if (v0 === 0xff || v1 === 0xff) {
|
|
108
|
+
throw new Error("Invalid base64: invalid character");
|
|
109
|
+
}
|
|
110
|
+
const isPad2 = c2 === 0x3d; // '='
|
|
111
|
+
const isPad3 = c3 === 0x3d;
|
|
112
|
+
const v2 = isPad2 ? 0 : DECODE_TABLE[c2];
|
|
113
|
+
const v3 = isPad3 ? 0 : DECODE_TABLE[c3];
|
|
114
|
+
if ((!isPad2 && v2 === 0xff) || (!isPad3 && v3 === 0xff)) {
|
|
115
|
+
throw new Error("Invalid base64: invalid character");
|
|
116
|
+
}
|
|
117
|
+
const n = (v0 << 18) | (v1 << 12) | (v2 << 6) | v3;
|
|
118
|
+
out[o++] = (n >> 16) & 0xff;
|
|
119
|
+
if (!isPad2) {
|
|
120
|
+
if (o >= out.length)
|
|
121
|
+
break;
|
|
122
|
+
out[o++] = (n >> 8) & 0xff;
|
|
123
|
+
}
|
|
124
|
+
if (!isPad3) {
|
|
125
|
+
if (o >= out.length)
|
|
126
|
+
break;
|
|
127
|
+
out[o++] = n & 0xff;
|
|
128
|
+
}
|
|
129
|
+
if (isPad2 || isPad3) {
|
|
130
|
+
// Padding is only valid in the final quartet.
|
|
131
|
+
if (i + 4 !== s.length) {
|
|
132
|
+
throw new Error("Invalid base64: padding can only appear at the end");
|
|
133
|
+
}
|
|
134
|
+
// If third char is padding, fourth must also be padding.
|
|
135
|
+
if (isPad2 && !isPad3) {
|
|
136
|
+
throw new Error("Invalid base64: invalid padding");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Create a base64 data URL.
|
|
144
|
+
*/
|
|
145
|
+
export function toDataUrl(mime, bytes) {
|
|
146
|
+
if (!mime)
|
|
147
|
+
throw new Error("mime is required");
|
|
148
|
+
return `data:${mime};base64,${encodeBase64(bytes)}`;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Parse a base64 data URL.
|
|
152
|
+
*
|
|
153
|
+
* Supports `data:<mime>;base64,<payload>`.
|
|
154
|
+
*/
|
|
155
|
+
export function parseDataUrl(url) {
|
|
156
|
+
if (!url.startsWith("data:")) {
|
|
157
|
+
throw new Error("Invalid data URL: must start with 'data:'");
|
|
158
|
+
}
|
|
159
|
+
const commaIndex = url.indexOf(",");
|
|
160
|
+
if (commaIndex === -1) {
|
|
161
|
+
throw new Error("Invalid data URL: missing ',' separator");
|
|
162
|
+
}
|
|
163
|
+
const meta = url.slice(5, commaIndex);
|
|
164
|
+
const payload = url.slice(commaIndex + 1);
|
|
165
|
+
// We only support base64 payloads.
|
|
166
|
+
const metaParts = meta.split(";");
|
|
167
|
+
const mime = metaParts[0] || "application/octet-stream";
|
|
168
|
+
const isBase64 = metaParts.some((p) => p.toLowerCase() === "base64");
|
|
169
|
+
if (!isBase64) {
|
|
170
|
+
throw new Error("Invalid data URL: only base64 payloads are supported");
|
|
171
|
+
}
|
|
172
|
+
return { mime, bytes: decodeBase64(payload) };
|
|
173
|
+
}
|
|
@@ -165,11 +165,13 @@ export class GIFEncoder {
|
|
|
165
165
|
}
|
|
166
166
|
return Math.max(2, bits);
|
|
167
167
|
}
|
|
168
|
-
encode() {
|
|
168
|
+
encode(options) {
|
|
169
169
|
if (this.frames.length === 0) {
|
|
170
170
|
throw new Error("No frames to encode");
|
|
171
171
|
}
|
|
172
172
|
const output = [];
|
|
173
|
+
// Get loop count from options (default to 0 = infinite)
|
|
174
|
+
const loopCount = options?.loop ?? 0;
|
|
173
175
|
// Quantize first frame for Global Color Table
|
|
174
176
|
const firstFrame = this.frames[0];
|
|
175
177
|
const { palette: globalPalette, indexed: firstIndexed } = this.quantize(firstFrame.data);
|
|
@@ -206,7 +208,7 @@ export class GIFEncoder {
|
|
|
206
208
|
this.writeString(output, "NETSCAPE2.0");
|
|
207
209
|
output.push(3); // Sub-block Size
|
|
208
210
|
output.push(1); // Loop Indicator (1 = loop)
|
|
209
|
-
this.writeUint16LE(output,
|
|
211
|
+
this.writeUint16LE(output, loopCount); // Loop Count (0 = infinite, 1+ = specific count)
|
|
210
212
|
output.push(0); // Block Terminator
|
|
211
213
|
}
|
|
212
214
|
// Encode frames
|
|
@@ -186,4 +186,35 @@ export declare function flipHorizontal(data: Uint8Array, width: number, height:
|
|
|
186
186
|
* @returns Flipped image data
|
|
187
187
|
*/
|
|
188
188
|
export declare function flipVertical(data: Uint8Array, width: number, height: number): Uint8Array;
|
|
189
|
+
/**
|
|
190
|
+
* Convert RGB color to CMYK color space
|
|
191
|
+
* @param r Red component (0-255)
|
|
192
|
+
* @param g Green component (0-255)
|
|
193
|
+
* @param b Blue component (0-255)
|
|
194
|
+
* @returns CMYK values: [c (0-1), m (0-1), y (0-1), k (0-1)]
|
|
195
|
+
*/
|
|
196
|
+
export declare function rgbToCmyk(r: number, g: number, b: number): [number, number, number, number];
|
|
197
|
+
/**
|
|
198
|
+
* Convert CMYK color to RGB color space
|
|
199
|
+
* @param c Cyan component (0-1)
|
|
200
|
+
* @param m Magenta component (0-1)
|
|
201
|
+
* @param y Yellow component (0-1)
|
|
202
|
+
* @param k Key/Black component (0-1)
|
|
203
|
+
* @returns RGB values: [r (0-255), g (0-255), b (0-255)]
|
|
204
|
+
*/
|
|
205
|
+
export declare function cmykToRgb(c: number, m: number, y: number, k: number): [number, number, number];
|
|
206
|
+
/**
|
|
207
|
+
* Convert RGBA image data to CMYK representation
|
|
208
|
+
* Returns a Float32Array with 4 values per pixel (C, M, Y, K) in 0-1 range
|
|
209
|
+
* @param data Image data (RGBA)
|
|
210
|
+
* @returns CMYK image data as Float32Array (4 values per pixel)
|
|
211
|
+
*/
|
|
212
|
+
export declare function rgbaToCmyk(data: Uint8Array): Float32Array;
|
|
213
|
+
/**
|
|
214
|
+
* Convert CMYK image data to RGBA representation
|
|
215
|
+
* @param cmykData CMYK image data (4 values per pixel in 0-1 range)
|
|
216
|
+
* @param alpha Optional alpha value for all pixels (0-255, default: 255)
|
|
217
|
+
* @returns RGBA image data
|
|
218
|
+
*/
|
|
219
|
+
export declare function cmykToRgba(cmykData: Float32Array, alpha?: number): Uint8Array;
|
|
189
220
|
//# sourceMappingURL=image_processing.d.ts.map
|
|
@@ -743,3 +743,91 @@ export function flipVertical(data, width, height) {
|
|
|
743
743
|
}
|
|
744
744
|
return result;
|
|
745
745
|
}
|
|
746
|
+
/**
|
|
747
|
+
* Convert RGB color to CMYK color space
|
|
748
|
+
* @param r Red component (0-255)
|
|
749
|
+
* @param g Green component (0-255)
|
|
750
|
+
* @param b Blue component (0-255)
|
|
751
|
+
* @returns CMYK values: [c (0-1), m (0-1), y (0-1), k (0-1)]
|
|
752
|
+
*/
|
|
753
|
+
export function rgbToCmyk(r, g, b) {
|
|
754
|
+
// Normalize RGB values to 0-1 range
|
|
755
|
+
const rNorm = r / 255;
|
|
756
|
+
const gNorm = g / 255;
|
|
757
|
+
const bNorm = b / 255;
|
|
758
|
+
// Calculate K (key/black)
|
|
759
|
+
const k = 1 - Math.max(rNorm, gNorm, bNorm);
|
|
760
|
+
// If K is 1 (pure black), CMY are undefined but conventionally set to 0
|
|
761
|
+
if (k === 1) {
|
|
762
|
+
return [0, 0, 0, 1];
|
|
763
|
+
}
|
|
764
|
+
// Calculate CMY components
|
|
765
|
+
const c = (1 - rNorm - k) / (1 - k);
|
|
766
|
+
const m = (1 - gNorm - k) / (1 - k);
|
|
767
|
+
const y = (1 - bNorm - k) / (1 - k);
|
|
768
|
+
return [c, m, y, k];
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Convert CMYK color to RGB color space
|
|
772
|
+
* @param c Cyan component (0-1)
|
|
773
|
+
* @param m Magenta component (0-1)
|
|
774
|
+
* @param y Yellow component (0-1)
|
|
775
|
+
* @param k Key/Black component (0-1)
|
|
776
|
+
* @returns RGB values: [r (0-255), g (0-255), b (0-255)]
|
|
777
|
+
*/
|
|
778
|
+
export function cmykToRgb(c, m, y, k) {
|
|
779
|
+
// Convert CMYK to RGB
|
|
780
|
+
const r = 255 * (1 - c) * (1 - k);
|
|
781
|
+
const g = 255 * (1 - m) * (1 - k);
|
|
782
|
+
const b = 255 * (1 - y) * (1 - k);
|
|
783
|
+
return [
|
|
784
|
+
Math.round(Math.max(0, Math.min(255, r))),
|
|
785
|
+
Math.round(Math.max(0, Math.min(255, g))),
|
|
786
|
+
Math.round(Math.max(0, Math.min(255, b))),
|
|
787
|
+
];
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Convert RGBA image data to CMYK representation
|
|
791
|
+
* Returns a Float32Array with 4 values per pixel (C, M, Y, K) in 0-1 range
|
|
792
|
+
* @param data Image data (RGBA)
|
|
793
|
+
* @returns CMYK image data as Float32Array (4 values per pixel)
|
|
794
|
+
*/
|
|
795
|
+
export function rgbaToCmyk(data) {
|
|
796
|
+
const pixelCount = data.length / 4;
|
|
797
|
+
const result = new Float32Array(pixelCount * 4);
|
|
798
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
799
|
+
const r = data[i];
|
|
800
|
+
const g = data[i + 1];
|
|
801
|
+
const b = data[i + 2];
|
|
802
|
+
const [c, m, y, k] = rgbToCmyk(r, g, b);
|
|
803
|
+
const outIdx = (i / 4) * 4;
|
|
804
|
+
result[outIdx] = c;
|
|
805
|
+
result[outIdx + 1] = m;
|
|
806
|
+
result[outIdx + 2] = y;
|
|
807
|
+
result[outIdx + 3] = k;
|
|
808
|
+
}
|
|
809
|
+
return result;
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Convert CMYK image data to RGBA representation
|
|
813
|
+
* @param cmykData CMYK image data (4 values per pixel in 0-1 range)
|
|
814
|
+
* @param alpha Optional alpha value for all pixels (0-255, default: 255)
|
|
815
|
+
* @returns RGBA image data
|
|
816
|
+
*/
|
|
817
|
+
export function cmykToRgba(cmykData, alpha = 255) {
|
|
818
|
+
const pixelCount = cmykData.length / 4;
|
|
819
|
+
const result = new Uint8Array(pixelCount * 4);
|
|
820
|
+
for (let i = 0; i < cmykData.length; i += 4) {
|
|
821
|
+
const c = cmykData[i];
|
|
822
|
+
const m = cmykData[i + 1];
|
|
823
|
+
const y = cmykData[i + 2];
|
|
824
|
+
const k = cmykData[i + 3];
|
|
825
|
+
const [r, g, b] = cmykToRgb(c, m, y, k);
|
|
826
|
+
const outIdx = i;
|
|
827
|
+
result[outIdx] = r;
|
|
828
|
+
result[outIdx + 1] = g;
|
|
829
|
+
result[outIdx + 2] = b;
|
|
830
|
+
result[outIdx + 3] = alpha;
|
|
831
|
+
}
|
|
832
|
+
return result;
|
|
833
|
+
}
|
package/package.json
CHANGED
package/script/mod.d.ts
CHANGED
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
* ```
|
|
44
44
|
*/
|
|
45
45
|
export { Image } from "./src/image.js";
|
|
46
|
-
export type { ASCIIEncoderOptions, FrameMetadata, ImageData, ImageDecoderOptions, ImageFormat, ImageFrame, ImageMetadata, JPEGEncoderOptions, MultiFrameImageData, ResizeOptions, TIFFEncoderOptions, WebPEncoderOptions, } from "./src/types.js";
|
|
46
|
+
export type { APNGEncoderOptions, ASCIIEncoderOptions, AVIFEncoderOptions, FrameMetadata, GIFEncoderOptions, HEICEncoderOptions, ImageData, ImageDecoderOptions, ImageFormat, ImageFrame, ImageMetadata, JPEGEncoderOptions, MultiFrameImageData, PNGEncoderOptions, ResizeOptions, TIFFEncoderOptions, WebPEncoderOptions, } from "./src/types.js";
|
|
47
47
|
export { PNGFormat } from "./src/formats/png.js";
|
|
48
48
|
export { APNGFormat } from "./src/formats/apng.js";
|
|
49
49
|
export { JPEGFormat } from "./src/formats/jpeg.js";
|
|
@@ -59,4 +59,6 @@ export { PPMFormat } from "./src/formats/ppm.js";
|
|
|
59
59
|
export { ASCIIFormat } from "./src/formats/ascii.js";
|
|
60
60
|
export { HEICFormat } from "./src/formats/heic.js";
|
|
61
61
|
export { AVIFFormat } from "./src/formats/avif.js";
|
|
62
|
+
export { decodeBase64, encodeBase64, parseDataUrl, toDataUrl } from "./src/utils/base64.js";
|
|
63
|
+
export { cmykToRgb, cmykToRgba, rgbaToCmyk, rgbToCmyk } from "./src/utils/image_processing.js";
|
|
62
64
|
//# sourceMappingURL=mod.d.ts.map
|
package/script/mod.js
CHANGED
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
* ```
|
|
45
45
|
*/
|
|
46
46
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
-
exports.AVIFFormat = exports.HEICFormat = exports.ASCIIFormat = exports.PPMFormat = exports.PCXFormat = exports.PAMFormat = exports.DNGFormat = exports.ICOFormat = exports.BMPFormat = exports.TIFFFormat = exports.GIFFormat = exports.WebPFormat = exports.JPEGFormat = exports.APNGFormat = exports.PNGFormat = exports.Image = void 0;
|
|
47
|
+
exports.rgbToCmyk = exports.rgbaToCmyk = exports.cmykToRgba = exports.cmykToRgb = exports.toDataUrl = exports.parseDataUrl = exports.encodeBase64 = exports.decodeBase64 = exports.AVIFFormat = exports.HEICFormat = exports.ASCIIFormat = exports.PPMFormat = exports.PCXFormat = exports.PAMFormat = exports.DNGFormat = exports.ICOFormat = exports.BMPFormat = exports.TIFFFormat = exports.GIFFormat = exports.WebPFormat = exports.JPEGFormat = exports.APNGFormat = exports.PNGFormat = exports.Image = void 0;
|
|
48
48
|
var image_js_1 = require("./src/image.js");
|
|
49
49
|
Object.defineProperty(exports, "Image", { enumerable: true, get: function () { return image_js_1.Image; } });
|
|
50
50
|
var png_js_1 = require("./src/formats/png.js");
|
|
@@ -77,3 +77,13 @@ var heic_js_1 = require("./src/formats/heic.js");
|
|
|
77
77
|
Object.defineProperty(exports, "HEICFormat", { enumerable: true, get: function () { return heic_js_1.HEICFormat; } });
|
|
78
78
|
var avif_js_1 = require("./src/formats/avif.js");
|
|
79
79
|
Object.defineProperty(exports, "AVIFFormat", { enumerable: true, get: function () { return avif_js_1.AVIFFormat; } });
|
|
80
|
+
var base64_js_1 = require("./src/utils/base64.js");
|
|
81
|
+
Object.defineProperty(exports, "decodeBase64", { enumerable: true, get: function () { return base64_js_1.decodeBase64; } });
|
|
82
|
+
Object.defineProperty(exports, "encodeBase64", { enumerable: true, get: function () { return base64_js_1.encodeBase64; } });
|
|
83
|
+
Object.defineProperty(exports, "parseDataUrl", { enumerable: true, get: function () { return base64_js_1.parseDataUrl; } });
|
|
84
|
+
Object.defineProperty(exports, "toDataUrl", { enumerable: true, get: function () { return base64_js_1.toDataUrl; } });
|
|
85
|
+
var image_processing_js_1 = require("./src/utils/image_processing.js");
|
|
86
|
+
Object.defineProperty(exports, "cmykToRgb", { enumerable: true, get: function () { return image_processing_js_1.cmykToRgb; } });
|
|
87
|
+
Object.defineProperty(exports, "cmykToRgba", { enumerable: true, get: function () { return image_processing_js_1.cmykToRgba; } });
|
|
88
|
+
Object.defineProperty(exports, "rgbaToCmyk", { enumerable: true, get: function () { return image_processing_js_1.rgbaToCmyk; } });
|
|
89
|
+
Object.defineProperty(exports, "rgbToCmyk", { enumerable: true, get: function () { return image_processing_js_1.rgbToCmyk; } });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata, MultiFrameImageData } from "../types.js";
|
|
1
|
+
import type { APNGEncoderOptions, ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata, MultiFrameImageData } from "../types.js";
|
|
2
2
|
import { PNGBase } from "./png_base.js";
|
|
3
3
|
/**
|
|
4
4
|
* APNG (Animated PNG) format handler
|
|
@@ -36,15 +36,17 @@ export declare class APNGFormat extends PNGBase implements ImageFormat {
|
|
|
36
36
|
/**
|
|
37
37
|
* Encode RGBA image data to APNG format (single frame)
|
|
38
38
|
* @param imageData Image data to encode
|
|
39
|
+
* @param options Encoding options (compressionLevel 0-9, default 6)
|
|
39
40
|
* @returns Encoded APNG image bytes
|
|
40
41
|
*/
|
|
41
|
-
encode(imageData: ImageData,
|
|
42
|
+
encode(imageData: ImageData, options?: APNGEncoderOptions): Promise<Uint8Array>;
|
|
42
43
|
/**
|
|
43
44
|
* Encode multi-frame image data to APNG format
|
|
44
45
|
* @param imageData Multi-frame image data to encode
|
|
46
|
+
* @param options Encoding options (compressionLevel 0-9, default 6)
|
|
45
47
|
* @returns Encoded APNG image bytes
|
|
46
48
|
*/
|
|
47
|
-
encodeFrames(imageData: MultiFrameImageData,
|
|
49
|
+
encodeFrames(imageData: MultiFrameImageData, options?: APNGEncoderOptions): Promise<Uint8Array>;
|
|
48
50
|
private decodeFrameData;
|
|
49
51
|
/**
|
|
50
52
|
* Get the list of metadata fields supported by APNG format
|
|
@@ -253,9 +253,10 @@ class APNGFormat extends png_base_js_1.PNGBase {
|
|
|
253
253
|
/**
|
|
254
254
|
* Encode RGBA image data to APNG format (single frame)
|
|
255
255
|
* @param imageData Image data to encode
|
|
256
|
+
* @param options Encoding options (compressionLevel 0-9, default 6)
|
|
256
257
|
* @returns Encoded APNG image bytes
|
|
257
258
|
*/
|
|
258
|
-
encode(imageData,
|
|
259
|
+
encode(imageData, options) {
|
|
259
260
|
// For single frame, create a multi-frame with one frame
|
|
260
261
|
const multiFrame = {
|
|
261
262
|
width: imageData.width,
|
|
@@ -268,15 +269,21 @@ class APNGFormat extends png_base_js_1.PNGBase {
|
|
|
268
269
|
}],
|
|
269
270
|
metadata: imageData.metadata,
|
|
270
271
|
};
|
|
271
|
-
return this.encodeFrames(multiFrame,
|
|
272
|
+
return this.encodeFrames(multiFrame, options);
|
|
272
273
|
}
|
|
273
274
|
/**
|
|
274
275
|
* Encode multi-frame image data to APNG format
|
|
275
276
|
* @param imageData Multi-frame image data to encode
|
|
277
|
+
* @param options Encoding options (compressionLevel 0-9, default 6)
|
|
276
278
|
* @returns Encoded APNG image bytes
|
|
277
279
|
*/
|
|
278
|
-
async encodeFrames(imageData,
|
|
280
|
+
async encodeFrames(imageData, options) {
|
|
279
281
|
const { width, height, frames, metadata } = imageData;
|
|
282
|
+
const compressionLevel = options?.compressionLevel ?? 6;
|
|
283
|
+
// Validate compression level
|
|
284
|
+
if (compressionLevel < 0 || compressionLevel > 9) {
|
|
285
|
+
throw new Error("Compression level must be between 0 and 9");
|
|
286
|
+
}
|
|
280
287
|
if (frames.length === 0) {
|
|
281
288
|
throw new Error("No frames to encode");
|
|
282
289
|
}
|
|
@@ -334,7 +341,7 @@ class APNGFormat extends png_base_js_1.PNGBase {
|
|
|
334
341
|
fctl[25] = 0; // blend_op: APNG_BLEND_OP_SOURCE
|
|
335
342
|
chunks.push(this.createChunk("fcTL", fctl));
|
|
336
343
|
// Filter and compress frame data
|
|
337
|
-
const filtered = this.filterData(frame.data, frame.width, frame.height);
|
|
344
|
+
const filtered = this.filterData(frame.data, frame.width, frame.height, compressionLevel);
|
|
338
345
|
const compressed = await this.deflate(filtered);
|
|
339
346
|
if (i === 0) {
|
|
340
347
|
// First frame uses IDAT
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata } from "../types.js";
|
|
1
|
+
import type { AVIFEncoderOptions, ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* AVIF format handler
|
|
4
4
|
* Supports AVIF images using runtime APIs (ImageDecoder/OffscreenCanvas)
|
|
@@ -32,7 +32,7 @@ export declare class AVIFFormat implements ImageFormat {
|
|
|
32
32
|
* @param imageData Image data to encode
|
|
33
33
|
* @returns Encoded AVIF image bytes
|
|
34
34
|
*/
|
|
35
|
-
encode(imageData: ImageData,
|
|
35
|
+
encode(imageData: ImageData, options?: AVIFEncoderOptions): Promise<Uint8Array>;
|
|
36
36
|
/**
|
|
37
37
|
* Decode using runtime APIs
|
|
38
38
|
* @param data Raw AVIF data
|
|
@@ -82,8 +82,9 @@ class AVIFFormat {
|
|
|
82
82
|
* @param imageData Image data to encode
|
|
83
83
|
* @returns Encoded AVIF image bytes
|
|
84
84
|
*/
|
|
85
|
-
async encode(imageData,
|
|
85
|
+
async encode(imageData, options) {
|
|
86
86
|
const { width, height, data, metadata: _metadata } = imageData;
|
|
87
|
+
const requestedQuality = options?.quality;
|
|
87
88
|
// Try to use runtime encoding if available
|
|
88
89
|
if (typeof OffscreenCanvas !== "undefined") {
|
|
89
90
|
try {
|
|
@@ -94,10 +95,19 @@ class AVIFFormat {
|
|
|
94
95
|
const imgDataData = new Uint8ClampedArray(data);
|
|
95
96
|
imgData.data.set(imgDataData);
|
|
96
97
|
ctx.putImageData(imgData, 0, 0);
|
|
98
|
+
const quality = requestedQuality === undefined
|
|
99
|
+
? undefined
|
|
100
|
+
: (requestedQuality <= 1
|
|
101
|
+
? Math.max(0, Math.min(1, requestedQuality))
|
|
102
|
+
: Math.max(1, Math.min(100, requestedQuality)) / 100);
|
|
97
103
|
// Try to encode as AVIF
|
|
98
104
|
const blob = await canvas.convertToBlob({
|
|
99
105
|
type: "image/avif",
|
|
106
|
+
...(quality === undefined ? {} : { quality }),
|
|
100
107
|
});
|
|
108
|
+
if (blob.type !== "image/avif") {
|
|
109
|
+
throw new Error(`Runtime did not encode AVIF (got '${blob.type || "(empty)"}')`);
|
|
110
|
+
}
|
|
101
111
|
const arrayBuffer = await blob.arrayBuffer();
|
|
102
112
|
const encoded = new Uint8Array(arrayBuffer);
|
|
103
113
|
// Note: Metadata injection for AVIF is complex and would require
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata, MultiFrameImageData } from "../types.js";
|
|
1
|
+
import type { GIFEncoderOptions, ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata, MultiFrameImageData } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* GIF format handler
|
|
4
4
|
* Now includes pure-JS implementation with custom LZW compression/decompression
|
|
@@ -39,7 +39,7 @@ export declare class GIFFormat implements ImageFormat {
|
|
|
39
39
|
* @param imageData Image data to encode
|
|
40
40
|
* @returns Encoded GIF image bytes
|
|
41
41
|
*/
|
|
42
|
-
encode(imageData: ImageData,
|
|
42
|
+
encode(imageData: ImageData, options?: GIFEncoderOptions): Promise<Uint8Array>;
|
|
43
43
|
/**
|
|
44
44
|
* Decode all frames from an animated GIF
|
|
45
45
|
*/
|
|
@@ -47,7 +47,7 @@ export declare class GIFFormat implements ImageFormat {
|
|
|
47
47
|
/**
|
|
48
48
|
* Encode multi-frame image data to animated GIF
|
|
49
49
|
*/
|
|
50
|
-
encodeFrames(imageData: MultiFrameImageData,
|
|
50
|
+
encodeFrames(imageData: MultiFrameImageData, options?: GIFEncoderOptions): Promise<Uint8Array>;
|
|
51
51
|
private mapDisposalMethod;
|
|
52
52
|
private decodeUsingRuntime;
|
|
53
53
|
private readDataSubBlocks;
|
|
@@ -156,12 +156,12 @@ class GIFFormat {
|
|
|
156
156
|
* @param imageData Image data to encode
|
|
157
157
|
* @returns Encoded GIF image bytes
|
|
158
158
|
*/
|
|
159
|
-
async encode(imageData,
|
|
159
|
+
async encode(imageData, options) {
|
|
160
160
|
const { width, height, data, metadata } = imageData;
|
|
161
161
|
// Try pure-JS encoder first
|
|
162
162
|
try {
|
|
163
163
|
const encoder = new gif_encoder_js_1.GIFEncoder(width, height, data);
|
|
164
|
-
const encoded = encoder.encode();
|
|
164
|
+
const encoded = encoder.encode(options);
|
|
165
165
|
// Inject metadata if present
|
|
166
166
|
if (metadata && Object.keys(metadata).length > 0) {
|
|
167
167
|
const injected = this.injectMetadata(encoded, metadata);
|
|
@@ -240,7 +240,7 @@ class GIFFormat {
|
|
|
240
240
|
/**
|
|
241
241
|
* Encode multi-frame image data to animated GIF
|
|
242
242
|
*/
|
|
243
|
-
encodeFrames(imageData,
|
|
243
|
+
encodeFrames(imageData, options) {
|
|
244
244
|
if (imageData.frames.length === 0) {
|
|
245
245
|
throw new Error("No frames to encode");
|
|
246
246
|
}
|
|
@@ -250,7 +250,7 @@ class GIFFormat {
|
|
|
250
250
|
const delay = frame.frameMetadata?.delay ?? 100;
|
|
251
251
|
encoder.addFrame(frame.data, delay);
|
|
252
252
|
}
|
|
253
|
-
return Promise.resolve(encoder.encode());
|
|
253
|
+
return Promise.resolve(encoder.encode(options));
|
|
254
254
|
}
|
|
255
255
|
mapDisposalMethod(disposal) {
|
|
256
256
|
switch (disposal) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata } from "../types.js";
|
|
1
|
+
import type { HEICEncoderOptions, ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* HEIC format handler
|
|
4
4
|
* Supports HEIC/HEIF images using runtime APIs (ImageDecoder/OffscreenCanvas)
|
|
@@ -32,7 +32,7 @@ export declare class HEICFormat implements ImageFormat {
|
|
|
32
32
|
* @param imageData Image data to encode
|
|
33
33
|
* @returns Encoded HEIC image bytes
|
|
34
34
|
*/
|
|
35
|
-
encode(imageData: ImageData,
|
|
35
|
+
encode(imageData: ImageData, options?: HEICEncoderOptions): Promise<Uint8Array>;
|
|
36
36
|
/**
|
|
37
37
|
* Decode using runtime APIs
|
|
38
38
|
* @param data Raw HEIC data
|
|
@@ -83,8 +83,9 @@ class HEICFormat {
|
|
|
83
83
|
* @param imageData Image data to encode
|
|
84
84
|
* @returns Encoded HEIC image bytes
|
|
85
85
|
*/
|
|
86
|
-
async encode(imageData,
|
|
86
|
+
async encode(imageData, options) {
|
|
87
87
|
const { width, height, data, metadata: _metadata } = imageData;
|
|
88
|
+
const requestedQuality = options?.quality;
|
|
88
89
|
// Try to use runtime encoding if available
|
|
89
90
|
if (typeof OffscreenCanvas !== "undefined") {
|
|
90
91
|
try {
|
|
@@ -95,10 +96,19 @@ class HEICFormat {
|
|
|
95
96
|
const imgDataData = new Uint8ClampedArray(data);
|
|
96
97
|
imgData.data.set(imgDataData);
|
|
97
98
|
ctx.putImageData(imgData, 0, 0);
|
|
99
|
+
const quality = requestedQuality === undefined
|
|
100
|
+
? undefined
|
|
101
|
+
: (requestedQuality <= 1
|
|
102
|
+
? Math.max(0, Math.min(1, requestedQuality))
|
|
103
|
+
: Math.max(1, Math.min(100, requestedQuality)) / 100);
|
|
98
104
|
// Try to encode as HEIC
|
|
99
105
|
const blob = await canvas.convertToBlob({
|
|
100
106
|
type: "image/heic",
|
|
107
|
+
...(quality === undefined ? {} : { quality }),
|
|
101
108
|
});
|
|
109
|
+
if (blob.type !== "image/heic") {
|
|
110
|
+
throw new Error(`Runtime did not encode HEIC (got '${blob.type || "(empty)"}')`);
|
|
111
|
+
}
|
|
102
112
|
const arrayBuffer = await blob.arrayBuffer();
|
|
103
113
|
const encoded = new Uint8Array(arrayBuffer);
|
|
104
114
|
// Note: Metadata injection for HEIC is complex and would require
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata } from "../types.js";
|
|
1
|
+
import type { ImageData, ImageDecoderOptions, ImageFormat, ImageMetadata, PNGEncoderOptions } from "../types.js";
|
|
2
2
|
import { PNGBase } from "./png_base.js";
|
|
3
3
|
/**
|
|
4
4
|
* PNG format handler
|
|
@@ -24,9 +24,10 @@ export declare class PNGFormat extends PNGBase implements ImageFormat {
|
|
|
24
24
|
/**
|
|
25
25
|
* Encode RGBA image data to PNG format
|
|
26
26
|
* @param imageData Image data to encode
|
|
27
|
+
* @param options Encoding options (compressionLevel 0-9, default 6)
|
|
27
28
|
* @returns Encoded PNG image bytes
|
|
28
29
|
*/
|
|
29
|
-
encode(imageData: ImageData,
|
|
30
|
+
encode(imageData: ImageData, options?: PNGEncoderOptions): Promise<Uint8Array>;
|
|
30
31
|
/**
|
|
31
32
|
* Get the list of metadata fields supported by PNG format
|
|
32
33
|
* Delegates to PNGBase implementation
|
|
@@ -113,10 +113,16 @@ class PNGFormat extends png_base_js_1.PNGBase {
|
|
|
113
113
|
/**
|
|
114
114
|
* Encode RGBA image data to PNG format
|
|
115
115
|
* @param imageData Image data to encode
|
|
116
|
+
* @param options Encoding options (compressionLevel 0-9, default 6)
|
|
116
117
|
* @returns Encoded PNG image bytes
|
|
117
118
|
*/
|
|
118
|
-
async encode(imageData,
|
|
119
|
+
async encode(imageData, options) {
|
|
119
120
|
const { width, height, data, metadata } = imageData;
|
|
121
|
+
const compressionLevel = options?.compressionLevel ?? 6;
|
|
122
|
+
// Validate compression level
|
|
123
|
+
if (compressionLevel < 0 || compressionLevel > 9) {
|
|
124
|
+
throw new Error("Compression level must be between 0 and 9");
|
|
125
|
+
}
|
|
120
126
|
// Prepare IHDR chunk
|
|
121
127
|
const ihdr = new Uint8Array(13);
|
|
122
128
|
this.writeUint32(ihdr, 0, width);
|
|
@@ -127,7 +133,7 @@ class PNGFormat extends png_base_js_1.PNGBase {
|
|
|
127
133
|
ihdr[11] = 0; // filter method
|
|
128
134
|
ihdr[12] = 0; // interlace method
|
|
129
135
|
// Filter and compress image data
|
|
130
|
-
const filtered = this.filterData(data, width, height);
|
|
136
|
+
const filtered = this.filterData(data, width, height, compressionLevel);
|
|
131
137
|
const compressed = await this.deflate(filtered);
|
|
132
138
|
// Build PNG
|
|
133
139
|
const chunks = [];
|