@vunk/plus 1.6.8 → 1.6.9
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/components/SendOutlined.mjs +8 -6
- package/components/SharedSystems.mjs +290 -72
- package/components/WebGLRenderer.mjs +32 -20
- package/components/WebGPURenderer.mjs +48 -10
- package/components/agent-chat-provider/index.d.ts +7 -0
- package/components/agent-chat-provider/index.mjs +6 -3
- package/components/agent-chat-provider/src/api.d.ts +3 -0
- package/components/agent-chat-provider/src/const-roles.d.ts +8 -0
- package/components/agent-chat-provider/src/const.d.ts +1 -0
- package/components/agent-chat-provider/src/index.vue.d.ts +22 -0
- package/components/agent-chat-provider/src/types.d.ts +50 -0
- package/components/agent-chat-provider/src/use.d.ts +5 -0
- package/components/avatar/index.d.ts +4 -0
- package/components/avatar/index.mjs +2 -2
- package/components/avatar/src/ctx.d.ts +6 -0
- package/components/avatar/src/index.vue.d.ts +180 -0
- package/components/avatar/src/types.d.ts +1 -0
- package/components/broadcasting-markdown/index.d.ts +7 -0
- package/components/broadcasting-markdown/index.mjs +78 -158
- package/components/broadcasting-markdown/src/const.d.ts +13 -0
- package/components/broadcasting-markdown/src/ctx.d.ts +129 -0
- package/components/broadcasting-markdown/src/howler-speech.vue.d.ts +104 -0
- package/components/broadcasting-markdown/src/index.vue.d.ts +439 -0
- package/components/broadcasting-markdown/src/paragraph.vue.d.ts +79 -0
- package/components/broadcasting-markdown/src/speech-error.vue.d.ts +67 -0
- package/components/broadcasting-markdown/src/types.d.ts +26 -0
- package/components/broadcasting-markdown/src/use.d.ts +21 -0
- package/components/browserAll.mjs +333 -562
- package/components/bubble-list/index.d.ts +5 -0
- package/components/bubble-list/index.mjs +91 -72
- package/components/bubble-list/src/ctx.d.ts +32 -0
- package/components/bubble-list/src/index.vue.d.ts +810 -0
- package/components/bubble-list/src/types.d.ts +4 -0
- package/components/bubble-templates/index.d.ts +4 -0
- package/components/bubble-templates/index.mjs +83 -68
- package/components/bubble-templates/src/index.vue.d.ts +595 -0
- package/components/bubble-templates/src/types.d.ts +40 -0
- package/components/chat-independent/index.d.ts +4 -0
- package/components/chat-independent/index.mjs +136 -115
- package/components/chat-independent/src/const.d.ts +13 -0
- package/components/chat-independent/src/ctx.d.ts +21 -0
- package/components/chat-independent/src/index.vue.d.ts +2429 -0
- package/components/chat-independent/src/types.d.ts +13 -0
- package/components/colorToUniform.mjs +2 -0
- package/components/index.d.ts +0 -0
- package/components/index2.mjs +11896 -8381
- package/components/init.mjs +1724 -998
- package/components/lottie-animation/index.d.ts +4 -0
- package/components/lottie-animation/index.mjs +2 -2
- package/components/lottie-animation/src/ctx.d.ts +10 -0
- package/components/lottie-animation/src/index.vue.d.ts +21 -0
- package/components/lottie-animation/src/types.d.ts +4 -0
- package/components/metahuman-broadcasting/index.d.ts +5 -0
- package/components/metahuman-broadcasting/index.mjs +57 -40
- package/components/metahuman-broadcasting/src/ctx.d.ts +57 -0
- package/components/metahuman-broadcasting/src/index.vue.d.ts +885 -0
- package/components/metahuman-broadcasting/src/types.d.ts +1 -0
- package/components/monaco-editor/index.d.ts +5 -0
- package/components/monaco-editor/index.mjs +2 -2
- package/components/monaco-editor/src/ctx.d.ts +23 -0
- package/components/monaco-editor/src/index.vue.d.ts +40 -0
- package/components/monaco-editor/src/types.d.ts +1 -0
- package/components/monaco-environment/index.d.ts +4 -0
- package/components/monaco-environment/index.mjs +1 -1
- package/components/monaco-environment/src/ctx.d.ts +8 -0
- package/components/monaco-environment/src/index.vue.d.ts +2 -0
- package/components/monaco-environment/src/types.d.ts +1 -0
- package/components/pixi-frame/index.d.ts +11 -0
- package/components/pixi-frame/index.mjs +2 -2
- package/components/pixi-frame/src/apng.vue.d.ts +161 -0
- package/components/pixi-frame/src/bitmap.vue.d.ts +158 -0
- package/components/pixi-frame/src/core.vue.d.ts +173 -0
- package/components/pixi-frame/src/ctx.d.ts +55 -0
- package/components/pixi-frame/src/index.vue.d.ts +591 -0
- package/components/pixi-frame/src/provider.vue.d.ts +2 -0
- package/components/pixi-frame/src/types.d.ts +17 -0
- package/components/pixi-frame/src/use.d.ts +16 -0
- package/components/pixi-frame/src/useSprite.d.ts +12 -0
- package/components/pixi-frame/src/video.vue.d.ts +159 -0
- package/components/pixi-frame/src/view.vue.d.ts +29 -0
- package/components/recorder-button/index.d.ts +4 -0
- package/components/recorder-button/index.mjs +2 -2
- package/components/recorder-button/src/ctx.d.ts +41 -0
- package/components/recorder-button/src/index.vue.d.ts +333 -0
- package/components/recorder-button/src/speech-to-text.d.ts +1 -0
- package/components/recorder-button/src/types.d.ts +7 -0
- package/components/routes-menu-content/index.d.ts +4 -0
- package/components/routes-menu-content/index.mjs +5 -5
- package/components/routes-menu-content/src/ctx.d.ts +17 -0
- package/components/routes-menu-content/src/index.vue.d.ts +350 -0
- package/components/routes-menu-content/src/item/core/ctx.d.ts +18 -0
- package/components/routes-menu-content/src/item/core/index.vue.d.ts +185 -0
- package/components/routes-menu-content/src/item/ctx.d.ts +14 -0
- package/components/routes-menu-content/src/item/index.vue.d.ts +316 -0
- package/components/routes-menu-content/src/types.d.ts +1 -0
- package/components/sender/index.d.ts +4 -0
- package/components/sender/index.mjs +180 -86
- package/components/sender/src/SenderHeader.vue.d.ts +270 -0
- package/components/sender/src/ctx.d.ts +64 -0
- package/components/sender/src/index.vue.d.ts +779 -0
- package/components/sender/src/types.d.ts +1 -0
- package/components/speech-button/index.d.ts +4 -0
- package/components/speech-button/index.mjs +13 -6
- package/components/speech-button/src/LoadingIcon.vue.d.ts +2 -0
- package/components/speech-button/src/RecordingIcon.vue.d.ts +2 -0
- package/components/speech-button/src/ctx.d.ts +10 -0
- package/components/speech-button/src/index.vue.d.ts +22 -0
- package/components/speech-button/src/types.d.ts +1 -0
- package/components/table-columns/index.d.ts +5 -0
- package/components/table-columns/index.mjs +6 -3
- package/components/table-columns/src/ctx.d.ts +59 -0
- package/components/table-columns/src/el-ctx.d.ts +57 -0
- package/components/table-columns/src/index.vue.d.ts +266 -0
- package/components/table-columns/src/types.d.ts +21 -0
- package/components/tables-v1/index.d.ts +5 -0
- package/components/tables-v1/index.mjs +3 -3
- package/components/tables-v1/src/ctx.d.ts +280 -0
- package/components/tables-v1/src/el-ctx.d.ts +262 -0
- package/components/tables-v1/src/index.vue.d.ts +3054 -0
- package/components/tables-v1/src/types.d.ts +3 -0
- package/components/tables-v1-checkbox/index.d.ts +5 -0
- package/components/tables-v1-checkbox/index.mjs +33 -269
- package/components/tables-v1-checkbox/src/ctx.d.ts +313 -0
- package/components/tables-v1-checkbox/src/index.vue.d.ts +3831 -0
- package/components/tables-v1-checkbox/src/types.d.ts +1 -0
- package/components/text/index.d.ts +4 -0
- package/components/text/index.mjs +52 -42
- package/components/text/src/ctx.d.ts +341 -0
- package/components/text/src/index.vue.d.ts +4593 -0
- package/components/text/src/types.d.ts +1 -0
- package/components/typing-markdown/index.d.ts +4 -0
- package/components/typing-markdown/index.mjs +2 -2
- package/components/typing-markdown/src/core.d.ts +2 -0
- package/components/typing-markdown/src/ctx.d.ts +41 -0
- package/components/typing-markdown/src/index.vue.d.ts +75 -0
- package/components/typing-markdown/src/types.d.ts +1 -0
- package/components/vditor/index.d.ts +5 -0
- package/components/vditor/index.mjs +9 -5
- package/components/vditor/src/ctx.d.ts +38 -0
- package/components/vditor/src/handle-fullscreen.vue.d.ts +18 -0
- package/components/vditor/src/handle-value.vue.d.ts +18 -0
- package/components/vditor/src/index.vue.d.ts +143 -0
- package/components/vditor/src/types.d.ts +9 -0
- package/components/vditor/src/use.d.ts +4 -0
- package/components/webworkerAll.mjs +1 -1
- package/composables/el-breakpoints/index.d.ts +5 -5
- package/composables/recorder/index.mjs +191 -288
- package/icons/end/index.mjs +3 -3
- package/icons/end/src/avatar.d.ts +2 -11
- package/icons/end/src/icon.d.ts +2 -56
- package/icons/keyboard/index.mjs +3 -3
- package/icons/keyboard/src/avatar.d.ts +2 -11
- package/icons/keyboard/src/icon.d.ts +2 -56
- package/icons/start/index.mjs +3 -3
- package/icons/start/src/avatar.d.ts +2 -11
- package/icons/start/src/icon.d.ts +2 -56
- package/icons/voice/index.mjs +2 -2
- package/icons/voice/src/avatar.d.ts +2 -11
- package/icons/voice/src/icon.d.ts +2 -56
- package/package.json +1 -1
package/components/init.mjs
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { E as ExtensionType, U as UPDATE_PRIORITY, T as Ticker,
|
|
1
|
+
import { E as ExtensionType, U as UPDATE_PRIORITY, T as Ticker, a2 as Geometry, k as UniformGroup, c as BindGroup, z as TexturePool, s as Texture, M as Matrix, R as RendererType, w as warn, F as Bounds, a9 as ViewContainer, aa as GraphicsContext, $ as deprecation, a0 as v8_0_0, b as Buffer, B as BufferUsage, D as DOMAdapter, ab as nextPow2, N as Rectangle, ac as CanvasTextMetrics, l as CanvasPool, ad as fontStringFromTextStyle, Y as Color, ae as getCanvasFillStyle, af as TextStyle, x as BigPool, ag as BatchableGraphics, ah as getAdjustedBlendModeBlend, J as getAttributeInfoFromFormat, ai as ViewableBuffer, q as Shader, t as GlProgram, G as GpuProgram, aj as TextureStyle, m as compileHighShaderGpuProgram, a4 as compileHighShaderGlProgram, p as roundPixelsBit, a7 as roundPixelsBitGl, n as colorBit, o as generateTextureBatchBit, a5 as colorBitGl, a6 as generateTextureBatchBitGl, a8 as getBatchSamplersUniformGroup, ak as BitmapFontManager, al as getBitmapTextLayout, am as Cache, an as updateQuadBounds, e as extensions, ao as GraphicsContextSystem } from './index2.mjs';
|
|
2
2
|
import { S as State, c as color32BitToUniform, a as localUniformBit, b as localUniformBitGl, B as BatchableSprite } from './colorToUniform.mjs';
|
|
3
3
|
|
|
4
4
|
class ResizePlugin {
|
|
5
5
|
/**
|
|
6
6
|
* Initialize the plugin with scope of application instance
|
|
7
|
-
* @static
|
|
8
7
|
* @private
|
|
9
8
|
* @param {object} [options] - See application options
|
|
10
9
|
*/
|
|
@@ -12,13 +11,6 @@ class ResizePlugin {
|
|
|
12
11
|
Object.defineProperty(
|
|
13
12
|
this,
|
|
14
13
|
"resizeTo",
|
|
15
|
-
/**
|
|
16
|
-
* The HTML element or window to automatically resize the
|
|
17
|
-
* renderer's view element to match width and height.
|
|
18
|
-
* @member {Window|HTMLElement}
|
|
19
|
-
* @name resizeTo
|
|
20
|
-
* @memberof app.Application#
|
|
21
|
-
*/
|
|
22
14
|
{
|
|
23
15
|
set(dom) {
|
|
24
16
|
globalThis.removeEventListener("resize", this.queueResize);
|
|
@@ -70,7 +62,6 @@ class ResizePlugin {
|
|
|
70
62
|
}
|
|
71
63
|
/**
|
|
72
64
|
* Clean up the ticker, scoped to application
|
|
73
|
-
* @static
|
|
74
65
|
* @private
|
|
75
66
|
*/
|
|
76
67
|
static destroy() {
|
|
@@ -88,7 +79,6 @@ ResizePlugin.extension = ExtensionType.Application;
|
|
|
88
79
|
class TickerPlugin {
|
|
89
80
|
/**
|
|
90
81
|
* Initialize the plugin with scope of application instance
|
|
91
|
-
* @static
|
|
92
82
|
* @private
|
|
93
83
|
* @param {object} [options] - See application options
|
|
94
84
|
*/
|
|
@@ -129,7 +119,6 @@ class TickerPlugin {
|
|
|
129
119
|
}
|
|
130
120
|
/**
|
|
131
121
|
* Clean up the ticker, scoped to application.
|
|
132
|
-
* @static
|
|
133
122
|
* @private
|
|
134
123
|
*/
|
|
135
124
|
static destroy() {
|
|
@@ -212,6 +201,55 @@ const quadGeometry = new Geometry({
|
|
|
212
201
|
},
|
|
213
202
|
indexBuffer: new Uint32Array([0, 1, 2, 0, 2, 3])
|
|
214
203
|
});
|
|
204
|
+
class FilterData {
|
|
205
|
+
constructor() {
|
|
206
|
+
/**
|
|
207
|
+
* Indicates whether the filter should be skipped.
|
|
208
|
+
* @type {boolean}
|
|
209
|
+
*/
|
|
210
|
+
this.skip = false;
|
|
211
|
+
/**
|
|
212
|
+
* The texture to which the filter is applied.
|
|
213
|
+
* @type {Texture}
|
|
214
|
+
*/
|
|
215
|
+
this.inputTexture = null;
|
|
216
|
+
/**
|
|
217
|
+
* The back texture used for blending, if required.
|
|
218
|
+
* @type {Texture | null}
|
|
219
|
+
*/
|
|
220
|
+
this.backTexture = null;
|
|
221
|
+
/**
|
|
222
|
+
* The list of filters to be applied.
|
|
223
|
+
* @type {Filter[]}
|
|
224
|
+
*/
|
|
225
|
+
this.filters = null;
|
|
226
|
+
/**
|
|
227
|
+
* The bounds of the filter area.
|
|
228
|
+
* @type {Bounds}
|
|
229
|
+
*/
|
|
230
|
+
this.bounds = new Bounds();
|
|
231
|
+
/**
|
|
232
|
+
* The container to which the filter is applied.
|
|
233
|
+
* @type {Container}
|
|
234
|
+
*/
|
|
235
|
+
this.container = null;
|
|
236
|
+
/**
|
|
237
|
+
* Indicates whether blending is required for the filter.
|
|
238
|
+
* @type {boolean}
|
|
239
|
+
*/
|
|
240
|
+
this.blendRequired = false;
|
|
241
|
+
/**
|
|
242
|
+
* The render surface where the output of the filter is rendered.
|
|
243
|
+
* @type {RenderSurface}
|
|
244
|
+
*/
|
|
245
|
+
this.outputRenderSurface = null;
|
|
246
|
+
/**
|
|
247
|
+
* The global frame of the filter area.
|
|
248
|
+
* @type {{ x: number, y: number, width: number, height: number }}
|
|
249
|
+
*/
|
|
250
|
+
this.globalFrame = { x: 0, y: 0, width: 0, height: 0 };
|
|
251
|
+
}
|
|
252
|
+
}
|
|
215
253
|
class FilterSystem {
|
|
216
254
|
constructor(renderer) {
|
|
217
255
|
this._filterStackIndex = 0;
|
|
@@ -234,147 +272,143 @@ class FilterSystem {
|
|
|
234
272
|
get activeBackTexture() {
|
|
235
273
|
return this._activeFilterData?.backTexture;
|
|
236
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Pushes a filter instruction onto the filter stack.
|
|
277
|
+
* @param instruction - The instruction containing the filter effect and container.
|
|
278
|
+
* @internal
|
|
279
|
+
*/
|
|
237
280
|
push(instruction) {
|
|
238
281
|
const renderer = this.renderer;
|
|
239
282
|
const filters = instruction.filterEffect.filters;
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
283
|
+
const filterData = this._pushFilterData();
|
|
284
|
+
filterData.skip = false;
|
|
285
|
+
filterData.filters = filters;
|
|
286
|
+
filterData.container = instruction.container;
|
|
287
|
+
filterData.outputRenderSurface = renderer.renderTarget.renderSurface;
|
|
288
|
+
const colorTextureSource = renderer.renderTarget.renderTarget.colorTexture.source;
|
|
289
|
+
const rootResolution = colorTextureSource.resolution;
|
|
290
|
+
const rootAntialias = colorTextureSource.antialias;
|
|
245
291
|
if (filters.length === 0) {
|
|
246
292
|
filterData.skip = true;
|
|
247
293
|
return;
|
|
248
294
|
}
|
|
249
295
|
const bounds = filterData.bounds;
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
bounds.clear();
|
|
254
|
-
bounds.addRect(instruction.filterEffect.filterArea);
|
|
255
|
-
bounds.applyMatrix(instruction.container.worldTransform);
|
|
256
|
-
} else {
|
|
257
|
-
instruction.container.getFastGlobalBounds(true, bounds);
|
|
258
|
-
}
|
|
259
|
-
if (instruction.container) {
|
|
260
|
-
const renderGroup = instruction.container.renderGroup || instruction.container.parentRenderGroup;
|
|
261
|
-
const filterFrameTransform = renderGroup.cacheToLocalTransform;
|
|
262
|
-
if (filterFrameTransform) {
|
|
263
|
-
bounds.applyMatrix(filterFrameTransform);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
const colorTextureSource = renderer.renderTarget.renderTarget.colorTexture.source;
|
|
267
|
-
let resolution = Infinity;
|
|
268
|
-
let padding = 0;
|
|
269
|
-
let antialias = true;
|
|
270
|
-
let blendRequired = false;
|
|
271
|
-
let enabled = false;
|
|
272
|
-
let clipToViewport = true;
|
|
273
|
-
for (let i = 0; i < filters.length; i++) {
|
|
274
|
-
const filter = filters[i];
|
|
275
|
-
resolution = Math.min(resolution, filter.resolution === "inherit" ? colorTextureSource._resolution : filter.resolution);
|
|
276
|
-
padding += filter.padding;
|
|
277
|
-
if (filter.antialias === "off") {
|
|
278
|
-
antialias = false;
|
|
279
|
-
} else if (filter.antialias === "inherit") {
|
|
280
|
-
antialias && (antialias = colorTextureSource.antialias);
|
|
281
|
-
}
|
|
282
|
-
if (!filter.clipToViewport) {
|
|
283
|
-
clipToViewport = false;
|
|
284
|
-
}
|
|
285
|
-
const isCompatible = !!(filter.compatibleRenderers & renderer.type);
|
|
286
|
-
if (!isCompatible) {
|
|
287
|
-
enabled = false;
|
|
288
|
-
break;
|
|
289
|
-
}
|
|
290
|
-
if (filter.blendRequired && !(renderer.backBuffer?.useBackBuffer ?? true)) {
|
|
291
|
-
warn("Blend filter requires backBuffer on WebGL renderer to be enabled. Set `useBackBuffer: true` in the renderer options.");
|
|
292
|
-
enabled = false;
|
|
293
|
-
break;
|
|
294
|
-
}
|
|
295
|
-
enabled = filter.enabled || enabled;
|
|
296
|
-
blendRequired || (blendRequired = filter.blendRequired);
|
|
297
|
-
}
|
|
298
|
-
if (!enabled) {
|
|
299
|
-
filterData.skip = true;
|
|
296
|
+
this._calculateFilterArea(instruction, bounds);
|
|
297
|
+
this._calculateFilterBounds(filterData, renderer.renderTarget.rootViewPort, rootAntialias, rootResolution, 1);
|
|
298
|
+
if (filterData.skip) {
|
|
300
299
|
return;
|
|
301
300
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
301
|
+
const previousFilterData = this._getPreviousFilterData();
|
|
302
|
+
const globalResolution = this._findFilterResolution(rootResolution);
|
|
303
|
+
let offsetX = 0;
|
|
304
|
+
let offsetY = 0;
|
|
305
|
+
if (previousFilterData) {
|
|
306
|
+
offsetX = previousFilterData.bounds.minX;
|
|
307
|
+
offsetY = previousFilterData.bounds.minY;
|
|
306
308
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
+
this._calculateGlobalFrame(
|
|
310
|
+
filterData,
|
|
311
|
+
offsetX,
|
|
312
|
+
offsetY,
|
|
313
|
+
globalResolution,
|
|
314
|
+
colorTextureSource.width,
|
|
315
|
+
colorTextureSource.height
|
|
316
|
+
);
|
|
317
|
+
this._setupFilterTextures(filterData, bounds, renderer, previousFilterData);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Applies filters to a texture.
|
|
321
|
+
*
|
|
322
|
+
* This method takes a texture and a list of filters, applies the filters to the texture,
|
|
323
|
+
* and returns the resulting texture.
|
|
324
|
+
* @param {object} params - The parameters for applying filters.
|
|
325
|
+
* @param {Texture} params.texture - The texture to apply filters to.
|
|
326
|
+
* @param {Filter[]} params.filters - The filters to apply.
|
|
327
|
+
* @returns {Texture} The resulting texture after all filters have been applied.
|
|
328
|
+
* @example
|
|
329
|
+
*
|
|
330
|
+
* ```ts
|
|
331
|
+
* // Create a texture and a list of filters
|
|
332
|
+
* const texture = new Texture(...);
|
|
333
|
+
* const filters = [new BlurFilter(), new ColorMatrixFilter()];
|
|
334
|
+
*
|
|
335
|
+
* // Apply the filters to the texture
|
|
336
|
+
* const resultTexture = filterSystem.applyToTexture({ texture, filters });
|
|
337
|
+
*
|
|
338
|
+
* // Use the resulting texture
|
|
339
|
+
* sprite.texture = resultTexture;
|
|
340
|
+
* ```
|
|
341
|
+
*
|
|
342
|
+
* Key Points:
|
|
343
|
+
* 1. padding is not currently supported here - so clipping may occur with filters that use padding.
|
|
344
|
+
* 2. If all filters are disabled or skipped, the original texture is returned.
|
|
345
|
+
*/
|
|
346
|
+
generateFilteredTexture({ texture, filters }) {
|
|
347
|
+
const filterData = this._pushFilterData();
|
|
348
|
+
this._activeFilterData = filterData;
|
|
349
|
+
filterData.skip = false;
|
|
350
|
+
filterData.filters = filters;
|
|
351
|
+
const colorTextureSource = texture.source;
|
|
352
|
+
const rootResolution = colorTextureSource.resolution;
|
|
353
|
+
const rootAntialias = colorTextureSource.antialias;
|
|
354
|
+
if (filters.length === 0) {
|
|
309
355
|
filterData.skip = true;
|
|
310
|
-
return;
|
|
356
|
+
return texture;
|
|
311
357
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
filterData.
|
|
315
|
-
filterData.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
358
|
+
const bounds = filterData.bounds;
|
|
359
|
+
bounds.addRect(texture.frame);
|
|
360
|
+
this._calculateFilterBounds(filterData, bounds.rectangle, rootAntialias, rootResolution, 0);
|
|
361
|
+
if (filterData.skip) {
|
|
362
|
+
return texture;
|
|
363
|
+
}
|
|
364
|
+
const globalResolution = rootResolution;
|
|
365
|
+
const offsetX = 0;
|
|
366
|
+
const offsetY = 0;
|
|
367
|
+
this._calculateGlobalFrame(
|
|
368
|
+
filterData,
|
|
369
|
+
offsetX,
|
|
370
|
+
offsetY,
|
|
371
|
+
globalResolution,
|
|
372
|
+
colorTextureSource.width,
|
|
373
|
+
colorTextureSource.height
|
|
374
|
+
);
|
|
375
|
+
filterData.outputRenderSurface = TexturePool.getOptimalTexture(
|
|
319
376
|
bounds.width,
|
|
320
377
|
bounds.height,
|
|
321
|
-
resolution,
|
|
322
|
-
antialias
|
|
378
|
+
filterData.resolution,
|
|
379
|
+
filterData.antialias
|
|
323
380
|
);
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
381
|
+
filterData.backTexture = Texture.EMPTY;
|
|
382
|
+
filterData.inputTexture = texture;
|
|
383
|
+
const renderer = this.renderer;
|
|
384
|
+
renderer.renderTarget.finishRenderPass();
|
|
385
|
+
this._applyFiltersToTexture(filterData, true);
|
|
386
|
+
const outputTexture = filterData.outputRenderSurface;
|
|
387
|
+
outputTexture.source.alphaMode = "premultiplied-alpha";
|
|
388
|
+
return outputTexture;
|
|
328
389
|
}
|
|
390
|
+
/** @internal */
|
|
329
391
|
pop() {
|
|
330
392
|
const renderer = this.renderer;
|
|
331
|
-
this.
|
|
332
|
-
const filterData = this._filterStack[this._filterStackIndex];
|
|
393
|
+
const filterData = this._popFilterData();
|
|
333
394
|
if (filterData.skip) {
|
|
334
395
|
return;
|
|
335
396
|
}
|
|
336
|
-
this._activeFilterData = filterData;
|
|
337
|
-
const inputTexture = filterData.inputTexture;
|
|
338
|
-
const bounds = filterData.bounds;
|
|
339
|
-
let backTexture = Texture.EMPTY;
|
|
340
|
-
renderer.renderTarget.finishRenderPass();
|
|
341
|
-
if (filterData.blendRequired) {
|
|
342
|
-
const previousBounds = this._filterStackIndex > 0 ? this._filterStack[this._filterStackIndex - 1].bounds : null;
|
|
343
|
-
const renderTarget = renderer.renderTarget.getRenderTarget(filterData.previousRenderSurface);
|
|
344
|
-
backTexture = this.getBackTexture(renderTarget, bounds, previousBounds);
|
|
345
|
-
}
|
|
346
|
-
filterData.backTexture = backTexture;
|
|
347
|
-
const filters = filterData.filterEffect.filters;
|
|
348
|
-
this._globalFilterBindGroup.setResource(inputTexture.source.style, 2);
|
|
349
|
-
this._globalFilterBindGroup.setResource(backTexture.source, 3);
|
|
350
397
|
renderer.globalUniforms.pop();
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
} else {
|
|
355
|
-
let flip = filterData.inputTexture;
|
|
356
|
-
let flop = TexturePool.getOptimalTexture(
|
|
357
|
-
bounds.width,
|
|
358
|
-
bounds.height,
|
|
359
|
-
flip.source._resolution,
|
|
360
|
-
false
|
|
361
|
-
);
|
|
362
|
-
let i = 0;
|
|
363
|
-
for (i = 0; i < filters.length - 1; ++i) {
|
|
364
|
-
const filter = filters[i];
|
|
365
|
-
filter.apply(this, flip, flop, true);
|
|
366
|
-
const t = flip;
|
|
367
|
-
flip = flop;
|
|
368
|
-
flop = t;
|
|
369
|
-
}
|
|
370
|
-
filters[i].apply(this, flip, filterData.previousRenderSurface, false);
|
|
371
|
-
TexturePool.returnTexture(flip);
|
|
372
|
-
TexturePool.returnTexture(flop);
|
|
373
|
-
}
|
|
398
|
+
renderer.renderTarget.finishRenderPass();
|
|
399
|
+
this._activeFilterData = filterData;
|
|
400
|
+
this._applyFiltersToTexture(filterData, false);
|
|
374
401
|
if (filterData.blendRequired) {
|
|
375
|
-
TexturePool.returnTexture(backTexture);
|
|
402
|
+
TexturePool.returnTexture(filterData.backTexture);
|
|
376
403
|
}
|
|
404
|
+
TexturePool.returnTexture(filterData.inputTexture);
|
|
377
405
|
}
|
|
406
|
+
/**
|
|
407
|
+
* Copies the last render surface to a texture.
|
|
408
|
+
* @param lastRenderSurface - The last render surface to copy from.
|
|
409
|
+
* @param bounds - The bounds of the area to copy.
|
|
410
|
+
* @param previousBounds - The previous bounds to use for offsetting the copy.
|
|
411
|
+
*/
|
|
378
412
|
getBackTexture(lastRenderSurface, bounds, previousBounds) {
|
|
379
413
|
const backgroundResolution = lastRenderSurface.colorTexture.source._resolution;
|
|
380
414
|
const backTexture = TexturePool.getOptimalTexture(
|
|
@@ -402,23 +436,144 @@ class FilterSystem {
|
|
|
402
436
|
);
|
|
403
437
|
return backTexture;
|
|
404
438
|
}
|
|
439
|
+
/**
|
|
440
|
+
* Applies a filter to a texture.
|
|
441
|
+
* @param filter - The filter to apply.
|
|
442
|
+
* @param input - The input texture.
|
|
443
|
+
* @param output - The output render surface.
|
|
444
|
+
* @param clear - Whether to clear the output surface before applying the filter.
|
|
445
|
+
*/
|
|
405
446
|
applyFilter(filter, input, output, clear) {
|
|
406
447
|
const renderer = this.renderer;
|
|
407
|
-
const filterData = this.
|
|
408
|
-
const
|
|
409
|
-
const
|
|
410
|
-
const
|
|
411
|
-
const
|
|
412
|
-
let
|
|
413
|
-
let
|
|
414
|
-
|
|
415
|
-
|
|
448
|
+
const filterData = this._activeFilterData;
|
|
449
|
+
const outputRenderSurface = filterData.outputRenderSurface;
|
|
450
|
+
const isFinalTarget = outputRenderSurface === output;
|
|
451
|
+
const rootResolution = renderer.renderTarget.rootRenderTarget.colorTexture.source._resolution;
|
|
452
|
+
const resolution = this._findFilterResolution(rootResolution);
|
|
453
|
+
let offsetX = 0;
|
|
454
|
+
let offsetY = 0;
|
|
455
|
+
if (isFinalTarget) {
|
|
456
|
+
const offset = this._findPreviousFilterOffset();
|
|
457
|
+
offsetX = offset.x;
|
|
458
|
+
offsetY = offset.y;
|
|
459
|
+
}
|
|
460
|
+
this._updateFilterUniforms(input, output, filterData, offsetX, offsetY, resolution, isFinalTarget, clear);
|
|
461
|
+
this._setupBindGroupsAndRender(filter, input, renderer);
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Multiply _input normalized coordinates_ to this matrix to get _sprite texture normalized coordinates_.
|
|
465
|
+
*
|
|
466
|
+
* Use `outputMatrix * vTextureCoord` in the shader.
|
|
467
|
+
* @param outputMatrix - The matrix to output to.
|
|
468
|
+
* @param {Sprite} sprite - The sprite to map to.
|
|
469
|
+
* @returns The mapped matrix.
|
|
470
|
+
*/
|
|
471
|
+
calculateSpriteMatrix(outputMatrix, sprite) {
|
|
472
|
+
const data = this._activeFilterData;
|
|
473
|
+
const mappedMatrix = outputMatrix.set(
|
|
474
|
+
data.inputTexture._source.width,
|
|
475
|
+
0,
|
|
476
|
+
0,
|
|
477
|
+
data.inputTexture._source.height,
|
|
478
|
+
data.bounds.minX,
|
|
479
|
+
data.bounds.minY
|
|
480
|
+
);
|
|
481
|
+
const worldTransform = sprite.worldTransform.copyTo(Matrix.shared);
|
|
482
|
+
const renderGroup = sprite.renderGroup || sprite.parentRenderGroup;
|
|
483
|
+
if (renderGroup && renderGroup.cacheToLocalTransform) {
|
|
484
|
+
worldTransform.prepend(renderGroup.cacheToLocalTransform);
|
|
485
|
+
}
|
|
486
|
+
worldTransform.invert();
|
|
487
|
+
mappedMatrix.prepend(worldTransform);
|
|
488
|
+
mappedMatrix.scale(
|
|
489
|
+
1 / sprite.texture.frame.width,
|
|
490
|
+
1 / sprite.texture.frame.height
|
|
491
|
+
);
|
|
492
|
+
mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y);
|
|
493
|
+
return mappedMatrix;
|
|
494
|
+
}
|
|
495
|
+
destroy() {
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Sets up the bind groups and renders the filter.
|
|
499
|
+
* @param filter - The filter to apply
|
|
500
|
+
* @param input - The input texture
|
|
501
|
+
* @param renderer - The renderer instance
|
|
502
|
+
*/
|
|
503
|
+
_setupBindGroupsAndRender(filter, input, renderer) {
|
|
504
|
+
if (renderer.renderPipes.uniformBatch) {
|
|
505
|
+
const batchUniforms = renderer.renderPipes.uniformBatch.getUboResource(this._filterGlobalUniforms);
|
|
506
|
+
this._globalFilterBindGroup.setResource(batchUniforms, 0);
|
|
507
|
+
} else {
|
|
508
|
+
this._globalFilterBindGroup.setResource(this._filterGlobalUniforms, 0);
|
|
509
|
+
}
|
|
510
|
+
this._globalFilterBindGroup.setResource(input.source, 1);
|
|
511
|
+
this._globalFilterBindGroup.setResource(input.source.style, 2);
|
|
512
|
+
filter.groups[0] = this._globalFilterBindGroup;
|
|
513
|
+
renderer.encoder.draw({
|
|
514
|
+
geometry: quadGeometry,
|
|
515
|
+
shader: filter,
|
|
516
|
+
state: filter._state,
|
|
517
|
+
topology: "triangle-list"
|
|
518
|
+
});
|
|
519
|
+
if (renderer.type === RendererType.WEBGL) {
|
|
520
|
+
renderer.renderTarget.finishRenderPass();
|
|
416
521
|
}
|
|
417
|
-
|
|
418
|
-
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Sets up the filter textures including input texture and back texture if needed.
|
|
525
|
+
* @param filterData - The filter data to update
|
|
526
|
+
* @param bounds - The bounds for the texture
|
|
527
|
+
* @param renderer - The renderer instance
|
|
528
|
+
* @param previousFilterData - The previous filter data for back texture calculation
|
|
529
|
+
*/
|
|
530
|
+
_setupFilterTextures(filterData, bounds, renderer, previousFilterData) {
|
|
531
|
+
filterData.backTexture = Texture.EMPTY;
|
|
532
|
+
if (filterData.blendRequired) {
|
|
533
|
+
renderer.renderTarget.finishRenderPass();
|
|
534
|
+
const renderTarget = renderer.renderTarget.getRenderTarget(filterData.outputRenderSurface);
|
|
535
|
+
filterData.backTexture = this.getBackTexture(renderTarget, bounds, previousFilterData?.bounds);
|
|
419
536
|
}
|
|
420
|
-
|
|
421
|
-
|
|
537
|
+
filterData.inputTexture = TexturePool.getOptimalTexture(
|
|
538
|
+
bounds.width,
|
|
539
|
+
bounds.height,
|
|
540
|
+
filterData.resolution,
|
|
541
|
+
filterData.antialias
|
|
542
|
+
);
|
|
543
|
+
renderer.renderTarget.bind(filterData.inputTexture, true);
|
|
544
|
+
renderer.globalUniforms.push({
|
|
545
|
+
offset: bounds
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Calculates and sets the global frame for the filter.
|
|
550
|
+
* @param filterData - The filter data to update
|
|
551
|
+
* @param offsetX - The X offset
|
|
552
|
+
* @param offsetY - The Y offset
|
|
553
|
+
* @param globalResolution - The global resolution
|
|
554
|
+
* @param sourceWidth - The source texture width
|
|
555
|
+
* @param sourceHeight - The source texture height
|
|
556
|
+
*/
|
|
557
|
+
_calculateGlobalFrame(filterData, offsetX, offsetY, globalResolution, sourceWidth, sourceHeight) {
|
|
558
|
+
const globalFrame = filterData.globalFrame;
|
|
559
|
+
globalFrame.x = offsetX * globalResolution;
|
|
560
|
+
globalFrame.y = offsetY * globalResolution;
|
|
561
|
+
globalFrame.width = sourceWidth * globalResolution;
|
|
562
|
+
globalFrame.height = sourceHeight * globalResolution;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Updates the filter uniforms with the current filter state.
|
|
566
|
+
* @param input - The input texture
|
|
567
|
+
* @param output - The output render surface
|
|
568
|
+
* @param filterData - The current filter data
|
|
569
|
+
* @param offsetX - The X offset for positioning
|
|
570
|
+
* @param offsetY - The Y offset for positioning
|
|
571
|
+
* @param resolution - The current resolution
|
|
572
|
+
* @param isFinalTarget - Whether this is the final render target
|
|
573
|
+
* @param clear - Whether to clear the output surface
|
|
574
|
+
*/
|
|
575
|
+
_updateFilterUniforms(input, output, filterData, offsetX, offsetY, resolution, isFinalTarget, clear) {
|
|
576
|
+
const uniforms = this._filterGlobalUniforms.uniforms;
|
|
422
577
|
const outputFrame = uniforms.uOutputFrame;
|
|
423
578
|
const inputSize = uniforms.uInputSize;
|
|
424
579
|
const inputPixel = uniforms.uInputPixel;
|
|
@@ -426,18 +581,8 @@ class FilterSystem {
|
|
|
426
581
|
const globalFrame = uniforms.uGlobalFrame;
|
|
427
582
|
const outputTexture = uniforms.uOutputTexture;
|
|
428
583
|
if (isFinalTarget) {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
lastIndex--;
|
|
432
|
-
const filterData2 = this._filterStack[this._filterStackIndex - 1];
|
|
433
|
-
if (!filterData2.skip) {
|
|
434
|
-
offset.x = filterData2.bounds.minX;
|
|
435
|
-
offset.y = filterData2.bounds.minY;
|
|
436
|
-
break;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
outputFrame[0] = bounds.minX - offset.x;
|
|
440
|
-
outputFrame[1] = bounds.minY - offset.y;
|
|
584
|
+
outputFrame[0] = filterData.bounds.minX - offsetX;
|
|
585
|
+
outputFrame[1] = filterData.bounds.minY - offsetY;
|
|
441
586
|
} else {
|
|
442
587
|
outputFrame[0] = 0;
|
|
443
588
|
outputFrame[1] = 0;
|
|
@@ -457,12 +602,14 @@ class FilterSystem {
|
|
|
457
602
|
inputClamp[2] = input.frame.width * inputSize[2] - 0.5 * inputPixel[2];
|
|
458
603
|
inputClamp[3] = input.frame.height * inputSize[3] - 0.5 * inputPixel[3];
|
|
459
604
|
const rootTexture = this.renderer.renderTarget.rootRenderTarget.colorTexture;
|
|
460
|
-
globalFrame[0] =
|
|
461
|
-
globalFrame[1] =
|
|
605
|
+
globalFrame[0] = offsetX * resolution;
|
|
606
|
+
globalFrame[1] = offsetY * resolution;
|
|
462
607
|
globalFrame[2] = rootTexture.source.width * resolution;
|
|
463
608
|
globalFrame[3] = rootTexture.source.height * resolution;
|
|
609
|
+
if (output instanceof Texture)
|
|
610
|
+
output.source.resource = null;
|
|
464
611
|
const renderTarget = this.renderer.renderTarget.getRenderTarget(output);
|
|
465
|
-
renderer.renderTarget.bind(output, !!clear);
|
|
612
|
+
this.renderer.renderTarget.bind(output, !!clear);
|
|
466
613
|
if (output instanceof Texture) {
|
|
467
614
|
outputTexture[0] = output.frame.width;
|
|
468
615
|
outputTexture[1] = output.frame.height;
|
|
@@ -471,68 +618,165 @@ class FilterSystem {
|
|
|
471
618
|
outputTexture[1] = renderTarget.height;
|
|
472
619
|
}
|
|
473
620
|
outputTexture[2] = renderTarget.isRoot ? -1 : 1;
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
this.
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
geometry: quadGeometry,
|
|
486
|
-
shader: filter,
|
|
487
|
-
state: filter._state,
|
|
488
|
-
topology: "triangle-list"
|
|
489
|
-
});
|
|
490
|
-
if (renderer.type === RendererType.WEBGL) {
|
|
491
|
-
renderer.renderTarget.finishRenderPass();
|
|
621
|
+
this._filterGlobalUniforms.update();
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Finds the correct resolution by looking back through the filter stack.
|
|
625
|
+
* @param rootResolution - The fallback root resolution to use
|
|
626
|
+
* @returns The resolution from the previous filter or root resolution
|
|
627
|
+
*/
|
|
628
|
+
_findFilterResolution(rootResolution) {
|
|
629
|
+
let currentIndex = this._filterStackIndex - 1;
|
|
630
|
+
while (currentIndex > 0 && this._filterStack[currentIndex].skip) {
|
|
631
|
+
--currentIndex;
|
|
492
632
|
}
|
|
633
|
+
return currentIndex > 0 && this._filterStack[currentIndex].inputTexture ? this._filterStack[currentIndex].inputTexture.source._resolution : rootResolution;
|
|
493
634
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
635
|
+
/**
|
|
636
|
+
* Finds the offset from the previous non-skipped filter in the stack.
|
|
637
|
+
* @returns The offset coordinates from the previous filter
|
|
638
|
+
*/
|
|
639
|
+
_findPreviousFilterOffset() {
|
|
640
|
+
let offsetX = 0;
|
|
641
|
+
let offsetY = 0;
|
|
642
|
+
let lastIndex = this._filterStackIndex;
|
|
643
|
+
while (lastIndex > 0) {
|
|
644
|
+
lastIndex--;
|
|
645
|
+
const prevFilterData = this._filterStack[lastIndex];
|
|
646
|
+
if (!prevFilterData.skip) {
|
|
647
|
+
offsetX = prevFilterData.bounds.minX;
|
|
648
|
+
offsetY = prevFilterData.bounds.minY;
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return { x: offsetX, y: offsetY };
|
|
504
653
|
}
|
|
505
654
|
/**
|
|
506
|
-
*
|
|
507
|
-
*
|
|
508
|
-
*
|
|
509
|
-
* @param outputMatrix - The matrix to output to.
|
|
510
|
-
* @param {Sprite} sprite - The sprite to map to.
|
|
511
|
-
* @returns The mapped matrix.
|
|
655
|
+
* Calculates the filter area bounds based on the instruction type.
|
|
656
|
+
* @param instruction - The filter instruction
|
|
657
|
+
* @param bounds - The bounds object to populate
|
|
512
658
|
*/
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
659
|
+
_calculateFilterArea(instruction, bounds) {
|
|
660
|
+
if (instruction.renderables) {
|
|
661
|
+
getGlobalRenderableBounds(instruction.renderables, bounds);
|
|
662
|
+
} else if (instruction.filterEffect.filterArea) {
|
|
663
|
+
bounds.clear();
|
|
664
|
+
bounds.addRect(instruction.filterEffect.filterArea);
|
|
665
|
+
bounds.applyMatrix(instruction.container.worldTransform);
|
|
666
|
+
} else {
|
|
667
|
+
instruction.container.getFastGlobalBounds(true, bounds);
|
|
668
|
+
}
|
|
669
|
+
if (instruction.container) {
|
|
670
|
+
const renderGroup = instruction.container.renderGroup || instruction.container.parentRenderGroup;
|
|
671
|
+
const filterFrameTransform = renderGroup.cacheToLocalTransform;
|
|
672
|
+
if (filterFrameTransform) {
|
|
673
|
+
bounds.applyMatrix(filterFrameTransform);
|
|
674
|
+
}
|
|
527
675
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
);
|
|
534
|
-
|
|
535
|
-
|
|
676
|
+
}
|
|
677
|
+
_applyFiltersToTexture(filterData, clear) {
|
|
678
|
+
const inputTexture = filterData.inputTexture;
|
|
679
|
+
const bounds = filterData.bounds;
|
|
680
|
+
const filters = filterData.filters;
|
|
681
|
+
this._globalFilterBindGroup.setResource(inputTexture.source.style, 2);
|
|
682
|
+
this._globalFilterBindGroup.setResource(filterData.backTexture.source, 3);
|
|
683
|
+
if (filters.length === 1) {
|
|
684
|
+
filters[0].apply(this, inputTexture, filterData.outputRenderSurface, clear);
|
|
685
|
+
} else {
|
|
686
|
+
let flip = filterData.inputTexture;
|
|
687
|
+
const tempTexture = TexturePool.getOptimalTexture(
|
|
688
|
+
bounds.width,
|
|
689
|
+
bounds.height,
|
|
690
|
+
flip.source._resolution,
|
|
691
|
+
false
|
|
692
|
+
);
|
|
693
|
+
let flop = tempTexture;
|
|
694
|
+
let i = 0;
|
|
695
|
+
for (i = 0; i < filters.length - 1; ++i) {
|
|
696
|
+
const filter = filters[i];
|
|
697
|
+
filter.apply(this, flip, flop, true);
|
|
698
|
+
const t = flip;
|
|
699
|
+
flip = flop;
|
|
700
|
+
flop = t;
|
|
701
|
+
}
|
|
702
|
+
filters[i].apply(this, flip, filterData.outputRenderSurface, clear);
|
|
703
|
+
TexturePool.returnTexture(tempTexture);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
_calculateFilterBounds(filterData, viewPort, rootAntialias, rootResolution, paddingMultiplier) {
|
|
707
|
+
const renderer = this.renderer;
|
|
708
|
+
const bounds = filterData.bounds;
|
|
709
|
+
const filters = filterData.filters;
|
|
710
|
+
let resolution = Infinity;
|
|
711
|
+
let padding = 0;
|
|
712
|
+
let antialias = true;
|
|
713
|
+
let blendRequired = false;
|
|
714
|
+
let enabled = false;
|
|
715
|
+
let clipToViewport = true;
|
|
716
|
+
for (let i = 0; i < filters.length; i++) {
|
|
717
|
+
const filter = filters[i];
|
|
718
|
+
resolution = Math.min(resolution, filter.resolution === "inherit" ? rootResolution : filter.resolution);
|
|
719
|
+
padding += filter.padding;
|
|
720
|
+
if (filter.antialias === "off") {
|
|
721
|
+
antialias = false;
|
|
722
|
+
} else if (filter.antialias === "inherit") {
|
|
723
|
+
antialias && (antialias = rootAntialias);
|
|
724
|
+
}
|
|
725
|
+
if (!filter.clipToViewport) {
|
|
726
|
+
clipToViewport = false;
|
|
727
|
+
}
|
|
728
|
+
const isCompatible = !!(filter.compatibleRenderers & renderer.type);
|
|
729
|
+
if (!isCompatible) {
|
|
730
|
+
enabled = false;
|
|
731
|
+
break;
|
|
732
|
+
}
|
|
733
|
+
if (filter.blendRequired && !(renderer.backBuffer?.useBackBuffer ?? true)) {
|
|
734
|
+
warn("Blend filter requires backBuffer on WebGL renderer to be enabled. Set `useBackBuffer: true` in the renderer options.");
|
|
735
|
+
enabled = false;
|
|
736
|
+
break;
|
|
737
|
+
}
|
|
738
|
+
enabled = filter.enabled || enabled;
|
|
739
|
+
blendRequired || (blendRequired = filter.blendRequired);
|
|
740
|
+
}
|
|
741
|
+
if (!enabled) {
|
|
742
|
+
filterData.skip = true;
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
if (clipToViewport) {
|
|
746
|
+
bounds.fitBounds(0, viewPort.width / rootResolution, 0, viewPort.height / rootResolution);
|
|
747
|
+
}
|
|
748
|
+
bounds.scale(resolution).ceil().scale(1 / resolution).pad((padding | 0) * paddingMultiplier);
|
|
749
|
+
if (!bounds.isPositive) {
|
|
750
|
+
filterData.skip = true;
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
filterData.antialias = antialias;
|
|
754
|
+
filterData.resolution = resolution;
|
|
755
|
+
filterData.blendRequired = blendRequired;
|
|
756
|
+
}
|
|
757
|
+
_popFilterData() {
|
|
758
|
+
this._filterStackIndex--;
|
|
759
|
+
return this._filterStack[this._filterStackIndex];
|
|
760
|
+
}
|
|
761
|
+
_getPreviousFilterData() {
|
|
762
|
+
let previousFilterData;
|
|
763
|
+
let index = this._filterStackIndex - 1;
|
|
764
|
+
while (index > 1) {
|
|
765
|
+
index--;
|
|
766
|
+
previousFilterData = this._filterStack[index];
|
|
767
|
+
if (!previousFilterData.skip) {
|
|
768
|
+
break;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return previousFilterData;
|
|
772
|
+
}
|
|
773
|
+
_pushFilterData() {
|
|
774
|
+
let filterData = this._filterStack[this._filterStackIndex];
|
|
775
|
+
if (!filterData) {
|
|
776
|
+
filterData = this._filterStack[this._filterStackIndex] = new FilterData();
|
|
777
|
+
}
|
|
778
|
+
this._filterStackIndex++;
|
|
779
|
+
return filterData;
|
|
536
780
|
}
|
|
537
781
|
}
|
|
538
782
|
/** @ignore */
|
|
@@ -546,6 +790,7 @@ FilterSystem.extension = {
|
|
|
546
790
|
|
|
547
791
|
class Graphics extends ViewContainer {
|
|
548
792
|
/**
|
|
793
|
+
* Creates a new Graphics object.
|
|
549
794
|
* @param options - Options for the Graphics.
|
|
550
795
|
*/
|
|
551
796
|
constructor(options) {
|
|
@@ -557,6 +802,7 @@ class Graphics extends ViewContainer {
|
|
|
557
802
|
label: "Graphics",
|
|
558
803
|
...rest
|
|
559
804
|
});
|
|
805
|
+
/** @internal */
|
|
560
806
|
this.renderPipeId = "graphics";
|
|
561
807
|
if (!context) {
|
|
562
808
|
this._context = this._ownedContext = new GraphicsContext();
|
|
@@ -564,6 +810,7 @@ class Graphics extends ViewContainer {
|
|
|
564
810
|
this._context = context;
|
|
565
811
|
}
|
|
566
812
|
this._context.on("update", this.onViewUpdate, this);
|
|
813
|
+
this.didViewUpdate = true;
|
|
567
814
|
this.allowChildren = false;
|
|
568
815
|
this.roundPixels = roundPixels ?? false;
|
|
569
816
|
}
|
|
@@ -575,12 +822,53 @@ class Graphics extends ViewContainer {
|
|
|
575
822
|
this._context.on("update", this.onViewUpdate, this);
|
|
576
823
|
this.onViewUpdate();
|
|
577
824
|
}
|
|
825
|
+
/**
|
|
826
|
+
* The underlying graphics context used for drawing operations.
|
|
827
|
+
* Controls how shapes and paths are rendered.
|
|
828
|
+
* @example
|
|
829
|
+
* ```ts
|
|
830
|
+
* // Create a shared context
|
|
831
|
+
* const sharedContext = new GraphicsContext();
|
|
832
|
+
*
|
|
833
|
+
* // Create graphics objects sharing the same context
|
|
834
|
+
* const graphics1 = new Graphics();
|
|
835
|
+
* const graphics2 = new Graphics();
|
|
836
|
+
*
|
|
837
|
+
* // Assign shared context
|
|
838
|
+
* graphics1.context = sharedContext;
|
|
839
|
+
* graphics2.context = sharedContext;
|
|
840
|
+
*
|
|
841
|
+
* // Both graphics will show the same shapes
|
|
842
|
+
* sharedContext
|
|
843
|
+
* .rect(0, 0, 100, 100)
|
|
844
|
+
* .fill({ color: 0xff0000 });
|
|
845
|
+
* ```
|
|
846
|
+
* @see {@link GraphicsContext} For drawing operations
|
|
847
|
+
* @see {@link GraphicsOptions} For context configuration
|
|
848
|
+
*/
|
|
578
849
|
get context() {
|
|
579
850
|
return this._context;
|
|
580
851
|
}
|
|
581
852
|
/**
|
|
582
|
-
* The local bounds of the
|
|
583
|
-
*
|
|
853
|
+
* The local bounds of the graphics object.
|
|
854
|
+
* Returns the boundaries after all graphical operations but before any transforms.
|
|
855
|
+
* @example
|
|
856
|
+
* ```ts
|
|
857
|
+
* const graphics = new Graphics();
|
|
858
|
+
*
|
|
859
|
+
* // Draw a shape
|
|
860
|
+
* graphics
|
|
861
|
+
* .rect(0, 0, 100, 100)
|
|
862
|
+
* .fill({ color: 0xff0000 });
|
|
863
|
+
*
|
|
864
|
+
* // Get bounds information
|
|
865
|
+
* const bounds = graphics.bounds;
|
|
866
|
+
* console.log(bounds.width); // 100
|
|
867
|
+
* console.log(bounds.height); // 100
|
|
868
|
+
* ```
|
|
869
|
+
* @readonly
|
|
870
|
+
* @see {@link Bounds} For bounds operations
|
|
871
|
+
* @see {@link Container#getBounds} For transformed bounds
|
|
584
872
|
*/
|
|
585
873
|
get bounds() {
|
|
586
874
|
return this._context.bounds;
|
|
@@ -593,7 +881,25 @@ class Graphics extends ViewContainer {
|
|
|
593
881
|
}
|
|
594
882
|
/**
|
|
595
883
|
* Checks if the object contains the given point.
|
|
596
|
-
*
|
|
884
|
+
* Returns true if the point lies within the Graphics object's rendered area.
|
|
885
|
+
* @example
|
|
886
|
+
* ```ts
|
|
887
|
+
* const graphics = new Graphics();
|
|
888
|
+
*
|
|
889
|
+
* // Draw a shape
|
|
890
|
+
* graphics
|
|
891
|
+
* .rect(0, 0, 100, 100)
|
|
892
|
+
* .fill({ color: 0xff0000 });
|
|
893
|
+
*
|
|
894
|
+
* // Check point intersection
|
|
895
|
+
* if (graphics.containsPoint({ x: 50, y: 50 })) {
|
|
896
|
+
* console.log('Point is inside rectangle!');
|
|
897
|
+
* }
|
|
898
|
+
* ```
|
|
899
|
+
* @param point - The point to check in local coordinates
|
|
900
|
+
* @returns True if the point is inside the Graphics object
|
|
901
|
+
* @see {@link Graphics#bounds} For bounding box checks
|
|
902
|
+
* @see {@link PointData} For point data structure
|
|
597
903
|
*/
|
|
598
904
|
containsPoint(point) {
|
|
599
905
|
return this._context.containsPoint(point);
|
|
@@ -609,9 +915,13 @@ class Graphics extends ViewContainer {
|
|
|
609
915
|
* then you should pass destroy({ context: false })
|
|
610
916
|
*
|
|
611
917
|
* If the context was passed in as an argument to the constructor then it will not be destroyed
|
|
612
|
-
* @
|
|
613
|
-
*
|
|
614
|
-
*
|
|
918
|
+
* @example
|
|
919
|
+
* ```ts
|
|
920
|
+
* // Destroy the graphics and its context
|
|
921
|
+
* graphics.destroy();
|
|
922
|
+
* graphics.destroy(true);
|
|
923
|
+
* graphics.destroy({ context: true, texture: true, textureSource: true });
|
|
924
|
+
* ```
|
|
615
925
|
*/
|
|
616
926
|
destroy(options) {
|
|
617
927
|
if (this._ownedContext && !options) {
|
|
@@ -629,21 +939,104 @@ class Graphics extends ViewContainer {
|
|
|
629
939
|
}
|
|
630
940
|
// --------------------------------------- GraphicsContext methods ---------------------------------------
|
|
631
941
|
/**
|
|
632
|
-
* Sets the current fill style of the graphics context.
|
|
633
|
-
* pattern, or a
|
|
634
|
-
* @
|
|
635
|
-
*
|
|
636
|
-
*
|
|
942
|
+
* Sets the current fill style of the graphics context.
|
|
943
|
+
* The fill style can be a color, gradient, pattern, or a complex style object.
|
|
944
|
+
* @example
|
|
945
|
+
* ```ts
|
|
946
|
+
* const graphics = new Graphics();
|
|
947
|
+
*
|
|
948
|
+
* // Basic color fill
|
|
949
|
+
* graphics
|
|
950
|
+
* .setFillStyle({ color: 0xff0000 }) // Red fill
|
|
951
|
+
* .rect(0, 0, 100, 100)
|
|
952
|
+
* .fill();
|
|
953
|
+
*
|
|
954
|
+
* // Gradient fill
|
|
955
|
+
* const gradient = new FillGradient({
|
|
956
|
+
* end: { x: 1, y: 0 },
|
|
957
|
+
* colorStops: [
|
|
958
|
+
* { offset: 0, color: 0xff0000 }, // Red at start
|
|
959
|
+
* { offset: 0.5, color: 0x00ff00 }, // Green at middle
|
|
960
|
+
* { offset: 1, color: 0x0000ff }, // Blue at end
|
|
961
|
+
* ],
|
|
962
|
+
* });
|
|
963
|
+
*
|
|
964
|
+
* graphics
|
|
965
|
+
* .setFillStyle(gradient)
|
|
966
|
+
* .circle(100, 100, 50)
|
|
967
|
+
* .fill();
|
|
968
|
+
*
|
|
969
|
+
* // Pattern fill
|
|
970
|
+
* const pattern = new FillPattern(texture);
|
|
971
|
+
* graphics
|
|
972
|
+
* .setFillStyle({
|
|
973
|
+
* fill: pattern,
|
|
974
|
+
* alpha: 0.5
|
|
975
|
+
* })
|
|
976
|
+
* .rect(0, 0, 200, 200)
|
|
977
|
+
* .fill();
|
|
978
|
+
* ```
|
|
979
|
+
* @param {FillInput} args - The fill style to apply
|
|
980
|
+
* @returns The Graphics instance for chaining
|
|
981
|
+
* @see {@link FillStyle} For fill style options
|
|
982
|
+
* @see {@link FillGradient} For gradient fills
|
|
983
|
+
* @see {@link FillPattern} For pattern fills
|
|
637
984
|
*/
|
|
638
985
|
setFillStyle(...args) {
|
|
639
986
|
return this._callContextMethod("setFillStyle", args);
|
|
640
987
|
}
|
|
641
988
|
/**
|
|
642
|
-
* Sets the current stroke style of the graphics context.
|
|
643
|
-
* encompass colors, gradients, patterns, or more detailed configurations
|
|
644
|
-
* @
|
|
645
|
-
*
|
|
646
|
-
*
|
|
989
|
+
* Sets the current stroke style of the graphics context.
|
|
990
|
+
* Similar to fill styles, stroke styles can encompass colors, gradients, patterns, or more detailed configurations.
|
|
991
|
+
* @example
|
|
992
|
+
* ```ts
|
|
993
|
+
* const graphics = new Graphics();
|
|
994
|
+
*
|
|
995
|
+
* // Basic color stroke
|
|
996
|
+
* graphics
|
|
997
|
+
* .setStrokeStyle({
|
|
998
|
+
* width: 2,
|
|
999
|
+
* color: 0x000000
|
|
1000
|
+
* })
|
|
1001
|
+
* .rect(0, 0, 100, 100)
|
|
1002
|
+
* .stroke();
|
|
1003
|
+
*
|
|
1004
|
+
* // Complex stroke style
|
|
1005
|
+
* graphics
|
|
1006
|
+
* .setStrokeStyle({
|
|
1007
|
+
* width: 4,
|
|
1008
|
+
* color: 0xff0000,
|
|
1009
|
+
* alpha: 0.5,
|
|
1010
|
+
* join: 'round',
|
|
1011
|
+
* cap: 'round',
|
|
1012
|
+
* alignment: 0.5
|
|
1013
|
+
* })
|
|
1014
|
+
* .circle(100, 100, 50)
|
|
1015
|
+
* .stroke();
|
|
1016
|
+
*
|
|
1017
|
+
* // Gradient stroke
|
|
1018
|
+
* const gradient = new FillGradient({
|
|
1019
|
+
* end: { x: 1, y: 0 },
|
|
1020
|
+
* colorStops: [
|
|
1021
|
+
* { offset: 0, color: 0xff0000 }, // Red at start
|
|
1022
|
+
* { offset: 0.5, color: 0x00ff00 }, // Green at middle
|
|
1023
|
+
* { offset: 1, color: 0x0000ff }, // Blue at end
|
|
1024
|
+
* ],
|
|
1025
|
+
* });
|
|
1026
|
+
*
|
|
1027
|
+
* graphics
|
|
1028
|
+
* .setStrokeStyle({
|
|
1029
|
+
* width: 10,
|
|
1030
|
+
* fill: gradient
|
|
1031
|
+
* })
|
|
1032
|
+
* .poly([0,0, 100,50, 0,100])
|
|
1033
|
+
* .stroke();
|
|
1034
|
+
* ```
|
|
1035
|
+
* @param {StrokeInput} args - The stroke style to apply
|
|
1036
|
+
* @returns The Graphics instance for chaining
|
|
1037
|
+
* @see {@link StrokeStyle} For stroke style options
|
|
1038
|
+
* @see {@link FillGradient} For gradient strokes
|
|
1039
|
+
* @see {@link FillPattern} For pattern strokes
|
|
647
1040
|
*/
|
|
648
1041
|
setStrokeStyle(...args) {
|
|
649
1042
|
return this._callContextMethod("setStrokeStyle", args);
|
|
@@ -652,11 +1045,53 @@ class Graphics extends ViewContainer {
|
|
|
652
1045
|
return this._callContextMethod("fill", args);
|
|
653
1046
|
}
|
|
654
1047
|
/**
|
|
655
|
-
* Strokes the current path with the current stroke style
|
|
656
|
-
*
|
|
657
|
-
* @
|
|
658
|
-
*
|
|
659
|
-
*
|
|
1048
|
+
* Strokes the current path with the current stroke style or specified style.
|
|
1049
|
+
* Outlines the shape using the stroke settings.
|
|
1050
|
+
* @example
|
|
1051
|
+
* ```ts
|
|
1052
|
+
* const graphics = new Graphics();
|
|
1053
|
+
*
|
|
1054
|
+
* // Stroke with direct color
|
|
1055
|
+
* graphics
|
|
1056
|
+
* .circle(50, 50, 25)
|
|
1057
|
+
* .stroke({
|
|
1058
|
+
* width: 2,
|
|
1059
|
+
* color: 0xff0000
|
|
1060
|
+
* }); // 2px red stroke
|
|
1061
|
+
*
|
|
1062
|
+
* // Fill with texture
|
|
1063
|
+
* graphics
|
|
1064
|
+
* .rect(0, 0, 100, 100)
|
|
1065
|
+
* .stroke(myTexture); // Fill with texture
|
|
1066
|
+
*
|
|
1067
|
+
* // Stroke with gradient
|
|
1068
|
+
* const gradient = new FillGradient({
|
|
1069
|
+
* end: { x: 1, y: 0 },
|
|
1070
|
+
* colorStops: [
|
|
1071
|
+
* { offset: 0, color: 0xff0000 },
|
|
1072
|
+
* { offset: 0.5, color: 0x00ff00 },
|
|
1073
|
+
* { offset: 1, color: 0x0000ff },
|
|
1074
|
+
* ],
|
|
1075
|
+
* });
|
|
1076
|
+
*
|
|
1077
|
+
* graphics
|
|
1078
|
+
* .rect(0, 0, 100, 100)
|
|
1079
|
+
* .stroke({
|
|
1080
|
+
* width: 4,
|
|
1081
|
+
* fill: gradient,
|
|
1082
|
+
* alignment: 0.5,
|
|
1083
|
+
* join: 'round'
|
|
1084
|
+
* });
|
|
1085
|
+
* ```
|
|
1086
|
+
* @param {StrokeStyle} args - Optional stroke style to apply. Can be:
|
|
1087
|
+
* - A stroke style object with width, color, etc.
|
|
1088
|
+
* - A gradient
|
|
1089
|
+
* - A pattern
|
|
1090
|
+
* If omitted, uses current stroke style.
|
|
1091
|
+
* @returns The Graphics instance for chaining
|
|
1092
|
+
* @see {@link StrokeStyle} For stroke style options
|
|
1093
|
+
* @see {@link FillGradient} For gradient strokes
|
|
1094
|
+
* @see {@link setStrokeStyle} For setting default stroke style
|
|
660
1095
|
*/
|
|
661
1096
|
stroke(...args) {
|
|
662
1097
|
return this._callContextMethod("stroke", args);
|
|
@@ -667,15 +1102,39 @@ class Graphics extends ViewContainer {
|
|
|
667
1102
|
/**
|
|
668
1103
|
* Resets the current path. Any previous path and its commands are discarded and a new path is
|
|
669
1104
|
* started. This is typically called before beginning a new shape or series of drawing commands.
|
|
670
|
-
* @
|
|
1105
|
+
* @example
|
|
1106
|
+
* ```ts
|
|
1107
|
+
* const graphics = new Graphics();
|
|
1108
|
+
* graphics
|
|
1109
|
+
* .circle(150, 150, 50)
|
|
1110
|
+
* .fill({ color: 0x00ff00 })
|
|
1111
|
+
* .beginPath() // Starts a new path
|
|
1112
|
+
* .circle(250, 150, 50)
|
|
1113
|
+
* .fill({ color: 0x0000ff });
|
|
1114
|
+
* ```
|
|
1115
|
+
* @returns The Graphics instance for chaining
|
|
1116
|
+
* @see {@link Graphics#moveTo} For starting a new subpath
|
|
1117
|
+
* @see {@link Graphics#closePath} For closing the current path
|
|
671
1118
|
*/
|
|
672
1119
|
beginPath() {
|
|
673
1120
|
return this._callContextMethod("beginPath", []);
|
|
674
1121
|
}
|
|
675
1122
|
/**
|
|
676
1123
|
* Applies a cutout to the last drawn shape. This is used to create holes or complex shapes by
|
|
677
|
-
* subtracting a path from the previously drawn path.
|
|
678
|
-
*
|
|
1124
|
+
* subtracting a path from the previously drawn path.
|
|
1125
|
+
*
|
|
1126
|
+
* If a hole is not completely in a shape, it will fail to cut correctly.
|
|
1127
|
+
* @example
|
|
1128
|
+
* ```ts
|
|
1129
|
+
* const graphics = new Graphics();
|
|
1130
|
+
*
|
|
1131
|
+
* // Draw outer circle
|
|
1132
|
+
* graphics
|
|
1133
|
+
* .circle(100, 100, 50)
|
|
1134
|
+
* .fill({ color: 0xff0000 });
|
|
1135
|
+
* .circle(100, 100, 25) // Inner circle
|
|
1136
|
+
* .cut() // Cuts out the inner circle from the outer circle
|
|
1137
|
+
* ```
|
|
679
1138
|
*/
|
|
680
1139
|
cut() {
|
|
681
1140
|
return this._callContextMethod("cut", []);
|
|
@@ -693,9 +1152,23 @@ class Graphics extends ViewContainer {
|
|
|
693
1152
|
return this._callContextMethod("bezierCurveTo", args);
|
|
694
1153
|
}
|
|
695
1154
|
/**
|
|
696
|
-
* Closes the current path by drawing a straight line back to the start.
|
|
697
|
-
*
|
|
698
|
-
*
|
|
1155
|
+
* Closes the current path by drawing a straight line back to the start point.
|
|
1156
|
+
*
|
|
1157
|
+
* This is useful for completing shapes and ensuring they are properly closed for fills.
|
|
1158
|
+
* @example
|
|
1159
|
+
* ```ts
|
|
1160
|
+
* // Create a triangle with closed path
|
|
1161
|
+
* const graphics = new Graphics();
|
|
1162
|
+
* graphics
|
|
1163
|
+
* .moveTo(50, 50)
|
|
1164
|
+
* .lineTo(100, 100)
|
|
1165
|
+
* .lineTo(0, 100)
|
|
1166
|
+
* .closePath()
|
|
1167
|
+
* ```
|
|
1168
|
+
* @returns The Graphics instance for method chaining
|
|
1169
|
+
* @see {@link Graphics#beginPath} For starting a new path
|
|
1170
|
+
* @see {@link Graphics#fill} For filling closed paths
|
|
1171
|
+
* @see {@link Graphics#stroke} For stroking paths
|
|
699
1172
|
*/
|
|
700
1173
|
closePath() {
|
|
701
1174
|
return this._callContextMethod("closePath", []);
|
|
@@ -751,13 +1224,69 @@ class Graphics extends ViewContainer {
|
|
|
751
1224
|
restore(...args) {
|
|
752
1225
|
return this._callContextMethod("restore", args);
|
|
753
1226
|
}
|
|
754
|
-
/**
|
|
1227
|
+
/**
|
|
1228
|
+
* Saves the current graphics state onto a stack. The state includes:
|
|
1229
|
+
* - Current transformation matrix
|
|
1230
|
+
* - Current fill style
|
|
1231
|
+
* - Current stroke style
|
|
1232
|
+
* @example
|
|
1233
|
+
* ```ts
|
|
1234
|
+
* const graphics = new Graphics();
|
|
1235
|
+
*
|
|
1236
|
+
* // Save state before complex operations
|
|
1237
|
+
* graphics.save();
|
|
1238
|
+
*
|
|
1239
|
+
* // Create transformed and styled shape
|
|
1240
|
+
* graphics
|
|
1241
|
+
* .translateTransform(100, 100)
|
|
1242
|
+
* .rotateTransform(Math.PI / 4)
|
|
1243
|
+
* .setFillStyle({
|
|
1244
|
+
* color: 0xff0000,
|
|
1245
|
+
* alpha: 0.5
|
|
1246
|
+
* })
|
|
1247
|
+
* .rect(-25, -25, 50, 50)
|
|
1248
|
+
* .fill();
|
|
1249
|
+
*
|
|
1250
|
+
* // Restore to original state
|
|
1251
|
+
* graphics.restore();
|
|
1252
|
+
*
|
|
1253
|
+
* // Continue drawing with previous state
|
|
1254
|
+
* graphics
|
|
1255
|
+
* .circle(50, 50, 25)
|
|
1256
|
+
* .fill();
|
|
1257
|
+
* ```
|
|
1258
|
+
* @returns The Graphics instance for method chaining
|
|
1259
|
+
* @see {@link Graphics#restore} For restoring the saved state
|
|
1260
|
+
* @see {@link Graphics#setTransform} For setting transformations
|
|
1261
|
+
*/
|
|
755
1262
|
save() {
|
|
756
1263
|
return this._callContextMethod("save", []);
|
|
757
1264
|
}
|
|
758
1265
|
/**
|
|
759
1266
|
* Returns the current transformation matrix of the graphics context.
|
|
1267
|
+
* This matrix represents all accumulated transformations including translate, scale, and rotate.
|
|
1268
|
+
* @example
|
|
1269
|
+
* ```ts
|
|
1270
|
+
* const graphics = new Graphics();
|
|
1271
|
+
*
|
|
1272
|
+
* // Apply some transformations
|
|
1273
|
+
* graphics
|
|
1274
|
+
* .translateTransform(100, 100)
|
|
1275
|
+
* .rotateTransform(Math.PI / 4);
|
|
1276
|
+
*
|
|
1277
|
+
* // Get the current transform matrix
|
|
1278
|
+
* const matrix = graphics.getTransform();
|
|
1279
|
+
* console.log(matrix.tx, matrix.ty); // 100, 100
|
|
1280
|
+
*
|
|
1281
|
+
* // Use the matrix for other operations
|
|
1282
|
+
* graphics
|
|
1283
|
+
* .setTransform(matrix)
|
|
1284
|
+
* .circle(0, 0, 50)
|
|
1285
|
+
* .fill({ color: 0xff0000 });
|
|
1286
|
+
* ```
|
|
760
1287
|
* @returns The current transformation matrix.
|
|
1288
|
+
* @see {@link Graphics#setTransform} For setting the transform matrix
|
|
1289
|
+
* @see {@link Matrix} For matrix operations
|
|
761
1290
|
*/
|
|
762
1291
|
getTransform() {
|
|
763
1292
|
return this.context.getTransform();
|
|
@@ -765,7 +1294,27 @@ class Graphics extends ViewContainer {
|
|
|
765
1294
|
/**
|
|
766
1295
|
* Resets the current transformation matrix to the identity matrix, effectively removing
|
|
767
1296
|
* any transformations (rotation, scaling, translation) previously applied.
|
|
768
|
-
* @
|
|
1297
|
+
* @example
|
|
1298
|
+
* ```ts
|
|
1299
|
+
* const graphics = new Graphics();
|
|
1300
|
+
*
|
|
1301
|
+
* // Apply transformations
|
|
1302
|
+
* graphics
|
|
1303
|
+
* .translateTransform(100, 100)
|
|
1304
|
+
* .scaleTransform(2, 2)
|
|
1305
|
+
* .circle(0, 0, 25)
|
|
1306
|
+
* .fill({ color: 0xff0000 });
|
|
1307
|
+
* // Reset transform to default state
|
|
1308
|
+
* graphics
|
|
1309
|
+
* .resetTransform()
|
|
1310
|
+
* .circle(50, 50, 25) // Will draw at actual coordinates
|
|
1311
|
+
* .fill({ color: 0x00ff00 });
|
|
1312
|
+
* ```
|
|
1313
|
+
* @returns The Graphics instance for method chaining
|
|
1314
|
+
* @see {@link Graphics#getTransform} For getting the current transform
|
|
1315
|
+
* @see {@link Graphics#setTransform} For setting a specific transform
|
|
1316
|
+
* @see {@link Graphics#save} For saving the current transform state
|
|
1317
|
+
* @see {@link Graphics#restore} For restoring a previous transform state
|
|
769
1318
|
*/
|
|
770
1319
|
resetTransform() {
|
|
771
1320
|
return this._callContextMethod("resetTransform", []);
|
|
@@ -786,16 +1335,79 @@ class Graphics extends ViewContainer {
|
|
|
786
1335
|
return this._callContextMethod("translate", args);
|
|
787
1336
|
}
|
|
788
1337
|
/**
|
|
789
|
-
* Clears all drawing commands from the graphics context, effectively resetting it.
|
|
790
|
-
*
|
|
791
|
-
*
|
|
1338
|
+
* Clears all drawing commands from the graphics context, effectively resetting it.
|
|
1339
|
+
* This includes clearing the current path, fill style, stroke style, and transformations.
|
|
1340
|
+
*
|
|
1341
|
+
* > [!NOTE] Graphics objects are not designed to be continuously cleared and redrawn.
|
|
1342
|
+
* > Instead, they are intended to be used for static or semi-static graphics that
|
|
1343
|
+
* > can be redrawn as needed. Frequent clearing and redrawing may lead to performance issues.
|
|
1344
|
+
* @example
|
|
1345
|
+
* ```ts
|
|
1346
|
+
* const graphics = new Graphics();
|
|
1347
|
+
*
|
|
1348
|
+
* // Draw some shapes
|
|
1349
|
+
* graphics
|
|
1350
|
+
* .circle(100, 100, 50)
|
|
1351
|
+
* .fill({ color: 0xff0000 })
|
|
1352
|
+
* .rect(200, 100, 100, 50)
|
|
1353
|
+
* .fill({ color: 0x00ff00 });
|
|
1354
|
+
*
|
|
1355
|
+
* // Clear all graphics
|
|
1356
|
+
* graphics.clear();
|
|
1357
|
+
*
|
|
1358
|
+
* // Start fresh with new shapes
|
|
1359
|
+
* graphics
|
|
1360
|
+
* .circle(150, 150, 30)
|
|
1361
|
+
* .fill({ color: 0x0000ff });
|
|
1362
|
+
* ```
|
|
1363
|
+
* @returns The Graphics instance for method chaining
|
|
1364
|
+
* @see {@link Graphics#beginPath} For starting a new path without clearing styles
|
|
1365
|
+
* @see {@link Graphics#save} For saving the current state
|
|
1366
|
+
* @see {@link Graphics#restore} For restoring a previous state
|
|
792
1367
|
*/
|
|
793
1368
|
clear() {
|
|
794
1369
|
return this._callContextMethod("clear", []);
|
|
795
1370
|
}
|
|
796
1371
|
/**
|
|
797
|
-
*
|
|
1372
|
+
* Gets or sets the current fill style for the graphics context. The fill style determines
|
|
1373
|
+
* how shapes are filled when using the fill() method.
|
|
1374
|
+
* @example
|
|
1375
|
+
* ```ts
|
|
1376
|
+
* const graphics = new Graphics();
|
|
1377
|
+
*
|
|
1378
|
+
* // Basic color fill
|
|
1379
|
+
* graphics.fillStyle = {
|
|
1380
|
+
* color: 0xff0000, // Red
|
|
1381
|
+
* alpha: 1
|
|
1382
|
+
* };
|
|
1383
|
+
*
|
|
1384
|
+
* // Using gradients
|
|
1385
|
+
* const gradient = new FillGradient({
|
|
1386
|
+
* end: { x: 0, y: 1 }, // Vertical gradient
|
|
1387
|
+
* stops: [
|
|
1388
|
+
* { offset: 0, color: 0xff0000, alpha: 1 }, // Start color
|
|
1389
|
+
* { offset: 1, color: 0x0000ff, alpha: 1 } // End color
|
|
1390
|
+
* ]
|
|
1391
|
+
* });
|
|
1392
|
+
*
|
|
1393
|
+
* graphics.fillStyle = {
|
|
1394
|
+
* fill: gradient,
|
|
1395
|
+
* alpha: 0.8
|
|
1396
|
+
* };
|
|
1397
|
+
*
|
|
1398
|
+
* // Using patterns
|
|
1399
|
+
* graphics.fillStyle = {
|
|
1400
|
+
* texture: myTexture,
|
|
1401
|
+
* alpha: 1,
|
|
1402
|
+
* matrix: new Matrix()
|
|
1403
|
+
* .scale(0.5, 0.5)
|
|
1404
|
+
* .rotate(Math.PI / 4)
|
|
1405
|
+
* };
|
|
1406
|
+
* ```
|
|
798
1407
|
* @type {ConvertedFillStyle}
|
|
1408
|
+
* @see {@link FillStyle} For all available fill style options
|
|
1409
|
+
* @see {@link FillGradient} For creating gradient fills
|
|
1410
|
+
* @see {@link Graphics#fill} For applying the fill to paths
|
|
799
1411
|
*/
|
|
800
1412
|
get fillStyle() {
|
|
801
1413
|
return this._context.fillStyle;
|
|
@@ -804,8 +1416,47 @@ class Graphics extends ViewContainer {
|
|
|
804
1416
|
this._context.fillStyle = value;
|
|
805
1417
|
}
|
|
806
1418
|
/**
|
|
807
|
-
*
|
|
808
|
-
*
|
|
1419
|
+
* Gets or sets the current stroke style for the graphics context. The stroke style determines
|
|
1420
|
+
* how paths are outlined when using the stroke() method.
|
|
1421
|
+
* @example
|
|
1422
|
+
* ```ts
|
|
1423
|
+
* const graphics = new Graphics();
|
|
1424
|
+
*
|
|
1425
|
+
* // Basic stroke style
|
|
1426
|
+
* graphics.strokeStyle = {
|
|
1427
|
+
* width: 2,
|
|
1428
|
+
* color: 0xff0000,
|
|
1429
|
+
* alpha: 1
|
|
1430
|
+
* };
|
|
1431
|
+
*
|
|
1432
|
+
* // Using with gradients
|
|
1433
|
+
* const gradient = new FillGradient({
|
|
1434
|
+
* end: { x: 0, y: 1 },
|
|
1435
|
+
* stops: [
|
|
1436
|
+
* { offset: 0, color: 0xff0000, alpha: 1 },
|
|
1437
|
+
* { offset: 1, color: 0x0000ff, alpha: 1 }
|
|
1438
|
+
* ]
|
|
1439
|
+
* });
|
|
1440
|
+
*
|
|
1441
|
+
* graphics.strokeStyle = {
|
|
1442
|
+
* width: 4,
|
|
1443
|
+
* fill: gradient,
|
|
1444
|
+
* alignment: 0.5,
|
|
1445
|
+
* join: 'round',
|
|
1446
|
+
* cap: 'round'
|
|
1447
|
+
* };
|
|
1448
|
+
*
|
|
1449
|
+
* // Complex stroke settings
|
|
1450
|
+
* graphics.strokeStyle = {
|
|
1451
|
+
* width: 6,
|
|
1452
|
+
* color: 0x00ff00,
|
|
1453
|
+
* alpha: 0.5,
|
|
1454
|
+
* join: 'miter',
|
|
1455
|
+
* miterLimit: 10,
|
|
1456
|
+
* };
|
|
1457
|
+
* ```
|
|
1458
|
+
* @see {@link StrokeStyle} For all available stroke style options
|
|
1459
|
+
* @see {@link Graphics#stroke} For applying the stroke to paths
|
|
809
1460
|
*/
|
|
810
1461
|
get strokeStyle() {
|
|
811
1462
|
return this._context.strokeStyle;
|
|
@@ -814,12 +1465,41 @@ class Graphics extends ViewContainer {
|
|
|
814
1465
|
this._context.strokeStyle = value;
|
|
815
1466
|
}
|
|
816
1467
|
/**
|
|
817
|
-
* Creates a new Graphics object.
|
|
818
|
-
*
|
|
819
|
-
*
|
|
820
|
-
*
|
|
821
|
-
*
|
|
822
|
-
*
|
|
1468
|
+
* Creates a new Graphics object that copies the current graphics content.
|
|
1469
|
+
* The clone can either share the same context (shallow clone) or have its own independent
|
|
1470
|
+
* context (deep clone).
|
|
1471
|
+
* @example
|
|
1472
|
+
* ```ts
|
|
1473
|
+
* const graphics = new Graphics();
|
|
1474
|
+
*
|
|
1475
|
+
* // Create original graphics content
|
|
1476
|
+
* graphics
|
|
1477
|
+
* .circle(100, 100, 50)
|
|
1478
|
+
* .fill({ color: 0xff0000 });
|
|
1479
|
+
*
|
|
1480
|
+
* // Create a shallow clone (shared context)
|
|
1481
|
+
* const shallowClone = graphics.clone();
|
|
1482
|
+
*
|
|
1483
|
+
* // Changes to original affect the clone
|
|
1484
|
+
* graphics
|
|
1485
|
+
* .circle(200, 100, 30)
|
|
1486
|
+
* .fill({ color: 0x00ff00 });
|
|
1487
|
+
*
|
|
1488
|
+
* // Create a deep clone (independent context)
|
|
1489
|
+
* const deepClone = graphics.clone(true);
|
|
1490
|
+
*
|
|
1491
|
+
* // Modify deep clone independently
|
|
1492
|
+
* deepClone
|
|
1493
|
+
* .translateTransform(100, 100)
|
|
1494
|
+
* .circle(0, 0, 40)
|
|
1495
|
+
* .fill({ color: 0x0000ff });
|
|
1496
|
+
* ```
|
|
1497
|
+
* @param deep - Whether to create a deep clone of the graphics object.
|
|
1498
|
+
* If false (default), the context will be shared between objects.
|
|
1499
|
+
* If true, creates an independent copy of the context.
|
|
1500
|
+
* @returns A new Graphics instance with either shared or copied context
|
|
1501
|
+
* @see {@link Graphics#context} For accessing the underlying graphics context
|
|
1502
|
+
* @see {@link GraphicsContext} For understanding the shared context behavior
|
|
823
1503
|
*/
|
|
824
1504
|
clone(deep = false) {
|
|
825
1505
|
if (deep) {
|
|
@@ -943,85 +1623,359 @@ const _MeshGeometry = class _MeshGeometry extends Geometry {
|
|
|
943
1623
|
uvs = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
|
|
944
1624
|
}
|
|
945
1625
|
}
|
|
946
|
-
const indices = options.indices || new Uint32Array([0, 1, 2, 0, 2, 3]);
|
|
947
|
-
const shrinkToFit = options.shrinkBuffersToFit;
|
|
948
|
-
const positionBuffer = new Buffer({
|
|
949
|
-
data: positions,
|
|
950
|
-
label: "attribute-mesh-positions",
|
|
951
|
-
shrinkToFit,
|
|
952
|
-
usage: BufferUsage.VERTEX | BufferUsage.COPY_DST
|
|
953
|
-
});
|
|
954
|
-
const uvBuffer = new Buffer({
|
|
955
|
-
data: uvs,
|
|
956
|
-
label: "attribute-mesh-uvs",
|
|
957
|
-
shrinkToFit,
|
|
958
|
-
usage: BufferUsage.VERTEX | BufferUsage.COPY_DST
|
|
959
|
-
});
|
|
960
|
-
const indexBuffer = new Buffer({
|
|
961
|
-
data: indices,
|
|
962
|
-
label: "index-mesh-buffer",
|
|
963
|
-
shrinkToFit,
|
|
964
|
-
usage: BufferUsage.INDEX | BufferUsage.COPY_DST
|
|
965
|
-
});
|
|
966
|
-
super({
|
|
967
|
-
attributes: {
|
|
968
|
-
aPosition: {
|
|
969
|
-
buffer: positionBuffer,
|
|
970
|
-
format: "float32x2",
|
|
971
|
-
stride: 2 * 4,
|
|
972
|
-
offset: 0
|
|
973
|
-
},
|
|
974
|
-
aUV: {
|
|
975
|
-
buffer: uvBuffer,
|
|
976
|
-
format: "float32x2",
|
|
977
|
-
stride: 2 * 4,
|
|
978
|
-
offset: 0
|
|
979
|
-
}
|
|
980
|
-
},
|
|
981
|
-
indexBuffer,
|
|
982
|
-
topology: options.topology
|
|
983
|
-
});
|
|
984
|
-
this.batchMode = "auto";
|
|
985
|
-
}
|
|
986
|
-
/** The positions of the mesh. */
|
|
987
|
-
get positions() {
|
|
988
|
-
return this.attributes.aPosition.buffer.data;
|
|
989
|
-
}
|
|
990
|
-
/**
|
|
991
|
-
* Set the positions of the mesh.
|
|
992
|
-
* When setting the positions, its important that the uvs array is at least as long as the positions array.
|
|
993
|
-
* otherwise the geometry will not be valid.
|
|
994
|
-
* @param {Float32Array} value - The positions of the mesh.
|
|
995
|
-
*/
|
|
996
|
-
set positions(value) {
|
|
997
|
-
this.attributes.aPosition.buffer.data = value;
|
|
998
|
-
}
|
|
999
|
-
/** The UVs of the mesh. */
|
|
1000
|
-
get uvs() {
|
|
1001
|
-
return this.attributes.aUV.buffer.data;
|
|
1626
|
+
const indices = options.indices || new Uint32Array([0, 1, 2, 0, 2, 3]);
|
|
1627
|
+
const shrinkToFit = options.shrinkBuffersToFit;
|
|
1628
|
+
const positionBuffer = new Buffer({
|
|
1629
|
+
data: positions,
|
|
1630
|
+
label: "attribute-mesh-positions",
|
|
1631
|
+
shrinkToFit,
|
|
1632
|
+
usage: BufferUsage.VERTEX | BufferUsage.COPY_DST
|
|
1633
|
+
});
|
|
1634
|
+
const uvBuffer = new Buffer({
|
|
1635
|
+
data: uvs,
|
|
1636
|
+
label: "attribute-mesh-uvs",
|
|
1637
|
+
shrinkToFit,
|
|
1638
|
+
usage: BufferUsage.VERTEX | BufferUsage.COPY_DST
|
|
1639
|
+
});
|
|
1640
|
+
const indexBuffer = new Buffer({
|
|
1641
|
+
data: indices,
|
|
1642
|
+
label: "index-mesh-buffer",
|
|
1643
|
+
shrinkToFit,
|
|
1644
|
+
usage: BufferUsage.INDEX | BufferUsage.COPY_DST
|
|
1645
|
+
});
|
|
1646
|
+
super({
|
|
1647
|
+
attributes: {
|
|
1648
|
+
aPosition: {
|
|
1649
|
+
buffer: positionBuffer,
|
|
1650
|
+
format: "float32x2",
|
|
1651
|
+
stride: 2 * 4,
|
|
1652
|
+
offset: 0
|
|
1653
|
+
},
|
|
1654
|
+
aUV: {
|
|
1655
|
+
buffer: uvBuffer,
|
|
1656
|
+
format: "float32x2",
|
|
1657
|
+
stride: 2 * 4,
|
|
1658
|
+
offset: 0
|
|
1659
|
+
}
|
|
1660
|
+
},
|
|
1661
|
+
indexBuffer,
|
|
1662
|
+
topology: options.topology
|
|
1663
|
+
});
|
|
1664
|
+
this.batchMode = "auto";
|
|
1665
|
+
}
|
|
1666
|
+
/** The positions of the mesh. */
|
|
1667
|
+
get positions() {
|
|
1668
|
+
return this.attributes.aPosition.buffer.data;
|
|
1669
|
+
}
|
|
1670
|
+
/**
|
|
1671
|
+
* Set the positions of the mesh.
|
|
1672
|
+
* When setting the positions, its important that the uvs array is at least as long as the positions array.
|
|
1673
|
+
* otherwise the geometry will not be valid.
|
|
1674
|
+
* @param {Float32Array} value - The positions of the mesh.
|
|
1675
|
+
*/
|
|
1676
|
+
set positions(value) {
|
|
1677
|
+
this.attributes.aPosition.buffer.data = value;
|
|
1678
|
+
}
|
|
1679
|
+
/** The UVs of the mesh. */
|
|
1680
|
+
get uvs() {
|
|
1681
|
+
return this.attributes.aUV.buffer.data;
|
|
1682
|
+
}
|
|
1683
|
+
/**
|
|
1684
|
+
* Set the UVs of the mesh.
|
|
1685
|
+
* Its important that the uvs array you set is at least as long as the positions array.
|
|
1686
|
+
* otherwise the geometry will not be valid.
|
|
1687
|
+
* @param {Float32Array} value - The UVs of the mesh.
|
|
1688
|
+
*/
|
|
1689
|
+
set uvs(value) {
|
|
1690
|
+
this.attributes.aUV.buffer.data = value;
|
|
1691
|
+
}
|
|
1692
|
+
/** The indices of the mesh. */
|
|
1693
|
+
get indices() {
|
|
1694
|
+
return this.indexBuffer.data;
|
|
1695
|
+
}
|
|
1696
|
+
set indices(value) {
|
|
1697
|
+
this.indexBuffer.data = value;
|
|
1698
|
+
}
|
|
1699
|
+
};
|
|
1700
|
+
_MeshGeometry.defaultOptions = {
|
|
1701
|
+
topology: "triangle-list",
|
|
1702
|
+
shrinkBuffersToFit: false
|
|
1703
|
+
};
|
|
1704
|
+
let MeshGeometry = _MeshGeometry;
|
|
1705
|
+
|
|
1706
|
+
let _internalCanvas = null;
|
|
1707
|
+
let _internalContext = null;
|
|
1708
|
+
function ensureInternalCanvas(width, height) {
|
|
1709
|
+
if (!_internalCanvas) {
|
|
1710
|
+
_internalCanvas = DOMAdapter.get().createCanvas(256, 128);
|
|
1711
|
+
_internalContext = _internalCanvas.getContext("2d", { willReadFrequently: true });
|
|
1712
|
+
_internalContext.globalCompositeOperation = "copy";
|
|
1713
|
+
_internalContext.globalAlpha = 1;
|
|
1714
|
+
}
|
|
1715
|
+
if (_internalCanvas.width < width || _internalCanvas.height < height) {
|
|
1716
|
+
_internalCanvas.width = nextPow2(width);
|
|
1717
|
+
_internalCanvas.height = nextPow2(height);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
function checkRow(data, width, y) {
|
|
1721
|
+
for (let x = 0, index = 4 * y * width; x < width; ++x, index += 4) {
|
|
1722
|
+
if (data[index + 3] !== 0)
|
|
1723
|
+
return false;
|
|
1724
|
+
}
|
|
1725
|
+
return true;
|
|
1726
|
+
}
|
|
1727
|
+
function checkColumn(data, width, x, top, bottom) {
|
|
1728
|
+
const stride = 4 * width;
|
|
1729
|
+
for (let y = top, index = top * stride + 4 * x; y <= bottom; ++y, index += stride) {
|
|
1730
|
+
if (data[index + 3] !== 0)
|
|
1731
|
+
return false;
|
|
1732
|
+
}
|
|
1733
|
+
return true;
|
|
1734
|
+
}
|
|
1735
|
+
function getCanvasBoundingBox(...args) {
|
|
1736
|
+
let options = args[0];
|
|
1737
|
+
if (!options.canvas) {
|
|
1738
|
+
options = { canvas: args[0], resolution: args[1] };
|
|
1739
|
+
}
|
|
1740
|
+
const { canvas } = options;
|
|
1741
|
+
const resolution = Math.min(options.resolution ?? 1, 1);
|
|
1742
|
+
const width = options.width ?? canvas.width;
|
|
1743
|
+
const height = options.height ?? canvas.height;
|
|
1744
|
+
let output = options.output;
|
|
1745
|
+
ensureInternalCanvas(width, height);
|
|
1746
|
+
if (!_internalContext) {
|
|
1747
|
+
throw new TypeError("Failed to get canvas 2D context");
|
|
1748
|
+
}
|
|
1749
|
+
_internalContext.drawImage(
|
|
1750
|
+
canvas,
|
|
1751
|
+
0,
|
|
1752
|
+
0,
|
|
1753
|
+
width,
|
|
1754
|
+
height,
|
|
1755
|
+
0,
|
|
1756
|
+
0,
|
|
1757
|
+
width * resolution,
|
|
1758
|
+
height * resolution
|
|
1759
|
+
);
|
|
1760
|
+
const imageData = _internalContext.getImageData(0, 0, width, height);
|
|
1761
|
+
const data = imageData.data;
|
|
1762
|
+
let left = 0;
|
|
1763
|
+
let top = 0;
|
|
1764
|
+
let right = width - 1;
|
|
1765
|
+
let bottom = height - 1;
|
|
1766
|
+
while (top < height && checkRow(data, width, top))
|
|
1767
|
+
++top;
|
|
1768
|
+
if (top === height)
|
|
1769
|
+
return Rectangle.EMPTY;
|
|
1770
|
+
while (checkRow(data, width, bottom))
|
|
1771
|
+
--bottom;
|
|
1772
|
+
while (checkColumn(data, width, left, top, bottom))
|
|
1773
|
+
++left;
|
|
1774
|
+
while (checkColumn(data, width, right, top, bottom))
|
|
1775
|
+
--right;
|
|
1776
|
+
++right;
|
|
1777
|
+
++bottom;
|
|
1778
|
+
_internalContext.globalCompositeOperation = "source-over";
|
|
1779
|
+
_internalContext.strokeRect(left, top, right - left, bottom - top);
|
|
1780
|
+
_internalContext.globalCompositeOperation = "copy";
|
|
1781
|
+
output ?? (output = new Rectangle());
|
|
1782
|
+
output.set(left / resolution, top / resolution, (right - left) / resolution, (bottom - top) / resolution);
|
|
1783
|
+
return output;
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
const tempRect = new Rectangle();
|
|
1787
|
+
class CanvasTextGeneratorClass {
|
|
1788
|
+
/**
|
|
1789
|
+
* Creates a canvas with the specified text rendered to it.
|
|
1790
|
+
*
|
|
1791
|
+
* Generates a canvas of appropriate size, renders the text with the provided style,
|
|
1792
|
+
* and returns both the canvas/context and a Rectangle representing the text bounds.
|
|
1793
|
+
*
|
|
1794
|
+
* When trim is enabled in the style, the frame will represent the bounds of the
|
|
1795
|
+
* non-transparent pixels, which can be smaller than the full canvas.
|
|
1796
|
+
* @param options - The options for generating the text canvas
|
|
1797
|
+
* @param options.text - The text to render
|
|
1798
|
+
* @param options.style - The style to apply to the text
|
|
1799
|
+
* @param options.resolution - The resolution of the canvas (defaults to 1)
|
|
1800
|
+
* @param options.padding
|
|
1801
|
+
* @returns An object containing the canvas/context and the frame (bounds) of the text
|
|
1802
|
+
*/
|
|
1803
|
+
getCanvasAndContext(options) {
|
|
1804
|
+
const { text, style, resolution = 1 } = options;
|
|
1805
|
+
const padding = style._getFinalPadding();
|
|
1806
|
+
const measured = CanvasTextMetrics.measureText(text || " ", style);
|
|
1807
|
+
const width = Math.ceil(Math.ceil(Math.max(1, measured.width) + padding * 2) * resolution);
|
|
1808
|
+
const height = Math.ceil(Math.ceil(Math.max(1, measured.height) + padding * 2) * resolution);
|
|
1809
|
+
const canvasAndContext = CanvasPool.getOptimalCanvasAndContext(width, height);
|
|
1810
|
+
this._renderTextToCanvas(text, style, padding, resolution, canvasAndContext);
|
|
1811
|
+
const frame = style.trim ? getCanvasBoundingBox({ canvas: canvasAndContext.canvas, width, height, resolution: 1, output: tempRect }) : tempRect.set(0, 0, width, height);
|
|
1812
|
+
return {
|
|
1813
|
+
canvasAndContext,
|
|
1814
|
+
frame
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1817
|
+
/**
|
|
1818
|
+
* Returns a canvas and context to the pool.
|
|
1819
|
+
*
|
|
1820
|
+
* This should be called when you're done with the canvas to allow reuse
|
|
1821
|
+
* and prevent memory leaks.
|
|
1822
|
+
* @param canvasAndContext - The canvas and context to return to the pool
|
|
1823
|
+
*/
|
|
1824
|
+
returnCanvasAndContext(canvasAndContext) {
|
|
1825
|
+
CanvasPool.returnCanvasAndContext(canvasAndContext);
|
|
1826
|
+
}
|
|
1827
|
+
/**
|
|
1828
|
+
* Renders text to its canvas, and updates its texture.
|
|
1829
|
+
* @param text - The text to render
|
|
1830
|
+
* @param style - The style of the text
|
|
1831
|
+
* @param padding - The padding of the text
|
|
1832
|
+
* @param resolution - The resolution of the text
|
|
1833
|
+
* @param canvasAndContext - The canvas and context to render the text to
|
|
1834
|
+
*/
|
|
1835
|
+
_renderTextToCanvas(text, style, padding, resolution, canvasAndContext) {
|
|
1836
|
+
const { canvas, context } = canvasAndContext;
|
|
1837
|
+
const font = fontStringFromTextStyle(style);
|
|
1838
|
+
const measured = CanvasTextMetrics.measureText(text || " ", style);
|
|
1839
|
+
const lines = measured.lines;
|
|
1840
|
+
const lineHeight = measured.lineHeight;
|
|
1841
|
+
const lineWidths = measured.lineWidths;
|
|
1842
|
+
const maxLineWidth = measured.maxLineWidth;
|
|
1843
|
+
const fontProperties = measured.fontProperties;
|
|
1844
|
+
const height = canvas.height;
|
|
1845
|
+
context.resetTransform();
|
|
1846
|
+
context.scale(resolution, resolution);
|
|
1847
|
+
context.textBaseline = style.textBaseline;
|
|
1848
|
+
if (style._stroke?.width) {
|
|
1849
|
+
const strokeStyle = style._stroke;
|
|
1850
|
+
context.lineWidth = strokeStyle.width;
|
|
1851
|
+
context.miterLimit = strokeStyle.miterLimit;
|
|
1852
|
+
context.lineJoin = strokeStyle.join;
|
|
1853
|
+
context.lineCap = strokeStyle.cap;
|
|
1854
|
+
}
|
|
1855
|
+
context.font = font;
|
|
1856
|
+
let linePositionX;
|
|
1857
|
+
let linePositionY;
|
|
1858
|
+
const passesCount = style.dropShadow ? 2 : 1;
|
|
1859
|
+
for (let i = 0; i < passesCount; ++i) {
|
|
1860
|
+
const isShadowPass = style.dropShadow && i === 0;
|
|
1861
|
+
const dsOffsetText = isShadowPass ? Math.ceil(Math.max(1, height) + padding * 2) : 0;
|
|
1862
|
+
const dsOffsetShadow = dsOffsetText * resolution;
|
|
1863
|
+
if (isShadowPass) {
|
|
1864
|
+
context.fillStyle = "black";
|
|
1865
|
+
context.strokeStyle = "black";
|
|
1866
|
+
const shadowOptions = style.dropShadow;
|
|
1867
|
+
const dropShadowColor = shadowOptions.color;
|
|
1868
|
+
const dropShadowAlpha = shadowOptions.alpha;
|
|
1869
|
+
context.shadowColor = Color.shared.setValue(dropShadowColor).setAlpha(dropShadowAlpha).toRgbaString();
|
|
1870
|
+
const dropShadowBlur = shadowOptions.blur * resolution;
|
|
1871
|
+
const dropShadowDistance = shadowOptions.distance * resolution;
|
|
1872
|
+
context.shadowBlur = dropShadowBlur;
|
|
1873
|
+
context.shadowOffsetX = Math.cos(shadowOptions.angle) * dropShadowDistance;
|
|
1874
|
+
context.shadowOffsetY = Math.sin(shadowOptions.angle) * dropShadowDistance + dsOffsetShadow;
|
|
1875
|
+
} else {
|
|
1876
|
+
context.fillStyle = style._fill ? getCanvasFillStyle(style._fill, context, measured) : null;
|
|
1877
|
+
if (style._stroke?.width) {
|
|
1878
|
+
const padding2 = style._stroke.width * style._stroke.alignment;
|
|
1879
|
+
context.strokeStyle = getCanvasFillStyle(style._stroke, context, measured, padding2);
|
|
1880
|
+
}
|
|
1881
|
+
context.shadowColor = "black";
|
|
1882
|
+
}
|
|
1883
|
+
let linePositionYShift = (lineHeight - fontProperties.fontSize) / 2;
|
|
1884
|
+
if (lineHeight - fontProperties.fontSize < 0) {
|
|
1885
|
+
linePositionYShift = 0;
|
|
1886
|
+
}
|
|
1887
|
+
const strokeWidth = style._stroke?.width ?? 0;
|
|
1888
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
1889
|
+
linePositionX = strokeWidth / 2;
|
|
1890
|
+
linePositionY = strokeWidth / 2 + i2 * lineHeight + fontProperties.ascent + linePositionYShift;
|
|
1891
|
+
if (style.align === "right") {
|
|
1892
|
+
linePositionX += maxLineWidth - lineWidths[i2];
|
|
1893
|
+
} else if (style.align === "center") {
|
|
1894
|
+
linePositionX += (maxLineWidth - lineWidths[i2]) / 2;
|
|
1895
|
+
}
|
|
1896
|
+
if (style._stroke?.width) {
|
|
1897
|
+
this._drawLetterSpacing(
|
|
1898
|
+
lines[i2],
|
|
1899
|
+
style,
|
|
1900
|
+
canvasAndContext,
|
|
1901
|
+
linePositionX + padding,
|
|
1902
|
+
linePositionY + padding - dsOffsetText,
|
|
1903
|
+
true
|
|
1904
|
+
);
|
|
1905
|
+
}
|
|
1906
|
+
if (style._fill !== void 0) {
|
|
1907
|
+
this._drawLetterSpacing(
|
|
1908
|
+
lines[i2],
|
|
1909
|
+
style,
|
|
1910
|
+
canvasAndContext,
|
|
1911
|
+
linePositionX + padding,
|
|
1912
|
+
linePositionY + padding - dsOffsetText
|
|
1913
|
+
);
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1002
1917
|
}
|
|
1003
1918
|
/**
|
|
1004
|
-
*
|
|
1005
|
-
*
|
|
1006
|
-
*
|
|
1007
|
-
*
|
|
1919
|
+
* Render the text with letter-spacing.
|
|
1920
|
+
*
|
|
1921
|
+
* This method handles rendering text with the correct letter spacing, using either:
|
|
1922
|
+
* 1. Native letter spacing if supported by the browser
|
|
1923
|
+
* 2. Manual letter spacing calculation if not natively supported
|
|
1924
|
+
*
|
|
1925
|
+
* For manual letter spacing, it calculates the position of each character
|
|
1926
|
+
* based on its width and the desired spacing.
|
|
1927
|
+
* @param text - The text to draw
|
|
1928
|
+
* @param style - The text style to apply
|
|
1929
|
+
* @param canvasAndContext - The canvas and context to draw to
|
|
1930
|
+
* @param x - Horizontal position to draw the text
|
|
1931
|
+
* @param y - Vertical position to draw the text
|
|
1932
|
+
* @param isStroke - Whether to render the stroke (true) or fill (false)
|
|
1933
|
+
* @private
|
|
1008
1934
|
*/
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1935
|
+
_drawLetterSpacing(text, style, canvasAndContext, x, y, isStroke = false) {
|
|
1936
|
+
const { context } = canvasAndContext;
|
|
1937
|
+
const letterSpacing = style.letterSpacing;
|
|
1938
|
+
let useExperimentalLetterSpacing = false;
|
|
1939
|
+
if (CanvasTextMetrics.experimentalLetterSpacingSupported) {
|
|
1940
|
+
if (CanvasTextMetrics.experimentalLetterSpacing) {
|
|
1941
|
+
context.letterSpacing = `${letterSpacing}px`;
|
|
1942
|
+
context.textLetterSpacing = `${letterSpacing}px`;
|
|
1943
|
+
useExperimentalLetterSpacing = true;
|
|
1944
|
+
} else {
|
|
1945
|
+
context.letterSpacing = "0px";
|
|
1946
|
+
context.textLetterSpacing = "0px";
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
if (letterSpacing === 0 || useExperimentalLetterSpacing) {
|
|
1950
|
+
if (isStroke) {
|
|
1951
|
+
context.strokeText(text, x, y);
|
|
1952
|
+
} else {
|
|
1953
|
+
context.fillText(text, x, y);
|
|
1954
|
+
}
|
|
1955
|
+
return;
|
|
1956
|
+
}
|
|
1957
|
+
let currentPosition = x;
|
|
1958
|
+
const stringArray = CanvasTextMetrics.graphemeSegmenter(text);
|
|
1959
|
+
let previousWidth = context.measureText(text).width;
|
|
1960
|
+
let currentWidth = 0;
|
|
1961
|
+
for (let i = 0; i < stringArray.length; ++i) {
|
|
1962
|
+
const currentChar = stringArray[i];
|
|
1963
|
+
if (isStroke) {
|
|
1964
|
+
context.strokeText(currentChar, currentPosition, y);
|
|
1965
|
+
} else {
|
|
1966
|
+
context.fillText(currentChar, currentPosition, y);
|
|
1967
|
+
}
|
|
1968
|
+
let textStr = "";
|
|
1969
|
+
for (let j = i + 1; j < stringArray.length; ++j) {
|
|
1970
|
+
textStr += stringArray[j];
|
|
1971
|
+
}
|
|
1972
|
+
currentWidth = context.measureText(textStr).width;
|
|
1973
|
+
currentPosition += previousWidth - currentWidth + letterSpacing;
|
|
1974
|
+
previousWidth = currentWidth;
|
|
1975
|
+
}
|
|
1018
1976
|
}
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
topology: "triangle-list",
|
|
1022
|
-
shrinkBuffersToFit: false
|
|
1023
|
-
};
|
|
1024
|
-
let MeshGeometry = _MeshGeometry;
|
|
1977
|
+
}
|
|
1978
|
+
const CanvasTextGenerator = new CanvasTextGeneratorClass();
|
|
1025
1979
|
|
|
1026
1980
|
function textStyleToCSS(style) {
|
|
1027
1981
|
const stroke = style._stroke;
|
|
@@ -1107,28 +2061,85 @@ class HTMLTextStyle extends TextStyle {
|
|
|
1107
2061
|
constructor(options = {}) {
|
|
1108
2062
|
super(options);
|
|
1109
2063
|
this._cssOverrides = [];
|
|
1110
|
-
this.cssOverrides
|
|
2064
|
+
this.cssOverrides = options.cssOverrides ?? [];
|
|
1111
2065
|
this.tagStyles = options.tagStyles ?? {};
|
|
1112
2066
|
}
|
|
1113
|
-
/**
|
|
2067
|
+
/**
|
|
2068
|
+
* List of CSS style overrides to apply to the HTML text.
|
|
2069
|
+
* These styles are added after the built-in styles and can override any default styling.
|
|
2070
|
+
* @advanced
|
|
2071
|
+
*/
|
|
1114
2072
|
set cssOverrides(value) {
|
|
1115
2073
|
this._cssOverrides = value instanceof Array ? value : [value];
|
|
1116
2074
|
this.update();
|
|
1117
2075
|
}
|
|
2076
|
+
/** @advanced */
|
|
1118
2077
|
get cssOverrides() {
|
|
1119
2078
|
return this._cssOverrides;
|
|
1120
2079
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
2080
|
+
/**
|
|
2081
|
+
* Updates the text style and triggers a refresh of the CSS style cache.
|
|
2082
|
+
* This method is called automatically when style properties are changed.
|
|
2083
|
+
* @example
|
|
2084
|
+
* ```ts
|
|
2085
|
+
* // Update after multiple changes
|
|
2086
|
+
* const text = new HTMLText({
|
|
2087
|
+
* text: 'Hello World',
|
|
2088
|
+
* style
|
|
2089
|
+
* });
|
|
2090
|
+
*
|
|
2091
|
+
* style.fontSize = 32;
|
|
2092
|
+
* style.fill = '#00ff00';
|
|
2093
|
+
* style.fontFamily = 'Arial';
|
|
2094
|
+
* style.update(); // Apply all changes at once
|
|
2095
|
+
* ```
|
|
2096
|
+
* @advanced
|
|
2097
|
+
* @see {@link HTMLTextStyle#cssStyle} For accessing the generated CSS
|
|
2098
|
+
* @see {@link HTMLTextStyle#cssOverrides} For managing CSS overrides
|
|
2099
|
+
*/
|
|
1125
2100
|
update() {
|
|
1126
2101
|
this._cssStyle = null;
|
|
1127
2102
|
super.update();
|
|
1128
2103
|
}
|
|
1129
2104
|
/**
|
|
1130
2105
|
* Creates a new HTMLTextStyle object with the same values as this one.
|
|
1131
|
-
*
|
|
2106
|
+
* This creates a deep copy of all style properties, including dropShadow and tag styles.
|
|
2107
|
+
* @example
|
|
2108
|
+
* ```ts
|
|
2109
|
+
* // Create original style
|
|
2110
|
+
* const originalStyle = new HTMLTextStyle({
|
|
2111
|
+
* fontSize: 24,
|
|
2112
|
+
* fill: '#ff0000',
|
|
2113
|
+
* tagStyles: {
|
|
2114
|
+
* header: { fontSize: 32, fill: '#00ff00' }
|
|
2115
|
+
* }
|
|
2116
|
+
* });
|
|
2117
|
+
*
|
|
2118
|
+
* // Clone the style
|
|
2119
|
+
* const clonedStyle = originalStyle.clone();
|
|
2120
|
+
*
|
|
2121
|
+
* // Modify cloned style independently
|
|
2122
|
+
* clonedStyle.fontSize = 36;
|
|
2123
|
+
* clonedStyle.fill = '#0000ff';
|
|
2124
|
+
*
|
|
2125
|
+
* // Original style remains unchanged
|
|
2126
|
+
* console.log(originalStyle.fontSize); // Still 24
|
|
2127
|
+
* console.log(originalStyle.fill); // Still '#ff0000'
|
|
2128
|
+
* ```
|
|
2129
|
+
*
|
|
2130
|
+
* Properties that are cloned:
|
|
2131
|
+
* - Basic text properties (fontSize, fontFamily, etc.)
|
|
2132
|
+
* - Fill and stroke styles
|
|
2133
|
+
* - Drop shadow configuration
|
|
2134
|
+
* - CSS overrides
|
|
2135
|
+
* - Tag styles (deep copied)
|
|
2136
|
+
* - Word wrap settings
|
|
2137
|
+
* - Alignment and spacing
|
|
2138
|
+
* @returns {HTMLTextStyle} A new HTMLTextStyle instance with the same properties
|
|
2139
|
+
* @see {@link HTMLTextStyle} For available style properties
|
|
2140
|
+
* @see {@link HTMLTextStyle#cssOverrides} For CSS override handling
|
|
2141
|
+
* @see {@link HTMLTextStyle#tagStyles} For tag style configuration
|
|
2142
|
+
* @standard
|
|
1132
2143
|
*/
|
|
1133
2144
|
clone() {
|
|
1134
2145
|
return new HTMLTextStyle({
|
|
@@ -1148,9 +2159,14 @@ class HTMLTextStyle extends TextStyle {
|
|
|
1148
2159
|
whiteSpace: this.whiteSpace,
|
|
1149
2160
|
wordWrap: this.wordWrap,
|
|
1150
2161
|
wordWrapWidth: this.wordWrapWidth,
|
|
1151
|
-
cssOverrides: this.cssOverrides
|
|
2162
|
+
cssOverrides: this.cssOverrides,
|
|
2163
|
+
tagStyles: { ...this.tagStyles }
|
|
1152
2164
|
});
|
|
1153
2165
|
}
|
|
2166
|
+
/**
|
|
2167
|
+
* The CSS style string that will be applied to the HTML text.
|
|
2168
|
+
* @advanced
|
|
2169
|
+
*/
|
|
1154
2170
|
get cssStyle() {
|
|
1155
2171
|
if (!this._cssStyle) {
|
|
1156
2172
|
this._cssStyle = textStyleToCSS(this);
|
|
@@ -1165,6 +2181,7 @@ class HTMLTextStyle extends TextStyle {
|
|
|
1165
2181
|
* @param {string} value - CSS style(s) to add.
|
|
1166
2182
|
* @example
|
|
1167
2183
|
* style.addOverride('background-color: red');
|
|
2184
|
+
* @advanced
|
|
1168
2185
|
*/
|
|
1169
2186
|
addOverride(...value) {
|
|
1170
2187
|
const toAdd = value.filter((v) => !this.cssOverrides.includes(v));
|
|
@@ -1178,6 +2195,7 @@ class HTMLTextStyle extends TextStyle {
|
|
|
1178
2195
|
* @param {string} value - CSS style to remove.
|
|
1179
2196
|
* @example
|
|
1180
2197
|
* style.removeOverride('background-color: red');
|
|
2198
|
+
* @advanced
|
|
1181
2199
|
*/
|
|
1182
2200
|
removeOverride(...value) {
|
|
1183
2201
|
const toRemove = value.filter((v) => this.cssOverrides.includes(v));
|
|
@@ -1186,12 +2204,83 @@ class HTMLTextStyle extends TextStyle {
|
|
|
1186
2204
|
this.update();
|
|
1187
2205
|
}
|
|
1188
2206
|
}
|
|
2207
|
+
/**
|
|
2208
|
+
* Sets the fill style for the text. HTML text only supports color fills (string or number values).
|
|
2209
|
+
* Texture fills are not supported and will trigger a warning in debug mode.
|
|
2210
|
+
* @example
|
|
2211
|
+
* ```ts
|
|
2212
|
+
* // Using hex colors
|
|
2213
|
+
* const text = new HTMLText({
|
|
2214
|
+
* text: 'Colored Text',
|
|
2215
|
+
* style: {
|
|
2216
|
+
* fill: 0xff0000 // Red color
|
|
2217
|
+
* }
|
|
2218
|
+
* });
|
|
2219
|
+
*
|
|
2220
|
+
* // Using CSS color strings
|
|
2221
|
+
* text.style.fill = '#00ff00'; // Hex string (Green)
|
|
2222
|
+
* text.style.fill = 'blue'; // Named color
|
|
2223
|
+
* text.style.fill = 'rgb(255,0,0)' // RGB
|
|
2224
|
+
* text.style.fill = '#f0f'; // Short hex
|
|
2225
|
+
*
|
|
2226
|
+
* // Invalid usage (will trigger warning in debug)
|
|
2227
|
+
* text.style.fill = {
|
|
2228
|
+
* type: 'pattern',
|
|
2229
|
+
* texture: Texture.from('pattern.png')
|
|
2230
|
+
* }; // Not supported, falls back to default
|
|
2231
|
+
* ```
|
|
2232
|
+
* @param value - The fill color to use. Must be a string or number.
|
|
2233
|
+
* @throws {Warning} In debug mode when attempting to use unsupported fill types
|
|
2234
|
+
* @see {@link TextStyle#fill} For full fill options in canvas text
|
|
2235
|
+
* @standard
|
|
2236
|
+
*/
|
|
1189
2237
|
set fill(value) {
|
|
1190
2238
|
if (typeof value !== "string" && typeof value !== "number") {
|
|
1191
2239
|
warn("[HTMLTextStyle] only color fill is not supported by HTMLText");
|
|
1192
2240
|
}
|
|
1193
2241
|
super.fill = value;
|
|
1194
2242
|
}
|
|
2243
|
+
/**
|
|
2244
|
+
* Sets the stroke style for the text. HTML text only supports color strokes (string or number values).
|
|
2245
|
+
* Texture strokes are not supported and will trigger a warning in debug mode.
|
|
2246
|
+
* @example
|
|
2247
|
+
* ```ts
|
|
2248
|
+
* // Using hex colors
|
|
2249
|
+
* const text = new HTMLText({
|
|
2250
|
+
* text: 'Outlined Text',
|
|
2251
|
+
* style: {
|
|
2252
|
+
* stroke: 0xff0000 // Red outline
|
|
2253
|
+
* }
|
|
2254
|
+
* });
|
|
2255
|
+
*
|
|
2256
|
+
* // Using CSS color strings
|
|
2257
|
+
* text.style.stroke = '#00ff00'; // Hex string (Green)
|
|
2258
|
+
* text.style.stroke = 'blue'; // Named color
|
|
2259
|
+
* text.style.stroke = 'rgb(255,0,0)' // RGB
|
|
2260
|
+
* text.style.stroke = '#f0f'; // Short hex
|
|
2261
|
+
*
|
|
2262
|
+
* // Using stroke width
|
|
2263
|
+
* text.style = {
|
|
2264
|
+
* stroke: {
|
|
2265
|
+
* color: '#ff0000',
|
|
2266
|
+
* width: 2
|
|
2267
|
+
* }
|
|
2268
|
+
* };
|
|
2269
|
+
*
|
|
2270
|
+
* // Remove stroke
|
|
2271
|
+
* text.style.stroke = null;
|
|
2272
|
+
*
|
|
2273
|
+
* // Invalid usage (will trigger warning in debug)
|
|
2274
|
+
* text.style.stroke = {
|
|
2275
|
+
* type: 'pattern',
|
|
2276
|
+
* texture: Texture.from('pattern.png')
|
|
2277
|
+
* }; // Not supported, falls back to default
|
|
2278
|
+
* ```
|
|
2279
|
+
* @param value - The stroke style to use. Must be a string, number, or stroke configuration object
|
|
2280
|
+
* @throws {Warning} In debug mode when attempting to use unsupported stroke types
|
|
2281
|
+
* @see {@link TextStyle#stroke} For full stroke options in canvas text
|
|
2282
|
+
* @standard
|
|
2283
|
+
*/
|
|
1195
2284
|
set stroke(value) {
|
|
1196
2285
|
if (value && typeof value !== "string" && typeof value !== "number") {
|
|
1197
2286
|
warn("[HTMLTextStyle] only color stroke is not supported by HTMLText");
|
|
@@ -1238,20 +2327,31 @@ function measureHtmlText(text, style, fontStyleCSS, htmlTextRenderData) {
|
|
|
1238
2327
|
};
|
|
1239
2328
|
}
|
|
1240
2329
|
|
|
2330
|
+
class GraphicsGpuData {
|
|
2331
|
+
constructor() {
|
|
2332
|
+
this.batches = [];
|
|
2333
|
+
this.batched = false;
|
|
2334
|
+
}
|
|
2335
|
+
destroy() {
|
|
2336
|
+
this.batches.forEach((batch) => {
|
|
2337
|
+
BigPool.return(batch);
|
|
2338
|
+
});
|
|
2339
|
+
this.batches.length = 0;
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
1241
2342
|
class GraphicsPipe {
|
|
1242
2343
|
constructor(renderer, adaptor) {
|
|
1243
2344
|
this.state = State.for2d();
|
|
1244
|
-
// batchable graphics list, used to render batches
|
|
1245
|
-
this._graphicsBatchesHash = /* @__PURE__ */ Object.create(null);
|
|
1246
|
-
this._destroyRenderableBound = this.destroyRenderable.bind(this);
|
|
1247
2345
|
this.renderer = renderer;
|
|
1248
2346
|
this._adaptor = adaptor;
|
|
1249
|
-
this.
|
|
1250
|
-
|
|
2347
|
+
this.renderer.runners.contextChange.add(this);
|
|
2348
|
+
}
|
|
2349
|
+
contextChange() {
|
|
2350
|
+
this._adaptor.contextChange(this.renderer);
|
|
1251
2351
|
}
|
|
1252
2352
|
validateRenderable(graphics) {
|
|
1253
2353
|
const context = graphics.context;
|
|
1254
|
-
const wasBatched = !!
|
|
2354
|
+
const wasBatched = !!graphics._gpuData;
|
|
1255
2355
|
const gpuContext = this.renderer.graphicsContext.updateGpuContext(context);
|
|
1256
2356
|
if (gpuContext.isBatchable || wasBatched !== gpuContext.isBatchable) {
|
|
1257
2357
|
return true;
|
|
@@ -1271,19 +2371,12 @@ class GraphicsPipe {
|
|
|
1271
2371
|
}
|
|
1272
2372
|
}
|
|
1273
2373
|
updateRenderable(graphics) {
|
|
1274
|
-
const
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
destroyRenderable(graphics) {
|
|
1283
|
-
if (this._graphicsBatchesHash[graphics.uid]) {
|
|
1284
|
-
this._removeBatchForRenderable(graphics.uid);
|
|
2374
|
+
const gpuData = this._getGpuDataForRenderable(graphics);
|
|
2375
|
+
const batches = gpuData.batches;
|
|
2376
|
+
for (let i = 0; i < batches.length; i++) {
|
|
2377
|
+
const batch = batches[i];
|
|
2378
|
+
batch._batcher.updateElement(batch);
|
|
1285
2379
|
}
|
|
1286
|
-
graphics.off("destroyed", this._destroyRenderableBound);
|
|
1287
2380
|
}
|
|
1288
2381
|
execute(graphics) {
|
|
1289
2382
|
if (!graphics.isRenderable)
|
|
@@ -1307,59 +2400,46 @@ class GraphicsPipe {
|
|
|
1307
2400
|
this._adaptor.execute(this, graphics);
|
|
1308
2401
|
}
|
|
1309
2402
|
_rebuild(graphics) {
|
|
1310
|
-
const
|
|
2403
|
+
const gpuData = this._getGpuDataForRenderable(graphics);
|
|
1311
2404
|
const gpuContext = this.renderer.graphicsContext.updateGpuContext(graphics.context);
|
|
1312
|
-
|
|
1313
|
-
this._removeBatchForRenderable(graphics.uid);
|
|
1314
|
-
}
|
|
2405
|
+
gpuData.destroy();
|
|
1315
2406
|
if (gpuContext.isBatchable) {
|
|
1316
|
-
this.
|
|
2407
|
+
this._updateBatchesForRenderable(graphics, gpuData);
|
|
1317
2408
|
}
|
|
1318
|
-
graphics.batched = gpuContext.isBatchable;
|
|
1319
2409
|
}
|
|
1320
2410
|
_addToBatcher(graphics, instructionSet) {
|
|
1321
2411
|
const batchPipe = this.renderer.renderPipes.batch;
|
|
1322
|
-
const batches = this.
|
|
2412
|
+
const batches = this._getGpuDataForRenderable(graphics).batches;
|
|
1323
2413
|
for (let i = 0; i < batches.length; i++) {
|
|
1324
2414
|
const batch = batches[i];
|
|
1325
2415
|
batchPipe.addToBatch(batch, instructionSet);
|
|
1326
2416
|
}
|
|
1327
2417
|
}
|
|
1328
|
-
|
|
1329
|
-
return
|
|
2418
|
+
_getGpuDataForRenderable(graphics) {
|
|
2419
|
+
return graphics._gpuData[this.renderer.uid] || this._initGpuDataForRenderable(graphics);
|
|
2420
|
+
}
|
|
2421
|
+
_initGpuDataForRenderable(graphics) {
|
|
2422
|
+
const gpuData = new GraphicsGpuData();
|
|
2423
|
+
graphics._gpuData[this.renderer.uid] = gpuData;
|
|
2424
|
+
return gpuData;
|
|
1330
2425
|
}
|
|
1331
|
-
|
|
2426
|
+
_updateBatchesForRenderable(graphics, gpuData) {
|
|
1332
2427
|
const context = graphics.context;
|
|
1333
2428
|
const gpuContext = this.renderer.graphicsContext.getGpuContext(context);
|
|
1334
2429
|
const roundPixels = this.renderer._roundPixels | graphics._roundPixels;
|
|
1335
|
-
|
|
2430
|
+
gpuData.batches = gpuContext.batches.map((batch) => {
|
|
1336
2431
|
const batchClone = BigPool.get(BatchableGraphics);
|
|
1337
2432
|
batch.copyTo(batchClone);
|
|
1338
2433
|
batchClone.renderable = graphics;
|
|
1339
2434
|
batchClone.roundPixels = roundPixels;
|
|
1340
2435
|
return batchClone;
|
|
1341
2436
|
});
|
|
1342
|
-
if (this._graphicsBatchesHash[graphics.uid] === void 0) {
|
|
1343
|
-
graphics.on("destroyed", this._destroyRenderableBound);
|
|
1344
|
-
}
|
|
1345
|
-
this._graphicsBatchesHash[graphics.uid] = batches;
|
|
1346
|
-
return batches;
|
|
1347
|
-
}
|
|
1348
|
-
_removeBatchForRenderable(graphicsUid) {
|
|
1349
|
-
this._graphicsBatchesHash[graphicsUid].forEach((batch) => {
|
|
1350
|
-
BigPool.return(batch);
|
|
1351
|
-
});
|
|
1352
|
-
this._graphicsBatchesHash[graphicsUid] = null;
|
|
1353
2437
|
}
|
|
1354
2438
|
destroy() {
|
|
1355
2439
|
this.renderer = null;
|
|
1356
2440
|
this._adaptor.destroy();
|
|
1357
2441
|
this._adaptor = null;
|
|
1358
2442
|
this.state = null;
|
|
1359
|
-
for (const i in this._graphicsBatchesHash) {
|
|
1360
|
-
this._removeBatchForRenderable(i);
|
|
1361
|
-
}
|
|
1362
|
-
this._graphicsBatchesHash = null;
|
|
1363
2443
|
}
|
|
1364
2444
|
}
|
|
1365
2445
|
/** @ignore */
|
|
@@ -1525,6 +2605,10 @@ class BatchableMesh {
|
|
|
1525
2605
|
}
|
|
1526
2606
|
}
|
|
1527
2607
|
|
|
2608
|
+
class MeshGpuData {
|
|
2609
|
+
destroy() {
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
1528
2612
|
class MeshPipe {
|
|
1529
2613
|
constructor(renderer, adaptor) {
|
|
1530
2614
|
this.localUniforms = new UniformGroup({
|
|
@@ -1535,14 +2619,9 @@ class MeshPipe {
|
|
|
1535
2619
|
this.localUniformsBindGroup = new BindGroup({
|
|
1536
2620
|
0: this.localUniforms
|
|
1537
2621
|
});
|
|
1538
|
-
this._meshDataHash = /* @__PURE__ */ Object.create(null);
|
|
1539
|
-
this._gpuBatchableMeshHash = /* @__PURE__ */ Object.create(null);
|
|
1540
|
-
this._destroyRenderableBound = this.destroyRenderable.bind(this);
|
|
1541
2622
|
this.renderer = renderer;
|
|
1542
2623
|
this._adaptor = adaptor;
|
|
1543
2624
|
this._adaptor.init();
|
|
1544
|
-
renderer.renderableGC.addManagedHash(this, "_gpuBatchableMeshHash");
|
|
1545
|
-
renderer.renderableGC.addManagedHash(this, "_meshDataHash");
|
|
1546
2625
|
}
|
|
1547
2626
|
validateRenderable(mesh) {
|
|
1548
2627
|
const meshData = this._getMeshData(mesh);
|
|
@@ -1584,21 +2663,12 @@ class MeshPipe {
|
|
|
1584
2663
|
}
|
|
1585
2664
|
updateRenderable(mesh) {
|
|
1586
2665
|
if (mesh.batched) {
|
|
1587
|
-
const gpuBatchableMesh = this.
|
|
2666
|
+
const gpuBatchableMesh = this._getBatchableMesh(mesh);
|
|
1588
2667
|
gpuBatchableMesh.setTexture(mesh._texture);
|
|
1589
2668
|
gpuBatchableMesh.geometry = mesh._geometry;
|
|
1590
2669
|
gpuBatchableMesh._batcher.updateElement(gpuBatchableMesh);
|
|
1591
2670
|
}
|
|
1592
2671
|
}
|
|
1593
|
-
destroyRenderable(mesh) {
|
|
1594
|
-
this._meshDataHash[mesh.uid] = null;
|
|
1595
|
-
const gpuMesh = this._gpuBatchableMeshHash[mesh.uid];
|
|
1596
|
-
if (gpuMesh) {
|
|
1597
|
-
BigPool.return(gpuMesh);
|
|
1598
|
-
this._gpuBatchableMeshHash[mesh.uid] = null;
|
|
1599
|
-
}
|
|
1600
|
-
mesh.off("destroyed", this._destroyRenderableBound);
|
|
1601
|
-
}
|
|
1602
2672
|
execute(mesh) {
|
|
1603
2673
|
if (!mesh.isRenderable)
|
|
1604
2674
|
return;
|
|
@@ -1615,37 +2685,33 @@ class MeshPipe {
|
|
|
1615
2685
|
this._adaptor.execute(this, mesh);
|
|
1616
2686
|
}
|
|
1617
2687
|
_getMeshData(mesh) {
|
|
1618
|
-
|
|
2688
|
+
var _a, _b;
|
|
2689
|
+
(_a = mesh._gpuData)[_b = this.renderer.uid] || (_a[_b] = new MeshGpuData());
|
|
2690
|
+
return mesh._gpuData[this.renderer.uid].meshData || this._initMeshData(mesh);
|
|
1619
2691
|
}
|
|
1620
2692
|
_initMeshData(mesh) {
|
|
1621
|
-
|
|
2693
|
+
mesh._gpuData[this.renderer.uid].meshData = {
|
|
1622
2694
|
batched: mesh.batched,
|
|
1623
2695
|
indexSize: mesh._geometry.indices?.length,
|
|
1624
2696
|
vertexSize: mesh._geometry.positions?.length
|
|
1625
2697
|
};
|
|
1626
|
-
mesh.
|
|
1627
|
-
return this._meshDataHash[mesh.uid];
|
|
2698
|
+
return mesh._gpuData[this.renderer.uid].meshData;
|
|
1628
2699
|
}
|
|
1629
2700
|
_getBatchableMesh(mesh) {
|
|
1630
|
-
|
|
2701
|
+
var _a, _b;
|
|
2702
|
+
(_a = mesh._gpuData)[_b = this.renderer.uid] || (_a[_b] = new MeshGpuData());
|
|
2703
|
+
return mesh._gpuData[this.renderer.uid].batchableMesh || this._initBatchableMesh(mesh);
|
|
1631
2704
|
}
|
|
1632
2705
|
_initBatchableMesh(mesh) {
|
|
1633
|
-
const gpuMesh =
|
|
2706
|
+
const gpuMesh = new BatchableMesh();
|
|
1634
2707
|
gpuMesh.renderable = mesh;
|
|
1635
2708
|
gpuMesh.setTexture(mesh._texture);
|
|
1636
2709
|
gpuMesh.transform = mesh.groupTransform;
|
|
1637
2710
|
gpuMesh.roundPixels = this.renderer._roundPixels | mesh._roundPixels;
|
|
1638
|
-
|
|
2711
|
+
mesh._gpuData[this.renderer.uid].batchableMesh = gpuMesh;
|
|
1639
2712
|
return gpuMesh;
|
|
1640
2713
|
}
|
|
1641
2714
|
destroy() {
|
|
1642
|
-
for (const i in this._gpuBatchableMeshHash) {
|
|
1643
|
-
if (this._gpuBatchableMeshHash[i]) {
|
|
1644
|
-
BigPool.return(this._gpuBatchableMeshHash[i]);
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
this._gpuBatchableMeshHash = null;
|
|
1648
|
-
this._meshDataHash = null;
|
|
1649
2715
|
this.localUniforms = null;
|
|
1650
2716
|
this.localUniformsBindGroup = null;
|
|
1651
2717
|
this._adaptor.destroy();
|
|
@@ -1728,7 +2794,7 @@ function generateParticleUpdateFunction(properties) {
|
|
|
1728
2794
|
function generateUpdateFunction(properties, dynamic) {
|
|
1729
2795
|
const funcFragments = [];
|
|
1730
2796
|
funcFragments.push(`
|
|
1731
|
-
|
|
2797
|
+
|
|
1732
2798
|
var index = 0;
|
|
1733
2799
|
|
|
1734
2800
|
for (let i = 0; i < ps.length; ++i)
|
|
@@ -1925,10 +2991,9 @@ class ParticleContainerPipe {
|
|
|
1925
2991
|
* @param adaptor
|
|
1926
2992
|
*/
|
|
1927
2993
|
constructor(renderer, adaptor) {
|
|
2994
|
+
/** @internal */
|
|
1928
2995
|
this.state = State.for2d();
|
|
1929
|
-
|
|
1930
|
-
// eslint-disable-next-line max-len
|
|
1931
|
-
this._destroyRenderableBound = this.destroyRenderable.bind(this);
|
|
2996
|
+
/** Local uniforms that are used for rendering particles. */
|
|
1932
2997
|
this.localUniforms = new UniformGroup({
|
|
1933
2998
|
uTranslationMatrix: { value: new Matrix(), type: "mat3x3<f32>" },
|
|
1934
2999
|
uColor: { value: new Float32Array(4), type: "vec4<f32>" },
|
|
@@ -1948,24 +3013,17 @@ class ParticleContainerPipe {
|
|
|
1948
3013
|
instructionSet.add(renderable);
|
|
1949
3014
|
}
|
|
1950
3015
|
getBuffers(renderable) {
|
|
1951
|
-
return
|
|
3016
|
+
return renderable._gpuData[this.renderer.uid] || this._initBuffer(renderable);
|
|
1952
3017
|
}
|
|
1953
3018
|
_initBuffer(renderable) {
|
|
1954
|
-
|
|
3019
|
+
renderable._gpuData[this.renderer.uid] = new ParticleBuffer({
|
|
1955
3020
|
size: renderable.particleChildren.length,
|
|
1956
3021
|
properties: renderable._properties
|
|
1957
3022
|
});
|
|
1958
|
-
renderable.
|
|
1959
|
-
return this._gpuBufferHash[renderable.uid];
|
|
3023
|
+
return renderable._gpuData[this.renderer.uid];
|
|
1960
3024
|
}
|
|
1961
3025
|
updateRenderable(_renderable) {
|
|
1962
3026
|
}
|
|
1963
|
-
destroyRenderable(renderable) {
|
|
1964
|
-
const buffer = this._gpuBufferHash[renderable.uid];
|
|
1965
|
-
buffer.destroy();
|
|
1966
|
-
this._gpuBufferHash[renderable.uid] = null;
|
|
1967
|
-
renderable.off("destroyed", this._destroyRenderableBound);
|
|
1968
|
-
}
|
|
1969
3027
|
execute(container) {
|
|
1970
3028
|
const children = container.particleChildren;
|
|
1971
3029
|
if (children.length === 0) {
|
|
@@ -2122,12 +3180,18 @@ _NineSliceGeometry.defaultOptions = {
|
|
|
2122
3180
|
};
|
|
2123
3181
|
let NineSliceGeometry = _NineSliceGeometry;
|
|
2124
3182
|
|
|
3183
|
+
class NineSliceSpriteGpuData extends BatchableMesh {
|
|
3184
|
+
constructor() {
|
|
3185
|
+
super();
|
|
3186
|
+
this.geometry = new NineSliceGeometry();
|
|
3187
|
+
}
|
|
3188
|
+
destroy() {
|
|
3189
|
+
this.geometry.destroy();
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
2125
3192
|
class NineSliceSpritePipe {
|
|
2126
3193
|
constructor(renderer) {
|
|
2127
|
-
this._gpuSpriteHash = /* @__PURE__ */ Object.create(null);
|
|
2128
|
-
this._destroyRenderableBound = this.destroyRenderable.bind(this);
|
|
2129
3194
|
this._renderer = renderer;
|
|
2130
|
-
this._renderer.renderableGC.addManagedHash(this, "_gpuSpriteHash");
|
|
2131
3195
|
}
|
|
2132
3196
|
addRenderable(sprite, instructionSet) {
|
|
2133
3197
|
const gpuSprite = this._getGpuSprite(sprite);
|
|
@@ -2136,7 +3200,7 @@ class NineSliceSpritePipe {
|
|
|
2136
3200
|
this._renderer.renderPipes.batch.addToBatch(gpuSprite, instructionSet);
|
|
2137
3201
|
}
|
|
2138
3202
|
updateRenderable(sprite) {
|
|
2139
|
-
const gpuSprite = this.
|
|
3203
|
+
const gpuSprite = this._getGpuSprite(sprite);
|
|
2140
3204
|
if (sprite.didViewUpdate)
|
|
2141
3205
|
this._updateBatchableSprite(sprite, gpuSprite);
|
|
2142
3206
|
gpuSprite._batcher.updateElement(gpuSprite);
|
|
@@ -2148,40 +3212,26 @@ class NineSliceSpritePipe {
|
|
|
2148
3212
|
sprite._texture
|
|
2149
3213
|
);
|
|
2150
3214
|
}
|
|
2151
|
-
destroyRenderable(sprite) {
|
|
2152
|
-
const batchableMesh = this._gpuSpriteHash[sprite.uid];
|
|
2153
|
-
BigPool.return(batchableMesh.geometry);
|
|
2154
|
-
BigPool.return(batchableMesh);
|
|
2155
|
-
this._gpuSpriteHash[sprite.uid] = null;
|
|
2156
|
-
sprite.off("destroyed", this._destroyRenderableBound);
|
|
2157
|
-
}
|
|
2158
3215
|
_updateBatchableSprite(sprite, batchableSprite) {
|
|
2159
3216
|
batchableSprite.geometry.update(sprite);
|
|
2160
3217
|
batchableSprite.setTexture(sprite._texture);
|
|
2161
3218
|
}
|
|
2162
3219
|
_getGpuSprite(sprite) {
|
|
2163
|
-
return
|
|
3220
|
+
return sprite._gpuData[this._renderer.uid] || this._initGPUSprite(sprite);
|
|
2164
3221
|
}
|
|
2165
3222
|
_initGPUSprite(sprite) {
|
|
2166
|
-
const
|
|
2167
|
-
batchableMesh
|
|
3223
|
+
const gpuData = sprite._gpuData[this._renderer.uid] = new NineSliceSpriteGpuData();
|
|
3224
|
+
const batchableMesh = gpuData;
|
|
2168
3225
|
batchableMesh.renderable = sprite;
|
|
2169
3226
|
batchableMesh.transform = sprite.groupTransform;
|
|
2170
3227
|
batchableMesh.texture = sprite._texture;
|
|
2171
3228
|
batchableMesh.roundPixels = this._renderer._roundPixels | sprite._roundPixels;
|
|
2172
|
-
this._gpuSpriteHash[sprite.uid] = batchableMesh;
|
|
2173
3229
|
if (!sprite.didViewUpdate) {
|
|
2174
3230
|
this._updateBatchableSprite(sprite, batchableMesh);
|
|
2175
3231
|
}
|
|
2176
|
-
|
|
2177
|
-
return batchableMesh;
|
|
3232
|
+
return gpuData;
|
|
2178
3233
|
}
|
|
2179
3234
|
destroy() {
|
|
2180
|
-
for (const i in this._gpuSpriteHash) {
|
|
2181
|
-
const batchableMesh = this._gpuSpriteHash[i];
|
|
2182
|
-
batchableMesh.geometry.destroy();
|
|
2183
|
-
}
|
|
2184
|
-
this._gpuSpriteHash = null;
|
|
2185
3235
|
this._renderer = null;
|
|
2186
3236
|
}
|
|
2187
3237
|
}
|
|
@@ -2254,7 +3304,7 @@ const tilingBit = {
|
|
|
2254
3304
|
if(unclamped.x == coord.x && unclamped.y == coord.y)
|
|
2255
3305
|
{
|
|
2256
3306
|
bias = -32.;
|
|
2257
|
-
}
|
|
3307
|
+
}
|
|
2258
3308
|
|
|
2259
3309
|
outColor = textureSampleBias(uTexture, uSampler, coord, bias);
|
|
2260
3310
|
`
|
|
@@ -2269,7 +3319,7 @@ const tilingBitGl = {
|
|
|
2269
3319
|
`
|
|
2270
3320
|
uniform mat3 uTextureTransform;
|
|
2271
3321
|
uniform vec4 uSizeAnchor;
|
|
2272
|
-
|
|
3322
|
+
|
|
2273
3323
|
`
|
|
2274
3324
|
),
|
|
2275
3325
|
main: (
|
|
@@ -2299,9 +3349,9 @@ const tilingBitGl = {
|
|
|
2299
3349
|
coord = (uMapCoord * vec3(coord, 1.0)).xy;
|
|
2300
3350
|
vec2 unclamped = coord;
|
|
2301
3351
|
coord = clamp(coord, uClampFrame.xy, uClampFrame.zw);
|
|
2302
|
-
|
|
3352
|
+
|
|
2303
3353
|
outColor = texture(uTexture, coord, unclamped == coord ? 0.0 : -32.0);// lod-bias very negative to force lod 0
|
|
2304
|
-
|
|
3354
|
+
|
|
2305
3355
|
`
|
|
2306
3356
|
)
|
|
2307
3357
|
}
|
|
@@ -2404,7 +3454,7 @@ function setPositions(tilingSprite, positions) {
|
|
|
2404
3454
|
|
|
2405
3455
|
function applyMatrix(array, stride, offset, matrix) {
|
|
2406
3456
|
let index = 0;
|
|
2407
|
-
const size = array.length / (stride);
|
|
3457
|
+
const size = array.length / (stride );
|
|
2408
3458
|
const a = matrix.a;
|
|
2409
3459
|
const b = matrix.b;
|
|
2410
3460
|
const c = matrix.c;
|
|
@@ -2446,13 +3496,24 @@ function setUvs(tilingSprite, uvs) {
|
|
|
2446
3496
|
}
|
|
2447
3497
|
|
|
2448
3498
|
const sharedQuad = new QuadGeometry();
|
|
3499
|
+
class TilingSpriteGpuData {
|
|
3500
|
+
constructor() {
|
|
3501
|
+
this.canBatch = true;
|
|
3502
|
+
this.geometry = new MeshGeometry({
|
|
3503
|
+
indices: sharedQuad.indices.slice(),
|
|
3504
|
+
positions: sharedQuad.positions.slice(),
|
|
3505
|
+
uvs: sharedQuad.uvs.slice()
|
|
3506
|
+
});
|
|
3507
|
+
}
|
|
3508
|
+
destroy() {
|
|
3509
|
+
this.geometry.destroy();
|
|
3510
|
+
this.shader?.destroy();
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
2449
3513
|
class TilingSpritePipe {
|
|
2450
3514
|
constructor(renderer) {
|
|
2451
3515
|
this._state = State.default2d;
|
|
2452
|
-
this._tilingSpriteDataHash = /* @__PURE__ */ Object.create(null);
|
|
2453
|
-
this._destroyRenderableBound = this.destroyRenderable.bind(this);
|
|
2454
3516
|
this._renderer = renderer;
|
|
2455
|
-
this._renderer.renderableGC.addManagedHash(this, "_tilingSpriteDataHash");
|
|
2456
3517
|
}
|
|
2457
3518
|
validateRenderable(renderable) {
|
|
2458
3519
|
const tilingSpriteData = this._getTilingSpriteData(renderable);
|
|
@@ -2493,7 +3554,7 @@ class TilingSpritePipe {
|
|
|
2493
3554
|
}
|
|
2494
3555
|
}
|
|
2495
3556
|
execute(tilingSprite) {
|
|
2496
|
-
const { shader } = this.
|
|
3557
|
+
const { shader } = this._getTilingSpriteData(tilingSprite);
|
|
2497
3558
|
shader.groups[0] = this._renderer.globalUniforms.bindGroup;
|
|
2498
3559
|
const localUniforms = shader.resources.localUniforms.uniforms;
|
|
2499
3560
|
localUniforms.uTransformMatrix = tilingSprite.groupTransform;
|
|
@@ -2530,29 +3591,14 @@ class TilingSpritePipe {
|
|
|
2530
3591
|
);
|
|
2531
3592
|
}
|
|
2532
3593
|
}
|
|
2533
|
-
destroyRenderable(tilingSprite) {
|
|
2534
|
-
const tilingSpriteData = this._getTilingSpriteData(tilingSprite);
|
|
2535
|
-
tilingSpriteData.batchableMesh = null;
|
|
2536
|
-
tilingSpriteData.shader?.destroy();
|
|
2537
|
-
this._tilingSpriteDataHash[tilingSprite.uid] = null;
|
|
2538
|
-
tilingSprite.off("destroyed", this._destroyRenderableBound);
|
|
2539
|
-
}
|
|
2540
3594
|
_getTilingSpriteData(renderable) {
|
|
2541
|
-
return
|
|
3595
|
+
return renderable._gpuData[this._renderer.uid] || this._initTilingSpriteData(renderable);
|
|
2542
3596
|
}
|
|
2543
3597
|
_initTilingSpriteData(tilingSprite) {
|
|
2544
|
-
const
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
});
|
|
2549
|
-
this._tilingSpriteDataHash[tilingSprite.uid] = {
|
|
2550
|
-
canBatch: true,
|
|
2551
|
-
renderable: tilingSprite,
|
|
2552
|
-
geometry
|
|
2553
|
-
};
|
|
2554
|
-
tilingSprite.on("destroyed", this._destroyRenderableBound);
|
|
2555
|
-
return this._tilingSpriteDataHash[tilingSprite.uid];
|
|
3598
|
+
const gpuData = new TilingSpriteGpuData();
|
|
3599
|
+
gpuData.renderable = tilingSprite;
|
|
3600
|
+
tilingSprite._gpuData[this._renderer.uid] = gpuData;
|
|
3601
|
+
return gpuData;
|
|
2556
3602
|
}
|
|
2557
3603
|
_updateBatchableMesh(tilingSprite) {
|
|
2558
3604
|
const renderableData = this._getTilingSpriteData(tilingSprite);
|
|
@@ -2566,10 +3612,6 @@ class TilingSpritePipe {
|
|
|
2566
3612
|
setPositions(tilingSprite, geometry.positions);
|
|
2567
3613
|
}
|
|
2568
3614
|
destroy() {
|
|
2569
|
-
for (const i in this._tilingSpriteDataHash) {
|
|
2570
|
-
this.destroyRenderable(this._tilingSpriteDataHash[i].renderable);
|
|
2571
|
-
}
|
|
2572
|
-
this._tilingSpriteDataHash = null;
|
|
2573
3615
|
this._renderer = null;
|
|
2574
3616
|
}
|
|
2575
3617
|
_updateCanBatch(tilingSprite) {
|
|
@@ -2641,7 +3683,7 @@ const localUniformMSDFBit = {
|
|
|
2641
3683
|
),
|
|
2642
3684
|
main: (
|
|
2643
3685
|
/* wgsl */
|
|
2644
|
-
`
|
|
3686
|
+
`
|
|
2645
3687
|
outColor = vec4<f32>(calculateMSDFAlpha(outColor, localUniforms.uColor, localUniforms.uDistance));
|
|
2646
3688
|
`
|
|
2647
3689
|
)
|
|
@@ -2684,7 +3726,7 @@ const localUniformMSDFBitGl = {
|
|
|
2684
3726
|
),
|
|
2685
3727
|
main: (
|
|
2686
3728
|
/* glsl */
|
|
2687
|
-
`
|
|
3729
|
+
`
|
|
2688
3730
|
outColor = vec4(calculateMSDFAlpha(outColor, vColor, uDistance));
|
|
2689
3731
|
`
|
|
2690
3732
|
)
|
|
@@ -2698,12 +3740,12 @@ const mSDFBit = {
|
|
|
2698
3740
|
/* wgsl */
|
|
2699
3741
|
`
|
|
2700
3742
|
fn calculateMSDFAlpha(msdfColor:vec4<f32>, shapeColor:vec4<f32>, distance:f32) -> f32 {
|
|
2701
|
-
|
|
3743
|
+
|
|
2702
3744
|
// MSDF
|
|
2703
3745
|
var median = msdfColor.r + msdfColor.g + msdfColor.b -
|
|
2704
3746
|
min(msdfColor.r, min(msdfColor.g, msdfColor.b)) -
|
|
2705
3747
|
max(msdfColor.r, max(msdfColor.g, msdfColor.b));
|
|
2706
|
-
|
|
3748
|
+
|
|
2707
3749
|
// SDF
|
|
2708
3750
|
median = min(median, msdfColor.a);
|
|
2709
3751
|
|
|
@@ -2721,7 +3763,7 @@ const mSDFBit = {
|
|
|
2721
3763
|
var coverage: f32 = pow(shapeColor.a * alpha, gamma);
|
|
2722
3764
|
|
|
2723
3765
|
return coverage;
|
|
2724
|
-
|
|
3766
|
+
|
|
2725
3767
|
}
|
|
2726
3768
|
`
|
|
2727
3769
|
)
|
|
@@ -2734,18 +3776,18 @@ const mSDFBitGl = {
|
|
|
2734
3776
|
/* glsl */
|
|
2735
3777
|
`
|
|
2736
3778
|
float calculateMSDFAlpha(vec4 msdfColor, vec4 shapeColor, float distance) {
|
|
2737
|
-
|
|
3779
|
+
|
|
2738
3780
|
// MSDF
|
|
2739
3781
|
float median = msdfColor.r + msdfColor.g + msdfColor.b -
|
|
2740
3782
|
min(msdfColor.r, min(msdfColor.g, msdfColor.b)) -
|
|
2741
3783
|
max(msdfColor.r, max(msdfColor.g, msdfColor.b));
|
|
2742
|
-
|
|
3784
|
+
|
|
2743
3785
|
// SDF
|
|
2744
3786
|
median = min(median, msdfColor.a);
|
|
2745
|
-
|
|
3787
|
+
|
|
2746
3788
|
float screenPxDistance = distance * (median - 0.5);
|
|
2747
3789
|
float alpha = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
|
2748
|
-
|
|
3790
|
+
|
|
2749
3791
|
if (median < 0.01) {
|
|
2750
3792
|
alpha = 0.0;
|
|
2751
3793
|
} else if (median > 0.99) {
|
|
@@ -2755,8 +3797,8 @@ const mSDFBitGl = {
|
|
|
2755
3797
|
// Gamma correction for coverage-like alpha
|
|
2756
3798
|
float luma = dot(shapeColor.rgb, vec3(0.299, 0.587, 0.114));
|
|
2757
3799
|
float gamma = mix(1.0, 1.0 / 2.2, luma);
|
|
2758
|
-
float coverage = pow(shapeColor.a * alpha, gamma);
|
|
2759
|
-
|
|
3800
|
+
float coverage = pow(shapeColor.a * alpha, gamma);
|
|
3801
|
+
|
|
2760
3802
|
return coverage;
|
|
2761
3803
|
}
|
|
2762
3804
|
`
|
|
@@ -2767,14 +3809,13 @@ const mSDFBitGl = {
|
|
|
2767
3809
|
let gpuProgram;
|
|
2768
3810
|
let glProgram;
|
|
2769
3811
|
class SdfShader extends Shader {
|
|
2770
|
-
constructor() {
|
|
3812
|
+
constructor(maxTextures) {
|
|
2771
3813
|
const uniforms = new UniformGroup({
|
|
2772
3814
|
uColor: { value: new Float32Array([1, 1, 1, 1]), type: "vec4<f32>" },
|
|
2773
3815
|
uTransformMatrix: { value: new Matrix(), type: "mat3x3<f32>" },
|
|
2774
3816
|
uDistance: { value: 4, type: "f32" },
|
|
2775
3817
|
uRound: { value: 0, type: "f32" }
|
|
2776
3818
|
});
|
|
2777
|
-
const maxTextures = getMaxTexturesPerBatch();
|
|
2778
3819
|
gpuProgram ?? (gpuProgram = compileHighShaderGpuProgram({
|
|
2779
3820
|
name: "sdf-shader",
|
|
2780
3821
|
bits: [
|
|
@@ -2806,10 +3847,16 @@ class SdfShader extends Shader {
|
|
|
2806
3847
|
}
|
|
2807
3848
|
}
|
|
2808
3849
|
|
|
3850
|
+
class BitmapTextGraphics extends Graphics {
|
|
3851
|
+
destroy() {
|
|
3852
|
+
if (this.context.customShader) {
|
|
3853
|
+
this.context.customShader.destroy();
|
|
3854
|
+
}
|
|
3855
|
+
super.destroy();
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
2809
3858
|
class BitmapTextPipe {
|
|
2810
3859
|
constructor(renderer) {
|
|
2811
|
-
this._gpuBitmapText = {};
|
|
2812
|
-
this._destroyRenderableBound = this.destroyRenderable.bind(this);
|
|
2813
3860
|
this._renderer = renderer;
|
|
2814
3861
|
this._renderer.renderableGC.addManagedHash(this, "_gpuBitmapText");
|
|
2815
3862
|
}
|
|
@@ -2833,19 +3880,6 @@ class BitmapTextPipe {
|
|
|
2833
3880
|
this._updateDistanceField(bitmapText);
|
|
2834
3881
|
}
|
|
2835
3882
|
}
|
|
2836
|
-
destroyRenderable(bitmapText) {
|
|
2837
|
-
bitmapText.off("destroyed", this._destroyRenderableBound);
|
|
2838
|
-
this._destroyRenderableByUid(bitmapText.uid);
|
|
2839
|
-
}
|
|
2840
|
-
_destroyRenderableByUid(renderableUid) {
|
|
2841
|
-
const context = this._gpuBitmapText[renderableUid].context;
|
|
2842
|
-
if (context.customShader) {
|
|
2843
|
-
BigPool.return(context.customShader);
|
|
2844
|
-
context.customShader = null;
|
|
2845
|
-
}
|
|
2846
|
-
BigPool.return(this._gpuBitmapText[renderableUid]);
|
|
2847
|
-
this._gpuBitmapText[renderableUid] = null;
|
|
2848
|
-
}
|
|
2849
3883
|
updateRenderable(bitmapText) {
|
|
2850
3884
|
const graphicsRenderable = this._getGpuBitmapText(bitmapText);
|
|
2851
3885
|
syncWithProxy(bitmapText, graphicsRenderable);
|
|
@@ -2860,14 +3894,13 @@ class BitmapTextPipe {
|
|
|
2860
3894
|
context.clear();
|
|
2861
3895
|
if (bitmapFont.distanceField.type !== "none") {
|
|
2862
3896
|
if (!context.customShader) {
|
|
2863
|
-
context.customShader =
|
|
3897
|
+
context.customShader = new SdfShader(this._renderer.limits.maxBatchableTextures);
|
|
2864
3898
|
}
|
|
2865
3899
|
}
|
|
2866
|
-
const chars =
|
|
3900
|
+
const chars = CanvasTextMetrics.graphemeSegmenter(bitmapText.text);
|
|
2867
3901
|
const style = bitmapText._style;
|
|
2868
3902
|
let currentY = bitmapFont.baseLineOffset;
|
|
2869
3903
|
const bitmapTextLayout = getBitmapTextLayout(chars, style, bitmapFont, true);
|
|
2870
|
-
let index = 0;
|
|
2871
3904
|
const padding = style.padding;
|
|
2872
3905
|
const scale = bitmapTextLayout.scale;
|
|
2873
3906
|
let tx = bitmapTextLayout.width;
|
|
@@ -2881,7 +3914,7 @@ class BitmapTextPipe {
|
|
|
2881
3914
|
for (let i = 0; i < bitmapTextLayout.lines.length; i++) {
|
|
2882
3915
|
const line = bitmapTextLayout.lines[i];
|
|
2883
3916
|
for (let j = 0; j < line.charPositions.length; j++) {
|
|
2884
|
-
const char = chars[
|
|
3917
|
+
const char = line.chars[j];
|
|
2885
3918
|
const charData = bitmapFont.chars[char];
|
|
2886
3919
|
if (charData?.texture) {
|
|
2887
3920
|
context.texture(
|
|
@@ -2896,14 +3929,13 @@ class BitmapTextPipe {
|
|
|
2896
3929
|
}
|
|
2897
3930
|
}
|
|
2898
3931
|
_getGpuBitmapText(bitmapText) {
|
|
2899
|
-
return
|
|
3932
|
+
return bitmapText._gpuData[this._renderer.uid] || this.initGpuText(bitmapText);
|
|
2900
3933
|
}
|
|
2901
3934
|
initGpuText(bitmapText) {
|
|
2902
|
-
const proxyRenderable =
|
|
2903
|
-
|
|
3935
|
+
const proxyRenderable = new BitmapTextGraphics();
|
|
3936
|
+
bitmapText._gpuData[this._renderer.uid] = proxyRenderable;
|
|
2904
3937
|
this._updateContext(bitmapText, proxyRenderable);
|
|
2905
|
-
|
|
2906
|
-
return this._gpuBitmapText[bitmapText.uid];
|
|
3938
|
+
return proxyRenderable;
|
|
2907
3939
|
}
|
|
2908
3940
|
_updateDistanceField(bitmapText) {
|
|
2909
3941
|
const context = this._getGpuBitmapText(bitmapText).context;
|
|
@@ -2918,10 +3950,6 @@ class BitmapTextPipe {
|
|
|
2918
3950
|
context.customShader.resources.localUniforms.uniforms.uDistance = distance;
|
|
2919
3951
|
}
|
|
2920
3952
|
destroy() {
|
|
2921
|
-
for (const uid in this._gpuBitmapText) {
|
|
2922
|
-
this._destroyRenderableByUid(uid);
|
|
2923
|
-
}
|
|
2924
|
-
this._gpuBitmapText = null;
|
|
2925
3953
|
this._renderer = null;
|
|
2926
3954
|
}
|
|
2927
3955
|
}
|
|
@@ -2946,136 +3974,102 @@ function syncWithProxy(container, proxy) {
|
|
|
2946
3974
|
proxy._roundPixels = container._roundPixels;
|
|
2947
3975
|
}
|
|
2948
3976
|
|
|
3977
|
+
class BatchableHTMLText extends BatchableSprite {
|
|
3978
|
+
/**
|
|
3979
|
+
* Creates an instance of BatchableHTMLText.
|
|
3980
|
+
* @param renderer - The renderer instance to be used.
|
|
3981
|
+
*/
|
|
3982
|
+
constructor(renderer) {
|
|
3983
|
+
super();
|
|
3984
|
+
this.generatingTexture = false;
|
|
3985
|
+
this._renderer = renderer;
|
|
3986
|
+
renderer.runners.resolutionChange.add(this);
|
|
3987
|
+
}
|
|
3988
|
+
/** Handles resolution changes for the HTML text. If the text has auto resolution enabled, it triggers a view update. */
|
|
3989
|
+
resolutionChange() {
|
|
3990
|
+
const text = this.renderable;
|
|
3991
|
+
if (text._autoResolution) {
|
|
3992
|
+
text.onViewUpdate();
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
/** Destroys the BatchableHTMLText instance. Returns the texture promise to the renderer and cleans up references. */
|
|
3996
|
+
destroy() {
|
|
3997
|
+
this._renderer.htmlText.returnTexturePromise(this.texturePromise);
|
|
3998
|
+
this.texturePromise = null;
|
|
3999
|
+
this._renderer = null;
|
|
4000
|
+
}
|
|
4001
|
+
}
|
|
4002
|
+
|
|
2949
4003
|
function updateTextBounds(batchableSprite, text) {
|
|
2950
4004
|
const { texture, bounds } = batchableSprite;
|
|
4005
|
+
const padding = text._style._getFinalPadding();
|
|
2951
4006
|
updateQuadBounds(bounds, text._anchor, texture);
|
|
2952
|
-
const
|
|
2953
|
-
|
|
2954
|
-
bounds.
|
|
2955
|
-
bounds.
|
|
2956
|
-
bounds.
|
|
4007
|
+
const paddingOffset = text._anchor._x * padding * 2;
|
|
4008
|
+
const paddingOffsetY = text._anchor._y * padding * 2;
|
|
4009
|
+
bounds.minX -= padding - paddingOffset;
|
|
4010
|
+
bounds.minY -= padding - paddingOffsetY;
|
|
4011
|
+
bounds.maxX -= padding - paddingOffset;
|
|
4012
|
+
bounds.maxY -= padding - paddingOffsetY;
|
|
2957
4013
|
}
|
|
2958
4014
|
|
|
2959
4015
|
class HTMLTextPipe {
|
|
2960
4016
|
constructor(renderer) {
|
|
2961
|
-
this._gpuText = /* @__PURE__ */ Object.create(null);
|
|
2962
|
-
this._destroyRenderableBound = this.destroyRenderable.bind(this);
|
|
2963
4017
|
this._renderer = renderer;
|
|
2964
|
-
this._renderer.runners.resolutionChange.add(this);
|
|
2965
|
-
this._renderer.renderableGC.addManagedHash(this, "_gpuText");
|
|
2966
|
-
}
|
|
2967
|
-
resolutionChange() {
|
|
2968
|
-
for (const i in this._gpuText) {
|
|
2969
|
-
const gpuText = this._gpuText[i];
|
|
2970
|
-
if (!gpuText)
|
|
2971
|
-
continue;
|
|
2972
|
-
const text = gpuText.batchableSprite.renderable;
|
|
2973
|
-
if (text._autoResolution) {
|
|
2974
|
-
text._resolution = this._renderer.resolution;
|
|
2975
|
-
text.onViewUpdate();
|
|
2976
|
-
}
|
|
2977
|
-
}
|
|
2978
4018
|
}
|
|
2979
4019
|
validateRenderable(htmlText) {
|
|
2980
|
-
|
|
2981
|
-
const newKey = htmlText._getKey();
|
|
2982
|
-
if (gpuText.textureNeedsUploading) {
|
|
2983
|
-
gpuText.textureNeedsUploading = false;
|
|
2984
|
-
return true;
|
|
2985
|
-
}
|
|
2986
|
-
if (gpuText.currentKey !== newKey) {
|
|
2987
|
-
return true;
|
|
2988
|
-
}
|
|
2989
|
-
return false;
|
|
4020
|
+
return htmlText._didTextUpdate;
|
|
2990
4021
|
}
|
|
2991
4022
|
addRenderable(htmlText, instructionSet) {
|
|
2992
|
-
const
|
|
2993
|
-
const batchableSprite = gpuText.batchableSprite;
|
|
2994
|
-
if (htmlText._didTextUpdate) {
|
|
2995
|
-
this._updateText(htmlText);
|
|
2996
|
-
}
|
|
2997
|
-
this._renderer.renderPipes.batch.addToBatch(batchableSprite, instructionSet);
|
|
2998
|
-
}
|
|
2999
|
-
updateRenderable(htmlText) {
|
|
3000
|
-
const gpuText = this._getGpuText(htmlText);
|
|
3001
|
-
const batchableSprite = gpuText.batchableSprite;
|
|
4023
|
+
const batchableHTMLText = this._getGpuText(htmlText);
|
|
3002
4024
|
if (htmlText._didTextUpdate) {
|
|
3003
|
-
this._updateText(htmlText);
|
|
3004
|
-
}
|
|
3005
|
-
batchableSprite._batcher.updateElement(batchableSprite);
|
|
3006
|
-
}
|
|
3007
|
-
destroyRenderable(htmlText) {
|
|
3008
|
-
htmlText.off("destroyed", this._destroyRenderableBound);
|
|
3009
|
-
this._destroyRenderableById(htmlText.uid);
|
|
3010
|
-
}
|
|
3011
|
-
_destroyRenderableById(htmlTextUid) {
|
|
3012
|
-
const gpuText = this._gpuText[htmlTextUid];
|
|
3013
|
-
this._renderer.htmlText.decreaseReferenceCount(gpuText.currentKey);
|
|
3014
|
-
BigPool.return(gpuText.batchableSprite);
|
|
3015
|
-
this._gpuText[htmlTextUid] = null;
|
|
3016
|
-
}
|
|
3017
|
-
_updateText(htmlText) {
|
|
3018
|
-
const newKey = htmlText._getKey();
|
|
3019
|
-
const gpuText = this._getGpuText(htmlText);
|
|
3020
|
-
const batchableSprite = gpuText.batchableSprite;
|
|
3021
|
-
if (gpuText.currentKey !== newKey) {
|
|
3022
4025
|
this._updateGpuText(htmlText).catch((e) => {
|
|
3023
4026
|
console.error(e);
|
|
3024
4027
|
});
|
|
4028
|
+
htmlText._didTextUpdate = false;
|
|
4029
|
+
updateTextBounds(batchableHTMLText, htmlText);
|
|
3025
4030
|
}
|
|
3026
|
-
|
|
3027
|
-
|
|
4031
|
+
this._renderer.renderPipes.batch.addToBatch(batchableHTMLText, instructionSet);
|
|
4032
|
+
}
|
|
4033
|
+
updateRenderable(htmlText) {
|
|
4034
|
+
const batchableHTMLText = this._getGpuText(htmlText);
|
|
4035
|
+
batchableHTMLText._batcher.updateElement(batchableHTMLText);
|
|
3028
4036
|
}
|
|
3029
4037
|
async _updateGpuText(htmlText) {
|
|
3030
4038
|
htmlText._didTextUpdate = false;
|
|
3031
|
-
const
|
|
3032
|
-
if (
|
|
4039
|
+
const batchableHTMLText = this._getGpuText(htmlText);
|
|
4040
|
+
if (batchableHTMLText.generatingTexture)
|
|
3033
4041
|
return;
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
)
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
htmlText.onViewUpdate();
|
|
3050
|
-
updateTextBounds(batchableSprite, htmlText);
|
|
4042
|
+
if (batchableHTMLText.texturePromise) {
|
|
4043
|
+
this._renderer.htmlText.returnTexturePromise(batchableHTMLText.texturePromise);
|
|
4044
|
+
batchableHTMLText.texturePromise = null;
|
|
4045
|
+
}
|
|
4046
|
+
batchableHTMLText.generatingTexture = true;
|
|
4047
|
+
htmlText._resolution = htmlText._autoResolution ? this._renderer.resolution : htmlText.resolution;
|
|
4048
|
+
const texturePromise = this._renderer.htmlText.getTexturePromise(htmlText);
|
|
4049
|
+
batchableHTMLText.texturePromise = texturePromise;
|
|
4050
|
+
batchableHTMLText.texture = await texturePromise;
|
|
4051
|
+
const renderGroup = htmlText.renderGroup || htmlText.parentRenderGroup;
|
|
4052
|
+
if (renderGroup) {
|
|
4053
|
+
renderGroup.structureDidChange = true;
|
|
4054
|
+
}
|
|
4055
|
+
batchableHTMLText.generatingTexture = false;
|
|
4056
|
+
updateTextBounds(batchableHTMLText, htmlText);
|
|
3051
4057
|
}
|
|
3052
4058
|
_getGpuText(htmlText) {
|
|
3053
|
-
return
|
|
4059
|
+
return htmlText._gpuData[this._renderer.uid] || this.initGpuText(htmlText);
|
|
3054
4060
|
}
|
|
3055
4061
|
initGpuText(htmlText) {
|
|
3056
|
-
const
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
};
|
|
3063
|
-
const batchableSprite = gpuTextData.batchableSprite;
|
|
3064
|
-
batchableSprite.renderable = htmlText;
|
|
3065
|
-
batchableSprite.transform = htmlText.groupTransform;
|
|
3066
|
-
batchableSprite.texture = Texture.EMPTY;
|
|
3067
|
-
batchableSprite.bounds = { minX: 0, maxX: 1, minY: 0, maxY: 0 };
|
|
3068
|
-
batchableSprite.roundPixels = this._renderer._roundPixels | htmlText._roundPixels;
|
|
4062
|
+
const batchableHTMLText = new BatchableHTMLText(this._renderer);
|
|
4063
|
+
batchableHTMLText.renderable = htmlText;
|
|
4064
|
+
batchableHTMLText.transform = htmlText.groupTransform;
|
|
4065
|
+
batchableHTMLText.texture = Texture.EMPTY;
|
|
4066
|
+
batchableHTMLText.bounds = { minX: 0, maxX: 1, minY: 0, maxY: 0 };
|
|
4067
|
+
batchableHTMLText.roundPixels = this._renderer._roundPixels | htmlText._roundPixels;
|
|
3069
4068
|
htmlText._resolution = htmlText._autoResolution ? this._renderer.resolution : htmlText.resolution;
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
return gpuTextData;
|
|
4069
|
+
htmlText._gpuData[this._renderer.uid] = batchableHTMLText;
|
|
4070
|
+
return batchableHTMLText;
|
|
3073
4071
|
}
|
|
3074
4072
|
destroy() {
|
|
3075
|
-
for (const i in this._gpuText) {
|
|
3076
|
-
this._destroyRenderableById(i);
|
|
3077
|
-
}
|
|
3078
|
-
this._gpuText = null;
|
|
3079
4073
|
this._renderer = null;
|
|
3080
4074
|
}
|
|
3081
4075
|
}
|
|
@@ -3233,34 +4227,21 @@ function loadSVGImage(image, url, delay) {
|
|
|
3233
4227
|
|
|
3234
4228
|
class HTMLTextSystem {
|
|
3235
4229
|
constructor(renderer) {
|
|
3236
|
-
this._activeTextures = {};
|
|
3237
4230
|
this._renderer = renderer;
|
|
3238
4231
|
this._createCanvas = renderer.type === RendererType.WEBGPU;
|
|
3239
4232
|
}
|
|
4233
|
+
/**
|
|
4234
|
+
* @param options
|
|
4235
|
+
* @deprecated Use getTexturePromise instead
|
|
4236
|
+
*/
|
|
3240
4237
|
getTexture(options) {
|
|
3241
|
-
return this.
|
|
3242
|
-
options.text,
|
|
3243
|
-
options.resolution,
|
|
3244
|
-
options.style
|
|
3245
|
-
);
|
|
4238
|
+
return this.getTexturePromise(options);
|
|
3246
4239
|
}
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
this._increaseReferenceCount(textKey);
|
|
3250
|
-
return this._activeTextures[textKey].promise;
|
|
3251
|
-
}
|
|
3252
|
-
const promise = this._buildTexturePromise(text, resolution, style).then((texture) => {
|
|
3253
|
-
this._activeTextures[textKey].texture = texture;
|
|
3254
|
-
return texture;
|
|
3255
|
-
});
|
|
3256
|
-
this._activeTextures[textKey] = {
|
|
3257
|
-
texture: null,
|
|
3258
|
-
promise,
|
|
3259
|
-
usageCount: 1
|
|
3260
|
-
};
|
|
3261
|
-
return promise;
|
|
4240
|
+
getTexturePromise(options) {
|
|
4241
|
+
return this._buildTexturePromise(options);
|
|
3262
4242
|
}
|
|
3263
|
-
async _buildTexturePromise(
|
|
4243
|
+
async _buildTexturePromise(options) {
|
|
4244
|
+
const { text, style, resolution, textureStyle } = options;
|
|
3264
4245
|
const htmlTextData = BigPool.get(HTMLTextRenderData);
|
|
3265
4246
|
const fontFamilies = extractFontFamilies(text, style);
|
|
3266
4247
|
const fontCSS = await getFontCss(
|
|
@@ -3288,6 +4269,8 @@ class HTMLTextSystem {
|
|
|
3288
4269
|
image.height - uvSafeOffset,
|
|
3289
4270
|
resolution
|
|
3290
4271
|
);
|
|
4272
|
+
if (textureStyle)
|
|
4273
|
+
texture.source.style = textureStyle;
|
|
3291
4274
|
if (this._createCanvas) {
|
|
3292
4275
|
this._renderer.texture.initSource(texture.source);
|
|
3293
4276
|
CanvasPool.returnCanvasAndContext(canvasAndContext);
|
|
@@ -3295,38 +4278,20 @@ class HTMLTextSystem {
|
|
|
3295
4278
|
BigPool.return(htmlTextData);
|
|
3296
4279
|
return texture;
|
|
3297
4280
|
}
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
return;
|
|
3305
|
-
activeTexture.usageCount--;
|
|
3306
|
-
if (activeTexture.usageCount === 0) {
|
|
3307
|
-
if (activeTexture.texture) {
|
|
3308
|
-
this._cleanUp(activeTexture);
|
|
3309
|
-
} else {
|
|
3310
|
-
activeTexture.promise.then((texture) => {
|
|
3311
|
-
activeTexture.texture = texture;
|
|
3312
|
-
this._cleanUp(activeTexture);
|
|
3313
|
-
}).catch(() => {
|
|
3314
|
-
warn("HTMLTextSystem: Failed to clean texture");
|
|
3315
|
-
});
|
|
3316
|
-
}
|
|
3317
|
-
this._activeTextures[textKey] = null;
|
|
3318
|
-
}
|
|
3319
|
-
}
|
|
3320
|
-
_cleanUp(activeTexture) {
|
|
3321
|
-
TexturePool.returnTexture(activeTexture.texture);
|
|
3322
|
-
activeTexture.texture.source.resource = null;
|
|
3323
|
-
activeTexture.texture.source.uploadMethodId = "unknown";
|
|
4281
|
+
returnTexturePromise(texturePromise) {
|
|
4282
|
+
texturePromise.then((texture) => {
|
|
4283
|
+
this._cleanUp(texture);
|
|
4284
|
+
}).catch(() => {
|
|
4285
|
+
warn("HTMLTextSystem: Failed to clean texture");
|
|
4286
|
+
});
|
|
3324
4287
|
}
|
|
3325
|
-
|
|
3326
|
-
|
|
4288
|
+
_cleanUp(texture) {
|
|
4289
|
+
TexturePool.returnTexture(texture, true);
|
|
4290
|
+
texture.source.resource = null;
|
|
4291
|
+
texture.source.uploadMethodId = "unknown";
|
|
3327
4292
|
}
|
|
3328
4293
|
destroy() {
|
|
3329
|
-
this.
|
|
4294
|
+
this._renderer = null;
|
|
3330
4295
|
}
|
|
3331
4296
|
}
|
|
3332
4297
|
/** @ignore */
|
|
@@ -3338,110 +4303,66 @@ HTMLTextSystem.extension = {
|
|
|
3338
4303
|
],
|
|
3339
4304
|
name: "htmlText"
|
|
3340
4305
|
};
|
|
3341
|
-
HTMLTextSystem.defaultFontOptions = {
|
|
3342
|
-
fontFamily: "Arial",
|
|
3343
|
-
fontStyle: "normal",
|
|
3344
|
-
fontWeight: "normal"
|
|
3345
|
-
};
|
|
3346
4306
|
|
|
3347
|
-
class
|
|
4307
|
+
class BatchableText extends BatchableSprite {
|
|
3348
4308
|
constructor(renderer) {
|
|
3349
|
-
|
|
3350
|
-
this._destroyRenderableBound = this.destroyRenderable.bind(this);
|
|
4309
|
+
super();
|
|
3351
4310
|
this._renderer = renderer;
|
|
3352
|
-
|
|
3353
|
-
this._renderer.renderableGC.addManagedHash(this, "_gpuText");
|
|
4311
|
+
renderer.runners.resolutionChange.add(this);
|
|
3354
4312
|
}
|
|
3355
4313
|
resolutionChange() {
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
continue;
|
|
3360
|
-
const text = gpuText.batchableSprite.renderable;
|
|
3361
|
-
if (text._autoResolution) {
|
|
3362
|
-
text._resolution = this._renderer.resolution;
|
|
3363
|
-
text.onViewUpdate();
|
|
3364
|
-
}
|
|
4314
|
+
const text = this.renderable;
|
|
4315
|
+
if (text._autoResolution) {
|
|
4316
|
+
text.onViewUpdate();
|
|
3365
4317
|
}
|
|
3366
4318
|
}
|
|
4319
|
+
destroy() {
|
|
4320
|
+
this._renderer.canvasText.returnTexture(this.texture);
|
|
4321
|
+
this._renderer = null;
|
|
4322
|
+
}
|
|
4323
|
+
}
|
|
4324
|
+
|
|
4325
|
+
class CanvasTextPipe {
|
|
4326
|
+
constructor(renderer) {
|
|
4327
|
+
this._renderer = renderer;
|
|
4328
|
+
}
|
|
3367
4329
|
validateRenderable(text) {
|
|
3368
|
-
|
|
3369
|
-
const newKey = text._getKey();
|
|
3370
|
-
if (gpuText.currentKey !== newKey) {
|
|
3371
|
-
return true;
|
|
3372
|
-
}
|
|
3373
|
-
return false;
|
|
4330
|
+
return text._didTextUpdate;
|
|
3374
4331
|
}
|
|
3375
4332
|
addRenderable(text, instructionSet) {
|
|
3376
|
-
const
|
|
3377
|
-
const batchableSprite = gpuText.batchableSprite;
|
|
4333
|
+
const batchableText = this._getGpuText(text);
|
|
3378
4334
|
if (text._didTextUpdate) {
|
|
3379
|
-
this.
|
|
4335
|
+
this._updateGpuText(text);
|
|
4336
|
+
text._didTextUpdate = false;
|
|
3380
4337
|
}
|
|
3381
|
-
this._renderer.renderPipes.batch.addToBatch(
|
|
4338
|
+
this._renderer.renderPipes.batch.addToBatch(batchableText, instructionSet);
|
|
3382
4339
|
}
|
|
3383
4340
|
updateRenderable(text) {
|
|
3384
|
-
const
|
|
3385
|
-
|
|
3386
|
-
if (text._didTextUpdate) {
|
|
3387
|
-
this._updateText(text);
|
|
3388
|
-
}
|
|
3389
|
-
batchableSprite._batcher.updateElement(batchableSprite);
|
|
3390
|
-
}
|
|
3391
|
-
destroyRenderable(text) {
|
|
3392
|
-
text.off("destroyed", this._destroyRenderableBound);
|
|
3393
|
-
this._destroyRenderableById(text.uid);
|
|
3394
|
-
}
|
|
3395
|
-
_destroyRenderableById(textUid) {
|
|
3396
|
-
const gpuText = this._gpuText[textUid];
|
|
3397
|
-
this._renderer.canvasText.decreaseReferenceCount(gpuText.currentKey);
|
|
3398
|
-
BigPool.return(gpuText.batchableSprite);
|
|
3399
|
-
this._gpuText[textUid] = null;
|
|
3400
|
-
}
|
|
3401
|
-
_updateText(text) {
|
|
3402
|
-
const newKey = text._getKey();
|
|
3403
|
-
const gpuText = this._getGpuText(text);
|
|
3404
|
-
const batchableSprite = gpuText.batchableSprite;
|
|
3405
|
-
if (gpuText.currentKey !== newKey) {
|
|
3406
|
-
this._updateGpuText(text);
|
|
3407
|
-
}
|
|
3408
|
-
text._didTextUpdate = false;
|
|
3409
|
-
updateTextBounds(batchableSprite, text);
|
|
4341
|
+
const batchableText = this._getGpuText(text);
|
|
4342
|
+
batchableText._batcher.updateElement(batchableText);
|
|
3410
4343
|
}
|
|
3411
4344
|
_updateGpuText(text) {
|
|
3412
|
-
const
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
this._renderer.canvasText.decreaseReferenceCount(gpuText.currentKey);
|
|
4345
|
+
const batchableText = this._getGpuText(text);
|
|
4346
|
+
if (batchableText.texture) {
|
|
4347
|
+
this._renderer.canvasText.returnTexture(batchableText.texture);
|
|
3416
4348
|
}
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
4349
|
+
text._resolution = text._autoResolution ? this._renderer.resolution : text.resolution;
|
|
4350
|
+
batchableText.texture = batchableText.texture = this._renderer.canvasText.getTexture(text);
|
|
4351
|
+
updateTextBounds(batchableText, text);
|
|
3420
4352
|
}
|
|
3421
4353
|
_getGpuText(text) {
|
|
3422
|
-
return
|
|
4354
|
+
return text._gpuData[this._renderer.uid] || this.initGpuText(text);
|
|
3423
4355
|
}
|
|
3424
4356
|
initGpuText(text) {
|
|
3425
|
-
const
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
gpuTextData.batchableSprite.bounds = { minX: 0, maxX: 1, minY: 0, maxY: 0 };
|
|
3433
|
-
gpuTextData.batchableSprite.roundPixels = this._renderer._roundPixels | text._roundPixels;
|
|
3434
|
-
this._gpuText[text.uid] = gpuTextData;
|
|
3435
|
-
text._resolution = text._autoResolution ? this._renderer.resolution : text.resolution;
|
|
3436
|
-
this._updateText(text);
|
|
3437
|
-
text.on("destroyed", this._destroyRenderableBound);
|
|
3438
|
-
return gpuTextData;
|
|
4357
|
+
const batchableText = new BatchableText(this._renderer);
|
|
4358
|
+
batchableText.renderable = text;
|
|
4359
|
+
batchableText.transform = text.groupTransform;
|
|
4360
|
+
batchableText.bounds = { minX: 0, maxX: 1, minY: 0, maxY: 0 };
|
|
4361
|
+
batchableText.roundPixels = this._renderer._roundPixels | text._roundPixels;
|
|
4362
|
+
text._gpuData[this._renderer.uid] = batchableText;
|
|
4363
|
+
return batchableText;
|
|
3439
4364
|
}
|
|
3440
4365
|
destroy() {
|
|
3441
|
-
for (const i in this._gpuText) {
|
|
3442
|
-
this._destroyRenderableById(i);
|
|
3443
|
-
}
|
|
3444
|
-
this._gpuText = null;
|
|
3445
4366
|
this._renderer = null;
|
|
3446
4367
|
}
|
|
3447
4368
|
}
|
|
@@ -3455,119 +4376,53 @@ CanvasTextPipe.extension = {
|
|
|
3455
4376
|
name: "text"
|
|
3456
4377
|
};
|
|
3457
4378
|
|
|
3458
|
-
function checkRow(data, width, y) {
|
|
3459
|
-
for (let x = 0, index = 4 * y * width; x < width; ++x, index += 4) {
|
|
3460
|
-
if (data[index + 3] !== 0)
|
|
3461
|
-
return false;
|
|
3462
|
-
}
|
|
3463
|
-
return true;
|
|
3464
|
-
}
|
|
3465
|
-
function checkColumn(data, width, x, top, bottom) {
|
|
3466
|
-
const stride = 4 * width;
|
|
3467
|
-
for (let y = top, index = top * stride + 4 * x; y <= bottom; ++y, index += stride) {
|
|
3468
|
-
if (data[index + 3] !== 0)
|
|
3469
|
-
return false;
|
|
3470
|
-
}
|
|
3471
|
-
return true;
|
|
3472
|
-
}
|
|
3473
|
-
function getCanvasBoundingBox(canvas, resolution = 1) {
|
|
3474
|
-
const { width, height } = canvas;
|
|
3475
|
-
const context = canvas.getContext("2d", {
|
|
3476
|
-
willReadFrequently: true
|
|
3477
|
-
});
|
|
3478
|
-
if (context === null) {
|
|
3479
|
-
throw new TypeError("Failed to get canvas 2D context");
|
|
3480
|
-
}
|
|
3481
|
-
const imageData = context.getImageData(0, 0, width, height);
|
|
3482
|
-
const data = imageData.data;
|
|
3483
|
-
let left = 0;
|
|
3484
|
-
let top = 0;
|
|
3485
|
-
let right = width - 1;
|
|
3486
|
-
let bottom = height - 1;
|
|
3487
|
-
while (top < height && checkRow(data, width, top))
|
|
3488
|
-
++top;
|
|
3489
|
-
if (top === height)
|
|
3490
|
-
return Rectangle.EMPTY;
|
|
3491
|
-
while (checkRow(data, width, bottom))
|
|
3492
|
-
--bottom;
|
|
3493
|
-
while (checkColumn(data, width, left, top, bottom))
|
|
3494
|
-
++left;
|
|
3495
|
-
while (checkColumn(data, width, right, top, bottom))
|
|
3496
|
-
--right;
|
|
3497
|
-
++right;
|
|
3498
|
-
++bottom;
|
|
3499
|
-
return new Rectangle(left / resolution, top / resolution, (right - left) / resolution, (bottom - top) / resolution);
|
|
3500
|
-
}
|
|
3501
|
-
|
|
3502
4379
|
class CanvasTextSystem {
|
|
3503
4380
|
constructor(_renderer) {
|
|
3504
|
-
this._activeTextures = {};
|
|
3505
4381
|
this._renderer = _renderer;
|
|
3506
4382
|
}
|
|
3507
|
-
|
|
3508
|
-
const measured = CanvasTextMetrics.measureText(text || " ", style);
|
|
3509
|
-
let width = Math.ceil(Math.ceil(Math.max(1, measured.width) + style.padding * 2) * resolution);
|
|
3510
|
-
let height = Math.ceil(Math.ceil(Math.max(1, measured.height) + style.padding * 2) * resolution);
|
|
3511
|
-
width = Math.ceil(width - 1e-6);
|
|
3512
|
-
height = Math.ceil(height - 1e-6);
|
|
3513
|
-
width = nextPow2(width);
|
|
3514
|
-
height = nextPow2(height);
|
|
3515
|
-
return { width, height };
|
|
3516
|
-
}
|
|
3517
|
-
getTexture(options, resolution, style, _textKey) {
|
|
4383
|
+
getTexture(options, _resolution, _style, _textKey) {
|
|
3518
4384
|
if (typeof options === "string") {
|
|
3519
4385
|
deprecation("8.0.0", "CanvasTextSystem.getTexture: Use object TextOptions instead of separate arguments");
|
|
3520
4386
|
options = {
|
|
3521
4387
|
text: options,
|
|
3522
|
-
style,
|
|
3523
|
-
resolution
|
|
4388
|
+
style: _style,
|
|
4389
|
+
resolution: _resolution
|
|
3524
4390
|
};
|
|
3525
4391
|
}
|
|
3526
4392
|
if (!(options.style instanceof TextStyle)) {
|
|
3527
4393
|
options.style = new TextStyle(options.style);
|
|
3528
4394
|
}
|
|
3529
|
-
|
|
3530
|
-
options
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
createTextureAndCanvas(options) {
|
|
3537
|
-
const { text, style } = options;
|
|
4395
|
+
if (!(options.textureStyle instanceof TextureStyle)) {
|
|
4396
|
+
options.textureStyle = new TextureStyle(options.textureStyle);
|
|
4397
|
+
}
|
|
4398
|
+
if (typeof options.text !== "string") {
|
|
4399
|
+
options.text = options.text.toString();
|
|
4400
|
+
}
|
|
4401
|
+
const { text, style, textureStyle } = options;
|
|
3538
4402
|
const resolution = options.resolution ?? this._renderer.resolution;
|
|
3539
|
-
const
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
4403
|
+
const { frame, canvasAndContext } = CanvasTextGenerator.getCanvasAndContext({
|
|
4404
|
+
text,
|
|
4405
|
+
style,
|
|
4406
|
+
resolution
|
|
4407
|
+
});
|
|
4408
|
+
const texture = getPo2TextureFromSource(canvasAndContext.canvas, frame.width, frame.height, resolution);
|
|
4409
|
+
if (textureStyle)
|
|
4410
|
+
texture.source.style = textureStyle;
|
|
3546
4411
|
if (style.trim) {
|
|
3547
|
-
|
|
3548
|
-
texture.frame.copyFrom(
|
|
4412
|
+
frame.pad(style.padding);
|
|
4413
|
+
texture.frame.copyFrom(frame);
|
|
3549
4414
|
texture.updateUvs();
|
|
3550
4415
|
}
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
if (this._activeTextures[textKey]) {
|
|
3557
|
-
this._increaseReferenceCount(textKey);
|
|
3558
|
-
return this._activeTextures[textKey].texture;
|
|
4416
|
+
if (style.filters) {
|
|
4417
|
+
const filteredTexture = this._applyFilters(texture, style.filters);
|
|
4418
|
+
this.returnTexture(texture);
|
|
4419
|
+
CanvasTextGenerator.returnCanvasAndContext(canvasAndContext);
|
|
4420
|
+
return filteredTexture;
|
|
3559
4421
|
}
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
canvasAndContext,
|
|
3563
|
-
texture,
|
|
3564
|
-
usageCount: 1
|
|
3565
|
-
};
|
|
4422
|
+
this._renderer.texture.initSource(texture._source);
|
|
4423
|
+
CanvasTextGenerator.returnCanvasAndContext(canvasAndContext);
|
|
3566
4424
|
return texture;
|
|
3567
4425
|
}
|
|
3568
|
-
_increaseReferenceCount(textKey) {
|
|
3569
|
-
this._activeTextures[textKey].usageCount++;
|
|
3570
|
-
}
|
|
3571
4426
|
/**
|
|
3572
4427
|
* Returns a texture that was created wit the above `getTexture` function.
|
|
3573
4428
|
* Handy if you are done with a texture and want to return it to the pool.
|
|
@@ -3578,168 +4433,39 @@ class CanvasTextSystem {
|
|
|
3578
4433
|
source.resource = null;
|
|
3579
4434
|
source.uploadMethodId = "unknown";
|
|
3580
4435
|
source.alphaMode = "no-premultiply-alpha";
|
|
3581
|
-
TexturePool.returnTexture(texture);
|
|
3582
|
-
}
|
|
3583
|
-
decreaseReferenceCount(textKey) {
|
|
3584
|
-
const activeTexture = this._activeTextures[textKey];
|
|
3585
|
-
activeTexture.usageCount--;
|
|
3586
|
-
if (activeTexture.usageCount === 0) {
|
|
3587
|
-
CanvasPool.returnCanvasAndContext(activeTexture.canvasAndContext);
|
|
3588
|
-
this.returnTexture(activeTexture.texture);
|
|
3589
|
-
this._activeTextures[textKey] = null;
|
|
3590
|
-
}
|
|
3591
|
-
}
|
|
3592
|
-
getReferenceCount(textKey) {
|
|
3593
|
-
return this._activeTextures[textKey].usageCount;
|
|
4436
|
+
TexturePool.returnTexture(texture, true);
|
|
3594
4437
|
}
|
|
3595
4438
|
/**
|
|
3596
4439
|
* Renders text to its canvas, and updates its texture.
|
|
3597
|
-
*
|
|
3598
|
-
* By default this is used internally to ensure the texture is correct before rendering,
|
|
3599
|
-
* but it can be used called externally, for example from this class to 'pre-generate' the texture from a piece of text,
|
|
3600
|
-
* and then shared across multiple Sprites.
|
|
3601
|
-
* @param text
|
|
3602
|
-
* @param style
|
|
3603
|
-
* @param resolution
|
|
3604
|
-
* @param canvasAndContext
|
|
4440
|
+
* @deprecated since 8.10.0
|
|
3605
4441
|
*/
|
|
3606
|
-
renderTextToCanvas(
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
const lineHeight = measured.lineHeight;
|
|
3612
|
-
const lineWidths = measured.lineWidths;
|
|
3613
|
-
const maxLineWidth = measured.maxLineWidth;
|
|
3614
|
-
const fontProperties = measured.fontProperties;
|
|
3615
|
-
const height = canvas.height;
|
|
3616
|
-
context.resetTransform();
|
|
3617
|
-
context.scale(resolution, resolution);
|
|
3618
|
-
context.textBaseline = style.textBaseline;
|
|
3619
|
-
if (style._stroke?.width) {
|
|
3620
|
-
const strokeStyle = style._stroke;
|
|
3621
|
-
context.lineWidth = strokeStyle.width;
|
|
3622
|
-
context.miterLimit = strokeStyle.miterLimit;
|
|
3623
|
-
context.lineJoin = strokeStyle.join;
|
|
3624
|
-
context.lineCap = strokeStyle.cap;
|
|
3625
|
-
}
|
|
3626
|
-
context.font = font;
|
|
3627
|
-
let linePositionX;
|
|
3628
|
-
let linePositionY;
|
|
3629
|
-
const passesCount = style.dropShadow ? 2 : 1;
|
|
3630
|
-
for (let i = 0; i < passesCount; ++i) {
|
|
3631
|
-
const isShadowPass = style.dropShadow && i === 0;
|
|
3632
|
-
const dsOffsetText = isShadowPass ? Math.ceil(Math.max(1, height) + style.padding * 2) : 0;
|
|
3633
|
-
const dsOffsetShadow = dsOffsetText * resolution;
|
|
3634
|
-
if (isShadowPass) {
|
|
3635
|
-
context.fillStyle = "black";
|
|
3636
|
-
context.strokeStyle = "black";
|
|
3637
|
-
const shadowOptions = style.dropShadow;
|
|
3638
|
-
const dropShadowColor = shadowOptions.color;
|
|
3639
|
-
const dropShadowAlpha = shadowOptions.alpha;
|
|
3640
|
-
context.shadowColor = Color.shared.setValue(dropShadowColor).setAlpha(dropShadowAlpha).toRgbaString();
|
|
3641
|
-
const dropShadowBlur = shadowOptions.blur * resolution;
|
|
3642
|
-
const dropShadowDistance = shadowOptions.distance * resolution;
|
|
3643
|
-
context.shadowBlur = dropShadowBlur;
|
|
3644
|
-
context.shadowOffsetX = Math.cos(shadowOptions.angle) * dropShadowDistance;
|
|
3645
|
-
context.shadowOffsetY = Math.sin(shadowOptions.angle) * dropShadowDistance + dsOffsetShadow;
|
|
3646
|
-
} else {
|
|
3647
|
-
context.fillStyle = style._fill ? getCanvasFillStyle(style._fill, context, measured) : null;
|
|
3648
|
-
if (style._stroke?.width) {
|
|
3649
|
-
const padding = style._stroke.width * style._stroke.alignment;
|
|
3650
|
-
context.strokeStyle = getCanvasFillStyle(style._stroke, context, measured, padding);
|
|
3651
|
-
}
|
|
3652
|
-
context.shadowColor = "black";
|
|
3653
|
-
}
|
|
3654
|
-
let linePositionYShift = (lineHeight - fontProperties.fontSize) / 2;
|
|
3655
|
-
if (lineHeight - fontProperties.fontSize < 0) {
|
|
3656
|
-
linePositionYShift = 0;
|
|
3657
|
-
}
|
|
3658
|
-
const strokeWidth = style._stroke?.width ?? 0;
|
|
3659
|
-
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
3660
|
-
linePositionX = strokeWidth / 2;
|
|
3661
|
-
linePositionY = strokeWidth / 2 + i2 * lineHeight + fontProperties.ascent + linePositionYShift;
|
|
3662
|
-
if (style.align === "right") {
|
|
3663
|
-
linePositionX += maxLineWidth - lineWidths[i2];
|
|
3664
|
-
} else if (style.align === "center") {
|
|
3665
|
-
linePositionX += (maxLineWidth - lineWidths[i2]) / 2;
|
|
3666
|
-
}
|
|
3667
|
-
if (style._stroke?.width) {
|
|
3668
|
-
this._drawLetterSpacing(
|
|
3669
|
-
lines[i2],
|
|
3670
|
-
style,
|
|
3671
|
-
canvasAndContext,
|
|
3672
|
-
linePositionX + style.padding,
|
|
3673
|
-
linePositionY + style.padding - dsOffsetText,
|
|
3674
|
-
true
|
|
3675
|
-
);
|
|
3676
|
-
}
|
|
3677
|
-
if (style._fill !== void 0) {
|
|
3678
|
-
this._drawLetterSpacing(
|
|
3679
|
-
lines[i2],
|
|
3680
|
-
style,
|
|
3681
|
-
canvasAndContext,
|
|
3682
|
-
linePositionX + style.padding,
|
|
3683
|
-
linePositionY + style.padding - dsOffsetText
|
|
3684
|
-
);
|
|
3685
|
-
}
|
|
3686
|
-
}
|
|
3687
|
-
}
|
|
4442
|
+
renderTextToCanvas() {
|
|
4443
|
+
deprecation(
|
|
4444
|
+
"8.10.0",
|
|
4445
|
+
"CanvasTextSystem.renderTextToCanvas: no longer supported, use CanvasTextSystem.getTexture instead"
|
|
4446
|
+
);
|
|
3688
4447
|
}
|
|
3689
4448
|
/**
|
|
3690
|
-
*
|
|
3691
|
-
*
|
|
3692
|
-
*
|
|
3693
|
-
*
|
|
3694
|
-
*
|
|
3695
|
-
* @param
|
|
3696
|
-
* @param
|
|
3697
|
-
*
|
|
4449
|
+
* Applies the specified filters to the given texture.
|
|
4450
|
+
*
|
|
4451
|
+
* This method takes a texture and a list of filters, applies the filters to the texture,
|
|
4452
|
+
* and returns the resulting texture. It also ensures that the alpha mode of the resulting
|
|
4453
|
+
* texture is set to 'premultiplied-alpha'.
|
|
4454
|
+
* @param {Texture} texture - The texture to which the filters will be applied.
|
|
4455
|
+
* @param {Filter[]} filters - The filters to apply to the texture.
|
|
4456
|
+
* @returns {Texture} The resulting texture after all filters have been applied.
|
|
3698
4457
|
*/
|
|
3699
|
-
|
|
3700
|
-
const
|
|
3701
|
-
const
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
useExperimentalLetterSpacing = true;
|
|
3708
|
-
} else {
|
|
3709
|
-
context.letterSpacing = "0px";
|
|
3710
|
-
context.textLetterSpacing = "0px";
|
|
3711
|
-
}
|
|
3712
|
-
}
|
|
3713
|
-
if (letterSpacing === 0 || useExperimentalLetterSpacing) {
|
|
3714
|
-
if (isStroke) {
|
|
3715
|
-
context.strokeText(text, x, y);
|
|
3716
|
-
} else {
|
|
3717
|
-
context.fillText(text, x, y);
|
|
3718
|
-
}
|
|
3719
|
-
return;
|
|
3720
|
-
}
|
|
3721
|
-
let currentPosition = x;
|
|
3722
|
-
const stringArray = CanvasTextMetrics.graphemeSegmenter(text);
|
|
3723
|
-
let previousWidth = context.measureText(text).width;
|
|
3724
|
-
let currentWidth = 0;
|
|
3725
|
-
for (let i = 0; i < stringArray.length; ++i) {
|
|
3726
|
-
const currentChar = stringArray[i];
|
|
3727
|
-
if (isStroke) {
|
|
3728
|
-
context.strokeText(currentChar, currentPosition, y);
|
|
3729
|
-
} else {
|
|
3730
|
-
context.fillText(currentChar, currentPosition, y);
|
|
3731
|
-
}
|
|
3732
|
-
let textStr = "";
|
|
3733
|
-
for (let j = i + 1; j < stringArray.length; ++j) {
|
|
3734
|
-
textStr += stringArray[j];
|
|
3735
|
-
}
|
|
3736
|
-
currentWidth = context.measureText(textStr).width;
|
|
3737
|
-
currentPosition += previousWidth - currentWidth + letterSpacing;
|
|
3738
|
-
previousWidth = currentWidth;
|
|
3739
|
-
}
|
|
4458
|
+
_applyFilters(texture, filters) {
|
|
4459
|
+
const currentRenderTarget = this._renderer.renderTarget.renderTarget;
|
|
4460
|
+
const resultTexture = this._renderer.filter.generateFilteredTexture({
|
|
4461
|
+
texture,
|
|
4462
|
+
filters
|
|
4463
|
+
});
|
|
4464
|
+
this._renderer.renderTarget.bind(currentRenderTarget, false);
|
|
4465
|
+
return resultTexture;
|
|
3740
4466
|
}
|
|
3741
4467
|
destroy() {
|
|
3742
|
-
this.
|
|
4468
|
+
this._renderer = null;
|
|
3743
4469
|
}
|
|
3744
4470
|
}
|
|
3745
4471
|
/** @ignore */
|