From bb38a7fa5559923cba01e2bf33cb447e1ae11461 Mon Sep 17 00:00:00 2001 From: Nathan Adams Date: Thu, 23 Feb 2023 03:40:41 +0100 Subject: [PATCH] render: Add Filter::GradientBevelFilter --- core/src/avm2/filters.rs | 150 ++++++++++++++++++++++++++++++++++++++- render/src/filters.rs | 55 +++++++++++++- 2 files changed, 203 insertions(+), 2 deletions(-) diff --git a/core/src/avm2/filters.rs b/core/src/avm2/filters.rs index e0aa32891..c4d1b8bec 100644 --- a/core/src/avm2/filters.rs +++ b/core/src/avm2/filters.rs @@ -1,8 +1,9 @@ use ruffle_render::filters::{ BevelFilter, BevelFilterType, BlurFilter, ColorMatrixFilter, ConvolutionFilter, DisplacementMapFilter, DisplacementMapFilterMode, DropShadowFilter, Filter, GlowFilter, + GradientBevelFilter, }; -use swf::Color; +use swf::{Color, GradientRecord}; use crate::avm2::error::{argument_error, type_error}; use crate::avm2::{Activation, ArrayObject, Error, Object, TObject, Value}; @@ -59,6 +60,11 @@ impl FilterAvm2Ext for Filter { return GlowFilter::from_avm2_object(activation, object); } + let gradient_bevel_filter = activation.avm2().classes().gradientbevelfilter; + if object.is_of_type(gradient_bevel_filter, activation) { + return GradientBevelFilter::from_avm2_object(activation, object); + } + Err(Error::AvmError(type_error( activation, &format!( @@ -80,6 +86,7 @@ impl FilterAvm2Ext for Filter { Filter::DisplacementMapFilter(filter) => filter.as_avm2_object(activation), Filter::DropShadowFilter(filter) => filter.as_avm2_object(activation), Filter::GlowFilter(filter) => filter.as_avm2_object(activation), + Filter::GradientBevelFilter(filter) => filter.as_avm2_object(activation), } } } @@ -575,3 +582,144 @@ impl FilterAvm2Ext for GlowFilter { ) } } + +impl FilterAvm2Ext for GradientBevelFilter { + fn from_avm2_object<'gc>( + activation: &mut Activation<'_, 'gc>, + object: Object<'gc>, + ) -> Result> { + let mut colors = vec![]; + let angle = object + .get_public_property("angle", activation)? + .coerce_to_number(activation)?; + let blur_x = object + .get_public_property("blurX", activation)? + .coerce_to_number(activation)?; + let blur_y = object + .get_public_property("blurY", activation)? + .coerce_to_number(activation)?; + let distance = object + .get_public_property("distance", activation)? + .coerce_to_number(activation)?; + let knockout = object + .get_public_property("knockout", activation)? + .coerce_to_boolean(); + let quality = object + .get_public_property("quality", activation)? + .coerce_to_u32(activation)?; + let strength = object + .get_public_property("strength", activation)? + .coerce_to_u32(activation)?; + let bevel_type = object + .get_public_property("type", activation)? + .coerce_to_string(activation)?; + if let Some(colors_object) = object + .get_public_property("colors", activation)? + .as_object() + { + if let Some(colors_array) = colors_object.as_array_storage() { + if let Some(alphas_object) = object + .get_public_property("alphas", activation)? + .as_object() + { + if let Some(alphas_array) = alphas_object.as_array_storage() { + if let Some(ratios_object) = object + .get_public_property("ratios", activation)? + .as_object() + { + if let Some(ratios_array) = ratios_object.as_array_storage() { + // Flash only keeps the elements from any array until the lowest index in each array + for i in 0..ratios_array + .length() + .min(alphas_array.length()) + .min(colors_array.length()) + as usize + { + let color = colors_array + .get(i) + .expect("Length was already checked at this point") + .coerce_to_u32(activation)?; + let alpha = colors_array + .get(i) + .expect("Length was already checked at this point") + .coerce_to_number(activation)? + as f32; + let ratio = colors_array + .get(i) + .expect("Length was already checked at this point") + .coerce_to_u32(activation)?; + colors.push(GradientRecord { + ratio: ratio.clamp(0, 255) as u8, + color: Color::from_rgb(color, (alpha * 255.0) as u8), + }) + } + } + } + } + } + } + } + Ok(Filter::GradientBevelFilter(GradientBevelFilter { + colors, + blur_x: blur_x as f32, + blur_y: blur_y as f32, + angle: angle as f32, + distance: distance as f32, + strength: strength.clamp(0, 255) as u8, + bevel_type: if &bevel_type == b"inner" { + BevelFilterType::Inner + } else if &bevel_type == b"outer" { + BevelFilterType::Outer + } else { + BevelFilterType::Full + }, + knockout, + quality: quality.clamp(1, 15) as u8, + })) + } + + fn as_avm2_object<'gc>( + &self, + activation: &mut Activation<'_, 'gc>, + ) -> Result, Error<'gc>> { + let colors = ArrayObject::from_storage( + activation, + self.colors + .iter() + .map(|v| Value::from(v.color.to_rgb())) + .collect(), + )?; + let alphas = ArrayObject::from_storage( + activation, + self.colors + .iter() + .map(|v| Value::from(f64::from(v.color.a) / 255.0)) + .collect(), + )?; + let ratios = ArrayObject::from_storage( + activation, + self.colors.iter().map(|v| Value::from(v.ratio)).collect(), + )?; + activation.avm2().classes().gradientbevelfilter.construct( + activation, + &[ + self.distance.into(), + self.angle.into(), + colors.into(), + alphas.into(), + ratios.into(), + self.blur_x.into(), + self.blur_y.into(), + self.strength.into(), + self.quality.into(), + match self.bevel_type { + BevelFilterType::Inner => "inner", + BevelFilterType::Outer => "outer", + BevelFilterType::Full => "full", + } + .into(), + self.knockout.into(), + ], + ) + } +} diff --git a/render/src/filters.rs b/render/src/filters.rs index 74605c0dc..cb9758e1a 100644 --- a/render/src/filters.rs +++ b/render/src/filters.rs @@ -1,5 +1,5 @@ use crate::bitmap::BitmapHandle; -use swf::{BevelFilterFlags, Color, Fixed16}; +use swf::{BevelFilterFlags, Color, Fixed16, GradientFilterFlags, GradientRecord}; #[derive(Debug, Clone)] pub enum Filter { @@ -10,6 +10,7 @@ pub enum Filter { DisplacementMapFilter(DisplacementMapFilter), DropShadowFilter(DropShadowFilter), GlowFilter(GlowFilter), + GradientBevelFilter(GradientBevelFilter), } impl Default for Filter { @@ -314,3 +315,55 @@ impl Default for GlowFilter { } } } + +#[derive(Debug, Clone)] +pub struct GradientBevelFilter { + pub colors: Vec, + pub blur_x: f32, + pub blur_y: f32, + pub angle: f32, + pub distance: f32, + pub strength: u8, + pub bevel_type: BevelFilterType, + pub knockout: bool, + pub quality: u8, +} + +impl From for GradientBevelFilter { + fn from(value: swf::GradientFilter) -> Self { + let quality = value.num_passes(); + Self { + colors: value.colors, + blur_x: value.blur_x.to_f32(), + blur_y: value.blur_y.to_f32(), + angle: value.angle.to_f32(), + distance: value.distance.to_f32(), + strength: (value.strength.to_f32() * 255.0) as u8, + bevel_type: if value.flags.contains(GradientFilterFlags::ON_TOP) { + BevelFilterType::Full + } else if value.flags.contains(GradientFilterFlags::INNER_SHADOW) { + BevelFilterType::Inner + } else { + BevelFilterType::Outer + }, + knockout: value.flags.contains(GradientFilterFlags::KNOCKOUT), + quality, + } + } +} + +impl Default for GradientBevelFilter { + fn default() -> Self { + Self { + colors: vec![], + blur_x: 4.0, + blur_y: 4.0, + angle: 45.0, + distance: 4.0, + strength: 1, + bevel_type: BevelFilterType::Inner, + knockout: false, + quality: 1, + } + } +}