@weasel-js/renderer 1.9.4 → 1.10.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-2VOBNWBN.js → chunk-43YL7HZV.js} +3 -3
- package/dist/chunk-43YL7HZV.js.map +1 -0
- package/dist/{chunk-VHQJ56XQ.js → chunk-LFD47OV2.js} +180 -130
- package/dist/chunk-LFD47OV2.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-DYcWftY8.d.ts} +66 -109
- package/dist/index.d.ts +192 -200
- package/dist/index.js +327 -145
- 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 +8 -16
- package/dist/patterns-builtin.js +7 -7
- package/dist/patterns-builtin.js.map +1 -1
- package/dist/test-seams.d.ts +22 -63
- 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, BATCH_VERT_SRC, BATCH_FRAG_SRC, BATCH_FILL_UNIFORMS, BATCH_FILL_ATTRIBUTES, GLImageCache, DrawBatch, getProgramSource, commandFill, cachedMarkerCommands, PAINT_MODE_PLAIN, PAINT_MODE_RADIAL, PAINT_MODE_CONIC, getMesh, quantizeStrokeScale, strokeMesh, axisAlignedClipRect, intersectClipRects, getPaintDrawKind, outlineMesh, outlineStrokeMesh, quantizeEmWidth } from './chunk-
|
|
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, BATCH_VERT_SRC, BATCH_FRAG_SRC, BATCH_FILL_UNIFORMS, BATCH_FILL_ATTRIBUTES, STRAIGHT_RGB_GLSL, GLImageCache, DrawBatch, getProgramSource, commandFill, commandPaint, uploadPremultiplied, cachedMarkerCommands, PAINT_MODE_PLAIN, PAINT_MODE_RADIAL, PAINT_MODE_CONIC, getMesh, quantizeStrokeScale, strokeMesh, axisAlignedClipRect, intersectClipRects, getPaintDrawKind, outlineMesh, outlineStrokeMesh, quantizeEmWidth } from './chunk-LFD47OV2.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-LFD47OV2.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() {
|
|
@@ -320,10 +320,11 @@ uniform float u_opacity;
|
|
|
320
320
|
uniform float u_alpha;
|
|
321
321
|
uniform mat4 u_colorMatrix;
|
|
322
322
|
uniform vec4 u_colorBias;
|
|
323
|
+
${STRAIGHT_RGB_GLSL}
|
|
323
324
|
vec4 shadePaint(vec2 world) {
|
|
324
325
|
vec2 uv = world / max(u_tileSize, vec2(0.0001));
|
|
325
326
|
vec4 texel = texture(u_sampler, uv);
|
|
326
|
-
vec4 mapped = clamp(u_colorMatrix * texel + u_colorBias, 0.0, 1.0);
|
|
327
|
+
vec4 mapped = clamp(u_colorMatrix * vec4(straightRgb(texel), texel.a) + u_colorBias, 0.0, 1.0);
|
|
327
328
|
float a = mapped.a * u_opacity * u_alpha;
|
|
328
329
|
return vec4(mapped.rgb * a, a);
|
|
329
330
|
}
|
|
@@ -510,11 +511,9 @@ var GLMeshCache = class {
|
|
|
510
511
|
return handle;
|
|
511
512
|
}
|
|
512
513
|
/**
|
|
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.
|
|
514
|
+
* Upload a Mesh that the caller knows is single-use. Returns a handle backed
|
|
515
|
+
* by fresh GL resources that the renderer frees at the end of the current
|
|
516
|
+
* frame via `freeTransient()`, so transient meshes never wait on GC.
|
|
518
517
|
*/
|
|
519
518
|
uploadTransient(mesh) {
|
|
520
519
|
const { handle, vbo, ibo } = this.upload(mesh);
|
|
@@ -628,6 +627,9 @@ var GLMeshCache = class {
|
|
|
628
627
|
};
|
|
629
628
|
|
|
630
629
|
// src/cache/GLTextureCache.ts
|
|
630
|
+
function uploadAsHeld(gl, source) {
|
|
631
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
|
|
632
|
+
}
|
|
631
633
|
var GLTextureCache = class {
|
|
632
634
|
constructor(gl) {
|
|
633
635
|
this.gl = gl;
|
|
@@ -644,8 +646,11 @@ var GLTextureCache = class {
|
|
|
644
646
|
/** Uploads the pixels once per id. `wrap` is re-applied whenever it differs
|
|
645
647
|
* from what the id was last uploaded under: the same registered image can
|
|
646
648
|
* be a clamped shader texture in one draw and a repeating pattern tile in
|
|
647
|
-
* the next
|
|
649
|
+
* the next. */
|
|
648
650
|
upload(id, source, wrap = "clamp") {
|
|
651
|
+
return this.ensure(id, source, wrap, uploadAsHeld);
|
|
652
|
+
}
|
|
653
|
+
ensure(id, source, wrap, send) {
|
|
649
654
|
const cached = this.map.get(id);
|
|
650
655
|
if (cached) {
|
|
651
656
|
if (this.wraps.get(id) !== wrap) this.applyWrap(id, cached, wrap);
|
|
@@ -656,7 +661,7 @@ var GLTextureCache = class {
|
|
|
656
661
|
if (!tex) throw new Error(`GLTextureCache: createTexture failed for id="${id}"`);
|
|
657
662
|
const wrapMode = wrap === "repeat" ? gl.REPEAT : gl.CLAMP_TO_EDGE;
|
|
658
663
|
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
659
|
-
|
|
664
|
+
send(gl, source);
|
|
660
665
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
661
666
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
662
667
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapMode);
|
|
@@ -667,17 +672,18 @@ var GLTextureCache = class {
|
|
|
667
672
|
return id;
|
|
668
673
|
}
|
|
669
674
|
/** `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.
|
|
675
|
+
* under the same id: a changed source sends its pixels again. A registered
|
|
676
|
+
* texture is an image, so its texels are premultiplied. */
|
|
671
677
|
uploadRegistered(id, source, wrap = "clamp") {
|
|
672
678
|
const cached = this.map.get(id);
|
|
673
679
|
if (cached && this.registered.get(id) !== source) {
|
|
674
680
|
const gl = this.gl;
|
|
675
681
|
gl.bindTexture(gl.TEXTURE_2D, cached);
|
|
676
|
-
|
|
682
|
+
uploadPremultiplied(gl, source);
|
|
677
683
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
678
684
|
}
|
|
679
685
|
this.registered.set(id, source);
|
|
680
|
-
return this.
|
|
686
|
+
return this.ensure(id, source, wrap, uploadPremultiplied);
|
|
681
687
|
}
|
|
682
688
|
/**
|
|
683
689
|
* Delete the texture of every registry-backed id the registry has dropped.
|
|
@@ -736,6 +742,35 @@ var GLTextureCache = class {
|
|
|
736
742
|
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4);
|
|
737
743
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
738
744
|
}
|
|
745
|
+
/** Create a texture from premultiplied RGBA bytes that are a color: a page
|
|
746
|
+
* of color glyphs. Mipmapped, as a font atlas must never be, because one is
|
|
747
|
+
* drawn well below the size it was baked at. No-op if `id` already exists. */
|
|
748
|
+
uploadRGBA(id, width, height, data) {
|
|
749
|
+
if (this.map.has(id)) return;
|
|
750
|
+
const gl = this.gl;
|
|
751
|
+
const tex = gl.createTexture();
|
|
752
|
+
if (!tex) throw new Error(`GLTextureCache: createTexture failed for id="${id}"`);
|
|
753
|
+
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
754
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
|
|
755
|
+
gl.generateMipmap(gl.TEXTURE_2D);
|
|
756
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
|
757
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
758
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
759
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
760
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
761
|
+
this.map.set(id, tex);
|
|
762
|
+
this.wraps.set(id, "clamp");
|
|
763
|
+
}
|
|
764
|
+
/** Patch a rect of an existing RGBA texture with tightly-packed w×h texels. */
|
|
765
|
+
subImageRGBA(id, x, y, w, h, data) {
|
|
766
|
+
const tex = this.map.get(id);
|
|
767
|
+
if (!tex) throw new Error(`GLTextureCache: texture "${id}" not uploaded`);
|
|
768
|
+
const gl = this.gl;
|
|
769
|
+
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
770
|
+
gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, w, h, gl.RGBA, gl.UNSIGNED_BYTE, data);
|
|
771
|
+
gl.generateMipmap(gl.TEXTURE_2D);
|
|
772
|
+
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
773
|
+
}
|
|
739
774
|
bind(id, unit) {
|
|
740
775
|
const tex = this.map.get(id);
|
|
741
776
|
if (!tex) throw new Error(`GLTextureCache: texture "${id}" not uploaded`);
|
|
@@ -744,10 +779,8 @@ var GLTextureCache = class {
|
|
|
744
779
|
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
745
780
|
}
|
|
746
781
|
/**
|
|
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.
|
|
782
|
+
* Delete every uploaded GL texture and clear the map. The cache is
|
|
783
|
+
* refillable afterward.
|
|
751
784
|
*/
|
|
752
785
|
free() {
|
|
753
786
|
const gl = this.gl;
|
|
@@ -858,10 +891,8 @@ var GradientRampAtlas = class {
|
|
|
858
891
|
this.cacheHits = 0;
|
|
859
892
|
}
|
|
860
893
|
/**
|
|
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).
|
|
894
|
+
* Delete the atlas texture and forget every row in it. The CPU mirror goes
|
|
895
|
+
* with it, so the atlas is refillable afterward. Stats are left as-is.
|
|
865
896
|
*/
|
|
866
897
|
free() {
|
|
867
898
|
if (this.texture) this.gl.deleteTexture(this.texture);
|
|
@@ -999,16 +1030,13 @@ var GroupState = class {
|
|
|
999
1030
|
this.colorMatrixStack.push(nextCM);
|
|
1000
1031
|
}
|
|
1001
1032
|
/**
|
|
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.
|
|
1033
|
+
* Push a frame whose children paint into a surface of their own. The
|
|
1034
|
+
* transform still accumulates. Alpha and color do not: applying them on the
|
|
1035
|
+
* way in would fade the pixels an effect is about to read, then fade them
|
|
1036
|
+
* again on the way out.
|
|
1008
1037
|
*
|
|
1009
1038
|
* Returns the pair the caller must apply at composite time — what `push`
|
|
1010
|
-
* would have left on the stack
|
|
1011
|
-
* in the dispatcher.
|
|
1039
|
+
* would have left on the stack.
|
|
1012
1040
|
*/
|
|
1013
1041
|
pushIsolated(frame) {
|
|
1014
1042
|
this.push(frame);
|
|
@@ -1085,6 +1113,49 @@ function compositeProgramId() {
|
|
|
1085
1113
|
return COMPOSITE_PROGRAM_ID;
|
|
1086
1114
|
}
|
|
1087
1115
|
|
|
1116
|
+
// src/effects/compositeOp.ts
|
|
1117
|
+
var OPERATORS = {
|
|
1118
|
+
"source-over": { source: "one", destination: "one-minus-source-alpha" },
|
|
1119
|
+
"source-in": { source: "destination-alpha", destination: "zero" },
|
|
1120
|
+
"source-out": { source: "one-minus-destination-alpha", destination: "zero" },
|
|
1121
|
+
"source-atop": { source: "destination-alpha", destination: "one-minus-source-alpha" },
|
|
1122
|
+
"destination-over": { source: "one-minus-destination-alpha", destination: "one" },
|
|
1123
|
+
"destination-in": { source: "zero", destination: "source-alpha" },
|
|
1124
|
+
"destination-out": { source: "zero", destination: "one-minus-source-alpha" },
|
|
1125
|
+
"destination-atop": { source: "one-minus-destination-alpha", destination: "source-alpha" },
|
|
1126
|
+
xor: { source: "one-minus-destination-alpha", destination: "one-minus-source-alpha" },
|
|
1127
|
+
lighter: { source: "one", destination: "one" },
|
|
1128
|
+
copy: { source: "one", destination: "zero" }
|
|
1129
|
+
};
|
|
1130
|
+
function blendFactorsOf(composite) {
|
|
1131
|
+
return typeof composite === "string" ? OPERATORS[composite] : composite;
|
|
1132
|
+
}
|
|
1133
|
+
function isOwnComposite(composite) {
|
|
1134
|
+
if (composite === void 0) return false;
|
|
1135
|
+
const { source, destination } = blendFactorsOf(composite);
|
|
1136
|
+
return source !== "one" || destination !== "one-minus-source-alpha";
|
|
1137
|
+
}
|
|
1138
|
+
function glFactor(gl, factor) {
|
|
1139
|
+
switch (factor) {
|
|
1140
|
+
case "zero":
|
|
1141
|
+
return gl.ZERO;
|
|
1142
|
+
case "one":
|
|
1143
|
+
return gl.ONE;
|
|
1144
|
+
case "source-alpha":
|
|
1145
|
+
return gl.SRC_ALPHA;
|
|
1146
|
+
case "one-minus-source-alpha":
|
|
1147
|
+
return gl.ONE_MINUS_SRC_ALPHA;
|
|
1148
|
+
case "destination-alpha":
|
|
1149
|
+
return gl.DST_ALPHA;
|
|
1150
|
+
case "one-minus-destination-alpha":
|
|
1151
|
+
return gl.ONE_MINUS_DST_ALPHA;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
function applyComposite(gl, composite) {
|
|
1155
|
+
const { source, destination } = blendFactorsOf(composite);
|
|
1156
|
+
gl.blendFunc(glFactor(gl, source), glFactor(gl, destination));
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1088
1159
|
// src/draw/drawTriangles.ts
|
|
1089
1160
|
function drawTriangles(ctx, count, type) {
|
|
1090
1161
|
ctx.gl.drawElements(ctx.gl.TRIANGLES, count, type, 0);
|
|
@@ -1128,14 +1199,17 @@ function endParityMask(ctx) {
|
|
|
1128
1199
|
|
|
1129
1200
|
// src/draw/customShader.ts
|
|
1130
1201
|
var warnedUniforms = /* @__PURE__ */ new Set();
|
|
1131
|
-
function
|
|
1132
|
-
const key = `${programId}:${name}`;
|
|
1202
|
+
function warnOnce(key, message) {
|
|
1133
1203
|
if (warnedUniforms.has(key)) return;
|
|
1134
1204
|
warnedUniforms.add(key);
|
|
1135
1205
|
const isDev = typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true;
|
|
1136
|
-
if (isDev)
|
|
1137
|
-
|
|
1138
|
-
|
|
1206
|
+
if (isDev) console.warn(message);
|
|
1207
|
+
}
|
|
1208
|
+
function warnOnceUniform(programId, name) {
|
|
1209
|
+
warnOnce(
|
|
1210
|
+
`${programId}:${name}`,
|
|
1211
|
+
`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).`
|
|
1212
|
+
);
|
|
1139
1213
|
}
|
|
1140
1214
|
function setUniform(gl, loc, value, textureCache, nextTexUnit) {
|
|
1141
1215
|
if (value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Float32Array) && "id" in value && typeof value.id === "string") {
|
|
@@ -1259,10 +1333,11 @@ function setUniforms(gl, program, programId, uniforms, textureCache, nextTexUnit
|
|
|
1259
1333
|
}
|
|
1260
1334
|
function drawShader(ctx, cmd) {
|
|
1261
1335
|
const { gl, programRegistry, quadVbo, quadIbo, textureCache } = ctx;
|
|
1262
|
-
const program = programRegistry.get(cmd.program.id);
|
|
1336
|
+
const program = ctx.ensureProgram?.(cmd.program.id) ?? programRegistry.get(cmd.program.id);
|
|
1263
1337
|
if (!program) {
|
|
1264
|
-
|
|
1265
|
-
|
|
1338
|
+
warnOnce(
|
|
1339
|
+
`${cmd.program.id}:(program)`,
|
|
1340
|
+
`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
1341
|
);
|
|
1267
1342
|
return;
|
|
1268
1343
|
}
|
|
@@ -1693,9 +1768,41 @@ function isPxMarker(ref) {
|
|
|
1693
1768
|
function resolvedMarker(ref, scale) {
|
|
1694
1769
|
return { ...ref, size: resolveMarkerSize(ref, 0, quantizeStrokeScale(ref.size.px, scale)) };
|
|
1695
1770
|
}
|
|
1771
|
+
|
|
1772
|
+
// src/draw/foldColor.ts
|
|
1773
|
+
function isIdentityColorMatrix(cm) {
|
|
1774
|
+
return cm === IDENTITY_COLOR_MATRIX || sameValues(IDENTITY_COLOR_MATRIX, cm);
|
|
1775
|
+
}
|
|
1776
|
+
var colorFolding = { enabled: true };
|
|
1777
|
+
var clamp01 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
1778
|
+
function foldColor(ctx, staged, r, g, b, a) {
|
|
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
|
+
];
|
|
1789
|
+
}
|
|
1790
|
+
var NO_TINT = [1, 1, 1, 1];
|
|
1791
|
+
var DIAGONAL = [0, 6, 12, 18];
|
|
1792
|
+
function textureTint(cm) {
|
|
1793
|
+
if (isIdentityColorMatrix(cm)) return NO_TINT;
|
|
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;
|
|
1797
|
+
}
|
|
1798
|
+
return [cm[0], cm[6], cm[12], cm[18]];
|
|
1799
|
+
}
|
|
1800
|
+
function stagedTint(staged, tint) {
|
|
1801
|
+
return staged.foldsAlpha && tint !== null ? tint : NO_TINT;
|
|
1802
|
+
}
|
|
1696
1803
|
function batchPaint(ctx, fill, hasVColors) {
|
|
1697
1804
|
if (hasVColors) return void 0;
|
|
1698
|
-
const kind = fill.
|
|
1805
|
+
const kind = fill.kind ?? "solid";
|
|
1699
1806
|
if (kind === "solid") {
|
|
1700
1807
|
const solid = fill;
|
|
1701
1808
|
return { kind: "solid", color: solid.color, opacity: solid.opacity };
|
|
@@ -1706,8 +1813,8 @@ function batchPaint(ctx, fill, hasVColors) {
|
|
|
1706
1813
|
const grad = fill;
|
|
1707
1814
|
const g = gradientMap(ctx, grad.units);
|
|
1708
1815
|
if (!g) return null;
|
|
1709
|
-
if (grad.
|
|
1710
|
-
if (grad.
|
|
1816
|
+
if (grad.kind === "linear-gradient") return ramp(grad, PAINT_MODE_PLAIN, linearRampUV(g, grad));
|
|
1817
|
+
if (grad.kind === "radial-gradient") return ramp(grad, PAINT_MODE_RADIAL, radialRampUV(g, grad));
|
|
1711
1818
|
return ramp(grad, PAINT_MODE_CONIC, conicRampUV(g, grad));
|
|
1712
1819
|
}
|
|
1713
1820
|
function ramp(fill, mode, uv) {
|
|
@@ -1780,16 +1887,14 @@ function spaceToDevice(ctx, units) {
|
|
|
1780
1887
|
}
|
|
1781
1888
|
|
|
1782
1889
|
// src/draw/batch.ts
|
|
1783
|
-
function
|
|
1784
|
-
return cm === IDENTITY_COLOR_MATRIX || sameValues(IDENTITY_COLOR_MATRIX, cm);
|
|
1785
|
-
}
|
|
1786
|
-
function stagedStateIsLive(ctx, staged, image, sampling, synthBold = 0) {
|
|
1890
|
+
function stagedStateIsLive(ctx, staged, image, sampling, synthBold = 0, folds = false) {
|
|
1787
1891
|
if (staged.clipDepth !== (ctx.clipStack.length === 0 ? ctx.clipDepth : runClipDepth(ctx))) return false;
|
|
1788
1892
|
if (staged.synthBold !== synthBold) return false;
|
|
1789
1893
|
if (!staged.foldsAlpha && staged.alpha !== ctx.state.alpha) return false;
|
|
1790
1894
|
if (image !== void 0 && slotForBitmap(staged, image, sampling) < 0) return false;
|
|
1791
1895
|
const colorMatrix = ctx.state.colorMatrix;
|
|
1792
|
-
|
|
1896
|
+
if (staged.colorMatrix === colorMatrix || sameValues(staged.colorMatrix, colorMatrix)) return true;
|
|
1897
|
+
return folds && staged.foldsAlpha;
|
|
1793
1898
|
}
|
|
1794
1899
|
function slotForBitmap(staged, image, sampling) {
|
|
1795
1900
|
for (let i = 0; i < staged.textures.length; i++) {
|
|
@@ -1827,13 +1932,15 @@ function canBatchMesh(ctx, mesh) {
|
|
|
1827
1932
|
return mesh.vertices.length >> 1 <= cap;
|
|
1828
1933
|
}
|
|
1829
1934
|
function stageSolid(ctx, vertices) {
|
|
1830
|
-
if (ctx.batchState !== void 0 && !stagedStateIsLive(ctx, ctx.batchState))
|
|
1935
|
+
if (ctx.batchState !== void 0 && !stagedStateIsLive(ctx, ctx.batchState, void 0, void 0, 0, colorFolding.enabled)) {
|
|
1936
|
+
flushBatch(ctx);
|
|
1937
|
+
}
|
|
1831
1938
|
if (ctx.drawBatch.wouldOverflow(vertices)) flushBatch(ctx);
|
|
1832
|
-
return openRun(ctx);
|
|
1939
|
+
return openRun(ctx, colorFolding.enabled);
|
|
1833
1940
|
}
|
|
1834
|
-
function openRun(ctx) {
|
|
1941
|
+
function openRun(ctx, folds = false) {
|
|
1835
1942
|
if (ctx.batchState === void 0) {
|
|
1836
|
-
const colorMatrix = ctx.state.colorMatrix;
|
|
1943
|
+
const colorMatrix = folds ? IDENTITY_COLOR_MATRIX : ctx.state.colorMatrix;
|
|
1837
1944
|
const foldsAlpha = isIdentityColorMatrix(colorMatrix);
|
|
1838
1945
|
ctx.batchState = {
|
|
1839
1946
|
alpha: foldsAlpha ? 1 : ctx.state.alpha,
|
|
@@ -1847,24 +1954,25 @@ function openRun(ctx) {
|
|
|
1847
1954
|
return ctx.batchState;
|
|
1848
1955
|
}
|
|
1849
1956
|
function stageImage(ctx, image, sampling) {
|
|
1850
|
-
|
|
1957
|
+
const tint = textureTint(ctx.state.colorMatrix) ;
|
|
1958
|
+
if (ctx.batchState !== void 0 && !stagedStateIsLive(ctx, ctx.batchState, image, sampling, 0, tint !== null)) {
|
|
1851
1959
|
flushBatch(ctx);
|
|
1852
1960
|
}
|
|
1853
1961
|
if (ctx.drawBatch.wouldOverflow(4)) flushBatch(ctx);
|
|
1854
|
-
const staged = openRun(ctx);
|
|
1962
|
+
const staged = openRun(ctx, tint !== null);
|
|
1855
1963
|
let slot = slotForBitmap(staged, image, sampling);
|
|
1856
1964
|
if (slot > staged.textures.length) {
|
|
1857
1965
|
staged.textures.push({ kind: "bitmap", image, sampling });
|
|
1858
1966
|
slot = staged.textures.length;
|
|
1859
1967
|
}
|
|
1860
|
-
return { staged, slot };
|
|
1968
|
+
return { staged, slot, tint: stagedTint(staged, tint) };
|
|
1861
1969
|
}
|
|
1862
1970
|
function stageGlyphs(ctx, atlasId, fieldScale, synthBold) {
|
|
1863
|
-
if (ctx.batchState !== void 0 && (!stagedStateIsLive(ctx, ctx.batchState, void 0, void 0, synthBold) || slotForAtlas(ctx.batchState, atlasId) < 0)) {
|
|
1971
|
+
if (ctx.batchState !== void 0 && (!stagedStateIsLive(ctx, ctx.batchState, void 0, void 0, synthBold, colorFolding.enabled) || slotForAtlas(ctx.batchState, atlasId) < 0)) {
|
|
1864
1972
|
flushBatch(ctx);
|
|
1865
1973
|
}
|
|
1866
1974
|
if (ctx.drawBatch.wouldOverflow(4)) flushBatch(ctx);
|
|
1867
|
-
const staged = openRun(ctx);
|
|
1975
|
+
const staged = openRun(ctx, colorFolding.enabled);
|
|
1868
1976
|
staged.synthBold = synthBold;
|
|
1869
1977
|
let slot = slotForAtlas(staged, atlasId);
|
|
1870
1978
|
if (slot > staged.textures.length) {
|
|
@@ -1892,7 +2000,7 @@ function stagedAlpha(ctx, staged, opacity) {
|
|
|
1892
2000
|
}
|
|
1893
2001
|
function stagedColor(ctx, staged, paint) {
|
|
1894
2002
|
const [r, g, b, a] = resolveColor(paint.color);
|
|
1895
|
-
return
|
|
2003
|
+
return foldColor(ctx, staged, r, g, b, a * (paint.opacity ?? 1));
|
|
1896
2004
|
}
|
|
1897
2005
|
function pushRect(ctx, rect, fill) {
|
|
1898
2006
|
const staged = stageSolid(ctx, 4);
|
|
@@ -1960,6 +2068,20 @@ function tryStageFill(ctx, mesh, paint) {
|
|
|
1960
2068
|
flushForOwnDraw(ctx);
|
|
1961
2069
|
return false;
|
|
1962
2070
|
}
|
|
2071
|
+
var NO_FIELD = [0, 0];
|
|
2072
|
+
function stageColorGlyphs(ctx, atlasId) {
|
|
2073
|
+
if (ctx.batchState !== void 0 && (!stagedStateIsLive(ctx, ctx.batchState) || slotForAtlas(ctx.batchState, atlasId) < 0)) {
|
|
2074
|
+
flushBatch(ctx);
|
|
2075
|
+
}
|
|
2076
|
+
if (ctx.drawBatch.wouldOverflow(4)) flushBatch(ctx);
|
|
2077
|
+
const staged = openRun(ctx);
|
|
2078
|
+
let slot = slotForAtlas(staged, atlasId);
|
|
2079
|
+
if (slot > staged.textures.length) {
|
|
2080
|
+
staged.textures.push({ kind: "atlas", id: atlasId, fieldScale: NO_FIELD });
|
|
2081
|
+
slot = staged.textures.length;
|
|
2082
|
+
}
|
|
2083
|
+
return { staged, slot };
|
|
2084
|
+
}
|
|
1963
2085
|
function patternTileSpace(spaceInverse, origin, transform) {
|
|
1964
2086
|
const [a, b, c, d] = transform ?? [1, 0, 0, 1];
|
|
1965
2087
|
const placement = mat3.fromAffine([a, b, c, d, origin.x, origin.y]);
|
|
@@ -2004,7 +2126,7 @@ function unownedPaintResources(gl) {
|
|
|
2004
2126
|
return made;
|
|
2005
2127
|
}
|
|
2006
2128
|
function bindPattern(ctx, prog, fill, model) {
|
|
2007
|
-
const tex = fill.
|
|
2129
|
+
const tex = fill.texture;
|
|
2008
2130
|
const entry = getTexture(tex.id);
|
|
2009
2131
|
if (!entry) {
|
|
2010
2132
|
const isDev = typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : true;
|
|
@@ -2048,7 +2170,7 @@ function bindGradient(ctx, prog, fill, model) {
|
|
|
2048
2170
|
gl.uniform1f(prog.uniform("u_rampV"), ctx.gradRamps.rowV(row));
|
|
2049
2171
|
setAlphaUniform(ctx, prog, ctx.state.alpha);
|
|
2050
2172
|
gl.uniform1f(prog.uniform("u_opacity"), fill.opacity ?? 1);
|
|
2051
|
-
if (fill.
|
|
2173
|
+
if (fill.kind === "linear-gradient") {
|
|
2052
2174
|
gl.uniform1i(prog.uniform("u_gradKind"), 0);
|
|
2053
2175
|
const dx = fill.to.x - fill.from.x;
|
|
2054
2176
|
const dy = fill.to.y - fill.from.y;
|
|
@@ -2058,7 +2180,7 @@ function bindGradient(ctx, prog, fill, model) {
|
|
|
2058
2180
|
gl.uniform1f(prog.uniform("u_gradLen"), len);
|
|
2059
2181
|
gl.uniform1f(prog.uniform("u_gradRadius"), 0);
|
|
2060
2182
|
gl.uniform1f(prog.uniform("u_gradAngle"), 0);
|
|
2061
|
-
} else if (fill.
|
|
2183
|
+
} else if (fill.kind === "radial-gradient") {
|
|
2062
2184
|
gl.uniform1i(prog.uniform("u_gradKind"), 1);
|
|
2063
2185
|
gl.uniform2f(prog.uniform("u_gradP0"), fill.center.x, fill.center.y);
|
|
2064
2186
|
gl.uniform2f(prog.uniform("u_gradDir"), 0, 0);
|
|
@@ -2078,12 +2200,11 @@ function bindGradient(ctx, prog, fill, model) {
|
|
|
2078
2200
|
|
|
2079
2201
|
// src/draw/pathPaint.ts
|
|
2080
2202
|
function drawPath(ctx, cmd) {
|
|
2081
|
-
if (cmd.fill) drawPathFill(ctx, cmd);
|
|
2203
|
+
if (cmd.fill?.paint) drawPathFill(ctx, cmd, cmd.fill.paint);
|
|
2082
2204
|
if (cmd.stroke?.paint) drawPathStroke(ctx, cmd);
|
|
2083
2205
|
}
|
|
2084
|
-
function drawPathFill(ctx, cmd) {
|
|
2085
|
-
const
|
|
2086
|
-
const isSolid2 = fill.fill === void 0 || fill.fill === "solid";
|
|
2206
|
+
function drawPathFill(ctx, cmd, fill) {
|
|
2207
|
+
const isSolid2 = fill.kind === void 0 || fill.kind === "solid";
|
|
2087
2208
|
const hasVColors = !!(cmd.vertexColors && cmd.vertexColors.length > 0);
|
|
2088
2209
|
const solid = fill;
|
|
2089
2210
|
const paint = batchPaint(ctx, fill, hasVColors);
|
|
@@ -2161,9 +2282,9 @@ function drawPathFillVColor(ctx, cmd, fill, handle) {
|
|
|
2161
2282
|
gl.bindVertexArray(null);
|
|
2162
2283
|
}
|
|
2163
2284
|
function bindPathFillByKind(ctx, fill) {
|
|
2164
|
-
const kind = fill.
|
|
2285
|
+
const kind = fill.kind ?? "solid";
|
|
2165
2286
|
if (kind === "solid") return bindPathFillSolid(ctx, fill);
|
|
2166
|
-
if (kind === "
|
|
2287
|
+
if (kind === "texture") return bindPathFillPattern(ctx, fill);
|
|
2167
2288
|
if (kind === "linear-gradient" || kind === "radial-gradient" || kind === "conic-gradient") {
|
|
2168
2289
|
return bindPathFillGradient(ctx, fill);
|
|
2169
2290
|
}
|
|
@@ -2224,7 +2345,7 @@ function drawPathStroke(ctx, rawCmd) {
|
|
|
2224
2345
|
function drawPathStrokeUnclipped(ctx, cmd, spaces) {
|
|
2225
2346
|
const stroke = cmd.stroke;
|
|
2226
2347
|
const paint = stroke.paint;
|
|
2227
|
-
const isSolid2 = paint.
|
|
2348
|
+
const isSolid2 = paint.kind === void 0 || paint.kind === "solid";
|
|
2228
2349
|
const solid = paint;
|
|
2229
2350
|
const mesh = strokeMesh(cmd.path, stroke, ctx.flattenTolerance, spaces);
|
|
2230
2351
|
if (mesh.indices.length === 0) return;
|
|
@@ -2273,7 +2394,7 @@ function doubledVertexWidths(widths) {
|
|
|
2273
2394
|
function drawPathStrokeStenciled(ctx, cmd, align, spaces) {
|
|
2274
2395
|
const stroke = cmd.stroke;
|
|
2275
2396
|
const paint = stroke.paint;
|
|
2276
|
-
const isSolid2 = paint.
|
|
2397
|
+
const isSolid2 = paint.kind === void 0 || paint.kind === "solid";
|
|
2277
2398
|
const solid = paint;
|
|
2278
2399
|
const widerStroke = {
|
|
2279
2400
|
...stroke,
|
|
@@ -2333,10 +2454,9 @@ function outlineGroupMesh(group, dx, dy) {
|
|
|
2333
2454
|
}
|
|
2334
2455
|
function outlineGroupStrokeMesh(group, dx, dy, transform) {
|
|
2335
2456
|
const stroke = group.stroke;
|
|
2336
|
-
if (!stroke) return null;
|
|
2457
|
+
if (!strokeHasInk(stroke)) return null;
|
|
2337
2458
|
const width = stroke.width ?? 1;
|
|
2338
2459
|
if (typeof width === "object") {
|
|
2339
|
-
if (!(width.px > 0)) return null;
|
|
2340
2460
|
const shear = shearOf(group);
|
|
2341
2461
|
const [a, b, c, d] = [transform[0], transform[1], transform[3], transform[4]];
|
|
2342
2462
|
return mergeGlyphMeshes(group, dx, dy, (glyph) => {
|
|
@@ -2355,7 +2475,6 @@ function outlineGroupStrokeMesh(group, dx, dy, transform) {
|
|
|
2355
2475
|
);
|
|
2356
2476
|
});
|
|
2357
2477
|
}
|
|
2358
|
-
if (!(width > 0)) return null;
|
|
2359
2478
|
return mergeGlyphMeshes(group, dx, dy, (glyph) => glyph.scale > 0 ? outlineStrokeMesh(
|
|
2360
2479
|
glyph.key,
|
|
2361
2480
|
glyph.d,
|
|
@@ -2395,6 +2514,48 @@ function mergeGlyphMeshes(group, dx, dy, meshFor) {
|
|
|
2395
2514
|
}
|
|
2396
2515
|
return { vertices, indices };
|
|
2397
2516
|
}
|
|
2517
|
+
function drawColorGlyphs(ctx, group, dx, dy) {
|
|
2518
|
+
const fill = group.fill?.paint;
|
|
2519
|
+
if (fill === void 0 || group.quads.length === 0) return;
|
|
2520
|
+
if (!syncColorPageTexture(ctx.textureCache, group.page)) return;
|
|
2521
|
+
const opacity = isSolidPaint(fill) ? resolveColor(fill.color)[3] : 1;
|
|
2522
|
+
const atlasId = colorPageTextureId(group.page);
|
|
2523
|
+
const batch = ctx.drawBatch;
|
|
2524
|
+
const m = ctx.state.transform;
|
|
2525
|
+
let { staged, slot } = stageColorGlyphs(ctx, atlasId);
|
|
2526
|
+
let alpha = opacity * (staged.foldsAlpha ? ctx.state.alpha : 1);
|
|
2527
|
+
const restage = () => {
|
|
2528
|
+
({ staged, slot } = stageColorGlyphs(ctx, atlasId));
|
|
2529
|
+
alpha = opacity * (staged.foldsAlpha ? ctx.state.alpha : 1);
|
|
2530
|
+
};
|
|
2531
|
+
for (const q of group.quads) {
|
|
2532
|
+
if (batch.wouldOverflow(4)) {
|
|
2533
|
+
flushBatch(ctx);
|
|
2534
|
+
restage();
|
|
2535
|
+
}
|
|
2536
|
+
let cut;
|
|
2537
|
+
while (!batch.pushQuad(
|
|
2538
|
+
q.x0 + dx,
|
|
2539
|
+
q.y0 + dy,
|
|
2540
|
+
q.x1 - q.x0,
|
|
2541
|
+
q.y1 - q.y0,
|
|
2542
|
+
m,
|
|
2543
|
+
q.u0,
|
|
2544
|
+
q.v0,
|
|
2545
|
+
q.u1,
|
|
2546
|
+
q.v1,
|
|
2547
|
+
alpha,
|
|
2548
|
+
slot
|
|
2549
|
+
)) {
|
|
2550
|
+
cut = enterUncut(ctx);
|
|
2551
|
+
restage();
|
|
2552
|
+
}
|
|
2553
|
+
if (cut !== void 0) {
|
|
2554
|
+
leaveUncut(ctx, cut);
|
|
2555
|
+
restage();
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2398
2559
|
|
|
2399
2560
|
// src/draw/scratchPass.ts
|
|
2400
2561
|
var UNIT_QUAD = {
|
|
@@ -2403,7 +2564,7 @@ var UNIT_QUAD = {
|
|
|
2403
2564
|
};
|
|
2404
2565
|
function renderPaintToScratch(ctx, fill, rect) {
|
|
2405
2566
|
const scratch = ctx.paintScratch;
|
|
2406
|
-
const bind = getPaintDrawKind(fill.
|
|
2567
|
+
const bind = getPaintDrawKind(fill.kind)?.bind;
|
|
2407
2568
|
if (!scratch || !bind) return null;
|
|
2408
2569
|
const ratio = ctx.deviceWidth && ctx.widthCss > 0 ? ctx.deviceWidth / ctx.widthCss : 1;
|
|
2409
2570
|
const deviceW = ctx.deviceWidth ?? Math.round(ctx.widthCss);
|
|
@@ -2492,21 +2653,22 @@ function drawText(ctx, cmd) {
|
|
|
2492
2653
|
align,
|
|
2493
2654
|
direction,
|
|
2494
2655
|
tabSize: style.tabSize,
|
|
2495
|
-
justify: cmd.justify
|
|
2496
|
-
outlineMinSize
|
|
2656
|
+
justify: cmd.justify
|
|
2497
2657
|
});
|
|
2498
|
-
|
|
2658
|
+
const { groups, decorations } = cachedPaintRuns(laid, cmd.runs, { outlineMinSize });
|
|
2659
|
+
if (groups.length === 0 && decorations.length === 0) return;
|
|
2499
2660
|
const dx = cmd.x;
|
|
2500
2661
|
const dy = cmd.y + verticalAlignOffset(cmd.verticalAlign, cmd.height, laid.bounds.height);
|
|
2501
|
-
for (const group of
|
|
2662
|
+
for (const group of groups) {
|
|
2502
2663
|
if (group.source === "outline") drawTextOutlineGroup(ctx, group, dx, dy);
|
|
2664
|
+
else if (group.source === "color") drawColorGlyphs(ctx, group, dx, dy);
|
|
2503
2665
|
else drawTextGroup(ctx, group, dx, dy);
|
|
2504
2666
|
}
|
|
2505
|
-
drawTextDecorations(ctx,
|
|
2667
|
+
drawTextDecorations(ctx, decorations, dx, dy);
|
|
2506
2668
|
}
|
|
2507
2669
|
function drawTextOutlineGroup(ctx, group, dx, dy) {
|
|
2508
|
-
const fill = group.fill;
|
|
2509
|
-
if (fill !==
|
|
2670
|
+
const fill = group.fill?.paint;
|
|
2671
|
+
if (fill !== void 0) {
|
|
2510
2672
|
const mesh = outlineGroupMesh(group, dx, dy);
|
|
2511
2673
|
if (mesh) drawOutlineMesh(ctx, fill, mesh);
|
|
2512
2674
|
}
|
|
@@ -2523,11 +2685,13 @@ function drawOutlineMesh(ctx, fill, mesh) {
|
|
|
2523
2685
|
}
|
|
2524
2686
|
function drawTextDecorations(ctx, decorations, dx, dy) {
|
|
2525
2687
|
for (const d of decorations) {
|
|
2526
|
-
|
|
2527
|
-
|
|
2688
|
+
const paint = d.fill.paint;
|
|
2689
|
+
if (paint === void 0) continue;
|
|
2690
|
+
if (glyphPaintKindOf(paint) !== null) {
|
|
2691
|
+
drawOutlineMesh(ctx, paint, ruleMesh(d.x0 + dx, d.y0 + dy, d.x1 + dx, d.y1 + dy));
|
|
2528
2692
|
continue;
|
|
2529
2693
|
}
|
|
2530
|
-
const [r, g, b, a] =
|
|
2694
|
+
const [r, g, b, a] = isSolidPaint(paint) ? resolveColor(paint.color) : [0, 0, 0, 1];
|
|
2531
2695
|
let cut;
|
|
2532
2696
|
for (; ; ) {
|
|
2533
2697
|
const staged = stageSolid(ctx, 4);
|
|
@@ -2537,10 +2701,7 @@ function drawTextDecorations(ctx, decorations, dx, dy) {
|
|
|
2537
2701
|
d.x1 - d.x0,
|
|
2538
2702
|
d.y1 - d.y0,
|
|
2539
2703
|
ctx.state.transform,
|
|
2540
|
-
r,
|
|
2541
|
-
g,
|
|
2542
|
-
b,
|
|
2543
|
-
a * (staged.foldsAlpha ? ctx.state.alpha : 1)
|
|
2704
|
+
...foldColor(ctx, staged, r, g, b, a)
|
|
2544
2705
|
)) break;
|
|
2545
2706
|
cut = enterUncut(ctx);
|
|
2546
2707
|
}
|
|
@@ -2572,21 +2733,23 @@ function drawTextGroup(ctx, group, dx, dy) {
|
|
|
2572
2733
|
if (!fieldScale) return;
|
|
2573
2734
|
const bold = group.synthetic.bold ? SYNTH_BOLD_AMOUNT : 0;
|
|
2574
2735
|
const tanItalic = group.synthetic.italic ? Math.tan(SYNTHETIC_ITALIC_RADIANS) : 0;
|
|
2575
|
-
const
|
|
2736
|
+
const fill = group.fill?.paint;
|
|
2737
|
+
if (fill === void 0) return;
|
|
2738
|
+
const paintKind = glyphPaintKindOf(fill);
|
|
2576
2739
|
if (paintKind !== null) {
|
|
2577
2740
|
drawPaintedGlyphs(ctx, group, dx, dy, paintKind, atlasId, mode, bold, tanItalic, fieldScale);
|
|
2578
2741
|
return;
|
|
2579
2742
|
}
|
|
2580
|
-
const color =
|
|
2743
|
+
const color = isSolidPaint(fill) ? resolveColor(fill.color) : [0, 0, 0, 1];
|
|
2581
2744
|
const batch = ctx.drawBatch;
|
|
2582
2745
|
const m = ctx.state.transform;
|
|
2583
2746
|
let { staged, slot } = stageGlyphs(ctx, atlasId, fieldScale, bold);
|
|
2584
|
-
let
|
|
2747
|
+
let rgba = foldColor(ctx, staged, color[0], color[1], color[2], color[3]);
|
|
2585
2748
|
for (const q of group.quads) {
|
|
2586
2749
|
if (batch.wouldOverflow(4)) {
|
|
2587
2750
|
flushBatch(ctx);
|
|
2588
2751
|
({ staged, slot } = stageGlyphs(ctx, atlasId, fieldScale, bold));
|
|
2589
|
-
|
|
2752
|
+
rgba = foldColor(ctx, staged, color[0], color[1], color[2], color[3]);
|
|
2590
2753
|
}
|
|
2591
2754
|
let cut;
|
|
2592
2755
|
while (!batch.pushGlyph(
|
|
@@ -2601,27 +2764,27 @@ function drawTextGroup(ctx, group, dx, dy) {
|
|
|
2601
2764
|
q.v0,
|
|
2602
2765
|
q.u1,
|
|
2603
2766
|
q.v1,
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2767
|
+
rgba[0],
|
|
2768
|
+
rgba[1],
|
|
2769
|
+
rgba[2],
|
|
2770
|
+
rgba[3],
|
|
2608
2771
|
slot,
|
|
2609
2772
|
mode
|
|
2610
2773
|
)) {
|
|
2611
2774
|
cut = enterUncut(ctx);
|
|
2612
2775
|
({ staged, slot } = stageGlyphs(ctx, atlasId, fieldScale, bold));
|
|
2613
|
-
|
|
2776
|
+
rgba = foldColor(ctx, staged, color[0], color[1], color[2], color[3]);
|
|
2614
2777
|
}
|
|
2615
2778
|
if (cut !== void 0) {
|
|
2616
2779
|
leaveUncut(ctx, cut);
|
|
2617
2780
|
({ staged, slot } = stageGlyphs(ctx, atlasId, fieldScale, bold));
|
|
2618
|
-
|
|
2781
|
+
rgba = foldColor(ctx, staged, color[0], color[1], color[2], color[3]);
|
|
2619
2782
|
}
|
|
2620
2783
|
}
|
|
2621
2784
|
}
|
|
2622
2785
|
function glyphPaintKindOf(fill) {
|
|
2623
|
-
const kind = fill
|
|
2624
|
-
if (kind === "
|
|
2786
|
+
const kind = fill.kind ?? "solid";
|
|
2787
|
+
if (kind === "texture") return "pattern";
|
|
2625
2788
|
if (kind === "linear-gradient" || kind === "radial-gradient" || kind === "conic-gradient") {
|
|
2626
2789
|
return "gradient";
|
|
2627
2790
|
}
|
|
@@ -2632,7 +2795,7 @@ function drawPaintedGlyphs(ctx, group, dx, dy, kind, atlasId, mode, bold, tanIta
|
|
|
2632
2795
|
const prog = ctx.glyphPaintProgram?.(kind);
|
|
2633
2796
|
if (!prog) return;
|
|
2634
2797
|
flushForOwnDraw(ctx);
|
|
2635
|
-
const fill = group.fill;
|
|
2798
|
+
const fill = group.fill.paint;
|
|
2636
2799
|
const m = ctx.state.transform;
|
|
2637
2800
|
let bound;
|
|
2638
2801
|
if (kind === "pattern") {
|
|
@@ -2713,9 +2876,22 @@ function glyphScreenBounds(group, dx, dy, tanItalic, m) {
|
|
|
2713
2876
|
var SPRITE_STRIDE = 9;
|
|
2714
2877
|
|
|
2715
2878
|
// src/draw/image.ts
|
|
2879
|
+
var MINIFIED = 2;
|
|
2880
|
+
function minifiedLimits(ctx) {
|
|
2881
|
+
const m = ctx.state.transform;
|
|
2882
|
+
const dpr = ctx.deviceWidth && ctx.widthCss ? ctx.deviceWidth / ctx.widthCss : 1;
|
|
2883
|
+
const k = (MINIFIED * dpr) ** 2;
|
|
2884
|
+
return [k * (m[0] * m[0] + m[1] * m[1]), k * (m[3] * m[3] + m[4] * m[4])];
|
|
2885
|
+
}
|
|
2886
|
+
var isMinified = (limit, sw, sh, dw, dh) => sw * sw >= limit[0] * dw * dw || sh * sh >= limit[1] * dh * dh;
|
|
2716
2887
|
function drawImage(ctx, cmd) {
|
|
2717
2888
|
ctx.imageCache.upload(cmd.image, cmd.image);
|
|
2718
|
-
|
|
2889
|
+
if (ctx.imageCache.lacksMipmaps(cmd.image)) {
|
|
2890
|
+
const sw = cmd.source?.w ?? cmd.image.width;
|
|
2891
|
+
const sh = cmd.source?.h ?? cmd.image.height;
|
|
2892
|
+
if (isMinified(minifiedLimits(ctx), sw, sh, cmd.w, cmd.h)) ctx.imageCache.buildMipmaps(cmd.image);
|
|
2893
|
+
}
|
|
2894
|
+
const { staged, slot, tint } = stageImage(ctx, cmd.image, cmd.sampling ?? "linear");
|
|
2719
2895
|
const src = cmd.source;
|
|
2720
2896
|
let u0 = 0, v0 = 0, u1 = 1, v1 = 1;
|
|
2721
2897
|
if (src) {
|
|
@@ -2748,8 +2924,11 @@ function drawImage(ctx, cmd) {
|
|
|
2748
2924
|
// Group alpha rides the vertices only where it rode the solid colors too:
|
|
2749
2925
|
// with a real color matrix in the run it is `u_alpha`, and folding it here
|
|
2750
2926
|
// as well would apply it twice.
|
|
2751
|
-
(cmd.opacity ?? 1) * (staged.foldsAlpha ? ctx.state.alpha : 1),
|
|
2752
|
-
slot
|
|
2927
|
+
(cmd.opacity ?? 1) * (staged.foldsAlpha ? ctx.state.alpha : 1) * tint[3],
|
|
2928
|
+
slot,
|
|
2929
|
+
tint[0],
|
|
2930
|
+
tint[1],
|
|
2931
|
+
tint[2]
|
|
2753
2932
|
)) retryUncut(ctx, cmd, void 0, drawImage);
|
|
2754
2933
|
}
|
|
2755
2934
|
function drawSprites(ctx, cmd) {
|
|
@@ -2757,18 +2936,26 @@ function drawSprites(ctx, cmd) {
|
|
|
2757
2936
|
const count = Math.floor(data.length / SPRITE_STRIDE);
|
|
2758
2937
|
if (count === 0) return;
|
|
2759
2938
|
ctx.imageCache.upload(cmd.image, cmd.image);
|
|
2939
|
+
if (ctx.imageCache.lacksMipmaps(cmd.image)) {
|
|
2940
|
+
const limit = minifiedLimits(ctx);
|
|
2941
|
+
for (let i = 0; i < count * SPRITE_STRIDE; i += SPRITE_STRIDE) {
|
|
2942
|
+
if (!isMinified(limit, data[i + 6], data[i + 7], data[i + 2], data[i + 3])) continue;
|
|
2943
|
+
ctx.imageCache.buildMipmaps(cmd.image);
|
|
2944
|
+
break;
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2760
2947
|
const sampling = cmd.sampling ?? "linear";
|
|
2761
|
-
let { staged, slot } = stageImage(ctx, cmd.image, sampling);
|
|
2948
|
+
let { staged, slot, tint } = stageImage(ctx, cmd.image, sampling);
|
|
2762
2949
|
const batch = ctx.drawBatch;
|
|
2763
2950
|
const m = ctx.state.transform;
|
|
2764
|
-
let groupAlpha = staged.foldsAlpha ? ctx.state.alpha : 1;
|
|
2951
|
+
let groupAlpha = (staged.foldsAlpha ? ctx.state.alpha : 1) * tint[3];
|
|
2765
2952
|
const tw = cmd.image.width;
|
|
2766
2953
|
const th = cmd.image.height;
|
|
2767
2954
|
for (let s = 0, i = 0; s < count; s++, i += SPRITE_STRIDE) {
|
|
2768
2955
|
if (batch.wouldOverflow(4)) {
|
|
2769
2956
|
flushBatch(ctx);
|
|
2770
|
-
({ staged, slot } = stageImage(ctx, cmd.image, sampling));
|
|
2771
|
-
groupAlpha = staged.foldsAlpha ? ctx.state.alpha : 1;
|
|
2957
|
+
({ staged, slot, tint } = stageImage(ctx, cmd.image, sampling));
|
|
2958
|
+
groupAlpha = (staged.foldsAlpha ? ctx.state.alpha : 1) * tint[3];
|
|
2772
2959
|
}
|
|
2773
2960
|
const sx = data[i + 4], sy = data[i + 5], sw = data[i + 6], sh = data[i + 7];
|
|
2774
2961
|
let cut;
|
|
@@ -2783,16 +2970,19 @@ function drawSprites(ctx, cmd) {
|
|
|
2783
2970
|
(sx + sw) / tw,
|
|
2784
2971
|
(sy + sh) / th,
|
|
2785
2972
|
data[i + 8] * groupAlpha,
|
|
2786
|
-
slot
|
|
2973
|
+
slot,
|
|
2974
|
+
tint[0],
|
|
2975
|
+
tint[1],
|
|
2976
|
+
tint[2]
|
|
2787
2977
|
)) {
|
|
2788
2978
|
cut = enterUncut(ctx);
|
|
2789
|
-
({ staged, slot } = stageImage(ctx, cmd.image, sampling));
|
|
2790
|
-
groupAlpha = staged.foldsAlpha ? ctx.state.alpha : 1;
|
|
2979
|
+
({ staged, slot, tint } = stageImage(ctx, cmd.image, sampling));
|
|
2980
|
+
groupAlpha = (staged.foldsAlpha ? ctx.state.alpha : 1) * tint[3];
|
|
2791
2981
|
}
|
|
2792
2982
|
if (cut !== void 0) {
|
|
2793
2983
|
leaveUncut(ctx, cut);
|
|
2794
|
-
({ staged, slot } = stageImage(ctx, cmd.image, sampling));
|
|
2795
|
-
groupAlpha = staged.foldsAlpha ? ctx.state.alpha : 1;
|
|
2984
|
+
({ staged, slot, tint } = stageImage(ctx, cmd.image, sampling));
|
|
2985
|
+
groupAlpha = (staged.foldsAlpha ? ctx.state.alpha : 1) * tint[3];
|
|
2796
2986
|
}
|
|
2797
2987
|
}
|
|
2798
2988
|
}
|
|
@@ -2842,7 +3032,7 @@ function drawGroupWithEffects(ctx, cmd, targets, width, height) {
|
|
|
2842
3032
|
flushBatch(ctx);
|
|
2843
3033
|
gl.disable(gl.BLEND);
|
|
2844
3034
|
gl.disable(gl.STENCIL_TEST);
|
|
2845
|
-
for (const effect of cmd.effects) {
|
|
3035
|
+
for (const effect of cmd.effects ?? []) {
|
|
2846
3036
|
const program = ctx.ensureProgram?.(effect.program.id) ?? null;
|
|
2847
3037
|
if (!program) {
|
|
2848
3038
|
warnOnceUniform(effect.program.id, "(program)");
|
|
@@ -2875,15 +3065,21 @@ function drawGroupWithEffects(ctx, cmd, targets, width, height) {
|
|
|
2875
3065
|
const composite = ctx.ensureProgram?.(compositeProgramId()) ?? null;
|
|
2876
3066
|
if (composite) {
|
|
2877
3067
|
applyClipTest(ctx);
|
|
3068
|
+
const ownBlend = isOwnComposite(cmd.composite);
|
|
3069
|
+
if (ownBlend) applyComposite(gl, cmd.composite);
|
|
2878
3070
|
drawFullscreenQuad(ctx, composite, front.texture, width, height, void 0, {
|
|
2879
3071
|
alpha: composited.alpha,
|
|
2880
3072
|
colorMatrix: composited.colorMatrix
|
|
2881
3073
|
});
|
|
3074
|
+
if (ownBlend) applyComposite(gl, "source-over");
|
|
2882
3075
|
}
|
|
2883
3076
|
targets.release(front);
|
|
2884
3077
|
}
|
|
3078
|
+
function ownsSurface(cmd) {
|
|
3079
|
+
return cmd.isolate === true || isOwnComposite(cmd.composite) || cmd.effects !== void 0 && cmd.effects.length > 0;
|
|
3080
|
+
}
|
|
2885
3081
|
function drawGroup(ctx, cmd) {
|
|
2886
|
-
if (cmd
|
|
3082
|
+
if (ownsSurface(cmd) && ctx.effectTargets && ctx.deviceWidth && ctx.deviceHeight) {
|
|
2887
3083
|
if (cmd.clip) {
|
|
2888
3084
|
ctx.state.push({ transform: cmd.transform });
|
|
2889
3085
|
try {
|
|
@@ -2985,8 +3181,7 @@ var EffectTargets = class {
|
|
|
2985
3181
|
}
|
|
2986
3182
|
/**
|
|
2987
3183
|
* 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.
|
|
3184
|
+
* drops every pooled buffer, borrowed ones included.
|
|
2990
3185
|
*/
|
|
2991
3186
|
acquire(width, height) {
|
|
2992
3187
|
if (width !== this.width || height !== this.height) {
|
|
@@ -3362,10 +3557,6 @@ var WeaselRenderer = class {
|
|
|
3362
3557
|
if (this.canvas) {
|
|
3363
3558
|
this.canvas.width = Math.round(opts.width * opts.dpr);
|
|
3364
3559
|
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
3560
|
}
|
|
3370
3561
|
const lossTarget = this.canvas ?? this.gl.canvas ?? null;
|
|
3371
3562
|
if (lossTarget && typeof lossTarget.addEventListener === "function") {
|
|
@@ -3415,12 +3606,11 @@ var WeaselRenderer = class {
|
|
|
3415
3606
|
/**
|
|
3416
3607
|
* Compile a consumer-registered shader program against this renderer's GL context.
|
|
3417
3608
|
*
|
|
3418
|
-
*
|
|
3419
|
-
*
|
|
3609
|
+
* Optional: a program compiles on the first frame that draws with it. Call
|
|
3610
|
+
* this after the module-level `registerProgram()` to pay for the compile
|
|
3611
|
+
* before that frame, or to catch the `ShaderCompileError` it throws.
|
|
3420
3612
|
*
|
|
3421
3613
|
* In dev mode, calling again with the same handle replaces the compiled program.
|
|
3422
|
-
*
|
|
3423
|
-
* @experimental
|
|
3424
3614
|
*/
|
|
3425
3615
|
registerProgram(handle) {
|
|
3426
3616
|
if (this.disposed) return;
|
|
@@ -3499,8 +3689,6 @@ var WeaselRenderer = class {
|
|
|
3499
3689
|
gl.bindVertexArray(null);
|
|
3500
3690
|
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
|
|
3501
3691
|
}
|
|
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
3692
|
/** This renderer's rect in device pixels, top-left origin. A tile rounds its
|
|
3505
3693
|
* edges rather than its size, so two that share a CSS edge share a device one. */
|
|
3506
3694
|
deviceRect() {
|
|
@@ -3515,6 +3703,8 @@ var WeaselRenderer = class {
|
|
|
3515
3703
|
height: Math.round((oy + this.heightCss) * this.dpr) - y
|
|
3516
3704
|
};
|
|
3517
3705
|
}
|
|
3706
|
+
/** Viewport and scissor for this frame. Re-applied inside `render()` because
|
|
3707
|
+
* a co-tenant on the same context moves both between our frames. */
|
|
3518
3708
|
applyTarget() {
|
|
3519
3709
|
const gl = this.gl;
|
|
3520
3710
|
const { x, y, width, height } = this.deviceRect();
|
|
@@ -3589,14 +3779,9 @@ var WeaselRenderer = class {
|
|
|
3589
3779
|
}
|
|
3590
3780
|
}
|
|
3591
3781
|
/** 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.
|
|
3782
|
+
* context-loss listeners. Idempotent. The context itself is left alone, so
|
|
3783
|
+
* a caller-owned context can host renderer after renderer without
|
|
3784
|
+
* accumulating anything.
|
|
3600
3785
|
*
|
|
3601
3786
|
* A disposed renderer ignores further `render()` and `registerProgram()`
|
|
3602
3787
|
* calls. */
|
|
@@ -3653,7 +3838,7 @@ var WeaselRenderer = class {
|
|
|
3653
3838
|
this.stats = { drawCalls: 0, ms: 0, spans: spanStats };
|
|
3654
3839
|
const gl = this.gl;
|
|
3655
3840
|
this.meshCache.drainPendingDeletes();
|
|
3656
|
-
this.imageCache.
|
|
3841
|
+
this.imageCache.beginFrame();
|
|
3657
3842
|
this.textureCache.sweep(textureRegistry);
|
|
3658
3843
|
this.refreshPrograms();
|
|
3659
3844
|
resetBakeBudget(this.bakeBudget);
|
|
@@ -3739,10 +3924,6 @@ var WeaselRenderer = class {
|
|
|
3739
3924
|
if (this.canvas) {
|
|
3740
3925
|
this.canvas.width = Math.round(dims.width * dims.dpr);
|
|
3741
3926
|
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
3927
|
}
|
|
3747
3928
|
this.applyTarget();
|
|
3748
3929
|
}
|
|
@@ -3867,6 +4048,7 @@ function cullOne(cmd, m, box) {
|
|
|
3867
4048
|
switch (cmd.kind) {
|
|
3868
4049
|
case "group": {
|
|
3869
4050
|
if (cmd.effects && cmd.effects.length > 0) return cmd;
|
|
4051
|
+
if (isOwnComposite(cmd.composite)) return cmd;
|
|
3870
4052
|
const inner = cmd.transform ? mat3.multiply(m, cmd.transform) : m;
|
|
3871
4053
|
const children = cullList(cmd.children, inner, box);
|
|
3872
4054
|
if (children === cmd.children) return cmd;
|
|
@@ -4070,15 +4252,15 @@ function textCommandFromPose(pose, scale = 1) {
|
|
|
4070
4252
|
};
|
|
4071
4253
|
}
|
|
4072
4254
|
function runsInPoseFrame(runs, box) {
|
|
4073
|
-
if (runs.every((r) => isSolid(r.fill) && isSolid(r.stroke?.paint))) return runs;
|
|
4255
|
+
if (runs.every((r) => isSolid(r.fill?.paint) && isSolid(r.stroke?.paint))) return runs;
|
|
4074
4256
|
return runs.map((r) => {
|
|
4075
4257
|
const fill = r.fill && commandFill(r.fill, box);
|
|
4076
|
-
const paint = r.stroke?.paint &&
|
|
4258
|
+
const paint = r.stroke?.paint && commandPaint(r.stroke.paint, box);
|
|
4077
4259
|
return { ...r, fill, stroke: r.stroke && { ...r.stroke, paint: paint || void 0 } };
|
|
4078
4260
|
});
|
|
4079
4261
|
}
|
|
4080
4262
|
function isSolid(paint) {
|
|
4081
|
-
return paint == null || (paint
|
|
4263
|
+
return paint == null || isSolidPaint(paint);
|
|
4082
4264
|
}
|
|
4083
4265
|
|
|
4084
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 };
|