@lightningjs/renderer 0.6.1 → 0.7.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 (189) hide show
  1. package/dist/src/common/CommonTypes.d.ts +8 -0
  2. package/dist/src/core/CoreNode.js +45 -16
  3. package/dist/src/core/CoreNode.js.map +1 -1
  4. package/dist/src/core/CoreTextNode.d.ts +10 -0
  5. package/dist/src/core/CoreTextNode.js +45 -0
  6. package/dist/src/core/CoreTextNode.js.map +1 -1
  7. package/dist/src/core/CoreTextureManager.d.ts +3 -1
  8. package/dist/src/core/CoreTextureManager.js +4 -1
  9. package/dist/src/core/CoreTextureManager.js.map +1 -1
  10. package/dist/src/core/Stage.d.ts +6 -0
  11. package/dist/src/core/Stage.js +12 -3
  12. package/dist/src/core/Stage.js.map +1 -1
  13. package/dist/src/core/lib/ImageWorker.d.ts +16 -0
  14. package/dist/src/core/lib/ImageWorker.js +111 -0
  15. package/dist/src/core/lib/ImageWorker.js.map +1 -0
  16. package/dist/src/core/lib/WebGlContextWrapper.d.ts +4 -0
  17. package/dist/src/core/lib/WebGlContextWrapper.js +7 -2
  18. package/dist/src/core/lib/WebGlContextWrapper.js.map +1 -1
  19. package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.d.ts +2 -1
  20. package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js +2 -2
  21. package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js.map +1 -1
  22. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.d.ts +3 -2
  23. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +23 -21
  24. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -1
  25. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.d.ts +3 -2
  26. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.js +9 -13
  27. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.js.map +1 -1
  28. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.d.ts +4 -1
  29. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +25 -24
  30. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -1
  31. package/dist/src/core/renderers/webgl/WebGlCoreShader.d.ts +2 -1
  32. package/dist/src/core/renderers/webgl/WebGlCoreShader.js +24 -24
  33. package/dist/src/core/renderers/webgl/WebGlCoreShader.js.map +1 -1
  34. package/dist/src/core/renderers/webgl/internal/RendererUtils.d.ts +8 -5
  35. package/dist/src/core/renderers/webgl/internal/RendererUtils.js +11 -13
  36. package/dist/src/core/renderers/webgl/internal/RendererUtils.js.map +1 -1
  37. package/dist/src/core/renderers/webgl/internal/ShaderUtils.d.ts +3 -2
  38. package/dist/src/core/renderers/webgl/internal/ShaderUtils.js +15 -15
  39. package/dist/src/core/renderers/webgl/internal/ShaderUtils.js.map +1 -1
  40. package/dist/src/core/renderers/webgl/shaders/DefaultShader.js +3 -6
  41. package/dist/src/core/renderers/webgl/shaders/DefaultShader.js.map +1 -1
  42. package/dist/src/core/renderers/webgl/shaders/DefaultShaderBatched.js +3 -3
  43. package/dist/src/core/renderers/webgl/shaders/DefaultShaderBatched.js.map +1 -1
  44. package/dist/src/core/renderers/webgl/shaders/DynamicShader.d.ts +1 -0
  45. package/dist/src/core/renderers/webgl/shaders/DynamicShader.js +32 -12
  46. package/dist/src/core/renderers/webgl/shaders/DynamicShader.js.map +1 -1
  47. package/dist/src/core/renderers/webgl/shaders/RoundedRectangle.js +3 -3
  48. package/dist/src/core/renderers/webgl/shaders/RoundedRectangle.js.map +1 -1
  49. package/dist/src/core/renderers/webgl/shaders/SdfShader.js +3 -3
  50. package/dist/src/core/renderers/webgl/shaders/SdfShader.js.map +1 -1
  51. package/dist/src/core/renderers/webgl/shaders/effects/BorderEffect.js +1 -1
  52. package/dist/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.d.ts +14 -1
  53. package/dist/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.js +15 -5
  54. package/dist/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.js.map +1 -1
  55. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js +3 -3
  56. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js.map +1 -1
  57. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.d.ts +2 -1
  58. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.js +4 -2
  59. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.js.map +1 -1
  60. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js +25 -0
  61. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +1 -1
  62. package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.d.ts +1 -1
  63. package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.js +6 -6
  64. package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.js.map +1 -1
  65. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +3 -2
  66. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +82 -50
  67. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
  68. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/findNearestMultiple.d.ts +8 -0
  69. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/findNearestMultiple.js +29 -0
  70. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/findNearestMultiple.js.map +1 -0
  71. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/getStartConditions.d.ts +4 -3
  72. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/getStartConditions.js +15 -11
  73. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/getStartConditions.js.map +1 -1
  74. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.d.ts +3 -2
  75. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js +30 -26
  76. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js.map +1 -1
  77. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/SdfBufferHelper.d.ts +19 -0
  78. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/SdfBufferHelper.js +84 -0
  79. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/SdfBufferHelper.js.map +1 -0
  80. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutLine.d.ts +8 -0
  81. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutLine.js +40 -0
  82. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutLine.js.map +1 -0
  83. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutText2.d.ts +2 -0
  84. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutText2.js +41 -0
  85. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/layoutText2.js.map +1 -0
  86. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/utils.d.ts +1 -0
  87. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/utils.js +4 -0
  88. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2/utils.js.map +1 -0
  89. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2.d.ts +1 -0
  90. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2.js +2 -0
  91. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText2.js.map +1 -0
  92. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/roundUpToMultiple.d.ts +9 -0
  93. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/roundUpToMultiple.js +32 -0
  94. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/roundUpToMultiple.js.map +1 -0
  95. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.d.ts +26 -0
  96. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.js +70 -0
  97. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.js.map +1 -0
  98. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/util.d.ts +16 -0
  99. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/util.js +39 -0
  100. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/util.js.map +1 -0
  101. package/dist/src/core/text-rendering/renderers/TextRenderer.d.ts +50 -0
  102. package/dist/src/core/text-rendering/renderers/TextRenderer.js +19 -0
  103. package/dist/src/core/text-rendering/renderers/TextRenderer.js.map +1 -1
  104. package/dist/src/core/textures/ImageTexture.js +14 -9
  105. package/dist/src/core/textures/ImageTexture.js.map +1 -1
  106. package/dist/src/core/utils.d.ts +1 -6
  107. package/dist/src/core/utils.js +3 -2
  108. package/dist/src/core/utils.js.map +1 -1
  109. package/dist/src/main-api/ICoreDriver.d.ts +2 -1
  110. package/dist/src/main-api/RendererMain.d.ts +25 -0
  111. package/dist/src/main-api/RendererMain.js +14 -5
  112. package/dist/src/main-api/RendererMain.js.map +1 -1
  113. package/dist/src/render-drivers/main/MainCoreDriver.d.ts +2 -1
  114. package/dist/src/render-drivers/main/MainCoreDriver.js +6 -4
  115. package/dist/src/render-drivers/main/MainCoreDriver.js.map +1 -1
  116. package/dist/src/render-drivers/main/MainOnlyTextNode.d.ts +10 -0
  117. package/dist/src/render-drivers/main/MainOnlyTextNode.js +45 -0
  118. package/dist/src/render-drivers/main/MainOnlyTextNode.js.map +1 -1
  119. package/dist/src/render-drivers/threadx/TextNodeStruct.d.ts +10 -0
  120. package/dist/src/render-drivers/threadx/TextNodeStruct.js +45 -0
  121. package/dist/src/render-drivers/threadx/TextNodeStruct.js.map +1 -1
  122. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.d.ts +2 -1
  123. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js +8 -1
  124. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js.map +1 -1
  125. package/dist/src/render-drivers/threadx/ThreadXMainTextNode.d.ts +5 -0
  126. package/dist/src/render-drivers/threadx/ThreadXMainTextNode.js +5 -0
  127. package/dist/src/render-drivers/threadx/ThreadXMainTextNode.js.map +1 -1
  128. package/dist/src/render-drivers/threadx/ThreadXRendererMessage.d.ts +4 -1
  129. package/dist/src/render-drivers/threadx/ThreadXRendererMessage.js.map +1 -1
  130. package/dist/src/render-drivers/threadx/worker/ThreadXRendererTextNode.d.ts +5 -0
  131. package/dist/src/render-drivers/threadx/worker/ThreadXRendererTextNode.js +10 -0
  132. package/dist/src/render-drivers/threadx/worker/ThreadXRendererTextNode.js.map +1 -1
  133. package/dist/src/render-drivers/threadx/worker/renderer.js +5 -3
  134. package/dist/src/render-drivers/threadx/worker/renderer.js.map +1 -1
  135. package/dist/src/utils.d.ts +2 -1
  136. package/dist/src/utils.js +22 -3
  137. package/dist/src/utils.js.map +1 -1
  138. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  139. package/package.json +3 -2
  140. package/src/common/CommonTypes.ts +9 -0
  141. package/src/core/CoreNode.ts +67 -34
  142. package/src/core/CoreTextNode.ts +56 -0
  143. package/src/core/CoreTextureManager.ts +4 -2
  144. package/src/core/Stage.ts +32 -3
  145. package/src/core/lib/ContextSpy.ts +41 -0
  146. package/src/core/lib/ImageWorker.ts +124 -0
  147. package/src/core/lib/WebGlContextWrapper.ts +965 -0
  148. package/src/core/renderers/webgl/WebGlCoreCtxSubTexture.ts +3 -2
  149. package/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +29 -28
  150. package/src/core/renderers/webgl/WebGlCoreRenderOp.ts +10 -14
  151. package/src/core/renderers/webgl/WebGlCoreRenderer.ts +26 -24
  152. package/src/core/renderers/webgl/WebGlCoreShader.ts +34 -25
  153. package/src/core/renderers/webgl/internal/RendererUtils.ts +13 -16
  154. package/src/core/renderers/webgl/internal/ShaderUtils.ts +16 -15
  155. package/src/core/renderers/webgl/shaders/DefaultShader.ts +3 -7
  156. package/src/core/renderers/webgl/shaders/DefaultShaderBatched.ts +3 -3
  157. package/src/core/renderers/webgl/shaders/DynamicShader.ts +42 -14
  158. package/src/core/renderers/webgl/shaders/RoundedRectangle.ts +3 -3
  159. package/src/core/renderers/webgl/shaders/SdfShader.ts +3 -3
  160. package/src/core/renderers/webgl/shaders/effects/BorderEffect.ts +1 -1
  161. package/src/core/renderers/webgl/shaders/effects/GrayscaleEffect.ts +35 -5
  162. package/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.ts +3 -3
  163. package/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.test.ts +9 -3
  164. package/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.ts +4 -2
  165. package/src/core/text-rendering/renderers/CanvasTextRenderer.ts +25 -0
  166. package/src/core/text-rendering/renderers/LightningTextTextureRenderer.ts +7 -7
  167. package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +115 -63
  168. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/getStartConditions.ts +26 -18
  169. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.ts +40 -28
  170. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/measureText.test.ts +6 -1
  171. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.test.ts +205 -0
  172. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/{makeRenderWindow.ts → setRenderWindow.ts} +50 -21
  173. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/util.ts +40 -0
  174. package/src/core/text-rendering/renderers/TextRenderer.ts +73 -0
  175. package/src/core/textures/ImageTexture.ts +17 -9
  176. package/src/core/utils.ts +87 -85
  177. package/src/env.d.ts +7 -0
  178. package/src/main-api/ICoreDriver.ts +2 -1
  179. package/src/main-api/RendererMain.ts +43 -5
  180. package/src/render-drivers/main/MainCoreDriver.ts +8 -5
  181. package/src/render-drivers/main/MainOnlyTextNode.ts +55 -1
  182. package/src/render-drivers/threadx/TextNodeStruct.ts +45 -0
  183. package/src/render-drivers/threadx/ThreadXCoreDriver.ts +10 -2
  184. package/src/render-drivers/threadx/ThreadXMainTextNode.ts +10 -0
  185. package/src/render-drivers/threadx/ThreadXRendererMessage.ts +5 -1
  186. package/src/render-drivers/threadx/worker/ThreadXRendererTextNode.ts +15 -0
  187. package/src/render-drivers/threadx/worker/renderer.ts +6 -4
  188. package/src/utils.ts +25 -4
  189. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/makeRenderWindow.test.ts +0 -136
@@ -23,6 +23,8 @@ import {
23
23
  type Rect,
24
24
  createBound,
25
25
  type BoundWithValid,
26
+ intersectRect,
27
+ isBoundPositive,
26
28
  } from '../../../lib/utils.js';
27
29
  import {
28
30
  TextRenderer,
@@ -35,7 +37,10 @@ import { SdfTrFontFace } from '../../font-face-types/SdfTrFontFace/SdfTrFontFace
35
37
  import { FLOATS_PER_GLYPH } from './internal/constants.js';
36
38
  import { getStartConditions } from './internal/getStartConditions.js';
37
39
  import { layoutText } from './internal/layoutText.js';
38
- import { makeRenderWindow } from './internal/makeRenderWindow.js';
40
+ import {
41
+ setRenderWindow,
42
+ type SdfRenderWindow,
43
+ } from './internal/setRenderWindow.js';
39
44
  import type { TrFontFace } from '../../font-face-types/TrFontFace.js';
40
45
  import { TrFontManager, type FontFamilyMap } from '../../TrFontManager.js';
41
46
  import { assertTruthy, mergeColorAlpha } from '../../../../utils.js';
@@ -77,7 +82,7 @@ export interface SdfTextRendererState extends TextRendererState {
77
82
  */
78
83
  lineCache: LineCacheItem[];
79
84
 
80
- renderWindow: Bound | undefined;
85
+ renderWindow: SdfRenderWindow;
81
86
 
82
87
  visibleWindow: BoundWithValid;
83
88
 
@@ -209,6 +214,26 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
209
214
  state.props.letterSpacing = value;
210
215
  this.invalidateLayoutCache(state);
211
216
  },
217
+ lineHeight: (state, value) => {
218
+ state.props.lineHeight = value;
219
+ this.invalidateLayoutCache(state);
220
+ },
221
+ maxLines: (state, value) => {
222
+ state.props.maxLines = value;
223
+ this.invalidateLayoutCache(state);
224
+ },
225
+ textBaseline: (state, value) => {
226
+ state.props.textBaseline = value;
227
+ this.invalidateLayoutCache(state);
228
+ },
229
+ verticalAlign: (state, value) => {
230
+ state.props.verticalAlign = value;
231
+ this.invalidateLayoutCache(state);
232
+ },
233
+ overflowSuffix: (state, value) => {
234
+ state.props.overflowSuffix = value;
235
+ this.invalidateLayoutCache(state);
236
+ },
212
237
  debug: (state, value) => {
213
238
  state.props.debug = value;
214
239
  },
@@ -262,7 +287,23 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
262
287
  emitter: new EventEmitter(),
263
288
  lineCache: [],
264
289
  forceFullLayoutCalc: false,
265
- renderWindow: undefined,
290
+ renderWindow: {
291
+ screen: {
292
+ x1: 0,
293
+ y1: 0,
294
+ x2: 0,
295
+ y2: 0,
296
+ },
297
+ sdf: {
298
+ x1: 0,
299
+ y1: 0,
300
+ x2: 0,
301
+ y2: 0,
302
+ },
303
+ firstLineIdx: 0,
304
+ numLines: 0,
305
+ valid: false,
306
+ },
266
307
  visibleWindow: {
267
308
  x1: 0,
268
309
  y1: 0,
@@ -325,22 +366,39 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
325
366
  // If the font is loaded then so should the data
326
367
  assertTruthy(trFontFace.data, 'Font face data should be loaded');
327
368
 
328
- const { text, fontSize, x, y, contain, width, height, scrollable } =
329
- state.props;
369
+ const {
370
+ text,
371
+ fontSize,
372
+ x,
373
+ y,
374
+ contain,
375
+ width,
376
+ height,
377
+ lineHeight,
378
+ verticalAlign,
379
+ scrollable,
380
+ overflowSuffix,
381
+ maxLines,
382
+ } = state.props;
330
383
 
331
384
  // scrollY only has an effect when contain === 'both' and scrollable === true
332
385
  const scrollY = contain === 'both' && scrollable ? state.props.scrollY : 0;
333
386
 
334
- let { renderWindow } = state;
387
+ const { renderWindow } = state;
335
388
 
336
- // Needed in renderWindow calculation
337
- const sdfLineHeight = trFontFace.data.info.size;
389
+ /**
390
+ * The font size of the SDF font face (the basis for SDF space units)
391
+ */
392
+ const sdfFontSize = trFontFace.data.info.size;
338
393
 
339
394
  /**
340
- * Divide screen space points by this to get the SDF space points
341
- * Mulitple SDF space points by this to get screen space points
395
+ * Divide screen space units by this to get the SDF space units
396
+ * Mulitple SDF space units by this to get screen space units
342
397
  */
343
- const fontSizeRatio = fontSize / sdfLineHeight;
398
+ const fontSizeRatio = fontSize / sdfFontSize;
399
+
400
+ // Needed in renderWindow calculation
401
+ const sdfLineHeight = lineHeight / fontSizeRatio;
344
402
 
345
403
  state.distanceRange =
346
404
  fontSizeRatio * trFontFace.data.distanceField.distanceRange;
@@ -373,44 +431,46 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
373
431
  // Return early if we're still viewing inside the established render window
374
432
  // No need to re-render what we've already rendered
375
433
  // (Only if there's an established renderWindow and we're not suppressing early exit)
376
- if (!forceFullLayoutCalc && renderWindow) {
434
+ if (!forceFullLayoutCalc && renderWindow.valid) {
435
+ const rwScreen = renderWindow.screen;
377
436
  if (
378
- x + renderWindow.x1 <= visibleWindow.x1 &&
379
- x + renderWindow.x2 >= visibleWindow.x2 &&
380
- y - scrollY + renderWindow.y1 <= visibleWindow.y1 &&
381
- y - scrollY + renderWindow.y2 >= visibleWindow.y2
437
+ x + rwScreen.x1 <= visibleWindow.x1 &&
438
+ x + rwScreen.x2 >= visibleWindow.x2 &&
439
+ y - scrollY + rwScreen.y1 <= visibleWindow.y1 &&
440
+ y - scrollY + rwScreen.y2 >= visibleWindow.y2
382
441
  ) {
383
442
  this.setStatus(state, 'loaded');
384
443
  return;
385
444
  }
386
- // Otherwise clear the renderWindow so it can be redone
387
- renderWindow = state.renderWindow = undefined;
445
+ // Otherwise invalidate the renderWindow so it can be redone
446
+ renderWindow.valid = false;
388
447
  this.setStatus(state, 'loading');
389
448
  }
390
449
 
391
450
  const { offsetY, textAlign } = state.props;
392
451
 
393
452
  // Create a new renderWindow if needed
394
- if (!renderWindow) {
395
- const visibleWindowHeight = visibleWindow.y2 - visibleWindow.y1;
396
- const maxLinesPerCanvasPage = Math.ceil(
397
- visibleWindowHeight / sdfLineHeight,
398
- );
399
- renderWindow = makeRenderWindow(
453
+ if (!renderWindow.valid) {
454
+ setRenderWindow(
455
+ renderWindow,
400
456
  x,
401
457
  y,
402
458
  scrollY,
403
- sdfLineHeight,
404
- maxLinesPerCanvasPage,
459
+ lineHeight,
460
+ visibleWindow.y2 - visibleWindow.y1,
405
461
  visibleWindow,
462
+ fontSizeRatio,
406
463
  );
464
+ // console.log('newRenderWindow', renderWindow);
407
465
  }
408
466
 
409
467
  const start = getStartConditions(
410
- fontSize,
468
+ sdfFontSize,
469
+ sdfLineHeight,
470
+ lineHeight,
471
+ verticalAlign,
411
472
  offsetY,
412
473
  fontSizeRatio,
413
- sdfLineHeight,
414
474
  renderWindow,
415
475
  lineCache,
416
476
  textH,
@@ -427,21 +487,24 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
427
487
 
428
488
  const out2 = layoutText(
429
489
  start.lineIndex,
430
- start.x,
431
- start.y,
490
+ start.sdfX,
491
+ start.sdfY,
432
492
  text,
433
493
  textAlign,
434
494
  width,
435
495
  height,
436
496
  fontSize,
497
+ lineHeight,
437
498
  letterSpacing,
438
499
  vertexBuffer,
439
500
  contain,
440
501
  lineCache,
441
- renderWindow,
502
+ renderWindow.sdf,
442
503
  trFontFace,
443
504
  forceFullLayoutCalc,
444
505
  scrollable,
506
+ overflowSuffix,
507
+ maxLines,
445
508
  );
446
509
 
447
510
  state.bufferUploaded = false;
@@ -476,14 +539,8 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
476
539
  return;
477
540
  }
478
541
 
479
- const drawStartTime = performance.now();
480
-
481
- const { sdfShader } = this;
482
-
483
542
  const { renderer } = this.stage;
484
543
 
485
- const { appWidth, appHeight } = this.stage.options;
486
-
487
544
  const { fontSize, color, contain, scrollable, zIndex, debug } = state.props;
488
545
 
489
546
  // scrollY only has an effect when contain === 'both' and scrollable === true
@@ -501,9 +558,9 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
501
558
  let { webGlBuffers } = state;
502
559
 
503
560
  if (!webGlBuffers) {
504
- const gl = renderer.gl;
561
+ const glw = renderer.glw;
505
562
  const stride = 4 * Float32Array.BYTES_PER_ELEMENT;
506
- const webGlBuffer = gl.createBuffer();
563
+ const webGlBuffer = glw.createBuffer();
507
564
  assertTruthy(webGlBuffer);
508
565
  state.webGlBuffers = new BufferCollection([
509
566
  {
@@ -512,7 +569,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
512
569
  a_position: {
513
570
  name: 'a_position',
514
571
  size: 2, // 2 components per iteration
515
- type: gl.FLOAT, // the data is 32bit floats
572
+ type: glw.FLOAT, // the data is 32bit floats
516
573
  normalized: false, // don't normalize the data
517
574
  stride, // 0 = move forward size * sizeof(type) each iteration to get the next position
518
575
  offset: 0, // start at the beginning of the buffer
@@ -520,7 +577,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
520
577
  a_textureCoordinate: {
521
578
  name: 'a_textureCoordinate',
522
579
  size: 2,
523
- type: gl.FLOAT,
580
+ type: glw.FLOAT,
524
581
  normalized: false,
525
582
  stride,
526
583
  offset: 2 * Float32Array.BYTES_PER_ELEMENT,
@@ -534,18 +591,30 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
534
591
  }
535
592
 
536
593
  if (!bufferUploaded) {
537
- const gl = renderer.gl;
594
+ const glw = renderer.glw;
538
595
 
539
596
  const buffer = webGlBuffers?.getBuffer('a_textureCoordinate') ?? null;
540
- gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
541
- gl.bufferData(gl.ARRAY_BUFFER, vertexBuffer, gl.STATIC_DRAW);
597
+ glw.arrayBufferData(buffer, vertexBuffer, glw.STATIC_DRAW);
542
598
  state.bufferUploaded = true;
543
599
  }
544
600
 
545
601
  assertTruthy(trFontFace);
546
602
 
603
+ if (scrollable && contain === 'both') {
604
+ const visibleWindowRect: Rect = {
605
+ x: state.visibleWindow.x1,
606
+ y: state.visibleWindow.y1,
607
+ width: state.visibleWindow.x2 - state.visibleWindow.x1,
608
+ height: state.visibleWindow.y2 - state.visibleWindow.y1,
609
+ };
610
+
611
+ clippingRect = clippingRect
612
+ ? intersectRect(clippingRect, visibleWindowRect)
613
+ : visibleWindowRect;
614
+ }
615
+
547
616
  const renderOp = new WebGlCoreRenderOp(
548
- renderer.gl,
617
+ renderer.glw,
549
618
  renderer.options,
550
619
  webGlBuffers,
551
620
  this.sdfShader,
@@ -577,23 +646,6 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
577
646
 
578
647
  renderer.addRenderOp(renderOp);
579
648
 
580
- // const elementRect = {
581
- // x: x,
582
- // y: y,
583
- // w: contain !== 'none' ? width : textW,
584
- // h: contain === 'both' ? height : textH,
585
- // };
586
-
587
- // const visibleRect = intersectRect(
588
- // {
589
- // x: 0,
590
- // y: 0,
591
- // w: renderer.w,
592
- // h: renderer.h,
593
- // },
594
- // elementRect,
595
- // );
596
-
597
649
  // if (!debug.disableScissor) {
598
650
  // renderer.enableScissor(
599
651
  // visibleRect.x,
@@ -677,7 +729,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
677
729
  */
678
730
  protected invalidateLayoutCache(state: SdfTextRendererState): void {
679
731
  state.visibleWindow.valid = false;
680
- state.renderWindow = undefined;
732
+ state.renderWindow.valid = false;
681
733
  state.textH = undefined;
682
734
  state.textW = undefined;
683
735
  state.lineCache = [];
@@ -17,8 +17,10 @@
17
17
  * limitations under the License.
18
18
  */
19
19
 
20
+ import type { Bound } from '../../../../lib/utils.js';
20
21
  import type { TrProps, TextRendererState } from '../../TextRenderer.js';
21
22
  import type { SdfTextRendererState } from '../SdfTextRenderer.js';
23
+ import type { SdfRenderWindow } from './setRenderWindow.js';
22
24
 
23
25
  /**
24
26
  * Gets the start conditions for the layout loop.
@@ -35,42 +37,48 @@ import type { SdfTextRendererState } from '../SdfTextRenderer.js';
35
37
  * @returns
36
38
  */
37
39
  export function getStartConditions(
38
- fontSize: TrProps['fontSize'],
40
+ sdfFontSize: number,
41
+ sdfLineHeight: number,
42
+ lineHeight: number,
43
+ verticalAlign: TrProps['verticalAlign'],
39
44
  offsetY: TrProps['offsetY'],
40
45
  fontSizeRatio: number,
41
- sdfLineHeight: number,
42
- renderWindow: SdfTextRendererState['renderWindow'],
46
+ renderWindow: SdfRenderWindow,
43
47
  lineCache: SdfTextRendererState['lineCache'],
44
48
  textH: TextRendererState['textH'],
45
49
  ):
46
50
  | {
47
- x: number;
48
- y: number;
51
+ sdfX: number;
52
+ sdfY: number;
49
53
  lineIndex: number;
50
54
  }
51
55
  | undefined {
52
56
  // State variables
53
- let startLineIndex = 0;
54
- if (renderWindow) {
55
- startLineIndex = Math.min(
56
- Math.max(Math.floor(renderWindow.y1 / fontSize), 0),
57
- lineCache.length,
58
- );
59
- }
57
+ const startLineIndex = Math.min(
58
+ Math.max(renderWindow.firstLineIdx, 0),
59
+ lineCache.length,
60
+ );
60
61
 
61
- // TODO: Possibly break out startX / startY into a separate function
62
62
  // TODO: (fontSize / 6.4286 / fontSizeRatio) Adding this to the startY helps the text line up better with Canvas rendered text
63
- const startX = 0;
64
- const startY = offsetY / fontSizeRatio + startLineIndex * sdfLineHeight; // TODO: Figure out what determines the initial y offset of text.
63
+ const sdfStartX = 0;
64
+ let sdfVerticalAlignYOffset = 0;
65
+ if (verticalAlign === 'middle') {
66
+ sdfVerticalAlignYOffset = (sdfLineHeight - sdfFontSize) / 2;
67
+ } else if (verticalAlign === 'bottom') {
68
+ sdfVerticalAlignYOffset = sdfLineHeight - sdfFontSize;
69
+ }
70
+ const sdfOffsetY = offsetY / fontSizeRatio;
71
+ const sdfStartY =
72
+ sdfOffsetY + startLineIndex * sdfLineHeight + sdfVerticalAlignYOffset; // TODO: Figure out what determines the initial y offset of text.
65
73
 
66
74
  // Don't attempt to render anything if we know we're starting past the established end of the text
67
- if (textH && startY >= textH / fontSizeRatio) {
75
+ if (textH && sdfStartY >= textH / fontSizeRatio) {
68
76
  return;
69
77
  }
70
78
 
71
79
  return {
72
- x: startX,
73
- y: startY,
80
+ sdfX: sdfStartX,
81
+ sdfY: sdfStartY,
74
82
  lineIndex: startLineIndex,
75
83
  };
76
84
  }
@@ -18,6 +18,7 @@
18
18
  */
19
19
 
20
20
  import { assertTruthy } from '../../../../../utils.js';
21
+ import type { Bound } from '../../../../lib/utils.js';
21
22
  import type {
22
23
  FontShaperProps,
23
24
  MappedGlyphInfo,
@@ -38,6 +39,7 @@ export function layoutText(
38
39
  width: TrProps['width'],
39
40
  height: TrProps['height'],
40
41
  fontSize: TrProps['fontSize'],
42
+ lineHeight: TrProps['lineHeight'],
41
43
  letterSpacing: TrProps['letterSpacing'],
42
44
  /**
43
45
  * Mutated
@@ -48,10 +50,12 @@ export function layoutText(
48
50
  * Mutated
49
51
  */
50
52
  lineCache: SdfTextRendererState['lineCache'],
51
- renderWindow: SdfTextRendererState['renderWindow'],
53
+ rwSdf: Bound,
52
54
  trFontFace: SdfTextRendererState['trFontFace'],
53
55
  forceFullLayoutCalc: TextRendererState['forceFullLayoutCalc'],
54
56
  scrollable: TrProps['scrollable'],
57
+ overflowSuffix: TrProps['overflowSuffix'],
58
+ maxLines: TrProps['maxLines'],
55
59
  ): {
56
60
  bufferNumFloats: number;
57
61
  bufferNumQuads: number;
@@ -75,13 +79,13 @@ export function layoutText(
75
79
  // We convert these to the vertex space by dividing them the `fontSizeRatio` factor.
76
80
 
77
81
  /**
78
- * `lineHeight` in vertex coordinates
82
+ * See above
79
83
  */
80
- const vertexLineHeight = trFontFace.data.info.size;
84
+ const fontSizeRatio = fontSize / trFontFace.data.info.size;
81
85
  /**
82
- * See above
86
+ * `lineHeight` in vertex coordinates
83
87
  */
84
- const fontSizeRatio = fontSize / vertexLineHeight;
88
+ const vertexLineHeight = lineHeight / fontSizeRatio;
85
89
  /**
86
90
  * `w` in vertex coordinates
87
91
  */
@@ -142,24 +146,32 @@ export function layoutText(
142
146
  bufferEnd: number;
143
147
  }[] = [];
144
148
 
145
- const truncateSeq = '...';
146
149
  const vertexTruncateHeight = height / fontSizeRatio;
147
- const truncateSeqVertexWidth = measureText(truncateSeq, shaperProps, shaper);
150
+ const overflowSuffVertexWidth = measureText(
151
+ overflowSuffix,
152
+ shaperProps,
153
+ shaper,
154
+ );
148
155
 
149
156
  // Line-by-line layout
150
157
  let moreLines = true;
151
158
  while (moreLines) {
152
159
  const nextLineWillFit =
153
- contain !== 'both' ||
154
- scrollable ||
155
- curY + vertexLineHeight + vertexLineHeight <= vertexTruncateHeight;
160
+ (maxLines === 0 || curLineIndex + 1 < maxLines) &&
161
+ (contain !== 'both' ||
162
+ scrollable ||
163
+ curY + vertexLineHeight + vertexLineHeight <= vertexTruncateHeight);
156
164
  const lineVertexW = nextLineWillFit
157
165
  ? vertexW
158
- : vertexW - truncateSeqVertexWidth;
166
+ : vertexW - overflowSuffVertexWidth;
159
167
  /**
160
168
  * Vertex X position to the beginning of the last word boundary. This becomes -1 when we start traversing a word.
161
169
  */
162
170
  let xStartLastWordBoundary = 0;
171
+
172
+ const lineIsBelowWindowTop = curY + vertexLineHeight >= rwSdf.y1;
173
+ const lineIsAboveWindowBottom = curY <= rwSdf.y2;
174
+ const lineIsWithinWindow = lineIsBelowWindowTop && lineIsAboveWindowBottom;
163
175
  // Layout glyphs in this line
164
176
  // Any break statements in this while loop will trigger a line break
165
177
  while ((glyphResult = glyphs.next()) && !glyphResult.done) {
@@ -220,7 +232,7 @@ export function layoutText(
220
232
  } else {
221
233
  glyphs = shaper.shapeText(
222
234
  shaperProps,
223
- new PeekableIterator(getUnicodeCodepoints(truncateSeq, 0), 0),
235
+ new PeekableIterator(getUnicodeCodepoints(overflowSuffix, 0), 0),
224
236
  );
225
237
  curX = lastWord.xStart;
226
238
  bufferOffset = lastWord.bufferOffset;
@@ -230,15 +242,8 @@ export function layoutText(
230
242
  const quadX = curX + glyph.xOffset;
231
243
  const quadY = curY + glyph.yOffset;
232
244
 
233
- const lineIsBelowWindowTop = renderWindow
234
- ? curY + vertexLineHeight >= renderWindow.y1 / fontSizeRatio
235
- : true;
236
- const lineIsAboveWindowBottom = renderWindow
237
- ? curY <= renderWindow.y2 / fontSizeRatio
238
- : true;
239
-
240
245
  // Only add to buffer for rendering if the line is within the render window
241
- if (lineIsBelowWindowTop && lineIsAboveWindowBottom) {
246
+ if (lineIsWithinWindow) {
242
247
  if (curLineBufferStart === -1) {
243
248
  curLineBufferStart = bufferOffset;
244
249
  }
@@ -288,7 +293,19 @@ export function layoutText(
288
293
 
289
294
  // Handle newlines
290
295
  if (glyph.codepoint === 10) {
291
- break;
296
+ if (nextLineWillFit) {
297
+ // The whole line fit, so we can break to the next line
298
+ break;
299
+ } else {
300
+ // The whole line won't fit, so we need to add the overflow suffix
301
+ glyphs = shaper.shapeText(
302
+ shaperProps,
303
+ new PeekableIterator(getUnicodeCodepoints(overflowSuffix, 0), 0),
304
+ );
305
+ // HACK: For the rest of the line when inserting the overflow suffix,
306
+ // set contain = 'none' to prevent an infinite loop.
307
+ contain = 'none';
308
+ }
292
309
  }
293
310
  }
294
311
  }
@@ -308,12 +325,7 @@ export function layoutText(
308
325
  xStartLastWordBoundary = 0;
309
326
 
310
327
  // Figure out if there are any more lines to render...
311
- if (
312
- !forceFullLayoutCalc &&
313
- contain === 'both' &&
314
- renderWindow &&
315
- curY > renderWindow.y2 / fontSizeRatio
316
- ) {
328
+ if (!forceFullLayoutCalc && contain === 'both' && curY > rwSdf.y2) {
317
329
  // Stop layout calculation early (for performance purposes) if:
318
330
  // - We're not forcing a full layout calculation (for width/height calculation)
319
331
  // - ...and we're containing the text vertically+horizontally (contain === 'both')
@@ -323,7 +335,7 @@ export function layoutText(
323
335
  } else if (glyphResult && glyphResult.done) {
324
336
  // If we've reached the end of the text, we know we're done
325
337
  moreLines = false;
326
- } else if (contain === 'both' && !scrollable && !nextLineWillFit) {
338
+ } else if (!nextLineWillFit) {
327
339
  // If we're contained vertically+horizontally (contain === 'both')
328
340
  // but not scrollable and the next line won't fit, we're done.
329
341
  moreLines = false;
@@ -25,10 +25,15 @@ import {
25
25
  type SdfFontData,
26
26
  } from '../../../font-face-types/SdfTrFontFace/internal/SdfFontShaper.js';
27
27
 
28
+ const glyphMap = new Map<number, SdfFontData['chars'][0]>();
29
+ sdfData.chars.forEach((glyph) => {
30
+ glyphMap.set(glyph.id, glyph);
31
+ });
32
+
28
33
  describe('measureText', () => {
29
34
  it('should measure text width', () => {
30
35
  const PERIOD_WIDTH = 10.332;
31
- const shaper = new SdfFontShaper(sdfData as unknown as SdfFontData);
36
+ const shaper = new SdfFontShaper(sdfData as unknown as SdfFontData, glyphMap);
32
37
  expect(measureText('', { letterSpacing: 0 }, shaper)).toBe(0);
33
38
  expect(measureText('.', { letterSpacing: 0 }, shaper)).toBe(PERIOD_WIDTH);
34
39
  expect(measureText('..', { letterSpacing: 0 }, shaper)).toBeCloseTo(