From 3777e026e91ea333f1067a48dc812377c8b9edf1 Mon Sep 17 00:00:00 2001 From: Eric Rozell Date: Tue, 14 Jun 2022 13:12:56 -0400 Subject: [PATCH 1/4] Uses custom matrix multiplication logic for transforms As reported in #10111, float4x4's operator* does not produce the same output as expected on other RN platforms. This change ports the logic from RN's MatrixMath.js to ensure multiplication of transforms produces the same result on Windows as on iOS and Android. Fixes #10111 --- .../Views/FrameworkElementViewManager.cpp | 103 +++++++++++++----- 1 file changed, 76 insertions(+), 27 deletions(-) diff --git a/vnext/Microsoft.ReactNative/Views/FrameworkElementViewManager.cpp b/vnext/Microsoft.ReactNative/Views/FrameworkElementViewManager.cpp index 86de820ef0d..1ccf1acf26e 100644 --- a/vnext/Microsoft.ReactNative/Views/FrameworkElementViewManager.cpp +++ b/vnext/Microsoft.ReactNative/Views/FrameworkElementViewManager.cpp @@ -115,6 +115,51 @@ inline float ToRadians(const winrt::Microsoft::ReactNative::JSValue &value) { return static_cast(num); // assume suffix is "rad" } +static void MultiplyInto( + winrt::Windows::Foundation::Numerics::float4x4 &m, + winrt::Windows::Foundation::Numerics::float4x4 o) { + // The `float4x4` type has an `operator*` implementation for matrix + // multiplication. However, the result of the multiplication differs from the + // result computed in: + // https://github.com/facebook/react-native/blob/master/Libraries/Utilities/MatrixMath.js + + float a00 = m.m11, a01 = m.m12, a02 = m.m13, a03 = m.m14, a10 = m.m21, a11 = m.m22, a12 = m.m23, a13 = m.m24, + a20 = m.m31, a21 = m.m32, a22 = m.m33, a23 = m.m34, a30 = m.m41, a31 = m.m42, a32 = m.m43, a33 = m.m44; + + float b0 = o.m11, b1 = o.m12, b2 = o.m13, b3 = o.m14; + m.m11 = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + m.m12 = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + m.m13 = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + m.m14 = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + + b0 = o.m21; + b1 = o.m22; + b2 = o.m23; + b3 = o.m24; + m.m21 = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + m.m22 = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + m.m23 = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + m.m24 = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + + b0 = o.m31; + b1 = o.m32; + b2 = o.m33; + b3 = o.m34; + m.m31 = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + m.m32 = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + m.m33 = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + m.m34 = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + + b0 = o.m41; + b1 = o.m42; + b2 = o.m43; + b3 = o.m44; + m.m41 = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; + m.m42 = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; + m.m43 = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; + m.m44 = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; +} + void FrameworkElementViewManager::GetNativeProps(const winrt::Microsoft::ReactNative::IJSValueWriter &writer) const { Super::GetNativeProps(writer); @@ -181,52 +226,56 @@ bool FrameworkElementViewManager::UpdateProperty( innerMatrix.m42 = static_cast(innerValue[13].AsDouble()); innerMatrix.m43 = static_cast(innerValue[14].AsDouble()); innerMatrix.m44 = static_cast(innerValue[15].AsDouble()); - transformMatrix = transformMatrix * innerMatrix; + MultiplyInto(transformMatrix, innerMatrix); } else if (transformType == "perspective") { auto innerMatrix = winrt::Windows::Foundation::Numerics::float4x4::identity(); innerMatrix.m34 = -1 / innerValue.AsSingle(); - transformMatrix = transformMatrix * innerMatrix; + MultiplyInto(transformMatrix, innerMatrix); } else if (transformType == "rotateX") { - transformMatrix = transformMatrix * - winrt::Windows::Foundation::Numerics::make_float4x4_rotation_x(ToRadians(innerValue)); + MultiplyInto( + transformMatrix, + winrt::Windows::Foundation::Numerics::make_float4x4_rotation_x(ToRadians(innerValue))); } else if (transformType == "rotateY") { - transformMatrix = transformMatrix * - winrt::Windows::Foundation::Numerics::make_float4x4_rotation_y(ToRadians(innerValue)); + MultiplyInto(transformMatrix, + winrt::Windows::Foundation::Numerics::make_float4x4_rotation_y(ToRadians(innerValue))); } else if (transformType == "rotate" || transformType == "rotateZ") { - transformMatrix = transformMatrix * - winrt::Windows::Foundation::Numerics::make_float4x4_rotation_z(ToRadians(innerValue)); + MultiplyInto( + transformMatrix, + winrt::Windows::Foundation::Numerics::make_float4x4_rotation_z(ToRadians(innerValue))); } else if (transformType == "scale") { - transformMatrix = transformMatrix * + MultiplyInto(transformMatrix, winrt::Windows::Foundation::Numerics::make_float4x4_scale( - innerValue.AsSingle(), innerValue.AsSingle(), 1); + innerValue.AsSingle(), innerValue.AsSingle(), 1)); } else if (transformType == "scaleX") { - transformMatrix = transformMatrix * - winrt::Windows::Foundation::Numerics::make_float4x4_scale(innerValue.AsSingle(), 1, 1); + MultiplyInto(transformMatrix, + winrt::Windows::Foundation::Numerics::make_float4x4_scale(innerValue.AsSingle(), 1, 1)); } else if (transformType == "scaleY") { - transformMatrix = transformMatrix * - winrt::Windows::Foundation::Numerics::make_float4x4_scale(1, innerValue.AsSingle(), 1); + MultiplyInto(transformMatrix, + winrt::Windows::Foundation::Numerics::make_float4x4_scale(1, innerValue.AsSingle(), 1)); } else if (transformType == "translate") { auto ¶ms = innerValue.AsArray(); - transformMatrix = - transformMatrix * + MultiplyInto( + transformMatrix, winrt::Windows::Foundation::Numerics::make_float4x4_translation( - params[0].AsSingle(), params[1].AsSingle(), params.size() > 2 ? params[2].AsSingle() : 0.f); + params[0].AsSingle(), params[1].AsSingle(), params.size() > 2 ? params[2].AsSingle() : 0.f)); } else if (transformType == "translateX") { - transformMatrix = transformMatrix * - winrt::Windows::Foundation::Numerics::make_float4x4_translation(innerValue.AsSingle(), 0.f, 0.f); + MultiplyInto( + transformMatrix, + winrt::Windows::Foundation::Numerics::make_float4x4_translation(innerValue.AsSingle(), 0.f, 0.f)); } else if (transformType == "translateY") { - transformMatrix = transformMatrix * - winrt::Windows::Foundation::Numerics::make_float4x4_translation(0.f, innerValue.AsSingle(), 0.f); + MultiplyInto( + transformMatrix, + winrt::Windows::Foundation::Numerics::make_float4x4_translation(0.f, innerValue.AsSingle(), 0.f)); } else if (transformType == "skewX") { - transformMatrix = - transformMatrix * + MultiplyInto( + transformMatrix, winrt::Windows::Foundation::Numerics::float4x4( - winrt::Windows::Foundation::Numerics::make_float3x2_skew(innerValue.AsSingle(), 0.f)); + winrt::Windows::Foundation::Numerics::make_float3x2_skew(innerValue.AsSingle(), 0.f))); } else if (transformType == "skewY") { - transformMatrix = - transformMatrix * + MultiplyInto( + transformMatrix, winrt::Windows::Foundation::Numerics::float4x4( - winrt::Windows::Foundation::Numerics::make_float3x2_skew(0.f, innerValue.AsSingle())); + winrt::Windows::Foundation::Numerics::make_float3x2_skew(0.f, innerValue.AsSingle()))); } } } From 064cabe7507347a65e58159f99aa909670aa6e51 Mon Sep 17 00:00:00 2001 From: Eric Rozell Date: Tue, 14 Jun 2022 13:25:52 -0400 Subject: [PATCH 2/4] yarn format --- .../Views/FrameworkElementViewManager.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/vnext/Microsoft.ReactNative/Views/FrameworkElementViewManager.cpp b/vnext/Microsoft.ReactNative/Views/FrameworkElementViewManager.cpp index 1ccf1acf26e..646234cc5a4 100644 --- a/vnext/Microsoft.ReactNative/Views/FrameworkElementViewManager.cpp +++ b/vnext/Microsoft.ReactNative/Views/FrameworkElementViewManager.cpp @@ -236,21 +236,25 @@ bool FrameworkElementViewManager::UpdateProperty( transformMatrix, winrt::Windows::Foundation::Numerics::make_float4x4_rotation_x(ToRadians(innerValue))); } else if (transformType == "rotateY") { - MultiplyInto(transformMatrix, + MultiplyInto( + transformMatrix, winrt::Windows::Foundation::Numerics::make_float4x4_rotation_y(ToRadians(innerValue))); } else if (transformType == "rotate" || transformType == "rotateZ") { MultiplyInto( transformMatrix, winrt::Windows::Foundation::Numerics::make_float4x4_rotation_z(ToRadians(innerValue))); } else if (transformType == "scale") { - MultiplyInto(transformMatrix, + MultiplyInto( + transformMatrix, winrt::Windows::Foundation::Numerics::make_float4x4_scale( - innerValue.AsSingle(), innerValue.AsSingle(), 1)); + innerValue.AsSingle(), innerValue.AsSingle(), 1)); } else if (transformType == "scaleX") { - MultiplyInto(transformMatrix, + MultiplyInto( + transformMatrix, winrt::Windows::Foundation::Numerics::make_float4x4_scale(innerValue.AsSingle(), 1, 1)); } else if (transformType == "scaleY") { - MultiplyInto(transformMatrix, + MultiplyInto( + transformMatrix, winrt::Windows::Foundation::Numerics::make_float4x4_scale(1, innerValue.AsSingle(), 1)); } else if (transformType == "translate") { auto ¶ms = innerValue.AsArray(); From 661b8ce5c763910cd30ab7ecb9bcdca17a6de29d Mon Sep 17 00:00:00 2001 From: Eric Rozell Date: Tue, 14 Jun 2022 13:26:07 -0400 Subject: [PATCH 3/4] Change files --- ...ative-windows-3db31062-b08b-44b3-8158-a73f139c1714.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/react-native-windows-3db31062-b08b-44b3-8158-a73f139c1714.json diff --git a/change/react-native-windows-3db31062-b08b-44b3-8158-a73f139c1714.json b/change/react-native-windows-3db31062-b08b-44b3-8158-a73f139c1714.json new file mode 100644 index 00000000000..d45f487e821 --- /dev/null +++ b/change/react-native-windows-3db31062-b08b-44b3-8158-a73f139c1714.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "yarn format", + "packageName": "react-native-windows", + "email": "erozell@outlook.com", + "dependentChangeType": "patch" +} From 8f1d7941f6518ace8e4d12e9f637eb51719d8c07 Mon Sep 17 00:00:00 2001 From: Eric Rozell Date: Tue, 14 Jun 2022 15:53:04 -0400 Subject: [PATCH 4/4] Use float4x4 operator* but get the order correct --- .../Views/FrameworkElementViewManager.cpp | 41 +------------------ 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/vnext/Microsoft.ReactNative/Views/FrameworkElementViewManager.cpp b/vnext/Microsoft.ReactNative/Views/FrameworkElementViewManager.cpp index 646234cc5a4..412f4f90dc5 100644 --- a/vnext/Microsoft.ReactNative/Views/FrameworkElementViewManager.cpp +++ b/vnext/Microsoft.ReactNative/Views/FrameworkElementViewManager.cpp @@ -118,46 +118,7 @@ inline float ToRadians(const winrt::Microsoft::ReactNative::JSValue &value) { static void MultiplyInto( winrt::Windows::Foundation::Numerics::float4x4 &m, winrt::Windows::Foundation::Numerics::float4x4 o) { - // The `float4x4` type has an `operator*` implementation for matrix - // multiplication. However, the result of the multiplication differs from the - // result computed in: - // https://github.com/facebook/react-native/blob/master/Libraries/Utilities/MatrixMath.js - - float a00 = m.m11, a01 = m.m12, a02 = m.m13, a03 = m.m14, a10 = m.m21, a11 = m.m22, a12 = m.m23, a13 = m.m24, - a20 = m.m31, a21 = m.m32, a22 = m.m33, a23 = m.m34, a30 = m.m41, a31 = m.m42, a32 = m.m43, a33 = m.m44; - - float b0 = o.m11, b1 = o.m12, b2 = o.m13, b3 = o.m14; - m.m11 = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; - m.m12 = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; - m.m13 = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; - m.m14 = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; - - b0 = o.m21; - b1 = o.m22; - b2 = o.m23; - b3 = o.m24; - m.m21 = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; - m.m22 = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; - m.m23 = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; - m.m24 = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; - - b0 = o.m31; - b1 = o.m32; - b2 = o.m33; - b3 = o.m34; - m.m31 = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; - m.m32 = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; - m.m33 = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; - m.m34 = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; - - b0 = o.m41; - b1 = o.m42; - b2 = o.m43; - b3 = o.m44; - m.m41 = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30; - m.m42 = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31; - m.m43 = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32; - m.m44 = b0 * a03 + b1 * a13 + b2 * a23 + b3 * a33; + m = o * m; } void FrameworkElementViewManager::GetNativeProps(const winrt::Microsoft::ReactNative::IJSValueWriter &writer) const {