From 431cc532be12794ebbd629dedc465db7f157bacf Mon Sep 17 00:00:00 2001 From: CUB3D Date: Thu, 2 Jul 2020 17:15:56 +0100 Subject: [PATCH] core: ColorTransform no longer passed through engine ColorTransform object --- core/src/avm1.rs | 1 + core/src/avm1/color_transform_object.rs | 325 +++++++++++++++++++++++ core/src/avm1/globals.rs | 3 + core/src/avm1/globals/color_transform.rs | 248 ++++++++++------- core/src/avm1/object.rs | 9 +- 5 files changed, 483 insertions(+), 103 deletions(-) create mode 100644 core/src/avm1/color_transform_object.rs diff --git a/core/src/avm1.rs b/core/src/avm1.rs index f172cc9b3..b3376ee3f 100644 --- a/core/src/avm1.rs +++ b/core/src/avm1.rs @@ -17,6 +17,7 @@ mod test_utils; pub mod listeners; pub mod activation; +pub mod color_transform_object; pub mod debug; pub mod error; mod fscommand; diff --git a/core/src/avm1/color_transform_object.rs b/core/src/avm1/color_transform_object.rs new file mode 100644 index 000000000..a39029f31 --- /dev/null +++ b/core/src/avm1/color_transform_object.rs @@ -0,0 +1,325 @@ +use crate::avm1::error::Error; +use crate::avm1::function::Executable; +use crate::avm1::property::Attribute; +use crate::avm1::return_value::ReturnValue; +use crate::avm1::sound_object::SoundObject; +use crate::avm1::{Avm1, Object, ObjectPtr, ScriptObject, TObject, Value}; +use crate::context::UpdateContext; +use crate::display_object::DisplayObject; +use enumset::EnumSet; +use gc_arena::{Collect, GcCell, MutationContext}; + +use std::borrow::Cow; +use std::fmt; + +/// A ColorTransform +#[derive(Clone, Copy, Collect)] +#[collect(no_drop)] +pub struct ColorTransformObject<'gc>(GcCell<'gc, ColorTransformData<'gc>>); + +#[derive(Clone, Collect)] +#[collect(no_drop)] +pub struct ColorTransformData<'gc> { + /// The underlying script object. + base: ScriptObject<'gc>, + + red_multiplier: f64, + green_multiplier: f64, + blue_multiplier: f64, + alpha_multiplier: f64, + red_offset: f64, + green_offset: f64, + blue_offset: f64, + alpha_offset: f64, +} + +macro_rules! add_object_accessors { + ($([$set_ident: ident, $get_ident: ident, $var: ident],)*) => { + $( + pub fn $set_ident(&self, gc_context: MutationContext<'gc, '_>, v: f64) { + self.0.write(gc_context).$var = v; + } + + pub fn $get_ident(&self) -> f64 { + self.0.read() + .$var + } + )* + } +} + +impl fmt::Debug for ColorTransformObject<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let this = self.0.read(); + f.debug_struct("ColorTransform") + .field("redMultiplier", &this.red_multiplier) + .field("greenMultiplier", &this.green_multiplier) + .field("blueMultiplier", &this.blue_multiplier) + .field("alphaMultiplier", &this.alpha_multiplier) + .field("redOffset", &this.red_offset) + .field("greenOffset", &this.green_offset) + .field("blueOffset", &this.blue_offset) + .field("alphaOffset", &this.alpha_offset) + .finish() + } +} + +impl<'gc> ColorTransformObject<'gc> { + pub fn empty_color_transform_object( + gc_context: MutationContext<'gc, '_>, + proto: Option>, + ) -> Self { + ColorTransformObject(GcCell::allocate( + gc_context, + ColorTransformData { + base: ScriptObject::object(gc_context, proto), + red_multiplier: 0.0, + green_multiplier: 0.0, + blue_multiplier: 0.0, + alpha_multiplier: 0.0, + red_offset: 0.0, + green_offset: 0.0, + blue_offset: 0.0, + alpha_offset: 0.0, + }, + )) + } + + add_object_accessors!( + [set_red_multiplier, get_red_multiplier, red_multiplier], + [set_green_multiplier, get_green_multiplier, green_multiplier], + [set_blue_multiplier, get_blue_multiplier, blue_multiplier], + [set_alpha_multiplier, get_alpha_multiplier, alpha_multiplier], + [set_red_offset, get_red_offset, red_offset], + [set_green_offset, get_green_offset, green_offset], + [set_blue_offset, get_blue_offset, blue_offset], + [set_alpha_offset, get_alpha_offset, alpha_offset], + ); + + fn base(self) -> ScriptObject<'gc> { + self.0.read().base + } +} + +impl<'gc> TObject<'gc> for ColorTransformObject<'gc> { + fn get_local( + &self, + name: &str, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, + ) -> Result, Error<'gc>> { + self.base().get_local(name, avm, context, this) + } + + fn set( + &self, + name: &str, + value: Value<'gc>, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + ) -> Result<(), Error<'gc>> { + self.base().set(name, value, avm, context) + } + + fn call( + &self, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, + base_proto: Option>, + args: &[Value<'gc>], + ) -> Result, Error<'gc>> { + self.base().call(avm, context, this, base_proto, args) + } + + fn call_setter( + &self, + name: &str, + value: Value<'gc>, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, + ) -> Result, Error<'gc>> { + self.base().call_setter(name, value, avm, context, this) + } + + #[allow(clippy::new_ret_no_self)] + fn new( + &self, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + _this: Object<'gc>, + _args: &[Value<'gc>], + ) -> Result, Error<'gc>> { + Ok(ColorTransformObject::empty_color_transform_object( + context.gc_context, + Some(avm.prototypes.color_transform), + ) + .into()) + } + + fn delete( + &self, + avm: &mut Avm1<'gc>, + gc_context: MutationContext<'gc, '_>, + name: &str, + ) -> bool { + self.base().delete(avm, gc_context, name) + } + + fn proto(&self) -> Option> { + self.base().proto() + } + + fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Option>) { + self.base().set_proto(gc_context, prototype); + } + + fn define_value( + &self, + gc_context: MutationContext<'gc, '_>, + name: &str, + value: Value<'gc>, + attributes: EnumSet, + ) { + self.base() + .define_value(gc_context, name, value, attributes) + } + + fn set_attributes( + &mut self, + gc_context: MutationContext<'gc, '_>, + name: Option<&str>, + set_attributes: EnumSet, + clear_attributes: EnumSet, + ) { + self.base() + .set_attributes(gc_context, name, set_attributes, clear_attributes) + } + + fn add_property( + &self, + gc_context: MutationContext<'gc, '_>, + name: &str, + get: Executable<'gc>, + set: Option>, + attributes: EnumSet, + ) { + self.base() + .add_property(gc_context, name, get, set, attributes) + } + + fn add_property_with_case( + &self, + avm: &mut Avm1<'gc>, + gc_context: MutationContext<'gc, '_>, + name: &str, + get: Executable<'gc>, + set: Option>, + attributes: EnumSet, + ) { + self.base() + .add_property_with_case(avm, gc_context, name, get, set, attributes) + } + + fn has_property( + &self, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + name: &str, + ) -> bool { + self.base().has_property(avm, context, name) + } + + fn has_own_property( + &self, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + name: &str, + ) -> bool { + self.base().has_own_property(avm, context, name) + } + + fn has_own_virtual( + &self, + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + name: &str, + ) -> bool { + self.base().has_own_virtual(avm, context, name) + } + + fn is_property_overwritable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { + self.base().is_property_overwritable(avm, name) + } + + fn is_property_enumerable(&self, avm: &mut Avm1<'gc>, name: &str) -> bool { + self.base().is_property_enumerable(avm, name) + } + + fn get_keys(&self, avm: &mut Avm1<'gc>) -> Vec { + self.base().get_keys(avm) + } + + fn as_string(&self) -> Cow { + Cow::Owned(self.base().as_string().into_owned()) + } + + fn type_of(&self) -> &'static str { + self.base().type_of() + } + + fn interfaces(&self) -> Vec> { + self.base().interfaces() + } + + fn set_interfaces( + &mut self, + gc_context: MutationContext<'gc, '_>, + iface_list: Vec>, + ) { + self.base().set_interfaces(gc_context, iface_list) + } + + fn as_script_object(&self) -> Option> { + Some(self.base()) + } + + fn as_color_transform_object(&self) -> Option> { + Some(*self) + } + + fn as_ptr(&self) -> *const ObjectPtr { + self.0.as_ptr() as *const ObjectPtr + } + + fn length(&self) -> usize { + self.base().length() + } + + fn array(&self) -> Vec> { + self.base().array() + } + + fn set_length(&self, gc_context: MutationContext<'gc, '_>, length: usize) { + self.base().set_length(gc_context, length) + } + + fn array_element(&self, index: usize) -> Value<'gc> { + self.base().array_element(index) + } + + fn set_array_element( + &self, + index: usize, + value: Value<'gc>, + gc_context: MutationContext<'gc, '_>, + ) -> usize { + self.base().set_array_element(index, value, gc_context) + } + + fn delete_array_element(&self, index: usize, gc_context: MutationContext<'gc, '_>) { + self.base().delete_array_element(index, gc_context) + } +} diff --git a/core/src/avm1/globals.rs b/core/src/avm1/globals.rs index 35f5262cc..aa32af57d 100644 --- a/core/src/avm1/globals.rs +++ b/core/src/avm1/globals.rs @@ -151,6 +151,7 @@ pub struct SystemPrototypes<'gc> { pub rectangle: Object<'gc>, pub rectangle_constructor: Object<'gc>, pub shared_object: Object<'gc>, + pub color_transform: Object<'gc>, } unsafe impl<'gc> gc_arena::Collect for SystemPrototypes<'gc> { @@ -309,6 +310,7 @@ pub fn create_globals<'gc>( Some(color_transform_proto), Some(function_proto), ); + eprintln!("CT: {:?}\n{:?}", color_transform_proto, color_transform); flash.define_value(gc_context, "geom", geom.into(), EnumSet::empty()); geom.define_value(gc_context, "Matrix", matrix.into(), EnumSet::empty()); @@ -490,6 +492,7 @@ pub fn create_globals<'gc>( rectangle: rectangle_proto, rectangle_constructor: rectangle, shared_object: shared_object_proto, + color_transform: color_transform_proto, }, globals.into(), listeners, diff --git a/core/src/avm1/globals/color_transform.rs b/core/src/avm1/globals/color_transform.rs index d966cede9..70cd80b80 100644 --- a/core/src/avm1/globals/color_transform.rs +++ b/core/src/avm1/globals/color_transform.rs @@ -1,5 +1,6 @@ //! ColorTransform object +use crate::avm1::color_transform_object::ColorTransformObject; use crate::avm1::error::Error; use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::return_value::ReturnValue; @@ -48,62 +49,27 @@ pub fn constructor<'gc>( .unwrap_or(&Value::Number(0.into())) .coerce_to_f64(avm, context)?; - let ct = ColorTransform { - r_mult: red_multiplier as f32, - g_mult: green_multiplier as f32, - b_mult: blue_multiplier as f32, - a_mult: alpha_multiplier as f32, - r_add: red_offset as f32, - g_add: green_offset as f32, - b_add: blue_offset as f32, - a_add: alpha_offset as f32, - }; - - apply_color_transform_to_object(ct, this, avm, context)?; + let ct = this.as_color_transform_object().unwrap(); + ct.set_red_multiplier(context.gc_context, red_multiplier); + ct.set_green_multiplier(context.gc_context, green_multiplier); + ct.set_blue_multiplier(context.gc_context, blue_multiplier); + ct.set_alpha_multiplier(context.gc_context, alpha_multiplier); + ct.set_red_offset(context.gc_context, red_offset); + ct.set_green_offset(context.gc_context, green_offset); + ct.set_blue_offset(context.gc_context, blue_offset); + ct.set_alpha_offset(context.gc_context, alpha_offset); Ok(Value::Undefined.into()) } -pub fn apply_color_transform_to_object<'gc>( - color_transform: ColorTransform, - object: Object<'gc>, - avm: &mut Avm1<'gc>, - context: &mut UpdateContext<'_, 'gc, '_>, -) -> Result<(), Error<'gc>> { - object.set("redMultiplier", color_transform.r_mult.into(), avm, context)?; - object.set( - "greenMultiplier", - color_transform.g_mult.into(), - avm, - context, - )?; - object.set( - "blueMultiplier", - color_transform.b_mult.into(), - avm, - context, - )?; - object.set( - "alphaMultiplier", - color_transform.a_mult.into(), - avm, - context, - )?; - object.set("redOffset", color_transform.r_add.into(), avm, context)?; - object.set("greenOffset", color_transform.g_add.into(), avm, context)?; - object.set("blueOffset", color_transform.b_add.into(), avm, context)?; - object.set("alphaOffset", color_transform.a_add.into(), avm, context)?; - Ok(()) -} - pub fn object_to_color_transform<'gc>( object: Object<'gc>, avm: &mut Avm1<'gc>, context: &mut UpdateContext<'_, 'gc, '_>, ) -> Result> { - let red_multiplier = object - .get("redMultiplier", avm, context)? - .coerce_to_f64(avm, context)? as f32; + let red_multiplier = 0.0; /*object + .get("redMultiplier", avm, context)? + .coerce_to_f64(avm, context)? as f32;*/ let green_multiplier = object .get("greenMultiplier", avm, context)? .coerce_to_f64(avm, context)? as f32; @@ -164,36 +130,112 @@ pub fn set_rgb<'gc>( let green = ((new_rgb >> 8) & 0xFF) as f64; let blue = (new_rgb & 0xFF) as f64; - this.set("redOffset", red.into(), avm, context)?; - this.set("greenOffset", green.into(), avm, context)?; - this.set("blueOffset", blue.into(), avm, context)?; + println!("Self: {:?}", this); - this.set("redMultiplier", 0.into(), avm, context)?; - this.set("greenMultiplier", 0.into(), avm, context)?; - this.set("blueMultiplier", 0.into(), avm, context)?; + let ct = this.as_color_transform_object().unwrap(); + + /*ct.set_red_offset(context.gc_context, red); + ct.set_green_offset(context.gc_context, green); + ct.set_blue_offset(context.gc_context, blue); + + ct.set_red_multiplier(context.gc_context, 0.0); + ct.set_green_multiplier(context.gc_context, 0.0); + ct.set_blue_multiplier(context.gc_context, 0.0);*/ Ok(Value::Undefined.into()) } pub fn create_color_transform_object<'gc>( gc_context: MutationContext<'gc, '_>, - point_proto: Option>, + color_transform_proto: Option>, fn_proto: Option>, ) -> Object<'gc> { FunctionObject::function( gc_context, Executable::Native(constructor), fn_proto, - point_proto, + color_transform_proto, ) } +macro_rules! color_transform_value_accessor { + ($([$get_ident: ident, $set_ident: ident],)*) => { + $( + pub fn $set_ident<'gc>( + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, + args: &[Value<'gc>], + ) -> Result, Error<'gc>> { + //TODO: update as comment + let new_val = args + .get(0) + .unwrap_or(&Value::Undefined) + .coerce_to_f64(avm, context)?; + + let ct = this.as_color_transform_object().unwrap(); + ct.$set_ident(context.gc_context, new_val); + Ok(Value::Undefined.into()) + } + + pub fn $get_ident<'gc>( + _avm: &mut Avm1<'gc>, + _context: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, + _args: &[Value<'gc>], + ) -> Result, Error<'gc>> { + let ct = this.as_color_transform_object().unwrap(); + Ok(Value::Number(ct.$get_ident()).into()) + } + )* + } +} + +color_transform_value_accessor!( + [get_red_multiplier, set_red_multiplier], + [get_green_multiplier, set_green_multiplier], + [get_blue_multiplier, set_blue_multiplier], + [get_alpha_multiplier, set_alpha_multiplier], + [get_red_offset, set_red_offset], + [get_green_offset, set_green_offset], + [get_blue_offset, set_blue_offset], + [get_alpha_offset, set_alpha_offset], +); + +macro_rules! with_color_transform { + ($obj: ident, $gc: ident, $($name: expr => [$get: ident, $set: ident],)*) => { + $( + $obj.add_property( + $gc, + $name, + Executable::Native($get), + Some(Executable::Native($set)), + EnumSet::empty(), + ); + )* + } +} + pub fn create_proto<'gc>( gc_context: MutationContext<'gc, '_>, proto: Object<'gc>, fn_proto: Object<'gc>, ) -> Object<'gc> { - let mut object = ScriptObject::object(gc_context, Some(proto)); + let color_transform_object = + ColorTransformObject::empty_color_transform_object(gc_context, Some(proto)); + let mut object = color_transform_object.as_script_object().unwrap(); + + with_color_transform!(object, gc_context, + "rgb" => [get_rgb, set_rgb], + "redMultiplier" => [get_red_multiplier, set_red_multiplier], + "greenMultiplier" => [get_green_multiplier, set_green_multiplier], + "blueMultiplier" => [get_blue_multiplier, set_blue_multiplier], + "alphaMultiplier" => [get_alpha_multiplier, set_alpha_multiplier], + "redOffset" => [get_red_offset, set_red_offset], + "greenOffset" => [get_green_offset, set_green_offset], + "blueOffset" => [get_blue_offset, set_blue_offset], + "alphaOffset" => [get_alpha_offset, set_alpha_offset], + ); object.force_set_function( "concat", @@ -211,15 +253,7 @@ pub fn create_proto<'gc>( Some(fn_proto), ); - object.add_property( - gc_context, - "rgb", - Executable::Native(get_rgb), - Some(Executable::Native(set_rgb)), - EnumSet::empty(), - ); - - object.into() + color_transform_object.into() } fn to_string<'gc>( @@ -231,14 +265,14 @@ fn to_string<'gc>( let ct = object_to_color_transform(this, avm, context)?; let formatted = format!("(redMultiplier={}, greenMultiplier={}, blueMultiplier={}, alphaMultiplier={}, redOffset={}, greenOffset={}, blueOffset={}, alphaOffset={})", - ct.r_mult, - ct.g_mult, - ct.b_mult, - ct.a_mult, - ct.r_add, - ct.g_add, - ct.b_add, - ct.a_add + this.get("redMultiplier", avm, context)?.coerce_to_string(avm, context)?, + this.get("greenMultiplier", avm, context)?.coerce_to_string(avm, context)?, + this.get("blueMultiplier", avm, context)?.coerce_to_string(avm, context)?, + this.get("alphaMultiplier", avm, context)?.coerce_to_string(avm, context)?, + this.get("redOffset", avm, context)?.coerce_to_string(avm, context)?, + this.get("greenOffset", avm, context)?.coerce_to_string(avm, context)?, + this.get("blueOffset", avm, context)?.coerce_to_string(avm, context)?, + this.get("alphaOffset", avm, context)?.coerce_to_string(avm, context)? ); Ok(Value::String(formatted).into()) @@ -257,42 +291,52 @@ fn concat<'gc>( return Ok(Value::Undefined.into()); } let other = arg.coerce_to_object(avm, context); - let other_ct: ColorTransform = object_to_color_transform(other, avm, context)?; - let this_ct: ColorTransform = object_to_color_transform(this, avm, context)?; - let new_ct = ColorTransform { - r_mult: other_ct.r_mult * this_ct.r_mult, - g_mult: other_ct.g_mult * this_ct.g_mult, - b_mult: other_ct.b_mult * this_ct.b_mult, - a_mult: other_ct.a_mult * this_ct.a_mult, - r_add: (other_ct.r_add * this_ct.r_mult) + this_ct.r_add, - g_add: (other_ct.g_add * this_ct.g_mult) + this_ct.g_add, - b_add: (other_ct.b_add * this_ct.b_mult) + this_ct.b_add, - a_add: (other_ct.a_add * this_ct.a_mult) + this_ct.a_add, - }; + let other_ct = other.as_color_transform_object().unwrap(); + let this_ct = this.as_color_transform_object().unwrap(); + + let red_multiplier = other_ct.get_red_multiplier() * this_ct.get_red_multiplier(); + let green_multiplier = other_ct.get_green_multiplier() * this_ct.get_green_multiplier(); + let blue_multiplier = other_ct.get_blue_multiplier() * this_ct.get_blue_multiplier(); + let alpha_multiplier = other_ct.get_alpha_multiplier() * this_ct.get_alpha_multiplier(); + let red_offset = (other_ct.get_red_multiplier() * this_ct.get_red_multiplier()) + + this_ct.get_red_multiplier(); + let green_offset = (other_ct.get_green_offset() * this_ct.get_green_multiplier()) + + this_ct.get_green_offset(); + let blue_offset = (other_ct.get_blue_offset() * this_ct.get_blue_multiplier()) + + this_ct.get_blue_offset(); + let alpha_offset = (other_ct.get_alpha_offset() * this_ct.get_alpha_multiplier()) + + this_ct.get_alpha_offset(); // This appears to do nothing if values are out of range - if 0.0 > new_ct.r_mult - || new_ct.r_mult > 1.0 - || 0.0 > new_ct.g_mult - || new_ct.g_mult > 1.0 - || 0.0 > new_ct.b_mult - || new_ct.b_mult > 1.0 - || 0.0 > new_ct.a_mult - || new_ct.a_mult > 1.0 - || -255.0 > new_ct.r_add - || new_ct.r_add > 255.0 - || -255.0 > new_ct.g_add - || new_ct.g_add > 255.0 - || -255.0 > new_ct.b_add - || new_ct.b_add > 255.0 - || -255.0 > new_ct.a_add - || new_ct.a_add > 255.0 + if 0.0 > red_multiplier + || red_multiplier > 1.0 + || 0.0 > green_multiplier + || green_multiplier > 1.0 + || 0.0 > blue_multiplier + || blue_multiplier > 1.0 + || 0.0 > alpha_multiplier + || alpha_multiplier > 1.0 + || -255.0 > red_offset + || red_offset > 255.0 + || -255.0 > green_offset + || green_offset > 255.0 + || -255.0 > blue_offset + || blue_offset > 255.0 + || -255.0 > alpha_offset + || alpha_offset > 255.0 { return Ok(Value::Undefined.into()); } - apply_color_transform_to_object(new_ct, this, avm, context)?; + this_ct.set_red_multiplier(context.gc_context, red_multiplier); + this_ct.set_green_multiplier(context.gc_context, green_multiplier); + this_ct.set_blue_multiplier(context.gc_context, blue_multiplier); + this_ct.set_alpha_multiplier(context.gc_context, alpha_multiplier); + this_ct.set_red_offset(context.gc_context, red_offset); + this_ct.set_green_offset(context.gc_context, green_offset); + this_ct.set_blue_offset(context.gc_context, blue_offset); + this_ct.set_alpha_offset(context.gc_context, alpha_offset); } Ok(Value::Undefined.into()) diff --git a/core/src/avm1/object.rs b/core/src/avm1/object.rs index 2f8534f64..a8fb791e2 100644 --- a/core/src/avm1/object.rs +++ b/core/src/avm1/object.rs @@ -8,6 +8,7 @@ use crate::avm1::super_object::SuperObject; use crate::avm1::value_object::ValueObject; use crate::avm1::activation::Activation; +use crate::avm1::color_transform_object::ColorTransformObject; use crate::avm1::xml_attributes_object::XMLAttributesObject; use crate::avm1::xml_idmap_object::XMLIDMapObject; use crate::avm1::xml_object::XMLObject; @@ -35,7 +36,8 @@ use std::fmt::Debug; XMLIDMapObject(XMLIDMapObject<'gc>), ValueObject(ValueObject<'gc>), FunctionObject(FunctionObject<'gc>), - SharedObject(SharedObject<'gc>) + SharedObject(SharedObject<'gc>), + ColorTransformObject(ColorTransformObject<'gc>), } )] pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy { @@ -399,6 +401,11 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy None } + /// Get the underlying `ColorTransformObject`, if it exists + fn as_color_transform_object(&self) -> Option> { + None + } + fn as_ptr(&self) -> *const ObjectPtr; /// Check if this object is in the prototype chain of the specified test object.