Add a hidden API to apply a transform the content of an event.

Unlike normal transform, this actually transforms each
point directly. This is only useful for transforming
injected events.

Bug: 179274888
Test: atest libinput_tests:MotionEventTest
Change-Id: Ifbd90649156d2dd00087bf5a97a372ccaea3d5f3
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index deb4679..649a140 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -569,6 +569,24 @@
     }
 }
 
+void MotionEvent::applyTransform(const std::array<float, 9>& matrix) {
+    // Determine how the origin is transformed by the matrix so that we
+    // can transform orientation vectors.
+    vec2 origin = transformPoint(matrix, 0, 0);
+
+    // Apply the transformation to all samples.
+    size_t numSamples = mSamplePointerCoords.size();
+    for (size_t i = 0; i < numSamples; i++) {
+        PointerCoords& c = mSamplePointerCoords.editItemAt(i);
+        float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+        c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
+                       transformAngle(matrix, orientation, origin.x, origin.y));
+        vec2 xy = transformPoint(matrix, c.getX(), c.getY());
+        c.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+        c.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+    }
+}
+
 #ifdef __linux__
 static status_t readFromParcel(ui::Transform& transform, const Parcel& parcel) {
     float dsdx, dtdx, tx, dtdy, dsdy, ty;
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 15ab428..32b72ba 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -636,28 +636,54 @@
     ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
 }
 
-TEST_F(MotionEventTest, RawCompatTransform) {
-    auto createTouchDownEvent = [](int x, int y, ui::Transform transform) {
-        std::vector<PointerProperties> pointerProperties;
-        pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER});
-        std::vector<PointerCoords> pointerCoords;
-        pointerCoords.emplace_back().clear();
-        pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, x);
-        pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-        nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
-        MotionEvent event;
-        event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN,
-                         /* displayId */ 0, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
-                         /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
-                         /* buttonState */ 0, MotionClassification::NONE, transform,
-                         /* xPrecision */ 0, /* yPrecision */ 0,
-                         AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                         AMOTION_EVENT_INVALID_CURSOR_POSITION, /* displayWidth */ 400,
-                         /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(),
-                         pointerProperties.data(), pointerCoords.data());
-        return event;
-    };
+MotionEvent createTouchDownEvent(int x, int y, ui::Transform transform) {
+    std::vector<PointerProperties> pointerProperties;
+    pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER});
+    std::vector<PointerCoords> pointerCoords;
+    pointerCoords.emplace_back().clear();
+    pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_X, x);
+    pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+    nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+    MotionEvent event;
+    event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN,
+                     /* displayId */ 0, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
+                     /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
+                     /* buttonState */ 0, MotionClassification::NONE, transform,
+                     /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+                     AMOTION_EVENT_INVALID_CURSOR_POSITION, /* displayWidth */ 400,
+                     /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(),
+                     pointerProperties.data(), pointerCoords.data());
+    return event;
+}
 
+TEST_F(MotionEventTest, ApplyTransform) {
+    // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+    ui::Transform identity;
+    ui::Transform xform(ui::Transform::ROT_90, 800, 400);
+    xform.set(xform.tx() + 20, xform.ty() + 40);
+    MotionEvent event = createTouchDownEvent(60, 100, xform);
+    ASSERT_EQ(700, event.getRawX(0));
+    ASSERT_EQ(60, event.getRawY(0));
+    ASSERT_NE(event.getRawX(0), event.getX(0));
+    ASSERT_NE(event.getRawY(0), event.getY(0));
+
+    MotionEvent changedEvent = createTouchDownEvent(60, 100, identity);
+    const std::array<float, 9> rowMajor{xform[0][0], xform[1][0], xform[2][0],
+                                        xform[0][1], xform[1][1], xform[2][1],
+                                        xform[0][2], xform[1][2], xform[2][2]};
+    changedEvent.applyTransform(rowMajor);
+
+    // transformContent effectively rotates the raw coordinates, so those should now include
+    // both rotation AND offset
+    ASSERT_EQ(720, changedEvent.getRawX(0));
+    ASSERT_EQ(100, changedEvent.getRawY(0));
+
+    // The transformed output should be the same then
+    ASSERT_NEAR(event.getX(0), changedEvent.getX(0), 0.001);
+    ASSERT_NEAR(event.getY(0), changedEvent.getY(0), 0.001);
+}
+
+TEST_F(MotionEventTest, RawCompatTransform) {
     {
         // Make sure raw is raw regardless of transform translation.
         ui::Transform xform;