@weasel-js/renderer 1.9.4 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,16 +1,28 @@
1
- import { ownTextureIds, rasterizeTile, drawTileShapes, tileGeometry, tileSize, registerTextureAs, textureRegistry, unregisterTexture } from './chunk-ORBXHIRB.js';
1
+ import { ownTextureIds, rasterizeTile, drawTileShapes, tileGeometry, tileSize, registerTextureAs, textureRegistry, unregisterTexture } from './chunk-Q4SIHV7Q.js';
2
+ import { defaultOpenFill, pathFromD, boundsOfPath } from '@weasel-js/geom';
2
3
  import { tessellate } from '@weasel-js/geom/tessellate';
3
- import { createReflectable } from '@weasel-js/registry';
4
+ import { createReflectable, createEmitter } from '@weasel-js/registry';
4
5
  import { resolveStrokeWidth, strokeInsets, tessellateStroke, markerGeneration, screenSpaceKey, markerHeads, metricKey, getMarker, markerKeyOf, markerReadsKey } from '@weasel-js/stroke';
5
- import { pathFromD, boundsOfPath } from '@weasel-js/geom';
6
+ import { isTileKind, isPatternPaint, tileSpecOf, isTilePaint, patternPlacementOf } from '@weasel-js/paint';
6
7
  import { GLYPH_COVERAGE_GLSL } from '@weasel-js/font';
7
8
 
9
+ // src/cache/uploadPremultiplied.ts
10
+ function uploadPremultiplied(gl, source) {
11
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
12
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
13
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
14
+ }
15
+
8
16
  // src/cache/GLImageCache.ts
9
17
  var DEFAULT_MAX_TEXTURE_SIDE = 4096;
10
18
  var live = /* @__PURE__ */ new Set();
11
19
  function releaseImageSource(key) {
12
20
  for (const cache7 of live) cache7.release(key);
13
21
  }
22
+ function imageSourceInLatestFrame(key) {
23
+ for (const cache7 of live) if (cache7.drewInLatestFrame(key)) return true;
24
+ return false;
25
+ }
14
26
  function maxImageTextureSide() {
15
27
  let side = Infinity;
16
28
  for (const cache7 of live) side = Math.min(side, cache7.maxTextureSide);
@@ -18,11 +30,10 @@ function maxImageTextureSide() {
18
30
  }
19
31
  var GLImageCache = class {
20
32
  /** `minification` selects the MIN_FILTER strategy for uploaded textures.
21
- * `'linear'` (default) is the screen path's existing behavior. `'mipmap'`
22
- * generates mipmaps and filters LINEAR_MIPMAP_LINEAR — required for
23
- * quality minification when a large source bitmap is drawn small (the
24
- * headless print/export path); bilinear-only minification undersamples
25
- * and produces moiré. */
33
+ * `'mipmap'` generates mipmaps and filters LINEAR_MIPMAP_LINEAR: when a
34
+ * large source bitmap is drawn small, bilinear-only minification
35
+ * undersamples and produces moiré. `'auto'` pays for that only on the
36
+ * textures a frame draws small, when it first does. */
26
37
  constructor(gl, minification = "linear") {
27
38
  this.gl = gl;
28
39
  this.minification = minification;
@@ -43,25 +54,29 @@ var GLImageCache = class {
43
54
  textures = /* @__PURE__ */ new Set();
44
55
  /**
45
56
  * MAG_FILTER each texture currently carries, so a redundant write can be
46
- * skipped.
47
- *
48
- * The batch sets this per flush — the same bitmap can be drawn at both
49
- * filters in one frame, and the value has to be right at the draw rather than
50
- * at upload. Filtering is state on the *texture object*, though, not on the
51
- * unit, so re-asserting a value it already has is a write to a live texture
52
- * for no reason. On a large sheet that is not free: a consumer's wall samples
53
- * a 5652px-square atlas, 122MB resident, and measured `sampling: 'nearest'`
54
- * costing up to 8x `'linear'` there — where the linear pass re-asserts the
55
- * upload default and the nearest pass changes state on every draw.
56
- *
57
- * Whether that is the cause is unproven (see `docs/TODO.md`), but the write
58
- * was redundant either way.
57
+ * skipped. The batch sets this per flush — the same bitmap can be drawn at
58
+ * both filters in one frame — but filtering is state on the *texture
59
+ * object*, not on the unit, so re-asserting a value it already has is a
60
+ * write to a live texture for no reason.
59
61
  */
60
62
  magFilters = /* @__PURE__ */ new WeakMap();
61
63
  finalizer;
62
64
  pendingDeletes = [];
63
- /** Delete the textures of sources collected since the last call. The
64
- * renderer calls this at the top of each `render()`. */
65
+ /** Under `'auto'`, the sources whose mipmaps are built. */
66
+ mipped = /* @__PURE__ */ new WeakSet();
67
+ frame = 0;
68
+ lastDrawn = /* @__PURE__ */ new WeakMap();
69
+ /** Open a frame: what {@link drewInLatestFrame} answers about from here on.
70
+ * The renderer calls this at the top of each `render()`. */
71
+ beginFrame() {
72
+ this.frame++;
73
+ this.drainPendingDeletes();
74
+ }
75
+ /** Whether `key` was uploaded or bound since the last {@link beginFrame}. */
76
+ drewInLatestFrame(key) {
77
+ return this.lastDrawn.get(key) === this.frame;
78
+ }
79
+ /** Delete the textures of sources collected since the last call. */
65
80
  drainPendingDeletes() {
66
81
  if (this.pendingDeletes.length === 0) return;
67
82
  for (const tex of this.pendingDeletes) this.gl.deleteTexture(tex);
@@ -76,6 +91,8 @@ var GLImageCache = class {
76
91
  this.finalizer.unregister(key);
77
92
  this.map.delete(key);
78
93
  this.magFilters.delete(key);
94
+ this.lastDrawn.delete(key);
95
+ this.mipped.delete(key);
79
96
  }
80
97
  /** Delete every texture this cache uploaded and stop answering
81
98
  * {@link releaseImageSource} and {@link maxImageTextureSide}. Idempotent. */
@@ -92,13 +109,14 @@ var GLImageCache = class {
92
109
  live.delete(this);
93
110
  }
94
111
  upload(key, source, repetition) {
112
+ this.lastDrawn.set(key, this.frame);
95
113
  const existing = this.map.get(key);
96
114
  if (existing) return existing;
97
115
  const gl = this.gl;
98
116
  const tex = gl.createTexture();
99
117
  if (!tex) throw new Error("GLImageCache: createTexture failed");
100
118
  gl.bindTexture(gl.TEXTURE_2D, tex);
101
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
119
+ uploadPremultiplied(gl, source);
102
120
  gl.texParameteri(
103
121
  gl.TEXTURE_2D,
104
122
  gl.TEXTURE_MIN_FILTER,
@@ -116,9 +134,27 @@ var GLImageCache = class {
116
134
  this.finalizer.register(key, tex, key);
117
135
  return tex;
118
136
  }
137
+ /** Whether `key` would gain from {@link buildMipmaps}: only under `'auto'`,
138
+ * and only until they are built. */
139
+ lacksMipmaps(key) {
140
+ return this.minification === "auto" && !this.mipped.has(key);
141
+ }
142
+ /** Build `key`'s mipmaps and filter minification through them from here
143
+ * on. Its texture must be uploaded. Leaves no texture bound. */
144
+ buildMipmaps(key) {
145
+ const tex = this.map.get(key);
146
+ if (!tex || this.mipped.has(key)) return;
147
+ const gl = this.gl;
148
+ gl.bindTexture(gl.TEXTURE_2D, tex);
149
+ gl.generateMipmap(gl.TEXTURE_2D);
150
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
151
+ gl.bindTexture(gl.TEXTURE_2D, null);
152
+ this.mipped.add(key);
153
+ }
119
154
  bind(key, unit) {
120
155
  const tex = this.map.get(key);
121
156
  if (!tex) throw new Error("GLImageCache: image not uploaded; call upload() first");
157
+ this.lastDrawn.set(key, this.frame);
122
158
  const gl = this.gl;
123
159
  gl.activeTexture(gl.TEXTURE0 + unit);
124
160
  gl.bindTexture(gl.TEXTURE_2D, tex);
@@ -146,10 +182,11 @@ function wrapModes(gl, rep) {
146
182
  }
147
183
  var cache = /* @__PURE__ */ new WeakMap();
148
184
  function getMesh(path) {
185
+ const openFill = defaultOpenFill();
149
186
  const cached = cache.get(path);
150
- if (cached !== void 0) return cached;
187
+ if (cached !== void 0 && cached.openFill === openFill) return cached.mesh;
151
188
  const mesh = tessellate(path);
152
- cache.set(path, mesh);
189
+ cache.set(path, { mesh, openFill });
153
190
  return mesh;
154
191
  }
155
192
  function _resetCacheForTests() {
@@ -298,11 +335,9 @@ function insetIn(length, head, ribbon) {
298
335
  function _resetStrokeMeshCacheForTests() {
299
336
  cache2 = /* @__PURE__ */ new WeakMap();
300
337
  }
301
-
302
- // src/fillInPoseFrame.ts
303
338
  function fillInPoseFrame(fill, box) {
304
339
  if (!isBuiltinKind(fill)) {
305
- return getPaintDrawKind(fill.fill)?.inPoseFrame?.(fill, box) ?? fill;
340
+ return getPaintDrawKind(fill.kind)?.inPoseFrame?.(fill, box) ?? fill;
306
341
  }
307
342
  if (isBoundsPattern(fill)) {
308
343
  const o = fill.origin ?? { x: 0, y: 0 };
@@ -313,10 +348,10 @@ function fillInPoseFrame(fill, box) {
313
348
  x: box.x + p.x * box.width,
314
349
  y: box.y + p.y * box.height
315
350
  });
316
- if (fill.fill === "linear-gradient") {
351
+ if (fill.kind === "linear-gradient") {
317
352
  return { ...fill, from: toBox(fill.from), to: toBox(fill.to), units: "local" };
318
353
  }
319
- if (fill.fill === "radial-gradient") {
354
+ if (fill.kind === "radial-gradient") {
320
355
  const scale = Math.hypot(box.width, box.height) / Math.SQRT2;
321
356
  return { ...fill, center: toBox(fill.center), radius: fill.radius * scale, units: "local" };
322
357
  }
@@ -324,45 +359,43 @@ function fillInPoseFrame(fill, box) {
324
359
  }
325
360
  function fillToBoundsFrame(fill, box) {
326
361
  if (!isBuiltinKind(fill)) {
327
- return getPaintDrawKind(fill.fill)?.toBoundsFrame?.(fill, box) ?? fill;
362
+ return getPaintDrawKind(fill.kind)?.toBoundsFrame?.(fill, box) ?? fill;
328
363
  }
329
- if (fill.fill === "pattern") {
364
+ if (isPatternPaint(fill)) {
330
365
  const o = fill.origin ?? { x: 0, y: 0 };
331
366
  return { ...fill, origin: { x: o.x - box.x, y: o.y - box.y }, units: "bounds" };
332
367
  }
333
- const isGradient = fill.fill === "linear-gradient" || fill.fill === "radial-gradient" || fill.fill === "conic-gradient";
368
+ const isGradient = fill.kind === "linear-gradient" || fill.kind === "radial-gradient" || fill.kind === "conic-gradient";
334
369
  if (!isGradient) return fill;
335
370
  if (box.width === 0 || box.height === 0) return fill;
336
371
  const fromBox = (p) => ({
337
372
  x: (p.x - box.x) / box.width,
338
373
  y: (p.y - box.y) / box.height
339
374
  });
340
- if (fill.fill === "linear-gradient") {
375
+ if (fill.kind === "linear-gradient") {
341
376
  return { ...fill, from: fromBox(fill.from), to: fromBox(fill.to), units: "bounds" };
342
377
  }
343
- if (fill.fill === "radial-gradient") {
378
+ if (fill.kind === "radial-gradient") {
344
379
  const scale = Math.hypot(box.width, box.height) / Math.SQRT2;
345
380
  return { ...fill, center: fromBox(fill.center), radius: fill.radius / scale, units: "bounds" };
346
381
  }
347
382
  return { ...fill, center: fromBox(fill.center), units: "bounds" };
348
383
  }
349
384
  function isBoundsPattern(fill) {
350
- return fill.fill === "pattern" && fill.units === "bounds";
385
+ return isPatternPaint(fill) && fill.units === "bounds";
351
386
  }
352
387
  function isBoundsGradient(fill) {
353
388
  if (fill === void 0) return false;
354
- const kind = fill.fill;
389
+ const kind = fill.kind;
355
390
  if (kind !== "linear-gradient" && kind !== "radial-gradient" && kind !== "conic-gradient") {
356
391
  return false;
357
392
  }
358
393
  return fill.units === "bounds";
359
394
  }
360
395
  function isBuiltinKind(fill) {
361
- const kind = fill.fill ?? "solid";
362
- return kind === "solid" || kind === "pattern" || kind === "linear-gradient" || kind === "radial-gradient" || kind === "conic-gradient";
396
+ const kind = fill.kind ?? "solid";
397
+ return kind === "solid" || isPatternPaint(fill) || kind === "linear-gradient" || kind === "radial-gradient" || kind === "conic-gradient";
363
398
  }
364
-
365
- // src/patterns/resolveSpec.ts
366
399
  var PATTERN_TILE_CACHE_LIMIT = 256;
367
400
  var ID_PREFIX = "pattern:";
368
401
  var cache3 = /* @__PURE__ */ new Map();
@@ -376,13 +409,14 @@ function _resetPatternSpecCacheForTests() {
376
409
  cache3.clear();
377
410
  }
378
411
  function resolvePatternSpec(spec) {
379
- const id = specId(spec);
412
+ const tile = tileSpecOf(spec);
413
+ const id = specId(tile);
380
414
  const hit = cache3.get(id);
381
415
  if (hit !== void 0) {
382
416
  markUsed(id);
383
417
  return hit;
384
418
  }
385
- return build(id, spec);
419
+ return build(id, tile);
386
420
  }
387
421
  function markUsed(id) {
388
422
  const hit = cache3.get(id);
@@ -391,7 +425,7 @@ function markUsed(id) {
391
425
  cache3.set(id, hit);
392
426
  }
393
427
  function build(id, spec) {
394
- const bitmap = TILES.has(spec.tile) ? rasterizeTile({
428
+ const bitmap = isTileKind(spec.kind) ? rasterizeTile({
395
429
  size: tileSize(spec),
396
430
  draw: (oc) => drawTileShapes(oc, tileGeometry(spec))
397
431
  }) : null;
@@ -401,12 +435,6 @@ function build(id, spec) {
401
435
  while (cache3.size > PATTERN_TILE_CACHE_LIMIT) evict(cache3.keys().next().value);
402
436
  return handle;
403
437
  }
404
- var TILES = /* @__PURE__ */ new Set([
405
- "hatch",
406
- "crosshatch",
407
- "dots",
408
- "chunks"
409
- ]);
410
438
  function evict(id) {
411
439
  cache3.delete(id);
412
440
  const source = textureRegistry.get(id)?.source;
@@ -420,27 +448,29 @@ function revive(id) {
420
448
  } catch {
421
449
  return;
422
450
  }
423
- if (typeof spec !== "object" || spec === null || !("tile" in spec)) return;
451
+ if (typeof spec !== "object" || spec === null || !("kind" in spec)) return;
424
452
  build(id, spec);
425
453
  }
426
454
  ownTextureIds(ID_PREFIX, { revive, touched: markUsed });
427
- function isPatternSpec(payload) {
428
- return "tile" in payload;
429
- }
430
455
  function resolveFillPattern(fill) {
431
- if (fill.fill !== "pattern") return fill;
432
- if (!isPatternSpec(fill.pattern)) return fill;
433
- const handle = resolvePatternSpec(fill.pattern);
434
- return handle ? { ...fill, pattern: handle } : null;
456
+ if (!isTilePaint(fill)) return fill;
457
+ const texture = resolvePatternSpec(fill);
458
+ return texture ? { kind: "texture", texture, ...patternPlacementOf(fill) } : null;
435
459
  }
436
460
 
437
461
  // src/patterns/commandPaint.ts
462
+ function commandPaint(paint, box) {
463
+ return resolveFillPattern(fillInPoseFrame(paint, box));
464
+ }
438
465
  function commandFill(fill, box) {
439
- return resolveFillPattern(fillInPoseFrame(fill, box));
466
+ if (fill.paint == null) return null;
467
+ const paint = commandPaint(fill.paint, box);
468
+ if (paint === null) return null;
469
+ return paint === fill.paint ? fill : { ...fill, paint };
440
470
  }
441
471
  function commandStroke(stroke, box) {
442
472
  if (stroke.paint == null) return null;
443
- const paint = commandFill(stroke.paint, box);
473
+ const paint = commandPaint(stroke.paint, box);
444
474
  if (paint === null) return null;
445
475
  return paint === stroke.paint ? stroke : { ...stroke, paint };
446
476
  }
@@ -448,7 +478,7 @@ function resolvePaint(p, stroke, fallback, head) {
448
478
  const v = p ?? fallback;
449
479
  if (v === "none") return void 0;
450
480
  if (v === "line") return stroke.paint;
451
- return commandFill(v, boundsOfPath(head)) ?? void 0;
481
+ return commandPaint(v, boundsOfPath(head)) ?? void 0;
452
482
  }
453
483
  function headCommands(heads, stroke) {
454
484
  const out = [];
@@ -460,7 +490,7 @@ function headCommands(heads, stroke) {
460
490
  out.push({
461
491
  kind: "path",
462
492
  path,
463
- ...fill ? { fill } : {},
493
+ ...fill ? { fill: { paint: fill } } : {},
464
494
  ...outline && outline.paint ? { stroke: { paint: outline.paint, width: outline.width, cap: "round", join: "round" } } : {}
465
495
  });
466
496
  }
@@ -477,14 +507,14 @@ function readsKey(stroke) {
477
507
  key += "/";
478
508
  continue;
479
509
  }
480
- const reads = markerReadsKey(entry, stroke);
481
- if (reads === null) return null;
482
- key += `${reads}/`;
510
+ const reads2 = markerReadsKey(entry, stroke);
511
+ if (reads2 === null) return null;
512
+ key += `${reads2}/`;
483
513
  }
484
514
  return key;
485
515
  }
486
- function mapFor(path, source, reads) {
487
- if (reads !== null) {
516
+ function mapFor(path, source, reads2) {
517
+ if (reads2 !== null) {
488
518
  let map2 = byValue.get(path);
489
519
  if (map2 === void 0) byValue.set(path, map2 = /* @__PURE__ */ new Map());
490
520
  return map2;
@@ -499,8 +529,8 @@ function cachedMarkerCommands(path, source, stroke, strokeWidth, flattenToleranc
499
529
  if (stroke.markerStart === void 0 && stroke.markerMid === void 0 && stroke.markerEnd === void 0) {
500
530
  return [];
501
531
  }
502
- const reads = readsKey(stroke);
503
- const map = mapFor(path, source, reads);
532
+ const reads2 = readsKey(stroke);
533
+ const map = mapFor(path, source, reads2);
504
534
  const key = [
505
535
  strokeWidth,
506
536
  flattenTolerance ?? "",
@@ -508,7 +538,7 @@ function cachedMarkerCommands(path, source, stroke, strokeWidth, flattenToleranc
508
538
  refKey(stroke.markerStart),
509
539
  refKey(stroke.markerMid),
510
540
  refKey(stroke.markerEnd),
511
- reads ?? "",
541
+ reads2 ?? "",
512
542
  screenSpaceKey(spaces.start),
513
543
  screenSpaceKey(spaces.mid),
514
544
  screenSpaceKey(spaces.end)
@@ -575,15 +605,35 @@ function outlineStrokeMesh(glyphKey, d, emWidth, stroke, metric) {
575
605
  function _resetOutlineStrokeMeshCacheForTests() {
576
606
  cache5 = /* @__PURE__ */ new Map();
577
607
  }
578
- var BATCH_TEXTURE_SLOTS = 8;
608
+
609
+ // src/shaders/straightRgb.ts
610
+ var STRAIGHT_RGB_GLSL = (
611
+ /* glsl */
612
+ `
613
+ vec3 straightRgb(vec4 premultiplied) {
614
+ return premultiplied.rgb / max(premultiplied.a, 0.0001);
615
+ }
616
+ `
617
+ );
618
+
619
+ // src/shaders/batchFill.ts
620
+ var BATCH_TEXTURE_SLOTS = 16;
579
621
  var WHITE_SLOT = 0;
622
+ var RAMP_SLOT = BATCH_TEXTURE_SLOTS - 1;
623
+ var BATCH_MATRIX_SLOTS = 8;
624
+ var PAINT_MODES = 8;
625
+ var MATRIX_STRIDE = BATCH_TEXTURE_SLOTS * PAINT_MODES;
580
626
  var PAINT_MODE_PLAIN = 0;
581
627
  var PAINT_MODE_RADIAL = 3;
582
628
  var PAINT_MODE_CONIC = 4;
629
+ var PAINT_MODE_IMAGE = 5;
583
630
  var INV_TAU = (1 / (2 * Math.PI)).toFixed(10);
584
631
  function packSlot(slot, mode) {
585
632
  return slot + BATCH_TEXTURE_SLOTS * mode;
586
633
  }
634
+ function withMatrix(slot, matrix) {
635
+ return slot + MATRIX_STRIDE * matrix;
636
+ }
587
637
  var BATCH_ATTRIBUTE_LOCATIONS = {
588
638
  a_position: 0,
589
639
  a_vertexColor: 1,
@@ -592,12 +642,16 @@ var BATCH_ATTRIBUTE_LOCATIONS = {
592
642
  a_texSlot: 4
593
643
  };
594
644
  var L = BATCH_ATTRIBUTE_LOCATIONS;
595
- function sampleChain(slots) {
596
- const arms = [];
597
- for (let i = 1; i < slots; i++) {
598
- arms.push(` if (slot == ${i}) return texture(u_samplers[${i}], uv);`);
599
- }
600
- return arms.join("\n");
645
+ function sampleTree(lo, hi, indent = " ") {
646
+ if (lo === hi) return `${indent}return texture(u_samplers[${lo}], uv);`;
647
+ const mid = lo + hi + 1 >> 1;
648
+ return [
649
+ `${indent}if (slot < ${mid}) {`,
650
+ sampleTree(lo, mid - 1, `${indent} `),
651
+ `${indent}} else {`,
652
+ sampleTree(mid, hi, `${indent} `),
653
+ `${indent}}`
654
+ ].join("\n");
601
655
  }
602
656
  var BATCH_VERT_SRC = (
603
657
  /* glsl */
@@ -614,6 +668,7 @@ out vec2 v_uv;
614
668
  out float v_post;
615
669
  flat out int v_texSlot;
616
670
  flat out float v_paintMode;
671
+ flat out int v_matrix;
617
672
  void main() {
618
673
  vec3 screen = u_model * vec3(a_position, 1.0);
619
674
  vec3 clip = u_proj * vec3(screen.xy, 1.0);
@@ -621,38 +676,44 @@ void main() {
621
676
  v_vertexColor = a_vertexColor;
622
677
  v_uv = a_uv;
623
678
  v_post = a_post;
624
- // slot + 8 * mode, unpacked \u2014 see the file header for why they share a float.
679
+ // slot + 16 * mode + 128 * matrix, unpacked \u2014 see the file header for why
680
+ // they share a float.
625
681
  int packed = int(a_texSlot + 0.5);
626
682
  v_texSlot = packed % ${BATCH_TEXTURE_SLOTS};
627
- v_paintMode = float(packed / ${BATCH_TEXTURE_SLOTS});
683
+ v_paintMode = float((packed / ${BATCH_TEXTURE_SLOTS}) % ${PAINT_MODES});
684
+ v_matrix = packed / ${MATRIX_STRIDE};
628
685
  }
629
686
  `
630
687
  );
631
- var BATCH_FRAG_SRC = (
632
- /* glsl */
633
- `#version 300 es
688
+ function batchFragSrc(matrices) {
689
+ const m = matrices === 1 ? "0" : "v_matrix";
690
+ return (
691
+ /* glsl */
692
+ `#version 300 es
634
693
  precision highp float;
635
694
  in vec4 v_vertexColor;
636
695
  in vec2 v_uv;
637
696
  in float v_post;
638
697
  flat in int v_texSlot;
639
698
  flat in float v_paintMode;
699
+ flat in int v_matrix;
640
700
  uniform sampler2D u_samplers[${BATCH_TEXTURE_SLOTS}];
641
701
  uniform vec4 u_color;
642
- uniform float u_alpha;
643
- uniform float u_synthBold;
702
+ uniform float u_alpha[${matrices}];
644
703
  uniform vec2 u_fieldScale[${BATCH_TEXTURE_SLOTS}];
645
- uniform mat4 u_colorMatrix;
646
- uniform vec4 u_colorBias;
704
+ uniform mat4 u_colorMatrix[${matrices}];
705
+ uniform vec4 u_colorBias[${matrices}];
647
706
  out vec4 outColor;
648
707
 
649
708
  vec4 sampleSlot(int slot, vec2 uv) {
650
- ${sampleChain(BATCH_TEXTURE_SLOTS)}
651
- return texture(u_samplers[0], uv);
709
+ ${sampleTree(0, BATCH_TEXTURE_SLOTS - 1)}
710
+ // Never reached: every slot returns above.
711
+ return vec4(0.0);
652
712
  }
653
713
  ${GLYPH_COVERAGE_GLSL}
714
+ ${STRAIGHT_RGB_GLSL}
654
715
  void main() {
655
- float isGrad = step(2.5, v_paintMode);
716
+ float isGrad = step(2.5, v_paintMode) * step(v_paintMode, ${PAINT_MODE_CONIC + 0.5});
656
717
  // A radial or conic vertex carries a gradient-space coordinate rather than a
657
718
  // texture coordinate, and its atlas row rather than an alpha. The sample is
658
719
  // split across the branch rather than the coordinate selected before it: an
@@ -670,30 +731,34 @@ void main() {
670
731
  float t = v_paintMode > ${PAINT_MODE_CONIC - 0.5}
671
732
  ? fract(atan(v_uv.y, v_uv.x) * ${INV_TAU})
672
733
  : length(v_uv);
673
- texel = sampleSlot(v_texSlot, vec2(t, v_post));
734
+ texel = texture(u_samplers[${RAMP_SLOT}], vec2(t, v_post));
674
735
  } else {
675
736
  texel = sampleSlot(v_texSlot, v_uv);
676
737
  }
738
+ // At 0 the mix is the texel itself, so a solid still gets its color exactly.
739
+ texel.rgb = mix(texel.rgb, straightRgb(texel), step(${PAINT_MODE_IMAGE - 0.5}, v_paintMode));
677
740
  float isGlyph = step(0.5, v_paintMode) * step(v_paintMode, 2.5);
678
741
  // Unconditional, and multiplied out afterwards rather than branched around:
679
- // see the file header.
680
- float coverage = glyphCoverage(texel, v_paintMode, u_synthBold, v_uv, u_fieldScale[v_texSlot]);
742
+ // see the file header. A glyph's a_post is its synthetic-bold shift.
743
+ float coverage = glyphCoverage(texel, v_paintMode, v_post * isGlyph, v_uv, u_fieldScale[v_texSlot]);
681
744
  // A glyph's texel is a distance field, so it takes no part in the color; a
682
745
  // plain vertex multiplies its texel in as it always did.
683
746
  vec4 src = mix(texel, vec4(1.0), isGlyph) * u_color * v_vertexColor;
684
- vec4 mapped = clamp(u_colorMatrix * src + u_colorBias, 0.0, 1.0);
685
- float a = mapped.a * u_alpha * mix(v_post, 1.0, isGrad) * mix(1.0, coverage, isGlyph);
747
+ vec4 mapped = clamp(u_colorMatrix[${m}] * src + u_colorBias[${m}], 0.0, 1.0);
748
+ float a = mapped.a * u_alpha[${m}] * mix(v_post, 1.0, max(isGrad, isGlyph)) * mix(1.0, coverage, isGlyph);
686
749
  outColor = vec4(mapped.rgb * a, a);
687
750
  }
688
751
  `
689
- );
752
+ );
753
+ }
754
+ var BATCH_FRAG_SRC = batchFragSrc(1);
755
+ var BATCH_MATRICES_FRAG_SRC = batchFragSrc(BATCH_MATRIX_SLOTS);
690
756
  var BATCH_FILL_UNIFORMS = [
691
757
  "u_proj",
692
758
  "u_model",
693
759
  "u_samplers",
694
760
  "u_color",
695
761
  "u_alpha",
696
- "u_synthBold",
697
762
  "u_fieldScale",
698
763
  "u_colorMatrix",
699
764
  "u_colorBias"
@@ -773,7 +838,6 @@ var INITIAL_VERTICES = 256;
773
838
  var INITIAL_INDICES = 384;
774
839
  var WHITE_U = 0.5;
775
840
  var WHITE_V = 0.5;
776
- var PLAIN_WHITE = packSlot(WHITE_SLOT, PAINT_MODE_PLAIN);
777
841
  var SOLID_RING_SIZE = 64;
778
842
  var BATCH_TIERS = [
779
843
  { vertices: 16, indices: 24 },
@@ -821,8 +885,7 @@ var DrawBatch = class {
821
885
  /** Whether the staged run is quads alone, so its indices are the canonical
822
886
  * pattern and a slot already holding that pattern needs no upload. */
823
887
  pureRects = true;
824
- /** The rect every push is cut to, or `null`. `draw.ts` sets it as rect
825
- * clips enter and leave — see `batchClip.ts`. A push returns `false`, having
888
+ /** The rect every push is cut to, or `null`. A push returns `false`, having
826
889
  * staged nothing, for geometry it cannot cut exactly; see `clipQuad`. */
827
890
  clip = null;
828
891
  /** Polygon scratch for `clip`: a quad cut by four edges has at most eight
@@ -857,10 +920,9 @@ var DrawBatch = class {
857
920
  }
858
921
  /**
859
922
  * Append one rect's four corners through `m`, all carrying `rgba` (straight
860
- * alpha). An affine maps a rect to a parallelogram, so two triangles still
861
- * cover it and the batch draws at `u_model` identity.
923
+ * alpha) and `P` in `a_texSlot`: the white texel, and the run's matrix.
862
924
  */
863
- pushRect(x, y, w, h, m, r, g, b, a) {
925
+ pushRect(x, y, w, h, m, r, g, b, a, P) {
864
926
  this.reserve(4, 6);
865
927
  let i = this.nVerts * FLOATS_PER_VERTEX;
866
928
  const ma = m[0], mb = m[1], mc = m[3], md = m[4], mtx = m[6], mty = m[7];
@@ -870,7 +932,6 @@ var DrawBatch = class {
870
932
  const bx = ma * x1 + mc * y + mtx, by = mb * x1 + md * y + mty;
871
933
  const cx = ma * x1 + mc * y1 + mtx, cy = mb * x1 + md * y1 + mty;
872
934
  const dx = ma * x + mc * y1 + mtx, dy = mb * x + md * y1 + mty;
873
- const P = PLAIN_WHITE;
874
935
  i = this.writeVertex(i, ax, ay, r, g, b, a, WHITE_U, WHITE_V, 1, P);
875
936
  i = this.writeVertex(i, bx, by, r, g, b, a, WHITE_U, WHITE_V, 1, P);
876
937
  i = this.writeVertex(i, cx, cy, r, g, b, a, WHITE_U, WHITE_V, 1, P);
@@ -881,17 +942,15 @@ var DrawBatch = class {
881
942
  /**
882
943
  * Append one image quad: the destination rect `(x, y, w, h)` mapped through
883
944
  * `m`, sampling `(u0, v0)`-`(u1, v1)`, every corner carrying `post` as the
884
- * after-the-color-matrix alpha factor.
885
- *
886
- * Corners wind top-left, top-right, bottom-right, bottom-left with UVs
887
- * following — the same winding `pushRect` uses, which is what lets a run of
888
- * mixed rects and quads keep the canonical index pattern. The flips a command
889
- * asks for are already in the `u` / `v` the caller passes.
945
+ * after-the-color-matrix alpha factor and `rgb` as the scale on its texel.
946
+ * `slot` is the texture unit the corners sample, and may carry the index of
947
+ * the run's matrix they are mapped by as well (`withMatrix`).
890
948
  *
891
- * `slot` is the texture unit the corners sample, which `draw.ts` assigns per
892
- * bitmap within the run.
949
+ * Corners wind top-left, top-right, bottom-right, bottom-left — the same
950
+ * winding `pushRect` uses, which is what lets a run of mixed rects and quads
951
+ * keep the canonical index pattern.
893
952
  */
894
- pushQuad(x, y, w, h, m, u0, v0, u1, v1, post, slot) {
953
+ pushQuad(x, y, w, h, m, u0, v0, u1, v1, post, slot, r = 1, g = 1, b = 1) {
895
954
  this.reserve(4, 6);
896
955
  let i = this.nVerts * FLOATS_PER_VERTEX;
897
956
  const ma = m[0], mb = m[1], mc = m[3], md = m[4], mtx = m[6], mty = m[7];
@@ -901,11 +960,11 @@ var DrawBatch = class {
901
960
  const bx = ma * x1 + mc * y + mtx, by = mb * x1 + md * y + mty;
902
961
  const cx = ma * x1 + mc * y1 + mtx, cy = mb * x1 + md * y1 + mty;
903
962
  const dx = ma * x + mc * y1 + mtx, dy = mb * x + md * y1 + mty;
904
- const P = packSlot(slot, PAINT_MODE_PLAIN);
905
- i = this.writeVertex(i, ax, ay, 1, 1, 1, 1, u0, v0, post, P);
906
- i = this.writeVertex(i, bx, by, 1, 1, 1, 1, u1, v0, post, P);
907
- i = this.writeVertex(i, cx, cy, 1, 1, 1, 1, u1, v1, post, P);
908
- this.writeVertex(i, dx, dy, 1, 1, 1, 1, u0, v1, post, P);
963
+ const P = packSlot(slot, PAINT_MODE_IMAGE);
964
+ i = this.writeVertex(i, ax, ay, r, g, b, 1, u0, v0, post, P);
965
+ i = this.writeVertex(i, bx, by, r, g, b, 1, u1, v0, post, P);
966
+ i = this.writeVertex(i, cx, cy, r, g, b, 1, u1, v1, post, P);
967
+ this.writeVertex(i, dx, dy, r, g, b, 1, u0, v1, post, P);
909
968
  this.pushQuadIndices();
910
969
  return this.clip === null || this.clipQuad(this.nVerts - 4, this.nIdx - 6, this.clip);
911
970
  }
@@ -915,22 +974,13 @@ var DrawBatch = class {
915
974
  * `rgba`.
916
975
  *
917
976
  * `mode` says which channels of that atlas carry the distance field, and
918
- * rides the vertices packed into the slot — so a run off a baked MSDF atlas
919
- * and one off the runtime canvas bake still share a draw. The synthetic-bold
920
- * threshold does not: it is `u_synthBold`, and `draw.ts` breaks the run when
921
- * it changes.
977
+ * rides the vertices packed into the slot. `synthBold` is the shift on the
978
+ * field's threshold, in `a_post`: a glyph has no use for an alpha factor there.
922
979
  *
923
- * **A synthetic oblique is sheared here rather than in the shader.** The old
924
- * text program carried the baseline per vertex and skewed against
925
- * `u_synthItalic`; the batch places its own corners, so the shear is one
926
- * multiply while they are being placed, and `tanItalic` is 0 for an upright
927
- * face. It has to happen before `m`, which is where the shader had it too.
928
- *
929
- * Corners wind top-left, top-right, bottom-right, bottom-left — `pushQuad`'s
930
- * winding, which is what lets a run of mixed rects, image quads and glyphs
931
- * keep the canonical index pattern.
980
+ * A synthetic oblique is sheared here, before `m`; `tanItalic` is 0 for an
981
+ * upright face. Corners wind as `pushQuad`'s do.
932
982
  */
933
- pushGlyph(x0, y0, x1, y1, baselineY, tanItalic, m, u0, v0, u1, v1, r, g, b, a, slot, mode) {
983
+ pushGlyph(x0, y0, x1, y1, baselineY, tanItalic, m, u0, v0, u1, v1, r, g, b, a, slot, mode, synthBold) {
934
984
  this.reserve(4, 6);
935
985
  let i = this.nVerts * FLOATS_PER_VERTEX;
936
986
  const ma = m[0], mb = m[1], mc = m[3], md = m[4], mtx = m[6], mty = m[7];
@@ -943,10 +993,10 @@ var DrawBatch = class {
943
993
  const cx = ma * bx1 + mc * y1 + mtx, cy = mb * bx1 + md * y1 + mty;
944
994
  const dx = ma * bx0 + mc * y1 + mtx, dy = mb * bx0 + md * y1 + mty;
945
995
  const P = packSlot(slot, mode);
946
- i = this.writeVertex(i, ax, ay, r, g, b, a, u0, v0, 1, P);
947
- i = this.writeVertex(i, bx, by, r, g, b, a, u1, v0, 1, P);
948
- i = this.writeVertex(i, cx, cy, r, g, b, a, u1, v1, 1, P);
949
- this.writeVertex(i, dx, dy, r, g, b, a, u0, v1, 1, P);
996
+ i = this.writeVertex(i, ax, ay, r, g, b, a, u0, v0, synthBold, P);
997
+ i = this.writeVertex(i, bx, by, r, g, b, a, u1, v0, synthBold, P);
998
+ i = this.writeVertex(i, cx, cy, r, g, b, a, u1, v1, synthBold, P);
999
+ this.writeVertex(i, dx, dy, r, g, b, a, u0, v1, synthBold, P);
950
1000
  this.pushQuadIndices();
951
1001
  return this.clip === null || this.clipQuad(this.nVerts - 4, this.nIdx - 6, this.clip);
952
1002
  }
@@ -955,16 +1005,10 @@ var DrawBatch = class {
955
1005
  * through `m`, sampling the ramp atlas at `slot`.
956
1006
  *
957
1007
  * `uv` gives each channel of `a_uv` as an affine function of the coordinates
958
- * the corners arrive in, which is what lets a gradient ride the vertices at
959
- * all: a linear one's ramp position is affine in position outright, and a
960
- * radial or conic one's gradient-space *coordinate* is, even though the ramp
961
- * position it yields is not. Either way the rasterizer's interpolation across
962
- * a triangle is exact. `mode` says which of the two the fragment shader is
963
- * looking at, and `post` carries the atlas row for the modes that read it —
964
- * see `shaders/batchFill.ts`.
965
- *
966
- * Values outside 0..1 are the sampler's business; the atlas clamps to the
967
- * edge texel, which is what the gradient shader's own `clamp` did.
1008
+ * the corners arrive in. `mode` says whether that is a ramp position or a
1009
+ * gradient-space coordinate, and `post` carries the atlas row for the modes
1010
+ * that read it — see `shaders/batchFill.ts`. Values outside 0..1 clamp to
1011
+ * the atlas's edge texel.
968
1012
  */
969
1013
  pushGradientRect(x, y, w, h, m, uv, post, slot, mode, r, g, b, a) {
970
1014
  this.reserve(4, 6);
@@ -1032,11 +1076,11 @@ var DrawBatch = class {
1032
1076
  return this.clip === null || this.clipMesh(base, this.nIdx - srcIdx.length, this.clip);
1033
1077
  }
1034
1078
  /**
1035
- * Append a tessellated mesh through `m`, all vertices carrying `rgba`. The
1036
- * mesh's own indices are rebased onto the staged vertices, which is why the
1037
- * index buffer is uploaded per flush rather than written once.
1079
+ * Append a tessellated mesh through `m`, all vertices carrying `rgba` and
1080
+ * `P`, as `pushRect`'s do. The mesh's own indices are rebased onto the staged
1081
+ * vertices, which is why the index buffer is uploaded per flush.
1038
1082
  */
1039
- pushMesh(mesh, m, r, g, b, a) {
1083
+ pushMesh(mesh, m, r, g, b, a, P) {
1040
1084
  const src = mesh.vertices;
1041
1085
  const srcIdx = mesh.indices;
1042
1086
  const n = src.length >> 1;
@@ -1057,7 +1101,7 @@ var DrawBatch = class {
1057
1101
  v[i++] = WHITE_U;
1058
1102
  v[i++] = WHITE_V;
1059
1103
  v[i++] = 1;
1060
- v[i++] = PLAIN_WHITE;
1104
+ v[i++] = P;
1061
1105
  }
1062
1106
  const base = this.nVerts;
1063
1107
  const out = this.idx;
@@ -1121,9 +1165,7 @@ var DrawBatch = class {
1121
1165
  * subpixel grid the rasterizer snaps to, and the shortened edge then covers
1122
1166
  * different samples than the whole one did; along an axis the cut edge is
1123
1167
  * the same line exactly. So a slanted quad that crosses is rolled back and
1124
- * refused, for `draw.ts` to clip through the stencil instead.
1125
- *
1126
- * A cut axis-aligned quad is still four corners, so the run stays pure.
1168
+ * refused, for the caller to clip through the stencil instead.
1127
1169
  */
1128
1170
  clipQuad(v0, i0, rect) {
1129
1171
  const v = this.verts;
@@ -1276,14 +1318,16 @@ var DrawBatch = class {
1276
1318
  gl.deleteVertexArray(set.vao);
1277
1319
  }
1278
1320
  };
1279
-
1280
- // src/images/imageCache.ts
1281
1321
  var MAX_VECTOR_RASTER_PIXELS = 4096 * 4096;
1282
1322
  var SHRINK_SLACK = 4;
1283
1323
  var FALLBACK_WIDTH = 300;
1284
1324
  var FALLBACK_HEIGHT = 150;
1325
+ var DEFAULT_BUDGET_BYTES = 256 * 1024 * 1024;
1285
1326
  var cache6 = /* @__PURE__ */ new Map();
1286
- var subscribers = /* @__PURE__ */ new Set();
1327
+ var budgetBytes = DEFAULT_BUDGET_BYTES;
1328
+ var reads = 0;
1329
+ var readsAtLastPass = 0;
1330
+ var ready = createEmitter();
1287
1331
  var decodedVectors = /* @__PURE__ */ new Map();
1288
1332
  function isVectorImageSrc(src) {
1289
1333
  if (/^data:image\/svg\+xml[;,]/i.test(src)) return true;
@@ -1328,10 +1372,30 @@ var defaultLoader = async (src, size) => {
1328
1372
  };
1329
1373
  var loader = defaultLoader;
1330
1374
  function notify() {
1331
- for (const cb of subscribers) cb();
1375
+ ready.emit();
1376
+ }
1377
+ var bytesOf = (bitmap) => bitmap ? bitmap.width * bitmap.height * 4 : 0;
1378
+ function enforceBudget(landed) {
1379
+ let total = 0;
1380
+ for (const entry of cache6.values()) total += bytesOf(entry.bitmap);
1381
+ if (total > budgetBytes) {
1382
+ const idle = [...cache6].filter(([src, entry]) => src !== landed && entry.bitmap !== void 0 && entry.readAt <= readsAtLastPass && !imageSourceInLatestFrame(entry.bitmap));
1383
+ idle.sort((a, b) => a[1].readAt - b[1].readAt);
1384
+ for (const [src, entry] of idle) {
1385
+ if (total <= budgetBytes) break;
1386
+ total -= bytesOf(entry.bitmap);
1387
+ releaseImage(src);
1388
+ }
1389
+ }
1390
+ readsAtLastPass = reads;
1391
+ }
1392
+ function setImageCacheBudget(bytes) {
1393
+ const previous = budgetBytes;
1394
+ budgetBytes = bytes;
1395
+ return previous;
1332
1396
  }
1333
1397
  function startLoad2(src) {
1334
- const entry = { status: "loading" };
1398
+ const entry = { status: "loading", readAt: ++reads };
1335
1399
  cache6.set(src, entry);
1336
1400
  loader(src).then(
1337
1401
  (bitmap) => {
@@ -1339,13 +1403,15 @@ function startLoad2(src) {
1339
1403
  cache6.set(src, {
1340
1404
  status: "ready",
1341
1405
  bitmap,
1406
+ readAt: entry.readAt,
1342
1407
  ...isVectorImageSrc(src) ? { vector: { natural: { width: bitmap.width, height: bitmap.height }, scale: 1, failed: /* @__PURE__ */ new Set(), floor: 0 } } : {}
1343
1408
  });
1409
+ enforceBudget(src);
1344
1410
  notify();
1345
1411
  },
1346
1412
  () => {
1347
1413
  if (cache6.get(src) !== entry) return;
1348
- cache6.set(src, { status: "error" });
1414
+ cache6.set(src, { status: "error", readAt: entry.readAt });
1349
1415
  notify();
1350
1416
  }
1351
1417
  );
@@ -1384,6 +1450,7 @@ function maybeReraster(src, entry, v, drawnAt) {
1384
1450
  v.shrunkTo = grow ? void 0 : target;
1385
1451
  v.scale = target;
1386
1452
  if (old && old !== bitmap) releaseImageSource(old);
1453
+ enforceBudget(src);
1387
1454
  notify();
1388
1455
  },
1389
1456
  () => {
@@ -1399,6 +1466,7 @@ function getImageBitmap(src, drawnAt) {
1399
1466
  startLoad2(src);
1400
1467
  return void 0;
1401
1468
  }
1469
+ entry.readAt = ++reads;
1402
1470
  if (drawnAt && entry.vector) maybeReraster(src, entry, entry.vector, drawnAt);
1403
1471
  return entry.bitmap;
1404
1472
  }
@@ -1417,21 +1485,21 @@ function retryImage(src) {
1417
1485
  notify();
1418
1486
  }
1419
1487
  function subscribeImageReady(cb) {
1420
- subscribers.add(cb);
1421
- return () => {
1422
- subscribers.delete(cb);
1423
- };
1488
+ return ready.subscribe(cb);
1424
1489
  }
1425
1490
  function __setImageLoaderForTests(fn) {
1426
1491
  loader = fn;
1427
1492
  }
1428
1493
  function _resetImageCacheForTests() {
1429
1494
  cache6.clear();
1430
- subscribers.clear();
1495
+ ready = createEmitter();
1431
1496
  decodedVectors.clear();
1432
1497
  loader = defaultLoader;
1498
+ budgetBytes = DEFAULT_BUDGET_BYTES;
1499
+ reads = 0;
1500
+ readsAtLastPass = 0;
1433
1501
  }
1434
1502
 
1435
- export { BATCH_ATTRIBUTE_LOCATIONS, BATCH_FILL_ATTRIBUTES, BATCH_FILL_UNIFORMS, BATCH_FRAG_SRC, BATCH_TEXTURE_SLOTS, BATCH_VERT_SRC, DrawBatch, FLOATS_PER_VERTEX, GLImageCache, PAINT_MODE_CONIC, PAINT_MODE_PLAIN, PAINT_MODE_RADIAL, __setImageLoaderForTests, _resetCacheForTests, _resetImageCacheForTests, _resetMarkerCommandCacheForTests, _resetOutlineMeshCacheForTests, _resetOutlineStrokeMeshCacheForTests, _resetPaintDrawKindsForTests, _resetPatternSpecCacheForTests, _resetProgramRegistryForTests, _resetStrokeMeshCacheForTests, axisAlignedClipRect, cachedMarkerCommands, commandFill, commandStroke, fillInPoseFrame, fillToBoundsFrame, getImageBitmap, getMesh, getPaintDrawKind, getProgramSource, imageStatus, intersectClipRects, isPaintKindKnown, isPatternSpec, isVectorImageSrc, outlineMesh, outlineStrokeMesh, paintDrawKindRegistry, programSourceRegistry, quantizeEmWidth, quantizeStrokeScale, registerPaintDrawKind, registerPaintDrawKindLoader, registerProgram, releaseImage, releaseImageSource, resolveFillPattern, resolvePatternSpec, retryImage, strokeMesh, subscribeImageReady, warmPaintKinds };
1436
- //# sourceMappingURL=chunk-VHQJ56XQ.js.map
1437
- //# sourceMappingURL=chunk-VHQJ56XQ.js.map
1503
+ export { BATCH_ATTRIBUTE_LOCATIONS, BATCH_FILL_ATTRIBUTES, BATCH_FILL_UNIFORMS, BATCH_FRAG_SRC, BATCH_MATRICES_FRAG_SRC, BATCH_MATRIX_SLOTS, BATCH_TEXTURE_SLOTS, BATCH_VERT_SRC, DrawBatch, FLOATS_PER_VERTEX, GLImageCache, PAINT_MODE_CONIC, PAINT_MODE_PLAIN, PAINT_MODE_RADIAL, RAMP_SLOT, STRAIGHT_RGB_GLSL, WHITE_SLOT, __setImageLoaderForTests, _resetCacheForTests, _resetImageCacheForTests, _resetMarkerCommandCacheForTests, _resetOutlineMeshCacheForTests, _resetOutlineStrokeMeshCacheForTests, _resetPaintDrawKindsForTests, _resetPatternSpecCacheForTests, _resetProgramRegistryForTests, _resetStrokeMeshCacheForTests, axisAlignedClipRect, cachedMarkerCommands, commandFill, commandPaint, commandStroke, fillInPoseFrame, fillToBoundsFrame, getImageBitmap, getMesh, getPaintDrawKind, getProgramSource, imageStatus, intersectClipRects, isPaintKindKnown, isVectorImageSrc, outlineMesh, outlineStrokeMesh, packSlot, paintDrawKindRegistry, programSourceRegistry, quantizeEmWidth, quantizeStrokeScale, registerPaintDrawKind, registerPaintDrawKindLoader, registerProgram, releaseImage, releaseImageSource, resolveFillPattern, resolvePatternSpec, retryImage, setImageCacheBudget, strokeMesh, subscribeImageReady, uploadPremultiplied, warmPaintKinds, withMatrix };
1504
+ //# sourceMappingURL=chunk-K7XCWVQH.js.map
1505
+ //# sourceMappingURL=chunk-K7XCWVQH.js.map