ruffle/core/src/pixel_bender.rs

163 lines
7.1 KiB
Rust

use ruffle_render::pixel_bender::{PixelBenderType, PixelBenderTypeOpcode};
use crate::{
avm2::{Activation, ArrayObject, ArrayStorage, Error, TObject, Value},
ecma_conversions::f64_to_wrapping_i32,
string::AvmString,
};
pub trait PixelBenderTypeExt {
fn from_avm2_value<'gc>(
activation: &mut Activation<'_, 'gc>,
value: Value<'gc>,
kind: &PixelBenderTypeOpcode,
) -> Result<Self, Error<'gc>>
where
Self: Sized;
fn as_avm2_value<'gc>(
&self,
activation: &mut Activation<'_, 'gc>,
tint_as_int: bool,
) -> Result<Value<'gc>, Error<'gc>>;
}
impl PixelBenderTypeExt for PixelBenderType {
fn from_avm2_value<'gc>(
activation: &mut Activation<'_, 'gc>,
value: Value<'gc>,
kind: &PixelBenderTypeOpcode,
) -> Result<Self, Error<'gc>>
where
Self: Sized,
{
let is_float = matches!(
kind,
PixelBenderTypeOpcode::TFloat
| PixelBenderTypeOpcode::TFloat2
| PixelBenderTypeOpcode::TFloat3
| PixelBenderTypeOpcode::TFloat4
| PixelBenderTypeOpcode::TFloat2x2
| PixelBenderTypeOpcode::TFloat3x3
| PixelBenderTypeOpcode::TFloat4x4
);
match value {
Value::String(s) => Ok(PixelBenderType::TString(s.to_string())),
Value::Number(n) => Ok(PixelBenderType::TFloat(n as f32)),
Value::Integer(i) => Ok(PixelBenderType::TInt(i as i16)),
Value::Object(o) => {
if let Some(array) = o.as_array_storage() {
if is_float {
let mut vals = array.iter().map(|val| {
val.expect("Array with hole")
.coerce_to_number(activation)
.unwrap() as f32
});
match kind {
PixelBenderTypeOpcode::TFloat => {
Ok(PixelBenderType::TFloat(vals.next().unwrap()))
}
PixelBenderTypeOpcode::TFloat2 => Ok(PixelBenderType::TFloat2(
vals.next().unwrap(),
vals.next().unwrap(),
)),
PixelBenderTypeOpcode::TFloat3 => Ok(PixelBenderType::TFloat3(
vals.next().unwrap(),
vals.next().unwrap(),
vals.next().unwrap(),
)),
PixelBenderTypeOpcode::TFloat4 => Ok(PixelBenderType::TFloat4(
vals.next().unwrap(),
vals.next().unwrap(),
vals.next().unwrap(),
vals.next().unwrap(),
)),
PixelBenderTypeOpcode::TFloat2x2 => Ok(PixelBenderType::TFloat2x2(
vals.collect::<Vec<_>>().try_into().unwrap(),
)),
PixelBenderTypeOpcode::TFloat3x3 => Ok(PixelBenderType::TFloat3x3(
vals.collect::<Vec<_>>().try_into().unwrap(),
)),
PixelBenderTypeOpcode::TFloat4x4 => Ok(PixelBenderType::TFloat4x4(
vals.collect::<Vec<_>>().try_into().unwrap(),
)),
_ => unreachable!("Unexpected float kind {kind:?}"),
}
} else {
let mut vals = array.iter().map(|val| {
val.expect("Array with hole")
.coerce_to_i32(activation)
.unwrap() as i16
});
match kind {
PixelBenderTypeOpcode::TInt => {
Ok(PixelBenderType::TInt(vals.next().unwrap()))
}
PixelBenderTypeOpcode::TInt2 => Ok(PixelBenderType::TInt2(
vals.next().unwrap(),
vals.next().unwrap(),
)),
PixelBenderTypeOpcode::TInt3 => Ok(PixelBenderType::TInt3(
vals.next().unwrap(),
vals.next().unwrap(),
vals.next().unwrap(),
)),
PixelBenderTypeOpcode::TInt4 => Ok(PixelBenderType::TInt4(
vals.next().unwrap(),
vals.next().unwrap(),
vals.next().unwrap(),
vals.next().unwrap(),
)),
_ => unreachable!("Unexpected int kind {kind:?}"),
}
}
} else {
panic!("Unexpected object {o:?}")
}
}
_ => panic!("Unexpected value {value:?}"),
}
}
fn as_avm2_value<'gc>(
&self,
activation: &mut Activation<'_, 'gc>,
tint_as_int: bool,
) -> Result<Value<'gc>, Error<'gc>> {
// Flash appears to use a uint/int if the float has no fractional part
let cv = |f: &f32| -> Value<'gc> {
if f.fract() == 0.0 {
f64_to_wrapping_i32(*f as f64).into()
} else {
(*f).into()
}
};
let vals: Vec<Value<'gc>> = match self {
PixelBenderType::TString(string) => {
return Ok(AvmString::new_utf8(activation.context.gc_context, string).into());
}
PixelBenderType::TInt(i) => {
if tint_as_int {
return Ok((*i).into());
} else {
vec![(*i).into()]
}
}
PixelBenderType::TFloat(f) => vec![cv(f)],
PixelBenderType::TFloat2(f1, f2) => vec![cv(f1), cv(f2)],
PixelBenderType::TFloat3(f1, f2, f3) => vec![cv(f1), cv(f2), cv(f3)],
PixelBenderType::TFloat4(f1, f2, f3, f4) => vec![cv(f1), cv(f2), cv(f3), cv(f4)],
PixelBenderType::TFloat2x2(floats) => floats.iter().map(cv).collect(),
PixelBenderType::TFloat3x3(floats) => floats.iter().map(cv).collect(),
PixelBenderType::TFloat4x4(floats) => floats.iter().map(cv).collect(),
PixelBenderType::TInt2(i1, i2) => vec![(*i1).into(), (*i2).into()],
PixelBenderType::TInt3(i1, i2, i3) => vec![(*i1).into(), (*i2).into(), (*i3).into()],
PixelBenderType::TInt4(i1, i2, i3, i4) => {
vec![(*i1).into(), (*i2).into(), (*i3).into(), (*i4).into()]
}
};
let storage = ArrayStorage::from_args(&vals);
Ok(ArrayObject::from_storage(activation, storage)?.into())
}
}