cross-image 0.1.5 → 0.2.0
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 +17 -5
- package/esm/mod.d.ts +28 -4
- package/esm/mod.js +28 -4
- package/esm/src/formats/dng.d.ts +27 -0
- package/esm/src/formats/dng.js +191 -0
- package/esm/src/formats/pam.d.ts +43 -0
- package/esm/src/formats/pam.js +177 -0
- package/esm/src/formats/pcx.d.ts +13 -0
- package/esm/src/formats/pcx.js +204 -0
- package/esm/src/formats/tiff.d.ts +7 -7
- package/esm/src/image.d.ts +99 -0
- package/esm/src/image.js +200 -2
- package/esm/src/utils/image_processing.d.ts +91 -0
- package/esm/src/utils/image_processing.js +231 -0
- package/esm/src/utils/webp_decoder.js +47 -12
- package/esm/src/utils/webp_encoder.js +97 -39
- package/package.json +4 -1
- package/script/mod.d.ts +28 -4
- package/script/mod.js +32 -6
- package/script/src/formats/dng.d.ts +27 -0
- package/script/src/formats/dng.js +195 -0
- package/script/src/formats/pam.d.ts +43 -0
- package/script/src/formats/pam.js +181 -0
- package/script/src/formats/pcx.d.ts +13 -0
- package/script/src/formats/pcx.js +208 -0
- package/script/src/formats/tiff.d.ts +7 -7
- package/script/src/image.d.ts +99 -0
- package/script/src/image.js +200 -2
- package/script/src/utils/image_processing.d.ts +91 -0
- package/script/src/utils/image_processing.js +242 -0
- package/script/src/utils/webp_decoder.js +47 -12
- package/script/src/utils/webp_encoder.js +97 -39
- package/esm/src/formats/raw.d.ts +0 -40
- package/esm/src/formats/raw.js +0 -118
- package/script/src/formats/raw.d.ts +0 -40
- package/script/src/formats/raw.js +0 -122
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PCXFormat = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* PCX format handler
|
|
6
|
+
* Implements PCX decoder and encoder
|
|
7
|
+
*/
|
|
8
|
+
class PCXFormat {
|
|
9
|
+
constructor() {
|
|
10
|
+
Object.defineProperty(this, "name", {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
writable: true,
|
|
14
|
+
value: "pcx"
|
|
15
|
+
});
|
|
16
|
+
Object.defineProperty(this, "mimeType", {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
configurable: true,
|
|
19
|
+
writable: true,
|
|
20
|
+
value: "image/x-pcx"
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
canDecode(data) {
|
|
24
|
+
// PCX header check
|
|
25
|
+
// Byte 0: Manufacturer (must be 0x0A)
|
|
26
|
+
// Byte 1: Version (0, 2, 3, 4, 5)
|
|
27
|
+
// Byte 2: Encoding (1 = RLE)
|
|
28
|
+
return data.length >= 128 &&
|
|
29
|
+
data[0] === 0x0A &&
|
|
30
|
+
(data[1] === 0 || data[1] === 2 || data[1] === 3 || data[1] === 4 ||
|
|
31
|
+
data[1] === 5) &&
|
|
32
|
+
data[2] === 1;
|
|
33
|
+
}
|
|
34
|
+
decode(data) {
|
|
35
|
+
if (!this.canDecode(data)) {
|
|
36
|
+
return Promise.reject(new Error("Invalid PCX data"));
|
|
37
|
+
}
|
|
38
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
39
|
+
// Parse header
|
|
40
|
+
// const manufacturer = view.getUint8(0);
|
|
41
|
+
// const version = view.getUint8(1);
|
|
42
|
+
// const encoding = view.getUint8(2);
|
|
43
|
+
const bitsPerPixel = view.getUint8(3);
|
|
44
|
+
const xMin = view.getUint16(4, true);
|
|
45
|
+
const yMin = view.getUint16(6, true);
|
|
46
|
+
const xMax = view.getUint16(8, true);
|
|
47
|
+
const yMax = view.getUint16(10, true);
|
|
48
|
+
// const hDpi = view.getUint16(12, true);
|
|
49
|
+
// const vDpi = view.getUint16(14, true);
|
|
50
|
+
// const colormap = data.slice(16, 64);
|
|
51
|
+
// const reserved = view.getUint8(64);
|
|
52
|
+
const nPlanes = view.getUint8(65);
|
|
53
|
+
const bytesPerLine = view.getUint16(66, true);
|
|
54
|
+
// const paletteInfo = view.getUint16(68, true);
|
|
55
|
+
// const hScreenSize = view.getUint16(70, true);
|
|
56
|
+
// const vScreenSize = view.getUint16(72, true);
|
|
57
|
+
const width = xMax - xMin + 1;
|
|
58
|
+
const height = yMax - yMin + 1;
|
|
59
|
+
if (width <= 0 || height <= 0) {
|
|
60
|
+
return Promise.reject(new Error("Invalid PCX dimensions"));
|
|
61
|
+
}
|
|
62
|
+
// Decode RLE data
|
|
63
|
+
let offset = 128;
|
|
64
|
+
const scanlineLength = nPlanes * bytesPerLine;
|
|
65
|
+
const rawData = new Uint8Array(height * scanlineLength);
|
|
66
|
+
let ptr = 0;
|
|
67
|
+
// Decode all scanlines
|
|
68
|
+
for (let y = 0; y < height; y++) {
|
|
69
|
+
let x = 0;
|
|
70
|
+
while (x < scanlineLength) {
|
|
71
|
+
if (offset >= data.length)
|
|
72
|
+
break;
|
|
73
|
+
let byte = data[offset++];
|
|
74
|
+
let count = 1;
|
|
75
|
+
if ((byte & 0xC0) === 0xC0) {
|
|
76
|
+
count = byte & 0x3F;
|
|
77
|
+
if (offset >= data.length)
|
|
78
|
+
break;
|
|
79
|
+
byte = data[offset++];
|
|
80
|
+
}
|
|
81
|
+
for (let i = 0; i < count; i++) {
|
|
82
|
+
if (ptr < rawData.length) {
|
|
83
|
+
rawData[ptr++] = byte;
|
|
84
|
+
}
|
|
85
|
+
x++;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const rgba = new Uint8Array(width * height * 4);
|
|
90
|
+
if (nPlanes === 3 && bitsPerPixel === 8) {
|
|
91
|
+
// 24-bit RGB
|
|
92
|
+
for (let y = 0; y < height; y++) {
|
|
93
|
+
for (let x = 0; x < width; x++) {
|
|
94
|
+
const r = rawData[y * scanlineLength + x];
|
|
95
|
+
const g = rawData[y * scanlineLength + bytesPerLine + x];
|
|
96
|
+
const b = rawData[y * scanlineLength + 2 * bytesPerLine + x];
|
|
97
|
+
const idx = (y * width + x) * 4;
|
|
98
|
+
rgba[idx] = r;
|
|
99
|
+
rgba[idx + 1] = g;
|
|
100
|
+
rgba[idx + 2] = b;
|
|
101
|
+
rgba[idx + 3] = 255;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else if (nPlanes === 1 && bitsPerPixel === 8) {
|
|
106
|
+
// 8-bit palette
|
|
107
|
+
// Check for palette at end of file
|
|
108
|
+
let palette;
|
|
109
|
+
if (data[data.length - 769] === 0x0C) {
|
|
110
|
+
palette = data.slice(data.length - 768);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Fallback or error?
|
|
114
|
+
// Some old PCX might use header palette but that's only 16 colors.
|
|
115
|
+
// For 8bpp we expect 256 color palette at end.
|
|
116
|
+
return Promise.reject(new Error("Missing PCX palette"));
|
|
117
|
+
}
|
|
118
|
+
for (let y = 0; y < height; y++) {
|
|
119
|
+
for (let x = 0; x < width; x++) {
|
|
120
|
+
const colorIndex = rawData[y * scanlineLength + x];
|
|
121
|
+
const idx = (y * width + x) * 4;
|
|
122
|
+
rgba[idx] = palette[colorIndex * 3];
|
|
123
|
+
rgba[idx + 1] = palette[colorIndex * 3 + 1];
|
|
124
|
+
rgba[idx + 2] = palette[colorIndex * 3 + 2];
|
|
125
|
+
rgba[idx + 3] = 255;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Unsupported PCX format (e.g. 1-bit, 4-bit)
|
|
131
|
+
// For now only supporting 24-bit and 8-bit
|
|
132
|
+
return Promise.reject(new Error("Unsupported PCX format"));
|
|
133
|
+
}
|
|
134
|
+
return Promise.resolve({
|
|
135
|
+
width,
|
|
136
|
+
height,
|
|
137
|
+
data: rgba,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
encode(image) {
|
|
141
|
+
const width = image.width;
|
|
142
|
+
const height = image.height;
|
|
143
|
+
const data = image.data;
|
|
144
|
+
// We will encode as 24-bit RGB (3 planes)
|
|
145
|
+
const header = new Uint8Array(128);
|
|
146
|
+
const view = new DataView(header.buffer);
|
|
147
|
+
view.setUint8(0, 0x0A); // Manufacturer
|
|
148
|
+
view.setUint8(1, 5); // Version 3.0+
|
|
149
|
+
view.setUint8(2, 1); // Encoding RLE
|
|
150
|
+
view.setUint8(3, 8); // Bits per pixel (8 per plane)
|
|
151
|
+
view.setUint16(4, 0, true); // XMin
|
|
152
|
+
view.setUint16(6, 0, true); // YMin
|
|
153
|
+
view.setUint16(8, width - 1, true); // XMax
|
|
154
|
+
view.setUint16(10, height - 1, true); // YMax
|
|
155
|
+
view.setUint16(12, 72, true); // HDpi
|
|
156
|
+
view.setUint16(14, 72, true); // VDpi
|
|
157
|
+
view.setUint8(65, 3); // NPlanes (3 for RGB)
|
|
158
|
+
view.setUint16(66, width + (width % 2), true); // BytesPerLine (must be even)
|
|
159
|
+
view.setUint16(68, 1, true); // PaletteInfo (Color/BW)
|
|
160
|
+
const bytesPerLine = width + (width % 2);
|
|
161
|
+
const scanlineLength = bytesPerLine * 3;
|
|
162
|
+
const rleData = [];
|
|
163
|
+
// Helper to write RLE
|
|
164
|
+
const writeRLE = (byte, count) => {
|
|
165
|
+
if ((byte & 0xC0) === 0xC0 || count > 1) {
|
|
166
|
+
rleData.push(0xC0 | count);
|
|
167
|
+
rleData.push(byte);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
rleData.push(byte);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
for (let y = 0; y < height; y++) {
|
|
174
|
+
// Prepare scanline planes
|
|
175
|
+
const lineR = new Uint8Array(bytesPerLine);
|
|
176
|
+
const lineG = new Uint8Array(bytesPerLine);
|
|
177
|
+
const lineB = new Uint8Array(bytesPerLine);
|
|
178
|
+
for (let x = 0; x < width; x++) {
|
|
179
|
+
const idx = (y * width + x) * 4;
|
|
180
|
+
lineR[x] = data[idx];
|
|
181
|
+
lineG[x] = data[idx + 1];
|
|
182
|
+
lineB[x] = data[idx + 2];
|
|
183
|
+
}
|
|
184
|
+
// Compress each plane
|
|
185
|
+
for (const plane of [lineR, lineG, lineB]) {
|
|
186
|
+
let currentByte = plane[0];
|
|
187
|
+
let runLength = 1;
|
|
188
|
+
for (let x = 1; x < bytesPerLine; x++) {
|
|
189
|
+
const byte = plane[x];
|
|
190
|
+
if (byte === currentByte && runLength < 63) {
|
|
191
|
+
runLength++;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
writeRLE(currentByte, runLength);
|
|
195
|
+
currentByte = byte;
|
|
196
|
+
runLength = 1;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
writeRLE(currentByte, runLength);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const result = new Uint8Array(header.length + rleData.length);
|
|
203
|
+
result.set(header);
|
|
204
|
+
result.set(rleData, header.length);
|
|
205
|
+
return Promise.resolve(result);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
exports.PCXFormat = PCXFormat;
|
|
@@ -15,9 +15,9 @@ export interface TIFFEncodeOptions {
|
|
|
15
15
|
*/
|
|
16
16
|
export declare class TIFFFormat implements ImageFormat {
|
|
17
17
|
/** Format name identifier */
|
|
18
|
-
readonly name
|
|
18
|
+
readonly name: string;
|
|
19
19
|
/** MIME type for TIFF images */
|
|
20
|
-
readonly mimeType
|
|
20
|
+
readonly mimeType: string;
|
|
21
21
|
/**
|
|
22
22
|
* Check if this format supports multiple frames (pages)
|
|
23
23
|
* @returns true for TIFF format
|
|
@@ -52,11 +52,11 @@ export declare class TIFFFormat implements ImageFormat {
|
|
|
52
52
|
* Extract metadata from an IFD
|
|
53
53
|
*/
|
|
54
54
|
private extractMetadataFromIFD;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
protected readUint16(data: Uint8Array, offset: number, isLittleEndian: boolean): number;
|
|
56
|
+
protected readUint32(data: Uint8Array, offset: number, isLittleEndian: boolean): number;
|
|
57
|
+
protected writeUint16LE(result: number[], value: number): void;
|
|
58
|
+
protected writeUint32LE(result: number[], value: number): void;
|
|
59
|
+
protected writeIFDEntry(result: number[], tag: number, type: number, count: number, valueOrOffset: number): void;
|
|
60
60
|
private getIFDValue;
|
|
61
61
|
private decodeUsingRuntime;
|
|
62
62
|
private readString;
|
package/script/src/image.d.ts
CHANGED
|
@@ -127,6 +127,17 @@ export declare class Image {
|
|
|
127
127
|
* @returns Image instance
|
|
128
128
|
*/
|
|
129
129
|
static fromRGBA(width: number, height: number, data: Uint8Array): Image;
|
|
130
|
+
/**
|
|
131
|
+
* Create a blank image with the specified dimensions and color
|
|
132
|
+
* @param width Image width
|
|
133
|
+
* @param height Image height
|
|
134
|
+
* @param r Red component (0-255, default: 0)
|
|
135
|
+
* @param g Green component (0-255, default: 0)
|
|
136
|
+
* @param b Blue component (0-255, default: 0)
|
|
137
|
+
* @param a Alpha component (0-255, default: 255)
|
|
138
|
+
* @returns Image instance
|
|
139
|
+
*/
|
|
140
|
+
static create(width: number, height: number, r?: number, g?: number, b?: number, a?: number): Image;
|
|
130
141
|
/**
|
|
131
142
|
* Resize the image
|
|
132
143
|
* @param options Resize options
|
|
@@ -153,5 +164,93 @@ export declare class Image {
|
|
|
153
164
|
* @returns New image instance with copied data and metadata
|
|
154
165
|
*/
|
|
155
166
|
clone(): Image;
|
|
167
|
+
/**
|
|
168
|
+
* Composite another image on top of this image at the specified position
|
|
169
|
+
* @param overlay Image to place on top
|
|
170
|
+
* @param x X position (can be negative)
|
|
171
|
+
* @param y Y position (can be negative)
|
|
172
|
+
* @param opacity Opacity of overlay (0-1, default: 1)
|
|
173
|
+
* @returns This image instance for chaining
|
|
174
|
+
*/
|
|
175
|
+
composite(overlay: Image, x: number, y: number, opacity?: number): this;
|
|
176
|
+
/**
|
|
177
|
+
* Adjust brightness of the image
|
|
178
|
+
* @param amount Brightness adjustment (-1 to 1, where 0 is no change)
|
|
179
|
+
* @returns This image instance for chaining
|
|
180
|
+
*/
|
|
181
|
+
brightness(amount: number): this;
|
|
182
|
+
/**
|
|
183
|
+
* Adjust contrast of the image
|
|
184
|
+
* @param amount Contrast adjustment (-1 to 1, where 0 is no change)
|
|
185
|
+
* @returns This image instance for chaining
|
|
186
|
+
*/
|
|
187
|
+
contrast(amount: number): this;
|
|
188
|
+
/**
|
|
189
|
+
* Adjust exposure of the image
|
|
190
|
+
* @param amount Exposure adjustment in stops (-3 to 3, where 0 is no change)
|
|
191
|
+
* @returns This image instance for chaining
|
|
192
|
+
*/
|
|
193
|
+
exposure(amount: number): this;
|
|
194
|
+
/**
|
|
195
|
+
* Adjust saturation of the image
|
|
196
|
+
* @param amount Saturation adjustment (-1 to 1, where 0 is no change)
|
|
197
|
+
* @returns This image instance for chaining
|
|
198
|
+
*/
|
|
199
|
+
saturation(amount: number): this;
|
|
200
|
+
/**
|
|
201
|
+
* Invert colors of the image
|
|
202
|
+
* @returns This image instance for chaining
|
|
203
|
+
*/
|
|
204
|
+
invert(): this;
|
|
205
|
+
/**
|
|
206
|
+
* Convert the image to grayscale
|
|
207
|
+
* @returns This image instance for chaining
|
|
208
|
+
*/
|
|
209
|
+
grayscale(): this;
|
|
210
|
+
/**
|
|
211
|
+
* Fill a rectangular region with a color
|
|
212
|
+
* @param x Starting X position
|
|
213
|
+
* @param y Starting Y position
|
|
214
|
+
* @param width Width of the fill region
|
|
215
|
+
* @param height Height of the fill region
|
|
216
|
+
* @param r Red component (0-255)
|
|
217
|
+
* @param g Green component (0-255)
|
|
218
|
+
* @param b Blue component (0-255)
|
|
219
|
+
* @param a Alpha component (0-255, default: 255)
|
|
220
|
+
* @returns This image instance for chaining
|
|
221
|
+
*/
|
|
222
|
+
fillRect(x: number, y: number, width: number, height: number, r: number, g: number, b: number, a?: number): this;
|
|
223
|
+
/**
|
|
224
|
+
* Crop the image to a rectangular region
|
|
225
|
+
* @param x Starting X position
|
|
226
|
+
* @param y Starting Y position
|
|
227
|
+
* @param width Width of the crop region
|
|
228
|
+
* @param height Height of the crop region
|
|
229
|
+
* @returns This image instance for chaining
|
|
230
|
+
*/
|
|
231
|
+
crop(x: number, y: number, width: number, height: number): this;
|
|
232
|
+
/**
|
|
233
|
+
* Get the pixel color at the specified position
|
|
234
|
+
* @param x X position
|
|
235
|
+
* @param y Y position
|
|
236
|
+
* @returns Object with r, g, b, a components (0-255) or undefined if out of bounds
|
|
237
|
+
*/
|
|
238
|
+
getPixel(x: number, y: number): {
|
|
239
|
+
r: number;
|
|
240
|
+
g: number;
|
|
241
|
+
b: number;
|
|
242
|
+
a: number;
|
|
243
|
+
} | undefined;
|
|
244
|
+
/**
|
|
245
|
+
* Set the pixel color at the specified position
|
|
246
|
+
* @param x X position
|
|
247
|
+
* @param y Y position
|
|
248
|
+
* @param r Red component (0-255)
|
|
249
|
+
* @param g Green component (0-255)
|
|
250
|
+
* @param b Blue component (0-255)
|
|
251
|
+
* @param a Alpha component (0-255, default: 255)
|
|
252
|
+
* @returns This image instance for chaining
|
|
253
|
+
*/
|
|
254
|
+
setPixel(x: number, y: number, r: number, g: number, b: number, a?: number): this;
|
|
156
255
|
}
|
|
157
256
|
//# sourceMappingURL=image.d.ts.map
|
package/script/src/image.js
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Image = void 0;
|
|
4
4
|
const resize_js_1 = require("./utils/resize.js");
|
|
5
|
+
const image_processing_js_1 = require("./utils/image_processing.js");
|
|
5
6
|
const png_js_1 = require("./formats/png.js");
|
|
6
7
|
const jpeg_js_1 = require("./formats/jpeg.js");
|
|
7
8
|
const webp_js_1 = require("./formats/webp.js");
|
|
8
9
|
const gif_js_1 = require("./formats/gif.js");
|
|
9
10
|
const tiff_js_1 = require("./formats/tiff.js");
|
|
10
11
|
const bmp_js_1 = require("./formats/bmp.js");
|
|
11
|
-
const
|
|
12
|
+
const dng_js_1 = require("./formats/dng.js");
|
|
13
|
+
const pam_js_1 = require("./formats/pam.js");
|
|
14
|
+
const pcx_js_1 = require("./formats/pcx.js");
|
|
12
15
|
const ascii_js_1 = require("./formats/ascii.js");
|
|
13
16
|
const security_js_1 = require("./utils/security.js");
|
|
14
17
|
/**
|
|
@@ -265,6 +268,31 @@ class Image {
|
|
|
265
268
|
image.imageData = { width, height, data: new Uint8Array(data) };
|
|
266
269
|
return image;
|
|
267
270
|
}
|
|
271
|
+
/**
|
|
272
|
+
* Create a blank image with the specified dimensions and color
|
|
273
|
+
* @param width Image width
|
|
274
|
+
* @param height Image height
|
|
275
|
+
* @param r Red component (0-255, default: 0)
|
|
276
|
+
* @param g Green component (0-255, default: 0)
|
|
277
|
+
* @param b Blue component (0-255, default: 0)
|
|
278
|
+
* @param a Alpha component (0-255, default: 255)
|
|
279
|
+
* @returns Image instance
|
|
280
|
+
*/
|
|
281
|
+
static create(width, height, r = 0, g = 0, b = 0, a = 255) {
|
|
282
|
+
// Validate dimensions for security (prevent integer overflow and heap exhaustion)
|
|
283
|
+
(0, security_js_1.validateImageDimensions)(width, height);
|
|
284
|
+
const data = new Uint8Array(width * height * 4);
|
|
285
|
+
// Fill with the specified color
|
|
286
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
287
|
+
data[i] = r;
|
|
288
|
+
data[i + 1] = g;
|
|
289
|
+
data[i + 2] = b;
|
|
290
|
+
data[i + 3] = a;
|
|
291
|
+
}
|
|
292
|
+
const image = new Image();
|
|
293
|
+
image.imageData = { width, height, data };
|
|
294
|
+
return image;
|
|
295
|
+
}
|
|
268
296
|
/**
|
|
269
297
|
* Resize the image
|
|
270
298
|
* @param options Resize options
|
|
@@ -351,6 +379,174 @@ class Image {
|
|
|
351
379
|
};
|
|
352
380
|
return image;
|
|
353
381
|
}
|
|
382
|
+
/**
|
|
383
|
+
* Composite another image on top of this image at the specified position
|
|
384
|
+
* @param overlay Image to place on top
|
|
385
|
+
* @param x X position (can be negative)
|
|
386
|
+
* @param y Y position (can be negative)
|
|
387
|
+
* @param opacity Opacity of overlay (0-1, default: 1)
|
|
388
|
+
* @returns This image instance for chaining
|
|
389
|
+
*/
|
|
390
|
+
composite(overlay, x, y, opacity = 1) {
|
|
391
|
+
if (!this.imageData)
|
|
392
|
+
throw new Error("No image loaded");
|
|
393
|
+
if (!overlay.imageData)
|
|
394
|
+
throw new Error("Overlay has no image loaded");
|
|
395
|
+
this.imageData.data = (0, image_processing_js_1.composite)(this.imageData.data, this.imageData.width, this.imageData.height, overlay.imageData.data, overlay.imageData.width, overlay.imageData.height, x, y, opacity);
|
|
396
|
+
return this;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Adjust brightness of the image
|
|
400
|
+
* @param amount Brightness adjustment (-1 to 1, where 0 is no change)
|
|
401
|
+
* @returns This image instance for chaining
|
|
402
|
+
*/
|
|
403
|
+
brightness(amount) {
|
|
404
|
+
if (!this.imageData)
|
|
405
|
+
throw new Error("No image loaded");
|
|
406
|
+
this.imageData.data = (0, image_processing_js_1.adjustBrightness)(this.imageData.data, amount);
|
|
407
|
+
return this;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Adjust contrast of the image
|
|
411
|
+
* @param amount Contrast adjustment (-1 to 1, where 0 is no change)
|
|
412
|
+
* @returns This image instance for chaining
|
|
413
|
+
*/
|
|
414
|
+
contrast(amount) {
|
|
415
|
+
if (!this.imageData)
|
|
416
|
+
throw new Error("No image loaded");
|
|
417
|
+
this.imageData.data = (0, image_processing_js_1.adjustContrast)(this.imageData.data, amount);
|
|
418
|
+
return this;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Adjust exposure of the image
|
|
422
|
+
* @param amount Exposure adjustment in stops (-3 to 3, where 0 is no change)
|
|
423
|
+
* @returns This image instance for chaining
|
|
424
|
+
*/
|
|
425
|
+
exposure(amount) {
|
|
426
|
+
if (!this.imageData)
|
|
427
|
+
throw new Error("No image loaded");
|
|
428
|
+
this.imageData.data = (0, image_processing_js_1.adjustExposure)(this.imageData.data, amount);
|
|
429
|
+
return this;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Adjust saturation of the image
|
|
433
|
+
* @param amount Saturation adjustment (-1 to 1, where 0 is no change)
|
|
434
|
+
* @returns This image instance for chaining
|
|
435
|
+
*/
|
|
436
|
+
saturation(amount) {
|
|
437
|
+
if (!this.imageData)
|
|
438
|
+
throw new Error("No image loaded");
|
|
439
|
+
this.imageData.data = (0, image_processing_js_1.adjustSaturation)(this.imageData.data, amount);
|
|
440
|
+
return this;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Invert colors of the image
|
|
444
|
+
* @returns This image instance for chaining
|
|
445
|
+
*/
|
|
446
|
+
invert() {
|
|
447
|
+
if (!this.imageData)
|
|
448
|
+
throw new Error("No image loaded");
|
|
449
|
+
this.imageData.data = (0, image_processing_js_1.invert)(this.imageData.data);
|
|
450
|
+
return this;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Convert the image to grayscale
|
|
454
|
+
* @returns This image instance for chaining
|
|
455
|
+
*/
|
|
456
|
+
grayscale() {
|
|
457
|
+
if (!this.imageData)
|
|
458
|
+
throw new Error("No image loaded");
|
|
459
|
+
this.imageData.data = (0, image_processing_js_1.grayscale)(this.imageData.data);
|
|
460
|
+
return this;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Fill a rectangular region with a color
|
|
464
|
+
* @param x Starting X position
|
|
465
|
+
* @param y Starting Y position
|
|
466
|
+
* @param width Width of the fill region
|
|
467
|
+
* @param height Height of the fill region
|
|
468
|
+
* @param r Red component (0-255)
|
|
469
|
+
* @param g Green component (0-255)
|
|
470
|
+
* @param b Blue component (0-255)
|
|
471
|
+
* @param a Alpha component (0-255, default: 255)
|
|
472
|
+
* @returns This image instance for chaining
|
|
473
|
+
*/
|
|
474
|
+
fillRect(x, y, width, height, r, g, b, a = 255) {
|
|
475
|
+
if (!this.imageData)
|
|
476
|
+
throw new Error("No image loaded");
|
|
477
|
+
this.imageData.data = (0, image_processing_js_1.fillRect)(this.imageData.data, this.imageData.width, this.imageData.height, x, y, width, height, r, g, b, a);
|
|
478
|
+
return this;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Crop the image to a rectangular region
|
|
482
|
+
* @param x Starting X position
|
|
483
|
+
* @param y Starting Y position
|
|
484
|
+
* @param width Width of the crop region
|
|
485
|
+
* @param height Height of the crop region
|
|
486
|
+
* @returns This image instance for chaining
|
|
487
|
+
*/
|
|
488
|
+
crop(x, y, width, height) {
|
|
489
|
+
if (!this.imageData)
|
|
490
|
+
throw new Error("No image loaded");
|
|
491
|
+
const result = (0, image_processing_js_1.crop)(this.imageData.data, this.imageData.width, this.imageData.height, x, y, width, height);
|
|
492
|
+
this.imageData.width = result.width;
|
|
493
|
+
this.imageData.height = result.height;
|
|
494
|
+
this.imageData.data = result.data;
|
|
495
|
+
// Update physical dimensions if DPI is set
|
|
496
|
+
if (this.imageData.metadata) {
|
|
497
|
+
const metadata = this.imageData.metadata;
|
|
498
|
+
if (metadata.dpiX) {
|
|
499
|
+
this.imageData.metadata.physicalWidth = result.width / metadata.dpiX;
|
|
500
|
+
}
|
|
501
|
+
if (metadata.dpiY) {
|
|
502
|
+
this.imageData.metadata.physicalHeight = result.height / metadata.dpiY;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return this;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Get the pixel color at the specified position
|
|
509
|
+
* @param x X position
|
|
510
|
+
* @param y Y position
|
|
511
|
+
* @returns Object with r, g, b, a components (0-255) or undefined if out of bounds
|
|
512
|
+
*/
|
|
513
|
+
getPixel(x, y) {
|
|
514
|
+
if (!this.imageData)
|
|
515
|
+
throw new Error("No image loaded");
|
|
516
|
+
if (x < 0 || x >= this.imageData.width || y < 0 || y >= this.imageData.height) {
|
|
517
|
+
return undefined;
|
|
518
|
+
}
|
|
519
|
+
const idx = (y * this.imageData.width + x) * 4;
|
|
520
|
+
return {
|
|
521
|
+
r: this.imageData.data[idx],
|
|
522
|
+
g: this.imageData.data[idx + 1],
|
|
523
|
+
b: this.imageData.data[idx + 2],
|
|
524
|
+
a: this.imageData.data[idx + 3],
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Set the pixel color at the specified position
|
|
529
|
+
* @param x X position
|
|
530
|
+
* @param y Y position
|
|
531
|
+
* @param r Red component (0-255)
|
|
532
|
+
* @param g Green component (0-255)
|
|
533
|
+
* @param b Blue component (0-255)
|
|
534
|
+
* @param a Alpha component (0-255, default: 255)
|
|
535
|
+
* @returns This image instance for chaining
|
|
536
|
+
*/
|
|
537
|
+
setPixel(x, y, r, g, b, a = 255) {
|
|
538
|
+
if (!this.imageData)
|
|
539
|
+
throw new Error("No image loaded");
|
|
540
|
+
if (x < 0 || x >= this.imageData.width || y < 0 || y >= this.imageData.height) {
|
|
541
|
+
return this;
|
|
542
|
+
}
|
|
543
|
+
const idx = (y * this.imageData.width + x) * 4;
|
|
544
|
+
this.imageData.data[idx] = r;
|
|
545
|
+
this.imageData.data[idx + 1] = g;
|
|
546
|
+
this.imageData.data[idx + 2] = b;
|
|
547
|
+
this.imageData.data[idx + 3] = a;
|
|
548
|
+
return this;
|
|
549
|
+
}
|
|
354
550
|
}
|
|
355
551
|
exports.Image = Image;
|
|
356
552
|
Object.defineProperty(Image, "formats", {
|
|
@@ -364,7 +560,9 @@ Object.defineProperty(Image, "formats", {
|
|
|
364
560
|
new gif_js_1.GIFFormat(),
|
|
365
561
|
new tiff_js_1.TIFFFormat(),
|
|
366
562
|
new bmp_js_1.BMPFormat(),
|
|
367
|
-
new
|
|
563
|
+
new dng_js_1.DNGFormat(),
|
|
564
|
+
new pam_js_1.PAMFormat(),
|
|
565
|
+
new pcx_js_1.PCXFormat(),
|
|
368
566
|
new ascii_js_1.ASCIIFormat(),
|
|
369
567
|
]
|
|
370
568
|
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image processing utilities for common operations like compositing,
|
|
3
|
+
* level adjustments, and color manipulations.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Composite one image on top of another at a specified position
|
|
7
|
+
* @param base Base image data (RGBA)
|
|
8
|
+
* @param baseWidth Base image width
|
|
9
|
+
* @param baseHeight Base image height
|
|
10
|
+
* @param overlay Overlay image data (RGBA)
|
|
11
|
+
* @param overlayWidth Overlay image width
|
|
12
|
+
* @param overlayHeight Overlay image height
|
|
13
|
+
* @param x X position to place overlay (can be negative)
|
|
14
|
+
* @param y Y position to place overlay (can be negative)
|
|
15
|
+
* @param opacity Opacity of overlay (0-1, default: 1)
|
|
16
|
+
* @returns New image data with overlay composited on base
|
|
17
|
+
*/
|
|
18
|
+
export declare function composite(base: Uint8Array, baseWidth: number, baseHeight: number, overlay: Uint8Array, overlayWidth: number, overlayHeight: number, x: number, y: number, opacity?: number): Uint8Array;
|
|
19
|
+
/**
|
|
20
|
+
* Adjust brightness of an image
|
|
21
|
+
* @param data Image data (RGBA)
|
|
22
|
+
* @param amount Brightness adjustment (-1 to 1, where 0 is no change)
|
|
23
|
+
* @returns New image data with adjusted brightness
|
|
24
|
+
*/
|
|
25
|
+
export declare function adjustBrightness(data: Uint8Array, amount: number): Uint8Array;
|
|
26
|
+
/**
|
|
27
|
+
* Adjust contrast of an image
|
|
28
|
+
* @param data Image data (RGBA)
|
|
29
|
+
* @param amount Contrast adjustment (-1 to 1, where 0 is no change)
|
|
30
|
+
* @returns New image data with adjusted contrast
|
|
31
|
+
*/
|
|
32
|
+
export declare function adjustContrast(data: Uint8Array, amount: number): Uint8Array;
|
|
33
|
+
/**
|
|
34
|
+
* Adjust exposure of an image
|
|
35
|
+
* @param data Image data (RGBA)
|
|
36
|
+
* @param amount Exposure adjustment in stops (-3 to 3, where 0 is no change)
|
|
37
|
+
* @returns New image data with adjusted exposure
|
|
38
|
+
*/
|
|
39
|
+
export declare function adjustExposure(data: Uint8Array, amount: number): Uint8Array;
|
|
40
|
+
/**
|
|
41
|
+
* Adjust saturation of an image
|
|
42
|
+
* @param data Image data (RGBA)
|
|
43
|
+
* @param amount Saturation adjustment (-1 to 1, where 0 is no change)
|
|
44
|
+
* @returns New image data with adjusted saturation
|
|
45
|
+
*/
|
|
46
|
+
export declare function adjustSaturation(data: Uint8Array, amount: number): Uint8Array;
|
|
47
|
+
/**
|
|
48
|
+
* Invert colors of an image
|
|
49
|
+
* @param data Image data (RGBA)
|
|
50
|
+
* @returns New image data with inverted colors
|
|
51
|
+
*/
|
|
52
|
+
export declare function invert(data: Uint8Array): Uint8Array;
|
|
53
|
+
/**
|
|
54
|
+
* Convert image to grayscale
|
|
55
|
+
* @param data Image data (RGBA)
|
|
56
|
+
* @returns New image data in grayscale
|
|
57
|
+
*/
|
|
58
|
+
export declare function grayscale(data: Uint8Array): Uint8Array;
|
|
59
|
+
/**
|
|
60
|
+
* Fill a rectangular region with a color
|
|
61
|
+
* @param data Image data (RGBA)
|
|
62
|
+
* @param width Image width
|
|
63
|
+
* @param height Image height
|
|
64
|
+
* @param x Starting X position
|
|
65
|
+
* @param y Starting Y position
|
|
66
|
+
* @param fillWidth Width of the fill region
|
|
67
|
+
* @param fillHeight Height of the fill region
|
|
68
|
+
* @param r Red component (0-255)
|
|
69
|
+
* @param g Green component (0-255)
|
|
70
|
+
* @param b Blue component (0-255)
|
|
71
|
+
* @param a Alpha component (0-255)
|
|
72
|
+
* @returns Modified image data
|
|
73
|
+
*/
|
|
74
|
+
export declare function fillRect(data: Uint8Array, width: number, height: number, x: number, y: number, fillWidth: number, fillHeight: number, r: number, g: number, b: number, a: number): Uint8Array;
|
|
75
|
+
/**
|
|
76
|
+
* Crop an image to a rectangular region
|
|
77
|
+
* @param data Image data (RGBA)
|
|
78
|
+
* @param width Image width
|
|
79
|
+
* @param height Image height
|
|
80
|
+
* @param x Starting X position
|
|
81
|
+
* @param y Starting Y position
|
|
82
|
+
* @param cropWidth Width of the crop region
|
|
83
|
+
* @param cropHeight Height of the crop region
|
|
84
|
+
* @returns Cropped image data and dimensions
|
|
85
|
+
*/
|
|
86
|
+
export declare function crop(data: Uint8Array, width: number, height: number, x: number, y: number, cropWidth: number, cropHeight: number): {
|
|
87
|
+
data: Uint8Array;
|
|
88
|
+
width: number;
|
|
89
|
+
height: number;
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=image_processing.d.ts.map
|