@godot-scene-web/canvas 0.1.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/dist/glyph-pass-DfQlp_IH.d.ts +882 -0
- package/dist/glyph-pass-DfQlp_IH.d.ts.map +1 -0
- package/dist/glyph-pass-hbgpu.d.ts +227 -0
- package/dist/glyph-pass-hbgpu.d.ts.map +1 -0
- package/dist/glyph-pass-hbgpu.js +387 -0
- package/dist/glyph-pass-hbgpu.js.map +1 -0
- package/dist/index.d.ts +1085 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4763 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,882 @@
|
|
|
1
|
+
import { ColorMatrix } from "@godot-scene-web/core";
|
|
2
|
+
|
|
3
|
+
//#region src/damage.d.ts
|
|
4
|
+
/** A top-left-origin, axis-aligned rectangle. It is used in design or backing
|
|
5
|
+
* pixel space; the caller decides which, and {@link transformDamageRect} moves
|
|
6
|
+
* between them conservatively. */
|
|
7
|
+
interface DamageRect {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
}
|
|
13
|
+
/** Public retained-rendering vocabulary for a rectangle that needs repainting. */
|
|
14
|
+
type DirtyRect = DamageRect;
|
|
15
|
+
/** A conservative rectangle reported by one draw command when its extent is
|
|
16
|
+
* known. `commandDamageBounds` returns `null` for a full/direct fallback. */
|
|
17
|
+
type CommandBounds = DamageRect;
|
|
18
|
+
/** A tile selected by {@link DamageTiles}. */
|
|
19
|
+
interface DamageTile extends DamageRect {
|
|
20
|
+
column: number;
|
|
21
|
+
row: number;
|
|
22
|
+
}
|
|
23
|
+
/** A tile accumulator for retaining a backing surface without retaining an
|
|
24
|
+
* unbounded list of tiny invalidations. */
|
|
25
|
+
interface DamageTiles {
|
|
26
|
+
readonly width: number;
|
|
27
|
+
readonly height: number;
|
|
28
|
+
/** Physical backing pixels in one tile. Retained callers use 64×64 tiles. */
|
|
29
|
+
readonly tileWidth: number;
|
|
30
|
+
readonly tileHeight: number;
|
|
31
|
+
readonly columns: number;
|
|
32
|
+
readonly rows: number;
|
|
33
|
+
readonly tileCount: number;
|
|
34
|
+
readonly dirtyCount: number;
|
|
35
|
+
/** `dirtyCount / tileCount`, or zero for an empty surface. */
|
|
36
|
+
readonly coverage: number;
|
|
37
|
+
readonly dirty: boolean;
|
|
38
|
+
resize(width: number, height: number): void;
|
|
39
|
+
mark(rect: DamageRect | null): void;
|
|
40
|
+
clear(): void;
|
|
41
|
+
/** The exact backing-pixel tiles currently marked, in paint order. */
|
|
42
|
+
tiles(): readonly DamageTile[];
|
|
43
|
+
/**
|
|
44
|
+
* Visit marked tiles without materialising `DamageTile` objects. The same
|
|
45
|
+
* mutable tile view is passed each time, so read it synchronously and do not
|
|
46
|
+
* retain it. This is the retained-frame hot path; `tiles()` is only a
|
|
47
|
+
* diagnostics convenience.
|
|
48
|
+
*/
|
|
49
|
+
forEach(callback: (tile: DamageTile) => void): void;
|
|
50
|
+
/**
|
|
51
|
+
* Visit an exact, non-overlapping rectangular cover of the marked tiles.
|
|
52
|
+
* Horizontally adjacent tiles are joined, then identical row runs are joined
|
|
53
|
+
* vertically. No clean pixel is included, so each region is safe to pass to
|
|
54
|
+
* a scissored retained replay independently. As with {@link forEach}, the
|
|
55
|
+
* rectangle is a reusable mutable view and must not be retained.
|
|
56
|
+
*/
|
|
57
|
+
forEachRegion(callback: (region: DamageRect) => void): void;
|
|
58
|
+
/** Return the marked tiles and clear the accumulator. */
|
|
59
|
+
consume(): readonly DamageTile[];
|
|
60
|
+
}
|
|
61
|
+
/** Retained replay uses fixed physical-pixel tiles, never CSS pixels. */
|
|
62
|
+
declare const RETAINED_DAMAGE_TILE_SIZE = 64;
|
|
63
|
+
/** The largest tile coverage that is worth planning for retained replay. */
|
|
64
|
+
declare const RETAINED_MAX_DAMAGE_COVERAGE = 0.2;
|
|
65
|
+
/** A `Transform2D` mapping the rect's coordinates into another top-left space. */
|
|
66
|
+
type DamageTransform = ArrayLike<number>;
|
|
67
|
+
declare function createDamageRect(): DamageRect;
|
|
68
|
+
declare function isDamageEmpty(rect: DamageRect): boolean;
|
|
69
|
+
/** Expand in the rectangle's own coordinate space without mutating inputs. */
|
|
70
|
+
declare function outsetDamageRect(rect: DamageRect, outset: number, out?: DamageRect): DamageRect | null;
|
|
71
|
+
/** True when two half-open rectangles overlap. Touching edges do not repaint. */
|
|
72
|
+
declare function damageIntersects(a: DamageRect, b: DamageRect): boolean;
|
|
73
|
+
/** Expand `out` to cover both input rectangles. */
|
|
74
|
+
declare function unionDamageRect(a: DamageRect, b: DamageRect, out?: DamageRect): DamageRect;
|
|
75
|
+
/** Transform all four corners, returning the enclosing axis-aligned rectangle. */
|
|
76
|
+
declare function transformDamageRect(rect: DamageRect, transform: DamageTransform, out?: DamageRect): DamageRect;
|
|
77
|
+
/**
|
|
78
|
+
* Compute a conservative visual bound for one command. `null` means unknown,
|
|
79
|
+
* which callers must treat as intersecting every damage region. Glyphs are
|
|
80
|
+
* known only when their producer supplied explicit local ink bounds; guessing
|
|
81
|
+
* from pen positions or em size could leave stale ink on a retained surface.
|
|
82
|
+
*/
|
|
83
|
+
declare function commandDamageBounds<TTexture>(list: DrawList<TTexture>, index: number, out?: DamageRect): CommandBounds | null;
|
|
84
|
+
declare function createDamageTiles(width: number, height: number, tileWidth?: number, tileHeight?: number): DamageTiles;
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/draw-list.d.ts
|
|
87
|
+
/**
|
|
88
|
+
* The draw-list IR for a Godot 2D scene: a flat, ordered recording of what one
|
|
89
|
+
* frame paints, produced by walking `CanvasItem`s in draw order and consumed by
|
|
90
|
+
* a GPU executor (a later wave). It is deliberately dumb — no scene concepts, no
|
|
91
|
+
* nodes, no styles, just quads, indexed textured meshes, nine-patches,
|
|
92
|
+
* polylines and clip pushes/pops in
|
|
93
|
+
* the order they must hit the framebuffer.
|
|
94
|
+
*
|
|
95
|
+
* Storage is a set of pooled parallel typed arrays, not an array of command
|
|
96
|
+
* objects:
|
|
97
|
+
*
|
|
98
|
+
* - `kinds` / `floatOffsets` / `intOffsets` are one entry per command,
|
|
99
|
+
* - `floats` and `ints` are arenas that every command's numeric payload is
|
|
100
|
+
* appended into (a quad writes 16 floats + 3 ints, a polyline writes a
|
|
101
|
+
* 5-float header plus 2 floats per point, …),
|
|
102
|
+
* - `colorMatrices` is a side arena for the rare 3x3 color transform,
|
|
103
|
+
* - `textures` is the ONLY object side-array — a texture handle cannot live in
|
|
104
|
+
* a typed array.
|
|
105
|
+
*
|
|
106
|
+
* That layout exists so a frame costs zero garbage: `reset()` rewinds the write
|
|
107
|
+
* cursors and the same buffers are refilled next frame, and reading a command
|
|
108
|
+
* back fills a caller-owned view instead of allocating one. Arrays grow on
|
|
109
|
+
* demand (capacity doubling) and never shrink.
|
|
110
|
+
*
|
|
111
|
+
* All geometry is in DESIGN space (the scene's own coordinate system); mapping
|
|
112
|
+
* design space to device pixels is the executor's job.
|
|
113
|
+
*/
|
|
114
|
+
/** A textured/solid rectangle: the workhorse command. */
|
|
115
|
+
declare const DRAW_QUAD = 0;
|
|
116
|
+
/** A 9-sliced rectangle; the executor expands it to up to 9 quads. */
|
|
117
|
+
declare const DRAW_NINE_PATCH = 1;
|
|
118
|
+
/** A flattened, constant-width line strip. */
|
|
119
|
+
declare const DRAW_POLYLINE = 2;
|
|
120
|
+
/** Push a scissor/clip rect; every later command is clipped until the pop. */
|
|
121
|
+
declare const DRAW_CLIP_PUSH = 3;
|
|
122
|
+
/** Pop the most recent clip rect. */
|
|
123
|
+
declare const DRAW_CLIP_POP = 4;
|
|
124
|
+
/**
|
|
125
|
+
* A run of glyphs from one face, at one size, in one colour.
|
|
126
|
+
*
|
|
127
|
+
* The ONLY command that is not reducible to a textured quad, which is why it
|
|
128
|
+
* exists as its own kind rather than as sugar over `pushQuad`. Its glyphs are
|
|
129
|
+
* outlines evaluated per fragment (see `@godot-scene-web/hb-gpu`), so they carry
|
|
130
|
+
* an atlas slot id instead of a source rect and stay crisp under rotation and
|
|
131
|
+
* scale — the whole reason for the kind. An executor with no glyph pass
|
|
132
|
+
* installed skips it.
|
|
133
|
+
*/
|
|
134
|
+
declare const DRAW_GLYPHS = 5;
|
|
135
|
+
/** An arbitrary triangle list with one texture and per-mesh premultiplied tint. */
|
|
136
|
+
declare const DRAW_TEXTURED_MESH = 6;
|
|
137
|
+
/** A screen-dependent pass executed at this exact painter position. */
|
|
138
|
+
declare const DRAW_SCREEN_EFFECT = 7;
|
|
139
|
+
/** A caller-owned GPU pass executed directly into the current framebuffer. */
|
|
140
|
+
declare const DRAW_EXTERNAL_EFFECT = 8;
|
|
141
|
+
interface ScreenEffectDrawContext {
|
|
142
|
+
readonly gl: WebGL2RenderingContext;
|
|
143
|
+
readonly framebuffer: WebGLFramebuffer | null;
|
|
144
|
+
readonly width: number;
|
|
145
|
+
readonly height: number;
|
|
146
|
+
readonly damage?: DamageRect;
|
|
147
|
+
readonly scissor: Readonly<{
|
|
148
|
+
x: number;
|
|
149
|
+
y: number;
|
|
150
|
+
width: number;
|
|
151
|
+
height: number;
|
|
152
|
+
}>;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* The live painter target handed to an external pass. Unlike a screen effect,
|
|
156
|
+
* this pass does not imply a snapshot or read from the accumulated framebuffer.
|
|
157
|
+
*/
|
|
158
|
+
interface ExternalEffectDrawContext {
|
|
159
|
+
readonly gl: WebGL2RenderingContext;
|
|
160
|
+
readonly framebuffer: WebGLFramebuffer | null;
|
|
161
|
+
readonly width: number;
|
|
162
|
+
readonly height: number;
|
|
163
|
+
readonly damage?: DamageRect;
|
|
164
|
+
readonly scissor: Readonly<{
|
|
165
|
+
x: number;
|
|
166
|
+
y: number;
|
|
167
|
+
width: number;
|
|
168
|
+
height: number;
|
|
169
|
+
}>;
|
|
170
|
+
}
|
|
171
|
+
/** A DOM-free pass that snapshots the executor's accumulated current target. */
|
|
172
|
+
interface ScreenEffectDrawCommand {
|
|
173
|
+
readonly screenDependent: true;
|
|
174
|
+
execute(context: ScreenEffectDrawContext): boolean;
|
|
175
|
+
}
|
|
176
|
+
/** A DOM-free pass that paints directly into the executor's current target. */
|
|
177
|
+
interface ExternalEffectDrawCommand {
|
|
178
|
+
execute(context: ExternalEffectDrawContext): boolean;
|
|
179
|
+
}
|
|
180
|
+
type DrawCommandKind = typeof DRAW_QUAD | typeof DRAW_NINE_PATCH | typeof DRAW_POLYLINE | typeof DRAW_CLIP_PUSH | typeof DRAW_CLIP_POP | typeof DRAW_GLYPHS | typeof DRAW_TEXTURED_MESH | typeof DRAW_SCREEN_EFFECT | typeof DRAW_EXTERNAL_EFFECT;
|
|
181
|
+
type DrawCommandName = "quad" | "ninePatch" | "polyline" | "clipPush" | "clipPop" | "glyphs" | "texturedMesh" | "screenEffect" | "externalEffect";
|
|
182
|
+
/** Indexed by {@link DrawCommandKind}; for debugging and test assertions. */
|
|
183
|
+
declare const DRAW_COMMAND_NAMES: readonly DrawCommandName[];
|
|
184
|
+
/** Godot `CanvasItemMaterial.BLEND_MODE_MIX`: normal alpha compositing. */
|
|
185
|
+
declare const BLEND_MIX = 0;
|
|
186
|
+
/** Godot `BLEND_MODE_ADD`. */
|
|
187
|
+
declare const BLEND_ADD = 1;
|
|
188
|
+
/** Godot `BLEND_MODE_SUB`. */
|
|
189
|
+
declare const BLEND_SUB = 2;
|
|
190
|
+
/** Godot `BLEND_MODE_MUL`. */
|
|
191
|
+
declare const BLEND_MUL = 3;
|
|
192
|
+
type BlendMode = typeof BLEND_MIX | typeof BLEND_ADD | typeof BLEND_SUB | typeof BLEND_MUL;
|
|
193
|
+
/** Bit in a command's packed flags int: mirror the source rect horizontally. */
|
|
194
|
+
declare const FLIP_H = 1;
|
|
195
|
+
/** Bit in a command's packed flags int: mirror the source rect vertically. */
|
|
196
|
+
declare const FLIP_V = 2;
|
|
197
|
+
/**
|
|
198
|
+
* A quad's numeric payload. Views are caller-owned and reusable: fill one and
|
|
199
|
+
* hand it to `pushQuad`, or pass one to `readQuad` to have it filled in place.
|
|
200
|
+
* Nothing here is retained by the list — the push copies into the arenas.
|
|
201
|
+
*/
|
|
202
|
+
interface QuadView {
|
|
203
|
+
/**
|
|
204
|
+
* The 2x3 affine that maps the unit-ish quad into design space, in Godot
|
|
205
|
+
* `Transform2D` order: `[xx, xy, yx, yy, originX, originY]`, i.e.
|
|
206
|
+
* `x' = xx*x + yx*y + originX`, `y' = xy*x + yy*y + originY`. Length 6.
|
|
207
|
+
*/
|
|
208
|
+
m: Float32Array;
|
|
209
|
+
/** Destination width in design units, before `m` is applied. */
|
|
210
|
+
w: number;
|
|
211
|
+
/** Destination height in design units, before `m` is applied. */
|
|
212
|
+
h: number;
|
|
213
|
+
/** Source rect on the texture page, in page pixels (not normalized). */
|
|
214
|
+
srcX: number;
|
|
215
|
+
srcY: number;
|
|
216
|
+
srcW: number;
|
|
217
|
+
srcH: number;
|
|
218
|
+
/** PREMULTIPLIED tint, linear 0..1 per channel (`rgb` already times `a`). */
|
|
219
|
+
r: number;
|
|
220
|
+
g: number;
|
|
221
|
+
b: number;
|
|
222
|
+
a: number;
|
|
223
|
+
blend: BlendMode;
|
|
224
|
+
flipH: boolean;
|
|
225
|
+
flipV: boolean;
|
|
226
|
+
/**
|
|
227
|
+
* Whether {@link QuadView.colorMatrix} is meaningful. Most quads carry no
|
|
228
|
+
* color transform at all (the field is the IR's "null"), so the matrix is
|
|
229
|
+
* stored in a side arena and skipped entirely when this is `false`.
|
|
230
|
+
*/
|
|
231
|
+
hasColorMatrix: boolean;
|
|
232
|
+
/**
|
|
233
|
+
* Row-major 3x3 linear RGB transform (`out_rgb = m * in_rgb`, alpha
|
|
234
|
+
* untouched) — an HSV-style shader tint. Length 9. Ignored unless
|
|
235
|
+
* `hasColorMatrix`.
|
|
236
|
+
*/
|
|
237
|
+
colorMatrix: Float32Array;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* A nine-patch's payload: a quad plus the four stretch margins. `srcX..srcH` is
|
|
241
|
+
* the patch REGION on the page; the margins are insets into that region, in
|
|
242
|
+
* page pixels, exactly like Godot's `NinePatchRect.patch_margin_*`.
|
|
243
|
+
*/
|
|
244
|
+
interface NinePatchView extends QuadView {
|
|
245
|
+
marginLeft: number;
|
|
246
|
+
marginTop: number;
|
|
247
|
+
marginRight: number;
|
|
248
|
+
marginBottom: number;
|
|
249
|
+
}
|
|
250
|
+
/** A polyline's payload: a flattened `x, y, x, y, …` strip with one width. */
|
|
251
|
+
interface PolylineView {
|
|
252
|
+
/**
|
|
253
|
+
* Flattened point coordinates in design space. Length is at least
|
|
254
|
+
* `pointCount * 2`; a longer buffer is allowed (and expected, since views are
|
|
255
|
+
* reused), only the first `pointCount * 2` entries are read.
|
|
256
|
+
*/
|
|
257
|
+
points: Float32Array;
|
|
258
|
+
pointCount: number;
|
|
259
|
+
/** Stroke width in design units. */
|
|
260
|
+
width: number;
|
|
261
|
+
/** PREMULTIPLIED stroke color, linear 0..1 per channel. */
|
|
262
|
+
r: number;
|
|
263
|
+
g: number;
|
|
264
|
+
b: number;
|
|
265
|
+
a: number;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* An indexed triangle list sampling one texture. Positions and UVs are paired
|
|
269
|
+
* by vertex index; positions are local design-space coordinates and `m` maps
|
|
270
|
+
* them to design space. UVs are normalized texture coordinates, intentionally
|
|
271
|
+
* not a quad source rect: arbitrary mesh topology must not be forced through a
|
|
272
|
+
* rectangle-shaped source contract.
|
|
273
|
+
*/
|
|
274
|
+
interface TexturedMeshView {
|
|
275
|
+
/** Godot `Transform2D` mapping local vertex positions into design space. */
|
|
276
|
+
m: Float32Array;
|
|
277
|
+
/** Flattened local `x, y` pairs. Only `vertexCount * 2` entries are read. */
|
|
278
|
+
positions: Float32Array;
|
|
279
|
+
/** Flattened normalized `u, v` pairs, one pair for every position. */
|
|
280
|
+
uvs: Float32Array;
|
|
281
|
+
vertexCount: number;
|
|
282
|
+
/** Triangle-list vertex indices. Only `indexCount` entries are read. */
|
|
283
|
+
indices: Uint32Array;
|
|
284
|
+
indexCount: number;
|
|
285
|
+
/** PREMULTIPLIED mesh tint, linear 0..1 per channel. */
|
|
286
|
+
r: number;
|
|
287
|
+
g: number;
|
|
288
|
+
b: number;
|
|
289
|
+
a: number;
|
|
290
|
+
blend: BlendMode;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* A glyph run's payload: one face, one size, one colour, N positioned glyphs.
|
|
294
|
+
*
|
|
295
|
+
* THE SLOT IDS ARE HANDLES, NOT ADDRESSES, and that distinction is the whole
|
|
296
|
+
* point of the indirection. A glyph atlas evicts — it is a fixed texture holding
|
|
297
|
+
* an unbounded pool — so recording a glyph's atlas OFFSET into a list that may be
|
|
298
|
+
* retained and repainted next frame is how you get a different glyph's outline
|
|
299
|
+
* drawn at the right size, in the right place, perfectly antialiased. Unreadable
|
|
300
|
+
* text that looks like working text. A slot id survives eviction because the
|
|
301
|
+
* atlas re-resolves it (re-uploading the outline if it has to) at draw time.
|
|
302
|
+
*
|
|
303
|
+
* Shaping is NOT this package's job. `positions` are pen positions a shaper
|
|
304
|
+
* produced; the list only records them.
|
|
305
|
+
*/
|
|
306
|
+
interface GlyphsView {
|
|
307
|
+
/**
|
|
308
|
+
* The 2x3 affine that maps the run's local space into design space, in the
|
|
309
|
+
* same Godot `Transform2D` order as {@link QuadView.m}. Length 6.
|
|
310
|
+
*
|
|
311
|
+
* ROTATION BELONGS HERE, NEVER IN `positions`. The glyph shader dilates each
|
|
312
|
+
* outline by half a SCREEN pixel and works out how far that is by pushing the
|
|
313
|
+
* quad's corner and its normal through this same matrix. Pen positions rotated
|
|
314
|
+
* on the CPU would be dilated along the wrong axes — a rim of clipped
|
|
315
|
+
* antialiasing on one side of every glyph.
|
|
316
|
+
*/
|
|
317
|
+
m: Float32Array;
|
|
318
|
+
/**
|
|
319
|
+
* Design units per em: the font size in the run's local space.
|
|
320
|
+
*
|
|
321
|
+
* The rendered outline is resolution-independent, but its ANTIALIASING is not
|
|
322
|
+
* unconditionally so: HarfBuzz's coverage shader takes a five-tap branch below
|
|
323
|
+
* ppem 16, and measured against an 8x-downsampled reference that branch is
|
|
324
|
+
* blurrier than a baked atlas at the same size. The ppem that matters is this
|
|
325
|
+
* times the scale in {@link GlyphsView.m} times the device-pixel ratio: a
|
|
326
|
+
* caller's zoom rides the matrix, and the shader sees it — see
|
|
327
|
+
* `docs/text-rendering.md`.
|
|
328
|
+
*/
|
|
329
|
+
pixelsPerEm: number;
|
|
330
|
+
/** PREMULTIPLIED colour, linear 0..1 per channel, for the whole run. */
|
|
331
|
+
r: number;
|
|
332
|
+
g: number;
|
|
333
|
+
b: number;
|
|
334
|
+
a: number;
|
|
335
|
+
/**
|
|
336
|
+
* One atlas slot id per glyph. Length is at least {@link GlyphsView.glyphCount}
|
|
337
|
+
* — a longer buffer is allowed and expected, since views are reused.
|
|
338
|
+
*/
|
|
339
|
+
slots: Int32Array;
|
|
340
|
+
/**
|
|
341
|
+
* Pen positions, flattened `x, y, x, y, …`, in the run's local space and
|
|
342
|
+
* BEFORE `m` is applied. Each is the glyph's em origin, i.e. a point on the
|
|
343
|
+
* baseline. Length is at least `glyphCount * 2`.
|
|
344
|
+
*/
|
|
345
|
+
positions: Float32Array;
|
|
346
|
+
glyphCount: number;
|
|
347
|
+
/**
|
|
348
|
+
* Grow every glyph outward by this many design units before filling. `0` (the
|
|
349
|
+
* default) is a plain fill.
|
|
350
|
+
*
|
|
351
|
+
* AN OUTLINED LABEL IS TWO RUNS, NOT ONE: the same glyphs and pens in the
|
|
352
|
+
* outline colour with a spread, then again in the fill colour with none. This
|
|
353
|
+
* kind carries one colour and one spread by design — merging them would need
|
|
354
|
+
* two draw calls behind one command and would hide the ordering, which is the
|
|
355
|
+
* part a caller has to get right (outline UNDER fill).
|
|
356
|
+
*
|
|
357
|
+
* A CENTRED `strokeText` OF WIDTH `W` REACHES `W / 2` OUTWARD, so a caller
|
|
358
|
+
* matching one records `W / 2`. The arithmetic is the caller's: only it knows
|
|
359
|
+
* whether the stroke it is reproducing is centred, inner or outer.
|
|
360
|
+
*
|
|
361
|
+
* THE DILATION FILLS THE INTERIOR AND A STROKE DOES NOT. Identical under an
|
|
362
|
+
* opaque fill, which covers every pixel the two disagree about; visibly
|
|
363
|
+
* different under a TRANSLUCENT one, where the outline colour shows through
|
|
364
|
+
* the glyph's middle. Stated because it is a real limit of a coverage-max
|
|
365
|
+
* dilation rather than a rounding difference — see `HbGpuRenderer.setSpread`.
|
|
366
|
+
*/
|
|
367
|
+
spreadPx: number;
|
|
368
|
+
/**
|
|
369
|
+
* The exact local-space rectangle containing the run's unspread glyph ink.
|
|
370
|
+
*
|
|
371
|
+
* These are optional for source compatibility with older producers. Omitting
|
|
372
|
+
* any one is deliberately an UNKNOWN bound, never an em-box estimate: a
|
|
373
|
+
* retained surface must repaint fully rather than leave stale text behind.
|
|
374
|
+
*/
|
|
375
|
+
localInkX?: number;
|
|
376
|
+
localInkY?: number;
|
|
377
|
+
localInkWidth?: number;
|
|
378
|
+
localInkHeight?: number;
|
|
379
|
+
/** Additional local-space reach for effects and antialiasing. The draw list
|
|
380
|
+
* also adds its own `spreadPx`, so this is for reach beyond the unspread
|
|
381
|
+
* outline (for example a shadow or caller-measured coverage fringe). */
|
|
382
|
+
localInkOutset?: number;
|
|
383
|
+
}
|
|
384
|
+
/** A clip push's payload. */
|
|
385
|
+
interface ClipRectView {
|
|
386
|
+
/** Clip rect in design space. */
|
|
387
|
+
x: number;
|
|
388
|
+
y: number;
|
|
389
|
+
w: number;
|
|
390
|
+
h: number;
|
|
391
|
+
/**
|
|
392
|
+
* Corner radius in design units; `0` (the common case) is a plain rect and
|
|
393
|
+
* lets the executor use a cheap scissor instead of a mask.
|
|
394
|
+
*/
|
|
395
|
+
cornerRadius: number;
|
|
396
|
+
/**
|
|
397
|
+
* One-axis slack: widen the rect by `outsetX` on BOTH x edges, i.e. clip to
|
|
398
|
+
* `[x - outsetX, x + w + outsetX]` while `y`/`h` stay exact. `0` (the common
|
|
399
|
+
* case) clips to the rect itself. This exists because a re-laid-out scene can
|
|
400
|
+
* legitimately paint slightly wider than the clip that Godot recorded, and
|
|
401
|
+
* only ever on x.
|
|
402
|
+
*/
|
|
403
|
+
outsetX: number;
|
|
404
|
+
}
|
|
405
|
+
interface DrawListOptions {
|
|
406
|
+
/** Initial command capacity (entries, not bytes). Grows on demand. */
|
|
407
|
+
commandCapacity?: number;
|
|
408
|
+
/** Initial float-arena capacity. Grows on demand. */
|
|
409
|
+
floatCapacity?: number;
|
|
410
|
+
/** Initial int-arena capacity. Grows on demand. */
|
|
411
|
+
intCapacity?: number;
|
|
412
|
+
/** Initial color-matrix capacity, in matrices. Grows on demand. */
|
|
413
|
+
colorMatrixCapacity?: number;
|
|
414
|
+
/** Retained in-place patch records for compiled consumers. */
|
|
415
|
+
patchJournalCapacity?: number;
|
|
416
|
+
}
|
|
417
|
+
/** Caller-owned, grow-only output for {@link DrawList.readPatchesSince}. */
|
|
418
|
+
interface DrawListPatchView {
|
|
419
|
+
readonly indices: readonly number[];
|
|
420
|
+
readonly revision: number;
|
|
421
|
+
readonly overflowed: boolean;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* A reusable, DOM- and GPU-ownership-free copy of a contiguous draw-list
|
|
425
|
+
* range. A fragment owns its arena bytes and command references, so it remains
|
|
426
|
+
* valid when the list it was captured from is reset, grows, or patched.
|
|
427
|
+
*
|
|
428
|
+
* Fragments deliberately retain texture and effect *references* rather than
|
|
429
|
+
* attempting to clone them: those handles are executor-owned identities, not
|
|
430
|
+
* draw-list data. Capturing and appending only copies the ordering and payload
|
|
431
|
+
* that names them.
|
|
432
|
+
*/
|
|
433
|
+
interface DrawListFragment<TTexture = unknown> {
|
|
434
|
+
/** Number of commands in the captured half-open source range. */
|
|
435
|
+
readonly count: number;
|
|
436
|
+
/** Always zero for a valid standalone fragment. */
|
|
437
|
+
readonly clipDepth: number;
|
|
438
|
+
/** Deepest clip nesting within the captured range. */
|
|
439
|
+
readonly maxClipDepth: number;
|
|
440
|
+
/** Forget the captured range but retain all backing storage for reuse. */
|
|
441
|
+
reset(): void;
|
|
442
|
+
/**
|
|
443
|
+
* Clone `[start, end)` from `source` into this fragment. The range must be
|
|
444
|
+
* independently clip-balanced; an unmatched pop or push is rejected rather
|
|
445
|
+
* than producing a fragment whose replay depends on hidden painter state.
|
|
446
|
+
*/
|
|
447
|
+
capture(source: DrawList<TTexture>, start: number, end?: number): void;
|
|
448
|
+
}
|
|
449
|
+
/** One same-shaped retained-fragment replacement in a draw list. */
|
|
450
|
+
interface DrawListFragmentPatch<TTexture = unknown> {
|
|
451
|
+
/** First destination command index. Patches are supplied in painter order. */
|
|
452
|
+
readonly start: number;
|
|
453
|
+
/** Replacement command payloads, captured through createDrawListFragment. */
|
|
454
|
+
readonly fragment: DrawListFragment<TTexture>;
|
|
455
|
+
}
|
|
456
|
+
declare function createDrawListPatchView(capacity?: number): DrawListPatchView;
|
|
457
|
+
/**
|
|
458
|
+
* An ordered, poolable recording of one frame's draws.
|
|
459
|
+
*
|
|
460
|
+
* `TTexture` is whatever the executor's texture handle is (a `WebGLTexture`, a
|
|
461
|
+
* `GPUTexture`, an atlas page id, …); the list only stores and returns it.
|
|
462
|
+
*/
|
|
463
|
+
interface DrawList<TTexture = unknown> {
|
|
464
|
+
/** Number of commands recorded since the last `reset()`. */
|
|
465
|
+
readonly count: number;
|
|
466
|
+
/** Clip pushes that are currently open (0 at a balanced end of frame). */
|
|
467
|
+
readonly clipDepth: number;
|
|
468
|
+
/** Deepest clip nesting seen since `reset()` — sizes an executor's stack. */
|
|
469
|
+
readonly maxClipDepth: number;
|
|
470
|
+
/** Changes when commands are appended, removed, or their layout changes. */
|
|
471
|
+
readonly structuralRevision: number;
|
|
472
|
+
/** Changes for every structural or in-place command patch. */
|
|
473
|
+
readonly contentRevision: number;
|
|
474
|
+
/** Per-command patch generation, for retained compiled consumers. */
|
|
475
|
+
commandRevisionAt(index: number): number;
|
|
476
|
+
/** Fill reusable `out` with patches after `revision`; overflow means rebuild. */
|
|
477
|
+
readPatchesSince(revision: number, out: DrawListPatchView): DrawListPatchView;
|
|
478
|
+
/**
|
|
479
|
+
* The float arena. The identity of this array CHANGES when it grows, so an
|
|
480
|
+
* executor must re-read it (never cache it across pushes).
|
|
481
|
+
*/
|
|
482
|
+
readonly floats: Float32Array;
|
|
483
|
+
/** The int arena. Same growth caveat as {@link DrawList.floats}. */
|
|
484
|
+
readonly ints: Int32Array;
|
|
485
|
+
/** The color-matrix arena, 9 floats per entry. Same growth caveat. */
|
|
486
|
+
readonly colorMatrices: Float32Array;
|
|
487
|
+
/** Rewind to an empty list, keeping (and reusing) the buffers. */
|
|
488
|
+
reset(): void;
|
|
489
|
+
/**
|
|
490
|
+
* Append a previously captured fragment in painter order and return the
|
|
491
|
+
* first destination command index. Empty fragments return `count` and do
|
|
492
|
+
* not change revisions.
|
|
493
|
+
*/
|
|
494
|
+
appendFragment(fragment: DrawListFragment<TTexture>): number;
|
|
495
|
+
/**
|
|
496
|
+
* Atomically overwrite a same-shaped recorded range from a retained
|
|
497
|
+
* fragment. The fragment must fit at `start` and every command must retain
|
|
498
|
+
* its kind, numeric payload lengths, object-side payload shape, clip
|
|
499
|
+
* sequence, and colour-matrix presence. A mismatch leaves this list wholly
|
|
500
|
+
* unchanged and returns `false`; successful overwrites preserve command
|
|
501
|
+
* indices and structural revision while publishing ordinary content patches.
|
|
502
|
+
*
|
|
503
|
+
* An empty fragment is a successful no-op at any insertion index from `0`
|
|
504
|
+
* through `count`.
|
|
505
|
+
*/
|
|
506
|
+
patchFragment(start: number, fragment: DrawListFragment<TTexture>): boolean;
|
|
507
|
+
/**
|
|
508
|
+
* Atomically apply non-overlapping same-shaped fragments in painter order.
|
|
509
|
+
* Every range is validated before any payload, reference, revision, or patch
|
|
510
|
+
* journal entry changes. Starts must be nondecreasing and non-empty ranges
|
|
511
|
+
* must not overlap.
|
|
512
|
+
*/
|
|
513
|
+
patchFragments(patches: readonly DrawListFragmentPatch<TTexture>[]): boolean;
|
|
514
|
+
kindAt(index: number): DrawCommandKind;
|
|
515
|
+
kindNameAt(index: number): DrawCommandName;
|
|
516
|
+
/** The command's texture handle, or `null` for untextured/geometry commands. */
|
|
517
|
+
textureAt(index: number): TTexture | null;
|
|
518
|
+
/** Screen-dependent pass recorded at index, if this is one. */
|
|
519
|
+
screenEffectAt(index: number): ScreenEffectDrawCommand | null;
|
|
520
|
+
/** Direct framebuffer pass recorded at index, if this is one. */
|
|
521
|
+
externalEffectAt(index: number): ExternalEffectDrawCommand | null;
|
|
522
|
+
/** Start of the command's payload in {@link DrawList.floats}. */
|
|
523
|
+
floatOffsetAt(index: number): number;
|
|
524
|
+
/** Start of the command's payload in {@link DrawList.ints}. */
|
|
525
|
+
intOffsetAt(index: number): number;
|
|
526
|
+
/**
|
|
527
|
+
* Index of the command's color matrix in {@link DrawList.colorMatrices}
|
|
528
|
+
* (multiply by 9 for the float offset), or `-1` when it has none.
|
|
529
|
+
*/
|
|
530
|
+
colorMatrixIndexAt(index: number): number;
|
|
531
|
+
/** Record a quad. Returns the command index. */
|
|
532
|
+
pushQuad(quad: QuadView, texture?: TTexture | null): number;
|
|
533
|
+
/** Record a nine-patch. Returns the command index. */
|
|
534
|
+
pushNinePatch(patch: NinePatchView, texture?: TTexture | null): number;
|
|
535
|
+
/** Record a polyline. Returns the command index. */
|
|
536
|
+
pushPolyline(line: PolylineView): number;
|
|
537
|
+
/** Record an indexed textured triangle mesh. Returns the command index. */
|
|
538
|
+
pushTexturedMesh(mesh: TexturedMeshView, texture?: TTexture | null): number;
|
|
539
|
+
/** Record a glyph run. Returns the command index. */
|
|
540
|
+
pushGlyphs(run: GlyphsView): number;
|
|
541
|
+
/** Record a pass that samples the accumulated framebuffer at this painter index. */
|
|
542
|
+
pushScreenEffect(command: ScreenEffectDrawCommand): number;
|
|
543
|
+
/** Record a direct framebuffer pass at this painter position. */
|
|
544
|
+
pushExternalEffect(command: ExternalEffectDrawCommand): number;
|
|
545
|
+
/** Open a clip rect. Returns the command index. */
|
|
546
|
+
pushClipRect(clip: ClipRectView): number;
|
|
547
|
+
/** Close the innermost clip rect. Returns the command index. */
|
|
548
|
+
popClip(): number;
|
|
549
|
+
/** Fill `out` from a `quad` command and return it. */
|
|
550
|
+
readQuad(index: number, out: QuadView): QuadView;
|
|
551
|
+
/** Fill `out` from a `ninePatch` command and return it. */
|
|
552
|
+
readNinePatch(index: number, out: NinePatchView): NinePatchView;
|
|
553
|
+
/**
|
|
554
|
+
* Fill `out` from a `polyline` command and return it. `out.points` is
|
|
555
|
+
* REPLACED by a larger buffer if it cannot hold the recorded points.
|
|
556
|
+
*/
|
|
557
|
+
readPolyline(index: number, out: PolylineView): PolylineView;
|
|
558
|
+
/** Fill `out` from a textured mesh, growing its caller-owned arrays if needed. */
|
|
559
|
+
readTexturedMesh(index: number, out: TexturedMeshView): TexturedMeshView;
|
|
560
|
+
/**
|
|
561
|
+
* Fill `out` from a `glyphs` command and return it. `out.slots` and
|
|
562
|
+
* `out.positions` are REPLACED by larger buffers if they cannot hold the run.
|
|
563
|
+
*/
|
|
564
|
+
readGlyphs(index: number, out: GlyphsView): GlyphsView;
|
|
565
|
+
/** Fill `out` from a `clipPush` command and return it. */
|
|
566
|
+
readClipRect(index: number, out: ClipRectView): ClipRectView;
|
|
567
|
+
/**
|
|
568
|
+
* Overwrite a recorded quad's transform IN PLACE, leaving the rest of its
|
|
569
|
+
* payload — size, source rect, colour, blend, flip flags, colour matrix —
|
|
570
|
+
* exactly as it was pushed.
|
|
571
|
+
*
|
|
572
|
+
* This exists for consumers that rebuild a whole frame today only because one
|
|
573
|
+
* node moved: they can keep last frame's list and repaint it, which is the
|
|
574
|
+
* point of a list that owns its storage. `m` is read in the same
|
|
575
|
+
* `Transform2D` order as {@link QuadView.m} and only its first 6 entries are
|
|
576
|
+
* used.
|
|
577
|
+
*
|
|
578
|
+
* PATCH THROUGH THIS METHOD, NEVER THROUGH A CACHED `floats`. The arenas are
|
|
579
|
+
* reallocated when they grow (see {@link DrawList.floats}), so a Float32Array
|
|
580
|
+
* captured before a `push` may be a DEAD copy — writing into it changes
|
|
581
|
+
* nothing that will be drawn, silently. The method re-reads the live arena on
|
|
582
|
+
* every call.
|
|
583
|
+
*
|
|
584
|
+
* Accepts `quad` and `ninePatch` commands (a nine-patch's payload IS a quad
|
|
585
|
+
* payload plus margins); anything else throws.
|
|
586
|
+
*/
|
|
587
|
+
patchQuadTransform(index: number, m: ArrayLike<number>): void;
|
|
588
|
+
/**
|
|
589
|
+
* Overwrite a recorded quad's PREMULTIPLIED tint in place, leaving geometry,
|
|
590
|
+
* source rect, blend, flip flags and colour matrix alone.
|
|
591
|
+
*
|
|
592
|
+
* `rgb` must already be multiplied by `a`, exactly as {@link QuadView} states
|
|
593
|
+
* — this is a raw write into the arena, not a colour operation, so nothing
|
|
594
|
+
* here will premultiply on the caller's behalf.
|
|
595
|
+
*
|
|
596
|
+
* The same cached-arena hazard as {@link DrawList.patchQuadTransform}
|
|
597
|
+
* applies, and the same two kinds are accepted.
|
|
598
|
+
*/
|
|
599
|
+
patchQuadColor(index: number, r: number, g: number, b: number, a: number): void;
|
|
600
|
+
/**
|
|
601
|
+
* Replace a quad-like command's texture and source rectangle without touching
|
|
602
|
+
* its destination geometry, tint, blend state, flags or colour matrix.
|
|
603
|
+
*
|
|
604
|
+
* Like the other patch methods this re-reads the current arenas. In
|
|
605
|
+
* particular, callers must not write source coordinates through a cached
|
|
606
|
+
* `floats` view: a later push may have grown that arena and made the cached
|
|
607
|
+
* view a detached copy.
|
|
608
|
+
*/
|
|
609
|
+
patchQuadSource(index: number, texture: TTexture | null, srcX: number, srcY: number, srcW: number, srcH: number): void;
|
|
610
|
+
/** Replace the local positions of a mesh without changing its topology. */
|
|
611
|
+
patchTexturedMeshPositions(index: number, positions: Float32Array): void;
|
|
612
|
+
/** Replace the normalized UV pairs of a mesh without changing its topology. */
|
|
613
|
+
patchTexturedMeshUvs(index: number, uvs: Float32Array): void;
|
|
614
|
+
/** Replace a mesh's texture handle without changing geometry or UVs. */
|
|
615
|
+
patchTexturedMeshSource(index: number, texture: TTexture | null): void;
|
|
616
|
+
/** Replace a mesh's local-to-design transform. */
|
|
617
|
+
patchTexturedMeshTransform(index: number, m: ArrayLike<number>): void;
|
|
618
|
+
/** Replace a mesh's premultiplied tint. */
|
|
619
|
+
patchTexturedMeshColor(index: number, r: number, g: number, b: number, a: number): void;
|
|
620
|
+
/**
|
|
621
|
+
* Overwrite a recorded glyph run's transform in place, leaving its glyphs,
|
|
622
|
+
* size and colour alone.
|
|
623
|
+
*
|
|
624
|
+
* The counterpart of {@link DrawList.patchQuadTransform}, and the reason the
|
|
625
|
+
* glyph kind stores a transform at all instead of pre-transformed pen
|
|
626
|
+
* positions: a label that translates every frame re-records nothing, and the
|
|
627
|
+
* shader still sees the whole transform (which is what keeps its half-pixel
|
|
628
|
+
* dilation on the right axes). The same cached-arena hazard applies — patch
|
|
629
|
+
* through this method, never through a captured `floats`.
|
|
630
|
+
*/
|
|
631
|
+
patchGlyphsTransform(index: number, m: ArrayLike<number>): void;
|
|
632
|
+
/**
|
|
633
|
+
* Overwrite a recorded glyph run's PREMULTIPLIED colour in place. `rgb` must
|
|
634
|
+
* already be multiplied by `a`, exactly as {@link GlyphsView} states.
|
|
635
|
+
*/
|
|
636
|
+
patchGlyphsColor(index: number, r: number, g: number, b: number, a: number): void;
|
|
637
|
+
}
|
|
638
|
+
/** A fresh quad view: identity transform, opaque white, mix blend, no matrix. */
|
|
639
|
+
declare function createQuadView(): QuadView;
|
|
640
|
+
/** A fresh nine-patch view: a quad view with zero margins. */
|
|
641
|
+
declare function createNinePatchView(): NinePatchView;
|
|
642
|
+
/** A fresh polyline view with room for `pointCapacity` points. */
|
|
643
|
+
declare function createPolylineView(pointCapacity?: number): PolylineView;
|
|
644
|
+
/** A reusable textured mesh view with room for the requested topology. */
|
|
645
|
+
declare function createTexturedMeshView(vertexCapacity?: number, indexCapacity?: number): TexturedMeshView;
|
|
646
|
+
/** A fresh glyph-run view with room for `glyphCapacity` glyphs. */
|
|
647
|
+
declare function createGlyphsView(glyphCapacity?: number): GlyphsView;
|
|
648
|
+
/** A fresh clip-rect view: empty rect, square corners, no outset. */
|
|
649
|
+
declare function createClipRectView(): ClipRectView;
|
|
650
|
+
/**
|
|
651
|
+
* Copy an `html` {@link ColorMatrix} into a quad/nine-patch view and arm it.
|
|
652
|
+
* Passing `null` disarms the view's matrix (leaving its contents alone), which
|
|
653
|
+
* is the shape most callers have: a computed transform that is usually absent.
|
|
654
|
+
*/
|
|
655
|
+
declare function setViewColorMatrix(view: QuadView, matrix: ColorMatrix | null | undefined): void;
|
|
656
|
+
/**
|
|
657
|
+
* Create reusable storage for a retained command fragment. The fragment holds
|
|
658
|
+
* only draw-list data and opaque caller references; it never creates or owns a
|
|
659
|
+
* DOM node, a WebGL object, or an executor.
|
|
660
|
+
*/
|
|
661
|
+
declare function createDrawListFragment<TTexture = unknown>(options?: DrawListOptions): DrawListFragment<TTexture>;
|
|
662
|
+
/**
|
|
663
|
+
* Create an empty draw list. Capacities are only a starting point; every arena
|
|
664
|
+
* grows on demand, so a list settles at the high-water mark of the frames it
|
|
665
|
+
* has recorded and then allocates nothing.
|
|
666
|
+
*/
|
|
667
|
+
declare function createDrawList<TTexture = unknown>(options?: DrawListOptions): DrawList<TTexture>;
|
|
668
|
+
//#endregion
|
|
669
|
+
//#region src/present.d.ts
|
|
670
|
+
/**
|
|
671
|
+
* The stage: a WebGL2 context of its OWN on a caller-supplied canvas, its exact
|
|
672
|
+
* backing-store size, the design->screen projection every other module in this
|
|
673
|
+
* package reads, and the context-loss plumbing.
|
|
674
|
+
*
|
|
675
|
+
* THE ONLY MODULE IN `@godot-scene-web/canvas` THAT TOUCHES A CANVAS. Everything
|
|
676
|
+
* else takes a `WebGL2RenderingContext` and does arithmetic; keeping the DOM
|
|
677
|
+
* surface to one file is what makes the rest of the package testable without a
|
|
678
|
+
* browser and reusable off the main thread.
|
|
679
|
+
*
|
|
680
|
+
* ITS OWN CONTEXT, not `html`'s shared one. The shared context exists so that N
|
|
681
|
+
* per-node effect canvases on one page do not each burn one of the browser's ~16
|
|
682
|
+
* live contexts; it renders offscreen and BLITS onto each node. The stage is the
|
|
683
|
+
* opposite shape — ONE canvas for the whole scene, presented directly — so it
|
|
684
|
+
* neither needs the blit nor wants to share a drawing buffer that is sized,
|
|
685
|
+
* cleared and grown by two other runtimes on their own schedule.
|
|
686
|
+
*
|
|
687
|
+
* THE CONTEXT ATTRIBUTES, and why each one:
|
|
688
|
+
*
|
|
689
|
+
* - `alpha: true` — the stage composites over whatever the page puts behind it
|
|
690
|
+
* (letterbox bars, a background layer), so it must carry real transparency.
|
|
691
|
+
* - `premultipliedAlpha: true` — the contract stated end to end: textures upload
|
|
692
|
+
* premultiplied (`./textures`), tints are premultiplied (the draw-list's own
|
|
693
|
+
* documentation), every fragment emits `vec4(rgb*a, a)`, the MIX blend is
|
|
694
|
+
* `(ONE, ONE_MINUS_SRC_ALPHA)`. This flag changes no byte in the drawing
|
|
695
|
+
* buffer; it tells the compositor how to READ them. Declare it wrong and
|
|
696
|
+
* nothing errors — the page simply multiplies by alpha a second time and every
|
|
697
|
+
* translucent pixel comes out dark. `html/webgl/shared-gl.ts` has the long
|
|
698
|
+
* version of this note and the bug that produced it.
|
|
699
|
+
* - `stencil: false`, `depth: false` — a painter's-algorithm 2D renderer uses
|
|
700
|
+
* neither, and both cost drawing-buffer memory on every device.
|
|
701
|
+
* - `antialias: false` — there is no geometry to multisample: every edge in the
|
|
702
|
+
* scene is a texture's own alpha, which LINEAR filtering already smooths. MSAA
|
|
703
|
+
* here would allocate a multisample buffer and resolve it every frame to change
|
|
704
|
+
* nothing.
|
|
705
|
+
* - `preserveDrawingBuffer: false` — the stage clears and repaints every frame,
|
|
706
|
+
* and preserving forces the implementation to keep a copy. Measurement trap
|
|
707
|
+
* that follows: once the stage is idle, a JS-side readback (`toDataURL`,
|
|
708
|
+
* `drawImage` of the canvas) returns an EMPTY image while the composited
|
|
709
|
+
* frame on screen is correct — the drawing buffer is gone after present, by
|
|
710
|
+
* design. Judge presentation only by compositor capture (a real screenshot
|
|
711
|
+
* of the tab), never by reading the canvas back.
|
|
712
|
+
* - `desynchronized` is deliberately NOT set. It can cut latency by letting the
|
|
713
|
+
* canvas bypass the compositor, but it also makes readback and screenshot
|
|
714
|
+
* behaviour implementation-defined — which is exactly what the pixel tests
|
|
715
|
+
* assert on — and on several drivers it silently disables the alpha compositing
|
|
716
|
+
* path the point above depends on. A latency measurement can turn it on later,
|
|
717
|
+
* with a test that proves the composite still holds.
|
|
718
|
+
*/
|
|
719
|
+
/** The subset of a canvas the stage needs. Structural so that both an
|
|
720
|
+
* `HTMLCanvasElement` and an `OffscreenCanvas` satisfy it. */
|
|
721
|
+
interface StageCanvas {
|
|
722
|
+
width: number;
|
|
723
|
+
height: number;
|
|
724
|
+
getContext(contextId: "webgl2", options?: WebGLContextAttributes): WebGL2RenderingContext | null;
|
|
725
|
+
addEventListener(type: string, listener: (event: Event) => void): void;
|
|
726
|
+
removeEventListener(type: string, listener: (event: Event) => void): void;
|
|
727
|
+
}
|
|
728
|
+
/** How design coordinates reach the screen, in the two forms consumers need. */
|
|
729
|
+
interface StageProjection {
|
|
730
|
+
/** Exact scene coordinate extent, independent of float32 matrix rounding. */
|
|
731
|
+
readonly designWidth: number;
|
|
732
|
+
readonly designHeight: number;
|
|
733
|
+
/**
|
|
734
|
+
* Design -> clip space as `(scaleX, scaleY, translateX, translateY)`:
|
|
735
|
+
* `clip = design * scale + translate`. `scaleY` is NEGATIVE — design space
|
|
736
|
+
* measures Y downwards and clip space upwards.
|
|
737
|
+
*/
|
|
738
|
+
readonly toClip: Float32Array;
|
|
739
|
+
/**
|
|
740
|
+
* Design -> framebuffer pixels as a 2x3 in the draw-list's `Transform2D` order
|
|
741
|
+
* `[xx, xy, yx, yy, originX, originY]`, with Y measured DOWN from the top. This
|
|
742
|
+
* is what `./clip-stack` turns into a scissor box.
|
|
743
|
+
*/
|
|
744
|
+
readonly toFramebuffer: Float32Array;
|
|
745
|
+
/** The drawing buffer's REAL size — see {@link CanvasStage.setStageSize}. */
|
|
746
|
+
readonly framebufferWidth: number;
|
|
747
|
+
readonly framebufferHeight: number;
|
|
748
|
+
}
|
|
749
|
+
interface CanvasStageOptions {
|
|
750
|
+
canvas: StageCanvas;
|
|
751
|
+
/** The scene's own coordinate extent, e.g. 1920x1080. */
|
|
752
|
+
designWidth: number;
|
|
753
|
+
designHeight: number;
|
|
754
|
+
/**
|
|
755
|
+
* Request an alpha-capable drawing buffer. Omitted preserves the stage's
|
|
756
|
+
* compositing default (`true`); an opaque scene may opt out when it paints
|
|
757
|
+
* every output pixel itself.
|
|
758
|
+
*/
|
|
759
|
+
alpha?: boolean;
|
|
760
|
+
/**
|
|
761
|
+
* Called after `webglcontextlost`. Every GL object the caller holds is dead:
|
|
762
|
+
* drop programs, buffers and textures (`CanvasTextureCache.reset`,
|
|
763
|
+
* `CanvasExecutor.invalidate`) without calling into GL to free them.
|
|
764
|
+
*/
|
|
765
|
+
onContextLost?(): void;
|
|
766
|
+
/** Called after `webglcontextrestored`: rebuild programs, buffers, textures. */
|
|
767
|
+
onContextRestored?(): void;
|
|
768
|
+
}
|
|
769
|
+
interface CanvasStage {
|
|
770
|
+
readonly canvas: StageCanvas;
|
|
771
|
+
readonly gl: WebGL2RenderingContext;
|
|
772
|
+
readonly designWidth: number;
|
|
773
|
+
readonly designHeight: number;
|
|
774
|
+
/** The drawing buffer's real width/height, which may be smaller than asked. */
|
|
775
|
+
readonly stageWidth: number;
|
|
776
|
+
readonly stageHeight: number;
|
|
777
|
+
/** True between `webglcontextlost` and `webglcontextrestored`. */
|
|
778
|
+
readonly contextLost: boolean;
|
|
779
|
+
/**
|
|
780
|
+
* Whether the context actually has an alpha channel. This is read from the
|
|
781
|
+
* created context when available, rather than assuming the request won.
|
|
782
|
+
*/
|
|
783
|
+
readonly alpha: boolean;
|
|
784
|
+
/**
|
|
785
|
+
* Size the backing store EXACTLY. Not grow-only: a stage that kept the largest
|
|
786
|
+
* size it had ever been asked for would keep a 4K drawing buffer alive after a
|
|
787
|
+
* window shrank, and would leave `gl.viewport` and the projection describing a
|
|
788
|
+
* buffer bigger than the one being presented. Re-reads the ACHIEVED size
|
|
789
|
+
* afterwards, because setting `canvas.width` only REQUESTS an allocation — an
|
|
790
|
+
* implementation may return a smaller buffer, and trusting the attribute over
|
|
791
|
+
* the buffer is how a renderer ends up drawing into rows that do not exist.
|
|
792
|
+
*/
|
|
793
|
+
setStageSize(width: number, height: number): void;
|
|
794
|
+
/** Change the design extent (a scene that re-lays-out for a new aspect). */
|
|
795
|
+
setDesignSize(width: number, height: number): void;
|
|
796
|
+
/** The current projection. The returned object is reused; read it, don't keep it. */
|
|
797
|
+
projection(): StageProjection;
|
|
798
|
+
/** Set `gl.viewport` to the whole drawing buffer. */
|
|
799
|
+
applyViewport(): void;
|
|
800
|
+
/** Detach the context-loss listeners. Does not delete the context. */
|
|
801
|
+
dispose(): void;
|
|
802
|
+
}
|
|
803
|
+
/** The attributes the stage asks for, exported so a test can assert the contract
|
|
804
|
+
* rather than restate it. */
|
|
805
|
+
declare const STAGE_CONTEXT_ATTRIBUTES: Readonly<WebGLContextAttributes>;
|
|
806
|
+
/**
|
|
807
|
+
* Create a stage on `canvas`, or `null` when the browser gives no WebGL2 context.
|
|
808
|
+
*
|
|
809
|
+
* Note what is NOT here: no software-renderer refusal. `html`'s shared context
|
|
810
|
+
* declines SwiftShader/llvmpipe because running full-screen procedural fragment
|
|
811
|
+
* shaders on a CPU rasterizer costs more than the CSS fallback it has. The stage
|
|
812
|
+
* has no fallback to fall back TO — it is the renderer — and its fragment shader
|
|
813
|
+
* is a texture fetch and a multiply, which software GL runs perfectly well. The
|
|
814
|
+
* decision of whether this device should use the canvas renderer at all belongs
|
|
815
|
+
* to the consumer, one level up, where the alternative is known.
|
|
816
|
+
*/
|
|
817
|
+
declare function createCanvasStage(options: CanvasStageOptions): CanvasStage | null;
|
|
818
|
+
//#endregion
|
|
819
|
+
//#region src/glyph-pass.d.ts
|
|
820
|
+
/**
|
|
821
|
+
* The seam between the executor and whatever draws glyph outlines.
|
|
822
|
+
*
|
|
823
|
+
* STRUCTURAL, AND IN THE CORE, SO THE BARREL STAYS FREE OF A GLYPH RENDERER. The only
|
|
824
|
+
* implementation this repo ships is backed by `@godot-scene-web/hb-gpu` and lives on the
|
|
825
|
+
* `./glyphs` subpath, for two reasons that are both about cost rather than taste: the main
|
|
826
|
+
* barrel's export list is mirrored by hand-maintained ambient `.d.ts` files in
|
|
827
|
+
* `../sts2-couch-coop` and `../spirectl` (see `AGENTS.md`), so every name added there is work
|
|
828
|
+
* downstream; and a scene with no text should not pull a glyph renderer, its wasm loader and its
|
|
829
|
+
* atlas into the bundle. A consumer that wants glyphs imports the adapter and injects it exactly
|
|
830
|
+
* the way `CanvasExecutorOptions.white` is injected today.
|
|
831
|
+
*
|
|
832
|
+
* ONE METHOD, AND NO FRAME LIFECYCLE. There is no `begin`/`end` here on purpose: the executor
|
|
833
|
+
* flushes its pending batch before calling {@link GlyphPass.drawRun} and restores its own GL state
|
|
834
|
+
* after, so a pass has nothing to get wrong about when a frame starts. Everything it needs about
|
|
835
|
+
* the frame is in the two arguments.
|
|
836
|
+
*/
|
|
837
|
+
interface GlyphPass {
|
|
838
|
+
/**
|
|
839
|
+
* Draw ONE glyph run, in the GL state the executor is currently in.
|
|
840
|
+
*
|
|
841
|
+
* ONE DRAW CALL PER RUN, and that is a property of the mechanism rather than a thing to optimise
|
|
842
|
+
* away later: hb-gpu's program carries the run's colour and its model matrix as UNIFORMS, so two
|
|
843
|
+
* runs can only share a draw when both are identical, and the model matrix in particular cannot
|
|
844
|
+
* be moved onto the instance without also moving the dilation `hb_gpu_dilate` computes through
|
|
845
|
+
* it. A caller that wants fewer draws should merge runs when it BUILDS the list.
|
|
846
|
+
*
|
|
847
|
+
* `run` is the executor's own reusable view and is valid only for the duration of the call —
|
|
848
|
+
* `run.slots` and `run.positions` are overwritten by the next `readGlyphs`. Read what is needed
|
|
849
|
+
* and return.
|
|
850
|
+
*
|
|
851
|
+
* `projection` is the frame's, and the pass is expected to honour BOTH halves of it: `toClip`
|
|
852
|
+
* maps design space (the space `run.positions` are in, after `run.m`) to clip space, and
|
|
853
|
+
* `framebufferWidth`/`framebufferHeight` is the achieved drawing buffer, which is what a
|
|
854
|
+
* resolution-independent glyph shader measures a screen pixel against. They differ by the
|
|
855
|
+
* device-pixel ratio and conflating them is a dilation error, not a placement error.
|
|
856
|
+
*
|
|
857
|
+
* WHAT THE PASS MAY ASSUME on entry: `BLEND` is enabled, `SCISSOR_TEST` is enabled and the
|
|
858
|
+
* scissor box is the frame's current clip, the viewport is the whole drawing buffer, and the
|
|
859
|
+
* pending quad batch has already been drawn. WHAT IT MAY LEAVE DIRTY: the program, the bound
|
|
860
|
+
* VAO, `ARRAY_BUFFER`, texture unit 0 and the blend equation/function — the executor rebinds and
|
|
861
|
+
* re-invalidates all of those. It must NOT clear, must NOT touch the viewport, and must NOT
|
|
862
|
+
* touch `SCISSOR_TEST` or the scissor box, because that is what clips the run.
|
|
863
|
+
*/
|
|
864
|
+
drawRun(run: GlyphsView, projection: StageProjection): {
|
|
865
|
+
glyphs: number;
|
|
866
|
+
drawCalls: number;
|
|
867
|
+
};
|
|
868
|
+
/**
|
|
869
|
+
* Draw consecutive glyph commands as one ordered submission. Optional so existing passes keep
|
|
870
|
+
* their exact one-command path. The executor only calls this for physically adjacent glyph
|
|
871
|
+
* commands in an unbroken clip/mask scope; implementations must preserve the supplied order.
|
|
872
|
+
*/
|
|
873
|
+
drawRuns?(runs: readonly GlyphsView[], projection: StageProjection): {
|
|
874
|
+
glyphs: number;
|
|
875
|
+
drawCalls: number;
|
|
876
|
+
};
|
|
877
|
+
/** Conservative residency admission for `drawRuns`; false keeps commands separate. */
|
|
878
|
+
canBatchRuns?(runs: readonly GlyphsView[]): boolean;
|
|
879
|
+
}
|
|
880
|
+
//#endregion
|
|
881
|
+
export { DamageRect as $, DrawListPatchView as A, ScreenEffectDrawContext as B, DRAW_TEXTURED_MESH as C, DrawListFragment as D, DrawList as E, GlyphsView as F, createDrawListPatchView as G, createClipRectView as H, NinePatchView as I, createPolylineView as J, createGlyphsView as K, PolylineView as L, ExternalEffectDrawContext as M, FLIP_H as N, DrawListFragmentPatch as O, FLIP_V as P, CommandBounds as Q, QuadView as R, DRAW_SCREEN_EFFECT as S, DrawCommandName as T, createDrawList as U, TexturedMeshView as V, createDrawListFragment as W, createTexturedMeshView as X, createQuadView as Y, setViewColorMatrix as Z, DRAW_EXTERNAL_EFFECT as _, StageCanvas as a, RETAINED_MAX_DAMAGE_COVERAGE as at, DRAW_POLYLINE as b, BLEND_ADD as c, createDamageTiles as ct, BLEND_SUB as d, outsetDamageRect as dt, DamageTile as et, BlendMode as f, transformDamageRect as ft, DRAW_COMMAND_NAMES as g, DRAW_CLIP_PUSH as h, STAGE_CONTEXT_ATTRIBUTES as i, RETAINED_DAMAGE_TILE_SIZE as it, ExternalEffectDrawCommand as j, DrawListOptions as k, BLEND_MIX as l, damageIntersects as lt, DRAW_CLIP_POP as m, CanvasStage as n, DamageTransform as nt, StageProjection as o, commandDamageBounds as ot, ClipRectView as p, unionDamageRect as pt, createNinePatchView as q, CanvasStageOptions as r, DirtyRect as rt, createCanvasStage as s, createDamageRect as st, GlyphPass as t, DamageTiles as tt, BLEND_MUL as u, isDamageEmpty as ut, DRAW_GLYPHS as v, DrawCommandKind as w, DRAW_QUAD as x, DRAW_NINE_PATCH as y, ScreenEffectDrawCommand as z };
|
|
882
|
+
//# sourceMappingURL=glyph-pass-DfQlp_IH.d.ts.map
|