cross-image 0.2.0 → 0.2.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 +41 -28
- package/esm/mod.d.ts +4 -1
- package/esm/mod.js +4 -1
- package/esm/src/formats/apng.d.ts +50 -0
- package/esm/src/formats/apng.js +364 -0
- package/esm/src/formats/bmp.d.ts +0 -6
- package/esm/src/formats/bmp.js +24 -47
- package/esm/src/formats/dng.js +4 -4
- package/esm/src/formats/gif.d.ts +0 -2
- package/esm/src/formats/gif.js +10 -16
- package/esm/src/formats/ico.d.ts +41 -0
- package/esm/src/formats/ico.js +214 -0
- package/esm/src/formats/pcx.js +1 -1
- package/esm/src/formats/png.d.ts +2 -21
- package/esm/src/formats/png.js +5 -429
- package/esm/src/formats/png_base.d.ts +108 -0
- package/esm/src/formats/png_base.js +487 -0
- package/esm/src/formats/ppm.d.ts +50 -0
- package/esm/src/formats/ppm.js +242 -0
- package/esm/src/formats/tiff.d.ts +4 -0
- package/esm/src/formats/tiff.js +163 -44
- package/esm/src/formats/webp.d.ts +0 -1
- package/esm/src/formats/webp.js +4 -7
- package/esm/src/image.d.ts +30 -0
- package/esm/src/image.js +62 -1
- package/esm/src/utils/byte_utils.d.ts +30 -0
- package/esm/src/utils/byte_utils.js +50 -0
- package/esm/src/utils/gif_encoder.d.ts +3 -2
- package/esm/src/utils/gif_encoder.js +115 -48
- package/esm/src/utils/image_processing.d.ts +43 -0
- package/esm/src/utils/image_processing.js +230 -0
- package/package.json +1 -1
- package/script/mod.d.ts +4 -1
- package/script/mod.js +8 -2
- package/script/src/formats/apng.d.ts +50 -0
- package/script/src/formats/apng.js +368 -0
- package/script/src/formats/bmp.d.ts +0 -6
- package/script/src/formats/bmp.js +24 -47
- package/script/src/formats/dng.js +4 -4
- package/script/src/formats/gif.d.ts +0 -2
- package/script/src/formats/gif.js +10 -16
- package/script/src/formats/ico.d.ts +41 -0
- package/script/src/formats/ico.js +218 -0
- package/script/src/formats/pcx.js +1 -1
- package/script/src/formats/png.d.ts +2 -21
- package/script/src/formats/png.js +5 -429
- package/script/src/formats/png_base.d.ts +108 -0
- package/script/src/formats/png_base.js +491 -0
- package/script/src/formats/ppm.d.ts +50 -0
- package/script/src/formats/ppm.js +246 -0
- package/script/src/formats/tiff.d.ts +4 -0
- package/script/src/formats/tiff.js +163 -44
- package/script/src/formats/webp.d.ts +0 -1
- package/script/src/formats/webp.js +4 -7
- package/script/src/image.d.ts +30 -0
- package/script/src/image.js +61 -0
- package/script/src/utils/byte_utils.d.ts +30 -0
- package/script/src/utils/byte_utils.js +58 -0
- package/script/src/utils/gif_encoder.d.ts +3 -2
- package/script/src/utils/gif_encoder.js +115 -48
- package/script/src/utils/image_processing.d.ts +43 -0
- package/script/src/utils/image_processing.js +235 -0
package/esm/src/image.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { resizeBilinear, resizeNearest } from "./utils/resize.js";
|
|
2
|
-
import { adjustBrightness, adjustContrast, adjustExposure, adjustSaturation, composite, crop, fillRect, grayscale, invert, } from "./utils/image_processing.js";
|
|
2
|
+
import { adjustBrightness, adjustContrast, adjustExposure, adjustSaturation, boxBlur, composite, crop, fillRect, gaussianBlur, grayscale, invert, medianFilter, sepia, sharpen, } from "./utils/image_processing.js";
|
|
3
3
|
import { PNGFormat } from "./formats/png.js";
|
|
4
|
+
import { APNGFormat } from "./formats/apng.js";
|
|
4
5
|
import { JPEGFormat } from "./formats/jpeg.js";
|
|
5
6
|
import { WebPFormat } from "./formats/webp.js";
|
|
6
7
|
import { GIFFormat } from "./formats/gif.js";
|
|
7
8
|
import { TIFFFormat } from "./formats/tiff.js";
|
|
8
9
|
import { BMPFormat } from "./formats/bmp.js";
|
|
10
|
+
import { ICOFormat } from "./formats/ico.js";
|
|
9
11
|
import { DNGFormat } from "./formats/dng.js";
|
|
10
12
|
import { PAMFormat } from "./formats/pam.js";
|
|
11
13
|
import { PCXFormat } from "./formats/pcx.js";
|
|
14
|
+
import { PPMFormat } from "./formats/ppm.js";
|
|
12
15
|
import { ASCIIFormat } from "./formats/ascii.js";
|
|
13
16
|
import { validateImageDimensions } from "./utils/security.js";
|
|
14
17
|
/**
|
|
@@ -456,6 +459,61 @@ export class Image {
|
|
|
456
459
|
this.imageData.data = grayscale(this.imageData.data);
|
|
457
460
|
return this;
|
|
458
461
|
}
|
|
462
|
+
/**
|
|
463
|
+
* Apply sepia tone effect to the image
|
|
464
|
+
* @returns This image instance for chaining
|
|
465
|
+
*/
|
|
466
|
+
sepia() {
|
|
467
|
+
if (!this.imageData)
|
|
468
|
+
throw new Error("No image loaded");
|
|
469
|
+
this.imageData.data = sepia(this.imageData.data);
|
|
470
|
+
return this;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Apply box blur filter to the image
|
|
474
|
+
* @param radius Blur radius (default: 1)
|
|
475
|
+
* @returns This image instance for chaining
|
|
476
|
+
*/
|
|
477
|
+
blur(radius = 1) {
|
|
478
|
+
if (!this.imageData)
|
|
479
|
+
throw new Error("No image loaded");
|
|
480
|
+
this.imageData.data = boxBlur(this.imageData.data, this.imageData.width, this.imageData.height, radius);
|
|
481
|
+
return this;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Apply Gaussian blur filter to the image
|
|
485
|
+
* @param radius Blur radius (default: 1)
|
|
486
|
+
* @param sigma Optional standard deviation (if not provided, calculated from radius)
|
|
487
|
+
* @returns This image instance for chaining
|
|
488
|
+
*/
|
|
489
|
+
gaussianBlur(radius = 1, sigma) {
|
|
490
|
+
if (!this.imageData)
|
|
491
|
+
throw new Error("No image loaded");
|
|
492
|
+
this.imageData.data = gaussianBlur(this.imageData.data, this.imageData.width, this.imageData.height, radius, sigma);
|
|
493
|
+
return this;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Apply sharpen filter to the image
|
|
497
|
+
* @param amount Sharpening amount (0-1, default: 0.5)
|
|
498
|
+
* @returns This image instance for chaining
|
|
499
|
+
*/
|
|
500
|
+
sharpen(amount = 0.5) {
|
|
501
|
+
if (!this.imageData)
|
|
502
|
+
throw new Error("No image loaded");
|
|
503
|
+
this.imageData.data = sharpen(this.imageData.data, this.imageData.width, this.imageData.height, amount);
|
|
504
|
+
return this;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Apply median filter to reduce noise
|
|
508
|
+
* @param radius Filter radius (default: 1)
|
|
509
|
+
* @returns This image instance for chaining
|
|
510
|
+
*/
|
|
511
|
+
medianFilter(radius = 1) {
|
|
512
|
+
if (!this.imageData)
|
|
513
|
+
throw new Error("No image loaded");
|
|
514
|
+
this.imageData.data = medianFilter(this.imageData.data, this.imageData.width, this.imageData.height, radius);
|
|
515
|
+
return this;
|
|
516
|
+
}
|
|
459
517
|
/**
|
|
460
518
|
* Fill a rectangular region with a color
|
|
461
519
|
* @param x Starting X position
|
|
@@ -551,14 +609,17 @@ Object.defineProperty(Image, "formats", {
|
|
|
551
609
|
writable: true,
|
|
552
610
|
value: [
|
|
553
611
|
new PNGFormat(),
|
|
612
|
+
new APNGFormat(),
|
|
554
613
|
new JPEGFormat(),
|
|
555
614
|
new WebPFormat(),
|
|
556
615
|
new GIFFormat(),
|
|
557
616
|
new TIFFFormat(),
|
|
558
617
|
new BMPFormat(),
|
|
618
|
+
new ICOFormat(),
|
|
559
619
|
new DNGFormat(),
|
|
560
620
|
new PAMFormat(),
|
|
561
621
|
new PCXFormat(),
|
|
622
|
+
new PPMFormat(),
|
|
562
623
|
new ASCIIFormat(),
|
|
563
624
|
]
|
|
564
625
|
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared byte-level read/write utilities for image formats
|
|
3
|
+
* These functions handle reading and writing multi-byte integers
|
|
4
|
+
* in little-endian byte order, commonly used in BMP, ICO, GIF, and other formats.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Read a 16-bit unsigned integer in little-endian format
|
|
8
|
+
*/
|
|
9
|
+
export declare function readUint16LE(data: Uint8Array, offset: number): number;
|
|
10
|
+
/**
|
|
11
|
+
* Read a 32-bit unsigned integer in little-endian format
|
|
12
|
+
*/
|
|
13
|
+
export declare function readUint32LE(data: Uint8Array, offset: number): number;
|
|
14
|
+
/**
|
|
15
|
+
* Read a 32-bit signed integer in little-endian format
|
|
16
|
+
*/
|
|
17
|
+
export declare function readInt32LE(data: Uint8Array, offset: number): number;
|
|
18
|
+
/**
|
|
19
|
+
* Write a 16-bit unsigned integer in little-endian format
|
|
20
|
+
*/
|
|
21
|
+
export declare function writeUint16LE(data: Uint8Array, offset: number, value: number): void;
|
|
22
|
+
/**
|
|
23
|
+
* Write a 32-bit unsigned integer in little-endian format
|
|
24
|
+
*/
|
|
25
|
+
export declare function writeUint32LE(data: Uint8Array, offset: number, value: number): void;
|
|
26
|
+
/**
|
|
27
|
+
* Write a 32-bit signed integer in little-endian format
|
|
28
|
+
*/
|
|
29
|
+
export declare function writeInt32LE(data: Uint8Array, offset: number, value: number): void;
|
|
30
|
+
//# sourceMappingURL=byte_utils.d.ts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared byte-level read/write utilities for image formats
|
|
3
|
+
* These functions handle reading and writing multi-byte integers
|
|
4
|
+
* in little-endian byte order, commonly used in BMP, ICO, GIF, and other formats.
|
|
5
|
+
*/
|
|
6
|
+
// Constants for signed/unsigned integer conversion
|
|
7
|
+
const INT32_MAX = 0x7fffffff;
|
|
8
|
+
const UINT32_RANGE = 0x100000000;
|
|
9
|
+
/**
|
|
10
|
+
* Read a 16-bit unsigned integer in little-endian format
|
|
11
|
+
*/
|
|
12
|
+
export function readUint16LE(data, offset) {
|
|
13
|
+
return data[offset] | (data[offset + 1] << 8);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Read a 32-bit unsigned integer in little-endian format
|
|
17
|
+
*/
|
|
18
|
+
export function readUint32LE(data, offset) {
|
|
19
|
+
return data[offset] | (data[offset + 1] << 8) |
|
|
20
|
+
(data[offset + 2] << 16) | (data[offset + 3] << 24);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Read a 32-bit signed integer in little-endian format
|
|
24
|
+
*/
|
|
25
|
+
export function readInt32LE(data, offset) {
|
|
26
|
+
const value = readUint32LE(data, offset);
|
|
27
|
+
return value > INT32_MAX ? value - UINT32_RANGE : value;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Write a 16-bit unsigned integer in little-endian format
|
|
31
|
+
*/
|
|
32
|
+
export function writeUint16LE(data, offset, value) {
|
|
33
|
+
data[offset] = value & 0xff;
|
|
34
|
+
data[offset + 1] = (value >>> 8) & 0xff;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Write a 32-bit unsigned integer in little-endian format
|
|
38
|
+
*/
|
|
39
|
+
export function writeUint32LE(data, offset, value) {
|
|
40
|
+
data[offset] = value & 0xff;
|
|
41
|
+
data[offset + 1] = (value >>> 8) & 0xff;
|
|
42
|
+
data[offset + 2] = (value >>> 16) & 0xff;
|
|
43
|
+
data[offset + 3] = (value >>> 24) & 0xff;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Write a 32-bit signed integer in little-endian format
|
|
47
|
+
*/
|
|
48
|
+
export function writeInt32LE(data, offset, value) {
|
|
49
|
+
writeUint32LE(data, offset, value < 0 ? value + UINT32_RANGE : value);
|
|
50
|
+
}
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
export declare class GIFEncoder {
|
|
6
6
|
private width;
|
|
7
7
|
private height;
|
|
8
|
-
private
|
|
9
|
-
constructor(width: number, height: number, data
|
|
8
|
+
private frames;
|
|
9
|
+
constructor(width: number, height: number, data?: Uint8Array);
|
|
10
|
+
addFrame(data: Uint8Array, delay?: number): void;
|
|
10
11
|
private writeBytes;
|
|
11
12
|
private writeUint16LE;
|
|
12
13
|
private writeString;
|
|
@@ -17,15 +17,20 @@ export class GIFEncoder {
|
|
|
17
17
|
writable: true,
|
|
18
18
|
value: void 0
|
|
19
19
|
});
|
|
20
|
-
Object.defineProperty(this, "
|
|
20
|
+
Object.defineProperty(this, "frames", {
|
|
21
21
|
enumerable: true,
|
|
22
22
|
configurable: true,
|
|
23
23
|
writable: true,
|
|
24
|
-
value:
|
|
24
|
+
value: []
|
|
25
25
|
});
|
|
26
26
|
this.width = width;
|
|
27
27
|
this.height = height;
|
|
28
|
-
|
|
28
|
+
if (data) {
|
|
29
|
+
this.addFrame(data);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
addFrame(data, delay = 0) {
|
|
33
|
+
this.frames.push({ data, delay });
|
|
29
34
|
}
|
|
30
35
|
writeBytes(output, bytes) {
|
|
31
36
|
output.push(...bytes);
|
|
@@ -54,7 +59,7 @@ export class GIFEncoder {
|
|
|
54
59
|
/**
|
|
55
60
|
* Quantize RGBA image to 256 colors using median cut algorithm
|
|
56
61
|
*/
|
|
57
|
-
quantize() {
|
|
62
|
+
quantize(data) {
|
|
58
63
|
// Simple quantization: collect unique colors and build palette
|
|
59
64
|
const colorMap = new Map();
|
|
60
65
|
const colors = [];
|
|
@@ -65,10 +70,10 @@ export class GIFEncoder {
|
|
|
65
70
|
const rgStep = 255 / RG_LEVELS; // Step size for R/G quantization
|
|
66
71
|
const bStep = 255 / B_LEVELS; // Step size for B quantization
|
|
67
72
|
// Collect unique colors
|
|
68
|
-
for (let i = 0; i <
|
|
69
|
-
const r =
|
|
70
|
-
const g =
|
|
71
|
-
const b =
|
|
73
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
74
|
+
const r = data[i];
|
|
75
|
+
const g = data[i + 1];
|
|
76
|
+
const b = data[i + 2];
|
|
72
77
|
const key = `${r},${g},${b}`;
|
|
73
78
|
if (!colorMap.has(key) && colors.length < 256) {
|
|
74
79
|
colorMap.set(key, colors.length);
|
|
@@ -83,12 +88,12 @@ export class GIFEncoder {
|
|
|
83
88
|
colorMap.clear();
|
|
84
89
|
colors.length = 0;
|
|
85
90
|
useColorReduction = true;
|
|
86
|
-
for (let i = 0; i <
|
|
91
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
87
92
|
// Reduce color depth: 3 bits for R/G channels, 2 bits for B channel
|
|
88
93
|
// This gives us 8 bits total = 256 possible colors
|
|
89
|
-
const r = this.quantizeChannel(
|
|
90
|
-
const g = this.quantizeChannel(
|
|
91
|
-
const b = this.quantizeChannel(
|
|
94
|
+
const r = this.quantizeChannel(data[i], RG_LEVELS, rgStep);
|
|
95
|
+
const g = this.quantizeChannel(data[i + 1], RG_LEVELS, rgStep);
|
|
96
|
+
const b = this.quantizeChannel(data[i + 2], B_LEVELS, bStep);
|
|
92
97
|
const key = `${r},${g},${b}`;
|
|
93
98
|
if (!colorMap.has(key)) {
|
|
94
99
|
if (colors.length < 256) {
|
|
@@ -112,10 +117,10 @@ export class GIFEncoder {
|
|
|
112
117
|
}
|
|
113
118
|
// Create indexed data
|
|
114
119
|
const indexed = new Uint8Array(this.width * this.height);
|
|
115
|
-
for (let i = 0, j = 0; i <
|
|
116
|
-
let r =
|
|
117
|
-
let g =
|
|
118
|
-
let b =
|
|
120
|
+
for (let i = 0, j = 0; i < data.length; i += 4, j++) {
|
|
121
|
+
let r = data[i];
|
|
122
|
+
let g = data[i + 1];
|
|
123
|
+
let b = data[i + 2];
|
|
119
124
|
// Apply color reduction if it was used for building the palette
|
|
120
125
|
if (useColorReduction) {
|
|
121
126
|
r = this.quantizeChannel(r, RG_LEVELS, rgStep);
|
|
@@ -161,10 +166,14 @@ export class GIFEncoder {
|
|
|
161
166
|
return Math.max(2, bits);
|
|
162
167
|
}
|
|
163
168
|
encode() {
|
|
169
|
+
if (this.frames.length === 0) {
|
|
170
|
+
throw new Error("No frames to encode");
|
|
171
|
+
}
|
|
164
172
|
const output = [];
|
|
165
|
-
// Quantize
|
|
166
|
-
const
|
|
167
|
-
const
|
|
173
|
+
// Quantize first frame for Global Color Table
|
|
174
|
+
const firstFrame = this.frames[0];
|
|
175
|
+
const { palette: globalPalette, indexed: firstIndexed } = this.quantize(firstFrame.data);
|
|
176
|
+
const paletteSize = globalPalette.length / 3;
|
|
168
177
|
const bitsPerColor = this.getBitsPerColor(paletteSize);
|
|
169
178
|
// Header
|
|
170
179
|
this.writeString(output, "GIF89a");
|
|
@@ -187,38 +196,96 @@ export class GIFEncoder {
|
|
|
187
196
|
// So we need to write that many colors, padding if necessary
|
|
188
197
|
const gctSize = 1 << bitsPerColor;
|
|
189
198
|
const paddedPalette = new Uint8Array(gctSize * 3);
|
|
190
|
-
paddedPalette.set(
|
|
199
|
+
paddedPalette.set(globalPalette);
|
|
191
200
|
this.writeBytes(output, paddedPalette);
|
|
192
|
-
//
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
201
|
+
// Netscape Application Extension (Looping)
|
|
202
|
+
if (this.frames.length > 1) {
|
|
203
|
+
output.push(0x21); // Extension Introducer
|
|
204
|
+
output.push(0xff); // Application Extension Label
|
|
205
|
+
output.push(11); // Block Size
|
|
206
|
+
this.writeString(output, "NETSCAPE2.0");
|
|
207
|
+
output.push(3); // Sub-block Size
|
|
208
|
+
output.push(1); // Loop Indicator (1 = loop)
|
|
209
|
+
this.writeUint16LE(output, 0); // Loop Count (0 = infinite)
|
|
210
|
+
output.push(0); // Block Terminator
|
|
211
|
+
}
|
|
212
|
+
// Encode frames
|
|
213
|
+
for (let i = 0; i < this.frames.length; i++) {
|
|
214
|
+
const frame = this.frames[i];
|
|
215
|
+
let indexed;
|
|
216
|
+
let useLocalPalette = false;
|
|
217
|
+
let localPalette = null;
|
|
218
|
+
let localBitsPerColor = bitsPerColor;
|
|
219
|
+
if (i === 0) {
|
|
220
|
+
indexed = firstIndexed;
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
// Quantize subsequent frames
|
|
224
|
+
// For simplicity, we use a Local Color Table for each frame to ensure colors are correct
|
|
225
|
+
const result = this.quantize(frame.data);
|
|
226
|
+
indexed = result.indexed;
|
|
227
|
+
localPalette = result.palette;
|
|
228
|
+
useLocalPalette = true;
|
|
229
|
+
const localPaletteSize = localPalette.length / 3;
|
|
230
|
+
localBitsPerColor = this.getBitsPerColor(localPaletteSize);
|
|
218
231
|
}
|
|
232
|
+
// Graphic Control Extension
|
|
233
|
+
output.push(0x21); // Extension Introducer
|
|
234
|
+
output.push(0xf9); // Graphic Control Label
|
|
235
|
+
output.push(4); // Byte Size
|
|
236
|
+
// Packed Field
|
|
237
|
+
// Reserved (3 bits)
|
|
238
|
+
// Disposal Method (3 bits): 2 (Restore to background) - usually safe for animation
|
|
239
|
+
// User Input Flag (1 bit): 0
|
|
240
|
+
// Transparent Color Flag (1 bit): 0
|
|
241
|
+
output.push(0x08); // Disposal method 2 (Restore to background)
|
|
242
|
+
// Delay Time (1/100ths of a second)
|
|
243
|
+
// Default to 10 (100ms) if not specified
|
|
244
|
+
const delay = frame.delay > 0 ? Math.round(frame.delay / 10) : 10;
|
|
245
|
+
this.writeUint16LE(output, delay);
|
|
246
|
+
// Transparent Color Index
|
|
247
|
+
output.push(0);
|
|
248
|
+
output.push(0); // Block Terminator
|
|
249
|
+
// Image Descriptor
|
|
250
|
+
output.push(0x2c); // Image Separator
|
|
251
|
+
this.writeUint16LE(output, 0); // Left
|
|
252
|
+
this.writeUint16LE(output, 0); // Top
|
|
253
|
+
this.writeUint16LE(output, this.width);
|
|
254
|
+
this.writeUint16LE(output, this.height);
|
|
255
|
+
// Packed Field
|
|
256
|
+
if (useLocalPalette && localPalette) {
|
|
257
|
+
// LCT Flag: 1
|
|
258
|
+
// Interlace: 0
|
|
259
|
+
// Sort: 0
|
|
260
|
+
// Reserved: 0
|
|
261
|
+
// Size of LCT: localBitsPerColor - 1
|
|
262
|
+
const lctPacked = 0x80 | (localBitsPerColor - 1);
|
|
263
|
+
output.push(lctPacked);
|
|
264
|
+
// Write Local Color Table
|
|
265
|
+
const lctSize = 1 << localBitsPerColor;
|
|
266
|
+
const paddedLct = new Uint8Array(lctSize * 3);
|
|
267
|
+
paddedLct.set(localPalette);
|
|
268
|
+
this.writeBytes(output, paddedLct);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
output.push(0); // No LCT
|
|
272
|
+
}
|
|
273
|
+
// LZW Minimum Code Size
|
|
274
|
+
const minCodeSize = Math.max(2, useLocalPalette ? localBitsPerColor : bitsPerColor);
|
|
275
|
+
output.push(minCodeSize);
|
|
276
|
+
// Compress image data with LZW
|
|
277
|
+
const encoder = new LZWEncoder(minCodeSize);
|
|
278
|
+
const compressed = encoder.compress(indexed);
|
|
279
|
+
// Write compressed data in sub-blocks (max 255 bytes per block)
|
|
280
|
+
for (let k = 0; k < compressed.length; k += 255) {
|
|
281
|
+
const blockSize = Math.min(255, compressed.length - k);
|
|
282
|
+
output.push(blockSize);
|
|
283
|
+
for (let j = 0; j < blockSize; j++) {
|
|
284
|
+
output.push(compressed[k + j]);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
output.push(0); // Block Terminator
|
|
219
288
|
}
|
|
220
|
-
// Block Terminator
|
|
221
|
-
output.push(0);
|
|
222
289
|
// Trailer
|
|
223
290
|
output.push(0x3b);
|
|
224
291
|
return new Uint8Array(output);
|
|
@@ -88,4 +88,47 @@ export declare function crop(data: Uint8Array, width: number, height: number, x:
|
|
|
88
88
|
width: number;
|
|
89
89
|
height: number;
|
|
90
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* Apply a box blur filter to an image
|
|
93
|
+
* @param data Image data (RGBA)
|
|
94
|
+
* @param width Image width
|
|
95
|
+
* @param height Image height
|
|
96
|
+
* @param radius Blur radius (default: 1)
|
|
97
|
+
* @returns New image data with box blur applied
|
|
98
|
+
*/
|
|
99
|
+
export declare function boxBlur(data: Uint8Array, width: number, height: number, radius?: number): Uint8Array;
|
|
100
|
+
/**
|
|
101
|
+
* Apply Gaussian blur to an image
|
|
102
|
+
* @param data Image data (RGBA)
|
|
103
|
+
* @param width Image width
|
|
104
|
+
* @param height Image height
|
|
105
|
+
* @param radius Blur radius (default: 1)
|
|
106
|
+
* @param sigma Optional standard deviation (if not provided, calculated from radius)
|
|
107
|
+
* @returns New image data with Gaussian blur applied
|
|
108
|
+
*/
|
|
109
|
+
export declare function gaussianBlur(data: Uint8Array, width: number, height: number, radius?: number, sigma?: number): Uint8Array;
|
|
110
|
+
/**
|
|
111
|
+
* Apply sharpen filter to an image
|
|
112
|
+
* @param data Image data (RGBA)
|
|
113
|
+
* @param width Image width
|
|
114
|
+
* @param height Image height
|
|
115
|
+
* @param amount Sharpening amount (0-1, default: 0.5)
|
|
116
|
+
* @returns New image data with sharpening applied
|
|
117
|
+
*/
|
|
118
|
+
export declare function sharpen(data: Uint8Array, width: number, height: number, amount?: number): Uint8Array;
|
|
119
|
+
/**
|
|
120
|
+
* Apply sepia tone effect to an image
|
|
121
|
+
* @param data Image data (RGBA)
|
|
122
|
+
* @returns New image data with sepia tone applied
|
|
123
|
+
*/
|
|
124
|
+
export declare function sepia(data: Uint8Array): Uint8Array;
|
|
125
|
+
/**
|
|
126
|
+
* Apply median filter to reduce noise
|
|
127
|
+
* @param data Image data (RGBA)
|
|
128
|
+
* @param width Image width
|
|
129
|
+
* @param height Image height
|
|
130
|
+
* @param radius Filter radius (default: 1)
|
|
131
|
+
* @returns New image data with median filter applied
|
|
132
|
+
*/
|
|
133
|
+
export declare function medianFilter(data: Uint8Array, width: number, height: number, radius?: number): Uint8Array;
|
|
91
134
|
//# sourceMappingURL=image_processing.d.ts.map
|