@vunk/plus 1.3.11 → 1.3.13
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/SharedSystems.mjs +3195 -0
- package/components/WebGLRenderer.mjs +3617 -0
- package/components/WebGPURenderer.mjs +2103 -0
- package/components/_commonjsHelpers.mjs +7 -0
- package/components/agent-chat-provider/src/types.d.ts +2 -1
- package/components/broadcasting-markdown/index.d.ts +2 -0
- package/components/broadcasting-markdown/index.mjs +238 -306
- package/components/broadcasting-markdown/src/const.d.ts +2 -7
- package/components/broadcasting-markdown/src/ctx.d.ts +28 -15
- package/components/broadcasting-markdown/src/howler-speech.vue.d.ts +24 -44
- package/components/broadcasting-markdown/src/index.vue.d.ts +31 -167
- package/components/broadcasting-markdown/src/paragraph.vue.d.ts +1 -14
- package/components/broadcasting-markdown/src/types.d.ts +10 -2
- package/components/broadcasting-markdown/src/use.d.ts +18 -0
- package/components/browserAll.mjs +3187 -0
- package/components/bubble-list/index.mjs +2 -1
- package/components/bubble-list/src/index.vue.d.ts +27 -161
- package/components/bubble-templates/src/index.vue.d.ts +27 -161
- package/components/bubble-templates/src/types.d.ts +6 -0
- package/components/chat-independent/index.mjs +3 -0
- package/components/chat-independent/src/index.vue.d.ts +27 -161
- package/components/colorToUniform.mjs +266 -0
- package/components/index2.mjs +21220 -0
- package/components/init.mjs +3779 -0
- package/components/pixi-frame/index.css +4 -0
- package/components/pixi-frame/index.d.ts +8 -0
- package/components/pixi-frame/index.mjs +6 -0
- package/components/pixi-frame/src/core.vue.d.ts +122 -0
- package/components/pixi-frame/src/ctx.d.ts +40 -0
- package/components/pixi-frame/src/index.vue.d.ts +215 -0
- package/components/pixi-frame/src/provider.vue.d.ts +2 -0
- package/components/pixi-frame/src/types.d.ts +1 -0
- package/components/pixi-frame/src/use.d.ts +3 -0
- package/components/pixi-frame/src/view.vue.d.ts +25 -0
- package/components/tables-v1/src/ctx.d.ts +1 -1
- package/components/tables-v1/src/el-ctx.d.ts +1 -1
- package/components/tables-v1-checkbox/src/ctx.d.ts +1 -1
- package/components/tables-v1-checkbox/src/index.vue.d.ts +1 -1
- package/components/webworkerAll.mjs +8 -0
- package/index.css +5 -4
- package/package.json +8 -4
- package/components/broadcasting-markdown/index.css +0 -3
- package/components/broadcasting-markdown/src/custom-speech.vue.d.ts +0 -117
- package/components/broadcasting-markdown/src/web-speech.vue.d.ts +0 -110
|
@@ -0,0 +1,3779 @@
|
|
|
1
|
+
import { E as ExtensionType, U as UPDATE_PRIORITY, T as Ticker, k as UniformGroup, c as BindGroup, w as warn, H as TexturePool, t as Texture, P as Point, a3 as Geometry, R as RendererType, F as Bounds, M as Matrix, a9 as ViewContainer, aa as GraphicsContext, a0 as deprecation, a1 as v8_0_0, b as Buffer, B as BufferUsage, Z as Color, ab as TextStyle, ac as generateTextStyleKey, y as BigPool, ad as BatchableGraphics, ae as getAdjustedBlendModeBlend, K as getAttributeInfoFromFormat, af as ViewableBuffer, s as Shader, u as GlProgram, G as GpuProgram, ag as TextureStyle, m as compileHighShaderGpuProgram, p as roundPixelsBit, a4 as compileHighShaderGlProgram, a7 as roundPixelsBitGl, q as getMaxTexturesPerBatch, n as colorBit, o as generateTextureBatchBit, a5 as colorBitGl, a6 as generateTextureBatchBitGl, a8 as getBatchSamplersUniformGroup, ah as BitmapFontManager, ai as getBitmapTextLayout, aj as Cache, ak as updateQuadBounds, D as DOMAdapter, l as CanvasPool, O as Rectangle, al as CanvasTextMetrics, am as nextPow2, an as fontStringFromTextStyle, ao as getCanvasFillStyle, e as extensions, ap as GraphicsContextSystem } from './index2.mjs';
|
|
2
|
+
import { S as State, c as color32BitToUniform, a as localUniformBit, b as localUniformBitGl, B as BatchableSprite } from './colorToUniform.mjs';
|
|
3
|
+
|
|
4
|
+
class ResizePlugin {
|
|
5
|
+
/**
|
|
6
|
+
* Initialize the plugin with scope of application instance
|
|
7
|
+
* @static
|
|
8
|
+
* @private
|
|
9
|
+
* @param {object} [options] - See application options
|
|
10
|
+
*/
|
|
11
|
+
static init(options) {
|
|
12
|
+
Object.defineProperty(
|
|
13
|
+
this,
|
|
14
|
+
"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
|
+
{
|
|
23
|
+
set(dom) {
|
|
24
|
+
globalThis.removeEventListener("resize", this.queueResize);
|
|
25
|
+
this._resizeTo = dom;
|
|
26
|
+
if (dom) {
|
|
27
|
+
globalThis.addEventListener("resize", this.queueResize);
|
|
28
|
+
this.resize();
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
get() {
|
|
32
|
+
return this._resizeTo;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
this.queueResize = () => {
|
|
37
|
+
if (!this._resizeTo) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
this._cancelResize();
|
|
41
|
+
this._resizeId = requestAnimationFrame(() => this.resize());
|
|
42
|
+
};
|
|
43
|
+
this._cancelResize = () => {
|
|
44
|
+
if (this._resizeId) {
|
|
45
|
+
cancelAnimationFrame(this._resizeId);
|
|
46
|
+
this._resizeId = null;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
this.resize = () => {
|
|
50
|
+
if (!this._resizeTo) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
this._cancelResize();
|
|
54
|
+
let width;
|
|
55
|
+
let height;
|
|
56
|
+
if (this._resizeTo === globalThis.window) {
|
|
57
|
+
width = globalThis.innerWidth;
|
|
58
|
+
height = globalThis.innerHeight;
|
|
59
|
+
} else {
|
|
60
|
+
const { clientWidth, clientHeight } = this._resizeTo;
|
|
61
|
+
width = clientWidth;
|
|
62
|
+
height = clientHeight;
|
|
63
|
+
}
|
|
64
|
+
this.renderer.resize(width, height);
|
|
65
|
+
this.render();
|
|
66
|
+
};
|
|
67
|
+
this._resizeId = null;
|
|
68
|
+
this._resizeTo = null;
|
|
69
|
+
this.resizeTo = options.resizeTo || null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Clean up the ticker, scoped to application
|
|
73
|
+
* @static
|
|
74
|
+
* @private
|
|
75
|
+
*/
|
|
76
|
+
static destroy() {
|
|
77
|
+
globalThis.removeEventListener("resize", this.queueResize);
|
|
78
|
+
this._cancelResize();
|
|
79
|
+
this._cancelResize = null;
|
|
80
|
+
this.queueResize = null;
|
|
81
|
+
this.resizeTo = null;
|
|
82
|
+
this.resize = null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** @ignore */
|
|
86
|
+
ResizePlugin.extension = ExtensionType.Application;
|
|
87
|
+
|
|
88
|
+
class TickerPlugin {
|
|
89
|
+
/**
|
|
90
|
+
* Initialize the plugin with scope of application instance
|
|
91
|
+
* @static
|
|
92
|
+
* @private
|
|
93
|
+
* @param {object} [options] - See application options
|
|
94
|
+
*/
|
|
95
|
+
static init(options) {
|
|
96
|
+
options = Object.assign({
|
|
97
|
+
autoStart: true,
|
|
98
|
+
sharedTicker: false
|
|
99
|
+
}, options);
|
|
100
|
+
Object.defineProperty(
|
|
101
|
+
this,
|
|
102
|
+
"ticker",
|
|
103
|
+
{
|
|
104
|
+
set(ticker) {
|
|
105
|
+
if (this._ticker) {
|
|
106
|
+
this._ticker.remove(this.render, this);
|
|
107
|
+
}
|
|
108
|
+
this._ticker = ticker;
|
|
109
|
+
if (ticker) {
|
|
110
|
+
ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
get() {
|
|
114
|
+
return this._ticker;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
this.stop = () => {
|
|
119
|
+
this._ticker.stop();
|
|
120
|
+
};
|
|
121
|
+
this.start = () => {
|
|
122
|
+
this._ticker.start();
|
|
123
|
+
};
|
|
124
|
+
this._ticker = null;
|
|
125
|
+
this.ticker = options.sharedTicker ? Ticker.shared : new Ticker();
|
|
126
|
+
if (options.autoStart) {
|
|
127
|
+
this.start();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Clean up the ticker, scoped to application.
|
|
132
|
+
* @static
|
|
133
|
+
* @private
|
|
134
|
+
*/
|
|
135
|
+
static destroy() {
|
|
136
|
+
if (this._ticker) {
|
|
137
|
+
const oldTicker = this._ticker;
|
|
138
|
+
this.ticker = null;
|
|
139
|
+
oldTicker.destroy();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/** @ignore */
|
|
144
|
+
TickerPlugin.extension = ExtensionType.Application;
|
|
145
|
+
|
|
146
|
+
class FilterPipe {
|
|
147
|
+
constructor(renderer) {
|
|
148
|
+
this._renderer = renderer;
|
|
149
|
+
}
|
|
150
|
+
push(filterEffect, container, instructionSet) {
|
|
151
|
+
const renderPipes = this._renderer.renderPipes;
|
|
152
|
+
renderPipes.batch.break(instructionSet);
|
|
153
|
+
instructionSet.add({
|
|
154
|
+
renderPipeId: "filter",
|
|
155
|
+
canBundle: false,
|
|
156
|
+
action: "pushFilter",
|
|
157
|
+
container,
|
|
158
|
+
filterEffect
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
pop(_filterEffect, _container, instructionSet) {
|
|
162
|
+
this._renderer.renderPipes.batch.break(instructionSet);
|
|
163
|
+
instructionSet.add({
|
|
164
|
+
renderPipeId: "filter",
|
|
165
|
+
action: "popFilter",
|
|
166
|
+
canBundle: false
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
execute(instruction) {
|
|
170
|
+
if (instruction.action === "pushFilter") {
|
|
171
|
+
this._renderer.filter.push(instruction);
|
|
172
|
+
} else if (instruction.action === "popFilter") {
|
|
173
|
+
this._renderer.filter.pop();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
destroy() {
|
|
177
|
+
this._renderer = null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
FilterPipe.extension = {
|
|
181
|
+
type: [
|
|
182
|
+
ExtensionType.WebGLPipes,
|
|
183
|
+
ExtensionType.WebGPUPipes,
|
|
184
|
+
ExtensionType.CanvasPipes
|
|
185
|
+
],
|
|
186
|
+
name: "filter"
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
function getGlobalRenderableBounds(renderables, bounds) {
|
|
190
|
+
bounds.clear();
|
|
191
|
+
const tempMatrix = bounds.matrix;
|
|
192
|
+
for (let i = 0; i < renderables.length; i++) {
|
|
193
|
+
const renderable = renderables[i];
|
|
194
|
+
if (renderable.globalDisplayStatus < 7) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
bounds.matrix = renderable.worldTransform;
|
|
198
|
+
bounds.addBounds(renderable.bounds);
|
|
199
|
+
}
|
|
200
|
+
bounds.matrix = tempMatrix;
|
|
201
|
+
return bounds;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const quadGeometry = new Geometry({
|
|
205
|
+
attributes: {
|
|
206
|
+
aPosition: {
|
|
207
|
+
buffer: new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]),
|
|
208
|
+
format: "float32x2",
|
|
209
|
+
stride: 2 * 4,
|
|
210
|
+
offset: 0
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
indexBuffer: new Uint32Array([0, 1, 2, 0, 2, 3])
|
|
214
|
+
});
|
|
215
|
+
class FilterSystem {
|
|
216
|
+
constructor(renderer) {
|
|
217
|
+
this._filterStackIndex = 0;
|
|
218
|
+
this._filterStack = [];
|
|
219
|
+
this._filterGlobalUniforms = new UniformGroup({
|
|
220
|
+
uInputSize: { value: new Float32Array(4), type: "vec4<f32>" },
|
|
221
|
+
uInputPixel: { value: new Float32Array(4), type: "vec4<f32>" },
|
|
222
|
+
uInputClamp: { value: new Float32Array(4), type: "vec4<f32>" },
|
|
223
|
+
uOutputFrame: { value: new Float32Array(4), type: "vec4<f32>" },
|
|
224
|
+
uGlobalFrame: { value: new Float32Array(4), type: "vec4<f32>" },
|
|
225
|
+
uOutputTexture: { value: new Float32Array(4), type: "vec4<f32>" }
|
|
226
|
+
});
|
|
227
|
+
this._globalFilterBindGroup = new BindGroup({});
|
|
228
|
+
this.renderer = renderer;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* The back texture of the currently active filter. Requires the filter to have `blendRequired` set to true.
|
|
232
|
+
* @readonly
|
|
233
|
+
*/
|
|
234
|
+
get activeBackTexture() {
|
|
235
|
+
return this._activeFilterData?.backTexture;
|
|
236
|
+
}
|
|
237
|
+
push(instruction) {
|
|
238
|
+
const renderer = this.renderer;
|
|
239
|
+
const filters = instruction.filterEffect.filters;
|
|
240
|
+
if (!this._filterStack[this._filterStackIndex]) {
|
|
241
|
+
this._filterStack[this._filterStackIndex] = this._getFilterData();
|
|
242
|
+
}
|
|
243
|
+
const filterData = this._filterStack[this._filterStackIndex];
|
|
244
|
+
this._filterStackIndex++;
|
|
245
|
+
if (filters.length === 0) {
|
|
246
|
+
filterData.skip = true;
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const bounds = filterData.bounds;
|
|
250
|
+
if (instruction.renderables) {
|
|
251
|
+
getGlobalRenderableBounds(instruction.renderables, bounds);
|
|
252
|
+
} else if (instruction.filterEffect.filterArea) {
|
|
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;
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (clipToViewport) {
|
|
303
|
+
const viewPort = renderer.renderTarget.rootViewPort;
|
|
304
|
+
const rootResolution = renderer.renderTarget.renderTarget.resolution;
|
|
305
|
+
bounds.fitBounds(0, viewPort.width / rootResolution, 0, viewPort.height / rootResolution);
|
|
306
|
+
}
|
|
307
|
+
bounds.scale(resolution).ceil().scale(1 / resolution).pad(padding | 0);
|
|
308
|
+
if (!bounds.isPositive) {
|
|
309
|
+
filterData.skip = true;
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
filterData.skip = false;
|
|
313
|
+
filterData.bounds = bounds;
|
|
314
|
+
filterData.blendRequired = blendRequired;
|
|
315
|
+
filterData.container = instruction.container;
|
|
316
|
+
filterData.filterEffect = instruction.filterEffect;
|
|
317
|
+
filterData.previousRenderSurface = renderer.renderTarget.renderSurface;
|
|
318
|
+
filterData.inputTexture = TexturePool.getOptimalTexture(
|
|
319
|
+
bounds.width,
|
|
320
|
+
bounds.height,
|
|
321
|
+
resolution,
|
|
322
|
+
antialias
|
|
323
|
+
);
|
|
324
|
+
renderer.renderTarget.bind(filterData.inputTexture, true);
|
|
325
|
+
renderer.globalUniforms.push({
|
|
326
|
+
offset: bounds
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
pop() {
|
|
330
|
+
const renderer = this.renderer;
|
|
331
|
+
this._filterStackIndex--;
|
|
332
|
+
const filterData = this._filterStack[this._filterStackIndex];
|
|
333
|
+
if (filterData.skip) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
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
|
+
renderer.globalUniforms.pop();
|
|
351
|
+
if (filters.length === 1) {
|
|
352
|
+
filters[0].apply(this, inputTexture, filterData.previousRenderSurface, false);
|
|
353
|
+
TexturePool.returnTexture(inputTexture);
|
|
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
|
+
}
|
|
374
|
+
if (filterData.blendRequired) {
|
|
375
|
+
TexturePool.returnTexture(backTexture);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
getBackTexture(lastRenderSurface, bounds, previousBounds) {
|
|
379
|
+
const backgroundResolution = lastRenderSurface.colorTexture.source._resolution;
|
|
380
|
+
const backTexture = TexturePool.getOptimalTexture(
|
|
381
|
+
bounds.width,
|
|
382
|
+
bounds.height,
|
|
383
|
+
backgroundResolution,
|
|
384
|
+
false
|
|
385
|
+
);
|
|
386
|
+
let x = bounds.minX;
|
|
387
|
+
let y = bounds.minY;
|
|
388
|
+
if (previousBounds) {
|
|
389
|
+
x -= previousBounds.minX;
|
|
390
|
+
y -= previousBounds.minY;
|
|
391
|
+
}
|
|
392
|
+
x = Math.floor(x * backgroundResolution);
|
|
393
|
+
y = Math.floor(y * backgroundResolution);
|
|
394
|
+
const width = Math.ceil(bounds.width * backgroundResolution);
|
|
395
|
+
const height = Math.ceil(bounds.height * backgroundResolution);
|
|
396
|
+
this.renderer.renderTarget.copyToTexture(
|
|
397
|
+
lastRenderSurface,
|
|
398
|
+
backTexture,
|
|
399
|
+
{ x, y },
|
|
400
|
+
{ width, height },
|
|
401
|
+
{ x: 0, y: 0 }
|
|
402
|
+
);
|
|
403
|
+
return backTexture;
|
|
404
|
+
}
|
|
405
|
+
applyFilter(filter, input, output, clear) {
|
|
406
|
+
const renderer = this.renderer;
|
|
407
|
+
const filterData = this._filterStack[this._filterStackIndex];
|
|
408
|
+
const bounds = filterData.bounds;
|
|
409
|
+
const offset = Point.shared;
|
|
410
|
+
const previousRenderSurface = filterData.previousRenderSurface;
|
|
411
|
+
const isFinalTarget = previousRenderSurface === output;
|
|
412
|
+
let resolution = this.renderer.renderTarget.rootRenderTarget.colorTexture.source._resolution;
|
|
413
|
+
let currentIndex = this._filterStackIndex - 1;
|
|
414
|
+
while (currentIndex > 0 && this._filterStack[currentIndex].skip) {
|
|
415
|
+
--currentIndex;
|
|
416
|
+
}
|
|
417
|
+
if (currentIndex > 0) {
|
|
418
|
+
resolution = this._filterStack[currentIndex].inputTexture.source._resolution;
|
|
419
|
+
}
|
|
420
|
+
const filterUniforms = this._filterGlobalUniforms;
|
|
421
|
+
const uniforms = filterUniforms.uniforms;
|
|
422
|
+
const outputFrame = uniforms.uOutputFrame;
|
|
423
|
+
const inputSize = uniforms.uInputSize;
|
|
424
|
+
const inputPixel = uniforms.uInputPixel;
|
|
425
|
+
const inputClamp = uniforms.uInputClamp;
|
|
426
|
+
const globalFrame = uniforms.uGlobalFrame;
|
|
427
|
+
const outputTexture = uniforms.uOutputTexture;
|
|
428
|
+
if (isFinalTarget) {
|
|
429
|
+
let lastIndex = this._filterStackIndex;
|
|
430
|
+
while (lastIndex > 0) {
|
|
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;
|
|
441
|
+
} else {
|
|
442
|
+
outputFrame[0] = 0;
|
|
443
|
+
outputFrame[1] = 0;
|
|
444
|
+
}
|
|
445
|
+
outputFrame[2] = input.frame.width;
|
|
446
|
+
outputFrame[3] = input.frame.height;
|
|
447
|
+
inputSize[0] = input.source.width;
|
|
448
|
+
inputSize[1] = input.source.height;
|
|
449
|
+
inputSize[2] = 1 / inputSize[0];
|
|
450
|
+
inputSize[3] = 1 / inputSize[1];
|
|
451
|
+
inputPixel[0] = input.source.pixelWidth;
|
|
452
|
+
inputPixel[1] = input.source.pixelHeight;
|
|
453
|
+
inputPixel[2] = 1 / inputPixel[0];
|
|
454
|
+
inputPixel[3] = 1 / inputPixel[1];
|
|
455
|
+
inputClamp[0] = 0.5 * inputPixel[2];
|
|
456
|
+
inputClamp[1] = 0.5 * inputPixel[3];
|
|
457
|
+
inputClamp[2] = input.frame.width * inputSize[2] - 0.5 * inputPixel[2];
|
|
458
|
+
inputClamp[3] = input.frame.height * inputSize[3] - 0.5 * inputPixel[3];
|
|
459
|
+
const rootTexture = this.renderer.renderTarget.rootRenderTarget.colorTexture;
|
|
460
|
+
globalFrame[0] = offset.x * resolution;
|
|
461
|
+
globalFrame[1] = offset.y * resolution;
|
|
462
|
+
globalFrame[2] = rootTexture.source.width * resolution;
|
|
463
|
+
globalFrame[3] = rootTexture.source.height * resolution;
|
|
464
|
+
const renderTarget = this.renderer.renderTarget.getRenderTarget(output);
|
|
465
|
+
renderer.renderTarget.bind(output, !!clear);
|
|
466
|
+
if (output instanceof Texture) {
|
|
467
|
+
outputTexture[0] = output.frame.width;
|
|
468
|
+
outputTexture[1] = output.frame.height;
|
|
469
|
+
} else {
|
|
470
|
+
outputTexture[0] = renderTarget.width;
|
|
471
|
+
outputTexture[1] = renderTarget.height;
|
|
472
|
+
}
|
|
473
|
+
outputTexture[2] = renderTarget.isRoot ? -1 : 1;
|
|
474
|
+
filterUniforms.update();
|
|
475
|
+
if (renderer.renderPipes.uniformBatch) {
|
|
476
|
+
const batchUniforms = renderer.renderPipes.uniformBatch.getUboResource(filterUniforms);
|
|
477
|
+
this._globalFilterBindGroup.setResource(batchUniforms, 0);
|
|
478
|
+
} else {
|
|
479
|
+
this._globalFilterBindGroup.setResource(filterUniforms, 0);
|
|
480
|
+
}
|
|
481
|
+
this._globalFilterBindGroup.setResource(input.source, 1);
|
|
482
|
+
this._globalFilterBindGroup.setResource(input.source.style, 2);
|
|
483
|
+
filter.groups[0] = this._globalFilterBindGroup;
|
|
484
|
+
renderer.encoder.draw({
|
|
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();
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
_getFilterData() {
|
|
495
|
+
return {
|
|
496
|
+
skip: false,
|
|
497
|
+
inputTexture: null,
|
|
498
|
+
bounds: new Bounds(),
|
|
499
|
+
container: null,
|
|
500
|
+
filterEffect: null,
|
|
501
|
+
blendRequired: false,
|
|
502
|
+
previousRenderSurface: null
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Multiply _input normalized coordinates_ to this matrix to get _sprite texture normalized coordinates_.
|
|
507
|
+
*
|
|
508
|
+
* Use `outputMatrix * vTextureCoord` in the shader.
|
|
509
|
+
* @param outputMatrix - The matrix to output to.
|
|
510
|
+
* @param {Sprite} sprite - The sprite to map to.
|
|
511
|
+
* @returns The mapped matrix.
|
|
512
|
+
*/
|
|
513
|
+
calculateSpriteMatrix(outputMatrix, sprite) {
|
|
514
|
+
const data = this._activeFilterData;
|
|
515
|
+
const mappedMatrix = outputMatrix.set(
|
|
516
|
+
data.inputTexture._source.width,
|
|
517
|
+
0,
|
|
518
|
+
0,
|
|
519
|
+
data.inputTexture._source.height,
|
|
520
|
+
data.bounds.minX,
|
|
521
|
+
data.bounds.minY
|
|
522
|
+
);
|
|
523
|
+
const worldTransform = sprite.worldTransform.copyTo(Matrix.shared);
|
|
524
|
+
const renderGroup = sprite.renderGroup || sprite.parentRenderGroup;
|
|
525
|
+
if (renderGroup && renderGroup.cacheToLocalTransform) {
|
|
526
|
+
worldTransform.prepend(renderGroup.cacheToLocalTransform);
|
|
527
|
+
}
|
|
528
|
+
worldTransform.invert();
|
|
529
|
+
mappedMatrix.prepend(worldTransform);
|
|
530
|
+
mappedMatrix.scale(
|
|
531
|
+
1 / sprite.texture.frame.width,
|
|
532
|
+
1 / sprite.texture.frame.height
|
|
533
|
+
);
|
|
534
|
+
mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y);
|
|
535
|
+
return mappedMatrix;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
/** @ignore */
|
|
539
|
+
FilterSystem.extension = {
|
|
540
|
+
type: [
|
|
541
|
+
ExtensionType.WebGLSystem,
|
|
542
|
+
ExtensionType.WebGPUSystem
|
|
543
|
+
],
|
|
544
|
+
name: "filter"
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
class Graphics extends ViewContainer {
|
|
548
|
+
/**
|
|
549
|
+
* @param options - Options for the Graphics.
|
|
550
|
+
*/
|
|
551
|
+
constructor(options) {
|
|
552
|
+
if (options instanceof GraphicsContext) {
|
|
553
|
+
options = { context: options };
|
|
554
|
+
}
|
|
555
|
+
const { context, roundPixels, ...rest } = options || {};
|
|
556
|
+
super({
|
|
557
|
+
label: "Graphics",
|
|
558
|
+
...rest
|
|
559
|
+
});
|
|
560
|
+
this.renderPipeId = "graphics";
|
|
561
|
+
if (!context) {
|
|
562
|
+
this._context = this._ownedContext = new GraphicsContext();
|
|
563
|
+
} else {
|
|
564
|
+
this._context = context;
|
|
565
|
+
}
|
|
566
|
+
this._context.on("update", this.onViewUpdate, this);
|
|
567
|
+
this.allowChildren = false;
|
|
568
|
+
this.roundPixels = roundPixels ?? false;
|
|
569
|
+
}
|
|
570
|
+
set context(context) {
|
|
571
|
+
if (context === this._context)
|
|
572
|
+
return;
|
|
573
|
+
this._context.off("update", this.onViewUpdate, this);
|
|
574
|
+
this._context = context;
|
|
575
|
+
this._context.on("update", this.onViewUpdate, this);
|
|
576
|
+
this.onViewUpdate();
|
|
577
|
+
}
|
|
578
|
+
get context() {
|
|
579
|
+
return this._context;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* The local bounds of the graphic.
|
|
583
|
+
* @type {rendering.Bounds}
|
|
584
|
+
*/
|
|
585
|
+
get bounds() {
|
|
586
|
+
return this._context.bounds;
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Graphics objects do not need to update their bounds as the context handles this.
|
|
590
|
+
* @private
|
|
591
|
+
*/
|
|
592
|
+
updateBounds() {
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Checks if the object contains the given point.
|
|
596
|
+
* @param point - The point to check
|
|
597
|
+
*/
|
|
598
|
+
containsPoint(point) {
|
|
599
|
+
return this._context.containsPoint(point);
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Destroys this graphics renderable and optionally its context.
|
|
603
|
+
* @param options - Options parameter. A boolean will act as if all options
|
|
604
|
+
*
|
|
605
|
+
* If the context was created by this graphics and `destroy(false)` or `destroy()` is called
|
|
606
|
+
* then the context will still be destroyed.
|
|
607
|
+
*
|
|
608
|
+
* If you want to explicitly not destroy this context that this graphics created,
|
|
609
|
+
* then you should pass destroy({ context: false })
|
|
610
|
+
*
|
|
611
|
+
* If the context was passed in as an argument to the constructor then it will not be destroyed
|
|
612
|
+
* @param {boolean} [options.texture=false] - Should destroy the texture of the graphics context
|
|
613
|
+
* @param {boolean} [options.textureSource=false] - Should destroy the texture source of the graphics context
|
|
614
|
+
* @param {boolean} [options.context=false] - Should destroy the context
|
|
615
|
+
*/
|
|
616
|
+
destroy(options) {
|
|
617
|
+
if (this._ownedContext && !options) {
|
|
618
|
+
this._ownedContext.destroy(options);
|
|
619
|
+
} else if (options === true || options?.context === true) {
|
|
620
|
+
this._context.destroy(options);
|
|
621
|
+
}
|
|
622
|
+
this._ownedContext = null;
|
|
623
|
+
this._context = null;
|
|
624
|
+
super.destroy(options);
|
|
625
|
+
}
|
|
626
|
+
_callContextMethod(method, args) {
|
|
627
|
+
this.context[method](...args);
|
|
628
|
+
return this;
|
|
629
|
+
}
|
|
630
|
+
// --------------------------------------- GraphicsContext methods ---------------------------------------
|
|
631
|
+
/**
|
|
632
|
+
* Sets the current fill style of the graphics context. The fill style can be a color, gradient,
|
|
633
|
+
* pattern, or a more complex style defined by a FillStyle object.
|
|
634
|
+
* @param {FillInput} args - The fill style to apply. This can be a simple color, a gradient or
|
|
635
|
+
* pattern object, or a FillStyle or ConvertedFillStyle object.
|
|
636
|
+
* @returns The instance of the current GraphicsContext for method chaining.
|
|
637
|
+
*/
|
|
638
|
+
setFillStyle(...args) {
|
|
639
|
+
return this._callContextMethod("setFillStyle", args);
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Sets the current stroke style of the graphics context. Similar to fill styles, stroke styles can
|
|
643
|
+
* encompass colors, gradients, patterns, or more detailed configurations via a StrokeStyle object.
|
|
644
|
+
* @param {StrokeInput} args - The stroke style to apply. Can be defined as a color, a gradient or pattern,
|
|
645
|
+
* or a StrokeStyle or ConvertedStrokeStyle object.
|
|
646
|
+
* @returns The instance of the current GraphicsContext for method chaining.
|
|
647
|
+
*/
|
|
648
|
+
setStrokeStyle(...args) {
|
|
649
|
+
return this._callContextMethod("setStrokeStyle", args);
|
|
650
|
+
}
|
|
651
|
+
fill(...args) {
|
|
652
|
+
return this._callContextMethod("fill", args);
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Strokes the current path with the current stroke style. This method can take an optional
|
|
656
|
+
* FillStyle parameter to define the stroke's appearance, including its color, width, and other properties.
|
|
657
|
+
* @param {FillStyle} args - (Optional) The stroke style to apply. Can be defined as a simple color or a more
|
|
658
|
+
* complex style object. If omitted, uses the current stroke style.
|
|
659
|
+
* @returns The instance of the current GraphicsContext for method chaining.
|
|
660
|
+
*/
|
|
661
|
+
stroke(...args) {
|
|
662
|
+
return this._callContextMethod("stroke", args);
|
|
663
|
+
}
|
|
664
|
+
texture(...args) {
|
|
665
|
+
return this._callContextMethod("texture", args);
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Resets the current path. Any previous path and its commands are discarded and a new path is
|
|
669
|
+
* started. This is typically called before beginning a new shape or series of drawing commands.
|
|
670
|
+
* @returns The instance of the current GraphicsContext for method chaining.
|
|
671
|
+
*/
|
|
672
|
+
beginPath() {
|
|
673
|
+
return this._callContextMethod("beginPath", []);
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* 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. If a hole is not completely in a shape, it will
|
|
678
|
+
* fail to cut correctly!
|
|
679
|
+
*/
|
|
680
|
+
cut() {
|
|
681
|
+
return this._callContextMethod("cut", []);
|
|
682
|
+
}
|
|
683
|
+
arc(...args) {
|
|
684
|
+
return this._callContextMethod("arc", args);
|
|
685
|
+
}
|
|
686
|
+
arcTo(...args) {
|
|
687
|
+
return this._callContextMethod("arcTo", args);
|
|
688
|
+
}
|
|
689
|
+
arcToSvg(...args) {
|
|
690
|
+
return this._callContextMethod("arcToSvg", args);
|
|
691
|
+
}
|
|
692
|
+
bezierCurveTo(...args) {
|
|
693
|
+
return this._callContextMethod("bezierCurveTo", args);
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Closes the current path by drawing a straight line back to the start.
|
|
697
|
+
* If the shape is already closed or there are no points in the path, this method does nothing.
|
|
698
|
+
* @returns The instance of the current object for chaining.
|
|
699
|
+
*/
|
|
700
|
+
closePath() {
|
|
701
|
+
return this._callContextMethod("closePath", []);
|
|
702
|
+
}
|
|
703
|
+
ellipse(...args) {
|
|
704
|
+
return this._callContextMethod("ellipse", args);
|
|
705
|
+
}
|
|
706
|
+
circle(...args) {
|
|
707
|
+
return this._callContextMethod("circle", args);
|
|
708
|
+
}
|
|
709
|
+
path(...args) {
|
|
710
|
+
return this._callContextMethod("path", args);
|
|
711
|
+
}
|
|
712
|
+
lineTo(...args) {
|
|
713
|
+
return this._callContextMethod("lineTo", args);
|
|
714
|
+
}
|
|
715
|
+
moveTo(...args) {
|
|
716
|
+
return this._callContextMethod("moveTo", args);
|
|
717
|
+
}
|
|
718
|
+
quadraticCurveTo(...args) {
|
|
719
|
+
return this._callContextMethod("quadraticCurveTo", args);
|
|
720
|
+
}
|
|
721
|
+
rect(...args) {
|
|
722
|
+
return this._callContextMethod("rect", args);
|
|
723
|
+
}
|
|
724
|
+
roundRect(...args) {
|
|
725
|
+
return this._callContextMethod("roundRect", args);
|
|
726
|
+
}
|
|
727
|
+
poly(...args) {
|
|
728
|
+
return this._callContextMethod("poly", args);
|
|
729
|
+
}
|
|
730
|
+
regularPoly(...args) {
|
|
731
|
+
return this._callContextMethod("regularPoly", args);
|
|
732
|
+
}
|
|
733
|
+
roundPoly(...args) {
|
|
734
|
+
return this._callContextMethod("roundPoly", args);
|
|
735
|
+
}
|
|
736
|
+
roundShape(...args) {
|
|
737
|
+
return this._callContextMethod("roundShape", args);
|
|
738
|
+
}
|
|
739
|
+
filletRect(...args) {
|
|
740
|
+
return this._callContextMethod("filletRect", args);
|
|
741
|
+
}
|
|
742
|
+
chamferRect(...args) {
|
|
743
|
+
return this._callContextMethod("chamferRect", args);
|
|
744
|
+
}
|
|
745
|
+
star(...args) {
|
|
746
|
+
return this._callContextMethod("star", args);
|
|
747
|
+
}
|
|
748
|
+
svg(...args) {
|
|
749
|
+
return this._callContextMethod("svg", args);
|
|
750
|
+
}
|
|
751
|
+
restore(...args) {
|
|
752
|
+
return this._callContextMethod("restore", args);
|
|
753
|
+
}
|
|
754
|
+
/** Saves the current graphics state, including transformations, fill styles, and stroke styles, onto a stack. */
|
|
755
|
+
save() {
|
|
756
|
+
return this._callContextMethod("save", []);
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Returns the current transformation matrix of the graphics context.
|
|
760
|
+
* @returns The current transformation matrix.
|
|
761
|
+
*/
|
|
762
|
+
getTransform() {
|
|
763
|
+
return this.context.getTransform();
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Resets the current transformation matrix to the identity matrix, effectively removing
|
|
767
|
+
* any transformations (rotation, scaling, translation) previously applied.
|
|
768
|
+
* @returns The instance of the current GraphicsContext for method chaining.
|
|
769
|
+
*/
|
|
770
|
+
resetTransform() {
|
|
771
|
+
return this._callContextMethod("resetTransform", []);
|
|
772
|
+
}
|
|
773
|
+
rotateTransform(...args) {
|
|
774
|
+
return this._callContextMethod("rotate", args);
|
|
775
|
+
}
|
|
776
|
+
scaleTransform(...args) {
|
|
777
|
+
return this._callContextMethod("scale", args);
|
|
778
|
+
}
|
|
779
|
+
setTransform(...args) {
|
|
780
|
+
return this._callContextMethod("setTransform", args);
|
|
781
|
+
}
|
|
782
|
+
transform(...args) {
|
|
783
|
+
return this._callContextMethod("transform", args);
|
|
784
|
+
}
|
|
785
|
+
translateTransform(...args) {
|
|
786
|
+
return this._callContextMethod("translate", args);
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Clears all drawing commands from the graphics context, effectively resetting it. This includes clearing the path,
|
|
790
|
+
* and optionally resetting transformations to the identity matrix.
|
|
791
|
+
* @returns The instance of the current GraphicsContext for method chaining.
|
|
792
|
+
*/
|
|
793
|
+
clear() {
|
|
794
|
+
return this._callContextMethod("clear", []);
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* The fill style to use.
|
|
798
|
+
* @type {ConvertedFillStyle}
|
|
799
|
+
*/
|
|
800
|
+
get fillStyle() {
|
|
801
|
+
return this._context.fillStyle;
|
|
802
|
+
}
|
|
803
|
+
set fillStyle(value) {
|
|
804
|
+
this._context.fillStyle = value;
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* The stroke style to use.
|
|
808
|
+
* @type {ConvertedStrokeStyle}
|
|
809
|
+
*/
|
|
810
|
+
get strokeStyle() {
|
|
811
|
+
return this._context.strokeStyle;
|
|
812
|
+
}
|
|
813
|
+
set strokeStyle(value) {
|
|
814
|
+
this._context.strokeStyle = value;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Creates a new Graphics object.
|
|
818
|
+
* Note that only the context of the object is cloned, not its transform (position,scale,etc)
|
|
819
|
+
* @param deep - Whether to create a deep clone of the graphics object. If false, the context
|
|
820
|
+
* will be shared between the two objects (default false). If true, the context will be
|
|
821
|
+
* cloned (recommended if you need to modify the context in any way).
|
|
822
|
+
* @returns - A clone of the graphics object
|
|
823
|
+
*/
|
|
824
|
+
clone(deep = false) {
|
|
825
|
+
if (deep) {
|
|
826
|
+
return new Graphics(this._context.clone());
|
|
827
|
+
}
|
|
828
|
+
this._ownedContext = null;
|
|
829
|
+
const clone = new Graphics(this._context);
|
|
830
|
+
return clone;
|
|
831
|
+
}
|
|
832
|
+
// -------- v7 deprecations ---------
|
|
833
|
+
/**
|
|
834
|
+
* @param width
|
|
835
|
+
* @param color
|
|
836
|
+
* @param alpha
|
|
837
|
+
* @deprecated since 8.0.0 Use {@link Graphics#setStrokeStyle} instead
|
|
838
|
+
*/
|
|
839
|
+
lineStyle(width, color, alpha) {
|
|
840
|
+
deprecation(v8_0_0, "Graphics#lineStyle is no longer needed. Use Graphics#setStrokeStyle to set the stroke style.");
|
|
841
|
+
const strokeStyle = {};
|
|
842
|
+
width && (strokeStyle.width = width);
|
|
843
|
+
color && (strokeStyle.color = color);
|
|
844
|
+
alpha && (strokeStyle.alpha = alpha);
|
|
845
|
+
this.context.strokeStyle = strokeStyle;
|
|
846
|
+
return this;
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* @param color
|
|
850
|
+
* @param alpha
|
|
851
|
+
* @deprecated since 8.0.0 Use {@link Graphics#fill} instead
|
|
852
|
+
*/
|
|
853
|
+
beginFill(color, alpha) {
|
|
854
|
+
deprecation(v8_0_0, "Graphics#beginFill is no longer needed. Use Graphics#fill to fill the shape with the desired style.");
|
|
855
|
+
const fillStyle = {};
|
|
856
|
+
if (color !== void 0)
|
|
857
|
+
fillStyle.color = color;
|
|
858
|
+
if (alpha !== void 0)
|
|
859
|
+
fillStyle.alpha = alpha;
|
|
860
|
+
this.context.fillStyle = fillStyle;
|
|
861
|
+
return this;
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* @deprecated since 8.0.0 Use {@link Graphics#fill} instead
|
|
865
|
+
*/
|
|
866
|
+
endFill() {
|
|
867
|
+
deprecation(v8_0_0, "Graphics#endFill is no longer needed. Use Graphics#fill to fill the shape with the desired style.");
|
|
868
|
+
this.context.fill();
|
|
869
|
+
const strokeStyle = this.context.strokeStyle;
|
|
870
|
+
if (strokeStyle.width !== GraphicsContext.defaultStrokeStyle.width || strokeStyle.color !== GraphicsContext.defaultStrokeStyle.color || strokeStyle.alpha !== GraphicsContext.defaultStrokeStyle.alpha) {
|
|
871
|
+
this.context.stroke();
|
|
872
|
+
}
|
|
873
|
+
return this;
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* @param {...any} args
|
|
877
|
+
* @deprecated since 8.0.0 Use {@link Graphics#circle} instead
|
|
878
|
+
*/
|
|
879
|
+
drawCircle(...args) {
|
|
880
|
+
deprecation(v8_0_0, "Graphics#drawCircle has been renamed to Graphics#circle");
|
|
881
|
+
return this._callContextMethod("circle", args);
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* @param {...any} args
|
|
885
|
+
* @deprecated since 8.0.0 Use {@link Graphics#ellipse} instead
|
|
886
|
+
*/
|
|
887
|
+
drawEllipse(...args) {
|
|
888
|
+
deprecation(v8_0_0, "Graphics#drawEllipse has been renamed to Graphics#ellipse");
|
|
889
|
+
return this._callContextMethod("ellipse", args);
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* @param {...any} args
|
|
893
|
+
* @deprecated since 8.0.0 Use {@link Graphics#poly} instead
|
|
894
|
+
*/
|
|
895
|
+
drawPolygon(...args) {
|
|
896
|
+
deprecation(v8_0_0, "Graphics#drawPolygon has been renamed to Graphics#poly");
|
|
897
|
+
return this._callContextMethod("poly", args);
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* @param {...any} args
|
|
901
|
+
* @deprecated since 8.0.0 Use {@link Graphics#rect} instead
|
|
902
|
+
*/
|
|
903
|
+
drawRect(...args) {
|
|
904
|
+
deprecation(v8_0_0, "Graphics#drawRect has been renamed to Graphics#rect");
|
|
905
|
+
return this._callContextMethod("rect", args);
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* @param {...any} args
|
|
909
|
+
* @deprecated since 8.0.0 Use {@link Graphics#roundRect} instead
|
|
910
|
+
*/
|
|
911
|
+
drawRoundedRect(...args) {
|
|
912
|
+
deprecation(v8_0_0, "Graphics#drawRoundedRect has been renamed to Graphics#roundRect");
|
|
913
|
+
return this._callContextMethod("roundRect", args);
|
|
914
|
+
}
|
|
915
|
+
/**
|
|
916
|
+
* @param {...any} args
|
|
917
|
+
* @deprecated since 8.0.0 Use {@link Graphics#star} instead
|
|
918
|
+
*/
|
|
919
|
+
drawStar(...args) {
|
|
920
|
+
deprecation(v8_0_0, "Graphics#drawStar has been renamed to Graphics#star");
|
|
921
|
+
return this._callContextMethod("star", args);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
const _MeshGeometry = class _MeshGeometry extends Geometry {
|
|
926
|
+
constructor(...args) {
|
|
927
|
+
let options = args[0] ?? {};
|
|
928
|
+
if (options instanceof Float32Array) {
|
|
929
|
+
deprecation(v8_0_0, "use new MeshGeometry({ positions, uvs, indices }) instead");
|
|
930
|
+
options = {
|
|
931
|
+
positions: options,
|
|
932
|
+
uvs: args[1],
|
|
933
|
+
indices: args[2]
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
options = { ..._MeshGeometry.defaultOptions, ...options };
|
|
937
|
+
const positions = options.positions || new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
|
|
938
|
+
let uvs = options.uvs;
|
|
939
|
+
if (!uvs) {
|
|
940
|
+
if (options.positions) {
|
|
941
|
+
uvs = new Float32Array(positions.length);
|
|
942
|
+
} else {
|
|
943
|
+
uvs = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
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;
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Set the UVs of the mesh.
|
|
1005
|
+
* Its important that the uvs array you set is at least as long as the positions array.
|
|
1006
|
+
* otherwise the geometry will not be valid.
|
|
1007
|
+
* @param {Float32Array} value - The UVs of the mesh.
|
|
1008
|
+
*/
|
|
1009
|
+
set uvs(value) {
|
|
1010
|
+
this.attributes.aUV.buffer.data = value;
|
|
1011
|
+
}
|
|
1012
|
+
/** The indices of the mesh. */
|
|
1013
|
+
get indices() {
|
|
1014
|
+
return this.indexBuffer.data;
|
|
1015
|
+
}
|
|
1016
|
+
set indices(value) {
|
|
1017
|
+
this.indexBuffer.data = value;
|
|
1018
|
+
}
|
|
1019
|
+
};
|
|
1020
|
+
_MeshGeometry.defaultOptions = {
|
|
1021
|
+
topology: "triangle-list",
|
|
1022
|
+
shrinkBuffersToFit: false
|
|
1023
|
+
};
|
|
1024
|
+
let MeshGeometry = _MeshGeometry;
|
|
1025
|
+
|
|
1026
|
+
function textStyleToCSS(style) {
|
|
1027
|
+
const stroke = style._stroke;
|
|
1028
|
+
const fill = style._fill;
|
|
1029
|
+
const cssStyleString = [
|
|
1030
|
+
`color: ${Color.shared.setValue(fill.color).toHex()}`,
|
|
1031
|
+
`font-size: ${style.fontSize}px`,
|
|
1032
|
+
`font-family: ${style.fontFamily}`,
|
|
1033
|
+
`font-weight: ${style.fontWeight}`,
|
|
1034
|
+
`font-style: ${style.fontStyle}`,
|
|
1035
|
+
`font-variant: ${style.fontVariant}`,
|
|
1036
|
+
`letter-spacing: ${style.letterSpacing}px`,
|
|
1037
|
+
`text-align: ${style.align}`,
|
|
1038
|
+
`padding: ${style.padding}px`,
|
|
1039
|
+
`white-space: ${style.whiteSpace === "pre" && style.wordWrap ? "pre-wrap" : style.whiteSpace}`,
|
|
1040
|
+
...style.lineHeight ? [`line-height: ${style.lineHeight}px`] : [],
|
|
1041
|
+
...style.wordWrap ? [
|
|
1042
|
+
`word-wrap: ${style.breakWords ? "break-all" : "break-word"}`,
|
|
1043
|
+
`max-width: ${style.wordWrapWidth}px`
|
|
1044
|
+
] : [],
|
|
1045
|
+
...stroke ? [strokeToCSS(stroke)] : [],
|
|
1046
|
+
...style.dropShadow ? [dropShadowToCSS(style.dropShadow)] : [],
|
|
1047
|
+
...style.cssOverrides
|
|
1048
|
+
].join(";");
|
|
1049
|
+
const cssStyles = [`div { ${cssStyleString} }`];
|
|
1050
|
+
tagStyleToCSS(style.tagStyles, cssStyles);
|
|
1051
|
+
return cssStyles.join(" ");
|
|
1052
|
+
}
|
|
1053
|
+
function dropShadowToCSS(dropShadowStyle) {
|
|
1054
|
+
const color = Color.shared.setValue(dropShadowStyle.color).setAlpha(dropShadowStyle.alpha).toHexa();
|
|
1055
|
+
const x = Math.round(Math.cos(dropShadowStyle.angle) * dropShadowStyle.distance);
|
|
1056
|
+
const y = Math.round(Math.sin(dropShadowStyle.angle) * dropShadowStyle.distance);
|
|
1057
|
+
const position = `${x}px ${y}px`;
|
|
1058
|
+
if (dropShadowStyle.blur > 0) {
|
|
1059
|
+
return `text-shadow: ${position} ${dropShadowStyle.blur}px ${color}`;
|
|
1060
|
+
}
|
|
1061
|
+
return `text-shadow: ${position} ${color}`;
|
|
1062
|
+
}
|
|
1063
|
+
function strokeToCSS(stroke) {
|
|
1064
|
+
return [
|
|
1065
|
+
`-webkit-text-stroke-width: ${stroke.width}px`,
|
|
1066
|
+
`-webkit-text-stroke-color: ${Color.shared.setValue(stroke.color).toHex()}`,
|
|
1067
|
+
`text-stroke-width: ${stroke.width}px`,
|
|
1068
|
+
`text-stroke-color: ${Color.shared.setValue(stroke.color).toHex()}`,
|
|
1069
|
+
"paint-order: stroke"
|
|
1070
|
+
].join(";");
|
|
1071
|
+
}
|
|
1072
|
+
const templates = {
|
|
1073
|
+
fontSize: `font-size: {{VALUE}}px`,
|
|
1074
|
+
fontFamily: `font-family: {{VALUE}}`,
|
|
1075
|
+
fontWeight: `font-weight: {{VALUE}}`,
|
|
1076
|
+
fontStyle: `font-style: {{VALUE}}`,
|
|
1077
|
+
fontVariant: `font-variant: {{VALUE}}`,
|
|
1078
|
+
letterSpacing: `letter-spacing: {{VALUE}}px`,
|
|
1079
|
+
align: `text-align: {{VALUE}}`,
|
|
1080
|
+
padding: `padding: {{VALUE}}px`,
|
|
1081
|
+
whiteSpace: `white-space: {{VALUE}}`,
|
|
1082
|
+
lineHeight: `line-height: {{VALUE}}px`,
|
|
1083
|
+
wordWrapWidth: `max-width: {{VALUE}}px`
|
|
1084
|
+
};
|
|
1085
|
+
const transform = {
|
|
1086
|
+
fill: (value) => `color: ${Color.shared.setValue(value).toHex()}`,
|
|
1087
|
+
breakWords: (value) => `word-wrap: ${value ? "break-all" : "break-word"}`,
|
|
1088
|
+
stroke: strokeToCSS,
|
|
1089
|
+
dropShadow: dropShadowToCSS
|
|
1090
|
+
};
|
|
1091
|
+
function tagStyleToCSS(tagStyles, out) {
|
|
1092
|
+
for (const i in tagStyles) {
|
|
1093
|
+
const tagStyle = tagStyles[i];
|
|
1094
|
+
const cssTagStyle = [];
|
|
1095
|
+
for (const j in tagStyle) {
|
|
1096
|
+
if (transform[j]) {
|
|
1097
|
+
cssTagStyle.push(transform[j](tagStyle[j]));
|
|
1098
|
+
} else if (templates[j]) {
|
|
1099
|
+
cssTagStyle.push(templates[j].replace("{{VALUE}}", tagStyle[j]));
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
out.push(`${i} { ${cssTagStyle.join(";")} }`);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
class HTMLTextStyle extends TextStyle {
|
|
1107
|
+
constructor(options = {}) {
|
|
1108
|
+
super(options);
|
|
1109
|
+
this._cssOverrides = [];
|
|
1110
|
+
this.cssOverrides ?? (this.cssOverrides = options.cssOverrides);
|
|
1111
|
+
this.tagStyles = options.tagStyles ?? {};
|
|
1112
|
+
}
|
|
1113
|
+
/** List of style overrides that will be applied to the HTML text. */
|
|
1114
|
+
set cssOverrides(value) {
|
|
1115
|
+
this._cssOverrides = value instanceof Array ? value : [value];
|
|
1116
|
+
this.update();
|
|
1117
|
+
}
|
|
1118
|
+
get cssOverrides() {
|
|
1119
|
+
return this._cssOverrides;
|
|
1120
|
+
}
|
|
1121
|
+
_generateKey() {
|
|
1122
|
+
this._styleKey = generateTextStyleKey(this) + this._cssOverrides.join("-");
|
|
1123
|
+
return this._styleKey;
|
|
1124
|
+
}
|
|
1125
|
+
update() {
|
|
1126
|
+
this._cssStyle = null;
|
|
1127
|
+
super.update();
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Creates a new HTMLTextStyle object with the same values as this one.
|
|
1131
|
+
* @returns New cloned HTMLTextStyle object
|
|
1132
|
+
*/
|
|
1133
|
+
clone() {
|
|
1134
|
+
return new HTMLTextStyle({
|
|
1135
|
+
align: this.align,
|
|
1136
|
+
breakWords: this.breakWords,
|
|
1137
|
+
dropShadow: this.dropShadow ? { ...this.dropShadow } : null,
|
|
1138
|
+
fill: this._fill,
|
|
1139
|
+
fontFamily: this.fontFamily,
|
|
1140
|
+
fontSize: this.fontSize,
|
|
1141
|
+
fontStyle: this.fontStyle,
|
|
1142
|
+
fontVariant: this.fontVariant,
|
|
1143
|
+
fontWeight: this.fontWeight,
|
|
1144
|
+
letterSpacing: this.letterSpacing,
|
|
1145
|
+
lineHeight: this.lineHeight,
|
|
1146
|
+
padding: this.padding,
|
|
1147
|
+
stroke: this._stroke,
|
|
1148
|
+
whiteSpace: this.whiteSpace,
|
|
1149
|
+
wordWrap: this.wordWrap,
|
|
1150
|
+
wordWrapWidth: this.wordWrapWidth,
|
|
1151
|
+
cssOverrides: this.cssOverrides
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
get cssStyle() {
|
|
1155
|
+
if (!this._cssStyle) {
|
|
1156
|
+
this._cssStyle = textStyleToCSS(this);
|
|
1157
|
+
}
|
|
1158
|
+
return this._cssStyle;
|
|
1159
|
+
}
|
|
1160
|
+
/**
|
|
1161
|
+
* Add a style override, this can be any CSS property
|
|
1162
|
+
* it will override any built-in style. This is the
|
|
1163
|
+
* property and the value as a string (e.g., `color: red`).
|
|
1164
|
+
* This will override any other internal style.
|
|
1165
|
+
* @param {string} value - CSS style(s) to add.
|
|
1166
|
+
* @example
|
|
1167
|
+
* style.addOverride('background-color: red');
|
|
1168
|
+
*/
|
|
1169
|
+
addOverride(...value) {
|
|
1170
|
+
const toAdd = value.filter((v) => !this.cssOverrides.includes(v));
|
|
1171
|
+
if (toAdd.length > 0) {
|
|
1172
|
+
this.cssOverrides.push(...toAdd);
|
|
1173
|
+
this.update();
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Remove any overrides that match the value.
|
|
1178
|
+
* @param {string} value - CSS style to remove.
|
|
1179
|
+
* @example
|
|
1180
|
+
* style.removeOverride('background-color: red');
|
|
1181
|
+
*/
|
|
1182
|
+
removeOverride(...value) {
|
|
1183
|
+
const toRemove = value.filter((v) => this.cssOverrides.includes(v));
|
|
1184
|
+
if (toRemove.length > 0) {
|
|
1185
|
+
this.cssOverrides = this.cssOverrides.filter((v) => !toRemove.includes(v));
|
|
1186
|
+
this.update();
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
set fill(value) {
|
|
1190
|
+
if (typeof value !== "string" && typeof value !== "number") {
|
|
1191
|
+
warn("[HTMLTextStyle] only color fill is not supported by HTMLText");
|
|
1192
|
+
}
|
|
1193
|
+
super.fill = value;
|
|
1194
|
+
}
|
|
1195
|
+
set stroke(value) {
|
|
1196
|
+
if (value && typeof value !== "string" && typeof value !== "number") {
|
|
1197
|
+
warn("[HTMLTextStyle] only color stroke is not supported by HTMLText");
|
|
1198
|
+
}
|
|
1199
|
+
super.stroke = value;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
const nssvg = "http://www.w3.org/2000/svg";
|
|
1204
|
+
const nsxhtml = "http://www.w3.org/1999/xhtml";
|
|
1205
|
+
class HTMLTextRenderData {
|
|
1206
|
+
constructor() {
|
|
1207
|
+
this.svgRoot = document.createElementNS(nssvg, "svg");
|
|
1208
|
+
this.foreignObject = document.createElementNS(nssvg, "foreignObject");
|
|
1209
|
+
this.domElement = document.createElementNS(nsxhtml, "div");
|
|
1210
|
+
this.styleElement = document.createElementNS(nsxhtml, "style");
|
|
1211
|
+
this.image = new Image();
|
|
1212
|
+
const { foreignObject, svgRoot, styleElement, domElement } = this;
|
|
1213
|
+
foreignObject.setAttribute("width", "10000");
|
|
1214
|
+
foreignObject.setAttribute("height", "10000");
|
|
1215
|
+
foreignObject.style.overflow = "hidden";
|
|
1216
|
+
svgRoot.appendChild(foreignObject);
|
|
1217
|
+
foreignObject.appendChild(styleElement);
|
|
1218
|
+
foreignObject.appendChild(domElement);
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
let tempHTMLTextRenderData;
|
|
1223
|
+
function measureHtmlText(text, style, fontStyleCSS, htmlTextRenderData) {
|
|
1224
|
+
htmlTextRenderData || (htmlTextRenderData = tempHTMLTextRenderData || (tempHTMLTextRenderData = new HTMLTextRenderData()));
|
|
1225
|
+
const { domElement, styleElement, svgRoot } = htmlTextRenderData;
|
|
1226
|
+
domElement.innerHTML = `<style>${style.cssStyle};</style><div style='padding:0'>${text}</div>`;
|
|
1227
|
+
domElement.setAttribute("style", "transform-origin: top left; display: inline-block");
|
|
1228
|
+
if (fontStyleCSS) {
|
|
1229
|
+
styleElement.textContent = fontStyleCSS;
|
|
1230
|
+
}
|
|
1231
|
+
document.body.appendChild(svgRoot);
|
|
1232
|
+
const contentBounds = domElement.getBoundingClientRect();
|
|
1233
|
+
svgRoot.remove();
|
|
1234
|
+
const doublePadding = style.padding * 2;
|
|
1235
|
+
return {
|
|
1236
|
+
width: contentBounds.width - doublePadding,
|
|
1237
|
+
height: contentBounds.height - doublePadding
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
class GraphicsPipe {
|
|
1242
|
+
constructor(renderer, adaptor) {
|
|
1243
|
+
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
|
+
this.renderer = renderer;
|
|
1248
|
+
this._adaptor = adaptor;
|
|
1249
|
+
this._adaptor.init();
|
|
1250
|
+
this.renderer.renderableGC.addManagedHash(this, "_graphicsBatchesHash");
|
|
1251
|
+
}
|
|
1252
|
+
validateRenderable(graphics) {
|
|
1253
|
+
const context = graphics.context;
|
|
1254
|
+
const wasBatched = !!this._graphicsBatchesHash[graphics.uid];
|
|
1255
|
+
const gpuContext = this.renderer.graphicsContext.updateGpuContext(context);
|
|
1256
|
+
if (gpuContext.isBatchable || wasBatched !== gpuContext.isBatchable) {
|
|
1257
|
+
return true;
|
|
1258
|
+
}
|
|
1259
|
+
return false;
|
|
1260
|
+
}
|
|
1261
|
+
addRenderable(graphics, instructionSet) {
|
|
1262
|
+
const gpuContext = this.renderer.graphicsContext.updateGpuContext(graphics.context);
|
|
1263
|
+
if (graphics.didViewUpdate) {
|
|
1264
|
+
this._rebuild(graphics);
|
|
1265
|
+
}
|
|
1266
|
+
if (gpuContext.isBatchable) {
|
|
1267
|
+
this._addToBatcher(graphics, instructionSet);
|
|
1268
|
+
} else {
|
|
1269
|
+
this.renderer.renderPipes.batch.break(instructionSet);
|
|
1270
|
+
instructionSet.add(graphics);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
updateRenderable(graphics) {
|
|
1274
|
+
const batches = this._graphicsBatchesHash[graphics.uid];
|
|
1275
|
+
if (batches) {
|
|
1276
|
+
for (let i = 0; i < batches.length; i++) {
|
|
1277
|
+
const batch = batches[i];
|
|
1278
|
+
batch._batcher.updateElement(batch);
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
destroyRenderable(graphics) {
|
|
1283
|
+
if (this._graphicsBatchesHash[graphics.uid]) {
|
|
1284
|
+
this._removeBatchForRenderable(graphics.uid);
|
|
1285
|
+
}
|
|
1286
|
+
graphics.off("destroyed", this._destroyRenderableBound);
|
|
1287
|
+
}
|
|
1288
|
+
execute(graphics) {
|
|
1289
|
+
if (!graphics.isRenderable)
|
|
1290
|
+
return;
|
|
1291
|
+
const renderer = this.renderer;
|
|
1292
|
+
const context = graphics.context;
|
|
1293
|
+
const contextSystem = renderer.graphicsContext;
|
|
1294
|
+
if (!contextSystem.getGpuContext(context).batches.length) {
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
const shader = context.customShader || this._adaptor.shader;
|
|
1298
|
+
this.state.blendMode = graphics.groupBlendMode;
|
|
1299
|
+
const localUniforms = shader.resources.localUniforms.uniforms;
|
|
1300
|
+
localUniforms.uTransformMatrix = graphics.groupTransform;
|
|
1301
|
+
localUniforms.uRound = renderer._roundPixels | graphics._roundPixels;
|
|
1302
|
+
color32BitToUniform(
|
|
1303
|
+
graphics.groupColorAlpha,
|
|
1304
|
+
localUniforms.uColor,
|
|
1305
|
+
0
|
|
1306
|
+
);
|
|
1307
|
+
this._adaptor.execute(this, graphics);
|
|
1308
|
+
}
|
|
1309
|
+
_rebuild(graphics) {
|
|
1310
|
+
const wasBatched = !!this._graphicsBatchesHash[graphics.uid];
|
|
1311
|
+
const gpuContext = this.renderer.graphicsContext.updateGpuContext(graphics.context);
|
|
1312
|
+
if (wasBatched) {
|
|
1313
|
+
this._removeBatchForRenderable(graphics.uid);
|
|
1314
|
+
}
|
|
1315
|
+
if (gpuContext.isBatchable) {
|
|
1316
|
+
this._initBatchesForRenderable(graphics);
|
|
1317
|
+
}
|
|
1318
|
+
graphics.batched = gpuContext.isBatchable;
|
|
1319
|
+
}
|
|
1320
|
+
_addToBatcher(graphics, instructionSet) {
|
|
1321
|
+
const batchPipe = this.renderer.renderPipes.batch;
|
|
1322
|
+
const batches = this._getBatchesForRenderable(graphics);
|
|
1323
|
+
for (let i = 0; i < batches.length; i++) {
|
|
1324
|
+
const batch = batches[i];
|
|
1325
|
+
batchPipe.addToBatch(batch, instructionSet);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
_getBatchesForRenderable(graphics) {
|
|
1329
|
+
return this._graphicsBatchesHash[graphics.uid] || this._initBatchesForRenderable(graphics);
|
|
1330
|
+
}
|
|
1331
|
+
_initBatchesForRenderable(graphics) {
|
|
1332
|
+
const context = graphics.context;
|
|
1333
|
+
const gpuContext = this.renderer.graphicsContext.getGpuContext(context);
|
|
1334
|
+
const roundPixels = this.renderer._roundPixels | graphics._roundPixels;
|
|
1335
|
+
const batches = gpuContext.batches.map((batch) => {
|
|
1336
|
+
const batchClone = BigPool.get(BatchableGraphics);
|
|
1337
|
+
batch.copyTo(batchClone);
|
|
1338
|
+
batchClone.renderable = graphics;
|
|
1339
|
+
batchClone.roundPixels = roundPixels;
|
|
1340
|
+
return batchClone;
|
|
1341
|
+
});
|
|
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
|
+
}
|
|
1354
|
+
destroy() {
|
|
1355
|
+
this.renderer = null;
|
|
1356
|
+
this._adaptor.destroy();
|
|
1357
|
+
this._adaptor = null;
|
|
1358
|
+
this.state = null;
|
|
1359
|
+
for (const i in this._graphicsBatchesHash) {
|
|
1360
|
+
this._removeBatchForRenderable(i);
|
|
1361
|
+
}
|
|
1362
|
+
this._graphicsBatchesHash = null;
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
/** @ignore */
|
|
1366
|
+
GraphicsPipe.extension = {
|
|
1367
|
+
type: [
|
|
1368
|
+
ExtensionType.WebGLPipes,
|
|
1369
|
+
ExtensionType.WebGPUPipes,
|
|
1370
|
+
ExtensionType.CanvasPipes
|
|
1371
|
+
],
|
|
1372
|
+
name: "graphics"
|
|
1373
|
+
};
|
|
1374
|
+
|
|
1375
|
+
const _PlaneGeometry = class _PlaneGeometry extends MeshGeometry {
|
|
1376
|
+
constructor(...args) {
|
|
1377
|
+
super({});
|
|
1378
|
+
let options = args[0] ?? {};
|
|
1379
|
+
if (typeof options === "number") {
|
|
1380
|
+
deprecation(v8_0_0, "PlaneGeometry constructor changed please use { width, height, verticesX, verticesY } instead");
|
|
1381
|
+
options = {
|
|
1382
|
+
width: options,
|
|
1383
|
+
height: args[1],
|
|
1384
|
+
verticesX: args[2],
|
|
1385
|
+
verticesY: args[3]
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
this.build(options);
|
|
1389
|
+
}
|
|
1390
|
+
/**
|
|
1391
|
+
* Refreshes plane coordinates
|
|
1392
|
+
* @param options - Options to be applied to plane geometry
|
|
1393
|
+
*/
|
|
1394
|
+
build(options) {
|
|
1395
|
+
options = { ..._PlaneGeometry.defaultOptions, ...options };
|
|
1396
|
+
this.verticesX = this.verticesX ?? options.verticesX;
|
|
1397
|
+
this.verticesY = this.verticesY ?? options.verticesY;
|
|
1398
|
+
this.width = this.width ?? options.width;
|
|
1399
|
+
this.height = this.height ?? options.height;
|
|
1400
|
+
const total = this.verticesX * this.verticesY;
|
|
1401
|
+
const verts = [];
|
|
1402
|
+
const uvs = [];
|
|
1403
|
+
const indices = [];
|
|
1404
|
+
const verticesX = this.verticesX - 1;
|
|
1405
|
+
const verticesY = this.verticesY - 1;
|
|
1406
|
+
const sizeX = this.width / verticesX;
|
|
1407
|
+
const sizeY = this.height / verticesY;
|
|
1408
|
+
for (let i = 0; i < total; i++) {
|
|
1409
|
+
const x = i % this.verticesX;
|
|
1410
|
+
const y = i / this.verticesX | 0;
|
|
1411
|
+
verts.push(x * sizeX, y * sizeY);
|
|
1412
|
+
uvs.push(x / verticesX, y / verticesY);
|
|
1413
|
+
}
|
|
1414
|
+
const totalSub = verticesX * verticesY;
|
|
1415
|
+
for (let i = 0; i < totalSub; i++) {
|
|
1416
|
+
const xpos = i % verticesX;
|
|
1417
|
+
const ypos = i / verticesX | 0;
|
|
1418
|
+
const value = ypos * this.verticesX + xpos;
|
|
1419
|
+
const value2 = ypos * this.verticesX + xpos + 1;
|
|
1420
|
+
const value3 = (ypos + 1) * this.verticesX + xpos;
|
|
1421
|
+
const value4 = (ypos + 1) * this.verticesX + xpos + 1;
|
|
1422
|
+
indices.push(
|
|
1423
|
+
value,
|
|
1424
|
+
value2,
|
|
1425
|
+
value3,
|
|
1426
|
+
value2,
|
|
1427
|
+
value4,
|
|
1428
|
+
value3
|
|
1429
|
+
);
|
|
1430
|
+
}
|
|
1431
|
+
this.buffers[0].data = new Float32Array(verts);
|
|
1432
|
+
this.buffers[1].data = new Float32Array(uvs);
|
|
1433
|
+
this.indexBuffer.data = new Uint32Array(indices);
|
|
1434
|
+
this.buffers[0].update();
|
|
1435
|
+
this.buffers[1].update();
|
|
1436
|
+
this.indexBuffer.update();
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
1439
|
+
_PlaneGeometry.defaultOptions = {
|
|
1440
|
+
width: 100,
|
|
1441
|
+
height: 100,
|
|
1442
|
+
verticesX: 10,
|
|
1443
|
+
verticesY: 10
|
|
1444
|
+
};
|
|
1445
|
+
let PlaneGeometry = _PlaneGeometry;
|
|
1446
|
+
|
|
1447
|
+
class BatchableMesh {
|
|
1448
|
+
constructor() {
|
|
1449
|
+
this.batcherName = "default";
|
|
1450
|
+
this.packAsQuad = false;
|
|
1451
|
+
this.indexOffset = 0;
|
|
1452
|
+
this.attributeOffset = 0;
|
|
1453
|
+
this.roundPixels = 0;
|
|
1454
|
+
this._batcher = null;
|
|
1455
|
+
this._batch = null;
|
|
1456
|
+
this._textureMatrixUpdateId = -1;
|
|
1457
|
+
this._uvUpdateId = -1;
|
|
1458
|
+
}
|
|
1459
|
+
get blendMode() {
|
|
1460
|
+
return this.renderable.groupBlendMode;
|
|
1461
|
+
}
|
|
1462
|
+
get topology() {
|
|
1463
|
+
return this._topology || this.geometry.topology;
|
|
1464
|
+
}
|
|
1465
|
+
set topology(value) {
|
|
1466
|
+
this._topology = value;
|
|
1467
|
+
}
|
|
1468
|
+
reset() {
|
|
1469
|
+
this.renderable = null;
|
|
1470
|
+
this.texture = null;
|
|
1471
|
+
this._batcher = null;
|
|
1472
|
+
this._batch = null;
|
|
1473
|
+
this.geometry = null;
|
|
1474
|
+
this._uvUpdateId = -1;
|
|
1475
|
+
this._textureMatrixUpdateId = -1;
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* Sets the texture for the batchable mesh.
|
|
1479
|
+
* As it does so, it resets the texture matrix update ID.
|
|
1480
|
+
* this is to ensure that the texture matrix is recalculated when the uvs are referenced
|
|
1481
|
+
* @param value - The texture to set.
|
|
1482
|
+
*/
|
|
1483
|
+
setTexture(value) {
|
|
1484
|
+
if (this.texture === value)
|
|
1485
|
+
return;
|
|
1486
|
+
this.texture = value;
|
|
1487
|
+
this._textureMatrixUpdateId = -1;
|
|
1488
|
+
}
|
|
1489
|
+
get uvs() {
|
|
1490
|
+
const geometry = this.geometry;
|
|
1491
|
+
const uvBuffer = geometry.getBuffer("aUV");
|
|
1492
|
+
const uvs = uvBuffer.data;
|
|
1493
|
+
let transformedUvs = uvs;
|
|
1494
|
+
const textureMatrix = this.texture.textureMatrix;
|
|
1495
|
+
if (!textureMatrix.isSimple) {
|
|
1496
|
+
transformedUvs = this._transformedUvs;
|
|
1497
|
+
if (this._textureMatrixUpdateId !== textureMatrix._updateID || this._uvUpdateId !== uvBuffer._updateID) {
|
|
1498
|
+
if (!transformedUvs || transformedUvs.length < uvs.length) {
|
|
1499
|
+
transformedUvs = this._transformedUvs = new Float32Array(uvs.length);
|
|
1500
|
+
}
|
|
1501
|
+
this._textureMatrixUpdateId = textureMatrix._updateID;
|
|
1502
|
+
this._uvUpdateId = uvBuffer._updateID;
|
|
1503
|
+
textureMatrix.multiplyUvs(uvs, transformedUvs);
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
return transformedUvs;
|
|
1507
|
+
}
|
|
1508
|
+
get positions() {
|
|
1509
|
+
return this.geometry.positions;
|
|
1510
|
+
}
|
|
1511
|
+
get indices() {
|
|
1512
|
+
return this.geometry.indices;
|
|
1513
|
+
}
|
|
1514
|
+
get color() {
|
|
1515
|
+
return this.renderable.groupColorAlpha;
|
|
1516
|
+
}
|
|
1517
|
+
get groupTransform() {
|
|
1518
|
+
return this.renderable.groupTransform;
|
|
1519
|
+
}
|
|
1520
|
+
get attributeSize() {
|
|
1521
|
+
return this.geometry.positions.length / 2;
|
|
1522
|
+
}
|
|
1523
|
+
get indexSize() {
|
|
1524
|
+
return this.geometry.indices.length;
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
class MeshPipe {
|
|
1529
|
+
constructor(renderer, adaptor) {
|
|
1530
|
+
this.localUniforms = new UniformGroup({
|
|
1531
|
+
uTransformMatrix: { value: new Matrix(), type: "mat3x3<f32>" },
|
|
1532
|
+
uColor: { value: new Float32Array([1, 1, 1, 1]), type: "vec4<f32>" },
|
|
1533
|
+
uRound: { value: 0, type: "f32" }
|
|
1534
|
+
});
|
|
1535
|
+
this.localUniformsBindGroup = new BindGroup({
|
|
1536
|
+
0: this.localUniforms
|
|
1537
|
+
});
|
|
1538
|
+
this._meshDataHash = /* @__PURE__ */ Object.create(null);
|
|
1539
|
+
this._gpuBatchableMeshHash = /* @__PURE__ */ Object.create(null);
|
|
1540
|
+
this._destroyRenderableBound = this.destroyRenderable.bind(this);
|
|
1541
|
+
this.renderer = renderer;
|
|
1542
|
+
this._adaptor = adaptor;
|
|
1543
|
+
this._adaptor.init();
|
|
1544
|
+
renderer.renderableGC.addManagedHash(this, "_gpuBatchableMeshHash");
|
|
1545
|
+
renderer.renderableGC.addManagedHash(this, "_meshDataHash");
|
|
1546
|
+
}
|
|
1547
|
+
validateRenderable(mesh) {
|
|
1548
|
+
const meshData = this._getMeshData(mesh);
|
|
1549
|
+
const wasBatched = meshData.batched;
|
|
1550
|
+
const isBatched = mesh.batched;
|
|
1551
|
+
meshData.batched = isBatched;
|
|
1552
|
+
if (wasBatched !== isBatched) {
|
|
1553
|
+
return true;
|
|
1554
|
+
} else if (isBatched) {
|
|
1555
|
+
const geometry = mesh._geometry;
|
|
1556
|
+
if (geometry.indices.length !== meshData.indexSize || geometry.positions.length !== meshData.vertexSize) {
|
|
1557
|
+
meshData.indexSize = geometry.indices.length;
|
|
1558
|
+
meshData.vertexSize = geometry.positions.length;
|
|
1559
|
+
return true;
|
|
1560
|
+
}
|
|
1561
|
+
const batchableMesh = this._getBatchableMesh(mesh);
|
|
1562
|
+
if (batchableMesh.texture.uid !== mesh._texture.uid) {
|
|
1563
|
+
batchableMesh._textureMatrixUpdateId = -1;
|
|
1564
|
+
}
|
|
1565
|
+
return !batchableMesh._batcher.checkAndUpdateTexture(
|
|
1566
|
+
batchableMesh,
|
|
1567
|
+
mesh._texture
|
|
1568
|
+
);
|
|
1569
|
+
}
|
|
1570
|
+
return false;
|
|
1571
|
+
}
|
|
1572
|
+
addRenderable(mesh, instructionSet) {
|
|
1573
|
+
const batcher = this.renderer.renderPipes.batch;
|
|
1574
|
+
const { batched } = this._getMeshData(mesh);
|
|
1575
|
+
if (batched) {
|
|
1576
|
+
const gpuBatchableMesh = this._getBatchableMesh(mesh);
|
|
1577
|
+
gpuBatchableMesh.setTexture(mesh._texture);
|
|
1578
|
+
gpuBatchableMesh.geometry = mesh._geometry;
|
|
1579
|
+
batcher.addToBatch(gpuBatchableMesh, instructionSet);
|
|
1580
|
+
} else {
|
|
1581
|
+
batcher.break(instructionSet);
|
|
1582
|
+
instructionSet.add(mesh);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
updateRenderable(mesh) {
|
|
1586
|
+
if (mesh.batched) {
|
|
1587
|
+
const gpuBatchableMesh = this._gpuBatchableMeshHash[mesh.uid];
|
|
1588
|
+
gpuBatchableMesh.setTexture(mesh._texture);
|
|
1589
|
+
gpuBatchableMesh.geometry = mesh._geometry;
|
|
1590
|
+
gpuBatchableMesh._batcher.updateElement(gpuBatchableMesh);
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
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
|
+
execute(mesh) {
|
|
1603
|
+
if (!mesh.isRenderable)
|
|
1604
|
+
return;
|
|
1605
|
+
mesh.state.blendMode = getAdjustedBlendModeBlend(mesh.groupBlendMode, mesh.texture._source);
|
|
1606
|
+
const localUniforms = this.localUniforms;
|
|
1607
|
+
localUniforms.uniforms.uTransformMatrix = mesh.groupTransform;
|
|
1608
|
+
localUniforms.uniforms.uRound = this.renderer._roundPixels | mesh._roundPixels;
|
|
1609
|
+
localUniforms.update();
|
|
1610
|
+
color32BitToUniform(
|
|
1611
|
+
mesh.groupColorAlpha,
|
|
1612
|
+
localUniforms.uniforms.uColor,
|
|
1613
|
+
0
|
|
1614
|
+
);
|
|
1615
|
+
this._adaptor.execute(this, mesh);
|
|
1616
|
+
}
|
|
1617
|
+
_getMeshData(mesh) {
|
|
1618
|
+
return this._meshDataHash[mesh.uid] || this._initMeshData(mesh);
|
|
1619
|
+
}
|
|
1620
|
+
_initMeshData(mesh) {
|
|
1621
|
+
this._meshDataHash[mesh.uid] = {
|
|
1622
|
+
batched: mesh.batched,
|
|
1623
|
+
indexSize: mesh._geometry.indices?.length,
|
|
1624
|
+
vertexSize: mesh._geometry.positions?.length
|
|
1625
|
+
};
|
|
1626
|
+
mesh.on("destroyed", this._destroyRenderableBound);
|
|
1627
|
+
return this._meshDataHash[mesh.uid];
|
|
1628
|
+
}
|
|
1629
|
+
_getBatchableMesh(mesh) {
|
|
1630
|
+
return this._gpuBatchableMeshHash[mesh.uid] || this._initBatchableMesh(mesh);
|
|
1631
|
+
}
|
|
1632
|
+
_initBatchableMesh(mesh) {
|
|
1633
|
+
const gpuMesh = BigPool.get(BatchableMesh);
|
|
1634
|
+
gpuMesh.renderable = mesh;
|
|
1635
|
+
gpuMesh.setTexture(mesh._texture);
|
|
1636
|
+
gpuMesh.transform = mesh.groupTransform;
|
|
1637
|
+
gpuMesh.roundPixels = this.renderer._roundPixels | mesh._roundPixels;
|
|
1638
|
+
this._gpuBatchableMeshHash[mesh.uid] = gpuMesh;
|
|
1639
|
+
return gpuMesh;
|
|
1640
|
+
}
|
|
1641
|
+
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
|
+
this.localUniforms = null;
|
|
1650
|
+
this.localUniformsBindGroup = null;
|
|
1651
|
+
this._adaptor.destroy();
|
|
1652
|
+
this._adaptor = null;
|
|
1653
|
+
this.renderer = null;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
/** @ignore */
|
|
1657
|
+
MeshPipe.extension = {
|
|
1658
|
+
type: [
|
|
1659
|
+
ExtensionType.WebGLPipes,
|
|
1660
|
+
ExtensionType.WebGPUPipes,
|
|
1661
|
+
ExtensionType.CanvasPipes
|
|
1662
|
+
],
|
|
1663
|
+
name: "mesh"
|
|
1664
|
+
};
|
|
1665
|
+
|
|
1666
|
+
class GlParticleContainerAdaptor {
|
|
1667
|
+
execute(particleContainerPipe, container) {
|
|
1668
|
+
const state = particleContainerPipe.state;
|
|
1669
|
+
const renderer = particleContainerPipe.renderer;
|
|
1670
|
+
const shader = container.shader || particleContainerPipe.defaultShader;
|
|
1671
|
+
shader.resources.uTexture = container.texture._source;
|
|
1672
|
+
shader.resources.uniforms = particleContainerPipe.localUniforms;
|
|
1673
|
+
const gl = renderer.gl;
|
|
1674
|
+
const buffer = particleContainerPipe.getBuffers(container);
|
|
1675
|
+
renderer.shader.bind(shader);
|
|
1676
|
+
renderer.state.set(state);
|
|
1677
|
+
renderer.geometry.bind(buffer.geometry, shader.glProgram);
|
|
1678
|
+
const byteSize = buffer.geometry.indexBuffer.data.BYTES_PER_ELEMENT;
|
|
1679
|
+
const glType = byteSize === 2 ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT;
|
|
1680
|
+
gl.drawElements(gl.TRIANGLES, container.particleChildren.length * 6, glType, 0);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
class GpuParticleContainerAdaptor {
|
|
1685
|
+
execute(particleContainerPipe, container) {
|
|
1686
|
+
const renderer = particleContainerPipe.renderer;
|
|
1687
|
+
const shader = container.shader || particleContainerPipe.defaultShader;
|
|
1688
|
+
shader.groups[0] = renderer.renderPipes.uniformBatch.getUniformBindGroup(particleContainerPipe.localUniforms, true);
|
|
1689
|
+
shader.groups[1] = renderer.texture.getTextureBindGroup(container.texture);
|
|
1690
|
+
const state = particleContainerPipe.state;
|
|
1691
|
+
const buffer = particleContainerPipe.getBuffers(container);
|
|
1692
|
+
renderer.encoder.draw({
|
|
1693
|
+
geometry: buffer.geometry,
|
|
1694
|
+
shader: container.shader || particleContainerPipe.defaultShader,
|
|
1695
|
+
state,
|
|
1696
|
+
size: container.particleChildren.length * 6
|
|
1697
|
+
});
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
function createIndicesForQuads(size, outBuffer = null) {
|
|
1702
|
+
const totalIndices = size * 6;
|
|
1703
|
+
if (totalIndices > 65535) {
|
|
1704
|
+
outBuffer || (outBuffer = new Uint32Array(totalIndices));
|
|
1705
|
+
} else {
|
|
1706
|
+
outBuffer || (outBuffer = new Uint16Array(totalIndices));
|
|
1707
|
+
}
|
|
1708
|
+
if (outBuffer.length !== totalIndices) {
|
|
1709
|
+
throw new Error(`Out buffer length is incorrect, got ${outBuffer.length} and expected ${totalIndices}`);
|
|
1710
|
+
}
|
|
1711
|
+
for (let i = 0, j = 0; i < totalIndices; i += 6, j += 4) {
|
|
1712
|
+
outBuffer[i + 0] = j + 0;
|
|
1713
|
+
outBuffer[i + 1] = j + 1;
|
|
1714
|
+
outBuffer[i + 2] = j + 2;
|
|
1715
|
+
outBuffer[i + 3] = j + 0;
|
|
1716
|
+
outBuffer[i + 4] = j + 2;
|
|
1717
|
+
outBuffer[i + 5] = j + 3;
|
|
1718
|
+
}
|
|
1719
|
+
return outBuffer;
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
function generateParticleUpdateFunction(properties) {
|
|
1723
|
+
return {
|
|
1724
|
+
dynamicUpdate: generateUpdateFunction(properties, true),
|
|
1725
|
+
staticUpdate: generateUpdateFunction(properties, false)
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
function generateUpdateFunction(properties, dynamic) {
|
|
1729
|
+
const funcFragments = [];
|
|
1730
|
+
funcFragments.push(`
|
|
1731
|
+
|
|
1732
|
+
var index = 0;
|
|
1733
|
+
|
|
1734
|
+
for (let i = 0; i < ps.length; ++i)
|
|
1735
|
+
{
|
|
1736
|
+
const p = ps[i];
|
|
1737
|
+
|
|
1738
|
+
`);
|
|
1739
|
+
let offset = 0;
|
|
1740
|
+
for (const i in properties) {
|
|
1741
|
+
const property = properties[i];
|
|
1742
|
+
if (dynamic !== property.dynamic)
|
|
1743
|
+
continue;
|
|
1744
|
+
funcFragments.push(`offset = index + ${offset}`);
|
|
1745
|
+
funcFragments.push(property.code);
|
|
1746
|
+
const attributeInfo = getAttributeInfoFromFormat(property.format);
|
|
1747
|
+
offset += attributeInfo.stride / 4;
|
|
1748
|
+
}
|
|
1749
|
+
funcFragments.push(`
|
|
1750
|
+
index += stride * 4;
|
|
1751
|
+
}
|
|
1752
|
+
`);
|
|
1753
|
+
funcFragments.unshift(`
|
|
1754
|
+
var stride = ${offset};
|
|
1755
|
+
`);
|
|
1756
|
+
const functionSource = funcFragments.join("\n");
|
|
1757
|
+
return new Function("ps", "f32v", "u32v", functionSource);
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
class ParticleBuffer {
|
|
1761
|
+
constructor(options) {
|
|
1762
|
+
this._size = 0;
|
|
1763
|
+
this._generateParticleUpdateCache = {};
|
|
1764
|
+
const size = this._size = options.size ?? 1e3;
|
|
1765
|
+
const properties = options.properties;
|
|
1766
|
+
let staticVertexSize = 0;
|
|
1767
|
+
let dynamicVertexSize = 0;
|
|
1768
|
+
for (const i in properties) {
|
|
1769
|
+
const property = properties[i];
|
|
1770
|
+
const attributeInfo = getAttributeInfoFromFormat(property.format);
|
|
1771
|
+
if (property.dynamic) {
|
|
1772
|
+
dynamicVertexSize += attributeInfo.stride;
|
|
1773
|
+
} else {
|
|
1774
|
+
staticVertexSize += attributeInfo.stride;
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
this._dynamicStride = dynamicVertexSize / 4;
|
|
1778
|
+
this._staticStride = staticVertexSize / 4;
|
|
1779
|
+
this.staticAttributeBuffer = new ViewableBuffer(size * 4 * staticVertexSize);
|
|
1780
|
+
this.dynamicAttributeBuffer = new ViewableBuffer(size * 4 * dynamicVertexSize);
|
|
1781
|
+
this.indexBuffer = createIndicesForQuads(size);
|
|
1782
|
+
const geometry = new Geometry();
|
|
1783
|
+
let dynamicOffset = 0;
|
|
1784
|
+
let staticOffset = 0;
|
|
1785
|
+
this._staticBuffer = new Buffer({
|
|
1786
|
+
data: new Float32Array(1),
|
|
1787
|
+
label: "static-particle-buffer",
|
|
1788
|
+
shrinkToFit: false,
|
|
1789
|
+
usage: BufferUsage.VERTEX | BufferUsage.COPY_DST
|
|
1790
|
+
});
|
|
1791
|
+
this._dynamicBuffer = new Buffer({
|
|
1792
|
+
data: new Float32Array(1),
|
|
1793
|
+
label: "dynamic-particle-buffer",
|
|
1794
|
+
shrinkToFit: false,
|
|
1795
|
+
usage: BufferUsage.VERTEX | BufferUsage.COPY_DST
|
|
1796
|
+
});
|
|
1797
|
+
for (const i in properties) {
|
|
1798
|
+
const property = properties[i];
|
|
1799
|
+
const attributeInfo = getAttributeInfoFromFormat(property.format);
|
|
1800
|
+
if (property.dynamic) {
|
|
1801
|
+
geometry.addAttribute(property.attributeName, {
|
|
1802
|
+
buffer: this._dynamicBuffer,
|
|
1803
|
+
stride: this._dynamicStride * 4,
|
|
1804
|
+
offset: dynamicOffset * 4,
|
|
1805
|
+
format: property.format
|
|
1806
|
+
});
|
|
1807
|
+
dynamicOffset += attributeInfo.size;
|
|
1808
|
+
} else {
|
|
1809
|
+
geometry.addAttribute(property.attributeName, {
|
|
1810
|
+
buffer: this._staticBuffer,
|
|
1811
|
+
stride: this._staticStride * 4,
|
|
1812
|
+
offset: staticOffset * 4,
|
|
1813
|
+
format: property.format
|
|
1814
|
+
});
|
|
1815
|
+
staticOffset += attributeInfo.size;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
geometry.addIndex(this.indexBuffer);
|
|
1819
|
+
const uploadFunction = this.getParticleUpdate(properties);
|
|
1820
|
+
this._dynamicUpload = uploadFunction.dynamicUpdate;
|
|
1821
|
+
this._staticUpload = uploadFunction.staticUpdate;
|
|
1822
|
+
this.geometry = geometry;
|
|
1823
|
+
}
|
|
1824
|
+
getParticleUpdate(properties) {
|
|
1825
|
+
const key = getParticleSyncKey(properties);
|
|
1826
|
+
if (this._generateParticleUpdateCache[key]) {
|
|
1827
|
+
return this._generateParticleUpdateCache[key];
|
|
1828
|
+
}
|
|
1829
|
+
this._generateParticleUpdateCache[key] = this.generateParticleUpdate(properties);
|
|
1830
|
+
return this._generateParticleUpdateCache[key];
|
|
1831
|
+
}
|
|
1832
|
+
generateParticleUpdate(properties) {
|
|
1833
|
+
return generateParticleUpdateFunction(properties);
|
|
1834
|
+
}
|
|
1835
|
+
update(particles, uploadStatic) {
|
|
1836
|
+
if (particles.length > this._size) {
|
|
1837
|
+
uploadStatic = true;
|
|
1838
|
+
this._size = Math.max(particles.length, this._size * 1.5 | 0);
|
|
1839
|
+
this.staticAttributeBuffer = new ViewableBuffer(this._size * this._staticStride * 4 * 4);
|
|
1840
|
+
this.dynamicAttributeBuffer = new ViewableBuffer(this._size * this._dynamicStride * 4 * 4);
|
|
1841
|
+
this.indexBuffer = createIndicesForQuads(this._size);
|
|
1842
|
+
this.geometry.indexBuffer.setDataWithSize(
|
|
1843
|
+
this.indexBuffer,
|
|
1844
|
+
this.indexBuffer.byteLength,
|
|
1845
|
+
true
|
|
1846
|
+
);
|
|
1847
|
+
}
|
|
1848
|
+
const dynamicAttributeBuffer = this.dynamicAttributeBuffer;
|
|
1849
|
+
this._dynamicUpload(particles, dynamicAttributeBuffer.float32View, dynamicAttributeBuffer.uint32View);
|
|
1850
|
+
this._dynamicBuffer.setDataWithSize(
|
|
1851
|
+
this.dynamicAttributeBuffer.float32View,
|
|
1852
|
+
particles.length * this._dynamicStride * 4,
|
|
1853
|
+
true
|
|
1854
|
+
);
|
|
1855
|
+
if (uploadStatic) {
|
|
1856
|
+
const staticAttributeBuffer = this.staticAttributeBuffer;
|
|
1857
|
+
this._staticUpload(particles, staticAttributeBuffer.float32View, staticAttributeBuffer.uint32View);
|
|
1858
|
+
this._staticBuffer.setDataWithSize(
|
|
1859
|
+
staticAttributeBuffer.float32View,
|
|
1860
|
+
particles.length * this._staticStride * 4,
|
|
1861
|
+
true
|
|
1862
|
+
);
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
destroy() {
|
|
1866
|
+
this._staticBuffer.destroy();
|
|
1867
|
+
this._dynamicBuffer.destroy();
|
|
1868
|
+
this.geometry.destroy();
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
function getParticleSyncKey(properties) {
|
|
1872
|
+
const keyGen = [];
|
|
1873
|
+
for (const key in properties) {
|
|
1874
|
+
const property = properties[key];
|
|
1875
|
+
keyGen.push(key, property.code, property.dynamic ? "d" : "s");
|
|
1876
|
+
}
|
|
1877
|
+
return keyGen.join("_");
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
var fragment = "varying vec2 vUV;\nvarying vec4 vColor;\n\nuniform sampler2D uTexture;\n\nvoid main(void){\n vec4 color = texture2D(uTexture, vUV) * vColor;\n gl_FragColor = color;\n}";
|
|
1881
|
+
|
|
1882
|
+
var vertex = "attribute vec2 aVertex;\nattribute vec2 aUV;\nattribute vec4 aColor;\n\nattribute vec2 aPosition;\nattribute float aRotation;\n\nuniform mat3 uTranslationMatrix;\nuniform float uRound;\nuniform vec2 uResolution;\nuniform vec4 uColor;\n\nvarying vec2 vUV;\nvarying vec4 vColor;\n\nvec2 roundPixels(vec2 position, vec2 targetSize)\n{ \n return (floor(((position * 0.5 + 0.5) * targetSize) + 0.5) / targetSize) * 2.0 - 1.0;\n}\n\nvoid main(void){\n float cosRotation = cos(aRotation);\n float sinRotation = sin(aRotation);\n float x = aVertex.x * cosRotation - aVertex.y * sinRotation;\n float y = aVertex.x * sinRotation + aVertex.y * cosRotation;\n\n vec2 v = vec2(x, y);\n v = v + aPosition;\n\n gl_Position = vec4((uTranslationMatrix * vec3(v, 1.0)).xy, 0.0, 1.0);\n\n if(uRound == 1.0)\n {\n gl_Position.xy = roundPixels(gl_Position.xy, uResolution);\n }\n\n vUV = aUV;\n vColor = vec4(aColor.rgb * aColor.a, aColor.a) * uColor;\n}\n";
|
|
1883
|
+
|
|
1884
|
+
var wgsl = "\nstruct ParticleUniforms {\n uProjectionMatrix:mat3x3<f32>,\n uColor:vec4<f32>,\n uResolution:vec2<f32>,\n uRoundPixels:f32,\n};\n\n@group(0) @binding(0) var<uniform> uniforms: ParticleUniforms;\n\n@group(1) @binding(0) var uTexture: texture_2d<f32>;\n@group(1) @binding(1) var uSampler : sampler;\n\nstruct VSOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) uv : vec2<f32>,\n @location(1) color : vec4<f32>,\n };\n@vertex\nfn mainVertex(\n @location(0) aVertex: vec2<f32>,\n @location(1) aPosition: vec2<f32>,\n @location(2) aUV: vec2<f32>,\n @location(3) aColor: vec4<f32>,\n @location(4) aRotation: f32,\n) -> VSOutput {\n \n let v = vec2(\n aVertex.x * cos(aRotation) - aVertex.y * sin(aRotation),\n aVertex.x * sin(aRotation) + aVertex.y * cos(aRotation)\n ) + aPosition;\n\n let position = vec4((uniforms.uProjectionMatrix * vec3(v, 1.0)).xy, 0.0, 1.0);\n\n let vColor = vec4(aColor.rgb * aColor.a, aColor.a) * uniforms.uColor;\n\n return VSOutput(\n position,\n aUV,\n vColor,\n );\n}\n\n@fragment\nfn mainFragment(\n @location(0) uv: vec2<f32>,\n @location(1) color: vec4<f32>,\n @builtin(position) position: vec4<f32>,\n) -> @location(0) vec4<f32> {\n\n var sample = textureSample(uTexture, uSampler, uv) * color;\n \n return sample;\n}";
|
|
1885
|
+
|
|
1886
|
+
class ParticleShader extends Shader {
|
|
1887
|
+
constructor() {
|
|
1888
|
+
const glProgram = GlProgram.from({
|
|
1889
|
+
vertex,
|
|
1890
|
+
fragment
|
|
1891
|
+
});
|
|
1892
|
+
const gpuProgram = GpuProgram.from({
|
|
1893
|
+
fragment: {
|
|
1894
|
+
source: wgsl,
|
|
1895
|
+
entryPoint: "mainFragment"
|
|
1896
|
+
},
|
|
1897
|
+
vertex: {
|
|
1898
|
+
source: wgsl,
|
|
1899
|
+
entryPoint: "mainVertex"
|
|
1900
|
+
}
|
|
1901
|
+
});
|
|
1902
|
+
super({
|
|
1903
|
+
glProgram,
|
|
1904
|
+
gpuProgram,
|
|
1905
|
+
resources: {
|
|
1906
|
+
// this will be replaced with the texture from the particle container
|
|
1907
|
+
uTexture: Texture.WHITE.source,
|
|
1908
|
+
// this will be replaced with the texture style from the particle container
|
|
1909
|
+
uSampler: new TextureStyle({}),
|
|
1910
|
+
// this will be replaced with the local uniforms from the particle container
|
|
1911
|
+
uniforms: {
|
|
1912
|
+
uTranslationMatrix: { value: new Matrix(), type: "mat3x3<f32>" },
|
|
1913
|
+
uColor: { value: new Color(16777215), type: "vec4<f32>" },
|
|
1914
|
+
uRound: { value: 1, type: "f32" },
|
|
1915
|
+
uResolution: { value: [0, 0], type: "vec2<f32>" }
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
});
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
class ParticleContainerPipe {
|
|
1923
|
+
/**
|
|
1924
|
+
* @param renderer - The renderer this sprite batch works for.
|
|
1925
|
+
* @param adaptor
|
|
1926
|
+
*/
|
|
1927
|
+
constructor(renderer, adaptor) {
|
|
1928
|
+
this.state = State.for2d();
|
|
1929
|
+
this._gpuBufferHash = /* @__PURE__ */ Object.create(null);
|
|
1930
|
+
// eslint-disable-next-line max-len
|
|
1931
|
+
this._destroyRenderableBound = this.destroyRenderable.bind(this);
|
|
1932
|
+
this.localUniforms = new UniformGroup({
|
|
1933
|
+
uTranslationMatrix: { value: new Matrix(), type: "mat3x3<f32>" },
|
|
1934
|
+
uColor: { value: new Float32Array(4), type: "vec4<f32>" },
|
|
1935
|
+
uRound: { value: 1, type: "f32" },
|
|
1936
|
+
uResolution: { value: [0, 0], type: "vec2<f32>" }
|
|
1937
|
+
});
|
|
1938
|
+
this.renderer = renderer;
|
|
1939
|
+
this.adaptor = adaptor;
|
|
1940
|
+
this.defaultShader = new ParticleShader();
|
|
1941
|
+
this.state = State.for2d();
|
|
1942
|
+
}
|
|
1943
|
+
validateRenderable(_renderable) {
|
|
1944
|
+
return false;
|
|
1945
|
+
}
|
|
1946
|
+
addRenderable(renderable, instructionSet) {
|
|
1947
|
+
this.renderer.renderPipes.batch.break(instructionSet);
|
|
1948
|
+
instructionSet.add(renderable);
|
|
1949
|
+
}
|
|
1950
|
+
getBuffers(renderable) {
|
|
1951
|
+
return this._gpuBufferHash[renderable.uid] || this._initBuffer(renderable);
|
|
1952
|
+
}
|
|
1953
|
+
_initBuffer(renderable) {
|
|
1954
|
+
this._gpuBufferHash[renderable.uid] = new ParticleBuffer({
|
|
1955
|
+
size: renderable.particleChildren.length,
|
|
1956
|
+
properties: renderable._properties
|
|
1957
|
+
});
|
|
1958
|
+
renderable.on("destroyed", this._destroyRenderableBound);
|
|
1959
|
+
return this._gpuBufferHash[renderable.uid];
|
|
1960
|
+
}
|
|
1961
|
+
updateRenderable(_renderable) {
|
|
1962
|
+
}
|
|
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
|
+
execute(container) {
|
|
1970
|
+
const children = container.particleChildren;
|
|
1971
|
+
if (children.length === 0) {
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
const renderer = this.renderer;
|
|
1975
|
+
const buffer = this.getBuffers(container);
|
|
1976
|
+
container.texture || (container.texture = children[0].texture);
|
|
1977
|
+
const state = this.state;
|
|
1978
|
+
buffer.update(children, container._childrenDirty);
|
|
1979
|
+
container._childrenDirty = false;
|
|
1980
|
+
state.blendMode = getAdjustedBlendModeBlend(container.blendMode, container.texture._source);
|
|
1981
|
+
const uniforms = this.localUniforms.uniforms;
|
|
1982
|
+
const transformationMatrix = uniforms.uTranslationMatrix;
|
|
1983
|
+
container.worldTransform.copyTo(transformationMatrix);
|
|
1984
|
+
transformationMatrix.prepend(renderer.globalUniforms.globalUniformData.projectionMatrix);
|
|
1985
|
+
uniforms.uResolution = renderer.globalUniforms.globalUniformData.resolution;
|
|
1986
|
+
uniforms.uRound = renderer._roundPixels | container._roundPixels;
|
|
1987
|
+
color32BitToUniform(
|
|
1988
|
+
container.groupColorAlpha,
|
|
1989
|
+
uniforms.uColor,
|
|
1990
|
+
0
|
|
1991
|
+
);
|
|
1992
|
+
this.adaptor.execute(this, container);
|
|
1993
|
+
}
|
|
1994
|
+
/** Destroys the ParticleRenderer. */
|
|
1995
|
+
destroy() {
|
|
1996
|
+
if (this.defaultShader) {
|
|
1997
|
+
this.defaultShader.destroy();
|
|
1998
|
+
this.defaultShader = null;
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
class GlParticleContainerPipe extends ParticleContainerPipe {
|
|
2004
|
+
constructor(renderer) {
|
|
2005
|
+
super(renderer, new GlParticleContainerAdaptor());
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
/** @ignore */
|
|
2009
|
+
GlParticleContainerPipe.extension = {
|
|
2010
|
+
type: [
|
|
2011
|
+
ExtensionType.WebGLPipes
|
|
2012
|
+
],
|
|
2013
|
+
name: "particle"
|
|
2014
|
+
};
|
|
2015
|
+
|
|
2016
|
+
class GpuParticleContainerPipe extends ParticleContainerPipe {
|
|
2017
|
+
constructor(renderer) {
|
|
2018
|
+
super(renderer, new GpuParticleContainerAdaptor());
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
/** @ignore */
|
|
2022
|
+
GpuParticleContainerPipe.extension = {
|
|
2023
|
+
type: [
|
|
2024
|
+
ExtensionType.WebGPUPipes
|
|
2025
|
+
],
|
|
2026
|
+
name: "particle"
|
|
2027
|
+
};
|
|
2028
|
+
|
|
2029
|
+
const _NineSliceGeometry = class _NineSliceGeometry extends PlaneGeometry {
|
|
2030
|
+
constructor(options = {}) {
|
|
2031
|
+
options = { ..._NineSliceGeometry.defaultOptions, ...options };
|
|
2032
|
+
super({
|
|
2033
|
+
width: options.width,
|
|
2034
|
+
height: options.height,
|
|
2035
|
+
verticesX: 4,
|
|
2036
|
+
verticesY: 4
|
|
2037
|
+
});
|
|
2038
|
+
this.update(options);
|
|
2039
|
+
}
|
|
2040
|
+
/**
|
|
2041
|
+
* Updates the NineSliceGeometry with the options.
|
|
2042
|
+
* @param options - The options of the NineSliceGeometry.
|
|
2043
|
+
*/
|
|
2044
|
+
update(options) {
|
|
2045
|
+
this.width = options.width ?? this.width;
|
|
2046
|
+
this.height = options.height ?? this.height;
|
|
2047
|
+
this._originalWidth = options.originalWidth ?? this._originalWidth;
|
|
2048
|
+
this._originalHeight = options.originalHeight ?? this._originalHeight;
|
|
2049
|
+
this._leftWidth = options.leftWidth ?? this._leftWidth;
|
|
2050
|
+
this._rightWidth = options.rightWidth ?? this._rightWidth;
|
|
2051
|
+
this._topHeight = options.topHeight ?? this._topHeight;
|
|
2052
|
+
this._bottomHeight = options.bottomHeight ?? this._bottomHeight;
|
|
2053
|
+
this._anchorX = options.anchor?.x;
|
|
2054
|
+
this._anchorY = options.anchor?.y;
|
|
2055
|
+
this.updateUvs();
|
|
2056
|
+
this.updatePositions();
|
|
2057
|
+
}
|
|
2058
|
+
/** Updates the positions of the vertices. */
|
|
2059
|
+
updatePositions() {
|
|
2060
|
+
const p = this.positions;
|
|
2061
|
+
const {
|
|
2062
|
+
width,
|
|
2063
|
+
height,
|
|
2064
|
+
_leftWidth,
|
|
2065
|
+
_rightWidth,
|
|
2066
|
+
_topHeight,
|
|
2067
|
+
_bottomHeight,
|
|
2068
|
+
_anchorX,
|
|
2069
|
+
_anchorY
|
|
2070
|
+
} = this;
|
|
2071
|
+
const w = _leftWidth + _rightWidth;
|
|
2072
|
+
const scaleW = width > w ? 1 : width / w;
|
|
2073
|
+
const h = _topHeight + _bottomHeight;
|
|
2074
|
+
const scaleH = height > h ? 1 : height / h;
|
|
2075
|
+
const scale = Math.min(scaleW, scaleH);
|
|
2076
|
+
const anchorOffsetX = _anchorX * width;
|
|
2077
|
+
const anchorOffsetY = _anchorY * height;
|
|
2078
|
+
p[0] = p[8] = p[16] = p[24] = -anchorOffsetX;
|
|
2079
|
+
p[2] = p[10] = p[18] = p[26] = _leftWidth * scale - anchorOffsetX;
|
|
2080
|
+
p[4] = p[12] = p[20] = p[28] = width - _rightWidth * scale - anchorOffsetX;
|
|
2081
|
+
p[6] = p[14] = p[22] = p[30] = width - anchorOffsetX;
|
|
2082
|
+
p[1] = p[3] = p[5] = p[7] = -anchorOffsetY;
|
|
2083
|
+
p[9] = p[11] = p[13] = p[15] = _topHeight * scale - anchorOffsetY;
|
|
2084
|
+
p[17] = p[19] = p[21] = p[23] = height - _bottomHeight * scale - anchorOffsetY;
|
|
2085
|
+
p[25] = p[27] = p[29] = p[31] = height - anchorOffsetY;
|
|
2086
|
+
this.getBuffer("aPosition").update();
|
|
2087
|
+
}
|
|
2088
|
+
/** Updates the UVs of the vertices. */
|
|
2089
|
+
updateUvs() {
|
|
2090
|
+
const uvs = this.uvs;
|
|
2091
|
+
uvs[0] = uvs[8] = uvs[16] = uvs[24] = 0;
|
|
2092
|
+
uvs[1] = uvs[3] = uvs[5] = uvs[7] = 0;
|
|
2093
|
+
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
|
|
2094
|
+
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
|
|
2095
|
+
const _uvw = 1 / this._originalWidth;
|
|
2096
|
+
const _uvh = 1 / this._originalHeight;
|
|
2097
|
+
uvs[2] = uvs[10] = uvs[18] = uvs[26] = _uvw * this._leftWidth;
|
|
2098
|
+
uvs[9] = uvs[11] = uvs[13] = uvs[15] = _uvh * this._topHeight;
|
|
2099
|
+
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - _uvw * this._rightWidth;
|
|
2100
|
+
uvs[17] = uvs[19] = uvs[21] = uvs[23] = 1 - _uvh * this._bottomHeight;
|
|
2101
|
+
this.getBuffer("aUV").update();
|
|
2102
|
+
}
|
|
2103
|
+
};
|
|
2104
|
+
/** The default options for the NineSliceGeometry. */
|
|
2105
|
+
_NineSliceGeometry.defaultOptions = {
|
|
2106
|
+
/** The width of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane. */
|
|
2107
|
+
width: 100,
|
|
2108
|
+
/** The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane. */
|
|
2109
|
+
height: 100,
|
|
2110
|
+
/** The width of the left column. */
|
|
2111
|
+
leftWidth: 10,
|
|
2112
|
+
/** The height of the top row. */
|
|
2113
|
+
topHeight: 10,
|
|
2114
|
+
/** The width of the right column. */
|
|
2115
|
+
rightWidth: 10,
|
|
2116
|
+
/** The height of the bottom row. */
|
|
2117
|
+
bottomHeight: 10,
|
|
2118
|
+
/** The original width of the texture */
|
|
2119
|
+
originalWidth: 100,
|
|
2120
|
+
/** The original height of the texture */
|
|
2121
|
+
originalHeight: 100
|
|
2122
|
+
};
|
|
2123
|
+
let NineSliceGeometry = _NineSliceGeometry;
|
|
2124
|
+
|
|
2125
|
+
class NineSliceSpritePipe {
|
|
2126
|
+
constructor(renderer) {
|
|
2127
|
+
this._gpuSpriteHash = /* @__PURE__ */ Object.create(null);
|
|
2128
|
+
this._destroyRenderableBound = this.destroyRenderable.bind(this);
|
|
2129
|
+
this._renderer = renderer;
|
|
2130
|
+
this._renderer.renderableGC.addManagedHash(this, "_gpuSpriteHash");
|
|
2131
|
+
}
|
|
2132
|
+
addRenderable(sprite, instructionSet) {
|
|
2133
|
+
const gpuSprite = this._getGpuSprite(sprite);
|
|
2134
|
+
if (sprite.didViewUpdate)
|
|
2135
|
+
this._updateBatchableSprite(sprite, gpuSprite);
|
|
2136
|
+
this._renderer.renderPipes.batch.addToBatch(gpuSprite, instructionSet);
|
|
2137
|
+
}
|
|
2138
|
+
updateRenderable(sprite) {
|
|
2139
|
+
const gpuSprite = this._gpuSpriteHash[sprite.uid];
|
|
2140
|
+
if (sprite.didViewUpdate)
|
|
2141
|
+
this._updateBatchableSprite(sprite, gpuSprite);
|
|
2142
|
+
gpuSprite._batcher.updateElement(gpuSprite);
|
|
2143
|
+
}
|
|
2144
|
+
validateRenderable(sprite) {
|
|
2145
|
+
const gpuSprite = this._getGpuSprite(sprite);
|
|
2146
|
+
return !gpuSprite._batcher.checkAndUpdateTexture(
|
|
2147
|
+
gpuSprite,
|
|
2148
|
+
sprite._texture
|
|
2149
|
+
);
|
|
2150
|
+
}
|
|
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
|
+
_updateBatchableSprite(sprite, batchableSprite) {
|
|
2159
|
+
batchableSprite.geometry.update(sprite);
|
|
2160
|
+
batchableSprite.setTexture(sprite._texture);
|
|
2161
|
+
}
|
|
2162
|
+
_getGpuSprite(sprite) {
|
|
2163
|
+
return this._gpuSpriteHash[sprite.uid] || this._initGPUSprite(sprite);
|
|
2164
|
+
}
|
|
2165
|
+
_initGPUSprite(sprite) {
|
|
2166
|
+
const batchableMesh = BigPool.get(BatchableMesh);
|
|
2167
|
+
batchableMesh.geometry = BigPool.get(NineSliceGeometry);
|
|
2168
|
+
batchableMesh.renderable = sprite;
|
|
2169
|
+
batchableMesh.transform = sprite.groupTransform;
|
|
2170
|
+
batchableMesh.texture = sprite._texture;
|
|
2171
|
+
batchableMesh.roundPixels = this._renderer._roundPixels | sprite._roundPixels;
|
|
2172
|
+
this._gpuSpriteHash[sprite.uid] = batchableMesh;
|
|
2173
|
+
if (!sprite.didViewUpdate) {
|
|
2174
|
+
this._updateBatchableSprite(sprite, batchableMesh);
|
|
2175
|
+
}
|
|
2176
|
+
sprite.on("destroyed", this._destroyRenderableBound);
|
|
2177
|
+
return batchableMesh;
|
|
2178
|
+
}
|
|
2179
|
+
destroy() {
|
|
2180
|
+
for (const i in this._gpuSpriteHash) {
|
|
2181
|
+
const batchableMesh = this._gpuSpriteHash[i];
|
|
2182
|
+
batchableMesh.geometry.destroy();
|
|
2183
|
+
}
|
|
2184
|
+
this._gpuSpriteHash = null;
|
|
2185
|
+
this._renderer = null;
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
/** @ignore */
|
|
2189
|
+
NineSliceSpritePipe.extension = {
|
|
2190
|
+
type: [
|
|
2191
|
+
ExtensionType.WebGLPipes,
|
|
2192
|
+
ExtensionType.WebGPUPipes,
|
|
2193
|
+
ExtensionType.CanvasPipes
|
|
2194
|
+
],
|
|
2195
|
+
name: "nineSliceSprite"
|
|
2196
|
+
};
|
|
2197
|
+
|
|
2198
|
+
const tilingBit = {
|
|
2199
|
+
name: "tiling-bit",
|
|
2200
|
+
vertex: {
|
|
2201
|
+
header: (
|
|
2202
|
+
/* wgsl */
|
|
2203
|
+
`
|
|
2204
|
+
struct TilingUniforms {
|
|
2205
|
+
uMapCoord:mat3x3<f32>,
|
|
2206
|
+
uClampFrame:vec4<f32>,
|
|
2207
|
+
uClampOffset:vec2<f32>,
|
|
2208
|
+
uTextureTransform:mat3x3<f32>,
|
|
2209
|
+
uSizeAnchor:vec4<f32>
|
|
2210
|
+
};
|
|
2211
|
+
|
|
2212
|
+
@group(2) @binding(0) var<uniform> tilingUniforms: TilingUniforms;
|
|
2213
|
+
@group(2) @binding(1) var uTexture: texture_2d<f32>;
|
|
2214
|
+
@group(2) @binding(2) var uSampler: sampler;
|
|
2215
|
+
`
|
|
2216
|
+
),
|
|
2217
|
+
main: (
|
|
2218
|
+
/* wgsl */
|
|
2219
|
+
`
|
|
2220
|
+
uv = (tilingUniforms.uTextureTransform * vec3(uv, 1.0)).xy;
|
|
2221
|
+
|
|
2222
|
+
position = (position - tilingUniforms.uSizeAnchor.zw) * tilingUniforms.uSizeAnchor.xy;
|
|
2223
|
+
`
|
|
2224
|
+
)
|
|
2225
|
+
},
|
|
2226
|
+
fragment: {
|
|
2227
|
+
header: (
|
|
2228
|
+
/* wgsl */
|
|
2229
|
+
`
|
|
2230
|
+
struct TilingUniforms {
|
|
2231
|
+
uMapCoord:mat3x3<f32>,
|
|
2232
|
+
uClampFrame:vec4<f32>,
|
|
2233
|
+
uClampOffset:vec2<f32>,
|
|
2234
|
+
uTextureTransform:mat3x3<f32>,
|
|
2235
|
+
uSizeAnchor:vec4<f32>
|
|
2236
|
+
};
|
|
2237
|
+
|
|
2238
|
+
@group(2) @binding(0) var<uniform> tilingUniforms: TilingUniforms;
|
|
2239
|
+
@group(2) @binding(1) var uTexture: texture_2d<f32>;
|
|
2240
|
+
@group(2) @binding(2) var uSampler: sampler;
|
|
2241
|
+
`
|
|
2242
|
+
),
|
|
2243
|
+
main: (
|
|
2244
|
+
/* wgsl */
|
|
2245
|
+
`
|
|
2246
|
+
|
|
2247
|
+
var coord = vUV + ceil(tilingUniforms.uClampOffset - vUV);
|
|
2248
|
+
coord = (tilingUniforms.uMapCoord * vec3(coord, 1.0)).xy;
|
|
2249
|
+
var unclamped = coord;
|
|
2250
|
+
coord = clamp(coord, tilingUniforms.uClampFrame.xy, tilingUniforms.uClampFrame.zw);
|
|
2251
|
+
|
|
2252
|
+
var bias = 0.;
|
|
2253
|
+
|
|
2254
|
+
if(unclamped.x == coord.x && unclamped.y == coord.y)
|
|
2255
|
+
{
|
|
2256
|
+
bias = -32.;
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
outColor = textureSampleBias(uTexture, uSampler, coord, bias);
|
|
2260
|
+
`
|
|
2261
|
+
)
|
|
2262
|
+
}
|
|
2263
|
+
};
|
|
2264
|
+
const tilingBitGl = {
|
|
2265
|
+
name: "tiling-bit",
|
|
2266
|
+
vertex: {
|
|
2267
|
+
header: (
|
|
2268
|
+
/* glsl */
|
|
2269
|
+
`
|
|
2270
|
+
uniform mat3 uTextureTransform;
|
|
2271
|
+
uniform vec4 uSizeAnchor;
|
|
2272
|
+
|
|
2273
|
+
`
|
|
2274
|
+
),
|
|
2275
|
+
main: (
|
|
2276
|
+
/* glsl */
|
|
2277
|
+
`
|
|
2278
|
+
uv = (uTextureTransform * vec3(aUV, 1.0)).xy;
|
|
2279
|
+
|
|
2280
|
+
position = (position - uSizeAnchor.zw) * uSizeAnchor.xy;
|
|
2281
|
+
`
|
|
2282
|
+
)
|
|
2283
|
+
},
|
|
2284
|
+
fragment: {
|
|
2285
|
+
header: (
|
|
2286
|
+
/* glsl */
|
|
2287
|
+
`
|
|
2288
|
+
uniform sampler2D uTexture;
|
|
2289
|
+
uniform mat3 uMapCoord;
|
|
2290
|
+
uniform vec4 uClampFrame;
|
|
2291
|
+
uniform vec2 uClampOffset;
|
|
2292
|
+
`
|
|
2293
|
+
),
|
|
2294
|
+
main: (
|
|
2295
|
+
/* glsl */
|
|
2296
|
+
`
|
|
2297
|
+
|
|
2298
|
+
vec2 coord = vUV + ceil(uClampOffset - vUV);
|
|
2299
|
+
coord = (uMapCoord * vec3(coord, 1.0)).xy;
|
|
2300
|
+
vec2 unclamped = coord;
|
|
2301
|
+
coord = clamp(coord, uClampFrame.xy, uClampFrame.zw);
|
|
2302
|
+
|
|
2303
|
+
outColor = texture(uTexture, coord, unclamped == coord ? 0.0 : -32.0);// lod-bias very negative to force lod 0
|
|
2304
|
+
|
|
2305
|
+
`
|
|
2306
|
+
)
|
|
2307
|
+
}
|
|
2308
|
+
};
|
|
2309
|
+
|
|
2310
|
+
let gpuProgram$1;
|
|
2311
|
+
let glProgram$1;
|
|
2312
|
+
class TilingSpriteShader extends Shader {
|
|
2313
|
+
constructor() {
|
|
2314
|
+
gpuProgram$1 ?? (gpuProgram$1 = compileHighShaderGpuProgram({
|
|
2315
|
+
name: "tiling-sprite-shader",
|
|
2316
|
+
bits: [
|
|
2317
|
+
localUniformBit,
|
|
2318
|
+
tilingBit,
|
|
2319
|
+
roundPixelsBit
|
|
2320
|
+
]
|
|
2321
|
+
}));
|
|
2322
|
+
glProgram$1 ?? (glProgram$1 = compileHighShaderGlProgram({
|
|
2323
|
+
name: "tiling-sprite-shader",
|
|
2324
|
+
bits: [
|
|
2325
|
+
localUniformBitGl,
|
|
2326
|
+
tilingBitGl,
|
|
2327
|
+
roundPixelsBitGl
|
|
2328
|
+
]
|
|
2329
|
+
}));
|
|
2330
|
+
const tilingUniforms = new UniformGroup({
|
|
2331
|
+
uMapCoord: { value: new Matrix(), type: "mat3x3<f32>" },
|
|
2332
|
+
uClampFrame: { value: new Float32Array([0, 0, 1, 1]), type: "vec4<f32>" },
|
|
2333
|
+
uClampOffset: { value: new Float32Array([0, 0]), type: "vec2<f32>" },
|
|
2334
|
+
uTextureTransform: { value: new Matrix(), type: "mat3x3<f32>" },
|
|
2335
|
+
uSizeAnchor: { value: new Float32Array([100, 100, 0.5, 0.5]), type: "vec4<f32>" }
|
|
2336
|
+
});
|
|
2337
|
+
super({
|
|
2338
|
+
glProgram: glProgram$1,
|
|
2339
|
+
gpuProgram: gpuProgram$1,
|
|
2340
|
+
resources: {
|
|
2341
|
+
localUniforms: new UniformGroup({
|
|
2342
|
+
uTransformMatrix: { value: new Matrix(), type: "mat3x3<f32>" },
|
|
2343
|
+
uColor: { value: new Float32Array([1, 1, 1, 1]), type: "vec4<f32>" },
|
|
2344
|
+
uRound: { value: 0, type: "f32" }
|
|
2345
|
+
}),
|
|
2346
|
+
tilingUniforms,
|
|
2347
|
+
uTexture: Texture.EMPTY.source,
|
|
2348
|
+
uSampler: Texture.EMPTY.source.style
|
|
2349
|
+
}
|
|
2350
|
+
});
|
|
2351
|
+
}
|
|
2352
|
+
updateUniforms(width, height, matrix, anchorX, anchorY, texture) {
|
|
2353
|
+
const tilingUniforms = this.resources.tilingUniforms;
|
|
2354
|
+
const textureWidth = texture.width;
|
|
2355
|
+
const textureHeight = texture.height;
|
|
2356
|
+
const textureMatrix = texture.textureMatrix;
|
|
2357
|
+
const uTextureTransform = tilingUniforms.uniforms.uTextureTransform;
|
|
2358
|
+
uTextureTransform.set(
|
|
2359
|
+
matrix.a * textureWidth / width,
|
|
2360
|
+
matrix.b * textureWidth / height,
|
|
2361
|
+
matrix.c * textureHeight / width,
|
|
2362
|
+
matrix.d * textureHeight / height,
|
|
2363
|
+
matrix.tx / width,
|
|
2364
|
+
matrix.ty / height
|
|
2365
|
+
);
|
|
2366
|
+
uTextureTransform.invert();
|
|
2367
|
+
tilingUniforms.uniforms.uMapCoord = textureMatrix.mapCoord;
|
|
2368
|
+
tilingUniforms.uniforms.uClampFrame = textureMatrix.uClampFrame;
|
|
2369
|
+
tilingUniforms.uniforms.uClampOffset = textureMatrix.uClampOffset;
|
|
2370
|
+
tilingUniforms.uniforms.uTextureTransform = uTextureTransform;
|
|
2371
|
+
tilingUniforms.uniforms.uSizeAnchor[0] = width;
|
|
2372
|
+
tilingUniforms.uniforms.uSizeAnchor[1] = height;
|
|
2373
|
+
tilingUniforms.uniforms.uSizeAnchor[2] = anchorX;
|
|
2374
|
+
tilingUniforms.uniforms.uSizeAnchor[3] = anchorY;
|
|
2375
|
+
if (texture) {
|
|
2376
|
+
this.resources.uTexture = texture.source;
|
|
2377
|
+
this.resources.uSampler = texture.source.style;
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
class QuadGeometry extends MeshGeometry {
|
|
2383
|
+
constructor() {
|
|
2384
|
+
super({
|
|
2385
|
+
positions: new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]),
|
|
2386
|
+
uvs: new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]),
|
|
2387
|
+
indices: new Uint32Array([0, 1, 2, 0, 2, 3])
|
|
2388
|
+
});
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
function setPositions(tilingSprite, positions) {
|
|
2393
|
+
const anchorX = tilingSprite.anchor.x;
|
|
2394
|
+
const anchorY = tilingSprite.anchor.y;
|
|
2395
|
+
positions[0] = -anchorX * tilingSprite.width;
|
|
2396
|
+
positions[1] = -anchorY * tilingSprite.height;
|
|
2397
|
+
positions[2] = (1 - anchorX) * tilingSprite.width;
|
|
2398
|
+
positions[3] = -anchorY * tilingSprite.height;
|
|
2399
|
+
positions[4] = (1 - anchorX) * tilingSprite.width;
|
|
2400
|
+
positions[5] = (1 - anchorY) * tilingSprite.height;
|
|
2401
|
+
positions[6] = -anchorX * tilingSprite.width;
|
|
2402
|
+
positions[7] = (1 - anchorY) * tilingSprite.height;
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2405
|
+
function applyMatrix(array, stride, offset, matrix) {
|
|
2406
|
+
let index = 0;
|
|
2407
|
+
const size = array.length / (stride);
|
|
2408
|
+
const a = matrix.a;
|
|
2409
|
+
const b = matrix.b;
|
|
2410
|
+
const c = matrix.c;
|
|
2411
|
+
const d = matrix.d;
|
|
2412
|
+
const tx = matrix.tx;
|
|
2413
|
+
const ty = matrix.ty;
|
|
2414
|
+
offset *= stride;
|
|
2415
|
+
while (index < size) {
|
|
2416
|
+
const x = array[offset];
|
|
2417
|
+
const y = array[offset + 1];
|
|
2418
|
+
array[offset] = a * x + c * y + tx;
|
|
2419
|
+
array[offset + 1] = b * x + d * y + ty;
|
|
2420
|
+
offset += stride;
|
|
2421
|
+
index++;
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
function setUvs(tilingSprite, uvs) {
|
|
2426
|
+
const texture = tilingSprite.texture;
|
|
2427
|
+
const width = texture.frame.width;
|
|
2428
|
+
const height = texture.frame.height;
|
|
2429
|
+
let anchorX = 0;
|
|
2430
|
+
let anchorY = 0;
|
|
2431
|
+
if (tilingSprite.applyAnchorToTexture) {
|
|
2432
|
+
anchorX = tilingSprite.anchor.x;
|
|
2433
|
+
anchorY = tilingSprite.anchor.y;
|
|
2434
|
+
}
|
|
2435
|
+
uvs[0] = uvs[6] = -anchorX;
|
|
2436
|
+
uvs[2] = uvs[4] = 1 - anchorX;
|
|
2437
|
+
uvs[1] = uvs[3] = -anchorY;
|
|
2438
|
+
uvs[5] = uvs[7] = 1 - anchorY;
|
|
2439
|
+
const textureMatrix = Matrix.shared;
|
|
2440
|
+
textureMatrix.copyFrom(tilingSprite._tileTransform.matrix);
|
|
2441
|
+
textureMatrix.tx /= tilingSprite.width;
|
|
2442
|
+
textureMatrix.ty /= tilingSprite.height;
|
|
2443
|
+
textureMatrix.invert();
|
|
2444
|
+
textureMatrix.scale(tilingSprite.width / width, tilingSprite.height / height);
|
|
2445
|
+
applyMatrix(uvs, 2, 0, textureMatrix);
|
|
2446
|
+
}
|
|
2447
|
+
|
|
2448
|
+
const sharedQuad = new QuadGeometry();
|
|
2449
|
+
class TilingSpritePipe {
|
|
2450
|
+
constructor(renderer) {
|
|
2451
|
+
this._state = State.default2d;
|
|
2452
|
+
this._tilingSpriteDataHash = /* @__PURE__ */ Object.create(null);
|
|
2453
|
+
this._destroyRenderableBound = this.destroyRenderable.bind(this);
|
|
2454
|
+
this._renderer = renderer;
|
|
2455
|
+
this._renderer.renderableGC.addManagedHash(this, "_tilingSpriteDataHash");
|
|
2456
|
+
}
|
|
2457
|
+
validateRenderable(renderable) {
|
|
2458
|
+
const tilingSpriteData = this._getTilingSpriteData(renderable);
|
|
2459
|
+
const couldBatch = tilingSpriteData.canBatch;
|
|
2460
|
+
this._updateCanBatch(renderable);
|
|
2461
|
+
const canBatch = tilingSpriteData.canBatch;
|
|
2462
|
+
if (canBatch && canBatch === couldBatch) {
|
|
2463
|
+
const { batchableMesh } = tilingSpriteData;
|
|
2464
|
+
return !batchableMesh._batcher.checkAndUpdateTexture(
|
|
2465
|
+
batchableMesh,
|
|
2466
|
+
renderable.texture
|
|
2467
|
+
);
|
|
2468
|
+
}
|
|
2469
|
+
return couldBatch !== canBatch;
|
|
2470
|
+
}
|
|
2471
|
+
addRenderable(tilingSprite, instructionSet) {
|
|
2472
|
+
const batcher = this._renderer.renderPipes.batch;
|
|
2473
|
+
this._updateCanBatch(tilingSprite);
|
|
2474
|
+
const tilingSpriteData = this._getTilingSpriteData(tilingSprite);
|
|
2475
|
+
const { geometry, canBatch } = tilingSpriteData;
|
|
2476
|
+
if (canBatch) {
|
|
2477
|
+
tilingSpriteData.batchableMesh || (tilingSpriteData.batchableMesh = new BatchableMesh());
|
|
2478
|
+
const batchableMesh = tilingSpriteData.batchableMesh;
|
|
2479
|
+
if (tilingSprite.didViewUpdate) {
|
|
2480
|
+
this._updateBatchableMesh(tilingSprite);
|
|
2481
|
+
batchableMesh.geometry = geometry;
|
|
2482
|
+
batchableMesh.renderable = tilingSprite;
|
|
2483
|
+
batchableMesh.transform = tilingSprite.groupTransform;
|
|
2484
|
+
batchableMesh.setTexture(tilingSprite._texture);
|
|
2485
|
+
}
|
|
2486
|
+
batchableMesh.roundPixels = this._renderer._roundPixels | tilingSprite._roundPixels;
|
|
2487
|
+
batcher.addToBatch(batchableMesh, instructionSet);
|
|
2488
|
+
} else {
|
|
2489
|
+
batcher.break(instructionSet);
|
|
2490
|
+
tilingSpriteData.shader || (tilingSpriteData.shader = new TilingSpriteShader());
|
|
2491
|
+
this.updateRenderable(tilingSprite);
|
|
2492
|
+
instructionSet.add(tilingSprite);
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
execute(tilingSprite) {
|
|
2496
|
+
const { shader } = this._tilingSpriteDataHash[tilingSprite.uid];
|
|
2497
|
+
shader.groups[0] = this._renderer.globalUniforms.bindGroup;
|
|
2498
|
+
const localUniforms = shader.resources.localUniforms.uniforms;
|
|
2499
|
+
localUniforms.uTransformMatrix = tilingSprite.groupTransform;
|
|
2500
|
+
localUniforms.uRound = this._renderer._roundPixels | tilingSprite._roundPixels;
|
|
2501
|
+
color32BitToUniform(
|
|
2502
|
+
tilingSprite.groupColorAlpha,
|
|
2503
|
+
localUniforms.uColor,
|
|
2504
|
+
0
|
|
2505
|
+
);
|
|
2506
|
+
this._state.blendMode = getAdjustedBlendModeBlend(tilingSprite.groupBlendMode, tilingSprite.texture._source);
|
|
2507
|
+
this._renderer.encoder.draw({
|
|
2508
|
+
geometry: sharedQuad,
|
|
2509
|
+
shader,
|
|
2510
|
+
state: this._state
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2513
|
+
updateRenderable(tilingSprite) {
|
|
2514
|
+
const tilingSpriteData = this._getTilingSpriteData(tilingSprite);
|
|
2515
|
+
const { canBatch } = tilingSpriteData;
|
|
2516
|
+
if (canBatch) {
|
|
2517
|
+
const { batchableMesh } = tilingSpriteData;
|
|
2518
|
+
if (tilingSprite.didViewUpdate)
|
|
2519
|
+
this._updateBatchableMesh(tilingSprite);
|
|
2520
|
+
batchableMesh._batcher.updateElement(batchableMesh);
|
|
2521
|
+
} else if (tilingSprite.didViewUpdate) {
|
|
2522
|
+
const { shader } = tilingSpriteData;
|
|
2523
|
+
shader.updateUniforms(
|
|
2524
|
+
tilingSprite.width,
|
|
2525
|
+
tilingSprite.height,
|
|
2526
|
+
tilingSprite._tileTransform.matrix,
|
|
2527
|
+
tilingSprite.anchor.x,
|
|
2528
|
+
tilingSprite.anchor.y,
|
|
2529
|
+
tilingSprite.texture
|
|
2530
|
+
);
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
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
|
+
_getTilingSpriteData(renderable) {
|
|
2541
|
+
return this._tilingSpriteDataHash[renderable.uid] || this._initTilingSpriteData(renderable);
|
|
2542
|
+
}
|
|
2543
|
+
_initTilingSpriteData(tilingSprite) {
|
|
2544
|
+
const geometry = new MeshGeometry({
|
|
2545
|
+
indices: sharedQuad.indices,
|
|
2546
|
+
positions: sharedQuad.positions.slice(),
|
|
2547
|
+
uvs: sharedQuad.uvs.slice()
|
|
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];
|
|
2556
|
+
}
|
|
2557
|
+
_updateBatchableMesh(tilingSprite) {
|
|
2558
|
+
const renderableData = this._getTilingSpriteData(tilingSprite);
|
|
2559
|
+
const { geometry } = renderableData;
|
|
2560
|
+
const style = tilingSprite.texture.source.style;
|
|
2561
|
+
if (style.addressMode !== "repeat") {
|
|
2562
|
+
style.addressMode = "repeat";
|
|
2563
|
+
style.update();
|
|
2564
|
+
}
|
|
2565
|
+
setUvs(tilingSprite, geometry.uvs);
|
|
2566
|
+
setPositions(tilingSprite, geometry.positions);
|
|
2567
|
+
}
|
|
2568
|
+
destroy() {
|
|
2569
|
+
for (const i in this._tilingSpriteDataHash) {
|
|
2570
|
+
this.destroyRenderable(this._tilingSpriteDataHash[i].renderable);
|
|
2571
|
+
}
|
|
2572
|
+
this._tilingSpriteDataHash = null;
|
|
2573
|
+
this._renderer = null;
|
|
2574
|
+
}
|
|
2575
|
+
_updateCanBatch(tilingSprite) {
|
|
2576
|
+
const renderableData = this._getTilingSpriteData(tilingSprite);
|
|
2577
|
+
const texture = tilingSprite.texture;
|
|
2578
|
+
let _nonPowOf2wrapping = true;
|
|
2579
|
+
if (this._renderer.type === RendererType.WEBGL) {
|
|
2580
|
+
_nonPowOf2wrapping = this._renderer.context.supports.nonPowOf2wrapping;
|
|
2581
|
+
}
|
|
2582
|
+
renderableData.canBatch = texture.textureMatrix.isSimple && (_nonPowOf2wrapping || texture.source.isPowerOfTwo);
|
|
2583
|
+
return renderableData.canBatch;
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
/** @ignore */
|
|
2587
|
+
TilingSpritePipe.extension = {
|
|
2588
|
+
type: [
|
|
2589
|
+
ExtensionType.WebGLPipes,
|
|
2590
|
+
ExtensionType.WebGPUPipes,
|
|
2591
|
+
ExtensionType.CanvasPipes
|
|
2592
|
+
],
|
|
2593
|
+
name: "tilingSprite"
|
|
2594
|
+
};
|
|
2595
|
+
|
|
2596
|
+
const localUniformMSDFBit = {
|
|
2597
|
+
name: "local-uniform-msdf-bit",
|
|
2598
|
+
vertex: {
|
|
2599
|
+
header: (
|
|
2600
|
+
/* wgsl */
|
|
2601
|
+
`
|
|
2602
|
+
struct LocalUniforms {
|
|
2603
|
+
uColor:vec4<f32>,
|
|
2604
|
+
uTransformMatrix:mat3x3<f32>,
|
|
2605
|
+
uDistance: f32,
|
|
2606
|
+
uRound:f32,
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
@group(2) @binding(0) var<uniform> localUniforms : LocalUniforms;
|
|
2610
|
+
`
|
|
2611
|
+
),
|
|
2612
|
+
main: (
|
|
2613
|
+
/* wgsl */
|
|
2614
|
+
`
|
|
2615
|
+
vColor *= localUniforms.uColor;
|
|
2616
|
+
modelMatrix *= localUniforms.uTransformMatrix;
|
|
2617
|
+
`
|
|
2618
|
+
),
|
|
2619
|
+
end: (
|
|
2620
|
+
/* wgsl */
|
|
2621
|
+
`
|
|
2622
|
+
if(localUniforms.uRound == 1)
|
|
2623
|
+
{
|
|
2624
|
+
vPosition = vec4(roundPixels(vPosition.xy, globalUniforms.uResolution), vPosition.zw);
|
|
2625
|
+
}
|
|
2626
|
+
`
|
|
2627
|
+
)
|
|
2628
|
+
},
|
|
2629
|
+
fragment: {
|
|
2630
|
+
header: (
|
|
2631
|
+
/* wgsl */
|
|
2632
|
+
`
|
|
2633
|
+
struct LocalUniforms {
|
|
2634
|
+
uColor:vec4<f32>,
|
|
2635
|
+
uTransformMatrix:mat3x3<f32>,
|
|
2636
|
+
uDistance: f32
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
@group(2) @binding(0) var<uniform> localUniforms : LocalUniforms;
|
|
2640
|
+
`
|
|
2641
|
+
),
|
|
2642
|
+
main: (
|
|
2643
|
+
/* wgsl */
|
|
2644
|
+
`
|
|
2645
|
+
outColor = vec4<f32>(calculateMSDFAlpha(outColor, localUniforms.uColor, localUniforms.uDistance));
|
|
2646
|
+
`
|
|
2647
|
+
)
|
|
2648
|
+
}
|
|
2649
|
+
};
|
|
2650
|
+
const localUniformMSDFBitGl = {
|
|
2651
|
+
name: "local-uniform-msdf-bit",
|
|
2652
|
+
vertex: {
|
|
2653
|
+
header: (
|
|
2654
|
+
/* glsl */
|
|
2655
|
+
`
|
|
2656
|
+
uniform mat3 uTransformMatrix;
|
|
2657
|
+
uniform vec4 uColor;
|
|
2658
|
+
uniform float uRound;
|
|
2659
|
+
`
|
|
2660
|
+
),
|
|
2661
|
+
main: (
|
|
2662
|
+
/* glsl */
|
|
2663
|
+
`
|
|
2664
|
+
vColor *= uColor;
|
|
2665
|
+
modelMatrix *= uTransformMatrix;
|
|
2666
|
+
`
|
|
2667
|
+
),
|
|
2668
|
+
end: (
|
|
2669
|
+
/* glsl */
|
|
2670
|
+
`
|
|
2671
|
+
if(uRound == 1.)
|
|
2672
|
+
{
|
|
2673
|
+
gl_Position.xy = roundPixels(gl_Position.xy, uResolution);
|
|
2674
|
+
}
|
|
2675
|
+
`
|
|
2676
|
+
)
|
|
2677
|
+
},
|
|
2678
|
+
fragment: {
|
|
2679
|
+
header: (
|
|
2680
|
+
/* glsl */
|
|
2681
|
+
`
|
|
2682
|
+
uniform float uDistance;
|
|
2683
|
+
`
|
|
2684
|
+
),
|
|
2685
|
+
main: (
|
|
2686
|
+
/* glsl */
|
|
2687
|
+
`
|
|
2688
|
+
outColor = vec4(calculateMSDFAlpha(outColor, vColor, uDistance));
|
|
2689
|
+
`
|
|
2690
|
+
)
|
|
2691
|
+
}
|
|
2692
|
+
};
|
|
2693
|
+
|
|
2694
|
+
const mSDFBit = {
|
|
2695
|
+
name: "msdf-bit",
|
|
2696
|
+
fragment: {
|
|
2697
|
+
header: (
|
|
2698
|
+
/* wgsl */
|
|
2699
|
+
`
|
|
2700
|
+
fn calculateMSDFAlpha(msdfColor:vec4<f32>, shapeColor:vec4<f32>, distance:f32) -> f32 {
|
|
2701
|
+
|
|
2702
|
+
// MSDF
|
|
2703
|
+
var median = msdfColor.r + msdfColor.g + msdfColor.b -
|
|
2704
|
+
min(msdfColor.r, min(msdfColor.g, msdfColor.b)) -
|
|
2705
|
+
max(msdfColor.r, max(msdfColor.g, msdfColor.b));
|
|
2706
|
+
|
|
2707
|
+
// SDF
|
|
2708
|
+
median = min(median, msdfColor.a);
|
|
2709
|
+
|
|
2710
|
+
var screenPxDistance = distance * (median - 0.5);
|
|
2711
|
+
var alpha = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
|
2712
|
+
if (median < 0.01) {
|
|
2713
|
+
alpha = 0.0;
|
|
2714
|
+
} else if (median > 0.99) {
|
|
2715
|
+
alpha = 1.0;
|
|
2716
|
+
}
|
|
2717
|
+
|
|
2718
|
+
// Gamma correction for coverage-like alpha
|
|
2719
|
+
var luma: f32 = dot(shapeColor.rgb, vec3<f32>(0.299, 0.587, 0.114));
|
|
2720
|
+
var gamma: f32 = mix(1.0, 1.0 / 2.2, luma);
|
|
2721
|
+
var coverage: f32 = pow(shapeColor.a * alpha, gamma);
|
|
2722
|
+
|
|
2723
|
+
return coverage;
|
|
2724
|
+
|
|
2725
|
+
}
|
|
2726
|
+
`
|
|
2727
|
+
)
|
|
2728
|
+
}
|
|
2729
|
+
};
|
|
2730
|
+
const mSDFBitGl = {
|
|
2731
|
+
name: "msdf-bit",
|
|
2732
|
+
fragment: {
|
|
2733
|
+
header: (
|
|
2734
|
+
/* glsl */
|
|
2735
|
+
`
|
|
2736
|
+
float calculateMSDFAlpha(vec4 msdfColor, vec4 shapeColor, float distance) {
|
|
2737
|
+
|
|
2738
|
+
// MSDF
|
|
2739
|
+
float median = msdfColor.r + msdfColor.g + msdfColor.b -
|
|
2740
|
+
min(msdfColor.r, min(msdfColor.g, msdfColor.b)) -
|
|
2741
|
+
max(msdfColor.r, max(msdfColor.g, msdfColor.b));
|
|
2742
|
+
|
|
2743
|
+
// SDF
|
|
2744
|
+
median = min(median, msdfColor.a);
|
|
2745
|
+
|
|
2746
|
+
float screenPxDistance = distance * (median - 0.5);
|
|
2747
|
+
float alpha = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
|
2748
|
+
|
|
2749
|
+
if (median < 0.01) {
|
|
2750
|
+
alpha = 0.0;
|
|
2751
|
+
} else if (median > 0.99) {
|
|
2752
|
+
alpha = 1.0;
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2755
|
+
// Gamma correction for coverage-like alpha
|
|
2756
|
+
float luma = dot(shapeColor.rgb, vec3(0.299, 0.587, 0.114));
|
|
2757
|
+
float gamma = mix(1.0, 1.0 / 2.2, luma);
|
|
2758
|
+
float coverage = pow(shapeColor.a * alpha, gamma);
|
|
2759
|
+
|
|
2760
|
+
return coverage;
|
|
2761
|
+
}
|
|
2762
|
+
`
|
|
2763
|
+
)
|
|
2764
|
+
}
|
|
2765
|
+
};
|
|
2766
|
+
|
|
2767
|
+
let gpuProgram;
|
|
2768
|
+
let glProgram;
|
|
2769
|
+
class SdfShader extends Shader {
|
|
2770
|
+
constructor() {
|
|
2771
|
+
const uniforms = new UniformGroup({
|
|
2772
|
+
uColor: { value: new Float32Array([1, 1, 1, 1]), type: "vec4<f32>" },
|
|
2773
|
+
uTransformMatrix: { value: new Matrix(), type: "mat3x3<f32>" },
|
|
2774
|
+
uDistance: { value: 4, type: "f32" },
|
|
2775
|
+
uRound: { value: 0, type: "f32" }
|
|
2776
|
+
});
|
|
2777
|
+
const maxTextures = getMaxTexturesPerBatch();
|
|
2778
|
+
gpuProgram ?? (gpuProgram = compileHighShaderGpuProgram({
|
|
2779
|
+
name: "sdf-shader",
|
|
2780
|
+
bits: [
|
|
2781
|
+
colorBit,
|
|
2782
|
+
generateTextureBatchBit(maxTextures),
|
|
2783
|
+
localUniformMSDFBit,
|
|
2784
|
+
mSDFBit,
|
|
2785
|
+
roundPixelsBit
|
|
2786
|
+
]
|
|
2787
|
+
}));
|
|
2788
|
+
glProgram ?? (glProgram = compileHighShaderGlProgram({
|
|
2789
|
+
name: "sdf-shader",
|
|
2790
|
+
bits: [
|
|
2791
|
+
colorBitGl,
|
|
2792
|
+
generateTextureBatchBitGl(maxTextures),
|
|
2793
|
+
localUniformMSDFBitGl,
|
|
2794
|
+
mSDFBitGl,
|
|
2795
|
+
roundPixelsBitGl
|
|
2796
|
+
]
|
|
2797
|
+
}));
|
|
2798
|
+
super({
|
|
2799
|
+
glProgram,
|
|
2800
|
+
gpuProgram,
|
|
2801
|
+
resources: {
|
|
2802
|
+
localUniforms: uniforms,
|
|
2803
|
+
batchSamplers: getBatchSamplersUniformGroup(maxTextures)
|
|
2804
|
+
}
|
|
2805
|
+
});
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
class BitmapTextPipe {
|
|
2810
|
+
constructor(renderer) {
|
|
2811
|
+
this._gpuBitmapText = {};
|
|
2812
|
+
this._destroyRenderableBound = this.destroyRenderable.bind(this);
|
|
2813
|
+
this._renderer = renderer;
|
|
2814
|
+
this._renderer.renderableGC.addManagedHash(this, "_gpuBitmapText");
|
|
2815
|
+
}
|
|
2816
|
+
validateRenderable(bitmapText) {
|
|
2817
|
+
const graphicsRenderable = this._getGpuBitmapText(bitmapText);
|
|
2818
|
+
if (bitmapText._didTextUpdate) {
|
|
2819
|
+
bitmapText._didTextUpdate = false;
|
|
2820
|
+
this._updateContext(bitmapText, graphicsRenderable);
|
|
2821
|
+
}
|
|
2822
|
+
return this._renderer.renderPipes.graphics.validateRenderable(graphicsRenderable);
|
|
2823
|
+
}
|
|
2824
|
+
addRenderable(bitmapText, instructionSet) {
|
|
2825
|
+
const graphicsRenderable = this._getGpuBitmapText(bitmapText);
|
|
2826
|
+
syncWithProxy(bitmapText, graphicsRenderable);
|
|
2827
|
+
if (bitmapText._didTextUpdate) {
|
|
2828
|
+
bitmapText._didTextUpdate = false;
|
|
2829
|
+
this._updateContext(bitmapText, graphicsRenderable);
|
|
2830
|
+
}
|
|
2831
|
+
this._renderer.renderPipes.graphics.addRenderable(graphicsRenderable, instructionSet);
|
|
2832
|
+
if (graphicsRenderable.context.customShader) {
|
|
2833
|
+
this._updateDistanceField(bitmapText);
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
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
|
+
updateRenderable(bitmapText) {
|
|
2850
|
+
const graphicsRenderable = this._getGpuBitmapText(bitmapText);
|
|
2851
|
+
syncWithProxy(bitmapText, graphicsRenderable);
|
|
2852
|
+
this._renderer.renderPipes.graphics.updateRenderable(graphicsRenderable);
|
|
2853
|
+
if (graphicsRenderable.context.customShader) {
|
|
2854
|
+
this._updateDistanceField(bitmapText);
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
_updateContext(bitmapText, proxyGraphics) {
|
|
2858
|
+
const { context } = proxyGraphics;
|
|
2859
|
+
const bitmapFont = BitmapFontManager.getFont(bitmapText.text, bitmapText._style);
|
|
2860
|
+
context.clear();
|
|
2861
|
+
if (bitmapFont.distanceField.type !== "none") {
|
|
2862
|
+
if (!context.customShader) {
|
|
2863
|
+
context.customShader = BigPool.get(SdfShader);
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
const chars = Array.from(bitmapText.text);
|
|
2867
|
+
const style = bitmapText._style;
|
|
2868
|
+
let currentY = bitmapFont.baseLineOffset;
|
|
2869
|
+
const bitmapTextLayout = getBitmapTextLayout(chars, style, bitmapFont, true);
|
|
2870
|
+
let index = 0;
|
|
2871
|
+
const padding = style.padding;
|
|
2872
|
+
const scale = bitmapTextLayout.scale;
|
|
2873
|
+
let tx = bitmapTextLayout.width;
|
|
2874
|
+
let ty = bitmapTextLayout.height + bitmapTextLayout.offsetY;
|
|
2875
|
+
if (style._stroke) {
|
|
2876
|
+
tx += style._stroke.width / scale;
|
|
2877
|
+
ty += style._stroke.width / scale;
|
|
2878
|
+
}
|
|
2879
|
+
context.translate(-bitmapText._anchor._x * tx - padding, -bitmapText._anchor._y * ty - padding).scale(scale, scale);
|
|
2880
|
+
const tint = bitmapFont.applyFillAsTint ? style._fill.color : 16777215;
|
|
2881
|
+
for (let i = 0; i < bitmapTextLayout.lines.length; i++) {
|
|
2882
|
+
const line = bitmapTextLayout.lines[i];
|
|
2883
|
+
for (let j = 0; j < line.charPositions.length; j++) {
|
|
2884
|
+
const char = chars[index++];
|
|
2885
|
+
const charData = bitmapFont.chars[char];
|
|
2886
|
+
if (charData?.texture) {
|
|
2887
|
+
context.texture(
|
|
2888
|
+
charData.texture,
|
|
2889
|
+
tint ? tint : "black",
|
|
2890
|
+
Math.round(line.charPositions[j] + charData.xOffset),
|
|
2891
|
+
Math.round(currentY + charData.yOffset)
|
|
2892
|
+
);
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
currentY += bitmapFont.lineHeight;
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
_getGpuBitmapText(bitmapText) {
|
|
2899
|
+
return this._gpuBitmapText[bitmapText.uid] || this.initGpuText(bitmapText);
|
|
2900
|
+
}
|
|
2901
|
+
initGpuText(bitmapText) {
|
|
2902
|
+
const proxyRenderable = BigPool.get(Graphics);
|
|
2903
|
+
this._gpuBitmapText[bitmapText.uid] = proxyRenderable;
|
|
2904
|
+
this._updateContext(bitmapText, proxyRenderable);
|
|
2905
|
+
bitmapText.on("destroyed", this._destroyRenderableBound);
|
|
2906
|
+
return this._gpuBitmapText[bitmapText.uid];
|
|
2907
|
+
}
|
|
2908
|
+
_updateDistanceField(bitmapText) {
|
|
2909
|
+
const context = this._getGpuBitmapText(bitmapText).context;
|
|
2910
|
+
const fontFamily = bitmapText._style.fontFamily;
|
|
2911
|
+
const dynamicFont = Cache.get(`${fontFamily}-bitmap`);
|
|
2912
|
+
const { a, b, c, d } = bitmapText.groupTransform;
|
|
2913
|
+
const dx = Math.sqrt(a * a + b * b);
|
|
2914
|
+
const dy = Math.sqrt(c * c + d * d);
|
|
2915
|
+
const worldScale = (Math.abs(dx) + Math.abs(dy)) / 2;
|
|
2916
|
+
const fontScale = dynamicFont.baseRenderedFontSize / bitmapText._style.fontSize;
|
|
2917
|
+
const distance = worldScale * dynamicFont.distanceField.range * (1 / fontScale);
|
|
2918
|
+
context.customShader.resources.localUniforms.uniforms.uDistance = distance;
|
|
2919
|
+
}
|
|
2920
|
+
destroy() {
|
|
2921
|
+
for (const uid in this._gpuBitmapText) {
|
|
2922
|
+
this._destroyRenderableByUid(uid);
|
|
2923
|
+
}
|
|
2924
|
+
this._gpuBitmapText = null;
|
|
2925
|
+
this._renderer = null;
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
/** @ignore */
|
|
2929
|
+
BitmapTextPipe.extension = {
|
|
2930
|
+
type: [
|
|
2931
|
+
ExtensionType.WebGLPipes,
|
|
2932
|
+
ExtensionType.WebGPUPipes,
|
|
2933
|
+
ExtensionType.CanvasPipes
|
|
2934
|
+
],
|
|
2935
|
+
name: "bitmapText"
|
|
2936
|
+
};
|
|
2937
|
+
function syncWithProxy(container, proxy) {
|
|
2938
|
+
proxy.groupTransform = container.groupTransform;
|
|
2939
|
+
proxy.groupColorAlpha = container.groupColorAlpha;
|
|
2940
|
+
proxy.groupColor = container.groupColor;
|
|
2941
|
+
proxy.groupBlendMode = container.groupBlendMode;
|
|
2942
|
+
proxy.globalDisplayStatus = container.globalDisplayStatus;
|
|
2943
|
+
proxy.groupTransform = container.groupTransform;
|
|
2944
|
+
proxy.localDisplayStatus = container.localDisplayStatus;
|
|
2945
|
+
proxy.groupAlpha = container.groupAlpha;
|
|
2946
|
+
proxy._roundPixels = container._roundPixels;
|
|
2947
|
+
}
|
|
2948
|
+
|
|
2949
|
+
function updateTextBounds(batchableSprite, text) {
|
|
2950
|
+
const { texture, bounds } = batchableSprite;
|
|
2951
|
+
updateQuadBounds(bounds, text._anchor, texture);
|
|
2952
|
+
const padding = text._style.padding;
|
|
2953
|
+
bounds.minX -= padding;
|
|
2954
|
+
bounds.minY -= padding;
|
|
2955
|
+
bounds.maxX -= padding;
|
|
2956
|
+
bounds.maxY -= padding;
|
|
2957
|
+
}
|
|
2958
|
+
|
|
2959
|
+
class HTMLTextPipe {
|
|
2960
|
+
constructor(renderer) {
|
|
2961
|
+
this._gpuText = /* @__PURE__ */ Object.create(null);
|
|
2962
|
+
this._destroyRenderableBound = this.destroyRenderable.bind(this);
|
|
2963
|
+
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
|
+
}
|
|
2979
|
+
validateRenderable(htmlText) {
|
|
2980
|
+
const gpuText = this._getGpuText(htmlText);
|
|
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;
|
|
2990
|
+
}
|
|
2991
|
+
addRenderable(htmlText, instructionSet) {
|
|
2992
|
+
const gpuText = this._getGpuText(htmlText);
|
|
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;
|
|
3002
|
+
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
|
+
this._updateGpuText(htmlText).catch((e) => {
|
|
3023
|
+
console.error(e);
|
|
3024
|
+
});
|
|
3025
|
+
}
|
|
3026
|
+
htmlText._didTextUpdate = false;
|
|
3027
|
+
updateTextBounds(batchableSprite, htmlText);
|
|
3028
|
+
}
|
|
3029
|
+
async _updateGpuText(htmlText) {
|
|
3030
|
+
htmlText._didTextUpdate = false;
|
|
3031
|
+
const gpuText = this._getGpuText(htmlText);
|
|
3032
|
+
if (gpuText.generatingTexture)
|
|
3033
|
+
return;
|
|
3034
|
+
const newKey = htmlText._getKey();
|
|
3035
|
+
this._renderer.htmlText.decreaseReferenceCount(gpuText.currentKey);
|
|
3036
|
+
gpuText.generatingTexture = true;
|
|
3037
|
+
gpuText.currentKey = newKey;
|
|
3038
|
+
const resolution = htmlText.resolution ?? this._renderer.resolution;
|
|
3039
|
+
const texture = await this._renderer.htmlText.getManagedTexture(
|
|
3040
|
+
htmlText.text,
|
|
3041
|
+
resolution,
|
|
3042
|
+
htmlText._style,
|
|
3043
|
+
htmlText._getKey()
|
|
3044
|
+
);
|
|
3045
|
+
const batchableSprite = gpuText.batchableSprite;
|
|
3046
|
+
batchableSprite.texture = gpuText.texture = texture;
|
|
3047
|
+
gpuText.generatingTexture = false;
|
|
3048
|
+
gpuText.textureNeedsUploading = true;
|
|
3049
|
+
htmlText.onViewUpdate();
|
|
3050
|
+
updateTextBounds(batchableSprite, htmlText);
|
|
3051
|
+
}
|
|
3052
|
+
_getGpuText(htmlText) {
|
|
3053
|
+
return this._gpuText[htmlText.uid] || this.initGpuText(htmlText);
|
|
3054
|
+
}
|
|
3055
|
+
initGpuText(htmlText) {
|
|
3056
|
+
const gpuTextData = {
|
|
3057
|
+
texture: Texture.EMPTY,
|
|
3058
|
+
currentKey: "--",
|
|
3059
|
+
batchableSprite: BigPool.get(BatchableSprite),
|
|
3060
|
+
textureNeedsUploading: false,
|
|
3061
|
+
generatingTexture: false
|
|
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;
|
|
3069
|
+
htmlText._resolution = htmlText._autoResolution ? this._renderer.resolution : htmlText.resolution;
|
|
3070
|
+
this._gpuText[htmlText.uid] = gpuTextData;
|
|
3071
|
+
htmlText.on("destroyed", this._destroyRenderableBound);
|
|
3072
|
+
return gpuTextData;
|
|
3073
|
+
}
|
|
3074
|
+
destroy() {
|
|
3075
|
+
for (const i in this._gpuText) {
|
|
3076
|
+
this._destroyRenderableById(i);
|
|
3077
|
+
}
|
|
3078
|
+
this._gpuText = null;
|
|
3079
|
+
this._renderer = null;
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
/** @ignore */
|
|
3083
|
+
HTMLTextPipe.extension = {
|
|
3084
|
+
type: [
|
|
3085
|
+
ExtensionType.WebGLPipes,
|
|
3086
|
+
ExtensionType.WebGPUPipes,
|
|
3087
|
+
ExtensionType.CanvasPipes
|
|
3088
|
+
],
|
|
3089
|
+
name: "htmlText"
|
|
3090
|
+
};
|
|
3091
|
+
|
|
3092
|
+
function isSafari() {
|
|
3093
|
+
const { userAgent } = DOMAdapter.get().getNavigator();
|
|
3094
|
+
return /^((?!chrome|android).)*safari/i.test(userAgent);
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
const tempBounds = new Bounds();
|
|
3098
|
+
function getPo2TextureFromSource(image, width, height, resolution) {
|
|
3099
|
+
const bounds = tempBounds;
|
|
3100
|
+
bounds.minX = 0;
|
|
3101
|
+
bounds.minY = 0;
|
|
3102
|
+
bounds.maxX = image.width / resolution | 0;
|
|
3103
|
+
bounds.maxY = image.height / resolution | 0;
|
|
3104
|
+
const texture = TexturePool.getOptimalTexture(
|
|
3105
|
+
bounds.width,
|
|
3106
|
+
bounds.height,
|
|
3107
|
+
resolution,
|
|
3108
|
+
false
|
|
3109
|
+
);
|
|
3110
|
+
texture.source.uploadMethodId = "image";
|
|
3111
|
+
texture.source.resource = image;
|
|
3112
|
+
texture.source.alphaMode = "premultiply-alpha-on-upload";
|
|
3113
|
+
texture.frame.width = width / resolution;
|
|
3114
|
+
texture.frame.height = height / resolution;
|
|
3115
|
+
texture.source.emit("update", texture.source);
|
|
3116
|
+
texture.updateUvs();
|
|
3117
|
+
return texture;
|
|
3118
|
+
}
|
|
3119
|
+
|
|
3120
|
+
function extractFontFamilies(text, style) {
|
|
3121
|
+
const fontFamily = style.fontFamily;
|
|
3122
|
+
const fontFamilies = [];
|
|
3123
|
+
const dedupe = {};
|
|
3124
|
+
const regex = /font-family:([^;"\s]+)/g;
|
|
3125
|
+
const matches = text.match(regex);
|
|
3126
|
+
function addFontFamily(fontFamily2) {
|
|
3127
|
+
if (!dedupe[fontFamily2]) {
|
|
3128
|
+
fontFamilies.push(fontFamily2);
|
|
3129
|
+
dedupe[fontFamily2] = true;
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
if (Array.isArray(fontFamily)) {
|
|
3133
|
+
for (let i = 0; i < fontFamily.length; i++) {
|
|
3134
|
+
addFontFamily(fontFamily[i]);
|
|
3135
|
+
}
|
|
3136
|
+
} else {
|
|
3137
|
+
addFontFamily(fontFamily);
|
|
3138
|
+
}
|
|
3139
|
+
if (matches) {
|
|
3140
|
+
matches.forEach((match) => {
|
|
3141
|
+
const fontFamily2 = match.split(":")[1].trim();
|
|
3142
|
+
addFontFamily(fontFamily2);
|
|
3143
|
+
});
|
|
3144
|
+
}
|
|
3145
|
+
for (const i in style.tagStyles) {
|
|
3146
|
+
const fontFamily2 = style.tagStyles[i].fontFamily;
|
|
3147
|
+
addFontFamily(fontFamily2);
|
|
3148
|
+
}
|
|
3149
|
+
return fontFamilies;
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
async function loadFontAsBase64(url) {
|
|
3153
|
+
const response = await DOMAdapter.get().fetch(url);
|
|
3154
|
+
const blob = await response.blob();
|
|
3155
|
+
const reader = new FileReader();
|
|
3156
|
+
const dataSrc = await new Promise((resolve, reject) => {
|
|
3157
|
+
reader.onloadend = () => resolve(reader.result);
|
|
3158
|
+
reader.onerror = reject;
|
|
3159
|
+
reader.readAsDataURL(blob);
|
|
3160
|
+
});
|
|
3161
|
+
return dataSrc;
|
|
3162
|
+
}
|
|
3163
|
+
|
|
3164
|
+
async function loadFontCSS(style, url) {
|
|
3165
|
+
const dataSrc = await loadFontAsBase64(url);
|
|
3166
|
+
return `@font-face {
|
|
3167
|
+
font-family: "${style.fontFamily}";
|
|
3168
|
+
src: url('${dataSrc}');
|
|
3169
|
+
font-weight: ${style.fontWeight};
|
|
3170
|
+
font-style: ${style.fontStyle};
|
|
3171
|
+
}`;
|
|
3172
|
+
}
|
|
3173
|
+
|
|
3174
|
+
const FontStylePromiseCache = /* @__PURE__ */ new Map();
|
|
3175
|
+
async function getFontCss(fontFamilies, style, defaultOptions) {
|
|
3176
|
+
const fontPromises = fontFamilies.filter((fontFamily) => Cache.has(`${fontFamily}-and-url`)).map((fontFamily, i) => {
|
|
3177
|
+
if (!FontStylePromiseCache.has(fontFamily)) {
|
|
3178
|
+
const { url } = Cache.get(`${fontFamily}-and-url`);
|
|
3179
|
+
if (i === 0) {
|
|
3180
|
+
FontStylePromiseCache.set(fontFamily, loadFontCSS({
|
|
3181
|
+
fontWeight: style.fontWeight,
|
|
3182
|
+
fontStyle: style.fontStyle,
|
|
3183
|
+
fontFamily
|
|
3184
|
+
}, url));
|
|
3185
|
+
} else {
|
|
3186
|
+
FontStylePromiseCache.set(fontFamily, loadFontCSS({
|
|
3187
|
+
fontWeight: defaultOptions.fontWeight,
|
|
3188
|
+
fontStyle: defaultOptions.fontStyle,
|
|
3189
|
+
fontFamily
|
|
3190
|
+
}, url));
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
return FontStylePromiseCache.get(fontFamily);
|
|
3194
|
+
});
|
|
3195
|
+
return (await Promise.all(fontPromises)).join("\n");
|
|
3196
|
+
}
|
|
3197
|
+
|
|
3198
|
+
function getSVGUrl(text, style, resolution, fontCSS, htmlTextData) {
|
|
3199
|
+
const { domElement, styleElement, svgRoot } = htmlTextData;
|
|
3200
|
+
domElement.innerHTML = `<style>${style.cssStyle}</style><div style='padding:0;'>${text}</div>`;
|
|
3201
|
+
domElement.setAttribute("style", `transform: scale(${resolution});transform-origin: top left; display: inline-block`);
|
|
3202
|
+
styleElement.textContent = fontCSS;
|
|
3203
|
+
const { width, height } = htmlTextData.image;
|
|
3204
|
+
svgRoot.setAttribute("width", width.toString());
|
|
3205
|
+
svgRoot.setAttribute("height", height.toString());
|
|
3206
|
+
return new XMLSerializer().serializeToString(svgRoot);
|
|
3207
|
+
}
|
|
3208
|
+
|
|
3209
|
+
function getTemporaryCanvasFromImage(image, resolution) {
|
|
3210
|
+
const canvasAndContext = CanvasPool.getOptimalCanvasAndContext(
|
|
3211
|
+
image.width,
|
|
3212
|
+
image.height,
|
|
3213
|
+
resolution
|
|
3214
|
+
);
|
|
3215
|
+
const { context } = canvasAndContext;
|
|
3216
|
+
context.clearRect(0, 0, image.width, image.height);
|
|
3217
|
+
context.drawImage(image, 0, 0);
|
|
3218
|
+
return canvasAndContext;
|
|
3219
|
+
}
|
|
3220
|
+
|
|
3221
|
+
function loadSVGImage(image, url, delay) {
|
|
3222
|
+
return new Promise(async (resolve) => {
|
|
3223
|
+
if (delay) {
|
|
3224
|
+
await new Promise((resolve2) => setTimeout(resolve2, 100));
|
|
3225
|
+
}
|
|
3226
|
+
image.onload = () => {
|
|
3227
|
+
resolve();
|
|
3228
|
+
};
|
|
3229
|
+
image.src = `data:image/svg+xml;charset=utf8,${encodeURIComponent(url)}`;
|
|
3230
|
+
image.crossOrigin = "anonymous";
|
|
3231
|
+
});
|
|
3232
|
+
}
|
|
3233
|
+
|
|
3234
|
+
class HTMLTextSystem {
|
|
3235
|
+
constructor(renderer) {
|
|
3236
|
+
this._activeTextures = {};
|
|
3237
|
+
this._renderer = renderer;
|
|
3238
|
+
this._createCanvas = renderer.type === RendererType.WEBGPU;
|
|
3239
|
+
}
|
|
3240
|
+
getTexture(options) {
|
|
3241
|
+
return this._buildTexturePromise(
|
|
3242
|
+
options.text,
|
|
3243
|
+
options.resolution,
|
|
3244
|
+
options.style
|
|
3245
|
+
);
|
|
3246
|
+
}
|
|
3247
|
+
getManagedTexture(text, resolution, style, textKey) {
|
|
3248
|
+
if (this._activeTextures[textKey]) {
|
|
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;
|
|
3262
|
+
}
|
|
3263
|
+
async _buildTexturePromise(text, resolution, style) {
|
|
3264
|
+
const htmlTextData = BigPool.get(HTMLTextRenderData);
|
|
3265
|
+
const fontFamilies = extractFontFamilies(text, style);
|
|
3266
|
+
const fontCSS = await getFontCss(
|
|
3267
|
+
fontFamilies,
|
|
3268
|
+
style,
|
|
3269
|
+
HTMLTextStyle.defaultTextStyle
|
|
3270
|
+
);
|
|
3271
|
+
const measured = measureHtmlText(text, style, fontCSS, htmlTextData);
|
|
3272
|
+
const width = Math.ceil(Math.ceil(Math.max(1, measured.width) + style.padding * 2) * resolution);
|
|
3273
|
+
const height = Math.ceil(Math.ceil(Math.max(1, measured.height) + style.padding * 2) * resolution);
|
|
3274
|
+
const image = htmlTextData.image;
|
|
3275
|
+
const uvSafeOffset = 2;
|
|
3276
|
+
image.width = (width | 0) + uvSafeOffset;
|
|
3277
|
+
image.height = (height | 0) + uvSafeOffset;
|
|
3278
|
+
const svgURL = getSVGUrl(text, style, resolution, fontCSS, htmlTextData);
|
|
3279
|
+
await loadSVGImage(image, svgURL, isSafari() && fontFamilies.length > 0);
|
|
3280
|
+
const resource = image;
|
|
3281
|
+
let canvasAndContext;
|
|
3282
|
+
if (this._createCanvas) {
|
|
3283
|
+
canvasAndContext = getTemporaryCanvasFromImage(image, resolution);
|
|
3284
|
+
}
|
|
3285
|
+
const texture = getPo2TextureFromSource(
|
|
3286
|
+
canvasAndContext ? canvasAndContext.canvas : resource,
|
|
3287
|
+
image.width - uvSafeOffset,
|
|
3288
|
+
image.height - uvSafeOffset,
|
|
3289
|
+
resolution
|
|
3290
|
+
);
|
|
3291
|
+
if (this._createCanvas) {
|
|
3292
|
+
this._renderer.texture.initSource(texture.source);
|
|
3293
|
+
CanvasPool.returnCanvasAndContext(canvasAndContext);
|
|
3294
|
+
}
|
|
3295
|
+
BigPool.return(htmlTextData);
|
|
3296
|
+
return texture;
|
|
3297
|
+
}
|
|
3298
|
+
_increaseReferenceCount(textKey) {
|
|
3299
|
+
this._activeTextures[textKey].usageCount++;
|
|
3300
|
+
}
|
|
3301
|
+
decreaseReferenceCount(textKey) {
|
|
3302
|
+
const activeTexture = this._activeTextures[textKey];
|
|
3303
|
+
if (!activeTexture)
|
|
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";
|
|
3324
|
+
}
|
|
3325
|
+
getReferenceCount(textKey) {
|
|
3326
|
+
return this._activeTextures[textKey].usageCount;
|
|
3327
|
+
}
|
|
3328
|
+
destroy() {
|
|
3329
|
+
this._activeTextures = null;
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
/** @ignore */
|
|
3333
|
+
HTMLTextSystem.extension = {
|
|
3334
|
+
type: [
|
|
3335
|
+
ExtensionType.WebGLSystem,
|
|
3336
|
+
ExtensionType.WebGPUSystem,
|
|
3337
|
+
ExtensionType.CanvasSystem
|
|
3338
|
+
],
|
|
3339
|
+
name: "htmlText"
|
|
3340
|
+
};
|
|
3341
|
+
HTMLTextSystem.defaultFontOptions = {
|
|
3342
|
+
fontFamily: "Arial",
|
|
3343
|
+
fontStyle: "normal",
|
|
3344
|
+
fontWeight: "normal"
|
|
3345
|
+
};
|
|
3346
|
+
|
|
3347
|
+
class CanvasTextPipe {
|
|
3348
|
+
constructor(renderer) {
|
|
3349
|
+
this._gpuText = /* @__PURE__ */ Object.create(null);
|
|
3350
|
+
this._destroyRenderableBound = this.destroyRenderable.bind(this);
|
|
3351
|
+
this._renderer = renderer;
|
|
3352
|
+
this._renderer.runners.resolutionChange.add(this);
|
|
3353
|
+
this._renderer.renderableGC.addManagedHash(this, "_gpuText");
|
|
3354
|
+
}
|
|
3355
|
+
resolutionChange() {
|
|
3356
|
+
for (const i in this._gpuText) {
|
|
3357
|
+
const gpuText = this._gpuText[i];
|
|
3358
|
+
if (!gpuText)
|
|
3359
|
+
continue;
|
|
3360
|
+
const text = gpuText.batchableSprite.renderable;
|
|
3361
|
+
if (text._autoResolution) {
|
|
3362
|
+
text._resolution = this._renderer.resolution;
|
|
3363
|
+
text.onViewUpdate();
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
validateRenderable(text) {
|
|
3368
|
+
const gpuText = this._getGpuText(text);
|
|
3369
|
+
const newKey = text._getKey();
|
|
3370
|
+
if (gpuText.currentKey !== newKey) {
|
|
3371
|
+
return true;
|
|
3372
|
+
}
|
|
3373
|
+
return false;
|
|
3374
|
+
}
|
|
3375
|
+
addRenderable(text, instructionSet) {
|
|
3376
|
+
const gpuText = this._getGpuText(text);
|
|
3377
|
+
const batchableSprite = gpuText.batchableSprite;
|
|
3378
|
+
if (text._didTextUpdate) {
|
|
3379
|
+
this._updateText(text);
|
|
3380
|
+
}
|
|
3381
|
+
this._renderer.renderPipes.batch.addToBatch(batchableSprite, instructionSet);
|
|
3382
|
+
}
|
|
3383
|
+
updateRenderable(text) {
|
|
3384
|
+
const gpuText = this._getGpuText(text);
|
|
3385
|
+
const batchableSprite = gpuText.batchableSprite;
|
|
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);
|
|
3410
|
+
}
|
|
3411
|
+
_updateGpuText(text) {
|
|
3412
|
+
const gpuText = this._getGpuText(text);
|
|
3413
|
+
const batchableSprite = gpuText.batchableSprite;
|
|
3414
|
+
if (gpuText.texture) {
|
|
3415
|
+
this._renderer.canvasText.decreaseReferenceCount(gpuText.currentKey);
|
|
3416
|
+
}
|
|
3417
|
+
gpuText.texture = batchableSprite.texture = this._renderer.canvasText.getManagedTexture(text);
|
|
3418
|
+
gpuText.currentKey = text._getKey();
|
|
3419
|
+
batchableSprite.texture = gpuText.texture;
|
|
3420
|
+
}
|
|
3421
|
+
_getGpuText(text) {
|
|
3422
|
+
return this._gpuText[text.uid] || this.initGpuText(text);
|
|
3423
|
+
}
|
|
3424
|
+
initGpuText(text) {
|
|
3425
|
+
const gpuTextData = {
|
|
3426
|
+
texture: null,
|
|
3427
|
+
currentKey: "--",
|
|
3428
|
+
batchableSprite: BigPool.get(BatchableSprite)
|
|
3429
|
+
};
|
|
3430
|
+
gpuTextData.batchableSprite.renderable = text;
|
|
3431
|
+
gpuTextData.batchableSprite.transform = text.groupTransform;
|
|
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;
|
|
3439
|
+
}
|
|
3440
|
+
destroy() {
|
|
3441
|
+
for (const i in this._gpuText) {
|
|
3442
|
+
this._destroyRenderableById(i);
|
|
3443
|
+
}
|
|
3444
|
+
this._gpuText = null;
|
|
3445
|
+
this._renderer = null;
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
/** @ignore */
|
|
3449
|
+
CanvasTextPipe.extension = {
|
|
3450
|
+
type: [
|
|
3451
|
+
ExtensionType.WebGLPipes,
|
|
3452
|
+
ExtensionType.WebGPUPipes,
|
|
3453
|
+
ExtensionType.CanvasPipes
|
|
3454
|
+
],
|
|
3455
|
+
name: "text"
|
|
3456
|
+
};
|
|
3457
|
+
|
|
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
|
+
class CanvasTextSystem {
|
|
3503
|
+
constructor(_renderer) {
|
|
3504
|
+
this._activeTextures = {};
|
|
3505
|
+
this._renderer = _renderer;
|
|
3506
|
+
}
|
|
3507
|
+
getTextureSize(text, resolution, style) {
|
|
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) {
|
|
3518
|
+
if (typeof options === "string") {
|
|
3519
|
+
deprecation("8.0.0", "CanvasTextSystem.getTexture: Use object TextOptions instead of separate arguments");
|
|
3520
|
+
options = {
|
|
3521
|
+
text: options,
|
|
3522
|
+
style,
|
|
3523
|
+
resolution
|
|
3524
|
+
};
|
|
3525
|
+
}
|
|
3526
|
+
if (!(options.style instanceof TextStyle)) {
|
|
3527
|
+
options.style = new TextStyle(options.style);
|
|
3528
|
+
}
|
|
3529
|
+
const { texture, canvasAndContext } = this.createTextureAndCanvas(
|
|
3530
|
+
options
|
|
3531
|
+
);
|
|
3532
|
+
this._renderer.texture.initSource(texture._source);
|
|
3533
|
+
CanvasPool.returnCanvasAndContext(canvasAndContext);
|
|
3534
|
+
return texture;
|
|
3535
|
+
}
|
|
3536
|
+
createTextureAndCanvas(options) {
|
|
3537
|
+
const { text, style } = options;
|
|
3538
|
+
const resolution = options.resolution ?? this._renderer.resolution;
|
|
3539
|
+
const measured = CanvasTextMetrics.measureText(text || " ", style);
|
|
3540
|
+
const width = Math.ceil(Math.ceil(Math.max(1, measured.width) + style.padding * 2) * resolution);
|
|
3541
|
+
const height = Math.ceil(Math.ceil(Math.max(1, measured.height) + style.padding * 2) * resolution);
|
|
3542
|
+
const canvasAndContext = CanvasPool.getOptimalCanvasAndContext(width, height);
|
|
3543
|
+
const { canvas } = canvasAndContext;
|
|
3544
|
+
this.renderTextToCanvas(text, style, resolution, canvasAndContext);
|
|
3545
|
+
const texture = getPo2TextureFromSource(canvas, width, height, resolution);
|
|
3546
|
+
if (style.trim) {
|
|
3547
|
+
const trimmed = getCanvasBoundingBox(canvas, resolution);
|
|
3548
|
+
texture.frame.copyFrom(trimmed);
|
|
3549
|
+
texture.updateUvs();
|
|
3550
|
+
}
|
|
3551
|
+
return { texture, canvasAndContext };
|
|
3552
|
+
}
|
|
3553
|
+
getManagedTexture(text) {
|
|
3554
|
+
text._resolution = text._autoResolution ? this._renderer.resolution : text.resolution;
|
|
3555
|
+
const textKey = text._getKey();
|
|
3556
|
+
if (this._activeTextures[textKey]) {
|
|
3557
|
+
this._increaseReferenceCount(textKey);
|
|
3558
|
+
return this._activeTextures[textKey].texture;
|
|
3559
|
+
}
|
|
3560
|
+
const { texture, canvasAndContext } = this.createTextureAndCanvas(text);
|
|
3561
|
+
this._activeTextures[textKey] = {
|
|
3562
|
+
canvasAndContext,
|
|
3563
|
+
texture,
|
|
3564
|
+
usageCount: 1
|
|
3565
|
+
};
|
|
3566
|
+
return texture;
|
|
3567
|
+
}
|
|
3568
|
+
_increaseReferenceCount(textKey) {
|
|
3569
|
+
this._activeTextures[textKey].usageCount++;
|
|
3570
|
+
}
|
|
3571
|
+
/**
|
|
3572
|
+
* Returns a texture that was created wit the above `getTexture` function.
|
|
3573
|
+
* Handy if you are done with a texture and want to return it to the pool.
|
|
3574
|
+
* @param texture - The texture to be returned.
|
|
3575
|
+
*/
|
|
3576
|
+
returnTexture(texture) {
|
|
3577
|
+
const source = texture.source;
|
|
3578
|
+
source.resource = null;
|
|
3579
|
+
source.uploadMethodId = "unknown";
|
|
3580
|
+
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;
|
|
3594
|
+
}
|
|
3595
|
+
/**
|
|
3596
|
+
* 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
|
|
3605
|
+
*/
|
|
3606
|
+
renderTextToCanvas(text, style, resolution, canvasAndContext) {
|
|
3607
|
+
const { canvas, context } = canvasAndContext;
|
|
3608
|
+
const font = fontStringFromTextStyle(style);
|
|
3609
|
+
const measured = CanvasTextMetrics.measureText(text || " ", style);
|
|
3610
|
+
const lines = measured.lines;
|
|
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
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
/**
|
|
3690
|
+
* Render the text with letter-spacing.
|
|
3691
|
+
* @param text - The text to draw
|
|
3692
|
+
* @param style
|
|
3693
|
+
* @param canvasAndContext
|
|
3694
|
+
* @param x - Horizontal position to draw the text
|
|
3695
|
+
* @param y - Vertical position to draw the text
|
|
3696
|
+
* @param isStroke - Is this drawing for the outside stroke of the
|
|
3697
|
+
* text? If not, it's for the inside fill
|
|
3698
|
+
*/
|
|
3699
|
+
_drawLetterSpacing(text, style, canvasAndContext, x, y, isStroke = false) {
|
|
3700
|
+
const { context } = canvasAndContext;
|
|
3701
|
+
const letterSpacing = style.letterSpacing;
|
|
3702
|
+
let useExperimentalLetterSpacing = false;
|
|
3703
|
+
if (CanvasTextMetrics.experimentalLetterSpacingSupported) {
|
|
3704
|
+
if (CanvasTextMetrics.experimentalLetterSpacing) {
|
|
3705
|
+
context.letterSpacing = `${letterSpacing}px`;
|
|
3706
|
+
context.textLetterSpacing = `${letterSpacing}px`;
|
|
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
|
+
}
|
|
3740
|
+
}
|
|
3741
|
+
destroy() {
|
|
3742
|
+
this._activeTextures = null;
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
/** @ignore */
|
|
3746
|
+
CanvasTextSystem.extension = {
|
|
3747
|
+
type: [
|
|
3748
|
+
ExtensionType.WebGLSystem,
|
|
3749
|
+
ExtensionType.WebGPUSystem,
|
|
3750
|
+
ExtensionType.CanvasSystem
|
|
3751
|
+
],
|
|
3752
|
+
name: "canvasText"
|
|
3753
|
+
};
|
|
3754
|
+
|
|
3755
|
+
extensions.add(ResizePlugin);
|
|
3756
|
+
extensions.add(TickerPlugin);
|
|
3757
|
+
|
|
3758
|
+
extensions.add(GraphicsPipe);
|
|
3759
|
+
extensions.add(GraphicsContextSystem);
|
|
3760
|
+
|
|
3761
|
+
extensions.add(MeshPipe);
|
|
3762
|
+
|
|
3763
|
+
extensions.add(GlParticleContainerPipe);
|
|
3764
|
+
extensions.add(GpuParticleContainerPipe);
|
|
3765
|
+
|
|
3766
|
+
extensions.add(CanvasTextSystem);
|
|
3767
|
+
extensions.add(CanvasTextPipe);
|
|
3768
|
+
|
|
3769
|
+
extensions.add(BitmapTextPipe);
|
|
3770
|
+
|
|
3771
|
+
extensions.add(HTMLTextSystem);
|
|
3772
|
+
extensions.add(HTMLTextPipe);
|
|
3773
|
+
|
|
3774
|
+
extensions.add(TilingSpritePipe);
|
|
3775
|
+
|
|
3776
|
+
extensions.add(NineSliceSpritePipe);
|
|
3777
|
+
|
|
3778
|
+
extensions.add(FilterSystem);
|
|
3779
|
+
extensions.add(FilterPipe);
|