@swf2es/render 0.1.20 → 0.1.21
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/dist/filter-plan.d.ts +5 -2
- package/dist/filter-plan.d.ts.map +1 -1
- package/dist/filter-plan.js +9 -6
- package/dist/filter-plan.js.map +1 -1
- package/dist/filter-shaders.d.ts +1 -1
- package/dist/filter-shaders.d.ts.map +1 -1
- package/dist/filter-shaders.js +3 -3
- package/dist/webgl/atlas.d.ts +8 -1
- package/dist/webgl/atlas.d.ts.map +1 -1
- package/dist/webgl/atlas.js +17 -2
- package/dist/webgl/atlas.js.map +1 -1
- package/dist/webgl/draws.d.ts +6 -3
- package/dist/webgl/draws.d.ts.map +1 -1
- package/dist/webgl/draws.js +23 -8
- package/dist/webgl/draws.js.map +1 -1
- package/dist/webgl/filters.d.ts +10 -0
- package/dist/webgl/filters.d.ts.map +1 -1
- package/dist/webgl/filters.js +50 -1
- package/dist/webgl/filters.js.map +1 -1
- package/dist/webgl/glyph-tables.d.ts +44 -0
- package/dist/webgl/glyph-tables.d.ts.map +1 -0
- package/dist/webgl/glyph-tables.js +148 -0
- package/dist/webgl/glyph-tables.js.map +1 -0
- package/dist/webgl/programs.d.ts +17 -5
- package/dist/webgl/programs.d.ts.map +1 -1
- package/dist/webgl/programs.js +36 -17
- package/dist/webgl/programs.js.map +1 -1
- package/package.json +2 -2
- package/src/filter-plan.ts +9 -6
- package/src/filter-shaders.ts +3 -3
- package/src/webgl/atlas.ts +23 -4
- package/src/webgl/draws.ts +25 -7
- package/src/webgl/filters.ts +55 -0
- package/src/webgl/glyph-tables.ts +179 -0
- package/src/webgl/programs.ts +36 -17
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import type { GlState } from "@swf2es/context3d/device";
|
|
2
|
+
import { TABLE_ENTRIES, TABLE_LINE, TABLE_SLOT, TABLE_UNIT } from "./programs.js";
|
|
3
|
+
|
|
4
|
+
/** Floats a slot: its texels' four each. */
|
|
5
|
+
const SLOT_FLOATS = TABLE_SLOT * 4;
|
|
6
|
+
/** Slots a line of the texture. */
|
|
7
|
+
const SLOTS_A_LINE = TABLE_LINE / TABLE_SLOT;
|
|
8
|
+
/** Floats a text's mode and table take of a slot, compared and hashed. */
|
|
9
|
+
const USED_FLOATS = 4 + TABLE_ENTRIES * 4;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Advanced text's modes and CSM cutoff tables (GeometryData.glyphs), each
|
|
13
|
+
* distinct one in a slot of a float texture the glyph program reads by
|
|
14
|
+
* texelFetch: the mode in a slot's first texel, its table's entries after
|
|
15
|
+
* it. They were a uniform array the shader indexed by each glyph's table
|
|
16
|
+
* start, the access that hung Intel's GPUs in the fill program's run table
|
|
17
|
+
* (#717), and uploaded whole for every text drawn. Texts alike share a slot; slots are only
|
|
18
|
+
* added, between the glyph atlas' repacks, which take them all afresh
|
|
19
|
+
* (reset), or once the texture is as large as it may be (full). Slot 0 is
|
|
20
|
+
* left for a text past the room (scratch), uploaded as it is drawn.
|
|
21
|
+
*/
|
|
22
|
+
export class GlyphTables {
|
|
23
|
+
private readonly gl: WebGL2RenderingContext;
|
|
24
|
+
private readonly state: GlState;
|
|
25
|
+
texture: WebGLTexture | null = null;
|
|
26
|
+
/** The slots' floats as uploaded and to upload, and the texture's lines. */
|
|
27
|
+
private data = new Float32Array(SLOT_FLOATS * SLOTS_A_LINE);
|
|
28
|
+
private words = new Uint32Array(this.data.buffer);
|
|
29
|
+
private lines = 0;
|
|
30
|
+
private lineMost = 1;
|
|
31
|
+
/** Slots taken, the first not yet uploaded, and whether the texture's lines are all taken. */
|
|
32
|
+
private count = 1;
|
|
33
|
+
private uploaded = 1;
|
|
34
|
+
full = false;
|
|
35
|
+
/** The first slot by its contents' hash, then the next of that hash, -1 for none. */
|
|
36
|
+
private readonly byHash = new Map<number, number>();
|
|
37
|
+
private next = new Int32Array(SLOTS_A_LINE);
|
|
38
|
+
|
|
39
|
+
constructor(gl: WebGL2RenderingContext, state: GlState) {
|
|
40
|
+
this.gl = gl;
|
|
41
|
+
this.state = state;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** No texture yet, nor slots, as the context comes or comes back. */
|
|
45
|
+
setUp(): void {
|
|
46
|
+
this.texture = null;
|
|
47
|
+
this.lines = 0;
|
|
48
|
+
this.lineMost = this.gl.getParameter(this.gl.MAX_TEXTURE_SIZE) as number;
|
|
49
|
+
this.reset();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Every slot but the scratch let go of, for the texts to take theirs again. */
|
|
53
|
+
reset(): void {
|
|
54
|
+
this.count = 1;
|
|
55
|
+
this.uploaded = 1;
|
|
56
|
+
this.full = false;
|
|
57
|
+
this.byHash.clear();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The slot holding `mode` and `table`, taken if none does; -1 where the room is full. */
|
|
61
|
+
slot(mode: Float32Array, table: Float32Array): number {
|
|
62
|
+
let hash = 0x811c9dc5;
|
|
63
|
+
const modeWords = new Uint32Array(mode.buffer, mode.byteOffset, 4);
|
|
64
|
+
const tableWords = new Uint32Array(table.buffer, table.byteOffset, TABLE_ENTRIES * 4);
|
|
65
|
+
for (let i = 0; i < 4; i++) {
|
|
66
|
+
hash = Math.imul(hash ^ modeWords[i], 0x01000193);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (let i = 0; i < tableWords.length; i++) {
|
|
70
|
+
hash = Math.imul(hash ^ tableWords[i], 0x01000193);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const words = this.words;
|
|
74
|
+
let s = this.byHash.get(hash) ?? -1;
|
|
75
|
+
for (; s >= 0; s = this.next[s]) {
|
|
76
|
+
const at = s * SLOT_FLOATS;
|
|
77
|
+
let k = 0;
|
|
78
|
+
while (k < 4 && words[at + k] === modeWords[k]) {
|
|
79
|
+
k++;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
while (k < USED_FLOATS && words[at + k] === tableWords[k - 4]) {
|
|
83
|
+
k++;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (k === USED_FLOATS) {
|
|
87
|
+
return s;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (this.count >= this.lineMost * SLOTS_A_LINE) {
|
|
92
|
+
this.full = true;
|
|
93
|
+
return -1;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
s = this.count++;
|
|
97
|
+
if (this.count * SLOT_FLOATS > this.data.length) {
|
|
98
|
+
const grown = new Float32Array(this.data.length * 2);
|
|
99
|
+
grown.set(this.data);
|
|
100
|
+
this.data = grown;
|
|
101
|
+
this.words = new Uint32Array(grown.buffer);
|
|
102
|
+
const next = new Int32Array(this.next.length * 2);
|
|
103
|
+
next.set(this.next);
|
|
104
|
+
this.next = next;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.write(s, mode, table);
|
|
108
|
+
this.next[s] = this.byHash.get(hash) ?? -1;
|
|
109
|
+
this.byHash.set(hash, s);
|
|
110
|
+
return s;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** The slots taken since uploaded: into a texture grown to hold them, all of them if grown. */
|
|
114
|
+
upload(): void {
|
|
115
|
+
const gl = this.gl;
|
|
116
|
+
if (this.uploaded === this.count || gl.isContextLost()) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const lines = Math.ceil(this.count / SLOTS_A_LINE);
|
|
121
|
+
let from = Math.floor(this.uploaded / SLOTS_A_LINE);
|
|
122
|
+
if (!this.texture || lines > this.lines) {
|
|
123
|
+
if (this.texture) {
|
|
124
|
+
this.state.dropTexture(this.texture);
|
|
125
|
+
gl.deleteTexture(this.texture);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this.lines = Math.min(this.lineMost, Math.max(lines, this.lines * 2));
|
|
129
|
+
this.texture = gl.createTexture() as WebGLTexture;
|
|
130
|
+
this.state.bindTexture(TABLE_UNIT, this.texture);
|
|
131
|
+
this.state.activeTexture(TABLE_UNIT);
|
|
132
|
+
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA32F, TABLE_LINE, this.lines);
|
|
133
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
134
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
135
|
+
from = 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
this.state.bindTexture(TABLE_UNIT, this.texture);
|
|
139
|
+
this.state.activeTexture(TABLE_UNIT);
|
|
140
|
+
gl.texSubImage2D(
|
|
141
|
+
gl.TEXTURE_2D,
|
|
142
|
+
0,
|
|
143
|
+
0,
|
|
144
|
+
from,
|
|
145
|
+
TABLE_LINE,
|
|
146
|
+
lines - from,
|
|
147
|
+
gl.RGBA,
|
|
148
|
+
gl.FLOAT,
|
|
149
|
+
this.data,
|
|
150
|
+
from * TABLE_LINE * 4,
|
|
151
|
+
);
|
|
152
|
+
this.uploaded = this.count;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** The scratch slot made to hold `mode` and `table` now, for a text drawn next past the room. */
|
|
156
|
+
scratch(mode: Float32Array, table: Float32Array): number {
|
|
157
|
+
this.write(0, mode, table);
|
|
158
|
+
const gl = this.gl;
|
|
159
|
+
this.state.bindTexture(TABLE_UNIT, this.texture);
|
|
160
|
+
this.state.activeTexture(TABLE_UNIT);
|
|
161
|
+
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, TABLE_SLOT, 1, gl.RGBA, gl.FLOAT, this.data, 0);
|
|
162
|
+
return 0;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private write(s: number, mode: Float32Array, table: Float32Array): void {
|
|
166
|
+
const at = s * SLOT_FLOATS;
|
|
167
|
+
this.data.set(mode.subarray(0, 4), at);
|
|
168
|
+
this.data.set(table.subarray(0, TABLE_ENTRIES * 4), at + 4);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** The texture made on the context, deleted. */
|
|
172
|
+
destroy(): void {
|
|
173
|
+
if (this.texture) {
|
|
174
|
+
this.state.dropTexture(this.texture);
|
|
175
|
+
this.gl.deleteTexture(this.texture);
|
|
176
|
+
this.texture = null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
package/src/webgl/programs.ts
CHANGED
|
@@ -37,6 +37,13 @@ export const DRAW_AT = 8;
|
|
|
37
37
|
|
|
38
38
|
/** The most cutoff table entries a text's draw takes, as the player's text/saffron.ts builds them. */
|
|
39
39
|
export const TABLE_ENTRIES = 48;
|
|
40
|
+
/**
|
|
41
|
+
* Texels a text's slot in the glyph tables' texture (GlyphTables): its mode,
|
|
42
|
+
* then its table's entries; and texels a line of it. Powers of two, which
|
|
43
|
+
* the vertex shader splits a slot's texel into column and line with.
|
|
44
|
+
*/
|
|
45
|
+
export const TABLE_SLOT = 64;
|
|
46
|
+
export const TABLE_LINE = 1024;
|
|
40
47
|
/** Segments a row of the outlines' texture, as the player's text/saffron.ts stores them. */
|
|
41
48
|
export const OUTLINE_ROW = 1024;
|
|
42
49
|
|
|
@@ -74,6 +81,8 @@ export const RUN_UNIT = FILL_UNIT + FILL_TEXTURES;
|
|
|
74
81
|
export const MERGED_UNIT = RUN_UNIT + 1;
|
|
75
82
|
/** The merged fills' rows a line of their integer texture: a power of two, as for RUN_MOST. */
|
|
76
83
|
export const MERGED_LINE = 1024;
|
|
84
|
+
/** The glyph tables' unit (GlyphTables), past the merged fills' rows. */
|
|
85
|
+
export const TABLE_UNIT = MERGED_UNIT + 1;
|
|
77
86
|
|
|
78
87
|
/**
|
|
79
88
|
* Places a vertex by its row's transform, on a quarter of a pixel of
|
|
@@ -299,11 +308,14 @@ void main(void) {
|
|
|
299
308
|
* pixels to Flash's, uUnit of them a stage pixel, on their grid from the
|
|
300
309
|
* stage's origin, a BitmapData's in a draw. A glyph's pen stands where its
|
|
301
310
|
* text's origin, at a whole pixel, puts it, snapped as its grid fits it
|
|
302
|
-
* (
|
|
303
|
-
* and its quad reaches out past its outline by the furthest
|
|
304
|
-
* colour is drawn under the row's colour transform, light or
|
|
305
|
-
* chooses its table.
|
|
306
|
-
*
|
|
311
|
+
* (its mode: gridFitType 0 none, 1 pixel, 2 subpixel, thickness,
|
|
312
|
+
* sharpness), and its quad reaches out past its outline by the furthest
|
|
313
|
+
* cutoff. Its colour is drawn under the row's colour transform, light or
|
|
314
|
+
* dark as adl chooses its table. The text's mode and tables are in its slot
|
|
315
|
+
* of uTables (uTable; GlyphTables), read by texelFetch: not a uniform array
|
|
316
|
+
* indexed by each glyph's table start, the access that hung Intel's GPUs
|
|
317
|
+
* in the fill program's run table. aOrigin: the pen, and its light and dark tables,
|
|
318
|
+
* each its first entry plus 64 times its entries; aGlyph: a corner of its
|
|
307
319
|
* outline's bounds and the way out from it; aRun: its segments, first and
|
|
308
320
|
* count, the pixels of a unit of its outline, and its size.
|
|
309
321
|
*/
|
|
@@ -318,8 +330,8 @@ uniform highp sampler2D uRows;
|
|
|
318
330
|
uniform int uRow;
|
|
319
331
|
uniform vec4 uView;
|
|
320
332
|
uniform float uUnit;
|
|
321
|
-
uniform
|
|
322
|
-
uniform
|
|
333
|
+
uniform highp sampler2D uTables;
|
|
334
|
+
uniform int uTable;
|
|
323
335
|
out vec2 vPixel;
|
|
324
336
|
flat out vec2 vPen;
|
|
325
337
|
flat out vec4 vToGlyph;
|
|
@@ -327,10 +339,16 @@ flat out vec4 vToPixel;
|
|
|
327
339
|
flat out vec4 vCut;
|
|
328
340
|
flat out vec4 vShown;
|
|
329
341
|
|
|
342
|
+
// Texel i of the text's slot: its mode, then its tables' entries.
|
|
343
|
+
vec4 slot(int i) {
|
|
344
|
+
int at = uTable * ${TABLE_SLOT} + i;
|
|
345
|
+
return texelFetch(uTables, ivec2(at & ${TABLE_LINE - 1}, at >> ${Math.log2(TABLE_LINE)}), 0);
|
|
346
|
+
}
|
|
347
|
+
|
|
330
348
|
// The cutoffs at ppem by the table of entries start to start + count, as text/csm.ts' cutoffs.
|
|
331
|
-
vec2 cutoffs(int start, int count, float ppem) {
|
|
332
|
-
vec4 first =
|
|
333
|
-
vec4 last =
|
|
349
|
+
vec2 cutoffs(vec4 mode, int start, int count, float ppem) {
|
|
350
|
+
vec4 first = slot(1 + start);
|
|
351
|
+
vec4 last = slot(start + count);
|
|
334
352
|
vec2 c = first.yz;
|
|
335
353
|
if (ppem >= last.x) {
|
|
336
354
|
c = last.yz;
|
|
@@ -340,9 +358,9 @@ vec2 cutoffs(int start, int count, float ppem) {
|
|
|
340
358
|
break;
|
|
341
359
|
}
|
|
342
360
|
|
|
343
|
-
vec4 b =
|
|
361
|
+
vec4 b = slot(1 + start + i);
|
|
344
362
|
if (ppem <= b.x) {
|
|
345
|
-
vec4 a =
|
|
363
|
+
vec4 a = slot(start + i);
|
|
346
364
|
c = mix(a.yz, b.yz, (ppem - a.x) / (b.x - a.x));
|
|
347
365
|
break;
|
|
348
366
|
}
|
|
@@ -350,8 +368,8 @@ vec2 cutoffs(int start, int count, float ppem) {
|
|
|
350
368
|
}
|
|
351
369
|
|
|
352
370
|
float unit = 0.0001 * ppem;
|
|
353
|
-
float t = clamp(
|
|
354
|
-
float s = clamp(
|
|
371
|
+
float t = clamp(mode.y, -200.0, 200.0);
|
|
372
|
+
float s = clamp(mode.z, -400.0, 400.0);
|
|
355
373
|
c -= unit * vec2(t + s * 0.5, t - s * 0.5);
|
|
356
374
|
if (c.x < c.y) {
|
|
357
375
|
c = vec2((c.x + c.y) * 0.5);
|
|
@@ -366,6 +384,7 @@ void main(void) {
|
|
|
366
384
|
vec4 t = texelFetch(uRows, at + ivec2(1, 0), 0);
|
|
367
385
|
vec4 mul = texelFetch(uRows, at + ivec2(2, 0), 0);
|
|
368
386
|
vec4 add = texelFetch(uRows, at + ivec2(3, 0), 0);
|
|
387
|
+
vec4 mode = slot(0);
|
|
369
388
|
// The text's pixels to Flash's.
|
|
370
389
|
mat2 linear = mat2(m.xy, m.zw) * uUnit;
|
|
371
390
|
vec2 origin = t.xy * uUnit;
|
|
@@ -373,9 +392,9 @@ void main(void) {
|
|
|
373
392
|
float across = max(abs(linear[0][1]), abs(linear[1][0]));
|
|
374
393
|
if (across > max(abs(linear[0][0]), abs(linear[1][1])) / 65536.0) {
|
|
375
394
|
// Turned or skewed: no grid.
|
|
376
|
-
} else if (
|
|
395
|
+
} else if (mode.x == 1.0) {
|
|
377
396
|
pen = vec2(roundEven(pen.x * 8.0) / 8.0 + 0.5, roundEven(pen.y));
|
|
378
|
-
} else if (
|
|
397
|
+
} else if (mode.x == 2.0) {
|
|
379
398
|
pen = vec2(roundEven(pen.x * 4.0) / 4.0 + 0.5, roundEven(pen.y));
|
|
380
399
|
} else {
|
|
381
400
|
pen = roundEven(pen * 4.0) / 4.0 + vec2(0.5, 0.0);
|
|
@@ -386,7 +405,7 @@ void main(void) {
|
|
|
386
405
|
vec4 shown = clamp(aColor * mul + add, 0.0, 1.0);
|
|
387
406
|
bool light = dot(floor(shown.rgb * 255.0 + 0.5), vec3(30.0, 59.0, 11.0)) > 20000.0;
|
|
388
407
|
int table = int(light ? aOrigin.z : aOrigin.w);
|
|
389
|
-
vec2 cut = cutoffs(table & 63, table >> 6, ppem);
|
|
408
|
+
vec2 cut = cutoffs(mode, table & 63, table >> 6, ppem);
|
|
390
409
|
mat2 toPixel = linear * aRun.z;
|
|
391
410
|
float margin = max(abs(cut.x), abs(cut.y)) + 1.0;
|
|
392
411
|
float det = determinant(toPixel);
|