Reforactor

This commit is contained in:
Mike Welsh 2019-04-27 23:08:59 -07:00
parent 2609d8c00d
commit ee64cc77bc
29 changed files with 244 additions and 256 deletions

View File

@ -1,6 +1,6 @@
[workspace] [workspace]
members = [ members = [
"lib", "core",
"desktop", "desktop",
"web" "web"
] ]

View File

@ -4,15 +4,18 @@ version = "0.1.0"
authors = ["Mike Welsh <mwelsh@gmail.com>"] authors = ["Mike Welsh <mwelsh@gmail.com>"]
edition = "2018" edition = "2018"
[features]
default = ["console_error_panic_hook", "console_log"]
[dependencies] [dependencies]
bacon_rajan_cc = "0.2" bacon_rajan_cc = "0.2"
log = "0.4" log = "0.4"
url = "1.7.2" url = "1.7.2"
svg = "0.5.12" svg = "0.5.12"
swf = { git = "https://github.com/Herschel/swf-rs", version = "*" } swf = { git = "https://github.com/Herschel/swf-rs", version = "*" }
js-sys = "0.3.19"
wasm-bindgen = "0.2"
[dependencies.web-sys]
version = "0.3.19"
features = ["CanvasRenderingContext2d", "HtmlCanvasElement", "HtmlImageElement"]
# Desktop dependencies # Desktop dependencies
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
@ -21,13 +24,3 @@ glium = "0.24"
glutin = "0.20" glutin = "0.20"
winit = "0.19.1" winit = "0.19.1"
# Wasm32 dependencies
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = { version = "0.1.1", optional = true }
console_log = { version = "0.1", optional = true }
js-sys = "0.3.19"
wasm-bindgen = "0.2"
[dependencies.web-sys]
version = "0.3.19"
features = ["CanvasRenderingContext2d", "Document", "Element", "HtmlCanvasElement", "Performance", "HtmlImageElement", "Window"]

1
core/src/backend.rs Normal file
View File

@ -0,0 +1 @@
pub mod render;

View File

@ -1,10 +1,12 @@
pub mod common; pub mod common;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub mod glium; pub mod glium;
pub mod null;
pub mod shape_utils; pub mod shape_utils;
#[cfg(target_arch = "wasm32")]
pub mod web_canvas; pub mod web_canvas;
pub use null::NullRenderer;
use self::common::ShapeHandle; use self::common::ShapeHandle;
use crate::{matrix::Matrix, Color}; use crate::{matrix::Matrix, Color};
@ -12,7 +14,7 @@ pub trait RenderBackend {
fn register_shape(&mut self, shape: &swf::Shape) -> common::ShapeHandle; fn register_shape(&mut self, shape: &swf::Shape) -> common::ShapeHandle;
fn begin_frame(&mut self); fn begin_frame(&mut self);
fn clear(&mut self, color: crate::Color); fn clear(&mut self, color: Color);
fn render_shape(&mut self, shape: ShapeHandle, matrix: &Matrix); fn render_shape(&mut self, shape: ShapeHandle, matrix: &Matrix);
fn end_frame(&mut self); fn end_frame(&mut self);
} }

View File

@ -1,9 +1,8 @@
use super::{common::ShapeHandle, shape_utils, RenderBackend}; use super::{common::ShapeHandle, RenderBackend};
use crate::backend::ui::glutin::GlutinBackend;
use crate::{matrix::Matrix, Color}; use crate::{matrix::Matrix, Color};
use glium::{implement_vertex, uniform, Display, Frame, Surface}; use glium::{implement_vertex, uniform, Display, Frame, Surface};
use lyon::tessellation::geometry_builder::{BuffersBuilder, VertexBuffers, VertexConstructor}; use glutin::WindowedContext;
use lyon::tessellation::FillVertex; use lyon::tessellation::geometry_builder::{BuffersBuilder, VertexBuffers};
use lyon::{path::PathEvent, tessellation, tessellation::FillTessellator}; use lyon::{path::PathEvent, tessellation, tessellation::FillTessellator};
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
use swf::{FillStyle, LineStyle}; use swf::{FillStyle, LineStyle};
@ -16,8 +15,10 @@ pub struct GliumRenderBackend {
} }
impl GliumRenderBackend { impl GliumRenderBackend {
pub fn new(ui: &mut GlutinBackend) -> Result<GliumRenderBackend, Box<std::error::Error>> { pub fn new(
let display = Display::from_gl_window(ui.take_context())?; windowed_context: WindowedContext,
) -> Result<GliumRenderBackend, Box<std::error::Error>> {
let display = Display::from_gl_window(windowed_context)?;
let shader_program = let shader_program =
glium::Program::from_source(&display, VERTEX_SHADER, FRAGMENT_SHADER, None)?; glium::Program::from_source(&display, VERTEX_SHADER, FRAGMENT_SHADER, None)?;
@ -42,7 +43,7 @@ impl RenderBackend for GliumRenderBackend {
let mut fill_tess = FillTessellator::new(); let mut fill_tess = FillTessellator::new();
for (cmd, path) in paths { for (cmd, path) in paths {
if let &PathCommandType::Stroke(_) = &cmd { if let PathCommandType::Stroke(_) = cmd {
continue; continue;
} }
let color = match cmd { let color = match cmd {
@ -188,7 +189,7 @@ fn swf_shape_to_lyon_paths(
let mut prev; let mut prev;
use lyon::geom::{LineSegment, QuadraticBezierSegment}; use lyon::geom::{LineSegment, QuadraticBezierSegment};
for cmd in cmds { for cmd in cmds {
if let PathCommandType::Fill(fill_style) = &cmd.command_type { if let PathCommandType::Fill(_fill_style) = &cmd.command_type {
let mut out_path = vec![PathEvent::MoveTo(point(cmd.path.start.0, cmd.path.start.1))]; let mut out_path = vec![PathEvent::MoveTo(point(cmd.path.start.0, cmd.path.start.1))];
prev = point(cmd.path.start.0, cmd.path.start.1); prev = point(cmd.path.start.0, cmd.path.start.1);
for edge in cmd.path.edges { for edge in cmd.path.edges {
@ -260,7 +261,7 @@ fn get_paths(shape: &swf::Shape) -> impl Iterator<Item = PathCommand> {
} }
if let Some(ref new_styles) = style_change.new_styles { if let Some(ref new_styles) = style_change.new_styles {
for (id, paths) in paths { for (_id, paths) in paths {
for path in paths.open_paths { for path in paths.open_paths {
out.push(PathCommand { out.push(PathCommand {
command_type: paths.command_type.clone(), command_type: paths.command_type.clone(),

View File

@ -0,0 +1,14 @@
use super::{common::ShapeHandle, RenderBackend};
use crate::{matrix::Matrix, Color};
pub struct NullRenderer;
impl RenderBackend for NullRenderer {
fn register_shape(&mut self, _shape: &swf::Shape) -> ShapeHandle {
ShapeHandle(0)
}
fn begin_frame(&mut self) {}
fn end_frame(&mut self) {}
fn clear(&mut self, _color: Color) {}
fn render_shape(&mut self, _shape: ShapeHandle, _matrix: &Matrix) {}
}

View File

@ -227,7 +227,7 @@ pub fn swf_shape_to_svg(shape: &Shape) -> String {
line_style.color.r, line_style.color.g, line_style.color.b, line_style.color.a line_style.color.r, line_style.color.g, line_style.color.b, line_style.color.a
), ),
) )
.set("width", line_style.width as f32 / 20.0); .set("width", f32::from(line_style.width) / 20.0);
let mut data = Data::new(); let mut data = Data::new();
for subpath in &stroke.subpaths { for subpath in &stroke.subpaths {

View File

@ -0,0 +1,95 @@
use super::{common::ShapeHandle, RenderBackend};
use crate::{matrix::Matrix, Color};
use log::info;
use wasm_bindgen::JsCast;
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, HtmlImageElement};
pub struct WebCanvasRenderBackend {
context: CanvasRenderingContext2d,
width: f64,
height: f64,
shapes: Vec<ShapeData>,
}
struct ShapeData {
image: HtmlImageElement,
x_min: f64,
y_min: f64,
}
impl WebCanvasRenderBackend {
pub fn new(canvas: HtmlCanvasElement) -> Result<Self, Box<std::error::Error>> {
let width = canvas.width();
let height = canvas.height();
let context: CanvasRenderingContext2d = canvas
.get_context("2d")
.map_err(|_| "Could not create context")?
.ok_or("Could not create context")?
.dyn_into()
.map_err(|_| "Expected CanvasRenderingContext2d")?;
Ok(Self {
context,
width: width.into(),
height: height.into(),
shapes: vec![],
})
}
}
impl RenderBackend for WebCanvasRenderBackend {
fn register_shape(&mut self, shape: &swf::Shape) -> ShapeHandle {
let handle = ShapeHandle(self.shapes.len());
let image = HtmlImageElement::new().unwrap();
use url::percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
let svg = super::shape_utils::swf_shape_to_svg(&shape);
let svg_encoded = format!(
"data:image/svg+xml,{}",
utf8_percent_encode(&svg, DEFAULT_ENCODE_SET)
);
info!("{}", svg_encoded);
image.set_src(&svg_encoded);
self.shapes.push(ShapeData {
image,
x_min: shape.shape_bounds.x_min.into(),
y_min: shape.shape_bounds.y_min.into(),
});
handle
}
fn begin_frame(&mut self) {
self.context.reset_transform().unwrap();
}
fn end_frame(&mut self) {
// Noop
}
fn clear(&mut self, color: Color) {
let color = format!("rgb({}, {}, {}", color.r, color.g, color.b);
self.context.set_fill_style(&color.into());
self.context.fill_rect(0.0, 0.0, self.width, self.height);
}
fn render_shape(&mut self, shape: ShapeHandle, matrix: &Matrix) {
let shape = &self.shapes[shape.0];
self.context
.set_transform(
matrix.a.into(),
matrix.b.into(),
matrix.c.into(),
matrix.d.into(),
matrix.tx.into(),
matrix.ty.into(),
)
.unwrap();
self.context
.draw_image_with_html_image_element(&shape.image, shape.x_min, shape.y_min)
.unwrap();
}
}

View File

@ -1,4 +1,4 @@
mod backend; pub mod backend;
mod character; mod character;
mod color_transform; mod color_transform;
mod display_object; mod display_object;

View File

@ -59,7 +59,7 @@ impl MovieClip {
context: &mut UpdateContext, context: &mut UpdateContext,
) { ) {
use swf::PlaceObjectAction; use swf::PlaceObjectAction;
let mut character = match place_object.action { let character = match place_object.action {
PlaceObjectAction::Place(id) => { PlaceObjectAction::Place(id) => {
// TODO(Herschel): Behavior when character doesn't exist/isn't a DisplayObject? // TODO(Herschel): Behavior when character doesn't exist/isn't a DisplayObject?
let character = let character =

View File

@ -1,4 +1,4 @@
use crate::backend::{render::RenderBackend, ui::UiBackend}; use crate::backend::render::RenderBackend;
use crate::color_transform::ColorTransformStack; use crate::color_transform::ColorTransformStack;
use crate::display_object::DisplayObject; use crate::display_object::DisplayObject;
use crate::library::Library; use crate::library::Library;
@ -25,8 +25,6 @@ pub struct Player {
render_context: RenderContext, render_context: RenderContext,
ui: Box<UiBackend>,
library: Library, library: Library,
stage: Cc<RefCell<Stage>>, stage: Cc<RefCell<Stage>>,
@ -35,21 +33,23 @@ pub struct Player {
} }
impl Player { impl Player {
pub fn new(swf_data: Vec<u8>) -> Result<Player, Box<std::error::Error>> { pub fn new(
Self::new_internal(swf_data) renderer: Box<RenderBackend>,
swf_data: Vec<u8>,
) -> Result<Player, Box<std::error::Error>> {
Self::new_internal(renderer, swf_data)
} }
fn new_internal(swf_data: Vec<u8>) -> Result<Player, Box<std::error::Error>> { fn new_internal(
renderer: Box<RenderBackend>,
swf_data: Vec<u8>,
) -> Result<Player, Box<std::error::Error>> {
let (swf, tag_stream) = swf::read::read_swf_header_decompressed(&swf_data[..]).unwrap(); let (swf, tag_stream) = swf::read::read_swf_header_decompressed(&swf_data[..]).unwrap();
info!("{}x{}", swf.stage_size.x_max, swf.stage_size.y_max); info!("{}x{}", swf.stage_size.x_max, swf.stage_size.y_max);
let (ui, renderer) = crate::backend::build()?;
Ok(Player { Ok(Player {
tag_stream, tag_stream,
ui,
render_context: RenderContext { render_context: RenderContext {
renderer, renderer,
matrix_stack: MatrixStack::new(), matrix_stack: MatrixStack::new(),
@ -64,22 +64,6 @@ impl Player {
}) })
} }
pub fn play(&mut self) {
use std::time::{Duration, Instant};
let mut time = Instant::now();
while self.ui.poll_events() {
let new_time = Instant::now();
let dt = new_time.duration_since(time).as_millis();
time = new_time;
self.tick(dt as f64);
std::thread::sleep(Duration::from_millis(1000 / 60));
}
}
pub fn tick(&mut self, dt: f64) { pub fn tick(&mut self, dt: f64) {
self.frame_accumulator += dt; self.frame_accumulator += dt;
let frame_time = 1000.0 / self.frame_rate; let frame_time = 1000.0 / self.frame_rate;

View File

@ -5,5 +5,6 @@ authors = ["Mike Welsh <mwelsh@gmail.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
fluster_core = { path = "../lib" } fluster_core = { path = "../core" }
glutin = "0.20"
structopt = "0.2.15" structopt = "0.2.15"

View File

@ -1,9 +1,7 @@
use fluster_core::Player; use fluster_core::{backend::render::glium::GliumRenderBackend, Player};
use glutin::{ContextBuilder, Event, EventsLoop, WindowBuilder, WindowEvent};
#[cfg(not(target_arch = "wasm32"))]
#[allow(dead_code)]
fn main() {
use std::path::PathBuf; use std::path::PathBuf;
use std::time::{Duration, Instant};
use structopt::StructOpt; use structopt::StructOpt;
#[derive(StructOpt, Debug)] #[derive(StructOpt, Debug)]
@ -13,9 +11,64 @@ fn main() {
input_path: PathBuf, input_path: PathBuf,
} }
fn main() {
let opt = Opt::from_args(); let opt = Opt::from_args();
let swf_data = std::fs::read(opt.input_path).unwrap(); let ret = run_player(opt.input_path);
let mut player = Player::new(swf_data).unwrap();
player.play(); if let Err(e) = ret {
eprintln!("Fatal error:\n{}", e);
std::process::exit(-1);
} }
}
fn run_player(input_path: PathBuf) -> Result<(), Box<std::error::Error>> {
let swf_data = std::fs::read(input_path)?;
let mut events_loop = EventsLoop::new();
let window_builder = WindowBuilder::new();
let windowed_context = ContextBuilder::new().build_windowed(window_builder, &events_loop)?;
let renderer = GliumRenderBackend::new(windowed_context)?;
let mut player = Player::new(Box::new(renderer), swf_data)?;
let mut time = Instant::now();
loop {
// Poll UI events
let mut request_close = false;
events_loop.poll_events(|event| match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => request_close = true,
_ => (),
});
if request_close {
break;
}
let new_time = Instant::now();
let dt = new_time.duration_since(time).as_millis();
time = new_time;
player.tick(dt as f64);
std::thread::sleep(Duration::from_millis(1000 / 60));
}
Ok(())
}
// impl UiBackend for GlutinBackend {
// fn poll_events(&mut self) -> bool {
// let mut request_close = false;
// self.events_loop.poll_events(|event| match event {
// Event::WindowEvent {
// event: WindowEvent::CloseRequested,
// ..
// } => request_close = true,
// _ => (),
// });
// !request_close
// }
// }

View File

@ -1,36 +0,0 @@
pub mod render;
pub mod ui;
use render::RenderBackend;
use ui::UiBackend;
#[cfg(not(target_arch = "wasm32"))]
pub fn build() -> Result<(Box<UiBackend>, Box<RenderBackend>), Box<std::error::Error>> {
let mut ui = ui::glutin::GlutinBackend::new(500, 500)?;
let renderer = render::glium::GliumRenderBackend::new(&mut ui)?;
Ok((Box::new(ui), Box::new(renderer)))
}
#[cfg(target_arch = "wasm32")]
pub fn build() -> Result<(Box<UiBackend>, Box<RenderBackend>), Box<std::error::Error>> {
use wasm_bindgen::JsCast;
let window = web_sys::window().ok_or("Expected window")?;
let document = window.document().ok_or("Expected document")?;
let canvas: web_sys::HtmlCanvasElement = document
.get_element_by_id("fluster-canvas")
.ok_or("Missing element")?
.dyn_into()
.map_err(|_| "Not a canvas")?;
let (width, height) = (f64::from(canvas.width()), f64::from(canvas.height()));
let context: web_sys::CanvasRenderingContext2d = canvas
.get_context("2d")
.map_err(|_| "Unable to make canvas context")?
.ok_or("Unable to make canvas context")?
.dyn_into()
.map_err(|_| "Not a CanvasRenderingContext2d")?;
let mut ui = ui::web_canvas::WebCanvasBackend::new(canvas)?;
let renderer = render::web_canvas::WebCanvasRenderBackend::new(context, width, height);
Ok((Box::new(ui), Box::new(renderer)))
}

View File

@ -1,69 +0,0 @@
use super::{common::ShapeHandle, RenderBackend};
use crate::{Color, matrix::Matrix};
use log::info;
use web_sys::{CanvasRenderingContext2d, HtmlImageElement};
pub struct WebCanvasRenderBackend {
context: CanvasRenderingContext2d,
width: f64,
height: f64,
shapes: Vec<ShapeData>,
}
struct ShapeData {
image: HtmlImageElement,
x_min: f64,
y_min: f64,
}
impl WebCanvasRenderBackend {
pub fn new(context: CanvasRenderingContext2d, width: f64, height: f64) -> Self {
Self {
context,
width,
height,
shapes: vec![],
}
}
}
impl RenderBackend for WebCanvasRenderBackend {
fn register_shape(&mut self, shape: &swf::Shape) -> ShapeHandle {
let handle = ShapeHandle(self.shapes.len());
let image = HtmlImageElement::new().unwrap();
use url::percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
let svg = super::shape_utils::swf_shape_to_svg(&shape);
let svg_encoded = format!("data:image/svg+xml,{}", utf8_percent_encode(&svg, DEFAULT_ENCODE_SET));
info!("{}", svg_encoded);
image.set_src(&svg_encoded);
self.shapes.push(ShapeData{
image, x_min: shape.shape_bounds.x_min.into(), y_min: shape.shape_bounds.y_min.into()
});
handle
}
fn begin_frame(&mut self) {
self.context.reset_transform();
}
fn end_frame(&mut self) {
// Noop
}
fn clear(&mut self, color: Color) {
let color = format!("rgb({}, {}, {}", color.r, color.g, color.b);
self.context.set_fill_style(&color.into());
self.context.fill_rect(0.0, 0.0, self.width, self.height);
}
fn render_shape(&mut self, shape: ShapeHandle, matrix: &Matrix) {
let shape = &self.shapes[shape.0];
self.context.set_transform(matrix.a.into(), matrix.b.into(), matrix.c.into(), matrix.d.into(), matrix.tx.into(), matrix.ty.into()).unwrap();
self.context.draw_image_with_html_image_element(&shape.image, shape.x_min, shape.y_min).unwrap();
}
}

View File

@ -1,9 +0,0 @@
#[cfg(not(target_arch = "wasm32"))]
pub mod glutin;
#[cfg(target_arch = "wasm32")]
pub mod web_canvas;
pub trait UiBackend {
fn poll_events(&mut self) -> bool;
}

View File

@ -1,38 +0,0 @@
use super::UiBackend;
use glutin::{ContextBuilder, Event, EventsLoop, WindowBuilder, WindowEvent, WindowedContext};
pub struct GlutinBackend {
context: Option<WindowedContext>,
events_loop: EventsLoop,
}
impl GlutinBackend {
pub fn new(width: u32, height: u32) -> Result<GlutinBackend, Box<std::error::Error>> {
let events_loop = EventsLoop::new();
let window_builder = WindowBuilder::new().with_dimensions((width, height).into());
let context = ContextBuilder::new().build_windowed(window_builder, &events_loop)?;
Ok(GlutinBackend {
context: Some(context),
events_loop,
})
}
pub fn take_context(&mut self) -> WindowedContext {
self.context.take().unwrap()
}
}
impl UiBackend for GlutinBackend {
fn poll_events(&mut self) -> bool {
let mut request_close = false;
self.events_loop.poll_events(|event| match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => request_close = true,
_ => (),
});
!request_close
}
}

View File

@ -1,23 +0,0 @@
use super::UiBackend;
use web_sys::HtmlCanvasElement;
pub struct WebCanvasBackend {
canvas: HtmlCanvasElement,
}
impl WebCanvasBackend {
pub fn new(canvas: HtmlCanvasElement) -> Result<Self, Box<std::error::Error>> {
use log::Level;
console_log::init_with_level(Level::Trace)?;
Ok(Self {
canvas
})
}
}
impl UiBackend for WebCanvasBackend {
fn poll_events(&mut self) -> bool {
true
}
}

View File

@ -7,12 +7,19 @@ edition = "2018"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook", "console_log"]
[dependencies] [dependencies]
console_error_panic_hook = { version = "0.1.1", optional = true } console_error_panic_hook = { version = "0.1.1", optional = true }
console_log = { version = "0.1", optional = true } console_log = { version = "0.1", optional = true }
fluster_core = { path = "../lib" } fluster_core = { path = "../core" }
js-sys = "0.3.19" js-sys = "0.3.19"
wasm-bindgen = "0.2" wasm-bindgen = "0.2"
[dependencies.web-sys]
version = "0.3.19"
features = ["CanvasRenderingContext2d", "HtmlCanvasElement"]
[dev-dependencies] [dev-dependencies]
wasm-bindgen-test = "0.2" wasm-bindgen-test = "0.2"

View File

@ -1,20 +1,33 @@
use fluster_core::backend::render::web_canvas::WebCanvasRenderBackend;
use js_sys::Uint8Array; use js_sys::Uint8Array;
use wasm_bindgen::{prelude::*, JsValue}; use wasm_bindgen::{prelude::*, JsValue};
use web_sys::HtmlCanvasElement;
#[wasm_bindgen] #[wasm_bindgen]
pub struct Player(fluster_core::Player); pub struct Player(fluster_core::Player);
#[wasm_bindgen] #[wasm_bindgen]
impl Player { impl Player {
pub fn new(swf_data: Uint8Array) -> Result<Player, JsValue> { pub fn new(canvas: HtmlCanvasElement, swf_data: Uint8Array) -> Result<Player, JsValue> {
let mut data = vec![0; swf_data.length() as usize]; Player::new_internal(canvas, swf_data).map_err(|_| "Error creating player".into())
swf_data.copy_to(&mut data[..]);
let player = fluster_core::Player::new(data).map_err(|_| JsValue::null())?;
Ok(Player(player))
} }
pub fn tick(&mut self, dt: f64) { pub fn tick(&mut self, dt: f64) {
self.0.tick(dt); self.0.tick(dt);
} }
} }
impl Player {
fn new_internal(
canvas: HtmlCanvasElement,
swf_data: Uint8Array,
) -> Result<Player, Box<std::error::Error>> {
let mut data = vec![0; swf_data.length() as usize];
swf_data.copy_to(&mut data[..]);
let renderer = WebCanvasRenderBackend::new(canvas)?;
let player = fluster_core::Player::new(Box::new(renderer), data)?;
Ok(Player(player))
}
}

View File

@ -1,4 +1,4 @@
import { Player } from "fluster"; import { Player } from "../pkg/fluster";
let fileInput = document.getElementById("file-input"); let fileInput = document.getElementById("file-input");
fileInput.addEventListener("change", fileSelected, false); fileInput.addEventListener("change", fileSelected, false);
@ -20,8 +20,7 @@ let timestamp = 0;
function playSwf(swfData) { function playSwf(swfData) {
let canvas = document.getElementById("fluster-canvas"); let canvas = document.getElementById("fluster-canvas");
if (swfData && canvas) { if (swfData && canvas) {
let data = new Uint8Array(swfData); player = Player.new(canvas, new Uint8Array(swfData));
player = Player.new(data);
timestamp = performance.now(); timestamp = performance.now();
window.requestAnimationFrame(tickPlayer); window.requestAnimationFrame(tickPlayer);
} }