ruffle/core/src/movie_clip.rs

251 lines
8.9 KiB
Rust
Raw Normal View History

2019-04-30 08:53:21 +00:00
use crate::audio::AudioStreamHandle;
2019-04-29 05:55:44 +00:00
use crate::character::Character;
2019-04-26 21:11:29 +00:00
use crate::color_transform::ColorTransform;
2019-04-29 05:55:44 +00:00
use crate::display_object::{
DisplayObject, DisplayObjectBase, DisplayObjectImpl, DisplayObjectUpdate,
};
2019-04-25 17:52:22 +00:00
use crate::matrix::Matrix;
2019-04-27 01:55:06 +00:00
use crate::player::{RenderContext, UpdateContext};
2019-04-25 17:52:22 +00:00
use bacon_rajan_cc::{Cc, Trace, Tracer};
2019-04-27 01:55:06 +00:00
use log::info;
2019-04-25 17:52:22 +00:00
use std::cell::RefCell;
use std::collections::HashMap;
type Depth = i16;
type FrameNumber = u16;
pub struct MovieClip {
2019-04-29 05:55:44 +00:00
base: DisplayObjectBase,
2019-04-26 03:27:44 +00:00
tag_stream_start: Option<u64>,
tag_stream_pos: u64,
2019-04-25 17:52:22 +00:00
is_playing: bool,
current_frame: FrameNumber,
2019-04-26 03:27:44 +00:00
next_frame: FrameNumber,
2019-04-25 17:52:22 +00:00
total_frames: FrameNumber,
2019-04-30 08:53:21 +00:00
audio_stream: Option<AudioStreamHandle>,
2019-04-29 05:55:44 +00:00
children: HashMap<Depth, Cc<RefCell<DisplayObject>>>,
2019-04-25 17:52:22 +00:00
}
2019-04-29 05:55:44 +00:00
impl_display_object!(MovieClip, base);
2019-04-25 17:52:22 +00:00
impl MovieClip {
2019-04-29 05:55:44 +00:00
pub fn new() -> MovieClip {
MovieClip {
base: Default::default(),
2019-04-26 03:27:44 +00:00
tag_stream_start: None,
tag_stream_pos: 0,
2019-04-25 17:52:22 +00:00
is_playing: true,
2019-04-26 03:27:44 +00:00
current_frame: 0,
next_frame: 1,
2019-04-25 17:52:22 +00:00
total_frames: 1,
2019-04-30 08:53:21 +00:00
audio_stream: None,
2019-04-25 17:52:22 +00:00
children: HashMap::new(),
2019-04-29 05:55:44 +00:00
}
2019-04-25 17:52:22 +00:00
}
2019-04-26 03:27:44 +00:00
pub fn new_with_data(tag_stream_start: u64, num_frames: u16) -> MovieClip {
2019-04-25 17:52:22 +00:00
MovieClip {
2019-04-29 05:55:44 +00:00
base: Default::default(),
2019-04-26 03:27:44 +00:00
tag_stream_start: Some(tag_stream_start),
tag_stream_pos: tag_stream_start,
2019-04-25 17:52:22 +00:00
is_playing: true,
2019-04-26 03:27:44 +00:00
current_frame: 0,
next_frame: 1,
2019-04-30 08:53:21 +00:00
audio_stream: None,
2019-04-25 17:52:22 +00:00
total_frames: num_frames,
children: HashMap::new(),
}
}
2019-04-26 03:27:44 +00:00
pub fn run_place_object(
2019-04-29 05:55:44 +00:00
children: &mut HashMap<Depth, Cc<RefCell<DisplayObject>>>,
2019-04-26 03:27:44 +00:00
place_object: &swf::PlaceObject,
context: &mut UpdateContext,
) {
2019-04-25 17:52:22 +00:00
use swf::PlaceObjectAction;
2019-04-28 06:08:59 +00:00
let character = match place_object.action {
2019-04-25 17:52:22 +00:00
PlaceObjectAction::Place(id) => {
// TODO(Herschel): Behavior when character doesn't exist/isn't a DisplayObject?
2019-04-26 03:27:44 +00:00
let character =
2019-04-27 01:55:06 +00:00
if let Ok(character) = context.library.instantiate_display_object(id) {
2019-04-26 03:27:44 +00:00
Cc::new(RefCell::new(character))
} else {
return;
};
2019-04-25 17:52:22 +00:00
// TODO(Herschel): Behavior when depth is occupied? (I think it replaces)
2019-04-26 03:27:44 +00:00
children.insert(place_object.depth, character.clone());
character
}
PlaceObjectAction::Modify => {
if let Some(child) = children.get(&place_object.depth) {
child.clone()
} else {
return;
}
2019-04-25 17:52:22 +00:00
}
PlaceObjectAction::Replace(id) => {
2019-04-26 03:27:44 +00:00
let character =
2019-04-27 01:55:06 +00:00
if let Ok(character) = context.library.instantiate_display_object(id) {
2019-04-26 03:27:44 +00:00
Cc::new(RefCell::new(character))
} else {
return;
};
2019-04-25 17:52:22 +00:00
2019-04-26 03:27:44 +00:00
children.insert(place_object.depth, character.clone());
character
2019-04-25 17:52:22 +00:00
}
2019-04-26 03:27:44 +00:00
};
let mut character = character.borrow_mut();
if let Some(matrix) = &place_object.matrix {
let m = matrix.clone();
2019-04-29 05:55:44 +00:00
character.set_matrix(&Matrix::from(m));
2019-04-25 17:52:22 +00:00
}
}
2019-04-30 08:53:21 +00:00
fn sound_stream_head(
&mut self,
stream_info: &swf::SoundStreamInfo,
context: &mut UpdateContext,
_length: usize,
_version: u8,
) {
if self.audio_stream.is_none() {
self.audio_stream = Some(context.audio.register_stream(stream_info));
}
}
fn sound_stream_block(&mut self, samples: &[u8], context: &mut UpdateContext, _length: usize) {
if let Some(stream) = self.audio_stream {
context.audio.queue_stream_samples(stream, samples)
}
}
2019-04-25 17:52:22 +00:00
}
2019-04-29 05:55:44 +00:00
impl DisplayObjectUpdate for MovieClip {
2019-04-26 03:27:44 +00:00
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);
2019-04-29 05:55:44 +00:00
let mut start_pos = self.tag_stream_pos;
2019-04-26 03:27:44 +00:00
while let Ok(Some(tag)) = context.tag_stream.read_tag() {
//trace!("mc: {:?}", tag);
2019-04-25 17:52:22 +00:00
match tag {
2019-04-29 05:55:44 +00:00
Tag::SetBackgroundColor(color) => *context.background_color = color,
Tag::DefineShape(shape) => {
if !context.library.contains_character(shape.id) {
let shape_handle = context.renderer.register_shape(&shape);
context.library.register_character(
shape.id,
Character::Graphic {
shape_handle,
x_min: shape.shape_bounds.x_min,
y_min: shape.shape_bounds.y_min,
},
);
}
}
Tag::DefineSprite(sprite) => {
let pos = context.tag_stream.get_ref().position();
context.tag_stream.get_inner().set_position(start_pos);
context.tag_stream.read_tag_code_and_length().unwrap();
context.tag_stream.read_u32().unwrap();
let mc_start_pos = context.tag_stream.get_ref().position();
context.tag_stream.get_inner().set_position(pos);
if !context.library.contains_character(sprite.id) {
context.library.register_character(
sprite.id,
Character::MovieClip {
num_frames: sprite.num_frames,
tag_stream_start: mc_start_pos,
},
);
}
}
2019-04-25 17:52:22 +00:00
Tag::ShowFrame => break,
Tag::PlaceObject(place_object) => {
2019-04-26 03:27:44 +00:00
MovieClip::run_place_object(&mut self.children, &*place_object, context)
}
2019-04-27 01:55:06 +00:00
Tag::RemoveObject { depth, .. } => {
2019-04-26 03:27:44 +00:00
// TODO(Herschel): How does the character ID work for RemoveObject?
self.children.remove(&depth);
2019-04-25 17:52:22 +00:00
}
2019-04-26 03:27:44 +00:00
2019-04-30 08:53:21 +00:00
Tag::SoundStreamHead(info) => self.sound_stream_head(&info, context, 0, 1),
Tag::SoundStreamHead2(info) => self.sound_stream_head(&info, context, 0, 2),
Tag::SoundStreamBlock(samples) => {
self.sound_stream_block(&samples[..], context, 0)
}
2019-04-26 03:27:44 +00:00
Tag::JpegTables(_) => (),
Tag::DoAction(_) => (),
_ => info!("Umimplemented tag: {:?}", tag),
2019-04-25 17:52:22 +00:00
}
2019-04-29 05:55:44 +00:00
start_pos = context.tag_stream.get_ref().position();
2019-04-25 17:52:22 +00:00
}
2019-04-26 03:27:44 +00:00
self.tag_stream_pos = context.tag_stream.get_ref().position();
context
.tag_stream
.get_inner()
.set_position(context.position_stack.pop().unwrap());
2019-04-25 17:52:22 +00:00
// Advance frame number.
2019-04-26 03:27:44 +00:00
if self.next_frame < self.total_frames {
self.next_frame += 1;
2019-04-25 17:52:22 +00:00
} else {
2019-04-26 03:27:44 +00:00
self.next_frame = 1;
if let Some(start) = self.tag_stream_start {
self.tag_stream_pos = start;
}
2019-04-25 17:52:22 +00:00
}
}
// TODO(Herschel): Verify order of execution for parent/children.
// Parent first? Children first? Sorted by depth?
for child in self.children.values() {
2019-04-26 03:27:44 +00:00
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();
2019-04-25 17:52:22 +00:00
}
}
fn render(&self, context: &mut RenderContext) {
2019-04-29 05:55:44 +00:00
context.matrix_stack.push(self.get_matrix());
context
.color_transform_stack
.push(self.get_color_transform());
2019-04-25 17:52:22 +00:00
2019-04-26 03:27:44 +00:00
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);
2019-04-25 17:52:22 +00:00
}
context.matrix_stack.pop();
2019-04-26 21:11:29 +00:00
context.color_transform_stack.pop();
2019-04-25 17:52:22 +00:00
}
}
impl Trace for MovieClip {
fn trace(&mut self, tracer: &mut Tracer) {
for child in self.children.values_mut() {
child.trace(tracer);
}
}
}