@vectojs/core 1.20.0 → 1.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-YLH7F4ZV.js → chunk-AGP4VLF4.js} +32 -0
- package/dist/{chunk-TJCXB2F6.mjs → chunk-FRMLD4PP.mjs} +32 -0
- package/dist/{chunk-L4SWVP2H.js → chunk-LHONR3GO.js} +336 -38
- package/dist/{chunk-QS3CUV7H.mjs → chunk-UPULSLKA.mjs} +298 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +451 -218
- package/dist/index.mjs +253 -20
- package/dist/renderer/CanvasRenderer.d.ts +20 -1
- package/dist/renderer/GlyphRasterAtlas.d.ts +186 -0
- package/dist/renderer/IRenderer.d.ts +94 -0
- package/dist/renderer/SVGRenderer.d.ts +2 -0
- package/dist/renderer/WebGLPointRenderer.d.ts +33 -0
- package/dist/renderer/index.d.ts +1 -0
- package/dist/renderer.js +4 -2
- package/dist/renderer.mjs +3 -1
- package/dist/text.js +2 -2
- package/dist/text.mjs +1 -1
- package/dist/tree/Entity.d.ts +128 -0
- package/dist/tree/Scene.d.ts +132 -1
- package/package.json +2 -2
|
@@ -964,6 +964,38 @@ var Entity = (_class2 = class {
|
|
|
964
964
|
*
|
|
965
965
|
* @returns The {@link A11yAttributes} for this entity's shadow node.
|
|
966
966
|
*/
|
|
967
|
+
/**
|
|
968
|
+
* Describe this entity's own debug surface for DevTools.
|
|
969
|
+
*
|
|
970
|
+
* Returns `null` by default, meaning "nothing beyond the generic `Entity`
|
|
971
|
+
* fields the inspector already shows". Override in a component to expose the
|
|
972
|
+
* state that makes it inspectable — see {@link DevtoolsDescriptor}.
|
|
973
|
+
*
|
|
974
|
+
* Called only while a panel is inspecting this entity, never per frame, so it
|
|
975
|
+
* may compute values that would be too expensive to track continuously.
|
|
976
|
+
*
|
|
977
|
+
* @returns A descriptor, or `null` to opt out.
|
|
978
|
+
*/
|
|
979
|
+
getDevtoolsDescriptor() {
|
|
980
|
+
return null;
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* Which of a child's properties this entity computes during layout.
|
|
984
|
+
*
|
|
985
|
+
* Returns an empty array by default, meaning "this entity does not position its
|
|
986
|
+
* children". A container that lays out children — `Stack`, `Table`, `Tabs` —
|
|
987
|
+
* overrides it so tooling can mark those values as parent-owned: editing `x` on
|
|
988
|
+
* a `Stack` child is reverted by the next layout, and knowing that in advance is
|
|
989
|
+
* the difference between a confusing tool and a correct one.
|
|
990
|
+
*
|
|
991
|
+
* @param child - The child being asked about. Containers whose control depends
|
|
992
|
+
* on the child (a `Table` cell versus its header) can answer per child.
|
|
993
|
+
* @returns Property names this entity overwrites on that child.
|
|
994
|
+
*/
|
|
995
|
+
getLayoutControlledProperties(child) {
|
|
996
|
+
void child;
|
|
997
|
+
return [];
|
|
998
|
+
}
|
|
967
999
|
getA11yAttributes() {
|
|
968
1000
|
return {};
|
|
969
1001
|
}
|
|
@@ -962,6 +962,38 @@ var Entity = class {
|
|
|
962
962
|
*
|
|
963
963
|
* @returns The {@link A11yAttributes} for this entity's shadow node.
|
|
964
964
|
*/
|
|
965
|
+
/**
|
|
966
|
+
* Describe this entity's own debug surface for DevTools.
|
|
967
|
+
*
|
|
968
|
+
* Returns `null` by default, meaning "nothing beyond the generic `Entity`
|
|
969
|
+
* fields the inspector already shows". Override in a component to expose the
|
|
970
|
+
* state that makes it inspectable — see {@link DevtoolsDescriptor}.
|
|
971
|
+
*
|
|
972
|
+
* Called only while a panel is inspecting this entity, never per frame, so it
|
|
973
|
+
* may compute values that would be too expensive to track continuously.
|
|
974
|
+
*
|
|
975
|
+
* @returns A descriptor, or `null` to opt out.
|
|
976
|
+
*/
|
|
977
|
+
getDevtoolsDescriptor() {
|
|
978
|
+
return null;
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Which of a child's properties this entity computes during layout.
|
|
982
|
+
*
|
|
983
|
+
* Returns an empty array by default, meaning "this entity does not position its
|
|
984
|
+
* children". A container that lays out children — `Stack`, `Table`, `Tabs` —
|
|
985
|
+
* overrides it so tooling can mark those values as parent-owned: editing `x` on
|
|
986
|
+
* a `Stack` child is reverted by the next layout, and knowing that in advance is
|
|
987
|
+
* the difference between a confusing tool and a correct one.
|
|
988
|
+
*
|
|
989
|
+
* @param child - The child being asked about. Containers whose control depends
|
|
990
|
+
* on the child (a `Table` cell versus its header) can answer per child.
|
|
991
|
+
* @returns Property names this entity overwrites on that child.
|
|
992
|
+
*/
|
|
993
|
+
getLayoutControlledProperties(child) {
|
|
994
|
+
void child;
|
|
995
|
+
return [];
|
|
996
|
+
}
|
|
965
997
|
getA11yAttributes() {
|
|
966
998
|
return {};
|
|
967
999
|
}
|
|
@@ -1,6 +1,21 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4;const __vecto_cjs_url=require("url").pathToFileURL(__filename).href;
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5;const __vecto_cjs_url=require("url").pathToFileURL(__filename).href;
|
|
2
2
|
|
|
3
3
|
// src/renderer/CanvasRenderer.ts
|
|
4
|
+
function emptyDrawCounters() {
|
|
5
|
+
return {
|
|
6
|
+
fills: 0,
|
|
7
|
+
strokes: 0,
|
|
8
|
+
texts: 0,
|
|
9
|
+
images: 0,
|
|
10
|
+
circles: 0,
|
|
11
|
+
flushes: 0,
|
|
12
|
+
saves: 0,
|
|
13
|
+
restores: 0,
|
|
14
|
+
clips: 0,
|
|
15
|
+
stateSwitches: 0,
|
|
16
|
+
overdrawRatio: 0
|
|
17
|
+
};
|
|
18
|
+
}
|
|
4
19
|
var TWO_PI = Math.PI * 2;
|
|
5
20
|
function getDevicePixelRatio() {
|
|
6
21
|
return typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
|
@@ -41,6 +56,17 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
|
|
|
41
56
|
__init6() {this.batchCount = 0}
|
|
42
57
|
__init7() {this._cachedFont = ""}
|
|
43
58
|
__init8() {this._cachedFill = ""}
|
|
59
|
+
/** Backend discriminator; see {@link IRenderer.kind}. */
|
|
60
|
+
__init9() {this.kind = "canvas2d"}
|
|
61
|
+
/**
|
|
62
|
+
* Draw counters, allocated only once counting is enabled.
|
|
63
|
+
*
|
|
64
|
+
* Null when off, so the guard on every op is a single null test and an inactive
|
|
65
|
+
* renderer carries no counter object at all.
|
|
66
|
+
*/
|
|
67
|
+
__init10() {this.counters = null}
|
|
68
|
+
/** Accumulated primitive area, kept separately so the ratio is derived on read. */
|
|
69
|
+
__init11() {this.drawnArea = 0}
|
|
44
70
|
/**
|
|
45
71
|
* @param canvas - The target canvas. Its backing store is resized to the
|
|
46
72
|
* logical size × devicePixelRatio.
|
|
@@ -50,7 +76,7 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
|
|
|
50
76
|
* set) so the canvas's own dimensions aren't clobbered by the window's.
|
|
51
77
|
* @param maxDPR - See {@link maxDPR}.
|
|
52
78
|
*/
|
|
53
|
-
constructor(canvas, size, maxDPR) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);
|
|
79
|
+
constructor(canvas, size, maxDPR) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);_class.prototype.__init9.call(this);_class.prototype.__init10.call(this);_class.prototype.__init11.call(this);
|
|
54
80
|
this.maxDPR = maxDPR;
|
|
55
81
|
const dpr = this.effectiveDPR();
|
|
56
82
|
this.width = _nullishCoalesce(_optionalChain([size, 'optionalAccess', _ => _.width]), () => ( (typeof window !== "undefined" ? window.innerWidth : canvas.width || 0)));
|
|
@@ -137,19 +163,47 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
|
|
|
137
163
|
return this.contextLost;
|
|
138
164
|
}
|
|
139
165
|
/** @inheritdoc */
|
|
166
|
+
/** @inheritdoc */
|
|
167
|
+
setDrawCounters(enabled) {
|
|
168
|
+
if (!enabled) {
|
|
169
|
+
this.counters = null;
|
|
170
|
+
this.drawnArea = 0;
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (!this.counters) this.counters = emptyDrawCounters();
|
|
174
|
+
}
|
|
175
|
+
/** @inheritdoc */
|
|
176
|
+
getDrawCounters() {
|
|
177
|
+
if (!this.counters) return null;
|
|
178
|
+
const area = this.width * this.height;
|
|
179
|
+
return {
|
|
180
|
+
...this.counters,
|
|
181
|
+
// Derived on read rather than maintained: the ratio is only meaningful
|
|
182
|
+
// against the current surface size, which can change under resize.
|
|
183
|
+
overdrawRatio: area > 0 ? Math.round(this.drawnArea / area * 100) / 100 : 0
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/** @inheritdoc */
|
|
187
|
+
clearDrawCounters() {
|
|
188
|
+
if (this.counters) this.counters = emptyDrawCounters();
|
|
189
|
+
this.drawnArea = 0;
|
|
190
|
+
}
|
|
140
191
|
clear() {
|
|
141
192
|
if (this.contextLost) return;
|
|
142
193
|
this.flush();
|
|
194
|
+
if (this.counters) this.drawnArea = 0;
|
|
143
195
|
this.ctx.clearRect(0, 0, this.width, this.height);
|
|
144
196
|
}
|
|
145
197
|
/** @inheritdoc */
|
|
146
198
|
save() {
|
|
147
199
|
this.flush();
|
|
200
|
+
if (this.counters) this.counters.saves++;
|
|
148
201
|
this.ctx.save();
|
|
149
202
|
}
|
|
150
203
|
/** @inheritdoc */
|
|
151
204
|
restore() {
|
|
152
205
|
this.flush();
|
|
206
|
+
if (this.counters) this.counters.restores++;
|
|
153
207
|
this.ctx.restore();
|
|
154
208
|
this._cachedFont = "";
|
|
155
209
|
this._cachedFill = "";
|
|
@@ -173,6 +227,7 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
|
|
|
173
227
|
/** @inheritdoc */
|
|
174
228
|
clip(x, y, width, height) {
|
|
175
229
|
this.flush();
|
|
230
|
+
if (this.counters) this.counters.clips++;
|
|
176
231
|
this.ctx.beginPath();
|
|
177
232
|
this.ctx.rect(x, y, width, height);
|
|
178
233
|
this.ctx.clip();
|
|
@@ -209,9 +264,22 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
|
|
|
209
264
|
/** @inheritdoc */
|
|
210
265
|
drawImage(source, dx, dy, dw, dh) {
|
|
211
266
|
this.flush();
|
|
267
|
+
if (this.counters) {
|
|
268
|
+
this.counters.images++;
|
|
269
|
+
this.drawnArea += Math.abs(dw * dh);
|
|
270
|
+
}
|
|
212
271
|
this.ctx.drawImage(source, dx, dy, dw, dh);
|
|
213
272
|
}
|
|
214
273
|
/** @inheritdoc */
|
|
274
|
+
drawImageRect(source, sx, sy, sw, sh, dx, dy, dw, dh) {
|
|
275
|
+
this.flush();
|
|
276
|
+
if (this.counters) {
|
|
277
|
+
this.counters.images++;
|
|
278
|
+
this.drawnArea += Math.abs(dw * dh);
|
|
279
|
+
}
|
|
280
|
+
this.ctx.drawImage(source, sx, sy, sw, sh, dx, dy, dw, dh);
|
|
281
|
+
}
|
|
282
|
+
/** @inheritdoc */
|
|
215
283
|
fillCircle(cx, cy, radius, color, alpha = 1) {
|
|
216
284
|
if (this.batchActive && (color !== this.batchColor || alpha !== this.batchAlpha)) {
|
|
217
285
|
this.flush();
|
|
@@ -222,6 +290,10 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
|
|
|
222
290
|
this.batchColor = color;
|
|
223
291
|
this.batchAlpha = alpha;
|
|
224
292
|
}
|
|
293
|
+
if (this.counters) {
|
|
294
|
+
this.counters.circles++;
|
|
295
|
+
this.drawnArea += Math.PI * radius * radius;
|
|
296
|
+
}
|
|
225
297
|
this.ctx.moveTo(cx + radius, cy);
|
|
226
298
|
this.ctx.arc(cx, cy, radius, 0, TWO_PI);
|
|
227
299
|
this.batchCount++;
|
|
@@ -230,6 +302,7 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
|
|
|
230
302
|
/** @inheritdoc */
|
|
231
303
|
flush() {
|
|
232
304
|
if (!this.batchActive) return;
|
|
305
|
+
if (this.counters) this.counters.flushes++;
|
|
233
306
|
this.ctx.globalAlpha = this.batchAlpha;
|
|
234
307
|
this.ctx.fillStyle = this.batchColor;
|
|
235
308
|
this.ctx.fill();
|
|
@@ -240,7 +313,9 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
|
|
|
240
313
|
/** @inheritdoc */
|
|
241
314
|
fill(color) {
|
|
242
315
|
this.flush();
|
|
316
|
+
if (this.counters) this.counters.fills++;
|
|
243
317
|
if (this._cachedFill !== color) {
|
|
318
|
+
if (this.counters) this.counters.stateSwitches++;
|
|
244
319
|
this.ctx.fillStyle = color;
|
|
245
320
|
this._cachedFill = color;
|
|
246
321
|
}
|
|
@@ -249,6 +324,7 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
|
|
|
249
324
|
/** @inheritdoc */
|
|
250
325
|
stroke(color, lineWidth = 1) {
|
|
251
326
|
this.flush();
|
|
327
|
+
if (this.counters) this.counters.strokes++;
|
|
252
328
|
this.ctx.strokeStyle = color;
|
|
253
329
|
this.ctx.lineWidth = lineWidth;
|
|
254
330
|
this.ctx.lineCap = "round";
|
|
@@ -258,11 +334,14 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
|
|
|
258
334
|
/** @inheritdoc */
|
|
259
335
|
fillText(text, x, y, font, color) {
|
|
260
336
|
this.flush();
|
|
337
|
+
if (this.counters) this.counters.texts++;
|
|
261
338
|
if (this._cachedFont !== font) {
|
|
339
|
+
if (this.counters) this.counters.stateSwitches++;
|
|
262
340
|
this.ctx.font = font;
|
|
263
341
|
this._cachedFont = font;
|
|
264
342
|
}
|
|
265
343
|
if (this._cachedFill !== color) {
|
|
344
|
+
if (this.counters) this.counters.stateSwitches++;
|
|
266
345
|
this.ctx.fillStyle = color;
|
|
267
346
|
this._cachedFill = color;
|
|
268
347
|
}
|
|
@@ -373,41 +452,43 @@ function fontFamilyFromShorthand(font, sizeToken) {
|
|
|
373
452
|
return family || "sans-serif";
|
|
374
453
|
}
|
|
375
454
|
var SVGRenderer = (_class2 = class {
|
|
455
|
+
/** Backend discriminator; see {@link IRenderer.kind}. */
|
|
456
|
+
__init12() {this.kind = "svg"}
|
|
376
457
|
|
|
377
458
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
459
|
+
__init13() {this.buffer = []}
|
|
460
|
+
__init14() {this.defsBuffer = []}
|
|
461
|
+
__init15() {this.currentPath = []}
|
|
381
462
|
// Inline matrix states (3x3 representation)
|
|
382
463
|
// [a, b, c, d, e, f] corresponding to:
|
|
383
464
|
// | a c e |
|
|
384
465
|
// | b d f |
|
|
385
466
|
// | 0 0 1 |
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
467
|
+
__init16() {this.mStack = []}
|
|
468
|
+
__init17() {this.ma = 1}
|
|
469
|
+
__init18() {this.mb = 0}
|
|
470
|
+
__init19() {this.mc = 0}
|
|
471
|
+
__init20() {this.md = 1}
|
|
472
|
+
__init21() {this.me = 0}
|
|
473
|
+
__init22() {this.mf = 0}
|
|
393
474
|
// Alpha stack
|
|
394
|
-
|
|
395
|
-
|
|
475
|
+
__init23() {this.alphaStack = []}
|
|
476
|
+
__init24() {this.globalAlpha = 1}
|
|
396
477
|
// Clipping stack
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
478
|
+
__init25() {this.clipDepthStack = []}
|
|
479
|
+
__init26() {this.clipDepth = 0}
|
|
480
|
+
__init27() {this.clipCounter = 0}
|
|
400
481
|
// uniquely identifies clipPaths
|
|
401
482
|
// Batch circles states (local coordinates)
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
483
|
+
__init28() {this.batchCircles = []}
|
|
484
|
+
__init29() {this.batchMatrix = [1, 0, 0, 1, 0, 0]}
|
|
485
|
+
__init30() {this.batchColor = ""}
|
|
486
|
+
__init31() {this.batchAlpha = 1}
|
|
487
|
+
__init32() {this.batchActive = false}
|
|
407
488
|
// Cache for generated gradient defs
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
constructor(width, height) {;_class2.prototype.
|
|
489
|
+
__init33() {this.gradientCounter = 0}
|
|
490
|
+
__init34() {this.gradientCache = /* @__PURE__ */ new Map()}
|
|
491
|
+
constructor(width, height) {;_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);_class2.prototype.__init18.call(this);_class2.prototype.__init19.call(this);_class2.prototype.__init20.call(this);_class2.prototype.__init21.call(this);_class2.prototype.__init22.call(this);_class2.prototype.__init23.call(this);_class2.prototype.__init24.call(this);_class2.prototype.__init25.call(this);_class2.prototype.__init26.call(this);_class2.prototype.__init27.call(this);_class2.prototype.__init28.call(this);_class2.prototype.__init29.call(this);_class2.prototype.__init30.call(this);_class2.prototype.__init31.call(this);_class2.prototype.__init32.call(this);_class2.prototype.__init33.call(this);_class2.prototype.__init34.call(this);
|
|
411
492
|
this.width = width;
|
|
412
493
|
this.height = height;
|
|
413
494
|
}
|
|
@@ -1225,6 +1306,12 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1225
1306
|
let spriteCount = 0;
|
|
1226
1307
|
let glyphData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD * 1024);
|
|
1227
1308
|
let glyphCount = 0;
|
|
1309
|
+
let frameDrawCalls = 0;
|
|
1310
|
+
let lastFrameDrawCalls = 0;
|
|
1311
|
+
let totalDrawCalls = 0;
|
|
1312
|
+
let atlasSwitches = 0;
|
|
1313
|
+
let circleQuadFallbacks = 0;
|
|
1314
|
+
let circlePoints = 0;
|
|
1228
1315
|
let circleQuadData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD * 64);
|
|
1229
1316
|
let circleQuadCount = 0;
|
|
1230
1317
|
let logicalW = 0;
|
|
@@ -1247,6 +1334,7 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1247
1334
|
gl.uniform1f(gURange, distanceRange);
|
|
1248
1335
|
ensureQuadIndices(glyphCount);
|
|
1249
1336
|
gl.drawElements(gl.TRIANGLES, glyphCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
|
|
1337
|
+
frameDrawCalls++;
|
|
1250
1338
|
gl.bindVertexArray(null);
|
|
1251
1339
|
glyphCount = 0;
|
|
1252
1340
|
};
|
|
@@ -1263,7 +1351,23 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1263
1351
|
canvas.style.height = `${height}px`;
|
|
1264
1352
|
gl.viewport(0, 0, canvas.width, canvas.height);
|
|
1265
1353
|
},
|
|
1354
|
+
stats() {
|
|
1355
|
+
return {
|
|
1356
|
+
drawCalls: lastFrameDrawCalls,
|
|
1357
|
+
totalDrawCalls,
|
|
1358
|
+
atlasSwitches,
|
|
1359
|
+
// Fixed at creation: five programs compiled once, never per frame, so
|
|
1360
|
+
// these are capability facts rather than counters.
|
|
1361
|
+
programs: 5,
|
|
1362
|
+
textures: (texture ? 1 : 0) + (msdfTexture ? 1 : 0),
|
|
1363
|
+
circleQuadFallbacks,
|
|
1364
|
+
circlePoints
|
|
1365
|
+
};
|
|
1366
|
+
},
|
|
1266
1367
|
begin() {
|
|
1368
|
+
lastFrameDrawCalls = frameDrawCalls;
|
|
1369
|
+
totalDrawCalls += frameDrawCalls;
|
|
1370
|
+
frameDrawCalls = 0;
|
|
1267
1371
|
pointCount = 0;
|
|
1268
1372
|
rectCount = 0;
|
|
1269
1373
|
spriteCount = 0;
|
|
@@ -1316,6 +1420,7 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1316
1420
|
distanceRange = range;
|
|
1317
1421
|
return;
|
|
1318
1422
|
}
|
|
1423
|
+
atlasSwitches++;
|
|
1319
1424
|
drawGlyphs();
|
|
1320
1425
|
distanceRange = range;
|
|
1321
1426
|
if (!msdfTexture) {
|
|
@@ -1358,6 +1463,7 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1358
1463
|
addCircle(x, y, radius, color, alpha = 1) {
|
|
1359
1464
|
const needsQuad = logicalW > 0 && (x < radius || y < radius || x > logicalW - radius || y > logicalH - radius) || radius * 2 * dpr > maxPointSize;
|
|
1360
1465
|
if (needsQuad) {
|
|
1466
|
+
circleQuadFallbacks++;
|
|
1361
1467
|
const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD;
|
|
1362
1468
|
circleQuadData = grow(circleQuadData, (circleQuadCount + 1) * stride);
|
|
1363
1469
|
const rgba = parseColorToRGBA(color);
|
|
@@ -1382,6 +1488,7 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1382
1488
|
circleQuadCount++;
|
|
1383
1489
|
return;
|
|
1384
1490
|
}
|
|
1491
|
+
circlePoints++;
|
|
1385
1492
|
pointData = grow(pointData, (pointCount + 1) * FLOATS_PER_POINT);
|
|
1386
1493
|
const [r, g, b, a] = parseColorToRGBA(color);
|
|
1387
1494
|
const o = pointCount * FLOATS_PER_POINT;
|
|
@@ -1423,6 +1530,7 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1423
1530
|
gl.uniform2f(rURes, logicalW, logicalH);
|
|
1424
1531
|
ensureQuadIndices(rectCount);
|
|
1425
1532
|
gl.drawElements(gl.TRIANGLES, rectCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
|
|
1533
|
+
frameDrawCalls++;
|
|
1426
1534
|
}
|
|
1427
1535
|
if (pointCount > 0) {
|
|
1428
1536
|
gl.useProgram(pointProgram);
|
|
@@ -1436,6 +1544,7 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1436
1544
|
gl.uniform2f(pURes, logicalW, logicalH);
|
|
1437
1545
|
gl.uniform1f(pUDpr, dpr);
|
|
1438
1546
|
gl.drawArrays(gl.POINTS, 0, pointCount);
|
|
1547
|
+
frameDrawCalls++;
|
|
1439
1548
|
}
|
|
1440
1549
|
if (circleQuadCount > 0) {
|
|
1441
1550
|
const floats = circleQuadCount * VERTS_PER_QUAD * FLOATS_PER_SPRITE_VERT;
|
|
@@ -1446,6 +1555,7 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1446
1555
|
gl.uniform2f(cURes, logicalW, logicalH);
|
|
1447
1556
|
ensureQuadIndices(circleQuadCount);
|
|
1448
1557
|
gl.drawElements(gl.TRIANGLES, circleQuadCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
|
|
1558
|
+
frameDrawCalls++;
|
|
1449
1559
|
}
|
|
1450
1560
|
if (spriteCount > 0 && texture) {
|
|
1451
1561
|
const floats = spriteCount * VERTS_PER_QUAD * FLOATS_PER_SPRITE_VERT;
|
|
@@ -1459,6 +1569,7 @@ function createWebGLPointRenderer(canvas) {
|
|
|
1459
1569
|
gl.uniform2f(sURes, logicalW, logicalH);
|
|
1460
1570
|
ensureQuadIndices(spriteCount);
|
|
1461
1571
|
gl.drawElements(gl.TRIANGLES, spriteCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
|
|
1572
|
+
frameDrawCalls++;
|
|
1462
1573
|
}
|
|
1463
1574
|
gl.bindVertexArray(null);
|
|
1464
1575
|
drawGlyphs();
|
|
@@ -1664,11 +1775,11 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
|
1664
1775
|
`;
|
|
1665
1776
|
var WebGPUParticleSystemManager = (_class3 = class {
|
|
1666
1777
|
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
constructor(device) {;_class3.prototype.
|
|
1778
|
+
__init35() {this.computePipeline = null}
|
|
1779
|
+
__init36() {this.renderPipeline = null}
|
|
1780
|
+
__init37() {this.computeBindGroupLayout = null}
|
|
1781
|
+
__init38() {this.renderBindGroupLayout = null}
|
|
1782
|
+
constructor(device) {;_class3.prototype.__init35.call(this);_class3.prototype.__init36.call(this);_class3.prototype.__init37.call(this);_class3.prototype.__init38.call(this);
|
|
1672
1783
|
this.device = device;
|
|
1673
1784
|
}
|
|
1674
1785
|
initPipelines(format) {
|
|
@@ -1809,15 +1920,201 @@ var WebGPUParticleSystemManager = (_class3 = class {
|
|
|
1809
1920
|
}
|
|
1810
1921
|
}, _class3);
|
|
1811
1922
|
|
|
1923
|
+
// src/renderer/GlyphRasterAtlas.ts
|
|
1924
|
+
var HARD_MAX_SIZE = 8192;
|
|
1925
|
+
var PAD = 2;
|
|
1926
|
+
var GlyphRasterAtlas = (_class4 = class {
|
|
1927
|
+
__init39() {this.slots = /* @__PURE__ */ new Map()}
|
|
1928
|
+
|
|
1929
|
+
|
|
1930
|
+
__init40() {this.canvas = null}
|
|
1931
|
+
__init41() {this.ctx = null}
|
|
1932
|
+
/**
|
|
1933
|
+
* Shelf packing: glyphs land left-to-right on a row, then a new row starts.
|
|
1934
|
+
* A monospace grid produces near-uniform widths, so shelves waste very little
|
|
1935
|
+
* and cost one comparison per insert — a real 2D packer would buy nothing here.
|
|
1936
|
+
*/
|
|
1937
|
+
__init42() {this.penX = PAD}
|
|
1938
|
+
__init43() {this.penY = PAD}
|
|
1939
|
+
__init44() {this.rowHeight = 0}
|
|
1940
|
+
__init45() {this._hits = 0}
|
|
1941
|
+
__init46() {this._misses = 0}
|
|
1942
|
+
__init47() {this._resets = 0}
|
|
1943
|
+
constructor(options = {}) {;_class4.prototype.__init39.call(this);_class4.prototype.__init40.call(this);_class4.prototype.__init41.call(this);_class4.prototype.__init42.call(this);_class4.prototype.__init43.call(this);_class4.prototype.__init44.call(this);_class4.prototype.__init45.call(this);_class4.prototype.__init46.call(this);_class4.prototype.__init47.call(this);
|
|
1944
|
+
this.dpr = Math.max(1, _nullishCoalesce(options.dpr, () => ( 1)));
|
|
1945
|
+
this.maxSize = Math.min(HARD_MAX_SIZE, Math.max(256, _nullishCoalesce(options.maxSize, () => ( 2048))));
|
|
1946
|
+
}
|
|
1947
|
+
/** Live instrumentation snapshot. */
|
|
1948
|
+
get stats() {
|
|
1949
|
+
return {
|
|
1950
|
+
hits: this._hits,
|
|
1951
|
+
misses: this._misses,
|
|
1952
|
+
size: this.slots.size,
|
|
1953
|
+
resets: this._resets
|
|
1954
|
+
};
|
|
1955
|
+
}
|
|
1956
|
+
/**
|
|
1957
|
+
* The atlas canvas, to pass as the blit source.
|
|
1958
|
+
*
|
|
1959
|
+
* `null` until the first successful {@link get}, and in any non-DOM context.
|
|
1960
|
+
*/
|
|
1961
|
+
get source() {
|
|
1962
|
+
return this.canvas;
|
|
1963
|
+
}
|
|
1964
|
+
ensureCanvas() {
|
|
1965
|
+
if (this.ctx) return true;
|
|
1966
|
+
if (typeof document === "undefined") return false;
|
|
1967
|
+
const canvas = document.createElement("canvas");
|
|
1968
|
+
canvas.width = this.maxSize;
|
|
1969
|
+
canvas.height = this.maxSize;
|
|
1970
|
+
const ctx = canvas.getContext("2d");
|
|
1971
|
+
if (!ctx) return false;
|
|
1972
|
+
this.canvas = canvas;
|
|
1973
|
+
this.ctx = ctx;
|
|
1974
|
+
return true;
|
|
1975
|
+
}
|
|
1976
|
+
/**
|
|
1977
|
+
* Look up a glyph, rasterizing it into the atlas on first request.
|
|
1978
|
+
*
|
|
1979
|
+
* @param font - Full CSS `font` shorthand, used for measuring and painting.
|
|
1980
|
+
* @param color - CSS color baked into the pixels.
|
|
1981
|
+
* @param glyph - A single grapheme cluster. Long strings are rejected
|
|
1982
|
+
* (`null`): they defeat the atlas's fixed-slot packing and belong in
|
|
1983
|
+
* `fillText` or {@link TextRasterCache}.
|
|
1984
|
+
* @returns The slot, or `null` when the caller must fall back to `fillText`
|
|
1985
|
+
* (headless, unrasterizable, or too large to pack).
|
|
1986
|
+
*/
|
|
1987
|
+
get(font, color, glyph) {
|
|
1988
|
+
const key = font + "\0" + color + "\0" + glyph;
|
|
1989
|
+
const existing = this.slots.get(key);
|
|
1990
|
+
if (existing !== void 0) {
|
|
1991
|
+
if (existing) this._hits++;
|
|
1992
|
+
return existing;
|
|
1993
|
+
}
|
|
1994
|
+
this._misses++;
|
|
1995
|
+
if (glyph.length === 0 || glyph.length > 8) {
|
|
1996
|
+
this.slots.set(key, null);
|
|
1997
|
+
return null;
|
|
1998
|
+
}
|
|
1999
|
+
if (!this.ensureCanvas()) return null;
|
|
2000
|
+
const ctx = this.ctx;
|
|
2001
|
+
ctx.font = font;
|
|
2002
|
+
const metrics = ctx.measureText(glyph);
|
|
2003
|
+
const advance = metrics.width;
|
|
2004
|
+
const ascent = metrics.actualBoundingBoxAscent || metrics.fontBoundingBoxAscent || advance * 0.9;
|
|
2005
|
+
const descent = metrics.actualBoundingBoxDescent || metrics.fontBoundingBoxDescent || advance * 0.25;
|
|
2006
|
+
const left = metrics.actualBoundingBoxLeft || 0;
|
|
2007
|
+
const right = metrics.actualBoundingBoxRight || advance;
|
|
2008
|
+
const offsetX = Math.ceil(left) + PAD;
|
|
2009
|
+
const offsetY = Math.ceil(ascent) + PAD;
|
|
2010
|
+
const w = offsetX + Math.ceil(right) + PAD;
|
|
2011
|
+
const h = offsetY + Math.ceil(descent) + PAD;
|
|
2012
|
+
if (!(w > 0) || !(h > 0)) {
|
|
2013
|
+
this.slots.set(key, null);
|
|
2014
|
+
return null;
|
|
2015
|
+
}
|
|
2016
|
+
const dpr = this.dpr;
|
|
2017
|
+
const dw = Math.ceil(w * dpr);
|
|
2018
|
+
const dh = Math.ceil(h * dpr);
|
|
2019
|
+
if (dw + PAD * 2 > this.maxSize || dh + PAD * 2 > this.maxSize) {
|
|
2020
|
+
this.slots.set(key, null);
|
|
2021
|
+
return null;
|
|
2022
|
+
}
|
|
2023
|
+
if (this.penX + dw + PAD > this.maxSize) {
|
|
2024
|
+
this.penX = PAD;
|
|
2025
|
+
this.penY += this.rowHeight + PAD;
|
|
2026
|
+
this.rowHeight = 0;
|
|
2027
|
+
}
|
|
2028
|
+
if (this.penY + dh + PAD > this.maxSize) {
|
|
2029
|
+
this.reset();
|
|
2030
|
+
if (!this.ensureCanvas()) return null;
|
|
2031
|
+
this.ctx.font = font;
|
|
2032
|
+
}
|
|
2033
|
+
const sx = this.penX;
|
|
2034
|
+
const sy = this.penY;
|
|
2035
|
+
const c = this.ctx;
|
|
2036
|
+
c.save();
|
|
2037
|
+
c.translate(sx, sy);
|
|
2038
|
+
if (dpr !== 1) c.scale(dpr, dpr);
|
|
2039
|
+
c.font = font;
|
|
2040
|
+
c.textBaseline = "alphabetic";
|
|
2041
|
+
c.fillStyle = color;
|
|
2042
|
+
c.fillText(glyph, offsetX, offsetY);
|
|
2043
|
+
c.restore();
|
|
2044
|
+
this.penX += dw + PAD;
|
|
2045
|
+
if (dh > this.rowHeight) this.rowHeight = dh;
|
|
2046
|
+
const slot = {
|
|
2047
|
+
sx,
|
|
2048
|
+
sy,
|
|
2049
|
+
sw: dw,
|
|
2050
|
+
sh: dh,
|
|
2051
|
+
w,
|
|
2052
|
+
h,
|
|
2053
|
+
offsetX,
|
|
2054
|
+
offsetY,
|
|
2055
|
+
glyph,
|
|
2056
|
+
font,
|
|
2057
|
+
advance,
|
|
2058
|
+
left,
|
|
2059
|
+
right
|
|
2060
|
+
};
|
|
2061
|
+
this.slots.set(key, slot);
|
|
2062
|
+
return slot;
|
|
2063
|
+
}
|
|
2064
|
+
/**
|
|
2065
|
+
* Find the slot occupying a source position, or `null`.
|
|
2066
|
+
*
|
|
2067
|
+
* The inverse of {@link get}: it maps a blit back to the glyph it drew. Exists
|
|
2068
|
+
* for instrumentation — a test or devtool that traces `drawImage` calls sees
|
|
2069
|
+
* only a source rect, and needs this to recover which cluster was painted and
|
|
2070
|
+
* with what metrics. Linear over resident slots, so it is a diagnostic, not a
|
|
2071
|
+
* per-frame call.
|
|
2072
|
+
*/
|
|
2073
|
+
slotAt(sx, sy) {
|
|
2074
|
+
for (const slot of this.slots.values()) {
|
|
2075
|
+
if (slot && slot.sx === sx && slot.sy === sy) return slot;
|
|
2076
|
+
}
|
|
2077
|
+
return null;
|
|
2078
|
+
}
|
|
2079
|
+
/**
|
|
2080
|
+
* Drop every glyph and reuse the canvas.
|
|
2081
|
+
*
|
|
2082
|
+
* Call after a font or theme change: slots are keyed by `(font, color, glyph)`
|
|
2083
|
+
* so stale entries are never *returned* wrongly, but they do occupy space.
|
|
2084
|
+
*/
|
|
2085
|
+
reset() {
|
|
2086
|
+
this.slots.clear();
|
|
2087
|
+
this.penX = PAD;
|
|
2088
|
+
this.penY = PAD;
|
|
2089
|
+
this.rowHeight = 0;
|
|
2090
|
+
this._resets++;
|
|
2091
|
+
if (this.ctx && this.canvas) {
|
|
2092
|
+
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
/** Release the backing canvas and all slots. */
|
|
2096
|
+
destroy() {
|
|
2097
|
+
this.slots.clear();
|
|
2098
|
+
this.canvas = null;
|
|
2099
|
+
this.ctx = null;
|
|
2100
|
+
this.penX = PAD;
|
|
2101
|
+
this.penY = PAD;
|
|
2102
|
+
this.rowHeight = 0;
|
|
2103
|
+
this._hits = 0;
|
|
2104
|
+
this._misses = 0;
|
|
2105
|
+
this._resets = 0;
|
|
2106
|
+
}
|
|
2107
|
+
}, _class4);
|
|
2108
|
+
|
|
1812
2109
|
// src/renderer/TextRasterCache.ts
|
|
1813
|
-
var TextRasterCache = (
|
|
1814
|
-
|
|
2110
|
+
var TextRasterCache = (_class5 = class {
|
|
2111
|
+
__init48() {this.cache = /* @__PURE__ */ new Map()}
|
|
1815
2112
|
|
|
1816
2113
|
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
constructor(options = {}) {;
|
|
2114
|
+
__init49() {this.scratchCtx = null}
|
|
2115
|
+
__init50() {this._hits = 0}
|
|
2116
|
+
__init51() {this._misses = 0}
|
|
2117
|
+
constructor(options = {}) {;_class5.prototype.__init48.call(this);_class5.prototype.__init49.call(this);_class5.prototype.__init50.call(this);_class5.prototype.__init51.call(this);
|
|
1821
2118
|
this.maxEntries = Math.max(1, _nullishCoalesce(options.maxEntries, () => ( 4096)));
|
|
1822
2119
|
this.dpr = Math.max(1, _nullishCoalesce(options.dpr, () => ( 1)));
|
|
1823
2120
|
}
|
|
@@ -1892,7 +2189,8 @@ var TextRasterCache = (_class4 = class {
|
|
|
1892
2189
|
this._hits = 0;
|
|
1893
2190
|
this._misses = 0;
|
|
1894
2191
|
}
|
|
1895
|
-
},
|
|
2192
|
+
}, _class5);
|
|
2193
|
+
|
|
1896
2194
|
|
|
1897
2195
|
|
|
1898
2196
|
|
|
@@ -1903,4 +2201,4 @@ var TextRasterCache = (_class4 = class {
|
|
|
1903
2201
|
|
|
1904
2202
|
|
|
1905
2203
|
|
|
1906
|
-
exports.CanvasRenderer = CanvasRenderer; exports.sanitizeUrl = sanitizeUrl; exports.isSafeUrl = isSafeUrl; exports.SVGRenderer = SVGRenderer; exports.parseColorToRGBA = parseColorToRGBA; exports.createWebGLPointRenderer = createWebGLPointRenderer; exports.WebGPUParticleSystemManager = WebGPUParticleSystemManager; exports.TextRasterCache = TextRasterCache;
|
|
2204
|
+
exports.CanvasRenderer = CanvasRenderer; exports.sanitizeUrl = sanitizeUrl; exports.isSafeUrl = isSafeUrl; exports.SVGRenderer = SVGRenderer; exports.parseColorToRGBA = parseColorToRGBA; exports.createWebGLPointRenderer = createWebGLPointRenderer; exports.WebGPUParticleSystemManager = WebGPUParticleSystemManager; exports.GlyphRasterAtlas = GlyphRasterAtlas; exports.TextRasterCache = TextRasterCache;
|