@lightningjs/renderer 3.0.2 → 3.0.4

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 (119) hide show
  1. package/README.md +56 -196
  2. package/dist/src/core/CoreNode.d.ts +2 -1
  3. package/dist/src/core/CoreNode.js +31 -7
  4. package/dist/src/core/CoreNode.js.map +1 -1
  5. package/dist/src/core/CoreTextNode.d.ts +26 -6
  6. package/dist/src/core/CoreTextNode.js +163 -60
  7. package/dist/src/core/CoreTextNode.js.map +1 -1
  8. package/dist/src/core/CoreTextureManager.d.ts +8 -0
  9. package/dist/src/core/CoreTextureManager.js +13 -1
  10. package/dist/src/core/CoreTextureManager.js.map +1 -1
  11. package/dist/src/core/Stage.d.ts +8 -0
  12. package/dist/src/core/Stage.js +23 -0
  13. package/dist/src/core/Stage.js.map +1 -1
  14. package/dist/src/core/TextureMemoryManager.d.ts +8 -13
  15. package/dist/src/core/TextureMemoryManager.js +22 -27
  16. package/dist/src/core/TextureMemoryManager.js.map +1 -1
  17. package/dist/src/core/lib/ImageWorker.d.ts +2 -2
  18. package/dist/src/core/lib/ImageWorker.js +31 -12
  19. package/dist/src/core/lib/ImageWorker.js.map +1 -1
  20. package/dist/src/core/lib/WebGlContextWrapper.d.ts +105 -56
  21. package/dist/src/core/lib/WebGlContextWrapper.js +164 -158
  22. package/dist/src/core/lib/WebGlContextWrapper.js.map +1 -1
  23. package/dist/src/core/lib/fps.d.ts +15 -0
  24. package/dist/src/core/lib/fps.js +62 -0
  25. package/dist/src/core/lib/fps.js.map +1 -0
  26. package/dist/src/core/lib/textureCompression.js +19 -10
  27. package/dist/src/core/lib/textureCompression.js.map +1 -1
  28. package/dist/src/core/lib/validateImageBitmap.d.ts +2 -1
  29. package/dist/src/core/lib/validateImageBitmap.js +4 -4
  30. package/dist/src/core/lib/validateImageBitmap.js.map +1 -1
  31. package/dist/src/core/platform.js +2 -2
  32. package/dist/src/core/platform.js.map +1 -1
  33. package/dist/src/core/platforms/Platform.d.ts +4 -0
  34. package/dist/src/core/platforms/Platform.js.map +1 -1
  35. package/dist/src/core/platforms/web/WebPlatform.d.ts +2 -0
  36. package/dist/src/core/platforms/web/WebPlatform.js +13 -0
  37. package/dist/src/core/platforms/web/WebPlatform.js.map +1 -1
  38. package/dist/src/core/renderers/CoreRenderer.d.ts +6 -0
  39. package/dist/src/core/renderers/CoreRenderer.js +8 -0
  40. package/dist/src/core/renderers/CoreRenderer.js.map +1 -1
  41. package/dist/src/core/renderers/canvas/CanvasRenderer.d.ts +1 -0
  42. package/dist/src/core/renderers/canvas/CanvasRenderer.js +5 -0
  43. package/dist/src/core/renderers/canvas/CanvasRenderer.js.map +1 -1
  44. package/dist/src/core/renderers/webgl/WebGlRenderOp.d.ts +45 -0
  45. package/dist/src/core/renderers/webgl/WebGlRenderOp.js +127 -0
  46. package/dist/src/core/renderers/webgl/WebGlRenderOp.js.map +1 -0
  47. package/dist/src/core/renderers/webgl/WebGlRenderer.d.ts +4 -2
  48. package/dist/src/core/renderers/webgl/WebGlRenderer.js +30 -22
  49. package/dist/src/core/renderers/webgl/WebGlRenderer.js.map +1 -1
  50. package/dist/src/core/renderers/webgl/WebGlShaderProgram.js +2 -3
  51. package/dist/src/core/renderers/webgl/WebGlShaderProgram.js.map +1 -1
  52. package/dist/src/core/text-rendering/CanvasFont.d.ts +14 -0
  53. package/dist/src/core/text-rendering/CanvasFont.js +120 -0
  54. package/dist/src/core/text-rendering/CanvasFont.js.map +1 -0
  55. package/dist/src/core/text-rendering/CanvasFontHandler.d.ts +1 -1
  56. package/dist/src/core/text-rendering/CanvasFontHandler.js +1 -1
  57. package/dist/src/core/text-rendering/CanvasFontHandler.js.map +1 -1
  58. package/dist/src/core/text-rendering/CanvasTextRenderer.d.ts +3 -5
  59. package/dist/src/core/text-rendering/CanvasTextRenderer.js +16 -22
  60. package/dist/src/core/text-rendering/CanvasTextRenderer.js.map +1 -1
  61. package/dist/src/core/text-rendering/CoreFont.d.ts +33 -0
  62. package/dist/src/core/text-rendering/CoreFont.js +48 -0
  63. package/dist/src/core/text-rendering/CoreFont.js.map +1 -0
  64. package/dist/src/core/text-rendering/FontManager.d.ts +11 -0
  65. package/dist/src/core/text-rendering/FontManager.js +41 -0
  66. package/dist/src/core/text-rendering/FontManager.js.map +1 -0
  67. package/dist/src/core/text-rendering/SdfFont.d.ts +29 -0
  68. package/dist/src/core/text-rendering/SdfFont.js +142 -0
  69. package/dist/src/core/text-rendering/SdfFont.js.map +1 -0
  70. package/dist/src/core/text-rendering/SdfTextRenderer.d.ts +4 -6
  71. package/dist/src/core/text-rendering/SdfTextRenderer.js +87 -168
  72. package/dist/src/core/text-rendering/SdfTextRenderer.js.map +1 -1
  73. package/dist/src/core/text-rendering/TextGenerator.d.ts +10 -0
  74. package/dist/src/core/text-rendering/TextGenerator.js +36 -0
  75. package/dist/src/core/text-rendering/TextGenerator.js.map +1 -0
  76. package/dist/src/core/text-rendering/TextLayoutEngine.js +43 -12
  77. package/dist/src/core/text-rendering/TextLayoutEngine.js.map +1 -1
  78. package/dist/src/core/text-rendering/TextRenderer.d.ts +41 -27
  79. package/dist/src/core/text-rendering/Utils.d.ts +2 -0
  80. package/dist/src/core/text-rendering/Utils.js +3 -0
  81. package/dist/src/core/text-rendering/Utils.js.map +1 -1
  82. package/dist/src/main-api/Inspector.d.ts +1 -1
  83. package/dist/src/main-api/Inspector.js +25 -20
  84. package/dist/src/main-api/Inspector.js.map +1 -1
  85. package/dist/src/main-api/Renderer.d.ts +14 -0
  86. package/dist/src/main-api/Renderer.js +29 -3
  87. package/dist/src/main-api/Renderer.js.map +1 -1
  88. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  89. package/package.json +2 -1
  90. package/src/core/CoreNode.test.ts +1 -1
  91. package/src/core/CoreNode.ts +37 -8
  92. package/src/core/CoreTextNode.test.ts +350 -0
  93. package/src/core/CoreTextNode.ts +201 -74
  94. package/src/core/CoreTextureManager.ts +14 -2
  95. package/src/core/Stage.ts +29 -0
  96. package/src/core/TextureMemoryManager.test.ts +134 -0
  97. package/src/core/TextureMemoryManager.ts +23 -30
  98. package/src/core/platforms/Platform.ts +5 -0
  99. package/src/core/platforms/web/WebPlatform.ts +13 -0
  100. package/src/core/renderers/CoreRenderer.ts +10 -0
  101. package/src/core/renderers/canvas/CanvasRenderer.ts +6 -0
  102. package/src/core/renderers/webgl/WebGlRenderer.rtt.test.ts +551 -0
  103. package/src/core/renderers/webgl/WebGlRenderer.ts +40 -31
  104. package/src/core/renderers/webgl/WebGlShaderProgram.test.ts +274 -0
  105. package/src/core/renderers/webgl/WebGlShaderProgram.ts +7 -7
  106. package/src/core/text-rendering/CanvasFontHandler.ts +2 -2
  107. package/src/core/text-rendering/CanvasTextRenderer.ts +24 -45
  108. package/src/core/text-rendering/SdfTextRenderer.ts +106 -215
  109. package/src/core/text-rendering/TextLayoutEngine.ts +61 -28
  110. package/src/core/text-rendering/TextRenderer.ts +42 -33
  111. package/src/core/text-rendering/Utils.ts +5 -1
  112. package/src/core/text-rendering/tests/TextLayoutEngine.test.ts +20 -0
  113. package/src/main-api/Inspector.ts +25 -25
  114. package/src/main-api/Renderer.test.ts +153 -0
  115. package/src/main-api/Renderer.ts +33 -3
  116. package/dist/src/core/renderers/webgl/WebGlCoreShader.destroy.d.ts +0 -1
  117. package/dist/src/core/renderers/webgl/WebGlCoreShader.destroy.js +0 -2
  118. package/dist/src/core/renderers/webgl/WebGlCoreShader.destroy.js.map +0 -1
  119. package/src/core/renderers/webgl/SdfRenderOp.ts +0 -106
@@ -247,6 +247,26 @@ describe('SDF Text Utils', () => {
247
247
  expect(result[0].length).toBeGreaterThan(1);
248
248
  expect(result[0][0]?.[0]).toBe('hello world test');
249
249
  });
250
+
251
+ it('should return non-empty output when maxWidth is smaller than glyph and suffix width', () => {
252
+ const result = wrapLine(
253
+ testMeasureText,
254
+ 'hello',
255
+ 'Arial',
256
+ 5,
257
+ 0,
258
+ 10,
259
+ '...',
260
+ 30,
261
+ 'break-word',
262
+ 1,
263
+ );
264
+
265
+ expect(result[0]).toHaveLength(1);
266
+ expect(result[0][0]?.[0]).toBe('...');
267
+ expect(result[0][0]?.[2]).toBe(true);
268
+ expect(result[1]).toBe(0);
269
+ });
250
270
  });
251
271
 
252
272
  describe('wrapText', () => {
@@ -740,12 +740,13 @@ export class Inspector {
740
740
  }
741
741
 
742
742
  createDiv(
743
- id: number,
743
+ node: CoreNode,
744
744
  properties: CoreNodeProps | CoreTextNodeProps,
745
745
  ): HTMLElement {
746
746
  const div = document.createElement('div');
747
747
  div.style.position = 'absolute';
748
- div.id = id.toString();
748
+ div.id = node.id.toString();
749
+ div.setAttribute('type', node.constructor.name);
749
750
 
750
751
  // set initial properties
751
752
  for (const key in properties) {
@@ -780,7 +781,7 @@ export class Inspector {
780
781
  }
781
782
 
782
783
  createNode(node: CoreNode): CoreNode {
783
- const div = this.createDiv(node.id, node.props);
784
+ const div = this.createDiv(node, node.props);
784
785
  (div as HTMLElement & { node: CoreNode }).node = node;
785
786
  (node as CoreNode & { div: HTMLElement }).div = div;
786
787
 
@@ -796,7 +797,7 @@ export class Inspector {
796
797
  // eslint-disable-next-line
797
798
  // @ts-ignore - textProps is a private property and keeping it that way
798
799
  // but we need it from the inspector to set the initial properties on the div element
799
- const div = this.createDiv(node.id, node.textProps);
800
+ const div = this.createDiv(node, node.textProps);
800
801
  (div as HTMLElement & { node: CoreNode }).node = node;
801
802
  (node as CoreTextNode & { div: HTMLElement }).div = div;
802
803
 
@@ -897,16 +898,16 @@ export class Inspector {
897
898
  };
898
899
  // Define traps for each property in knownProperties
899
900
  knownProperties.forEach((property) => {
900
- let originalProp = Object.getOwnPropertyDescriptor(node, property);
901
-
902
- if (originalProp === undefined) {
903
- // Search the prototype chain for the property descriptor
904
- const proto = Object.getPrototypeOf(node) as CoreNode | CoreTextNode;
905
- originalProp = Object.getOwnPropertyDescriptor(proto, property);
906
- }
907
-
908
- if (originalProp === undefined) {
909
- return;
901
+ let proto: CoreNode | ObjectConstructor | null = node;
902
+ let originalProp: PropertyDescriptor | undefined | null = Object.getOwnPropertyDescriptor(proto, property);
903
+
904
+ // Search the prototype chain for the property descriptor
905
+ while(originalProp === undefined) {
906
+ proto = Object.getPrototypeOf(proto) as ObjectConstructor;
907
+ if (proto === null) {
908
+ return;
909
+ }
910
+ originalProp = Object.getOwnPropertyDescriptor(proto, property);
910
911
  }
911
912
 
912
913
  if (property === 'text') {
@@ -1101,7 +1102,7 @@ export class Inspector {
1101
1102
  value: any,
1102
1103
  props: CoreNodeProps | CoreTextNodeProps,
1103
1104
  ) {
1104
- if (this.root === null || value === undefined || value === null) {
1105
+ if (this.root === null || value === undefined) {
1105
1106
  return;
1106
1107
  }
1107
1108
 
@@ -1109,17 +1110,16 @@ export class Inspector {
1109
1110
  * Special case for parent property
1110
1111
  */
1111
1112
  if (property === 'parent') {
1112
- const parentId: number = value.id;
1113
-
1114
- // only way to detect if the parent is the root node
1115
- // if you are reading this and have a better way, please let me know
1116
- if (parentId === 1) {
1117
- this.root.appendChild(div);
1118
- return;
1113
+ if (value) {
1114
+ // detect if the parent is the root node
1115
+ if (value.id === value.stage.root.id) {
1116
+ this.root.appendChild(div);
1117
+ } else {
1118
+ value.div.appendChild(div);
1119
+ }
1120
+ } else {
1121
+ div.parentNode?.removeChild(div);
1119
1122
  }
1120
-
1121
- const parent = document.getElementById(parentId.toString());
1122
- parent?.appendChild(div);
1123
1123
  return;
1124
1124
  }
1125
1125
 
@@ -0,0 +1,153 @@
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
+ // @vitest-environment happy-dom
21
+
22
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
23
+ import { RendererMain } from './Renderer.js';
24
+ import type { Platform } from '../core/platforms/Platform.js';
25
+ import type { Inspector } from './Inspector.js';
26
+ import type { CanvasRenderer } from '../core/renderers/canvas/CanvasRenderer.js';
27
+ import type { WebGlRenderer } from '../core/renderers/webgl/WebGlRenderer.js';
28
+
29
+ // Mock isProductionEnvironment so the Inspector gets initialized
30
+ vi.mock('../utils.js', async (importOriginal) => {
31
+ const actual = (await importOriginal()) as typeof import('../utils.js');
32
+ return {
33
+ ...actual,
34
+ isProductionEnvironment: false,
35
+ };
36
+ });
37
+
38
+ // Mock Stage to avoid real WebGL/Canvas renderer setup
39
+ vi.mock('../core/Stage.js', () => {
40
+ const mockStage = {
41
+ root: {
42
+ id: 1,
43
+ children: [],
44
+ props: {},
45
+ on: vi.fn(),
46
+ off: vi.fn(),
47
+ emit: vi.fn(),
48
+ },
49
+ destroy: vi.fn(),
50
+ on: vi.fn(),
51
+ off: vi.fn(),
52
+ emit: vi.fn(),
53
+ txManager: { destroy: vi.fn() },
54
+ };
55
+ return {
56
+ Stage: vi.fn(() => mockStage),
57
+ };
58
+ });
59
+
60
+ /**
61
+ * Minimal platform mock: returns a real <canvas> element so the Renderer
62
+ * can set its width/height and append it to the target.
63
+ */
64
+ function makeMockPlatform() {
65
+ const canvas = document.createElement('canvas');
66
+ return vi.fn().mockReturnValue({ canvas, settings: {}, glw: null });
67
+ }
68
+
69
+ /** Convenience: construct RendererMain with the minimum required settings. */
70
+ function makeRenderer(
71
+ target: HTMLElement,
72
+ extra: Partial<{
73
+ inspector: typeof Inspector | false;
74
+ }> = {},
75
+ ) {
76
+ const MockPlatform = makeMockPlatform();
77
+ return new RendererMain(
78
+ {
79
+ appWidth: 1920,
80
+ appHeight: 1080,
81
+ inspector: extra.inspector ?? false,
82
+ platform: MockPlatform as unknown as typeof Platform,
83
+ renderEngine: {} as unknown as
84
+ | typeof CanvasRenderer
85
+ | typeof WebGlRenderer,
86
+ },
87
+ target,
88
+ );
89
+ }
90
+
91
+ describe('RendererMain.close()', () => {
92
+ let target: HTMLDivElement;
93
+
94
+ beforeEach(() => {
95
+ target = document.createElement('div');
96
+ document.body.appendChild(target);
97
+ vi.clearAllMocks();
98
+ });
99
+
100
+ afterEach(() => {
101
+ target.remove();
102
+ });
103
+
104
+ it('should call inspector.destroy() when close() is called', () => {
105
+ const inspectorDestroy = vi.fn();
106
+ const MockInspector = vi.fn(() => ({ destroy: inspectorDestroy }));
107
+
108
+ const renderer = makeRenderer(target, {
109
+ inspector: MockInspector as unknown as typeof Inspector,
110
+ });
111
+
112
+ renderer.close();
113
+
114
+ expect(inspectorDestroy).toHaveBeenCalledOnce();
115
+ });
116
+
117
+ it('should call stage.destroy() when close() is called', async () => {
118
+ const { Stage } = await import('../core/Stage.js');
119
+ const renderer = makeRenderer(target);
120
+
121
+ const mockStageInstance = vi.mocked(Stage).mock.results[0]?.value as {
122
+ destroy: ReturnType<typeof vi.fn>;
123
+ };
124
+
125
+ renderer.close();
126
+
127
+ expect(mockStageInstance?.destroy).toHaveBeenCalledOnce();
128
+ });
129
+
130
+ it('should remove the canvas from the DOM when close() is called', () => {
131
+ const renderer = makeRenderer(target);
132
+ const canvas = renderer.canvas;
133
+
134
+ expect(target.contains(canvas)).toBe(true);
135
+
136
+ renderer.close();
137
+
138
+ expect(document.contains(canvas)).toBe(false);
139
+ });
140
+
141
+ it('should not throw when close() is called a second time', () => {
142
+ const MockInspector = vi.fn(() => ({ destroy: vi.fn() }));
143
+
144
+ const renderer = makeRenderer(target, {
145
+ inspector: MockInspector as unknown as typeof Inspector,
146
+ });
147
+
148
+ renderer.close();
149
+
150
+ // inspector is set to null after close(); the ?. guard must handle it
151
+ expect(() => renderer.close()).not.toThrow();
152
+ });
153
+ });
@@ -533,7 +533,7 @@ export class RendererMain extends EventEmitter {
533
533
  boundsMargin: settings.boundsMargin || 0,
534
534
  deviceLogicalPixelRatio: settings.deviceLogicalPixelRatio || 1,
535
535
  devicePhysicalPixelRatio:
536
- settings.devicePhysicalPixelRatio || window.devicePixelRatio || 1,
536
+ settings.devicePhysicalPixelRatio || this.windowDevicePixelRatio() || 1,
537
537
  clearColor: settings.clearColor ?? 0x00000000,
538
538
  fpsUpdateInterval: settings.fpsUpdateInterval || 0,
539
539
  enableClear: settings.enableClear ?? true,
@@ -582,8 +582,10 @@ export class RendererMain extends EventEmitter {
582
582
  this.canvas.width = deviceLogicalWidth * devicePhysicalPixelRatio;
583
583
  this.canvas.height = deviceLogicalHeight * devicePhysicalPixelRatio;
584
584
 
585
- this.canvas.style.width = `${deviceLogicalWidth}px`;
586
- this.canvas.style.height = `${deviceLogicalHeight}px`;
585
+ if (this.canvas.style) {
586
+ this.canvas.style.width = `${deviceLogicalWidth}px`;
587
+ this.canvas.style.height = `${deviceLogicalHeight}px`;
588
+ }
587
589
 
588
590
  // Initialize the stage
589
591
  this.stage = new Stage({
@@ -1027,4 +1029,32 @@ export class RendererMain extends EventEmitter {
1027
1029
  this.stage.options.targetFPS = fps > 0 ? fps : 0;
1028
1030
  this.stage.updateTargetFrameTime();
1029
1031
  }
1032
+
1033
+ private windowDevicePixelRatio() {
1034
+ return typeof window !== 'undefined' ? window.devicePixelRatio : undefined;
1035
+ }
1036
+
1037
+ /**
1038
+ * Close and destroy the renderer, releasing all resources.
1039
+ *
1040
+ * @remarks
1041
+ * This method performs a full teardown of the renderer:
1042
+ * - Stops the platform render loop
1043
+ * - Destroys all scene nodes (including text node font resources)
1044
+ * - Releases all texture memory and GPU resources
1045
+ * - Terminates image worker threads
1046
+ * - Removes the canvas element from the target div
1047
+ * - Destroys the inspector if active
1048
+ */
1049
+ close(): void {
1050
+ // Destroy the inspector first
1051
+ this.inspector?.destroy();
1052
+ this.inspector = null;
1053
+
1054
+ // Destroy the stage (stops loop, destroys nodes, releases textures/GPU)
1055
+ this.stage.destroy();
1056
+
1057
+ // Remove the canvas from the DOM
1058
+ this.canvas.remove();
1059
+ }
1030
1060
  }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=WebGlCoreShader.destroy.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"WebGlCoreShader.destroy.js","sourceRoot":"","sources":["../../../../../src/core/renderers/webgl/WebGlCoreShader.destroy.ts"],"names":[],"mappings":""}
@@ -1,106 +0,0 @@
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 { CoreRenderOp } from '../CoreRenderOp.js';
21
- import type { WebGlCtxTexture } from './WebGlCtxTexture.js';
22
- import type { WebGlRenderer } from './WebGlRenderer.js';
23
- import type { BufferCollection } from './internal/BufferCollection.js';
24
- import type { WebGlShaderNode } from './WebGlShaderNode.js';
25
- import type { RectWithValid } from '../../lib/utils.js';
26
- import type { Dimensions } from '../../../common/CommonTypes.js';
27
- import type { Stage } from '../../Stage.js';
28
-
29
- /**
30
- * Can render multiple quads with multiple textures (up to vertex shader texture limit)
31
- *
32
- */
33
- export class SdfRenderOp extends CoreRenderOp {
34
- public numQuads = 0;
35
- public readonly isCoreNode = false as const;
36
- public renderOpTextures: WebGlCtxTexture[] = [];
37
- public time: number = 0;
38
- readonly stage: Stage;
39
-
40
- constructor(
41
- readonly renderer: WebGlRenderer,
42
- readonly shader: WebGlShaderNode,
43
- readonly sdfShaderProps: Record<string, unknown>,
44
- readonly quadBufferCollection: BufferCollection,
45
- readonly worldAlpha: number,
46
- readonly clippingRect: RectWithValid,
47
- readonly w: number,
48
- readonly h: number,
49
- readonly rtt: boolean,
50
- readonly parentHasRenderTexture: boolean,
51
- readonly framebufferDimensions: Dimensions | null,
52
- ) {
53
- super();
54
- this.stage = renderer.stage;
55
- }
56
-
57
- addTexture(texture: WebGlCtxTexture): number {
58
- const { renderOpTextures } = this;
59
- const length = renderOpTextures.length;
60
-
61
- // We only support 1 texture (atlas) for SDF for now, but following the pattern
62
- for (let i = 0; i < length; i++) {
63
- if (renderOpTextures[i] === texture) {
64
- return i;
65
- }
66
- }
67
-
68
- renderOpTextures.push(texture);
69
- return length;
70
- }
71
-
72
- draw() {
73
- const { glw, stage } = this.renderer;
74
- const canvas = stage.platform!.canvas!;
75
-
76
- stage.shManager.useShader(this.shader.program);
77
- this.shader.program.bindRenderOp(this);
78
-
79
- // Clipping
80
- if (this.clippingRect.valid === true) {
81
- const pixelRatio = this.parentHasRenderTexture ? 1 : stage.pixelRatio;
82
- const clipX = Math.round(this.clippingRect.x * pixelRatio);
83
- const clipWidth = Math.round(this.clippingRect.w * pixelRatio);
84
- const clipHeight = Math.round(this.clippingRect.h * pixelRatio);
85
- let clipY = Math.round(
86
- canvas.height - clipHeight - this.clippingRect.y * pixelRatio,
87
- );
88
- // if parent has render texture, we need to adjust the scissor rect
89
- // to be relative to the parent's framebuffer
90
- if (this.parentHasRenderTexture) {
91
- clipY = this.framebufferDimensions
92
- ? this.framebufferDimensions.h - this.h
93
- : 0;
94
- }
95
-
96
- glw.setScissorTest(true);
97
- glw.scissor(clipX, clipY, clipWidth, clipHeight);
98
- } else {
99
- glw.setScissorTest(false);
100
- }
101
-
102
- // SDF rendering uses drawArrays with explicit triangle vertices (6 vertices per quad)
103
- // Note: buffers should be bound by bindRenderOp -> bindBufferCollection
104
- glw.drawArrays(glw.TRIANGLES, 0, 6 * this.numQuads);
105
- }
106
- }