TouchInputMapper: Use ui::Transform to "cook" raw input coordinates
Instead of manually scaling and rotating the input device's raw
coordinates to the un-rotated display's coordinate space, we use a
transfomation matrix as a helper.
This change also exposed some bugs, where the tests were enforcing
incorrect behavior.
Bug: 236798672
Test: atest inputflinger_tests
Test: manual with touchscreen and stylus
Change-Id: I15b5fa6696720736ff1a598f88a4bc7067644cba
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 160f9eb..7c56631 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -82,13 +82,13 @@
}
static std::tuple<ui::Size /*displayBounds*/, Rect /*physicalFrame*/> getNaturalDisplayInfo(
- const DisplayViewport& viewport, ui::Rotation naturalOrientation) {
+ const DisplayViewport& viewport) {
ui::Size rotatedDisplaySize{viewport.deviceWidth, viewport.deviceHeight};
- if (naturalOrientation == ui::ROTATION_90 || naturalOrientation == ui::ROTATION_270) {
+ if (viewport.orientation == ui::ROTATION_90 || viewport.orientation == ui::ROTATION_270) {
std::swap(rotatedDisplaySize.width, rotatedDisplaySize.height);
}
- ui::Transform rotate(ui::Transform::toRotationFlags(naturalOrientation),
+ ui::Transform rotate(ui::Transform::toRotationFlags(viewport.orientation),
rotatedDisplaySize.width, rotatedDisplaySize.height);
Rect physicalFrame{viewport.physicalLeft, viewport.physicalTop, viewport.physicalRight,
@@ -224,6 +224,7 @@
dumpDisplay(dump);
dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n");
+ mRawToDisplay.dump(dump, "RawToDisplay Transform:", INDENT4);
dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale);
dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale);
dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision);
@@ -855,6 +856,34 @@
}
}
+ui::Transform TouchInputMapper::computeInputTransform() const {
+ const ui::Size rawSize{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()};
+
+ ui::Size rotatedRawSize = rawSize;
+ if (mInputDeviceOrientation == ui::ROTATION_270 || mInputDeviceOrientation == ui::ROTATION_90) {
+ std::swap(rotatedRawSize.width, rotatedRawSize.height);
+ }
+
+ // Step 1: Undo the raw offset so that the raw coordinate space now starts at (0, 0).
+ ui::Transform undoRawOffset;
+ undoRawOffset.set(-mRawPointerAxes.x.minValue, -mRawPointerAxes.y.minValue);
+
+ // Step 2: Rotate the raw coordinates to the expected orientation.
+ ui::Transform rotate;
+ // When rotating raw coordinates, the raw size will be used as an offset.
+ // Account for the extra unit added to the raw range when the raw size was calculated.
+ rotate.set(ui::Transform::toRotationFlags(-mInputDeviceOrientation), rotatedRawSize.width - 1,
+ rotatedRawSize.height - 1);
+
+ // Step 3: Scale the raw coordinates to the display space.
+ ui::Transform scaleToDisplay;
+ const float xScale = static_cast<float>(mDisplayBounds.width) / rotatedRawSize.width;
+ const float yScale = static_cast<float>(mDisplayBounds.height) / rotatedRawSize.height;
+ scaleToDisplay.set(xScale, 0, 0, yScale);
+
+ return (scaleToDisplay * (rotate * undoRawOffset));
+}
+
void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) {
const DeviceMode oldDeviceMode = mDeviceMode;
@@ -926,14 +955,7 @@
if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
const auto oldDisplayBounds = mDisplayBounds;
- // Apply the inverse of the input device orientation so that the input device is
- // configured in the same orientation as the viewport. The input device orientation will
- // be re-applied by mInputDeviceOrientation.
- const ui::Rotation naturalDeviceOrientation =
- mViewport.orientation - mParameters.orientation;
-
- std::tie(mDisplayBounds, mPhysicalFrameInDisplay) =
- getNaturalDisplayInfo(mViewport, naturalDeviceOrientation);
+ std::tie(mDisplayBounds, mPhysicalFrameInDisplay) = getNaturalDisplayInfo(mViewport);
// InputReader works in the un-rotated display coordinate space, so we don't need to do
// anything if the device is already orientation-aware. If the device is not
@@ -950,10 +972,13 @@
// Apply the input device orientation for the device.
mInputDeviceOrientation = mInputDeviceOrientation + mParameters.orientation;
+ mRawToDisplay = computeInputTransform();
} else {
mDisplayBounds = rawSize;
mPhysicalFrameInDisplay = Rect{mDisplayBounds};
mInputDeviceOrientation = ui::ROTATION_0;
+ mRawToDisplay.reset();
+ mRawToDisplay.set(-mRawPointerAxes.x.minValue, -mRawPointerAxes.y.minValue);
}
}
@@ -2344,11 +2369,11 @@
break;
}
- // Adjust X,Y coords for device calibration
+ // Adjust X,Y coords for device calibration and convert to the natural display coordinates.
+ vec2 transformed = {in.x, in.y};
// TODO: Adjust coverage coords?
- float xTransformed = in.x, yTransformed = in.y;
- mAffineTransform.applyTo(xTransformed, yTransformed);
- rotateAndScale(xTransformed, yTransformed);
+ mAffineTransform.applyTo(transformed.x /*byRef*/, transformed.y /*byRef*/);
+ transformed = mRawToDisplay.transform(transformed);
// Adjust X, Y, and coverage coords for input device orientation.
float left, top, right, bottom;
@@ -2398,8 +2423,8 @@
// Write output coords.
PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i];
out.clear();
- out.setAxisValue(AMOTION_EVENT_AXIS_X, xTransformed);
- out.setAxisValue(AMOTION_EVENT_AXIS_Y, yTransformed);
+ out.setAxisValue(AMOTION_EVENT_AXIS_X, transformed.x);
+ out.setAxisValue(AMOTION_EVENT_AXIS_Y, transformed.y);
out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size);
out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor);
@@ -2422,8 +2447,8 @@
if (mSource == AINPUT_SOURCE_TOUCHPAD &&
mLastCookedState.cookedPointerData.hasPointerCoordsForId(id)) {
const PointerCoords& p = mLastCookedState.cookedPointerData.pointerCoordsForId(id);
- float dx = xTransformed - p.getAxisValue(AMOTION_EVENT_AXIS_X);
- float dy = yTransformed - p.getAxisValue(AMOTION_EVENT_AXIS_Y);
+ float dx = transformed.x - p.getAxisValue(AMOTION_EVENT_AXIS_X);
+ float dy = transformed.y - p.getAxisValue(AMOTION_EVENT_AXIS_Y);
out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, dx);
out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, dy);
}
@@ -3796,41 +3821,6 @@
return out;
}
-// Transform input device coordinates to display panel coordinates.
-void TouchInputMapper::rotateAndScale(float& x, float& y) const {
- const float xScaled = float(x - mRawPointerAxes.x.minValue) * mXScale;
- const float yScaled = float(y - mRawPointerAxes.y.minValue) * mYScale;
-
- const float xScaledMax = float(mRawPointerAxes.x.maxValue - x) * mXScale;
- const float yScaledMax = float(mRawPointerAxes.y.maxValue - y) * mYScale;
-
- // Rotate to display coordinate.
- // 0 - no swap and reverse.
- // 90 - swap x/y and reverse y.
- // 180 - reverse x, y.
- // 270 - swap x/y and reverse x.
- switch (mInputDeviceOrientation) {
- case ui::ROTATION_0:
- x = xScaled;
- y = yScaled;
- break;
- case ui::ROTATION_90:
- y = xScaledMax;
- x = yScaled;
- break;
- case ui::ROTATION_180:
- x = xScaledMax;
- y = yScaledMax;
- break;
- case ui::ROTATION_270:
- y = xScaled;
- x = yScaledMax;
- break;
- default:
- assert(false);
- }
-}
-
bool TouchInputMapper::isPointInsidePhysicalFrame(int32_t x, int32_t y) const {
const float xScaled = (x - mRawPointerAxes.x.minValue) * mXScale;
const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale;