diff --git a/core/src/avm1/globals.rs b/core/src/avm1/globals.rs index 9c3028571..2dc7f260d 100644 --- a/core/src/avm1/globals.rs +++ b/core/src/avm1/globals.rs @@ -13,7 +13,7 @@ use std::str; mod accessibility; mod array; pub(crate) mod as_broadcaster; -mod bevel_filter; +pub(crate) mod bevel_filter; mod bitmap_data; mod bitmap_filter; pub(crate) mod blur_filter; @@ -498,8 +498,6 @@ pub struct SystemPrototypes<'gc> { pub context_menu_item_constructor: Object<'gc>, pub bitmap_filter: Object<'gc>, pub bitmap_filter_constructor: Object<'gc>, - pub bevel_filter: Object<'gc>, - pub bevel_filter_constructor: Object<'gc>, pub glow_filter: Object<'gc>, pub glow_filter_constructor: Object<'gc>, pub drop_shadow_filter: Object<'gc>, @@ -754,15 +752,8 @@ pub fn create_globals<'gc>( let blur_filter = blur_filter::create_constructor(gc_context, bitmap_filter_proto, function_proto); - let bevel_filter_proto = - bevel_filter::create_proto(gc_context, bitmap_filter_proto, function_proto); - let bevel_filter = FunctionObject::constructor( - gc_context, - Executable::Native(bevel_filter::constructor), - constructor_to_fn!(bevel_filter::constructor), - function_proto, - bevel_filter_proto, - ); + let bevel_filter = + bevel_filter::create_constructor(gc_context, bitmap_filter_proto, function_proto); let glow_filter_proto = glow_filter::create_proto(gc_context, bitmap_filter_proto, function_proto); @@ -1148,8 +1139,6 @@ pub fn create_globals<'gc>( context_menu_item_constructor: context_menu_item, bitmap_filter: bitmap_filter_proto, bitmap_filter_constructor: bitmap_filter, - bevel_filter: bevel_filter_proto, - bevel_filter_constructor: bevel_filter, glow_filter: glow_filter_proto, glow_filter_constructor: glow_filter, drop_shadow_filter: drop_shadow_filter_proto, diff --git a/core/src/avm1/globals/bevel_filter.rs b/core/src/avm1/globals/bevel_filter.rs index 043904387..fe641d96a 100644 --- a/core/src/avm1/globals/bevel_filter.rs +++ b/core/src/avm1/globals/bevel_filter.rs @@ -1,415 +1,539 @@ //! flash.filters.BevelFilter object -use crate::avm1::activation::Activation; -use crate::avm1::error::Error; -use crate::avm1::object::bevel_filter::{BevelFilterObject, BevelFilterType}; +use crate::avm1::function::{Executable, FunctionObject}; +use crate::avm1::object::NativeObject; use crate::avm1::property_decl::{define_properties_on, Declaration}; -use crate::avm1::{Object, TObject, Value}; +use crate::avm1::{Activation, Error, Object, ScriptObject, TObject, Value}; use crate::string::{AvmString, WStr}; -use gc_arena::MutationContext; +use gc_arena::{Collect, GcCell, MutationContext}; +use swf::Color; + +#[derive(Copy, Clone, Debug, Collect)] +#[collect(no_drop)] +pub enum BevelFilterType { + Inner, + Outer, + Full, +} + +impl From<&WStr> for BevelFilterType { + fn from(value: &WStr) -> Self { + if value == b"inner" { + Self::Inner + } else if value == b"outer" { + Self::Outer + } else { + Self::Full + } + } +} + +impl From for &'static WStr { + fn from(type_: BevelFilterType) -> &'static WStr { + match type_ { + BevelFilterType::Inner => WStr::from_units(b"inner"), + BevelFilterType::Outer => WStr::from_units(b"outer"), + BevelFilterType::Full => WStr::from_units(b"full"), + } + } +} + +#[derive(Clone, Debug, Collect)] +#[collect(require_static)] +pub struct BevelFilterObject { + distance: f64, + // TODO: Introduce `Angle` struct. + angle: f64, + highlight: Color, + shadow: Color, + quality: i32, + // TODO: Introduce unsigned `Fixed8`? + strength: u16, + knockout: bool, + blur_x: f64, + blur_y: f64, + type_: BevelFilterType, +} + +impl BevelFilterObject { + fn new<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + args: &[Value<'gc>], + ) -> Result, Error<'gc>> { + let bevel_filter = GcCell::allocate(activation.context.gc_context, Default::default()); + bevel_filter.set_distance(activation, args.get(0))?; + bevel_filter.set_angle(activation, args.get(1))?; + bevel_filter.set_highlight_color(activation, args.get(2))?; + bevel_filter.set_highlight_alpha(activation, args.get(3))?; + bevel_filter.set_shadow_color(activation, args.get(4))?; + bevel_filter.set_shadow_alpha(activation, args.get(5))?; + bevel_filter.set_blur_x(activation, args.get(6))?; + bevel_filter.set_blur_y(activation, args.get(7))?; + bevel_filter.set_strength(activation, args.get(8))?; + bevel_filter.set_quality(activation, args.get(9))?; + bevel_filter.set_type(activation, args.get(10))?; + bevel_filter.set_knockout(activation, args.get(11))?; + Ok(bevel_filter) + } +} + +impl Default for BevelFilterObject { + #[allow(clippy::approx_constant)] + fn default() -> Self { + Self { + distance: 4.0, + angle: 0.785398163, // ~45 degrees + highlight: Color::WHITE, + shadow: Color::BLACK, + quality: 1, + strength: 1 << 8, + knockout: false, + blur_x: 4.0, + blur_y: 4.0, + type_: BevelFilterType::Inner, + } + } +} + +trait BevelFilterExt<'gc> { + fn distance(self) -> f64; + + fn set_distance( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>>; + + fn angle(self) -> f64; + + fn set_angle( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>>; + + fn highlight_color(self) -> i32; + + fn set_highlight_color( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>>; + + fn highlight_alpha(self) -> f64; + + fn set_highlight_alpha( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>>; + + fn shadow_color(self) -> i32; + + fn set_shadow_color( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>>; + + fn shadow_alpha(self) -> f64; + + fn set_shadow_alpha( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>>; + + fn quality(self) -> i32; + + fn set_quality( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>>; + + fn strength(self) -> f64; + + fn set_strength( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>>; + + fn knockout(self) -> bool; + + fn set_knockout( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>>; + + fn blur_x(self) -> f64; + + fn set_blur_x( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>>; + + fn blur_y(self) -> f64; + + fn set_blur_y( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>>; + + fn type_(self) -> BevelFilterType; + + fn set_type( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>>; +} + +impl<'gc> BevelFilterExt<'gc> for GcCell<'gc, BevelFilterObject> { + fn distance(self) -> f64 { + self.read().distance + } + + fn set_distance( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>> { + if let Some(value) = value { + let distance = value.coerce_to_f64(activation)?; + self.write(activation.context.gc_context).distance = distance; + } + Ok(()) + } + + fn angle(self) -> f64 { + self.read().angle.to_degrees() + } + + fn set_angle( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>> { + if let Some(value) = value { + let angle = (value.coerce_to_f64(activation)? % 360.0).to_radians(); + self.write(activation.context.gc_context).angle = angle; + } + Ok(()) + } + + fn highlight_color(self) -> i32 { + self.read().highlight.to_rgb() as i32 + } + + fn set_highlight_color( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>> { + if let Some(value) = value { + let color = Color::from_rgb(value.coerce_to_u32(activation)?, 0); + self.write(activation.context.gc_context).highlight = color; + } + Ok(()) + } + + fn highlight_alpha(self) -> f64 { + f64::from(self.read().highlight.a) / 255.0 + } + + fn set_highlight_alpha( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>> { + if let Some(value) = value { + let alpha = (value.coerce_to_f64(activation)? * 255.0) as u8; + self.write(activation.context.gc_context).highlight.a = alpha; + } + Ok(()) + } + + fn shadow_color(self) -> i32 { + self.read().shadow.to_rgb() as i32 + } + + fn set_shadow_color( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>> { + if let Some(value) = value { + let color = Color::from_rgb(value.coerce_to_u32(activation)?, 0); + self.write(activation.context.gc_context).shadow = color; + } + Ok(()) + } + + fn shadow_alpha(self) -> f64 { + f64::from(self.read().shadow.a) / 255.0 + } + + fn set_shadow_alpha( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>> { + if let Some(value) = value { + let alpha = (value.coerce_to_f64(activation)? * 255.0) as u8; + self.write(activation.context.gc_context).shadow.a = alpha; + } + Ok(()) + } + + fn quality(self) -> i32 { + self.read().quality + } + + fn set_quality( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>> { + if let Some(value) = value { + let quality = value.coerce_to_i32(activation)?.clamp(0, 15); + self.write(activation.context.gc_context).quality = quality; + } + Ok(()) + } + + fn strength(self) -> f64 { + f64::from(self.read().strength) / 256.0 + } + + fn set_strength( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>> { + if let Some(value) = value { + let strength = ((value.coerce_to_f64(activation)? * 256.0) as u16).clamp(0, 0xFF00); + self.write(activation.context.gc_context).strength = strength; + } + Ok(()) + } + + fn knockout(self) -> bool { + self.read().knockout + } + + fn set_knockout( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>> { + if let Some(value) = value { + let knockout = value.as_bool(activation.swf_version()); + self.write(activation.context.gc_context).knockout = knockout; + } + Ok(()) + } + + fn blur_x(self) -> f64 { + self.read().blur_x + } + + fn set_blur_x( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>> { + if let Some(value) = value { + let blur_x = value.coerce_to_f64(activation)?.clamp(0.0, 255.0); + self.write(activation.context.gc_context).blur_x = blur_x; + } + Ok(()) + } + + fn blur_y(self) -> f64 { + self.read().blur_y + } + + fn set_blur_y( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>> { + if let Some(value) = value { + let blur_y = value.coerce_to_f64(activation)?.clamp(0.0, 255.0); + self.write(activation.context.gc_context).blur_y = blur_y; + } + Ok(()) + } + + fn type_(self) -> BevelFilterType { + self.read().type_ + } + + fn set_type( + self, + activation: &mut Activation<'_, 'gc, '_>, + value: Option<&Value<'gc>>, + ) -> Result<(), Error<'gc>> { + if let Some(value) = value { + let type_ = value.coerce_to_string(activation)?.as_wstr().into(); + self.write(activation.context.gc_context).type_ = type_; + } + Ok(()) + } +} + +macro_rules! bevel_filter_method { + ($index:literal) => { + |activation, this, args| method(activation, this, args, $index) + }; +} const PROTO_DECLS: &[Declaration] = declare_properties! { - "distance" => property(distance, set_distance); - "angle" => property(angle, set_angle); - "highlightColor" => property(highlight_color, set_highlight_color); - "highlightAlpha" => property(highlight_alpha, set_highlight_alpha); - "shadowColor" => property(shadow_color, set_shadow_color); - "shadowAlpha" => property(shadow_alpha, set_shadow_alpha); - "quality" => property(quality, set_quality); - "strength" => property(strength, set_strength); - "knockout" => property(knockout, set_knockout); - "blurX" => property(blur_x, set_blur_x); - "blurY" => property(blur_y, set_blur_y); - "type" => property(get_type, set_type); + "distance" => property(bevel_filter_method!(1), bevel_filter_method!(2)); + "angle" => property(bevel_filter_method!(3), bevel_filter_method!(4)); + "highlightColor" => property(bevel_filter_method!(5), bevel_filter_method!(6)); + "highlightAlpha" => property(bevel_filter_method!(7), bevel_filter_method!(8)); + "shadowColor" => property(bevel_filter_method!(9), bevel_filter_method!(10)); + "shadowAlpha" => property(bevel_filter_method!(11), bevel_filter_method!(12)); + "quality" => property(bevel_filter_method!(13), bevel_filter_method!(14)); + "strength" => property(bevel_filter_method!(15), bevel_filter_method!(16)); + "knockout" => property(bevel_filter_method!(17), bevel_filter_method!(18)); + "blurX" => property(bevel_filter_method!(19), bevel_filter_method!(20)); + "blurY" => property(bevel_filter_method!(21), bevel_filter_method!(22)); + "type" => property(bevel_filter_method!(23), bevel_filter_method!(24)); }; -pub fn constructor<'gc>( +fn method<'gc>( activation: &mut Activation<'_, 'gc, '_>, this: Object<'gc>, args: &[Value<'gc>], + index: u8, ) -> Result, Error<'gc>> { - set_distance(activation, this, args.get(0..1).unwrap_or_default())?; - set_angle(activation, this, args.get(1..2).unwrap_or_default())?; - set_highlight_color(activation, this, args.get(2..3).unwrap_or_default())?; - set_highlight_alpha(activation, this, args.get(3..4).unwrap_or_default())?; - set_shadow_color(activation, this, args.get(4..5).unwrap_or_default())?; - set_shadow_alpha(activation, this, args.get(5..6).unwrap_or_default())?; - set_blur_x(activation, this, args.get(6..7).unwrap_or_default())?; - set_blur_y(activation, this, args.get(7..8).unwrap_or_default())?; - set_strength(activation, this, args.get(8..9).unwrap_or_default())?; - set_quality(activation, this, args.get(9..10).unwrap_or_default())?; - set_type(activation, this, args.get(10..11).unwrap_or_default())?; - set_knockout(activation, this, args.get(11..12).unwrap_or_default())?; + const CONSTRUCTOR: u8 = 0; + const GET_DISTANCE: u8 = 1; + const SET_DISTANCE: u8 = 2; + const GET_ANGLE: u8 = 3; + const SET_ANGLE: u8 = 4; + const GET_HIGHLIGHT_COLOR: u8 = 5; + const SET_HIGHLIGHT_COLOR: u8 = 6; + const GET_HIGHLIGHT_ALPHA: u8 = 7; + const SET_HIGHLIGHT_ALPHA: u8 = 8; + const GET_SHADOW_COLOR: u8 = 9; + const SET_SHADOW_COLOR: u8 = 10; + const GET_SHADOW_ALPHA: u8 = 11; + const SET_SHADOW_ALPHA: u8 = 12; + const GET_QUALITY: u8 = 13; + const SET_QUALITY: u8 = 14; + const GET_STRENGTH: u8 = 15; + const SET_STRENGTH: u8 = 16; + const GET_KNOCKOUT: u8 = 17; + const SET_KNOCKOUT: u8 = 18; + const GET_BLUR_X: u8 = 19; + const SET_BLUR_X: u8 = 20; + const GET_BLUR_Y: u8 = 21; + const SET_BLUR_Y: u8 = 22; + const GET_TYPE: u8 = 23; + const SET_TYPE: u8 = 24; - Ok(this.into()) -} - -pub fn distance<'gc>( - _activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - if let Some(filter) = this.as_bevel_filter_object() { - return Ok(filter.distance().into()); + if index == CONSTRUCTOR { + let bevel_filter = BevelFilterObject::new(activation, args)?; + this.set_native( + activation.context.gc_context, + NativeObject::BevelFilter(bevel_filter), + ); + return Ok(this.into()); } - Ok(Value::Undefined) -} - -pub fn set_distance<'gc>( - activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - let distance = args.get(0).unwrap_or(&4.into()).coerce_to_f64(activation)?; - - if let Some(filter) = this.as_bevel_filter_object() { - filter.set_distance(activation.context.gc_context, distance); - } - - Ok(Value::Undefined) -} - -pub fn angle<'gc>( - _activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - if let Some(filter) = this.as_bevel_filter_object() { - return Ok(filter.angle().into()); - } - - Ok(Value::Undefined) -} - -pub fn set_angle<'gc>( - activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - let angle = args - .get(0) - .unwrap_or(&44.9999999772279.into()) - .coerce_to_f64(activation)?; - - let clamped_angle = if angle.is_sign_negative() { - -(angle.abs() % 360.0) - } else { - angle % 360.0 + let this = match this.native() { + NativeObject::BevelFilter(bevel_filter) => bevel_filter, + _ => return Ok(Value::Undefined), }; - if let Some(filter) = this.as_bevel_filter_object() { - filter.set_angle(activation.context.gc_context, clamped_angle); - } - - Ok(Value::Undefined) + Ok(match index { + GET_DISTANCE => this.distance().into(), + SET_DISTANCE => { + this.set_distance(activation, args.get(0))?; + Value::Undefined + } + GET_ANGLE => this.angle().into(), + SET_ANGLE => { + this.set_angle(activation, args.get(0))?; + Value::Undefined + } + GET_HIGHLIGHT_COLOR => this.highlight_color().into(), + SET_HIGHLIGHT_COLOR => { + this.set_highlight_color(activation, args.get(0))?; + Value::Undefined + } + GET_HIGHLIGHT_ALPHA => this.highlight_alpha().into(), + SET_HIGHLIGHT_ALPHA => { + this.set_highlight_alpha(activation, args.get(0))?; + Value::Undefined + } + GET_SHADOW_COLOR => this.shadow_color().into(), + SET_SHADOW_COLOR => { + this.set_shadow_color(activation, args.get(0))?; + Value::Undefined + } + GET_SHADOW_ALPHA => this.shadow_alpha().into(), + SET_SHADOW_ALPHA => { + this.set_shadow_alpha(activation, args.get(0))?; + Value::Undefined + } + GET_QUALITY => this.quality().into(), + SET_QUALITY => { + this.set_quality(activation, args.get(0))?; + Value::Undefined + } + GET_STRENGTH => this.strength().into(), + SET_STRENGTH => { + this.set_strength(activation, args.get(0))?; + Value::Undefined + } + GET_KNOCKOUT => this.knockout().into(), + SET_KNOCKOUT => { + this.set_knockout(activation, args.get(0))?; + Value::Undefined + } + GET_BLUR_X => this.blur_x().into(), + SET_BLUR_X => { + this.set_blur_x(activation, args.get(0))?; + Value::Undefined + } + GET_BLUR_Y => this.blur_y().into(), + SET_BLUR_Y => { + this.set_blur_y(activation, args.get(0))?; + Value::Undefined + } + GET_TYPE => { + let type_: &WStr = this.type_().into(); + AvmString::from(type_).into() + } + SET_TYPE => { + this.set_type(activation, args.get(0))?; + Value::Undefined + } + _ => Value::Undefined, + }) } -pub fn highlight_color<'gc>( - _activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - if let Some(filter) = this.as_bevel_filter_object() { - return Ok(filter.highlight_color().into()); - } - - Ok(Value::Undefined) -} - -pub fn set_highlight_color<'gc>( - activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - let highlight_color = args - .get(0) - .unwrap_or(&0xFFFFFF.into()) - .coerce_to_u32(activation)?; - - if let Some(filter) = this.as_bevel_filter_object() { - filter.set_highlight_color(activation.context.gc_context, highlight_color & 0xFFFFFF); - } - - Ok(Value::Undefined) -} - -pub fn highlight_alpha<'gc>( - _activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - if let Some(filter) = this.as_bevel_filter_object() { - return Ok(filter.highlight_alpha().into()); - } - - Ok(Value::Undefined) -} - -pub fn set_highlight_alpha<'gc>( - activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - let highlight_alpha = args - .get(0) - .unwrap_or(&1.into()) - .coerce_to_f64(activation) - .map(|x| x.clamp(0.0, 1.0))?; - - if let Some(filter) = this.as_bevel_filter_object() { - filter.set_highlight_alpha(activation.context.gc_context, highlight_alpha); - } - - Ok(Value::Undefined) -} - -pub fn shadow_color<'gc>( - _activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - if let Some(filter) = this.as_bevel_filter_object() { - return Ok(filter.shadow_color().into()); - } - - Ok(Value::Undefined) -} - -pub fn set_shadow_color<'gc>( - activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - let color = args - .get(0) - .unwrap_or(&0x000000.into()) - .coerce_to_u32(activation)?; - - if let Some(filter) = this.as_bevel_filter_object() { - filter.set_shadow_color(activation.context.gc_context, color & 0xFFFFFF); - } - - Ok(Value::Undefined) -} - -pub fn shadow_alpha<'gc>( - _activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - if let Some(filter) = this.as_bevel_filter_object() { - return Ok(filter.shadow_alpha().into()); - } - - Ok(Value::Undefined) -} - -pub fn set_shadow_alpha<'gc>( - activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - let shadow_alpha = args - .get(0) - .unwrap_or(&1.into()) - .coerce_to_f64(activation) - .map(|x| x.clamp(0.0, 1.0))?; - - if let Some(filter) = this.as_bevel_filter_object() { - filter.set_shadow_alpha(activation.context.gc_context, shadow_alpha); - } - - Ok(Value::Undefined) -} - -pub fn quality<'gc>( - _activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - if let Some(filter) = this.as_bevel_filter_object() { - return Ok(filter.quality().into()); - } - - Ok(Value::Undefined) -} - -pub fn set_quality<'gc>( - activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - let quality = args - .get(0) - .unwrap_or(&1.into()) - .coerce_to_i32(activation) - .map(|x| x.clamp(0, 15))?; - - if let Some(filter) = this.as_bevel_filter_object() { - filter.set_quality(activation.context.gc_context, quality); - } - - Ok(Value::Undefined) -} - -pub fn strength<'gc>( - _activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - if let Some(filter) = this.as_bevel_filter_object() { - return Ok(filter.strength().into()); - } - - Ok(Value::Undefined) -} - -pub fn set_strength<'gc>( - activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - let strength = args - .get(0) - .unwrap_or(&1.into()) - .coerce_to_f64(activation) - .map(|x| x.clamp(0.0, 255.0))?; - - if let Some(filter) = this.as_bevel_filter_object() { - filter.set_strength(activation.context.gc_context, strength); - } - - Ok(Value::Undefined) -} - -pub fn knockout<'gc>( - _activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - if let Some(filter) = this.as_bevel_filter_object() { - return Ok(filter.knockout().into()); - } - - Ok(Value::Undefined) -} - -pub fn set_knockout<'gc>( - activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - let knockout = args - .get(0) - .unwrap_or(&false.into()) - .as_bool(activation.swf_version()); - - if let Some(filter) = this.as_bevel_filter_object() { - filter.set_knockout(activation.context.gc_context, knockout); - } - - Ok(Value::Undefined) -} - -pub fn blur_x<'gc>( - _activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - if let Some(filter) = this.as_bevel_filter_object() { - return Ok(filter.blur_x().into()); - } - - Ok(Value::Undefined) -} - -pub fn set_blur_x<'gc>( - activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - let blur_x = args - .get(0) - .unwrap_or(&4.into()) - .coerce_to_f64(activation) - .map(|x| x.clamp(0.0, 255.0))?; - - if let Some(filter) = this.as_bevel_filter_object() { - filter.set_blur_x(activation.context.gc_context, blur_x); - } - - Ok(Value::Undefined) -} - -pub fn blur_y<'gc>( - _activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - if let Some(filter) = this.as_bevel_filter_object() { - return Ok(filter.blur_y().into()); - } - - Ok(Value::Undefined) -} - -pub fn set_blur_y<'gc>( - activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - let blur_y = args - .get(0) - .unwrap_or(&4.into()) - .coerce_to_f64(activation) - .map(|x| x.clamp(0.0, 255.0))?; - - if let Some(filter) = this.as_bevel_filter_object() { - filter.set_blur_y(activation.context.gc_context, blur_y); - } - - Ok(Value::Undefined) -} - -pub fn get_type<'gc>( - _activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - _args: &[Value<'gc>], -) -> Result, Error<'gc>> { - if let Some(filter) = this.as_bevel_filter_object() { - let type_: &WStr = filter.get_type().into(); - return Ok(AvmString::from(type_).into()); - } - - Ok(Value::Undefined) -} - -pub fn set_type<'gc>( - activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, - args: &[Value<'gc>], -) -> Result, Error<'gc>> { - let type_: BevelFilterType = args - .get(0) - .unwrap_or(&"inner".into()) - .coerce_to_string(activation) - .map(|s| s.as_wstr().into())?; - - if let Some(filter) = this.as_bevel_filter_object() { - filter.set_type(activation.context.gc_context, type_); - } - - Ok(Value::Undefined) -} - -pub fn create_proto<'gc>( +pub fn create_constructor<'gc>( gc_context: MutationContext<'gc, '_>, proto: Object<'gc>, fn_proto: Object<'gc>, ) -> Object<'gc> { - let object = BevelFilterObject::empty_object(gc_context, proto); - let script_object = object.as_script_object().unwrap(); - define_properties_on(PROTO_DECLS, gc_context, script_object, fn_proto); - object.into() + let bevel_filter_proto = ScriptObject::new(gc_context, Some(proto)); + define_properties_on(PROTO_DECLS, gc_context, bevel_filter_proto, fn_proto); + FunctionObject::constructor( + gc_context, + Executable::Native(bevel_filter_method!(0)), + constructor_to_fn!(bevel_filter_method!(0)), + fn_proto, + bevel_filter_proto.into(), + ) } diff --git a/core/src/avm1/globals/bitmap_filter.rs b/core/src/avm1/globals/bitmap_filter.rs index 116b15e12..886e91312 100644 --- a/core/src/avm1/globals/bitmap_filter.rs +++ b/core/src/avm1/globals/bitmap_filter.rs @@ -29,6 +29,10 @@ pub fn clone<'gc>( activation.context.gc_context, blur_filter.read().clone(), )), + NativeObject::BevelFilter(bevel_filter) => NativeObject::BevelFilter(GcCell::allocate( + activation.context.gc_context, + bevel_filter.read().clone(), + )), _ => NativeObject::None, }; if !matches!(native, NativeObject::None) { @@ -48,46 +52,6 @@ pub fn clone<'gc>( return Ok(cloned.into()); } - if let Some(this) = this.as_bevel_filter_object() { - let proto = activation - .context - .avm1 - .prototypes() - .bevel_filter_constructor; - - let distance = this.get("distance", activation)?; - let angle = this.get("angle", activation)?; - let highlight_color = this.get("highlightColor", activation)?; - let highlight_alpha = this.get("highlightAlpha", activation)?; - let shadow_color = this.get("shadowColor", activation)?; - let shadow_alpha = this.get("shadowAlpha", activation)?; - let blur_x = this.get("blurX", activation)?; - let blur_y = this.get("blurY", activation)?; - let strength = this.get("strength", activation)?; - let quality = this.get("quality", activation)?; - let type_ = this.get("type", activation)?; - let knockout = this.get("knockout", activation)?; - - let cloned = proto.construct( - activation, - &[ - distance, - angle, - highlight_color, - highlight_alpha, - shadow_color, - shadow_alpha, - blur_x, - blur_y, - strength, - quality, - type_, - knockout, - ], - )?; - return Ok(cloned); - } - if let Some(this) = this.as_glow_filter_object() { let proto = activation.context.avm1.prototypes().glow_filter_constructor; diff --git a/core/src/avm1/globals/gradient_bevel_filter.rs b/core/src/avm1/globals/gradient_bevel_filter.rs index d8514c5a9..4f65aeeca 100644 --- a/core/src/avm1/globals/gradient_bevel_filter.rs +++ b/core/src/avm1/globals/gradient_bevel_filter.rs @@ -2,7 +2,7 @@ use crate::avm1::activation::Activation; use crate::avm1::error::Error; -use crate::avm1::object::bevel_filter::BevelFilterType; +use crate::avm1::globals::bevel_filter::BevelFilterType; use crate::avm1::object::gradient_bevel_filter::GradientBevelFilterObject; use crate::avm1::property_decl::{define_properties_on, Declaration}; use crate::avm1::{ArrayObject, Object, TObject, Value}; diff --git a/core/src/avm1/globals/gradient_glow_filter.rs b/core/src/avm1/globals/gradient_glow_filter.rs index e254c6140..6eae725f6 100644 --- a/core/src/avm1/globals/gradient_glow_filter.rs +++ b/core/src/avm1/globals/gradient_glow_filter.rs @@ -2,7 +2,7 @@ use crate::avm1::activation::Activation; use crate::avm1::error::Error; -use crate::avm1::object::bevel_filter::BevelFilterType; +use crate::avm1::globals::bevel_filter::BevelFilterType; use crate::avm1::object::gradient_glow_filter::GradientGlowFilterObject; use crate::avm1::property_decl::{define_properties_on, Declaration}; use crate::avm1::{ArrayObject, Object, TObject, Value}; diff --git a/core/src/avm1/object.rs b/core/src/avm1/object.rs index 6e6e50a7f..060550bad 100644 --- a/core/src/avm1/object.rs +++ b/core/src/avm1/object.rs @@ -1,11 +1,11 @@ //! Object trait to expose objects to AVM use crate::avm1::function::{Executable, ExecutionName, ExecutionReason, FunctionObject}; +use crate::avm1::globals::bevel_filter::BevelFilterObject; use crate::avm1::globals::blur_filter::BlurFilterObject; use crate::avm1::globals::color_transform::ColorTransformObject; use crate::avm1::globals::date::Date; use crate::avm1::object::array_object::ArrayObject; -use crate::avm1::object::bevel_filter::BevelFilterObject; use crate::avm1::object::bitmap_data::BitmapDataObject; use crate::avm1::object::color_matrix_filter::ColorMatrixFilterObject; use crate::avm1::object::convolution_filter::ConvolutionFilterObject; @@ -30,7 +30,6 @@ use ruffle_macros::enum_trait_object; use std::fmt::Debug; pub mod array_object; -pub mod bevel_filter; pub mod bitmap_data; pub mod color_matrix_filter; pub mod convolution_filter; @@ -56,6 +55,7 @@ pub enum NativeObject<'gc> { None, Date(GcCell<'gc, Date>), BlurFilter(GcCell<'gc, BlurFilterObject>), + BevelFilter(GcCell<'gc, BevelFilterObject>), ColorTransform(GcCell<'gc, ColorTransformObject>), TextFormat(GcCell<'gc, TextFormat>), } @@ -78,7 +78,6 @@ pub enum NativeObject<'gc> { FunctionObject(FunctionObject<'gc>), SharedObject(SharedObject<'gc>), TransformObject(TransformObject<'gc>), - BevelFilterObject(BevelFilterObject<'gc>), GlowFilterObject(GlowFilterObject<'gc>), DropShadowFilterObject(DropShadowFilterObject<'gc>), ColorMatrixFilterObject(ColorMatrixFilterObject<'gc>), @@ -556,11 +555,6 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into> + Clone + Copy None } - /// Get the underlying `BevelFilterObject`, if it exists - fn as_bevel_filter_object(&self) -> Option> { - None - } - /// Get the underlying `GlowFilterObject`, if it exists fn as_glow_filter_object(&self) -> Option> { None diff --git a/core/src/avm1/object/bevel_filter.rs b/core/src/avm1/object/bevel_filter.rs deleted file mode 100644 index cefa55faf..000000000 --- a/core/src/avm1/object/bevel_filter.rs +++ /dev/null @@ -1,126 +0,0 @@ -use crate::add_field_accessors; -use crate::avm1::{Object, ScriptObject, TObject}; -use crate::impl_custom_object; -use crate::string::WStr; -use gc_arena::{Collect, GcCell, MutationContext}; - -use std::fmt; - -#[derive(Copy, Clone, Debug, Collect)] -#[collect(no_drop)] -pub enum BevelFilterType { - Inner, - Outer, - Full, -} - -impl<'a> From<&'a WStr> for BevelFilterType { - fn from(value: &'a WStr) -> Self { - if value == b"inner" { - BevelFilterType::Inner - } else if value == b"outer" { - BevelFilterType::Outer - } else { - BevelFilterType::Full - } - } -} - -impl From for &'static WStr { - fn from(v: BevelFilterType) -> &'static WStr { - let s: &[u8] = match v { - BevelFilterType::Inner => b"inner", - BevelFilterType::Outer => b"outer", - BevelFilterType::Full => b"full", - }; - WStr::from_units(s) - } -} - -/// A BevelFilter -#[derive(Clone, Copy, Collect)] -#[collect(no_drop)] -pub struct BevelFilterObject<'gc>(GcCell<'gc, BevelFilterData<'gc>>); - -#[derive(Clone, Collect)] -#[collect(no_drop)] -pub struct BevelFilterData<'gc> { - /// The underlying script object. - base: ScriptObject<'gc>, - - angle: f64, - blur_x: f64, - blur_y: f64, - distance: f64, - highlight_alpha: f64, - highlight_color: u32, - knockout: bool, - quality: i32, - shadow_alpha: f64, - shadow_color: u32, - strength: f64, - type_: BevelFilterType, -} - -impl fmt::Debug for BevelFilterObject<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let this = self.0.read(); - f.debug_struct("BevelFilter") - .field("angle", &this.angle) - .field("blurX", &this.blur_x) - .field("blurY", &this.blur_y) - .field("distance", &this.distance) - .field("highlightAlpha", &this.highlight_alpha) - .field("highlightColor", &this.highlight_color) - .field("knockout", &this.knockout) - .field("quality", &this.quality) - .field("shadowAlpha", &this.shadow_alpha) - .field("strength", &this.strength) - .field("type", &this.type_) - .finish() - } -} - -impl<'gc> BevelFilterObject<'gc> { - add_field_accessors!( - [set_angle, angle, angle, f64], - [set_blur_x, blur_x, blur_x, f64], - [set_blur_y, blur_y, blur_y, f64], - [set_distance, distance, distance, f64], - [set_highlight_alpha, highlight_alpha, highlight_alpha, f64], - [set_highlight_color, highlight_color, highlight_color, u32], - [set_knockout, knockout, knockout, bool], - [set_quality, quality, quality, i32], - [set_shadow_alpha, shadow_alpha, shadow_alpha, f64], - [set_shadow_color, shadow_color, shadow_color, u32], - [set_strength, strength, strength, f64], - [set_type, get_type, type_, BevelFilterType], - ); - - pub fn empty_object(gc_context: MutationContext<'gc, '_>, proto: Object<'gc>) -> Self { - BevelFilterObject(GcCell::allocate( - gc_context, - BevelFilterData { - base: ScriptObject::new(gc_context, Some(proto)), - angle: 44.9999999772279, - blur_x: 4.0, - blur_y: 4.0, - distance: 4.0, - highlight_alpha: 1.0, - highlight_color: 0xFFFFFF, - knockout: false, - quality: 1, - shadow_alpha: 1.0, - shadow_color: 0x000000, - strength: 1.0, - type_: BevelFilterType::Inner, - }, - )) - } -} - -impl<'gc> TObject<'gc> for BevelFilterObject<'gc> { - impl_custom_object!(base { - bare_object(as_bevel_filter_object -> BevelFilterObject::empty_object); - }); -} diff --git a/core/src/avm1/object/gradient_bevel_filter.rs b/core/src/avm1/object/gradient_bevel_filter.rs index aff6b5a95..be96e2ccd 100644 --- a/core/src/avm1/object/gradient_bevel_filter.rs +++ b/core/src/avm1/object/gradient_bevel_filter.rs @@ -1,9 +1,8 @@ use crate::add_field_accessors; +use crate::avm1::globals::bevel_filter::BevelFilterType; use crate::avm1::{Object, ScriptObject, TObject}; use crate::impl_custom_object; use gc_arena::{Collect, GcCell, MutationContext}; - -use crate::avm1::object::bevel_filter::BevelFilterType; use std::fmt; /// A GradientBevelFilter diff --git a/core/src/avm1/object/gradient_glow_filter.rs b/core/src/avm1/object/gradient_glow_filter.rs index f97a2cc75..c0c9d2e3f 100644 --- a/core/src/avm1/object/gradient_glow_filter.rs +++ b/core/src/avm1/object/gradient_glow_filter.rs @@ -1,9 +1,8 @@ use crate::add_field_accessors; +use crate::avm1::globals::bevel_filter::BevelFilterType; use crate::avm1::{Object, ScriptObject, TObject}; use crate::impl_custom_object; use gc_arena::{Collect, GcCell, MutationContext}; - -use crate::avm1::object::bevel_filter::BevelFilterType; use std::fmt; /// A GradientGlowFilter