avm1: Implement MovieClip.filters

This commit is contained in:
Nathan Adams 2023-06-30 18:12:42 +02:00
parent 681ff3dd24
commit 2b48f579e6
11 changed files with 211 additions and 44 deletions

View File

@ -499,6 +499,15 @@ pub struct SystemPrototypes<'gc> {
pub bitmap_data: Object<'gc>, pub bitmap_data: Object<'gc>,
pub video: Object<'gc>, pub video: Object<'gc>,
pub video_constructor: Object<'gc>, pub video_constructor: Object<'gc>,
pub blur_filter: Object<'gc>,
pub bevel_filter: Object<'gc>,
pub glow_filter: Object<'gc>,
pub drop_shadow_filter: Object<'gc>,
pub color_matrix_filter: Object<'gc>,
pub displacement_map_filter: Object<'gc>,
pub convolution_filter: Object<'gc>,
pub gradient_bevel_filter: Object<'gc>,
pub gradient_glow_filter: Object<'gc>,
} }
/// Initialize default global scope and builtins for an AVM1 instance. /// Initialize default global scope and builtins for an AVM1 instance.
@ -728,30 +737,50 @@ pub fn create_globals<'gc>(
bitmap_filter_proto, bitmap_filter_proto,
); );
let blur_filter = blur_filter::create_constructor(context, bitmap_filter_proto, function_proto); let blur_filter = blur_filter::create_proto(context, bitmap_filter_proto, function_proto);
let blur_filter_constructor =
blur_filter::create_constructor(context, blur_filter, function_proto);
let bevel_filter = let bevel_filter = bevel_filter::create_proto(context, bitmap_filter_proto, function_proto);
bevel_filter::create_constructor(context, bitmap_filter_proto, function_proto); let bevel_filter_constructor =
bevel_filter::create_constructor(context, bevel_filter, function_proto);
let glow_filter = glow_filter::create_constructor(context, bitmap_filter_proto, function_proto); let glow_filter = glow_filter::create_proto(context, bitmap_filter_proto, function_proto);
let glow_filter_constructor =
glow_filter::create_constructor(context, glow_filter, function_proto);
let drop_shadow_filter = let drop_shadow_filter =
drop_shadow_filter::create_constructor(context, bitmap_filter_proto, function_proto); drop_shadow_filter::create_proto(context, bitmap_filter_proto, function_proto);
let drop_shadow_filter_constructor =
drop_shadow_filter::create_constructor(context, drop_shadow_filter, function_proto);
let color_matrix_filter = let color_matrix_filter =
color_matrix_filter::create_constructor(context, bitmap_filter_proto, function_proto); color_matrix_filter::create_proto(context, bitmap_filter_proto, function_proto);
let color_matrix_filter_constructor =
color_matrix_filter::create_constructor(context, color_matrix_filter, function_proto);
let displacement_map_filter = let displacement_map_filter =
displacement_map_filter::create_constructor(context, bitmap_filter_proto, function_proto); displacement_map_filter::create_proto(context, bitmap_filter_proto, function_proto);
let displacement_map_filter_constructor = displacement_map_filter::create_constructor(
context,
displacement_map_filter,
function_proto,
);
let convolution_filter = let convolution_filter =
convolution_filter::create_constructor(context, bitmap_filter_proto, function_proto); convolution_filter::create_proto(context, bitmap_filter_proto, function_proto);
let convolution_filter_constructor =
convolution_filter::create_constructor(context, convolution_filter, function_proto);
let gradient_bevel_filter = let gradient_bevel_filter =
gradient_filter::create_bevel_constructor(context, bitmap_filter_proto, function_proto); gradient_filter::create_bevel_proto(context, bitmap_filter_proto, function_proto);
let gradient_bevel_filter_constructor =
gradient_filter::create_bevel_constructor(context, gradient_bevel_filter, function_proto);
let gradient_glow_filter = let gradient_glow_filter =
gradient_filter::create_glow_constructor(context, bitmap_filter_proto, function_proto); gradient_filter::create_glow_proto(context, bitmap_filter_proto, function_proto);
let gradient_glow_filter_constructor =
gradient_filter::create_glow_constructor(context, gradient_glow_filter, function_proto);
filters.define_value( filters.define_value(
gc_context, gc_context,
@ -762,56 +791,56 @@ pub fn create_globals<'gc>(
filters.define_value( filters.define_value(
gc_context, gc_context,
"BlurFilter", "BlurFilter",
blur_filter.into(), blur_filter_constructor.into(),
Attribute::empty(), Attribute::empty(),
); );
filters.define_value( filters.define_value(
gc_context, gc_context,
"BevelFilter", "BevelFilter",
bevel_filter.into(), bevel_filter_constructor.into(),
Attribute::empty(), Attribute::empty(),
); );
filters.define_value( filters.define_value(
gc_context, gc_context,
"GlowFilter", "GlowFilter",
glow_filter.into(), glow_filter_constructor.into(),
Attribute::empty(), Attribute::empty(),
); );
filters.define_value( filters.define_value(
gc_context, gc_context,
"DropShadowFilter", "DropShadowFilter",
drop_shadow_filter.into(), drop_shadow_filter_constructor.into(),
Attribute::empty(), Attribute::empty(),
); );
filters.define_value( filters.define_value(
gc_context, gc_context,
"ColorMatrixFilter", "ColorMatrixFilter",
color_matrix_filter.into(), color_matrix_filter_constructor.into(),
Attribute::empty(), Attribute::empty(),
); );
filters.define_value( filters.define_value(
gc_context, gc_context,
"DisplacementMapFilter", "DisplacementMapFilter",
displacement_map_filter.into(), displacement_map_filter_constructor.into(),
Attribute::empty(), Attribute::empty(),
); );
filters.define_value( filters.define_value(
gc_context, gc_context,
"ConvolutionFilter", "ConvolutionFilter",
convolution_filter.into(), convolution_filter_constructor.into(),
Attribute::empty(), Attribute::empty(),
); );
filters.define_value( filters.define_value(
gc_context, gc_context,
"GradientBevelFilter", "GradientBevelFilter",
gradient_bevel_filter.into(), gradient_bevel_filter_constructor.into(),
Attribute::empty(), Attribute::empty(),
); );
filters.define_value( filters.define_value(
gc_context, gc_context,
"GradientGlowFilter", "GradientGlowFilter",
gradient_glow_filter.into(), gradient_glow_filter_constructor.into(),
Attribute::empty(), Attribute::empty(),
); );
@ -1073,6 +1102,15 @@ pub fn create_globals<'gc>(
bitmap_data: bitmap_data_proto.into(), bitmap_data: bitmap_data_proto.into(),
video: video_proto, video: video_proto,
video_constructor: video, video_constructor: video,
blur_filter,
bevel_filter,
glow_filter,
drop_shadow_filter,
color_matrix_filter,
displacement_map_filter,
convolution_filter,
gradient_bevel_filter,
gradient_glow_filter,
}, },
globals.into(), globals.into(),
broadcaster_functions, broadcaster_functions,

View File

@ -443,18 +443,26 @@ fn method<'gc>(
}) })
} }
pub fn create_constructor<'gc>( pub fn create_proto<'gc>(
context: &mut GcContext<'_, 'gc>, context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>, proto: Object<'gc>,
fn_proto: Object<'gc>, fn_proto: Object<'gc>,
) -> Object<'gc> { ) -> Object<'gc> {
let bevel_filter_proto = ScriptObject::new(context.gc_context, Some(proto)); let bevel_filter_proto = ScriptObject::new(context.gc_context, Some(proto));
define_properties_on(PROTO_DECLS, context, bevel_filter_proto, fn_proto); define_properties_on(PROTO_DECLS, context, bevel_filter_proto, fn_proto);
bevel_filter_proto.into()
}
pub fn create_constructor<'gc>(
context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>,
fn_proto: Object<'gc>,
) -> Object<'gc> {
FunctionObject::constructor( FunctionObject::constructor(
context.gc_context, context.gc_context,
Executable::Native(bevel_filter_method!(0)), Executable::Native(bevel_filter_method!(0)),
constructor_to_fn!(bevel_filter_method!(0)), constructor_to_fn!(bevel_filter_method!(0)),
fn_proto, fn_proto,
bevel_filter_proto.into(), proto,
) )
} }

View File

@ -6,6 +6,7 @@ use crate::avm1::object::NativeObject;
use crate::avm1::property_decl::{define_properties_on, Declaration}; use crate::avm1::property_decl::{define_properties_on, Declaration};
use crate::avm1::{Attribute, Object, ScriptObject, TObject, Value}; use crate::avm1::{Attribute, Object, ScriptObject, TObject, Value};
use crate::context::GcContext; use crate::context::GcContext;
use ruffle_render::filters::Filter;
const PROTO_DECLS: &[Declaration] = declare_properties! { const PROTO_DECLS: &[Declaration] = declare_properties! {
"clone" => method(clone); "clone" => method(clone);
@ -59,19 +60,42 @@ pub fn clone<'gc>(
_ => return Ok(Value::Undefined), _ => return Ok(Value::Undefined),
}; };
let proto = this.get_local_stored("__proto__", activation); let proto = this.get_local_stored("__proto__", activation);
let cloned = ScriptObject::new(activation.context.gc_context, None); Ok(create_instance(activation, native, proto).into())
}
pub fn avm1_to_filter(_object: Object) -> Option<Filter> {
// TODO
// 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
// 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
}
pub fn create_instance<'gc>(
activation: &mut Activation<'_, 'gc>,
native: NativeObject<'gc>,
proto: Option<Value<'gc>>,
) -> ScriptObject<'gc> {
let result = ScriptObject::new(activation.context.gc_context, None);
// Set `__proto__` manually since `ScriptObject::new()` doesn't support primitive prototypes. // Set `__proto__` manually since `ScriptObject::new()` doesn't support primitive prototypes.
// TODO: Pass `proto` to `ScriptObject::new()` once possible. // TODO: Pass `proto` to `ScriptObject::new()` once possible.
if let Some(proto) = proto { if let Some(proto) = proto {
cloned.define_value( result.define_value(
activation.context.gc_context, activation.context.gc_context,
"__proto__", "__proto__",
proto, proto,
Attribute::DONT_ENUM | Attribute::DONT_DELETE, Attribute::DONT_ENUM | Attribute::DONT_DELETE,
); );
} }
cloned.set_native(activation.context.gc_context, native); result.set_native(activation.context.gc_context, native);
Ok(cloned.into()) result
} }
pub fn create_proto<'gc>( pub fn create_proto<'gc>(

View File

@ -155,18 +155,26 @@ fn method<'gc>(
}) })
} }
pub fn create_constructor<'gc>( pub fn create_proto<'gc>(
context: &mut GcContext<'_, 'gc>, context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>, proto: Object<'gc>,
fn_proto: Object<'gc>, fn_proto: Object<'gc>,
) -> Object<'gc> { ) -> Object<'gc> {
let blur_filter_proto = ScriptObject::new(context.gc_context, Some(proto)); let blur_filter_proto = ScriptObject::new(context.gc_context, Some(proto));
define_properties_on(PROTO_DECLS, context, blur_filter_proto, fn_proto); define_properties_on(PROTO_DECLS, context, blur_filter_proto, fn_proto);
blur_filter_proto.into()
}
pub fn create_constructor<'gc>(
context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>,
fn_proto: Object<'gc>,
) -> Object<'gc> {
FunctionObject::constructor( FunctionObject::constructor(
context.gc_context, context.gc_context,
Executable::Native(blur_filter_method!(0)), Executable::Native(blur_filter_method!(0)),
constructor_to_fn!(blur_filter_method!(0)), constructor_to_fn!(blur_filter_method!(0)),
fn_proto, fn_proto,
blur_filter_proto.into(), proto,
) )
} }

View File

@ -121,18 +121,26 @@ fn method<'gc>(
}) })
} }
pub fn create_constructor<'gc>( pub fn create_proto<'gc>(
context: &mut GcContext<'_, 'gc>, context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>, proto: Object<'gc>,
fn_proto: Object<'gc>, fn_proto: Object<'gc>,
) -> Object<'gc> { ) -> Object<'gc> {
let color_matrix_filter_proto = ScriptObject::new(context.gc_context, Some(proto)); let color_matrix_filter_proto = ScriptObject::new(context.gc_context, Some(proto));
define_properties_on(PROTO_DECLS, context, color_matrix_filter_proto, fn_proto); define_properties_on(PROTO_DECLS, context, color_matrix_filter_proto, fn_proto);
color_matrix_filter_proto.into()
}
pub fn create_constructor<'gc>(
context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>,
fn_proto: Object<'gc>,
) -> Object<'gc> {
FunctionObject::constructor( FunctionObject::constructor(
context.gc_context, context.gc_context,
Executable::Native(color_matrix_filter_method!(0)), Executable::Native(color_matrix_filter_method!(0)),
constructor_to_fn!(color_matrix_filter_method!(0)), constructor_to_fn!(color_matrix_filter_method!(0)),
fn_proto, fn_proto,
color_matrix_filter_proto.into(), proto,
) )
} }

View File

@ -381,18 +381,26 @@ fn method<'gc>(
}) })
} }
pub fn create_constructor<'gc>( pub fn create_proto<'gc>(
context: &mut GcContext<'_, 'gc>, context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>, proto: Object<'gc>,
fn_proto: Object<'gc>, fn_proto: Object<'gc>,
) -> Object<'gc> { ) -> Object<'gc> {
let convolution_filter_proto = ScriptObject::new(context.gc_context, Some(proto)); let convolution_filter_proto = ScriptObject::new(context.gc_context, Some(proto));
define_properties_on(PROTO_DECLS, context, convolution_filter_proto, fn_proto); define_properties_on(PROTO_DECLS, context, convolution_filter_proto, fn_proto);
convolution_filter_proto.into()
}
pub fn create_constructor<'gc>(
context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>,
fn_proto: Object<'gc>,
) -> Object<'gc> {
FunctionObject::constructor( FunctionObject::constructor(
context.gc_context, context.gc_context,
Executable::Native(convolution_filter_method!(0)), Executable::Native(convolution_filter_method!(0)),
constructor_to_fn!(convolution_filter_method!(0)), constructor_to_fn!(convolution_filter_method!(0)),
fn_proto, fn_proto,
convolution_filter_proto.into(), proto,
) )
} }

View File

@ -366,7 +366,7 @@ fn method<'gc>(
}) })
} }
pub fn create_constructor<'gc>( pub fn create_proto<'gc>(
context: &mut GcContext<'_, 'gc>, context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>, proto: Object<'gc>,
fn_proto: Object<'gc>, fn_proto: Object<'gc>,
@ -378,11 +378,19 @@ pub fn create_constructor<'gc>(
displacement_map_filter_proto, displacement_map_filter_proto,
fn_proto, fn_proto,
); );
displacement_map_filter_proto.into()
}
pub fn create_constructor<'gc>(
context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>,
fn_proto: Object<'gc>,
) -> Object<'gc> {
FunctionObject::constructor( FunctionObject::constructor(
context.gc_context, context.gc_context,
Executable::Native(displacement_map_filter_method!(0)), Executable::Native(displacement_map_filter_method!(0)),
constructor_to_fn!(displacement_map_filter_method!(0)), constructor_to_fn!(displacement_map_filter_method!(0)),
fn_proto, fn_proto,
displacement_map_filter_proto.into(), proto,
) )
} }

View File

@ -384,18 +384,26 @@ fn method<'gc>(
}) })
} }
pub fn create_constructor<'gc>( pub fn create_proto<'gc>(
context: &mut GcContext<'_, 'gc>, context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>, proto: Object<'gc>,
fn_proto: Object<'gc>, fn_proto: Object<'gc>,
) -> Object<'gc> { ) -> Object<'gc> {
let drop_shadow_filter_proto = ScriptObject::new(context.gc_context, Some(proto)); let drop_shadow_filter_proto = ScriptObject::new(context.gc_context, Some(proto));
define_properties_on(PROTO_DECLS, context, drop_shadow_filter_proto, fn_proto); define_properties_on(PROTO_DECLS, context, drop_shadow_filter_proto, fn_proto);
drop_shadow_filter_proto.into()
}
pub fn create_constructor<'gc>(
context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>,
fn_proto: Object<'gc>,
) -> Object<'gc> {
FunctionObject::constructor( FunctionObject::constructor(
context.gc_context, context.gc_context,
Executable::Native(drop_shadow_filter_method!(0)), Executable::Native(drop_shadow_filter_method!(0)),
constructor_to_fn!(drop_shadow_filter_method!(0)), constructor_to_fn!(drop_shadow_filter_method!(0)),
fn_proto, fn_proto,
drop_shadow_filter_proto.into(), proto,
) )
} }

View File

@ -301,18 +301,26 @@ fn method<'gc>(
}) })
} }
pub fn create_constructor<'gc>( pub fn create_proto<'gc>(
context: &mut GcContext<'_, 'gc>, context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>, proto: Object<'gc>,
fn_proto: Object<'gc>, fn_proto: Object<'gc>,
) -> Object<'gc> { ) -> Object<'gc> {
let glow_filter_proto = ScriptObject::new(context.gc_context, Some(proto)); let glow_filter_proto = ScriptObject::new(context.gc_context, Some(proto));
define_properties_on(PROTO_DECLS, context, glow_filter_proto, fn_proto); define_properties_on(PROTO_DECLS, context, glow_filter_proto, fn_proto);
glow_filter_proto.into()
}
pub fn create_constructor<'gc>(
context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>,
fn_proto: Object<'gc>,
) -> Object<'gc> {
FunctionObject::constructor( FunctionObject::constructor(
context.gc_context, context.gc_context,
Executable::Native(glow_filter_method!(0)), Executable::Native(glow_filter_method!(0)),
constructor_to_fn!(glow_filter_method!(0)), constructor_to_fn!(glow_filter_method!(0)),
fn_proto, fn_proto,
glow_filter_proto.into(), proto,
) )
} }

View File

@ -456,34 +456,50 @@ fn method<'gc>(
}) })
} }
pub fn create_bevel_constructor<'gc>( pub fn create_bevel_proto<'gc>(
context: &mut GcContext<'_, 'gc>, context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>, proto: Object<'gc>,
fn_proto: Object<'gc>, fn_proto: Object<'gc>,
) -> Object<'gc> { ) -> Object<'gc> {
let gradient_bevel_filter_proto = ScriptObject::new(context.gc_context, Some(proto)); let gradient_bevel_filter_proto = ScriptObject::new(context.gc_context, Some(proto));
define_properties_on(PROTO_DECLS, context, gradient_bevel_filter_proto, fn_proto); define_properties_on(PROTO_DECLS, context, gradient_bevel_filter_proto, fn_proto);
gradient_bevel_filter_proto.into()
}
pub fn create_bevel_constructor<'gc>(
context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>,
fn_proto: Object<'gc>,
) -> Object<'gc> {
FunctionObject::constructor( FunctionObject::constructor(
context.gc_context, context.gc_context,
Executable::Native(gradient_filter_method!(1000)), Executable::Native(gradient_filter_method!(1000)),
constructor_to_fn!(gradient_filter_method!(1000)), constructor_to_fn!(gradient_filter_method!(1000)),
fn_proto, fn_proto,
gradient_bevel_filter_proto.into(), proto,
) )
} }
pub fn create_glow_proto<'gc>(
context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>,
fn_proto: Object<'gc>,
) -> Object<'gc> {
let gradient_bevel_filter_proto = ScriptObject::new(context.gc_context, Some(proto));
define_properties_on(PROTO_DECLS, context, gradient_bevel_filter_proto, fn_proto);
gradient_bevel_filter_proto.into()
}
pub fn create_glow_constructor<'gc>( pub fn create_glow_constructor<'gc>(
context: &mut GcContext<'_, 'gc>, context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>, proto: Object<'gc>,
fn_proto: Object<'gc>, fn_proto: Object<'gc>,
) -> Object<'gc> { ) -> Object<'gc> {
let gradient_bevel_filter_proto = ScriptObject::new(context.gc_context, Some(proto));
define_properties_on(PROTO_DECLS, context, gradient_bevel_filter_proto, fn_proto);
FunctionObject::constructor( FunctionObject::constructor(
context.gc_context, context.gc_context,
Executable::Native(gradient_filter_method!(0)), Executable::Native(gradient_filter_method!(0)),
constructor_to_fn!(gradient_filter_method!(0)), constructor_to_fn!(gradient_filter_method!(0)),
fn_proto, fn_proto,
gradient_bevel_filter_proto.into(), proto,
) )
} }

View File

@ -3,10 +3,10 @@
use crate::avm1::activation::Activation; use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::globals::matrix::gradient_object_to_matrix; use crate::avm1::globals::matrix::gradient_object_to_matrix;
use crate::avm1::globals::{self, AVM_DEPTH_BIAS, AVM_MAX_DEPTH}; use crate::avm1::globals::{self, bitmap_filter, AVM_DEPTH_BIAS, AVM_MAX_DEPTH};
use crate::avm1::object::NativeObject; use crate::avm1::object::NativeObject;
use crate::avm1::property_decl::{define_properties_on, Declaration}; use crate::avm1::property_decl::{define_properties_on, Declaration};
use crate::avm1::{self, Object, ScriptObject, TObject, Value}; use crate::avm1::{self, ArrayObject, Object, ScriptObject, TObject, Value};
use crate::avm_error; use crate::avm_error;
use crate::avm_warn; use crate::avm_warn;
use crate::backend::navigator::NavigationMethod; use crate::backend::navigator::NavigationMethod;
@ -109,6 +109,7 @@ const PROTO_DECLS: &[Declaration] = declare_properties! {
"blendMode" => property(mc_getter!(blend_mode), mc_setter!(set_blend_mode); DONT_DELETE | DONT_ENUM | VERSION_8); "blendMode" => property(mc_getter!(blend_mode), mc_setter!(set_blend_mode); DONT_DELETE | DONT_ENUM | VERSION_8);
"cacheAsBitmap" => property(mc_getter!(cache_as_bitmap), mc_setter!(set_cache_as_bitmap); DONT_DELETE | DONT_ENUM | VERSION_8); "cacheAsBitmap" => property(mc_getter!(cache_as_bitmap), mc_setter!(set_cache_as_bitmap); DONT_DELETE | DONT_ENUM | VERSION_8);
"filters" => property(mc_getter!(filters), mc_setter!(set_filters); DONT_DELETE | DONT_ENUM | VERSION_8);
"opaqueBackground" => property(mc_getter!(opaque_background), mc_setter!(set_opaque_background); DONT_DELETE | DONT_ENUM | VERSION_8); "opaqueBackground" => property(mc_getter!(opaque_background), mc_setter!(set_opaque_background); DONT_DELETE | DONT_ENUM | VERSION_8);
"enabled" => bool(true; DONT_ENUM); "enabled" => bool(true; DONT_ENUM);
"_lockroot" => property(mc_getter!(lock_root), mc_setter!(set_lock_root); DONT_DELETE | DONT_ENUM); "_lockroot" => property(mc_getter!(lock_root), mc_setter!(set_lock_root); DONT_DELETE | DONT_ENUM);
@ -1710,3 +1711,35 @@ fn set_opaque_background<'gc>(
} }
Ok(()) Ok(())
} }
fn filters<'gc>(
this: MovieClip<'gc>,
activation: &mut Activation<'_, 'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
Ok(ArrayObject::new(
activation.context.gc_context,
activation.context.avm1.prototypes().array,
this.filters()
.into_iter()
.map(|filter| bitmap_filter::filter_to_avm1(activation, filter)),
)
.into())
}
fn set_filters<'gc>(
this: MovieClip<'gc>,
activation: &mut Activation<'_, 'gc>,
value: Value<'gc>,
) -> Result<(), Error<'gc>> {
let mut filters = vec![];
if let Value::Object(value) = value {
for index in value.get_keys(activation, false) {
let filter_object = value.get(index, activation)?.coerce_to_object(activation);
if let Some(filter) = bitmap_filter::avm1_to_filter(filter_object) {
filters.push(filter);
}
}
}
this.set_filters(activation.context.gc_context, filters);
Ok(())
}