@forgeax/engine-codec 0.0.0-dev.8d955ade1c79

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.
Files changed (116) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +261 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/__tests__/basis-encode-fast-preset.unit.test.d.ts +2 -0
  5. package/dist/__tests__/basis-encode-fast-preset.unit.test.d.ts.map +1 -0
  6. package/dist/__tests__/basis-encode-lifecycle.unit.test.d.ts +2 -0
  7. package/dist/__tests__/basis-encode-lifecycle.unit.test.d.ts.map +1 -0
  8. package/dist/__tests__/basis-transcoder.unit.test.d.ts +2 -0
  9. package/dist/__tests__/basis-transcoder.unit.test.d.ts.map +1 -0
  10. package/dist/__tests__/block-format.unit.test.d.ts +2 -0
  11. package/dist/__tests__/block-format.unit.test.d.ts.map +1 -0
  12. package/dist/__tests__/byte-identical-falsification.test.d.ts +2 -0
  13. package/dist/__tests__/byte-identical-falsification.test.d.ts.map +1 -0
  14. package/dist/__tests__/codec-errors.test-d.d.ts +2 -0
  15. package/dist/__tests__/codec-errors.test-d.d.ts.map +1 -0
  16. package/dist/__tests__/codec-errors.unit.test.d.ts +2 -0
  17. package/dist/__tests__/codec-errors.unit.test.d.ts.map +1 -0
  18. package/dist/__tests__/compression-field.unit.test.d.ts +19 -0
  19. package/dist/__tests__/compression-field.unit.test.d.ts.map +1 -0
  20. package/dist/__tests__/encode-pixel-limit.unit.test.d.ts +2 -0
  21. package/dist/__tests__/encode-pixel-limit.unit.test.d.ts.map +1 -0
  22. package/dist/__tests__/encode-roundtrip.unit.test.d.ts +2 -0
  23. package/dist/__tests__/encode-roundtrip.unit.test.d.ts.map +1 -0
  24. package/dist/__tests__/ktx2-basis-capability-matrix.unit.test.d.ts +2 -0
  25. package/dist/__tests__/ktx2-basis-capability-matrix.unit.test.d.ts.map +1 -0
  26. package/dist/__tests__/ktx2-errors.test.d.ts +2 -0
  27. package/dist/__tests__/ktx2-errors.test.d.ts.map +1 -0
  28. package/dist/__tests__/ktx2-parse-scheme0.test.d.ts +2 -0
  29. package/dist/__tests__/ktx2-parse-scheme0.test.d.ts.map +1 -0
  30. package/dist/__tests__/ktx2-parse-scheme2.test.d.ts +2 -0
  31. package/dist/__tests__/ktx2-parse-scheme2.test.d.ts.map +1 -0
  32. package/dist/__tests__/ktx2-scheme1-and-transcode-errors.unit.test.d.ts +2 -0
  33. package/dist/__tests__/ktx2-scheme1-and-transcode-errors.unit.test.d.ts.map +1 -0
  34. package/dist/__tests__/ktx2-to-texture.integration.test.d.ts +2 -0
  35. package/dist/__tests__/ktx2-to-texture.integration.test.d.ts.map +1 -0
  36. package/dist/__tests__/transcode-select-target.unit.test.d.ts +2 -0
  37. package/dist/__tests__/transcode-select-target.unit.test.d.ts.map +1 -0
  38. package/dist/__tests__/wasm-init-smoke.test.d.ts +2 -0
  39. package/dist/__tests__/wasm-init-smoke.test.d.ts.map +1 -0
  40. package/dist/__tests__/zstd-deterministic.unit.test.d.ts +2 -0
  41. package/dist/__tests__/zstd-deterministic.unit.test.d.ts.map +1 -0
  42. package/dist/__tests__/zstd-encode-singleton.unit.test.d.ts +2 -0
  43. package/dist/__tests__/zstd-encode-singleton.unit.test.d.ts.map +1 -0
  44. package/dist/__tests__/zstd-roundtrip.unit.test.d.ts +2 -0
  45. package/dist/__tests__/zstd-roundtrip.unit.test.d.ts.map +1 -0
  46. package/dist/__tests__/zstd-singleton.unit.test.d.ts +2 -0
  47. package/dist/__tests__/zstd-singleton.unit.test.d.ts.map +1 -0
  48. package/dist/basis-transcoder.d.ts +86 -0
  49. package/dist/basis-transcoder.d.ts.map +1 -0
  50. package/dist/block-format.d.ts +41 -0
  51. package/dist/block-format.d.ts.map +1 -0
  52. package/dist/encode/basis-encode.d.ts +68 -0
  53. package/dist/encode/basis-encode.d.ts.map +1 -0
  54. package/dist/encode/index.d.ts +10 -0
  55. package/dist/encode/index.d.ts.map +1 -0
  56. package/dist/encode/index.mjs +207 -0
  57. package/dist/encode/index.mjs.map +1 -0
  58. package/dist/encode-impl.d.ts +30 -0
  59. package/dist/encode-impl.d.ts.map +1 -0
  60. package/dist/errors.d.ts +61 -0
  61. package/dist/errors.d.ts.map +1 -0
  62. package/dist/index.d.ts +17 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.mjs +610 -0
  65. package/dist/index.mjs.map +1 -0
  66. package/dist/ktx2.d.ts +111 -0
  67. package/dist/ktx2.d.ts.map +1 -0
  68. package/dist/transcode.d.ts +55 -0
  69. package/dist/transcode.d.ts.map +1 -0
  70. package/dist/wasm/basis-types.d.ts +179 -0
  71. package/dist/wasm/basis-types.d.ts.map +1 -0
  72. package/dist/zstd.d.ts +28 -0
  73. package/dist/zstd.d.ts.map +1 -0
  74. package/package.json +71 -0
  75. package/pkg/basis_transcoder.mjs +2 -0
  76. package/pkg/basis_transcoder.wasm +0 -0
  77. package/pkg/encode/basis_encoder.mjs +2 -0
  78. package/pkg/encode/basis_encoder.wasm +0 -0
  79. package/scripts/build-wasm.mjs +370 -0
  80. package/scripts/content-key.mjs +85 -0
  81. package/scripts/ensure-wasm.mjs +38 -0
  82. package/scripts/fetch-basis.mjs +150 -0
  83. package/scripts/fetch-wasm.mjs +97 -0
  84. package/src/__tests__/basis-encode-fast-preset.unit.test.ts +204 -0
  85. package/src/__tests__/basis-encode-lifecycle.unit.test.ts +249 -0
  86. package/src/__tests__/basis-transcoder.unit.test.ts +151 -0
  87. package/src/__tests__/block-format.unit.test.ts +215 -0
  88. package/src/__tests__/byte-identical-falsification.test.ts +164 -0
  89. package/src/__tests__/codec-errors.test-d.ts +67 -0
  90. package/src/__tests__/codec-errors.unit.test.ts +128 -0
  91. package/src/__tests__/compression-field.unit.test.ts +245 -0
  92. package/src/__tests__/encode-pixel-limit.unit.test.ts +117 -0
  93. package/src/__tests__/encode-roundtrip.unit.test.ts +239 -0
  94. package/src/__tests__/ktx2-basis-capability-matrix.unit.test.ts +241 -0
  95. package/src/__tests__/ktx2-errors.test.ts +239 -0
  96. package/src/__tests__/ktx2-parse-scheme0.test.ts +263 -0
  97. package/src/__tests__/ktx2-parse-scheme2.test.ts +220 -0
  98. package/src/__tests__/ktx2-scheme1-and-transcode-errors.unit.test.ts +178 -0
  99. package/src/__tests__/ktx2-to-texture.integration.test.ts +384 -0
  100. package/src/__tests__/transcode-select-target.unit.test.ts +167 -0
  101. package/src/__tests__/wasm-init-smoke.test.ts +102 -0
  102. package/src/__tests__/zstd-deterministic.unit.test.ts +49 -0
  103. package/src/__tests__/zstd-encode-singleton.unit.test.ts +63 -0
  104. package/src/__tests__/zstd-roundtrip.unit.test.ts +87 -0
  105. package/src/__tests__/zstd-singleton.unit.test.ts +105 -0
  106. package/src/basis-transcoder.ts +315 -0
  107. package/src/block-format.ts +128 -0
  108. package/src/encode/basis-encode.ts +264 -0
  109. package/src/encode/index.ts +19 -0
  110. package/src/encode-impl.ts +103 -0
  111. package/src/errors.ts +79 -0
  112. package/src/index.ts +38 -0
  113. package/src/ktx2.ts +469 -0
  114. package/src/transcode.ts +99 -0
  115. package/src/wasm/basis-types.ts +220 -0
  116. package/src/zstd.ts +85 -0
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Hand-authored TypeScript contracts for the two self-built basis_universal
3
+ * WASM modules (see scripts/build-wasm.mjs). The .mjs/.wasm outputs are
4
+ * gitignored (zero-binary, AC-12) and produced by embind, so no generated
5
+ * .d.ts exists -- these interfaces are the typed surface M2 (transcoder glue)
6
+ * and M3 (encoder glue) import against.
7
+ *
8
+ * Scope: only the API this Loop uses (ETC1S / UASTC-LDR / UASTC-HDR encode +
9
+ * KTX2 transcode). ASTC-HDR-6x6 / PVRTC / DDS / .basis-file paths present in
10
+ * the embind surface are intentionally not declared (OOS).
11
+ *
12
+ * Signatures mirror vendor/basis/webgl/transcoder/basis_wrappers.cpp
13
+ * EMSCRIPTEN_BINDINGS(basis) at the pinned commit (D-2).
14
+ */
15
+
16
+ /**
17
+ * An embind `enum_<T>` value. Accessing `Module.enumName.MEMBER` yields one of
18
+ * these; `.value` is the underlying integer passed back into WASM calls.
19
+ */
20
+ export interface BasisEnumValue {
21
+ readonly value: number;
22
+ }
23
+
24
+ /**
25
+ * Transcode target formats (embind `transcoder_texture_format`). Only the
26
+ * members this Loop's priority chain selects are named; the enum object carries
27
+ * the full upstream set at runtime.
28
+ */
29
+ export interface TranscoderTextureFormatEnum {
30
+ readonly cTFBC1_RGB: BasisEnumValue;
31
+ readonly cTFBC3_RGBA: BasisEnumValue;
32
+ readonly cTFBC4_R: BasisEnumValue;
33
+ readonly cTFBC5_RG: BasisEnumValue;
34
+ readonly cTFBC7_RGBA: BasisEnumValue;
35
+ readonly cTFBC6H: BasisEnumValue;
36
+ readonly cTFETC1_RGB: BasisEnumValue;
37
+ readonly cTFETC2_RGBA: BasisEnumValue;
38
+ readonly cTFASTC_4x4_RGBA: BasisEnumValue;
39
+ readonly cTFASTC_HDR_4x4_RGBA: BasisEnumValue;
40
+ readonly cTFRGBA32: BasisEnumValue;
41
+ readonly cTFRGBA_HALF: BasisEnumValue;
42
+ }
43
+
44
+ /**
45
+ * Source encode formats (embind `basis_tex_format`). Only the three delivery
46
+ * encodings this Loop produces are named.
47
+ */
48
+ export interface BasisTexFormatEnum {
49
+ readonly cETC1S: BasisEnumValue;
50
+ readonly cUASTC_LDR_4x4: BasisEnumValue;
51
+ readonly cUASTC_HDR_4x4: BasisEnumValue;
52
+ }
53
+
54
+ /**
55
+ * A KTX2 container opened for inspection + transcoding. Construct with the raw
56
+ * container bytes; `close()` frees the WASM-side copy.
57
+ */
58
+ export interface KTX2File {
59
+ isValid(): boolean;
60
+ close(): void;
61
+ getWidth(): number;
62
+ getHeight(): number;
63
+ getLevels(): number;
64
+ getLayers(): number;
65
+ getFaces(): number;
66
+ getBlockWidth(): number;
67
+ getBlockHeight(): number;
68
+ getHasAlpha(): number;
69
+ /** DFD color model: 166 = UASTC-LDR/ETC1S, 167 = UASTC-HDR (D-3). */
70
+ getDFDColorModel(): number;
71
+ isSRGB(): boolean;
72
+ isUASTC_LDR_4x4(): boolean;
73
+ isETC1S(): boolean;
74
+ isHDR(): boolean;
75
+ isHDR4x4(): boolean;
76
+ isLDR(): boolean;
77
+ getBasisTexFormat(): number;
78
+ /** width/height/blocks for one (level, layer, face). */
79
+ getImageLevelInfo(level: number, layer: number, face: number): KTX2ImageLevelInfo;
80
+ getImageTranscodedSizeInBytes(level: number, layer: number, face: number, format: number): number;
81
+ /** Must be called once before any transcodeImage. Returns nonzero on success. */
82
+ startTranscoding(): number;
83
+ /**
84
+ * Transcode one image into `dst`. Returns nonzero on success.
85
+ * channel0/channel1 select source channels for R/RG targets (-1 = default).
86
+ */
87
+ transcodeImage(
88
+ dst: Uint8Array,
89
+ level: number,
90
+ layer: number,
91
+ face: number,
92
+ format: number,
93
+ getAlphaForOpaqueFormats: number,
94
+ channel0: number,
95
+ channel1: number,
96
+ ): number;
97
+ }
98
+
99
+ export interface KTX2ImageLevelInfo {
100
+ readonly origWidth: number;
101
+ readonly origHeight: number;
102
+ readonly width: number;
103
+ readonly height: number;
104
+ readonly numBlocksX: number;
105
+ readonly numBlocksY: number;
106
+ }
107
+
108
+ export interface KTX2FileConstructor {
109
+ new (data: Uint8Array): KTX2File;
110
+ }
111
+
112
+ /** A raw `.basis` source opened by the build-time BasisFile arm. */
113
+ export interface BasisFile {
114
+ close(): void;
115
+ getNumImages(): number;
116
+ getNumLevels(imageIndex: number): number;
117
+ getImageWidth(imageIndex: number, levelIndex: number): number;
118
+ getImageHeight(imageIndex: number, levelIndex: number): number;
119
+ getBasisTexFormat(): number;
120
+ getHasAlpha(): number;
121
+ startTranscoding(): number;
122
+ getImageTranscodedSizeInBytes(imageIndex: number, levelIndex: number, format: number): number;
123
+ transcodeImage(
124
+ dst: Uint8Array,
125
+ imageIndex: number,
126
+ levelIndex: number,
127
+ format: number,
128
+ decodeFlags: number,
129
+ ): number;
130
+ }
131
+
132
+ export interface BasisFileConstructor {
133
+ new (data: Uint8Array): BasisFile;
134
+ }
135
+
136
+ /**
137
+ * The transcoder WASM module (runtime-safe entry). Slim build: no encoder.
138
+ */
139
+ export interface BasisTranscoderModule {
140
+ /** Global one-time init; must run before constructing any KTX2File. */
141
+ initializeBasis(): void;
142
+ readonly transcoder_texture_format: TranscoderTextureFormatEnum;
143
+ readonly basis_tex_format: BasisTexFormatEnum;
144
+ readonly KTX2File: KTX2FileConstructor;
145
+ readonly BasisFile?: BasisFileConstructor;
146
+ /** Bytes per block (compressed) or per pixel (uncompressed) for a format. */
147
+ getBytesPerBlockOrPixel(format: number): number;
148
+ }
149
+
150
+ /**
151
+ * A one-shot texture encoder. `delete()` frees the WASM-side object.
152
+ */
153
+ export interface BasisEncoder {
154
+ /** img_type: 0 = raw RGBA bytes, 1 = PNG bytes. */
155
+ setSliceSourceImage(
156
+ sliceIndex: number,
157
+ image: Uint8Array,
158
+ width: number,
159
+ height: number,
160
+ imgType: number,
161
+ ): boolean;
162
+ /**
163
+ * HDR source (RGBA float / half) for UASTC-HDR encode. `imgType` 0 =
164
+ * cHITRGBAHalfFloat (rgba16float bytes), 1 = cHITRGBAFloat (rgba32float).
165
+ * `ldrToHdrNitMultiplier` scales an upconverted LDR source (unused when the
166
+ * source is already HDR; pass 1.0).
167
+ */
168
+ setSliceSourceImageHDR(
169
+ sliceIndex: number,
170
+ image: Uint8Array,
171
+ width: number,
172
+ height: number,
173
+ imgType: number,
174
+ ldrSrgbToLinear: boolean,
175
+ ldrToHdrNitMultiplier: number,
176
+ ): boolean;
177
+ setFormatMode(texFormat: number): void;
178
+ setCreateKTX2File(flag: boolean): void;
179
+ setKTX2UASTCSupercompression(flag: boolean): void;
180
+ /** Write the sRGB transfer function into the KTX2 header + DFD + basis file. */
181
+ setKTX2AndBasisSRGBTransferFunc(flag: boolean): void;
182
+ setPerceptual(flag: boolean): void;
183
+ setMipGen(flag: boolean): void;
184
+ setQualityLevel(quality: number): void;
185
+ /**
186
+ * ETC1S perf-vs-size effort level, range [0,6] (higher = slower / smaller).
187
+ * Orthogonal to `setQualityLevel`; a lower value only trades file size for
188
+ * encode speed (R-9 fast preset).
189
+ */
190
+ setETC1SCompressionLevel(compLevel: number): void;
191
+ /**
192
+ * UASTC-LDR pack flags; the low nibble is the pack level
193
+ * `cPackUASTCLevelFastest`(0)..`cPackUASTCLevelVerySlow`(4) (R-9 fast preset).
194
+ */
195
+ setPackUASTCFlags(packFlags: number): void;
196
+ /** UASTC-HDR search quality level, range [0,4] (higher = slower). */
197
+ setUASTCHDRQualityLevel(level: number): void;
198
+ /** Enable/disable threading; pass (false, 0) for deterministic single-thread encode. */
199
+ controlThreading(enable: boolean, numExtraWorkerThreads: number): void;
200
+ /** Encodes into `dst`; returns the byte length written (0 on failure). */
201
+ encode(dst: Uint8Array): number;
202
+ delete(): void;
203
+ }
204
+
205
+ export interface BasisEncoderConstructor {
206
+ new (): BasisEncoder;
207
+ }
208
+
209
+ /**
210
+ * The encoder WASM module (build-time entry; also carries the transcoder).
211
+ */
212
+ export interface BasisEncoderModule {
213
+ initializeBasis(): void;
214
+ readonly basis_tex_format: BasisTexFormatEnum;
215
+ readonly BasisEncoder: BasisEncoderConstructor;
216
+ readonly KTX2File: KTX2FileConstructor;
217
+ }
218
+
219
+ /** The emscripten MODULARIZE factory default export of each .mjs glue module. */
220
+ export type BasisModuleFactory<TModule> = (overrides?: Record<string, unknown>) => Promise<TModule>;
package/src/zstd.ts ADDED
@@ -0,0 +1,85 @@
1
+ import type { CodecResult } from './errors.js';
2
+ import { codecError } from './errors.js';
3
+
4
+ /** The decompression function returned by fzstd's dynamic import. */
5
+ type FzstdDecompress = (dat: Uint8Array, buf?: Uint8Array) => Uint8Array;
6
+
7
+ /** How the decompressor is loaded. Overridable in tests via {@link _setZstdImporter}. */
8
+ type ZstdImporter = () => Promise<FzstdDecompress>;
9
+
10
+ const defaultImporter: ZstdImporter = () =>
11
+ import('fzstd').then((mod) => mod.decompress as FzstdDecompress);
12
+
13
+ let importer: ZstdImporter = defaultImporter;
14
+
15
+ /**
16
+ * Lazy-init singleton: initialized on first call to decompressZstd,
17
+ * shared across concurrent callers (AC-12).
18
+ *
19
+ * null = never attempted, Promise in-flight = init pending,
20
+ * resolved = decompressor ready, rejected = init failed (retry on next call).
21
+ * @internal
22
+ */
23
+ let _initPromise: Promise<FzstdDecompress> | null = null;
24
+
25
+ /** Count of importer invocations; observed in tests to prove single-init (AC-12). @internal */
26
+ let initCount = 0;
27
+
28
+ function getDecompressor(): Promise<FzstdDecompress> {
29
+ if (_initPromise !== null) {
30
+ return _initPromise;
31
+ }
32
+ initCount++;
33
+ _initPromise = importer().catch((cause: unknown) => {
34
+ // Clear cached failure so subsequent calls retry (do not permanently cache).
35
+ _initPromise = null;
36
+ throw new Error('codec-init-failed', { cause });
37
+ });
38
+ return _initPromise;
39
+ }
40
+
41
+ /**
42
+ * Test-only: number of times the fzstd importer has been invoked. Proves the
43
+ * lazy singleton loads the decompressor at most once under concurrency (AC-12).
44
+ * @internal
45
+ */
46
+ export function _zstdInitCount(): number {
47
+ return initCount;
48
+ }
49
+
50
+ /**
51
+ * Test-only: reset the lazy singleton and optionally override the importer, so
52
+ * init counting / concurrency / failure-retry can be exercised deterministically.
53
+ * Call with no argument to restore the real fzstd importer.
54
+ * @internal
55
+ */
56
+ export function _setZstdImporter(next?: ZstdImporter): void {
57
+ importer = next ?? defaultImporter;
58
+ _initPromise = null;
59
+ initCount = 0;
60
+ }
61
+
62
+ /**
63
+ * Decompress zstd-compressed bytes using fzstd (pure JS, no WASM).
64
+ *
65
+ * On success returns `{ ok: true, value: decompressed }`.
66
+ * On failure returns a CodecError with `codec-init-failed` or
67
+ * `decompression-failed` per the closed CodecErrorCode union (D-8).
68
+ */
69
+ export async function decompressZstd(bytes: Uint8Array): Promise<CodecResult<Uint8Array>> {
70
+ let decompress: FzstdDecompress;
71
+ try {
72
+ decompress = await getDecompressor();
73
+ } catch {
74
+ return codecError('codec-init-failed', { stage: 'dynamic-import-fzstd' });
75
+ }
76
+
77
+ try {
78
+ const result = decompress(bytes);
79
+ return { ok: true, value: result };
80
+ } catch {
81
+ return codecError('decompression-failed', {
82
+ reason: 'zstd decompression failed: corrupt or invalid compressed data',
83
+ });
84
+ }
85
+ }