@twin.org/image 0.0.2-next.9 → 0.0.3-next.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/dist/es/color.js +213 -0
- package/dist/es/color.js.map +1 -0
- package/dist/es/encoders/jpegEncoder.js +740 -0
- package/dist/es/encoders/jpegEncoder.js.map +1 -0
- package/dist/es/encoders/png/frame.js +4 -0
- package/dist/es/encoders/png/frame.js.map +1 -0
- package/dist/es/encoders/png/imageData.js +2 -0
- package/dist/es/encoders/png/imageData.js.map +1 -0
- package/dist/es/encoders/png/leaf.js +4 -0
- package/dist/es/encoders/png/leaf.js.map +1 -0
- package/dist/es/encoders/pngEncoder.js +939 -0
- package/dist/es/encoders/pngEncoder.js.map +1 -0
- package/dist/es/index.js +6 -0
- package/dist/es/index.js.map +1 -0
- package/dist/types/color.d.ts +4 -0
- package/dist/types/encoders/jpegEncoder.d.ts +4 -0
- package/dist/types/index.d.ts +3 -3
- package/docs/changelog.md +281 -0
- package/docs/reference/classes/Color.md +10 -2
- package/docs/reference/classes/JpegEncoder.md +8 -0
- package/locales/en.json +4 -0
- package/package.json +19 -10
- package/dist/cjs/index.cjs +0 -1898
- package/dist/esm/index.mjs +0 -1894
package/dist/esm/index.mjs
DELETED
|
@@ -1,1894 +0,0 @@
|
|
|
1
|
-
import { Guards, GeneralError, Is, Compression } from '@twin.org/core';
|
|
2
|
-
|
|
3
|
-
// Copyright 2024 IOTA Stiftung.
|
|
4
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
5
|
-
/* eslint-disable no-bitwise */
|
|
6
|
-
/**
|
|
7
|
-
* Class to represent a color.
|
|
8
|
-
*/
|
|
9
|
-
class Color {
|
|
10
|
-
/**
|
|
11
|
-
* Runtime name for the class.
|
|
12
|
-
* @internal
|
|
13
|
-
*/
|
|
14
|
-
static _CLASS_NAME = "Color";
|
|
15
|
-
/**
|
|
16
|
-
* @internal
|
|
17
|
-
*/
|
|
18
|
-
_alpha;
|
|
19
|
-
/**
|
|
20
|
-
* @internal
|
|
21
|
-
*/
|
|
22
|
-
_red;
|
|
23
|
-
/**
|
|
24
|
-
* @internal
|
|
25
|
-
*/
|
|
26
|
-
_green;
|
|
27
|
-
/**
|
|
28
|
-
* @internal
|
|
29
|
-
*/
|
|
30
|
-
_blue;
|
|
31
|
-
/**
|
|
32
|
-
* Create a new instance of color.
|
|
33
|
-
* @param alpha The alpha element of the color.
|
|
34
|
-
* @param red The red element of the color.
|
|
35
|
-
* @param green The green element of the color.
|
|
36
|
-
* @param blue The blue element of the color.
|
|
37
|
-
*/
|
|
38
|
-
constructor(alpha, red, green, blue) {
|
|
39
|
-
Guards.number(Color._CLASS_NAME, "alpha", alpha);
|
|
40
|
-
Guards.number(Color._CLASS_NAME, "red", red);
|
|
41
|
-
Guards.number(Color._CLASS_NAME, "green", green);
|
|
42
|
-
Guards.number(Color._CLASS_NAME, "blue", blue);
|
|
43
|
-
if (alpha < 0 || alpha > 255) {
|
|
44
|
-
throw new GeneralError(Color._CLASS_NAME, "range", {
|
|
45
|
-
prop: "alpha",
|
|
46
|
-
value: alpha
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
if (red < 0 || red > 255) {
|
|
50
|
-
throw new GeneralError(Color._CLASS_NAME, "range", {
|
|
51
|
-
prop: "red",
|
|
52
|
-
value: red
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
if (green < 0 || green > 255) {
|
|
56
|
-
throw new GeneralError(Color._CLASS_NAME, "range", {
|
|
57
|
-
prop: "green",
|
|
58
|
-
value: green
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
if (blue < 0 || blue > 255) {
|
|
62
|
-
throw new GeneralError(Color._CLASS_NAME, "range", {
|
|
63
|
-
prop: "blue",
|
|
64
|
-
value: blue
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
this._alpha = alpha;
|
|
68
|
-
this._red = red;
|
|
69
|
-
this._green = green;
|
|
70
|
-
this._blue = blue;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Construct a color from a hex string.
|
|
74
|
-
* @param hex The hex string to parse.
|
|
75
|
-
* @returns The color.
|
|
76
|
-
* @throws Error if the format is incorrect.
|
|
77
|
-
*/
|
|
78
|
-
static fromHex(hex) {
|
|
79
|
-
Guards.stringValue(Color._CLASS_NAME, "hex", hex);
|
|
80
|
-
let alpha;
|
|
81
|
-
let red;
|
|
82
|
-
let green;
|
|
83
|
-
let blue;
|
|
84
|
-
if (/^#[\dA-Fa-f]{3}$/.test(hex)) {
|
|
85
|
-
// #RGB
|
|
86
|
-
alpha = "0xFF";
|
|
87
|
-
red = hex.slice(1, 2).repeat(2);
|
|
88
|
-
green = hex.slice(2, 3).repeat(2);
|
|
89
|
-
blue = hex.slice(3, 4).repeat(2);
|
|
90
|
-
}
|
|
91
|
-
else if (/^#[\dA-Fa-f]{4}$/.test(hex)) {
|
|
92
|
-
// #ARGB
|
|
93
|
-
alpha = hex.slice(1, 2).repeat(2);
|
|
94
|
-
red = hex.slice(2, 3).repeat(2);
|
|
95
|
-
green = hex.slice(3, 4).repeat(2);
|
|
96
|
-
blue = hex.slice(4, 5).repeat(2);
|
|
97
|
-
}
|
|
98
|
-
else if (/^#[\dA-Fa-f]{6}$/.test(hex)) {
|
|
99
|
-
// #RRGGBB
|
|
100
|
-
alpha = "0xFF";
|
|
101
|
-
red = hex.slice(1, 3);
|
|
102
|
-
green = hex.slice(3, 5);
|
|
103
|
-
blue = hex.slice(5, 7);
|
|
104
|
-
}
|
|
105
|
-
else if (/^#[\dA-Fa-f]{8}$/.test(hex)) {
|
|
106
|
-
// #AARRGGBB
|
|
107
|
-
alpha = hex.slice(1, 3);
|
|
108
|
-
red = hex.slice(3, 5);
|
|
109
|
-
green = hex.slice(5, 7);
|
|
110
|
-
blue = hex.slice(7, 9);
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
throw new GeneralError(Color._CLASS_NAME, "hex");
|
|
114
|
-
}
|
|
115
|
-
return new Color(Number.parseInt(alpha, 16), Number.parseInt(red, 16), Number.parseInt(green, 16), Number.parseInt(blue, 16));
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Coerce an unknown type to a color.
|
|
119
|
-
* @param value The value to try and convert.
|
|
120
|
-
* @returns The color if one can be created.
|
|
121
|
-
*/
|
|
122
|
-
static coerce(value) {
|
|
123
|
-
if (Is.object(value) &&
|
|
124
|
-
Is.number(value._alpha) &&
|
|
125
|
-
Is.number(value._red) &&
|
|
126
|
-
Is.number(value._green) &&
|
|
127
|
-
Is.number(value._blue)) {
|
|
128
|
-
return new Color(value._alpha, value._red, value._green, value._blue);
|
|
129
|
-
}
|
|
130
|
-
else if (Is.stringValue(value) && value.startsWith("#")) {
|
|
131
|
-
try {
|
|
132
|
-
return Color.fromHex(value);
|
|
133
|
-
}
|
|
134
|
-
catch { }
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Get the alpha element.
|
|
139
|
-
* @returns The alpha element.
|
|
140
|
-
*/
|
|
141
|
-
alpha() {
|
|
142
|
-
return this._alpha;
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Get the red element.
|
|
146
|
-
* @returns The red element.
|
|
147
|
-
*/
|
|
148
|
-
red() {
|
|
149
|
-
return this._red;
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Get the green element.
|
|
153
|
-
* @returns The green element.
|
|
154
|
-
*/
|
|
155
|
-
green() {
|
|
156
|
-
return this._green;
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Get the blue element.
|
|
160
|
-
* @returns The blue element.
|
|
161
|
-
*/
|
|
162
|
-
blue() {
|
|
163
|
-
return this._blue;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Get color as argb.
|
|
167
|
-
* @returns The color as argb.
|
|
168
|
-
*/
|
|
169
|
-
argb() {
|
|
170
|
-
return ((this._alpha << 24) | (this._red << 16) | (this._green << 8) | this._blue) >>> 0;
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Get color as rgba.
|
|
174
|
-
* @returns The color as rgba.
|
|
175
|
-
*/
|
|
176
|
-
rgba() {
|
|
177
|
-
return ((this._red << 24) | (this._green << 16) | (this._blue << 8) | this._alpha) >>> 0;
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Get color as rgb text.
|
|
181
|
-
* @returns The color as rgb.
|
|
182
|
-
*/
|
|
183
|
-
rgbText() {
|
|
184
|
-
return `rgb(${this._red},${this._green},${this._blue})`;
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Get color as rgba text.
|
|
188
|
-
* @returns The color as rgba.
|
|
189
|
-
*/
|
|
190
|
-
rgbaText() {
|
|
191
|
-
return `rgba(${this._red},${this._green},${this._blue},${Math.round((this._alpha / 255) * 100) / 100})`;
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Get color as hex no alpha.
|
|
195
|
-
* @returns The color as hex with no alpha component.
|
|
196
|
-
*/
|
|
197
|
-
hex() {
|
|
198
|
-
const red = `00${this._red.toString(16)}`.slice(-2);
|
|
199
|
-
const green = `00${this._green.toString(16)}`.slice(-2);
|
|
200
|
-
const blue = `00${this._blue.toString(16)}`.slice(-2);
|
|
201
|
-
return `#${red}${green}${blue}`.toUpperCase();
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Get color as hex with alpha.
|
|
205
|
-
* @returns The color as hex with with alpha component.
|
|
206
|
-
*/
|
|
207
|
-
hexWithAlpha() {
|
|
208
|
-
const alpha = `00${this._alpha.toString(16)}`.slice(-2);
|
|
209
|
-
const red = `00${this._red.toString(16)}`.slice(-2);
|
|
210
|
-
const green = `00${this._green.toString(16)}`.slice(-2);
|
|
211
|
-
const blue = `00${this._blue.toString(16)}`.slice(-2);
|
|
212
|
-
return `#${alpha}${red}${green}${blue}`.toUpperCase();
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Copyright 2024 IOTA Stiftung.
|
|
217
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
218
|
-
/* eslint-disable no-bitwise */
|
|
219
|
-
/* eslint-disable no-mixed-operators */
|
|
220
|
-
/**
|
|
221
|
-
* JPEG Encoder.
|
|
222
|
-
* Based on JPEG encoder ported to JavaScript and optimized by Andreas Ritter.
|
|
223
|
-
*/
|
|
224
|
-
class JpegEncoder {
|
|
225
|
-
/**
|
|
226
|
-
* Runtime name for the class.
|
|
227
|
-
* @internal
|
|
228
|
-
*/
|
|
229
|
-
static _CLASS_NAME = "JpegEncoder";
|
|
230
|
-
/**
|
|
231
|
-
* @internal
|
|
232
|
-
*/
|
|
233
|
-
static _STD_DC_LUMINANCE_NR_CODES = [
|
|
234
|
-
0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
|
|
235
|
-
];
|
|
236
|
-
/**
|
|
237
|
-
* @internal
|
|
238
|
-
*/
|
|
239
|
-
static _STD_DC_LUMINANCE_VALUES = [
|
|
240
|
-
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
|
|
241
|
-
];
|
|
242
|
-
/**
|
|
243
|
-
* @internal
|
|
244
|
-
*/
|
|
245
|
-
static _STD_AC_LUMINANCE_NR_CODES = [
|
|
246
|
-
0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d
|
|
247
|
-
];
|
|
248
|
-
/**
|
|
249
|
-
* @internal
|
|
250
|
-
*/
|
|
251
|
-
static _STD_AC_LUMINANCE_VALUES = [
|
|
252
|
-
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
|
|
253
|
-
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
|
|
254
|
-
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
|
255
|
-
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
|
256
|
-
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
|
257
|
-
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
|
258
|
-
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
|
|
259
|
-
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
|
|
260
|
-
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
|
261
|
-
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
|
262
|
-
0xf9, 0xfa
|
|
263
|
-
];
|
|
264
|
-
/**
|
|
265
|
-
* @internal
|
|
266
|
-
*/
|
|
267
|
-
static _STD_DC_CHROMINANCE_NR_CODES = [
|
|
268
|
-
0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
|
|
269
|
-
];
|
|
270
|
-
/**
|
|
271
|
-
* @internal
|
|
272
|
-
*/
|
|
273
|
-
static _STD_DC_CHROMINANCE_VALUES = [
|
|
274
|
-
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
|
|
275
|
-
];
|
|
276
|
-
/**
|
|
277
|
-
* @internal
|
|
278
|
-
*/
|
|
279
|
-
static _STD_AC_CHROMINANCE_NR_CODES = [
|
|
280
|
-
0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77
|
|
281
|
-
];
|
|
282
|
-
/**
|
|
283
|
-
* @internal
|
|
284
|
-
*/
|
|
285
|
-
static _STD_AC_CHROMINANCE_VALUES = [
|
|
286
|
-
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
|
|
287
|
-
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
|
|
288
|
-
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
|
289
|
-
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
|
|
290
|
-
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
|
|
291
|
-
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
|
292
|
-
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
|
|
293
|
-
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
|
|
294
|
-
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
|
295
|
-
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
|
|
296
|
-
0xf9, 0xfa
|
|
297
|
-
];
|
|
298
|
-
/** @internal */
|
|
299
|
-
static _SIG_ZAG = [
|
|
300
|
-
0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11,
|
|
301
|
-
18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34,
|
|
302
|
-
37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63
|
|
303
|
-
];
|
|
304
|
-
/**
|
|
305
|
-
* @internal
|
|
306
|
-
*/
|
|
307
|
-
_yTable;
|
|
308
|
-
/**
|
|
309
|
-
* @internal
|
|
310
|
-
*/
|
|
311
|
-
_uvTable;
|
|
312
|
-
/**
|
|
313
|
-
* @internal
|
|
314
|
-
*/
|
|
315
|
-
_fdTblY;
|
|
316
|
-
/**
|
|
317
|
-
* @internal
|
|
318
|
-
*/
|
|
319
|
-
_fdTblUV;
|
|
320
|
-
/**
|
|
321
|
-
* @internal
|
|
322
|
-
*/
|
|
323
|
-
_ydcHashTable;
|
|
324
|
-
/**
|
|
325
|
-
* @internal
|
|
326
|
-
*/
|
|
327
|
-
_uVdcHashTable;
|
|
328
|
-
/**
|
|
329
|
-
* @internal
|
|
330
|
-
*/
|
|
331
|
-
_yacHashTable;
|
|
332
|
-
/**
|
|
333
|
-
* @internal
|
|
334
|
-
*/
|
|
335
|
-
_uVacHashTable;
|
|
336
|
-
/**
|
|
337
|
-
* @internal
|
|
338
|
-
*/
|
|
339
|
-
_bitCode;
|
|
340
|
-
/**
|
|
341
|
-
* @internal
|
|
342
|
-
*/
|
|
343
|
-
_category;
|
|
344
|
-
/**
|
|
345
|
-
* @internal
|
|
346
|
-
*/
|
|
347
|
-
_outputFDctQuant;
|
|
348
|
-
/**
|
|
349
|
-
* @internal
|
|
350
|
-
*/
|
|
351
|
-
_du;
|
|
352
|
-
/**
|
|
353
|
-
* @internal
|
|
354
|
-
*/
|
|
355
|
-
_byteOut;
|
|
356
|
-
/**
|
|
357
|
-
* @internal
|
|
358
|
-
*/
|
|
359
|
-
_byteNew;
|
|
360
|
-
/**
|
|
361
|
-
* @internal
|
|
362
|
-
*/
|
|
363
|
-
_bytePos;
|
|
364
|
-
/**
|
|
365
|
-
* @internal
|
|
366
|
-
*/
|
|
367
|
-
_ydu;
|
|
368
|
-
/**
|
|
369
|
-
* @internal
|
|
370
|
-
*/
|
|
371
|
-
_udu;
|
|
372
|
-
/**
|
|
373
|
-
* @internal
|
|
374
|
-
*/
|
|
375
|
-
_vdu;
|
|
376
|
-
/**
|
|
377
|
-
* @internal
|
|
378
|
-
*/
|
|
379
|
-
_rgbYuvTable;
|
|
380
|
-
/**
|
|
381
|
-
* Create a new instance of JpegEncoder.
|
|
382
|
-
*/
|
|
383
|
-
constructor() {
|
|
384
|
-
this._yTable = Array.from({ length: 64 });
|
|
385
|
-
this._uvTable = Array.from({ length: 64 });
|
|
386
|
-
this._fdTblY = Array.from({ length: 64 });
|
|
387
|
-
this._fdTblUV = Array.from({ length: 64 });
|
|
388
|
-
this._bitCode = Array.from({ length: 65535 });
|
|
389
|
-
this._category = Array.from({ length: 65535 });
|
|
390
|
-
this._outputFDctQuant = Array.from({ length: 64 });
|
|
391
|
-
this._du = Array.from({ length: 64 });
|
|
392
|
-
this._byteOut = [];
|
|
393
|
-
this._byteNew = 0;
|
|
394
|
-
this._bytePos = 7;
|
|
395
|
-
this._ydu = Array.from({ length: 64 });
|
|
396
|
-
this._udu = Array.from({ length: 64 });
|
|
397
|
-
this._vdu = Array.from({ length: 64 });
|
|
398
|
-
this._rgbYuvTable = Array.from({ length: 2048 });
|
|
399
|
-
this.initHuffmanTbl();
|
|
400
|
-
this.initCategoryNumber();
|
|
401
|
-
this.initRGBYUVTable();
|
|
402
|
-
}
|
|
403
|
-
/**
|
|
404
|
-
* Encode the image with the given quality.
|
|
405
|
-
* @param width The width of the image to encode.
|
|
406
|
-
* @param height The height of the image to encode.
|
|
407
|
-
* @param imageData The data for the image.
|
|
408
|
-
* @param quality The quality to encode the image at.
|
|
409
|
-
* @returns The data for the encoded image.
|
|
410
|
-
*/
|
|
411
|
-
encode(width, height, imageData, quality) {
|
|
412
|
-
this.setQuality(quality);
|
|
413
|
-
// Initialize bit writer
|
|
414
|
-
this._byteOut = [];
|
|
415
|
-
this._byteNew = 0;
|
|
416
|
-
this._bytePos = 7;
|
|
417
|
-
// Add JPEG headers
|
|
418
|
-
this.writeWord(0xffd8); // SOI
|
|
419
|
-
this.writeAPP0();
|
|
420
|
-
this.writeDQT();
|
|
421
|
-
this.writeSOF0(width, height);
|
|
422
|
-
this.writeDHT();
|
|
423
|
-
this.writeSOS();
|
|
424
|
-
// Encode 8x8 macro blocks
|
|
425
|
-
let DCY = 0;
|
|
426
|
-
let DCU = 0;
|
|
427
|
-
let DCV = 0;
|
|
428
|
-
this._byteNew = 0;
|
|
429
|
-
this._bytePos = 7;
|
|
430
|
-
const quadWidth = width * 4;
|
|
431
|
-
let x;
|
|
432
|
-
let y = 0;
|
|
433
|
-
let r;
|
|
434
|
-
let g;
|
|
435
|
-
let b;
|
|
436
|
-
let start;
|
|
437
|
-
let p;
|
|
438
|
-
let col;
|
|
439
|
-
let row;
|
|
440
|
-
let pos;
|
|
441
|
-
while (y < height) {
|
|
442
|
-
x = 0;
|
|
443
|
-
while (x < quadWidth) {
|
|
444
|
-
start = quadWidth * y + x;
|
|
445
|
-
p = start;
|
|
446
|
-
col = -1;
|
|
447
|
-
row = 0;
|
|
448
|
-
for (pos = 0; pos < 64; pos++) {
|
|
449
|
-
row = pos >> 3; // /8
|
|
450
|
-
col = (pos & 7) * 4; // %8
|
|
451
|
-
p = start + row * quadWidth + col;
|
|
452
|
-
if (y + row >= height) {
|
|
453
|
-
// padding bottom
|
|
454
|
-
p -= quadWidth * (y + 1 + row - height);
|
|
455
|
-
}
|
|
456
|
-
if (x + col >= quadWidth) {
|
|
457
|
-
// padding right
|
|
458
|
-
p -= x + col - quadWidth + 4;
|
|
459
|
-
}
|
|
460
|
-
r = imageData[p++];
|
|
461
|
-
g = imageData[p++];
|
|
462
|
-
b = imageData[p++];
|
|
463
|
-
// use lookup table (slightly faster)
|
|
464
|
-
this._ydu[pos] =
|
|
465
|
-
((this._rgbYuvTable[r] +
|
|
466
|
-
this._rgbYuvTable[Math.trunc(g + 256)] +
|
|
467
|
-
this._rgbYuvTable[Math.trunc(b + 512)]) >>
|
|
468
|
-
16) -
|
|
469
|
-
128;
|
|
470
|
-
this._udu[pos] =
|
|
471
|
-
((this._rgbYuvTable[Math.trunc(r + 768)] +
|
|
472
|
-
this._rgbYuvTable[Math.trunc(g + 1024)] +
|
|
473
|
-
this._rgbYuvTable[Math.trunc(b + 1280)]) >>
|
|
474
|
-
16) -
|
|
475
|
-
128;
|
|
476
|
-
this._vdu[pos] =
|
|
477
|
-
((this._rgbYuvTable[Math.trunc(r + 1280)] +
|
|
478
|
-
this._rgbYuvTable[Math.trunc(g + 1536)] +
|
|
479
|
-
this._rgbYuvTable[Math.trunc(b + 1792)]) >>
|
|
480
|
-
16) -
|
|
481
|
-
128;
|
|
482
|
-
}
|
|
483
|
-
if (this._ydcHashTable && this._yacHashTable) {
|
|
484
|
-
DCY = this.processDU(this._ydu, this._fdTblY, DCY, this._ydcHashTable, this._yacHashTable);
|
|
485
|
-
}
|
|
486
|
-
if (this._uVdcHashTable && this._uVacHashTable) {
|
|
487
|
-
DCU = this.processDU(this._udu, this._fdTblUV, DCU, this._uVdcHashTable, this._uVacHashTable);
|
|
488
|
-
}
|
|
489
|
-
if (this._uVdcHashTable && this._uVacHashTable) {
|
|
490
|
-
DCV = this.processDU(this._vdu, this._fdTblUV, DCV, this._uVdcHashTable, this._uVacHashTable);
|
|
491
|
-
}
|
|
492
|
-
x += 32;
|
|
493
|
-
}
|
|
494
|
-
y += 8;
|
|
495
|
-
}
|
|
496
|
-
// Do the bit alignment of the EOI marker
|
|
497
|
-
if (this._bytePos >= 0) {
|
|
498
|
-
const fillBits = [];
|
|
499
|
-
fillBits[1] = this._bytePos + 1;
|
|
500
|
-
fillBits[0] = (1 << (this._bytePos + 1)) - 1;
|
|
501
|
-
this.writeBits(fillBits);
|
|
502
|
-
}
|
|
503
|
-
this.writeWord(0xffd9); // EOI
|
|
504
|
-
return new Uint8Array(this._byteOut);
|
|
505
|
-
}
|
|
506
|
-
/**
|
|
507
|
-
* @internal
|
|
508
|
-
*/
|
|
509
|
-
setQuality(quality) {
|
|
510
|
-
if (quality <= 0 || quality > 100) {
|
|
511
|
-
throw new GeneralError(JpegEncoder._CLASS_NAME, "invalidQuality", { value: quality });
|
|
512
|
-
}
|
|
513
|
-
let sf = 0;
|
|
514
|
-
if (quality < 50) {
|
|
515
|
-
sf = Math.floor(5000 / quality);
|
|
516
|
-
}
|
|
517
|
-
else {
|
|
518
|
-
sf = Math.floor(200 - quality * 2);
|
|
519
|
-
}
|
|
520
|
-
this.initQuantTables(sf);
|
|
521
|
-
}
|
|
522
|
-
/**
|
|
523
|
-
* @internal
|
|
524
|
-
*/
|
|
525
|
-
initQuantTables(sf) {
|
|
526
|
-
const YQT = [
|
|
527
|
-
16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69,
|
|
528
|
-
56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104,
|
|
529
|
-
113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99
|
|
530
|
-
];
|
|
531
|
-
for (let i = 0; i < 64; i++) {
|
|
532
|
-
let t = Math.floor((YQT[i] * sf + 50) / 100);
|
|
533
|
-
if (t < 1) {
|
|
534
|
-
t = 1;
|
|
535
|
-
}
|
|
536
|
-
else if (t > 255) {
|
|
537
|
-
t = 255;
|
|
538
|
-
}
|
|
539
|
-
this._yTable[JpegEncoder._SIG_ZAG[i]] = t;
|
|
540
|
-
}
|
|
541
|
-
const UVQT = [
|
|
542
|
-
17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99,
|
|
543
|
-
99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
|
|
544
|
-
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99
|
|
545
|
-
];
|
|
546
|
-
for (let j = 0; j < 64; j++) {
|
|
547
|
-
let u = Math.floor((UVQT[j] * sf + 50) / 100);
|
|
548
|
-
if (u < 1) {
|
|
549
|
-
u = 1;
|
|
550
|
-
}
|
|
551
|
-
else if (u > 255) {
|
|
552
|
-
u = 255;
|
|
553
|
-
}
|
|
554
|
-
this._uvTable[JpegEncoder._SIG_ZAG[j]] = u;
|
|
555
|
-
}
|
|
556
|
-
const aAsf = [1, 1.387039845, 1.306562965, 1.175875602, 1, 0.785694958, 0.5411961, 0.275899379];
|
|
557
|
-
let k = 0;
|
|
558
|
-
for (let row = 0; row < 8; row++) {
|
|
559
|
-
for (let col = 0; col < 8; col++) {
|
|
560
|
-
this._fdTblY[k] = 1 / (this._yTable[JpegEncoder._SIG_ZAG[k]] * aAsf[row] * aAsf[col] * 8);
|
|
561
|
-
this._fdTblUV[k] = 1 / (this._uvTable[JpegEncoder._SIG_ZAG[k]] * aAsf[row] * aAsf[col] * 8);
|
|
562
|
-
k++;
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* @internal
|
|
568
|
-
*/
|
|
569
|
-
computeHuffmanTbl(nrCodes, stdTable) {
|
|
570
|
-
let codeValue = 0;
|
|
571
|
-
let posInTable = 0;
|
|
572
|
-
const HT = [];
|
|
573
|
-
for (let k = 1; k <= 16; k++) {
|
|
574
|
-
for (let j = 1; j <= nrCodes[k]; j++) {
|
|
575
|
-
HT[stdTable[posInTable]] = [];
|
|
576
|
-
HT[stdTable[posInTable]][0] = codeValue;
|
|
577
|
-
HT[stdTable[posInTable]][1] = k;
|
|
578
|
-
posInTable++;
|
|
579
|
-
codeValue++;
|
|
580
|
-
}
|
|
581
|
-
codeValue *= 2;
|
|
582
|
-
}
|
|
583
|
-
return HT;
|
|
584
|
-
}
|
|
585
|
-
/**
|
|
586
|
-
* @internal
|
|
587
|
-
*/
|
|
588
|
-
initHuffmanTbl() {
|
|
589
|
-
this._ydcHashTable = this.computeHuffmanTbl(JpegEncoder._STD_DC_LUMINANCE_NR_CODES, JpegEncoder._STD_DC_LUMINANCE_VALUES);
|
|
590
|
-
this._uVdcHashTable = this.computeHuffmanTbl(JpegEncoder._STD_DC_CHROMINANCE_NR_CODES, JpegEncoder._STD_DC_CHROMINANCE_VALUES);
|
|
591
|
-
this._yacHashTable = this.computeHuffmanTbl(JpegEncoder._STD_AC_LUMINANCE_NR_CODES, JpegEncoder._STD_AC_LUMINANCE_VALUES);
|
|
592
|
-
this._uVacHashTable = this.computeHuffmanTbl(JpegEncoder._STD_AC_CHROMINANCE_NR_CODES, JpegEncoder._STD_AC_CHROMINANCE_VALUES);
|
|
593
|
-
}
|
|
594
|
-
/**
|
|
595
|
-
* @internal
|
|
596
|
-
*/
|
|
597
|
-
initCategoryNumber() {
|
|
598
|
-
let nrLower = 1;
|
|
599
|
-
let nrUpper = 2;
|
|
600
|
-
for (let cat = 1; cat <= 15; cat++) {
|
|
601
|
-
// Positive numbers
|
|
602
|
-
for (let nr = nrLower; nr < nrUpper; nr++) {
|
|
603
|
-
this._category[32767 + nr] = cat;
|
|
604
|
-
this._bitCode[32767 + nr] = [];
|
|
605
|
-
this._bitCode[32767 + nr][1] = cat;
|
|
606
|
-
this._bitCode[32767 + nr][0] = nr;
|
|
607
|
-
}
|
|
608
|
-
// Negative numbers
|
|
609
|
-
for (let nrNeg = -(nrUpper - 1); nrNeg <= -nrLower; nrNeg++) {
|
|
610
|
-
this._category[32767 + nrNeg] = cat;
|
|
611
|
-
this._bitCode[32767 + nrNeg] = [];
|
|
612
|
-
this._bitCode[32767 + nrNeg][1] = cat;
|
|
613
|
-
this._bitCode[32767 + nrNeg][0] = nrUpper - 1 + nrNeg;
|
|
614
|
-
}
|
|
615
|
-
nrLower <<= 1;
|
|
616
|
-
nrUpper <<= 1;
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
/**
|
|
620
|
-
* @internal
|
|
621
|
-
*/
|
|
622
|
-
initRGBYUVTable() {
|
|
623
|
-
for (let i = 0; i < 256; i++) {
|
|
624
|
-
this._rgbYuvTable[i] = 19595 * i;
|
|
625
|
-
this._rgbYuvTable[Math.trunc(i + 256)] = 38470 * i;
|
|
626
|
-
this._rgbYuvTable[Math.trunc(i + 512)] = 7471 * i + 0x8000;
|
|
627
|
-
this._rgbYuvTable[Math.trunc(i + 768)] = -11059 * i;
|
|
628
|
-
this._rgbYuvTable[Math.trunc(i + 1024)] = -21709 * i;
|
|
629
|
-
this._rgbYuvTable[Math.trunc(i + 1280)] = 32768 * i + 0x807fff;
|
|
630
|
-
this._rgbYuvTable[Math.trunc(i + 1536)] = -27439 * i;
|
|
631
|
-
this._rgbYuvTable[Math.trunc(i + 1792)] = -5329 * i;
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
/**
|
|
635
|
-
* @internal
|
|
636
|
-
*/
|
|
637
|
-
writeBits(bs) {
|
|
638
|
-
const value = bs[0];
|
|
639
|
-
let posVal = bs[1] - 1;
|
|
640
|
-
while (posVal >= 0) {
|
|
641
|
-
if (value & (1 << posVal)) {
|
|
642
|
-
this._byteNew |= 1 << this._bytePos;
|
|
643
|
-
}
|
|
644
|
-
posVal--;
|
|
645
|
-
this._bytePos--;
|
|
646
|
-
if (this._bytePos < 0) {
|
|
647
|
-
if (this._byteNew === 0xff) {
|
|
648
|
-
this.writeByte(0xff);
|
|
649
|
-
this.writeByte(0);
|
|
650
|
-
}
|
|
651
|
-
else {
|
|
652
|
-
this.writeByte(this._byteNew);
|
|
653
|
-
}
|
|
654
|
-
this._bytePos = 7;
|
|
655
|
-
this._byteNew = 0;
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
/**
|
|
660
|
-
* @internal
|
|
661
|
-
*/
|
|
662
|
-
writeByte(value) {
|
|
663
|
-
this._byteOut.push(value);
|
|
664
|
-
}
|
|
665
|
-
/**
|
|
666
|
-
* @internal
|
|
667
|
-
*/
|
|
668
|
-
writeWord(value) {
|
|
669
|
-
this.writeByte((value >> 8) & 0xff);
|
|
670
|
-
this.writeByte(value & 0xff);
|
|
671
|
-
}
|
|
672
|
-
/**
|
|
673
|
-
* @internal
|
|
674
|
-
*/
|
|
675
|
-
fDCTQuant(data, fdTbl) {
|
|
676
|
-
let d0;
|
|
677
|
-
let d1;
|
|
678
|
-
let d2;
|
|
679
|
-
let d3;
|
|
680
|
-
let d4;
|
|
681
|
-
let d5;
|
|
682
|
-
let d6;
|
|
683
|
-
let d7;
|
|
684
|
-
/* Pass 1: process rows. */
|
|
685
|
-
let dataOff = 0;
|
|
686
|
-
let i;
|
|
687
|
-
const I8 = 8;
|
|
688
|
-
const I64 = 64;
|
|
689
|
-
for (i = 0; i < I8; ++i) {
|
|
690
|
-
d0 = data[dataOff];
|
|
691
|
-
d1 = data[dataOff + 1];
|
|
692
|
-
d2 = data[dataOff + 2];
|
|
693
|
-
d3 = data[dataOff + 3];
|
|
694
|
-
d4 = data[dataOff + 4];
|
|
695
|
-
d5 = data[dataOff + 5];
|
|
696
|
-
d6 = data[dataOff + 6];
|
|
697
|
-
d7 = data[dataOff + 7];
|
|
698
|
-
const tmp0 = d0 + d7;
|
|
699
|
-
const tmp7 = d0 - d7;
|
|
700
|
-
const tmp1 = d1 + d6;
|
|
701
|
-
const tmp6 = d1 - d6;
|
|
702
|
-
const tmp2 = d2 + d5;
|
|
703
|
-
const tmp5 = d2 - d5;
|
|
704
|
-
const tmp3 = d3 + d4;
|
|
705
|
-
const tmp4 = d3 - d4;
|
|
706
|
-
/* Even part */
|
|
707
|
-
let tmp10 = tmp0 + tmp3; /* phase 2 */
|
|
708
|
-
const tmp13 = tmp0 - tmp3;
|
|
709
|
-
let tmp11 = tmp1 + tmp2;
|
|
710
|
-
let tmp12 = tmp1 - tmp2;
|
|
711
|
-
data[dataOff] = tmp10 + tmp11; /* phase 3 */
|
|
712
|
-
data[dataOff + 4] = tmp10 - tmp11;
|
|
713
|
-
const z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */
|
|
714
|
-
data[dataOff + 2] = tmp13 + z1; /* phase 5 */
|
|
715
|
-
data[dataOff + 6] = tmp13 - z1;
|
|
716
|
-
/* Odd part */
|
|
717
|
-
tmp10 = tmp4 + tmp5; /* phase 2 */
|
|
718
|
-
tmp11 = tmp5 + tmp6;
|
|
719
|
-
tmp12 = tmp6 + tmp7;
|
|
720
|
-
/* The rotator is modified from fig 4-8 to avoid extra negations. */
|
|
721
|
-
const z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */
|
|
722
|
-
const z2 = 0.5411961 * tmp10 + z5; /* c2-c6 */
|
|
723
|
-
const z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */
|
|
724
|
-
const z3 = tmp11 * 0.707106781; /* c4 */
|
|
725
|
-
const z11 = tmp7 + z3; /* phase 5 */
|
|
726
|
-
const z13 = tmp7 - z3;
|
|
727
|
-
data[dataOff + 5] = z13 + z2; /* phase 6 */
|
|
728
|
-
data[dataOff + 3] = z13 - z2;
|
|
729
|
-
data[dataOff + 1] = z11 + z4;
|
|
730
|
-
data[dataOff + 7] = z11 - z4;
|
|
731
|
-
dataOff += 8; /* advance pointer to next row */
|
|
732
|
-
}
|
|
733
|
-
/* Pass 2: process columns. */
|
|
734
|
-
dataOff = 0;
|
|
735
|
-
for (i = 0; i < I8; ++i) {
|
|
736
|
-
d0 = data[dataOff];
|
|
737
|
-
d1 = data[dataOff + 8];
|
|
738
|
-
d2 = data[dataOff + 16];
|
|
739
|
-
d3 = data[dataOff + 24];
|
|
740
|
-
d4 = data[dataOff + 32];
|
|
741
|
-
d5 = data[dataOff + 40];
|
|
742
|
-
d6 = data[dataOff + 48];
|
|
743
|
-
d7 = data[dataOff + 56];
|
|
744
|
-
const tmp0p2 = d0 + d7;
|
|
745
|
-
const tmp7p2 = d0 - d7;
|
|
746
|
-
const tmp1p2 = d1 + d6;
|
|
747
|
-
const tmp6p2 = d1 - d6;
|
|
748
|
-
const tmp2p2 = d2 + d5;
|
|
749
|
-
const tmp5p2 = d2 - d5;
|
|
750
|
-
const tmp3p2 = d3 + d4;
|
|
751
|
-
const tmp4p2 = d3 - d4;
|
|
752
|
-
/* Even part */
|
|
753
|
-
let tmp10p2 = tmp0p2 + tmp3p2; /* phase 2 */
|
|
754
|
-
const tmp13p2 = tmp0p2 - tmp3p2;
|
|
755
|
-
let tmp11p2 = tmp1p2 + tmp2p2;
|
|
756
|
-
let tmp12p2 = tmp1p2 - tmp2p2;
|
|
757
|
-
data[dataOff] = tmp10p2 + tmp11p2; /* phase 3 */
|
|
758
|
-
data[dataOff + 32] = tmp10p2 - tmp11p2;
|
|
759
|
-
const z1p2 = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */
|
|
760
|
-
data[dataOff + 16] = tmp13p2 + z1p2; /* phase 5 */
|
|
761
|
-
data[dataOff + 48] = tmp13p2 - z1p2;
|
|
762
|
-
/* Odd part */
|
|
763
|
-
tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */
|
|
764
|
-
tmp11p2 = tmp5p2 + tmp6p2;
|
|
765
|
-
tmp12p2 = tmp6p2 + tmp7p2;
|
|
766
|
-
/* The rotator is modified from fig 4-8 to avoid extra negations. */
|
|
767
|
-
const z5p2 = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */
|
|
768
|
-
const z2p2 = 0.5411961 * tmp10p2 + z5p2; /* c2-c6 */
|
|
769
|
-
const z4p2 = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */
|
|
770
|
-
const z3p2 = tmp11p2 * 0.707106781; /* c4 */
|
|
771
|
-
const z11p2 = tmp7p2 + z3p2; /* phase 5 */
|
|
772
|
-
const z13p2 = tmp7p2 - z3p2;
|
|
773
|
-
data[dataOff + 40] = z13p2 + z2p2; /* phase 6 */
|
|
774
|
-
data[dataOff + 24] = z13p2 - z2p2;
|
|
775
|
-
data[dataOff + 8] = z11p2 + z4p2;
|
|
776
|
-
data[dataOff + 56] = z11p2 - z4p2;
|
|
777
|
-
dataOff++; /* advance pointer to next column */
|
|
778
|
-
}
|
|
779
|
-
// Quantize/descale the coefficients
|
|
780
|
-
let fDCTQuant;
|
|
781
|
-
for (i = 0; i < I64; ++i) {
|
|
782
|
-
// Apply the quantization and scaling factor & Round to nearest integer
|
|
783
|
-
fDCTQuant = data[i] * fdTbl[i];
|
|
784
|
-
this._outputFDctQuant[i] =
|
|
785
|
-
fDCTQuant > 0 ? Math.trunc(fDCTQuant + 0.5) : Math.trunc(fDCTQuant - 0.5);
|
|
786
|
-
}
|
|
787
|
-
return this._outputFDctQuant;
|
|
788
|
-
}
|
|
789
|
-
/**
|
|
790
|
-
* @internal
|
|
791
|
-
*/
|
|
792
|
-
writeAPP0() {
|
|
793
|
-
this.writeWord(0xffe0); // marker
|
|
794
|
-
this.writeWord(16); // length
|
|
795
|
-
this.writeByte(0x4a); // J
|
|
796
|
-
this.writeByte(0x46); // F
|
|
797
|
-
this.writeByte(0x49); // I
|
|
798
|
-
this.writeByte(0x46); // F
|
|
799
|
-
// cspell:disable-next-line
|
|
800
|
-
this.writeByte(0); // = "JFIF",'\0'
|
|
801
|
-
this.writeByte(1); // version hi
|
|
802
|
-
this.writeByte(1); // version lo
|
|
803
|
-
this.writeByte(0); // xy units
|
|
804
|
-
this.writeWord(1); // x density
|
|
805
|
-
this.writeWord(1); // y density
|
|
806
|
-
this.writeByte(0); // thumb n width
|
|
807
|
-
this.writeByte(0); // thumb n height
|
|
808
|
-
}
|
|
809
|
-
/**
|
|
810
|
-
* @internal
|
|
811
|
-
*/
|
|
812
|
-
writeSOF0(width, height) {
|
|
813
|
-
this.writeWord(0xffc0); // marker
|
|
814
|
-
this.writeWord(17); // length, true color YUV JPG
|
|
815
|
-
this.writeByte(8); // precision
|
|
816
|
-
this.writeWord(height);
|
|
817
|
-
this.writeWord(width);
|
|
818
|
-
this.writeByte(3); // nr of components
|
|
819
|
-
this.writeByte(1); // IdY
|
|
820
|
-
this.writeByte(0x11); // HVY
|
|
821
|
-
this.writeByte(0); // QTY
|
|
822
|
-
this.writeByte(2); // IdU
|
|
823
|
-
this.writeByte(0x11); // HVU
|
|
824
|
-
this.writeByte(1); // QTU
|
|
825
|
-
this.writeByte(3); // IdV
|
|
826
|
-
this.writeByte(0x11); // HVV
|
|
827
|
-
this.writeByte(1); // QTV
|
|
828
|
-
}
|
|
829
|
-
/**
|
|
830
|
-
* @internal
|
|
831
|
-
*/
|
|
832
|
-
writeDQT() {
|
|
833
|
-
this.writeWord(0xffdb); // marker
|
|
834
|
-
this.writeWord(132); // length
|
|
835
|
-
this.writeByte(0);
|
|
836
|
-
for (let i = 0; i < 64; i++) {
|
|
837
|
-
this.writeByte(this._yTable[i]);
|
|
838
|
-
}
|
|
839
|
-
this.writeByte(1);
|
|
840
|
-
for (let j = 0; j < 64; j++) {
|
|
841
|
-
this.writeByte(this._uvTable[j]);
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
/**
|
|
845
|
-
* @internal
|
|
846
|
-
*/
|
|
847
|
-
writeDHT() {
|
|
848
|
-
this.writeWord(0xffc4); // marker
|
|
849
|
-
this.writeWord(0x01a2); // length
|
|
850
|
-
this.writeByte(0); // HTYDc info
|
|
851
|
-
for (let i = 0; i < 16; i++) {
|
|
852
|
-
this.writeByte(JpegEncoder._STD_DC_LUMINANCE_NR_CODES[i + 1]);
|
|
853
|
-
}
|
|
854
|
-
for (let j = 0; j <= 11; j++) {
|
|
855
|
-
this.writeByte(JpegEncoder._STD_DC_LUMINANCE_VALUES[j]);
|
|
856
|
-
}
|
|
857
|
-
this.writeByte(0x10); // HTYAc info
|
|
858
|
-
for (let k = 0; k < 16; k++) {
|
|
859
|
-
this.writeByte(JpegEncoder._STD_AC_LUMINANCE_NR_CODES[k + 1]);
|
|
860
|
-
}
|
|
861
|
-
for (let l = 0; l <= 161; l++) {
|
|
862
|
-
this.writeByte(JpegEncoder._STD_AC_LUMINANCE_VALUES[l]);
|
|
863
|
-
}
|
|
864
|
-
this.writeByte(1); // HTUDc info
|
|
865
|
-
for (let m = 0; m < 16; m++) {
|
|
866
|
-
this.writeByte(JpegEncoder._STD_DC_CHROMINANCE_NR_CODES[m + 1]);
|
|
867
|
-
}
|
|
868
|
-
for (let n = 0; n <= 11; n++) {
|
|
869
|
-
this.writeByte(JpegEncoder._STD_DC_CHROMINANCE_VALUES[n]);
|
|
870
|
-
}
|
|
871
|
-
this.writeByte(0x11); // HTUAc info
|
|
872
|
-
for (let o = 0; o < 16; o++) {
|
|
873
|
-
this.writeByte(JpegEncoder._STD_AC_CHROMINANCE_NR_CODES[o + 1]);
|
|
874
|
-
}
|
|
875
|
-
for (let p = 0; p <= 161; p++) {
|
|
876
|
-
this.writeByte(JpegEncoder._STD_AC_CHROMINANCE_VALUES[p]);
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
/**
|
|
880
|
-
* @internal
|
|
881
|
-
*/
|
|
882
|
-
writeSOS() {
|
|
883
|
-
this.writeWord(0xffda); // marker
|
|
884
|
-
this.writeWord(12); // length
|
|
885
|
-
this.writeByte(3); // nr of components
|
|
886
|
-
this.writeByte(1); // IdY
|
|
887
|
-
this.writeByte(0); // HTY
|
|
888
|
-
this.writeByte(2); // IdU
|
|
889
|
-
this.writeByte(0x11); // HTU
|
|
890
|
-
this.writeByte(3); // IdV
|
|
891
|
-
this.writeByte(0x11); // HTV
|
|
892
|
-
this.writeByte(0); // Ss
|
|
893
|
-
this.writeByte(0x3f); // Se
|
|
894
|
-
this.writeByte(0); // Bf
|
|
895
|
-
}
|
|
896
|
-
/**
|
|
897
|
-
* @internal
|
|
898
|
-
*/
|
|
899
|
-
processDU(CDU, fdTbl, passedDC, HTDc, HTAc) {
|
|
900
|
-
let DC = passedDC;
|
|
901
|
-
const EOB = HTAc[0x00];
|
|
902
|
-
const m16zeroes = HTAc[0xf0];
|
|
903
|
-
let pos;
|
|
904
|
-
const I16 = 16;
|
|
905
|
-
const I63 = 63;
|
|
906
|
-
const I64 = 64;
|
|
907
|
-
const DU_DCT = this.fDCTQuant(CDU, fdTbl);
|
|
908
|
-
// ZigZag reorder
|
|
909
|
-
for (let j = 0; j < I64; ++j) {
|
|
910
|
-
this._du[JpegEncoder._SIG_ZAG[j]] = DU_DCT[j];
|
|
911
|
-
}
|
|
912
|
-
const diff = this._du[0] - DC;
|
|
913
|
-
DC = this._du[0];
|
|
914
|
-
// Encode DC
|
|
915
|
-
if (diff === 0) {
|
|
916
|
-
this.writeBits(HTDc[0]); // Diff might be 0
|
|
917
|
-
}
|
|
918
|
-
else {
|
|
919
|
-
pos = 32767 + diff;
|
|
920
|
-
this.writeBits(HTDc[this._category[pos]]);
|
|
921
|
-
this.writeBits(this._bitCode[pos]);
|
|
922
|
-
}
|
|
923
|
-
// Encode ACs
|
|
924
|
-
let end0pos = 63; // was const... which is crazy
|
|
925
|
-
for (; end0pos > 0 && this._du[end0pos] === 0; end0pos--) { }
|
|
926
|
-
// end0pos = first element in reverse order !=0
|
|
927
|
-
if (end0pos === 0) {
|
|
928
|
-
this.writeBits(EOB);
|
|
929
|
-
return DC;
|
|
930
|
-
}
|
|
931
|
-
let i = 1;
|
|
932
|
-
let lng;
|
|
933
|
-
while (i <= end0pos) {
|
|
934
|
-
const startPos = i;
|
|
935
|
-
for (; this._du[i] === 0 && i <= end0pos; ++i) { }
|
|
936
|
-
let nrZeroes = i - startPos;
|
|
937
|
-
if (nrZeroes >= I16) {
|
|
938
|
-
lng = nrZeroes >> 4;
|
|
939
|
-
for (let nrMarker = 1; nrMarker <= lng; ++nrMarker) {
|
|
940
|
-
this.writeBits(m16zeroes);
|
|
941
|
-
}
|
|
942
|
-
nrZeroes &= 0xf;
|
|
943
|
-
}
|
|
944
|
-
pos = 32767 + this._du[i];
|
|
945
|
-
this.writeBits(HTAc[(nrZeroes << 4) + this._category[pos]]);
|
|
946
|
-
this.writeBits(this._bitCode[pos]);
|
|
947
|
-
i++;
|
|
948
|
-
}
|
|
949
|
-
if (end0pos !== I63) {
|
|
950
|
-
this.writeBits(EOB);
|
|
951
|
-
}
|
|
952
|
-
return DC;
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
// Copyright 2024 IOTA Stiftung.
|
|
957
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
958
|
-
/* eslint-disable no-bitwise */
|
|
959
|
-
/* eslint-disable no-mixed-operators */
|
|
960
|
-
/* eslint-disable no-continue */
|
|
961
|
-
/**
|
|
962
|
-
* PNG Encoder.
|
|
963
|
-
* Based on https://github.com/photopea/UPNG.js.
|
|
964
|
-
*/
|
|
965
|
-
class PngEncoder {
|
|
966
|
-
/**
|
|
967
|
-
* Encode the image frames to png.
|
|
968
|
-
* @param buffers The frame buffers to encode.
|
|
969
|
-
* @param w The image width.
|
|
970
|
-
* @param h The image height.
|
|
971
|
-
* @returns The data for the image.
|
|
972
|
-
*/
|
|
973
|
-
async encode(buffers, w, h) {
|
|
974
|
-
const ps = 0;
|
|
975
|
-
const forbidPlte = false;
|
|
976
|
-
const data = new Uint8Array(buffers[0].byteLength * buffers.length + 100);
|
|
977
|
-
const wr = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
|
978
|
-
for (let i = 0; i < 8; i++) {
|
|
979
|
-
data[i] = wr[i];
|
|
980
|
-
}
|
|
981
|
-
let offset = 8;
|
|
982
|
-
const nImg = await this.compressPNG(buffers, w, h, ps, forbidPlte);
|
|
983
|
-
this.writeUint(data, offset, 13);
|
|
984
|
-
offset += 4;
|
|
985
|
-
// cspell:disable-next-line
|
|
986
|
-
this.writeASCII(data, offset, "IHDR");
|
|
987
|
-
offset += 4;
|
|
988
|
-
this.writeUint(data, offset, w);
|
|
989
|
-
offset += 4;
|
|
990
|
-
this.writeUint(data, offset, h);
|
|
991
|
-
offset += 4;
|
|
992
|
-
data[offset] = nImg.depth;
|
|
993
|
-
offset++;
|
|
994
|
-
data[offset] = nImg.cType;
|
|
995
|
-
offset++;
|
|
996
|
-
data[offset] = 0; // compress
|
|
997
|
-
offset++;
|
|
998
|
-
data[offset] = 0; // filter
|
|
999
|
-
offset++;
|
|
1000
|
-
data[offset] = 0; // interlace
|
|
1001
|
-
offset++;
|
|
1002
|
-
this.writeUint(data, offset, this.crc(data, offset - 17, 17));
|
|
1003
|
-
offset += 4; // crc
|
|
1004
|
-
// 9 bytes to say, that it is sRGB
|
|
1005
|
-
this.writeUint(data, offset, 1);
|
|
1006
|
-
offset += 4;
|
|
1007
|
-
this.writeASCII(data, offset, "sRGB");
|
|
1008
|
-
offset += 4;
|
|
1009
|
-
data[offset] = 1;
|
|
1010
|
-
offset++;
|
|
1011
|
-
this.writeUint(data, offset, this.crc(data, offset - 5, 5));
|
|
1012
|
-
offset += 4; // crc
|
|
1013
|
-
const anim = buffers.length > 1;
|
|
1014
|
-
if (anim) {
|
|
1015
|
-
this.writeUint(data, offset, 8);
|
|
1016
|
-
offset += 4;
|
|
1017
|
-
this.writeASCII(data, offset, "acTL");
|
|
1018
|
-
offset += 4;
|
|
1019
|
-
this.writeUint(data, offset, buffers.length);
|
|
1020
|
-
offset += 4;
|
|
1021
|
-
this.writeUint(data, offset, 0);
|
|
1022
|
-
offset += 4;
|
|
1023
|
-
this.writeUint(data, offset, this.crc(data, offset - 12, 12));
|
|
1024
|
-
offset += 4; // crc
|
|
1025
|
-
}
|
|
1026
|
-
if (nImg.cType === 3) {
|
|
1027
|
-
const dl = nImg.plte.length;
|
|
1028
|
-
this.writeUint(data, offset, dl * 3);
|
|
1029
|
-
offset += 4;
|
|
1030
|
-
this.writeASCII(data, offset, "PLTE");
|
|
1031
|
-
offset += 4;
|
|
1032
|
-
for (let i = 0; i < dl; i++) {
|
|
1033
|
-
const ti = i * 3;
|
|
1034
|
-
const c = nImg.plte[i];
|
|
1035
|
-
const r = c & 255;
|
|
1036
|
-
const g = (c >> 8) & 255;
|
|
1037
|
-
const b = (c >> 16) & 255;
|
|
1038
|
-
data[offset + ti + 0] = r;
|
|
1039
|
-
data[offset + ti + 1] = g;
|
|
1040
|
-
data[offset + ti + 2] = b;
|
|
1041
|
-
}
|
|
1042
|
-
offset += dl * 3;
|
|
1043
|
-
this.writeUint(data, offset, this.crc(data, offset - dl * 3 - 4, dl * 3 + 4));
|
|
1044
|
-
offset += 4; // crc
|
|
1045
|
-
if (nImg.gotAlpha) {
|
|
1046
|
-
this.writeUint(data, offset, dl);
|
|
1047
|
-
offset += 4;
|
|
1048
|
-
this.writeASCII(data, offset, "tRNS");
|
|
1049
|
-
offset += 4;
|
|
1050
|
-
for (let i = 0; i < dl; i++) {
|
|
1051
|
-
data[offset + i] = (nImg.plte[i] >> 24) & 255;
|
|
1052
|
-
}
|
|
1053
|
-
offset += dl;
|
|
1054
|
-
this.writeUint(data, offset, this.crc(data, offset - dl - 4, dl + 4));
|
|
1055
|
-
offset += 4; // crc
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
let fi = 0;
|
|
1059
|
-
for (let j = 0; j < nImg.frames.length; j++) {
|
|
1060
|
-
const fr = nImg.frames[j];
|
|
1061
|
-
if (anim) {
|
|
1062
|
-
this.writeUint(data, offset, 26);
|
|
1063
|
-
offset += 4;
|
|
1064
|
-
this.writeASCII(data, offset, "fcTL");
|
|
1065
|
-
offset += 4;
|
|
1066
|
-
this.writeUint(data, offset, fi++);
|
|
1067
|
-
offset += 4;
|
|
1068
|
-
this.writeUint(data, offset, fr.rect.width);
|
|
1069
|
-
offset += 4;
|
|
1070
|
-
this.writeUint(data, offset, fr.rect.height);
|
|
1071
|
-
offset += 4;
|
|
1072
|
-
this.writeUint(data, offset, fr.rect.x);
|
|
1073
|
-
offset += 4;
|
|
1074
|
-
this.writeUint(data, offset, fr.rect.y);
|
|
1075
|
-
offset += 4;
|
|
1076
|
-
this.writeUshort(data, offset, 0);
|
|
1077
|
-
offset += 2;
|
|
1078
|
-
this.writeUshort(data, offset, 1000);
|
|
1079
|
-
offset += 2;
|
|
1080
|
-
data[offset] = fr.dispose;
|
|
1081
|
-
offset++; // dispose
|
|
1082
|
-
data[offset] = fr.blend;
|
|
1083
|
-
offset++; // blend
|
|
1084
|
-
this.writeUint(data, offset, this.crc(data, offset - 30, 30));
|
|
1085
|
-
offset += 4; // crc
|
|
1086
|
-
}
|
|
1087
|
-
const imgD = fr.cImg;
|
|
1088
|
-
const dl = imgD?.length ?? 0;
|
|
1089
|
-
this.writeUint(data, offset, dl + (j === 0 ? 0 : 4));
|
|
1090
|
-
offset += 4;
|
|
1091
|
-
const iOff = offset;
|
|
1092
|
-
// cspell:disable-next-line
|
|
1093
|
-
this.writeASCII(data, offset, j === 0 ? "IDAT" : "fdAT");
|
|
1094
|
-
offset += 4;
|
|
1095
|
-
if (j !== 0) {
|
|
1096
|
-
this.writeUint(data, offset, fi++);
|
|
1097
|
-
offset += 4;
|
|
1098
|
-
}
|
|
1099
|
-
if (imgD) {
|
|
1100
|
-
for (let i = 0; i < dl; i++) {
|
|
1101
|
-
data[offset + i] = imgD[i];
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
offset += dl;
|
|
1105
|
-
this.writeUint(data, offset, this.crc(data, iOff, offset - iOff));
|
|
1106
|
-
offset += 4; // crc
|
|
1107
|
-
}
|
|
1108
|
-
this.writeUint(data, offset, 0);
|
|
1109
|
-
offset += 4;
|
|
1110
|
-
// cspell:disable-next-line
|
|
1111
|
-
this.writeASCII(data, offset, "IEND");
|
|
1112
|
-
offset += 4;
|
|
1113
|
-
this.writeUint(data, offset, this.crc(data, offset - 4, 4));
|
|
1114
|
-
offset += 4; // crc
|
|
1115
|
-
return new Uint8Array(data.buffer.slice(0, offset));
|
|
1116
|
-
}
|
|
1117
|
-
/**
|
|
1118
|
-
* @internal
|
|
1119
|
-
*/
|
|
1120
|
-
async compressPNG(buffers, w, h, ps, forbidPlte) {
|
|
1121
|
-
const out = this.compress(buffers, w, h, ps, 0, forbidPlte);
|
|
1122
|
-
for (let i = 0; i < buffers.length; i++) {
|
|
1123
|
-
const frm = out.frames[i];
|
|
1124
|
-
const nw = frm.rect.width;
|
|
1125
|
-
const nh = frm.rect.height;
|
|
1126
|
-
const bpl = frm.bpl;
|
|
1127
|
-
const bpp = frm.bpp;
|
|
1128
|
-
const fData = new Uint8Array(nw * bpl + nh);
|
|
1129
|
-
frm.cImg = await this.filterZero(frm.img, nh, bpp, bpl, fData);
|
|
1130
|
-
}
|
|
1131
|
-
return out;
|
|
1132
|
-
}
|
|
1133
|
-
/**
|
|
1134
|
-
* @internal
|
|
1135
|
-
*/
|
|
1136
|
-
compress(inBuffers, w, h, inPs, forGIF, forbidPlte) {
|
|
1137
|
-
let cType = 6;
|
|
1138
|
-
let depth = 8;
|
|
1139
|
-
let bpp = 4;
|
|
1140
|
-
let alphaAnd = 255;
|
|
1141
|
-
let ps = inPs;
|
|
1142
|
-
let buffers = inBuffers;
|
|
1143
|
-
for (let j = 0; j < buffers.length; j++) {
|
|
1144
|
-
// when not quantized, other frames can contain colors, that are not in an initial frame
|
|
1145
|
-
const img = new Uint8Array(buffers[j]);
|
|
1146
|
-
const iLen = img.length;
|
|
1147
|
-
for (let i = 0; i < iLen; i += 4) {
|
|
1148
|
-
alphaAnd &= img[i + 3];
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
let gotAlpha = alphaAnd !== 255;
|
|
1152
|
-
const cMap = {};
|
|
1153
|
-
const pLte = [];
|
|
1154
|
-
if (buffers.length !== 0) {
|
|
1155
|
-
cMap[0] = 0;
|
|
1156
|
-
pLte.push(0);
|
|
1157
|
-
if (ps !== 0) {
|
|
1158
|
-
ps--;
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
if (ps !== 0) {
|
|
1162
|
-
const qRes = this.quantize(buffers, ps, forGIF);
|
|
1163
|
-
buffers = qRes.buffers;
|
|
1164
|
-
for (let i = 0; i < qRes.plte.length; i++) {
|
|
1165
|
-
const c = qRes.plte[i].est?.rgba ?? 0;
|
|
1166
|
-
if (!cMap[c]) {
|
|
1167
|
-
cMap[c] = pLte.length;
|
|
1168
|
-
pLte.push(c);
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
else {
|
|
1173
|
-
// what if ps==0, but there are <=256 colors? we still need to detect, if the palette could be used
|
|
1174
|
-
for (let j = 0; j < buffers.length; j++) {
|
|
1175
|
-
// when not quantized, other frames can contain colors, that are not in an initial frame
|
|
1176
|
-
const img32 = new Uint32Array(buffers[j]);
|
|
1177
|
-
const iLen = img32.length;
|
|
1178
|
-
for (let i = 0; i < iLen; i++) {
|
|
1179
|
-
const c = img32[i];
|
|
1180
|
-
if ((i < w || (c !== img32[i - 1] && c !== img32[i - w])) && !cMap[c]) {
|
|
1181
|
-
cMap[c] = pLte.length;
|
|
1182
|
-
pLte.push(c);
|
|
1183
|
-
if (pLte.length >= 300) {
|
|
1184
|
-
break;
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
const brute = gotAlpha ? forGIF : false; // brute : frames can only be copied, not "blended"
|
|
1191
|
-
const cc = pLte.length;
|
|
1192
|
-
if (cc <= 256 && !forbidPlte) {
|
|
1193
|
-
if (cc <= 2) {
|
|
1194
|
-
depth = 1;
|
|
1195
|
-
}
|
|
1196
|
-
else if (cc <= 4) {
|
|
1197
|
-
depth = 2;
|
|
1198
|
-
}
|
|
1199
|
-
else if (cc <= 16) {
|
|
1200
|
-
depth = 4;
|
|
1201
|
-
}
|
|
1202
|
-
else {
|
|
1203
|
-
depth = 8;
|
|
1204
|
-
}
|
|
1205
|
-
if (forGIF) {
|
|
1206
|
-
depth = 8;
|
|
1207
|
-
}
|
|
1208
|
-
gotAlpha = true;
|
|
1209
|
-
}
|
|
1210
|
-
const frames = [];
|
|
1211
|
-
for (let j = 0; j < buffers.length; j++) {
|
|
1212
|
-
let cImg = new Uint8Array(buffers[j]);
|
|
1213
|
-
let cImg32 = new Uint32Array(cImg.buffer);
|
|
1214
|
-
let nx = 0;
|
|
1215
|
-
let ny = 0;
|
|
1216
|
-
let nw = w;
|
|
1217
|
-
let nh = h;
|
|
1218
|
-
let blend = 0;
|
|
1219
|
-
if (j !== 0 && !brute) {
|
|
1220
|
-
const tLim = forGIF || j === 1 || frames[frames.length - 2].dispose === 2 ? 1 : 2;
|
|
1221
|
-
let tStp = 0;
|
|
1222
|
-
let tArea = 1e9;
|
|
1223
|
-
for (let it = 0; it < tLim; it++) {
|
|
1224
|
-
const p32 = new Uint32Array(buffers[j - 1 - it]);
|
|
1225
|
-
let mix = w;
|
|
1226
|
-
let miy = h;
|
|
1227
|
-
let max = -1;
|
|
1228
|
-
let may = -1;
|
|
1229
|
-
for (let y = 0; y < h; y++) {
|
|
1230
|
-
for (let x = 0; x < w; x++) {
|
|
1231
|
-
const i = y * w + x;
|
|
1232
|
-
if (cImg32[i] !== p32[i]) {
|
|
1233
|
-
if (x < mix) {
|
|
1234
|
-
mix = x;
|
|
1235
|
-
}
|
|
1236
|
-
if (x > max) {
|
|
1237
|
-
max = x;
|
|
1238
|
-
}
|
|
1239
|
-
if (y < miy) {
|
|
1240
|
-
miy = y;
|
|
1241
|
-
}
|
|
1242
|
-
if (y > may) {
|
|
1243
|
-
may = y;
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
const sArea = max === -1 ? 1 : (max - mix + 1) * (may - miy + 1);
|
|
1249
|
-
if (sArea < tArea) {
|
|
1250
|
-
tArea = sArea;
|
|
1251
|
-
tStp = it;
|
|
1252
|
-
if (max === -1) {
|
|
1253
|
-
nx = 0;
|
|
1254
|
-
ny = 0;
|
|
1255
|
-
nw = 1;
|
|
1256
|
-
nh = 1;
|
|
1257
|
-
}
|
|
1258
|
-
else {
|
|
1259
|
-
nx = mix;
|
|
1260
|
-
ny = miy;
|
|
1261
|
-
nw = max - mix + 1;
|
|
1262
|
-
nh = may - miy + 1;
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1266
|
-
const pImg = new Uint8Array(buffers[j - 1 - tStp]);
|
|
1267
|
-
if (tStp === 1) {
|
|
1268
|
-
frames[frames.length - 1].dispose = 2;
|
|
1269
|
-
}
|
|
1270
|
-
const nImg = new Uint8Array(nw * nh * 4);
|
|
1271
|
-
this.copyTile(pImg, w, h, nImg, nw, nh, -nx, -ny, 0);
|
|
1272
|
-
if (this.copyTile(cImg, w, h, nImg, nw, nh, -nx, -ny, 3)) {
|
|
1273
|
-
this.copyTile(cImg, w, h, nImg, nw, nh, -nx, -ny, 2);
|
|
1274
|
-
blend = 1;
|
|
1275
|
-
}
|
|
1276
|
-
else {
|
|
1277
|
-
this.copyTile(cImg, w, h, nImg, nw, nh, -nx, -ny, 0);
|
|
1278
|
-
blend = 0;
|
|
1279
|
-
}
|
|
1280
|
-
cImg = nImg;
|
|
1281
|
-
cImg32 = new Uint32Array(cImg.buffer);
|
|
1282
|
-
}
|
|
1283
|
-
let bpl = 4 * nw;
|
|
1284
|
-
if (cc <= 256 && !forbidPlte) {
|
|
1285
|
-
bpl = Math.ceil((depth * nw) / 8);
|
|
1286
|
-
const nImg = new Uint8Array(bpl * nh);
|
|
1287
|
-
for (let y = 0; y < nh; y++) {
|
|
1288
|
-
const i = y * bpl;
|
|
1289
|
-
const ii = y * nw;
|
|
1290
|
-
if (depth === 8) {
|
|
1291
|
-
for (let x = 0; x < nw; x++) {
|
|
1292
|
-
nImg[i + x] = cMap[cImg32[ii + x]];
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
else if (depth === 4) {
|
|
1296
|
-
for (let x = 0; x < nw; x++) {
|
|
1297
|
-
nImg[i + (x >> 1)] |= cMap[cImg32[ii + x]] << (4 - (x & 1) * 4);
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1300
|
-
else if (depth === 2) {
|
|
1301
|
-
for (let x = 0; x < nw; x++) {
|
|
1302
|
-
nImg[i + (x >> 2)] |= cMap[cImg32[ii + x]] << (6 - (x & 3) * 2);
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
else if (depth === 1) {
|
|
1306
|
-
for (let x = 0; x < nw; x++) {
|
|
1307
|
-
nImg[i + (x >> 3)] |= cMap[cImg32[ii + x]] << (7 - (x & 7) * 1);
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
cImg = nImg;
|
|
1312
|
-
cType = 3;
|
|
1313
|
-
bpp = 1;
|
|
1314
|
-
}
|
|
1315
|
-
else if (!gotAlpha && buffers.length === 1) {
|
|
1316
|
-
// some next "reduced" frames may contain alpha for blending
|
|
1317
|
-
const nImg = new Uint8Array(nw * nh * 3);
|
|
1318
|
-
const area = nw * nh;
|
|
1319
|
-
for (let i = 0; i < area; i++) {
|
|
1320
|
-
const ti = i * 3;
|
|
1321
|
-
const qi = i * 4;
|
|
1322
|
-
nImg[ti] = cImg[qi];
|
|
1323
|
-
nImg[ti + 1] = cImg[qi + 1];
|
|
1324
|
-
nImg[ti + 2] = cImg[qi + 2];
|
|
1325
|
-
}
|
|
1326
|
-
cImg = nImg;
|
|
1327
|
-
cType = 2;
|
|
1328
|
-
bpp = 3;
|
|
1329
|
-
bpl = 3 * nw;
|
|
1330
|
-
}
|
|
1331
|
-
frames.push({
|
|
1332
|
-
rect: {
|
|
1333
|
-
x: nx,
|
|
1334
|
-
y: ny,
|
|
1335
|
-
width: nw,
|
|
1336
|
-
height: nh
|
|
1337
|
-
},
|
|
1338
|
-
img: cImg,
|
|
1339
|
-
bpl,
|
|
1340
|
-
bpp,
|
|
1341
|
-
blend,
|
|
1342
|
-
dispose: brute ? 1 : 0
|
|
1343
|
-
});
|
|
1344
|
-
}
|
|
1345
|
-
return { cType, depth, plte: pLte, gotAlpha, frames };
|
|
1346
|
-
}
|
|
1347
|
-
/**
|
|
1348
|
-
* @internal
|
|
1349
|
-
*/
|
|
1350
|
-
async filterZero(img, h, bpp, bpl, data) {
|
|
1351
|
-
const fls = [];
|
|
1352
|
-
for (let t = 0; t < 5; t++) {
|
|
1353
|
-
if (h * bpl > 500000 && (t === 2 || t === 3 || t === 4)) {
|
|
1354
|
-
continue;
|
|
1355
|
-
}
|
|
1356
|
-
for (let y = 0; y < h; y++) {
|
|
1357
|
-
this.filterLine(data, img, y, bpl, bpp, t);
|
|
1358
|
-
}
|
|
1359
|
-
const deflated = await Compression.compress(data, "deflate");
|
|
1360
|
-
fls.push(deflated);
|
|
1361
|
-
if (bpp === 1) {
|
|
1362
|
-
break;
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
let ti = 0;
|
|
1366
|
-
let tSize = 1e9;
|
|
1367
|
-
for (let i = 0; i < fls.length; i++) {
|
|
1368
|
-
if (fls[i].length < tSize) {
|
|
1369
|
-
ti = i;
|
|
1370
|
-
tSize = fls[i].length;
|
|
1371
|
-
}
|
|
1372
|
-
}
|
|
1373
|
-
return fls[ti];
|
|
1374
|
-
}
|
|
1375
|
-
/**
|
|
1376
|
-
* @internal
|
|
1377
|
-
*/
|
|
1378
|
-
filterLine(data, img, y, bpl, bpp, type) {
|
|
1379
|
-
const i = y * bpl;
|
|
1380
|
-
let di = i + y;
|
|
1381
|
-
data[di] = type;
|
|
1382
|
-
di++;
|
|
1383
|
-
if (type === 0) {
|
|
1384
|
-
for (let x = 0; x < bpl; x++) {
|
|
1385
|
-
data[di + x] = img[i + x];
|
|
1386
|
-
}
|
|
1387
|
-
}
|
|
1388
|
-
else if (type === 1) {
|
|
1389
|
-
for (let x = 0; x < bpp; x++) {
|
|
1390
|
-
data[di + x] = img[i + x];
|
|
1391
|
-
}
|
|
1392
|
-
for (let x = bpp; x < bpl; x++) {
|
|
1393
|
-
data[di + x] = (img[i + x] - img[i + x - bpp] + 256) & 255;
|
|
1394
|
-
}
|
|
1395
|
-
}
|
|
1396
|
-
else if (y === 0) {
|
|
1397
|
-
for (let x = 0; x < bpp; x++) {
|
|
1398
|
-
data[di + x] = img[i + x];
|
|
1399
|
-
}
|
|
1400
|
-
if (type === 2) {
|
|
1401
|
-
for (let x = bpp; x < bpl; x++) {
|
|
1402
|
-
data[di + x] = img[i + x];
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
if (type === 3) {
|
|
1406
|
-
for (let x = bpp; x < bpl; x++) {
|
|
1407
|
-
data[di + x] = (img[i + x] - (img[i + x - bpp] >> 1) + 256) & 255;
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
if (type === 4) {
|
|
1411
|
-
for (let x = bpp; x < bpl; x++) {
|
|
1412
|
-
data[di + x] = (img[i + x] - this.paeth(img[i + x - bpp], 0, 0) + 256) & 255;
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
else {
|
|
1417
|
-
if (type === 2) {
|
|
1418
|
-
for (let x = 0; x < bpl; x++) {
|
|
1419
|
-
data[di + x] = (img[i + x] + 256 - img[i + x - bpl]) & 255;
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
if (type === 3) {
|
|
1423
|
-
for (let x = 0; x < bpp; x++) {
|
|
1424
|
-
data[di + x] = (img[i + x] + 256 - (img[i + x - bpl] >> 1)) & 255;
|
|
1425
|
-
}
|
|
1426
|
-
for (let x = bpp; x < bpl; x++) {
|
|
1427
|
-
data[di + x] = (img[i + x] + 256 - ((img[i + x - bpl] + img[i + x - bpp]) >> 1)) & 255;
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1430
|
-
if (type === 4) {
|
|
1431
|
-
for (let x = 0; x < bpp; x++) {
|
|
1432
|
-
data[di + x] = (img[i + x] + 256 - this.paeth(0, img[i + x - bpl], 0)) & 255;
|
|
1433
|
-
}
|
|
1434
|
-
for (let x = bpp; x < bpl; x++) {
|
|
1435
|
-
data[di + x] =
|
|
1436
|
-
(img[i + x] +
|
|
1437
|
-
256 -
|
|
1438
|
-
this.paeth(img[i + x - bpp], img[i + x - bpl], img[i + x - bpp - bpl])) &
|
|
1439
|
-
255;
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
/**
|
|
1445
|
-
* @internal
|
|
1446
|
-
*/
|
|
1447
|
-
paeth(a, b, c) {
|
|
1448
|
-
const p = a + b - c;
|
|
1449
|
-
const pa = Math.abs(p - a);
|
|
1450
|
-
const pb = Math.abs(p - b);
|
|
1451
|
-
const pc = Math.abs(p - c);
|
|
1452
|
-
if (pa <= pb && pa <= pc) {
|
|
1453
|
-
return a;
|
|
1454
|
-
}
|
|
1455
|
-
if (pb <= pc) {
|
|
1456
|
-
return b;
|
|
1457
|
-
}
|
|
1458
|
-
return c;
|
|
1459
|
-
}
|
|
1460
|
-
/**
|
|
1461
|
-
* @internal
|
|
1462
|
-
*/
|
|
1463
|
-
writeASCII(data, p, s) {
|
|
1464
|
-
for (let i = 0; i < s.length; i++) {
|
|
1465
|
-
data[p + i] = s.charCodeAt(i);
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
/**
|
|
1469
|
-
* @internal
|
|
1470
|
-
*/
|
|
1471
|
-
writeUint(buff, p, n) {
|
|
1472
|
-
buff[p] = (n >> 24) & 255;
|
|
1473
|
-
buff[p + 1] = (n >> 16) & 255;
|
|
1474
|
-
buff[p + 2] = (n >> 8) & 255;
|
|
1475
|
-
buff[p + 3] = n & 255;
|
|
1476
|
-
}
|
|
1477
|
-
/**
|
|
1478
|
-
* @internal
|
|
1479
|
-
*/
|
|
1480
|
-
writeUshort(buff, p, n) {
|
|
1481
|
-
buff[p] = (n >> 8) & 255;
|
|
1482
|
-
buff[p + 1] = n & 255;
|
|
1483
|
-
}
|
|
1484
|
-
/**
|
|
1485
|
-
* @internal
|
|
1486
|
-
*/
|
|
1487
|
-
copyTile(sb, sw, sh, tb, tw, th, xOffset, yOffset, mode) {
|
|
1488
|
-
const w = Math.min(sw, tw);
|
|
1489
|
-
const h = Math.min(sh, th);
|
|
1490
|
-
let si = 0;
|
|
1491
|
-
let ti = 0;
|
|
1492
|
-
for (let y = 0; y < h; y++) {
|
|
1493
|
-
for (let x = 0; x < w; x++) {
|
|
1494
|
-
if (xOffset >= 0 && yOffset >= 0) {
|
|
1495
|
-
si = (y * sw + x) << 2;
|
|
1496
|
-
ti = ((yOffset + y) * tw + xOffset + x) << 2;
|
|
1497
|
-
}
|
|
1498
|
-
else {
|
|
1499
|
-
si = ((-yOffset + y) * sw - xOffset + x) << 2;
|
|
1500
|
-
ti = (y * tw + x) << 2;
|
|
1501
|
-
}
|
|
1502
|
-
if (mode === 0) {
|
|
1503
|
-
tb[ti] = sb[si];
|
|
1504
|
-
tb[ti + 1] = sb[si + 1];
|
|
1505
|
-
tb[ti + 2] = sb[si + 2];
|
|
1506
|
-
tb[ti + 3] = sb[si + 3];
|
|
1507
|
-
}
|
|
1508
|
-
else if (mode === 1) {
|
|
1509
|
-
const fa = sb[si + 3] * (1 / 255);
|
|
1510
|
-
const fr = sb[si] * fa;
|
|
1511
|
-
const fg = sb[si + 1] * fa;
|
|
1512
|
-
const fb = sb[si + 2] * fa;
|
|
1513
|
-
const ba = tb[ti + 3] * (1 / 255);
|
|
1514
|
-
const br = tb[ti] * ba;
|
|
1515
|
-
const bg = tb[ti + 1] * ba;
|
|
1516
|
-
const bb = tb[ti + 2] * ba;
|
|
1517
|
-
const ifa = 1 - fa;
|
|
1518
|
-
const oa = fa + ba * ifa;
|
|
1519
|
-
const ioa = oa === 0 ? 0 : 1 / oa;
|
|
1520
|
-
tb[ti + 3] = 255 * oa;
|
|
1521
|
-
tb[ti + 0] = (fr + br * ifa) * ioa;
|
|
1522
|
-
tb[ti + 1] = (fg + bg * ifa) * ioa;
|
|
1523
|
-
tb[ti + 2] = (fb + bb * ifa) * ioa;
|
|
1524
|
-
}
|
|
1525
|
-
else if (mode === 2) {
|
|
1526
|
-
// copy only differences, otherwise zero
|
|
1527
|
-
const fa = sb[si + 3];
|
|
1528
|
-
const fr = sb[si];
|
|
1529
|
-
const fg = sb[si + 1];
|
|
1530
|
-
const fb = sb[si + 2];
|
|
1531
|
-
const ba = tb[ti + 3];
|
|
1532
|
-
const br = tb[ti];
|
|
1533
|
-
const bg = tb[ti + 1];
|
|
1534
|
-
const bb = tb[ti + 2];
|
|
1535
|
-
if (fa === ba && fr === br && fg === bg && fb === bb) {
|
|
1536
|
-
tb[ti] = 0;
|
|
1537
|
-
tb[ti + 1] = 0;
|
|
1538
|
-
tb[ti + 2] = 0;
|
|
1539
|
-
tb[ti + 3] = 0;
|
|
1540
|
-
}
|
|
1541
|
-
else {
|
|
1542
|
-
tb[ti] = fr;
|
|
1543
|
-
tb[ti + 1] = fg;
|
|
1544
|
-
tb[ti + 2] = fb;
|
|
1545
|
-
tb[ti + 3] = fa;
|
|
1546
|
-
}
|
|
1547
|
-
}
|
|
1548
|
-
else if (mode === 3) {
|
|
1549
|
-
// check if can be blended
|
|
1550
|
-
const fa = sb[si + 3];
|
|
1551
|
-
const fr = sb[si];
|
|
1552
|
-
const fg = sb[si + 1];
|
|
1553
|
-
const fb = sb[si + 2];
|
|
1554
|
-
const ba = tb[ti + 3];
|
|
1555
|
-
const br = tb[ti];
|
|
1556
|
-
const bg = tb[ti + 1];
|
|
1557
|
-
const bb = tb[ti + 2];
|
|
1558
|
-
if (fa === ba && fr === br && fg === bg && fb === bb) {
|
|
1559
|
-
continue;
|
|
1560
|
-
}
|
|
1561
|
-
if (fa < 220 && ba > 20) {
|
|
1562
|
-
return false;
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
1566
|
-
}
|
|
1567
|
-
return true;
|
|
1568
|
-
}
|
|
1569
|
-
/**
|
|
1570
|
-
* @internal
|
|
1571
|
-
*/
|
|
1572
|
-
crc(b, o, l) {
|
|
1573
|
-
return this.crcUpdate(0xffffffff, b, o, l) ^ 0xffffffff;
|
|
1574
|
-
}
|
|
1575
|
-
/**
|
|
1576
|
-
* @internal
|
|
1577
|
-
*/
|
|
1578
|
-
crcUpdate(c, buf, off, len) {
|
|
1579
|
-
let localC = c;
|
|
1580
|
-
const crcTable = this.crcTable();
|
|
1581
|
-
for (let i = 0; i < len; i++) {
|
|
1582
|
-
localC = crcTable[(localC ^ buf[off + i]) & 0xff] ^ (localC >>> 8);
|
|
1583
|
-
}
|
|
1584
|
-
return localC;
|
|
1585
|
-
}
|
|
1586
|
-
/**
|
|
1587
|
-
* @internal
|
|
1588
|
-
*/
|
|
1589
|
-
crcTable() {
|
|
1590
|
-
const tab = new Uint32Array(256);
|
|
1591
|
-
for (let n = 0; n < 256; n++) {
|
|
1592
|
-
let c = n;
|
|
1593
|
-
for (let k = 0; k < 8; k++) {
|
|
1594
|
-
if (c & 1) {
|
|
1595
|
-
c = 0xedb88320 ^ (c >>> 1);
|
|
1596
|
-
}
|
|
1597
|
-
else {
|
|
1598
|
-
c >>>= 1;
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
tab[n] = c;
|
|
1602
|
-
}
|
|
1603
|
-
return tab;
|
|
1604
|
-
}
|
|
1605
|
-
/**
|
|
1606
|
-
* @internal
|
|
1607
|
-
*/
|
|
1608
|
-
quantize(buffers, ps, roundAlpha) {
|
|
1609
|
-
const imgs = [];
|
|
1610
|
-
let total = 0;
|
|
1611
|
-
for (let i = 0; i < buffers.length; i++) {
|
|
1612
|
-
imgs.push(this.alphaMul(new Uint8Array(buffers[i]), roundAlpha));
|
|
1613
|
-
total += buffers[i].byteLength;
|
|
1614
|
-
}
|
|
1615
|
-
const nImg = new Uint8Array(total);
|
|
1616
|
-
const nImg32 = new Uint32Array(nImg.buffer);
|
|
1617
|
-
let nOff = 0;
|
|
1618
|
-
for (let i = 0; i < imgs.length; i++) {
|
|
1619
|
-
const img = imgs[i];
|
|
1620
|
-
const il = img.length;
|
|
1621
|
-
for (let j = 0; j < il; j++) {
|
|
1622
|
-
nImg[nOff + j] = img[j];
|
|
1623
|
-
}
|
|
1624
|
-
nOff += il;
|
|
1625
|
-
}
|
|
1626
|
-
const root = {
|
|
1627
|
-
i0: 0,
|
|
1628
|
-
i1: nImg.length,
|
|
1629
|
-
bst: null,
|
|
1630
|
-
est: null,
|
|
1631
|
-
tDst: 0,
|
|
1632
|
-
left: null,
|
|
1633
|
-
right: null
|
|
1634
|
-
};
|
|
1635
|
-
root.bst = this.quantizeStats(nImg, root.i0, root.i1);
|
|
1636
|
-
root.est = this.quantizeEStats(root.bst);
|
|
1637
|
-
const leafs = [root];
|
|
1638
|
-
while (leafs.length < ps) {
|
|
1639
|
-
let maxL = 0;
|
|
1640
|
-
let mi = 0;
|
|
1641
|
-
for (let i = 0; i < leafs.length; i++) {
|
|
1642
|
-
const est = leafs[i].est;
|
|
1643
|
-
if (est && est.L > maxL) {
|
|
1644
|
-
maxL = est.L;
|
|
1645
|
-
mi = i;
|
|
1646
|
-
}
|
|
1647
|
-
}
|
|
1648
|
-
if (maxL < 1e-3) {
|
|
1649
|
-
break;
|
|
1650
|
-
}
|
|
1651
|
-
const node = leafs[mi];
|
|
1652
|
-
const s0 = this.quantizeSplitPixels(nImg, nImg32, node.i0, node.i1, node.est?.e ?? [], node.est?.eMq255 ?? 0);
|
|
1653
|
-
const ln = {
|
|
1654
|
-
i0: node.i0,
|
|
1655
|
-
i1: s0,
|
|
1656
|
-
bst: null,
|
|
1657
|
-
est: null,
|
|
1658
|
-
tDst: 0,
|
|
1659
|
-
left: null,
|
|
1660
|
-
right: null
|
|
1661
|
-
};
|
|
1662
|
-
ln.bst = this.quantizeStats(nImg, ln.i0, ln.i1);
|
|
1663
|
-
ln.est = this.quantizeEStats(ln.bst);
|
|
1664
|
-
const rn = {
|
|
1665
|
-
i0: s0,
|
|
1666
|
-
i1: node.i1,
|
|
1667
|
-
bst: null,
|
|
1668
|
-
est: null,
|
|
1669
|
-
tDst: 0,
|
|
1670
|
-
left: null,
|
|
1671
|
-
right: null
|
|
1672
|
-
};
|
|
1673
|
-
rn.bst = {
|
|
1674
|
-
R: [],
|
|
1675
|
-
m: [],
|
|
1676
|
-
N: (node.bst?.N ?? 0) - ln.bst.N
|
|
1677
|
-
};
|
|
1678
|
-
for (let i = 0; i < 16; i++) {
|
|
1679
|
-
rn.bst.R[i] = (node.bst?.R[i] ?? 0) - ln.bst.R[i];
|
|
1680
|
-
}
|
|
1681
|
-
for (let i = 0; i < 4; i++) {
|
|
1682
|
-
rn.bst.m[i] = (node.bst?.m[i] ?? 0) - ln.bst.m[i];
|
|
1683
|
-
}
|
|
1684
|
-
rn.est = this.quantizeEStats(rn.bst);
|
|
1685
|
-
node.left = ln;
|
|
1686
|
-
node.right = rn;
|
|
1687
|
-
leafs[mi] = ln;
|
|
1688
|
-
leafs.push(rn);
|
|
1689
|
-
}
|
|
1690
|
-
leafs.sort((a, b) => (b.bst?.N ?? 0) - (a.bst?.N ?? 0));
|
|
1691
|
-
const outBuffers = [];
|
|
1692
|
-
for (let ii = 0; ii < imgs.length; ii++) {
|
|
1693
|
-
const sb = new Uint8Array(imgs[ii]);
|
|
1694
|
-
const tb = new Uint32Array(imgs[ii]);
|
|
1695
|
-
const len = sb.length;
|
|
1696
|
-
for (let i = 0; i < len; i += 4) {
|
|
1697
|
-
const r = sb[i] * (1 / 255);
|
|
1698
|
-
const g = sb[i + 1] * (1 / 255);
|
|
1699
|
-
const b = sb[i + 2] * (1 / 255);
|
|
1700
|
-
const a = sb[i + 3] * (1 / 255);
|
|
1701
|
-
let nd = root;
|
|
1702
|
-
while (nd?.left) {
|
|
1703
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1704
|
-
nd = this.quantizePlaneDst(nd.est, r, g, b, a) <= 0 ? nd.left : nd.right;
|
|
1705
|
-
}
|
|
1706
|
-
tb[i >> 2] = nd?.est?.rgba ?? 0;
|
|
1707
|
-
}
|
|
1708
|
-
outBuffers[ii] = tb.buffer;
|
|
1709
|
-
}
|
|
1710
|
-
return { buffers: outBuffers, plte: leafs };
|
|
1711
|
-
}
|
|
1712
|
-
/**
|
|
1713
|
-
* @internal
|
|
1714
|
-
*/
|
|
1715
|
-
quantizeStats(nImg, i0, i1) {
|
|
1716
|
-
const R = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
1717
|
-
const m = [0, 0, 0, 0];
|
|
1718
|
-
const N = (i1 - i0) >> 2;
|
|
1719
|
-
for (let i = i0; i < i1; i += 4) {
|
|
1720
|
-
const r = nImg[i] * (1 / 255);
|
|
1721
|
-
const g = nImg[i + 1] * (1 / 255);
|
|
1722
|
-
const b = nImg[i + 2] * (1 / 255);
|
|
1723
|
-
const a = nImg[i + 3] * (1 / 255);
|
|
1724
|
-
m[0] += r;
|
|
1725
|
-
m[1] += g;
|
|
1726
|
-
m[2] += b;
|
|
1727
|
-
m[3] += a;
|
|
1728
|
-
R[0] += r * r;
|
|
1729
|
-
R[1] += r * g;
|
|
1730
|
-
R[2] += r * b;
|
|
1731
|
-
R[3] += r * a;
|
|
1732
|
-
R[5] += g * g;
|
|
1733
|
-
R[6] += g * b;
|
|
1734
|
-
R[7] += g * a;
|
|
1735
|
-
R[10] += b * b;
|
|
1736
|
-
R[11] += b * a;
|
|
1737
|
-
R[15] += a * a;
|
|
1738
|
-
}
|
|
1739
|
-
R[4] = R[1];
|
|
1740
|
-
R[8] = R[2];
|
|
1741
|
-
R[12] = R[3];
|
|
1742
|
-
R[9] = R[6];
|
|
1743
|
-
R[13] = R[7];
|
|
1744
|
-
R[14] = R[11];
|
|
1745
|
-
return { R, m, N };
|
|
1746
|
-
}
|
|
1747
|
-
/**
|
|
1748
|
-
* @internal
|
|
1749
|
-
*/
|
|
1750
|
-
quantizeEStats(stats) {
|
|
1751
|
-
const R = stats.R;
|
|
1752
|
-
const m = stats.m;
|
|
1753
|
-
const N = stats.N;
|
|
1754
|
-
const m0 = m[0];
|
|
1755
|
-
const m1 = m[1];
|
|
1756
|
-
const m2 = m[2];
|
|
1757
|
-
const m3 = m[3];
|
|
1758
|
-
const iN = N === 0 ? 0 : 1 / N;
|
|
1759
|
-
const rj = [
|
|
1760
|
-
R[0] - m0 * m0 * iN,
|
|
1761
|
-
R[1] - m0 * m1 * iN,
|
|
1762
|
-
R[2] - m0 * m2 * iN,
|
|
1763
|
-
R[3] - m0 * m3 * iN,
|
|
1764
|
-
R[4] - m1 * m0 * iN,
|
|
1765
|
-
R[5] - m1 * m1 * iN,
|
|
1766
|
-
R[6] - m1 * m2 * iN,
|
|
1767
|
-
R[7] - m1 * m3 * iN,
|
|
1768
|
-
R[8] - m2 * m0 * iN,
|
|
1769
|
-
R[9] - m2 * m1 * iN,
|
|
1770
|
-
R[10] - m2 * m2 * iN,
|
|
1771
|
-
R[11] - m2 * m3 * iN,
|
|
1772
|
-
R[12] - m3 * m0 * iN,
|
|
1773
|
-
R[13] - m3 * m1 * iN,
|
|
1774
|
-
R[14] - m3 * m2 * iN,
|
|
1775
|
-
R[15] - m3 * m3 * iN
|
|
1776
|
-
];
|
|
1777
|
-
const A = rj;
|
|
1778
|
-
let b = [0.5, 0.5, 0.5, 0.5];
|
|
1779
|
-
let mi = 0;
|
|
1780
|
-
let tmi = 0;
|
|
1781
|
-
if (N !== 0) {
|
|
1782
|
-
for (let i = 0; i < 10; i++) {
|
|
1783
|
-
b = this.m4MultiplyVec(A, b);
|
|
1784
|
-
tmi = Math.sqrt(this.m4Dot(b, b));
|
|
1785
|
-
b = this.m4Sml(1 / tmi, b);
|
|
1786
|
-
if (Math.abs(tmi - mi) < 1e-9) {
|
|
1787
|
-
break;
|
|
1788
|
-
}
|
|
1789
|
-
mi = tmi;
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
const q = [m0 * iN, m1 * iN, m2 * iN, m3 * iN];
|
|
1793
|
-
const eMq255 = this.m4Dot(this.m4Sml(255, q), b);
|
|
1794
|
-
const ia = q[3] < 0.001 ? 0 : 1 / q[3];
|
|
1795
|
-
return {
|
|
1796
|
-
Cov: rj,
|
|
1797
|
-
q,
|
|
1798
|
-
e: b,
|
|
1799
|
-
L: mi,
|
|
1800
|
-
eMq255,
|
|
1801
|
-
eMq: this.m4Dot(b, q),
|
|
1802
|
-
rgba: ((Math.round(255 * q[3]) << 24) |
|
|
1803
|
-
(Math.round(255 * q[2] * ia) << 16) |
|
|
1804
|
-
(Math.round(255 * q[1] * ia) << 8) |
|
|
1805
|
-
(Math.round(255 * q[0] * ia) << 0)) >>>
|
|
1806
|
-
0
|
|
1807
|
-
};
|
|
1808
|
-
}
|
|
1809
|
-
/**
|
|
1810
|
-
* @internal
|
|
1811
|
-
*/
|
|
1812
|
-
quantizePlaneDst(est, r, g, b, a) {
|
|
1813
|
-
const e = est.e;
|
|
1814
|
-
return e[0] * r + e[1] * g + e[2] * b + e[3] * a - est.eMq;
|
|
1815
|
-
}
|
|
1816
|
-
/**
|
|
1817
|
-
* @internal
|
|
1818
|
-
*/
|
|
1819
|
-
quantizeSplitPixels(nImg, nImg32, i0in, i1in, e, eMq) {
|
|
1820
|
-
let i1 = i1in - 4;
|
|
1821
|
-
let i0 = i0in;
|
|
1822
|
-
while (i0 < i1) {
|
|
1823
|
-
while (this.quantizeVecDot(nImg, i0, e) <= eMq) {
|
|
1824
|
-
i0 += 4;
|
|
1825
|
-
}
|
|
1826
|
-
while (this.quantizeVecDot(nImg, i1, e) > eMq) {
|
|
1827
|
-
i1 -= 4;
|
|
1828
|
-
}
|
|
1829
|
-
if (i0 >= i1) {
|
|
1830
|
-
break;
|
|
1831
|
-
}
|
|
1832
|
-
const t = nImg32[i0 >> 2];
|
|
1833
|
-
nImg32[i0 >> 2] = nImg32[i1 >> 2];
|
|
1834
|
-
nImg32[i1 >> 2] = t;
|
|
1835
|
-
i0 += 4;
|
|
1836
|
-
i1 -= 4;
|
|
1837
|
-
}
|
|
1838
|
-
while (this.quantizeVecDot(nImg, i0, e) > eMq) {
|
|
1839
|
-
i0 -= 4;
|
|
1840
|
-
}
|
|
1841
|
-
return i0 + 4;
|
|
1842
|
-
}
|
|
1843
|
-
/**
|
|
1844
|
-
* @internal
|
|
1845
|
-
*/
|
|
1846
|
-
quantizeVecDot(nImg, i, e) {
|
|
1847
|
-
return nImg[i] * e[0] + nImg[i + 1] * e[1] + nImg[i + 2] * e[2] + nImg[i + 3] * e[3];
|
|
1848
|
-
}
|
|
1849
|
-
/**
|
|
1850
|
-
* @internal
|
|
1851
|
-
*/
|
|
1852
|
-
m4MultiplyVec(m, v) {
|
|
1853
|
-
return [
|
|
1854
|
-
m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * v[3],
|
|
1855
|
-
m[4] * v[0] + m[5] * v[1] + m[6] * v[2] + m[7] * v[3],
|
|
1856
|
-
m[8] * v[0] + m[9] * v[1] + m[10] * v[2] + m[11] * v[3],
|
|
1857
|
-
m[12] * v[0] + m[13] * v[1] + m[14] * v[2] + m[15] * v[3]
|
|
1858
|
-
];
|
|
1859
|
-
}
|
|
1860
|
-
/**
|
|
1861
|
-
* @internal
|
|
1862
|
-
*/
|
|
1863
|
-
m4Dot(x, y) {
|
|
1864
|
-
return x[0] * y[0] + x[1] * y[1] + x[2] * y[2] + x[3] * y[3];
|
|
1865
|
-
}
|
|
1866
|
-
/**
|
|
1867
|
-
* @internal
|
|
1868
|
-
*/
|
|
1869
|
-
m4Sml(a, y) {
|
|
1870
|
-
return [a * y[0], a * y[1], a * y[2], a * y[3]];
|
|
1871
|
-
}
|
|
1872
|
-
/**
|
|
1873
|
-
* @internal
|
|
1874
|
-
*/
|
|
1875
|
-
alphaMul(img, roundA) {
|
|
1876
|
-
const nImg = new Uint8Array(img.length);
|
|
1877
|
-
const area = img.length >> 2;
|
|
1878
|
-
for (let i = 0; i < area; i++) {
|
|
1879
|
-
const qi = i << 2;
|
|
1880
|
-
let ia = img[qi + 3];
|
|
1881
|
-
if (roundA) {
|
|
1882
|
-
ia = ia < 128 ? 0 : 255;
|
|
1883
|
-
}
|
|
1884
|
-
const a = ia * (1 / 255);
|
|
1885
|
-
nImg[qi + 0] = img[qi + 0] * a;
|
|
1886
|
-
nImg[qi + 1] = img[qi + 1] * a;
|
|
1887
|
-
nImg[qi + 2] = img[qi + 2] * a;
|
|
1888
|
-
nImg[qi + 3] = ia;
|
|
1889
|
-
}
|
|
1890
|
-
return nImg;
|
|
1891
|
-
}
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
|
-
export { Color, JpegEncoder, PngEncoder };
|