@woosh/meep-engine 3.14.0 → 3.14.2

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 (220) hide show
  1. package/README.md +33 -64
  2. package/package.json +1 -1
  3. package/src/avif/native/DECISIONS.md +544 -536
  4. package/src/avif/native/heif/parse_avif_file.d.ts.map +1 -1
  5. package/src/avif/native/heif/parse_avif_file.js +384 -383
  6. package/src/avif/native/heif/parse_image_grid.d.ts.map +1 -1
  7. package/src/avif/native/heif/parse_image_grid.js +49 -48
  8. package/src/basis/BasisBitReader.d.ts +77 -0
  9. package/src/basis/BasisBitReader.d.ts.map +1 -0
  10. package/src/basis/BasisBitReader.js +231 -0
  11. package/src/basis/BasisHuffmanTable.d.ts +53 -0
  12. package/src/basis/BasisHuffmanTable.d.ts.map +1 -0
  13. package/src/basis/BasisHuffmanTable.js +263 -0
  14. package/src/basis/basis_read_huffman_table.d.ts +20 -0
  15. package/src/basis/basis_read_huffman_table.d.ts.map +1 -0
  16. package/src/basis/basis_read_huffman_table.js +187 -0
  17. package/src/basis/etc1s/Etc1sSelectorHistory.d.ts +50 -0
  18. package/src/basis/etc1s/Etc1sSelectorHistory.d.ts.map +1 -0
  19. package/src/basis/etc1s/Etc1sSelectorHistory.js +89 -0
  20. package/src/basis/etc1s/etc1s_block_colors.d.ts +14 -0
  21. package/src/basis/etc1s/etc1s_block_colors.d.ts.map +1 -0
  22. package/src/basis/etc1s/etc1s_block_colors.js +75 -0
  23. package/src/basis/etc1s/etc1s_decode_slice.d.ts +57 -0
  24. package/src/basis/etc1s/etc1s_decode_slice.d.ts.map +1 -0
  25. package/src/basis/etc1s/etc1s_decode_slice.js +258 -0
  26. package/src/basis/etc1s/etc1s_read_endpoint_codebook.d.ts +48 -0
  27. package/src/basis/etc1s/etc1s_read_endpoint_codebook.d.ts.map +1 -0
  28. package/src/basis/etc1s/etc1s_read_endpoint_codebook.js +111 -0
  29. package/src/basis/etc1s/etc1s_read_selector_codebook.d.ts +42 -0
  30. package/src/basis/etc1s/etc1s_read_selector_codebook.d.ts.map +1 -0
  31. package/src/basis/etc1s/etc1s_read_selector_codebook.js +127 -0
  32. package/src/basis/etc1s/etc1s_read_slice_tables.d.ts +54 -0
  33. package/src/basis/etc1s/etc1s_read_slice_tables.d.ts.map +1 -0
  34. package/src/basis/etc1s/etc1s_read_slice_tables.js +47 -0
  35. package/src/basis/transcode/build_range_plan.js +54 -0
  36. package/src/basis/transcode/etc1s_to_bc1.d.ts +24 -0
  37. package/src/basis/transcode/etc1s_to_bc1.d.ts.map +1 -0
  38. package/src/basis/transcode/etc1s_to_bc1.js +205 -0
  39. package/src/basis/transcode/etc1s_to_bc7.d.ts +30 -0
  40. package/src/basis/transcode/etc1s_to_bc7.d.ts.map +1 -0
  41. package/src/basis/transcode/etc1s_to_bc7.js +496 -0
  42. package/src/basis/transcode/fit_endpoints.d.ts +59 -0
  43. package/src/basis/transcode/fit_endpoints.d.ts.map +1 -0
  44. package/src/basis/transcode/fit_endpoints.js +226 -0
  45. package/src/{avif/native/isobmff → core/binary}/ByteCursor.d.ts +19 -6
  46. package/src/core/binary/ByteCursor.d.ts.map +1 -0
  47. package/src/{avif/native/isobmff → core/binary}/ByteCursor.js +41 -14
  48. package/src/engine/EngineHarness.js +1 -1
  49. package/src/engine/asset/codec/Codec.d.ts.map +1 -0
  50. package/src/engine/asset/codec/CodecWithFallback.d.ts.map +1 -0
  51. package/src/engine/asset/{loaders/image/codec → codec}/CodecWithFallback.js +2 -2
  52. package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +4 -4
  53. package/src/engine/asset/loaders/image/ImageRGBADataLoader.d.ts +1 -1
  54. package/src/engine/asset/loaders/image/ImageRGBADataLoader.d.ts.map +1 -1
  55. package/src/engine/asset/loaders/image/ImageRGBADataLoader.js +1 -1
  56. package/src/engine/asset/loaders/image/codec/NativeImageDecoder.d.ts +1 -1
  57. package/src/engine/asset/loaders/image/codec/NativeImageDecoder.d.ts.map +1 -1
  58. package/src/engine/asset/loaders/image/codec/NativeImageDecoder.js +1 -1
  59. package/src/engine/asset/loaders/image/codec/ThreadedImageDecoder.d.ts +1 -1
  60. package/src/engine/asset/loaders/image/codec/ThreadedImageDecoder.d.ts.map +1 -1
  61. package/src/engine/asset/loaders/image/codec/ThreadedImageDecoder.js +1 -1
  62. package/src/engine/graphics/texture/atlas/TextureAtlas.d.ts.map +1 -1
  63. package/src/engine/graphics/texture/atlas/TextureAtlas.js +13 -0
  64. package/src/engine/physics/broadphase/generate_pairs.d.ts.map +1 -1
  65. package/src/engine/physics/broadphase/generate_pairs.js +7 -1
  66. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  67. package/src/ktx2/KTX2_IDENTIFIER.d.ts +14 -0
  68. package/src/ktx2/KTX2_IDENTIFIER.d.ts.map +1 -0
  69. package/src/ktx2/KTX2_IDENTIFIER.js +15 -0
  70. package/src/ktx2/KhrDfModel.d.ts +35 -0
  71. package/src/ktx2/KhrDfModel.d.ts.map +1 -0
  72. package/src/ktx2/KhrDfModel.js +49 -0
  73. package/src/ktx2/SupercompressionScheme.d.ts +16 -0
  74. package/src/ktx2/SupercompressionScheme.d.ts.map +1 -0
  75. package/src/ktx2/SupercompressionScheme.js +23 -0
  76. package/src/ktx2/ktx2_read.d.ts +71 -0
  77. package/src/ktx2/ktx2_read.d.ts.map +1 -0
  78. package/src/ktx2/ktx2_read.js +581 -0
  79. package/src/ktx2/ktx2_read_basis_lz_global_data.d.ts +27 -0
  80. package/src/ktx2/ktx2_read_basis_lz_global_data.d.ts.map +1 -0
  81. package/src/ktx2/ktx2_read_basis_lz_global_data.js +189 -0
  82. package/src/ktx2/vk_format_to_texture_format.d.ts +55 -0
  83. package/src/ktx2/vk_format_to_texture_format.d.ts.map +1 -0
  84. package/src/ktx2/vk_format_to_texture_format.js +290 -0
  85. package/src/shade/descriptor/GPUTextureUsage.d.ts +23 -0
  86. package/src/shade/descriptor/GPUTextureUsage.d.ts.map +1 -0
  87. package/src/shade/descriptor/GPUTextureUsage.js +24 -0
  88. package/src/shade/descriptor/WebGPUExtensionType.d.ts +3 -0
  89. package/src/shade/descriptor/WebGPUExtensionType.js +26 -0
  90. package/src/shade/descriptor/texture/TextureDescriptor.d.ts +2 -6
  91. package/src/shade/descriptor/texture/TextureDescriptor.d.ts.map +1 -1
  92. package/src/shade/descriptor/texture/TextureDescriptor.js +24 -40
  93. package/src/shade/descriptor/texture/format/TextureFormat.d.ts +125 -0
  94. package/src/shade/descriptor/texture/format/TextureFormat.d.ts.map +1 -0
  95. package/src/shade/descriptor/texture/format/TextureFormat.js +146 -0
  96. package/src/shade/descriptor/texture/format/gpu_texture_format_info.d.ts +60 -0
  97. package/src/shade/descriptor/texture/format/gpu_texture_format_info.d.ts.map +1 -1
  98. package/src/shade/descriptor/texture/format/gpu_texture_format_info.js +102 -0
  99. package/src/shade/device/graph/TextureResourceDescriptor.d.ts.map +1 -1
  100. package/src/shade/device/graph/TextureResourceDescriptor.js +160 -155
  101. package/src/shade/device/mock/SoftwareGPUQueue.d.ts +11 -4
  102. package/src/shade/device/mock/SoftwareGPUQueue.d.ts.map +1 -1
  103. package/src/shade/device/mock/SoftwareGPUQueue.js +31 -37
  104. package/src/shade/device/mock/SoftwareGPUTexture.d.ts.map +1 -1
  105. package/src/shade/device/mock/SoftwareGPUTexture.js +8 -4
  106. package/src/shade/device/timing/profile/frame_graph_extract_topology.d.ts.map +1 -1
  107. package/src/shade/device/timing/profile/frame_graph_extract_topology.js +164 -174
  108. package/src/shade/playground/ground_seam/README.md +235 -0
  109. package/src/shade/playground/ground_seam/capture_scene_color.d.ts +45 -0
  110. package/src/shade/playground/ground_seam/capture_scene_color.d.ts.map +1 -0
  111. package/src/shade/playground/ground_seam/capture_scene_color.js +242 -0
  112. package/src/shade/playground/ground_seam/depth_step_across_seam.d.ts +11 -0
  113. package/src/shade/playground/ground_seam/depth_step_across_seam.d.ts.map +1 -0
  114. package/src/shade/playground/ground_seam/depth_step_across_seam.js +185 -0
  115. package/src/shade/playground/ground_seam/index.html +195 -0
  116. package/src/shade/playground/ground_seam/main.d.ts +2 -0
  117. package/src/shade/playground/ground_seam/main.d.ts.map +1 -0
  118. package/src/shade/playground/ground_seam/main.js +879 -0
  119. package/src/shade/playground/ground_seam/make_grid_texture.d.ts +29 -0
  120. package/src/shade/playground/ground_seam/make_grid_texture.d.ts.map +1 -0
  121. package/src/shade/playground/ground_seam/make_grid_texture.js +105 -0
  122. package/src/shade/playground/ground_seam/make_ground.d.ts +70 -0
  123. package/src/shade/playground/ground_seam/make_ground.d.ts.map +1 -0
  124. package/src/shade/playground/ground_seam/make_ground.js +161 -0
  125. package/src/shade/playground/ground_seam/measure_flicker.d.ts +71 -0
  126. package/src/shade/playground/ground_seam/measure_flicker.d.ts.map +1 -0
  127. package/src/shade/playground/ground_seam/measure_flicker.js +531 -0
  128. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  129. package/src/shade/renderer/Renderer.js +2519 -2511
  130. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  131. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +275 -274
  132. package/src/shade/renderer/light/GPULightCollection.d.ts.map +1 -1
  133. package/src/shade/renderer/light/GPULightCollection.js +323 -314
  134. package/src/shade/renderer/lightmap/GPULightMap.js +336 -336
  135. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
  136. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +12 -2
  137. package/src/shade/renderer/material/standard/fragment_gbuffer.d.ts.map +1 -1
  138. package/src/shade/renderer/material/standard/fragment_gbuffer.js +3 -1
  139. package/src/shade/renderer/material/standard/fragment_gbuffer_vt.js +169 -169
  140. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.d.ts.map +1 -1
  141. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.js +3 -1
  142. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  143. package/src/shade/renderer/rasterize/native/oit/chunk_forward_shade_standard_fragment_brick4.d.ts.map +1 -1
  144. package/src/shade/renderer/rasterize/native/oit/chunk_forward_shade_standard_fragment_brick4.js +3 -1
  145. package/src/shade/renderer/rasterize/native/oit/chunk_forward_shade_standard_fragment_ibl.d.ts.map +1 -1
  146. package/src/shade/renderer/rasterize/native/oit/chunk_forward_shade_standard_fragment_ibl.js +3 -1
  147. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_tangent_normal.d.ts +28 -0
  148. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_tangent_normal.d.ts.map +1 -0
  149. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_decode_tangent_normal.js +39 -0
  150. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_full.d.ts +45 -0
  151. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_full.d.ts.map +1 -1
  152. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_full.js +94 -61
  153. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_solve.d.ts +24 -0
  154. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_solve.d.ts.map +1 -0
  155. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_barycentric_solve.js +43 -0
  156. package/src/shade/renderer/texture/COMPRESSED_TEXTURE_PLAN_2026_09_01.md +1462 -0
  157. package/src/shade/renderer/texture/TextureManager.d.ts.map +1 -1
  158. package/src/shade/renderer/texture/TextureManager.js +20 -8
  159. package/src/shade/renderer/texture/TextureRole.d.ts +19 -0
  160. package/src/shade/renderer/texture/TextureRole.d.ts.map +1 -0
  161. package/src/shade/renderer/texture/TextureRole.js +26 -0
  162. package/src/shade/renderer/texture/TextureVariantSet.d.ts +71 -0
  163. package/src/shade/renderer/texture/TextureVariantSet.d.ts.map +1 -0
  164. package/src/shade/renderer/texture/TextureVariantSet.js +105 -0
  165. package/src/shade/renderer/texture/download_texture_data.d.ts.map +1 -1
  166. package/src/shade/renderer/texture/download_texture_data.js +22 -0
  167. package/src/shade/renderer/texture/format/gpu_texture_compression_support.d.ts +15 -0
  168. package/src/shade/renderer/texture/format/gpu_texture_compression_support.d.ts.map +1 -0
  169. package/src/shade/renderer/texture/format/gpu_texture_compression_support.js +50 -0
  170. package/src/shade/renderer/texture/format/gpu_texture_format_can_generate_mipmaps.d.ts +27 -0
  171. package/src/shade/renderer/texture/format/gpu_texture_format_can_generate_mipmaps.d.ts.map +1 -0
  172. package/src/shade/renderer/texture/format/gpu_texture_format_can_generate_mipmaps.js +65 -0
  173. package/src/shade/renderer/texture/format/infer_format_from_parameters.d.ts +11 -0
  174. package/src/shade/renderer/texture/format/infer_format_from_parameters.d.ts.map +1 -0
  175. package/src/shade/renderer/texture/format/infer_format_from_parameters.js +88 -0
  176. package/src/shade/renderer/texture/format/texture_format_from_shade_image.d.ts +24 -0
  177. package/src/shade/renderer/texture/format/texture_format_from_shade_image.d.ts.map +1 -0
  178. package/src/shade/renderer/texture/format/texture_format_from_shade_image.js +71 -0
  179. package/src/shade/renderer/texture/format/texture_format_preference.d.ts +22 -0
  180. package/src/shade/renderer/texture/format/texture_format_preference.d.ts.map +1 -0
  181. package/src/shade/renderer/texture/format/texture_format_preference.js +89 -0
  182. package/src/shade/renderer/texture/format/texture_variant_select.d.ts +24 -0
  183. package/src/shade/renderer/texture/format/texture_variant_select.d.ts.map +1 -0
  184. package/src/shade/renderer/texture/format/texture_variant_select.js +38 -0
  185. package/src/shade/renderer/texture/shade_texture_might_have_alpha.d.ts +9 -1
  186. package/src/shade/renderer/texture/shade_texture_might_have_alpha.d.ts.map +1 -1
  187. package/src/shade/renderer/texture/shade_texture_might_have_alpha.js +73 -8
  188. package/src/shade/renderer/texture/source/AlphaMode.d.ts +27 -0
  189. package/src/shade/renderer/texture/source/AlphaMode.d.ts.map +1 -0
  190. package/src/shade/renderer/texture/source/AlphaMode.js +52 -0
  191. package/src/shade/renderer/texture/source/ShadeImage.d.ts +33 -0
  192. package/src/shade/renderer/texture/source/ShadeImage.d.ts.map +1 -1
  193. package/src/shade/renderer/texture/source/ShadeImage.js +74 -0
  194. package/src/shade/renderer/texture/source/ShadeTexelData.d.ts +78 -0
  195. package/src/shade/renderer/texture/source/ShadeTexelData.d.ts.map +1 -0
  196. package/src/shade/renderer/texture/source/ShadeTexelData.js +158 -0
  197. package/src/shade/renderer/texture/texture_descriptor_from_texture_shade.d.ts +0 -10
  198. package/src/shade/renderer/texture/texture_descriptor_from_texture_shade.d.ts.map +1 -1
  199. package/src/shade/renderer/texture/texture_descriptor_from_texture_shade.js +73 -175
  200. package/src/shade/renderer/texture/texture_write_level.d.ts +32 -0
  201. package/src/shade/renderer/texture/texture_write_level.d.ts.map +1 -0
  202. package/src/shade/renderer/texture/texture_write_level.js +77 -0
  203. package/src/shade/renderer/texture/texture_write_to_gpu.d.ts +8 -1
  204. package/src/shade/renderer/texture/texture_write_to_gpu.d.ts.map +1 -1
  205. package/src/shade/renderer/texture/texture_write_to_gpu.js +62 -37
  206. package/src/shade/renderer/texture/virtual/source/VTSourceImage.d.ts +8 -0
  207. package/src/shade/renderer/texture/virtual/source/VTSourceImage.d.ts.map +1 -1
  208. package/src/shade/renderer/texture/virtual/source/VTSourceImage.js +8 -0
  209. package/src/shade/util/texture_mip_extent.d.ts +49 -0
  210. package/src/shade/util/texture_mip_extent.d.ts.map +1 -0
  211. package/src/shade/util/texture_mip_extent.js +87 -0
  212. package/src/avif/native/isobmff/ByteCursor.d.ts.map +0 -1
  213. package/src/engine/asset/loaders/image/codec/Codec.d.ts.map +0 -1
  214. package/src/engine/asset/loaders/image/codec/CodecWithFallback.d.ts.map +0 -1
  215. package/src/shade/util/compute_max_mip_count.d.ts +0 -8
  216. package/src/shade/util/compute_max_mip_count.d.ts.map +0 -1
  217. package/src/shade/util/compute_max_mip_count.js +0 -27
  218. /package/src/engine/asset/{loaders/image/codec → codec}/Codec.d.ts +0 -0
  219. /package/src/engine/asset/{loaders/image/codec → codec}/Codec.js +0 -0
  220. /package/src/engine/asset/{loaders/image/codec → codec}/CodecWithFallback.d.ts +0 -0
@@ -0,0 +1,263 @@
1
+ import { BasisBitReader } from "./BasisBitReader.js";
2
+
3
+ /**
4
+ * Longest code the format permits, from the specification's `cHuffmanMaxSupportedCodeSize`.
5
+ *
6
+ * @readonly
7
+ * @type {number}
8
+ */
9
+ export const HUFFMAN_MAX_CODE_SIZE = 16;
10
+
11
+ /**
12
+ * Most symbols a table may describe, from the specification's `cHuffmanMaxSyms` — `1 << 14`.
13
+ *
14
+ * @readonly
15
+ * @type {number}
16
+ */
17
+ export const HUFFMAN_MAX_SYMBOLS = 1 << 14;
18
+
19
+ /**
20
+ * Bits the fast lookup covers. A code no longer than this is decoded by one array read.
21
+ *
22
+ * Ten is the reference decoder's choice and a reasonable one: the table costs 1024 entries per
23
+ * Huffman table, and the codes that miss it are by construction the rare symbols.
24
+ *
25
+ * @readonly
26
+ * @type {number}
27
+ */
28
+ const FAST_LOOKUP_BITS = 10;
29
+
30
+ /**
31
+ * Bits of a {@link BasisHuffmanTable#fast} entry given over to the code length, which is at most
32
+ * {@link HUFFMAN_MAX_CODE_SIZE} and so needs five.
33
+ *
34
+ * @readonly
35
+ * @type {number}
36
+ */
37
+ const FAST_LENGTH_BITS = 5;
38
+
39
+ /**
40
+ * A canonical Huffman decoding table, built from per-symbol code lengths.
41
+ *
42
+ * **Canonical in the Deflate sense**, which the specification says outright: codes are assigned in
43
+ * order of increasing length and, within a length, increasing symbol. What differs from Deflate is
44
+ * only where the bits come from — {@link BasisBitReader} hands them over least significant first, so
45
+ * a code appears in the stream with its bits reversed, and the fast table is indexed by that
46
+ * reversal.
47
+ *
48
+ * **Incomplete tables are legal.** The specification calls out the single-symbol case explicitly —
49
+ * one symbol with a one-bit code, assigned the code zero, leaving half the code space unused. So an
50
+ * under-full table is built rather than refused, and only a code that actually lands in the unused
51
+ * space throws.
52
+ *
53
+ * @author Alex Goldring
54
+ * @copyright Company Named Limited (c) 2026
55
+ */
56
+ export class BasisHuffmanTable {
57
+ /**
58
+ * `(symbol << FAST_LENGTH_BITS) | code_length` for each of the `1 << FAST_LOOKUP_BITS` possible
59
+ * peeks, or `0` where no code of at most {@link FAST_LOOKUP_BITS} bits starts with those bits.
60
+ *
61
+ * Packed into one array rather than two because it is read once per symbol on the hottest path
62
+ * in the transcoder.
63
+ *
64
+ * @type {Int32Array}
65
+ */
66
+ #fast = null;
67
+
68
+ /**
69
+ * Number of codes of each length, indexed by length. Entry 0 is unused.
70
+ * @type {Int32Array}
71
+ */
72
+ #length_count = null;
73
+
74
+ /**
75
+ * Smallest canonical code of each length, most significant bit first.
76
+ * @type {Int32Array}
77
+ */
78
+ #first_code = null;
79
+
80
+ /**
81
+ * Index into {@link #sorted_symbols} of the first symbol of each length.
82
+ * @type {Int32Array}
83
+ */
84
+ #first_index = null;
85
+
86
+ /**
87
+ * Symbols with a non-zero code length, ordered by length then by symbol — which is the order
88
+ * canonical codes are assigned in, so a code's rank within its length indexes straight into it.
89
+ * @type {Int32Array}
90
+ */
91
+ #sorted_symbols = null;
92
+
93
+ /**
94
+ * Build the table from an array of per-symbol code lengths, `0` meaning "symbol not used".
95
+ *
96
+ * @param {Uint8Array} code_lengths
97
+ * @returns {BasisHuffmanTable} this
98
+ * @throws {Error} for a length past the format's maximum, or a set of lengths that
99
+ * over-subscribes the code space
100
+ */
101
+ init(code_lengths) {
102
+ const symbol_count = code_lengths.length;
103
+
104
+ const length_count = new Int32Array(HUFFMAN_MAX_CODE_SIZE + 1);
105
+
106
+ for (let symbol = 0; symbol < symbol_count; symbol++) {
107
+ const length = code_lengths[symbol];
108
+
109
+ if (length > HUFFMAN_MAX_CODE_SIZE) {
110
+ throw new Error(
111
+ `Basis: a Huffman symbol declares a code length of ${length} bits, and the`
112
+ + ` format's longest code is ${HUFFMAN_MAX_CODE_SIZE}`
113
+ );
114
+ }
115
+
116
+ length_count[length]++;
117
+ }
118
+
119
+ // a length of zero means "unused", not "a zero-bit code", so it takes no code space
120
+ length_count[0] = 0;
121
+
122
+ const first_code = new Int32Array(HUFFMAN_MAX_CODE_SIZE + 2);
123
+ const first_index = new Int32Array(HUFFMAN_MAX_CODE_SIZE + 2);
124
+
125
+ let code = 0;
126
+ let index = 0;
127
+
128
+ for (let length = 1; length <= HUFFMAN_MAX_CODE_SIZE; length++) {
129
+ first_code[length] = code;
130
+ first_index[length] = index;
131
+
132
+ code += length_count[length];
133
+ index += length_count[length];
134
+
135
+ /*
136
+ After absorbing every code of this length, `code` counts the codes that would exist if
137
+ the tree were full at this depth. More than that means two symbols were handed the same
138
+ prefix, which is a corrupt table rather than an unusual one — unlike the under-full case
139
+ the specification explicitly permits.
140
+ */
141
+ if (code > (1 << length)) {
142
+ throw new Error(
143
+ `Basis: the Huffman code lengths over-subscribe the code space at length`
144
+ + ` ${length}, so no canonical table can be built from them`
145
+ );
146
+ }
147
+
148
+ code <<= 1;
149
+ }
150
+
151
+ const sorted_symbols = new Int32Array(index);
152
+ const next_index = first_index.slice();
153
+
154
+ for (let symbol = 0; symbol < symbol_count; symbol++) {
155
+ const length = code_lengths[symbol];
156
+
157
+ if (length !== 0) {
158
+ sorted_symbols[next_index[length]++] = symbol;
159
+ }
160
+ }
161
+
162
+ this.#length_count = length_count;
163
+ this.#first_code = first_code;
164
+ this.#first_index = first_index;
165
+ this.#sorted_symbols = sorted_symbols;
166
+
167
+ this.#build_fast_lookup(code_lengths, symbol_count);
168
+
169
+ return this;
170
+ }
171
+
172
+ /**
173
+ * Fill {@link #fast} so a peek of {@link FAST_LOOKUP_BITS} bits resolves any short code without
174
+ * walking the tree.
175
+ *
176
+ * @param {Uint8Array} code_lengths
177
+ * @param {number} symbol_count
178
+ */
179
+ #build_fast_lookup(code_lengths, symbol_count) {
180
+ const fast = new Int32Array(1 << FAST_LOOKUP_BITS);
181
+
182
+ const next_code = this.#first_code.slice();
183
+
184
+ for (let symbol = 0; symbol < symbol_count; symbol++) {
185
+ const length = code_lengths[symbol];
186
+
187
+ if (length === 0) {
188
+ continue;
189
+ }
190
+
191
+ const canonical = next_code[length]++;
192
+
193
+ if (length > FAST_LOOKUP_BITS) {
194
+ // left to the slow path, which rebuilds the code as it reads rather than tabulating
195
+ continue;
196
+ }
197
+
198
+ /*
199
+ The stream hands bits over least significant first, so a code arrives reversed within
200
+ its own width. Every peek whose low `length` bits match that reversal decodes to this
201
+ symbol, whatever the bits above happen to be — which is what the stride fills in.
202
+ */
203
+ let reversed = 0;
204
+
205
+ for (let bit = 0; bit < length; bit++) {
206
+ reversed |= ((canonical >>> (length - 1 - bit)) & 1) << bit;
207
+ }
208
+
209
+ const packed = (symbol << FAST_LENGTH_BITS) | length;
210
+
211
+ for (let i = reversed; i < fast.length; i += 1 << length) {
212
+ fast[i] = packed;
213
+ }
214
+ }
215
+
216
+ this.#fast = fast;
217
+ }
218
+
219
+ /**
220
+ * Read one symbol.
221
+ *
222
+ * @param {BasisBitReader} reader
223
+ * @returns {number} the symbol
224
+ * @throws {Error} for a prefix that matches no code in the table
225
+ */
226
+ decode(reader) {
227
+ const packed = this.#fast[reader.peek(FAST_LOOKUP_BITS)];
228
+
229
+ if (packed !== 0) {
230
+ reader.consume(packed & ((1 << FAST_LENGTH_BITS) - 1));
231
+
232
+ return packed >>> FAST_LENGTH_BITS;
233
+ }
234
+
235
+ /*
236
+ Either a code longer than the fast lookup covers, or one that is not in the table at all.
237
+ Rebuild it a bit at a time, most significant first, which is the order canonical codes
238
+ compare in.
239
+ */
240
+ let code = 0;
241
+
242
+ for (let length = 1; length <= HUFFMAN_MAX_CODE_SIZE; length++) {
243
+ code = (code << 1) | ((reader.peek(length) >>> (length - 1)) & 1);
244
+
245
+ const count = this.#length_count[length];
246
+
247
+ if (count !== 0) {
248
+ const offset = code - this.#first_code[length];
249
+
250
+ if (offset >= 0 && offset < count) {
251
+ reader.consume(length);
252
+
253
+ return this.#sorted_symbols[this.#first_index[length] + offset];
254
+ }
255
+ }
256
+ }
257
+
258
+ throw new Error(
259
+ `Basis: a ${HUFFMAN_MAX_CODE_SIZE} bit prefix matched no code in the Huffman table,`
260
+ + ` which means the bitstream and the table disagree`
261
+ );
262
+ }
263
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Read one of the format's compressed Huffman tables.
3
+ *
4
+ * The table is described by its symbols' code lengths, and those lengths are themselves run-length
5
+ * and Huffman coded — the same two-level arrangement Deflate uses, which the specification says it
6
+ * is deliberately compatible with. So this reads a small table first, then uses it to read the
7
+ * lengths of the one that was asked for.
8
+ *
9
+ * @param {BasisBitReader} reader positioned at the start of the table
10
+ * @returns {BasisHuffmanTable}
11
+ * @throws {Error} naming the field, for a table whose header or code lengths are not self-consistent
12
+ * @see the specification, section 6.0
13
+ *
14
+ * @author Alex Goldring
15
+ * @copyright Company Named Limited (c) 2026
16
+ */
17
+ export function basis_read_huffman_table(reader: BasisBitReader): BasisHuffmanTable;
18
+ import { BasisBitReader } from "./BasisBitReader.js";
19
+ import { BasisHuffmanTable } from "./BasisHuffmanTable.js";
20
+ //# sourceMappingURL=basis_read_huffman_table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"basis_read_huffman_table.d.ts","sourceRoot":"","sources":["../../../src/basis/basis_read_huffman_table.js"],"names":[],"mappings":"AA4EA;;;;;;;;;;;;;;;GAeG;AACH,iDARW,cAAc,GACZ,iBAAiB,CAqG7B;+BA1L8B,qBAAqB;kCAK7C,wBAAwB"}
@@ -0,0 +1,187 @@
1
+ import { BasisBitReader } from "./BasisBitReader.js";
2
+ import {
3
+ BasisHuffmanTable,
4
+ HUFFMAN_MAX_CODE_SIZE,
5
+ HUFFMAN_MAX_SYMBOLS
6
+ } from "./BasisHuffmanTable.js";
7
+
8
+ /**
9
+ * Symbols in the little table that codes the big table's code lengths: the seventeen lengths
10
+ * `[0, 16]` plus four run codes. The specification's `cHuffmanTotalCodelengthCodes`.
11
+ *
12
+ * @readonly
13
+ * @type {number}
14
+ */
15
+ const CODELENGTH_CODE_COUNT = 21;
16
+
17
+ /**
18
+ * A run of symbols with no code, three to ten long. The specification's `cHuffmanSmallZeroRunCode`.
19
+ *
20
+ * @readonly
21
+ * @type {number}
22
+ */
23
+ const SMALL_ZERO_RUN_CODE = 17;
24
+
25
+ /**
26
+ * A run of symbols with no code, eleven to a hundred and thirty-eight long.
27
+ *
28
+ * @readonly
29
+ * @type {number}
30
+ */
31
+ const BIG_ZERO_RUN_CODE = 18;
32
+
33
+ /**
34
+ * A run repeating the previous symbol's code length, three to six long.
35
+ *
36
+ * @readonly
37
+ * @type {number}
38
+ */
39
+ const SMALL_REPEAT_CODE = 19;
40
+
41
+ /**
42
+ * A run repeating the previous symbol's code length, seven to a hundred and thirty-four long.
43
+ *
44
+ * @readonly
45
+ * @type {number}
46
+ */
47
+ const BIG_REPEAT_CODE = 20;
48
+
49
+ /**
50
+ * Extra bits, and the minimum the run may be, for each of the four run codes — indexed by the code
51
+ * itself so the decode loop reads them rather than branching four ways.
52
+ *
53
+ * @readonly
54
+ * @type {Object<number, {extra_bits: number, minimum: number}>}
55
+ */
56
+ const RUNS = {
57
+ [SMALL_ZERO_RUN_CODE]: { extra_bits: 3, minimum: 3 },
58
+ [BIG_ZERO_RUN_CODE]: { extra_bits: 7, minimum: 11 },
59
+ [SMALL_REPEAT_CODE]: { extra_bits: 2, minimum: 3 },
60
+ [BIG_REPEAT_CODE]: { extra_bits: 7, minimum: 7 },
61
+ };
62
+
63
+ /**
64
+ * The order the code lengths of the little table arrive in.
65
+ *
66
+ * Not sorted, and not arbitrary: the run codes come first and the ordinary lengths radiate outward
67
+ * from eight, so the lengths a typical table never uses cluster at the end and can be left unsent.
68
+ *
69
+ * @readonly
70
+ * @type {number[]}
71
+ */
72
+ const CODELENGTH_CODE_ORDER = [
73
+ SMALL_ZERO_RUN_CODE, BIG_ZERO_RUN_CODE, SMALL_REPEAT_CODE, BIG_REPEAT_CODE,
74
+ 0, 8, 7, 9, 6, 0xA, 5, 0xB, 4, 0xC, 3, 0xD, 2, 0xE, 1, 0xF, 0x10,
75
+ ];
76
+
77
+ /**
78
+ * Read one of the format's compressed Huffman tables.
79
+ *
80
+ * The table is described by its symbols' code lengths, and those lengths are themselves run-length
81
+ * and Huffman coded — the same two-level arrangement Deflate uses, which the specification says it
82
+ * is deliberately compatible with. So this reads a small table first, then uses it to read the
83
+ * lengths of the one that was asked for.
84
+ *
85
+ * @param {BasisBitReader} reader positioned at the start of the table
86
+ * @returns {BasisHuffmanTable}
87
+ * @throws {Error} naming the field, for a table whose header or code lengths are not self-consistent
88
+ * @see the specification, section 6.0
89
+ *
90
+ * @author Alex Goldring
91
+ * @copyright Company Named Limited (c) 2026
92
+ */
93
+ export function basis_read_huffman_table(reader) {
94
+ const total_used_syms = reader.bits(14);
95
+
96
+ if (total_used_syms === 0 || total_used_syms > HUFFMAN_MAX_SYMBOLS) {
97
+ throw new Error(
98
+ `Basis: a Huffman table declares ${total_used_syms} symbols, and the format allows`
99
+ + ` 1 to ${HUFFMAN_MAX_SYMBOLS}`
100
+ );
101
+ }
102
+
103
+ const codelength_code_count = reader.bits(5);
104
+
105
+ if (codelength_code_count === 0 || codelength_code_count > CODELENGTH_CODE_COUNT) {
106
+ throw new Error(
107
+ `Basis: a Huffman table declares ${codelength_code_count} code length codes, and the`
108
+ + ` format allows 1 to ${CODELENGTH_CODE_COUNT}`
109
+ );
110
+ }
111
+
112
+ const codelength_code_lengths = new Uint8Array(CODELENGTH_CODE_COUNT);
113
+
114
+ for (let i = 0; i < codelength_code_count; i++) {
115
+ codelength_code_lengths[CODELENGTH_CODE_ORDER[i]] = reader.bits(3);
116
+ }
117
+
118
+ const codelength_table = new BasisHuffmanTable().init(codelength_code_lengths);
119
+
120
+ const code_lengths = new Uint8Array(total_used_syms);
121
+
122
+ let symbol = 0;
123
+
124
+ while (symbol < total_used_syms) {
125
+ const code = codelength_table.decode(reader);
126
+
127
+ if (code <= HUFFMAN_MAX_CODE_SIZE) {
128
+ code_lengths[symbol++] = code;
129
+
130
+ continue;
131
+ }
132
+
133
+ const run = RUNS[code];
134
+
135
+ if (run === undefined) {
136
+ throw new Error(
137
+ `Basis: a Huffman table's code length stream used symbol ${code}, and only`
138
+ + ` 0 to ${BIG_REPEAT_CODE} are defined`
139
+ );
140
+ }
141
+
142
+ const count = reader.bits(run.extra_bits) + run.minimum;
143
+
144
+ if (symbol + count > total_used_syms) {
145
+ throw new Error(
146
+ `Basis: a run of ${count} code lengths starting at symbol ${symbol} overruns the`
147
+ + ` ${total_used_syms} symbols the Huffman table declared`
148
+ );
149
+ }
150
+
151
+ const repeats = code === SMALL_REPEAT_CODE || code === BIG_REPEAT_CODE;
152
+
153
+ if (!repeats) {
154
+ // a zero run; the array is already zero, so the run only has to advance past it
155
+ symbol += count;
156
+
157
+ continue;
158
+ }
159
+
160
+ /*
161
+ The specification forbids a repeat as the first symbol, and forbids repeating a length of
162
+ zero — the run says "the same as the last one" and there has to be a last one for it to
163
+ name. Both would otherwise read as a run of unused symbols, which is what a zero run is for.
164
+ */
165
+ if (symbol === 0) {
166
+ throw new Error(
167
+ "Basis: a Huffman table's code length stream opens with a repeat run, which has no"
168
+ + " previous length to repeat"
169
+ );
170
+ }
171
+
172
+ const previous = code_lengths[symbol - 1];
173
+
174
+ if (previous === 0) {
175
+ throw new Error(
176
+ `Basis: a Huffman table's code length stream repeats symbol ${symbol - 1}, which has`
177
+ + ` no code — a zero run is how unused symbols are spelled`
178
+ );
179
+ }
180
+
181
+ code_lengths.fill(previous, symbol, symbol + count);
182
+
183
+ symbol += count;
184
+ }
185
+
186
+ return new BasisHuffmanTable().init(code_lengths);
187
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The recently used selector indices, as an approximate move-to-front list.
3
+ *
4
+ * A slice refers to a selector either by its codebook index or by its position in this list, and the
5
+ * second is cheaper whenever a block reuses something a nearby block used — which most blocks do.
6
+ * What makes it *approximate* is the two shortcuts it takes over a real move-to-front transform, and
7
+ * both matter to a decoder because they define the state exactly:
8
+ *
9
+ * - **A used entry swaps with the one at half its index** rather than moving to the front and
10
+ * shifting everything after it along. So a repeatedly used entry walks toward the front
11
+ * geometrically instead of arriving in one step, and the list is never actually re-ordered.
12
+ * - **A new entry overwrites at a rover** that sweeps the back half of the list and wraps, rather
13
+ * than pushing the oldest entry off the end.
14
+ *
15
+ * Both make the encoder's and decoder's work constant per block instead of linear in the list, which
16
+ * for a list of up to several thousand entries is the whole reason it is affordable at all.
17
+ *
18
+ * @author Alex Goldring
19
+ * @copyright Company Named Limited (c) 2026
20
+ */
21
+ export class Etc1sSelectorHistory {
22
+ /**
23
+ * @param {number} size from the slice tables section; may be zero, for a file whose blocks never
24
+ * refer to the history
25
+ */
26
+ constructor(size: number);
27
+ /**
28
+ * @returns {number}
29
+ */
30
+ get size(): number;
31
+ /**
32
+ * @param {number} index
33
+ * @returns {number} the selector codebook index remembered there
34
+ */
35
+ at(index: number): number;
36
+ /**
37
+ * Remember a selector index that was referred to by its codebook index.
38
+ *
39
+ * @param {number} value
40
+ */
41
+ add(value: number): void;
42
+ /**
43
+ * Promote the entry at `index`, having just referred to it.
44
+ *
45
+ * @param {number} index
46
+ */
47
+ use(index: number): void;
48
+ #private;
49
+ }
50
+ //# sourceMappingURL=Etc1sSelectorHistory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Etc1sSelectorHistory.d.ts","sourceRoot":"","sources":["../../../../src/basis/etc1s/Etc1sSelectorHistory.js"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH;IAaI;;;OAGG;IACH,kBAHW,MAAM,EAMhB;IAED;;OAEG;IACH,mBAEC;IAED;;;OAGG;IACH,UAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;;OAIG;IACH,WAFW,MAAM,QAQhB;IAED;;;;OAIG;IACH,WAFW,MAAM,QAchB;;CACJ"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * The recently used selector indices, as an approximate move-to-front list.
3
+ *
4
+ * A slice refers to a selector either by its codebook index or by its position in this list, and the
5
+ * second is cheaper whenever a block reuses something a nearby block used — which most blocks do.
6
+ * What makes it *approximate* is the two shortcuts it takes over a real move-to-front transform, and
7
+ * both matter to a decoder because they define the state exactly:
8
+ *
9
+ * - **A used entry swaps with the one at half its index** rather than moving to the front and
10
+ * shifting everything after it along. So a repeatedly used entry walks toward the front
11
+ * geometrically instead of arriving in one step, and the list is never actually re-ordered.
12
+ * - **A new entry overwrites at a rover** that sweeps the back half of the list and wraps, rather
13
+ * than pushing the oldest entry off the end.
14
+ *
15
+ * Both make the encoder's and decoder's work constant per block instead of linear in the list, which
16
+ * for a list of up to several thousand entries is the whole reason it is affordable at all.
17
+ *
18
+ * @author Alex Goldring
19
+ * @copyright Company Named Limited (c) 2026
20
+ */
21
+ export class Etc1sSelectorHistory {
22
+ /**
23
+ * @type {Int32Array}
24
+ */
25
+ #values = null;
26
+
27
+ /**
28
+ * Where {@link add} writes next. Sweeps the back half of {@link #values} and wraps to its
29
+ * middle, so the front half is only ever reached by {@link use} promoting something into it.
30
+ * @type {number}
31
+ */
32
+ #rover = 0;
33
+
34
+ /**
35
+ * @param {number} size from the slice tables section; may be zero, for a file whose blocks never
36
+ * refer to the history
37
+ */
38
+ constructor(size) {
39
+ this.#values = new Int32Array(size);
40
+ this.#rover = size >> 1;
41
+ }
42
+
43
+ /**
44
+ * @returns {number}
45
+ */
46
+ get size() {
47
+ return this.#values.length;
48
+ }
49
+
50
+ /**
51
+ * @param {number} index
52
+ * @returns {number} the selector codebook index remembered there
53
+ */
54
+ at(index) {
55
+ return this.#values[index];
56
+ }
57
+
58
+ /**
59
+ * Remember a selector index that was referred to by its codebook index.
60
+ *
61
+ * @param {number} value
62
+ */
63
+ add(value) {
64
+ this.#values[this.#rover++] = value;
65
+
66
+ if (this.#rover === this.#values.length) {
67
+ this.#rover = this.#values.length >> 1;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Promote the entry at `index`, having just referred to it.
73
+ *
74
+ * @param {number} index
75
+ */
76
+ use(index) {
77
+ if (index === 0) {
78
+ // already as far forward as the list goes, and halving would swap it with itself
79
+ return;
80
+ }
81
+
82
+ const half = index >> 1;
83
+
84
+ const promoted = this.#values[index];
85
+
86
+ this.#values[index] = this.#values[half];
87
+ this.#values[half] = promoted;
88
+ }
89
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The four colours a block's endpoint entry defines, which its selectors choose between.
3
+ *
4
+ * @param {Uint8Array} out receives twelve bytes, four `[r, g, b]` triples darkest first
5
+ * @param {number} out_offset
6
+ * @param {Uint8Array} color5 the endpoint codebook's channel array
7
+ * @param {Uint8Array} intensity the endpoint codebook's intensity array
8
+ * @param {number} endpoint_index
9
+ *
10
+ * @author Alex Goldring
11
+ * @copyright Company Named Limited (c) 2026
12
+ */
13
+ export function etc1s_block_colors(out: Uint8Array, out_offset: number, color5: Uint8Array, intensity: Uint8Array, endpoint_index: number): void;
14
+ //# sourceMappingURL=etc1s_block_colors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"etc1s_block_colors.d.ts","sourceRoot":"","sources":["../../../../src/basis/etc1s/etc1s_block_colors.js"],"names":[],"mappings":"AAgDA;;;;;;;;;;;GAWG;AACH,wCATW,UAAU,cACV,MAAM,UACN,UAAU,aACV,UAAU,kBACV,MAAM,QAmBhB"}