From a807cf7623fe9f8e92713773960eeec33f0cfb31 Mon Sep 17 00:00:00 2001 From: David Wendt Date: Tue, 31 Aug 2021 00:14:47 -0400 Subject: [PATCH] avm2: Stub `flash.display.BitmapData` & storage object --- core/src/avm2/globals.rs | 11 ++ core/src/avm2/globals/flash/display.rs | 1 + .../avm2/globals/flash/display/bitmapdata.rs | 50 ++++++++ core/src/avm2/object.rs | 18 +++ core/src/avm2/object/bitmapdata_object.rs | 113 ++++++++++++++++++ 5 files changed, 193 insertions(+) create mode 100644 core/src/avm2/globals/flash/display/bitmapdata.rs create mode 100644 core/src/avm2/object/bitmapdata_object.rs diff --git a/core/src/avm2/globals.rs b/core/src/avm2/globals.rs index 1c48b2086..a82a87aa1 100644 --- a/core/src/avm2/globals.rs +++ b/core/src/avm2/globals.rs @@ -119,6 +119,7 @@ pub struct SystemPrototypes<'gc> { pub soundtransform: Object<'gc>, pub soundchannel: Object<'gc>, pub bitmap: Object<'gc>, + pub bitmapdata: Object<'gc>, } impl<'gc> SystemPrototypes<'gc> { @@ -172,6 +173,7 @@ impl<'gc> SystemPrototypes<'gc> { soundtransform: empty, soundchannel: empty, bitmap: empty, + bitmapdata: empty, } } } @@ -216,6 +218,7 @@ pub struct SystemClasses<'gc> { pub soundtransform: Object<'gc>, pub soundchannel: Object<'gc>, pub bitmap: Object<'gc>, + pub bitmapdata: Object<'gc>, } impl<'gc> SystemClasses<'gc> { @@ -269,6 +272,7 @@ impl<'gc> SystemClasses<'gc> { soundtransform: empty, soundchannel: empty, bitmap: empty, + bitmapdata: empty, } } } @@ -802,6 +806,13 @@ pub fn load_player_globals<'gc>( domain, script ); + avm2_system_class!( + bitmapdata, + activation, + flash::display::bitmapdata::create_class(mc), + domain, + script + ); // package `flash.geom` avm2_system_class!( diff --git a/core/src/avm2/globals/flash/display.rs b/core/src/avm2/globals/flash/display.rs index c4510d184..48d24584a 100644 --- a/core/src/avm2/globals/flash/display.rs +++ b/core/src/avm2/globals/flash/display.rs @@ -2,6 +2,7 @@ pub mod actionscriptversion; pub mod bitmap; +pub mod bitmapdata; pub mod capsstyle; pub mod displayobject; pub mod displayobjectcontainer; diff --git a/core/src/avm2/globals/flash/display/bitmapdata.rs b/core/src/avm2/globals/flash/display/bitmapdata.rs new file mode 100644 index 000000000..ada1e4d83 --- /dev/null +++ b/core/src/avm2/globals/flash/display/bitmapdata.rs @@ -0,0 +1,50 @@ +//! `flash.display.BitmapData` builtin/prototype + +use crate::avm2::activation::Activation; +use crate::avm2::class::{Class, ClassAttributes}; +use crate::avm2::method::Method; +use crate::avm2::names::{Namespace, QName}; +use crate::avm2::object::{bitmapdata_allocator, Object}; +use crate::avm2::value::Value; +use crate::avm2::Error; +use gc_arena::{GcCell, MutationContext}; + +/// Implements `flash.display.BitmapData`'s instance constructor. +pub fn instance_init<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + if let Some(this) = this { + activation.super_init(this, &[])?; + } + + Ok(Value::Undefined) +} + +/// Implements `flash.display.BitmapData`'s class constructor. +pub fn class_init<'gc>( + _activation: &mut Activation<'_, 'gc, '_>, + _this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + Ok(Value::Undefined) +} + +/// Construct `BitmapData`'s class. +pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> { + let class = Class::new( + QName::new(Namespace::package("flash.media"), "BitmapData"), + Some(QName::new(Namespace::package(""), "Object").into()), + Method::from_builtin(instance_init, "", mc), + Method::from_builtin(class_init, "", mc), + mc, + ); + + let mut write = class.write(mc); + + write.set_attributes(ClassAttributes::SEALED); + write.set_instance_allocator(bitmapdata_allocator); + + class +} diff --git a/core/src/avm2/object.rs b/core/src/avm2/object.rs index 665615c98..77dd58d54 100644 --- a/core/src/avm2/object.rs +++ b/core/src/avm2/object.rs @@ -16,6 +16,7 @@ use crate::avm2::value::{Hint, Value}; use crate::avm2::vector::VectorStorage; use crate::avm2::Error; use crate::backend::audio::{SoundHandle, SoundInstanceHandle}; +use crate::bitmap::bitmap_data::BitmapData; use crate::display_object::DisplayObject; use gc_arena::{Collect, GcCell, MutationContext}; use ruffle_macros::enum_trait_object; @@ -24,6 +25,7 @@ use std::fmt::Debug; use std::hash::{Hash, Hasher}; mod array_object; +mod bitmapdata_object; mod bytearray_object; mod class_object; mod custom_object; @@ -43,6 +45,7 @@ mod vector_object; mod xml_object; pub use crate::avm2::object::array_object::{array_allocator, ArrayObject}; +pub use crate::avm2::object::bitmapdata_object::{bitmapdata_allocator, BitmapDataObject}; pub use crate::avm2::object::bytearray_object::{bytearray_allocator, ByteArrayObject}; pub use crate::avm2::object::class_object::ClassObject; pub use crate::avm2::object::dispatch_object::DispatchObject; @@ -86,6 +89,7 @@ pub use crate::avm2::object::xml_object::{xml_allocator, XmlObject}; VectorObject(VectorObject<'gc>), SoundObject(SoundObject<'gc>), SoundChannelObject(SoundChannelObject<'gc>), + BitmapDataObject(BitmapDataObject<'gc>), } )] pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy { @@ -1237,6 +1241,20 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy /// /// This does nothing if the object is not a sound channel. fn set_sound_instance(self, _mc: MutationContext<'gc, '_>, _sound: SoundInstanceHandle) {} + + /// Unwrap this object's bitmap data + fn as_bitmap_data(&self) -> Option> { + None + } + + /// Initialize the bitmap data in this object, if it's capable of + /// supporting said data + fn init_bitmap_data( + &self, + _mc: MutationContext<'gc, '_>, + _new_bitmap: GcCell<'gc, BitmapData>, + ) { + } } pub enum ObjectPtr {} diff --git a/core/src/avm2/object/bitmapdata_object.rs b/core/src/avm2/object/bitmapdata_object.rs new file mode 100644 index 000000000..7a7965767 --- /dev/null +++ b/core/src/avm2/object/bitmapdata_object.rs @@ -0,0 +1,113 @@ +//! Object representation for BitmapData + +use crate::avm2::activation::Activation; +use crate::avm2::class::Class; +use crate::avm2::names::{Namespace, QName}; +use crate::avm2::object::script_object::ScriptObjectData; +use crate::avm2::object::{Object, ObjectPtr, TObject}; +use crate::avm2::scope::Scope; +use crate::avm2::string::AvmString; +use crate::avm2::value::Value; +use crate::avm2::Error; +use crate::bitmap::bitmap_data::BitmapData; +use crate::{ + impl_avm2_custom_object, impl_avm2_custom_object_instance, impl_avm2_custom_object_properties, +}; +use gc_arena::{Collect, GcCell, MutationContext}; + +/// A class instance allocator that allocates BitmapData objects. +pub fn bitmapdata_allocator<'gc>( + class: Object<'gc>, + proto: Object<'gc>, + activation: &mut Activation<'_, 'gc, '_>, +) -> Result, Error> { + let base = ScriptObjectData::base_new(Some(proto), Some(class)); + + Ok(BitmapDataObject(GcCell::allocate( + activation.context.gc_context, + BitmapDataObjectData { + base, + bitmap_data: None, + }, + )) + .into()) +} + +#[derive(Clone, Collect, Debug, Copy)] +#[collect(no_drop)] +pub struct BitmapDataObject<'gc>(GcCell<'gc, BitmapDataObjectData<'gc>>); + +#[derive(Clone, Collect, Debug)] +#[collect(no_drop)] +pub struct BitmapDataObjectData<'gc> { + /// Base script object + base: ScriptObjectData<'gc>, + + bitmap_data: Option>, +} + +impl<'gc> BitmapDataObject<'gc> { + pub fn from_bitmap_data( + activation: &mut Activation<'_, 'gc, '_>, + bitmap_data: GcCell<'gc, BitmapData>, + class: Object<'gc>, + ) -> Result, Error> { + let proto = class + .get_property( + class, + &QName::new(Namespace::public(), "prototype"), + activation, + )? + .coerce_to_object(activation)?; + + let mut instance = Self(GcCell::allocate( + activation.context.gc_context, + BitmapDataObjectData { + base: ScriptObjectData::base_new(Some(proto), Some(class)), + bitmap_data: Some(bitmap_data), + }, + )); + instance.install_instance_traits(activation, class)?; + class.call_native_init(Some(instance.into()), &[], activation, Some(class))?; + + Ok(instance.into()) + } +} + +impl<'gc> TObject<'gc> for BitmapDataObject<'gc> { + impl_avm2_custom_object!(base); + impl_avm2_custom_object_properties!(base); + impl_avm2_custom_object_instance!(base); + + fn derive(&self, activation: &mut Activation<'_, 'gc, '_>) -> Result, Error> { + let base = ScriptObjectData::base_new(Some((*self).into()), None); + + Ok(BitmapDataObject(GcCell::allocate( + activation.context.gc_context, + BitmapDataObjectData { + base, + bitmap_data: None, + }, + )) + .into()) + } + + fn to_string(&self, _mc: MutationContext<'gc, '_>) -> Result, Error> { + Ok(Value::Object(Object::from(*self))) + } + + fn value_of(&self, _mc: MutationContext<'gc, '_>) -> Result, Error> { + Ok(Value::Object(Object::from(*self))) + } + + /// Unwrap this object's bitmap data + fn as_bitmap_data(&self) -> Option> { + self.0.read().bitmap_data + } + + /// Initialize the bitmap data in this object, if it's capable of + /// supporting said data + fn init_bitmap_data(&self, mc: MutationContext<'gc, '_>, new_bitmap: GcCell<'gc, BitmapData>) { + self.0.write(mc).bitmap_data = Some(new_bitmap) + } +}