@lightningjs/renderer 0.8.1 → 0.8.3

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 (100) hide show
  1. package/dist/src/core/CoreNode.d.ts +5 -0
  2. package/dist/src/core/CoreNode.js +35 -4
  3. package/dist/src/core/CoreNode.js.map +1 -1
  4. package/dist/src/core/CoreShaderManager.d.ts +2 -0
  5. package/dist/src/core/CoreShaderManager.js +8 -0
  6. package/dist/src/core/CoreShaderManager.js.map +1 -1
  7. package/dist/src/core/Stage.d.ts +3 -2
  8. package/dist/src/core/Stage.js +16 -5
  9. package/dist/src/core/Stage.js.map +1 -1
  10. package/dist/src/core/lib/ImageWorker.js +2 -2
  11. package/dist/src/core/renderers/CoreRenderer.d.ts +21 -3
  12. package/dist/src/core/renderers/CoreRenderer.js +13 -2
  13. package/dist/src/core/renderers/CoreRenderer.js.map +1 -1
  14. package/dist/src/core/renderers/canvas/CanvasCoreRenderer.d.ts +16 -0
  15. package/dist/src/core/renderers/canvas/CanvasCoreRenderer.js +155 -0
  16. package/dist/src/core/renderers/canvas/CanvasCoreRenderer.js.map +1 -0
  17. package/dist/src/core/renderers/canvas/CanvasCoreTexture.d.ts +17 -0
  18. package/dist/src/core/renderers/canvas/CanvasCoreTexture.js +122 -0
  19. package/dist/src/core/renderers/canvas/CanvasCoreTexture.js.map +1 -0
  20. package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.d.ts +5 -0
  21. package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.js +32 -0
  22. package/dist/src/core/renderers/canvas/internal/C2DShaderUtils.js.map +1 -0
  23. package/dist/src/core/renderers/canvas/internal/ColorUtils.d.ts +15 -0
  24. package/dist/src/core/renderers/canvas/internal/ColorUtils.js +45 -0
  25. package/dist/src/core/renderers/canvas/internal/ColorUtils.js.map +1 -0
  26. package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.d.ts +10 -0
  27. package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.js +43 -0
  28. package/dist/src/core/renderers/canvas/shaders/UnsupportedShader.js.map +1 -0
  29. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.d.ts +2 -20
  30. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +3 -15
  31. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -1
  32. package/dist/src/core/renderers/webgl/shaders/DynamicShader.d.ts +1 -0
  33. package/dist/src/core/renderers/webgl/shaders/DynamicShader.js +23 -0
  34. package/dist/src/core/renderers/webgl/shaders/DynamicShader.js.map +1 -1
  35. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +3 -1
  36. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
  37. package/dist/src/core/textures/ImageTexture.js +3 -0
  38. package/dist/src/core/textures/ImageTexture.js.map +1 -1
  39. package/dist/src/main-api/INode.d.ts +13 -0
  40. package/dist/src/main-api/Inspector.js +11 -1
  41. package/dist/src/main-api/Inspector.js.map +1 -1
  42. package/dist/src/main-api/RendererMain.d.ts +4 -0
  43. package/dist/src/main-api/RendererMain.js +2 -0
  44. package/dist/src/main-api/RendererMain.js.map +1 -1
  45. package/dist/src/render-drivers/main/MainCoreDriver.js +1 -0
  46. package/dist/src/render-drivers/main/MainCoreDriver.js.map +1 -1
  47. package/dist/src/render-drivers/main/MainOnlyNode.d.ts +2 -0
  48. package/dist/src/render-drivers/main/MainOnlyNode.js +7 -0
  49. package/dist/src/render-drivers/main/MainOnlyNode.js.map +1 -1
  50. package/dist/src/render-drivers/main/MainOnlyTextNode.js +1 -0
  51. package/dist/src/render-drivers/main/MainOnlyTextNode.js.map +1 -1
  52. package/dist/src/render-drivers/threadx/NodeStruct.d.ts +3 -0
  53. package/dist/src/render-drivers/threadx/NodeStruct.js +9 -0
  54. package/dist/src/render-drivers/threadx/NodeStruct.js.map +1 -1
  55. package/dist/src/render-drivers/threadx/SharedNode.d.ts +1 -0
  56. package/dist/src/render-drivers/threadx/SharedNode.js +1 -0
  57. package/dist/src/render-drivers/threadx/SharedNode.js.map +1 -1
  58. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js +2 -0
  59. package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js.map +1 -1
  60. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js +1 -0
  61. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js.map +1 -1
  62. package/dist/src/render-drivers/threadx/worker/ThreadXRendererTextNode.js +1 -0
  63. package/dist/src/render-drivers/threadx/worker/ThreadXRendererTextNode.js.map +1 -1
  64. package/dist/src/render-drivers/threadx/worker/renderer.js +2 -0
  65. package/dist/src/render-drivers/threadx/worker/renderer.js.map +1 -1
  66. package/dist/src/render-drivers/utils.js +1 -1
  67. package/dist/src/render-drivers/utils.js.map +1 -1
  68. package/dist/src/utils.d.ts +20 -6
  69. package/dist/src/utils.js +29 -9
  70. package/dist/src/utils.js.map +1 -1
  71. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  72. package/package.json +1 -1
  73. package/src/core/CoreNode.ts +49 -4
  74. package/src/core/CoreShaderManager.ts +10 -0
  75. package/src/core/Stage.ts +19 -5
  76. package/src/core/lib/ImageWorker.ts +2 -2
  77. package/src/core/renderers/CoreRenderer.ts +32 -6
  78. package/src/core/renderers/canvas/CanvasCoreRenderer.ts +178 -0
  79. package/src/core/renderers/canvas/CanvasCoreTexture.ts +138 -0
  80. package/src/core/renderers/canvas/internal/C2DShaderUtils.ts +34 -0
  81. package/src/core/renderers/canvas/internal/ColorUtils.ts +55 -0
  82. package/src/core/renderers/canvas/shaders/UnsupportedShader.ts +49 -0
  83. package/src/core/renderers/webgl/WebGlCoreRenderer.ts +8 -38
  84. package/src/core/renderers/webgl/shaders/DynamicShader.ts +31 -0
  85. package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +3 -1
  86. package/src/core/textures/ImageTexture.ts +3 -0
  87. package/src/main-api/INode.ts +13 -0
  88. package/src/main-api/Inspector.ts +11 -1
  89. package/src/main-api/RendererMain.ts +8 -0
  90. package/src/render-drivers/main/MainCoreDriver.ts +1 -0
  91. package/src/render-drivers/main/MainOnlyNode.ts +9 -0
  92. package/src/render-drivers/main/MainOnlyTextNode.ts +1 -0
  93. package/src/render-drivers/threadx/NodeStruct.ts +10 -0
  94. package/src/render-drivers/threadx/SharedNode.ts +2 -0
  95. package/src/render-drivers/threadx/ThreadXCoreDriver.ts +2 -0
  96. package/src/render-drivers/threadx/worker/ThreadXRendererNode.ts +1 -0
  97. package/src/render-drivers/threadx/worker/ThreadXRendererTextNode.ts +1 -0
  98. package/src/render-drivers/threadx/worker/renderer.ts +2 -0
  99. package/src/render-drivers/utils.ts +1 -1
  100. package/src/utils.ts +32 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightningjs/renderer",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "description": "Lightning 3 Renderer",
5
5
  "type": "module",
6
6
  "main": "./dist/exports/index.js",
@@ -17,7 +17,11 @@
17
17
  * limitations under the License.
18
18
  */
19
19
 
20
- import { assertTruthy, mergeColorAlphaPremultiplied } from '../utils.js';
20
+ import {
21
+ assertTruthy,
22
+ mergeColorAlphaPremultiplied,
23
+ getImageAspectRatio,
24
+ } from '../utils.js';
21
25
  import type { ShaderMap } from './CoreShaderManager.js';
22
26
  import type {
23
27
  ExtractProps,
@@ -34,6 +38,7 @@ import type {
34
38
  TextureLoadedEventHandler,
35
39
  } from './textures/Texture.js';
36
40
  import type {
41
+ Dimensions,
37
42
  NodeTextureFailedPayload,
38
43
  NodeTextureFreedPayload,
39
44
  NodeTextureLoadedPayload,
@@ -72,6 +77,7 @@ export interface CoreNodeProps {
72
77
  width: number;
73
78
  height: number;
74
79
  alpha: number;
80
+ autosize: boolean;
75
81
  clipping: boolean;
76
82
  color: number;
77
83
  colorTop: number;
@@ -306,7 +312,15 @@ export class CoreNode extends EventEmitter implements ICoreNode {
306
312
  this.setUpdateType(UpdateType.IsRenderable);
307
313
  }
308
314
 
315
+ autosizeNode(dimensions: Dimensions) {
316
+ if (this.autosize) {
317
+ this.width = dimensions.width;
318
+ this.height = dimensions.height;
319
+ }
320
+ }
321
+
309
322
  private onTextureLoaded: TextureLoadedEventHandler = (target, dimensions) => {
323
+ this.autosizeNode(dimensions);
310
324
  // Texture was loaded. In case the RAF loop has already stopped, we request
311
325
  // a render to ensure the texture is rendered.
312
326
  this.stage.requestRender();
@@ -437,7 +451,11 @@ export class CoreNode extends EventEmitter implements ICoreNode {
437
451
  } else {
438
452
  this.worldAlpha = this.props.alpha;
439
453
  }
440
- this.setUpdateType(UpdateType.Children | UpdateType.PremultipliedColors);
454
+ this.setUpdateType(
455
+ UpdateType.Children |
456
+ UpdateType.PremultipliedColors |
457
+ UpdateType.IsRenderable,
458
+ );
441
459
  childUpdateType |= UpdateType.WorldAlpha;
442
460
  }
443
461
 
@@ -608,7 +626,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
608
626
  updateRenderState(parentClippingRect: RectWithValid) {
609
627
  const renderState = this.checkRenderBounds(parentClippingRect);
610
628
  if (renderState !== this.renderState) {
611
- const previous = this.renderState;
629
+ let previous = this.renderState;
612
630
  this.renderState = renderState;
613
631
  if (previous === CoreNodeRenderState.InViewport) {
614
632
  this.emit('outOfViewport', {
@@ -616,6 +634,25 @@ export class CoreNode extends EventEmitter implements ICoreNode {
616
634
  current: renderState,
617
635
  });
618
636
  }
637
+ if (
638
+ previous < CoreNodeRenderState.InBounds &&
639
+ renderState === CoreNodeRenderState.InViewport
640
+ ) {
641
+ this.emit(CoreNodeRenderStateMap.get(CoreNodeRenderState.InBounds)!, {
642
+ previous,
643
+ current: renderState,
644
+ });
645
+ previous = CoreNodeRenderState.InBounds;
646
+ } else if (
647
+ previous > CoreNodeRenderState.InBounds &&
648
+ renderState === CoreNodeRenderState.OutOfBounds
649
+ ) {
650
+ this.emit(CoreNodeRenderStateMap.get(CoreNodeRenderState.InBounds)!, {
651
+ previous,
652
+ current: renderState,
653
+ });
654
+ previous = CoreNodeRenderState.InBounds;
655
+ }
619
656
  const event = CoreNodeRenderStateMap.get(renderState);
620
657
  assertTruthy(event);
621
658
  this.emit(event, {
@@ -639,7 +676,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
639
676
  */
640
677
  updateIsRenderable() {
641
678
  let newIsRenderable;
642
- if (!this.checkRenderProps()) {
679
+ if (this.worldAlpha === 0 || !this.checkRenderProps()) {
643
680
  newIsRenderable = false;
644
681
  } else {
645
682
  newIsRenderable = this.renderState > CoreNodeRenderState.OutOfBounds;
@@ -1017,6 +1054,14 @@ export class CoreNode extends EventEmitter implements ICoreNode {
1017
1054
  this.setUpdateType(UpdateType.PremultipliedColors | UpdateType.WorldAlpha);
1018
1055
  }
1019
1056
 
1057
+ get autosize(): boolean {
1058
+ return this.props.autosize;
1059
+ }
1060
+
1061
+ set autosize(value: boolean) {
1062
+ this.props.autosize = value;
1063
+ }
1064
+
1020
1065
  get clipping(): boolean {
1021
1066
  return this.props.clipping;
1022
1067
  }
@@ -61,6 +61,8 @@ import {
61
61
  type RadialProgressEffectProps,
62
62
  } from './renderers/webgl/shaders/effects/RadialProgressEffect.js';
63
63
  import { HolePunchEffect } from './renderers/webgl/shaders/effects/HolePunchEffect.js';
64
+ import { WebGlCoreShader } from './renderers/webgl/WebGlCoreShader.js';
65
+ import { UnsupportedShader } from './renderers/canvas/shaders/UnsupportedShader.js';
64
66
 
65
67
  export type { FadeOutEffectProps };
66
68
  export type { LinearGradientEffectProps };
@@ -75,6 +77,7 @@ export interface ShaderMap {
75
77
  RoundedRectangle: typeof RoundedRectangle;
76
78
  DynamicShader: typeof DynamicShader;
77
79
  SdfShader: typeof SdfShader;
80
+ UnsupportedShader: typeof UnsupportedShader;
78
81
  }
79
82
 
80
83
  export type ShaderNode<Type extends keyof ShaderMap> = {
@@ -170,6 +173,13 @@ export class CoreShaderManager {
170
173
  throw new Error(`Shader type "${shType as string}" is not registered`);
171
174
  }
172
175
 
176
+ if (this.renderer.mode === 'canvas' && ShaderClass.prototype instanceof WebGlCoreShader) {
177
+ return {
178
+ shader: new UnsupportedShader(shType) as InstanceType<ShaderMap[Type]>,
179
+ props: props as Record<string, unknown>
180
+ }
181
+ }
182
+
173
183
  if (shType === 'DynamicShader') {
174
184
  return this.loadDynamicShader(props!);
175
185
  }
package/src/core/Stage.ts CHANGED
@@ -18,7 +18,7 @@
18
18
  */
19
19
  import { startLoop, getTimeStamp } from './platform.js';
20
20
  import { WebGlCoreRenderer } from './renderers/webgl/WebGlCoreRenderer.js';
21
- import { assertTruthy } from '../utils.js';
21
+ import { assertTruthy, setPremultiplyMode } from '../utils.js';
22
22
  import { AnimationManager } from './animations/AnimationManager.js';
23
23
  import { CoreNode } from './CoreNode.js';
24
24
  import { CoreTextureManager } from './CoreTextureManager.js';
@@ -38,6 +38,8 @@ import type {
38
38
  FrameTickPayload,
39
39
  } from '../common/CommonTypes.js';
40
40
  import { TextureMemoryManager } from './TextureMemoryManager.js';
41
+ import type { CoreRenderer, CoreRendererOptions } from './renderers/CoreRenderer.js';
42
+ import { CanvasCoreRenderer } from './renderers/canvas/CanvasCoreRenderer.js';
41
43
 
42
44
  export interface StageOptions {
43
45
  rootId: number;
@@ -52,6 +54,7 @@ export interface StageOptions {
52
54
  fpsUpdateInterval: number;
53
55
  enableContextSpy: boolean;
54
56
  numImageWorkers: number;
57
+ renderMode: 'webgl' | 'canvas';
55
58
 
56
59
  debug?: {
57
60
  monitorTextureCache?: boolean;
@@ -79,7 +82,7 @@ export class Stage extends EventEmitter {
79
82
  public readonly fontManager: TrFontManager;
80
83
  public readonly textRenderers: Partial<TextRendererMap>;
81
84
  public readonly shManager: CoreShaderManager;
82
- public readonly renderer: WebGlCoreRenderer;
85
+ public readonly renderer: CoreRenderer;
83
86
  public readonly root: CoreNode;
84
87
  public readonly boundsMargin: [number, number, number, number];
85
88
 
@@ -110,6 +113,7 @@ export class Stage extends EventEmitter {
110
113
  enableContextSpy,
111
114
  numImageWorkers,
112
115
  txMemByteThreshold,
116
+ renderMode
113
117
  } = options;
114
118
 
115
119
  this.txManager = new CoreTextureManager(numImageWorkers);
@@ -135,7 +139,7 @@ export class Stage extends EventEmitter {
135
139
  }, 1000);
136
140
  }
137
141
 
138
- this.renderer = new WebGlCoreRenderer({
142
+ const rendererOptions: CoreRendererOptions = {
139
143
  stage: this,
140
144
  canvas,
141
145
  pixelRatio:
@@ -146,14 +150,23 @@ export class Stage extends EventEmitter {
146
150
  txMemManager: this.txMemManager,
147
151
  shManager: this.shManager,
148
152
  contextSpy: this.contextSpy,
149
- });
153
+ }
154
+
155
+ if (renderMode === 'canvas') {
156
+ this.renderer = new CanvasCoreRenderer(rendererOptions);
157
+ } else {
158
+ this.renderer = new WebGlCoreRenderer(rendererOptions);
159
+ }
160
+ setPremultiplyMode(renderMode);
150
161
 
151
162
  // Must do this after renderer is created
152
163
  this.txManager.renderer = this.renderer;
153
164
 
154
- this.textRenderers = {
165
+ this.textRenderers = renderMode === 'webgl' ? {
155
166
  canvas: new CanvasTextRenderer(this),
156
167
  sdf: new SdfTextRenderer(this),
168
+ } : {
169
+ canvas: new CanvasTextRenderer(this),
157
170
  };
158
171
  this.fontManager = new TrFontManager(this.textRenderers);
159
172
 
@@ -165,6 +178,7 @@ export class Stage extends EventEmitter {
165
178
  width: appWidth,
166
179
  height: appHeight,
167
180
  alpha: 1,
181
+ autosize: false,
168
182
  clipping: false,
169
183
  color: 0x00000000,
170
184
  colorTop: 0x00000000,
@@ -58,7 +58,7 @@ export class ImageWorkerManager {
58
58
  return (mimeType.indexOf("image/png") !== -1);
59
59
  }
60
60
 
61
- async function getImage(src, premultiplyAlpha) {
61
+ function getImage(src, premultiplyAlpha) {
62
62
  return new Promise(function(resolve, reject) {
63
63
  var xhr = new XMLHttpRequest();
64
64
  xhr.open('GET', src, true);
@@ -91,7 +91,7 @@ export class ImageWorkerManager {
91
91
  });
92
92
  }
93
93
 
94
- self.onmessage = async (event) => {
94
+ self.onmessage = (event) => {
95
95
  var src = event.data.src;
96
96
  var premultiplyAlpha = event.data.premultiplyAlpha;
97
97
 
@@ -18,12 +18,14 @@
18
18
  */
19
19
 
20
20
  import type { CoreShaderManager } from '../CoreShaderManager.js';
21
- import type { TextureOptions } from '../CoreTextureManager.js';
21
+ import type { CoreTextureManager, TextureOptions } from '../CoreTextureManager.js';
22
22
  import type { Stage } from '../Stage.js';
23
- import type { Rect, RectWithValid } from '../lib/utils.js';
23
+ import type { TextureMemoryManager } from '../TextureMemoryManager.js';
24
+ import type { ContextSpy } from '../lib/ContextSpy.js';
25
+ import type { RectWithValid } from '../lib/utils.js';
26
+ import { ColorTexture } from '../textures/ColorTexture.js';
24
27
  import type { Texture } from '../textures/Texture.js';
25
28
  import { CoreContextTexture } from './CoreContextTexture.js';
26
- import type { CoreRenderOp } from './CoreRenderOp.js';
27
29
  import type { CoreShader } from './CoreShader.js';
28
30
 
29
31
  export interface QuadOptions {
@@ -48,15 +50,39 @@ export interface QuadOptions {
48
50
  td: number;
49
51
  }
50
52
 
53
+ export interface CoreRendererOptions {
54
+ stage: Stage;
55
+ canvas: HTMLCanvasElement | OffscreenCanvas;
56
+ pixelRatio: number;
57
+ txManager: CoreTextureManager;
58
+ txMemManager: TextureMemoryManager;
59
+ shManager: CoreShaderManager;
60
+ clearColor: number;
61
+ bufferMemory: number;
62
+ contextSpy: ContextSpy | null;
63
+ }
64
+
51
65
  export abstract class CoreRenderer {
66
+ public options: CoreRendererOptions;
67
+ public mode: 'webgl' | 'canvas' | undefined;
68
+
52
69
  protected stage: Stage;
53
70
 
54
- constructor(stage: Stage) {
55
- this.stage = stage;
71
+ //// Core Managers
72
+ txManager: CoreTextureManager;
73
+ txMemManager: TextureMemoryManager;
74
+ shManager: CoreShaderManager;
75
+
76
+ constructor(options: CoreRendererOptions) {
77
+ this.options = options;
78
+ this.stage = options.stage;
79
+ this.txManager = options.txManager;
80
+ this.txMemManager = options.txMemManager;
81
+ this.shManager = options.shManager;
56
82
  }
57
83
 
58
84
  abstract reset(): void;
59
- abstract render(surface: 'screen' | CoreContextTexture): void;
85
+ abstract render(surface?: 'screen' | CoreContextTexture): void;
60
86
  abstract addQuad(quad: QuadOptions): void;
61
87
  abstract createCtxTexture(textureSource: Texture): CoreContextTexture;
62
88
  abstract getShaderManager(): CoreShaderManager;
@@ -0,0 +1,178 @@
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 type { CoreShaderManager } from "../../CoreShaderManager.js";
21
+ import { getRgbaComponents, type RGBA } from "../../lib/utils.js";
22
+ import { SubTexture } from "../../textures/SubTexture.js";
23
+ import type { Texture } from "../../textures/Texture.js";
24
+ import type { CoreContextTexture } from "../CoreContextTexture.js";
25
+ import { CoreRenderer, type CoreRendererOptions, type QuadOptions } from "../CoreRenderer.js";
26
+ import { CanvasCoreTexture } from "./CanvasCoreTexture.js";
27
+ import { getRadius } from "./internal/C2DShaderUtils.js";
28
+ import { formatRgba, parseColor, type IParsedColor } from "./internal/ColorUtils.js";
29
+
30
+ export class CanvasCoreRenderer extends CoreRenderer {
31
+
32
+ private context: CanvasRenderingContext2D;
33
+ private canvas: HTMLCanvasElement;
34
+ private pixelRatio: number;
35
+ private clearColor: RGBA | undefined;
36
+
37
+ constructor(options: CoreRendererOptions) {
38
+ super(options);
39
+
40
+ this.mode = 'canvas';
41
+ this.shManager.renderer = this;
42
+
43
+ const { canvas, pixelRatio, clearColor } = options;
44
+ this.canvas = canvas as HTMLCanvasElement;
45
+ this.context = canvas.getContext('2d') as CanvasRenderingContext2D;
46
+ this.pixelRatio = pixelRatio;
47
+ this.clearColor = clearColor ? getRgbaComponents(clearColor) : undefined;
48
+ }
49
+
50
+ reset(): void {
51
+ // eslint-disable-next-line no-self-assign
52
+ this.canvas.width = this.canvas.width; // quick reset canvas
53
+
54
+ const ctx = this.context;
55
+
56
+ if (this.clearColor) {
57
+ const [r, g, b, a] = this.clearColor;
58
+ ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${a / 255})`;
59
+ ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
60
+ }
61
+
62
+ ctx.scale(this.pixelRatio, this.pixelRatio);
63
+ }
64
+
65
+ render(): void {
66
+ // noop
67
+ }
68
+
69
+ addQuad(quad: QuadOptions): void {
70
+ const ctx = this.context;
71
+ const {
72
+ tx, ty, width, height, alpha, colorTl, colorTr, colorBr, ta, tb, tc, td, clippingRect
73
+ } = quad;
74
+ let texture = quad.texture;
75
+ let ctxTexture: CanvasCoreTexture | undefined = undefined;
76
+ let frame: { x: number, y: number, width: number, height: number } | undefined;
77
+
78
+ if (texture) {
79
+ if (texture instanceof SubTexture) {
80
+ frame = texture.props;
81
+ texture = texture.parentTexture;
82
+ }
83
+
84
+ ctxTexture = this.txManager.getCtxTexture(texture) as CanvasCoreTexture;
85
+ if (texture.state === 'freed') {
86
+ ctxTexture.load();
87
+ return;
88
+ }
89
+ if (texture.state !== 'loaded' || !ctxTexture.hasImage()) {
90
+ return;
91
+ }
92
+ }
93
+
94
+ const color = parseColor(colorTl);
95
+ const hasTransform = ta !== 1;
96
+ const hasClipping = clippingRect.width !== 0 && clippingRect.height !== 0;
97
+ const hasGradient = colorTl !== colorTr || colorTl !== colorBr;
98
+ const radius = quad.shader ? getRadius(quad) : 0;
99
+
100
+ if (hasTransform || hasClipping || radius) {
101
+ ctx.save();
102
+ }
103
+
104
+ if (hasClipping) {
105
+ const path = new Path2D();
106
+ const { x, y, width, height } = clippingRect;
107
+ path.rect(x, y, width, height);
108
+ ctx.clip(path);
109
+ }
110
+
111
+ if (hasTransform) {
112
+ // Quad transform:
113
+ // | ta tb tx |
114
+ // | tc td ty |
115
+ // | 0 0 1 |
116
+ // C2D transform:
117
+ // | a c e |
118
+ // | b d f |
119
+ // | 0 0 1 |
120
+ const scale = this.pixelRatio;
121
+ ctx.setTransform(ta, tc, tb, td, tx * scale, ty * scale);
122
+ ctx.scale(scale, scale);
123
+ ctx.translate(-tx, -ty);
124
+ }
125
+
126
+ if (radius) {
127
+ const path = new Path2D();
128
+ path.roundRect(tx, ty, width, height, radius);
129
+ ctx.clip(path);
130
+ }
131
+
132
+ if (ctxTexture) {
133
+ const image = ctxTexture.getImage(color);
134
+ ctx.globalAlpha = alpha;
135
+ if (frame) {
136
+ ctx.drawImage(image, frame.x, frame.y, frame.width, frame.height, tx, ty, width, height);
137
+ } else {
138
+ ctx.drawImage(image, tx, ty, width, height);
139
+ }
140
+ ctx.globalAlpha = 1;
141
+ } else if (hasGradient) {
142
+ let endX: number = tx;
143
+ let endY: number = ty;
144
+ let endColor: IParsedColor;
145
+ if (colorTl === colorTr) {
146
+ // vertical
147
+ endX = tx;
148
+ endY = ty + height;
149
+ endColor = parseColor(colorBr);
150
+ } else {
151
+ // horizontal
152
+ endX = tx + width;
153
+ endY = ty;
154
+ endColor = parseColor(colorTr);
155
+ }
156
+ const gradient = ctx.createLinearGradient(tx, ty, endX, endY);
157
+ gradient.addColorStop(0, formatRgba(color));
158
+ gradient.addColorStop(1, formatRgba(endColor));
159
+ ctx.fillStyle = gradient;
160
+ ctx.fillRect(tx, ty, width, height);
161
+ } else {
162
+ ctx.fillStyle = formatRgba(color);
163
+ ctx.fillRect(tx, ty, width, height);
164
+ }
165
+
166
+ if (hasTransform || hasClipping || radius) {
167
+ ctx.restore();
168
+ }
169
+ }
170
+
171
+ createCtxTexture(textureSource: Texture): CoreContextTexture {
172
+ return new CanvasCoreTexture(this.txMemManager, textureSource);
173
+ }
174
+
175
+ getShaderManager(): CoreShaderManager {
176
+ return this.shManager;
177
+ }
178
+ }
@@ -0,0 +1,138 @@
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 type { Dimensions } from "../../../common/CommonTypes.js";
21
+ import { assertTruthy } from "../../../utils.js";
22
+ import { CoreContextTexture } from "../CoreContextTexture.js";
23
+ import { formatRgba, type IParsedColor } from "./internal/ColorUtils.js";
24
+
25
+ export class CanvasCoreTexture extends CoreContextTexture {
26
+
27
+ protected image: ImageBitmap | HTMLCanvasElement | undefined;
28
+ protected tintCache: {
29
+ key: string;
30
+ image: HTMLCanvasElement
31
+ } | undefined;
32
+
33
+ load(): void {
34
+ if (this.textureSource.state !== 'freed') {
35
+ return;
36
+ }
37
+ this.textureSource.setState('loading');
38
+ this.onLoadRequest().then((size) => {
39
+ this.textureSource.setState('loaded', size);
40
+ this.updateMemSize();
41
+ }).catch((err) => {
42
+ this.textureSource.setState('failed', err as Error);
43
+ });
44
+ }
45
+
46
+ free(): void {
47
+ this.image = undefined;
48
+ this.tintCache = undefined;
49
+ this.textureSource.setState('freed');
50
+ this.memManager.setTextureMemUse(this, 0);
51
+ }
52
+
53
+ updateMemSize(): void {
54
+ // Counting memory usage for:
55
+ // - main image
56
+ // - tinted image
57
+ const mult = this.tintCache ? 8 : 4;
58
+ if (this.textureSource.dimensions) {
59
+ const { width, height } = this.textureSource.dimensions;
60
+ this.memManager.setTextureMemUse(this, width * height * mult);
61
+ }
62
+ }
63
+
64
+ hasImage(): boolean {
65
+ return this.image !== undefined;
66
+ }
67
+
68
+ getImage(color: IParsedColor): ImageBitmap | HTMLCanvasElement {
69
+ const image = this.image;
70
+ assertTruthy(image, 'Attempt to get unloaded image texture');
71
+
72
+ if (color.isWhite) {
73
+ if (this.tintCache) {
74
+ this.tintCache = undefined;
75
+ this.updateMemSize();
76
+ }
77
+ return image;
78
+ }
79
+ const key = formatRgba(color);
80
+ if (this.tintCache?.key === key) {
81
+ return this.tintCache.image;
82
+ }
83
+
84
+ const tintedImage = this.tintTexture(image, key);
85
+ this.tintCache = {
86
+ key,
87
+ image: tintedImage
88
+ }
89
+ this.updateMemSize();
90
+ return tintedImage;
91
+ }
92
+
93
+ protected tintTexture(source: ImageBitmap | HTMLCanvasElement, color: string) {
94
+ const { width, height } = source;
95
+ const canvas = document.createElement('canvas');
96
+ canvas.width = width;
97
+ canvas.height = height;
98
+ const ctx = canvas.getContext('2d');
99
+ if (ctx) {
100
+ // fill with target color
101
+ ctx.fillStyle = color;
102
+ ctx.globalCompositeOperation = 'copy';
103
+ ctx.fillRect(0, 0, width, height);
104
+
105
+ // multiply with image, resulting in non-transparent tinted image
106
+ ctx.globalCompositeOperation = 'multiply';
107
+ ctx.drawImage(source, 0, 0, width, height, 0, 0, width, height);
108
+
109
+ // apply original image alpha
110
+ ctx.globalCompositeOperation = 'destination-in';
111
+ ctx.drawImage(source, 0, 0, width, height, 0, 0, width, height);
112
+ }
113
+ return canvas;
114
+ }
115
+
116
+ get renderable(): boolean {
117
+ return this.textureSource.renderable;
118
+ }
119
+
120
+ private async onLoadRequest(): Promise<Dimensions> {
121
+ const { data } = await this.textureSource.getTextureData();
122
+ // TODO: canvas from text renderer should be able to provide the canvas directly
123
+ // instead of having to re-draw it into a new canvas...
124
+ if (data instanceof ImageData) {
125
+ const canvas = document.createElement('canvas');
126
+ canvas.width = data.width;
127
+ canvas.height = data.height;
128
+ const ctx = canvas.getContext('2d');
129
+ if (ctx) ctx.putImageData(data, 0, 0);
130
+ this.image = canvas;
131
+ return { width: data.width, height: data.height };
132
+ } else if (data instanceof ImageBitmap) {
133
+ this.image = data;
134
+ return { width: data.width, height: data.height };
135
+ }
136
+ return { width: 0, height: 0 };
137
+ }
138
+ }
@@ -0,0 +1,34 @@
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 type { QuadOptions } from "../../CoreRenderer.js";
21
+ import { ROUNDED_RECTANGLE_SHADER_TYPE, UnsupportedShader } from "../shaders/UnsupportedShader.js";
22
+
23
+ /**
24
+ * Extract `RoundedRectangle` shader radius to apply as a clipping
25
+ */
26
+ export function getRadius(quad: QuadOptions): number {
27
+ if (quad.shader instanceof UnsupportedShader) {
28
+ const shType = quad.shader.shType;
29
+ if (shType === ROUNDED_RECTANGLE_SHADER_TYPE) {
30
+ return quad.shaderProps?.radius as number ?? 0;
31
+ }
32
+ }
33
+ return 0;
34
+ }