etro 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +43 -0
- package/CONTRIBUTING.md +23 -20
- package/README.md +3 -2
- package/dist/custom-array.d.ts +10 -0
- package/dist/effect/base.d.ts +10 -1
- package/dist/effect/shader.d.ts +11 -1
- package/dist/effect/stack.d.ts +6 -2
- package/dist/etro-cjs.js +1156 -575
- package/dist/etro-iife.js +1156 -575
- package/dist/event.d.ts +10 -5
- package/dist/layer/audio-source.d.ts +9 -4
- package/dist/layer/audio.d.ts +15 -2
- package/dist/layer/base.d.ts +49 -3
- package/dist/layer/image.d.ts +15 -1
- package/dist/layer/text.d.ts +3 -0
- package/dist/layer/video.d.ts +13 -1
- package/dist/layer/visual.d.ts +6 -2
- package/dist/movie/effects.d.ts +6 -0
- package/dist/movie/index.d.ts +1 -0
- package/dist/movie/layers.d.ts +6 -0
- package/dist/movie/movie.d.ts +260 -0
- package/dist/object.d.ts +9 -6
- package/dist/util.d.ts +4 -10
- package/eslint.conf.js +2 -2
- package/karma.conf.js +4 -7
- package/package.json +8 -7
- package/src/custom-array.ts +43 -0
- package/src/effect/base.ts +23 -22
- package/src/effect/gaussian-blur.ts +11 -6
- package/src/effect/pixelate.ts +3 -3
- package/src/effect/shader.ts +33 -27
- package/src/effect/stack.ts +43 -30
- package/src/effect/transform.ts +14 -7
- package/src/event.ts +111 -21
- package/src/layer/audio-source.ts +60 -20
- package/src/layer/audio.ts +25 -3
- package/src/layer/base.ts +79 -26
- package/src/layer/image.ts +26 -2
- package/src/layer/text.ts +7 -0
- package/src/layer/video.ts +31 -4
- package/src/layer/visual-source.ts +43 -1
- package/src/layer/visual.ts +50 -28
- package/src/movie/effects.ts +26 -0
- package/src/movie/index.ts +1 -0
- package/src/movie/layers.ts +26 -0
- package/src/movie/movie.ts +855 -0
- package/src/object.ts +9 -6
- package/src/util.ts +68 -89
- package/dist/movie.d.ts +0 -201
- package/src/movie.ts +0 -744
- /package/scripts/{gen-effect-samples.html → effect/gen-effect-samples.html} +0 -0
- /package/scripts/{save-effect-samples.js → effect/save-effect-samples.js} +0 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "etro",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "An extendable video-editing framework for the browser
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "An extendable video-editing framework for the browser",
|
|
5
5
|
"browser": "dist/etro-cjs.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"directories": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"@types/jest": "^29.0.0",
|
|
15
15
|
"@typescript-eslint/eslint-plugin": "^5.30.7",
|
|
16
16
|
"@typescript-eslint/parser": "^5.30.7",
|
|
17
|
+
"canvas": "^2.11.2",
|
|
17
18
|
"docdash": "^1.1.1",
|
|
18
19
|
"ecstatic": ">=4.1.3",
|
|
19
20
|
"eslint": "^8.20.0",
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
"rollup-plugin-node-resolve": "^5.2.0",
|
|
41
42
|
"rollup-plugin-typescript2": "^0.29.0",
|
|
42
43
|
"rollup-plugin-uglify-es": "^0.0.1",
|
|
43
|
-
"shipjs": "^0.
|
|
44
|
+
"shipjs": "^0.26.3",
|
|
44
45
|
"typedoc": "^0.23.8",
|
|
45
46
|
"typescript": "^4.7.4"
|
|
46
47
|
},
|
|
@@ -48,11 +49,11 @@
|
|
|
48
49
|
"build": "rollup -c",
|
|
49
50
|
"doc": "rm -rf docs && npx typedoc src/etro.ts --excludePrivate --readme none",
|
|
50
51
|
"assets": "git fetch origin example-assets:example-assets && git cherry-pick example-assets && git reset --soft HEAD^ && git reset HEAD examples/assets",
|
|
51
|
-
"effects": "node scripts/save-effect-samples.js",
|
|
52
|
+
"effects": "node scripts/effect/save-effect-samples.js",
|
|
52
53
|
"lint": "npm run --silent lint:main && npm run --silent lint:test && npm run --silent lint:examples",
|
|
53
|
-
"lint:main": "eslint -c eslint.typescript-conf.js --ext .ts src",
|
|
54
|
-
"lint:test": "eslint -c eslint.test-conf.js --ext .ts spec",
|
|
55
|
-
"lint:examples": "eslint -c eslint.example-conf.js --ext .html examples",
|
|
54
|
+
"lint:main": "eslint -c eslint.typescript-conf.js --ext .ts --fix src",
|
|
55
|
+
"lint:test": "eslint -c eslint.test-conf.js --ext .ts --fix spec",
|
|
56
|
+
"lint:examples": "eslint -c eslint.example-conf.js --ext .html --fix examples",
|
|
56
57
|
"start": "http-server",
|
|
57
58
|
"test": "karma start",
|
|
58
59
|
"release": "shipjs prepare"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export abstract class CustomArrayListener<T> {
|
|
2
|
+
abstract onAdd(item: T): void
|
|
3
|
+
abstract onRemove(item: T): void
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* An array that notifies a listener when items are added or removed.
|
|
8
|
+
*/
|
|
9
|
+
export class CustomArray<T> extends Array<T> {
|
|
10
|
+
constructor (target: T[], listener: CustomArrayListener<T>) {
|
|
11
|
+
super()
|
|
12
|
+
|
|
13
|
+
for (const item of target) {
|
|
14
|
+
listener.onAdd(item)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Create proxy
|
|
18
|
+
return new Proxy(target, {
|
|
19
|
+
deleteProperty (target, property): boolean {
|
|
20
|
+
const value = target[property]
|
|
21
|
+
delete target[property]
|
|
22
|
+
listener.onRemove(value)
|
|
23
|
+
return true
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
set (target, property, value): boolean {
|
|
27
|
+
const oldValue = target[property]
|
|
28
|
+
target[property] = value
|
|
29
|
+
|
|
30
|
+
// Check if property is a number (index)
|
|
31
|
+
if (!isNaN(Number(property))) {
|
|
32
|
+
if (oldValue !== undefined) {
|
|
33
|
+
listener.onRemove(oldValue)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
listener.onAdd(value)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
}) as T[]
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/effect/base.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { watchPublic } from '../util'
|
|
2
|
-
import { publish, subscribe } from '../event'
|
|
3
1
|
import { Movie } from '../movie'
|
|
4
2
|
import { Base as BaseLayer } from '../layer/index'
|
|
5
3
|
import BaseObject from '../object'
|
|
@@ -23,31 +21,24 @@ export class Base implements BaseObject {
|
|
|
23
21
|
private _occurrenceCount: number
|
|
24
22
|
|
|
25
23
|
constructor () {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
newThis._occurrenceCount = 0
|
|
30
|
-
newThis._target = null
|
|
31
|
-
|
|
32
|
-
// Propogate up to target
|
|
33
|
-
subscribe(newThis, 'effect.change.modify', event => {
|
|
34
|
-
if (!newThis._target)
|
|
35
|
-
return
|
|
36
|
-
|
|
37
|
-
const type = `${newThis._target.type}.change.effect.modify`
|
|
38
|
-
publish(newThis._target, type, { ...event, target: newThis._target, source: newThis, type })
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
return newThis
|
|
24
|
+
this.enabled = true
|
|
25
|
+
this._occurrenceCount = 0
|
|
26
|
+
this._target = null
|
|
42
27
|
}
|
|
43
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Wait until this effect is ready to be applied
|
|
31
|
+
*/
|
|
32
|
+
async whenReady (): Promise<void> {} // eslint-disable-line @typescript-eslint/no-empty-function
|
|
33
|
+
|
|
44
34
|
/**
|
|
45
35
|
* Attaches this effect to `target` if not already attached.
|
|
46
36
|
* @ignore
|
|
47
37
|
*/
|
|
48
38
|
tryAttach (target: Movie | BaseLayer): void {
|
|
49
|
-
if (this._occurrenceCount === 0)
|
|
39
|
+
if (this._occurrenceCount === 0) {
|
|
50
40
|
this.attach(target)
|
|
41
|
+
}
|
|
51
42
|
|
|
52
43
|
this._occurrenceCount++
|
|
53
44
|
}
|
|
@@ -57,21 +48,23 @@ export class Base implements BaseObject {
|
|
|
57
48
|
}
|
|
58
49
|
|
|
59
50
|
/**
|
|
60
|
-
*
|
|
51
|
+
* Detaches this effect from its target if the number of times `tryDetach`
|
|
61
52
|
* has been called (including this call) equals the number of times
|
|
62
53
|
* `tryAttach` has been called.
|
|
63
54
|
*
|
|
64
55
|
* @ignore
|
|
65
56
|
*/
|
|
66
57
|
tryDetach (): void {
|
|
67
|
-
if (this._target === null)
|
|
58
|
+
if (this._target === null) {
|
|
68
59
|
throw new Error('No movie to detach from')
|
|
60
|
+
}
|
|
69
61
|
|
|
70
62
|
this._occurrenceCount--
|
|
71
63
|
// If this effect occurs in another place in the containing array, do not
|
|
72
64
|
// unset _target. (For calling `unshift` on the `layers` proxy)
|
|
73
|
-
if (this._occurrenceCount === 0)
|
|
65
|
+
if (this._occurrenceCount === 0) {
|
|
74
66
|
this.detach()
|
|
67
|
+
}
|
|
75
68
|
}
|
|
76
69
|
|
|
77
70
|
detach (): void {
|
|
@@ -96,6 +89,11 @@ export class Base implements BaseObject {
|
|
|
96
89
|
return this._target ? this._target.currentTime : undefined
|
|
97
90
|
}
|
|
98
91
|
|
|
92
|
+
/** `true` if this effect is ready to be applied */
|
|
93
|
+
get ready (): boolean {
|
|
94
|
+
return true
|
|
95
|
+
}
|
|
96
|
+
|
|
99
97
|
get parent (): Movie | BaseLayer {
|
|
100
98
|
return this._target
|
|
101
99
|
}
|
|
@@ -104,6 +102,9 @@ export class Base implements BaseObject {
|
|
|
104
102
|
return this._target ? this._target.movie : undefined
|
|
105
103
|
}
|
|
106
104
|
|
|
105
|
+
/**
|
|
106
|
+
* @deprecated See {@link https://github.com/etro-js/etro/issues/131}
|
|
107
|
+
*/
|
|
107
108
|
getDefaultOptions (): Record<string, unknown> {
|
|
108
109
|
return {}
|
|
109
110
|
}
|
|
@@ -12,7 +12,7 @@ export interface GaussianBlurOptions {
|
|
|
12
12
|
* Applies a Gaussian blur
|
|
13
13
|
*/
|
|
14
14
|
// TODO: Improve performance
|
|
15
|
-
// TODO: Make sure this is truly gaussian even though it
|
|
15
|
+
// TODO: Make sure this is truly gaussian even though it doesn't require a
|
|
16
16
|
// standard deviation
|
|
17
17
|
export class GaussianBlur extends Stack {
|
|
18
18
|
constructor (options: GaussianBlurOptions) {
|
|
@@ -63,11 +63,12 @@ class GaussianBlurComponent extends Shader {
|
|
|
63
63
|
|
|
64
64
|
apply (target: Movie | Visual, reltime: number): void {
|
|
65
65
|
const radiusVal = val(this, 'radius', reltime)
|
|
66
|
-
if (radiusVal !== this._radiusCache)
|
|
66
|
+
if (radiusVal !== this._radiusCache) {
|
|
67
67
|
// Regenerate gaussian distribution canvas.
|
|
68
68
|
this.shape = GaussianBlurComponent._render1DKernel(
|
|
69
69
|
GaussianBlurComponent._gen1DKernel(radiusVal)
|
|
70
70
|
)
|
|
71
|
+
}
|
|
71
72
|
|
|
72
73
|
this._radiusCache = radiusVal
|
|
73
74
|
|
|
@@ -106,18 +107,21 @@ class GaussianBlurComponent extends Shader {
|
|
|
106
107
|
const pascal = GaussianBlurComponent._genPascalRow(2 * radius + 1)
|
|
107
108
|
// don't use `reduce` and `map` (overhead?)
|
|
108
109
|
let sum = 0
|
|
109
|
-
for (let i = 0; i < pascal.length; i++)
|
|
110
|
+
for (let i = 0; i < pascal.length; i++) {
|
|
110
111
|
sum += pascal[i]
|
|
112
|
+
}
|
|
111
113
|
|
|
112
|
-
for (let i = 0; i < pascal.length; i++)
|
|
114
|
+
for (let i = 0; i < pascal.length; i++) {
|
|
113
115
|
pascal[i] /= sum
|
|
116
|
+
}
|
|
114
117
|
|
|
115
118
|
return pascal
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
private static _genPascalRow (index: number): number[] {
|
|
119
|
-
if (index < 0)
|
|
122
|
+
if (index < 0) {
|
|
120
123
|
throw new Error(`Invalid index ${index}`)
|
|
124
|
+
}
|
|
121
125
|
|
|
122
126
|
let currRow = [1]
|
|
123
127
|
for (let i = 1; i < index; i++) {
|
|
@@ -125,8 +129,9 @@ class GaussianBlurComponent extends Shader {
|
|
|
125
129
|
nextRow.length = currRow.length + 1
|
|
126
130
|
// edges are always 1's
|
|
127
131
|
nextRow[0] = nextRow[nextRow.length - 1] = 1
|
|
128
|
-
for (let j = 1; j < nextRow.length - 1; j++)
|
|
132
|
+
for (let j = 1; j < nextRow.length - 1; j++) {
|
|
129
133
|
nextRow[j] = currRow[j - 1] + currRow[j]
|
|
134
|
+
}
|
|
130
135
|
|
|
131
136
|
currRow = nextRow
|
|
132
137
|
}
|
package/src/effect/pixelate.ts
CHANGED
|
@@ -43,15 +43,15 @@ export class Pixelate extends Shader {
|
|
|
43
43
|
pixelSize: '1i'
|
|
44
44
|
}
|
|
45
45
|
})
|
|
46
|
-
|
|
47
|
-
*/
|
|
46
|
+
|
|
48
47
|
this.pixelSize = options.pixelSize || 1
|
|
49
48
|
}
|
|
50
49
|
|
|
51
50
|
apply (target: Movie | Visual, reltime: number): void {
|
|
52
51
|
const ps = val(this, 'pixelSize', reltime)
|
|
53
|
-
if (ps % 1 !== 0 || ps < 0)
|
|
52
|
+
if (ps % 1 !== 0 || ps < 0) {
|
|
54
53
|
throw new Error('Pixel size must be a nonnegative integer')
|
|
54
|
+
}
|
|
55
55
|
|
|
56
56
|
super.apply(target, reltime)
|
|
57
57
|
}
|
package/src/effect/shader.ts
CHANGED
|
@@ -4,6 +4,9 @@ import { val } from '../util'
|
|
|
4
4
|
import { Visual } from './visual'
|
|
5
5
|
|
|
6
6
|
export interface UniformOptions {
|
|
7
|
+
/**
|
|
8
|
+
* The type of the uniform.
|
|
9
|
+
*/
|
|
7
10
|
type?: string
|
|
8
11
|
defaultFloatComponent?: number
|
|
9
12
|
}
|
|
@@ -117,8 +120,9 @@ export class Shader extends Visual {
|
|
|
117
120
|
private _initGl () {
|
|
118
121
|
this._canvas = document.createElement('canvas')
|
|
119
122
|
const gl = this._canvas.getContext('webgl')
|
|
120
|
-
if (gl === null)
|
|
123
|
+
if (gl === null) {
|
|
121
124
|
throw new Error('Unable to initialize WebGL. Your browser or machine may not support it.')
|
|
125
|
+
}
|
|
122
126
|
|
|
123
127
|
this._gl = gl
|
|
124
128
|
return gl
|
|
@@ -127,8 +131,9 @@ export class Shader extends Visual {
|
|
|
127
131
|
private _initTextures (userUniforms, userTextures, sourceTextureOptions) {
|
|
128
132
|
const gl = this._gl
|
|
129
133
|
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)
|
|
130
|
-
if (userTextures.length > maxTextures)
|
|
134
|
+
if (userTextures.length > maxTextures) {
|
|
131
135
|
console.warn('Too many textures!')
|
|
136
|
+
}
|
|
132
137
|
|
|
133
138
|
this._userTextures = {}
|
|
134
139
|
for (const name in userTextures) {
|
|
@@ -143,8 +148,9 @@ export class Shader extends Visual {
|
|
|
143
148
|
* textures, without having to define multiple properties in the effect
|
|
144
149
|
* object.
|
|
145
150
|
*/
|
|
146
|
-
if (userUniforms[name])
|
|
151
|
+
if (userUniforms[name]) {
|
|
147
152
|
throw new Error(`Texture - uniform naming conflict: ${name}!`)
|
|
153
|
+
}
|
|
148
154
|
|
|
149
155
|
// Add this as a "user uniform".
|
|
150
156
|
userUniforms[name] = '1i' // texture pointer
|
|
@@ -183,19 +189,6 @@ export class Shader extends Visual {
|
|
|
183
189
|
}
|
|
184
190
|
}
|
|
185
191
|
|
|
186
|
-
// Not needed, right?
|
|
187
|
-
/* watchWebGLOptions() {
|
|
188
|
-
const pubChange = () => {
|
|
189
|
-
this.publish("change", {});
|
|
190
|
-
};
|
|
191
|
-
for (let name in this._userTextures) {
|
|
192
|
-
watch(this, name, pubChange);
|
|
193
|
-
}
|
|
194
|
-
for (let name in this._userUniforms) {
|
|
195
|
-
watch(this, name, pubChange);
|
|
196
|
-
}
|
|
197
|
-
} */
|
|
198
|
-
|
|
199
192
|
apply (target: Movie | VisualLayer, reltime: number): void {
|
|
200
193
|
this._checkDimensions(target)
|
|
201
194
|
this._refreshGl()
|
|
@@ -303,18 +296,24 @@ export class Shader extends Visual {
|
|
|
303
296
|
}
|
|
304
297
|
}
|
|
305
298
|
|
|
299
|
+
/**
|
|
300
|
+
* Set the shader's uniforms.
|
|
301
|
+
* @param target The movie or layer to apply the shader to.
|
|
302
|
+
* @param reltime The relative time of the movie or layer.
|
|
303
|
+
*/
|
|
306
304
|
private _prepareUniforms (target, reltime) {
|
|
307
305
|
const gl = this._gl
|
|
308
|
-
// Set the shader uniforms.
|
|
309
306
|
|
|
310
307
|
// Tell the shader we bound the texture to texture unit 0.
|
|
311
308
|
// All base (Shader class) uniforms are optional.
|
|
312
|
-
if (this._uniformLocations.source)
|
|
309
|
+
if (this._uniformLocations.source) {
|
|
313
310
|
gl.uniform1i(this._uniformLocations.source, 0)
|
|
311
|
+
}
|
|
314
312
|
|
|
315
313
|
// All base (Shader class) uniforms are optional.
|
|
316
|
-
if (this._uniformLocations.size)
|
|
314
|
+
if (this._uniformLocations.size) {
|
|
317
315
|
gl.uniform2iv(this._uniformLocations.size, [target.canvas.width, target.canvas.height])
|
|
316
|
+
}
|
|
318
317
|
|
|
319
318
|
for (const unprefixed in this._userUniforms) {
|
|
320
319
|
const options = this._userUniforms[unprefixed] as UniformOptions
|
|
@@ -343,11 +342,13 @@ export class Shader extends Visual {
|
|
|
343
342
|
/**
|
|
344
343
|
* Converts a value of a standard type for javascript to a standard type for
|
|
345
344
|
* GLSL
|
|
345
|
+
*
|
|
346
346
|
* @param value - the raw value to prepare
|
|
347
347
|
* @param outputType - the WebGL type of |value|; example:
|
|
348
348
|
* <code>1f</code> for a float
|
|
349
349
|
* @param reltime - current time, relative to the target
|
|
350
|
-
* @param [options]
|
|
350
|
+
* @param [options]
|
|
351
|
+
* @returns the prepared value
|
|
351
352
|
*/
|
|
352
353
|
private _prepareValue (value, outputType, reltime, options: UniformOptions = {}) {
|
|
353
354
|
const def = options.defaultFloatComponent || 0
|
|
@@ -369,8 +370,9 @@ export class Shader extends Visual {
|
|
|
369
370
|
let i = 0
|
|
370
371
|
for (const name in this._userTextures) {
|
|
371
372
|
const testValue = val(this, name, reltime)
|
|
372
|
-
if (value === testValue)
|
|
373
|
+
if (value === testValue) {
|
|
373
374
|
value = Shader.INTERNAL_TEXTURE_UNITS + i // after the internal texture units
|
|
375
|
+
}
|
|
374
376
|
|
|
375
377
|
i++
|
|
376
378
|
}
|
|
@@ -378,32 +380,36 @@ export class Shader extends Visual {
|
|
|
378
380
|
|
|
379
381
|
if (outputType === '3fv') {
|
|
380
382
|
// allow 4-component vectors; TODO: why?
|
|
381
|
-
if (Array.isArray(value) && (value.length === 3 || value.length === 4))
|
|
383
|
+
if (Array.isArray(value) && (value.length === 3 || value.length === 4)) {
|
|
382
384
|
return value
|
|
385
|
+
}
|
|
383
386
|
|
|
384
387
|
// kind of loose so this can be changed if needed
|
|
385
|
-
if (typeof value === 'object')
|
|
388
|
+
if (typeof value === 'object') {
|
|
386
389
|
return [
|
|
387
390
|
value.r !== undefined ? value.r : def,
|
|
388
391
|
value.g !== undefined ? value.g : def,
|
|
389
392
|
value.b !== undefined ? value.b : def
|
|
390
393
|
]
|
|
394
|
+
}
|
|
391
395
|
|
|
392
396
|
throw new Error(`Invalid type: ${outputType} or value: ${value}`)
|
|
393
397
|
}
|
|
394
398
|
|
|
395
399
|
if (outputType === '4fv') {
|
|
396
|
-
if (Array.isArray(value) && value.length === 4)
|
|
400
|
+
if (Array.isArray(value) && value.length === 4) {
|
|
397
401
|
return value
|
|
402
|
+
}
|
|
398
403
|
|
|
399
404
|
// kind of loose so this can be changed if needed
|
|
400
|
-
if (typeof value === 'object')
|
|
405
|
+
if (typeof value === 'object') {
|
|
401
406
|
return [
|
|
402
407
|
value.r !== undefined ? value.r : def,
|
|
403
408
|
value.g !== undefined ? value.g : def,
|
|
404
409
|
value.b !== undefined ? value.b : def,
|
|
405
410
|
value.a !== undefined ? value.a : def
|
|
406
411
|
]
|
|
412
|
+
}
|
|
407
413
|
|
|
408
414
|
throw new Error(`Invalid type: ${outputType} or value: ${value}`)
|
|
409
415
|
}
|
|
@@ -509,8 +515,9 @@ export class Shader extends Visual {
|
|
|
509
515
|
} else {
|
|
510
516
|
// No, it's not a power of 2. Turn off mips and set
|
|
511
517
|
// wrapping to clamp to edge
|
|
512
|
-
if (wrapS !== gl.CLAMP_TO_EDGE || wrapT !== gl.CLAMP_TO_EDGE)
|
|
518
|
+
if (wrapS !== gl.CLAMP_TO_EDGE || wrapT !== gl.CLAMP_TO_EDGE) {
|
|
513
519
|
console.warn('Wrap mode is not CLAMP_TO_EDGE for a non-power-of-two texture. Defaulting to CLAMP_TO_EDGE')
|
|
520
|
+
}
|
|
514
521
|
|
|
515
522
|
gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
|
|
516
523
|
gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
|
|
@@ -552,6 +559,5 @@ export class Shader extends Visual {
|
|
|
552
559
|
return shader
|
|
553
560
|
}
|
|
554
561
|
}
|
|
555
|
-
// Shader.prototype.getpublicExcludes = () =>
|
|
556
562
|
|
|
557
563
|
const isPowerOf2 = value => (value && (value - 1)) === 0
|
package/src/effect/stack.ts
CHANGED
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
import { Movie } from '../movie'
|
|
2
2
|
import { Visual } from './visual'
|
|
3
3
|
import { Visual as VisualLayer } from '../layer'
|
|
4
|
+
import { CustomArray, CustomArrayListener } from '../custom-array'
|
|
5
|
+
|
|
6
|
+
class StackEffectsListener extends CustomArrayListener<Visual> {
|
|
7
|
+
// eslint-disable-next-line no-use-before-define
|
|
8
|
+
private _stack: Stack
|
|
9
|
+
|
|
10
|
+
constructor (stack: Stack) {
|
|
11
|
+
super()
|
|
12
|
+
this._stack = stack
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
onAdd (effect: Visual) {
|
|
16
|
+
if (!this._stack.parent) {
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
effect.tryAttach(this._stack.parent)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
onRemove (effect: Visual) {
|
|
24
|
+
if (!this._stack.parent) {
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
effect.tryDetach()
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class StackEffects extends CustomArray<Visual> {
|
|
33
|
+
// eslint-disable-next-line no-use-before-define
|
|
34
|
+
constructor (target: Visual[], stack: Stack) {
|
|
35
|
+
super(target, new StackEffectsListener(stack))
|
|
36
|
+
}
|
|
37
|
+
}
|
|
4
38
|
|
|
5
39
|
export interface StackOptions {
|
|
6
40
|
effects: Visual[]
|
|
@@ -11,58 +45,37 @@ export interface StackOptions {
|
|
|
11
45
|
* for defining reused effect sequences as one effect.
|
|
12
46
|
*/
|
|
13
47
|
export class Stack extends Visual {
|
|
14
|
-
readonly effects:
|
|
15
|
-
|
|
16
|
-
private _effectsBack: Visual[]
|
|
48
|
+
readonly effects: StackEffects
|
|
17
49
|
|
|
18
50
|
constructor (options: StackOptions) {
|
|
19
51
|
super()
|
|
20
52
|
|
|
21
|
-
this.
|
|
22
|
-
// TODO: Throw 'change' events in handlers
|
|
23
|
-
this.effects = new Proxy(this._effectsBack, {
|
|
24
|
-
deleteProperty: function (target: Visual[], property: string | symbol): boolean {
|
|
25
|
-
const value = target[property]
|
|
26
|
-
value.detach() // Detach effect from movie
|
|
27
|
-
delete target[property]
|
|
28
|
-
return true
|
|
29
|
-
},
|
|
30
|
-
set: function (target: Visual[], property: string | symbol, value: Visual): boolean {
|
|
31
|
-
// TODO: make sure type check works
|
|
32
|
-
if (!isNaN(Number(property))) { // if property is a number (index)
|
|
33
|
-
if (target[property])
|
|
34
|
-
target[property].detach() // Detach old effect from movie
|
|
35
|
-
|
|
36
|
-
value.attach(this._target) // Attach effect to movie
|
|
37
|
-
}
|
|
38
|
-
target[property] = value
|
|
39
|
-
return true
|
|
40
|
-
}
|
|
41
|
-
})
|
|
53
|
+
this.effects = new StackEffects(options.effects, this)
|
|
42
54
|
options.effects.forEach(effect => this.effects.push(effect))
|
|
43
|
-
|
|
44
|
-
// TODO: Propogate 'change' events from children up
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
attach (movie: Movie): void {
|
|
48
58
|
super.attach(movie)
|
|
59
|
+
|
|
49
60
|
this.effects.filter(effect => !!effect).forEach(effect => {
|
|
50
|
-
effect.
|
|
51
|
-
effect.attach(movie)
|
|
61
|
+
effect.tryAttach(movie)
|
|
52
62
|
})
|
|
53
63
|
}
|
|
54
64
|
|
|
55
65
|
detach (): void {
|
|
56
66
|
super.detach()
|
|
67
|
+
|
|
57
68
|
this.effects.filter(effect => !!effect).forEach(effect => {
|
|
58
|
-
effect.
|
|
69
|
+
effect.tryDetach()
|
|
59
70
|
})
|
|
60
71
|
}
|
|
61
72
|
|
|
62
73
|
apply (target: Movie | VisualLayer, reltime: number): void {
|
|
63
74
|
for (let i = 0; i < this.effects.length; i++) {
|
|
64
75
|
const effect = this.effects[i]
|
|
65
|
-
if (!effect)
|
|
76
|
+
if (!effect) {
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
66
79
|
effect.apply(target, reltime)
|
|
67
80
|
}
|
|
68
81
|
}
|
package/src/effect/transform.ts
CHANGED
|
@@ -36,11 +36,13 @@ class Transform extends Visual {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
apply (target: Movie | VisualLayer, reltime: number): void {
|
|
39
|
-
if (target.canvas.width !== this._tmpCanvas.width)
|
|
39
|
+
if (target.canvas.width !== this._tmpCanvas.width) {
|
|
40
40
|
this._tmpCanvas.width = target.canvas.width
|
|
41
|
+
}
|
|
41
42
|
|
|
42
|
-
if (target.canvas.height !== this._tmpCanvas.height)
|
|
43
|
+
if (target.canvas.height !== this._tmpCanvas.height) {
|
|
43
44
|
this._tmpCanvas.height = target.canvas.height
|
|
45
|
+
}
|
|
44
46
|
|
|
45
47
|
// Use data, since that's the underlying storage
|
|
46
48
|
this._tmpMatrix.data = val(this, 'matrix.data', reltime)
|
|
@@ -81,8 +83,9 @@ namespace Transform { // eslint-disable-line @typescript-eslint/no-namespace
|
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
identity (): Matrix {
|
|
84
|
-
for (let i = 0; i < this.data.length; i++)
|
|
86
|
+
for (let i = 0; i < this.data.length; i++) {
|
|
85
87
|
this.data[i] = Matrix.IDENTITY.data[i]
|
|
88
|
+
}
|
|
86
89
|
|
|
87
90
|
return this
|
|
88
91
|
}
|
|
@@ -93,8 +96,9 @@ namespace Transform { // eslint-disable-line @typescript-eslint/no-namespace
|
|
|
93
96
|
* @param [val]
|
|
94
97
|
*/
|
|
95
98
|
cell (x: number, y: number, val?: number): number {
|
|
96
|
-
if (val !== undefined)
|
|
99
|
+
if (val !== undefined) {
|
|
97
100
|
this.data[3 * y + x] = val
|
|
101
|
+
}
|
|
98
102
|
|
|
99
103
|
return this.data[3 * y + x]
|
|
100
104
|
}
|
|
@@ -130,18 +134,21 @@ namespace Transform { // eslint-disable-line @typescript-eslint/no-namespace
|
|
|
130
134
|
*/
|
|
131
135
|
multiply (other: Matrix): Matrix {
|
|
132
136
|
// copy to temporary matrix to avoid modifying `this` while reading from it
|
|
133
|
-
for (let x = 0; x < 3; x++)
|
|
137
|
+
for (let x = 0; x < 3; x++) {
|
|
134
138
|
for (let y = 0; y < 3; y++) {
|
|
135
139
|
let sum = 0
|
|
136
|
-
for (let i = 0; i < 3; i++)
|
|
140
|
+
for (let i = 0; i < 3; i++) {
|
|
137
141
|
sum += this.cell(x, i) * other.cell(i, y)
|
|
142
|
+
}
|
|
138
143
|
|
|
139
144
|
Matrix._TMP_MATRIX.cell(x, y, sum)
|
|
140
145
|
}
|
|
146
|
+
}
|
|
141
147
|
|
|
142
148
|
// copy data from TMP_MATRIX to this
|
|
143
|
-
for (let i = 0; i < Matrix._TMP_MATRIX.data.length; i++)
|
|
149
|
+
for (let i = 0; i < Matrix._TMP_MATRIX.data.length; i++) {
|
|
144
150
|
this.data[i] = Matrix._TMP_MATRIX.data[i]
|
|
151
|
+
}
|
|
145
152
|
|
|
146
153
|
return this
|
|
147
154
|
}
|