Avm2: Implement Stage.invalidate
This commit is contained in:
parent
a818dd0918
commit
37ec94f95b
|
@ -66,7 +66,7 @@ pub use crate::avm2::value::Value;
|
|||
|
||||
use self::scope::Scope;
|
||||
|
||||
const BROADCAST_WHITELIST: [&str; 3] = ["enterFrame", "exitFrame", "frameConstructed"];
|
||||
const BROADCAST_WHITELIST: [&str; 4] = ["enterFrame", "exitFrame", "frameConstructed", "render"];
|
||||
|
||||
/// The state of an AVM2 interpreter.
|
||||
#[derive(Collect)]
|
||||
|
|
|
@ -726,6 +726,21 @@ pub fn stage3ds<'gc>(
|
|||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Implement `invalidate`
|
||||
pub fn invalidate<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(stage) = this
|
||||
.and_then(|this| this.as_display_object())
|
||||
.and_then(|this| this.as_stage())
|
||||
{
|
||||
stage.set_invalidated(activation.context.gc_context, true);
|
||||
}
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Stage.fullScreenSourceRect's getter
|
||||
pub fn full_screen_source_rect<'gc>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
|
@ -863,5 +878,8 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
|
|||
];
|
||||
write.define_public_builtin_instance_properties(mc, PUBLIC_INSTANCE_PROPERTIES);
|
||||
|
||||
const PUBLIC_INSTANCE_METHODS: &[(&str, NativeMethodImpl)] = &[("invalidate", invalidate)];
|
||||
write.define_public_builtin_instance_methods(mc, PUBLIC_INSTANCE_METHODS);
|
||||
|
||||
class
|
||||
}
|
||||
|
|
|
@ -90,6 +90,9 @@ pub struct StageData<'gc> {
|
|||
/// The alignment of the stage.
|
||||
align: StageAlign,
|
||||
|
||||
/// Whether or not a RENDER event should be dispatched on the next render
|
||||
invalidated: bool,
|
||||
|
||||
/// Whether to use high quality downsampling for bitmaps.
|
||||
///
|
||||
/// This is usally implied by `quality` being `Best` or higher, but the AVM1
|
||||
|
@ -149,6 +152,7 @@ impl<'gc> Stage<'gc> {
|
|||
} else {
|
||||
StageDisplayState::Normal
|
||||
},
|
||||
invalidated: false,
|
||||
align: Default::default(),
|
||||
use_bitmap_downsampling: false,
|
||||
view_bounds: Default::default(),
|
||||
|
@ -210,6 +214,16 @@ impl<'gc> Stage<'gc> {
|
|||
self.0.write(gc_context).loader_info = loader_info;
|
||||
}
|
||||
|
||||
// Get the invalidation state
|
||||
pub fn invalidated(self) -> bool {
|
||||
self.0.read().invalidated
|
||||
}
|
||||
|
||||
// Set the invalidation state
|
||||
pub fn set_invalidated(self, gc_context: MutationContext<'gc, '_>, value: bool) {
|
||||
self.0.write(gc_context).invalidated = value;
|
||||
}
|
||||
|
||||
/// Returns the quality setting of the stage.
|
||||
///
|
||||
/// In the Flash Player, the quality setting affects anti-aliasing and smoothing of bitmaps.
|
||||
|
@ -617,6 +631,25 @@ impl<'gc> Stage<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Broadcast the 'render' event
|
||||
///
|
||||
/// TODO: Need additional check as Flash Player does not
|
||||
/// broadcast the 'render' event on the first render
|
||||
pub fn broadcast_render(&self, context: &mut UpdateContext<'_, 'gc>) {
|
||||
let render_evt = Avm2EventObject::bare_default_event(context, "render");
|
||||
|
||||
let dobject_constr = context.avm2.classes().display_object;
|
||||
|
||||
if let Err(e) = Avm2::broadcast_event(context, render_evt, dobject_constr) {
|
||||
tracing::error!(
|
||||
"Encountered AVM2 error when broadcasting render event: {}",
|
||||
e
|
||||
);
|
||||
}
|
||||
|
||||
self.set_invalidated(context.gc_context, false);
|
||||
}
|
||||
|
||||
/// Fires `Stage.onFullScreen` in AVM1 or `Event.FULLSCREEN` in AVM2.
|
||||
pub fn fire_fullscreen_event(self, context: &mut UpdateContext<'_, 'gc>) {
|
||||
if !context.is_action_script_3() {
|
||||
|
|
|
@ -1481,6 +1481,17 @@ impl Player {
|
|||
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub fn render(&mut self) {
|
||||
let invalidated = self
|
||||
.gc_arena
|
||||
.borrow()
|
||||
.mutate(|_, gc_root| gc_root.data.read().stage.invalidated());
|
||||
if invalidated {
|
||||
self.update(|context| {
|
||||
let stage = context.stage;
|
||||
stage.broadcast_render(context);
|
||||
});
|
||||
}
|
||||
|
||||
let (renderer, ui, transform_stack) =
|
||||
(&mut self.renderer, &mut self.ui, &mut self.transform_stack);
|
||||
let mut background_color = Color::WHITE;
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package {
|
||||
import flash.display.Sprite;
|
||||
import flash.events.Event;
|
||||
|
||||
public class Test extends Sprite {
|
||||
private var test_stage: int = 0;
|
||||
|
||||
public function Test() {
|
||||
addEventListener(Event.ENTER_FRAME, enterFrameListener);
|
||||
addEventListener(Event.FRAME_CONSTRUCTED, frameConstructedListener);
|
||||
addEventListener(Event.EXIT_FRAME, exitFrameListener);
|
||||
addEventListener(Event.RENDER, renderListener);
|
||||
test_stage = 0;
|
||||
}
|
||||
|
||||
public function enterFrameListener(e: Event) : void {
|
||||
trace("ENTER_FRAME ( test_stage = ", test_stage, " )");
|
||||
if ((test_stage == 2) || (test_stage == 14) || (test_stage == 17)) {
|
||||
trace("Invalidate called inside enterFrameListener");
|
||||
stage.invalidate();
|
||||
}
|
||||
test_stage = (test_stage + 1) % 21;
|
||||
}
|
||||
|
||||
public function frameConstructedListener(e: Event) : void {
|
||||
trace("FRAME_CONSTRUCTED ( test_stage = ", test_stage, " )");
|
||||
if ((test_stage == 6) || (test_stage == 12) || (test_stage == 15)) {
|
||||
trace("Invalidate called inside frameConstructedListener");
|
||||
stage.invalidate();
|
||||
}
|
||||
test_stage = (test_stage + 1) % 21;
|
||||
}
|
||||
|
||||
public function exitFrameListener(e: Event) : void {
|
||||
trace("EXIT_FRAME ( test_stage = ", test_stage, " )");
|
||||
if ((test_stage == 10) || (test_stage == 13) || (test_stage == 19)) {
|
||||
trace("Invalidate called inside exitFrameListener");
|
||||
stage.invalidate();
|
||||
}
|
||||
test_stage = (test_stage + 1) % 21;
|
||||
}
|
||||
|
||||
public function renderListener(e: Event) : void {
|
||||
trace("RENDER ( test_stage = ", test_stage, " )");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
FRAME_CONSTRUCTED ( test_stage = 0 )
|
||||
EXIT_FRAME ( test_stage = 1 )
|
||||
ENTER_FRAME ( test_stage = 2 )
|
||||
Invalidate called inside enterFrameListener
|
||||
FRAME_CONSTRUCTED ( test_stage = 3 )
|
||||
EXIT_FRAME ( test_stage = 4 )
|
||||
RENDER ( test_stage = 5 )
|
||||
ENTER_FRAME ( test_stage = 5 )
|
||||
FRAME_CONSTRUCTED ( test_stage = 6 )
|
||||
Invalidate called inside frameConstructedListener
|
||||
EXIT_FRAME ( test_stage = 7 )
|
||||
RENDER ( test_stage = 8 )
|
||||
ENTER_FRAME ( test_stage = 8 )
|
||||
FRAME_CONSTRUCTED ( test_stage = 9 )
|
||||
EXIT_FRAME ( test_stage = 10 )
|
||||
Invalidate called inside exitFrameListener
|
||||
RENDER ( test_stage = 11 )
|
||||
ENTER_FRAME ( test_stage = 11 )
|
||||
FRAME_CONSTRUCTED ( test_stage = 12 )
|
||||
Invalidate called inside frameConstructedListener
|
||||
EXIT_FRAME ( test_stage = 13 )
|
||||
Invalidate called inside exitFrameListener
|
||||
RENDER ( test_stage = 14 )
|
||||
ENTER_FRAME ( test_stage = 14 )
|
||||
Invalidate called inside enterFrameListener
|
||||
FRAME_CONSTRUCTED ( test_stage = 15 )
|
||||
Invalidate called inside frameConstructedListener
|
||||
EXIT_FRAME ( test_stage = 16 )
|
||||
RENDER ( test_stage = 17 )
|
||||
ENTER_FRAME ( test_stage = 17 )
|
||||
Invalidate called inside enterFrameListener
|
||||
FRAME_CONSTRUCTED ( test_stage = 18 )
|
||||
EXIT_FRAME ( test_stage = 19 )
|
||||
Invalidate called inside exitFrameListener
|
||||
RENDER ( test_stage = 20 )
|
||||
ENTER_FRAME ( test_stage = 20 )
|
||||
FRAME_CONSTRUCTED ( test_stage = 0 )
|
||||
EXIT_FRAME ( test_stage = 1 )
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
num_frames = 8
|
Loading…
Reference in New Issue