@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,755 @@
1
+ // A filtered layer's chain of Flash's filters as passes planned ahead of the
2
+ // GPU: for each pass, its program, the targets it reads and draws into, by
3
+ // slot, their frames, and its uniforms' values, as the backend's runner
4
+ // (webgl/filters.ts) draws them. Pure, so that a frame's chains can be
5
+ // planned all at once and their passes run in any order their slots allow.
6
+ import {
7
+ BEVEL,
8
+ BLUR,
9
+ blurShrink,
10
+ COLOR_MATRIX,
11
+ CONVOLUTION,
12
+ DISPLACEMENT,
13
+ type Filters,
14
+ GLOW,
15
+ GRADIENT,
16
+ SHADER,
17
+ } from "./filters.js";
18
+ import {
19
+ filterKernel,
20
+ type KernelImage,
21
+ kernelImagesFit,
22
+ PIXELS_BACK,
23
+ PIXELS_DOWN,
24
+ } from "./kernel-shaders.js";
25
+
26
+ /** The passes' programs, each a fragment shader of filter-shaders.ts. */
27
+ export const BOX_PASS = 1;
28
+ export const SHRINK_PASS = 2;
29
+ export const READ_BACK_PASS = 3;
30
+ export const GLOW_PASS = 4;
31
+ export const BEVEL_PASS = 5;
32
+ export const GRADIENT_PASS = 6;
33
+ export const MATRIX_PASS = 7;
34
+ export const CONVOLUTION_PASS = 8;
35
+ export const DISPLACE_PASS = 9;
36
+ /** A Pixel Bender kernel's pass, its program FilterPass.kernel. */
37
+ export const KERNEL_PASS = 10;
38
+
39
+ /**
40
+ * How a uniform's values are set: one to four floats, an array of vec4s, a
41
+ * 4 by 4 matrix, an array of ivec4s, or a sampler, its value the texture of
42
+ * the frame's it reads.
43
+ */
44
+ export const FLOATS = 1;
45
+ export const VEC4S = 2;
46
+ export const MATRIX4 = 3;
47
+ export const IVEC4S = 4;
48
+ export const SAMPLER = 5;
49
+
50
+ /** A pass's blurred input (FilterPass.blurred): none read, or the pass's input itself. */
51
+ export const NO_BLUR = -2;
52
+ export const INPUT_BLUR = -1;
53
+
54
+ /** The most floats a convolution's matrix takes in its shader: 57 vec4s. */
55
+ const CONVOLUTION_FLOATS = 57 * 4;
56
+
57
+ /** One pass: a program drawn from slot `input` into slot `output`, its uniforms in its plan's. */
58
+ export interface FilterPass {
59
+ program: number;
60
+ input: number;
61
+ /** The input's texels read, from its corner: its frame, or more of it for a shrink. */
62
+ readWidth: number;
63
+ readHeight: number;
64
+ output: number;
65
+ width: number;
66
+ height: number;
67
+ /** The slot read as the blur (NO_BLUR, INPUT_BLUR), and how much coarser it is each way. */
68
+ blurred: number;
69
+ across: number;
70
+ down: number;
71
+ /** A texture of the frame's it reads, a gradient's colours or a map, or -1. */
72
+ texture: number;
73
+ /** A kernel's pass's fragment shader (KERNEL_PASS), or null. */
74
+ kernel: string | null;
75
+ /** Its uniforms in the plan's (uniformName and the rest), from `first` to before `end`. */
76
+ first: number;
77
+ end: number;
78
+ }
79
+
80
+ /**
81
+ * A chain's passes, its targets by slot, each a frame of a width and
82
+ * height, slot 0 the layer it runs on, and the slot its output is left
83
+ * in. Kept from chain to chain, its records reused.
84
+ */
85
+ export class FilterPlan {
86
+ readonly passes: FilterPass[] = [];
87
+ count = 0;
88
+ readonly slotWidth: number[] = [];
89
+ readonly slotHeight: number[] = [];
90
+ slots = 0;
91
+ result = 0;
92
+ /** The target's pixels a unit of its own, which the shaders read their input's size in. */
93
+ resolution = 1;
94
+ /** The uniforms: each one's name, how it is set, where its values start and how many. */
95
+ readonly uniformName: string[] = [];
96
+ readonly uniformKind: number[] = [];
97
+ readonly uniformAt: number[] = [];
98
+ readonly uniformCount: number[] = [];
99
+ uniforms = 0;
100
+ values = new Float32Array(1024);
101
+ valueCount = 0;
102
+ /** The ints of the IVEC4S uniforms, which floats would round: their uniformAt is in here. */
103
+ ints = new Int32Array(64);
104
+ intCount = 0;
105
+
106
+ /** Empty, for a chain run at `resolution` on a layer of `width` by `height`. */
107
+ reset(width: number, height: number, resolution: number): void {
108
+ this.count = 0;
109
+ this.slots = 0;
110
+ this.uniforms = 0;
111
+ this.valueCount = 0;
112
+ this.intCount = 0;
113
+ this.resolution = resolution;
114
+ this.result = this.slot(width, height);
115
+ }
116
+
117
+ /** A new slot, a target of `width` by `height`. */
118
+ slot(width: number, height: number): number {
119
+ this.slotWidth[this.slots] = width;
120
+ this.slotHeight[this.slots] = height;
121
+ return this.slots++;
122
+ }
123
+
124
+ /** A pass begun, its uniforms the ones set after it until the next. */
125
+ pass(
126
+ program: number,
127
+ input: number,
128
+ readWidth: number,
129
+ readHeight: number,
130
+ output: number,
131
+ width: number,
132
+ height: number,
133
+ ): FilterPass {
134
+ let pass = this.passes[this.count];
135
+ if (!pass) {
136
+ pass = {
137
+ program: 0,
138
+ input: 0,
139
+ readWidth: 0,
140
+ readHeight: 0,
141
+ output: 0,
142
+ width: 0,
143
+ height: 0,
144
+ blurred: NO_BLUR,
145
+ across: 1,
146
+ down: 1,
147
+ texture: -1,
148
+ kernel: null,
149
+ first: 0,
150
+ end: 0,
151
+ };
152
+ this.passes[this.count] = pass;
153
+ }
154
+
155
+ this.count++;
156
+ pass.program = program;
157
+ pass.input = input;
158
+ pass.readWidth = readWidth;
159
+ pass.readHeight = readHeight;
160
+ pass.output = output;
161
+ pass.width = width;
162
+ pass.height = height;
163
+ pass.blurred = NO_BLUR;
164
+ pass.across = 1;
165
+ pass.down = 1;
166
+ pass.texture = -1;
167
+ pass.kernel = null;
168
+ pass.first = this.uniforms;
169
+ pass.end = this.uniforms;
170
+ return pass;
171
+ }
172
+
173
+ /** A uniform of `count` floats of the pass begun last, `x` and those after. */
174
+ uniform(name: string, count: number, x: number, y = 0, z = 0, w = 0): void {
175
+ const four = this.four;
176
+ four[0] = x;
177
+ four[1] = y;
178
+ four[2] = z;
179
+ four[3] = w;
180
+ this.uniformFrom(name, FLOATS, four, 0, count);
181
+ }
182
+
183
+ /** uniform's values, passed on without an array of their own. */
184
+ private readonly four = new Float64Array(4);
185
+
186
+ /** A uniform of the pass begun last, set `kind` from `count` of `source` from `at`. */
187
+ uniformFrom(name: string, kind: number, source: ArrayLike<number>, at: number, count: number) {
188
+ const u = this.uniforms++;
189
+ this.uniformName[u] = name;
190
+ this.uniformKind[u] = kind;
191
+ this.uniformAt[u] = this.valueCount;
192
+ this.uniformCount[u] = count;
193
+ if (this.valueCount + count > this.values.length) {
194
+ const grown = new Float32Array(Math.max(this.values.length * 2, this.valueCount + count));
195
+ grown.set(this.values);
196
+ this.values = grown;
197
+ }
198
+
199
+ for (let k = 0; k < count; k++) {
200
+ this.values[this.valueCount + k] = source[at + k];
201
+ }
202
+
203
+ this.valueCount += count;
204
+ this.passes[this.count - 1].end = this.uniforms;
205
+ }
206
+ /** An array of ivec4s of the pass begun last, `count` ints of `source` from `at`. */
207
+ uniformInts(name: string, source: Int32Array, at: number, count: number): void {
208
+ const u = this.uniforms++;
209
+ this.uniformName[u] = name;
210
+ this.uniformKind[u] = IVEC4S;
211
+ this.uniformAt[u] = this.intCount;
212
+ this.uniformCount[u] = count;
213
+ if (this.intCount + count > this.ints.length) {
214
+ const grown = new Int32Array(Math.max(this.ints.length * 2, this.intCount + count));
215
+ grown.set(this.ints);
216
+ this.ints = grown;
217
+ }
218
+
219
+ this.ints.set(source.subarray(at, at + count), this.intCount);
220
+ this.intCount += count;
221
+ this.passes[this.count - 1].end = this.uniforms;
222
+ }
223
+ }
224
+
225
+ /** What a blur made of a slot: the slot it is in, and how much coarser it is each way. */
226
+ interface Blurred {
227
+ slot: number;
228
+ across: number;
229
+ down: number;
230
+ }
231
+
232
+ /**
233
+ * Where a filtered layer's object lies in it, in texels from its corner,
234
+ * and its size: Flash's bitmap of the object, which a ShaderFilter's
235
+ * coordinates start at, though the layer was cut to the target. Null for
236
+ * the layer less its padding each way.
237
+ */
238
+ export interface KernelObject {
239
+ x: number;
240
+ y: number;
241
+ width: number;
242
+ height: number;
243
+ }
244
+
245
+ /**
246
+ * Chain `chain` of `filters` planned into `plan` over a layer of `width` by
247
+ * `height` texels at `resolution`, `hasTexture` telling which of the
248
+ * frame's textures there are: a filter whose texture is not leaves its
249
+ * input as it is, as one that changes nothing does; `links` whether a
250
+ * kernel's pass's fragment shader links, as one that does not leaves it
251
+ * too; `object` where the layer's object lies in it.
252
+ */
253
+ export function planChain(
254
+ plan: FilterPlan,
255
+ filters: Filters,
256
+ chain: number,
257
+ width: number,
258
+ height: number,
259
+ resolution: number,
260
+ hasTexture: (id: number) => boolean,
261
+ links: (fragment: string) => boolean = () => true,
262
+ object: KernelObject | null = null,
263
+ ): void {
264
+ plan.reset(width, height, resolution);
265
+ const end = filters.chains[chain + 1];
266
+ for (let i = filters.chains[chain]; i < end; i++) {
267
+ plan.result =
268
+ filters.kind[i] === SHADER
269
+ ? shader(plan, filters, i, plan.result, width, height, hasTexture, links, object)
270
+ : planFilter(
271
+ plan,
272
+ filters.kind[i],
273
+ filters.values,
274
+ filters.start[i],
275
+ filters.texture[i],
276
+ plan.result,
277
+ width,
278
+ height,
279
+ hasTexture,
280
+ );
281
+ }
282
+ }
283
+
284
+ /** One filter, its values from `v[at]`, over slot `input`: the slot it drew into, or `input`. */
285
+ function planFilter(
286
+ plan: FilterPlan,
287
+ kind: number,
288
+ v: Float32Array,
289
+ at: number,
290
+ texture: number,
291
+ input: number,
292
+ width: number,
293
+ height: number,
294
+ hasTexture: (id: number) => boolean,
295
+ ): number {
296
+ switch (kind) {
297
+ case BLUR:
298
+ return blurFilter(plan, v, at, input, width, height);
299
+ case GLOW:
300
+ return glow(plan, v, at, input, width, height);
301
+ case BEVEL:
302
+ return bevel(plan, v, at, input, width, height);
303
+ case GRADIENT:
304
+ return hasTexture(texture) ? gradient(plan, v, at, texture, input, width, height) : input;
305
+ case COLOR_MATRIX:
306
+ return colorMatrix(plan, v, at, input, width, height);
307
+ case CONVOLUTION:
308
+ return convolution(plan, v, at, input, width, height);
309
+ case DISPLACEMENT:
310
+ return hasTexture(texture) ? displacement(plan, v, at, texture, input, width, height) : input;
311
+ default:
312
+ return input;
313
+ }
314
+ }
315
+
316
+ /** A blur: the box's runs, done coarser and drawn back between their texels where that is close enough. */
317
+ function blurFilter(
318
+ plan: FilterPlan,
319
+ v: Float32Array,
320
+ at: number,
321
+ input: number,
322
+ width: number,
323
+ height: number,
324
+ ): number {
325
+ const blurX = v[at];
326
+ const blurY = v[at + 1];
327
+ const quality = v[at + 2];
328
+ if (!(quality > 0 && (blurX > 1 || blurY > 1))) {
329
+ return input;
330
+ }
331
+
332
+ const texels = plan.resolution * v[at + 3];
333
+ const across = blurShrink(blurX, quality, 1, texels);
334
+ const down = blurShrink(blurY, quality, 1, texels);
335
+ if (across === 1 && down === 1) {
336
+ const out = plan.slot(width, height);
337
+ boxRuns(plan, input, width, height, out, blurX, blurY, quality, texels, texels);
338
+ return out;
339
+ }
340
+
341
+ const blurred = blurredOf(
342
+ plan,
343
+ input,
344
+ width,
345
+ height,
346
+ blurX,
347
+ blurY,
348
+ quality,
349
+ texels,
350
+ across,
351
+ down,
352
+ );
353
+ const out = plan.slot(width, height);
354
+ readBlurred(plan.pass(READ_BACK_PASS, input, width, height, out, width, height), blurred);
355
+ return out;
356
+ }
357
+
358
+ /** A glow or a drop shadow: the input's alpha blurred, coarser where close enough, in its colour. */
359
+ function glow(
360
+ plan: FilterPlan,
361
+ v: Float32Array,
362
+ at: number,
363
+ input: number,
364
+ width: number,
365
+ height: number,
366
+ ): number {
367
+ const blurX = v[at];
368
+ const blurY = v[at + 1];
369
+ const quality = v[at + 2];
370
+ const strength = v[at + 8];
371
+ const texels = plan.resolution * v[at + 3];
372
+ const across = blurShrink(blurX, quality, strength, texels);
373
+ const down = blurShrink(blurY, quality, strength, texels);
374
+ const blurred = blurredOf(
375
+ plan,
376
+ input,
377
+ width,
378
+ height,
379
+ blurX,
380
+ blurY,
381
+ quality,
382
+ texels,
383
+ across,
384
+ down,
385
+ );
386
+ const out = plan.slot(width, height);
387
+ readBlurred(plan.pass(GLOW_PASS, input, width, height, out, width, height), blurred);
388
+ plan.uniformFrom("uColor", FLOATS, v, at + 4, 3);
389
+ plan.uniform("uAlpha", 1, v[at + 7]);
390
+ plan.uniform("uStrength", 1, strength);
391
+ plan.uniformFrom("uOffset", FLOATS, v, at + 9, 2);
392
+ plan.uniform("uInner", 1, v[at + 11]);
393
+ plan.uniform("uKnockout", 1, v[at + 12]);
394
+ plan.uniform("uHide", 1, v[at + 13]);
395
+ return out;
396
+ }
397
+
398
+ /** A bevel: the input's alpha blurred, read on and back by its offset. */
399
+ function bevel(
400
+ plan: FilterPlan,
401
+ v: Float32Array,
402
+ at: number,
403
+ input: number,
404
+ width: number,
405
+ height: number,
406
+ ): number {
407
+ const texels = plan.resolution * v[at + 3];
408
+ const blurred = blurredOf(plan, input, width, height, v[at], v[at + 1], v[at + 2], texels, 1, 1);
409
+ const out = plan.slot(width, height);
410
+ readBlurred(plan.pass(BEVEL_PASS, input, width, height, out, width, height), blurred);
411
+ plan.uniformFrom("uHighlight", FLOATS, v, at + 4, 3);
412
+ plan.uniformFrom("uShadow", FLOATS, v, at + 7, 3);
413
+ plan.uniform("uHighlightAlpha", 1, v[at + 10]);
414
+ plan.uniform("uShadowAlpha", 1, v[at + 11]);
415
+ plan.uniform("uStrength", 1, v[at + 12]);
416
+ plan.uniformFrom("uOffset", FLOATS, v, at + 13, 2);
417
+ plan.uniform("uType", 1, v[at + 15]);
418
+ plan.uniform("uKnockout", 1, v[at + 16]);
419
+ return out;
420
+ }
421
+
422
+ /** A gradient glow or bevel: the blurred alpha picking a colour of its gradient, texture `id`. */
423
+ function gradient(
424
+ plan: FilterPlan,
425
+ v: Float32Array,
426
+ at: number,
427
+ id: number,
428
+ input: number,
429
+ width: number,
430
+ height: number,
431
+ ): number {
432
+ const units = v[at + 3];
433
+ const texels = plan.resolution * units;
434
+ const blurred = blurredOf(plan, input, width, height, v[at], v[at + 1], v[at + 2], texels, 1, 1);
435
+ const out = plan.slot(width, height);
436
+ const pass = plan.pass(GRADIENT_PASS, input, width, height, out, width, height);
437
+ readBlurred(pass, blurred);
438
+ pass.texture = id;
439
+ plan.uniform("uStrength", 1, v[at + 4]);
440
+ plan.uniformFrom("uOffset", FLOATS, v, at + 5, 2);
441
+ plan.uniform("uType", 1, v[at + 7]);
442
+ plan.uniform("uKnockout", 1, v[at + 8]);
443
+ plan.uniform("uBevel", 1, v[at + 9]);
444
+ const inset = v[at + 10];
445
+ plan.uniform(
446
+ "uRegion",
447
+ 4,
448
+ inset - v[at + 11] * units,
449
+ inset - v[at + 12] * units,
450
+ width / plan.resolution - inset + units + v[at + 13] * units,
451
+ height / plan.resolution - inset + units + v[at + 14] * units,
452
+ );
453
+ return out;
454
+ }
455
+
456
+ function colorMatrix(
457
+ plan: FilterPlan,
458
+ v: Float32Array,
459
+ at: number,
460
+ input: number,
461
+ width: number,
462
+ height: number,
463
+ ): number {
464
+ const out = plan.slot(width, height);
465
+ plan.pass(MATRIX_PASS, input, width, height, out, width, height);
466
+ plan.uniformFrom("uMatrix", MATRIX4, v, at, 16);
467
+ plan.uniformFrom("uOffset", FLOATS, v, at + 16, 4);
468
+ return out;
469
+ }
470
+
471
+ /** A convolution's weights, its matrix by rows padded to the shader's array. */
472
+ const weights = new Float32Array(CONVOLUTION_FLOATS);
473
+
474
+ function convolution(
475
+ plan: FilterPlan,
476
+ v: Float32Array,
477
+ at: number,
478
+ input: number,
479
+ width: number,
480
+ height: number,
481
+ ): number {
482
+ const columns = v[at];
483
+ const rows = v[at + 1];
484
+ weights.fill(0);
485
+ weights.set(v.subarray(at + 12, at + 12 + Math.min(columns * rows, weights.length)));
486
+ const out = plan.slot(width, height);
487
+ plan.pass(CONVOLUTION_PASS, input, width, height, out, width, height);
488
+ plan.uniformFrom("uMatrix", VEC4S, weights, 0, weights.length);
489
+ plan.uniform("uSize", 2, columns, rows);
490
+ plan.uniformFrom("uEdge", FLOATS, v, at + 2, 4);
491
+ plan.uniform("uDivisor", 1, v[at + 6]);
492
+ plan.uniform("uReciprocal", 1, v[at + 7]);
493
+ plan.uniform("uBias", 1, v[at + 8]);
494
+ plan.uniform("uClamp", 1, v[at + 9]);
495
+ plan.uniform("uPreserve", 1, v[at + 10]);
496
+ const inset = v[at + 11];
497
+ plan.uniform(
498
+ "uBox",
499
+ 4,
500
+ inset,
501
+ inset,
502
+ width / plan.resolution - inset + 1,
503
+ height / plan.resolution - inset + 1,
504
+ );
505
+ return out;
506
+ }
507
+
508
+ /** A displacement map: each of the object's pixels read from where its map, texture `id`, moves it. */
509
+ function displacement(
510
+ plan: FilterPlan,
511
+ v: Float32Array,
512
+ at: number,
513
+ id: number,
514
+ input: number,
515
+ width: number,
516
+ height: number,
517
+ ): number {
518
+ const out = plan.slot(width, height);
519
+ const pass = plan.pass(DISPLACE_PASS, input, width, height, out, width, height);
520
+ pass.texture = id;
521
+ plan.uniformFrom("uComponentX", FLOATS, v, at, 4);
522
+ plan.uniformFrom("uComponentY", FLOATS, v, at + 4, 4);
523
+ plan.uniformFrom("uScale", FLOATS, v, at + 8, 2);
524
+ plan.uniform("uMode", 1, v[at + 10]);
525
+ plan.uniformFrom("uColor", FLOATS, v, at + 11, 4);
526
+ plan.uniformFrom("uGrow", FLOATS, v, at + 15, 2);
527
+ const inset = v[at + 17];
528
+ plan.uniform(
529
+ "uBox",
530
+ 4,
531
+ inset,
532
+ inset,
533
+ width / plan.resolution - inset + 1,
534
+ height / plan.resolution - inset + 1,
535
+ );
536
+ plan.uniform("uMapAt", 4, inset + v[at + 18], inset + v[at + 19], v[at + 20], v[at + 21]);
537
+ return out;
538
+ }
539
+
540
+ /** A kernel's images, read as the plan's of a ShaderFilter are: each one's number and kind. */
541
+ const kernelImages: KernelImage[] = [];
542
+
543
+ /**
544
+ * A ShaderFilter, filter `i` of `filters`: its kernel (kernel-shaders.ts,
545
+ * filterKernel) over the input, the bitmap it reads lying where the
546
+ * object's does, grown by the filters before it, and its output that
547
+ * grown by its extensions; its images and parameters as uniforms. Run
548
+ * once a stage pixel, as Flash runs it over its bitmap of the object:
549
+ * where a whole number of the input's texels make a stage pixel and the
550
+ * bitmap starts on a texel, the input is shrunk to one a pixel on the
551
+ * bitmap's grid, the mean of its texels within the frame, run, and drawn
552
+ * back, each texel its pixel's; else run at each texel. One of whose
553
+ * images there is no texture, of more images than a program binds, or
554
+ * whose shader will not link, leaves its input be.
555
+ */
556
+ function shader(
557
+ plan: FilterPlan,
558
+ filters: Filters,
559
+ i: number,
560
+ input: number,
561
+ width: number,
562
+ height: number,
563
+ hasTexture: (id: number) => boolean,
564
+ links: (fragment: string) => boolean,
565
+ object: KernelObject | null,
566
+ ): number {
567
+ const v = filters.values;
568
+ const at = filters.start[i];
569
+ const kernel = filters.kernels[i];
570
+ const units = v[at];
571
+ const inset = v[at + 1];
572
+ const padding = v[at + 2];
573
+ const count = v[at + 7];
574
+ kernelImages.length = 0;
575
+ for (let k = 0; k < count; k++) {
576
+ const id = v[at + 9 + k * 3];
577
+ if (id >= 0 && !hasTexture(id)) {
578
+ return input;
579
+ }
580
+
581
+ kernelImages.push({ texture: v[at + 8 + k * 3], layer: id < 0, float: v[at + 10 + k * 3] > 0 });
582
+ }
583
+
584
+ const fragment = kernel ? filterKernel(kernel, kernelImages) : null;
585
+ if (!fragment || !kernelImagesFit(kernelImages) || !links(fragment)) {
586
+ return input;
587
+ }
588
+
589
+ // The bitmap this filter reads: the object's, grown by the filters before it.
590
+ const res = plan.resolution;
591
+ const texels = units * res;
592
+ const before = (padding - inset) * res;
593
+ const x = (object ? object.x : padding * res) - before;
594
+ const y = (object ? object.y : padding * res) - before;
595
+ const bitmapWidth = (object ? object.width : width - padding * res * 2) + before * 2;
596
+ const bitmapHeight = (object ? object.height : height - padding * res * 2) + before * 2;
597
+ const shrink =
598
+ texels > 1 &&
599
+ texels <= 16 &&
600
+ Number.isInteger(texels) &&
601
+ Number.isInteger(x) &&
602
+ Number.isInteger(y)
603
+ ? texels
604
+ : 1;
605
+ // The grid's first pixel from the frame's corner, at or before it, a line of it on the bitmap's.
606
+ const startX = x - shrink * Math.ceil(x / shrink);
607
+ const startY = y - shrink * Math.ceil(y / shrink);
608
+ let from = input;
609
+ let w = width;
610
+ let h = height;
611
+ if (shrink > 1) {
612
+ w = Math.ceil((width - startX) / shrink);
613
+ h = Math.ceil((height - startY) / shrink);
614
+ from = plan.slot(w, h);
615
+ plan.pass(KERNEL_PASS, input, width, height, from, w, h).kernel = PIXELS_DOWN;
616
+ plan.uniform("uDown", 3, shrink, startX, startY);
617
+ plan.uniform("uKernelFrame", 2, width, height);
618
+ }
619
+
620
+ let out = plan.slot(w, h);
621
+ const pass = plan.pass(KERNEL_PASS, from, w, h, out, w, h);
622
+ pass.kernel = fragment;
623
+ plan.uniform("uKernelAt", 3, (x - startX) / shrink, (y - startY) / shrink, texels / shrink);
624
+ plan.uniform("uKernelFrame", 2, w, h);
625
+ plan.uniform(
626
+ "uKernelRegion",
627
+ 4,
628
+ -v[at + 3],
629
+ -v[at + 4],
630
+ bitmapWidth / texels + v[at + 5],
631
+ bitmapHeight / texels + v[at + 6],
632
+ );
633
+ for (let k = 0; k < count; k++) {
634
+ if (v[at + 9 + k * 3] >= 0) {
635
+ plan.uniformFrom(`pbImage${v[at + 8 + k * 3]}`, SAMPLER, v, at + 9 + k * 3, 1);
636
+ }
637
+ }
638
+
639
+ const floats = at + 8 + count * 3;
640
+ if (v[floats] > 0) {
641
+ plan.uniformFrom("pbFloats", VEC4S, v, floats + 1, v[floats]);
642
+ }
643
+
644
+ const intsAt = filters.intStart[i];
645
+ const ints = filters.intStart[i + 1] - intsAt;
646
+ if (ints > 0) {
647
+ plan.uniformInts("pbInts", filters.ints, intsAt, ints);
648
+ }
649
+
650
+ if (shrink > 1) {
651
+ const pixels = out;
652
+ out = plan.slot(width, height);
653
+ plan.pass(KERNEL_PASS, pixels, w, h, out, width, height).kernel = PIXELS_BACK;
654
+ plan.uniform("uDown", 3, shrink, startX, startY);
655
+ }
656
+
657
+ return out;
658
+ }
659
+
660
+ /** `pass` reading `blurred` as its blur, or its input where nothing blurred it. */
661
+ function readBlurred(pass: FilterPass, blurred: Blurred | null): void {
662
+ pass.blurred = blurred ? blurred.slot : INPUT_BLUR;
663
+ pass.across = blurred ? blurred.across : 1;
664
+ pass.down = blurred ? blurred.down : 1;
665
+ }
666
+
667
+ /**
668
+ * The box's runs over `input`'s frame into a new slot, `across` times
669
+ * narrower and `down` times lower (blurShrink); null where no run would
670
+ * change it.
671
+ */
672
+ function blurredOf(
673
+ plan: FilterPlan,
674
+ input: number,
675
+ width: number,
676
+ height: number,
677
+ blurX: number,
678
+ blurY: number,
679
+ quality: number,
680
+ texels: number,
681
+ across: number,
682
+ down: number,
683
+ ): Blurred | null {
684
+ if (!(quality > 0 && (blurX > 1 || blurY > 1))) {
685
+ return null;
686
+ }
687
+
688
+ let source = input;
689
+ let w = width;
690
+ let h = height;
691
+ if (across > 1 || down > 1) {
692
+ w = Math.ceil(width / across);
693
+ h = Math.ceil(height / down);
694
+ source = plan.slot(w, h);
695
+ plan.pass(SHRINK_PASS, input, w * across, h * down, source, w, h);
696
+ plan.uniform("uFactor", 2, across, down);
697
+ }
698
+
699
+ const out = plan.slot(w, h);
700
+ boxRuns(plan, source, w, h, out, blurX, blurY, quality, texels / across, texels / down);
701
+ blurredScratch.slot = out;
702
+ blurredScratch.across = across;
703
+ blurredScratch.down = down;
704
+ return blurredScratch;
705
+ }
706
+
707
+ /** What blurredOf gives, read before it is asked again. */
708
+ const blurredScratch: Blurred = { slot: 0, across: 1, down: 1 };
709
+
710
+ /**
711
+ * The box's runs over slot `input`'s frame into slot `output`'s, through
712
+ * slots between them, the second only where a third run needs one:
713
+ * `texelsX` and `texelsY` to a stage pixel each way, which the blur is in,
714
+ * each step a texel of the input.
715
+ */
716
+ function boxRuns(
717
+ plan: FilterPlan,
718
+ input: number,
719
+ width: number,
720
+ height: number,
721
+ output: number,
722
+ blurX: number,
723
+ blurY: number,
724
+ quality: number,
725
+ texelsX: number,
726
+ texelsY: number,
727
+ ): void {
728
+ const step = 1 / plan.resolution;
729
+ const across = blurX > 1;
730
+ const down = blurY > 1;
731
+ const runs = quality * ((across ? 1 : 0) + (down ? 1 : 0));
732
+ if (runs === 0) {
733
+ plan.pass(BOX_PASS, input, width, height, output, width, height);
734
+ plan.uniform("uDirection", 2, 1, 0);
735
+ plan.uniform("uWidth", 1, 1);
736
+ return;
737
+ }
738
+
739
+ const first = runs > 1 ? plan.slot(width, height) : -1;
740
+ const second = runs > 2 ? plan.slot(width, height) : -1;
741
+ let from = input;
742
+ for (let k = 0; k < runs; k++) {
743
+ const to = k === runs - 1 ? output : k % 2 === 0 ? first : second;
744
+ plan.pass(BOX_PASS, from, width, height, to, width, height);
745
+ if (across && (!down || k % 2 === 0)) {
746
+ plan.uniform("uDirection", 2, step, 0);
747
+ plan.uniform("uWidth", 1, blurX * texelsX);
748
+ } else {
749
+ plan.uniform("uDirection", 2, 0, step);
750
+ plan.uniform("uWidth", 1, blurY * texelsY);
751
+ }
752
+
753
+ from = to;
754
+ }
755
+ }