@lightningjs/renderer 1.0.1 → 2.1.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.
- package/README.md +17 -0
- package/dist/exports/canvas.d.ts +20 -0
- package/dist/exports/canvas.js +39 -0
- package/dist/exports/canvas.js.map +1 -0
- package/dist/exports/index.d.ts +3 -5
- package/dist/exports/index.js +2 -4
- package/dist/exports/index.js.map +1 -1
- package/dist/exports/inspector.d.ts +4 -0
- package/dist/exports/inspector.js +23 -0
- package/dist/exports/inspector.js.map +1 -0
- package/dist/exports/utils.d.ts +2 -0
- package/dist/exports/utils.js +2 -0
- package/dist/exports/utils.js.map +1 -1
- package/dist/exports/webgl.d.ts +19 -0
- package/dist/exports/webgl.js +38 -0
- package/dist/exports/webgl.js.map +1 -0
- package/dist/src/common/EventEmitter.d.ts +1 -1
- package/dist/src/common/IAnimationController.d.ts +1 -1
- package/dist/src/common/IEventEmitter.d.ts +8 -0
- package/dist/src/common/IEventEmitter.js +18 -0
- package/dist/src/common/IEventEmitter.js.map +1 -0
- package/dist/src/core/CoreNode.d.ts +66 -2
- package/dist/src/core/CoreNode.js +128 -24
- package/dist/src/core/CoreNode.js.map +1 -1
- package/dist/src/core/CoreTextNode.d.ts +2 -2
- package/dist/src/core/CoreTextNode.js +20 -30
- package/dist/src/core/CoreTextNode.js.map +1 -1
- package/dist/src/core/Stage.d.ts +11 -5
- package/dist/src/core/Stage.js +58 -30
- package/dist/src/core/Stage.js.map +1 -1
- package/dist/src/core/TextureMemoryManager.js +4 -2
- package/dist/src/core/TextureMemoryManager.js.map +1 -1
- package/dist/src/core/animations/CoreAnimation.js +1 -1
- package/dist/src/core/animations/CoreAnimation.js.map +1 -1
- package/dist/src/core/lib/ImageWorker.d.ts +1 -1
- package/dist/src/core/lib/ImageWorker.js +25 -3
- package/dist/src/core/lib/ImageWorker.js.map +1 -1
- package/dist/src/core/lib/textureSvg.d.ts +16 -0
- package/dist/src/core/lib/textureSvg.js +63 -0
- package/dist/src/core/lib/textureSvg.js.map +1 -0
- package/dist/src/core/renderers/canvas/CanvasCoreTexture.js +2 -1
- package/dist/src/core/renderers/canvas/CanvasCoreTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +2 -1
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +10 -6
- package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -1
- package/dist/src/core/renderers/webgl/shaders/DynamicShader.js +2 -1
- package/dist/src/core/renderers/webgl/shaders/DynamicShader.js.map +1 -1
- package/dist/src/core/text-rendering/TrFontManager.js +11 -4
- package/dist/src/core/text-rendering/TrFontManager.js.map +1 -1
- package/dist/src/core/text-rendering/font-face-types/WebTrFontFace.js +13 -5
- package/dist/src/core/text-rendering/font-face-types/WebTrFontFace.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.d.ts +1 -0
- package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js +1 -0
- package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +1 -0
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +1 -0
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/TextRenderer.d.ts +1 -0
- package/dist/src/core/text-rendering/renderers/TextRenderer.js.map +1 -1
- package/dist/src/core/textures/ImageTexture.d.ts +52 -0
- package/dist/src/core/textures/ImageTexture.js +78 -31
- package/dist/src/core/textures/ImageTexture.js.map +1 -1
- package/dist/src/core/textures/Texture.d.ts +1 -0
- package/dist/src/core/textures/Texture.js +1 -0
- package/dist/src/core/textures/Texture.js.map +1 -1
- package/dist/src/main-api/Inspector.js +2 -2
- package/dist/src/main-api/Inspector.js.map +1 -1
- package/dist/src/main-api/Renderer.d.ts +50 -8
- package/dist/src/main-api/Renderer.js +8 -7
- package/dist/src/main-api/Renderer.js.map +1 -1
- package/dist/tsconfig.dist.tsbuildinfo +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/exports/canvas.ts +39 -0
- package/exports/index.ts +4 -4
- package/exports/inspector.ts +24 -0
- package/exports/utils.ts +2 -0
- package/exports/webgl.ts +38 -0
- package/package.json +5 -7
- package/src/common/EventEmitter.ts +1 -1
- package/src/common/IAnimationController.ts +1 -1
- package/src/common/IEventEmitter.ts +28 -0
- package/src/core/CoreNode.test.ts +1 -0
- package/src/core/CoreNode.ts +229 -40
- package/src/core/CoreTextNode.ts +58 -65
- package/src/core/Stage.ts +92 -40
- package/src/core/TextureMemoryManager.ts +4 -2
- package/src/core/animations/CoreAnimation.ts +2 -1
- package/src/core/lib/ImageWorker.ts +44 -7
- package/src/core/lib/textureSvg.ts +78 -0
- package/src/core/renderers/canvas/CanvasCoreTexture.ts +4 -1
- package/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +2 -1
- package/src/core/renderers/webgl/WebGlCoreRenderer.ts +11 -6
- package/src/core/renderers/webgl/shaders/DynamicShader.ts +1 -0
- package/src/core/text-rendering/TrFontManager.ts +15 -4
- package/src/core/text-rendering/font-face-types/WebTrFontFace.ts +15 -8
- package/src/core/text-rendering/renderers/CanvasTextRenderer.ts +2 -0
- package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +2 -0
- package/src/core/text-rendering/renderers/TextRenderer.ts +1 -0
- package/src/core/textures/ImageTexture.ts +159 -35
- package/src/core/textures/Texture.ts +2 -0
- package/src/main-api/Inspector.ts +2 -2
- package/src/main-api/Renderer.ts +59 -15
package/src/core/Stage.ts
CHANGED
|
@@ -17,20 +17,18 @@
|
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
19
|
import { startLoop, getTimeStamp } from './platform.js';
|
|
20
|
-
import { WebGlCoreRenderer } from './renderers/webgl/WebGlCoreRenderer.js';
|
|
21
20
|
import { assertTruthy, setPremultiplyMode } from '../utils.js';
|
|
22
21
|
import { AnimationManager } from './animations/AnimationManager.js';
|
|
23
22
|
import { CoreNode, type CoreNodeProps } from './CoreNode.js';
|
|
24
23
|
import { CoreTextureManager } from './CoreTextureManager.js';
|
|
25
24
|
import { TrFontManager } from './text-rendering/TrFontManager.js';
|
|
26
25
|
import { CoreShaderManager, type ShaderMap } from './CoreShaderManager.js';
|
|
27
|
-
import
|
|
26
|
+
import {
|
|
28
27
|
TextRenderer,
|
|
29
|
-
TextRendererMap,
|
|
30
|
-
TrProps,
|
|
28
|
+
type TextRendererMap,
|
|
29
|
+
type TrProps,
|
|
31
30
|
} from './text-rendering/renderers/TextRenderer.js';
|
|
32
|
-
|
|
33
|
-
import { CanvasTextRenderer } from './text-rendering/renderers/CanvasTextRenderer.js';
|
|
31
|
+
|
|
34
32
|
import { EventEmitter } from '../common/EventEmitter.js';
|
|
35
33
|
import { ContextSpy } from './lib/ContextSpy.js';
|
|
36
34
|
import type {
|
|
@@ -41,14 +39,15 @@ import {
|
|
|
41
39
|
TextureMemoryManager,
|
|
42
40
|
type TextureMemoryManagerSettings,
|
|
43
41
|
} from './TextureMemoryManager.js';
|
|
44
|
-
import type {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
} from './renderers/
|
|
48
|
-
import { CanvasCoreRenderer } from './renderers/canvas/CanvasCoreRenderer.js';
|
|
42
|
+
import type { CoreRendererOptions } from './renderers/CoreRenderer.js';
|
|
43
|
+
import { CoreRenderer } from './renderers/CoreRenderer.js';
|
|
44
|
+
import type { WebGlCoreRenderer } from './renderers/webgl/WebGlCoreRenderer.js';
|
|
45
|
+
import type { CanvasCoreRenderer } from './renderers/canvas/CanvasCoreRenderer.js';
|
|
49
46
|
import type { BaseShaderController } from '../main-api/ShaderController.js';
|
|
50
47
|
import { CoreTextNode, type CoreTextNodeProps } from './CoreTextNode.js';
|
|
51
48
|
import { santizeCustomDataMap } from '../main-api/utils.js';
|
|
49
|
+
import type { SdfTextRenderer } from './text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js';
|
|
50
|
+
import type { CanvasTextRenderer } from './text-rendering/renderers/CanvasTextRenderer.js';
|
|
52
51
|
|
|
53
52
|
export interface StageOptions {
|
|
54
53
|
appWidth: number;
|
|
@@ -62,9 +61,10 @@ export interface StageOptions {
|
|
|
62
61
|
fpsUpdateInterval: number;
|
|
63
62
|
enableContextSpy: boolean;
|
|
64
63
|
numImageWorkers: number;
|
|
65
|
-
|
|
64
|
+
renderEngine: typeof WebGlCoreRenderer | typeof CanvasCoreRenderer;
|
|
66
65
|
eventBus: EventEmitter;
|
|
67
66
|
quadBufferSize: number;
|
|
67
|
+
fontEngines: (typeof CanvasTextRenderer | typeof SdfTextRenderer)[];
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
export type StageFpsUpdateHandler = (
|
|
@@ -111,6 +111,8 @@ export class Stage {
|
|
|
111
111
|
private fpsElapsedTime = 0;
|
|
112
112
|
private renderRequested = false;
|
|
113
113
|
private frameEventQueue: [name: string, payload: unknown][] = [];
|
|
114
|
+
private fontResolveMap: Record<string, CanvasTextRenderer | SdfTextRenderer> =
|
|
115
|
+
{};
|
|
114
116
|
|
|
115
117
|
/// Debug data
|
|
116
118
|
contextSpy: ContextSpy | null = null;
|
|
@@ -128,7 +130,8 @@ export class Stage {
|
|
|
128
130
|
enableContextSpy,
|
|
129
131
|
numImageWorkers,
|
|
130
132
|
textureMemory,
|
|
131
|
-
|
|
133
|
+
renderEngine,
|
|
134
|
+
fontEngines,
|
|
132
135
|
} = options;
|
|
133
136
|
|
|
134
137
|
this.eventBus = options.eventBus;
|
|
@@ -159,26 +162,42 @@ export class Stage {
|
|
|
159
162
|
contextSpy: this.contextSpy,
|
|
160
163
|
};
|
|
161
164
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
this.renderer = new WebGlCoreRenderer(rendererOptions);
|
|
166
|
-
}
|
|
165
|
+
this.renderer = new renderEngine(rendererOptions);
|
|
166
|
+
const renderMode = this.renderer.mode || 'webgl';
|
|
167
|
+
|
|
167
168
|
this.defShaderCtr = this.renderer.getDefShaderCtr();
|
|
168
169
|
setPremultiplyMode(renderMode);
|
|
169
170
|
|
|
170
171
|
// Must do this after renderer is created
|
|
171
172
|
this.txManager.renderer = this.renderer;
|
|
172
173
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
174
|
+
// Create text renderers
|
|
175
|
+
this.textRenderers = {};
|
|
176
|
+
fontEngines.forEach((fontEngineConstructor) => {
|
|
177
|
+
const fontEngineInstance = new fontEngineConstructor(this);
|
|
178
|
+
const className = fontEngineInstance.type;
|
|
179
|
+
|
|
180
|
+
if (className === 'sdf' && renderMode === 'canvas') {
|
|
181
|
+
console.warn(
|
|
182
|
+
'SdfTextRenderer is not compatible with Canvas renderer. Skipping...',
|
|
183
|
+
);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (fontEngineInstance instanceof TextRenderer) {
|
|
188
|
+
if (className === 'canvas') {
|
|
189
|
+
this.textRenderers['canvas'] =
|
|
190
|
+
fontEngineInstance as CanvasTextRenderer;
|
|
191
|
+
} else if (className === 'sdf') {
|
|
192
|
+
this.textRenderers['sdf'] = fontEngineInstance as SdfTextRenderer;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
if (Object.keys(this.textRenderers).length === 0) {
|
|
198
|
+
console.warn('No text renderers available. Your text will not render.');
|
|
199
|
+
}
|
|
200
|
+
|
|
182
201
|
this.fontManager = new TrFontManager(this.textRenderers);
|
|
183
202
|
|
|
184
203
|
// create root node
|
|
@@ -217,6 +236,7 @@ export class Stage {
|
|
|
217
236
|
rtt: false,
|
|
218
237
|
src: null,
|
|
219
238
|
scale: 1,
|
|
239
|
+
preventCleanup: false,
|
|
220
240
|
});
|
|
221
241
|
|
|
222
242
|
this.root = rootNode;
|
|
@@ -361,20 +381,20 @@ export class Stage {
|
|
|
361
381
|
}
|
|
362
382
|
|
|
363
383
|
addQuads(node: CoreNode) {
|
|
364
|
-
assertTruthy(this.renderer
|
|
384
|
+
assertTruthy(this.renderer);
|
|
365
385
|
|
|
366
|
-
if (node.isRenderable) {
|
|
386
|
+
if (node.isRenderable === true) {
|
|
367
387
|
node.renderQuads(this.renderer);
|
|
368
388
|
}
|
|
369
389
|
|
|
370
390
|
for (let i = 0; i < node.children.length; i++) {
|
|
371
391
|
const child = node.children[i];
|
|
372
392
|
|
|
373
|
-
if (
|
|
393
|
+
if (child === undefined) {
|
|
374
394
|
continue;
|
|
375
395
|
}
|
|
376
396
|
|
|
377
|
-
if (child
|
|
397
|
+
if (child.worldAlpha === 0) {
|
|
378
398
|
continue;
|
|
379
399
|
}
|
|
380
400
|
|
|
@@ -393,7 +413,7 @@ export class Stage {
|
|
|
393
413
|
* Given a font name, and possible renderer override, return the best compatible text renderer.
|
|
394
414
|
*
|
|
395
415
|
* @remarks
|
|
396
|
-
* Will
|
|
416
|
+
* Will try to return a canvas renderer if no other suitable renderer can be resolved.
|
|
397
417
|
*
|
|
398
418
|
* @param fontFamily
|
|
399
419
|
* @param textRendererOverride
|
|
@@ -402,10 +422,20 @@ export class Stage {
|
|
|
402
422
|
resolveTextRenderer(
|
|
403
423
|
trProps: TrProps,
|
|
404
424
|
textRendererOverride: keyof TextRendererMap | null = null,
|
|
405
|
-
): TextRenderer {
|
|
406
|
-
|
|
425
|
+
): TextRenderer | null {
|
|
426
|
+
const fontCacheString = `${trProps.fontFamily}${trProps.fontStyle}${
|
|
427
|
+
trProps.fontWeight
|
|
428
|
+
}${trProps.fontStretch}${textRendererOverride ? textRendererOverride : ''}`;
|
|
429
|
+
|
|
430
|
+
// check our resolve cache first
|
|
431
|
+
if (this.fontResolveMap[fontCacheString] !== undefined) {
|
|
432
|
+
return this.fontResolveMap[fontCacheString] as unknown as TextRenderer;
|
|
433
|
+
}
|
|
407
434
|
|
|
435
|
+
// Resolve the text renderer
|
|
436
|
+
let rendererId = textRendererOverride;
|
|
408
437
|
let overrideFallback = false;
|
|
438
|
+
|
|
409
439
|
// Check if the override is valid (if one is provided)
|
|
410
440
|
if (rendererId) {
|
|
411
441
|
const possibleRenderer = this.textRenderers[rendererId];
|
|
@@ -426,16 +456,12 @@ export class Stage {
|
|
|
426
456
|
if (!rendererId) {
|
|
427
457
|
// Iterate through the text renderers and find the first one that can render the font
|
|
428
458
|
for (const [trId, tr] of Object.entries(this.textRenderers)) {
|
|
429
|
-
if (trId === 'canvas') {
|
|
430
|
-
// Canvas is always a fallback
|
|
431
|
-
continue;
|
|
432
|
-
}
|
|
433
459
|
if (tr.canRenderFont(trProps)) {
|
|
434
460
|
rendererId = trId as keyof TextRendererMap;
|
|
435
461
|
break;
|
|
436
462
|
}
|
|
437
463
|
}
|
|
438
|
-
if (!rendererId) {
|
|
464
|
+
if (!rendererId && this.textRenderers.canvas !== undefined) {
|
|
439
465
|
// If no renderer can be found, use the canvas renderer
|
|
440
466
|
rendererId = 'canvas';
|
|
441
467
|
}
|
|
@@ -445,10 +471,19 @@ export class Stage {
|
|
|
445
471
|
console.warn(`Falling back to text renderer ${String(rendererId)}`);
|
|
446
472
|
}
|
|
447
473
|
|
|
474
|
+
if (!rendererId) {
|
|
475
|
+
// silently fail if no renderer can be found, the error is already created
|
|
476
|
+
// at the constructor level
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
|
|
448
480
|
// By now we are guaranteed to have a valid rendererId (at least Canvas);
|
|
449
481
|
const resolvedTextRenderer = this.textRenderers[rendererId];
|
|
450
482
|
assertTruthy(resolvedTextRenderer, 'resolvedTextRenderer undefined');
|
|
451
483
|
|
|
484
|
+
// cache the resolved renderer for future use with these trProps
|
|
485
|
+
this.fontResolveMap[fontCacheString] = resolvedTextRenderer;
|
|
486
|
+
|
|
452
487
|
// Need to explicitly cast to TextRenderer because TS doesn't like
|
|
453
488
|
// the covariant state argument in the setter method map
|
|
454
489
|
return resolvedTextRenderer as unknown as TextRenderer;
|
|
@@ -499,7 +534,18 @@ export class Stage {
|
|
|
499
534
|
shaderProps: null,
|
|
500
535
|
};
|
|
501
536
|
|
|
502
|
-
|
|
537
|
+
const resolvedTextRenderer = this.resolveTextRenderer(
|
|
538
|
+
resolvedProps,
|
|
539
|
+
props.textRendererOverride,
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
if (!resolvedTextRenderer) {
|
|
543
|
+
throw new Error(
|
|
544
|
+
`No compatible text renderer found for ${resolvedProps.fontFamily}`,
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return new CoreTextNode(this, resolvedProps, resolvedTextRenderer);
|
|
503
549
|
}
|
|
504
550
|
|
|
505
551
|
/**
|
|
@@ -550,6 +596,10 @@ export class Stage {
|
|
|
550
596
|
// Since setting the `src` will trigger a texture load, we need to set it after
|
|
551
597
|
// we set the texture. Otherwise, problems happen.
|
|
552
598
|
src: props.src ?? null,
|
|
599
|
+
srcHeight: props.srcHeight,
|
|
600
|
+
srcWidth: props.srcWidth,
|
|
601
|
+
srcX: props.srcX,
|
|
602
|
+
srcY: props.srcY,
|
|
553
603
|
scale: props.scale ?? null,
|
|
554
604
|
scaleX: props.scaleX ?? props.scale ?? 1,
|
|
555
605
|
scaleY: props.scaleY ?? props.scale ?? 1,
|
|
@@ -562,6 +612,8 @@ export class Stage {
|
|
|
562
612
|
rotation: props.rotation ?? 0,
|
|
563
613
|
rtt: props.rtt ?? false,
|
|
564
614
|
data: data,
|
|
615
|
+
preventCleanup: props.preventCleanup ?? false,
|
|
616
|
+
imageType: props.imageType,
|
|
565
617
|
};
|
|
566
618
|
}
|
|
567
619
|
}
|
|
@@ -227,8 +227,10 @@ export class TextureMemoryManager {
|
|
|
227
227
|
// We don't want to free renderable textures because they will just likely be reloaded in the next frame
|
|
228
228
|
break;
|
|
229
229
|
}
|
|
230
|
-
texture.
|
|
231
|
-
|
|
230
|
+
if (texture.preventCleanup === false) {
|
|
231
|
+
texture.ctxTexture.free();
|
|
232
|
+
txManager.removeTextureFromCache(texture);
|
|
233
|
+
}
|
|
232
234
|
if (this.memUsed <= memTarget) {
|
|
233
235
|
// Stop once we've freed enough textures to reach under the target threshold
|
|
234
236
|
break;
|
|
@@ -60,7 +60,8 @@ export class CoreAnimation extends EventEmitter {
|
|
|
60
60
|
this.propValuesMap['props'] = {};
|
|
61
61
|
}
|
|
62
62
|
this.propValuesMap['props'][key] = {
|
|
63
|
-
start:
|
|
63
|
+
start:
|
|
64
|
+
node[key as keyof Omit<CoreNodeAnimateProps, 'shaderProps'>] || 0,
|
|
64
65
|
target: props[
|
|
65
66
|
key as keyof Omit<CoreNodeAnimateProps, 'shaderProps'>
|
|
66
67
|
] as number,
|
|
@@ -25,6 +25,17 @@ interface getImageReturn {
|
|
|
25
25
|
premultiplyAlpha: boolean | null;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
interface ImageWorkerMessage {
|
|
29
|
+
id: number;
|
|
30
|
+
src: string;
|
|
31
|
+
data: getImageReturn;
|
|
32
|
+
error: string;
|
|
33
|
+
sx: number | null;
|
|
34
|
+
sy: number | null;
|
|
35
|
+
sw: number | null;
|
|
36
|
+
sh: number | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
28
39
|
/**
|
|
29
40
|
* Note that, within the createImageWorker function, we must only use ES5 code to keep it ES5-valid after babelifying, as
|
|
30
41
|
* the converted code of this section is converted to a blob and used as the js of the web worker thread.
|
|
@@ -43,6 +54,10 @@ function createImageWorker() {
|
|
|
43
54
|
function getImage(
|
|
44
55
|
src: string,
|
|
45
56
|
premultiplyAlpha: boolean | null,
|
|
57
|
+
x: number | null,
|
|
58
|
+
y: number | null,
|
|
59
|
+
width: number | null,
|
|
60
|
+
height: number | null,
|
|
46
61
|
): Promise<getImageReturn> {
|
|
47
62
|
return new Promise(function (resolve, reject) {
|
|
48
63
|
var xhr = new XMLHttpRequest();
|
|
@@ -60,6 +75,21 @@ function createImageWorker() {
|
|
|
60
75
|
? premultiplyAlpha
|
|
61
76
|
: hasAlphaChannel(blob.type);
|
|
62
77
|
|
|
78
|
+
if (width !== null && height !== null) {
|
|
79
|
+
createImageBitmap(blob, x || 0, y || 0, width, height, {
|
|
80
|
+
premultiplyAlpha: withAlphaChannel ? 'premultiply' : 'none',
|
|
81
|
+
colorSpaceConversion: 'none',
|
|
82
|
+
imageOrientation: 'none',
|
|
83
|
+
})
|
|
84
|
+
.then(function (data) {
|
|
85
|
+
resolve({ data, premultiplyAlpha: premultiplyAlpha });
|
|
86
|
+
})
|
|
87
|
+
.catch(function (error) {
|
|
88
|
+
reject(error);
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
63
93
|
createImageBitmap(blob, {
|
|
64
94
|
premultiplyAlpha: withAlphaChannel ? 'premultiply' : 'none',
|
|
65
95
|
colorSpaceConversion: 'none',
|
|
@@ -87,8 +117,12 @@ function createImageWorker() {
|
|
|
87
117
|
var src = event.data.src;
|
|
88
118
|
var id = event.data.id;
|
|
89
119
|
var premultiplyAlpha = event.data.premultiplyAlpha;
|
|
120
|
+
var x = event.data.sx;
|
|
121
|
+
var y = event.data.sy;
|
|
122
|
+
var width = event.data.sw;
|
|
123
|
+
var height = event.data.sh;
|
|
90
124
|
|
|
91
|
-
getImage(src, premultiplyAlpha)
|
|
125
|
+
getImage(src, premultiplyAlpha, x, y, width, height)
|
|
92
126
|
.then(function (data) {
|
|
93
127
|
self.postMessage({ id: id, src: src, data: data });
|
|
94
128
|
})
|
|
@@ -114,12 +148,7 @@ export class ImageWorkerManager {
|
|
|
114
148
|
}
|
|
115
149
|
|
|
116
150
|
private handleMessage(event: MessageEvent) {
|
|
117
|
-
const { id, data, error } = event.data as
|
|
118
|
-
id: number;
|
|
119
|
-
src: string;
|
|
120
|
-
data?: any;
|
|
121
|
-
error?: string;
|
|
122
|
-
};
|
|
151
|
+
const { id, data, error } = event.data as ImageWorkerMessage;
|
|
123
152
|
const msg = this.messageManager[id];
|
|
124
153
|
if (msg) {
|
|
125
154
|
const [resolve, reject] = msg;
|
|
@@ -155,6 +184,10 @@ export class ImageWorkerManager {
|
|
|
155
184
|
getImage(
|
|
156
185
|
src: string,
|
|
157
186
|
premultiplyAlpha: boolean | null,
|
|
187
|
+
sx: number | null,
|
|
188
|
+
sy: number | null,
|
|
189
|
+
sw: number | null,
|
|
190
|
+
sh: number | null,
|
|
158
191
|
): Promise<TextureData> {
|
|
159
192
|
return new Promise((resolve, reject) => {
|
|
160
193
|
try {
|
|
@@ -167,6 +200,10 @@ export class ImageWorkerManager {
|
|
|
167
200
|
id,
|
|
168
201
|
src: src,
|
|
169
202
|
premultiplyAlpha,
|
|
203
|
+
sx,
|
|
204
|
+
sy,
|
|
205
|
+
sw,
|
|
206
|
+
sh,
|
|
170
207
|
});
|
|
171
208
|
}
|
|
172
209
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
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 { assertTruthy } from '../../utils.js';
|
|
21
|
+
import { type TextureData } from '../textures/Texture.js';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Tests if the given location is a SVG
|
|
25
|
+
* @param url
|
|
26
|
+
* @remarks
|
|
27
|
+
* This function is used to determine if the given image url is a SVG
|
|
28
|
+
* image
|
|
29
|
+
* @returns
|
|
30
|
+
*/
|
|
31
|
+
export function isSvgImage(url: string): boolean {
|
|
32
|
+
return /\.(svg)$/.test(url);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Loads a SVG image
|
|
37
|
+
* @param url
|
|
38
|
+
* @returns
|
|
39
|
+
*/
|
|
40
|
+
export const loadSvg = (
|
|
41
|
+
url: string,
|
|
42
|
+
width: number | null,
|
|
43
|
+
height: number | null,
|
|
44
|
+
sx: number | null,
|
|
45
|
+
sy: number | null,
|
|
46
|
+
sw: number | null,
|
|
47
|
+
sh: number | null,
|
|
48
|
+
): Promise<TextureData> => {
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const canvas = document.createElement('canvas');
|
|
51
|
+
const ctx = canvas.getContext('2d');
|
|
52
|
+
assertTruthy(ctx);
|
|
53
|
+
|
|
54
|
+
ctx.imageSmoothingEnabled = true;
|
|
55
|
+
const img = new Image();
|
|
56
|
+
img.onload = () => {
|
|
57
|
+
const x = sx ?? 0;
|
|
58
|
+
const y = sy ?? 0;
|
|
59
|
+
const w = width || img.width;
|
|
60
|
+
const h = height || img.height;
|
|
61
|
+
|
|
62
|
+
canvas.width = w;
|
|
63
|
+
canvas.height = h;
|
|
64
|
+
ctx.drawImage(img, 0, 0, w, h);
|
|
65
|
+
|
|
66
|
+
resolve({
|
|
67
|
+
data: ctx.getImageData(x, y, sw ?? w, sh ?? h),
|
|
68
|
+
premultiplyAlpha: false,
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
img.onerror = (err) => {
|
|
73
|
+
reject(err);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
img.src = url;
|
|
77
|
+
});
|
|
78
|
+
};
|
|
@@ -131,7 +131,10 @@ export class CanvasCoreTexture extends CoreContextTexture {
|
|
|
131
131
|
if (ctx) ctx.putImageData(data, 0, 0);
|
|
132
132
|
this.image = canvas;
|
|
133
133
|
return { width: data.width, height: data.height };
|
|
134
|
-
} else if (
|
|
134
|
+
} else if (
|
|
135
|
+
typeof ImageBitmap !== 'undefined' &&
|
|
136
|
+
data instanceof ImageBitmap
|
|
137
|
+
) {
|
|
135
138
|
this.image = data;
|
|
136
139
|
return { width: data.width, height: data.height };
|
|
137
140
|
}
|
|
@@ -134,7 +134,8 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
134
134
|
// If textureData is null, the texture is empty (0, 0) and we don't need to
|
|
135
135
|
// upload any data to the GPU.
|
|
136
136
|
if (
|
|
137
|
-
|
|
137
|
+
(typeof ImageBitmap !== 'undefined' &&
|
|
138
|
+
textureData.data instanceof ImageBitmap) ||
|
|
138
139
|
textureData.data instanceof ImageData ||
|
|
139
140
|
// not using typeof HTMLImageElement due to web worker
|
|
140
141
|
isHTMLImageElement(textureData.data)
|
|
@@ -254,13 +254,18 @@ export class WebGlCoreRenderer extends CoreRenderer {
|
|
|
254
254
|
|
|
255
255
|
/**
|
|
256
256
|
* If the shader props contain any automatic properties, update it with the
|
|
257
|
-
* current dimensions that will be used to render the quad.
|
|
257
|
+
* current dimensions and or alpha that will be used to render the quad.
|
|
258
258
|
*/
|
|
259
|
-
if (shaderProps
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
259
|
+
if (shaderProps !== null) {
|
|
260
|
+
if (hasOwn(shaderProps, '$dimensions')) {
|
|
261
|
+
const dimensions = shaderProps.$dimensions as Dimensions;
|
|
262
|
+
dimensions.width = width;
|
|
263
|
+
dimensions.height = height;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (hasOwn(shaderProps, '$alpha')) {
|
|
267
|
+
shaderProps.$alpha = alpha;
|
|
268
|
+
}
|
|
264
269
|
}
|
|
265
270
|
|
|
266
271
|
texture = texture ?? this.defaultTexture;
|
|
@@ -172,6 +172,7 @@ export class DynamicShader extends WebGlCoreShader {
|
|
|
172
172
|
propsB: Required<DynamicShaderProps>,
|
|
173
173
|
): boolean {
|
|
174
174
|
if (
|
|
175
|
+
propsA.$alpha !== propsB.$alpha ||
|
|
175
176
|
propsA.$dimensions.width !== propsB.$dimensions.width ||
|
|
176
177
|
propsA.$dimensions.height !== propsB.$dimensions.height ||
|
|
177
178
|
propsA.effects.length !== propsB.effects.length
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
|
|
20
20
|
import type { TrFontFace } from './font-face-types/TrFontFace.js';
|
|
21
21
|
import type { TextRendererMap, TrFontProps } from './renderers/TextRenderer.js';
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
const fontCache = new Map<string, TrFontFace>();
|
|
23
24
|
|
|
24
25
|
const weightConversions: { [key: string]: number } = {
|
|
25
26
|
normal: 400,
|
|
@@ -36,7 +37,7 @@ const fontWeightToNumber = (weight: string | number): number => {
|
|
|
36
37
|
return weightConversions[weight] || 400;
|
|
37
38
|
};
|
|
38
39
|
|
|
39
|
-
function
|
|
40
|
+
function resolveFontToUse(
|
|
40
41
|
familyMapsByPriority: FontFamilyMap[],
|
|
41
42
|
family: string,
|
|
42
43
|
weightIn: string | number,
|
|
@@ -119,7 +120,6 @@ function rawResolveFontToUse(
|
|
|
119
120
|
|
|
120
121
|
return;
|
|
121
122
|
}
|
|
122
|
-
const resolveFontToUse = memize(rawResolveFontToUse);
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
125
|
* Structure mapping font family names to a set of font faces.
|
|
@@ -159,12 +159,23 @@ export class TrFontManager {
|
|
|
159
159
|
props: TrFontProps,
|
|
160
160
|
): TrFontFace | undefined {
|
|
161
161
|
const { fontFamily, fontWeight, fontStyle, fontStretch } = props;
|
|
162
|
-
|
|
162
|
+
const fontCacheString = `${fontFamily}${fontStyle}${fontWeight}${fontStretch}`;
|
|
163
|
+
|
|
164
|
+
if (fontCache.has(fontCacheString) === true) {
|
|
165
|
+
return fontCache.get(fontCacheString);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const resolvedFont = resolveFontToUse(
|
|
163
169
|
familyMapsByPriority,
|
|
164
170
|
fontFamily,
|
|
165
171
|
fontWeight,
|
|
166
172
|
fontStyle,
|
|
167
173
|
fontStretch,
|
|
168
174
|
);
|
|
175
|
+
if (resolvedFont !== undefined) {
|
|
176
|
+
fontCache.set(fontCacheString, resolvedFont);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return resolvedFont;
|
|
169
180
|
}
|
|
170
181
|
}
|
|
@@ -67,14 +67,21 @@ export class WebTrFontFace extends TrFontFace {
|
|
|
67
67
|
cssDescriptors,
|
|
68
68
|
);
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
if (fontUrlWithoutParentheses.length > 0) {
|
|
71
|
+
fontFace
|
|
72
|
+
.load()
|
|
73
|
+
.then(() => {
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
75
|
+
(this.loaded as boolean) = true;
|
|
76
|
+
this.emit('loaded');
|
|
77
|
+
})
|
|
78
|
+
.catch(console.error);
|
|
79
|
+
} else {
|
|
80
|
+
// Default font
|
|
81
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
82
|
+
(this.loaded as boolean) = true;
|
|
83
|
+
this.emit('loaded');
|
|
84
|
+
}
|
|
78
85
|
|
|
79
86
|
this.fontFace = fontFace;
|
|
80
87
|
this.fontUrl = fontUrl;
|
|
@@ -90,6 +90,8 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
|
|
|
90
90
|
private fontFamilies: FontFamilyMap = {};
|
|
91
91
|
private fontFamilyArray: FontFamilyMap[] = [this.fontFamilies];
|
|
92
92
|
|
|
93
|
+
public type: 'canvas' | 'sdf' = 'canvas';
|
|
94
|
+
|
|
93
95
|
constructor(stage: Stage) {
|
|
94
96
|
super(stage);
|
|
95
97
|
if (typeof OffscreenCanvas !== 'undefined') {
|
|
@@ -142,6 +142,8 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
|
|
|
142
142
|
private sdfShader: SdfShader;
|
|
143
143
|
private rendererBounds: Bound;
|
|
144
144
|
|
|
145
|
+
public type: 'canvas' | 'sdf' = 'sdf';
|
|
146
|
+
|
|
145
147
|
constructor(stage: Stage) {
|
|
146
148
|
super(stage);
|
|
147
149
|
this.sdfShader = this.stage.shManager.loadShader('SdfShader').shader;
|
|
@@ -422,6 +422,7 @@ export abstract class TextRenderer<
|
|
|
422
422
|
StateT extends TextRendererState = TextRendererState,
|
|
423
423
|
> {
|
|
424
424
|
readonly set: Readonly<TrPropSetters<StateT>>;
|
|
425
|
+
abstract type: 'canvas' | 'sdf';
|
|
425
426
|
|
|
426
427
|
constructor(protected stage: Stage) {
|
|
427
428
|
const propSetters = {
|