@lightningjs/renderer 3.0.0-beta20 → 3.0.0-beta21

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 (152) hide show
  1. package/dist/src/core/CoreNode.d.ts +53 -7
  2. package/dist/src/core/CoreNode.js +175 -65
  3. package/dist/src/core/CoreNode.js.map +1 -1
  4. package/dist/src/core/CoreTextNode.d.ts +1 -1
  5. package/dist/src/core/CoreTextNode.js +3 -5
  6. package/dist/src/core/CoreTextNode.js.map +1 -1
  7. package/dist/src/core/CoreTextureManager.js +1 -1
  8. package/dist/src/core/CoreTextureManager.js.map +1 -1
  9. package/dist/src/core/Stage.d.ts +2 -1
  10. package/dist/src/core/Stage.js +9 -7
  11. package/dist/src/core/Stage.js.map +1 -1
  12. package/dist/src/core/TextureMemoryManager.d.ts +1 -1
  13. package/dist/src/core/TextureMemoryManager.js +3 -3
  14. package/dist/src/core/TextureMemoryManager.js.map +1 -1
  15. package/dist/src/core/animations/Animation.d.ts +21 -0
  16. package/dist/src/core/animations/Animation.js +194 -0
  17. package/dist/src/core/animations/Animation.js.map +1 -0
  18. package/dist/src/core/animations/Playback.d.ts +64 -0
  19. package/dist/src/core/animations/Playback.js +169 -0
  20. package/dist/src/core/animations/Playback.js.map +1 -0
  21. package/dist/src/core/animations/Transition.d.ts +27 -0
  22. package/dist/src/core/animations/Transition.js +52 -0
  23. package/dist/src/core/animations/Transition.js.map +1 -0
  24. package/dist/src/core/animations/utils.d.ts +2 -0
  25. package/dist/src/core/animations/utils.js +136 -0
  26. package/dist/src/core/animations/utils.js.map +1 -0
  27. package/dist/src/core/lib/ImageWorker.d.ts +2 -2
  28. package/dist/src/core/lib/ImageWorker.js +30 -11
  29. package/dist/src/core/lib/ImageWorker.js.map +1 -1
  30. package/dist/src/core/lib/WebGlContextWrapper.js +1 -1
  31. package/dist/src/core/lib/WebGlContextWrapper.js.map +1 -1
  32. package/dist/src/core/lib/utils.d.ts +6 -2
  33. package/dist/src/core/lib/utils.js +21 -21
  34. package/dist/src/core/lib/utils.js.map +1 -1
  35. package/dist/src/core/renderers/CoreRenderer.d.ts +1 -31
  36. package/dist/src/core/renderers/CoreRenderer.js.map +1 -1
  37. package/dist/src/core/renderers/CoreShaderNode.d.ts +4 -0
  38. package/dist/src/core/renderers/CoreShaderNode.js +15 -0
  39. package/dist/src/core/renderers/CoreShaderNode.js.map +1 -1
  40. package/dist/src/core/renderers/canvas/CanvasRenderer.d.ts +3 -3
  41. package/dist/src/core/renderers/canvas/CanvasRenderer.js +38 -33
  42. package/dist/src/core/renderers/canvas/CanvasRenderer.js.map +1 -1
  43. package/dist/src/core/renderers/canvas/CanvasShaderNode.d.ts +1 -2
  44. package/dist/src/core/renderers/canvas/CanvasShaderNode.js.map +1 -1
  45. package/dist/src/core/renderers/webgl/SdfRenderOp.d.ts +33 -0
  46. package/dist/src/core/renderers/webgl/SdfRenderOp.js +97 -0
  47. package/dist/src/core/renderers/webgl/SdfRenderOp.js.map +1 -0
  48. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +1 -1
  49. package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -1
  50. package/dist/src/core/renderers/webgl/WebGlCtxTexture.js +12 -8
  51. package/dist/src/core/renderers/webgl/WebGlCtxTexture.js.map +1 -1
  52. package/dist/src/core/renderers/webgl/WebGlRenderOp.d.ts +2 -3
  53. package/dist/src/core/renderers/webgl/WebGlRenderOp.js +1 -3
  54. package/dist/src/core/renderers/webgl/WebGlRenderOp.js.map +1 -1
  55. package/dist/src/core/renderers/webgl/WebGlRenderer.d.ts +6 -18
  56. package/dist/src/core/renderers/webgl/WebGlRenderer.js +48 -61
  57. package/dist/src/core/renderers/webgl/WebGlRenderer.js.map +1 -1
  58. package/dist/src/core/renderers/webgl/WebGlShaderNode.d.ts +2 -4
  59. package/dist/src/core/renderers/webgl/WebGlShaderNode.js.map +1 -1
  60. package/dist/src/core/renderers/webgl/WebGlShaderProgram.d.ts +3 -4
  61. package/dist/src/core/renderers/webgl/WebGlShaderProgram.js +40 -29
  62. package/dist/src/core/renderers/webgl/WebGlShaderProgram.js.map +1 -1
  63. package/dist/src/core/shaders/canvas/Border.d.ts +8 -2
  64. package/dist/src/core/shaders/canvas/Border.js +62 -23
  65. package/dist/src/core/shaders/canvas/Border.js.map +1 -1
  66. package/dist/src/core/shaders/canvas/HolePunch.js +2 -1
  67. package/dist/src/core/shaders/canvas/HolePunch.js.map +1 -1
  68. package/dist/src/core/shaders/canvas/LinearGradient.js +5 -3
  69. package/dist/src/core/shaders/canvas/LinearGradient.js.map +1 -1
  70. package/dist/src/core/shaders/canvas/RadialGradient.js +7 -5
  71. package/dist/src/core/shaders/canvas/RadialGradient.js.map +1 -1
  72. package/dist/src/core/shaders/canvas/Rounded.js +2 -2
  73. package/dist/src/core/shaders/canvas/Rounded.js.map +1 -1
  74. package/dist/src/core/shaders/canvas/RoundedWithBorder.d.ts +6 -3
  75. package/dist/src/core/shaders/canvas/RoundedWithBorder.js +39 -9
  76. package/dist/src/core/shaders/canvas/RoundedWithBorder.js.map +1 -1
  77. package/dist/src/core/shaders/canvas/RoundedWithBorderAndShadow.d.ts +2 -3
  78. package/dist/src/core/shaders/canvas/RoundedWithBorderAndShadow.js +44 -7
  79. package/dist/src/core/shaders/canvas/RoundedWithBorderAndShadow.js.map +1 -1
  80. package/dist/src/core/shaders/canvas/RoundedWithShadow.js +5 -4
  81. package/dist/src/core/shaders/canvas/RoundedWithShadow.js.map +1 -1
  82. package/dist/src/core/shaders/canvas/Shadow.js +4 -2
  83. package/dist/src/core/shaders/canvas/Shadow.js.map +1 -1
  84. package/dist/src/core/shaders/canvas/utils/render.d.ts +1 -1
  85. package/dist/src/core/shaders/canvas/utils/render.js +31 -18
  86. package/dist/src/core/shaders/canvas/utils/render.js.map +1 -1
  87. package/dist/src/core/shaders/templates/BorderTemplate.d.ts +10 -0
  88. package/dist/src/core/shaders/templates/BorderTemplate.js +20 -0
  89. package/dist/src/core/shaders/templates/BorderTemplate.js.map +1 -1
  90. package/dist/src/core/shaders/webgl/Border.js +72 -14
  91. package/dist/src/core/shaders/webgl/Border.js.map +1 -1
  92. package/dist/src/core/shaders/webgl/RoundedWithBorder.js +101 -31
  93. package/dist/src/core/shaders/webgl/RoundedWithBorder.js.map +1 -1
  94. package/dist/src/core/shaders/webgl/RoundedWithBorderAndShadow.js +102 -38
  95. package/dist/src/core/shaders/webgl/RoundedWithBorderAndShadow.js.map +1 -1
  96. package/dist/src/core/shaders/webgl/RoundedWithShadow.js +5 -4
  97. package/dist/src/core/shaders/webgl/RoundedWithShadow.js.map +1 -1
  98. package/dist/src/core/shaders/webgl/SdfShadowShader.d.ts +9 -0
  99. package/dist/src/core/shaders/webgl/SdfShadowShader.js +100 -0
  100. package/dist/src/core/shaders/webgl/SdfShadowShader.js.map +1 -0
  101. package/dist/src/core/shaders/webgl/Shadow.js +12 -6
  102. package/dist/src/core/shaders/webgl/Shadow.js.map +1 -1
  103. package/dist/src/core/text-rendering/SdfTextRenderer.js +12 -20
  104. package/dist/src/core/text-rendering/SdfTextRenderer.js.map +1 -1
  105. package/dist/src/core/utils.d.ts +1 -1
  106. package/dist/src/main-api/Inspector.d.ts +1 -1
  107. package/dist/src/main-api/Inspector.js +4 -1
  108. package/dist/src/main-api/Inspector.js.map +1 -1
  109. package/dist/src/main-api/Renderer.d.ts +10 -0
  110. package/dist/src/main-api/Renderer.js +2 -0
  111. package/dist/src/main-api/Renderer.js.map +1 -1
  112. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  113. package/package.json +1 -1
  114. package/src/core/Autosizer.ts +224 -0
  115. package/src/core/CoreNode.test.ts +116 -2
  116. package/src/core/CoreNode.ts +247 -78
  117. package/src/core/CoreTextNode.ts +3 -5
  118. package/src/core/CoreTextureManager.ts +1 -1
  119. package/src/core/Stage.ts +10 -7
  120. package/src/core/TextureMemoryManager.ts +3 -3
  121. package/src/core/lib/ImageWorker.ts +36 -11
  122. package/src/core/lib/WebGlContextWrapper.ts +1 -1
  123. package/src/core/lib/utils.ts +28 -25
  124. package/src/core/renderers/CoreRenderer.ts +1 -32
  125. package/src/core/renderers/CoreShaderNode.ts +20 -0
  126. package/src/core/renderers/canvas/CanvasRenderer.ts +43 -51
  127. package/src/core/renderers/canvas/CanvasShaderNode.ts +1 -2
  128. package/src/core/renderers/webgl/SdfRenderOp.ts +105 -0
  129. package/src/core/renderers/webgl/WebGlCtxTexture.ts +16 -9
  130. package/src/core/renderers/webgl/WebGlRenderer.ts +56 -78
  131. package/src/core/renderers/webgl/WebGlShaderNode.ts +2 -7
  132. package/src/core/renderers/webgl/WebGlShaderProgram.ts +48 -38
  133. package/src/core/shaders/canvas/Border.ts +86 -29
  134. package/src/core/shaders/canvas/HolePunch.ts +2 -1
  135. package/src/core/shaders/canvas/LinearGradient.ts +8 -6
  136. package/src/core/shaders/canvas/RadialGradient.ts +7 -10
  137. package/src/core/shaders/canvas/Rounded.ts +5 -5
  138. package/src/core/shaders/canvas/RoundedWithBorder.ts +68 -18
  139. package/src/core/shaders/canvas/RoundedWithBorderAndShadow.ts +71 -23
  140. package/src/core/shaders/canvas/RoundedWithShadow.ts +6 -5
  141. package/src/core/shaders/canvas/Shadow.ts +7 -5
  142. package/src/core/shaders/canvas/utils/render.ts +45 -36
  143. package/src/core/shaders/templates/BorderTemplate.ts +30 -0
  144. package/src/core/shaders/webgl/Border.ts +72 -15
  145. package/src/core/shaders/webgl/RoundedWithBorder.ts +101 -31
  146. package/src/core/shaders/webgl/RoundedWithBorderAndShadow.ts +102 -38
  147. package/src/core/shaders/webgl/RoundedWithShadow.ts +5 -4
  148. package/src/core/shaders/webgl/Shadow.ts +12 -6
  149. package/src/core/text-rendering/SdfTextRenderer.ts +18 -21
  150. package/src/main-api/Inspector.ts +6 -3
  151. package/src/main-api/Renderer.ts +13 -0
  152. package/src/core/renderers/webgl/WebGlRenderOp.ts +0 -170
@@ -178,7 +178,7 @@ export class ImageWorkerManager {
178
178
  imageWorkersEnabled = true;
179
179
  messageManager: Record<number, MessageCallback> = {};
180
180
  workers: Worker[] = [];
181
- workerIndex = 0;
181
+ workerLoad: number[] = [];
182
182
  nextId = 0;
183
183
 
184
184
  constructor(
@@ -189,14 +189,19 @@ export class ImageWorkerManager {
189
189
  numImageWorkers,
190
190
  createImageBitmapSupport,
191
191
  );
192
- this.workers.forEach((worker) => {
193
- worker.onmessage = this.handleMessage.bind(this);
192
+ this.workers.forEach((worker, index) => {
193
+ worker.onmessage = (event) => this.handleMessage(event, index);
194
194
  });
195
195
  }
196
196
 
197
- private handleMessage(event: MessageEvent) {
197
+ private handleMessage(event: MessageEvent, workerIndex: number) {
198
198
  const { id, data, error } = event.data as ImageWorkerMessage;
199
199
  const msg = this.messageManager[id];
200
+
201
+ if (this.workerLoad[workerIndex]) {
202
+ this.workerLoad[workerIndex]--;
203
+ }
204
+
200
205
  if (msg) {
201
206
  const [resolve, reject] = msg;
202
207
  delete this.messageManager[id];
@@ -242,14 +247,30 @@ export class ImageWorkerManager {
242
247
  const workers: Worker[] = [];
243
248
  for (let i = 0; i < numWorkers; i++) {
244
249
  workers.push(new Worker(blobURL));
250
+ this.workerLoad.push(0);
245
251
  }
246
252
  return workers;
247
253
  }
248
254
 
249
- private getNextWorker(): Worker | undefined {
250
- const worker = this.workers[this.workerIndex];
251
- this.workerIndex = (this.workerIndex + 1) % this.workers.length;
252
- return worker;
255
+ private getNextWorkerIndex(): number {
256
+ if (this.workers.length === 0) return -1;
257
+
258
+ let minLoad = 99;
259
+ let workerIndex = 0;
260
+
261
+ for (let i = 0; i < this.workers.length; i++) {
262
+ const load = this.workerLoad[i] || 0;
263
+
264
+ if (load === 0) {
265
+ return i;
266
+ }
267
+
268
+ if (load < minLoad) {
269
+ minLoad = load;
270
+ workerIndex = i;
271
+ }
272
+ }
273
+ return workerIndex;
253
274
  }
254
275
 
255
276
  getImage(
@@ -265,9 +286,13 @@ export class ImageWorkerManager {
265
286
  if (this.workers) {
266
287
  const id = this.nextId++;
267
288
  this.messageManager[id] = [resolve, reject];
268
- const nextWorker = this.getNextWorker();
269
- if (nextWorker) {
270
- nextWorker.postMessage({
289
+ const nextWorkerIndex = this.getNextWorkerIndex();
290
+
291
+ if (nextWorkerIndex !== -1) {
292
+ const worker = this.workers[nextWorkerIndex];
293
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
294
+ this.workerLoad[nextWorkerIndex]!++;
295
+ worker!.postMessage({
271
296
  id,
272
297
  src: src,
273
298
  premultiplyAlpha,
@@ -2,7 +2,7 @@
2
2
  /* eslint-disable @typescript-eslint/no-explicit-any */
3
3
  /* eslint-disable @typescript-eslint/no-unsafe-argument */
4
4
 
5
- import { assertTruthy } from '../../utils.js';
5
+ import { assertTruthy, isProductionEnvironment } from '../../utils.js';
6
6
  import type {
7
7
  Vec2,
8
8
  Vec3,
@@ -82,8 +82,8 @@ export function getRgbaString(color: RGBA) {
82
82
  export interface Rect {
83
83
  x: number;
84
84
  y: number;
85
- width: number;
86
- height: number;
85
+ w: number;
86
+ h: number;
87
87
  }
88
88
 
89
89
  export interface RectWithValid extends Rect {
@@ -97,6 +97,11 @@ export interface Bound {
97
97
  y2: number;
98
98
  }
99
99
 
100
+ export interface Coord {
101
+ x: number;
102
+ y: number;
103
+ }
104
+
100
105
  export interface BoundWithValid extends Bound {
101
106
  valid: boolean;
102
107
  }
@@ -154,15 +159,15 @@ export function convertBoundToRect(bound: Bound, out?: Rect): Rect {
154
159
  if (out) {
155
160
  out.x = bound.x1;
156
161
  out.y = bound.y1;
157
- out.width = bound.x2 - bound.x1;
158
- out.height = bound.y2 - bound.y1;
162
+ out.w = bound.x2 - bound.x1;
163
+ out.h = bound.y2 - bound.y1;
159
164
  return out;
160
165
  }
161
166
  return {
162
167
  x: bound.x1,
163
168
  y: bound.y1,
164
- width: bound.x2 - bound.x1,
165
- height: bound.y2 - bound.y1,
169
+ w: bound.x2 - bound.x1,
170
+ h: bound.y2 - bound.y1,
166
171
  };
167
172
  }
168
173
 
@@ -175,35 +180,35 @@ export function intersectRect<T extends Rect = Rect>(
175
180
  export function intersectRect(a: Rect, b: Rect, out?: Rect): Rect {
176
181
  const x = Math.max(a.x, b.x);
177
182
  const y = Math.max(a.y, b.y);
178
- const width = Math.min(a.x + a.width, b.x + b.width) - x;
179
- const height = Math.min(a.y + a.height, b.y + b.height) - y;
180
- if (width > 0 && height > 0) {
183
+ const w = Math.min(a.x + a.w, b.x + b.w) - x;
184
+ const h = Math.min(a.y + a.h, b.y + b.h) - y;
185
+ if (w > 0 && h > 0) {
181
186
  if (out) {
182
187
  out.x = x;
183
188
  out.y = y;
184
- out.width = width;
185
- out.height = height;
189
+ out.w = w;
190
+ out.h = h;
186
191
  return out;
187
192
  }
188
193
  return {
189
194
  x,
190
195
  y,
191
- width,
192
- height,
196
+ w,
197
+ h,
193
198
  };
194
199
  }
195
200
  if (out) {
196
201
  out.x = 0;
197
202
  out.y = 0;
198
- out.width = 0;
199
- out.height = 0;
203
+ out.w = 0;
204
+ out.h = 0;
200
205
  return out;
201
206
  }
202
207
  return {
203
208
  x: 0,
204
209
  y: 0,
205
- width: 0,
206
- height: 0,
210
+ w: 0,
211
+ h: 0,
207
212
  };
208
213
  }
209
214
 
@@ -213,15 +218,15 @@ export function copyRect(a: Rect, out?: Rect): Rect {
213
218
  if (out) {
214
219
  out.x = a.x;
215
220
  out.y = a.y;
216
- out.width = a.width;
217
- out.height = a.height;
221
+ out.w = a.w;
222
+ out.h = a.h;
218
223
  return out;
219
224
  }
220
225
  return {
221
226
  x: a.x,
222
227
  y: a.y,
223
- width: a.width,
224
- height: a.height,
228
+ w: a.w,
229
+ h: a.h,
225
230
  };
226
231
  }
227
232
 
@@ -232,9 +237,7 @@ export function compareRect(a: Rect | null, b: Rect | null): boolean {
232
237
  if (a === null || b === null) {
233
238
  return false;
234
239
  }
235
- return (
236
- a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height
237
- );
240
+ return a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h;
238
241
  }
239
242
 
240
243
  export function boundInsideBound(bound1: Bound, bound2: Bound) {
@@ -264,7 +267,7 @@ export function isBoundPositive(bound: Bound): boolean {
264
267
  }
265
268
 
266
269
  export function isRectPositive(rect: Rect): boolean {
267
- return rect.width > 0 && rect.height > 0;
270
+ return rect.w > 0 && rect.h > 0;
268
271
  }
269
272
 
270
273
  /**
@@ -17,45 +17,14 @@
17
17
  * limitations under the License.
18
18
  */
19
19
 
20
- import type { Dimensions } from '../../common/CommonTypes.js';
21
20
  import type { CoreNode } from '../CoreNode.js';
22
- import type { TextureOptions } from '../CoreTextureManager.js';
23
21
  import type { Stage } from '../Stage.js';
24
22
  import type { ContextSpy } from '../lib/ContextSpy.js';
25
- import type { RenderCoords } from '../lib/RenderCoords.js';
26
- import type { RectWithValid } from '../lib/utils.js';
27
23
  import type { CoreShaderProgram } from './CoreShaderProgram.js';
28
24
  import type { Texture, TextureCoords } from '../textures/Texture.js';
29
25
  import { CoreContextTexture } from './CoreContextTexture.js';
30
26
  import type { CoreShaderType, CoreShaderNode } from './CoreShaderNode.js';
31
27
 
32
- export interface QuadOptions {
33
- width: number;
34
- height: number;
35
- colorTl: number;
36
- colorTr: number;
37
- colorBl: number;
38
- colorBr: number;
39
- texture: Texture | null;
40
- textureOptions: TextureOptions | null;
41
- textureCoords: TextureCoords | undefined;
42
- zIndex: number;
43
- shader: CoreShaderNode | null;
44
- alpha: number;
45
- clippingRect: RectWithValid;
46
- tx: number;
47
- ty: number;
48
- ta: number;
49
- tb: number;
50
- tc: number;
51
- td: number;
52
- renderCoords?: RenderCoords;
53
- rtt: boolean;
54
- parentHasRenderTexture: boolean;
55
- framebufferDimensions: Dimensions | null;
56
- time?: number | null;
57
- }
58
-
59
28
  export interface CoreRendererOptions {
60
29
  stage: Stage;
61
30
  canvas: HTMLCanvasElement | OffscreenCanvas;
@@ -84,7 +53,7 @@ export abstract class CoreRenderer {
84
53
 
85
54
  abstract reset(): void;
86
55
  abstract render(surface?: 'screen' | CoreContextTexture): void;
87
- abstract addQuad(quad: QuadOptions): void;
56
+ abstract addQuad(node: CoreNode): void;
88
57
  abstract createCtxTexture(textureSource: Texture): CoreContextTexture;
89
58
  abstract createShaderProgram(
90
59
  shaderConfig: Readonly<CoreShaderType>,
@@ -96,6 +96,10 @@ export class CoreShaderNode<Props extends object = Record<string, unknown>> {
96
96
  protected node: CoreNode | null = null;
97
97
  readonly time: CoreShaderType['time'] = undefined;
98
98
  update: (() => void) | undefined = undefined;
99
+ private _valueKeyCache = '';
100
+ private _valueKeyDirty = true;
101
+ private _lastW = 0;
102
+ private _lastH = 0;
99
103
 
100
104
  constructor(
101
105
  readonly shaderKey: string,
@@ -141,6 +145,7 @@ export class CoreShaderNode<Props extends object = Record<string, unknown>> {
141
145
  } else {
142
146
  this.resolvedProps![key] = value;
143
147
  }
148
+ this._valueKeyDirty = true;
144
149
 
145
150
  if (this.update !== undefined && this.node !== null) {
146
151
  this.node.setUpdateType(UpdateType.RecalcUniforms);
@@ -158,12 +163,27 @@ export class CoreShaderNode<Props extends object = Record<string, unknown>> {
158
163
  }
159
164
 
160
165
  createValueKey() {
166
+ if (
167
+ this._valueKeyDirty === false &&
168
+ this.node !== null &&
169
+ this.node.w === this._lastW &&
170
+ this.node.h === this._lastH
171
+ ) {
172
+ return this._valueKeyCache;
173
+ }
174
+
161
175
  let valueKey = '';
162
176
  for (const key in this.resolvedProps) {
163
177
  valueKey += `${key}:${this.resolvedProps[key]!};`;
164
178
  }
165
179
  valueKey += `node-width:${this.node!.w}`;
166
180
  valueKey += `node-height:${this.node!.h}`;
181
+
182
+ this._valueKeyCache = valueKey;
183
+ this._valueKeyDirty = false;
184
+ this._lastW = this.node!.w;
185
+ this._lastH = this.node!.h;
186
+
167
187
  return valueKey;
168
188
  }
169
189
 
@@ -20,11 +20,7 @@ import type { CoreNode } from '../../CoreNode.js';
20
20
  import { SubTexture } from '../../textures/SubTexture.js';
21
21
  import { TextureType, type Texture } from '../../textures/Texture.js';
22
22
  import type { CoreContextTexture } from '../CoreContextTexture.js';
23
- import {
24
- CoreRenderer,
25
- type CoreRendererOptions,
26
- type QuadOptions,
27
- } from '../CoreRenderer.js';
23
+ import { CoreRenderer, type CoreRendererOptions } from '../CoreRenderer.js';
28
24
  import { CanvasTexture } from './CanvasTexture.js';
29
25
  import { parseColor } from '../../lib/colorParser.js';
30
26
  import { CanvasShaderNode, type CanvasShaderType } from './CanvasShaderNode.js';
@@ -66,10 +62,11 @@ export class CanvasRenderer extends CoreRenderer {
66
62
  // noop
67
63
  }
68
64
 
69
- addQuad(quad: QuadOptions): void {
65
+ addQuad(node: CoreNode): void {
70
66
  const ctx = this.context;
71
- const { tx, ty, ta, tb, tc, td, clippingRect } = quad;
72
- let texture = quad.texture;
67
+ const { tx, ty, ta, tb, tc, td } = node.globalTransform!;
68
+ const clippingRect = node.clippingRect;
69
+ let texture = (node.props.texture || this.stage.defaultTexture) as Texture;
73
70
  // The Canvas2D renderer only supports image textures, no textures are used for color blocks
74
71
  if (texture !== null) {
75
72
  const textureType = texture.type;
@@ -84,13 +81,13 @@ export class CanvasRenderer extends CoreRenderer {
84
81
  }
85
82
 
86
83
  const hasTransform = ta !== 1;
87
- const hasClipping = clippingRect.width !== 0 && clippingRect.height !== 0;
88
- const hasShader = quad.shader !== null;
84
+ const hasClipping = clippingRect.w !== 0 && clippingRect.h !== 0;
85
+ const shader = node.props.shader;
86
+ const hasShader = shader !== null;
89
87
 
90
88
  let saveAndRestore = hasTransform === true || hasClipping === true;
91
89
  if (hasShader === true) {
92
- saveAndRestore =
93
- saveAndRestore || (quad.shader as CanvasShaderNode).applySNR;
90
+ saveAndRestore = saveAndRestore || (shader as CanvasShaderNode).applySNR;
94
91
  }
95
92
 
96
93
  if (saveAndRestore) {
@@ -99,8 +96,8 @@ export class CanvasRenderer extends CoreRenderer {
99
96
 
100
97
  if (hasClipping === true) {
101
98
  const path = new Path2D();
102
- const { x, y, width, height } = clippingRect;
103
- path.rect(x, y, width, height);
99
+ const { x, y, w, h } = clippingRect;
100
+ path.rect(x, y, w, h);
104
101
  ctx.clip(path);
105
102
  }
106
103
 
@@ -121,12 +118,13 @@ export class CanvasRenderer extends CoreRenderer {
121
118
 
122
119
  if (hasShader === true) {
123
120
  let renderContext: (() => void) | null = () => {
124
- this.renderContext(quad);
121
+ this.renderContext(node, texture);
125
122
  };
126
- (quad.shader as CanvasShaderNode).render(ctx, quad, renderContext);
123
+
124
+ (shader as CanvasShaderNode).render(ctx, node, renderContext);
127
125
  renderContext = null;
128
126
  } else {
129
- this.renderContext(quad);
127
+ this.renderContext(node, texture);
130
128
  }
131
129
 
132
130
  if (saveAndRestore) {
@@ -134,22 +132,20 @@ export class CanvasRenderer extends CoreRenderer {
134
132
  }
135
133
  }
136
134
 
137
- renderContext(quad: QuadOptions) {
138
- const color = quad.colorTl;
139
- const texture = quad.texture!;
135
+ renderContext(node: CoreNode, texture: Texture) {
136
+ const color = node.premultipliedColorTl;
140
137
  const textureType = texture.type;
138
+ const tx = node.globalTransform!.tx;
139
+ const ty = node.globalTransform!.ty;
140
+ const width = node.props.w;
141
+ const height = node.props.h;
142
+
141
143
  if (textureType !== TextureType.color) {
142
144
  const tintColor = parseColor(color);
143
145
  if (textureType !== TextureType.subTexture) {
144
146
  const image = (texture.ctxTexture as CanvasTexture).getImage(tintColor);
145
- this.context.globalAlpha = tintColor.a ?? quad.alpha;
146
- this.context.drawImage(
147
- image,
148
- quad.tx,
149
- quad.ty,
150
- quad.width,
151
- quad.height,
152
- );
147
+ this.context.globalAlpha = tintColor.a ?? node.worldAlpha;
148
+ this.context.drawImage(image, tx, ty, width, height);
153
149
  this.context.globalAlpha = 1;
154
150
  return;
155
151
  }
@@ -158,51 +154,47 @@ export class CanvasRenderer extends CoreRenderer {
158
154
  ).getImage(tintColor);
159
155
  const props = (texture as SubTexture).props;
160
156
 
161
- this.context.globalAlpha = tintColor.a ?? quad.alpha;
157
+ this.context.globalAlpha = tintColor.a ?? node.worldAlpha;
162
158
  this.context.drawImage(
163
159
  image,
164
160
  props.x,
165
161
  props.y,
166
162
  props.w,
167
163
  props.h,
168
- quad.tx,
169
- quad.ty,
170
- quad.width,
171
- quad.height,
164
+ tx,
165
+ ty,
166
+ width,
167
+ height,
172
168
  );
173
169
  this.context.globalAlpha = 1;
174
170
  return;
175
171
  }
176
172
  const hasGradient =
177
- quad.colorTl !== quad.colorTr || quad.colorTl !== quad.colorBr;
173
+ node.premultipliedColorTl !== node.premultipliedColorTr ||
174
+ node.premultipliedColorTl !== node.premultipliedColorBr;
178
175
  if (hasGradient === true) {
179
- let endX: number = quad.tx;
180
- let endY: number = quad.ty;
176
+ let endX: number = tx;
177
+ let endY: number = ty;
181
178
  let endColor: number;
182
- if (quad.colorTl === quad.colorTr) {
179
+ if (node.premultipliedColorTl === node.premultipliedColorTr) {
183
180
  // vertical
184
- endX = quad.tx;
185
- endY = quad.ty + quad.height;
186
- endColor = quad.colorBr;
181
+ endX = tx;
182
+ endY = ty + height;
183
+ endColor = node.premultipliedColorBr;
187
184
  } else {
188
185
  // horizontal
189
- endX = quad.tx + quad.width;
190
- endY = quad.ty;
191
- endColor = quad.colorTr;
186
+ endX = tx + width;
187
+ endY = ty;
188
+ endColor = node.premultipliedColorTr;
192
189
  }
193
- const gradient = this.context.createLinearGradient(
194
- quad.tx,
195
- quad.ty,
196
- endX,
197
- endY,
198
- );
190
+ const gradient = this.context.createLinearGradient(tx, ty, endX, endY);
199
191
  gradient.addColorStop(0, normalizeCanvasColor(color));
200
192
  gradient.addColorStop(1, normalizeCanvasColor(endColor));
201
193
  this.context.fillStyle = gradient;
202
- this.context.fillRect(quad.tx, quad.ty, quad.width, quad.height);
194
+ this.context.fillRect(tx, ty, width, height);
203
195
  } else {
204
196
  this.context.fillStyle = normalizeCanvasColor(color);
205
- this.context.fillRect(quad.tx, quad.ty, quad.width, quad.height);
197
+ this.context.fillRect(tx, ty, width, height);
206
198
  }
207
199
  }
208
200
 
@@ -18,7 +18,6 @@
18
18
  import type { CoreNode } from '../../CoreNode.js';
19
19
  import { normalizeCanvasColor } from '../../lib/colorCache.js';
20
20
  import type { Stage } from '../../Stage.js';
21
- import type { QuadOptions } from '../CoreRenderer.js';
22
21
  import { CoreShaderNode, type CoreShaderType } from '../CoreShaderNode.js';
23
22
 
24
23
  export type CanvasShaderType<
@@ -28,7 +27,7 @@ export type CanvasShaderType<
28
27
  render: (
29
28
  this: CanvasShaderNode<T, C>,
30
29
  ctx: CanvasRenderingContext2D,
31
- quad: QuadOptions,
30
+ node: CoreNode,
32
31
  renderContext: () => void,
33
32
  ) => void;
34
33
  update?: (this: CanvasShaderNode<T, C>, node: CoreNode) => void;
@@ -0,0 +1,105 @@
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, options, stage } = this.renderer;
74
+
75
+ stage.shManager.useShader(this.shader.program);
76
+ this.shader.program.bindRenderOp(this);
77
+
78
+ // Clipping
79
+ if (this.clippingRect.valid === true) {
80
+ const pixelRatio = this.parentHasRenderTexture ? 1 : stage.pixelRatio;
81
+ const clipX = Math.round(this.clippingRect.x * pixelRatio);
82
+ const clipWidth = Math.round(this.clippingRect.w * pixelRatio);
83
+ const clipHeight = Math.round(this.clippingRect.h * pixelRatio);
84
+ let clipY = Math.round(
85
+ options.canvas.height - clipHeight - this.clippingRect.y * pixelRatio,
86
+ );
87
+ // if parent has render texture, we need to adjust the scissor rect
88
+ // to be relative to the parent's framebuffer
89
+ if (this.parentHasRenderTexture) {
90
+ clipY = this.framebufferDimensions
91
+ ? this.framebufferDimensions.h - this.h
92
+ : 0;
93
+ }
94
+
95
+ glw.setScissorTest(true);
96
+ glw.scissor(clipX, clipY, clipWidth, clipHeight);
97
+ } else {
98
+ glw.setScissorTest(false);
99
+ }
100
+
101
+ // SDF rendering uses drawArrays with explicit triangle vertices (6 vertices per quad)
102
+ // Note: buffers should be bound by bindRenderOp -> bindBufferCollection
103
+ glw.drawArrays(glw.TRIANGLES, 0, 6 * this.numQuads);
104
+ }
105
+ }
@@ -25,6 +25,7 @@ import { uploadCompressedTexture } from '../../lib/textureCompression.js';
25
25
  import { CoreContextTexture } from '../CoreContextTexture.js';
26
26
  import { isHTMLImageElement } from './internal/RendererUtils.js';
27
27
  import type { Bound } from '../../lib/utils.js';
28
+ import { isProductionEnvironment } from '../../../utils.js';
28
29
 
29
30
  const TRANSPARENT_TEXTURE_DATA = new Uint8Array([0, 0, 0, 0]);
30
31
 
@@ -69,11 +70,16 @@ export class WebGlCtxTexture extends CoreContextTexture {
69
70
  return true;
70
71
  }
71
72
 
72
- const error = this.glw.getError();
73
- if (error !== 0) {
74
- this.state = 'failed';
75
- this.textureSource.setState('failed', new Error(`WebGL Error: ${error}`));
76
- return true;
73
+ if (isProductionEnvironment === false) {
74
+ const error = this.glw.getError();
75
+ if (error !== 0) {
76
+ this.state = 'failed';
77
+ this.textureSource.setState(
78
+ 'failed',
79
+ new Error(`WebGL Error: ${error}`),
80
+ );
81
+ return true;
82
+ }
77
83
  }
78
84
  return false;
79
85
  }
@@ -187,11 +193,13 @@ export class WebGlCtxTexture extends CoreContextTexture {
187
193
  const format = glw.RGBA;
188
194
  const formatBytes = 4;
189
195
  const memoryPadding = 1.1; // Add padding to account for GPU Padding
196
+ const isImageBitmap =
197
+ typeof ImageBitmap !== 'undefined' && tdata instanceof ImageBitmap;
190
198
 
191
199
  // If textureData is null, the texture is empty (0, 0) and we don't need to
192
200
  // upload any data to the GPU.
193
201
  if (
194
- (typeof ImageBitmap !== 'undefined' && tdata instanceof ImageBitmap) ||
202
+ isImageBitmap ||
195
203
  tdata instanceof ImageData ||
196
204
  // not using typeof HTMLI mageElement due to web worker
197
205
  isHTMLImageElement(tdata) === true
@@ -201,7 +209,7 @@ export class WebGlCtxTexture extends CoreContextTexture {
201
209
  glw.bindTexture(this._nativeCtxTexture);
202
210
  glw.pixelStorei(
203
211
  glw.UNPACK_PREMULTIPLY_ALPHA_WEBGL,
204
- !!textureData.premultiplyAlpha,
212
+ isImageBitmap ? false : !!textureData.premultiplyAlpha,
205
213
  );
206
214
 
207
215
  glw.texImage2D(0, format, format, glw.UNSIGNED_BYTE, tdata);
@@ -265,9 +273,8 @@ export class WebGlCtxTexture extends CoreContextTexture {
265
273
 
266
274
  this.setTextureMemUse(w * h * formatBytes);
267
275
  } else {
268
- console.error(
276
+ throw new Error(
269
277
  `WebGlCoreCtxTexture.onLoadRequest: Unexpected textureData returned`,
270
- textureData,
271
278
  );
272
279
  }
273
280