@weasel-js/renderer 1.9.4 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{chunk-2VOBNWBN.js → chunk-43YL7HZV.js} +3 -3
- package/dist/chunk-43YL7HZV.js.map +1 -0
- package/dist/{chunk-VHQJ56XQ.js → chunk-K7XCWVQH.js} +236 -168
- package/dist/chunk-K7XCWVQH.js.map +1 -0
- package/dist/{chunk-ORBXHIRB.js → chunk-Q4SIHV7Q.js} +4 -4
- package/dist/chunk-Q4SIHV7Q.js.map +1 -0
- package/dist/{imageCache-oJVgxhkr.d.ts → imageCache-DucOxC7l.d.ts} +73 -115
- package/dist/index.d.ts +201 -204
- package/dist/index.js +577 -291
- package/dist/index.js.map +1 -1
- package/dist/mesh.d.ts +34 -67
- package/dist/mesh.js +5 -5
- package/dist/mesh.js.map +1 -1
- package/dist/{paintKinds-B2vEy9A4.d.ts → paintKinds-BmPAT51k.d.ts} +22 -38
- package/dist/patterns-builtin.d.ts +10 -16
- package/dist/patterns-builtin.js +7 -7
- package/dist/patterns-builtin.js.map +1 -1
- package/dist/test-seams.d.ts +22 -65
- package/dist/test-seams.js +2 -2
- package/dist/testing/glRecorder.d.ts +2 -5
- package/dist/testing/glRecorder.js +3 -0
- package/dist/testing/glRecorder.js.map +1 -1
- package/dist/testing/vertexUploads.d.ts +4 -6
- package/dist/testing/vertexUploads.js.map +1 -1
- package/package.json +7 -7
- package/dist/chunk-2VOBNWBN.js.map +0 -1
- package/dist/chunk-ORBXHIRB.js.map +0 -1
- package/dist/chunk-VHQJ56XQ.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
export { createTilePattern } from './chunk-
|
|
2
|
-
import { BATCH_ATTRIBUTE_LOCATIONS, BATCH_TEXTURE_SLOTS, registerProgram,
|
|
3
|
-
export { commandFill, commandStroke, fillInPoseFrame, fillToBoundsFrame, getImageBitmap, getPaintDrawKind, getProgramSource, imageStatus, isPaintKindKnown,
|
|
4
|
-
import { textureRegistry, getTexture } from './chunk-
|
|
5
|
-
export { registerTexture, textureRegistry, unregisterTexture } from './chunk-
|
|
6
|
-
import { DEFAULT_BAKE_BUDGET, markAllFontsNotUploaded, resetBakeBudget, GLYPH_COVERAGE_GLSL, syncDynamicPageTexture, ensureFontTexture, dynamicPageTextureId, textureCacheKey, GLYPH_MODE_R8, GLYPH_MODE_MSDF, glyphFieldScale } from '@weasel-js/font';
|
|
1
|
+
export { createTilePattern } from './chunk-43YL7HZV.js';
|
|
2
|
+
import { BATCH_ATTRIBUTE_LOCATIONS, BATCH_TEXTURE_SLOTS, registerProgram, STRAIGHT_RGB_GLSL, BATCH_FILL_ATTRIBUTES, BATCH_FILL_UNIFORMS, BATCH_MATRICES_FRAG_SRC, BATCH_VERT_SRC, GLImageCache, DrawBatch, getProgramSource, 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, quantizeStrokeScale, strokeMesh, axisAlignedClipRect, intersectClipRects, getPaintDrawKind, outlineMesh, outlineStrokeMesh, quantizeEmWidth } from './chunk-K7XCWVQH.js';
|
|
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-K7XCWVQH.js';
|
|
4
|
+
import { textureRegistry, getTexture } from './chunk-Q4SIHV7Q.js';
|
|
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';
|
|
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, resolveColor } from '@weasel-js/paint';
|
|
8
|
+
import { resolveGradientStops, sampleResolvedStops, resolveScreenLength, isSolidPaint, resolveColor, strokeHasInk } from '@weasel-js/paint';
|
|
9
9
|
import { mat3 } from '@weasel-js/geom';
|
|
10
10
|
export { mat3, viewToMat3 } from '@weasel-js/geom';
|
|
11
11
|
import { tessellate } from '@weasel-js/geom/tessellate';
|
|
12
12
|
export { tessellate } from '@weasel-js/geom/tessellate';
|
|
13
13
|
import { resolveStrokeWidth, markerReach, metricKeepsRects, strokeSpaceOf, resolveMarkerSize } from '@weasel-js/stroke';
|
|
14
14
|
export { tessellateStroke } from '@weasel-js/stroke';
|
|
15
|
-
import { resolveTextStyle, resolveRuns, textPoseLayoutInput, cachedLayoutRuns, verticalAlignOffset, SYNTHETIC_ITALIC_RADIANS } from '@weasel-js/text';
|
|
15
|
+
import { resolveTextStyle, resolveRuns, textPoseLayoutInput, cachedLayoutRuns, cachedPaintRuns, verticalAlignOffset, SYNTHETIC_ITALIC_RADIANS } from '@weasel-js/text';
|
|
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",
|
|
@@ -320,10 +317,11 @@ uniform float u_opacity;
|
|
|
320
317
|
uniform float u_alpha;
|
|
321
318
|
uniform mat4 u_colorMatrix;
|
|
322
319
|
uniform vec4 u_colorBias;
|
|
320
|
+
${STRAIGHT_RGB_GLSL}
|
|
323
321
|
vec4 shadePaint(vec2 world) {
|
|
324
322
|
vec2 uv = world / max(u_tileSize, vec2(0.0001));
|
|
325
323
|
vec4 texel = texture(u_sampler, uv);
|
|
326
|
-
vec4 mapped = clamp(u_colorMatrix * texel + u_colorBias, 0.0, 1.0);
|
|
324
|
+
vec4 mapped = clamp(u_colorMatrix * vec4(straightRgb(texel), texel.a) + u_colorBias, 0.0, 1.0);
|
|
327
325
|
float a = mapped.a * u_opacity * u_alpha;
|
|
328
326
|
return vec4(mapped.rgb * a, a);
|
|
329
327
|
}
|
|
@@ -354,6 +352,72 @@ var PATTERN_FILL_UNIFORMS = [
|
|
|
354
352
|
"u_colorBias"
|
|
355
353
|
];
|
|
356
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
|
+
}
|
|
357
421
|
var L = BATCH_ATTRIBUTE_LOCATIONS;
|
|
358
422
|
var GLYPH_PAINT_VERT_SRC = (
|
|
359
423
|
/* glsl */
|
|
@@ -510,11 +574,9 @@ var GLMeshCache = class {
|
|
|
510
574
|
return handle;
|
|
511
575
|
}
|
|
512
576
|
/**
|
|
513
|
-
* Upload a Mesh that the caller knows is single-use
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
* of the current frame via `freeTransient()`. Bypasses the WeakMap cache
|
|
517
|
-
* and the FinalizationRegistry, so transient meshes never wait on GC.
|
|
577
|
+
* Upload a Mesh that the caller knows is single-use. Returns a handle backed
|
|
578
|
+
* by fresh GL resources that the renderer frees at the end of the current
|
|
579
|
+
* frame via `freeTransient()`, so transient meshes never wait on GC.
|
|
518
580
|
*/
|
|
519
581
|
uploadTransient(mesh) {
|
|
520
582
|
const { handle, vbo, ibo } = this.upload(mesh);
|
|
@@ -628,6 +690,9 @@ var GLMeshCache = class {
|
|
|
628
690
|
};
|
|
629
691
|
|
|
630
692
|
// src/cache/GLTextureCache.ts
|
|
693
|
+
function uploadAsHeld(gl, source) {
|
|
694
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
|
695
|
+
}
|
|
631
696
|
var GLTextureCache = class {
|
|
632
697
|
constructor(gl) {
|
|
633
698
|
this.gl = gl;
|
|
@@ -644,8 +709,11 @@ var GLTextureCache = class {
|
|
|
644
709
|
/** Uploads the pixels once per id. `wrap` is re-applied whenever it differs
|
|
645
710
|
* from what the id was last uploaded under: the same registered image can
|
|
646
711
|
* be a clamped shader texture in one draw and a repeating pattern tile in
|
|
647
|
-
* the next
|
|
712
|
+
* the next. */
|
|
648
713
|
upload(id, source, wrap = "clamp") {
|
|
714
|
+
return this.ensure(id, source, wrap, uploadAsHeld);
|
|
715
|
+
}
|
|
716
|
+
ensure(id, source, wrap, send) {
|
|
649
717
|
const cached = this.map.get(id);
|
|
650
718
|
if (cached) {
|
|
651
719
|
if (this.wraps.get(id) !== wrap) this.applyWrap(id, cached, wrap);
|
|
@@ -656,7 +724,7 @@ var GLTextureCache = class {
|
|
|
656
724
|
if (!tex) throw new Error(`GLTextureCache: createTexture failed for id="${id}"`);
|
|
657
725
|
const wrapMode = wrap === "repeat" ? gl.REPEAT : gl.CLAMP_TO_EDGE;
|
|
658
726
|
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
659
|
-
|
|
727
|
+
send(gl, source);
|
|
660
728
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
661
729
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
662
730
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapMode);
|
|
@@ -667,17 +735,18 @@ var GLTextureCache = class {
|
|
|
667
735
|
return id;
|
|
668
736
|
}
|
|
669
737
|
/** `upload` for an id from the texture registry, whose image can be replaced
|
|
670
|
-
* under the same id: a changed source sends its pixels again.
|
|
738
|
+
* under the same id: a changed source sends its pixels again. A registered
|
|
739
|
+
* texture is an image, so its texels are premultiplied. */
|
|
671
740
|
uploadRegistered(id, source, wrap = "clamp") {
|
|
672
741
|
const cached = this.map.get(id);
|
|
673
742
|
if (cached && this.registered.get(id) !== source) {
|
|
674
743
|
const gl = this.gl;
|
|
675
744
|
gl.bindTexture(gl.TEXTURE_2D, cached);
|
|
676
|
-
|
|
745
|
+
uploadPremultiplied(gl, source);
|
|
677
746
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
678
747
|
}
|
|
679
748
|
this.registered.set(id, source);
|
|
680
|
-
return this.
|
|
749
|
+
return this.ensure(id, source, wrap, uploadPremultiplied);
|
|
681
750
|
}
|
|
682
751
|
/**
|
|
683
752
|
* Delete the texture of every registry-backed id the registry has dropped.
|
|
@@ -736,6 +805,35 @@ var GLTextureCache = class {
|
|
|
736
805
|
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);
|
|
737
806
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
738
807
|
}
|
|
808
|
+
/** Create a texture from premultiplied RGBA bytes that are a color: a page
|
|
809
|
+
* of color glyphs. Mipmapped, as a font atlas must never be, because one is
|
|
810
|
+
* drawn well below the size it was baked at. No-op if `id` already exists. */
|
|
811
|
+
uploadRGBA(id, width, height, data) {
|
|
812
|
+
if (this.map.has(id)) return;
|
|
813
|
+
const gl = this.gl;
|
|
814
|
+
const tex = gl.createTexture();
|
|
815
|
+
if (!tex) throw new Error(`GLTextureCache: createTexture failed for id="${id}"`);
|
|
816
|
+
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
817
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
|
|
818
|
+
gl.generateMipmap(gl.TEXTURE_2D);
|
|
819
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
|
820
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
821
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
822
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
823
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
824
|
+
this.map.set(id, tex);
|
|
825
|
+
this.wraps.set(id, "clamp");
|
|
826
|
+
}
|
|
827
|
+
/** Patch a rect of an existing RGBA texture with tightly-packed w×h texels. */
|
|
828
|
+
subImageRGBA(id, x, y, w, h, data) {
|
|
829
|
+
const tex = this.map.get(id);
|
|
830
|
+
if (!tex) throw new Error(`GLTextureCache: texture "${id}" not uploaded`);
|
|
831
|
+
const gl = this.gl;
|
|
832
|
+
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
833
|
+
gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, w, h, gl.RGBA, gl.UNSIGNED_BYTE, data);
|
|
834
|
+
gl.generateMipmap(gl.TEXTURE_2D);
|
|
835
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
836
|
+
}
|
|
739
837
|
bind(id, unit) {
|
|
740
838
|
const tex = this.map.get(id);
|
|
741
839
|
if (!tex) throw new Error(`GLTextureCache: texture "${id}" not uploaded`);
|
|
@@ -744,10 +842,8 @@ var GLTextureCache = class {
|
|
|
744
842
|
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
745
843
|
}
|
|
746
844
|
/**
|
|
747
|
-
* Delete every uploaded GL texture and clear the map.
|
|
748
|
-
*
|
|
749
|
-
* afterward — a subsequent `upload()` for a previously-seen id re-creates
|
|
750
|
-
* the texture rather than restoring the deleted one.
|
|
845
|
+
* Delete every uploaded GL texture and clear the map. The cache is
|
|
846
|
+
* refillable afterward.
|
|
751
847
|
*/
|
|
752
848
|
free() {
|
|
753
849
|
const gl = this.gl;
|
|
@@ -858,10 +954,8 @@ var GradientRampAtlas = class {
|
|
|
858
954
|
this.cacheHits = 0;
|
|
859
955
|
}
|
|
860
956
|
/**
|
|
861
|
-
* Delete the atlas texture and forget every row in it.
|
|
862
|
-
*
|
|
863
|
-
* unusable but refillable afterward (stats are left as-is; call
|
|
864
|
-
* `resetStats()` separately if desired).
|
|
957
|
+
* Delete the atlas texture and forget every row in it. The CPU mirror goes
|
|
958
|
+
* with it, so the atlas is refillable afterward. Stats are left as-is.
|
|
865
959
|
*/
|
|
866
960
|
free() {
|
|
867
961
|
if (this.texture) this.gl.deleteTexture(this.texture);
|
|
@@ -999,16 +1093,13 @@ var GroupState = class {
|
|
|
999
1093
|
this.colorMatrixStack.push(nextCM);
|
|
1000
1094
|
}
|
|
1001
1095
|
/**
|
|
1002
|
-
* Push a frame whose children paint into a surface of their own.
|
|
1003
|
-
*
|
|
1004
|
-
*
|
|
1005
|
-
*
|
|
1006
|
-
* joins the frame, and applying them on the way in as well would fade the
|
|
1007
|
-
* pixels an effect is about to read, then fade them again on the way out.
|
|
1096
|
+
* Push a frame whose children paint into a surface of their own. The
|
|
1097
|
+
* transform still accumulates. Alpha and color do not: applying them on the
|
|
1098
|
+
* way in would fade the pixels an effect is about to read, then fade them
|
|
1099
|
+
* again on the way out.
|
|
1008
1100
|
*
|
|
1009
1101
|
* Returns the pair the caller must apply at composite time — what `push`
|
|
1010
|
-
* would have left on the stack
|
|
1011
|
-
* in the dispatcher.
|
|
1102
|
+
* would have left on the stack.
|
|
1012
1103
|
*/
|
|
1013
1104
|
pushIsolated(frame) {
|
|
1014
1105
|
this.push(frame);
|
|
@@ -1085,6 +1176,49 @@ function compositeProgramId() {
|
|
|
1085
1176
|
return COMPOSITE_PROGRAM_ID;
|
|
1086
1177
|
}
|
|
1087
1178
|
|
|
1179
|
+
// src/effects/compositeOp.ts
|
|
1180
|
+
var OPERATORS = {
|
|
1181
|
+
"source-over": { source: "one", destination: "one-minus-source-alpha" },
|
|
1182
|
+
"source-in": { source: "destination-alpha", destination: "zero" },
|
|
1183
|
+
"source-out": { source: "one-minus-destination-alpha", destination: "zero" },
|
|
1184
|
+
"source-atop": { source: "destination-alpha", destination: "one-minus-source-alpha" },
|
|
1185
|
+
"destination-over": { source: "one-minus-destination-alpha", destination: "one" },
|
|
1186
|
+
"destination-in": { source: "zero", destination: "source-alpha" },
|
|
1187
|
+
"destination-out": { source: "zero", destination: "one-minus-source-alpha" },
|
|
1188
|
+
"destination-atop": { source: "one-minus-destination-alpha", destination: "source-alpha" },
|
|
1189
|
+
xor: { source: "one-minus-destination-alpha", destination: "one-minus-source-alpha" },
|
|
1190
|
+
lighter: { source: "one", destination: "one" },
|
|
1191
|
+
copy: { source: "one", destination: "zero" }
|
|
1192
|
+
};
|
|
1193
|
+
function blendFactorsOf(composite) {
|
|
1194
|
+
return typeof composite === "string" ? OPERATORS[composite] : composite;
|
|
1195
|
+
}
|
|
1196
|
+
function isOwnComposite(composite) {
|
|
1197
|
+
if (composite === void 0) return false;
|
|
1198
|
+
const { source, destination } = blendFactorsOf(composite);
|
|
1199
|
+
return source !== "one" || destination !== "one-minus-source-alpha";
|
|
1200
|
+
}
|
|
1201
|
+
function glFactor(gl, factor) {
|
|
1202
|
+
switch (factor) {
|
|
1203
|
+
case "zero":
|
|
1204
|
+
return gl.ZERO;
|
|
1205
|
+
case "one":
|
|
1206
|
+
return gl.ONE;
|
|
1207
|
+
case "source-alpha":
|
|
1208
|
+
return gl.SRC_ALPHA;
|
|
1209
|
+
case "one-minus-source-alpha":
|
|
1210
|
+
return gl.ONE_MINUS_SRC_ALPHA;
|
|
1211
|
+
case "destination-alpha":
|
|
1212
|
+
return gl.DST_ALPHA;
|
|
1213
|
+
case "one-minus-destination-alpha":
|
|
1214
|
+
return gl.ONE_MINUS_DST_ALPHA;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
function applyComposite(gl, composite) {
|
|
1218
|
+
const { source, destination } = blendFactorsOf(composite);
|
|
1219
|
+
gl.blendFunc(glFactor(gl, source), glFactor(gl, destination));
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1088
1222
|
// src/draw/drawTriangles.ts
|
|
1089
1223
|
function drawTriangles(ctx, count, type) {
|
|
1090
1224
|
ctx.gl.drawElements(ctx.gl.TRIANGLES, count, type, 0);
|
|
@@ -1128,14 +1262,17 @@ function endParityMask(ctx) {
|
|
|
1128
1262
|
|
|
1129
1263
|
// src/draw/customShader.ts
|
|
1130
1264
|
var warnedUniforms = /* @__PURE__ */ new Set();
|
|
1131
|
-
function
|
|
1132
|
-
const key = `${programId}:${name}`;
|
|
1265
|
+
function warnOnce(key, message) {
|
|
1133
1266
|
if (warnedUniforms.has(key)) return;
|
|
1134
1267
|
warnedUniforms.add(key);
|
|
1135
1268
|
const isDev = typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true;
|
|
1136
|
-
if (isDev)
|
|
1137
|
-
|
|
1138
|
-
|
|
1269
|
+
if (isDev) console.warn(message);
|
|
1270
|
+
}
|
|
1271
|
+
function warnOnceUniform(programId, name) {
|
|
1272
|
+
warnOnce(
|
|
1273
|
+
`${programId}:${name}`,
|
|
1274
|
+
`weasel drawShader: uniform "${name}" not found in program "${programId}". Check spelling, ensure it's used in the shader (unused uniforms are optimized away by the driver).`
|
|
1275
|
+
);
|
|
1139
1276
|
}
|
|
1140
1277
|
function setUniform(gl, loc, value, textureCache, nextTexUnit) {
|
|
1141
1278
|
if (value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Float32Array) && "id" in value && typeof value.id === "string") {
|
|
@@ -1259,10 +1396,11 @@ function setUniforms(gl, program, programId, uniforms, textureCache, nextTexUnit
|
|
|
1259
1396
|
}
|
|
1260
1397
|
function drawShader(ctx, cmd) {
|
|
1261
1398
|
const { gl, programRegistry, quadVbo, quadIbo, textureCache } = ctx;
|
|
1262
|
-
const program = programRegistry.get(cmd.program.id);
|
|
1399
|
+
const program = ctx.ensureProgram?.(cmd.program.id) ?? programRegistry.get(cmd.program.id);
|
|
1263
1400
|
if (!program) {
|
|
1264
|
-
|
|
1265
|
-
|
|
1401
|
+
warnOnce(
|
|
1402
|
+
`${cmd.program.id}:(program)`,
|
|
1403
|
+
`weasel drawShader: program "${cmd.program.id}" has no compiled program on this renderer. Register its source with the module-level registerProgram() first; a source that failed to compile is reported above.`
|
|
1266
1404
|
);
|
|
1267
1405
|
return;
|
|
1268
1406
|
}
|
|
@@ -1478,6 +1616,83 @@ function meshHandle(ctx, mesh) {
|
|
|
1478
1616
|
function fillMeshHandle(ctx, path) {
|
|
1479
1617
|
return meshHandle(ctx, fillMesh(ctx, path));
|
|
1480
1618
|
}
|
|
1619
|
+
|
|
1620
|
+
// src/draw/foldColor.ts
|
|
1621
|
+
function isIdentityColorMatrix(cm) {
|
|
1622
|
+
return cm === IDENTITY_COLOR_MATRIX || sameValues(IDENTITY_COLOR_MATRIX, cm);
|
|
1623
|
+
}
|
|
1624
|
+
var clamp01 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
1625
|
+
function foldColor(ctx, r, g, b, a) {
|
|
1626
|
+
const cm = ctx.state.colorMatrix;
|
|
1627
|
+
const alpha = ctx.state.alpha;
|
|
1628
|
+
if (isIdentityColorMatrix(cm)) return [r, g, b, a * alpha];
|
|
1629
|
+
return [
|
|
1630
|
+
clamp01(cm[0] * r + cm[1] * g + cm[2] * b + cm[3] * a + cm[4]),
|
|
1631
|
+
clamp01(cm[5] * r + cm[6] * g + cm[7] * b + cm[8] * a + cm[9]),
|
|
1632
|
+
clamp01(cm[10] * r + cm[11] * g + cm[12] * b + cm[13] * a + cm[14]),
|
|
1633
|
+
clamp01(cm[15] * r + cm[16] * g + cm[17] * b + cm[18] * a + cm[19]) * alpha
|
|
1634
|
+
];
|
|
1635
|
+
}
|
|
1636
|
+
var NO_TINT = [1, 1, 1, 1];
|
|
1637
|
+
var DIAGONAL = [0, 6, 12, 18];
|
|
1638
|
+
function textureTint(ctx) {
|
|
1639
|
+
const cm = ctx.state.colorMatrix;
|
|
1640
|
+
const alpha = ctx.state.alpha;
|
|
1641
|
+
if (isIdentityColorMatrix(cm)) return alpha === 1 ? NO_TINT : [1, 1, 1, alpha];
|
|
1642
|
+
for (let i = 0; i < 20; i++) {
|
|
1643
|
+
const v = cm[i];
|
|
1644
|
+
if (DIAGONAL.includes(i) ? v < 0 || v > 1 : v !== 0) return null;
|
|
1645
|
+
}
|
|
1646
|
+
return [cm[0], cm[6], cm[12], cm[18] * alpha];
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
// src/draw/batchMatrices.ts
|
|
1650
|
+
function matrixIndex(matrices, colorMatrix, alpha) {
|
|
1651
|
+
for (let i = 0; i < matrices.length; i++) {
|
|
1652
|
+
const m = matrices[i];
|
|
1653
|
+
if (m.alpha === alpha && (m.colorMatrix === colorMatrix || sameValues(m.colorMatrix, colorMatrix))) return i;
|
|
1654
|
+
}
|
|
1655
|
+
return matrices.length < (BATCH_MATRIX_SLOTS ) ? matrices.length : -1;
|
|
1656
|
+
}
|
|
1657
|
+
var MATRICES = new Float32Array(16 * BATCH_MATRIX_SLOTS);
|
|
1658
|
+
var BIASES = new Float32Array(4 * BATCH_MATRIX_SLOTS);
|
|
1659
|
+
var ALPHAS = new Float32Array(BATCH_MATRIX_SLOTS);
|
|
1660
|
+
var UPLOADED = /* @__PURE__ */ new WeakMap();
|
|
1661
|
+
function holds(held, matrices) {
|
|
1662
|
+
if (held.length < matrices.length) return false;
|
|
1663
|
+
for (let i = 0; i < matrices.length; i++) {
|
|
1664
|
+
const a = held[i];
|
|
1665
|
+
const b = matrices[i];
|
|
1666
|
+
if (a.alpha !== b.alpha) return false;
|
|
1667
|
+
if (a.colorMatrix !== b.colorMatrix && !sameValues(a.colorMatrix, b.colorMatrix)) return false;
|
|
1668
|
+
}
|
|
1669
|
+
return true;
|
|
1670
|
+
}
|
|
1671
|
+
function uploadBatchMatrices(ctx, prog, matrices) {
|
|
1672
|
+
let byProgram = UPLOADED.get(ctx);
|
|
1673
|
+
if (!byProgram) {
|
|
1674
|
+
byProgram = /* @__PURE__ */ new WeakMap();
|
|
1675
|
+
UPLOADED.set(ctx, byProgram);
|
|
1676
|
+
}
|
|
1677
|
+
const held = byProgram.get(prog);
|
|
1678
|
+
if (held !== void 0 && holds(held, matrices)) return;
|
|
1679
|
+
const n = matrices.length;
|
|
1680
|
+
for (let i = 0; i < n; i++) {
|
|
1681
|
+
const cm = matrices[i].colorMatrix;
|
|
1682
|
+
for (let row = 0; row < 4; row++) {
|
|
1683
|
+
for (let col = 0; col < 4; col++) MATRICES[16 * i + col * 4 + row] = cm[row * 5 + col];
|
|
1684
|
+
BIASES[4 * i + row] = cm[row * 5 + 4];
|
|
1685
|
+
}
|
|
1686
|
+
ALPHAS[i] = matrices[i].alpha;
|
|
1687
|
+
}
|
|
1688
|
+
const gl = ctx.gl;
|
|
1689
|
+
gl.uniformMatrix4fv(prog.uniform("u_colorMatrix"), false, MATRICES.subarray(0, 16 * n));
|
|
1690
|
+
gl.uniform4fv(prog.uniform("u_colorBias"), BIASES.subarray(0, 4 * n));
|
|
1691
|
+
gl.uniform1fv(prog.uniform("u_alpha"), ALPHAS.subarray(0, n));
|
|
1692
|
+
byProgram.set(prog, matrices.slice());
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
// src/draw/flushBatch.ts
|
|
1481
1696
|
var BATCH_MODEL = mat3.identity();
|
|
1482
1697
|
var SAMPLER_UNITS = new Int32Array(
|
|
1483
1698
|
Array.from({ length: BATCH_TEXTURE_SLOTS }, (_, i) => i)
|
|
@@ -1491,14 +1706,12 @@ function flushBatch(ctx) {
|
|
|
1491
1706
|
return;
|
|
1492
1707
|
}
|
|
1493
1708
|
const gl = ctx.gl;
|
|
1494
|
-
const prog = ctx.batchFill;
|
|
1709
|
+
const prog = staged.matrices.length > 1 ? ctx.batchFillMatrices ?? ctx.batchFill : ctx.batchFill;
|
|
1495
1710
|
gl.useProgram(prog.handle);
|
|
1496
1711
|
const indexCount = batch.uploadAndBind();
|
|
1497
1712
|
setProjAndModel(ctx, prog, BATCH_MODEL);
|
|
1498
1713
|
setColorUniform(ctx, prog, 1, 1, 1, 1);
|
|
1499
|
-
|
|
1500
|
-
gl.uniform1f(prog.uniform("u_synthBold"), staged.synthBold);
|
|
1501
|
-
setColorMatrixUniforms(ctx, prog, staged.colorMatrix);
|
|
1714
|
+
uploadBatchMatrices(ctx, prog, staged.matrices);
|
|
1502
1715
|
gl.activeTexture(gl.TEXTURE0);
|
|
1503
1716
|
gl.bindTexture(gl.TEXTURE_2D, ctx.whiteTexture);
|
|
1504
1717
|
for (let i = 0; i < staged.textures.length; i++) {
|
|
@@ -1509,16 +1722,13 @@ function flushBatch(ctx) {
|
|
|
1509
1722
|
FIELD_SCALES[2 * (i + 1) + 1] = entry.fieldScale[1];
|
|
1510
1723
|
continue;
|
|
1511
1724
|
}
|
|
1512
|
-
if (entry.kind === "ramps") {
|
|
1513
|
-
ctx.gradRamps.bind(i + 1);
|
|
1514
|
-
continue;
|
|
1515
|
-
}
|
|
1516
1725
|
ctx.imageCache.bind(entry.image, i + 1);
|
|
1517
1726
|
ctx.imageCache.setMagFilter(
|
|
1518
1727
|
entry.image,
|
|
1519
1728
|
entry.sampling === "nearest" ? gl.NEAREST : gl.LINEAR
|
|
1520
1729
|
);
|
|
1521
1730
|
}
|
|
1731
|
+
if (staged.ramps) ctx.gradRamps.bind(RAMP_SLOT);
|
|
1522
1732
|
gl.uniform1iv(prog.uniform("u_samplers"), SAMPLER_UNITS);
|
|
1523
1733
|
gl.uniform2fv(prog.uniform("u_fieldScale"), FIELD_SCALES);
|
|
1524
1734
|
applyClipTest(ctx, staged.clipDepth);
|
|
@@ -1693,9 +1903,46 @@ function isPxMarker(ref) {
|
|
|
1693
1903
|
function resolvedMarker(ref, scale) {
|
|
1694
1904
|
return { ...ref, size: resolveMarkerSize(ref, 0, quantizeStrokeScale(ref.size.px, scale)) };
|
|
1695
1905
|
}
|
|
1906
|
+
|
|
1907
|
+
// src/draw/batchRun.ts
|
|
1908
|
+
function slotForBitmap(staged, image, sampling) {
|
|
1909
|
+
for (let i = 0; i < staged.textures.length; i++) {
|
|
1910
|
+
const entry = staged.textures[i];
|
|
1911
|
+
if (entry.kind !== "bitmap" || entry.image !== image) continue;
|
|
1912
|
+
return entry.sampling === sampling ? i + 1 : -1;
|
|
1913
|
+
}
|
|
1914
|
+
return nextFreeSlot(staged);
|
|
1915
|
+
}
|
|
1916
|
+
function slotForRamps(staged) {
|
|
1917
|
+
if (staged.ramps) return RAMP_SLOT;
|
|
1918
|
+
return nextFreeSlot(staged) < 0 ? -1 : RAMP_SLOT;
|
|
1919
|
+
}
|
|
1920
|
+
function slotForAtlas(staged, id) {
|
|
1921
|
+
for (let i = 0; i < staged.textures.length; i++) {
|
|
1922
|
+
const entry = staged.textures[i];
|
|
1923
|
+
if (entry.kind === "atlas" && entry.id === id) return i + 1;
|
|
1924
|
+
}
|
|
1925
|
+
return nextFreeSlot(staged);
|
|
1926
|
+
}
|
|
1927
|
+
function nextFreeSlot(staged) {
|
|
1928
|
+
const taken = staged.textures.length + (staged.ramps ? 1 : 0);
|
|
1929
|
+
return taken + 1 < BATCH_TEXTURE_SLOTS ? staged.textures.length + 1 : -1;
|
|
1930
|
+
}
|
|
1931
|
+
function runTakes(ctx, staged, colorMatrix, alpha) {
|
|
1932
|
+
return staged.clipDepth === runClipDepth(ctx) && matrixIndex(staged.matrices, colorMatrix, alpha) >= 0;
|
|
1933
|
+
}
|
|
1934
|
+
function openRun(ctx) {
|
|
1935
|
+
ctx.batchState ??= { clipDepth: runClipDepth(ctx), matrices: [], textures: [], ramps: false };
|
|
1936
|
+
return ctx.batchState;
|
|
1937
|
+
}
|
|
1938
|
+
function takeMatrix(staged, colorMatrix, alpha) {
|
|
1939
|
+
const index = matrixIndex(staged.matrices, colorMatrix, alpha);
|
|
1940
|
+
if (index === staged.matrices.length) staged.matrices.push({ colorMatrix, alpha });
|
|
1941
|
+
return index;
|
|
1942
|
+
}
|
|
1696
1943
|
function batchPaint(ctx, fill, hasVColors) {
|
|
1697
1944
|
if (hasVColors) return void 0;
|
|
1698
|
-
const kind = fill.
|
|
1945
|
+
const kind = fill.kind ?? "solid";
|
|
1699
1946
|
if (kind === "solid") {
|
|
1700
1947
|
const solid = fill;
|
|
1701
1948
|
return { kind: "solid", color: solid.color, opacity: solid.opacity };
|
|
@@ -1706,8 +1953,8 @@ function batchPaint(ctx, fill, hasVColors) {
|
|
|
1706
1953
|
const grad = fill;
|
|
1707
1954
|
const g = gradientMap(ctx, grad.units);
|
|
1708
1955
|
if (!g) return null;
|
|
1709
|
-
if (grad.
|
|
1710
|
-
if (grad.
|
|
1956
|
+
if (grad.kind === "linear-gradient") return ramp(grad, PAINT_MODE_PLAIN, linearRampUV(g, grad));
|
|
1957
|
+
if (grad.kind === "radial-gradient") return ramp(grad, PAINT_MODE_RADIAL, radialRampUV(g, grad));
|
|
1711
1958
|
return ramp(grad, PAINT_MODE_CONIC, conicRampUV(g, grad));
|
|
1712
1959
|
}
|
|
1713
1960
|
function ramp(fill, mode, uv) {
|
|
@@ -1780,41 +2027,6 @@ function spaceToDevice(ctx, units) {
|
|
|
1780
2027
|
}
|
|
1781
2028
|
|
|
1782
2029
|
// src/draw/batch.ts
|
|
1783
|
-
function isIdentityColorMatrix(cm) {
|
|
1784
|
-
return cm === IDENTITY_COLOR_MATRIX || sameValues(IDENTITY_COLOR_MATRIX, cm);
|
|
1785
|
-
}
|
|
1786
|
-
function stagedStateIsLive(ctx, staged, image, sampling, synthBold = 0) {
|
|
1787
|
-
if (staged.clipDepth !== (ctx.clipStack.length === 0 ? ctx.clipDepth : runClipDepth(ctx))) return false;
|
|
1788
|
-
if (staged.synthBold !== synthBold) return false;
|
|
1789
|
-
if (!staged.foldsAlpha && staged.alpha !== ctx.state.alpha) return false;
|
|
1790
|
-
if (image !== void 0 && slotForBitmap(staged, image, sampling) < 0) return false;
|
|
1791
|
-
const colorMatrix = ctx.state.colorMatrix;
|
|
1792
|
-
return staged.colorMatrix === colorMatrix || sameValues(staged.colorMatrix, colorMatrix);
|
|
1793
|
-
}
|
|
1794
|
-
function slotForBitmap(staged, image, sampling) {
|
|
1795
|
-
for (let i = 0; i < staged.textures.length; i++) {
|
|
1796
|
-
const entry = staged.textures[i];
|
|
1797
|
-
if (entry.kind !== "bitmap" || entry.image !== image) continue;
|
|
1798
|
-
return entry.sampling === sampling ? i + 1 : -1;
|
|
1799
|
-
}
|
|
1800
|
-
return nextFreeSlot(staged);
|
|
1801
|
-
}
|
|
1802
|
-
function slotForRamps(staged) {
|
|
1803
|
-
for (let i = 0; i < staged.textures.length; i++) {
|
|
1804
|
-
if (staged.textures[i].kind === "ramps") return i + 1;
|
|
1805
|
-
}
|
|
1806
|
-
return nextFreeSlot(staged);
|
|
1807
|
-
}
|
|
1808
|
-
function slotForAtlas(staged, id) {
|
|
1809
|
-
for (let i = 0; i < staged.textures.length; i++) {
|
|
1810
|
-
const entry = staged.textures[i];
|
|
1811
|
-
if (entry.kind === "atlas" && entry.id === id) return i + 1;
|
|
1812
|
-
}
|
|
1813
|
-
return nextFreeSlot(staged);
|
|
1814
|
-
}
|
|
1815
|
-
function nextFreeSlot(staged) {
|
|
1816
|
-
return staged.textures.length + 1 < BATCH_TEXTURE_SLOTS ? staged.textures.length + 1 : -1;
|
|
1817
|
-
}
|
|
1818
2030
|
var MAX_BATCHED_MESH_VERTICES = 96;
|
|
1819
2031
|
var MAX_BATCHED_MESH_VERTICES_IN_RUN = 1024;
|
|
1820
2032
|
var meshBatching = {
|
|
@@ -1827,27 +2039,26 @@ function canBatchMesh(ctx, mesh) {
|
|
|
1827
2039
|
return mesh.vertices.length >> 1 <= cap;
|
|
1828
2040
|
}
|
|
1829
2041
|
function stageSolid(ctx, vertices) {
|
|
1830
|
-
|
|
2042
|
+
const colorMatrix = IDENTITY_COLOR_MATRIX ;
|
|
2043
|
+
const alpha = 1 ;
|
|
2044
|
+
const open = ctx.batchState;
|
|
2045
|
+
if (open !== void 0 && !runTakes(ctx, open, colorMatrix, alpha)) flushBatch(ctx);
|
|
1831
2046
|
if (ctx.drawBatch.wouldOverflow(vertices)) flushBatch(ctx);
|
|
1832
|
-
|
|
2047
|
+
const matrix = takeMatrix(openRun(ctx), colorMatrix, alpha);
|
|
2048
|
+
return packSlot(withMatrix(WHITE_SLOT, matrix), PAINT_MODE_PLAIN);
|
|
1833
2049
|
}
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
clipDepth: runClipDepth(ctx),
|
|
1842
|
-
synthBold: 0,
|
|
1843
|
-
foldsAlpha,
|
|
1844
|
-
textures: []
|
|
1845
|
-
};
|
|
1846
|
-
}
|
|
1847
|
-
return ctx.batchState;
|
|
2050
|
+
var UNTINTED = [1, 1, 1, 1];
|
|
2051
|
+
function texturedUnder(ctx, alphaInEntry) {
|
|
2052
|
+
const tint = textureTint(ctx);
|
|
2053
|
+
if (tint !== null) return { colorMatrix: IDENTITY_COLOR_MATRIX, alpha: 1, tint, slot: 0 };
|
|
2054
|
+
const colorMatrix = ctx.state.colorMatrix;
|
|
2055
|
+
const alpha = ctx.state.alpha;
|
|
2056
|
+
return alphaInEntry ? { colorMatrix, alpha, tint: UNTINTED, slot: 0 } : { colorMatrix, alpha: 1, tint: [1, 1, 1, alpha], slot: 0 };
|
|
1848
2057
|
}
|
|
1849
2058
|
function stageImage(ctx, image, sampling) {
|
|
1850
|
-
|
|
2059
|
+
const under = texturedUnder(ctx, false);
|
|
2060
|
+
const open = ctx.batchState;
|
|
2061
|
+
if (open !== void 0 && (!runTakes(ctx, open, under.colorMatrix, under.alpha) || slotForBitmap(open, image, sampling) < 0)) {
|
|
1851
2062
|
flushBatch(ctx);
|
|
1852
2063
|
}
|
|
1853
2064
|
if (ctx.drawBatch.wouldOverflow(4)) flushBatch(ctx);
|
|
@@ -1857,46 +2068,49 @@ function stageImage(ctx, image, sampling) {
|
|
|
1857
2068
|
staged.textures.push({ kind: "bitmap", image, sampling });
|
|
1858
2069
|
slot = staged.textures.length;
|
|
1859
2070
|
}
|
|
1860
|
-
|
|
2071
|
+
under.slot = withMatrix(slot, takeMatrix(staged, under.colorMatrix, under.alpha));
|
|
2072
|
+
return under;
|
|
1861
2073
|
}
|
|
1862
|
-
function stageGlyphs(ctx, atlasId, fieldScale
|
|
1863
|
-
|
|
2074
|
+
function stageGlyphs(ctx, atlasId, fieldScale) {
|
|
2075
|
+
const colorMatrix = IDENTITY_COLOR_MATRIX ;
|
|
2076
|
+
const alpha = 1 ;
|
|
2077
|
+
const open = ctx.batchState;
|
|
2078
|
+
if (open !== void 0 && (!runTakes(ctx, open, colorMatrix, alpha) || slotForAtlas(open, atlasId) < 0)) {
|
|
1864
2079
|
flushBatch(ctx);
|
|
1865
2080
|
}
|
|
1866
2081
|
if (ctx.drawBatch.wouldOverflow(4)) flushBatch(ctx);
|
|
1867
2082
|
const staged = openRun(ctx);
|
|
1868
|
-
staged.synthBold = synthBold;
|
|
1869
2083
|
let slot = slotForAtlas(staged, atlasId);
|
|
1870
2084
|
if (slot > staged.textures.length) {
|
|
1871
2085
|
staged.textures.push({ kind: "atlas", id: atlasId, fieldScale });
|
|
1872
2086
|
slot = staged.textures.length;
|
|
1873
2087
|
}
|
|
1874
|
-
return
|
|
2088
|
+
return withMatrix(slot, takeMatrix(staged, colorMatrix, alpha));
|
|
1875
2089
|
}
|
|
1876
2090
|
function stageRamps(ctx, stops, space, vertices) {
|
|
1877
2091
|
if (ctx.gradRamps.wouldReshape(stops, space)) flushBatch(ctx);
|
|
1878
|
-
|
|
2092
|
+
const under = texturedUnder(ctx, true);
|
|
2093
|
+
const open = ctx.batchState;
|
|
2094
|
+
if (open !== void 0 && (!runTakes(ctx, open, under.colorMatrix, under.alpha) || slotForRamps(open) < 0)) {
|
|
1879
2095
|
flushBatch(ctx);
|
|
1880
2096
|
}
|
|
1881
2097
|
if (ctx.drawBatch.wouldOverflow(vertices)) flushBatch(ctx);
|
|
1882
2098
|
const staged = openRun(ctx);
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
slot
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
}
|
|
1890
|
-
function stagedAlpha(ctx, staged, opacity) {
|
|
1891
|
-
return (opacity ?? 1) * (staged.foldsAlpha ? ctx.state.alpha : 1);
|
|
2099
|
+
const slot = slotForRamps(staged);
|
|
2100
|
+
staged.ramps = true;
|
|
2101
|
+
return {
|
|
2102
|
+
slot: withMatrix(slot, takeMatrix(staged, under.colorMatrix, under.alpha)),
|
|
2103
|
+
tint: under.tint,
|
|
2104
|
+
rowV: ctx.gradRamps.rowV(ctx.gradRamps.upload(stops, space))
|
|
2105
|
+
};
|
|
1892
2106
|
}
|
|
1893
|
-
function stagedColor(ctx,
|
|
2107
|
+
function stagedColor(ctx, paint) {
|
|
1894
2108
|
const [r, g, b, a] = resolveColor(paint.color);
|
|
1895
|
-
return
|
|
2109
|
+
return foldColor(ctx, r, g, b, a * (paint.opacity ?? 1));
|
|
1896
2110
|
}
|
|
1897
2111
|
function pushRect(ctx, rect, fill) {
|
|
1898
|
-
const
|
|
1899
|
-
const [r, g, b, a] = stagedColor(ctx,
|
|
2112
|
+
const slot = stageSolid(ctx, 4);
|
|
2113
|
+
const [r, g, b, a] = stagedColor(ctx, fill);
|
|
1900
2114
|
if (!ctx.drawBatch.pushRect(
|
|
1901
2115
|
rect.x,
|
|
1902
2116
|
rect.y,
|
|
@@ -1906,18 +2120,19 @@ function pushRect(ctx, rect, fill) {
|
|
|
1906
2120
|
r,
|
|
1907
2121
|
g,
|
|
1908
2122
|
b,
|
|
1909
|
-
a
|
|
2123
|
+
a,
|
|
2124
|
+
slot
|
|
1910
2125
|
)) retryUncut(ctx, rect, fill, pushRect);
|
|
1911
2126
|
}
|
|
1912
2127
|
function pushMesh(ctx, mesh, paint) {
|
|
1913
|
-
const
|
|
1914
|
-
const [r, g, b, a] = stagedColor(ctx,
|
|
1915
|
-
if (!ctx.drawBatch.pushMesh(mesh, ctx.state.transform, r, g, b, a)) {
|
|
2128
|
+
const slot = stageSolid(ctx, mesh.vertices.length >> 1);
|
|
2129
|
+
const [r, g, b, a] = stagedColor(ctx, paint);
|
|
2130
|
+
if (!ctx.drawBatch.pushMesh(mesh, ctx.state.transform, r, g, b, a, slot)) {
|
|
1916
2131
|
retryUncut(ctx, mesh, paint, pushMesh);
|
|
1917
2132
|
}
|
|
1918
2133
|
}
|
|
1919
2134
|
function pushRampRect(ctx, rect, paint) {
|
|
1920
|
-
const {
|
|
2135
|
+
const { slot, tint, rowV } = stageRamps(ctx, paint.stops, paint.space, 4);
|
|
1921
2136
|
const { uv, post } = rampVertices(paint, rowV);
|
|
1922
2137
|
if (!ctx.drawBatch.pushGradientRect(
|
|
1923
2138
|
rect.x,
|
|
@@ -1929,14 +2144,14 @@ function pushRampRect(ctx, rect, paint) {
|
|
|
1929
2144
|
post,
|
|
1930
2145
|
slot,
|
|
1931
2146
|
paint.mode,
|
|
1932
|
-
|
|
1933
|
-
1,
|
|
1934
|
-
|
|
1935
|
-
|
|
2147
|
+
tint[0],
|
|
2148
|
+
tint[1],
|
|
2149
|
+
tint[2],
|
|
2150
|
+
(paint.opacity ?? 1) * tint[3]
|
|
1936
2151
|
)) retryUncut(ctx, rect, paint, pushRampRect);
|
|
1937
2152
|
}
|
|
1938
2153
|
function pushRampMesh(ctx, mesh, paint) {
|
|
1939
|
-
const {
|
|
2154
|
+
const { slot, tint, rowV } = stageRamps(ctx, paint.stops, paint.space, mesh.vertices.length >> 1);
|
|
1940
2155
|
const { uv, post } = rampVertices(paint, rowV);
|
|
1941
2156
|
if (!ctx.drawBatch.pushGradientMesh(
|
|
1942
2157
|
mesh,
|
|
@@ -1945,10 +2160,10 @@ function pushRampMesh(ctx, mesh, paint) {
|
|
|
1945
2160
|
post,
|
|
1946
2161
|
slot,
|
|
1947
2162
|
paint.mode,
|
|
1948
|
-
|
|
1949
|
-
1,
|
|
1950
|
-
|
|
1951
|
-
|
|
2163
|
+
tint[0],
|
|
2164
|
+
tint[1],
|
|
2165
|
+
tint[2],
|
|
2166
|
+
(paint.opacity ?? 1) * tint[3]
|
|
1952
2167
|
)) retryUncut(ctx, mesh, paint, pushRampMesh);
|
|
1953
2168
|
}
|
|
1954
2169
|
function tryStageFill(ctx, mesh, paint) {
|
|
@@ -1960,6 +2175,23 @@ function tryStageFill(ctx, mesh, paint) {
|
|
|
1960
2175
|
flushForOwnDraw(ctx);
|
|
1961
2176
|
return false;
|
|
1962
2177
|
}
|
|
2178
|
+
var NO_FIELD = [0, 0];
|
|
2179
|
+
function stageColorGlyphs(ctx, atlasId) {
|
|
2180
|
+
const under = texturedUnder(ctx, false);
|
|
2181
|
+
const open = ctx.batchState;
|
|
2182
|
+
if (open !== void 0 && (!runTakes(ctx, open, under.colorMatrix, under.alpha) || slotForAtlas(open, atlasId) < 0)) {
|
|
2183
|
+
flushBatch(ctx);
|
|
2184
|
+
}
|
|
2185
|
+
if (ctx.drawBatch.wouldOverflow(4)) flushBatch(ctx);
|
|
2186
|
+
const staged = openRun(ctx);
|
|
2187
|
+
let slot = slotForAtlas(staged, atlasId);
|
|
2188
|
+
if (slot > staged.textures.length) {
|
|
2189
|
+
staged.textures.push({ kind: "atlas", id: atlasId, fieldScale: NO_FIELD });
|
|
2190
|
+
slot = staged.textures.length;
|
|
2191
|
+
}
|
|
2192
|
+
under.slot = withMatrix(slot, takeMatrix(staged, under.colorMatrix, under.alpha));
|
|
2193
|
+
return under;
|
|
2194
|
+
}
|
|
1963
2195
|
function patternTileSpace(spaceInverse, origin, transform) {
|
|
1964
2196
|
const [a, b, c, d] = transform ?? [1, 0, 0, 1];
|
|
1965
2197
|
const placement = mat3.fromAffine([a, b, c, d, origin.x, origin.y]);
|
|
@@ -2004,7 +2236,7 @@ function unownedPaintResources(gl) {
|
|
|
2004
2236
|
return made;
|
|
2005
2237
|
}
|
|
2006
2238
|
function bindPattern(ctx, prog, fill, model) {
|
|
2007
|
-
const tex = fill.
|
|
2239
|
+
const tex = fill.texture;
|
|
2008
2240
|
const entry = getTexture(tex.id);
|
|
2009
2241
|
if (!entry) {
|
|
2010
2242
|
const isDev = typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true;
|
|
@@ -2020,8 +2252,10 @@ function bindPattern(ctx, prog, fill, model) {
|
|
|
2020
2252
|
gl.useProgram(prog.handle);
|
|
2021
2253
|
setProjAndModel(ctx, prog, model);
|
|
2022
2254
|
setColorMatrixUniforms(ctx, prog);
|
|
2023
|
-
gl.uniformMatrix3fv(prog.uniform("u_worldInv"), false, mat3.toUniform(tileSpace));
|
|
2024
2255
|
const [tw, th] = textureSize(entry.source);
|
|
2256
|
+
tileSpace[6] -= Math.floor(tileSpace[6] / tw) * tw;
|
|
2257
|
+
tileSpace[7] -= Math.floor(tileSpace[7] / th) * th;
|
|
2258
|
+
gl.uniformMatrix3fv(prog.uniform("u_worldInv"), false, mat3.toUniform(tileSpace));
|
|
2025
2259
|
gl.uniform2f(prog.uniform("u_tileSize"), tw, th);
|
|
2026
2260
|
ctx.textureCache.bind(tex.id, 0);
|
|
2027
2261
|
gl.uniform1i(prog.uniform("u_sampler"), 0);
|
|
@@ -2037,37 +2271,36 @@ function textureSize(source) {
|
|
|
2037
2271
|
function bindGradient(ctx, prog, fill, model) {
|
|
2038
2272
|
const inverse = gradientSpaceInverse(ctx, fill.units);
|
|
2039
2273
|
if (!inverse) return null;
|
|
2274
|
+
const about = fill.kind === "linear-gradient" ? fill.from : fill.center;
|
|
2275
|
+
const aboutStart = mat3.multiply(mat3.translated(mat3.identity(), -about.x, -about.y), inverse);
|
|
2040
2276
|
const gl = ctx.gl;
|
|
2041
2277
|
const row = ctx.gradRamps.upload(fill.stops, fill.interpolate ?? "rgb");
|
|
2042
2278
|
gl.useProgram(prog.handle);
|
|
2043
2279
|
setProjAndModel(ctx, prog, model);
|
|
2044
|
-
gl.uniformMatrix3fv(prog.uniform("u_worldInv"), false, mat3.toUniform(
|
|
2280
|
+
gl.uniformMatrix3fv(prog.uniform("u_worldInv"), false, mat3.toUniform(aboutStart));
|
|
2045
2281
|
setColorMatrixUniforms(ctx, prog);
|
|
2046
2282
|
ctx.gradRamps.bind(0);
|
|
2047
2283
|
gl.uniform1i(prog.uniform("u_ramp"), 0);
|
|
2048
2284
|
gl.uniform1f(prog.uniform("u_rampV"), ctx.gradRamps.rowV(row));
|
|
2049
2285
|
setAlphaUniform(ctx, prog, ctx.state.alpha);
|
|
2050
2286
|
gl.uniform1f(prog.uniform("u_opacity"), fill.opacity ?? 1);
|
|
2051
|
-
if (fill.
|
|
2287
|
+
if (fill.kind === "linear-gradient") {
|
|
2052
2288
|
gl.uniform1i(prog.uniform("u_gradKind"), 0);
|
|
2053
2289
|
const dx = fill.to.x - fill.from.x;
|
|
2054
2290
|
const dy = fill.to.y - fill.from.y;
|
|
2055
2291
|
const len = Math.hypot(dx, dy) || 1;
|
|
2056
|
-
gl.uniform2f(prog.uniform("u_gradP0"), fill.from.x, fill.from.y);
|
|
2057
2292
|
gl.uniform2f(prog.uniform("u_gradDir"), dx / len, dy / len);
|
|
2058
2293
|
gl.uniform1f(prog.uniform("u_gradLen"), len);
|
|
2059
2294
|
gl.uniform1f(prog.uniform("u_gradRadius"), 0);
|
|
2060
2295
|
gl.uniform1f(prog.uniform("u_gradAngle"), 0);
|
|
2061
|
-
} else if (fill.
|
|
2296
|
+
} else if (fill.kind === "radial-gradient") {
|
|
2062
2297
|
gl.uniform1i(prog.uniform("u_gradKind"), 1);
|
|
2063
|
-
gl.uniform2f(prog.uniform("u_gradP0"), fill.center.x, fill.center.y);
|
|
2064
2298
|
gl.uniform2f(prog.uniform("u_gradDir"), 0, 0);
|
|
2065
2299
|
gl.uniform1f(prog.uniform("u_gradLen"), 0);
|
|
2066
2300
|
gl.uniform1f(prog.uniform("u_gradRadius"), fill.radius);
|
|
2067
2301
|
gl.uniform1f(prog.uniform("u_gradAngle"), 0);
|
|
2068
2302
|
} else {
|
|
2069
2303
|
gl.uniform1i(prog.uniform("u_gradKind"), 2);
|
|
2070
|
-
gl.uniform2f(prog.uniform("u_gradP0"), fill.center.x, fill.center.y);
|
|
2071
2304
|
gl.uniform2f(prog.uniform("u_gradDir"), 0, 0);
|
|
2072
2305
|
gl.uniform1f(prog.uniform("u_gradLen"), 0);
|
|
2073
2306
|
gl.uniform1f(prog.uniform("u_gradRadius"), 0);
|
|
@@ -2078,12 +2311,11 @@ function bindGradient(ctx, prog, fill, model) {
|
|
|
2078
2311
|
|
|
2079
2312
|
// src/draw/pathPaint.ts
|
|
2080
2313
|
function drawPath(ctx, cmd) {
|
|
2081
|
-
if (cmd.fill) drawPathFill(ctx, cmd);
|
|
2314
|
+
if (cmd.fill?.paint) drawPathFill(ctx, cmd, cmd.fill.paint);
|
|
2082
2315
|
if (cmd.stroke?.paint) drawPathStroke(ctx, cmd);
|
|
2083
2316
|
}
|
|
2084
|
-
function drawPathFill(ctx, cmd) {
|
|
2085
|
-
const
|
|
2086
|
-
const isSolid2 = fill.fill === void 0 || fill.fill === "solid";
|
|
2317
|
+
function drawPathFill(ctx, cmd, fill) {
|
|
2318
|
+
const isSolid2 = fill.kind === void 0 || fill.kind === "solid";
|
|
2087
2319
|
const hasVColors = !!(cmd.vertexColors && cmd.vertexColors.length > 0);
|
|
2088
2320
|
const solid = fill;
|
|
2089
2321
|
const paint = batchPaint(ctx, fill, hasVColors);
|
|
@@ -2161,9 +2393,9 @@ function drawPathFillVColor(ctx, cmd, fill, handle) {
|
|
|
2161
2393
|
gl.bindVertexArray(null);
|
|
2162
2394
|
}
|
|
2163
2395
|
function bindPathFillByKind(ctx, fill) {
|
|
2164
|
-
const kind = fill.
|
|
2396
|
+
const kind = fill.kind ?? "solid";
|
|
2165
2397
|
if (kind === "solid") return bindPathFillSolid(ctx, fill);
|
|
2166
|
-
if (kind === "
|
|
2398
|
+
if (kind === "texture") return bindPathFillPattern(ctx, fill);
|
|
2167
2399
|
if (kind === "linear-gradient" || kind === "radial-gradient" || kind === "conic-gradient") {
|
|
2168
2400
|
return bindPathFillGradient(ctx, fill);
|
|
2169
2401
|
}
|
|
@@ -2224,7 +2456,7 @@ function drawPathStroke(ctx, rawCmd) {
|
|
|
2224
2456
|
function drawPathStrokeUnclipped(ctx, cmd, spaces) {
|
|
2225
2457
|
const stroke = cmd.stroke;
|
|
2226
2458
|
const paint = stroke.paint;
|
|
2227
|
-
const isSolid2 = paint.
|
|
2459
|
+
const isSolid2 = paint.kind === void 0 || paint.kind === "solid";
|
|
2228
2460
|
const solid = paint;
|
|
2229
2461
|
const mesh = strokeMesh(cmd.path, stroke, ctx.flattenTolerance, spaces);
|
|
2230
2462
|
if (mesh.indices.length === 0) return;
|
|
@@ -2273,7 +2505,7 @@ function doubledVertexWidths(widths) {
|
|
|
2273
2505
|
function drawPathStrokeStenciled(ctx, cmd, align, spaces) {
|
|
2274
2506
|
const stroke = cmd.stroke;
|
|
2275
2507
|
const paint = stroke.paint;
|
|
2276
|
-
const isSolid2 = paint.
|
|
2508
|
+
const isSolid2 = paint.kind === void 0 || paint.kind === "solid";
|
|
2277
2509
|
const solid = paint;
|
|
2278
2510
|
const widerStroke = {
|
|
2279
2511
|
...stroke,
|
|
@@ -2333,10 +2565,9 @@ function outlineGroupMesh(group, dx, dy) {
|
|
|
2333
2565
|
}
|
|
2334
2566
|
function outlineGroupStrokeMesh(group, dx, dy, transform) {
|
|
2335
2567
|
const stroke = group.stroke;
|
|
2336
|
-
if (!stroke) return null;
|
|
2568
|
+
if (!strokeHasInk(stroke)) return null;
|
|
2337
2569
|
const width = stroke.width ?? 1;
|
|
2338
2570
|
if (typeof width === "object") {
|
|
2339
|
-
if (!(width.px > 0)) return null;
|
|
2340
2571
|
const shear = shearOf(group);
|
|
2341
2572
|
const [a, b, c, d] = [transform[0], transform[1], transform[3], transform[4]];
|
|
2342
2573
|
return mergeGlyphMeshes(group, dx, dy, (glyph) => {
|
|
@@ -2355,7 +2586,6 @@ function outlineGroupStrokeMesh(group, dx, dy, transform) {
|
|
|
2355
2586
|
);
|
|
2356
2587
|
});
|
|
2357
2588
|
}
|
|
2358
|
-
if (!(width > 0)) return null;
|
|
2359
2589
|
return mergeGlyphMeshes(group, dx, dy, (glyph) => glyph.scale > 0 ? outlineStrokeMesh(
|
|
2360
2590
|
glyph.key,
|
|
2361
2591
|
glyph.d,
|
|
@@ -2395,6 +2625,49 @@ function mergeGlyphMeshes(group, dx, dy, meshFor) {
|
|
|
2395
2625
|
}
|
|
2396
2626
|
return { vertices, indices };
|
|
2397
2627
|
}
|
|
2628
|
+
function drawColorGlyphs(ctx, group, dx, dy) {
|
|
2629
|
+
const fill = group.fill?.paint;
|
|
2630
|
+
if (fill === void 0 || group.quads.length === 0) return;
|
|
2631
|
+
if (!syncColorPageTexture(ctx.textureCache, group.page)) return;
|
|
2632
|
+
const opacity = isSolidPaint(fill) ? resolveColor(fill.color)[3] : 1;
|
|
2633
|
+
const atlasId = colorPageTextureId(group.page);
|
|
2634
|
+
const batch = ctx.drawBatch;
|
|
2635
|
+
const m = ctx.state.transform;
|
|
2636
|
+
let { slot, tint } = stageColorGlyphs(ctx, atlasId);
|
|
2637
|
+
const restage = () => {
|
|
2638
|
+
({ slot, tint } = stageColorGlyphs(ctx, atlasId));
|
|
2639
|
+
};
|
|
2640
|
+
for (const q of group.quads) {
|
|
2641
|
+
if (batch.wouldOverflow(4)) {
|
|
2642
|
+
flushBatch(ctx);
|
|
2643
|
+
restage();
|
|
2644
|
+
}
|
|
2645
|
+
let cut;
|
|
2646
|
+
while (!batch.pushQuad(
|
|
2647
|
+
q.x0 + dx,
|
|
2648
|
+
q.y0 + dy,
|
|
2649
|
+
q.x1 - q.x0,
|
|
2650
|
+
q.y1 - q.y0,
|
|
2651
|
+
m,
|
|
2652
|
+
q.u0,
|
|
2653
|
+
q.v0,
|
|
2654
|
+
q.u1,
|
|
2655
|
+
q.v1,
|
|
2656
|
+
opacity * tint[3],
|
|
2657
|
+
slot,
|
|
2658
|
+
tint[0],
|
|
2659
|
+
tint[1],
|
|
2660
|
+
tint[2]
|
|
2661
|
+
)) {
|
|
2662
|
+
cut = enterUncut(ctx);
|
|
2663
|
+
restage();
|
|
2664
|
+
}
|
|
2665
|
+
if (cut !== void 0) {
|
|
2666
|
+
leaveUncut(ctx, cut);
|
|
2667
|
+
restage();
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2398
2671
|
|
|
2399
2672
|
// src/draw/scratchPass.ts
|
|
2400
2673
|
var UNIT_QUAD = {
|
|
@@ -2403,7 +2676,7 @@ var UNIT_QUAD = {
|
|
|
2403
2676
|
};
|
|
2404
2677
|
function renderPaintToScratch(ctx, fill, rect) {
|
|
2405
2678
|
const scratch = ctx.paintScratch;
|
|
2406
|
-
const bind = getPaintDrawKind(fill.
|
|
2679
|
+
const bind = getPaintDrawKind(fill.kind)?.bind;
|
|
2407
2680
|
if (!scratch || !bind) return null;
|
|
2408
2681
|
const ratio = ctx.deviceWidth && ctx.widthCss > 0 ? ctx.deviceWidth / ctx.widthCss : 1;
|
|
2409
2682
|
const deviceW = ctx.deviceWidth ?? Math.round(ctx.widthCss);
|
|
@@ -2492,21 +2765,22 @@ function drawText(ctx, cmd) {
|
|
|
2492
2765
|
align,
|
|
2493
2766
|
direction,
|
|
2494
2767
|
tabSize: style.tabSize,
|
|
2495
|
-
justify: cmd.justify
|
|
2496
|
-
outlineMinSize
|
|
2768
|
+
justify: cmd.justify
|
|
2497
2769
|
});
|
|
2498
|
-
|
|
2770
|
+
const { groups, decorations } = cachedPaintRuns(laid, cmd.runs, { outlineMinSize });
|
|
2771
|
+
if (groups.length === 0 && decorations.length === 0) return;
|
|
2499
2772
|
const dx = cmd.x;
|
|
2500
2773
|
const dy = cmd.y + verticalAlignOffset(cmd.verticalAlign, cmd.height, laid.bounds.height);
|
|
2501
|
-
for (const group of
|
|
2774
|
+
for (const group of groups) {
|
|
2502
2775
|
if (group.source === "outline") drawTextOutlineGroup(ctx, group, dx, dy);
|
|
2776
|
+
else if (group.source === "color") drawColorGlyphs(ctx, group, dx, dy);
|
|
2503
2777
|
else drawTextGroup(ctx, group, dx, dy);
|
|
2504
2778
|
}
|
|
2505
|
-
drawTextDecorations(ctx,
|
|
2779
|
+
drawTextDecorations(ctx, decorations, dx, dy);
|
|
2506
2780
|
}
|
|
2507
2781
|
function drawTextOutlineGroup(ctx, group, dx, dy) {
|
|
2508
|
-
const fill = group.fill;
|
|
2509
|
-
if (fill !==
|
|
2782
|
+
const fill = group.fill?.paint;
|
|
2783
|
+
if (fill !== void 0) {
|
|
2510
2784
|
const mesh = outlineGroupMesh(group, dx, dy);
|
|
2511
2785
|
if (mesh) drawOutlineMesh(ctx, fill, mesh);
|
|
2512
2786
|
}
|
|
@@ -2523,24 +2797,28 @@ function drawOutlineMesh(ctx, fill, mesh) {
|
|
|
2523
2797
|
}
|
|
2524
2798
|
function drawTextDecorations(ctx, decorations, dx, dy) {
|
|
2525
2799
|
for (const d of decorations) {
|
|
2526
|
-
|
|
2527
|
-
|
|
2800
|
+
const paint = d.fill.paint;
|
|
2801
|
+
if (paint === void 0) continue;
|
|
2802
|
+
if (glyphPaintKindOf(paint) !== null) {
|
|
2803
|
+
drawOutlineMesh(ctx, paint, ruleMesh(d.x0 + dx, d.y0 + dy, d.x1 + dx, d.y1 + dy));
|
|
2528
2804
|
continue;
|
|
2529
2805
|
}
|
|
2530
|
-
const [r, g, b, a] =
|
|
2806
|
+
const [r, g, b, a] = isSolidPaint(paint) ? resolveColor(paint.color) : [0, 0, 0, 1];
|
|
2531
2807
|
let cut;
|
|
2532
2808
|
for (; ; ) {
|
|
2533
|
-
const
|
|
2809
|
+
const slot = stageSolid(ctx, 4);
|
|
2810
|
+
const rgba = foldColor(ctx, r, g, b, a);
|
|
2534
2811
|
if (ctx.drawBatch.pushRect(
|
|
2535
2812
|
d.x0 + dx,
|
|
2536
2813
|
d.y0 + dy,
|
|
2537
2814
|
d.x1 - d.x0,
|
|
2538
2815
|
d.y1 - d.y0,
|
|
2539
2816
|
ctx.state.transform,
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2817
|
+
rgba[0],
|
|
2818
|
+
rgba[1],
|
|
2819
|
+
rgba[2],
|
|
2820
|
+
rgba[3],
|
|
2821
|
+
slot
|
|
2544
2822
|
)) break;
|
|
2545
2823
|
cut = enterUncut(ctx);
|
|
2546
2824
|
}
|
|
@@ -2572,21 +2850,22 @@ function drawTextGroup(ctx, group, dx, dy) {
|
|
|
2572
2850
|
if (!fieldScale) return;
|
|
2573
2851
|
const bold = group.synthetic.bold ? SYNTH_BOLD_AMOUNT : 0;
|
|
2574
2852
|
const tanItalic = group.synthetic.italic ? Math.tan(SYNTHETIC_ITALIC_RADIANS) : 0;
|
|
2575
|
-
const
|
|
2853
|
+
const fill = group.fill?.paint;
|
|
2854
|
+
if (fill === void 0) return;
|
|
2855
|
+
const paintKind = glyphPaintKindOf(fill);
|
|
2576
2856
|
if (paintKind !== null) {
|
|
2577
2857
|
drawPaintedGlyphs(ctx, group, dx, dy, paintKind, atlasId, mode, bold, tanItalic, fieldScale);
|
|
2578
2858
|
return;
|
|
2579
2859
|
}
|
|
2580
|
-
const color =
|
|
2860
|
+
const color = isSolidPaint(fill) ? resolveColor(fill.color) : [0, 0, 0, 1];
|
|
2581
2861
|
const batch = ctx.drawBatch;
|
|
2582
2862
|
const m = ctx.state.transform;
|
|
2583
|
-
let
|
|
2584
|
-
|
|
2863
|
+
let slot = stageGlyphs(ctx, atlasId, fieldScale);
|
|
2864
|
+
const rgba = foldColor(ctx, color[0], color[1], color[2], color[3]);
|
|
2585
2865
|
for (const q of group.quads) {
|
|
2586
2866
|
if (batch.wouldOverflow(4)) {
|
|
2587
2867
|
flushBatch(ctx);
|
|
2588
|
-
|
|
2589
|
-
alpha = color[3] * (staged.foldsAlpha ? ctx.state.alpha : 1);
|
|
2868
|
+
slot = stageGlyphs(ctx, atlasId, fieldScale);
|
|
2590
2869
|
}
|
|
2591
2870
|
let cut;
|
|
2592
2871
|
while (!batch.pushGlyph(
|
|
@@ -2601,27 +2880,26 @@ function drawTextGroup(ctx, group, dx, dy) {
|
|
|
2601
2880
|
q.v0,
|
|
2602
2881
|
q.u1,
|
|
2603
2882
|
q.v1,
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2883
|
+
rgba[0],
|
|
2884
|
+
rgba[1],
|
|
2885
|
+
rgba[2],
|
|
2886
|
+
rgba[3],
|
|
2608
2887
|
slot,
|
|
2609
|
-
mode
|
|
2888
|
+
mode,
|
|
2889
|
+
bold
|
|
2610
2890
|
)) {
|
|
2611
2891
|
cut = enterUncut(ctx);
|
|
2612
|
-
|
|
2613
|
-
alpha = color[3] * (staged.foldsAlpha ? ctx.state.alpha : 1);
|
|
2892
|
+
slot = stageGlyphs(ctx, atlasId, fieldScale);
|
|
2614
2893
|
}
|
|
2615
2894
|
if (cut !== void 0) {
|
|
2616
2895
|
leaveUncut(ctx, cut);
|
|
2617
|
-
|
|
2618
|
-
alpha = color[3] * (staged.foldsAlpha ? ctx.state.alpha : 1);
|
|
2896
|
+
slot = stageGlyphs(ctx, atlasId, fieldScale);
|
|
2619
2897
|
}
|
|
2620
2898
|
}
|
|
2621
2899
|
}
|
|
2622
2900
|
function glyphPaintKindOf(fill) {
|
|
2623
|
-
const kind = fill
|
|
2624
|
-
if (kind === "
|
|
2901
|
+
const kind = fill.kind ?? "solid";
|
|
2902
|
+
if (kind === "texture") return "pattern";
|
|
2625
2903
|
if (kind === "linear-gradient" || kind === "radial-gradient" || kind === "conic-gradient") {
|
|
2626
2904
|
return "gradient";
|
|
2627
2905
|
}
|
|
@@ -2632,7 +2910,7 @@ function drawPaintedGlyphs(ctx, group, dx, dy, kind, atlasId, mode, bold, tanIta
|
|
|
2632
2910
|
const prog = ctx.glyphPaintProgram?.(kind);
|
|
2633
2911
|
if (!prog) return;
|
|
2634
2912
|
flushForOwnDraw(ctx);
|
|
2635
|
-
const fill = group.fill;
|
|
2913
|
+
const fill = group.fill.paint;
|
|
2636
2914
|
const m = ctx.state.transform;
|
|
2637
2915
|
let bound;
|
|
2638
2916
|
if (kind === "pattern") {
|
|
@@ -2672,7 +2950,8 @@ function drawPaintedGlyphs(ctx, group, dx, dy, kind, atlasId, mode, bold, tanIta
|
|
|
2672
2950
|
1,
|
|
2673
2951
|
1,
|
|
2674
2952
|
GLYPH_PAINT_ATLAS_UNIT,
|
|
2675
|
-
mode
|
|
2953
|
+
mode,
|
|
2954
|
+
0
|
|
2676
2955
|
);
|
|
2677
2956
|
}
|
|
2678
2957
|
drawStagedGlyphs(ctx);
|
|
@@ -2713,9 +2992,22 @@ function glyphScreenBounds(group, dx, dy, tanItalic, m) {
|
|
|
2713
2992
|
var SPRITE_STRIDE = 9;
|
|
2714
2993
|
|
|
2715
2994
|
// src/draw/image.ts
|
|
2995
|
+
var MINIFIED = 2;
|
|
2996
|
+
function minifiedLimits(ctx) {
|
|
2997
|
+
const m = ctx.state.transform;
|
|
2998
|
+
const dpr = ctx.deviceWidth && ctx.widthCss ? ctx.deviceWidth / ctx.widthCss : 1;
|
|
2999
|
+
const k = (MINIFIED * dpr) ** 2;
|
|
3000
|
+
return [k * (m[0] * m[0] + m[1] * m[1]), k * (m[3] * m[3] + m[4] * m[4])];
|
|
3001
|
+
}
|
|
3002
|
+
var isMinified = (limit, sw, sh, dw, dh) => sw * sw >= limit[0] * dw * dw || sh * sh >= limit[1] * dh * dh;
|
|
2716
3003
|
function drawImage(ctx, cmd) {
|
|
2717
3004
|
ctx.imageCache.upload(cmd.image, cmd.image);
|
|
2718
|
-
|
|
3005
|
+
if (ctx.imageCache.lacksMipmaps(cmd.image)) {
|
|
3006
|
+
const sw = cmd.source?.w ?? cmd.image.width;
|
|
3007
|
+
const sh = cmd.source?.h ?? cmd.image.height;
|
|
3008
|
+
if (isMinified(minifiedLimits(ctx), sw, sh, cmd.w, cmd.h)) ctx.imageCache.buildMipmaps(cmd.image);
|
|
3009
|
+
}
|
|
3010
|
+
const { slot, tint } = stageImage(ctx, cmd.image, cmd.sampling ?? "linear");
|
|
2719
3011
|
const src = cmd.source;
|
|
2720
3012
|
let u0 = 0, v0 = 0, u1 = 1, v1 = 1;
|
|
2721
3013
|
if (src) {
|
|
@@ -2745,11 +3037,12 @@ function drawImage(ctx, cmd) {
|
|
|
2745
3037
|
v0,
|
|
2746
3038
|
u1,
|
|
2747
3039
|
v1,
|
|
2748
|
-
// Group alpha rides
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
3040
|
+
// Group alpha rides here with the opacity, in the tint's fourth channel.
|
|
3041
|
+
(cmd.opacity ?? 1) * tint[3],
|
|
3042
|
+
slot,
|
|
3043
|
+
tint[0],
|
|
3044
|
+
tint[1],
|
|
3045
|
+
tint[2]
|
|
2753
3046
|
)) retryUncut(ctx, cmd, void 0, drawImage);
|
|
2754
3047
|
}
|
|
2755
3048
|
function drawSprites(ctx, cmd) {
|
|
@@ -2757,18 +3050,24 @@ function drawSprites(ctx, cmd) {
|
|
|
2757
3050
|
const count = Math.floor(data.length / SPRITE_STRIDE);
|
|
2758
3051
|
if (count === 0) return;
|
|
2759
3052
|
ctx.imageCache.upload(cmd.image, cmd.image);
|
|
3053
|
+
if (ctx.imageCache.lacksMipmaps(cmd.image)) {
|
|
3054
|
+
const limit = minifiedLimits(ctx);
|
|
3055
|
+
for (let i = 0; i < count * SPRITE_STRIDE; i += SPRITE_STRIDE) {
|
|
3056
|
+
if (!isMinified(limit, data[i + 6], data[i + 7], data[i + 2], data[i + 3])) continue;
|
|
3057
|
+
ctx.imageCache.buildMipmaps(cmd.image);
|
|
3058
|
+
break;
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
2760
3061
|
const sampling = cmd.sampling ?? "linear";
|
|
2761
|
-
let {
|
|
3062
|
+
let { slot, tint } = stageImage(ctx, cmd.image, sampling);
|
|
2762
3063
|
const batch = ctx.drawBatch;
|
|
2763
3064
|
const m = ctx.state.transform;
|
|
2764
|
-
let groupAlpha = staged.foldsAlpha ? ctx.state.alpha : 1;
|
|
2765
3065
|
const tw = cmd.image.width;
|
|
2766
3066
|
const th = cmd.image.height;
|
|
2767
3067
|
for (let s = 0, i = 0; s < count; s++, i += SPRITE_STRIDE) {
|
|
2768
3068
|
if (batch.wouldOverflow(4)) {
|
|
2769
3069
|
flushBatch(ctx);
|
|
2770
|
-
({
|
|
2771
|
-
groupAlpha = staged.foldsAlpha ? ctx.state.alpha : 1;
|
|
3070
|
+
({ slot, tint } = stageImage(ctx, cmd.image, sampling));
|
|
2772
3071
|
}
|
|
2773
3072
|
const sx = data[i + 4], sy = data[i + 5], sw = data[i + 6], sh = data[i + 7];
|
|
2774
3073
|
let cut;
|
|
@@ -2782,17 +3081,18 @@ function drawSprites(ctx, cmd) {
|
|
|
2782
3081
|
sy / th,
|
|
2783
3082
|
(sx + sw) / tw,
|
|
2784
3083
|
(sy + sh) / th,
|
|
2785
|
-
data[i + 8] *
|
|
2786
|
-
slot
|
|
3084
|
+
data[i + 8] * tint[3],
|
|
3085
|
+
slot,
|
|
3086
|
+
tint[0],
|
|
3087
|
+
tint[1],
|
|
3088
|
+
tint[2]
|
|
2787
3089
|
)) {
|
|
2788
3090
|
cut = enterUncut(ctx);
|
|
2789
|
-
({
|
|
2790
|
-
groupAlpha = staged.foldsAlpha ? ctx.state.alpha : 1;
|
|
3091
|
+
({ slot, tint } = stageImage(ctx, cmd.image, sampling));
|
|
2791
3092
|
}
|
|
2792
3093
|
if (cut !== void 0) {
|
|
2793
3094
|
leaveUncut(ctx, cut);
|
|
2794
|
-
({
|
|
2795
|
-
groupAlpha = staged.foldsAlpha ? ctx.state.alpha : 1;
|
|
3095
|
+
({ slot, tint } = stageImage(ctx, cmd.image, sampling));
|
|
2796
3096
|
}
|
|
2797
3097
|
}
|
|
2798
3098
|
}
|
|
@@ -2842,7 +3142,7 @@ function drawGroupWithEffects(ctx, cmd, targets, width, height) {
|
|
|
2842
3142
|
flushBatch(ctx);
|
|
2843
3143
|
gl.disable(gl.BLEND);
|
|
2844
3144
|
gl.disable(gl.STENCIL_TEST);
|
|
2845
|
-
for (const effect of cmd.effects) {
|
|
3145
|
+
for (const effect of cmd.effects ?? []) {
|
|
2846
3146
|
const program = ctx.ensureProgram?.(effect.program.id) ?? null;
|
|
2847
3147
|
if (!program) {
|
|
2848
3148
|
warnOnceUniform(effect.program.id, "(program)");
|
|
@@ -2875,15 +3175,21 @@ function drawGroupWithEffects(ctx, cmd, targets, width, height) {
|
|
|
2875
3175
|
const composite = ctx.ensureProgram?.(compositeProgramId()) ?? null;
|
|
2876
3176
|
if (composite) {
|
|
2877
3177
|
applyClipTest(ctx);
|
|
3178
|
+
const ownBlend = isOwnComposite(cmd.composite);
|
|
3179
|
+
if (ownBlend) applyComposite(gl, cmd.composite);
|
|
2878
3180
|
drawFullscreenQuad(ctx, composite, front.texture, width, height, void 0, {
|
|
2879
3181
|
alpha: composited.alpha,
|
|
2880
3182
|
colorMatrix: composited.colorMatrix
|
|
2881
3183
|
});
|
|
3184
|
+
if (ownBlend) applyComposite(gl, "source-over");
|
|
2882
3185
|
}
|
|
2883
3186
|
targets.release(front);
|
|
2884
3187
|
}
|
|
3188
|
+
function ownsSurface(cmd) {
|
|
3189
|
+
return cmd.isolate === true || isOwnComposite(cmd.composite) || cmd.effects !== void 0 && cmd.effects.length > 0;
|
|
3190
|
+
}
|
|
2885
3191
|
function drawGroup(ctx, cmd) {
|
|
2886
|
-
if (cmd
|
|
3192
|
+
if (ownsSurface(cmd) && ctx.effectTargets && ctx.deviceWidth && ctx.deviceHeight) {
|
|
2887
3193
|
if (cmd.clip) {
|
|
2888
3194
|
ctx.state.push({ transform: cmd.transform });
|
|
2889
3195
|
try {
|
|
@@ -2985,8 +3291,7 @@ var EffectTargets = class {
|
|
|
2985
3291
|
}
|
|
2986
3292
|
/**
|
|
2987
3293
|
* Borrow a target sized `width` × `height` device pixels. A size change
|
|
2988
|
-
* drops every pooled buffer
|
|
2989
|
-
* cannot be resized under its holder, and a resize is not a per-frame event.
|
|
3294
|
+
* drops every pooled buffer, borrowed ones included.
|
|
2990
3295
|
*/
|
|
2991
3296
|
acquire(width, height) {
|
|
2992
3297
|
if (width !== this.width || height !== this.height) {
|
|
@@ -3289,9 +3594,12 @@ var GLYPH_PAINT_SOURCES = {
|
|
|
3289
3594
|
var WeaselRenderer = class {
|
|
3290
3595
|
gl;
|
|
3291
3596
|
stats = { drawCalls: 0, ms: 0, spans: [] };
|
|
3597
|
+
/** Built on the first frame that draws with each, so a one-shot render of
|
|
3598
|
+
* plain shapes builds the batch program alone. */
|
|
3292
3599
|
pathFill;
|
|
3293
3600
|
pathFillVColor;
|
|
3294
3601
|
batchFill;
|
|
3602
|
+
batchFillMatrices;
|
|
3295
3603
|
gradFill;
|
|
3296
3604
|
patternFill;
|
|
3297
3605
|
/** Compiled on first use: only atlas text under a texture paint needs one. */
|
|
@@ -3362,10 +3670,6 @@ var WeaselRenderer = class {
|
|
|
3362
3670
|
if (this.canvas) {
|
|
3363
3671
|
this.canvas.width = Math.round(opts.width * opts.dpr);
|
|
3364
3672
|
this.canvas.height = Math.round(opts.height * opts.dpr);
|
|
3365
|
-
if (this.canvas.style) {
|
|
3366
|
-
this.canvas.style.width = `${opts.width}px`;
|
|
3367
|
-
this.canvas.style.height = `${opts.height}px`;
|
|
3368
|
-
}
|
|
3369
3673
|
}
|
|
3370
3674
|
const lossTarget = this.canvas ?? this.gl.canvas ?? null;
|
|
3371
3675
|
if (lossTarget && typeof lossTarget.addEventListener === "function") {
|
|
@@ -3375,21 +3679,12 @@ var WeaselRenderer = class {
|
|
|
3375
3679
|
}
|
|
3376
3680
|
this.applyGlState();
|
|
3377
3681
|
this.applyTarget();
|
|
3378
|
-
this.pathFill = new
|
|
3379
|
-
this.
|
|
3380
|
-
this.
|
|
3381
|
-
this.
|
|
3382
|
-
this.
|
|
3383
|
-
this.
|
|
3384
|
-
this.batchFill = new ShaderProgram(this.gl, BATCH_VERT_SRC, BATCH_FRAG_SRC);
|
|
3385
|
-
this.batchFill.lookupUniforms(BATCH_FILL_UNIFORMS);
|
|
3386
|
-
this.batchFill.lookupAttributes(BATCH_FILL_ATTRIBUTES);
|
|
3387
|
-
this.gradFill = new ShaderProgram(this.gl, GRAD_VERT_SRC, GRAD_FRAG_SRC);
|
|
3388
|
-
this.gradFill.lookupUniforms(GRAD_FILL_UNIFORMS);
|
|
3389
|
-
this.gradFill.lookupAttributes(GRAD_FILL_ATTRIBUTES);
|
|
3390
|
-
this.patternFill = new ShaderProgram(this.gl, PATTERN_VERT_SRC, PATTERN_FRAG_SRC);
|
|
3391
|
-
this.patternFill.lookupUniforms(PATTERN_FILL_UNIFORMS);
|
|
3392
|
-
this.patternFill.lookupAttributes(PATTERN_FILL_ATTRIBUTES);
|
|
3682
|
+
this.pathFill = new LazyProgram(this.gl, PATH_FILL_SPEC);
|
|
3683
|
+
this.pathFillVColor = new LazyProgram(this.gl, PATH_FILL_VCOLOR_SPEC);
|
|
3684
|
+
this.gradFill = new LazyProgram(this.gl, GRAD_FILL_SPEC);
|
|
3685
|
+
this.patternFill = new LazyProgram(this.gl, PATTERN_FILL_SPEC);
|
|
3686
|
+
this.batchFillMatrices = new LazyProgram(this.gl, BATCH_FILL_MATRICES_SPEC);
|
|
3687
|
+
this.batchFill = createBatchFill(this.gl);
|
|
3393
3688
|
this.meshCache = new GLMeshCache(this.gl);
|
|
3394
3689
|
this.textureCache = new GLTextureCache(this.gl);
|
|
3395
3690
|
this.imageCache = new GLImageCache(this.gl, this.imageMinification);
|
|
@@ -3398,6 +3693,9 @@ var WeaselRenderer = class {
|
|
|
3398
3693
|
this.drawBatch = new DrawBatch(this.gl, this.batchFill);
|
|
3399
3694
|
this.whiteTexture = createWhiteTexture(this.gl);
|
|
3400
3695
|
}
|
|
3696
|
+
lazyPrograms() {
|
|
3697
|
+
return [this.pathFill, this.pathFillVColor, this.gradFill, this.patternFill, this.batchFillMatrices];
|
|
3698
|
+
}
|
|
3401
3699
|
uploadQuadGeometry() {
|
|
3402
3700
|
const gl = this.gl;
|
|
3403
3701
|
this.quadVbo = gl.createBuffer();
|
|
@@ -3415,12 +3713,11 @@ var WeaselRenderer = class {
|
|
|
3415
3713
|
/**
|
|
3416
3714
|
* Compile a consumer-registered shader program against this renderer's GL context.
|
|
3417
3715
|
*
|
|
3418
|
-
*
|
|
3419
|
-
*
|
|
3716
|
+
* Optional: a program compiles on the first frame that draws with it. Call
|
|
3717
|
+
* this after the module-level `registerProgram()` to pay for the compile
|
|
3718
|
+
* before that frame, or to catch the `ShaderCompileError` it throws.
|
|
3420
3719
|
*
|
|
3421
3720
|
* In dev mode, calling again with the same handle replaces the compiled program.
|
|
3422
|
-
*
|
|
3423
|
-
* @experimental
|
|
3424
3721
|
*/
|
|
3425
3722
|
registerProgram(handle) {
|
|
3426
3723
|
if (this.disposed) return;
|
|
@@ -3499,8 +3796,6 @@ var WeaselRenderer = class {
|
|
|
3499
3796
|
gl.bindVertexArray(null);
|
|
3500
3797
|
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
|
3501
3798
|
}
|
|
3502
|
-
/** Viewport and scissor for this frame. Re-applied inside `render()` because
|
|
3503
|
-
* a co-tenant on the same context moves both between our frames. */
|
|
3504
3799
|
/** This renderer's rect in device pixels, top-left origin. A tile rounds its
|
|
3505
3800
|
* edges rather than its size, so two that share a CSS edge share a device one. */
|
|
3506
3801
|
deviceRect() {
|
|
@@ -3515,6 +3810,8 @@ var WeaselRenderer = class {
|
|
|
3515
3810
|
height: Math.round((oy + this.heightCss) * this.dpr) - y
|
|
3516
3811
|
};
|
|
3517
3812
|
}
|
|
3813
|
+
/** Viewport and scissor for this frame. Re-applied inside `render()` because
|
|
3814
|
+
* a co-tenant on the same context moves both between our frames. */
|
|
3518
3815
|
applyTarget() {
|
|
3519
3816
|
const gl = this.gl;
|
|
3520
3817
|
const { x, y, width, height } = this.deviceRect();
|
|
@@ -3552,21 +3849,8 @@ var WeaselRenderer = class {
|
|
|
3552
3849
|
this.paintScratch.release();
|
|
3553
3850
|
this.paintResources.release();
|
|
3554
3851
|
this.paintResources = createPaintResources();
|
|
3555
|
-
|
|
3556
|
-
this.
|
|
3557
|
-
this.pathFill.lookupAttributes(PATH_FILL_ATTRIBUTES);
|
|
3558
|
-
this.pathFillVColor = new ShaderProgram(this.gl, VCOLOR_VERT_SRC, VCOLOR_FRAG_SRC);
|
|
3559
|
-
this.pathFillVColor.lookupUniforms(PATH_FILL_UNIFORMS);
|
|
3560
|
-
this.pathFillVColor.lookupAttributes(PATH_FILL_VCOLOR_ATTRIBUTES);
|
|
3561
|
-
this.batchFill = new ShaderProgram(this.gl, BATCH_VERT_SRC, BATCH_FRAG_SRC);
|
|
3562
|
-
this.batchFill.lookupUniforms(BATCH_FILL_UNIFORMS);
|
|
3563
|
-
this.batchFill.lookupAttributes(BATCH_FILL_ATTRIBUTES);
|
|
3564
|
-
this.gradFill = new ShaderProgram(this.gl, GRAD_VERT_SRC, GRAD_FRAG_SRC);
|
|
3565
|
-
this.gradFill.lookupUniforms(GRAD_FILL_UNIFORMS);
|
|
3566
|
-
this.gradFill.lookupAttributes(GRAD_FILL_ATTRIBUTES);
|
|
3567
|
-
this.patternFill = new ShaderProgram(this.gl, PATTERN_VERT_SRC, PATTERN_FRAG_SRC);
|
|
3568
|
-
this.patternFill.lookupUniforms(PATTERN_FILL_UNIFORMS);
|
|
3569
|
-
this.patternFill.lookupAttributes(PATTERN_FILL_ATTRIBUTES);
|
|
3852
|
+
for (const lazy of this.lazyPrograms()) lazy.abandon();
|
|
3853
|
+
this.batchFill = createBatchFill(this.gl);
|
|
3570
3854
|
this.glyphPaintPrograms.clear();
|
|
3571
3855
|
this.meshCache = new GLMeshCache(this.gl);
|
|
3572
3856
|
this.textureCache = new GLTextureCache(this.gl);
|
|
@@ -3589,14 +3873,9 @@ var WeaselRenderer = class {
|
|
|
3589
3873
|
}
|
|
3590
3874
|
}
|
|
3591
3875
|
/** Free every GL resource this renderer created and detach its
|
|
3592
|
-
* context-loss listeners. Idempotent.
|
|
3593
|
-
*
|
|
3594
|
-
*
|
|
3595
|
-
* go with them), the shared quad geometry, the batch, the effect targets,
|
|
3596
|
-
* and every cache: atlas and gradient-ramp textures, bitmap and pattern
|
|
3597
|
-
* textures, and meshes — transient, persistent, and those whose Path was
|
|
3598
|
-
* already collected. The context itself is left alone, so a caller-owned
|
|
3599
|
-
* context can host renderer after renderer without accumulating anything.
|
|
3876
|
+
* context-loss listeners. Idempotent. The context itself is left alone, so
|
|
3877
|
+
* a caller-owned context can host renderer after renderer without
|
|
3878
|
+
* accumulating anything.
|
|
3600
3879
|
*
|
|
3601
3880
|
* A disposed renderer ignores further `render()` and `registerProgram()`
|
|
3602
3881
|
* calls. */
|
|
@@ -3610,14 +3889,8 @@ var WeaselRenderer = class {
|
|
|
3610
3889
|
}
|
|
3611
3890
|
this.meshCache.dispose();
|
|
3612
3891
|
this.paintResources.release();
|
|
3613
|
-
for (const
|
|
3614
|
-
|
|
3615
|
-
this.pathFillVColor,
|
|
3616
|
-
this.batchFill,
|
|
3617
|
-
this.gradFill,
|
|
3618
|
-
this.patternFill,
|
|
3619
|
-
...this.glyphPaintPrograms.values()
|
|
3620
|
-
]) {
|
|
3892
|
+
for (const lazy of this.lazyPrograms()) lazy.release();
|
|
3893
|
+
for (const prog of [this.batchFill, ...this.glyphPaintPrograms.values()]) {
|
|
3621
3894
|
gl.deleteProgram(prog.handle);
|
|
3622
3895
|
}
|
|
3623
3896
|
this.glyphPaintPrograms.clear();
|
|
@@ -3653,7 +3926,7 @@ var WeaselRenderer = class {
|
|
|
3653
3926
|
this.stats = { drawCalls: 0, ms: 0, spans: spanStats };
|
|
3654
3927
|
const gl = this.gl;
|
|
3655
3928
|
this.meshCache.drainPendingDeletes();
|
|
3656
|
-
this.imageCache.
|
|
3929
|
+
this.imageCache.beginFrame();
|
|
3657
3930
|
this.textureCache.sweep(textureRegistry);
|
|
3658
3931
|
this.refreshPrograms();
|
|
3659
3932
|
resetBakeBudget(this.bakeBudget);
|
|
@@ -3668,13 +3941,25 @@ var WeaselRenderer = class {
|
|
|
3668
3941
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
|
|
3669
3942
|
}
|
|
3670
3943
|
const device = this.deviceRect();
|
|
3944
|
+
const { pathFill, pathFillVColor, gradFill, patternFill, batchFillMatrices } = this;
|
|
3671
3945
|
const ctx = {
|
|
3672
3946
|
gl,
|
|
3673
|
-
pathFill
|
|
3674
|
-
|
|
3947
|
+
get pathFill() {
|
|
3948
|
+
return pathFill.get();
|
|
3949
|
+
},
|
|
3950
|
+
get pathFillVColor() {
|
|
3951
|
+
return pathFillVColor.get();
|
|
3952
|
+
},
|
|
3675
3953
|
batchFill: this.batchFill,
|
|
3676
|
-
|
|
3677
|
-
|
|
3954
|
+
get batchFillMatrices() {
|
|
3955
|
+
return batchFillMatrices.get();
|
|
3956
|
+
},
|
|
3957
|
+
get gradFill() {
|
|
3958
|
+
return gradFill.get();
|
|
3959
|
+
},
|
|
3960
|
+
get patternFill() {
|
|
3961
|
+
return patternFill.get();
|
|
3962
|
+
},
|
|
3678
3963
|
meshCache: this.meshCache,
|
|
3679
3964
|
textureCache: this.textureCache,
|
|
3680
3965
|
imageCache: this.imageCache,
|
|
@@ -3739,10 +4024,6 @@ var WeaselRenderer = class {
|
|
|
3739
4024
|
if (this.canvas) {
|
|
3740
4025
|
this.canvas.width = Math.round(dims.width * dims.dpr);
|
|
3741
4026
|
this.canvas.height = Math.round(dims.height * dims.dpr);
|
|
3742
|
-
if (this.canvas.style) {
|
|
3743
|
-
this.canvas.style.width = `${dims.width}px`;
|
|
3744
|
-
this.canvas.style.height = `${dims.height}px`;
|
|
3745
|
-
}
|
|
3746
4027
|
}
|
|
3747
4028
|
this.applyTarget();
|
|
3748
4029
|
}
|
|
@@ -3752,11 +4033,11 @@ var WeaselRenderer = class {
|
|
|
3752
4033
|
}
|
|
3753
4034
|
/** @internal */
|
|
3754
4035
|
_pathFill() {
|
|
3755
|
-
return this.pathFill;
|
|
4036
|
+
return this.pathFill.get();
|
|
3756
4037
|
}
|
|
3757
4038
|
/** @internal */
|
|
3758
4039
|
_pathFillVColor() {
|
|
3759
|
-
return this.pathFillVColor;
|
|
4040
|
+
return this.pathFillVColor.get();
|
|
3760
4041
|
}
|
|
3761
4042
|
/** @internal */
|
|
3762
4043
|
_drawBatch() {
|
|
@@ -3767,12 +4048,16 @@ var WeaselRenderer = class {
|
|
|
3767
4048
|
return this.batchFill;
|
|
3768
4049
|
}
|
|
3769
4050
|
/** @internal */
|
|
4051
|
+
_batchFillMatrices() {
|
|
4052
|
+
return this.batchFillMatrices.get();
|
|
4053
|
+
}
|
|
4054
|
+
/** @internal */
|
|
3770
4055
|
_gradFill() {
|
|
3771
|
-
return this.gradFill;
|
|
4056
|
+
return this.gradFill.get();
|
|
3772
4057
|
}
|
|
3773
4058
|
/** @internal */
|
|
3774
4059
|
_patternFill() {
|
|
3775
|
-
return this.patternFill;
|
|
4060
|
+
return this.patternFill.get();
|
|
3776
4061
|
}
|
|
3777
4062
|
/** @internal */
|
|
3778
4063
|
_meshCache() {
|
|
@@ -3867,6 +4152,7 @@ function cullOne(cmd, m, box) {
|
|
|
3867
4152
|
switch (cmd.kind) {
|
|
3868
4153
|
case "group": {
|
|
3869
4154
|
if (cmd.effects && cmd.effects.length > 0) return cmd;
|
|
4155
|
+
if (isOwnComposite(cmd.composite)) return cmd;
|
|
3870
4156
|
const inner = cmd.transform ? mat3.multiply(m, cmd.transform) : m;
|
|
3871
4157
|
const children = cullList(cmd.children, inner, box);
|
|
3872
4158
|
if (children === cmd.children) return cmd;
|
|
@@ -4070,15 +4356,15 @@ function textCommandFromPose(pose, scale = 1) {
|
|
|
4070
4356
|
};
|
|
4071
4357
|
}
|
|
4072
4358
|
function runsInPoseFrame(runs, box) {
|
|
4073
|
-
if (runs.every((r) => isSolid(r.fill) && isSolid(r.stroke?.paint))) return runs;
|
|
4359
|
+
if (runs.every((r) => isSolid(r.fill?.paint) && isSolid(r.stroke?.paint))) return runs;
|
|
4074
4360
|
return runs.map((r) => {
|
|
4075
4361
|
const fill = r.fill && commandFill(r.fill, box);
|
|
4076
|
-
const paint = r.stroke?.paint &&
|
|
4362
|
+
const paint = r.stroke?.paint && commandPaint(r.stroke.paint, box);
|
|
4077
4363
|
return { ...r, fill, stroke: r.stroke && { ...r.stroke, paint: paint || void 0 } };
|
|
4078
4364
|
});
|
|
4079
4365
|
}
|
|
4080
4366
|
function isSolid(paint) {
|
|
4081
|
-
return paint == null || (paint
|
|
4367
|
+
return paint == null || isSolidPaint(paint);
|
|
4082
4368
|
}
|
|
4083
4369
|
|
|
4084
4370
|
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 };
|