@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,360 @@
1
+ // The fragment shaders of Flash's filters, as adl draws them (docs/architecture/compositing.md,
2
+ // "Filters"), which the renderer's passes (webgl/filters.ts) run, so that their
3
+ // exactness rules live in one place. GLSL ES 3 bodies, with no version or precision
4
+ // line; each reads vTextureCoord, its input's texture coordinate, and writes finalColor.
5
+
6
+ /** Truncated to 8 bits, as adl's each pass is: a hair over, so that an exact value stays. */
7
+ export const TRUNCATE = "floor(c * 255.0 + 0.001) / 255.0";
8
+
9
+ /**
10
+ * One run of the box one way: `uWidth` texels wide, its end texels weighted
11
+ * by their part; `uDirection` a texel's step in the input's pixels, which
12
+ * uInputSize gives the coordinates in.
13
+ */
14
+ export const BOX = `in vec2 vTextureCoord;
15
+ out vec4 finalColor;
16
+ uniform sampler2D uTexture;
17
+ // As the vertex shader has it, which is highp: a program's uniforms agree.
18
+ uniform highp vec4 uInputSize;
19
+ uniform vec2 uDirection;
20
+ uniform float uWidth;
21
+ void main(void) {
22
+ float half_ = uWidth * 0.5;
23
+ int reach = int(ceil(half_));
24
+ vec4 sum = vec4(0.0);
25
+ // As far as 255 pixels at 4 texels a pixel, each way.
26
+ for (int i = 0; i <= 2040; i++) {
27
+ if (i > 2 * reach) { break; }
28
+ float f = float(i - reach);
29
+ float weight = max(0.0, min(f + 0.5, half_) - max(f - 0.5, -half_));
30
+ sum += texture(uTexture, vTextureCoord + uDirection * uInputSize.zw * f) * weight;
31
+ }
32
+ vec4 c = sum / uWidth;
33
+ finalColor = ${TRUNCATE};
34
+ }`;
35
+
36
+ // The mean of the uFactor.x by uFactor.y texels of the input that a texel of the output covers,
37
+ // the output that many times narrower and lower (blurShrink). Two texels each way a read: a linear read midway between texel centres is their mean, so a
38
+ // block takes a quarter of the reads. An odd factor's last column or row is read alone, weighted
39
+ // by the texels each read stands for. A pool texture has no levels but its base.
40
+ export const SHRINK = `in vec2 vTextureCoord;
41
+ out vec4 finalColor;
42
+ uniform sampler2D uTexture;
43
+ uniform highp vec4 uInputPixel;
44
+ uniform vec2 uFactor;
45
+ void main(void) {
46
+ vec4 sum = vec4(0.0);
47
+ vec2 centre = (uFactor - 1.0) * 0.5;
48
+ for (int j = 0; j < 16; j += 2) {
49
+ float y = float(j);
50
+ if (y >= uFactor.y) { break; }
51
+ float ny = min(2.0, uFactor.y - y);
52
+ for (int i = 0; i < 16; i += 2) {
53
+ float x = float(i);
54
+ if (x >= uFactor.x) { break; }
55
+ float nx = min(2.0, uFactor.x - x);
56
+ vec2 at = vec2(x, y) + (vec2(nx, ny) - 1.0) * 0.5 - centre;
57
+ sum += texture(uTexture, vTextureCoord + at * uInputPixel.zw) * (nx * ny);
58
+ }
59
+ }
60
+ finalColor = sum / (uFactor.x * uFactor.y);
61
+ }`;
62
+
63
+ /** A blur's runs, done coarser (blurred), drawn back between their texels over the input's frame. */
64
+ export const READ_BACK = `in vec2 vTextureCoord;
65
+ out vec4 finalColor;
66
+ uniform sampler2D uBlurred;
67
+ uniform vec2 uBlurredScale;
68
+ void main(void) {
69
+ finalColor = texture(uBlurred, vTextureCoord * uBlurredScale);
70
+ }`;
71
+
72
+ /**
73
+ * A glow or a shadow: the input's blurred alpha (uBlurred), from `uOffset`
74
+ * texels back for a shadow, times strength and alpha, in its colour; behind
75
+ * the object, or inside it for an inner one; the object kept, or not.
76
+ */
77
+ export const GLOW = `in vec2 vTextureCoord;
78
+ out vec4 finalColor;
79
+ uniform sampler2D uTexture;
80
+ uniform sampler2D uBlurred;
81
+ uniform highp vec4 uInputSize;
82
+ uniform vec3 uColor;
83
+ uniform float uAlpha;
84
+ uniform float uStrength;
85
+ uniform vec2 uOffset;
86
+ uniform vec2 uBlurredScale;
87
+ uniform float uInner;
88
+ uniform float uKnockout;
89
+ uniform float uHide;
90
+ void main(void) {
91
+ vec4 src = texture(uTexture, vTextureCoord);
92
+ float a = texture(uBlurred, (vTextureCoord - uOffset * uInputSize.zw) * uBlurredScale).a;
93
+ vec4 c;
94
+ if (uInner > 0.5) {
95
+ float g = clamp((1.0 - a) * uStrength, 0.0, 1.0) * uAlpha * src.a;
96
+ vec4 glow = vec4(uColor * g, g);
97
+ c = uKnockout > 0.5 ? glow : glow + src * (1.0 - g);
98
+ } else {
99
+ float g = clamp(a * uStrength, 0.0, 1.0) * uAlpha;
100
+ vec4 glow = vec4(uColor * g, g);
101
+ c = uKnockout > 0.5 ? glow * (1.0 - src.a) : uHide > 0.5 ? glow : src + glow * (1.0 - src.a);
102
+ }
103
+ finalColor = ${TRUNCATE};
104
+ }`;
105
+
106
+ /**
107
+ * A bevel: the input's blurred alpha (uBlurred) from uOffset texels on and
108
+ * back; their difference times strength, clamped, times the colour's alpha,
109
+ * the highlight where on is more and the shadow where it is less. Inner
110
+ * (uType 0) atop the object, outer (1) behind it, full (2) over it;
111
+ * knocked out, alone, masked to where the object is (inner) or is not
112
+ * (outer).
113
+ */
114
+ export const BEVEL = `in vec2 vTextureCoord;
115
+ out vec4 finalColor;
116
+ uniform sampler2D uTexture;
117
+ uniform sampler2D uBlurred;
118
+ uniform highp vec4 uInputSize;
119
+ uniform vec3 uHighlight;
120
+ uniform vec3 uShadow;
121
+ uniform float uHighlightAlpha;
122
+ uniform float uShadowAlpha;
123
+ uniform float uStrength;
124
+ uniform vec2 uOffset;
125
+ uniform float uType;
126
+ uniform float uKnockout;
127
+ void main(void) {
128
+ // Keep source samples sharp; the bevel reads alpha at a stage pixel's centre.
129
+ vec2 at = (floor(vTextureCoord * uInputSize.xy) + 0.5) * uInputSize.zw;
130
+ vec4 src = texture(uTexture, vTextureCoord);
131
+ vec2 step_ = uOffset * uInputSize.zw;
132
+ // In 255ths, each read truncated, as adl's are.
133
+ float on = floor(texture(uBlurred, at + step_).a * 255.0 + 0.001);
134
+ float back = floor(texture(uBlurred, at - step_).a * 255.0 + 0.001);
135
+ float d = on - back;
136
+ bool lit = d > 0.0;
137
+ float strong = min(255.0, floor(abs(d) * uStrength + 0.001));
138
+ float la = floor(strong * (lit ? uHighlightAlpha : uShadowAlpha) + 0.5) / 255.0;
139
+ float m = uType < 0.5 ? src.a : (uType < 1.5 && uKnockout > 0.5 ? 1.0 - src.a : 1.0);
140
+ vec4 layer = vec4((lit ? uHighlight : uShadow) * la, la) * m;
141
+ vec4 c;
142
+ if (uKnockout > 0.5) {
143
+ c = layer;
144
+ } else if (uType > 0.5 && uType < 1.5) {
145
+ c = src + layer * (1.0 - src.a);
146
+ } else {
147
+ c = layer + src * (1.0 - (uType < 0.5 ? la : layer.a));
148
+ }
149
+ finalColor = ${TRUNCATE};
150
+ }`;
151
+
152
+ /**
153
+ * How a bevel or a gradient filter places its layer (premultiplied) on the
154
+ * object's pixel, as bitmap/filters' placer does: masked to where the
155
+ * object is (uType 0, inner) or, knocked out, is not (1, outer); inner
156
+ * atop it, outer behind it, full (2) over it, knocked out alone.
157
+ */
158
+ export const PLACE = `vec4 place(vec4 src, vec4 layer) {
159
+ float m = uType < 0.5 ? src.a : (uType < 1.5 && uKnockout > 0.5 ? 1.0 - src.a : 1.0);
160
+ vec4 masked = layer * m;
161
+ if (uKnockout > 0.5) {
162
+ return masked;
163
+ }
164
+ if (uType > 0.5 && uType < 1.5) {
165
+ return src + masked * (1.0 - src.a);
166
+ }
167
+ return masked + src * (1.0 - (uType < 0.5 ? layer.a : masked.a));
168
+ }`;
169
+
170
+ /**
171
+ * A gradient glow: the input's blurred alpha (uBlurred) from uOffset back,
172
+ * as a shadow reads it, times strength, picking a texel of the gradient
173
+ * (uGradient, 256 wide); a gradient bevel (uBevel 1): the difference of
174
+ * that alpha on and back, the middle of the gradient where they agree.
175
+ * Each pixel is evaluated once, at its centre, in 255ths truncated.
176
+ */
177
+ export const GRADIENT = `in vec2 vTextureCoord;
178
+ out vec4 finalColor;
179
+ uniform sampler2D uTexture;
180
+ uniform sampler2D uBlurred;
181
+ uniform sampler2D uGradient;
182
+ uniform highp vec4 uInputSize;
183
+ uniform float uStrength;
184
+ uniform vec2 uOffset;
185
+ uniform float uType;
186
+ uniform float uKnockout;
187
+ uniform float uBevel;
188
+ uniform vec4 uRegion;
189
+ ${PLACE}
190
+ float alphaAt(vec2 at) {
191
+ return floor(texture(uBlurred, at).a * 255.0 + 0.001);
192
+ }
193
+ void main(void) {
194
+ vec2 p = floor(vTextureCoord * uInputSize.xy) + 0.5;
195
+ // Past the filter's rect it draws nothing, though its gradient may have colour at 0.
196
+ if (any(lessThan(p, uRegion.xy)) || any(greaterThanEqual(p, uRegion.zw))) {
197
+ finalColor = vec4(0.0);
198
+ return;
199
+ }
200
+ vec2 at = p * uInputSize.zw;
201
+ vec4 src = texture(uTexture, at);
202
+ vec2 step_ = uOffset * uInputSize.zw;
203
+ float index;
204
+ if (uBevel > 0.5) {
205
+ float d = clamp(floor((alphaAt(at + step_) - alphaAt(at - step_)) * uStrength), -255.0, 255.0);
206
+ index = floor((d + 256.0) / 2.0);
207
+ } else {
208
+ index = min(255.0, floor(alphaAt(at - step_) * uStrength + 0.001));
209
+ }
210
+ vec4 layer = texture(uGradient, vec2((index + 0.5) / 256.0, 0.5));
211
+ vec4 c = place(src, layer);
212
+ finalColor = ${TRUNCATE};
213
+ }`;
214
+
215
+ /**
216
+ * A displacement map, in the input's pixels from its frame's corner: each
217
+ * pixel of the object's (uBox) where the map lies (uMapAt, uMapSize) takes
218
+ * the input at itself moved by the map's channel (uComponents picks one
219
+ * for x, one for y) less 128, times scale, in 256ths toward 0, between
220
+ * pixels with weights in 256ths; past the object's pixels as uMode says:
221
+ * 0 wrap, 1 clamp, 2 its own place, 3 uColor, which also fills uGrow pixels
222
+ * round the object, as adl's drawing of it does. Each pixel once, at its
223
+ * centre.
224
+ */
225
+ export const DISPLACE = `in vec2 vTextureCoord;
226
+ out vec4 finalColor;
227
+ uniform sampler2D uTexture;
228
+ uniform sampler2D uMap;
229
+ uniform highp vec4 uInputSize;
230
+ uniform vec4 uBox;
231
+ uniform vec4 uMapAt;
232
+ uniform vec4 uComponentX;
233
+ uniform vec4 uComponentY;
234
+ uniform vec2 uScale;
235
+ uniform float uMode;
236
+ uniform vec4 uColor;
237
+ uniform vec2 uGrow;
238
+ vec4 read(vec2 at, vec2 own) {
239
+ vec2 size = uBox.zw - uBox.xy;
240
+ vec2 i = at - uBox.xy;
241
+ if (any(lessThan(i, vec2(0.0))) || any(greaterThanEqual(i, size))) {
242
+ if (uMode < 0.5) {
243
+ i = mod(i, size);
244
+ } else if (uMode < 2.5) {
245
+ i = clamp(i, vec2(0.0), size - 1.0);
246
+ } else {
247
+ return uColor;
248
+ }
249
+ }
250
+ return texture(uTexture, (uBox.xy + i + 0.5) * uInputSize.zw);
251
+ }
252
+ void main(void) {
253
+ vec2 p = floor(vTextureCoord * uInputSize.xy);
254
+ vec4 own = texture(uTexture, (p + 0.5) * uInputSize.zw);
255
+ vec2 m = p - uMapAt.xy;
256
+ if (any(lessThan(p, uBox.xy)) || any(greaterThanEqual(p, uBox.zw))) {
257
+ bool near = all(greaterThanEqual(p, uBox.xy - uGrow)) && all(lessThan(p, uBox.zw + uGrow));
258
+ finalColor = uMode > 2.5 && near ? uColor : vec4(0.0);
259
+ return;
260
+ }
261
+ if (any(lessThan(m, vec2(0.0))) || any(greaterThanEqual(m, uMapAt.zw))) {
262
+ finalColor = own;
263
+ return;
264
+ }
265
+ vec4 map = texture(uMap, (m + 0.5) / uMapAt.zw);
266
+ vec4 straight = map.a > 0.0 ? vec4(map.rgb / map.a, map.a) : vec4(0.0);
267
+ vec2 c = floor(vec2(dot(straight, uComponentX), dot(straight, uComponentY)) * 255.0 + 0.5);
268
+ // None picked: no move.
269
+ c = vec2(uComponentX == vec4(0.0) ? 128.0 : c.x, uComponentY == vec4(0.0) ? 128.0 : c.y);
270
+ // Toward 0, without trunc, which GLSL ES 1 has not.
271
+ vec2 moved = (c - 128.0) * uScale;
272
+ vec2 q = p * 256.0 + sign(moved) * floor(abs(moved));
273
+ vec2 whole = floor(q / 256.0);
274
+ vec2 f = q - whole * 256.0;
275
+ if (uMode > 1.5 && uMode < 2.5) {
276
+ vec2 i = whole - uBox.xy;
277
+ vec2 size = uBox.zw - uBox.xy;
278
+ whole = vec2(i.x < 0.0 || i.x >= size.x ? p.x : whole.x, i.y < 0.0 || i.y >= size.y ? p.y : whole.y);
279
+ }
280
+ float w00 = floor((256.0 - f.x) * (256.0 - f.y) / 256.0);
281
+ float w10 = floor(f.x * (256.0 - f.y) / 256.0);
282
+ float w01 = floor((256.0 - f.x) * f.y / 256.0);
283
+ float w11 = floor(f.x * f.y / 256.0);
284
+ vec4 v = read(whole, p) * w00 + read(whole + vec2(1.0, 0.0), p) * w10 +
285
+ read(whole + vec2(0.0, 1.0), p) * w01 + read(whole + vec2(1.0, 1.0), p) * w11;
286
+ vec4 c8 = floor(v * 255.0 / 256.0 + 0.001) / 255.0;
287
+ finalColor = c8;
288
+ }`;
289
+
290
+ /** A colour matrix of a pixel's straight colour, its offsets in 255ths, transparent pixels too. */
291
+ export const MATRIX = `in vec2 vTextureCoord;
292
+ out vec4 finalColor;
293
+ uniform sampler2D uTexture;
294
+ uniform mat4 uMatrix;
295
+ uniform vec4 uOffset;
296
+ void main(void) {
297
+ vec4 src = texture(uTexture, vTextureCoord);
298
+ vec4 straight = src.a > 0.0 ? vec4(src.rgb / src.a, src.a) : vec4(0.0);
299
+ vec4 s = clamp(uMatrix * straight + uOffset, 0.0, 1.0);
300
+ vec4 c = vec4(s.rgb * s.a, s.a);
301
+ finalColor = ${TRUNCATE};
302
+ }`;
303
+
304
+ /**
305
+ * A convolution, in 255ths: the taps' straight colour over uBox, the
306
+ * object's pixels as adl filters them, in the input's pixels from its
307
+ * frame's corner; past it the nearest edge's or uEdge; summed, divided,
308
+ * biased, clamped and premultiplied as bitmap/filters' convolve does, the
309
+ * fixed-point way too (uReciprocal not 0) where every tap lies in the box.
310
+ */
311
+ export const CONVOLUTION = `in vec2 vTextureCoord;
312
+ out vec4 finalColor;
313
+ uniform sampler2D uTexture;
314
+ uniform highp vec4 uInputSize;
315
+ uniform vec4 uMatrix[57];
316
+ uniform vec2 uSize;
317
+ uniform vec4 uBox;
318
+ uniform vec4 uEdge;
319
+ uniform float uDivisor;
320
+ uniform float uReciprocal;
321
+ uniform float uBias;
322
+ uniform float uClamp;
323
+ uniform float uPreserve;
324
+ vec4 straight(vec2 p) {
325
+ if (any(lessThan(p, uBox.xy)) || any(greaterThanEqual(p, uBox.zw))) {
326
+ if (uClamp < 0.5) { return uEdge; }
327
+ p = clamp(p, floor(uBox.xy) + 0.5, floor(uBox.zw) - 0.5);
328
+ }
329
+ vec4 c = texture(uTexture, p * uInputSize.zw);
330
+ return c.a > 0.0 ? floor(vec4(c.rgb / c.a, c.a) * 255.0 + 0.5) : vec4(0.0);
331
+ }
332
+ void main(void) {
333
+ // The pixel's centre, which all its texels read alike, as adl's one pixel.
334
+ vec2 p = floor(vTextureCoord * uInputSize.xy) + 0.5;
335
+ vec2 reach = floor(uSize * 0.5);
336
+ if (any(lessThan(p, uBox.xy - reach)) || any(greaterThanEqual(p, uBox.zw + reach))) {
337
+ finalColor = vec4(0.0);
338
+ return;
339
+ }
340
+ bool fixed_ = uReciprocal != 0.0 && all(greaterThanEqual(p - 1.0, uBox.xy)) &&
341
+ all(lessThan(p + 1.0, uBox.zw));
342
+ int cols = int(uSize.x);
343
+ int count = cols * int(uSize.y);
344
+ vec4 sum = vec4(0.0);
345
+ // One loop, its index alone indexing the weights, as GLSL ES 1 allows.
346
+ for (int k = 0; k < 225; k++) {
347
+ if (k >= count) { break; }
348
+ float w = dot(uMatrix[k / 4], vec4(equal(vec4(float(k - k / 4 * 4)), vec4(0.0, 1.0, 2.0, 3.0))));
349
+ if (w != 0.0) {
350
+ int j = k / cols;
351
+ vec2 at = vec2(float(k - j * cols), float(j)) - reach;
352
+ sum += straight(fixed_ && k == 8 ? p : p + at) * w;
353
+ }
354
+ }
355
+ sum = fixed_ ? floor(sum * uReciprocal) : sum / uDivisor;
356
+ vec4 v = floor(clamp(sum + uBias, 0.0, 255.0) + 0.0001);
357
+ float a = uPreserve > 0.5 ? straight(p).a : v.a;
358
+ vec3 c = fixed_ ? floor(v.rgb * a / 255.0) : floor((v.rgb * a + 127.0) / 255.0);
359
+ finalColor = vec4(c, a) / 255.0;
360
+ }`;
package/src/filters.ts ADDED
@@ -0,0 +1,220 @@
1
+ // Flash's filters as passes a layer takes before it is composited: what
2
+ // each kind reads, as the floats a frame's filter list holds for it, and
3
+ // the rules its passes keep to match adl (docs/architecture/compositing.md,
4
+ // "Filters"), which the backend's runner (webgl/filters.ts) follows.
5
+ // Offsets and insets are in the target's units, its pixels over the
6
+ // resolution, as the shaders read their input's size in.
7
+
8
+ /** A blur: blurX, blurY, quality, units (the target's units a stage pixel). */
9
+ export const BLUR = 1;
10
+ /**
11
+ * A glow or a drop shadow: blurX, blurY, quality, units; its colour's red,
12
+ * green and blue from 0 to 1, alpha, strength; the offset x and y it is
13
+ * cast by; inner, knockout and hide object, 1 or 0.
14
+ */
15
+ export const GLOW = 2;
16
+ /**
17
+ * A bevel: blurX, blurY, quality, units; the highlight's red, green and
18
+ * blue, the shadow's, the highlight's alpha, the shadow's, strength; the
19
+ * offset x and y; its type (0 inner, 1 outer, 2 full), knockout.
20
+ */
21
+ export const BEVEL = 3;
22
+ /**
23
+ * A gradient glow or bevel, its texture the gradient's 256 colours: blurX,
24
+ * blurY, quality, units, strength; the offset x and y; type, knockout,
25
+ * bevel (1, else a glow); how far in the object's pixels start; how far
26
+ * its rect grows past them in stage pixels, left, top, right and bottom.
27
+ */
28
+ export const GRADIENT = 4;
29
+ /** A colour matrix: its 4 by 4 part by columns, then its offsets over 255. */
30
+ export const COLOR_MATRIX = 5;
31
+ /**
32
+ * A convolution: its columns and rows; the edge colour's red, green, blue
33
+ * and alpha in 255ths; divisor, the fixed-point reciprocal over 65536 (0
34
+ * for none), bias, clamp, preserve alpha; how far in the object's pixels
35
+ * start; then the matrix by rows.
36
+ */
37
+ export const CONVOLUTION = 6;
38
+ /**
39
+ * A displacement map, its texture the map's pixels: the channel that
40
+ * moves x, picked by four weights for red, green, blue and alpha, and the
41
+ * one that moves y; scale x and y; its mode (0 wrap, 1 clamp, 2 ignore, 3
42
+ * colour), the colour's red, green, blue and alpha, premultiplied, and how
43
+ * far round the object it fills, x and y; how far in the object's pixels
44
+ * start; the map's point, and its width and height.
45
+ */
46
+ export const DISPLACEMENT = 7;
47
+ /**
48
+ * A ShaderFilter, its kernel's GLSL the filter's kernel (kernel-shaders.ts):
49
+ * units; how far in from its input's corner the filter's input starts, and
50
+ * how far the first filter's, the object's bitmap, is from that, both in
51
+ * units; its extensions left, top, right and bottom in stage pixels; how
52
+ * many images it samples, then each one's texture number, the texture of
53
+ * the frame's it reads (-1 for the layer) and whether its texels are
54
+ * floats; how many floats its parameters take, then them. Its parameters'
55
+ * ints are the filter's ints (Filters.ints).
56
+ */
57
+ export const SHADER = 8;
58
+ /**
59
+ * A blend shader, alone in a chain a layer's KERNEL mode names, which no
60
+ * filter pass runs: its values as SHADER's, its images' textures -1 for
61
+ * what is below and -2 for the layer, but its first, 1 where it blends
62
+ * the layer's whole rectangle rather than where the layer covers anything.
63
+ */
64
+ export const BLEND = 9;
65
+ /**
66
+ * A shader fill, alone in a chain a SHADED draw names (scene.ts), which no
67
+ * filter pass runs: its values as SHADER's, every image a texture.
68
+ */
69
+ export const FILL_SHADER = 10;
70
+
71
+ /**
72
+ * A frame's filters in chains, one a filtered layer: each filter its kind,
73
+ * its values and the texture it reads (-1 for none), written in order and
74
+ * closed into a chain the layer's end names.
75
+ */
76
+ export class Filters {
77
+ kind = new Uint8Array(64);
78
+ texture = new Int32Array(64);
79
+ /** A ShaderFilter's kernel, its GLSL, by the filter; null for the others. */
80
+ kernels: (string | null)[] = [];
81
+ /** Where each filter's values start in `values`, and where the next one's do. */
82
+ start = new Int32Array(65);
83
+ values = new Float32Array(1024);
84
+ /** Each filter's ints, which floats would round: from `intStart` on, as values are. */
85
+ intStart = new Int32Array(65);
86
+ ints = new Int32Array(256);
87
+ count = 0;
88
+ /** Where each chain's filters start, and where the next one's do: the first's, 0. */
89
+ chains = new Int32Array(17);
90
+ chainCount = 0;
91
+
92
+ /** Moves on at each reset: a chain number is good while it is the same. */
93
+ generation = 0;
94
+
95
+ reset(): void {
96
+ this.count = 0;
97
+ this.chainCount = 0;
98
+ this.kernels.length = 0;
99
+ this.generation++;
100
+ }
101
+
102
+ /** A filter of `kind` added to the chain open: its values, the texture it reads, its kernel and its ints. */
103
+ add(
104
+ kind: number,
105
+ values: ArrayLike<number>,
106
+ texture = -1,
107
+ kernel: string | null = null,
108
+ ints: ArrayLike<number> = NO_INTS,
109
+ ): void {
110
+ const at = this.count++;
111
+ if (this.count >= this.kind.length) {
112
+ this.kind = grow(this.kind);
113
+ this.texture = grow(this.texture);
114
+ this.start = grow(this.start);
115
+ this.intStart = grow(this.intStart);
116
+ }
117
+
118
+ const intFrom = at > 0 ? this.intStart[at] : 0;
119
+ if (intFrom + ints.length > this.ints.length) {
120
+ const grown = new Int32Array(Math.max(this.ints.length * 2, intFrom + ints.length));
121
+ grown.set(this.ints);
122
+ this.ints = grown;
123
+ }
124
+
125
+ this.intStart[at] = intFrom;
126
+ this.ints.set(ints, intFrom);
127
+ this.intStart[at + 1] = intFrom + ints.length;
128
+
129
+ const from = at > 0 ? this.start[at] : 0;
130
+ if (from + values.length > this.values.length) {
131
+ const grown = new Float32Array(Math.max(this.values.length * 2, from + values.length));
132
+ grown.set(this.values);
133
+ this.values = grown;
134
+ }
135
+
136
+ this.kind[at] = kind;
137
+ this.texture[at] = texture;
138
+ this.kernels[at] = kernel;
139
+ this.start[at] = from;
140
+ this.values.set(values, from);
141
+ this.start[at + 1] = from + values.length;
142
+ }
143
+
144
+ /** The filters added since the last chain closed, as a chain: its number. */
145
+ close(): number {
146
+ const chain = this.chainCount++;
147
+ if (this.chainCount >= this.chains.length) {
148
+ this.chains = grow(this.chains);
149
+ }
150
+
151
+ this.chains[chain + 1] = this.count;
152
+ return chain;
153
+ }
154
+ }
155
+
156
+ const NO_INTS = new Int32Array(0);
157
+
158
+ function grow<T extends Int32Array | Uint8Array>(array: T): T {
159
+ const grown = new (array.constructor as new (length: number) => T)(array.length * 2);
160
+ grown.set(array);
161
+ return grown;
162
+ }
163
+
164
+ /**
165
+ * The fewest texels a shrunk run spans, for each time a glow's strength
166
+ * multiplies its alpha. Under it the bound below fails: an edge's ramp a
167
+ * few coarse texels long bends sharply where a box ends, and drawn back
168
+ * linearly between them a glow 8 wide at strength 2.37 shrunk three times
169
+ * was off by 10 levels; a glow's strength multiplies that too.
170
+ */
171
+ const SHRUNK_TEXELS = 8;
172
+
173
+ /** The most, in 255ths of alpha times strength, that drawing a shrunk blur back may be off by. */
174
+ const SHRUNK_ERROR = 2;
175
+
176
+ /**
177
+ * The strongest glow or shadow a shrunk blur serves. Each run truncates to
178
+ * a 255th, and drawn back between coarser texels a level's step moves by up
179
+ * to a texel: off by a level the strength multiplies, however wide the blur.
180
+ */
181
+ const SHRUNK_STRENGTH = 3;
182
+
183
+ /**
184
+ * How many times coarser than its input a blur's runs one way may go: none
185
+ * at a texel a stage pixel or under, nor where it does not blur that way;
186
+ * otherwise whole times, to a texel a stage pixel at most, the run still
187
+ * SHRUNK_TEXELS wide for each time strength multiplies it. A run reads a
188
+ * texel for each of its width, so at three texels a stage pixel a glow 68
189
+ * wide read 204 for each texel of an area nine times the stage's; shrunk
190
+ * three times each way, it reads 68 for each of the stage's. Drawn back
191
+ * between the coarser texels, what it blurred is off where it bends: at the
192
+ * ends of a single box's ramp by a quarter of a coarse texel's rise, and
193
+ * where two boxes or more bend smoothly by a sixth of the coarse texel
194
+ * squared over the box's width squared; times the strength a glow multiplies
195
+ * its alpha by, that stays within SHRUNK_ERROR. Each way is its own: a
196
+ * streak 68 wide and 5 high goes coarser across alone.
197
+ */
198
+ export function blurShrink(
199
+ blur: number,
200
+ quality: number,
201
+ strength: number,
202
+ texels: number,
203
+ ): number {
204
+ if (!(blur > 1 && quality > 0) || strength > SHRUNK_STRENGTH) {
205
+ return 1;
206
+ }
207
+
208
+ const box = blur * texels;
209
+ const amplified = 255 * Math.max(1, strength);
210
+ const error =
211
+ quality === 1
212
+ ? (4 * SHRUNK_ERROR * box) / amplified
213
+ : box * Math.sqrt((6 * SHRUNK_ERROR) / amplified);
214
+ const most = Math.min(
215
+ 16,
216
+ Math.floor(texels + 1e-6),
217
+ Math.floor(box / (SHRUNK_TEXELS * Math.max(1, strength))),
218
+ );
219
+ return Math.max(1, Math.min(most, Math.floor(error)));
220
+ }