avm1: Correct `TextFormat` properties coercion

This commit is contained in:
relrelb 2021-11-15 11:52:08 +02:00 committed by relrelb
parent 068e7acc6f
commit 10b7c69719
5 changed files with 1176 additions and 15 deletions

View File

@ -88,8 +88,7 @@ fn set_size<'gc>(
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
text_format.size = match value { text_format.size = match value {
Value::Undefined | Value::Null => None, Value::Undefined | Value::Null => None,
// TODO: round up value => Some(round_to_i32(value.coerce_to_f64(activation)?).into()),
value => Some(value.coerce_to_i32(activation)?.into()),
}; };
Ok(()) Ok(())
} }
@ -233,7 +232,7 @@ fn set_align<'gc>(
"center" => Some(swf::TextAlign::Center), "center" => Some(swf::TextAlign::Center),
"right" => Some(swf::TextAlign::Right), "right" => Some(swf::TextAlign::Right),
"justify" => Some(swf::TextAlign::Justify), "justify" => Some(swf::TextAlign::Justify),
_ => None, _ => return Ok(()),
}, },
}; };
Ok(()) Ok(())
@ -256,8 +255,7 @@ fn set_left_margin<'gc>(
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
text_format.left_margin = match value { text_format.left_margin = match value {
Value::Undefined | Value::Null => None, Value::Undefined | Value::Null => None,
// TODO: round up value => Some(round_to_i32(value.coerce_to_f64(activation)?.max(0.0)).into()),
value => Some(value.coerce_to_i32(activation)?.into()),
}; };
Ok(()) Ok(())
} }
@ -279,8 +277,7 @@ fn set_right_margin<'gc>(
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
text_format.right_margin = match value { text_format.right_margin = match value {
Value::Undefined | Value::Null => None, Value::Undefined | Value::Null => None,
// TODO: round up value => Some(round_to_i32(value.coerce_to_f64(activation)?.max(0.0)).into()),
value => Some(value.coerce_to_i32(activation)?.into()),
}; };
Ok(()) Ok(())
} }
@ -299,8 +296,7 @@ fn set_indent<'gc>(
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
text_format.indent = match value { text_format.indent = match value {
Value::Undefined | Value::Null => None, Value::Undefined | Value::Null => None,
// TODO: round up value => Some(round_to_i32(value.coerce_to_f64(activation)?).into()),
value => Some(value.coerce_to_i32(activation)?.into()),
}; };
Ok(()) Ok(())
} }
@ -319,8 +315,7 @@ fn set_leading<'gc>(
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
text_format.leading = match value { text_format.leading = match value {
Value::Undefined | Value::Null => None, Value::Undefined | Value::Null => None,
// TODO: round up value => Some(round_to_i32(value.coerce_to_f64(activation)?).into()),
value => Some(value.coerce_to_i32(activation)?.into()),
}; };
Ok(()) Ok(())
} }
@ -342,8 +337,7 @@ fn set_block_indent<'gc>(
) -> Result<(), Error<'gc>> { ) -> Result<(), Error<'gc>> {
text_format.block_indent = match value { text_format.block_indent = match value {
Value::Undefined | Value::Null => None, Value::Undefined | Value::Null => None,
// TODO: round up value => Some(round_to_i32(value.coerce_to_f64(activation)?).into()),
value => Some(value.coerce_to_i32(activation)?.into()),
}; };
Ok(()) Ok(())
} }
@ -376,8 +370,7 @@ fn set_tab_stops<'gc>(
let tab_stops: Result<Vec<_>, Error<'gc>> = (0..length) let tab_stops: Result<Vec<_>, Error<'gc>> = (0..length)
.map(|i| { .map(|i| {
let element = object.get_element(activation, i); let element = object.get_element(activation, i);
// TODO: round up Ok(round_to_i32(element.coerce_to_f64(activation)?).into())
Ok(element.coerce_to_i32(activation)?.into())
}) })
.collect(); .collect();
Some(tab_stops?) Some(tab_stops?)
@ -551,3 +544,23 @@ pub fn create_proto<'gc>(
define_properties_on(PROTO_DECLS, gc_context, object, fn_proto); define_properties_on(PROTO_DECLS, gc_context, object, fn_proto);
text_format.into() text_format.into()
} }
/// Implements the IEEE-754 "Round to nearest, ties to even" rounding rule.
/// (e.g., both 1.5 and 2.5 will round to 2).
/// This is the rounding method used by Flash for the above coercions.
/// Although this is easy to do on most architectures, Rust provides no standard
/// way to round in this manner (`f64::round` always rounds away from zero).
/// For more info and the below code snippet, see: https://github.com/rust-lang/rust/issues/55107
/// This also clamps out-of-range values and NaN to `i32::MIN`.
/// TODO: Investigate using SSE/wasm intrinsics for this.
fn round_to_i32(x: f64) -> i32 {
let k = 1.0 / f64::EPSILON;
let a = x.abs();
let out = if a < k { ((a + k) - k).copysign(x) } else { x };
// Values outside of i32 range get clamped to i32::MIN.
if out.is_finite() && out >= i32::MIN.into() && out <= i32::MAX.into() {
out as i32
} else {
i32::MIN
}
}

View File

@ -324,6 +324,7 @@ swf_tests! {
(textfield_background_color, "avm1/textfield_background_color", 1), (textfield_background_color, "avm1/textfield_background_color", 1),
(textfield_border_color, "avm1/textfield_border_color", 1), (textfield_border_color, "avm1/textfield_border_color", 1),
(textfield_variable, "avm1/textfield_variable", 8), (textfield_variable, "avm1/textfield_variable", 8),
(text_format, "avm1/text_format", 1),
(error, "avm1/error", 1), (error, "avm1/error", 1),
(color_transform, "avm1/color_transform", 1), (color_transform, "avm1/color_transform", 1),
(with, "avm1/with", 1), (with, "avm1/with", 1),

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.