Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3d76d5d7 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Rotate TouchVideoFrames"

parents 54300ac8 8154bbdf
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@

#include <stdint.h>
#include <sys/time.h>
#include <ui/DisplayInfo.h>
#include <vector>

namespace android {
@@ -55,11 +56,23 @@ public:
     */
    const struct timeval& getTimestamp() const;

    /**
     * Rotate the video frame.
     * The rotation value is an enum from ui/DisplayInfo.h
     */
    void rotate(int32_t orientation);

private:
    uint32_t mHeight;
    uint32_t mWidth;
    std::vector<int16_t> mData;
    struct timeval mTimestamp;

    /**
     * Common method for 90 degree and 270 degree rotation
     */
    void rotateQuarterTurn(bool clockwise);
    void rotate180();
};

} // namespace android
+58 −0
Original line number Diff line number Diff line
@@ -39,4 +39,62 @@ const std::vector<int16_t>& TouchVideoFrame::getData() const { return mData; }

const struct timeval& TouchVideoFrame::getTimestamp() const { return mTimestamp; }

void TouchVideoFrame::rotate(int32_t orientation) {
    switch (orientation) {
        case DISPLAY_ORIENTATION_90:
            rotateQuarterTurn(true /*clockwise*/);
            break;
        case DISPLAY_ORIENTATION_180:
            rotate180();
            break;
        case DISPLAY_ORIENTATION_270:
            rotateQuarterTurn(false /*clockwise*/);
            break;
    }
}

/**
 * Rotate once clockwise by a quarter turn === rotate 90 degrees
 * Rotate once counterclockwise by a quarter turn === rotate 270 degrees
 * For a clockwise rotation:
 *     An element at position (i, j) is rotated to (j, height - i - 1)
 * For a counterclockwise rotation:
 *     An element at position (i, j) is rotated to (width - j - 1, i)
 */
void TouchVideoFrame::rotateQuarterTurn(bool clockwise) {
    std::vector<int16_t> rotated(mData.size());
    for (size_t i = 0; i < mHeight; i++) {
        for (size_t j = 0; j < mWidth; j++) {
            size_t iRotated, jRotated;
            if (clockwise) {
                iRotated = j;
                jRotated = mHeight - i - 1;
            } else {
                iRotated = mWidth - j - 1;
                jRotated = i;
            }
            size_t indexRotated = iRotated * mHeight + jRotated;
            rotated[indexRotated] = mData[i * mWidth + j];
        }
    }
    mData = std::move(rotated);
    std::swap(mHeight, mWidth);
}

/**
 * An element at position (i, j) is rotated to (height - i - 1, width - j - 1)
 * This is equivalent to moving element [i] to position [height * width - i - 1]
 * Since element at [height * width - i - 1] would move to position [i],
 * we can just swap elements [i] and [height * width - i - 1].
 */
void TouchVideoFrame::rotate180() {
    if (mData.size() == 0) {
        return;
    }
    // Just need to swap elements i and (height * width - 1 - i)
    for (size_t i = 0; i < mData.size() / 2; i++) {
        std::swap(mData[i], mData[mHeight * mWidth - 1 - i]);
    }
}

} // namespace android
+125 −0
Original line number Diff line number Diff line
@@ -67,5 +67,130 @@ TEST(TouchVideoFrame, Equality) {
    ASSERT_FALSE(frame == changedTimestampFrame);
}

// --- Rotate 90 degrees ---

TEST(TouchVideoFrame, Rotate90_0x0) {
    TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
    TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
    frame.rotate(DISPLAY_ORIENTATION_90);
    ASSERT_EQ(frame, frameRotated);
}

TEST(TouchVideoFrame, Rotate90_1x1) {
    TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
    TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
    frame.rotate(DISPLAY_ORIENTATION_90);
    ASSERT_EQ(frame, frameRotated);
}

TEST(TouchVideoFrame, Rotate90_2x2) {
    TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
    TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP);
    frame.rotate(DISPLAY_ORIENTATION_90);
    ASSERT_EQ(frame, frameRotated);
}

TEST(TouchVideoFrame, Rotate90_3x2) {
    TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
    TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP);
    frame.rotate(DISPLAY_ORIENTATION_90);
    ASSERT_EQ(frame, frameRotated);
}

TEST(TouchVideoFrame, Rotate90_3x2_4times) {
    TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
    TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
    frame.rotate(DISPLAY_ORIENTATION_90);
    frame.rotate(DISPLAY_ORIENTATION_90);
    frame.rotate(DISPLAY_ORIENTATION_90);
    frame.rotate(DISPLAY_ORIENTATION_90);
    ASSERT_EQ(frame, frameOriginal);
}

// --- Rotate 180 degrees ---

TEST(TouchVideoFrame, Rotate180_0x0) {
    TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
    TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
    frame.rotate(DISPLAY_ORIENTATION_180);
    ASSERT_EQ(frame, frameRotated);
}

TEST(TouchVideoFrame, Rotate180_1x1) {
    TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
    TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
    frame.rotate(DISPLAY_ORIENTATION_180);
    ASSERT_EQ(frame, frameRotated);
}

TEST(TouchVideoFrame, Rotate180_2x2) {
    TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
    TouchVideoFrame frameRotated(2, 2, {4, 3, 2, 1}, TIMESTAMP);
    frame.rotate(DISPLAY_ORIENTATION_180);
    ASSERT_EQ(frame, frameRotated);
}

TEST(TouchVideoFrame, Rotate180_3x2) {
    TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
    TouchVideoFrame frameRotated(3, 2, {6, 5, 4, 3, 2, 1}, TIMESTAMP);
    frame.rotate(DISPLAY_ORIENTATION_180);
    ASSERT_EQ(frame, frameRotated);
}

TEST(TouchVideoFrame, Rotate180_3x2_2times) {
    TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
    TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
    frame.rotate(DISPLAY_ORIENTATION_180);
    frame.rotate(DISPLAY_ORIENTATION_180);
    ASSERT_EQ(frame, frameOriginal);
}

TEST(TouchVideoFrame, Rotate180_3x3) {
    TouchVideoFrame frame(3, 3, {1, 2, 3, 4, 5, 6, 7, 8, 9}, TIMESTAMP);
    TouchVideoFrame frameRotated(3, 3, {9, 8, 7, 6, 5, 4, 3, 2, 1}, TIMESTAMP);
    frame.rotate(DISPLAY_ORIENTATION_180);
    ASSERT_EQ(frame, frameRotated);
}

// --- Rotate 270 degrees ---

TEST(TouchVideoFrame, Rotate270_0x0) {
    TouchVideoFrame frame(0, 0, {}, TIMESTAMP);
    TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP);
    frame.rotate(DISPLAY_ORIENTATION_270);
    ASSERT_EQ(frame, frameRotated);
}

TEST(TouchVideoFrame, Rotate270_1x1) {
    TouchVideoFrame frame(1, 1, {1}, TIMESTAMP);
    TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP);
    frame.rotate(DISPLAY_ORIENTATION_270);
    ASSERT_EQ(frame, frameRotated);
}

TEST(TouchVideoFrame, Rotate270_2x2) {
    TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
    TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP);
    frame.rotate(DISPLAY_ORIENTATION_270);
    ASSERT_EQ(frame, frameRotated);
}

TEST(TouchVideoFrame, Rotate270_3x2) {
    TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
    TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP);
    frame.rotate(DISPLAY_ORIENTATION_270);
    ASSERT_EQ(frame, frameRotated);
}

TEST(TouchVideoFrame, Rotate270_3x2_4times) {
    TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
    TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
    frame.rotate(DISPLAY_ORIENTATION_270);
    frame.rotate(DISPLAY_ORIENTATION_270);
    frame.rotate(DISPLAY_ORIENTATION_270);
    frame.rotate(DISPLAY_ORIENTATION_270);
    ASSERT_EQ(frame, frameOriginal);
}

} // namespace test
} // namespace android
+2 −0
Original line number Diff line number Diff line
@@ -6565,6 +6565,8 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32
    const int32_t displayId = getAssociatedDisplay().value_or(ADISPLAY_ID_NONE);
    const int32_t deviceId = getDeviceId();
    std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId);
    std::for_each(frames.begin(), frames.end(),
            [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
    NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId,
            source, displayId, policyFlags,
            action, actionButton, flags, metaState, buttonState, MotionClassification::NONE,
+48 −0
Original line number Diff line number Diff line
@@ -6453,4 +6453,52 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) {
    ASSERT_EQ(std::vector<TouchVideoFrame>(), motionArgs.videoFrames);
}

TEST_F(MultiTouchInputMapperTest, VideoFrames_AreRotated) {
    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
    prepareAxes(POSITION);
    addConfigurationProperty("touch.deviceType", "touchScreen");
    addMapperAndConfigure(mapper);
    // Unrotated video frame
    TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
    NotifyMotionArgs motionArgs;

    // Test all 4 orientations
    for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90,
             DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) {
        SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
        clearViewports();
        prepareDisplay(orientation);
        std::vector<TouchVideoFrame> frames{frame};
        mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}});
        processPosition(mapper, 100, 200);
        processSync(mapper);
        ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
        frames[0].rotate(orientation);
        ASSERT_EQ(frames, motionArgs.videoFrames);
    }
}

TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) {
    MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice);
    prepareAxes(POSITION);
    addConfigurationProperty("touch.deviceType", "touchScreen");
    addMapperAndConfigure(mapper);
    // Unrotated video frames. There's no rule that they must all have the same dimensions,
    // so mix these.
    TouchVideoFrame frame1(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2});
    TouchVideoFrame frame2(3, 3, {0, 1, 2, 3, 4, 5, 6, 7, 8}, {1, 3});
    TouchVideoFrame frame3(2, 2, {10, 20, 10, 0}, {1, 4});
    std::vector<TouchVideoFrame> frames{frame1, frame2, frame3};
    NotifyMotionArgs motionArgs;

    prepareDisplay(DISPLAY_ORIENTATION_90);
    mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}});
    processPosition(mapper, 100, 200);
    processSync(mapper);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
    std::for_each(frames.begin(), frames.end(),
            [](TouchVideoFrame& frame) { frame.rotate(DISPLAY_ORIENTATION_90); });
    ASSERT_EQ(frames, motionArgs.videoFrames);
}

} // namespace android