@weasel-js/renderer 1.10.0 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{chunk-LFD47OV2.js → chunk-CUJEWF5G.js} +102 -52
- package/dist/chunk-CUJEWF5G.js.map +1 -0
- package/dist/{imageCache-DYcWftY8.d.ts → imageCache-CDm3qgsg.d.ts} +24 -11
- package/dist/index.d.ts +122 -21
- package/dist/index.js +1036 -408
- package/dist/index.js.map +1 -1
- package/dist/patterns-builtin.d.ts +2 -0
- package/dist/patterns-builtin.js.map +1 -1
- package/dist/test-seams.d.ts +6 -8
- package/dist/test-seams.js +1 -1
- package/package.json +10 -7
- package/dist/chunk-LFD47OV2.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
export { createTilePattern } from './chunk-43YL7HZV.js';
|
|
2
|
-
import { BATCH_ATTRIBUTE_LOCATIONS, BATCH_TEXTURE_SLOTS, registerProgram,
|
|
3
|
-
export { commandFill, commandPaint, commandStroke, fillInPoseFrame, fillToBoundsFrame, getImageBitmap, getPaintDrawKind, getProgramSource, imageStatus, isPaintKindKnown, isVectorImageSrc, paintDrawKindRegistry, programSourceRegistry, registerPaintDrawKind, registerPaintDrawKindLoader, registerProgram, releaseImage, releaseImageSource, resolveFillPattern, resolvePatternSpec, retryImage, setImageCacheBudget, subscribeImageReady, warmPaintKinds } from './chunk-
|
|
2
|
+
import { BATCH_ATTRIBUTE_LOCATIONS, BATCH_TEXTURE_SLOTS, registerProgram, quantizeStrokeScale, STRAIGHT_RGB_GLSL, BATCH_FILL_ATTRIBUTES, BATCH_FILL_UNIFORMS, BATCH_MATRICES_FRAG_SRC, BATCH_VERT_SRC, GLImageCache, DrawBatch, getProgramSource, paintDrawKindRegistry, commandFill, commandPaint, BATCH_FRAG_SRC, uploadPremultiplied, RAMP_SLOT, withMatrix, cachedMarkerCommands, BATCH_MATRIX_SLOTS, packSlot, WHITE_SLOT, PAINT_MODE_PLAIN, PAINT_MODE_RADIAL, PAINT_MODE_CONIC, getMesh, strokeMesh, axisAlignedClipRect, intersectClipRects, getPaintDrawKind, outlineMesh, outlineStrokeMesh, quantizeEmWidth } from './chunk-CUJEWF5G.js';
|
|
3
|
+
export { bitmapSrc, commandFill, commandPaint, commandStroke, fillInPoseFrame, fillToBoundsFrame, getImageBitmap, getPaintDrawKind, getProgramSource, imageStatus, isPaintKindKnown, isVectorImageSrc, paintDrawKindRegistry, programSourceRegistry, registerPaintDrawKind, registerPaintDrawKindLoader, registerProgram, releaseImage, releaseImageSource, resolveFillPattern, resolvePatternSpec, retryImage, setImageCacheBudget, subscribeImageReady, warmPaintKinds } from './chunk-CUJEWF5G.js';
|
|
4
4
|
import { textureRegistry, getTexture } from './chunk-Q4SIHV7Q.js';
|
|
5
5
|
export { registerTexture, textureRegistry, unregisterTexture } from './chunk-Q4SIHV7Q.js';
|
|
6
|
-
import { DEFAULT_BAKE_BUDGET, markAllFontsNotUploaded, resetBakeBudget, GLYPH_COVERAGE_GLSL, syncColorPageTexture, colorPageTextureId, syncDynamicPageTexture, ensureFontTexture, dynamicPageTextureId, textureCacheKey, GLYPH_MODE_R8, GLYPH_MODE_MSDF, glyphFieldScale } from '@weasel-js/font';
|
|
6
|
+
import { DEFAULT_BAKE_BUDGET, markAllFontsNotUploaded, glyphGeneration, resetBakeBudget, GLYPH_COVERAGE_GLSL, syncColorPageTexture, colorPageTextureId, syncDynamicPageTexture, ensureFontTexture, dynamicPageTextureId, textureCacheKey, GLYPH_MODE_R8, GLYPH_MODE_MSDF, glyphFieldScale, SYNTH_BOLD_AMOUNT, sharperColorGlyph, syncSharpColorPageTexture, sharpColorPageTextureId, DYNAMIC_PAGE_SIZE } from '@weasel-js/font';
|
|
7
7
|
export { canQueryLocalFonts, cssFontFamily, cssFontFamilyLoading, enableLocalFontOutlines, fontOutlineRegistry, fontRegistry, hasFontOutlines, isCanvasFont, listFontOutlines, loadFontOutlines, outlineStatus, registerCanvasFont, registerFont, registerFontOutlines, subscribeGlyphReady, unregisterCanvasFont, unregisterFontOutlines } from '@weasel-js/font';
|
|
8
|
-
import { resolveGradientStops, sampleResolvedStops, resolveScreenLength, isSolidPaint, resolveColor,
|
|
9
|
-
import { mat3 } from '@weasel-js/geom';
|
|
8
|
+
import { resolveGradientStops, sampleResolvedStops, resolveScreenLength, isSolidPaint, strokeHasInk, resolveColor, scaleVertexWidth } from '@weasel-js/paint';
|
|
9
|
+
import { mat3, DEFAULT_FLATTEN_TOLERANCE, resolveCornersWithSources, remapPerAnchor } from '@weasel-js/geom';
|
|
10
10
|
export { mat3, viewToMat3 } from '@weasel-js/geom';
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
import { resolveStrokeWidth, markerReach, metricKeepsRects, strokeSpaceOf, resolveMarkerSize } from '@weasel-js/stroke';
|
|
11
|
+
import { isDev } from '@weasel-js/registry';
|
|
12
|
+
import { resolveStrokeWidth, widestStrokeWidth, markerReach, strokeSpaceOf, resolveMarkerSize, metricKeepsRects } from '@weasel-js/stroke';
|
|
14
13
|
export { tessellateStroke } from '@weasel-js/stroke';
|
|
15
14
|
import { resolveTextStyle, resolveRuns, textPoseLayoutInput, cachedLayoutRuns, cachedPaintRuns, verticalAlignOffset, SYNTHETIC_ITALIC_RADIANS } from '@weasel-js/text';
|
|
15
|
+
export { tessellate } from '@weasel-js/geom/tessellate';
|
|
16
16
|
|
|
17
17
|
// src/paintResources.ts
|
|
18
18
|
function createPaintResources() {
|
|
@@ -229,7 +229,6 @@ uniform mat4 u_colorMatrix;
|
|
|
229
229
|
uniform vec4 u_colorBias;
|
|
230
230
|
uniform float u_opacity;
|
|
231
231
|
uniform int u_gradKind;
|
|
232
|
-
uniform vec2 u_gradP0;
|
|
233
232
|
uniform vec2 u_gradDir;
|
|
234
233
|
uniform float u_gradLen;
|
|
235
234
|
uniform float u_gradRadius;
|
|
@@ -240,12 +239,11 @@ const float PI = 3.14159265358979323846;
|
|
|
240
239
|
vec4 shadePaint(vec2 world) {
|
|
241
240
|
float t;
|
|
242
241
|
if (u_gradKind == 0) {
|
|
243
|
-
|
|
244
|
-
t = dot(d, u_gradDir) / max(u_gradLen, 0.0001);
|
|
242
|
+
t = dot(world, u_gradDir) / max(u_gradLen, 0.0001);
|
|
245
243
|
} else if (u_gradKind == 1) {
|
|
246
|
-
t = length(world
|
|
244
|
+
t = length(world) / max(u_gradRadius, 0.0001);
|
|
247
245
|
} else {
|
|
248
|
-
float a = atan(world.y
|
|
246
|
+
float a = atan(world.y, world.x) - u_gradAngle;
|
|
249
247
|
t = fract(a / (2.0 * PI));
|
|
250
248
|
}
|
|
251
249
|
t = clamp(t, 0.0, 1.0);
|
|
@@ -284,7 +282,6 @@ var GRAD_FILL_UNIFORMS = [
|
|
|
284
282
|
"u_colorMatrix",
|
|
285
283
|
"u_colorBias",
|
|
286
284
|
"u_gradKind",
|
|
287
|
-
"u_gradP0",
|
|
288
285
|
"u_gradDir",
|
|
289
286
|
"u_gradLen",
|
|
290
287
|
"u_gradRadius",
|
|
@@ -355,6 +352,72 @@ var PATTERN_FILL_UNIFORMS = [
|
|
|
355
352
|
"u_colorBias"
|
|
356
353
|
];
|
|
357
354
|
var PATTERN_FILL_ATTRIBUTES = ["a_position"];
|
|
355
|
+
|
|
356
|
+
// src/shaders/builtinPrograms.ts
|
|
357
|
+
var LazyProgram = class {
|
|
358
|
+
constructor(gl, spec) {
|
|
359
|
+
this.gl = gl;
|
|
360
|
+
this.spec = spec;
|
|
361
|
+
}
|
|
362
|
+
gl;
|
|
363
|
+
spec;
|
|
364
|
+
program = null;
|
|
365
|
+
get() {
|
|
366
|
+
if (!this.program) {
|
|
367
|
+
const { vert, frag, uniforms, attributes } = this.spec;
|
|
368
|
+
const program = new ShaderProgram(this.gl, vert, frag);
|
|
369
|
+
program.lookupUniforms(uniforms);
|
|
370
|
+
program.lookupAttributes(attributes);
|
|
371
|
+
this.program = program;
|
|
372
|
+
}
|
|
373
|
+
return this.program;
|
|
374
|
+
}
|
|
375
|
+
/** Delete the program if it was built. It builds again on the next `get`. */
|
|
376
|
+
release() {
|
|
377
|
+
if (this.program) this.gl.deleteProgram(this.program.handle);
|
|
378
|
+
this.program = null;
|
|
379
|
+
}
|
|
380
|
+
/** Forget a program its lost context already took with it. */
|
|
381
|
+
abandon() {
|
|
382
|
+
this.program = null;
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
var PATH_FILL_SPEC = {
|
|
386
|
+
vert: VERT_SRC,
|
|
387
|
+
frag: FRAG_SRC,
|
|
388
|
+
uniforms: PATH_FILL_UNIFORMS,
|
|
389
|
+
attributes: PATH_FILL_ATTRIBUTES
|
|
390
|
+
};
|
|
391
|
+
var PATH_FILL_VCOLOR_SPEC = {
|
|
392
|
+
vert: VCOLOR_VERT_SRC,
|
|
393
|
+
frag: VCOLOR_FRAG_SRC,
|
|
394
|
+
uniforms: PATH_FILL_UNIFORMS,
|
|
395
|
+
attributes: PATH_FILL_VCOLOR_ATTRIBUTES
|
|
396
|
+
};
|
|
397
|
+
var GRAD_FILL_SPEC = {
|
|
398
|
+
vert: GRAD_VERT_SRC,
|
|
399
|
+
frag: GRAD_FRAG_SRC,
|
|
400
|
+
uniforms: GRAD_FILL_UNIFORMS,
|
|
401
|
+
attributes: GRAD_FILL_ATTRIBUTES
|
|
402
|
+
};
|
|
403
|
+
var BATCH_FILL_MATRICES_SPEC = {
|
|
404
|
+
vert: BATCH_VERT_SRC,
|
|
405
|
+
frag: BATCH_MATRICES_FRAG_SRC,
|
|
406
|
+
uniforms: BATCH_FILL_UNIFORMS,
|
|
407
|
+
attributes: BATCH_FILL_ATTRIBUTES
|
|
408
|
+
};
|
|
409
|
+
var PATTERN_FILL_SPEC = {
|
|
410
|
+
vert: PATTERN_VERT_SRC,
|
|
411
|
+
frag: PATTERN_FRAG_SRC,
|
|
412
|
+
uniforms: PATTERN_FILL_UNIFORMS,
|
|
413
|
+
attributes: PATTERN_FILL_ATTRIBUTES
|
|
414
|
+
};
|
|
415
|
+
function createBatchFill(gl) {
|
|
416
|
+
const program = new ShaderProgram(gl, BATCH_VERT_SRC, BATCH_FRAG_SRC);
|
|
417
|
+
program.lookupUniforms(BATCH_FILL_UNIFORMS);
|
|
418
|
+
program.lookupAttributes(BATCH_FILL_ATTRIBUTES);
|
|
419
|
+
return program;
|
|
420
|
+
}
|
|
358
421
|
var L = BATCH_ATTRIBUTE_LOCATIONS;
|
|
359
422
|
var GLYPH_PAINT_VERT_SRC = (
|
|
360
423
|
/* glsl */
|
|
@@ -1064,6 +1127,22 @@ var GroupState = class {
|
|
|
1064
1127
|
this.colorMatrixStack.pop();
|
|
1065
1128
|
}
|
|
1066
1129
|
};
|
|
1130
|
+
var DEFAULT_FLATTEN_TOLERANCE_PX = DEFAULT_FLATTEN_TOLERANCE;
|
|
1131
|
+
function worldFlattenTolerance(tolerance, transform) {
|
|
1132
|
+
if (typeof tolerance === "number") return tolerance;
|
|
1133
|
+
const px = tolerance?.px ?? DEFAULT_FLATTEN_TOLERANCE_PX;
|
|
1134
|
+
const [a, b, , c, d] = transform;
|
|
1135
|
+
const sum = a * a + b * b + c * c + d * d;
|
|
1136
|
+
const det = a * d - b * c;
|
|
1137
|
+
const stretch = Math.sqrt((sum + Math.sqrt(Math.max(0, sum * sum - 4 * det * det))) / 2);
|
|
1138
|
+
const world = px / stretch;
|
|
1139
|
+
if (!(world > 0) || !Number.isFinite(world)) return DEFAULT_FLATTEN_TOLERANCE;
|
|
1140
|
+
return 2 ** Math.floor(Math.log2(world));
|
|
1141
|
+
}
|
|
1142
|
+
function inCssPixels(tolerance, dpr) {
|
|
1143
|
+
if (typeof tolerance === "number") return tolerance;
|
|
1144
|
+
return { px: (tolerance?.px ?? DEFAULT_FLATTEN_TOLERANCE_PX) / dpr };
|
|
1145
|
+
}
|
|
1067
1146
|
|
|
1068
1147
|
// src/effects/effectPrelude.ts
|
|
1069
1148
|
var EFFECT_VERT_SRC = (
|
|
@@ -1196,14 +1275,11 @@ function endParityMask(ctx) {
|
|
|
1196
1275
|
gl.clear(gl.STENCIL_BUFFER_BIT);
|
|
1197
1276
|
gl.disable(gl.STENCIL_TEST);
|
|
1198
1277
|
}
|
|
1199
|
-
|
|
1200
|
-
// src/draw/customShader.ts
|
|
1201
1278
|
var warnedUniforms = /* @__PURE__ */ new Set();
|
|
1202
1279
|
function warnOnce(key, message) {
|
|
1203
1280
|
if (warnedUniforms.has(key)) return;
|
|
1204
1281
|
warnedUniforms.add(key);
|
|
1205
|
-
|
|
1206
|
-
if (isDev) console.warn(message);
|
|
1282
|
+
if (isDev()) console.warn(message);
|
|
1207
1283
|
}
|
|
1208
1284
|
function warnOnceUniform(programId, name) {
|
|
1209
1285
|
warnOnce(
|
|
@@ -1216,8 +1292,7 @@ function setUniform(gl, loc, value, textureCache, nextTexUnit) {
|
|
|
1216
1292
|
const handle = value;
|
|
1217
1293
|
const entry = getTexture(handle.id);
|
|
1218
1294
|
if (!entry) {
|
|
1219
|
-
|
|
1220
|
-
if (isDev) console.warn(`weasel setUniform: TextureHandle "${handle.id}" not registered`);
|
|
1295
|
+
if (isDev()) console.warn(`weasel setUniform: TextureHandle "${handle.id}" not registered`);
|
|
1221
1296
|
return;
|
|
1222
1297
|
}
|
|
1223
1298
|
const unit = nextTexUnit.value++;
|
|
@@ -1234,8 +1309,7 @@ function setUniform(gl, loc, value, textureCache, nextTexUnit) {
|
|
|
1234
1309
|
} else if (value.length === 16) {
|
|
1235
1310
|
gl.uniformMatrix4fv(loc, false, value);
|
|
1236
1311
|
} else {
|
|
1237
|
-
|
|
1238
|
-
if (isDev) throw new TypeError(`weasel setUniform: Float32Array must be length 9 (mat3) or 16 (mat4), got ${value.length}`);
|
|
1312
|
+
if (isDev()) throw new TypeError(`weasel setUniform: Float32Array must be length 9 (mat3) or 16 (mat4), got ${value.length}`);
|
|
1239
1313
|
}
|
|
1240
1314
|
return;
|
|
1241
1315
|
}
|
|
@@ -1252,8 +1326,7 @@ function setUniform(gl, loc, value, textureCache, nextTexUnit) {
|
|
|
1252
1326
|
gl.uniform4fv(loc, arr);
|
|
1253
1327
|
break;
|
|
1254
1328
|
default: {
|
|
1255
|
-
|
|
1256
|
-
if (isDev) throw new TypeError(`weasel setUniform: array length ${arr.length} not supported`);
|
|
1329
|
+
if (isDev()) throw new TypeError(`weasel setUniform: array length ${arr.length} not supported`);
|
|
1257
1330
|
}
|
|
1258
1331
|
}
|
|
1259
1332
|
return;
|
|
@@ -1297,14 +1370,13 @@ function isArrayValue(value) {
|
|
|
1297
1370
|
return Array.isArray(value) || value instanceof Float32Array || value instanceof Int32Array || value instanceof Uint32Array;
|
|
1298
1371
|
}
|
|
1299
1372
|
function setUniformArray(gl, name, arr, value) {
|
|
1300
|
-
const isDev = typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true;
|
|
1301
1373
|
const upload = ARRAY_UPLOADS[arr.type];
|
|
1302
1374
|
if (!upload) {
|
|
1303
|
-
if (isDev) console.warn(`weasel setUniform: array uniform "${name}" has type ${arr.type}, which cannot be written as a whole`);
|
|
1375
|
+
if (isDev()) console.warn(`weasel setUniform: array uniform "${name}" has type ${arr.type}, which cannot be written as a whole`);
|
|
1304
1376
|
return;
|
|
1305
1377
|
}
|
|
1306
1378
|
if (value.length % upload.n !== 0 || value.length > upload.n * arr.size) {
|
|
1307
|
-
if (isDev) {
|
|
1379
|
+
if (isDev()) {
|
|
1308
1380
|
throw new TypeError(
|
|
1309
1381
|
`weasel setUniform: "${name}" is ${arr.type}[${arr.size}], so its value must hold a multiple of ${upload.n} numbers, at most ${upload.n * arr.size}; got ${value.length}`
|
|
1310
1382
|
);
|
|
@@ -1499,7 +1571,8 @@ function returnToRenderTarget(ctx, width, height, scissorWasOn) {
|
|
|
1499
1571
|
const target = ctx.renderTarget ?? null;
|
|
1500
1572
|
gl.bindFramebuffer(gl.FRAMEBUFFER, target ? target.fbo : null);
|
|
1501
1573
|
if (target) {
|
|
1502
|
-
|
|
1574
|
+
if (target.restore) target.restore();
|
|
1575
|
+
else gl.viewport(0, 0, width, height);
|
|
1503
1576
|
} else {
|
|
1504
1577
|
ctx.restoreTargetRect?.();
|
|
1505
1578
|
if (scissorWasOn) gl.enable(gl.SCISSOR_TEST);
|
|
@@ -1540,19 +1613,97 @@ function drawFullscreenQuad(ctx, program, texture, width, height, uniforms, comp
|
|
|
1540
1613
|
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
|
1541
1614
|
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
|
|
1542
1615
|
}
|
|
1616
|
+
|
|
1617
|
+
// src/draw/mesh.ts
|
|
1618
|
+
function flattenToleranceOf(ctx) {
|
|
1619
|
+
return worldFlattenTolerance(ctx.flattenTolerance, ctx.state.transform);
|
|
1620
|
+
}
|
|
1543
1621
|
function fillMesh(ctx, path) {
|
|
1544
|
-
|
|
1545
|
-
return tessellate(path, { flattenTolerance: ctx.flattenTolerance });
|
|
1546
|
-
}
|
|
1547
|
-
return getMesh(path);
|
|
1622
|
+
return getMesh(path, flattenToleranceOf(ctx));
|
|
1548
1623
|
}
|
|
1549
1624
|
function meshHandle(ctx, mesh) {
|
|
1550
|
-
if (ctx.flattenTolerance !== void 0) return ctx.meshCache.uploadTransient(mesh);
|
|
1551
1625
|
return ctx.meshCache.handleFor(mesh);
|
|
1552
1626
|
}
|
|
1553
1627
|
function fillMeshHandle(ctx, path) {
|
|
1554
1628
|
return meshHandle(ctx, fillMesh(ctx, path));
|
|
1555
1629
|
}
|
|
1630
|
+
|
|
1631
|
+
// src/draw/foldColor.ts
|
|
1632
|
+
function isIdentityColorMatrix(cm) {
|
|
1633
|
+
return cm === IDENTITY_COLOR_MATRIX || sameValues(IDENTITY_COLOR_MATRIX, cm);
|
|
1634
|
+
}
|
|
1635
|
+
var clamp01 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
1636
|
+
function foldColor(ctx, r, g, b, a) {
|
|
1637
|
+
const cm = ctx.state.colorMatrix;
|
|
1638
|
+
const alpha = ctx.state.alpha;
|
|
1639
|
+
if (isIdentityColorMatrix(cm)) return [r, g, b, a * alpha];
|
|
1640
|
+
return [
|
|
1641
|
+
clamp01(cm[0] * r + cm[1] * g + cm[2] * b + cm[3] * a + cm[4]),
|
|
1642
|
+
clamp01(cm[5] * r + cm[6] * g + cm[7] * b + cm[8] * a + cm[9]),
|
|
1643
|
+
clamp01(cm[10] * r + cm[11] * g + cm[12] * b + cm[13] * a + cm[14]),
|
|
1644
|
+
clamp01(cm[15] * r + cm[16] * g + cm[17] * b + cm[18] * a + cm[19]) * alpha
|
|
1645
|
+
];
|
|
1646
|
+
}
|
|
1647
|
+
var NO_TINT = [1, 1, 1, 1];
|
|
1648
|
+
var DIAGONAL = [0, 6, 12, 18];
|
|
1649
|
+
function textureTint(ctx) {
|
|
1650
|
+
const cm = ctx.state.colorMatrix;
|
|
1651
|
+
const alpha = ctx.state.alpha;
|
|
1652
|
+
if (isIdentityColorMatrix(cm)) return alpha === 1 ? NO_TINT : [1, 1, 1, alpha];
|
|
1653
|
+
for (let i = 0; i < 20; i++) {
|
|
1654
|
+
const v = cm[i];
|
|
1655
|
+
if (DIAGONAL.includes(i) ? v < 0 || v > 1 : v !== 0) return null;
|
|
1656
|
+
}
|
|
1657
|
+
return [cm[0], cm[6], cm[12], cm[18] * alpha];
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
// src/draw/batchMatrices.ts
|
|
1661
|
+
function matrixIndex(matrices, colorMatrix, alpha) {
|
|
1662
|
+
for (let i = 0; i < matrices.length; i++) {
|
|
1663
|
+
const m = matrices[i];
|
|
1664
|
+
if (m.alpha === alpha && (m.colorMatrix === colorMatrix || sameValues(m.colorMatrix, colorMatrix))) return i;
|
|
1665
|
+
}
|
|
1666
|
+
return matrices.length < (BATCH_MATRIX_SLOTS ) ? matrices.length : -1;
|
|
1667
|
+
}
|
|
1668
|
+
var MATRICES = new Float32Array(16 * BATCH_MATRIX_SLOTS);
|
|
1669
|
+
var BIASES = new Float32Array(4 * BATCH_MATRIX_SLOTS);
|
|
1670
|
+
var ALPHAS = new Float32Array(BATCH_MATRIX_SLOTS);
|
|
1671
|
+
var UPLOADED = /* @__PURE__ */ new WeakMap();
|
|
1672
|
+
function holds(held, matrices) {
|
|
1673
|
+
if (held.length < matrices.length) return false;
|
|
1674
|
+
for (let i = 0; i < matrices.length; i++) {
|
|
1675
|
+
const a = held[i];
|
|
1676
|
+
const b = matrices[i];
|
|
1677
|
+
if (a.alpha !== b.alpha) return false;
|
|
1678
|
+
if (a.colorMatrix !== b.colorMatrix && !sameValues(a.colorMatrix, b.colorMatrix)) return false;
|
|
1679
|
+
}
|
|
1680
|
+
return true;
|
|
1681
|
+
}
|
|
1682
|
+
function uploadBatchMatrices(ctx, prog, matrices) {
|
|
1683
|
+
let byProgram = UPLOADED.get(ctx);
|
|
1684
|
+
if (!byProgram) {
|
|
1685
|
+
byProgram = /* @__PURE__ */ new WeakMap();
|
|
1686
|
+
UPLOADED.set(ctx, byProgram);
|
|
1687
|
+
}
|
|
1688
|
+
const held = byProgram.get(prog);
|
|
1689
|
+
if (held !== void 0 && holds(held, matrices)) return;
|
|
1690
|
+
const n = matrices.length;
|
|
1691
|
+
for (let i = 0; i < n; i++) {
|
|
1692
|
+
const cm = matrices[i].colorMatrix;
|
|
1693
|
+
for (let row = 0; row < 4; row++) {
|
|
1694
|
+
for (let col = 0; col < 4; col++) MATRICES[16 * i + col * 4 + row] = cm[row * 5 + col];
|
|
1695
|
+
BIASES[4 * i + row] = cm[row * 5 + 4];
|
|
1696
|
+
}
|
|
1697
|
+
ALPHAS[i] = matrices[i].alpha;
|
|
1698
|
+
}
|
|
1699
|
+
const gl = ctx.gl;
|
|
1700
|
+
gl.uniformMatrix4fv(prog.uniform("u_colorMatrix"), false, MATRICES.subarray(0, 16 * n));
|
|
1701
|
+
gl.uniform4fv(prog.uniform("u_colorBias"), BIASES.subarray(0, 4 * n));
|
|
1702
|
+
gl.uniform1fv(prog.uniform("u_alpha"), ALPHAS.subarray(0, n));
|
|
1703
|
+
byProgram.set(prog, matrices.slice());
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
// src/draw/flushBatch.ts
|
|
1556
1707
|
var BATCH_MODEL = mat3.identity();
|
|
1557
1708
|
var SAMPLER_UNITS = new Int32Array(
|
|
1558
1709
|
Array.from({ length: BATCH_TEXTURE_SLOTS }, (_, i) => i)
|
|
@@ -1566,14 +1717,12 @@ function flushBatch(ctx) {
|
|
|
1566
1717
|
return;
|
|
1567
1718
|
}
|
|
1568
1719
|
const gl = ctx.gl;
|
|
1569
|
-
const prog = ctx.batchFill;
|
|
1720
|
+
const prog = staged.matrices.length > 1 ? ctx.batchFillMatrices ?? ctx.batchFill : ctx.batchFill;
|
|
1570
1721
|
gl.useProgram(prog.handle);
|
|
1571
1722
|
const indexCount = batch.uploadAndBind();
|
|
1572
1723
|
setProjAndModel(ctx, prog, BATCH_MODEL);
|
|
1573
1724
|
setColorUniform(ctx, prog, 1, 1, 1, 1);
|
|
1574
|
-
|
|
1575
|
-
gl.uniform1f(prog.uniform("u_synthBold"), staged.synthBold);
|
|
1576
|
-
setColorMatrixUniforms(ctx, prog, staged.colorMatrix);
|
|
1725
|
+
uploadBatchMatrices(ctx, prog, staged.matrices);
|
|
1577
1726
|
gl.activeTexture(gl.TEXTURE0);
|
|
1578
1727
|
gl.bindTexture(gl.TEXTURE_2D, ctx.whiteTexture);
|
|
1579
1728
|
for (let i = 0; i < staged.textures.length; i++) {
|
|
@@ -1584,16 +1733,13 @@ function flushBatch(ctx) {
|
|
|
1584
1733
|
FIELD_SCALES[2 * (i + 1) + 1] = entry.fieldScale[1];
|
|
1585
1734
|
continue;
|
|
1586
1735
|
}
|
|
1587
|
-
if (entry.kind === "ramps") {
|
|
1588
|
-
ctx.gradRamps.bind(i + 1);
|
|
1589
|
-
continue;
|
|
1590
|
-
}
|
|
1591
1736
|
ctx.imageCache.bind(entry.image, i + 1);
|
|
1592
1737
|
ctx.imageCache.setMagFilter(
|
|
1593
1738
|
entry.image,
|
|
1594
1739
|
entry.sampling === "nearest" ? gl.NEAREST : gl.LINEAR
|
|
1595
1740
|
);
|
|
1596
1741
|
}
|
|
1742
|
+
if (staged.ramps) ctx.gradRamps.bind(RAMP_SLOT);
|
|
1597
1743
|
gl.uniform1iv(prog.uniform("u_samplers"), SAMPLER_UNITS);
|
|
1598
1744
|
gl.uniform2fv(prog.uniform("u_fieldScale"), FIELD_SCALES);
|
|
1599
1745
|
applyClipTest(ctx, staged.clipDepth);
|
|
@@ -1605,22 +1751,21 @@ function flushBatch(ctx) {
|
|
|
1605
1751
|
}
|
|
1606
1752
|
|
|
1607
1753
|
// src/draw/clip.ts
|
|
1608
|
-
function enterGroupClip(ctx, clip) {
|
|
1754
|
+
function enterGroupClip(ctx, clip, transform = ctx.state.transform) {
|
|
1609
1755
|
const pending = ctx.clipStack.reduce((n, e) => n + (e.depth === 0 ? 1 : 0), 0);
|
|
1610
1756
|
if (ctx.clipDepth + pending + 1 > 7) {
|
|
1611
1757
|
throw new Error(
|
|
1612
1758
|
"weasel: clip nesting depth exceeded (max 7). You can't nest more than 7 levels of clipped containers in a single draw tree. Flatten the hierarchy or compose poses outside the scene graph."
|
|
1613
1759
|
);
|
|
1614
1760
|
}
|
|
1615
|
-
enterClip(ctx, clip);
|
|
1761
|
+
enterClip(ctx, clip, transform);
|
|
1616
1762
|
}
|
|
1617
|
-
function enterClip(ctx, path) {
|
|
1618
|
-
const transform = ctx.state.transform;
|
|
1763
|
+
function enterClip(ctx, path, transform) {
|
|
1619
1764
|
const rect = axisAlignedClipRect(path, transform);
|
|
1620
1765
|
if (rect === null) {
|
|
1621
1766
|
materializeClips(ctx);
|
|
1622
1767
|
const depth = ctx.clipDepth + 1;
|
|
1623
|
-
pushClip(ctx, path, depth);
|
|
1768
|
+
pushClip(ctx, path, depth, transform);
|
|
1624
1769
|
ctx.clipDepth = depth;
|
|
1625
1770
|
ctx.clipStack.push({ path, transform, rect: null, depth });
|
|
1626
1771
|
} else {
|
|
@@ -1734,6 +1879,287 @@ function popClip(ctx, path, oldDepth, model = ctx.state.transform) {
|
|
|
1734
1879
|
gl.colorMask(true, true, true, true);
|
|
1735
1880
|
gl.stencilMask(255);
|
|
1736
1881
|
}
|
|
1882
|
+
|
|
1883
|
+
// src/DrawCommand.ts
|
|
1884
|
+
var SPRITE_STRIDE = 9;
|
|
1885
|
+
var CULL_SLACK_PX = 1;
|
|
1886
|
+
var DEFAULT_MITER_LIMIT = 4;
|
|
1887
|
+
function cullDrawCommands(cmds, transform, rect) {
|
|
1888
|
+
return cullList(cmds, transform, slackBox(rect));
|
|
1889
|
+
}
|
|
1890
|
+
function cullList(cmds, m, box) {
|
|
1891
|
+
let out = null;
|
|
1892
|
+
for (let i = 0; i < cmds.length; i++) {
|
|
1893
|
+
const cmd = cmds[i];
|
|
1894
|
+
const kept = cullOne(cmd, m, box);
|
|
1895
|
+
if (kept === cmd) {
|
|
1896
|
+
out?.push(cmd);
|
|
1897
|
+
continue;
|
|
1898
|
+
}
|
|
1899
|
+
out ??= cmds.slice(0, i);
|
|
1900
|
+
if (kept) out.push(kept);
|
|
1901
|
+
}
|
|
1902
|
+
return out ?? cmds;
|
|
1903
|
+
}
|
|
1904
|
+
function cullOne(cmd, m, box) {
|
|
1905
|
+
switch (cmd.kind) {
|
|
1906
|
+
case "group": {
|
|
1907
|
+
if (cmd.effects && cmd.effects.length > 0) return cmd;
|
|
1908
|
+
if (isOwnComposite(cmd.composite)) return cmd;
|
|
1909
|
+
const inner = cmd.transform ? mat3.multiply(m, cmd.transform) : m;
|
|
1910
|
+
const children = cullList(cmd.children, inner, box);
|
|
1911
|
+
if (children === cmd.children) return cmd;
|
|
1912
|
+
return children.length === 0 ? null : { ...cmd, children };
|
|
1913
|
+
}
|
|
1914
|
+
case "path":
|
|
1915
|
+
case "image":
|
|
1916
|
+
case "sprites":
|
|
1917
|
+
return boxMisses(paintedBox(cmd, m), box) ? null : cmd;
|
|
1918
|
+
default:
|
|
1919
|
+
return cmd;
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
function paintedBox(cmd, m) {
|
|
1923
|
+
switch (cmd.kind) {
|
|
1924
|
+
case "path":
|
|
1925
|
+
return pathBox(cmd.path, cmd.stroke, m);
|
|
1926
|
+
case "image":
|
|
1927
|
+
return screenBox(cmd.x, cmd.y, cmd.x + cmd.w, cmd.y + cmd.h, m);
|
|
1928
|
+
case "sprites":
|
|
1929
|
+
return spritesBox(cmd.sprites, m);
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
function pathBox(path, stroke, m) {
|
|
1933
|
+
let minX, minY, maxX, maxY;
|
|
1934
|
+
if (path.kind === "rect") {
|
|
1935
|
+
minX = Math.min(path.x, path.x + path.width);
|
|
1936
|
+
maxX = Math.max(path.x, path.x + path.width);
|
|
1937
|
+
minY = Math.min(path.y, path.y + path.height);
|
|
1938
|
+
maxY = Math.max(path.y, path.y + path.height);
|
|
1939
|
+
} else if (path.kind === "polygon") {
|
|
1940
|
+
const c = path.coords;
|
|
1941
|
+
if (c.length < 2) return null;
|
|
1942
|
+
minX = maxX = c[0];
|
|
1943
|
+
minY = maxY = c[1];
|
|
1944
|
+
for (let i = 2; i + 1 < c.length; i += 2) {
|
|
1945
|
+
const x = c[i], y = c[i + 1];
|
|
1946
|
+
if (x < minX) minX = x;
|
|
1947
|
+
else if (x > maxX) maxX = x;
|
|
1948
|
+
if (y < minY) minY = y;
|
|
1949
|
+
else if (y > maxY) maxY = y;
|
|
1950
|
+
}
|
|
1951
|
+
} else {
|
|
1952
|
+
return null;
|
|
1953
|
+
}
|
|
1954
|
+
const reach = stroke ? strokeReach(stroke, m) : 0;
|
|
1955
|
+
return screenBox(minX - reach, minY - reach, maxX + reach, maxY + reach, m);
|
|
1956
|
+
}
|
|
1957
|
+
function strokeReach(stroke, m) {
|
|
1958
|
+
return strokeReachAt(stroke, mat3.meanScaleOf(m), mat3.minScaleOf(m));
|
|
1959
|
+
}
|
|
1960
|
+
function strokeReachAt(stroke, scale, least = scale) {
|
|
1961
|
+
const at = least > 0 ? least : scale;
|
|
1962
|
+
const base = resolveStrokeWidth(stroke.width ?? 1, typeof stroke.width === "object" ? at : scale);
|
|
1963
|
+
const width = widestStrokeWidth(base, stroke.vertexWidths);
|
|
1964
|
+
const spike = Math.max(stroke.miterLimit ?? DEFAULT_MITER_LIMIT, Math.SQRT2);
|
|
1965
|
+
const heads = markerReach(stroke);
|
|
1966
|
+
return Math.max(Math.abs(width) * spike, heads.world, resolveScreenLength({ px: heads.px }, at));
|
|
1967
|
+
}
|
|
1968
|
+
function spritesBox(sprites, m) {
|
|
1969
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
1970
|
+
for (let i = 0; i + SPRITE_STRIDE <= sprites.length; i += SPRITE_STRIDE) {
|
|
1971
|
+
const x0 = sprites[i], y0 = sprites[i + 1];
|
|
1972
|
+
const x1 = x0 + sprites[i + 2], y1 = y0 + sprites[i + 3];
|
|
1973
|
+
minX = Math.min(minX, x0, x1);
|
|
1974
|
+
maxX = Math.max(maxX, x0, x1);
|
|
1975
|
+
minY = Math.min(minY, y0, y1);
|
|
1976
|
+
maxY = Math.max(maxY, y0, y1);
|
|
1977
|
+
}
|
|
1978
|
+
if (minX === Infinity) return null;
|
|
1979
|
+
return screenBox(minX, minY, maxX, maxY, m);
|
|
1980
|
+
}
|
|
1981
|
+
function boundsMissRect(bounds, transform, rect) {
|
|
1982
|
+
const x0 = Math.min(bounds.x, bounds.x + bounds.width);
|
|
1983
|
+
const x1 = Math.max(bounds.x, bounds.x + bounds.width);
|
|
1984
|
+
const y0 = Math.min(bounds.y, bounds.y + bounds.height);
|
|
1985
|
+
const y1 = Math.max(bounds.y, bounds.y + bounds.height);
|
|
1986
|
+
return boxMisses(screenBox(x0, y0, x1, y1, transform), slackBox(rect));
|
|
1987
|
+
}
|
|
1988
|
+
function slackBox(rect) {
|
|
1989
|
+
return {
|
|
1990
|
+
minX: rect.x - CULL_SLACK_PX,
|
|
1991
|
+
minY: rect.y - CULL_SLACK_PX,
|
|
1992
|
+
maxX: rect.x + rect.width + CULL_SLACK_PX,
|
|
1993
|
+
maxY: rect.y + rect.height + CULL_SLACK_PX
|
|
1994
|
+
};
|
|
1995
|
+
}
|
|
1996
|
+
function screenBox(x0, y0, x1, y1, m) {
|
|
1997
|
+
const [ax, ay] = mat3.apply(m, x0, y0);
|
|
1998
|
+
const [bx, by] = mat3.apply(m, x1, y0);
|
|
1999
|
+
const [cx, cy] = mat3.apply(m, x1, y1);
|
|
2000
|
+
const [dx, dy] = mat3.apply(m, x0, y1);
|
|
2001
|
+
return {
|
|
2002
|
+
minX: Math.min(ax, bx, cx, dx),
|
|
2003
|
+
minY: Math.min(ay, by, cy, dy),
|
|
2004
|
+
maxX: Math.max(ax, bx, cx, dx),
|
|
2005
|
+
maxY: Math.max(ay, by, cy, dy)
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
function boxMisses(b, box) {
|
|
2009
|
+
return b !== null && (b.maxX < box.minX || b.minX > box.maxX || b.maxY < box.minY || b.minY > box.maxY);
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
// src/retain/contentBounds.ts
|
|
2013
|
+
var EMPTY = { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity };
|
|
2014
|
+
var ANYWHERE = { minX: -Infinity, minY: -Infinity, maxX: Infinity, maxY: Infinity };
|
|
2015
|
+
function contentBounds(children, transform, clips, scale, width, height) {
|
|
2016
|
+
let box = listBox(children, transform);
|
|
2017
|
+
for (const clip of clips) box = clipped(box, pathBox(clip.path, void 0, clip.transform));
|
|
2018
|
+
const x0 = Math.max(0, Math.floor((box.minX - CULL_SLACK_PX) * scale));
|
|
2019
|
+
const y0 = Math.max(0, Math.floor((box.minY - CULL_SLACK_PX) * scale));
|
|
2020
|
+
const x1 = Math.min(width, Math.ceil((box.maxX + CULL_SLACK_PX) * scale));
|
|
2021
|
+
const y1 = Math.min(height, Math.ceil((box.maxY + CULL_SLACK_PX) * scale));
|
|
2022
|
+
if ([x0, y0, x1, y1].some(Number.isNaN)) return { x: 0, y: 0, width, height };
|
|
2023
|
+
if (x1 <= x0 || y1 <= y0) return { x: 0, y: 0, width: 0, height: 0 };
|
|
2024
|
+
return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
|
|
2025
|
+
}
|
|
2026
|
+
function listBox(cmds, m) {
|
|
2027
|
+
let acc = EMPTY;
|
|
2028
|
+
for (const cmd of cmds) {
|
|
2029
|
+
acc = union(acc, commandBox(cmd, m));
|
|
2030
|
+
if (acc === ANYWHERE) break;
|
|
2031
|
+
}
|
|
2032
|
+
return acc;
|
|
2033
|
+
}
|
|
2034
|
+
function commandBox(cmd, m) {
|
|
2035
|
+
switch (cmd.kind) {
|
|
2036
|
+
case "group": {
|
|
2037
|
+
const inner = cmd.transform ? mat3.multiply(m, cmd.transform) : m;
|
|
2038
|
+
const box = cmd.effects && cmd.effects.length > 0 ? ANYWHERE : listBox(cmd.children, inner);
|
|
2039
|
+
return cmd.clip ? clipped(box, pathBox(cmd.clip, void 0, inner)) : box;
|
|
2040
|
+
}
|
|
2041
|
+
case "path":
|
|
2042
|
+
case "image":
|
|
2043
|
+
case "sprites":
|
|
2044
|
+
return paintedBox(cmd, m) ?? ANYWHERE;
|
|
2045
|
+
default:
|
|
2046
|
+
return ANYWHERE;
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
function union(a, b) {
|
|
2050
|
+
if (a === EMPTY || b === ANYWHERE) return b;
|
|
2051
|
+
if (b === EMPTY || a === ANYWHERE) return a;
|
|
2052
|
+
return {
|
|
2053
|
+
minX: Math.min(a.minX, b.minX),
|
|
2054
|
+
minY: Math.min(a.minY, b.minY),
|
|
2055
|
+
maxX: Math.max(a.maxX, b.maxX),
|
|
2056
|
+
maxY: Math.max(a.maxY, b.maxY)
|
|
2057
|
+
};
|
|
2058
|
+
}
|
|
2059
|
+
function clipped(a, clip) {
|
|
2060
|
+
if (!clip || a === EMPTY) return a;
|
|
2061
|
+
const box = {
|
|
2062
|
+
minX: Math.max(a.minX, clip.minX),
|
|
2063
|
+
minY: Math.max(a.minY, clip.minY),
|
|
2064
|
+
maxX: Math.min(a.maxX, clip.maxX),
|
|
2065
|
+
maxY: Math.min(a.maxY, clip.maxY)
|
|
2066
|
+
};
|
|
2067
|
+
return box.minX > box.maxX || box.minY > box.maxY ? EMPTY : box;
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
// src/draw/retained.ts
|
|
2071
|
+
var COMPOSITE_AS_DRAWN = { alpha: 1, colorMatrix: IDENTITY_COLOR_MATRIX };
|
|
2072
|
+
var ownPixelsOnly = /* @__PURE__ */ new WeakMap();
|
|
2073
|
+
function readsOnlyOwnPixels(children) {
|
|
2074
|
+
const known = ownPixelsOnly.get(children);
|
|
2075
|
+
if (known !== void 0) return known;
|
|
2076
|
+
let ok = true;
|
|
2077
|
+
for (const child of children) {
|
|
2078
|
+
if (child.kind !== "group") continue;
|
|
2079
|
+
if (isOwnComposite(child.composite) || !readsOnlyOwnPixels(child.children)) {
|
|
2080
|
+
ok = false;
|
|
2081
|
+
break;
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
ownPixelsOnly.set(children, ok);
|
|
2085
|
+
return ok;
|
|
2086
|
+
}
|
|
2087
|
+
function drawRetainedGroup(ctx, cmd, ownsSurface2, dispatch2) {
|
|
2088
|
+
const store = ctx.retained;
|
|
2089
|
+
const width = ctx.deviceWidth;
|
|
2090
|
+
const height = ctx.deviceHeight;
|
|
2091
|
+
if (!store || !width || !height) return false;
|
|
2092
|
+
if (ownsSurface2 || ctx.renderTarget || !readsOnlyOwnPixels(cmd.children)) {
|
|
2093
|
+
store.decline();
|
|
2094
|
+
return false;
|
|
2095
|
+
}
|
|
2096
|
+
const id = cmd.retain;
|
|
2097
|
+
ctx.state.push({ transform: cmd.transform, alpha: cmd.alpha, colorMatrix: cmd.colorMatrix });
|
|
2098
|
+
const clips = ctx.clipStack.map(({ path, transform }) => ({ path, transform }));
|
|
2099
|
+
if (cmd.clip) clips.push({ path: cmd.clip, transform: ctx.state.transform });
|
|
2100
|
+
const key = {
|
|
2101
|
+
children: cmd.children,
|
|
2102
|
+
transform: ctx.state.transform,
|
|
2103
|
+
alpha: ctx.state.alpha,
|
|
2104
|
+
colorMatrix: ctx.state.colorMatrix,
|
|
2105
|
+
viewMatrix: ctx.viewMatrix,
|
|
2106
|
+
clips
|
|
2107
|
+
};
|
|
2108
|
+
let pixels = store.lookup(id, width, height, key);
|
|
2109
|
+
if (!pixels) {
|
|
2110
|
+
const rect2 = contentBounds(cmd.children, ctx.state.transform, clips, width / ctx.widthCss, width, height);
|
|
2111
|
+
const draw = store.reserve(id, width, height, rect2);
|
|
2112
|
+
if (!draw) {
|
|
2113
|
+
ctx.state.pop();
|
|
2114
|
+
return false;
|
|
2115
|
+
}
|
|
2116
|
+
flushBatch(ctx);
|
|
2117
|
+
if (draw.fbo) drawInto(ctx, cmd.children, clips, draw.fbo, rect2, dispatch2);
|
|
2118
|
+
pixels = draw.commit(key);
|
|
2119
|
+
}
|
|
2120
|
+
ctx.state.pop();
|
|
2121
|
+
const { texture, rect } = pixels;
|
|
2122
|
+
if (!texture) return true;
|
|
2123
|
+
flushBatch(ctx);
|
|
2124
|
+
const composite = ctx.ensureProgram?.(compositeProgramId()) ?? null;
|
|
2125
|
+
if (composite && ctx.viewportWithin) {
|
|
2126
|
+
applyClipTest(ctx, 0);
|
|
2127
|
+
ctx.viewportWithin(rect);
|
|
2128
|
+
drawFullscreenQuad(ctx, composite, texture, rect.width, rect.height, void 0, COMPOSITE_AS_DRAWN);
|
|
2129
|
+
ctx.restoreTargetRect?.();
|
|
2130
|
+
}
|
|
2131
|
+
return true;
|
|
2132
|
+
}
|
|
2133
|
+
function drawInto(ctx, children, clips, fbo, rect, dispatch2) {
|
|
2134
|
+
const gl = ctx.gl;
|
|
2135
|
+
const width = ctx.deviceWidth;
|
|
2136
|
+
const height = ctx.deviceHeight;
|
|
2137
|
+
const parentClipDepth = ctx.clipDepth;
|
|
2138
|
+
const parentClipStack = ctx.clipStack;
|
|
2139
|
+
const parentBatchClip = ctx.drawBatch.clip;
|
|
2140
|
+
const scissorWasOn = gl.isEnabled(gl.SCISSOR_TEST);
|
|
2141
|
+
ctx.clipDepth = 0;
|
|
2142
|
+
ctx.clipStack = [];
|
|
2143
|
+
ctx.drawBatch.clip = null;
|
|
2144
|
+
const restore = () => {
|
|
2145
|
+
gl.viewport(-rect.x, rect.y + rect.height - height, width, height);
|
|
2146
|
+
gl.scissor(0, 0, rect.width, rect.height);
|
|
2147
|
+
gl.enable(gl.SCISSOR_TEST);
|
|
2148
|
+
};
|
|
2149
|
+
ctx.renderTarget = { fbo, restore };
|
|
2150
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
|
|
2151
|
+
restore();
|
|
2152
|
+
gl.stencilMask(255);
|
|
2153
|
+
gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
|
|
2154
|
+
for (const clip of clips) enterGroupClip(ctx, clip.path, clip.transform);
|
|
2155
|
+
for (const child of children) dispatch2(ctx, child);
|
|
2156
|
+
flushBatch(ctx);
|
|
2157
|
+
ctx.clipDepth = parentClipDepth;
|
|
2158
|
+
ctx.clipStack = parentClipStack;
|
|
2159
|
+
ctx.drawBatch.clip = parentBatchClip;
|
|
2160
|
+
ctx.renderTarget = null;
|
|
2161
|
+
returnToRenderTarget(ctx, width, height, scissorWasOn);
|
|
2162
|
+
}
|
|
1737
2163
|
var WORLD = {};
|
|
1738
2164
|
function resolveStrokeLengths(transform, cmd) {
|
|
1739
2165
|
const stroke = cmd.stroke;
|
|
@@ -1768,37 +2194,72 @@ function isPxMarker(ref) {
|
|
|
1768
2194
|
function resolvedMarker(ref, scale) {
|
|
1769
2195
|
return { ...ref, size: resolveMarkerSize(ref, 0, quantizeStrokeScale(ref.size.px, scale)) };
|
|
1770
2196
|
}
|
|
2197
|
+
var REMAPPED = /* @__PURE__ */ new WeakMap();
|
|
2198
|
+
function remapped(values, sources, stride) {
|
|
2199
|
+
let bySources = REMAPPED.get(values);
|
|
2200
|
+
if (bySources === void 0) {
|
|
2201
|
+
bySources = /* @__PURE__ */ new WeakMap();
|
|
2202
|
+
REMAPPED.set(values, bySources);
|
|
2203
|
+
}
|
|
2204
|
+
let out = bySources.get(sources);
|
|
2205
|
+
if (out === void 0) {
|
|
2206
|
+
out = remapPerAnchor(values, sources, stride);
|
|
2207
|
+
bySources.set(sources, out);
|
|
2208
|
+
}
|
|
2209
|
+
return out;
|
|
2210
|
+
}
|
|
2211
|
+
function withCornersResolved(cmd) {
|
|
2212
|
+
if (cmd.path.kind !== "polygon") return cmd;
|
|
2213
|
+
const { path, anchorSources: src } = resolveCornersWithSources(cmd.path);
|
|
2214
|
+
if (path === cmd.path) return cmd;
|
|
2215
|
+
const out = { ...cmd, path };
|
|
2216
|
+
if (src && cmd.vertexColors) out.vertexColors = remapped(cmd.vertexColors, src, 4);
|
|
2217
|
+
if (src && cmd.stroke && (cmd.stroke.vertexColors || cmd.stroke.vertexWidths)) {
|
|
2218
|
+
const { vertexColors, vertexWidths } = cmd.stroke;
|
|
2219
|
+
out.stroke = {
|
|
2220
|
+
...cmd.stroke,
|
|
2221
|
+
...vertexColors ? { vertexColors: remapped(vertexColors, src, 4) } : {},
|
|
2222
|
+
...vertexWidths ? { vertexWidths: remapped(vertexWidths, src, 1) } : {}
|
|
2223
|
+
};
|
|
2224
|
+
}
|
|
2225
|
+
return out;
|
|
2226
|
+
}
|
|
1771
2227
|
|
|
1772
|
-
// src/draw/
|
|
1773
|
-
function
|
|
1774
|
-
|
|
2228
|
+
// src/draw/batchRun.ts
|
|
2229
|
+
function slotForBitmap(staged, image, sampling) {
|
|
2230
|
+
for (let i = 0; i < staged.textures.length; i++) {
|
|
2231
|
+
const entry = staged.textures[i];
|
|
2232
|
+
if (entry.kind !== "bitmap" || entry.image !== image) continue;
|
|
2233
|
+
return entry.sampling === sampling ? i + 1 : -1;
|
|
2234
|
+
}
|
|
2235
|
+
return nextFreeSlot(staged);
|
|
1775
2236
|
}
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
if (!staged.foldsAlpha) return [r, g, b, a];
|
|
1780
|
-
const cm = ctx.state.colorMatrix;
|
|
1781
|
-
const alpha = ctx.state.alpha;
|
|
1782
|
-
if (isIdentityColorMatrix(cm)) return [r, g, b, a * alpha];
|
|
1783
|
-
return [
|
|
1784
|
-
clamp01(cm[0] * r + cm[1] * g + cm[2] * b + cm[3] * a + cm[4]),
|
|
1785
|
-
clamp01(cm[5] * r + cm[6] * g + cm[7] * b + cm[8] * a + cm[9]),
|
|
1786
|
-
clamp01(cm[10] * r + cm[11] * g + cm[12] * b + cm[13] * a + cm[14]),
|
|
1787
|
-
clamp01(cm[15] * r + cm[16] * g + cm[17] * b + cm[18] * a + cm[19]) * alpha
|
|
1788
|
-
];
|
|
2237
|
+
function slotForRamps(staged) {
|
|
2238
|
+
if (staged.ramps) return RAMP_SLOT;
|
|
2239
|
+
return nextFreeSlot(staged) < 0 ? -1 : RAMP_SLOT;
|
|
1789
2240
|
}
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
for (let i = 0; i < 20; i++) {
|
|
1795
|
-
const v = cm[i];
|
|
1796
|
-
if (DIAGONAL.includes(i) ? v < 0 || v > 1 : v !== 0) return null;
|
|
2241
|
+
function slotForAtlas(staged, id) {
|
|
2242
|
+
for (let i = 0; i < staged.textures.length; i++) {
|
|
2243
|
+
const entry = staged.textures[i];
|
|
2244
|
+
if (entry.kind === "atlas" && entry.id === id) return i + 1;
|
|
1797
2245
|
}
|
|
1798
|
-
return
|
|
2246
|
+
return nextFreeSlot(staged);
|
|
2247
|
+
}
|
|
2248
|
+
function nextFreeSlot(staged) {
|
|
2249
|
+
const taken = staged.textures.length + (staged.ramps ? 1 : 0);
|
|
2250
|
+
return taken + 1 < BATCH_TEXTURE_SLOTS ? staged.textures.length + 1 : -1;
|
|
2251
|
+
}
|
|
2252
|
+
function runTakes(ctx, staged, colorMatrix, alpha) {
|
|
2253
|
+
return staged.clipDepth === runClipDepth(ctx) && matrixIndex(staged.matrices, colorMatrix, alpha) >= 0;
|
|
1799
2254
|
}
|
|
1800
|
-
function
|
|
1801
|
-
|
|
2255
|
+
function openRun(ctx) {
|
|
2256
|
+
ctx.batchState ??= { clipDepth: runClipDepth(ctx), matrices: [], textures: [], ramps: false };
|
|
2257
|
+
return ctx.batchState;
|
|
2258
|
+
}
|
|
2259
|
+
function takeMatrix(staged, colorMatrix, alpha) {
|
|
2260
|
+
const index = matrixIndex(staged.matrices, colorMatrix, alpha);
|
|
2261
|
+
if (index === staged.matrices.length) staged.matrices.push({ colorMatrix, alpha });
|
|
2262
|
+
return index;
|
|
1802
2263
|
}
|
|
1803
2264
|
function batchPaint(ctx, fill, hasVColors) {
|
|
1804
2265
|
if (hasVColors) return void 0;
|
|
@@ -1887,39 +2348,6 @@ function spaceToDevice(ctx, units) {
|
|
|
1887
2348
|
}
|
|
1888
2349
|
|
|
1889
2350
|
// src/draw/batch.ts
|
|
1890
|
-
function stagedStateIsLive(ctx, staged, image, sampling, synthBold = 0, folds = false) {
|
|
1891
|
-
if (staged.clipDepth !== (ctx.clipStack.length === 0 ? ctx.clipDepth : runClipDepth(ctx))) return false;
|
|
1892
|
-
if (staged.synthBold !== synthBold) return false;
|
|
1893
|
-
if (!staged.foldsAlpha && staged.alpha !== ctx.state.alpha) return false;
|
|
1894
|
-
if (image !== void 0 && slotForBitmap(staged, image, sampling) < 0) return false;
|
|
1895
|
-
const colorMatrix = ctx.state.colorMatrix;
|
|
1896
|
-
if (staged.colorMatrix === colorMatrix || sameValues(staged.colorMatrix, colorMatrix)) return true;
|
|
1897
|
-
return folds && staged.foldsAlpha;
|
|
1898
|
-
}
|
|
1899
|
-
function slotForBitmap(staged, image, sampling) {
|
|
1900
|
-
for (let i = 0; i < staged.textures.length; i++) {
|
|
1901
|
-
const entry = staged.textures[i];
|
|
1902
|
-
if (entry.kind !== "bitmap" || entry.image !== image) continue;
|
|
1903
|
-
return entry.sampling === sampling ? i + 1 : -1;
|
|
1904
|
-
}
|
|
1905
|
-
return nextFreeSlot(staged);
|
|
1906
|
-
}
|
|
1907
|
-
function slotForRamps(staged) {
|
|
1908
|
-
for (let i = 0; i < staged.textures.length; i++) {
|
|
1909
|
-
if (staged.textures[i].kind === "ramps") return i + 1;
|
|
1910
|
-
}
|
|
1911
|
-
return nextFreeSlot(staged);
|
|
1912
|
-
}
|
|
1913
|
-
function slotForAtlas(staged, id) {
|
|
1914
|
-
for (let i = 0; i < staged.textures.length; i++) {
|
|
1915
|
-
const entry = staged.textures[i];
|
|
1916
|
-
if (entry.kind === "atlas" && entry.id === id) return i + 1;
|
|
1917
|
-
}
|
|
1918
|
-
return nextFreeSlot(staged);
|
|
1919
|
-
}
|
|
1920
|
-
function nextFreeSlot(staged) {
|
|
1921
|
-
return staged.textures.length + 1 < BATCH_TEXTURE_SLOTS ? staged.textures.length + 1 : -1;
|
|
1922
|
-
}
|
|
1923
2351
|
var MAX_BATCHED_MESH_VERTICES = 96;
|
|
1924
2352
|
var MAX_BATCHED_MESH_VERTICES_IN_RUN = 1024;
|
|
1925
2353
|
var meshBatching = {
|
|
@@ -1932,79 +2360,82 @@ function canBatchMesh(ctx, mesh) {
|
|
|
1932
2360
|
return mesh.vertices.length >> 1 <= cap;
|
|
1933
2361
|
}
|
|
1934
2362
|
function stageSolid(ctx, vertices) {
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
2363
|
+
const colorMatrix = IDENTITY_COLOR_MATRIX ;
|
|
2364
|
+
const alpha = 1 ;
|
|
2365
|
+
const open = ctx.batchState;
|
|
2366
|
+
if (open !== void 0 && !runTakes(ctx, open, colorMatrix, alpha)) flushBatch(ctx);
|
|
1938
2367
|
if (ctx.drawBatch.wouldOverflow(vertices)) flushBatch(ctx);
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
synthBold: 0,
|
|
1950
|
-
foldsAlpha,
|
|
1951
|
-
textures: []
|
|
1952
|
-
};
|
|
1953
|
-
}
|
|
1954
|
-
return ctx.batchState;
|
|
2368
|
+
const matrix = takeMatrix(openRun(ctx), colorMatrix, alpha);
|
|
2369
|
+
return packSlot(withMatrix(WHITE_SLOT, matrix), PAINT_MODE_PLAIN);
|
|
2370
|
+
}
|
|
2371
|
+
var UNTINTED = [1, 1, 1, 1];
|
|
2372
|
+
function texturedUnder(ctx, alphaInEntry) {
|
|
2373
|
+
const tint = textureTint(ctx);
|
|
2374
|
+
if (tint !== null) return { colorMatrix: IDENTITY_COLOR_MATRIX, alpha: 1, tint, slot: 0 };
|
|
2375
|
+
const colorMatrix = ctx.state.colorMatrix;
|
|
2376
|
+
const alpha = ctx.state.alpha;
|
|
2377
|
+
return alphaInEntry ? { colorMatrix, alpha, tint: UNTINTED, slot: 0 } : { colorMatrix, alpha: 1, tint: [1, 1, 1, alpha], slot: 0 };
|
|
1955
2378
|
}
|
|
1956
2379
|
function stageImage(ctx, image, sampling) {
|
|
1957
|
-
const
|
|
1958
|
-
|
|
2380
|
+
const under = texturedUnder(ctx, false);
|
|
2381
|
+
const open = ctx.batchState;
|
|
2382
|
+
if (open !== void 0 && (!runTakes(ctx, open, under.colorMatrix, under.alpha) || slotForBitmap(open, image, sampling) < 0)) {
|
|
1959
2383
|
flushBatch(ctx);
|
|
1960
2384
|
}
|
|
1961
2385
|
if (ctx.drawBatch.wouldOverflow(4)) flushBatch(ctx);
|
|
1962
|
-
const staged = openRun(ctx
|
|
2386
|
+
const staged = openRun(ctx);
|
|
1963
2387
|
let slot = slotForBitmap(staged, image, sampling);
|
|
1964
2388
|
if (slot > staged.textures.length) {
|
|
1965
2389
|
staged.textures.push({ kind: "bitmap", image, sampling });
|
|
1966
2390
|
slot = staged.textures.length;
|
|
1967
2391
|
}
|
|
1968
|
-
|
|
2392
|
+
under.slot = withMatrix(slot, takeMatrix(staged, under.colorMatrix, under.alpha));
|
|
2393
|
+
return under;
|
|
1969
2394
|
}
|
|
1970
|
-
function stageGlyphs(ctx, atlasId, fieldScale
|
|
1971
|
-
|
|
2395
|
+
function stageGlyphs(ctx, atlasId, fieldScale) {
|
|
2396
|
+
const colorMatrix = IDENTITY_COLOR_MATRIX ;
|
|
2397
|
+
const alpha = 1 ;
|
|
2398
|
+
const open = ctx.batchState;
|
|
2399
|
+
if (open !== void 0 && (!runTakes(ctx, open, colorMatrix, alpha) || slotForAtlas(open, atlasId) < 0)) {
|
|
1972
2400
|
flushBatch(ctx);
|
|
1973
2401
|
}
|
|
1974
2402
|
if (ctx.drawBatch.wouldOverflow(4)) flushBatch(ctx);
|
|
1975
|
-
const staged = openRun(ctx
|
|
1976
|
-
staged.synthBold = synthBold;
|
|
2403
|
+
const staged = openRun(ctx);
|
|
1977
2404
|
let slot = slotForAtlas(staged, atlasId);
|
|
1978
2405
|
if (slot > staged.textures.length) {
|
|
1979
2406
|
staged.textures.push({ kind: "atlas", id: atlasId, fieldScale });
|
|
1980
2407
|
slot = staged.textures.length;
|
|
1981
2408
|
}
|
|
1982
|
-
return
|
|
2409
|
+
return withMatrix(slot, takeMatrix(staged, colorMatrix, alpha));
|
|
1983
2410
|
}
|
|
1984
2411
|
function stageRamps(ctx, stops, space, vertices) {
|
|
1985
2412
|
if (ctx.gradRamps.wouldReshape(stops, space)) flushBatch(ctx);
|
|
1986
|
-
|
|
2413
|
+
const under = texturedUnder(ctx, true);
|
|
2414
|
+
const open = ctx.batchState;
|
|
2415
|
+
if (open !== void 0 && (!runTakes(ctx, open, under.colorMatrix, under.alpha) || slotForRamps(open) < 0)) {
|
|
1987
2416
|
flushBatch(ctx);
|
|
1988
2417
|
}
|
|
1989
2418
|
if (ctx.drawBatch.wouldOverflow(vertices)) flushBatch(ctx);
|
|
1990
2419
|
const staged = openRun(ctx);
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
slot
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
}
|
|
1998
|
-
function stagedAlpha(ctx, staged, opacity) {
|
|
1999
|
-
return (opacity ?? 1) * (staged.foldsAlpha ? ctx.state.alpha : 1);
|
|
2420
|
+
const slot = slotForRamps(staged);
|
|
2421
|
+
staged.ramps = true;
|
|
2422
|
+
return {
|
|
2423
|
+
slot: withMatrix(slot, takeMatrix(staged, under.colorMatrix, under.alpha)),
|
|
2424
|
+
tint: under.tint,
|
|
2425
|
+
rowV: ctx.gradRamps.rowV(ctx.gradRamps.upload(stops, space))
|
|
2426
|
+
};
|
|
2000
2427
|
}
|
|
2001
|
-
function
|
|
2428
|
+
function solidColor(paint) {
|
|
2002
2429
|
const [r, g, b, a] = resolveColor(paint.color);
|
|
2003
|
-
return
|
|
2430
|
+
return [r, g, b, a * (paint.opacity ?? 1)];
|
|
2431
|
+
}
|
|
2432
|
+
function stagedColor(ctx, paint) {
|
|
2433
|
+
const [r, g, b, a] = solidColor(paint);
|
|
2434
|
+
return foldColor(ctx, r, g, b, a);
|
|
2004
2435
|
}
|
|
2005
2436
|
function pushRect(ctx, rect, fill) {
|
|
2006
|
-
const
|
|
2007
|
-
const [r, g, b, a] = stagedColor(ctx,
|
|
2437
|
+
const slot = stageSolid(ctx, 4);
|
|
2438
|
+
const [r, g, b, a] = stagedColor(ctx, fill);
|
|
2008
2439
|
if (!ctx.drawBatch.pushRect(
|
|
2009
2440
|
rect.x,
|
|
2010
2441
|
rect.y,
|
|
@@ -2014,18 +2445,19 @@ function pushRect(ctx, rect, fill) {
|
|
|
2014
2445
|
r,
|
|
2015
2446
|
g,
|
|
2016
2447
|
b,
|
|
2017
|
-
a
|
|
2448
|
+
a,
|
|
2449
|
+
slot
|
|
2018
2450
|
)) retryUncut(ctx, rect, fill, pushRect);
|
|
2019
2451
|
}
|
|
2020
2452
|
function pushMesh(ctx, mesh, paint) {
|
|
2021
|
-
const
|
|
2022
|
-
const [r, g, b, a] = stagedColor(ctx,
|
|
2023
|
-
if (!ctx.drawBatch.pushMesh(mesh, ctx.state.transform, r, g, b, a)) {
|
|
2453
|
+
const slot = stageSolid(ctx, mesh.vertices.length >> 1);
|
|
2454
|
+
const [r, g, b, a] = stagedColor(ctx, paint);
|
|
2455
|
+
if (!ctx.drawBatch.pushMesh(mesh, ctx.state.transform, r, g, b, a, slot)) {
|
|
2024
2456
|
retryUncut(ctx, mesh, paint, pushMesh);
|
|
2025
2457
|
}
|
|
2026
2458
|
}
|
|
2027
2459
|
function pushRampRect(ctx, rect, paint) {
|
|
2028
|
-
const {
|
|
2460
|
+
const { slot, tint, rowV } = stageRamps(ctx, paint.stops, paint.space, 4);
|
|
2029
2461
|
const { uv, post } = rampVertices(paint, rowV);
|
|
2030
2462
|
if (!ctx.drawBatch.pushGradientRect(
|
|
2031
2463
|
rect.x,
|
|
@@ -2037,14 +2469,14 @@ function pushRampRect(ctx, rect, paint) {
|
|
|
2037
2469
|
post,
|
|
2038
2470
|
slot,
|
|
2039
2471
|
paint.mode,
|
|
2040
|
-
|
|
2041
|
-
1,
|
|
2042
|
-
|
|
2043
|
-
|
|
2472
|
+
tint[0],
|
|
2473
|
+
tint[1],
|
|
2474
|
+
tint[2],
|
|
2475
|
+
(paint.opacity ?? 1) * tint[3]
|
|
2044
2476
|
)) retryUncut(ctx, rect, paint, pushRampRect);
|
|
2045
2477
|
}
|
|
2046
2478
|
function pushRampMesh(ctx, mesh, paint) {
|
|
2047
|
-
const {
|
|
2479
|
+
const { slot, tint, rowV } = stageRamps(ctx, paint.stops, paint.space, mesh.vertices.length >> 1);
|
|
2048
2480
|
const { uv, post } = rampVertices(paint, rowV);
|
|
2049
2481
|
if (!ctx.drawBatch.pushGradientMesh(
|
|
2050
2482
|
mesh,
|
|
@@ -2053,10 +2485,10 @@ function pushRampMesh(ctx, mesh, paint) {
|
|
|
2053
2485
|
post,
|
|
2054
2486
|
slot,
|
|
2055
2487
|
paint.mode,
|
|
2056
|
-
|
|
2057
|
-
1,
|
|
2058
|
-
|
|
2059
|
-
|
|
2488
|
+
tint[0],
|
|
2489
|
+
tint[1],
|
|
2490
|
+
tint[2],
|
|
2491
|
+
(paint.opacity ?? 1) * tint[3]
|
|
2060
2492
|
)) retryUncut(ctx, mesh, paint, pushRampMesh);
|
|
2061
2493
|
}
|
|
2062
2494
|
function tryStageFill(ctx, mesh, paint) {
|
|
@@ -2070,7 +2502,9 @@ function tryStageFill(ctx, mesh, paint) {
|
|
|
2070
2502
|
}
|
|
2071
2503
|
var NO_FIELD = [0, 0];
|
|
2072
2504
|
function stageColorGlyphs(ctx, atlasId) {
|
|
2073
|
-
|
|
2505
|
+
const under = texturedUnder(ctx, false);
|
|
2506
|
+
const open = ctx.batchState;
|
|
2507
|
+
if (open !== void 0 && (!runTakes(ctx, open, under.colorMatrix, under.alpha) || slotForAtlas(open, atlasId) < 0)) {
|
|
2074
2508
|
flushBatch(ctx);
|
|
2075
2509
|
}
|
|
2076
2510
|
if (ctx.drawBatch.wouldOverflow(4)) flushBatch(ctx);
|
|
@@ -2080,7 +2514,8 @@ function stageColorGlyphs(ctx, atlasId) {
|
|
|
2080
2514
|
staged.textures.push({ kind: "atlas", id: atlasId, fieldScale: NO_FIELD });
|
|
2081
2515
|
slot = staged.textures.length;
|
|
2082
2516
|
}
|
|
2083
|
-
|
|
2517
|
+
under.slot = withMatrix(slot, takeMatrix(staged, under.colorMatrix, under.alpha));
|
|
2518
|
+
return under;
|
|
2084
2519
|
}
|
|
2085
2520
|
function patternTileSpace(spaceInverse, origin, transform) {
|
|
2086
2521
|
const [a, b, c, d] = transform ?? [1, 0, 0, 1];
|
|
@@ -2088,8 +2523,6 @@ function patternTileSpace(spaceInverse, origin, transform) {
|
|
|
2088
2523
|
const unplace = mat3.invert(placement);
|
|
2089
2524
|
return unplace && mat3.multiply(unplace, spaceInverse);
|
|
2090
2525
|
}
|
|
2091
|
-
|
|
2092
|
-
// src/draw/paintBind.ts
|
|
2093
2526
|
function paintBindContext(ctx) {
|
|
2094
2527
|
const cached = BIND_CONTEXTS.get(ctx);
|
|
2095
2528
|
if (cached) return cached;
|
|
@@ -2129,8 +2562,7 @@ function bindPattern(ctx, prog, fill, model) {
|
|
|
2129
2562
|
const tex = fill.texture;
|
|
2130
2563
|
const entry = getTexture(tex.id);
|
|
2131
2564
|
if (!entry) {
|
|
2132
|
-
|
|
2133
|
-
if (isDev) console.warn(`weasel: pattern TextureHandle "${tex.id}" not registered`);
|
|
2565
|
+
if (isDev()) console.warn(`weasel: pattern TextureHandle "${tex.id}" not registered`);
|
|
2134
2566
|
return null;
|
|
2135
2567
|
}
|
|
2136
2568
|
const inverse = gradientSpaceInverse(ctx, fill.units);
|
|
@@ -2142,8 +2574,10 @@ function bindPattern(ctx, prog, fill, model) {
|
|
|
2142
2574
|
gl.useProgram(prog.handle);
|
|
2143
2575
|
setProjAndModel(ctx, prog, model);
|
|
2144
2576
|
setColorMatrixUniforms(ctx, prog);
|
|
2145
|
-
gl.uniformMatrix3fv(prog.uniform("u_worldInv"), false, mat3.toUniform(tileSpace));
|
|
2146
2577
|
const [tw, th] = textureSize(entry.source);
|
|
2578
|
+
tileSpace[6] -= Math.floor(tileSpace[6] / tw) * tw;
|
|
2579
|
+
tileSpace[7] -= Math.floor(tileSpace[7] / th) * th;
|
|
2580
|
+
gl.uniformMatrix3fv(prog.uniform("u_worldInv"), false, mat3.toUniform(tileSpace));
|
|
2147
2581
|
gl.uniform2f(prog.uniform("u_tileSize"), tw, th);
|
|
2148
2582
|
ctx.textureCache.bind(tex.id, 0);
|
|
2149
2583
|
gl.uniform1i(prog.uniform("u_sampler"), 0);
|
|
@@ -2159,11 +2593,13 @@ function textureSize(source) {
|
|
|
2159
2593
|
function bindGradient(ctx, prog, fill, model) {
|
|
2160
2594
|
const inverse = gradientSpaceInverse(ctx, fill.units);
|
|
2161
2595
|
if (!inverse) return null;
|
|
2596
|
+
const about = fill.kind === "linear-gradient" ? fill.from : fill.center;
|
|
2597
|
+
const aboutStart = mat3.multiply(mat3.translated(mat3.identity(), -about.x, -about.y), inverse);
|
|
2162
2598
|
const gl = ctx.gl;
|
|
2163
2599
|
const row = ctx.gradRamps.upload(fill.stops, fill.interpolate ?? "rgb");
|
|
2164
2600
|
gl.useProgram(prog.handle);
|
|
2165
2601
|
setProjAndModel(ctx, prog, model);
|
|
2166
|
-
gl.uniformMatrix3fv(prog.uniform("u_worldInv"), false, mat3.toUniform(
|
|
2602
|
+
gl.uniformMatrix3fv(prog.uniform("u_worldInv"), false, mat3.toUniform(aboutStart));
|
|
2167
2603
|
setColorMatrixUniforms(ctx, prog);
|
|
2168
2604
|
ctx.gradRamps.bind(0);
|
|
2169
2605
|
gl.uniform1i(prog.uniform("u_ramp"), 0);
|
|
@@ -2175,21 +2611,18 @@ function bindGradient(ctx, prog, fill, model) {
|
|
|
2175
2611
|
const dx = fill.to.x - fill.from.x;
|
|
2176
2612
|
const dy = fill.to.y - fill.from.y;
|
|
2177
2613
|
const len = Math.hypot(dx, dy) || 1;
|
|
2178
|
-
gl.uniform2f(prog.uniform("u_gradP0"), fill.from.x, fill.from.y);
|
|
2179
2614
|
gl.uniform2f(prog.uniform("u_gradDir"), dx / len, dy / len);
|
|
2180
2615
|
gl.uniform1f(prog.uniform("u_gradLen"), len);
|
|
2181
2616
|
gl.uniform1f(prog.uniform("u_gradRadius"), 0);
|
|
2182
2617
|
gl.uniform1f(prog.uniform("u_gradAngle"), 0);
|
|
2183
2618
|
} else if (fill.kind === "radial-gradient") {
|
|
2184
2619
|
gl.uniform1i(prog.uniform("u_gradKind"), 1);
|
|
2185
|
-
gl.uniform2f(prog.uniform("u_gradP0"), fill.center.x, fill.center.y);
|
|
2186
2620
|
gl.uniform2f(prog.uniform("u_gradDir"), 0, 0);
|
|
2187
2621
|
gl.uniform1f(prog.uniform("u_gradLen"), 0);
|
|
2188
2622
|
gl.uniform1f(prog.uniform("u_gradRadius"), fill.radius);
|
|
2189
2623
|
gl.uniform1f(prog.uniform("u_gradAngle"), 0);
|
|
2190
2624
|
} else {
|
|
2191
2625
|
gl.uniform1i(prog.uniform("u_gradKind"), 2);
|
|
2192
|
-
gl.uniform2f(prog.uniform("u_gradP0"), fill.center.x, fill.center.y);
|
|
2193
2626
|
gl.uniform2f(prog.uniform("u_gradDir"), 0, 0);
|
|
2194
2627
|
gl.uniform1f(prog.uniform("u_gradLen"), 0);
|
|
2195
2628
|
gl.uniform1f(prog.uniform("u_gradRadius"), 0);
|
|
@@ -2199,7 +2632,8 @@ function bindGradient(ctx, prog, fill, model) {
|
|
|
2199
2632
|
}
|
|
2200
2633
|
|
|
2201
2634
|
// src/draw/pathPaint.ts
|
|
2202
|
-
function drawPath(ctx,
|
|
2635
|
+
function drawPath(ctx, given) {
|
|
2636
|
+
const cmd = withCornersResolved(given);
|
|
2203
2637
|
if (cmd.fill?.paint) drawPathFill(ctx, cmd, cmd.fill.paint);
|
|
2204
2638
|
if (cmd.stroke?.paint) drawPathStroke(ctx, cmd);
|
|
2205
2639
|
}
|
|
@@ -2339,7 +2773,7 @@ function drawPathStroke(ctx, rawCmd) {
|
|
|
2339
2773
|
drawPathStrokeUnclipped(ctx, cmd, spaces);
|
|
2340
2774
|
}
|
|
2341
2775
|
const width = resolveStrokeWidth(stroke.width ?? 1, 1);
|
|
2342
|
-
const heads = cachedMarkerCommands(cmd.path, rawCmd.stroke, stroke, width, ctx
|
|
2776
|
+
const heads = cachedMarkerCommands(cmd.path, rawCmd.stroke, stroke, width, flattenToleranceOf(ctx), spaces);
|
|
2343
2777
|
for (const marker of heads) drawPath(ctx, marker);
|
|
2344
2778
|
}
|
|
2345
2779
|
function drawPathStrokeUnclipped(ctx, cmd, spaces) {
|
|
@@ -2347,7 +2781,7 @@ function drawPathStrokeUnclipped(ctx, cmd, spaces) {
|
|
|
2347
2781
|
const paint = stroke.paint;
|
|
2348
2782
|
const isSolid2 = paint.kind === void 0 || paint.kind === "solid";
|
|
2349
2783
|
const solid = paint;
|
|
2350
|
-
const mesh = strokeMesh(cmd.path, stroke, ctx
|
|
2784
|
+
const mesh = strokeMesh(cmd.path, stroke, flattenToleranceOf(ctx), spaces);
|
|
2351
2785
|
if (mesh.indices.length === 0) return;
|
|
2352
2786
|
const hasVColors = !!(stroke.vertexColors && stroke.vertexColors.length > 0);
|
|
2353
2787
|
const batched = batchPaint(ctx, paint, hasVColors);
|
|
@@ -2386,7 +2820,7 @@ var DOUBLED_VERTEX_WIDTHS = /* @__PURE__ */ new WeakMap();
|
|
|
2386
2820
|
function doubledVertexWidths(widths) {
|
|
2387
2821
|
let doubled = DOUBLED_VERTEX_WIDTHS.get(widths);
|
|
2388
2822
|
if (doubled === void 0) {
|
|
2389
|
-
doubled = widths.map((w) => w
|
|
2823
|
+
doubled = widths.map((w) => scaleVertexWidth(w, 2));
|
|
2390
2824
|
DOUBLED_VERTEX_WIDTHS.set(widths, doubled);
|
|
2391
2825
|
}
|
|
2392
2826
|
return doubled;
|
|
@@ -2404,7 +2838,7 @@ function drawPathStrokeStenciled(ctx, cmd, align, spaces) {
|
|
|
2404
2838
|
};
|
|
2405
2839
|
const useVColor = !!(stroke.vertexColors && stroke.vertexColors.length > 0);
|
|
2406
2840
|
const fillHandle = fillMeshHandle(ctx, cmd.path);
|
|
2407
|
-
const ribbonMesh = strokeMesh(cmd.path, widerStroke, ctx
|
|
2841
|
+
const ribbonMesh = strokeMesh(cmd.path, widerStroke, flattenToleranceOf(ctx), spaces);
|
|
2408
2842
|
if (ribbonMesh.indices.length === 0) return;
|
|
2409
2843
|
const ribbonHandle = useVColor ? ctx.meshCache.uploadTransient(ribbonMesh) : ctx.meshCache.uploadRecurring(ribbonMesh);
|
|
2410
2844
|
const gl = ctx.gl;
|
|
@@ -2514,21 +2948,38 @@ function mergeGlyphMeshes(group, dx, dy, meshFor) {
|
|
|
2514
2948
|
}
|
|
2515
2949
|
return { vertices, indices };
|
|
2516
2950
|
}
|
|
2951
|
+
|
|
2952
|
+
// src/draw/modelScale.ts
|
|
2953
|
+
function modelScale(m) {
|
|
2954
|
+
const sx = Math.hypot(m[0], m[1]);
|
|
2955
|
+
const sy = Math.hypot(m[3], m[4]);
|
|
2956
|
+
return Math.sqrt(sx * sy) || 1;
|
|
2957
|
+
}
|
|
2958
|
+
|
|
2959
|
+
// src/draw/colorGlyphs.ts
|
|
2517
2960
|
function drawColorGlyphs(ctx, group, dx, dy) {
|
|
2518
2961
|
const fill = group.fill?.paint;
|
|
2519
2962
|
if (fill === void 0 || group.quads.length === 0) return;
|
|
2520
2963
|
if (!syncColorPageTexture(ctx.textureCache, group.page)) return;
|
|
2521
|
-
const opacity = isSolidPaint(fill) ?
|
|
2522
|
-
const
|
|
2964
|
+
const opacity = isSolidPaint(fill) ? solidColor(fill)[3] : 1;
|
|
2965
|
+
const baseId = colorPageTextureId(group.page);
|
|
2523
2966
|
const batch = ctx.drawBatch;
|
|
2524
2967
|
const m = ctx.state.transform;
|
|
2525
|
-
|
|
2526
|
-
|
|
2968
|
+
const devicePerCss = ctx.deviceWidth && ctx.widthCss ? ctx.deviceWidth / ctx.widthCss : 1;
|
|
2969
|
+
const toDevice = modelScale(m) * devicePerCss;
|
|
2970
|
+
let atlasId = baseId;
|
|
2971
|
+
let { slot, tint } = stageColorGlyphs(ctx, atlasId);
|
|
2527
2972
|
const restage = () => {
|
|
2528
|
-
({
|
|
2529
|
-
alpha = opacity * (staged.foldsAlpha ? ctx.state.alpha : 1);
|
|
2973
|
+
({ slot, tint } = stageColorGlyphs(ctx, atlasId));
|
|
2530
2974
|
};
|
|
2531
|
-
for (const
|
|
2975
|
+
for (const base of group.quads) {
|
|
2976
|
+
const sharp = sharper(ctx, base, toDevice);
|
|
2977
|
+
const q = sharp?.quad ?? base;
|
|
2978
|
+
const id = sharp?.atlasId ?? baseId;
|
|
2979
|
+
if (id !== atlasId) {
|
|
2980
|
+
atlasId = id;
|
|
2981
|
+
restage();
|
|
2982
|
+
}
|
|
2532
2983
|
if (batch.wouldOverflow(4)) {
|
|
2533
2984
|
flushBatch(ctx);
|
|
2534
2985
|
restage();
|
|
@@ -2544,8 +2995,11 @@ function drawColorGlyphs(ctx, group, dx, dy) {
|
|
|
2544
2995
|
q.v0,
|
|
2545
2996
|
q.u1,
|
|
2546
2997
|
q.v1,
|
|
2547
|
-
|
|
2548
|
-
slot
|
|
2998
|
+
opacity * tint[3],
|
|
2999
|
+
slot,
|
|
3000
|
+
tint[0],
|
|
3001
|
+
tint[1],
|
|
3002
|
+
tint[2]
|
|
2549
3003
|
)) {
|
|
2550
3004
|
cut = enterUncut(ctx);
|
|
2551
3005
|
restage();
|
|
@@ -2556,6 +3010,31 @@ function drawColorGlyphs(ctx, group, dx, dy) {
|
|
|
2556
3010
|
}
|
|
2557
3011
|
}
|
|
2558
3012
|
}
|
|
3013
|
+
function sharper(ctx, q, toDevice) {
|
|
3014
|
+
const char = q.colorGlyph;
|
|
3015
|
+
if (!char || char.width === 0) return null;
|
|
3016
|
+
const texel = (q.x1 - q.x0) / char.width;
|
|
3017
|
+
const sharp = sharperColorGlyph(char, texel * toDevice);
|
|
3018
|
+
if (!sharp || !syncSharpColorPageTexture(ctx.textureCache, sharp.glyph.page)) return null;
|
|
3019
|
+
const g = sharp.glyph;
|
|
3020
|
+
const k = 2 ** sharp.level;
|
|
3021
|
+
const x0 = q.x0 + (g.xoffset - char.xoffset) * texel;
|
|
3022
|
+
const y0 = q.y0 + (g.yoffset - char.yoffset) * texel;
|
|
3023
|
+
return {
|
|
3024
|
+
quad: {
|
|
3025
|
+
x0,
|
|
3026
|
+
y0,
|
|
3027
|
+
x1: x0 + g.width / k * texel,
|
|
3028
|
+
y1: y0 + g.height / k * texel,
|
|
3029
|
+
u0: g.x / DYNAMIC_PAGE_SIZE,
|
|
3030
|
+
v0: g.y / DYNAMIC_PAGE_SIZE,
|
|
3031
|
+
u1: (g.x + g.width) / DYNAMIC_PAGE_SIZE,
|
|
3032
|
+
v1: (g.y + g.height) / DYNAMIC_PAGE_SIZE,
|
|
3033
|
+
baselineY: q.baselineY
|
|
3034
|
+
},
|
|
3035
|
+
atlasId: sharpColorPageTextureId(g.page)
|
|
3036
|
+
};
|
|
3037
|
+
}
|
|
2559
3038
|
|
|
2560
3039
|
// src/draw/scratchPass.ts
|
|
2561
3040
|
var UNIT_QUAD = {
|
|
@@ -2633,11 +3112,6 @@ function bindScratchPaint(ctx, prog, painted) {
|
|
|
2633
3112
|
|
|
2634
3113
|
// src/draw/text.ts
|
|
2635
3114
|
var OUTLINE_MIN_SCREEN_PX = 48;
|
|
2636
|
-
function modelScale(m) {
|
|
2637
|
-
const sx = Math.hypot(m[0], m[1]);
|
|
2638
|
-
const sy = Math.hypot(m[3], m[4]);
|
|
2639
|
-
return Math.sqrt(sx * sy) || 1;
|
|
2640
|
-
}
|
|
2641
3115
|
function drawText(ctx, cmd) {
|
|
2642
3116
|
const style = resolveTextStyle(cmd.style);
|
|
2643
3117
|
const lineHeight = style.lineHeight;
|
|
@@ -2691,17 +3165,22 @@ function drawTextDecorations(ctx, decorations, dx, dy) {
|
|
|
2691
3165
|
drawOutlineMesh(ctx, paint, ruleMesh(d.x0 + dx, d.y0 + dy, d.x1 + dx, d.y1 + dy));
|
|
2692
3166
|
continue;
|
|
2693
3167
|
}
|
|
2694
|
-
const [r, g, b, a] = isSolidPaint(paint) ?
|
|
3168
|
+
const [r, g, b, a] = isSolidPaint(paint) ? solidColor(paint) : [0, 0, 0, 1];
|
|
2695
3169
|
let cut;
|
|
2696
3170
|
for (; ; ) {
|
|
2697
|
-
const
|
|
3171
|
+
const slot = stageSolid(ctx, 4);
|
|
3172
|
+
const rgba = foldColor(ctx, r, g, b, a);
|
|
2698
3173
|
if (ctx.drawBatch.pushRect(
|
|
2699
3174
|
d.x0 + dx,
|
|
2700
3175
|
d.y0 + dy,
|
|
2701
3176
|
d.x1 - d.x0,
|
|
2702
3177
|
d.y1 - d.y0,
|
|
2703
3178
|
ctx.state.transform,
|
|
2704
|
-
|
|
3179
|
+
rgba[0],
|
|
3180
|
+
rgba[1],
|
|
3181
|
+
rgba[2],
|
|
3182
|
+
rgba[3],
|
|
3183
|
+
slot
|
|
2705
3184
|
)) break;
|
|
2706
3185
|
cut = enterUncut(ctx);
|
|
2707
3186
|
}
|
|
@@ -2714,7 +3193,6 @@ function ruleMesh(x0, y0, x1, y1) {
|
|
|
2714
3193
|
indices: new Uint32Array([0, 1, 2, 0, 2, 3])
|
|
2715
3194
|
};
|
|
2716
3195
|
}
|
|
2717
|
-
var SYNTH_BOLD_AMOUNT = 0.08;
|
|
2718
3196
|
function drawTextGroup(ctx, group, dx, dy) {
|
|
2719
3197
|
if (group.source === "canvas") {
|
|
2720
3198
|
if (!syncDynamicPageTexture(ctx.textureCache, group.page)) return;
|
|
@@ -2740,16 +3218,15 @@ function drawTextGroup(ctx, group, dx, dy) {
|
|
|
2740
3218
|
drawPaintedGlyphs(ctx, group, dx, dy, paintKind, atlasId, mode, bold, tanItalic, fieldScale);
|
|
2741
3219
|
return;
|
|
2742
3220
|
}
|
|
2743
|
-
const color = isSolidPaint(fill) ?
|
|
3221
|
+
const color = isSolidPaint(fill) ? solidColor(fill) : [0, 0, 0, 1];
|
|
2744
3222
|
const batch = ctx.drawBatch;
|
|
2745
3223
|
const m = ctx.state.transform;
|
|
2746
|
-
let
|
|
2747
|
-
|
|
3224
|
+
let slot = stageGlyphs(ctx, atlasId, fieldScale);
|
|
3225
|
+
const rgba = foldColor(ctx, color[0], color[1], color[2], color[3]);
|
|
2748
3226
|
for (const q of group.quads) {
|
|
2749
3227
|
if (batch.wouldOverflow(4)) {
|
|
2750
3228
|
flushBatch(ctx);
|
|
2751
|
-
|
|
2752
|
-
rgba = foldColor(ctx, staged, color[0], color[1], color[2], color[3]);
|
|
3229
|
+
slot = stageGlyphs(ctx, atlasId, fieldScale);
|
|
2753
3230
|
}
|
|
2754
3231
|
let cut;
|
|
2755
3232
|
while (!batch.pushGlyph(
|
|
@@ -2769,16 +3246,15 @@ function drawTextGroup(ctx, group, dx, dy) {
|
|
|
2769
3246
|
rgba[2],
|
|
2770
3247
|
rgba[3],
|
|
2771
3248
|
slot,
|
|
2772
|
-
mode
|
|
3249
|
+
mode,
|
|
3250
|
+
bold
|
|
2773
3251
|
)) {
|
|
2774
3252
|
cut = enterUncut(ctx);
|
|
2775
|
-
|
|
2776
|
-
rgba = foldColor(ctx, staged, color[0], color[1], color[2], color[3]);
|
|
3253
|
+
slot = stageGlyphs(ctx, atlasId, fieldScale);
|
|
2777
3254
|
}
|
|
2778
3255
|
if (cut !== void 0) {
|
|
2779
3256
|
leaveUncut(ctx, cut);
|
|
2780
|
-
|
|
2781
|
-
rgba = foldColor(ctx, staged, color[0], color[1], color[2], color[3]);
|
|
3257
|
+
slot = stageGlyphs(ctx, atlasId, fieldScale);
|
|
2782
3258
|
}
|
|
2783
3259
|
}
|
|
2784
3260
|
}
|
|
@@ -2835,7 +3311,8 @@ function drawPaintedGlyphs(ctx, group, dx, dy, kind, atlasId, mode, bold, tanIta
|
|
|
2835
3311
|
1,
|
|
2836
3312
|
1,
|
|
2837
3313
|
GLYPH_PAINT_ATLAS_UNIT,
|
|
2838
|
-
mode
|
|
3314
|
+
mode,
|
|
3315
|
+
0
|
|
2839
3316
|
);
|
|
2840
3317
|
}
|
|
2841
3318
|
drawStagedGlyphs(ctx);
|
|
@@ -2872,9 +3349,6 @@ function glyphScreenBounds(group, dx, dy, tanItalic, m) {
|
|
|
2872
3349
|
return b;
|
|
2873
3350
|
}
|
|
2874
3351
|
|
|
2875
|
-
// src/DrawCommand.ts
|
|
2876
|
-
var SPRITE_STRIDE = 9;
|
|
2877
|
-
|
|
2878
3352
|
// src/draw/image.ts
|
|
2879
3353
|
var MINIFIED = 2;
|
|
2880
3354
|
function minifiedLimits(ctx) {
|
|
@@ -2891,7 +3365,7 @@ function drawImage(ctx, cmd) {
|
|
|
2891
3365
|
const sh = cmd.source?.h ?? cmd.image.height;
|
|
2892
3366
|
if (isMinified(minifiedLimits(ctx), sw, sh, cmd.w, cmd.h)) ctx.imageCache.buildMipmaps(cmd.image);
|
|
2893
3367
|
}
|
|
2894
|
-
const {
|
|
3368
|
+
const { slot, tint } = stageImage(ctx, cmd.image, cmd.sampling ?? "linear");
|
|
2895
3369
|
const src = cmd.source;
|
|
2896
3370
|
let u0 = 0, v0 = 0, u1 = 1, v1 = 1;
|
|
2897
3371
|
if (src) {
|
|
@@ -2921,10 +3395,8 @@ function drawImage(ctx, cmd) {
|
|
|
2921
3395
|
v0,
|
|
2922
3396
|
u1,
|
|
2923
3397
|
v1,
|
|
2924
|
-
// Group alpha rides
|
|
2925
|
-
|
|
2926
|
-
// as well would apply it twice.
|
|
2927
|
-
(cmd.opacity ?? 1) * (staged.foldsAlpha ? ctx.state.alpha : 1) * tint[3],
|
|
3398
|
+
// Group alpha rides here with the opacity, in the tint's fourth channel.
|
|
3399
|
+
(cmd.opacity ?? 1) * tint[3],
|
|
2928
3400
|
slot,
|
|
2929
3401
|
tint[0],
|
|
2930
3402
|
tint[1],
|
|
@@ -2945,17 +3417,15 @@ function drawSprites(ctx, cmd) {
|
|
|
2945
3417
|
}
|
|
2946
3418
|
}
|
|
2947
3419
|
const sampling = cmd.sampling ?? "linear";
|
|
2948
|
-
let {
|
|
3420
|
+
let { slot, tint } = stageImage(ctx, cmd.image, sampling);
|
|
2949
3421
|
const batch = ctx.drawBatch;
|
|
2950
3422
|
const m = ctx.state.transform;
|
|
2951
|
-
let groupAlpha = (staged.foldsAlpha ? ctx.state.alpha : 1) * tint[3];
|
|
2952
3423
|
const tw = cmd.image.width;
|
|
2953
3424
|
const th = cmd.image.height;
|
|
2954
3425
|
for (let s = 0, i = 0; s < count; s++, i += SPRITE_STRIDE) {
|
|
2955
3426
|
if (batch.wouldOverflow(4)) {
|
|
2956
3427
|
flushBatch(ctx);
|
|
2957
|
-
({
|
|
2958
|
-
groupAlpha = (staged.foldsAlpha ? ctx.state.alpha : 1) * tint[3];
|
|
3428
|
+
({ slot, tint } = stageImage(ctx, cmd.image, sampling));
|
|
2959
3429
|
}
|
|
2960
3430
|
const sx = data[i + 4], sy = data[i + 5], sw = data[i + 6], sh = data[i + 7];
|
|
2961
3431
|
let cut;
|
|
@@ -2969,20 +3439,18 @@ function drawSprites(ctx, cmd) {
|
|
|
2969
3439
|
sy / th,
|
|
2970
3440
|
(sx + sw) / tw,
|
|
2971
3441
|
(sy + sh) / th,
|
|
2972
|
-
data[i + 8] *
|
|
3442
|
+
data[i + 8] * tint[3],
|
|
2973
3443
|
slot,
|
|
2974
3444
|
tint[0],
|
|
2975
3445
|
tint[1],
|
|
2976
3446
|
tint[2]
|
|
2977
3447
|
)) {
|
|
2978
3448
|
cut = enterUncut(ctx);
|
|
2979
|
-
({
|
|
2980
|
-
groupAlpha = (staged.foldsAlpha ? ctx.state.alpha : 1) * tint[3];
|
|
3449
|
+
({ slot, tint } = stageImage(ctx, cmd.image, sampling));
|
|
2981
3450
|
}
|
|
2982
3451
|
if (cut !== void 0) {
|
|
2983
3452
|
leaveUncut(ctx, cut);
|
|
2984
|
-
({
|
|
2985
|
-
groupAlpha = (staged.foldsAlpha ? ctx.state.alpha : 1) * tint[3];
|
|
3453
|
+
({ slot, tint } = stageImage(ctx, cmd.image, sampling));
|
|
2986
3454
|
}
|
|
2987
3455
|
}
|
|
2988
3456
|
}
|
|
@@ -3079,6 +3547,7 @@ function ownsSurface(cmd) {
|
|
|
3079
3547
|
return cmd.isolate === true || isOwnComposite(cmd.composite) || cmd.effects !== void 0 && cmd.effects.length > 0;
|
|
3080
3548
|
}
|
|
3081
3549
|
function drawGroup(ctx, cmd) {
|
|
3550
|
+
if (cmd.retain !== void 0 && drawRetainedGroup(ctx, cmd, ownsSurface(cmd), dispatch)) return;
|
|
3082
3551
|
if (ownsSurface(cmd) && ctx.effectTargets && ctx.deviceWidth && ctx.deviceHeight) {
|
|
3083
3552
|
if (cmd.clip) {
|
|
3084
3553
|
ctx.state.push({ transform: cmd.transform });
|
|
@@ -3242,6 +3711,253 @@ var EffectTargets = class {
|
|
|
3242
3711
|
}
|
|
3243
3712
|
};
|
|
3244
3713
|
|
|
3714
|
+
// src/retain/retainedKey.ts
|
|
3715
|
+
function keepKey(key) {
|
|
3716
|
+
return {
|
|
3717
|
+
...key,
|
|
3718
|
+
transform: key.transform.slice(),
|
|
3719
|
+
colorMatrix: key.colorMatrix.slice(),
|
|
3720
|
+
viewMatrix: key.viewMatrix?.slice(),
|
|
3721
|
+
clips: key.clips.map((c) => ({ path: c.path, transform: c.transform.slice() }))
|
|
3722
|
+
};
|
|
3723
|
+
}
|
|
3724
|
+
function sameKey(a, b) {
|
|
3725
|
+
return a.children === b.children && a.alpha === b.alpha && sameFloats(a.transform, b.transform) && sameFloats(a.colorMatrix, b.colorMatrix) && (a.viewMatrix === void 0 ? b.viewMatrix === void 0 : b.viewMatrix !== void 0 && sameFloats(a.viewMatrix, b.viewMatrix)) && sameClips(a.clips, b.clips);
|
|
3726
|
+
}
|
|
3727
|
+
function sameClips(a, b) {
|
|
3728
|
+
if (a.length !== b.length) return false;
|
|
3729
|
+
for (let i = 0; i < a.length; i++) {
|
|
3730
|
+
if (a[i].path !== b[i].path || !sameFloats(a[i].transform, b[i].transform)) return false;
|
|
3731
|
+
}
|
|
3732
|
+
return true;
|
|
3733
|
+
}
|
|
3734
|
+
function sameFloats(a, b) {
|
|
3735
|
+
if (a.length !== b.length) return false;
|
|
3736
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
3737
|
+
return true;
|
|
3738
|
+
}
|
|
3739
|
+
function sameStamp(a, b) {
|
|
3740
|
+
if (a.length !== b.length) return false;
|
|
3741
|
+
for (let i = 0; i < a.length; i++) if (!Object.is(a[i], b[i])) return false;
|
|
3742
|
+
return true;
|
|
3743
|
+
}
|
|
3744
|
+
|
|
3745
|
+
// src/retain/surfaceGl.ts
|
|
3746
|
+
function createSurfaceTexture(gl, width, height) {
|
|
3747
|
+
const texture = gl.createTexture();
|
|
3748
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
3749
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
3750
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
3751
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
3752
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
3753
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
3754
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
3755
|
+
const fbo = gl.createFramebuffer();
|
|
3756
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
|
|
3757
|
+
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
|
|
3758
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
3759
|
+
return { texture, fbo };
|
|
3760
|
+
}
|
|
3761
|
+
function deleteSurfaceTexture(gl, surface) {
|
|
3762
|
+
gl.deleteFramebuffer(surface.fbo);
|
|
3763
|
+
gl.deleteTexture(surface.texture);
|
|
3764
|
+
}
|
|
3765
|
+
function createScratch(gl, width, height, samples) {
|
|
3766
|
+
const storage = (format) => {
|
|
3767
|
+
const rb = gl.createRenderbuffer();
|
|
3768
|
+
gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
|
|
3769
|
+
if (samples > 0) {
|
|
3770
|
+
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, format, width, height);
|
|
3771
|
+
} else {
|
|
3772
|
+
gl.renderbufferStorage(gl.RENDERBUFFER, format, width, height);
|
|
3773
|
+
}
|
|
3774
|
+
return rb;
|
|
3775
|
+
};
|
|
3776
|
+
const color = storage(gl.RGBA8);
|
|
3777
|
+
const stencil = storage(gl.DEPTH24_STENCIL8);
|
|
3778
|
+
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
|
|
3779
|
+
const fbo = gl.createFramebuffer();
|
|
3780
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
|
|
3781
|
+
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, color);
|
|
3782
|
+
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, stencil);
|
|
3783
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
3784
|
+
return { fbo, color, stencil, width, height };
|
|
3785
|
+
}
|
|
3786
|
+
function deleteScratch(gl, scratch) {
|
|
3787
|
+
gl.deleteFramebuffer(scratch.fbo);
|
|
3788
|
+
gl.deleteRenderbuffer(scratch.color);
|
|
3789
|
+
gl.deleteRenderbuffer(scratch.stencil);
|
|
3790
|
+
}
|
|
3791
|
+
function resolveInto(gl, scratch, target, width, height) {
|
|
3792
|
+
const scissor = gl.isEnabled(gl.SCISSOR_TEST);
|
|
3793
|
+
gl.disable(gl.SCISSOR_TEST);
|
|
3794
|
+
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, scratch.fbo);
|
|
3795
|
+
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, target.fbo);
|
|
3796
|
+
gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
|
|
3797
|
+
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
|
3798
|
+
if (scissor) gl.enable(gl.SCISSOR_TEST);
|
|
3799
|
+
}
|
|
3800
|
+
|
|
3801
|
+
// src/retain/RetainedSurfaces.ts
|
|
3802
|
+
var RetainedSurfaces = class {
|
|
3803
|
+
gl;
|
|
3804
|
+
budget;
|
|
3805
|
+
samples;
|
|
3806
|
+
surfaces = /* @__PURE__ */ new Map();
|
|
3807
|
+
/** Shared: retained draws never nest. */
|
|
3808
|
+
scratch = null;
|
|
3809
|
+
width = 0;
|
|
3810
|
+
height = 0;
|
|
3811
|
+
frame = 0;
|
|
3812
|
+
stamp = [];
|
|
3813
|
+
composited = 0;
|
|
3814
|
+
redrawn = 0;
|
|
3815
|
+
declined = 0;
|
|
3816
|
+
constructor(gl, budget) {
|
|
3817
|
+
this.gl = gl;
|
|
3818
|
+
this.budget = budget;
|
|
3819
|
+
const samples = gl.getParameter(gl.SAMPLES);
|
|
3820
|
+
this.samples = typeof samples === "number" && samples > 1 ? samples : 0;
|
|
3821
|
+
}
|
|
3822
|
+
/** GPU memory held now, scratch buffer included. */
|
|
3823
|
+
get bytes() {
|
|
3824
|
+
return this.keptBytes() + (this.scratch ? this.scratchBytes(this.scratch.width, this.scratch.height) : 0);
|
|
3825
|
+
}
|
|
3826
|
+
/** Change the budget. Lowered below what the surfaces hold, every surface
|
|
3827
|
+
* goes, and groups are drawn again within the new one. */
|
|
3828
|
+
setBudget(budget) {
|
|
3829
|
+
this.budget = budget;
|
|
3830
|
+
if (this.keptBytes() > budget) this.releaseAll();
|
|
3831
|
+
}
|
|
3832
|
+
/** Open a frame. `stamp` holds what every surface depends on beyond its
|
|
3833
|
+
* key — resource generations the renderer watches — compared entry by
|
|
3834
|
+
* entry. */
|
|
3835
|
+
beginFrame(stamp) {
|
|
3836
|
+
this.frame++;
|
|
3837
|
+
this.stamp = stamp;
|
|
3838
|
+
this.composited = 0;
|
|
3839
|
+
this.redrawn = 0;
|
|
3840
|
+
this.declined = 0;
|
|
3841
|
+
}
|
|
3842
|
+
/** `id`'s pixels if they were drawn under `key`, in a drawing buffer
|
|
3843
|
+
* `width × height` device pixels. */
|
|
3844
|
+
lookup(id, width, height, key) {
|
|
3845
|
+
this.fitTo(width, height);
|
|
3846
|
+
const surface = this.surfaces.get(id);
|
|
3847
|
+
if (!surface?.key || !sameKey(surface.key, key) || !sameStamp(surface.stamp, this.stamp)) return null;
|
|
3848
|
+
surface.frame = this.frame;
|
|
3849
|
+
this.composited++;
|
|
3850
|
+
return pixelsOf(surface);
|
|
3851
|
+
}
|
|
3852
|
+
/** Somewhere to draw `id` this frame, covering `rect` of a drawing buffer
|
|
3853
|
+
* `width × height` device pixels, or `null` when that would take the total
|
|
3854
|
+
* past the budget. */
|
|
3855
|
+
reserve(id, width, height, rect) {
|
|
3856
|
+
this.fitTo(width, height);
|
|
3857
|
+
const existing = this.surfaces.get(id);
|
|
3858
|
+
const empty = rect.width === 0 || rect.height === 0;
|
|
3859
|
+
const held = this.scratch;
|
|
3860
|
+
const scratchW = Math.max(held?.width ?? 0, empty ? 0 : rect.width);
|
|
3861
|
+
const scratchH = Math.max(held?.height ?? 0, empty ? 0 : rect.height);
|
|
3862
|
+
const after = this.keptBytes() + rectBytes(rect) - (existing ? rectBytes(existing.rect) : 0);
|
|
3863
|
+
if (after > this.budget) {
|
|
3864
|
+
this.declined++;
|
|
3865
|
+
return null;
|
|
3866
|
+
}
|
|
3867
|
+
const surface = existing ?? { gl: null, rect, key: null, stamp: [], frame: this.frame };
|
|
3868
|
+
this.surfaces.set(id, surface);
|
|
3869
|
+
this.fitSurface(surface, rect);
|
|
3870
|
+
surface.frame = this.frame;
|
|
3871
|
+
this.redrawn++;
|
|
3872
|
+
const scratch = surface.gl ? this.scratchFor(scratchW, scratchH) : null;
|
|
3873
|
+
return {
|
|
3874
|
+
fbo: scratch?.fbo ?? null,
|
|
3875
|
+
commit: (key) => {
|
|
3876
|
+
if (scratch && surface.gl) resolveInto(this.gl, scratch, surface.gl, surface.rect.width, surface.rect.height);
|
|
3877
|
+
surface.key = keepKey(key);
|
|
3878
|
+
surface.stamp = this.stamp;
|
|
3879
|
+
return pixelsOf(surface);
|
|
3880
|
+
}
|
|
3881
|
+
};
|
|
3882
|
+
}
|
|
3883
|
+
/** Count a group drawn straight through for want of a surface. */
|
|
3884
|
+
decline() {
|
|
3885
|
+
this.declined++;
|
|
3886
|
+
}
|
|
3887
|
+
/** Free every surface no group asked for this frame, and the scratch
|
|
3888
|
+
* buffer once this frame redrew nothing or it is larger than every surface
|
|
3889
|
+
* left needs. */
|
|
3890
|
+
endFrame() {
|
|
3891
|
+
let needW = 0;
|
|
3892
|
+
let needH = 0;
|
|
3893
|
+
for (const [id, surface] of this.surfaces) {
|
|
3894
|
+
if (surface.frame === this.frame) {
|
|
3895
|
+
needW = Math.max(needW, surface.rect.width);
|
|
3896
|
+
needH = Math.max(needH, surface.rect.height);
|
|
3897
|
+
continue;
|
|
3898
|
+
}
|
|
3899
|
+
this.deleteSurface(surface);
|
|
3900
|
+
this.surfaces.delete(id);
|
|
3901
|
+
}
|
|
3902
|
+
const oversized = this.scratch && (this.scratch.width > needW || this.scratch.height > needH);
|
|
3903
|
+
if (this.redrawn === 0 || oversized) this.dropScratch();
|
|
3904
|
+
}
|
|
3905
|
+
/** Drop everything: on dispose, and on context loss, where the handles are
|
|
3906
|
+
* already gone and only the bookkeeping has to go. */
|
|
3907
|
+
releaseAll() {
|
|
3908
|
+
for (const surface of this.surfaces.values()) this.deleteSurface(surface);
|
|
3909
|
+
this.surfaces.clear();
|
|
3910
|
+
this.dropScratch();
|
|
3911
|
+
}
|
|
3912
|
+
fitTo(width, height) {
|
|
3913
|
+
if (width === this.width && height === this.height) return;
|
|
3914
|
+
this.releaseAll();
|
|
3915
|
+
this.width = width;
|
|
3916
|
+
this.height = height;
|
|
3917
|
+
}
|
|
3918
|
+
keptBytes() {
|
|
3919
|
+
let n = 0;
|
|
3920
|
+
for (const surface of this.surfaces.values()) n += rectBytes(surface.rect);
|
|
3921
|
+
return n;
|
|
3922
|
+
}
|
|
3923
|
+
scratchBytes(width, height) {
|
|
3924
|
+
return width * height * Math.max(1, this.samples) * 8;
|
|
3925
|
+
}
|
|
3926
|
+
/** The scratch buffer at `width × height`, reallocated if it is not. */
|
|
3927
|
+
scratchFor(width, height) {
|
|
3928
|
+
const held = this.scratch;
|
|
3929
|
+
if (held && held.width === width && held.height === height) return held;
|
|
3930
|
+
this.dropScratch();
|
|
3931
|
+
return this.scratch = createScratch(this.gl, width, height, this.samples);
|
|
3932
|
+
}
|
|
3933
|
+
/** Give `surface` a texture the size of `rect`, or none for an empty one,
|
|
3934
|
+
* keeping one that is already that size. */
|
|
3935
|
+
fitSurface(surface, rect) {
|
|
3936
|
+
const was = surface.rect;
|
|
3937
|
+
surface.rect = { ...rect };
|
|
3938
|
+
if (surface.gl && was.width === rect.width && was.height === rect.height) return;
|
|
3939
|
+
this.deleteSurface(surface);
|
|
3940
|
+
if (rect.width > 0 && rect.height > 0) surface.gl = createSurfaceTexture(this.gl, rect.width, rect.height);
|
|
3941
|
+
}
|
|
3942
|
+
deleteSurface(surface) {
|
|
3943
|
+
if (surface.gl) deleteSurfaceTexture(this.gl, surface.gl);
|
|
3944
|
+
surface.gl = null;
|
|
3945
|
+
}
|
|
3946
|
+
dropScratch() {
|
|
3947
|
+
if (this.scratch) deleteScratch(this.gl, this.scratch);
|
|
3948
|
+
this.scratch = null;
|
|
3949
|
+
}
|
|
3950
|
+
};
|
|
3951
|
+
function pixelsOf(surface) {
|
|
3952
|
+
return { texture: surface.gl?.texture ?? null, rect: surface.rect };
|
|
3953
|
+
}
|
|
3954
|
+
function rectBytes(rect) {
|
|
3955
|
+
return rect.width * rect.height * 4;
|
|
3956
|
+
}
|
|
3957
|
+
|
|
3958
|
+
// src/rendererTypes.ts
|
|
3959
|
+
var DEFAULT_RETAIN_BUDGET = 256 * 1024 * 1024;
|
|
3960
|
+
|
|
3245
3961
|
// src/shaders/extractUniformNames.ts
|
|
3246
3962
|
var QUALIFIERS = /* @__PURE__ */ new Set([
|
|
3247
3963
|
"lowp",
|
|
@@ -3473,6 +4189,7 @@ function compileCustomProgram(gl, src) {
|
|
|
3473
4189
|
program.lookupAttributes(CUSTOM_ATTRIBUTES);
|
|
3474
4190
|
return program;
|
|
3475
4191
|
}
|
|
4192
|
+
var NO_RETAINED = { composited: 0, redrawn: 0, declined: 0, bytes: 0 };
|
|
3476
4193
|
function now() {
|
|
3477
4194
|
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
3478
4195
|
}
|
|
@@ -3483,10 +4200,13 @@ var GLYPH_PAINT_SOURCES = {
|
|
|
3483
4200
|
};
|
|
3484
4201
|
var WeaselRenderer = class {
|
|
3485
4202
|
gl;
|
|
3486
|
-
stats = { drawCalls: 0, ms: 0, spans: [] };
|
|
4203
|
+
stats = { drawCalls: 0, ms: 0, spans: [], retained: NO_RETAINED };
|
|
4204
|
+
/** Built on the first frame that draws with each, so a one-shot render of
|
|
4205
|
+
* plain shapes builds the batch program alone. */
|
|
3487
4206
|
pathFill;
|
|
3488
4207
|
pathFillVColor;
|
|
3489
4208
|
batchFill;
|
|
4209
|
+
batchFillMatrices;
|
|
3490
4210
|
gradFill;
|
|
3491
4211
|
patternFill;
|
|
3492
4212
|
/** Compiled on first use: only atlas text under a texture paint needs one. */
|
|
@@ -3517,6 +4237,10 @@ var WeaselRenderer = class {
|
|
|
3517
4237
|
/** Offscreen buffers for group effects. Allocates nothing until a group
|
|
3518
4238
|
* with effects asks, so a canvas without them pays no memory. */
|
|
3519
4239
|
effectTargets;
|
|
4240
|
+
retained;
|
|
4241
|
+
/** Advanced when a program is compiled again over one already drawn with,
|
|
4242
|
+
* which leaves every retained surface drawn by the old one stale. */
|
|
4243
|
+
programEpoch = 0;
|
|
3520
4244
|
paintScratch;
|
|
3521
4245
|
imageMinification;
|
|
3522
4246
|
flattenTolerance;
|
|
@@ -3539,6 +4263,7 @@ var WeaselRenderer = class {
|
|
|
3539
4263
|
if (!gl) throw new Error("WeaselRenderer: WebGL2 not available");
|
|
3540
4264
|
this.gl = gl;
|
|
3541
4265
|
this.effectTargets = new EffectTargets(this.gl);
|
|
4266
|
+
this.retained = new RetainedSurfaces(this.gl, opts.retainBudget ?? DEFAULT_RETAIN_BUDGET);
|
|
3542
4267
|
this.paintScratch = new PaintScratch(this.gl);
|
|
3543
4268
|
const attrs = typeof this.gl.getContextAttributes === "function" ? this.gl.getContextAttributes() : null;
|
|
3544
4269
|
if (attrs && attrs.stencil === false) {
|
|
@@ -3566,21 +4291,12 @@ var WeaselRenderer = class {
|
|
|
3566
4291
|
}
|
|
3567
4292
|
this.applyGlState();
|
|
3568
4293
|
this.applyTarget();
|
|
3569
|
-
this.pathFill = new
|
|
3570
|
-
this.
|
|
3571
|
-
this.
|
|
3572
|
-
this.
|
|
3573
|
-
this.
|
|
3574
|
-
this.
|
|
3575
|
-
this.batchFill = new ShaderProgram(this.gl, BATCH_VERT_SRC, BATCH_FRAG_SRC);
|
|
3576
|
-
this.batchFill.lookupUniforms(BATCH_FILL_UNIFORMS);
|
|
3577
|
-
this.batchFill.lookupAttributes(BATCH_FILL_ATTRIBUTES);
|
|
3578
|
-
this.gradFill = new ShaderProgram(this.gl, GRAD_VERT_SRC, GRAD_FRAG_SRC);
|
|
3579
|
-
this.gradFill.lookupUniforms(GRAD_FILL_UNIFORMS);
|
|
3580
|
-
this.gradFill.lookupAttributes(GRAD_FILL_ATTRIBUTES);
|
|
3581
|
-
this.patternFill = new ShaderProgram(this.gl, PATTERN_VERT_SRC, PATTERN_FRAG_SRC);
|
|
3582
|
-
this.patternFill.lookupUniforms(PATTERN_FILL_UNIFORMS);
|
|
3583
|
-
this.patternFill.lookupAttributes(PATTERN_FILL_ATTRIBUTES);
|
|
4294
|
+
this.pathFill = new LazyProgram(this.gl, PATH_FILL_SPEC);
|
|
4295
|
+
this.pathFillVColor = new LazyProgram(this.gl, PATH_FILL_VCOLOR_SPEC);
|
|
4296
|
+
this.gradFill = new LazyProgram(this.gl, GRAD_FILL_SPEC);
|
|
4297
|
+
this.patternFill = new LazyProgram(this.gl, PATTERN_FILL_SPEC);
|
|
4298
|
+
this.batchFillMatrices = new LazyProgram(this.gl, BATCH_FILL_MATRICES_SPEC);
|
|
4299
|
+
this.batchFill = createBatchFill(this.gl);
|
|
3584
4300
|
this.meshCache = new GLMeshCache(this.gl);
|
|
3585
4301
|
this.textureCache = new GLTextureCache(this.gl);
|
|
3586
4302
|
this.imageCache = new GLImageCache(this.gl, this.imageMinification);
|
|
@@ -3589,6 +4305,9 @@ var WeaselRenderer = class {
|
|
|
3589
4305
|
this.drawBatch = new DrawBatch(this.gl, this.batchFill);
|
|
3590
4306
|
this.whiteTexture = createWhiteTexture(this.gl);
|
|
3591
4307
|
}
|
|
4308
|
+
lazyPrograms() {
|
|
4309
|
+
return [this.pathFill, this.pathFillVColor, this.gradFill, this.patternFill, this.batchFillMatrices];
|
|
4310
|
+
}
|
|
3592
4311
|
uploadQuadGeometry() {
|
|
3593
4312
|
const gl = this.gl;
|
|
3594
4313
|
this.quadVbo = gl.createBuffer();
|
|
@@ -3616,15 +4335,16 @@ var WeaselRenderer = class {
|
|
|
3616
4335
|
if (this.disposed) return;
|
|
3617
4336
|
const src = getProgramSource(handle.id);
|
|
3618
4337
|
if (!src) {
|
|
3619
|
-
throw new Error(
|
|
3620
|
-
`WeaselRenderer.registerProgram: program "${handle.id}" not found in source registry. Call the module-level registerProgram() first.`
|
|
3621
|
-
);
|
|
4338
|
+
throw new Error(`WeaselRenderer.registerProgram: program "${handle.id}" not found in source registry. Call the module-level registerProgram() first.`);
|
|
3622
4339
|
}
|
|
3623
4340
|
this.installProgram(handle.id, src, compileCustomProgram(this.gl, src));
|
|
3624
4341
|
}
|
|
3625
4342
|
installProgram(id, src, program) {
|
|
3626
4343
|
const previous = this.programRegistry.get(id);
|
|
3627
|
-
if (previous)
|
|
4344
|
+
if (previous) {
|
|
4345
|
+
this.gl.deleteProgram(previous.handle);
|
|
4346
|
+
this.programEpoch++;
|
|
4347
|
+
}
|
|
3628
4348
|
this.programRegistry.set(id, program);
|
|
3629
4349
|
this.programSources.set(id, src);
|
|
3630
4350
|
}
|
|
@@ -3696,28 +4416,28 @@ var WeaselRenderer = class {
|
|
|
3696
4416
|
const oy = this.target?.origin.y ?? 0;
|
|
3697
4417
|
const x = Math.round(ox * this.dpr);
|
|
3698
4418
|
const y = Math.round(oy * this.dpr);
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
y,
|
|
3702
|
-
width: Math.round((ox + this.widthCss) * this.dpr) - x,
|
|
3703
|
-
height: Math.round((oy + this.heightCss) * this.dpr) - y
|
|
3704
|
-
};
|
|
4419
|
+
const width = Math.round((ox + this.widthCss) * this.dpr) - x;
|
|
4420
|
+
return { x, y, width, height: Math.round((oy + this.heightCss) * this.dpr) - y };
|
|
3705
4421
|
}
|
|
3706
4422
|
/** Viewport and scissor for this frame. Re-applied inside `render()` because
|
|
3707
4423
|
* a co-tenant on the same context moves both between our frames. */
|
|
3708
4424
|
applyTarget() {
|
|
3709
4425
|
const gl = this.gl;
|
|
3710
|
-
const { x, y, width, height } = this.
|
|
4426
|
+
const { x, y, width, height } = this.glRect();
|
|
4427
|
+
gl.viewport(x, y, width, height);
|
|
3711
4428
|
if (!this.target) {
|
|
3712
4429
|
gl.disable(gl.SCISSOR_TEST);
|
|
3713
|
-
gl.viewport(0, 0, width, height);
|
|
3714
4430
|
return;
|
|
3715
4431
|
}
|
|
3716
|
-
|
|
3717
|
-
gl.viewport(x, glY, width, height);
|
|
3718
|
-
gl.scissor(x, glY, width, height);
|
|
4432
|
+
gl.scissor(x, y, width, height);
|
|
3719
4433
|
gl.enable(gl.SCISSOR_TEST);
|
|
3720
4434
|
}
|
|
4435
|
+
/** This renderer's rect as GL addresses it, from the buffer's bottom left. */
|
|
4436
|
+
glRect() {
|
|
4437
|
+
const { x, y, width, height } = this.deviceRect();
|
|
4438
|
+
if (!this.target) return { x: 0, y: 0, width, height };
|
|
4439
|
+
return { x, y: this.gl.drawingBufferHeight - y - height, width, height };
|
|
4440
|
+
}
|
|
3721
4441
|
/** Confine this renderer to a rect of its drawing buffer, or pass null to
|
|
3722
4442
|
* give it the whole buffer back. Takes effect from the next `render()`. */
|
|
3723
4443
|
setTarget(target) {
|
|
@@ -3739,24 +4459,12 @@ var WeaselRenderer = class {
|
|
|
3739
4459
|
this.applyGlState();
|
|
3740
4460
|
this.applyTarget();
|
|
3741
4461
|
this.effectTargets.releaseAll();
|
|
4462
|
+
this.retained.releaseAll();
|
|
3742
4463
|
this.paintScratch.release();
|
|
3743
4464
|
this.paintResources.release();
|
|
3744
4465
|
this.paintResources = createPaintResources();
|
|
3745
|
-
|
|
3746
|
-
this.
|
|
3747
|
-
this.pathFill.lookupAttributes(PATH_FILL_ATTRIBUTES);
|
|
3748
|
-
this.pathFillVColor = new ShaderProgram(this.gl, VCOLOR_VERT_SRC, VCOLOR_FRAG_SRC);
|
|
3749
|
-
this.pathFillVColor.lookupUniforms(PATH_FILL_UNIFORMS);
|
|
3750
|
-
this.pathFillVColor.lookupAttributes(PATH_FILL_VCOLOR_ATTRIBUTES);
|
|
3751
|
-
this.batchFill = new ShaderProgram(this.gl, BATCH_VERT_SRC, BATCH_FRAG_SRC);
|
|
3752
|
-
this.batchFill.lookupUniforms(BATCH_FILL_UNIFORMS);
|
|
3753
|
-
this.batchFill.lookupAttributes(BATCH_FILL_ATTRIBUTES);
|
|
3754
|
-
this.gradFill = new ShaderProgram(this.gl, GRAD_VERT_SRC, GRAD_FRAG_SRC);
|
|
3755
|
-
this.gradFill.lookupUniforms(GRAD_FILL_UNIFORMS);
|
|
3756
|
-
this.gradFill.lookupAttributes(GRAD_FILL_ATTRIBUTES);
|
|
3757
|
-
this.patternFill = new ShaderProgram(this.gl, PATTERN_VERT_SRC, PATTERN_FRAG_SRC);
|
|
3758
|
-
this.patternFill.lookupUniforms(PATTERN_FILL_UNIFORMS);
|
|
3759
|
-
this.patternFill.lookupAttributes(PATTERN_FILL_ATTRIBUTES);
|
|
4466
|
+
for (const lazy of this.lazyPrograms()) lazy.abandon();
|
|
4467
|
+
this.batchFill = createBatchFill(this.gl);
|
|
3760
4468
|
this.glyphPaintPrograms.clear();
|
|
3761
4469
|
this.meshCache = new GLMeshCache(this.gl);
|
|
3762
4470
|
this.textureCache = new GLTextureCache(this.gl);
|
|
@@ -3795,20 +4503,10 @@ var WeaselRenderer = class {
|
|
|
3795
4503
|
}
|
|
3796
4504
|
this.meshCache.dispose();
|
|
3797
4505
|
this.paintResources.release();
|
|
3798
|
-
for (const
|
|
3799
|
-
|
|
3800
|
-
this.pathFillVColor,
|
|
3801
|
-
this.batchFill,
|
|
3802
|
-
this.gradFill,
|
|
3803
|
-
this.patternFill,
|
|
3804
|
-
...this.glyphPaintPrograms.values()
|
|
3805
|
-
]) {
|
|
3806
|
-
gl.deleteProgram(prog.handle);
|
|
3807
|
-
}
|
|
4506
|
+
for (const lazy of this.lazyPrograms()) lazy.release();
|
|
4507
|
+
for (const prog of [this.batchFill, ...this.glyphPaintPrograms.values()]) gl.deleteProgram(prog.handle);
|
|
3808
4508
|
this.glyphPaintPrograms.clear();
|
|
3809
|
-
for (const prog of this.programRegistry.values())
|
|
3810
|
-
gl.deleteProgram(prog.handle);
|
|
3811
|
-
}
|
|
4509
|
+
for (const prog of this.programRegistry.values()) gl.deleteProgram(prog.handle);
|
|
3812
4510
|
this.programRegistry.clear();
|
|
3813
4511
|
this.programSources.clear();
|
|
3814
4512
|
this.textureCache.free();
|
|
@@ -3820,6 +4518,7 @@ var WeaselRenderer = class {
|
|
|
3820
4518
|
if (this.whiteTexture) gl.deleteTexture(this.whiteTexture);
|
|
3821
4519
|
this.whiteTexture = null;
|
|
3822
4520
|
this.effectTargets.releaseAll();
|
|
4521
|
+
this.retained.releaseAll();
|
|
3823
4522
|
this.paintScratch.release();
|
|
3824
4523
|
}
|
|
3825
4524
|
/**
|
|
@@ -3835,12 +4534,22 @@ var WeaselRenderer = class {
|
|
|
3835
4534
|
const t0 = now();
|
|
3836
4535
|
const counters = { drawCalls: 0 };
|
|
3837
4536
|
const spanStats = [];
|
|
3838
|
-
this.stats = { drawCalls: 0, ms: 0, spans: spanStats };
|
|
4537
|
+
this.stats = { drawCalls: 0, ms: 0, spans: spanStats, retained: NO_RETAINED };
|
|
3839
4538
|
const gl = this.gl;
|
|
3840
4539
|
this.meshCache.drainPendingDeletes();
|
|
3841
4540
|
this.imageCache.beginFrame();
|
|
3842
4541
|
this.textureCache.sweep(textureRegistry);
|
|
3843
4542
|
this.refreshPrograms();
|
|
4543
|
+
this.retained.beginFrame([
|
|
4544
|
+
glyphGeneration(),
|
|
4545
|
+
textureRegistry.getVersion(),
|
|
4546
|
+
paintDrawKindRegistry.getVersion(),
|
|
4547
|
+
this.programEpoch,
|
|
4548
|
+
this.flattenTolerance,
|
|
4549
|
+
this.widthCss,
|
|
4550
|
+
this.heightCss,
|
|
4551
|
+
this.dpr
|
|
4552
|
+
]);
|
|
3844
4553
|
resetBakeBudget(this.bakeBudget);
|
|
3845
4554
|
this.groupState.reset();
|
|
3846
4555
|
this.drawBatch.reset();
|
|
@@ -3853,13 +4562,25 @@ var WeaselRenderer = class {
|
|
|
3853
4562
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
|
|
3854
4563
|
}
|
|
3855
4564
|
const device = this.deviceRect();
|
|
4565
|
+
const { pathFill, pathFillVColor, gradFill, patternFill, batchFillMatrices } = this;
|
|
3856
4566
|
const ctx = {
|
|
3857
4567
|
gl,
|
|
3858
|
-
pathFill
|
|
3859
|
-
|
|
4568
|
+
get pathFill() {
|
|
4569
|
+
return pathFill.get();
|
|
4570
|
+
},
|
|
4571
|
+
get pathFillVColor() {
|
|
4572
|
+
return pathFillVColor.get();
|
|
4573
|
+
},
|
|
3860
4574
|
batchFill: this.batchFill,
|
|
3861
|
-
|
|
3862
|
-
|
|
4575
|
+
get batchFillMatrices() {
|
|
4576
|
+
return batchFillMatrices.get();
|
|
4577
|
+
},
|
|
4578
|
+
get gradFill() {
|
|
4579
|
+
return gradFill.get();
|
|
4580
|
+
},
|
|
4581
|
+
get patternFill() {
|
|
4582
|
+
return patternFill.get();
|
|
4583
|
+
},
|
|
3863
4584
|
meshCache: this.meshCache,
|
|
3864
4585
|
textureCache: this.textureCache,
|
|
3865
4586
|
imageCache: this.imageCache,
|
|
@@ -3876,15 +4597,20 @@ var WeaselRenderer = class {
|
|
|
3876
4597
|
heightCss: this.heightCss,
|
|
3877
4598
|
clipDepth: 0,
|
|
3878
4599
|
clipStack: [],
|
|
3879
|
-
flattenTolerance: this.flattenTolerance,
|
|
4600
|
+
flattenTolerance: inCssPixels(this.flattenTolerance, this.dpr),
|
|
3880
4601
|
textOutlineMinScreenSize: this.textOutlineMinScreenSize,
|
|
3881
4602
|
viewMatrix,
|
|
3882
4603
|
effectTargets: this.effectTargets,
|
|
4604
|
+
retained: this.retained,
|
|
3883
4605
|
paintScratch: this.paintScratch,
|
|
3884
4606
|
renderTarget: null,
|
|
3885
4607
|
deviceWidth: device.width,
|
|
3886
4608
|
deviceHeight: device.height,
|
|
3887
4609
|
restoreTargetRect: () => this.applyTarget(),
|
|
4610
|
+
viewportWithin: (rect) => {
|
|
4611
|
+
const own = this.glRect();
|
|
4612
|
+
gl.viewport(own.x + rect.x, own.y + own.height - rect.y - rect.height, rect.width, rect.height);
|
|
4613
|
+
},
|
|
3888
4614
|
paintResources: this.paintResources,
|
|
3889
4615
|
stats: counters
|
|
3890
4616
|
};
|
|
@@ -3903,6 +4629,14 @@ var WeaselRenderer = class {
|
|
|
3903
4629
|
for (; next < commands.length; next++) dispatch(ctx, commands[next]);
|
|
3904
4630
|
flushBatch(ctx);
|
|
3905
4631
|
this.meshCache.freeTransient();
|
|
4632
|
+
const retained = this.retained;
|
|
4633
|
+
retained.endFrame();
|
|
4634
|
+
this.stats.retained = {
|
|
4635
|
+
composited: retained.composited,
|
|
4636
|
+
redrawn: retained.redrawn,
|
|
4637
|
+
declined: retained.declined,
|
|
4638
|
+
bytes: retained.bytes
|
|
4639
|
+
};
|
|
3906
4640
|
this.stats.drawCalls = counters.drawCalls;
|
|
3907
4641
|
this.stats.ms = now() - t0;
|
|
3908
4642
|
}
|
|
@@ -3912,11 +4646,14 @@ var WeaselRenderer = class {
|
|
|
3912
4646
|
lastFrameStats() {
|
|
3913
4647
|
return this.stats;
|
|
3914
4648
|
}
|
|
3915
|
-
/** Change
|
|
3916
|
-
* restores the cached `DEFAULT_FLATTEN_TOLERANCE` route. */
|
|
4649
|
+
/** Change `flattenTolerance` after construction; `undefined` restores the default. */
|
|
3917
4650
|
setFlattenTolerance(tolerance) {
|
|
3918
4651
|
this.flattenTolerance = tolerance;
|
|
3919
4652
|
}
|
|
4653
|
+
/** Change `retainBudget` after construction; `undefined` restores the default. */
|
|
4654
|
+
setRetainBudget(bytes) {
|
|
4655
|
+
this.retained.setBudget(bytes ?? DEFAULT_RETAIN_BUDGET);
|
|
4656
|
+
}
|
|
3920
4657
|
resize(dims) {
|
|
3921
4658
|
this.widthCss = dims.width;
|
|
3922
4659
|
this.heightCss = dims.height;
|
|
@@ -3933,11 +4670,11 @@ var WeaselRenderer = class {
|
|
|
3933
4670
|
}
|
|
3934
4671
|
/** @internal */
|
|
3935
4672
|
_pathFill() {
|
|
3936
|
-
return this.pathFill;
|
|
4673
|
+
return this.pathFill.get();
|
|
3937
4674
|
}
|
|
3938
4675
|
/** @internal */
|
|
3939
4676
|
_pathFillVColor() {
|
|
3940
|
-
return this.pathFillVColor;
|
|
4677
|
+
return this.pathFillVColor.get();
|
|
3941
4678
|
}
|
|
3942
4679
|
/** @internal */
|
|
3943
4680
|
_drawBatch() {
|
|
@@ -3948,12 +4685,16 @@ var WeaselRenderer = class {
|
|
|
3948
4685
|
return this.batchFill;
|
|
3949
4686
|
}
|
|
3950
4687
|
/** @internal */
|
|
4688
|
+
_batchFillMatrices() {
|
|
4689
|
+
return this.batchFillMatrices.get();
|
|
4690
|
+
}
|
|
4691
|
+
/** @internal */
|
|
3951
4692
|
_gradFill() {
|
|
3952
|
-
return this.gradFill;
|
|
4693
|
+
return this.gradFill.get();
|
|
3953
4694
|
}
|
|
3954
4695
|
/** @internal */
|
|
3955
4696
|
_patternFill() {
|
|
3956
|
-
return this.patternFill;
|
|
4697
|
+
return this.patternFill.get();
|
|
3957
4698
|
}
|
|
3958
4699
|
/** @internal */
|
|
3959
4700
|
_meshCache() {
|
|
@@ -4025,119 +4766,6 @@ function frameRect(sheet, index) {
|
|
|
4025
4766
|
h: frameHeight
|
|
4026
4767
|
};
|
|
4027
4768
|
}
|
|
4028
|
-
var SLACK_PX = 1;
|
|
4029
|
-
var DEFAULT_MITER_LIMIT = 4;
|
|
4030
|
-
function cullDrawCommands(cmds, transform, rect) {
|
|
4031
|
-
return cullList(cmds, transform, slackBox(rect));
|
|
4032
|
-
}
|
|
4033
|
-
function cullList(cmds, m, box) {
|
|
4034
|
-
let out = null;
|
|
4035
|
-
for (let i = 0; i < cmds.length; i++) {
|
|
4036
|
-
const cmd = cmds[i];
|
|
4037
|
-
const kept = cullOne(cmd, m, box);
|
|
4038
|
-
if (kept === cmd) {
|
|
4039
|
-
out?.push(cmd);
|
|
4040
|
-
continue;
|
|
4041
|
-
}
|
|
4042
|
-
out ??= cmds.slice(0, i);
|
|
4043
|
-
if (kept) out.push(kept);
|
|
4044
|
-
}
|
|
4045
|
-
return out ?? cmds;
|
|
4046
|
-
}
|
|
4047
|
-
function cullOne(cmd, m, box) {
|
|
4048
|
-
switch (cmd.kind) {
|
|
4049
|
-
case "group": {
|
|
4050
|
-
if (cmd.effects && cmd.effects.length > 0) return cmd;
|
|
4051
|
-
if (isOwnComposite(cmd.composite)) return cmd;
|
|
4052
|
-
const inner = cmd.transform ? mat3.multiply(m, cmd.transform) : m;
|
|
4053
|
-
const children = cullList(cmd.children, inner, box);
|
|
4054
|
-
if (children === cmd.children) return cmd;
|
|
4055
|
-
return children.length === 0 ? null : { ...cmd, children };
|
|
4056
|
-
}
|
|
4057
|
-
case "path":
|
|
4058
|
-
return pathMisses(cmd.path, cmd.stroke, m, box) ? null : cmd;
|
|
4059
|
-
case "image":
|
|
4060
|
-
return quadMisses(cmd.x, cmd.y, cmd.x + cmd.w, cmd.y + cmd.h, m, box) ? null : cmd;
|
|
4061
|
-
case "sprites":
|
|
4062
|
-
return spritesMiss(cmd.sprites, m, box) ? null : cmd;
|
|
4063
|
-
default:
|
|
4064
|
-
return cmd;
|
|
4065
|
-
}
|
|
4066
|
-
}
|
|
4067
|
-
function pathMisses(path, stroke, m, box) {
|
|
4068
|
-
let minX, minY, maxX, maxY;
|
|
4069
|
-
if (path.kind === "rect") {
|
|
4070
|
-
minX = Math.min(path.x, path.x + path.width);
|
|
4071
|
-
maxX = Math.max(path.x, path.x + path.width);
|
|
4072
|
-
minY = Math.min(path.y, path.y + path.height);
|
|
4073
|
-
maxY = Math.max(path.y, path.y + path.height);
|
|
4074
|
-
} else if (path.kind === "polygon") {
|
|
4075
|
-
const c = path.coords;
|
|
4076
|
-
if (c.length < 2) return false;
|
|
4077
|
-
minX = maxX = c[0];
|
|
4078
|
-
minY = maxY = c[1];
|
|
4079
|
-
for (let i = 2; i + 1 < c.length; i += 2) {
|
|
4080
|
-
const x = c[i], y = c[i + 1];
|
|
4081
|
-
if (x < minX) minX = x;
|
|
4082
|
-
else if (x > maxX) maxX = x;
|
|
4083
|
-
if (y < minY) minY = y;
|
|
4084
|
-
else if (y > maxY) maxY = y;
|
|
4085
|
-
}
|
|
4086
|
-
} else {
|
|
4087
|
-
return false;
|
|
4088
|
-
}
|
|
4089
|
-
const reach = stroke ? strokeReach(stroke, m) : 0;
|
|
4090
|
-
return quadMisses(minX - reach, minY - reach, maxX + reach, maxY + reach, m, box);
|
|
4091
|
-
}
|
|
4092
|
-
function strokeReach(stroke, m) {
|
|
4093
|
-
return strokeReachAt(stroke, mat3.meanScaleOf(m), mat3.minScaleOf(m));
|
|
4094
|
-
}
|
|
4095
|
-
function strokeReachAt(stroke, scale, least = scale) {
|
|
4096
|
-
const at = least > 0 ? least : scale;
|
|
4097
|
-
const base = resolveStrokeWidth(stroke.width ?? 1, typeof stroke.width === "object" ? at : scale);
|
|
4098
|
-
let width = base;
|
|
4099
|
-
if (stroke.vertexWidths) {
|
|
4100
|
-
for (const v of stroke.vertexWidths) if (v > width) width = v;
|
|
4101
|
-
}
|
|
4102
|
-
const spike = Math.max(stroke.miterLimit ?? DEFAULT_MITER_LIMIT, Math.SQRT2);
|
|
4103
|
-
const heads = markerReach(stroke);
|
|
4104
|
-
return Math.max(Math.abs(width) * spike, heads.world, resolveScreenLength({ px: heads.px }, at));
|
|
4105
|
-
}
|
|
4106
|
-
function spritesMiss(sprites, m, box) {
|
|
4107
|
-
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
4108
|
-
for (let i = 0; i + SPRITE_STRIDE <= sprites.length; i += SPRITE_STRIDE) {
|
|
4109
|
-
const x0 = sprites[i], y0 = sprites[i + 1];
|
|
4110
|
-
const x1 = x0 + sprites[i + 2], y1 = y0 + sprites[i + 3];
|
|
4111
|
-
minX = Math.min(minX, x0, x1);
|
|
4112
|
-
maxX = Math.max(maxX, x0, x1);
|
|
4113
|
-
minY = Math.min(minY, y0, y1);
|
|
4114
|
-
maxY = Math.max(maxY, y0, y1);
|
|
4115
|
-
}
|
|
4116
|
-
if (minX === Infinity) return false;
|
|
4117
|
-
return quadMisses(minX, minY, maxX, maxY, m, box);
|
|
4118
|
-
}
|
|
4119
|
-
function boundsMissRect(bounds, transform, rect) {
|
|
4120
|
-
const x0 = Math.min(bounds.x, bounds.x + bounds.width);
|
|
4121
|
-
const x1 = Math.max(bounds.x, bounds.x + bounds.width);
|
|
4122
|
-
const y0 = Math.min(bounds.y, bounds.y + bounds.height);
|
|
4123
|
-
const y1 = Math.max(bounds.y, bounds.y + bounds.height);
|
|
4124
|
-
return quadMisses(x0, y0, x1, y1, transform, slackBox(rect));
|
|
4125
|
-
}
|
|
4126
|
-
function slackBox(rect) {
|
|
4127
|
-
return {
|
|
4128
|
-
minX: rect.x - SLACK_PX,
|
|
4129
|
-
minY: rect.y - SLACK_PX,
|
|
4130
|
-
maxX: rect.x + rect.width + SLACK_PX,
|
|
4131
|
-
maxY: rect.y + rect.height + SLACK_PX
|
|
4132
|
-
};
|
|
4133
|
-
}
|
|
4134
|
-
function quadMisses(x0, y0, x1, y1, m, box) {
|
|
4135
|
-
const [ax, ay] = mat3.apply(m, x0, y0);
|
|
4136
|
-
const [bx, by] = mat3.apply(m, x1, y0);
|
|
4137
|
-
const [cx, cy] = mat3.apply(m, x1, y1);
|
|
4138
|
-
const [dx, dy] = mat3.apply(m, x0, y1);
|
|
4139
|
-
return Math.max(ax, bx, cx, dx) < box.minX || Math.min(ax, bx, cx, dx) > box.maxX || Math.max(ay, by, cy, dy) < box.minY || Math.min(ay, by, cy, dy) > box.maxY;
|
|
4140
|
-
}
|
|
4141
4769
|
|
|
4142
4770
|
// src/effects/types.ts
|
|
4143
4771
|
function registerEffect(id, frag) {
|
|
@@ -4263,6 +4891,6 @@ function isSolid(paint) {
|
|
|
4263
4891
|
return paint == null || isSolidPaint(paint);
|
|
4264
4892
|
}
|
|
4265
4893
|
|
|
4266
|
-
export { EFFECT_KIT_UNIFORMS, EFFECT_VERT_SRC, IDENTITY_COLOR_MATRIX, OUTLINE_MIN_SCREEN_PX, SPRITE_STRIDE, ShaderCompileError, WeaselRenderer, blur, boundsMissRect, buildGradientRamp, cullDrawCommands, frameRect, registerEffect, strokeReachAt, textCommand, textCommandFromPose, textCommandFromRuns, vignette };
|
|
4894
|
+
export { DEFAULT_FLATTEN_TOLERANCE_PX, DEFAULT_RETAIN_BUDGET, EFFECT_KIT_UNIFORMS, EFFECT_VERT_SRC, IDENTITY_COLOR_MATRIX, OUTLINE_MIN_SCREEN_PX, SPRITE_STRIDE, ShaderCompileError, WeaselRenderer, blur, boundsMissRect, buildGradientRamp, cullDrawCommands, frameRect, registerEffect, resolveStrokeLengths, strokeReachAt, textCommand, textCommandFromPose, textCommandFromRuns, vignette };
|
|
4267
4895
|
//# sourceMappingURL=index.js.map
|
|
4268
4896
|
//# sourceMappingURL=index.js.map
|