@weasel-js/renderer 0.0.0-stage → 1.9.4
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/LICENSE +21 -0
- package/README.md +61 -2
- package/dist/chunk-2VOBNWBN.js +11 -0
- package/dist/chunk-2VOBNWBN.js.map +1 -0
- package/dist/chunk-ORBXHIRB.js +178 -0
- package/dist/chunk-ORBXHIRB.js.map +1 -0
- package/dist/chunk-VHQJ56XQ.js +1437 -0
- package/dist/chunk-VHQJ56XQ.js.map +1 -0
- package/dist/imageCache-oJVgxhkr.d.ts +372 -0
- package/dist/index.d.ts +1070 -0
- package/dist/index.js +4086 -0
- package/dist/index.js.map +1 -0
- package/dist/mesh.d.ts +274 -0
- package/dist/mesh.js +603 -0
- package/dist/mesh.js.map +1 -0
- package/dist/paintKinds-B2vEy9A4.d.ts +330 -0
- package/dist/patterns-builtin.d.ts +118 -0
- package/dist/patterns-builtin.js +28 -0
- package/dist/patterns-builtin.js.map +1 -0
- package/dist/test-seams.d.ts +120 -0
- package/dist/test-seams.js +4 -0
- package/dist/test-seams.js.map +1 -0
- package/dist/testing/glRecorder.d.ts +36 -0
- package/dist/testing/glRecorder.js +155 -0
- package/dist/testing/glRecorder.js.map +1 -0
- package/dist/testing/vertexUploads.d.ts +30 -0
- package/dist/testing/vertexUploads.js +48 -0
- package/dist/testing/vertexUploads.js.map +1 -0
- package/package.json +71 -4
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1070 @@
|
|
|
1
|
+
import { S as ShaderProgramHandle, a as ShaderUniform, b as ShaderProgram, F as FillPoseBox } from './paintKinds-B2vEy9A4.js';
|
|
2
|
+
export { P as PaintBindContext, c as PaintDrawKind, d as PaintDrawKindLoader, e as PaintProgram, f as PaintResources, g as ProgramSource, h as ShaderCompileError, i as fillInPoseFrame, j as fillToBoundsFrame, k as getPaintDrawKind, l as getProgramSource, m as isPaintKindKnown, p as paintDrawKindRegistry, n as programSourceRegistry, r as registerPaintDrawKind, o as registerPaintDrawKindLoader, q as registerProgram, w as warmPaintKinds } from './paintKinds-B2vEy9A4.js';
|
|
3
|
+
import { Mesh } from '@weasel-js/geom/tessellate';
|
|
4
|
+
export { Mesh, TessellateOptions, tessellate } from '@weasel-js/geom/tessellate';
|
|
5
|
+
import { GradStop, ColorSpace, CommandFill, CommandStroke, Stroke, FillStyle, TextureHandle } from '@weasel-js/paint';
|
|
6
|
+
export { CommandFill, CommandPaintUnits, CommandStroke, PosedFill, SolidFill, TextureHandle } from '@weasel-js/paint';
|
|
7
|
+
import { GlMat3, Path } from '@weasel-js/geom';
|
|
8
|
+
export { GlMat3, View as ViewLike, mat3, viewToMat3 } from '@weasel-js/geom';
|
|
9
|
+
import { ResolvedRun, TextAlign, TextDirection, TextStyle, TextVerticalAlign, TextPaint, TextPose, StyledRun } from '@weasel-js/text';
|
|
10
|
+
import { D as DrawBatch } from './imageCache-oJVgxhkr.js';
|
|
11
|
+
export { I as ImageNodeData, a as ImageRasterSize, b as ImageStatus, T as TextureEntry, g as getImageBitmap, i as imageStatus, c as isPatternSpec, d as isVectorImageSrc, r as registerTexture, e as releaseImage, f as resolveFillPattern, h as resolvePatternSpec, j as retryImage, s as subscribeImageReady, t as textureRegistry, u as unregisterTexture } from './imageCache-oJVgxhkr.js';
|
|
12
|
+
export { StrokeOptions, tessellateStroke } from '@weasel-js/stroke';
|
|
13
|
+
export { RegisterFontOptions, canQueryLocalFonts, cssFontFamily, cssFontFamilyLoading, enableLocalFontOutlines, fontOutlineRegistry, fontRegistry, hasFontOutlines, isCanvasFont, listFontOutlines, loadFontOutlines, outlineStatus, registerCanvasFont, registerFont, registerFontOutlines, subscribeGlyphReady, unregisterCanvasFont, unregisterFontOutlines } from '@weasel-js/font';
|
|
14
|
+
import '@weasel-js/registry';
|
|
15
|
+
|
|
16
|
+
interface GLMeshHandle {
|
|
17
|
+
readonly vao: WebGLVertexArrayObject;
|
|
18
|
+
readonly indexCount: number;
|
|
19
|
+
readonly requiresStencil: boolean;
|
|
20
|
+
readonly anchorA?: Uint32Array;
|
|
21
|
+
readonly anchorB?: Uint32Array;
|
|
22
|
+
readonly anchorT?: Float32Array;
|
|
23
|
+
}
|
|
24
|
+
/** Resources to release when a Mesh is reclaimed by GC. */
|
|
25
|
+
interface MeshResources {
|
|
26
|
+
vao: WebGLVertexArrayObject;
|
|
27
|
+
vbo: WebGLBuffer;
|
|
28
|
+
ibo: WebGLBuffer;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Caches GL-side buffers + VAO per `Mesh` identity. Upload happens lazily
|
|
32
|
+
* on first `handleFor(mesh)` call.
|
|
33
|
+
*
|
|
34
|
+
* **GC-aware cleanup (deferred-delete queue):** when a Mesh becomes
|
|
35
|
+
* unreachable, the WeakMap entry is dropped automatically — but the
|
|
36
|
+
* underlying GL resources (VAO, VBO, IBO) would leak forever without an
|
|
37
|
+
* explicit `gl.delete*` call. We register each Mesh with a
|
|
38
|
+
* `FinalizationRegistry`; when the Mesh is reclaimed, the finalizer pushes
|
|
39
|
+
* the resources onto an internal queue. The renderer drains this queue at
|
|
40
|
+
* the start of each `render()` call (via `drainPendingDeletes()`), when GL
|
|
41
|
+
* is in a known state — no VAO bound, no draw mid-flight. Deleting from the
|
|
42
|
+
* finalizer directly was racy (it could fire between two `gl.bindVertexArray`
|
|
43
|
+
* calls inside `dispatch()`, sometimes corrupting the next draw); deferring
|
|
44
|
+
* the actual delete to a known-safe point keeps the use-after-free risk to
|
|
45
|
+
* zero.
|
|
46
|
+
*
|
|
47
|
+
* Caveats:
|
|
48
|
+
* - Finalizer timing is non-deterministic; resources may live for a frame
|
|
49
|
+
* or two beyond their Mesh, but they will be reclaimed.
|
|
50
|
+
* - If the GL context is lost between finalizer and drain, the drain
|
|
51
|
+
* no-ops safely (`gl.isContextLost()` makes deletes no-ops, and
|
|
52
|
+
* `gl.deleteBuffer(null)` is allowed).
|
|
53
|
+
*
|
|
54
|
+
* The cache is GL-context-bound; if the context is lost and re-created, the
|
|
55
|
+
* renderer should construct a new GLMeshCache. (Context loss handling lives
|
|
56
|
+
* in `WeaselRenderer`.)
|
|
57
|
+
*/
|
|
58
|
+
declare class GLMeshCache {
|
|
59
|
+
private readonly gl;
|
|
60
|
+
private readonly map;
|
|
61
|
+
/** Meshes this context has drawn at least once; see `uploadRecurring`. */
|
|
62
|
+
private readonly seen;
|
|
63
|
+
private readonly finalizer;
|
|
64
|
+
/** Every persistent upload not yet freed, so `dispose` can reach them. */
|
|
65
|
+
private readonly persistent;
|
|
66
|
+
private readonly pendingDeletes;
|
|
67
|
+
private disposed;
|
|
68
|
+
/** Transient resources allocated this frame; freed at end of render(). */
|
|
69
|
+
private readonly transientThisFrame;
|
|
70
|
+
constructor(gl: WebGL2RenderingContext);
|
|
71
|
+
handleFor(mesh: Mesh): GLMeshHandle;
|
|
72
|
+
/**
|
|
73
|
+
* Upload a Mesh that the caller knows is single-use (e.g. the per-frame
|
|
74
|
+
* stroke ribbon from `tessellateStroke`). Returns a handle backed by fresh
|
|
75
|
+
* GL resources that the renderer will free deterministically at the end
|
|
76
|
+
* of the current frame via `freeTransient()`. Bypasses the WeakMap cache
|
|
77
|
+
* and the FinalizationRegistry, so transient meshes never wait on GC.
|
|
78
|
+
*/
|
|
79
|
+
uploadTransient(mesh: Mesh): GLMeshHandle;
|
|
80
|
+
/**
|
|
81
|
+
* Upload a Mesh that may or may not recur across frames. Its first sight in
|
|
82
|
+
* *this* context takes `uploadTransient`; every later one takes the
|
|
83
|
+
* persistent handle. One transient upload to find out is cheaper than
|
|
84
|
+
* stranding a persistent VAO on a mesh that never returns, whose release
|
|
85
|
+
* would wait on GC. The transient upload does not populate the persistent
|
|
86
|
+
* map, so steady-state reuse begins on the third frame.
|
|
87
|
+
*/
|
|
88
|
+
uploadRecurring(mesh: Mesh): GLMeshHandle;
|
|
89
|
+
/**
|
|
90
|
+
* Free all transient resources allocated since the last call. Called by
|
|
91
|
+
* the renderer at the end of each `render()`. Safe under context loss.
|
|
92
|
+
*/
|
|
93
|
+
freeTransient(): void;
|
|
94
|
+
/** @internal — for tests asserting the transient-list size. */
|
|
95
|
+
_transientCount(): number;
|
|
96
|
+
/**
|
|
97
|
+
* Free GL resources whose Mesh has been GC'd since the last drain.
|
|
98
|
+
* Called by the renderer at the top of each `render()`, before any draws.
|
|
99
|
+
*/
|
|
100
|
+
drainPendingDeletes(): void;
|
|
101
|
+
/**
|
|
102
|
+
* Free every GL resource this cache holds — persistent, pending and
|
|
103
|
+
* transient — and ignore finalizers from here on. Idempotent. Unlike the
|
|
104
|
+
* other paths this does not wait on GC, which is what lets a renderer on a
|
|
105
|
+
* context it does not own give back everything it uploaded.
|
|
106
|
+
*/
|
|
107
|
+
dispose(): void;
|
|
108
|
+
/** @internal — for tests asserting the queue size. */
|
|
109
|
+
_pendingDeleteCount(): number;
|
|
110
|
+
/** @internal — for tests that need to simulate the finalizer firing. */
|
|
111
|
+
_enqueueDeleteForTest(resources: MeshResources): void;
|
|
112
|
+
private upload;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* GL texture upload + cache for MSDF font atlases (and future images).
|
|
117
|
+
*
|
|
118
|
+
* Textures are keyed by a string id (the font family name or image id).
|
|
119
|
+
* Pixels are uploaded once per id; a later upload of the same id reuses the
|
|
120
|
+
* texture and only re-applies its wrap mode when that changed.
|
|
121
|
+
* The cache is GL-context-bound; discard and create a new one on context loss.
|
|
122
|
+
*
|
|
123
|
+
* Atlas format: RGBA UNSIGNED_BYTE, linear filtering, no mipmaps.
|
|
124
|
+
* Mipmap generation is deliberately skipped — MSDF works correctly with
|
|
125
|
+
* linear filtering, and mipmap resampling corrupts the multi-channel SDF signal.
|
|
126
|
+
*/
|
|
127
|
+
type TexSource$1 = HTMLImageElement | ImageBitmap | ImageData | HTMLCanvasElement;
|
|
128
|
+
/** How a texture samples outside `0..1`. Pattern tiles need `'repeat'`;
|
|
129
|
+
* everything else clamps. WebGL2 allows `REPEAT` on NPOT textures, so a
|
|
130
|
+
* tile of any size tiles correctly. */
|
|
131
|
+
type TextureWrap = 'clamp' | 'repeat';
|
|
132
|
+
/** The part of the texture registry a sweep reads. */
|
|
133
|
+
interface RegisteredTextureIds {
|
|
134
|
+
getVersion(): number;
|
|
135
|
+
has(id: string): boolean;
|
|
136
|
+
}
|
|
137
|
+
declare class GLTextureCache {
|
|
138
|
+
private readonly gl;
|
|
139
|
+
private readonly map;
|
|
140
|
+
private readonly wraps;
|
|
141
|
+
/** The source each registry-backed id was last uploaded from. */
|
|
142
|
+
private readonly registered;
|
|
143
|
+
private sweptVersion;
|
|
144
|
+
constructor(gl: WebGL2RenderingContext);
|
|
145
|
+
has(id: string): boolean;
|
|
146
|
+
/** Uploads the pixels once per id. `wrap` is re-applied whenever it differs
|
|
147
|
+
* from what the id was last uploaded under: the same registered image can
|
|
148
|
+
* be a clamped shader texture in one draw and a repeating pattern tile in
|
|
149
|
+
* the next, and first-upload-wins gave the second one the first one's mode. */
|
|
150
|
+
upload(id: string, source: TexSource$1, wrap?: TextureWrap): string;
|
|
151
|
+
/** `upload` for an id from the texture registry, whose image can be replaced
|
|
152
|
+
* under the same id: a changed source sends its pixels again. */
|
|
153
|
+
uploadRegistered(id: string, source: TexSource$1, wrap?: TextureWrap): string;
|
|
154
|
+
/**
|
|
155
|
+
* Delete the texture of every registry-backed id the registry has dropped.
|
|
156
|
+
* The renderer calls this at the top of `render()`, before any draw, so a
|
|
157
|
+
* texture is never deleted under a draw that has it bound.
|
|
158
|
+
*/
|
|
159
|
+
sweep(registry: RegisteredTextureIds): void;
|
|
160
|
+
private applyWrap;
|
|
161
|
+
/** Create a single-channel R8 texture from raw bytes (full upload).
|
|
162
|
+
* No-op if `id` already exists. Same LINEAR/CLAMP params as `upload`;
|
|
163
|
+
* UNPACK_ALIGNMENT dropped to 1 for non-4-aligned row widths. */
|
|
164
|
+
uploadR8(id: string, width: number, height: number, data: Uint8Array): void;
|
|
165
|
+
/** Patch a rect of an existing R8 texture with tightly-packed w×h bytes. */
|
|
166
|
+
subImageR8(id: string, x: number, y: number, w: number, h: number, data: Uint8Array): void;
|
|
167
|
+
bind(id: string, unit: number): void;
|
|
168
|
+
/**
|
|
169
|
+
* Delete every uploaded GL texture and clear the map. Called by
|
|
170
|
+
* `WeaselRenderer.dispose()`. The cache is unusable but refillable
|
|
171
|
+
* afterward — a subsequent `upload()` for a previously-seen id re-creates
|
|
172
|
+
* the texture rather than restoring the deleted one.
|
|
173
|
+
*/
|
|
174
|
+
free(): void;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* GL texture upload cache for ImageBitmap objects.
|
|
179
|
+
*
|
|
180
|
+
* Key: ImageBitmap (or pattern source object) identity (WeakMap) — lets GC
|
|
181
|
+
* reclaim unreferenced bitmaps. A collected source's texture is queued and
|
|
182
|
+
* deleted at the renderer's next frame, the way `GLMeshCache` frees a mesh's
|
|
183
|
+
* buffers; GC timing is not prompt, so a source's owner that knows it will
|
|
184
|
+
* never be drawn again frees it at once with {@link releaseImageSource}, and
|
|
185
|
+
* `dispose` frees them all.
|
|
186
|
+
*
|
|
187
|
+
* Wrapping is set once at upload time per the `repetition` parameter:
|
|
188
|
+
* - undefined / 'no-repeat' → CLAMP_TO_EDGE
|
|
189
|
+
* - 'repeat' → REPEAT (both axes)
|
|
190
|
+
* - 'repeat-x' → REPEAT on S, CLAMP on T
|
|
191
|
+
* - 'repeat-y' → CLAMP on S, REPEAT on T
|
|
192
|
+
*
|
|
193
|
+
* Convention §2: texels stored straight; shader premultiplies.
|
|
194
|
+
*/
|
|
195
|
+
type PatternRepetition = 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat';
|
|
196
|
+
/** MIN_FILTER strategy for uploaded textures. */
|
|
197
|
+
type ImageMinification = 'linear' | 'mipmap';
|
|
198
|
+
type TexSource = ImageBitmap | ImageData | HTMLCanvasElement | HTMLImageElement;
|
|
199
|
+
/** Free `key`'s texture in every live renderer. For an owner that has replaced
|
|
200
|
+
* a source and will never draw the old one again — a later draw of it simply
|
|
201
|
+
* re-uploads. */
|
|
202
|
+
declare function releaseImageSource(key: object): void;
|
|
203
|
+
declare class GLImageCache {
|
|
204
|
+
private readonly gl;
|
|
205
|
+
private readonly minification;
|
|
206
|
+
/** This context's `MAX_TEXTURE_SIZE`. */
|
|
207
|
+
readonly maxTextureSide: number;
|
|
208
|
+
private readonly map;
|
|
209
|
+
/** Every texture `map` holds, so `dispose` can reach them — a WeakMap
|
|
210
|
+
* cannot be enumerated. */
|
|
211
|
+
private readonly textures;
|
|
212
|
+
/**
|
|
213
|
+
* MAG_FILTER each texture currently carries, so a redundant write can be
|
|
214
|
+
* skipped.
|
|
215
|
+
*
|
|
216
|
+
* The batch sets this per flush — the same bitmap can be drawn at both
|
|
217
|
+
* filters in one frame, and the value has to be right at the draw rather than
|
|
218
|
+
* at upload. Filtering is state on the *texture object*, though, not on the
|
|
219
|
+
* unit, so re-asserting a value it already has is a write to a live texture
|
|
220
|
+
* for no reason. On a large sheet that is not free: a consumer's wall samples
|
|
221
|
+
* a 5652px-square atlas, 122MB resident, and measured `sampling: 'nearest'`
|
|
222
|
+
* costing up to 8x `'linear'` there — where the linear pass re-asserts the
|
|
223
|
+
* upload default and the nearest pass changes state on every draw.
|
|
224
|
+
*
|
|
225
|
+
* Whether that is the cause is unproven (see `docs/TODO.md`), but the write
|
|
226
|
+
* was redundant either way.
|
|
227
|
+
*/
|
|
228
|
+
private readonly magFilters;
|
|
229
|
+
private readonly finalizer;
|
|
230
|
+
private readonly pendingDeletes;
|
|
231
|
+
/** `minification` selects the MIN_FILTER strategy for uploaded textures.
|
|
232
|
+
* `'linear'` (default) is the screen path's existing behavior. `'mipmap'`
|
|
233
|
+
* generates mipmaps and filters LINEAR_MIPMAP_LINEAR — required for
|
|
234
|
+
* quality minification when a large source bitmap is drawn small (the
|
|
235
|
+
* headless print/export path); bilinear-only minification undersamples
|
|
236
|
+
* and produces moiré. */
|
|
237
|
+
constructor(gl: WebGL2RenderingContext, minification?: ImageMinification);
|
|
238
|
+
/** Delete the textures of sources collected since the last call. The
|
|
239
|
+
* renderer calls this at the top of each `render()`. */
|
|
240
|
+
drainPendingDeletes(): void;
|
|
241
|
+
/** Delete `key`'s texture, if it has one. */
|
|
242
|
+
release(key: object): void;
|
|
243
|
+
/** Delete every texture this cache uploaded and stop answering
|
|
244
|
+
* {@link releaseImageSource} and {@link maxImageTextureSide}. Idempotent. */
|
|
245
|
+
dispose(): void;
|
|
246
|
+
/** {@link dispose} without the deletes, for a cache whose context was lost:
|
|
247
|
+
* its textures died with it. */
|
|
248
|
+
abandon(): void;
|
|
249
|
+
upload(key: object, source: TexSource, repetition?: PatternRepetition): WebGLTexture;
|
|
250
|
+
bind(key: object, unit: number): void;
|
|
251
|
+
/** Set `key`'s MAG_FILTER, skipping the call when it already holds that
|
|
252
|
+
* value. Its texture must be bound to the active unit — callers pair this
|
|
253
|
+
* with `bind`. */
|
|
254
|
+
setMagFilter(key: object, filter: number): void;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* CPU gradient-ramp builder + the GL texture every baked ramp lives in.
|
|
259
|
+
*
|
|
260
|
+
* Each unique stop list is baked once into a 256-texel strip and written to its
|
|
261
|
+
* own **row** of one RGBA texture, keyed by `JSON.stringify(stops)`. One
|
|
262
|
+
* texture rather than one per ramp is what lets a gradient take a batch texture
|
|
263
|
+
* slot the way a bitmap or a font atlas does: every gradient in a frame samples
|
|
264
|
+
* the same unit, so a run does not break per gradient. The atlas is
|
|
265
|
+
* GL-context-bound; discard and recreate on context loss.
|
|
266
|
+
*
|
|
267
|
+
* Output convention §2: texels are stored as straight RGBA. The fragment
|
|
268
|
+
* shader applies premultiplication before writing outColor.
|
|
269
|
+
*/
|
|
270
|
+
|
|
271
|
+
/** Bake gradient stops into a 256-entry RGBA lookup strip, which the shader
|
|
272
|
+
* samples instead of evaluating stops per fragment. `space` is the gradient's
|
|
273
|
+
* `interpolate` — the ramp is where a non-sRGB blend is paid for, once. */
|
|
274
|
+
declare function buildGradientRamp(stops: GradStop[], space?: ColorSpace): Uint8ClampedArray;
|
|
275
|
+
declare class GradientRampAtlas {
|
|
276
|
+
private readonly gl;
|
|
277
|
+
/** Row per stop list, in least-recently-used order: a hit re-inserts, so the
|
|
278
|
+
* first entry is the row a full atlas recycles. */
|
|
279
|
+
private readonly rowByKey;
|
|
280
|
+
private texture;
|
|
281
|
+
private rows;
|
|
282
|
+
/** Mirror of the texture, so growth can respecify it without re-baking every
|
|
283
|
+
* ramp it already holds. */
|
|
284
|
+
private pixels;
|
|
285
|
+
private totalQueries;
|
|
286
|
+
private cacheHits;
|
|
287
|
+
constructor(gl: WebGL2RenderingContext);
|
|
288
|
+
/** Rows the atlas currently holds — its texture height. */
|
|
289
|
+
get height(): number;
|
|
290
|
+
/**
|
|
291
|
+
* Bake `stops` into a row and return it, reusing the row an identical stop
|
|
292
|
+
* list already holds.
|
|
293
|
+
*
|
|
294
|
+
* **A returned row outlives the frame only while the atlas has room.** Past
|
|
295
|
+
* `RAMP_ATLAS_MAX_ROWS` an upload recycles the least recently used row, which
|
|
296
|
+
* rewrites texels a row handed out earlier still names. Nothing that defers
|
|
297
|
+
* its draw past this call may hold a row across another `upload` without
|
|
298
|
+
* arranging to be flushed first.
|
|
299
|
+
*/
|
|
300
|
+
upload(stops: GradStop[], space?: ColorSpace): number;
|
|
301
|
+
/**
|
|
302
|
+
* Whether uploading `stops` would move where existing rows sit — by growing
|
|
303
|
+
* the atlas, which changes every row's `v`, or by recycling one, which
|
|
304
|
+
* rewrites its texels.
|
|
305
|
+
*
|
|
306
|
+
* Anything holding a row past this call asks first and gets itself out of
|
|
307
|
+
* the way, because neither can be undone once it has happened.
|
|
308
|
+
*/
|
|
309
|
+
wouldReshape(stops: GradStop[], space?: ColorSpace): boolean;
|
|
310
|
+
/**
|
|
311
|
+
* The `v` a row is sampled at — its center.
|
|
312
|
+
*
|
|
313
|
+
* The center is load-bearing: the atlas filters LINEAR, so a `v` anywhere
|
|
314
|
+
* else blends the ramp beside it into this one. At the center the neighbor's
|
|
315
|
+
* weight is exactly zero.
|
|
316
|
+
*/
|
|
317
|
+
rowV(row: number): number;
|
|
318
|
+
bind(unit: number): void;
|
|
319
|
+
hitRate(): number;
|
|
320
|
+
resetStats(): void;
|
|
321
|
+
/**
|
|
322
|
+
* Delete the atlas texture and forget every row in it. Called by
|
|
323
|
+
* `WeaselRenderer.dispose()`. The CPU mirror goes with it, so the atlas is
|
|
324
|
+
* unusable but refillable afterward (stats are left as-is; call
|
|
325
|
+
* `resetStats()` separately if desired).
|
|
326
|
+
*/
|
|
327
|
+
free(): void;
|
|
328
|
+
/** The row the next ramp is written to: a free one, a taller atlas, or the
|
|
329
|
+
* least recently used row of a full one. */
|
|
330
|
+
private claimRow;
|
|
331
|
+
/** Grow to `rows`, keeping every row at the index it already had — a row
|
|
332
|
+
* index is a stable name, and callers hold them. */
|
|
333
|
+
private resize;
|
|
334
|
+
private writeRow;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** Row-major 4×5 color matrix identity. */
|
|
338
|
+
declare const IDENTITY_COLOR_MATRIX: Float32Array<ArrayBuffer>;
|
|
339
|
+
interface GroupFrame {
|
|
340
|
+
transform?: GlMat3;
|
|
341
|
+
alpha?: number;
|
|
342
|
+
/** Row-major 4×5 color matrix (20 floats). Absent leaves the stack unchanged. */
|
|
343
|
+
colorMatrix?: Float32Array | number[];
|
|
344
|
+
}
|
|
345
|
+
declare class GroupState {
|
|
346
|
+
private transformStack;
|
|
347
|
+
private alphaStack;
|
|
348
|
+
private colorMatrixStack;
|
|
349
|
+
get transform(): GlMat3;
|
|
350
|
+
get alpha(): number;
|
|
351
|
+
get colorMatrix(): Float32Array;
|
|
352
|
+
push(frame: GroupFrame): void;
|
|
353
|
+
/**
|
|
354
|
+
* Push a frame whose children paint into a surface of their own.
|
|
355
|
+
*
|
|
356
|
+
* The transform still accumulates — the children draw where they would have
|
|
357
|
+
* drawn. Alpha and colour do not: they describe how the finished surface
|
|
358
|
+
* joins the frame, and applying them on the way in as well would fade the
|
|
359
|
+
* pixels an effect is about to read, then fade them again on the way out.
|
|
360
|
+
*
|
|
361
|
+
* Returns the pair the caller must apply at composite time — what `push`
|
|
362
|
+
* would have left on the stack — so the composition rules live here and not
|
|
363
|
+
* in the dispatcher.
|
|
364
|
+
*/
|
|
365
|
+
pushIsolated(frame: GroupFrame): {
|
|
366
|
+
alpha: number;
|
|
367
|
+
colorMatrix: Float32Array;
|
|
368
|
+
};
|
|
369
|
+
/** Drop every pushed frame, leaving the root. A frame that throws part-way
|
|
370
|
+
* down the tree never pops, and the next frame would draw under leftovers. */
|
|
371
|
+
reset(): void;
|
|
372
|
+
pop(): void;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* One full-screen pass over what a group has already drawn.
|
|
377
|
+
*
|
|
378
|
+
* The renderer runs a group's effects in order, each reading the previous
|
|
379
|
+
* one's output through `u_source` and writing a whole new buffer — so an
|
|
380
|
+
* effect is free to read neighbouring pixels, which is the entire point and
|
|
381
|
+
* the one thing `colorMatrix` can never do.
|
|
382
|
+
*
|
|
383
|
+
* `uniforms` are the effect's own; `u_source`, `u_resolution` and `u_texel`
|
|
384
|
+
* come from the renderer and must not be passed here.
|
|
385
|
+
*/
|
|
386
|
+
interface Effect {
|
|
387
|
+
program: ShaderProgramHandle;
|
|
388
|
+
uniforms?: Record<string, ShaderUniform>;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Register a fragment shader as an effect. Sugar over `registerProgram` with
|
|
392
|
+
* the effect vertex shader, and the reason a consumer never imports the
|
|
393
|
+
* prelude: an effect that registers with the *custom-shader* vertex shader
|
|
394
|
+
* compiles, runs, and samples its source upside down.
|
|
395
|
+
*
|
|
396
|
+
* The fragment shader reads `v_uv` and `u_source`, and may declare
|
|
397
|
+
* `u_resolution` / `u_texel`. See `effectPrelude.ts` for the full contract,
|
|
398
|
+
* including the premultiplied-alpha requirement.
|
|
399
|
+
*
|
|
400
|
+
* @experimental
|
|
401
|
+
*/
|
|
402
|
+
declare function registerEffect(id: string, frag: string): ShaderProgramHandle;
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Solid-fill paint variant (subset of the full `FillStyle` union from
|
|
406
|
+
* `@weasel-js/paint`). Kept for back-compat with step-1/2 consumers and
|
|
407
|
+
* because some code reads `fill.color` directly. Through step 4, fills can
|
|
408
|
+
* be any `FillStyle` variant — solid, pattern, or gradient.
|
|
409
|
+
*/
|
|
410
|
+
interface SolidPaint {
|
|
411
|
+
fill?: 'solid';
|
|
412
|
+
/** Any CSS color string accepted by `parseColor`: hex, `rgb()`/`rgba()`, `hsl()`/`hsla()`, named, or `transparent`. */
|
|
413
|
+
color: string;
|
|
414
|
+
opacity?: number;
|
|
415
|
+
}
|
|
416
|
+
/** DrawCommand variants implemented through step 6. */
|
|
417
|
+
type DrawCommand = PathDrawCommand | GroupDrawCommand | TextDrawCommand | ImageDrawCommand | SpritesDrawCommand | ShaderDrawCommand;
|
|
418
|
+
/** Draw a path, filled and/or stroked. The workhorse command: every shape the
|
|
419
|
+
* kit draws that is not text, an image, or a custom shader is one of these. */
|
|
420
|
+
interface PathDrawCommand {
|
|
421
|
+
kind: 'path';
|
|
422
|
+
path: Path;
|
|
423
|
+
/** Solid, pattern, or gradient (linear/radial/conic), resolved for the
|
|
424
|
+
* renderer: `commandFill` makes one from a node's `FillStyle`. */
|
|
425
|
+
fill?: CommandFill;
|
|
426
|
+
/** Stroke spec. Its `paint` takes what `fill` does. */
|
|
427
|
+
stroke?: CommandStroke;
|
|
428
|
+
/**
|
|
429
|
+
* Optional flat RGBA-per-path-anchor color array (length =
|
|
430
|
+
* `4 × countPathAnchors(path)`, floats in 0..1). The renderer
|
|
431
|
+
* arc-length-interpolates these per-anchor colors across the
|
|
432
|
+
* flattened/triangulated mesh between consecutive anchors using the
|
|
433
|
+
* mesh's `anchorA` / `anchorB` / `anchorT` parameterization.
|
|
434
|
+
*
|
|
435
|
+
* **`fill` must also be set when using `vertexColors`.** The renderer
|
|
436
|
+
* only enters the per-vertex shader path when the command has a fill
|
|
437
|
+
* (the fill provides the opacity uniform; the vertex colors override
|
|
438
|
+
* the fill's color). Pass any solid `fill` (e.g. `{ color: '#fff' }`)
|
|
439
|
+
* as the placeholder; the per-vertex colors win in the shader.
|
|
440
|
+
*/
|
|
441
|
+
vertexColors?: number[];
|
|
442
|
+
}
|
|
443
|
+
/** Draw a list of commands under a shared transform, opacity, color matrix
|
|
444
|
+
* and clip. Groups nest, and their effects accumulate down the stack — this
|
|
445
|
+
* is how a container node's transform reaches its descendants. */
|
|
446
|
+
interface GroupDrawCommand {
|
|
447
|
+
kind: 'group';
|
|
448
|
+
transform?: GlMat3;
|
|
449
|
+
alpha?: number;
|
|
450
|
+
/**
|
|
451
|
+
* Optional 4×5 color matrix (row-major, 20 numbers) — `out = M₄ₓ₄ * in + bias`.
|
|
452
|
+
* Accumulated multiplicatively down the group stack. Defaults to identity.
|
|
453
|
+
*/
|
|
454
|
+
colorMatrix?: number[];
|
|
455
|
+
/** Optional clip path. When set, the renderer rasterizes this path into
|
|
456
|
+
* the stencil buffer before drawing `children`; the children paint only
|
|
457
|
+
* where the clip covers. Nested groups with clips intersect — a child
|
|
458
|
+
* cannot escape an ancestor's clip. Max 7 nesting levels; the renderer
|
|
459
|
+
* throws if exceeded. */
|
|
460
|
+
clip?: Path;
|
|
461
|
+
/**
|
|
462
|
+
* Full-screen passes run over this group's own pixels, in order, before it
|
|
463
|
+
* is composited into its parent.
|
|
464
|
+
*
|
|
465
|
+
* Unlike every other field here, this does not accumulate down the group
|
|
466
|
+
* stack — it is a render-target boundary. The children draw into a buffer of
|
|
467
|
+
* their own, each effect reads the previous one's output, and the result is
|
|
468
|
+
* composited back under this group's `transform`, `alpha`, `colorMatrix` and
|
|
469
|
+
* whatever clip encloses it. So a blur here blurs this group and nothing
|
|
470
|
+
* around it, which is what a CSS `filter` on the canvas cannot do.
|
|
471
|
+
*
|
|
472
|
+
* An empty or absent list costs nothing: no buffer is allocated until a
|
|
473
|
+
* group asks for one.
|
|
474
|
+
*/
|
|
475
|
+
effects?: readonly Effect[];
|
|
476
|
+
children: DrawCommand[];
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Text draw command. Renders one or more runs at (`x`, `y`) in screen
|
|
480
|
+
* space, optionally word-wrapping at `maxWidth`. The renderer resolves
|
|
481
|
+
* each run's `(fontFamily, fontWeight, fontStyle)` to an MSDF atlas via
|
|
482
|
+
* `resolveFontVariant` and bucket-draws by atlas + color group.
|
|
483
|
+
*
|
|
484
|
+
* `style` carries node-level defaults (`lineHeight`, anti-alias width)
|
|
485
|
+
* that don't belong on individual runs.
|
|
486
|
+
*/
|
|
487
|
+
interface TextDrawCommand {
|
|
488
|
+
kind: 'text';
|
|
489
|
+
x: number;
|
|
490
|
+
y: number;
|
|
491
|
+
runs: ResolvedRun[];
|
|
492
|
+
/** Wrap width. Absent never wraps. */
|
|
493
|
+
maxWidth?: number;
|
|
494
|
+
align?: TextAlign;
|
|
495
|
+
/** What `start` / `end` resolve against, and the base direction of bidi
|
|
496
|
+
* reordering — see `LayoutRunsOpts.direction`. Default `style`'s. */
|
|
497
|
+
direction?: TextDirection | 'auto';
|
|
498
|
+
/** Spread each wrapped line across `width` — see `LayoutRunsOpts.justify`.
|
|
499
|
+
* `align` then sets only the lines it does not spread. Default `false`. */
|
|
500
|
+
justify?: boolean;
|
|
501
|
+
/** Box width `align` resolves within, from `x`. Default `maxWidth`; with
|
|
502
|
+
* neither, `x` is the line's left edge, midpoint or right edge. */
|
|
503
|
+
width?: number;
|
|
504
|
+
style: TextStyle;
|
|
505
|
+
/** Box height for vertical alignment. When set with `verticalAlign`,
|
|
506
|
+
* the laid-out block shifts within `[y, y+height]`. */
|
|
507
|
+
height?: number;
|
|
508
|
+
/** Default 'top' — the legacy top-anchored behavior. */
|
|
509
|
+
verticalAlign?: TextVerticalAlign;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Image draw command — renders `image` at screen-space rect (x, y, w, h).
|
|
513
|
+
* The image is stretched to fit; no tiling. Use a pattern FillStyle on a path
|
|
514
|
+
* for tiling.
|
|
515
|
+
*/
|
|
516
|
+
interface ImageDrawCommand {
|
|
517
|
+
kind: 'image';
|
|
518
|
+
image: ImageBitmap;
|
|
519
|
+
x: number;
|
|
520
|
+
y: number;
|
|
521
|
+
w: number;
|
|
522
|
+
h: number;
|
|
523
|
+
opacity?: number;
|
|
524
|
+
/** Magnification filter. `'linear'` (default) smooths; `'nearest'` shows
|
|
525
|
+
* device pixels as hard squares — required by anything magnifying a
|
|
526
|
+
* framebuffer readback, where blur destroys the point of the readback. */
|
|
527
|
+
sampling?: 'linear' | 'nearest';
|
|
528
|
+
/** Sub-rectangle of `image` to draw, in bitmap pixels from the top-left.
|
|
529
|
+
* Omitted draws the whole bitmap. Not range-checked: a rect past the edge
|
|
530
|
+
* samples outside [0..1], which CLAMP_TO_EDGE smears. With
|
|
531
|
+
* `sampling: 'linear'` the filter reaches half a texel beyond `source`, so
|
|
532
|
+
* atlas frames need a gutter (see `SpriteSheet.spacing`) or `'nearest'`. */
|
|
533
|
+
source?: {
|
|
534
|
+
x: number;
|
|
535
|
+
y: number;
|
|
536
|
+
w: number;
|
|
537
|
+
h: number;
|
|
538
|
+
};
|
|
539
|
+
/** Mirror the sampled region within the destination rect. The quad does not
|
|
540
|
+
* move — a flipped draw covers exactly the pixels an unflipped one does. */
|
|
541
|
+
flipX?: boolean;
|
|
542
|
+
flipY?: boolean;
|
|
543
|
+
}
|
|
544
|
+
/** Floats per sprite in `SpritesDrawCommand.sprites`. */
|
|
545
|
+
declare const SPRITE_STRIDE = 9;
|
|
546
|
+
/**
|
|
547
|
+
* Draw many quads sampling one bitmap — an atlas, a sprite sheet, a wall of
|
|
548
|
+
* thumbnails. The same picture as a run of `ImageDrawCommand`s the renderer
|
|
549
|
+
* would coalesce anyway, handed over already packed so it never walks a
|
|
550
|
+
* command object per quad.
|
|
551
|
+
*
|
|
552
|
+
* Reach for it past a few thousand sprites. Below that a plain run of image
|
|
553
|
+
* commands merges into the same single draw and reads better; the packed form
|
|
554
|
+
* exists because at 20,000 the object walk is about half the frame.
|
|
555
|
+
*
|
|
556
|
+
* The sprites are one run: they share a texture, a filter, and whatever group
|
|
557
|
+
* transform, alpha, color matrix and clip are live, exactly as a merged run of
|
|
558
|
+
* image commands would. Anything varying per sprite is in the array.
|
|
559
|
+
*/
|
|
560
|
+
interface SpritesDrawCommand {
|
|
561
|
+
kind: 'sprites';
|
|
562
|
+
image: ImageBitmap;
|
|
563
|
+
/** Magnification filter for the whole run, as `ImageDrawCommand.sampling`. */
|
|
564
|
+
sampling?: 'linear' | 'nearest';
|
|
565
|
+
/**
|
|
566
|
+
* `SPRITE_STRIDE` floats per sprite:
|
|
567
|
+
* `dx, dy, dw, dh, sx, sy, sw, sh, opacity`.
|
|
568
|
+
*
|
|
569
|
+
* Destination is in the group's coordinates; source is in bitmap pixels,
|
|
570
|
+
* like `ImageDrawCommand.source`. A negative `sw` or `sh` mirrors that axis
|
|
571
|
+
* within the source rect, which is what `flipX` / `flipY` do. A trailing
|
|
572
|
+
* partial sprite is ignored.
|
|
573
|
+
*/
|
|
574
|
+
sprites: Float32Array;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Custom shader draw command. The renderer generates a quad over `bounds`
|
|
578
|
+
* and dispatches the consumer's fragment shader with the kit's vertex prelude.
|
|
579
|
+
*
|
|
580
|
+
* `uniforms` keys must match names declared in the consumer's fragment shader.
|
|
581
|
+
* The kit automatically sets `u_bounds`, `u_view`, and `u_proj` — do not
|
|
582
|
+
* declare those in `uniforms`.
|
|
583
|
+
*
|
|
584
|
+
* @experimental API may change before v2.
|
|
585
|
+
*/
|
|
586
|
+
interface ShaderDrawCommand {
|
|
587
|
+
kind: 'shader';
|
|
588
|
+
program: ShaderProgramHandle;
|
|
589
|
+
uniforms: Record<string, ShaderUniform>;
|
|
590
|
+
/** Screen-space bounding rect in CSS pixels. */
|
|
591
|
+
bounds: {
|
|
592
|
+
x: number;
|
|
593
|
+
y: number;
|
|
594
|
+
w: number;
|
|
595
|
+
h: number;
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/** A run of top-level commands, `[start, end)` by index, that
|
|
600
|
+
* {@link WeaselRenderer.lastFrameStats} accounts for separately — one render
|
|
601
|
+
* layer's commands, say. Spans are in order and do not overlap. */
|
|
602
|
+
interface RenderSpan {
|
|
603
|
+
id: string;
|
|
604
|
+
start: number;
|
|
605
|
+
end: number;
|
|
606
|
+
}
|
|
607
|
+
/** Per-frame options for {@link WeaselRenderer.render}. */
|
|
608
|
+
interface RenderOptions {
|
|
609
|
+
spans?: readonly RenderSpan[];
|
|
610
|
+
}
|
|
611
|
+
/** What one span cost. A batched run is drawn when something closes it, so
|
|
612
|
+
* its draw call counts toward the span that closed it. */
|
|
613
|
+
interface RenderSpanStats {
|
|
614
|
+
id: string;
|
|
615
|
+
drawCalls: number;
|
|
616
|
+
ms: number;
|
|
617
|
+
}
|
|
618
|
+
/** What one `render()` cost. See {@link WeaselRenderer.lastFrameStats}. */
|
|
619
|
+
interface RenderStats {
|
|
620
|
+
drawCalls: number;
|
|
621
|
+
ms: number;
|
|
622
|
+
spans: RenderSpanStats[];
|
|
623
|
+
}
|
|
624
|
+
/** Construction options for {@link WeaselRenderer}. Pass `gl` or `canvas`;
|
|
625
|
+
* a context passed in must have a stencil buffer. */
|
|
626
|
+
interface WeaselRendererOptions {
|
|
627
|
+
/** A WebGL2 context to draw with. Wins over `canvas`. */
|
|
628
|
+
gl?: WebGL2RenderingContext;
|
|
629
|
+
/** A canvas to open a stencil-backed WebGL2 context on, when `gl` is absent. */
|
|
630
|
+
canvas?: HTMLCanvasElement;
|
|
631
|
+
/** Drawing size in CSS pixels. */
|
|
632
|
+
width: number;
|
|
633
|
+
/** Drawing size in CSS pixels. */
|
|
634
|
+
height: number;
|
|
635
|
+
/** Device pixels per CSS pixel. */
|
|
636
|
+
dpr: number;
|
|
637
|
+
/** MIN_FILTER strategy for image/pattern textures (`GLImageCache`).
|
|
638
|
+
* Default `'linear'` — the existing screen behavior. The headless
|
|
639
|
+
* `renderSceneToPixels` path passes `'mipmap'` for print-quality
|
|
640
|
+
* minification. Explicitly passing `'linear'` is always valid. */
|
|
641
|
+
imageMinification?: ImageMinification;
|
|
642
|
+
/** Flatness tolerance for curve tessellation, in WORLD units (see
|
|
643
|
+
* `TessellateOptions.flattenTolerance`). When set, path fills are
|
|
644
|
+
* tessellated fresh at this tolerance per frame (transient pool) instead
|
|
645
|
+
* of served from the Path-identity mesh cache — the cache key does not
|
|
646
|
+
* include tolerance. Default: unset — the existing cached behavior at
|
|
647
|
+
* `DEFAULT_FLATTEN_TOLERANCE`. The headless path derives this from the
|
|
648
|
+
* requested output scale; screen callers normally leave it unset. */
|
|
649
|
+
flattenTolerance?: number;
|
|
650
|
+
/** Per-render synchronous bake budget for dynamic canvas-SDF glyphs.
|
|
651
|
+
* Default DEFAULT_BAKE_BUDGET (16). The headless renderSceneToPixels
|
|
652
|
+
* path passes Infinity so print never defers a glyph. */
|
|
653
|
+
bakeBudget?: number;
|
|
654
|
+
/** On-screen glyph size, in CSS pixels, at or above which text renders from
|
|
655
|
+
* tessellated font outlines instead of a distance field — see
|
|
656
|
+
* `OUTLINE_MIN_SCREEN_PX`. Only faces registered with
|
|
657
|
+
* `registerFontOutlines` are affected; everything else keeps its SDF tier
|
|
658
|
+
* whatever this says. Pass `Infinity` to disable the tier outright, or 0
|
|
659
|
+
* to use outlines wherever they exist (what the headless path does, since
|
|
660
|
+
* print has no reason to sample a field it could evaluate exactly). */
|
|
661
|
+
textOutlineMinScreenSize?: number;
|
|
662
|
+
}
|
|
663
|
+
/** Where a renderer draws inside a buffer it does not own.
|
|
664
|
+
*
|
|
665
|
+
* The rect's SIZE is the renderer's own `width`/`height` — `resize()` owns
|
|
666
|
+
* that, and a second copy here could disagree with the one `DrawContext`
|
|
667
|
+
* reports to screen-space layers. */
|
|
668
|
+
interface RenderTarget {
|
|
669
|
+
/** Top-left of this renderer's output within the drawing buffer, in CSS
|
|
670
|
+
* pixels, with the origin at the buffer's top-left. GL's bottom-left origin
|
|
671
|
+
* is handled internally. */
|
|
672
|
+
origin: {
|
|
673
|
+
x: number;
|
|
674
|
+
y: number;
|
|
675
|
+
};
|
|
676
|
+
/** Clear colour and stencil within the rect before drawing. Default true.
|
|
677
|
+
* Pass false only when the caller clears the whole buffer itself on behalf
|
|
678
|
+
* of every co-tenant — a frame that clears neither inherits its neighbour's
|
|
679
|
+
* stencil bits, and even-odd fills then fill their holes. */
|
|
680
|
+
clear?: boolean;
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* The WebGL2 renderer: takes a list of draw commands and paints them.
|
|
684
|
+
*
|
|
685
|
+
* It knows nothing about the scene — commands are the whole interface, which
|
|
686
|
+
* is what lets layers, HUD widgets and overlays all draw through the same
|
|
687
|
+
* pipeline. GPU resources (meshes, textures, gradient ramps) are cached across
|
|
688
|
+
* frames and keyed by identity, so re-issuing the same command is cheap.
|
|
689
|
+
*/
|
|
690
|
+
declare class WeaselRenderer {
|
|
691
|
+
private readonly gl;
|
|
692
|
+
private stats;
|
|
693
|
+
private pathFill;
|
|
694
|
+
private pathFillVColor;
|
|
695
|
+
private batchFill;
|
|
696
|
+
private gradFill;
|
|
697
|
+
private patternFill;
|
|
698
|
+
/** Compiled on first use: only atlas text under a texture paint needs one. */
|
|
699
|
+
private glyphPaintPrograms;
|
|
700
|
+
private meshCache;
|
|
701
|
+
private textureCache;
|
|
702
|
+
private imageCache;
|
|
703
|
+
private gradRamps;
|
|
704
|
+
private programRegistry;
|
|
705
|
+
/** The source each compiled custom program was built from — or, after a
|
|
706
|
+
* failed recompile, the source that failed, so it is not retried every frame. */
|
|
707
|
+
private programSources;
|
|
708
|
+
private quadVbo;
|
|
709
|
+
private quadIbo;
|
|
710
|
+
private drawBatch;
|
|
711
|
+
/** 1x1 white, so a batch flush sampling no image still samples something —
|
|
712
|
+
* see `shaders/batchFill.ts`. */
|
|
713
|
+
private whiteTexture;
|
|
714
|
+
private readonly groupState;
|
|
715
|
+
private widthCss;
|
|
716
|
+
private heightCss;
|
|
717
|
+
private dpr;
|
|
718
|
+
private canvas;
|
|
719
|
+
/** Where context loss is announced: the context's own canvas, which the
|
|
720
|
+
* renderer need not own the size of. */
|
|
721
|
+
private lossTarget;
|
|
722
|
+
private target;
|
|
723
|
+
/** Offscreen buffers for group effects. Allocates nothing until a group
|
|
724
|
+
* with effects asks, so a canvas without them pays no memory. */
|
|
725
|
+
private readonly effectTargets;
|
|
726
|
+
private readonly paintScratch;
|
|
727
|
+
private readonly imageMinification;
|
|
728
|
+
private flattenTolerance?;
|
|
729
|
+
private readonly bakeBudget;
|
|
730
|
+
private readonly textOutlineMinScreenSize;
|
|
731
|
+
private contextLost;
|
|
732
|
+
private boundOnLost;
|
|
733
|
+
private boundOnRestored;
|
|
734
|
+
/** True after `dispose()`. A disposed renderer ignores further `render()`
|
|
735
|
+
* calls and `registerProgram()` calls. */
|
|
736
|
+
private disposed;
|
|
737
|
+
/** What registered paint kinds hang their GPU state on. One per context
|
|
738
|
+
* generation: a restore releases it and opens the next. */
|
|
739
|
+
private paintResources;
|
|
740
|
+
constructor(opts: WeaselRendererOptions);
|
|
741
|
+
private uploadQuadGeometry;
|
|
742
|
+
/**
|
|
743
|
+
* Compile a consumer-registered shader program against this renderer's GL context.
|
|
744
|
+
*
|
|
745
|
+
* Call once per renderer after the module-level `registerProgram()`.
|
|
746
|
+
* Throws `ShaderCompileError` if compilation fails.
|
|
747
|
+
*
|
|
748
|
+
* In dev mode, calling again with the same handle replaces the compiled program.
|
|
749
|
+
*
|
|
750
|
+
* @experimental
|
|
751
|
+
*/
|
|
752
|
+
registerProgram(handle: ShaderProgramHandle): void;
|
|
753
|
+
private installProgram;
|
|
754
|
+
/** Recompile every program whose module-level source was re-registered
|
|
755
|
+
* since it was compiled. A source that fails keeps the old program drawing. */
|
|
756
|
+
private refreshPrograms;
|
|
757
|
+
/**
|
|
758
|
+
* The compiled program for `id`, compiling it against this context on first
|
|
759
|
+
* use. Unlike {@link registerProgram} this neither throws on a missing
|
|
760
|
+
* source nor on a compile failure: a paint kind asking for a program it
|
|
761
|
+
* never registered must decline the paint, not take down the frame.
|
|
762
|
+
*/
|
|
763
|
+
private ensureProgram;
|
|
764
|
+
private glyphPaintProgram;
|
|
765
|
+
/** The blend, depth, cull, clear-color, color-mask, VAO, and unpack-flip
|
|
766
|
+
* state every frame assumes.
|
|
767
|
+
* Applied per `render()` rather than once at construction because a
|
|
768
|
+
* co-tenant sharing this context moves all of it between our frames. */
|
|
769
|
+
private applyGlState;
|
|
770
|
+
/** Viewport and scissor for this frame. Re-applied inside `render()` because
|
|
771
|
+
* a co-tenant on the same context moves both between our frames. */
|
|
772
|
+
/** This renderer's rect in device pixels, top-left origin. A tile rounds its
|
|
773
|
+
* edges rather than its size, so two that share a CSS edge share a device one. */
|
|
774
|
+
private deviceRect;
|
|
775
|
+
private applyTarget;
|
|
776
|
+
/** Confine this renderer to a rect of its drawing buffer, or pass null to
|
|
777
|
+
* give it the whole buffer back. Takes effect from the next `render()`. */
|
|
778
|
+
setTarget(target: RenderTarget | null): void;
|
|
779
|
+
getTarget(): RenderTarget | null;
|
|
780
|
+
isContextLost(): boolean;
|
|
781
|
+
private onContextLost;
|
|
782
|
+
private onContextRestored;
|
|
783
|
+
/** Free every GL resource this renderer created and detach its
|
|
784
|
+
* context-loss listeners. Idempotent.
|
|
785
|
+
*
|
|
786
|
+
* That covers the built-in and consumer-registered programs (their shaders
|
|
787
|
+
* go with them), the shared quad geometry, the batch, the effect targets,
|
|
788
|
+
* and every cache: atlas and gradient-ramp textures, bitmap and pattern
|
|
789
|
+
* textures, and meshes — transient, persistent, and those whose Path was
|
|
790
|
+
* already collected. The context itself is left alone, so a caller-owned
|
|
791
|
+
* context can host renderer after renderer without accumulating anything.
|
|
792
|
+
*
|
|
793
|
+
* A disposed renderer ignores further `render()` and `registerProgram()`
|
|
794
|
+
* calls. */
|
|
795
|
+
dispose(): void;
|
|
796
|
+
/**
|
|
797
|
+
* Draw one frame.
|
|
798
|
+
*
|
|
799
|
+
* `viewMatrix` is the frame's world→screen transform. It is only consulted
|
|
800
|
+
* by `units: 'world'` gradients, which fall back to screen space without
|
|
801
|
+
* it — every other command carries its own transform in the stream, so
|
|
802
|
+
* callers with no view concept can keep calling `render(commands)`.
|
|
803
|
+
*/
|
|
804
|
+
render(commands: DrawCommand[], viewMatrix?: GlMat3, opts?: RenderOptions): void;
|
|
805
|
+
/** What the last `render()` cost: GL draw calls issued and CPU milliseconds
|
|
806
|
+
* spent issuing them, in total and per requested span. GPU time is not
|
|
807
|
+
* measured; the GPU runs asynchronously. */
|
|
808
|
+
lastFrameStats(): RenderStats;
|
|
809
|
+
/** Change the curve-flattening tolerance after construction. `undefined`
|
|
810
|
+
* restores the cached `DEFAULT_FLATTEN_TOLERANCE` route. */
|
|
811
|
+
setFlattenTolerance(tolerance: number | undefined): void;
|
|
812
|
+
resize(dims: {
|
|
813
|
+
width: number;
|
|
814
|
+
height: number;
|
|
815
|
+
dpr: number;
|
|
816
|
+
}): void;
|
|
817
|
+
/** @internal */ _gl(): WebGL2RenderingContext;
|
|
818
|
+
/** @internal */ _pathFill(): ShaderProgram;
|
|
819
|
+
/** @internal */ _pathFillVColor(): ShaderProgram;
|
|
820
|
+
/** @internal */ _drawBatch(): DrawBatch;
|
|
821
|
+
/** @internal */ _batchFill(): ShaderProgram;
|
|
822
|
+
/** @internal */ _gradFill(): ShaderProgram;
|
|
823
|
+
/** @internal */ _patternFill(): ShaderProgram;
|
|
824
|
+
/** @internal */ _meshCache(): GLMeshCache;
|
|
825
|
+
/** @internal */ _textureCache(): GLTextureCache;
|
|
826
|
+
/** @internal */ _imageCache(): GLImageCache;
|
|
827
|
+
/** @internal */ _gradRamps(): GradientRampAtlas;
|
|
828
|
+
/** @internal */ _groupState(): GroupState;
|
|
829
|
+
/** @internal */ _widthCss(): number;
|
|
830
|
+
/** @internal */ _heightCss(): number;
|
|
831
|
+
/** @internal */ _dpr(): number;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/**
|
|
835
|
+
* Uniform-grid sprite sheet layout — frame index to the source rect an
|
|
836
|
+
* `ImageDrawCommand` samples. Pure arithmetic over the grid description; it
|
|
837
|
+
* never touches the bitmap.
|
|
838
|
+
*/
|
|
839
|
+
/** A sprite sheet's grid. `margin` and `spacing` follow the Tiled / Aseprite
|
|
840
|
+
* tileset convention, so a sheet exported from either describes itself here. */
|
|
841
|
+
interface SpriteSheet {
|
|
842
|
+
frameWidth: number;
|
|
843
|
+
frameHeight: number;
|
|
844
|
+
columns: number;
|
|
845
|
+
/** Empty pixels around the whole grid. Default 0. */
|
|
846
|
+
margin?: number;
|
|
847
|
+
/** Empty pixels between adjacent cells — the gutter that keeps linear
|
|
848
|
+
* sampling off the neighboring frame. Default 0. */
|
|
849
|
+
spacing?: number;
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Row-major source rect for frame `index`, counting from 0 at the top-left.
|
|
853
|
+
*
|
|
854
|
+
* `index` is not range-checked and does not wrap: past the last cell this
|
|
855
|
+
* returns a rect below the sheet, which draws as an edge smear. Wrapping is
|
|
856
|
+
* the animation's business (`frameRect(sheet, tick % count)`) — a sheet does
|
|
857
|
+
* not know how many of its cells are filled.
|
|
858
|
+
*/
|
|
859
|
+
declare function frameRect(sheet: SpriteSheet, index: number): {
|
|
860
|
+
x: number;
|
|
861
|
+
y: number;
|
|
862
|
+
w: number;
|
|
863
|
+
h: number;
|
|
864
|
+
};
|
|
865
|
+
|
|
866
|
+
/** A screen-space rectangle, CSS pixels. */
|
|
867
|
+
interface CullRect {
|
|
868
|
+
x: number;
|
|
869
|
+
y: number;
|
|
870
|
+
width: number;
|
|
871
|
+
height: number;
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* `cmds` without the commands that cannot put a pixel inside `rect`, where
|
|
875
|
+
* `transform` maps the commands' space to screen space — `viewToMat3(view)`
|
|
876
|
+
* for a world-space layer.
|
|
877
|
+
*
|
|
878
|
+
* Conservative by construction: a command is dropped only when a bound that
|
|
879
|
+
* encloses everything it can paint misses `rect`. Paths are bounded by their
|
|
880
|
+
* control hull plus the farthest a stroke can reach (miter spikes and square
|
|
881
|
+
* caps included), images and sprites by their quads, and every bound is taken
|
|
882
|
+
* through the accumulated transform as the AABB of its corners, so rotation
|
|
883
|
+
* only enlarges it. Text and shader commands are always kept, and so is a
|
|
884
|
+
* group with `effects` — an effect may move pixels. Clips are ignored, which
|
|
885
|
+
* can only keep more. A group whose children all go is dropped.
|
|
886
|
+
*
|
|
887
|
+
* Returns `cmds` itself when nothing was dropped.
|
|
888
|
+
*/
|
|
889
|
+
declare function cullDrawCommands(cmds: DrawCommand[], transform: GlMat3, rect: CullRect): DrawCommand[];
|
|
890
|
+
/**
|
|
891
|
+
* {@link strokeReach} at a known world-to-screen scale — what a painter
|
|
892
|
+
* bounding its own output before it paints can know. `least` is the scale
|
|
893
|
+
* along the axis the transform shrinks most, where it differs from `scale`.
|
|
894
|
+
*
|
|
895
|
+
* A `{ px }` ribbon or head is built to its screen size in every direction,
|
|
896
|
+
* so in world it reaches farthest along that axis. A head with a world size is
|
|
897
|
+
* built in world, whatever the line's width is in.
|
|
898
|
+
*/
|
|
899
|
+
declare function strokeReachAt(stroke: Stroke, scale: number, least?: number): number;
|
|
900
|
+
/**
|
|
901
|
+
* True when `bounds`, taken through `transform` to screen space, cannot put a
|
|
902
|
+
* pixel inside `rect` — the per-command test above, for a caller holding a box
|
|
903
|
+
* rather than a command. Same slack, same NaN-keeps rule.
|
|
904
|
+
*/
|
|
905
|
+
declare function boundsMissRect(bounds: {
|
|
906
|
+
x: number;
|
|
907
|
+
y: number;
|
|
908
|
+
width: number;
|
|
909
|
+
height: number;
|
|
910
|
+
}, transform: GlMat3, rect: CullRect): boolean;
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* On-screen glyph size (CSS px) at or above which a face with registered
|
|
914
|
+
* outlines is drawn as geometry instead of sampled from a distance field.
|
|
915
|
+
*
|
|
916
|
+
* 48 is the dynamic tier's own `BAKE_SIZE`, which makes it the size at which
|
|
917
|
+
* that tier stops being an interpolation and starts being a magnification:
|
|
918
|
+
* `glyphRasterizer.ts` measures coverage error bottoming out *at* the bake
|
|
919
|
+
* size and rising on both sides, and past a few times it the reconstructed
|
|
920
|
+
* field shows the raster as contour wobble. The same number is a safe
|
|
921
|
+
* crossing for the baked MSDF atlas, which is still crisp here — outlines are
|
|
922
|
+
* exact, so switching early costs nothing but the tessellation, and the
|
|
923
|
+
* hinting the atlas has and outlines don't stopped mattering well below this.
|
|
924
|
+
*/
|
|
925
|
+
declare const OUTLINE_MIN_SCREEN_PX = 48;
|
|
926
|
+
|
|
927
|
+
/**
|
|
928
|
+
* Effects that ship with the kit.
|
|
929
|
+
*
|
|
930
|
+
* Each is a function returning `Effect[]`, not a single `Effect`, because a
|
|
931
|
+
* separable kernel is genuinely two passes and hiding that behind one entry
|
|
932
|
+
* would make the cost invisible at the callsite:
|
|
933
|
+
*
|
|
934
|
+
* effects={[...blur({ radius: 4 }), ...vignette({ amount: 0.6 })]}
|
|
935
|
+
*/
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* Gaussian blur, as a horizontal pass followed by a vertical one.
|
|
939
|
+
*
|
|
940
|
+
* `radius` is in device pixels and is the distance of the outermost tap, not a
|
|
941
|
+
* standard deviation — 0 is a copy, and the falloff is the same nine-tap
|
|
942
|
+
* kernel at every radius, so a large one is a wide blur rather than a better
|
|
943
|
+
* one. Two passes at O(9) beat one at O(81) and look the same.
|
|
944
|
+
*/
|
|
945
|
+
declare function blur({ radius }: {
|
|
946
|
+
radius: number;
|
|
947
|
+
}): Effect[];
|
|
948
|
+
/**
|
|
949
|
+
* Darken toward the corners. `amount` is how dark the corner gets (0..1) and
|
|
950
|
+
* `feather` how much of the radius the falloff occupies.
|
|
951
|
+
*/
|
|
952
|
+
declare function vignette({ amount, feather }: {
|
|
953
|
+
amount: number;
|
|
954
|
+
feather?: number;
|
|
955
|
+
}): Effect[];
|
|
956
|
+
|
|
957
|
+
/**
|
|
958
|
+
* Kit-supplied vertex shader for a full-screen effect pass.
|
|
959
|
+
*
|
|
960
|
+
* Distinct from `CUSTOM_VERT_SRC` on purpose. That one places its quad through
|
|
961
|
+
* `u_bounds` and `u_proj`, so its `v_uv` runs top-down in *screen* space; an
|
|
962
|
+
* effect samples a texture the scene was just rendered into, whose v axis runs
|
|
963
|
+
* the other way. Passing `a_position` straight to `gl_Position` and `a_uv`
|
|
964
|
+
* straight out keeps the two aligned — `QUAD_VERTICES` already carries
|
|
965
|
+
* `a_uv == (a_position + 1) / 2` on both axes.
|
|
966
|
+
*
|
|
967
|
+
* PUBLIC CONTRACT — what every effect fragment shader may read:
|
|
968
|
+
*
|
|
969
|
+
* in vec2 v_uv 0..1 across the source texture
|
|
970
|
+
* uniform sampler2D u_source what has been drawn so far
|
|
971
|
+
* uniform vec2 u_resolution source size in device pixels
|
|
972
|
+
* uniform vec2 u_texel 1.0 / u_resolution — one texel step
|
|
973
|
+
*
|
|
974
|
+
* PREMULTIPLIED ALPHA: `u_source` holds premultiplied RGBA, because that is
|
|
975
|
+
* what the scene blends into it. An effect that weights neighbouring samples
|
|
976
|
+
* (a blur) is doing the right thing on premultiplied values; one that
|
|
977
|
+
* manipulates rgb against a must premultiply its own output the same way the
|
|
978
|
+
* custom-shader prelude requires.
|
|
979
|
+
*/
|
|
980
|
+
declare const EFFECT_VERT_SRC = "#version 300 es\nin vec2 a_position;\nin vec2 a_uv;\nout vec2 v_uv;\n\nvoid main() {\n v_uv = a_uv;\n gl_Position = vec4(a_position, 0.0, 1.0);\n}\n";
|
|
981
|
+
/** Uniforms the renderer sets on every effect pass. An effect must not
|
|
982
|
+
* redeclare them with different meanings; it may leave any of them out. */
|
|
983
|
+
declare const EFFECT_KIT_UNIFORMS: readonly ["u_source", "u_resolution", "u_texel"];
|
|
984
|
+
|
|
985
|
+
/**
|
|
986
|
+
* A node's paint, resolved into what a draw command takes. One place composes
|
|
987
|
+
* the two resolutions so every painter and layer applies both.
|
|
988
|
+
*/
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* `fill` as a draw command's fill: `'bounds'` units mapped onto `box`, and a
|
|
992
|
+
* tile spec swapped for its texture. `null` when a spec cannot rasterize.
|
|
993
|
+
*/
|
|
994
|
+
declare function commandFill(fill: FillStyle, box: FillPoseBox): CommandFill | null;
|
|
995
|
+
/**
|
|
996
|
+
* `stroke` as a draw command's stroke, its paint resolved as
|
|
997
|
+
* {@link commandFill} resolves a fill. `null` when the stroke carries no
|
|
998
|
+
* paint, or its paint cannot be resolved.
|
|
999
|
+
*/
|
|
1000
|
+
declare function commandStroke(stroke: Stroke, box: FillPoseBox): CommandStroke | null;
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* Tile-pattern primitive — a small helper for building repeating texture
|
|
1004
|
+
* tiles from a draw callback. The result plugs into a `FillStyle` of
|
|
1005
|
+
* `{ fill: 'pattern', pattern }` (see `@weasel-js/paint`).
|
|
1006
|
+
*
|
|
1007
|
+
* GL-backed: the tile is rendered to an `OffscreenCanvas` (with a 2D fallback
|
|
1008
|
+
* to a regular `<canvas>` for environments without `OffscreenCanvas`),
|
|
1009
|
+
* converted to an `ImageBitmap`, and registered with `registerTexture()`.
|
|
1010
|
+
* The returned `TextureHandle` is what `FillStyle.pattern` accepts.
|
|
1011
|
+
*
|
|
1012
|
+
* For ready-to-use named patterns (hatch, crosshatch, dots, chunks),
|
|
1013
|
+
* see the `@weasel-js/renderer/patterns-builtin` subpath.
|
|
1014
|
+
*/
|
|
1015
|
+
|
|
1016
|
+
/** Options for `createTilePattern`. */
|
|
1017
|
+
interface TilePatternOpts {
|
|
1018
|
+
/** Edge length (in pixels) of the square offscreen tile. */
|
|
1019
|
+
size: number;
|
|
1020
|
+
/** FillStyle one tile at integer-pixel coordinates `(0, 0) .. (size, size)`. */
|
|
1021
|
+
draw: (oc: CanvasRenderingContext2D, size: number) => void;
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* Render a single tile to an offscreen canvas and register it as a GL
|
|
1025
|
+
* texture, returning a `TextureHandle` you can wrap in a `FillStyle` of
|
|
1026
|
+
* `{ fill: 'pattern', pattern }`. Returns `null` only when an
|
|
1027
|
+
* `OffscreenCanvas`/2D context can't be acquired (very rare; same fallback
|
|
1028
|
+
* semantics as the prior `CanvasPattern`-based implementation).
|
|
1029
|
+
*/
|
|
1030
|
+
declare function createTilePattern(opts: TilePatternOpts): TextureHandle | null;
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* Build a `TextDrawCommand`. `textCommandFromPose` is the command for a text
|
|
1034
|
+
* node — what `kit:text` and `createTextLayer` both emit. `textCommand` takes
|
|
1035
|
+
* a plain string and wraps it in one run; `textCommandFromRuns` takes the
|
|
1036
|
+
* styled runs directly, for text that has no pose. All three
|
|
1037
|
+
* resolve through `resolveTextStyle` + `resolveRuns`, so every emitter of a
|
|
1038
|
+
* text command derives `align` and per-run resolution the same way — a
|
|
1039
|
+
* caller that assembles the command by hand is one field away from a
|
|
1040
|
+
* silently different result.
|
|
1041
|
+
*
|
|
1042
|
+
* `paint` is the node's `data.fill` / `data.stroke`. A caller with no node —
|
|
1043
|
+
* a HUD widget, a debug overlay — states its color on the run instead.
|
|
1044
|
+
*
|
|
1045
|
+
* `scale` is the view scale a `{ px }` `fontSize` or `letterSpacing` resolves
|
|
1046
|
+
* against — for a world-space layer, the mean of `view.scale.x` and
|
|
1047
|
+
* `view.scale.y`. It has to be applied here and not at draw time, because a
|
|
1048
|
+
* screen-pixel size moves the glyph advances and so the wrap points.
|
|
1049
|
+
*/
|
|
1050
|
+
|
|
1051
|
+
/** Build a draw command from styled runs, for text with no pose. `style`
|
|
1052
|
+
* supplies the defaults each run inherits; the rest match `textCommand`. */
|
|
1053
|
+
declare function textCommandFromRuns(x: number, y: number, runs: readonly StyledRun[], style?: TextStyle, maxWidth?: number, height?: number, verticalAlign?: TextVerticalAlign, paint?: TextPaint, width?: number, scale?: number): DrawCommand;
|
|
1054
|
+
/** Build a draw command for a single unstyled string. Text is laid out with
|
|
1055
|
+
* the registered font's metrics, wrapped at `maxWidth`, aligned across
|
|
1056
|
+
* `width` (default `maxWidth`) and within `height` when `verticalAlign` is
|
|
1057
|
+
* given. */
|
|
1058
|
+
declare function textCommand(x: number, y: number, text: string, style?: TextStyle, maxWidth?: number, height?: number, verticalAlign?: TextVerticalAlign, paint?: TextPaint, width?: number, scale?: number): DrawCommand;
|
|
1059
|
+
/**
|
|
1060
|
+
* The draw command for a text pose. Its wrap width, alignment box and runs
|
|
1061
|
+
* come from `textPoseLayoutInput`, so the renderer lays it out into exactly
|
|
1062
|
+
* the lines `layoutTextPose` reports for the same pose.
|
|
1063
|
+
*
|
|
1064
|
+
* Each run's paints are resolved against the pose box the way every other
|
|
1065
|
+
* node's are — a `'bounds'` paint mapped onto it, a pattern spec swapped for
|
|
1066
|
+
* its texture — since the renderer can do neither.
|
|
1067
|
+
*/
|
|
1068
|
+
declare function textCommandFromPose(pose: TextPose, scale?: number): TextDrawCommand;
|
|
1069
|
+
|
|
1070
|
+
export { type CullRect, type DrawCommand, EFFECT_KIT_UNIFORMS, EFFECT_VERT_SRC, type Effect, FillPoseBox, type GroupDrawCommand, IDENTITY_COLOR_MATRIX, type ImageDrawCommand, type ImageMinification, OUTLINE_MIN_SCREEN_PX, type PathDrawCommand, type RenderOptions, type RenderSpan, type RenderSpanStats, type RenderStats, type RenderTarget, SPRITE_STRIDE, type ShaderDrawCommand, ShaderProgram, ShaderProgramHandle, ShaderUniform, type SolidPaint, type SpriteSheet, type SpritesDrawCommand, type TextDrawCommand, type TilePatternOpts, WeaselRenderer, type WeaselRendererOptions, blur, boundsMissRect, buildGradientRamp, commandFill, commandStroke, createTilePattern, cullDrawCommands, frameRect, registerEffect, releaseImageSource, strokeReachAt, textCommand, textCommandFromPose, textCommandFromRuns, vignette };
|