core: ColorTransform no longer passed through engine ColorTransform object

This commit is contained in:
CUB3D 2020-07-02 17:15:56 +01:00 committed by Mike Welsh
parent a8b1be2afa
commit 431cc532be
5 changed files with 483 additions and 103 deletions

View File

@ -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;

View File

@ -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<Object<'gc>>,
) -> 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<Value<'gc>, 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<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, 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<ReturnValue<'gc>, 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<Object<'gc>, 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<Object<'gc>> {
self.base().proto()
}
fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Option<Object<'gc>>) {
self.base().set_proto(gc_context, prototype);
}
fn define_value(
&self,
gc_context: MutationContext<'gc, '_>,
name: &str,
value: Value<'gc>,
attributes: EnumSet<Attribute>,
) {
self.base()
.define_value(gc_context, name, value, attributes)
}
fn set_attributes(
&mut self,
gc_context: MutationContext<'gc, '_>,
name: Option<&str>,
set_attributes: EnumSet<Attribute>,
clear_attributes: EnumSet<Attribute>,
) {
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<Executable<'gc>>,
attributes: EnumSet<Attribute>,
) {
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<Executable<'gc>>,
attributes: EnumSet<Attribute>,
) {
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<String> {
self.base().get_keys(avm)
}
fn as_string(&self) -> Cow<str> {
Cow::Owned(self.base().as_string().into_owned())
}
fn type_of(&self) -> &'static str {
self.base().type_of()
}
fn interfaces(&self) -> Vec<Object<'gc>> {
self.base().interfaces()
}
fn set_interfaces(
&mut self,
gc_context: MutationContext<'gc, '_>,
iface_list: Vec<Object<'gc>>,
) {
self.base().set_interfaces(gc_context, iface_list)
}
fn as_script_object(&self) -> Option<ScriptObject<'gc>> {
Some(self.base())
}
fn as_color_transform_object(&self) -> Option<ColorTransformObject<'gc>> {
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<Value<'gc>> {
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)
}
}

View File

@ -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,

View File

@ -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<ColorTransform, Error<'gc>> {
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<Object<'gc>>,
color_transform_proto: Option<Object<'gc>>,
fn_proto: Option<Object<'gc>>,
) -> 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<ReturnValue<'gc>, 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<ReturnValue<'gc>, 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())

View File

@ -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<Object<'gc>> + Clone + Copy {
@ -399,6 +401,11 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
None
}
/// Get the underlying `ColorTransformObject`, if it exists
fn as_color_transform_object(&self) -> Option<ColorTransformObject<'gc>> {
None
}
fn as_ptr(&self) -> *const ObjectPtr;
/// Check if this object is in the prototype chain of the specified test object.