Work
This commit is contained in:
parent
8d4ce5af8a
commit
975d31ac58
|
@ -16,6 +16,7 @@ console_error_panic_hook = { version = "0.1.1", optional = true }
|
|||
console_log = { version = "0.1", optional = true }
|
||||
js-sys = "0.3.19"
|
||||
log = "0.4"
|
||||
url = "1.7.2"
|
||||
svg = "0.5.12"
|
||||
swf = { path = "../swf-rs", version = "*" }
|
||||
wasm-bindgen = "0.2"
|
||||
|
|
|
@ -3,10 +3,12 @@ use web_sys::HtmlImageElement;
|
|||
pub enum Character {
|
||||
Graphic {
|
||||
image: HtmlImageElement,
|
||||
x_min: f32,
|
||||
y_min: f32,
|
||||
},
|
||||
MovieClip {
|
||||
num_frames: u16,
|
||||
tag_stream: swf::read::Reader<std::io::Cursor<Vec<u8>>>,
|
||||
tag_stream_start: u64,
|
||||
},
|
||||
Sound,
|
||||
}
|
||||
|
|
|
@ -1,24 +1,36 @@
|
|||
use crate::Library;
|
||||
use crate::RenderContext;
|
||||
use crate::{graphic::Graphic, MovieClip};
|
||||
use crate::Matrix;
|
||||
use crate::{graphic::Graphic, MovieClip, Stage};
|
||||
use crate::{RenderContext, UpdateContext};
|
||||
use bacon_rajan_cc::{Trace, Tracer};
|
||||
|
||||
pub trait DisplayObject {
|
||||
//fn children_gc_mut(&self) -> std::slice::Iter<&mut DisplayObjectNode>;
|
||||
fn run_frame(&mut self, library: &Library);
|
||||
fn run_frame(&mut self, context: &mut UpdateContext);
|
||||
fn update_frame_number(&mut self) {}
|
||||
fn render(&self, context: &mut RenderContext);
|
||||
fn set_matrix(&mut self, matrix: Matrix);
|
||||
}
|
||||
|
||||
pub enum DisplayObjectNode {
|
||||
Graphic(Graphic),
|
||||
MovieClip(MovieClip),
|
||||
Stage(Stage),
|
||||
}
|
||||
|
||||
impl DisplayObject for DisplayObjectNode {
|
||||
fn run_frame(&mut self, library: &Library) {
|
||||
fn run_frame(&mut self, context: &mut UpdateContext) {
|
||||
match self {
|
||||
DisplayObjectNode::Graphic(graphic) => graphic.run_frame(library),
|
||||
DisplayObjectNode::MovieClip(movie_clip) => movie_clip.run_frame(library),
|
||||
DisplayObjectNode::Graphic(graphic) => graphic.run_frame(context),
|
||||
DisplayObjectNode::MovieClip(movie_clip) => movie_clip.run_frame(context),
|
||||
DisplayObjectNode::Stage(stage) => stage.run_frame(context),
|
||||
}
|
||||
}
|
||||
|
||||
fn update_frame_number(&mut self) {
|
||||
match self {
|
||||
DisplayObjectNode::Graphic(graphic) => graphic.update_frame_number(),
|
||||
DisplayObjectNode::MovieClip(movie_clip) => movie_clip.update_frame_number(),
|
||||
DisplayObjectNode::Stage(stage) => stage.update_frame_number(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,6 +38,15 @@ impl DisplayObject for DisplayObjectNode {
|
|||
match self {
|
||||
DisplayObjectNode::Graphic(graphic) => graphic.render(context),
|
||||
DisplayObjectNode::MovieClip(movie_clip) => movie_clip.render(context),
|
||||
DisplayObjectNode::Stage(stage) => stage.render(context),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_matrix(&mut self, matrix: Matrix) {
|
||||
match self {
|
||||
DisplayObjectNode::Graphic(graphic) => graphic.set_matrix(matrix),
|
||||
DisplayObjectNode::MovieClip(movie_clip) => movie_clip.set_matrix(matrix),
|
||||
DisplayObjectNode::Stage(stage) => stage.set_matrix(matrix),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +56,7 @@ impl Trace for DisplayObjectNode {
|
|||
match self {
|
||||
DisplayObjectNode::Graphic(graphic) => graphic.trace(tracer),
|
||||
DisplayObjectNode::MovieClip(movie_clip) => movie_clip.trace(tracer),
|
||||
DisplayObjectNode::Stage(stage) => stage.trace(tracer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,31 @@
|
|||
use crate::display_object::DisplayObject;
|
||||
use crate::library::Library;
|
||||
use crate::Matrix;
|
||||
use crate::RenderContext;
|
||||
use crate::{RenderContext, UpdateContext};
|
||||
use bacon_rajan_cc::{Trace, Tracer};
|
||||
use log::{info, trace, warn};
|
||||
use web_sys::HtmlImageElement;
|
||||
|
||||
pub struct Graphic {
|
||||
matrix: Matrix,
|
||||
x_min: f32,
|
||||
y_min: f32,
|
||||
image: HtmlImageElement,
|
||||
}
|
||||
|
||||
impl Graphic {
|
||||
pub fn new(image: HtmlImageElement) -> Graphic {
|
||||
pub fn new(image: HtmlImageElement, x_min: f32, y_min: f32) -> Graphic {
|
||||
Graphic {
|
||||
image,
|
||||
x_min,
|
||||
y_min,
|
||||
matrix: std::default::Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DisplayObject for Graphic {
|
||||
fn run_frame(&mut self, _library: &Library) {
|
||||
fn run_frame(&mut self, _context: &mut UpdateContext) {
|
||||
// Noop
|
||||
}
|
||||
|
||||
|
@ -29,7 +34,7 @@ impl DisplayObject for Graphic {
|
|||
let world_matrix = context.matrix_stack.matrix();
|
||||
context
|
||||
.context_2d
|
||||
.transform(
|
||||
.set_transform(
|
||||
world_matrix.a.into(),
|
||||
world_matrix.b.into(),
|
||||
world_matrix.c.into(),
|
||||
|
@ -40,10 +45,14 @@ impl DisplayObject for Graphic {
|
|||
.unwrap();
|
||||
context
|
||||
.context_2d
|
||||
.draw_image_with_html_image_element(&self.image, 0.0, 0.0)
|
||||
.unwrap();
|
||||
.draw_image_with_html_image_element(&self.image, self.x_min.into(), self.y_min.into())
|
||||
.expect("Couldn't render image");
|
||||
context.matrix_stack.pop();
|
||||
}
|
||||
|
||||
fn set_matrix(&mut self, matrix: Matrix) {
|
||||
self.matrix = matrix;
|
||||
}
|
||||
}
|
||||
|
||||
impl Trace for Graphic {
|
||||
|
|
75
src/lib.rs
75
src/lib.rs
|
@ -5,12 +5,13 @@ mod library;
|
|||
mod matrix;
|
||||
mod movie_clip;
|
||||
mod shape_utils;
|
||||
mod stage;
|
||||
|
||||
use self::character::Character;
|
||||
use self::display_object::DisplayObject;
|
||||
use self::library::Library;
|
||||
use self::matrix::{Matrix, MatrixStack};
|
||||
use self::movie_clip::MovieClip;
|
||||
use self::stage::Stage;
|
||||
use bacon_rajan_cc::Cc;
|
||||
use js_sys::{ArrayBuffer, Uint8Array};
|
||||
use log::{info, trace, warn};
|
||||
|
@ -18,7 +19,6 @@ use std::cell::RefCell;
|
|||
use std::collections::HashMap;
|
||||
use std::io::Cursor;
|
||||
use std::rc::Rc;
|
||||
use swf::Color;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, HtmlImageElement};
|
||||
|
@ -28,17 +28,16 @@ type CharacterId = swf::CharacterId;
|
|||
#[wasm_bindgen]
|
||||
pub struct Player {
|
||||
tag_stream: swf::read::Reader<Cursor<Vec<u8>>>,
|
||||
|
||||
canvas: HtmlCanvasElement,
|
||||
render_context: RenderContext,
|
||||
|
||||
library: Library,
|
||||
root: Cc<RefCell<MovieClip>>,
|
||||
stage: Cc<RefCell<Stage>>,
|
||||
|
||||
frame_rate: f64,
|
||||
frame_accumulator: f64,
|
||||
cur_timestamp: f64,
|
||||
|
||||
background_color: Color,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
|
@ -66,15 +65,15 @@ impl Player {
|
|||
|
||||
Player {
|
||||
tag_stream,
|
||||
|
||||
canvas,
|
||||
|
||||
render_context: RenderContext {
|
||||
context_2d: context,
|
||||
matrix_stack: MatrixStack::new(),
|
||||
},
|
||||
|
||||
library: Library::new(),
|
||||
root: MovieClip::new(),
|
||||
stage: Stage::new(swf.num_frames),
|
||||
|
||||
frame_rate: swf.frame_rate.into(),
|
||||
frame_accumulator: 0.0,
|
||||
|
@ -83,12 +82,6 @@ impl Player {
|
|||
.performance()
|
||||
.expect("Expected performance")
|
||||
.now(),
|
||||
background_color: Color {
|
||||
r: 255,
|
||||
g: 255,
|
||||
b: 255,
|
||||
a: 255,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,48 +105,28 @@ impl Player {
|
|||
|
||||
impl Player {
|
||||
fn run_frame(&mut self) {
|
||||
use swf::Tag;
|
||||
let mut update_context = UpdateContext {
|
||||
tag_stream: &mut self.tag_stream,
|
||||
position_stack: vec![],
|
||||
library: &mut self.library,
|
||||
};
|
||||
|
||||
while let Ok(Some(tag)) = self.tag_stream.read_tag() {
|
||||
trace!("{:?}", tag);
|
||||
match tag {
|
||||
Tag::FileAttributes(file_attributes) => {}
|
||||
|
||||
Tag::SetBackgroundColor(color) => self.background_color = color,
|
||||
|
||||
Tag::ShowFrame => break,
|
||||
|
||||
Tag::DefineSceneAndFrameLabelData {
|
||||
scenes,
|
||||
frame_labels,
|
||||
} => (), // TODO(Herschel)
|
||||
|
||||
Tag::DefineShape(shape) => {
|
||||
if !self.library.contains_character(shape.id) {
|
||||
let svg = shape_utils::swf_shape_to_svg(&shape);
|
||||
info!("{}", svg);
|
||||
let mut image = HtmlImageElement::new().unwrap();
|
||||
image.set_src(&format!("data:image/svg+xml;utf8;{}", svg));
|
||||
self.library
|
||||
.register_character(shape.id, Character::Graphic { image });
|
||||
}
|
||||
}
|
||||
|
||||
//tag => self.root.borrow_mut().run_tag(tag)
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
let mut stage = self.stage.borrow_mut();
|
||||
stage.run_frame(&mut update_context);
|
||||
stage.update_frame_number();
|
||||
}
|
||||
|
||||
fn render(&mut self) {
|
||||
let background_color = format!(
|
||||
let stage = self.stage.borrow_mut();
|
||||
let background_color = stage.background_color();
|
||||
let css_color = format!(
|
||||
"rgb({}, {}, {})",
|
||||
self.background_color.r, self.background_color.g, self.background_color.b
|
||||
background_color.r, background_color.g, background_color.b
|
||||
);
|
||||
info!("{:?}", background_color);
|
||||
self.render_context.context_2d.reset_transform().unwrap();
|
||||
self.render_context
|
||||
.context_2d
|
||||
.set_fill_style(&background_color.into());
|
||||
.set_fill_style(&format!("{}", css_color).into());
|
||||
|
||||
let width: f64 = self.canvas.width().into();
|
||||
let height: f64 = self.canvas.height().into();
|
||||
|
@ -162,10 +135,16 @@ impl Player {
|
|||
.context_2d
|
||||
.fill_rect(0.0, 0.0, width, height);
|
||||
|
||||
self.root.borrow_mut().render(&mut self.render_context);
|
||||
stage.render(&mut self.render_context);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UpdateContext<'a> {
|
||||
tag_stream: &'a mut swf::read::Reader<Cursor<Vec<u8>>>,
|
||||
position_stack: Vec<u64>,
|
||||
library: &'a mut Library,
|
||||
}
|
||||
|
||||
pub struct RenderContext {
|
||||
context_2d: CanvasRenderingContext2d,
|
||||
matrix_stack: MatrixStack,
|
||||
|
|
|
@ -30,14 +30,20 @@ impl Library {
|
|||
id: CharacterId,
|
||||
) -> Result<DisplayObjectNode, Box<std::error::Error>> {
|
||||
match self.characters.get(&id) {
|
||||
Some(Character::Graphic { image }) => {
|
||||
Ok(DisplayObjectNode::Graphic(Graphic::new(image.clone())))
|
||||
}
|
||||
Some(Character::Graphic {
|
||||
image,
|
||||
x_min,
|
||||
y_min,
|
||||
}) => Ok(DisplayObjectNode::Graphic(Graphic::new(
|
||||
image.clone(),
|
||||
*x_min,
|
||||
*y_min,
|
||||
))),
|
||||
Some(Character::MovieClip {
|
||||
tag_stream,
|
||||
tag_stream_start,
|
||||
num_frames,
|
||||
}) => Ok(DisplayObjectNode::MovieClip(MovieClip::new_with_data(
|
||||
tag_stream.clone(),
|
||||
*tag_stream_start,
|
||||
*num_frames,
|
||||
))),
|
||||
Some(_) => Err("Not a DisplayObject".into()),
|
||||
|
|
|
@ -12,8 +12,8 @@ impl From<swf::Matrix> for Matrix {
|
|||
fn from(matrix: swf::Matrix) -> Matrix {
|
||||
Matrix {
|
||||
a: matrix.scale_x,
|
||||
b: matrix.rotate_skew_1,
|
||||
c: matrix.rotate_skew_0,
|
||||
b: matrix.rotate_skew_0,
|
||||
c: matrix.rotate_skew_1,
|
||||
d: matrix.scale_y,
|
||||
tx: matrix.translate_x,
|
||||
ty: matrix.translate_y,
|
||||
|
@ -69,7 +69,7 @@ impl MatrixStack {
|
|||
}
|
||||
|
||||
pub fn push(&mut self, matrix: &Matrix) {
|
||||
let new_matrix = *matrix * *self.matrix();
|
||||
let new_matrix = *self.matrix() * *matrix;
|
||||
self.0.push(new_matrix);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::display_object::{DisplayObject, DisplayObjectNode};
|
||||
use crate::matrix::Matrix;
|
||||
use crate::Library;
|
||||
use crate::RenderContext;
|
||||
use crate::{RenderContext, UpdateContext};
|
||||
use bacon_rajan_cc::{Cc, Trace, Tracer};
|
||||
use log::{info, trace, warn};
|
||||
use std::cell::RefCell;
|
||||
|
@ -12,10 +12,12 @@ type Depth = i16;
|
|||
type FrameNumber = u16;
|
||||
|
||||
pub struct MovieClip {
|
||||
tag_stream: Option<swf::read::Reader<Cursor<Vec<u8>>>>,
|
||||
tag_stream_start: Option<u64>,
|
||||
tag_stream_pos: u64,
|
||||
is_playing: bool,
|
||||
matrix: Matrix,
|
||||
current_frame: FrameNumber,
|
||||
next_frame: FrameNumber,
|
||||
total_frames: FrameNumber,
|
||||
children: HashMap<Depth, Cc<RefCell<DisplayObjectNode>>>,
|
||||
}
|
||||
|
@ -23,106 +25,164 @@ pub struct MovieClip {
|
|||
impl MovieClip {
|
||||
pub fn new() -> Cc<RefCell<MovieClip>> {
|
||||
let clip = MovieClip {
|
||||
tag_stream: None,
|
||||
tag_stream_start: None,
|
||||
tag_stream_pos: 0,
|
||||
is_playing: true,
|
||||
matrix: Matrix::default(),
|
||||
current_frame: 1,
|
||||
current_frame: 0,
|
||||
next_frame: 1,
|
||||
total_frames: 1,
|
||||
children: HashMap::new(),
|
||||
};
|
||||
Cc::new(RefCell::new(clip))
|
||||
}
|
||||
|
||||
pub fn new_with_data(
|
||||
tag_stream: swf::read::Reader<Cursor<Vec<u8>>>,
|
||||
num_frames: u16,
|
||||
) -> MovieClip {
|
||||
pub fn new_with_data(tag_stream_start: u64, num_frames: u16) -> MovieClip {
|
||||
info!("start: {} ", tag_stream_start);
|
||||
MovieClip {
|
||||
tag_stream: Some(tag_stream),
|
||||
tag_stream_start: Some(tag_stream_start),
|
||||
tag_stream_pos: tag_stream_start,
|
||||
is_playing: true,
|
||||
matrix: Matrix::default(),
|
||||
current_frame: 1,
|
||||
current_frame: 0,
|
||||
next_frame: 1,
|
||||
total_frames: num_frames,
|
||||
children: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_place_object(&mut self, place_object: &swf::PlaceObject, library: &Library) {
|
||||
pub fn run_place_object(
|
||||
children: &mut HashMap<Depth, Cc<RefCell<DisplayObjectNode>>>,
|
||||
place_object: &swf::PlaceObject,
|
||||
context: &mut UpdateContext,
|
||||
) {
|
||||
use swf::PlaceObjectAction;
|
||||
match place_object.action {
|
||||
let mut character = match place_object.action {
|
||||
PlaceObjectAction::Place(id) => {
|
||||
// TODO(Herschel): Behavior when character doesn't exist/isn't a DisplayObject?
|
||||
let mut character = library.instantiate_display_object(id).unwrap();
|
||||
let character =
|
||||
if let Ok(mut character) = context.library.instantiate_display_object(id) {
|
||||
Cc::new(RefCell::new(character))
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// TODO(Herschel): Behavior when depth is occupied? (I think it replaces)
|
||||
self.children
|
||||
.insert(place_object.depth, Cc::new(RefCell::new(character)));
|
||||
children.insert(place_object.depth, character.clone());
|
||||
character
|
||||
}
|
||||
PlaceObjectAction::Modify => {
|
||||
if let Some(child) = children.get(&place_object.depth) {
|
||||
child.clone()
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
PlaceObjectAction::Modify => (),
|
||||
PlaceObjectAction::Replace(id) => {
|
||||
let mut character = library.instantiate_display_object(id).unwrap();
|
||||
let character =
|
||||
if let Ok(mut character) = context.library.instantiate_display_object(id) {
|
||||
Cc::new(RefCell::new(character))
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// TODO(Herschel): Behavior when depth is occupied? (I think it replaces)
|
||||
self.children
|
||||
.insert(place_object.depth, Cc::new(RefCell::new(character)));
|
||||
children.insert(place_object.depth, character.clone());
|
||||
character
|
||||
}
|
||||
};
|
||||
|
||||
let mut character = character.borrow_mut();
|
||||
if let Some(matrix) = &place_object.matrix {
|
||||
let m = matrix.clone();
|
||||
character.set_matrix(Matrix::from(m));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DisplayObject for MovieClip {
|
||||
fn run_frame(&mut self, library: &Library) {
|
||||
use swf::Tag;
|
||||
if self.tag_stream.is_some() {
|
||||
while let Ok(Some(tag)) = self.tag_stream.as_mut().unwrap().read_tag() {
|
||||
trace!("{:?}", tag);
|
||||
fn run_frame(&mut self, context: &mut UpdateContext) {
|
||||
use swf::{read::SwfRead, Tag};
|
||||
if self.tag_stream_start.is_some() {
|
||||
context
|
||||
.position_stack
|
||||
.push(context.tag_stream.get_ref().position());
|
||||
context
|
||||
.tag_stream
|
||||
.get_inner()
|
||||
.set_position(self.tag_stream_pos);
|
||||
|
||||
while let Ok(Some(tag)) = context.tag_stream.read_tag() {
|
||||
//trace!("mc: {:?}", tag);
|
||||
match tag {
|
||||
Tag::ShowFrame => break,
|
||||
Tag::PlaceObject(place_object) => {
|
||||
self.run_place_object(&*place_object, library)
|
||||
MovieClip::run_place_object(&mut self.children, &*place_object, context)
|
||||
}
|
||||
_ => unimplemented!("Umimplemented tag: {:?}", tag),
|
||||
Tag::RemoveObject {
|
||||
depth,
|
||||
character_id,
|
||||
} => {
|
||||
// TODO(Herschel): How does the character ID work for RemoveObject?
|
||||
self.children.remove(&depth);
|
||||
info!("REMOVE {} {}", depth, self.children.len());
|
||||
}
|
||||
|
||||
Tag::JpegTables(_) => (),
|
||||
Tag::SoundStreamHead(_) => (),
|
||||
Tag::SoundStreamHead2(_) => (),
|
||||
Tag::SoundStreamBlock(_) => (),
|
||||
Tag::DoAction(_) => (),
|
||||
_ => info!("Umimplemented tag: {:?}", tag),
|
||||
}
|
||||
}
|
||||
self.tag_stream_pos = context.tag_stream.get_ref().position();
|
||||
context
|
||||
.tag_stream
|
||||
.get_inner()
|
||||
.set_position(context.position_stack.pop().unwrap());
|
||||
|
||||
// Advance frame number.
|
||||
self.current_frame += 1;
|
||||
if self.current_frame < self.total_frames {
|
||||
self.current_frame += 1;
|
||||
if self.next_frame < self.total_frames {
|
||||
self.next_frame += 1;
|
||||
} else {
|
||||
self.current_frame = 1;
|
||||
//tag_stream.to_inner().set_position(0);
|
||||
self.next_frame = 1;
|
||||
if let Some(start) = self.tag_stream_start {
|
||||
self.tag_stream_pos = start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(Herschel): Verify order of execution for parent/children.
|
||||
// Parent first? Children first? Sorted by depth?
|
||||
for child in self.children.values() {
|
||||
child.borrow_mut().run_frame(library);
|
||||
child.borrow_mut().run_frame(context);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_frame_number(&mut self) {
|
||||
self.current_frame = self.next_frame;
|
||||
for child in self.children.values() {
|
||||
child.borrow_mut().update_frame_number();
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&self, context: &mut RenderContext) {
|
||||
context.matrix_stack.push(&self.matrix);
|
||||
let world_matrix = context.matrix_stack.matrix();
|
||||
context
|
||||
.context_2d
|
||||
.transform(
|
||||
world_matrix.a.into(),
|
||||
world_matrix.b.into(),
|
||||
world_matrix.c.into(),
|
||||
world_matrix.d.into(),
|
||||
world_matrix.tx.into(),
|
||||
world_matrix.ty.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for child in self.children.values() {
|
||||
child.borrow_mut().render(context);
|
||||
let mut sorted_children: Vec<_> = self.children.iter().collect();
|
||||
sorted_children.sort_by_key(|(depth, _)| *depth);
|
||||
|
||||
for child in sorted_children {
|
||||
child.1.borrow_mut().render(context);
|
||||
}
|
||||
|
||||
context.matrix_stack.pop();
|
||||
}
|
||||
|
||||
fn set_matrix(&mut self, matrix: Matrix) {
|
||||
self.matrix = matrix;
|
||||
}
|
||||
}
|
||||
|
||||
impl Trace for MovieClip {
|
||||
|
|
|
@ -1,23 +1,27 @@
|
|||
use crate::Matrix;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use svg::node::element::{path::Data, Definitions, LinearGradient, Path as SvgPath, Stop};
|
||||
use svg::node::element::{
|
||||
path::Data, Definitions, Image, LinearGradient, Path as SvgPath, Pattern, RadialGradient, Stop,
|
||||
};
|
||||
use svg::Document;
|
||||
use swf::{Color, FillStyle, LineStyle, Shape};
|
||||
|
||||
pub fn swf_shape_to_svg(shape: &Shape) -> String {
|
||||
//let mut svg = String::new();
|
||||
let (width, height) = (
|
||||
shape.shape_bounds.x_max - shape.shape_bounds.x_min,
|
||||
shape.shape_bounds.y_max - shape.shape_bounds.y_min,
|
||||
);
|
||||
let mut document = Document::new()
|
||||
.set("width", shape.shape_bounds.x_max - shape.shape_bounds.x_min)
|
||||
.set(
|
||||
"height",
|
||||
shape.shape_bounds.y_max - shape.shape_bounds.y_min,
|
||||
)
|
||||
.set("width", width)
|
||||
.set("height", height)
|
||||
.set(
|
||||
"viewBox",
|
||||
(
|
||||
shape.shape_bounds.x_min,
|
||||
shape.shape_bounds.y_min,
|
||||
shape.shape_bounds.x_max - shape.shape_bounds.x_min,
|
||||
shape.shape_bounds.y_max - shape.shape_bounds.y_min,
|
||||
width,
|
||||
height,
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -33,19 +37,34 @@ pub fn swf_shape_to_svg(shape: &Shape) -> String {
|
|||
match path.fill_style {
|
||||
FillStyle::Color(Color { r, g, b, a }) => format!("rgba({},{},{},{})", r, g, b, a),
|
||||
FillStyle::LinearGradient(gradient) => {
|
||||
let x1 = 0.0;
|
||||
let y1 = 0.0;
|
||||
let x2 = 1.0;
|
||||
let y2 = 1.0;
|
||||
let matrix = Matrix::from(gradient.matrix);
|
||||
let shift = Matrix {
|
||||
a: 1638.4 / width,
|
||||
d: 1638.4 / height,
|
||||
tx: -819.2,
|
||||
ty: -819.2,
|
||||
..Default::default()
|
||||
};
|
||||
let gradient_matrix = matrix * shift;
|
||||
|
||||
let mut svg_gradient = LinearGradient::new()
|
||||
.set("id", format!("f{}", num_defs))
|
||||
.set("x1", x1)
|
||||
.set("y1", y1)
|
||||
.set("x2", x2)
|
||||
.set("y2", y2);
|
||||
.set("gradientUnits", "userSpaceOnUse")
|
||||
.set(
|
||||
"gradientTransform",
|
||||
format!(
|
||||
"matrix({} {} {} {} {} {})",
|
||||
gradient_matrix.a,
|
||||
gradient_matrix.b,
|
||||
gradient_matrix.c,
|
||||
gradient_matrix.d,
|
||||
gradient_matrix.tx,
|
||||
gradient_matrix.ty
|
||||
),
|
||||
);
|
||||
for record in &gradient.records {
|
||||
let stop = Stop::new()
|
||||
.set("offset", format!("{}%", f32::from(record.ratio) / 255.0))
|
||||
.set("offset", format!("{}%", f32::from(record.ratio) / 2.55))
|
||||
.set(
|
||||
"stop-color",
|
||||
format!(
|
||||
|
@ -61,7 +80,116 @@ pub fn swf_shape_to_svg(shape: &Shape) -> String {
|
|||
num_defs += 1;
|
||||
fill_id
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
FillStyle::RadialGradient(gradient) => {
|
||||
let matrix = Matrix::from(gradient.matrix);
|
||||
let shift = Matrix {
|
||||
a: 1638.4 / width,
|
||||
d: 1638.4 / height,
|
||||
tx: -819.2,
|
||||
ty: -819.2,
|
||||
..Default::default()
|
||||
};
|
||||
let gradient_matrix = matrix * shift;
|
||||
|
||||
let mut svg_gradient = RadialGradient::new()
|
||||
.set("id", format!("f{}", num_defs))
|
||||
.set("gradientUnits", "userSpaceOnUse")
|
||||
.set(
|
||||
"gradientTransform",
|
||||
format!(
|
||||
"matrix({} {} {} {} {} {})",
|
||||
gradient_matrix.a,
|
||||
gradient_matrix.b,
|
||||
gradient_matrix.c,
|
||||
gradient_matrix.d,
|
||||
gradient_matrix.tx,
|
||||
gradient_matrix.ty
|
||||
),
|
||||
);
|
||||
for record in &gradient.records {
|
||||
let stop = Stop::new()
|
||||
.set("offset", format!("{}%", f32::from(record.ratio) / 2.55))
|
||||
.set(
|
||||
"stop-color",
|
||||
format!(
|
||||
"rgba({},{},{},{})",
|
||||
record.color.r, record.color.g, record.color.b, record.color.a
|
||||
),
|
||||
);
|
||||
svg_gradient = svg_gradient.add(stop);
|
||||
}
|
||||
defs = defs.add(svg_gradient);
|
||||
|
||||
let fill_id = format!("url(#f{})", num_defs);
|
||||
num_defs += 1;
|
||||
fill_id
|
||||
}
|
||||
FillStyle::FocalGradient {
|
||||
gradient,
|
||||
focal_point,
|
||||
} => {
|
||||
let matrix = Matrix::from(gradient.matrix);
|
||||
let shift = Matrix {
|
||||
a: 1638.4 / width,
|
||||
d: 1638.4 / height,
|
||||
tx: -819.2,
|
||||
ty: -819.2,
|
||||
..Default::default()
|
||||
};
|
||||
let gradient_matrix = matrix * shift;
|
||||
|
||||
let mut svg_gradient = RadialGradient::new()
|
||||
.set("id", format!("f{}", num_defs))
|
||||
.set("fx", -focal_point)
|
||||
.set("gradientUnits", "userSpaceOnUse")
|
||||
.set(
|
||||
"gradientTransform",
|
||||
format!(
|
||||
"matrix({} {} {} {} {} {})",
|
||||
gradient_matrix.a,
|
||||
gradient_matrix.b,
|
||||
gradient_matrix.c,
|
||||
gradient_matrix.d,
|
||||
gradient_matrix.tx,
|
||||
gradient_matrix.ty
|
||||
),
|
||||
);
|
||||
for record in &gradient.records {
|
||||
let stop = Stop::new()
|
||||
.set("offset", format!("{}%", f32::from(record.ratio) / 2.55))
|
||||
.set(
|
||||
"stop-color",
|
||||
format!(
|
||||
"rgba({},{},{},{})",
|
||||
record.color.r, record.color.g, record.color.b, record.color.a
|
||||
),
|
||||
);
|
||||
svg_gradient = svg_gradient.add(stop);
|
||||
}
|
||||
defs = defs.add(svg_gradient);
|
||||
|
||||
let fill_id = format!("url(#f{})", num_defs);
|
||||
num_defs += 1;
|
||||
fill_id
|
||||
}
|
||||
FillStyle::Bitmap {
|
||||
id,
|
||||
matrix,
|
||||
is_smoothed,
|
||||
is_repeating,
|
||||
} => {
|
||||
let svg_image = Image::new(); // TODO: .set("xlink:href", "");
|
||||
|
||||
let svg_pattern = Pattern::new()
|
||||
.set("id", format!("f{}", num_defs))
|
||||
.add(svg_image);
|
||||
|
||||
defs = defs.add(svg_pattern);
|
||||
|
||||
let fill_id = format!("url(#f{})", num_defs);
|
||||
num_defs += 1;
|
||||
fill_id
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -574,7 +574,7 @@
|
|||
},
|
||||
"browserify-aes": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
|
||||
"integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -611,7 +611,7 @@
|
|||
},
|
||||
"browserify-rsa": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
|
||||
"integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -645,7 +645,7 @@
|
|||
},
|
||||
"buffer": {
|
||||
"version": "4.9.1",
|
||||
"resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
|
||||
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -1075,7 +1075,7 @@
|
|||
},
|
||||
"create-hash": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
||||
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -1088,7 +1088,7 @@
|
|||
},
|
||||
"create-hmac": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
|
||||
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
|
||||
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -1347,7 +1347,7 @@
|
|||
},
|
||||
"diffie-hellman": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
|
||||
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
|
||||
"integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -1521,7 +1521,7 @@
|
|||
},
|
||||
"events": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "http://registry.npmjs.org/events/-/events-3.0.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz",
|
||||
"integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -1964,14 +1964,14 @@
|
|||
"dev": true
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz",
|
||||
"integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==",
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.8.tgz",
|
||||
"integrity": "sha512-tPvHgPGB7m40CZ68xqFGkKuzN+RnpGmSV+hgeKxhRpbxdqKXUFJGC3yonBOLzQBcJyGpdZFDfCsdOC2KFsXzeA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"nan": "^2.9.2",
|
||||
"node-pre-gyp": "^0.10.0"
|
||||
"nan": "^2.12.1",
|
||||
"node-pre-gyp": "^0.12.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"abbrev": {
|
||||
|
@ -2043,12 +2043,12 @@
|
|||
"optional": true
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"version": "4.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"deep-extend": {
|
||||
|
@ -2213,24 +2213,31 @@
|
|||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.13.2",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz",
|
||||
"integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"needle": {
|
||||
"version": "2.2.4",
|
||||
"version": "2.3.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"debug": "^2.1.2",
|
||||
"debug": "^4.1.0",
|
||||
"iconv-lite": "^0.4.4",
|
||||
"sax": "^1.2.4"
|
||||
}
|
||||
},
|
||||
"node-pre-gyp": {
|
||||
"version": "0.10.3",
|
||||
"version": "0.12.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
|
@ -2258,13 +2265,13 @@
|
|||
}
|
||||
},
|
||||
"npm-bundled": {
|
||||
"version": "1.0.5",
|
||||
"version": "1.0.6",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"npm-packlist": {
|
||||
"version": "1.2.0",
|
||||
"version": "1.4.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
|
@ -2400,7 +2407,7 @@
|
|||
"optional": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.6.0",
|
||||
"version": "5.7.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
|
@ -3783,7 +3790,7 @@
|
|||
},
|
||||
"parse-asn1": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz",
|
||||
"resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz",
|
||||
"integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -4380,7 +4387,7 @@
|
|||
},
|
||||
"sha.js": {
|
||||
"version": "2.4.11",
|
||||
"resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
|
||||
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
|
||||
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -5353,12 +5360,14 @@
|
|||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -5378,7 +5387,8 @@
|
|||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
|
@ -5526,6 +5536,7 @@
|
|||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
|
|
@ -27,10 +27,10 @@
|
|||
},
|
||||
"homepage": "https://github.com/rustwasm/create-wasm-app#readme",
|
||||
"devDependencies": {
|
||||
"copy-webpack-plugin": "^5.0.0",
|
||||
"hello-wasm-pack": "^0.1.0",
|
||||
"webpack": "^4.29.3",
|
||||
"webpack": "^4.30.0",
|
||||
"webpack-cli": "^3.1.0",
|
||||
"webpack-dev-server": "^3.1.5",
|
||||
"copy-webpack-plugin": "^5.0.0"
|
||||
"webpack-dev-server": "^3.1.5"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue