@lightningjs/renderer 0.8.3 → 0.9.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 (177) hide show
  1. package/dist/src/core/CoreNode.d.ts +27 -1
  2. package/dist/src/core/CoreNode.js +130 -5
  3. package/dist/src/core/CoreNode.js.map +1 -1
  4. package/dist/src/core/CoreShaderManager.js +3 -2
  5. package/dist/src/core/CoreShaderManager.js.map +1 -1
  6. package/dist/src/core/CoreTextNode.js +20 -1
  7. package/dist/src/core/CoreTextNode.js.map +1 -1
  8. package/dist/src/core/CoreTextureManager.d.ts +2 -0
  9. package/dist/src/core/CoreTextureManager.js +2 -0
  10. package/dist/src/core/CoreTextureManager.js.map +1 -1
  11. package/dist/src/core/Stage.js +25 -9
  12. package/dist/src/core/Stage.js.map +1 -1
  13. package/dist/src/core/TextureMemoryManager.d.ts +1 -0
  14. package/dist/src/core/TextureMemoryManager.js +3 -1
  15. package/dist/src/core/TextureMemoryManager.js.map +1 -1
  16. package/dist/src/core/lib/ImageWorker.d.ts +2 -1
  17. package/dist/src/core/lib/ImageWorker.js +10 -6
  18. package/dist/src/core/lib/ImageWorker.js.map +1 -1
  19. package/dist/src/core/lib/Matrix3d.d.ts +38 -24
  20. package/dist/src/core/lib/Matrix3d.js +143 -166
  21. package/dist/src/core/lib/Matrix3d.js.map +1 -1
  22. package/dist/src/core/lib/WebGlContextWrapper.d.ts +26 -1
  23. package/dist/src/core/lib/WebGlContextWrapper.js +37 -1
  24. package/dist/src/core/lib/WebGlContextWrapper.js.map +1 -1
  25. package/dist/src/core/renderers/CoreRenderer.d.ts +11 -0
  26. package/dist/src/core/renderers/CoreRenderer.js +1 -0
  27. package/dist/src/core/renderers/CoreRenderer.js.map +1 -1
  28. package/dist/src/core/renderers/canvas/CanvasCoreRenderer.d.ts +10 -4
  29. package/dist/src/core/renderers/canvas/CanvasCoreRenderer.js +18 -7
  30. package/dist/src/core/renderers/canvas/CanvasCoreRenderer.js.map +1 -1
  31. package/dist/src/core/renderers/canvas/CanvasCoreTexture.d.ts +2 -2
  32. package/dist/src/core/renderers/canvas/CanvasCoreTexture.js +8 -6
  33. package/dist/src/core/renderers/canvas/CanvasCoreTexture.js.map +1 -1
  34. package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.d.ts +1 -1
  35. package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.js +1 -1
  36. package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.js.map +1 -1
  37. package/dist/src/core/renderers/canvas/internal/ColorUtils.js +4 -4
  38. package/dist/src/core/renderers/canvas/internal/ColorUtils.js.map +1 -1
  39. package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.d.ts +1 -1
  40. package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.js +2 -2
  41. package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.js.map +1 -1
  42. package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.d.ts +11 -0
  43. package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.js +51 -0
  44. package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.js.map +1 -0
  45. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.d.ts +11 -1
  46. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +22 -11
  47. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -1
  48. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.d.ts +4 -1
  49. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.js +7 -2
  50. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.js.map +1 -1
  51. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.d.ts +16 -1
  52. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +119 -27
  53. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -1
  54. package/dist/src/core/renderers/webgl/WebGlCoreShader.js +19 -4
  55. package/dist/src/core/renderers/webgl/WebGlCoreShader.js.map +1 -1
  56. package/dist/src/core/renderers/webgl/shaders/DefaultShader.js +4 -6
  57. package/dist/src/core/renderers/webgl/shaders/DefaultShader.js.map +1 -1
  58. package/dist/src/core/renderers/webgl/shaders/SdfShader.js +6 -1
  59. package/dist/src/core/renderers/webgl/shaders/SdfShader.js.map +1 -1
  60. package/dist/src/core/text-rendering/TextRenderingUtils.d.ts +12 -0
  61. package/dist/src/core/text-rendering/TextRenderingUtils.js +14 -0
  62. package/dist/src/core/text-rendering/TextRenderingUtils.js.map +1 -0
  63. package/dist/src/core/text-rendering/TextTextureRendererUtils.d.ts +19 -0
  64. package/dist/src/core/text-rendering/TextTextureRendererUtils.js +61 -0
  65. package/dist/src/core/text-rendering/TextTextureRendererUtils.js.map +1 -1
  66. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.d.ts +8 -2
  67. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js +24 -3
  68. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js.map +1 -1
  69. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.d.ts +2 -0
  70. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.js.map +1 -1
  71. package/dist/src/core/text-rendering/font-face-types/TrFontFace.d.ts +72 -1
  72. package/dist/src/core/text-rendering/font-face-types/TrFontFace.js +11 -1
  73. package/dist/src/core/text-rendering/font-face-types/TrFontFace.js.map +1 -1
  74. package/dist/src/core/text-rendering/font-face-types/WebTrFontFace.d.ts +5 -2
  75. package/dist/src/core/text-rendering/font-face-types/WebTrFontFace.js +4 -3
  76. package/dist/src/core/text-rendering/font-face-types/WebTrFontFace.js.map +1 -1
  77. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.d.ts +8 -0
  78. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js +42 -16
  79. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +1 -1
  80. package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.d.ts +7 -1
  81. package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.js +42 -11
  82. package/dist/src/core/text-rendering/renderers/LightningTextTextureRenderer.js.map +1 -1
  83. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +6 -1
  84. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +27 -9
  85. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
  86. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/getStartConditions.d.ts +2 -1
  87. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/getStartConditions.js +32 -5
  88. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/getStartConditions.js.map +1 -1
  89. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.d.ts +2 -1
  90. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js +2 -1
  91. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js.map +1 -1
  92. package/dist/src/core/text-rendering/renderers/TextRenderer.d.ts +10 -5
  93. package/dist/src/core/text-rendering/renderers/TextRenderer.js +6 -5
  94. package/dist/src/core/text-rendering/renderers/TextRenderer.js.map +1 -1
  95. package/dist/src/core/textures/RenderTexture.d.ts +28 -0
  96. package/dist/src/core/textures/RenderTexture.js +52 -0
  97. package/dist/src/core/textures/RenderTexture.js.map +1 -0
  98. package/dist/src/core/utils.d.ts +6 -1
  99. package/dist/src/core/utils.js +74 -82
  100. package/dist/src/core/utils.js.map +1 -1
  101. package/dist/src/main-api/INode.d.ts +9 -0
  102. package/dist/src/main-api/RendererMain.js +4 -3
  103. package/dist/src/main-api/RendererMain.js.map +1 -1
  104. package/dist/src/render-drivers/main/MainOnlyNode.d.ts +3 -0
  105. package/dist/src/render-drivers/main/MainOnlyNode.js +29 -0
  106. package/dist/src/render-drivers/main/MainOnlyNode.js.map +1 -1
  107. package/dist/src/render-drivers/main/MainOnlyTextNode.js +2 -3
  108. package/dist/src/render-drivers/main/MainOnlyTextNode.js.map +1 -1
  109. package/dist/src/render-drivers/threadx/NodeStruct.d.ts +3 -0
  110. package/dist/src/render-drivers/threadx/NodeStruct.js +9 -0
  111. package/dist/src/render-drivers/threadx/NodeStruct.js.map +1 -1
  112. package/dist/src/render-drivers/threadx/SharedNode.d.ts +1 -0
  113. package/dist/src/render-drivers/threadx/SharedNode.js +1 -0
  114. package/dist/src/render-drivers/threadx/SharedNode.js.map +1 -1
  115. package/dist/src/render-drivers/threadx/TextNodeStruct.js +3 -1
  116. package/dist/src/render-drivers/threadx/TextNodeStruct.js.map +1 -1
  117. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js +2 -0
  118. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js.map +1 -1
  119. package/dist/src/render-drivers/threadx/ThreadXMainNode.d.ts +3 -0
  120. package/dist/src/render-drivers/threadx/ThreadXMainNode.js +7 -0
  121. package/dist/src/render-drivers/threadx/ThreadXMainNode.js.map +1 -1
  122. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js +1 -0
  123. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js.map +1 -1
  124. package/dist/src/render-drivers/threadx/worker/ThreadXRendererTextNode.js +1 -0
  125. package/dist/src/render-drivers/threadx/worker/ThreadXRendererTextNode.js.map +1 -1
  126. package/dist/src/render-drivers/threadx/worker/renderer.js +1 -0
  127. package/dist/src/render-drivers/threadx/worker/renderer.js.map +1 -1
  128. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  129. package/package.json +2 -2
  130. package/src/core/CoreNode.ts +164 -8
  131. package/src/core/CoreShaderManager.ts +6 -3
  132. package/src/core/CoreTextNode.ts +25 -0
  133. package/src/core/CoreTextureManager.ts +3 -0
  134. package/src/core/Stage.ts +32 -11
  135. package/src/core/TextureMemoryManager.ts +3 -1
  136. package/src/core/lib/ImageWorker.ts +12 -7
  137. package/src/core/lib/Matrix3d.ts +144 -190
  138. package/src/core/lib/WebGlContextWrapper.ts +51 -1
  139. package/src/core/renderers/CoreRenderer.ts +15 -1
  140. package/src/core/renderers/canvas/CanvasCoreRenderer.ts +59 -14
  141. package/src/core/renderers/canvas/CanvasCoreTexture.ts +24 -18
  142. package/src/core/renderers/canvas/internal/C2DShaderUtils.ts +6 -3
  143. package/src/core/renderers/canvas/internal/ColorUtils.ts +6 -6
  144. package/src/core/renderers/canvas/shaders/UnsupportedShader.ts +2 -3
  145. package/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.ts +79 -0
  146. package/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +26 -24
  147. package/src/core/renderers/webgl/WebGlCoreRenderOp.ts +4 -2
  148. package/src/core/renderers/webgl/WebGlCoreRenderer.ts +166 -33
  149. package/src/core/renderers/webgl/WebGlCoreShader.ts +29 -4
  150. package/src/core/renderers/webgl/shaders/DefaultShader.ts +4 -6
  151. package/src/core/renderers/webgl/shaders/SdfShader.ts +6 -1
  152. package/src/core/text-rendering/TextRenderingUtils.ts +36 -0
  153. package/src/core/text-rendering/TextTextureRendererUtils.ts +74 -0
  154. package/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.ts +41 -12
  155. package/src/core/text-rendering/font-face-types/SdfTrFontFace/internal/SdfFontShaper.ts +2 -0
  156. package/src/core/text-rendering/font-face-types/TrFontFace.ts +86 -1
  157. package/src/core/text-rendering/font-face-types/WebTrFontFace.ts +13 -7
  158. package/src/core/text-rendering/renderers/CanvasTextRenderer.ts +52 -20
  159. package/src/core/text-rendering/renderers/LightningTextTextureRenderer.ts +59 -13
  160. package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +36 -8
  161. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/getStartConditions.ts +38 -5
  162. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.ts +5 -2
  163. package/src/core/text-rendering/renderers/TextRenderer.ts +17 -10
  164. package/src/core/textures/RenderTexture.ts +81 -0
  165. package/src/core/utils.ts +101 -93
  166. package/src/main-api/INode.ts +11 -0
  167. package/src/main-api/RendererMain.ts +4 -3
  168. package/src/render-drivers/main/MainOnlyNode.ts +44 -0
  169. package/src/render-drivers/main/MainOnlyTextNode.ts +2 -3
  170. package/src/render-drivers/threadx/NodeStruct.ts +10 -0
  171. package/src/render-drivers/threadx/SharedNode.ts +2 -0
  172. package/src/render-drivers/threadx/TextNodeStruct.ts +3 -1
  173. package/src/render-drivers/threadx/ThreadXCoreDriver.ts +2 -0
  174. package/src/render-drivers/threadx/ThreadXMainNode.ts +9 -0
  175. package/src/render-drivers/threadx/worker/ThreadXRendererNode.ts +1 -0
  176. package/src/render-drivers/threadx/worker/ThreadXRendererTextNode.ts +1 -0
  177. package/src/render-drivers/threadx/worker/renderer.ts +1 -0
@@ -64,13 +64,98 @@ export interface TrFontFaceDescriptors {
64
64
  variant?: string;
65
65
  }
66
66
 
67
+ export interface FontMetrics {
68
+ /**
69
+ * The distance, in font units, from the baseline to the highest point of the font.
70
+ */
71
+ ascender: number;
72
+ /**
73
+ * The distance, in font units, from the baseline to the lowest point of the font.
74
+ */
75
+ descender: number;
76
+ /**
77
+ * The additional space used in the calculation of the default line height in font units.
78
+ */
79
+ lineGap: number;
80
+ /**
81
+ * The number of font units per 1 EM.
82
+ */
83
+ unitsPerEm: number;
84
+ }
85
+
86
+ export interface NormalizedFontMetrics {
87
+ /**
88
+ * The distance, as a fraction of 1 EM, from the baseline to the highest point of the font.
89
+ *
90
+ * @remarks
91
+ * This value should be positive.
92
+ */
93
+ ascender: number;
94
+ /**
95
+ * The distance, as a fraction of 1 EM, from the baseline to the lowest point of the font.
96
+ *
97
+ * @remarks
98
+ * This value should be positive.
99
+ */
100
+ descender: number;
101
+ /**
102
+ * The additional space used in the calculation of the default line height as a fraction of 1 EM
103
+ *
104
+ * @remarks
105
+ * This value should be positive.
106
+ */
107
+ lineGap: number;
108
+ }
109
+
110
+ export interface TrFontFaceOptions {
111
+ fontFamily: string;
112
+ descriptors: Partial<TrFontFaceDescriptors>;
113
+ /**
114
+ * Font metrics used for layout and default line height calculations.
115
+ *
116
+ * @remarks
117
+ * Provides the font metrics in order to ensure consistent text layout across
118
+ * different font types (Canvas/Web, SDF, etc.).
119
+ *
120
+ * **Important**: We HIGHLY recommend providing these metrics for both SDF
121
+ * and Web/Canvas fonts. You can use the Lightning 3 [msdf-generator](https://github.com/lightning-js/msdf-generator/)
122
+ * tool to generate these metrics from a font file. The metrics generated by
123
+ * this tool can be used with both SDF and Web/Canvas fonts.
124
+ *
125
+ * If not provided, a warning will be logged and the text layout may not be
126
+ * consistent across different font types.
127
+ *
128
+ * If not provided, the metrics will be gathered depending on the font type.
129
+ * - For SDF fonts, the metrics will be gathered from the font atlas data.
130
+ * If the font is generated using Lightning 3's msdf-generator tool, the
131
+ * metrics will be the most accurate.
132
+ * - For Web/Canvas fonts, the metrics will be gathered from the
133
+ * CanvasRenderingContext2D.measureText() method. The accuracy/consistency
134
+ * of these metrics depends on the browser implementation. And will not
135
+ * guarantee the line-by-line alignment of text with SDF fonts.
136
+ */
137
+ metrics?: FontMetrics;
138
+ }
139
+
67
140
  export class TrFontFace extends EventEmitter {
68
141
  public readonly fontFamily: string;
69
142
  public readonly descriptors: TrFontFaceDescriptors;
70
143
  public readonly loaded: boolean = false;
144
+ public readonly metrics: NormalizedFontMetrics | null = null;
71
145
 
72
- constructor(fontFamily: string, descriptors: Partial<TrFontFaceDescriptors>) {
146
+ constructor(options: TrFontFaceOptions) {
73
147
  super();
148
+ const { fontFamily, descriptors, metrics } = options;
149
+
150
+ if (metrics) {
151
+ // Normalize metrics to be in the range of 0 to 1
152
+ this.metrics = {
153
+ ascender: metrics.ascender / metrics.unitsPerEm,
154
+ descender: metrics.descender / metrics.unitsPerEm,
155
+ lineGap: metrics.lineGap / metrics.unitsPerEm,
156
+ };
157
+ }
158
+
74
159
  this.fontFamily = fontFamily;
75
160
  this.descriptors = {
76
161
  style: 'normal',
@@ -17,7 +17,11 @@
17
17
  * limitations under the License.
18
18
  */
19
19
 
20
- import { TrFontFace, type TrFontFaceDescriptors } from './TrFontFace.js';
20
+ import {
21
+ TrFontFace,
22
+ type TrFontFaceDescriptors,
23
+ type TrFontFaceOptions,
24
+ } from './TrFontFace.js';
21
25
 
22
26
  declare module './TrFontFace.js' {
23
27
  interface TrFontFaceMap {
@@ -25,16 +29,18 @@ declare module './TrFontFace.js' {
25
29
  }
26
30
  }
27
31
 
32
+ export interface WebTrFontFaceOptions extends TrFontFaceOptions {
33
+ fontUrl: string;
34
+ }
35
+
28
36
  export class WebTrFontFace extends TrFontFace {
29
37
  public readonly fontFace: FontFace;
30
38
  public readonly fontUrl: string;
31
39
 
32
- constructor(
33
- fontFamily: string,
34
- descriptors: Partial<TrFontFaceDescriptors>,
35
- fontUrl: string,
36
- ) {
37
- super(fontFamily, descriptors);
40
+ constructor(options: WebTrFontFaceOptions) {
41
+ super(options);
42
+
43
+ const { fontFamily, fontUrl } = options;
38
44
 
39
45
  // Filter out parentheses from fontUrl
40
46
  const fontUrlWithoutParentheses = fontUrl.replace(/\(|\)/g, '');
@@ -33,6 +33,7 @@ import {
33
33
  type RectWithValid,
34
34
  } from '../../lib/utils.js';
35
35
  import type { ImageTexture } from '../../textures/ImageTexture.js';
36
+ import { TrFontManager, type FontFamilyMap } from '../TrFontManager.js';
36
37
  import type { TrFontFace } from '../font-face-types/TrFontFace.js';
37
38
  import { WebTrFontFace } from '../font-face-types/WebTrFontFace.js';
38
39
  import {
@@ -81,6 +82,7 @@ export interface CanvasTextRendererState extends TextRendererState {
81
82
  fontFaceLoadedHandler: (() => void) | undefined;
82
83
  fontInfo:
83
84
  | {
85
+ fontFace: WebTrFontFace;
84
86
  cssString: string;
85
87
  loaded: boolean;
86
88
  }
@@ -107,6 +109,12 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
107
109
  | OffscreenCanvasRenderingContext2D
108
110
  | CanvasRenderingContext2D;
109
111
  private rendererBounds: Bound;
112
+ /**
113
+ * Font family map used to store web font faces that were added to the
114
+ * canvas text renderer.
115
+ */
116
+ private fontFamilies: FontFamilyMap = {};
117
+ private fontFamilyArray: FontFamilyMap[] = [this.fontFamilies];
110
118
 
111
119
  constructor(stage: Stage) {
112
120
  super(stage);
@@ -135,6 +143,14 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
135
143
  x2: this.stage.options.appWidth,
136
144
  y2: this.stage.options.appHeight,
137
145
  };
146
+ // Install the default 'san-serif' font face
147
+ this.addFontFace(
148
+ new WebTrFontFace({
149
+ fontFamily: 'sans-serif',
150
+ descriptors: {},
151
+ fontUrl: '',
152
+ }),
153
+ );
138
154
  }
139
155
 
140
156
  //#region Overrides
@@ -259,10 +275,25 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
259
275
  // the `isFontFaceSupported` check)
260
276
  assertTruthy(fontFace instanceof WebTrFontFace);
261
277
 
262
- // We simply add the font face to the document
263
- // @ts-expect-error `add()` method should be available from a FontFaceSet
264
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call
265
- globalFontSet.add(fontFace.fontFace);
278
+ // Add the font face to the document
279
+ // Except for the 'sans-serif' font family, which the Renderer provides
280
+ // as a special default fallback.
281
+ if (fontFace.fontFamily !== 'sans-serif') {
282
+ // @ts-expect-error `add()` method should be available from a FontFaceSet
283
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
284
+ globalFontSet.add(fontFace.fontFace);
285
+ }
286
+
287
+ const { fontFamilies } = this;
288
+ const familyName = fontFace.fontFace.family;
289
+
290
+ let faceSet = fontFamilies[familyName];
291
+ if (!faceSet) {
292
+ faceSet = new Set();
293
+ fontFamilies[familyName] = faceSet;
294
+ }
295
+
296
+ faceSet.add(fontFace);
266
297
  }
267
298
 
268
299
  override createState(props: TrProps): CanvasTextRendererState {
@@ -312,7 +343,13 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
312
343
  // If fontInfo is invalid, we need to establish it
313
344
  if (!state.fontInfo) {
314
345
  const cssString = getFontCssString(state.props);
346
+ const trFontFace = TrFontManager.resolveFontFace(
347
+ this.fontFamilyArray,
348
+ state.props,
349
+ ) as WebTrFontFace | undefined;
350
+ assertTruthy(trFontFace, `Could not resolve font face for ${cssString}`);
315
351
  state.fontInfo = {
352
+ fontFace: trFontFace,
316
353
  cssString: cssString,
317
354
  // TODO: For efficiency we would use this here but it's not reliable on WPE -> document.fonts.check(cssString),
318
355
  loaded: false,
@@ -333,22 +370,11 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
333
370
  }
334
371
 
335
372
  if (!state.renderInfo) {
336
- const maxLines = state.props.maxLines;
337
- const containedMaxLines =
338
- state.props.contain === 'both'
339
- ? Math.floor(
340
- (state.props.height - state.props.offsetY) /
341
- state.props.lineHeight,
342
- )
343
- : 0;
344
- const calcMaxLines =
345
- containedMaxLines > 0 && maxLines > 0
346
- ? Math.min(containedMaxLines, maxLines)
347
- : Math.max(containedMaxLines, maxLines);
348
373
  state.lightning2TextRenderer.settings = {
349
374
  text: state.props.text,
350
375
  textAlign: state.props.textAlign,
351
- fontFace: state.props.fontFamily,
376
+ fontFamily: state.props.fontFamily,
377
+ trFontFace: state.fontInfo.fontFace,
352
378
  fontSize: state.props.fontSize,
353
379
  fontStyle: [
354
380
  state.props.fontStretch,
@@ -356,16 +382,21 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
356
382
  state.props.fontWeight,
357
383
  ].join(' '),
358
384
  textColor: getNormalizedRgbaComponents(state.props.color),
359
- offsetY: state.props.fontSize + state.props.offsetY,
385
+ offsetY: state.props.offsetY,
360
386
  wordWrap: state.props.contain !== 'none',
361
387
  wordWrapWidth:
362
388
  state.props.contain === 'none' ? undefined : state.props.width,
363
389
  letterSpacing: state.props.letterSpacing,
364
- lineHeight: state.props.lineHeight,
365
- maxLines: calcMaxLines,
390
+ lineHeight: state.props.lineHeight ?? null,
391
+ maxLines: state.props.maxLines,
392
+ maxHeight:
393
+ state.props.contain === 'both'
394
+ ? state.props.height - state.props.offsetY
395
+ : null,
366
396
  textBaseline: state.props.textBaseline,
367
397
  verticalAlign: state.props.verticalAlign,
368
398
  overflowSuffix: state.props.overflowSuffix,
399
+ w: state.props.contain !== 'none' ? state.props.width : undefined,
369
400
  };
370
401
  // const renderInfoCalculateTime = performance.now();
371
402
  state.renderInfo = state.lightning2TextRenderer.calculateRenderInfo();
@@ -712,6 +743,7 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
712
743
  }
713
744
 
714
745
  override destroyState(state: CanvasTextRendererState): void {
746
+ super.destroyState(state);
715
747
  // Remove state object owner from any canvas page textures
716
748
  state.canvasPages?.forEach((pageInfo) => {
717
749
  pageInfo.texture?.setRenderableOwner(state, false);
@@ -19,7 +19,12 @@
19
19
 
20
20
  /* eslint-disable @typescript-eslint/no-non-null-assertion */
21
21
 
22
+ import { assertTruthy } from '../../../utils.js';
22
23
  import { getRgbaString, type RGBA } from '../../lib/utils.js';
24
+ import { calcDefaultLineHeight } from '../TextRenderingUtils.js';
25
+ import { getWebFontMetrics } from '../TextTextureRendererUtils.js';
26
+ import type { NormalizedFontMetrics } from '../font-face-types/TrFontFace.js';
27
+ import type { WebTrFontFace } from '../font-face-types/WebTrFontFace.js';
23
28
 
24
29
  const MAX_TEXTURE_DIMENSION = 2048;
25
30
 
@@ -62,7 +67,8 @@ export interface Settings {
62
67
  fontStyle: string;
63
68
  fontSize: number;
64
69
  fontBaselineRatio: number;
65
- fontFace: string | null;
70
+ fontFamily: string | null;
71
+ trFontFace: WebTrFontFace | null;
66
72
  wordWrap: boolean;
67
73
  wordWrapWidth: number;
68
74
  wordBreak: boolean;
@@ -73,6 +79,7 @@ export interface Settings {
73
79
  verticalAlign: TextVerticalAlign;
74
80
  offsetY: number | null;
75
81
  maxLines: number;
82
+ maxHeight: number | null;
76
83
  overflowSuffix: string;
77
84
  precision: number;
78
85
  textColor: RGBA;
@@ -117,12 +124,14 @@ export interface RenderInfo {
117
124
  cutEx: number;
118
125
  cutEy: number;
119
126
  lineHeight: number;
127
+ defLineHeight: number;
120
128
  lineWidths: number[];
121
129
  offsetY: number;
122
130
  paddingLeft: number;
123
131
  paddingRight: number;
124
132
  letterSpacing: number;
125
133
  textIndent: number;
134
+ metrics: NormalizedFontMetrics;
126
135
  }
127
136
 
128
137
  /**
@@ -186,7 +195,7 @@ export class LightningTextTextureRenderer {
186
195
  }
187
196
 
188
197
  _getFontSetting() {
189
- const ff = [this._settings.fontFace];
198
+ const ff = [this._settings.fontFamily];
190
199
 
191
200
  const ffs = [];
192
201
  for (let i = 0, n = ff.length; i < n; i++) {
@@ -238,7 +247,6 @@ export class LightningTextTextureRenderer {
238
247
  this._settings.offsetY === null
239
248
  ? null
240
249
  : this._settings.offsetY * precision;
241
- let lineHeight = (this._settings.lineHeight || fontSize) * precision;
242
250
  const w = this._settings.w * precision;
243
251
  const h = this._settings.h * precision;
244
252
  let wordWrapWidth = this._settings.wordWrapWidth * precision;
@@ -248,10 +256,31 @@ export class LightningTextTextureRenderer {
248
256
  const cutEy = this._settings.cutEy * precision;
249
257
  const letterSpacing = (this._settings.letterSpacing || 0) * precision;
250
258
  const textIndent = this._settings.textIndent * precision;
259
+ const trFontFace = this._settings.trFontFace;
251
260
 
252
261
  // Set font properties.
253
262
  this.setFontProperties();
254
263
 
264
+ assertTruthy(trFontFace);
265
+ const metrics = getWebFontMetrics(this._context, trFontFace, fontSize);
266
+ const defLineHeight = calcDefaultLineHeight(metrics, fontSize) * precision;
267
+ const lineHeight =
268
+ this._settings.lineHeight !== null
269
+ ? this._settings.lineHeight * precision
270
+ : defLineHeight;
271
+
272
+ const maxHeight = this._settings.maxHeight;
273
+ const containedMaxLines =
274
+ maxHeight !== null && lineHeight > 0
275
+ ? Math.floor(maxHeight / lineHeight)
276
+ : 0;
277
+
278
+ const setMaxLines = this._settings.maxLines;
279
+ const calcMaxLines =
280
+ containedMaxLines > 0 && setMaxLines > 0
281
+ ? Math.min(containedMaxLines, setMaxLines)
282
+ : Math.max(containedMaxLines, setMaxLines);
283
+
255
284
  // Total width.
256
285
  let width = w || 2048 / this.getPrecision();
257
286
 
@@ -305,8 +334,8 @@ export class LightningTextTextureRenderer {
305
334
  }
306
335
  let lines = linesInfo.l;
307
336
 
308
- if (this._settings.maxLines && lines.length > this._settings.maxLines) {
309
- const usedLines = lines.slice(0, this._settings.maxLines);
337
+ if (calcMaxLines && lines.length > calcMaxLines) {
338
+ const usedLines = lines.slice(0, calcMaxLines);
310
339
 
311
340
  let otherLines = null;
312
341
  if (this._settings.overflowSuffix) {
@@ -333,7 +362,7 @@ export class LightningTextTextureRenderer {
333
362
  const n = lines.length;
334
363
  let j = 0;
335
364
  const m = linesInfo.n.length;
336
- for (i = this._settings.maxLines; i < n; i++) {
365
+ for (i = calcMaxLines; i < n; i++) {
337
366
  otherLines[j] += `${otherLines[j] ? ' ' : ''}${lines[i]!}`;
338
367
  if (i + 1 < m && linesInfo.n[i + 1]) {
339
368
  j++;
@@ -368,9 +397,6 @@ export class LightningTextTextureRenderer {
368
397
  innerWidth = maxLineWidth;
369
398
  }
370
399
 
371
- // calculate text height
372
- lineHeight = lineHeight || fontSize;
373
-
374
400
  let height;
375
401
  if (h) {
376
402
  height = h;
@@ -420,12 +446,14 @@ export class LightningTextTextureRenderer {
420
446
  renderInfo.cutEx = cutEx;
421
447
  renderInfo.cutEy = cutEy;
422
448
  renderInfo.lineHeight = lineHeight;
449
+ renderInfo.defLineHeight = defLineHeight;
423
450
  renderInfo.lineWidths = lineWidths;
424
451
  renderInfo.offsetY = offsetY;
425
452
  renderInfo.paddingLeft = paddingLeft;
426
453
  renderInfo.paddingRight = paddingRight;
427
454
  renderInfo.letterSpacing = letterSpacing;
428
455
  renderInfo.textIndent = textIndent;
456
+ renderInfo.metrics = metrics;
429
457
 
430
458
  return renderInfo as RenderInfo;
431
459
  }
@@ -477,17 +505,33 @@ export class LightningTextTextureRenderer {
477
505
 
478
506
  const drawLines = [];
479
507
 
508
+ const { metrics } = renderInfo;
509
+
510
+ /**
511
+ * Ascender (in pixels)
512
+ */
513
+ const ascenderPx = metrics
514
+ ? metrics.ascender * renderInfo.fontSize
515
+ : renderInfo.fontSize;
516
+
517
+ /**
518
+ * Bare line height is the distance between the ascender and descender of the font.
519
+ * without the line gap metric.
520
+ */
521
+ const bareLineHeightPx =
522
+ (metrics.ascender - metrics.descender) * renderInfo.fontSize;
523
+
480
524
  // Draw lines line by line.
481
525
  for (let i = 0, n = lines.length; i < n; i++) {
482
526
  linePositionX = i === 0 ? renderInfo.textIndent : 0;
483
527
 
484
528
  // By default, text is aligned to top
485
- linePositionY = i * renderInfo.lineHeight + renderInfo.offsetY;
529
+ linePositionY = i * renderInfo.lineHeight + ascenderPx;
486
530
 
487
531
  if (this._settings.verticalAlign == 'middle') {
488
- linePositionY += (renderInfo.lineHeight - renderInfo.fontSize) / 2;
532
+ linePositionY += (renderInfo.lineHeight - bareLineHeightPx) / 2;
489
533
  } else if (this._settings.verticalAlign == 'bottom') {
490
- linePositionY += renderInfo.lineHeight - renderInfo.fontSize;
534
+ linePositionY += renderInfo.lineHeight - bareLineHeightPx;
491
535
  }
492
536
 
493
537
  if (this._settings.textAlign === 'right') {
@@ -699,7 +743,8 @@ export class LightningTextTextureRenderer {
699
743
  h: 0,
700
744
  fontStyle: 'normal',
701
745
  fontSize: 40,
702
- fontFace: null,
746
+ fontFamily: null,
747
+ trFontFace: null,
703
748
  wordWrap: true,
704
749
  wordWrapWidth: 0,
705
750
  wordBreak: false,
@@ -710,6 +755,7 @@ export class LightningTextTextureRenderer {
710
755
  verticalAlign: 'top',
711
756
  offsetY: null,
712
757
  maxLines: 0,
758
+ maxHeight: null,
713
759
  overflowSuffix: '...',
714
760
  textColor: [1.0, 1.0, 1.0, 1.0],
715
761
  paddingLeft: 0,
@@ -56,7 +56,9 @@ import type {
56
56
  import type { WebGlCoreCtxTexture } from '../../../renderers/webgl/WebGlCoreCtxTexture.js';
57
57
  import { EventEmitter } from '../../../../common/EventEmitter.js';
58
58
  import type { Matrix3d } from '../../../lib/Matrix3d.js';
59
+ import type { Dimensions } from '../../../../common/CommonTypes.js';
59
60
  import { WebGlCoreRenderer } from '../../../renderers/webgl/WebGlCoreRenderer.js';
61
+ import { calcDefaultLineHeight } from '../../TextRenderingUtils.js';
60
62
 
61
63
  declare module '../TextRenderer.js' {
62
64
  interface TextRendererMap {
@@ -104,6 +106,11 @@ export interface SdfTextRendererState extends TextRendererState {
104
106
  distanceRange: number;
105
107
 
106
108
  trFontFace: SdfTrFontFace | undefined;
109
+
110
+ /**
111
+ * Resolved line height in logical screen pixel units
112
+ */
113
+ resLineHeight: number | undefined;
107
114
  }
108
115
 
109
116
  /**
@@ -250,6 +257,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
250
257
  },
251
258
  lineHeight: (state, value) => {
252
259
  state.props.lineHeight = value;
260
+ state.resLineHeight = undefined;
253
261
  this.invalidateLayoutCache(state);
254
262
  },
255
263
  maxLines: (state, value) => {
@@ -362,6 +370,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
362
370
  distanceRange: 0,
363
371
  trFontFace: undefined,
364
372
  isRenderable: false,
373
+ resLineHeight: undefined,
365
374
  debugData: {
366
375
  updateCount: 0,
367
376
  layoutCount: 0,
@@ -408,6 +417,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
408
417
 
409
418
  // If the font is loaded then so should the data
410
419
  assertTruthy(trFontFace.data, 'Font face data should be loaded');
420
+ assertTruthy(trFontFace.metrics, 'Font face metrics should be loaded');
411
421
 
412
422
  const {
413
423
  text,
@@ -417,7 +427,6 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
417
427
  contain,
418
428
  width,
419
429
  height,
420
- lineHeight,
421
430
  verticalAlign,
422
431
  scrollable,
423
432
  overflowSuffix,
@@ -440,8 +449,21 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
440
449
  */
441
450
  const fontSizeRatio = fontSize / sdfFontSize;
442
451
 
452
+ // If not already resolved, resolve the line height and store it in the state
453
+ let resLineHeight = state.resLineHeight;
454
+ if (resLineHeight === undefined) {
455
+ const lineHeight = state.props.lineHeight;
456
+ // If lineHeight is undefined, use the maxCharHeight from the font face
457
+ if (lineHeight === undefined) {
458
+ resLineHeight = calcDefaultLineHeight(trFontFace.metrics, fontSize);
459
+ } else {
460
+ resLineHeight = lineHeight;
461
+ }
462
+ state.resLineHeight = resLineHeight;
463
+ }
464
+
443
465
  // Needed in renderWindow calculation
444
- const sdfLineHeight = lineHeight / fontSizeRatio;
466
+ const sdfLineHeight = resLineHeight / fontSizeRatio;
445
467
 
446
468
  state.distanceRange =
447
469
  fontSizeRatio * trFontFace.data.distanceField.distanceRange;
@@ -498,7 +520,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
498
520
  x,
499
521
  y,
500
522
  scrollY,
501
- lineHeight,
523
+ resLineHeight,
502
524
  contain === 'both' ? elementBounds.y2 - elementBounds.y1 : 0,
503
525
  elementBounds,
504
526
  fontSizeRatio,
@@ -509,7 +531,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
509
531
  const start = getStartConditions(
510
532
  sdfFontSize,
511
533
  sdfLineHeight,
512
- lineHeight,
534
+ trFontFace,
513
535
  verticalAlign,
514
536
  offsetY,
515
537
  fontSizeRatio,
@@ -536,7 +558,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
536
558
  width,
537
559
  height,
538
560
  fontSize,
539
- lineHeight,
561
+ resLineHeight,
540
562
  letterSpacing,
541
563
  vertexBuffer,
542
564
  contain,
@@ -560,7 +582,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
560
582
  // If we didn't exit early, we know we have completely computed w/h
561
583
  if (out2.fullyProcessed) {
562
584
  state.textW = out2.maxX * fontSizeRatio;
563
- state.textH = out2.maxY * fontSizeRatio;
585
+ state.textH = out2.numLines * sdfLineHeight * fontSizeRatio;
564
586
  }
565
587
 
566
588
  // if (state.props.debug.printLayoutTime) {
@@ -575,13 +597,15 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
575
597
  transform: Matrix3d,
576
598
  clippingRect: Readonly<RectWithValid>,
577
599
  alpha: number,
600
+ parentHasRenderTexture: boolean,
601
+ framebufferDimensions: Dimensions,
578
602
  ): void {
579
603
  if (!state.vertexBuffer) {
580
604
  // Nothing to draw
581
605
  return;
582
606
  }
583
607
 
584
- const renderer: WebGlCoreRenderer = this.stage.renderer as WebGlCoreRenderer;
608
+ const renderer = this.stage.renderer;
585
609
  assertTruthy(renderer instanceof WebGlCoreRenderer);
586
610
 
587
611
  const { fontSize, color, contain, scrollable, zIndex, debug } = state.props;
@@ -666,7 +690,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
666
690
  webGlBuffers,
667
691
  this.sdfShader,
668
692
  {
669
- transform: transform.data,
693
+ transform: transform.getFloatArr(),
670
694
  // IMPORTANT: The SDF Shader expects the color NOT to be premultiplied
671
695
  // for the best blending results. Which is why we use `mergeColorAlpha`
672
696
  // instead of `mergeColorAlphaPremultiplied` here.
@@ -681,6 +705,9 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
681
705
  { height: textH, width: textW },
682
706
  0,
683
707
  zIndex,
708
+ false,
709
+ parentHasRenderTexture,
710
+ framebufferDimensions,
684
711
  );
685
712
 
686
713
  const texture = state.trFontFace?.texture;
@@ -773,6 +800,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
773
800
  * @param state
774
801
  */
775
802
  protected releaseFontFace(state: SdfTextRendererState) {
803
+ state.resLineHeight = undefined;
776
804
  if (state.trFontFace) {
777
805
  state.trFontFace.texture.setRenderableOwner(state, false);
778
806
  state.trFontFace = undefined;