@flighthq/texture-formats 0.2.1-next.451.d9a4a4e → 0.2.1-next.530.a223ca9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/byteReader.d.ts +1 -0
- package/dist/byteReader.d.ts.map +1 -1
- package/dist/byteReader.js +7 -0
- package/dist/byteReader.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/parseBasis.d.ts.map +1 -1
- package/dist/parseBasis.js +43 -15
- package/dist/parseBasis.js.map +1 -1
- package/package.json +3 -3
- package/src/byteReader.test.ts +14 -0
- package/src/parseBasis.test.ts +72 -23
- package/src/parseDds.test.ts +19 -0
- package/src/parseKtx2.test.ts +40 -0
package/dist/byteReader.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export interface ByteReader {
|
|
|
5
5
|
export declare function createByteReader(bytes: Readonly<Uint8Array>, offset?: number): ByteReader;
|
|
6
6
|
export declare function hasByteReaderBytes(reader: Readonly<ByteReader>, count: number): boolean;
|
|
7
7
|
export declare function readByteReaderU16(reader: ByteReader): number;
|
|
8
|
+
export declare function readByteReaderU24(reader: ByteReader): number;
|
|
8
9
|
export declare function readByteReaderU24BigEndian(reader: ByteReader): number;
|
|
9
10
|
export declare function readByteReaderU32(reader: ByteReader): number;
|
|
10
11
|
export declare function readByteReaderU32BigEndian(reader: ByteReader): number;
|
package/dist/byteReader.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"byteReader.d.ts","sourceRoot":"","sources":["../src/byteReader.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,SAAI,GAAG,UAAU,CAEpF;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEvF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAI5D;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAMrE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAI5D;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAIrE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAK5D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAI3D;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAEtE"}
|
|
1
|
+
{"version":3,"file":"byteReader.d.ts","sourceRoot":"","sources":["../src/byteReader.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,SAAI,GAAG,UAAU,CAEpF;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAEvF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAI5D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAM5D;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAMrE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAI5D;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAIrE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAK5D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAI3D;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAEtE"}
|
package/dist/byteReader.js
CHANGED
|
@@ -17,6 +17,13 @@ export function readByteReaderU16(reader) {
|
|
|
17
17
|
reader.offset += 2;
|
|
18
18
|
return value;
|
|
19
19
|
}
|
|
20
|
+
export function readByteReaderU24(reader) {
|
|
21
|
+
const view = reader.view;
|
|
22
|
+
const offset = reader.offset;
|
|
23
|
+
const value = view.getUint8(offset) | (view.getUint8(offset + 1) << 8) | (view.getUint8(offset + 2) << 16);
|
|
24
|
+
reader.offset += 3;
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
20
27
|
export function readByteReaderU24BigEndian(reader) {
|
|
21
28
|
const view = reader.view;
|
|
22
29
|
const offset = reader.offset;
|
package/dist/byteReader.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"byteReader.js","sourceRoot":"","sources":["../src/byteReader.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,oGAAoG;AACpG,sGAAsG;AACtG,mGAAmG;AACnG,uGAAuG;AACvG,wGAAwG;AACxG,wGAAwG;AACxG,sEAAsE;AAOtE,MAAM,UAAU,gBAAgB,CAAC,KAA2B,EAAE,MAAM,GAAG,CAAC;IACtE,OAAO,EAAE,IAAI,EAAE,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;AAC1F,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAA4B,EAAE,KAAa;IAC5E,OAAO,KAAK,IAAI,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAkB;IAClD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzD,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IACnB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,MAAkB;IAC3D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IACzB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC7B,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3G,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IACnB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAkB;IAClD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACzD,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IACnB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,MAAkB;IAC3D,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC1D,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IACnB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAkB;IAClD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACvD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;IAC5D,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IACnB,OAAO,IAAI,GAAG,aAAa,GAAG,GAAG,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAkB;IACjD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAClD,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IACnB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAkB,EAAE,KAAa;IAC9D,MAAM,CAAC,MAAM,IAAI,KAAK,CAAC;AACzB,CAAC"}
|
|
1
|
+
{"version":3,"file":"byteReader.js","sourceRoot":"","sources":["../src/byteReader.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,sBAAsB,CAAC"}
|
package/dist/parseBasis.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseBasis.d.ts","sourceRoot":"","sources":["../src/parseBasis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"parseBasis.d.ts","sourceRoot":"","sources":["../src/parseBasis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AA0BxD,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,gBAAgB,GAAG,IAAI,CAuD/E"}
|
package/dist/parseBasis.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createByteReader, hasByteReaderBytes, readByteReaderU16, readByteReaderU32, readByteReaderU8, } from './byteReader';
|
|
1
|
+
import { createByteReader, hasByteReaderBytes, readByteReaderU16, readByteReaderU24, readByteReaderU32, readByteReaderU8, } from './byteReader';
|
|
2
2
|
// Parses a Basis Universal `.basis` container into a `TextureContainer`, or returns `null` if the bytes
|
|
3
3
|
// are not a `.basis` file, are truncated, or carry an unknown texture format.
|
|
4
4
|
//
|
|
@@ -9,21 +9,25 @@ import { createByteReader, hasByteReaderBytes, readByteReaderU16, readByteReader
|
|
|
9
9
|
// compressed — transcoding ETC1S/UASTC to a GPU format is the caller's / `flight-rs`'s job.
|
|
10
10
|
//
|
|
11
11
|
// The ETC1S codebook/selector/table sections and per-slice CRCs are not read (they are inputs to the
|
|
12
|
-
// transcoder, not to locating a slice).
|
|
13
|
-
//
|
|
12
|
+
// transcoder, not to locating a slice). 2D/array images become layers, cubemap-array images become six
|
|
13
|
+
// faces per layer, and volume images become depth slices. Video frames are rejected because the common
|
|
14
|
+
// container descriptor cannot preserve their temporal/conditional-replenishment semantics truthfully.
|
|
14
15
|
export function parseBasis(bytes) {
|
|
15
16
|
if (!hasBasisSignature(bytes))
|
|
16
17
|
return null;
|
|
17
18
|
if (bytes.byteLength < basisHeaderMinSize)
|
|
18
19
|
return null;
|
|
19
20
|
const header = createByteReader(bytes, basisTotalSlicesOffset);
|
|
20
|
-
const totalSlices =
|
|
21
|
-
const totalImages =
|
|
22
|
-
const format = basisTexFormat[bytes[basisTexFormatOffset]]; // m_tex_format (offset
|
|
21
|
+
const totalSlices = readByteReaderU24(header); // m_total_slices (offset 14)
|
|
22
|
+
const totalImages = readByteReaderU24(header); // m_total_images (offset 17)
|
|
23
|
+
const format = basisTexFormat[bytes[basisTexFormatOffset]]; // m_tex_format (offset 20)
|
|
23
24
|
if (format === undefined)
|
|
24
25
|
return null;
|
|
25
26
|
if (totalSlices === 0)
|
|
26
27
|
return null;
|
|
28
|
+
const shape = getBasisTextureShape(bytes[basisTexTypeOffset], totalImages);
|
|
29
|
+
if (shape === null)
|
|
30
|
+
return null;
|
|
27
31
|
const sliceDescReader = createByteReader(bytes, basisSliceDescOffsetField);
|
|
28
32
|
const sliceDescOffset = readByteReaderU32(sliceDescReader);
|
|
29
33
|
const table = createByteReader(bytes, sliceDescOffset);
|
|
@@ -34,7 +38,7 @@ export function parseBasis(bytes) {
|
|
|
34
38
|
let baseHeight = 0;
|
|
35
39
|
let maxLevel = 0;
|
|
36
40
|
for (let slice = 0; slice < totalSlices; slice += 1) {
|
|
37
|
-
const imageIndex =
|
|
41
|
+
const imageIndex = readByteReaderU24(table);
|
|
38
42
|
const levelIndex = readByteReaderU8(table);
|
|
39
43
|
readByteReaderU8(table); // m_flags
|
|
40
44
|
const width = readByteReaderU16(table);
|
|
@@ -55,26 +59,50 @@ export function parseBasis(bytes) {
|
|
|
55
59
|
levels.push({ byteLength, byteOffset, height, width });
|
|
56
60
|
}
|
|
57
61
|
return {
|
|
58
|
-
depth:
|
|
59
|
-
faces:
|
|
62
|
+
depth: shape.depth,
|
|
63
|
+
faces: shape.faces,
|
|
60
64
|
format,
|
|
61
65
|
height: baseHeight || (levels[0]?.height ?? 0),
|
|
62
|
-
layers:
|
|
66
|
+
layers: shape.layers,
|
|
63
67
|
levels,
|
|
64
68
|
mipLevels: Math.max(1, maxLevel),
|
|
65
69
|
supercompression: 'None',
|
|
66
70
|
width: baseWidth || (levels[0]?.width ?? 0),
|
|
67
71
|
};
|
|
68
72
|
}
|
|
73
|
+
function getBasisTextureShape(textureType, totalImages) {
|
|
74
|
+
const images = Math.max(1, totalImages);
|
|
75
|
+
switch (textureType) {
|
|
76
|
+
case basisTextureType2d:
|
|
77
|
+
case basisTextureType2dArray:
|
|
78
|
+
return { depth: 1, faces: 1, layers: images };
|
|
79
|
+
case basisTextureTypeCubemapArray:
|
|
80
|
+
if (totalImages === 0 || totalImages % 6 !== 0)
|
|
81
|
+
return null;
|
|
82
|
+
return { depth: 1, faces: 6, layers: totalImages / 6 };
|
|
83
|
+
case basisTextureTypeVideoFrames:
|
|
84
|
+
return null;
|
|
85
|
+
case basisTextureTypeVolume:
|
|
86
|
+
return { depth: images, faces: 1, layers: 1 };
|
|
87
|
+
default:
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
69
91
|
function hasBasisSignature(bytes) {
|
|
70
92
|
// m_sig = 0x4273, stored little-endian: byte 0 = 0x73 ('s'), byte 1 = 0x42 ('B').
|
|
71
93
|
return bytes.byteLength >= 2 && bytes[0] === 0x73 && bytes[1] === 0x42;
|
|
72
94
|
}
|
|
73
|
-
const basisTotalSlicesOffset =
|
|
74
|
-
const basisTexFormatOffset =
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
const
|
|
95
|
+
const basisTotalSlicesOffset = 14;
|
|
96
|
+
const basisTexFormatOffset = 20;
|
|
97
|
+
const basisTexTypeOffset = 23;
|
|
98
|
+
const basisSliceDescOffsetField = 63;
|
|
99
|
+
const basisHeaderMinSize = 67; // through m_slice_desc_file_ofs (offset 63, 4 bytes)
|
|
100
|
+
const basisSliceDescSize = 23;
|
|
101
|
+
const basisTextureType2d = 0;
|
|
102
|
+
const basisTextureType2dArray = 1;
|
|
103
|
+
const basisTextureTypeCubemapArray = 2;
|
|
104
|
+
const basisTextureTypeVideoFrames = 3;
|
|
105
|
+
const basisTextureTypeVolume = 4;
|
|
78
106
|
const basisTexFormat = {
|
|
79
107
|
0: 'etc1s',
|
|
80
108
|
1: 'uastc',
|
package/dist/parseBasis.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parseBasis.js","sourceRoot":"","sources":["../src/parseBasis.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,wGAAwG;AACxG,8EAA8E;AAC9E,EAAE;AACF,uGAAuG;AACvG,oGAAoG;AACpG,sGAAsG;AACtG,iGAAiG;AACjG,4FAA4F;AAC5F,EAAE;AACF,qGAAqG;AACrG,
|
|
1
|
+
{"version":3,"file":"parseBasis.js","sourceRoot":"","sources":["../src/parseBasis.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/texture-formats",
|
|
3
|
-
"version": "0.2.1-next.
|
|
3
|
+
"version": "0.2.1-next.530.a223ca9",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/flighthq/flight.git",
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@flighthq/types": "0.2.1-next.
|
|
35
|
+
"@flighthq/types": "0.2.1-next.530.a223ca9"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"typescript": "^5.3.0"
|
|
39
39
|
},
|
|
40
|
-
"description": "Tree-shakable GPU texture-container parsers (KTX2 / DDS / Basis)
|
|
40
|
+
"description": "Tree-shakable GPU texture-container parsers (KTX2 / DDS / Basis / ATF), peer selection, and level layout — no transcode",
|
|
41
41
|
"sideEffects": false
|
|
42
42
|
}
|
package/src/byteReader.test.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
createByteReader,
|
|
5
5
|
hasByteReaderBytes,
|
|
6
6
|
readByteReaderU16,
|
|
7
|
+
readByteReaderU24,
|
|
7
8
|
readByteReaderU24BigEndian,
|
|
8
9
|
readByteReaderU32,
|
|
9
10
|
readByteReaderU32BigEndian,
|
|
@@ -43,6 +44,19 @@ describe('readByteReaderU16', () => {
|
|
|
43
44
|
});
|
|
44
45
|
});
|
|
45
46
|
|
|
47
|
+
describe('readByteReaderU24', () => {
|
|
48
|
+
it('reads a 3-byte little-endian value and advances 3', () => {
|
|
49
|
+
const reader = createByteReader(new Uint8Array([0x56, 0x34, 0x12]));
|
|
50
|
+
expect(readByteReaderU24(reader)).toBe(0x123456);
|
|
51
|
+
expect(reader.offset).toBe(3);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('reads the full 24-bit range without sign issues', () => {
|
|
55
|
+
const reader = createByteReader(new Uint8Array([0xff, 0xff, 0xff]));
|
|
56
|
+
expect(readByteReaderU24(reader)).toBe(0xffffff);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
46
60
|
describe('readByteReaderU24BigEndian', () => {
|
|
47
61
|
it('reads a 3-byte big-endian value and advances 3', () => {
|
|
48
62
|
const reader = createByteReader(new Uint8Array([0x12, 0x34, 0x56]));
|
package/src/parseBasis.test.ts
CHANGED
|
@@ -14,14 +14,21 @@ interface BasisSlice {
|
|
|
14
14
|
interface BasisOptions {
|
|
15
15
|
totalImages?: number;
|
|
16
16
|
texFormat?: number;
|
|
17
|
+
texType?: number;
|
|
17
18
|
slices: readonly BasisSlice[];
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
const basisHeaderSize = 77;
|
|
21
|
-
const basisSliceDescSize =
|
|
22
|
+
const basisSliceDescSize = 23;
|
|
23
|
+
|
|
24
|
+
function setU24(dv: DataView, offset: number, value: number): void {
|
|
25
|
+
dv.setUint8(offset, value & 0xff);
|
|
26
|
+
dv.setUint8(offset + 1, (value >>> 8) & 0xff);
|
|
27
|
+
dv.setUint8(offset + 2, (value >>> 16) & 0xff);
|
|
28
|
+
}
|
|
22
29
|
|
|
23
30
|
function buildBasis(opts: BasisOptions): Uint8Array {
|
|
24
|
-
const { totalImages = 1, texFormat = 0, slices } = opts;
|
|
31
|
+
const { totalImages = 1, texFormat = 0, texType = 0, slices } = opts;
|
|
25
32
|
const sliceTableOffset = basisHeaderSize;
|
|
26
33
|
const dataStart = sliceTableOffset + slices.length * basisSliceDescSize;
|
|
27
34
|
const total = slices.reduce((max, s) => Math.max(max, s.fileOfs + s.fileSize), dataStart);
|
|
@@ -29,18 +36,19 @@ function buildBasis(opts: BasisOptions): Uint8Array {
|
|
|
29
36
|
const dv = new DataView(bytes.buffer);
|
|
30
37
|
bytes[0] = 0x73; // 's'
|
|
31
38
|
bytes[1] = 0x42; // 'B'
|
|
32
|
-
dv
|
|
33
|
-
dv
|
|
34
|
-
bytes[
|
|
35
|
-
|
|
39
|
+
setU24(dv, 14, slices.length); // m_total_slices
|
|
40
|
+
setU24(dv, 17, totalImages); // m_total_images
|
|
41
|
+
bytes[20] = texFormat; // m_tex_format
|
|
42
|
+
bytes[23] = texType; // m_tex_type
|
|
43
|
+
dv.setUint32(63, sliceTableOffset, true); // m_slice_desc_file_ofs
|
|
36
44
|
let off = sliceTableOffset;
|
|
37
45
|
for (const slice of slices) {
|
|
38
|
-
dv
|
|
39
|
-
bytes[off +
|
|
40
|
-
dv.setUint16(off +
|
|
41
|
-
dv.setUint16(off +
|
|
42
|
-
dv.setUint32(off +
|
|
43
|
-
dv.setUint32(off +
|
|
46
|
+
setU24(dv, off, slice.imageIndex);
|
|
47
|
+
bytes[off + 3] = slice.levelIndex;
|
|
48
|
+
dv.setUint16(off + 5, slice.width, true);
|
|
49
|
+
dv.setUint16(off + 7, slice.height, true);
|
|
50
|
+
dv.setUint32(off + 13, slice.fileOfs, true);
|
|
51
|
+
dv.setUint32(off + 17, slice.fileSize, true);
|
|
44
52
|
off += basisSliceDescSize;
|
|
45
53
|
}
|
|
46
54
|
return bytes;
|
|
@@ -49,7 +57,7 @@ function buildBasis(opts: BasisOptions): Uint8Array {
|
|
|
49
57
|
describe('parseBasis', () => {
|
|
50
58
|
it('parses a single-slice ETC1S image', () => {
|
|
51
59
|
const container = parseBasis(
|
|
52
|
-
buildBasis({ slices: [{ fileOfs:
|
|
60
|
+
buildBasis({ slices: [{ fileOfs: 100, fileSize: 8, height: 4, imageIndex: 0, levelIndex: 0, width: 4 }] }),
|
|
53
61
|
);
|
|
54
62
|
expect(container).not.toBeNull();
|
|
55
63
|
expect(container!.format).toBe('etc1s');
|
|
@@ -59,28 +67,28 @@ describe('parseBasis', () => {
|
|
|
59
67
|
expect(container!.layers).toBe(1);
|
|
60
68
|
expect(container!.faces).toBe(1);
|
|
61
69
|
expect(container!.supercompression).toBe('None');
|
|
62
|
-
expect(container!.levels).toEqual([{ byteLength: 8, byteOffset:
|
|
70
|
+
expect(container!.levels).toEqual([{ byteLength: 8, byteOffset: 100, height: 4, width: 4 }]);
|
|
63
71
|
});
|
|
64
72
|
|
|
65
73
|
it('reports a mip chain from the slice level indices', () => {
|
|
66
74
|
const container = parseBasis(
|
|
67
75
|
buildBasis({
|
|
68
76
|
slices: [
|
|
69
|
-
{ fileOfs:
|
|
70
|
-
{ fileOfs:
|
|
77
|
+
{ fileOfs: 123, fileSize: 8, height: 4, imageIndex: 0, levelIndex: 0, width: 4 },
|
|
78
|
+
{ fileOfs: 131, fileSize: 4, height: 2, imageIndex: 0, levelIndex: 1, width: 2 },
|
|
71
79
|
],
|
|
72
80
|
}),
|
|
73
81
|
);
|
|
74
82
|
expect(container).not.toBeNull();
|
|
75
83
|
expect(container!.mipLevels).toBe(2);
|
|
76
84
|
expect(container!.levels.map((l) => l.width)).toEqual([4, 2]);
|
|
77
|
-
expect(container!.levels.map((l) => l.byteOffset)).toEqual([
|
|
85
|
+
expect(container!.levels.map((l) => l.byteOffset)).toEqual([123, 131]);
|
|
78
86
|
});
|
|
79
87
|
|
|
80
88
|
it('maps the UASTC texture format', () => {
|
|
81
89
|
const container = parseBasis(
|
|
82
90
|
buildBasis({
|
|
83
|
-
slices: [{ fileOfs:
|
|
91
|
+
slices: [{ fileOfs: 100, fileSize: 16, height: 4, imageIndex: 0, levelIndex: 0, width: 4 }],
|
|
84
92
|
texFormat: 1,
|
|
85
93
|
}),
|
|
86
94
|
);
|
|
@@ -91,8 +99,8 @@ describe('parseBasis', () => {
|
|
|
91
99
|
const container = parseBasis(
|
|
92
100
|
buildBasis({
|
|
93
101
|
slices: [
|
|
94
|
-
{ fileOfs:
|
|
95
|
-
{ fileOfs:
|
|
102
|
+
{ fileOfs: 123, fileSize: 8, height: 4, imageIndex: 0, levelIndex: 0, width: 4 },
|
|
103
|
+
{ fileOfs: 131, fileSize: 8, height: 4, imageIndex: 1, levelIndex: 0, width: 4 },
|
|
96
104
|
],
|
|
97
105
|
totalImages: 2,
|
|
98
106
|
}),
|
|
@@ -101,13 +109,54 @@ describe('parseBasis', () => {
|
|
|
101
109
|
expect(container!.levels).toHaveLength(2);
|
|
102
110
|
});
|
|
103
111
|
|
|
112
|
+
it('reads 24-bit image counts and slice image indices', () => {
|
|
113
|
+
const container = parseBasis(
|
|
114
|
+
buildBasis({
|
|
115
|
+
slices: [{ fileOfs: 100, fileSize: 8, height: 4, imageIndex: 70_000, levelIndex: 0, width: 4 }],
|
|
116
|
+
totalImages: 70_001,
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
expect(container).not.toBeNull();
|
|
120
|
+
expect(container!.layers).toBe(70_001);
|
|
121
|
+
expect(container!.levels).toEqual([{ byteLength: 8, byteOffset: 100, height: 4, width: 4 }]);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('preserves cubemap-array faces and layers', () => {
|
|
125
|
+
const slices: BasisSlice[] = [];
|
|
126
|
+
for (let imageIndex = 0; imageIndex < 12; imageIndex++) {
|
|
127
|
+
slices.push({ fileOfs: 353 + imageIndex * 8, fileSize: 8, height: 4, imageIndex, levelIndex: 0, width: 4 });
|
|
128
|
+
}
|
|
129
|
+
const container = parseBasis(buildBasis({ slices, texType: 2, totalImages: 12 }));
|
|
130
|
+
expect(container).not.toBeNull();
|
|
131
|
+
expect(container!.faces).toBe(6);
|
|
132
|
+
expect(container!.layers).toBe(2);
|
|
133
|
+
expect(container!.depth).toBe(1);
|
|
134
|
+
expect(container!.levels).toHaveLength(12);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('preserves volume depth and rejects temporal or malformed cube shapes', () => {
|
|
138
|
+
const slices = [
|
|
139
|
+
{ fileOfs: 146, fileSize: 8, height: 4, imageIndex: 0, levelIndex: 0, width: 4 },
|
|
140
|
+
{ fileOfs: 154, fileSize: 8, height: 4, imageIndex: 1, levelIndex: 0, width: 4 },
|
|
141
|
+
{ fileOfs: 162, fileSize: 8, height: 4, imageIndex: 2, levelIndex: 0, width: 4 },
|
|
142
|
+
];
|
|
143
|
+
const volume = parseBasis(buildBasis({ slices, texType: 4, totalImages: 3 }));
|
|
144
|
+
expect(volume).not.toBeNull();
|
|
145
|
+
expect(volume!.depth).toBe(3);
|
|
146
|
+
expect(volume!.faces).toBe(1);
|
|
147
|
+
expect(volume!.layers).toBe(1);
|
|
148
|
+
|
|
149
|
+
expect(parseBasis(buildBasis({ slices, texType: 3, totalImages: 3 }))).toBeNull();
|
|
150
|
+
expect(parseBasis(buildBasis({ slices, texType: 2, totalImages: 3 }))).toBeNull();
|
|
151
|
+
});
|
|
152
|
+
|
|
104
153
|
it('returns null for a non-Basis, truncated, or unknown-format buffer', () => {
|
|
105
154
|
expect(parseBasis(new Uint8Array([0x44, 0x44, 0x53, 0x20]))).toBeNull();
|
|
106
155
|
expect(parseBasis(new Uint8Array([0x73, 0x42, 0, 0]))).toBeNull();
|
|
107
156
|
expect(
|
|
108
157
|
parseBasis(
|
|
109
158
|
buildBasis({
|
|
110
|
-
slices: [{ fileOfs:
|
|
159
|
+
slices: [{ fileOfs: 100, fileSize: 8, height: 4, imageIndex: 0, levelIndex: 0, width: 4 }],
|
|
111
160
|
texFormat: 5,
|
|
112
161
|
}),
|
|
113
162
|
),
|
|
@@ -116,9 +165,9 @@ describe('parseBasis', () => {
|
|
|
116
165
|
|
|
117
166
|
it('returns null when the slice table runs past the buffer', () => {
|
|
118
167
|
const bytes = buildBasis({
|
|
119
|
-
slices: [{ fileOfs:
|
|
168
|
+
slices: [{ fileOfs: 100, fileSize: 8, height: 4, imageIndex: 0, levelIndex: 0, width: 4 }],
|
|
120
169
|
});
|
|
121
|
-
new DataView(bytes.buffer).setUint32(
|
|
170
|
+
new DataView(bytes.buffer).setUint32(63, 1_000_000, true); // corrupt m_slice_desc_file_ofs
|
|
122
171
|
expect(parseBasis(bytes)).toBeNull();
|
|
123
172
|
});
|
|
124
173
|
});
|
package/src/parseDds.test.ts
CHANGED
|
@@ -131,6 +131,25 @@ describe('parseDds', () => {
|
|
|
131
131
|
expect(container!.levels[0].byteOffset).toBe(148); // data follows the 20-byte DX10 header
|
|
132
132
|
});
|
|
133
133
|
|
|
134
|
+
it('lays out every DX10 array layer as an independent subresource chain', () => {
|
|
135
|
+
const container = parseDds(
|
|
136
|
+
buildDds({
|
|
137
|
+
dx10: { arraySize: 3, dxgiFormat: 98 },
|
|
138
|
+
fourCC: fourCC('DX10'),
|
|
139
|
+
height: 4,
|
|
140
|
+
pfFlags: 0x4,
|
|
141
|
+
width: 4,
|
|
142
|
+
}),
|
|
143
|
+
);
|
|
144
|
+
expect(container).not.toBeNull();
|
|
145
|
+
expect(container!.layers).toBe(3);
|
|
146
|
+
expect(container!.levels).toEqual([
|
|
147
|
+
{ byteLength: 16, byteOffset: 148, height: 4, width: 4 },
|
|
148
|
+
{ byteLength: 16, byteOffset: 164, height: 4, width: 4 },
|
|
149
|
+
{ byteLength: 16, byteOffset: 180, height: 4, width: 4 },
|
|
150
|
+
]);
|
|
151
|
+
});
|
|
152
|
+
|
|
134
153
|
it('returns null for a non-DDS, truncated, unknown-format, or volume texture', () => {
|
|
135
154
|
expect(parseDds(new Uint8Array([0xab, 0x4b, 0x54, 0x58]))).toBeNull();
|
|
136
155
|
expect(parseDds(new Uint8Array([0x44, 0x44, 0x53, 0x20, 0, 0, 0, 0]))).toBeNull();
|
package/src/parseKtx2.test.ts
CHANGED
|
@@ -74,6 +74,25 @@ describe('parseKtx2', () => {
|
|
|
74
74
|
expect(container!.levels.map((l) => l.byteOffset)).toEqual([104, 168, 232, 296, 360, 424]);
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
+
it('splits an uncompressed array level into one range per layer', () => {
|
|
78
|
+
const container = parseKtx2(
|
|
79
|
+
buildKtx2({
|
|
80
|
+
layerCount: 3,
|
|
81
|
+
levels: [{ byteLength: 192, byteOffset: 104 }],
|
|
82
|
+
vkFormat: 37,
|
|
83
|
+
width: 4,
|
|
84
|
+
height: 4,
|
|
85
|
+
}),
|
|
86
|
+
);
|
|
87
|
+
expect(container).not.toBeNull();
|
|
88
|
+
expect(container!.layers).toBe(3);
|
|
89
|
+
expect(container!.levels).toEqual([
|
|
90
|
+
{ byteLength: 64, byteOffset: 104, height: 4, width: 4 },
|
|
91
|
+
{ byteLength: 64, byteOffset: 168, height: 4, width: 4 },
|
|
92
|
+
{ byteLength: 64, byteOffset: 232, height: 4, width: 4 },
|
|
93
|
+
]);
|
|
94
|
+
});
|
|
95
|
+
|
|
77
96
|
it('reports each mip of a mip chain from the level index', () => {
|
|
78
97
|
const container = parseKtx2(
|
|
79
98
|
buildKtx2({
|
|
@@ -112,6 +131,27 @@ describe('parseKtx2', () => {
|
|
|
112
131
|
expect(container!.format).toBe('uastc');
|
|
113
132
|
});
|
|
114
133
|
|
|
134
|
+
it('preserves Zstd and ZLIB supercompressed levels as indivisible blobs', () => {
|
|
135
|
+
for (const [scheme, supercompression] of [
|
|
136
|
+
[2, 'Zstd'],
|
|
137
|
+
[3, 'ZLIB'],
|
|
138
|
+
] as const) {
|
|
139
|
+
const container = parseKtx2(
|
|
140
|
+
buildKtx2({
|
|
141
|
+
layerCount: 2,
|
|
142
|
+
levels: [{ byteLength: 48, byteOffset: 104 }],
|
|
143
|
+
scheme,
|
|
144
|
+
vkFormat: 37,
|
|
145
|
+
width: 4,
|
|
146
|
+
height: 4,
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
expect(container).not.toBeNull();
|
|
150
|
+
expect(container!.supercompression).toBe(supercompression);
|
|
151
|
+
expect(container!.levels).toEqual([{ byteLength: 48, byteOffset: 104, height: 4, width: 4 }]);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
115
155
|
it('returns null for a non-KTX2 buffer', () => {
|
|
116
156
|
expect(parseKtx2(new Uint8Array([0x44, 0x44, 0x53, 0x20]))).toBeNull();
|
|
117
157
|
});
|