diff --git a/core/src/avm1/globals/bevel_filter.rs b/core/src/avm1/globals/bevel_filter.rs index b42a2d22e..00c6179b0 100644 --- a/core/src/avm1/globals/bevel_filter.rs +++ b/core/src/avm1/globals/bevel_filter.rs @@ -7,7 +7,8 @@ use crate::avm1::{Activation, Error, Object, ScriptObject, TObject, Value}; use crate::context::GcContext; use crate::string::{AvmString, WStr}; use gc_arena::{Collect, GcCell, MutationContext}; -use swf::Color; +use std::ops::Deref; +use swf::{BevelFilterFlags, Color, Fixed16, Fixed8}; #[derive(Copy, Clone, Debug, Collect)] #[collect(no_drop)] @@ -39,6 +40,28 @@ impl From for &'static WStr { } } +impl BevelFilterType { + pub fn as_flags(&self) -> BevelFilterFlags { + match self { + BevelFilterType::Inner => BevelFilterFlags::INNER_SHADOW, + BevelFilterType::Outer => BevelFilterFlags::empty(), + BevelFilterType::Full => BevelFilterFlags::ON_TOP, + } + } +} + +impl From for BevelFilterType { + fn from(value: BevelFilterFlags) -> Self { + if value.contains(BevelFilterFlags::ON_TOP) { + BevelFilterType::Full + } else if value.contains(BevelFilterFlags::INNER_SHADOW) { + BevelFilterType::Inner + } else { + BevelFilterType::Outer + } + } +} + #[derive(Clone, Debug, Collect)] #[collect(require_static)] struct BevelFilterData { @@ -56,6 +79,42 @@ struct BevelFilterData { type_: BevelFilterType, } +impl From<&BevelFilterData> for swf::BevelFilter { + fn from(filter: &BevelFilterData) -> swf::BevelFilter { + let mut flags = BevelFilterFlags::COMPOSITE_SOURCE; + flags |= BevelFilterFlags::from_passes(filter.quality as u8); + flags |= filter.type_.as_flags(); + flags.set(BevelFilterFlags::KNOCKOUT, filter.knockout); + swf::BevelFilter { + shadow_color: filter.shadow, + highlight_color: filter.highlight, + blur_x: Fixed16::from_f64(filter.blur_x), + blur_y: Fixed16::from_f64(filter.blur_y), + angle: Fixed16::from_f64(filter.angle), + distance: Fixed16::from_f64(filter.distance), + strength: Fixed8::from_f64(filter.strength()), + flags, + } + } +} + +impl From for BevelFilterData { + fn from(filter: swf::BevelFilter) -> BevelFilterData { + Self { + distance: filter.distance.into(), + angle: filter.angle.into(), + highlight: filter.highlight_color, + shadow: filter.shadow_color, + quality: filter.num_passes().into(), + strength: (filter.strength.to_f64() * 256.0) as u16, + knockout: filter.is_knockout(), + blur_x: filter.blur_x.into(), + blur_y: filter.blur_y.into(), + type_: filter.flags.into(), + } + } +} + impl Default for BevelFilterData { #[allow(clippy::approx_constant)] fn default() -> Self { @@ -110,6 +169,10 @@ impl<'gc> BevelFilter<'gc> { Ok(bevel_filter) } + pub fn from_filter(gc_context: MutationContext<'gc, '_>, filter: swf::BevelFilter) -> Self { + Self(GcCell::allocate(gc_context, filter.into())) + } + pub(crate) fn duplicate(&self, gc_context: MutationContext<'gc, '_>) -> Self { Self(GcCell::allocate(gc_context, self.0.read().clone())) } @@ -306,6 +369,10 @@ impl<'gc> BevelFilter<'gc> { } Ok(()) } + + pub fn filter(&self) -> swf::BevelFilter { + self.0.read().deref().into() + } } macro_rules! bevel_filter_method { diff --git a/core/src/avm1/globals/bitmap_filter.rs b/core/src/avm1/globals/bitmap_filter.rs index 5a2ff2b13..1ddd40ffa 100644 --- a/core/src/avm1/globals/bitmap_filter.rs +++ b/core/src/avm1/globals/bitmap_filter.rs @@ -2,6 +2,7 @@ use crate::avm1::activation::Activation; use crate::avm1::error::Error; +use crate::avm1::globals::bevel_filter::BevelFilter; use crate::avm1::object::NativeObject; use crate::avm1::property_decl::{define_properties_on, Declaration}; use crate::avm1::{Attribute, Object, ScriptObject, TObject, Value}; @@ -63,19 +64,33 @@ pub fn clone<'gc>( Ok(create_instance(activation, native, proto).into()) } -pub fn avm1_to_filter(_object: Object) -> Option { - // TODO +pub fn avm1_to_filter(object: Object) -> Option { + let native = object.native(); + match native { + NativeObject::BevelFilter(filter) => Some(Filter::BevelFilter(filter.filter())), - // Invalid filters are silently dropped/ignored, no errors are thrown. - None + // Invalid filters are silently dropped/ignored, no errors are thrown. + _ => None, + } } -pub fn filter_to_avm1<'gc>(_activation: &mut Activation<'_, 'gc>, _filter: Filter) -> Value<'gc> { - // TODO +pub fn filter_to_avm1<'gc>(activation: &mut Activation<'_, 'gc>, filter: Filter) -> Value<'gc> { + let (native, proto) = match filter { + Filter::BevelFilter(filter) => ( + NativeObject::BevelFilter(BevelFilter::from_filter( + activation.context.gc_context, + filter, + )), + activation.context.avm1.prototypes().bevel_filter, + ), + _ => { + // Unrepresentable filters (eg Shader) will just return as Null. + // Not sure there's a way to even get to that state though, they can only be added in avm2. + return Value::Null; + } + }; - // Unrepresentable filters (eg Shader) will just return as Null. - // Not sure there's a way to even get to that state though, they can only be added in avm2. - Value::Null + create_instance(activation, native, Some(proto.into())).into() } pub fn create_instance<'gc>( diff --git a/tests/tests/swfs/avm1/bevel_filter/output.txt b/tests/tests/swfs/avm1/bevel_filter/output.txt index 8d2cee918..d397ff230 100644 --- a/tests/tests/swfs/avm1/bevel_filter/output.txt +++ b/tests/tests/swfs/avm1/bevel_filter/output.txt @@ -1,3 +1,28 @@ +// test.filters[0].angle +44.999253346525 +// test.filters[0].blurX +10 +// test.filters[0].blurY +10 +// test.filters[0].distance +10 +// test.filters[0].highlightAlpha +1 +// test.filters[0].highlightColor +65280 +// test.filters[0].knockout +false +// test.filters[0].quality +2 +// test.filters[0].shadowAlpha +1 +// test.filters[0].shadowColor +255 +// test.filters[0].strength +1.5 +// test.filters[0].type +inner + // new BevelFilter [object Object] // x.clone() @@ -28,6 +53,33 @@ false 1 // x.type inner + +test.filters = [x] +// test.filters[0].angle +44.9999999772279 +// test.filters[0].blurX +4 +// test.filters[0].blurY +4 +// test.filters[0].distance +4 +// test.filters[0].highlightAlpha +1 +// test.filters[0].highlightColor +16777215 +// test.filters[0].knockout +false +// test.filters[0].quality +1 +// test.filters[0].shadowAlpha +1 +// test.filters[0].shadowColor +0 +// test.filters[0].strength +1 +// test.filters[0].type +inner + // x.angle (after set to 360) 0 // x.angle (after set to 361) diff --git a/tests/tests/swfs/avm1/bevel_filter/test.fla b/tests/tests/swfs/avm1/bevel_filter/test.fla index 9b287e00a..1056b8da8 100644 Binary files a/tests/tests/swfs/avm1/bevel_filter/test.fla and b/tests/tests/swfs/avm1/bevel_filter/test.fla differ diff --git a/tests/tests/swfs/avm1/bevel_filter/test.swf b/tests/tests/swfs/avm1/bevel_filter/test.swf index 0151b3911..fb0994b0e 100644 Binary files a/tests/tests/swfs/avm1/bevel_filter/test.swf and b/tests/tests/swfs/avm1/bevel_filter/test.swf differ diff --git a/tests/tests/swfs/avm1/bevel_filter/test.toml b/tests/tests/swfs/avm1/bevel_filter/test.toml index dbee897f5..6c6a402e8 100644 --- a/tests/tests/swfs/avm1/bevel_filter/test.toml +++ b/tests/tests/swfs/avm1/bevel_filter/test.toml @@ -1 +1,4 @@ num_frames = 1 + +[approximations] +epsilon = 0.001 \ No newline at end of file