avm1: Implement bevel filter conversions

This commit is contained in:
Nathan Adams 2023-06-30 19:33:01 +02:00
parent 2b48f579e6
commit 73bc637ad7
6 changed files with 147 additions and 10 deletions

View File

@ -7,7 +7,8 @@ use crate::avm1::{Activation, Error, Object, ScriptObject, TObject, Value};
use crate::context::GcContext; use crate::context::GcContext;
use crate::string::{AvmString, WStr}; use crate::string::{AvmString, WStr};
use gc_arena::{Collect, GcCell, MutationContext}; use gc_arena::{Collect, GcCell, MutationContext};
use swf::Color; use std::ops::Deref;
use swf::{BevelFilterFlags, Color, Fixed16, Fixed8};
#[derive(Copy, Clone, Debug, Collect)] #[derive(Copy, Clone, Debug, Collect)]
#[collect(no_drop)] #[collect(no_drop)]
@ -39,6 +40,28 @@ impl From<BevelFilterType> 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<BevelFilterFlags> 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)] #[derive(Clone, Debug, Collect)]
#[collect(require_static)] #[collect(require_static)]
struct BevelFilterData { struct BevelFilterData {
@ -56,6 +79,42 @@ struct BevelFilterData {
type_: BevelFilterType, 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<swf::BevelFilter> 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 { impl Default for BevelFilterData {
#[allow(clippy::approx_constant)] #[allow(clippy::approx_constant)]
fn default() -> Self { fn default() -> Self {
@ -110,6 +169,10 @@ impl<'gc> BevelFilter<'gc> {
Ok(bevel_filter) 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 { pub(crate) fn duplicate(&self, gc_context: MutationContext<'gc, '_>) -> Self {
Self(GcCell::allocate(gc_context, self.0.read().clone())) Self(GcCell::allocate(gc_context, self.0.read().clone()))
} }
@ -306,6 +369,10 @@ impl<'gc> BevelFilter<'gc> {
} }
Ok(()) Ok(())
} }
pub fn filter(&self) -> swf::BevelFilter {
self.0.read().deref().into()
}
} }
macro_rules! bevel_filter_method { macro_rules! bevel_filter_method {

View File

@ -2,6 +2,7 @@
use crate::avm1::activation::Activation; use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::globals::bevel_filter::BevelFilter;
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::{Attribute, Object, ScriptObject, TObject, Value}; use crate::avm1::{Attribute, Object, ScriptObject, TObject, Value};
@ -63,19 +64,33 @@ pub fn clone<'gc>(
Ok(create_instance(activation, native, proto).into()) Ok(create_instance(activation, native, proto).into())
} }
pub fn avm1_to_filter(_object: Object) -> Option<Filter> { pub fn avm1_to_filter(object: Object) -> Option<Filter> {
// TODO let native = object.native();
match native {
NativeObject::BevelFilter(filter) => Some(Filter::BevelFilter(filter.filter())),
// Invalid filters are silently dropped/ignored, no errors are thrown. // Invalid filters are silently dropped/ignored, no errors are thrown.
None _ => None,
}
} }
pub fn filter_to_avm1<'gc>(_activation: &mut Activation<'_, 'gc>, _filter: Filter) -> Value<'gc> { pub fn filter_to_avm1<'gc>(activation: &mut Activation<'_, 'gc>, filter: Filter) -> Value<'gc> {
// TODO 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. create_instance(activation, native, Some(proto.into())).into()
// 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>( pub fn create_instance<'gc>(

View File

@ -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 // new BevelFilter
[object Object] [object Object]
// x.clone() // x.clone()
@ -28,6 +53,33 @@ false
1 1
// x.type // x.type
inner 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) // x.angle (after set to 360)
0 0
// x.angle (after set to 361) // x.angle (after set to 361)

View File

@ -1 +1,4 @@
num_frames = 1 num_frames = 1
[approximations]
epsilon = 0.001