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