@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,75 @@
1
+ /**
2
+ * ETC1's intensity modifier table: what is added to every channel of the block's base colour, for
3
+ * each of the four selector values.
4
+ *
5
+ * This is ETC1's own table, not something Basis invented — ETC1S is a subset of ETC1 and its blocks
6
+ * decode identically on any ETC1 hardware. The four entries of a row are ordered darkest to
7
+ * brightest, which is the order a selector indexes them in.
8
+ *
9
+ * The same offset goes to all three channels, so a block's four colours lie on a line parallel to
10
+ * the grey axis. That is the fact the transcoders below rely on: a format with two endpoints and an
11
+ * interpolant can hold such a block almost exactly.
12
+ *
13
+ * @readonly
14
+ * @type {Int32Array}
15
+ * @see the ETC1 specification, and section 2.0 of the Basis specification
16
+ */
17
+ const INTENSITY_MODIFIER = new Int32Array([
18
+ -8, -2, 2, 8,
19
+ -17, -5, 5, 17,
20
+ -29, -9, 9, 29,
21
+ -42, -13, 13, 42,
22
+ -60, -18, 18, 60,
23
+ -80, -24, 24, 80,
24
+ -106, -33, 33, 106,
25
+ -183, -47, 47, 183,
26
+ ]);
27
+
28
+ /**
29
+ * Widen a five-bit channel to eight bits.
30
+ *
31
+ * Replicating the top three bits into the bottom rather than shifting alone is what makes 31 map to
32
+ * 255 instead of 248 — an endpoint of full white has to decode as white.
33
+ *
34
+ * @param {number} value five bits
35
+ * @returns {number} eight bits
36
+ */
37
+ function expand5(value) {
38
+ return (value << 3) | (value >> 2);
39
+ }
40
+
41
+ /**
42
+ * @param {number} value
43
+ * @returns {number} `value` brought into `[0, 255]`
44
+ */
45
+ function clamp255(value) {
46
+ return value < 0 ? 0 : value > 255 ? 255 : value;
47
+ }
48
+
49
+ /**
50
+ * The four colours a block's endpoint entry defines, which its selectors choose between.
51
+ *
52
+ * @param {Uint8Array} out receives twelve bytes, four `[r, g, b]` triples darkest first
53
+ * @param {number} out_offset
54
+ * @param {Uint8Array} color5 the endpoint codebook's channel array
55
+ * @param {Uint8Array} intensity the endpoint codebook's intensity array
56
+ * @param {number} endpoint_index
57
+ *
58
+ * @author Alex Goldring
59
+ * @copyright Company Named Limited (c) 2026
60
+ */
61
+ export function etc1s_block_colors(out, out_offset, color5, intensity, endpoint_index) {
62
+ const r = expand5(color5[endpoint_index * 3]);
63
+ const g = expand5(color5[endpoint_index * 3 + 1]);
64
+ const b = expand5(color5[endpoint_index * 3 + 2]);
65
+
66
+ const row = intensity[endpoint_index] * 4;
67
+
68
+ for (let selector = 0; selector < 4; selector++) {
69
+ const modifier = INTENSITY_MODIFIER[row + selector];
70
+
71
+ out[out_offset + selector * 3] = clamp255(r + modifier);
72
+ out[out_offset + selector * 3 + 1] = clamp255(g + modifier);
73
+ out[out_offset + selector * 3 + 2] = clamp255(b + modifier);
74
+ }
75
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * One slice's blocks, as indices into the two codebooks.
3
+ *
4
+ * @typedef {object} Etc1sSlice
5
+ * @property {Uint16Array} endpoint_index one per block, in raster order
6
+ * @property {Uint16Array} selector_index one per block, in raster order
7
+ * @property {number} blocks_x
8
+ * @property {number} blocks_y
9
+ */
10
+ /**
11
+ * Decode one ETC1S slice into per-block codebook indices.
12
+ *
13
+ * A slice is a 2D array of blocks in raster order, and each block is two codebook references: which
14
+ * base colour and intensity it uses, and which arrangement of the four resulting colours. Neither
15
+ * reference is sent outright when it can be predicted, which is what the endpoint predictor and the
16
+ * selector history between them exist to exploit.
17
+ *
18
+ * **This decodes references, not texels.** Turning a pair of indices into colour is the transcoder's
19
+ * job and depends on what it is transcoding to, so the two are kept apart — the slice decode is
20
+ * identical whether the destination is BC1, BC7 or a debug image.
21
+ *
22
+ * **Texture video is not decoded.** The format lets a block copy both indices from the block at the
23
+ * same position in the previous frame, which needs the previous frame kept and the frames decoded in
24
+ * order. Nothing here plays video textures, and a still image never sets the flag, so the caller
25
+ * refuses such a file rather than this function silently reading predictor 2 as "upper left" and
26
+ * producing a frame that is wrong wherever the encoder skipped a block.
27
+ *
28
+ * @param {Uint8Array} data the slice's bytes
29
+ * @param {number} blocks_x
30
+ * @param {number} blocks_y
31
+ * @param {Etc1sSliceTables} tables
32
+ * @param {number} endpoint_count entries in the endpoint codebook
33
+ * @param {number} selector_count entries in the selector codebook
34
+ * @returns {Etc1sSlice}
35
+ * @throws {Error} for a slice whose bitstream runs out, or which refers past either codebook
36
+ * @see the specification, section 10.3
37
+ *
38
+ * @author Alex Goldring
39
+ * @copyright Company Named Limited (c) 2026
40
+ */
41
+ export function etc1s_decode_slice(data: Uint8Array, blocks_x: number, blocks_y: number, tables: Etc1sSliceTables, endpoint_count: number, selector_count: number): Etc1sSlice;
42
+ /**
43
+ * One slice's blocks, as indices into the two codebooks.
44
+ */
45
+ export type Etc1sSlice = {
46
+ /**
47
+ * one per block, in raster order
48
+ */
49
+ endpoint_index: Uint16Array;
50
+ /**
51
+ * one per block, in raster order
52
+ */
53
+ selector_index: Uint16Array;
54
+ blocks_x: number;
55
+ blocks_y: number;
56
+ };
57
+ //# sourceMappingURL=etc1s_decode_slice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"etc1s_decode_slice.d.ts","sourceRoot":"","sources":["../../../../src/basis/etc1s/etc1s_decode_slice.js"],"names":[],"mappings":"AAsDA;;;;;;;;GAQG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,yCAbW,UAAU,YACV,MAAM,YACN,MAAM,4CAEN,MAAM,kBACN,MAAM,GACJ,UAAU,CAyKtB;;;;;;;;oBAvMa,WAAW;;;;oBACX,WAAW;cACX,MAAM;cACN,MAAM"}
@@ -0,0 +1,258 @@
1
+ import { BasisBitReader } from "../BasisBitReader.js";
2
+ import { Etc1sSelectorHistory } from "./Etc1sSelectorHistory.js";
3
+
4
+ /**
5
+ * The endpoint predictor symbol that means "repeat the last one, several times over". One past the
6
+ * 256 real symbols, which are four two-bit predictors packed into a byte.
7
+ *
8
+ * @readonly
9
+ * @type {number}
10
+ */
11
+ const ENDPOINT_PRED_REPEAT_LAST_SYMBOL = 4 * 4 * 4 * 4;
12
+
13
+ /**
14
+ * Shortest run the repeat symbol is used for, which is therefore what a decoded run length is
15
+ * counted from.
16
+ *
17
+ * @readonly
18
+ * @type {number}
19
+ */
20
+ const ENDPOINT_PRED_MIN_REPEAT_COUNT = 3;
21
+
22
+ /**
23
+ * Chunk width of the variable-length code carrying an endpoint predictor run length.
24
+ *
25
+ * @readonly
26
+ * @type {number}
27
+ */
28
+ const ENDPOINT_PRED_COUNT_VLC_BITS = 4;
29
+
30
+ /**
31
+ * Shortest selector run, counted from for the same reason as the endpoint one.
32
+ *
33
+ * @readonly
34
+ * @type {number}
35
+ */
36
+ const SELECTOR_HISTORY_RLE_COUNT_THRESH = 3;
37
+
38
+ /**
39
+ * Run lengths the selector run table can name directly. The largest is reserved to mean "longer than
40
+ * this", and is followed by a variable-length code.
41
+ *
42
+ * @readonly
43
+ * @type {number}
44
+ */
45
+ const SELECTOR_HISTORY_RLE_COUNT_TOTAL = 1 << 6;
46
+
47
+ /**
48
+ * Chunk width of the variable-length code carrying a long selector run length.
49
+ *
50
+ * @readonly
51
+ * @type {number}
52
+ */
53
+ const SELECTOR_HISTORY_RLE_VLC_BITS = 7;
54
+
55
+ /**
56
+ * One slice's blocks, as indices into the two codebooks.
57
+ *
58
+ * @typedef {object} Etc1sSlice
59
+ * @property {Uint16Array} endpoint_index one per block, in raster order
60
+ * @property {Uint16Array} selector_index one per block, in raster order
61
+ * @property {number} blocks_x
62
+ * @property {number} blocks_y
63
+ */
64
+
65
+ /**
66
+ * Decode one ETC1S slice into per-block codebook indices.
67
+ *
68
+ * A slice is a 2D array of blocks in raster order, and each block is two codebook references: which
69
+ * base colour and intensity it uses, and which arrangement of the four resulting colours. Neither
70
+ * reference is sent outright when it can be predicted, which is what the endpoint predictor and the
71
+ * selector history between them exist to exploit.
72
+ *
73
+ * **This decodes references, not texels.** Turning a pair of indices into colour is the transcoder's
74
+ * job and depends on what it is transcoding to, so the two are kept apart — the slice decode is
75
+ * identical whether the destination is BC1, BC7 or a debug image.
76
+ *
77
+ * **Texture video is not decoded.** The format lets a block copy both indices from the block at the
78
+ * same position in the previous frame, which needs the previous frame kept and the frames decoded in
79
+ * order. Nothing here plays video textures, and a still image never sets the flag, so the caller
80
+ * refuses such a file rather than this function silently reading predictor 2 as "upper left" and
81
+ * producing a frame that is wrong wherever the encoder skipped a block.
82
+ *
83
+ * @param {Uint8Array} data the slice's bytes
84
+ * @param {number} blocks_x
85
+ * @param {number} blocks_y
86
+ * @param {Etc1sSliceTables} tables
87
+ * @param {number} endpoint_count entries in the endpoint codebook
88
+ * @param {number} selector_count entries in the selector codebook
89
+ * @returns {Etc1sSlice}
90
+ * @throws {Error} for a slice whose bitstream runs out, or which refers past either codebook
91
+ * @see the specification, section 10.3
92
+ *
93
+ * @author Alex Goldring
94
+ * @copyright Company Named Limited (c) 2026
95
+ */
96
+ export function etc1s_decode_slice(
97
+ data, blocks_x, blocks_y, tables, endpoint_count, selector_count
98
+ ) {
99
+ const reader = new BasisBitReader().init(data);
100
+
101
+ const block_count = blocks_x * blocks_y;
102
+
103
+ const endpoint_index = new Uint16Array(block_count);
104
+ const selector_index = new Uint16Array(block_count);
105
+
106
+ /*
107
+ Two rows of per-block state, indexed by the low bit of the row: the row being decoded and the
108
+ one above it. A block predicting "the same endpoint as the block above" reads the second, and a
109
+ 2x2 predictor group decoded on an even row leaves its lower half's predictor bits there for the
110
+ odd row beneath to pick up.
111
+ */
112
+ const pred_endpoint_index = [new Uint16Array(blocks_x), new Uint16Array(blocks_x)];
113
+ const pred_bits_row = [new Uint8Array(blocks_x), new Uint8Array(blocks_x)];
114
+
115
+ const selector_history = new Etc1sSelectorHistory(tables.selector_history_size);
116
+
117
+ let current_pred_bits = 0;
118
+ let previous_endpoint_pred_symbol = 0;
119
+ let endpoint_pred_repeat_count = 0;
120
+ let previous_endpoint_index = 0;
121
+ let selector_rle_count = 0;
122
+
123
+ for (let block_y = 0; block_y < blocks_y; block_y++) {
124
+ const current_row = block_y & 1;
125
+
126
+ for (let block_x = 0; block_x < blocks_x; block_x++) {
127
+ if ((block_x & 1) === 0) {
128
+ if ((block_y & 1) === 0) {
129
+ /*
130
+ One symbol carries the predictor for a whole 2x2 group of blocks — two bits
131
+ each, eight bits in all. The top half is used now; the bottom half is put aside
132
+ for the odd row, which is why no symbol is read there.
133
+ */
134
+ if (endpoint_pred_repeat_count > 0) {
135
+ endpoint_pred_repeat_count--;
136
+ current_pred_bits = previous_endpoint_pred_symbol;
137
+ } else {
138
+ current_pred_bits = tables.endpoint_pred.decode(reader);
139
+
140
+ if (current_pred_bits === ENDPOINT_PRED_REPEAT_LAST_SYMBOL) {
141
+ endpoint_pred_repeat_count = reader.vlc(ENDPOINT_PRED_COUNT_VLC_BITS)
142
+ + ENDPOINT_PRED_MIN_REPEAT_COUNT - 1;
143
+
144
+ current_pred_bits = previous_endpoint_pred_symbol;
145
+ } else {
146
+ previous_endpoint_pred_symbol = current_pred_bits;
147
+ }
148
+ }
149
+
150
+ pred_bits_row[current_row ^ 1][block_x] = current_pred_bits >> 4;
151
+ } else {
152
+ current_pred_bits = pred_bits_row[current_row][block_x];
153
+ }
154
+ }
155
+
156
+ const pred = current_pred_bits & 3;
157
+
158
+ current_pred_bits >>= 2;
159
+
160
+ let block_endpoint_index;
161
+
162
+ if (pred === 0) {
163
+ block_endpoint_index = previous_endpoint_index;
164
+ } else if (pred === 1) {
165
+ block_endpoint_index = pred_endpoint_index[current_row ^ 1][block_x];
166
+ } else if (pred === 2) {
167
+ block_endpoint_index = pred_endpoint_index[current_row ^ 1][block_x - 1];
168
+ } else {
169
+ const delta = tables.delta_endpoint.decode(reader);
170
+
171
+ block_endpoint_index = delta + previous_endpoint_index;
172
+
173
+ /*
174
+ The delta is against a codebook that wraps rather than saturates, so an index past
175
+ the end comes back around. Subtracting once is enough because the symbol alphabet is
176
+ the codebook size, so the sum is below twice it.
177
+ */
178
+ if (block_endpoint_index >= endpoint_count) {
179
+ block_endpoint_index -= endpoint_count;
180
+ }
181
+ }
182
+
183
+ if (block_endpoint_index >= endpoint_count) {
184
+ throw new Error(
185
+ `Basis: a block refers to endpoint ${block_endpoint_index} of a`
186
+ + ` ${endpoint_count} entry codebook`
187
+ );
188
+ }
189
+
190
+ pred_endpoint_index[current_row][block_x] = block_endpoint_index;
191
+ previous_endpoint_index = block_endpoint_index;
192
+
193
+ let selector_symbol;
194
+
195
+ if (selector_rle_count > 0) {
196
+ selector_rle_count--;
197
+
198
+ // a run repeats the *history* reference, which resolves below to its front entry
199
+ selector_symbol = selector_count;
200
+ } else {
201
+ selector_symbol = tables.selector.decode(reader);
202
+
203
+ const rle_symbol = selector_count + selector_history.size;
204
+
205
+ if (selector_symbol === rle_symbol) {
206
+ const run = tables.selector_history_rle.decode(reader);
207
+
208
+ selector_rle_count = run === SELECTOR_HISTORY_RLE_COUNT_TOTAL - 1
209
+ ? reader.vlc(SELECTOR_HISTORY_RLE_VLC_BITS)
210
+ + SELECTOR_HISTORY_RLE_COUNT_THRESH
211
+ : run + SELECTOR_HISTORY_RLE_COUNT_THRESH;
212
+
213
+ selector_symbol = selector_count;
214
+
215
+ // this block is the run's first, so it is already accounted for
216
+ selector_rle_count--;
217
+ }
218
+ }
219
+
220
+ let block_selector_index;
221
+
222
+ if (selector_symbol >= selector_count) {
223
+ const history_index = selector_symbol - selector_count;
224
+
225
+ if (history_index >= selector_history.size) {
226
+ throw new Error(
227
+ `Basis: a block refers to entry ${history_index} of a`
228
+ + ` ${selector_history.size} entry selector history`
229
+ );
230
+ }
231
+
232
+ block_selector_index = selector_history.at(history_index);
233
+
234
+ selector_history.use(history_index);
235
+ } else {
236
+ block_selector_index = selector_symbol;
237
+
238
+ if (selector_history.size > 0) {
239
+ selector_history.add(block_selector_index);
240
+ }
241
+ }
242
+
243
+ if (block_selector_index >= selector_count) {
244
+ throw new Error(
245
+ `Basis: a block refers to selector ${block_selector_index} of a`
246
+ + ` ${selector_count} entry codebook`
247
+ );
248
+ }
249
+
250
+ const block = block_y * blocks_x + block_x;
251
+
252
+ endpoint_index[block] = block_endpoint_index;
253
+ selector_index[block] = block_selector_index;
254
+ }
255
+ }
256
+
257
+ return { endpoint_index, selector_index, blocks_x, blocks_y };
258
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * One decoded endpoint codebook.
3
+ *
4
+ * Stored as two flat arrays rather than an array of objects: there may be up to sixteen thousand
5
+ * entries and every block decode indexes one, so the shape that avoids an object per entry is worth
6
+ * the slightly noisier access.
7
+ *
8
+ * @typedef {object} Etc1sEndpointCodebook
9
+ * @property {Uint8Array} color5 three five-bit channels per entry, `[r, g, b]` interleaved
10
+ * @property {Uint8Array} intensity one three-bit table index per entry
11
+ * @property {number} count
12
+ */
13
+ /**
14
+ * Read the ETC1S colour endpoint codebook.
15
+ *
16
+ * Each entry is an ETC1S block's base colour — five bits per channel — and its intensity table
17
+ * index. Both are sent as deltas against the previous entry, so the codebook has to be read in one
18
+ * pass from the start and an entry cannot be decoded on its own.
19
+ *
20
+ * @param {Uint8Array} data the endpoints section of the global data
21
+ * @param {number} count how many entries the section holds, from the global data header
22
+ * @returns {Etc1sEndpointCodebook}
23
+ * @throws {Error} for a section whose bitstream runs out before the entries do
24
+ * @see the specification, section 7.0
25
+ *
26
+ * @author Alex Goldring
27
+ * @copyright Company Named Limited (c) 2026
28
+ */
29
+ export function etc1s_read_endpoint_codebook(data: Uint8Array, count: number): Etc1sEndpointCodebook;
30
+ /**
31
+ * One decoded endpoint codebook.
32
+ *
33
+ * Stored as two flat arrays rather than an array of objects: there may be up to sixteen thousand
34
+ * entries and every block decode indexes one, so the shape that avoids an object per entry is worth
35
+ * the slightly noisier access.
36
+ */
37
+ export type Etc1sEndpointCodebook = {
38
+ /**
39
+ * three five-bit channels per entry, `[r, g, b]` interleaved
40
+ */
41
+ color5: Uint8Array;
42
+ /**
43
+ * one three-bit table index per entry
44
+ */
45
+ intensity: Uint8Array;
46
+ count: number;
47
+ };
48
+ //# sourceMappingURL=etc1s_read_endpoint_codebook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"etc1s_read_endpoint_codebook.d.ts","sourceRoot":"","sources":["../../../../src/basis/etc1s/etc1s_read_endpoint_codebook.js"],"names":[],"mappings":"AAsBA;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,mDATW,UAAU,SACV,MAAM,GACJ,qBAAqB,CAkEjC;;;;;;;;;;;;YAhFa,UAAU;;;;eACV,UAAU;WACV,MAAM"}
@@ -0,0 +1,111 @@
1
+ import { BasisBitReader } from "../BasisBitReader.js";
2
+ import { basis_read_huffman_table } from "../basis_read_huffman_table.js";
3
+
4
+ /**
5
+ * Upper bound on the previous channel value that selects the first of the three delta models, and
6
+ * likewise for the second. Past both, the third is used.
7
+ *
8
+ * Three models rather than one because the delta's *range* depends on where the previous value sat:
9
+ * a channel already near zero can only go up, one near thirty-one can only go down, and giving each
10
+ * case its own statistics is most of what makes the codebook small.
11
+ *
12
+ * @readonly
13
+ * @type {number}
14
+ */
15
+ const COLOR5_PAL0_PREV_HI = 9;
16
+
17
+ /**
18
+ * @readonly
19
+ * @type {number}
20
+ */
21
+ const COLOR5_PAL1_PREV_HI = 21;
22
+
23
+ /**
24
+ * One decoded endpoint codebook.
25
+ *
26
+ * Stored as two flat arrays rather than an array of objects: there may be up to sixteen thousand
27
+ * entries and every block decode indexes one, so the shape that avoids an object per entry is worth
28
+ * the slightly noisier access.
29
+ *
30
+ * @typedef {object} Etc1sEndpointCodebook
31
+ * @property {Uint8Array} color5 three five-bit channels per entry, `[r, g, b]` interleaved
32
+ * @property {Uint8Array} intensity one three-bit table index per entry
33
+ * @property {number} count
34
+ */
35
+
36
+ /**
37
+ * Read the ETC1S colour endpoint codebook.
38
+ *
39
+ * Each entry is an ETC1S block's base colour — five bits per channel — and its intensity table
40
+ * index. Both are sent as deltas against the previous entry, so the codebook has to be read in one
41
+ * pass from the start and an entry cannot be decoded on its own.
42
+ *
43
+ * @param {Uint8Array} data the endpoints section of the global data
44
+ * @param {number} count how many entries the section holds, from the global data header
45
+ * @returns {Etc1sEndpointCodebook}
46
+ * @throws {Error} for a section whose bitstream runs out before the entries do
47
+ * @see the specification, section 7.0
48
+ *
49
+ * @author Alex Goldring
50
+ * @copyright Company Named Limited (c) 2026
51
+ */
52
+ export function etc1s_read_endpoint_codebook(data, count) {
53
+ const reader = new BasisBitReader().init(data);
54
+
55
+ const color5_delta_model = [
56
+ basis_read_huffman_table(reader),
57
+ basis_read_huffman_table(reader),
58
+ basis_read_huffman_table(reader),
59
+ ];
60
+
61
+ const intensity_delta_model = basis_read_huffman_table(reader);
62
+
63
+ const grayscale = reader.bits(1) !== 0;
64
+
65
+ const color5 = new Uint8Array(count * 3);
66
+ const intensity = new Uint8Array(count);
67
+
68
+ /*
69
+ The specification's starting state, and it is a state rather than a convention: the first
70
+ entry's delta is against these, so a decoder that started from zero would produce a codebook
71
+ that is wrong everywhere rather than wrong in its first entry.
72
+ */
73
+ const previous_color5 = [16, 16, 16];
74
+
75
+ let previous_intensity = 0;
76
+
77
+ for (let i = 0; i < count; i++) {
78
+ previous_intensity = (intensity_delta_model.decode(reader) + previous_intensity) & 7;
79
+
80
+ intensity[i] = previous_intensity;
81
+
82
+ const channels = grayscale ? 1 : 3;
83
+
84
+ for (let c = 0; c < channels; c++) {
85
+ const previous = previous_color5[c];
86
+
87
+ const model = previous <= COLOR5_PAL0_PREV_HI
88
+ ? color5_delta_model[0]
89
+ : previous <= COLOR5_PAL1_PREV_HI
90
+ ? color5_delta_model[1]
91
+ : color5_delta_model[2];
92
+
93
+ const value = (previous + model.decode(reader)) & 31;
94
+
95
+ color5[i * 3 + c] = value;
96
+ previous_color5[c] = value;
97
+ }
98
+
99
+ if (grayscale) {
100
+ /*
101
+ A grayscale codebook sends one channel and means three. The previous-value state stays
102
+ one channel wide — the next entry's delta is against R alone — so the copy is into the
103
+ output only.
104
+ */
105
+ color5[i * 3 + 1] = color5[i * 3];
106
+ color5[i * 3 + 2] = color5[i * 3];
107
+ }
108
+ }
109
+
110
+ return { color5, intensity, count };
111
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * One decoded selector codebook.
3
+ *
4
+ * @typedef {object} Etc1sSelectorCodebook
5
+ * @property {Uint8Array} selectors sixteen two-bit selectors per entry, one byte each, in
6
+ * raster order within the block
7
+ * @property {number} count
8
+ */
9
+ /**
10
+ * Read the ETC1S selector codebook.
11
+ *
12
+ * Each entry is a block's sixteen two-bit selectors — which of the four colours the block's endpoint
13
+ * defines each texel takes. The entries are stored as four bytes, one per row of the block, and are
14
+ * either raw or coded as a byte-wise exclusive-or against the previous entry.
15
+ *
16
+ * **Selectors are stored one per byte rather than packed.** Four times the memory of the packed form
17
+ * and worth it: every block transcode reads all sixteen, and unpacking them here once is unpacking
18
+ * them once per codebook entry rather than once per block that references it.
19
+ *
20
+ * @param {Uint8Array} data the selectors section of the global data
21
+ * @param {number} count how many entries the section holds, from the global data header
22
+ * @returns {Etc1sSelectorCodebook}
23
+ * @throws {Error} for the global and hybrid codebook forms, which the format defines and no encoder
24
+ * produces
25
+ * @see the specification, section 8.0
26
+ *
27
+ * @author Alex Goldring
28
+ * @copyright Company Named Limited (c) 2026
29
+ */
30
+ export function etc1s_read_selector_codebook(data: Uint8Array, count: number): Etc1sSelectorCodebook;
31
+ /**
32
+ * One decoded selector codebook.
33
+ */
34
+ export type Etc1sSelectorCodebook = {
35
+ /**
36
+ * sixteen two-bit selectors per entry, one byte each, in
37
+ * raster order within the block
38
+ */
39
+ selectors: Uint8Array;
40
+ count: number;
41
+ };
42
+ //# sourceMappingURL=etc1s_read_selector_codebook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"etc1s_read_selector_codebook.d.ts","sourceRoot":"","sources":["../../../../src/basis/etc1s/etc1s_read_selector_codebook.js"],"names":[],"mappings":"AAmBA;;;;;;;GAOG;AAEH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,mDAVW,UAAU,SACV,MAAM,GACJ,qBAAqB,CAqEjC;;;;;;;;;eAvFa,UAAU;WAEV,MAAM"}