@woosh/meep-engine 3.8.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 (735) 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/rasterize/native/oit/shader_oit_blend.d.ts.map +1 -1
  634. package/src/shade/renderer/rasterize/native/oit/shader_oit_blend.js +5 -4
  635. package/src/shade/renderer/scene/serialization/decode_image_source_hdr.d.ts +14 -3
  636. package/src/shade/renderer/scene/serialization/decode_image_source_hdr.d.ts.map +1 -1
  637. package/src/shade/renderer/scene/serialization/decode_image_source_hdr.js +50 -51
  638. package/src/shade/renderer/scene/serialization/deserialize_scene.d.ts.map +1 -1
  639. package/src/shade/renderer/scene/serialization/deserialize_scene.js +531 -521
  640. package/src/shade/renderer/scene/serialization/write_image_source.d.ts.map +1 -1
  641. package/src/shade/renderer/scene/serialization/write_image_source.js +6 -4
  642. package/src/shade/renderer/shader/chunk/color/chunk_rgb_to_luminance.d.ts.map +1 -1
  643. package/src/shade/renderer/shader/chunk/color/chunk_rgb_to_luminance.js +34 -32
  644. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_eotf_st2084.d.ts +31 -0
  645. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_eotf_st2084.d.ts.map +1 -1
  646. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_eotf_st2084.js +62 -31
  647. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_inverse_eotf_st2084.d.ts +10 -0
  648. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_inverse_eotf_st2084.d.ts.map +1 -1
  649. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_inverse_eotf_st2084.js +32 -22
  650. package/src/shade/renderer/shader/chunk/color/sRGB/chunk_sRGBTransferOETF.d.ts.map +1 -1
  651. package/src/shade/renderer/shader/chunk/color/sRGB/chunk_sRGBTransferOETF.js +22 -21
  652. package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.d.ts +6 -0
  653. package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.d.ts.map +1 -1
  654. package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.js +110 -104
  655. package/src/shade/renderer/shader/chunk/color/xyz/chunk_rgb_to_xyz.d.ts.map +1 -1
  656. package/src/shade/renderer/shader/chunk/color/xyz/chunk_rgb_to_xyz.js +23 -21
  657. package/src/shade/renderer/shader/chunk/math/chunk_magnitude_floor.d.ts +9 -0
  658. package/src/shade/renderer/shader/chunk/math/chunk_magnitude_floor.d.ts.map +1 -0
  659. package/src/shade/renderer/shader/chunk/math/chunk_magnitude_floor.js +16 -0
  660. package/src/shade/renderer/shader/chunk/math/complex/chunk_complex_magnitude_floor.d.ts +10 -0
  661. package/src/shade/renderer/shader/chunk/math/complex/chunk_complex_magnitude_floor.d.ts.map +1 -0
  662. package/src/shade/renderer/shader/chunk/math/complex/chunk_complex_magnitude_floor.js +17 -0
  663. package/src/shade/renderer/shader/chunk/math/complex/chunk_complex_solve_cubic_blinn.d.ts.map +1 -1
  664. package/src/shade/renderer/shader/chunk/math/complex/chunk_complex_solve_cubic_blinn.js +7 -1
  665. package/src/shade/renderer/shader/chunk/math/complex/chunk_complex_square_root.d.ts.map +1 -1
  666. package/src/shade/renderer/shader/chunk/math/complex/chunk_complex_square_root.js +9 -3
  667. package/src/shade/renderer/shader/chunk/math/polynomial/chunk_solve_cubic.d.ts.map +1 -1
  668. package/src/shade/renderer/shader/chunk/math/polynomial/chunk_solve_cubic.js +13 -7
  669. package/src/shade/renderer/shader/chunk/math/polynomial/chunk_solve_cubic_blinn_smallest.d.ts.map +1 -1
  670. package/src/shade/renderer/shader/chunk/math/polynomial/chunk_solve_cubic_blinn_smallest.js +13 -4
  671. package/src/shade/renderer/shader/chunk/math/polynomial/chunk_solve_quadratic.d.ts.map +1 -1
  672. package/src/shade/renderer/shader/chunk/math/polynomial/chunk_solve_quadratic.js +20 -13
  673. package/src/shade/renderer/shader/chunk/math/polynomial/chunk_solve_quartic_neumark.d.ts.map +1 -1
  674. package/src/shade/renderer/shader/chunk/math/polynomial/chunk_solve_quartic_neumark.js +8 -4
  675. package/src/shade/renderer/shader/chunk/mboit/NOTES.md +35 -1
  676. package/src/shade/renderer/shader/chunk/mboit/moments/geometric/chunk_compute_transmittance_from_2_trigonometric_moments.d.ts.map +1 -1
  677. package/src/shade/renderer/shader/chunk/mboit/moments/geometric/chunk_compute_transmittance_from_2_trigonometric_moments.js +18 -5
  678. package/src/shade/renderer/shader/chunk/mboit/moments/geometric/chunk_compute_transmittance_from_3_trigonometric_moments.d.ts.map +1 -1
  679. package/src/shade/renderer/shader/chunk/mboit/moments/geometric/chunk_compute_transmittance_from_3_trigonometric_moments.js +21 -8
  680. package/src/shade/renderer/shader/chunk/mboit/moments/geometric/chunk_compute_transmittance_from_4_trigonometric_moments.d.ts.map +1 -1
  681. package/src/shade/renderer/shader/chunk/mboit/moments/geometric/chunk_compute_transmittance_from_4_trigonometric_moments.js +25 -12
  682. package/src/shade/renderer/shader/chunk/mboit/moments/geometric/chunk_generate_trigonometric_moments_4.d.ts +6 -2
  683. package/src/shade/renderer/shader/chunk/mboit/moments/geometric/chunk_generate_trigonometric_moments_4.d.ts.map +1 -1
  684. package/src/shade/renderer/shader/chunk/mboit/moments/geometric/chunk_generate_trigonometric_moments_4.js +42 -35
  685. package/src/shade/renderer/shader/chunk/mboit/moments/power/chunk_compute_transmittance_from_4_power_moments.d.ts.map +1 -1
  686. package/src/shade/renderer/shader/chunk/mboit/moments/power/chunk_compute_transmittance_from_4_power_moments.js +18 -5
  687. package/src/shade/renderer/shader/chunk/mboit/moments/power/chunk_compute_transmittance_from_6_power_moments.d.ts.map +1 -1
  688. package/src/shade/renderer/shader/chunk/mboit/moments/power/chunk_compute_transmittance_from_6_power_moments.js +24 -9
  689. package/src/shade/renderer/shader/chunk/mboit/moments/power/chunk_compute_transmittance_from_8_power_moments.d.ts.map +1 -1
  690. package/src/shade/renderer/shader/chunk/mboit/moments/power/chunk_compute_transmittance_from_8_power_moments.js +29 -13
  691. package/src/shade/renderer/shader/chunk/mboit/moments/power/chunk_generate_power_moments_4.d.ts +12 -0
  692. package/src/shade/renderer/shader/chunk/mboit/moments/power/chunk_generate_power_moments_4.d.ts.map +1 -1
  693. package/src/shade/renderer/shader/chunk/mboit/moments/power/chunk_generate_power_moments_4.js +38 -48
  694. package/src/view/elements/ColorPickerView.js +296 -296
  695. package/src/avif/codec/dec/Readme.md +0 -14
  696. package/src/avif/codec/dec/avif_dec.cpp +0 -234
  697. package/src/avif/codec/dec/avif_dec.d.ts +0 -3
  698. package/src/avif/codec/dec/avif_dec.d.ts.map +0 -1
  699. package/src/avif/codec/dec/avif_dec.js +0 -16
  700. package/src/avif/codec/dec/avif_dec.wasm +0 -0
  701. package/src/avif/codec/enc/README.md +0 -37
  702. package/src/avif/codec/enc/avif_enc.cpp +0 -216
  703. package/src/avif/codec/enc/avif_enc.d.ts +0 -3
  704. package/src/avif/codec/enc/avif_enc.d.ts.map +0 -1
  705. package/src/avif/codec/enc/avif_enc.js +0 -6144
  706. package/src/avif/codec/enc/avif_enc.wasm +0 -0
  707. package/src/avif/codec/enc/avif_enc_mt.d.ts +0 -3
  708. package/src/avif/codec/enc/avif_enc_mt.d.ts.map +0 -1
  709. package/src/avif/codec/enc/avif_enc_mt.js +0 -6811
  710. package/src/avif/codec/enc/avif_enc_mt.wasm +0 -0
  711. package/src/avif/codec/enc/avif_enc_mt.worker.d.mts +0 -2
  712. package/src/avif/codec/enc/avif_enc_mt.worker.d.mts.map +0 -1
  713. package/src/avif/codec/enc/avif_enc_mt.worker.d.ts +0 -11
  714. package/src/avif/codec/enc/avif_enc_mt.worker.d.ts.map +0 -1
  715. package/src/avif/codec/enc/avif_enc_mt.worker.js +0 -171
  716. package/src/avif/codec/enc/avif_enc_mt.worker.mjs +0 -1
  717. package/src/avif/decode.d.ts +0 -3
  718. package/src/avif/decode.d.ts.map +0 -1
  719. package/src/avif/decode.js +0 -42
  720. package/src/avif/encode.d.ts +0 -3
  721. package/src/avif/encode.d.ts.map +0 -1
  722. package/src/avif/encode.js +0 -75
  723. package/src/avif/meta.d.ts +0 -24
  724. package/src/avif/meta.d.ts.map +0 -1
  725. package/src/avif/meta.js +0 -23
  726. package/src/avif/tsconfig.tsbuildinfo +0 -1
  727. package/src/avif/utils.d.ts +0 -17
  728. package/src/avif/utils.d.ts.map +0 -1
  729. package/src/avif/utils.js +0 -31
  730. package/src/engine/asset/loaders/image/jpeg/idct8x8_fixed.d.ts +0 -23
  731. package/src/engine/asset/loaders/image/jpeg/idct8x8_fixed.d.ts.map +0 -1
  732. package/src/engine/asset/loaders/image/jpeg/idct8x8_fixed.js +0 -214
  733. package/src/engine/asset/loaders/image/png/inflate.d.ts +0 -7
  734. package/src/engine/asset/loaders/image/png/inflate.d.ts.map +0 -1
  735. package/src/engine/asset/loaders/image/png/inflate.js +0 -20
@@ -0,0 +1,779 @@
1
+ # COLOR_REVIEW — `src/core/color`
2
+
3
+ Date: 2026-08-28 · Reviewer: Claude (Opus 5) · Scope: every one of the 58 source files and 33 spec
4
+ files under `src/core/color`, plus the CPU/GPU boundary with `src/shade/renderer/shader/chunk/color`.
5
+
6
+ **Method.** Every source file was read in full. Claims that could be executed were executed: the
7
+ package's own suite (`npx vitest run src/core/color` — **33 files, 158 tests, all passing**) plus six
8
+ throwaway probe scripts run against the real modules (Appendix A). Every defect below marked
9
+ *(reproduced)* has a printed failing value next to it; nothing in §2 is inferred from reading alone.
10
+ Reachability was established by grepping actual `import ... from ".../<file>.js"` statements across
11
+ `src/`, not by symbol name.
12
+
13
+ Severity, following the `core/geom` reviews: **major** = wrong results, silent data loss, or a
14
+ cross-cutting trap the caller cannot predict; **minor** = consistency/ergonomics gap with no
15
+ correctness impact.
16
+
17
+ ---
18
+
19
+ ## 0. TL;DR
20
+
21
+ The package is a **well-built colour-science library with a badly maintained perimeter**. The
22
+ numerically hard parts — Lab, OkLab/OkHSV, PQ, ycxcz, the D65/CMF tables, ACES — are correct,
23
+ carefully documented, and in several places better than the textbook (the D65 white point is derived
24
+ from the actual matrix rows so linear white lands on `L* = 100, a* = 0, b* = 0` *exactly*, which I
25
+ confirmed). The problems are all at the edges: the ingest path (`parse_color`/`hex2rgb`), the
26
+ `Color` façade, the alpha channel, and the seams between approximation regimes.
27
+
28
+ Five things worth acting on first:
29
+
30
+ 1. **`kelvin_to_rgb` has a 0.078 hard step in blue at exactly 2000 K** (§2.1 C1, reproduced). The
31
+ author already blended the 6600 K seam; the 2000 K seam was missed. Any temperature ramp bands
32
+ there.
33
+ 2. **`rgb_to_kelvin` is non-injective below 2000 K** — 800 K, 1500 K and 1999 K all return 1000 K
34
+ (§2.1 C2, reproduced). It is a consequence of C1 and cannot be fixed independently.
35
+ 3. **Alpha is silently dropped in five public entry points** — `Color.from_linear_to_sRGB`,
36
+ `from_sRGB_to_linear`, and all four `color_lighten/darken/saturate/desaturate` (§2.1 C5, C6,
37
+ reproduced). `color_lerp` handles alpha correctly, which makes the inconsistency worse, not better.
38
+ 4. **`Color.parse` accepts malformed input and produces garbage instead of throwing** — `#f00`
39
+ yields `b: NaN`, `0xFF8000FF` yields `r: -0.502` (§2.1 C3, C4, reproduced). This is the one
40
+ defect class that reaches user-authored data.
41
+ 5. **A third of the package is unreachable** (§1.4): 8 files no module imports at all, another 12
42
+ reachable only from their own tests. Including `prototype_kelvin_to_rgb.js`, a dev scratch script
43
+ that calls `document.body.append` at import time from inside a core library folder.
44
+
45
+ The single largest *structural* issue is not a bug: **nothing in the package records whether a
46
+ `Color` holds linear or encoded values** (§3.4). `color_lighten` calls `linear_srgb_to_okhsv` on
47
+ whatever `Color` you hand it, and `Color.parse("#808080")` produces encoded values. Both are
48
+ reasonable; together they are wrong, silently.
49
+
50
+ ---
51
+
52
+ ## 1. Overall shape of the package
53
+
54
+ ### 1.1 Inventory
55
+
56
+ | | |
57
+ |---|---|
58
+ | Source files / LOC | 58 / 3,263 (544 of which are tabulated data) |
59
+ | Spec files / LOC / tests | 33 / 2,090 / 158 — all passing |
60
+ | Directories | 16 — `PQ` `YCbCr` `YCoCg` `colormap` `hex` `hsv` `hunt` `illuminant` `kelvin` `lab` `oklab` `operations` `sRGB` `tonemap` `xyz` `ycxcz` |
61
+ | Entry point | none — no barrel, consistent with the rest of `core` (no `index.js` exists anywhere under `src/core`). Not a gap. |
62
+
63
+ ### 1.2 The three layers that are actually there
64
+
65
+ The directory listing suggests one flat namespace; the code is really three layers with different
66
+ authors, ages, and quality levels.
67
+
68
+ **Layer A — the modern colour-science core.** `lab/`, `ycxcz/`, `oklab/`, `PQ/`, `illuminant/`,
69
+ `xyz/`, `tonemap/`, `colormap/`. Snake-case, out-param-first, offset-aware, heavily documented with
70
+ the *reason* for each choice, not just the formula. [illuminant/D65_TRISTIMULUS_XYZ.js](illuminant/D65_TRISTIMULUS_XYZ.js)
71
+ and [lab/xyz_to_lab.js](lab/xyz_to_lab.js) are the best files in the package: they explain why the
72
+ white point is derived rather than quoted, and what breaks if you mix sources. This layer exists
73
+ because of two consumers — the ꟻLIP image-difference metric (`engine/graphics/texture/sampler/compare/flip/`)
74
+ and the AVIF encoder — and it shows: it is exactly as complete as those two need and no more.
75
+
76
+ **Layer B — the legacy CSS/UI layer.** [Color.js](Color.js), [parse_color.js](parse_color.js),
77
+ `hex/`, `hsv/`, [int2rgb.js](int2rgb.js), [rgb2uint24.js](rgb2uint24.js), [rgb2uint32.js](rgb2uint32.js).
78
+ `name2name` naming, object-returning, allocating, 0–255 units, no offsets. This is the layer with
79
+ essentially all of §2's defects. It has 29 non-spec importers (via `Color.js`) and is the most-used
80
+ and least-maintained part of the package.
81
+
82
+ **Layer C — orphan experiments.** `hunt/`, `kelvin/`, `YCbCr/`, `YCoCg/`,
83
+ [sRGB/sRGB_cmf.js](sRGB/sRGB_cmf.js), [oklab/find_gamut_intersection.js](oklab/find_gamut_intersection.js),
84
+ [illuminant/D65_spd_analytical.js](illuminant/D65_spd_analytical.js). Each is a competent
85
+ implementation of something nobody calls (§1.4).
86
+
87
+ ### 1.3 Where the boundary is drawn wrong
88
+
89
+ Three placement problems, in descending order of how much they cost:
90
+
91
+ - **`operations/` sits above `Color`, but `Color` sits above nothing.** `Color.js` imports
92
+ `hsv2rgb_float`, `rgb2hsv`, `rgb2uint24/32`, `rgb_to_luminance`, `parse_color`, `rgb2hex`,
93
+ `linear_to_sRGB`, `sRGB_to_linear` — eight modules. Meanwhile `operations/` imports `Color`. So
94
+ the dependency graph is `operations → Color → {8 leaf modules}`, and the OkHSV operations live
95
+ *outside* the class while HSV/HSL/HCL/HSI setters live *inside* it. There is no principle
96
+ separating the two sets; `setHSV` could as easily have been `color_set_hsv`.
97
+ - **`hunt/` contains the wrong Hunt.** [hunt/xyz_to_hpe.js](hunt/xyz_to_hpe.js) is the
98
+ Hunt–Pointer–Estévez *cone response matrix*; the actual Hunt-*effect* function is
99
+ [lab/lab_apply_hunt_adjustment.js](lab/lab_apply_hunt_adjustment.js). The latter carries an
100
+ explicit disclaimer — *"Unrelated to `xyz_to_hpe`"* — which is the tell that the directory name is
101
+ doing damage. `xyz_to_hpe` has zero importers; it belongs in `xyz/` or nowhere.
102
+ - **`colormap/` holds one unused LUT.** Magma, base64-encoded, well documented, zero importers,
103
+ while the GPU side ships four different LUTs (inferno/plasma/turbo/viridis) that the CPU cannot
104
+ reach. The directory is right; the contents are the wrong four-fifths.
105
+
106
+ ### 1.4 Reachability
107
+
108
+ Established by grepping real import statements. This is the most actionable single table in the
109
+ review.
110
+
111
+ **Unreferenced by anything, including their own tests (8 files):**
112
+
113
+ | File | Note |
114
+ |---|---|
115
+ | [kelvin/prototype_kelvin_to_rgb.js](kelvin/prototype_kelvin_to_rgb.js) | **Side-effecting dev script.** Appends a canvas to `document.body` and `console.log`s at *import* time. Reaches into `engine/graphics` and `view/elements` from `core/`, inverting the layering. Delete or move to a playground. |
116
+ | [oklab/find_gamut_intersection.js](oklab/find_gamut_intersection.js) | 100 lines of correct Halley-iteration gamut math. Its whole purpose is gamut clipping, and no clipper exists (§4.3). |
117
+ | [int2rgb.js](int2rgb.js) | Third copy of uint24 unpacking (§5.1). |
118
+ | [hunt/xyz_to_hpe.js](hunt/xyz_to_hpe.js) | Matrix verified correct against the standard HPE matrix. |
119
+ | [illuminant/D65_spd_analytical.js](illuminant/D65_spd_analytical.js) | Sole importer of `planckian_radiance` → a dead 2-file subtree. |
120
+ | [operations/color_lighten.js](operations/color_lighten.js) · [color_saturate.js](operations/color_saturate.js) · [color_desaturate.js](operations/color_desaturate.js) | Untested *and* unused; `color_darken` is the only one of the four with a spec. |
121
+
122
+ **Reachable only from their own spec (12 files):** [PQ/PQ_to_linear.js](PQ/PQ_to_linear.js) (the
123
+ encoder only encodes), `YCbCr/*` (2), `YCoCg/*` (2), [colormap/MAGMA_LUT.js](colormap/MAGMA_LUT.js),
124
+ [kelvin/rgb_to_kelvin.js](kelvin/rgb_to_kelvin.js), [oklab/oklab_to_xyz.js](oklab/oklab_to_xyz.js),
125
+ [oklab/xyz_to_oklab.js](oklab/xyz_to_oklab.js), [operations/color_darken.js](operations/color_darken.js),
126
+ [operations/color_lerp.js](operations/color_lerp.js), [rgb_to_rgbe9995.js](rgb_to_rgbe9995.js),
127
+ [rgbe9995_to_rgb.js](rgbe9995_to_rgb.js), [sRGB/sRGB_cmf.js](sRGB/sRGB_cmf.js).
128
+
129
+ **Load-bearing (≥3 non-spec importers):** `Color.js` (29), `sRGB/sRGB_to_linear.js` (5),
130
+ `xyz/rgb_to_xyz.js` (4), `xyz/xyz_to_rgb.js` (4), `sRGB/linear_to_sRGB.js` (4),
131
+ `operations/color_scale_okhsv_channel.js` (4), `oklab/okhsv_to_linear_srgb.js` (4),
132
+ `illuminant/D65_TRISTIMULUS_XYZ.js` (3), `kelvin/kelvin_to_rgb.js` (3), `oklab/find_cusp.js` (3),
133
+ `oklab/oklab_to_linear_srgb.js` (3), `rgb_to_luminance.js` (3).
134
+
135
+ ---
136
+
137
+ ## 2. Correctness
138
+
139
+ ### 2.1 Confirmed defects
140
+
141
+ | # | Symbol | File | Defect | Sev |
142
+ |---|---|---|---|---|
143
+ | C1 | `kelvin_to_rgb` | [kelvin/kelvin_to_rgb.js:23](kelvin/kelvin_to_rgb.js) | Blue steps 0 → 0.0779 across 2000 K | major |
144
+ | C2 | `rgb_to_kelvin` | [kelvin/rgb_to_kelvin.js](kelvin/rgb_to_kelvin.js) | All T < 2000 K collapse to 1000 K | major |
145
+ | C3 | `hex2rgb` | [hex/hex2rgb.js:12](hex/hex2rgb.js) | 3-/4-digit hex silently yields `NaN` | major |
146
+ | C4 | `parse_color` | [parse_color.js:68](parse_color.js) | Numeric branch unmasked; negative channels | major |
147
+ | C5 | `Color.from_*` | [Color.js:788](Color.js) | Drop alpha; never fire `onChanged` | major |
148
+ | C6 | `color_lighten` &c. | [operations/color_scale_okhsv_channel.js](operations/color_scale_okhsv_channel.js) | Drop alpha (but `color_lerp` keeps it) | major |
149
+ | C7 | `rgb2uint32` | [rgb2uint32.js:17](rgb2uint32.js) | Returns signed int32, not uint32 | minor |
150
+ | C8 | `Color.setHCL` / `setHSI` | [Color.js:332](Color.js) | Emit out-of-gamut, unclamped, undocumented | minor |
151
+ | C9 | `Color.multiplyScalar` | [Color.js:702](Color.js) | Scales alpha along with RGB | minor |
152
+ | C10 | `Color.fromUint` | [Color.js:497](Color.js) | `>> 16` with no `& 0xFF` | minor |
153
+ | C11 | `parse_color` | [parse_color.js:6](parse_color.js) | Regexes unanchored | minor |
154
+ | C12 | `linear_srgb_to_okhsv` | [oklab/linear_srgb_to_okhsv.js:43](oklab/linear_srgb_to_okhsv.js) | Unguarded divide by zero chroma | minor |
155
+ | C13 | `color_lighten` | [operations/color_lighten.js](operations/color_lighten.js) | Multiplicative — cannot lighten black | minor |
156
+ | C14 | `rgb_to_luminance` | [rgb_to_luminance.js](rgb_to_luminance.js) | Doc conflates luminance with luma | minor |
157
+ | C15 | `Color.hash` | [Color.js:563](Color.js) | Ignores alpha | minor |
158
+ | C16 | `D65_spd_tabulated` | [illuminant/D65_spd_tabulated.js](illuminant/D65_spd_tabulated.js) | Boundary logic asymmetric with its CMF twin | minor |
159
+
160
+ ---
161
+
162
+ #### C1 · `kelvin_to_rgb` — 0.078 discontinuity in blue at exactly 2000 K · **major** · *(reproduced)*
163
+
164
+ `approximate_regime_red` gates the blue channel with `t <= 20 ? 0 : clamp01(...)`
165
+ ([kelvin/kelvin_to_rgb.js:23](kelvin/kelvin_to_rgb.js)). The logarithmic fit it guards does not
166
+ approach 0 at `t = 20`; it approaches 0.0779. So blue jumps by a step ~20× the 8-bit quantum:
167
+
168
+ ```
169
+ 1999.99 K → 1.000000, 0.544072, 0.000000
170
+ 2000.00 K → 1.000000, 0.544074, 0.000000
171
+ 2000.01 K → 1.000000, 0.544076, 0.077917 ← +0.0779 in one 0.01 K step
172
+ ```
173
+
174
+ The 1000 K seam is clean (the blackbody-fade hue is locked to the value the red regime produces at
175
+ `t = 10`, and it matches to 6 decimals — verified), and the 6600 K seam is explicitly blended over
176
+ `t ∈ [65, 67]`. So the author already understood this class of problem and solved it twice; this
177
+ third seam was simply not noticed.
178
+
179
+ **Fix:** either blend across `t ∈ [20, 20.x]` the way the 6600 K seam is blended, or — better —
180
+ shift the fit so the blue term is continuous at its own gate. The seam is where the physical
181
+ Draper-adjacent region hands over to the fit, so a short blend window matches the existing house
182
+ style. Add a spec that walks temperature in fine steps and asserts a bounded first difference on
183
+ each channel; the existing [kelvin/kelvin_to_rgb.spec.js](kelvin/kelvin_to_rgb.spec.js) has 9 tests
184
+ and none of them samples across a seam.
185
+
186
+ #### C2 · `rgb_to_kelvin` collapses everything below 2000 K to 1000 K · **major** · *(reproduced)*
187
+
188
+ The solver bisects on the ratio `b / r`. Because C1 pins blue to exactly 0 for all `t <= 20`, that
189
+ ratio is 0 across the entire 0–2000 K span, so the bisection has no gradient to follow and always
190
+ walks to the lower bound:
191
+
192
+ ```
193
+ kelvin_to_rgb(T) → rgb_to_kelvin:
194
+ 800 K → 1000 1500 K → 1000 1999 K → 1000
195
+ 1000 K → 1000 1800 K → 1000 2000 K → 1000
196
+ 2100 K → 2100 2500 K → 2500 (correct from here up)
197
+ ```
198
+
199
+ Candle-light (~1900 K) and tungsten (~2400 K) straddle the break, so this is not an academic range.
200
+ Two secondary issues in the same file: the green channel is never read, so any colour with a matching
201
+ `b/r` maps to the same temperature regardless of how far it is from the Planckian locus; and the
202
+ JSDoc promises "approx 1000 to 40000" while the function returns `0` for black.
203
+
204
+ **Fix:** C1 first — the collapse is downstream of it. Then either document the ±0.4 K `epsilon` and
205
+ the 2000 K floor honestly, or switch to a proper CCT estimator (McCamy's approximation over CIE
206
+ 1960 `uv`, or Robertson) which needs no solver, uses all three channels, and has a well-defined
207
+ Duv-based validity domain.
208
+
209
+ #### C3 · `hex2rgb` silently produces `NaN` for short-form hex · **major** · *(reproduced)*
210
+
211
+ [hex/hex2rgb.js](hex/hex2rgb.js) slices fixed byte offsets and never validates length, so the two
212
+ most common CSS hex forms decode to nonsense:
213
+
214
+ ```
215
+ hex2rgb('#f00') → { r: 240, g: 0, b: NaN, a: 255 }
216
+ parse_color('#f00') → [ 240, 0, NaN, 1 ]
217
+ ```
218
+
219
+ `Color.parse('#f00')` then reaches `set()`, whose `assert.notNaN` throws — but with a message about
220
+ a NaN channel, pointing at `Color.js`, five frames away from the actual cause. And that only holds
221
+ while asserts are live: the guard is `assert`, not a validating parser, so any build that strips
222
+ asserts propagates `NaN` downstream.
223
+
224
+ **Fix:** handle `#RGB` and `#RGBA` by expansion (`f00` → `ff0000`), and throw a message naming the
225
+ offending string for anything that is not 3/4/6/8 hex digits. `rgb2hex` should grow the matching
226
+ alpha form at the same time (§4.1).
227
+
228
+ #### C4 · `parse_color` numeric branch is unmasked and unvalidated · **major** · *(reproduced)*
229
+
230
+ [parse_color.js:68](parse_color.js) does `r = c >> 16` with no `& 0xFF` and no range check.
231
+ For any value with bit 23 set, the shift is signed:
232
+
233
+ ```
234
+ parse_color(0xFF8000) → [ 255, 128, 0, 1 ] ✓
235
+ parse_color(0xFF8000FF) → [ -128, 0, 255, 1 ] ✗ (a plausible RGBA literal)
236
+ parse_color(-1) → [ -1, 255, 255, 1 ] ✗
237
+ Color.parse(0xFF8000FF) → { r: -0.502, g: 0, b: 1, a: 1 }
238
+ ```
239
+
240
+ No assert fires, because `-0.502` is a finite number — `Color` explicitly supports out-of-`[0,1]`
241
+ values for HDR, so the very feature that makes the class useful is what lets this through silently.
242
+ `Color.parse`'s own JSDoc advertises `Color.parse(0xFF00FF)`, so numeric input is a documented path.
243
+
244
+ **Fix:** mask all three channels, and reject values outside `[0, 0xFFFFFF]` with a thrown error
245
+ rather than wrapping. Decide explicitly whether `0xAARRGGBB`-style 32-bit input is supported; today
246
+ it is neither supported nor rejected.
247
+
248
+ #### C5 · `Color.from_linear_to_sRGB` / `from_sRGB_to_linear` drop alpha and skip `onChanged` · **major** · *(reproduced)*
249
+
250
+ ```js
251
+ Color.from_linear_to_sRGB(new Color(0.5, 0.5, 0.5, 0.25))
252
+ // → { r: 0.735, g: 0.735, b: 0.735, a: 1 } alpha 0.25 lost
253
+
254
+ const out = new Color(); let fired = 0; out.onChanged.add(() => fired++);
255
+ Color.from_sRGB_to_linear(new Color(0.5, 0.5, 0.5), out);
256
+ // → out mutated to 0.214…, fired === 0 listeners never notified
257
+ ```
258
+
259
+ Two distinct causes, one call site. The underlying `linear_to_sRGB(output, 0, input, 0)` writes
260
+ indices 0–2 only, so with the default `output = new Color()` alpha stays at the constructor's `1`
261
+ and the caller's alpha is discarded. And because the write goes through `Color`'s numeric index
262
+ setters (`set 0(v) { this.r = v }`), it bypasses `set()` entirely — no equality check, no
263
+ `onChanged.send8`. Any observer bound to that `Color` (the editor's `ColorEditor`, a material
264
+ uniform, a signal-driven binding) sees stale state while the object has already changed. This is the
265
+ most dangerous defect in the review because it is *silent on both sides*.
266
+
267
+ **Fix:** in both statics, copy `input.a` to `output` and route the write through `output.set(...)`
268
+ rather than raw index assignment. The same pattern is used by `okhsv_to_linear_srgb(result, …)` in
269
+ `color_scale_okhsv_channel` — safe there only because the target is freshly allocated, which is a
270
+ precondition nothing states. Worth a comment on the index setters warning that writing a `Color`
271
+ positionally is an unobserved mutation.
272
+
273
+ #### C6 · The four `operations/` scale functions drop alpha · **major** · *(reproduced)*
274
+
275
+ ```js
276
+ color_lighten(new Color(0.2, 0.4, 0.6, 0.3), 0.5)
277
+ // → { r: 0.333, g: 0.667, b: 1.0, a: 1 } alpha 0.3 → 1
278
+ ```
279
+
280
+ [operations/color_scale_okhsv_channel.js](operations/color_scale_okhsv_channel.js) allocates
281
+ `new Color()` and never touches `.a`. Its sibling [color_lerp](operations/color_lerp.js) *does*
282
+ interpolate alpha (`r.a = lerp(color_0.a, color_1.a, _t)`), so within a five-file directory two
283
+ different alpha policies coexist with nothing marking the difference. Fix: `result.a = input.a` in
284
+ `color_scale_okhsv_channel`; it fixes all four callers at once.
285
+
286
+ #### C7 · `rgb2uint32` returns a signed int32 · **minor**
287
+
288
+ `(partial << 8) | (_a & 0xFF)` ([rgb2uint32.js:17](rgb2uint32.js)) overflows into the sign bit
289
+ for any red ≥ 0x80: `Color.white.toUint32() === -1`. This is **already known** —
290
+ [Color.spec.js:51](Color.spec.js) documents it and normalises with `>>> 0` in every assertion,
291
+ and `deserialize_scene.js` reads the value back through `>>>`. So it is not a live bug; it is a
292
+ method whose name promises a `uint32` and whose contract is "an int32 you must convert". Appending
293
+ `>>> 0` to the return is behaviour-preserving for every existing consumer (both already normalise)
294
+ and removes the wart. Low risk, worth doing.
295
+
296
+ #### C8 · `setHCL` and `setHSI` emit unclamped out-of-gamut values · **minor** · *(reproduced)*
297
+
298
+ ```
299
+ setHCL(0, 1, 0.5) → { r: 1.2, g: 0.2, b: 0.2 }
300
+ setHCL(1/3, 1, 0.5) → { r: -0.09, g: 0.91, b: -0.09 }
301
+ setHSI(0, 1, 0.5) → { r: 1.5, g: 0, b: 0 }
302
+ ```
303
+
304
+ For a class documented as "0 to 1 for SDR" this is surprising, and `setHSL`/`setHSV` — verified
305
+ correct against CSS at all six primaries — do not behave this way. `setHSI` additionally carries the
306
+ author's own `NOTE: I'm not sure if the implementation is correct`. Neither has a spec.
307
+ Both switch statements also leave `r`/`g`/`b` as bare `let` with no `default` case; the sextant range
308
+ makes that unreachable today, but it is one refactor away from `undefined` propagation.
309
+
310
+ **Fix:** decide per method whether the contract is "clamped to gamut" or "unbounded, caller clamps",
311
+ document it, and add a spec. If `setHSI` cannot be validated against a reference, deleting it is a
312
+ better outcome than shipping a method whose docstring says it might be wrong.
313
+
314
+ #### C9 · `multiplyScalar` scales alpha · **minor** · *(reproduced)*
315
+
316
+ `new Color(0.5,0.5,0.5,1).multiplyScalar(0.5)` → `{ …, a: 0.5 }`. Defensible as "scale the 4-vector",
317
+ but the overwhelming use of a scalar multiply on a colour is exposure/tint adjustment, where fading
318
+ transparency as a side effect is a bug in the caller that looks like a bug in the engine. `multiply`
319
+ (component-wise) has the same behaviour. At minimum document it; better, add `multiplyRGB`.
320
+
321
+ #### C10 · `Color.fromUint` does not mask the red byte · **minor** · *(reproduced)*
322
+
323
+ `value >> 16` with no `& 0xFF` ([Color.js:501](Color.js)). Round-trips with `toUint()` correctly
324
+ (uint24 never sets bit 31), but `fromUint(0xFF8000FF)` throws `Expected -128 >= 0` from inside
325
+ `uint8_to_float` — an error message that names neither the method nor the input. `int2rgb` masks all
326
+ three channels; `parse_color` masks two of three; `fromUint` masks two of three. Three copies, three
327
+ behaviours (§5.1).
328
+
329
+ #### C11 · `parse_color` regexes are unanchored · **minor** · *(reproduced)*
330
+
331
+ `parse_color('background: rgb(1,2,3) !important')` → `[1, 2, 3, 1]`. Lenient parsing of a
332
+ colour literal is a reasonable choice, but it is not the documented one ("Parses most CSS-format
333
+ colors"), and it means a malformed string that happens to contain a colour never surfaces as an
334
+ error. Anchor with `^…$` after trimming, or state the substring-scan behaviour in the docstring.
335
+
336
+ #### C12 · `linear_srgb_to_okhsv` divides by an unguarded chroma · **minor**
337
+
338
+ [oklab/linear_srgb_to_okhsv.js:43](oklab/linear_srgb_to_okhsv.js) computes `a_ = Lab[1] / C`
339
+ with an early return only for `L === 0`, not `C === 0`. In practice exact-zero chroma appears
340
+ unreachable from the sRGB path — the three OkLab row sums differ in their last bits, so neutrals
341
+ land at `C ≈ 1e-7` rather than 0 — but the consequence of that near-miss is itself worth knowing:
342
+
343
+ ```
344
+ linear rgb (1,1,1) → okhsv (0.24965…, 1.0348e-7, 1.0000000270)
345
+ linear rgb (0.5,0.5,0.5) → okhsv (0.24965…, 1.0348e-7, 0.7600066)
346
+ ```
347
+
348
+ Every neutral gets the same **arbitrary hue 0.2496** and a saturation of `1e-7` instead of `0`. That
349
+ is what makes `color_lerp(white, red)` behave (it interpolates from a stable, if meaningless, hue) —
350
+ but it is load-bearing accident, not design. Note also `v = 1.0000000270 > 1`, and the OkHSV
351
+ round-trip of white returns `(1.00000007, 0.99999998, 0.99999974)`: the `operations/` functions do
352
+ not clamp their output, so they can hand back marginally out-of-`[0,1]` colours.
353
+
354
+ **Fix:** early-return `s = 0, h = 0` when `C` is below a small epsilon (not just when it is exactly
355
+ 0), and clamp the final RGB in `color_scale_okhsv_channel`.
356
+
357
+ #### C13 · `color_lighten` cannot lighten black · **minor** · *(reproduced)*
358
+
359
+ `color_lighten(new Color(0,0,0), 1)` → `{0,0,0}`. The operation is `v * (1 + amount)`, and `0`
360
+ is a fixed point. Symmetrically `color_darken` cannot darken further once `v` hits 0, which is
361
+ fine, but the lighten direction failing at the most common input (black) is a design gap.
362
+ `v + amount * (1 - v)` is the usual formulation and has no fixed point at either end. Whatever is
363
+ chosen, this needs a spec — three of the four operations have none at all.
364
+
365
+ #### C14 · `rgb_to_luminance` documents itself as space-agnostic · **minor**
366
+
367
+ The docstring says *"Red component (linear or gamma-corrected)"* and the function is named
368
+ `…_luminance`. Applied to linear values these coefficients give **luminance**; applied to encoded
369
+ values they give **luma** (`Y′`). Both are legitimate quantities and both use these numbers by
370
+ definition, but they are not interchangeable, and the doc invites callers to treat them as such. The
371
+ GPU twin gets this right — `chunk_rgb_to_luminance` says *"Input color must be in linear space to get
372
+ correct results"*. Align the JS doc with it. (See also §3.5 on the coefficient precision split.)
373
+
374
+ #### C15 · `Color.hash()` ignores alpha · **minor** · *(reproduced)*
375
+
376
+ `hash()` delegates to `toUint()` (24-bit, RGB only) while `equals()` compares all four channels, so
377
+ colours differing only in alpha share a hash. The `equals ⇒ same hash` contract still holds, so this
378
+ is correct-but-lossy rather than broken; using `toUint32()` would make it exact at no cost.
379
+
380
+ #### C16 · `D65_spd_tabulated` and `xyz_cmf_tabulated` guard their edges differently · **minor**
381
+
382
+ [D65_spd_tabulated](illuminant/D65_spd_tabulated.js) uses `<=` / `>=` and then re-tests for equality
383
+ inside the rejection branch; [xyz_cmf_tabulated](xyz/xyz_cmf_tabulated.js) uses `<` / `>` and clamps
384
+ `i1`. Both produce correct values; they are two solutions to the same problem sitting in sibling
385
+ files, and one asserts its input while the other does not. Pick one shape.
386
+
387
+ ### 2.2 Verified correct
388
+
389
+ Recorded so the next reviewer does not re-derive it. Each was checked by execution, not by reading.
390
+
391
+ - **sRGB OETF/EOTF** round-trip to a maximum error of `2.2e-11` over 100,001 samples of `[0,1]`;
392
+ breakpoints `0.0031308` / `0.04045` are mutually consistent; negative and >1 inputs both behave.
393
+ - **`rgb_to_xyz` → `xyz_to_lab`** maps linear white to `(100, 0, 0)` **exactly**, and black to
394
+ `(0,0,0)`. This is the payoff of deriving `D65_TRISTIMULUS_XYZ` from the matrix rows and it works.
395
+ `xyz_to_ycxcz` lands within `5.6e-14` of the same, as expected.
396
+ - **YCbCr** Rec.709 coefficients re-derived from `Kr = 0.2126 / Kb = 0.0722`: forward and inverse
397
+ both match to 4 dp. **YCoCg** forward/inverse are exact algebraic inverses (verified symbolically).
398
+ **ycxcz ↔ xyz** is an exact inverse.
399
+ - **PQ** OETF/EOTF round-trip to ~1e-15 relative across `[0, 1]`; the 203-nit normalisation is
400
+ correctly applied by the one real consumer (`avif/encoder_worker.js`). The `PQ_constants.js`
401
+ docstring's warning about "the classic way this round-trip ends up off by a factor of ~49" is
402
+ earned — see §3.5 for where the engine does still disagree with itself.
403
+ - **rgbe9995** round-trips `[1,1,1]`, `[0.5,0.25,0.125]`, `[10,5,0]` exactly and `[0.001,0,0]` to
404
+ 0.05%; the shared-exponent crush at `[100000,1,1]` is inherent to the format. Both files are
405
+ marked `TODO unverified` — that TODO can be retired.
406
+ - **`xyz_to_hpe`** matches the standard Hunt–Pointer–Estévez matrix; in-place safe.
407
+ - **`setHSL`** matches CSS `hsl()` at all six primaries and at neutral.
408
+ - **In-place safety** claimed by `tonemap_aces`, `lab_apply_hunt_adjustment`, `rgb_to_xyz`,
409
+ `xyz_to_rgb`, `xyz_to_hpe` holds — all read their inputs before the first write.
410
+ - **Frozen constants** (`Color.red` &c.) do throw on mutation under module strict mode. Note the
411
+ failure is state-dependent: `Color.red.set(1,0,0,1)` returns silently via `set()`'s no-change
412
+ early return, while `Color.red.setA(0.5)` throws `TypeError`. Worth a one-line comment.
413
+
414
+ ---
415
+
416
+ ## 3. Consistency
417
+
418
+ ### 3.1 Seven calling conventions for one package
419
+
420
+ This is the package's largest consistency cost, and unlike naming it is not fixable by rename alone.
421
+
422
+ | Convention | Members |
423
+ |---|---|
424
+ | `(out, out_off, in, in_off)` | `linear_to_sRGB`, `sRGB_to_linear`, `xyz_to_lab`, `xyz_to_ycxcz`, `ycxcz_to_xyz`, `lab_apply_hunt_adjustment`, `tonemap_aces`, `lab_distance_hyab` (as `(a, a_off, b, b_off)`) |
425
+ | `(out, in)` — no offsets | `rgb_to_xyz`, `xyz_to_rgb`, `xyz_to_hpe` |
426
+ | `(out, out_off, …scalars)` | `rgb_to_ycocg`, `ycocg_to_rgb`, `kelvin_to_rgb`, `rgbe9995_to_rgb` |
427
+ | `(out, …scalars)` — no offset | `xyz_to_oklab`, `oklab_to_xyz`, `linear_srgb_to_oklab`, `oklab_to_linear_srgb`, `linear_srgb_to_okhsv`, `okhsv_to_linear_srgb`, `find_cusp`, `xyz_cmf_wyman`, `xyz_cmf_tabulated`, `sRGB_cmf` |
428
+ | `(…scalars) → new object` | `hsv2rgb`, `hsv2rgb_float`, `rgb2hsv`, `hex2rgb`, `int2rgb` |
429
+ | `(…scalars) → scalar` | `linear_to_PQ`, `PQ_to_linear`, `convert_channel_*`, `rgb_to_luminance`, `toe`, `toe_inv`, `compute_max_saturation`, `find_gamut_intersection`, `D65_spd_*`, `planckian_radiance`, `rgb_to_YCbCr_uint24`, `YCbCr_to_rgb_uint24`, `rgb_to_rgbe9995`, `rgb2uint24`, `rgb2uint32`, `rgb2hex` |
430
+ | `(Color, …) → new Color` | `color_lerp`, `color_lighten`, `color_darken`, `color_saturate`, `color_desaturate`, `color_scale_okhsv_channel` |
431
+
432
+ The split is not even directory-clean. `xyz/` holds both `rgb_to_xyz(out, in)` and
433
+ `xyz_cmf_wyman(out, nm)`. `oklab/` holds `(out, scalars)` conversions next to
434
+ `find_gamut_intersection(a,b,L1,C1,L0) → number`. Chaining across two conventions requires a
435
+ temporary and an offset the caller has to remember is *not* there:
436
+
437
+ ```js
438
+ rgb_to_xyz(xyz, rgb); // no offsets
439
+ xyz_to_lab(lab, 0, xyz, 0); // offsets
440
+ lab_apply_hunt_adjustment(lab, 0, lab, 0);
441
+ ```
442
+
443
+ — which is real code from `flip_compute_color_difference.js`.
444
+
445
+ **Recommendation:** the `(out, out_off, in, in_off)` form is the most capable and is what the newest
446
+ and most-used code uses; treat it as the target. The `(out, …scalars)` form is legitimate where the
447
+ inputs genuinely are not a contiguous triple (`kelvin_to_rgb`, the CMFs). What has no defence is
448
+ `(out, in)` without offsets on `rgb_to_xyz`/`xyz_to_rgb` — the two most-imported conversions in the
449
+ package (4 non-spec importers each) — and the allocating object-returning Layer-B set. Note this is
450
+ the same finding the `core/geom` review raised as *"API shape: parameter order, output-argument
451
+ placement, calling conventions"*; the same disease, one directory over.
452
+
453
+ ### 3.2 Units are not encoded in names or checked at boundaries
454
+
455
+ | Unit | Members |
456
+ |---|---|
457
+ | `[0,1]` float | `Color`, `hsv2rgb_float`, `sRGB_*`, all of `oklab/`, `rgb2uint24/32` inputs |
458
+ | `[0,255]` | `hsv2rgb`, `rgb2hex`, `hex2rgb`, `int2rgb`, `rgb_to_YCbCr_uint24`, `YCbCr_to_rgb_uint24` |
459
+ | **mixed within one value** | `parse_color` returns `[0-255, 0-255, 0-255, 0-1]` — RGB in bytes, alpha normalised |
460
+ | `L* ∈ [0,100]` | `xyz_to_lab`, `xyz_to_ycxcz` |
461
+ | `1.0 = 10000 nits` | `PQ/*` (documented loudly, and correctly) |
462
+ | Kelvin / Kelvin÷100 | `kelvin_to_rgb` public vs its three private regime helpers |
463
+
464
+ `hsv2rgb` and `hsv2rgb_float` differ *only* in output scale, and the byte version is the one with
465
+ the unsuffixed name — so the default-looking call is the non-default unit. `parse_color`'s mixed
466
+ return is the most surprising: `Color.parse` compensates by dividing three of four elements by 255,
467
+ which is exactly the kind of knowledge that should not live in the caller.
468
+
469
+ ### 3.3 Naming
470
+
471
+ - **`2` vs `to`.** `hsv2rgb`, `rgb2hsv`, `rgb2hex`, `hex2rgb`, `int2rgb`, `rgb2uint24`, `rgb2uint32`
472
+ (Layer B) against `rgb_to_xyz`, `xyz_to_lab`, `linear_to_PQ`, `rgb_to_ycocg`, … (Layers A/C).
473
+ Splits cleanly by age, which makes it a mechanical rename.
474
+ - **sRGB casing.** `sRGB_to_linear` / `linear_to_sRGB` / `sRGB_cmf` against `linear_srgb_to_oklab` /
475
+ `oklab_to_linear_srgb` / `linear_srgb_to_okhsv`. Same token, two casings, adjacent directories.
476
+ - **Directory casing.** `PQ`, `YCbCr`, `YCoCg`, `sRGB` uppercase; `colormap`, `hex`, `hsv`, `hunt`,
477
+ `illuminant`, `kelvin`, `lab`, `oklab`, `operations`, `tonemap`, `xyz`, `ycxcz` lowercase. Under
478
+ `YCoCg/` the functions are `rgb_to_ycocg` (lower) while under `YCbCr/` they are
479
+ `rgb_to_YCbCr_uint24` (mixed).
480
+ - **Two files named `XYZ.spec.js`** — [xyz/XYZ.spec.js](xyz/XYZ.spec.js) and
481
+ [oklab/XYZ.spec.js](oklab/XYZ.spec.js) — and *three* tests all called `"to/from consistency"`
482
+ (those two plus [sRGB/sRGB.spec.js](sRGB/sRGB.spec.js)). A failure report naming
483
+ `XYZ.spec.js > to/from consistency` is genuinely ambiguous.
484
+ - **`hunt/`** — see §1.3.
485
+
486
+ ### 3.4 The unstated linear-vs-encoded contract — the most consequential inconsistency
487
+
488
+ Nothing in the package records which transfer function a value carries. Consider what a caller
489
+ actually gets:
490
+
491
+ - `Color.parse("#808080")` divides bytes by 255 → the `Color` holds **encoded** sRGB.
492
+ - `color_lighten(that)` calls `linear_srgb_to_okhsv(hsv, input.r, input.g, input.b)` → treats it as
493
+ **linear**.
494
+ - `Color.prototype.computeLuminance()` applies Rec.709 weights → correct only for **linear**.
495
+ - `Color.prototype.lerpColors` interpolates raw channels → correct only for **linear**.
496
+ - `kelvin_to_rgb`'s docstring explicitly says its output is **encoded** and warns you to convert
497
+ before mixing — the one place the distinction is stated.
498
+
499
+ So the most common construction path yields encoded values and three of the class's own methods
500
+ assume linear. Each individual choice is defensible; the combination is a silent, systematic error
501
+ in the perceptual operations. It is not visible in tests because every `operations/` spec builds its
502
+ inputs from raw floats rather than from `Color.parse`.
503
+
504
+ **Recommendation:** this is the one issue worth a design decision rather than a patch. Either (a)
505
+ declare `Color` to be linear, make `parse`/`toHex`/`toCssRGBAString` apply the transfer function, and
506
+ fix the resulting fallout; or (b) keep `Color` encoded and have `operations/` decode on entry and
507
+ re-encode on exit. (a) is the right answer for an HDR renderer and matches what `Color`'s own
508
+ `@param {number} [r=0] Red from 0 to 1 for SDR` and the HDR-tolerant `set()` already imply; (b) is
509
+ the smaller change. Either way, say so in `Color`'s class docstring, which currently says nothing.
510
+
511
+ ### 3.5 CPU and GPU disagree about the same constants
512
+
513
+ `src/shade/renderer/shader/chunk/color/` reimplements much of this package in WGSL. Independent
514
+ implementations are fine; independently drifting constants are not.
515
+
516
+ | Quantity | CPU | GPU | Note |
517
+ |---|---|---|---|
518
+ | Rec.709 luma weights | `0.2126, 0.7152, 0.0722` ([rgb_to_luminance.js](rgb_to_luminance.js)) | `0.212639005871510, …` (`chunk_rgb_to_luminance`) | and `rgb_to_xyz` row 1 carries **20** digits of the same number. Three precisions, one quantity. |
519
+ | **PQ / ST 2084 reference white** | **203 nits** (BT.2408, [PQ/PQ_constants.js](PQ/PQ_constants.js)) | **100 nits** (`chunk_eotf_st2084`) | **Real divergence.** Same engine, two SDR-white conventions; content encoded on one path and decoded on the other is off by 2.03×. |
520
+ | sRGB OETF exponent | `0.4166666666666667` | `0.41666666666` | Harmless; still two spellings of `1/2.4`. |
521
+ | `rgb_to_xyz` matrix | 20 significant digits | 16 | Same matrix, two roundings. |
522
+
523
+ The PQ row is the one to act on. `PQ_constants.js` goes out of its way to document why folding a
524
+ white point into the transfer function is dangerous — and the GPU chunk folds a *different* white
525
+ point into it. Whichever is right, they must agree, and the constant should exist once.
526
+
527
+ ### 3.6 Documentation, asserts and tests are applied unevenly
528
+
529
+ - **Docs.** Layer A files carry multi-paragraph rationale. Layer B files carry a bare `@param`
530
+ list, and `int2rgb.js` has no prose at all. `Color.js` — the most-imported file in the package —
531
+ documents index accessors that need no explanation and says nothing about colour space, HDR range,
532
+ the `onChanged` contract, or why the numeric index setters bypass `set()`. Its class JSDoc also
533
+ claims `@extends {Array.<number>}`, which is false (`Array.isArray(color) === false`).
534
+ - **Asserts.** `planckian_radiance` and `D65_spd_tabulated` validate their scalar inputs;
535
+ `xyz_cmf_tabulated`, `xyz_cmf_wyman`, `linear_to_PQ`, `D65_spd_analytical` do not. `kelvin_to_rgb`
536
+ asserts, `rgb_to_ycocg` does not. No visible rule.
537
+ - **`@author` / `@copyright`.** Present on `compute_max_saturation`, `find_cusp`,
538
+ `find_gamut_intersection`, `okhsv_to_linear_srgb`, `oklab_to_linear_srgb`, `oklab_to_xyz`,
539
+ `xyz_to_oklab`, `color_scale_okhsv_channel`, `PQ/*`; absent on `linear_srgb_to_okhsv`,
540
+ `linear_srgb_to_oklab`, `toe` — sitting in the same directory, from the same source.
541
+ - **Tests.** 158 tests over 33 files, but the distribution is 21 for `Color` and 1 each for
542
+ `Okhsv.spec.js`, `YCbCr.spec.js`, `oklab/XYZ.spec.js`, `xyz/XYZ.spec.js`, `sRGB.spec.js`,
543
+ `compute_max_saturation.spec.js`. Six of the package's hardest files share six assertions between
544
+ them. §4.5 lists what is untested.
545
+
546
+ ---
547
+
548
+ ## 4. Prominent missing features and members
549
+
550
+ ### 4.1 Missing inverses and partners
551
+
552
+ Every one of these has its opposite direction already present.
553
+
554
+ | Present | Missing | Consequence |
555
+ |---|---|---|
556
+ | `xyz_to_lab` | **`lab_to_xyz`** | Lab is write-only. Nothing can move a colour *in* Lab and come back — the standard reason to go there. |
557
+ | `xyz_to_hpe` | `hpe_to_xyz` | Cone-space adaptation is one-way. |
558
+ | `rgb2hex` | alpha-aware `rgba2hex` | `hex2rgb` already **reads** `#RRGGBBAA`; the writer cannot produce it, and `Color.toHex()` silently drops alpha. |
559
+ | `hsv2rgb` (0-255) | `rgb2hsv_uint8` | Asymmetric pair — one direction has both unit variants, the other has one. |
560
+ | `Color.setHSL` | `Color.getHSL`, `Color.fromHSL` | `getHSV`/`fromHSV` exist; HSL has neither. |
561
+ | `Color.setHCL` / `setHSI` | `getHCL` / `getHSI` | Write-only. |
562
+ | `linear_srgb_to_okhsv` | `srgb_to_okhsv` (encoded input) | Every UI caller has encoded values (§3.4). |
563
+ | `oklab` L/a/b | **OkLCh** (`oklab_to_oklch` / back) | OkLCh is the space CSS Color 4 exposes and the natural one for hue rotation; the package has OkHSV but not OkLCh. |
564
+ | `toe` / `toe_inv` | **OkHSL** pair | Ottosson defines OkHSL alongside OkHSV; only OkHSV was ported. OkHSL is the better choice for lighten/darken (§4.3). |
565
+ | `linear_to_PQ` | HLG (`linear_to_HLG` / back) | The other half of the HDR transfer-function pair. |
566
+
567
+ ### 4.2 Missing on `Color`
568
+
569
+ `Color` is the package's public face (29 non-spec importers) and its surface has gaps a caller hits
570
+ in the first hour:
571
+
572
+ - **No `static fromUint` / `fromHex` / `setHex`** — `fromUint` and `parse` exist as instance
573
+ methods, but only `fromRGB`, `fromHSV`, `fromArray` and `parse` are static. Constructing from a
574
+ packed integer takes two lines.
575
+ - **No arithmetic** beyond `multiply` / `multiplyScalar`: no `add`, `sub`, `addScaled`, `divide`.
576
+ Blending and accumulation — the two things an engine does most with colour — are absent.
577
+ - **No instance `lerp(other, f)`**, only `lerpColors(a, b, f)` which writes into `this` from two
578
+ *other* colours. `Color.lerp(a,b,f) → Color` as a static is also missing.
579
+ - **No `Color.gray(v)`**, no `Color.fromTemperature(k)` (despite `kelvin_to_rgb` existing), no
580
+ `Color.orange`/`grey` — the constant set stops at the six primaries plus black/white/transparent.
581
+ - **No `clamp()` / `isInGamut()`**, despite three methods that can produce out-of-gamut values (C8)
582
+ and OkHSV round-trips that overshoot by ~1e-7 (C12).
583
+ - **No space tag** (§3.4) — the single most valuable missing member.
584
+ - **`onChanged` is allocated eagerly for every instance.** Measured: 500k `Color` allocations take
585
+ **71.7 ms** versus **17.9 ms** for an otherwise identical class without the `Signal` — a **4×**
586
+ construction cost paid by every caller, including the many that never subscribe. A lazily created
587
+ signal (or a `Color`/`ObservableColor` split) is the standard fix, and matters because `Color` is
588
+ used in per-particle and per-vertex paths.
589
+
590
+ ### 4.3 Missing spaces and operations
591
+
592
+ - **No gamut clipping.** `find_gamut_intersection` — the exact primitive Ottosson provides *for*
593
+ gamut clipping — is present, correct, and imported by nothing. There is no `gamut_clip_preserve_chroma`
594
+ / `_project_to_lcusp` / `_adaptive_L0` entry point. This is the largest single missing feature: the
595
+ hard part is done and the 10-line wrapper that makes it usable is absent.
596
+ - **No colour-difference metrics beyond HyAB.** No ΔE76, ΔE94, ΔE2000, or ΔE-OK. `lab_distance_hyab`
597
+ exists solely for ꟻLIP; anything wanting a perceptual "are these two colours the same" must
598
+ reimplement.
599
+ - **No chromatic adaptation.** No Bradford / CAT02 / von Kries, and no white point other than D65.
600
+ `xyz_to_lab` hardcodes `D65_TRISTIMULUS_XYZ`, so D50 workflows (ICC, print, most JPEG/TIFF
601
+ metadata) are unreachable.
602
+ - **No primaries other than sRGB/Rec.709.** No Display-P3, Rec.2020, ACEScg, or Adobe RGB matrices —
603
+ while the GPU side ships all four `CRM_FROM_*` conversion matrices. An HDR renderer that can encode
604
+ PQ (a Rec.2020-centric format) has no CPU-side way to get *into* Rec.2020.
605
+ - **No BT.601 YCbCr.** The `YCbCr/` directory is Rec.709 only, so `engine/asset/loaders/image/jpeg/JpegImage.js`
606
+ carries its own inline BT.601 matrix (`1.402`, `-0.3441363`, `-0.71413636`, `1.772`) — the correct
607
+ coefficients for JPEG, which the package cannot supply.
608
+ - **No `Color`-level gradient/palette type**, despite `color_lerp` and `MAGMA_LUT` both existing.
609
+
610
+ ### 4.4 CPU/GPU parity gaps
611
+
612
+ Every one of these exists on the GPU and not on the CPU, which matters for CPU-side baking,
613
+ thumbnailing, and test oracles:
614
+
615
+ | GPU has | CPU has |
616
+ |---|---|
617
+ | ACES, AgX, BT.2390, Khronos PBR, PBR-neutral, Reinhard-luma, GT7 | **ACES only** |
618
+ | inferno, plasma, turbo, viridis LUTs | **magma only** (and unused) |
619
+ | iCtCp (+ ST 2084 pair) | PQ scalars only, no iCtCp |
620
+ | LogLuv encode/decode | — |
621
+ | P3↔Rec2020↔Rec709 matrices | — |
622
+ | EV100 / exposure helpers | — |
623
+ | Purkinje shift | — |
624
+
625
+ ### 4.5 Test gaps
626
+
627
+ **33 of 58 source files have no spec.** The ones that matter:
628
+
629
+ - **All of `oklab/`'s primitives** — `linear_srgb_to_oklab`, `oklab_to_linear_srgb`, `find_cusp`,
630
+ `toe`, `linear_srgb_to_okhsv`, `okhsv_to_linear_srgb` are covered only by two whole-pipeline
631
+ round-trip tests with 5 sample points between them. `find_gamut_intersection` has none at all.
632
+ - **`xyz/rgb_to_xyz` and `xyz_to_rgb`** (4 non-spec importers each) are covered only by a single
633
+ mutual round-trip, which cannot catch a matrix that is wrong in both directions.
634
+ - **`rgb_to_luminance`** — 3 non-spec importers, no spec.
635
+ - **`rgb2uint24` / `rgb2uint32`** — tested only indirectly through `Color`.
636
+ - **`ycxcz`, `YCoCg`, `PQ`** are covered only by combined round-trip specs; a systematically wrong
637
+ forward matrix with a matching inverse passes every one of them. Absolute reference values are
638
+ needed, not just inverses.
639
+ - **No seam/monotonicity tests** anywhere — which is why C1 survived.
640
+ - **No alpha tests** on any `operations/` function — which is why C6 survived.
641
+ - **No allocation or performance guard** on `Color`.
642
+
643
+ ---
644
+
645
+ ## 5. Redundancies
646
+
647
+ ### 5.1 Three implementations of uint24 → RGB, with three different behaviours
648
+
649
+ | Site | Red | Green | Blue |
650
+ |---|---|---|---|
651
+ | [int2rgb.js](int2rgb.js) | `(v >> 16) & 255` | masked | masked |
652
+ | [Color.js:501](Color.js) `fromUint` | `v >> 16` **unmasked** | masked | masked |
653
+ | [parse_color.js:68](parse_color.js) | `c >> 16` **unmasked** | masked | masked |
654
+
655
+ `int2rgb` has no importers; the other two both carry C4/C10. Keep one (masked, validating), delete
656
+ `int2rgb.js`, and route the other two through it.
657
+
658
+ ### 5.2 Hue wrapping copy-pasted four times, in two orders
659
+
660
+ `Color.setHSL`, `setHCL` and `setHSI` all do `h % 1` then a negative fixup; `hsv2rgb_float` does the
661
+ negative fixup *then* `% 1`. Both orders happen to be correct, which is why the divergence has
662
+ survived. One `wrap01(h)` helper in `core/math` replaces all four.
663
+
664
+ ### 5.3 The HCL/HSI sextant switch is duplicated verbatim
665
+
666
+ `setHCL` and `setHSI` contain **byte-identical** 30-line `switch (Math.floor(h_face))` blocks; only
667
+ the `c`/`x` derivation above and the `m` offset below differ. `hsv2rgb_float` contains a third
668
+ variant of the same sextant dispatch. One `hue_to_rgb_sextant(out, h_face, c, x)` collapses all
669
+ three — and would have made C8's missing clamp a one-place fix.
670
+
671
+ ### 5.4 Two lerps with different semantics and no signpost
672
+
673
+ `Color.prototype.lerpColors` interpolates raw channels linearly;
674
+ [operations/color_lerp.js](operations/color_lerp.js) converts to OkHSV and interpolates hue along the
675
+ shortest arc. Neither docstring mentions the other. A caller reaching for "blend two colours" will
676
+ find whichever they grep first, and the results differ substantially at mid-`t`.
677
+
678
+ ### 5.5 Two D65 SPDs and two CMF sets, one of each dead
679
+
680
+ - `D65_spd_tabulated` (used by the Mie scattering code) vs `D65_spd_analytical` (**0 importers**),
681
+ which is the sole importer of `planckian_radiance` → a dead two-file subtree.
682
+ - `xyz_cmf_tabulated` (used) vs `xyz_cmf_wyman` (reachable only via `sRGB_cmf`, itself unused) →
683
+ a second dead two-file subtree.
684
+
685
+ Keeping an analytic alternative alongside a tabulated one is defensible — the MBOIT chunk library
686
+ sets that precedent. If that is the intent here, say so in the file header the way the MBOIT chunks
687
+ do; today nothing marks them as deliberately-retained.
688
+
689
+ ### 5.6 `sRGB_cmf` documents itself as the wrong choice
690
+
691
+ Its own docstring reads *"Prefer to integrate in XYZ space and then perform the conversion to sRGB"* —
692
+ i.e. do not call this. It has no importers. It is a two-line composition of `xyz_cmf_wyman` and
693
+ `xyz_to_rgb` that a caller can write inline. Deleting it also frees `xyz_cmf_wyman` for deletion or
694
+ promotion.
695
+
696
+ ### 5.7 CPU/GPU duplication with drifting constants
697
+
698
+ `rgb_to_xyz`, `xyz_to_rgb`, the OkLab pair, the sRGB OETF, ACES, rgbe9995, YCoCg and ST 2084 all
699
+ exist twice (§3.5). The duplication is unavoidable — WGSL cannot import JS — but the *constants*
700
+ need not be: a single generated source of truth for matrices and transfer-function coefficients
701
+ would remove the 203-vs-100-nit divergence structurally rather than by vigilance. The AVIF work
702
+ already generates normative constant tables into source (`feat(avif): generate the normative AV1
703
+ constant tables`), so the mechanism exists in-repo.
704
+
705
+ ### 5.8 `prototype_kelvin_to_rgb.js`
706
+
707
+ A dev visualisation script inside a core library directory. It executes at import time
708
+ (`document.body.append`), imports from `engine/` and `view/` — inverting the `core/` layering — and
709
+ would crash any non-DOM consumer that touched it. Delete it, or move it to a playground directory
710
+ where side-effecting entry points are expected.
711
+
712
+ ### 5.9 Test-name collisions
713
+
714
+ `xyz/XYZ.spec.js` and `oklab/XYZ.spec.js` — same filename, same suite name, plus `sRGB/sRGB.spec.js`
715
+ sharing the test name `"to/from consistency"`. Rename to `rgb_xyz_roundtrip.spec.js`,
716
+ `oklab_xyz_roundtrip.spec.js`, `srgb_transfer_roundtrip.spec.js`.
717
+
718
+ ---
719
+
720
+ ## 6. Suggested order of work
721
+
722
+ Sequenced by risk-reduction per unit of effort; each step is independently shippable.
723
+
724
+ 1. **Alpha** — C5, C6. Two small edits (`result.a = input.a`, route through `set()`), removes a
725
+ whole class of silent data loss, and fixes the `onChanged` blind spot at the same time.
726
+ 2. **Ingest hardening** — C3, C4, C11. Make `parse_color`/`hex2rgb` reject what they cannot parse
727
+ instead of emitting `NaN` and negative channels. Add short-hex support while in there.
728
+ 3. **Kelvin seam** — C1, then C2 falls out. Add a monotonicity/continuity spec that sweeps
729
+ temperature; it is the test shape that would have caught this and is reusable for the 1000 K and
730
+ 6600 K seams.
731
+ 4. **Decide the colour-space contract** (§3.4) and write it into `Color`'s class docstring. This is
732
+ a decision, not a patch, and everything in `operations/` depends on it.
733
+ 5. **Delete the dead perimeter** — §1.4's 8 unreferenced files (`prototype_kelvin_to_rgb.js` first),
734
+ §5.5's two dead subtrees, §5.6, §5.1's `int2rgb.js`. Roughly 400 lines, zero behavioural risk.
735
+ For anything deliberately retained, add the "kept on purpose, here's why" header.
736
+ 6. **Reconcile the PQ white point** with the GPU (§3.5) — a correctness issue that only shows up
737
+ across the CPU/GPU boundary and will be expensive to diagnose later.
738
+ 7. **Gamut clipping** (§4.3) — the highest-value missing feature, and most of it is already written.
739
+ 8. **Convention convergence** (§3.1, §3.2, §3.3) — mechanical, large, best done last and in one
740
+ sweep so the churn lands once. `rgb_to_xyz`/`xyz_to_rgb` gaining offsets is the highest-value
741
+ single change.
742
+
743
+ ---
744
+
745
+ ## Appendix A — reproduction
746
+
747
+ Every *(reproduced)* claim above came from running the modules directly. To re-check, save as
748
+ `probe.mjs` in the repo root and run `node probe.mjs`:
749
+
750
+ ```js
751
+ import { Color } from './src/core/color/Color.js';
752
+ import { kelvin_to_rgb } from './src/core/color/kelvin/kelvin_to_rgb.js';
753
+ import { rgb_to_kelvin } from './src/core/color/kelvin/rgb_to_kelvin.js';
754
+ import { parse_color } from './src/core/color/parse_color.js';
755
+ import { hex2rgb } from './src/core/color/hex/hex2rgb.js';
756
+ import { color_lighten } from './src/core/color/operations/color_lighten.js';
757
+
758
+ const o = [0, 0, 0];
759
+ for (const T of [1999.99, 2000, 2000.01]) { // C1
760
+ kelvin_to_rgb(o, 0, T); console.log(T, o.map(x => x.toFixed(6)).join(', '));
761
+ }
762
+ for (const T of [800, 1500, 1999, 2100]) { // C2
763
+ kelvin_to_rgb(o, 0, T); console.log(T, '->', rgb_to_kelvin(o, 0));
764
+ }
765
+ console.log(hex2rgb('#f00')); // C3 b: NaN
766
+ console.log(parse_color(0xFF8000FF)); // C4 r: -128
767
+ console.log(Color.from_linear_to_sRGB(new Color(0.5, 0.5, 0.5, 0.25)).toJSON()); // C5 a: 1
768
+ const out = new Color(); let fired = 0; out.onChanged.add(() => fired++);
769
+ Color.from_sRGB_to_linear(new Color(0.5, 0.5, 0.5), out);
770
+ console.log('onChanged fired:', fired); // C5 0
771
+ console.log(color_lighten(new Color(0.2, 0.4, 0.6, 0.3), 0.5).toJSON()); // C6 a: 1
772
+ console.log(new Color(1, 1, 1, 1).toUint32()); // C7 -1
773
+ const c = new Color(); c.setHCL(0, 1, 0.5); console.log(c.toJSON()); // C8 r: 1.2
774
+ console.log(parse_color('background: rgb(1,2,3) !important')); // C11
775
+ console.log(color_lighten(new Color(0, 0, 0), 1).toJSON()); // C13 unchanged
776
+ ```
777
+
778
+ Baseline for comparison: `npx vitest run src/core/color` → **33 files, 158 tests, all passing** as of
779
+ this review. None of the defects above is caught by the existing suite.