From a8f7638d990e4ee39498e2b06bf48e67fdbb31f1 Mon Sep 17 00:00:00 2001 From: CUB3D Date: Sun, 28 Jun 2020 03:02:33 +0100 Subject: [PATCH] core: Implement ColorTransform --- core/src/avm1/globals.rs | 14 + core/src/avm1/globals/color_transform.rs | 309 +++++++++++++++++++++++ 2 files changed, 323 insertions(+) create mode 100644 core/src/avm1/globals/color_transform.rs diff --git a/core/src/avm1/globals.rs b/core/src/avm1/globals.rs index 0fb8f40c7..35f5262cc 100644 --- a/core/src/avm1/globals.rs +++ b/core/src/avm1/globals.rs @@ -14,6 +14,7 @@ mod array; pub(crate) mod boolean; pub(crate) mod button; mod color; +mod color_transform; pub(crate) mod display_object; pub(crate) mod error; mod function; @@ -218,6 +219,8 @@ pub fn create_globals<'gc>( let point_proto: Object<'gc> = point::create_proto(gc_context, object_proto, function_proto); let rectangle_proto: Object<'gc> = rectangle::create_proto(gc_context, object_proto, function_proto); + let color_transform_proto: Object<'gc> = + color_transform::create_proto(gc_context, object_proto, function_proto); //TODO: These need to be constructors and should also set `.prototype` on each one let object = object::create_object_object(gc_context, object_proto, function_proto); @@ -301,11 +304,22 @@ pub fn create_globals<'gc>( let point = point::create_point_object(gc_context, Some(point_proto), Some(function_proto)); let rectangle = rectangle::create_rectangle_object(gc_context, Some(rectangle_proto), Some(function_proto)); + let color_transform = color_transform::create_color_transform_object( + gc_context, + Some(color_transform_proto), + Some(function_proto), + ); flash.define_value(gc_context, "geom", geom.into(), EnumSet::empty()); geom.define_value(gc_context, "Matrix", matrix.into(), EnumSet::empty()); geom.define_value(gc_context, "Point", point.into(), EnumSet::empty()); geom.define_value(gc_context, "Rectangle", rectangle.into(), EnumSet::empty()); + geom.define_value( + gc_context, + "ColorTransform", + color_transform.into(), + EnumSet::empty(), + ); let listeners = SystemListeners::new(gc_context, Some(array_proto)); diff --git a/core/src/avm1/globals/color_transform.rs b/core/src/avm1/globals/color_transform.rs new file mode 100644 index 000000000..4f6a836f1 --- /dev/null +++ b/core/src/avm1/globals/color_transform.rs @@ -0,0 +1,309 @@ +//! ColorTransform object + +use crate::avm1::error::Error; +use crate::avm1::function::{Executable, FunctionObject}; +use crate::avm1::return_value::ReturnValue; +use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value}; +use crate::color_transform::ColorTransform; +use enumset::EnumSet; +use gc_arena::MutationContext; +use std::borrow::ToOwned; +use std::convert::Into; + +pub fn constructor<'gc>( + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, + args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let red_multiplier = args + .get(0) + .unwrap_or(&Value::Number(1.into())) + .to_owned() + .coerce_to_f64(avm, context)?; + let green_multiplier = args + .get(1) + .unwrap_or(&Value::Number(1.into())) + .to_owned() + .coerce_to_f64(avm, context)?; + let blue_multiplier = args + .get(2) + .unwrap_or(&Value::Number(1.into())) + .to_owned() + .coerce_to_f64(avm, context)?; + let alpha_multiplier = args + .get(3) + .unwrap_or(&Value::Number(1.into())) + .to_owned() + .coerce_to_f64(avm, context)?; + let red_offset = args + .get(4) + .unwrap_or(&Value::Number(0.into())) + .to_owned() + .coerce_to_f64(avm, context)?; + let green_offset = args + .get(5) + .unwrap_or(&Value::Number(0.into())) + .to_owned() + .coerce_to_f64(avm, context)?; + let blue_offset = args + .get(6) + .unwrap_or(&Value::Number(0.into())) + .to_owned() + .coerce_to_f64(avm, context)?; + let alpha_offset = args + .get(7) + .unwrap_or(&Value::Number(0.into())) + .to_owned() + .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)?; + + 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 green_multiplier = object + .get("greenMultiplier", avm, context)? + .coerce_to_f64(avm, context)? as f32; + let blue_multiplier = object + .get("blueMultiplier", avm, context)? + .coerce_to_f64(avm, context)? as f32; + let alpha_multiplier = object + .get("alphaMultiplier", avm, context)? + .coerce_to_f64(avm, context)? as f32; + let red_offset = object + .get("redOffset", avm, context)? + .coerce_to_f64(avm, context)? as f32; + let green_offset = object + .get("greenOffset", avm, context)? + .coerce_to_f64(avm, context)? as f32; + let blue_offset = object + .get("blueOffset", avm, context)? + .coerce_to_f64(avm, context)? as f32; + let alpha_offset = object + .get("alphaOffset", avm, context)? + .coerce_to_f64(avm, context)? as f32; + + Ok(ColorTransform { + r_mult: red_multiplier, + g_mult: green_multiplier, + b_mult: blue_multiplier, + a_mult: alpha_multiplier, + r_add: red_offset, + g_add: green_offset, + b_add: blue_offset, + a_add: alpha_offset, + }) +} + +pub fn get_rgb<'gc>( + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let ct = object_to_color_transform(this, avm, context)?; + let rgb = ((ct.r_add as u32) << 16) | ((ct.g_add as u32) << 8) | (ct.b_add as u32); + Ok(Value::Number(rgb.into()).into()) +} + +pub fn set_rgb<'gc>( + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, + args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let new_rgb = args + .get(0) + .unwrap_or(&Value::Undefined) + .coerce_to_f64(avm, context)? as u64; + + let red = ((new_rgb >> 16) & 0xFF) as f64; + 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)?; + + this.set("redMultiplier", 0.into(), avm, context)?; + this.set("greenMultiplier", 0.into(), avm, context)?; + this.set("blueMultiplier", 0.into(), avm, context)?; + + Ok(Value::Undefined.into()) +} + +pub fn create_color_transform_object<'gc>( + gc_context: MutationContext<'gc, '_>, + point_proto: Option>, + fn_proto: Option>, +) -> Object<'gc> { + FunctionObject::function( + gc_context, + Executable::Native(constructor), + fn_proto, + point_proto, + ) +} + +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)); + + object.force_set_function( + "concat", + concat, + gc_context, + EnumSet::empty(), + Some(fn_proto), + ); + + object.force_set_function( + "toString", + to_string, + gc_context, + EnumSet::empty(), + Some(fn_proto), + ); + + object.add_property( + gc_context, + "rgb", + Executable::Native(get_rgb), + Some(Executable::Native(set_rgb)), + EnumSet::empty(), + ); + + object.into() +} + +fn to_string<'gc>( + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'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 + ); + + Ok(Value::String(formatted).into()) +} + +fn concat<'gc>( + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + // Without an arg, does nothing + if let Some(arg) = _args.get(0) { + // If given an invalid var, return undefined + if arg == &Value::Undefined { + 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, + }; + + // 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 + { + return Ok(Value::Undefined.into()); + } + + + apply_color_transform_to_object(new_ct, this, avm, context)?; + } + + Ok(Value::Undefined.into()) +}