rw-parser-ng 2.0.5 → 2.1.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/LICENSE +674 -674
- package/README.md +88 -88
- package/lib/renderware/RwFile.js +3 -0
- package/lib/renderware/dff/DffParser.js +16 -2
- package/lib/utils/ByteStream.d.ts +1 -0
- package/lib/utils/ByteStream.js +4 -0
- package/package.json +10 -9
- package/src/index.ts +12 -12
- package/src/renderware/RwFile.ts +26 -22
- package/src/renderware/RwSections.ts +27 -27
- package/src/renderware/common/types.ts +58 -58
- package/src/renderware/dff/DffModelType.ts +4 -4
- package/src/renderware/dff/DffParser.ts +611 -596
- package/src/renderware/errors/RwParseError.ts +11 -11
- package/src/renderware/ifp/IfpData.ts +32 -32
- package/src/renderware/ifp/IfpParser.ts +202 -202
- package/src/renderware/txd/TxdParser.ts +234 -234
- package/src/renderware/utils/ImageDecoder.ts +570 -570
- package/src/renderware/utils/ImageFormatEnums.ts +27 -27
- package/src/renderware/utils/RwVersion.ts +28 -28
- package/src/utils/ByteStream.ts +80 -75
- package/tsconfig.json +66 -66
|
@@ -1,571 +1,571 @@
|
|
|
1
|
-
// Source: https://github.com/Parik27/DragonFF/blob/master/gtaLib/txd.py
|
|
2
|
-
export class ImageDecoder {
|
|
3
|
-
|
|
4
|
-
static readUInt16LE(buf: Uint8Array, offset: number): number {
|
|
5
|
-
return buf[offset] | (buf[offset + 1] << 8);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
static readUInt32LE(buf: Uint8Array, offset: number): number {
|
|
9
|
-
return (
|
|
10
|
-
buf[offset] |
|
|
11
|
-
(buf[offset + 1] << 8) |
|
|
12
|
-
(buf[offset + 2] << 16) |
|
|
13
|
-
(buf[offset + 3] << 24)
|
|
14
|
-
);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
static decode565(bits: number): [number, number, number] {
|
|
18
|
-
const r = (bits >> 11) & 0b11111;
|
|
19
|
-
const g = (bits >> 5) & 0b111111;
|
|
20
|
-
const b = bits & 0b11111;
|
|
21
|
-
|
|
22
|
-
return [
|
|
23
|
-
(r << 3) | (r >> 2),
|
|
24
|
-
(g << 2) | (g >> 4),
|
|
25
|
-
(b << 3) | (b >> 2)
|
|
26
|
-
];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
static decode555(bits:number): [number, number, number] {
|
|
30
|
-
const r = Math.round(((bits >> 10) & 0b11111) * 255 / 31);
|
|
31
|
-
const g = Math.round(((bits >> 5) & 0b11111) * 255 / 31);
|
|
32
|
-
const b = Math.round((bits & 0b11111) * 255 / 31);
|
|
33
|
-
return [r, g, b];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
static decode1555(bits: number): [number, number, number, number] {
|
|
37
|
-
const a = Math.round(((bits >> 15) & 0b1) * 255);
|
|
38
|
-
const r = Math.round(((bits >> 10) & 0b11111) * 255 / 31);
|
|
39
|
-
const g = Math.round(((bits >> 5) & 0b11111) * 255 / 31);
|
|
40
|
-
const b = Math.round((bits & 0b11111) * 255 / 31);
|
|
41
|
-
return [a, r, g, b];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
static decode4444(bits: number): [number, number, number, number] {
|
|
45
|
-
const a = Math.round(((bits >> 12) & 0b1111) * 255 / 15);
|
|
46
|
-
const r = Math.round(((bits >> 8) & 0b1111) * 255 / 15);
|
|
47
|
-
const g = Math.round(((bits >> 4) & 0b1111) * 255 / 15);
|
|
48
|
-
const b = Math.round((bits & 0b1111) * 255 / 15);
|
|
49
|
-
return [a, r, g, b];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/*
|
|
53
|
-
bc1 - block compression format, using for DXT1
|
|
54
|
-
compress 4x4 block of pixels
|
|
55
|
-
format:
|
|
56
|
-
+---------------+
|
|
57
|
-
| color0 | color0 in palette. 16bit (RGB 565 format)
|
|
58
|
-
+---------------+
|
|
59
|
-
| color1 | color1 in palette. 16bit (RGB 565 format)
|
|
60
|
-
+---+---+---+---+
|
|
61
|
-
| a | b | c | d | a-p color palette index 2bit * 16
|
|
62
|
-
+---+---+---+---+
|
|
63
|
-
| e | f | g | h |
|
|
64
|
-
+---+---+---+---+
|
|
65
|
-
| i | j | k | l |
|
|
66
|
-
+---+---+---+---+
|
|
67
|
-
| m | n | o | p | total: 8byte in 4x4 colors
|
|
68
|
-
+---+---+---+---+
|
|
69
|
-
|
|
70
|
-
color2 and color3 in the palette are calculated by interpolating other colors or choosing the average between them.
|
|
71
|
-
color0 > color1 => interpolation, else => average
|
|
72
|
-
*/
|
|
73
|
-
static bc1(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
74
|
-
const rgba = new Uint8Array(4 * width * height);
|
|
75
|
-
const colorPalette = new Uint8Array(16);
|
|
76
|
-
let offset = 0;
|
|
77
|
-
|
|
78
|
-
for (let y = 0; y < height; y += 4) {
|
|
79
|
-
for (let x = 0; x < width; x += 4) {
|
|
80
|
-
const color0 = ImageDecoder.readUInt16LE(data, offset);
|
|
81
|
-
const color1 = ImageDecoder.readUInt16LE(data, offset + 2);
|
|
82
|
-
let colorBits = ImageDecoder.readUInt32LE(data, offset + 4);
|
|
83
|
-
offset += 8;
|
|
84
|
-
|
|
85
|
-
let [c0r, c0g, c0b] = ImageDecoder.decode565(color0);
|
|
86
|
-
let [c1r, c1g, c1b] = ImageDecoder.decode565(color1);
|
|
87
|
-
|
|
88
|
-
if (color0 > color1) {
|
|
89
|
-
colorPalette[0] = c0r; colorPalette[1] = c0g; colorPalette[2] = c0b;
|
|
90
|
-
colorPalette[4] = c1r; colorPalette[5] = c1g; colorPalette[6] = c1b;
|
|
91
|
-
colorPalette[8] = (2 * c0r + c1r) / 3;
|
|
92
|
-
colorPalette[9] = (2 * c0g + c1g) / 3;
|
|
93
|
-
colorPalette[10] = (2 * c0b + c1b) / 3;
|
|
94
|
-
colorPalette[12] = (c0r + 2 * c1r) / 3;
|
|
95
|
-
colorPalette[13] = (c0g + 2 * c1g) / 3;
|
|
96
|
-
colorPalette[14] = (c0b + 2 * c1b) / 3;
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
colorPalette[0] = c0r; colorPalette[1] = c0g; colorPalette[2] = c0b;
|
|
100
|
-
colorPalette[4] = c1r; colorPalette[5] = c1g; colorPalette[6] = c1b;
|
|
101
|
-
colorPalette[8] = (c0r + c1r) >> 1;
|
|
102
|
-
colorPalette[9] = (c0g + c1g) >> 1;
|
|
103
|
-
colorPalette[10] = (c0b + c1b) >> 1;
|
|
104
|
-
colorPalette[12] = 0; colorPalette[13] = 0; colorPalette[14] = 0;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const baseIndex = (y * width + x) * 4;
|
|
108
|
-
for (let k = 0; k < 16; k++) {
|
|
109
|
-
const colorIdx = colorBits & 0x3;
|
|
110
|
-
colorBits >>>= 2;
|
|
111
|
-
|
|
112
|
-
const j = k >> 2;
|
|
113
|
-
const i = k & 3;
|
|
114
|
-
const idx = baseIndex + ((j * width + i) << 2);
|
|
115
|
-
|
|
116
|
-
rgba[idx + 0] = colorPalette[colorIdx * 4];
|
|
117
|
-
rgba[idx + 1] = colorPalette[colorIdx * 4 + 1];
|
|
118
|
-
rgba[idx + 2] = colorPalette[colorIdx * 4 + 2];
|
|
119
|
-
|
|
120
|
-
if (color0 <= color1 && colorIdx === 3) {
|
|
121
|
-
rgba[idx + 3] = 0;
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
rgba[idx + 3] = 255;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return rgba;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/*
|
|
134
|
-
bc2 - block compression format, using for DXT2 and DXT3
|
|
135
|
-
compress 4x4 block of pixels with 4x4 4bit alpha
|
|
136
|
-
format:
|
|
137
|
-
+---+---+---+---+
|
|
138
|
-
| a | b | c | d | a-p pixel alpha. 4bit * 16
|
|
139
|
-
+---+---+---+---+
|
|
140
|
-
| e | f | g | h |
|
|
141
|
-
+---+---+---+---+
|
|
142
|
-
| i | j | k | l |
|
|
143
|
-
+---+---+---+---+
|
|
144
|
-
| m | n | o | p |
|
|
145
|
-
+---+---+---+---+
|
|
146
|
-
| | bc1 color compression. 8byte
|
|
147
|
-
| bc1 block |
|
|
148
|
-
| | total: 16byte in 4x4 colors
|
|
149
|
-
+---------------+
|
|
150
|
-
|
|
151
|
-
in DXT2, the color data is interpreted as being premultiplied by alpha
|
|
152
|
-
*/
|
|
153
|
-
static bc2(data: Uint8Array, width: number, height: number, premultiplied: boolean): Uint8Array {
|
|
154
|
-
const rgba = new Uint8Array(4 * width * height);
|
|
155
|
-
const colorPalette = new Uint8Array(16);
|
|
156
|
-
|
|
157
|
-
const alphaTable = new Uint8Array(16);
|
|
158
|
-
for (let i = 0; i < 16; i++) {
|
|
159
|
-
alphaTable[i] = (i * 255 + 7.5) / 15 | 0;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
let offset = 0;
|
|
163
|
-
|
|
164
|
-
for (let y = 0; y < height; y += 4) {
|
|
165
|
-
for (let x = 0; x < width; x += 4) {
|
|
166
|
-
const alpha0 = ImageDecoder.readUInt32LE(data, offset);
|
|
167
|
-
const alpha1 = ImageDecoder.readUInt32LE(data, offset + 4);
|
|
168
|
-
offset += 8;
|
|
169
|
-
|
|
170
|
-
const color0 = ImageDecoder.readUInt16LE(data, offset);
|
|
171
|
-
const color1 = ImageDecoder.readUInt16LE(data, offset + 2);
|
|
172
|
-
let colorBits = ImageDecoder.readUInt32LE(data, offset + 4);
|
|
173
|
-
offset += 8;
|
|
174
|
-
|
|
175
|
-
let [c0r, c0g, c0b] = ImageDecoder.decode565(color0);
|
|
176
|
-
let [c1r, c1g, c1b] = ImageDecoder.decode565(color1);
|
|
177
|
-
|
|
178
|
-
if (color0 > color1) {
|
|
179
|
-
colorPalette[0] = c0r; colorPalette[1] = c0g; colorPalette[2] = c0b;
|
|
180
|
-
colorPalette[4] = c1r; colorPalette[5] = c1g; colorPalette[6] = c1b;
|
|
181
|
-
colorPalette[8] = (2 * c0r + c1r) / 3;
|
|
182
|
-
colorPalette[9] = (2 * c0g + c1g) / 3;
|
|
183
|
-
colorPalette[10] = (2 * c0b + c1b) / 3;
|
|
184
|
-
colorPalette[12] = (c0r + 2 * c1r) / 3;
|
|
185
|
-
colorPalette[13] = (c0g + 2 * c1g) / 3;
|
|
186
|
-
colorPalette[14] = (c0b + 2 * c1b) / 3;
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
colorPalette[0] = c0r; colorPalette[1] = c0g; colorPalette[2] = c0b;
|
|
190
|
-
colorPalette[4] = c1r; colorPalette[5] = c1g; colorPalette[6] = c1b;
|
|
191
|
-
colorPalette[8] = (c0r + c1r) >> 1;
|
|
192
|
-
colorPalette[9] = (c0g + c1g) >> 1;
|
|
193
|
-
colorPalette[10] = (c0b + c1b) >> 1;
|
|
194
|
-
colorPalette[12] = 0; colorPalette[13] = 0; colorPalette[14] = 0;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const baseIndex = ((y * width + x) << 2);
|
|
198
|
-
|
|
199
|
-
for (let k = 0; k < 16; k++) {
|
|
200
|
-
const j = k >> 2;
|
|
201
|
-
const i = k & 3;
|
|
202
|
-
|
|
203
|
-
const idx = baseIndex + ((((j * width + i) << 2)));
|
|
204
|
-
|
|
205
|
-
const colorIdx = colorBits & 0x3;
|
|
206
|
-
colorBits >>>= 2;
|
|
207
|
-
|
|
208
|
-
const bitPos = (j << 2) + i;
|
|
209
|
-
const byteIndex = bitPos >> 3;
|
|
210
|
-
const shift = (bitPos & 7) << 2;
|
|
211
|
-
|
|
212
|
-
const alpha4 = ((byteIndex === 0 ? alpha0 : alpha1) >>> shift) & 0xF;
|
|
213
|
-
const alpha = alphaTable[alpha4];
|
|
214
|
-
|
|
215
|
-
rgba[idx + 0] = colorPalette[colorIdx * 4];
|
|
216
|
-
rgba[idx + 1] = colorPalette[colorIdx * 4 + 1];
|
|
217
|
-
rgba[idx + 2] = colorPalette[colorIdx * 4 + 2];
|
|
218
|
-
rgba[idx + 3] = alpha;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (premultiplied && alpha > 0 && alpha < 255) {
|
|
222
|
-
const factor = 255 / alpha;
|
|
223
|
-
rgba[idx + 0] = Math.min(255, Math.round(rgba[idx + 0] * factor));
|
|
224
|
-
rgba[idx + 1] = Math.min(255, Math.round(rgba[idx + 1] * factor));
|
|
225
|
-
rgba[idx + 2] = Math.min(255, Math.round(rgba[idx + 2] * factor));
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return rgba;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/*
|
|
235
|
-
bc3 - block compression format, using for DXT4 and DXT5
|
|
236
|
-
compress 4x4 block of pixels with alpha
|
|
237
|
-
format:
|
|
238
|
-
+---------------+
|
|
239
|
-
| alpha0 | min alpha value. 8bit
|
|
240
|
-
+---------------+
|
|
241
|
-
| alpha1 | max alpha value. 8bit
|
|
242
|
-
+---+---+---+---+
|
|
243
|
-
| a | b | c | d | bc1-like alpha block but 3bit * 16 (index in alpha palette)
|
|
244
|
-
+---+---+---+---+
|
|
245
|
-
| e | f | g | h |
|
|
246
|
-
+---+---+---+---+
|
|
247
|
-
| i | j | k | l |
|
|
248
|
-
+---+---+---+---+
|
|
249
|
-
| m | n | o | p |
|
|
250
|
-
+---+---+---+---+
|
|
251
|
-
| | bc1 color compression. 8byte
|
|
252
|
-
| bc1 block |
|
|
253
|
-
| | total: 16byte in 4x4 colors
|
|
254
|
-
+---------------+
|
|
255
|
-
|
|
256
|
-
in DXT4, the color data is interpreted as being premultiplied by alpha
|
|
257
|
-
*/
|
|
258
|
-
static bc3(data: Uint8Array, width: number, height: number, premultiplied: boolean): Uint8Array {
|
|
259
|
-
const rgba = new Uint8Array(4 * width * height);
|
|
260
|
-
const alphaPalette = new Uint8Array(8);
|
|
261
|
-
const colorPalette = new Uint8Array(16);
|
|
262
|
-
const alphaIndices = new Uint8Array(16);
|
|
263
|
-
let offset = 0;
|
|
264
|
-
|
|
265
|
-
for (let y = 0; y < height; y += 4) {
|
|
266
|
-
for (let x = 0; x < width; x += 4) {
|
|
267
|
-
const alpha0 = data[offset++];
|
|
268
|
-
const alpha1 = data[offset++];
|
|
269
|
-
|
|
270
|
-
const alphaBits = data.subarray(offset, offset + 6);
|
|
271
|
-
offset += 6;
|
|
272
|
-
|
|
273
|
-
const color0 = ImageDecoder.readUInt16LE(data, offset);
|
|
274
|
-
const color1 = ImageDecoder.readUInt16LE(data, offset + 2);
|
|
275
|
-
let colorBits = ImageDecoder.readUInt32LE(data, offset + 4);
|
|
276
|
-
offset += 8;
|
|
277
|
-
|
|
278
|
-
let [c0r, c0g, c0b] = ImageDecoder.decode565(color0);
|
|
279
|
-
let [c1r, c1g, c1b] = ImageDecoder.decode565(color1);
|
|
280
|
-
|
|
281
|
-
if (color0 > color1) {
|
|
282
|
-
colorPalette[0] = c0r; colorPalette[1] = c0g; colorPalette[2] = c0b;
|
|
283
|
-
colorPalette[4] = c1r; colorPalette[5] = c1g; colorPalette[6] = c1b;
|
|
284
|
-
colorPalette[8] = (2 * c0r + c1r) / 3;
|
|
285
|
-
colorPalette[9] = (2 * c0g + c1g) / 3;
|
|
286
|
-
colorPalette[10] = (2 * c0b + c1b) / 3;
|
|
287
|
-
colorPalette[12] = (c0r + 2 * c1r) / 3;
|
|
288
|
-
colorPalette[13] = (c0g + 2 * c1g) / 3;
|
|
289
|
-
colorPalette[14] = (c0b + 2 * c1b) / 3;
|
|
290
|
-
}
|
|
291
|
-
else {
|
|
292
|
-
colorPalette[0] = c0r; colorPalette[1] = c0g; colorPalette[2] = c0b;
|
|
293
|
-
colorPalette[4] = c1r; colorPalette[5] = c1g; colorPalette[6] = c1b;
|
|
294
|
-
colorPalette[8] = (c0r + c1r) >> 1;
|
|
295
|
-
colorPalette[9] = (c0g + c1g) >> 1;
|
|
296
|
-
colorPalette[10] = (c0b + c1b) >> 1;
|
|
297
|
-
colorPalette[12] = 0; colorPalette[13] = 0; colorPalette[14] = 0;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
if (alpha0 > alpha1) {
|
|
301
|
-
alphaPalette[0] = alpha0;
|
|
302
|
-
alphaPalette[1] = alpha1;
|
|
303
|
-
alphaPalette[2] = (alpha0 * 6 + alpha1 * 1 + 3) / 7;
|
|
304
|
-
alphaPalette[3] = (alpha0 * 5 + alpha1 * 2 + 3) / 7;
|
|
305
|
-
alphaPalette[4] = (alpha0 * 4 + alpha1 * 3 + 3) / 7;
|
|
306
|
-
alphaPalette[5] = (alpha0 * 3 + alpha1 * 4 + 3) / 7;
|
|
307
|
-
alphaPalette[6] = (alpha0 * 2 + alpha1 * 5 + 3) / 7;
|
|
308
|
-
alphaPalette[7] = (alpha0 * 1 + alpha1 * 6 + 3) / 7;
|
|
309
|
-
}
|
|
310
|
-
else {
|
|
311
|
-
alphaPalette[0] = alpha0;
|
|
312
|
-
alphaPalette[1] = alpha1;
|
|
313
|
-
alphaPalette[2] = (alpha0 * 4 + alpha1 * 1 + 2) / 5;
|
|
314
|
-
alphaPalette[3] = (alpha0 * 3 + alpha1 * 2 + 2) / 5;
|
|
315
|
-
alphaPalette[4] = (alpha0 * 2 + alpha1 * 3 + 2) / 5;
|
|
316
|
-
alphaPalette[5] = (alpha0 * 1 + alpha1 * 4 + 2) / 5;
|
|
317
|
-
alphaPalette[6] = 0;
|
|
318
|
-
alphaPalette[7] = 255;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
for (let k = 0; k < 16; k++) {
|
|
322
|
-
const bitOffset = k * 3;
|
|
323
|
-
const byteOffset = bitOffset >> 3;
|
|
324
|
-
const shift = bitOffset & 7;
|
|
325
|
-
|
|
326
|
-
if (shift <= 5) {
|
|
327
|
-
alphaIndices[k] = (alphaBits[byteOffset] >> shift) & 0x7;
|
|
328
|
-
}
|
|
329
|
-
else {
|
|
330
|
-
const part1 = (alphaBits[byteOffset] >> shift) & 0x7;
|
|
331
|
-
const part2 = (alphaBits[byteOffset + 1] << (8 - shift)) & 0x7;
|
|
332
|
-
alphaIndices[k] = part1 | part2;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
const baseIndex = (y * width + x) << 2;
|
|
337
|
-
let bits = colorBits;
|
|
338
|
-
|
|
339
|
-
for (let k = 0; k < 16; k++) {
|
|
340
|
-
const j = k >> 2;
|
|
341
|
-
const i = k & 3;
|
|
342
|
-
|
|
343
|
-
const idx = baseIndex + ((((j * width + i) << 2)));
|
|
344
|
-
const colorIdx = bits & 0x3;
|
|
345
|
-
bits >>>= 2;
|
|
346
|
-
|
|
347
|
-
const alpha = alphaPalette[alphaIndices[k] & 0x7];
|
|
348
|
-
|
|
349
|
-
rgba[idx + 0] = colorPalette[colorIdx * 4];
|
|
350
|
-
rgba[idx + 1] = colorPalette[colorIdx * 4 + 1];
|
|
351
|
-
rgba[idx + 2] = colorPalette[colorIdx * 4 + 2];
|
|
352
|
-
rgba[idx + 3] = alpha;
|
|
353
|
-
|
|
354
|
-
if (premultiplied && alpha > 0 && alpha < 255) {
|
|
355
|
-
const factor = 255 / alpha;
|
|
356
|
-
rgba[idx] = Math.min(255, Math.round(rgba[idx] * factor));
|
|
357
|
-
rgba[idx + 1] = Math.min(255, Math.round(rgba[idx + 1] * factor));
|
|
358
|
-
rgba[idx + 2] = Math.min(255, Math.round(rgba[idx + 2] * factor));
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
return rgba;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
static bgra1555(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
368
|
-
const rbga = new Uint8Array(4 * width * height);
|
|
369
|
-
let offset = 0;
|
|
370
|
-
|
|
371
|
-
for (let i = 0; i < data.length; i += 2) {
|
|
372
|
-
const color = ImageDecoder.readUInt16LE(data, i);
|
|
373
|
-
const [a, r, g, b] = ImageDecoder.decode1555(color);
|
|
374
|
-
|
|
375
|
-
rbga[offset++] = r;
|
|
376
|
-
rbga[offset++] = g;
|
|
377
|
-
rbga[offset++] = b;
|
|
378
|
-
rbga[offset++] = a;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
return rbga;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
static bgra4444(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
385
|
-
const rgba = new Uint8Array(4 * width * height);
|
|
386
|
-
let offset = 0;
|
|
387
|
-
|
|
388
|
-
for (let i = 0; i < data.length; i += 2) {
|
|
389
|
-
const color = ImageDecoder.readUInt16LE(data, i);
|
|
390
|
-
const [a, r, g, b] = ImageDecoder.decode4444(color);
|
|
391
|
-
|
|
392
|
-
rgba[offset++] = r;
|
|
393
|
-
rgba[offset++] = g;
|
|
394
|
-
rgba[offset++] = b;
|
|
395
|
-
rgba[offset++] = a;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
return rgba;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
static bgra555(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
402
|
-
const rgba = new Uint8Array(4 * width * height);
|
|
403
|
-
let offset = 0;
|
|
404
|
-
|
|
405
|
-
for (let i = 0; i < data.length; i += 2) {
|
|
406
|
-
const color = ImageDecoder.readUInt16LE(data, i);
|
|
407
|
-
const [r, g, b] = ImageDecoder.decode555(color);
|
|
408
|
-
|
|
409
|
-
rgba[offset++] = r;
|
|
410
|
-
rgba[offset++] = g;
|
|
411
|
-
rgba[offset++] = b;
|
|
412
|
-
rgba[offset++] = 0xff;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
return rgba;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
static bgra565(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
419
|
-
const rgba = new Uint8Array(4 * width * height);
|
|
420
|
-
let offset = 0;
|
|
421
|
-
|
|
422
|
-
for (let i = 0; i < data.length; i += 2) {
|
|
423
|
-
const color = ImageDecoder.readUInt16LE(data, i);
|
|
424
|
-
const [r, g, b] = ImageDecoder.decode565(color);
|
|
425
|
-
|
|
426
|
-
rgba[offset++] = r;
|
|
427
|
-
rgba[offset++] = g;
|
|
428
|
-
rgba[offset++] = b;
|
|
429
|
-
rgba[offset++] = 0xff;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
return rgba;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
static bgra888(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
436
|
-
const rgba = new Uint8Array(4 * width * height);
|
|
437
|
-
for (let i = 0; i < data.length; i += 4) {
|
|
438
|
-
rgba[i + 0] = data[i + 2];
|
|
439
|
-
rgba[i + 1] = data[i + 1];
|
|
440
|
-
rgba[i + 2] = data[i + 0];
|
|
441
|
-
rgba[i + 3] = 0xff;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
return rgba;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
static bgra8888(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
448
|
-
const rgba = new Uint8Array(4 * width * height);
|
|
449
|
-
for (let i = 0; i < data.length; i += 4) {
|
|
450
|
-
rgba[i + 0] = data[i + 2];
|
|
451
|
-
rgba[i + 1] = data[i + 1];
|
|
452
|
-
rgba[i + 2] = data[i + 0];
|
|
453
|
-
rgba[i + 3] = data[i + 3];
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
return rgba;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
static lum8(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
460
|
-
const rgba = new Uint8Array(4 * width * height);
|
|
461
|
-
|
|
462
|
-
for (let i = 0; i < data.length; i++) {
|
|
463
|
-
const offset = i * 4;
|
|
464
|
-
const luminance = data[i];
|
|
465
|
-
rgba[offset + 0] = luminance; // R
|
|
466
|
-
rgba[offset + 1] = luminance; // G
|
|
467
|
-
rgba[offset + 2] = luminance; // B
|
|
468
|
-
rgba[offset + 3] = 0xff;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
return rgba;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
static lum8a8(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
475
|
-
const rgba = new Uint8Array(4 * width * height);
|
|
476
|
-
let offset = 0;
|
|
477
|
-
|
|
478
|
-
for (let i = 0; i < data.length; i += 2) {
|
|
479
|
-
const luminance = data[i];
|
|
480
|
-
const alpha = data[i + 1];
|
|
481
|
-
|
|
482
|
-
rgba[offset++] = luminance;
|
|
483
|
-
rgba[offset++] = luminance;
|
|
484
|
-
rgba[offset++] = luminance;
|
|
485
|
-
rgba[offset++] = alpha;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
return rgba;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
static pal4(data: Uint8Array, palette: Uint8Array, width: number, height: number): Uint8Array {
|
|
492
|
-
const rgba = new Uint8Array(4 * width * height);
|
|
493
|
-
let offset = 0;
|
|
494
|
-
|
|
495
|
-
for (let i = 0; i < data.length; i++) {
|
|
496
|
-
const b = data[i];
|
|
497
|
-
const idx1 = (b >> 4) & 0xf;
|
|
498
|
-
const idx2 = b & 0xf;
|
|
499
|
-
|
|
500
|
-
// Copying RGBA from the palette for two pixels
|
|
501
|
-
rgba[offset++] = palette[idx1 * 4 + 0]; // R
|
|
502
|
-
rgba[offset++] = palette[idx1 * 4 + 1]; // G
|
|
503
|
-
rgba[offset++] = palette[idx1 * 4 + 2]; // B
|
|
504
|
-
rgba[offset++] = palette[idx1 * 4 + 3]; // A
|
|
505
|
-
|
|
506
|
-
rgba[offset++] = palette[idx2 * 4 + 0]; // R
|
|
507
|
-
rgba[offset++] = palette[idx2 * 4 + 1]; // G
|
|
508
|
-
rgba[offset++] = palette[idx2 * 4 + 2]; // B
|
|
509
|
-
rgba[offset++] = palette[idx2 * 4 + 3]; // A
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
return rgba;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
static pal4NoAlpha(data: Uint8Array, palette: Uint8Array, width: number, height: number): Uint8Array {
|
|
516
|
-
const rgba = new Uint8Array(4 * width * height);
|
|
517
|
-
let offset = 0;
|
|
518
|
-
|
|
519
|
-
for (let i = 0; i < data.length; i++) {
|
|
520
|
-
const b = data[i];
|
|
521
|
-
const colorIndex1 = (b >> 4) & 0xf;
|
|
522
|
-
const colorIndex2 = b & 0xf;
|
|
523
|
-
|
|
524
|
-
// First pixel
|
|
525
|
-
rgba[offset++] = palette[colorIndex1 * 4 + 0]; // R
|
|
526
|
-
rgba[offset++] = palette[colorIndex1 * 4 + 1]; // G
|
|
527
|
-
rgba[offset++] = palette[colorIndex1 * 4 + 2]; // B
|
|
528
|
-
rgba[offset++] = 0xff;
|
|
529
|
-
|
|
530
|
-
// Second pixel
|
|
531
|
-
rgba[offset++] = palette[colorIndex2 * 4 + 0]; // R
|
|
532
|
-
rgba[offset++] = palette[colorIndex2 * 4 + 1]; // G
|
|
533
|
-
rgba[offset++] = palette[colorIndex2 * 4 + 2]; // B
|
|
534
|
-
rgba[offset++] = 0xff;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
return rgba;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
static pal8(data: Uint8Array, palette: Uint8Array, width: number, height: number): Uint8Array {
|
|
541
|
-
const rgba = new Uint8Array(4 * width * height);
|
|
542
|
-
|
|
543
|
-
for (let i = 0; i < data.length; i++) {
|
|
544
|
-
const colorIndex = data[i];
|
|
545
|
-
|
|
546
|
-
// Copy RGBA from palette
|
|
547
|
-
rgba[i * 4 + 0] = palette[colorIndex * 4 + 0]; // R
|
|
548
|
-
rgba[i * 4 + 1] = palette[colorIndex * 4 + 1]; // G
|
|
549
|
-
rgba[i * 4 + 2] = palette[colorIndex * 4 + 2]; // B
|
|
550
|
-
rgba[i * 4 + 3] = palette[colorIndex * 4 + 3]; // A
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
return rgba;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
static pal8NoAlpha(data: Uint8Array, palette: Uint8Array, width: number, height: number): Uint8Array {
|
|
557
|
-
const rgba = new Uint8Array(4 * width * height);
|
|
558
|
-
|
|
559
|
-
for (let i = 0; i < data.length; i++) {
|
|
560
|
-
const colorIndex = data[i];
|
|
561
|
-
|
|
562
|
-
// Copy RGB from palette
|
|
563
|
-
rgba[i * 4 + 0] = palette[colorIndex * 4 + 0]; // R
|
|
564
|
-
rgba[i * 4 + 1] = palette[colorIndex * 4 + 1]; // G
|
|
565
|
-
rgba[i * 4 + 2] = palette[colorIndex * 4 + 2]; // B
|
|
566
|
-
rgba[i * 4 + 3] = 0xff;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
return rgba;
|
|
570
|
-
}
|
|
1
|
+
// Source: https://github.com/Parik27/DragonFF/blob/master/gtaLib/txd.py
|
|
2
|
+
export class ImageDecoder {
|
|
3
|
+
|
|
4
|
+
static readUInt16LE(buf: Uint8Array, offset: number): number {
|
|
5
|
+
return buf[offset] | (buf[offset + 1] << 8);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
static readUInt32LE(buf: Uint8Array, offset: number): number {
|
|
9
|
+
return (
|
|
10
|
+
buf[offset] |
|
|
11
|
+
(buf[offset + 1] << 8) |
|
|
12
|
+
(buf[offset + 2] << 16) |
|
|
13
|
+
(buf[offset + 3] << 24)
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static decode565(bits: number): [number, number, number] {
|
|
18
|
+
const r = (bits >> 11) & 0b11111;
|
|
19
|
+
const g = (bits >> 5) & 0b111111;
|
|
20
|
+
const b = bits & 0b11111;
|
|
21
|
+
|
|
22
|
+
return [
|
|
23
|
+
(r << 3) | (r >> 2),
|
|
24
|
+
(g << 2) | (g >> 4),
|
|
25
|
+
(b << 3) | (b >> 2)
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static decode555(bits:number): [number, number, number] {
|
|
30
|
+
const r = Math.round(((bits >> 10) & 0b11111) * 255 / 31);
|
|
31
|
+
const g = Math.round(((bits >> 5) & 0b11111) * 255 / 31);
|
|
32
|
+
const b = Math.round((bits & 0b11111) * 255 / 31);
|
|
33
|
+
return [r, g, b];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static decode1555(bits: number): [number, number, number, number] {
|
|
37
|
+
const a = Math.round(((bits >> 15) & 0b1) * 255);
|
|
38
|
+
const r = Math.round(((bits >> 10) & 0b11111) * 255 / 31);
|
|
39
|
+
const g = Math.round(((bits >> 5) & 0b11111) * 255 / 31);
|
|
40
|
+
const b = Math.round((bits & 0b11111) * 255 / 31);
|
|
41
|
+
return [a, r, g, b];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static decode4444(bits: number): [number, number, number, number] {
|
|
45
|
+
const a = Math.round(((bits >> 12) & 0b1111) * 255 / 15);
|
|
46
|
+
const r = Math.round(((bits >> 8) & 0b1111) * 255 / 15);
|
|
47
|
+
const g = Math.round(((bits >> 4) & 0b1111) * 255 / 15);
|
|
48
|
+
const b = Math.round((bits & 0b1111) * 255 / 15);
|
|
49
|
+
return [a, r, g, b];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/*
|
|
53
|
+
bc1 - block compression format, using for DXT1
|
|
54
|
+
compress 4x4 block of pixels
|
|
55
|
+
format:
|
|
56
|
+
+---------------+
|
|
57
|
+
| color0 | color0 in palette. 16bit (RGB 565 format)
|
|
58
|
+
+---------------+
|
|
59
|
+
| color1 | color1 in palette. 16bit (RGB 565 format)
|
|
60
|
+
+---+---+---+---+
|
|
61
|
+
| a | b | c | d | a-p color palette index 2bit * 16
|
|
62
|
+
+---+---+---+---+
|
|
63
|
+
| e | f | g | h |
|
|
64
|
+
+---+---+---+---+
|
|
65
|
+
| i | j | k | l |
|
|
66
|
+
+---+---+---+---+
|
|
67
|
+
| m | n | o | p | total: 8byte in 4x4 colors
|
|
68
|
+
+---+---+---+---+
|
|
69
|
+
|
|
70
|
+
color2 and color3 in the palette are calculated by interpolating other colors or choosing the average between them.
|
|
71
|
+
color0 > color1 => interpolation, else => average
|
|
72
|
+
*/
|
|
73
|
+
static bc1(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
74
|
+
const rgba = new Uint8Array(4 * width * height);
|
|
75
|
+
const colorPalette = new Uint8Array(16);
|
|
76
|
+
let offset = 0;
|
|
77
|
+
|
|
78
|
+
for (let y = 0; y < height; y += 4) {
|
|
79
|
+
for (let x = 0; x < width; x += 4) {
|
|
80
|
+
const color0 = ImageDecoder.readUInt16LE(data, offset);
|
|
81
|
+
const color1 = ImageDecoder.readUInt16LE(data, offset + 2);
|
|
82
|
+
let colorBits = ImageDecoder.readUInt32LE(data, offset + 4);
|
|
83
|
+
offset += 8;
|
|
84
|
+
|
|
85
|
+
let [c0r, c0g, c0b] = ImageDecoder.decode565(color0);
|
|
86
|
+
let [c1r, c1g, c1b] = ImageDecoder.decode565(color1);
|
|
87
|
+
|
|
88
|
+
if (color0 > color1) {
|
|
89
|
+
colorPalette[0] = c0r; colorPalette[1] = c0g; colorPalette[2] = c0b;
|
|
90
|
+
colorPalette[4] = c1r; colorPalette[5] = c1g; colorPalette[6] = c1b;
|
|
91
|
+
colorPalette[8] = (2 * c0r + c1r) / 3;
|
|
92
|
+
colorPalette[9] = (2 * c0g + c1g) / 3;
|
|
93
|
+
colorPalette[10] = (2 * c0b + c1b) / 3;
|
|
94
|
+
colorPalette[12] = (c0r + 2 * c1r) / 3;
|
|
95
|
+
colorPalette[13] = (c0g + 2 * c1g) / 3;
|
|
96
|
+
colorPalette[14] = (c0b + 2 * c1b) / 3;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
colorPalette[0] = c0r; colorPalette[1] = c0g; colorPalette[2] = c0b;
|
|
100
|
+
colorPalette[4] = c1r; colorPalette[5] = c1g; colorPalette[6] = c1b;
|
|
101
|
+
colorPalette[8] = (c0r + c1r) >> 1;
|
|
102
|
+
colorPalette[9] = (c0g + c1g) >> 1;
|
|
103
|
+
colorPalette[10] = (c0b + c1b) >> 1;
|
|
104
|
+
colorPalette[12] = 0; colorPalette[13] = 0; colorPalette[14] = 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const baseIndex = (y * width + x) * 4;
|
|
108
|
+
for (let k = 0; k < 16; k++) {
|
|
109
|
+
const colorIdx = colorBits & 0x3;
|
|
110
|
+
colorBits >>>= 2;
|
|
111
|
+
|
|
112
|
+
const j = k >> 2;
|
|
113
|
+
const i = k & 3;
|
|
114
|
+
const idx = baseIndex + ((j * width + i) << 2);
|
|
115
|
+
|
|
116
|
+
rgba[idx + 0] = colorPalette[colorIdx * 4];
|
|
117
|
+
rgba[idx + 1] = colorPalette[colorIdx * 4 + 1];
|
|
118
|
+
rgba[idx + 2] = colorPalette[colorIdx * 4 + 2];
|
|
119
|
+
|
|
120
|
+
if (color0 <= color1 && colorIdx === 3) {
|
|
121
|
+
rgba[idx + 3] = 0;
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
rgba[idx + 3] = 255;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return rgba;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/*
|
|
134
|
+
bc2 - block compression format, using for DXT2 and DXT3
|
|
135
|
+
compress 4x4 block of pixels with 4x4 4bit alpha
|
|
136
|
+
format:
|
|
137
|
+
+---+---+---+---+
|
|
138
|
+
| a | b | c | d | a-p pixel alpha. 4bit * 16
|
|
139
|
+
+---+---+---+---+
|
|
140
|
+
| e | f | g | h |
|
|
141
|
+
+---+---+---+---+
|
|
142
|
+
| i | j | k | l |
|
|
143
|
+
+---+---+---+---+
|
|
144
|
+
| m | n | o | p |
|
|
145
|
+
+---+---+---+---+
|
|
146
|
+
| | bc1 color compression. 8byte
|
|
147
|
+
| bc1 block |
|
|
148
|
+
| | total: 16byte in 4x4 colors
|
|
149
|
+
+---------------+
|
|
150
|
+
|
|
151
|
+
in DXT2, the color data is interpreted as being premultiplied by alpha
|
|
152
|
+
*/
|
|
153
|
+
static bc2(data: Uint8Array, width: number, height: number, premultiplied: boolean): Uint8Array {
|
|
154
|
+
const rgba = new Uint8Array(4 * width * height);
|
|
155
|
+
const colorPalette = new Uint8Array(16);
|
|
156
|
+
|
|
157
|
+
const alphaTable = new Uint8Array(16);
|
|
158
|
+
for (let i = 0; i < 16; i++) {
|
|
159
|
+
alphaTable[i] = (i * 255 + 7.5) / 15 | 0;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let offset = 0;
|
|
163
|
+
|
|
164
|
+
for (let y = 0; y < height; y += 4) {
|
|
165
|
+
for (let x = 0; x < width; x += 4) {
|
|
166
|
+
const alpha0 = ImageDecoder.readUInt32LE(data, offset);
|
|
167
|
+
const alpha1 = ImageDecoder.readUInt32LE(data, offset + 4);
|
|
168
|
+
offset += 8;
|
|
169
|
+
|
|
170
|
+
const color0 = ImageDecoder.readUInt16LE(data, offset);
|
|
171
|
+
const color1 = ImageDecoder.readUInt16LE(data, offset + 2);
|
|
172
|
+
let colorBits = ImageDecoder.readUInt32LE(data, offset + 4);
|
|
173
|
+
offset += 8;
|
|
174
|
+
|
|
175
|
+
let [c0r, c0g, c0b] = ImageDecoder.decode565(color0);
|
|
176
|
+
let [c1r, c1g, c1b] = ImageDecoder.decode565(color1);
|
|
177
|
+
|
|
178
|
+
if (color0 > color1) {
|
|
179
|
+
colorPalette[0] = c0r; colorPalette[1] = c0g; colorPalette[2] = c0b;
|
|
180
|
+
colorPalette[4] = c1r; colorPalette[5] = c1g; colorPalette[6] = c1b;
|
|
181
|
+
colorPalette[8] = (2 * c0r + c1r) / 3;
|
|
182
|
+
colorPalette[9] = (2 * c0g + c1g) / 3;
|
|
183
|
+
colorPalette[10] = (2 * c0b + c1b) / 3;
|
|
184
|
+
colorPalette[12] = (c0r + 2 * c1r) / 3;
|
|
185
|
+
colorPalette[13] = (c0g + 2 * c1g) / 3;
|
|
186
|
+
colorPalette[14] = (c0b + 2 * c1b) / 3;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
colorPalette[0] = c0r; colorPalette[1] = c0g; colorPalette[2] = c0b;
|
|
190
|
+
colorPalette[4] = c1r; colorPalette[5] = c1g; colorPalette[6] = c1b;
|
|
191
|
+
colorPalette[8] = (c0r + c1r) >> 1;
|
|
192
|
+
colorPalette[9] = (c0g + c1g) >> 1;
|
|
193
|
+
colorPalette[10] = (c0b + c1b) >> 1;
|
|
194
|
+
colorPalette[12] = 0; colorPalette[13] = 0; colorPalette[14] = 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const baseIndex = ((y * width + x) << 2);
|
|
198
|
+
|
|
199
|
+
for (let k = 0; k < 16; k++) {
|
|
200
|
+
const j = k >> 2;
|
|
201
|
+
const i = k & 3;
|
|
202
|
+
|
|
203
|
+
const idx = baseIndex + ((((j * width + i) << 2)));
|
|
204
|
+
|
|
205
|
+
const colorIdx = colorBits & 0x3;
|
|
206
|
+
colorBits >>>= 2;
|
|
207
|
+
|
|
208
|
+
const bitPos = (j << 2) + i;
|
|
209
|
+
const byteIndex = bitPos >> 3;
|
|
210
|
+
const shift = (bitPos & 7) << 2;
|
|
211
|
+
|
|
212
|
+
const alpha4 = ((byteIndex === 0 ? alpha0 : alpha1) >>> shift) & 0xF;
|
|
213
|
+
const alpha = alphaTable[alpha4];
|
|
214
|
+
|
|
215
|
+
rgba[idx + 0] = colorPalette[colorIdx * 4];
|
|
216
|
+
rgba[idx + 1] = colorPalette[colorIdx * 4 + 1];
|
|
217
|
+
rgba[idx + 2] = colorPalette[colorIdx * 4 + 2];
|
|
218
|
+
rgba[idx + 3] = alpha;
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
if (premultiplied && alpha > 0 && alpha < 255) {
|
|
222
|
+
const factor = 255 / alpha;
|
|
223
|
+
rgba[idx + 0] = Math.min(255, Math.round(rgba[idx + 0] * factor));
|
|
224
|
+
rgba[idx + 1] = Math.min(255, Math.round(rgba[idx + 1] * factor));
|
|
225
|
+
rgba[idx + 2] = Math.min(255, Math.round(rgba[idx + 2] * factor));
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return rgba;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/*
|
|
235
|
+
bc3 - block compression format, using for DXT4 and DXT5
|
|
236
|
+
compress 4x4 block of pixels with alpha
|
|
237
|
+
format:
|
|
238
|
+
+---------------+
|
|
239
|
+
| alpha0 | min alpha value. 8bit
|
|
240
|
+
+---------------+
|
|
241
|
+
| alpha1 | max alpha value. 8bit
|
|
242
|
+
+---+---+---+---+
|
|
243
|
+
| a | b | c | d | bc1-like alpha block but 3bit * 16 (index in alpha palette)
|
|
244
|
+
+---+---+---+---+
|
|
245
|
+
| e | f | g | h |
|
|
246
|
+
+---+---+---+---+
|
|
247
|
+
| i | j | k | l |
|
|
248
|
+
+---+---+---+---+
|
|
249
|
+
| m | n | o | p |
|
|
250
|
+
+---+---+---+---+
|
|
251
|
+
| | bc1 color compression. 8byte
|
|
252
|
+
| bc1 block |
|
|
253
|
+
| | total: 16byte in 4x4 colors
|
|
254
|
+
+---------------+
|
|
255
|
+
|
|
256
|
+
in DXT4, the color data is interpreted as being premultiplied by alpha
|
|
257
|
+
*/
|
|
258
|
+
static bc3(data: Uint8Array, width: number, height: number, premultiplied: boolean): Uint8Array {
|
|
259
|
+
const rgba = new Uint8Array(4 * width * height);
|
|
260
|
+
const alphaPalette = new Uint8Array(8);
|
|
261
|
+
const colorPalette = new Uint8Array(16);
|
|
262
|
+
const alphaIndices = new Uint8Array(16);
|
|
263
|
+
let offset = 0;
|
|
264
|
+
|
|
265
|
+
for (let y = 0; y < height; y += 4) {
|
|
266
|
+
for (let x = 0; x < width; x += 4) {
|
|
267
|
+
const alpha0 = data[offset++];
|
|
268
|
+
const alpha1 = data[offset++];
|
|
269
|
+
|
|
270
|
+
const alphaBits = data.subarray(offset, offset + 6);
|
|
271
|
+
offset += 6;
|
|
272
|
+
|
|
273
|
+
const color0 = ImageDecoder.readUInt16LE(data, offset);
|
|
274
|
+
const color1 = ImageDecoder.readUInt16LE(data, offset + 2);
|
|
275
|
+
let colorBits = ImageDecoder.readUInt32LE(data, offset + 4);
|
|
276
|
+
offset += 8;
|
|
277
|
+
|
|
278
|
+
let [c0r, c0g, c0b] = ImageDecoder.decode565(color0);
|
|
279
|
+
let [c1r, c1g, c1b] = ImageDecoder.decode565(color1);
|
|
280
|
+
|
|
281
|
+
if (color0 > color1) {
|
|
282
|
+
colorPalette[0] = c0r; colorPalette[1] = c0g; colorPalette[2] = c0b;
|
|
283
|
+
colorPalette[4] = c1r; colorPalette[5] = c1g; colorPalette[6] = c1b;
|
|
284
|
+
colorPalette[8] = (2 * c0r + c1r) / 3;
|
|
285
|
+
colorPalette[9] = (2 * c0g + c1g) / 3;
|
|
286
|
+
colorPalette[10] = (2 * c0b + c1b) / 3;
|
|
287
|
+
colorPalette[12] = (c0r + 2 * c1r) / 3;
|
|
288
|
+
colorPalette[13] = (c0g + 2 * c1g) / 3;
|
|
289
|
+
colorPalette[14] = (c0b + 2 * c1b) / 3;
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
colorPalette[0] = c0r; colorPalette[1] = c0g; colorPalette[2] = c0b;
|
|
293
|
+
colorPalette[4] = c1r; colorPalette[5] = c1g; colorPalette[6] = c1b;
|
|
294
|
+
colorPalette[8] = (c0r + c1r) >> 1;
|
|
295
|
+
colorPalette[9] = (c0g + c1g) >> 1;
|
|
296
|
+
colorPalette[10] = (c0b + c1b) >> 1;
|
|
297
|
+
colorPalette[12] = 0; colorPalette[13] = 0; colorPalette[14] = 0;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (alpha0 > alpha1) {
|
|
301
|
+
alphaPalette[0] = alpha0;
|
|
302
|
+
alphaPalette[1] = alpha1;
|
|
303
|
+
alphaPalette[2] = (alpha0 * 6 + alpha1 * 1 + 3) / 7;
|
|
304
|
+
alphaPalette[3] = (alpha0 * 5 + alpha1 * 2 + 3) / 7;
|
|
305
|
+
alphaPalette[4] = (alpha0 * 4 + alpha1 * 3 + 3) / 7;
|
|
306
|
+
alphaPalette[5] = (alpha0 * 3 + alpha1 * 4 + 3) / 7;
|
|
307
|
+
alphaPalette[6] = (alpha0 * 2 + alpha1 * 5 + 3) / 7;
|
|
308
|
+
alphaPalette[7] = (alpha0 * 1 + alpha1 * 6 + 3) / 7;
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
alphaPalette[0] = alpha0;
|
|
312
|
+
alphaPalette[1] = alpha1;
|
|
313
|
+
alphaPalette[2] = (alpha0 * 4 + alpha1 * 1 + 2) / 5;
|
|
314
|
+
alphaPalette[3] = (alpha0 * 3 + alpha1 * 2 + 2) / 5;
|
|
315
|
+
alphaPalette[4] = (alpha0 * 2 + alpha1 * 3 + 2) / 5;
|
|
316
|
+
alphaPalette[5] = (alpha0 * 1 + alpha1 * 4 + 2) / 5;
|
|
317
|
+
alphaPalette[6] = 0;
|
|
318
|
+
alphaPalette[7] = 255;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
for (let k = 0; k < 16; k++) {
|
|
322
|
+
const bitOffset = k * 3;
|
|
323
|
+
const byteOffset = bitOffset >> 3;
|
|
324
|
+
const shift = bitOffset & 7;
|
|
325
|
+
|
|
326
|
+
if (shift <= 5) {
|
|
327
|
+
alphaIndices[k] = (alphaBits[byteOffset] >> shift) & 0x7;
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
const part1 = (alphaBits[byteOffset] >> shift) & 0x7;
|
|
331
|
+
const part2 = (alphaBits[byteOffset + 1] << (8 - shift)) & 0x7;
|
|
332
|
+
alphaIndices[k] = part1 | part2;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const baseIndex = (y * width + x) << 2;
|
|
337
|
+
let bits = colorBits;
|
|
338
|
+
|
|
339
|
+
for (let k = 0; k < 16; k++) {
|
|
340
|
+
const j = k >> 2;
|
|
341
|
+
const i = k & 3;
|
|
342
|
+
|
|
343
|
+
const idx = baseIndex + ((((j * width + i) << 2)));
|
|
344
|
+
const colorIdx = bits & 0x3;
|
|
345
|
+
bits >>>= 2;
|
|
346
|
+
|
|
347
|
+
const alpha = alphaPalette[alphaIndices[k] & 0x7];
|
|
348
|
+
|
|
349
|
+
rgba[idx + 0] = colorPalette[colorIdx * 4];
|
|
350
|
+
rgba[idx + 1] = colorPalette[colorIdx * 4 + 1];
|
|
351
|
+
rgba[idx + 2] = colorPalette[colorIdx * 4 + 2];
|
|
352
|
+
rgba[idx + 3] = alpha;
|
|
353
|
+
|
|
354
|
+
if (premultiplied && alpha > 0 && alpha < 255) {
|
|
355
|
+
const factor = 255 / alpha;
|
|
356
|
+
rgba[idx] = Math.min(255, Math.round(rgba[idx] * factor));
|
|
357
|
+
rgba[idx + 1] = Math.min(255, Math.round(rgba[idx + 1] * factor));
|
|
358
|
+
rgba[idx + 2] = Math.min(255, Math.round(rgba[idx + 2] * factor));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return rgba;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
static bgra1555(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
368
|
+
const rbga = new Uint8Array(4 * width * height);
|
|
369
|
+
let offset = 0;
|
|
370
|
+
|
|
371
|
+
for (let i = 0; i < data.length; i += 2) {
|
|
372
|
+
const color = ImageDecoder.readUInt16LE(data, i);
|
|
373
|
+
const [a, r, g, b] = ImageDecoder.decode1555(color);
|
|
374
|
+
|
|
375
|
+
rbga[offset++] = r;
|
|
376
|
+
rbga[offset++] = g;
|
|
377
|
+
rbga[offset++] = b;
|
|
378
|
+
rbga[offset++] = a;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return rbga;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
static bgra4444(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
385
|
+
const rgba = new Uint8Array(4 * width * height);
|
|
386
|
+
let offset = 0;
|
|
387
|
+
|
|
388
|
+
for (let i = 0; i < data.length; i += 2) {
|
|
389
|
+
const color = ImageDecoder.readUInt16LE(data, i);
|
|
390
|
+
const [a, r, g, b] = ImageDecoder.decode4444(color);
|
|
391
|
+
|
|
392
|
+
rgba[offset++] = r;
|
|
393
|
+
rgba[offset++] = g;
|
|
394
|
+
rgba[offset++] = b;
|
|
395
|
+
rgba[offset++] = a;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return rgba;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
static bgra555(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
402
|
+
const rgba = new Uint8Array(4 * width * height);
|
|
403
|
+
let offset = 0;
|
|
404
|
+
|
|
405
|
+
for (let i = 0; i < data.length; i += 2) {
|
|
406
|
+
const color = ImageDecoder.readUInt16LE(data, i);
|
|
407
|
+
const [r, g, b] = ImageDecoder.decode555(color);
|
|
408
|
+
|
|
409
|
+
rgba[offset++] = r;
|
|
410
|
+
rgba[offset++] = g;
|
|
411
|
+
rgba[offset++] = b;
|
|
412
|
+
rgba[offset++] = 0xff;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return rgba;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
static bgra565(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
419
|
+
const rgba = new Uint8Array(4 * width * height);
|
|
420
|
+
let offset = 0;
|
|
421
|
+
|
|
422
|
+
for (let i = 0; i < data.length; i += 2) {
|
|
423
|
+
const color = ImageDecoder.readUInt16LE(data, i);
|
|
424
|
+
const [r, g, b] = ImageDecoder.decode565(color);
|
|
425
|
+
|
|
426
|
+
rgba[offset++] = r;
|
|
427
|
+
rgba[offset++] = g;
|
|
428
|
+
rgba[offset++] = b;
|
|
429
|
+
rgba[offset++] = 0xff;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return rgba;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
static bgra888(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
436
|
+
const rgba = new Uint8Array(4 * width * height);
|
|
437
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
438
|
+
rgba[i + 0] = data[i + 2];
|
|
439
|
+
rgba[i + 1] = data[i + 1];
|
|
440
|
+
rgba[i + 2] = data[i + 0];
|
|
441
|
+
rgba[i + 3] = 0xff;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return rgba;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
static bgra8888(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
448
|
+
const rgba = new Uint8Array(4 * width * height);
|
|
449
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
450
|
+
rgba[i + 0] = data[i + 2];
|
|
451
|
+
rgba[i + 1] = data[i + 1];
|
|
452
|
+
rgba[i + 2] = data[i + 0];
|
|
453
|
+
rgba[i + 3] = data[i + 3];
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return rgba;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
static lum8(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
460
|
+
const rgba = new Uint8Array(4 * width * height);
|
|
461
|
+
|
|
462
|
+
for (let i = 0; i < data.length; i++) {
|
|
463
|
+
const offset = i * 4;
|
|
464
|
+
const luminance = data[i];
|
|
465
|
+
rgba[offset + 0] = luminance; // R
|
|
466
|
+
rgba[offset + 1] = luminance; // G
|
|
467
|
+
rgba[offset + 2] = luminance; // B
|
|
468
|
+
rgba[offset + 3] = 0xff;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return rgba;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
static lum8a8(data: Uint8Array, width: number, height: number): Uint8Array {
|
|
475
|
+
const rgba = new Uint8Array(4 * width * height);
|
|
476
|
+
let offset = 0;
|
|
477
|
+
|
|
478
|
+
for (let i = 0; i < data.length; i += 2) {
|
|
479
|
+
const luminance = data[i];
|
|
480
|
+
const alpha = data[i + 1];
|
|
481
|
+
|
|
482
|
+
rgba[offset++] = luminance;
|
|
483
|
+
rgba[offset++] = luminance;
|
|
484
|
+
rgba[offset++] = luminance;
|
|
485
|
+
rgba[offset++] = alpha;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return rgba;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
static pal4(data: Uint8Array, palette: Uint8Array, width: number, height: number): Uint8Array {
|
|
492
|
+
const rgba = new Uint8Array(4 * width * height);
|
|
493
|
+
let offset = 0;
|
|
494
|
+
|
|
495
|
+
for (let i = 0; i < data.length; i++) {
|
|
496
|
+
const b = data[i];
|
|
497
|
+
const idx1 = (b >> 4) & 0xf;
|
|
498
|
+
const idx2 = b & 0xf;
|
|
499
|
+
|
|
500
|
+
// Copying RGBA from the palette for two pixels
|
|
501
|
+
rgba[offset++] = palette[idx1 * 4 + 0]; // R
|
|
502
|
+
rgba[offset++] = palette[idx1 * 4 + 1]; // G
|
|
503
|
+
rgba[offset++] = palette[idx1 * 4 + 2]; // B
|
|
504
|
+
rgba[offset++] = palette[idx1 * 4 + 3]; // A
|
|
505
|
+
|
|
506
|
+
rgba[offset++] = palette[idx2 * 4 + 0]; // R
|
|
507
|
+
rgba[offset++] = palette[idx2 * 4 + 1]; // G
|
|
508
|
+
rgba[offset++] = palette[idx2 * 4 + 2]; // B
|
|
509
|
+
rgba[offset++] = palette[idx2 * 4 + 3]; // A
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return rgba;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
static pal4NoAlpha(data: Uint8Array, palette: Uint8Array, width: number, height: number): Uint8Array {
|
|
516
|
+
const rgba = new Uint8Array(4 * width * height);
|
|
517
|
+
let offset = 0;
|
|
518
|
+
|
|
519
|
+
for (let i = 0; i < data.length; i++) {
|
|
520
|
+
const b = data[i];
|
|
521
|
+
const colorIndex1 = (b >> 4) & 0xf;
|
|
522
|
+
const colorIndex2 = b & 0xf;
|
|
523
|
+
|
|
524
|
+
// First pixel
|
|
525
|
+
rgba[offset++] = palette[colorIndex1 * 4 + 0]; // R
|
|
526
|
+
rgba[offset++] = palette[colorIndex1 * 4 + 1]; // G
|
|
527
|
+
rgba[offset++] = palette[colorIndex1 * 4 + 2]; // B
|
|
528
|
+
rgba[offset++] = 0xff;
|
|
529
|
+
|
|
530
|
+
// Second pixel
|
|
531
|
+
rgba[offset++] = palette[colorIndex2 * 4 + 0]; // R
|
|
532
|
+
rgba[offset++] = palette[colorIndex2 * 4 + 1]; // G
|
|
533
|
+
rgba[offset++] = palette[colorIndex2 * 4 + 2]; // B
|
|
534
|
+
rgba[offset++] = 0xff;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return rgba;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
static pal8(data: Uint8Array, palette: Uint8Array, width: number, height: number): Uint8Array {
|
|
541
|
+
const rgba = new Uint8Array(4 * width * height);
|
|
542
|
+
|
|
543
|
+
for (let i = 0; i < data.length; i++) {
|
|
544
|
+
const colorIndex = data[i];
|
|
545
|
+
|
|
546
|
+
// Copy RGBA from palette
|
|
547
|
+
rgba[i * 4 + 0] = palette[colorIndex * 4 + 0]; // R
|
|
548
|
+
rgba[i * 4 + 1] = palette[colorIndex * 4 + 1]; // G
|
|
549
|
+
rgba[i * 4 + 2] = palette[colorIndex * 4 + 2]; // B
|
|
550
|
+
rgba[i * 4 + 3] = palette[colorIndex * 4 + 3]; // A
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return rgba;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
static pal8NoAlpha(data: Uint8Array, palette: Uint8Array, width: number, height: number): Uint8Array {
|
|
557
|
+
const rgba = new Uint8Array(4 * width * height);
|
|
558
|
+
|
|
559
|
+
for (let i = 0; i < data.length; i++) {
|
|
560
|
+
const colorIndex = data[i];
|
|
561
|
+
|
|
562
|
+
// Copy RGB from palette
|
|
563
|
+
rgba[i * 4 + 0] = palette[colorIndex * 4 + 0]; // R
|
|
564
|
+
rgba[i * 4 + 1] = palette[colorIndex * 4 + 1]; // G
|
|
565
|
+
rgba[i * 4 + 2] = palette[colorIndex * 4 + 2]; // B
|
|
566
|
+
rgba[i * 4 + 3] = 0xff;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return rgba;
|
|
570
|
+
}
|
|
571
571
|
}
|