@weasel-js/core 1.4.3 → 1.5.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +1299 -2197
  2. package/README.md +118 -75
  3. package/dist/{pointSnapToGrid-D1bRdq-j.d.ts → autoPoseDescriptor-CvjflWJK.d.ts} +35 -17
  4. package/dist/{chunk-ORZNKVXM.js → chunk-BDWAA634.js} +4 -22
  5. package/dist/chunk-BDWAA634.js.map +1 -0
  6. package/dist/{chunk-WYPSGIYS.js → chunk-MG7OXCAI.js} +4149 -5316
  7. package/dist/chunk-MG7OXCAI.js.map +1 -0
  8. package/dist/{chunk-BLJNRMKB.js → chunk-MQI4PIX3.js} +3 -3
  9. package/dist/chunk-MQI4PIX3.js.map +1 -0
  10. package/dist/{chunk-SBFC6J3C.js → chunk-UCPV7JXC.js} +201 -256
  11. package/dist/chunk-UCPV7JXC.js.map +1 -0
  12. package/dist/clipboard.d.ts +2 -3
  13. package/dist/clone.d.ts +3 -2
  14. package/dist/depSchema-nMqj_qTM.d.ts +3490 -0
  15. package/dist/{grid-Bw8cSde-.d.ts → grid-BrIa38gG.d.ts} +7 -23
  16. package/dist/index.d.ts +2134 -1210
  17. package/dist/index.js +4 -5
  18. package/dist/insert.d.ts +4 -4
  19. package/dist/insert.js +1 -1
  20. package/dist/move.d.ts +5 -6
  21. package/dist/move.js +3 -6
  22. package/dist/move.js.map +1 -1
  23. package/dist/{options-DYfUlZxk.d.ts → options-BDyCnrp8.d.ts} +3 -2
  24. package/dist/poseDescriptor-CGOgIgf8.d.ts +134 -0
  25. package/dist/renderer.d.ts +10 -4
  26. package/dist/renderer.js +4 -5
  27. package/dist/resize.d.ts +10 -11
  28. package/dist/resize.js +2 -2
  29. package/dist/routing.d.ts +1 -142
  30. package/dist/routing.js +1 -1
  31. package/dist/routing.js.map +1 -1
  32. package/dist/{types-BJqsTlXl.d.ts → types-DMyo7dnM.d.ts} +12 -41
  33. package/dist/{types-CoVTbo_y.d.ts → types-DtjCJA5r.d.ts} +10 -9
  34. package/package.json +13 -10
  35. package/dist/DrawCommand-CD-ug3d9.d.ts +0 -332
  36. package/dist/builtins-CtGORLCG.d.ts +0 -740
  37. package/dist/chunk-BLJNRMKB.js.map +0 -1
  38. package/dist/chunk-LEURURX3.js +0 -561
  39. package/dist/chunk-LEURURX3.js.map +0 -1
  40. package/dist/chunk-ORZNKVXM.js.map +0 -1
  41. package/dist/chunk-SBFC6J3C.js.map +0 -1
  42. package/dist/chunk-WYPSGIYS.js.map +0 -1
  43. package/dist/geometry-CHR36Ub_.d.ts +0 -114
  44. package/dist/path-JEV2c5If.d.ts +0 -48
  45. package/dist/registry-DnoTWGED.d.ts +0 -4003
  46. package/dist/types-BHK2dkMu.d.ts +0 -172
  47. package/dist/types-H7o6MaPo.d.ts +0 -512
  48. package/dist/view-DSQgxBJB.d.ts +0 -63
@@ -1,740 +0,0 @@
1
- import { GradStop, Stroke } from '@weasel-js/paint';
2
- import { M as Mat3, b as ShaderProgramHandle, D as DrawCommand, E as Effect } from './DrawCommand-CD-ug3d9.js';
3
- import { P as Path } from './path-JEV2c5If.js';
4
-
5
- /**
6
- * Minimal compile/link/lookup wrapper for a GL program. Throws
7
- * `ShaderCompileError` on compile or link failure with the GL info log
8
- * embedded in the error message — never returns a half-initialized program.
9
- *
10
- * Designed so test code can stub `getShaderParameter` / `getProgramParameter`
11
- * via the recorder Proxy without further special-casing.
12
- */
13
- type Stage = 'vertex' | 'fragment' | 'link';
14
- /** Thrown when a shader fails to compile or link, carrying which stage failed
15
- * and the driver's log. */
16
- declare class ShaderCompileError extends Error {
17
- readonly stage: Stage;
18
- readonly log: string;
19
- constructor(stage: Stage, log: string);
20
- }
21
- declare class ShaderProgram {
22
- private readonly gl;
23
- readonly handle: WebGLProgram;
24
- private readonly uniforms;
25
- private readonly attributes;
26
- constructor(gl: WebGL2RenderingContext, vertSrc: string, fragSrc: string);
27
- private compile;
28
- lookupUniforms(names: readonly string[]): void;
29
- lookupAttributes(names: readonly string[]): void;
30
- uniform(name: string): WebGLUniformLocation | undefined;
31
- attribute(name: string): number | undefined;
32
- }
33
-
34
- /**
35
- * A tessellated representation of a Path, ready to upload to GL.
36
- *
37
- * - `vertices` is interleaved x,y in path-local coordinates (`Float32Array`
38
- * of length `2 * vertexCount`).
39
- * - `indices` are triangle indices into `vertices` (`Uint32Array`, length
40
- * `3 * triangleCount`).
41
- * - `requiresStencil` is set for paths whose fillRule is `'evenodd'` and
42
- * whose triangulation is a *naive* per-contour fan rather than a clean
43
- * inside/outside triangulation. The renderer must use a stencil
44
- * two-pass when this flag is true. Single-contour paths and `'nonzero'`
45
- * multi-contour paths leave it false.
46
- * - `anchorA` / `anchorB` / `anchorT` parameterize each mesh vertex by
47
- * the two consecutive path anchors it lies between and the arc-length
48
- * fraction along that segment (0 = at A, 1 = at B). Vertices that fall
49
- * exactly on an anchor set A === B and t = 0. Used at draw time when
50
- * the DrawCommand supplies per-anchor colors so the renderer can lerp
51
- * per mesh vertex. Optional — emitted by the path tessellators; absent
52
- * on meshes built by other paths (e.g. text glyphs).
53
- */
54
- interface Mesh {
55
- readonly vertices: Float32Array;
56
- readonly indices: Uint32Array;
57
- readonly requiresStencil?: boolean;
58
- readonly anchorA?: Uint32Array;
59
- readonly anchorB?: Uint32Array;
60
- readonly anchorT?: Float32Array;
61
- }
62
-
63
- interface GLMeshHandle {
64
- readonly vao: WebGLVertexArrayObject;
65
- readonly indexCount: number;
66
- readonly requiresStencil: boolean;
67
- readonly anchorA?: Uint32Array;
68
- readonly anchorB?: Uint32Array;
69
- readonly anchorT?: Float32Array;
70
- }
71
- /** Resources to release when a Mesh is reclaimed by GC. */
72
- interface MeshResources {
73
- vao: WebGLVertexArrayObject;
74
- vbo: WebGLBuffer;
75
- ibo: WebGLBuffer;
76
- }
77
- /**
78
- * Caches GL-side buffers + VAO per `Mesh` identity. Upload happens lazily
79
- * on first `handleFor(mesh)` call.
80
- *
81
- * **GC-aware cleanup (deferred-delete queue):** when a Mesh becomes
82
- * unreachable, the WeakMap entry is dropped automatically — but the
83
- * underlying GL resources (VAO, VBO, IBO) would leak forever without an
84
- * explicit `gl.delete*` call. We register each Mesh with a
85
- * `FinalizationRegistry`; when the Mesh is reclaimed, the finalizer pushes
86
- * the resources onto an internal queue. The renderer drains this queue at
87
- * the start of each `render()` call (via `drainPendingDeletes()`), when GL
88
- * is in a known state — no VAO bound, no draw mid-flight. Deleting from the
89
- * finalizer directly was racy (it could fire between two `gl.bindVertexArray`
90
- * calls inside `dispatch()`, sometimes corrupting the next draw); deferring
91
- * the actual delete to a known-safe point keeps the use-after-free risk to
92
- * zero.
93
- *
94
- * Caveats:
95
- * - Finalizer timing is non-deterministic; resources may live for a frame
96
- * or two beyond their Mesh, but they will be reclaimed.
97
- * - If the GL context is lost between finalizer and drain, the drain
98
- * no-ops safely (`gl.isContextLost()` makes deletes no-ops, and
99
- * `gl.deleteBuffer(null)` is allowed).
100
- *
101
- * The cache is GL-context-bound; if the context is lost and re-created, the
102
- * renderer should construct a new GLMeshCache. (Context loss handling lives
103
- * in `WeaselRenderer`.)
104
- */
105
- declare class GLMeshCache {
106
- private readonly gl;
107
- private readonly aPositionLoc;
108
- private readonly map;
109
- /** Meshes this context has drawn at least once; see `uploadRecurring`. */
110
- private readonly seen;
111
- private readonly finalizer;
112
- private readonly pendingDeletes;
113
- /** Transient resources allocated this frame; freed at end of render(). */
114
- private readonly transientThisFrame;
115
- constructor(gl: WebGL2RenderingContext, aPositionLoc: number);
116
- handleFor(mesh: Mesh): GLMeshHandle;
117
- /**
118
- * Upload a Mesh that the caller knows is single-use (e.g. the per-frame
119
- * stroke ribbon from `tessellateStroke`). Returns a handle backed by fresh
120
- * GL resources that the renderer will free deterministically at the end
121
- * of the current frame via `freeTransient()`. Bypasses the WeakMap cache
122
- * and the FinalizationRegistry, so transient meshes never wait on GC.
123
- */
124
- uploadTransient(mesh: Mesh): GLMeshHandle;
125
- /**
126
- * Upload a Mesh that may or may not recur across frames. Its first sight in
127
- * *this* context takes `uploadTransient`; every later one takes the
128
- * persistent handle. One transient upload to find out is cheaper than
129
- * stranding a persistent VAO on a mesh that never returns, whose release
130
- * would wait on GC. The transient upload does not populate the persistent
131
- * map, so steady-state reuse begins on the third frame.
132
- */
133
- uploadRecurring(mesh: Mesh): GLMeshHandle;
134
- /**
135
- * Free all transient resources allocated since the last call. Called by
136
- * the renderer at the end of each `render()`. Safe under context loss.
137
- */
138
- freeTransient(): void;
139
- /** @internal — for tests asserting the transient-list size. */
140
- _transientCount(): number;
141
- /**
142
- * Free GL resources whose Mesh has been GC'd since the last drain.
143
- * Called by the renderer at the top of each `render()`, before any draws.
144
- */
145
- drainPendingDeletes(): void;
146
- /** @internal — for tests asserting the queue size. */
147
- _pendingDeleteCount(): number;
148
- /** @internal — for tests that need to simulate the finalizer firing. */
149
- _enqueueDeleteForTest(resources: MeshResources): void;
150
- private upload;
151
- }
152
-
153
- /**
154
- * GL texture upload + cache for MSDF font atlases (and future images).
155
- *
156
- * Textures are keyed by a string id (the font family name or image id).
157
- * Pixels are uploaded once per id; a later upload of the same id reuses the
158
- * texture and only re-applies its wrap mode when that changed.
159
- * The cache is GL-context-bound; discard and create a new one on context loss.
160
- *
161
- * Atlas format: RGBA UNSIGNED_BYTE, linear filtering, no mipmaps.
162
- * Mipmap generation is deliberately skipped — MSDF works correctly with
163
- * linear filtering, and mipmap resampling corrupts the multi-channel SDF signal.
164
- */
165
- type TexSource$1 = HTMLImageElement | ImageBitmap | ImageData | HTMLCanvasElement;
166
- /** How a texture samples outside `0..1`. Pattern tiles need `'repeat'`;
167
- * everything else clamps. WebGL2 allows `REPEAT` on NPOT textures, so a
168
- * tile of any size tiles correctly. */
169
- type TextureWrap = 'clamp' | 'repeat';
170
- declare class GLTextureCache {
171
- private readonly gl;
172
- private readonly map;
173
- private readonly wraps;
174
- constructor(gl: WebGL2RenderingContext);
175
- has(id: string): boolean;
176
- /** Uploads the pixels once per id. `wrap` is re-applied whenever it differs
177
- * from what the id was last uploaded under: the same registered image can
178
- * be a clamped shader texture in one draw and a repeating pattern tile in
179
- * the next, and first-upload-wins gave the second one the first one's mode. */
180
- upload(id: string, source: TexSource$1, wrap?: TextureWrap): string;
181
- private applyWrap;
182
- /** Create a single-channel R8 texture from raw bytes (full upload).
183
- * No-op if `id` already exists. Same LINEAR/CLAMP params as `upload`;
184
- * UNPACK_ALIGNMENT dropped to 1 for non-4-aligned row widths. */
185
- uploadR8(id: string, width: number, height: number, data: Uint8Array): void;
186
- /** Patch a rect of an existing R8 texture with tightly-packed w×h bytes. */
187
- subImageR8(id: string, x: number, y: number, w: number, h: number, data: Uint8Array): void;
188
- bind(id: string, unit: number): void;
189
- /**
190
- * Delete every uploaded GL texture and clear the map. Called by
191
- * `WeaselRenderer.dispose()`. The cache is unusable but refillable
192
- * afterward — a subsequent `upload()` for a previously-seen id re-creates
193
- * the texture rather than restoring the deleted one.
194
- */
195
- free(): void;
196
- }
197
-
198
- /**
199
- * GL texture upload cache for ImageBitmap objects.
200
- *
201
- * Key: ImageBitmap (or pattern source object) identity (WeakMap) — lets GC
202
- * reclaim unreferenced bitmaps. The GL textures are NOT freed when the source
203
- * is gc'd; deferred to v2.
204
- *
205
- * Wrapping is set once at upload time per the `repetition` parameter:
206
- * - undefined / 'no-repeat' → CLAMP_TO_EDGE
207
- * - 'repeat' → REPEAT (both axes)
208
- * - 'repeat-x' → REPEAT on S, CLAMP on T
209
- * - 'repeat-y' → CLAMP on S, REPEAT on T
210
- *
211
- * Convention §2: texels stored straight; shader premultiplies.
212
- */
213
- type PatternRepetition = 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat';
214
- /** MIN_FILTER strategy for uploaded textures. */
215
- type ImageMinification = 'linear' | 'mipmap';
216
- type TexSource = ImageBitmap | ImageData | HTMLCanvasElement | HTMLImageElement;
217
- declare class GLImageCache {
218
- private readonly gl;
219
- private readonly minification;
220
- private readonly map;
221
- /** `minification` selects the MIN_FILTER strategy for uploaded textures.
222
- * `'linear'` (default) is the screen path's existing behavior. `'mipmap'`
223
- * generates mipmaps and filters LINEAR_MIPMAP_LINEAR — required for
224
- * quality minification when a large source bitmap is drawn small (the
225
- * headless print/export path); bilinear-only minification undersamples
226
- * and produces moiré. */
227
- constructor(gl: WebGL2RenderingContext, minification?: ImageMinification);
228
- upload(key: object, source: TexSource, repetition?: PatternRepetition): WebGLTexture;
229
- bind(key: object, unit: number): void;
230
- }
231
-
232
- /**
233
- * CPU gradient-ramp builder + GL 1×256 RGBA texture cache.
234
- *
235
- * Each unique stop list is uploaded once. Key = JSON.stringify(stops).
236
- * The cache is GL-context-bound; discard and recreate on context loss.
237
- *
238
- * Output convention §2: texels are stored as straight RGBA. The fragment
239
- * shader applies premultiplication before writing outColor.
240
- */
241
-
242
- /** Bake gradient stops into a 256-entry RGBA lookup strip, which the shader
243
- * samples instead of evaluating stops per fragment. */
244
- declare function buildGradientRamp(stops: GradStop[]): Uint8ClampedArray;
245
- declare class GradientRampCache {
246
- private readonly gl;
247
- private readonly map;
248
- private totalQueries;
249
- private cacheHits;
250
- constructor(gl: WebGL2RenderingContext);
251
- upload(stops: GradStop[]): string;
252
- bind(key: string, unit: number): void;
253
- hitRate(): number;
254
- resetStats(): void;
255
- /**
256
- * Delete every uploaded GL ramp texture and clear the map. Called by
257
- * `WeaselRenderer.dispose()`. Only the GL textures are owned resources —
258
- * `buildGradientRamp`'s CPU-side `Uint8ClampedArray` is transient per
259
- * `upload()` call and already gone by the time a ramp is cached. The
260
- * cache is unusable but refillable afterward (stats are left as-is; call
261
- * `resetStats()` separately if desired).
262
- */
263
- free(): void;
264
- }
265
-
266
- /** Row-major 4×5 color matrix identity. */
267
- declare const IDENTITY_COLOR_MATRIX: Float32Array<ArrayBuffer>;
268
- interface GroupFrame {
269
- transform?: Mat3;
270
- alpha?: number;
271
- /** Row-major 4×5 color matrix (20 floats). Absent leaves the stack unchanged. */
272
- colorMatrix?: Float32Array | number[];
273
- }
274
- declare class GroupState {
275
- private transformStack;
276
- private alphaStack;
277
- private colorMatrixStack;
278
- get transform(): Mat3;
279
- get alpha(): number;
280
- get colorMatrix(): Float32Array;
281
- push(frame: GroupFrame): void;
282
- /**
283
- * Push a frame whose children paint into a surface of their own.
284
- *
285
- * The transform still accumulates — the children draw where they would have
286
- * drawn. Alpha and colour do not: they describe how the finished surface
287
- * joins the frame, and applying them on the way in as well would fade the
288
- * pixels an effect is about to read, then fade them again on the way out.
289
- *
290
- * Returns the pair the caller must apply at composite time — what `push`
291
- * would have left on the stack — so the composition rules live here and not
292
- * in the dispatcher.
293
- */
294
- pushIsolated(frame: GroupFrame): {
295
- alpha: number;
296
- colorMatrix: Float32Array;
297
- };
298
- /** Drop every pushed frame, leaving the root. A frame that throws part-way
299
- * down the tree never pops, and the next frame would draw under leftovers. */
300
- reset(): void;
301
- pop(): void;
302
- }
303
-
304
- /**
305
- * Growable vertex staging for consecutive solid-fill geometry.
306
- *
307
- * Geometry only: `draw.ts` owns when a run starts, what breaks it, and the
308
- * uniforms the flush draws under. Colors ride the vertices (the batch program
309
- * is `pathFillVColor` with `u_color` left at white) because shapes in a run
310
- * differ in color and a merged draw has one set of uniforms — and so does the
311
- * model transform, applied here rather than uploaded, so that shapes under
312
- * different transforms still share a draw.
313
- */
314
-
315
- declare class SolidBatch {
316
- private readonly gl;
317
- private readonly aPos;
318
- private readonly aColor;
319
- /** Cycled per flush, and created on first use so a renderer that flushes
320
- * rarely allocates as few slots as it flushes. */
321
- private readonly ring;
322
- private next;
323
- /** The same, for flushes past a slot's capacity; these sets grow to fit. */
324
- private readonly largeRing;
325
- private nextLarge;
326
- private verts;
327
- private idx;
328
- private nVerts;
329
- private nIdx;
330
- /** Whether the staged run is rects alone, so its indices are the canonical
331
- * quad pattern and a slot already holding that pattern needs no upload. */
332
- private pureRects;
333
- constructor(gl: WebGL2RenderingContext, prog: ShaderProgram);
334
- get length(): number;
335
- /** Whether staging `vertices` more would put the run past the per-flush cap. */
336
- wouldOverflow(vertices: number): boolean;
337
- /**
338
- * Append one rect's four corners through `m`, all carrying `rgba` (straight
339
- * alpha). An affine maps a rect to a parallelogram, so two triangles still
340
- * cover it and the batch draws at `u_model` identity.
341
- */
342
- pushRect(x: number, y: number, w: number, h: number, m: Mat3, r: number, g: number, b: number, a: number): void;
343
- /**
344
- * Append a tessellated mesh through `m`, all vertices carrying `rgba`. The
345
- * mesh's own indices are rebased onto the staged vertices, which is why the
346
- * index buffer is uploaded per flush rather than written once.
347
- */
348
- pushMesh(mesh: Mesh, m: Mat3, r: number, g: number, b: number, a: number): void;
349
- /** Upload the staged geometry into the next set of buffers and bind its VAO.
350
- * Returns the index count for the caller's `drawElements`. */
351
- uploadAndBind(): number;
352
- reset(): void;
353
- dispose(): void;
354
- /** Grow the CPU arrays so `vertices` / `indices` more fit. */
355
- private reserve;
356
- private nextRingSlot;
357
- private nextLargeSlot;
358
- private createSet;
359
- private deleteSet;
360
- }
361
-
362
- /**
363
- * Growable vertex staging for consecutive image quads.
364
- *
365
- * Geometry only: `draw.ts` owns when a run starts, what breaks it, and the
366
- * uniforms the flush draws under. The counterpart to `SolidBatch`, and the same
367
- * two tricks — the model transform is applied here rather than uploaded, so
368
- * quads under different group transforms still share a draw, and opacity rides
369
- * the vertices, so quads under different opacities do too. What it cannot
370
- * absorb is the texture: a batch samples one, which is why an atlas is what
371
- * makes a large run coalesce at all.
372
- *
373
- * Every run is quads, so a slot's index buffer is written once at creation and
374
- * never again: the pattern for N quads is a prefix of the pattern for any
375
- * larger N, so the pattern for a slot's capacity serves every flush it takes.
376
- * `SolidBatch` carries meshes, whose indices are rebased per flush, and has to
377
- * re-upload; this does not.
378
- */
379
-
380
- declare class ImageBatch {
381
- private readonly gl;
382
- private readonly aPos;
383
- private readonly aUv;
384
- private readonly aOpacity;
385
- /** One ring per tier, cycled per flush. Slots are created on first use, so a
386
- * renderer that flushes rarely allocates as few as it flushes. */
387
- private readonly rings;
388
- private readonly nextInRing;
389
- /** For flushes past the largest tier; these sets grow to fit. */
390
- private readonly largeRing;
391
- private nextLarge;
392
- private verts;
393
- private nQuads;
394
- constructor(gl: WebGL2RenderingContext, prog: ShaderProgram);
395
- /** Indices staged — what a caller passes to `drawElements`. */
396
- get length(): number;
397
- get quads(): number;
398
- /** Whether staging one more quad would put the run past the per-flush cap. */
399
- wouldOverflow(): boolean;
400
- /**
401
- * Append one image quad: the destination rect `(x, y, w, h)` mapped through
402
- * `m`, sampling `(u0, v0)`-`(u1, v1)`, every corner carrying `opacity`.
403
- *
404
- * An affine maps a rect to a parallelogram, so two triangles still cover it
405
- * and the batch draws at `u_model` identity. Corners wind top-left,
406
- * top-right, bottom-right, bottom-left, with UVs following — the flips a
407
- * command asks for are already in the `u`/`v` the caller passes.
408
- */
409
- pushQuad(x: number, y: number, w: number, h: number, m: Mat3, u0: number, v0: number, u1: number, v1: number, opacity: number): void;
410
- /** Upload the staged geometry into the next set of buffers and bind its VAO.
411
- * Returns the index count for the caller's `drawElements`. */
412
- uploadAndBind(): number;
413
- reset(): void;
414
- dispose(): void;
415
- /** Grow the CPU arrays so one more quad fits. */
416
- private reserve;
417
- private nextRingSlot;
418
- private nextLargeSlot;
419
- private createSet;
420
- private deleteSet;
421
- }
422
-
423
- /** How to construct a `WeaselRenderer`: the GL context or canvas to draw
424
- * into, the output size, and the quality knobs that separate screen
425
- * rendering from print. */
426
- interface WeaselRendererOptions {
427
- gl?: WebGL2RenderingContext;
428
- canvas?: HTMLCanvasElement;
429
- width: number;
430
- height: number;
431
- dpr: number;
432
- /** MIN_FILTER strategy for image/pattern textures (`GLImageCache`).
433
- * Default `'linear'` — the existing screen behavior. The headless
434
- * `renderSceneToPixels` path passes `'mipmap'` for print-quality
435
- * minification. Explicitly passing `'linear'` is always valid. */
436
- imageMinification?: ImageMinification;
437
- /** Flatness tolerance for curve tessellation, in WORLD units (see
438
- * `TessellateOptions.flattenTolerance`). When set, path fills are
439
- * tessellated fresh at this tolerance per frame (transient pool) instead
440
- * of served from the Path-identity mesh cache — the cache key does not
441
- * include tolerance. Default: unset — the existing cached behavior at
442
- * `DEFAULT_FLATTEN_TOLERANCE`. The headless path derives this from the
443
- * requested output scale; screen callers normally leave it unset. */
444
- flattenTolerance?: number;
445
- /** Per-render synchronous bake budget for dynamic canvas-SDF glyphs.
446
- * Default DEFAULT_BAKE_BUDGET (16). The headless renderSceneToPixels
447
- * path passes Infinity so print never defers a glyph. */
448
- bakeBudget?: number;
449
- /** On-screen glyph size, in CSS pixels, at or above which text renders from
450
- * tessellated font outlines instead of a distance field — see
451
- * `OUTLINE_MIN_SCREEN_PX`. Only faces registered with
452
- * `registerFontOutlines` are affected; everything else keeps its SDF tier
453
- * whatever this says. Pass `Infinity` to disable the tier outright, or 0
454
- * to use outlines wherever they exist (what the headless path does, since
455
- * print has no reason to sample a field it could evaluate exactly). */
456
- textOutlineMinScreenSize?: number;
457
- }
458
- /** Where a renderer draws inside a buffer it does not own.
459
- *
460
- * The rect's SIZE is the renderer's own `width`/`height` — `resize()` owns
461
- * that, and a second copy here could disagree with the one `DrawContext`
462
- * reports to screen-space layers. */
463
- interface RenderTarget {
464
- /** Top-left of this renderer's output within the drawing buffer, in CSS
465
- * pixels, with the origin at the buffer's top-left. GL's bottom-left origin
466
- * is handled internally. */
467
- origin: {
468
- x: number;
469
- y: number;
470
- };
471
- /** Clear colour and stencil within the rect before drawing. Default true.
472
- * Pass false only when the caller clears the whole buffer itself on behalf
473
- * of every co-tenant — a frame that clears neither inherits its neighbour's
474
- * stencil bits, and even-odd fills then fill their holes. */
475
- clear?: boolean;
476
- }
477
- /**
478
- * The WebGL2 renderer: takes a list of draw commands and paints them.
479
- *
480
- * It knows nothing about the scene — commands are the whole interface, which
481
- * is what lets layers, HUD widgets and overlays all draw through the same
482
- * pipeline. GPU resources (meshes, textures, gradient ramps) are cached across
483
- * frames and keyed by identity, so re-issuing the same command is cheap.
484
- */
485
- declare class WeaselRenderer {
486
- private readonly gl;
487
- private pathFill;
488
- private pathFillVColor;
489
- private textSdf;
490
- private textSdfR8;
491
- private imageFill;
492
- private imageFillVOpacity;
493
- private gradFill;
494
- private patternFill;
495
- private meshCache;
496
- private textureCache;
497
- private imageCache;
498
- private gradRampCache;
499
- private programRegistry;
500
- private quadVbo;
501
- private quadIbo;
502
- private solidBatch;
503
- private imageBatch;
504
- private readonly groupState;
505
- private widthCss;
506
- private heightCss;
507
- private dpr;
508
- private canvas;
509
- private target;
510
- /** Offscreen buffers for group effects. Allocates nothing until a group
511
- * with effects asks, so a canvas without them pays no memory. */
512
- private readonly effectTargets;
513
- private readonly imageMinification;
514
- private readonly flattenTolerance?;
515
- private readonly bakeBudget;
516
- private readonly textOutlineMinScreenSize;
517
- private contextLost;
518
- private boundOnLost;
519
- private boundOnRestored;
520
- /** True after `dispose()`. A disposed renderer ignores further `render()`
521
- * calls and `registerProgram()` calls. */
522
- private disposed;
523
- constructor(opts: WeaselRendererOptions);
524
- private uploadQuadGeometry;
525
- /**
526
- * Compile a consumer-registered shader program against this renderer's GL context.
527
- *
528
- * Call once per renderer after the module-level `registerProgram()`.
529
- * Throws `ShaderCompileError` if compilation fails.
530
- *
531
- * In dev mode, calling again with the same handle replaces the compiled program.
532
- *
533
- * @experimental
534
- */
535
- registerProgram(handle: ShaderProgramHandle): void;
536
- /**
537
- * The compiled program for `id`, compiling it against this context on first
538
- * use. Unlike {@link registerProgram} this neither throws on a missing
539
- * source nor on a compile failure: a paint kind asking for a program it
540
- * never registered must decline the paint, not take down the frame.
541
- */
542
- private ensureProgram;
543
- /** The GL state every frame assumes. Applied per `render()` rather than once
544
- * at construction because a co-tenant sharing this context moves all of it
545
- * between our frames. */
546
- private applyGlState;
547
- /** Viewport and scissor for this frame. Re-applied inside `render()` because
548
- * a co-tenant on the same context moves both between our frames. */
549
- private applyTarget;
550
- /** Confine this renderer to a rect of its drawing buffer, or pass null to
551
- * give it the whole buffer back. Takes effect from the next `render()`. */
552
- setTarget(target: RenderTarget | null): void;
553
- getTarget(): RenderTarget | null;
554
- isContextLost(): boolean;
555
- private onContextLost;
556
- private onContextRestored;
557
- /** Free the GL resources this renderer itself owns and detach context-loss
558
- * listeners when a canvas was supplied. Idempotent.
559
- *
560
- * Scope: built-in shader programs, any consumer-registered programs, the
561
- * shared quad/rect geometry, any in-flight transient meshes, and the
562
- * enumerable Map-keyed caches (`GLTextureCache` atlas/image textures,
563
- * `GradientRampCache` ramp textures) ARE freed.
564
- *
565
- * NOT freed: `GLImageCache` (bitmap/pattern textures) and `GLMeshCache`'s
566
- * persistent per-Path mesh cache are keyed by `WeakMap`, not enumerable,
567
- * and are only reclaimed when the GL context itself goes away (cf.
568
- * `GLImageCache`'s own "deferred to v2" note). On a caller-owned
569
- * long-lived context — the headless render-to-pixels path hands the same
570
- * `gl` to many short-lived renderers — the image/pattern textures and
571
- * persistent Path meshes each renderer uploads DO accumulate across
572
- * renderer instances until the caller recycles the context.
573
- *
574
- * Also: a Mesh that gets GC'd after `dispose()` still lands in
575
- * `GLMeshCache`'s `pendingDeletes` queue via its `FinalizationRegistry`,
576
- * but nothing drains that queue post-dispose (only `render()` does) —
577
- * those GL resources leak until the context goes away too.
578
- *
579
- * A disposed renderer ignores further `render()` and `registerProgram()`
580
- * calls. */
581
- dispose(): void;
582
- /**
583
- * Draw one frame.
584
- *
585
- * `viewMatrix` is the frame's world→screen transform. It is only consulted
586
- * by `units: 'world'` gradients, which fall back to screen space without
587
- * it — every other command carries its own transform in the stream, so
588
- * callers with no view concept can keep calling `render(commands)`.
589
- */
590
- render(commands: DrawCommand[], viewMatrix?: Mat3): void;
591
- resize(dims: {
592
- width: number;
593
- height: number;
594
- dpr: number;
595
- }): void;
596
- /** @internal */ _gl(): WebGL2RenderingContext;
597
- /** @internal */ _pathFill(): ShaderProgram;
598
- /** @internal */ _pathFillVColor(): ShaderProgram;
599
- /** @internal */ _solidBatch(): SolidBatch;
600
- /** @internal */ _imageBatch(): ImageBatch;
601
- /** @internal */ _textSdf(): ShaderProgram;
602
- /** @internal */ _textSdfR8(): ShaderProgram;
603
- /** @internal */ _imageFill(): ShaderProgram;
604
- /** @internal */ _imageFillVOpacity(): ShaderProgram;
605
- /** @internal */ _gradFill(): ShaderProgram;
606
- /** @internal */ _patternFill(): ShaderProgram;
607
- /** @internal */ _meshCache(): GLMeshCache;
608
- /** @internal */ _textureCache(): GLTextureCache;
609
- /** @internal */ _imageCache(): GLImageCache;
610
- /** @internal */ _gradRampCache(): GradientRampCache;
611
- /** @internal */ _groupState(): GroupState;
612
- /** @internal */ _widthCss(): number;
613
- /** @internal */ _heightCss(): number;
614
- /** @internal */ _dpr(): number;
615
- }
616
-
617
- /**
618
- * Uniform-grid sprite sheet layout — frame index to the source rect an
619
- * `ImageDrawCommand` samples. Pure arithmetic over the grid description; it
620
- * never touches the bitmap.
621
- */
622
- /** A sprite sheet's grid. `margin` and `spacing` follow the Tiled / Aseprite
623
- * tileset convention, so a sheet exported from either describes itself here. */
624
- interface SpriteSheet {
625
- frameWidth: number;
626
- frameHeight: number;
627
- columns: number;
628
- /** Empty pixels around the whole grid. Default 0. */
629
- margin?: number;
630
- /** Empty pixels between adjacent cells — the gutter that keeps linear
631
- * sampling off the neighboring frame. Default 0. */
632
- spacing?: number;
633
- }
634
- /**
635
- * Row-major source rect for frame `index`, counting from 0 at the top-left.
636
- *
637
- * `index` is not range-checked and does not wrap: past the last cell this
638
- * returns a rect below the sheet, which draws as an edge smear. Wrapping is
639
- * the animation's business (`frameRect(sheet, tick % count)`) — a sheet does
640
- * not know how many of its cells are filled.
641
- */
642
- declare function frameRect(sheet: SpriteSheet, index: number): {
643
- x: number;
644
- y: number;
645
- w: number;
646
- h: number;
647
- };
648
-
649
- /**
650
- * View → Mat3 helper for layer `draw` implementations on world-space layers.
651
- *
652
- * The kit's main package ships a `View` type at
653
- * `src/core/viewport/view.ts`; we re-declare a structurally compatible
654
- * shape here to avoid a runtime cross-package import. Exported as
655
- * `ViewLike` from the package barrel.
656
- */
657
-
658
- /** A weasel View — `{x, y, scale: {x, y}}`. Local type to avoid a cross-package import. */
659
- interface View {
660
- x: number;
661
- y: number;
662
- scale: {
663
- x: number;
664
- y: number;
665
- };
666
- }
667
- /**
668
- * Build the world→screen transform matrix for a `View`.
669
- * Use as the `transform` field of a `kind: 'group'` DrawCommand to wrap
670
- * world-space content emitted from a layer `draw` implementation.
671
- *
672
- * Mapping: `screen = (world − {view.x, view.y}) × {view.scale.x, view.scale.y}`.
673
- *
674
- * Column-major layout (matches `mat3.identity()`):
675
- * `[scale.x, 0, 0, 0, scale.y, 0, -view.x*scale.x, -view.y*scale.y, 1]`.
676
- */
677
- declare function viewToMat3(view: View): Mat3;
678
-
679
- /** Options for stroke tessellation. */
680
- interface StrokeOptions {
681
- flattenTolerance?: number;
682
- /**
683
- * Shorten each open subpath by this much at its start / end, in the same
684
- * world units as the path. Stroke markers use this so a filled head is not
685
- * speared by its own line; the caller resolves the distance, because this
686
- * layer knows nothing about the marker registry.
687
- */
688
- startInset?: number;
689
- endInset?: number;
690
- }
691
- /** Resolve a stroke width to world units. A number is already world units;
692
- * `{ px }` is screen pixels divided by the accumulated scale, so it holds its
693
- * on-screen thickness as the view zooms. */
694
- declare function resolveStrokeWidth(width: number | {
695
- px: number;
696
- }, scale: number): number;
697
- /**
698
- * Build a triangle-mesh ribbon from a stroked Path.
699
- *
700
- * Supports:
701
- * - cap: 'butt' | 'round' | 'square'
702
- * - join: 'miter' | 'round' | 'bevel'
703
- * - center alignment directly; RectPath inner/outer alignment via a
704
- * pre-shifted rect; PolygonPath inner/outer alignment via stencil at the
705
- * renderer level
706
- * - dash splitting
707
- */
708
- declare function tessellateStroke(path: Path, stroke: Stroke, opts?: StrokeOptions): Mesh;
709
-
710
- /**
711
- * Effects that ship with the kit.
712
- *
713
- * Each is a function returning `Effect[]`, not a single `Effect`, because a
714
- * separable kernel is genuinely two passes and hiding that behind one entry
715
- * would make the cost invisible at the callsite:
716
- *
717
- * effects={[...blur({ radius: 4 }), ...vignette({ amount: 0.6 })]}
718
- */
719
-
720
- /**
721
- * Gaussian blur, as a horizontal pass followed by a vertical one.
722
- *
723
- * `radius` is in device pixels and is the distance of the outermost tap, not a
724
- * standard deviation — 0 is a copy, and the falloff is the same nine-tap
725
- * kernel at every radius, so a large one is a wide blur rather than a better
726
- * one. Two passes at O(9) beat one at O(81) and look the same.
727
- */
728
- declare function blur({ radius }: {
729
- radius: number;
730
- }): Effect[];
731
- /**
732
- * Darken toward the corners. `amount` is how dark the corner gets (0..1) and
733
- * `feather` how much of the radius the falloff occupies.
734
- */
735
- declare function vignette({ amount, feather }: {
736
- amount: number;
737
- feather?: number;
738
- }): Effect[];
739
-
740
- export { IDENTITY_COLOR_MATRIX as I, type Mesh as M, type RenderTarget as R, ShaderCompileError as S, type View as V, WeaselRenderer as W, type ImageMinification as a, type SpriteSheet as b, type StrokeOptions as c, type WeaselRendererOptions as d, blur as e, buildGradientRamp as f, frameRect as g, vignette as h, ShaderProgram as i, resolveStrokeWidth as r, tessellateStroke as t, viewToMat3 as v };