@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.
- package/README.md +2 -2
- package/dist/{chunk-2VOBNWBN.js → chunk-43YL7HZV.js} +3 -3
- package/dist/chunk-43YL7HZV.js.map +1 -0
- package/dist/{chunk-VHQJ56XQ.js → chunk-K7XCWVQH.js} +236 -168
- package/dist/chunk-K7XCWVQH.js.map +1 -0
- package/dist/{chunk-ORBXHIRB.js → chunk-Q4SIHV7Q.js} +4 -4
- package/dist/chunk-Q4SIHV7Q.js.map +1 -0
- package/dist/{imageCache-oJVgxhkr.d.ts → imageCache-DucOxC7l.d.ts} +73 -115
- package/dist/index.d.ts +201 -204
- package/dist/index.js +577 -291
- package/dist/index.js.map +1 -1
- package/dist/mesh.d.ts +34 -67
- package/dist/mesh.js +5 -5
- package/dist/mesh.js.map +1 -1
- package/dist/{paintKinds-B2vEy9A4.d.ts → paintKinds-BmPAT51k.d.ts} +22 -38
- package/dist/patterns-builtin.d.ts +10 -16
- package/dist/patterns-builtin.js +7 -7
- package/dist/patterns-builtin.js.map +1 -1
- package/dist/test-seams.d.ts +22 -65
- package/dist/test-seams.js +2 -2
- package/dist/testing/glRecorder.d.ts +2 -5
- package/dist/testing/glRecorder.js +3 -0
- package/dist/testing/glRecorder.js.map +1 -1
- package/dist/testing/vertexUploads.d.ts +4 -6
- package/dist/testing/vertexUploads.js.map +1 -1
- package/package.json +7 -7
- package/dist/chunk-2VOBNWBN.js.map +0 -1
- package/dist/chunk-ORBXHIRB.js.map +0 -1
- package/dist/chunk-VHQJ56XQ.js.map +0 -1
|
@@ -1,16 +1,28 @@
|
|
|
1
|
-
import { ownTextureIds, rasterizeTile, drawTileShapes, tileGeometry, tileSize, registerTextureAs, textureRegistry, unregisterTexture } from './chunk-
|
|
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 {
|
|
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
|
-
* `'
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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
|
-
*
|
|
49
|
-
*
|
|
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
|
-
/**
|
|
64
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
362
|
+
return getPaintDrawKind(fill.kind)?.toBoundsFrame?.(fill, box) ?? fill;
|
|
328
363
|
}
|
|
329
|
-
if (fill
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
362
|
-
return kind === "solid" ||
|
|
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
|
|
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,
|
|
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 =
|
|
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 || !("
|
|
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
|
|
432
|
-
|
|
433
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
481
|
-
if (
|
|
482
|
-
key += `${
|
|
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,
|
|
487
|
-
if (
|
|
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
|
|
503
|
-
const map = mapFor(path, source,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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 +
|
|
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
|
-
|
|
632
|
-
|
|
633
|
-
|
|
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
|
-
${
|
|
651
|
-
|
|
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 =
|
|
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,
|
|
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`.
|
|
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)
|
|
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
|
-
*
|
|
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
|
-
*
|
|
892
|
-
*
|
|
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,
|
|
905
|
-
i = this.writeVertex(i, ax, ay,
|
|
906
|
-
i = this.writeVertex(i, bx, by,
|
|
907
|
-
i = this.writeVertex(i, cx, cy,
|
|
908
|
-
this.writeVertex(i, dx, dy,
|
|
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
|
|
919
|
-
*
|
|
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
|
-
*
|
|
924
|
-
*
|
|
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,
|
|
947
|
-
i = this.writeVertex(i, bx, by, r, g, b, a, u1, v0,
|
|
948
|
-
i = this.writeVertex(i, cx, cy, r, g, b, a, u1, v1,
|
|
949
|
-
this.writeVertex(i, dx, dy, r, g, b, a, u0, v1,
|
|
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
|
|
959
|
-
*
|
|
960
|
-
*
|
|
961
|
-
*
|
|
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
|
|
1036
|
-
* mesh's own indices are rebased onto the staged
|
|
1037
|
-
* index buffer is uploaded per flush
|
|
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++] =
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
1436
|
-
//# sourceMappingURL=chunk-
|
|
1437
|
-
//# sourceMappingURL=chunk-
|
|
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
|