@weasel-js/renderer 0.0.0-stage → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +61 -2
- package/dist/chunk-43YL7HZV.js +11 -0
- package/dist/chunk-43YL7HZV.js.map +1 -0
- package/dist/chunk-LFD47OV2.js +1487 -0
- package/dist/chunk-LFD47OV2.js.map +1 -0
- package/dist/chunk-Q4SIHV7Q.js +178 -0
- package/dist/chunk-Q4SIHV7Q.js.map +1 -0
- package/dist/imageCache-DYcWftY8.d.ts +329 -0
- package/dist/index.d.ts +1062 -0
- package/dist/index.js +4268 -0
- package/dist/index.js.map +1 -0
- package/dist/mesh.d.ts +241 -0
- package/dist/mesh.js +603 -0
- package/dist/mesh.js.map +1 -0
- package/dist/paintKinds-BmPAT51k.d.ts +314 -0
- package/dist/patterns-builtin.d.ts +110 -0
- package/dist/patterns-builtin.js +28 -0
- package/dist/patterns-builtin.js.map +1 -0
- package/dist/test-seams.d.ts +79 -0
- package/dist/test-seams.js +4 -0
- package/dist/test-seams.js.map +1 -0
- package/dist/testing/glRecorder.d.ts +33 -0
- package/dist/testing/glRecorder.js +158 -0
- package/dist/testing/glRecorder.js.map +1 -0
- package/dist/testing/vertexUploads.d.ts +28 -0
- package/dist/testing/vertexUploads.js +48 -0
- package/dist/testing/vertexUploads.js.map +1 -0
- package/package.json +71 -4
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { TextureHandle, PosedPaint, CommandPaint, Paint, TilePatternSpec } from '@weasel-js/paint';
|
|
2
|
+
import { Reflection } from '@weasel-js/registry';
|
|
3
|
+
import { Mesh } from '@weasel-js/geom/tessellate';
|
|
4
|
+
import { GlMat3 } from '@weasel-js/geom';
|
|
5
|
+
import { b as ShaderProgram } from './paintKinds-BmPAT51k.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A module-level registry of image sources by opaque id. It stores image data
|
|
9
|
+
* only — no per-renderer state; each renderer uploads to GL lazily at draw
|
|
10
|
+
* time. A texture lives until `unregisterTexture`.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** A registered texture's image source, uploaded to GL lazily at first draw. */
|
|
14
|
+
interface TextureEntry {
|
|
15
|
+
source: HTMLImageElement | ImageBitmap;
|
|
16
|
+
}
|
|
17
|
+
/** Every registered texture, keyed by its handle id. */
|
|
18
|
+
declare const textureRegistry: Reflection<TextureEntry>;
|
|
19
|
+
/** @internal Test helper — do not call from product code. */
|
|
20
|
+
declare function _resetTextureRegistryForTests(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Register an image for use as a shader texture uniform. Returns a
|
|
23
|
+
* TextureHandle whose `id` can be passed as a ShaderUniform value.
|
|
24
|
+
*
|
|
25
|
+
* The texture holds premultiplied texels whichever source it came from, so a
|
|
26
|
+
* shader sampling one reads `vec4(rgb * a, a)`, the form its own output takes.
|
|
27
|
+
*
|
|
28
|
+
* @param image HTMLImageElement must be fully loaded. An ImageBitmap must
|
|
29
|
+
* hold premultiplied pixels, the `createImageBitmap` default.
|
|
30
|
+
*/
|
|
31
|
+
declare function registerTexture(image: HTMLImageElement | ImageBitmap): TextureHandle;
|
|
32
|
+
/**
|
|
33
|
+
* Drop a registered texture. Every renderer that uploaded it frees its GL
|
|
34
|
+
* copy at the top of its next frame; a draw command still holding the handle
|
|
35
|
+
* then draws nothing and warns. The image itself is the caller's to release.
|
|
36
|
+
*
|
|
37
|
+
* @returns Whether the handle was registered.
|
|
38
|
+
*/
|
|
39
|
+
declare function unregisterTexture(handle: TextureHandle): boolean;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Clipping staged geometry to an axis-aligned rect on the CPU, so a rect clip
|
|
43
|
+
* needs no stencil and does not break the run.
|
|
44
|
+
*
|
|
45
|
+
* A stencil clip is GL state, so content inside one cannot share a draw with
|
|
46
|
+
* content outside it. A rect under a transform that keeps it axis-aligned
|
|
47
|
+
* is the common clip, and for that one the clip can ride the geometry instead:
|
|
48
|
+
* cut each staged polygon to the rect before it joins the run.
|
|
49
|
+
*
|
|
50
|
+
* Exact, not approximate: every attribute a batch vertex carries is affine
|
|
51
|
+
* across the polygon it belongs to, so interpolating along a cut edge gives
|
|
52
|
+
* the value the rasterizer would have produced there, and the cut polygon
|
|
53
|
+
* covers the same pixel centers the stencil would have passed.
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/** In the space batch vertices are staged in: after the model transform,
|
|
57
|
+
* before the projection. */
|
|
58
|
+
interface ClipRect {
|
|
59
|
+
x0: number;
|
|
60
|
+
y0: number;
|
|
61
|
+
x1: number;
|
|
62
|
+
y1: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Growable vertex staging for a run of solid-fill geometry, image quads and
|
|
67
|
+
* glyphs. Geometry only: the caller owns when a run starts, what breaks it, and
|
|
68
|
+
* the uniforms the flush draws under.
|
|
69
|
+
*
|
|
70
|
+
* One batch for all of them, because a page interleaves them. Everything a
|
|
71
|
+
* vertex needs to say which it is fits in the same vertex: solids carry the UV
|
|
72
|
+
* of a 1x1 white texel and are their own color, quads carry their atlas UV and
|
|
73
|
+
* a white color, glyphs carry a font atlas UV, their text color, and a paint
|
|
74
|
+
* mode saying the texel is a distance field rather than a color. Gradients
|
|
75
|
+
* sample the ramp atlas. See `shaders/batchFill.ts`.
|
|
76
|
+
*
|
|
77
|
+
* The model transform is applied here rather than uploaded, so shapes under
|
|
78
|
+
* different transforms still share a draw. The texture rides the vertices as a
|
|
79
|
+
* slot index into the units the flush binds: slot 0 is the white texel every
|
|
80
|
+
* solid samples.
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
/** vec2 a_position + vec4 a_vertexColor + vec2 a_uv + float a_post + float
|
|
84
|
+
* a_texSlot. */
|
|
85
|
+
declare const FLOATS_PER_VERTEX = 10;
|
|
86
|
+
/**
|
|
87
|
+
* A gradient vertex's `a_uv`, each channel affine in the coordinates the
|
|
88
|
+
* geometry arrives in: `u = ux*x + uy*y + u0`, and the same for `v`.
|
|
89
|
+
*
|
|
90
|
+
* A linear gradient's `v` row is the constant atlas row and its `u` row is the
|
|
91
|
+
* ramp position; a radial or conic one's two rows are the gradient-space
|
|
92
|
+
* coordinate, and the atlas row rides `a_post` instead.
|
|
93
|
+
*/
|
|
94
|
+
interface GradientUV {
|
|
95
|
+
ux: number;
|
|
96
|
+
uy: number;
|
|
97
|
+
u0: number;
|
|
98
|
+
vx: number;
|
|
99
|
+
vy: number;
|
|
100
|
+
v0: number;
|
|
101
|
+
}
|
|
102
|
+
declare class DrawBatch {
|
|
103
|
+
private readonly gl;
|
|
104
|
+
private readonly aPos;
|
|
105
|
+
private readonly aColor;
|
|
106
|
+
private readonly aUv;
|
|
107
|
+
private readonly aPost;
|
|
108
|
+
private readonly aSlot;
|
|
109
|
+
/** One ring per tier, cycled per flush. Slots are created on first use, so a
|
|
110
|
+
* renderer that flushes rarely allocates as few as it flushes. */
|
|
111
|
+
private readonly rings;
|
|
112
|
+
private readonly nextInRing;
|
|
113
|
+
/** The same, for flushes past the largest tier; these sets grow to fit. */
|
|
114
|
+
private readonly largeRing;
|
|
115
|
+
private nextLarge;
|
|
116
|
+
private verts;
|
|
117
|
+
private idx;
|
|
118
|
+
private nVerts;
|
|
119
|
+
private nIdx;
|
|
120
|
+
/** Whether the staged run is quads alone, so its indices are the canonical
|
|
121
|
+
* pattern and a slot already holding that pattern needs no upload. */
|
|
122
|
+
private pureRects;
|
|
123
|
+
/** The rect every push is cut to, or `null`. A push returns `false`, having
|
|
124
|
+
* staged nothing, for geometry it cannot cut exactly; see `clipQuad`. */
|
|
125
|
+
clip: ClipRect | null;
|
|
126
|
+
/** Polygon scratch for `clip`: a quad cut by four edges has at most eight
|
|
127
|
+
* corners. */
|
|
128
|
+
private readonly polyIn;
|
|
129
|
+
private readonly polyOut;
|
|
130
|
+
private readonly polyTmp;
|
|
131
|
+
constructor(gl: WebGL2RenderingContext, prog: ShaderProgram);
|
|
132
|
+
get length(): number;
|
|
133
|
+
/** Whether staging `vertices` more would put the run past the per-flush cap. */
|
|
134
|
+
wouldOverflow(vertices: number): boolean;
|
|
135
|
+
/**
|
|
136
|
+
* Append one rect's four corners through `m`, all carrying `rgba` (straight
|
|
137
|
+
* alpha).
|
|
138
|
+
*/
|
|
139
|
+
pushRect(x: number, y: number, w: number, h: number, m: GlMat3, r: number, g: number, b: number, a: number): boolean;
|
|
140
|
+
/**
|
|
141
|
+
* Append one image quad: the destination rect `(x, y, w, h)` mapped through
|
|
142
|
+
* `m`, sampling `(u0, v0)`-`(u1, v1)`, every corner carrying `post` as the
|
|
143
|
+
* after-the-color-matrix alpha factor and `rgb` as the scale on its texel.
|
|
144
|
+
* `slot` is the texture unit the corners sample.
|
|
145
|
+
*
|
|
146
|
+
* Corners wind top-left, top-right, bottom-right, bottom-left — the same
|
|
147
|
+
* winding `pushRect` uses, which is what lets a run of mixed rects and quads
|
|
148
|
+
* keep the canonical index pattern.
|
|
149
|
+
*/
|
|
150
|
+
pushQuad(x: number, y: number, w: number, h: number, m: GlMat3, u0: number, v0: number, u1: number, v1: number, post: number, slot: number, r?: number, g?: number, b?: number): boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Append one glyph quad: the box `(x0, y0)`-`(x1, y1)` mapped through `m`,
|
|
153
|
+
* sampling `(u0, v0)`-`(u1, v1)` of the font atlas at `slot`, painted in
|
|
154
|
+
* `rgba`.
|
|
155
|
+
*
|
|
156
|
+
* `mode` says which channels of that atlas carry the distance field, and
|
|
157
|
+
* rides the vertices packed into the slot. The synthetic-bold threshold does
|
|
158
|
+
* not: it is `u_synthBold`, and the caller breaks the run when it changes.
|
|
159
|
+
*
|
|
160
|
+
* A synthetic oblique is sheared here, before `m`; `tanItalic` is 0 for an
|
|
161
|
+
* upright face. Corners wind as `pushQuad`'s do.
|
|
162
|
+
*/
|
|
163
|
+
pushGlyph(x0: number, y0: number, x1: number, y1: number, baselineY: number, tanItalic: number, m: GlMat3, u0: number, v0: number, u1: number, v1: number, r: number, g: number, b: number, a: number, slot: number, mode: number): boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Append one rect filled by a gradient: the corners of `(x, y, w, h)` mapped
|
|
166
|
+
* through `m`, sampling the ramp atlas at `slot`.
|
|
167
|
+
*
|
|
168
|
+
* `uv` gives each channel of `a_uv` as an affine function of the coordinates
|
|
169
|
+
* the corners arrive in. `mode` says whether that is a ramp position or a
|
|
170
|
+
* gradient-space coordinate, and `post` carries the atlas row for the modes
|
|
171
|
+
* that read it — see `shaders/batchFill.ts`. Values outside 0..1 clamp to
|
|
172
|
+
* the atlas's edge texel.
|
|
173
|
+
*/
|
|
174
|
+
pushGradientRect(x: number, y: number, w: number, h: number, m: GlMat3, uv: GradientUV, post: number, slot: number, mode: number, r: number, g: number, b: number, a: number): boolean;
|
|
175
|
+
/** `pushMesh` for a mesh filled by a gradient — see `pushGradientRect` for
|
|
176
|
+
* what `uv`, `post` and `mode` carry. */
|
|
177
|
+
pushGradientMesh(mesh: Mesh, m: GlMat3, uv: GradientUV, post: number, slot: number, mode: number, r: number, g: number, b: number, a: number): boolean;
|
|
178
|
+
/**
|
|
179
|
+
* Append a tessellated mesh through `m`, all vertices carrying `rgba`. The
|
|
180
|
+
* mesh's own indices are rebased onto the staged vertices, which is why the
|
|
181
|
+
* index buffer is uploaded per flush rather than written once.
|
|
182
|
+
*/
|
|
183
|
+
pushMesh(mesh: Mesh, m: GlMat3, r: number, g: number, b: number, a: number): boolean;
|
|
184
|
+
/** Upload the staged geometry into the next set of buffers and bind its VAO.
|
|
185
|
+
* Returns the index count for the caller's `drawElements`. */
|
|
186
|
+
uploadAndBind(): number;
|
|
187
|
+
reset(): void;
|
|
188
|
+
dispose(): void;
|
|
189
|
+
private writeVertex;
|
|
190
|
+
/**
|
|
191
|
+
* Fit the quad just written at `v0` to `rect`: keep it if it lies inside,
|
|
192
|
+
* drop it if it misses, and cut it if it crosses — but only if its edges
|
|
193
|
+
* run along the axes. Cutting a slanted edge moves its endpoint off the
|
|
194
|
+
* subpixel grid the rasterizer snaps to, and the shortened edge then covers
|
|
195
|
+
* different samples than the whole one did; along an axis the cut edge is
|
|
196
|
+
* the same line exactly. So a slanted quad that crosses is rolled back and
|
|
197
|
+
* refused, for the caller to clip through the stencil instead.
|
|
198
|
+
*/
|
|
199
|
+
private clipQuad;
|
|
200
|
+
/** `clipQuad` for a mesh staged from `v0`. A mesh's edges are slanted in
|
|
201
|
+
* general, so one that crosses is refused rather than cut. */
|
|
202
|
+
private clipMesh;
|
|
203
|
+
/** Append the `n`-corner convex polygon in `poly` as a triangle fan. */
|
|
204
|
+
private appendFan;
|
|
205
|
+
/** Two triangles over the four corners just written. */
|
|
206
|
+
private pushQuadIndices;
|
|
207
|
+
/** Grow the CPU arrays so `vertices` / `indices` more fit. */
|
|
208
|
+
private reserve;
|
|
209
|
+
private nextRingSlot;
|
|
210
|
+
private nextLargeSlot;
|
|
211
|
+
private createSet;
|
|
212
|
+
private deleteSet;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* `TilePatternSpec` → `TextureHandle`. The bridge between a tile paint and the
|
|
217
|
+
* GL texture that paints it.
|
|
218
|
+
*
|
|
219
|
+
* Memoized on the spec's field values, so identical specs across nodes share
|
|
220
|
+
* one texture and a re-render never re-rasterizes. The memo keeps the
|
|
221
|
+
* `PATTERN_TILE_CACHE_LIMIT` tiles most recently resolved or drawn and
|
|
222
|
+
* unregisters the rest. A handle outliving its tile still draws: its id is
|
|
223
|
+
* the spec, and the texture registry asks this module to rebuild it.
|
|
224
|
+
*/
|
|
225
|
+
|
|
226
|
+
/** @internal Test helper — do not call from product code. */
|
|
227
|
+
declare function _resetPatternSpecCacheForTests(): void;
|
|
228
|
+
/**
|
|
229
|
+
* Build (or fetch) the texture for a spec. Returns `null` when the tile
|
|
230
|
+
* can't be rasterized — no `OffscreenCanvas`, same as `createTilePattern`.
|
|
231
|
+
*/
|
|
232
|
+
declare function resolvePatternSpec(spec: TilePatternSpec): TextureHandle | null;
|
|
233
|
+
/**
|
|
234
|
+
* Swap a tile paint for the `texture` paint that draws it, placement kept,
|
|
235
|
+
* leaving every other fill untouched. The built-in painters run this
|
|
236
|
+
* alongside `fillInPoseFrame`; a consumer emitting its own draw commands
|
|
237
|
+
* runs it at the same place it runs that one.
|
|
238
|
+
*
|
|
239
|
+
* A tile that can't rasterize drops the fill rather than painting garbage.
|
|
240
|
+
*/
|
|
241
|
+
declare function resolveFillPattern(fill: PosedPaint): CommandPaint | null;
|
|
242
|
+
declare function resolveFillPattern(fill: Paint): Paint | null;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* The kit's per-`src` image loader + cache. Shape painters are synchronous
|
|
246
|
+
* and decoding an image is async, so `getImageBitmap` is a synchronous read
|
|
247
|
+
* that returns the decoded `ImageBitmap` when ready, and otherwise returns
|
|
248
|
+
* `undefined` and lazily kicks off a de-duped async load. When a load
|
|
249
|
+
* resolves (or errors), every `subscribeImageReady` listener fires.
|
|
250
|
+
*
|
|
251
|
+
* `src` is any browser-loadable image string: a remote URL, a `blob:` URL, or
|
|
252
|
+
* a `data:image/…;base64,…` URI. The decoded bitmap lives only here, keyed by
|
|
253
|
+
* `src` — never in node `data`.
|
|
254
|
+
*/
|
|
255
|
+
/** Public shape of an image node's `data`. `src` round-trips through
|
|
256
|
+
* serialization; the decoded bitmap is held in this cache, not on the node. */
|
|
257
|
+
interface ImageNodeData {
|
|
258
|
+
image: {
|
|
259
|
+
src: string;
|
|
260
|
+
opacity?: number;
|
|
261
|
+
/** The part of the bitmap drawn into the pose rect, as fractions of the
|
|
262
|
+
* bitmap's width and height from its top-left. Omitted draws the whole
|
|
263
|
+
* bitmap. Fractions, so the data means the same thing before the bitmap
|
|
264
|
+
* has decoded and after `src` is swapped for another resolution. */
|
|
265
|
+
source?: {
|
|
266
|
+
x: number;
|
|
267
|
+
y: number;
|
|
268
|
+
width: number;
|
|
269
|
+
height: number;
|
|
270
|
+
};
|
|
271
|
+
/** Mirror the drawn region within the pose rect. The rect does not move. */
|
|
272
|
+
flipX?: boolean;
|
|
273
|
+
flipY?: boolean;
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
/** Where an image is in its load: never requested, in flight, decoded, or
|
|
277
|
+
* failed. Renderers paint a placeholder for anything but `'ready'`. */
|
|
278
|
+
type ImageStatus = 'idle' | 'loading' | 'ready' | 'error';
|
|
279
|
+
/** A size in device pixels. */
|
|
280
|
+
interface ImageRasterSize {
|
|
281
|
+
width: number;
|
|
282
|
+
height: number;
|
|
283
|
+
}
|
|
284
|
+
/** `size` is absent for the first load, which decodes at natural size. */
|
|
285
|
+
type Loader = (src: string, size?: ImageRasterSize) => Promise<ImageBitmap>;
|
|
286
|
+
/** True when `src` names SVG — a `data:image/svg+xml` URI, or a URL whose path
|
|
287
|
+
* ends in `.svg`/`.svgz`. Such sources re-rasterize at the size they are drawn
|
|
288
|
+
* at; everything else decodes once. A `blob:` URL carries no type and counts
|
|
289
|
+
* as raster. */
|
|
290
|
+
declare function isVectorImageSrc(src: string): boolean;
|
|
291
|
+
/**
|
|
292
|
+
* Set how many bytes of decoded RGBA the cache may hold, and return the
|
|
293
|
+
* previous value. Default 256MB; `Infinity` never drops anything. Takes
|
|
294
|
+
* effect when the next image lands. A dropped image loads again on its next
|
|
295
|
+
* {@link getImageBitmap}, as one never requested does.
|
|
296
|
+
*/
|
|
297
|
+
declare function setImageCacheBudget(bytes: number): number;
|
|
298
|
+
/** Synchronous read. Returns the decoded bitmap when ready; otherwise returns
|
|
299
|
+
* `undefined` and lazily starts a de-duped async load for `src`.
|
|
300
|
+
*
|
|
301
|
+
* `drawnAt` is the size in device pixels the image is about to cover. A
|
|
302
|
+
* vector source ({@link isVectorImageSrc}) uses it to re-rasterize in
|
|
303
|
+
* power-of-two steps — the current raster is returned until the sharper one
|
|
304
|
+
* is ready. Raster sources ignore it. */
|
|
305
|
+
declare function getImageBitmap(src: string, drawnAt?: ImageRasterSize): ImageBitmap | undefined;
|
|
306
|
+
/** Current load status for `src` (`'idle'` if never requested). */
|
|
307
|
+
declare function imageStatus(src: string): ImageStatus;
|
|
308
|
+
/**
|
|
309
|
+
* Forget `src`: drop its decoded bitmap, its decoded SVG, and the texture every
|
|
310
|
+
* live renderer holds for it, and abandon a load in flight. The cache is keyed
|
|
311
|
+
* by `src` across every scene on the page, so it cannot know when the last use
|
|
312
|
+
* is gone; whoever does know calls this. A later {@link getImageBitmap} for the
|
|
313
|
+
* same `src` loads it again.
|
|
314
|
+
*/
|
|
315
|
+
declare function releaseImage(src: string): void;
|
|
316
|
+
/** Load a `src` whose last load failed again. Does nothing for a `src` in any
|
|
317
|
+
* other state; one that failed otherwise stays `'error'` for the page's life. */
|
|
318
|
+
declare function retryImage(src: string): void;
|
|
319
|
+
/** Subscribe to load-resolution events (fires after any `src` resolves or
|
|
320
|
+
* errors). Returns an unsubscribe. */
|
|
321
|
+
declare function subscribeImageReady(cb: () => void): () => void;
|
|
322
|
+
/** @internal test seam — override the async loader so unit tests don't need
|
|
323
|
+
* a real DOM image. Not part of the public barrel. */
|
|
324
|
+
declare function __setImageLoaderForTests(fn: Loader): void;
|
|
325
|
+
/** @internal test seam — clear cache + subscribers and restore the default
|
|
326
|
+
* loader. Not part of the public barrel. */
|
|
327
|
+
declare function _resetImageCacheForTests(): void;
|
|
328
|
+
|
|
329
|
+
export { DrawBatch as D, FLOATS_PER_VERTEX as F, type ImageNodeData as I, type TextureEntry as T, __setImageLoaderForTests as _, type ImageRasterSize as a, type ImageStatus as b, isVectorImageSrc as c, releaseImage as d, resolveFillPattern as e, resolvePatternSpec as f, getImageBitmap as g, retryImage as h, imageStatus as i, subscribeImageReady as j, _resetImageCacheForTests as k, _resetPatternSpecCacheForTests as l, _resetTextureRegistryForTests as m, registerTexture as r, setImageCacheBudget as s, textureRegistry as t, unregisterTexture as u };
|