From 223edb9bc1d83361170ebed711ea23529bb4ea8b Mon Sep 17 00:00:00 2001 From: Mike Welsh Date: Tue, 18 Feb 2020 11:39:53 -0800 Subject: [PATCH] core: Matrix translation is in twips --- core/src/avm1/globals/text_field.rs | 4 +- core/src/display_object.rs | 8 +- core/src/display_object/edit_text.rs | 43 ++- core/src/display_object/text.rs | 12 +- core/src/font.rs | 28 +- core/src/matrix.rs | 287 ++++++++++-------- core/src/player.rs | 4 +- core/tests/regression_tests.rs | 4 +- .../swfs/avm1/movieclip_getbounds/test.swf | Bin 820 -> 817 bytes .../avm1/stage_object_properties/test.swf | Bin 1358 -> 1352 bytes desktop/src/render.rs | 4 +- web/src/render.rs | 16 +- 12 files changed, 217 insertions(+), 193 deletions(-) diff --git a/core/src/avm1/globals/text_field.rs b/core/src/avm1/globals/text_field.rs index b3bfdb18d..968745fb0 100644 --- a/core/src/avm1/globals/text_field.rs +++ b/core/src/avm1/globals/text_field.rs @@ -85,7 +85,7 @@ pub fn text_width<'gc>( { let metrics = etext.measure_text(context); - return Ok(metrics.0.into()); + return Ok(metrics.0.to_pixels().into()); } Ok(Value::Undefined.into()) @@ -103,7 +103,7 @@ pub fn text_height<'gc>( { let metrics = etext.measure_text(context); - return Ok(metrics.1.into()); + return Ok(metrics.1.to_pixels().into()); } Ok(Value::Undefined.into()) diff --git a/core/src/display_object.rs b/core/src/display_object.rs index 2ac20bb7b..0328efb5b 100644 --- a/core/src/display_object.rs +++ b/core/src/display_object.rs @@ -142,18 +142,18 @@ impl<'gc> DisplayObjectBase<'gc> { self.transform.color_transform = *color_transform; } fn x(&self) -> f64 { - f64::from(self.transform.matrix.tx) / Twips::TWIPS_PER_PIXEL + self.transform.matrix.tx.to_pixels() } fn set_x(&mut self, value: f64) { self.set_transformed_by_script(true); - self.transform.matrix.tx = (value * Twips::TWIPS_PER_PIXEL) as f32 + self.transform.matrix.tx = Twips::from_pixels(value) } fn y(&self) -> f64 { - f64::from(self.transform.matrix.ty) / Twips::TWIPS_PER_PIXEL + self.transform.matrix.ty.to_pixels() } fn set_y(&mut self, value: f64) { self.set_transformed_by_script(true); - self.transform.matrix.ty = (value * Twips::TWIPS_PER_PIXEL) as f32 + self.transform.matrix.ty = Twips::from_pixels(value) } /// Caches the scale and rotation factors for this display object, if necessary. diff --git a/core/src/display_object/edit_text.rs b/core/src/display_object/edit_text.rs index 9f1abdd33..4b707df76 100644 --- a/core/src/display_object/edit_text.rs +++ b/core/src/display_object/edit_text.rs @@ -226,9 +226,9 @@ impl<'gc> EditText<'gc> { transform.color_transform.a_mult = f32::from(color.a) / 255.0; if let Some(layout) = &static_data.text.layout { - transform.matrix.tx += layout.left_margin.get() as f32; - transform.matrix.tx += layout.indent.get() as f32; - transform.matrix.ty -= layout.leading.get() as f32; + transform.matrix.tx += layout.left_margin; + transform.matrix.tx += layout.indent; + transform.matrix.ty -= layout.leading; } transform @@ -239,31 +239,31 @@ impl<'gc> EditText<'gc> { /// /// This function takes the current font size and the transform to adjust, /// and returns the adjusted transform. - pub fn newline(self, height: f32, mut transform: Transform) -> Transform { + pub fn newline(self, height: Twips, mut transform: Transform) -> Transform { let edit_text = self.0.read(); let static_data = &edit_text.static_data; - transform.matrix.tx = 0.0; - transform.matrix.ty += height * Twips::TWIPS_PER_PIXEL as f32; + transform.matrix.tx = Twips::new(0); + transform.matrix.ty += height; if let Some(layout) = &static_data.text.layout { - transform.matrix.tx += layout.left_margin.get() as f32; - transform.matrix.tx += layout.indent.get() as f32; - transform.matrix.ty += layout.leading.get() as f32; + transform.matrix.tx += layout.left_margin; + transform.matrix.tx += layout.indent; + transform.matrix.ty += layout.leading; } transform } - pub fn line_width(self) -> f32 { + pub fn line_width(self) -> Twips { let edit_text = self.0.read(); let static_data = &edit_text.static_data; - let mut base_width = self.width() as f32; + let mut base_width = Twips::from_pixels(self.width()); if let Some(layout) = &static_data.text.layout { - base_width -= layout.left_margin.to_pixels() as f32; - base_width -= layout.indent.to_pixels() as f32; - base_width -= layout.right_margin.to_pixels() as f32; + base_width -= layout.left_margin; + base_width -= layout.indent; + base_width -= layout.right_margin; } base_width @@ -311,8 +311,7 @@ impl<'gc> EditText<'gc> { let height = static_data .text .height - .map(|v| v.to_pixels() as f32) - .unwrap_or_else(|| font.scale()); + .unwrap_or_else(|| Twips::from_pixels(font.scale().into())); for natural_line in edit_text.text.split('\n') { if break_base != 0 { @@ -364,14 +363,14 @@ impl<'gc> EditText<'gc> { /// Measure the width and height of the `EditText`'s current text load. /// /// The returned tuple should be interpreted as width, then height. - pub fn measure_text(self, context: &mut UpdateContext<'_, 'gc, '_>) -> (f32, f32) { + pub fn measure_text(self, context: &mut UpdateContext<'_, 'gc, '_>) -> (Twips, Twips) { let breakpoints = self.line_breaks_cached(context.gc_context, context.library); let edit_text = self.0.read(); let static_data = &edit_text.static_data; let font_id = static_data.text.font_id.unwrap_or(0); - let mut size: (f32, f32) = (0.0, 0.0); + let mut size: (Twips, Twips) = Default::default(); if let Some(font) = context .library @@ -399,15 +398,14 @@ impl<'gc> EditText<'gc> { let height = static_data .text .height - .map(|v| v.to_pixels() as f32) - .unwrap_or_else(|| font.scale()); + .unwrap_or_else(|| Twips::from_pixels(font.scale().into())); for chunk in chunks { let chunk_size = font.measure(chunk, height); size.0 = size.0.max(chunk_size.0); if let Some(layout) = &static_data.text.layout { - size.1 += layout.leading.to_pixels() as f32; + size.1 += layout.leading; } size.1 += chunk_size.1; } @@ -490,8 +488,7 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> { let height = static_data .text .height - .map(|v| v.to_pixels() as f32) - .unwrap_or_else(|| font.scale()); + .unwrap_or_else(|| Twips::from_pixels(font.scale().into())); let breakpoints = edit_text .cached_break_points diff --git a/core/src/display_object/text.rs b/core/src/display_object/text.rs index 65ff0e9d3..048b49da6 100644 --- a/core/src/display_object/text.rs +++ b/core/src/display_object/text.rs @@ -71,25 +71,25 @@ impl<'gc> TDisplayObject<'gc> for Text<'gc> { a: 0, }; let mut font_id = 0; - let mut height = 0.0; + let mut height = Twips::new(0); let mut transform: Transform = Default::default(); for block in &tf.static_data.text_blocks { if let Some(x) = block.x_offset { - transform.matrix.tx = x.get() as f32; + transform.matrix.tx = x; } if let Some(y) = block.y_offset { - transform.matrix.ty = y.get() as f32; + transform.matrix.ty = y; } color = block.color.as_ref().unwrap_or(&color).clone(); font_id = block.font_id.unwrap_or(font_id); - height = block.height.map(|h| h.get() as f32).unwrap_or(height); + height = block.height.unwrap_or(height); if let Some(font) = context .library .library_for_movie(self.movie().unwrap()) .unwrap() .get_font(font_id) { - let scale = height / font.scale(); + let scale = (height.get() as f32) / font.scale(); transform.matrix.a = scale; transform.matrix.d = scale; transform.color_transform.r_mult = f32::from(color.r) / 255.0; @@ -103,7 +103,7 @@ impl<'gc> TDisplayObject<'gc> for Text<'gc> { .renderer .render_shape(glyph.shape, context.transform_stack.transform()); context.transform_stack.pop(); - transform.matrix.tx += c.advance as f32; + transform.matrix.tx += Twips::new(c.advance); } } } diff --git a/core/src/font.rs b/core/src/font.rs index 2b5568b12..d3f6e6740 100644 --- a/core/src/font.rs +++ b/core/src/font.rs @@ -132,13 +132,13 @@ impl<'gc> Font<'gc> { self, text: &str, mut transform: Transform, - height: f32, + height: Twips, mut glyph_func: FGlyph, ) where FGlyph: FnMut(&Transform, &Glyph), { - transform.matrix.ty += height * Twips::TWIPS_PER_PIXEL as f32; - let scale = (height * Twips::TWIPS_PER_PIXEL as f32) / self.scale(); + transform.matrix.ty += height; + let scale = height.get() as f32 / self.scale(); transform.matrix.a = scale; transform.matrix.d = scale; @@ -148,26 +148,24 @@ impl<'gc> Font<'gc> { if let Some(glyph) = self.get_glyph_for_char(c) { glyph_func(&transform, &glyph); // Step horizontally. - let mut advance = f32::from(glyph.advance); + let mut advance = Twips::new(glyph.advance); if has_kerning_info { - advance += self - .get_kerning_offset(c, chars.peek().cloned().unwrap_or('\0')) - .get() as f32; + advance += self.get_kerning_offset(c, chars.peek().cloned().unwrap_or('\0')); } - transform.matrix.tx += advance * scale; + transform.matrix.tx += Twips::new((advance.get() as f32 * scale) as i32); } } } /// Measure a particular string's metrics (width and height). - pub fn measure(self, text: &str, height: f32) -> (f32, f32) { - let mut size = (0.0, 0.0); + pub fn measure(self, text: &str, height: Twips) -> (Twips, Twips) { + let mut size = (Twips::new(0), Twips::new(0)); self.evaluate(text, Default::default(), height, |transform, _glyph| { - let tx = transform.matrix.tx / Twips::TWIPS_PER_PIXEL as f32; - let ty = transform.matrix.ty / Twips::TWIPS_PER_PIXEL as f32; - size.0 = f32::max(size.0, tx); - size.1 = f32::max(size.1, ty); + let tx = transform.matrix.tx; + let ty = transform.matrix.ty; + size.0 = std::cmp::max(size.0, tx); + size.1 = std::cmp::max(size.1, ty); }); size @@ -178,7 +176,7 @@ impl<'gc> Font<'gc> { /// /// This function assumes only `" "` is valid whitespace to split words on, /// and will not attempt to break words that are longer than `width`. - pub fn split_wrapped_lines(self, text: &str, height: f32, width: f32) -> Vec { + pub fn split_wrapped_lines(self, text: &str, height: Twips, width: Twips) -> Vec { let mut result = vec![]; let mut current_width = width; let mut current_word = &text[0..0]; diff --git a/core/src/matrix.rs b/core/src/matrix.rs index b7740ee90..f11ede8be 100644 --- a/core/src/matrix.rs +++ b/core/src/matrix.rs @@ -6,20 +6,30 @@ pub struct Matrix { pub b: f32, pub c: f32, pub d: f32, - pub tx: f32, - pub ty: f32, + pub tx: Twips, + pub ty: Twips, } impl Matrix { pub fn invert(&mut self) { + let (tx, ty) = (self.tx.get() as f32, self.ty.get() as f32); let det = self.a * self.d - self.b * self.c; let a = self.d / det; let b = self.b / -det; let c = self.c / -det; let d = self.a / det; - let tx = (self.d * self.tx - self.c * self.ty) / -det; - let ty = (self.b * self.tx - self.a * self.ty) / det; - *self = Matrix { a, b, c, d, tx, ty }; + let (out_tx, out_ty) = ( + round_to_i32((self.d * tx - self.c * ty) / -det), + round_to_i32((self.b * tx - self.a * ty) / det), + ); + *self = Matrix { + a, + b, + c, + d, + tx: Twips::new(out_tx), + ty: Twips::new(out_ty), + }; } } @@ -30,8 +40,8 @@ impl From for Matrix { b: matrix.rotate_skew_0, c: matrix.rotate_skew_1, d: matrix.scale_y, - tx: matrix.translate_x.get() as f32, - ty: matrix.translate_y.get() as f32, + tx: matrix.translate_x, + ty: matrix.translate_y, } } } @@ -39,13 +49,18 @@ impl From for Matrix { impl std::ops::Mul for Matrix { type Output = Self; fn mul(self, rhs: Self) -> Self { + let (rhs_tx, rhs_ty) = (rhs.tx.get() as f32, rhs.ty.get() as f32); + let (out_tx, out_ty) = ( + round_to_i32(self.a * rhs_tx + self.c * rhs_ty).wrapping_add(self.tx.get()), + round_to_i32(self.b * rhs_tx + self.d * rhs_ty).wrapping_add(self.ty.get()), + ); Matrix { a: self.a * rhs.a + self.c * rhs.b, b: self.b * rhs.a + self.d * rhs.b, c: self.a * rhs.c + self.c * rhs.d, d: self.b * rhs.c + self.d * rhs.d, - tx: self.a * rhs.tx + self.c * rhs.ty + self.tx, - ty: self.b * rhs.tx + self.d * rhs.ty + self.ty, + tx: Twips::new(out_tx), + ty: Twips::new(out_ty), } } } @@ -54,9 +69,9 @@ impl std::ops::Mul<(Twips, Twips)> for Matrix { type Output = (Twips, Twips); fn mul(self, (x, y): (Twips, Twips)) -> (Twips, Twips) { let (x, y) = (x.get() as f32, y.get() as f32); - let out_x = self.a * x + self.c * y + self.tx; - let out_y = self.b * x + self.d * y + self.ty; - (Twips::new(out_x as i32), Twips::new(out_y as i32)) + let out_x = round_to_i32(self.a * x + self.c * y).wrapping_add(self.tx.get()); + let out_y = round_to_i32(self.b * x + self.d * y).wrapping_add(self.ty.get()); + (Twips::new(out_x), Twips::new(out_y)) } } @@ -65,23 +80,28 @@ impl std::default::Default for Matrix { Matrix { a: 1.0, c: 0.0, - tx: 0.0, + tx: Twips::new(0), b: 0.0, d: 1.0, - ty: 0.0, + ty: Twips::new(0), } } } impl std::ops::MulAssign for Matrix { fn mul_assign(&mut self, rhs: Self) { + let (rhs_tx, rhs_ty) = (rhs.tx.get() as f32, rhs.ty.get() as f32); + let (out_tx, out_ty) = ( + round_to_i32(self.a * rhs_tx + self.c * rhs_ty) + self.tx.get(), + round_to_i32(self.b * rhs_tx + self.d * rhs_ty) + self.ty.get(), + ); *self = Matrix { a: self.a * rhs.a + self.c * rhs.b, b: self.b * rhs.a + self.d * rhs.b, c: self.a * rhs.c + self.c * rhs.d, d: self.b * rhs.c + self.d * rhs.d, - tx: self.a * rhs.tx + self.c * rhs.ty + self.tx, - ty: self.b * rhs.tx + self.d * rhs.ty + self.ty, + tx: Twips::new(out_tx), + ty: Twips::new(out_ty), } } } @@ -129,18 +149,18 @@ mod tests { } impl AbsDiffEq for Matrix { - type Epsilon = ::Epsilon; + type Epsilon = (::Epsilon, ::Epsilon); fn default_epsilon() -> Self::Epsilon { - f32::default_epsilon() + (f32::default_epsilon(), i32::default_epsilon()) } fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool { - self.a.abs_diff_eq(&other.a, epsilon) - && self.b.abs_diff_eq(&other.b, epsilon) - && self.c.abs_diff_eq(&other.c, epsilon) - && self.d.abs_diff_eq(&other.d, epsilon) - && self.tx.abs_diff_eq(&other.tx, epsilon) - && self.ty.abs_diff_eq(&other.ty, epsilon) + self.a.abs_diff_eq(&other.a, epsilon.0) + && self.b.abs_diff_eq(&other.b, epsilon.0) + && self.c.abs_diff_eq(&other.c, epsilon.0) + && self.d.abs_diff_eq(&other.d, epsilon.0) + && self.tx.get().abs_diff_eq(&other.tx.get(), epsilon.1) + && self.ty.get().abs_diff_eq(&other.ty.get(), epsilon.1) } } @@ -150,12 +170,12 @@ mod tests { } fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool { - self.a.ulps_eq(&other.a, epsilon, max_ulps) - && self.b.ulps_eq(&other.b, epsilon, max_ulps) - && self.c.ulps_eq(&other.c, epsilon, max_ulps) - && self.d.ulps_eq(&other.d, epsilon, max_ulps) - && self.tx.ulps_eq(&other.tx, epsilon, max_ulps) - && self.ty.ulps_eq(&other.ty, epsilon, max_ulps) + self.a.ulps_eq(&other.a, epsilon.0, max_ulps) + && self.b.ulps_eq(&other.b, epsilon.0, max_ulps) + && self.c.ulps_eq(&other.c, epsilon.0, max_ulps) + && self.d.ulps_eq(&other.d, epsilon.0, max_ulps) + && self.tx == other.tx + && self.ty == other.ty } } @@ -172,72 +192,72 @@ mod tests { Matrix { a: 1.0, c: 4.0, - tx: 7.0, + tx: Twips::from_pixels(7.0), b: 2.0, d: 5.0, - ty: 2.0 + ty: Twips::from_pixels(2.0) }, Matrix { a: -1.666_666_6, c: 1.333_333_3, - tx: 9.0, + tx: Twips::from_pixels(9.0), b: 0.666_666_6, d: -0.333_333_3, - ty: -4.0 + ty: Twips::from_pixels(-4.0) } ), ( Matrix { a: -1.0, c: -4.0, - tx: -7.0, + tx: Twips::from_pixels(-7.0), b: -2.0, d: -5.0, - ty: -2.0 + ty: Twips::from_pixels(-2.0) }, Matrix { a: 1.666_666_6, c: -1.333_333_3, - tx: 9.0, + tx: Twips::from_pixels(9.0), b: -0.666_666_6, d: 0.333_333_3, - ty: -4.0 + ty: Twips::from_pixels(-4.0) } ), ( Matrix { a: 1.5, c: 1.2, - tx: 1.0, + tx: Twips::from_pixels(1.0), b: -2.7, d: 3.4, - ty: -2.4 + ty: Twips::from_pixels(-2.4) }, Matrix { a: 0.407_673_9, c: -0.143_884_9, - tx: -0.752_997_6, + tx: Twips::from_pixels(-0.752_997_6), b: 0.323_741, d: 0.179_856_1, - ty: 0.107_913_67 + ty: Twips::from_pixels(0.107_913_67) } ), ( Matrix { a: -2.0, c: 0.0, - tx: 10.0, + tx: Twips::from_pixels(10.0), b: 0.0, d: -1.0, - ty: 5.0 + ty: Twips::from_pixels(5.0) }, Matrix { a: -0.5, c: 0.0, - tx: 5.0, + tx: Twips::from_pixels(5.0), b: 0.0, d: -1.0, - ty: 5.0 + ty: Twips::from_pixels(5.0) } ) ); @@ -251,37 +271,37 @@ mod tests { Matrix { a: 1.0, c: 4.0, - tx: 7.0, + tx: Twips::from_pixels(7.0), b: 2.0, d: 5.0, - ty: 2.0 + ty: Twips::from_pixels(2.0) }, Matrix { a: 1.0, c: 4.0, - tx: 7.0, + tx: Twips::from_pixels(7.0), b: 2.0, d: 5.0, - ty: 2.0 + ty: Twips::from_pixels(2.0) } ), ( Matrix { a: 1.0, c: 4.0, - tx: 7.0, + tx: Twips::from_pixels(7.0), b: 2.0, d: 5.0, - ty: 2.0 + ty: Twips::from_pixels(2.0) }, Matrix::default(), Matrix { a: 1.0, c: 4.0, - tx: 7.0, + tx: Twips::from_pixels(7.0), b: 2.0, d: 5.0, - ty: 2.0 + ty: Twips::from_pixels(2.0) } ) ); @@ -293,130 +313,130 @@ mod tests { Matrix { a: 6.0, c: 4.0, - tx: 2.0, + tx: Twips::new(2), b: 5.0, d: 3.0, - ty: 1.0 + ty: Twips::new(1) }, Matrix { a: 1.0, c: 3.0, - tx: 5.0, + tx: Twips::new(5), b: 2.0, d: 4.0, - ty: 6.0 + ty: Twips::new(6) }, Matrix { a: 14.0, c: 34.0, - tx: 56.0, + tx: Twips::new(56), b: 11.0, d: 27.0, - ty: 44.0 + ty: Twips::new(44) } ), ( Matrix { a: 1.0, c: 3.0, - tx: 5.0, + tx: Twips::new(5), b: 2.0, d: 4.0, - ty: 6.0 + ty: Twips::new(6) }, Matrix { a: 6.0, c: 4.0, - tx: 2.0, + tx: Twips::new(2), b: 5.0, d: 3.0, - ty: 1.0 + ty: Twips::new(1) }, Matrix { a: 21.0, c: 13.0, - tx: 10.0, + tx: Twips::new(10), b: 32.0, d: 20.0, - ty: 14.0 + ty: Twips::new(14) } ), ( Matrix { a: 1.0, c: 2.0, - tx: 3.0, + tx: Twips::new(3), b: 4.0, d: 5.0, - ty: 6.0 + ty: Twips::new(6) }, Matrix { a: 6.0, c: 5.0, - tx: 4.0, + tx: Twips::new(4), b: 3.0, d: 2.0, - ty: 1.0 + ty: Twips::new(1) }, Matrix { a: 12.0, c: 9.0, - tx: 9.0, + tx: Twips::new(9), b: 39.0, d: 30.0, - ty: 27.0 + ty: Twips::new(27) } ), ( Matrix { a: 6.0, c: 5.0, - tx: 4.0, + tx: Twips::new(4), b: 3.0, d: 2.0, - ty: 1.0 + ty: Twips::new(1) }, Matrix { a: 1.0, c: 2.0, - tx: 3.0, + tx: Twips::new(3), b: 4.0, d: 5.0, - ty: 6.0 + ty: Twips::new(6) }, Matrix { a: 26.0, c: 37.0, - tx: 52.0, + tx: Twips::new(52), b: 11.0, d: 16.0, - ty: 22.0 + ty: Twips::new(22) } ), ( Matrix { a: 1.0, c: 2.0, - tx: 3.0, + tx: Twips::new(3), b: 4.0, d: 5.0, - ty: 6.0 + ty: Twips::new(6) }, Matrix { a: 1.0, c: 2.0, - tx: 3.0, + tx: Twips::new(3), b: 4.0, d: 5.0, - ty: 6.0 + ty: Twips::new(6) }, Matrix { a: 9.0, c: 12.0, - tx: 18.0, + tx: Twips::new(18), b: 24.0, d: 33.0, - ty: 48.0 + ty: Twips::new(48) } ) ); @@ -453,10 +473,10 @@ mod tests { Matrix { a: 1.0, c: 0.0, - tx: 10.0, + tx: Twips::new(10), b: 0.0, d: 1.0, - ty: 5.0 + ty: Twips::new(5) }, (Twips::new(0), Twips::new(0)), (Twips::new(10), Twips::new(5)) @@ -465,25 +485,13 @@ mod tests { Matrix { a: 1.0, c: 0.0, - tx: -200.0, + tx: Twips::new(-200), b: 0.0, d: 1.0, - ty: 0.0 + ty: Twips::new(0) }, (Twips::new(50), Twips::new(20)), (Twips::new(-150), Twips::new(20)) - ), - ( - Matrix { - a: 1.0, - c: 0.0, - tx: 1.125, - b: 0.0, - d: 1.0, - ty: 1.925 - }, - (Twips::new(0), Twips::new(0)), - (Twips::new(1), Twips::new(1)) ) ); @@ -494,10 +502,10 @@ mod tests { Matrix { a: 3.0, c: 0.0, - tx: 0.0, + tx: Twips::new(0), b: 0.0, d: 3.0, - ty: 0.0 + ty: Twips::new(0) }, (Twips::new(0), Twips::new(0)), (Twips::new(0), Twips::new(0)) @@ -506,10 +514,10 @@ mod tests { Matrix { a: 3.0, c: 0.0, - tx: 0.0, + tx: Twips::new(0), b: 0.0, d: 3.0, - ty: 0.0 + ty: Twips::new(0) }, (Twips::new(10), Twips::new(10)), (Twips::new(30), Twips::new(30)) @@ -518,10 +526,10 @@ mod tests { Matrix { a: 0.6, c: 0.0, - tx: 0.0, + tx: Twips::new(0), b: 0.0, d: 0.2, - ty: 0.0 + ty: Twips::new(0) }, (Twips::new(5), Twips::new(10)), (Twips::new(3), Twips::new(2)) @@ -530,10 +538,10 @@ mod tests { Matrix { a: 0.5, c: 0.0, - tx: 0.0, + tx: Twips::new(0), b: 0.0, d: 0.5, - ty: 0.0 + ty: Twips::new(0) }, (Twips::new(5), Twips::new(5)), (Twips::new(2), Twips::new(2)) @@ -547,10 +555,10 @@ mod tests { Matrix { a: 0.0, c: -1.0, - tx: 0.0, + tx: Twips::new(0), b: 1.0, d: 0.0, - ty: 0.0 + ty: Twips::new(0) }, (Twips::new(10), Twips::new(0)), (Twips::new(0), Twips::new(10)) @@ -559,10 +567,10 @@ mod tests { Matrix { a: 0.0, c: -1.0, - tx: 0.0, + tx: Twips::new(0), b: 1.0, d: 0.0, - ty: 0.0 + ty: Twips::new(0) }, (Twips::new(0), Twips::new(10)), (Twips::new(-10), Twips::new(0)) @@ -571,10 +579,10 @@ mod tests { Matrix { a: 0.0, c: 1.0, - tx: 0.0, + tx: Twips::new(0), b: -1.0, d: 0.0, - ty: 0.0 + ty: Twips::new(0) }, (Twips::new(10), Twips::new(10)), (Twips::new(10), Twips::new(-10)) @@ -583,22 +591,22 @@ mod tests { Matrix { a: f32::cos(std::f32::consts::FRAC_PI_4), c: f32::sin(std::f32::consts::FRAC_PI_4), - tx: 0.0, + tx: Twips::new(0), b: -f32::sin(std::f32::consts::FRAC_PI_4), d: f32::cos(std::f32::consts::FRAC_PI_4), - ty: 0.0 + ty: Twips::new(0) }, (Twips::new(100), Twips::new(0)), - (Twips::new(70), Twips::new(-70)) + (Twips::new(71), Twips::new(-71)) ), ( Matrix { a: f32::cos(std::f32::consts::FRAC_PI_4), c: f32::sin(std::f32::consts::FRAC_PI_4), - tx: 0.0, + tx: Twips::new(0), b: -f32::sin(std::f32::consts::FRAC_PI_4), d: f32::cos(std::f32::consts::FRAC_PI_4), - ty: 0.0 + ty: Twips::new(0) }, (Twips::new(100), Twips::new(100)), (Twips::new(141), Twips::new(0)) @@ -613,10 +621,10 @@ mod tests { Matrix { a: 3.0 * f32::cos(std::f32::consts::FRAC_PI_4), c: 3.0 * f32::sin(std::f32::consts::FRAC_PI_4), - tx: 0.0, + tx: Twips::new(0), b: 3.0 * -f32::sin(std::f32::consts::FRAC_PI_4), d: 3.0 * f32::cos(std::f32::consts::FRAC_PI_4), - ty: 0.0 + ty: Twips::new(0) }, (Twips::new(100), Twips::new(100)), (Twips::new(424), Twips::new(0)) @@ -626,10 +634,10 @@ mod tests { Matrix { a: 3.0 * f32::cos(std::f32::consts::FRAC_PI_4), c: 3.0 * f32::sin(std::f32::consts::FRAC_PI_4), - tx: -5.0, + tx: Twips::new(-5), b: 3.0 * -f32::sin(std::f32::consts::FRAC_PI_4), d: 3.0 * f32::cos(std::f32::consts::FRAC_PI_4), - ty: 5.0 + ty: Twips::new(5) }, (Twips::new(100), Twips::new(100)), (Twips::new(419), Twips::new(5)) @@ -639,10 +647,10 @@ mod tests { Matrix { a: f32::cos(std::f32::consts::FRAC_PI_4), c: f32::sin(std::f32::consts::FRAC_PI_4), - tx: -5.0, + tx: Twips::new(-5), b: -f32::sin(std::f32::consts::FRAC_PI_4), d: f32::cos(std::f32::consts::FRAC_PI_4), - ty: 5.0 + ty: Twips::new(5) }, (Twips::new(100), Twips::new(100)), (Twips::new(136), Twips::new(5)) @@ -652,13 +660,38 @@ mod tests { Matrix { a: f32::cos(std::f32::consts::FRAC_PI_4), c: f32::sin(std::f32::consts::FRAC_PI_4), - tx: 0.0, + tx: Twips::new(0), b: -f32::sin(std::f32::consts::FRAC_PI_4), d: f32::cos(std::f32::consts::FRAC_PI_4), - ty: 10.0 * f32::sin(std::f32::consts::FRAC_PI_4) + ty: Twips::new((10.0 * f32::sin(std::f32::consts::FRAC_PI_4)) as i32) }, (Twips::new(105), Twips::new(95)), (Twips::new(141), Twips::new(0)) ) ); } + +/// 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 transforms. +/// Although this is easy to do on most architectures, Rust provides no standard +/// way to round in this manner (`f32::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(f: f32) -> i32 { + if f.is_finite() { + let a = f.abs(); + if f < 2_147_483_648.0_f32 { + let k = 1.0 / std::f32::EPSILON; + let out = if a < k { ((a + k) - k).copysign(f) } else { f }; + out as i32 + } else { + // Out-of-range clamps to MIN. + std::i32::MIN + } + } else { + // NAN/Infinity goes to 0. + 0 + } +} diff --git a/core/src/player.rs b/core/src/player.rs index 1a6dbe517..d034d8cb3 100644 --- a/core/src/player.rs +++ b/core/src/player.rs @@ -728,8 +728,8 @@ impl Player { b: 0.0, c: 0.0, d: scale, - tx: margin_width * 20.0, - ty: margin_height * 20.0, + tx: Twips::from_pixels(margin_width.into()), + ty: Twips::from_pixels(margin_height.into()), }; self.inverse_view_matrix = self.view_matrix; self.inverse_view_matrix.invert(); diff --git a/core/tests/regression_tests.rs b/core/tests/regression_tests.rs index 946839198..2f0124162 100644 --- a/core/tests/regression_tests.rs +++ b/core/tests/regression_tests.rs @@ -181,10 +181,10 @@ swf_tests! { // Eventually we can hopefully make some of these match exactly (see #193). // Some will probably always need to be approx. (if they rely on trig functions, etc.) swf_tests_approx! { - (local_to_global, "avm1/local_to_global", 1, 0.4), + (local_to_global, "avm1/local_to_global", 1, 0.051), (stage_object_properties, "avm1/stage_object_properties", 4, 0.051), (stage_object_properties_swf6, "avm1/stage_object_properties_swf6", 4, 0.051), - (movieclip_getbounds, "avm1/movieclip_getbounds", 1, 0.1), + (movieclip_getbounds, "avm1/movieclip_getbounds", 1, 0.051), } #[test] diff --git a/core/tests/swfs/avm1/movieclip_getbounds/test.swf b/core/tests/swfs/avm1/movieclip_getbounds/test.swf index 95154f477bdd661eedffd32efb982a7555c1502f..9a86ab293e761f1a9f00d39ec7756f871e38d7c8 100644 GIT binary patch delta 791 zcmV+y1L*v;2C)W4LswG|M+*P|c$~GC&ubGw6vy9gHk(cQEBz6h#@ME|Zkk#fY)=-g z#R{HUq#i6qh^0+Rf@HO>7z-7tAc7!x=z@x%;E$0;D1U)J1JLmjAS%H5PQYtctW+vz zeLF#bhfjQID9z=IaEA<~x#behaDa3=v2rbceVh(!S^b=LtGIe|Bn3nOO8|X+c_q7$ zUD4e#EG^ZLXpFfAGwYhBr+|KWEil0fn6Q{yIGIl3@{R!_kRYwy0U*Dps*p7|!ATp5 zLpGN$W`9>1HwhZ(+9iEGUtEKnra7f(t~J{hb2wAdH7{w2)#HxW)n(G8so3x8%sK4Q zwfcA?$R$*5t83asUFlS2ZRA9=g?|U^t+U^K8s+bBE#}Y{mz>&W4n3)z!tBW79^0Qv z0C5xq4Q9X)K@z%Nh>!w9!l?rDDw_!AL^LP9d4Kh(2r|o2aa1Ha`cg*K~h`H)aLM3inle3w^O{MSv;&7K~OsFC=Yc} zsM{Orp-{vdI;0v3_?Xn|#7Lk-mA>bG(rju5B{psN88CaA{60(zjMfTtTnCl7Z7=Ve zKz~WG8S)4%Qt~dcpF#u8te0?i_aFx!ZEVzklr+?=yZO{%FL(3!F)w%Xxa#F@9#8V_ z=AjYZ-8_`y-OWRzoVy1P>h9yt?&kLzb8)u|>ELdjJi)u0hsNF9U5`Q;25>6CmFQ-8 zc^?Go@Nh(XwZD=0{I+#!VD`;ELUB|@Sbu~~5C~KuQ1_SLr!p6Yqd)HhRDxlYd>DEl z%lK#ke6A366i3Naaq+U;gNiC5n!86bF0Se9#gFkrIQkJR%Mro}nEa{GR_O5WOkMs_ z1IuzB3;W?~A+)shacS#)Cimg{ZHAQP7zz7um)$~05AGI%A=tQqTe4TQG9+kguSmjJ z7(dl|B9A2K-`?7L_v_i+y~PBcW*X&yd3w3-^s()sO|YKG7-=UFiA3brC+RB}J1Ds? VDw+r4-(sm5_HITZe*lpXGe${LfpGu; delta 794 zcmV+#1Lgd&2DAo7LswG|ObY-2c$~GC&ubGw6vy9gHk(cQEBz6h#@HXW-88i}*q$s} zixoVzNIh7Jkd`(n36j;iVk}gof(U}(p$jU47QvB5D1U-K4bb))AS%G6cED>^ESJj{ ze0xEF$IpBzD9+^yaE}bdxs@W$aDY^*fAvQ0<~SYJGx|mCPGRl#ND_zumH_(3%4%jI zv#PsgSX#0!Q6F;+rZ+TAPXhh&T3~_|Fkvy(a3Yn!;`oTx+&1=5VHjYhJ<y6mMx1Z>4x!qj*>~f}ph9Q6B1` zP^UN4MWKi{bWAlA@G+^|iIG5wDm^d#q}kLAN^IKjGhp@{`F)%g7_AoQxDG0D+g{!` zfq#->BjgEMq~u*>FNOLVS+C#S-2)tavc6IOQPN!(Q&@an;M+Jf7g) z%|j!+yLl+dyPJnbId=~n)!nC@-OcYc=HhM_(!t$4d75`O4~@IIyB38K^x;&1>(Q<7 z${`5U;o*q3+q{{c-EE#){`}?;p*X4{EPuil2m~qY+w9Q_EE{ zf+e|!h5hg~A6nY}xU~H~o&E6rE<;LkjD&r-%YHtj2lw;A5NzJUE!itt35IEFmrG$R zj2~K$Kn@1&ktY)L?ra~t`}N}f!D2riX&U8#S%O?sLTqPn3#`X7Mp{WkA`$uR8T#JE YPD^f%iVlMKw^(w9y`YiEABonjB(I!nJKr$kUlawUN zlRQdViLTsa6L0G{mhHAlP$lyyv=#I-QWR7QM4cB9srlP^3sy6)i%7 zs;KZ&%aHClOH<<;{XBNOJvjA>33t+WEz%np+87YW5-LI#PZcNcoUq1(Pcv*av&plh) zUGMmL!wZ*RUOYM9`Cz#A?LJs}75XoHG`{q4y7iU1;%#S--0J+}oMV1vK)C(*+V4L` z+K)_?H|?$bvU%x1Y}hk&tL@DDDNAYNpPycR)N{qN=KiE?WKY!&Uz7VA%64<+*W=eu z9e?Pj$39HIDV(3E_q0(LnbMM*mYUeo0`<;mSWGpXNcr!uOEaiy{FmNcg7us~MBvf?FcmZDaAmFmm9bUL?AKVZ6;5dU)=5#qI9@L z#_c-XD&wpUx5;>(4o5Oh>u|e_Gdi4=ahnd$lX0ZO9Ww6H;Z7NM=x~=(FCbS0jpCwu z)~T$7Bq_2jt^gXQRa=}n`YdaUGef&6)fQ)jj^bNVw#5}ed$Rt-m!fjXW|bLPscK1x z{|7;X7i~5QpCxF*;Ihq&OFxBA*S8|#>S?aaBo0mZu&rD0VOxcA7~~Vu_7|duLJ?uq zhs%R}LfrhCOrs~%qh=fbPBt-eFdpZl$+39mIFcqloY-$fs*AY+VP$iVIv5K@54HQ6sGSMq1y9^ne&shBh+lZcu%*THmbd z0mq+3=ZU-2$S&QL?Mi1aW+U5a#^RB)j44AK8Fk01b8=|sk}5z)pYDGbrZlWF56MyDiF zntV~F?8W9(Nlpq6+{>7E(kz6w7j53FG$)1C*sH2>i+S&|d8^pMCZ2OE)i7%XNvCu4 zz+%`W3Ec%Ow8tXgRE17TA8IqngCujZ#A#%UR5tme%8+&_A6JH)!yrOZ02%2b?9Boe zJz$ltG6Jw=({H4BqIKUbc7n)p+%g1XOnYggp#6t0wW38uZT{(MvDwdvdpFK|_);X@ K;(r6JV;CKBubXKA literal 1358 zcmV-U1+n@=S5pr!5C8ypoaI-4OdC}k|K45O*P|^hv}53a!N6h+!eDA*{{T9ef&)57 znQTrk9URbEO4D`&=0Esjx*3-k{~%#AbCS8uL^q9_X32zU5NBitqfVpC@YmuN6ZH>u zi3zLku6NgWcfG|w%>2V9efPef&wJl{?|t9=OCI1~Go4DM8kt1~ zV9y;?C3Gc2@nkqUP!@|1$9P=j6FvZL)Cawha12*~8}s2ma3kTq@ER?2Bf-n<= zg(MI`SV@8A3}kwhLr|ock%Ow$EppM!^;C)gTw14g^p?6sKB^~LnDE5RY3mk)0Io0! zV6jmEON;_oY81egMgc4{3ShZW09P3WutFwa1{l1I6h!ULm&bRPj?=Ja%_L0XWhpCp z;-0$mPi@$txeczE!PtJD~Bd~9MXx4T5{R_`qj*rv(;ji1_!0+BK-s`>l zncO#yRRO#DqlXXAyFIl9g^&LB>?1!6kH7R9%x$bXQB$46dKZWI%V*73#~X6D_qR;0 zpZfaUTko1(+5Pz^OIMG(>o$3JUG6S9y70i2KmAY6U7Y@KVf65JNU_~m?P$CdjZCAU zdk4XJ1Kkt#P8K^wDpBPnlQP z?i8XlxJkmT8r&@5tOmD8c#Z}~5>9JytAsNeoRx5k2G5ajq`_?x?$F?N3Abr*hg^4& zD}wrPQ88hcS3;B&$re@s_0Wnf%p9G`*uu=vW=gSz8KK?ymXvH^MbMUvKjEdQNVHjb zMrNu)6ypCu(BNr{g~DeEnlLzH@!--+;nVf8fH*qW)}<4NCVbcyP57`aKoJD_gt+~~ z(Vd}4xUUSCJNSgK`Bj-lXQ)%jHvXM#qIX9;&PS8|@$_*dO?)`9--uWjas$H3a46SFB9jnaArk<0H^k8EQ8CuUM zlBD={sC_#~4-UqVq4iAKcP`=EsrKz8JvbRdhSoDKVy*L#M;)iUbenvljayV0qK!M< z#zTxwPNY=%qC{Dj8B+yWDLilwW86u#5b9pEaj)fBDXhjGMU9({dlw!s3oR_dIX6=c z6J`)~I!g~MhE1Z-y@Q2zPXz3W(240oZ8~`nWloYfm28p9CVf=t(hm9KN|$r!L`V!E zBVL4o3}Dg%X7MT`081wQMzSYb^W9=4h%Cn~O)&bjm(~l~fA~@}IwGshKV2<0^BHmT Q#(5K8io{#|Z%u+8*H5pHqyPW_ diff --git a/desktop/src/render.rs b/desktop/src/render.rs index 0dfff6ab1..8e81fef0e 100644 --- a/desktop/src/render.rs +++ b/desktop/src/render.rs @@ -710,8 +710,8 @@ impl RenderBackend for GliumRenderBackend { [transform.matrix.c, transform.matrix.d, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [ - transform.matrix.tx / 20.0, - transform.matrix.ty / 20.0, + transform.matrix.tx.to_pixels() as f32, + transform.matrix.ty.to_pixels() as f32, 0.0, 1.0, ], diff --git a/web/src/render.rs b/web/src/render.rs index 75f5b33ca..71874a3d4 100644 --- a/web/src/render.rs +++ b/web/src/render.rs @@ -305,8 +305,8 @@ impl WebCanvasRenderBackend { matrix.b.into(), matrix.c.into(), matrix.d.into(), - f64::from(matrix.tx) / 20.0, - f64::from(matrix.ty) / 20.0, + matrix.tx.to_pixels(), + matrix.ty.to_pixels(), ) .unwrap(); } @@ -774,8 +774,8 @@ fn swf_shape_to_svg( let shift = Matrix { a: 32768.0 / width, d: 32768.0 / height, - tx: -16384.0, - ty: -16384.0, + tx: swf::Twips::new(-16384), + ty: swf::Twips::new(-16384), ..Default::default() }; let gradient_matrix = matrix * shift; @@ -821,8 +821,6 @@ fn swf_shape_to_svg( let shift = Matrix { a: 32768.0, d: 32768.0, - tx: 0.0, - ty: 0.0, ..Default::default() }; let gradient_matrix = matrix * shift; @@ -874,8 +872,6 @@ fn swf_shape_to_svg( let shift = Matrix { a: 32768.0, d: 32768.0, - tx: 0.0, - ty: 0.0, ..Default::default() }; let gradient_matrix = matrix * shift; @@ -1219,8 +1215,8 @@ fn swf_shape_to_canvas_commands( matrix.set_b(a.b); matrix.set_c(a.c); matrix.set_d(a.d); - matrix.set_e(a.tx); - matrix.set_f(a.ty); + matrix.set_e(a.tx.get() as f32); + matrix.set_f(a.ty.get() as f32); bitmap_pattern.set_transform(&matrix);