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.
Files changed (52) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/CONTRIBUTING.md +23 -20
  3. package/README.md +3 -2
  4. package/dist/custom-array.d.ts +10 -0
  5. package/dist/effect/base.d.ts +10 -1
  6. package/dist/effect/shader.d.ts +11 -1
  7. package/dist/effect/stack.d.ts +6 -2
  8. package/dist/etro-cjs.js +1156 -575
  9. package/dist/etro-iife.js +1156 -575
  10. package/dist/event.d.ts +10 -5
  11. package/dist/layer/audio-source.d.ts +9 -4
  12. package/dist/layer/audio.d.ts +15 -2
  13. package/dist/layer/base.d.ts +49 -3
  14. package/dist/layer/image.d.ts +15 -1
  15. package/dist/layer/text.d.ts +3 -0
  16. package/dist/layer/video.d.ts +13 -1
  17. package/dist/layer/visual.d.ts +6 -2
  18. package/dist/movie/effects.d.ts +6 -0
  19. package/dist/movie/index.d.ts +1 -0
  20. package/dist/movie/layers.d.ts +6 -0
  21. package/dist/movie/movie.d.ts +260 -0
  22. package/dist/object.d.ts +9 -6
  23. package/dist/util.d.ts +4 -10
  24. package/eslint.conf.js +2 -2
  25. package/karma.conf.js +4 -7
  26. package/package.json +8 -7
  27. package/src/custom-array.ts +43 -0
  28. package/src/effect/base.ts +23 -22
  29. package/src/effect/gaussian-blur.ts +11 -6
  30. package/src/effect/pixelate.ts +3 -3
  31. package/src/effect/shader.ts +33 -27
  32. package/src/effect/stack.ts +43 -30
  33. package/src/effect/transform.ts +14 -7
  34. package/src/event.ts +111 -21
  35. package/src/layer/audio-source.ts +60 -20
  36. package/src/layer/audio.ts +25 -3
  37. package/src/layer/base.ts +79 -26
  38. package/src/layer/image.ts +26 -2
  39. package/src/layer/text.ts +7 -0
  40. package/src/layer/video.ts +31 -4
  41. package/src/layer/visual-source.ts +43 -1
  42. package/src/layer/visual.ts +50 -28
  43. package/src/movie/effects.ts +26 -0
  44. package/src/movie/index.ts +1 -0
  45. package/src/movie/layers.ts +26 -0
  46. package/src/movie/movie.ts +855 -0
  47. package/src/object.ts +9 -6
  48. package/src/util.ts +68 -89
  49. package/dist/movie.d.ts +0 -201
  50. package/src/movie.ts +0 -744
  51. /package/scripts/{gen-effect-samples.html → effect/gen-effect-samples.html} +0 -0
  52. /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.9.1",
4
- "description": "An extendable video-editing framework for the browser and Node",
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.24.4",
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
+ }
@@ -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
- const newThis = watchPublic(this) as Base // proxy that will be returned by constructor
27
-
28
- newThis.enabled = true
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
- * Dettaches this effect from its target if the number of times `tryDetach`
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 doens't require a
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
  }
@@ -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
  }
@@ -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] - Optional config
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
@@ -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: Visual[]
15
-
16
- private _effectsBack: Visual[]
48
+ readonly effects: StackEffects
17
49
 
18
50
  constructor (options: StackOptions) {
19
51
  super()
20
52
 
21
- this._effectsBack = []
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.detach()
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.detach()
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) continue
76
+ if (!effect) {
77
+ continue
78
+ }
66
79
  effect.apply(target, reltime)
67
80
  }
68
81
  }
@@ -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
  }