cross-image 0.4.0 → 0.4.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/README.md +186 -5
- 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/jpeg.d.ts +21 -1
- package/esm/src/formats/jpeg.js +59 -0
- 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 +54 -1
- package/esm/src/image.js +97 -1
- package/esm/src/types.d.ts +129 -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/esm/src/utils/jpeg_decoder.d.ts +25 -2
- package/esm/src/utils/jpeg_decoder.js +101 -10
- package/esm/src/utils/jpeg_encoder.d.ts +19 -0
- package/esm/src/utils/jpeg_encoder.js +267 -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/jpeg.d.ts +21 -1
- package/script/src/formats/jpeg.js +59 -0
- 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 +54 -1
- package/script/src/image.js +96 -0
- package/script/src/types.d.ts +129 -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
- package/script/src/utils/jpeg_decoder.d.ts +25 -2
- package/script/src/utils/jpeg_decoder.js +101 -10
- package/script/src/utils/jpeg_encoder.d.ts +19 -0
- package/script/src/utils/jpeg_encoder.js +267 -0
package/esm/src/types.d.ts
CHANGED
|
@@ -1,3 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JPEG quantized DCT coefficients for steganography and advanced processing
|
|
3
|
+
* Contains the frequency-domain representation of the image
|
|
4
|
+
*/
|
|
5
|
+
export interface JPEGQuantizedCoefficients {
|
|
6
|
+
/** Format identifier */
|
|
7
|
+
format: "jpeg";
|
|
8
|
+
/** Image width in pixels */
|
|
9
|
+
width: number;
|
|
10
|
+
/** Image height in pixels */
|
|
11
|
+
height: number;
|
|
12
|
+
/** Whether the JPEG is progressive */
|
|
13
|
+
isProgressive: boolean;
|
|
14
|
+
/** Component data (Y, Cb, Cr for color images) */
|
|
15
|
+
components: JPEGComponentCoefficients[];
|
|
16
|
+
/** Quantization tables used (indexed by table ID) */
|
|
17
|
+
quantizationTables: (Uint8Array | number[])[];
|
|
18
|
+
/** MCU width (number of 8x8 blocks horizontally) */
|
|
19
|
+
mcuWidth: number;
|
|
20
|
+
/** MCU height (number of 8x8 blocks vertically) */
|
|
21
|
+
mcuHeight: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Coefficients for a single JPEG component (Y, Cb, or Cr)
|
|
25
|
+
*/
|
|
26
|
+
export interface JPEGComponentCoefficients {
|
|
27
|
+
/** Component ID (1=Y, 2=Cb, 3=Cr typically) */
|
|
28
|
+
id: number;
|
|
29
|
+
/** Horizontal sampling factor */
|
|
30
|
+
h: number;
|
|
31
|
+
/** Vertical sampling factor */
|
|
32
|
+
v: number;
|
|
33
|
+
/** Quantization table index */
|
|
34
|
+
qTable: number;
|
|
35
|
+
/**
|
|
36
|
+
* Quantized DCT coefficient blocks
|
|
37
|
+
* blocks[blockRow][blockCol] contains a 64-element array in zigzag order
|
|
38
|
+
* Coefficients are quantized (divided by quantization table values)
|
|
39
|
+
*/
|
|
40
|
+
blocks: Int32Array[][];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Union type for coefficient data from different formats
|
|
44
|
+
* Currently only JPEG is supported, but this allows for future extension
|
|
45
|
+
*/
|
|
46
|
+
export type CoefficientData = JPEGQuantizedCoefficients;
|
|
1
47
|
/**
|
|
2
48
|
* Image metadata
|
|
3
49
|
*/
|
|
@@ -135,6 +181,42 @@ export interface ResizeOptions {
|
|
|
135
181
|
*/
|
|
136
182
|
fit?: "stretch" | "fit" | "fill" | "cover" | "contain";
|
|
137
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Options for PNG encoding.
|
|
186
|
+
*/
|
|
187
|
+
export interface PNGEncoderOptions {
|
|
188
|
+
/**
|
|
189
|
+
* Compression level (0-9)
|
|
190
|
+
* - 0-2: No filtering (fastest)
|
|
191
|
+
* - 3-6: Sub filter (balanced, default is 6)
|
|
192
|
+
* - 7-9: Adaptive filtering per scanline (best compression)
|
|
193
|
+
*
|
|
194
|
+
* Default: 6 (balanced)
|
|
195
|
+
*
|
|
196
|
+
* Note: Affects PNG filter selection. The native deflate compression
|
|
197
|
+
* is used regardless of level.
|
|
198
|
+
*/
|
|
199
|
+
compressionLevel?: number;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Options for APNG (Animated PNG) encoding.
|
|
203
|
+
*
|
|
204
|
+
* APNG uses PNG encoding for each frame.
|
|
205
|
+
*/
|
|
206
|
+
export interface APNGEncoderOptions extends PNGEncoderOptions {
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Options for GIF encoding.
|
|
210
|
+
*/
|
|
211
|
+
export interface GIFEncoderOptions {
|
|
212
|
+
/**
|
|
213
|
+
* Loop count for animated GIFs.
|
|
214
|
+
* - 0 (default): Loop infinitely
|
|
215
|
+
* - 1+: Loop a specific number of times
|
|
216
|
+
* - undefined or not set: Loop infinitely (same as 0)
|
|
217
|
+
*/
|
|
218
|
+
loop?: number;
|
|
219
|
+
}
|
|
138
220
|
/**
|
|
139
221
|
* Options for ASCII art encoding
|
|
140
222
|
*/
|
|
@@ -158,6 +240,8 @@ export interface TIFFEncoderOptions {
|
|
|
158
240
|
grayscale?: boolean;
|
|
159
241
|
/** Encode as RGB without alpha channel (default: false, ignored if grayscale is true) */
|
|
160
242
|
rgb?: boolean;
|
|
243
|
+
/** Encode as CMYK color space (default: false, ignored if grayscale is true) */
|
|
244
|
+
cmyk?: boolean;
|
|
161
245
|
}
|
|
162
246
|
/**
|
|
163
247
|
* Options for WebP encoding
|
|
@@ -189,6 +273,34 @@ export interface JPEGEncoderOptions {
|
|
|
189
273
|
*/
|
|
190
274
|
progressive?: boolean;
|
|
191
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* Options for AVIF encoding.
|
|
278
|
+
*
|
|
279
|
+
* Note: AVIF encoding is currently delegated to runtime APIs (OffscreenCanvas).
|
|
280
|
+
* Many runtimes ignore `quality` for AVIF, or may not support AVIF encoding at all.
|
|
281
|
+
*/
|
|
282
|
+
export interface AVIFEncoderOptions {
|
|
283
|
+
/**
|
|
284
|
+
* Best-effort encoding quality.
|
|
285
|
+
*
|
|
286
|
+
* Accepts either 0-1 (canvas-style) or 1-100 (library-style).
|
|
287
|
+
*/
|
|
288
|
+
quality?: number;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Options for HEIC encoding.
|
|
292
|
+
*
|
|
293
|
+
* Note: HEIC encoding is currently delegated to runtime APIs (OffscreenCanvas).
|
|
294
|
+
* Many runtimes do not support HEIC encoding.
|
|
295
|
+
*/
|
|
296
|
+
export interface HEICEncoderOptions {
|
|
297
|
+
/**
|
|
298
|
+
* Best-effort encoding quality.
|
|
299
|
+
*
|
|
300
|
+
* Accepts either 0-1 (canvas-style) or 1-100 (library-style).
|
|
301
|
+
*/
|
|
302
|
+
quality?: number;
|
|
303
|
+
}
|
|
192
304
|
/**
|
|
193
305
|
* Common options for decode APIs.
|
|
194
306
|
*
|
|
@@ -273,5 +385,22 @@ export interface ImageFormat {
|
|
|
273
385
|
* @returns Metadata extracted from the image, or undefined if extraction fails
|
|
274
386
|
*/
|
|
275
387
|
extractMetadata?(data: Uint8Array): Promise<ImageMetadata | undefined>;
|
|
388
|
+
/**
|
|
389
|
+
* Extract coefficients from encoded image data (optional)
|
|
390
|
+
* For JPEG, this returns quantized DCT coefficients
|
|
391
|
+
* Useful for steganography and advanced image processing
|
|
392
|
+
* @param data Raw image data
|
|
393
|
+
* @param options Decoder options
|
|
394
|
+
* @returns Format-specific coefficient structure or undefined if not supported
|
|
395
|
+
*/
|
|
396
|
+
extractCoefficients?(data: Uint8Array, options?: ImageDecoderOptions): Promise<CoefficientData | undefined>;
|
|
397
|
+
/**
|
|
398
|
+
* Encode image from coefficients (optional)
|
|
399
|
+
* For JPEG, accepts quantized DCT coefficients and produces a valid JPEG
|
|
400
|
+
* @param coeffs Format-specific coefficient structure
|
|
401
|
+
* @param options Format-specific encoding options
|
|
402
|
+
* @returns Encoded image bytes
|
|
403
|
+
*/
|
|
404
|
+
encodeFromCoefficients?(coeffs: CoefficientData, options?: unknown): Promise<Uint8Array>;
|
|
276
405
|
}
|
|
277
406
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base64 utilities.
|
|
3
|
+
*
|
|
4
|
+
* Designed to work across Deno, Node.js, and Bun without external dependencies.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Encode bytes into a standard Base64 string.
|
|
8
|
+
*/
|
|
9
|
+
export declare function encodeBase64(bytes: Uint8Array): string;
|
|
10
|
+
/**
|
|
11
|
+
* Decode a Base64 (or Base64URL) string into bytes.
|
|
12
|
+
*
|
|
13
|
+
* - Whitespace is ignored.
|
|
14
|
+
* - Missing padding is tolerated.
|
|
15
|
+
* - `-`/`_` are accepted as URL-safe variants.
|
|
16
|
+
*/
|
|
17
|
+
export declare function decodeBase64(base64: string): Uint8Array;
|
|
18
|
+
export interface ParsedDataUrl {
|
|
19
|
+
mime: string;
|
|
20
|
+
bytes: Uint8Array;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a base64 data URL.
|
|
24
|
+
*/
|
|
25
|
+
export declare function toDataUrl(mime: string, bytes: Uint8Array): string;
|
|
26
|
+
/**
|
|
27
|
+
* Parse a base64 data URL.
|
|
28
|
+
*
|
|
29
|
+
* Supports `data:<mime>;base64,<payload>`.
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseDataUrl(url: string): ParsedDataUrl;
|
|
32
|
+
//# sourceMappingURL=base64.d.ts.map
|
|
@@ -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
|
+
}
|
|
@@ -8,7 +8,17 @@
|
|
|
8
8
|
* This is a pure JavaScript implementation that handles common JPEG files.
|
|
9
9
|
* For complex or non-standard JPEGs, the ImageDecoder API fallback is preferred.
|
|
10
10
|
*/
|
|
11
|
-
import type { ImageDecoderOptions } from "../types.js";
|
|
11
|
+
import type { ImageDecoderOptions, JPEGQuantizedCoefficients } from "../types.js";
|
|
12
|
+
/**
|
|
13
|
+
* Extended decoder options including coefficient extraction
|
|
14
|
+
*/
|
|
15
|
+
interface JPEGDecoderOptions extends ImageDecoderOptions {
|
|
16
|
+
/**
|
|
17
|
+
* When true, stores quantized DCT coefficients for later retrieval
|
|
18
|
+
* via getQuantizedCoefficients(). Coefficients are stored in zigzag order.
|
|
19
|
+
*/
|
|
20
|
+
extractCoefficients?: boolean;
|
|
21
|
+
}
|
|
12
22
|
export declare class JPEGDecoder {
|
|
13
23
|
private data;
|
|
14
24
|
private pos;
|
|
@@ -29,8 +39,20 @@ export declare class JPEGDecoder {
|
|
|
29
39
|
private successiveLow;
|
|
30
40
|
private scanComponentIds;
|
|
31
41
|
private eobRun;
|
|
32
|
-
|
|
42
|
+
private quantizedCoefficients;
|
|
43
|
+
constructor(data: Uint8Array, settings?: JPEGDecoderOptions);
|
|
33
44
|
decode(): Uint8Array;
|
|
45
|
+
/**
|
|
46
|
+
* Get the quantized DCT coefficients after decoding
|
|
47
|
+
* Only available if extractCoefficients option was set to true
|
|
48
|
+
* @returns JPEGQuantizedCoefficients or undefined if not available
|
|
49
|
+
*/
|
|
50
|
+
getQuantizedCoefficients(): JPEGQuantizedCoefficients | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Store quantized coefficients in the output structure
|
|
53
|
+
* Called after decoding when extractCoefficients is true
|
|
54
|
+
*/
|
|
55
|
+
private storeQuantizedCoefficients;
|
|
34
56
|
private readMarker;
|
|
35
57
|
private readUint16;
|
|
36
58
|
private skipSegment;
|
|
@@ -49,4 +71,5 @@ export declare class JPEGDecoder {
|
|
|
49
71
|
private idct;
|
|
50
72
|
private convertToRGB;
|
|
51
73
|
}
|
|
74
|
+
export {};
|
|
52
75
|
//# sourceMappingURL=jpeg_decoder.d.ts.map
|