@vectojs/core 1.21.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.
@@ -1,6 +1,21 @@
1
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,11 +264,19 @@ 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 */
215
274
  drawImageRect(source, sx, sy, sw, sh, dx, dy, dw, dh) {
216
275
  this.flush();
276
+ if (this.counters) {
277
+ this.counters.images++;
278
+ this.drawnArea += Math.abs(dw * dh);
279
+ }
217
280
  this.ctx.drawImage(source, sx, sy, sw, sh, dx, dy, dw, dh);
218
281
  }
219
282
  /** @inheritdoc */
@@ -227,6 +290,10 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
227
290
  this.batchColor = color;
228
291
  this.batchAlpha = alpha;
229
292
  }
293
+ if (this.counters) {
294
+ this.counters.circles++;
295
+ this.drawnArea += Math.PI * radius * radius;
296
+ }
230
297
  this.ctx.moveTo(cx + radius, cy);
231
298
  this.ctx.arc(cx, cy, radius, 0, TWO_PI);
232
299
  this.batchCount++;
@@ -235,6 +302,7 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
235
302
  /** @inheritdoc */
236
303
  flush() {
237
304
  if (!this.batchActive) return;
305
+ if (this.counters) this.counters.flushes++;
238
306
  this.ctx.globalAlpha = this.batchAlpha;
239
307
  this.ctx.fillStyle = this.batchColor;
240
308
  this.ctx.fill();
@@ -245,7 +313,9 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
245
313
  /** @inheritdoc */
246
314
  fill(color) {
247
315
  this.flush();
316
+ if (this.counters) this.counters.fills++;
248
317
  if (this._cachedFill !== color) {
318
+ if (this.counters) this.counters.stateSwitches++;
249
319
  this.ctx.fillStyle = color;
250
320
  this._cachedFill = color;
251
321
  }
@@ -254,6 +324,7 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
254
324
  /** @inheritdoc */
255
325
  stroke(color, lineWidth = 1) {
256
326
  this.flush();
327
+ if (this.counters) this.counters.strokes++;
257
328
  this.ctx.strokeStyle = color;
258
329
  this.ctx.lineWidth = lineWidth;
259
330
  this.ctx.lineCap = "round";
@@ -263,11 +334,14 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
263
334
  /** @inheritdoc */
264
335
  fillText(text, x, y, font, color) {
265
336
  this.flush();
337
+ if (this.counters) this.counters.texts++;
266
338
  if (this._cachedFont !== font) {
339
+ if (this.counters) this.counters.stateSwitches++;
267
340
  this.ctx.font = font;
268
341
  this._cachedFont = font;
269
342
  }
270
343
  if (this._cachedFill !== color) {
344
+ if (this.counters) this.counters.stateSwitches++;
271
345
  this.ctx.fillStyle = color;
272
346
  this._cachedFill = color;
273
347
  }
@@ -378,41 +452,43 @@ function fontFamilyFromShorthand(font, sizeToken) {
378
452
  return family || "sans-serif";
379
453
  }
380
454
  var SVGRenderer = (_class2 = class {
455
+ /** Backend discriminator; see {@link IRenderer.kind}. */
456
+ __init12() {this.kind = "svg"}
381
457
 
382
458
 
383
- __init9() {this.buffer = []}
384
- __init10() {this.defsBuffer = []}
385
- __init11() {this.currentPath = []}
459
+ __init13() {this.buffer = []}
460
+ __init14() {this.defsBuffer = []}
461
+ __init15() {this.currentPath = []}
386
462
  // Inline matrix states (3x3 representation)
387
463
  // [a, b, c, d, e, f] corresponding to:
388
464
  // | a c e |
389
465
  // | b d f |
390
466
  // | 0 0 1 |
391
- __init12() {this.mStack = []}
392
- __init13() {this.ma = 1}
393
- __init14() {this.mb = 0}
394
- __init15() {this.mc = 0}
395
- __init16() {this.md = 1}
396
- __init17() {this.me = 0}
397
- __init18() {this.mf = 0}
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}
398
474
  // Alpha stack
399
- __init19() {this.alphaStack = []}
400
- __init20() {this.globalAlpha = 1}
475
+ __init23() {this.alphaStack = []}
476
+ __init24() {this.globalAlpha = 1}
401
477
  // Clipping stack
402
- __init21() {this.clipDepthStack = []}
403
- __init22() {this.clipDepth = 0}
404
- __init23() {this.clipCounter = 0}
478
+ __init25() {this.clipDepthStack = []}
479
+ __init26() {this.clipDepth = 0}
480
+ __init27() {this.clipCounter = 0}
405
481
  // uniquely identifies clipPaths
406
482
  // Batch circles states (local coordinates)
407
- __init24() {this.batchCircles = []}
408
- __init25() {this.batchMatrix = [1, 0, 0, 1, 0, 0]}
409
- __init26() {this.batchColor = ""}
410
- __init27() {this.batchAlpha = 1}
411
- __init28() {this.batchActive = false}
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}
412
488
  // Cache for generated gradient defs
413
- __init29() {this.gradientCounter = 0}
414
- __init30() {this.gradientCache = /* @__PURE__ */ new Map()}
415
- constructor(width, height) {;_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);_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);
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);
416
492
  this.width = width;
417
493
  this.height = height;
418
494
  }
@@ -1230,6 +1306,12 @@ function createWebGLPointRenderer(canvas) {
1230
1306
  let spriteCount = 0;
1231
1307
  let glyphData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD * 1024);
1232
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;
1233
1315
  let circleQuadData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD * 64);
1234
1316
  let circleQuadCount = 0;
1235
1317
  let logicalW = 0;
@@ -1252,6 +1334,7 @@ function createWebGLPointRenderer(canvas) {
1252
1334
  gl.uniform1f(gURange, distanceRange);
1253
1335
  ensureQuadIndices(glyphCount);
1254
1336
  gl.drawElements(gl.TRIANGLES, glyphCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1337
+ frameDrawCalls++;
1255
1338
  gl.bindVertexArray(null);
1256
1339
  glyphCount = 0;
1257
1340
  };
@@ -1268,7 +1351,23 @@ function createWebGLPointRenderer(canvas) {
1268
1351
  canvas.style.height = `${height}px`;
1269
1352
  gl.viewport(0, 0, canvas.width, canvas.height);
1270
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
+ },
1271
1367
  begin() {
1368
+ lastFrameDrawCalls = frameDrawCalls;
1369
+ totalDrawCalls += frameDrawCalls;
1370
+ frameDrawCalls = 0;
1272
1371
  pointCount = 0;
1273
1372
  rectCount = 0;
1274
1373
  spriteCount = 0;
@@ -1321,6 +1420,7 @@ function createWebGLPointRenderer(canvas) {
1321
1420
  distanceRange = range;
1322
1421
  return;
1323
1422
  }
1423
+ atlasSwitches++;
1324
1424
  drawGlyphs();
1325
1425
  distanceRange = range;
1326
1426
  if (!msdfTexture) {
@@ -1363,6 +1463,7 @@ function createWebGLPointRenderer(canvas) {
1363
1463
  addCircle(x, y, radius, color, alpha = 1) {
1364
1464
  const needsQuad = logicalW > 0 && (x < radius || y < radius || x > logicalW - radius || y > logicalH - radius) || radius * 2 * dpr > maxPointSize;
1365
1465
  if (needsQuad) {
1466
+ circleQuadFallbacks++;
1366
1467
  const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD;
1367
1468
  circleQuadData = grow(circleQuadData, (circleQuadCount + 1) * stride);
1368
1469
  const rgba = parseColorToRGBA(color);
@@ -1387,6 +1488,7 @@ function createWebGLPointRenderer(canvas) {
1387
1488
  circleQuadCount++;
1388
1489
  return;
1389
1490
  }
1491
+ circlePoints++;
1390
1492
  pointData = grow(pointData, (pointCount + 1) * FLOATS_PER_POINT);
1391
1493
  const [r, g, b, a] = parseColorToRGBA(color);
1392
1494
  const o = pointCount * FLOATS_PER_POINT;
@@ -1428,6 +1530,7 @@ function createWebGLPointRenderer(canvas) {
1428
1530
  gl.uniform2f(rURes, logicalW, logicalH);
1429
1531
  ensureQuadIndices(rectCount);
1430
1532
  gl.drawElements(gl.TRIANGLES, rectCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1533
+ frameDrawCalls++;
1431
1534
  }
1432
1535
  if (pointCount > 0) {
1433
1536
  gl.useProgram(pointProgram);
@@ -1441,6 +1544,7 @@ function createWebGLPointRenderer(canvas) {
1441
1544
  gl.uniform2f(pURes, logicalW, logicalH);
1442
1545
  gl.uniform1f(pUDpr, dpr);
1443
1546
  gl.drawArrays(gl.POINTS, 0, pointCount);
1547
+ frameDrawCalls++;
1444
1548
  }
1445
1549
  if (circleQuadCount > 0) {
1446
1550
  const floats = circleQuadCount * VERTS_PER_QUAD * FLOATS_PER_SPRITE_VERT;
@@ -1451,6 +1555,7 @@ function createWebGLPointRenderer(canvas) {
1451
1555
  gl.uniform2f(cURes, logicalW, logicalH);
1452
1556
  ensureQuadIndices(circleQuadCount);
1453
1557
  gl.drawElements(gl.TRIANGLES, circleQuadCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1558
+ frameDrawCalls++;
1454
1559
  }
1455
1560
  if (spriteCount > 0 && texture) {
1456
1561
  const floats = spriteCount * VERTS_PER_QUAD * FLOATS_PER_SPRITE_VERT;
@@ -1464,6 +1569,7 @@ function createWebGLPointRenderer(canvas) {
1464
1569
  gl.uniform2f(sURes, logicalW, logicalH);
1465
1570
  ensureQuadIndices(spriteCount);
1466
1571
  gl.drawElements(gl.TRIANGLES, spriteCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1572
+ frameDrawCalls++;
1467
1573
  }
1468
1574
  gl.bindVertexArray(null);
1469
1575
  drawGlyphs();
@@ -1669,11 +1775,11 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
1669
1775
  `;
1670
1776
  var WebGPUParticleSystemManager = (_class3 = class {
1671
1777
 
1672
- __init31() {this.computePipeline = null}
1673
- __init32() {this.renderPipeline = null}
1674
- __init33() {this.computeBindGroupLayout = null}
1675
- __init34() {this.renderBindGroupLayout = null}
1676
- constructor(device) {;_class3.prototype.__init31.call(this);_class3.prototype.__init32.call(this);_class3.prototype.__init33.call(this);_class3.prototype.__init34.call(this);
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);
1677
1783
  this.device = device;
1678
1784
  }
1679
1785
  initPipelines(format) {
@@ -1818,23 +1924,23 @@ var WebGPUParticleSystemManager = (_class3 = class {
1818
1924
  var HARD_MAX_SIZE = 8192;
1819
1925
  var PAD = 2;
1820
1926
  var GlyphRasterAtlas = (_class4 = class {
1821
- __init35() {this.slots = /* @__PURE__ */ new Map()}
1927
+ __init39() {this.slots = /* @__PURE__ */ new Map()}
1822
1928
 
1823
1929
 
1824
- __init36() {this.canvas = null}
1825
- __init37() {this.ctx = null}
1930
+ __init40() {this.canvas = null}
1931
+ __init41() {this.ctx = null}
1826
1932
  /**
1827
1933
  * Shelf packing: glyphs land left-to-right on a row, then a new row starts.
1828
1934
  * A monospace grid produces near-uniform widths, so shelves waste very little
1829
1935
  * and cost one comparison per insert — a real 2D packer would buy nothing here.
1830
1936
  */
1831
- __init38() {this.penX = PAD}
1832
- __init39() {this.penY = PAD}
1833
- __init40() {this.rowHeight = 0}
1834
- __init41() {this._hits = 0}
1835
- __init42() {this._misses = 0}
1836
- __init43() {this._resets = 0}
1837
- constructor(options = {}) {;_class4.prototype.__init35.call(this);_class4.prototype.__init36.call(this);_class4.prototype.__init37.call(this);_class4.prototype.__init38.call(this);_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);
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);
1838
1944
  this.dpr = Math.max(1, _nullishCoalesce(options.dpr, () => ( 1)));
1839
1945
  this.maxSize = Math.min(HARD_MAX_SIZE, Math.max(256, _nullishCoalesce(options.maxSize, () => ( 2048))));
1840
1946
  }
@@ -2002,13 +2108,13 @@ var GlyphRasterAtlas = (_class4 = class {
2002
2108
 
2003
2109
  // src/renderer/TextRasterCache.ts
2004
2110
  var TextRasterCache = (_class5 = class {
2005
- __init44() {this.cache = /* @__PURE__ */ new Map()}
2111
+ __init48() {this.cache = /* @__PURE__ */ new Map()}
2006
2112
 
2007
2113
 
2008
- __init45() {this.scratchCtx = null}
2009
- __init46() {this._hits = 0}
2010
- __init47() {this._misses = 0}
2011
- constructor(options = {}) {;_class5.prototype.__init44.call(this);_class5.prototype.__init45.call(this);_class5.prototype.__init46.call(this);_class5.prototype.__init47.call(this);
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);
2012
2118
  this.maxEntries = Math.max(1, _nullishCoalesce(options.maxEntries, () => ( 4096)));
2013
2119
  this.dpr = Math.max(1, _nullishCoalesce(options.dpr, () => ( 1)));
2014
2120
  }
@@ -1,4 +1,19 @@
1
1
  // src/renderer/CanvasRenderer.ts
2
+ function emptyDrawCounters() {
3
+ return {
4
+ fills: 0,
5
+ strokes: 0,
6
+ texts: 0,
7
+ images: 0,
8
+ circles: 0,
9
+ flushes: 0,
10
+ saves: 0,
11
+ restores: 0,
12
+ clips: 0,
13
+ stateSwitches: 0,
14
+ overdrawRatio: 0
15
+ };
16
+ }
2
17
  var TWO_PI = Math.PI * 2;
3
18
  function getDevicePixelRatio() {
4
19
  return typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
@@ -39,6 +54,17 @@ var CanvasRenderer = class _CanvasRenderer {
39
54
  batchCount = 0;
40
55
  _cachedFont = "";
41
56
  _cachedFill = "";
57
+ /** Backend discriminator; see {@link IRenderer.kind}. */
58
+ kind = "canvas2d";
59
+ /**
60
+ * Draw counters, allocated only once counting is enabled.
61
+ *
62
+ * Null when off, so the guard on every op is a single null test and an inactive
63
+ * renderer carries no counter object at all.
64
+ */
65
+ counters = null;
66
+ /** Accumulated primitive area, kept separately so the ratio is derived on read. */
67
+ drawnArea = 0;
42
68
  /**
43
69
  * @param canvas - The target canvas. Its backing store is resized to the
44
70
  * logical size × devicePixelRatio.
@@ -135,19 +161,47 @@ var CanvasRenderer = class _CanvasRenderer {
135
161
  return this.contextLost;
136
162
  }
137
163
  /** @inheritdoc */
164
+ /** @inheritdoc */
165
+ setDrawCounters(enabled) {
166
+ if (!enabled) {
167
+ this.counters = null;
168
+ this.drawnArea = 0;
169
+ return;
170
+ }
171
+ if (!this.counters) this.counters = emptyDrawCounters();
172
+ }
173
+ /** @inheritdoc */
174
+ getDrawCounters() {
175
+ if (!this.counters) return null;
176
+ const area = this.width * this.height;
177
+ return {
178
+ ...this.counters,
179
+ // Derived on read rather than maintained: the ratio is only meaningful
180
+ // against the current surface size, which can change under resize.
181
+ overdrawRatio: area > 0 ? Math.round(this.drawnArea / area * 100) / 100 : 0
182
+ };
183
+ }
184
+ /** @inheritdoc */
185
+ clearDrawCounters() {
186
+ if (this.counters) this.counters = emptyDrawCounters();
187
+ this.drawnArea = 0;
188
+ }
138
189
  clear() {
139
190
  if (this.contextLost) return;
140
191
  this.flush();
192
+ if (this.counters) this.drawnArea = 0;
141
193
  this.ctx.clearRect(0, 0, this.width, this.height);
142
194
  }
143
195
  /** @inheritdoc */
144
196
  save() {
145
197
  this.flush();
198
+ if (this.counters) this.counters.saves++;
146
199
  this.ctx.save();
147
200
  }
148
201
  /** @inheritdoc */
149
202
  restore() {
150
203
  this.flush();
204
+ if (this.counters) this.counters.restores++;
151
205
  this.ctx.restore();
152
206
  this._cachedFont = "";
153
207
  this._cachedFill = "";
@@ -171,6 +225,7 @@ var CanvasRenderer = class _CanvasRenderer {
171
225
  /** @inheritdoc */
172
226
  clip(x, y, width, height) {
173
227
  this.flush();
228
+ if (this.counters) this.counters.clips++;
174
229
  this.ctx.beginPath();
175
230
  this.ctx.rect(x, y, width, height);
176
231
  this.ctx.clip();
@@ -207,11 +262,19 @@ var CanvasRenderer = class _CanvasRenderer {
207
262
  /** @inheritdoc */
208
263
  drawImage(source, dx, dy, dw, dh) {
209
264
  this.flush();
265
+ if (this.counters) {
266
+ this.counters.images++;
267
+ this.drawnArea += Math.abs(dw * dh);
268
+ }
210
269
  this.ctx.drawImage(source, dx, dy, dw, dh);
211
270
  }
212
271
  /** @inheritdoc */
213
272
  drawImageRect(source, sx, sy, sw, sh, dx, dy, dw, dh) {
214
273
  this.flush();
274
+ if (this.counters) {
275
+ this.counters.images++;
276
+ this.drawnArea += Math.abs(dw * dh);
277
+ }
215
278
  this.ctx.drawImage(source, sx, sy, sw, sh, dx, dy, dw, dh);
216
279
  }
217
280
  /** @inheritdoc */
@@ -225,6 +288,10 @@ var CanvasRenderer = class _CanvasRenderer {
225
288
  this.batchColor = color;
226
289
  this.batchAlpha = alpha;
227
290
  }
291
+ if (this.counters) {
292
+ this.counters.circles++;
293
+ this.drawnArea += Math.PI * radius * radius;
294
+ }
228
295
  this.ctx.moveTo(cx + radius, cy);
229
296
  this.ctx.arc(cx, cy, radius, 0, TWO_PI);
230
297
  this.batchCount++;
@@ -233,6 +300,7 @@ var CanvasRenderer = class _CanvasRenderer {
233
300
  /** @inheritdoc */
234
301
  flush() {
235
302
  if (!this.batchActive) return;
303
+ if (this.counters) this.counters.flushes++;
236
304
  this.ctx.globalAlpha = this.batchAlpha;
237
305
  this.ctx.fillStyle = this.batchColor;
238
306
  this.ctx.fill();
@@ -243,7 +311,9 @@ var CanvasRenderer = class _CanvasRenderer {
243
311
  /** @inheritdoc */
244
312
  fill(color) {
245
313
  this.flush();
314
+ if (this.counters) this.counters.fills++;
246
315
  if (this._cachedFill !== color) {
316
+ if (this.counters) this.counters.stateSwitches++;
247
317
  this.ctx.fillStyle = color;
248
318
  this._cachedFill = color;
249
319
  }
@@ -252,6 +322,7 @@ var CanvasRenderer = class _CanvasRenderer {
252
322
  /** @inheritdoc */
253
323
  stroke(color, lineWidth = 1) {
254
324
  this.flush();
325
+ if (this.counters) this.counters.strokes++;
255
326
  this.ctx.strokeStyle = color;
256
327
  this.ctx.lineWidth = lineWidth;
257
328
  this.ctx.lineCap = "round";
@@ -261,11 +332,14 @@ var CanvasRenderer = class _CanvasRenderer {
261
332
  /** @inheritdoc */
262
333
  fillText(text, x, y, font, color) {
263
334
  this.flush();
335
+ if (this.counters) this.counters.texts++;
264
336
  if (this._cachedFont !== font) {
337
+ if (this.counters) this.counters.stateSwitches++;
265
338
  this.ctx.font = font;
266
339
  this._cachedFont = font;
267
340
  }
268
341
  if (this._cachedFill !== color) {
342
+ if (this.counters) this.counters.stateSwitches++;
269
343
  this.ctx.fillStyle = color;
270
344
  this._cachedFill = color;
271
345
  }
@@ -376,6 +450,8 @@ function fontFamilyFromShorthand(font, sizeToken) {
376
450
  return family || "sans-serif";
377
451
  }
378
452
  var SVGRenderer = class {
453
+ /** Backend discriminator; see {@link IRenderer.kind}. */
454
+ kind = "svg";
379
455
  width;
380
456
  height;
381
457
  buffer = [];
@@ -1228,6 +1304,12 @@ function createWebGLPointRenderer(canvas) {
1228
1304
  let spriteCount = 0;
1229
1305
  let glyphData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD * 1024);
1230
1306
  let glyphCount = 0;
1307
+ let frameDrawCalls = 0;
1308
+ let lastFrameDrawCalls = 0;
1309
+ let totalDrawCalls = 0;
1310
+ let atlasSwitches = 0;
1311
+ let circleQuadFallbacks = 0;
1312
+ let circlePoints = 0;
1231
1313
  let circleQuadData = new Float32Array(FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD * 64);
1232
1314
  let circleQuadCount = 0;
1233
1315
  let logicalW = 0;
@@ -1250,6 +1332,7 @@ function createWebGLPointRenderer(canvas) {
1250
1332
  gl.uniform1f(gURange, distanceRange);
1251
1333
  ensureQuadIndices(glyphCount);
1252
1334
  gl.drawElements(gl.TRIANGLES, glyphCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1335
+ frameDrawCalls++;
1253
1336
  gl.bindVertexArray(null);
1254
1337
  glyphCount = 0;
1255
1338
  };
@@ -1266,7 +1349,23 @@ function createWebGLPointRenderer(canvas) {
1266
1349
  canvas.style.height = `${height}px`;
1267
1350
  gl.viewport(0, 0, canvas.width, canvas.height);
1268
1351
  },
1352
+ stats() {
1353
+ return {
1354
+ drawCalls: lastFrameDrawCalls,
1355
+ totalDrawCalls,
1356
+ atlasSwitches,
1357
+ // Fixed at creation: five programs compiled once, never per frame, so
1358
+ // these are capability facts rather than counters.
1359
+ programs: 5,
1360
+ textures: (texture ? 1 : 0) + (msdfTexture ? 1 : 0),
1361
+ circleQuadFallbacks,
1362
+ circlePoints
1363
+ };
1364
+ },
1269
1365
  begin() {
1366
+ lastFrameDrawCalls = frameDrawCalls;
1367
+ totalDrawCalls += frameDrawCalls;
1368
+ frameDrawCalls = 0;
1270
1369
  pointCount = 0;
1271
1370
  rectCount = 0;
1272
1371
  spriteCount = 0;
@@ -1319,6 +1418,7 @@ function createWebGLPointRenderer(canvas) {
1319
1418
  distanceRange = range;
1320
1419
  return;
1321
1420
  }
1421
+ atlasSwitches++;
1322
1422
  drawGlyphs();
1323
1423
  distanceRange = range;
1324
1424
  if (!msdfTexture) {
@@ -1361,6 +1461,7 @@ function createWebGLPointRenderer(canvas) {
1361
1461
  addCircle(x, y, radius, color, alpha = 1) {
1362
1462
  const needsQuad = logicalW > 0 && (x < radius || y < radius || x > logicalW - radius || y > logicalH - radius) || radius * 2 * dpr > maxPointSize;
1363
1463
  if (needsQuad) {
1464
+ circleQuadFallbacks++;
1364
1465
  const stride = FLOATS_PER_SPRITE_VERT * VERTS_PER_QUAD;
1365
1466
  circleQuadData = grow(circleQuadData, (circleQuadCount + 1) * stride);
1366
1467
  const rgba = parseColorToRGBA(color);
@@ -1385,6 +1486,7 @@ function createWebGLPointRenderer(canvas) {
1385
1486
  circleQuadCount++;
1386
1487
  return;
1387
1488
  }
1489
+ circlePoints++;
1388
1490
  pointData = grow(pointData, (pointCount + 1) * FLOATS_PER_POINT);
1389
1491
  const [r, g, b, a] = parseColorToRGBA(color);
1390
1492
  const o = pointCount * FLOATS_PER_POINT;
@@ -1426,6 +1528,7 @@ function createWebGLPointRenderer(canvas) {
1426
1528
  gl.uniform2f(rURes, logicalW, logicalH);
1427
1529
  ensureQuadIndices(rectCount);
1428
1530
  gl.drawElements(gl.TRIANGLES, rectCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1531
+ frameDrawCalls++;
1429
1532
  }
1430
1533
  if (pointCount > 0) {
1431
1534
  gl.useProgram(pointProgram);
@@ -1439,6 +1542,7 @@ function createWebGLPointRenderer(canvas) {
1439
1542
  gl.uniform2f(pURes, logicalW, logicalH);
1440
1543
  gl.uniform1f(pUDpr, dpr);
1441
1544
  gl.drawArrays(gl.POINTS, 0, pointCount);
1545
+ frameDrawCalls++;
1442
1546
  }
1443
1547
  if (circleQuadCount > 0) {
1444
1548
  const floats = circleQuadCount * VERTS_PER_QUAD * FLOATS_PER_SPRITE_VERT;
@@ -1449,6 +1553,7 @@ function createWebGLPointRenderer(canvas) {
1449
1553
  gl.uniform2f(cURes, logicalW, logicalH);
1450
1554
  ensureQuadIndices(circleQuadCount);
1451
1555
  gl.drawElements(gl.TRIANGLES, circleQuadCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1556
+ frameDrawCalls++;
1452
1557
  }
1453
1558
  if (spriteCount > 0 && texture) {
1454
1559
  const floats = spriteCount * VERTS_PER_QUAD * FLOATS_PER_SPRITE_VERT;
@@ -1462,6 +1567,7 @@ function createWebGLPointRenderer(canvas) {
1462
1567
  gl.uniform2f(sURes, logicalW, logicalH);
1463
1568
  ensureQuadIndices(spriteCount);
1464
1569
  gl.drawElements(gl.TRIANGLES, spriteCount * INDICES_PER_QUAD, gl.UNSIGNED_INT, 0);
1570
+ frameDrawCalls++;
1465
1571
  }
1466
1572
  gl.bindVertexArray(null);
1467
1573
  drawGlyphs();