@swf2es/render 0.0.0-stage → 0.1.1

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 (143) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +17 -2
  3. package/dist/commands.d.ts +67 -0
  4. package/dist/commands.d.ts.map +1 -0
  5. package/dist/commands.js +93 -0
  6. package/dist/commands.js.map +1 -0
  7. package/dist/filter-plan.d.ts +110 -0
  8. package/dist/filter-plan.d.ts.map +1 -0
  9. package/dist/filter-plan.js +480 -0
  10. package/dist/filter-plan.js.map +1 -0
  11. package/dist/filter-shaders.d.ts +63 -0
  12. package/dist/filter-shaders.d.ts.map +1 -0
  13. package/dist/filter-shaders.js +350 -0
  14. package/dist/filter-shaders.js.map +1 -0
  15. package/dist/filters.d.ts +107 -0
  16. package/dist/filters.d.ts.map +1 -0
  17. package/dist/filters.js +187 -0
  18. package/dist/filters.js.map +1 -0
  19. package/dist/geometry.d.ts +83 -0
  20. package/dist/geometry.d.ts.map +1 -0
  21. package/dist/geometry.js +93 -0
  22. package/dist/geometry.js.map +1 -0
  23. package/dist/index.d.ts +11 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +13 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/keeps.d.ts +17 -0
  28. package/dist/keeps.d.ts.map +1 -0
  29. package/dist/keeps.js +17 -0
  30. package/dist/keeps.js.map +1 -0
  31. package/dist/kernel-shaders.d.ts +74 -0
  32. package/dist/kernel-shaders.d.ts.map +1 -0
  33. package/dist/kernel-shaders.js +323 -0
  34. package/dist/kernel-shaders.js.map +1 -0
  35. package/dist/measure.d.ts +56 -0
  36. package/dist/measure.d.ts.map +1 -0
  37. package/dist/measure.js +287 -0
  38. package/dist/measure.js.map +1 -0
  39. package/dist/plan.d.ts +14 -0
  40. package/dist/plan.d.ts.map +1 -0
  41. package/dist/plan.js +247 -0
  42. package/dist/plan.js.map +1 -0
  43. package/dist/registry.d.ts +49 -0
  44. package/dist/registry.d.ts.map +1 -0
  45. package/dist/registry.js +87 -0
  46. package/dist/registry.js.map +1 -0
  47. package/dist/rows.d.ts +39 -0
  48. package/dist/rows.d.ts.map +1 -0
  49. package/dist/rows.js +60 -0
  50. package/dist/rows.js.map +1 -0
  51. package/dist/scene.d.ts +179 -0
  52. package/dist/scene.d.ts.map +1 -0
  53. package/dist/scene.js +265 -0
  54. package/dist/scene.js.map +1 -0
  55. package/dist/shelves.d.ts +23 -0
  56. package/dist/shelves.d.ts.map +1 -0
  57. package/dist/shelves.js +63 -0
  58. package/dist/shelves.js.map +1 -0
  59. package/dist/textures.d.ts +72 -0
  60. package/dist/textures.d.ts.map +1 -0
  61. package/dist/textures.js +160 -0
  62. package/dist/textures.js.map +1 -0
  63. package/dist/webgl/atlas.d.ts +63 -0
  64. package/dist/webgl/atlas.d.ts.map +1 -0
  65. package/dist/webgl/atlas.js +328 -0
  66. package/dist/webgl/atlas.js.map +1 -0
  67. package/dist/webgl/backend.d.ts +141 -0
  68. package/dist/webgl/backend.d.ts.map +1 -0
  69. package/dist/webgl/backend.js +659 -0
  70. package/dist/webgl/backend.js.map +1 -0
  71. package/dist/webgl/composite.d.ts +101 -0
  72. package/dist/webgl/composite.d.ts.map +1 -0
  73. package/dist/webgl/composite.js +293 -0
  74. package/dist/webgl/composite.js.map +1 -0
  75. package/dist/webgl/draws.d.ts +213 -0
  76. package/dist/webgl/draws.d.ts.map +1 -0
  77. package/dist/webgl/draws.js +714 -0
  78. package/dist/webgl/draws.js.map +1 -0
  79. package/dist/webgl/filters.d.ts +107 -0
  80. package/dist/webgl/filters.d.ts.map +1 -0
  81. package/dist/webgl/filters.js +351 -0
  82. package/dist/webgl/filters.js.map +1 -0
  83. package/dist/webgl/gpu-textures.d.ts +44 -0
  84. package/dist/webgl/gpu-textures.d.ts.map +1 -0
  85. package/dist/webgl/gpu-textures.js +178 -0
  86. package/dist/webgl/gpu-textures.js.map +1 -0
  87. package/dist/webgl/holds.d.ts +97 -0
  88. package/dist/webgl/holds.d.ts.map +1 -0
  89. package/dist/webgl/holds.js +453 -0
  90. package/dist/webgl/holds.js.map +1 -0
  91. package/dist/webgl/index.d.ts +2 -0
  92. package/dist/webgl/index.d.ts.map +1 -0
  93. package/dist/webgl/index.js +4 -0
  94. package/dist/webgl/index.js.map +1 -0
  95. package/dist/webgl/kernels.d.ts +64 -0
  96. package/dist/webgl/kernels.d.ts.map +1 -0
  97. package/dist/webgl/kernels.js +160 -0
  98. package/dist/webgl/kernels.js.map +1 -0
  99. package/dist/webgl/pool.d.ts +71 -0
  100. package/dist/webgl/pool.d.ts.map +1 -0
  101. package/dist/webgl/pool.js +192 -0
  102. package/dist/webgl/pool.js.map +1 -0
  103. package/dist/webgl/programs.d.ts +130 -0
  104. package/dist/webgl/programs.d.ts.map +1 -0
  105. package/dist/webgl/programs.js +582 -0
  106. package/dist/webgl/programs.js.map +1 -0
  107. package/dist/webgl/stage.d.ts +67 -0
  108. package/dist/webgl/stage.d.ts.map +1 -0
  109. package/dist/webgl/stage.js +173 -0
  110. package/dist/webgl/stage.js.map +1 -0
  111. package/dist/webgl/targets.d.ts +172 -0
  112. package/dist/webgl/targets.d.ts.map +1 -0
  113. package/dist/webgl/targets.js +323 -0
  114. package/dist/webgl/targets.js.map +1 -0
  115. package/package.json +35 -3
  116. package/src/commands.ts +98 -0
  117. package/src/filter-plan.ts +755 -0
  118. package/src/filter-shaders.ts +360 -0
  119. package/src/filters.ts +220 -0
  120. package/src/geometry.ts +143 -0
  121. package/src/index.ts +87 -0
  122. package/src/keeps.ts +21 -0
  123. package/src/kernel-shaders.ts +375 -0
  124. package/src/measure.ts +400 -0
  125. package/src/plan.ts +331 -0
  126. package/src/registry.ts +105 -0
  127. package/src/rows.ts +93 -0
  128. package/src/scene.ts +297 -0
  129. package/src/shelves.ts +70 -0
  130. package/src/textures.ts +198 -0
  131. package/src/webgl/atlas.ts +370 -0
  132. package/src/webgl/backend.ts +973 -0
  133. package/src/webgl/composite.ts +372 -0
  134. package/src/webgl/draws.ts +902 -0
  135. package/src/webgl/filters.ts +502 -0
  136. package/src/webgl/gpu-textures.ts +253 -0
  137. package/src/webgl/holds.ts +624 -0
  138. package/src/webgl/index.ts +3 -0
  139. package/src/webgl/kernels.ts +205 -0
  140. package/src/webgl/pool.ts +245 -0
  141. package/src/webgl/programs.ts +620 -0
  142. package/src/webgl/stage.ts +214 -0
  143. package/src/webgl/targets.ts +464 -0
@@ -0,0 +1,620 @@
1
+ import { ROWS_A_LINE } from "../rows.js";
2
+ import {
3
+ ADD,
4
+ ALPHA,
5
+ DARKEN,
6
+ DIFFERENCE,
7
+ ERASE,
8
+ HARDLIGHT,
9
+ INVERT,
10
+ LAYER,
11
+ LIGHTEN,
12
+ MULTIPLY,
13
+ NORMAL,
14
+ OVERLAY,
15
+ SCREEN,
16
+ SUBTRACT,
17
+ } from "../scene.js";
18
+
19
+ /** Texels a row (ROW_FLOATS over four). */
20
+ export const ROW_TEXELS = 4;
21
+
22
+ /**
23
+ * The vertex atlas' attributes, where both the fill and the mesh programs
24
+ * read them: a mesh's depth is the four bytes of its colour, as a float.
25
+ */
26
+ export const POSITION_AT = 0;
27
+ export const TEXEL_AT = 1;
28
+ export const COLOR_AT = 2;
29
+ export const DEPTH_AT = 3;
30
+ /** Advanced text's glyph quads' attributes past their position (GeometryData.glyphs), in theirs. */
31
+ export const ORIGIN_AT = 4;
32
+ export const GLYPH_AT = 5;
33
+ export const RUN_AT = 6;
34
+ export const TINT_AT = 7;
35
+
36
+ /** The most cutoff table entries a text's draw takes, as the player's text/saffron.ts builds them. */
37
+ export const TABLE_ENTRIES = 48;
38
+ /** Segments a row of the outlines' texture, as the player's text/saffron.ts stores them. */
39
+ export const OUTLINE_ROW = 1024;
40
+
41
+ /**
42
+ * The most draws a multi-draw of the fill program makes, their rows in its
43
+ * integer texture, a line of it. A power of two: the vertex shader splits a
44
+ * draw's place in the table into column and line with a mask and a shift.
45
+ */
46
+ export const RUN_MOST = 256;
47
+
48
+ /** Textures the fill program samples in one draw call, each on a unit of its own. */
49
+ export const FILL_TEXTURES = 8;
50
+
51
+ /**
52
+ * A fill draw's row and how it is drawn, in one int: the row in the bits
53
+ * below FILL_SLOT, then which of the fill program's textures it samples, one
54
+ * past its index or 0 for none, then SNAP_BIT where it snaps (SNAP). Rows
55
+ * are fewer: ROWS_A_LINE to a line of a texture of at most 16384 lines.
56
+ */
57
+ export const FILL_SLOT = 24;
58
+ export const SNAP_BIT = 1 << 28;
59
+
60
+ /** The texture unit a mesh's texture is bound to, and textures are uploaded on; the rows are on 0. */
61
+ export const TEXTURE_UNIT = 1;
62
+ /** The units a composite reads its layer and what is below from. */
63
+ export const LAYER_UNIT = 2;
64
+ export const BACK_UNIT = 3;
65
+ /** The unit advanced text's outlines are on (uploadOutlines); the filters take 4 to 6. */
66
+ export const OUTLINE_UNIT = 7;
67
+ /** The first of the units the fill program's textures are bound to (FILL_TEXTURES). */
68
+ export const FILL_UNIT = 8;
69
+ /** The multi-draw row table's unit, past the fill textures. */
70
+ export const RUN_UNIT = FILL_UNIT + FILL_TEXTURES;
71
+
72
+ /**
73
+ * Places a vertex by its row's transform, on a quarter of a pixel of
74
+ * Flash's (uUnit of them a stage pixel, from the stage's origin) where it
75
+ * snaps (SNAP), then on the target by uView (scale and offset into clip
76
+ * space), and passes on its straight colour, the row's colour transform
77
+ * and the texture it samples. uRow names the draw's row (FILL_SLOT); with
78
+ * `multi` (WEBGL_multi_draw), uRunTable names each draw's of a multi-draw by
79
+ * gl_DrawID, which is 0 in a draw of its own.
80
+ */
81
+ export const fillVertex = (multi: boolean) => `#version 300 es
82
+ ${multi ? "#extension GL_ANGLE_multi_draw : require" : ""}
83
+ precision highp float;
84
+ layout(location = ${POSITION_AT}) in vec2 aPosition;
85
+ layout(location = ${TEXEL_AT}) in vec2 aTexel;
86
+ layout(location = ${COLOR_AT}) in vec4 aColor;
87
+ uniform highp sampler2D uRows;
88
+ ${multi ? "uniform highp isampler2D uRunTable;\nuniform int uRunBase;" : "uniform int uRow;"}
89
+ uniform vec4 uView;
90
+ uniform float uUnit;
91
+ out vec4 vColor;
92
+ out vec2 vTexel;
93
+ flat out vec4 vMul;
94
+ flat out vec4 vAdd;
95
+ flat out int vSlot;
96
+
97
+ void main(void) {
98
+ int draw = ${multi ? `texelFetch(uRunTable, ivec2((uRunBase + gl_DrawID) & ${RUN_MOST - 1}, (uRunBase + gl_DrawID) >> ${Math.log2(RUN_MOST)}), 0).r` : "uRow"};
99
+ int row = draw & ${(1 << FILL_SLOT) - 1};
100
+ vSlot = (draw >> ${FILL_SLOT}) & 15;
101
+ ivec2 at = ivec2((row & ${ROWS_A_LINE - 1}) * ${ROW_TEXELS}, row >> ${Math.log2(ROWS_A_LINE)});
102
+ vec4 m = texelFetch(uRows, at, 0);
103
+ vec4 t = texelFetch(uRows, at + ivec2(1, 0), 0);
104
+ vMul = texelFetch(uRows, at + ivec2(2, 0), 0);
105
+ vAdd = texelFetch(uRows, at + ivec2(3, 0), 0);
106
+ vec2 p = vec2(m.x * aPosition.x + m.z * aPosition.y + t.x, m.y * aPosition.x + m.w * aPosition.y + t.y);
107
+ if ((draw & ${SNAP_BIT}) != 0) {
108
+ p = roundEven(p * uUnit * 4.0) / (uUnit * 4.0);
109
+ }
110
+
111
+ gl_Position = vec4(p * uView.xy + uView.zw, 0.0, 1.0);
112
+ vColor = aColor;
113
+ vTexel = aTexel;
114
+ }
115
+ `;
116
+
117
+ /**
118
+ * Texel `at` of the texture vSlot names, as only a constant may index the
119
+ * samplers. At level 0: the textures have no mipmaps and sample alike up
120
+ * close and far off, and so no derivatives are taken in the switch.
121
+ */
122
+ const slotTexel = Array.from(
123
+ { length: FILL_TEXTURES },
124
+ (_, k) => ` case ${k + 1}:
125
+ return textureLod(uTextures[${k}], at / vec2(textureSize(uTextures[${k}], 0)), 0.0);`,
126
+ ).join("\n");
127
+
128
+ /**
129
+ * Flash's colour transform on the straight colour, clamped, then
130
+ * premultiplied for blending: the vertex' colour, or with a texture the
131
+ * texel's (premultiplied in the texture) times it, as the colour batcher
132
+ * did, a clear texel staying clear.
133
+ */
134
+ export const fillFragment = `#version 300 es
135
+ precision highp float;
136
+ in vec4 vColor;
137
+ in vec2 vTexel;
138
+ flat in vec4 vMul;
139
+ flat in vec4 vAdd;
140
+ flat in int vSlot;
141
+ uniform sampler2D uTextures[${FILL_TEXTURES}];
142
+ out vec4 finalColor;
143
+
144
+ vec4 texel(vec2 at) {
145
+ switch (vSlot) {
146
+ ${slotTexel}
147
+ }
148
+
149
+ return vec4(0.0);
150
+ }
151
+
152
+ void main(void) {
153
+ vec4 straight = vColor;
154
+ if (vSlot != 0) {
155
+ vec4 t = texel(vTexel);
156
+ if (t.a == 0.0) {
157
+ finalColor = vec4(0.0);
158
+ return;
159
+ }
160
+
161
+ straight = vec4(t.rgb / t.a * vColor.rgb, t.a * vColor.a);
162
+ }
163
+
164
+ vec4 c = clamp(straight * vMul + vAdd, 0.0, 1.0);
165
+ finalColor = vec4(c.rgb * c.a, c.a);
166
+ }
167
+ `;
168
+
169
+ /**
170
+ * drawTriangles' textured triangles (MESH), placed as fills are, their u
171
+ * and v carried times their depth t, and t, which the GPU interpolates
172
+ * straight across the screen for each pixel to divide.
173
+ */
174
+ export const meshVertex = `#version 300 es
175
+ precision highp float;
176
+ layout(location = ${POSITION_AT}) in vec2 aPosition;
177
+ layout(location = ${TEXEL_AT}) in vec2 aTexel;
178
+ layout(location = ${DEPTH_AT}) in float aDepth;
179
+ uniform highp sampler2D uRows;
180
+ uniform int uRow;
181
+ uniform vec4 uView;
182
+ out vec3 vUVT;
183
+ flat out vec4 vMul;
184
+ flat out vec4 vAdd;
185
+
186
+ void main(void) {
187
+ ivec2 at = ivec2((uRow & ${ROWS_A_LINE - 1}) * ${ROW_TEXELS}, uRow >> ${Math.log2(ROWS_A_LINE)});
188
+ vec4 m = texelFetch(uRows, at, 0);
189
+ vec4 t = texelFetch(uRows, at + ivec2(1, 0), 0);
190
+ vMul = texelFetch(uRows, at + ivec2(2, 0), 0);
191
+ vAdd = texelFetch(uRows, at + ivec2(3, 0), 0);
192
+ vec2 p = vec2(m.x * aPosition.x + m.z * aPosition.y + t.x, m.y * aPosition.x + m.w * aPosition.y + t.y);
193
+ gl_Position = vec4(p * uView.xy + uView.zw, 0.0, 1.0);
194
+ vUVT = vec3(aTexel * aDepth, aDepth);
195
+ }
196
+ `;
197
+
198
+ /**
199
+ * drawTriangles' bitmap as adl samples it: u and v from 0 to 1 onto the first
200
+ * texel's centre to the last's, wrapped into that or clamped to it, read
201
+ * once a pixel of Flash's, at its top left corner; not smoothed, the nearer
202
+ * texel, the lower of two as near; smoothed, the four around, weighed.
203
+ * uFrag takes a fragment to the target's pixels from the stage's origin,
204
+ * down (x + uFrag.x, uFrag.y + uFrag.z * y), and is a pixel of Flash's
205
+ * that many of them (w). uMode: whether it repeats, whether it is smoothed,
206
+ * and whether it is a 3D object's quad (PLANE), which reads its bitmap
207
+ * otherwise. The colour transform then as on a fill.
208
+ */
209
+ export const meshFragment = `#version 300 es
210
+ precision highp float;
211
+ in vec3 vUVT;
212
+ flat in vec4 vMul;
213
+ flat in vec4 vAdd;
214
+ uniform sampler2D uTexture;
215
+ uniform vec4 uFrag;
216
+ uniform ivec3 uMode;
217
+ out vec4 finalColor;
218
+
219
+ vec4 texel(ivec2 at) {
220
+ return texelFetch(uTexture, at, 0);
221
+ }
222
+
223
+ void main(void) {
224
+ float pixel = uFrag.w;
225
+ vec2 f = vec2(gl_FragCoord.x + uFrag.x, uFrag.y + uFrag.z * gl_FragCoord.y);
226
+ vec2 size = vec2(textureSize(uTexture, 0));
227
+ if (uMode.z != 0) {
228
+ // A 3D object's quad (PLANE): its bitmap read smoothed at the pixel's centre, in its texels.
229
+ vec2 centre = (floor(f / pixel) + 0.5) * pixel;
230
+ vec3 at = vUVT + dFdx(vUVT) * (centre.x - f.x) + dFdy(vUVT) * ((centre.y - f.y) * uFrag.z);
231
+ vec4 shown = texture(uTexture, (at.z != 0.0 ? at.xy / at.z : vec2(0.0)) / size);
232
+ if (shown.a == 0.0) {
233
+ finalColor = vec4(0.0);
234
+ return;
235
+ }
236
+
237
+ vec4 s = clamp(vec4(shown.rgb / shown.a, shown.a) * vMul + vAdd, 0.0, 1.0);
238
+ finalColor = vec4(s.rgb * s.a, s.a);
239
+ return;
240
+ }
241
+
242
+ vec2 corner = floor(f / pixel) * pixel;
243
+ vec3 uvt = vUVT + dFdx(vUVT) * (corner.x - f.x) + dFdy(vUVT) * ((corner.y - f.y) * uFrag.z);
244
+ vec2 uv = uvt.z != 0.0 ? uvt.xy / uvt.z : vec2(0.0);
245
+ // As adl draws one, a bitmap a texel high shows its first texel throughout.
246
+ if (size.y == 1.0) {
247
+ uv = vec2(0.0);
248
+ }
249
+
250
+ // A hair nearer 0, as adl's come out, stepped across the triangle in fixed point: 1 wraps to
251
+ // the last texel, not the first, and a corner halfway between texels takes the one nearer 0.
252
+ uv -= sign(uv) / 16384.0;
253
+ uv = uMode.x != 0 ? fract(uv) : clamp(uv, 0.0, 1.0);
254
+ vec2 p = uv * (size - 1.0);
255
+ vec4 color;
256
+ if (uMode.y != 0) {
257
+ vec2 below = floor(p);
258
+ vec2 w = p - below;
259
+ ivec2 a = ivec2(below);
260
+ ivec2 b = min(a + 1, ivec2(size) - 1);
261
+ color = mix(
262
+ mix(texel(a), texel(ivec2(b.x, a.y)), w.x),
263
+ mix(texel(ivec2(a.x, b.y)), texel(b), w.x),
264
+ w.y
265
+ );
266
+ } else {
267
+ color = texel(ivec2(floor(p + 0.5)));
268
+ }
269
+
270
+ if (color.a == 0.0) {
271
+ finalColor = vec4(0.0);
272
+ return;
273
+ }
274
+
275
+ vec4 c = clamp(vec4(color.rgb / color.a, color.a) * vMul + vAdd, 0.0, 1.0);
276
+ finalColor = vec4(c.rgb * c.a, c.a);
277
+ }
278
+ `;
279
+
280
+ /**
281
+ * Advanced text's glyph quads (player's text/saffron.ts), placed by the
282
+ * row's transform: the text's
283
+ * pixels to Flash's, uUnit of them a stage pixel, on their grid from the
284
+ * stage's origin, a BitmapData's in a draw. A glyph's pen stands where its
285
+ * text's origin, at a whole pixel, puts it, snapped as its grid fits it
286
+ * (uMode: gridFitType 0 none, 1 pixel, 2 subpixel, thickness, sharpness),
287
+ * and its quad reaches out past its outline by the furthest cutoff. Its
288
+ * colour is drawn under the row's colour transform, light or dark as adl
289
+ * chooses its table. aOrigin: the pen, and its light and dark tables, each
290
+ * its first entry plus 64 times its entries; aGlyph: a corner of its
291
+ * outline's bounds and the way out from it; aRun: its segments, first and
292
+ * count, the pixels of a unit of its outline, and its size.
293
+ */
294
+ export const glyphsVertex = `#version 300 es
295
+ precision highp float;
296
+ layout(location = ${POSITION_AT}) in vec2 aPosition;
297
+ layout(location = ${ORIGIN_AT}) in vec4 aOrigin;
298
+ layout(location = ${GLYPH_AT}) in vec4 aGlyph;
299
+ layout(location = ${RUN_AT}) in vec4 aRun;
300
+ layout(location = ${TINT_AT}) in vec4 aColor;
301
+ uniform highp sampler2D uRows;
302
+ uniform int uRow;
303
+ uniform vec4 uView;
304
+ uniform float uUnit;
305
+ uniform vec4 uMode;
306
+ uniform vec4 uTable[${TABLE_ENTRIES}];
307
+ out vec2 vPixel;
308
+ flat out vec2 vPen;
309
+ flat out vec4 vToGlyph;
310
+ flat out vec4 vToPixel;
311
+ flat out vec4 vCut;
312
+ flat out vec4 vShown;
313
+
314
+ // The cutoffs at ppem by the table of entries start to start + count, as text/csm.ts' cutoffs.
315
+ vec2 cutoffs(int start, int count, float ppem) {
316
+ vec4 first = uTable[start];
317
+ vec4 last = uTable[start + count - 1];
318
+ vec2 c = first.yz;
319
+ if (ppem >= last.x) {
320
+ c = last.yz;
321
+ } else if (ppem > first.x) {
322
+ for (int i = 1; i < ${TABLE_ENTRIES}; i++) {
323
+ if (i >= count) {
324
+ break;
325
+ }
326
+
327
+ vec4 b = uTable[start + i];
328
+ if (ppem <= b.x) {
329
+ vec4 a = uTable[start + i - 1];
330
+ c = mix(a.yz, b.yz, (ppem - a.x) / (b.x - a.x));
331
+ break;
332
+ }
333
+ }
334
+ }
335
+
336
+ float unit = 0.0001 * ppem;
337
+ float t = clamp(uMode.y, -200.0, 200.0);
338
+ float s = clamp(uMode.z, -400.0, 400.0);
339
+ c -= unit * vec2(t + s * 0.5, t - s * 0.5);
340
+ if (c.x < c.y) {
341
+ c = vec2((c.x + c.y) * 0.5);
342
+ }
343
+
344
+ return c;
345
+ }
346
+
347
+ void main(void) {
348
+ ivec2 at = ivec2((uRow & ${ROWS_A_LINE - 1}) * ${ROW_TEXELS}, uRow >> ${Math.log2(ROWS_A_LINE)});
349
+ vec4 m = texelFetch(uRows, at, 0);
350
+ vec4 t = texelFetch(uRows, at + ivec2(1, 0), 0);
351
+ vec4 mul = texelFetch(uRows, at + ivec2(2, 0), 0);
352
+ vec4 add = texelFetch(uRows, at + ivec2(3, 0), 0);
353
+ // The text's pixels to Flash's.
354
+ mat2 linear = mat2(m.xy, m.zw) * uUnit;
355
+ vec2 origin = t.xy * uUnit;
356
+ vec2 pen = linear * aOrigin.xy + roundEven(origin);
357
+ float across = max(abs(linear[0][1]), abs(linear[1][0]));
358
+ if (across > max(abs(linear[0][0]), abs(linear[1][1])) / 65536.0) {
359
+ // Turned or skewed: no grid.
360
+ } else if (uMode.x == 1.0) {
361
+ pen = vec2(roundEven(pen.x * 8.0) / 8.0 + 0.5, roundEven(pen.y));
362
+ } else if (uMode.x == 2.0) {
363
+ pen = vec2(roundEven(pen.x * 4.0) / 4.0 + 0.5, roundEven(pen.y));
364
+ } else {
365
+ pen = roundEven(pen * 4.0) / 4.0 + vec2(0.5, 0.0);
366
+ }
367
+
368
+ float ppem = aRun.w * sqrt(abs(determinant(linear)));
369
+ // The colour as drawn, straight: light or dark by it, as adl chooses.
370
+ vec4 shown = clamp(aColor * mul + add, 0.0, 1.0);
371
+ bool light = dot(floor(shown.rgb * 255.0 + 0.5), vec3(30.0, 59.0, 11.0)) > 20000.0;
372
+ int table = int(light ? aOrigin.z : aOrigin.w);
373
+ vec2 cut = cutoffs(table & 63, table >> 6, ppem);
374
+ mat2 toPixel = linear * aRun.z;
375
+ float margin = max(abs(cut.x), abs(cut.y)) + 1.0;
376
+ float det = determinant(toPixel);
377
+ mat2 inverse = det != 0.0
378
+ ? mat2(toPixel[1][1], -toPixel[0][1], -toPixel[1][0], toPixel[0][0]) / det
379
+ : mat2(0.0);
380
+ // Out in the glyph's space, so that the margin stays outside under reflections and turns: each
381
+ // row of the inverse gives the glyph's units a pixel across that edge takes.
382
+ vec2 reach = vec2(length(vec2(inverse[0][0], inverse[1][0])),
383
+ length(vec2(inverse[0][1], inverse[1][1]))) * margin;
384
+ vec2 p = pen + toPixel * (aGlyph.xy + aGlyph.zw * reach);
385
+ vPixel = p;
386
+ vPen = pen;
387
+ vToGlyph = vec4(inverse[0], inverse[1]);
388
+ vToPixel = vec4(toPixel[0], toPixel[1]);
389
+ vCut = vec4(cut, aRun.xy);
390
+ vShown = shown;
391
+ gl_Position = vec4((p / uUnit) * uView.xy + uView.zw, 0.0, 1.0);
392
+ }
393
+ `;
394
+
395
+ /**
396
+ * Each pixel of Flash's a glyph's quad covers, by its signed distance to
397
+ * the glyph's outline (the segments of its run in uSegments, the nearest
398
+ * and, by parity, whether it is inside), between the cutoffs, as Saffron
399
+ * covers it. Nothing outside: in a mask, which
400
+ * writes the stencil wherever it draws, a glyph is its shape.
401
+ */
402
+ export const glyphsFragment = `#version 300 es
403
+ precision highp float;
404
+ precision highp int;
405
+ in vec2 vPixel;
406
+ flat in vec2 vPen;
407
+ flat in vec4 vToGlyph;
408
+ flat in vec4 vToPixel;
409
+ flat in vec4 vCut;
410
+ flat in vec4 vShown;
411
+ out vec4 finalColor;
412
+ uniform highp sampler2D uSegments;
413
+
414
+ void main(void) {
415
+ vec2 centre = floor(vPixel) + 0.5;
416
+ mat2 toGlyph = mat2(vToGlyph.xy, vToGlyph.zw);
417
+ mat2 toPixel = mat2(vToPixel.xy, vToPixel.zw);
418
+ vec2 g = toGlyph * (centre - vPen);
419
+ int start = int(vCut.z);
420
+ int count = int(vCut.w);
421
+ float nearest = 3.4e38;
422
+ vec2 along = vec2(1.0, 0.0);
423
+ bool inside = false;
424
+ for (int i = 0; i < count; i++) {
425
+ int k = start + i;
426
+ vec4 s = texelFetch(uSegments, ivec2(k & ${OUTLINE_ROW - 1}, k >> ${Math.log2(OUTLINE_ROW)}), 0);
427
+ vec2 a = s.xy;
428
+ vec2 e = s.zw - a;
429
+ vec2 w = g - a;
430
+ float t = clamp(dot(w, e) / max(dot(e, e), 1e-20), 0.0, 1.0);
431
+ vec2 off = w - e * t;
432
+ float d = dot(off, off);
433
+ if (d < nearest) {
434
+ nearest = d;
435
+ along = e;
436
+ }
437
+
438
+ if ((a.y > g.y) != (s.w > g.y) && a.x + (g.y - a.y) * e.x / e.y > g.x) {
439
+ inside = !inside;
440
+ }
441
+ }
442
+
443
+ // In Flash's pixels, across the nearest edge as the transform stretches it.
444
+ float stretch = length(toPixel * along);
445
+ float d = stretch > 0.0
446
+ ? sqrt(nearest) * abs(determinant(toPixel)) * length(along) / stretch
447
+ : 0.0;
448
+ d = inside ? d : -d;
449
+ float coverage = vCut.x > vCut.y
450
+ ? clamp((d - vCut.y) / (vCut.x - vCut.y), 0.0, 1.0)
451
+ : step(vCut.x, d);
452
+ if (coverage <= 0.0) {
453
+ discard;
454
+ }
455
+
456
+ finalColor = vec4(vShown.rgb * vShown.a, vShown.a) * coverage;
457
+ }
458
+ `;
459
+
460
+ /**
461
+ * Two triangles over a layer's rectangle of the target, uRect its corners
462
+ * in clip space: a rasteriser then steps over those pixels alone, as a
463
+ * software one does not for a triangle over the whole target cut by the
464
+ * scissor.
465
+ */
466
+ export const compositeVertex = `#version 300 es
467
+ uniform vec4 uRect;
468
+ void main(void) {
469
+ int k = gl_VertexID;
470
+ vec2 p = vec2(k == 1 || k == 2 || k == 4 ? 1.0 : 0.0, k == 2 || k == 4 || k == 5 ? 1.0 : 0.0);
471
+ gl_Position = vec4(mix(uRect.xy, uRect.zw, p), 0.0, 1.0);
472
+ }
473
+ `;
474
+
475
+ /**
476
+ * A composited layer's pixel, `front`, coloured by its row's colour
477
+ * transform where it has one (`row` not -1).
478
+ */
479
+ const layerColor = (row: string) => ` if (${row} >= 0 && front.a > 0.0) {
480
+ ivec2 row = ivec2((${row} & ${ROWS_A_LINE - 1}) * ${ROW_TEXELS}, ${row} >> ${Math.log2(ROWS_A_LINE)});
481
+ vec4 mul = texelFetch(uRows, row + ivec2(2, 0), 0);
482
+ vec4 add = texelFetch(uRows, row + ivec2(3, 0), 0);
483
+ vec4 c = clamp(vec4(front.rgb / front.a, front.a) * mul + add, 0.0, 1.0);
484
+ front = vec4(c.rgb * c.a, c.a);
485
+ }
486
+ `;
487
+
488
+ /** A composited layer's pixel, `front`, coloured by its row's colour transform where it has one (uRow not -1). */
489
+ export const LAYER_COLOR = layerColor("uRow");
490
+
491
+ /**
492
+ * `front`, premultiplied, blended in mode uMode onto what is below it
493
+ * (uBack, a copy of it, whose texel at the fragment is uBackOffset short of
494
+ * it) into finalColor: normal, layer, add and screen as `front` alone, GL's
495
+ * blending computing them; the separable modes blend straight colours and
496
+ * composite source over, the others have formulas of their own.
497
+ */
498
+ const BLEND_FRONT = ` if (uMode == ${NORMAL} || uMode == ${LAYER} || uMode == ${SCREEN} || uMode == ${ADD}) {
499
+ finalColor = front;
500
+ return;
501
+ }
502
+
503
+ vec4 back = texelFetch(uBack, ivec2(gl_FragCoord.xy) - uBackOffset, 0);
504
+ vec3 cb = straight(back);
505
+ vec3 cf = straight(front);
506
+ vec3 b;
507
+ switch (uMode) {
508
+ case ${MULTIPLY}: b = cb * cf; break;
509
+ case ${LIGHTEN}: b = max(cb, cf); break;
510
+ case ${DARKEN}: b = min(cb, cf); break;
511
+ case ${DIFFERENCE}: b = abs(cb - cf); break;
512
+ case ${OVERLAY}: b = hardlight(cf, cb); break;
513
+ case ${HARDLIGHT}: b = hardlight(cb, cf); break;
514
+ case ${SUBTRACT}:
515
+ finalColor = vec4(max(back.rgb - front.rgb, 0.0), front.a + back.a * (1.0 - front.a));
516
+ return;
517
+ case ${INVERT}:
518
+ finalColor = vec4((back.a - back.rgb) * front.a + back.rgb * (1.0 - front.a), back.a);
519
+ return;
520
+ case ${ALPHA}:
521
+ finalColor = front.a > 0.0 ? back * front.a : back;
522
+ return;
523
+ case ${ERASE}:
524
+ finalColor = back * (1.0 - front.a);
525
+ return;
526
+ default:
527
+ finalColor = front + back * (1.0 - front.a);
528
+ return;
529
+ }
530
+
531
+ vec3 co = front.rgb * (1.0 - back.a) + back.rgb * (1.0 - front.a) + front.a * back.a * b;
532
+ finalColor = vec4(co, front.a + back.a * (1.0 - front.a));
533
+ `;
534
+
535
+ /** BLEND_FRONT's helpers. */
536
+ const BLEND_HELPERS = `vec3 straight(vec4 c) { return c.a > 0.0 ? c.rgb / c.a : vec3(0.0); }
537
+ vec3 hardlight(vec3 cb, vec3 cf) {
538
+ vec3 multiply = 2.0 * cb * cf;
539
+ vec3 screen = 1.0 - 2.0 * (1.0 - cb) * (1.0 - cf);
540
+ return mix(multiply, screen, step(0.5, cf));
541
+ }
542
+ `;
543
+
544
+ /**
545
+ * A layer composited onto what is below over its rectangle, pixel for
546
+ * pixel: its rows run down, as the filters read them, and the target's up
547
+ * where it is the canvas (uFlip), from uOffset in its framebuffer. Coloured
548
+ * by its row's colour transform where it has one (uRow not -1), on its
549
+ * straight colour where it covers anything; then blended by its mode
550
+ * (BLEND_FRONT).
551
+ */
552
+ export const compositeFragment = `#version 300 es
553
+ precision highp float;
554
+ uniform sampler2D uLayer;
555
+ uniform sampler2D uBack;
556
+ uniform highp sampler2D uRows;
557
+ uniform int uRow;
558
+ uniform int uMode;
559
+ uniform ivec2 uOffset;
560
+ uniform bool uFlip;
561
+ uniform ivec2 uBackOffset;
562
+ out vec4 finalColor;
563
+
564
+ ${BLEND_HELPERS}
565
+ void main(void) {
566
+ ivec2 frag = ivec2(gl_FragCoord.xy);
567
+ ivec2 at = ivec2(frag.x - uOffset.x, uFlip ? uOffset.y - frag.y : frag.y - uOffset.y);
568
+ vec4 front = texelFetch(uLayer, at, 0);
569
+ ${LAYER_COLOR}
570
+ ${BLEND_FRONT}}
571
+ `;
572
+
573
+ /**
574
+ * A fill drawn straight onto what is below it in a blend mode, where its
575
+ * layer would hold it alone (a flat fill, GeometryData.flat): its pixel as
576
+ * fillFragment has it, then as compositeFragment composites its layer's,
577
+ * coloured by the layer's row (uLayerRow, -1 for none) and blended by uMode.
578
+ */
579
+ export const blendFillFragment = `#version 300 es
580
+ precision highp float;
581
+ in vec4 vColor;
582
+ in vec2 vTexel;
583
+ flat in vec4 vMul;
584
+ flat in vec4 vAdd;
585
+ flat in int vSlot;
586
+ uniform sampler2D uTextures[${FILL_TEXTURES}];
587
+ uniform sampler2D uBack;
588
+ uniform highp sampler2D uRows;
589
+ uniform int uLayerRow;
590
+ uniform int uMode;
591
+ uniform ivec2 uBackOffset;
592
+ out vec4 finalColor;
593
+
594
+ ${BLEND_HELPERS}
595
+ vec4 texel(vec2 at) {
596
+ switch (vSlot) {
597
+ ${slotTexel}
598
+ }
599
+
600
+ return vec4(0.0);
601
+ }
602
+
603
+ void main(void) {
604
+ vec4 front = vec4(0.0);
605
+ vec4 fill = vColor;
606
+ bool clear = false;
607
+ if (vSlot != 0) {
608
+ vec4 t = texel(vTexel);
609
+ clear = t.a == 0.0;
610
+ fill = clear ? fill : vec4(t.rgb / t.a * vColor.rgb, t.a * vColor.a);
611
+ }
612
+
613
+ if (!clear) {
614
+ vec4 c = clamp(fill * vMul + vAdd, 0.0, 1.0);
615
+ front = vec4(c.rgb * c.a, c.a);
616
+ }
617
+
618
+ ${layerColor("uLayerRow")}
619
+ ${BLEND_FRONT}}
620
+ `;