@woosh/meep-engine 3.9.0 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (696) hide show
  1. package/build/bundle-worker-image-decoder.js +1 -1
  2. package/build/bundle-worker-terrain.js +1 -1
  3. package/package.json +3 -2
  4. package/src/avif/encode_image_source.d.ts +70 -0
  5. package/src/avif/encode_image_source.d.ts.map +1 -0
  6. package/src/avif/encode_image_source.js +117 -0
  7. package/src/avif/encoder_worker.js +15 -126
  8. package/src/avif/index.d.ts +4 -2
  9. package/src/avif/index.js +21 -2
  10. package/src/avif/native/DECISIONS.md +537 -0
  11. package/src/avif/native/NOTICE.md +61 -0
  12. package/src/avif/native/api/AvifDecoder.d.ts +67 -0
  13. package/src/avif/native/api/AvifDecoder.d.ts.map +1 -0
  14. package/src/avif/native/api/AvifDecoder.js +121 -0
  15. package/src/avif/native/api/apply_transformations.d.ts +30 -0
  16. package/src/avif/native/api/apply_transformations.d.ts.map +1 -0
  17. package/src/avif/native/api/apply_transformations.js +120 -0
  18. package/src/avif/native/api/avif_to_sampler2d.d.ts +25 -0
  19. package/src/avif/native/api/avif_to_sampler2d.d.ts.map +1 -0
  20. package/src/avif/native/api/avif_to_sampler2d.js +42 -0
  21. package/src/avif/native/api/convert_to_rgba.d.ts +54 -0
  22. package/src/avif/native/api/convert_to_rgba.d.ts.map +1 -0
  23. package/src/avif/native/api/convert_to_rgba.js +204 -0
  24. package/src/avif/native/api/decode_avif.d.ts +24 -0
  25. package/src/avif/native/api/decode_avif.d.ts.map +1 -0
  26. package/src/avif/native/api/decode_avif.js +111 -0
  27. package/src/avif/native/api/decode_image_item.d.ts +18 -0
  28. package/src/avif/native/api/decode_image_item.d.ts.map +1 -0
  29. package/src/avif/native/api/decode_image_item.js +234 -0
  30. package/src/avif/native/api/encode_avif.d.ts +73 -0
  31. package/src/avif/native/api/encode_avif.d.ts.map +1 -0
  32. package/src/avif/native/api/encode_avif.js +488 -0
  33. package/src/avif/native/api/sampler2d_to_avif.d.ts +22 -0
  34. package/src/avif/native/api/sampler2d_to_avif.d.ts.map +1 -0
  35. package/src/avif/native/api/sampler2d_to_avif.js +108 -0
  36. package/src/avif/native/av1/decode/Av1FrameContext.d.ts +286 -0
  37. package/src/avif/native/av1/decode/Av1FrameContext.d.ts.map +1 -0
  38. package/src/avif/native/av1/decode/Av1FrameContext.js +622 -0
  39. package/src/avif/native/av1/decode/block_decoded.d.ts +27 -0
  40. package/src/avif/native/av1/decode/block_decoded.d.ts.map +1 -0
  41. package/src/avif/native/av1/decode/block_decoded.js +59 -0
  42. package/src/avif/native/av1/decode/decode_av1_still.d.ts +22 -0
  43. package/src/avif/native/av1/decode/decode_av1_still.d.ts.map +1 -0
  44. package/src/avif/native/av1/decode/decode_av1_still.js +309 -0
  45. package/src/avif/native/av1/decode/decode_coefficients.d.ts +20 -0
  46. package/src/avif/native/av1/decode/decode_coefficients.d.ts.map +1 -0
  47. package/src/avif/native/av1/decode/decode_coefficients.js +259 -0
  48. package/src/avif/native/av1/decode/decode_palette.d.ts +28 -0
  49. package/src/avif/native/av1/decode/decode_palette.d.ts.map +1 -0
  50. package/src/avif/native/av1/decode/decode_palette.js +442 -0
  51. package/src/avif/native/av1/decode/decode_tile.d.ts +14 -0
  52. package/src/avif/native/av1/decode/decode_tile.d.ts.map +1 -0
  53. package/src/avif/native/av1/decode/decode_tile.js +1213 -0
  54. package/src/avif/native/av1/decode/get_scan.d.ts +16 -0
  55. package/src/avif/native/av1/decode/get_scan.d.ts.map +1 -0
  56. package/src/avif/native/av1/decode/get_scan.js +138 -0
  57. package/src/avif/native/av1/decode/read_lr.d.ts +35 -0
  58. package/src/avif/native/av1/decode/read_lr.d.ts.map +1 -0
  59. package/src/avif/native/av1/decode/read_lr.js +277 -0
  60. package/src/avif/native/av1/decode/read_transform_type.d.ts +19 -0
  61. package/src/avif/native/av1/decode/read_transform_type.d.ts.map +1 -0
  62. package/src/avif/native/av1/decode/read_transform_type.js +92 -0
  63. package/src/avif/native/av1/decode/reconstruct.d.ts +36 -0
  64. package/src/avif/native/av1/decode/reconstruct.d.ts.map +1 -0
  65. package/src/avif/native/av1/decode/reconstruct.js +241 -0
  66. package/src/avif/native/av1/decode/transform_type.d.ts +49 -0
  67. package/src/avif/native/av1/decode/transform_type.d.ts.map +1 -0
  68. package/src/avif/native/av1/decode/transform_type.js +137 -0
  69. package/src/avif/native/av1/encode/Av1BlockSnapshot.d.ts +81 -0
  70. package/src/avif/native/av1/encode/Av1BlockSnapshot.d.ts.map +1 -0
  71. package/src/avif/native/av1/encode/Av1BlockSnapshot.js +299 -0
  72. package/src/avif/native/av1/encode/Av1EncodeContext.d.ts +384 -0
  73. package/src/avif/native/av1/encode/Av1EncodeContext.d.ts.map +1 -0
  74. package/src/avif/native/av1/encode/Av1EncodeContext.js +590 -0
  75. package/src/avif/native/av1/encode/build_headers.d.ts +50 -0
  76. package/src/avif/native/av1/encode/build_headers.d.ts.map +1 -0
  77. package/src/avif/native/av1/encode/build_headers.js +359 -0
  78. package/src/avif/native/av1/encode/choose_chroma_alpha.d.ts +26 -0
  79. package/src/avif/native/av1/encode/choose_chroma_alpha.d.ts.map +1 -0
  80. package/src/avif/native/av1/encode/choose_chroma_alpha.js +90 -0
  81. package/src/avif/native/av1/encode/choose_filters.d.ts +21 -0
  82. package/src/avif/native/av1/encode/choose_filters.d.ts.map +1 -0
  83. package/src/avif/native/av1/encode/choose_filters.js +318 -0
  84. package/src/avif/native/av1/encode/choose_restoration.d.ts +35 -0
  85. package/src/avif/native/av1/encode/choose_restoration.d.ts.map +1 -0
  86. package/src/avif/native/av1/encode/choose_restoration.js +616 -0
  87. package/src/avif/native/av1/encode/decode_effort.d.ts +57 -0
  88. package/src/avif/native/av1/encode/decode_effort.d.ts.map +1 -0
  89. package/src/avif/native/av1/encode/decode_effort.js +59 -0
  90. package/src/avif/native/av1/encode/encode_av1_still.d.ts +17 -0
  91. package/src/avif/native/av1/encode/encode_av1_still.d.ts.map +1 -0
  92. package/src/avif/native/av1/encode/encode_av1_still.js +194 -0
  93. package/src/avif/native/av1/encode/encode_effort.d.ts +56 -0
  94. package/src/avif/native/av1/encode/encode_effort.d.ts.map +1 -0
  95. package/src/avif/native/av1/encode/encode_effort.js +97 -0
  96. package/src/avif/native/av1/encode/encode_tile.d.ts +26 -0
  97. package/src/avif/native/av1/encode/encode_tile.d.ts.map +1 -0
  98. package/src/avif/native/av1/encode/encode_tile.js +2331 -0
  99. package/src/avif/native/av1/encode/quantise.d.ts +51 -0
  100. package/src/avif/native/av1/encode/quantise.d.ts.map +1 -0
  101. package/src/avif/native/av1/encode/quantise.js +212 -0
  102. package/src/avif/native/av1/encode/write_coefficients.d.ts +42 -0
  103. package/src/avif/native/av1/encode/write_coefficients.d.ts.map +1 -0
  104. package/src/avif/native/av1/encode/write_coefficients.js +367 -0
  105. package/src/avif/native/av1/encode/write_lr.d.ts +33 -0
  106. package/src/avif/native/av1/encode/write_lr.d.ts.map +1 -0
  107. package/src/avif/native/av1/encode/write_lr.js +174 -0
  108. package/src/avif/native/av1/encode/write_transform_type.d.ts +21 -0
  109. package/src/avif/native/av1/encode/write_transform_type.d.ts.map +1 -0
  110. package/src/avif/native/av1/encode/write_transform_type.js +70 -0
  111. package/src/avif/native/av1/entropy/CdfContext.d.ts +167 -0
  112. package/src/avif/native/av1/entropy/CdfContext.d.ts.map +1 -0
  113. package/src/avif/native/av1/entropy/CdfContext.js +433 -0
  114. package/src/avif/native/av1/entropy/SymbolReader.d.ts +103 -0
  115. package/src/avif/native/av1/entropy/SymbolReader.d.ts.map +1 -0
  116. package/src/avif/native/av1/entropy/SymbolReader.js +317 -0
  117. package/src/avif/native/av1/entropy/SymbolWriter.d.ts +107 -0
  118. package/src/avif/native/av1/entropy/SymbolWriter.d.ts.map +1 -0
  119. package/src/avif/native/av1/entropy/SymbolWriter.js +352 -0
  120. package/src/avif/native/av1/entropy/coefficient_context.d.ts +71 -0
  121. package/src/avif/native/av1/entropy/coefficient_context.d.ts.map +1 -0
  122. package/src/avif/native/av1/entropy/coefficient_context.js +325 -0
  123. package/src/avif/native/av1/entropy/partition_cdf.d.ts +54 -0
  124. package/src/avif/native/av1/entropy/partition_cdf.d.ts.map +1 -0
  125. package/src/avif/native/av1/entropy/partition_cdf.js +98 -0
  126. package/src/avif/native/av1/entropy/symbol_cost.d.ts +37 -0
  127. package/src/avif/native/av1/entropy/symbol_cost.d.ts.map +1 -0
  128. package/src/avif/native/av1/entropy/symbol_cost.js +76 -0
  129. package/src/avif/native/av1/filter/cdef.d.ts +15 -0
  130. package/src/avif/native/av1/filter/cdef.d.ts.map +1 -0
  131. package/src/avif/native/av1/filter/cdef.js +313 -0
  132. package/src/avif/native/av1/filter/loop_filter.d.ts +14 -0
  133. package/src/avif/native/av1/filter/loop_filter.d.ts.map +1 -0
  134. package/src/avif/native/av1/filter/loop_filter.js +377 -0
  135. package/src/avif/native/av1/filter/loop_restoration.d.ts +42 -0
  136. package/src/avif/native/av1/filter/loop_restoration.d.ts.map +1 -0
  137. package/src/avif/native/av1/filter/loop_restoration.js +451 -0
  138. package/src/avif/native/av1/filter/superres.d.ts +18 -0
  139. package/src/avif/native/av1/filter/superres.d.ts.map +1 -0
  140. package/src/avif/native/av1/filter/superres.js +86 -0
  141. package/src/avif/native/av1/grain/FilmGrainState.d.ts +62 -0
  142. package/src/avif/native/av1/grain/FilmGrainState.d.ts.map +1 -0
  143. package/src/avif/native/av1/grain/FilmGrainState.js +87 -0
  144. package/src/avif/native/av1/grain/film_grain.d.ts +16 -0
  145. package/src/avif/native/av1/grain/film_grain.d.ts.map +1 -0
  146. package/src/avif/native/av1/grain/film_grain.js +623 -0
  147. package/src/avif/native/av1/obu/FrameHeader.d.ts +248 -0
  148. package/src/avif/native/av1/obu/FrameHeader.d.ts.map +1 -0
  149. package/src/avif/native/av1/obu/FrameHeader.js +381 -0
  150. package/src/avif/native/av1/obu/ObuHeader.d.ts +53 -0
  151. package/src/avif/native/av1/obu/ObuHeader.d.ts.map +1 -0
  152. package/src/avif/native/av1/obu/ObuHeader.js +62 -0
  153. package/src/avif/native/av1/obu/SequenceHeader.d.ts +157 -0
  154. package/src/avif/native/av1/obu/SequenceHeader.d.ts.map +1 -0
  155. package/src/avif/native/av1/obu/SequenceHeader.js +225 -0
  156. package/src/avif/native/av1/obu/for_each_obu.d.ts +24 -0
  157. package/src/avif/native/av1/obu/for_each_obu.d.ts.map +1 -0
  158. package/src/avif/native/av1/obu/for_each_obu.js +50 -0
  159. package/src/avif/native/av1/obu/parse_frame_header.d.ts +18 -0
  160. package/src/avif/native/av1/obu/parse_frame_header.d.ts.map +1 -0
  161. package/src/avif/native/av1/obu/parse_frame_header.js +841 -0
  162. package/src/avif/native/av1/obu/parse_obu_header.d.ts +23 -0
  163. package/src/avif/native/av1/obu/parse_obu_header.d.ts.map +1 -0
  164. package/src/avif/native/av1/obu/parse_obu_header.js +69 -0
  165. package/src/avif/native/av1/obu/parse_sequence_header.d.ts +17 -0
  166. package/src/avif/native/av1/obu/parse_sequence_header.d.ts.map +1 -0
  167. package/src/avif/native/av1/obu/parse_sequence_header.js +263 -0
  168. package/src/avif/native/av1/obu/write_frame_header.d.ts +16 -0
  169. package/src/avif/native/av1/obu/write_frame_header.d.ts.map +1 -0
  170. package/src/avif/native/av1/obu/write_frame_header.js +579 -0
  171. package/src/avif/native/av1/obu/write_sequence_header.d.ts +17 -0
  172. package/src/avif/native/av1/obu/write_sequence_header.d.ts.map +1 -0
  173. package/src/avif/native/av1/obu/write_sequence_header.js +189 -0
  174. package/src/avif/native/av1/predict/IntraPredictionState.d.ts +49 -0
  175. package/src/avif/native/av1/predict/IntraPredictionState.d.ts.map +1 -0
  176. package/src/avif/native/av1/predict/IntraPredictionState.js +58 -0
  177. package/src/avif/native/av1/predict/intra_filter_type.d.ts +17 -0
  178. package/src/avif/native/av1/predict/intra_filter_type.d.ts.map +1 -0
  179. package/src/avif/native/av1/predict/intra_filter_type.js +79 -0
  180. package/src/avif/native/av1/predict/predict_chroma_from_luma.d.ts +50 -0
  181. package/src/avif/native/av1/predict/predict_chroma_from_luma.d.ts.map +1 -0
  182. package/src/avif/native/av1/predict/predict_chroma_from_luma.js +153 -0
  183. package/src/avif/native/av1/predict/predict_intra.d.ts +42 -0
  184. package/src/avif/native/av1/predict/predict_intra.d.ts.map +1 -0
  185. package/src/avif/native/av1/predict/predict_intra.js +803 -0
  186. package/src/avif/native/av1/predict/predict_palette.d.ts +23 -0
  187. package/src/avif/native/av1/predict/predict_palette.d.ts.map +1 -0
  188. package/src/avif/native/av1/predict/predict_palette.js +36 -0
  189. package/src/avif/native/av1/tables/av1_symbols.d.ts +411 -0
  190. package/src/avif/native/av1/tables/av1_symbols.d.ts.map +1 -0
  191. package/src/avif/native/av1/tables/av1_symbols.js +419 -0
  192. package/src/avif/native/av1/tables/block_tables.d.ts +181 -0
  193. package/src/avif/native/av1/tables/block_tables.d.ts.map +1 -0
  194. package/src/avif/native/av1/tables/block_tables.js +270 -0
  195. package/src/avif/native/av1/tables/cdf_tables.d.ts +961 -0
  196. package/src/avif/native/av1/tables/cdf_tables.d.ts.map +1 -0
  197. package/src/avif/native/av1/tables/cdf_tables.js +1893 -0
  198. package/src/avif/native/av1/tables/coefficient_tables.d.ts +41 -0
  199. package/src/avif/native/av1/tables/coefficient_tables.d.ts.map +1 -0
  200. package/src/avif/native/av1/tables/coefficient_tables.js +82 -0
  201. package/src/avif/native/av1/tables/derived_block_tables.d.ts +15 -0
  202. package/src/avif/native/av1/tables/derived_block_tables.d.ts.map +1 -0
  203. package/src/avif/native/av1/tables/derived_block_tables.js +23 -0
  204. package/src/avif/native/av1/tables/filter_tables.d.ts +171 -0
  205. package/src/avif/native/av1/tables/filter_tables.d.ts.map +1 -0
  206. package/src/avif/native/av1/tables/filter_tables.js +313 -0
  207. package/src/avif/native/av1/tables/grain_tables.d.ts +11 -0
  208. package/src/avif/native/av1/tables/grain_tables.d.ts.map +1 -0
  209. package/src/avif/native/av1/tables/grain_tables.js +190 -0
  210. package/src/avif/native/av1/tables/prediction_tables.d.ts +141 -0
  211. package/src/avif/native/av1/tables/prediction_tables.d.ts.map +1 -0
  212. package/src/avif/native/av1/tables/prediction_tables.js +223 -0
  213. package/src/avif/native/av1/tables/quantizer_matrix_tables.d.ts +21 -0
  214. package/src/avif/native/av1/tables/quantizer_matrix_tables.d.ts.map +1 -0
  215. package/src/avif/native/av1/tables/quantizer_matrix_tables.js +38 -0
  216. package/src/avif/native/av1/tables/quantizer_tables.d.ts +21 -0
  217. package/src/avif/native/av1/tables/quantizer_tables.d.ts.map +1 -0
  218. package/src/avif/native/av1/tables/quantizer_tables.js +160 -0
  219. package/src/avif/native/av1/tables/scan_tables.d.ts +321 -0
  220. package/src/avif/native/av1/tables/scan_tables.d.ts.map +1 -0
  221. package/src/avif/native/av1/tables/scan_tables.js +727 -0
  222. package/src/avif/native/av1/tables/segmentation_tables.d.ts +31 -0
  223. package/src/avif/native/av1/tables/segmentation_tables.d.ts.map +1 -0
  224. package/src/avif/native/av1/tables/segmentation_tables.js +48 -0
  225. package/src/avif/native/av1/tables/transform_tables.d.ts +91 -0
  226. package/src/avif/native/av1/tables/transform_tables.d.ts.map +1 -0
  227. package/src/avif/native/av1/tables/transform_tables.js +140 -0
  228. package/src/avif/native/av1/tables/unpack_table.d.ts +33 -0
  229. package/src/avif/native/av1/tables/unpack_table.d.ts.map +1 -0
  230. package/src/avif/native/av1/tables/unpack_table.js +73 -0
  231. package/src/avif/native/av1/transform/forward_transform_2d.d.ts +61 -0
  232. package/src/avif/native/av1/transform/forward_transform_2d.d.ts.map +1 -0
  233. package/src/avif/native/av1/transform/forward_transform_2d.js +574 -0
  234. package/src/avif/native/av1/transform/inverse_transform_1d.d.ts +76 -0
  235. package/src/avif/native/av1/transform/inverse_transform_1d.d.ts.map +1 -0
  236. package/src/avif/native/av1/transform/inverse_transform_1d.js +590 -0
  237. package/src/avif/native/av1/transform/inverse_transform_2d.d.ts +46 -0
  238. package/src/avif/native/av1/transform/inverse_transform_2d.d.ts.map +1 -0
  239. package/src/avif/native/av1/transform/inverse_transform_2d.js +180 -0
  240. package/src/avif/native/av1/util/ceil_log2.d.ts +13 -0
  241. package/src/avif/native/av1/util/ceil_log2.d.ts.map +1 -0
  242. package/src/avif/native/av1/util/ceil_log2.js +18 -0
  243. package/src/avif/native/av1/util/floor_log2.d.ts +20 -0
  244. package/src/avif/native/av1/util/floor_log2.d.ts.map +1 -0
  245. package/src/avif/native/av1/util/floor_log2.js +20 -0
  246. package/src/avif/native/av1/util/round2.d.ts +22 -0
  247. package/src/avif/native/av1/util/round2.d.ts.map +1 -0
  248. package/src/avif/native/av1/util/round2.js +27 -0
  249. package/src/avif/native/av1/util/round2_signed.d.ts +14 -0
  250. package/src/avif/native/av1/util/round2_signed.d.ts.map +1 -0
  251. package/src/avif/native/av1/util/round2_signed.js +17 -0
  252. package/src/avif/native/av1/util/tile_log2.d.ts +16 -0
  253. package/src/avif/native/av1/util/tile_log2.d.ts.map +1 -0
  254. package/src/avif/native/av1/util/tile_log2.js +23 -0
  255. package/src/avif/native/bitstream/BitReader.d.ts +174 -0
  256. package/src/avif/native/bitstream/BitReader.d.ts.map +1 -0
  257. package/src/avif/native/bitstream/BitReader.js +391 -0
  258. package/src/avif/native/bitstream/BitWriter.d.ts +122 -0
  259. package/src/avif/native/bitstream/BitWriter.d.ts.map +1 -0
  260. package/src/avif/native/bitstream/BitWriter.js +351 -0
  261. package/src/avif/native/color/ColourTransform.d.ts +62 -0
  262. package/src/avif/native/color/ColourTransform.d.ts.map +1 -0
  263. package/src/avif/native/color/ColourTransform.js +189 -0
  264. package/src/avif/native/color/YuvImage.d.ts +51 -0
  265. package/src/avif/native/color/YuvImage.d.ts.map +1 -0
  266. package/src/avif/native/color/YuvImage.js +66 -0
  267. package/src/avif/native/color/clamp_sample.d.ts +18 -0
  268. package/src/avif/native/color/clamp_sample.d.ts.map +1 -0
  269. package/src/avif/native/color/clamp_sample.js +25 -0
  270. package/src/avif/native/color/linear_to_transfer.d.ts +24 -0
  271. package/src/avif/native/color/linear_to_transfer.d.ts.map +1 -0
  272. package/src/avif/native/color/linear_to_transfer.js +126 -0
  273. package/src/avif/native/color/primaries.d.ts +20 -0
  274. package/src/avif/native/color/primaries.d.ts.map +1 -0
  275. package/src/avif/native/color/primaries.js +94 -0
  276. package/src/avif/native/color/rgb_row_to_ycbcr.d.ts +20 -0
  277. package/src/avif/native/color/rgb_row_to_ycbcr.d.ts.map +1 -0
  278. package/src/avif/native/color/rgb_row_to_ycbcr.js +78 -0
  279. package/src/avif/native/color/transfer_to_linear.d.ts +49 -0
  280. package/src/avif/native/color/transfer_to_linear.d.ts.map +1 -0
  281. package/src/avif/native/color/transfer_to_linear.js +171 -0
  282. package/src/avif/native/color/upsample_chroma_row.d.ts +35 -0
  283. package/src/avif/native/color/upsample_chroma_row.d.ts.map +1 -0
  284. package/src/avif/native/color/upsample_chroma_row.js +89 -0
  285. package/src/avif/native/color/ycbcr_row_to_rgb.d.ts +23 -0
  286. package/src/avif/native/color/ycbcr_row_to_rgb.d.ts.map +1 -0
  287. package/src/avif/native/color/ycbcr_row_to_rgb.js +78 -0
  288. package/src/avif/native/heif/AvifFile.d.ts +112 -0
  289. package/src/avif/native/heif/AvifFile.d.ts.map +1 -0
  290. package/src/avif/native/heif/AvifFile.js +142 -0
  291. package/src/avif/native/heif/ItemProperty.d.ts +244 -0
  292. package/src/avif/native/heif/ItemProperty.d.ts.map +1 -0
  293. package/src/avif/native/heif/ItemProperty.js +321 -0
  294. package/src/avif/native/heif/find_item_property.d.ts +17 -0
  295. package/src/avif/native/heif/find_item_property.d.ts.map +1 -0
  296. package/src/avif/native/heif/find_item_property.js +28 -0
  297. package/src/avif/native/heif/find_item_references.d.ts +16 -0
  298. package/src/avif/native/heif/find_item_references.d.ts.map +1 -0
  299. package/src/avif/native/heif/find_item_references.js +29 -0
  300. package/src/avif/native/heif/find_items_referencing.d.ts +17 -0
  301. package/src/avif/native/heif/find_items_referencing.d.ts.map +1 -0
  302. package/src/avif/native/heif/find_items_referencing.js +36 -0
  303. package/src/avif/native/heif/parse_avif_file.d.ts +15 -0
  304. package/src/avif/native/heif/parse_avif_file.d.ts.map +1 -0
  305. package/src/avif/native/heif/parse_avif_file.js +383 -0
  306. package/src/avif/native/heif/parse_image_grid.d.ts +18 -0
  307. package/src/avif/native/heif/parse_image_grid.d.ts.map +1 -0
  308. package/src/avif/native/heif/parse_image_grid.js +48 -0
  309. package/src/avif/native/heif/parse_item_property.d.ts +18 -0
  310. package/src/avif/native/heif/parse_item_property.d.ts.map +1 -0
  311. package/src/avif/native/heif/parse_item_property.js +243 -0
  312. package/src/avif/native/heif/read_item_data.d.ts +22 -0
  313. package/src/avif/native/heif/read_item_data.d.ts.map +1 -0
  314. package/src/avif/native/heif/read_item_data.js +97 -0
  315. package/src/avif/native/heif/write_avif_file.d.ts +50 -0
  316. package/src/avif/native/heif/write_avif_file.d.ts.map +1 -0
  317. package/src/avif/native/heif/write_avif_file.js +348 -0
  318. package/src/avif/native/heif/write_item_property.d.ts +16 -0
  319. package/src/avif/native/heif/write_item_property.d.ts.map +1 -0
  320. package/src/avif/native/heif/write_item_property.js +182 -0
  321. package/src/avif/native/index.d.ts +14 -0
  322. package/src/avif/native/index.d.ts.map +1 -0
  323. package/src/avif/native/index.js +56 -0
  324. package/src/avif/native/isobmff/BoxHeader.d.ts +50 -0
  325. package/src/avif/native/isobmff/BoxHeader.d.ts.map +1 -0
  326. package/src/avif/native/isobmff/BoxHeader.js +57 -0
  327. package/src/avif/native/isobmff/BoxWriter.d.ts +118 -0
  328. package/src/avif/native/isobmff/BoxWriter.d.ts.map +1 -0
  329. package/src/avif/native/isobmff/BoxWriter.js +264 -0
  330. package/src/avif/native/isobmff/ByteCursor.d.ts +125 -0
  331. package/src/avif/native/isobmff/ByteCursor.d.ts.map +1 -0
  332. package/src/avif/native/isobmff/ByteCursor.js +298 -0
  333. package/src/avif/native/isobmff/for_each_box.d.ts +23 -0
  334. package/src/avif/native/isobmff/for_each_box.d.ts.map +1 -0
  335. package/src/avif/native/isobmff/for_each_box.js +39 -0
  336. package/src/avif/native/isobmff/read_box_header.d.ts +22 -0
  337. package/src/avif/native/isobmff/read_box_header.d.ts.map +1 -0
  338. package/src/avif/native/isobmff/read_box_header.js +59 -0
  339. package/src/avif/native/isobmff/read_full_box_header.d.ts +26 -0
  340. package/src/avif/native/isobmff/read_full_box_header.d.ts.map +1 -0
  341. package/src/avif/native/isobmff/read_full_box_header.js +33 -0
  342. package/src/avif/threaded_image_encoder.d.ts +7 -2
  343. package/src/avif/threaded_image_encoder.d.ts.map +1 -1
  344. package/src/avif/threaded_image_encoder.js +107 -21
  345. package/src/core/binary/BinaryBuffer.d.ts +40 -0
  346. package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
  347. package/src/core/binary/BinaryBuffer.js +100 -0
  348. package/src/core/binary/hash/crc32.d.ts +16 -0
  349. package/src/core/binary/hash/crc32.d.ts.map +1 -0
  350. package/src/core/binary/hash/crc32.js +96 -0
  351. package/src/core/binary/utf8/utf8_decode.d.ts +21 -0
  352. package/src/core/binary/utf8/utf8_decode.d.ts.map +1 -0
  353. package/src/core/binary/utf8/utf8_decode.js +43 -0
  354. package/src/core/binary/utf8/utf8_encode_into.d.ts +20 -0
  355. package/src/core/binary/utf8/utf8_encode_into.d.ts.map +1 -0
  356. package/src/core/binary/utf8/utf8_encode_into.js +48 -0
  357. package/src/core/binary/utf8/utf8_encoded_length.d.ts +14 -0
  358. package/src/core/binary/utf8/utf8_encoded_length.d.ts.map +1 -0
  359. package/src/core/binary/utf8/utf8_encoded_length.js +36 -0
  360. package/src/core/color/COLOR_FIX_PLAN_2026_08_28.md +417 -0
  361. package/src/core/color/COLOR_REVIEW_2026_08_28.md +779 -0
  362. package/src/core/color/Color.d.ts +128 -20
  363. package/src/core/color/Color.d.ts.map +1 -1
  364. package/src/core/color/Color.js +959 -866
  365. package/src/core/color/PQ/PQ_constants.d.ts +10 -0
  366. package/src/core/color/PQ/PQ_constants.d.ts.map +1 -1
  367. package/src/core/color/PQ/PQ_constants.js +10 -0
  368. package/src/core/color/PQ/linear_to_PQ.d.ts.map +1 -1
  369. package/src/core/color/PQ/linear_to_PQ.js +6 -0
  370. package/src/core/color/REC709_PRIMARIES.d.ts +50 -0
  371. package/src/core/color/REC709_PRIMARIES.d.ts.map +1 -0
  372. package/src/core/color/REC709_PRIMARIES.js +53 -0
  373. package/src/core/color/construct/color_from_hex.d.ts +21 -0
  374. package/src/core/color/construct/color_from_hex.d.ts.map +1 -0
  375. package/src/core/color/construct/color_from_hex.js +33 -0
  376. package/src/core/color/construct/color_from_temperature.d.ts +22 -0
  377. package/src/core/color/construct/color_from_temperature.d.ts.map +1 -0
  378. package/src/core/color/construct/color_from_temperature.js +33 -0
  379. package/src/core/color/construct/color_from_uint24.d.ts +19 -0
  380. package/src/core/color/construct/color_from_uint24.d.ts.map +1 -0
  381. package/src/core/color/construct/color_from_uint24.js +26 -0
  382. package/src/core/color/construct/color_from_uint32.d.ts +20 -0
  383. package/src/core/color/construct/color_from_uint32.d.ts.map +1 -0
  384. package/src/core/color/construct/color_from_uint32.js +39 -0
  385. package/src/core/color/construct/color_gray.d.ts +17 -0
  386. package/src/core/color/construct/color_gray.d.ts.map +1 -0
  387. package/src/core/color/construct/color_gray.js +22 -0
  388. package/src/core/color/hex/hex2rgb.d.ts +1 -11
  389. package/src/core/color/hex/hex2rgb.d.ts.map +1 -1
  390. package/src/core/color/hex/hex2rgb.js +7 -24
  391. package/src/core/color/hex/hex_to_rgb.d.ts +18 -0
  392. package/src/core/color/hex/hex_to_rgb.d.ts.map +1 -0
  393. package/src/core/color/hex/hex_to_rgb.js +62 -0
  394. package/src/core/color/hex/rgb2hex.d.ts +1 -8
  395. package/src/core/color/hex/rgb2hex.d.ts.map +1 -1
  396. package/src/core/color/hex/rgb2hex.js +7 -13
  397. package/src/core/color/hex/rgb_to_hex.d.ts +9 -0
  398. package/src/core/color/hex/rgb_to_hex.d.ts.map +1 -0
  399. package/src/core/color/hex/rgb_to_hex.js +13 -0
  400. package/src/core/color/hex/rgba_to_hex.d.ts +19 -0
  401. package/src/core/color/hex/rgba_to_hex.d.ts.map +1 -0
  402. package/src/core/color/hex/rgba_to_hex.js +23 -0
  403. package/src/core/color/hsv/hsv2rgb.d.ts +1 -12
  404. package/src/core/color/hsv/hsv2rgb.d.ts.map +1 -1
  405. package/src/core/color/hsv/hsv2rgb.js +8 -21
  406. package/src/core/color/hsv/hsv2rgb_float.d.ts +1 -12
  407. package/src/core/color/hsv/hsv2rgb_float.d.ts.map +1 -1
  408. package/src/core/color/hsv/hsv2rgb_float.js +8 -56
  409. package/src/core/color/hsv/hsv_to_rgb.d.ts +13 -0
  410. package/src/core/color/hsv/hsv_to_rgb.d.ts.map +1 -0
  411. package/src/core/color/hsv/hsv_to_rgb.js +39 -0
  412. package/src/core/color/hsv/hsv_to_rgb_uint8.d.ts +13 -0
  413. package/src/core/color/hsv/hsv_to_rgb_uint8.d.ts.map +1 -0
  414. package/src/core/color/hsv/hsv_to_rgb_uint8.js +21 -0
  415. package/src/core/color/hsv/hue_to_rgb_sextant.d.ts +24 -0
  416. package/src/core/color/hsv/hue_to_rgb_sextant.d.ts.map +1 -0
  417. package/src/core/color/hsv/hue_to_rgb_sextant.js +67 -0
  418. package/src/core/color/hsv/rgb2hsv.d.ts +1 -12
  419. package/src/core/color/hsv/rgb2hsv.d.ts.map +1 -1
  420. package/src/core/color/hsv/rgb2hsv.js +7 -42
  421. package/src/core/color/hsv/rgb_to_hsv.d.ts +13 -0
  422. package/src/core/color/hsv/rgb_to_hsv.d.ts.map +1 -0
  423. package/src/core/color/hsv/rgb_to_hsv.js +42 -0
  424. package/src/core/color/hunt/hpe_to_xyz.d.ts +2 -0
  425. package/src/core/color/hunt/hpe_to_xyz.d.ts.map +1 -0
  426. package/src/core/color/hunt/hpe_to_xyz.js +4 -0
  427. package/src/core/color/hunt/xyz_to_hpe.d.ts +1 -6
  428. package/src/core/color/hunt/xyz_to_hpe.d.ts.map +1 -1
  429. package/src/core/color/hunt/xyz_to_hpe.js +12 -15
  430. package/src/core/color/illuminant/D65_spd_tabulated.d.ts.map +1 -1
  431. package/src/core/color/illuminant/D65_spd_tabulated.js +107 -104
  432. package/src/core/color/int2rgb.d.ts +1 -10
  433. package/src/core/color/int2rgb.d.ts.map +1 -1
  434. package/src/core/color/int2rgb.js +7 -12
  435. package/src/core/color/int_to_rgb.d.ts +21 -0
  436. package/src/core/color/int_to_rgb.d.ts.map +1 -0
  437. package/src/core/color/int_to_rgb.js +33 -0
  438. package/src/core/color/kelvin/kelvin_to_rgb.d.ts.map +1 -1
  439. package/src/core/color/kelvin/kelvin_to_rgb.js +22 -2
  440. package/src/core/color/kelvin/rgb_to_kelvin.d.ts +22 -2
  441. package/src/core/color/kelvin/rgb_to_kelvin.d.ts.map +1 -1
  442. package/src/core/color/kelvin/rgb_to_kelvin.js +22 -2
  443. package/src/core/color/lab/lab_to_xyz.d.ts +20 -0
  444. package/src/core/color/lab/lab_to_xyz.d.ts.map +1 -0
  445. package/src/core/color/lab/lab_to_xyz.js +69 -0
  446. package/src/core/color/oklab/gamut_clip_adaptive_l0.d.ts +34 -0
  447. package/src/core/color/oklab/gamut_clip_adaptive_l0.d.ts.map +1 -0
  448. package/src/core/color/oklab/gamut_clip_adaptive_l0.js +46 -0
  449. package/src/core/color/oklab/gamut_clip_at_l0.d.ts +33 -0
  450. package/src/core/color/oklab/gamut_clip_at_l0.d.ts.map +1 -0
  451. package/src/core/color/oklab/gamut_clip_at_l0.js +84 -0
  452. package/src/core/color/oklab/gamut_clip_preserve_chroma.d.ts +24 -0
  453. package/src/core/color/oklab/gamut_clip_preserve_chroma.d.ts.map +1 -0
  454. package/src/core/color/oklab/gamut_clip_preserve_chroma.js +37 -0
  455. package/src/core/color/oklab/gamut_clip_project_to_lcusp.d.ts +23 -0
  456. package/src/core/color/oklab/gamut_clip_project_to_lcusp.d.ts.map +1 -0
  457. package/src/core/color/oklab/gamut_clip_project_to_lcusp.js +46 -0
  458. package/src/core/color/oklab/linear_srgb_to_okhsl.d.ts +19 -0
  459. package/src/core/color/oklab/linear_srgb_to_okhsl.d.ts.map +1 -0
  460. package/src/core/color/oklab/linear_srgb_to_okhsl.js +93 -0
  461. package/src/core/color/oklab/linear_srgb_to_okhsv.d.ts +12 -4
  462. package/src/core/color/oklab/linear_srgb_to_okhsv.d.ts.map +1 -1
  463. package/src/core/color/oklab/linear_srgb_to_okhsv.js +108 -84
  464. package/src/core/color/oklab/linear_srgb_to_oklab.d.ts +13 -4
  465. package/src/core/color/oklab/linear_srgb_to_oklab.d.ts.map +1 -1
  466. package/src/core/color/oklab/linear_srgb_to_oklab.js +28 -19
  467. package/src/core/color/oklab/okhsl_chroma_bounds.d.ts +51 -0
  468. package/src/core/color/oklab/okhsl_chroma_bounds.d.ts.map +1 -0
  469. package/src/core/color/oklab/okhsl_chroma_bounds.js +128 -0
  470. package/src/core/color/oklab/okhsl_to_linear_srgb.d.ts +19 -0
  471. package/src/core/color/oklab/okhsl_to_linear_srgb.d.ts.map +1 -0
  472. package/src/core/color/oklab/okhsl_to_linear_srgb.js +85 -0
  473. package/src/core/color/oklab/oklab_to_oklch.d.ts +24 -0
  474. package/src/core/color/oklab/oklab_to_oklch.d.ts.map +1 -0
  475. package/src/core/color/oklab/oklab_to_oklch.js +35 -0
  476. package/src/core/color/oklab/oklch_to_oklab.d.ts +18 -0
  477. package/src/core/color/oklab/oklch_to_oklab.d.ts.map +1 -0
  478. package/src/core/color/oklab/oklch_to_oklab.js +26 -0
  479. package/src/core/color/oklab/toe.d.ts +12 -0
  480. package/src/core/color/oklab/toe.d.ts.map +1 -1
  481. package/src/core/color/oklab/toe.js +34 -22
  482. package/src/core/color/operations/color_add.d.ts +21 -0
  483. package/src/core/color/operations/color_add.d.ts.map +1 -0
  484. package/src/core/color/operations/color_add.js +23 -0
  485. package/src/core/color/operations/color_add_scaled.d.ts +20 -0
  486. package/src/core/color/operations/color_add_scaled.d.ts.map +1 -0
  487. package/src/core/color/operations/color_add_scaled.js +30 -0
  488. package/src/core/color/operations/color_clamp.d.ts +22 -0
  489. package/src/core/color/operations/color_clamp.d.ts.map +1 -0
  490. package/src/core/color/operations/color_clamp.js +25 -0
  491. package/src/core/color/operations/color_darken.d.ts +2 -1
  492. package/src/core/color/operations/color_darken.d.ts.map +1 -1
  493. package/src/core/color/operations/color_darken.js +25 -24
  494. package/src/core/color/operations/color_desaturate.d.ts +2 -1
  495. package/src/core/color/operations/color_desaturate.d.ts.map +1 -1
  496. package/src/core/color/operations/color_desaturate.js +25 -24
  497. package/src/core/color/operations/color_get_hsl.d.ts +18 -0
  498. package/src/core/color/operations/color_get_hsl.d.ts.map +1 -0
  499. package/src/core/color/operations/color_get_hsl.js +60 -0
  500. package/src/core/color/operations/color_is_in_gamut.d.ts +20 -0
  501. package/src/core/color/operations/color_is_in_gamut.d.ts.map +1 -0
  502. package/src/core/color/operations/color_is_in_gamut.js +30 -0
  503. package/src/core/color/operations/color_lerp.d.ts +12 -3
  504. package/src/core/color/operations/color_lerp.d.ts.map +1 -1
  505. package/src/core/color/operations/color_lerp.js +55 -46
  506. package/src/core/color/operations/color_lighten.d.ts +9 -5
  507. package/src/core/color/operations/color_lighten.d.ts.map +1 -1
  508. package/src/core/color/operations/color_lighten.js +29 -24
  509. package/src/core/color/operations/color_mix_okhsv_channel.d.ts +24 -0
  510. package/src/core/color/operations/color_mix_okhsv_channel.d.ts.map +1 -0
  511. package/src/core/color/operations/color_mix_okhsv_channel.js +48 -0
  512. package/src/core/color/operations/color_multiply_rgb.d.ts +21 -0
  513. package/src/core/color/operations/color_multiply_rgb.d.ts.map +1 -0
  514. package/src/core/color/operations/color_multiply_rgb.js +26 -0
  515. package/src/core/color/operations/color_saturate.d.ts +9 -5
  516. package/src/core/color/operations/color_saturate.d.ts.map +1 -1
  517. package/src/core/color/operations/color_saturate.js +29 -24
  518. package/src/core/color/operations/color_scale_okhsv_channel.d.ts +1 -1
  519. package/src/core/color/operations/color_scale_okhsv_channel.d.ts.map +1 -1
  520. package/src/core/color/operations/color_scale_okhsv_channel.js +58 -45
  521. package/src/core/color/operations/color_srgb_apply.d.ts +28 -0
  522. package/src/core/color/operations/color_srgb_apply.d.ts.map +1 -0
  523. package/src/core/color/operations/color_srgb_apply.js +40 -0
  524. package/src/core/color/operations/color_sub.d.ts +16 -0
  525. package/src/core/color/operations/color_sub.d.ts.map +1 -0
  526. package/src/core/color/operations/color_sub.js +18 -0
  527. package/src/core/color/parse_color.d.ts +10 -1
  528. package/src/core/color/parse_color.d.ts.map +1 -1
  529. package/src/core/color/parse_color.js +100 -84
  530. package/src/core/color/parse_color_normalized.d.ts +24 -0
  531. package/src/core/color/parse_color_normalized.d.ts.map +1 -0
  532. package/src/core/color/parse_color_normalized.js +38 -0
  533. package/src/core/color/rgb2uint24.d.ts +1 -8
  534. package/src/core/color/rgb2uint24.d.ts.map +1 -1
  535. package/src/core/color/rgb2uint24.js +7 -20
  536. package/src/core/color/rgb2uint32.d.ts +1 -9
  537. package/src/core/color/rgb2uint32.d.ts.map +1 -1
  538. package/src/core/color/rgb2uint32.js +7 -18
  539. package/src/core/color/rgb_to_luminance.d.ts +14 -5
  540. package/src/core/color/rgb_to_luminance.d.ts.map +1 -1
  541. package/src/core/color/rgb_to_luminance.js +24 -13
  542. package/src/core/color/rgb_to_uint24.d.ts +9 -0
  543. package/src/core/color/rgb_to_uint24.d.ts.map +1 -0
  544. package/src/core/color/rgb_to_uint24.js +20 -0
  545. package/src/core/color/rgb_to_uint32.d.ts +10 -0
  546. package/src/core/color/rgb_to_uint32.d.ts.map +1 -0
  547. package/src/core/color/rgb_to_uint32.js +21 -0
  548. package/src/core/color/sRGB/linear_to_sRGB.d.ts +7 -0
  549. package/src/core/color/sRGB/linear_to_sRGB.d.ts.map +1 -1
  550. package/src/core/color/sRGB/linear_to_sRGB.js +39 -32
  551. package/src/core/color/xyz/hpe_to_xyz.d.ts +24 -0
  552. package/src/core/color/xyz/hpe_to_xyz.d.ts.map +1 -0
  553. package/src/core/color/xyz/hpe_to_xyz.js +39 -0
  554. package/src/core/color/xyz/rgb_to_xyz.d.ts +17 -2
  555. package/src/core/color/xyz/rgb_to_xyz.d.ts.map +1 -1
  556. package/src/core/color/xyz/rgb_to_xyz.js +44 -18
  557. package/src/core/color/xyz/xyz_cmf_tabulated.d.ts.map +1 -1
  558. package/src/core/color/xyz/xyz_cmf_tabulated.js +217 -211
  559. package/src/core/color/xyz/xyz_cmf_wyman.d.ts +4 -0
  560. package/src/core/color/xyz/xyz_cmf_wyman.d.ts.map +1 -1
  561. package/src/core/color/xyz/xyz_cmf_wyman.js +74 -65
  562. package/src/core/color/xyz/xyz_to_hpe.d.ts +18 -0
  563. package/src/core/color/xyz/xyz_to_hpe.d.ts.map +1 -0
  564. package/src/core/color/xyz/xyz_to_hpe.js +33 -0
  565. package/src/core/color/xyz/xyz_to_rgb.d.ts +17 -2
  566. package/src/core/color/xyz/xyz_to_rgb.d.ts.map +1 -1
  567. package/src/core/color/xyz/xyz_to_rgb.js +39 -16
  568. package/src/core/color/ycxcz/xyz_to_ycxcz.d.ts +7 -2
  569. package/src/core/color/ycxcz/xyz_to_ycxcz.d.ts.map +1 -1
  570. package/src/core/color/ycxcz/xyz_to_ycxcz.js +33 -28
  571. package/src/core/events/signal/Signal.js +820 -820
  572. package/src/core/math/clamp_index.d.ts +18 -0
  573. package/src/core/math/clamp_index.d.ts.map +1 -0
  574. package/src/core/math/clamp_index.js +23 -0
  575. package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +803 -0
  576. package/src/engine/asset/loaders/image/jpeg/JpegFrame.d.ts +5 -3
  577. package/src/engine/asset/loaders/image/jpeg/JpegFrame.d.ts.map +1 -1
  578. package/src/engine/asset/loaders/image/jpeg/JpegFrame.js +5 -3
  579. package/src/engine/asset/loaders/image/jpeg/JpegFrameComponent.d.ts +2 -1
  580. package/src/engine/asset/loaders/image/jpeg/JpegFrameComponent.d.ts.map +1 -1
  581. package/src/engine/asset/loaders/image/jpeg/JpegFrameComponent.js +2 -1
  582. package/src/engine/asset/loaders/image/jpeg/JpegImage.d.ts +17 -0
  583. package/src/engine/asset/loaders/image/jpeg/JpegImage.d.ts.map +1 -1
  584. package/src/engine/asset/loaders/image/jpeg/JpegImage.js +197 -157
  585. package/src/engine/asset/loaders/image/jpeg/idct8x8_float.d.ts +0 -1
  586. package/src/engine/asset/loaders/image/jpeg/idct8x8_float.d.ts.map +1 -1
  587. package/src/engine/asset/loaders/image/jpeg/idct8x8_float.js +0 -1
  588. package/src/engine/asset/loaders/image/jpeg/jpeg_component_row.d.ts +28 -0
  589. package/src/engine/asset/loaders/image/jpeg/jpeg_component_row.d.ts.map +1 -0
  590. package/src/engine/asset/loaders/image/jpeg/jpeg_component_row.js +31 -0
  591. package/src/engine/asset/loaders/image/png/PNG.d.ts +3 -18
  592. package/src/engine/asset/loaders/image/png/PNG.d.ts.map +1 -1
  593. package/src/engine/asset/loaders/image/png/PNG.js +3 -139
  594. package/src/engine/asset/loaders/image/png/PNGReader.d.ts +16 -16
  595. package/src/engine/asset/loaders/image/png/PNGReader.d.ts.map +1 -1
  596. package/src/engine/asset/loaders/image/png/PNGReader.js +143 -160
  597. package/src/engine/asset/loaders/image/png/chunk/png_chunk_decode_iTXt.js +3 -3
  598. package/src/engine/asset/loaders/image/png/chunk/png_chunk_decode_zTXt.js +3 -3
  599. package/src/engine/asset/loaders/image/png/crc32.d.ts +1 -15
  600. package/src/engine/asset/loaders/image/png/crc32.d.ts.map +1 -1
  601. package/src/engine/asset/loaders/image/png/crc32.js +5 -89
  602. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.d.ts +12 -10
  603. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.d.ts.map +1 -1
  604. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterNone.js +20 -45
  605. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.d.ts +2 -1
  606. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.d.ts.map +1 -1
  607. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterSub.js +2 -0
  608. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.d.ts +2 -1
  609. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.d.ts.map +1 -1
  610. package/src/engine/asset/loaders/image/png/filter/png_filter_unFilterUp.js +2 -0
  611. package/src/engine/asset/loaders/image/png/filter/png_unfilter.d.ts +22 -0
  612. package/src/engine/asset/loaders/image/png/filter/png_unfilter.d.ts.map +1 -0
  613. package/src/engine/asset/loaders/image/png/filter/png_unfilter.js +68 -0
  614. package/src/engine/asset/loaders/image/png/png_inflate.d.ts +20 -0
  615. package/src/engine/asset/loaders/image/png/png_inflate.d.ts.map +1 -0
  616. package/src/engine/asset/loaders/image/png/png_inflate.js +85 -0
  617. package/src/engine/asset/loaders/image/png/png_unpack_samples.d.ts +22 -0
  618. package/src/engine/asset/loaders/image/png/png_unpack_samples.d.ts.map +1 -0
  619. package/src/engine/asset/loaders/image/png/png_unpack_samples.js +38 -0
  620. package/src/engine/graphics/ecs/sprite/SpriteSystemPE.d.ts.map +1 -1
  621. package/src/engine/graphics/ecs/sprite/SpriteSystemPE.js +135 -132
  622. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts +16 -3
  623. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts.map +1 -1
  624. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +1157 -1149
  625. package/src/engine/graphics/render/frame_graph/FrameGraph.js +900 -900
  626. package/src/engine/physics/ecs/PhysicsSystem.d.ts +72 -81
  627. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  628. package/src/engine/physics/ecs/PhysicsSystem.js +86 -77
  629. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  630. package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +451 -0
  631. package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +625 -0
  632. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  633. package/src/shade/renderer/scene/serialization/decode_image_source_hdr.d.ts +14 -3
  634. package/src/shade/renderer/scene/serialization/decode_image_source_hdr.d.ts.map +1 -1
  635. package/src/shade/renderer/scene/serialization/decode_image_source_hdr.js +50 -51
  636. package/src/shade/renderer/scene/serialization/deserialize_scene.d.ts.map +1 -1
  637. package/src/shade/renderer/scene/serialization/deserialize_scene.js +531 -521
  638. package/src/shade/renderer/scene/serialization/write_image_source.d.ts.map +1 -1
  639. package/src/shade/renderer/scene/serialization/write_image_source.js +6 -4
  640. package/src/shade/renderer/shader/chunk/color/chunk_rgb_to_luminance.d.ts.map +1 -1
  641. package/src/shade/renderer/shader/chunk/color/chunk_rgb_to_luminance.js +34 -32
  642. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_eotf_st2084.d.ts +31 -0
  643. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_eotf_st2084.d.ts.map +1 -1
  644. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_eotf_st2084.js +62 -31
  645. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_inverse_eotf_st2084.d.ts +10 -0
  646. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_inverse_eotf_st2084.d.ts.map +1 -1
  647. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_inverse_eotf_st2084.js +32 -22
  648. package/src/shade/renderer/shader/chunk/color/sRGB/chunk_sRGBTransferOETF.d.ts.map +1 -1
  649. package/src/shade/renderer/shader/chunk/color/sRGB/chunk_sRGBTransferOETF.js +22 -21
  650. package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.d.ts +6 -0
  651. package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.d.ts.map +1 -1
  652. package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.js +110 -104
  653. package/src/shade/renderer/shader/chunk/color/xyz/chunk_rgb_to_xyz.d.ts.map +1 -1
  654. package/src/shade/renderer/shader/chunk/color/xyz/chunk_rgb_to_xyz.js +23 -21
  655. package/src/view/elements/ColorPickerView.js +296 -296
  656. package/src/avif/codec/dec/Readme.md +0 -14
  657. package/src/avif/codec/dec/avif_dec.cpp +0 -234
  658. package/src/avif/codec/dec/avif_dec.d.ts +0 -3
  659. package/src/avif/codec/dec/avif_dec.d.ts.map +0 -1
  660. package/src/avif/codec/dec/avif_dec.js +0 -16
  661. package/src/avif/codec/dec/avif_dec.wasm +0 -0
  662. package/src/avif/codec/enc/README.md +0 -37
  663. package/src/avif/codec/enc/avif_enc.cpp +0 -216
  664. package/src/avif/codec/enc/avif_enc.d.ts +0 -3
  665. package/src/avif/codec/enc/avif_enc.d.ts.map +0 -1
  666. package/src/avif/codec/enc/avif_enc.js +0 -6144
  667. package/src/avif/codec/enc/avif_enc.wasm +0 -0
  668. package/src/avif/codec/enc/avif_enc_mt.d.ts +0 -3
  669. package/src/avif/codec/enc/avif_enc_mt.d.ts.map +0 -1
  670. package/src/avif/codec/enc/avif_enc_mt.js +0 -6811
  671. package/src/avif/codec/enc/avif_enc_mt.wasm +0 -0
  672. package/src/avif/codec/enc/avif_enc_mt.worker.d.mts +0 -2
  673. package/src/avif/codec/enc/avif_enc_mt.worker.d.mts.map +0 -1
  674. package/src/avif/codec/enc/avif_enc_mt.worker.d.ts +0 -11
  675. package/src/avif/codec/enc/avif_enc_mt.worker.d.ts.map +0 -1
  676. package/src/avif/codec/enc/avif_enc_mt.worker.js +0 -171
  677. package/src/avif/codec/enc/avif_enc_mt.worker.mjs +0 -1
  678. package/src/avif/decode.d.ts +0 -3
  679. package/src/avif/decode.d.ts.map +0 -1
  680. package/src/avif/decode.js +0 -42
  681. package/src/avif/encode.d.ts +0 -3
  682. package/src/avif/encode.d.ts.map +0 -1
  683. package/src/avif/encode.js +0 -75
  684. package/src/avif/meta.d.ts +0 -24
  685. package/src/avif/meta.d.ts.map +0 -1
  686. package/src/avif/meta.js +0 -23
  687. package/src/avif/tsconfig.tsbuildinfo +0 -1
  688. package/src/avif/utils.d.ts +0 -17
  689. package/src/avif/utils.d.ts.map +0 -1
  690. package/src/avif/utils.js +0 -31
  691. package/src/engine/asset/loaders/image/jpeg/idct8x8_fixed.d.ts +0 -23
  692. package/src/engine/asset/loaders/image/jpeg/idct8x8_fixed.d.ts.map +0 -1
  693. package/src/engine/asset/loaders/image/jpeg/idct8x8_fixed.js +0 -214
  694. package/src/engine/asset/loaders/image/png/inflate.d.ts +0 -7
  695. package/src/engine/asset/loaders/image/png/inflate.d.ts.map +0 -1
  696. package/src/engine/asset/loaders/image/png/inflate.js +0 -20
@@ -0,0 +1,2331 @@
1
+ import {
2
+ ADST_ADST,
3
+ ADST_DCT,
4
+ BLOCK_128X128,
5
+ BLOCK_INVALID,
6
+ BLOCK_16X16,
7
+ BLOCK_32X32,
8
+ BLOCK_4X4,
9
+ BLOCK_64X64,
10
+ BLOCK_8X8,
11
+ D113_PRED,
12
+ D135_PRED,
13
+ D157_PRED,
14
+ D203_PRED,
15
+ D45_PRED,
16
+ D67_PRED,
17
+ DC_PRED,
18
+ DCT_ADST,
19
+ DCT_DCT,
20
+ H_PRED,
21
+ MAX_ANGLE_DELTA,
22
+ MI_SIZE,
23
+ PAETH_PRED,
24
+ PARTITION_HORZ,
25
+ PARTITION_HORZ_4,
26
+ PARTITION_HORZ_A,
27
+ PARTITION_HORZ_B,
28
+ PARTITION_NONE,
29
+ PARTITION_SPLIT,
30
+ PARTITION_VERT,
31
+ PARTITION_VERT_4,
32
+ PARTITION_VERT_A,
33
+ PARTITION_VERT_B,
34
+ SMOOTH_H_PRED,
35
+ SMOOTH_PRED,
36
+ SMOOTH_V_PRED,
37
+ TX_4X4,
38
+ UV_CFL_PRED,
39
+ TX_SET_DCTONLY,
40
+ V_PRED
41
+ } from "../tables/av1_symbols.js";
42
+ import {
43
+ MAX_TX_DEPTH,
44
+ MAX_TX_SIZE_RECT,
45
+ NUM_4X4_BLOCKS_HIGH,
46
+ NUM_4X4_BLOCKS_WIDE,
47
+ PARTITION_SUBSIZE,
48
+ PARTITION_SUBSIZE_SHAPE,
49
+ SPLIT_TX_SIZE,
50
+ SUBSAMPLED_SIZE,
51
+ TX_HEIGHT,
52
+ TX_HEIGHT_LOG2,
53
+ TX_WIDTH,
54
+ TX_WIDTH_LOG2
55
+ } from "../tables/block_tables.js";
56
+ import { BLOCK_DECODED_ORIGIN, BLOCK_DECODED_STRIDE } from "../decode/Av1FrameContext.js";
57
+ import { assert } from "../../../../core/assert.js";
58
+ import { capture_block, restore_block } from "./Av1BlockSnapshot.js";
59
+ import {
60
+ DECISION_SKIP,
61
+ DECISION_TX_SIZE,
62
+ DECISION_TX_TYPE,
63
+ DECISION_UV_MODE,
64
+ DECISION_Y_MODE
65
+ } from "./Av1EncodeContext.js";
66
+ import { SGRPROJ_XQD_MID, WIENER_TAPS_MID } from "../tables/filter_tables.js";
67
+ import { clear_block_decoded_flags, is_inside } from "../decode/block_decoded.js";
68
+ import { write_lr } from "./write_lr.js";
69
+ import { FORWARD_ADST, FORWARD_DCT, forward_transform_2d, forward_transform_bound, forward_wht_4x4 } from "../transform/forward_transform_2d.js";
70
+ import { INTRA_MODE_CONTEXT } from "../tables/prediction_tables.js";
71
+ import { column_is_dct, row_is_dct } from "../transform/inverse_transform_2d.js";
72
+ import { compute_tx_type, get_tx_set } from "../decode/transform_type.js";
73
+ import { get_filter_type } from "../predict/intra_filter_type.js";
74
+ import { partition_cdf, partition_context, probability_of } from "../entropy/partition_cdf.js";
75
+ import { reset_block_context } from "../entropy/coefficient_context.js";
76
+ import { COST_ONE_BIT, symbol_cost } from "../entropy/symbol_cost.js";
77
+ import { choose_chroma_alpha } from "./choose_chroma_alpha.js";
78
+ import { is_directional_mode, predict_intra } from "../predict/predict_intra.js";
79
+ import { predict_chroma_from_luma } from "../predict/predict_chroma_from_luma.js";
80
+ import { get_scan } from "../decode/get_scan.js";
81
+ import { quantise_transform_block, quantiser_zero_threshold } from "./quantise.js";
82
+ import { reconstruct } from "../decode/reconstruct.js";
83
+ import { write_coefficients } from "./write_coefficients.js";
84
+
85
+ /**
86
+ * The intra modes the luma decision chooses between: all thirteen the format has.
87
+ *
88
+ * They are affordable now because they are not all priced. Every one is predicted and ranked, which
89
+ * costs a prediction each, and only the best few are transformed and costed — so widening the set
90
+ * buys the oriented edges photographs are full of without paying thirteen full trials for them.
91
+ * @type {number[]}
92
+ */
93
+ const LUMA_CANDIDATES = [
94
+ DC_PRED,
95
+ V_PRED,
96
+ H_PRED,
97
+ D45_PRED,
98
+ D135_PRED,
99
+ D113_PRED,
100
+ D157_PRED,
101
+ D203_PRED,
102
+ D67_PRED,
103
+ SMOOTH_PRED,
104
+ SMOOTH_V_PRED,
105
+ SMOOTH_H_PRED,
106
+ PAETH_PRED
107
+ ];
108
+
109
+ /**
110
+ * The modes the chroma decision chooses between.
111
+ *
112
+ * Narrower than luma's on purpose: subsampled chroma is smooth, its transform blocks are a quarter
113
+ * the area, and the directional modes almost never win there — while every candidate costs two
114
+ * predictions, one per chroma plane.
115
+ * @type {number[]}
116
+ */
117
+ const CHROMA_CANDIDATES = [DC_PRED, V_PRED, H_PRED, SMOOTH_PRED, PAETH_PRED, UV_CFL_PRED];
118
+
119
+ /**
120
+ * The smallest block this encoder partitions down to.
121
+ *
122
+ * Four by four, which is the smallest the format has. A block this size does not own its chroma —
123
+ * the format gives one chroma block to each 8x8 of luma, and `begin_block` works out which of the
124
+ * four owns it — and codes no partition symbol, because there is nothing left to divide.
125
+ * @type {number}
126
+ */
127
+ const MIN_BLOCK = BLOCK_4X4;
128
+
129
+ /**
130
+ * The largest.
131
+ *
132
+ * Not the superblock: a 64x64 block's transform codes only its top left 32x32 coefficients and
133
+ * discards the rest, which is a low pass filter rather than a coding decision.
134
+ * @type {number}
135
+ */
136
+ const MAX_BLOCK = BLOCK_32X32;
137
+
138
+ /**
139
+ * Row stride of {@link PARTITION_SUBSIZE}, which is indexed by partition and then by block size.
140
+ * @type {number}
141
+ */
142
+ const PARTITION_SUBSIZE_STRIDE = PARTITION_SUBSIZE_SHAPE[1];
143
+
144
+ /**
145
+ * Squared error in the coefficient domain, per unit of squared error in samples.
146
+ *
147
+ * The specification's inverse transform is orthonormal up to a scale, and the scale is the same
148
+ * sixty-fourth for every size and every type this encoder emits — the sizes whose dequantiser
149
+ * divides by two or four included, because that division happens before the transform. Measured
150
+ * rather than derived, and it is what lets a trial price what a candidate loses without running an
151
+ * inverse transform to find out.
152
+ * @type {number}
153
+ */
154
+ const COEFFICIENT_ENERGY_PER_SAMPLE = 64;
155
+
156
+ /**
157
+ * Encode one tile — which, for a still image, is the whole picture.
158
+ *
159
+ * Each superblock is coded twice. The first pass searches: it prices both answers to every
160
+ * partition decision by *making* each of them — predicting, quantising, reconstructing, advancing
161
+ * the contexts — against a writer that emits nothing, and puts the state back between them. The
162
+ * second pass writes the tree the first settled on.
163
+ *
164
+ * It has to be that way round. A partition symbol comes before everything under it in the stream,
165
+ * and choosing which one to write means knowing what the whole subtree would cost. What makes the
166
+ * two passes agree is that the second starts from exactly the state the first did, and replays the
167
+ * decisions the first recorded, so it reproduces the winning path sample for sample.
168
+ *
169
+ * Searching a superblock at a time rather than the whole tile keeps the probabilities honest: the
170
+ * CDFs a search prices against are the ones the previous superblock left behind, which is within a
171
+ * superblock of the ones its own symbols will meet.
172
+ *
173
+ * @param {Av1EncodeContext} ec
174
+ * @param {number} replay `0` to search and write, `1` to write again from what the search recorded
175
+ * @see AV1 spec 5.11.2 "Decode tile syntax", of which this is the mirror
176
+ *
177
+ * @author Alex Goldring
178
+ * @copyright Company Named Limited (c) 2026
179
+ */
180
+ export function encode_tile(ec, replay) {
181
+ const ctx = ec.frame_context;
182
+
183
+ clear_above_context(ctx);
184
+ clear_restoration_reference(ctx);
185
+
186
+ ctx.delta_lf.fill(0);
187
+
188
+ const sb_size = BLOCK_64X64;
189
+ const sb_size4 = NUM_4X4_BLOCKS_WIDE[sb_size];
190
+
191
+ for (let r = ctx.mi_row_start; r < ctx.mi_row_end; r += sb_size4) {
192
+ clear_left_context(ctx);
193
+
194
+ for (let c = ctx.mi_col_start; c < ctx.mi_col_end; c += sb_size4) {
195
+ ctx.read_deltas = 0;
196
+
197
+ clear_block_decoded_flags(ctx, r, c, sb_size4);
198
+
199
+ ctx.cdef_idx[r * ctx.mi_stride + c] = -1;
200
+
201
+ if (replay === 0) {
202
+ capture_block(ctx, ec.snapshots[0], r, c, sb_size);
203
+
204
+ search_partition(ec, r, c, sb_size, 0);
205
+
206
+ record_decisions(ec, r, c, sb_size);
207
+ restore_block(ctx, ec.snapshots[0], r, c, sb_size);
208
+ } else {
209
+ // only the second pass has anything to say here: the filter these carry was fitted
210
+ // to the picture the first pass produced
211
+ write_lr(ec, ctx.writer, r, c, sb_size);
212
+ }
213
+
214
+ write_partition_tree(ec, r, c, sb_size, 0);
215
+ }
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Restoration parameters are coded as differences from the unit before, and a tile starts from the
221
+ * middle of each parameter's range rather than from whatever the tile before it left.
222
+ *
223
+ * @param {Av1FrameContext} ctx
224
+ */
225
+ function clear_restoration_reference(ctx) {
226
+ for (let plane = 0; plane < 3; plane++) {
227
+ for (let pass = 0; pass < 2; pass++) {
228
+ ctx.ref_sgr_xqd[plane * 2 + pass] = SGRPROJ_XQD_MID[pass];
229
+
230
+ for (let i = 0; i < 3; i++) {
231
+ ctx.ref_lr_wiener[(plane * 2 + pass) * 3 + i] = WIENER_TAPS_MID[i];
232
+ }
233
+ }
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Copy what the search settled on out of the frame context, before the search is undone.
239
+ *
240
+ * Five fields per 4x4 position: the two prediction modes, the skip flag, and the transform's type
241
+ * and size. Everything else the writing pass needs it works out again from these.
242
+ *
243
+ * @param {Av1EncodeContext} ec
244
+ * @param {number} r
245
+ * @param {number} c
246
+ * @param {number} b_size
247
+ */
248
+ function record_decisions(ec, r, c, b_size) {
249
+ const ctx = ec.frame_context;
250
+
251
+ const stride = ctx.mi_stride;
252
+
253
+ const rows = Math.min(NUM_4X4_BLOCKS_HIGH[b_size], ctx.mi_rows - r);
254
+ const columns = Math.min(NUM_4X4_BLOCKS_WIDE[b_size], ctx.mi_cols - c);
255
+
256
+ const decisions = ec.decisions;
257
+ const field = ec.decision_stride;
258
+
259
+ for (let y = 0; y < rows; y++) {
260
+ const row = (r + y) * stride + c;
261
+
262
+ for (let x = 0; x < columns; x++) {
263
+ const at = row + x;
264
+
265
+ decisions[DECISION_Y_MODE * field + at] = ctx.y_modes[at];
266
+ decisions[DECISION_UV_MODE * field + at] = ctx.uv_modes[at];
267
+ decisions[DECISION_SKIP * field + at] = ctx.skips[at];
268
+ decisions[DECISION_TX_TYPE * field + at] = ctx.tx_types[at];
269
+ decisions[DECISION_TX_SIZE * field + at] = ctx.inter_tx_sizes[at];
270
+ }
271
+ }
272
+ }
273
+
274
+ /**
275
+ * @param {Av1FrameContext} ctx
276
+ */
277
+ function clear_above_context(ctx) {
278
+ for (let plane = 0; plane < ctx.num_planes; plane++) {
279
+ ctx.above_level_context[plane].fill(0);
280
+ ctx.above_dc_context[plane].fill(0);
281
+ }
282
+ }
283
+
284
+ /**
285
+ * @param {Av1FrameContext} ctx
286
+ */
287
+ function clear_left_context(ctx) {
288
+ for (let plane = 0; plane < ctx.num_planes; plane++) {
289
+ ctx.left_level_context[plane].fill(0);
290
+ ctx.left_dc_context[plane].fill(0);
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Search a partition: price coding the block whole against coding it as four, and leave the state
296
+ * the winner produced.
297
+ *
298
+ * Only two of the ten partitions are considered — none, and a split into four. The rest exist to
299
+ * fit rectangular detail, and each one added is another whole subtree to price.
300
+ *
301
+ * @param {Av1EncodeContext} ec
302
+ * @param {number} r
303
+ * @param {number} c
304
+ * @param {number} b_size
305
+ * @param {number} depth how deep below the superblock this node is, which picks its snapshots
306
+ * @returns {number} squared error plus lambda times rate, for this node and everything under it
307
+ */
308
+ function search_partition(ec, r, c, b_size, depth) {
309
+ const ctx = ec.frame_context;
310
+
311
+ if (r >= ctx.mi_rows || c >= ctx.mi_cols) {
312
+ return 0;
313
+ }
314
+
315
+ const num4x4 = NUM_4X4_BLOCKS_WIDE[b_size];
316
+ const half4x4 = num4x4 >> 1;
317
+
318
+ const has_rows = r + half4x4 < ctx.mi_rows;
319
+ const has_cols = c + half4x4 < ctx.mi_cols;
320
+
321
+ // a superblock is always split: its transform would be 64x64, which codes only a quarter of
322
+ // its coefficients and throws the rest away. So is a block hanging off the edge of the picture,
323
+ // because the format codes no partition there that would keep it whole
324
+ const must_split = b_size > MAX_BLOCK || !has_rows || !has_cols;
325
+
326
+ const recorded = depth * ec.decision_stride + r * ctx.mi_stride + c;
327
+
328
+ if (b_size <= MIN_BLOCK) {
329
+ ec.partitions[recorded] = PARTITION_NONE;
330
+
331
+ return partition_rate(ec, r, c, b_size, PARTITION_NONE, has_rows, has_cols)
332
+ + search_block(ec, r, c, b_size);
333
+ }
334
+
335
+ if (must_split) {
336
+ ec.partitions[recorded] = PARTITION_SPLIT;
337
+
338
+ return partition_rate(ec, r, c, b_size, PARTITION_SPLIT, has_rows, has_cols)
339
+ + search_children(ec, r, c, b_size, depth, Infinity);
340
+ }
341
+
342
+ const before = ec.snapshots[depth * 2 - 1];
343
+ const winner = ec.snapshots[depth * 2];
344
+
345
+ capture_block(ctx, before, r, c, b_size);
346
+
347
+ let best = partition_rate(ec, r, c, b_size, PARTITION_NONE, has_rows, has_cols)
348
+ + search_block(ec, r, c, b_size);
349
+
350
+ let chosen = PARTITION_NONE;
351
+
352
+ // four quarters cost a partition symbol and four sets of mode info before they code anything,
353
+ // so a block already costing less than that cannot be improved by splitting it — and the whole
354
+ // subtree underneath goes unsearched
355
+ if (best <= ec.lambda * COST_ONE_BIT * ec.split_floor) {
356
+ ec.partitions[recorded] = PARTITION_NONE;
357
+
358
+ return best;
359
+ }
360
+
361
+ // keeping what an answer produced is cheaper than making it again if it turns out to have won
362
+ capture_block(ctx, winner, r, c, b_size);
363
+ restore_block(ctx, before, r, c, b_size);
364
+
365
+ const split_overhead = partition_rate(ec, r, c, b_size, PARTITION_SPLIT, has_rows, has_cols);
366
+
367
+ const split = split_overhead + search_children(ec, r, c, b_size, depth, best - split_overhead);
368
+
369
+ if (split < best) {
370
+ best = split;
371
+ chosen = PARTITION_SPLIT;
372
+
373
+ capture_block(ctx, winner, r, c, b_size);
374
+ }
375
+
376
+ // An uneven cut is the answer between a whole and a quarter, so it is worth asking about exactly
377
+ // when those two are close. When one of them wins outright the picture has already said whether
378
+ // the detail here is worth another partition symbol, and more subtrees will not change its mind
379
+ // — while each of them costs as much again as the unsplit answer did, or half as much more.
380
+ const uneven = Math.min(ec.uneven_partitions, UNEVEN_PARTITIONS.length);
381
+
382
+ if (Math.max(split, best) < Math.min(split, best) * ec.rectangle_margin) {
383
+ for (let i = 0; i < uneven; i++) {
384
+ const partition = UNEVEN_PARTITIONS[i];
385
+
386
+ // only the two halves have a symbol at 8x8
387
+ if (i >= 2 && b_size < RECTANGLE_MIN_BLOCK) {
388
+ break;
389
+ }
390
+
391
+ if (!partition_is_available(ctx, b_size, partition)) {
392
+ continue;
393
+ }
394
+
395
+ restore_block(ctx, before, r, c, b_size);
396
+
397
+ const overhead = partition_rate(ec, r, c, b_size, partition, has_rows, has_cols);
398
+
399
+ const cost = overhead + search_layout(ec, r, c, b_size, partition, best - overhead);
400
+
401
+ if (cost < best) {
402
+ best = cost;
403
+ chosen = partition;
404
+
405
+ capture_block(ctx, winner, r, c, b_size);
406
+ }
407
+ }
408
+ }
409
+
410
+ restore_block(ctx, winner, r, c, b_size);
411
+
412
+ ec.partitions[recorded] = chosen;
413
+
414
+ return best;
415
+ }
416
+
417
+ /**
418
+ * Where the blocks of each partition go, and which of the two sizes each is.
419
+ *
420
+ * Three numbers per block: how far down and how far right its corner sits, as quarters of the
421
+ * node's side — multiplied by the side and shifted, never divided, because a quarter of an 8x8 is
422
+ * half a mode-info unit and an integer quarter would be none of one — and whether it takes the
423
+ * partition's own sub-size or the size a plain split would have given. The specification lays these out as ten branches of one switch; a table is the same
424
+ * thing with the arithmetic factored out, and it means the encoder walks a partition the same way
425
+ * whichever one it is.
426
+ *
427
+ * `PARTITION_SPLIT` is absent because it is not a leaf: it recurses.
428
+ * @type {Int32Array[]}
429
+ */
430
+ const PARTITION_LAYOUT = build_partition_layouts();
431
+
432
+ /**
433
+ * @returns {Int32Array[]}
434
+ */
435
+ function build_partition_layouts() {
436
+ const layouts = new Array(10).fill(null);
437
+
438
+ layouts[PARTITION_NONE] = new Int32Array([0, 0, 0]);
439
+ layouts[PARTITION_HORZ] = new Int32Array([0, 0, 0, 2, 0, 0]);
440
+ layouts[PARTITION_VERT] = new Int32Array([0, 0, 0, 0, 2, 0]);
441
+
442
+ layouts[PARTITION_HORZ_A] = new Int32Array([0, 0, 1, 0, 2, 1, 2, 0, 0]);
443
+ layouts[PARTITION_HORZ_B] = new Int32Array([0, 0, 0, 2, 0, 1, 2, 2, 1]);
444
+ layouts[PARTITION_VERT_A] = new Int32Array([0, 0, 1, 2, 0, 1, 0, 2, 0]);
445
+ layouts[PARTITION_VERT_B] = new Int32Array([0, 0, 0, 0, 2, 1, 2, 2, 1]);
446
+
447
+ layouts[PARTITION_HORZ_4] = new Int32Array([0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0]);
448
+ layouts[PARTITION_VERT_4] = new Int32Array([0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0]);
449
+
450
+ return layouts;
451
+ }
452
+
453
+ /**
454
+ * The partitions that cut a block unevenly, in the order they are worth asking about.
455
+ *
456
+ * The two halves first, because they are where most of the gain is and each is two blocks rather
457
+ * than three. Then the four T-shapes, which fit a block with detail in one corner. Then the two
458
+ * quarterings, which fit a band.
459
+ * @type {number[]}
460
+ */
461
+ const UNEVEN_PARTITIONS = [
462
+ PARTITION_HORZ,
463
+ PARTITION_VERT,
464
+ PARTITION_HORZ_A,
465
+ PARTITION_HORZ_B,
466
+ PARTITION_VERT_A,
467
+ PARTITION_VERT_B,
468
+ PARTITION_HORZ_4,
469
+ PARTITION_VERT_4
470
+ ];
471
+
472
+ /**
473
+ * The smallest block the T-shapes and the quarterings are offered for.
474
+ *
475
+ * The format codes only four partitions at 8x8 — none, the two halves, and the quartering — so
476
+ * there is no symbol for the rest of them there. The halves are available all the way down.
477
+ * @type {number}
478
+ */
479
+ const RECTANGLE_MIN_BLOCK = BLOCK_16X16;
480
+
481
+ /**
482
+ * Whether every block a partition lays down is one this encoder can code.
483
+ *
484
+ * What stops it is a size the format has no chroma block for at all: an 8x16 block at 4:2:2 would
485
+ * want a 4x16 chroma block, and the specification's table of subsampled sizes says there is no such
486
+ * thing — so a vertical halving is out at that subsampling, and asking for one produces a stream no
487
+ * decoder can read.
488
+ *
489
+ * @param {Av1FrameContext} ctx
490
+ * @param {number} b_size
491
+ * @param {number} partition
492
+ * @returns {boolean}
493
+ * @see AV1 spec, `Subsampled_Size`
494
+ */
495
+ function partition_is_available(ctx, b_size, partition) {
496
+ const sub_size = PARTITION_SUBSIZE[partition * PARTITION_SUBSIZE_STRIDE + b_size];
497
+
498
+ if (sub_size === BLOCK_INVALID || !owns_chroma(ctx, sub_size)) {
499
+ return false;
500
+ }
501
+
502
+ const layout = PARTITION_LAYOUT[partition];
503
+
504
+ const split_size = PARTITION_SUBSIZE[PARTITION_SPLIT * PARTITION_SUBSIZE_STRIDE + b_size];
505
+
506
+ for (let i = 0; i < layout.length; i += 3) {
507
+ if (layout[i + 2] === 1 && (split_size === BLOCK_INVALID || !owns_chroma(ctx, split_size))) {
508
+ return false;
509
+ }
510
+ }
511
+
512
+ return true;
513
+ }
514
+
515
+ /**
516
+ * @param {Av1FrameContext} ctx
517
+ * @param {number} b_size
518
+ * @returns {boolean}
519
+ */
520
+ function owns_chroma(ctx, b_size) {
521
+ if (ctx.num_planes === 1) {
522
+ return true;
523
+ }
524
+
525
+ return SUBSAMPLED_SIZE[(b_size * 2 + ctx.subsampling_x) * 2 + ctx.subsampling_y] !== BLOCK_INVALID;
526
+ }
527
+
528
+ /**
529
+ * Search one uneven partition's blocks, giving up as soon as they cost more than the best so far.
530
+ *
531
+ * @param {Av1EncodeContext} ec
532
+ * @param {number} r
533
+ * @param {number} c
534
+ * @param {number} b_size
535
+ * @param {number} partition
536
+ * @param {number} budget cost above which this partition has already lost
537
+ * @returns {number}
538
+ */
539
+ function search_layout(ec, r, c, b_size, partition, budget) {
540
+ const ctx = ec.frame_context;
541
+
542
+ const num4x4 = NUM_4X4_BLOCKS_WIDE[b_size];
543
+
544
+ const sub_size = PARTITION_SUBSIZE[partition * PARTITION_SUBSIZE_STRIDE + b_size];
545
+ const split_size = PARTITION_SUBSIZE[PARTITION_SPLIT * PARTITION_SUBSIZE_STRIDE + b_size];
546
+
547
+ const layout = PARTITION_LAYOUT[partition];
548
+
549
+ let total = 0;
550
+
551
+ for (let i = 0; i < layout.length; i += 3) {
552
+ const row = r + ((layout[i] * num4x4) >> 2);
553
+ const column = c + ((layout[i + 1] * num4x4) >> 2);
554
+
555
+ if (row >= ctx.mi_rows || column >= ctx.mi_cols) {
556
+ continue;
557
+ }
558
+
559
+ total += search_block(ec, row, column, layout[i + 2] === 1 ? split_size : sub_size);
560
+
561
+ if (total >= budget) {
562
+ return Infinity;
563
+ }
564
+ }
565
+
566
+ return total;
567
+ }
568
+
569
+ /**
570
+ * Search the four quarters of a block, giving up as soon as they cost more than the whole did.
571
+ *
572
+ * The quarters' costs only ever add up, so once the running total passes what keeping the block
573
+ * whole cost, no amount of searching the rest can bring it back under. Abandoning there is not an
574
+ * approximation — the answer is the same one, reached without paying for it — and on flat content
575
+ * it stops after the first quarter.
576
+ *
577
+ * @param {Av1EncodeContext} ec
578
+ * @param {number} r
579
+ * @param {number} c
580
+ * @param {number} b_size
581
+ * @param {number} depth
582
+ * @param {number} budget cost above which the split has already lost
583
+ * @returns {number} the split's cost, or infinity if it passed the budget on the way
584
+ */
585
+ function search_children(ec, r, c, b_size, depth, budget) {
586
+ const half4x4 = NUM_4X4_BLOCKS_WIDE[b_size] >> 1;
587
+ const sub_size = SPLIT_SIZE[b_size];
588
+
589
+ let total = search_partition(ec, r, c, sub_size, depth + 1);
590
+
591
+ if (total >= budget) {
592
+ return Infinity;
593
+ }
594
+
595
+ total += search_partition(ec, r, c + half4x4, sub_size, depth + 1);
596
+
597
+ if (total >= budget) {
598
+ return Infinity;
599
+ }
600
+
601
+ total += search_partition(ec, r + half4x4, c, sub_size, depth + 1);
602
+
603
+ if (total >= budget) {
604
+ return Infinity;
605
+ }
606
+
607
+ return total + search_partition(ec, r + half4x4, c + half4x4, sub_size, depth + 1);
608
+ }
609
+
610
+ /**
611
+ * Write the partition tree the search settled on, and the blocks under it.
612
+ *
613
+ * The tree is read back out of what the search recorded, one partition per node. Deriving it from
614
+ * the block sizes it left behind would very nearly work and would be wrong in one case: a node cut
615
+ * into a T and a node quartered whose first quarter was then left whole put the same size in the
616
+ * same corner, and the two are different partitions.
617
+ *
618
+ * @param {Av1EncodeContext} ec
619
+ * @param {number} r
620
+ * @param {number} c
621
+ * @param {number} b_size
622
+ * @param {number} depth how deep below the superblock this node is, which is how the search
623
+ * recorded what it chose
624
+ */
625
+ function write_partition_tree(ec, r, c, b_size, depth) {
626
+ const ctx = ec.frame_context;
627
+
628
+ if (r >= ctx.mi_rows || c >= ctx.mi_cols) {
629
+ return;
630
+ }
631
+
632
+ const avail_u = is_inside(ctx, r - 1, c) ? 1 : 0;
633
+ const avail_l = is_inside(ctx, r, c - 1) ? 1 : 0;
634
+
635
+ const num4x4 = NUM_4X4_BLOCKS_WIDE[b_size];
636
+ const half4x4 = num4x4 >> 1;
637
+
638
+ const has_rows = r + half4x4 < ctx.mi_rows;
639
+ const has_cols = c + half4x4 < ctx.mi_cols;
640
+
641
+ const partition = ec.partitions[depth * ec.decision_stride + r * ctx.mi_stride + c];
642
+
643
+ if (b_size < BLOCK_8X8) {
644
+ write_block(ec, r, c, b_size);
645
+
646
+ return;
647
+ }
648
+
649
+ if (has_rows && has_cols) {
650
+ write_partition(ec, ctx.writer, r, c, b_size, partition, avail_u, avail_l);
651
+ } else if (has_cols) {
652
+ write_split_or_horz(ec, ctx.writer, r, c, b_size, avail_u, avail_l);
653
+ } else if (has_rows) {
654
+ write_split_or_vert(ec, ctx.writer, r, c, b_size, avail_u, avail_l);
655
+ }
656
+
657
+ if (partition === PARTITION_SPLIT) {
658
+ const sub_size = SPLIT_SIZE[b_size];
659
+
660
+ write_partition_tree(ec, r, c, sub_size, depth + 1);
661
+ write_partition_tree(ec, r, c + half4x4, sub_size, depth + 1);
662
+ write_partition_tree(ec, r + half4x4, c, sub_size, depth + 1);
663
+ write_partition_tree(ec, r + half4x4, c + half4x4, sub_size, depth + 1);
664
+
665
+ return;
666
+ }
667
+
668
+ const sub_size = PARTITION_SUBSIZE[partition * PARTITION_SUBSIZE_STRIDE + b_size];
669
+ const split_size = PARTITION_SUBSIZE[PARTITION_SPLIT * PARTITION_SUBSIZE_STRIDE + b_size];
670
+
671
+ const layout = PARTITION_LAYOUT[partition];
672
+
673
+ for (let i = 0; i < layout.length; i += 3) {
674
+ const row = r + ((layout[i] * num4x4) >> 2);
675
+ const column = c + ((layout[i + 1] * num4x4) >> 2);
676
+
677
+ if (row >= ctx.mi_rows || column >= ctx.mi_cols) {
678
+ continue;
679
+ }
680
+
681
+ write_block(ec, row, column, layout[i + 2] === 1 ? split_size : sub_size);
682
+ }
683
+ }
684
+
685
+ /**
686
+ * What the partition symbol at a node would cost, priced against the CDFs it will be written with.
687
+ *
688
+ * Routed through the same three cases the writer takes, with the cost writer standing in for it, so
689
+ * that a decision cannot be priced against a symbol the writer would not emit.
690
+ *
691
+ * @param {Av1EncodeContext} ec
692
+ * @param {number} r
693
+ * @param {number} c
694
+ * @param {number} b_size
695
+ * @param {number} partition
696
+ * @param {boolean} has_rows
697
+ * @param {boolean} has_cols
698
+ * @returns {number} lambda times the rate, so that it adds to a squared error
699
+ */
700
+ function partition_rate(ec, r, c, b_size, partition, has_rows, has_cols) {
701
+ const ctx = ec.frame_context;
702
+
703
+ // a block below 8x8 is already the smallest the format divides into, so nothing is coded to say
704
+ // it was not divided further
705
+ if (b_size < BLOCK_8X8) {
706
+ return 0;
707
+ }
708
+
709
+ const avail_u = is_inside(ctx, r - 1, c) ? 1 : 0;
710
+ const avail_l = is_inside(ctx, r, c - 1) ? 1 : 0;
711
+
712
+ const writer = ec.partition_cost_writer;
713
+
714
+ writer.reset_costing();
715
+
716
+ if (has_rows && has_cols) {
717
+ write_partition(ec, writer, r, c, b_size, partition, avail_u, avail_l);
718
+ } else if (has_cols) {
719
+ write_split_or_horz(ec, writer, r, c, b_size, avail_u, avail_l);
720
+ } else if (has_rows) {
721
+ write_split_or_vert(ec, writer, r, c, b_size, avail_u, avail_l);
722
+ }
723
+
724
+ return ec.lambda * writer.cost;
725
+ }
726
+
727
+ /**
728
+ * The square block half the size, by block size. Only the sizes this encoder produces are filled
729
+ * in; anything else is a bug rather than a case to handle.
730
+ * @type {Int32Array}
731
+ */
732
+ const SPLIT_SIZE = build_split_sizes();
733
+
734
+ /**
735
+ * @returns {Int32Array}
736
+ */
737
+ function build_split_sizes() {
738
+ const table = new Int32Array(BLOCK_128X128 + 1).fill(-1);
739
+
740
+ table[BLOCK_64X64] = BLOCK_32X32;
741
+ table[BLOCK_32X32] = BLOCK_16X16;
742
+ table[BLOCK_16X16] = BLOCK_8X8;
743
+ table[BLOCK_8X8] = BLOCK_4X4;
744
+
745
+ return table;
746
+ }
747
+
748
+ /**
749
+ * @param {Av1EncodeContext} ec
750
+ * @param {SymbolWriter} writer
751
+ * @param {number} r
752
+ * @param {number} c
753
+ * @param {number} b_size
754
+ * @param {number} partition
755
+ * @param {number} avail_u
756
+ * @param {number} avail_l
757
+ */
758
+ function write_partition(ec, writer, r, c, b_size, partition, avail_u, avail_l) {
759
+ const ctx = ec.frame_context;
760
+
761
+ const context = partition_context(ctx, r, c, b_size, avail_u, avail_l);
762
+ const selected = partition_cdf(ctx, b_size, context);
763
+
764
+ writer.write_symbol(partition, selected.cdf, selected.offset, selected.symbols);
765
+ }
766
+
767
+ /**
768
+ * Write the one-bit decision the decoder reads when the block runs off the bottom of the picture.
769
+ *
770
+ * The CDF is built from the partition CDF at every use and never adapts, which the writer has to
771
+ * respect too — an update here would be an update the decoder does not make.
772
+ *
773
+ * @param {Av1EncodeContext} ec
774
+ * @param {SymbolWriter} writer
775
+ * @param {number} r
776
+ * @param {number} c
777
+ * @param {number} b_size
778
+ * @param {number} avail_u
779
+ * @param {number} avail_l
780
+ */
781
+ function write_split_or_horz(ec, writer, r, c, b_size, avail_u, avail_l) {
782
+ const ctx = ec.frame_context;
783
+
784
+ const context = partition_context(ctx, r, c, b_size, avail_u, avail_l);
785
+ const selected = partition_cdf(ctx, b_size, context);
786
+
787
+ const cdf = selected.cdf;
788
+ const base = selected.offset;
789
+
790
+ let psum = probability_of(cdf, base, PARTITION_VERT)
791
+ + probability_of(cdf, base, PARTITION_SPLIT)
792
+ + probability_of(cdf, base, PARTITION_HORZ_A)
793
+ + probability_of(cdf, base, PARTITION_VERT_A)
794
+ + probability_of(cdf, base, PARTITION_VERT_B);
795
+
796
+ if (b_size !== BLOCK_128X128) {
797
+ psum += probability_of(cdf, base, PARTITION_VERT_4);
798
+ }
799
+
800
+ write_binary(writer, psum);
801
+ }
802
+
803
+ /**
804
+ * @param {Av1EncodeContext} ec
805
+ * @param {SymbolWriter} writer
806
+ * @param {number} r
807
+ * @param {number} c
808
+ * @param {number} b_size
809
+ * @param {number} avail_u
810
+ * @param {number} avail_l
811
+ */
812
+ function write_split_or_vert(ec, writer, r, c, b_size, avail_u, avail_l) {
813
+ const ctx = ec.frame_context;
814
+
815
+ const context = partition_context(ctx, r, c, b_size, avail_u, avail_l);
816
+ const selected = partition_cdf(ctx, b_size, context);
817
+
818
+ const cdf = selected.cdf;
819
+ const base = selected.offset;
820
+
821
+ let psum = probability_of(cdf, base, PARTITION_HORZ)
822
+ + probability_of(cdf, base, PARTITION_SPLIT)
823
+ + probability_of(cdf, base, PARTITION_HORZ_A)
824
+ + probability_of(cdf, base, PARTITION_HORZ_B)
825
+ + probability_of(cdf, base, PARTITION_VERT_A);
826
+
827
+ if (b_size !== BLOCK_128X128) {
828
+ psum += probability_of(cdf, base, PARTITION_HORZ_4);
829
+ }
830
+
831
+ write_binary(writer, psum);
832
+ }
833
+
834
+ /**
835
+ * The two-entry CDF the edge decisions are coded with.
836
+ * @type {Uint16Array}
837
+ */
838
+ const BINARY_PARTITION_CDF = new Uint16Array(3);
839
+
840
+ /**
841
+ * @param {SymbolWriter} writer
842
+ * @param {number} psum probability mass of the partitions that count as a split
843
+ */
844
+ function write_binary(writer, psum) {
845
+ BINARY_PARTITION_CDF[0] = (1 << 15) - psum;
846
+ BINARY_PARTITION_CDF[1] = 1 << 15;
847
+ BINARY_PARTITION_CDF[2] = 0;
848
+
849
+ const saved = writer.disable_cdf_update;
850
+
851
+ writer.disable_cdf_update = 1;
852
+
853
+ writer.write_symbol(1, BINARY_PARTITION_CDF, 0, 2);
854
+
855
+ writer.disable_cdf_update = saved;
856
+ }
857
+
858
+ /**
859
+ * Choose everything about one block and code it against the cost writer, so that the partition
860
+ * decision above it has a price to compare.
861
+ *
862
+ * The block really is coded: the contexts advance, the reconstruction lands in the plane, the mode
863
+ * info is recorded. That is what the block after it has to predict and derive contexts from, and
864
+ * pricing a decision against a state that will not exist is pricing the wrong decision. Undoing it
865
+ * is the snapshot's job.
866
+ *
867
+ * @param {Av1EncodeContext} ec
868
+ * @param {number} r
869
+ * @param {number} c
870
+ * @param {number} b_size
871
+ * @returns {number} squared error plus lambda times rate
872
+ */
873
+ function search_block(ec, r, c, b_size) {
874
+ const ctx = ec.frame_context;
875
+
876
+ begin_block(ec, r, c, b_size);
877
+
878
+ ctx.y_mode = choose_luma_mode(ec);
879
+
880
+ choose_transform_depth(ec);
881
+
882
+ choose_luma_transform(ec);
883
+
884
+ if (ctx.has_chroma === 1) {
885
+ materialise_plane(ec, 0);
886
+
887
+ ctx.uv_mode = choose_chroma_mode(ec);
888
+ } else {
889
+ ctx.uv_mode = DC_PRED;
890
+ }
891
+
892
+ // nothing was coded, so nothing has to be: coding the block as skipped saves the per-plane
893
+ // "this transform block is empty" symbols, and says the same thing
894
+ ctx.skip = block_is_empty(ec) ? 1 : 0;
895
+
896
+ const writer = ec.block_cost_writer;
897
+
898
+ writer.reset_costing();
899
+
900
+ code_block(ec, writer, STAGE_CODE);
901
+
902
+ return ec.block_distortion + ec.lambda * writer.cost;
903
+ }
904
+
905
+ /**
906
+ * Code one block for real, from the decisions the search recorded.
907
+ *
908
+ * The levels are worked out again rather than kept: the state here is the state the search was in
909
+ * when it made them, so the same prediction, the same transform and the same quantiser produce the
910
+ * same numbers, and a superblock's worth of levels does not have to be carried between the passes.
911
+ *
912
+ * @param {Av1EncodeContext} ec
913
+ * @param {number} r
914
+ * @param {number} c
915
+ * @param {number} b_size
916
+ */
917
+ function write_block(ec, r, c, b_size) {
918
+ const ctx = ec.frame_context;
919
+
920
+ begin_block(ec, r, c, b_size);
921
+
922
+ const decisions = ec.decisions;
923
+
924
+ const field = ec.decision_stride;
925
+ const decided = r * ctx.mi_stride + c;
926
+
927
+ ctx.y_mode = decisions[DECISION_Y_MODE * field + decided];
928
+ ctx.uv_mode = ctx.has_chroma === 1 ? decisions[DECISION_UV_MODE * field + decided] : DC_PRED;
929
+ ctx.skip = decisions[DECISION_SKIP * field + decided];
930
+
931
+ ec.luma_tx_type = decisions[DECISION_TX_TYPE * field + decided];
932
+
933
+ ctx.tx_size = ctx.lossless === 1 ? TX_4X4 : decisions[DECISION_TX_SIZE * field + decided];
934
+
935
+ // the scalings go in the bitstream before the luma residual they are fitted to, so the luma has
936
+ // to be made before the block is written, and fitted against again. It is the same fit: the
937
+ // state it reads is the state the search read
938
+ if (ctx.uv_mode === UV_CFL_PRED) {
939
+ materialise_plane(ec, 0);
940
+
941
+ prepare_chroma_from_luma(
942
+ ec,
943
+ (ctx.mi_col >> ctx.subsampling_x) * MI_SIZE,
944
+ (ctx.mi_row >> ctx.subsampling_y) * MI_SIZE,
945
+ transform_size(ctx, 1)
946
+ );
947
+ }
948
+
949
+ code_block(ec, ctx.writer, STAGE_CODE);
950
+ }
951
+
952
+ /**
953
+ * Emit a block's mode info and residual, and record what it leaves behind.
954
+ *
955
+ * @param {Av1EncodeContext} ec
956
+ * @param {SymbolWriter} writer
957
+ * @param {number} stage always {@link STAGE_CODE}; the writer says whether this is the search
958
+ */
959
+ function code_block(ec, writer, stage) {
960
+ const ctx = ec.frame_context;
961
+
962
+ const r = ctx.mi_row;
963
+ const c = ctx.mi_col;
964
+
965
+ const bw4 = NUM_4X4_BLOCKS_WIDE[ctx.mi_size];
966
+ const bh4 = NUM_4X4_BLOCKS_HIGH[ctx.mi_size];
967
+
968
+ write_block_mode_info(ec, writer);
969
+
970
+ mark_cdef(ec, writer);
971
+
972
+ write_transform_size(ec, writer);
973
+
974
+ if (ctx.skip === 1) {
975
+ reset_block_context(ctx, bw4, bh4);
976
+ }
977
+
978
+ const stride = ctx.mi_stride;
979
+
980
+ for (let y = 0; y < bh4 && r + y < ctx.mi_rows; y++) {
981
+ for (let x = 0; x < bw4 && c + x < ctx.mi_cols; x++) {
982
+ const index = (r + y) * stride + c + x;
983
+
984
+ ctx.y_modes[index] = ctx.y_mode;
985
+
986
+ if (ctx.has_chroma === 1) {
987
+ ctx.uv_modes[index] = ctx.uv_mode;
988
+ }
989
+ }
990
+ }
991
+
992
+ const planes = 1 + ctx.has_chroma * 2;
993
+
994
+ ec.block_distortion = 0;
995
+
996
+ for (let plane = 0; plane < planes; plane++) {
997
+ ec.block_distortion += code_plane(ec, plane, writer, stage);
998
+ }
999
+
1000
+ for (let y = 0; y < bh4 && r + y < ctx.mi_rows; y++) {
1001
+ for (let x = 0; x < bw4 && c + x < ctx.mi_cols; x++) {
1002
+ const index = (r + y) * stride + c + x;
1003
+
1004
+ ctx.is_inters[index] = 0;
1005
+ ctx.skips[index] = ctx.skip;
1006
+ ctx.tx_sizes[index] = ctx.tx_size;
1007
+ ctx.inter_tx_sizes[index] = ctx.tx_size;
1008
+ ctx.mi_sizes[index] = ctx.mi_size;
1009
+ ctx.segment_ids[index] = 0;
1010
+ ctx.palette_sizes[index] = 0;
1011
+
1012
+ // the transform the search chose, kept where the write pass can find it.
1013
+ // `write_coefficients` records the same thing when the block codes any coefficients,
1014
+ // and records a plain DCT when it codes none — which is what the decoder does, and
1015
+ // would lose the choice for a block whose luma quantised to nothing while its chroma
1016
+ // did not. Nothing in this encoder reads the plane back for coding
1017
+ if (ctx.skip !== 1) {
1018
+ ctx.tx_types[index] = ec.luma_tx_type;
1019
+ }
1020
+ }
1021
+ }
1022
+ }
1023
+
1024
+ /**
1025
+ * Record that a superblock is one the directional filter will visit.
1026
+ *
1027
+ * The decoder reads an index here — zero bits wide, because there is one strength for the frame —
1028
+ * and only for a block that is not skipped, so a superblock every one of whose blocks was skipped
1029
+ * is left alone by the filter. `choose_filters` measures strengths against the frame the filter
1030
+ * would actually produce, so the encoder has to reach the same conclusion about which superblocks
1031
+ * those are.
1032
+ *
1033
+ * @param {Av1EncodeContext} ec
1034
+ * @param {SymbolWriter} writer, which says whether this is the writing pass
1035
+ * @see AV1 spec 5.11.56 "Read CDEF syntax", of which this is the mirror
1036
+ */
1037
+ function mark_cdef(ec, writer) {
1038
+ const ctx = ec.frame_context;
1039
+
1040
+ if (writer !== ctx.writer || ctx.skip === 1 || ec.sequence.enable_cdef === 0) {
1041
+ return;
1042
+ }
1043
+
1044
+ const cdef_size4 = NUM_4X4_BLOCKS_WIDE[BLOCK_64X64];
1045
+ const mask = ~(cdef_size4 - 1);
1046
+
1047
+ ctx.cdef_idx[(ctx.mi_row & mask) * ctx.mi_stride + (ctx.mi_col & mask)] = 0;
1048
+ }
1049
+
1050
+ /**
1051
+ * Point the context's per-block registers at a block, ahead of any decision about it.
1052
+ *
1053
+ * @param {Av1EncodeContext} ec
1054
+ * @param {number} r
1055
+ * @param {number} c
1056
+ * @param {number} b_size
1057
+ */
1058
+ function begin_block(ec, r, c, b_size) {
1059
+ const ctx = ec.frame_context;
1060
+
1061
+ ctx.mi_row = r;
1062
+ ctx.mi_col = c;
1063
+ ctx.mi_size = b_size;
1064
+
1065
+ const bw4 = NUM_4X4_BLOCKS_WIDE[b_size];
1066
+ const bh4 = NUM_4X4_BLOCKS_HIGH[b_size];
1067
+
1068
+ // a block four samples high or wide in a subsampled direction does not carry chroma of its own:
1069
+ // the format gives one chroma block to each pair, and the second of the pair codes it for both
1070
+ if (bh4 === 1 && ctx.subsampling_y === 1 && (r & 1) === 0) {
1071
+ ctx.has_chroma = 0;
1072
+ } else if (bw4 === 1 && ctx.subsampling_x === 1 && (c & 1) === 0) {
1073
+ ctx.has_chroma = 0;
1074
+ } else {
1075
+ ctx.has_chroma = ctx.num_planes > 1 ? 1 : 0;
1076
+ }
1077
+
1078
+ ctx.avail_u = is_inside(ctx, r - 1, c) ? 1 : 0;
1079
+ ctx.avail_l = is_inside(ctx, r, c - 1) ? 1 : 0;
1080
+ ctx.avail_u_chroma = ctx.avail_u;
1081
+ ctx.avail_l_chroma = ctx.avail_l;
1082
+
1083
+ if (ctx.has_chroma === 1) {
1084
+ // and where it does carry chroma for a pair, the chroma above and to the left of *that* is
1085
+ // a block further away than the one above and to the left of this
1086
+ if (ctx.subsampling_y === 1 && bh4 === 1) {
1087
+ ctx.avail_u_chroma = is_inside(ctx, r - 2, c) ? 1 : 0;
1088
+ }
1089
+
1090
+ if (ctx.subsampling_x === 1 && bw4 === 1) {
1091
+ ctx.avail_l_chroma = is_inside(ctx, r, c - 2) ? 1 : 0;
1092
+ }
1093
+ } else {
1094
+ ctx.avail_u_chroma = 0;
1095
+ ctx.avail_l_chroma = 0;
1096
+ }
1097
+
1098
+ ctx.skip = 0;
1099
+ ctx.segment_id = 0;
1100
+ ctx.palette_size_y = 0;
1101
+ ctx.palette_size_uv = 0;
1102
+ ctx.use_filter_intra = 0;
1103
+ ctx.filter_intra_mode = 0;
1104
+ ctx.angle_delta_y = 0;
1105
+ ctx.angle_delta_uv = 0;
1106
+ ctx.cfl_alpha_u = 0;
1107
+ ctx.cfl_alpha_v = 0;
1108
+
1109
+ ctx.tx_size = ctx.lossless === 1 ? TX_4X4 : MAX_TX_SIZE_RECT[b_size];
1110
+
1111
+ // the mode search runs before the transform search and has to run against something; a plain
1112
+ // DCT is the one type every transform size allows
1113
+ ec.luma_tx_type = DCT_DCT;
1114
+ }
1115
+
1116
+ /**
1117
+ * Whether every plane of the block quantised to nothing.
1118
+ *
1119
+ * @param {Av1EncodeContext} ec
1120
+ * @returns {boolean}
1121
+ */
1122
+ function block_is_empty(ec) {
1123
+ const ctx = ec.frame_context;
1124
+
1125
+ const planes = 1 + ctx.has_chroma * 2;
1126
+
1127
+ for (let plane = 0; plane < planes; plane++) {
1128
+ if (ec.chosen_empty[plane] === 0) {
1129
+ return false;
1130
+ }
1131
+ }
1132
+
1133
+ return true;
1134
+ }
1135
+
1136
+ /**
1137
+ * Write the block's skip flag and prediction modes, in the order the decoder reads them.
1138
+ *
1139
+ * @param {Av1EncodeContext} ec
1140
+ * @param {SymbolWriter} writer
1141
+ */
1142
+ function write_block_mode_info(ec, writer) {
1143
+ const ctx = ec.frame_context;
1144
+
1145
+ const cdf = ctx.cdf;
1146
+
1147
+ write_skip(ec, writer);
1148
+
1149
+ const stride = ctx.mi_stride;
1150
+
1151
+ const above_mode = INTRA_MODE_CONTEXT[ctx.avail_u === 1 ? ctx.y_modes[(ctx.mi_row - 1) * stride + ctx.mi_col] : DC_PRED];
1152
+ const left_mode = INTRA_MODE_CONTEXT[ctx.avail_l === 1 ? ctx.y_modes[ctx.mi_row * stride + ctx.mi_col - 1] : DC_PRED];
1153
+
1154
+ writer.write_symbol(ctx.y_mode, cdf.intra_frame_y_mode, ((above_mode * 5) + left_mode) * 14, 13);
1155
+
1156
+ // a directional mode carries a fine angle adjustment; this encoder always chooses the nominal
1157
+ // angle, but the symbol is not optional — the decoder reads it either way
1158
+ if (ctx.mi_size >= BLOCK_8X8 && is_directional_mode(ctx.y_mode)) {
1159
+ writer.write_symbol(MAX_ANGLE_DELTA, cdf.angle_delta, (ctx.y_mode - V_PRED) * 8, 7);
1160
+ }
1161
+
1162
+ if (ctx.has_chroma === 1) {
1163
+ if (cfl_is_allowed(ctx)) {
1164
+ writer.write_symbol(ctx.uv_mode, cdf.uv_mode_cfl_allowed, ctx.y_mode * 15, 14);
1165
+ } else {
1166
+ writer.write_symbol(ctx.uv_mode, cdf.uv_mode_cfl_not_allowed, ctx.y_mode * 14, 13);
1167
+ }
1168
+
1169
+ if (ctx.uv_mode === UV_CFL_PRED) {
1170
+ write_chroma_alphas(ec, writer);
1171
+ }
1172
+
1173
+ if (ctx.mi_size >= BLOCK_8X8 && is_directional_mode(ctx.uv_mode)) {
1174
+ writer.write_symbol(MAX_ANGLE_DELTA, cdf.angle_delta, (ctx.uv_mode - V_PRED) * 8, 7);
1175
+ }
1176
+ }
1177
+ }
1178
+
1179
+ /**
1180
+ * Write the two chroma-from-luma scalings, as a joint sign and then a magnitude each.
1181
+ *
1182
+ * The joint symbol has eight values and not nine: both scalings zero is not codable, because a
1183
+ * chroma-from-luma block that borrows nothing from the luma is a flat block, which the format
1184
+ * already has a mode for. `choose_chroma_mode` never offers one.
1185
+ *
1186
+ * @param {Av1EncodeContext} ec
1187
+ * @param {SymbolWriter} writer
1188
+ * @see AV1 spec 5.11.45 "Read CFL alphas syntax", of which this is the mirror
1189
+ */
1190
+ function write_chroma_alphas(ec, writer) {
1191
+ const ctx = ec.frame_context;
1192
+
1193
+ const sign_u = sign_of(ctx.cfl_alpha_u);
1194
+ const sign_v = sign_of(ctx.cfl_alpha_v);
1195
+
1196
+ writer.write_symbol(sign_u * 3 + sign_v - 1, ctx.cdf.cfl_sign, 0, 8);
1197
+
1198
+ if (sign_u !== 0) {
1199
+ const context = (sign_u - 1) * 3 + sign_v;
1200
+
1201
+ writer.write_symbol(Math.abs(ctx.cfl_alpha_u) - 1, ctx.cdf.cfl_alpha, context * 17, 16);
1202
+ }
1203
+
1204
+ if (sign_v !== 0) {
1205
+ const context = (sign_v - 1) * 3 + sign_u;
1206
+
1207
+ writer.write_symbol(Math.abs(ctx.cfl_alpha_v) - 1, ctx.cdf.cfl_alpha, context * 17, 16);
1208
+ }
1209
+ }
1210
+
1211
+ /**
1212
+ * @param {number} alpha
1213
+ * @returns {number} `0` for none, `1` for negative, `2` for positive, as the joint symbol counts them
1214
+ */
1215
+ function sign_of(alpha) {
1216
+ if (alpha === 0) {
1217
+ return 0;
1218
+ }
1219
+
1220
+ return alpha < 0 ? 1 : 2;
1221
+ }
1222
+
1223
+ /**
1224
+ * Write the block's transform size, as the number of times the largest one is halved.
1225
+ *
1226
+ * @param {Av1EncodeContext} ec
1227
+ * @param {SymbolWriter} writer
1228
+ * @see AV1 spec 5.11.15 "Block tx size syntax", of which this is the mirror
1229
+ */
1230
+ function write_transform_size(ec, writer) {
1231
+ const ctx = ec.frame_context;
1232
+
1233
+ const stride = ctx.mi_stride;
1234
+
1235
+ if (ctx.lossless !== 1 && ctx.mi_size > BLOCK_4X4) {
1236
+ const max_rect = MAX_TX_SIZE_RECT[ctx.mi_size];
1237
+
1238
+ let depth = 0;
1239
+
1240
+ for (let size = max_rect; size !== ctx.tx_size; size = SPLIT_TX_SIZE[size]) {
1241
+ depth++;
1242
+ }
1243
+
1244
+ writer.write_symbol(depth, depth_cdf(ec), depth_offset(ec), depth_symbols(ec));
1245
+ }
1246
+
1247
+ for (let row = ctx.mi_row; row < ctx.mi_row + NUM_4X4_BLOCKS_HIGH[ctx.mi_size] && row < ctx.mi_rows; row++) {
1248
+ for (let col = ctx.mi_col; col < ctx.mi_col + NUM_4X4_BLOCKS_WIDE[ctx.mi_size] && col < ctx.mi_cols; col++) {
1249
+ ctx.inter_tx_sizes[row * stride + col] = ctx.tx_size;
1250
+ }
1251
+ }
1252
+ }
1253
+
1254
+ /**
1255
+ * The context a transform depth is coded against: whether the neighbours' transforms are at least
1256
+ * as large as the largest this block could use.
1257
+ *
1258
+ * @param {Av1EncodeContext} ec
1259
+ * @returns {number}
1260
+ * @see AV1 spec 8.3.2, `tx_depth`
1261
+ */
1262
+ function depth_context(ec) {
1263
+ const ctx = ec.frame_context;
1264
+
1265
+ const stride = ctx.mi_stride;
1266
+
1267
+ const max_rect = MAX_TX_SIZE_RECT[ctx.mi_size];
1268
+
1269
+ const above_w = ctx.avail_u === 1 ? TX_WIDTH[ctx.inter_tx_sizes[(ctx.mi_row - 1) * stride + ctx.mi_col]] : 0;
1270
+ const left_h = ctx.avail_l === 1 ? TX_HEIGHT[ctx.inter_tx_sizes[ctx.mi_row * stride + ctx.mi_col - 1]] : 0;
1271
+
1272
+ return (above_w >= TX_WIDTH[max_rect] ? 1 : 0) + (left_h >= TX_HEIGHT[max_rect] ? 1 : 0);
1273
+ }
1274
+
1275
+ /**
1276
+ * @param {Av1EncodeContext} ec
1277
+ * @returns {Uint16Array}
1278
+ */
1279
+ function depth_cdf(ec) {
1280
+ const cdf = ec.frame_context.cdf;
1281
+
1282
+ const depth = MAX_TX_DEPTH[ec.frame_context.mi_size];
1283
+
1284
+ if (depth === 4) {
1285
+ return cdf.tx_64x64;
1286
+ }
1287
+
1288
+ if (depth === 3) {
1289
+ return cdf.tx_32x32;
1290
+ }
1291
+
1292
+ return depth === 2 ? cdf.tx_16x16 : cdf.tx_8x8;
1293
+ }
1294
+
1295
+ /**
1296
+ * @param {Av1EncodeContext} ec
1297
+ * @returns {number}
1298
+ */
1299
+ function depth_offset(ec) {
1300
+ const depth = MAX_TX_DEPTH[ec.frame_context.mi_size];
1301
+
1302
+ return depth_context(ec) * (depth === 1 ? 3 : 4);
1303
+ }
1304
+
1305
+ /**
1306
+ * @param {Av1EncodeContext} ec
1307
+ * @returns {number}
1308
+ */
1309
+ function depth_symbols(ec) {
1310
+ return MAX_TX_DEPTH[ec.frame_context.mi_size] === 1 ? 2 : 3;
1311
+ }
1312
+
1313
+ /**
1314
+ * What the block's transform depth symbol would cost.
1315
+ *
1316
+ * @param {Av1EncodeContext} ec
1317
+ * @param {number} depth
1318
+ * @returns {number} cost in `COST_ONE_BIT` units
1319
+ */
1320
+ function transform_depth_cost(ec, depth) {
1321
+ if (ec.frame_context.lossless === 1 || ec.frame_context.mi_size <= BLOCK_4X4) {
1322
+ return 0;
1323
+ }
1324
+
1325
+ return symbol_cost(depth_cdf(ec), depth_offset(ec), depth, depth_symbols(ec));
1326
+ }
1327
+
1328
+ /**
1329
+ * Choose how finely the block's luma is cut into transforms.
1330
+ *
1331
+ * A transform spanning detail it cannot fit spends coefficients describing the mismatch; four
1332
+ * transforms that each fit spend fewer, and cost one symbol saying so. Only luma is asked — chroma's
1333
+ * transform follows from the block's subsampled size and is not coded.
1334
+ *
1335
+ * Searched after the mode and before the transform type, on the same argument as the type: the mode
1336
+ * that predicts best at the largest transform is the mode that predicts best at any of them.
1337
+ *
1338
+ * @param {Av1EncodeContext} ec
1339
+ */
1340
+ function choose_transform_depth(ec) {
1341
+ const ctx = ec.frame_context;
1342
+
1343
+ const max_rect = MAX_TX_SIZE_RECT[ctx.mi_size];
1344
+
1345
+ ctx.tx_size = max_rect;
1346
+
1347
+ if (ctx.lossless === 1 || ctx.mi_size <= BLOCK_4X4) {
1348
+ return;
1349
+ }
1350
+
1351
+ const depths = Math.min(ec.transform_depths, MAX_TX_DEPTH[ctx.mi_size] + 1);
1352
+
1353
+ let best = max_rect;
1354
+ let best_cost = ec.chosen_plane_cost + ec.lambda * transform_depth_cost(ec, 0);
1355
+
1356
+ let size = max_rect;
1357
+
1358
+ for (let depth = 1; depth < depths; depth++) {
1359
+ size = SPLIT_TX_SIZE[size];
1360
+
1361
+ ctx.tx_size = size;
1362
+
1363
+ const cost = trial_plane(ec, 0) + ec.lambda * transform_depth_cost(ec, depth);
1364
+
1365
+ if (cost < best_cost) {
1366
+ best_cost = cost;
1367
+ best = size;
1368
+
1369
+ ec.chosen_empty[0] = ec.trial_empty;
1370
+ }
1371
+ }
1372
+
1373
+ ctx.tx_size = best;
1374
+
1375
+ // the transform search picks up from here, and it starts from what the winning depth cost
1376
+ ec.chosen_plane_cost = best_cost - ec.lambda * transform_depth_cost(ec, depth_of(ctx, best));
1377
+ }
1378
+
1379
+ /**
1380
+ * @param {Av1FrameContext} ctx
1381
+ * @param {number} tx_size
1382
+ * @returns {number} how many halvings from the largest transform the block allows
1383
+ */
1384
+ function depth_of(ctx, tx_size) {
1385
+ let depth = 0;
1386
+
1387
+ for (let size = MAX_TX_SIZE_RECT[ctx.mi_size]; size !== tx_size; size = SPLIT_TX_SIZE[size]) {
1388
+ depth++;
1389
+ }
1390
+
1391
+ return depth;
1392
+ }
1393
+
1394
+ /**
1395
+ * What the block's luma mode symbols would cost, which is part of what choosing one costs.
1396
+ *
1397
+ * @param {Av1EncodeContext} ec
1398
+ * @param {number} mode
1399
+ * @returns {number} cost in `COST_ONE_BIT` units
1400
+ */
1401
+ function luma_mode_cost(ec, mode) {
1402
+ const ctx = ec.frame_context;
1403
+
1404
+ const stride = ctx.mi_stride;
1405
+
1406
+ const above_mode = INTRA_MODE_CONTEXT[ctx.avail_u === 1 ? ctx.y_modes[(ctx.mi_row - 1) * stride + ctx.mi_col] : DC_PRED];
1407
+ const left_mode = INTRA_MODE_CONTEXT[ctx.avail_l === 1 ? ctx.y_modes[ctx.mi_row * stride + ctx.mi_col - 1] : DC_PRED];
1408
+
1409
+ let cost = symbol_cost(ctx.cdf.intra_frame_y_mode, ((above_mode * 5) + left_mode) * 14, mode, 13);
1410
+
1411
+ if (ctx.mi_size >= BLOCK_8X8 && is_directional_mode(mode)) {
1412
+ cost += symbol_cost(ctx.cdf.angle_delta, (mode - V_PRED) * 8, MAX_ANGLE_DELTA, 7);
1413
+ }
1414
+
1415
+ return cost;
1416
+ }
1417
+
1418
+ /**
1419
+ * @param {Av1EncodeContext} ec
1420
+ * @param {number} mode
1421
+ * @returns {number} cost in `COST_ONE_BIT` units
1422
+ */
1423
+ function chroma_mode_cost(ec, mode) {
1424
+ const ctx = ec.frame_context;
1425
+
1426
+ let cost = cfl_is_allowed(ctx)
1427
+ ? symbol_cost(ctx.cdf.uv_mode_cfl_allowed, ctx.y_mode * 15, mode, 14)
1428
+ : symbol_cost(ctx.cdf.uv_mode_cfl_not_allowed, ctx.y_mode * 14, mode, 13);
1429
+
1430
+ if (mode === UV_CFL_PRED) {
1431
+ const sign_u = sign_of(ctx.cfl_alpha_u);
1432
+ const sign_v = sign_of(ctx.cfl_alpha_v);
1433
+
1434
+ cost += symbol_cost(ctx.cdf.cfl_sign, 0, sign_u * 3 + sign_v - 1, 8);
1435
+
1436
+ if (sign_u !== 0) {
1437
+ cost += symbol_cost(ctx.cdf.cfl_alpha, ((sign_u - 1) * 3 + sign_v) * 17, Math.abs(ctx.cfl_alpha_u) - 1, 16);
1438
+ }
1439
+
1440
+ if (sign_v !== 0) {
1441
+ cost += symbol_cost(ctx.cdf.cfl_alpha, ((sign_v - 1) * 3 + sign_u) * 17, Math.abs(ctx.cfl_alpha_v) - 1, 16);
1442
+ }
1443
+ }
1444
+
1445
+ if (ctx.mi_size >= BLOCK_8X8 && is_directional_mode(mode)) {
1446
+ cost += symbol_cost(ctx.cdf.angle_delta, (mode - V_PRED) * 8, MAX_ANGLE_DELTA, 7);
1447
+ }
1448
+
1449
+ return cost;
1450
+ }
1451
+
1452
+ /**
1453
+ * Choose the block's luma mode by what it costs and what it loses.
1454
+ *
1455
+ * Every candidate is predicted and ranked by how far its prediction lands from the source, and only
1456
+ * the best few are then transformed, quantised and priced. The ranking is one prediction each; the
1457
+ * pricing is a transform, a quantisation, a run through the coefficient syntax and an inverse
1458
+ * transform, and the mode that wins on rate and distortion is almost always among the two or three
1459
+ * that predicted best.
1460
+ *
1461
+ * @param {Av1EncodeContext} ec
1462
+ * @returns {number}
1463
+ */
1464
+ function choose_luma_mode(ec) {
1465
+ const ctx = ec.frame_context;
1466
+
1467
+ const count = rank_modes(ec, 0, LUMA_CANDIDATES);
1468
+
1469
+ const trials = Math.min(Math.max(ec.mode_trials, 1), count);
1470
+
1471
+ let best = ec.mode_order[0];
1472
+ let best_cost = Infinity;
1473
+
1474
+ for (let i = 0; i < trials; i++) {
1475
+ const mode = ec.mode_order[i];
1476
+
1477
+ ctx.y_mode = mode;
1478
+
1479
+ const plane_cost = trial_plane(ec, 0);
1480
+
1481
+ const cost = plane_cost + ec.lambda * luma_mode_cost(ec, mode);
1482
+
1483
+ if (cost < best_cost) {
1484
+ best_cost = cost;
1485
+ best = mode;
1486
+
1487
+ // the transform search picks up from here, and the mode symbol is the same price
1488
+ // whatever transform follows it, so what it needs is the plane on its own
1489
+ ec.chosen_plane_cost = plane_cost;
1490
+
1491
+ ec.chosen_empty[0] = ec.trial_empty;
1492
+ }
1493
+ }
1494
+
1495
+ return best;
1496
+ }
1497
+
1498
+ /**
1499
+ * Choose the block's chroma mode, which both chroma planes share and so is priced across both.
1500
+ *
1501
+ * @param {Av1EncodeContext} ec
1502
+ * @returns {number}
1503
+ */
1504
+ function choose_chroma_mode(ec) {
1505
+ const ctx = ec.frame_context;
1506
+
1507
+ const tx_size = transform_size(ctx, 1);
1508
+
1509
+ const start_x = (ctx.mi_col >> ctx.subsampling_x) * MI_SIZE;
1510
+ const start_y = (ctx.mi_row >> ctx.subsampling_y) * MI_SIZE;
1511
+
1512
+ const count = rank_modes(ec, 1, CHROMA_CANDIDATES);
1513
+
1514
+ const trials = Math.min(Math.max(ec.mode_trials, 1), count);
1515
+
1516
+ let best = ec.mode_order[0];
1517
+ let best_cost = Infinity;
1518
+
1519
+ for (let i = 0; i < trials; i++) {
1520
+ const mode = ec.mode_order[i];
1521
+
1522
+ ctx.uv_mode = mode;
1523
+
1524
+ if (mode === UV_CFL_PRED) {
1525
+ prepare_chroma_from_luma(ec, start_x, start_y, tx_size);
1526
+ }
1527
+
1528
+ const cost = trial_plane(ec, 1) + trial_plane(ec, 2) + ec.lambda * chroma_mode_cost(ec, mode);
1529
+
1530
+ if (cost < best_cost) {
1531
+ best_cost = cost;
1532
+ best = mode;
1533
+
1534
+ ec.chosen_empty[1] = ec.trial_empty_u;
1535
+ ec.chosen_empty[2] = ec.trial_empty;
1536
+ }
1537
+ }
1538
+
1539
+ ctx.uv_mode = best;
1540
+
1541
+ // the winner may not have been the last thing tried, and the scalings the block is written with
1542
+ // have to be the ones it was chosen with
1543
+ if (best === UV_CFL_PRED) {
1544
+ prepare_chroma_from_luma(ec, start_x, start_y, tx_size);
1545
+ }
1546
+
1547
+ return best;
1548
+ }
1549
+
1550
+ /**
1551
+ * Fit both chroma planes' scalings against the luma reconstruction under them.
1552
+ *
1553
+ * The fit needs the flat prediction to measure against, so the planes are predicted with a scaling
1554
+ * of nothing first — which is what a flat prediction is — and then fitted. Deterministic given the
1555
+ * block's state, so the search and the writing pass reach the same two numbers without one having
1556
+ * to tell the other.
1557
+ *
1558
+ * @param {Av1EncodeContext} ec
1559
+ * @param {number} start_x left edge of the chroma transform block
1560
+ * @param {number} start_y
1561
+ * @param {number} tx_size
1562
+ * @returns {boolean} whether the format can express what was fitted
1563
+ */
1564
+ function prepare_chroma_from_luma(ec, start_x, start_y, tx_size) {
1565
+ const ctx = ec.frame_context;
1566
+
1567
+ ctx.cfl_alpha_u = 0;
1568
+ ctx.cfl_alpha_v = 0;
1569
+
1570
+ predict_transform_block(ec, 1, start_x, start_y, tx_size, 0, 0);
1571
+ predict_transform_block(ec, 2, start_x, start_y, tx_size, 0, 0);
1572
+
1573
+ ctx.cfl_alpha_u = choose_chroma_alpha(ec, 1, start_x, start_y, tx_size);
1574
+ ctx.cfl_alpha_v = choose_chroma_alpha(ec, 2, start_x, start_y, tx_size);
1575
+
1576
+ return ctx.cfl_alpha_u !== 0 || ctx.cfl_alpha_v !== 0;
1577
+ }
1578
+
1579
+ /**
1580
+ * Code a plane without writing anything, so that what comes after it can see its reconstruction.
1581
+ *
1582
+ * Chroma-from-luma needs the luma the decoder will have, and the format puts the scalings in the
1583
+ * bitstream *before* the luma residual that produces it — so the encoder has to make the luma first
1584
+ * and write it second. This is the making.
1585
+ *
1586
+ * @param {Av1EncodeContext} ec
1587
+ * @param {number} plane
1588
+ */
1589
+ function materialise_plane(ec, plane) {
1590
+ ec.materialising = 1;
1591
+
1592
+ code_plane(ec, plane, ec.cost_writer.reset_costing(), STAGE_TRIAL);
1593
+
1594
+ ec.materialising = 0;
1595
+ }
1596
+
1597
+ /**
1598
+ * Predict a plane with every candidate mode and order them by how close the prediction landed.
1599
+ *
1600
+ * Ranked on the first transform block only, which for every block this encoder emits outside the
1601
+ * lossless path is the whole plane.
1602
+ *
1603
+ * @param {Av1EncodeContext} ec
1604
+ * @param {number} plane the plane to rank on; `1` ranks both chroma planes together
1605
+ * @param {number[]} candidates
1606
+ * @returns {number} how many entries of `ec.mode_order` were filled
1607
+ */
1608
+ function rank_modes(ec, plane, candidates) {
1609
+ const ctx = ec.frame_context;
1610
+
1611
+ const order = ec.mode_order;
1612
+ const errors = ec.mode_error;
1613
+
1614
+ const sub_x = plane > 0 ? ctx.subsampling_x : 0;
1615
+ const sub_y = plane > 0 ? ctx.subsampling_y : 0;
1616
+
1617
+ const tx_size = transform_size(ctx, plane);
1618
+
1619
+ // the mode info position is subsampled and *then* scaled, never the other way round: a block at
1620
+ // an odd mode-info column shares its chroma with the block beside it, and the pair's chroma
1621
+ // starts at the even one. Scaling first puts it half a chroma block to the right, off the end
1622
+ // of the row and into the start of the next
1623
+ const start_x = (ctx.mi_col >> sub_x) * MI_SIZE;
1624
+ const start_y = (ctx.mi_row >> sub_y) * MI_SIZE;
1625
+
1626
+ let filled = 0;
1627
+
1628
+ for (let i = 0; i < candidates.length; i++) {
1629
+ const mode = candidates[i];
1630
+
1631
+ if (mode === UV_CFL_PRED && !cfl_is_allowed(ctx)) {
1632
+ continue;
1633
+ }
1634
+
1635
+ if (plane === 0) {
1636
+ ctx.y_mode = mode;
1637
+ } else {
1638
+ ctx.uv_mode = mode;
1639
+ }
1640
+
1641
+ if (mode === UV_CFL_PRED && !prepare_chroma_from_luma(ec, start_x, start_y, tx_size)) {
1642
+ // nothing of the luma's shape is worth borrowing, and the format cannot say so: a
1643
+ // chroma-from-luma block scaling the luma by nothing is a flat block, which has its own
1644
+ // mode already
1645
+ continue;
1646
+ }
1647
+
1648
+ predict_transform_block(ec, plane, start_x, start_y, tx_size, 0, 0);
1649
+
1650
+ let error = transform_block_error(ec, plane, start_x, start_y, tx_size);
1651
+
1652
+ if (plane === 1) {
1653
+ predict_transform_block(ec, 2, start_x, start_y, tx_size, 0, 0);
1654
+
1655
+ error += transform_block_error(ec, 2, start_x, start_y, tx_size);
1656
+ }
1657
+
1658
+ let at = filled;
1659
+
1660
+ while (at > 0 && errors[at - 1] > error) {
1661
+ errors[at] = errors[at - 1];
1662
+ order[at] = order[at - 1];
1663
+ at--;
1664
+ }
1665
+
1666
+ errors[at] = error;
1667
+ order[at] = mode;
1668
+
1669
+ filled++;
1670
+ }
1671
+
1672
+ return filled;
1673
+ }
1674
+
1675
+ /**
1676
+ * A candidate being priced: nothing is recorded, nothing is written, and every mark the trial makes
1677
+ * is taken back.
1678
+ * @type {number}
1679
+ */
1680
+ const STAGE_TRIAL = 0;
1681
+
1682
+ /**
1683
+ * The block being made: the contexts, the reconstruction and the mode info all advance, because the
1684
+ * block after this one has to see them, and the symbols go wherever the writer sends them — to the
1685
+ * bitstream on the writing pass, and to a running total on the searching one.
1686
+ * @type {number}
1687
+ */
1688
+ const STAGE_CODE = 1;
1689
+
1690
+ /**
1691
+ * Code one plane of the current block, either to price it or to make it.
1692
+ *
1693
+ * One function rather than two near-copies, because they differ only in what they leave behind —
1694
+ * and a copy that drifted from the other would be an encoder whose decisions were made about a
1695
+ * block it did not then write.
1696
+ *
1697
+ * ### Why a trial does not reconstruct
1698
+ *
1699
+ * What a trial needs from a candidate is what it costs and what it loses. The cost comes from the
1700
+ * coefficient syntax; the loss the quantiser caused is a number the quantiser already had, in the
1701
+ * coefficient domain, and the specification's inverse transform carries a fixed sixty-fourth of
1702
+ * that energy into samples. So the trial reads the loss off the quantiser instead of running an
1703
+ * inverse transform and subtracting a thousand samples to find it again — which was, before this,
1704
+ * a fifth of the encoder's running time.
1705
+ *
1706
+ * It reconstructs anyway when a plane holds more than one transform block, because then each of
1707
+ * them predicts from the one before and a prediction from an unreconstructed block is not the
1708
+ * prediction the decoder will make.
1709
+ *
1710
+ * @param {Av1EncodeContext} ec
1711
+ * @param {number} plane
1712
+ * @param {SymbolWriter} writer
1713
+ * @param {number} stage {@link STAGE_TRIAL} or {@link STAGE_CODE}
1714
+ * @returns {number} squared error in samples, which is zero at the writing pass because nothing
1715
+ * downstream of it reads one
1716
+ */
1717
+ function code_plane(ec, plane, writer, stage) {
1718
+ const ctx = ec.frame_context;
1719
+
1720
+ const sub_x = plane > 0 ? ctx.subsampling_x : 0;
1721
+ const sub_y = plane > 0 ? ctx.subsampling_y : 0;
1722
+
1723
+ const tx_size = transform_size(ctx, plane);
1724
+
1725
+ const step_x = TX_WIDTH[tx_size] >> 2;
1726
+ const step_y = TX_HEIGHT[tx_size] >> 2;
1727
+
1728
+ const size = SUBSAMPLED_SIZE[(ctx.mi_size * 2 + sub_x) * 2 + sub_y];
1729
+
1730
+ const num4x4_w = NUM_4X4_BLOCKS_WIDE[size];
1731
+ const num4x4_h = NUM_4X4_BLOCKS_HIGH[size];
1732
+
1733
+ const base_x = (ctx.mi_col >> sub_x) * MI_SIZE;
1734
+ const base_y = (ctx.mi_row >> sub_y) * MI_SIZE;
1735
+
1736
+ const max_x = (ctx.mi_cols * MI_SIZE) >> sub_x;
1737
+ const max_y = (ctx.mi_rows * MI_SIZE) >> sub_y;
1738
+
1739
+ const trial = stage === STAGE_TRIAL ? 1 : 0;
1740
+
1741
+ // a trial has not decided the skip flag yet, and the block it is pricing is by definition one
1742
+ // that codes its coefficients
1743
+ const skip = trial === 0 && ctx.skip === 1;
1744
+
1745
+ // the writing pass is downstream of every decision, so what it costs in error is a number
1746
+ // nothing will read. Everything else — a candidate being priced, and a block being made so that
1747
+ // the partition above it has a price — is measured
1748
+ const measure = writer !== ctx.writer ? 1 : 0;
1749
+
1750
+ const one_block = step_x >= num4x4_w && step_y >= num4x4_h;
1751
+
1752
+ const levels = ec.levels[plane];
1753
+
1754
+ const levels_step = TX_WIDTH[tx_size] * TX_HEIGHT[tx_size];
1755
+
1756
+ let distortion = 0;
1757
+ let offset = 0;
1758
+ let empty = 1;
1759
+
1760
+ for (let y = 0; y < num4x4_h; y += step_y) {
1761
+ for (let x = 0; x < num4x4_w; x += step_x) {
1762
+ const start_x = base_x + 4 * x;
1763
+ const start_y = base_y + 4 * y;
1764
+
1765
+ if (start_x >= max_x || start_y >= max_y) {
1766
+ continue;
1767
+ }
1768
+
1769
+ predict_transform_block(ec, plane, start_x, start_y, tx_size, x, y);
1770
+
1771
+ let eob = 0;
1772
+ let tx_type = DCT_DCT;
1773
+
1774
+ if (skip) {
1775
+ if (measure === 1) {
1776
+ distortion += transform_block_error(ec, plane, start_x, start_y, tx_size);
1777
+ }
1778
+ } else {
1779
+ tx_type = transform_type_of(ec, plane, tx_size);
1780
+
1781
+ const energy = take_residual(ec, plane, start_x, start_y, tx_size);
1782
+
1783
+ const scan = get_scan(tx_size, tx_type);
1784
+
1785
+ // no coefficient of this residual can reach the quantiser's first step, so every
1786
+ // one of them is going to be zero and the transform that would have proved it one
1787
+ // multiply at a time is not run. The error is then the whole residual, which is
1788
+ // what its energy already says
1789
+ if (survives_quantiser(ec, plane, tx_size, tx_type, energy)) {
1790
+ forward_transform(ec, tx_size, tx_type);
1791
+
1792
+ eob = quantise_transform_block(ec, plane, tx_size, tx_type, ec.residual, levels, offset, scan);
1793
+ } else {
1794
+ levels.fill(0, offset, offset + levels_step);
1795
+
1796
+ ec.quantised_error = energy * COEFFICIENT_ENERGY_PER_SAMPLE;
1797
+ }
1798
+
1799
+ // a candidate is priced on what the quantiser says it lost, which costs nothing;
1800
+ // a block being *made* is priced on what it actually left, which costs a subtraction
1801
+ // over a reconstruction it had to build anyway. The two are within half a percent of
1802
+ // each other on a mode decision and eight per cent apart on a partition one, which
1803
+ // is what decides where each is used
1804
+ const cheap = trial === 1 && ec.exact_trial_distortion === 0 && ec.materialising === 0;
1805
+
1806
+ if (measure === 1 && cheap) {
1807
+ distortion += ec.quantised_error / COEFFICIENT_ENERGY_PER_SAMPLE;
1808
+ }
1809
+
1810
+ write_coefficients(ctx, writer, 1 - trial, plane, start_x, start_y, tx_size, tx_type, levels, offset, eob);
1811
+
1812
+ if (eob > 0) {
1813
+ empty = 0;
1814
+ }
1815
+
1816
+ if (eob > 0 && (!cheap || !one_block)) {
1817
+ reconstruct(ctx, plane, start_x, start_y, tx_size);
1818
+ }
1819
+
1820
+ if (measure === 1 && !cheap) {
1821
+ distortion += transform_block_error(ec, plane, start_x, start_y, tx_size);
1822
+ }
1823
+ }
1824
+
1825
+ if (plane === 0) {
1826
+ ctx.max_luma_w = start_x + TX_WIDTH[tx_size];
1827
+ ctx.max_luma_h = start_y + TX_HEIGHT[tx_size];
1828
+ }
1829
+
1830
+ if (writer === ctx.writer && ctx.trace !== null) {
1831
+ // the same record the decoder writes, so that a desync can be found by walking the
1832
+ // two traces together until they disagree — which is the only practical way to
1833
+ // debug an arithmetic coder that has already gone wrong
1834
+ ctx.trace.push({
1835
+ plane,
1836
+ x: start_x,
1837
+ y: start_y,
1838
+ tx_size,
1839
+ tx_type,
1840
+ mi_row: ctx.mi_row,
1841
+ mi_col: ctx.mi_col,
1842
+ mi_size: ctx.mi_size,
1843
+ y_mode: ctx.y_mode,
1844
+ uv_mode: ctx.uv_mode,
1845
+ use_filter_intra: 0,
1846
+ filter_intra_mode: 0,
1847
+ angle_delta_y: 0,
1848
+ palette_size_y: 0,
1849
+ skip: ctx.skip,
1850
+ eob
1851
+ });
1852
+ }
1853
+
1854
+ mark_decoded(ctx, plane, start_x, start_y, tx_size, 1);
1855
+
1856
+ offset += levels_step;
1857
+ }
1858
+ }
1859
+
1860
+ if (trial === 1) {
1861
+ // the marks a trial made are taken back, so that the next candidate starts where this one
1862
+ // did. They had to be made: within a block, each transform block predicts from the last
1863
+ for (let y = 0; y < num4x4_h; y += step_y) {
1864
+ for (let x = 0; x < num4x4_w; x += step_x) {
1865
+ const start_x = base_x + 4 * x;
1866
+ const start_y = base_y + 4 * y;
1867
+
1868
+ if (start_x >= max_x || start_y >= max_y) {
1869
+ continue;
1870
+ }
1871
+
1872
+ mark_decoded(ctx, plane, start_x, start_y, tx_size, 0);
1873
+ }
1874
+ }
1875
+
1876
+ // chroma is priced two planes at a time and the second call would overwrite the first's
1877
+ // answer, so the U plane parks its result where the chroma decision can still find it
1878
+ if (plane === 1) {
1879
+ ec.trial_empty_u = empty;
1880
+ }
1881
+
1882
+ ec.trial_empty = empty;
1883
+ }
1884
+
1885
+ return distortion;
1886
+ }
1887
+
1888
+ /**
1889
+ * Price one candidate for one plane, leaving nothing behind.
1890
+ *
1891
+ * @param {Av1EncodeContext} ec
1892
+ * @param {number} plane
1893
+ * @returns {number} the Lagrangian cost of the plane: squared error plus lambda times rate
1894
+ */
1895
+ function trial_plane(ec, plane) {
1896
+ const writer = ec.cost_writer;
1897
+
1898
+ writer.reset_costing();
1899
+
1900
+ const distortion = code_plane(ec, plane, writer, STAGE_TRIAL);
1901
+
1902
+ return distortion + ec.lambda * writer.cost;
1903
+ }
1904
+
1905
+ /**
1906
+ * Whether any coefficient of the residual now in `ec.residual` can survive the quantiser.
1907
+ *
1908
+ * The bound is Cauchy-Schwarz on the transform matrix and is therefore a fact rather than a guess:
1909
+ * a `false` here is a block that would have quantised to nothing, and skipping the transform for it
1910
+ * changes what the encoder does by not one bit.
1911
+ *
1912
+ * @param {Av1EncodeContext} ec
1913
+ * @param {number} plane
1914
+ * @param {number} tx_size
1915
+ * @param {number} tx_type
1916
+ * @param {number} energy sum of the residual's squares
1917
+ * @returns {boolean}
1918
+ */
1919
+ function survives_quantiser(ec, plane, tx_size, tx_type, energy) {
1920
+ if (ec.frame_context.lossless === 1) {
1921
+ return true;
1922
+ }
1923
+
1924
+ const bound = forward_transform_bound(
1925
+ tx_size,
1926
+ row_is_dct(tx_type) ? FORWARD_DCT : FORWARD_ADST,
1927
+ column_is_dct(tx_type) ? FORWARD_DCT : FORWARD_ADST
1928
+ );
1929
+
1930
+ return bound * Math.sqrt(energy) >= quantiser_zero_threshold(ec, plane, tx_size);
1931
+ }
1932
+
1933
+ /**
1934
+ * Fill `ec.residual` with the difference between the source and the prediction now in the plane.
1935
+ *
1936
+ * @param {Av1EncodeContext} ec
1937
+ * @param {number} plane
1938
+ * @param {number} start_x
1939
+ * @param {number} start_y
1940
+ * @param {number} tx_size
1941
+ * @returns {number} the sum of the residual's squares, which is both what the block would cost if
1942
+ * nothing of it were coded and what decides whether anything can be
1943
+ */
1944
+ function take_residual(ec, plane, start_x, start_y, tx_size) {
1945
+ const ctx = ec.frame_context;
1946
+
1947
+ const width = TX_WIDTH[tx_size];
1948
+ const height = TX_HEIGHT[tx_size];
1949
+
1950
+ const residual = ec.residual;
1951
+
1952
+ const reconstruction = ctx.planes[plane];
1953
+ const reconstruction_stride = ctx.plane_stride[plane];
1954
+
1955
+ const source = ec.source[plane];
1956
+ const source_stride = ec.source_stride[plane];
1957
+
1958
+ let energy = 0;
1959
+
1960
+ for (let i = 0; i < height; i++) {
1961
+ const a = (start_y + i) * reconstruction_stride + start_x;
1962
+ const b = (start_y + i) * source_stride + start_x;
1963
+
1964
+ for (let j = 0; j < width; j++) {
1965
+ const difference = source[b + j] - reconstruction[a + j];
1966
+
1967
+ residual[i * width + j] = difference;
1968
+
1969
+ energy += difference * difference;
1970
+ }
1971
+ }
1972
+
1973
+ return energy;
1974
+ }
1975
+
1976
+ /**
1977
+ * @param {Av1EncodeContext} ec
1978
+ * @param {number} tx_size
1979
+ * @param {number} tx_type
1980
+ */
1981
+ function forward_transform(ec, tx_size, tx_type) {
1982
+ if (ec.frame_context.lossless === 1) {
1983
+ forward_wht_4x4(ec.residual, ec.scratch);
1984
+
1985
+ return;
1986
+ }
1987
+
1988
+ forward_transform_2d(
1989
+ ec.residual,
1990
+ tx_size,
1991
+ row_is_dct(tx_type) ? FORWARD_DCT : FORWARD_ADST,
1992
+ column_is_dct(tx_type) ? FORWARD_DCT : FORWARD_ADST
1993
+ );
1994
+ }
1995
+
1996
+ /**
1997
+ * The transform type a plane's transform block is coded with.
1998
+ *
1999
+ * Luma writes whichever type the search settled on. Chroma codes no type at all — it inherits one
2000
+ * from the chroma prediction mode, and the decoder derives exactly this.
2001
+ *
2002
+ * @param {Av1EncodeContext} ec
2003
+ * @param {number} plane
2004
+ * @param {number} tx_size
2005
+ * @returns {number}
2006
+ */
2007
+ function transform_type_of(ec, plane, tx_size) {
2008
+ if (plane === 0) {
2009
+ return ec.luma_tx_type;
2010
+ }
2011
+
2012
+ return compute_tx_type(ec.frame_context, 1, tx_size, 0, 0);
2013
+ }
2014
+
2015
+ /**
2016
+ * The types the intra sets hold that are a transform in each direction, less the plain DCT the mode
2017
+ * search has already priced.
2018
+ *
2019
+ * Both sets also hold the identity — coding a residual untransformed, which suits the hard edges of
2020
+ * a screenshot and almost never a photograph — and set one holds two more that are an identity in
2021
+ * one direction. `forward_transform_2d` builds its matrices by inverting the specification's
2022
+ * inverse transform, and there is no matrix to invert for an identity, so those three are out of
2023
+ * reach and are not searched. Everything here is in both sets.
2024
+ * @type {number[]}
2025
+ */
2026
+ const TRANSFORM_CANDIDATES = [ADST_DCT, DCT_ADST, ADST_ADST];
2027
+
2028
+ /**
2029
+ * Choose the transform the block's luma residual is coded with.
2030
+ *
2031
+ * Searched after the prediction mode rather than alongside it: the two interact, but pricing every
2032
+ * pairing is four times the work for a fraction of the gain, and the mode that wins under a plain
2033
+ * DCT is very nearly always the mode that wins under the best transform for it.
2034
+ *
2035
+ * A residual left by intra prediction is not stationary — it grows with distance from the edge the
2036
+ * prediction came from — and that is exactly the shape an ADST fits and a DCT does not. Which
2037
+ * direction it grows in depends on which edge, so all four pairings are worth asking about.
2038
+ *
2039
+ * @param {Av1EncodeContext} ec
2040
+ */
2041
+ function choose_luma_transform(ec) {
2042
+ const ctx = ec.frame_context;
2043
+
2044
+ ec.luma_tx_type = DCT_DCT;
2045
+
2046
+ const tx_size = transform_size(ctx, 0);
2047
+
2048
+ // at a large transform the format codes no type, and at quantizer index zero the block is
2049
+ // lossless and the transform is the Walsh-Hadamard pair whatever this said
2050
+ if (ctx.frame.base_q_idx === 0 || get_tx_set(tx_size, 0, ctx.frame.reduced_tx_set) === TX_SET_DCTONLY) {
2051
+ return;
2052
+ }
2053
+
2054
+ // nothing survived the quantiser, so there is no residual to fit a transform to and no symbol
2055
+ // naming one. Another transform could bring a coefficient back, but the mode search has already
2056
+ // been over the prediction space that would make one worth having
2057
+ if (ec.chosen_empty[0] === 1) {
2058
+ return;
2059
+ }
2060
+
2061
+ let best = DCT_DCT;
2062
+ let best_cost = ec.chosen_plane_cost;
2063
+
2064
+ const trials = Math.min(ec.transform_trials, TRANSFORM_CANDIDATES.length);
2065
+
2066
+ for (let i = 0; i < trials; i++) {
2067
+ ec.luma_tx_type = TRANSFORM_CANDIDATES[i];
2068
+
2069
+ const cost = trial_plane(ec, 0);
2070
+
2071
+ if (cost < best_cost) {
2072
+ best_cost = cost;
2073
+ best = ec.luma_tx_type;
2074
+
2075
+ ec.chosen_empty[0] = ec.trial_empty;
2076
+ }
2077
+ }
2078
+
2079
+ ec.luma_tx_type = best;
2080
+ }
2081
+
2082
+ /**
2083
+ * @param {Av1FrameContext} ctx
2084
+ * @returns {boolean}
2085
+ */
2086
+ function cfl_is_allowed(ctx) {
2087
+ if (ctx.lossless === 1) {
2088
+ const size = SUBSAMPLED_SIZE[(ctx.mi_size * 2 + ctx.subsampling_x) * 2 + ctx.subsampling_y];
2089
+
2090
+ return size === 0;
2091
+ }
2092
+
2093
+ return NUM_4X4_BLOCKS_WIDE[ctx.mi_size] * MI_SIZE <= 32 && NUM_4X4_BLOCKS_HIGH[ctx.mi_size] * MI_SIZE <= 32;
2094
+ }
2095
+
2096
+ /**
2097
+ * @param {Av1EncodeContext} ec
2098
+ * @param {SymbolWriter} writer
2099
+ */
2100
+ function write_skip(ec, writer) {
2101
+ const ctx = ec.frame_context;
2102
+
2103
+ const stride = ctx.mi_stride;
2104
+
2105
+ let context = 0;
2106
+
2107
+ if (ctx.avail_u === 1) {
2108
+ context += ctx.skips[(ctx.mi_row - 1) * stride + ctx.mi_col];
2109
+ }
2110
+
2111
+ if (ctx.avail_l === 1) {
2112
+ context += ctx.skips[ctx.mi_row * stride + ctx.mi_col - 1];
2113
+ }
2114
+
2115
+ writer.write_symbol(ctx.skip, ctx.cdf.skip, context * 3, 2);
2116
+ }
2117
+
2118
+ /**
2119
+ * Sum of squared differences between the reconstruction plane, which currently holds a trial
2120
+ * prediction, and the source.
2121
+ *
2122
+ * @param {Av1EncodeContext} ec
2123
+ * @param {number} plane
2124
+ * @param {number} start_x
2125
+ * @param {number} start_y
2126
+ * @param {number} tx_size
2127
+ * @returns {number}
2128
+ */
2129
+ function transform_block_error(ec, plane, start_x, start_y, tx_size) {
2130
+ const ctx = ec.frame_context;
2131
+
2132
+ const x0 = start_x;
2133
+ const y0 = start_y;
2134
+
2135
+ const x1 = Math.min(x0 + TX_WIDTH[tx_size], ctx.plane_width[plane]);
2136
+ const y1 = Math.min(y0 + TX_HEIGHT[tx_size], ctx.plane_height[plane]);
2137
+
2138
+ const reconstruction = ctx.planes[plane];
2139
+ const reconstruction_stride = ctx.plane_stride[plane];
2140
+
2141
+ const source = ec.source[plane];
2142
+ const source_stride = ec.source_stride[plane];
2143
+
2144
+ let error = 0;
2145
+
2146
+ for (let y = y0; y < y1; y++) {
2147
+ const a = y * reconstruction_stride;
2148
+ const b = y * source_stride;
2149
+
2150
+ for (let x = x0; x < x1; x++) {
2151
+ const difference = reconstruction[a + x] - source[b + x];
2152
+
2153
+ error += difference * difference;
2154
+ }
2155
+ }
2156
+
2157
+ return error;
2158
+ }
2159
+
2160
+ /**
2161
+ * The transform size a plane of the current block uses.
2162
+ *
2163
+ * @param {Av1FrameContext} ctx
2164
+ * @param {number} plane
2165
+ * @returns {number}
2166
+ */
2167
+ function transform_size(ctx, plane) {
2168
+ if (ctx.lossless === 1) {
2169
+ return TX_4X4;
2170
+ }
2171
+
2172
+ if (plane === 0) {
2173
+ return ctx.tx_size;
2174
+ }
2175
+
2176
+ const sub_x = ctx.subsampling_x;
2177
+ const sub_y = ctx.subsampling_y;
2178
+
2179
+ return MAX_TX_SIZE_RECT[SUBSAMPLED_SIZE[(ctx.mi_size * 2 + sub_x) * 2 + sub_y]];
2180
+ }
2181
+
2182
+ /**
2183
+ * @param {Av1EncodeContext} ec
2184
+ * @param {number} plane
2185
+ * @param {number} start_x
2186
+ * @param {number} start_y
2187
+ * @param {number} tx_size
2188
+ * @param {number} x
2189
+ * @param {number} y
2190
+ */
2191
+ function predict_transform_block(ec, plane, start_x, start_y, tx_size, x, y) {
2192
+ const ctx = ec.frame_context;
2193
+
2194
+ const sub_x = plane > 0 ? ctx.subsampling_x : 0;
2195
+ const sub_y = plane > 0 ? ctx.subsampling_y : 0;
2196
+
2197
+ const max_x = (ctx.mi_cols * MI_SIZE) >> sub_x;
2198
+ const max_y = (ctx.mi_rows * MI_SIZE) >> sub_y;
2199
+
2200
+ const intra = ctx.intra;
2201
+
2202
+ intra.bit_depth = ctx.bit_depth;
2203
+ intra.enable_intra_edge_filter = ctx.sequence.enable_intra_edge_filter;
2204
+ intra.angle_delta = 0;
2205
+ intra.use_filter_intra = 0;
2206
+ intra.filter_intra_mode = 0;
2207
+ intra.filter_type = get_filter_type(ctx, plane);
2208
+
2209
+ const have_left = ((plane === 0 ? ctx.avail_l : ctx.avail_l_chroma) === 1 || x > 0) ? 1 : 0;
2210
+ const have_above = ((plane === 0 ? ctx.avail_u : ctx.avail_u_chroma) === 1 || y > 0) ? 1 : 0;
2211
+
2212
+ const step_x = TX_WIDTH[tx_size] >> 2;
2213
+ const step_y = TX_HEIGHT[tx_size] >> 2;
2214
+
2215
+ const row = (start_y << sub_y) >> 2;
2216
+ const col = (start_x << sub_x) >> 2;
2217
+
2218
+ const sub_block_mi_row = row & 15;
2219
+ const sub_block_mi_col = col & 15;
2220
+
2221
+ const flags = ctx.block_decoded[plane];
2222
+
2223
+ const above_right = flags[
2224
+ (((sub_block_mi_row >> sub_y) - 1) + BLOCK_DECODED_ORIGIN) * BLOCK_DECODED_STRIDE
2225
+ + ((sub_block_mi_col >> sub_x) + step_x) + BLOCK_DECODED_ORIGIN
2226
+ ];
2227
+
2228
+ const below_left = flags[
2229
+ (((sub_block_mi_row >> sub_y) + step_y) + BLOCK_DECODED_ORIGIN) * BLOCK_DECODED_STRIDE
2230
+ + ((sub_block_mi_col >> sub_x) - 1) + BLOCK_DECODED_ORIGIN
2231
+ ];
2232
+
2233
+ // prediction writes the whole transform block whether or not the picture reaches that far, so
2234
+ // the plane is padded out to a whole superblock to receive it. Running past *that* wraps into
2235
+ // the next row and corrupts a strip of a block coded long ago, which is a very quiet way to be
2236
+ // wrong — so it is checked here rather than found later
2237
+ if (start_x + TX_WIDTH[tx_size] > ctx.plane_stride[plane] || start_y + TX_HEIGHT[tx_size] > ctx.plane_rows[plane]) {
2238
+ assert.ok(false, `a ${TX_WIDTH[tx_size]}x${TX_HEIGHT[tx_size]} transform at ${start_x},${start_y} runs off plane ${plane}`);
2239
+ }
2240
+
2241
+ const cfl = plane > 0 && ctx.uv_mode === UV_CFL_PRED;
2242
+
2243
+ predict_intra(
2244
+ intra,
2245
+ ctx.planes[plane],
2246
+ ctx.plane_stride[plane],
2247
+ plane,
2248
+ start_x,
2249
+ start_y,
2250
+ max_x - 1,
2251
+ max_y - 1,
2252
+ have_left,
2253
+ have_above,
2254
+ above_right,
2255
+ below_left,
2256
+ plane === 0 ? ctx.y_mode : (cfl ? DC_PRED : ctx.uv_mode),
2257
+ TX_WIDTH_LOG2[tx_size],
2258
+ TX_HEIGHT_LOG2[tx_size]
2259
+ );
2260
+
2261
+ if (cfl) {
2262
+ predict_chroma_from_luma(
2263
+ ctx.planes[plane],
2264
+ ctx.plane_stride[plane],
2265
+ ctx.planes[0],
2266
+ ctx.plane_stride[0],
2267
+ start_x,
2268
+ start_y,
2269
+ tx_size,
2270
+ plane === 1 ? ctx.cfl_alpha_u : ctx.cfl_alpha_v,
2271
+ ctx.subsampling_x,
2272
+ ctx.subsampling_y,
2273
+ ctx.max_luma_w,
2274
+ ctx.max_luma_h,
2275
+ ctx.bit_depth
2276
+ );
2277
+ }
2278
+ }
2279
+
2280
+ /**
2281
+ * Record that a transform block has been coded, which is what tells the transform block below and
2282
+ * to the right of it that there are samples there to predict from.
2283
+ *
2284
+ * A trial marks its transform blocks too and clears them again afterwards, with `decoded` of zero:
2285
+ * a block cut into several transform blocks predicts each from the last, so a trial that did not
2286
+ * mark would price a prediction the real pass would never make.
2287
+ *
2288
+ * @param {Av1FrameContext} ctx
2289
+ * @param {number} plane
2290
+ * @param {number} start_x
2291
+ * @param {number} start_y
2292
+ * @param {number} tx_size
2293
+ * @param {number} decoded `1` when coding, `0` to undo a trial's marks
2294
+ */
2295
+ function mark_decoded(ctx, plane, start_x, start_y, tx_size, decoded) {
2296
+ const sub_x = plane > 0 ? ctx.subsampling_x : 0;
2297
+ const sub_y = plane > 0 ? ctx.subsampling_y : 0;
2298
+
2299
+ const step_x = TX_WIDTH[tx_size] >> 2;
2300
+ const step_y = TX_HEIGHT[tx_size] >> 2;
2301
+
2302
+ const row = (start_y << sub_y) >> 2;
2303
+ const col = (start_x << sub_x) >> 2;
2304
+
2305
+ const sub_block_mi_row = row & 15;
2306
+ const sub_block_mi_col = col & 15;
2307
+
2308
+ const flags = ctx.block_decoded[plane];
2309
+
2310
+ const tx_size_plane = ctx.loop_filter_tx_sizes[plane];
2311
+ const tx_stride = ctx.plane_stride[plane] >> 2;
2312
+
2313
+ for (let i = 0; i < step_y; i++) {
2314
+ for (let j = 0; j < step_x; j++) {
2315
+ flags[
2316
+ (((sub_block_mi_row >> sub_y) + i) + BLOCK_DECODED_ORIGIN) * BLOCK_DECODED_STRIDE
2317
+ + (((sub_block_mi_col >> sub_x) + j) + BLOCK_DECODED_ORIGIN)
2318
+ ] = decoded;
2319
+
2320
+ if (decoded === 0) {
2321
+ continue;
2322
+ }
2323
+
2324
+ const index = ((row >> sub_y) + i) * tx_stride + (col >> sub_x) + j;
2325
+
2326
+ if (index < tx_size_plane.length) {
2327
+ tx_size_plane[index] = tx_size;
2328
+ }
2329
+ }
2330
+ }
2331
+ }