@lightningjs/renderer 0.7.5 → 0.8.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 (252) hide show
  1. package/LICENSE +202 -202
  2. package/NOTICE +3 -3
  3. package/README.md +233 -221
  4. package/dist/src/common/CommonTypes.d.ts +12 -0
  5. package/dist/src/core/CoreNode.d.ts +83 -9
  6. package/dist/src/core/CoreNode.js +232 -44
  7. package/dist/src/core/CoreNode.js.map +1 -1
  8. package/dist/src/core/CoreTextNode.d.ts +6 -1
  9. package/dist/src/core/CoreTextNode.js +33 -20
  10. package/dist/src/core/CoreTextNode.js.map +1 -1
  11. package/dist/src/core/CoreTextureManager.d.ts +3 -1
  12. package/dist/src/core/CoreTextureManager.js +11 -2
  13. package/dist/src/core/CoreTextureManager.js.map +1 -1
  14. package/dist/src/core/Stage.d.ts +6 -0
  15. package/dist/src/core/Stage.js +16 -1
  16. package/dist/src/core/Stage.js.map +1 -1
  17. package/dist/src/core/TextureMemoryManager.d.ts +12 -0
  18. package/dist/src/core/TextureMemoryManager.js +42 -0
  19. package/dist/src/core/TextureMemoryManager.js.map +1 -0
  20. package/dist/src/core/lib/ImageWorker.d.ts +0 -1
  21. package/dist/src/core/lib/ImageWorker.js +55 -40
  22. package/dist/src/core/lib/ImageWorker.js.map +1 -1
  23. package/dist/src/core/lib/RenderCoords.d.ts +13 -0
  24. package/dist/src/core/lib/RenderCoords.js +63 -0
  25. package/dist/src/core/lib/RenderCoords.js.map +1 -0
  26. package/dist/src/core/lib/WebGlContext.d.ts +414 -0
  27. package/dist/src/core/lib/WebGlContext.js +640 -0
  28. package/dist/src/core/lib/WebGlContext.js.map +1 -0
  29. package/dist/src/core/lib/utils.d.ts +1 -0
  30. package/dist/src/core/lib/utils.js +6 -0
  31. package/dist/src/core/lib/utils.js.map +1 -1
  32. package/dist/src/core/platform.js +8 -0
  33. package/dist/src/core/platform.js.map +1 -1
  34. package/dist/src/core/renderers/CoreContextTexture.d.ts +5 -1
  35. package/dist/src/core/renderers/CoreContextTexture.js +3 -1
  36. package/dist/src/core/renderers/CoreContextTexture.js.map +1 -1
  37. package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.d.ts +2 -1
  38. package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js +2 -2
  39. package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js.map +1 -1
  40. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.d.ts +3 -1
  41. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +26 -6
  42. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -1
  43. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.d.ts +3 -0
  44. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +4 -2
  45. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -1
  46. package/dist/src/core/renderers/webgl/internal/RendererUtils.d.ts +9 -0
  47. package/dist/src/core/renderers/webgl/internal/RendererUtils.js +14 -0
  48. package/dist/src/core/renderers/webgl/internal/RendererUtils.js.map +1 -1
  49. package/dist/src/core/renderers/webgl/shaders/DefaultShader.js +47 -47
  50. package/dist/src/core/renderers/webgl/shaders/DefaultShaderBatched.js +61 -61
  51. package/dist/src/core/renderers/webgl/shaders/DynamicShader.js +93 -93
  52. package/dist/src/core/renderers/webgl/shaders/RoundedRectangle.js +63 -63
  53. package/dist/src/core/renderers/webgl/shaders/SdfShader.js +49 -49
  54. package/dist/src/core/renderers/webgl/shaders/effects/BorderBottomEffect.js +15 -15
  55. package/dist/src/core/renderers/webgl/shaders/effects/BorderEffect.js +5 -5
  56. package/dist/src/core/renderers/webgl/shaders/effects/BorderLeftEffect.js +15 -15
  57. package/dist/src/core/renderers/webgl/shaders/effects/BorderRightEffect.js +15 -15
  58. package/dist/src/core/renderers/webgl/shaders/effects/BorderTopEffect.js +15 -15
  59. package/dist/src/core/renderers/webgl/shaders/effects/FadeOutEffect.js +42 -42
  60. package/dist/src/core/renderers/webgl/shaders/effects/GlitchEffect.js +44 -44
  61. package/dist/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.js +3 -3
  62. package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js +44 -57
  63. package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js.map +1 -1
  64. package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.d.ts +1 -0
  65. package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js +33 -39
  66. package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js.map +1 -1
  67. package/dist/src/core/renderers/webgl/shaders/effects/RadialProgressEffect.js +37 -37
  68. package/dist/src/core/renderers/webgl/shaders/effects/RadiusEffect.js +19 -19
  69. package/dist/src/core/scene/Scene.d.ts +59 -0
  70. package/dist/src/core/scene/Scene.js +106 -0
  71. package/dist/src/core/scene/Scene.js.map +1 -0
  72. package/dist/src/core/text-rendering/TrFontManager.js +30 -25
  73. package/dist/src/core/text-rendering/TrFontManager.js.map +1 -1
  74. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.d.ts +2 -0
  75. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js +26 -2
  76. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +1 -1
  77. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +8 -0
  78. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +34 -6
  79. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
  80. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/findNearestMultiple.d.ts +8 -0
  81. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/findNearestMultiple.js +29 -0
  82. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/findNearestMultiple.js.map +1 -0
  83. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js +1 -3
  84. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js.map +1 -1
  85. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/SdfBufferHelper.d.ts +19 -0
  86. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/SdfBufferHelper.js +84 -0
  87. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/SdfBufferHelper.js.map +1 -0
  88. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutLine.d.ts +8 -0
  89. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutLine.js +40 -0
  90. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutLine.js.map +1 -0
  91. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutText2.d.ts +2 -0
  92. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutText2.js +41 -0
  93. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutText2.js.map +1 -0
  94. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/utils.d.ts +1 -0
  95. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/utils.js +4 -0
  96. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/utils.js.map +1 -0
  97. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2.d.ts +1 -0
  98. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2.js +2 -0
  99. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2.js.map +1 -0
  100. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/makeRenderWindow.d.ts +20 -0
  101. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/makeRenderWindow.js +55 -0
  102. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/makeRenderWindow.js.map +1 -0
  103. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/roundUpToMultiple.d.ts +9 -0
  104. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/roundUpToMultiple.js +32 -0
  105. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/roundUpToMultiple.js.map +1 -0
  106. package/dist/src/core/text-rendering/renderers/TextRenderer.d.ts +31 -0
  107. package/dist/src/core/text-rendering/renderers/TextRenderer.js +26 -0
  108. package/dist/src/core/text-rendering/renderers/TextRenderer.js.map +1 -1
  109. package/dist/src/core/textures/ImageTexture.js +16 -2
  110. package/dist/src/core/textures/ImageTexture.js.map +1 -1
  111. package/dist/src/core/textures/Texture.d.ts +27 -2
  112. package/dist/src/core/textures/Texture.js +30 -1
  113. package/dist/src/core/textures/Texture.js.map +1 -1
  114. package/dist/src/core/utils.d.ts +1 -1
  115. package/dist/src/main-api/ICoreDriver.d.ts +1 -0
  116. package/dist/src/main-api/Inspector.js +2 -1
  117. package/dist/src/main-api/Inspector.js.map +1 -1
  118. package/dist/src/main-api/RendererMain.d.ts +15 -0
  119. package/dist/src/main-api/RendererMain.js +7 -1
  120. package/dist/src/main-api/RendererMain.js.map +1 -1
  121. package/dist/src/render-drivers/main/MainCoreDriver.d.ts +1 -0
  122. package/dist/src/render-drivers/main/MainCoreDriver.js +8 -0
  123. package/dist/src/render-drivers/main/MainCoreDriver.js.map +1 -1
  124. package/dist/src/render-drivers/main/MainOnlyNode.d.ts +5 -0
  125. package/dist/src/render-drivers/main/MainOnlyNode.js +26 -0
  126. package/dist/src/render-drivers/main/MainOnlyNode.js.map +1 -1
  127. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js +2 -0
  128. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js.map +1 -1
  129. package/dist/src/render-drivers/threadx/ThreadXRendererMessage.d.ts +2 -0
  130. package/dist/src/render-drivers/threadx/ThreadXRendererMessage.js.map +1 -1
  131. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js +3 -0
  132. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js.map +1 -1
  133. package/dist/src/render-drivers/threadx/worker/renderer.js +2 -0
  134. package/dist/src/render-drivers/threadx/worker/renderer.js.map +1 -1
  135. package/dist/src/utils.d.ts +6 -0
  136. package/dist/src/utils.js +9 -1
  137. package/dist/src/utils.js.map +1 -1
  138. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  139. package/exports/core-api.ts +102 -102
  140. package/exports/main-api.ts +60 -60
  141. package/exports/utils.ts +41 -41
  142. package/package.json +1 -1
  143. package/scripts/please-use-pnpm.js +13 -13
  144. package/src/common/CommonTypes.ts +132 -113
  145. package/src/common/EventEmitter.ts +77 -77
  146. package/src/common/IAnimationController.ts +29 -29
  147. package/src/core/CoreExtension.ts +32 -32
  148. package/src/core/CoreNode.ts +1199 -955
  149. package/src/core/CoreShaderManager.ts +243 -243
  150. package/src/core/CoreTextNode.ts +400 -391
  151. package/src/core/CoreTextureManager.ts +339 -326
  152. package/src/core/Stage.ts +375 -354
  153. package/src/core/TextureMemoryManager.ts +66 -0
  154. package/src/core/animations/AnimationManager.ts +38 -38
  155. package/src/core/animations/CoreAnimation.ts +181 -181
  156. package/src/core/animations/CoreAnimationController.ts +148 -148
  157. package/src/core/lib/ContextSpy.ts +41 -41
  158. package/src/core/lib/ImageWorker.ts +149 -135
  159. package/src/core/lib/Matrix3d.ts +290 -290
  160. package/src/core/lib/RenderCoords.ts +86 -0
  161. package/src/core/lib/WebGlContextWrapper.ts +992 -992
  162. package/src/core/lib/textureCompression.ts +152 -152
  163. package/src/core/lib/utils.ts +250 -241
  164. package/src/core/platform.ts +54 -46
  165. package/src/core/renderers/CoreContextTexture.ts +35 -30
  166. package/src/core/renderers/CoreRenderOp.ts +22 -22
  167. package/src/core/renderers/CoreRenderer.ts +63 -63
  168. package/src/core/renderers/CoreShader.ts +41 -41
  169. package/src/core/renderers/webgl/WebGlCoreCtxSubTexture.ts +42 -37
  170. package/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +261 -230
  171. package/src/core/renderers/webgl/WebGlCoreRenderOp.ts +107 -107
  172. package/src/core/renderers/webgl/WebGlCoreRenderer.ts +528 -520
  173. package/src/core/renderers/webgl/WebGlCoreShader.ts +337 -337
  174. package/src/core/renderers/webgl/internal/BufferCollection.ts +54 -54
  175. package/src/core/renderers/webgl/internal/RendererUtils.ts +148 -131
  176. package/src/core/renderers/webgl/internal/ShaderUtils.ts +136 -136
  177. package/src/core/renderers/webgl/internal/WebGlUtils.ts +35 -35
  178. package/src/core/renderers/webgl/shaders/DefaultShader.ts +95 -95
  179. package/src/core/renderers/webgl/shaders/DefaultShaderBatched.ts +132 -132
  180. package/src/core/renderers/webgl/shaders/DynamicShader.ts +474 -474
  181. package/src/core/renderers/webgl/shaders/RoundedRectangle.ts +161 -161
  182. package/src/core/renderers/webgl/shaders/SdfShader.ts +174 -174
  183. package/src/core/renderers/webgl/shaders/effects/BorderBottomEffect.ts +101 -101
  184. package/src/core/renderers/webgl/shaders/effects/BorderEffect.ts +86 -86
  185. package/src/core/renderers/webgl/shaders/effects/BorderLeftEffect.ts +101 -101
  186. package/src/core/renderers/webgl/shaders/effects/BorderRightEffect.ts +101 -101
  187. package/src/core/renderers/webgl/shaders/effects/BorderTopEffect.ts +101 -101
  188. package/src/core/renderers/webgl/shaders/effects/EffectUtils.ts +33 -33
  189. package/src/core/renderers/webgl/shaders/effects/FadeOutEffect.ts +135 -135
  190. package/src/core/renderers/webgl/shaders/effects/GlitchEffect.ts +145 -145
  191. package/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.ts +67 -67
  192. package/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.ts +160 -176
  193. package/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.ts +153 -159
  194. package/src/core/renderers/webgl/shaders/effects/RadialProgressEffect.ts +186 -186
  195. package/src/core/renderers/webgl/shaders/effects/RadiusEffect.ts +121 -121
  196. package/src/core/renderers/webgl/shaders/effects/ShaderEffect.ts +114 -114
  197. package/src/core/text-rendering/TextTextureRendererUtils.ts +189 -189
  198. package/src/core/text-rendering/TrFontManager.ts +170 -166
  199. package/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.ts +141 -141
  200. package/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/FontShaper.ts +139 -139
  201. package/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.test.ts +173 -173
  202. package/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.ts +169 -169
  203. package/src/core/text-rendering/font-face-types/TrFontFace.ts +105 -105
  204. package/src/core/text-rendering/font-face-types/WebTrFontFace.ts +77 -77
  205. package/src/core/text-rendering/renderers/CanvasTextRenderer.ts +780 -751
  206. package/src/core/text-rendering/renderers/LightningTextTextureRenderer.ts +741 -741
  207. package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +812 -778
  208. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/PeekableGenerator.test.ts +48 -48
  209. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/PeekableGenerator.ts +66 -66
  210. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/SpecialCodepoints.ts +52 -52
  211. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/constants.ts +32 -32
  212. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/getStartConditions.ts +84 -84
  213. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/getUnicodeCodepoints.test.ts +133 -133
  214. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/getUnicodeCodepoints.ts +38 -38
  215. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.ts +391 -393
  216. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/measureText.test.ts +49 -49
  217. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/measureText.ts +51 -51
  218. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.test.ts +205 -205
  219. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.ts +93 -93
  220. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/util.ts +40 -40
  221. package/src/core/text-rendering/renderers/TextRenderer.ts +548 -504
  222. package/src/core/textures/ColorTexture.ts +86 -86
  223. package/src/core/textures/ImageTexture.ts +154 -140
  224. package/src/core/textures/NoiseTexture.ts +96 -96
  225. package/src/core/textures/SubTexture.ts +143 -143
  226. package/src/core/textures/Texture.ts +261 -218
  227. package/src/core/utils.ts +224 -224
  228. package/src/env.d.ts +7 -7
  229. package/src/main-api/ICoreDriver.ts +68 -66
  230. package/src/main-api/INode.ts +499 -499
  231. package/src/main-api/Inspector.ts +440 -439
  232. package/src/main-api/RendererMain.ts +676 -652
  233. package/src/main-api/texture-usage-trackers/FinalizationRegistryTextureUsageTracker.ts +45 -45
  234. package/src/main-api/texture-usage-trackers/ManualCountTextureUsageTracker.ts +154 -154
  235. package/src/main-api/texture-usage-trackers/TextureUsageTracker.ts +54 -54
  236. package/src/render-drivers/main/MainCoreDriver.ts +158 -148
  237. package/src/render-drivers/main/MainOnlyNode.ts +500 -466
  238. package/src/render-drivers/main/MainOnlyTextNode.ts +261 -261
  239. package/src/render-drivers/threadx/NodeStruct.ts +300 -300
  240. package/src/render-drivers/threadx/SharedNode.ts +97 -97
  241. package/src/render-drivers/threadx/TextNodeStruct.ts +211 -211
  242. package/src/render-drivers/threadx/ThreadXCoreDriver.ts +287 -285
  243. package/src/render-drivers/threadx/ThreadXMainAnimationController.ts +99 -99
  244. package/src/render-drivers/threadx/ThreadXMainNode.ts +192 -192
  245. package/src/render-drivers/threadx/ThreadXMainTextNode.ts +85 -85
  246. package/src/render-drivers/threadx/ThreadXRendererMessage.ts +112 -110
  247. package/src/render-drivers/threadx/worker/ThreadXRendererNode.ts +245 -238
  248. package/src/render-drivers/threadx/worker/ThreadXRendererTextNode.ts +149 -149
  249. package/src/render-drivers/threadx/worker/renderer.ts +153 -151
  250. package/src/render-drivers/utils.ts +97 -97
  251. package/src/utils.ts +216 -207
  252. package/COPYING +0 -1
@@ -1,778 +1,812 @@
1
- /*
2
- * If not stated otherwise in this file or this component's LICENSE file the
3
- * following copyright and licenses apply:
4
- *
5
- * Copyright 2023 Comcast Cable Communications Management, LLC.
6
- *
7
- * Licensed under the Apache License, Version 2.0 (the License);
8
- * you may not use this file except in compliance with the License.
9
- * You may obtain a copy of the License at
10
- *
11
- * http://www.apache.org/licenses/LICENSE-2.0
12
- *
13
- * Unless required by applicable law or agreed to in writing, software
14
- * distributed under the License is distributed on an "AS IS" BASIS,
15
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- * See the License for the specific language governing permissions and
17
- * limitations under the License.
18
- */
19
-
20
- import {
21
- type Bound,
22
- type Rect,
23
- createBound,
24
- type BoundWithValid,
25
- intersectRect,
26
- type RectWithValid,
27
- copyRect,
28
- boundsOverlap,
29
- convertBoundToRect,
30
- } from '../../../lib/utils.js';
31
- import {
32
- TextRenderer,
33
- type TrProps,
34
- type TextRendererState,
35
- type TrFontProps,
36
- type TrPropSetters,
37
- } from '../TextRenderer.js';
38
- import { SdfTrFontFace } from '../../font-face-types/SdfTrFontFace/SdfTrFontFace.js';
39
- import { FLOATS_PER_GLYPH } from './internal/constants.js';
40
- import { getStartConditions } from './internal/getStartConditions.js';
41
- import { layoutText } from './internal/layoutText.js';
42
- import {
43
- setRenderWindow,
44
- type SdfRenderWindow,
45
- } from './internal/setRenderWindow.js';
46
- import type { TrFontFace } from '../../font-face-types/TrFontFace.js';
47
- import { TrFontManager, type FontFamilyMap } from '../../TrFontManager.js';
48
- import { assertTruthy, mergeColorAlpha } from '../../../../utils.js';
49
- import type { Stage } from '../../../Stage.js';
50
- import { WebGlCoreRenderOp } from '../../../renderers/webgl/WebGlCoreRenderOp.js';
51
- import { BufferCollection } from '../../../renderers/webgl/internal/BufferCollection.js';
52
- import type {
53
- SdfShader,
54
- SdfShaderProps,
55
- } from '../../../renderers/webgl/shaders/SdfShader.js';
56
- import type { WebGlCoreCtxTexture } from '../../../renderers/webgl/WebGlCoreCtxTexture.js';
57
- import { EventEmitter } from '../../../../common/EventEmitter.js';
58
- import type { Matrix3d } from '../../../lib/Matrix3d.js';
59
-
60
- declare module '../TextRenderer.js' {
61
- interface TextRendererMap {
62
- sdf: SdfTextRenderer;
63
- }
64
-
65
- // Add prefixed SDF-specific props to TextRendererDebugProps
66
- interface TextRendererDebugProps {
67
- sdfShaderDebug: boolean;
68
- }
69
- }
70
-
71
- export interface LineCacheItem {
72
- codepointIndex: number;
73
- maxY: number;
74
- maxX: number;
75
- }
76
-
77
- export interface SdfTextRendererState extends TextRendererState {
78
- /**
79
- * Cache for layout resume points indexed by the `curY` for each line
80
- * in the render sequence.
81
- *
82
- * Allows faster rendering by skipping parts of the layout loop that are
83
- * outside of the renderWindow.
84
- */
85
- lineCache: LineCacheItem[];
86
-
87
- renderWindow: SdfRenderWindow;
88
-
89
- elementBounds: BoundWithValid;
90
-
91
- clippingRect: RectWithValid;
92
-
93
- bufferNumFloats: number;
94
-
95
- bufferNumQuads: number;
96
-
97
- vertexBuffer: Float32Array | undefined;
98
-
99
- webGlBuffers: BufferCollection | null;
100
-
101
- bufferUploaded: boolean;
102
-
103
- distanceRange: number;
104
-
105
- trFontFace: SdfTrFontFace | undefined;
106
- }
107
-
108
- /**
109
- * Ephemeral rect object used for calculations
110
- */
111
- const tmpRect: Rect = {
112
- x: 0,
113
- y: 0,
114
- width: 0,
115
- height: 0,
116
- };
117
-
118
- /**
119
- * Singleton class for rendering text using signed distance fields.
120
- *
121
- * @remarks
122
- * SdfTextRenderer supports both single-channel and multi-channel signed distance fields.
123
- */
124
- export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
125
- /**
126
- * Map of font family names to a set of font faces.
127
- */
128
- private ssdfFontFamilies: FontFamilyMap = {};
129
- private msdfFontFamilies: FontFamilyMap = {};
130
- private fontFamilyArray: FontFamilyMap[] = [
131
- this.ssdfFontFamilies,
132
- this.msdfFontFamilies,
133
- ];
134
- private sdfShader: SdfShader;
135
- private rendererBounds: Bound;
136
-
137
- constructor(stage: Stage) {
138
- super(stage);
139
- this.sdfShader = this.stage.shManager.loadShader('SdfShader').shader;
140
- this.rendererBounds = {
141
- x1: 0,
142
- y1: 0,
143
- x2: this.stage.options.appWidth,
144
- y2: this.stage.options.appHeight,
145
- };
146
- }
147
-
148
- //#region Overrides
149
- getPropertySetters(): Partial<TrPropSetters<SdfTextRendererState>> {
150
- return {
151
- fontFamily: (state, value) => {
152
- state.props.fontFamily = value;
153
- state.trFontFace = undefined;
154
- this.invalidateLayoutCache(state);
155
- },
156
- fontWeight: (state, value) => {
157
- state.props.fontWeight = value;
158
- state.trFontFace = undefined;
159
- this.invalidateLayoutCache(state);
160
- },
161
- fontStyle: (state, value) => {
162
- state.props.fontStyle = value;
163
- state.trFontFace = undefined;
164
- this.invalidateLayoutCache(state);
165
- },
166
- fontStretch: (state, value) => {
167
- state.props.fontStretch = value;
168
- state.trFontFace = undefined;
169
- this.invalidateLayoutCache(state);
170
- },
171
- fontSize: (state, value) => {
172
- state.props.fontSize = value;
173
- this.invalidateLayoutCache(state);
174
- },
175
- text: (state, value) => {
176
- state.props.text = value;
177
- this.invalidateLayoutCache(state);
178
- },
179
- textAlign: (state, value) => {
180
- state.props.textAlign = value;
181
- this.invalidateLayoutCache(state);
182
- },
183
- color: (state, value) => {
184
- state.props.color = value;
185
- },
186
- x: (state, value) => {
187
- state.props.x = value;
188
- if (state.elementBounds.valid) {
189
- this.setElementBoundsX(state);
190
- // Only schedule an update if the text is not already rendered
191
- // (renderWindow is invalid) and the element possibly overlaps the screen
192
- // This is to avoid unnecessary updates when we know text is off-screen
193
- if (
194
- !state.renderWindow.valid &&
195
- boundsOverlap(state.elementBounds, this.rendererBounds)
196
- ) {
197
- this.scheduleUpdateState(state);
198
- }
199
- }
200
- },
201
- y: (state, value) => {
202
- state.props.y = value;
203
- if (state.elementBounds.valid) {
204
- this.setElementBoundsY(state);
205
- // See x() for explanation
206
- if (
207
- !state.renderWindow.valid &&
208
- boundsOverlap(state.elementBounds, this.rendererBounds)
209
- ) {
210
- this.scheduleUpdateState(state);
211
- }
212
- }
213
- },
214
- contain: (state, value) => {
215
- state.props.contain = value;
216
- this.invalidateLayoutCache(state);
217
- },
218
- width: (state, value) => {
219
- state.props.width = value;
220
- this.invalidateLayoutCache(state);
221
- },
222
- height: (state, value) => {
223
- state.props.height = value;
224
- this.invalidateLayoutCache(state);
225
- },
226
- offsetY: (state, value) => {
227
- state.props.offsetY = value;
228
- this.invalidateLayoutCache(state);
229
- },
230
- scrollable: (state, value) => {
231
- state.props.scrollable = value;
232
- this.invalidateLayoutCache(state);
233
- },
234
- scrollY: (state, value) => {
235
- state.props.scrollY = value;
236
- // Scrolling doesn't need to invalidate any caches, but it does need to
237
- // schedule an update
238
- this.scheduleUpdateState(state);
239
- },
240
- letterSpacing: (state, value) => {
241
- state.props.letterSpacing = value;
242
- this.invalidateLayoutCache(state);
243
- },
244
- lineHeight: (state, value) => {
245
- state.props.lineHeight = value;
246
- this.invalidateLayoutCache(state);
247
- },
248
- maxLines: (state, value) => {
249
- state.props.maxLines = value;
250
- this.invalidateLayoutCache(state);
251
- },
252
- textBaseline: (state, value) => {
253
- state.props.textBaseline = value;
254
- this.invalidateLayoutCache(state);
255
- },
256
- verticalAlign: (state, value) => {
257
- state.props.verticalAlign = value;
258
- this.invalidateLayoutCache(state);
259
- },
260
- overflowSuffix: (state, value) => {
261
- state.props.overflowSuffix = value;
262
- this.invalidateLayoutCache(state);
263
- },
264
- debug: (state, value) => {
265
- state.props.debug = value;
266
- },
267
- };
268
- }
269
-
270
- override canRenderFont(props: TrFontProps): boolean {
271
- // TODO: Support matching on font stretch, weight and style (if/when needed)
272
- // For now we just match on the font family name
273
- // '$$SDF_FAILURE_TEST$$' is used to test the 'failure' event coming from text
274
- const { fontFamily } = props;
275
- return (
276
- fontFamily in this.ssdfFontFamilies ||
277
- fontFamily in this.msdfFontFamilies ||
278
- fontFamily === '$$SDF_FAILURE_TEST$$'
279
- );
280
- }
281
-
282
- override isFontFaceSupported(fontFace: TrFontFace): boolean {
283
- return fontFace instanceof SdfTrFontFace;
284
- }
285
-
286
- override addFontFace(fontFace: TrFontFace): void {
287
- // Make sure the font face is an SDF font face (it should have already passed
288
- // the `isFontFaceSupported` check)
289
- assertTruthy(fontFace instanceof SdfTrFontFace);
290
- const familyName = fontFace.fontFamily;
291
- const fontFamiles =
292
- fontFace.type === 'ssdf'
293
- ? this.ssdfFontFamilies
294
- : fontFace.type === 'msdf'
295
- ? this.msdfFontFamilies
296
- : undefined;
297
- if (!fontFamiles) {
298
- console.warn(`Invalid font face type: ${fontFace.type as string}`);
299
- return;
300
- }
301
- let faceSet = fontFamiles[familyName];
302
- if (!faceSet) {
303
- faceSet = new Set();
304
- fontFamiles[familyName] = faceSet;
305
- }
306
- faceSet.add(fontFace);
307
- }
308
-
309
- override createState(props: TrProps): SdfTextRendererState {
310
- return {
311
- props,
312
- status: 'initialState',
313
- updateScheduled: false,
314
- emitter: new EventEmitter(),
315
- lineCache: [],
316
- forceFullLayoutCalc: false,
317
- renderWindow: {
318
- screen: {
319
- x1: 0,
320
- y1: 0,
321
- x2: 0,
322
- y2: 0,
323
- },
324
- sdf: {
325
- x1: 0,
326
- y1: 0,
327
- x2: 0,
328
- y2: 0,
329
- },
330
- firstLineIdx: 0,
331
- numLines: 0,
332
- valid: false,
333
- },
334
- elementBounds: {
335
- x1: 0,
336
- y1: 0,
337
- x2: 0,
338
- y2: 0,
339
- valid: false,
340
- },
341
- clippingRect: {
342
- x: 0,
343
- y: 0,
344
- width: 0,
345
- height: 0,
346
- valid: false,
347
- },
348
- bufferNumFloats: 0,
349
- bufferNumQuads: 0,
350
- vertexBuffer: undefined,
351
- webGlBuffers: null,
352
- bufferUploaded: false,
353
- textH: undefined,
354
- textW: undefined,
355
- distanceRange: 0,
356
- trFontFace: undefined,
357
- debugData: {
358
- updateCount: 0,
359
- layoutCount: 0,
360
- lastLayoutNumCharacters: 0,
361
- layoutSum: 0,
362
- drawSum: 0,
363
- drawCount: 0,
364
- bufferSize: 0,
365
- },
366
- };
367
- }
368
-
369
- override updateState(state: SdfTextRendererState): void {
370
- let { trFontFace } = state;
371
- const { textH, lineCache, debugData, forceFullLayoutCalc } = state;
372
- debugData.updateCount++;
373
-
374
- // On the first update call we need to set the status to loading
375
- if (state.status === 'initialState') {
376
- this.setStatus(state, 'loading');
377
- }
378
-
379
- // Resolve font face if we haven't yet
380
- if (!trFontFace) {
381
- trFontFace = this.resolveFontFace(state.props);
382
- state.trFontFace = trFontFace;
383
- if (!trFontFace) {
384
- const msg = `SdfTextRenderer: Could not resolve font face for family: '${state.props.fontFamily}'`;
385
- console.error(msg);
386
- this.setStatus(state, 'failed', new Error(msg));
387
- return;
388
- }
389
- }
390
-
391
- // If the font hasn't been loaded yet, stop here.
392
- // Listen for the 'loaded' event and forward fontLoaded event
393
- if (!trFontFace.loaded) {
394
- trFontFace.once('loaded', () => {
395
- this.scheduleUpdateState(state);
396
- });
397
- return;
398
- }
399
-
400
- // If the font is loaded then so should the data
401
- assertTruthy(trFontFace.data, 'Font face data should be loaded');
402
-
403
- const {
404
- text,
405
- fontSize,
406
- x,
407
- y,
408
- contain,
409
- width,
410
- height,
411
- lineHeight,
412
- verticalAlign,
413
- scrollable,
414
- overflowSuffix,
415
- maxLines,
416
- } = state.props;
417
-
418
- // scrollY only has an effect when contain === 'both' and scrollable === true
419
- const scrollY = contain === 'both' && scrollable ? state.props.scrollY : 0;
420
-
421
- const { renderWindow } = state;
422
-
423
- /**
424
- * The font size of the SDF font face (the basis for SDF space units)
425
- */
426
- const sdfFontSize = trFontFace.data.info.size;
427
-
428
- /**
429
- * Divide screen space units by this to get the SDF space units
430
- * Mulitple SDF space units by this to get screen space units
431
- */
432
- const fontSizeRatio = fontSize / sdfFontSize;
433
-
434
- // Needed in renderWindow calculation
435
- const sdfLineHeight = lineHeight / fontSizeRatio;
436
-
437
- state.distanceRange =
438
- fontSizeRatio * trFontFace.data.distanceField.distanceRange;
439
-
440
- // Allocate buffers if needed
441
- const neededLength = text.length * FLOATS_PER_GLYPH;
442
- let vertexBuffer = state.vertexBuffer;
443
- if (!vertexBuffer || vertexBuffer.length < neededLength) {
444
- vertexBuffer = new Float32Array(neededLength * 2);
445
- }
446
-
447
- const elementBounds = state.elementBounds;
448
- if (!elementBounds.valid) {
449
- this.setElementBoundsX(state);
450
- this.setElementBoundsY(state);
451
- elementBounds.valid = true;
452
- }
453
-
454
- // Return early if we're still viewing inside the established render window
455
- // No need to re-render what we've already rendered
456
- // (Only if there's an established renderWindow and we're not suppressing early exit)
457
- if (!forceFullLayoutCalc && renderWindow.valid) {
458
- const rwScreen = renderWindow.screen;
459
- if (
460
- x + rwScreen.x1 <= elementBounds.x1 &&
461
- x + rwScreen.x2 >= elementBounds.x2 &&
462
- y - scrollY + rwScreen.y1 <= elementBounds.y1 &&
463
- y - scrollY + rwScreen.y2 >= elementBounds.y2
464
- ) {
465
- this.setStatus(state, 'loaded');
466
- return;
467
- }
468
- // Otherwise invalidate the renderWindow so it can be redone
469
- renderWindow.valid = false;
470
- this.setStatus(state, 'loading');
471
- }
472
-
473
- const { offsetY, textAlign } = state.props;
474
-
475
- // Create a new renderWindow if needed
476
- if (!renderWindow.valid) {
477
- const isPossiblyOnScreen = boundsOverlap(
478
- elementBounds,
479
- this.rendererBounds,
480
- );
481
-
482
- if (!isPossiblyOnScreen) {
483
- // If the element is not possibly on screen, we can skip the layout and rendering completely
484
- return;
485
- }
486
-
487
- setRenderWindow(
488
- renderWindow,
489
- x,
490
- y,
491
- scrollY,
492
- lineHeight,
493
- contain === 'both' ? elementBounds.y2 - elementBounds.y1 : 0,
494
- elementBounds,
495
- fontSizeRatio,
496
- );
497
- // console.log('newRenderWindow', renderWindow);
498
- }
499
-
500
- const start = getStartConditions(
501
- sdfFontSize,
502
- sdfLineHeight,
503
- lineHeight,
504
- verticalAlign,
505
- offsetY,
506
- fontSizeRatio,
507
- renderWindow,
508
- lineCache,
509
- textH,
510
- );
511
-
512
- if (!start) {
513
- // Nothing to render, return early, but still mark as loaded (since the text is just scrolled
514
- // out of view)
515
- this.setStatus(state, 'loaded');
516
- return;
517
- }
518
-
519
- const { letterSpacing } = state.props;
520
-
521
- const out2 = layoutText(
522
- start.lineIndex,
523
- start.sdfX,
524
- start.sdfY,
525
- text,
526
- textAlign,
527
- width,
528
- height,
529
- fontSize,
530
- lineHeight,
531
- letterSpacing,
532
- vertexBuffer,
533
- contain,
534
- lineCache,
535
- renderWindow.sdf,
536
- trFontFace,
537
- forceFullLayoutCalc,
538
- scrollable,
539
- overflowSuffix,
540
- maxLines,
541
- );
542
-
543
- state.bufferUploaded = false;
544
- state.bufferNumFloats = out2.bufferNumFloats;
545
- state.bufferNumQuads = out2.bufferNumQuads;
546
- state.vertexBuffer = vertexBuffer;
547
- state.renderWindow = renderWindow;
548
- debugData.lastLayoutNumCharacters = out2.layoutNumCharacters;
549
- debugData.bufferSize = vertexBuffer.byteLength;
550
-
551
- // If we didn't exit early, we know we have completely computed w/h
552
- if (out2.fullyProcessed) {
553
- state.textW = out2.maxX * fontSizeRatio;
554
- state.textH = out2.maxY * fontSizeRatio;
555
- }
556
-
557
- // if (state.props.debug.printLayoutTime) {
558
- // debugData.layoutSum += performance.now() - updateStartTime;
559
- // debugData.layoutCount++;
560
- // }
561
- this.setStatus(state, 'loaded');
562
- }
563
-
564
- override renderQuads(
565
- state: SdfTextRendererState,
566
- transform: Matrix3d,
567
- clippingRect: Readonly<RectWithValid>,
568
- alpha: number,
569
- ): void {
570
- if (!state.vertexBuffer) {
571
- // Nothing to draw
572
- return;
573
- }
574
-
575
- const { renderer } = this.stage;
576
-
577
- const { fontSize, color, contain, scrollable, zIndex, debug } = state.props;
578
-
579
- // scrollY only has an effect when contain === 'both' and scrollable === true
580
- const scrollY = contain === 'both' && scrollable ? state.props.scrollY : 0;
581
-
582
- const {
583
- textW = 0,
584
- textH = 0,
585
- distanceRange,
586
- vertexBuffer,
587
- bufferUploaded,
588
- trFontFace,
589
- elementBounds,
590
- } = state;
591
-
592
- let { webGlBuffers } = state;
593
-
594
- if (!webGlBuffers) {
595
- const glw = renderer.glw;
596
- const stride = 4 * Float32Array.BYTES_PER_ELEMENT;
597
- const webGlBuffer = glw.createBuffer();
598
- assertTruthy(webGlBuffer);
599
- state.webGlBuffers = new BufferCollection([
600
- {
601
- buffer: webGlBuffer,
602
- attributes: {
603
- a_position: {
604
- name: 'a_position',
605
- size: 2, // 2 components per iteration
606
- type: glw.FLOAT, // the data is 32bit floats
607
- normalized: false, // don't normalize the data
608
- stride, // 0 = move forward size * sizeof(type) each iteration to get the next position
609
- offset: 0, // start at the beginning of the buffer
610
- },
611
- a_textureCoordinate: {
612
- name: 'a_textureCoordinate',
613
- size: 2,
614
- type: glw.FLOAT,
615
- normalized: false,
616
- stride,
617
- offset: 2 * Float32Array.BYTES_PER_ELEMENT,
618
- },
619
- },
620
- },
621
- ]);
622
- state.bufferUploaded = false;
623
- assertTruthy(state.webGlBuffers);
624
- webGlBuffers = state.webGlBuffers;
625
- }
626
-
627
- if (!bufferUploaded) {
628
- const glw = renderer.glw;
629
-
630
- const buffer = webGlBuffers?.getBuffer('a_textureCoordinate') ?? null;
631
- glw.arrayBufferData(buffer, vertexBuffer, glw.STATIC_DRAW);
632
- state.bufferUploaded = true;
633
- }
634
-
635
- assertTruthy(trFontFace);
636
- if (scrollable && contain === 'both') {
637
- assertTruthy(elementBounds.valid);
638
- const elementRect = convertBoundToRect(elementBounds, tmpRect);
639
-
640
- if (clippingRect.valid) {
641
- state.clippingRect.valid = true;
642
- clippingRect = intersectRect(
643
- clippingRect,
644
- elementRect,
645
- state.clippingRect,
646
- );
647
- } else {
648
- state.clippingRect.valid = true;
649
- clippingRect = copyRect(elementRect, state.clippingRect);
650
- }
651
- }
652
-
653
- const renderOp = new WebGlCoreRenderOp(
654
- renderer.glw,
655
- renderer.options,
656
- webGlBuffers,
657
- this.sdfShader,
658
- {
659
- transform: transform.data,
660
- // IMPORTANT: The SDF Shader expects the color NOT to be premultiplied
661
- // for the best blending results. Which is why we use `mergeColorAlpha`
662
- // instead of `mergeColorAlphaPremultiplied` here.
663
- color: mergeColorAlpha(color, alpha),
664
- size: fontSize / (trFontFace.data?.info.size || 0),
665
- scrollY,
666
- distanceRange,
667
- debug: debug.sdfShaderDebug,
668
- } satisfies SdfShaderProps,
669
- alpha,
670
- clippingRect,
671
- { height: textH, width: textW },
672
- 0,
673
- zIndex,
674
- );
675
-
676
- const texture = state.trFontFace?.texture;
677
- assertTruthy(texture);
678
- const ctxTexture = this.stage.txManager.getCtxTexture(texture);
679
-
680
- renderOp.addTexture(ctxTexture as WebGlCoreCtxTexture);
681
- renderOp.length = state.bufferNumFloats;
682
- renderOp.numQuads = state.bufferNumQuads;
683
-
684
- renderer.addRenderOp(renderOp);
685
-
686
- // if (!debug.disableScissor) {
687
- // renderer.enableScissor(
688
- // visibleRect.x,
689
- // visibleRect.y,
690
- // visibleRect.w,
691
- // visibleRect.h,
692
- // );
693
- // }
694
-
695
- // Draw the arrays
696
- // gl.drawArrays(
697
- // gl.TRIANGLES, // Primitive type
698
- // 0,
699
- // bufferNumVertices, // Number of verticies
700
- // );
701
-
702
- // renderer.disableScissor();
703
-
704
- // if (debug.showElementRect) {
705
- // this.renderer.drawBorder(
706
- // Colors.Blue,
707
- // elementRect.x,
708
- // elementRect.y,
709
- // elementRect.w,
710
- // elementRect.h,
711
- // );
712
- // }
713
-
714
- // if (debug.showVisibleRect) {
715
- // this.renderer.drawBorder(
716
- // Colors.Green,
717
- // visibleRect.x,
718
- // visibleRect.y,
719
- // visibleRect.w,
720
- // visibleRect.h,
721
- // );
722
- // }
723
-
724
- // if (debug.showRenderWindow && renderWindow) {
725
- // this.renderer.drawBorder(
726
- // Colors.Red,
727
- // x + renderWindow.x1,
728
- // y + renderWindow.y1 - scrollY,
729
- // x + renderWindow.x2 - (x + renderWindow.x1),
730
- // y + renderWindow.y2 - scrollY - (y + renderWindow.y1 - scrollY),
731
- // );
732
- // }
733
- // if (debug.printLayoutTime) {
734
- // debugData.drawSum += performance.now() - drawStartTime;
735
- // debugData.drawCount++;
736
- // }
737
- }
738
- //#endregion Overrides
739
-
740
- public resolveFontFace(props: TrFontProps): SdfTrFontFace | undefined {
741
- return TrFontManager.resolveFontFace(this.fontFamilyArray, props) as
742
- | SdfTrFontFace
743
- | undefined;
744
- }
745
-
746
- /**
747
- * Invalidate the layout cache stored in the state. This will cause the text
748
- * to be re-layed out on the next update.
749
- *
750
- * @remarks
751
- * This also invalidates the visible window cache.
752
- *
753
- * @param state
754
- */
755
- protected invalidateLayoutCache(state: SdfTextRendererState): void {
756
- state.renderWindow.valid = false;
757
- state.elementBounds.valid = false;
758
- state.textH = undefined;
759
- state.textW = undefined;
760
- state.lineCache = [];
761
- this.setStatus(state, 'loading');
762
- this.scheduleUpdateState(state);
763
- }
764
-
765
- protected setElementBoundsX(state: SdfTextRendererState): void {
766
- const { x, contain, width } = state.props;
767
- const { elementBounds } = state;
768
- elementBounds.x1 = x;
769
- elementBounds.x2 = contain !== 'none' ? x + width : Infinity;
770
- }
771
-
772
- protected setElementBoundsY(state: SdfTextRendererState): void {
773
- const { y, contain, height } = state.props;
774
- const { elementBounds } = state;
775
- elementBounds.y1 = y;
776
- elementBounds.y2 = contain === 'both' ? y + height : Infinity;
777
- }
778
- }
1
+ /*
2
+ * If not stated otherwise in this file or this component's LICENSE file the
3
+ * following copyright and licenses apply:
4
+ *
5
+ * Copyright 2023 Comcast Cable Communications Management, LLC.
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the License);
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+
20
+ import {
21
+ type Bound,
22
+ type Rect,
23
+ createBound,
24
+ type BoundWithValid,
25
+ intersectRect,
26
+ type RectWithValid,
27
+ copyRect,
28
+ boundsOverlap,
29
+ convertBoundToRect,
30
+ } from '../../../lib/utils.js';
31
+ import {
32
+ TextRenderer,
33
+ type TrProps,
34
+ type TextRendererState,
35
+ type TrFontProps,
36
+ type TrPropSetters,
37
+ } from '../TextRenderer.js';
38
+ import { SdfTrFontFace } from '../../font-face-types/SdfTrFontFace/SdfTrFontFace.js';
39
+ import { FLOATS_PER_GLYPH } from './internal/constants.js';
40
+ import { getStartConditions } from './internal/getStartConditions.js';
41
+ import { layoutText } from './internal/layoutText.js';
42
+ import {
43
+ setRenderWindow,
44
+ type SdfRenderWindow,
45
+ } from './internal/setRenderWindow.js';
46
+ import type { TrFontFace } from '../../font-face-types/TrFontFace.js';
47
+ import { TrFontManager, type FontFamilyMap } from '../../TrFontManager.js';
48
+ import { assertTruthy, mergeColorAlpha } from '../../../../utils.js';
49
+ import type { Stage } from '../../../Stage.js';
50
+ import { WebGlCoreRenderOp } from '../../../renderers/webgl/WebGlCoreRenderOp.js';
51
+ import { BufferCollection } from '../../../renderers/webgl/internal/BufferCollection.js';
52
+ import type {
53
+ SdfShader,
54
+ SdfShaderProps,
55
+ } from '../../../renderers/webgl/shaders/SdfShader.js';
56
+ import type { WebGlCoreCtxTexture } from '../../../renderers/webgl/WebGlCoreCtxTexture.js';
57
+ import { EventEmitter } from '../../../../common/EventEmitter.js';
58
+ import type { Matrix3d } from '../../../lib/Matrix3d.js';
59
+
60
+ declare module '../TextRenderer.js' {
61
+ interface TextRendererMap {
62
+ sdf: SdfTextRenderer;
63
+ }
64
+
65
+ // Add prefixed SDF-specific props to TextRendererDebugProps
66
+ interface TextRendererDebugProps {
67
+ sdfShaderDebug: boolean;
68
+ }
69
+ }
70
+
71
+ export interface LineCacheItem {
72
+ codepointIndex: number;
73
+ maxY: number;
74
+ maxX: number;
75
+ }
76
+
77
+ export interface SdfTextRendererState extends TextRendererState {
78
+ /**
79
+ * Cache for layout resume points indexed by the `curY` for each line
80
+ * in the render sequence.
81
+ *
82
+ * Allows faster rendering by skipping parts of the layout loop that are
83
+ * outside of the renderWindow.
84
+ */
85
+ lineCache: LineCacheItem[];
86
+
87
+ renderWindow: SdfRenderWindow;
88
+
89
+ elementBounds: BoundWithValid;
90
+
91
+ clippingRect: RectWithValid;
92
+
93
+ bufferNumFloats: number;
94
+
95
+ bufferNumQuads: number;
96
+
97
+ vertexBuffer: Float32Array | undefined;
98
+
99
+ webGlBuffers: BufferCollection | null;
100
+
101
+ bufferUploaded: boolean;
102
+
103
+ distanceRange: number;
104
+
105
+ trFontFace: SdfTrFontFace | undefined;
106
+ }
107
+
108
+ /**
109
+ * Ephemeral rect object used for calculations
110
+ */
111
+ const tmpRect: Rect = {
112
+ x: 0,
113
+ y: 0,
114
+ width: 0,
115
+ height: 0,
116
+ };
117
+
118
+ /**
119
+ * Singleton class for rendering text using signed distance fields.
120
+ *
121
+ * @remarks
122
+ * SdfTextRenderer supports both single-channel and multi-channel signed distance fields.
123
+ */
124
+ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
125
+ /**
126
+ * Map of font family names to a set of font faces.
127
+ */
128
+ private ssdfFontFamilies: FontFamilyMap = {};
129
+ private msdfFontFamilies: FontFamilyMap = {};
130
+ private fontFamilyArray: FontFamilyMap[] = [
131
+ this.ssdfFontFamilies,
132
+ this.msdfFontFamilies,
133
+ ];
134
+ private sdfShader: SdfShader;
135
+ private rendererBounds: Bound;
136
+
137
+ constructor(stage: Stage) {
138
+ super(stage);
139
+ this.sdfShader = this.stage.shManager.loadShader('SdfShader').shader;
140
+ this.rendererBounds = {
141
+ x1: 0,
142
+ y1: 0,
143
+ x2: this.stage.options.appWidth,
144
+ y2: this.stage.options.appHeight,
145
+ };
146
+ }
147
+
148
+ //#region Overrides
149
+ getPropertySetters(): Partial<TrPropSetters<SdfTextRendererState>> {
150
+ return {
151
+ fontFamily: (state, value) => {
152
+ state.props.fontFamily = value;
153
+ this.releaseFontFace(state);
154
+ this.invalidateLayoutCache(state);
155
+ },
156
+ fontWeight: (state, value) => {
157
+ state.props.fontWeight = value;
158
+ this.releaseFontFace(state);
159
+ this.invalidateLayoutCache(state);
160
+ },
161
+ fontStyle: (state, value) => {
162
+ state.props.fontStyle = value;
163
+ this.releaseFontFace(state);
164
+ this.invalidateLayoutCache(state);
165
+ },
166
+ fontStretch: (state, value) => {
167
+ state.props.fontStretch = value;
168
+ this.releaseFontFace(state);
169
+ this.invalidateLayoutCache(state);
170
+ },
171
+ fontSize: (state, value) => {
172
+ state.props.fontSize = value;
173
+ this.invalidateLayoutCache(state);
174
+ },
175
+ text: (state, value) => {
176
+ state.props.text = value;
177
+ this.invalidateLayoutCache(state);
178
+ },
179
+ textAlign: (state, value) => {
180
+ state.props.textAlign = value;
181
+ this.invalidateLayoutCache(state);
182
+ },
183
+ color: (state, value) => {
184
+ state.props.color = value;
185
+ },
186
+ x: (state, value) => {
187
+ state.props.x = value;
188
+ if (state.elementBounds.valid) {
189
+ this.setElementBoundsX(state);
190
+ // Only schedule an update if the text is not already rendered
191
+ // (renderWindow is invalid) and the element possibly overlaps the screen
192
+ // This is to avoid unnecessary updates when we know text is off-screen
193
+ if (
194
+ !state.renderWindow.valid &&
195
+ boundsOverlap(state.elementBounds, this.rendererBounds)
196
+ ) {
197
+ this.scheduleUpdateState(state);
198
+ }
199
+ }
200
+ },
201
+ y: (state, value) => {
202
+ state.props.y = value;
203
+ if (state.elementBounds.valid) {
204
+ this.setElementBoundsY(state);
205
+ // See x() for explanation
206
+ if (
207
+ !state.renderWindow.valid &&
208
+ boundsOverlap(state.elementBounds, this.rendererBounds)
209
+ ) {
210
+ this.scheduleUpdateState(state);
211
+ }
212
+ }
213
+ },
214
+ contain: (state, value) => {
215
+ state.props.contain = value;
216
+ this.invalidateLayoutCache(state);
217
+ },
218
+ width: (state, value) => {
219
+ state.props.width = value;
220
+ // Only invalidate layout cache if we're containing in the horizontal direction
221
+ if (state.props.contain !== 'none') {
222
+ this.invalidateLayoutCache(state);
223
+ }
224
+ },
225
+ height: (state, value) => {
226
+ state.props.height = value;
227
+ // Only invalidate layout cache if we're containing in the vertical direction
228
+ if (state.props.contain === 'both') {
229
+ this.invalidateLayoutCache(state);
230
+ }
231
+ },
232
+ offsetY: (state, value) => {
233
+ state.props.offsetY = value;
234
+ this.invalidateLayoutCache(state);
235
+ },
236
+ scrollable: (state, value) => {
237
+ state.props.scrollable = value;
238
+ this.invalidateLayoutCache(state);
239
+ },
240
+ scrollY: (state, value) => {
241
+ state.props.scrollY = value;
242
+ // Scrolling doesn't need to invalidate any caches, but it does need to
243
+ // schedule an update
244
+ this.scheduleUpdateState(state);
245
+ },
246
+ letterSpacing: (state, value) => {
247
+ state.props.letterSpacing = value;
248
+ this.invalidateLayoutCache(state);
249
+ },
250
+ lineHeight: (state, value) => {
251
+ state.props.lineHeight = value;
252
+ this.invalidateLayoutCache(state);
253
+ },
254
+ maxLines: (state, value) => {
255
+ state.props.maxLines = value;
256
+ this.invalidateLayoutCache(state);
257
+ },
258
+ textBaseline: (state, value) => {
259
+ state.props.textBaseline = value;
260
+ this.invalidateLayoutCache(state);
261
+ },
262
+ verticalAlign: (state, value) => {
263
+ state.props.verticalAlign = value;
264
+ this.invalidateLayoutCache(state);
265
+ },
266
+ overflowSuffix: (state, value) => {
267
+ state.props.overflowSuffix = value;
268
+ this.invalidateLayoutCache(state);
269
+ },
270
+ debug: (state, value) => {
271
+ state.props.debug = value;
272
+ },
273
+ };
274
+ }
275
+
276
+ override canRenderFont(props: TrFontProps): boolean {
277
+ // TODO: Support matching on font stretch, weight and style (if/when needed)
278
+ // For now we just match on the font family name
279
+ // '$$SDF_FAILURE_TEST$$' is used to test the 'failure' event coming from text
280
+ const { fontFamily } = props;
281
+ return (
282
+ fontFamily in this.ssdfFontFamilies ||
283
+ fontFamily in this.msdfFontFamilies ||
284
+ fontFamily === '$$SDF_FAILURE_TEST$$'
285
+ );
286
+ }
287
+
288
+ override isFontFaceSupported(fontFace: TrFontFace): boolean {
289
+ return fontFace instanceof SdfTrFontFace;
290
+ }
291
+
292
+ override addFontFace(fontFace: TrFontFace): void {
293
+ // Make sure the font face is an SDF font face (it should have already passed
294
+ // the `isFontFaceSupported` check)
295
+ assertTruthy(fontFace instanceof SdfTrFontFace);
296
+ const familyName = fontFace.fontFamily;
297
+ const fontFamiles =
298
+ fontFace.type === 'ssdf'
299
+ ? this.ssdfFontFamilies
300
+ : fontFace.type === 'msdf'
301
+ ? this.msdfFontFamilies
302
+ : undefined;
303
+ if (!fontFamiles) {
304
+ console.warn(`Invalid font face type: ${fontFace.type as string}`);
305
+ return;
306
+ }
307
+ let faceSet = fontFamiles[familyName];
308
+ if (!faceSet) {
309
+ faceSet = new Set();
310
+ fontFamiles[familyName] = faceSet;
311
+ }
312
+ faceSet.add(fontFace);
313
+ }
314
+
315
+ override createState(props: TrProps): SdfTextRendererState {
316
+ return {
317
+ props,
318
+ status: 'initialState',
319
+ updateScheduled: false,
320
+ emitter: new EventEmitter(),
321
+ lineCache: [],
322
+ forceFullLayoutCalc: false,
323
+ renderWindow: {
324
+ screen: {
325
+ x1: 0,
326
+ y1: 0,
327
+ x2: 0,
328
+ y2: 0,
329
+ },
330
+ sdf: {
331
+ x1: 0,
332
+ y1: 0,
333
+ x2: 0,
334
+ y2: 0,
335
+ },
336
+ firstLineIdx: 0,
337
+ numLines: 0,
338
+ valid: false,
339
+ },
340
+ elementBounds: {
341
+ x1: 0,
342
+ y1: 0,
343
+ x2: 0,
344
+ y2: 0,
345
+ valid: false,
346
+ },
347
+ clippingRect: {
348
+ x: 0,
349
+ y: 0,
350
+ width: 0,
351
+ height: 0,
352
+ valid: false,
353
+ },
354
+ bufferNumFloats: 0,
355
+ bufferNumQuads: 0,
356
+ vertexBuffer: undefined,
357
+ webGlBuffers: null,
358
+ bufferUploaded: false,
359
+ textH: undefined,
360
+ textW: undefined,
361
+ distanceRange: 0,
362
+ trFontFace: undefined,
363
+ isRenderable: false,
364
+ debugData: {
365
+ updateCount: 0,
366
+ layoutCount: 0,
367
+ lastLayoutNumCharacters: 0,
368
+ layoutSum: 0,
369
+ drawSum: 0,
370
+ drawCount: 0,
371
+ bufferSize: 0,
372
+ },
373
+ };
374
+ }
375
+
376
+ override updateState(state: SdfTextRendererState): void {
377
+ let { trFontFace } = state;
378
+ const { textH, lineCache, debugData, forceFullLayoutCalc } = state;
379
+ debugData.updateCount++;
380
+
381
+ // On the first update call we need to set the status to loading
382
+ if (state.status === 'initialState') {
383
+ this.setStatus(state, 'loading');
384
+ }
385
+
386
+ // Resolve font face if we haven't yet
387
+ if (!trFontFace) {
388
+ trFontFace = this.resolveFontFace(state.props);
389
+ state.trFontFace = trFontFace;
390
+ if (!trFontFace) {
391
+ const msg = `SdfTextRenderer: Could not resolve font face for family: '${state.props.fontFamily}'`;
392
+ console.error(msg);
393
+ this.setStatus(state, 'failed', new Error(msg));
394
+ return;
395
+ }
396
+ trFontFace.texture.setRenderableOwner(state, state.isRenderable);
397
+ }
398
+
399
+ // If the font hasn't been loaded yet, stop here.
400
+ // Listen for the 'loaded' event and forward fontLoaded event
401
+ if (!trFontFace.loaded) {
402
+ trFontFace.once('loaded', () => {
403
+ this.scheduleUpdateState(state);
404
+ });
405
+ return;
406
+ }
407
+
408
+ // If the font is loaded then so should the data
409
+ assertTruthy(trFontFace.data, 'Font face data should be loaded');
410
+
411
+ const {
412
+ text,
413
+ fontSize,
414
+ x,
415
+ y,
416
+ contain,
417
+ width,
418
+ height,
419
+ lineHeight,
420
+ verticalAlign,
421
+ scrollable,
422
+ overflowSuffix,
423
+ maxLines,
424
+ } = state.props;
425
+
426
+ // scrollY only has an effect when contain === 'both' and scrollable === true
427
+ const scrollY = contain === 'both' && scrollable ? state.props.scrollY : 0;
428
+
429
+ const { renderWindow } = state;
430
+
431
+ /**
432
+ * The font size of the SDF font face (the basis for SDF space units)
433
+ */
434
+ const sdfFontSize = trFontFace.data.info.size;
435
+
436
+ /**
437
+ * Divide screen space units by this to get the SDF space units
438
+ * Mulitple SDF space units by this to get screen space units
439
+ */
440
+ const fontSizeRatio = fontSize / sdfFontSize;
441
+
442
+ // Needed in renderWindow calculation
443
+ const sdfLineHeight = lineHeight / fontSizeRatio;
444
+
445
+ state.distanceRange =
446
+ fontSizeRatio * trFontFace.data.distanceField.distanceRange;
447
+
448
+ // Allocate buffers if needed
449
+ const neededLength = text.length * FLOATS_PER_GLYPH;
450
+ let vertexBuffer = state.vertexBuffer;
451
+ if (!vertexBuffer || vertexBuffer.length < neededLength) {
452
+ vertexBuffer = new Float32Array(neededLength * 2);
453
+ }
454
+
455
+ const elementBounds = state.elementBounds;
456
+ if (!elementBounds.valid) {
457
+ this.setElementBoundsX(state);
458
+ this.setElementBoundsY(state);
459
+ elementBounds.valid = true;
460
+ }
461
+
462
+ // Return early if we're still viewing inside the established render window
463
+ // No need to re-render what we've already rendered
464
+ // (Only if there's an established renderWindow and we're not suppressing early exit)
465
+ if (!forceFullLayoutCalc && renderWindow.valid) {
466
+ const rwScreen = renderWindow.screen;
467
+ if (
468
+ x + rwScreen.x1 <= elementBounds.x1 &&
469
+ x + rwScreen.x2 >= elementBounds.x2 &&
470
+ y - scrollY + rwScreen.y1 <= elementBounds.y1 &&
471
+ y - scrollY + rwScreen.y2 >= elementBounds.y2
472
+ ) {
473
+ this.setStatus(state, 'loaded');
474
+ return;
475
+ }
476
+ // Otherwise invalidate the renderWindow so it can be redone
477
+ renderWindow.valid = false;
478
+ this.setStatus(state, 'loading');
479
+ }
480
+
481
+ const { offsetY, textAlign } = state.props;
482
+
483
+ // Create a new renderWindow if needed
484
+ if (!renderWindow.valid) {
485
+ const isPossiblyOnScreen = boundsOverlap(
486
+ elementBounds,
487
+ this.rendererBounds,
488
+ );
489
+
490
+ if (!isPossiblyOnScreen) {
491
+ // If the element is not possibly on screen, we can skip the layout and rendering completely
492
+ return;
493
+ }
494
+
495
+ setRenderWindow(
496
+ renderWindow,
497
+ x,
498
+ y,
499
+ scrollY,
500
+ lineHeight,
501
+ contain === 'both' ? elementBounds.y2 - elementBounds.y1 : 0,
502
+ elementBounds,
503
+ fontSizeRatio,
504
+ );
505
+ // console.log('newRenderWindow', renderWindow);
506
+ }
507
+
508
+ const start = getStartConditions(
509
+ sdfFontSize,
510
+ sdfLineHeight,
511
+ lineHeight,
512
+ verticalAlign,
513
+ offsetY,
514
+ fontSizeRatio,
515
+ renderWindow,
516
+ lineCache,
517
+ textH,
518
+ );
519
+
520
+ if (!start) {
521
+ // Nothing to render, return early, but still mark as loaded (since the text is just scrolled
522
+ // out of view)
523
+ this.setStatus(state, 'loaded');
524
+ return;
525
+ }
526
+
527
+ const { letterSpacing } = state.props;
528
+
529
+ const out2 = layoutText(
530
+ start.lineIndex,
531
+ start.sdfX,
532
+ start.sdfY,
533
+ text,
534
+ textAlign,
535
+ width,
536
+ height,
537
+ fontSize,
538
+ lineHeight,
539
+ letterSpacing,
540
+ vertexBuffer,
541
+ contain,
542
+ lineCache,
543
+ renderWindow.sdf,
544
+ trFontFace,
545
+ forceFullLayoutCalc,
546
+ scrollable,
547
+ overflowSuffix,
548
+ maxLines,
549
+ );
550
+
551
+ state.bufferUploaded = false;
552
+ state.bufferNumFloats = out2.bufferNumFloats;
553
+ state.bufferNumQuads = out2.bufferNumQuads;
554
+ state.vertexBuffer = vertexBuffer;
555
+ state.renderWindow = renderWindow;
556
+ debugData.lastLayoutNumCharacters = out2.layoutNumCharacters;
557
+ debugData.bufferSize = vertexBuffer.byteLength;
558
+
559
+ // If we didn't exit early, we know we have completely computed w/h
560
+ if (out2.fullyProcessed) {
561
+ state.textW = out2.maxX * fontSizeRatio;
562
+ state.textH = out2.maxY * fontSizeRatio;
563
+ }
564
+
565
+ // if (state.props.debug.printLayoutTime) {
566
+ // debugData.layoutSum += performance.now() - updateStartTime;
567
+ // debugData.layoutCount++;
568
+ // }
569
+ this.setStatus(state, 'loaded');
570
+ }
571
+
572
+ override renderQuads(
573
+ state: SdfTextRendererState,
574
+ transform: Matrix3d,
575
+ clippingRect: Readonly<RectWithValid>,
576
+ alpha: number,
577
+ ): void {
578
+ if (!state.vertexBuffer) {
579
+ // Nothing to draw
580
+ return;
581
+ }
582
+
583
+ const { renderer } = this.stage;
584
+
585
+ const { fontSize, color, contain, scrollable, zIndex, debug } = state.props;
586
+
587
+ // scrollY only has an effect when contain === 'both' and scrollable === true
588
+ const scrollY = contain === 'both' && scrollable ? state.props.scrollY : 0;
589
+
590
+ const {
591
+ textW = 0,
592
+ textH = 0,
593
+ distanceRange,
594
+ vertexBuffer,
595
+ bufferUploaded,
596
+ trFontFace,
597
+ elementBounds,
598
+ } = state;
599
+
600
+ let { webGlBuffers } = state;
601
+
602
+ if (!webGlBuffers) {
603
+ const glw = renderer.glw;
604
+ const stride = 4 * Float32Array.BYTES_PER_ELEMENT;
605
+ const webGlBuffer = glw.createBuffer();
606
+ assertTruthy(webGlBuffer);
607
+ state.webGlBuffers = new BufferCollection([
608
+ {
609
+ buffer: webGlBuffer,
610
+ attributes: {
611
+ a_position: {
612
+ name: 'a_position',
613
+ size: 2, // 2 components per iteration
614
+ type: glw.FLOAT, // the data is 32bit floats
615
+ normalized: false, // don't normalize the data
616
+ stride, // 0 = move forward size * sizeof(type) each iteration to get the next position
617
+ offset: 0, // start at the beginning of the buffer
618
+ },
619
+ a_textureCoordinate: {
620
+ name: 'a_textureCoordinate',
621
+ size: 2,
622
+ type: glw.FLOAT,
623
+ normalized: false,
624
+ stride,
625
+ offset: 2 * Float32Array.BYTES_PER_ELEMENT,
626
+ },
627
+ },
628
+ },
629
+ ]);
630
+ state.bufferUploaded = false;
631
+ assertTruthy(state.webGlBuffers);
632
+ webGlBuffers = state.webGlBuffers;
633
+ }
634
+
635
+ if (!bufferUploaded) {
636
+ const glw = renderer.glw;
637
+
638
+ const buffer = webGlBuffers?.getBuffer('a_textureCoordinate') ?? null;
639
+ glw.arrayBufferData(buffer, vertexBuffer, glw.STATIC_DRAW);
640
+ state.bufferUploaded = true;
641
+ }
642
+
643
+ assertTruthy(trFontFace);
644
+ if (scrollable && contain === 'both') {
645
+ assertTruthy(elementBounds.valid);
646
+ const elementRect = convertBoundToRect(elementBounds, tmpRect);
647
+
648
+ if (clippingRect.valid) {
649
+ state.clippingRect.valid = true;
650
+ clippingRect = intersectRect(
651
+ clippingRect,
652
+ elementRect,
653
+ state.clippingRect,
654
+ );
655
+ } else {
656
+ state.clippingRect.valid = true;
657
+ clippingRect = copyRect(elementRect, state.clippingRect);
658
+ }
659
+ }
660
+
661
+ const renderOp = new WebGlCoreRenderOp(
662
+ renderer.glw,
663
+ renderer.options,
664
+ webGlBuffers,
665
+ this.sdfShader,
666
+ {
667
+ transform: transform.data,
668
+ // IMPORTANT: The SDF Shader expects the color NOT to be premultiplied
669
+ // for the best blending results. Which is why we use `mergeColorAlpha`
670
+ // instead of `mergeColorAlphaPremultiplied` here.
671
+ color: mergeColorAlpha(color, alpha),
672
+ size: fontSize / (trFontFace.data?.info.size || 0),
673
+ scrollY,
674
+ distanceRange,
675
+ debug: debug.sdfShaderDebug,
676
+ } satisfies SdfShaderProps,
677
+ alpha,
678
+ clippingRect,
679
+ { height: textH, width: textW },
680
+ 0,
681
+ zIndex,
682
+ );
683
+
684
+ const texture = state.trFontFace?.texture;
685
+ assertTruthy(texture);
686
+ const ctxTexture = this.stage.txManager.getCtxTexture(texture);
687
+
688
+ renderOp.addTexture(ctxTexture as WebGlCoreCtxTexture);
689
+ renderOp.length = state.bufferNumFloats;
690
+ renderOp.numQuads = state.bufferNumQuads;
691
+
692
+ renderer.addRenderOp(renderOp);
693
+
694
+ // if (!debug.disableScissor) {
695
+ // renderer.enableScissor(
696
+ // visibleRect.x,
697
+ // visibleRect.y,
698
+ // visibleRect.w,
699
+ // visibleRect.h,
700
+ // );
701
+ // }
702
+
703
+ // Draw the arrays
704
+ // gl.drawArrays(
705
+ // gl.TRIANGLES, // Primitive type
706
+ // 0,
707
+ // bufferNumVertices, // Number of verticies
708
+ // );
709
+
710
+ // renderer.disableScissor();
711
+
712
+ // if (debug.showElementRect) {
713
+ // this.renderer.drawBorder(
714
+ // Colors.Blue,
715
+ // elementRect.x,
716
+ // elementRect.y,
717
+ // elementRect.w,
718
+ // elementRect.h,
719
+ // );
720
+ // }
721
+
722
+ // if (debug.showVisibleRect) {
723
+ // this.renderer.drawBorder(
724
+ // Colors.Green,
725
+ // visibleRect.x,
726
+ // visibleRect.y,
727
+ // visibleRect.w,
728
+ // visibleRect.h,
729
+ // );
730
+ // }
731
+
732
+ // if (debug.showRenderWindow && renderWindow) {
733
+ // this.renderer.drawBorder(
734
+ // Colors.Red,
735
+ // x + renderWindow.x1,
736
+ // y + renderWindow.y1 - scrollY,
737
+ // x + renderWindow.x2 - (x + renderWindow.x1),
738
+ // y + renderWindow.y2 - scrollY - (y + renderWindow.y1 - scrollY),
739
+ // );
740
+ // }
741
+ // if (debug.printLayoutTime) {
742
+ // debugData.drawSum += performance.now() - drawStartTime;
743
+ // debugData.drawCount++;
744
+ // }
745
+ }
746
+
747
+ override setIsRenderable(
748
+ state: SdfTextRendererState,
749
+ renderable: boolean,
750
+ ): void {
751
+ super.setIsRenderable(state, renderable);
752
+ state.trFontFace?.texture.setRenderableOwner(state, renderable);
753
+ }
754
+
755
+ override destroyState(state: SdfTextRendererState): void {
756
+ super.destroyState(state);
757
+ // If there's a Font Face assigned we must free the owner relation to its texture
758
+ state.trFontFace?.texture.setRenderableOwner(state, false);
759
+ }
760
+ //#endregion Overrides
761
+
762
+ public resolveFontFace(props: TrFontProps): SdfTrFontFace | undefined {
763
+ return TrFontManager.resolveFontFace(this.fontFamilyArray, props) as
764
+ | SdfTrFontFace
765
+ | undefined;
766
+ }
767
+
768
+ /**
769
+ * Release the loaded SDF font face
770
+ *
771
+ * @param state
772
+ */
773
+ protected releaseFontFace(state: SdfTextRendererState) {
774
+ if (state.trFontFace) {
775
+ state.trFontFace.texture.setRenderableOwner(state, false);
776
+ state.trFontFace = undefined;
777
+ }
778
+ }
779
+
780
+ /**
781
+ * Invalidate the layout cache stored in the state. This will cause the text
782
+ * to be re-layed out on the next update.
783
+ *
784
+ * @remarks
785
+ * This also invalidates the visible window cache.
786
+ *
787
+ * @param state
788
+ */
789
+ protected invalidateLayoutCache(state: SdfTextRendererState): void {
790
+ state.renderWindow.valid = false;
791
+ state.elementBounds.valid = false;
792
+ state.textH = undefined;
793
+ state.textW = undefined;
794
+ state.lineCache = [];
795
+ this.setStatus(state, 'loading');
796
+ this.scheduleUpdateState(state);
797
+ }
798
+
799
+ protected setElementBoundsX(state: SdfTextRendererState): void {
800
+ const { x, contain, width } = state.props;
801
+ const { elementBounds } = state;
802
+ elementBounds.x1 = x;
803
+ elementBounds.x2 = contain !== 'none' ? x + width : Infinity;
804
+ }
805
+
806
+ protected setElementBoundsY(state: SdfTextRendererState): void {
807
+ const { y, contain, height } = state.props;
808
+ const { elementBounds } = state;
809
+ elementBounds.y1 = y;
810
+ elementBounds.y2 = contain === 'both' ? y + height : Infinity;
811
+ }
812
+ }