@react-x11/components 0.8.0 → 0.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 (721) hide show
  1. package/README.md +105 -38
  2. package/dist/charts/node.d.ts +12 -2
  3. package/dist/charts/node.d.ts.map +1 -1
  4. package/dist/charts/node.js +50 -16
  5. package/dist/charts/node.js.map +1 -1
  6. package/dist/charts/render.d.ts +12 -3
  7. package/dist/charts/render.d.ts.map +1 -1
  8. package/dist/charts/render.js +94 -25
  9. package/dist/charts/render.js.map +1 -1
  10. package/dist/code/index.d.ts.map +1 -1
  11. package/dist/code/index.js +3 -1
  12. package/dist/code/index.js.map +1 -1
  13. package/dist/code-editor/completion.d.ts.map +1 -1
  14. package/dist/code-editor/completion.js +3 -2
  15. package/dist/code-editor/completion.js.map +1 -1
  16. package/dist/code-editor/index.d.ts.map +1 -1
  17. package/dist/code-editor/index.js +19 -0
  18. package/dist/code-editor/index.js.map +1 -1
  19. package/dist/code-editor/node.d.ts +177 -7
  20. package/dist/code-editor/node.d.ts.map +1 -1
  21. package/dist/code-editor/node.js +1329 -164
  22. package/dist/code-editor/node.js.map +1 -1
  23. package/dist/code-language/languages/glsl.d.ts.map +1 -1
  24. package/dist/code-language/languages/glsl.js +10 -2
  25. package/dist/code-language/languages/glsl.js.map +1 -1
  26. package/dist/code-language/languages/javascript.d.ts.map +1 -1
  27. package/dist/code-language/languages/javascript.js +18 -3
  28. package/dist/code-language/languages/javascript.js.map +1 -1
  29. package/dist/code-language/languages/json.d.ts.map +1 -1
  30. package/dist/code-language/languages/json.js +10 -2
  31. package/dist/code-language/languages/json.js.map +1 -1
  32. package/dist/code-language/languages/shell.d.ts.map +1 -1
  33. package/dist/code-language/languages/shell.js +10 -2
  34. package/dist/code-language/languages/shell.js.map +1 -1
  35. package/dist/code-language/languages/sql.d.ts.map +1 -1
  36. package/dist/code-language/languages/sql.js +20 -2
  37. package/dist/code-language/languages/sql.js.map +1 -1
  38. package/dist/code-language/stream.d.ts +22 -0
  39. package/dist/code-language/stream.d.ts.map +1 -1
  40. package/dist/code-language/stream.js +259 -9
  41. package/dist/code-language/stream.js.map +1 -1
  42. package/dist/code-language/types.d.ts +5 -4
  43. package/dist/code-language/types.d.ts.map +1 -1
  44. package/dist/codeblock/index.d.ts +13 -2
  45. package/dist/codeblock/index.d.ts.map +1 -1
  46. package/dist/codeblock/index.js +20 -3
  47. package/dist/codeblock/index.js.map +1 -1
  48. package/dist/embed/client.d.ts +48 -3
  49. package/dist/embed/client.d.ts.map +1 -1
  50. package/dist/embed/client.js +79 -5
  51. package/dist/embed/client.js.map +1 -1
  52. package/dist/embed/index.d.ts +1 -1
  53. package/dist/embed/index.d.ts.map +1 -1
  54. package/dist/embed/index.js +1 -1
  55. package/dist/embed/index.js.map +1 -1
  56. package/dist/flow/draw.d.ts +50 -0
  57. package/dist/flow/draw.d.ts.map +1 -1
  58. package/dist/flow/draw.js +132 -33
  59. package/dist/flow/draw.js.map +1 -1
  60. package/dist/flow/gl/color.d.ts +13 -0
  61. package/dist/flow/gl/color.d.ts.map +1 -0
  62. package/dist/flow/gl/color.js +82 -0
  63. package/dist/flow/gl/color.js.map +1 -0
  64. package/dist/flow/gl/index.d.ts +60 -0
  65. package/dist/flow/gl/index.d.ts.map +1 -0
  66. package/dist/flow/gl/index.js +283 -0
  67. package/dist/flow/gl/index.js.map +1 -0
  68. package/dist/flow/gl/pack.d.ts +133 -0
  69. package/dist/flow/gl/pack.d.ts.map +1 -0
  70. package/dist/flow/gl/pack.js +408 -0
  71. package/dist/flow/gl/pack.js.map +1 -0
  72. package/dist/flow/gl/renderer.d.ts +95 -0
  73. package/dist/flow/gl/renderer.d.ts.map +1 -0
  74. package/dist/flow/gl/renderer.js +459 -0
  75. package/dist/flow/gl/renderer.js.map +1 -0
  76. package/dist/flow/gl/shaders.d.ts +79 -0
  77. package/dist/flow/gl/shaders.d.ts.map +1 -0
  78. package/dist/flow/gl/shaders.js +346 -0
  79. package/dist/flow/gl/shaders.js.map +1 -0
  80. package/dist/flow/gl/text.d.ts +318 -0
  81. package/dist/flow/gl/text.d.ts.map +1 -0
  82. package/dist/flow/gl/text.js +1103 -0
  83. package/dist/flow/gl/text.js.map +1 -0
  84. package/dist/flow/index.d.ts +1 -1
  85. package/dist/flow/index.d.ts.map +1 -1
  86. package/dist/flow/index.js +619 -64
  87. package/dist/flow/index.js.map +1 -1
  88. package/dist/flow/node.d.ts +621 -72
  89. package/dist/flow/node.d.ts.map +1 -1
  90. package/dist/flow/node.js +2253 -1018
  91. package/dist/flow/node.js.map +1 -1
  92. package/dist/flow/paint.d.ts +35 -0
  93. package/dist/flow/paint.d.ts.map +1 -0
  94. package/dist/flow/paint.js +393 -0
  95. package/dist/flow/paint.js.map +1 -0
  96. package/dist/flow/paths.d.ts +14 -0
  97. package/dist/flow/paths.d.ts.map +1 -1
  98. package/dist/flow/paths.js +45 -19
  99. package/dist/flow/paths.js.map +1 -1
  100. package/dist/flow/scene.d.ts +584 -0
  101. package/dist/flow/scene.d.ts.map +1 -0
  102. package/dist/flow/scene.js +1453 -0
  103. package/dist/flow/scene.js.map +1 -0
  104. package/dist/flow/types.d.ts +111 -1
  105. package/dist/flow/types.d.ts.map +1 -1
  106. package/dist/html/controls.d.ts +28 -0
  107. package/dist/html/controls.d.ts.map +1 -1
  108. package/dist/html/controls.js +52 -2
  109. package/dist/html/controls.js.map +1 -1
  110. package/dist/html/css/calc.d.ts +14 -0
  111. package/dist/html/css/calc.d.ts.map +1 -0
  112. package/dist/html/css/calc.js +344 -0
  113. package/dist/html/css/calc.js.map +1 -0
  114. package/dist/html/css/cascade.d.ts +240 -6
  115. package/dist/html/css/cascade.d.ts.map +1 -1
  116. package/dist/html/css/cascade.js +1327 -66
  117. package/dist/html/css/cascade.js.map +1 -1
  118. package/dist/html/css/color.d.ts +23 -0
  119. package/dist/html/css/color.d.ts.map +1 -0
  120. package/dist/html/css/color.js +740 -0
  121. package/dist/html/css/color.js.map +1 -0
  122. package/dist/html/css/content.d.ts +92 -0
  123. package/dist/html/css/content.d.ts.map +1 -0
  124. package/dist/html/css/content.js +388 -0
  125. package/dist/html/css/content.js.map +1 -0
  126. package/dist/html/css/counter-styles.d.ts +76 -0
  127. package/dist/html/css/counter-styles.d.ts.map +1 -0
  128. package/dist/html/css/counter-styles.js +893 -0
  129. package/dist/html/css/counter-styles.js.map +1 -0
  130. package/dist/html/css/decode.d.ts +12 -0
  131. package/dist/html/css/decode.d.ts.map +1 -0
  132. package/dist/html/css/decode.js +116 -0
  133. package/dist/html/css/decode.js.map +1 -0
  134. package/dist/html/css/parse.d.ts +110 -10
  135. package/dist/html/css/parse.d.ts.map +1 -1
  136. package/dist/html/css/parse.js +1473 -180
  137. package/dist/html/css/parse.js.map +1 -1
  138. package/dist/html/css/style.d.ts +498 -15
  139. package/dist/html/css/style.d.ts.map +1 -1
  140. package/dist/html/css/style.js +3698 -206
  141. package/dist/html/css/style.js.map +1 -1
  142. package/dist/html/css/ua.d.ts.map +1 -1
  143. package/dist/html/css/ua.js +57 -11
  144. package/dist/html/css/ua.js.map +1 -1
  145. package/dist/html/css/values.d.ts +37 -3
  146. package/dist/html/css/values.d.ts.map +1 -1
  147. package/dist/html/css/values.js +122 -48
  148. package/dist/html/css/values.js.map +1 -1
  149. package/dist/html/css/vars.d.ts +52 -0
  150. package/dist/html/css/vars.d.ts.map +1 -0
  151. package/dist/html/css/vars.js +358 -0
  152. package/dist/html/css/vars.js.map +1 -0
  153. package/dist/html/dom.d.ts +30 -4
  154. package/dist/html/dom.d.ts.map +1 -1
  155. package/dist/html/dom.js +278 -17
  156. package/dist/html/dom.js.map +1 -1
  157. package/dist/html/fonts.d.ts +106 -0
  158. package/dist/html/fonts.d.ts.map +1 -0
  159. package/dist/html/fonts.js +565 -0
  160. package/dist/html/fonts.js.map +1 -0
  161. package/dist/html/gif.d.ts +15 -0
  162. package/dist/html/gif.d.ts.map +1 -0
  163. package/dist/html/gif.js +229 -0
  164. package/dist/html/gif.js.map +1 -0
  165. package/dist/html/index.d.ts +50 -11
  166. package/dist/html/index.d.ts.map +1 -1
  167. package/dist/html/index.js +115 -9
  168. package/dist/html/index.js.map +1 -1
  169. package/dist/html/layout/block.d.ts +226 -3
  170. package/dist/html/layout/block.d.ts.map +1 -1
  171. package/dist/html/layout/block.js +2937 -197
  172. package/dist/html/layout/block.js.map +1 -1
  173. package/dist/html/layout/boxes.d.ts +340 -17
  174. package/dist/html/layout/boxes.d.ts.map +1 -1
  175. package/dist/html/layout/boxes.js +1714 -182
  176. package/dist/html/layout/boxes.js.map +1 -1
  177. package/dist/html/layout/cache.d.ts +58 -0
  178. package/dist/html/layout/cache.d.ts.map +1 -0
  179. package/dist/html/layout/cache.js +224 -0
  180. package/dist/html/layout/cache.js.map +1 -0
  181. package/dist/html/layout/collapse.d.ts +45 -0
  182. package/dist/html/layout/collapse.d.ts.map +1 -0
  183. package/dist/html/layout/collapse.js +246 -0
  184. package/dist/html/layout/collapse.js.map +1 -0
  185. package/dist/html/layout/css-grid.d.ts +7 -0
  186. package/dist/html/layout/css-grid.d.ts.map +1 -0
  187. package/dist/html/layout/css-grid.js +900 -0
  188. package/dist/html/layout/css-grid.js.map +1 -0
  189. package/dist/html/layout/flex.d.ts.map +1 -1
  190. package/dist/html/layout/flex.js +1025 -89
  191. package/dist/html/layout/flex.js.map +1 -1
  192. package/dist/html/layout/floats.d.ts +45 -6
  193. package/dist/html/layout/floats.d.ts.map +1 -1
  194. package/dist/html/layout/floats.js +113 -14
  195. package/dist/html/layout/floats.js.map +1 -1
  196. package/dist/html/layout/grid-lines.d.ts +39 -0
  197. package/dist/html/layout/grid-lines.d.ts.map +1 -0
  198. package/dist/html/layout/grid-lines.js +119 -0
  199. package/dist/html/layout/grid-lines.js.map +1 -0
  200. package/dist/html/layout/grid.d.ts +53 -0
  201. package/dist/html/layout/grid.d.ts.map +1 -0
  202. package/dist/html/layout/grid.js +153 -0
  203. package/dist/html/layout/grid.js.map +1 -0
  204. package/dist/html/layout/inline.d.ts +151 -1
  205. package/dist/html/layout/inline.d.ts.map +1 -1
  206. package/dist/html/layout/inline.js +3375 -174
  207. package/dist/html/layout/inline.js.map +1 -1
  208. package/dist/html/layout/shaping.d.ts +6 -0
  209. package/dist/html/layout/shaping.d.ts.map +1 -0
  210. package/dist/html/layout/shaping.js +100 -0
  211. package/dist/html/layout/shaping.js.map +1 -0
  212. package/dist/html/layout/table.d.ts +3 -0
  213. package/dist/html/layout/table.d.ts.map +1 -1
  214. package/dist/html/layout/table.js +681 -175
  215. package/dist/html/layout/table.js.map +1 -1
  216. package/dist/html/layout/tracks.d.ts +44 -0
  217. package/dist/html/layout/tracks.d.ts.map +1 -0
  218. package/dist/html/layout/tracks.js +349 -0
  219. package/dist/html/layout/tracks.js.map +1 -0
  220. package/dist/html/node.d.ts +172 -5
  221. package/dist/html/node.d.ts.map +1 -1
  222. package/dist/html/node.js +1336 -103
  223. package/dist/html/node.js.map +1 -1
  224. package/dist/html/paint.d.ts +92 -3
  225. package/dist/html/paint.d.ts.map +1 -1
  226. package/dist/html/paint.js +3503 -141
  227. package/dist/html/paint.js.map +1 -1
  228. package/dist/html/resources.d.ts +60 -14
  229. package/dist/html/resources.d.ts.map +1 -1
  230. package/dist/html/resources.js +122 -36
  231. package/dist/html/resources.js.map +1 -1
  232. package/dist/html/surfaces.d.ts +23 -0
  233. package/dist/html/surfaces.d.ts.map +1 -0
  234. package/dist/html/surfaces.js +68 -0
  235. package/dist/html/surfaces.js.map +1 -0
  236. package/dist/html/svg.d.ts +80 -0
  237. package/dist/html/svg.d.ts.map +1 -0
  238. package/dist/html/svg.js +435 -0
  239. package/dist/html/svg.js.map +1 -0
  240. package/dist/html/url.d.ts +23 -0
  241. package/dist/html/url.d.ts.map +1 -0
  242. package/dist/html/url.js +80 -0
  243. package/dist/html/url.js.map +1 -0
  244. package/dist/index.d.ts +2 -2
  245. package/dist/index.d.ts.map +1 -1
  246. package/dist/index.js +1 -1
  247. package/dist/index.js.map +1 -1
  248. package/dist/internal/collate.d.ts +3 -0
  249. package/dist/internal/collate.d.ts.map +1 -0
  250. package/dist/internal/collate.js +11 -0
  251. package/dist/internal/collate.js.map +1 -0
  252. package/dist/{markdown → internal/markdown}/ast.d.ts +23 -0
  253. package/dist/internal/markdown/ast.d.ts.map +1 -0
  254. package/dist/internal/markdown/ast.js +22 -0
  255. package/dist/internal/markdown/ast.js.map +1 -0
  256. package/dist/internal/markdown/parse.d.ts.map +1 -0
  257. package/dist/{markdown → internal/markdown}/parse.js +221 -30
  258. package/dist/internal/markdown/parse.js.map +1 -0
  259. package/dist/internal/markdown/stringify.d.ts +4 -0
  260. package/dist/internal/markdown/stringify.d.ts.map +1 -0
  261. package/dist/internal/markdown/stringify.js +684 -0
  262. package/dist/internal/markdown/stringify.js.map +1 -0
  263. package/dist/internal/markdown/tags.d.ts.map +1 -0
  264. package/dist/internal/markdown/tags.js.map +1 -0
  265. package/dist/internal/prewarm.d.ts +18 -0
  266. package/dist/internal/prewarm.d.ts.map +1 -0
  267. package/dist/internal/prewarm.js +42 -0
  268. package/dist/internal/prewarm.js.map +1 -0
  269. package/dist/internal/scroll.d.ts +3 -2
  270. package/dist/internal/scroll.d.ts.map +1 -1
  271. package/dist/internal/scroll.js +1 -0
  272. package/dist/internal/scroll.js.map +1 -1
  273. package/dist/internal/sdf.d.ts +20 -0
  274. package/dist/internal/sdf.d.ts.map +1 -0
  275. package/dist/internal/sdf.js +216 -0
  276. package/dist/internal/sdf.js.map +1 -0
  277. package/dist/internal/splice.d.ts +12 -0
  278. package/dist/internal/splice.d.ts.map +1 -0
  279. package/dist/internal/splice.js +23 -0
  280. package/dist/internal/splice.js.map +1 -0
  281. package/dist/internal/window.d.ts +12 -0
  282. package/dist/internal/window.d.ts.map +1 -1
  283. package/dist/internal/window.js +23 -6
  284. package/dist/internal/window.js.map +1 -1
  285. package/dist/maps/anchors.d.ts +103 -0
  286. package/dist/maps/anchors.d.ts.map +1 -0
  287. package/dist/maps/anchors.js +790 -0
  288. package/dist/maps/anchors.js.map +1 -0
  289. package/dist/maps/controller.d.ts +340 -0
  290. package/dist/maps/controller.d.ts.map +1 -0
  291. package/dist/maps/controller.js +822 -0
  292. package/dist/maps/controller.js.map +1 -0
  293. package/dist/maps/gl/buckets.d.ts +99 -0
  294. package/dist/maps/gl/buckets.d.ts.map +1 -0
  295. package/dist/maps/gl/buckets.js +365 -0
  296. package/dist/maps/gl/buckets.js.map +1 -0
  297. package/dist/maps/gl/build-worker.d.ts +15 -0
  298. package/dist/maps/gl/build-worker.d.ts.map +1 -0
  299. package/dist/maps/gl/build-worker.js +44 -0
  300. package/dist/maps/gl/build-worker.js.map +1 -0
  301. package/dist/maps/gl/color.d.ts +8 -0
  302. package/dist/maps/gl/color.d.ts.map +1 -0
  303. package/dist/maps/gl/color.js +57 -0
  304. package/dist/maps/gl/color.js.map +1 -0
  305. package/dist/maps/gl/cover.d.ts +70 -0
  306. package/dist/maps/gl/cover.d.ts.map +1 -0
  307. package/dist/maps/gl/cover.js +137 -0
  308. package/dist/maps/gl/cover.js.map +1 -0
  309. package/dist/maps/gl/index.d.ts +3 -0
  310. package/dist/maps/gl/index.d.ts.map +1 -0
  311. package/dist/maps/gl/index.js +20 -0
  312. package/dist/maps/gl/index.js.map +1 -0
  313. package/dist/maps/gl/markers.d.ts +28 -0
  314. package/dist/maps/gl/markers.d.ts.map +1 -0
  315. package/dist/maps/gl/markers.js +60 -0
  316. package/dist/maps/gl/markers.js.map +1 -0
  317. package/dist/maps/gl/overlays.d.ts +68 -0
  318. package/dist/maps/gl/overlays.d.ts.map +1 -0
  319. package/dist/maps/gl/overlays.js +300 -0
  320. package/dist/maps/gl/overlays.js.map +1 -0
  321. package/dist/maps/gl/pane.d.ts +12 -0
  322. package/dist/maps/gl/pane.d.ts.map +1 -0
  323. package/dist/maps/gl/pane.js +37 -0
  324. package/dist/maps/gl/pane.js.map +1 -0
  325. package/dist/maps/gl/placement.d.ts +124 -0
  326. package/dist/maps/gl/placement.d.ts.map +1 -0
  327. package/dist/maps/gl/placement.js +642 -0
  328. package/dist/maps/gl/placement.js.map +1 -0
  329. package/dist/maps/gl/renderer.d.ts +384 -0
  330. package/dist/maps/gl/renderer.d.ts.map +1 -0
  331. package/dist/maps/gl/renderer.js +1184 -0
  332. package/dist/maps/gl/renderer.js.map +1 -0
  333. package/dist/maps/gl/shaders.d.ts +121 -0
  334. package/dist/maps/gl/shaders.d.ts.map +1 -0
  335. package/dist/maps/gl/shaders.js +453 -0
  336. package/dist/maps/gl/shaders.js.map +1 -0
  337. package/dist/maps/gl/store.d.ts +125 -0
  338. package/dist/maps/gl/store.d.ts.map +1 -0
  339. package/dist/maps/gl/store.js +512 -0
  340. package/dist/maps/gl/store.js.map +1 -0
  341. package/dist/maps/gl/text.d.ts +283 -0
  342. package/dist/maps/gl/text.d.ts.map +1 -0
  343. package/dist/maps/gl/text.js +698 -0
  344. package/dist/maps/gl/text.js.map +1 -0
  345. package/dist/maps/gl/view.d.ts +345 -0
  346. package/dist/maps/gl/view.d.ts.map +1 -0
  347. package/dist/maps/gl/view.js +1209 -0
  348. package/dist/maps/gl/view.js.map +1 -0
  349. package/dist/maps/icons.d.ts +32 -0
  350. package/dist/maps/icons.d.ts.map +1 -0
  351. package/dist/maps/icons.js +151 -0
  352. package/dist/maps/icons.js.map +1 -0
  353. package/dist/maps/index.d.ts +3 -1
  354. package/dist/maps/index.d.ts.map +1 -1
  355. package/dist/maps/index.js +218 -52
  356. package/dist/maps/index.js.map +1 -1
  357. package/dist/maps/labels.d.ts +91 -18
  358. package/dist/maps/labels.d.ts.map +1 -1
  359. package/dist/maps/labels.js +376 -140
  360. package/dist/maps/labels.js.map +1 -1
  361. package/dist/maps/node.d.ts +179 -56
  362. package/dist/maps/node.d.ts.map +1 -1
  363. package/dist/maps/node.js +663 -537
  364. package/dist/maps/node.js.map +1 -1
  365. package/dist/maps/overlay.d.ts +72 -9
  366. package/dist/maps/overlay.d.ts.map +1 -1
  367. package/dist/maps/overlay.js +108 -28
  368. package/dist/maps/overlay.js.map +1 -1
  369. package/dist/maps/paint.d.ts +3 -0
  370. package/dist/maps/paint.d.ts.map +1 -1
  371. package/dist/maps/paint.js.map +1 -1
  372. package/dist/maps/proj.d.ts +62 -0
  373. package/dist/maps/proj.d.ts.map +1 -1
  374. package/dist/maps/proj.js +101 -0
  375. package/dist/maps/proj.js.map +1 -1
  376. package/dist/maps/renderer.d.ts +69 -0
  377. package/dist/maps/renderer.d.ts.map +1 -0
  378. package/dist/maps/renderer.js +66 -0
  379. package/dist/maps/renderer.js.map +1 -0
  380. package/dist/maps/sources.d.ts +30 -6
  381. package/dist/maps/sources.d.ts.map +1 -1
  382. package/dist/maps/sources.js +22 -3
  383. package/dist/maps/sources.js.map +1 -1
  384. package/dist/maps/style.d.ts +35 -0
  385. package/dist/maps/style.d.ts.map +1 -1
  386. package/dist/maps/style.js.map +1 -1
  387. package/dist/maps/styles.d.ts +12 -0
  388. package/dist/maps/styles.d.ts.map +1 -1
  389. package/dist/maps/styles.js +120 -6
  390. package/dist/maps/styles.js.map +1 -1
  391. package/dist/maps/theme.d.ts +19 -0
  392. package/dist/maps/theme.d.ts.map +1 -0
  393. package/dist/maps/theme.js +44 -0
  394. package/dist/maps/theme.js.map +1 -0
  395. package/dist/maps/tiles.d.ts +147 -27
  396. package/dist/maps/tiles.d.ts.map +1 -1
  397. package/dist/maps/tiles.js +221 -54
  398. package/dist/maps/tiles.js.map +1 -1
  399. package/dist/maps/types.d.ts +213 -21
  400. package/dist/maps/types.d.ts.map +1 -1
  401. package/dist/markdown/expressions.d.ts +1 -1
  402. package/dist/markdown/expressions.d.ts.map +1 -1
  403. package/dist/markdown/expressions.js.map +1 -1
  404. package/dist/markdown/index.d.ts +7 -6
  405. package/dist/markdown/index.d.ts.map +1 -1
  406. package/dist/markdown/index.js +121 -50
  407. package/dist/markdown/index.js.map +1 -1
  408. package/dist/markdown/spans.d.ts +1 -1
  409. package/dist/markdown/spans.d.ts.map +1 -1
  410. package/dist/markdown/spans.js +6 -0
  411. package/dist/markdown/spans.js.map +1 -1
  412. package/dist/media-player/index.d.ts +11 -3
  413. package/dist/media-player/index.d.ts.map +1 -1
  414. package/dist/media-player/index.js +13 -2
  415. package/dist/media-player/index.js.map +1 -1
  416. package/dist/qml/containers.d.ts.map +1 -1
  417. package/dist/qml/containers.js +6 -1
  418. package/dist/qml/containers.js.map +1 -1
  419. package/dist/qml/interaction.d.ts.map +1 -1
  420. package/dist/qml/interaction.js +38 -5
  421. package/dist/qml/interaction.js.map +1 -1
  422. package/dist/qml/layouts.d.ts.map +1 -1
  423. package/dist/qml/layouts.js +21 -6
  424. package/dist/qml/layouts.js.map +1 -1
  425. package/dist/qml/qtquick.d.ts.map +1 -1
  426. package/dist/qml/qtquick.js +9 -2
  427. package/dist/qml/qtquick.js.map +1 -1
  428. package/dist/qml/react.d.ts.map +1 -1
  429. package/dist/qml/react.js +9 -7
  430. package/dist/qml/react.js.map +1 -1
  431. package/dist/reorder/clock.d.ts +7 -0
  432. package/dist/reorder/clock.d.ts.map +1 -0
  433. package/dist/reorder/clock.js +26 -0
  434. package/dist/reorder/clock.js.map +1 -0
  435. package/dist/reorder/index.d.ts +25 -3
  436. package/dist/reorder/index.d.ts.map +1 -1
  437. package/dist/reorder/index.js +64 -19
  438. package/dist/reorder/index.js.map +1 -1
  439. package/dist/rich-text-editor/clipboard.d.ts +23 -0
  440. package/dist/rich-text-editor/clipboard.d.ts.map +1 -0
  441. package/dist/rich-text-editor/clipboard.js +307 -0
  442. package/dist/rich-text-editor/clipboard.js.map +1 -0
  443. package/dist/rich-text-editor/collab.d.ts +32 -0
  444. package/dist/rich-text-editor/collab.d.ts.map +1 -0
  445. package/dist/rich-text-editor/collab.js +55 -0
  446. package/dist/rich-text-editor/collab.js.map +1 -0
  447. package/dist/rich-text-editor/commands.d.ts +50 -0
  448. package/dist/rich-text-editor/commands.d.ts.map +1 -0
  449. package/dist/rich-text-editor/commands.js +376 -0
  450. package/dist/rich-text-editor/commands.js.map +1 -0
  451. package/dist/rich-text-editor/drag.d.ts +68 -0
  452. package/dist/rich-text-editor/drag.d.ts.map +1 -0
  453. package/dist/rich-text-editor/drag.js +148 -0
  454. package/dist/rich-text-editor/drag.js.map +1 -0
  455. package/dist/rich-text-editor/html.d.ts +145 -0
  456. package/dist/rich-text-editor/html.d.ts.map +1 -0
  457. package/dist/rich-text-editor/html.js +516 -0
  458. package/dist/rich-text-editor/html.js.map +1 -0
  459. package/dist/rich-text-editor/index.d.ts +252 -0
  460. package/dist/rich-text-editor/index.d.ts.map +1 -0
  461. package/dist/rich-text-editor/index.js +856 -0
  462. package/dist/rich-text-editor/index.js.map +1 -0
  463. package/dist/rich-text-editor/inline.d.ts +94 -0
  464. package/dist/rich-text-editor/inline.d.ts.map +1 -0
  465. package/dist/rich-text-editor/inline.js +252 -0
  466. package/dist/rich-text-editor/inline.js.map +1 -0
  467. package/dist/rich-text-editor/keymap.d.ts +71 -0
  468. package/dist/rich-text-editor/keymap.d.ts.map +1 -0
  469. package/dist/rich-text-editor/keymap.js +394 -0
  470. package/dist/rich-text-editor/keymap.js.map +1 -0
  471. package/dist/rich-text-editor/keys.d.ts +40 -0
  472. package/dist/rich-text-editor/keys.d.ts.map +1 -0
  473. package/dist/rich-text-editor/keys.js +202 -0
  474. package/dist/rich-text-editor/keys.js.map +1 -0
  475. package/dist/rich-text-editor/look.d.ts +71 -0
  476. package/dist/rich-text-editor/look.d.ts.map +1 -0
  477. package/dist/rich-text-editor/look.js +342 -0
  478. package/dist/rich-text-editor/look.js.map +1 -0
  479. package/dist/rich-text-editor/markdown.d.ts +18 -0
  480. package/dist/rich-text-editor/markdown.d.ts.map +1 -0
  481. package/dist/rich-text-editor/markdown.js +496 -0
  482. package/dist/rich-text-editor/markdown.js.map +1 -0
  483. package/dist/rich-text-editor/marks.d.ts +9 -0
  484. package/dist/rich-text-editor/marks.d.ts.map +1 -0
  485. package/dist/rich-text-editor/marks.js +83 -0
  486. package/dist/rich-text-editor/marks.js.map +1 -0
  487. package/dist/rich-text-editor/nodes.d.ts +135 -0
  488. package/dist/rich-text-editor/nodes.d.ts.map +1 -0
  489. package/dist/rich-text-editor/nodes.js +397 -0
  490. package/dist/rich-text-editor/nodes.js.map +1 -0
  491. package/dist/rich-text-editor/render.d.ts +68 -0
  492. package/dist/rich-text-editor/render.d.ts.map +1 -0
  493. package/dist/rich-text-editor/render.js +518 -0
  494. package/dist/rich-text-editor/render.js.map +1 -0
  495. package/dist/rich-text-editor/schema.d.ts +16 -0
  496. package/dist/rich-text-editor/schema.d.ts.map +1 -0
  497. package/dist/rich-text-editor/schema.js +386 -0
  498. package/dist/rich-text-editor/schema.js.map +1 -0
  499. package/dist/rich-text-editor/store.d.ts +11 -0
  500. package/dist/rich-text-editor/store.d.ts.map +1 -0
  501. package/dist/rich-text-editor/store.js +43 -0
  502. package/dist/rich-text-editor/store.js.map +1 -0
  503. package/dist/rich-text-editor/suggest.d.ts +127 -0
  504. package/dist/rich-text-editor/suggest.d.ts.map +1 -0
  505. package/dist/rich-text-editor/suggest.js +0 -0
  506. package/dist/rich-text-editor/suggest.js.map +1 -0
  507. package/dist/rich-text-editor/tables.d.ts +41 -0
  508. package/dist/rich-text-editor/tables.d.ts.map +1 -0
  509. package/dist/rich-text-editor/tables.js +204 -0
  510. package/dist/rich-text-editor/tables.js.map +1 -0
  511. package/dist/rich-text-editor/toolbar.d.ts +58 -0
  512. package/dist/rich-text-editor/toolbar.d.ts.map +1 -0
  513. package/dist/rich-text-editor/toolbar.js +548 -0
  514. package/dist/rich-text-editor/toolbar.js.map +1 -0
  515. package/dist/rich-text-editor/view.d.ts +368 -0
  516. package/dist/rich-text-editor/view.d.ts.map +1 -0
  517. package/dist/rich-text-editor/view.js +1968 -0
  518. package/dist/rich-text-editor/view.js.map +1 -0
  519. package/dist/rich-text-editor/virtual.d.ts +54 -0
  520. package/dist/rich-text-editor/virtual.d.ts.map +1 -0
  521. package/dist/rich-text-editor/virtual.js +279 -0
  522. package/dist/rich-text-editor/virtual.js.map +1 -0
  523. package/dist/richtext/node.d.ts +71 -4
  524. package/dist/richtext/node.d.ts.map +1 -1
  525. package/dist/richtext/node.js +233 -28
  526. package/dist/richtext/node.js.map +1 -1
  527. package/dist/richtext/runs.d.ts +13 -8
  528. package/dist/richtext/runs.d.ts.map +1 -1
  529. package/dist/richtext/runs.js +18 -8
  530. package/dist/richtext/runs.js.map +1 -1
  531. package/dist/table/index.d.ts.map +1 -1
  532. package/dist/table/index.js +23 -0
  533. package/dist/table/index.js.map +1 -1
  534. package/dist/table/rows.d.ts +3 -2
  535. package/dist/table/rows.d.ts.map +1 -1
  536. package/dist/table/rows.js +20 -6
  537. package/dist/table/rows.js.map +1 -1
  538. package/dist/terminal/backends.d.ts +4 -1
  539. package/dist/terminal/backends.d.ts.map +1 -1
  540. package/dist/terminal/backends.js.map +1 -1
  541. package/dist/terminal/index.d.ts +18 -7
  542. package/dist/terminal/index.d.ts.map +1 -1
  543. package/dist/terminal/index.js +32 -11
  544. package/dist/terminal/index.js.map +1 -1
  545. package/dist/terminal/vt/pty.d.ts +13 -2
  546. package/dist/terminal/vt/pty.d.ts.map +1 -1
  547. package/dist/terminal/vt/pty.js +18 -3
  548. package/dist/terminal/vt/pty.js.map +1 -1
  549. package/dist/terminal-output/index.d.ts.map +1 -1
  550. package/dist/terminal-output/index.js +6 -1
  551. package/dist/terminal-output/index.js.map +1 -1
  552. package/dist/three/canvas.d.ts.map +1 -1
  553. package/dist/three/canvas.js +85 -7
  554. package/dist/three/canvas.js.map +1 -1
  555. package/dist/three/events.d.ts +22 -4
  556. package/dist/three/events.d.ts.map +1 -1
  557. package/dist/three/events.js +34 -4
  558. package/dist/three/events.js.map +1 -1
  559. package/dist/tree/index.d.ts.map +1 -1
  560. package/dist/tree/index.js +17 -0
  561. package/dist/tree/index.js.map +1 -1
  562. package/package.json +59 -31
  563. package/src/charts/node.ts +68 -28
  564. package/src/charts/render.ts +120 -30
  565. package/src/code/index.ts +3 -1
  566. package/src/code-editor/completion.ts +3 -2
  567. package/src/code-editor/index.ts +19 -0
  568. package/src/code-editor/node.ts +1574 -184
  569. package/src/code-language/languages/glsl.ts +12 -2
  570. package/src/code-language/languages/javascript.ts +21 -4
  571. package/src/code-language/languages/json.ts +12 -2
  572. package/src/code-language/languages/shell.ts +12 -2
  573. package/src/code-language/languages/sql.ts +22 -2
  574. package/src/code-language/stream.ts +257 -17
  575. package/src/code-language/types.ts +5 -4
  576. package/src/codeblock/index.ts +20 -3
  577. package/src/embed/client.ts +102 -8
  578. package/src/embed/index.ts +2 -0
  579. package/src/flow/draw.ts +167 -41
  580. package/src/flow/gl/color.ts +85 -0
  581. package/src/flow/gl/index.ts +342 -0
  582. package/src/flow/gl/pack.ts +585 -0
  583. package/src/flow/gl/renderer.ts +679 -0
  584. package/src/flow/gl/shaders.ts +371 -0
  585. package/src/flow/gl/text.ts +1295 -0
  586. package/src/flow/index.ts +794 -67
  587. package/src/flow/node.ts +2496 -1256
  588. package/src/flow/paint.ts +511 -0
  589. package/src/flow/paths.ts +80 -15
  590. package/src/flow/scene.ts +2199 -0
  591. package/src/flow/types.ts +113 -1
  592. package/src/html/controls.ts +73 -2
  593. package/src/html/css/calc.ts +385 -0
  594. package/src/html/css/cascade.ts +1403 -62
  595. package/src/html/css/color.ts +755 -0
  596. package/src/html/css/content.ts +417 -0
  597. package/src/html/css/counter-styles.ts +973 -0
  598. package/src/html/css/decode.ts +130 -0
  599. package/src/html/css/parse.ts +1514 -160
  600. package/src/html/css/style.ts +4102 -222
  601. package/src/html/css/ua.ts +58 -12
  602. package/src/html/css/values.ts +150 -50
  603. package/src/html/css/vars.ts +348 -0
  604. package/src/html/dom.ts +284 -17
  605. package/src/html/fonts.ts +641 -0
  606. package/src/html/gif.ts +233 -0
  607. package/src/html/index.ts +180 -20
  608. package/src/html/layout/block.ts +3456 -230
  609. package/src/html/layout/boxes.ts +2100 -190
  610. package/src/html/layout/cache.ts +289 -0
  611. package/src/html/layout/collapse.ts +317 -0
  612. package/src/html/layout/css-grid.ts +1063 -0
  613. package/src/html/layout/flex.ts +1115 -106
  614. package/src/html/layout/floats.ts +128 -14
  615. package/src/html/layout/grid-lines.ts +141 -0
  616. package/src/html/layout/grid.ts +181 -0
  617. package/src/html/layout/inline.ts +3943 -192
  618. package/src/html/layout/shaping.ts +140 -0
  619. package/src/html/layout/table.ts +696 -186
  620. package/src/html/layout/tracks.ts +437 -0
  621. package/src/html/node.ts +1486 -121
  622. package/src/html/paint.ts +4293 -169
  623. package/src/html/resources.ts +161 -50
  624. package/src/html/surfaces.ts +81 -0
  625. package/src/html/svg.ts +493 -0
  626. package/src/html/url.ts +86 -0
  627. package/src/index.ts +4 -0
  628. package/src/internal/collate.ts +12 -0
  629. package/src/{markdown → internal/markdown}/ast.ts +35 -7
  630. package/src/{markdown → internal/markdown}/parse.ts +288 -35
  631. package/src/internal/markdown/stringify.ts +772 -0
  632. package/src/internal/prewarm.ts +45 -0
  633. package/src/internal/scroll.ts +5 -3
  634. package/src/internal/sdf.ts +227 -0
  635. package/src/internal/splice.ts +25 -0
  636. package/src/internal/window.ts +36 -5
  637. package/src/maps/anchors.ts +913 -0
  638. package/src/maps/controller.ts +1084 -0
  639. package/src/maps/gl/buckets.ts +461 -0
  640. package/src/maps/gl/build-worker.ts +66 -0
  641. package/src/maps/gl/color.ts +62 -0
  642. package/src/maps/gl/cover.ts +205 -0
  643. package/src/maps/gl/index.ts +23 -0
  644. package/src/maps/gl/markers.ts +83 -0
  645. package/src/maps/gl/overlays.ts +379 -0
  646. package/src/maps/gl/pane.ts +42 -0
  647. package/src/maps/gl/placement.ts +968 -0
  648. package/src/maps/gl/renderer.ts +1660 -0
  649. package/src/maps/gl/shaders.ts +489 -0
  650. package/src/maps/gl/store.ts +615 -0
  651. package/src/maps/gl/text.ts +868 -0
  652. package/src/maps/gl/view.ts +1489 -0
  653. package/src/maps/icons.ts +183 -0
  654. package/src/maps/index.ts +274 -81
  655. package/src/maps/labels.ts +457 -144
  656. package/src/maps/node.ts +832 -665
  657. package/src/maps/overlay.ts +148 -43
  658. package/src/maps/paint.ts +3 -0
  659. package/src/maps/proj.ts +118 -0
  660. package/src/maps/renderer.ts +155 -0
  661. package/src/maps/sources.ts +43 -6
  662. package/src/maps/style.ts +32 -0
  663. package/src/maps/styles.ts +215 -4
  664. package/src/maps/theme.ts +51 -0
  665. package/src/maps/tiles.ts +253 -62
  666. package/src/maps/types.ts +213 -21
  667. package/src/markdown/expressions.ts +5 -1
  668. package/src/markdown/index.ts +163 -78
  669. package/src/markdown/spans.ts +6 -1
  670. package/src/media-player/index.ts +18 -4
  671. package/src/qml/containers.tsx +6 -1
  672. package/src/qml/interaction.tsx +42 -5
  673. package/src/qml/layouts.tsx +21 -6
  674. package/src/qml/qtquick.tsx +9 -2
  675. package/src/qml/react.tsx +7 -5
  676. package/src/reorder/clock.ts +27 -0
  677. package/src/reorder/index.ts +100 -27
  678. package/src/rich-text-editor/clipboard.ts +439 -0
  679. package/src/rich-text-editor/collab.ts +91 -0
  680. package/src/rich-text-editor/commands.ts +448 -0
  681. package/src/rich-text-editor/dom-globals.d.ts +23 -0
  682. package/src/rich-text-editor/drag.ts +211 -0
  683. package/src/rich-text-editor/html.ts +619 -0
  684. package/src/rich-text-editor/index.ts +1364 -0
  685. package/src/rich-text-editor/inline.ts +345 -0
  686. package/src/rich-text-editor/keymap.ts +510 -0
  687. package/src/rich-text-editor/keys.ts +228 -0
  688. package/src/rich-text-editor/look.ts +427 -0
  689. package/src/rich-text-editor/markdown.ts +653 -0
  690. package/src/rich-text-editor/marks.ts +101 -0
  691. package/src/rich-text-editor/nodes.ts +497 -0
  692. package/src/rich-text-editor/render.ts +837 -0
  693. package/src/rich-text-editor/schema.ts +431 -0
  694. package/src/rich-text-editor/store.ts +39 -0
  695. package/src/rich-text-editor/suggest.ts +597 -0
  696. package/src/rich-text-editor/tables.ts +272 -0
  697. package/src/rich-text-editor/toolbar.ts +724 -0
  698. package/src/rich-text-editor/view.ts +2287 -0
  699. package/src/rich-text-editor/virtual.ts +372 -0
  700. package/src/richtext/node.ts +294 -44
  701. package/src/richtext/runs.ts +23 -12
  702. package/src/table/index.ts +29 -0
  703. package/src/table/rows.ts +18 -8
  704. package/src/terminal/backends.ts +4 -1
  705. package/src/terminal/index.ts +47 -15
  706. package/src/terminal/vt/pty.ts +16 -2
  707. package/src/terminal-output/index.ts +6 -1
  708. package/src/three/canvas.ts +102 -8
  709. package/src/three/events.ts +49 -9
  710. package/src/tree/index.ts +19 -0
  711. package/dist/markdown/ast.d.ts.map +0 -1
  712. package/dist/markdown/ast.js +0 -17
  713. package/dist/markdown/ast.js.map +0 -1
  714. package/dist/markdown/parse.d.ts.map +0 -1
  715. package/dist/markdown/parse.js.map +0 -1
  716. package/dist/markdown/tags.d.ts.map +0 -1
  717. package/dist/markdown/tags.js.map +0 -1
  718. /package/dist/{markdown → internal/markdown}/parse.d.ts +0 -0
  719. /package/dist/{markdown → internal/markdown}/tags.d.ts +0 -0
  720. /package/dist/{markdown → internal/markdown}/tags.js +0 -0
  721. /package/src/{markdown → internal/markdown}/tags.ts +0 -0
@@ -0,0 +1,1660 @@
1
+ // The GL renderer: every frame drawn from geometry, nothing cached as pixels.
2
+ //
3
+ // The retained renderer's frame composites tile *surfaces*; this one's frame
4
+ // is the whole style drawn again from each tile's buckets (`./buckets.ts`),
5
+ // which is a few hundred draw calls and a few uniform writes per call. Pan,
6
+ // zoom and restyle are therefore the same operation — draw — and a
7
+ // fractional zoom is not a scaled bitmap but the vector map at that zoom,
8
+ // line widths included.
9
+ //
10
+ // The frame is **layer-major**: for each style layer, every tile's share of
11
+ // it, before the next layer. That is what the cartography needs — every
12
+ // tile's water under any tile's roads, every casing under any fill, so
13
+ // junctions knit across tile seams exactly as they do inside a tile — and it
14
+ // is cheap because each tile is **scissored** to its own square. The camera
15
+ // has no rotation, so a tile's footprint on screen is an axis-aligned
16
+ // rectangle, and a scissor is a clip that costs one call and no stencil.
17
+ // Tiles carry a buffer of geometry past their edges; the scissor is what
18
+ // keeps two tiles from drawing the same road twice.
19
+ //
20
+ // Two things a caller can ask of a frame beyond "the style, here": that it
21
+ // be **cheaper** (`detail`, `edges` — adaptive quality's levers, priced in
22
+ // advance by `estimate`), and that a second scene be **faded in** over it
23
+ // (`render(frame, fade)` — the cross-fade between two pyramid levels, drawn
24
+ // into an offscreen target and composited once).
25
+ import type { PreparedStyle } from '../paint.js';
26
+ import type { MapStyleLayer } from '../style.js';
27
+ import { resolveZoomed } from '../style.js';
28
+ import { RECORD_BYTES, TILE_EXTENT } from './buckets.js';
29
+ import type { GlTileData } from './buckets.js';
30
+ import { parseColor, premultiplied } from './color.js';
31
+ import type { Rgba } from './color.js';
32
+ import { MARKER_INSTANCE } from './markers.js';
33
+ import type { MarkerBatch } from './markers.js';
34
+ import type { OverlayPass } from './overlays.js';
35
+ import { LABEL_INSTANCE } from './placement.js';
36
+ import type { LabelBatch } from './placement.js';
37
+ import {
38
+ ATTRIBUTES,
39
+ BLIT_FRAGMENT,
40
+ BLIT_VERTEX,
41
+ CIRCLE_FRAGMENT,
42
+ CIRCLE_VERTEX,
43
+ COVER_FRAGMENT,
44
+ COVER_VERTEX,
45
+ FAN_FRAGMENT,
46
+ FAN_VERTEX,
47
+ LABEL_FRAGMENT,
48
+ LABEL_VERTEX,
49
+ LINE_FRAGMENT,
50
+ LINE_VERTEX,
51
+ MARKER_FRAGMENT,
52
+ MARKER_VERTEX,
53
+ RASTER_FRAGMENT,
54
+ RASTER_VERTEX,
55
+ linkProgram,
56
+ } from './shaders.js';
57
+ import type { GL, Program } from './shaders.js';
58
+ import { QUAD_INSET } from './text.js';
59
+ import type { LabelAtlas } from './text.js';
60
+
61
+ /** A rectangle in device pixels, top-left origin. */
62
+ export interface DeviceRect {
63
+ x: number;
64
+ y: number;
65
+ width: number;
66
+ height: number;
67
+ }
68
+
69
+ /** One tile's data placed on screen. */
70
+ export interface RenderTile {
71
+ data: GlTileData;
72
+ /** Where the data tile's square lands, in device pixels: its top-left
73
+ * corner and its edge length. */
74
+ x: number;
75
+ y: number;
76
+ size: number;
77
+ /**
78
+ * The part of the viewport this tile draws into. Its own square, unless
79
+ * it is standing in for a tile that has no data yet — an ancestor scaled
80
+ * up, drawn only over the square it covers for.
81
+ */
82
+ clip: DeviceRect;
83
+ }
84
+
85
+ /** Everything a frame is drawn from. */
86
+ export interface RenderFrame {
87
+ /** Device pixels. */
88
+ width: number;
89
+ height: number;
90
+ /** The camera zoom: what zoom ramps and layer ranges resolve at. */
91
+ zoom: number;
92
+ /**
93
+ * How much of each of the style's layers to draw, by the layer's index in
94
+ * `style.layers` — a layer the camera is zooming past `minZoom` is ramped
95
+ * out rather than cut, and what is not listed is `drawsAt`'s own answer.
96
+ *
97
+ * Per layer rather than per scene because the flips overlap: the zoom a
98
+ * layer ends at is not where the pyramid changes level, so a scene-wide
99
+ * cross-fade of the two would keep retargeting, and each retarget is a
100
+ * step in the picture. An opacity is a number that can move on its own.
101
+ */
102
+ layerAlpha?: readonly number[];
103
+ /** Device pixels per logical pixel: what style lengths are multiplied by. */
104
+ scale: number;
105
+ style: PreparedStyle;
106
+ background?: string;
107
+ /**
108
+ * Each source's tiles, in the map's order — a basemap, then a pyramid
109
+ * over it. Each is drawn whole, layer-major, before the next, which is
110
+ * how the retained renderer lays one source's tiles over another's.
111
+ */
112
+ sources: readonly (readonly RenderTile[])[];
113
+ /**
114
+ * Leave out the style layers that begin within this many levels of the
115
+ * zoom: a layer whose `minZoom` is above `zoom - detail` is skipped. `0`
116
+ * (the default) draws what the style says.
117
+ *
118
+ * The layers a style brings in last as the map zooms in are its details —
119
+ * buildings, service roads, paths, sites — so this drops exactly those, in
120
+ * that order, and it needs nothing from the style that the style has not
121
+ * already said.
122
+ */
123
+ detail?: number;
124
+ /** The fill edge pass for this frame, over the renderer's `antialias`. */
125
+ edges?: boolean;
126
+ }
127
+
128
+ /** A second scene, and how much of it to show over the first. */
129
+ export interface FadeFrame {
130
+ frame: RenderFrame;
131
+ /** `0` shows only the base scene, `1` only this one. */
132
+ alpha: number;
133
+ }
134
+
135
+ /**
136
+ * The attribution: a box of the theme's background, and the text in it —
137
+ * one label's worth of instance data from the labels' atlas, set level and
138
+ * on whole pixels.
139
+ */
140
+ export interface AttributionDraw {
141
+ /** Device pixels, top-left origin. */
142
+ box: DeviceRect;
143
+ /** Premultiplied, its opacity folded in. */
144
+ boxColor: Rgba;
145
+ text: LabelBatch;
146
+ }
147
+
148
+ /** The overlays' bucket, and where its units land this frame. */
149
+ export interface OverlayDraw {
150
+ data: GlTileData;
151
+ passes: readonly OverlayPass[];
152
+ /** Device pixels per unit of the bucket's records. */
153
+ unit: number;
154
+ /** Where the bucket's origin lands, in device pixels. */
155
+ x: number;
156
+ y: number;
157
+ }
158
+
159
+ /** What a frame draws over its scene, in this order. */
160
+ export interface RenderExtras {
161
+ /** A second scene, faded in over the first. */
162
+ fade?: FadeFrame | null;
163
+ /** The labels, over both. */
164
+ labels?: LabelBatch | null;
165
+ /** The overlays, over the labels — where the retained renderer draws
166
+ * them. */
167
+ overlays?: OverlayDraw | null;
168
+ /** The markers, over the labels — where the retained renderer draws
169
+ * them, a marker being what the user aims at. */
170
+ markers?: MarkerBatch | null;
171
+ /** The attribution, over everything — drawn last, as the retained
172
+ * renderer draws it. */
173
+ attribution?: AttributionDraw | null;
174
+ }
175
+
176
+ export interface GlRenderOptions {
177
+ /**
178
+ * Draw each polygon's boundary as a half-pixel antialiased line after the
179
+ * fill. `true` by default. There is no multisampling on these surfaces
180
+ * (x11-dri exposes no `renderbufferStorageMultisample`), so without it a
181
+ * fill's edge is a staircase; this is the same trade MapLibre's
182
+ * `fill-antialias` makes.
183
+ */
184
+ antialias?: boolean;
185
+ /**
186
+ * `'nonzero'` (the default) is the rule the retained renderer fills with,
187
+ * and costs two stencil passes, one per facing — or one, on a GL table
188
+ * with `stencilOpSeparate` (x11-dri 0.8, and the Cocoa backend's), which
189
+ * counts both facings in the same draw. `'evenodd'` is one pass —
190
+ * and wrong wherever two features of one layer overlap, which real
191
+ * landuse does: the overlap is cut out as if it were a hole.
192
+ */
193
+ fillRule?: 'nonzero' | 'evenodd';
194
+ /**
195
+ * Draw into an offscreen framebuffer and copy it to the surface. `'auto'`
196
+ * (the default) does that only when the surface has no stencil buffer —
197
+ * which is XQuartz's Apple-DRI surface today, whose context ntk creates
198
+ * without one.
199
+ */
200
+ offscreen?: 'auto' | boolean;
201
+ }
202
+
203
+ /** What one frame cost, and what it drew. */
204
+ export interface GlRenderStats {
205
+ tiles: number;
206
+ layers: number;
207
+ drawCalls: number;
208
+ /** Segment instances drawn, every pass counted — the frame's work. */
209
+ instances: number;
210
+ /** Tiles uploaded this frame, and their bytes. */
211
+ uploads: number;
212
+ uploadBytes: number;
213
+ /** Milliseconds on this thread issuing the frame — not the GPU's time. */
214
+ cpuMs: number;
215
+ offscreen: boolean;
216
+ /** How much of a second scene was faded in; `0` when there was none. */
217
+ fade: number;
218
+ /** Labels drawn. */
219
+ labels: number;
220
+ /** Markers drawn. */
221
+ markers: number;
222
+ /** Overlay passes drawn — a line with a casing is two. */
223
+ overlays: number;
224
+ }
225
+
226
+ /** A stroke: premultiplied colour, half its width in device pixels, and its
227
+ * dash pattern as {@link dashPattern} gives one. */
228
+ interface StrokePaint {
229
+ color: Rgba;
230
+ half: number;
231
+ dash: readonly number[] | null;
232
+ }
233
+
234
+ /** Entries of a dash pattern the line program holds: four dashes. */
235
+ const DASH_ENTRIES = 8;
236
+
237
+ /**
238
+ * A style's or an overlay's dash array as the line program draws it: in
239
+ * device pixels, and read as a canvas reads one — an odd number of entries
240
+ * is repeated to make an even one, and an entry that is negative or not a
241
+ * number, or entries that are all zero, make a solid line. A pattern of
242
+ * more than four dashes is cut to its first four.
243
+ */
244
+ export function dashPattern(
245
+ dash: readonly number[] | undefined,
246
+ scale: number,
247
+ ): number[] | null {
248
+ if (!dash || dash.length === 0) return null;
249
+ if (dash.some((d) => !Number.isFinite(d) || d < 0)) return null;
250
+ if (!dash.some((d) => d > 0)) return null;
251
+ const even = dash.length % 2 === 1 ? [...dash, ...dash] : dash;
252
+ return even.slice(0, DASH_ENTRIES).map((d) => d * scale);
253
+ }
254
+
255
+ /** A polygon: its colour, and its edge — the antialiasing, or an
256
+ * outline. */
257
+ interface FillPaint {
258
+ color: Rgba;
259
+ antialias: boolean;
260
+ outline: Rgba | null;
261
+ outlineHalf: number;
262
+ }
263
+
264
+ const CLEAR: Rgba = [0, 0, 0, 0];
265
+
266
+ interface TileGpu {
267
+ line: unknown;
268
+ fill: unknown;
269
+ /** One vertex array per range start and stream: `first * 2 + (fill ? 1 : 0)`. */
270
+ vaos: Map<number, unknown>;
271
+ /** A raster tile's image. */
272
+ texture: unknown;
273
+ bytes: number;
274
+ }
275
+
276
+ interface Offscreen {
277
+ fbo: unknown;
278
+ texture: unknown;
279
+ depth: unknown;
280
+ width: number;
281
+ height: number;
282
+ }
283
+
284
+ const globals = globalThis as { performance?: { now(): number } };
285
+ const now = (): number => globals.performance?.now() ?? Date.now();
286
+
287
+ /** Whether a style layer draws at this zoom, with `detail` levels of its
288
+ * newest layers left out — `paint.ts`'s gate, plus adaptive quality's. */
289
+ function drawsAt(layer: MapStyleLayer, zoom: number, detail: number): boolean {
290
+ if (layer.visible === false) return false;
291
+ if (
292
+ layer.type !== 'fill' &&
293
+ layer.type !== 'line' &&
294
+ layer.type !== 'circle'
295
+ ) {
296
+ return false;
297
+ }
298
+ if (layer.minZoom !== undefined && zoom < layer.minZoom) return false;
299
+ if (layer.maxZoom !== undefined && zoom >= layer.maxZoom) return false;
300
+ if (
301
+ detail > 0 &&
302
+ layer.minZoom !== undefined &&
303
+ layer.minZoom > zoom - detail
304
+ ) {
305
+ return false;
306
+ }
307
+ return true;
308
+ }
309
+
310
+ /**
311
+ * How much of a layer this frame draws: its `layerAlpha` where the frame
312
+ * has one, and otherwise the gate. The ramp is the whole answer where there
313
+ * is one — whoever set it has already folded in every gate, `detail`
314
+ * included — but never for a layer the style has turned off or a kind this
315
+ * renderer does not draw.
316
+ *
317
+ * Exported for the tests that watch a swept frame decide.
318
+ */
319
+ export function gateOf(
320
+ frame: RenderFrame,
321
+ index: number,
322
+ layer: MapStyleLayer,
323
+ detail: number,
324
+ ): number {
325
+ if (layer.visible === false) return 0;
326
+ if (layer.type !== 'fill' && layer.type !== 'line' && layer.type !== 'circle')
327
+ return 0;
328
+ const ramp = frame.layerAlpha?.[index];
329
+ if (ramp === undefined) return drawsAt(layer, frame.zoom, detail) ? 1 : 0;
330
+ return ramp > 0 ? Math.min(1, ramp) : 0;
331
+ }
332
+
333
+ /**
334
+ * Which of a style's layers the gates draw, as a `layerAlpha` of ones and
335
+ * zeroes — what a ramp moves towards. `detail` is adaptive quality's, so a
336
+ * rung that drops the style's newest layers is something to ramp like any
337
+ * other gate rather than a step in the picture.
338
+ */
339
+ export function gatesAt(
340
+ style: PreparedStyle,
341
+ zoom: number,
342
+ detail = 0,
343
+ ): number[] {
344
+ return style.layers.map(({ layer }) =>
345
+ drawsAt(layer, zoom, detail) ? 1 : 0,
346
+ );
347
+ }
348
+
349
+ /** Label fields taken into the atlas texture per frame at most. */
350
+ const ATLAS_UPLOADS_PER_FRAME = 24;
351
+
352
+ /** GL's own name for "which VAO is bound", which the table may not carry. */
353
+ const VERTEX_ARRAY_BINDING = 0x85b5;
354
+ const FRAMEBUFFER_BINDING = 0x8ca6;
355
+
356
+ export class GlMapRenderer {
357
+ private readonly _gl: GL;
358
+ private readonly _options: Required<GlRenderOptions>;
359
+ private readonly _line: Program;
360
+ private readonly _fan: Program;
361
+ private readonly _cover: Program;
362
+ private _blit: Program | null = null;
363
+ private readonly _corners: unknown;
364
+ private readonly _coverVao: unknown;
365
+ private readonly _tiles = new Map<GlTileData, TileGpu>();
366
+ private readonly _colors = new Map<string, Rgba | null>();
367
+ private _stencil: boolean | null = null;
368
+ /** Whether the table sets each facing's stencil operation apart, which
369
+ * makes the non-zero winding one pass. */
370
+ private readonly _separate: boolean;
371
+ /** Offscreen targets by role: `'output'` stands in for a surface with no
372
+ * stencil buffer, `'fade'` holds the scene being faded in. */
373
+ private readonly _targets = new Map<string, Offscreen>();
374
+ /** The label pass: its program, its instance buffer and the vertex array
375
+ * over it, and the atlas texture with the atlas generation it holds. */
376
+ private _label: Program | null = null;
377
+ private _labelVao: unknown = null;
378
+ private _labelBuffer: unknown = null;
379
+ private _marker: Program | null = null;
380
+ private _markerVao: unknown = null;
381
+ private _markerBuffer: unknown = null;
382
+ private _circle: Program | null = null;
383
+ private _raster: Program | null = null;
384
+ private _atlasTexture: unknown = null;
385
+ /** RGBA a field is widened into for its upload, reused. Its colour
386
+ * channels are white and never written again; only alpha changes. */
387
+ private _fieldScratch = new Uint8Array(0);
388
+ private _atlasOwner: LabelAtlas | null = null;
389
+ private _stats: GlRenderStats = GlMapRenderer._emptyStats();
390
+ /** Bytes of tile geometry resident on the GPU. */
391
+ gpuBytes = 0;
392
+
393
+ constructor(gl: GL, options: GlRenderOptions = {}) {
394
+ this._gl = gl;
395
+ this._options = {
396
+ antialias: options.antialias ?? true,
397
+ fillRule: options.fillRule ?? 'nonzero',
398
+ offscreen: options.offscreen ?? 'auto',
399
+ };
400
+ // Asked of the table itself: an x11-dri from before 0.8 has no such
401
+ // entry at all, and an entry that is there works.
402
+ this._separate =
403
+ 'stencilOpSeparate' in (gl as object) &&
404
+ typeof gl.stencilOpSeparate === 'function';
405
+ this._line = linkProgram(gl, LINE_VERTEX, LINE_FRAGMENT, [
406
+ 'u_tile',
407
+ 'u_viewport',
408
+ 'u_half',
409
+ 'u_color',
410
+ 'u_dash_a',
411
+ 'u_dash_b',
412
+ 'u_period',
413
+ 'u_part',
414
+ ]);
415
+ this._fan = linkProgram(gl, FAN_VERTEX, FAN_FRAGMENT, [
416
+ 'u_tile',
417
+ 'u_viewport',
418
+ ]);
419
+ this._cover = linkProgram(gl, COVER_VERTEX, COVER_FRAGMENT, ['u_color']);
420
+
421
+ const previous = gl.getParameter(VERTEX_ARRAY_BINDING);
422
+ // The quad every instance is drawn over. A capsule is a four-vertex
423
+ // strip; a fan triangle takes the first three corners.
424
+ this._corners = gl.createBuffer();
425
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._corners);
426
+ gl.bufferData(
427
+ gl.ARRAY_BUFFER,
428
+ new Float32Array([0, -1, 1, -1, 0, 1, 1, 1]),
429
+ gl.STATIC_DRAW,
430
+ );
431
+ this._coverVao = gl.createVertexArray();
432
+ gl.bindVertexArray(this._coverVao);
433
+ gl.enableVertexAttribArray(ATTRIBUTES.a_corner);
434
+ gl.vertexAttribPointer(ATTRIBUTES.a_corner, 2, gl.FLOAT, false, 0, 0);
435
+ gl.bindVertexArray(previous);
436
+ }
437
+
438
+ set antialias(on: boolean) {
439
+ this._options.antialias = on;
440
+ }
441
+
442
+ set fillRule(rule: 'nonzero' | 'evenodd') {
443
+ this._options.fillRule = rule;
444
+ }
445
+
446
+ private static _emptyStats(): GlRenderStats {
447
+ return {
448
+ tiles: 0,
449
+ layers: 0,
450
+ drawCalls: 0,
451
+ instances: 0,
452
+ uploads: 0,
453
+ uploadBytes: 0,
454
+ cpuMs: 0,
455
+ offscreen: false,
456
+ fade: 0,
457
+ labels: 0,
458
+ markers: 0,
459
+ overlays: 0,
460
+ };
461
+ }
462
+
463
+ /**
464
+ * What a frame would cost, in the unit `stats.instances` counts — segment
465
+ * instances, every pass included — without drawing it. The same gates the
466
+ * frame itself applies, over the ranges' record counts, so it is a walk of
467
+ * a few hundred numbers: cheap enough to price several versions of the
468
+ * next frame and draw the one that fits.
469
+ */
470
+ estimate(frame: RenderFrame): number {
471
+ const { width, height } = frame;
472
+ const layers = frame.style.layers;
473
+ const detail = frame.detail ?? 0;
474
+ const edges = frame.edges ?? this._options.antialias;
475
+ const stencil =
476
+ this._options.fillRule === 'nonzero' && !this._separate ? 2 : 1;
477
+ let work = 0;
478
+ for (const tiles of frame.sources) {
479
+ const visible = tiles.map(
480
+ (t) => scissorOf(t.clip, width, height) !== null,
481
+ );
482
+ for (let i = 0; i < layers.length; i++) {
483
+ const layer = layers[i].layer;
484
+ // The gate, not the ramp: what a rung is priced at is what it
485
+ // settles at. A layer on its way out is still being drawn, and
486
+ // pricing that would put the next rung's estimate above the budget
487
+ // — which would drop another layer, which would ramp, which is a
488
+ // ladder that climbs on its own transients.
489
+ if (!drawsAt(layer, frame.zoom, detail)) continue;
490
+ let records = 0;
491
+ for (let t = 0; t < tiles.length; t++) {
492
+ if (!visible[t]) continue;
493
+ const draw = tiles[t].data.draws[i];
494
+ if (!draw) continue;
495
+ for (let r = 1; r < draw.ranges.length; r += 2) {
496
+ records += draw.ranges[r] - 1;
497
+ }
498
+ }
499
+ if (layer.type === 'fill') {
500
+ const edge = layer.outlineColor !== undefined || edges ? 1 : 0;
501
+ work += records * (stencil + edge);
502
+ } else {
503
+ work += records;
504
+ }
505
+ }
506
+ }
507
+ return work;
508
+ }
509
+
510
+ /**
511
+ * Draw a frame into whatever framebuffer is bound — and, given `fade`, a
512
+ * second scene over it at `fade.alpha`, and given `labels`, the labels
513
+ * over both.
514
+ *
515
+ * The second scene is drawn whole into an offscreen target and composited
516
+ * once, rather than layer by layer at reduced opacity: a map faded layer
517
+ * by layer shows its casings through its fills and its water through its
518
+ * land. Both scenes are opaque, so the composite is exactly a cross-fade.
519
+ * Labels go on after it, at full strength: they are placed for the view,
520
+ * not for either level, and a name that dimmed with the level it came
521
+ * from would blink at every level change.
522
+ */
523
+ render(frame: RenderFrame, extras: RenderExtras = {}): GlRenderStats {
524
+ const { fade, labels, overlays, markers, attribution } = extras;
525
+ const gl = this._gl;
526
+ const started = now();
527
+ const stats = (this._stats = GlMapRenderer._emptyStats());
528
+ const previousVao = gl.getParameter(VERTEX_ARRAY_BINDING);
529
+ const surface = gl.getParameter(FRAMEBUFFER_BINDING);
530
+ const { width, height } = frame;
531
+
532
+ // Before the probe: it draws, and a context that has never had a
533
+ // viewport set — a windowless one, bound to a framebuffer — has a 0×0
534
+ // one, which draws nothing and reads as "no stencil".
535
+ gl.viewport(0, 0, width, height);
536
+ if (this._stencil === null) this._stencil = this._probeStencil();
537
+ const mode = this._options.offscreen;
538
+ const offscreen = mode === true || (mode === 'auto' && !this._stencil);
539
+ stats.offscreen = offscreen;
540
+ const output = offscreen ? this._target('output', width, height) : null;
541
+ gl.bindFramebuffer(gl.FRAMEBUFFER, output ? output.fbo : surface);
542
+
543
+ this._scene(frame);
544
+
545
+ if (fade && fade.alpha > 0) {
546
+ const layer = this._target('fade', width, height);
547
+ gl.bindFramebuffer(gl.FRAMEBUFFER, layer.fbo);
548
+ this._scene(fade.frame);
549
+ gl.bindFramebuffer(gl.FRAMEBUFFER, output ? output.fbo : surface);
550
+ this._composite(layer, width, height, Math.min(1, fade.alpha));
551
+ stats.fade = Math.min(1, fade.alpha);
552
+ }
553
+
554
+ // Even with nothing to draw: the batch's atlas may have fields waiting
555
+ // for the texture, and until they are in it nothing of theirs can be.
556
+ if (labels) {
557
+ this._labels(labels, width, height);
558
+ // Labels, not instances: an icon's plate and glyph are two more.
559
+ stats.labels = labels.labels ?? labels.count;
560
+ }
561
+
562
+ if (overlays) stats.overlays = this._overlays(overlays, frame);
563
+
564
+ if (markers) stats.markers = this._markers(markers, width, height);
565
+
566
+ // Last, over the labels and everything else, which is where the
567
+ // retained renderer draws it: a licence condition is not something a
568
+ // street name may cover.
569
+ if (attribution) this._attribution(attribution, width, height);
570
+
571
+ if (output) {
572
+ gl.bindFramebuffer(gl.FRAMEBUFFER, surface);
573
+ this._composite(output, width, height, 1);
574
+ }
575
+ gl.bindVertexArray(previousVao);
576
+ stats.cpuMs = now() - started;
577
+ return stats;
578
+ }
579
+
580
+ /** Drop one tile's buffers. */
581
+ release(data: GlTileData): void {
582
+ const gpu = this._tiles.get(data);
583
+ if (!gpu) return;
584
+ const gl = this._gl;
585
+ for (const vao of gpu.vaos.values()) gl.deleteVertexArray(vao);
586
+ if (gpu.line) gl.deleteBuffer(gpu.line);
587
+ if (gpu.fill) gl.deleteBuffer(gpu.fill);
588
+ if (gpu.texture) gl.deleteTexture(gpu.texture);
589
+ this.gpuBytes -= gpu.bytes;
590
+ this._tiles.delete(data);
591
+ }
592
+
593
+ /** Whether a tile's buffers are resident. */
594
+ resident(data: GlTileData): boolean {
595
+ return this._tiles.has(data);
596
+ }
597
+
598
+ dispose(): void {
599
+ const gl = this._gl;
600
+ for (const data of [...this._tiles.keys()]) this.release(data);
601
+ for (const program of [
602
+ this._line,
603
+ this._fan,
604
+ this._cover,
605
+ this._blit,
606
+ this._label,
607
+ this._marker,
608
+ this._circle,
609
+ this._raster,
610
+ ]) {
611
+ if (program) gl.deleteProgram(program.program);
612
+ }
613
+ gl.deleteBuffer(this._corners);
614
+ gl.deleteVertexArray(this._coverVao);
615
+ if (this._labelVao) gl.deleteVertexArray(this._labelVao);
616
+ if (this._labelBuffer) gl.deleteBuffer(this._labelBuffer);
617
+ if (this._markerVao) gl.deleteVertexArray(this._markerVao);
618
+ if (this._markerBuffer) gl.deleteBuffer(this._markerBuffer);
619
+ if (this._atlasTexture) gl.deleteTexture(this._atlasTexture);
620
+ for (const target of this._targets.values()) this._dropTarget(target);
621
+ this._targets.clear();
622
+ }
623
+
624
+ // --- the scene ------------------------------------------------------------
625
+
626
+ /** The whole style, into the bound framebuffer, from a clear. */
627
+ private _scene(frame: RenderFrame): void {
628
+ const gl = this._gl;
629
+ const stats = this._stats;
630
+ const { width, height } = frame;
631
+ gl.viewport(0, 0, width, height);
632
+ const background = premultiplied(
633
+ this._color(frame.background ?? '#ffffff') ?? [1, 1, 1, 1],
634
+ );
635
+ gl.disable(gl.SCISSOR_TEST);
636
+ gl.clearColor(background[0], background[1], background[2], background[3]);
637
+ gl.clearStencil(0);
638
+ gl.stencilMask(0xff);
639
+ gl.colorMask(true, true, true, true);
640
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
641
+ gl.disable(gl.DEPTH_TEST);
642
+ gl.disable(gl.CULL_FACE);
643
+ gl.enable(gl.BLEND);
644
+ gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
645
+ gl.enable(gl.SCISSOR_TEST);
646
+
647
+ for (const program of [this._line, this._fan]) {
648
+ gl.useProgram(program.program);
649
+ gl.uniform2f(program.uniforms.u_viewport, width, height);
650
+ }
651
+
652
+ const edges = frame.edges ?? this._options.antialias;
653
+ const detail = frame.detail ?? 0;
654
+ const layers = frame.style.layers;
655
+ for (const tiles of frame.sources) {
656
+ // Upload before drawing, so a frame's uploads are counted once and a
657
+ // tile arriving mid-frame is not half drawn.
658
+ const scissors: (number[] | null)[] = [];
659
+ for (const tile of tiles) {
660
+ this._gpu(tile.data);
661
+ scissors.push(scissorOf(tile.clip, width, height));
662
+ }
663
+ stats.tiles += tiles.length;
664
+ // A raster source is its images, and no style.
665
+ if (tiles.some((tile) => tile.data.raster)) {
666
+ this._rasters(tiles, scissors, width, height);
667
+ continue;
668
+ }
669
+ for (let i = 0; i < layers.length; i++) {
670
+ const layer = layers[i].layer;
671
+ const gate = gateOf(frame, i, layer, detail);
672
+ if (gate <= 0) continue;
673
+ if (layer.type === 'fill') {
674
+ if (this._fill(frame, tiles, i, layer, scissors, edges, gate)) {
675
+ stats.layers++;
676
+ }
677
+ } else if (layer.type === 'line') {
678
+ if (this._lines(frame, tiles, i, layer, scissors, gate)) {
679
+ stats.layers++;
680
+ }
681
+ } else if (layer.type === 'circle') {
682
+ if (this._circles(frame, tiles, i, layer, scissors, gate)) {
683
+ stats.layers++;
684
+ }
685
+ }
686
+ }
687
+ }
688
+
689
+ gl.disable(gl.SCISSOR_TEST);
690
+ gl.disable(gl.STENCIL_TEST);
691
+ }
692
+
693
+ // --- layers ---------------------------------------------------------------
694
+
695
+ private _fill(
696
+ frame: RenderFrame,
697
+ tiles: readonly RenderTile[],
698
+ index: number,
699
+ layer: Extract<MapStyleLayer, { type: 'fill' }>,
700
+ scissors: (number[] | null)[],
701
+ edges: boolean,
702
+ gate = 1,
703
+ ): boolean {
704
+ const zoom = frame.zoom;
705
+ const opacity =
706
+ (layer.opacity === undefined ? 1 : resolveZoomed(layer.opacity, zoom)) *
707
+ gate;
708
+ if (opacity <= 0) return false;
709
+ const base = this._color(resolveZoomed(layer.color, zoom));
710
+ if (!base) return false;
711
+ const outline =
712
+ layer.outlineColor === undefined
713
+ ? undefined
714
+ : this._color(resolveZoomed(layer.outlineColor, zoom));
715
+ return this._fillWith(tiles, index, scissors, {
716
+ color: premultiplied(base, opacity),
717
+ // The edge: the outline colour a style asked for, at one logical
718
+ // pixel, or else the fill colour at half a device pixel — which is
719
+ // the antialiasing.
720
+ antialias: !outline && edges,
721
+ outline: outline ? premultiplied(outline, opacity) : null,
722
+ outlineHalf: Math.max(0.5, frame.scale / 2),
723
+ });
724
+ }
725
+
726
+ /**
727
+ * A polygon's ranges, over every tile that has them: the winding number
728
+ * into the stencil buffer, the colour wherever it is not zero, then the
729
+ * edge — the antialiasing, or an outline.
730
+ */
731
+ private _fillWith(
732
+ tiles: readonly RenderTile[],
733
+ index: number,
734
+ scissors: (number[] | null)[],
735
+ paint: FillPaint,
736
+ ): boolean {
737
+ const gl = this._gl;
738
+ // The cover's extent: the union of the squares this layer drew in.
739
+ const box = this._unionOf(tiles, index, scissors);
740
+ if (!box) return false;
741
+ const stats = this._stats;
742
+
743
+ // 1. The winding number, into the stencil buffer.
744
+ gl.useProgram(this._fan.program);
745
+ gl.colorMask(false, false, false, false);
746
+ gl.enable(gl.STENCIL_TEST);
747
+ gl.stencilFunc(gl.ALWAYS, 0, 0xff);
748
+ const fan = (count: number): void => {
749
+ gl.drawArraysInstanced(gl.TRIANGLES, 0, 3, count);
750
+ };
751
+ const nonzero = this._options.fillRule === 'nonzero';
752
+ if (nonzero && this._separate) {
753
+ // Front faces count up and back faces down, in the one draw.
754
+ gl.disable(gl.CULL_FACE);
755
+ gl.stencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.INCR_WRAP);
756
+ gl.stencilOpSeparate(gl.BACK, gl.KEEP, gl.KEEP, gl.DECR_WRAP);
757
+ this._eachTile(tiles, index, scissors, this._fan, true, fan);
758
+ } else {
759
+ const passes: [number | null, number][] = nonzero
760
+ ? [
761
+ [gl.BACK, gl.INCR_WRAP],
762
+ [gl.FRONT, gl.DECR_WRAP],
763
+ ]
764
+ : [[null, gl.INVERT]];
765
+ for (const [cull, op] of passes) {
766
+ if (cull === null) gl.disable(gl.CULL_FACE);
767
+ else {
768
+ gl.enable(gl.CULL_FACE);
769
+ gl.cullFace(cull);
770
+ }
771
+ gl.stencilOp(gl.KEEP, gl.KEEP, op);
772
+ this._eachTile(tiles, index, scissors, this._fan, true, fan);
773
+ }
774
+ }
775
+ gl.disable(gl.CULL_FACE);
776
+
777
+ // 2. The colour, wherever the winding is not zero. An opaque fill sets
778
+ // the stencil back to zero as it goes, so the next layer starts clean;
779
+ // a translucent one keeps it for its edge, which is drawn outside the
780
+ // polygon only — laid over the fill's own colour it would be a darker
781
+ // rim round every translucent polygon — and clears it after.
782
+ const translucent = paint.color[3] < 1;
783
+ gl.colorMask(true, true, true, true);
784
+ gl.stencilFunc(gl.NOTEQUAL, 0, 0xff);
785
+ if (translucent) gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
786
+ else gl.stencilOp(gl.ZERO, gl.ZERO, gl.ZERO);
787
+ gl.scissor(box[0], box[1], box[2], box[3]);
788
+ this._drawCover(paint.color);
789
+ stats.drawCalls++;
790
+
791
+ // 3. The edge, which is the fill stream drawn as segments.
792
+ if (translucent) {
793
+ if (paint.antialias) {
794
+ // Where the winding is zero and no edge has been yet, each pixel
795
+ // once, at what a pixel the polygon partly covers is covered by:
796
+ // a segment of no width, whose ramp is half a pixel either side.
797
+ gl.stencilFunc(gl.EQUAL, 0, 0xff);
798
+ gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR);
799
+ this._segments(
800
+ tiles,
801
+ index,
802
+ scissors,
803
+ { color: paint.color, half: 0, dash: null },
804
+ true,
805
+ 0,
806
+ );
807
+ }
808
+ this._clearStencil(box);
809
+ gl.disable(gl.STENCIL_TEST);
810
+ } else {
811
+ gl.disable(gl.STENCIL_TEST);
812
+ if (paint.antialias) {
813
+ this._strokeWith(
814
+ tiles,
815
+ index,
816
+ scissors,
817
+ { color: paint.color, half: 0.5, dash: null },
818
+ true,
819
+ );
820
+ }
821
+ }
822
+ if (paint.outline) {
823
+ this._strokeWith(
824
+ tiles,
825
+ index,
826
+ scissors,
827
+ { color: paint.outline, half: paint.outlineHalf, dash: null },
828
+ true,
829
+ );
830
+ }
831
+ return true;
832
+ }
833
+
834
+ private _lines(
835
+ frame: RenderFrame,
836
+ tiles: readonly RenderTile[],
837
+ index: number,
838
+ layer: Extract<MapStyleLayer, { type: 'line' }>,
839
+ scissors: (number[] | null)[],
840
+ gate = 1,
841
+ ): boolean {
842
+ const zoom = frame.zoom;
843
+ const logical = resolveZoomed(layer.width, zoom);
844
+ if (!(logical > 0)) return false;
845
+ const opacity =
846
+ (layer.opacity === undefined ? 1 : resolveZoomed(layer.opacity, zoom)) *
847
+ gate;
848
+ if (opacity <= 0) return false;
849
+ const base = this._color(resolveZoomed(layer.color, zoom));
850
+ if (!base) return false;
851
+ if (!this._unionOf(tiles, index, scissors)) return false;
852
+ // A road narrower than a pixel is drawn *at* a pixel, as paint.ts does:
853
+ // a motorway network that vanishes at zoom 6 is worse than a heavy one.
854
+ const width = Math.max(1, logical * frame.scale);
855
+ this._strokeWith(
856
+ tiles,
857
+ index,
858
+ scissors,
859
+ {
860
+ color: premultiplied(base, opacity),
861
+ half: width / 2,
862
+ dash: dashPattern(layer.dash, frame.scale),
863
+ },
864
+ false,
865
+ );
866
+ return true;
867
+ }
868
+
869
+ /**
870
+ * A circle layer: a disc per point, one instanced draw per tile — the
871
+ * radius, colours and stroke `paint.ts` reads, at this zoom.
872
+ */
873
+ private _circles(
874
+ frame: RenderFrame,
875
+ tiles: readonly RenderTile[],
876
+ index: number,
877
+ layer: Extract<MapStyleLayer, { type: 'circle' }>,
878
+ scissors: (number[] | null)[],
879
+ gate = 1,
880
+ ): boolean {
881
+ const zoom = frame.zoom;
882
+ const radius =
883
+ (layer.radius === undefined ? 3 : resolveZoomed(layer.radius, zoom)) *
884
+ frame.scale;
885
+ if (!(radius > 0)) return false;
886
+ const opacity =
887
+ (layer.opacity === undefined ? 1 : resolveZoomed(layer.opacity, zoom)) *
888
+ gate;
889
+ if (opacity <= 0) return false;
890
+ const base = this._color(resolveZoomed(layer.color, zoom));
891
+ if (!base) return false;
892
+ if (!this._unionOf(tiles, index, scissors)) return false;
893
+ const ring =
894
+ layer.strokeColor === undefined
895
+ ? null
896
+ : this._color(resolveZoomed(layer.strokeColor, zoom));
897
+ const stroke = ring
898
+ ? (layer.strokeWidth === undefined
899
+ ? 1
900
+ : resolveZoomed(layer.strokeWidth, zoom)) * frame.scale
901
+ : 0;
902
+ const gl = this._gl;
903
+ const program = (this._circle ??= linkProgram(
904
+ gl,
905
+ CIRCLE_VERTEX,
906
+ CIRCLE_FRAGMENT,
907
+ [
908
+ 'u_tile',
909
+ 'u_viewport',
910
+ 'u_radius',
911
+ 'u_stroke',
912
+ 'u_color',
913
+ 'u_stroke_color',
914
+ ],
915
+ ));
916
+ const u = program.uniforms;
917
+ const color = premultiplied(base, opacity);
918
+ const edge = ring ? premultiplied(ring, opacity) : CLEAR;
919
+ gl.useProgram(program.program);
920
+ gl.uniform2f(u.u_viewport, frame.width, frame.height);
921
+ gl.uniform1f(u.u_radius, radius);
922
+ gl.uniform1f(u.u_stroke, stroke);
923
+ gl.uniform4f(u.u_color, color[0], color[1], color[2], color[3]);
924
+ gl.uniform4f(u.u_stroke_color, edge[0], edge[1], edge[2], edge[3]);
925
+ this._eachTile(tiles, index, scissors, program, false, (count) => {
926
+ gl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, count);
927
+ });
928
+ return true;
929
+ }
930
+
931
+ /**
932
+ * A raster source's tiles: each one's image over its square, clipped to
933
+ * the part of the view it draws. Past the source's depth the square is
934
+ * the data tile's, larger than the view's cells, so the image is drawn
935
+ * whole and stretched — the retained renderer's rule since #98.
936
+ */
937
+ private _rasters(
938
+ tiles: readonly RenderTile[],
939
+ scissors: (number[] | null)[],
940
+ width: number,
941
+ height: number,
942
+ ): void {
943
+ const gl = this._gl;
944
+ const program = (this._raster ??= linkProgram(
945
+ gl,
946
+ RASTER_VERTEX,
947
+ RASTER_FRAGMENT,
948
+ ['u_tile', 'u_viewport', 'u_image'],
949
+ ));
950
+ const u = program.uniforms;
951
+ gl.useProgram(program.program);
952
+ gl.uniform2f(u.u_viewport, width, height);
953
+ gl.uniform1i(u.u_image, 0);
954
+ gl.activeTexture(gl.TEXTURE0);
955
+ gl.bindVertexArray(this._coverVao);
956
+ gl.disable(gl.STENCIL_TEST);
957
+ gl.enable(gl.BLEND);
958
+ gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
959
+ for (let t = 0; t < tiles.length; t++) {
960
+ const tile = tiles[t];
961
+ const s = scissors[t];
962
+ const texture = this._tiles.get(tile.data)?.texture;
963
+ if (!s || !texture) continue;
964
+ gl.scissor(s[0], s[1], s[2], s[3]);
965
+ gl.bindTexture(gl.TEXTURE_2D, texture);
966
+ gl.uniform3f(u.u_tile, tile.size, tile.x, tile.y);
967
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
968
+ this._stats.drawCalls++;
969
+ }
970
+ }
971
+
972
+ /**
973
+ * A stroke of one layer's ranges — of the line stream, or of the fill
974
+ * stream as a polygon's outline. A translucent one is drawn each pixel
975
+ * once: two capsules overlap at every join, and colour laid down twice
976
+ * there is a darker bead at every vertex. So the pixels a capsule covers
977
+ * wholly go first, under the stencil, the first capsule to reach one
978
+ * taking it; then the antialiased fringe, where nothing has been yet.
979
+ */
980
+ private _strokeWith(
981
+ tiles: readonly RenderTile[],
982
+ index: number,
983
+ scissors: (number[] | null)[],
984
+ paint: StrokePaint,
985
+ fillStream: boolean,
986
+ ): void {
987
+ if (paint.color[3] >= 1) {
988
+ this._segments(tiles, index, scissors, paint, fillStream, 0);
989
+ return;
990
+ }
991
+ const box = this._unionOf(tiles, index, scissors);
992
+ if (!box) return;
993
+ const gl = this._gl;
994
+ gl.enable(gl.STENCIL_TEST);
995
+ gl.stencilFunc(gl.EQUAL, 0, 0xff);
996
+ gl.stencilOp(gl.KEEP, gl.KEEP, gl.INCR);
997
+ this._segments(tiles, index, scissors, paint, fillStream, 1);
998
+ this._segments(tiles, index, scissors, paint, fillStream, 2);
999
+ this._clearStencil(box);
1000
+ gl.disable(gl.STENCIL_TEST);
1001
+ }
1002
+
1003
+ /** One layer's ranges as capsules, over every tile that has them. `part`
1004
+ * 1 draws only the pixels a capsule covers wholly, 2 only the rest, and
1005
+ * 0 both. */
1006
+ private _segments(
1007
+ tiles: readonly RenderTile[],
1008
+ index: number,
1009
+ scissors: (number[] | null)[],
1010
+ paint: StrokePaint,
1011
+ fillStream: boolean,
1012
+ part: number,
1013
+ ): void {
1014
+ const gl = this._gl;
1015
+ const u = this._line.uniforms;
1016
+ const { color, dash } = paint;
1017
+ gl.useProgram(this._line.program);
1018
+ gl.uniform4f(u.u_color, color[0], color[1], color[2], color[3]);
1019
+ gl.uniform1f(u.u_half, paint.half);
1020
+ // Unused entries are zero, and a dash of no length draws nothing.
1021
+ const d = (i: number): number => dash?.[i] ?? 0;
1022
+ gl.uniform4f(u.u_dash_a, d(0), d(1), d(2), d(3));
1023
+ gl.uniform4f(u.u_dash_b, d(4), d(5), d(6), d(7));
1024
+ gl.uniform1f(u.u_period, dash ? dash.reduce((sum, v) => sum + v, 0) : 0);
1025
+ gl.uniform1f(u.u_part, part);
1026
+ this._eachTile(tiles, index, scissors, this._line, fillStream, (count) => {
1027
+ gl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, count);
1028
+ });
1029
+ }
1030
+
1031
+ /** The union of the squares a layer draws in, as a scissor box; null
1032
+ * when no tile draws it. */
1033
+ private _unionOf(
1034
+ tiles: readonly RenderTile[],
1035
+ index: number,
1036
+ scissors: (number[] | null)[],
1037
+ ): number[] | null {
1038
+ let x0 = Infinity;
1039
+ let y0 = Infinity;
1040
+ let x1 = -Infinity;
1041
+ let y1 = -Infinity;
1042
+ for (let t = 0; t < tiles.length; t++) {
1043
+ const s = scissors[t];
1044
+ if (!s || !tiles[t].data.draws[index]) continue;
1045
+ if (s[0] < x0) x0 = s[0];
1046
+ if (s[1] < y0) y0 = s[1];
1047
+ if (s[0] + s[2] > x1) x1 = s[0] + s[2];
1048
+ if (s[1] + s[3] > y1) y1 = s[1] + s[3];
1049
+ }
1050
+ return x0 === Infinity ? null : [x0, y0, x1 - x0, y1 - y0];
1051
+ }
1052
+
1053
+ /** The stencil back to zero over a box, the colour untouched. */
1054
+ private _clearStencil(box: number[]): void {
1055
+ const gl = this._gl;
1056
+ gl.colorMask(false, false, false, false);
1057
+ gl.stencilFunc(gl.ALWAYS, 0, 0xff);
1058
+ gl.stencilOp(gl.ZERO, gl.ZERO, gl.ZERO);
1059
+ gl.scissor(box[0], box[1], box[2], box[3]);
1060
+ this._drawCover(CLEAR);
1061
+ this._stats.drawCalls++;
1062
+ gl.colorMask(true, true, true, true);
1063
+ }
1064
+
1065
+ // --- overlays ---------------------------------------------------------------
1066
+
1067
+ /**
1068
+ * The overlays, over the labels — the retained renderer's order — from
1069
+ * their bucket, placed this frame, pass by pass: a fill as a style's fill
1070
+ * layer is drawn, a stroke as its line layer is. Answers how many passes
1071
+ * it drew.
1072
+ */
1073
+ private _overlays(draw: OverlayDraw, frame: RenderFrame): number {
1074
+ const gl = this._gl;
1075
+ const { width, height, scale } = frame;
1076
+ this._gpu(draw.data);
1077
+ const clip = { x: 0, y: 0, width, height };
1078
+ const tiles: RenderTile[] = [
1079
+ {
1080
+ data: draw.data,
1081
+ x: draw.x,
1082
+ y: draw.y,
1083
+ size: draw.unit * TILE_EXTENT,
1084
+ clip,
1085
+ },
1086
+ ];
1087
+ const scissors = [scissorOf(clip, width, height)];
1088
+ gl.viewport(0, 0, width, height);
1089
+ gl.enable(gl.SCISSOR_TEST);
1090
+ gl.disable(gl.STENCIL_TEST);
1091
+ gl.enable(gl.BLEND);
1092
+ gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
1093
+ for (const program of [this._line, this._fan]) {
1094
+ gl.useProgram(program.program);
1095
+ gl.uniform2f(program.uniforms.u_viewport, width, height);
1096
+ }
1097
+ let drawn = 0;
1098
+ for (const pass of draw.passes) {
1099
+ const base = this._color(pass.color);
1100
+ if (!base) continue;
1101
+ const color = premultiplied(base, pass.opacity);
1102
+ if (pass.kind === 'fill') {
1103
+ this._fillWith(tiles, pass.index, scissors, {
1104
+ color,
1105
+ antialias: this._options.antialias,
1106
+ outline: null,
1107
+ outlineHalf: 0,
1108
+ });
1109
+ } else {
1110
+ this._strokeWith(
1111
+ tiles,
1112
+ pass.index,
1113
+ scissors,
1114
+ {
1115
+ color,
1116
+ half: (pass.width * scale) / 2,
1117
+ dash: dashPattern(pass.dash, scale),
1118
+ },
1119
+ pass.outline === true,
1120
+ );
1121
+ }
1122
+ drawn++;
1123
+ }
1124
+ gl.disable(gl.SCISSOR_TEST);
1125
+ return drawn;
1126
+ }
1127
+
1128
+ /**
1129
+ * Every tile's ranges for one layer: scissor, place, draw. The loop the
1130
+ * whole frame is made of, so it allocates nothing and sets only what
1131
+ * changes between tiles.
1132
+ */
1133
+ private _eachTile(
1134
+ tiles: readonly RenderTile[],
1135
+ index: number,
1136
+ scissors: (number[] | null)[],
1137
+ program: Program,
1138
+ fillStream: boolean,
1139
+ draw: (instances: number) => void,
1140
+ ): void {
1141
+ const gl = this._gl;
1142
+ const stats = this._stats;
1143
+ for (let t = 0; t < tiles.length; t++) {
1144
+ const tile = tiles[t];
1145
+ const layerDraw = tile.data.draws[index];
1146
+ const s = scissors[t];
1147
+ if (!layerDraw || !s) continue;
1148
+ const gpu = this._tiles.get(tile.data);
1149
+ if (!gpu) continue;
1150
+ gl.scissor(s[0], s[1], s[2], s[3]);
1151
+ gl.uniform3f(
1152
+ program.uniforms.u_tile,
1153
+ tile.size / TILE_EXTENT,
1154
+ tile.x,
1155
+ tile.y,
1156
+ );
1157
+ // A fill layer's edges come from the fill stream too; which stream a
1158
+ // range indexes is the draw's kind, not the program's.
1159
+ const stream = fillStream || layerDraw.kind === 'fill';
1160
+ const ranges = layerDraw.ranges;
1161
+ for (let r = 0; r < ranges.length; r += 2) {
1162
+ const instances = ranges[r + 1] - 1;
1163
+ if (instances <= 0) continue;
1164
+ gl.bindVertexArray(this._vao(gpu, stream, ranges[r]));
1165
+ draw(instances);
1166
+ stats.drawCalls++;
1167
+ stats.instances += instances;
1168
+ }
1169
+ }
1170
+ }
1171
+
1172
+ // --- labels -----------------------------------------------------------------
1173
+
1174
+ /**
1175
+ * The attribution, over whatever is bound: its box — the cover program,
1176
+ * scissored to it — and then its text, one more label quad.
1177
+ */
1178
+ private _attribution(
1179
+ draw: AttributionDraw,
1180
+ width: number,
1181
+ height: number,
1182
+ ): void {
1183
+ const gl = this._gl;
1184
+ const box = scissorOf(draw.box, width, height);
1185
+ if (box) {
1186
+ gl.enable(gl.SCISSOR_TEST);
1187
+ gl.scissor(box[0], box[1], box[2], box[3]);
1188
+ gl.disable(gl.STENCIL_TEST);
1189
+ gl.enable(gl.BLEND);
1190
+ gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
1191
+ this._drawCover(draw.boxColor);
1192
+ this._stats.drawCalls++;
1193
+ gl.disable(gl.SCISSOR_TEST);
1194
+ }
1195
+ this._labels(draw.text, width, height);
1196
+ }
1197
+
1198
+ /**
1199
+ * A batch of labels, over whatever is bound: one instanced draw of the
1200
+ * batch's quads, after the atlas texture has what the batch draws.
1201
+ * Answers how many it drew.
1202
+ */
1203
+ private _labels(batch: LabelBatch, width: number, height: number): number {
1204
+ const gl = this._gl;
1205
+ const atlas = batch.atlas;
1206
+ const program = (this._label ??= linkProgram(
1207
+ gl,
1208
+ LABEL_VERTEX,
1209
+ LABEL_FRAGMENT,
1210
+ ['u_viewport', 'u_atlas', 'u_inset', 'u_spread', 'u_image'],
1211
+ ));
1212
+ gl.activeTexture(gl.TEXTURE0);
1213
+ this._uploadAtlas(atlas);
1214
+ if (batch.count === 0) return 0;
1215
+
1216
+ if (!this._labelVao) {
1217
+ this._labelBuffer = gl.createBuffer();
1218
+ this._labelVao = gl.createVertexArray();
1219
+ gl.bindVertexArray(this._labelVao);
1220
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._corners);
1221
+ gl.enableVertexAttribArray(ATTRIBUTES.a_corner);
1222
+ gl.vertexAttribPointer(ATTRIBUTES.a_corner, 2, gl.FLOAT, false, 0, 0);
1223
+ gl.vertexAttribDivisor(ATTRIBUTES.a_corner, 0);
1224
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._labelBuffer);
1225
+ const stride = LABEL_INSTANCE * 4;
1226
+ const fields: [number, number, number][] = [
1227
+ [ATTRIBUTES.a_anchor, 4, 0],
1228
+ [ATTRIBUTES.a_rect, 4, 16],
1229
+ [ATTRIBUTES.a_ink, 4, 32],
1230
+ [ATTRIBUTES.a_halo, 4, 48],
1231
+ [ATTRIBUTES.a_params, 3, 64],
1232
+ ];
1233
+ for (const [location, size, offset] of fields) {
1234
+ gl.enableVertexAttribArray(location);
1235
+ gl.vertexAttribPointer(location, size, gl.FLOAT, false, stride, offset);
1236
+ gl.vertexAttribDivisor(location, 1);
1237
+ }
1238
+ }
1239
+ gl.bindVertexArray(this._labelVao);
1240
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._labelBuffer);
1241
+ gl.bufferData(
1242
+ gl.ARRAY_BUFFER,
1243
+ batch.instances.subarray(0, batch.count * LABEL_INSTANCE),
1244
+ gl.STREAM_DRAW,
1245
+ );
1246
+
1247
+ gl.viewport(0, 0, width, height);
1248
+ gl.disable(gl.SCISSOR_TEST);
1249
+ gl.disable(gl.STENCIL_TEST);
1250
+ gl.enable(gl.BLEND);
1251
+ gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
1252
+ gl.useProgram(program.program);
1253
+ const u = program.uniforms;
1254
+ gl.uniform2f(u.u_viewport, width, height);
1255
+ gl.uniform2f(u.u_atlas, atlas.size, atlas.size);
1256
+ gl.uniform1f(u.u_inset, QUAD_INSET);
1257
+ gl.uniform1f(u.u_spread, atlas.pad);
1258
+ gl.uniform1i(u.u_image, 0);
1259
+ gl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, batch.count);
1260
+ this._stats.drawCalls++;
1261
+ return batch.count;
1262
+ }
1263
+
1264
+ // --- markers ----------------------------------------------------------------
1265
+
1266
+ /**
1267
+ * The markers, over whatever is bound: one instanced draw of a quad each,
1268
+ * every pixel shaded by its distance to the marker's outline. Answers how
1269
+ * many it drew.
1270
+ */
1271
+ private _markers(batch: MarkerBatch, width: number, height: number): number {
1272
+ if (batch.count === 0) return 0;
1273
+ const gl = this._gl;
1274
+ const program = (this._marker ??= linkProgram(
1275
+ gl,
1276
+ MARKER_VERTEX,
1277
+ MARKER_FRAGMENT,
1278
+ ['u_viewport'],
1279
+ ));
1280
+ if (!this._markerVao) {
1281
+ this._markerBuffer = gl.createBuffer();
1282
+ this._markerVao = gl.createVertexArray();
1283
+ gl.bindVertexArray(this._markerVao);
1284
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._corners);
1285
+ gl.enableVertexAttribArray(ATTRIBUTES.a_corner);
1286
+ gl.vertexAttribPointer(ATTRIBUTES.a_corner, 2, gl.FLOAT, false, 0, 0);
1287
+ gl.vertexAttribDivisor(ATTRIBUTES.a_corner, 0);
1288
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._markerBuffer);
1289
+ const stride = MARKER_INSTANCE * 4;
1290
+ const fields: [number, number, number][] = [
1291
+ [ATTRIBUTES.a_anchor, 4, 0],
1292
+ [ATTRIBUTES.a_ink, 4, 16],
1293
+ [ATTRIBUTES.a_halo, 4, 32],
1294
+ [ATTRIBUTES.a_params, 2, 48],
1295
+ ];
1296
+ for (const [location, size, offset] of fields) {
1297
+ gl.enableVertexAttribArray(location);
1298
+ gl.vertexAttribPointer(location, size, gl.FLOAT, false, stride, offset);
1299
+ gl.vertexAttribDivisor(location, 1);
1300
+ }
1301
+ }
1302
+ gl.bindVertexArray(this._markerVao);
1303
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._markerBuffer);
1304
+ gl.bufferData(
1305
+ gl.ARRAY_BUFFER,
1306
+ batch.instances.subarray(0, batch.count * MARKER_INSTANCE),
1307
+ gl.STREAM_DRAW,
1308
+ );
1309
+ gl.viewport(0, 0, width, height);
1310
+ gl.disable(gl.SCISSOR_TEST);
1311
+ gl.disable(gl.STENCIL_TEST);
1312
+ gl.enable(gl.BLEND);
1313
+ gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
1314
+ gl.useProgram(program.program);
1315
+ gl.uniform2f(program.uniforms.u_viewport, width, height);
1316
+ gl.drawArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, batch.count);
1317
+ this._stats.drawCalls++;
1318
+ return batch.count;
1319
+ }
1320
+
1321
+ /**
1322
+ * Take what the atlas has waiting into the texture — a few dozen fields
1323
+ * a frame at most, so a batch landing is spread over frames rather than
1324
+ * paid in one. A texture that is new, or another atlas's, starts over:
1325
+ * the atlas queues everything again. Leaves the texture bound.
1326
+ *
1327
+ * Each field goes up in the alpha channel, the only one the shader
1328
+ * reads; the texture stays RGBA because the one-channel formats are not
1329
+ * in the core profile Apple's GL is.
1330
+ */
1331
+ private _uploadAtlas(atlas: LabelAtlas): void {
1332
+ const gl = this._gl;
1333
+ if (!this._atlasTexture || this._atlasOwner !== atlas) {
1334
+ if (this._atlasTexture) gl.deleteTexture(this._atlasTexture);
1335
+ this._atlasTexture = gl.createTexture();
1336
+ gl.bindTexture(gl.TEXTURE_2D, this._atlasTexture);
1337
+ // Left undefined: every texel a label samples is one its own upload
1338
+ // wrote, margin included.
1339
+ gl.texImage2D(
1340
+ gl.TEXTURE_2D,
1341
+ 0,
1342
+ gl.RGBA,
1343
+ atlas.size,
1344
+ atlas.size,
1345
+ 0,
1346
+ gl.RGBA,
1347
+ gl.UNSIGNED_BYTE,
1348
+ null,
1349
+ );
1350
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
1351
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
1352
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
1353
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
1354
+ this._atlasOwner = atlas;
1355
+ atlas.restart();
1356
+ } else {
1357
+ gl.bindTexture(gl.TEXTURE_2D, this._atlasTexture);
1358
+ }
1359
+ for (const entry of atlas.takeUploads(ATLAS_UPLOADS_PER_FRAME)) {
1360
+ // A field is one byte a texel; the texture is RGBA, because the
1361
+ // one-channel formats are not in a core profile, so it goes up in
1362
+ // alpha — the one channel the shader reads.
1363
+ const texels = entry.width * entry.height;
1364
+ if (this._fieldScratch.length < texels * 4) {
1365
+ this._fieldScratch = new Uint8Array(texels * 4).fill(255);
1366
+ }
1367
+ const rgba = this._fieldScratch.subarray(0, texels * 4);
1368
+ const field = entry.pixels;
1369
+ for (let i = 0; i < texels; i++) rgba[i * 4 + 3] = field[i];
1370
+ gl.texSubImage2D(
1371
+ gl.TEXTURE_2D,
1372
+ 0,
1373
+ entry.x,
1374
+ entry.y,
1375
+ entry.width,
1376
+ entry.height,
1377
+ gl.RGBA,
1378
+ gl.UNSIGNED_BYTE,
1379
+ rgba,
1380
+ );
1381
+ entry.ready = true;
1382
+ this._stats.uploadBytes += rgba.length;
1383
+ }
1384
+ }
1385
+
1386
+ // --- resources ------------------------------------------------------------
1387
+
1388
+ private _color(value: string): Rgba | null {
1389
+ let color = this._colors.get(value);
1390
+ if (color === undefined) {
1391
+ color = parseColor(value);
1392
+ this._colors.set(value, color);
1393
+ }
1394
+ return color;
1395
+ }
1396
+
1397
+ private _gpu(data: GlTileData): TileGpu {
1398
+ let gpu = this._tiles.get(data);
1399
+ if (gpu) return gpu;
1400
+ const gl = this._gl;
1401
+ gpu = { line: null, fill: null, vaos: new Map(), texture: null, bytes: 0 };
1402
+ if (data.lineRecords > 1) {
1403
+ gpu.line = gl.createBuffer();
1404
+ gl.bindBuffer(gl.ARRAY_BUFFER, gpu.line);
1405
+ gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(data.line), gl.STATIC_DRAW);
1406
+ gpu.bytes += data.line.byteLength;
1407
+ }
1408
+ if (data.fillRecords > 1) {
1409
+ gpu.fill = gl.createBuffer();
1410
+ gl.bindBuffer(gl.ARRAY_BUFFER, gpu.fill);
1411
+ gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(data.fill), gl.STATIC_DRAW);
1412
+ gpu.bytes += data.fill.byteLength;
1413
+ }
1414
+ if (data.raster) {
1415
+ const { width, height, pixels } = data.raster;
1416
+ gpu.texture = gl.createTexture();
1417
+ gl.bindTexture(gl.TEXTURE_2D, gpu.texture);
1418
+ gl.texImage2D(
1419
+ gl.TEXTURE_2D,
1420
+ 0,
1421
+ gl.RGBA,
1422
+ width,
1423
+ height,
1424
+ 0,
1425
+ gl.RGBA,
1426
+ gl.UNSIGNED_BYTE,
1427
+ pixels,
1428
+ );
1429
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
1430
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
1431
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
1432
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
1433
+ gpu.bytes += width * height * 4;
1434
+ }
1435
+ this._tiles.set(data, gpu);
1436
+ this.gpuBytes += gpu.bytes;
1437
+ this._stats.uploads++;
1438
+ this._stats.uploadBytes += gpu.bytes;
1439
+ return gpu;
1440
+ }
1441
+
1442
+ /**
1443
+ * The vertex array for a range: the corner quad per vertex, and records
1444
+ * `first`, `first + 1` (and the third attribute) per instance.
1445
+ *
1446
+ * One per range because instanced attributes ignore `first` — GL 4.1 and
1447
+ * ES 3.0 have no base instance — so the range's start is baked into the
1448
+ * attribute offsets instead. A tile has one range per style layer it
1449
+ * draws, so this is two dozen small objects per tile, made once.
1450
+ */
1451
+ private _vao(gpu: TileGpu, fill: boolean, first: number): unknown {
1452
+ const key = first * 2 + (fill ? 1 : 0);
1453
+ let vao = gpu.vaos.get(key);
1454
+ if (vao) return vao;
1455
+ const gl = this._gl;
1456
+ vao = gl.createVertexArray();
1457
+ gl.bindVertexArray(vao);
1458
+ gl.bindBuffer(gl.ARRAY_BUFFER, this._corners);
1459
+ gl.enableVertexAttribArray(ATTRIBUTES.a_corner);
1460
+ gl.vertexAttribPointer(ATTRIBUTES.a_corner, 2, gl.FLOAT, false, 0, 0);
1461
+ gl.vertexAttribDivisor(ATTRIBUTES.a_corner, 0);
1462
+ gl.bindBuffer(gl.ARRAY_BUFFER, fill ? gpu.fill : gpu.line);
1463
+ const base = first * RECORD_BYTES;
1464
+ const S = RECORD_BYTES;
1465
+ gl.enableVertexAttribArray(ATTRIBUTES.a_p0);
1466
+ gl.vertexAttribPointer(ATTRIBUTES.a_p0, 2, gl.SHORT, false, S, base);
1467
+ gl.vertexAttribDivisor(ATTRIBUTES.a_p0, 1);
1468
+ gl.enableVertexAttribArray(ATTRIBUTES.a_p1);
1469
+ gl.vertexAttribPointer(ATTRIBUTES.a_p1, 2, gl.SHORT, false, S, base + S);
1470
+ gl.vertexAttribDivisor(ATTRIBUTES.a_p1, 1);
1471
+ gl.enableVertexAttribArray(ATTRIBUTES.a_x);
1472
+ if (fill) {
1473
+ gl.vertexAttribPointer(ATTRIBUTES.a_x, 2, gl.SHORT, false, S, base + 4);
1474
+ } else {
1475
+ gl.vertexAttribPointer(ATTRIBUTES.a_x, 1, gl.FLOAT, false, S, base + 4);
1476
+ }
1477
+ gl.vertexAttribDivisor(ATTRIBUTES.a_x, 1);
1478
+ gpu.vaos.set(key, vao);
1479
+ return vao;
1480
+ }
1481
+
1482
+ private _drawCover(color: Rgba): void {
1483
+ const gl = this._gl;
1484
+ gl.useProgram(this._cover.program);
1485
+ gl.uniform4f(
1486
+ this._cover.uniforms.u_color,
1487
+ color[0],
1488
+ color[1],
1489
+ color[2],
1490
+ color[3],
1491
+ );
1492
+ gl.bindVertexArray(this._coverVao);
1493
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
1494
+ }
1495
+
1496
+ /**
1497
+ * Does the bound framebuffer have a stencil buffer? Asked by drawing
1498
+ * rather than by querying, because the query differs between the default
1499
+ * framebuffer of a core profile (where `STENCIL_BITS` is gone) and a
1500
+ * user one, and the Cocoa surface is the second while XQuartz's is the
1501
+ * first. Two pixels, one read-back, on the first frame only.
1502
+ *
1503
+ * Both halves are needed, and the first version had only the second: **a
1504
+ * framebuffer with no stencil buffer passes every stencil test**, so "write
1505
+ * 1, then draw where it is 1" draws either way and cannot tell the two
1506
+ * apart. XQuartz's surface — which has none — answered yes, the frame went
1507
+ * straight to it, and every fill's cover pass painted the whole view in
1508
+ * that layer's colour. So pixel 0 asks the question that only a real
1509
+ * buffer answers "no" to: a draw where a *cleared* stencil is not zero.
1510
+ */
1511
+ private _probeStencil(): boolean {
1512
+ const gl = this._gl;
1513
+ const previousVao = gl.getParameter(VERTEX_ARRAY_BINDING);
1514
+ gl.disable(gl.BLEND);
1515
+ gl.enable(gl.SCISSOR_TEST);
1516
+ gl.scissor(0, 0, 2, 1);
1517
+ gl.clearColor(0, 0, 0, 0);
1518
+ gl.clearStencil(0);
1519
+ gl.stencilMask(0xff);
1520
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
1521
+ gl.enable(gl.STENCIL_TEST);
1522
+ // Pixel 0: stencil is 0 everywhere, so NOTEQUAL 0 must draw nothing.
1523
+ gl.scissor(0, 0, 1, 1);
1524
+ gl.stencilFunc(gl.NOTEQUAL, 0, 0xff);
1525
+ gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
1526
+ this._drawCover([1, 0, 1, 1]);
1527
+ // Pixel 1: write 1, then draw where it is 1 — which must draw.
1528
+ gl.scissor(1, 0, 1, 1);
1529
+ gl.stencilFunc(gl.ALWAYS, 1, 0xff);
1530
+ gl.stencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE);
1531
+ gl.colorMask(false, false, false, false);
1532
+ this._drawCover([0, 0, 0, 0]);
1533
+ gl.colorMask(true, true, true, true);
1534
+ gl.stencilFunc(gl.EQUAL, 1, 0xff);
1535
+ gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
1536
+ this._drawCover([1, 0, 1, 1]);
1537
+ gl.disable(gl.STENCIL_TEST);
1538
+ const pixels = new Uint8Array(8);
1539
+ gl.readPixels(0, 0, 2, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
1540
+ gl.disable(gl.SCISSOR_TEST);
1541
+ gl.bindVertexArray(previousVao);
1542
+ const magenta = (at: number) =>
1543
+ pixels[at] > 200 && pixels[at + 1] < 60 && pixels[at + 2] > 200;
1544
+ return !magenta(0) && magenta(4);
1545
+ }
1546
+
1547
+ /** An offscreen colour target with its own stencil, made or remade at
1548
+ * this size. Leaves its framebuffer bound. */
1549
+ private _target(role: string, width: number, height: number): Offscreen {
1550
+ const gl = this._gl;
1551
+ const existing = this._targets.get(role);
1552
+ if (existing && existing.width === width && existing.height === height) {
1553
+ return existing;
1554
+ }
1555
+ if (existing) this._dropTarget(existing);
1556
+ const texture = gl.createTexture();
1557
+ gl.bindTexture(gl.TEXTURE_2D, texture);
1558
+ gl.texImage2D(
1559
+ gl.TEXTURE_2D,
1560
+ 0,
1561
+ gl.RGBA,
1562
+ width,
1563
+ height,
1564
+ 0,
1565
+ gl.RGBA,
1566
+ gl.UNSIGNED_BYTE,
1567
+ null,
1568
+ );
1569
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
1570
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
1571
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
1572
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
1573
+ const depth = gl.createRenderbuffer();
1574
+ gl.bindRenderbuffer(gl.RENDERBUFFER, depth);
1575
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH24_STENCIL8, width, height);
1576
+ const fbo = gl.createFramebuffer();
1577
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
1578
+ gl.framebufferTexture2D(
1579
+ gl.FRAMEBUFFER,
1580
+ gl.COLOR_ATTACHMENT0,
1581
+ gl.TEXTURE_2D,
1582
+ texture,
1583
+ 0,
1584
+ );
1585
+ gl.framebufferRenderbuffer(
1586
+ gl.FRAMEBUFFER,
1587
+ gl.DEPTH_ATTACHMENT,
1588
+ gl.RENDERBUFFER,
1589
+ depth,
1590
+ );
1591
+ gl.framebufferRenderbuffer(
1592
+ gl.FRAMEBUFFER,
1593
+ gl.STENCIL_ATTACHMENT,
1594
+ gl.RENDERBUFFER,
1595
+ depth,
1596
+ );
1597
+ const target = { fbo, texture, depth, width, height };
1598
+ this._targets.set(role, target);
1599
+ return target;
1600
+ }
1601
+
1602
+ /** An offscreen target onto the bound framebuffer: a copy at `alpha` 1,
1603
+ * a cross-fade below it. */
1604
+ private _composite(
1605
+ target: Offscreen,
1606
+ width: number,
1607
+ height: number,
1608
+ alpha: number,
1609
+ ): void {
1610
+ const gl = this._gl;
1611
+ this._blit ??= linkProgram(gl, BLIT_VERTEX, BLIT_FRAGMENT, [
1612
+ 'u_image',
1613
+ 'u_alpha',
1614
+ ]);
1615
+ gl.viewport(0, 0, width, height);
1616
+ gl.disable(gl.SCISSOR_TEST);
1617
+ gl.disable(gl.STENCIL_TEST);
1618
+ if (alpha >= 1) gl.disable(gl.BLEND);
1619
+ else {
1620
+ gl.enable(gl.BLEND);
1621
+ gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
1622
+ }
1623
+ gl.useProgram(this._blit.program);
1624
+ gl.activeTexture(gl.TEXTURE0);
1625
+ gl.bindTexture(gl.TEXTURE_2D, target.texture);
1626
+ gl.uniform1i(this._blit.uniforms.u_image, 0);
1627
+ gl.uniform1f(this._blit.uniforms.u_alpha, alpha);
1628
+ gl.bindVertexArray(this._coverVao);
1629
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
1630
+ this._stats.drawCalls++;
1631
+ }
1632
+
1633
+ private _dropTarget(target: Offscreen): void {
1634
+ const gl = this._gl;
1635
+ gl.deleteFramebuffer(target.fbo);
1636
+ gl.deleteRenderbuffer(target.depth);
1637
+ gl.deleteTexture(target.texture);
1638
+ }
1639
+ }
1640
+
1641
+ /**
1642
+ * A clip rectangle as a GL scissor box: whole pixels, bottom-left origin,
1643
+ * clamped to the viewport, or null when nothing of it is visible.
1644
+ *
1645
+ * Rounded edge by edge rather than origin-and-size, so two tiles that share
1646
+ * an edge round it to the same pixel column and neither a gap nor a double
1647
+ * row can open between them at any fractional zoom.
1648
+ */
1649
+ export function scissorOf(
1650
+ clip: DeviceRect,
1651
+ width: number,
1652
+ height: number,
1653
+ ): number[] | null {
1654
+ const x0 = Math.max(0, Math.round(clip.x));
1655
+ const x1 = Math.min(width, Math.round(clip.x + clip.width));
1656
+ const y0 = Math.max(0, Math.round(clip.y));
1657
+ const y1 = Math.min(height, Math.round(clip.y + clip.height));
1658
+ if (x1 <= x0 || y1 <= y0) return null;
1659
+ return [x0, height - y1, x1 - x0, y1 - y0];
1660
+ }