uplot-webgpu 0.1.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.
Files changed (50) hide show
  1. package/CANVAS_PROXY.md +602 -0
  2. package/README.md +854 -0
  3. package/favicon.ico +0 -0
  4. package/index.html +14 -0
  5. package/index.js +21 -0
  6. package/original/paths.canvas2d/bars.js +252 -0
  7. package/original/paths.canvas2d/catmullRomCentrip.js +125 -0
  8. package/original/paths.canvas2d/linear.js +170 -0
  9. package/original/paths.canvas2d/monotoneCubic.js +68 -0
  10. package/original/paths.canvas2d/points.js +66 -0
  11. package/original/paths.canvas2d/spline.js +103 -0
  12. package/original/paths.canvas2d/stepped.js +124 -0
  13. package/original/paths.canvas2d/utils.js +301 -0
  14. package/original/uPlot.canvas2d.js +3548 -0
  15. package/package.json +110 -0
  16. package/paths/bars.js +253 -0
  17. package/paths/catmullRomCentrip.js +126 -0
  18. package/paths/linear.js +171 -0
  19. package/paths/monotoneCubic.js +69 -0
  20. package/paths/points.js +67 -0
  21. package/paths/spline.js +104 -0
  22. package/paths/stepped.js +125 -0
  23. package/paths/utils.js +301 -0
  24. package/scripts/uPlot.css +168 -0
  25. package/scripts/uPlot.d.ts +26 -0
  26. package/scripts/uPlot.js +3687 -0
  27. package/scripts/utils/dom.js +124 -0
  28. package/scripts/utils/domClasses.js +22 -0
  29. package/scripts/utils/feats.js +13 -0
  30. package/scripts/utils/fmtDate.js +398 -0
  31. package/scripts/utils/opts.js +844 -0
  32. package/scripts/utils/strings.js +22 -0
  33. package/scripts/utils/sync.js +27 -0
  34. package/scripts/utils/utils.js +692 -0
  35. package/scripts/webgpu/GPUPath.d.ts +46 -0
  36. package/scripts/webgpu/GPUPath.js +633 -0
  37. package/scripts/webgpu/GPUPath.ts +634 -0
  38. package/scripts/webgpu/WebGPURenderer.d.ts +176 -0
  39. package/scripts/webgpu/WebGPURenderer.js +4256 -0
  40. package/scripts/webgpu/WebGPURenderer.ts +4257 -0
  41. package/scripts/webgpu/browserSmokeHarness.js +105 -0
  42. package/scripts/webgpu/exporters.d.ts +8 -0
  43. package/scripts/webgpu/exporters.js +212 -0
  44. package/scripts/webgpu/shaders.d.ts +2 -0
  45. package/scripts/webgpu/shaders.js +76 -0
  46. package/scripts/webgpu/shaders.ts +77 -0
  47. package/scripts/webgpu/smokeTest.d.ts +2 -0
  48. package/scripts/webgpu/smokeTest.js +144 -0
  49. package/scripts/webgpu/webgpu-ambient.d.ts +41 -0
  50. package/tinybuild.config.js +109 -0
@@ -0,0 +1,4257 @@
1
+ // @ts-nocheck
2
+ import { GPUPath } from './GPUPath.js';
3
+ import { CHART_WGSL, IMAGE_WGSL } from './shaders.js';
4
+
5
+ const DEFAULT_COLOR = [0, 0, 0, 1];
6
+ const TRANSPARENT = [0, 0, 0, 0];
7
+ const EPS = 1e-9;
8
+ const FLOATS_PER_VERTEX = 6;
9
+ const FLOATS_PER_IMAGE_VERTEX = 5;
10
+ const CONTEXT_ATTRIBUTES = Object.freeze({
11
+ alpha: true,
12
+ colorSpace: 'srgb',
13
+ colorType: 'unorm8',
14
+ desynchronized: false,
15
+ willReadFrequently: false,
16
+ });
17
+
18
+ const MEMORY_PRESETS = Object.freeze({
19
+ low: {
20
+ releaseCommandsAfterPresent: true,
21
+ maxRetainedUploadBytes: 1 * 1024 * 1024,
22
+ maxRetainedVertexBufferBytes: 8 * 1024 * 1024,
23
+ maxTextureBytes: 16 * 1024 * 1024,
24
+ maxTextureRecords: 24,
25
+ maxColorCacheEntries: 384,
26
+ maxTextMeasureCacheEntries: 512,
27
+ },
28
+ balanced: {
29
+ releaseCommandsAfterPresent: true,
30
+ maxRetainedUploadBytes: 4 * 1024 * 1024,
31
+ maxRetainedVertexBufferBytes: 32 * 1024 * 1024,
32
+ maxTextureBytes: 64 * 1024 * 1024,
33
+ maxTextureRecords: 64,
34
+ maxColorCacheEntries: 768,
35
+ maxTextMeasureCacheEntries: 1024,
36
+ },
37
+ throughput: {
38
+ releaseCommandsAfterPresent: false,
39
+ maxRetainedUploadBytes: 64 * 1024 * 1024,
40
+ maxRetainedVertexBufferBytes: 256 * 1024 * 1024,
41
+ maxTextureBytes: 256 * 1024 * 1024,
42
+ maxTextureRecords: 256,
43
+ maxColorCacheEntries: 2048,
44
+ maxTextMeasureCacheEntries: 4096,
45
+ },
46
+ });
47
+
48
+ function byteLimit(value, fallback) {
49
+ value = Number(value);
50
+ return Number.isFinite(value) && value >= 0 ? value : fallback;
51
+ }
52
+
53
+ function countLimit(value, fallback) {
54
+ value = Number(value);
55
+ return Number.isFinite(value) && value >= 0 ? Math.floor(value) : fallback;
56
+ }
57
+
58
+ function normalizeMemoryOptions(options = null) {
59
+ let mode = String(options?.memoryMode || options?.memory || 'balanced').toLowerCase();
60
+ let base = MEMORY_PRESETS[mode] || MEMORY_PRESETS.balanced;
61
+ return {
62
+ mode: MEMORY_PRESETS[mode] ? mode : 'balanced',
63
+ releaseCommandsAfterPresent: options?.retainCommands === true || options?.retainCommandsForReadback === true ? false : options?.releaseCommandsAfterPresent !== false && base.releaseCommandsAfterPresent,
64
+ maxRetainedUploadBytes: byteLimit(options?.maxRetainedUploadBytes, base.maxRetainedUploadBytes),
65
+ maxRetainedVertexBufferBytes: byteLimit(options?.maxRetainedVertexBufferBytes, base.maxRetainedVertexBufferBytes),
66
+ maxTextureBytes: byteLimit(options?.maxTextureBytes, base.maxTextureBytes),
67
+ maxTextureRecords: countLimit(options?.maxTextureRecords, base.maxTextureRecords),
68
+ maxColorCacheEntries: countLimit(options?.maxColorCacheEntries, base.maxColorCacheEntries),
69
+ maxTextMeasureCacheEntries: countLimit(options?.maxTextMeasureCacheEntries, base.maxTextMeasureCacheEntries),
70
+ };
71
+ }
72
+
73
+ function textureByteSize(width, height) {
74
+ return Math.max(0, Math.floor(width || 0)) * Math.max(0, Math.floor(height || 0)) * 4;
75
+ }
76
+
77
+ function byteLengthOf(value) {
78
+ return value?.byteLength || 0;
79
+ }
80
+
81
+
82
+ function perfNow() {
83
+ return typeof performance != 'undefined' && typeof performance.now == 'function' ? performance.now() : Date.now();
84
+ }
85
+
86
+ function identity() {
87
+ return [1, 0, 0, 1, 0, 0];
88
+ }
89
+
90
+ function cloneState(state) {
91
+ return {
92
+ strokeStyle: state.strokeStyle,
93
+ fillStyle: state.fillStyle,
94
+ lineWidth: state.lineWidth,
95
+ lineJoin: state.lineJoin,
96
+ lineCap: state.lineCap,
97
+ lineDash: state.lineDash.slice(),
98
+ font: state.font,
99
+ textAlign: state.textAlign,
100
+ textBaseline: state.textBaseline,
101
+ globalAlpha: state.globalAlpha,
102
+ transform: state.transform.slice(),
103
+ clipRegions: cloneClipRegions(state.clipRegions),
104
+ lineDashOffset: state.lineDashOffset,
105
+ miterLimit: state.miterLimit,
106
+ globalCompositeOperation: state.globalCompositeOperation,
107
+ shadowColor: state.shadowColor,
108
+ shadowBlur: state.shadowBlur,
109
+ shadowOffsetX: state.shadowOffsetX,
110
+ shadowOffsetY: state.shadowOffsetY,
111
+ direction: state.direction,
112
+ filter: state.filter,
113
+ fontKerning: state.fontKerning,
114
+ fontStretch: state.fontStretch,
115
+ fontVariantCaps: state.fontVariantCaps,
116
+ letterSpacing: state.letterSpacing,
117
+ textRendering: state.textRendering,
118
+ wordSpacing: state.wordSpacing,
119
+ imageSmoothingEnabled: state.imageSmoothingEnabled,
120
+ imageSmoothingQuality: state.imageSmoothingQuality,
121
+ };
122
+ }
123
+
124
+ function cloneClipRegions(regions) {
125
+ return regions == null ? null : regions.map(poly => poly.map(p => [p[0], p[1]]));
126
+ }
127
+
128
+ function normalizeRect(rect) {
129
+ let {x, y, w, h} = rect;
130
+
131
+ if (w < 0) {
132
+ x += w;
133
+ w = -w;
134
+ }
135
+
136
+ if (h < 0) {
137
+ y += h;
138
+ h = -h;
139
+ }
140
+
141
+ return {x, y, w, h};
142
+ }
143
+
144
+ function rectToPolygon(rect, m) {
145
+ let r = normalizeRect(rect);
146
+ return [
147
+ transformPoint(m, r.x, r.y),
148
+ transformPoint(m, r.x + r.w, r.y),
149
+ transformPoint(m, r.x + r.w, r.y + r.h),
150
+ transformPoint(m, r.x, r.y + r.h),
151
+ ];
152
+ }
153
+
154
+ function polygonArea(poly) {
155
+ let sum = 0;
156
+
157
+ for (let i = 0, j = poly.length - 1; i < poly.length; j = i++)
158
+ sum += poly[j][0] * poly[i][1] - poly[i][0] * poly[j][1];
159
+
160
+ return sum / 2;
161
+ }
162
+
163
+ function lineIntersection(a, b, c, d) {
164
+ let bax = b[0] - a[0];
165
+ let bay = b[1] - a[1];
166
+ let dcx = d[0] - c[0];
167
+ let dcy = d[1] - c[1];
168
+ let den = bax * dcy - bay * dcx;
169
+
170
+ if (Math.abs(den) < EPS)
171
+ return b;
172
+
173
+ let acx = c[0] - a[0];
174
+ let acy = c[1] - a[1];
175
+ let t = (acx * dcy - acy * dcx) / den;
176
+ return [a[0] + bax * t, a[1] + bay * t];
177
+ }
178
+
179
+ function clipConvexPolygon(subject, clipper) {
180
+ if (subject.length < 3 || clipper.length < 3)
181
+ return [];
182
+
183
+ let out = subject;
184
+ let ccw = polygonArea(clipper) >= 0;
185
+
186
+ for (let i = 0; i < clipper.length; i++) {
187
+ let a = clipper[i];
188
+ let b = clipper[(i + 1) % clipper.length];
189
+ let input = out;
190
+ out = [];
191
+
192
+ if (input.length == 0)
193
+ break;
194
+
195
+ let inside = p => {
196
+ let cross = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]);
197
+ return ccw ? cross >= -EPS : cross <= EPS;
198
+ };
199
+
200
+ let prev = input[input.length - 1];
201
+ let prevInside = inside(prev);
202
+
203
+ for (let curr of input) {
204
+ let currInside = inside(curr);
205
+
206
+ if (currInside) {
207
+ if (!prevInside)
208
+ out.push(lineIntersection(prev, curr, a, b));
209
+ out.push(curr);
210
+ }
211
+ else if (prevInside)
212
+ out.push(lineIntersection(prev, curr, a, b));
213
+
214
+ prev = curr;
215
+ prevInside = currInside;
216
+ }
217
+ }
218
+
219
+ return cleanPolygon(out);
220
+ }
221
+
222
+ function triangleToRegion(tri) {
223
+ return [tri[0], tri[1], tri[2]];
224
+ }
225
+
226
+ function rectsToEvenOddRegions(rects, transform) {
227
+ let normalized = rects
228
+ .map(normalizeRect)
229
+ .filter(r => r.w > EPS && r.h > EPS);
230
+
231
+ if (normalized.length == 0)
232
+ return [];
233
+
234
+ let xs = [];
235
+ let ys = [];
236
+ for (let r of normalized) {
237
+ xs.push(r.x, r.x + r.w);
238
+ ys.push(r.y, r.y + r.h);
239
+ }
240
+ xs = Array.from(new Set(xs)).sort((a, b) => a - b);
241
+ ys = Array.from(new Set(ys)).sort((a, b) => a - b);
242
+
243
+ let regions = [];
244
+ for (let yi = 0; yi < ys.length - 1; yi++) {
245
+ let y0 = ys[yi];
246
+ let y1 = ys[yi + 1];
247
+ if (y1 - y0 <= EPS)
248
+ continue;
249
+ for (let xi = 0; xi < xs.length - 1; xi++) {
250
+ let x0 = xs[xi];
251
+ let x1 = xs[xi + 1];
252
+ if (x1 - x0 <= EPS)
253
+ continue;
254
+ let cx = (x0 + x1) / 2;
255
+ let cy = (y0 + y1) / 2;
256
+ let hits = 0;
257
+ for (let r of normalized) {
258
+ if (cx >= r.x - EPS && cx <= r.x + r.w + EPS && cy >= r.y - EPS && cy <= r.y + r.h + EPS)
259
+ hits++;
260
+ }
261
+ if ((hits & 1) == 1)
262
+ regions.push(rectToPolygon({x: x0, y: y0, w: x1 - x0, h: y1 - y0}, transform));
263
+ }
264
+ }
265
+
266
+ return regions;
267
+ }
268
+
269
+ function rectsToNonZeroRegions(rects, transform) {
270
+ let normalized = rects
271
+ .map(r => {
272
+ let winding = Number.isFinite(Number(r.winding)) ? Math.sign(Number(r.winding)) : Math.sign(Number(r.w) * Number(r.h));
273
+ return {...normalizeRect(r), winding: winding || 0};
274
+ })
275
+ .filter(r => r.w > EPS && r.h > EPS && r.winding != 0);
276
+
277
+ if (normalized.length == 0)
278
+ return [];
279
+
280
+ let xs = [];
281
+ let ys = [];
282
+ for (let r of normalized) {
283
+ xs.push(r.x, r.x + r.w);
284
+ ys.push(r.y, r.y + r.h);
285
+ }
286
+ xs = Array.from(new Set(xs)).sort((a, b) => a - b);
287
+ ys = Array.from(new Set(ys)).sort((a, b) => a - b);
288
+
289
+ let regions = [];
290
+ for (let yi = 0; yi < ys.length - 1; yi++) {
291
+ let y0 = ys[yi];
292
+ let y1 = ys[yi + 1];
293
+ if (y1 - y0 <= EPS)
294
+ continue;
295
+ for (let xi = 0; xi < xs.length - 1; xi++) {
296
+ let x0 = xs[xi];
297
+ let x1 = xs[xi + 1];
298
+ if (x1 - x0 <= EPS)
299
+ continue;
300
+ let cx = (x0 + x1) / 2;
301
+ let cy = (y0 + y1) / 2;
302
+ let winding = 0;
303
+ for (let r of normalized) {
304
+ if (cx >= r.x - EPS && cx <= r.x + r.w + EPS && cy >= r.y - EPS && cy <= r.y + r.h + EPS)
305
+ winding += r.winding;
306
+ }
307
+ if (winding != 0)
308
+ regions.push(rectToPolygon({x: x0, y: y0, w: x1 - x0, h: y1 - y0}, transform));
309
+ }
310
+ }
311
+
312
+ return regions;
313
+ }
314
+
315
+ function pathToClipRegions(path, transform, fillRule = 'nonzero') {
316
+ if (path == null)
317
+ return [];
318
+
319
+ let rects = path.getRects?.();
320
+
321
+ if (rects != null) {
322
+ if (fillRule == 'evenodd' && rects.length > 1)
323
+ return rectsToEvenOddRegions(rects, transform);
324
+ if (fillRule == 'nonzero' && rects.length > 1)
325
+ return rectsToNonZeroRegions(rects, transform);
326
+ return rects.map(rect => rectToPolygon(rect, transform));
327
+ }
328
+
329
+ let regions = [];
330
+ let subpaths = path.toSubpaths?.() || [];
331
+ let polys = subpaths
332
+ .map(sub => cleanPolygon(sub.map(p => transformPoint(transform, p[0], p[1]))))
333
+ .filter(poly => poly.length >= 3 && Math.abs(polygonArea(poly)) > EPS);
334
+
335
+ if (polys.length == 1)
336
+ return triangulate(polys[0]).map(triangleToRegion);
337
+
338
+ for (let poly of polys) {
339
+ for (let tri of triangulate(poly)) {
340
+ let cx = (tri[0][0] + tri[1][0] + tri[2][0]) / 3;
341
+ let cy = (tri[0][1] + tri[1][1] + tri[2][1]) / 3;
342
+ if (fillContainsPoint([cx, cy], polys, fillRule))
343
+ regions.push(triangleToRegion(tri));
344
+ }
345
+ }
346
+
347
+ return regions;
348
+ }
349
+
350
+ function pathToFillRegions(path, transform, fillRule = 'nonzero') {
351
+ if (path == null)
352
+ return [];
353
+
354
+ return pathToClipRegions(path, transform, fillRule);
355
+ }
356
+
357
+
358
+ function combineClipRegions(oldRegions, newRegions) {
359
+ newRegions = newRegions.filter(poly => poly.length >= 3 && Math.abs(polygonArea(poly)) > EPS);
360
+
361
+ if (newRegions.length == 0)
362
+ return [];
363
+
364
+ if (oldRegions == null)
365
+ return newRegions;
366
+
367
+ let out = [];
368
+
369
+ for (let a of oldRegions) {
370
+ for (let b of newRegions) {
371
+ let clipped = clipConvexPolygon(a, b);
372
+ if (clipped.length >= 3 && Math.abs(polygonArea(clipped)) > EPS)
373
+ out.push(clipped);
374
+ }
375
+ }
376
+
377
+ return out;
378
+ }
379
+
380
+ function pushTri(out, a, b, c, color) {
381
+ pushPaintedTriangle(out, a, b, c, color);
382
+ }
383
+
384
+ function fanEmit(out, poly, color) {
385
+ poly = cleanPolygon(poly);
386
+
387
+ for (let i = 1; i < poly.length - 1; i++)
388
+ pushTri(out, poly[0], poly[i], poly[i + 1], color);
389
+ }
390
+
391
+ function vertexIntersection(a, b, c, d) {
392
+ let bax = b[0] - a[0];
393
+ let bay = b[1] - a[1];
394
+ let dcx = d[0] - c[0];
395
+ let dcy = d[1] - c[1];
396
+ let den = bax * dcy - bay * dcx;
397
+
398
+ if (Math.abs(den) < EPS)
399
+ return b.slice();
400
+
401
+ let acx = c[0] - a[0];
402
+ let acy = c[1] - a[1];
403
+ let t = (acx * dcy - acy * dcx) / den;
404
+ let out = [a[0] + bax * t, a[1] + bay * t];
405
+ for (let i = 2; i < FLOATS_PER_VERTEX; i++)
406
+ out[i] = a[i] + (b[i] - a[i]) * t;
407
+ return out;
408
+ }
409
+
410
+ function clipVertexPolygon(subject, clipper) {
411
+ if (subject.length < 3 || clipper.length < 3)
412
+ return [];
413
+
414
+ let out = subject;
415
+ let ccw = polygonArea(clipper) >= 0;
416
+
417
+ for (let i = 0; i < clipper.length; i++) {
418
+ let a = clipper[i];
419
+ let b = clipper[(i + 1) % clipper.length];
420
+ let input = out;
421
+ out = [];
422
+
423
+ if (input.length == 0)
424
+ break;
425
+
426
+ let inside = p => {
427
+ let cross = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]);
428
+ return ccw ? cross >= -EPS : cross <= EPS;
429
+ };
430
+
431
+ let prev = input[input.length - 1];
432
+ let prevInside = inside(prev);
433
+
434
+ for (let curr of input) {
435
+ let currInside = inside(curr);
436
+
437
+ if (currInside) {
438
+ if (!prevInside)
439
+ out.push(vertexIntersection(prev, curr, a, b));
440
+ out.push(curr);
441
+ }
442
+ else if (prevInside)
443
+ out.push(vertexIntersection(prev, curr, a, b));
444
+
445
+ prev = curr;
446
+ prevInside = currInside;
447
+ }
448
+ }
449
+
450
+ return out;
451
+ }
452
+
453
+ function fanEmitVertices(out, poly) {
454
+ for (let i = 1; i < poly.length - 1; i++)
455
+ out.push(...poly[0], ...poly[i], ...poly[i + 1]);
456
+ }
457
+
458
+ function clipVertices(vertices, regions) {
459
+ if (regions == null)
460
+ return vertices;
461
+
462
+ if (regions.length == 0)
463
+ return [];
464
+
465
+ let out = [];
466
+
467
+ for (let i = 0; i < vertices.length; i += FLOATS_PER_VERTEX * 3) {
468
+ let tri = [
469
+ Array.from(vertices.slice(i, i + FLOATS_PER_VERTEX)),
470
+ Array.from(vertices.slice(i + FLOATS_PER_VERTEX, i + FLOATS_PER_VERTEX * 2)),
471
+ Array.from(vertices.slice(i + FLOATS_PER_VERTEX * 2, i + FLOATS_PER_VERTEX * 3)),
472
+ ];
473
+
474
+ for (let region of regions) {
475
+ let clipped = clipVertexPolygon(tri, region);
476
+ if (clipped.length >= 3)
477
+ fanEmitVertices(out, clipped);
478
+ }
479
+ }
480
+
481
+ return out;
482
+ }
483
+
484
+ function transformPoint(m, x, y) {
485
+ return [m[0] * x + m[2] * y + m[4], m[1] * x + m[3] * y + m[5]];
486
+ }
487
+
488
+ function transformVec(m, x, y) {
489
+ return [m[0] * x + m[2] * y, m[1] * x + m[3] * y];
490
+ }
491
+
492
+ function transformScale(m) {
493
+ let sx = Math.hypot(m[0], m[1]);
494
+ let sy = Math.hypot(m[2], m[3]);
495
+ return (sx + sy) / 2 || 1;
496
+ }
497
+
498
+ function pointInPolygon(point, polygon) {
499
+ let inside = false;
500
+ let x = point[0];
501
+ let y = point[1];
502
+
503
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
504
+ let xi = polygon[i][0];
505
+ let yi = polygon[i][1];
506
+ let xj = polygon[j][0];
507
+ let yj = polygon[j][1];
508
+ let intersects = ((yi > y) != (yj > y)) && x < (xj - xi) * (y - yi) / ((yj - yi) || EPS) + xi;
509
+ if (intersects)
510
+ inside = !inside;
511
+ }
512
+
513
+ return inside;
514
+ }
515
+
516
+ function windingNumber(point, polygon) {
517
+ let x = point[0];
518
+ let y = point[1];
519
+ let winding = 0;
520
+
521
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
522
+ let a = polygon[j];
523
+ let b = polygon[i];
524
+
525
+ if (a[1] <= y) {
526
+ if (b[1] > y && ((b[0] - a[0]) * (y - a[1]) - (x - a[0]) * (b[1] - a[1])) > 0)
527
+ winding++;
528
+ }
529
+ else if (b[1] <= y && ((b[0] - a[0]) * (y - a[1]) - (x - a[0]) * (b[1] - a[1])) < 0)
530
+ winding--;
531
+ }
532
+
533
+ return winding;
534
+ }
535
+
536
+ function fillContainsPoint(point, polygons, fillRule = 'nonzero') {
537
+ if (polygons == null || polygons.length == 0)
538
+ return false;
539
+
540
+ if (fillRule == 'evenodd') {
541
+ let hits = 0;
542
+ for (let poly of polygons) {
543
+ if (pointInPolygon(point, poly))
544
+ hits++;
545
+ }
546
+ return (hits & 1) == 1;
547
+ }
548
+
549
+ let winding = 0;
550
+ for (let poly of polygons)
551
+ winding += windingNumber(point, poly);
552
+ return winding != 0;
553
+ }
554
+
555
+ function pointInClipRegions(point, regions) {
556
+ if (regions == null)
557
+ return true;
558
+
559
+ for (let region of regions) {
560
+ if (pointInPolygon(point, region))
561
+ return true;
562
+ }
563
+
564
+ return false;
565
+ }
566
+
567
+ function multiply(a, b) {
568
+ return [
569
+ a[0] * b[0] + a[2] * b[1],
570
+ a[1] * b[0] + a[3] * b[1],
571
+ a[0] * b[2] + a[2] * b[3],
572
+ a[1] * b[2] + a[3] * b[3],
573
+ a[0] * b[4] + a[2] * b[5] + a[4],
574
+ a[1] * b[4] + a[3] * b[5] + a[5],
575
+ ];
576
+ }
577
+
578
+ function colorKey(color, alpha) {
579
+ return color + '@' + alpha;
580
+ }
581
+
582
+ function applyAlpha(color, alpha) {
583
+ let a = color[3] * alpha;
584
+ return [color[0] * a, color[1] * a, color[2] * a, a];
585
+ }
586
+
587
+ function parseHex(hex) {
588
+ let h = hex.slice(1);
589
+
590
+ if (h.length == 3 || h.length == 4)
591
+ h = h.split('').map(c => c + c).join('');
592
+
593
+ if (h.length == 6)
594
+ h += 'ff';
595
+
596
+ if (h.length != 8)
597
+ return null;
598
+
599
+ let n = Number.parseInt(h, 16);
600
+ return [
601
+ ((n >>> 24) & 255) / 255,
602
+ ((n >>> 16) & 255) / 255,
603
+ ((n >>> 8) & 255) / 255,
604
+ (n & 255) / 255,
605
+ ];
606
+ }
607
+
608
+ function parseRgb(str) {
609
+ let m = str.match(/^rgba?\((.*)\)$/i);
610
+ if (m == null)
611
+ return null;
612
+
613
+ let raw = m[1].trim();
614
+ let alphaPart = null;
615
+ if (raw.includes('/')) {
616
+ let split = raw.split('/');
617
+ raw = split[0].trim();
618
+ alphaPart = split.slice(1).join('/').trim();
619
+ }
620
+
621
+ let parts = raw.includes(',') ? raw.split(',').map(v => v.trim()) : raw.split(/\s+/).filter(Boolean);
622
+ if (parts.length < 3)
623
+ return null;
624
+ if (parts.length > 3 && alphaPart == null)
625
+ alphaPart = parts[3];
626
+
627
+ let channel = v => {
628
+ let n = Number.parseFloat(v);
629
+ if (!Number.isFinite(n))
630
+ return 0;
631
+ if (String(v).endsWith('%'))
632
+ return Math.max(0, Math.min(255, n * 2.55));
633
+ return Math.max(0, Math.min(255, n));
634
+ };
635
+ let alpha = v => {
636
+ if (v == null || v === '')
637
+ return 1;
638
+ let n = Number.parseFloat(v);
639
+ if (!Number.isFinite(n))
640
+ return 1;
641
+ if (String(v).endsWith('%'))
642
+ return Math.max(0, Math.min(1, n / 100));
643
+ return Math.max(0, Math.min(1, n));
644
+ };
645
+
646
+ return [
647
+ channel(parts[0]) / 255,
648
+ channel(parts[1]) / 255,
649
+ channel(parts[2]) / 255,
650
+ alpha(alphaPart),
651
+ ];
652
+ }
653
+
654
+
655
+ function clamp01(v) {
656
+ return Math.max(0, Math.min(1, v));
657
+ }
658
+
659
+ function interpColor(a, b, t, alpha) {
660
+ let c = [
661
+ a[0] + (b[0] - a[0]) * t,
662
+ a[1] + (b[1] - a[1]) * t,
663
+ a[2] + (b[2] - a[2]) * t,
664
+ a[3] + (b[3] - a[3]) * t,
665
+ ];
666
+ return applyAlpha(c, alpha);
667
+ }
668
+
669
+ function parsePlainCssColor(str) {
670
+ if (str == null || str === 'transparent')
671
+ return TRANSPARENT;
672
+
673
+ if (Array.isArray(str))
674
+ return str;
675
+
676
+ if (typeof str == 'string') {
677
+ let s = str.trim();
678
+ if (s[0] == '#')
679
+ return parseHex(s) || DEFAULT_COLOR;
680
+ if (s.startsWith('rgb'))
681
+ return parseRgb(s) || DEFAULT_COLOR;
682
+ return parseCssColorWithDom(s) || DEFAULT_COLOR;
683
+ }
684
+
685
+ return DEFAULT_COLOR;
686
+ }
687
+
688
+ function normalizeStops(stops) {
689
+ let parsed = stops
690
+ .map(s => ({offset: clamp01(s.offset), color: parsePlainCssColor(s.color)}))
691
+ .sort((a, b) => a.offset - b.offset);
692
+
693
+ if (parsed.length == 0)
694
+ parsed.push({offset: 0, color: DEFAULT_COLOR}, {offset: 1, color: DEFAULT_COLOR});
695
+ else if (parsed.length == 1)
696
+ parsed.push({offset: 1, color: parsed[0].color});
697
+
698
+ return parsed;
699
+ }
700
+
701
+ function sampleStops(stops, t, alpha) {
702
+ stops = normalizeStops(stops);
703
+ t = clamp01(t);
704
+
705
+ if (t <= stops[0].offset)
706
+ return applyAlpha(stops[0].color, alpha);
707
+
708
+ for (let i = 1; i < stops.length; i++) {
709
+ let lo = stops[i - 1];
710
+ let hi = stops[i];
711
+
712
+ if (t <= hi.offset) {
713
+ let span = hi.offset - lo.offset;
714
+ let local = span <= EPS ? 0 : (t - lo.offset) / span;
715
+ return interpColor(lo.color, hi.color, local, alpha);
716
+ }
717
+ }
718
+
719
+ return applyAlpha(stops[stops.length - 1].color, alpha);
720
+ }
721
+
722
+ function makeLinearGradient(x0, y0, x1, y1) {
723
+ let stops = [];
724
+ let dx = x1 - x0;
725
+ let dy = y1 - y0;
726
+ let den = dx * dx + dy * dy || 1;
727
+
728
+ return {
729
+ _uPlotWebGPUGradient: true,
730
+ addColorStop(offset, color) {
731
+ stops.push({offset: Number(offset), color});
732
+ },
733
+ colorAt(x, y, alpha = 1) {
734
+ let t = ((x - x0) * dx + (y - y0) * dy) / den;
735
+ return sampleStops(stops, t, alpha);
736
+ },
737
+ };
738
+ }
739
+
740
+ function makeRadialGradient(x0, y0, r0, x1, y1, r1) {
741
+ let stops = [];
742
+ let dr = r1 - r0 || 1;
743
+
744
+ return {
745
+ _uPlotWebGPUGradient: true,
746
+ addColorStop(offset, color) {
747
+ stops.push({offset: Number(offset), color});
748
+ },
749
+ colorAt(x, y, alpha = 1) {
750
+ let d0 = Math.hypot(x - x0, y - y0);
751
+ let d1 = Math.hypot(x - x1, y - y1);
752
+ let d = x0 == x1 && y0 == y1 ? d0 : (d0 + d1) / 2;
753
+ return sampleStops(stops, (d - r0) / dr, alpha);
754
+ },
755
+ };
756
+ }
757
+
758
+ function parseCssColorWithDom(str) {
759
+ if (typeof document == 'undefined')
760
+ return null;
761
+
762
+ let probe = parseCssColorWithDom._probe;
763
+
764
+ if (probe == null) {
765
+ probe = document.createElement('span');
766
+ probe.style.position = 'absolute';
767
+ probe.style.left = '-99999px';
768
+ probe.style.top = '-99999px';
769
+ probe.style.visibility = 'hidden';
770
+ document.documentElement.appendChild(probe);
771
+ parseCssColorWithDom._probe = probe;
772
+ }
773
+
774
+ probe.style.color = '';
775
+ probe.style.color = str;
776
+
777
+ if (!probe.style.color)
778
+ return null;
779
+
780
+ return parseRgb(getComputedStyle(probe).color);
781
+ }
782
+
783
+ function paintColor(paint, x, y) {
784
+ return paint?.colorAt ? paint.colorAt(x, y) : paint;
785
+ }
786
+
787
+ function pushVertex(out, x, y, color) {
788
+ let c = paintColor(color, x, y);
789
+ out.push(x, y, c[0], c[1], c[2], c[3]);
790
+ }
791
+
792
+ function pushSolidVertex(out, x, y, color) {
793
+ out.push(x, y, color[0], color[1], color[2], color[3]);
794
+ }
795
+
796
+ function pushPaintedTriangleCoords(out, ax, ay, bx, by, cx, cy, color) {
797
+ if (Array.isArray(color)) {
798
+ pushSolidVertex(out, ax, ay, color);
799
+ pushSolidVertex(out, bx, by, color);
800
+ pushSolidVertex(out, cx, cy, color);
801
+ }
802
+ else {
803
+ pushVertex(out, ax, ay, color);
804
+ pushVertex(out, bx, by, color);
805
+ pushVertex(out, cx, cy, color);
806
+ }
807
+ }
808
+
809
+ function pushPaintedTriangle(out, a, b, c, color) {
810
+ pushPaintedTriangleCoords(out, a[0], a[1], b[0], b[1], c[0], c[1], color);
811
+ }
812
+
813
+ function pushPaintedRegion(out, region, color) {
814
+ if (region.length == 3) {
815
+ pushPaintedTriangle(out, region[0], region[1], region[2], color);
816
+ return;
817
+ }
818
+
819
+ if (region.length == 4) {
820
+ pushPaintedTriangle(out, region[0], region[1], region[2], color);
821
+ pushPaintedTriangle(out, region[0], region[2], region[3], color);
822
+ return;
823
+ }
824
+
825
+ for (let tri of triangulate(region))
826
+ pushPaintedTriangle(out, tri[0], tri[1], tri[2], color);
827
+ }
828
+
829
+ function appendNumericArray(out, values) {
830
+ for (let i = 0; i < values.length; i++)
831
+ out.push(values[i]);
832
+ }
833
+
834
+ function signedArea(points) {
835
+ let sum = 0;
836
+
837
+ for (let i = 0, j = points.length - 1; i < points.length; j = i++)
838
+ sum += (points[j][0] * points[i][1]) - (points[i][0] * points[j][1]);
839
+
840
+ return sum / 2;
841
+ }
842
+
843
+ function cleanPolygon(points) {
844
+ let out = [];
845
+
846
+ for (let p of points) {
847
+ let q = out[out.length - 1];
848
+ if (q == null || Math.abs(q[0] - p[0]) > EPS || Math.abs(q[1] - p[1]) > EPS)
849
+ out.push(p);
850
+ }
851
+
852
+ if (out.length > 1) {
853
+ let a = out[0];
854
+ let b = out[out.length - 1];
855
+ if (Math.abs(a[0] - b[0]) <= EPS && Math.abs(a[1] - b[1]) <= EPS)
856
+ out.pop();
857
+ }
858
+
859
+ if (out.length < 3)
860
+ return out;
861
+
862
+ let changed = true;
863
+ while (changed && out.length >= 3) {
864
+ changed = false;
865
+ let reduced = [];
866
+
867
+ for (let i = 0; i < out.length; i++) {
868
+ let a = out[(i + out.length - 1) % out.length];
869
+ let b = out[i];
870
+ let c = out[(i + 1) % out.length];
871
+ let cross = (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
872
+
873
+ if (Math.abs(cross) <= EPS)
874
+ changed = true;
875
+ else
876
+ reduced.push(b);
877
+ }
878
+
879
+ if (reduced.length >= 3)
880
+ out = reduced;
881
+ else
882
+ break;
883
+ }
884
+
885
+ return out;
886
+ }
887
+
888
+ function pointInTri(p, a, b, c) {
889
+ let v0x = c[0] - a[0];
890
+ let v0y = c[1] - a[1];
891
+ let v1x = b[0] - a[0];
892
+ let v1y = b[1] - a[1];
893
+ let v2x = p[0] - a[0];
894
+ let v2y = p[1] - a[1];
895
+ let den = v0x * v1y - v1x * v0y;
896
+
897
+ if (Math.abs(den) < EPS)
898
+ return false;
899
+
900
+ let u = (v2x * v1y - v1x * v2y) / den;
901
+ let v = (v0x * v2y - v2x * v0y) / den;
902
+ return u >= -EPS && v >= -EPS && u + v <= 1 + EPS;
903
+ }
904
+
905
+ function nonDecreasingX(points) {
906
+ for (let i = 1; i < points.length; i++) {
907
+ if (points[i][0] < points[i - 1][0] - EPS)
908
+ return false;
909
+ }
910
+
911
+ return true;
912
+ }
913
+
914
+ function sortedXChain(points) {
915
+ if (nonDecreasingX(points))
916
+ return points;
917
+
918
+ return points
919
+ .map((p, i) => ({p, i}))
920
+ .sort((a, b) => {
921
+ let dx = a.p[0] - b.p[0];
922
+ return Math.abs(dx) > EPS ? dx : a.i - b.i;
923
+ })
924
+ .map(item => item.p);
925
+ }
926
+
927
+ function chainRange(chain) {
928
+ let min = Infinity;
929
+ let max = -Infinity;
930
+
931
+ for (let p of chain) {
932
+ min = Math.min(min, p[0]);
933
+ max = Math.max(max, p[0]);
934
+ }
935
+
936
+ return [min, max];
937
+ }
938
+
939
+ function xMonotoneChains(points) {
940
+ if (points.length < 3)
941
+ return null;
942
+
943
+ let left = 0;
944
+ let right = 0;
945
+
946
+ for (let i = 1; i < points.length; i++) {
947
+ let p = points[i];
948
+ let l = points[left];
949
+ let r = points[right];
950
+
951
+ if (p[0] < l[0] - EPS || (Math.abs(p[0] - l[0]) <= EPS && p[1] < l[1]))
952
+ left = i;
953
+
954
+ if (p[0] > r[0] + EPS || (Math.abs(p[0] - r[0]) <= EPS && p[1] > r[1]))
955
+ right = i;
956
+ }
957
+
958
+ let forward = [points[left]];
959
+ for (let i = left; i != right;) {
960
+ i = (i + 1) % points.length;
961
+ forward.push(points[i]);
962
+ }
963
+
964
+ let backward = [points[left]];
965
+ for (let i = left; i != right;) {
966
+ i = (i + points.length - 1) % points.length;
967
+ backward.push(points[i]);
968
+ }
969
+
970
+ forward = sortedXChain(forward);
971
+ backward = sortedXChain(backward);
972
+
973
+ let fr = chainRange(forward);
974
+ let br = chainRange(backward);
975
+ let overlapMin = Math.max(fr[0], br[0]);
976
+ let overlapMax = Math.min(fr[1], br[1]);
977
+
978
+ if (!Number.isFinite(overlapMin) || !Number.isFinite(overlapMax) || overlapMax - overlapMin <= EPS)
979
+ return null;
980
+
981
+ return [forward, backward];
982
+ }
983
+
984
+ function chainYAtX(chain, x) {
985
+ if (chain.length == 0)
986
+ return 0;
987
+
988
+ for (let i = 1; i < chain.length; i++) {
989
+ let a = chain[i - 1];
990
+ let b = chain[i];
991
+ let dx = b[0] - a[0];
992
+
993
+ if (Math.abs(dx) <= EPS)
994
+ continue;
995
+
996
+ if (x <= b[0] + EPS) {
997
+ let t = clamp01((x - a[0]) / dx);
998
+ return a[1] + (b[1] - a[1]) * t;
999
+ }
1000
+ }
1001
+
1002
+ return chain[chain.length - 1][1];
1003
+ }
1004
+
1005
+ function uniqueSortedXs(chains) {
1006
+ let xs = [];
1007
+
1008
+ for (let chain of chains) {
1009
+ for (let p of chain)
1010
+ xs.push(p[0]);
1011
+ }
1012
+
1013
+ xs.sort((a, b) => a - b);
1014
+ let out = [];
1015
+
1016
+ for (let x of xs) {
1017
+ let prev = out[out.length - 1];
1018
+ if (prev == null || Math.abs(x - prev) > EPS)
1019
+ out.push(x);
1020
+ }
1021
+
1022
+ return out;
1023
+ }
1024
+
1025
+ function triangulateXMonotone(points) {
1026
+ let chains = xMonotoneChains(points);
1027
+ if (chains == null)
1028
+ return null;
1029
+
1030
+ let [a, b] = chains;
1031
+ let xs = uniqueSortedXs(chains);
1032
+ let triangles = [];
1033
+
1034
+ for (let i = 1; i < xs.length; i++) {
1035
+ let x0 = xs[i - 1];
1036
+ let x1 = xs[i];
1037
+
1038
+ if (x1 - x0 <= EPS)
1039
+ continue;
1040
+
1041
+ let a0 = [x0, chainYAtX(a, x0)];
1042
+ let a1 = [x1, chainYAtX(a, x1)];
1043
+ let b0 = [x0, chainYAtX(b, x0)];
1044
+ let b1 = [x1, chainYAtX(b, x1)];
1045
+
1046
+ if (Math.abs(signedArea([a0, a1, b0])) > EPS)
1047
+ triangles.push([a0, a1, b0]);
1048
+
1049
+ if (Math.abs(signedArea([b0, a1, b1])) > EPS)
1050
+ triangles.push([b0, a1, b1]);
1051
+ }
1052
+
1053
+ return triangles;
1054
+ }
1055
+
1056
+ function triangulationArea(triangles) {
1057
+ let area = 0;
1058
+
1059
+ for (let tri of triangles)
1060
+ area += Math.abs(signedArea(tri));
1061
+
1062
+ return area;
1063
+ }
1064
+
1065
+ function triangulationMatchesPolygon(points, triangles) {
1066
+ if (triangles == null || triangles.length == 0)
1067
+ return false;
1068
+
1069
+ let polyArea = Math.abs(signedArea(points));
1070
+ let triArea = triangulationArea(triangles);
1071
+ let tolerance = Math.max(1e-2, polyArea * 1e-3);
1072
+ return Math.abs(triArea - polyArea) <= tolerance;
1073
+ }
1074
+
1075
+ function triangulate(points) {
1076
+ points = cleanPolygon(points);
1077
+
1078
+ if (points.length < 3)
1079
+ return [];
1080
+
1081
+ let monotone = triangulateXMonotone(points);
1082
+ if (monotone != null && monotone.length > 0)
1083
+ return monotone;
1084
+
1085
+ if (points.length > 4096)
1086
+ return fanTriangulate(points);
1087
+
1088
+ let verts = points.map((_, i) => i);
1089
+ let ccw = signedArea(points) > 0;
1090
+ let triangles = [];
1091
+ let guard = points.length * points.length;
1092
+
1093
+ while (verts.length > 3 && guard-- > 0) {
1094
+ let clipped = false;
1095
+
1096
+ for (let vi = 0; vi < verts.length; vi++) {
1097
+ let i0 = verts[(vi + verts.length - 1) % verts.length];
1098
+ let i1 = verts[vi];
1099
+ let i2 = verts[(vi + 1) % verts.length];
1100
+ let a = points[i0];
1101
+ let b = points[i1];
1102
+ let c = points[i2];
1103
+ let cross = (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
1104
+
1105
+ if (Math.abs(cross) <= EPS) {
1106
+ verts.splice(vi, 1);
1107
+ clipped = true;
1108
+ break;
1109
+ }
1110
+
1111
+ if (ccw ? cross < -EPS : cross > EPS)
1112
+ continue;
1113
+
1114
+ let hasInside = false;
1115
+
1116
+ for (let j = 0; j < verts.length; j++) {
1117
+ let idx = verts[j];
1118
+ if (idx == i0 || idx == i1 || idx == i2)
1119
+ continue;
1120
+
1121
+ if (pointInTri(points[idx], a, b, c)) {
1122
+ hasInside = true;
1123
+ break;
1124
+ }
1125
+ }
1126
+
1127
+ if (!hasInside) {
1128
+ triangles.push([a, b, c]);
1129
+ verts.splice(vi, 1);
1130
+ clipped = true;
1131
+ break;
1132
+ }
1133
+ }
1134
+
1135
+ if (!clipped) {
1136
+ if (triangulationMatchesPolygon(points, triangles))
1137
+ return triangles;
1138
+
1139
+ if (monotone != null && triangulationMatchesPolygon(points, monotone))
1140
+ return monotone;
1141
+
1142
+ return fanTriangulate(points);
1143
+ }
1144
+ }
1145
+
1146
+ if (verts.length == 3)
1147
+ triangles.push([points[verts[0]], points[verts[1]], points[verts[2]]]);
1148
+
1149
+ if (triangulationMatchesPolygon(points, triangles))
1150
+ return triangles;
1151
+
1152
+ if (monotone != null && triangulationMatchesPolygon(points, monotone))
1153
+ return monotone;
1154
+
1155
+ return fanTriangulate(points);
1156
+ }
1157
+
1158
+ function fanTriangulate(points) {
1159
+ let out = [];
1160
+ let poly = cleanPolygon(points);
1161
+
1162
+ for (let i = 1; i < poly.length - 1; i++)
1163
+ out.push([poly[0], poly[i], poly[i + 1]]);
1164
+
1165
+ return out;
1166
+ }
1167
+
1168
+ function addDisc(out, cx, cy, radius, color, steps = 18) {
1169
+ for (let i = 0; i < steps; i++) {
1170
+ let a0 = Math.PI * 2 * i / steps;
1171
+ let a1 = Math.PI * 2 * (i + 1) / steps;
1172
+ pushPaintedTriangleCoords(
1173
+ out,
1174
+ cx, cy,
1175
+ cx + Math.cos(a0) * radius, cy + Math.sin(a0) * radius,
1176
+ cx + Math.cos(a1) * radius, cy + Math.sin(a1) * radius,
1177
+ color,
1178
+ );
1179
+ }
1180
+ }
1181
+
1182
+ function addQuad(out, a, b, c, d, color) {
1183
+ pushPaintedTriangle(out, a, b, c, color);
1184
+ pushPaintedTriangle(out, c, b, d, color);
1185
+ }
1186
+
1187
+ function addLineSegment(out, ax, ay, bx, by, width, color) {
1188
+ let dx = bx - ax;
1189
+ let dy = by - ay;
1190
+ let len = Math.hypot(dx, dy);
1191
+
1192
+ if (len < EPS) {
1193
+ addDisc(out, ax, ay, width / 2, color, 12);
1194
+ return;
1195
+ }
1196
+
1197
+ let nx = -dy / len * width / 2;
1198
+ let ny = dx / len * width / 2;
1199
+
1200
+ pushPaintedTriangleCoords(out, ax + nx, ay + ny, ax - nx, ay - ny, bx + nx, by + ny, color);
1201
+ pushPaintedTriangleCoords(out, bx + nx, by + ny, ax - nx, ay - ny, bx - nx, by - ny, color);
1202
+
1203
+ }
1204
+
1205
+ function addLineCap(out, x, y, dx, dy, width, color, cap, end) {
1206
+ if (cap == 'round')
1207
+ addDisc(out, x, y, width / 2, color, 12);
1208
+ else if (cap == 'square') {
1209
+ let half = width / 2;
1210
+ let nx = -dy * half;
1211
+ let ny = dx * half;
1212
+ let ex = dx * half * (end ? 1 : -1);
1213
+ let ey = dy * half * (end ? 1 : -1);
1214
+ addQuad(out,
1215
+ [x + nx, y + ny],
1216
+ [x - nx, y - ny],
1217
+ [x + nx + ex, y + ny + ey],
1218
+ [x - nx + ex, y - ny + ey],
1219
+ color,
1220
+ );
1221
+ }
1222
+ }
1223
+
1224
+ function addBevelJoin(out, x, y, prev, next, half, side, color) {
1225
+ pushTri(out,
1226
+ [x, y],
1227
+ [x + prev.nx * half * side, y + prev.ny * half * side],
1228
+ [x + next.nx * half * side, y + next.ny * half * side],
1229
+ color,
1230
+ );
1231
+ }
1232
+
1233
+ function intersectLines(p, r, q, s) {
1234
+ let den = r[0] * s[1] - r[1] * s[0];
1235
+
1236
+ if (Math.abs(den) < EPS)
1237
+ return null;
1238
+
1239
+ let qpx = q[0] - p[0];
1240
+ let qpy = q[1] - p[1];
1241
+ let t = (qpx * s[1] - qpy * s[0]) / den;
1242
+ return [p[0] + r[0] * t, p[1] + r[1] * t];
1243
+ }
1244
+
1245
+ function addMiterJoin(out, x, y, prev, next, half, side, color, miterLimit) {
1246
+ let a = [x + prev.nx * half * side, y + prev.ny * half * side];
1247
+ let b = [x + next.nx * half * side, y + next.ny * half * side];
1248
+ let miter = intersectLines(a, [prev.dx, prev.dy], b, [next.dx, next.dy]);
1249
+
1250
+ if (miter == null || Math.hypot(miter[0] - x, miter[1] - y) > miterLimit * half) {
1251
+ addBevelJoin(out, x, y, prev, next, half, side, color);
1252
+ return;
1253
+ }
1254
+
1255
+ pushTri(out, a, miter, b, color);
1256
+ }
1257
+
1258
+ function addLineJoin(out, x, y, prev, next, width, color, join, miterLimit) {
1259
+ let cross = prev.dx * next.dy - prev.dy * next.dx;
1260
+
1261
+ if (Math.abs(cross) < EPS)
1262
+ return;
1263
+
1264
+ if (join == 'round') {
1265
+ addDisc(out, x, y, width / 2, color, 12);
1266
+ return;
1267
+ }
1268
+
1269
+ let side = cross > 0 ? 1 : -1;
1270
+ let half = width / 2;
1271
+
1272
+ if (join == 'miter')
1273
+ addMiterJoin(out, x, y, prev, next, half, side, color, miterLimit);
1274
+ else
1275
+ addBevelJoin(out, x, y, prev, next, half, side, color);
1276
+ }
1277
+
1278
+ function addPolyline(out, points, width, color, cap, join, miterLimit = 10) {
1279
+ let closed = !!points.closed;
1280
+ let segs = [];
1281
+
1282
+ for (let i = 1; i < points.length; i++) {
1283
+ let ax = points[i - 1][0];
1284
+ let ay = points[i - 1][1];
1285
+ let bx = points[i][0];
1286
+ let by = points[i][1];
1287
+ let dx = bx - ax;
1288
+ let dy = by - ay;
1289
+ let len = Math.hypot(dx, dy);
1290
+
1291
+ if (len < EPS)
1292
+ continue;
1293
+
1294
+ dx /= len;
1295
+ dy /= len;
1296
+ let seg = {x0: ax, y0: ay, x1: bx, y1: by, dx, dy, nx: -dy, ny: dx};
1297
+ segs.push(seg);
1298
+ addLineSegment(out, ax, ay, bx, by, width, color);
1299
+ }
1300
+
1301
+ if (segs.length == 0) {
1302
+ if (points.length > 0)
1303
+ addDisc(out, points[0][0], points[0][1], width / 2, color, 12);
1304
+ return;
1305
+ }
1306
+
1307
+ for (let i = 1; i < segs.length; i++)
1308
+ addLineJoin(out, segs[i - 1].x1, segs[i - 1].y1, segs[i - 1], segs[i], width, color, join, miterLimit);
1309
+
1310
+ let first = segs[0];
1311
+ let last = segs[segs.length - 1];
1312
+
1313
+ if (closed)
1314
+ addLineJoin(out, first.x0, first.y0, last, first, width, color, join, miterLimit);
1315
+ else {
1316
+ addLineCap(out, first.x0, first.y0, first.dx, first.dy, width, color, cap, false);
1317
+ addLineCap(out, last.x1, last.y1, last.dx, last.dy, width, color, cap, true);
1318
+ }
1319
+ }
1320
+
1321
+ function dashSegments(points, dash, offset = 0) {
1322
+ if (dash == null || dash.length == 0)
1323
+ return [points];
1324
+
1325
+ let pattern = dash.filter(v => v > 0);
1326
+ if (pattern.length == 0)
1327
+ return [points];
1328
+ if (pattern.length % 2 == 1)
1329
+ pattern = pattern.concat(pattern);
1330
+
1331
+ let out = [];
1332
+ let current = [];
1333
+ let total = pattern.reduce((a, b) => a + b, 0);
1334
+ offset = total > 0 ? ((offset % total) + total) % total : 0;
1335
+ let dashIdx = 0;
1336
+ let remain = pattern[0];
1337
+ let draw = true;
1338
+
1339
+ while (offset > EPS && total > 0) {
1340
+ let step = Math.min(remain, offset);
1341
+ offset -= step;
1342
+ remain -= step;
1343
+
1344
+ if (remain <= EPS) {
1345
+ dashIdx = (dashIdx + 1) % pattern.length;
1346
+ remain = pattern[dashIdx];
1347
+ draw = !draw;
1348
+ }
1349
+ }
1350
+
1351
+ for (let i = 1; i < points.length; i++) {
1352
+ let ax = points[i - 1][0];
1353
+ let ay = points[i - 1][1];
1354
+ let bx = points[i][0];
1355
+ let by = points[i][1];
1356
+ let dx = bx - ax;
1357
+ let dy = by - ay;
1358
+ let segLen = Math.hypot(dx, dy);
1359
+ let used = 0;
1360
+
1361
+ while (segLen - used > EPS) {
1362
+ let step = Math.min(remain, segLen - used);
1363
+ let t0 = used / segLen;
1364
+ let t1 = (used + step) / segLen;
1365
+ let p0 = [ax + dx * t0, ay + dy * t0];
1366
+ let p1 = [ax + dx * t1, ay + dy * t1];
1367
+
1368
+ if (draw) {
1369
+ if (current.length == 0)
1370
+ current.push(p0);
1371
+ current.push(p1);
1372
+ }
1373
+
1374
+ used += step;
1375
+ remain -= step;
1376
+
1377
+ if (remain <= EPS) {
1378
+ if (draw && current.length > 1) {
1379
+ out.push(current);
1380
+ current = [];
1381
+ }
1382
+
1383
+ dashIdx = (dashIdx + 1) % pattern.length;
1384
+ remain = pattern[dashIdx];
1385
+ draw = !draw;
1386
+ }
1387
+ }
1388
+ }
1389
+
1390
+ if (current.length > 1)
1391
+ out.push(current);
1392
+
1393
+ return out;
1394
+ }
1395
+
1396
+ function extractFontPx(font) {
1397
+ let m = /([0-9.]+)px/.exec(font || '');
1398
+ return m == null ? 12 : Number.parseFloat(m[1]);
1399
+ }
1400
+
1401
+ function cssFont(font, ratio) {
1402
+ return (font || '12px sans-serif').replace(/([0-9.]+)px/, (_, n) => (Number.parseFloat(n) / ratio) + 'px');
1403
+ }
1404
+
1405
+ function cssColor(color, x = 0, y = 0, alpha = 1) {
1406
+ if (color == null)
1407
+ return 'transparent';
1408
+
1409
+ let c = null;
1410
+ if (Array.isArray(color))
1411
+ c = applyAlpha(color, alpha);
1412
+ else if (typeof color == 'object' && color.colorAt)
1413
+ c = color.colorAt(x, y, alpha);
1414
+
1415
+ if (c != null) {
1416
+ let a = c[3] <= 0 ? 0 : c[3];
1417
+ let r = a > 0 ? Math.round(clamp01(c[0] / a) * 255) : 0;
1418
+ let g = a > 0 ? Math.round(clamp01(c[1] / a) * 255) : 0;
1419
+ let b = a > 0 ? Math.round(clamp01(c[2] / a) * 255) : 0;
1420
+ return `rgba(${r}, ${g}, ${b}, ${clamp01(a)})`;
1421
+ }
1422
+
1423
+ return color === 'transparent' ? 'transparent' : String(color);
1424
+ }
1425
+
1426
+
1427
+ const COMPOSITE_MODES = [
1428
+ 'source-over',
1429
+ 'copy',
1430
+ 'destination-over',
1431
+ 'source-in',
1432
+ 'source-out',
1433
+ 'source-atop',
1434
+ 'destination-in',
1435
+ 'destination-out',
1436
+ 'destination-atop',
1437
+ 'xor',
1438
+ 'lighter',
1439
+ ];
1440
+
1441
+ function clampCompositeMode(mode) {
1442
+ if (mode == null)
1443
+ return 'source-over';
1444
+
1445
+ mode = String(mode);
1446
+ return COMPOSITE_MODES.includes(mode) ? mode : 'source-over';
1447
+ }
1448
+
1449
+ function blendForComposite(mode, premultiplied = true) {
1450
+ mode = clampCompositeMode(mode);
1451
+
1452
+ if (mode == 'copy')
1453
+ return undefined;
1454
+
1455
+ if (mode == 'destination-over') {
1456
+ return {
1457
+ color: {srcFactor: 'one-minus-dst-alpha', dstFactor: 'one', operation: 'add'},
1458
+ alpha: {srcFactor: 'one-minus-dst-alpha', dstFactor: 'one', operation: 'add'},
1459
+ };
1460
+ }
1461
+
1462
+ if (mode == 'source-in') {
1463
+ return {
1464
+ color: {srcFactor: 'dst-alpha', dstFactor: 'zero', operation: 'add'},
1465
+ alpha: {srcFactor: 'dst-alpha', dstFactor: 'zero', operation: 'add'},
1466
+ };
1467
+ }
1468
+
1469
+ if (mode == 'source-out') {
1470
+ return {
1471
+ color: {srcFactor: 'one-minus-dst-alpha', dstFactor: 'zero', operation: 'add'},
1472
+ alpha: {srcFactor: 'one-minus-dst-alpha', dstFactor: 'zero', operation: 'add'},
1473
+ };
1474
+ }
1475
+
1476
+ if (mode == 'source-atop') {
1477
+ return {
1478
+ color: {srcFactor: 'dst-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add'},
1479
+ alpha: {srcFactor: 'dst-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add'},
1480
+ };
1481
+ }
1482
+
1483
+ if (mode == 'destination-in') {
1484
+ return {
1485
+ color: {srcFactor: 'zero', dstFactor: 'src-alpha', operation: 'add'},
1486
+ alpha: {srcFactor: 'zero', dstFactor: 'src-alpha', operation: 'add'},
1487
+ };
1488
+ }
1489
+
1490
+ if (mode == 'destination-out') {
1491
+ return {
1492
+ color: {srcFactor: 'zero', dstFactor: 'one-minus-src-alpha', operation: 'add'},
1493
+ alpha: {srcFactor: 'zero', dstFactor: 'one-minus-src-alpha', operation: 'add'},
1494
+ };
1495
+ }
1496
+
1497
+ if (mode == 'destination-atop') {
1498
+ return {
1499
+ color: {srcFactor: 'one-minus-dst-alpha', dstFactor: 'src-alpha', operation: 'add'},
1500
+ alpha: {srcFactor: 'one-minus-dst-alpha', dstFactor: 'src-alpha', operation: 'add'},
1501
+ };
1502
+ }
1503
+
1504
+ if (mode == 'xor') {
1505
+ return {
1506
+ color: {srcFactor: 'one-minus-dst-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add'},
1507
+ alpha: {srcFactor: 'one-minus-dst-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add'},
1508
+ };
1509
+ }
1510
+
1511
+ if (mode == 'lighter') {
1512
+ return {
1513
+ color: {srcFactor: 'one', dstFactor: 'one', operation: 'add'},
1514
+ alpha: {srcFactor: 'one', dstFactor: 'one', operation: 'add'},
1515
+ };
1516
+ }
1517
+
1518
+ return premultiplied ? {
1519
+ color: {srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add'},
1520
+ alpha: {srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add'},
1521
+ } : {
1522
+ color: {srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add'},
1523
+ alpha: {srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add'},
1524
+ };
1525
+ }
1526
+
1527
+ function sourceWidth(source) {
1528
+ return source?.naturalWidth || source?.videoWidth || source?.displayWidth || source?.width || 0;
1529
+ }
1530
+
1531
+ function sourceHeight(source) {
1532
+ return source?.naturalHeight || source?.videoHeight || source?.displayHeight || source?.height || 0;
1533
+ }
1534
+
1535
+ function finiteNumber(v) {
1536
+ v = Number(v);
1537
+ return Number.isFinite(v) ? v : null;
1538
+ }
1539
+
1540
+ function finiteTransform(values) {
1541
+ let out = Array.from(values, Number);
1542
+ return out.length >= 6 && out.slice(0, 6).every(Number.isFinite) ? out.slice(0, 6) : null;
1543
+ }
1544
+
1545
+ function matrixFromObject(transform) {
1546
+ if (transform == null)
1547
+ return null;
1548
+ if (Array.isArray(transform))
1549
+ return finiteTransform(transform);
1550
+ if (typeof DOMMatrix != 'undefined' && transform instanceof DOMMatrix)
1551
+ return finiteTransform([transform.a, transform.b, transform.c, transform.d, transform.e, transform.f]);
1552
+ if (typeof transform == 'object')
1553
+ return finiteTransform([transform.a, transform.b, transform.c, transform.d, transform.e, transform.f]);
1554
+ return null;
1555
+ }
1556
+
1557
+ function validEnum(value, allowed, fallback) {
1558
+ value = value == null ? fallback : String(value);
1559
+ return allowed.includes(value) ? value : fallback;
1560
+ }
1561
+
1562
+ function isImageDataLike(source) {
1563
+ return source != null && source.data != null && Number.isFinite(Number(source.width)) && Number.isFinite(Number(source.height));
1564
+ }
1565
+
1566
+ function createImageDataObject(width, height, data = null) {
1567
+ width = Math.max(0, Math.floor(Number(width) || 0));
1568
+ height = Math.max(0, Math.floor(Number(height) || 0));
1569
+ let pixels = data instanceof Uint8ClampedArray ? data : new Uint8ClampedArray(width * height * 4);
1570
+
1571
+ if (typeof ImageData != 'undefined') {
1572
+ try {
1573
+ return new ImageData(pixels, width, height);
1574
+ }
1575
+ catch (err) {
1576
+ return new ImageData(width, height);
1577
+ }
1578
+ }
1579
+
1580
+ return {width, height, data: pixels};
1581
+ }
1582
+
1583
+ let exportToolsPromise = null;
1584
+
1585
+ function loadExportTools() {
1586
+ return exportToolsPromise || (exportToolsPromise = import('./exporters.js'));
1587
+ }
1588
+
1589
+ function alignBytesPerRow(bytes) {
1590
+ return Math.ceil(bytes / 256) * 256;
1591
+ }
1592
+
1593
+ function normalizeReadRect(x, y, w, h, frameWidth, frameHeight) {
1594
+ let vals = [x, y, w, h].map(finiteNumber);
1595
+ if (vals.some(v => v == null))
1596
+ return null;
1597
+
1598
+ [x, y, w, h] = vals;
1599
+ if (w < 0) {
1600
+ x += w;
1601
+ w = -w;
1602
+ }
1603
+ if (h < 0) {
1604
+ y += h;
1605
+ h = -h;
1606
+ }
1607
+
1608
+ let x0 = Math.max(0, Math.min(frameWidth, Math.floor(x)));
1609
+ let y0 = Math.max(0, Math.min(frameHeight, Math.floor(y)));
1610
+ let x1 = Math.max(0, Math.min(frameWidth, Math.ceil(x + w)));
1611
+ let y1 = Math.max(0, Math.min(frameHeight, Math.ceil(y + h)));
1612
+
1613
+ if (x1 <= x0 || y1 <= y0)
1614
+ return null;
1615
+
1616
+ return {x: x0, y: y0, w: x1 - x0, h: y1 - y0};
1617
+ }
1618
+
1619
+ function normalizeDirtyRect(imageData, dirtyX, dirtyY, dirtyWidth, dirtyHeight) {
1620
+ let vals = [dirtyX, dirtyY, dirtyWidth, dirtyHeight].map(finiteNumber);
1621
+ if (vals.some(v => v == null))
1622
+ return null;
1623
+
1624
+ let [x, y, w, h] = vals;
1625
+ let width = Math.max(0, Math.floor(Number(imageData?.width) || 0));
1626
+ let height = Math.max(0, Math.floor(Number(imageData?.height) || 0));
1627
+ if (width == 0 || height == 0)
1628
+ return null;
1629
+
1630
+ if (w < 0) {
1631
+ x += w;
1632
+ w = -w;
1633
+ }
1634
+ if (h < 0) {
1635
+ y += h;
1636
+ h = -h;
1637
+ }
1638
+
1639
+ let x0 = Math.max(0, Math.min(width, Math.floor(x)));
1640
+ let y0 = Math.max(0, Math.min(height, Math.floor(y)));
1641
+ let x1 = Math.max(0, Math.min(width, Math.ceil(x + w)));
1642
+ let y1 = Math.max(0, Math.min(height, Math.ceil(y + h)));
1643
+ if (x1 <= x0 || y1 <= y0)
1644
+ return null;
1645
+
1646
+ return {x: x0, y: y0, w: x1 - x0, h: y1 - y0};
1647
+ }
1648
+
1649
+ function normalizeImageSmoothingQuality(value) {
1650
+ return validEnum(value, ['low', 'medium', 'high'], 'high');
1651
+ }
1652
+
1653
+ function normalizeImageRect(record, sx, sy, sw, sh, dx, dy, dw, dh) {
1654
+ let vals = [sx, sy, sw, sh, dx, dy, dw, dh].map(finiteNumber);
1655
+ if (vals.some(v => v == null))
1656
+ return null;
1657
+
1658
+ [sx, sy, sw, sh, dx, dy, dw, dh] = vals;
1659
+ if (sw == 0 || sh == 0 || dw == 0 || dh == 0)
1660
+ return null;
1661
+
1662
+ if (sw < 0) {
1663
+ sx += sw;
1664
+ sw = -sw;
1665
+ }
1666
+ if (sh < 0) {
1667
+ sy += sh;
1668
+ sh = -sh;
1669
+ }
1670
+ if (dw < 0) {
1671
+ dx += dw;
1672
+ dw = -dw;
1673
+ }
1674
+ if (dh < 0) {
1675
+ dy += dh;
1676
+ dh = -dh;
1677
+ }
1678
+
1679
+ let sx0 = Math.max(0, Math.min(record.width, sx));
1680
+ let sy0 = Math.max(0, Math.min(record.height, sy));
1681
+ let sx1 = Math.max(0, Math.min(record.width, sx + sw));
1682
+ let sy1 = Math.max(0, Math.min(record.height, sy + sh));
1683
+
1684
+ if (sx1 <= sx0 || sy1 <= sy0)
1685
+ return null;
1686
+
1687
+ let tx0 = (sx0 - sx) / sw;
1688
+ let tx1 = (sx1 - sx) / sw;
1689
+ let ty0 = (sy0 - sy) / sh;
1690
+ let ty1 = (sy1 - sy) / sh;
1691
+
1692
+ return {
1693
+ sx: sx0,
1694
+ sy: sy0,
1695
+ sw: sx1 - sx0,
1696
+ sh: sy1 - sy0,
1697
+ dx: dx + tx0 * dw,
1698
+ dy: dy + ty0 * dh,
1699
+ dw: (tx1 - tx0) * dw,
1700
+ dh: (ty1 - ty0) * dh,
1701
+ };
1702
+ }
1703
+
1704
+ function hasActiveShadow(state) {
1705
+ if (state == null)
1706
+ return false;
1707
+
1708
+ let color = parsePlainCssColor(state.shadowColor || 'rgba(0,0,0,0)') || TRANSPARENT;
1709
+ return color[3] > 0 && (Math.abs(state.shadowOffsetX || 0) > EPS || Math.abs(state.shadowOffsetY || 0) > EPS || Math.abs(state.shadowBlur || 0) > EPS);
1710
+ }
1711
+
1712
+ function offsetSolidVertices(vertices, dx, dy, alphaScale = 1) {
1713
+ let out = new Float32Array(vertices.length);
1714
+ for (let i = 0; i < vertices.length; i += FLOATS_PER_VERTEX) {
1715
+ out[i + 0] = vertices[i + 0] + dx;
1716
+ out[i + 1] = vertices[i + 1] + dy;
1717
+ out[i + 2] = vertices[i + 2];
1718
+ out[i + 3] = vertices[i + 3];
1719
+ out[i + 4] = vertices[i + 4];
1720
+ out[i + 5] = vertices[i + 5] * alphaScale;
1721
+ }
1722
+ return out;
1723
+ }
1724
+
1725
+ function invert2x2(a, b, c, d) {
1726
+ let det = a * d - b * c;
1727
+ if (Math.abs(det) < EPS)
1728
+ return null;
1729
+ let inv = 1 / det;
1730
+ return [d * inv, -b * inv, -c * inv, a * inv];
1731
+ }
1732
+
1733
+ function filterStyleValue(filter) {
1734
+ return filter == null || filter === '' ? 'none' : String(filter);
1735
+ }
1736
+
1737
+ function isPatternPaint(paint) {
1738
+ return paint != null && paint._uPlotWebGPUPattern === true;
1739
+ }
1740
+
1741
+ function makePattern(source, repetition = 'repeat') {
1742
+ return {
1743
+ _uPlotWebGPUPattern: true,
1744
+ source,
1745
+ repetition: repetition || 'repeat',
1746
+ transform: identity(),
1747
+ setTransform(transform) {
1748
+ let m = matrixFromObject(transform);
1749
+ this.transform = m == null ? identity() : m;
1750
+ },
1751
+ };
1752
+ }
1753
+
1754
+ function boundsOfRegions(regions) {
1755
+ let minX = Infinity;
1756
+ let minY = Infinity;
1757
+ let maxX = -Infinity;
1758
+ let maxY = -Infinity;
1759
+
1760
+ for (let region of regions || []) {
1761
+ for (let p of region) {
1762
+ minX = Math.min(minX, p[0]);
1763
+ minY = Math.min(minY, p[1]);
1764
+ maxX = Math.max(maxX, p[0]);
1765
+ maxY = Math.max(maxY, p[1]);
1766
+ }
1767
+ }
1768
+
1769
+ return minX == Infinity ? null : {x: minX, y: minY, w: maxX - minX, h: maxY - minY};
1770
+ }
1771
+
1772
+ function makeImageVertex(x, y, u, v, alpha) {
1773
+ return {x, y, u, v, alpha};
1774
+ }
1775
+
1776
+ function pushImageVertex(out, v) {
1777
+ out.push(v.x, v.y, v.u, v.v, v.alpha);
1778
+ }
1779
+
1780
+ function pushImageTri(out, a, b, c) {
1781
+ pushImageVertex(out, a);
1782
+ pushImageVertex(out, b);
1783
+ pushImageVertex(out, c);
1784
+ }
1785
+
1786
+ function addImageQuad(out, a, b, c, d) {
1787
+ pushImageTri(out, a, b, c);
1788
+ pushImageTri(out, c, b, d);
1789
+ }
1790
+
1791
+ function clipImagePolygonAgainstEdge(subject, a, b, ccw) {
1792
+ if (subject.length == 0)
1793
+ return [];
1794
+
1795
+ let out = [];
1796
+ let inside = p => {
1797
+ let cross = (b[0] - a[0]) * (p.y - a[1]) - (b[1] - a[1]) * (p.x - a[0]);
1798
+ return ccw ? cross >= -EPS : cross <= EPS;
1799
+ };
1800
+ let intersect = (p, q) => {
1801
+ let bax = q.x - p.x;
1802
+ let bay = q.y - p.y;
1803
+ let dcx = b[0] - a[0];
1804
+ let dcy = b[1] - a[1];
1805
+ let den = bax * dcy - bay * dcx;
1806
+ let t = 0;
1807
+ if (Math.abs(den) >= EPS) {
1808
+ let acx = a[0] - p.x;
1809
+ let acy = a[1] - p.y;
1810
+ t = (acx * dcy - acy * dcx) / den;
1811
+ }
1812
+ t = clamp01(t);
1813
+ return makeImageVertex(
1814
+ p.x + (q.x - p.x) * t,
1815
+ p.y + (q.y - p.y) * t,
1816
+ p.u + (q.u - p.u) * t,
1817
+ p.v + (q.v - p.v) * t,
1818
+ p.alpha + (q.alpha - p.alpha) * t,
1819
+ );
1820
+ };
1821
+
1822
+ let prev = subject[subject.length - 1];
1823
+ let prevInside = inside(prev);
1824
+
1825
+ for (let curr of subject) {
1826
+ let currInside = inside(curr);
1827
+
1828
+ if (currInside) {
1829
+ if (!prevInside)
1830
+ out.push(intersect(prev, curr));
1831
+ out.push(curr);
1832
+ }
1833
+ else if (prevInside)
1834
+ out.push(intersect(prev, curr));
1835
+
1836
+ prev = curr;
1837
+ prevInside = currInside;
1838
+ }
1839
+
1840
+ return out;
1841
+ }
1842
+
1843
+ function clipImagePolygon(subject, clipper) {
1844
+ if (subject.length < 3 || clipper.length < 3)
1845
+ return [];
1846
+
1847
+ let out = subject;
1848
+ let ccw = polygonArea(clipper) >= 0;
1849
+
1850
+ for (let i = 0; i < clipper.length; i++) {
1851
+ out = clipImagePolygonAgainstEdge(out, clipper[i], clipper[(i + 1) % clipper.length], ccw);
1852
+ if (out.length == 0)
1853
+ break;
1854
+ }
1855
+
1856
+ return out;
1857
+ }
1858
+
1859
+ function clipImageVertices(vertices, regions) {
1860
+ if (regions == null)
1861
+ return vertices;
1862
+
1863
+ if (regions.length == 0)
1864
+ return [];
1865
+
1866
+ let out = [];
1867
+
1868
+ for (let i = 0; i < vertices.length; i += FLOATS_PER_IMAGE_VERTEX * 3) {
1869
+ let tri = [
1870
+ makeImageVertex(vertices[i], vertices[i + 1], vertices[i + 2], vertices[i + 3], vertices[i + 4]),
1871
+ makeImageVertex(vertices[i + FLOATS_PER_IMAGE_VERTEX], vertices[i + FLOATS_PER_IMAGE_VERTEX + 1], vertices[i + FLOATS_PER_IMAGE_VERTEX + 2], vertices[i + FLOATS_PER_IMAGE_VERTEX + 3], vertices[i + FLOATS_PER_IMAGE_VERTEX + 4]),
1872
+ makeImageVertex(vertices[i + FLOATS_PER_IMAGE_VERTEX * 2], vertices[i + FLOATS_PER_IMAGE_VERTEX * 2 + 1], vertices[i + FLOATS_PER_IMAGE_VERTEX * 2 + 2], vertices[i + FLOATS_PER_IMAGE_VERTEX * 2 + 3], vertices[i + FLOATS_PER_IMAGE_VERTEX * 2 + 4]),
1873
+ ];
1874
+
1875
+ for (let region of regions) {
1876
+ let clipped = clipImagePolygon(tri, region);
1877
+ for (let j = 1; j < clipped.length - 1; j++)
1878
+ pushImageTri(out, clipped[0], clipped[j], clipped[j + 1]);
1879
+ }
1880
+ }
1881
+
1882
+ return out;
1883
+ }
1884
+
1885
+ let sharedRuntimePromise = null;
1886
+ let sharedRuntime = null;
1887
+ let sharedRuntimeRefs = 0;
1888
+ let sharedRuntimeEnabled = true;
1889
+
1890
+ async function createWebGPURuntime(owner, epoch) {
1891
+ if (typeof navigator == 'undefined' || navigator.gpu == null)
1892
+ throw new Error('WebGPU is not available in this environment.');
1893
+
1894
+ let adapter = await navigator.gpu.requestAdapter();
1895
+ if (adapter == null)
1896
+ throw new Error('No WebGPU adapter was returned.');
1897
+
1898
+ let adapterInfo = null;
1899
+ try {
1900
+ if (adapter.info)
1901
+ adapterInfo = {vendor: adapter.info.vendor || '', architecture: adapter.info.architecture || '', device: adapter.info.device || '', description: adapter.info.description || ''};
1902
+ else if (typeof adapter.requestAdapterInfo == 'function') {
1903
+ let info = await adapter.requestAdapterInfo();
1904
+ adapterInfo = {vendor: info.vendor || '', architecture: info.architecture || '', device: info.device || '', description: info.description || ''};
1905
+ }
1906
+ }
1907
+ catch (err) {
1908
+ adapterInfo = null;
1909
+ }
1910
+
1911
+ let device = await adapter.requestDevice();
1912
+
1913
+ let format = navigator.gpu.getPreferredCanvasFormat();
1914
+ let module = device.createShaderModule({code: CHART_WGSL});
1915
+ let imageModule = device.createShaderModule({code: IMAGE_WGSL});
1916
+ let bindGroupLayout = device.createBindGroupLayout({
1917
+ entries: [{
1918
+ binding: 0,
1919
+ visibility: GPUShaderStage.VERTEX,
1920
+ buffer: {type: 'uniform'},
1921
+ }],
1922
+ });
1923
+ let imageBindGroupLayout = device.createBindGroupLayout({
1924
+ entries: [
1925
+ {binding: 0, visibility: GPUShaderStage.FRAGMENT, sampler: {type: 'filtering'}},
1926
+ {binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: {sampleType: 'float'}},
1927
+ ],
1928
+ });
1929
+ let pipelineLayout = device.createPipelineLayout({bindGroupLayouts: [bindGroupLayout]});
1930
+ let imagePipelineLayout = device.createPipelineLayout({bindGroupLayouts: [bindGroupLayout, imageBindGroupLayout]});
1931
+ let runtime = {
1932
+ adapter,
1933
+ adapterInfo,
1934
+ device,
1935
+ format,
1936
+ module,
1937
+ imageModule,
1938
+ bindGroupLayout,
1939
+ imageBindGroupLayout,
1940
+ pipelineLayout,
1941
+ imagePipelineLayout,
1942
+ pipelines: {},
1943
+ imagePipelines: {},
1944
+ samplers: new Map(),
1945
+ lost: false,
1946
+ };
1947
+
1948
+ await Promise.all(COMPOSITE_MODES.map(async mode => {
1949
+ if (device.createRenderPipelineAsync) {
1950
+ runtime.pipelines[mode] = await createRuntimePipelineAsync(runtime, mode);
1951
+ runtime.imagePipelines[mode] = await createRuntimeImagePipelineAsync(runtime, mode);
1952
+ }
1953
+ else {
1954
+ runtime.pipelines[mode] = createRuntimePipeline(runtime, mode);
1955
+ runtime.imagePipelines[mode] = createRuntimeImagePipeline(runtime, mode);
1956
+ }
1957
+ }));
1958
+
1959
+ runtime.sampler = getRuntimeSampler(runtime, true, 'high');
1960
+ runtime.clearPipeline = runtime.pipelines.copy;
1961
+
1962
+ device.lost?.then(info => {
1963
+ runtime.lost = true;
1964
+ if (sharedRuntime === runtime) {
1965
+ sharedRuntime = null;
1966
+ sharedRuntimePromise = null;
1967
+ }
1968
+ console.error('WebGPU device lost:', info?.message || info);
1969
+ });
1970
+
1971
+ return runtime;
1972
+ }
1973
+
1974
+ function createRuntimePipeline(runtime, composite = 'source-over') {
1975
+ return runtime.device.createRenderPipeline({
1976
+ layout: runtime.pipelineLayout,
1977
+ vertex: {
1978
+ module: runtime.module,
1979
+ entryPoint: 'vsMain',
1980
+ buffers: [{
1981
+ arrayStride: FLOATS_PER_VERTEX * 4,
1982
+ attributes: [
1983
+ {shaderLocation: 0, offset: 0, format: 'float32x2'},
1984
+ {shaderLocation: 1, offset: 8, format: 'float32x4'},
1985
+ ],
1986
+ }],
1987
+ },
1988
+ fragment: {
1989
+ module: runtime.module,
1990
+ entryPoint: 'fsMain',
1991
+ targets: [{
1992
+ format: runtime.format,
1993
+ blend: blendForComposite(composite),
1994
+ }],
1995
+ },
1996
+ primitive: {topology: 'triangle-list'},
1997
+ });
1998
+ }
1999
+
2000
+
2001
+ function createRuntimePipelineAsync(runtime, composite = 'source-over') {
2002
+ return runtime.device.createRenderPipelineAsync({
2003
+ layout: runtime.pipelineLayout,
2004
+ vertex: {
2005
+ module: runtime.module,
2006
+ entryPoint: 'vsMain',
2007
+ buffers: [{
2008
+ arrayStride: FLOATS_PER_VERTEX * 4,
2009
+ attributes: [
2010
+ {shaderLocation: 0, offset: 0, format: 'float32x2'},
2011
+ {shaderLocation: 1, offset: 8, format: 'float32x4'},
2012
+ ],
2013
+ }],
2014
+ },
2015
+ fragment: {
2016
+ module: runtime.module,
2017
+ entryPoint: 'fsMain',
2018
+ targets: [{
2019
+ format: runtime.format,
2020
+ blend: blendForComposite(composite),
2021
+ }],
2022
+ },
2023
+ primitive: {topology: 'triangle-list'},
2024
+ });
2025
+ }
2026
+
2027
+ function createRuntimeImagePipeline(runtime, composite = 'source-over') {
2028
+ return runtime.device.createRenderPipeline({
2029
+ layout: runtime.imagePipelineLayout,
2030
+ vertex: {
2031
+ module: runtime.imageModule,
2032
+ entryPoint: 'vsImage',
2033
+ buffers: [{
2034
+ arrayStride: FLOATS_PER_IMAGE_VERTEX * 4,
2035
+ attributes: [
2036
+ {shaderLocation: 0, offset: 0, format: 'float32x2'},
2037
+ {shaderLocation: 1, offset: 8, format: 'float32x2'},
2038
+ {shaderLocation: 2, offset: 16, format: 'float32'},
2039
+ ],
2040
+ }],
2041
+ },
2042
+ fragment: {
2043
+ module: runtime.imageModule,
2044
+ entryPoint: 'fsImage',
2045
+ targets: [{
2046
+ format: runtime.format,
2047
+ blend: blendForComposite(composite),
2048
+ }],
2049
+ },
2050
+ primitive: {topology: 'triangle-list'},
2051
+ });
2052
+ }
2053
+
2054
+
2055
+ function createRuntimeImagePipelineAsync(runtime, composite = 'source-over') {
2056
+ return runtime.device.createRenderPipelineAsync({
2057
+ layout: runtime.imagePipelineLayout,
2058
+ vertex: {
2059
+ module: runtime.imageModule,
2060
+ entryPoint: 'vsImage',
2061
+ buffers: [{
2062
+ arrayStride: FLOATS_PER_IMAGE_VERTEX * 4,
2063
+ attributes: [
2064
+ {shaderLocation: 0, offset: 0, format: 'float32x2'},
2065
+ {shaderLocation: 1, offset: 8, format: 'float32x2'},
2066
+ {shaderLocation: 2, offset: 16, format: 'float32'},
2067
+ ],
2068
+ }],
2069
+ },
2070
+ fragment: {
2071
+ module: runtime.imageModule,
2072
+ entryPoint: 'fsImage',
2073
+ targets: [{
2074
+ format: runtime.format,
2075
+ blend: blendForComposite(composite),
2076
+ }],
2077
+ },
2078
+ primitive: {topology: 'triangle-list'},
2079
+ });
2080
+ }
2081
+
2082
+ function getRuntimeSampler(runtime, smoothingEnabled = true, quality = 'high') {
2083
+ let filter = smoothingEnabled ? 'linear' : 'nearest';
2084
+ let key = filter + ':' + quality;
2085
+ let sampler = runtime.samplers.get(key);
2086
+ if (sampler == null) {
2087
+ sampler = runtime.device.createSampler({magFilter: filter, minFilter: filter});
2088
+ runtime.samplers.set(key, sampler);
2089
+ }
2090
+ return sampler;
2091
+ }
2092
+
2093
+ async function getSharedRuntime(owner, epoch) {
2094
+ if (!sharedRuntimeEnabled)
2095
+ return createWebGPURuntime(owner, epoch);
2096
+
2097
+ if (sharedRuntime != null && !sharedRuntime.lost)
2098
+ return sharedRuntime;
2099
+
2100
+ if (sharedRuntimePromise == null) {
2101
+ sharedRuntimePromise = createWebGPURuntime(owner, epoch).then(runtime => {
2102
+ if (runtime != null)
2103
+ sharedRuntime = runtime;
2104
+ return runtime;
2105
+ }).catch(err => {
2106
+ sharedRuntimePromise = null;
2107
+ throw err;
2108
+ });
2109
+ }
2110
+
2111
+ return sharedRuntimePromise;
2112
+ }
2113
+
2114
+
2115
+ function createWarmupCanvas(width, height) {
2116
+ if (typeof OffscreenCanvas != 'undefined')
2117
+ return new OffscreenCanvas(width, height);
2118
+ if (typeof document != 'undefined') {
2119
+ let canvas = document.createElement('canvas');
2120
+ canvas.width = width;
2121
+ canvas.height = height;
2122
+ canvas.style.position = 'fixed';
2123
+ canvas.style.left = '-10000px';
2124
+ canvas.style.top = '-10000px';
2125
+ canvas.style.width = width + 'px';
2126
+ canvas.style.height = height + 'px';
2127
+ document.body?.appendChild?.(canvas);
2128
+ return canvas;
2129
+ }
2130
+ return null;
2131
+ }
2132
+
2133
+ async function prewarmWebGPURuntime(options = null) {
2134
+ options = options || {};
2135
+ let startedAt = perfNow();
2136
+ let previousSharedRuntimeEnabled = sharedRuntimeEnabled;
2137
+ let shouldUseSharedRuntime = options.sharedRuntime !== false;
2138
+ if (shouldUseSharedRuntime)
2139
+ sharedRuntimeEnabled = true;
2140
+
2141
+ let runtime;
2142
+ let canvas;
2143
+ let context;
2144
+ let uniformBuffer;
2145
+ let solidBuffer;
2146
+ let imageBuffer;
2147
+ let texture;
2148
+ let imageBindGroup;
2149
+ let submitted = false;
2150
+
2151
+ try {
2152
+ runtime = await getSharedRuntime(null, 0);
2153
+ if (runtime == null || runtime.device == null)
2154
+ throw new Error('WebGPU runtime was not created.');
2155
+
2156
+ let width = Math.max(1, Math.floor(Number(options.width) || 16));
2157
+ let height = Math.max(1, Math.floor(Number(options.height) || 16));
2158
+ canvas = options.canvas || createWarmupCanvas(width, height);
2159
+ if (canvas == null || typeof canvas.getContext != 'function')
2160
+ throw new Error('No canvas is available for WebGPU warmup.');
2161
+
2162
+ canvas.width = width;
2163
+ canvas.height = height;
2164
+ context = canvas.getContext('webgpu');
2165
+ if (context == null)
2166
+ throw new Error('Could not create a WebGPU warmup canvas context.');
2167
+
2168
+ context.configure({
2169
+ device: runtime.device,
2170
+ format: runtime.format,
2171
+ alphaMode: 'premultiplied',
2172
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
2173
+ });
2174
+
2175
+ uniformBuffer = runtime.device.createBuffer({
2176
+ size: 16,
2177
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
2178
+ });
2179
+ runtime.device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([width, height, 0, 0]));
2180
+ let uniformBindGroup = runtime.device.createBindGroup({
2181
+ layout: runtime.bindGroupLayout,
2182
+ entries: [{binding: 0, resource: {buffer: uniformBuffer}}],
2183
+ });
2184
+
2185
+ let solidVertices = new Float32Array([
2186
+ 0, 0, 1, 0, 0, 1,
2187
+ width, 0, 0, 1, 0, 1,
2188
+ 0, height, 0, 0, 1, 1,
2189
+ ]);
2190
+ solidBuffer = runtime.device.createBuffer({
2191
+ size: solidVertices.byteLength,
2192
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
2193
+ });
2194
+ runtime.device.queue.writeBuffer(solidBuffer, 0, solidVertices);
2195
+
2196
+ let imageVertices = new Float32Array([
2197
+ 0, 0, 0, 0, 1,
2198
+ width, 0, 1, 0, 1,
2199
+ width, height, 1, 1, 1,
2200
+ 0, 0, 0, 0, 1,
2201
+ width, height, 1, 1, 1,
2202
+ 0, height, 0, 1, 1,
2203
+ ]);
2204
+ imageBuffer = runtime.device.createBuffer({
2205
+ size: imageVertices.byteLength,
2206
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
2207
+ });
2208
+ runtime.device.queue.writeBuffer(imageBuffer, 0, imageVertices);
2209
+
2210
+ texture = runtime.device.createTexture({
2211
+ size: [1, 1, 1],
2212
+ format: 'rgba8unorm',
2213
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
2214
+ });
2215
+ runtime.device.queue.writeTexture(
2216
+ {texture},
2217
+ new Uint8Array(256).fill(255),
2218
+ {bytesPerRow: 256, rowsPerImage: 1},
2219
+ [1, 1, 1]
2220
+ );
2221
+ imageBindGroup = runtime.device.createBindGroup({
2222
+ layout: runtime.imageBindGroupLayout,
2223
+ entries: [
2224
+ {binding: 0, resource: getRuntimeSampler(runtime, true, 'high')},
2225
+ {binding: 1, resource: texture.createView()},
2226
+ ],
2227
+ });
2228
+
2229
+ let encoder = runtime.device.createCommandEncoder();
2230
+ let pass = encoder.beginRenderPass({
2231
+ colorAttachments: [{
2232
+ view: context.getCurrentTexture().createView(),
2233
+ clearValue: {r: 0, g: 0, b: 0, a: 0},
2234
+ loadOp: 'clear',
2235
+ storeOp: 'store',
2236
+ }],
2237
+ });
2238
+ pass.setBindGroup(0, uniformBindGroup);
2239
+ pass.setVertexBuffer(0, solidBuffer);
2240
+ for (let mode of COMPOSITE_MODES) {
2241
+ let pipeline = runtime.pipelines[mode];
2242
+ if (pipeline) {
2243
+ pass.setPipeline(pipeline);
2244
+ pass.draw(3, 1, 0, 0);
2245
+ }
2246
+ }
2247
+ pass.setBindGroup(1, imageBindGroup);
2248
+ pass.setVertexBuffer(0, imageBuffer);
2249
+ for (let mode of COMPOSITE_MODES) {
2250
+ let pipeline = runtime.imagePipelines[mode];
2251
+ if (pipeline) {
2252
+ pass.setPipeline(pipeline);
2253
+ pass.draw(6, 1, 0, 0);
2254
+ }
2255
+ }
2256
+ pass.end();
2257
+ runtime.device.queue.submit([encoder.finish()]);
2258
+ submitted = true;
2259
+ await runtime.device.queue.onSubmittedWorkDone?.();
2260
+
2261
+ return {
2262
+ ok: true,
2263
+ ms: perfNow() - startedAt,
2264
+ submitted,
2265
+ sharedRuntime: shouldUseSharedRuntime,
2266
+ runtime: WebGPURenderer.getSharedRuntimeStats(),
2267
+ };
2268
+ }
2269
+ catch (err) {
2270
+ return {
2271
+ ok: false,
2272
+ ms: perfNow() - startedAt,
2273
+ submitted,
2274
+ sharedRuntime: shouldUseSharedRuntime,
2275
+ error: err?.message || String(err),
2276
+ runtime: WebGPURenderer.getSharedRuntimeStats(),
2277
+ };
2278
+ }
2279
+ finally {
2280
+ try { texture?.destroy?.(); }
2281
+ catch (err) {}
2282
+ try { solidBuffer?.destroy?.(); }
2283
+ catch (err) {}
2284
+ try { imageBuffer?.destroy?.(); }
2285
+ catch (err) {}
2286
+ try { uniformBuffer?.destroy?.(); }
2287
+ catch (err) {}
2288
+ if (!options.canvas && canvas?.parentNode)
2289
+ canvas.parentNode.removeChild(canvas);
2290
+ if (!shouldUseSharedRuntime)
2291
+ sharedRuntimeEnabled = previousSharedRuntimeEnabled;
2292
+ }
2293
+ }
2294
+
2295
+ function measureTextWithDom(font, text, state = null) {
2296
+ if (typeof document == 'undefined')
2297
+ return null;
2298
+
2299
+ let probe = measureTextWithDom._probe;
2300
+
2301
+ if (probe == null) {
2302
+ probe = document.createElement('span');
2303
+ probe.style.position = 'absolute';
2304
+ probe.style.left = '-99999px';
2305
+ probe.style.top = '-99999px';
2306
+ probe.style.visibility = 'hidden';
2307
+ probe.style.whiteSpace = 'pre';
2308
+ document.documentElement.appendChild(probe);
2309
+ measureTextWithDom._probe = probe;
2310
+ }
2311
+
2312
+ probe.style.font = font || '12px sans-serif';
2313
+ if (state != null) {
2314
+ probe.style.fontKerning = state.fontKerning || 'auto';
2315
+ probe.style.fontStretch = state.fontStretch || 'normal';
2316
+ probe.style.fontVariantCaps = state.fontVariantCaps || 'normal';
2317
+ probe.style.letterSpacing = state.letterSpacing || '0px';
2318
+ probe.style.textRendering = state.textRendering || 'auto';
2319
+ probe.style.wordSpacing = state.wordSpacing || '0px';
2320
+ }
2321
+ probe.textContent = text == null ? '' : String(text);
2322
+ let rect = probe.getBoundingClientRect();
2323
+ return rect.width;
2324
+ }
2325
+
2326
+ export class WebGPURenderer {
2327
+ constructor(canvas, options = null) {
2328
+ this.canvas = canvas;
2329
+ this.options = options || {};
2330
+ this.memory = normalizeMemoryOptions(this.options);
2331
+ this.context = null;
2332
+ this.adapter = null;
2333
+ this.device = null;
2334
+ this.runtime = null;
2335
+ this.ownsRuntime = false;
2336
+ this.sharedRuntimeRefHeld = false;
2337
+ this.format = null;
2338
+ this.pipeline = null;
2339
+ this.pipelines = null;
2340
+ this.imagePipelines = null;
2341
+ this.clearPipeline = null;
2342
+ this.bindGroupLayout = null;
2343
+ this.pipelineLayout = null;
2344
+ this.uniformBuffer = null;
2345
+ this.bindGroup = null;
2346
+ this.ready = false;
2347
+ this.failed = false;
2348
+ this.disposed = false;
2349
+ this.initEpoch = 0;
2350
+ this.contextConfigured = false;
2351
+ this.pendingPresent = false;
2352
+ this.frameCommandsReleased = false;
2353
+ this.memoryTrimToken = 0;
2354
+ this.commands = [];
2355
+ this.path = new GPUPath();
2356
+ this.stack = [];
2357
+ this.textLayer = null;
2358
+ this.colorCache = new Map();
2359
+ this.textMeasureCache = new Map();
2360
+ this.vertexBuffer = null;
2361
+ this.vertexBufferSize = 0;
2362
+ this.imageVertexBuffer = null;
2363
+ this.imageVertexBufferSize = 0;
2364
+ this.solidUploadArray = null;
2365
+ this.imageUploadArray = null;
2366
+ this.uniformUploadArray = null;
2367
+ this.lastFrameStats = {
2368
+ presents: 0,
2369
+ commands: 0,
2370
+ solidCommands: 0,
2371
+ imageCommands: 0,
2372
+ solidVertices: 0,
2373
+ imageVertices: 0,
2374
+ solidBytes: 0,
2375
+ imageBytes: 0,
2376
+ uploadBytes: 0,
2377
+ writeCalls: 0,
2378
+ solidUploadCapacityBytes: 0,
2379
+ imageUploadCapacityBytes: 0,
2380
+ prepareMs: 0,
2381
+ encodeMs: 0,
2382
+ submitMs: 0,
2383
+ cpuFrameMs: 0,
2384
+ frameWidth: 0,
2385
+ frameHeight: 0,
2386
+ pixelRatio: 1,
2387
+ textNodes: 0,
2388
+ retainedCPUBytes: 0,
2389
+ retainedGPUBytes: 0,
2390
+ textureBytes: 0,
2391
+ commandsReleased: false,
2392
+ };
2393
+ this.textureCache = typeof WeakMap != 'undefined' ? new WeakMap() : null;
2394
+ this.textureRecords = new Set();
2395
+ this.textureBytes = 0;
2396
+ this.textureSerial = 0;
2397
+ this.sampler = null;
2398
+ this.imageBindGroupLayout = null;
2399
+ this.frameWidth = 1;
2400
+ this.frameHeight = 1;
2401
+ this.pixelRatio = 1;
2402
+ this.state = {
2403
+ strokeStyle: '#000',
2404
+ fillStyle: '#000',
2405
+ lineWidth: 1,
2406
+ lineJoin: 'round',
2407
+ lineCap: 'butt',
2408
+ lineDash: [],
2409
+ font: '12px sans-serif',
2410
+ textAlign: 'start',
2411
+ textBaseline: 'alphabetic',
2412
+ globalAlpha: 1,
2413
+ transform: identity(),
2414
+ clipRegions: null,
2415
+ lineDashOffset: 0,
2416
+ miterLimit: 10,
2417
+ globalCompositeOperation: 'source-over',
2418
+ shadowColor: 'rgba(0,0,0,0)',
2419
+ shadowBlur: 0,
2420
+ shadowOffsetX: 0,
2421
+ shadowOffsetY: 0,
2422
+ direction: 'inherit',
2423
+ filter: 'none',
2424
+ fontKerning: 'auto',
2425
+ fontStretch: 'normal',
2426
+ fontVariantCaps: 'normal',
2427
+ letterSpacing: '0px',
2428
+ textRendering: 'auto',
2429
+ wordSpacing: '0px',
2430
+ imageSmoothingEnabled: true,
2431
+ imageSmoothingQuality: 'high',
2432
+ };
2433
+
2434
+ this.initPromise = this.init();
2435
+ }
2436
+
2437
+ static setSharedRuntimeEnabled(enabled) {
2438
+ sharedRuntimeEnabled = enabled !== false;
2439
+ }
2440
+
2441
+ static async prewarm(options = null) {
2442
+ return prewarmWebGPURuntime(options);
2443
+ }
2444
+
2445
+ static getSharedRuntimeStats() {
2446
+ return {
2447
+ enabled: sharedRuntimeEnabled,
2448
+ active: sharedRuntime != null && !sharedRuntime.lost,
2449
+ refs: sharedRuntimeRefs,
2450
+ format: sharedRuntime?.format || null,
2451
+ adapterInfo: sharedRuntime?.adapterInfo || null,
2452
+ compositePipelines: sharedRuntime?.pipelines ? Object.keys(sharedRuntime.pipelines).length : 0,
2453
+ imagePipelines: sharedRuntime?.imagePipelines ? Object.keys(sharedRuntime.imagePipelines).length : 0,
2454
+ };
2455
+ }
2456
+
2457
+ getLastFrameStats() {
2458
+ return {...this.lastFrameStats};
2459
+ }
2460
+
2461
+ getDevice() {
2462
+ return this.device;
2463
+ }
2464
+
2465
+ getRuntime() {
2466
+ return this.runtime;
2467
+ }
2468
+
2469
+ getCanvasContext() {
2470
+ return this.context;
2471
+ }
2472
+
2473
+ async init() {
2474
+ let epoch = ++this.initEpoch;
2475
+
2476
+ if (this.disposed)
2477
+ return;
2478
+
2479
+ try {
2480
+ this.failed = false;
2481
+ let runtime = await getSharedRuntime(this, epoch);
2482
+ if (runtime == null || this.disposed || epoch != this.initEpoch)
2483
+ return;
2484
+
2485
+ this.runtime = runtime;
2486
+ this.ownsRuntime = !sharedRuntimeEnabled;
2487
+ if (sharedRuntimeEnabled && !this.sharedRuntimeRefHeld) {
2488
+ sharedRuntimeRefs++;
2489
+ this.sharedRuntimeRefHeld = true;
2490
+ }
2491
+
2492
+ this.adapter = runtime.adapter;
2493
+ this.device = runtime.device;
2494
+ this.context = this.canvas.getContext('webgpu');
2495
+ this.format = runtime.format;
2496
+ this.bindGroupLayout = runtime.bindGroupLayout;
2497
+ this.imageBindGroupLayout = runtime.imageBindGroupLayout;
2498
+ this.pipelineLayout = runtime.pipelineLayout;
2499
+ this.pipelines = runtime.pipelines;
2500
+ this.imagePipelines = runtime.imagePipelines;
2501
+ this.pipeline = runtime.pipelines['source-over'];
2502
+ this.clearPipeline = runtime.clearPipeline;
2503
+ this.sampler = getRuntimeSampler(runtime, this.state.imageSmoothingEnabled, this.state.imageSmoothingQuality);
2504
+ this.configure(true);
2505
+
2506
+ this.uniformBuffer = this.device.createBuffer({
2507
+ size: 16,
2508
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
2509
+ });
2510
+ this.bindGroup = this.device.createBindGroup({
2511
+ layout: this.bindGroupLayout,
2512
+ entries: [{binding: 0, resource: {buffer: this.uniformBuffer}}],
2513
+ });
2514
+
2515
+ if (this.disposed || epoch != this.initEpoch) {
2516
+ this.releaseGPUResources();
2517
+ return;
2518
+ }
2519
+
2520
+ this.ready = true;
2521
+ if (this.pendingPresent)
2522
+ this.present();
2523
+ }
2524
+ catch (err) {
2525
+ if (this.disposed || epoch != this.initEpoch)
2526
+ return;
2527
+ this.failed = true;
2528
+ console.error(err);
2529
+ }
2530
+ }
2531
+
2532
+
2533
+ createPipeline(module, composite = 'source-over') {
2534
+ return this.device.createRenderPipeline({
2535
+ layout: this.pipelineLayout,
2536
+ vertex: {
2537
+ module,
2538
+ entryPoint: 'vsMain',
2539
+ buffers: [{
2540
+ arrayStride: FLOATS_PER_VERTEX * 4,
2541
+ attributes: [
2542
+ {shaderLocation: 0, offset: 0, format: 'float32x2'},
2543
+ {shaderLocation: 1, offset: 8, format: 'float32x4'},
2544
+ ],
2545
+ }],
2546
+ },
2547
+ fragment: {
2548
+ module,
2549
+ entryPoint: 'fsMain',
2550
+ targets: [{
2551
+ format: this.format,
2552
+ blend: blendForComposite(composite),
2553
+ }],
2554
+ },
2555
+ primitive: {topology: 'triangle-list'},
2556
+ });
2557
+ }
2558
+
2559
+ createImagePipeline(module, layout, composite = 'source-over') {
2560
+ return this.device.createRenderPipeline({
2561
+ layout,
2562
+ vertex: {
2563
+ module,
2564
+ entryPoint: 'vsImage',
2565
+ buffers: [{
2566
+ arrayStride: FLOATS_PER_IMAGE_VERTEX * 4,
2567
+ attributes: [
2568
+ {shaderLocation: 0, offset: 0, format: 'float32x2'},
2569
+ {shaderLocation: 1, offset: 8, format: 'float32x2'},
2570
+ {shaderLocation: 2, offset: 16, format: 'float32'},
2571
+ ],
2572
+ }],
2573
+ },
2574
+ fragment: {
2575
+ module,
2576
+ entryPoint: 'fsImage',
2577
+ targets: [{
2578
+ format: this.format,
2579
+ blend: blendForComposite(composite),
2580
+ }],
2581
+ },
2582
+ primitive: {topology: 'triangle-list'},
2583
+ });
2584
+ }
2585
+
2586
+ configure(force = false) {
2587
+ if (this.context == null || this.device == null || this.format == null)
2588
+ return;
2589
+
2590
+ if (force || !this.contextConfigured) {
2591
+ this.context.configure({
2592
+ device: this.device,
2593
+ format: this.format,
2594
+ alphaMode: 'premultiplied',
2595
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
2596
+ });
2597
+ this.contextConfigured = true;
2598
+ }
2599
+ }
2600
+
2601
+ mount(wrap, canvas) {
2602
+ if (typeof document == 'undefined' || this.textLayer != null)
2603
+ return;
2604
+
2605
+ let layer = document.createElement('div');
2606
+ layer.className = 'u-webgpu-text';
2607
+ layer.style.position = 'absolute';
2608
+ layer.style.inset = '0';
2609
+ layer.style.pointerEvents = 'none';
2610
+ layer.style.overflow = 'hidden';
2611
+ layer.style.zIndex = '1';
2612
+ this.textLayer = layer;
2613
+
2614
+ if (canvas.nextSibling)
2615
+ wrap.insertBefore(layer, canvas.nextSibling);
2616
+ else
2617
+ wrap.appendChild(layer);
2618
+ }
2619
+
2620
+ syncFrameMetrics() {
2621
+ let width = Math.max(1, this.canvas.width || 1);
2622
+ let height = Math.max(1, this.canvas.height || 1);
2623
+ let cssW = this.canvas.clientWidth || width;
2624
+ let ratio = width / cssW || 1;
2625
+ let resized = width != this.frameWidth || height != this.frameHeight;
2626
+ let ratioChanged = Math.abs(ratio - this.pixelRatio) > 1e-6;
2627
+
2628
+ this.frameWidth = width;
2629
+ this.frameHeight = height;
2630
+ this.pixelRatio = ratio;
2631
+
2632
+ if ((resized || ratioChanged) && this.ready)
2633
+ this.configure(true);
2634
+
2635
+ if (this.textLayer != null) {
2636
+ this.textLayer.style.width = (this.frameWidth / this.pixelRatio) + 'px';
2637
+ this.textLayer.style.height = (this.frameHeight / this.pixelRatio) + 'px';
2638
+ }
2639
+ }
2640
+
2641
+ reset() {
2642
+ this.commands.length = 0;
2643
+ this.frameCommandsReleased = false;
2644
+ this.path.clear();
2645
+ this.stack.length = 0;
2646
+ this.state = {
2647
+ strokeStyle: '#000',
2648
+ fillStyle: '#000',
2649
+ lineWidth: 1,
2650
+ lineJoin: 'round',
2651
+ lineCap: 'butt',
2652
+ lineDash: [],
2653
+ font: '12px sans-serif',
2654
+ textAlign: 'start',
2655
+ textBaseline: 'alphabetic',
2656
+ globalAlpha: 1,
2657
+ transform: identity(),
2658
+ clipRegions: null,
2659
+ lineDashOffset: 0,
2660
+ miterLimit: 10,
2661
+ globalCompositeOperation: 'source-over',
2662
+ shadowColor: 'rgba(0,0,0,0)',
2663
+ shadowBlur: 0,
2664
+ shadowOffsetX: 0,
2665
+ shadowOffsetY: 0,
2666
+ direction: 'inherit',
2667
+ filter: 'none',
2668
+ fontKerning: 'auto',
2669
+ fontStretch: 'normal',
2670
+ fontVariantCaps: 'normal',
2671
+ letterSpacing: '0px',
2672
+ textRendering: 'auto',
2673
+ wordSpacing: '0px',
2674
+ imageSmoothingEnabled: true,
2675
+ imageSmoothingQuality: 'high',
2676
+ };
2677
+ if (this.textLayer != null)
2678
+ this.textLayer.textContent = '';
2679
+ }
2680
+
2681
+ getContextAttributes() {
2682
+ return {...CONTEXT_ATTRIBUTES};
2683
+ }
2684
+
2685
+ isContextLost() {
2686
+ return this.disposed || this.failed || this.device == null && this.initPromise == null;
2687
+ }
2688
+
2689
+ async flush() {
2690
+ if (!this.ready && this.initPromise != null)
2691
+ await this.initPromise;
2692
+ try {
2693
+ await this.device?.queue?.onSubmittedWorkDone?.();
2694
+ }
2695
+ catch (err) {}
2696
+ }
2697
+
2698
+ commit() {
2699
+ this.present();
2700
+ }
2701
+
2702
+ drawFocusIfNeeded() {}
2703
+ scrollPathIntoView() {}
2704
+
2705
+ releaseGPUResources() {
2706
+ this.vertexBuffer?.destroy?.();
2707
+ this.vertexBuffer = null;
2708
+ this.vertexBufferSize = 0;
2709
+
2710
+ this.imageVertexBuffer?.destroy?.();
2711
+ this.imageVertexBuffer = null;
2712
+ this.imageVertexBufferSize = 0;
2713
+ this.solidUploadArray = null;
2714
+ this.imageUploadArray = null;
2715
+ this.uniformUploadArray = null;
2716
+ this.memoryTrimToken++;
2717
+
2718
+ for (let record of this.textureRecords)
2719
+ record?.texture?.destroy?.();
2720
+ this.textureRecords.clear();
2721
+ this.textureCache = typeof WeakMap != 'undefined' ? new WeakMap() : null;
2722
+ this.textureBytes = 0;
2723
+
2724
+ this.uniformBuffer?.destroy?.();
2725
+ this.uniformBuffer = null;
2726
+ this.bindGroup = null;
2727
+ this.sampler = null;
2728
+
2729
+ if (this.sharedRuntimeRefHeld && sharedRuntimeRefs > 0) {
2730
+ sharedRuntimeRefs--;
2731
+ this.sharedRuntimeRefHeld = false;
2732
+ }
2733
+ this.runtime = null;
2734
+ this.ownsRuntime = false;
2735
+
2736
+ this.pipeline = null;
2737
+ this.pipelines = null;
2738
+ this.imagePipelines = null;
2739
+ this.clearPipeline = null;
2740
+ this.contextConfigured = false;
2741
+ }
2742
+
2743
+ getMemoryStats() {
2744
+ let retainedCPUBytes = byteLengthOf(this.solidUploadArray) + byteLengthOf(this.imageUploadArray) + byteLengthOf(this.uniformUploadArray);
2745
+ let retainedGPUBytes = (this.vertexBufferSize || 0) + (this.imageVertexBufferSize || 0) + (this.uniformBuffer ? 16 : 0) + (this.textureBytes || 0);
2746
+ return {
2747
+ mode: this.memory.mode,
2748
+ commands: this.commands.length,
2749
+ commandsReleased: this.frameCommandsReleased,
2750
+ retainedCPUBytes,
2751
+ retainedGPUBytes,
2752
+ solidUploadBytes: byteLengthOf(this.solidUploadArray),
2753
+ imageUploadBytes: byteLengthOf(this.imageUploadArray),
2754
+ solidBufferBytes: this.vertexBufferSize || 0,
2755
+ imageBufferBytes: this.imageVertexBufferSize || 0,
2756
+ textureBytes: this.textureBytes || 0,
2757
+ textureRecords: this.textureRecords.size,
2758
+ };
2759
+ }
2760
+
2761
+ trimUploadArrays() {
2762
+ let maxBytes = this.memory.maxRetainedUploadBytes;
2763
+ if (byteLengthOf(this.solidUploadArray) > maxBytes)
2764
+ this.solidUploadArray = null;
2765
+ if (byteLengthOf(this.imageUploadArray) > maxBytes)
2766
+ this.imageUploadArray = null;
2767
+ }
2768
+
2769
+ trimCaches() {
2770
+ if (this.colorCache.size > this.memory.maxColorCacheEntries)
2771
+ this.colorCache.clear();
2772
+ if (this.textMeasureCache.size > this.memory.maxTextMeasureCacheEntries)
2773
+ this.textMeasureCache.clear();
2774
+ this.enforceTextureBudget();
2775
+ }
2776
+
2777
+ trimGPUBuffers() {
2778
+ let maxBytes = this.memory.maxRetainedVertexBufferBytes;
2779
+ if (this.vertexBuffer != null && this.vertexBufferSize > maxBytes) {
2780
+ this.vertexBuffer.destroy?.();
2781
+ this.vertexBuffer = null;
2782
+ this.vertexBufferSize = 0;
2783
+ }
2784
+ if (this.imageVertexBuffer != null && this.imageVertexBufferSize > maxBytes) {
2785
+ this.imageVertexBuffer.destroy?.();
2786
+ this.imageVertexBuffer = null;
2787
+ this.imageVertexBufferSize = 0;
2788
+ }
2789
+ }
2790
+
2791
+ trimMemory() {
2792
+ this.trimUploadArrays();
2793
+ this.trimCaches();
2794
+ if (this.commands.length == 0 || this.frameCommandsReleased)
2795
+ this.trimGPUBuffers();
2796
+ return this.getMemoryStats();
2797
+ }
2798
+
2799
+ scheduleMemoryTrim() {
2800
+ if (this.disposed || this.device == null)
2801
+ return;
2802
+
2803
+ this.trimUploadArrays();
2804
+ this.trimCaches();
2805
+
2806
+ let maxBytes = this.memory.maxRetainedVertexBufferBytes;
2807
+ let needsGpuTrim = this.vertexBufferSize > maxBytes || this.imageVertexBufferSize > maxBytes;
2808
+ if (!needsGpuTrim)
2809
+ return;
2810
+
2811
+ let token = ++this.memoryTrimToken;
2812
+ let queue = this.device.queue;
2813
+ queue?.onSubmittedWorkDone?.().then(() => {
2814
+ if (this.disposed || token != this.memoryTrimToken)
2815
+ return;
2816
+ if (this.commands.length == 0 || this.frameCommandsReleased)
2817
+ this.trimGPUBuffers();
2818
+ }).catch(() => {});
2819
+ }
2820
+
2821
+ ensureVertexBuffer(byteLength) {
2822
+ if (byteLength <= this.vertexBufferSize && this.vertexBuffer != null)
2823
+ return this.vertexBuffer;
2824
+
2825
+ this.vertexBuffer?.destroy?.();
2826
+ let size = Math.max(1024, 1 << Math.ceil(Math.log2(byteLength || 1)));
2827
+ this.vertexBuffer = this.device.createBuffer({
2828
+ size,
2829
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
2830
+ });
2831
+ this.vertexBufferSize = size;
2832
+ return this.vertexBuffer;
2833
+ }
2834
+
2835
+
2836
+ ensureImageVertexBuffer(byteLength) {
2837
+ if (byteLength <= this.imageVertexBufferSize && this.imageVertexBuffer != null)
2838
+ return this.imageVertexBuffer;
2839
+
2840
+ this.imageVertexBuffer?.destroy?.();
2841
+ let size = Math.max(1024, 1 << Math.ceil(Math.log2(byteLength || 1)));
2842
+ this.imageVertexBuffer = this.device.createBuffer({
2843
+ size,
2844
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
2845
+ });
2846
+ this.imageVertexBufferSize = size;
2847
+ return this.imageVertexBuffer;
2848
+ }
2849
+
2850
+ getTextureRecord(source) {
2851
+ if (source == null || this.device == null || this.imageBindGroupLayout == null)
2852
+ return null;
2853
+
2854
+ let width = Math.floor(sourceWidth(source));
2855
+ let height = Math.floor(sourceHeight(source));
2856
+ if (width <= 0 || height <= 0)
2857
+ return null;
2858
+
2859
+ if (this.runtime != null)
2860
+ this.sampler = getRuntimeSampler(this.runtime, this.state.imageSmoothingEnabled, this.state.imageSmoothingQuality);
2861
+
2862
+ let record = this.textureCache?.get(source);
2863
+ let needsCreate = record == null || record.width != width || record.height != height;
2864
+
2865
+ if (needsCreate) {
2866
+ if (record != null) {
2867
+ record.texture?.destroy?.();
2868
+ this.textureRecords.delete(record);
2869
+ this.textureBytes = Math.max(0, this.textureBytes - (record.bytes || 0));
2870
+ }
2871
+ let texture = this.device.createTexture({
2872
+ size: [width, height, 1],
2873
+ format: 'rgba8unorm',
2874
+ usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
2875
+ });
2876
+ let bindGroup = this.device.createBindGroup({
2877
+ layout: this.imageBindGroupLayout,
2878
+ entries: [
2879
+ {binding: 0, resource: this.sampler},
2880
+ {binding: 1, resource: texture.createView()},
2881
+ ],
2882
+ });
2883
+ record = {source, texture, bindGroup, width, height, bytes: textureByteSize(width, height), lastUsed: ++this.textureSerial};
2884
+ this.textureBytes += record.bytes;
2885
+ this.textureRecords.add(record);
2886
+ this.textureCache?.set(source, record);
2887
+ }
2888
+ else {
2889
+ record.lastUsed = ++this.textureSerial;
2890
+ }
2891
+
2892
+ try {
2893
+ if (isImageDataLike(source)) {
2894
+ let data = source.data;
2895
+ if (data == null || data.length < width * height * 4)
2896
+ return null;
2897
+ this.device.queue.writeTexture(
2898
+ {texture: record.texture},
2899
+ data,
2900
+ {bytesPerRow: width * 4, rowsPerImage: height},
2901
+ [width, height],
2902
+ );
2903
+ }
2904
+ else {
2905
+ this.device.queue.copyExternalImageToTexture(
2906
+ {source},
2907
+ {texture: record.texture},
2908
+ [width, height],
2909
+ );
2910
+ }
2911
+ }
2912
+ catch (err) {
2913
+ return null;
2914
+ }
2915
+
2916
+ this.enforceTextureBudget(record);
2917
+ return record;
2918
+ }
2919
+
2920
+ destroyTextureRecord(record) {
2921
+ if (record == null)
2922
+ return;
2923
+ record.texture?.destroy?.();
2924
+ this.textureRecords.delete(record);
2925
+ this.textureCache?.delete?.(record.source);
2926
+ this.textureBytes = Math.max(0, this.textureBytes - (record.bytes || 0));
2927
+ }
2928
+
2929
+ enforceTextureBudget(keep = null) {
2930
+ let maxBytes = this.memory.maxTextureBytes;
2931
+ let maxRecords = this.memory.maxTextureRecords;
2932
+ if (this.textureRecords.size <= maxRecords && this.textureBytes <= maxBytes)
2933
+ return;
2934
+
2935
+ let victims = Array.from(this.textureRecords).filter(record => record !== keep).sort((a, b) => (a.lastUsed || 0) - (b.lastUsed || 0));
2936
+ for (let record of victims) {
2937
+ if (this.textureRecords.size <= maxRecords && this.textureBytes <= maxBytes)
2938
+ break;
2939
+ this.destroyTextureRecord(record);
2940
+ }
2941
+ }
2942
+
2943
+ clearRect(x = 0, y = 0, w = this.canvas.width || 0, h = this.canvas.height || 0) {
2944
+ this.syncFrameMetrics();
2945
+
2946
+ let m = this.state.transform;
2947
+ let fullClear = x <= 0 && y <= 0 && x + w >= this.frameWidth && y + h >= this.frameHeight
2948
+ && Math.abs(m[0] - 1) < EPS && Math.abs(m[1]) < EPS && Math.abs(m[2]) < EPS
2949
+ && Math.abs(m[3] - 1) < EPS && Math.abs(m[4]) < EPS && Math.abs(m[5]) < EPS;
2950
+
2951
+ if (fullClear) {
2952
+ this.commands.length = 0;
2953
+ this.frameCommandsReleased = false;
2954
+ this.path.clear();
2955
+ }
2956
+ else {
2957
+ let path = new GPUPath();
2958
+ path.rect(x, y, w, h);
2959
+ let prevFill = this.state.fillStyle;
2960
+ let prevAlpha = this.state.globalAlpha;
2961
+ this.state.fillStyle = TRANSPARENT;
2962
+ this.state.globalAlpha = 1;
2963
+ this.fill(path, 'nonzero', 'clear');
2964
+ this.state.fillStyle = prevFill;
2965
+ this.state.globalAlpha = prevAlpha;
2966
+ this.clearTextInPath(path);
2967
+ }
2968
+
2969
+ if (this.textLayer != null && fullClear)
2970
+ this.textLayer.textContent = '';
2971
+ }
2972
+
2973
+ clearTextInPath(path) {
2974
+ if (this.textLayer == null || path == null)
2975
+ return;
2976
+
2977
+ let regions = pathToClipRegions(path, this.state.transform, 'nonzero');
2978
+ if (regions.length == 0)
2979
+ return;
2980
+
2981
+ for (let node of Array.from(this.textLayer.children)) {
2982
+ let x = Number(node.dataset.uplotX);
2983
+ let y = Number(node.dataset.uplotY);
2984
+ if (!Number.isFinite(x) || !Number.isFinite(y))
2985
+ continue;
2986
+
2987
+ if (pointInClipRegions([x, y], regions))
2988
+ node.remove();
2989
+ }
2990
+ }
2991
+
2992
+ prepareDrawBuffers() {
2993
+ let device = this.device;
2994
+ if (device == null || this.uniformBuffer == null)
2995
+ return {solidBuffer: null, imageBuffer: null, stats: {commands: this.commands.length, solidCommands: 0, imageCommands: 0, solidVertices: 0, imageVertices: 0, solidBytes: 0, imageBytes: 0, uploadBytes: 0, writeCalls: 0, solidUploadCapacityBytes: this.vertexBufferSize || 0, imageUploadCapacityBytes: this.imageVertexBufferSize || 0, frameWidth: this.frameWidth, frameHeight: this.frameHeight, pixelRatio: this.pixelRatio, textNodes: this.textLayer?.childElementCount || 0}};
2996
+
2997
+ let uploadBytes = 0;
2998
+ let writeCalls = 0;
2999
+ let uniformData = this.uniformUploadArray || (this.uniformUploadArray = new Float32Array(2));
3000
+ uniformData[0] = this.frameWidth;
3001
+ uniformData[1] = this.frameHeight;
3002
+ device.queue.writeBuffer(this.uniformBuffer, 0, uniformData);
3003
+ uploadBytes += uniformData.byteLength;
3004
+ writeCalls++;
3005
+
3006
+ let solidVertexCount = 0;
3007
+ let imageVertexCount = 0;
3008
+ let solidCommands = 0;
3009
+ let imageCommands = 0;
3010
+
3011
+ for (let cmd of this.commands) {
3012
+ if (cmd.kind == 'image') {
3013
+ imageVertexCount += cmd.vertices.length / FLOATS_PER_IMAGE_VERTEX;
3014
+ imageCommands++;
3015
+ }
3016
+ else {
3017
+ solidVertexCount += cmd.vertices.length / FLOATS_PER_VERTEX;
3018
+ solidCommands++;
3019
+ }
3020
+ }
3021
+
3022
+ let solidBuffer = null;
3023
+ if (solidVertexCount > 0) {
3024
+ let needed = solidVertexCount * FLOATS_PER_VERTEX;
3025
+ if (this.solidUploadArray == null || this.solidUploadArray.length < needed)
3026
+ this.solidUploadArray = new Float32Array(1 << Math.ceil(Math.log2(needed || 1)));
3027
+ let vertexArray = this.solidUploadArray.subarray(0, needed);
3028
+ let offset = 0;
3029
+
3030
+ for (let cmd of this.commands) {
3031
+ if (cmd.kind == 'image')
3032
+ continue;
3033
+ cmd.offset = offset / FLOATS_PER_VERTEX;
3034
+ cmd.count = cmd.vertices.length / FLOATS_PER_VERTEX;
3035
+ vertexArray.set(cmd.vertices, offset);
3036
+ offset += cmd.vertices.length;
3037
+ }
3038
+
3039
+ solidBuffer = this.ensureVertexBuffer(vertexArray.byteLength);
3040
+ device.queue.writeBuffer(solidBuffer, 0, vertexArray);
3041
+ uploadBytes += vertexArray.byteLength;
3042
+ writeCalls++;
3043
+ }
3044
+
3045
+ let imageBuffer = null;
3046
+ if (imageVertexCount > 0) {
3047
+ let needed = imageVertexCount * FLOATS_PER_IMAGE_VERTEX;
3048
+ if (this.imageUploadArray == null || this.imageUploadArray.length < needed)
3049
+ this.imageUploadArray = new Float32Array(1 << Math.ceil(Math.log2(needed || 1)));
3050
+ let vertexArray = this.imageUploadArray.subarray(0, needed);
3051
+ let offset = 0;
3052
+
3053
+ for (let cmd of this.commands) {
3054
+ if (cmd.kind != 'image')
3055
+ continue;
3056
+ cmd.offset = offset / FLOATS_PER_IMAGE_VERTEX;
3057
+ cmd.count = cmd.vertices.length / FLOATS_PER_IMAGE_VERTEX;
3058
+ vertexArray.set(cmd.vertices, offset);
3059
+ offset += cmd.vertices.length;
3060
+ }
3061
+
3062
+ imageBuffer = this.ensureImageVertexBuffer(vertexArray.byteLength);
3063
+ device.queue.writeBuffer(imageBuffer, 0, vertexArray);
3064
+ uploadBytes += vertexArray.byteLength;
3065
+ writeCalls++;
3066
+ }
3067
+
3068
+ let stats = {
3069
+ commands: this.commands.length,
3070
+ solidCommands,
3071
+ imageCommands,
3072
+ solidVertices: solidVertexCount,
3073
+ imageVertices: imageVertexCount,
3074
+ solidBytes: solidVertexCount * FLOATS_PER_VERTEX * 4,
3075
+ imageBytes: imageVertexCount * FLOATS_PER_IMAGE_VERTEX * 4,
3076
+ uploadBytes,
3077
+ writeCalls,
3078
+ solidUploadCapacityBytes: this.vertexBufferSize || 0,
3079
+ imageUploadCapacityBytes: this.imageVertexBufferSize || 0,
3080
+ frameWidth: this.frameWidth,
3081
+ frameHeight: this.frameHeight,
3082
+ pixelRatio: this.pixelRatio,
3083
+ textNodes: this.textLayer?.childElementCount || 0,
3084
+ retainedCPUBytes: byteLengthOf(this.solidUploadArray) + byteLengthOf(this.imageUploadArray) + byteLengthOf(this.uniformUploadArray),
3085
+ retainedGPUBytes: (this.vertexBufferSize || 0) + (this.imageVertexBufferSize || 0) + (this.uniformBuffer ? 16 : 0) + (this.textureBytes || 0),
3086
+ textureBytes: this.textureBytes || 0,
3087
+ commandsReleased: false,
3088
+ };
3089
+
3090
+ return {solidBuffer, imageBuffer, stats};
3091
+ }
3092
+
3093
+ encodeDrawPass(encoder, view, solidBuffer, imageBuffer) {
3094
+ let pass = encoder.beginRenderPass({
3095
+ colorAttachments: [{
3096
+ view,
3097
+ clearValue: {r: 0, g: 0, b: 0, a: 0},
3098
+ loadOp: 'clear',
3099
+ storeOp: 'store',
3100
+ }],
3101
+ });
3102
+
3103
+ pass.setBindGroup(0, this.bindGroup);
3104
+ let activePipeline = null;
3105
+ let activeBindGroup = null;
3106
+ let activeBufferKind = null;
3107
+
3108
+ for (let cmd of this.commands) {
3109
+ let composite = clampCompositeMode(cmd.composite);
3110
+
3111
+ if (cmd.kind == 'image') {
3112
+ if (cmd.bindGroup == null && cmd.source != null) {
3113
+ let record = this.getTextureRecord(cmd.source);
3114
+ if (record != null)
3115
+ cmd.bindGroup = record.bindGroup;
3116
+ }
3117
+ if (imageBuffer == null || cmd.bindGroup == null || cmd.count <= 0)
3118
+ continue;
3119
+
3120
+ let pipeline = this.imagePipelines[composite] || this.imagePipelines['source-over'];
3121
+ if (pipeline !== activePipeline) {
3122
+ pass.setPipeline(pipeline);
3123
+ activePipeline = pipeline;
3124
+ }
3125
+ if (activeBufferKind != 'image') {
3126
+ pass.setVertexBuffer(0, imageBuffer);
3127
+ activeBufferKind = 'image';
3128
+ }
3129
+ if (cmd.bindGroup !== activeBindGroup) {
3130
+ pass.setBindGroup(1, cmd.bindGroup);
3131
+ activeBindGroup = cmd.bindGroup;
3132
+ }
3133
+ pass.draw(cmd.count, 1, cmd.offset, 0);
3134
+ }
3135
+ else {
3136
+ if (solidBuffer == null || cmd.count <= 0)
3137
+ continue;
3138
+
3139
+ let pipeline = this.pipelines[composite] || this.pipeline;
3140
+ if (pipeline !== activePipeline) {
3141
+ pass.setPipeline(pipeline);
3142
+ activePipeline = pipeline;
3143
+ }
3144
+ if (activeBufferKind != 'solid') {
3145
+ pass.setVertexBuffer(0, solidBuffer);
3146
+ activeBufferKind = 'solid';
3147
+ }
3148
+ pass.draw(cmd.count, 1, cmd.offset, 0);
3149
+ }
3150
+ }
3151
+
3152
+ pass.end();
3153
+ }
3154
+
3155
+ present() {
3156
+ this.syncFrameMetrics();
3157
+
3158
+ if (!this.ready) {
3159
+ this.pendingPresent = true;
3160
+ return;
3161
+ }
3162
+
3163
+ this.pendingPresent = false;
3164
+ if (this.frameCommandsReleased && this.commands.length == 0)
3165
+ return;
3166
+
3167
+ this.configure(false);
3168
+
3169
+ if (this.frameWidth <= 0 || this.frameHeight <= 0)
3170
+ return;
3171
+
3172
+ let device = this.device;
3173
+ let frameStart = perfNow();
3174
+ let prepareStart = perfNow();
3175
+ let {solidBuffer, imageBuffer, stats} = this.prepareDrawBuffers();
3176
+ let prepareMs = perfNow() - prepareStart;
3177
+
3178
+ let currentTexture;
3179
+ try {
3180
+ currentTexture = this.context.getCurrentTexture();
3181
+ }
3182
+ catch (err) {
3183
+ this.configure(true);
3184
+ try {
3185
+ currentTexture = this.context.getCurrentTexture();
3186
+ }
3187
+ catch (err2) {
3188
+ this.pendingPresent = true;
3189
+ return;
3190
+ }
3191
+ }
3192
+
3193
+ let encodeStart = perfNow();
3194
+ let encoder = device.createCommandEncoder();
3195
+ this.encodeDrawPass(encoder, currentTexture.createView(), solidBuffer, imageBuffer);
3196
+ let commandBuffer = encoder.finish();
3197
+ let encodeMs = perfNow() - encodeStart;
3198
+
3199
+ try {
3200
+ let submitStart = perfNow();
3201
+ device.queue.submit([commandBuffer]);
3202
+ let submitMs = perfNow() - submitStart;
3203
+ let commandsReleased = false;
3204
+
3205
+ if (this.memory.releaseCommandsAfterPresent) {
3206
+ this.commands.length = 0;
3207
+ this.frameCommandsReleased = true;
3208
+ commandsReleased = true;
3209
+ }
3210
+
3211
+ this.trimUploadArrays();
3212
+ this.trimCaches();
3213
+ let memoryStats = this.getMemoryStats();
3214
+ this.lastFrameStats = {
3215
+ ...stats,
3216
+ prepareMs,
3217
+ encodeMs,
3218
+ submitMs,
3219
+ cpuFrameMs: perfNow() - frameStart,
3220
+ presents: (this.lastFrameStats?.presents || 0) + 1,
3221
+ retainedCPUBytes: memoryStats.retainedCPUBytes,
3222
+ retainedGPUBytes: memoryStats.retainedGPUBytes,
3223
+ textureBytes: memoryStats.textureBytes,
3224
+ commandsReleased,
3225
+ };
3226
+ this.scheduleMemoryTrim();
3227
+ }
3228
+ catch (err) {
3229
+ this.ready = false;
3230
+ this.failed = true;
3231
+ this.releaseGPUResources();
3232
+ if (!this.disposed)
3233
+ this.initPromise = this.init();
3234
+ }
3235
+ }
3236
+
3237
+ getImageData(sx = 0, sy = 0, sw = this.frameWidth, sh = this.frameHeight) {
3238
+ this.syncFrameMetrics();
3239
+ let rect = normalizeReadRect(sx, sy, sw, sh, this.frameWidth, this.frameHeight);
3240
+ if (rect == null)
3241
+ return createImageDataObject(0, 0);
3242
+ return createImageDataObject(rect.w, rect.h);
3243
+ }
3244
+
3245
+ async getImageDataAsync(sx = 0, sy = 0, sw = this.frameWidth, sh = this.frameHeight) {
3246
+ this.syncFrameMetrics();
3247
+ let rect = normalizeReadRect(sx, sy, sw, sh, this.frameWidth, this.frameHeight);
3248
+ if (rect == null)
3249
+ return createImageDataObject(0, 0);
3250
+
3251
+ if (!this.ready && this.initPromise != null)
3252
+ await this.initPromise;
3253
+ if (!this.ready || this.disposed || this.device == null)
3254
+ return createImageDataObject(rect.w, rect.h);
3255
+
3256
+ let device = this.device;
3257
+ let bytesPerPixel = 4;
3258
+ let unpaddedBytesPerRow = rect.w * bytesPerPixel;
3259
+ let bytesPerRow = alignBytesPerRow(unpaddedBytesPerRow);
3260
+ let bufferSize = bytesPerRow * rect.h;
3261
+ let texture = device.createTexture({
3262
+ size: [this.frameWidth, this.frameHeight, 1],
3263
+ format: this.format,
3264
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
3265
+ });
3266
+ let readBuffer = device.createBuffer({
3267
+ size: bufferSize,
3268
+ usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
3269
+ });
3270
+
3271
+ let {solidBuffer, imageBuffer} = this.prepareDrawBuffers();
3272
+ let encoder = device.createCommandEncoder();
3273
+ this.encodeDrawPass(encoder, texture.createView(), solidBuffer, imageBuffer);
3274
+ encoder.copyTextureToBuffer(
3275
+ {texture, origin: {x: rect.x, y: rect.y, z: 0}},
3276
+ {buffer: readBuffer, bytesPerRow, rowsPerImage: rect.h},
3277
+ {width: rect.w, height: rect.h, depthOrArrayLayers: 1},
3278
+ );
3279
+ device.queue.submit([encoder.finish()]);
3280
+
3281
+ try {
3282
+ await readBuffer.mapAsync(GPUMapMode.READ);
3283
+ let mapped = new Uint8Array(readBuffer.getMappedRange());
3284
+ let out = new Uint8ClampedArray(rect.w * rect.h * 4);
3285
+ let bgra = String(this.format || '').toLowerCase().startsWith('bgra');
3286
+ for (let y = 0; y < rect.h; y++) {
3287
+ let src = y * bytesPerRow;
3288
+ let dst = y * unpaddedBytesPerRow;
3289
+ if (bgra) {
3290
+ for (let x = 0; x < rect.w; x++) {
3291
+ let si = src + x * 4;
3292
+ let di = dst + x * 4;
3293
+ out[di + 0] = mapped[si + 2];
3294
+ out[di + 1] = mapped[si + 1];
3295
+ out[di + 2] = mapped[si + 0];
3296
+ out[di + 3] = mapped[si + 3];
3297
+ }
3298
+ }
3299
+ else
3300
+ out.set(mapped.subarray(src, src + unpaddedBytesPerRow), dst);
3301
+ }
3302
+ readBuffer.unmap();
3303
+ return createImageDataObject(rect.w, rect.h, out);
3304
+ }
3305
+ finally {
3306
+ texture.destroy?.();
3307
+ readBuffer.destroy?.();
3308
+ }
3309
+ }
3310
+
3311
+ async exportImageBytes(type = 'image/png') {
3312
+ let tools = await loadExportTools();
3313
+ type = tools.normalizeExportType(type);
3314
+ let img = await this.getImageDataAsync(0, 0, this.frameWidth, this.frameHeight);
3315
+ let bytes = type == 'image/bmp' ? tools.rgbaImageDataToBmpBytes(img) : tools.rgbaImageDataToPngBytes(img);
3316
+ return {type, bytes};
3317
+ }
3318
+
3319
+ async convertToBlob(options = {}) {
3320
+ let tools = await loadExportTools();
3321
+ let requestedType = typeof options == 'string' ? options : options?.type;
3322
+ let {type, bytes} = await this.exportImageBytes(requestedType || 'image/png');
3323
+ return tools.bytesToBlob(bytes, type);
3324
+ }
3325
+
3326
+ async toDataURLAsync(type = 'image/png') {
3327
+ let tools = await loadExportTools();
3328
+ let exported = await this.exportImageBytes(type);
3329
+ return `data:${exported.type};base64,${tools.bytesToBase64(exported.bytes)}`;
3330
+ }
3331
+
3332
+ async toBlob(callback, type = 'image/png') {
3333
+ let blob = await this.convertToBlob({type});
3334
+ if (typeof callback == 'function')
3335
+ callback(blob);
3336
+ return blob;
3337
+ }
3338
+
3339
+ async toSVGStringAsync(options = {}) {
3340
+ let tools = await loadExportTools();
3341
+ let pixelRatio = this.pixelRatio || 1;
3342
+ let width = this.frameWidth / pixelRatio;
3343
+ let height = this.frameHeight / pixelRatio;
3344
+ let imageHref = options.background === false ? '' : await this.toDataURLAsync(options.imageType || 'image/png');
3345
+ let image = imageHref
3346
+ ? `<image href="${tools.escapeXmlAttr(imageHref)}" x="0" y="0" width="${width}" height="${height}"/>`
3347
+ : '';
3348
+ let html = this.textLayer?.innerHTML || '';
3349
+ let text = html
3350
+ ? `<foreignObject x="0" y="0" width="${width}" height="${height}"><div xmlns="http://www.w3.org/1999/xhtml" style="position:relative;width:${width}px;height:${height}px;overflow:hidden">${html}</div></foreignObject>`
3351
+ : '';
3352
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">${image}${text}</svg>`;
3353
+ }
3354
+
3355
+ async toSVGBlob(options = {}) {
3356
+ let tools = await loadExportTools();
3357
+ let svg = await this.toSVGStringAsync(options);
3358
+ return tools.textToBlob(svg, 'image/svg+xml');
3359
+ }
3360
+
3361
+ async toSVGDataURLAsync(options = {}) {
3362
+ let tools = await loadExportTools();
3363
+ let svg = await this.toSVGStringAsync(options);
3364
+ return `data:image/svg+xml;base64,${tools.encodeTextBase64(svg)}`;
3365
+ }
3366
+
3367
+ save() {
3368
+ this.stack.push(cloneState(this.state));
3369
+ }
3370
+
3371
+ restore() {
3372
+ let state = this.stack.pop();
3373
+ if (state != null)
3374
+ this.state = state;
3375
+ }
3376
+
3377
+ translate(x, y) {
3378
+ x = Number(x);
3379
+ y = Number(y);
3380
+ if (Number.isFinite(x) && Number.isFinite(y))
3381
+ this.state.transform = multiply(this.state.transform, [1, 0, 0, 1, x, y]);
3382
+ }
3383
+
3384
+ rotate(angle) {
3385
+ angle = Number(angle);
3386
+ if (!Number.isFinite(angle))
3387
+ return;
3388
+ let c = Math.cos(angle);
3389
+ let s = Math.sin(angle);
3390
+ this.state.transform = multiply(this.state.transform, [c, s, -s, c, 0, 0]);
3391
+ }
3392
+
3393
+ scale(x, y = x) {
3394
+ x = Number(x);
3395
+ y = Number(y);
3396
+ if (Number.isFinite(x) && Number.isFinite(y))
3397
+ this.state.transform = multiply(this.state.transform, [x, 0, 0, y, 0, 0]);
3398
+ }
3399
+
3400
+ transform(a = 1, b = 0, c = 0, d = 1, e = 0, f = 0) {
3401
+ let next = finiteTransform([a, b, c, d, e, f]);
3402
+ if (next != null)
3403
+ this.state.transform = multiply(this.state.transform, next);
3404
+ }
3405
+
3406
+ getTransform() {
3407
+ let [a, b, c, d, e, f] = this.state.transform;
3408
+
3409
+ if (typeof DOMMatrix != 'undefined')
3410
+ return new DOMMatrix([a, b, c, d, e, f]);
3411
+
3412
+ return {a, b, c, d, e, f, is2D: true};
3413
+ }
3414
+
3415
+ setTransform(a = 1, b = 0, c = 0, d = 1, e = 0, f = 0) {
3416
+ let next = typeof a == 'object' && a != null
3417
+ ? finiteTransform([a.a, a.b, a.c, a.d, a.e, a.f])
3418
+ : finiteTransform([a, b, c, d, e, f]);
3419
+ if (next != null)
3420
+ this.state.transform = next;
3421
+ }
3422
+
3423
+ resetTransform() {
3424
+ this.state.transform = identity();
3425
+ }
3426
+
3427
+ getLineDash() {
3428
+ return this.state.lineDash.slice();
3429
+ }
3430
+
3431
+ setLineDash(dash) {
3432
+ if (dash == null) {
3433
+ this.state.lineDash = [];
3434
+ return;
3435
+ }
3436
+
3437
+ let next = Array.from(dash, Number);
3438
+ if (next.every(v => Number.isFinite(v) && v >= 0)) {
3439
+ if (next.length % 2 == 1)
3440
+ next = next.concat(next);
3441
+ this.state.lineDash = next;
3442
+ }
3443
+ }
3444
+
3445
+ beginPath() {
3446
+ this.path.clear();
3447
+ }
3448
+
3449
+ moveTo(x, y) {
3450
+ this.path.moveTo(x, y);
3451
+ }
3452
+
3453
+ lineTo(x, y) {
3454
+ this.path.lineTo(x, y);
3455
+ }
3456
+
3457
+ rect(x, y, w, h) {
3458
+ this.path.rect(x, y, w, h);
3459
+ }
3460
+
3461
+ arc(x, y, r, startAngle, endAngle, counterclockwise = false) {
3462
+ this.path.arc(x, y, r, startAngle, endAngle, counterclockwise);
3463
+ }
3464
+
3465
+ arcTo(x1, y1, x2, y2, r) {
3466
+ this.path.arcTo(x1, y1, x2, y2, r);
3467
+ }
3468
+
3469
+ ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise = false) {
3470
+ this.path.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise);
3471
+ }
3472
+
3473
+ roundRect(x, y, w, h, radii = 0) {
3474
+ this.path.roundRect(x, y, w, h, radii);
3475
+ }
3476
+
3477
+ closePath() {
3478
+ this.path.closePath();
3479
+ }
3480
+
3481
+ quadraticCurveTo(cpx, cpy, x, y) {
3482
+ this.path.quadraticCurveTo(cpx, cpy, x, y);
3483
+ }
3484
+
3485
+ bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
3486
+ this.path.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
3487
+ }
3488
+
3489
+ addPath(path, transform = null) {
3490
+ this.path.addPath(path, transform);
3491
+ }
3492
+
3493
+ clip(path = this.path, fillRule = 'nonzero') {
3494
+ if (typeof path == 'string') {
3495
+ fillRule = path;
3496
+ path = this.path;
3497
+ }
3498
+
3499
+ if (path == null)
3500
+ return;
3501
+
3502
+ let regions = pathToClipRegions(path, this.state.transform, fillRule);
3503
+ this.state.clipRegions = combineClipRegions(this.state.clipRegions, regions);
3504
+ }
3505
+
3506
+ pathBounds(path) {
3507
+ let subpaths = path?.toSubpaths?.() || [];
3508
+ let minX = Infinity;
3509
+ let minY = Infinity;
3510
+ let maxX = -Infinity;
3511
+ let maxY = -Infinity;
3512
+
3513
+ for (let sub of subpaths) {
3514
+ for (let p of sub) {
3515
+ minX = Math.min(minX, p[0]);
3516
+ minY = Math.min(minY, p[1]);
3517
+ maxX = Math.max(maxX, p[0]);
3518
+ maxY = Math.max(maxY, p[1]);
3519
+ }
3520
+ }
3521
+
3522
+ return minX == Infinity ? null : {x: minX, y: minY, w: maxX - minX, h: maxY - minY};
3523
+ }
3524
+
3525
+ shadowColorValue() {
3526
+ return this.parseColor(this.state.shadowColor, this.state.globalAlpha);
3527
+ }
3528
+
3529
+ shadowOffsetVector() {
3530
+ return transformVec(this.state.transform, this.state.shadowOffsetX || 0, this.state.shadowOffsetY || 0);
3531
+ }
3532
+
3533
+ enqueueShadowVertices(vertices) {
3534
+ if (!hasActiveShadow(this.state) || vertices == null || vertices.length == 0)
3535
+ return;
3536
+
3537
+ let color = this.shadowColorValue();
3538
+ if (color[3] <= 0)
3539
+ return;
3540
+
3541
+ let [dx, dy] = this.shadowOffsetVector();
3542
+ let blur = Math.max(0, (this.state.shadowBlur || 0) * transformScale(this.state.transform));
3543
+ let copies = blur > 0 ? Math.min(12, Math.max(4, Math.round(blur / 2))) : 0;
3544
+ let batches = [];
3545
+
3546
+ if (copies == 0)
3547
+ batches.push(offsetSolidVertices(vertices, dx, dy));
3548
+ else {
3549
+ let baseAlpha = 1 / (copies + 1);
3550
+ batches.push(offsetSolidVertices(vertices, dx, dy, baseAlpha));
3551
+ for (let i = 0; i < copies; i++) {
3552
+ let ang = (i / copies) * Math.PI * 2;
3553
+ let ox = dx + Math.cos(ang) * blur;
3554
+ let oy = dy + Math.sin(ang) * blur;
3555
+ batches.push(offsetSolidVertices(vertices, ox, oy, baseAlpha));
3556
+ }
3557
+ }
3558
+
3559
+ let out = [];
3560
+ for (let verts of batches) {
3561
+ for (let i = 0; i < verts.length; i += FLOATS_PER_VERTEX) {
3562
+ verts[i + 2] = color[0];
3563
+ verts[i + 3] = color[1];
3564
+ verts[i + 4] = color[2];
3565
+ verts[i + 5] *= color[3];
3566
+ }
3567
+ out.push(...verts);
3568
+ }
3569
+
3570
+ this.addGeometry(out);
3571
+ }
3572
+
3573
+ enqueuePathShadow(path, fillRule = 'nonzero') {
3574
+ if (!hasActiveShadow(this.state) || path == null)
3575
+ return;
3576
+
3577
+ let vertices = [];
3578
+ let regions = pathToClipRegions(path, this.state.transform, fillRule);
3579
+ let seedColor = [1, 1, 1, 1];
3580
+
3581
+ for (let region of regions) {
3582
+ for (let tri of triangulate(region)) {
3583
+ pushVertex(vertices, tri[0][0], tri[0][1], seedColor);
3584
+ pushVertex(vertices, tri[1][0], tri[1][1], seedColor);
3585
+ pushVertex(vertices, tri[2][0], tri[2][1], seedColor);
3586
+ }
3587
+ }
3588
+
3589
+ this.enqueueShadowVertices(vertices);
3590
+ }
3591
+
3592
+ stroke(path = this.path) {
3593
+ if (typeof path == 'string')
3594
+ path = this.path;
3595
+
3596
+ if (path == null || this.state.lineWidth <= 0)
3597
+ return;
3598
+
3599
+ let color = this.parseColor(this.state.strokeStyle, this.state.globalAlpha);
3600
+ if (color[3] <= 0)
3601
+ return;
3602
+
3603
+ let vertices = [];
3604
+ let subpaths = path.toSubpaths();
3605
+ let m = this.state.transform;
3606
+ let scale = transformScale(m);
3607
+ let width = Math.max(0.01, this.state.lineWidth * scale);
3608
+ let dash = this.state.lineDash.map(v => v * scale);
3609
+ let dashOffset = this.state.lineDashOffset * scale;
3610
+
3611
+ for (let sub of subpaths) {
3612
+ let transformed = sub.map(p => transformPoint(m, p[0], p[1]));
3613
+ transformed.closed = !!sub.closed && dash.length == 0;
3614
+ let segments = dashSegments(transformed, dash, dashOffset);
3615
+
3616
+ for (let seg of segments)
3617
+ addPolyline(vertices, seg, width, color, this.state.lineCap, this.state.lineJoin, this.state.miterLimit);
3618
+ }
3619
+
3620
+ this.enqueueShadowVertices(vertices);
3621
+ this.addGeometry(vertices);
3622
+ }
3623
+
3624
+ fill(path = this.path, fillRule = 'nonzero', kind = 'draw') {
3625
+ if (typeof path == 'string') {
3626
+ fillRule = path;
3627
+ path = this.path;
3628
+ }
3629
+
3630
+ if (path == null)
3631
+ return;
3632
+
3633
+ if (kind != 'clear' && isPatternPaint(this.state.fillStyle)) {
3634
+ this.fillPattern(path, this.state.fillStyle, fillRule);
3635
+ return;
3636
+ }
3637
+
3638
+ let color = this.parseColor(this.state.fillStyle, this.state.globalAlpha);
3639
+ if (kind != 'clear' && color[3] <= 0)
3640
+ return;
3641
+
3642
+ let vertices = [];
3643
+ let regions = pathToFillRegions(path, this.state.transform, fillRule);
3644
+
3645
+ for (let region of regions)
3646
+ pushPaintedRegion(vertices, region, color);
3647
+
3648
+ if (kind != 'clear')
3649
+ this.enqueueShadowVertices(vertices);
3650
+ this.addGeometry(vertices, kind);
3651
+ }
3652
+
3653
+
3654
+ fillPattern(path, pattern, fillRule = 'nonzero') {
3655
+ let record = this.getTextureRecord(pattern.source) || (!this.ready ? {source: pattern.source, width: sourceWidth(pattern.source), height: sourceHeight(pattern.source), bindGroup: null} : null);
3656
+ if (record == null || record.width <= 0 || record.height <= 0)
3657
+ return;
3658
+
3659
+ let regions = pathToClipRegions(path, this.state.transform, fillRule);
3660
+ if (regions.length == 0)
3661
+ return;
3662
+
3663
+ if (hasActiveShadow(this.state))
3664
+ this.enqueuePathShadow(path, fillRule);
3665
+
3666
+ let combined = combineClipRegions(this.state.clipRegions, regions);
3667
+ let bounds = boundsOfRegions(combined);
3668
+ if (bounds == null)
3669
+ return;
3670
+
3671
+ let repeatX = pattern.repetition == 'repeat' || pattern.repetition == 'repeat-x' || pattern.repetition == '';
3672
+ let repeatY = pattern.repetition == 'repeat' || pattern.repetition == 'repeat-y' || pattern.repetition == '';
3673
+ let pm = pattern.transform || identity();
3674
+ let vx = [pm[0] * record.width, pm[1] * record.width];
3675
+ let vy = [pm[2] * record.height, pm[3] * record.height];
3676
+ let origin = [pm[4] || 0, pm[5] || 0];
3677
+ let inv = invert2x2(vx[0], vx[1], vy[0], vy[1]);
3678
+ if (inv == null) {
3679
+ this.addImageRect(record, 0, 0, record.width, record.height, origin[0], origin[1], record.width, record.height, combined, true);
3680
+ return;
3681
+ }
3682
+
3683
+ let corners = [
3684
+ [bounds.x, bounds.y],
3685
+ [bounds.x + bounds.w, bounds.y],
3686
+ [bounds.x, bounds.y + bounds.h],
3687
+ [bounds.x + bounds.w, bounds.y + bounds.h],
3688
+ ];
3689
+ let minI = Infinity, minJ = Infinity, maxI = -Infinity, maxJ = -Infinity;
3690
+ for (let [x, y] of corners) {
3691
+ let rx = x - origin[0];
3692
+ let ry = y - origin[1];
3693
+ let i = inv[0] * rx + inv[2] * ry;
3694
+ let j = inv[1] * rx + inv[3] * ry;
3695
+ minI = Math.min(minI, i);
3696
+ maxI = Math.max(maxI, i);
3697
+ minJ = Math.min(minJ, j);
3698
+ maxJ = Math.max(maxJ, j);
3699
+ }
3700
+
3701
+ let i0 = repeatX ? Math.floor(minI) - 1 : 0;
3702
+ let i1 = repeatX ? Math.ceil(maxI) + 1 : 0;
3703
+ let j0 = repeatY ? Math.floor(minJ) - 1 : 0;
3704
+ let j1 = repeatY ? Math.ceil(maxJ) + 1 : 0;
3705
+
3706
+ for (let j = j0; j <= j1; j++) {
3707
+ for (let i = i0; i <= i1; i++) {
3708
+ let p0 = [origin[0] + vx[0] * i + vy[0] * j, origin[1] + vx[1] * i + vy[1] * j];
3709
+ let p1 = [p0[0] + vx[0], p0[1] + vx[1]];
3710
+ let p2 = [p0[0] + vy[0], p0[1] + vy[1]];
3711
+ let p3 = [p1[0] + vy[0], p1[1] + vy[1]];
3712
+ this.addImageQuadGeometry(record, p0, p1, p2, p3, combined);
3713
+ }
3714
+ }
3715
+ }
3716
+
3717
+ createImageData(width, height) {
3718
+ if (typeof width == 'object' && width != null) {
3719
+ height = width.height;
3720
+ width = width.width;
3721
+ }
3722
+
3723
+ return createImageDataObject(width, height);
3724
+ }
3725
+
3726
+ putImageData(imageData, dx, dy, dirtyX = 0, dirtyY = 0, dirtyWidth = imageData?.width || 0, dirtyHeight = imageData?.height || 0) {
3727
+ if (!isImageDataLike(imageData))
3728
+ return;
3729
+
3730
+ dx = Number(dx);
3731
+ dy = Number(dy);
3732
+ if (!Number.isFinite(dx) || !Number.isFinite(dy))
3733
+ return;
3734
+
3735
+ let dirty = normalizeDirtyRect(imageData, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
3736
+ if (dirty == null)
3737
+ return;
3738
+
3739
+ let record = this.getTextureRecord(imageData) || (!this.ready ? {source: imageData, width: imageData.width, height: imageData.height, bindGroup: null} : null);
3740
+ if (record == null)
3741
+ return;
3742
+
3743
+ let prevComposite = this.state.globalCompositeOperation;
3744
+ let prevAlpha = this.state.globalAlpha;
3745
+ this.state.globalCompositeOperation = 'copy';
3746
+ this.state.globalAlpha = 1;
3747
+ this.addImageRect(record, dirty.x, dirty.y, dirty.w, dirty.h, dx + dirty.x, dy + dirty.y, dirty.w, dirty.h, null, true);
3748
+ this.state.globalCompositeOperation = prevComposite;
3749
+ this.state.globalAlpha = prevAlpha;
3750
+ }
3751
+
3752
+ drawImage(source, ...args) {
3753
+ let sw0 = sourceWidth(source);
3754
+ let sh0 = sourceHeight(source);
3755
+ if (sw0 <= 0 || sh0 <= 0)
3756
+ return;
3757
+
3758
+ let sx = 0;
3759
+ let sy = 0;
3760
+ let sw = sw0;
3761
+ let sh = sh0;
3762
+ let dx = 0;
3763
+ let dy = 0;
3764
+ let dw = sw0;
3765
+ let dh = sh0;
3766
+
3767
+ if (args.length == 2)
3768
+ [dx, dy] = args;
3769
+ else if (args.length == 4)
3770
+ [dx, dy, dw, dh] = args;
3771
+ else if (args.length >= 8)
3772
+ [sx, sy, sw, sh, dx, dy, dw, dh] = args;
3773
+ else
3774
+ return;
3775
+
3776
+ let record = this.getTextureRecord(source) || (!this.ready ? {source, width: sw0, height: sh0, bindGroup: null} : null);
3777
+ if (record == null)
3778
+ return;
3779
+
3780
+ let rect = normalizeImageRect(record, sx, sy, sw, sh, dx, dy, dw, dh);
3781
+ if (rect == null)
3782
+ return;
3783
+ ({sx, sy, sw, sh, dx, dy, dw, dh} = rect);
3784
+
3785
+ if (hasActiveShadow(this.state)) {
3786
+ let shadowPath = new GPUPath();
3787
+ shadowPath.rect(dx, dy, dw, dh);
3788
+ this.enqueuePathShadow(shadowPath, 'nonzero');
3789
+ }
3790
+
3791
+ this.addImageRect(record, sx, sy, sw, sh, dx, dy, dw, dh, this.state.clipRegions, false);
3792
+ }
3793
+
3794
+ addImageRect(record, sx, sy, sw, sh, dx, dy, dw, dh, clipRegions, alreadyTransformed) {
3795
+ if (record == null)
3796
+ return;
3797
+
3798
+ let rect = normalizeImageRect(record, sx, sy, sw, sh, dx, dy, dw, dh);
3799
+ if (rect == null)
3800
+ return;
3801
+
3802
+ ({sx, sy, sw, sh, dx, dy, dw, dh} = rect);
3803
+ let m = alreadyTransformed ? identity() : this.state.transform;
3804
+ let p0 = transformPoint(m, dx, dy);
3805
+ let p1 = transformPoint(m, dx + dw, dy);
3806
+ let p2 = transformPoint(m, dx, dy + dh);
3807
+ let p3 = transformPoint(m, dx + dw, dy + dh);
3808
+ let u0 = sx / record.width;
3809
+ let v0 = sy / record.height;
3810
+ let u1 = (sx + sw) / record.width;
3811
+ let v1 = (sy + sh) / record.height;
3812
+ return this.addImageQuadGeometry(record, p0, p1, p2, p3, clipRegions, u0, v0, u1, v1);
3813
+ }
3814
+
3815
+ addImageQuadGeometry(record, p0, p1, p2, p3, clipRegions, u0 = 0, v0 = 0, u1 = 1, v1 = 1) {
3816
+ if (record == null)
3817
+ return;
3818
+
3819
+ let alpha = clamp01(this.state.globalAlpha);
3820
+ if (alpha <= 0 && this.state.globalCompositeOperation != 'destination-out')
3821
+ return;
3822
+
3823
+ let vertices = [];
3824
+
3825
+ addImageQuad(vertices,
3826
+ makeImageVertex(p0[0], p0[1], u0, v0, alpha),
3827
+ makeImageVertex(p1[0], p1[1], u1, v0, alpha),
3828
+ makeImageVertex(p2[0], p2[1], u0, v1, alpha),
3829
+ makeImageVertex(p3[0], p3[1], u1, v1, alpha),
3830
+ );
3831
+
3832
+ vertices = clipImageVertices(vertices, clipRegions);
3833
+ if (vertices.length == 0)
3834
+ return;
3835
+
3836
+ this.frameCommandsReleased = false;
3837
+ this.memoryTrimToken++;
3838
+ this.commands.push({
3839
+ kind: 'image',
3840
+ vertices,
3841
+ source: record.source,
3842
+ bindGroup: record.bindGroup,
3843
+ composite: this.state.globalCompositeOperation,
3844
+ });
3845
+ }
3846
+
3847
+ createPattern(source, repetition = 'repeat') {
3848
+ if (source == null)
3849
+ return null;
3850
+ return makePattern(source, repetition);
3851
+ }
3852
+
3853
+ fillRect(x, y, w, h) {
3854
+ if (isPatternPaint(this.state.fillStyle)) {
3855
+ let path = new GPUPath();
3856
+ path.rect(x, y, w, h);
3857
+ this.fill(path);
3858
+ return;
3859
+ }
3860
+
3861
+ let color = this.parseColor(this.state.fillStyle, this.state.globalAlpha);
3862
+ if (color[3] <= 0)
3863
+ return;
3864
+
3865
+ let vertices = [];
3866
+ let region = rectToPolygon({x, y, w, h}, this.state.transform);
3867
+ pushPaintedRegion(vertices, region, color);
3868
+
3869
+ this.enqueueShadowVertices(vertices);
3870
+ this.addGeometry(vertices);
3871
+ }
3872
+
3873
+ strokeRect(x, y, w, h) {
3874
+ let path = new GPUPath();
3875
+ path.rect(x, y, w, h);
3876
+ this.stroke(path);
3877
+ }
3878
+
3879
+ addGeometry(vertices, kind = 'draw') {
3880
+ if (vertices.length == 0)
3881
+ return;
3882
+
3883
+ vertices = clipVertices(vertices, this.state.clipRegions);
3884
+ if (vertices.length == 0)
3885
+ return;
3886
+
3887
+ this.frameCommandsReleased = false;
3888
+ this.memoryTrimToken++;
3889
+ let composite = kind == 'clear' ? 'copy' : this.state.globalCompositeOperation;
3890
+ let prev = this.commands[this.commands.length - 1];
3891
+
3892
+ if (kind != 'clear' && prev != null && prev.kind != 'image' && prev.kind == kind && prev.composite == composite && vertices.length <= 4096 && prev.vertices.length <= 262144)
3893
+ appendNumericArray(prev.vertices, vertices);
3894
+ else
3895
+ this.commands.push({vertices, kind, composite});
3896
+ }
3897
+
3898
+ isPointInPath(path, x, y, fillRule = 'nonzero') {
3899
+ if (typeof path == 'number') {
3900
+ fillRule = y || 'nonzero';
3901
+ y = x;
3902
+ x = path;
3903
+ path = this.path;
3904
+ }
3905
+ else if (typeof path == 'string') {
3906
+ fillRule = path;
3907
+ path = this.path;
3908
+ }
3909
+
3910
+ x = Number(x);
3911
+ y = Number(y);
3912
+ if (!Number.isFinite(x) || !Number.isFinite(y))
3913
+ return false;
3914
+
3915
+ let point = [x, y];
3916
+ let polygons = (path?.toSubpaths?.() || []).map(sub => sub.map(p => transformPoint(this.state.transform, p[0], p[1])));
3917
+ return fillContainsPoint(point, polygons, fillRule);
3918
+ }
3919
+
3920
+ isPointInStroke(path, x, y) {
3921
+ if (typeof path == 'number') {
3922
+ y = x;
3923
+ x = path;
3924
+ path = this.path;
3925
+ }
3926
+
3927
+ x = Number(x);
3928
+ y = Number(y);
3929
+ if (!Number.isFinite(x) || !Number.isFinite(y))
3930
+ return false;
3931
+
3932
+ let m = this.state.transform;
3933
+ let width = Math.max(0.01, this.state.lineWidth * transformScale(m));
3934
+ let half = width / 2;
3935
+ let px = x;
3936
+ let py = y;
3937
+
3938
+ for (let sub of path?.toSubpaths?.() || []) {
3939
+ let pts = sub.map(p => transformPoint(m, p[0], p[1]));
3940
+ for (let i = 1; i < pts.length; i++) {
3941
+ let a = pts[i - 1];
3942
+ let b = pts[i];
3943
+ let dx = b[0] - a[0];
3944
+ let dy = b[1] - a[1];
3945
+ let len2 = dx * dx + dy * dy || 1;
3946
+ let t = clamp01(((px - a[0]) * dx + (py - a[1]) * dy) / len2);
3947
+ let qx = a[0] + dx * t;
3948
+ let qy = a[1] + dy * t;
3949
+ if (Math.hypot(px - qx, py - qy) <= half + EPS)
3950
+ return true;
3951
+ }
3952
+ }
3953
+
3954
+ return false;
3955
+ }
3956
+
3957
+ strokeText(text, x, y, maxWidth) {
3958
+ this.drawText(text, x, y, true, maxWidth);
3959
+ }
3960
+
3961
+ fillText(text, x, y, maxWidth) {
3962
+ this.drawText(text, x, y, false, maxWidth);
3963
+ }
3964
+
3965
+ drawText(text, x, y, stroke, maxWidth) {
3966
+ if (this.textLayer == null || text == null)
3967
+ return;
3968
+
3969
+ if (!Number.isFinite(x) || !Number.isFinite(y))
3970
+ return;
3971
+
3972
+ let [tx, ty] = transformPoint(this.state.transform, x, y);
3973
+
3974
+ if (!pointInClipRegions([tx, ty], this.state.clipRegions))
3975
+ return;
3976
+
3977
+ let origin = this.textOrigin(this.state.textAlign, this.state.textBaseline, this.state.direction);
3978
+ let div = document.createElement('div');
3979
+ let paint = stroke ? this.state.strokeStyle : this.state.fillStyle;
3980
+ div.textContent = text;
3981
+ div.style.position = 'absolute';
3982
+ div.style.left = (tx / this.pixelRatio) + 'px';
3983
+ div.style.top = (ty / this.pixelRatio) + 'px';
3984
+ div.dataset.uplotX = String(tx);
3985
+ div.dataset.uplotY = String(ty);
3986
+ div.style.color = cssColor(paint, tx, ty, this.state.globalAlpha);
3987
+ div.style.font = cssFont(this.state.font, this.pixelRatio);
3988
+ div.style.whiteSpace = 'pre';
3989
+ div.style.transformOrigin = '0 0';
3990
+ div.style.opacity = typeof paint == 'object' ? '1' : String(this.state.globalAlpha);
3991
+ div.style.lineHeight = 'normal';
3992
+ div.style.willChange = 'transform';
3993
+ div.style.userSelect = 'none';
3994
+ div.style.direction = this.state.direction;
3995
+ div.style.filter = filterStyleValue(this.state.filter);
3996
+ div.style.fontKerning = this.state.fontKerning;
3997
+ div.style.fontStretch = this.state.fontStretch;
3998
+ div.style.fontVariantCaps = this.state.fontVariantCaps;
3999
+ div.style.letterSpacing = this.state.letterSpacing;
4000
+ div.style.textRendering = this.state.textRendering;
4001
+ div.style.wordSpacing = this.state.wordSpacing;
4002
+
4003
+ if (stroke) {
4004
+ div.style.webkitTextFillColor = 'transparent';
4005
+ div.style.webkitTextStrokeColor = cssColor(this.state.strokeStyle, tx, ty, this.state.globalAlpha);
4006
+ div.style.webkitTextStrokeWidth = Math.max(1, this.state.lineWidth / this.pixelRatio) + 'px';
4007
+ }
4008
+
4009
+ if (hasActiveShadow(this.state)) {
4010
+ let [sdx, sdy] = this.shadowOffsetVector();
4011
+ div.style.textShadow = `${sdx / this.pixelRatio}px ${sdy / this.pixelRatio}px ${Math.max(0, this.state.shadowBlur || 0) / this.pixelRatio}px ${cssColor(this.state.shadowColor, tx, ty, this.state.globalAlpha)}`;
4012
+ }
4013
+
4014
+ let [a, b, c, d] = this.state.transform;
4015
+ let linear = Math.abs(a - 1) > 1e-6 || Math.abs(b) > 1e-6 || Math.abs(c) > 1e-6 || Math.abs(d - 1) > 1e-6
4016
+ ? ` matrix(${a}, ${b}, ${c}, ${d}, 0, 0)`
4017
+ : '';
4018
+ let maxScale = '';
4019
+ maxWidth = Number(maxWidth);
4020
+ if (Number.isFinite(maxWidth) && maxWidth > 0) {
4021
+ let metrics = this.measureText(text);
4022
+ if (metrics.width > maxWidth)
4023
+ maxScale = ` scaleX(${maxWidth / metrics.width})`;
4024
+ }
4025
+ div.style.transform = `translate(${origin.x}, ${origin.y})${linear}${maxScale}`;
4026
+
4027
+ this.textLayer.appendChild(div);
4028
+ }
4029
+
4030
+ textOrigin(align, baseline, direction = 'inherit') {
4031
+ let rtl = direction == 'rtl' || direction == 'inherit' && typeof document != 'undefined' && document.dir == 'rtl';
4032
+ let logicalAlign = align == 'start' ? (rtl ? 'right' : 'left') : align == 'end' ? (rtl ? 'left' : 'right') : align;
4033
+ let x = logicalAlign == 'center' ? '-50%' : logicalAlign == 'right' ? '-100%' : '0';
4034
+ let y = '0';
4035
+
4036
+ if (baseline == 'middle')
4037
+ y = '-50%';
4038
+ else if (baseline == 'bottom')
4039
+ y = '-100%';
4040
+ else if (baseline == 'alphabetic')
4041
+ y = '-0.78em';
4042
+ else if (baseline == 'ideographic')
4043
+ y = '-0.9em';
4044
+ else if (baseline == 'hanging')
4045
+ y = '-0.2em';
4046
+
4047
+ return {x, y};
4048
+ }
4049
+
4050
+ measureText(text) {
4051
+ let key = [this.state.font, this.state.fontKerning, this.state.fontStretch, this.state.fontVariantCaps, this.state.letterSpacing, this.state.textRendering, this.state.wordSpacing, text].join('\n');
4052
+ let width = this.textMeasureCache.get(key);
4053
+
4054
+ if (width == null) {
4055
+ width = measureTextWithDom(this.state.font, text, this.state);
4056
+
4057
+ if (width == null) {
4058
+ let size = extractFontPx(this.state.font);
4059
+ width = String(text).length * size * 0.56;
4060
+ }
4061
+
4062
+ if (this.textMeasureCache.size > 2000)
4063
+ this.textMeasureCache.clear();
4064
+ this.textMeasureCache.set(key, width);
4065
+ }
4066
+
4067
+ let size = extractFontPx(this.state.font);
4068
+ return {
4069
+ width,
4070
+ actualBoundingBoxLeft: 0,
4071
+ actualBoundingBoxRight: width,
4072
+ actualBoundingBoxAscent: size * 0.8,
4073
+ actualBoundingBoxDescent: size * 0.2,
4074
+ fontBoundingBoxAscent: size,
4075
+ fontBoundingBoxDescent: size * 0.25,
4076
+ emHeightAscent: size * 0.8,
4077
+ emHeightDescent: size * 0.2,
4078
+ };
4079
+ }
4080
+
4081
+ destroy() {
4082
+ this.disposed = true;
4083
+ this.initEpoch++;
4084
+ this.ready = false;
4085
+ this.context?.unconfigure?.();
4086
+ this.releaseGPUResources();
4087
+ this.imageBindGroupLayout = null;
4088
+ this.bindGroupLayout = null;
4089
+ this.pipelineLayout = null;
4090
+ this.context = null;
4091
+ this.device = null;
4092
+ this.adapter = null;
4093
+ this.textLayer?.remove?.();
4094
+ this.textLayer = null;
4095
+ }
4096
+
4097
+ parseColor(str, alpha = 1) {
4098
+ if (str == null || str === 'transparent')
4099
+ return TRANSPARENT;
4100
+
4101
+ if (typeof str == 'object' && str.colorAt) {
4102
+ return {
4103
+ colorAt(x, y) {
4104
+ return str.colorAt(x, y, alpha);
4105
+ },
4106
+ };
4107
+ }
4108
+
4109
+ let key = colorKey(str, alpha);
4110
+ let cached = this.colorCache.get(key);
4111
+ if (cached != null)
4112
+ return cached;
4113
+
4114
+ let color = parsePlainCssColor(str);
4115
+ color = applyAlpha(color || DEFAULT_COLOR, alpha);
4116
+ if (this.colorCache.size > this.memory.maxColorCacheEntries)
4117
+ this.colorCache.clear();
4118
+ this.colorCache.set(key, color);
4119
+ return color;
4120
+ }
4121
+
4122
+ createLinearGradient(x0, y0, x1, y1) {
4123
+ let a = transformPoint(this.state.transform, x0, y0);
4124
+ let b = transformPoint(this.state.transform, x1, y1);
4125
+ return makeLinearGradient(a[0], a[1], b[0], b[1]);
4126
+ }
4127
+
4128
+ createRadialGradient(x0, y0, r0, x1, y1, r1) {
4129
+ let a = transformPoint(this.state.transform, x0, y0);
4130
+ let b = transformPoint(this.state.transform, x1, y1);
4131
+ let scale = transformScale(this.state.transform);
4132
+ return makeRadialGradient(a[0], a[1], r0 * scale, b[0], b[1], r1 * scale);
4133
+ }
4134
+
4135
+ createConicGradient(startAngle, x, y) {
4136
+ let c = transformPoint(this.state.transform, x, y);
4137
+ let stops = [];
4138
+ return {
4139
+ addColorStop(offset, color) {
4140
+ stops.push({offset, color});
4141
+ },
4142
+ colorAt(px, py, alpha = 1) {
4143
+ let ang = Math.atan2(py - c[1], px - c[0]) - startAngle;
4144
+ let t = ((ang / (Math.PI * 2)) % 1 + 1) % 1;
4145
+ return sampleStops(normalizeStops(stops), t, alpha);
4146
+ },
4147
+ };
4148
+ }
4149
+
4150
+ get strokeStyle() { return this.state.strokeStyle; }
4151
+ set strokeStyle(v) { this.state.strokeStyle = v; }
4152
+ get fillStyle() { return this.state.fillStyle; }
4153
+ set fillStyle(v) { this.state.fillStyle = v; }
4154
+ get lineWidth() { return this.state.lineWidth; }
4155
+ set lineWidth(v) {
4156
+ v = Number(v);
4157
+ if (Number.isFinite(v) && v >= 0)
4158
+ this.state.lineWidth = v;
4159
+ }
4160
+ get lineJoin() { return this.state.lineJoin; }
4161
+ set lineJoin(v) { this.state.lineJoin = validEnum(v, ['round', 'bevel', 'miter'], this.state.lineJoin); }
4162
+ get lineCap() { return this.state.lineCap; }
4163
+ set lineCap(v) { this.state.lineCap = validEnum(v, ['butt', 'round', 'square'], this.state.lineCap); }
4164
+ get font() { return this.state.font; }
4165
+ set font(v) { if (v != null && String(v).trim() !== '') this.state.font = String(v); }
4166
+ get textAlign() { return this.state.textAlign; }
4167
+ set textAlign(v) { this.state.textAlign = validEnum(v, ['start', 'end', 'left', 'right', 'center'], this.state.textAlign); }
4168
+ get textBaseline() { return this.state.textBaseline; }
4169
+ set textBaseline(v) { this.state.textBaseline = validEnum(v, ['top', 'hanging', 'middle', 'alphabetic', 'ideographic', 'bottom'], this.state.textBaseline); }
4170
+ get globalAlpha() { return this.state.globalAlpha; }
4171
+ set globalAlpha(v) {
4172
+ v = Number(v);
4173
+ if (Number.isFinite(v))
4174
+ this.state.globalAlpha = clamp01(v);
4175
+ }
4176
+ get lineDashOffset() { return this.state.lineDashOffset; }
4177
+ set lineDashOffset(v) {
4178
+ v = Number(v);
4179
+ this.state.lineDashOffset = Number.isFinite(v) ? v : 0;
4180
+ }
4181
+ get miterLimit() { return this.state.miterLimit; }
4182
+ set miterLimit(v) {
4183
+ v = Number(v);
4184
+ if (Number.isFinite(v) && v > 0)
4185
+ this.state.miterLimit = v;
4186
+ }
4187
+ get imageSmoothingEnabled() { return this.state.imageSmoothingEnabled; }
4188
+ set imageSmoothingEnabled(v) {
4189
+ this.state.imageSmoothingEnabled = !!v;
4190
+ if (this.runtime != null)
4191
+ this.sampler = getRuntimeSampler(this.runtime, this.state.imageSmoothingEnabled, this.state.imageSmoothingQuality);
4192
+ }
4193
+ get imageSmoothingQuality() { return this.state.imageSmoothingQuality; }
4194
+ set imageSmoothingQuality(v) {
4195
+ this.state.imageSmoothingQuality = normalizeImageSmoothingQuality(v);
4196
+ if (this.runtime != null)
4197
+ this.sampler = getRuntimeSampler(this.runtime, this.state.imageSmoothingEnabled, this.state.imageSmoothingQuality);
4198
+ }
4199
+ get fontKerning() { return this.state.fontKerning; }
4200
+ set fontKerning(v) { this.state.fontKerning = v == null ? 'auto' : String(v); }
4201
+ get fontStretch() { return this.state.fontStretch; }
4202
+ set fontStretch(v) { this.state.fontStretch = v == null ? 'normal' : String(v); }
4203
+ get fontVariantCaps() { return this.state.fontVariantCaps; }
4204
+ set fontVariantCaps(v) { this.state.fontVariantCaps = v == null ? 'normal' : String(v); }
4205
+ get letterSpacing() { return this.state.letterSpacing; }
4206
+ set letterSpacing(v) { this.state.letterSpacing = v == null ? '0px' : String(v); }
4207
+ get textRendering() { return this.state.textRendering; }
4208
+ set textRendering(v) { this.state.textRendering = v == null ? 'auto' : String(v); }
4209
+ get wordSpacing() { return this.state.wordSpacing; }
4210
+ set wordSpacing(v) { this.state.wordSpacing = v == null ? '0px' : String(v); }
4211
+ get globalCompositeOperation() { return this.state.globalCompositeOperation; }
4212
+ set globalCompositeOperation(v) { this.state.globalCompositeOperation = clampCompositeMode(v); }
4213
+ get shadowColor() { return this.state.shadowColor; }
4214
+ set shadowColor(v) { this.state.shadowColor = v; }
4215
+ get shadowBlur() { return this.state.shadowBlur; }
4216
+ set shadowBlur(v) {
4217
+ v = Number(v);
4218
+ this.state.shadowBlur = Number.isFinite(v) && v > 0 ? v : 0;
4219
+ }
4220
+ get shadowOffsetX() { return this.state.shadowOffsetX; }
4221
+ set shadowOffsetX(v) {
4222
+ v = Number(v);
4223
+ this.state.shadowOffsetX = Number.isFinite(v) ? v : 0;
4224
+ }
4225
+ get shadowOffsetY() { return this.state.shadowOffsetY; }
4226
+ set shadowOffsetY(v) {
4227
+ v = Number(v);
4228
+ this.state.shadowOffsetY = Number.isFinite(v) ? v : 0;
4229
+ }
4230
+ get direction() { return this.state.direction; }
4231
+ set direction(v) { this.state.direction = v || 'inherit'; }
4232
+ get filter() { return this.state.filter; }
4233
+ set filter(v) { this.state.filter = filterStyleValue(v); }
4234
+ }
4235
+
4236
+ export const WebGPURendererInternals = {
4237
+ blendForComposite,
4238
+ clampCompositeMode,
4239
+ clipVertices,
4240
+ fillContainsPoint,
4241
+ normalizeDirtyRect,
4242
+ normalizeImageRect,
4243
+ normalizeReadRect,
4244
+ alignBytesPerRow,
4245
+ getSharedRuntimeStats: () => WebGPURenderer.getSharedRuntimeStats(),
4246
+ setSharedRuntimeEnabled: enabled => WebGPURenderer.setSharedRuntimeEnabled(enabled),
4247
+ getLastFrameStats: renderer => renderer?.getLastFrameStats?.() || null,
4248
+ getMemoryStats: renderer => renderer?.getMemoryStats?.() || null,
4249
+ trimMemory: renderer => renderer?.trimMemory?.() || null,
4250
+ parsePlainCssColor,
4251
+ pathToClipRegions,
4252
+ pointInPolygon,
4253
+ triangulate,
4254
+ windingNumber,
4255
+ };
4256
+
4257
+ export default WebGPURenderer;