ruffle/core/src/movie_clip.rs

396 lines
14 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;
2019-05-02 00:46:49 +00:00
use std::collections::{HashMap, VecDeque};
use swf::read::SwfRead;
2019-04-25 17:52:22 +00:00
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,
2019-05-02 00:46:49 +00:00
goto_queue: VecDeque<FrameNumber>,
2019-04-25 17:52:22 +00:00
current_frame: FrameNumber,
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-05-02 00:46:49 +00:00
goto_queue: VecDeque::new(),
2019-04-26 03:27:44 +00:00
current_frame: 0,
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-05-02 00:46:49 +00:00
goto_queue: VecDeque::new(),
2019-04-26 03:27:44 +00:00
current_frame: 0,
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-05-02 00:46:49 +00:00
pub fn playing(&self) -> bool {
self.is_playing
}
pub fn next_frame(&mut self) {
if self.current_frame + 1 <= self.total_frames {
self.goto_frame(self.current_frame + 1, true);
}
}
pub fn play(&mut self) {
self.is_playing = true;
}
pub fn prev_frame(&mut self) {
if self.current_frame > 1 {
self.goto_frame(self.current_frame - 1, true);
}
}
pub fn stop(&mut self) {
self.is_playing = false;
}
pub fn goto_frame(&mut self, frame: FrameNumber, stop: bool) {
self.goto_queue.push_back(frame);
if stop {
self.stop();
} else {
self.play();
}
}
fn run_goto_queue(&mut self, context: &mut UpdateContext) {
let mut i = 0;
while i < self.goto_queue.len() {
let frame = self.goto_queue[i];
if frame >= self.current_frame {
// Advancing
while self.current_frame + 1 < frame {
self.run_frame_internal(context, true);
}
self.run_frame_internal(context, false);
} else {
// Rewind
// Reset everything to blank, start from frame 1,
// and advance forward
self.children.clear();
self.tag_stream_pos = self.tag_stream_start.unwrap_or(0);
self.current_frame = 0;
while self.current_frame + 1 < frame {
self.run_frame_internal(context, true);
}
self.run_frame_internal(context, false);
}
i += 1;
}
self.goto_queue.clear();
}
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-05-01 16:55:54 +00:00
if let Some(prev_character) = children.insert(place_object.depth, character.clone())
{
character
.borrow_mut()
.set_matrix(prev_character.borrow().get_matrix());
character
.borrow_mut()
.set_color_transform(prev_character.borrow().get_color_transform());
}
2019-04-26 03:27:44 +00:00
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-05-01 16:55:54 +00:00
if let Some(color_transform) = &place_object.color_transform {
character.set_color_transform(&ColorTransform::from(color_transform.clone()));
}
2019-04-25 17:52:22 +00:00
}
2019-04-30 08:53:21 +00:00
2019-05-02 00:46:49 +00:00
fn do_action(&mut self, context: &mut UpdateContext, data: &[u8]) {
let mut action_context = crate::avm1::ActionContext {
global_time: context.global_time,
active_clip: self,
audio: context.audio,
};
if let Err(e) = context.avm1.do_action(&mut action_context, &data[..]) {}
2019-04-30 08:53:21 +00:00
}
2019-05-02 00:46:49 +00:00
fn run_frame_internal(&mut self, context: &mut UpdateContext, only_display_actions: bool) {
use swf::Tag;
// Advance frame number.
if self.current_frame < self.total_frames {
self.current_frame += 1;
} else {
self.current_frame = 1;
self.children.clear();
if let Some(start) = self.tag_stream_start {
self.tag_stream_pos = start;
}
2019-04-30 08:53:21 +00:00
}
2019-04-25 17:52:22 +00:00
2019-05-02 00:46:49 +00:00
context
.tag_stream
.get_inner()
.set_position(self.tag_stream_pos);
let mut start_pos = self.tag_stream_pos;
2019-04-26 03:27:44 +00:00
2019-05-02 00:46:49 +00:00
while let Ok(Some(tag)) = context.tag_stream.read_tag() {
if only_display_actions {
match tag {
Tag::ShowFrame => break,
Tag::PlaceObject(place_object) => {
MovieClip::run_place_object(&mut self.children, &*place_object, context)
}
Tag::RemoveObject { depth, .. } => {
// TODO(Herschel): How does the character ID work for RemoveObject?
self.children.remove(&depth);
}
// All non-display-list tags get ignored.
_ => (),
}
} else {
2019-04-25 17:52:22 +00:00
match tag {
// Definition Tags
2019-04-29 05:55:44 +00:00
Tag::SetBackgroundColor(color) => *context.background_color = color,
2019-05-02 05:34:02 +00:00
Tag::DefineButton2(button) => {
if !context.library.contains_character(button.id) {
context
.library
.register_character(button.id, Character::Button(button));
}
}
Tag::DefineBits { id, jpeg_data } => {
if !context.library.contains_character(id) {
let handle = context.renderer.register_bitmap_jpeg(
id,
&jpeg_data,
context.library.jpeg_tables().unwrap(),
);
context
.library
.register_character(id, Character::Bitmap(handle));
}
}
Tag::DefineBitsJpeg2 { id, jpeg_data } => {
if !context.library.contains_character(id) {
let handle = context.renderer.register_bitmap_jpeg_2(id, &jpeg_data);
context
.library
.register_character(id, Character::Bitmap(handle));
}
}
Tag::DefineBitsLossless(bitmap) => {
if !context.library.contains_character(bitmap.id) {
let handle = context.renderer.register_bitmap_png(&bitmap);
context
.library
.register_character(bitmap.id, Character::Bitmap(handle));
}
}
2019-04-29 05:55:44 +00:00
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,
},
);
}
}
Tag::JpegTables(data) => context.library.set_jpeg_tables(data),
2019-04-29 05:55:44 +00:00
// Control Tags
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(_) => (),
2019-05-02 00:46:49 +00:00
Tag::DoAction(data) => self.do_action(context, &data[..]),
2019-04-26 03:27:44 +00:00
_ => 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-05-02 00:46:49 +00:00
}
self.tag_stream_pos = context.tag_stream.get_ref().position();
}
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)
}
}
}
impl DisplayObjectUpdate for MovieClip {
fn run_frame(&mut self, context: &mut UpdateContext) {
if self.is_playing && self.tag_stream_start.is_some() {
context
.position_stack
.push(context.tag_stream.get_ref().position());
self.run_frame_internal(context, false);
// 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(context);
}
2019-04-26 03:27:44 +00:00
context
.tag_stream
.get_inner()
.set_position(context.position_stack.pop().unwrap());
}
}
2019-05-02 00:46:49 +00:00
fn run_post_frame(&mut self, context: &mut UpdateContext) {
self.run_goto_queue(context);
2019-04-26 03:27:44 +00:00
for child in self.children.values() {
2019-05-02 00:46:49 +00:00
child.borrow_mut().run_post_frame(context);
2019-04-25 17:52:22 +00:00
}
}
fn render(&self, context: &mut RenderContext) {
2019-05-01 16:55:54 +00:00
context.transform_stack.push(self.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
}
2019-05-01 16:55:54 +00:00
context.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);
}
}
}