@swf2es/player 0.1.0 → 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 (277) hide show
  1. package/package.json +7 -7
  2. package/dist/audio.d.ts +0 -22
  3. package/dist/audio.d.ts.map +0 -1
  4. package/dist/audio.js +0 -106
  5. package/dist/audio.js.map +0 -1
  6. package/dist/bitmap-filters.d.ts +0 -29
  7. package/dist/bitmap-filters.d.ts.map +0 -1
  8. package/dist/bitmap-filters.js +0 -906
  9. package/dist/bitmap-filters.js.map +0 -1
  10. package/dist/bitmap-ops.d.ts +0 -113
  11. package/dist/bitmap-ops.d.ts.map +0 -1
  12. package/dist/bitmap-ops.js +0 -725
  13. package/dist/bitmap-ops.js.map +0 -1
  14. package/dist/bitmap.d.ts +0 -98
  15. package/dist/bitmap.d.ts.map +0 -1
  16. package/dist/bitmap.js +0 -314
  17. package/dist/bitmap.js.map +0 -1
  18. package/dist/bounds.d.ts +0 -26
  19. package/dist/bounds.d.ts.map +0 -1
  20. package/dist/bounds.js +0 -300
  21. package/dist/bounds.js.map +0 -1
  22. package/dist/color.d.ts +0 -7
  23. package/dist/color.d.ts.map +0 -1
  24. package/dist/color.js +0 -42
  25. package/dist/color.js.map +0 -1
  26. package/dist/display.d.ts +0 -425
  27. package/dist/display.d.ts.map +0 -1
  28. package/dist/display.js +0 -1274
  29. package/dist/display.js.map +0 -1
  30. package/dist/drawing.d.ts +0 -52
  31. package/dist/drawing.d.ts.map +0 -1
  32. package/dist/drawing.js +0 -204
  33. package/dist/drawing.js.map +0 -1
  34. package/dist/filters.d.ts +0 -54
  35. package/dist/filters.d.ts.map +0 -1
  36. package/dist/filters.js +0 -155
  37. package/dist/filters.js.map +0 -1
  38. package/dist/fonts.d.ts +0 -23
  39. package/dist/fonts.d.ts.map +0 -1
  40. package/dist/fonts.js +0 -91
  41. package/dist/fonts.js.map +0 -1
  42. package/dist/geometry.d.ts +0 -21
  43. package/dist/geometry.d.ts.map +0 -1
  44. package/dist/geometry.js +0 -76
  45. package/dist/geometry.js.map +0 -1
  46. package/dist/gradients.d.ts +0 -29
  47. package/dist/gradients.d.ts.map +0 -1
  48. package/dist/gradients.js +0 -116
  49. package/dist/gradients.js.map +0 -1
  50. package/dist/images.d.ts +0 -22
  51. package/dist/images.d.ts.map +0 -1
  52. package/dist/images.js +0 -147
  53. package/dist/images.js.map +0 -1
  54. package/dist/input.d.ts +0 -79
  55. package/dist/input.d.ts.map +0 -1
  56. package/dist/input.js +0 -366
  57. package/dist/input.js.map +0 -1
  58. package/dist/keyboard.d.ts +0 -97
  59. package/dist/keyboard.d.ts.map +0 -1
  60. package/dist/keyboard.js +0 -583
  61. package/dist/keyboard.js.map +0 -1
  62. package/dist/morph.d.ts +0 -17
  63. package/dist/morph.d.ts.map +0 -1
  64. package/dist/morph.js +0 -218
  65. package/dist/morph.js.map +0 -1
  66. package/dist/pixi-blend.d.ts +0 -7
  67. package/dist/pixi-blend.d.ts.map +0 -1
  68. package/dist/pixi-blend.js +0 -253
  69. package/dist/pixi-blend.js.map +0 -1
  70. package/dist/pixi-color.d.ts +0 -54
  71. package/dist/pixi-color.d.ts.map +0 -1
  72. package/dist/pixi-color.js +0 -336
  73. package/dist/pixi-color.js.map +0 -1
  74. package/dist/pixi-filters.d.ts +0 -40
  75. package/dist/pixi-filters.d.ts.map +0 -1
  76. package/dist/pixi-filters.js +0 -953
  77. package/dist/pixi-filters.js.map +0 -1
  78. package/dist/pixi-resolve.d.ts +0 -4
  79. package/dist/pixi-resolve.d.ts.map +0 -1
  80. package/dist/pixi-resolve.js +0 -76
  81. package/dist/pixi-resolve.js.map +0 -1
  82. package/dist/pixi.d.ts +0 -219
  83. package/dist/pixi.d.ts.map +0 -1
  84. package/dist/pixi.js +0 -2169
  85. package/dist/pixi.js.map +0 -1
  86. package/dist/playerglobal/flash/filters/filters.d.ts +0 -26
  87. package/dist/playerglobal/flash/filters/filters.d.ts.map +0 -1
  88. package/dist/playerglobal/flash/filters/filters.js +0 -287
  89. package/dist/playerglobal/flash/filters/filters.js.map +0 -1
  90. package/dist/playerglobal/flash/system/FSCommand.d.ts +0 -4
  91. package/dist/playerglobal/flash/system/FSCommand.d.ts.map +0 -1
  92. package/dist/playerglobal/flash/system/FSCommand.js +0 -18
  93. package/dist/playerglobal/flash/system/FSCommand.js.map +0 -1
  94. package/dist/playerglobal/natives.d.ts +0 -10
  95. package/dist/playerglobal/natives.d.ts.map +0 -1
  96. package/dist/playerglobal/natives.js +0 -53
  97. package/dist/playerglobal/natives.js.map +0 -1
  98. package/dist/png.d.ts +0 -4
  99. package/dist/png.d.ts.map +0 -1
  100. package/dist/png.js +0 -140
  101. package/dist/png.js.map +0 -1
  102. package/dist/render/art.d.ts +0 -148
  103. package/dist/render/art.d.ts.map +0 -1
  104. package/dist/render/art.js +0 -575
  105. package/dist/render/art.js.map +0 -1
  106. package/dist/render/batchers.d.ts +0 -23
  107. package/dist/render/batchers.d.ts.map +0 -1
  108. package/dist/render/batchers.js +0 -52
  109. package/dist/render/batchers.js.map +0 -1
  110. package/dist/render/bitmaps.d.ts +0 -117
  111. package/dist/render/bitmaps.d.ts.map +0 -1
  112. package/dist/render/bitmaps.js +0 -422
  113. package/dist/render/bitmaps.js.map +0 -1
  114. package/dist/render/blend.d.ts +0 -37
  115. package/dist/render/blend.d.ts.map +0 -1
  116. package/dist/render/blend.js +0 -195
  117. package/dist/render/blend.js.map +0 -1
  118. package/dist/render/color.d.ts +0 -111
  119. package/dist/render/color.d.ts.map +0 -1
  120. package/dist/render/color.js +0 -533
  121. package/dist/render/color.js.map +0 -1
  122. package/dist/render/compositing.d.ts +0 -77
  123. package/dist/render/compositing.d.ts.map +0 -1
  124. package/dist/render/compositing.js +0 -188
  125. package/dist/render/compositing.js.map +0 -1
  126. package/dist/render/filters.d.ts +0 -98
  127. package/dist/render/filters.d.ts.map +0 -1
  128. package/dist/render/filters.js +0 -1326
  129. package/dist/render/filters.js.map +0 -1
  130. package/dist/render/hooks.d.ts +0 -4
  131. package/dist/render/hooks.d.ts.map +0 -1
  132. package/dist/render/hooks.js +0 -48
  133. package/dist/render/hooks.js.map +0 -1
  134. package/dist/render/node.d.ts +0 -139
  135. package/dist/render/node.d.ts.map +0 -1
  136. package/dist/render/node.js +0 -59
  137. package/dist/render/node.js.map +0 -1
  138. package/dist/render/patches.d.ts +0 -54
  139. package/dist/render/patches.d.ts.map +0 -1
  140. package/dist/render/patches.js +0 -154
  141. package/dist/render/patches.js.map +0 -1
  142. package/dist/render/planes.d.ts +0 -74
  143. package/dist/render/planes.d.ts.map +0 -1
  144. package/dist/render/planes.js +0 -267
  145. package/dist/render/planes.js.map +0 -1
  146. package/dist/render/pointer.d.ts +0 -5
  147. package/dist/render/pointer.d.ts.map +0 -1
  148. package/dist/render/pointer.js +0 -279
  149. package/dist/render/pointer.js.map +0 -1
  150. package/dist/render/pools.d.ts +0 -122
  151. package/dist/render/pools.d.ts.map +0 -1
  152. package/dist/render/pools.js +0 -238
  153. package/dist/render/pools.js.map +0 -1
  154. package/dist/render/resolve.d.ts +0 -4
  155. package/dist/render/resolve.d.ts.map +0 -1
  156. package/dist/render/resolve.js +0 -76
  157. package/dist/render/resolve.js.map +0 -1
  158. package/dist/render/saffron.d.ts +0 -135
  159. package/dist/render/saffron.d.ts.map +0 -1
  160. package/dist/render/saffron.js +0 -725
  161. package/dist/render/saffron.js.map +0 -1
  162. package/dist/render/settle.d.ts +0 -60
  163. package/dist/render/settle.d.ts.map +0 -1
  164. package/dist/render/settle.js +0 -128
  165. package/dist/render/settle.js.map +0 -1
  166. package/dist/render/shared.d.ts +0 -25
  167. package/dist/render/shared.d.ts.map +0 -1
  168. package/dist/render/shared.js +0 -33
  169. package/dist/render/shared.js.map +0 -1
  170. package/dist/render/snapshot.d.ts +0 -21
  171. package/dist/render/snapshot.d.ts.map +0 -1
  172. package/dist/render/snapshot.js +0 -91
  173. package/dist/render/snapshot.js.map +0 -1
  174. package/dist/render/spares.d.ts +0 -56
  175. package/dist/render/spares.d.ts.map +0 -1
  176. package/dist/render/spares.js +0 -160
  177. package/dist/render/spares.js.map +0 -1
  178. package/dist/render/strokes.d.ts +0 -107
  179. package/dist/render/strokes.d.ts.map +0 -1
  180. package/dist/render/strokes.js +0 -448
  181. package/dist/render/strokes.js.map +0 -1
  182. package/dist/render/table.d.ts +0 -213
  183. package/dist/render/table.d.ts.map +0 -1
  184. package/dist/render/table.js +0 -795
  185. package/dist/render/table.js.map +0 -1
  186. package/dist/render/tessellate.d.ts +0 -89
  187. package/dist/render/tessellate.d.ts.map +0 -1
  188. package/dist/render/tessellate.js +0 -386
  189. package/dist/render/tessellate.js.map +0 -1
  190. package/dist/render/text.d.ts +0 -40
  191. package/dist/render/text.d.ts.map +0 -1
  192. package/dist/render/text.js +0 -309
  193. package/dist/render/text.js.map +0 -1
  194. package/dist/render/three.d.ts +0 -83
  195. package/dist/render/three.d.ts.map +0 -1
  196. package/dist/render/three.js +0 -511
  197. package/dist/render/three.js.map +0 -1
  198. package/dist/render/triangles.d.ts +0 -28
  199. package/dist/render/triangles.d.ts.map +0 -1
  200. package/dist/render/triangles.js +0 -189
  201. package/dist/render/triangles.js.map +0 -1
  202. package/dist/render/view.d.ts +0 -238
  203. package/dist/render/view.d.ts.map +0 -1
  204. package/dist/render/view.js +0 -1173
  205. package/dist/render/view.js.map +0 -1
  206. package/dist/render/worker/bitmaps.d.ts +0 -43
  207. package/dist/render/worker/bitmaps.d.ts.map +0 -1
  208. package/dist/render/worker/bitmaps.js +0 -151
  209. package/dist/render/worker/bitmaps.js.map +0 -1
  210. package/dist/render/worker/drawing.d.ts +0 -21
  211. package/dist/render/worker/drawing.d.ts.map +0 -1
  212. package/dist/render/worker/drawing.js +0 -356
  213. package/dist/render/worker/drawing.js.map +0 -1
  214. package/dist/render/worker/filters.d.ts +0 -7
  215. package/dist/render/worker/filters.d.ts.map +0 -1
  216. package/dist/render/worker/filters.js +0 -125
  217. package/dist/render/worker/filters.js.map +0 -1
  218. package/dist/render/worker/frames.d.ts +0 -32
  219. package/dist/render/worker/frames.d.ts.map +0 -1
  220. package/dist/render/worker/frames.js +0 -124
  221. package/dist/render/worker/frames.js.map +0 -1
  222. package/dist/render/worker/host.d.ts +0 -60
  223. package/dist/render/worker/host.d.ts.map +0 -1
  224. package/dist/render/worker/host.js +0 -297
  225. package/dist/render/worker/host.js.map +0 -1
  226. package/dist/render/worker/libraries.d.ts +0 -36
  227. package/dist/render/worker/libraries.d.ts.map +0 -1
  228. package/dist/render/worker/libraries.js +0 -153
  229. package/dist/render/worker/libraries.js.map +0 -1
  230. package/dist/render/worker/presentation.d.ts +0 -18
  231. package/dist/render/worker/presentation.d.ts.map +0 -1
  232. package/dist/render/worker/presentation.js +0 -47
  233. package/dist/render/worker/presentation.js.map +0 -1
  234. package/dist/render/worker/renderer.d.ts +0 -4
  235. package/dist/render/worker/renderer.d.ts.map +0 -1
  236. package/dist/render/worker/renderer.js +0 -21
  237. package/dist/render/worker/renderer.js.map +0 -1
  238. package/dist/render/worker/scene.d.ts +0 -32
  239. package/dist/render/worker/scene.d.ts.map +0 -1
  240. package/dist/render/worker/scene.js +0 -611
  241. package/dist/render/worker/scene.js.map +0 -1
  242. package/dist/render/worker/text.d.ts +0 -47
  243. package/dist/render/worker/text.d.ts.map +0 -1
  244. package/dist/render/worker/text.js +0 -261
  245. package/dist/render/worker/text.js.map +0 -1
  246. package/dist/render/worker/thread.d.ts +0 -23
  247. package/dist/render/worker/thread.d.ts.map +0 -1
  248. package/dist/render/worker/thread.js +0 -107
  249. package/dist/render/worker/thread.js.map +0 -1
  250. package/dist/render/worker/values.d.ts +0 -18
  251. package/dist/render/worker/values.d.ts.map +0 -1
  252. package/dist/render/worker/values.js +0 -64
  253. package/dist/render/worker/values.js.map +0 -1
  254. package/dist/sha256.d.ts +0 -5
  255. package/dist/sha256.d.ts.map +0 -1
  256. package/dist/sha256.js +0 -96
  257. package/dist/sha256.js.map +0 -1
  258. package/dist/shapes.d.ts +0 -71
  259. package/dist/shapes.d.ts.map +0 -1
  260. package/dist/shapes.js +0 -340
  261. package/dist/shapes.js.map +0 -1
  262. package/dist/static-text.d.ts +0 -33
  263. package/dist/static-text.d.ts.map +0 -1
  264. package/dist/static-text.js +0 -105
  265. package/dist/static-text.js.map +0 -1
  266. package/dist/text-layout.d.ts +0 -66
  267. package/dist/text-layout.d.ts.map +0 -1
  268. package/dist/text-layout.js +0 -315
  269. package/dist/text-layout.js.map +0 -1
  270. package/dist/text.d.ts +0 -67
  271. package/dist/text.d.ts.map +0 -1
  272. package/dist/text.js +0 -383
  273. package/dist/text.js.map +0 -1
  274. package/dist/timeline.d.ts +0 -150
  275. package/dist/timeline.d.ts.map +0 -1
  276. package/dist/timeline.js +0 -266
  277. package/dist/timeline.js.map +0 -1
package/dist/display.js DELETED
@@ -1,1274 +0,0 @@
1
- // The display list the player owns: display objects, containers and
2
- // movie clips, as Flash has them. A renderer mirrors it (see pixi.ts) and
3
- // never decides its order. A container keeps its children in render order,
4
- // which is their index in AS3, and apart from it the children the timeline
5
- // placed, by depth, which is how SWF tags address them; a child the
6
- // timeline places goes before the first child of a greater depth.
7
- import { BUTTON_DOWN, BUTTON_HIT_TEST, BUTTON_OVER, BUTTON_UP, IDENTITY, readFilters, } from "@swf2es/format";
8
- import { BitmapStore } from "./bitmap.js";
9
- import { filterOfSwf } from "./filters.js";
10
- import { shifted } from "./geometry.js";
11
- import { morphAt } from "./morph.js";
12
- import { placeGlyphs } from "./static-text.js";
13
- import { TextModel } from "./text.js";
14
- import { GUTTER, layoutText } from "./text-layout.js";
15
- import { INVALID_PIXELS, } from "./timeline.js";
16
- /** Nothing changed since the renderer last looked, or what did. */
17
- export const CLEAN = 0;
18
- export const TRANSFORM = 1;
19
- export const CHILDREN = 2;
20
- export const CONTENT = 4;
21
- /** A Bitmap's pixels changed in place: the texture uploads again, nothing is rebuilt. */
22
- export const PIXELS = 8;
23
- const NO_FILTERS = Object.freeze([]);
24
- const NO_FILTER_BYTES = new Uint8Array([0]);
25
- /** The blend modes by the number PlaceObject3 gives them: 0 and 1 are normal. */
26
- export const BLEND_MODES = [
27
- "normal",
28
- "normal",
29
- "layer",
30
- "multiply",
31
- "screen",
32
- "lighten",
33
- "darken",
34
- "difference",
35
- "add",
36
- "subtract",
37
- "invert",
38
- "alpha",
39
- "erase",
40
- "overlay",
41
- "hardlight",
42
- ];
43
- /** Display objects are numbered as they are made: Flash runs orphans' scripts newest first. */
44
- let made = 0;
45
- const DEGREES = 180 / Math.PI;
46
- /** A matrix column of scale `from` stretched to scale `to`; zeroed for a NaN, and null where there is no proportion to keep. */
47
- function scaled(to, from, x, y) {
48
- if (Number.isNaN(to)) {
49
- return [0, 0];
50
- }
51
- const f = to / from;
52
- return Number.isFinite(f) ? [x * f, y * f] : null;
53
- }
54
- /** The sine and cosine of `degrees`, exact at the quarter turns, where Flash's matrix has 0s and 1s and not the doubles' rounding (`replaces`). */
55
- function sinCos(degrees) {
56
- const quarters = degrees / 90;
57
- if (Number.isInteger(quarters)) {
58
- const k = ((quarters % 4) + 4) % 4;
59
- return [[0, 1, 0, -1][k], [1, 0, -1, 0][k]];
60
- }
61
- const r = degrees / DEGREES;
62
- return [Math.sin(r), Math.cos(r)];
63
- }
64
- /** Degrees into Flash's range, -180 to 180 with both ends kept as given; a NaN stays NaN. */
65
- export function normalizeDegrees(degrees) {
66
- let d = degrees % 360;
67
- if (d > 180) {
68
- d -= 360;
69
- }
70
- else if (d < -180) {
71
- d += 360;
72
- }
73
- return d;
74
- }
75
- export class DisplayObject {
76
- serial = made++;
77
- parent = null;
78
- /** The timeline depth it was placed at, or null for one a script added. */
79
- depth = null;
80
- /** The frame of its parent's timeline that placed it, 1 the first; 0 for one a script added. */
81
- placeFrame = 0;
82
- /**
83
- * The ratio its placements gave, 0 for none: a rewind keeps the child
84
- * only where the frames replayed give it the same (gotoFrame).
85
- */
86
- ratio = 0;
87
- name = "";
88
- /** Whether its name is one the timeline gave it, which its parent has a property of; not a default instanceN. */
89
- timelineNamed = false;
90
- /**
91
- * Its transform in its parent, translation in pixels: made from the
92
- * scales, rotation and skew below, which are the object's own, as Flash
93
- * keeps them apart from the matrix; set whole, it is taken apart into them.
94
- */
95
- matrix = { ...IDENTITY };
96
- scaleX = 1;
97
- scaleY = 1;
98
- /** In degrees, -180 to 180, as Flash reports it. */
99
- rotation = 0;
100
- /** The second column's turn beyond the first's, in degrees: 0 but for a matrix that skews. */
101
- skew = 0;
102
- colorTransform = null;
103
- visible = true;
104
- /**
105
- * Where it has the keyboard's focus, how to drop it: Flash takes focus
106
- * from an object taken off its parent or hidden (keyboard.ts).
107
- */
108
- focusDrop = null;
109
- /** The character it was made from, or null. */
110
- character = null;
111
- /** Whether a script set a property of it; from then on the timeline swaps no shape under it, as Flash's does not. */
112
- scripted = false;
113
- /**
114
- * Whether a script set its transform or another property a place gives:
115
- * from then on the timeline's places, moves and a rewind's alike, leave
116
- * its matrix, colour, ratio, visibility, blend mode and filters as they
117
- * are, all of them whichever was set, as Flash's do (`scripted-moves`).
118
- * Setting visible, mask or cacheAsBitmap is no such touch.
119
- */
120
- transformed = false;
121
- /** Its other face, the AS3 object a script sees; null in an AVM1 movie. */
122
- object = null;
123
- /** The root of an AVM1 movie an AS3 Loader loaded: its other face, an AVM1Movie, is no InteractiveObject. */
124
- avm1Root = false;
125
- /** The LoaderInfo of the SWF this is the root of: set on the main root and on each loaded SWF's; null below. */
126
- loaderInfo = null;
127
- /** What its Graphics drew, for a Shape or Sprite a script draws in; null until one does. */
128
- drawing = null;
129
- /** What changed since the renderer last synced it: TRANSFORM, CHILDREN, CONTENT. */
130
- dirty = TRANSFORM | CONTENT;
131
- /** This object, weakly, as a store it shows or fills with holds it. */
132
- ref = new WeakRef(this);
133
- /**
134
- * The last depth this clips, as a timeline's mask, or 0: a mask is not
135
- * drawn, and clips the children after it until one placed deeper.
136
- */
137
- clipDepth = 0;
138
- /** The object `mask` set clips this one to; and the one it clips, for such a mask. */
139
- mask = null;
140
- maskOf = null;
141
- /**
142
- * scrollRect as set, its edges whole pixels, and as the last render took
143
- * it, which the drawing, bounds and points go by: Flash's takes effect
144
- * when it next draws.
145
- */
146
- scrollRect = null;
147
- scroll = null;
148
- /** Its blend mode, as BlendMode names it. */
149
- blendMode = "normal";
150
- /** Its filters' values, which its `filters` reads copies of. */
151
- filters = NO_FILTERS;
152
- /** A store it shows or fills with changed its pixels, or was disposed. */
153
- pixelsChanged(disposed) {
154
- this.invalidate(disposed ? CONTENT : PIXELS);
155
- }
156
- /** Mark a change, and that its ancestors have a changed descendant. */
157
- invalidate(what) {
158
- this.dirty |= what;
159
- for (let p = this.parent; p && !p.descendantsDirty; p = p.parent) {
160
- p.descendantsDirty = true;
161
- }
162
- }
163
- /** Its matrix after its scroll's shift: from what it draws to its parent's space. */
164
- get placed() {
165
- return this.scroll ? shifted(this.matrix, this.scroll) : this.matrix;
166
- }
167
- /** Whether `o` is this or under it. */
168
- encloses(o) {
169
- for (let p = o; p; p = p.parent) {
170
- if (p === this) {
171
- return true;
172
- }
173
- }
174
- return false;
175
- }
176
- /**
177
- * Clip this to `mask`'s fills, or to nothing for null. A mask clips one
178
- * object: set on another it leaves the one before, as Flash's does.
179
- */
180
- setMask(mask) {
181
- if (this.mask === mask) {
182
- return;
183
- }
184
- if (this.mask) {
185
- this.mask.maskOf = null;
186
- this.mask.invalidate(TRANSFORM);
187
- }
188
- if (mask?.maskOf) {
189
- mask.maskOf.mask = null;
190
- mask.maskOf.invalidate(TRANSFORM);
191
- }
192
- this.mask = mask;
193
- if (mask) {
194
- mask.maskOf = this;
195
- mask.invalidate(TRANSFORM);
196
- }
197
- this.invalidate(TRANSFORM);
198
- }
199
- /** A script set a property a place gives (`transformed`), which is also a touch (`scripted`). */
200
- touch() {
201
- this.scripted = true;
202
- this.transformed = true;
203
- }
204
- /**
205
- * The matrix set whole, and taken apart: the scales are its columns'
206
- * lengths, the rotation the first column's angle, the skew the second's
207
- * beyond that. A negative scale set as such is not recovered: a half
208
- * turn is what the matrix says.
209
- */
210
- setMatrix(m) {
211
- this.matrix = { ...m };
212
- this.scaleX = Math.hypot(m.a, m.b);
213
- this.scaleY = Math.hypot(m.c, m.d);
214
- this.rotation = Math.atan2(m.b, m.a) * DEGREES;
215
- this.skew = normalizeDegrees(Math.atan2(-m.c, m.d) * DEGREES - this.rotation);
216
- this.invalidate(TRANSFORM);
217
- }
218
- /**
219
- * A scale set stretches its column of the matrix in proportion, so the
220
- * other column stays exactly as it was, as Flash's does (the corpus's
221
- * `displayobject_invalid_floats`); from a scale of 0 or NaN, where there
222
- * is no proportion, the column is made anew from the angles. A NaN is
223
- * reported back as set, as Flash reports it: a NaN scale zeroes its
224
- * column, a NaN rotation leaves the matrix as it was and counts as none
225
- * from then on.
226
- */
227
- setScaleX(v) {
228
- const column = scaled(v, this.scaleX, this.matrix.a, this.matrix.b);
229
- this.scaleX = v;
230
- if (column) {
231
- this.matrix = { ...this.matrix, a: column[0], b: column[1] };
232
- this.invalidate(TRANSFORM);
233
- }
234
- else {
235
- this.remake();
236
- }
237
- }
238
- setScaleY(v) {
239
- const column = scaled(v, this.scaleY, this.matrix.c, this.matrix.d);
240
- this.scaleY = v;
241
- if (column) {
242
- this.matrix = { ...this.matrix, c: column[0], d: column[1] };
243
- this.invalidate(TRANSFORM);
244
- }
245
- else {
246
- this.remake();
247
- }
248
- }
249
- setRotation(degrees) {
250
- this.rotation = normalizeDegrees(degrees);
251
- if (!Number.isNaN(degrees)) {
252
- this.remake();
253
- }
254
- }
255
- remake() {
256
- const [sinR, cosR] = sinCos(this.rotation || 0);
257
- const [sinQ, cosQ] = sinCos((this.rotation || 0) + this.skew);
258
- this.matrix = {
259
- a: (this.scaleX || 0) * cosR,
260
- b: (this.scaleX || 0) * sinR,
261
- c: -(this.scaleY || 0) * sinQ,
262
- d: (this.scaleY || 0) * cosQ,
263
- tx: this.matrix.tx,
264
- ty: this.matrix.ty,
265
- };
266
- this.invalidate(TRANSFORM);
267
- }
268
- /** Apply a place's transform, colour, name and visibility; to one a script transformed, its name and clip depth only. */
269
- applyPlace(place) {
270
- if (place.name !== null) {
271
- this.name = place.name;
272
- this.timelineNamed = true;
273
- }
274
- if (this.transformed) {
275
- if (place.clipDepth !== null) {
276
- this.applyRare({ ...place, blendMode: null, filters: null });
277
- }
278
- return;
279
- }
280
- if (place.matrix) {
281
- const m = place.matrix;
282
- this.setMatrix({ a: m.a, b: m.b, c: m.c, d: m.d, tx: m.tx / 20, ty: m.ty / 20 });
283
- }
284
- if (place.colorTransform) {
285
- this.colorTransform = place.colorTransform;
286
- this.invalidate(TRANSFORM);
287
- }
288
- if (place.ratio !== null) {
289
- this.ratio = place.ratio;
290
- }
291
- if (place.visible !== null) {
292
- this.visible = place.visible;
293
- this.invalidate(TRANSFORM);
294
- if (!place.visible) {
295
- this.focusDrop?.(this);
296
- }
297
- }
298
- // Apart, and once tested: applyPlace runs for each move of each frame, and grown it is no longer inlined.
299
- if (place.clipDepth !== null || place.blendMode !== null || place.filters !== null) {
300
- this.applyRare(place);
301
- }
302
- }
303
- /** What a place sets that few do: a clip depth, a blend mode, filters. */
304
- applyRare(place) {
305
- if (place.filters !== null) {
306
- this.filters = readFilters(place.filters).map(filterOfSwf);
307
- this.invalidate(TRANSFORM);
308
- }
309
- if (place.clipDepth !== null && place.clipDepth !== this.clipDepth) {
310
- this.clipDepth = place.clipDepth;
311
- this.invalidate(TRANSFORM);
312
- this.parent?.invalidate(CHILDREN);
313
- }
314
- if (place.blendMode !== null) {
315
- this.setBlendMode(BLEND_MODES[place.blendMode] ?? "normal");
316
- }
317
- }
318
- /** How it is composited with what is below it (BLEND_MODES). */
319
- setBlendMode(mode) {
320
- if (mode !== this.blendMode) {
321
- this.blendMode = mode;
322
- this.invalidate(TRANSFORM);
323
- }
324
- }
325
- }
326
- export class ShapeObject extends DisplayObject {
327
- /** The shape it draws, or last drew; null for a Shape a script made, which draws nothing yet. */
328
- shape;
329
- /** A MorphShape's morph, which `shape` is a blend of. */
330
- morph = null;
331
- /**
332
- * The blend drawn() last made, and its morph and ratio: it stands while
333
- * `shape` is still it, which a swap to a shape is not.
334
- */
335
- blended = null;
336
- constructor(shape) {
337
- super();
338
- this.shape = shape;
339
- this.character = shape;
340
- }
341
- /** A MorphShape, its morph's start until it is drawn at another ratio. */
342
- static ofMorph(morph) {
343
- const shape = morphAt(morph, 0);
344
- const object = new ShapeObject(shape);
345
- object.morph = morph;
346
- object.blended = { morph, ratio: 0, shape };
347
- object.character = morph;
348
- return object;
349
- }
350
- /**
351
- * A MorphShape takes on a new ratio when it is next drawn (drawn): until
352
- * then its bounds and hit tests are the last drawn blend's, as Flash's
353
- * are (the corpus's hittest_morph).
354
- */
355
- applyPlace(place) {
356
- const ratio = this.ratio;
357
- super.applyPlace(place);
358
- if (this.morph && this.ratio !== ratio) {
359
- this.invalidate(CONTENT);
360
- }
361
- }
362
- /** The shape to draw now: a MorphShape's blend at its ratio, which it keeps from here. */
363
- drawn() {
364
- // The blend it has is kept while its morph and ratio are: asking the
365
- // morph again would turn over its few latest blends for nothing.
366
- const b = this.blended;
367
- if (this.morph &&
368
- (b?.morph !== this.morph || b.ratio !== this.ratio || b.shape !== this.shape)) {
369
- this.shape = morphAt(this.morph, this.ratio);
370
- this.blended = { morph: this.morph, ratio: this.ratio, shape: this.shape };
371
- }
372
- return this.shape;
373
- }
374
- }
375
- /** A StaticText: DefineText's glyphs, which only a timeline places; its bounds the tag's. */
376
- export class StaticTextObject extends DisplayObject {
377
- definition;
378
- characters;
379
- laidGlyphs = null;
380
- constructor(definition, characters) {
381
- super();
382
- this.definition = definition;
383
- this.characters = characters;
384
- this.character = definition;
385
- }
386
- /**
387
- * Its glyphs as placed, and its text, found the first time they are
388
- * asked for: by then its SWF's fonts are all read, those defined after it
389
- * too.
390
- */
391
- get glyphs() {
392
- this.laidGlyphs ??= placeGlyphs(this.definition, this.characters);
393
- return this.laidGlyphs;
394
- }
395
- }
396
- /**
397
- * A TextField: placed by DefineEditText, or made by a script. Its text and
398
- * formats are a TextModel; a timeline's starts with its tag's text, read
399
- * as HTML if the tag says so, in the format the tag gives.
400
- */
401
- export class TextObject extends DisplayObject {
402
- definition;
403
- model = new TextModel();
404
- /** The embedded fonts its text may be in: its SWF's. */
405
- fonts = null;
406
- laid = null;
407
- /** The field's rectangle, in its own pixels: from (left, top), width by height. */
408
- left = 0;
409
- top = 0;
410
- width = 100;
411
- height = 100;
412
- border = false;
413
- borderColor = 0;
414
- background = false;
415
- backgroundColor = 0xffffff;
416
- multiline = false;
417
- wordWrap = false;
418
- type = "dynamic";
419
- embedFonts = false;
420
- autoSize = "none";
421
- selectable = true;
422
- maxChars = 0;
423
- displayAsPassword = false;
424
- condenseWhite = false;
425
- antiAliasType = "normal";
426
- gridFitType = "pixel";
427
- sharpness = 0;
428
- thickness = 0;
429
- restrict = null;
430
- mouseWheelEnabled = true;
431
- alwaysShowSelection = false;
432
- useRichTextClipboard = false;
433
- scrollH = 0;
434
- scrollV = 1;
435
- /** The selection, from the end it was started at to the caret, as text indices; empty, a caret alone. */
436
- anchorAt = 0;
437
- caretAt = 0;
438
- /** Whether it has the keyboard's focus, an input field then showing its caret. */
439
- focused = false;
440
- constructor(definition, initialText = true) {
441
- super();
442
- this.definition = definition;
443
- this.character = definition;
444
- const edit = definition?.definition;
445
- if (!edit) {
446
- return;
447
- }
448
- this.left = edit.bounds.xMin / 20;
449
- this.top = edit.bounds.yMin / 20;
450
- this.width = (edit.bounds.xMax - edit.bounds.xMin) / 20;
451
- this.height = (edit.bounds.yMax - edit.bounds.yMin) / 20;
452
- this.multiline = edit.multiline;
453
- this.wordWrap = edit.wordWrap;
454
- this.type = edit.readOnly ? "dynamic" : "input";
455
- this.displayAsPassword = edit.password;
456
- this.autoSize = edit.autoSize ? "left" : "none";
457
- this.selectable = edit.selectable;
458
- // A border in the tag is Flash's border and white background together.
459
- this.border = edit.border;
460
- this.background = edit.border;
461
- this.maxChars = edit.maxLength ?? 0;
462
- this.embedFonts = edit.useOutlines;
463
- const font = definition?.font;
464
- const color = edit.color;
465
- this.model.defaultFormat = {
466
- ...this.model.defaultFormat,
467
- font: font?.name ?? edit.fontClass ?? this.model.defaultFormat.font,
468
- bold: font?.bold ?? false,
469
- italic: font?.italic ?? false,
470
- size: edit.fontHeight === null ? 12 : edit.fontHeight / 20,
471
- // RGBA as the tag stores it.
472
- color: color === null ? 0 : ((color & 0xff) << 16) | (color & 0xff00) | ((color >>> 16) & 0xff),
473
- align: ["left", "right", "center", "justify"][edit.align] ?? "left",
474
- leftMargin: edit.leftMargin / 20,
475
- rightMargin: edit.rightMargin / 20,
476
- indent: edit.indent / 20,
477
- leading: edit.leading / 20,
478
- };
479
- if (!initialText) {
480
- this.model.setText("");
481
- }
482
- else if (edit.html) {
483
- this.model.setHtml(edit.text, edit.multiline);
484
- }
485
- else {
486
- this.model.setText(edit.text);
487
- }
488
- }
489
- /** Its text laid out, as of now: made again only when the text, its formats or the field changed. */
490
- get layout() {
491
- const key = `${this.model.revision}|${this.width}|${this.wordWrap}|${this.embedFonts}|${this.displayAsPassword}`;
492
- if (this.laid?.key !== key) {
493
- const text = this.model.text;
494
- this.laid = {
495
- key,
496
- layout: layoutText({
497
- // A password's characters are laid out, and drawn, as asterisks; its lines stay.
498
- text: this.displayAsPassword ? text.replace(/[^\r]/g, "*") : text,
499
- formats: this.model.formats,
500
- defaultFormat: this.model.defaultFormat,
501
- width: this.width,
502
- wordWrap: this.wordWrap,
503
- embedFonts: this.embedFonts,
504
- fonts: this.fonts,
505
- authored: this.definition !== null,
506
- }),
507
- };
508
- }
509
- return this.laid.layout;
510
- }
511
- /**
512
- * Fit the field to its text, as autoSize does: its height to the text's
513
- * and the gutters, and its width too unless it wraps, keeping its left,
514
- * centre or right edge where it was.
515
- */
516
- fit() {
517
- if (this.autoSize === "none") {
518
- return;
519
- }
520
- const layout = this.layout;
521
- const height = (layout.height + 2 * GUTTER) / 20;
522
- if (!this.wordWrap) {
523
- const width = (layout.width + 2 * GUTTER) / 20;
524
- const moved = this.autoSize === "center"
525
- ? (this.width - width) / 2
526
- : this.autoSize === "right"
527
- ? this.width - width
528
- : 0;
529
- if (moved !== 0) {
530
- this.matrix = { ...this.matrix, tx: Math.round((this.matrix.tx + moved) * 20) / 20 };
531
- this.invalidate(TRANSFORM);
532
- }
533
- this.width = width;
534
- }
535
- this.height = height;
536
- this.invalidate(CONTENT);
537
- }
538
- /** The text, \r between its lines. */
539
- get text() {
540
- return this.model.text;
541
- }
542
- /** The alignment of its first paragraph. */
543
- get align() {
544
- return (this.model.formats[0] ?? this.model.defaultFormat).align;
545
- }
546
- /** The end the selection was started at, within the text as it is now, which a script may have shortened. */
547
- get anchor() {
548
- return Math.min(this.anchorAt, this.model.text.length);
549
- }
550
- /** The caret, within the text as it is now. */
551
- get caret() {
552
- return Math.min(this.caretAt, this.model.text.length);
553
- }
554
- /** The selection's first and last index, in order. */
555
- get selection() {
556
- const [anchor, caret] = [this.anchor, this.caret];
557
- return anchor <= caret ? [anchor, caret] : [caret, anchor];
558
- }
559
- /** Select from `anchor` to `caret`, each kept within the text; a focused field shows its caret there. */
560
- select(anchor, caret) {
561
- const length = this.model.text.length;
562
- const [a, c] = [Math.max(0, Math.min(length, anchor)), Math.max(0, Math.min(length, caret))];
563
- // A drag selects on every move, mostly what it had: redraw only for a change.
564
- if (a === this.anchor && c === this.caret) {
565
- return;
566
- }
567
- this.anchorAt = a;
568
- this.caretAt = c;
569
- this.invalidate(CONTENT);
570
- }
571
- }
572
- /**
573
- * Another character placed in a child's stead, with the move flag or where
574
- * a rewind keeps the child: Flash makes no new object, and only a Shape or
575
- * MorphShape no script has touched takes the new shape's or morph's
576
- * graphic; a clip, a Shape a script set a property of, and a Shape a
577
- * sprite is placed over all stay as they are (`replaces`).
578
- */
579
- function swap(existing, character) {
580
- if (!(existing instanceof ShapeObject) || existing.scripted || existing.character === character) {
581
- return;
582
- }
583
- // A morph is blended when it is next drawn, at the ratio the place gives.
584
- if (character.type === "morph") {
585
- existing.morph = character;
586
- }
587
- else if (character.type === "shape") {
588
- existing.morph = null;
589
- existing.shape = character;
590
- }
591
- else {
592
- return;
593
- }
594
- existing.character = character;
595
- existing.invalidate(CONTENT);
596
- }
597
- /** A Bitmap: a display object that shows a BitmapData's pixels, its bounds the data's size. */
598
- export class BitmapObject extends DisplayObject {
599
- shown = null;
600
- smoothing = false;
601
- pixelSnapping = "auto";
602
- /** The SWF's bitmap it shows a copy of, placed by a timeline or made by its class; null for one a script made. */
603
- character = null;
604
- /** PlaceObject3's HasImage where a timeline placed it: Flash then takes a bound class for its data's. */
605
- hasImage = false;
606
- constructor(store) {
607
- super();
608
- this.store = store;
609
- }
610
- /** The store shown; the object watches it, so a pixel set marks the object for the renderer. */
611
- get store() {
612
- return this.shown;
613
- }
614
- set store(store) {
615
- this.shown?.views.delete(this.ref);
616
- this.shown = store;
617
- store?.views.add(this.ref);
618
- this.invalidate(CONTENT);
619
- }
620
- }
621
- export class Container extends DisplayObject {
622
- /** The library whose timeline this is, for what it tells of children going; a script's container has none. */
623
- library = null;
624
- /** Its children in render order: index 0 is drawn first, below the rest. */
625
- children = [];
626
- /** The children the timeline placed, by depth. */
627
- depths = new Map();
628
- /** Whether a descendant changed since the renderer last synced. */
629
- descendantsDirty = true;
630
- /** Place `child` at timeline depth `depth`: before the first child of a greater depth. */
631
- placeAtDepth(child, depth) {
632
- this.removeAtDepth(depth);
633
- let index = this.children.length;
634
- for (let i = 0; i < this.children.length; i++) {
635
- const d = this.children[i].depth;
636
- if (d !== null && d > depth) {
637
- index = i;
638
- break;
639
- }
640
- }
641
- child.depth = depth;
642
- child.parent = this;
643
- this.children.splice(index, 0, child);
644
- this.depths.set(depth, child);
645
- this.invalidate(CHILDREN);
646
- }
647
- removeAtDepth(depth) {
648
- const child = this.depths.get(depth);
649
- if (!child) {
650
- return null;
651
- }
652
- // The timeline's removal: a script's goes through the natives, which tell of it themselves.
653
- this.library?.removing?.(child, true);
654
- this.removeChild(child);
655
- return child;
656
- }
657
- /** Take `child` out, wherever it is in the list, and off the timeline's depths. */
658
- removeChild(child) {
659
- if (child.depth !== null) {
660
- this.depths.delete(child.depth);
661
- child.depth = null;
662
- }
663
- this.children.splice(this.children.indexOf(child), 1);
664
- child.parent = null;
665
- child.focusDrop?.(child);
666
- this.invalidate(CHILDREN);
667
- }
668
- /**
669
- * Put `child` at `index` in render order, as a script's addChildAt does:
670
- * out of its parent first, and off the timeline's depths, which no
671
- * longer place it.
672
- */
673
- addChildAt(child, index) {
674
- const same = child.parent === this;
675
- child.parent?.removeChild(child);
676
- child.parent = this;
677
- this.children.splice(same ? Math.min(index, this.children.length) : index, 0, child);
678
- this.invalidate(CHILDREN);
679
- }
680
- swapChildren(a, b) {
681
- const i = this.children.indexOf(a);
682
- const j = this.children.indexOf(b);
683
- this.children[i] = b;
684
- this.children[j] = a;
685
- this.invalidate(CHILDREN);
686
- }
687
- }
688
- /**
689
- * A button (SimpleButton): four states, each a display object, of which it
690
- * shows the up, over or down one, as the pointer has it, and hit tests with
691
- * the fourth. The state it shows is its one child, which it is drawn and
692
- * bounded by; the others are its own, off the display list, but each has
693
- * its frames as Flash's do (`frameChildren`). Not a container to a script.
694
- */
695
- export class ButtonObject extends Container {
696
- upState = null;
697
- overState = null;
698
- downState = null;
699
- hitTestState = null;
700
- state = "up";
701
- /** Its states' next frame scripts run up, over, down, hit: the first, in a SWF after 9 (Scripting.construct). */
702
- firstScripts = false;
703
- enabled = true;
704
- useHandCursor = true;
705
- trackAsMenu = false;
706
- /** The display object of state `state`. */
707
- stateObject(state) {
708
- return state === "up" ? this.upState : state === "over" ? this.overState : this.downState;
709
- }
710
- /** Show state `state`. */
711
- setState(state) {
712
- this.state = state;
713
- this.show();
714
- }
715
- /**
716
- * Have the state it is in be its one child, as it is now. The states it
717
- * does not show are parented to nothing, as Flash has them once made.
718
- */
719
- show() {
720
- const current = this.stateObject(this.state);
721
- for (const child of [...this.children]) {
722
- if (child !== current) {
723
- this.removeChild(child);
724
- }
725
- }
726
- for (const state of [this.upState, this.overState, this.downState, this.hitTestState]) {
727
- if (state && state !== current && state.parent === this && !this.children.includes(state)) {
728
- state.parent = null;
729
- }
730
- }
731
- if (current && !this.children.includes(current)) {
732
- if (current.parent !== this) {
733
- current.parent?.removeChild(current);
734
- }
735
- this.children.splice(0, 0, current);
736
- current.parent = this;
737
- this.invalidate(CHILDREN);
738
- }
739
- }
740
- }
741
- /**
742
- * Make a button's states from its records, as Flash does when the button
743
- * is made, up, over, down, then hit test: each state the one character it
744
- * shows, else a container of its characters by depth, one for none too. A
745
- * character in several states is made once for each. `made` brings a
746
- * character to life once placed, and `holderMade` each container, once all
747
- * four states' characters are, as Flash names them after those; the hit
748
- * test state takes only the records' transforms.
749
- */
750
- export function buttonStates(button, character, library, made, holderMade) {
751
- const holders = [];
752
- const state = (flag) => {
753
- const parts = [];
754
- for (const record of character.records) {
755
- const c = record.states & flag ? library.characters.get(record.character) : undefined;
756
- if (c &&
757
- c.type !== "binary" &&
758
- c.type !== "font" &&
759
- c.type !== "fontCff" &&
760
- c.type !== "sound") {
761
- const display = displayFor(c, library);
762
- display.applyPlace(recordPlace(record, flag !== BUTTON_HIT_TEST));
763
- parts.push({ display, character: c, record });
764
- }
765
- }
766
- // Made as the button's, under it, though in no container's list: a
767
- // script sees the stage from them, and no parent above them.
768
- if (parts.length === 1) {
769
- parts[0].display.parent = button;
770
- made(parts[0].display, parts[0].character);
771
- return parts[0].display;
772
- }
773
- const holder = new Container();
774
- holder.parent = button;
775
- for (const part of parts) {
776
- holder.placeAtDepth(part.display, part.record.depth);
777
- made(part.display, part.character);
778
- }
779
- holders.push(holder);
780
- return holder;
781
- };
782
- button.upState = state(BUTTON_UP);
783
- button.overState = state(BUTTON_OVER);
784
- button.downState = state(BUTTON_DOWN);
785
- button.hitTestState = state(BUTTON_HIT_TEST);
786
- for (const holder of holders) {
787
- holderMade(holder);
788
- }
789
- button.show();
790
- }
791
- /** A button record as a place of its character: its transform, and, but in the hit test state, its look. */
792
- function recordPlace(record, look) {
793
- return {
794
- depth: record.depth,
795
- move: false,
796
- character: record.character,
797
- matrix: record.matrix,
798
- colorTransform: look ? record.colorTransform : null,
799
- ratio: null,
800
- name: null,
801
- clipDepth: null,
802
- className: null,
803
- hasImage: false,
804
- blendMode: look ? record.blendMode : null,
805
- cacheAsBitmap: null,
806
- visible: null,
807
- opaqueBackground: null,
808
- filters: look ? record.filters : null,
809
- clipActions: null,
810
- };
811
- }
812
- /**
813
- * The display objects whose frame scripts a display object's run: those
814
- * whose frames its frames run, but a button's first, in a SWF after 9, up,
815
- * over, down, then hit test, as Flash runs them while the button is made.
816
- */
817
- export function scriptChildren(d) {
818
- if (d instanceof ButtonObject && d.firstScripts) {
819
- d.firstScripts = false;
820
- return [d.upState, d.overState, d.downState, d.hitTestState].filter((o, i, all) => o !== null && all.indexOf(o) === i);
821
- }
822
- return frameChildren(d);
823
- }
824
- /**
825
- * The display objects whose frames a display object's frames run: a
826
- * container's children, and all a button's states, as Ruffle orders them,
827
- * the hit test state first, whichever it shows.
828
- */
829
- export function frameChildren(d) {
830
- if (d instanceof ButtonObject) {
831
- return [d.hitTestState, d.upState, d.downState, d.overState].filter((o, i, all) => o !== null && all.indexOf(o) === i);
832
- }
833
- return d instanceof Container ? d.children : [];
834
- }
835
- /**
836
- * A walk of a container's children in render order, for the timeline
837
- * masks that clip each: a mask clips the children after it until one
838
- * placed deeper than its clip depth; one a script put among them has no
839
- * depth and ends nothing. A class, not a generator: the renderer walks
840
- * every child each time a container's children change.
841
- */
842
- export class Clips {
843
- /** The masks open, outermost first. */
844
- masks = [];
845
- /** The next child: how many of `masks`, from the first, clip it; it clips those after it, if a mask. */
846
- enter(child) {
847
- const depth = child.depth;
848
- const masks = this.masks;
849
- while (depth !== null && masks.length > 0 && masks[masks.length - 1].clipDepth < depth) {
850
- masks.pop();
851
- }
852
- const n = masks.length;
853
- if (child.clipDepth > 0) {
854
- masks.push(child);
855
- }
856
- return n;
857
- }
858
- }
859
- export class MovieClip extends Container {
860
- timeline;
861
- /** The frame it shows, 1 the first; 0 before its first frame is entered. */
862
- currentFrame = 0;
863
- playing = true;
864
- /** Made by a script with `new`: Flash has such a clip sit out the next frame's advance. */
865
- fresh = false;
866
- /** The scripts addFrameScript registered, by frame, 1 the first. */
867
- frameScripts = new Map();
868
- /** The frame whose script last ran, so that entering a frame runs its script once. */
869
- scriptedFrame = 0;
870
- /** A goto a frame script asked for, taken when the script returns, as Flash defers it; null for none. */
871
- queuedGoto = null;
872
- /** Whether that goto plays or stops the clip, as it happens, not as it is asked for. */
873
- queuedPlay = false;
874
- /**
875
- * The frame count (Scripting.frames) at a script's goto in a SWF of
876
- * version 9 or earlier, a goto to the frame it is on among them: the next
877
- * frame it sits out, with all in it, as Flash has such a clip
878
- * (`goto-children`). A stamp, not a flag: one a skipped parent took along,
879
- * or a clip no frame reached, is past by the frame after and never skips.
880
- * From version 10 a goto leaves its frames as they were.
881
- */
882
- skipsAfter = -1;
883
- constructor(timeline, library) {
884
- super();
885
- this.timeline = timeline;
886
- this.library = library;
887
- }
888
- get totalFrames() {
889
- return this.timeline.frames.length;
890
- }
891
- /**
892
- * First-frame children placed but not yet made alive: placed before the
893
- * clip's class constructor runs, made by Sprite's constructChildren in its
894
- * super(), as Flash has a constructor find them (`instantiation_on_enter_frame`).
895
- */
896
- held = [];
897
- /**
898
- * Run frame `frame`'s commands as the playhead reaches it: place, move and
899
- * remove the timeline's children. A place without the move flag at a depth
900
- * already taken is let be, as Flash lets it (`same-depth`).
901
- * A new child is made alive at once, or `later`: by the frame's construct
902
- * phase, after ENTER_FRAME, for a frame played on; by constructChildren,
903
- * held, for a clip's first frame.
904
- */
905
- runFrame(frame, later = null) {
906
- for (const command of this.timeline.frames[frame - 1] ?? []) {
907
- if (command.type === "remove") {
908
- this.removeAtDepth(command.depth);
909
- continue;
910
- }
911
- const place = command.place;
912
- const existing = this.depths.get(place.depth);
913
- if (!place.move && existing) {
914
- continue;
915
- }
916
- if (place.move && existing && place.character === null) {
917
- existing.applyPlace(place);
918
- continue;
919
- }
920
- // A new character, or one that replaces what is at the depth.
921
- // Data is no display object: Flash places nothing for it.
922
- const character = place.character === null ? null : this.library.characters.get(place.character);
923
- if (!character ||
924
- character.type === "binary" ||
925
- character.type === "font" ||
926
- character.type === "fontCff" ||
927
- character.type === "sound") {
928
- existing?.applyPlace(place);
929
- continue;
930
- }
931
- // With the move flag the child stays, another character or not; and
932
- // where there is none, the move places nothing (`rewind-first`).
933
- if (place.move) {
934
- if (existing) {
935
- swap(existing, character);
936
- existing.applyPlace(place);
937
- }
938
- continue;
939
- }
940
- const child = displayFor(character, this.library, place.hasImage);
941
- child.applyPlace(place);
942
- child.placeFrame = frame;
943
- this.placeAtDepth(child, place.depth);
944
- if (later === "held" && this.library.construct) {
945
- this.held.push({ display: child, character });
946
- }
947
- else if (later === "frame" && this.library.constructLater) {
948
- this.library.constructLater(child, character);
949
- }
950
- else {
951
- construct(child, character, this.library);
952
- }
953
- }
954
- }
955
- /**
956
- * Jump to frame `frame`, as Flash does rather than by running the frames
957
- * between: the frames up to it (from the first, for a rewind) are
958
- * replayed into one jump per depth. On a rewind the children the timeline
959
- * placed after it go, but one at a depth the frames up to it end on a
960
- * place without the move flag at, which stays and takes the place; and
961
- * any child whose ratio is not the one the replayed frames give goes,
962
- * whenever it was placed. Each jump then changes the child still at its
963
- * depth, or makes one where the frames placed one anew, or where a
964
- * rewind ends on another character or ratio. The result is what playing
965
- * the frames would leave, except that a child placed before the frame and
966
- * untouched since keeps playing, and its identity; on a rewind a place
967
- * puts back what it leaves unsaid too, so it looks as it did when first
968
- * placed. A clip's own loop (`looping`) makes the children it places
969
- * alive in the frame's construct phase, as playing on does (`loop-ratio`);
970
- * a script's goto, at once.
971
- */
972
- gotoFrame(frame, looping = false) {
973
- const target = Math.max(1, Math.min(frame, this.totalFrames));
974
- const rewind = target < this.currentFrame;
975
- const jumps = new Map();
976
- for (let f = (rewind ? 0 : this.currentFrame) + 1; f <= target; f++) {
977
- for (const command of this.timeline.frames[f - 1] ?? []) {
978
- if (command.type === "remove") {
979
- jumps.delete(command.depth);
980
- // Going forward, a removal between the frames takes effect.
981
- if (!rewind) {
982
- this.removeAtDepth(command.depth);
983
- }
984
- continue;
985
- }
986
- const place = command.place;
987
- const jump = jumps.get(place.depth) ?? {
988
- before: null,
989
- place: null,
990
- frame: f,
991
- placed: false,
992
- };
993
- // A rewind replays from an empty display list, so what the frames do
994
- // at a depth nothing has placed yet is known: a change does nothing,
995
- // and a place in a child's stead places anew.
996
- if (place.character === null) {
997
- if (jump.place) {
998
- jump.place = mergePlace(jump.place, place);
999
- }
1000
- else if (!rewind) {
1001
- jump.before = jump.before ? mergePlace(jump.before, place) : place;
1002
- }
1003
- }
1004
- else if (!place.move && (jump.place || (!rewind && this.depths.has(place.depth)))) {
1005
- // At a depth taken by then, as frame by frame: let be.
1006
- continue;
1007
- }
1008
- else if (!place.move) {
1009
- // Anew: the child starts from nothing, and from here.
1010
- jump.before = null;
1011
- jump.place = rewind ? asFirstPlaced(place) : place;
1012
- jump.frame = f;
1013
- jump.placed = true;
1014
- }
1015
- else if (jump.place) {
1016
- jump.place = mergePlace(jump.place, place);
1017
- }
1018
- else if (!rewind && this.depths.has(place.depth)) {
1019
- jump.place = place;
1020
- jump.frame = f;
1021
- }
1022
- else {
1023
- // Another character with the move flag where nothing is: as frame
1024
- // by frame, nothing is placed (`rewind-first`).
1025
- continue;
1026
- }
1027
- jumps.set(place.depth, jump);
1028
- }
1029
- }
1030
- // On a rewind, what the timeline placed after the target goes, but where
1031
- // the frames replayed end on a place without the move flag: that child
1032
- // stays and takes the place, as Flash keeps it (`same-depth` at the
1033
- // loop, `rewind-first`), a clip its character and a shape the place's
1034
- // (swap, `morph-shapes`). Replayed from the first frame, a depth the
1035
- // frames left empty is empty. Whenever it was placed, a child whose
1036
- // ratio is not the one the frames replayed give goes too, to be made
1037
- // anew: authoring tools give each placement a ratio of its own, and
1038
- // Flash takes another ratio for another object, of every kind
1039
- // (`rewind-ratio`, `rewind-kinds`), where Ruffle compares it only for
1040
- // children placed after the target, and for morphs. In render order, as
1041
- // Ruffle removes.
1042
- const kept = new Set();
1043
- if (rewind) {
1044
- for (const child of [...this.children]) {
1045
- const depth = child.depth;
1046
- if (depth === null || child.placeFrame <= 0) {
1047
- continue;
1048
- }
1049
- const jump = jumps.get(depth);
1050
- if (!jump || (jump.place && jump.place.ratio !== child.ratio)) {
1051
- this.removeAtDepth(depth);
1052
- }
1053
- else if (child.placeFrame > target && jump.placed) {
1054
- kept.add(depth);
1055
- }
1056
- else if (child.placeFrame > target) {
1057
- this.removeAtDepth(depth);
1058
- }
1059
- }
1060
- }
1061
- // On the frame before its children are made: one constructed now sees it there, as in Flash.
1062
- this.currentFrame = target;
1063
- for (const [depth, jump] of jumps) {
1064
- const existing = this.depths.get(depth);
1065
- if (existing && kept.has(depth) && jump.place) {
1066
- const id = jump.place.character;
1067
- const character = id === null ? undefined : this.library.characters.get(id);
1068
- if (character) {
1069
- swap(existing, character);
1070
- }
1071
- existing.applyPlace(jump.place);
1072
- continue;
1073
- }
1074
- if (existing && jump.before) {
1075
- existing.applyPlace(jump.before);
1076
- }
1077
- if (!jump.place) {
1078
- continue;
1079
- }
1080
- const character = jump.place.character === null ? null : this.library.characters.get(jump.place.character);
1081
- if (!character ||
1082
- character.type === "binary" ||
1083
- character.type === "font" ||
1084
- character.type === "fontCff" ||
1085
- character.type === "sound") {
1086
- existing?.applyPlace(jump.place);
1087
- continue;
1088
- }
1089
- // Forward, the child stays for a place with the move flag, whatever
1090
- // character it names, as frame by frame; on a rewind it stays only
1091
- // for its own character, and another, however the frames between went,
1092
- // makes a new child (the corpus's `place_object_replace_2`). Placed
1093
- // anew it is a new child.
1094
- const same = existing?.character === character;
1095
- if (existing && (rewind ? same : jump.place.move)) {
1096
- swap(existing, character);
1097
- existing.applyPlace(jump.place);
1098
- continue;
1099
- }
1100
- const child = displayFor(character, this.library, jump.place.hasImage);
1101
- child.applyPlace(jump.place);
1102
- child.placeFrame = jump.frame;
1103
- this.placeAtDepth(child, depth);
1104
- if (looping && this.library.constructLater) {
1105
- this.library.constructLater(child, character);
1106
- }
1107
- else if (this.library.uncaught) {
1108
- // A constructor that throws is reported, and the children after it
1109
- // are still made; a first frame's, made in its parent's super(),
1110
- // still throw into the parent's constructor, as in Flash.
1111
- try {
1112
- construct(child, character, this.library);
1113
- }
1114
- catch (error) {
1115
- this.library.uncaught(error);
1116
- }
1117
- }
1118
- else {
1119
- construct(child, character, this.library);
1120
- }
1121
- }
1122
- }
1123
- /** The first frame, as a clip runs it when it is made, its children placed and made alive; once. */
1124
- enterFirstFrame() {
1125
- this.placeFirstFrame();
1126
- // One at a time: a constructor that throws stops here, as its error stops the frame.
1127
- for (let next = this.held.shift(); next; next = this.held.shift()) {
1128
- if (!next.display.object && next.display.parent === this) {
1129
- construct(next.display, next.character, this.library);
1130
- }
1131
- }
1132
- }
1133
- /** The first frame's children placed, to be made alive by enterFirstFrame; once. */
1134
- placeFirstFrame() {
1135
- if (this.currentFrame !== 0) {
1136
- return;
1137
- }
1138
- this.currentFrame = 1;
1139
- this.runFrame(1, "held");
1140
- }
1141
- /**
1142
- * On to the next frame, if it plays and has one. From the last frame it
1143
- * loops to the first as a goto, so what the first frame placed stays.
1144
- */
1145
- advance() {
1146
- if (this.fresh) {
1147
- this.fresh = false;
1148
- return;
1149
- }
1150
- if (!this.playing || this.totalFrames <= 1) {
1151
- return;
1152
- }
1153
- if (this.currentFrame >= this.totalFrames) {
1154
- this.gotoFrame(1, true);
1155
- return;
1156
- }
1157
- this.currentFrame++;
1158
- this.runFrame(this.currentFrame, "frame");
1159
- }
1160
- }
1161
- /**
1162
- * A rewind's place with what it leaves unsaid said: the transform, colour,
1163
- * ratio and the rest as a character first placed has them, as Ruffle's
1164
- * goto fills them in. The name and visibility are not among them: they
1165
- * stay as they were.
1166
- */
1167
- function asFirstPlaced(place) {
1168
- return {
1169
- ...place,
1170
- matrix: place.matrix ?? IDENTITY,
1171
- colorTransform: place.colorTransform ?? IDENTITY_COLOR,
1172
- ratio: place.ratio ?? 0,
1173
- blendMode: place.blendMode ?? 0,
1174
- // An empty FILTERLIST: a rewind clears what the frames after set.
1175
- filters: place.filters ?? NO_FILTER_BYTES,
1176
- cacheAsBitmap: place.cacheAsBitmap ?? false,
1177
- };
1178
- }
1179
- const IDENTITY_COLOR = {
1180
- rMul: 1,
1181
- gMul: 1,
1182
- bMul: 1,
1183
- aMul: 1,
1184
- rAdd: 0,
1185
- gAdd: 0,
1186
- bAdd: 0,
1187
- aAdd: 0,
1188
- };
1189
- /** `next` over `previous`: what the later place sets, and the rest as it was. */
1190
- function mergePlace(previous, next) {
1191
- return {
1192
- ...previous,
1193
- character: next.character ?? previous.character,
1194
- matrix: next.matrix ?? previous.matrix,
1195
- colorTransform: next.colorTransform ?? previous.colorTransform,
1196
- ratio: next.ratio ?? previous.ratio,
1197
- name: next.name ?? previous.name,
1198
- clipDepth: next.clipDepth ?? previous.clipDepth,
1199
- className: next.className ?? previous.className,
1200
- blendMode: next.blendMode ?? previous.blendMode,
1201
- cacheAsBitmap: next.cacheAsBitmap ?? previous.cacheAsBitmap,
1202
- visible: next.visible ?? previous.visible,
1203
- opaqueBackground: next.opaqueBackground ?? previous.opaqueBackground,
1204
- filters: next.filters ?? previous.filters,
1205
- clipActions: next.clipActions ?? previous.clipActions,
1206
- };
1207
- }
1208
- /** A display object for a character, before its first frame: a shape, or a clip. */
1209
- /** `hasImage` is PlaceObject3's flag where a timeline places it, which a bitmap's construction reads. */
1210
- export function displayFor(character, library, hasImage = false) {
1211
- if (character.type === "shape") {
1212
- return new ShapeObject(character);
1213
- }
1214
- if (character.type === "morph") {
1215
- return ShapeObject.ofMorph(character);
1216
- }
1217
- if (character.type === "bitmap") {
1218
- // With scripts, its Bitmap's constructor gives it its data; without, a copy of the pixels.
1219
- const bitmap = new BitmapObject(library.construct ? null : BitmapStore.of(character.pixels ?? INVALID_PIXELS));
1220
- bitmap.character = character;
1221
- bitmap.hasImage = hasImage;
1222
- return bitmap;
1223
- }
1224
- if (character.type === "static") {
1225
- return new StaticTextObject(character, library.characters);
1226
- }
1227
- if (character.type === "text") {
1228
- const text = new TextObject(character);
1229
- text.fonts = library.fonts;
1230
- return text;
1231
- }
1232
- // Its states are made where it is constructed (Scripting.construct).
1233
- if (character.type === "button") {
1234
- const button = new ButtonObject();
1235
- button.character = character;
1236
- button.trackAsMenu = character.trackAsMenu;
1237
- return button;
1238
- }
1239
- const clip = new MovieClip(character.timeline, library);
1240
- clip.character = character;
1241
- return clip;
1242
- }
1243
- /**
1244
- * Bring a display object the timeline placed to life: its AS3 object
1245
- * constructed where the SWF has scripts, which enters a clip's first frame
1246
- * and names it on its parent, else a clip's first frame entered. After the
1247
- * placement, as Flash has it, so that the parent and the name are there.
1248
- */
1249
- function construct(display, character, library) {
1250
- if (library.construct) {
1251
- library.construct(display, character);
1252
- }
1253
- else if (display instanceof MovieClip) {
1254
- display.enterFirstFrame();
1255
- }
1256
- else if (display instanceof ButtonObject && character.type === "button") {
1257
- buttonStates(display, character, library, (d, c) => construct(d, c, library), () => { });
1258
- }
1259
- }
1260
- /** A display object for a character as the timeline would place it, alive, but in no container. */
1261
- export function instantiate(character, library) {
1262
- const display = displayFor(character, library);
1263
- construct(display, character, library);
1264
- return display;
1265
- }
1266
- /** A timeline of one empty frame: a clip a script makes. */
1267
- export const EMPTY_TIMELINE = {
1268
- frames: [[]],
1269
- labels: [],
1270
- gotoLabels: [],
1271
- frameLabels: new Map(),
1272
- scenes: [{ name: "", frame: 1 }],
1273
- };
1274
- //# sourceMappingURL=display.js.map