@lightningjs/renderer 3.0.0-beta10 → 3.0.0-beta12

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 (355) hide show
  1. package/LICENSE +202 -202
  2. package/NOTICE +3 -3
  3. package/README.md +133 -133
  4. package/dist/src/common/CommonTypes.d.ts +2 -2
  5. package/dist/src/core/CoreNode.d.ts +8 -7
  6. package/dist/src/core/CoreNode.js +57 -61
  7. package/dist/src/core/CoreNode.js.map +1 -1
  8. package/dist/src/core/CoreTextNode.d.ts +3 -0
  9. package/dist/src/core/CoreTextNode.js +54 -19
  10. package/dist/src/core/CoreTextNode.js.map +1 -1
  11. package/dist/src/core/Stage.js +4 -4
  12. package/dist/src/core/Stage.js.map +1 -1
  13. package/dist/src/core/lib/textureCompression.js +4 -4
  14. package/dist/src/core/lib/textureCompression.js.map +1 -1
  15. package/dist/src/core/platform.d.ts +10 -0
  16. package/dist/src/core/platform.js +81 -0
  17. package/dist/src/core/platform.js.map +1 -0
  18. package/dist/src/core/renderers/CoreShader.d.ts +9 -0
  19. package/dist/src/core/renderers/CoreShader.js +28 -0
  20. package/dist/src/core/renderers/CoreShader.js.map +1 -0
  21. package/dist/src/core/renderers/CoreShaderNode.js +2 -2
  22. package/dist/src/core/renderers/CoreShaderNode.js.map +1 -1
  23. package/dist/src/core/renderers/canvas/CanvasCoreRenderer.d.ts +33 -0
  24. package/dist/src/core/renderers/canvas/CanvasCoreRenderer.js +250 -0
  25. package/dist/src/core/renderers/canvas/CanvasCoreRenderer.js.map +1 -0
  26. package/dist/src/core/renderers/canvas/CanvasCoreTexture.d.ts +16 -0
  27. package/dist/src/core/renderers/canvas/CanvasCoreTexture.js +124 -0
  28. package/dist/src/core/renderers/canvas/CanvasCoreTexture.js.map +1 -0
  29. package/dist/src/core/renderers/canvas/CanvasRenderer.js +1 -1
  30. package/dist/src/core/renderers/canvas/CanvasRenderer.js.map +1 -1
  31. package/dist/src/core/renderers/canvas/CanvasTexture.js +5 -5
  32. package/dist/src/core/renderers/canvas/CanvasTexture.js.map +1 -1
  33. package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.d.ts +13 -0
  34. package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.js +113 -192
  35. package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.js.map +1 -1
  36. package/dist/src/core/renderers/canvas/internal/ColorUtils.d.ts +0 -2
  37. package/dist/src/core/renderers/canvas/internal/ColorUtils.js +0 -14
  38. package/dist/src/core/renderers/canvas/internal/ColorUtils.js.map +1 -1
  39. package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.d.ts +10 -0
  40. package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.js +43 -0
  41. package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.js.map +1 -0
  42. package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.d.ts +12 -0
  43. package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.js +58 -0
  44. package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.js.map +1 -0
  45. package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.d.ts +9 -0
  46. package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js +38 -0
  47. package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js.map +1 -0
  48. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.d.ts +56 -0
  49. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +239 -0
  50. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -0
  51. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.d.ts +34 -0
  52. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.js +114 -0
  53. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.js.map +1 -0
  54. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.d.ts +133 -0
  55. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +616 -0
  56. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -0
  57. package/dist/src/core/renderers/webgl/WebGlCoreShader.d.ts +83 -0
  58. package/dist/src/core/renderers/webgl/WebGlCoreShader.js +233 -0
  59. package/dist/src/core/renderers/webgl/WebGlCoreShader.js.map +1 -0
  60. package/dist/src/core/renderers/webgl/WebGlCtxRenderTexture.js +5 -6
  61. package/dist/src/core/renderers/webgl/WebGlCtxRenderTexture.js.map +1 -1
  62. package/dist/src/core/renderers/webgl/WebGlCtxSubTexture.d.ts +13 -0
  63. package/dist/src/core/renderers/webgl/WebGlCtxSubTexture.js +34 -5
  64. package/dist/src/core/renderers/webgl/WebGlCtxSubTexture.js.map +1 -1
  65. package/dist/src/core/renderers/webgl/WebGlCtxTexture.js +21 -21
  66. package/dist/src/core/renderers/webgl/WebGlCtxTexture.js.map +1 -1
  67. package/dist/src/core/renderers/webgl/WebGlRenderOp.js +1 -1
  68. package/dist/src/core/renderers/webgl/WebGlRenderOp.js.map +1 -1
  69. package/dist/src/core/renderers/webgl/WebGlRenderer.js +12 -11
  70. package/dist/src/core/renderers/webgl/WebGlRenderer.js.map +1 -1
  71. package/dist/src/core/renderers/webgl/WebGlShaderProgram.js +2 -2
  72. package/dist/src/core/renderers/webgl/WebGlShaderProgram.js.map +1 -1
  73. package/dist/src/core/renderers/webgl/internal/ShaderUtils.js +35 -35
  74. package/dist/src/core/renderers/webgl/shaders/DefaultShader.d.ts +9 -0
  75. package/dist/src/core/renderers/webgl/shaders/DefaultShader.js +87 -0
  76. package/dist/src/core/renderers/webgl/shaders/DefaultShader.js.map +1 -0
  77. package/dist/src/core/renderers/webgl/shaders/DefaultShaderBatched.d.ts +10 -0
  78. package/dist/src/core/renderers/webgl/shaders/DefaultShaderBatched.js +119 -0
  79. package/dist/src/core/renderers/webgl/shaders/DefaultShaderBatched.js.map +1 -0
  80. package/dist/src/core/renderers/webgl/shaders/DynamicShader.d.ts +29 -0
  81. package/dist/src/core/renderers/webgl/shaders/DynamicShader.js +413 -0
  82. package/dist/src/core/renderers/webgl/shaders/DynamicShader.js.map +1 -0
  83. package/dist/src/core/renderers/webgl/shaders/RoundedRectangle.d.ts +28 -0
  84. package/dist/src/core/renderers/webgl/shaders/RoundedRectangle.js +131 -0
  85. package/dist/src/core/renderers/webgl/shaders/RoundedRectangle.js.map +1 -0
  86. package/dist/src/core/renderers/webgl/shaders/SdfShader.d.ts +47 -0
  87. package/dist/src/core/renderers/webgl/shaders/SdfShader.js +160 -0
  88. package/dist/src/core/renderers/webgl/shaders/SdfShader.js.map +1 -0
  89. package/dist/src/core/renderers/webgl/shaders/effects/BorderBottomEffect.d.ts +31 -0
  90. package/dist/src/core/renderers/webgl/shaders/effects/BorderBottomEffect.js +71 -0
  91. package/dist/src/core/renderers/webgl/shaders/effects/BorderBottomEffect.js.map +1 -0
  92. package/dist/src/core/renderers/webgl/shaders/effects/BorderEffect.d.ts +30 -0
  93. package/dist/src/core/renderers/webgl/shaders/effects/BorderEffect.js +58 -0
  94. package/dist/src/core/renderers/webgl/shaders/effects/BorderEffect.js.map +1 -0
  95. package/dist/src/core/renderers/webgl/shaders/effects/BorderLeftEffect.d.ts +31 -0
  96. package/dist/src/core/renderers/webgl/shaders/effects/BorderLeftEffect.js +71 -0
  97. package/dist/src/core/renderers/webgl/shaders/effects/BorderLeftEffect.js.map +1 -0
  98. package/dist/src/core/renderers/webgl/shaders/effects/BorderRightEffect.d.ts +31 -0
  99. package/dist/src/core/renderers/webgl/shaders/effects/BorderRightEffect.js +71 -0
  100. package/dist/src/core/renderers/webgl/shaders/effects/BorderRightEffect.js.map +1 -0
  101. package/dist/src/core/renderers/webgl/shaders/effects/BorderTopEffect.d.ts +31 -0
  102. package/dist/src/core/renderers/webgl/shaders/effects/BorderTopEffect.js +71 -0
  103. package/dist/src/core/renderers/webgl/shaders/effects/BorderTopEffect.js.map +1 -0
  104. package/dist/src/core/renderers/webgl/shaders/effects/EffectUtils.d.ts +9 -0
  105. package/dist/src/core/renderers/webgl/shaders/effects/EffectUtils.js +136 -0
  106. package/dist/src/core/renderers/webgl/shaders/effects/EffectUtils.js.map +1 -0
  107. package/dist/src/core/renderers/webgl/shaders/effects/FadeOutEffect.d.ts +36 -0
  108. package/dist/src/core/renderers/webgl/shaders/effects/FadeOutEffect.js +85 -0
  109. package/dist/src/core/renderers/webgl/shaders/effects/FadeOutEffect.js.map +1 -0
  110. package/dist/src/core/renderers/webgl/shaders/effects/GlitchEffect.d.ts +45 -0
  111. package/dist/src/core/renderers/webgl/shaders/effects/GlitchEffect.js +104 -0
  112. package/dist/src/core/renderers/webgl/shaders/effects/GlitchEffect.js.map +1 -0
  113. package/dist/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.d.ts +22 -0
  114. package/dist/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.js +45 -0
  115. package/dist/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.js.map +1 -0
  116. package/dist/src/core/renderers/webgl/shaders/effects/HolePunchEffect.d.ts +58 -0
  117. package/dist/src/core/renderers/webgl/shaders/effects/HolePunchEffect.js +80 -0
  118. package/dist/src/core/renderers/webgl/shaders/effects/HolePunchEffect.js.map +1 -0
  119. package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.d.ts +35 -0
  120. package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js +129 -0
  121. package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js.map +1 -0
  122. package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.d.ts +39 -0
  123. package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js +116 -0
  124. package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js.map +1 -0
  125. package/dist/src/core/renderers/webgl/shaders/effects/RadialProgressEffect.d.ts +61 -0
  126. package/dist/src/core/renderers/webgl/shaders/effects/RadialProgressEffect.js +127 -0
  127. package/dist/src/core/renderers/webgl/shaders/effects/RadialProgressEffect.js.map +1 -0
  128. package/dist/src/core/renderers/webgl/shaders/effects/RadiusEffect.d.ts +40 -0
  129. package/dist/src/core/renderers/webgl/shaders/effects/RadiusEffect.js +71 -0
  130. package/dist/src/core/renderers/webgl/shaders/effects/RadiusEffect.js.map +1 -0
  131. package/dist/src/core/renderers/webgl/shaders/effects/ShaderEffect.d.ts +115 -0
  132. package/dist/src/core/renderers/webgl/shaders/effects/ShaderEffect.js +61 -0
  133. package/dist/src/core/renderers/webgl/shaders/effects/ShaderEffect.js.map +1 -0
  134. package/dist/src/core/shaders/canvas/Border.js +4 -4
  135. package/dist/src/core/shaders/canvas/Border.js.map +1 -1
  136. package/dist/src/core/shaders/canvas/HolePunch.js +3 -3
  137. package/dist/src/core/shaders/canvas/HolePunch.js.map +1 -1
  138. package/dist/src/core/shaders/canvas/LinearGradient.js +2 -2
  139. package/dist/src/core/shaders/canvas/LinearGradient.js.map +1 -1
  140. package/dist/src/core/shaders/canvas/RadialGradient.js +4 -4
  141. package/dist/src/core/shaders/canvas/RadialGradient.js.map +1 -1
  142. package/dist/src/core/shaders/canvas/Rounded.js +1 -1
  143. package/dist/src/core/shaders/canvas/Rounded.js.map +1 -1
  144. package/dist/src/core/shaders/canvas/RoundedWithBorder.js +3 -3
  145. package/dist/src/core/shaders/canvas/RoundedWithBorder.js.map +1 -1
  146. package/dist/src/core/shaders/canvas/RoundedWithBorderAndShadow.js +3 -3
  147. package/dist/src/core/shaders/canvas/RoundedWithBorderAndShadow.js.map +1 -1
  148. package/dist/src/core/shaders/canvas/RoundedWithShadow.js +1 -1
  149. package/dist/src/core/shaders/canvas/RoundedWithShadow.js.map +1 -1
  150. package/dist/src/core/shaders/templates/BorderTemplate.d.ts +1 -1
  151. package/dist/src/core/shaders/templates/BorderTemplate.js +10 -10
  152. package/dist/src/core/shaders/templates/BorderTemplate.js.map +1 -1
  153. package/dist/src/core/shaders/templates/HolePunchTemplate.d.ts +2 -2
  154. package/dist/src/core/shaders/templates/HolePunchTemplate.js +2 -2
  155. package/dist/src/core/shaders/templates/HolePunchTemplate.js.map +1 -1
  156. package/dist/src/core/shaders/templates/RadialGradientTemplate.d.ts +2 -2
  157. package/dist/src/core/shaders/templates/RadialGradientTemplate.js +2 -2
  158. package/dist/src/core/shaders/templates/RadialGradientTemplate.js.map +1 -1
  159. package/dist/src/core/shaders/templates/shaderUtils.d.ts +5 -0
  160. package/dist/src/core/shaders/templates/shaderUtils.js +41 -0
  161. package/dist/src/core/shaders/templates/shaderUtils.js.map +1 -0
  162. package/dist/src/core/shaders/webgl/Border.js +83 -83
  163. package/dist/src/core/shaders/webgl/Border.js.map +1 -1
  164. package/dist/src/core/shaders/webgl/Default.js +47 -47
  165. package/dist/src/core/shaders/webgl/DefaultBatched.js +61 -61
  166. package/dist/src/core/shaders/webgl/HolePunch.js +34 -34
  167. package/dist/src/core/shaders/webgl/HolePunch.js.map +1 -1
  168. package/dist/src/core/shaders/webgl/LinearGradient.js +36 -36
  169. package/dist/src/core/shaders/webgl/RadialGradient.js +35 -35
  170. package/dist/src/core/shaders/webgl/RadialGradient.js.map +1 -1
  171. package/dist/src/core/shaders/webgl/Rounded.js +72 -72
  172. package/dist/src/core/shaders/webgl/Rounded.js.map +1 -1
  173. package/dist/src/core/shaders/webgl/RoundedWithBorder.js +113 -113
  174. package/dist/src/core/shaders/webgl/RoundedWithBorder.js.map +1 -1
  175. package/dist/src/core/shaders/webgl/RoundedWithBorderAndShadow.js +132 -132
  176. package/dist/src/core/shaders/webgl/RoundedWithBorderAndShadow.js.map +1 -1
  177. package/dist/src/core/shaders/webgl/RoundedWithShadow.js +55 -55
  178. package/dist/src/core/shaders/webgl/RoundedWithShadow.js.map +1 -1
  179. package/dist/src/core/shaders/webgl/SdfShader.js +62 -62
  180. package/dist/src/core/shaders/webgl/Shadow.js +83 -83
  181. package/dist/src/core/shaders/webgl/Spinner.d.ts +1 -0
  182. package/dist/src/core/shaders/webgl/Spinner.js +2 -0
  183. package/dist/src/core/shaders/webgl/Spinner.js.map +1 -0
  184. package/dist/src/core/text-rendering/CanvasFontHandler.d.ts +16 -0
  185. package/dist/src/core/text-rendering/CanvasFontHandler.js +29 -0
  186. package/dist/src/core/text-rendering/CanvasFontHandler.js.map +1 -1
  187. package/dist/src/core/text-rendering/SdfFontHandler.d.ts +15 -0
  188. package/dist/src/core/text-rendering/SdfFontHandler.js +34 -2
  189. package/dist/src/core/text-rendering/SdfFontHandler.js.map +1 -1
  190. package/dist/src/core/text-rendering/TextRenderer.d.ts +2 -0
  191. package/dist/src/core/text-rendering/TextTextureRendererUtils.js.map +1 -1
  192. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js +2 -2
  193. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js.map +1 -1
  194. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js +0 -5
  195. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +1 -1
  196. package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.d.ts +1 -7
  197. package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.js +2 -50
  198. package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.js.map +1 -1
  199. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +3 -2
  200. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +83 -42
  201. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
  202. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.d.ts +1 -1
  203. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js +8 -66
  204. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js.map +1 -1
  205. package/dist/src/core/text-rendering/renderers/TextRenderer.d.ts +4 -14
  206. package/dist/src/core/text-rendering/renderers/TextRenderer.js +0 -3
  207. package/dist/src/core/text-rendering/renderers/TextRenderer.js.map +1 -1
  208. package/dist/src/core/text-rendering/sdf/PeekableGenerator.d.ts +12 -0
  209. package/dist/src/core/text-rendering/sdf/PeekableGenerator.js +61 -0
  210. package/dist/src/core/text-rendering/sdf/PeekableGenerator.js.map +1 -0
  211. package/dist/src/core/text-rendering/sdf/SimpleFontShaper.d.ts +45 -0
  212. package/dist/src/core/text-rendering/sdf/SimpleFontShaper.js +69 -0
  213. package/dist/src/core/text-rendering/sdf/SimpleFontShaper.js.map +1 -0
  214. package/dist/src/core/textures/ColorTexture.js +1 -1
  215. package/dist/src/core/textures/ColorTexture.js.map +1 -1
  216. package/dist/src/core/textures/ImageTexture.d.ts +2 -2
  217. package/dist/src/core/textures/ImageTexture.js +11 -11
  218. package/dist/src/core/textures/ImageTexture.js.map +1 -1
  219. package/dist/src/core/textures/NoiseTexture.d.ts +2 -2
  220. package/dist/src/core/textures/NoiseTexture.js +6 -6
  221. package/dist/src/core/textures/NoiseTexture.js.map +1 -1
  222. package/dist/src/core/textures/RenderTexture.d.ts +6 -6
  223. package/dist/src/core/textures/RenderTexture.js +10 -10
  224. package/dist/src/core/textures/RenderTexture.js.map +1 -1
  225. package/dist/src/core/textures/SubTexture.d.ts +4 -4
  226. package/dist/src/core/textures/SubTexture.js +8 -8
  227. package/dist/src/core/textures/SubTexture.js.map +1 -1
  228. package/dist/src/core/textures/Texture.d.ts +3 -4
  229. package/dist/src/core/textures/Texture.js +4 -4
  230. package/dist/src/core/textures/Texture.js.map +1 -1
  231. package/dist/src/main-api/DynamicShaderController.d.ts +29 -0
  232. package/dist/src/main-api/DynamicShaderController.js +58 -0
  233. package/dist/src/main-api/DynamicShaderController.js.map +1 -0
  234. package/dist/src/main-api/Inspector.js +7 -7
  235. package/dist/src/main-api/Inspector.js.map +1 -1
  236. package/dist/src/main-api/Renderer.js +2 -2
  237. package/dist/src/main-api/Renderer.js.map +1 -1
  238. package/dist/src/main-api/ShaderController.d.ts +31 -0
  239. package/dist/src/main-api/ShaderController.js +37 -0
  240. package/dist/src/main-api/ShaderController.js.map +1 -0
  241. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  242. package/exports/canvas-shaders.ts +28 -28
  243. package/exports/canvas.ts +45 -45
  244. package/exports/index.ts +82 -82
  245. package/exports/inspector.ts +24 -24
  246. package/exports/utils.ts +50 -50
  247. package/exports/webgl-shaders.ts +28 -28
  248. package/exports/webgl.ts +52 -52
  249. package/package.json +2 -1
  250. package/src/common/CommonTypes.ts +146 -146
  251. package/src/common/EventEmitter.ts +77 -77
  252. package/src/common/IAnimationController.ts +92 -92
  253. package/src/common/IEventEmitter.ts +28 -28
  254. package/src/core/CoreNode.test.ts +202 -202
  255. package/src/core/CoreNode.ts +2481 -2491
  256. package/src/core/CoreShaderManager.ts +188 -188
  257. package/src/core/CoreTextNode.ts +483 -443
  258. package/src/core/CoreTextureManager.ts +565 -565
  259. package/src/core/Stage.ts +906 -906
  260. package/src/core/TextureMemoryManager.ts +445 -445
  261. package/src/core/animations/AnimationManager.ts +38 -38
  262. package/src/core/animations/CoreAnimation.ts +291 -291
  263. package/src/core/animations/CoreAnimationController.ts +166 -166
  264. package/src/core/lib/ContextSpy.ts +41 -41
  265. package/src/core/lib/ImageWorker.ts +286 -286
  266. package/src/core/lib/Matrix3d.ts +244 -244
  267. package/src/core/lib/RenderCoords.ts +71 -71
  268. package/src/core/lib/WebGlContextWrapper.ts +1381 -1381
  269. package/src/core/lib/colorCache.ts +20 -20
  270. package/src/core/lib/colorParser.ts +85 -85
  271. package/src/core/lib/textureCompression.ts +152 -152
  272. package/src/core/lib/textureSvg.ts +78 -78
  273. package/src/core/lib/utils.ts +412 -412
  274. package/src/core/lib/validateImageBitmap.ts +87 -87
  275. package/src/core/platforms/Platform.ts +77 -77
  276. package/src/core/platforms/web/WebPlatform.ts +121 -121
  277. package/src/core/renderers/CoreContextTexture.ts +43 -43
  278. package/src/core/renderers/CoreRenderOp.ts +22 -22
  279. package/src/core/renderers/CoreRenderer.ts +110 -110
  280. package/src/core/renderers/CoreShaderNode.ts +175 -175
  281. package/src/core/renderers/CoreShaderProgram.ts +23 -23
  282. package/src/core/renderers/canvas/CanvasRenderer.ts +283 -283
  283. package/src/core/renderers/canvas/CanvasShaderNode.ts +96 -96
  284. package/src/core/renderers/canvas/CanvasTexture.ts +156 -156
  285. package/src/core/renderers/webgl/WebGlCtxRenderTexture.ts +81 -91
  286. package/src/core/renderers/webgl/WebGlCtxSubTexture.ts +95 -50
  287. package/src/core/renderers/webgl/WebGlCtxTexture.ts +301 -310
  288. package/src/core/renderers/webgl/WebGlRenderOp.ts +167 -167
  289. package/src/core/renderers/webgl/WebGlRenderer.ts +746 -747
  290. package/src/core/renderers/webgl/WebGlShaderNode.ts +435 -435
  291. package/src/core/renderers/webgl/WebGlShaderProgram.ts +341 -341
  292. package/src/core/renderers/webgl/internal/BufferCollection.ts +54 -54
  293. package/src/core/renderers/webgl/internal/RendererUtils.ts +155 -155
  294. package/src/core/renderers/webgl/internal/ShaderUtils.ts +281 -281
  295. package/src/core/renderers/webgl/internal/WebGlUtils.ts +35 -35
  296. package/src/core/shaders/canvas/Border.ts +75 -75
  297. package/src/core/shaders/canvas/HolePunch.ts +55 -62
  298. package/src/core/shaders/canvas/LinearGradient.ts +71 -71
  299. package/src/core/shaders/canvas/RadialGradient.ts +99 -99
  300. package/src/core/shaders/canvas/Rounded.ts +55 -55
  301. package/src/core/shaders/canvas/RoundedWithBorder.ts +72 -74
  302. package/src/core/shaders/canvas/RoundedWithBorderAndShadow.ts +88 -90
  303. package/src/core/shaders/canvas/RoundedWithShadow.ts +70 -70
  304. package/src/core/shaders/canvas/Shadow.ts +52 -52
  305. package/src/core/shaders/canvas/utils/render.ts +151 -151
  306. package/src/core/shaders/templates/BorderTemplate.ts +115 -115
  307. package/src/core/shaders/templates/HolePunchTemplate.ts +82 -82
  308. package/src/core/shaders/templates/LinearGradientTemplate.ts +71 -71
  309. package/src/core/shaders/templates/RadialGradientTemplate.ts +81 -81
  310. package/src/core/shaders/templates/RoundedTemplate.ts +98 -98
  311. package/src/core/shaders/templates/RoundedWithBorderAndShadowTemplate.ts +38 -38
  312. package/src/core/shaders/templates/RoundedWithBorderTemplate.ts +35 -35
  313. package/src/core/shaders/templates/RoundedWithShadowTemplate.ts +35 -35
  314. package/src/core/shaders/templates/ShadowTemplate.ts +106 -106
  315. package/src/core/shaders/utils.ts +46 -46
  316. package/src/core/shaders/webgl/Border.ts +116 -116
  317. package/src/core/shaders/webgl/Default.ts +89 -89
  318. package/src/core/shaders/webgl/DefaultBatched.ts +129 -129
  319. package/src/core/shaders/webgl/HolePunch.ts +75 -75
  320. package/src/core/shaders/webgl/LinearGradient.ts +82 -82
  321. package/src/core/shaders/webgl/RadialGradient.ts +85 -85
  322. package/src/core/shaders/webgl/Rounded.ts +113 -117
  323. package/src/core/shaders/webgl/RoundedWithBorder.ts +151 -155
  324. package/src/core/shaders/webgl/RoundedWithBorderAndShadow.ts +175 -175
  325. package/src/core/shaders/webgl/RoundedWithShadow.ts +94 -98
  326. package/src/core/shaders/webgl/SdfShader.ts +134 -134
  327. package/src/core/shaders/webgl/Shadow.ts +115 -115
  328. package/src/core/text-rendering/CanvasFontHandler.ts +210 -176
  329. package/src/core/text-rendering/CanvasTextRenderer.ts +622 -622
  330. package/src/core/text-rendering/SdfFontHandler.ts +554 -517
  331. package/src/core/text-rendering/SdfTextRenderer.ts +466 -466
  332. package/src/core/text-rendering/TextRenderer.ts +406 -404
  333. package/src/core/text-rendering/Utils.ts +257 -257
  334. package/src/core/text-rendering/canvas/Settings.ts +99 -99
  335. package/src/core/text-rendering/canvas/Utils.test.ts +206 -206
  336. package/src/core/text-rendering/canvas/Utils.ts +178 -178
  337. package/src/core/text-rendering/canvas/calculateRenderInfo.ts +299 -299
  338. package/src/core/text-rendering/canvas/draw.ts +165 -165
  339. package/src/core/text-rendering/sdf/Utils.test.ts +402 -402
  340. package/src/core/text-rendering/sdf/Utils.ts +436 -436
  341. package/src/core/text-rendering/sdf/index.ts +20 -20
  342. package/src/core/textures/ColorTexture.ts +102 -102
  343. package/src/core/textures/ImageTexture.ts +418 -418
  344. package/src/core/textures/NoiseTexture.ts +104 -104
  345. package/src/core/textures/RenderTexture.ts +85 -85
  346. package/src/core/textures/SubTexture.ts +205 -205
  347. package/src/core/textures/Texture.ts +381 -381
  348. package/src/core/utils.ts +227 -227
  349. package/src/env.d.ts +7 -7
  350. package/src/main-api/INode.ts +100 -100
  351. package/src/main-api/Inspector.ts +567 -567
  352. package/src/main-api/Renderer.ts +873 -873
  353. package/src/main-api/utils.ts +45 -45
  354. package/src/utils.ts +267 -267
  355. package/COPYING +0 -1
@@ -1,622 +1,622 @@
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 { assertTruthy } from '../../utils.js';
21
- import type { Stage } from '../Stage.js';
22
- import type {
23
- TextLayout,
24
- NormalizedFontMetrics,
25
- TextBaseline,
26
- } from './TextRenderer.js';
27
- import * as CanvasFontHandler from './CanvasFontHandler.js';
28
- import { type LineType } from './canvas/calculateRenderInfo.js';
29
- import { calcHeight, measureText, wrapText, wrapWord } from './canvas/Utils.js';
30
- import { normalizeCanvasColor } from '../lib/colorCache.js';
31
- import type { CoreTextNodeProps } from '../CoreTextNode.js';
32
- import { isZeroWidthSpace } from './Utils.js';
33
-
34
- const MAX_TEXTURE_DIMENSION = 4096;
35
-
36
- const type = 'canvas' as const;
37
-
38
- let canvas: HTMLCanvasElement | OffscreenCanvas | null = null;
39
- let context:
40
- | CanvasRenderingContext2D
41
- | OffscreenCanvasRenderingContext2D
42
- | null = null;
43
-
44
- // Separate canvas and context for text measurements
45
- let measureCanvas: HTMLCanvasElement | OffscreenCanvas | null = null;
46
- let measureContext:
47
- | CanvasRenderingContext2D
48
- | OffscreenCanvasRenderingContext2D
49
- | null = null;
50
-
51
- // Cache for text layout calculations
52
- const layoutCache = new Map<
53
- string,
54
- {
55
- lines: string[];
56
- lineWidths: number[];
57
- maxLineWidth: number;
58
- remainingText: string;
59
- moreTextLines: boolean;
60
- }
61
- >();
62
-
63
- // Initialize the Text Renderer
64
- const init = (stage: Stage): void => {
65
- // Drawing canvas and context
66
- canvas = stage.platform.createCanvas() as HTMLCanvasElement | OffscreenCanvas;
67
- context = canvas.getContext('2d', { willReadFrequently: true }) as
68
- | CanvasRenderingContext2D
69
- | OffscreenCanvasRenderingContext2D;
70
-
71
- // Separate measuring canvas and context
72
- measureCanvas = stage.platform.createCanvas() as
73
- | HTMLCanvasElement
74
- | OffscreenCanvas;
75
- measureContext = measureCanvas.getContext('2d') as
76
- | CanvasRenderingContext2D
77
- | OffscreenCanvasRenderingContext2D;
78
-
79
- // Set up a minimal size for the measuring canvas since we only use it for measurements
80
- measureCanvas.width = 1;
81
- measureCanvas.height = 1;
82
-
83
- CanvasFontHandler.init(context);
84
- };
85
-
86
- /**
87
- * Canvas text renderer
88
- *
89
- * @param stage - Stage instance for font resolution
90
- * @param props - Text rendering properties
91
- * @returns Object containing ImageData and dimensions
92
- */
93
- const renderText = (
94
- stage: Stage,
95
- props: CoreTextNodeProps,
96
- ): {
97
- imageData: ImageData | null;
98
- width: number;
99
- height: number;
100
- layout?: TextLayout;
101
- } => {
102
- assertTruthy(canvas, 'Canvas is not initialized');
103
- assertTruthy(context, 'Canvas context is not available');
104
-
105
- // Extract already normalized properties
106
- const {
107
- text,
108
- fontFamily,
109
- fontStyle,
110
- fontSize,
111
- textAlign,
112
- lineHeight: propLineHeight,
113
- maxLines,
114
- textBaseline,
115
- verticalAlign,
116
- overflowSuffix,
117
- maxWidth,
118
- maxHeight,
119
- offsetY,
120
- letterSpacing,
121
- } = props;
122
-
123
- // Performance optimization constants
124
- const precision = 1;
125
- const paddingLeft = 0;
126
- const paddingRight = 0;
127
- const textIndent = 0;
128
- const textRenderIssueMargin = 0;
129
- const textColor = 0xffffffff;
130
-
131
- // Determine word wrap behavior
132
- const wordWrap = maxWidth > 0;
133
- const textOverflow = overflowSuffix ? 'ellipsis' : null;
134
-
135
- // Calculate scaled values
136
- const scaledFontSize = fontSize * precision;
137
- const scaledOffsetY = offsetY * precision;
138
- const scaledLetterSpacing = letterSpacing * precision;
139
- // Get font metrics and calculate line height
140
- context.font = `${fontStyle} ${scaledFontSize}px ${fontFamily}`;
141
- context.textBaseline = textBaseline;
142
-
143
- const metrics = CanvasFontHandler.getFontMetrics(fontFamily, scaledFontSize);
144
- const lineHeight =
145
- propLineHeight === 0
146
- ? scaledFontSize *
147
- (metrics.ascender - metrics.descender + metrics.lineGap) *
148
- precision
149
- : propLineHeight;
150
-
151
- // Calculate max lines constraint
152
- const containedMaxLines =
153
- maxHeight !== null ? Math.floor(maxHeight / lineHeight) : 0;
154
- const computedMaxLines = calculateMaxLines(containedMaxLines, maxLines);
155
-
156
- // Calculate initial width and inner width
157
- let width = maxWidth || 2048 / precision;
158
- let innerWidth = width - paddingLeft;
159
- if (innerWidth < 10) {
160
- width += 10 - innerWidth;
161
- innerWidth = 10;
162
- }
163
- const finalWordWrapWidth = maxWidth === 0 ? innerWidth : maxWidth;
164
-
165
- // Calculate text layout using cached helper function
166
- const layout = calculateTextLayout(
167
- text,
168
- fontFamily,
169
- scaledFontSize,
170
- fontStyle,
171
- wordWrap,
172
- finalWordWrapWidth,
173
- scaledLetterSpacing,
174
- textIndent,
175
- computedMaxLines,
176
- overflowSuffix,
177
- textOverflow,
178
- );
179
-
180
- // Calculate final dimensions
181
- const dimensions = calculateTextDimensions(
182
- layout,
183
- paddingLeft,
184
- paddingRight,
185
- textBaseline,
186
- scaledFontSize,
187
- lineHeight,
188
- scaledOffsetY,
189
- maxWidth,
190
- maxHeight,
191
- wordWrap,
192
- textAlign,
193
- );
194
-
195
- // Set up canvas dimensions
196
- canvas.width = Math.min(
197
- Math.ceil(dimensions.width + textRenderIssueMargin),
198
- MAX_TEXTURE_DIMENSION,
199
- );
200
- canvas.height = Math.min(Math.ceil(dimensions.height), MAX_TEXTURE_DIMENSION);
201
-
202
- // Reset font context after canvas resize
203
- context.font = `${fontStyle} ${scaledFontSize}px ${fontFamily}`;
204
- context.textBaseline = textBaseline;
205
-
206
- // Performance optimization for large fonts
207
- if (scaledFontSize >= 128) {
208
- context.globalAlpha = 0.01;
209
- context.fillRect(0, 0, 0.01, 0.01);
210
- context.globalAlpha = 1.0;
211
- }
212
-
213
- // Calculate drawing positions
214
- const drawLines = calculateDrawPositions(
215
- layout.lines,
216
- layout.lineWidths,
217
- textAlign,
218
- verticalAlign,
219
- innerWidth,
220
- paddingLeft,
221
- textIndent,
222
- lineHeight,
223
- metrics,
224
- scaledFontSize,
225
- );
226
-
227
- // Render text to canvas
228
- renderTextToCanvas(
229
- context,
230
- drawLines,
231
- scaledLetterSpacing,
232
- textColor,
233
- fontStyle,
234
- scaledFontSize,
235
- fontFamily,
236
- );
237
-
238
- width = dimensions.width;
239
- const height = lineHeight * layout.lines.length;
240
- // Extract image data
241
- let imageData: ImageData | null = null;
242
- if (canvas.width > 0 && canvas.height > 0) {
243
- imageData = context.getImageData(0, 0, width, height);
244
- }
245
-
246
- return {
247
- imageData,
248
- width,
249
- height,
250
- };
251
- };
252
-
253
- /**
254
- * Calculate the effective max lines constraint
255
- */
256
- function calculateMaxLines(
257
- containedMaxLines: number,
258
- maxLines: number,
259
- ): number {
260
- if (containedMaxLines > 0 && maxLines > 0) {
261
- return containedMaxLines < maxLines ? containedMaxLines : maxLines;
262
- } else {
263
- return containedMaxLines > maxLines ? containedMaxLines : maxLines;
264
- }
265
- }
266
-
267
- /**
268
- * Generate a cache key for text layout calculations
269
- */
270
- function generateLayoutCacheKey(
271
- text: string,
272
- fontFamily: string,
273
- fontSize: number,
274
- fontStyle: string,
275
- wordWrap: boolean,
276
- wordWrapWidth: number,
277
- letterSpacing: number,
278
- maxLines: number,
279
- overflowSuffix: string,
280
- ): string {
281
- return `${text}-${fontFamily}-${fontSize}-${fontStyle}-${wordWrap}-${wordWrapWidth}-${letterSpacing}-${maxLines}-${overflowSuffix}`;
282
- }
283
-
284
- /**
285
- * Calculate text dimensions and wrapping
286
- */
287
- function calculateTextLayout(
288
- text: string,
289
- fontFamily: string,
290
- fontSize: number,
291
- fontStyle: string,
292
- wordWrap: boolean,
293
- wordWrapWidth: number,
294
- letterSpacing: number,
295
- textIndent: number,
296
- maxLines: number,
297
- overflowSuffix: string,
298
- textOverflow: string | null,
299
- ): {
300
- lines: string[];
301
- lineWidths: number[];
302
- maxLineWidth: number;
303
- remainingText: string;
304
- moreTextLines: boolean;
305
- } {
306
- assertTruthy(measureContext, 'Measure context is not available');
307
-
308
- // Check cache first
309
- const cacheKey = generateLayoutCacheKey(
310
- text,
311
- fontFamily,
312
- fontSize,
313
- fontStyle,
314
- wordWrap,
315
- wordWrapWidth,
316
- letterSpacing,
317
- maxLines,
318
- overflowSuffix,
319
- );
320
-
321
- const cached = layoutCache.get(cacheKey);
322
- if (cached) {
323
- return cached;
324
- }
325
-
326
- // Set font context for measurements on the dedicated measuring context
327
- measureContext.font = `${fontStyle} ${fontSize}px ${fontFamily}`;
328
-
329
- // Handle text overflow for non-wrapped text
330
- let processedText = text;
331
- if (textOverflow !== null && wordWrap === false) {
332
- let suffix: string;
333
- if (textOverflow === 'clip') {
334
- suffix = '';
335
- } else if (textOverflow === 'ellipsis') {
336
- suffix = overflowSuffix;
337
- } else {
338
- suffix = textOverflow;
339
- }
340
- processedText = wrapWord(
341
- measureContext,
342
- text,
343
- wordWrapWidth - textIndent,
344
- suffix,
345
- letterSpacing,
346
- );
347
- }
348
-
349
- // Word wrap
350
- let linesInfo: { n: number[]; l: string[] };
351
- if (wordWrap === true) {
352
- linesInfo = wrapText(
353
- measureContext,
354
- processedText,
355
- wordWrapWidth,
356
- letterSpacing,
357
- textIndent,
358
- );
359
- } else {
360
- linesInfo = { l: processedText.split(/(?:\r\n|\r|\n)/), n: [] };
361
- const n = linesInfo.l.length;
362
- for (let i = 0; i < n - 1; i++) {
363
- linesInfo.n.push(i);
364
- }
365
- }
366
- let lines: string[] = linesInfo.l;
367
-
368
- let remainingText = '';
369
- let moreTextLines = false;
370
-
371
- // Handle max lines constraint
372
- if (maxLines > 0 && lines.length > maxLines) {
373
- const usedLines = lines.slice(0, maxLines);
374
- let otherLines: string[] = [];
375
- if (overflowSuffix.length > 0) {
376
- const w = measureText(measureContext, overflowSuffix, letterSpacing);
377
- const al = wrapText(
378
- measureContext,
379
- usedLines[usedLines.length - 1] || '',
380
- wordWrapWidth - w,
381
- letterSpacing,
382
- textIndent,
383
- );
384
- usedLines[usedLines.length - 1] = `${al.l[0] || ''}${overflowSuffix}`;
385
- otherLines = [al.l.length > 1 ? al.l[1] || '' : ''];
386
- } else {
387
- otherLines = [''];
388
- }
389
-
390
- // Re-assemble the remaining text
391
- let i: number;
392
- const n = lines.length;
393
- let j = 0;
394
- const m = linesInfo.n.length;
395
- for (i = maxLines; i < n; i++) {
396
- otherLines[j] += `${otherLines[j] ? ' ' : ''}${lines[i] ?? ''}`;
397
- if (i + 1 < m && linesInfo.n[i + 1] !== undefined) {
398
- j++;
399
- }
400
- }
401
- remainingText = otherLines.join('\n');
402
- moreTextLines = true;
403
- lines = usedLines;
404
- }
405
-
406
- // Calculate line widths using the dedicated measuring context
407
- let maxLineWidth = 0;
408
- const lineWidths: number[] = [];
409
- for (let i = 0; i < lines.length; i++) {
410
- const lineWidth =
411
- measureText(measureContext, lines[i] || '', letterSpacing) +
412
- (i === 0 ? textIndent : 0);
413
- lineWidths.push(lineWidth);
414
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
415
- }
416
-
417
- const result = {
418
- lines,
419
- lineWidths,
420
- maxLineWidth,
421
- remainingText,
422
- moreTextLines,
423
- };
424
-
425
- // Cache the result
426
- layoutCache.set(cacheKey, result);
427
-
428
- return result;
429
- }
430
-
431
- /**
432
- * Calculate text dimensions based on layout
433
- */
434
- function calculateTextDimensions(
435
- layout: {
436
- lines: string[];
437
- lineWidths: number[];
438
- maxLineWidth: number;
439
- },
440
- paddingLeft: number,
441
- paddingRight: number,
442
- textBaseline: TextBaseline,
443
- fontSize: number,
444
- lineHeight: number,
445
- offsetY: number,
446
- initialWidth: number,
447
- initialHeight: number,
448
- wordWrap: boolean,
449
- textAlign: string,
450
- ): { width: number; height: number } {
451
- let width = initialWidth;
452
- let height = initialHeight;
453
-
454
- // Calculate width
455
- if (initialWidth === 0) {
456
- width = layout.maxLineWidth + paddingLeft + paddingRight;
457
- }
458
-
459
- // Adjust width for single-line left-aligned wrapped text
460
- if (
461
- wordWrap === true &&
462
- width > layout.maxLineWidth &&
463
- textAlign === 'left' &&
464
- layout.lines.length === 1
465
- ) {
466
- width = layout.maxLineWidth + paddingLeft + paddingRight;
467
- }
468
-
469
- // Calculate height if not provided
470
- if (height === 0) {
471
- height = calcHeight(
472
- textBaseline,
473
- fontSize,
474
- lineHeight,
475
- layout.lines.length,
476
- offsetY,
477
- );
478
- }
479
-
480
- return { width, height };
481
- }
482
-
483
- /**
484
- * Calculate drawing positions for text lines
485
- */
486
- function calculateDrawPositions(
487
- lines: string[],
488
- lineWidths: number[],
489
- textAlign: string,
490
- verticalAlign: string,
491
- innerWidth: number,
492
- paddingLeft: number,
493
- textIndent: number,
494
- lineHeight: number,
495
- metrics: NormalizedFontMetrics,
496
- fontSize: number,
497
- ): LineType[] {
498
- const drawLines: LineType[] = [];
499
- const ascenderPx = metrics.ascender * fontSize;
500
- const bareLineHeightPx = (metrics.ascender - metrics.descender) * fontSize;
501
-
502
- for (let i = 0, n = lines.length; i < n; i++) {
503
- let linePositionX = i === 0 ? textIndent : 0;
504
- let linePositionY = i * lineHeight + ascenderPx;
505
-
506
- // Vertical alignment
507
- if (verticalAlign == 'middle') {
508
- linePositionY += (lineHeight - bareLineHeightPx) / 2;
509
- } else if (verticalAlign == 'bottom') {
510
- linePositionY += lineHeight - bareLineHeightPx;
511
- }
512
-
513
- // Horizontal alignment
514
- const lineWidth = lineWidths[i];
515
- if (lineWidth !== undefined) {
516
- if (textAlign === 'right') {
517
- linePositionX += innerWidth - lineWidth;
518
- } else if (textAlign === 'center') {
519
- linePositionX += (innerWidth - lineWidth) / 2;
520
- }
521
- }
522
-
523
- linePositionX += paddingLeft;
524
-
525
- const lineText = lines[i];
526
- if (lineText !== undefined) {
527
- drawLines.push({
528
- text: lineText,
529
- x: linePositionX,
530
- y: linePositionY,
531
- w: lineWidth || 0,
532
- });
533
- }
534
- }
535
-
536
- return drawLines;
537
- }
538
-
539
- /**
540
- * Render text lines to canvas
541
- */
542
- function renderTextToCanvas(
543
- context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
544
- drawLines: LineType[],
545
- letterSpacing: number,
546
- textColor: number,
547
- fontStyle: string,
548
- fontSize: number,
549
- fontFamily: string,
550
- ): void {
551
- assertTruthy(measureContext, 'Measure context is not available');
552
-
553
- context.fillStyle = normalizeCanvasColor(textColor);
554
-
555
- // Sync font settings to measure context if we need to use it for letter spacing
556
- if (letterSpacing > 0) {
557
- measureContext.font = `${fontStyle} ${fontSize}px ${fontFamily}`;
558
- }
559
-
560
- for (let i = 0, n = drawLines.length; i < n; i++) {
561
- const drawLine = drawLines[i];
562
- if (drawLine) {
563
- if (letterSpacing === 0) {
564
- context.fillText(drawLine.text, drawLine.x, drawLine.y);
565
- } else {
566
- const textSplit = drawLine.text.split('');
567
- let x = drawLine.x;
568
- for (let j = 0, k = textSplit.length; j < k; j++) {
569
- const char = textSplit[j];
570
- if (char) {
571
- // Skip zero-width spaces for rendering but keep them in the text flow
572
- if (isZeroWidthSpace(char)) {
573
- continue;
574
- }
575
- context.fillText(char, x, drawLine.y);
576
- // Use the dedicated measuring context for letter spacing calculations
577
- x += measureText(measureContext, char, letterSpacing);
578
- }
579
- }
580
- }
581
- }
582
- }
583
- }
584
-
585
- /**
586
- * Clear layout cache for memory management
587
- */
588
- const clearLayoutCache = (): void => {
589
- layoutCache.clear();
590
- };
591
-
592
- /**
593
- * Add quads for rendering (Canvas doesn't use quads)
594
- */
595
- const addQuads = (): Float32Array | null => {
596
- // Canvas renderer doesn't use quad-based rendering
597
- // Return null for interface compatibility
598
- return null;
599
- };
600
-
601
- /**
602
- * Render quads for Canvas renderer (Canvas doesn't use quad-based rendering)
603
- */
604
- const renderQuads = (): void => {
605
- // Canvas renderer doesn't use quad-based rendering
606
- // This method is for interface compatibility only
607
- };
608
-
609
- /**
610
- * Canvas Text Renderer - implements TextRenderer interface
611
- */
612
- const CanvasTextRenderer = {
613
- type,
614
- font: CanvasFontHandler,
615
- renderText,
616
- addQuads,
617
- renderQuads,
618
- init,
619
- clearLayoutCache,
620
- };
621
-
622
- export default CanvasTextRenderer;
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 { assertTruthy } from '../../utils.js';
21
+ import type { Stage } from '../Stage.js';
22
+ import type {
23
+ TextLayout,
24
+ NormalizedFontMetrics,
25
+ TextBaseline,
26
+ } from './TextRenderer.js';
27
+ import * as CanvasFontHandler from './CanvasFontHandler.js';
28
+ import { type LineType } from './canvas/calculateRenderInfo.js';
29
+ import { calcHeight, measureText, wrapText, wrapWord } from './canvas/Utils.js';
30
+ import { normalizeCanvasColor } from '../lib/colorCache.js';
31
+ import type { CoreTextNodeProps } from '../CoreTextNode.js';
32
+ import { isZeroWidthSpace } from './Utils.js';
33
+
34
+ const MAX_TEXTURE_DIMENSION = 4096;
35
+
36
+ const type = 'canvas' as const;
37
+
38
+ let canvas: HTMLCanvasElement | OffscreenCanvas | null = null;
39
+ let context:
40
+ | CanvasRenderingContext2D
41
+ | OffscreenCanvasRenderingContext2D
42
+ | null = null;
43
+
44
+ // Separate canvas and context for text measurements
45
+ let measureCanvas: HTMLCanvasElement | OffscreenCanvas | null = null;
46
+ let measureContext:
47
+ | CanvasRenderingContext2D
48
+ | OffscreenCanvasRenderingContext2D
49
+ | null = null;
50
+
51
+ // Cache for text layout calculations
52
+ const layoutCache = new Map<
53
+ string,
54
+ {
55
+ lines: string[];
56
+ lineWidths: number[];
57
+ maxLineWidth: number;
58
+ remainingText: string;
59
+ moreTextLines: boolean;
60
+ }
61
+ >();
62
+
63
+ // Initialize the Text Renderer
64
+ const init = (stage: Stage): void => {
65
+ // Drawing canvas and context
66
+ canvas = stage.platform.createCanvas() as HTMLCanvasElement | OffscreenCanvas;
67
+ context = canvas.getContext('2d', { willReadFrequently: true }) as
68
+ | CanvasRenderingContext2D
69
+ | OffscreenCanvasRenderingContext2D;
70
+
71
+ // Separate measuring canvas and context
72
+ measureCanvas = stage.platform.createCanvas() as
73
+ | HTMLCanvasElement
74
+ | OffscreenCanvas;
75
+ measureContext = measureCanvas.getContext('2d') as
76
+ | CanvasRenderingContext2D
77
+ | OffscreenCanvasRenderingContext2D;
78
+
79
+ // Set up a minimal size for the measuring canvas since we only use it for measurements
80
+ measureCanvas.width = 1;
81
+ measureCanvas.height = 1;
82
+
83
+ CanvasFontHandler.init(context);
84
+ };
85
+
86
+ /**
87
+ * Canvas text renderer
88
+ *
89
+ * @param stage - Stage instance for font resolution
90
+ * @param props - Text rendering properties
91
+ * @returns Object containing ImageData and dimensions
92
+ */
93
+ const renderText = (
94
+ stage: Stage,
95
+ props: CoreTextNodeProps,
96
+ ): {
97
+ imageData: ImageData | null;
98
+ width: number;
99
+ height: number;
100
+ layout?: TextLayout;
101
+ } => {
102
+ assertTruthy(canvas, 'Canvas is not initialized');
103
+ assertTruthy(context, 'Canvas context is not available');
104
+
105
+ // Extract already normalized properties
106
+ const {
107
+ text,
108
+ fontFamily,
109
+ fontStyle,
110
+ fontSize,
111
+ textAlign,
112
+ lineHeight: propLineHeight,
113
+ maxLines,
114
+ textBaseline,
115
+ verticalAlign,
116
+ overflowSuffix,
117
+ maxWidth,
118
+ maxHeight,
119
+ offsetY,
120
+ letterSpacing,
121
+ } = props;
122
+
123
+ // Performance optimization constants
124
+ const precision = 1;
125
+ const paddingLeft = 0;
126
+ const paddingRight = 0;
127
+ const textIndent = 0;
128
+ const textRenderIssueMargin = 0;
129
+ const textColor = 0xffffffff;
130
+
131
+ // Determine word wrap behavior
132
+ const wordWrap = maxWidth > 0;
133
+ const textOverflow = overflowSuffix ? 'ellipsis' : null;
134
+
135
+ // Calculate scaled values
136
+ const scaledFontSize = fontSize * precision;
137
+ const scaledOffsetY = offsetY * precision;
138
+ const scaledLetterSpacing = letterSpacing * precision;
139
+ // Get font metrics and calculate line height
140
+ context.font = `${fontStyle} ${scaledFontSize}px ${fontFamily}`;
141
+ context.textBaseline = textBaseline;
142
+
143
+ const metrics = CanvasFontHandler.getFontMetrics(fontFamily, scaledFontSize);
144
+ const lineHeight =
145
+ propLineHeight === 0
146
+ ? scaledFontSize *
147
+ (metrics.ascender - metrics.descender + metrics.lineGap) *
148
+ precision
149
+ : propLineHeight;
150
+
151
+ // Calculate max lines constraint
152
+ const containedMaxLines =
153
+ maxHeight !== null ? Math.floor(maxHeight / lineHeight) : 0;
154
+ const computedMaxLines = calculateMaxLines(containedMaxLines, maxLines);
155
+
156
+ // Calculate initial width and inner width
157
+ let width = maxWidth || 2048 / precision;
158
+ let innerWidth = width - paddingLeft;
159
+ if (innerWidth < 10) {
160
+ width += 10 - innerWidth;
161
+ innerWidth = 10;
162
+ }
163
+ const finalWordWrapWidth = maxWidth === 0 ? innerWidth : maxWidth;
164
+
165
+ // Calculate text layout using cached helper function
166
+ const layout = calculateTextLayout(
167
+ text,
168
+ fontFamily,
169
+ scaledFontSize,
170
+ fontStyle,
171
+ wordWrap,
172
+ finalWordWrapWidth,
173
+ scaledLetterSpacing,
174
+ textIndent,
175
+ computedMaxLines,
176
+ overflowSuffix,
177
+ textOverflow,
178
+ );
179
+
180
+ // Calculate final dimensions
181
+ const dimensions = calculateTextDimensions(
182
+ layout,
183
+ paddingLeft,
184
+ paddingRight,
185
+ textBaseline,
186
+ scaledFontSize,
187
+ lineHeight,
188
+ scaledOffsetY,
189
+ maxWidth,
190
+ maxHeight,
191
+ wordWrap,
192
+ textAlign,
193
+ );
194
+
195
+ // Set up canvas dimensions
196
+ canvas.width = Math.min(
197
+ Math.ceil(dimensions.width + textRenderIssueMargin),
198
+ MAX_TEXTURE_DIMENSION,
199
+ );
200
+ canvas.height = Math.min(Math.ceil(dimensions.height), MAX_TEXTURE_DIMENSION);
201
+
202
+ // Reset font context after canvas resize
203
+ context.font = `${fontStyle} ${scaledFontSize}px ${fontFamily}`;
204
+ context.textBaseline = textBaseline;
205
+
206
+ // Performance optimization for large fonts
207
+ if (scaledFontSize >= 128) {
208
+ context.globalAlpha = 0.01;
209
+ context.fillRect(0, 0, 0.01, 0.01);
210
+ context.globalAlpha = 1.0;
211
+ }
212
+
213
+ // Calculate drawing positions
214
+ const drawLines = calculateDrawPositions(
215
+ layout.lines,
216
+ layout.lineWidths,
217
+ textAlign,
218
+ verticalAlign,
219
+ innerWidth,
220
+ paddingLeft,
221
+ textIndent,
222
+ lineHeight,
223
+ metrics,
224
+ scaledFontSize,
225
+ );
226
+
227
+ // Render text to canvas
228
+ renderTextToCanvas(
229
+ context,
230
+ drawLines,
231
+ scaledLetterSpacing,
232
+ textColor,
233
+ fontStyle,
234
+ scaledFontSize,
235
+ fontFamily,
236
+ );
237
+
238
+ width = dimensions.width;
239
+ const height = lineHeight * layout.lines.length;
240
+ // Extract image data
241
+ let imageData: ImageData | null = null;
242
+ if (canvas.width > 0 && canvas.height > 0) {
243
+ imageData = context.getImageData(0, 0, width, height);
244
+ }
245
+
246
+ return {
247
+ imageData,
248
+ width,
249
+ height,
250
+ };
251
+ };
252
+
253
+ /**
254
+ * Calculate the effective max lines constraint
255
+ */
256
+ function calculateMaxLines(
257
+ containedMaxLines: number,
258
+ maxLines: number,
259
+ ): number {
260
+ if (containedMaxLines > 0 && maxLines > 0) {
261
+ return containedMaxLines < maxLines ? containedMaxLines : maxLines;
262
+ } else {
263
+ return containedMaxLines > maxLines ? containedMaxLines : maxLines;
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Generate a cache key for text layout calculations
269
+ */
270
+ function generateLayoutCacheKey(
271
+ text: string,
272
+ fontFamily: string,
273
+ fontSize: number,
274
+ fontStyle: string,
275
+ wordWrap: boolean,
276
+ wordWrapWidth: number,
277
+ letterSpacing: number,
278
+ maxLines: number,
279
+ overflowSuffix: string,
280
+ ): string {
281
+ return `${text}-${fontFamily}-${fontSize}-${fontStyle}-${wordWrap}-${wordWrapWidth}-${letterSpacing}-${maxLines}-${overflowSuffix}`;
282
+ }
283
+
284
+ /**
285
+ * Calculate text dimensions and wrapping
286
+ */
287
+ function calculateTextLayout(
288
+ text: string,
289
+ fontFamily: string,
290
+ fontSize: number,
291
+ fontStyle: string,
292
+ wordWrap: boolean,
293
+ wordWrapWidth: number,
294
+ letterSpacing: number,
295
+ textIndent: number,
296
+ maxLines: number,
297
+ overflowSuffix: string,
298
+ textOverflow: string | null,
299
+ ): {
300
+ lines: string[];
301
+ lineWidths: number[];
302
+ maxLineWidth: number;
303
+ remainingText: string;
304
+ moreTextLines: boolean;
305
+ } {
306
+ assertTruthy(measureContext, 'Measure context is not available');
307
+
308
+ // Check cache first
309
+ const cacheKey = generateLayoutCacheKey(
310
+ text,
311
+ fontFamily,
312
+ fontSize,
313
+ fontStyle,
314
+ wordWrap,
315
+ wordWrapWidth,
316
+ letterSpacing,
317
+ maxLines,
318
+ overflowSuffix,
319
+ );
320
+
321
+ const cached = layoutCache.get(cacheKey);
322
+ if (cached) {
323
+ return cached;
324
+ }
325
+
326
+ // Set font context for measurements on the dedicated measuring context
327
+ measureContext.font = `${fontStyle} ${fontSize}px ${fontFamily}`;
328
+
329
+ // Handle text overflow for non-wrapped text
330
+ let processedText = text;
331
+ if (textOverflow !== null && wordWrap === false) {
332
+ let suffix: string;
333
+ if (textOverflow === 'clip') {
334
+ suffix = '';
335
+ } else if (textOverflow === 'ellipsis') {
336
+ suffix = overflowSuffix;
337
+ } else {
338
+ suffix = textOverflow;
339
+ }
340
+ processedText = wrapWord(
341
+ measureContext,
342
+ text,
343
+ wordWrapWidth - textIndent,
344
+ suffix,
345
+ letterSpacing,
346
+ );
347
+ }
348
+
349
+ // Word wrap
350
+ let linesInfo: { n: number[]; l: string[] };
351
+ if (wordWrap === true) {
352
+ linesInfo = wrapText(
353
+ measureContext,
354
+ processedText,
355
+ wordWrapWidth,
356
+ letterSpacing,
357
+ textIndent,
358
+ );
359
+ } else {
360
+ linesInfo = { l: processedText.split(/(?:\r\n|\r|\n)/), n: [] };
361
+ const n = linesInfo.l.length;
362
+ for (let i = 0; i < n - 1; i++) {
363
+ linesInfo.n.push(i);
364
+ }
365
+ }
366
+ let lines: string[] = linesInfo.l;
367
+
368
+ let remainingText = '';
369
+ let moreTextLines = false;
370
+
371
+ // Handle max lines constraint
372
+ if (maxLines > 0 && lines.length > maxLines) {
373
+ const usedLines = lines.slice(0, maxLines);
374
+ let otherLines: string[] = [];
375
+ if (overflowSuffix.length > 0) {
376
+ const w = measureText(measureContext, overflowSuffix, letterSpacing);
377
+ const al = wrapText(
378
+ measureContext,
379
+ usedLines[usedLines.length - 1] || '',
380
+ wordWrapWidth - w,
381
+ letterSpacing,
382
+ textIndent,
383
+ );
384
+ usedLines[usedLines.length - 1] = `${al.l[0] || ''}${overflowSuffix}`;
385
+ otherLines = [al.l.length > 1 ? al.l[1] || '' : ''];
386
+ } else {
387
+ otherLines = [''];
388
+ }
389
+
390
+ // Re-assemble the remaining text
391
+ let i: number;
392
+ const n = lines.length;
393
+ let j = 0;
394
+ const m = linesInfo.n.length;
395
+ for (i = maxLines; i < n; i++) {
396
+ otherLines[j] += `${otherLines[j] ? ' ' : ''}${lines[i] ?? ''}`;
397
+ if (i + 1 < m && linesInfo.n[i + 1] !== undefined) {
398
+ j++;
399
+ }
400
+ }
401
+ remainingText = otherLines.join('\n');
402
+ moreTextLines = true;
403
+ lines = usedLines;
404
+ }
405
+
406
+ // Calculate line widths using the dedicated measuring context
407
+ let maxLineWidth = 0;
408
+ const lineWidths: number[] = [];
409
+ for (let i = 0; i < lines.length; i++) {
410
+ const lineWidth =
411
+ measureText(measureContext, lines[i] || '', letterSpacing) +
412
+ (i === 0 ? textIndent : 0);
413
+ lineWidths.push(lineWidth);
414
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
415
+ }
416
+
417
+ const result = {
418
+ lines,
419
+ lineWidths,
420
+ maxLineWidth,
421
+ remainingText,
422
+ moreTextLines,
423
+ };
424
+
425
+ // Cache the result
426
+ layoutCache.set(cacheKey, result);
427
+
428
+ return result;
429
+ }
430
+
431
+ /**
432
+ * Calculate text dimensions based on layout
433
+ */
434
+ function calculateTextDimensions(
435
+ layout: {
436
+ lines: string[];
437
+ lineWidths: number[];
438
+ maxLineWidth: number;
439
+ },
440
+ paddingLeft: number,
441
+ paddingRight: number,
442
+ textBaseline: TextBaseline,
443
+ fontSize: number,
444
+ lineHeight: number,
445
+ offsetY: number,
446
+ initialWidth: number,
447
+ initialHeight: number,
448
+ wordWrap: boolean,
449
+ textAlign: string,
450
+ ): { width: number; height: number } {
451
+ let width = initialWidth;
452
+ let height = initialHeight;
453
+
454
+ // Calculate width
455
+ if (initialWidth === 0) {
456
+ width = layout.maxLineWidth + paddingLeft + paddingRight;
457
+ }
458
+
459
+ // Adjust width for single-line left-aligned wrapped text
460
+ if (
461
+ wordWrap === true &&
462
+ width > layout.maxLineWidth &&
463
+ textAlign === 'left' &&
464
+ layout.lines.length === 1
465
+ ) {
466
+ width = layout.maxLineWidth + paddingLeft + paddingRight;
467
+ }
468
+
469
+ // Calculate height if not provided
470
+ if (height === 0) {
471
+ height = calcHeight(
472
+ textBaseline,
473
+ fontSize,
474
+ lineHeight,
475
+ layout.lines.length,
476
+ offsetY,
477
+ );
478
+ }
479
+
480
+ return { width, height };
481
+ }
482
+
483
+ /**
484
+ * Calculate drawing positions for text lines
485
+ */
486
+ function calculateDrawPositions(
487
+ lines: string[],
488
+ lineWidths: number[],
489
+ textAlign: string,
490
+ verticalAlign: string,
491
+ innerWidth: number,
492
+ paddingLeft: number,
493
+ textIndent: number,
494
+ lineHeight: number,
495
+ metrics: NormalizedFontMetrics,
496
+ fontSize: number,
497
+ ): LineType[] {
498
+ const drawLines: LineType[] = [];
499
+ const ascenderPx = metrics.ascender * fontSize;
500
+ const bareLineHeightPx = (metrics.ascender - metrics.descender) * fontSize;
501
+
502
+ for (let i = 0, n = lines.length; i < n; i++) {
503
+ let linePositionX = i === 0 ? textIndent : 0;
504
+ let linePositionY = i * lineHeight + ascenderPx;
505
+
506
+ // Vertical alignment
507
+ if (verticalAlign == 'middle') {
508
+ linePositionY += (lineHeight - bareLineHeightPx) / 2;
509
+ } else if (verticalAlign == 'bottom') {
510
+ linePositionY += lineHeight - bareLineHeightPx;
511
+ }
512
+
513
+ // Horizontal alignment
514
+ const lineWidth = lineWidths[i];
515
+ if (lineWidth !== undefined) {
516
+ if (textAlign === 'right') {
517
+ linePositionX += innerWidth - lineWidth;
518
+ } else if (textAlign === 'center') {
519
+ linePositionX += (innerWidth - lineWidth) / 2;
520
+ }
521
+ }
522
+
523
+ linePositionX += paddingLeft;
524
+
525
+ const lineText = lines[i];
526
+ if (lineText !== undefined) {
527
+ drawLines.push({
528
+ text: lineText,
529
+ x: linePositionX,
530
+ y: linePositionY,
531
+ w: lineWidth || 0,
532
+ });
533
+ }
534
+ }
535
+
536
+ return drawLines;
537
+ }
538
+
539
+ /**
540
+ * Render text lines to canvas
541
+ */
542
+ function renderTextToCanvas(
543
+ context: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
544
+ drawLines: LineType[],
545
+ letterSpacing: number,
546
+ textColor: number,
547
+ fontStyle: string,
548
+ fontSize: number,
549
+ fontFamily: string,
550
+ ): void {
551
+ assertTruthy(measureContext, 'Measure context is not available');
552
+
553
+ context.fillStyle = normalizeCanvasColor(textColor);
554
+
555
+ // Sync font settings to measure context if we need to use it for letter spacing
556
+ if (letterSpacing > 0) {
557
+ measureContext.font = `${fontStyle} ${fontSize}px ${fontFamily}`;
558
+ }
559
+
560
+ for (let i = 0, n = drawLines.length; i < n; i++) {
561
+ const drawLine = drawLines[i];
562
+ if (drawLine) {
563
+ if (letterSpacing === 0) {
564
+ context.fillText(drawLine.text, drawLine.x, drawLine.y);
565
+ } else {
566
+ const textSplit = drawLine.text.split('');
567
+ let x = drawLine.x;
568
+ for (let j = 0, k = textSplit.length; j < k; j++) {
569
+ const char = textSplit[j];
570
+ if (char) {
571
+ // Skip zero-width spaces for rendering but keep them in the text flow
572
+ if (isZeroWidthSpace(char)) {
573
+ continue;
574
+ }
575
+ context.fillText(char, x, drawLine.y);
576
+ // Use the dedicated measuring context for letter spacing calculations
577
+ x += measureText(measureContext, char, letterSpacing);
578
+ }
579
+ }
580
+ }
581
+ }
582
+ }
583
+ }
584
+
585
+ /**
586
+ * Clear layout cache for memory management
587
+ */
588
+ const clearLayoutCache = (): void => {
589
+ layoutCache.clear();
590
+ };
591
+
592
+ /**
593
+ * Add quads for rendering (Canvas doesn't use quads)
594
+ */
595
+ const addQuads = (): Float32Array | null => {
596
+ // Canvas renderer doesn't use quad-based rendering
597
+ // Return null for interface compatibility
598
+ return null;
599
+ };
600
+
601
+ /**
602
+ * Render quads for Canvas renderer (Canvas doesn't use quad-based rendering)
603
+ */
604
+ const renderQuads = (): void => {
605
+ // Canvas renderer doesn't use quad-based rendering
606
+ // This method is for interface compatibility only
607
+ };
608
+
609
+ /**
610
+ * Canvas Text Renderer - implements TextRenderer interface
611
+ */
612
+ const CanvasTextRenderer = {
613
+ type,
614
+ font: CanvasFontHandler,
615
+ renderText,
616
+ addQuads,
617
+ renderQuads,
618
+ init,
619
+ clearLayoutCache,
620
+ };
621
+
622
+ export default CanvasTextRenderer;