s3m-three 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -202
- package/dist/render/DatasetManager.d.ts +10 -3
- package/dist/render/DatasetManager.d.ts.map +1 -1
- package/dist/render/DatasetManager.js +14 -5
- package/dist/render/DatasetManager.js.map +1 -1
- package/dist/render/EdgeLines.js +46 -46
- package/dist/render/GroundManager.d.ts +9 -0
- package/dist/render/GroundManager.d.ts.map +1 -1
- package/dist/render/GroundManager.js +13 -1
- package/dist/render/GroundManager.js.map +1 -1
- package/dist/render/Picker.js +66 -66
- package/dist/render/S3MViewer.d.ts +11 -3
- package/dist/render/S3MViewer.d.ts.map +1 -1
- package/dist/render/S3MViewer.js +18 -3
- package/dist/render/S3MViewer.js.map +1 -1
- package/dist/render/StyleMaterial.js +265 -265
- package/dist/vendor/meshopt_decoder.module.js +118 -118
- package/docs/API.md +20 -0
- package/package.json +1 -1
- package/src/core/BinaryReader.ts +116 -116
- package/src/core/DXTTextureDecode.ts +260 -260
- package/src/core/FeatureInfo.ts +150 -150
- package/src/core/MeshOpt.ts +401 -401
- package/src/core/S3MBParser.ts +590 -590
- package/src/core/types.ts +222 -222
- package/src/render/DatasetManager.ts +17 -6
- package/src/render/EdgeLines.ts +343 -343
- package/src/render/GeometryBuilder.ts +840 -840
- package/src/render/GroundManager.ts +14 -1
- package/src/render/Picker.ts +293 -293
- package/src/render/S3MViewer.ts +18 -3
- package/src/render/StyleMaterial.ts +602 -602
- package/src/render/TextureFactory.ts +134 -134
- package/src/tileset/S3MTileset.ts +552 -552
- package/src/vendor/meshopt_decoder.module.d.ts +31 -31
- package/src/vendor/meshopt_decoder.module.js +118 -118
- package/dist/render/CoordinateHelper.d.ts +0 -90
- package/dist/render/CoordinateHelper.d.ts.map +0 -1
- package/dist/render/CoordinateHelper.js +0 -85
- package/dist/render/CoordinateHelper.js.map +0 -1
- package/dist/render/S3MLayer.d.ts +0 -457
- package/dist/render/S3MLayer.d.ts.map +0 -1
- package/dist/render/S3MLayer.js +0 -1860
- package/dist/render/S3MLayer.js.map +0 -1
|
@@ -1,260 +1,260 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DXT1 / DXT3 / DXT5 纹理解码(squish 算法的 JS 移植)
|
|
3
|
-
*
|
|
4
|
-
* 来源:SuperMap iClient3D S3M SDK(Apache-2.0)。
|
|
5
|
-
* 迁移到 TS 时做了两件事,算法本身未改:
|
|
6
|
-
* 1. 补齐类型;
|
|
7
|
-
* 2. 修掉 `DecompressAlphaDxt3` 里把 `block` 误写成未定义全局 `bytes` 的笔误
|
|
8
|
-
* (DXT3 纹理此前会直接 ReferenceError)。
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const kDxt1 = 1 << 0;
|
|
12
|
-
const kDxt3 = 1 << 1;
|
|
13
|
-
const kDxt5 = 1 << 2;
|
|
14
|
-
const krgb565 = 1 << 5;
|
|
15
|
-
|
|
16
|
-
function Unpack565(packed0: number, packed1: number, colour: Uint8Array, offset: number): number {
|
|
17
|
-
const value = packed0 | (packed1 << 8);
|
|
18
|
-
|
|
19
|
-
const red = (value >> 11) & 0x1f;
|
|
20
|
-
const green = (value >> 5) & 0x3f;
|
|
21
|
-
const blue = value & 0x1f;
|
|
22
|
-
|
|
23
|
-
colour[offset + 0] = (red << 3) | (red >> 2);
|
|
24
|
-
colour[offset + 1] = (green << 2) | (green >> 4);
|
|
25
|
-
colour[offset + 2] = (blue << 3) | (blue >> 2);
|
|
26
|
-
colour[offset + 3] = 255;
|
|
27
|
-
|
|
28
|
-
return value;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function DecompressColour(rgba: Uint8Array, block: Uint8Array, nOffset: number, isDxt1: boolean): void {
|
|
32
|
-
const codes = new Uint8Array(16);
|
|
33
|
-
|
|
34
|
-
const a = Unpack565(block[nOffset + 0], block[nOffset + 1], codes, 0);
|
|
35
|
-
const b = Unpack565(block[nOffset + 2], block[nOffset + 3], codes, 4);
|
|
36
|
-
|
|
37
|
-
for (let i = 0; i < 3; i++) {
|
|
38
|
-
const c = codes[i];
|
|
39
|
-
const d = codes[4 + i];
|
|
40
|
-
|
|
41
|
-
if (isDxt1 && a <= b) {
|
|
42
|
-
codes[8 + i] = (c + d) / 2;
|
|
43
|
-
codes[12 + i] = 0;
|
|
44
|
-
} else {
|
|
45
|
-
codes[8 + i] = (2 * c + d) / 3;
|
|
46
|
-
codes[12 + i] = (c + 2 * d) / 3;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
codes[8 + 3] = 255;
|
|
51
|
-
codes[12 + 3] = isDxt1 && a <= b ? 0 : 255;
|
|
52
|
-
|
|
53
|
-
const indices = new Uint8Array(16);
|
|
54
|
-
for (let i = 0; i < 4; ++i) {
|
|
55
|
-
const packed = block[nOffset + 4 + i];
|
|
56
|
-
|
|
57
|
-
indices[4 * i + 0] = packed & 0x3;
|
|
58
|
-
indices[4 * i + 1] = (packed >> 2) & 0x3;
|
|
59
|
-
indices[4 * i + 2] = (packed >> 4) & 0x3;
|
|
60
|
-
indices[4 * i + 3] = (packed >> 6) & 0x3;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
for (let i = 0; i < 16; ++i) {
|
|
64
|
-
const offset = 4 * indices[i];
|
|
65
|
-
for (let j = 0; j < 4; ++j) rgba[4 * i + j] = codes[offset + j];
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function DecompressAlphaDxt3(rgba: Uint8Array, block: Uint8Array, nOffset: number): void {
|
|
70
|
-
// unpack the alpha values pairwise
|
|
71
|
-
for (let i = 0; i < 8; ++i) {
|
|
72
|
-
// quantise down to 4 bits
|
|
73
|
-
const quant = block[nOffset + i];
|
|
74
|
-
|
|
75
|
-
// unpack the values
|
|
76
|
-
const lo = quant & 0x0f;
|
|
77
|
-
const hi = quant & 0xf0;
|
|
78
|
-
|
|
79
|
-
// convert back up to bytes
|
|
80
|
-
rgba[8 * i + 3] = lo | (lo << 4);
|
|
81
|
-
rgba[8 * i + 7] = hi | (hi >> 4);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function DecompressAlphaDxt5(rgba: Uint8Array, block: Uint8Array, start: number): void {
|
|
86
|
-
const alpha0 = block[start + 0];
|
|
87
|
-
const alpha1 = block[start + 1];
|
|
88
|
-
|
|
89
|
-
const codes = new Uint8Array(8);
|
|
90
|
-
|
|
91
|
-
codes[0] = alpha0;
|
|
92
|
-
codes[1] = alpha1;
|
|
93
|
-
if (alpha0 <= alpha1) {
|
|
94
|
-
// use 5-alpha codebook
|
|
95
|
-
for (let i = 1; i < 5; ++i) codes[1 + i] = ((5 - i) * alpha0 + i * alpha1) / 5;
|
|
96
|
-
codes[6] = 0;
|
|
97
|
-
codes[7] = 255;
|
|
98
|
-
} else {
|
|
99
|
-
// use 7-alpha codebook
|
|
100
|
-
for (let i = 1; i < 7; ++i) codes[1 + i] = ((7 - i) * alpha0 + i * alpha1) / 7;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const indices = new Uint8Array(16);
|
|
104
|
-
let nOffset = start + 2;
|
|
105
|
-
let nBegin = 0;
|
|
106
|
-
for (let i = 0; i < 2; ++i) {
|
|
107
|
-
// grab 3 bytes
|
|
108
|
-
let value = 0;
|
|
109
|
-
for (let j = 0; j < 3; ++j) {
|
|
110
|
-
const byte = block[nOffset++];
|
|
111
|
-
value |= byte << (8 * j);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// unpack 8 3-bit values from it
|
|
115
|
-
for (let j = 0; j < 8; ++j) {
|
|
116
|
-
const index = (value >> (3 * j)) & 0x7;
|
|
117
|
-
indices[nBegin++] = index;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
for (let i = 0; i < 16; ++i) rgba[4 * i + 3] = codes[indices[i]];
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function Decompress(rgba: Uint8Array, block: Uint8Array, nOffset: number, flags: number): void {
|
|
125
|
-
let nOffset2 = 0;
|
|
126
|
-
if ((flags & (kDxt3 | kDxt5)) !== 0) nOffset2 = 8;
|
|
127
|
-
|
|
128
|
-
DecompressColour(rgba, block, nOffset + nOffset2, (flags & kDxt1) !== 0);
|
|
129
|
-
|
|
130
|
-
if ((flags & kDxt3) !== 0) {
|
|
131
|
-
DecompressAlphaDxt3(rgba, block, nOffset);
|
|
132
|
-
} else if ((flags & kDxt5) !== 0) {
|
|
133
|
-
DecompressAlphaDxt5(rgba, block, nOffset);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/** 把 block 视为 uint16 数组:DXT1 的 565 快路径需要按 16 位读 */
|
|
138
|
-
function asU16(view: Uint8Array): Uint16Array {
|
|
139
|
-
const start = view.byteOffset & ~1;
|
|
140
|
-
const len = (view.byteLength + (view.byteOffset & 1)) >> 1;
|
|
141
|
-
return new Uint16Array(view.buffer, start, len);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function DecompressImage565(rgb565: Uint16Array, width: number, height: number, blocks: Uint16Array): Uint16Array {
|
|
145
|
-
const c = new Uint16Array(4);
|
|
146
|
-
const dst = rgb565;
|
|
147
|
-
let m = 0;
|
|
148
|
-
let dstI = 0;
|
|
149
|
-
let i = 0;
|
|
150
|
-
let r0 = 0;
|
|
151
|
-
let g0 = 0;
|
|
152
|
-
let b0 = 0;
|
|
153
|
-
let r1 = 0;
|
|
154
|
-
let g1 = 0;
|
|
155
|
-
let b1 = 0;
|
|
156
|
-
|
|
157
|
-
const blockWidth = width / 4;
|
|
158
|
-
const blockHeight = height / 4;
|
|
159
|
-
for (let blockY = 0; blockY < blockHeight; blockY++) {
|
|
160
|
-
for (let blockX = 0; blockX < blockWidth; blockX++) {
|
|
161
|
-
i = 4 * ((blockHeight - blockY) * blockWidth + blockX);
|
|
162
|
-
c[0] = blocks[i];
|
|
163
|
-
c[1] = blocks[i + 1];
|
|
164
|
-
r0 = c[0] & 0x1f;
|
|
165
|
-
g0 = c[0] & 0x7e0;
|
|
166
|
-
b0 = c[0] & 0xf800;
|
|
167
|
-
r1 = c[1] & 0x1f;
|
|
168
|
-
g1 = c[1] & 0x7e0;
|
|
169
|
-
b1 = c[1] & 0xf800;
|
|
170
|
-
// Interpolate between c0 and c1 to get c2 and c3.
|
|
171
|
-
// 1/3 用 3/8 近似、2/3 用 5/8 近似 —— 这也是多数 GPU 硬件解码器的做法
|
|
172
|
-
c[2] = ((5 * r0 + 3 * r1) >> 3) | (((5 * g0 + 3 * g1) >> 3) & 0x7e0) | (((5 * b0 + 3 * b1) >> 3) & 0xf800);
|
|
173
|
-
c[3] = ((5 * r1 + 3 * r0) >> 3) | (((5 * g1 + 3 * g0) >> 3) & 0x7e0) | (((5 * b1 + 3 * b0) >> 3) & 0xf800);
|
|
174
|
-
m = blocks[i + 2];
|
|
175
|
-
dstI = blockY * 4 * width + blockX * 4;
|
|
176
|
-
dst[dstI] = c[m & 0x3];
|
|
177
|
-
dst[dstI + 1] = c[(m >> 2) & 0x3];
|
|
178
|
-
dst[dstI + 2] = c[(m >> 4) & 0x3];
|
|
179
|
-
dst[dstI + 3] = c[(m >> 6) & 0x3];
|
|
180
|
-
dstI += width;
|
|
181
|
-
dst[dstI] = c[(m >> 8) & 0x3];
|
|
182
|
-
dst[dstI + 1] = c[(m >> 10) & 0x3];
|
|
183
|
-
dst[dstI + 2] = c[(m >> 12) & 0x3];
|
|
184
|
-
dst[dstI + 3] = c[(m >> 14) & 0x3];
|
|
185
|
-
m = blocks[i + 3];
|
|
186
|
-
dstI += width;
|
|
187
|
-
dst[dstI] = c[m & 0x3];
|
|
188
|
-
dst[dstI + 1] = c[(m >> 2) & 0x3];
|
|
189
|
-
dst[dstI + 2] = c[(m >> 4) & 0x3];
|
|
190
|
-
dst[dstI + 3] = c[(m >> 6) & 0x3];
|
|
191
|
-
dstI += width;
|
|
192
|
-
dst[dstI] = c[(m >> 8) & 0x3];
|
|
193
|
-
dst[dstI + 1] = c[(m >> 10) & 0x3];
|
|
194
|
-
dst[dstI + 2] = c[(m >> 12) & 0x3];
|
|
195
|
-
dst[dstI + 3] = c[(m >> 14) & 0x3];
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
return dst;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* 解压整张 DXT 图。
|
|
203
|
-
*
|
|
204
|
-
* 解出的像素按 `{ r1,g1,b1,a1, ... , rn,gn,bn,an }`(n = width*height)连续写入 `rgba`。
|
|
205
|
-
*/
|
|
206
|
-
function DecompressImage(rgba: Uint8Array, width: number, height: number, blocks: Uint8Array, flags: number): void {
|
|
207
|
-
const bytesPerBlock = (flags & kDxt1) !== 0 ? 8 : 16;
|
|
208
|
-
|
|
209
|
-
let nOffset = 0;
|
|
210
|
-
for (let y = 0; y < height; y += 4) {
|
|
211
|
-
for (let x = 0; x < width; x += 4) {
|
|
212
|
-
const targetRgba = new Uint8Array(4 * 16);
|
|
213
|
-
Decompress(targetRgba, blocks, nOffset, flags);
|
|
214
|
-
|
|
215
|
-
let nOffsetTarget = 0;
|
|
216
|
-
for (let py = 0; py < 4; ++py) {
|
|
217
|
-
for (let px = 0; px < 4; ++px) {
|
|
218
|
-
const sx = x + px;
|
|
219
|
-
const sy = y + py;
|
|
220
|
-
if (sx < width && sy < height) {
|
|
221
|
-
// flip Y
|
|
222
|
-
let nBegin = 4 * (width * (height - sy) + sx);
|
|
223
|
-
|
|
224
|
-
for (let i = 0; i < 4; ++i) rgba[nBegin++] = targetRgba[nOffsetTarget++];
|
|
225
|
-
} else {
|
|
226
|
-
nOffsetTarget += 4;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// advance
|
|
232
|
-
nOffset += bytesPerBlock;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const DXTTextureDecode = {
|
|
238
|
-
/**
|
|
239
|
-
* @param out 输出缓冲,长度须为 width*height*4
|
|
240
|
-
* @param width 像素宽
|
|
241
|
-
* @param height 像素高
|
|
242
|
-
* @param block 压缩块
|
|
243
|
-
* @param format S3M pixelFormat(>11 或 5 走 DXT5,其余走 DXT1)
|
|
244
|
-
*/
|
|
245
|
-
decode(out: Uint8Array, width: number, height: number, block: Uint8Array, format: number): void {
|
|
246
|
-
if (out == null || block == null || height === 0 || width === 0) return;
|
|
247
|
-
|
|
248
|
-
// 有 alpha 通道 → RGBA,否则走 rgb565
|
|
249
|
-
// S3MPixelFormat.BGR -> 11 / LUMINANCE_ALPHA -> 5
|
|
250
|
-
const flags = format > 11 || format === 5 ? kDxt5 : kDxt1 | krgb565;
|
|
251
|
-
|
|
252
|
-
if (flags & kDxt1 && flags & krgb565) {
|
|
253
|
-
DecompressImage565(asU16(out), width, height, asU16(block));
|
|
254
|
-
} else {
|
|
255
|
-
DecompressImage(out, width, height, block, flags);
|
|
256
|
-
}
|
|
257
|
-
},
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
export default DXTTextureDecode;
|
|
1
|
+
/**
|
|
2
|
+
* DXT1 / DXT3 / DXT5 纹理解码(squish 算法的 JS 移植)
|
|
3
|
+
*
|
|
4
|
+
* 来源:SuperMap iClient3D S3M SDK(Apache-2.0)。
|
|
5
|
+
* 迁移到 TS 时做了两件事,算法本身未改:
|
|
6
|
+
* 1. 补齐类型;
|
|
7
|
+
* 2. 修掉 `DecompressAlphaDxt3` 里把 `block` 误写成未定义全局 `bytes` 的笔误
|
|
8
|
+
* (DXT3 纹理此前会直接 ReferenceError)。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const kDxt1 = 1 << 0;
|
|
12
|
+
const kDxt3 = 1 << 1;
|
|
13
|
+
const kDxt5 = 1 << 2;
|
|
14
|
+
const krgb565 = 1 << 5;
|
|
15
|
+
|
|
16
|
+
function Unpack565(packed0: number, packed1: number, colour: Uint8Array, offset: number): number {
|
|
17
|
+
const value = packed0 | (packed1 << 8);
|
|
18
|
+
|
|
19
|
+
const red = (value >> 11) & 0x1f;
|
|
20
|
+
const green = (value >> 5) & 0x3f;
|
|
21
|
+
const blue = value & 0x1f;
|
|
22
|
+
|
|
23
|
+
colour[offset + 0] = (red << 3) | (red >> 2);
|
|
24
|
+
colour[offset + 1] = (green << 2) | (green >> 4);
|
|
25
|
+
colour[offset + 2] = (blue << 3) | (blue >> 2);
|
|
26
|
+
colour[offset + 3] = 255;
|
|
27
|
+
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function DecompressColour(rgba: Uint8Array, block: Uint8Array, nOffset: number, isDxt1: boolean): void {
|
|
32
|
+
const codes = new Uint8Array(16);
|
|
33
|
+
|
|
34
|
+
const a = Unpack565(block[nOffset + 0], block[nOffset + 1], codes, 0);
|
|
35
|
+
const b = Unpack565(block[nOffset + 2], block[nOffset + 3], codes, 4);
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < 3; i++) {
|
|
38
|
+
const c = codes[i];
|
|
39
|
+
const d = codes[4 + i];
|
|
40
|
+
|
|
41
|
+
if (isDxt1 && a <= b) {
|
|
42
|
+
codes[8 + i] = (c + d) / 2;
|
|
43
|
+
codes[12 + i] = 0;
|
|
44
|
+
} else {
|
|
45
|
+
codes[8 + i] = (2 * c + d) / 3;
|
|
46
|
+
codes[12 + i] = (c + 2 * d) / 3;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
codes[8 + 3] = 255;
|
|
51
|
+
codes[12 + 3] = isDxt1 && a <= b ? 0 : 255;
|
|
52
|
+
|
|
53
|
+
const indices = new Uint8Array(16);
|
|
54
|
+
for (let i = 0; i < 4; ++i) {
|
|
55
|
+
const packed = block[nOffset + 4 + i];
|
|
56
|
+
|
|
57
|
+
indices[4 * i + 0] = packed & 0x3;
|
|
58
|
+
indices[4 * i + 1] = (packed >> 2) & 0x3;
|
|
59
|
+
indices[4 * i + 2] = (packed >> 4) & 0x3;
|
|
60
|
+
indices[4 * i + 3] = (packed >> 6) & 0x3;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (let i = 0; i < 16; ++i) {
|
|
64
|
+
const offset = 4 * indices[i];
|
|
65
|
+
for (let j = 0; j < 4; ++j) rgba[4 * i + j] = codes[offset + j];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function DecompressAlphaDxt3(rgba: Uint8Array, block: Uint8Array, nOffset: number): void {
|
|
70
|
+
// unpack the alpha values pairwise
|
|
71
|
+
for (let i = 0; i < 8; ++i) {
|
|
72
|
+
// quantise down to 4 bits
|
|
73
|
+
const quant = block[nOffset + i];
|
|
74
|
+
|
|
75
|
+
// unpack the values
|
|
76
|
+
const lo = quant & 0x0f;
|
|
77
|
+
const hi = quant & 0xf0;
|
|
78
|
+
|
|
79
|
+
// convert back up to bytes
|
|
80
|
+
rgba[8 * i + 3] = lo | (lo << 4);
|
|
81
|
+
rgba[8 * i + 7] = hi | (hi >> 4);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function DecompressAlphaDxt5(rgba: Uint8Array, block: Uint8Array, start: number): void {
|
|
86
|
+
const alpha0 = block[start + 0];
|
|
87
|
+
const alpha1 = block[start + 1];
|
|
88
|
+
|
|
89
|
+
const codes = new Uint8Array(8);
|
|
90
|
+
|
|
91
|
+
codes[0] = alpha0;
|
|
92
|
+
codes[1] = alpha1;
|
|
93
|
+
if (alpha0 <= alpha1) {
|
|
94
|
+
// use 5-alpha codebook
|
|
95
|
+
for (let i = 1; i < 5; ++i) codes[1 + i] = ((5 - i) * alpha0 + i * alpha1) / 5;
|
|
96
|
+
codes[6] = 0;
|
|
97
|
+
codes[7] = 255;
|
|
98
|
+
} else {
|
|
99
|
+
// use 7-alpha codebook
|
|
100
|
+
for (let i = 1; i < 7; ++i) codes[1 + i] = ((7 - i) * alpha0 + i * alpha1) / 7;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const indices = new Uint8Array(16);
|
|
104
|
+
let nOffset = start + 2;
|
|
105
|
+
let nBegin = 0;
|
|
106
|
+
for (let i = 0; i < 2; ++i) {
|
|
107
|
+
// grab 3 bytes
|
|
108
|
+
let value = 0;
|
|
109
|
+
for (let j = 0; j < 3; ++j) {
|
|
110
|
+
const byte = block[nOffset++];
|
|
111
|
+
value |= byte << (8 * j);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// unpack 8 3-bit values from it
|
|
115
|
+
for (let j = 0; j < 8; ++j) {
|
|
116
|
+
const index = (value >> (3 * j)) & 0x7;
|
|
117
|
+
indices[nBegin++] = index;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (let i = 0; i < 16; ++i) rgba[4 * i + 3] = codes[indices[i]];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function Decompress(rgba: Uint8Array, block: Uint8Array, nOffset: number, flags: number): void {
|
|
125
|
+
let nOffset2 = 0;
|
|
126
|
+
if ((flags & (kDxt3 | kDxt5)) !== 0) nOffset2 = 8;
|
|
127
|
+
|
|
128
|
+
DecompressColour(rgba, block, nOffset + nOffset2, (flags & kDxt1) !== 0);
|
|
129
|
+
|
|
130
|
+
if ((flags & kDxt3) !== 0) {
|
|
131
|
+
DecompressAlphaDxt3(rgba, block, nOffset);
|
|
132
|
+
} else if ((flags & kDxt5) !== 0) {
|
|
133
|
+
DecompressAlphaDxt5(rgba, block, nOffset);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** 把 block 视为 uint16 数组:DXT1 的 565 快路径需要按 16 位读 */
|
|
138
|
+
function asU16(view: Uint8Array): Uint16Array {
|
|
139
|
+
const start = view.byteOffset & ~1;
|
|
140
|
+
const len = (view.byteLength + (view.byteOffset & 1)) >> 1;
|
|
141
|
+
return new Uint16Array(view.buffer, start, len);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function DecompressImage565(rgb565: Uint16Array, width: number, height: number, blocks: Uint16Array): Uint16Array {
|
|
145
|
+
const c = new Uint16Array(4);
|
|
146
|
+
const dst = rgb565;
|
|
147
|
+
let m = 0;
|
|
148
|
+
let dstI = 0;
|
|
149
|
+
let i = 0;
|
|
150
|
+
let r0 = 0;
|
|
151
|
+
let g0 = 0;
|
|
152
|
+
let b0 = 0;
|
|
153
|
+
let r1 = 0;
|
|
154
|
+
let g1 = 0;
|
|
155
|
+
let b1 = 0;
|
|
156
|
+
|
|
157
|
+
const blockWidth = width / 4;
|
|
158
|
+
const blockHeight = height / 4;
|
|
159
|
+
for (let blockY = 0; blockY < blockHeight; blockY++) {
|
|
160
|
+
for (let blockX = 0; blockX < blockWidth; blockX++) {
|
|
161
|
+
i = 4 * ((blockHeight - blockY) * blockWidth + blockX);
|
|
162
|
+
c[0] = blocks[i];
|
|
163
|
+
c[1] = blocks[i + 1];
|
|
164
|
+
r0 = c[0] & 0x1f;
|
|
165
|
+
g0 = c[0] & 0x7e0;
|
|
166
|
+
b0 = c[0] & 0xf800;
|
|
167
|
+
r1 = c[1] & 0x1f;
|
|
168
|
+
g1 = c[1] & 0x7e0;
|
|
169
|
+
b1 = c[1] & 0xf800;
|
|
170
|
+
// Interpolate between c0 and c1 to get c2 and c3.
|
|
171
|
+
// 1/3 用 3/8 近似、2/3 用 5/8 近似 —— 这也是多数 GPU 硬件解码器的做法
|
|
172
|
+
c[2] = ((5 * r0 + 3 * r1) >> 3) | (((5 * g0 + 3 * g1) >> 3) & 0x7e0) | (((5 * b0 + 3 * b1) >> 3) & 0xf800);
|
|
173
|
+
c[3] = ((5 * r1 + 3 * r0) >> 3) | (((5 * g1 + 3 * g0) >> 3) & 0x7e0) | (((5 * b1 + 3 * b0) >> 3) & 0xf800);
|
|
174
|
+
m = blocks[i + 2];
|
|
175
|
+
dstI = blockY * 4 * width + blockX * 4;
|
|
176
|
+
dst[dstI] = c[m & 0x3];
|
|
177
|
+
dst[dstI + 1] = c[(m >> 2) & 0x3];
|
|
178
|
+
dst[dstI + 2] = c[(m >> 4) & 0x3];
|
|
179
|
+
dst[dstI + 3] = c[(m >> 6) & 0x3];
|
|
180
|
+
dstI += width;
|
|
181
|
+
dst[dstI] = c[(m >> 8) & 0x3];
|
|
182
|
+
dst[dstI + 1] = c[(m >> 10) & 0x3];
|
|
183
|
+
dst[dstI + 2] = c[(m >> 12) & 0x3];
|
|
184
|
+
dst[dstI + 3] = c[(m >> 14) & 0x3];
|
|
185
|
+
m = blocks[i + 3];
|
|
186
|
+
dstI += width;
|
|
187
|
+
dst[dstI] = c[m & 0x3];
|
|
188
|
+
dst[dstI + 1] = c[(m >> 2) & 0x3];
|
|
189
|
+
dst[dstI + 2] = c[(m >> 4) & 0x3];
|
|
190
|
+
dst[dstI + 3] = c[(m >> 6) & 0x3];
|
|
191
|
+
dstI += width;
|
|
192
|
+
dst[dstI] = c[(m >> 8) & 0x3];
|
|
193
|
+
dst[dstI + 1] = c[(m >> 10) & 0x3];
|
|
194
|
+
dst[dstI + 2] = c[(m >> 12) & 0x3];
|
|
195
|
+
dst[dstI + 3] = c[(m >> 14) & 0x3];
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return dst;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* 解压整张 DXT 图。
|
|
203
|
+
*
|
|
204
|
+
* 解出的像素按 `{ r1,g1,b1,a1, ... , rn,gn,bn,an }`(n = width*height)连续写入 `rgba`。
|
|
205
|
+
*/
|
|
206
|
+
function DecompressImage(rgba: Uint8Array, width: number, height: number, blocks: Uint8Array, flags: number): void {
|
|
207
|
+
const bytesPerBlock = (flags & kDxt1) !== 0 ? 8 : 16;
|
|
208
|
+
|
|
209
|
+
let nOffset = 0;
|
|
210
|
+
for (let y = 0; y < height; y += 4) {
|
|
211
|
+
for (let x = 0; x < width; x += 4) {
|
|
212
|
+
const targetRgba = new Uint8Array(4 * 16);
|
|
213
|
+
Decompress(targetRgba, blocks, nOffset, flags);
|
|
214
|
+
|
|
215
|
+
let nOffsetTarget = 0;
|
|
216
|
+
for (let py = 0; py < 4; ++py) {
|
|
217
|
+
for (let px = 0; px < 4; ++px) {
|
|
218
|
+
const sx = x + px;
|
|
219
|
+
const sy = y + py;
|
|
220
|
+
if (sx < width && sy < height) {
|
|
221
|
+
// flip Y
|
|
222
|
+
let nBegin = 4 * (width * (height - sy) + sx);
|
|
223
|
+
|
|
224
|
+
for (let i = 0; i < 4; ++i) rgba[nBegin++] = targetRgba[nOffsetTarget++];
|
|
225
|
+
} else {
|
|
226
|
+
nOffsetTarget += 4;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// advance
|
|
232
|
+
nOffset += bytesPerBlock;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const DXTTextureDecode = {
|
|
238
|
+
/**
|
|
239
|
+
* @param out 输出缓冲,长度须为 width*height*4
|
|
240
|
+
* @param width 像素宽
|
|
241
|
+
* @param height 像素高
|
|
242
|
+
* @param block 压缩块
|
|
243
|
+
* @param format S3M pixelFormat(>11 或 5 走 DXT5,其余走 DXT1)
|
|
244
|
+
*/
|
|
245
|
+
decode(out: Uint8Array, width: number, height: number, block: Uint8Array, format: number): void {
|
|
246
|
+
if (out == null || block == null || height === 0 || width === 0) return;
|
|
247
|
+
|
|
248
|
+
// 有 alpha 通道 → RGBA,否则走 rgb565
|
|
249
|
+
// S3MPixelFormat.BGR -> 11 / LUMINANCE_ALPHA -> 5
|
|
250
|
+
const flags = format > 11 || format === 5 ? kDxt5 : kDxt1 | krgb565;
|
|
251
|
+
|
|
252
|
+
if (flags & kDxt1 && flags & krgb565) {
|
|
253
|
+
DecompressImage565(asU16(out), width, height, asU16(block));
|
|
254
|
+
} else {
|
|
255
|
+
DecompressImage(out, width, height, block, flags);
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
export default DXTTextureDecode;
|