Reforactor
This commit is contained in:
parent
2609d8c00d
commit
ee64cc77bc
|
@ -1,6 +1,6 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"lib",
|
||||
"core",
|
||||
"desktop",
|
||||
"web"
|
||||
]
|
||||
|
|
|
@ -4,15 +4,18 @@ version = "0.1.0"
|
|||
authors = ["Mike Welsh <mwelsh@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
default = ["console_error_panic_hook", "console_log"]
|
||||
|
||||
[dependencies]
|
||||
bacon_rajan_cc = "0.2"
|
||||
log = "0.4"
|
||||
url = "1.7.2"
|
||||
svg = "0.5.12"
|
||||
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
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
|
@ -21,13 +24,3 @@ glium = "0.24"
|
|||
glutin = "0.20"
|
||||
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"]
|
|
@ -0,0 +1 @@
|
|||
pub mod render;
|
|
@ -1,10 +1,12 @@
|
|||
pub mod common;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod glium;
|
||||
pub mod null;
|
||||
pub mod shape_utils;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod web_canvas;
|
||||
|
||||
pub use null::NullRenderer;
|
||||
|
||||
use self::common::ShapeHandle;
|
||||
use crate::{matrix::Matrix, Color};
|
||||
|
||||
|
@ -12,7 +14,7 @@ pub trait RenderBackend {
|
|||
fn register_shape(&mut self, shape: &swf::Shape) -> common::ShapeHandle;
|
||||
|
||||
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 end_frame(&mut self);
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
use super::{common::ShapeHandle, shape_utils, RenderBackend};
|
||||
use crate::backend::ui::glutin::GlutinBackend;
|
||||
use super::{common::ShapeHandle, RenderBackend};
|
||||
use crate::{matrix::Matrix, Color};
|
||||
use glium::{implement_vertex, uniform, Display, Frame, Surface};
|
||||
use lyon::tessellation::geometry_builder::{BuffersBuilder, VertexBuffers, VertexConstructor};
|
||||
use lyon::tessellation::FillVertex;
|
||||
use glutin::WindowedContext;
|
||||
use lyon::tessellation::geometry_builder::{BuffersBuilder, VertexBuffers};
|
||||
use lyon::{path::PathEvent, tessellation, tessellation::FillTessellator};
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use swf::{FillStyle, LineStyle};
|
||||
|
@ -16,8 +15,10 @@ pub struct GliumRenderBackend {
|
|||
}
|
||||
|
||||
impl GliumRenderBackend {
|
||||
pub fn new(ui: &mut GlutinBackend) -> Result<GliumRenderBackend, Box<std::error::Error>> {
|
||||
let display = Display::from_gl_window(ui.take_context())?;
|
||||
pub fn new(
|
||||
windowed_context: WindowedContext,
|
||||
) -> Result<GliumRenderBackend, Box<std::error::Error>> {
|
||||
let display = Display::from_gl_window(windowed_context)?;
|
||||
|
||||
let shader_program =
|
||||
glium::Program::from_source(&display, VERTEX_SHADER, FRAGMENT_SHADER, None)?;
|
||||
|
@ -42,7 +43,7 @@ impl RenderBackend for GliumRenderBackend {
|
|||
let mut fill_tess = FillTessellator::new();
|
||||
|
||||
for (cmd, path) in paths {
|
||||
if let &PathCommandType::Stroke(_) = &cmd {
|
||||
if let PathCommandType::Stroke(_) = cmd {
|
||||
continue;
|
||||
}
|
||||
let color = match cmd {
|
||||
|
@ -188,7 +189,7 @@ fn swf_shape_to_lyon_paths(
|
|||
let mut prev;
|
||||
use lyon::geom::{LineSegment, QuadraticBezierSegment};
|
||||
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))];
|
||||
prev = point(cmd.path.start.0, cmd.path.start.1);
|
||||
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 {
|
||||
for (id, paths) in paths {
|
||||
for (_id, paths) in paths {
|
||||
for path in paths.open_paths {
|
||||
out.push(PathCommand {
|
||||
command_type: paths.command_type.clone(),
|
|
@ -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) {}
|
||||
}
|
|
@ -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
|
||||
),
|
||||
)
|
||||
.set("width", line_style.width as f32 / 20.0);
|
||||
.set("width", f32::from(line_style.width) / 20.0);
|
||||
|
||||
let mut data = Data::new();
|
||||
for subpath in &stroke.subpaths {
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
mod backend;
|
||||
pub mod backend;
|
||||
mod character;
|
||||
mod color_transform;
|
||||
mod display_object;
|
|
@ -59,7 +59,7 @@ impl MovieClip {
|
|||
context: &mut UpdateContext,
|
||||
) {
|
||||
use swf::PlaceObjectAction;
|
||||
let mut character = match place_object.action {
|
||||
let character = match place_object.action {
|
||||
PlaceObjectAction::Place(id) => {
|
||||
// TODO(Herschel): Behavior when character doesn't exist/isn't a DisplayObject?
|
||||
let character =
|
|
@ -1,4 +1,4 @@
|
|||
use crate::backend::{render::RenderBackend, ui::UiBackend};
|
||||
use crate::backend::render::RenderBackend;
|
||||
use crate::color_transform::ColorTransformStack;
|
||||
use crate::display_object::DisplayObject;
|
||||
use crate::library::Library;
|
||||
|
@ -25,8 +25,6 @@ pub struct Player {
|
|||
|
||||
render_context: RenderContext,
|
||||
|
||||
ui: Box<UiBackend>,
|
||||
|
||||
library: Library,
|
||||
stage: Cc<RefCell<Stage>>,
|
||||
|
||||
|
@ -35,21 +33,23 @@ pub struct Player {
|
|||
}
|
||||
|
||||
impl Player {
|
||||
pub fn new(swf_data: Vec<u8>) -> Result<Player, Box<std::error::Error>> {
|
||||
Self::new_internal(swf_data)
|
||||
pub fn new(
|
||||
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();
|
||||
info!("{}x{}", swf.stage_size.x_max, swf.stage_size.y_max);
|
||||
|
||||
let (ui, renderer) = crate::backend::build()?;
|
||||
|
||||
Ok(Player {
|
||||
tag_stream,
|
||||
|
||||
ui,
|
||||
|
||||
render_context: RenderContext {
|
||||
renderer,
|
||||
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) {
|
||||
self.frame_accumulator += dt;
|
||||
let frame_time = 1000.0 / self.frame_rate;
|
|
@ -5,5 +5,6 @@ authors = ["Mike Welsh <mwelsh@gmail.com>"]
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
fluster_core = { path = "../lib" }
|
||||
fluster_core = { path = "../core" }
|
||||
glutin = "0.20"
|
||||
structopt = "0.2.15"
|
|
@ -1,21 +1,74 @@
|
|||
use fluster_core::Player;
|
||||
use fluster_core::{backend::render::glium::GliumRenderBackend, Player};
|
||||
use glutin::{ContextBuilder, Event, EventsLoop, WindowBuilder, WindowEvent};
|
||||
use std::path::PathBuf;
|
||||
use std::time::{Duration, Instant};
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
#[structopt(name = "basic")]
|
||||
struct Opt {
|
||||
#[structopt(name = "FILE", parse(from_os_str))]
|
||||
input_path: PathBuf,
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[allow(dead_code)]
|
||||
fn main() {
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
#[structopt(name = "basic")]
|
||||
struct Opt {
|
||||
#[structopt(name = "FILE", parse(from_os_str))]
|
||||
input_path: PathBuf,
|
||||
}
|
||||
|
||||
let opt = Opt::from_args();
|
||||
|
||||
let swf_data = std::fs::read(opt.input_path).unwrap();
|
||||
let mut player = Player::new(swf_data).unwrap();
|
||||
player.play();
|
||||
let ret = run_player(opt.input_path);
|
||||
|
||||
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
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -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)))
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -7,12 +7,19 @@ edition = "2018"
|
|||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
default = ["console_error_panic_hook", "console_log"]
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = { version = "0.1.1", optional = true }
|
||||
console_log = { version = "0.1", optional = true }
|
||||
fluster_core = { path = "../lib" }
|
||||
fluster_core = { path = "../core" }
|
||||
js-sys = "0.3.19"
|
||||
wasm-bindgen = "0.2"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3.19"
|
||||
features = ["CanvasRenderingContext2d", "HtmlCanvasElement"]
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.2"
|
|
@ -1,20 +1,33 @@
|
|||
use fluster_core::backend::render::web_canvas::WebCanvasRenderBackend;
|
||||
use js_sys::Uint8Array;
|
||||
use wasm_bindgen::{prelude::*, JsValue};
|
||||
use web_sys::HtmlCanvasElement;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct Player(fluster_core::Player);
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl Player {
|
||||
pub fn new(swf_data: Uint8Array) -> Result<Player, JsValue> {
|
||||
let mut data = vec![0; swf_data.length() as usize];
|
||||
swf_data.copy_to(&mut data[..]);
|
||||
|
||||
let player = fluster_core::Player::new(data).map_err(|_| JsValue::null())?;
|
||||
Ok(Player(player))
|
||||
pub fn new(canvas: HtmlCanvasElement, swf_data: Uint8Array) -> Result<Player, JsValue> {
|
||||
Player::new_internal(canvas, swf_data).map_err(|_| "Error creating player".into())
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, dt: f64) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Player } from "fluster";
|
||||
import { Player } from "../pkg/fluster";
|
||||
|
||||
let fileInput = document.getElementById("file-input");
|
||||
fileInput.addEventListener("change", fileSelected, false);
|
||||
|
@ -20,8 +20,7 @@ let timestamp = 0;
|
|||
function playSwf(swfData) {
|
||||
let canvas = document.getElementById("fluster-canvas");
|
||||
if (swfData && canvas) {
|
||||
let data = new Uint8Array(swfData);
|
||||
player = Player.new(data);
|
||||
player = Player.new(canvas, new Uint8Array(swfData));
|
||||
timestamp = performance.now();
|
||||
window.requestAnimationFrame(tickPlayer);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue