@lightningjs/renderer 0.7.6 → 0.8.1
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 +4 -0
- package/dist/src/common/CommonTypes.d.ts +6 -0
- package/dist/src/core/CoreNode.d.ts +65 -8
- package/dist/src/core/CoreNode.js +104 -28
- package/dist/src/core/CoreNode.js.map +1 -1
- package/dist/src/core/CoreShaderManager.d.ts +2 -0
- package/dist/src/core/CoreShaderManager.js +2 -0
- package/dist/src/core/CoreShaderManager.js.map +1 -1
- package/dist/src/core/CoreTextNode.d.ts +5 -0
- package/dist/src/core/CoreTextNode.js +15 -10
- package/dist/src/core/CoreTextNode.js.map +1 -1
- package/dist/src/core/CoreTextureManager.js +2 -0
- package/dist/src/core/CoreTextureManager.js.map +1 -1
- package/dist/src/core/Stage.d.ts +4 -0
- package/dist/src/core/Stage.js +8 -1
- package/dist/src/core/Stage.js.map +1 -1
- package/dist/src/core/TextureMemoryManager.d.ts +12 -0
- package/dist/src/core/TextureMemoryManager.js +42 -0
- package/dist/src/core/TextureMemoryManager.js.map +1 -0
- package/dist/src/core/platform.js +8 -0
- package/dist/src/core/platform.js.map +1 -1
- package/dist/src/core/renderers/CoreContextTexture.d.ts +5 -1
- package/dist/src/core/renderers/CoreContextTexture.js +3 -1
- package/dist/src/core/renderers/CoreContextTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.d.ts +2 -1
- package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js +2 -2
- package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.d.ts +3 -1
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +22 -5
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlCoreRenderer.d.ts +3 -0
- package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +4 -2
- package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -1
- package/dist/src/core/renderers/webgl/shaders/effects/HolePunchEffect.d.ts +58 -0
- package/dist/src/core/renderers/webgl/shaders/effects/HolePunchEffect.js +112 -0
- package/dist/src/core/renderers/webgl/shaders/effects/HolePunchEffect.js.map +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js +17 -30
- package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js.map +1 -1
- package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.d.ts +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js +24 -30
- package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.d.ts +2 -0
- package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js +18 -0
- package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +8 -0
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +26 -4
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js +4 -3
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/TextRenderer.d.ts +19 -0
- package/dist/src/core/text-rendering/renderers/TextRenderer.js +26 -0
- package/dist/src/core/text-rendering/renderers/TextRenderer.js.map +1 -1
- package/dist/src/core/textures/Texture.d.ts +26 -1
- package/dist/src/core/textures/Texture.js +30 -1
- package/dist/src/core/textures/Texture.js.map +1 -1
- package/dist/src/main-api/ICoreDriver.d.ts +1 -0
- package/dist/src/main-api/Inspector.js +2 -1
- package/dist/src/main-api/Inspector.js.map +1 -1
- package/dist/src/main-api/RendererMain.d.ts +10 -1
- package/dist/src/main-api/RendererMain.js +6 -1
- package/dist/src/main-api/RendererMain.js.map +1 -1
- package/dist/src/render-drivers/main/MainCoreDriver.d.ts +1 -0
- package/dist/src/render-drivers/main/MainCoreDriver.js +7 -0
- package/dist/src/render-drivers/main/MainCoreDriver.js.map +1 -1
- package/dist/src/render-drivers/main/MainOnlyNode.d.ts +1 -0
- package/dist/src/render-drivers/main/MainOnlyNode.js +10 -6
- package/dist/src/render-drivers/main/MainOnlyNode.js.map +1 -1
- package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js +1 -0
- package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js.map +1 -1
- package/dist/src/render-drivers/threadx/ThreadXRendererMessage.d.ts +1 -0
- package/dist/src/render-drivers/threadx/ThreadXRendererMessage.js.map +1 -1
- package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js +3 -0
- package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js.map +1 -1
- package/dist/src/render-drivers/threadx/worker/renderer.js +1 -0
- package/dist/src/render-drivers/threadx/worker/renderer.js.map +1 -1
- package/dist/src/utils.d.ts +6 -0
- package/dist/src/utils.js +9 -1
- package/dist/src/utils.js.map +1 -1
- package/dist/tsconfig.dist.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/common/CommonTypes.ts +7 -0
- package/src/core/CoreNode.ts +118 -34
- package/src/core/CoreShaderManager.ts +3 -0
- package/src/core/CoreTextNode.ts +44 -43
- package/src/core/CoreTextureManager.ts +2 -0
- package/src/core/Stage.ts +10 -0
- package/src/core/TextureMemoryManager.ts +66 -0
- package/src/core/platform.ts +8 -0
- package/src/core/renderers/CoreContextTexture.ts +6 -1
- package/src/core/renderers/webgl/WebGlCoreCtxSubTexture.ts +7 -2
- package/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +34 -6
- package/src/core/renderers/webgl/WebGlCoreRenderer.ts +10 -2
- package/src/core/renderers/webgl/shaders/effects/HolePunchEffect.ts +166 -0
- package/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.ts +16 -32
- package/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.ts +26 -32
- package/src/core/text-rendering/renderers/CanvasTextRenderer.ts +23 -0
- package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +32 -4
- package/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.ts +4 -3
- package/src/core/text-rendering/renderers/TextRenderer.ts +32 -0
- package/src/core/textures/Texture.ts +39 -2
- package/src/main-api/ICoreDriver.ts +2 -0
- package/src/main-api/Inspector.ts +2 -1
- package/src/main-api/RendererMain.ts +19 -2
- package/src/render-drivers/main/MainCoreDriver.ts +9 -0
- package/src/render-drivers/main/MainOnlyNode.ts +12 -6
- package/src/render-drivers/threadx/ThreadXCoreDriver.ts +1 -0
- package/src/render-drivers/threadx/ThreadXRendererMessage.ts +1 -0
- package/src/render-drivers/threadx/worker/ThreadXRendererNode.ts +7 -0
- package/src/render-drivers/threadx/worker/renderer.ts +1 -0
- package/src/utils.ts +10 -1
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
import type { Dimensions } from '../../../common/CommonTypes.js';
|
|
21
21
|
import { assertTruthy } from '../../../utils.js';
|
|
22
|
+
import type { TextureMemoryManager } from '../../TextureMemoryManager.js';
|
|
22
23
|
import type { WebGlContextWrapper } from '../../lib/WebGlContextWrapper.js';
|
|
23
24
|
import type { Texture } from '../../textures/Texture.js';
|
|
24
25
|
import { isPowerOfTwo } from '../../utils.js';
|
|
@@ -44,8 +45,12 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
44
45
|
private _w = 0;
|
|
45
46
|
private _h = 0;
|
|
46
47
|
|
|
47
|
-
constructor(
|
|
48
|
-
|
|
48
|
+
constructor(
|
|
49
|
+
protected glw: WebGlContextWrapper,
|
|
50
|
+
memManager: TextureMemoryManager,
|
|
51
|
+
textureSource: Texture,
|
|
52
|
+
) {
|
|
53
|
+
super(memManager, textureSource);
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
get ctxTexture(): WebGLTexture {
|
|
@@ -56,6 +61,10 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
56
61
|
return this._nativeCtxTexture;
|
|
57
62
|
}
|
|
58
63
|
|
|
64
|
+
get renderable(): boolean {
|
|
65
|
+
return this.textureSource.renderable;
|
|
66
|
+
}
|
|
67
|
+
|
|
59
68
|
get w() {
|
|
60
69
|
return this._w;
|
|
61
70
|
}
|
|
@@ -80,8 +89,12 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
80
89
|
}
|
|
81
90
|
this._state = 'loading';
|
|
82
91
|
this.textureSource.setState('loading');
|
|
92
|
+
this._nativeCtxTexture = this.createNativeCtxTexture();
|
|
83
93
|
this.onLoadRequest()
|
|
84
94
|
.then(({ width, height }) => {
|
|
95
|
+
if (this._state === 'freed') {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
85
98
|
this._state = 'loaded';
|
|
86
99
|
this._w = width;
|
|
87
100
|
this._h = height;
|
|
@@ -100,8 +113,7 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
100
113
|
* Called when the texture data needs to be loaded and uploaded to a texture
|
|
101
114
|
*/
|
|
102
115
|
async onLoadRequest(): Promise<Dimensions> {
|
|
103
|
-
|
|
104
|
-
const { glw } = this;
|
|
116
|
+
const { glw, memManager } = this;
|
|
105
117
|
|
|
106
118
|
// On initial load request, create a 1x1 transparent texture to use until
|
|
107
119
|
// the texture data is finally loaded.
|
|
@@ -126,10 +138,17 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
126
138
|
glw.UNSIGNED_BYTE,
|
|
127
139
|
TRANSPARENT_TEXTURE_DATA,
|
|
128
140
|
);
|
|
141
|
+
memManager.setTextureMemUse(this, TRANSPARENT_TEXTURE_DATA.byteLength);
|
|
129
142
|
|
|
130
143
|
const textureData = await this.textureSource?.getTextureData();
|
|
144
|
+
// If the texture has been freed while loading, return early.
|
|
145
|
+
if (!this._nativeCtxTexture) {
|
|
146
|
+
assertTruthy(this._state === 'freed');
|
|
147
|
+
return { width: 0, height: 0 };
|
|
148
|
+
}
|
|
131
149
|
let width = 0;
|
|
132
150
|
let height = 0;
|
|
151
|
+
|
|
133
152
|
assertTruthy(this._nativeCtxTexture);
|
|
134
153
|
glw.activeTexture(0);
|
|
135
154
|
// If textureData is null, the texture is empty (0, 0) and we don't need to
|
|
@@ -150,6 +169,7 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
150
169
|
);
|
|
151
170
|
|
|
152
171
|
glw.texImage2D(0, glw.RGBA, glw.RGBA, glw.UNSIGNED_BYTE, data);
|
|
172
|
+
memManager.setTextureMemUse(this, width * height * 4);
|
|
153
173
|
|
|
154
174
|
// generate mipmaps for power-of-2 textures or in WebGL2RenderingContext
|
|
155
175
|
if (glw.isWebGl2() || (isPowerOfTwo(width) && isPowerOfTwo(height))) {
|
|
@@ -160,6 +180,7 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
160
180
|
height = 0;
|
|
161
181
|
// Reset to a 1x1 transparent texture
|
|
162
182
|
glw.bindTexture(this._nativeCtxTexture);
|
|
183
|
+
|
|
163
184
|
glw.texImage2D(
|
|
164
185
|
0,
|
|
165
186
|
glw.RGBA,
|
|
@@ -170,6 +191,7 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
170
191
|
glw.UNSIGNED_BYTE,
|
|
171
192
|
TRANSPARENT_TEXTURE_DATA,
|
|
172
193
|
);
|
|
194
|
+
memManager.setTextureMemUse(this, TRANSPARENT_TEXTURE_DATA.byteLength);
|
|
173
195
|
} else if ('mipmaps' in textureData.data && textureData.data.mipmaps) {
|
|
174
196
|
const {
|
|
175
197
|
mipmaps,
|
|
@@ -184,12 +206,14 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
184
206
|
: (mipmaps[0] as unknown as ArrayBufferView);
|
|
185
207
|
|
|
186
208
|
glw.bindTexture(this._nativeCtxTexture);
|
|
187
|
-
glw.compressedTexImage2D(0, glInternalFormat, width, height, 0, view);
|
|
188
209
|
|
|
210
|
+
glw.compressedTexImage2D(0, glInternalFormat, width, height, 0, view);
|
|
189
211
|
glw.texParameteri(glw.TEXTURE_WRAP_S, glw.CLAMP_TO_EDGE);
|
|
190
212
|
glw.texParameteri(glw.TEXTURE_WRAP_T, glw.CLAMP_TO_EDGE);
|
|
191
213
|
glw.texParameteri(glw.TEXTURE_MAG_FILTER, glw.LINEAR);
|
|
192
214
|
glw.texParameteri(glw.TEXTURE_MIN_FILTER, glw.LINEAR);
|
|
215
|
+
|
|
216
|
+
memManager.setTextureMemUse(this, view.byteLength);
|
|
193
217
|
} else {
|
|
194
218
|
console.error(
|
|
195
219
|
`WebGlCoreCtxTexture.onLoadRequest: Unexpected textureData returned`,
|
|
@@ -202,6 +226,7 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
202
226
|
height,
|
|
203
227
|
};
|
|
204
228
|
}
|
|
229
|
+
|
|
205
230
|
/**
|
|
206
231
|
* Free the WebGLTexture from the GPU
|
|
207
232
|
*
|
|
@@ -212,13 +237,16 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
212
237
|
return;
|
|
213
238
|
}
|
|
214
239
|
this._state = 'freed';
|
|
240
|
+
this.textureSource.setState('freed');
|
|
215
241
|
this._w = 0;
|
|
216
242
|
this._h = 0;
|
|
217
243
|
if (!this._nativeCtxTexture) {
|
|
218
244
|
return;
|
|
219
245
|
}
|
|
220
|
-
const { glw } = this;
|
|
246
|
+
const { glw, memManager } = this;
|
|
247
|
+
|
|
221
248
|
glw.deleteTexture(this._nativeCtxTexture);
|
|
249
|
+
memManager.setTextureMemUse(this, 0);
|
|
222
250
|
this._nativeCtxTexture = null;
|
|
223
251
|
}
|
|
224
252
|
|
|
@@ -57,6 +57,7 @@ import { WebGlCoreShader } from './WebGlCoreShader.js';
|
|
|
57
57
|
import { RoundedRectangle } from './shaders/RoundedRectangle.js';
|
|
58
58
|
import { ContextSpy } from '../../lib/ContextSpy.js';
|
|
59
59
|
import { WebGlContextWrapper } from '../../lib/WebGlContextWrapper.js';
|
|
60
|
+
import type { TextureMemoryManager } from '../../TextureMemoryManager.js';
|
|
60
61
|
|
|
61
62
|
const WORDS_PER_QUAD = 24;
|
|
62
63
|
const BYTES_PER_QUAD = WORDS_PER_QUAD * 4;
|
|
@@ -66,6 +67,7 @@ export interface WebGlCoreRendererOptions {
|
|
|
66
67
|
canvas: HTMLCanvasElement | OffscreenCanvas;
|
|
67
68
|
pixelRatio: number;
|
|
68
69
|
txManager: CoreTextureManager;
|
|
70
|
+
txMemManager: TextureMemoryManager;
|
|
69
71
|
shManager: CoreShaderManager;
|
|
70
72
|
clearColor: number;
|
|
71
73
|
bufferMemory: number;
|
|
@@ -84,6 +86,7 @@ export class WebGlCoreRenderer extends CoreRenderer {
|
|
|
84
86
|
|
|
85
87
|
//// Core Managers
|
|
86
88
|
txManager: CoreTextureManager;
|
|
89
|
+
txMemManager: TextureMemoryManager;
|
|
87
90
|
shManager: CoreShaderManager;
|
|
88
91
|
|
|
89
92
|
//// Options
|
|
@@ -114,6 +117,7 @@ export class WebGlCoreRenderer extends CoreRenderer {
|
|
|
114
117
|
const { canvas, clearColor, bufferMemory } = options;
|
|
115
118
|
this.options = options;
|
|
116
119
|
this.txManager = options.txManager;
|
|
120
|
+
this.txMemManager = options.txMemManager;
|
|
117
121
|
this.shManager = options.shManager;
|
|
118
122
|
this.defaultTexture = new ColorTexture(this.txManager);
|
|
119
123
|
// When the default texture is loaded, request a render in case the
|
|
@@ -198,9 +202,13 @@ export class WebGlCoreRenderer extends CoreRenderer {
|
|
|
198
202
|
|
|
199
203
|
override createCtxTexture(textureSource: Texture): CoreContextTexture {
|
|
200
204
|
if (textureSource instanceof SubTexture) {
|
|
201
|
-
return new WebGlCoreCtxSubTexture(
|
|
205
|
+
return new WebGlCoreCtxSubTexture(
|
|
206
|
+
this.glw,
|
|
207
|
+
this.txMemManager,
|
|
208
|
+
textureSource,
|
|
209
|
+
);
|
|
202
210
|
}
|
|
203
|
-
return new WebGlCoreCtxTexture(this.glw, textureSource);
|
|
211
|
+
return new WebGlCoreCtxTexture(this.glw, this.txMemManager, textureSource);
|
|
204
212
|
}
|
|
205
213
|
|
|
206
214
|
/**
|
|
@@ -0,0 +1,166 @@
|
|
|
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
|
+
import {
|
|
20
|
+
ShaderEffect,
|
|
21
|
+
type DefaultEffectProps,
|
|
22
|
+
type ShaderEffectUniforms,
|
|
23
|
+
} from './ShaderEffect.js';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Properties of the {@link RadiusEffect} shader
|
|
27
|
+
*/
|
|
28
|
+
export interface HolePunchEffectProps extends DefaultEffectProps {
|
|
29
|
+
/**
|
|
30
|
+
* X position where the hole punch starts
|
|
31
|
+
*/
|
|
32
|
+
x?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Y position where the hole punch starts
|
|
35
|
+
*/
|
|
36
|
+
y?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Width of the hole punch
|
|
39
|
+
*/
|
|
40
|
+
width?: number;
|
|
41
|
+
/**
|
|
42
|
+
* height of the hole punch
|
|
43
|
+
*
|
|
44
|
+
* @remarks if not defined uses the width value
|
|
45
|
+
*/
|
|
46
|
+
height?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Corner radius in pixels, to cut out of the corners of the hole punch
|
|
49
|
+
*
|
|
50
|
+
* @remarks
|
|
51
|
+
* You can input an array with a length of up to four or a number.
|
|
52
|
+
*
|
|
53
|
+
* array length 4:
|
|
54
|
+
* [topLeft, topRight, bottomRight, bottomLeft]
|
|
55
|
+
*
|
|
56
|
+
* array length 2:
|
|
57
|
+
* [20, 40] -> [20(topLeft), 40(topRight), 20(bottomRight), 40(bottomLeft)]
|
|
58
|
+
*
|
|
59
|
+
* array length 3:
|
|
60
|
+
* [20, 40, 60] -> [20(topLeft), 40(topRight), 60(bottomRight), 20(bottomLeft)]
|
|
61
|
+
*
|
|
62
|
+
* number:
|
|
63
|
+
* 30 -> [30, 30, 30, 30]
|
|
64
|
+
*
|
|
65
|
+
* @default 0
|
|
66
|
+
*/
|
|
67
|
+
radius?: number | number[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Masks the current maskcolor a holepunch effect with rounded corners similar to {@link RoundedRectangle}
|
|
72
|
+
*/
|
|
73
|
+
export class HolePunchEffect extends ShaderEffect {
|
|
74
|
+
static z$__type__Props: HolePunchEffectProps;
|
|
75
|
+
override readonly name = 'holePunch';
|
|
76
|
+
|
|
77
|
+
static override getEffectKey(): string {
|
|
78
|
+
return `holePunch`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static override uniforms: ShaderEffectUniforms = {
|
|
82
|
+
x: {
|
|
83
|
+
value: 0,
|
|
84
|
+
method: 'uniform1f',
|
|
85
|
+
type: 'float',
|
|
86
|
+
},
|
|
87
|
+
y: {
|
|
88
|
+
value: 0,
|
|
89
|
+
method: 'uniform1f',
|
|
90
|
+
type: 'float',
|
|
91
|
+
},
|
|
92
|
+
width: {
|
|
93
|
+
value: 0,
|
|
94
|
+
method: 'uniform1f',
|
|
95
|
+
type: 'float',
|
|
96
|
+
},
|
|
97
|
+
height: {
|
|
98
|
+
value: 0,
|
|
99
|
+
method: 'uniform1f',
|
|
100
|
+
type: 'float',
|
|
101
|
+
},
|
|
102
|
+
radius: {
|
|
103
|
+
value: 0,
|
|
104
|
+
method: 'uniform4fv',
|
|
105
|
+
type: 'vec4',
|
|
106
|
+
validator: (value: number | number[]) => {
|
|
107
|
+
let r = value;
|
|
108
|
+
if (Array.isArray(r)) {
|
|
109
|
+
if (r.length === 2) {
|
|
110
|
+
r = [r[0], r[1], r[0], r[1]] as number[];
|
|
111
|
+
} else if (r.length === 3) {
|
|
112
|
+
r = [r[0], r[1], r[2], r[0]] as number[];
|
|
113
|
+
} else if (r.length !== 4) {
|
|
114
|
+
r = [r[0], r[0], r[0], r[0]] as number[];
|
|
115
|
+
}
|
|
116
|
+
} else if (typeof r === 'number') {
|
|
117
|
+
r = [r, r, r, r];
|
|
118
|
+
}
|
|
119
|
+
return r;
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
static override resolveDefaults(
|
|
125
|
+
props: HolePunchEffectProps,
|
|
126
|
+
): Required<HolePunchEffectProps> {
|
|
127
|
+
return {
|
|
128
|
+
x: props.x || 0,
|
|
129
|
+
y: props.y || 0,
|
|
130
|
+
width: props.width || 50,
|
|
131
|
+
height: props.height || 50,
|
|
132
|
+
radius: props.radius ?? 0,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
static override methods: Record<string, string> = {
|
|
137
|
+
fillMask: `
|
|
138
|
+
float function(float dist) {
|
|
139
|
+
return clamp(-dist, 0.0, 1.0);
|
|
140
|
+
}
|
|
141
|
+
`,
|
|
142
|
+
boxDist: `
|
|
143
|
+
float function(vec2 p, vec2 size, float radius) {
|
|
144
|
+
size -= vec2(radius);
|
|
145
|
+
vec2 d = abs(p) - size;
|
|
146
|
+
return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - radius;
|
|
147
|
+
}
|
|
148
|
+
`,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
static override onShaderMask = `
|
|
152
|
+
vec2 halfDimensions = u_dimensions * 0.5;
|
|
153
|
+
vec2 size = vec2(width, height) * 0.5;
|
|
154
|
+
vec2 basePos = v_textureCoordinate.xy * u_dimensions.xy - vec2(x, y);
|
|
155
|
+
vec2 pos = basePos - size;
|
|
156
|
+
float r = radius[0] * step(pos.x, 0.5) * step(pos.y, 0.5);
|
|
157
|
+
r = r + radius[1] * step(0.5, pos.x) * step(pos.y, 0.5);
|
|
158
|
+
r = r + radius[2] * step(0.5, pos.x) * step(0.5, pos.y);
|
|
159
|
+
r = r + radius[3] * step(pos.x, 0.5) * step(0.5, pos.y);
|
|
160
|
+
return $boxDist(pos, size, r);
|
|
161
|
+
`;
|
|
162
|
+
|
|
163
|
+
static override onEffectMask = `
|
|
164
|
+
return mix(maskColor, vec4(0.0), $fillMask(shaderMask));
|
|
165
|
+
`;
|
|
166
|
+
}
|
|
@@ -61,13 +61,22 @@ export class LinearGradientEffect extends ShaderEffect {
|
|
|
61
61
|
): Required<LinearGradientEffectProps> {
|
|
62
62
|
const colors = props.colors ?? [0xff000000, 0xffffffff];
|
|
63
63
|
|
|
64
|
-
let stops = props.stops;
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
let stops = props.stops || [];
|
|
65
|
+
if (stops.length === 0 || stops.length !== colors.length) {
|
|
66
|
+
const colorsL = colors.length;
|
|
67
|
+
let i = 0;
|
|
68
|
+
const tmp = stops;
|
|
69
|
+
for (; i < colorsL; i++) {
|
|
70
|
+
if (stops[i]) {
|
|
71
|
+
tmp[i] = stops[i]!;
|
|
72
|
+
if (stops[i - 1] === undefined && tmp[i - 2] !== undefined) {
|
|
73
|
+
tmp[i - 1] = tmp[i - 2]! + (stops[i]! - tmp[i - 2]!) / 2;
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
tmp[i] = i * (1 / (colors.length - 1));
|
|
77
|
+
}
|
|
70
78
|
}
|
|
79
|
+
stops = tmp;
|
|
71
80
|
}
|
|
72
81
|
return {
|
|
73
82
|
colors,
|
|
@@ -94,28 +103,6 @@ export class LinearGradientEffect extends ShaderEffect {
|
|
|
94
103
|
},
|
|
95
104
|
stops: {
|
|
96
105
|
value: [],
|
|
97
|
-
validator: (
|
|
98
|
-
value: number[],
|
|
99
|
-
props: LinearGradientEffectProps,
|
|
100
|
-
): number[] => {
|
|
101
|
-
const colors = props.colors ?? [];
|
|
102
|
-
let stops = value;
|
|
103
|
-
const tmp: number[] = value;
|
|
104
|
-
if (stops.length === 0 || (stops && stops.length !== colors.length)) {
|
|
105
|
-
for (let i = 0; i < colors.length; i++) {
|
|
106
|
-
if (stops[i]) {
|
|
107
|
-
tmp[i] = stops[i]!;
|
|
108
|
-
if (stops[i - 1] === undefined && tmp[i - 2] !== undefined) {
|
|
109
|
-
tmp[i - 1] = tmp[i - 2]! + (stops[i]! - tmp[i - 2]!) / 2;
|
|
110
|
-
}
|
|
111
|
-
} else {
|
|
112
|
-
tmp[i] = i * (1 / (colors.length - 1));
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
stops = tmp;
|
|
116
|
-
}
|
|
117
|
-
return tmp;
|
|
118
|
-
},
|
|
119
106
|
size: (props: LinearGradientEffectProps) => props.colors!.length,
|
|
120
107
|
method: 'uniform1fv',
|
|
121
108
|
type: 'float',
|
|
@@ -166,10 +153,7 @@ export class LinearGradientEffect extends ShaderEffect {
|
|
|
166
153
|
|
|
167
154
|
float stopCalc = (dist - stops[0]) / (stops[1] - stops[0]);
|
|
168
155
|
vec4 colorOut = $fromLinear(mix($toLinear(colors[0]), $toLinear(colors[1]), stopCalc));
|
|
169
|
-
|
|
170
|
-
stopCalc = (dist - stops[i]) / (stops[i + 1] - stops[i]);
|
|
171
|
-
colorOut = mix(colorOut, colors[i + 1], clamp(stopCalc, 0.0, 1.0));
|
|
172
|
-
}
|
|
156
|
+
${this.ColorLoop(colors)}
|
|
173
157
|
return mix(maskColor, colorOut, clamp(colorOut.a, 0.0, 1.0));
|
|
174
158
|
`;
|
|
175
159
|
};
|
|
@@ -66,13 +66,22 @@ export class RadialGradientEffect extends ShaderEffect {
|
|
|
66
66
|
): Required<RadialGradientEffectProps> {
|
|
67
67
|
const colors = props.colors ?? [0xff000000, 0xffffffff];
|
|
68
68
|
|
|
69
|
-
let stops = props.stops;
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
let stops = props.stops || [];
|
|
70
|
+
if (stops.length === 0 || stops.length !== colors.length) {
|
|
71
|
+
const colorsL = colors.length;
|
|
72
|
+
let i = 0;
|
|
73
|
+
const tmp = stops;
|
|
74
|
+
for (; i < colorsL; i++) {
|
|
75
|
+
if (stops[i]) {
|
|
76
|
+
tmp[i] = stops[i]!;
|
|
77
|
+
if (stops[i - 1] === undefined && tmp[i - 2] !== undefined) {
|
|
78
|
+
tmp[i - 1] = tmp[i - 2]! + (stops[i]! - tmp[i - 2]!) / 2;
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
tmp[i] = i * (1 / (colors.length - 1));
|
|
82
|
+
}
|
|
75
83
|
}
|
|
84
|
+
stops = tmp;
|
|
76
85
|
}
|
|
77
86
|
return {
|
|
78
87
|
colors,
|
|
@@ -111,34 +120,22 @@ export class RadialGradientEffect extends ShaderEffect {
|
|
|
111
120
|
},
|
|
112
121
|
stops: {
|
|
113
122
|
value: [],
|
|
114
|
-
validator: (
|
|
115
|
-
value: number[],
|
|
116
|
-
props: RadialGradientEffectProps,
|
|
117
|
-
): number[] => {
|
|
118
|
-
const colors = props.colors ?? [];
|
|
119
|
-
let stops = value;
|
|
120
|
-
const tmp: number[] = value;
|
|
121
|
-
if (stops.length === 0 || (stops && stops.length !== colors.length)) {
|
|
122
|
-
for (let i = 0; i < colors.length; i++) {
|
|
123
|
-
if (stops[i]) {
|
|
124
|
-
tmp[i] = stops[i]!;
|
|
125
|
-
if (stops[i - 1] === undefined && tmp[i - 2] !== undefined) {
|
|
126
|
-
tmp[i - 1] = tmp[i - 2]! + (stops[i]! - tmp[i - 2]!) / 2;
|
|
127
|
-
}
|
|
128
|
-
} else {
|
|
129
|
-
tmp[i] = i * (1 / (colors.length - 1));
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
stops = tmp;
|
|
133
|
-
}
|
|
134
|
-
return tmp;
|
|
135
|
-
},
|
|
136
123
|
size: (props: RadialGradientEffectProps) => props.colors!.length,
|
|
137
124
|
method: 'uniform1fv',
|
|
138
125
|
type: 'float',
|
|
139
126
|
},
|
|
140
127
|
};
|
|
141
128
|
|
|
129
|
+
static ColorLoop = (amount: number): string => {
|
|
130
|
+
let loop = '';
|
|
131
|
+
for (let i = 2; i < amount; i++) {
|
|
132
|
+
loop += `colorOut = mix(colorOut, colors[${i}], clamp((dist - stops[${
|
|
133
|
+
i - 1
|
|
134
|
+
}]) / (stops[${i}] - stops[${i - 1}]), 0.0, 1.0));`;
|
|
135
|
+
}
|
|
136
|
+
return loop;
|
|
137
|
+
};
|
|
138
|
+
|
|
142
139
|
static override onColorize = (props: RadialGradientEffectProps) => {
|
|
143
140
|
const colors = props.colors!.length || 1;
|
|
144
141
|
return `
|
|
@@ -149,10 +146,7 @@ export class RadialGradientEffect extends ShaderEffect {
|
|
|
149
146
|
|
|
150
147
|
float stopCalc = (dist - stops[0]) / (stops[1] - stops[0]);
|
|
151
148
|
vec4 colorOut = mix(colors[0], colors[1], stopCalc);
|
|
152
|
-
|
|
153
|
-
stopCalc = (dist - stops[i]) / (stops[i + 1] - stops[i]);
|
|
154
|
-
colorOut = mix(colorOut, colors[i + 1], clamp(stopCalc, 0.0, 1.0));
|
|
155
|
-
}
|
|
149
|
+
${this.ColorLoop(colors)}
|
|
156
150
|
return mix(maskColor, colorOut, clamp(colorOut.a, 0.0, 1.0));
|
|
157
151
|
`;
|
|
158
152
|
};
|
|
@@ -290,6 +290,7 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
|
|
|
290
290
|
textH: 0,
|
|
291
291
|
fontInfo: undefined,
|
|
292
292
|
fontFaceLoadedHandler: undefined,
|
|
293
|
+
isRenderable: false,
|
|
293
294
|
debugData: {
|
|
294
295
|
updateCount: 0,
|
|
295
296
|
layoutCount: 0,
|
|
@@ -500,9 +501,11 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
|
|
|
500
501
|
for (const pageInfo of canvasPages) {
|
|
501
502
|
if (pageInfo.valid) continue;
|
|
502
503
|
if (pageInfo.lineNumStart < 0) {
|
|
504
|
+
pageInfo.texture?.setRenderableOwner(state, false);
|
|
503
505
|
pageInfo.texture = this.stage.txManager.loadTexture('ImageTexture', {
|
|
504
506
|
src: '',
|
|
505
507
|
});
|
|
508
|
+
pageInfo.texture.setRenderableOwner(state, state.isRenderable);
|
|
506
509
|
pageInfo.valid = true;
|
|
507
510
|
continue;
|
|
508
511
|
}
|
|
@@ -517,6 +520,7 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
|
|
|
517
520
|
),
|
|
518
521
|
});
|
|
519
522
|
if (!(this.canvas.width === 0 || this.canvas.height === 0)) {
|
|
523
|
+
pageInfo.texture?.setRenderableOwner(state, false);
|
|
520
524
|
pageInfo.texture = this.stage.txManager.loadTexture(
|
|
521
525
|
'ImageTexture',
|
|
522
526
|
{
|
|
@@ -531,6 +535,7 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
|
|
|
531
535
|
preload: true,
|
|
532
536
|
},
|
|
533
537
|
);
|
|
538
|
+
pageInfo.texture.setRenderableOwner(state, state.isRenderable);
|
|
534
539
|
}
|
|
535
540
|
pageInfo.valid = true;
|
|
536
541
|
}
|
|
@@ -694,6 +699,24 @@ export class CanvasTextRenderer extends TextRenderer<CanvasTextRendererState> {
|
|
|
694
699
|
// );
|
|
695
700
|
// }
|
|
696
701
|
}
|
|
702
|
+
|
|
703
|
+
override setIsRenderable(
|
|
704
|
+
state: CanvasTextRendererState,
|
|
705
|
+
renderable: boolean,
|
|
706
|
+
): void {
|
|
707
|
+
super.setIsRenderable(state, renderable);
|
|
708
|
+
// Set state object owner from any canvas page textures
|
|
709
|
+
state.canvasPages?.forEach((pageInfo) => {
|
|
710
|
+
pageInfo.texture?.setRenderableOwner(state, renderable);
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
override destroyState(state: CanvasTextRendererState): void {
|
|
715
|
+
// Remove state object owner from any canvas page textures
|
|
716
|
+
state.canvasPages?.forEach((pageInfo) => {
|
|
717
|
+
pageInfo.texture?.setRenderableOwner(state, false);
|
|
718
|
+
});
|
|
719
|
+
}
|
|
697
720
|
//#endregion Overrides
|
|
698
721
|
|
|
699
722
|
/**
|
|
@@ -150,22 +150,22 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
|
|
|
150
150
|
return {
|
|
151
151
|
fontFamily: (state, value) => {
|
|
152
152
|
state.props.fontFamily = value;
|
|
153
|
-
state
|
|
153
|
+
this.releaseFontFace(state);
|
|
154
154
|
this.invalidateLayoutCache(state);
|
|
155
155
|
},
|
|
156
156
|
fontWeight: (state, value) => {
|
|
157
157
|
state.props.fontWeight = value;
|
|
158
|
-
state
|
|
158
|
+
this.releaseFontFace(state);
|
|
159
159
|
this.invalidateLayoutCache(state);
|
|
160
160
|
},
|
|
161
161
|
fontStyle: (state, value) => {
|
|
162
162
|
state.props.fontStyle = value;
|
|
163
|
-
state
|
|
163
|
+
this.releaseFontFace(state);
|
|
164
164
|
this.invalidateLayoutCache(state);
|
|
165
165
|
},
|
|
166
166
|
fontStretch: (state, value) => {
|
|
167
167
|
state.props.fontStretch = value;
|
|
168
|
-
state
|
|
168
|
+
this.releaseFontFace(state);
|
|
169
169
|
this.invalidateLayoutCache(state);
|
|
170
170
|
},
|
|
171
171
|
fontSize: (state, value) => {
|
|
@@ -360,6 +360,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
|
|
|
360
360
|
textW: undefined,
|
|
361
361
|
distanceRange: 0,
|
|
362
362
|
trFontFace: undefined,
|
|
363
|
+
isRenderable: false,
|
|
363
364
|
debugData: {
|
|
364
365
|
updateCount: 0,
|
|
365
366
|
layoutCount: 0,
|
|
@@ -392,6 +393,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
|
|
|
392
393
|
this.setStatus(state, 'failed', new Error(msg));
|
|
393
394
|
return;
|
|
394
395
|
}
|
|
396
|
+
trFontFace.texture.setRenderableOwner(state, state.isRenderable);
|
|
395
397
|
}
|
|
396
398
|
|
|
397
399
|
// If the font hasn't been loaded yet, stop here.
|
|
@@ -741,6 +743,20 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
|
|
|
741
743
|
// debugData.drawCount++;
|
|
742
744
|
// }
|
|
743
745
|
}
|
|
746
|
+
|
|
747
|
+
override setIsRenderable(
|
|
748
|
+
state: SdfTextRendererState,
|
|
749
|
+
renderable: boolean,
|
|
750
|
+
): void {
|
|
751
|
+
super.setIsRenderable(state, renderable);
|
|
752
|
+
state.trFontFace?.texture.setRenderableOwner(state, renderable);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
override destroyState(state: SdfTextRendererState): void {
|
|
756
|
+
super.destroyState(state);
|
|
757
|
+
// If there's a Font Face assigned we must free the owner relation to its texture
|
|
758
|
+
state.trFontFace?.texture.setRenderableOwner(state, false);
|
|
759
|
+
}
|
|
744
760
|
//#endregion Overrides
|
|
745
761
|
|
|
746
762
|
public resolveFontFace(props: TrFontProps): SdfTrFontFace | undefined {
|
|
@@ -749,6 +765,18 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
|
|
|
749
765
|
| undefined;
|
|
750
766
|
}
|
|
751
767
|
|
|
768
|
+
/**
|
|
769
|
+
* Release the loaded SDF font face
|
|
770
|
+
*
|
|
771
|
+
* @param state
|
|
772
|
+
*/
|
|
773
|
+
protected releaseFontFace(state: SdfTextRendererState) {
|
|
774
|
+
if (state.trFontFace) {
|
|
775
|
+
state.trFontFace.texture.setRenderableOwner(state, false);
|
|
776
|
+
state.trFontFace = undefined;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
752
780
|
/**
|
|
753
781
|
* Invalidate the layout cache stored in the state. This will cause the text
|
|
754
782
|
* to be re-layed out on the next update.
|
|
@@ -212,8 +212,6 @@ export function layoutText(
|
|
|
212
212
|
charEndX >= lineVertexW &&
|
|
213
213
|
// There is a last word that we can break to the next line
|
|
214
214
|
lastWord.codepointIndex !== -1 &&
|
|
215
|
-
// We have advanced at least one character since the last word started
|
|
216
|
-
lastWord.codepointIndex < glyph.cluster &&
|
|
217
215
|
// Prevents infinite loop when a single word is longer than the width
|
|
218
216
|
lastWord.xStart > 0
|
|
219
217
|
) {
|
|
@@ -237,6 +235,9 @@ export function layoutText(
|
|
|
237
235
|
);
|
|
238
236
|
curX = lastWord.xStart;
|
|
239
237
|
bufferOffset = lastWord.bufferOffset;
|
|
238
|
+
// HACK: For the rest of the line when inserting the overflow suffix,
|
|
239
|
+
// set contain = 'none' to prevent an infinite loop.
|
|
240
|
+
contain = 'none';
|
|
240
241
|
}
|
|
241
242
|
} else {
|
|
242
243
|
// This glyph fits, so we can add it to the buffer
|
|
@@ -286,8 +287,8 @@ export function layoutText(
|
|
|
286
287
|
}
|
|
287
288
|
|
|
288
289
|
maxY = Math.max(maxY, quadY + glyph.height);
|
|
290
|
+
maxX = Math.max(maxX, quadX + glyph.width);
|
|
289
291
|
curX += glyph.xAdvance;
|
|
290
|
-
maxX = Math.max(maxX, curX);
|
|
291
292
|
}
|
|
292
293
|
} else {
|
|
293
294
|
// Unmapped character
|