From 0955ab40e67d4668f3ebd5fbdf3602750e35abb8 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 28 May 2023 16:53:46 -0500 Subject: [PATCH] avm2: Fix rounding behavior in BitmapData rectangle operations Flash Player performs `x + width` and `y + height` as floating point operations before `round_to_even`. This affects the extent covered by a `Rectangle` in various BitmapData methods, as the sum of two values might be large enough to be rounded up to a larger value (when rounding `x` and `width` individually would have produced a smaller overall extent). --- .../avm2/globals/flash/display/bitmap_data.rs | 25 +++++++++--- .../bitmapdata_rectangle_rounding/Test.as | 37 ++++++++++++++++++ .../bitmapdata_rectangle_rounding/output.txt | 16 ++++++++ .../bitmapdata_rectangle_rounding/test.fla | Bin 0 -> 3815 bytes .../bitmapdata_rectangle_rounding/test.swf | Bin 0 -> 1140 bytes .../bitmapdata_rectangle_rounding/test.toml | 1 + 6 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/Test.as create mode 100644 tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/output.txt create mode 100644 tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/test.fla create mode 100644 tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/test.swf create mode 100644 tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/test.toml diff --git a/core/src/avm2/globals/flash/display/bitmap_data.rs b/core/src/avm2/globals/flash/display/bitmap_data.rs index 9adbec744..3899c5418 100644 --- a/core/src/avm2/globals/flash/display/bitmap_data.rs +++ b/core/src/avm2/globals/flash/display/bitmap_data.rs @@ -17,30 +17,45 @@ use crate::bitmap::{is_size_valid, operations}; use crate::character::Character; use crate::display_object::Bitmap; use crate::display_object::TDisplayObject; +use crate::ecma_conversions::round_to_even; use crate::swf::BlendMode; use gc_arena::GcCell; use ruffle_render::filters::Filter; use ruffle_render::transform::Transform; use std::str::FromStr; +// Computes the integer x,y,width,height values from +// the given `Rectangle`. This method performs `x + width` +// and `y + height` as floating point operations before +// `round_to_even`, which is needed to match Flash Player's +// rounding behavior. fn get_rectangle_x_y_width_height<'gc>( activation: &mut Activation<'_, 'gc>, rectangle: Object<'gc>, ) -> Result<(i32, i32, i32, i32), Error<'gc>> { let x = rectangle .get_public_property("x", activation)? - .coerce_to_i32(activation)?; + .coerce_to_number(activation)?; let y = rectangle .get_public_property("y", activation)? - .coerce_to_i32(activation)?; + .coerce_to_number(activation)?; let width = rectangle .get_public_property("width", activation)? - .coerce_to_i32(activation)?; + .coerce_to_number(activation)?; let height = rectangle .get_public_property("height", activation)? - .coerce_to_i32(activation)?; + .coerce_to_number(activation)?; - Ok((x, y, width, height)) + let x_max = round_to_even(x + width); + let y_max = round_to_even(y + height); + + let x_int = round_to_even(x); + let y_int = round_to_even(y); + + let width_int = x_max - x_int; + let height_int = y_max - y_int; + + Ok((x_int, y_int, width_int, height_int)) } /// Copy the static data from a given Bitmap into a new BitmapData. diff --git a/tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/Test.as b/tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/Test.as new file mode 100644 index 000000000..d9d26ce3a --- /dev/null +++ b/tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/Test.as @@ -0,0 +1,37 @@ +package { + import flash.display.BitmapData; + import flash.geom.Rectangle; + + public class Test { + public function Test() { + var otherData = new BitmapData(5, 5, true, 0); + otherData.fillRect(new Rectangle(1.6, 1.6, 1.8, 2), 0xFFFFFFFF); + trace("First fillRect"); + print(otherData); + + otherData.fillRect(new Rectangle(0.1, 0, 1.8, 2.5), 0xFFaabbcc); + trace("Second fillRect"); + print(otherData); + + var bytes = otherData.getPixels(new Rectangle(0.5, 0.5, 2.5, 2.5)); + bytes.position = 0; + trace("Bytes len: " + bytes.length); + var byteString = ""; + while (bytes.bytesAvailable != 0) { + byteString += bytes.readUnsignedInt().toString(16) + " "; + } + trace("Bytes: " + byteString); + } + + private function print(data: BitmapData) { + for (var y = 0; y < data.height; y++) { + var line = ""; + for (var x = 0; x < data.width; x++) { + line += data.getPixel32(x, y).toString(16) + " " + } + trace(line) + } + trace() + } + } +} \ No newline at end of file diff --git a/tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/output.txt b/tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/output.txt new file mode 100644 index 000000000..0ec1a1bfd --- /dev/null +++ b/tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/output.txt @@ -0,0 +1,16 @@ +First fillRect +0 0 0 0 0 +0 0 0 0 0 +0 0 ffffffff 0 0 +0 0 ffffffff 0 0 +0 0 0 0 0 + +Second fillRect +ffaabbcc ffaabbcc 0 0 0 +ffaabbcc ffaabbcc 0 0 0 +0 0 ffffffff 0 0 +0 0 ffffffff 0 0 +0 0 0 0 0 + +Bytes len: 36 +Bytes: ffaabbcc ffaabbcc 0 ffaabbcc ffaabbcc 0 0 0 ffffffff diff --git a/tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/test.fla b/tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/test.fla new file mode 100644 index 0000000000000000000000000000000000000000..4f112b21e316648b67d01c2aced0b90fdbead746 GIT binary patch literal 3815 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 R@9YA8DS*KX0q%i7{{v3} literal 0 HcmV?d00001 diff --git a/tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/test.swf b/tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/test.swf new file mode 100644 index 0000000000000000000000000000000000000000..ccde4230d5ee4ae2c0ed7b8f0fc1fb2eeeab6b48 GIT binary patch literal 1140 zcmV-)1dIDaS5qsH1^@tfoSjwOP8&xQo-_M1yKAt`j|~A#OtMLvgyIF#G;%H|fl?(^ zN>b7^s<80d9@|@)wQCOq?jox%kjwUpyFNp&@&c`7j2hIts4w8nMK3Bwtws9lkz-ynTQ}3$VQR-JUvgy%h6F?8gRtP97^Md@8$S&>6f47_-l!f*T~#k z4(&fJk^WFWCgl14SAX=DL;dyAEn+_AEs>F8uv~Ln)wz<_YSi3)>#irZ-Nvdb+~Gc? z(e!p*!L#`G#T9|3EnJX&;ClXJZ=2UVpJ(1$%T0Hi&)@hD)Oo8ao#^X@Z+KB?eILdd zZs0u5d{E!@_)5)dB;LuRhpv~t&Th*2)~RegE4Mb?-R*_VLO-t(=C##CU#?%N@vUZk z$1iz)<<1=$#+=(S>!Qk=GJCDCj}(@a8v^61SYe$iyuNLIamHYjy;@Cb)+vF#czdQQ zL?fHYxTSi5&lT(2ndObe%!1?G$`p3In(%y0G~FVPu6fOtNS`spHh8h_m)`1Tg^P#Y z99@R!&rA1Y_3B}`kdF2!G z)?b`WtFi{Luga>yo^|GBaY1|Ll|)sqa<5Vqsk182ov(8CbcdsEskA1)Hi}KJAuj(5 z_`Eg{mXGgpU$kPM$k!^ioGXf0@WIuv`@%jL!aO)uNO-$Ev?TvAzf$)FGV`XsfgL0G zNB?irH={`<3FC%lBqQU|u}E@M8yf>-e8Na(6sDTgiYNm>rx>V=8jPAuwU{=*V34sG zGZJPJ7`HZH5C9bj)pSEAIxrv_9%e!npeh1EQvnRbD5}zNKE-9as%l+$6A$!` zgU>zEgCW9tFbwe?jL?yQ<-pdiU$x17P}{!-#$nFL0AAFl_YF0pteZ42bJpy{x&?vN zIb2sE7?2>o?cguqZh@XXE zqD^h$yIs<&ng1=sKOVj-=no?u+)T?qPW}@grI<<*ap!1eY-wg<{ReIu1so zMIlTMkf zCux6{A-#E{<8UIW$DI7QlfU5PC!GAGlh^Tn3aM~ZwKpI3%d<2xaoR%fJc$H<0OW52 GGw4_!CP<9{ literal 0 HcmV?d00001 diff --git a/tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/test.toml b/tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/test.toml new file mode 100644 index 000000000..dbee897f5 --- /dev/null +++ b/tests/tests/swfs/avm2/bitmapdata_rectangle_rounding/test.toml @@ -0,0 +1 @@ +num_frames = 1