@px-lsp/server 0.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 -0
- package/README.md +334 -0
- package/THIRD-PARTY-NOTICES.md +83 -0
- package/data/ck3/dataTypes.json +2195 -0
- package/data/ck3/data_types/data_types_common.txt +2040 -0
- package/data/ck3/data_types/data_types_gui.txt +5264 -0
- package/data/ck3/data_types/data_types_internalclausewitzgui.txt +14843 -0
- package/data/ck3/data_types/data_types_script.txt +4251 -0
- package/data/ck3/data_types/data_types_uncategorized.txt +109984 -0
- package/data/ck3/freqs.json +1 -0
- package/data/ck3/guiSchema.json +6344 -0
- package/data/ck3/script_docs/effects.log +16059 -0
- package/data/ck3/script_docs/event_targets.log +2098 -0
- package/data/ck3/script_docs/modifiers.log +2228 -0
- package/data/ck3/script_docs/on_actions.log +5275 -0
- package/data/ck3/script_docs/triggers.log +11991 -0
- package/data/ck3/structures.json +9743 -0
- package/data/ck3/wikidocs/ATTRIBUTION.md +18 -0
- package/data/ck3/wikidocs/Data_types.md +2568 -0
- package/data/ck3/wikidocs/Effects_list.md +1176 -0
- package/data/ck3/wikidocs/Scopes_list.md +341 -0
- package/data/ck3/wikidocs/Triggers_list.md +1097 -0
- package/data/eu5/data_types/data_types_common.txt +2087 -0
- package/data/eu5/data_types/data_types_gui.txt +6732 -0
- package/data/eu5/data_types/data_types_internalclausewitzgui.txt +19276 -0
- package/data/eu5/data_types/data_types_script.txt +5688 -0
- package/data/eu5/data_types/data_types_uncategorized.txt +135569 -0
- package/data/vic3/data_types/data_types_common.txt +2021 -0
- package/data/vic3/data_types/data_types_gui.txt +5592 -0
- package/data/vic3/data_types/data_types_internalclausewitzgui.txt +17304 -0
- package/data/vic3/data_types/data_types_script.txt +2817 -0
- package/data/vic3/data_types/data_types_uncategorized.txt +84354 -0
- package/data/vic3/freqs.json +1 -0
- package/data/vic3/guiSchema.json +5578 -0
- package/data/vic3/script_docs/effects.log +38135 -0
- package/data/vic3/script_docs/event_targets.log +2028 -0
- package/data/vic3/script_docs/modifiers.log +18954 -0
- package/data/vic3/script_docs/on_actions.log +1561 -0
- package/data/vic3/script_docs/triggers.log +15738 -0
- package/data/vic3/structures.json +10189 -0
- package/dist/server.js +63668 -0
- package/media/px-lsp.svg +12 -0
- package/package.json +50 -0
- package/src/clientMode.ts +60 -0
- package/src/coa/coa.ts +184 -0
- package/src/coa/coaParse.ts +267 -0
- package/src/context.ts +78 -0
- package/src/contextKeywords.ts +224 -0
- package/src/data/dataBindingMacros.ts +82 -0
- package/src/data/dataFnDocs.ts +152 -0
- package/src/data/dataFnUsage.ts +431 -0
- package/src/data/dataTypes.ts +279 -0
- package/src/data/defines.ts +123 -0
- package/src/data/docsParser.ts +453 -0
- package/src/data/keywordDocs.ts +98 -0
- package/src/data/modifierTemplates.ts +143 -0
- package/src/data/textFormatting.ts +165 -0
- package/src/data/wikiDocs.ts +187 -0
- package/src/dds/decoder.ts +1007 -0
- package/src/dds/encode.ts +235 -0
- package/src/dds/index.ts +58 -0
- package/src/dds/png.ts +96 -0
- package/src/dds/tga.ts +62 -0
- package/src/documents.ts +35 -0
- package/src/features/assetPaths.ts +169 -0
- package/src/features/codeActions.ts +148 -0
- package/src/features/colors.ts +244 -0
- package/src/features/completion.ts +961 -0
- package/src/features/datafunction.ts +729 -0
- package/src/features/definition.ts +84 -0
- package/src/features/diagnostics.ts +244 -0
- package/src/features/folding.ts +106 -0
- package/src/features/formatting.ts +60 -0
- package/src/features/guiLanguage.ts +366 -0
- package/src/features/guiNavigation.ts +140 -0
- package/src/features/guiTree.ts +97 -0
- package/src/features/hover.ts +817 -0
- package/src/features/hoverRender.ts +222 -0
- package/src/features/inlayHints.ts +147 -0
- package/src/features/locFormatting.ts +127 -0
- package/src/features/references.ts +70 -0
- package/src/features/rename.ts +135 -0
- package/src/features/scopeAt.ts +65 -0
- package/src/features/semanticTokens.ts +188 -0
- package/src/features/signatureHelp.ts +72 -0
- package/src/features/symbols.ts +241 -0
- package/src/features/textureHover.ts +143 -0
- package/src/features/workspaceSymbols.ts +69 -0
- package/src/games/active.ts +19 -0
- package/src/games/ck3/ambientScopes.ts +273 -0
- package/src/games/ck3/index.ts +38 -0
- package/src/games/ck3/meta.ts +28 -0
- package/src/games/ck3/modifierPlaceholders.ts +61 -0
- package/src/games/ck3/saveSchema.ts +134 -0
- package/src/games/ck3/scaffolds.ts +197 -0
- package/src/games/ck3/schema.ts +422 -0
- package/src/games/ck3/structures.ts +887 -0
- package/src/games/eu5/index.ts +75 -0
- package/src/games/eu5/meta.ts +44 -0
- package/src/games/eu5/scaffolds.ts +49 -0
- package/src/games/eu5/schema.generated.ts +1043 -0
- package/src/games/jomini/variables.ts +134 -0
- package/src/games/profile.ts +205 -0
- package/src/games/registry.ts +27 -0
- package/src/games/vic3/index.ts +52 -0
- package/src/games/vic3/meta.ts +55 -0
- package/src/games/vic3/saveSchema.ts +77 -0
- package/src/games/vic3/scaffolds.ts +135 -0
- package/src/games/vic3/schema.ts +650 -0
- package/src/games/vic3/structures.ts +33 -0
- package/src/gui/anchorSpec.ts +66 -0
- package/src/gui/declMarkers.ts +30 -0
- package/src/gui/fillGeometry.ts +101 -0
- package/src/gui/guiDefs.ts +386 -0
- package/src/gui/guiDependencies.ts +352 -0
- package/src/gui/guiLinks.ts +64 -0
- package/src/gui/layoutEngine.ts +1998 -0
- package/src/gui/layoutService.ts +221 -0
- package/src/gui/measuredMetrics.ts +21 -0
- package/src/gui/previewService.ts +89 -0
- package/src/gui/saveSchema.ts +220 -0
- package/src/gui/saveValues.ts +399 -0
- package/src/gui/saveZip.ts +60 -0
- package/src/gui/sourceEdit.ts +535 -0
- package/src/gui/sourceEditService.ts +439 -0
- package/src/gui/sourceModel.ts +603 -0
- package/src/gui/textResolve.ts +145 -0
- package/src/gui/textureInfo.ts +106 -0
- package/src/gui/vocabulary.ts +149 -0
- package/src/gui/widgetEdit.ts +52 -0
- package/src/gui/widgetInfo.ts +245 -0
- package/src/index/docComments.ts +103 -0
- package/src/index/extract.ts +252 -0
- package/src/index/indexer.ts +369 -0
- package/src/index/intern.ts +101 -0
- package/src/index/lazyRefs.ts +145 -0
- package/src/index/modOrigin.ts +69 -0
- package/src/index/references.ts +534 -0
- package/src/overview/dependencies.ts +240 -0
- package/src/overview/eventBanner.ts +95 -0
- package/src/overview/eventDetail.ts +482 -0
- package/src/overview/eventGraph.ts +617 -0
- package/src/overview/eventVocabulary.ts +214 -0
- package/src/overview/locCoverage.ts +138 -0
- package/src/overview/modOverview.ts +29 -0
- package/src/overview/overrides.ts +89 -0
- package/src/parseCache.ts +81 -0
- package/src/parser/cst.ts +257 -0
- package/src/parser/encoding.ts +106 -0
- package/src/parser/index.ts +7 -0
- package/src/parser/lexer.ts +245 -0
- package/src/parser/locParser.ts +276 -0
- package/src/parser/parser.ts +360 -0
- package/src/schema/freqs.ts +70 -0
- package/src/schema/loader.ts +113 -0
- package/src/schema/types.ts +142 -0
- package/src/scopes/inference.ts +478 -0
- package/src/scopes/model.ts +148 -0
- package/src/scopes/varTypes.ts +290 -0
- package/src/server.ts +1894 -0
- package/src/serverData.ts +98 -0
- package/src/structure.ts +56 -0
- package/src/wordAt.ts +49 -0
|
@@ -0,0 +1,1007 @@
|
|
|
1
|
+
export interface DecodedImage {
|
|
2
|
+
width: number;
|
|
3
|
+
height: number;
|
|
4
|
+
/** RGBA8, row-major */
|
|
5
|
+
pixels: Uint8Array;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Header parsing
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
const DDS_MAGIC = 0x20534444; // "DDS " little-endian
|
|
13
|
+
|
|
14
|
+
// DDS_PIXELFORMAT flags
|
|
15
|
+
const DDPF_ALPHAPIXELS = 0x1;
|
|
16
|
+
const DDPF_FOURCC = 0x4;
|
|
17
|
+
const DDPF_RGB = 0x40;
|
|
18
|
+
const DDPF_LUMINANCE = 0x20000;
|
|
19
|
+
|
|
20
|
+
function fourCC(a: string): number {
|
|
21
|
+
return (
|
|
22
|
+
((a.charCodeAt(0) & 0xff) |
|
|
23
|
+
((a.charCodeAt(1) & 0xff) << 8) |
|
|
24
|
+
((a.charCodeAt(2) & 0xff) << 16) |
|
|
25
|
+
((a.charCodeAt(3) & 0xff) << 24)) >>>
|
|
26
|
+
0
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const FOURCC_DXT1 = fourCC("DXT1");
|
|
31
|
+
const FOURCC_DXT2 = fourCC("DXT2");
|
|
32
|
+
const FOURCC_DXT3 = fourCC("DXT3");
|
|
33
|
+
const FOURCC_DXT4 = fourCC("DXT4");
|
|
34
|
+
const FOURCC_DXT5 = fourCC("DXT5");
|
|
35
|
+
const FOURCC_DX10 = fourCC("DX10");
|
|
36
|
+
|
|
37
|
+
// DXGI formats we care about
|
|
38
|
+
const enum Dxgi {
|
|
39
|
+
R8G8B8A8_UNORM = 28,
|
|
40
|
+
R8G8B8A8_UNORM_SRGB = 29,
|
|
41
|
+
BC1_UNORM = 71,
|
|
42
|
+
BC1_UNORM_SRGB = 72,
|
|
43
|
+
BC2_UNORM = 74,
|
|
44
|
+
BC2_UNORM_SRGB = 75,
|
|
45
|
+
BC3_UNORM = 77,
|
|
46
|
+
BC3_UNORM_SRGB = 78,
|
|
47
|
+
B8G8R8A8_UNORM = 87,
|
|
48
|
+
B8G8R8A8_UNORM_SRGB = 91,
|
|
49
|
+
BC7_UNORM = 98,
|
|
50
|
+
BC7_UNORM_SRGB = 99,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type Kind =
|
|
54
|
+
| { k: "dxt1" }
|
|
55
|
+
| { k: "dxt3" }
|
|
56
|
+
| { k: "dxt5" }
|
|
57
|
+
| { k: "bc7" }
|
|
58
|
+
| { k: "bgra8"; hasAlpha: boolean } // A8R8G8B8 / X8R8G8B8 (BGRA byte order in file)
|
|
59
|
+
| { k: "rgba8" } // DXGI R8G8B8A8
|
|
60
|
+
| { k: "rgb8" }; // 24-bit R8G8B8
|
|
61
|
+
|
|
62
|
+
interface ParsedHeader {
|
|
63
|
+
width: number;
|
|
64
|
+
height: number;
|
|
65
|
+
kind: Kind;
|
|
66
|
+
/** byte offset of the first mip level's pixel/block data */
|
|
67
|
+
dataOffset: number;
|
|
68
|
+
formatName: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Decode budget, in pixels. Every decoder allocates width*height*4 bytes of
|
|
73
|
+
* RGBA out of dimensions the file declares, so the budget has to be per pixel:
|
|
74
|
+
* a per-axis cap of 0x10000 still admits 65535x65535, which reserved 16.1 GB
|
|
75
|
+
* (measured) before the data-length check could refuse it. 4096*4096 is the cap
|
|
76
|
+
* the GUI editor's texture cache already applies (MAX_TEXTURE_PIXELS in
|
|
77
|
+
* webviews/guiEditor/textureCache.ts), only after the decode instead of before.
|
|
78
|
+
*/
|
|
79
|
+
export const MAX_DECODE_PIXELS = 4096 * 4096;
|
|
80
|
+
|
|
81
|
+
function readU32(buf: Uint8Array, off: number): number {
|
|
82
|
+
return (buf[off] | (buf[off + 1] << 8) | (buf[off + 2] << 16) | (buf[off + 3] << 24)) >>> 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function parseHeader(buf: Uint8Array): ParsedHeader | null {
|
|
86
|
+
// 4 (magic) + 124 (DDS_HEADER) = 128 bytes minimum
|
|
87
|
+
if (buf.length < 128) return null;
|
|
88
|
+
if (readU32(buf, 0) !== DDS_MAGIC) return null;
|
|
89
|
+
|
|
90
|
+
const headerSize = readU32(buf, 4);
|
|
91
|
+
if (headerSize !== 124) return null;
|
|
92
|
+
|
|
93
|
+
const height = readU32(buf, 12);
|
|
94
|
+
const width = readU32(buf, 16);
|
|
95
|
+
if (width === 0 || height === 0) return null;
|
|
96
|
+
// Throw rather than return null so ddsFormatInfo still reports the declared
|
|
97
|
+
// size (its catch branch) and the caller can say why the preview is missing.
|
|
98
|
+
if (width * height > MAX_DECODE_PIXELS) {
|
|
99
|
+
throw new Error(`image too large: ${width}x${height} exceeds ${MAX_DECODE_PIXELS} pixels`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// DDS_PIXELFORMAT starts at offset 76, size 32 bytes
|
|
103
|
+
const pfOffset = 76;
|
|
104
|
+
const pfFlags = readU32(buf, pfOffset + 4);
|
|
105
|
+
const pfFourCC = readU32(buf, pfOffset + 8);
|
|
106
|
+
const pfRgbBitCount = readU32(buf, pfOffset + 12);
|
|
107
|
+
const pfRBitMask = readU32(buf, pfOffset + 16);
|
|
108
|
+
const pfGBitMask = readU32(buf, pfOffset + 20);
|
|
109
|
+
const pfBBitMask = readU32(buf, pfOffset + 24);
|
|
110
|
+
const pfABitMask = readU32(buf, pfOffset + 28);
|
|
111
|
+
|
|
112
|
+
let dataOffset = 128;
|
|
113
|
+
let kind: Kind;
|
|
114
|
+
let formatName: string;
|
|
115
|
+
|
|
116
|
+
if (pfFlags & DDPF_FOURCC) {
|
|
117
|
+
if (pfFourCC === FOURCC_DX10) {
|
|
118
|
+
// DDS_HEADER_DXT10 (20 bytes) follows the main header
|
|
119
|
+
if (buf.length < 148) return null;
|
|
120
|
+
const dxgiFormat = readU32(buf, 128);
|
|
121
|
+
dataOffset = 148;
|
|
122
|
+
switch (dxgiFormat) {
|
|
123
|
+
case Dxgi.BC1_UNORM:
|
|
124
|
+
case Dxgi.BC1_UNORM_SRGB:
|
|
125
|
+
kind = { k: "dxt1" };
|
|
126
|
+
formatName = "BC1 (DX10)";
|
|
127
|
+
break;
|
|
128
|
+
case Dxgi.BC2_UNORM:
|
|
129
|
+
case Dxgi.BC2_UNORM_SRGB:
|
|
130
|
+
kind = { k: "dxt3" };
|
|
131
|
+
formatName = "BC2 (DX10)";
|
|
132
|
+
break;
|
|
133
|
+
case Dxgi.BC3_UNORM:
|
|
134
|
+
case Dxgi.BC3_UNORM_SRGB:
|
|
135
|
+
kind = { k: "dxt5" };
|
|
136
|
+
formatName = "BC3 (DX10)";
|
|
137
|
+
break;
|
|
138
|
+
case Dxgi.BC7_UNORM:
|
|
139
|
+
case Dxgi.BC7_UNORM_SRGB:
|
|
140
|
+
kind = { k: "bc7" };
|
|
141
|
+
formatName = "BC7 (DX10)";
|
|
142
|
+
break;
|
|
143
|
+
case Dxgi.R8G8B8A8_UNORM:
|
|
144
|
+
case Dxgi.R8G8B8A8_UNORM_SRGB:
|
|
145
|
+
kind = { k: "rgba8" };
|
|
146
|
+
formatName = "R8G8B8A8 (DX10)";
|
|
147
|
+
break;
|
|
148
|
+
case Dxgi.B8G8R8A8_UNORM:
|
|
149
|
+
case Dxgi.B8G8R8A8_UNORM_SRGB:
|
|
150
|
+
kind = { k: "bgra8", hasAlpha: true };
|
|
151
|
+
formatName = "B8G8R8A8 (DX10)";
|
|
152
|
+
break;
|
|
153
|
+
default:
|
|
154
|
+
throw new Error(`unsupported format: DXGI ${dxgiFormat}`);
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
switch (pfFourCC) {
|
|
158
|
+
case FOURCC_DXT1:
|
|
159
|
+
kind = { k: "dxt1" };
|
|
160
|
+
formatName = "DXT1";
|
|
161
|
+
break;
|
|
162
|
+
case FOURCC_DXT2:
|
|
163
|
+
case FOURCC_DXT3:
|
|
164
|
+
kind = { k: "dxt3" };
|
|
165
|
+
formatName = "DXT3";
|
|
166
|
+
break;
|
|
167
|
+
case FOURCC_DXT4:
|
|
168
|
+
case FOURCC_DXT5:
|
|
169
|
+
kind = { k: "dxt5" };
|
|
170
|
+
formatName = "DXT5";
|
|
171
|
+
break;
|
|
172
|
+
default: {
|
|
173
|
+
const tag = String.fromCharCode(
|
|
174
|
+
pfFourCC & 0xff,
|
|
175
|
+
(pfFourCC >>> 8) & 0xff,
|
|
176
|
+
(pfFourCC >>> 16) & 0xff,
|
|
177
|
+
(pfFourCC >>> 24) & 0xff
|
|
178
|
+
).replace(/[^\x20-\x7e]/g, "?");
|
|
179
|
+
throw new Error(`unsupported format: FourCC '${tag}'`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
} else if (pfFlags & DDPF_RGB) {
|
|
184
|
+
if (pfRgbBitCount === 32) {
|
|
185
|
+
// Expect BGRA byte order: R mask 0x00FF0000, G 0x0000FF00, B 0x000000FF
|
|
186
|
+
const bgra = pfRBitMask === 0x00ff0000 && pfGBitMask === 0x0000ff00 && pfBBitMask === 0x000000ff;
|
|
187
|
+
const rgba = pfRBitMask === 0x000000ff && pfGBitMask === 0x0000ff00 && pfBBitMask === 0x00ff0000;
|
|
188
|
+
if (bgra) {
|
|
189
|
+
kind = { k: "bgra8", hasAlpha: (pfFlags & DDPF_ALPHAPIXELS) !== 0 && pfABitMask !== 0 };
|
|
190
|
+
formatName = pfABitMask ? "A8R8G8B8" : "X8R8G8B8";
|
|
191
|
+
} else if (rgba) {
|
|
192
|
+
kind = { k: "rgba8" };
|
|
193
|
+
formatName = "A8B8G8R8";
|
|
194
|
+
} else {
|
|
195
|
+
throw new Error("unsupported format: 32-bit RGB with unusual masks");
|
|
196
|
+
}
|
|
197
|
+
} else if (pfRgbBitCount === 24) {
|
|
198
|
+
// 24-bit: assume R8G8B8 in the common D3D order (B,G,R bytes)
|
|
199
|
+
kind = { k: "rgb8" };
|
|
200
|
+
formatName = "R8G8B8";
|
|
201
|
+
} else {
|
|
202
|
+
throw new Error(`unsupported format: ${pfRgbBitCount}-bit RGB`);
|
|
203
|
+
}
|
|
204
|
+
} else if (pfFlags & DDPF_LUMINANCE) {
|
|
205
|
+
throw new Error("unsupported format: luminance");
|
|
206
|
+
} else {
|
|
207
|
+
throw new Error("unsupported format: unknown pixel format");
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return { width, height, kind, dataOffset, formatName };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** Cheap header peek: returns format name + dimensions, or null if not a DDS. */
|
|
214
|
+
export function ddsFormatInfo(buf: Uint8Array): { format: string; width: number; height: number } | null {
|
|
215
|
+
let parsed: ParsedHeader | null;
|
|
216
|
+
try {
|
|
217
|
+
parsed = parseHeader(buf);
|
|
218
|
+
} catch {
|
|
219
|
+
// It IS a DDS but an unsupported format; still report what we can.
|
|
220
|
+
if (buf.length >= 128 && readU32(buf, 0) === DDS_MAGIC) {
|
|
221
|
+
const height = readU32(buf, 12);
|
|
222
|
+
const width = readU32(buf, 16);
|
|
223
|
+
return { format: "unsupported", width, height };
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
if (!parsed) return null;
|
|
228
|
+
return { format: parsed.formatName, width: parsed.width, height: parsed.height };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
// Block-compression decoders
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
|
|
235
|
+
/** Decode 5-6-5 color into RGB (each 0..255). */
|
|
236
|
+
function rgb565(c: number): [number, number, number] {
|
|
237
|
+
const r = (c >> 11) & 0x1f;
|
|
238
|
+
const g = (c >> 5) & 0x3f;
|
|
239
|
+
const b = c & 0x1f;
|
|
240
|
+
return [(r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2)];
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function putPixel(
|
|
244
|
+
out: Uint8Array,
|
|
245
|
+
width: number,
|
|
246
|
+
height: number,
|
|
247
|
+
px: number,
|
|
248
|
+
py: number,
|
|
249
|
+
r: number,
|
|
250
|
+
g: number,
|
|
251
|
+
b: number,
|
|
252
|
+
a: number
|
|
253
|
+
): void {
|
|
254
|
+
if (px >= width || py >= height) return;
|
|
255
|
+
const o = (py * width + px) * 4;
|
|
256
|
+
out[o] = r;
|
|
257
|
+
out[o + 1] = g;
|
|
258
|
+
out[o + 2] = b;
|
|
259
|
+
out[o + 3] = a;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function decodeDxt1(buf: Uint8Array, off: number, width: number, height: number): Uint8Array {
|
|
263
|
+
const blocksX = Math.ceil(width / 4);
|
|
264
|
+
const blocksY = Math.ceil(height / 4);
|
|
265
|
+
const needed = off + blocksX * blocksY * 8;
|
|
266
|
+
// Refuse before allocating: a truncated file must not reserve the full RGBA
|
|
267
|
+
// buffer its header claims.
|
|
268
|
+
if (needed > buf.length) throw new Error("truncated DXT1 data");
|
|
269
|
+
const out = new Uint8Array(width * height * 4);
|
|
270
|
+
|
|
271
|
+
let p = off;
|
|
272
|
+
for (let by = 0; by < blocksY; by++) {
|
|
273
|
+
for (let bx = 0; bx < blocksX; bx++) {
|
|
274
|
+
const c0 = buf[p] | (buf[p + 1] << 8);
|
|
275
|
+
const c1 = buf[p + 2] | (buf[p + 3] << 8);
|
|
276
|
+
const bits = buf[p + 4] | (buf[p + 5] << 8) | (buf[p + 6] << 16) | (buf[p + 7] << 24);
|
|
277
|
+
p += 8;
|
|
278
|
+
|
|
279
|
+
const [r0, g0, b0] = rgb565(c0);
|
|
280
|
+
const [r1, g1, b1] = rgb565(c1);
|
|
281
|
+
const colors: [number, number, number, number][] = [
|
|
282
|
+
[r0, g0, b0, 255],
|
|
283
|
+
[r1, g1, b1, 255],
|
|
284
|
+
[0, 0, 0, 255],
|
|
285
|
+
[0, 0, 0, 255],
|
|
286
|
+
];
|
|
287
|
+
if (c0 > c1) {
|
|
288
|
+
colors[2] = [(2 * r0 + r1) / 3, (2 * g0 + g1) / 3, (2 * b0 + b1) / 3, 255];
|
|
289
|
+
colors[3] = [(r0 + 2 * r1) / 3, (g0 + 2 * g1) / 3, (b0 + 2 * b1) / 3, 255];
|
|
290
|
+
} else {
|
|
291
|
+
colors[2] = [(r0 + r1) / 2, (g0 + g1) / 2, (b0 + b1) / 2, 255];
|
|
292
|
+
colors[3] = [0, 0, 0, 0]; // transparent
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
for (let i = 0; i < 16; i++) {
|
|
296
|
+
const idx = (bits >>> (i * 2)) & 0x3;
|
|
297
|
+
const [r, g, b, a] = colors[idx];
|
|
298
|
+
putPixel(out, width, height, bx * 4 + (i & 3), by * 4 + (i >> 2), r | 0, g | 0, b | 0, a);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return out;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** Shared BC color block for DXT3/DXT5 (4-color mode, no 1-bit alpha). */
|
|
306
|
+
function decodeColorBlock565(buf: Uint8Array, p: number, outColors: [number, number, number][]): void {
|
|
307
|
+
const c0 = buf[p] | (buf[p + 1] << 8);
|
|
308
|
+
const c1 = buf[p + 2] | (buf[p + 3] << 8);
|
|
309
|
+
const [r0, g0, b0] = rgb565(c0);
|
|
310
|
+
const [r1, g1, b1] = rgb565(c1);
|
|
311
|
+
outColors[0] = [r0, g0, b0];
|
|
312
|
+
outColors[1] = [r1, g1, b1];
|
|
313
|
+
outColors[2] = [(2 * r0 + r1) / 3, (2 * g0 + g1) / 3, (2 * b0 + b1) / 3];
|
|
314
|
+
outColors[3] = [(r0 + 2 * r1) / 3, (g0 + 2 * g1) / 3, (b0 + 2 * b1) / 3];
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function decodeDxt3(buf: Uint8Array, off: number, width: number, height: number): Uint8Array {
|
|
318
|
+
const blocksX = Math.ceil(width / 4);
|
|
319
|
+
const blocksY = Math.ceil(height / 4);
|
|
320
|
+
if (off + blocksX * blocksY * 16 > buf.length) throw new Error("truncated DXT3 data");
|
|
321
|
+
const out = new Uint8Array(width * height * 4);
|
|
322
|
+
|
|
323
|
+
const colors: [number, number, number][] = [
|
|
324
|
+
[0, 0, 0],
|
|
325
|
+
[0, 0, 0],
|
|
326
|
+
[0, 0, 0],
|
|
327
|
+
[0, 0, 0],
|
|
328
|
+
];
|
|
329
|
+
let p = off;
|
|
330
|
+
for (let by = 0; by < blocksY; by++) {
|
|
331
|
+
for (let bx = 0; bx < blocksX; bx++) {
|
|
332
|
+
// 8 bytes explicit 4-bit alpha, then 8 bytes color block
|
|
333
|
+
const alphaLo = buf[p] | (buf[p + 1] << 8) | (buf[p + 2] << 16) | (buf[p + 3] << 24);
|
|
334
|
+
const alphaHi = buf[p + 4] | (buf[p + 5] << 8) | (buf[p + 6] << 16) | (buf[p + 7] << 24);
|
|
335
|
+
decodeColorBlock565(buf, p + 8, colors);
|
|
336
|
+
const bits = buf[p + 12] | (buf[p + 13] << 8) | (buf[p + 14] << 16) | (buf[p + 15] << 24);
|
|
337
|
+
p += 16;
|
|
338
|
+
|
|
339
|
+
for (let i = 0; i < 16; i++) {
|
|
340
|
+
const idx = (bits >>> (i * 2)) & 0x3;
|
|
341
|
+
const [r, g, b] = colors[idx];
|
|
342
|
+
// 4-bit alpha nibble
|
|
343
|
+
let a4: number;
|
|
344
|
+
if (i < 8) a4 = (alphaLo >>> (i * 4)) & 0xf;
|
|
345
|
+
else a4 = (alphaHi >>> ((i - 8) * 4)) & 0xf;
|
|
346
|
+
const a = (a4 << 4) | a4;
|
|
347
|
+
putPixel(out, width, height, bx * 4 + (i & 3), by * 4 + (i >> 2), r | 0, g | 0, b | 0, a);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return out;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function decodeDxt5(buf: Uint8Array, off: number, width: number, height: number): Uint8Array {
|
|
355
|
+
const blocksX = Math.ceil(width / 4);
|
|
356
|
+
const blocksY = Math.ceil(height / 4);
|
|
357
|
+
if (off + blocksX * blocksY * 16 > buf.length) throw new Error("truncated DXT5 data");
|
|
358
|
+
const out = new Uint8Array(width * height * 4);
|
|
359
|
+
|
|
360
|
+
const colors: [number, number, number][] = [
|
|
361
|
+
[0, 0, 0],
|
|
362
|
+
[0, 0, 0],
|
|
363
|
+
[0, 0, 0],
|
|
364
|
+
[0, 0, 0],
|
|
365
|
+
];
|
|
366
|
+
const alpha = new Uint8Array(8);
|
|
367
|
+
let p = off;
|
|
368
|
+
for (let by = 0; by < blocksY; by++) {
|
|
369
|
+
for (let bx = 0; bx < blocksX; bx++) {
|
|
370
|
+
const a0 = buf[p];
|
|
371
|
+
const a1 = buf[p + 1];
|
|
372
|
+
alpha[0] = a0;
|
|
373
|
+
alpha[1] = a1;
|
|
374
|
+
if (a0 > a1) {
|
|
375
|
+
alpha[2] = (6 * a0 + 1 * a1) / 7;
|
|
376
|
+
alpha[3] = (5 * a0 + 2 * a1) / 7;
|
|
377
|
+
alpha[4] = (4 * a0 + 3 * a1) / 7;
|
|
378
|
+
alpha[5] = (3 * a0 + 4 * a1) / 7;
|
|
379
|
+
alpha[6] = (2 * a0 + 5 * a1) / 7;
|
|
380
|
+
alpha[7] = (1 * a0 + 6 * a1) / 7;
|
|
381
|
+
} else {
|
|
382
|
+
alpha[2] = (4 * a0 + 1 * a1) / 5;
|
|
383
|
+
alpha[3] = (3 * a0 + 2 * a1) / 5;
|
|
384
|
+
alpha[4] = (2 * a0 + 3 * a1) / 5;
|
|
385
|
+
alpha[5] = (1 * a0 + 4 * a1) / 5;
|
|
386
|
+
alpha[6] = 0;
|
|
387
|
+
alpha[7] = 255;
|
|
388
|
+
}
|
|
389
|
+
// 6 bytes = 48 bits of 3-bit alpha indices
|
|
390
|
+
const aLo = buf[p + 2] | (buf[p + 3] << 8) | (buf[p + 4] << 16); // 24 bits
|
|
391
|
+
const aHi = buf[p + 5] | (buf[p + 6] << 8) | (buf[p + 7] << 16); // 24 bits
|
|
392
|
+
|
|
393
|
+
decodeColorBlock565(buf, p + 8, colors);
|
|
394
|
+
const bits = buf[p + 12] | (buf[p + 13] << 8) | (buf[p + 14] << 16) | (buf[p + 15] << 24);
|
|
395
|
+
p += 16;
|
|
396
|
+
|
|
397
|
+
for (let i = 0; i < 16; i++) {
|
|
398
|
+
const idx = (bits >>> (i * 2)) & 0x3;
|
|
399
|
+
const [r, g, b] = colors[idx];
|
|
400
|
+
let aIdx: number;
|
|
401
|
+
if (i < 8) aIdx = (aLo >>> (i * 3)) & 0x7;
|
|
402
|
+
else aIdx = (aHi >>> ((i - 8) * 3)) & 0x7;
|
|
403
|
+
putPixel(out, width, height, bx * 4 + (i & 3), by * 4 + (i >> 2), r | 0, g | 0, b | 0, alpha[aIdx]);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return out;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// ---------------------------------------------------------------------------
|
|
411
|
+
// BC7 decoder (full 8-mode)
|
|
412
|
+
// ---------------------------------------------------------------------------
|
|
413
|
+
|
|
414
|
+
// Partition tables (BC7 spec). 2-subset: 64 patterns; 3-subset: 64 patterns.
|
|
415
|
+
// Each entry is 16 subset indices (one per texel in row-major 4x4).
|
|
416
|
+
const BC7_PARTITIONS_2: number[][] = [
|
|
417
|
+
[0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1],
|
|
418
|
+
[0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1],
|
|
419
|
+
[0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1],
|
|
420
|
+
[0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1],
|
|
421
|
+
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1],
|
|
422
|
+
[0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1],
|
|
423
|
+
[0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1],
|
|
424
|
+
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1],
|
|
425
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1],
|
|
426
|
+
[0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
|
427
|
+
[0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1],
|
|
428
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1],
|
|
429
|
+
[0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
|
430
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
|
|
431
|
+
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
|
432
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1],
|
|
433
|
+
[0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1],
|
|
434
|
+
[0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
435
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0],
|
|
436
|
+
[0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0],
|
|
437
|
+
[0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
438
|
+
[0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0],
|
|
439
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0],
|
|
440
|
+
[0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1],
|
|
441
|
+
[0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
|
|
442
|
+
[0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0],
|
|
443
|
+
[0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0],
|
|
444
|
+
[0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0],
|
|
445
|
+
[0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0],
|
|
446
|
+
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
|
447
|
+
[0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0],
|
|
448
|
+
[0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0],
|
|
449
|
+
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
|
|
450
|
+
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1],
|
|
451
|
+
[0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0],
|
|
452
|
+
[0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0],
|
|
453
|
+
[0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0],
|
|
454
|
+
[0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0],
|
|
455
|
+
[0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1],
|
|
456
|
+
[0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1],
|
|
457
|
+
[0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0],
|
|
458
|
+
[0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0],
|
|
459
|
+
[0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0],
|
|
460
|
+
[0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0],
|
|
461
|
+
[0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0],
|
|
462
|
+
[0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1],
|
|
463
|
+
[0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1],
|
|
464
|
+
[0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0],
|
|
465
|
+
[0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0],
|
|
466
|
+
[0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0],
|
|
467
|
+
[0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0],
|
|
468
|
+
[0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0],
|
|
469
|
+
[0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1],
|
|
470
|
+
[0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1],
|
|
471
|
+
[0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0],
|
|
472
|
+
[0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0],
|
|
473
|
+
[0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1],
|
|
474
|
+
[0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1],
|
|
475
|
+
[0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1],
|
|
476
|
+
[0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1],
|
|
477
|
+
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1],
|
|
478
|
+
[0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
|
|
479
|
+
[0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0],
|
|
480
|
+
[0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1],
|
|
481
|
+
];
|
|
482
|
+
|
|
483
|
+
const BC7_PARTITIONS_3: number[][] = [
|
|
484
|
+
[0, 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 1, 2, 2, 2, 2],
|
|
485
|
+
[0, 0, 0, 1, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 2, 1],
|
|
486
|
+
[0, 0, 0, 0, 2, 0, 0, 1, 2, 2, 1, 1, 2, 2, 1, 1],
|
|
487
|
+
[0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 1, 0, 1, 1, 1],
|
|
488
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2],
|
|
489
|
+
[0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 2, 0, 0, 2, 2],
|
|
490
|
+
[0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1],
|
|
491
|
+
[0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1],
|
|
492
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2],
|
|
493
|
+
[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2],
|
|
494
|
+
[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2],
|
|
495
|
+
[0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2],
|
|
496
|
+
[0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2],
|
|
497
|
+
[0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2],
|
|
498
|
+
[0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 2, 2, 1, 2, 2, 2],
|
|
499
|
+
[0, 0, 1, 1, 2, 0, 0, 1, 2, 2, 0, 0, 2, 2, 2, 0],
|
|
500
|
+
[0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 2, 2],
|
|
501
|
+
[0, 1, 1, 1, 0, 0, 1, 1, 2, 0, 0, 1, 2, 2, 0, 0],
|
|
502
|
+
[0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2],
|
|
503
|
+
[0, 0, 2, 2, 0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 1, 1],
|
|
504
|
+
[0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2],
|
|
505
|
+
[0, 0, 0, 1, 0, 0, 0, 1, 2, 2, 2, 1, 2, 2, 2, 1],
|
|
506
|
+
[0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 0, 1, 2, 2],
|
|
507
|
+
[0, 0, 0, 0, 1, 1, 0, 0, 2, 2, 1, 0, 2, 2, 1, 0],
|
|
508
|
+
[0, 1, 2, 2, 0, 1, 2, 2, 0, 0, 1, 1, 0, 0, 0, 0],
|
|
509
|
+
[0, 0, 1, 2, 0, 0, 1, 2, 1, 1, 2, 2, 2, 2, 2, 2],
|
|
510
|
+
[0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1, 0, 1, 1, 0],
|
|
511
|
+
[0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1],
|
|
512
|
+
[0, 0, 2, 2, 1, 1, 0, 2, 1, 1, 0, 2, 0, 0, 2, 2],
|
|
513
|
+
[0, 1, 1, 0, 0, 1, 1, 0, 2, 0, 0, 2, 2, 2, 2, 2],
|
|
514
|
+
[0, 0, 1, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 0, 1, 1],
|
|
515
|
+
[0, 0, 0, 0, 2, 0, 0, 0, 2, 2, 1, 1, 2, 2, 2, 1],
|
|
516
|
+
[0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 2, 2, 2],
|
|
517
|
+
[0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 2, 0, 0, 1, 1],
|
|
518
|
+
[0, 0, 1, 1, 0, 0, 1, 2, 0, 0, 2, 2, 0, 2, 2, 2],
|
|
519
|
+
[0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0],
|
|
520
|
+
[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0],
|
|
521
|
+
[0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0],
|
|
522
|
+
[0, 1, 2, 0, 2, 0, 1, 2, 1, 2, 0, 1, 0, 1, 2, 0],
|
|
523
|
+
[0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2, 0, 0, 1, 1],
|
|
524
|
+
[0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1],
|
|
525
|
+
[0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2],
|
|
526
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1],
|
|
527
|
+
[0, 0, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2, 1, 1, 2, 2],
|
|
528
|
+
[0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 2, 2, 0, 0, 1, 1],
|
|
529
|
+
[0, 2, 2, 0, 1, 2, 2, 1, 0, 2, 2, 0, 1, 2, 2, 1],
|
|
530
|
+
[0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 1],
|
|
531
|
+
[0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1],
|
|
532
|
+
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2],
|
|
533
|
+
[0, 2, 2, 2, 0, 1, 1, 1, 0, 2, 2, 2, 0, 1, 1, 1],
|
|
534
|
+
[0, 0, 0, 2, 1, 1, 1, 2, 0, 0, 0, 2, 1, 1, 1, 2],
|
|
535
|
+
[0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2],
|
|
536
|
+
[0, 2, 2, 2, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2],
|
|
537
|
+
[0, 0, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2],
|
|
538
|
+
[0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 2, 2],
|
|
539
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 1, 2],
|
|
540
|
+
[0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2],
|
|
541
|
+
[0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 2],
|
|
542
|
+
[0, 0, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2],
|
|
543
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2],
|
|
544
|
+
[0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1],
|
|
545
|
+
[0, 2, 2, 2, 1, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 2],
|
|
546
|
+
[0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
|
|
547
|
+
[0, 1, 1, 1, 2, 0, 1, 1, 2, 2, 0, 1, 2, 2, 2, 0],
|
|
548
|
+
];
|
|
549
|
+
|
|
550
|
+
// Anchor index tables (index that has an implicit high bit = 0).
|
|
551
|
+
const BC7_ANCHOR_2: number[] = [
|
|
552
|
+
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 8, 2, 2, 8, 8, 15, 2, 8, 2, 2, 8, 8,
|
|
553
|
+
2, 2, 15, 15, 6, 8, 2, 8, 15, 15, 2, 8, 2, 2, 2, 15, 15, 6, 6, 2, 6, 8, 15, 15, 2, 2, 15, 15, 15, 15, 15, 2,
|
|
554
|
+
2, 15,
|
|
555
|
+
];
|
|
556
|
+
const BC7_ANCHOR_3_1: number[] = [
|
|
557
|
+
3, 3, 15, 15, 8, 3, 15, 15, 8, 8, 6, 6, 6, 5, 3, 3, 3, 3, 8, 15, 3, 3, 6, 10, 5, 8, 8, 6, 8, 5, 15, 15, 8,
|
|
558
|
+
15, 3, 5, 6, 10, 8, 15, 15, 3, 15, 5, 15, 15, 15, 15, 3, 15, 5, 5, 5, 8, 5, 10, 5, 10, 8, 13, 15, 12, 3, 3,
|
|
559
|
+
];
|
|
560
|
+
const BC7_ANCHOR_3_2: number[] = [
|
|
561
|
+
15, 8, 8, 3, 15, 15, 3, 8, 15, 15, 15, 15, 15, 15, 15, 8, 15, 8, 15, 3, 15, 8, 15, 8, 3, 15, 6, 10, 15, 15,
|
|
562
|
+
10, 8, 15, 3, 15, 10, 10, 8, 9, 10, 6, 15, 8, 15, 3, 6, 6, 8, 15, 3, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
|
|
563
|
+
3, 15, 15, 8,
|
|
564
|
+
];
|
|
565
|
+
|
|
566
|
+
interface Bc7ModeInfo {
|
|
567
|
+
subsets: number;
|
|
568
|
+
partitionBits: number;
|
|
569
|
+
rotationBits: number;
|
|
570
|
+
idxModeBits: number;
|
|
571
|
+
colorBits: number;
|
|
572
|
+
alphaBits: number;
|
|
573
|
+
endpointPBits: number; // per-endpoint p-bit
|
|
574
|
+
sharedPBits: number; // shared p-bit per subset (mode 1)
|
|
575
|
+
indexBits: number;
|
|
576
|
+
index2Bits: number;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Per-mode configuration (modes 0..7).
|
|
580
|
+
const BC7_MODES: Bc7ModeInfo[] = [
|
|
581
|
+
{
|
|
582
|
+
subsets: 3,
|
|
583
|
+
partitionBits: 4,
|
|
584
|
+
rotationBits: 0,
|
|
585
|
+
idxModeBits: 0,
|
|
586
|
+
colorBits: 4,
|
|
587
|
+
alphaBits: 0,
|
|
588
|
+
endpointPBits: 1,
|
|
589
|
+
sharedPBits: 0,
|
|
590
|
+
indexBits: 3,
|
|
591
|
+
index2Bits: 0,
|
|
592
|
+
}, // 0
|
|
593
|
+
{
|
|
594
|
+
subsets: 2,
|
|
595
|
+
partitionBits: 6,
|
|
596
|
+
rotationBits: 0,
|
|
597
|
+
idxModeBits: 0,
|
|
598
|
+
colorBits: 6,
|
|
599
|
+
alphaBits: 0,
|
|
600
|
+
endpointPBits: 0,
|
|
601
|
+
sharedPBits: 1,
|
|
602
|
+
indexBits: 3,
|
|
603
|
+
index2Bits: 0,
|
|
604
|
+
}, // 1
|
|
605
|
+
{
|
|
606
|
+
subsets: 3,
|
|
607
|
+
partitionBits: 6,
|
|
608
|
+
rotationBits: 0,
|
|
609
|
+
idxModeBits: 0,
|
|
610
|
+
colorBits: 5,
|
|
611
|
+
alphaBits: 0,
|
|
612
|
+
endpointPBits: 0,
|
|
613
|
+
sharedPBits: 0,
|
|
614
|
+
indexBits: 2,
|
|
615
|
+
index2Bits: 0,
|
|
616
|
+
}, // 2
|
|
617
|
+
{
|
|
618
|
+
subsets: 2,
|
|
619
|
+
partitionBits: 6,
|
|
620
|
+
rotationBits: 0,
|
|
621
|
+
idxModeBits: 0,
|
|
622
|
+
colorBits: 7,
|
|
623
|
+
alphaBits: 0,
|
|
624
|
+
endpointPBits: 1,
|
|
625
|
+
sharedPBits: 0,
|
|
626
|
+
indexBits: 2,
|
|
627
|
+
index2Bits: 0,
|
|
628
|
+
}, // 3
|
|
629
|
+
{
|
|
630
|
+
subsets: 1,
|
|
631
|
+
partitionBits: 0,
|
|
632
|
+
rotationBits: 2,
|
|
633
|
+
idxModeBits: 1,
|
|
634
|
+
colorBits: 5,
|
|
635
|
+
alphaBits: 6,
|
|
636
|
+
endpointPBits: 0,
|
|
637
|
+
sharedPBits: 0,
|
|
638
|
+
indexBits: 2,
|
|
639
|
+
index2Bits: 3,
|
|
640
|
+
}, // 4
|
|
641
|
+
{
|
|
642
|
+
subsets: 1,
|
|
643
|
+
partitionBits: 0,
|
|
644
|
+
rotationBits: 2,
|
|
645
|
+
idxModeBits: 0,
|
|
646
|
+
colorBits: 7,
|
|
647
|
+
alphaBits: 8,
|
|
648
|
+
endpointPBits: 0,
|
|
649
|
+
sharedPBits: 0,
|
|
650
|
+
indexBits: 2,
|
|
651
|
+
index2Bits: 2,
|
|
652
|
+
}, // 5
|
|
653
|
+
{
|
|
654
|
+
subsets: 1,
|
|
655
|
+
partitionBits: 0,
|
|
656
|
+
rotationBits: 0,
|
|
657
|
+
idxModeBits: 0,
|
|
658
|
+
colorBits: 7,
|
|
659
|
+
alphaBits: 7,
|
|
660
|
+
endpointPBits: 1,
|
|
661
|
+
sharedPBits: 0,
|
|
662
|
+
indexBits: 4,
|
|
663
|
+
index2Bits: 0,
|
|
664
|
+
}, // 6
|
|
665
|
+
{
|
|
666
|
+
subsets: 2,
|
|
667
|
+
partitionBits: 6,
|
|
668
|
+
rotationBits: 0,
|
|
669
|
+
idxModeBits: 0,
|
|
670
|
+
colorBits: 5,
|
|
671
|
+
alphaBits: 5,
|
|
672
|
+
endpointPBits: 1,
|
|
673
|
+
sharedPBits: 0,
|
|
674
|
+
indexBits: 2,
|
|
675
|
+
index2Bits: 0,
|
|
676
|
+
}, // 7
|
|
677
|
+
];
|
|
678
|
+
|
|
679
|
+
/** Little-endian bit reader over a 16-byte block. */
|
|
680
|
+
class BitReader {
|
|
681
|
+
private bytes: Uint8Array;
|
|
682
|
+
private pos = 0; // bit position
|
|
683
|
+
constructor(bytes: Uint8Array, offset: number) {
|
|
684
|
+
this.bytes = bytes.subarray(offset, offset + 16);
|
|
685
|
+
}
|
|
686
|
+
read(n: number): number {
|
|
687
|
+
let v = 0;
|
|
688
|
+
for (let i = 0; i < n; i++) {
|
|
689
|
+
const bit = (this.bytes[this.pos >> 3] >> (this.pos & 7)) & 1;
|
|
690
|
+
v |= bit << i;
|
|
691
|
+
this.pos++;
|
|
692
|
+
}
|
|
693
|
+
return v >>> 0;
|
|
694
|
+
}
|
|
695
|
+
get bitPos(): number {
|
|
696
|
+
return this.pos;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function bc7Interp(e0: number, e1: number, index: number, indexBits: number): number {
|
|
701
|
+
// interpolation weights
|
|
702
|
+
const W2 = [0, 21, 43, 64];
|
|
703
|
+
const W3 = [0, 9, 18, 27, 37, 46, 55, 64];
|
|
704
|
+
const W4 = [0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64];
|
|
705
|
+
const w = indexBits === 2 ? W2[index] : indexBits === 3 ? W3[index] : W4[index];
|
|
706
|
+
return (e0 * (64 - w) + e1 * w + 32) >> 6;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
function decodeBc7Block(
|
|
710
|
+
buf: Uint8Array,
|
|
711
|
+
off: number,
|
|
712
|
+
out: Uint8Array,
|
|
713
|
+
bx: number,
|
|
714
|
+
by: number,
|
|
715
|
+
width: number,
|
|
716
|
+
height: number
|
|
717
|
+
): void {
|
|
718
|
+
const br = new BitReader(buf, off);
|
|
719
|
+
|
|
720
|
+
// Find mode: number of leading zero bits before the first 1.
|
|
721
|
+
let mode = 0;
|
|
722
|
+
while (mode < 8 && br.read(1) === 0) mode++;
|
|
723
|
+
if (mode === 8) {
|
|
724
|
+
// Invalid block -> fill transparent black
|
|
725
|
+
for (let i = 0; i < 16; i++) {
|
|
726
|
+
putPixel(out, width, height, bx * 4 + (i & 3), by * 4 + (i >> 2), 0, 0, 0, 0);
|
|
727
|
+
}
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
const info = BC7_MODES[mode];
|
|
732
|
+
const numSubsets = info.subsets;
|
|
733
|
+
|
|
734
|
+
const partition = info.partitionBits > 0 ? br.read(info.partitionBits) : 0;
|
|
735
|
+
const rotation = info.rotationBits > 0 ? br.read(info.rotationBits) : 0;
|
|
736
|
+
const idxMode = info.idxModeBits > 0 ? br.read(info.idxModeBits) : 0;
|
|
737
|
+
|
|
738
|
+
const numEndpoints = numSubsets * 2;
|
|
739
|
+
|
|
740
|
+
// Read endpoint color components (R, G, B), then alpha if present.
|
|
741
|
+
const r: number[] = new Array(numEndpoints);
|
|
742
|
+
const g: number[] = new Array(numEndpoints);
|
|
743
|
+
const b: number[] = new Array(numEndpoints);
|
|
744
|
+
const a: number[] = new Array(numEndpoints).fill(255);
|
|
745
|
+
|
|
746
|
+
for (let i = 0; i < numEndpoints; i++) r[i] = br.read(info.colorBits);
|
|
747
|
+
for (let i = 0; i < numEndpoints; i++) g[i] = br.read(info.colorBits);
|
|
748
|
+
for (let i = 0; i < numEndpoints; i++) b[i] = br.read(info.colorBits);
|
|
749
|
+
if (info.alphaBits > 0) {
|
|
750
|
+
for (let i = 0; i < numEndpoints; i++) a[i] = br.read(info.alphaBits);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// P-bits
|
|
754
|
+
const pbits: number[] = new Array(numEndpoints).fill(0);
|
|
755
|
+
if (info.endpointPBits > 0) {
|
|
756
|
+
for (let i = 0; i < numEndpoints; i++) pbits[i] = br.read(1);
|
|
757
|
+
} else if (info.sharedPBits > 0) {
|
|
758
|
+
// one p-bit per subset, shared by its 2 endpoints
|
|
759
|
+
for (let s = 0; s < numSubsets; s++) {
|
|
760
|
+
const pb = br.read(1);
|
|
761
|
+
pbits[s * 2] = pb;
|
|
762
|
+
pbits[s * 2 + 1] = pb;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// Assemble full-precision endpoints (unquantize).
|
|
767
|
+
const colorPrec = info.colorBits + (info.endpointPBits > 0 || info.sharedPBits > 0 ? 1 : 0);
|
|
768
|
+
const alphaPrec =
|
|
769
|
+
info.alphaBits + (info.alphaBits > 0 && (info.endpointPBits > 0 || info.sharedPBits > 0) ? 1 : 0);
|
|
770
|
+
|
|
771
|
+
function unquant(val: number, bits: number, pbit: number, usePbit: boolean): number {
|
|
772
|
+
let v = val;
|
|
773
|
+
let precision = bits;
|
|
774
|
+
if (usePbit) {
|
|
775
|
+
v = (v << 1) | pbit;
|
|
776
|
+
precision += 1;
|
|
777
|
+
}
|
|
778
|
+
// shift up to 8 bits and replicate high bits
|
|
779
|
+
v = v << (8 - precision);
|
|
780
|
+
v |= v >> precision;
|
|
781
|
+
return v & 0xff;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
const usePbit = info.endpointPBits > 0 || info.sharedPBits > 0;
|
|
785
|
+
const R = new Array(numEndpoints);
|
|
786
|
+
const G = new Array(numEndpoints);
|
|
787
|
+
const B = new Array(numEndpoints);
|
|
788
|
+
const A = new Array(numEndpoints);
|
|
789
|
+
for (let i = 0; i < numEndpoints; i++) {
|
|
790
|
+
R[i] = unquant(r[i], info.colorBits, pbits[i], usePbit);
|
|
791
|
+
G[i] = unquant(g[i], info.colorBits, pbits[i], usePbit);
|
|
792
|
+
B[i] = unquant(b[i], info.colorBits, pbits[i], usePbit);
|
|
793
|
+
if (info.alphaBits > 0) {
|
|
794
|
+
A[i] = unquant(a[i], info.alphaBits, pbits[i], usePbit);
|
|
795
|
+
} else {
|
|
796
|
+
A[i] = 255;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
void colorPrec;
|
|
800
|
+
void alphaPrec;
|
|
801
|
+
|
|
802
|
+
// Determine partition indices per texel.
|
|
803
|
+
let partTable: number[];
|
|
804
|
+
if (numSubsets === 1) {
|
|
805
|
+
partTable = new Array(16).fill(0);
|
|
806
|
+
} else if (numSubsets === 2) {
|
|
807
|
+
partTable = BC7_PARTITIONS_2[partition];
|
|
808
|
+
} else {
|
|
809
|
+
partTable = BC7_PARTITIONS_3[partition];
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// Anchor indices per subset.
|
|
813
|
+
const anchors: number[] = [0];
|
|
814
|
+
if (numSubsets === 2) {
|
|
815
|
+
anchors.push(BC7_ANCHOR_2[partition]);
|
|
816
|
+
} else if (numSubsets === 3) {
|
|
817
|
+
anchors.push(BC7_ANCHOR_3_1[partition]);
|
|
818
|
+
anchors.push(BC7_ANCHOR_3_2[partition]);
|
|
819
|
+
}
|
|
820
|
+
function isAnchor(texel: number): boolean {
|
|
821
|
+
for (let s = 0; s < numSubsets; s++) if (anchors[s] === texel) return true;
|
|
822
|
+
return false;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// Read color indices (and alpha indices for modes 4/5).
|
|
826
|
+
const colorIdx = new Array(16).fill(0);
|
|
827
|
+
const alphaIdx = new Array(16).fill(0);
|
|
828
|
+
|
|
829
|
+
const primaryBits = info.indexBits;
|
|
830
|
+
for (let i = 0; i < 16; i++) {
|
|
831
|
+
// anchor texels lose the MSB
|
|
832
|
+
const bits = isAnchorForSubset(i, partTable, anchors) ? primaryBits - 1 : primaryBits;
|
|
833
|
+
colorIdx[i] = br.read(bits);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
const hasSecondary = info.index2Bits > 0;
|
|
837
|
+
if (hasSecondary) {
|
|
838
|
+
// for modes 4/5, the second index set is for the single subset, anchor = texel 0
|
|
839
|
+
for (let i = 0; i < 16; i++) {
|
|
840
|
+
const bits = i === 0 ? info.index2Bits - 1 : info.index2Bits;
|
|
841
|
+
alphaIdx[i] = br.read(bits);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
void isAnchor;
|
|
845
|
+
|
|
846
|
+
// Emit pixels.
|
|
847
|
+
for (let i = 0; i < 16; i++) {
|
|
848
|
+
const subset = partTable[i];
|
|
849
|
+
const e0 = subset * 2;
|
|
850
|
+
const e1 = subset * 2 + 1;
|
|
851
|
+
|
|
852
|
+
let outR: number;
|
|
853
|
+
let outG: number;
|
|
854
|
+
let outB: number;
|
|
855
|
+
let outA: number;
|
|
856
|
+
|
|
857
|
+
if (hasSecondary) {
|
|
858
|
+
// mode 4/5: color uses indexBits (or swapped by idxMode), alpha uses index2Bits
|
|
859
|
+
let cIdxBits = info.indexBits;
|
|
860
|
+
let aIdxBits = info.index2Bits;
|
|
861
|
+
let cIdxVal = colorIdx[i];
|
|
862
|
+
let aIdxVal = alphaIdx[i];
|
|
863
|
+
if (idxMode === 1) {
|
|
864
|
+
// swap roles
|
|
865
|
+
[cIdxBits, aIdxBits] = [aIdxBits, cIdxBits];
|
|
866
|
+
[cIdxVal, aIdxVal] = [aIdxVal, cIdxVal];
|
|
867
|
+
}
|
|
868
|
+
outR = bc7Interp(R[e0], R[e1], cIdxVal, cIdxBits);
|
|
869
|
+
outG = bc7Interp(G[e0], G[e1], cIdxVal, cIdxBits);
|
|
870
|
+
outB = bc7Interp(B[e0], B[e1], cIdxVal, cIdxBits);
|
|
871
|
+
outA = bc7Interp(A[e0], A[e1], aIdxVal, aIdxBits);
|
|
872
|
+
} else {
|
|
873
|
+
const idx = colorIdx[i];
|
|
874
|
+
outR = bc7Interp(R[e0], R[e1], idx, primaryBits);
|
|
875
|
+
outG = bc7Interp(G[e0], G[e1], idx, primaryBits);
|
|
876
|
+
outB = bc7Interp(B[e0], B[e1], idx, primaryBits);
|
|
877
|
+
outA = info.alphaBits > 0 ? bc7Interp(A[e0], A[e1], idx, primaryBits) : 255;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// Apply rotation (swaps a channel with alpha).
|
|
881
|
+
if (rotation === 1) {
|
|
882
|
+
[outA, outR] = [outR, outA];
|
|
883
|
+
} else if (rotation === 2) {
|
|
884
|
+
[outA, outG] = [outG, outA];
|
|
885
|
+
} else if (rotation === 3) {
|
|
886
|
+
[outA, outB] = [outB, outA];
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
putPixel(out, width, height, bx * 4 + (i & 3), by * 4 + (i >> 2), outR, outG, outB, outA);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/** Is texel `i` an anchor of the subset it belongs to? Anchors drop their index MSB. */
|
|
894
|
+
function isAnchorForSubset(i: number, partTable: number[], anchors: number[]): boolean {
|
|
895
|
+
const subset = partTable[i];
|
|
896
|
+
return anchors[subset] === i;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
function decodeBc7(buf: Uint8Array, off: number, width: number, height: number): Uint8Array {
|
|
900
|
+
const blocksX = Math.ceil(width / 4);
|
|
901
|
+
const blocksY = Math.ceil(height / 4);
|
|
902
|
+
if (off + blocksX * blocksY * 16 > buf.length) throw new Error("truncated BC7 data");
|
|
903
|
+
const out = new Uint8Array(width * height * 4);
|
|
904
|
+
let p = off;
|
|
905
|
+
for (let by = 0; by < blocksY; by++) {
|
|
906
|
+
for (let bx = 0; bx < blocksX; bx++) {
|
|
907
|
+
decodeBc7Block(buf, p, out, bx, by, width, height);
|
|
908
|
+
p += 16;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
return out;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// ---------------------------------------------------------------------------
|
|
915
|
+
// Uncompressed decoders
|
|
916
|
+
// ---------------------------------------------------------------------------
|
|
917
|
+
|
|
918
|
+
function decodeBgra8(
|
|
919
|
+
buf: Uint8Array,
|
|
920
|
+
off: number,
|
|
921
|
+
width: number,
|
|
922
|
+
height: number,
|
|
923
|
+
hasAlpha: boolean
|
|
924
|
+
): Uint8Array {
|
|
925
|
+
const need = off + width * height * 4;
|
|
926
|
+
if (need > buf.length) throw new Error("truncated BGRA8 data");
|
|
927
|
+
const out = new Uint8Array(width * height * 4);
|
|
928
|
+
let s = off;
|
|
929
|
+
for (let i = 0; i < width * height; i++) {
|
|
930
|
+
const bch = buf[s];
|
|
931
|
+
const gch = buf[s + 1];
|
|
932
|
+
const rch = buf[s + 2];
|
|
933
|
+
const ach = buf[s + 3];
|
|
934
|
+
s += 4;
|
|
935
|
+
const o = i * 4;
|
|
936
|
+
out[o] = rch;
|
|
937
|
+
out[o + 1] = gch;
|
|
938
|
+
out[o + 2] = bch;
|
|
939
|
+
out[o + 3] = hasAlpha ? ach : 255;
|
|
940
|
+
}
|
|
941
|
+
return out;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
function decodeRgba8(buf: Uint8Array, off: number, width: number, height: number): Uint8Array {
|
|
945
|
+
const need = off + width * height * 4;
|
|
946
|
+
if (need > buf.length) throw new Error("truncated RGBA8 data");
|
|
947
|
+
const out = new Uint8Array(width * height * 4);
|
|
948
|
+
out.set(buf.subarray(off, need));
|
|
949
|
+
return out;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
function decodeRgb8(buf: Uint8Array, off: number, width: number, height: number): Uint8Array {
|
|
953
|
+
const need = off + width * height * 3;
|
|
954
|
+
if (need > buf.length) throw new Error("truncated RGB8 data");
|
|
955
|
+
const out = new Uint8Array(width * height * 4);
|
|
956
|
+
let s = off;
|
|
957
|
+
for (let i = 0; i < width * height; i++) {
|
|
958
|
+
// D3DFMT_R8G8B8 is stored B,G,R in memory
|
|
959
|
+
const bch = buf[s];
|
|
960
|
+
const gch = buf[s + 1];
|
|
961
|
+
const rch = buf[s + 2];
|
|
962
|
+
s += 3;
|
|
963
|
+
const o = i * 4;
|
|
964
|
+
out[o] = rch;
|
|
965
|
+
out[o + 1] = gch;
|
|
966
|
+
out[o + 2] = bch;
|
|
967
|
+
out[o + 3] = 255;
|
|
968
|
+
}
|
|
969
|
+
return out;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// ---------------------------------------------------------------------------
|
|
973
|
+
// Public API
|
|
974
|
+
// ---------------------------------------------------------------------------
|
|
975
|
+
|
|
976
|
+
/** Decode the top mip / first array slice of a DDS to RGBA8. Throws on unsupported formats. */
|
|
977
|
+
export function decodeDds(buf: Uint8Array): DecodedImage {
|
|
978
|
+
const parsed = parseHeader(buf);
|
|
979
|
+
if (!parsed) throw new Error("not a valid DDS file");
|
|
980
|
+
const { width, height, kind, dataOffset } = parsed;
|
|
981
|
+
|
|
982
|
+
let pixels: Uint8Array;
|
|
983
|
+
switch (kind.k) {
|
|
984
|
+
case "dxt1":
|
|
985
|
+
pixels = decodeDxt1(buf, dataOffset, width, height);
|
|
986
|
+
break;
|
|
987
|
+
case "dxt3":
|
|
988
|
+
pixels = decodeDxt3(buf, dataOffset, width, height);
|
|
989
|
+
break;
|
|
990
|
+
case "dxt5":
|
|
991
|
+
pixels = decodeDxt5(buf, dataOffset, width, height);
|
|
992
|
+
break;
|
|
993
|
+
case "bc7":
|
|
994
|
+
pixels = decodeBc7(buf, dataOffset, width, height);
|
|
995
|
+
break;
|
|
996
|
+
case "bgra8":
|
|
997
|
+
pixels = decodeBgra8(buf, dataOffset, width, height, kind.hasAlpha);
|
|
998
|
+
break;
|
|
999
|
+
case "rgba8":
|
|
1000
|
+
pixels = decodeRgba8(buf, dataOffset, width, height);
|
|
1001
|
+
break;
|
|
1002
|
+
case "rgb8":
|
|
1003
|
+
pixels = decodeRgb8(buf, dataOffset, width, height);
|
|
1004
|
+
break;
|
|
1005
|
+
}
|
|
1006
|
+
return { width, height, pixels };
|
|
1007
|
+
}
|