libgui: Add support for post-xform crops.

This change adds support for specifying a crop rectangle to a
SurfaceTextureClient that is in post-transformed coordinate space.

Change-Id: I247901de343e71b32850f7ae3bac62dfa612ad3d
Bug: 6299171
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index e7fac3d..1c80d0c 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -170,7 +170,6 @@
            mFrameNumber(0),
            mBuf(INVALID_BUFFER_SLOT) {
              mCrop.makeInvalid();
-             mActiveRect.makeInvalid();
          }
         // mGraphicBuffer points to the buffer allocated for this slot or is NULL
         // if no buffer has been allocated.
@@ -194,11 +193,6 @@
 
         // mBuf is the slot index of this buffer
         int mBuf;
-
-        // mActiveRect is the active rectangle for the buffer.  Pixels outside
-        // this rectangle are considered completely transparent for the purposes
-        // of window composition.
-        Rect mActiveRect;
     };
 
     // The following public functions is the consumer facing interface
@@ -302,7 +296,6 @@
           mAcquireCalled(false),
           mNeedsCleanupOnRelease(false) {
             mCrop.makeInvalid();
-            mActiveRect.makeInvalid();
         }
 
         // mGraphicBuffer points to the buffer allocated for this slot or is NULL
@@ -359,12 +352,6 @@
         // mCrop is the current crop rectangle for this buffer slot.
         Rect mCrop;
 
-        // mActiveRect is the current active rectangle for this buffer slot.
-        // Pixels outside of this rectangle are to be treated as completely
-        // transparent during window composition.  The rectangle is in buffer
-        // pixel coordinates.
-        Rect mActiveRect;
-
         // mTransform is the current transform flags for this buffer slot.
         uint32_t mTransform;
 
diff --git a/include/gui/ISurfaceTexture.h b/include/gui/ISurfaceTexture.h
index 7abc7db..1e33764 100644
--- a/include/gui/ISurfaceTexture.h
+++ b/include/gui/ISurfaceTexture.h
@@ -86,25 +86,21 @@
     // QueueBufferInput must be a POD structure
     struct QueueBufferInput {
         inline QueueBufferInput(int64_t timestamp,
-                const Rect& crop, int scalingMode, uint32_t transform,
-                const Rect& activeRect)
+                const Rect& crop, int scalingMode, uint32_t transform)
         : timestamp(timestamp), crop(crop), scalingMode(scalingMode),
-          transform(transform), activeRect(activeRect) { }
+          transform(transform) { }
         inline void deflate(int64_t* outTimestamp, Rect* outCrop,
-                int* outScalingMode, uint32_t* outTransform,
-                Rect* outActiveRect) const {
+                int* outScalingMode, uint32_t* outTransform) const {
             *outTimestamp = timestamp;
             *outCrop = crop;
             *outScalingMode = scalingMode;
             *outTransform = transform;
-            *outActiveRect = activeRect;
         }
     private:
         int64_t timestamp;
         Rect crop;
         int scalingMode;
         uint32_t transform;
-        Rect activeRect;
     };
 
     // QueueBufferOutput must be a POD structure
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 7540ed8..91cba76 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -154,9 +154,6 @@
     // getCurrentCrop returns the cropping rectangle of the current buffer.
     Rect getCurrentCrop() const;
 
-    // getCurrentActiveRect returns the active rectangle of the current buffer.
-    Rect getCurrentActiveRect() const;
-
     // getCurrentTransform returns the transform of the current buffer.
     uint32_t getCurrentTransform() const;
 
@@ -273,12 +270,6 @@
     // It gets set each time updateTexImage is called.
     Rect mCurrentCrop;
 
-    // mCurrentActiveRect is the active rectangle that applies to the current
-    // texture.  It gets set each time updateTexImage is called.  All pixels
-    // outside the active rectangle are be considered completely transparent for
-    // the purpose of window composition.
-    Rect mCurrentActiveRect;
-
     // mCurrentTransform is the transform identifier for the current texture. It
     // gets set each time updateTexImage is called.
     uint32_t mCurrentTransform;
diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h
index c4d4320..b11d572 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -80,10 +80,10 @@
     int dispatchSetBuffersTransform(va_list args);
     int dispatchSetBuffersTimestamp(va_list args);
     int dispatchSetCrop(va_list args);
+    int dispatchSetPostTransformCrop(va_list args);
     int dispatchSetUsage(va_list args);
     int dispatchLock(va_list args);
     int dispatchUnlockAndPost(va_list args);
-    int dispatchSetActiveRect(va_list args);
 
 protected:
     virtual int cancelBuffer(ANativeWindowBuffer* buffer);
@@ -104,10 +104,10 @@
     virtual int setBuffersTransform(int transform);
     virtual int setBuffersTimestamp(int64_t timestamp);
     virtual int setCrop(Rect const* rect);
+    virtual int setPostTransformCrop(Rect const* rect);
     virtual int setUsage(uint32_t reqUsage);
     virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
     virtual int unlockAndPost();
-    virtual int setActiveRect(Rect const* rect);
 
     enum { NUM_BUFFER_SLOTS = BufferQueue::NUM_BUFFER_SLOTS };
     enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
@@ -159,6 +159,13 @@
     // that gets queued. It is set by calling setCrop.
     Rect mCrop;
 
+    // mCropNeedsTransform indicates whether mCrop is in post-transform
+    // coordinates and must be transformed using the inverse of mTransform
+    // before being queued with a buffer.  Otherwise the crop is passed
+    // untransformed.  It is initialized to false, is set to true by
+    // setPostTransformCrop, and set to false by setCrop.
+    bool mCropNeedsTransform;
+
     // mScalingMode is the scaling mode that will be used for the next
     // buffers that get queued. It is set by calling setScalingMode.
     int mScalingMode;
@@ -167,10 +174,6 @@
     // buffer that gets queued. It is set by calling setTransform.
     uint32_t mTransform;
 
-    // mActiveRect is the active rectangle that will be used for the next buffer
-    // that gets queued.  It is set by calling setActiveRect.
-    Rect mActiveRect;
-
      // mDefaultWidth is default width of the buffers, regardless of the
      // native_window_set_buffers_dimensions call.
      uint32_t mDefaultWidth;
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 5095ebd..e53162b 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -540,15 +540,11 @@
     uint32_t transform;
     int scalingMode;
     int64_t timestamp;
-    Rect activeRect;
 
-    input.deflate(&timestamp, &crop, &scalingMode, &transform,
-            &activeRect);
+    input.deflate(&timestamp, &crop, &scalingMode, &transform);
 
-    ST_LOGV("queueBuffer: slot=%d time=%lld crop=[%d,%d,%d,%d] "
-            "active=[%d,%d,%d,%d]", buf, timestamp, crop.left, crop.top,
-            crop.right, crop.bottom, activeRect.left, activeRect.top,
-            activeRect.right, activeRect.bottom);
+    ST_LOGV("queueBuffer: slot=%d time=%lld crop=[%d,%d,%d,%d]",
+            buf, timestamp, crop.left, crop.top, crop.right, crop.bottom);
 
     sp<ConsumerListener> listener;
 
@@ -572,6 +568,16 @@
             return -EINVAL;
         }
 
+        const sp<GraphicBuffer>& graphicBuffer(mSlots[buf].mGraphicBuffer);
+        Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight());
+        Rect croppedCrop;
+        crop.intersect(bufferRect, &croppedCrop);
+        if (croppedCrop != crop) {
+            ST_LOGE("queueBuffer: crop rect is not contained within the "
+                    "buffer in slot %d", buf);
+            return -EINVAL;
+        }
+
         if (mSynchronousMode) {
             // In synchronous mode we queue all buffers in a FIFO.
             mQueue.push_back(buf);
@@ -600,12 +606,12 @@
         mSlots[buf].mTimestamp = timestamp;
         mSlots[buf].mCrop = crop;
         mSlots[buf].mTransform = transform;
-        mSlots[buf].mActiveRect = activeRect;
 
         switch (scalingMode) {
             case NATIVE_WINDOW_SCALING_MODE_FREEZE:
             case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
             case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
+            case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
                 break;
             default:
                 ST_LOGE("unknown scaling mode: %d (ignoring)", scalingMode);
@@ -859,7 +865,6 @@
         buffer->mFrameNumber = mSlots[buf].mFrameNumber;
         buffer->mTimestamp = mSlots[buf].mTimestamp;
         buffer->mBuf = buf;
-        buffer->mActiveRect = mSlots[buf].mActiveRect;
         mSlots[buf].mAcquireCalled = true;
 
         mSlots[buf].mBufferState = BufferSlot::ACQUIRED;
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index a6ca085..73f7c54 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -294,7 +294,6 @@
         mCurrentTransform = item.mTransform;
         mCurrentScalingMode = item.mScalingMode;
         mCurrentTimestamp = item.mTimestamp;
-        mCurrentActiveRect = item.mActiveRect;
         computeCurrentTransformMatrix();
     } else  {
         if (err < 0) {
@@ -534,8 +533,9 @@
     }
 
     sp<GraphicBuffer>& buf(mCurrentTextureBuf);
+    Rect cropRect = mCurrentCrop;
     float tx, ty, sx, sy;
-    if (!mCurrentCrop.isEmpty()) {
+    if (!cropRect.isEmpty()) {
         // In order to prevent bilinear sampling at the of the crop rectangle we
         // may need to shrink it by 2 texels in each direction.  Normally this
         // would just need to take 1/2 a texel off each end, but because the
@@ -552,14 +552,16 @@
         // correct edge behavior.
         const float shrinkAmount = 1.0f; // the amount that each edge is shrunk
 
-        tx = (float(mCurrentCrop.left) + shrinkAmount) /
-                float(buf->getWidth());
-        ty = (float(buf->getHeight() - mCurrentCrop.bottom) +
-                shrinkAmount) / float(buf->getHeight());
-        sx = (float(mCurrentCrop.width()) - (2.0f * shrinkAmount)) /
-                float(buf->getWidth());
-        sy = (float(mCurrentCrop.height()) - (2.0f * shrinkAmount)) /
-                float(buf->getHeight());
+        float bufferWidth = buf->getWidth();
+        float bufferHeight = buf->getHeight();
+
+        tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
+        ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) /
+                bufferHeight;
+        sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) /
+                bufferWidth;
+        sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) /
+                bufferHeight;
     } else {
         tx = 0.0f;
         ty = 0.0f;
@@ -653,11 +655,6 @@
     return outCrop;
 }
 
-Rect SurfaceTexture::getCurrentActiveRect() const {
-    Mutex::Autolock lock(mMutex);
-    return mCurrentActiveRect;
-}
-
 uint32_t SurfaceTexture::getCurrentTransform() const {
     Mutex::Autolock lock(mMutex);
     return mCurrentTransform;
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index fdd14c8..9c3e28d 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -76,9 +76,9 @@
     mReqUsage = 0;
     mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;
     mCrop.clear();
+    mCropNeedsTransform = false;
     mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
     mTransform = 0;
-    mActiveRect.clear();
     mDefaultWidth = 0;
     mDefaultHeight = 0;
     mUserWidth = 0;
@@ -238,9 +238,29 @@
         return i;
     }
 
+    Rect crop(mCrop);
+    if (mCropNeedsTransform) {
+        // The crop rect was specified in the post-transform coordinate space,
+        // so we need to transform that rect by the inverse of mTransform to
+        // put it into the buffer pixel space before queuing it.
+        uint32_t invTransform = mTransform;
+        int32_t width = buffer->width;
+        int32_t height = buffer->height;
+        if (mTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+            invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V |
+                    NATIVE_WINDOW_TRANSFORM_FLIP_H;
+            width = buffer->height;
+            height = buffer->width;
+        }
+        crop = mCrop.transform(invTransform, width, height);
+    }
+
+    // Make sure the crop rectangle is entirely inside the buffer.
+    crop.intersect(Rect(buffer->width, buffer->height), &crop);
+
     ISurfaceTexture::QueueBufferOutput output;
-    ISurfaceTexture::QueueBufferInput input(timestamp,
-            mCrop, mScalingMode, mTransform, mActiveRect);
+    ISurfaceTexture::QueueBufferInput input(timestamp, crop, mScalingMode,
+            mTransform);
     status_t err = mSurfaceTexture->queueBuffer(i, input, &output);
     if (err != OK)  {
         ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
@@ -321,6 +341,9 @@
     case NATIVE_WINDOW_SET_CROP:
         res = dispatchSetCrop(args);
         break;
+    case NATIVE_WINDOW_SET_POST_TRANSFORM_CROP:
+        res = dispatchSetPostTransformCrop(args);
+        break;
     case NATIVE_WINDOW_SET_BUFFER_COUNT:
         res = dispatchSetBufferCount(args);
         break;
@@ -357,9 +380,6 @@
     case NATIVE_WINDOW_API_DISCONNECT:
         res = dispatchDisconnect(args);
         break;
-    case NATIVE_WINDOW_SET_ACTIVE_RECT:
-        res = dispatchSetActiveRect(args);
-        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -387,6 +407,11 @@
     return setCrop(reinterpret_cast<Rect const*>(rect));
 }
 
+int SurfaceTextureClient::dispatchSetPostTransformCrop(va_list args) {
+    android_native_rect_t const* rect = va_arg(args, android_native_rect_t*);
+    return setPostTransformCrop(reinterpret_cast<Rect const*>(rect));
+}
+
 int SurfaceTextureClient::dispatchSetBufferCount(va_list args) {
     size_t bufferCount = va_arg(args, size_t);
     return setBufferCount(bufferCount);
@@ -430,11 +455,6 @@
     return setBuffersTransform(transform);
 }
 
-int SurfaceTextureClient::dispatchSetActiveRect(va_list args) {
-    android_native_rect_t const* rect = va_arg(args, android_native_rect_t*);
-    return setActiveRect(reinterpret_cast<Rect const*>(rect));
-}
-
 int SurfaceTextureClient::dispatchSetBuffersTimestamp(va_list args) {
     int64_t timestamp = va_arg(args, int64_t);
     return setBuffersTimestamp(timestamp);
@@ -481,6 +501,7 @@
         mReqHeight = 0;
         mReqUsage = 0;
         mCrop.clear();
+        mCropNeedsTransform = false;
         mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
         mTransform = 0;
         if (api == NATIVE_WINDOW_API_CPU) {
@@ -512,6 +533,25 @@
 
     Mutex::Autolock lock(mMutex);
     mCrop = realRect;
+    mCropNeedsTransform = false;
+    return NO_ERROR;
+}
+
+int SurfaceTextureClient::setPostTransformCrop(Rect const* rect)
+{
+    ATRACE_CALL();
+    ALOGV("SurfaceTextureClient::setPostTransformCrop");
+
+    Rect realRect;
+    if (rect == NULL || rect->isEmpty()) {
+        realRect.clear();
+    } else {
+        realRect = *rect;
+    }
+
+    Mutex::Autolock lock(mMutex);
+    mCrop = realRect;
+    mCropNeedsTransform = true;
     return NO_ERROR;
 }
 
@@ -546,7 +586,6 @@
     Mutex::Autolock lock(mMutex);
     mReqWidth = w;
     mReqHeight = h;
-    mCrop.clear();
     return NO_ERROR;
 }
 
@@ -564,7 +603,6 @@
     Mutex::Autolock lock(mMutex);
     mUserWidth = w;
     mUserHeight = h;
-    mCrop.clear();
     return NO_ERROR;
 }
 
@@ -589,6 +627,7 @@
         case NATIVE_WINDOW_SCALING_MODE_FREEZE:
         case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
         case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
+        case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
             break;
         default:
             ALOGE("unknown scaling mode: %d", mode);
@@ -609,23 +648,6 @@
     return NO_ERROR;
 }
 
-int SurfaceTextureClient::setActiveRect(Rect const* rect)
-{
-    ATRACE_CALL();
-    ALOGV("SurfaceTextureClient::setActiveRect");
-
-    Rect realRect;
-    if (rect == NULL || rect->isEmpty()) {
-        realRect.clear();
-    } else {
-        realRect = *rect;
-    }
-
-    Mutex::Autolock lock(mMutex);
-    mActiveRect = realRect;
-    return NO_ERROR;
-}
-
 int SurfaceTextureClient::setBuffersTimestamp(int64_t timestamp)
 {
     ALOGV("SurfaceTextureClient::setBuffersTimestamp");
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index b576ca5..8546fb9 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -20,6 +20,7 @@
 #include <EGL/egl.h>
 #include <gtest/gtest.h>
 #include <gui/SurfaceTextureClient.h>
+#include <system/graphics.h>
 #include <utils/Log.h>
 #include <utils/Thread.h>
 
@@ -441,6 +442,68 @@
     ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[2]));
 }
 
+TEST_F(SurfaceTextureClientTest, SetPostTransformCropUntransforms) {
+    android_native_rect_t rect = {1, 5, 4, 14};
+    native_window_set_post_transform_crop(mANW.get(), &rect);
+
+    uint32_t xforms[] = {
+        HAL_TRANSFORM_FLIP_H,
+        HAL_TRANSFORM_FLIP_V,
+        HAL_TRANSFORM_ROT_90,
+        HAL_TRANSFORM_ROT_180,
+        HAL_TRANSFORM_ROT_270,
+    };
+
+    Rect expectedRects[] = {
+        Rect(4, 5, 7, 14), // HAL_TRANSFORM_FLIP_H
+        Rect(1, 2, 4, 11), // HAL_TRANSFORM_FLIP_V
+        Rect(5, 4, 14, 7), // HAL_TRANSFORM_ROT_90
+        Rect(4, 2, 7, 11), // HAL_TRANSFORM_ROT_180
+        Rect(2, 1, 11, 4), // HAL_TRANSFORM_ROT_270
+    };
+
+    for (size_t i = 0; i < sizeof(xforms)/sizeof(xforms[0]); i++) {
+        SCOPED_TRACE(String8::format("xform=%#x", xforms[i]).string());
+
+        int w = 8, h = 16;
+        if (xforms[i] & HAL_TRANSFORM_ROT_90) {
+            w = 16;
+            h = 8;
+        }
+        ASSERT_EQ(OK, native_window_set_buffers_transform(mANW.get(), xforms[i]));
+        ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), w, h));
+
+        android_native_buffer_t* buf;
+        ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf));
+        ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf));
+        ASSERT_EQ(OK, mST->updateTexImage());
+
+        Rect crop = mST->getCurrentCrop();
+        EXPECT_EQ(expectedRects[i].left, crop.left);
+        EXPECT_EQ(expectedRects[i].top, crop.top);
+        EXPECT_EQ(expectedRects[i].right, crop.right);
+        EXPECT_EQ(expectedRects[i].bottom, crop.bottom);
+    }
+}
+
+TEST_F(SurfaceTextureClientTest, SetCropCropsCrop) {
+    android_native_rect_t rect = {-2, -13, 40, 18};
+    native_window_set_crop(mANW.get(), &rect);
+
+    ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 4, 4));
+
+    android_native_buffer_t* buf;
+    ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf));
+    ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf));
+    ASSERT_EQ(OK, mST->updateTexImage());
+
+    Rect crop = mST->getCurrentCrop();
+    EXPECT_EQ(0, crop.left);
+    EXPECT_EQ(0, crop.top);
+    EXPECT_EQ(4, crop.right);
+    EXPECT_EQ(4, crop.bottom);
+}
+
 // XXX: This is not expected to pass until the synchronization hacks are removed
 // from the SurfaceTexture class.
 TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) {
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index cff4476..d708f6d 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -1262,28 +1262,6 @@
     native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU);
 }
 
-TEST_F(SurfaceTextureGLTest, GetCurrentActiveRectWorks) {
-    ASSERT_EQ(OK, mST->setSynchronousMode(true));
-
-    ASSERT_EQ(OK, native_window_api_connect(mANW.get(),
-            NATIVE_WINDOW_API_CPU));
-
-    ANativeWindowBuffer *anb;
-
-    android_native_rect_t odd = {23, 78, 123, 477};
-    ASSERT_EQ(OK, native_window_set_active_rect(mANW.get(), &odd));
-    EXPECT_EQ (OK, mANW->dequeueBuffer(mANW.get(), &anb));
-    EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb));
-    mFW->waitForFrame();
-    EXPECT_EQ(OK,mST->updateTexImage());
-    Rect r = mST->getCurrentCrop();
-    assertRectEq(Rect(23, 78, 123, 477), r);
-
-    ASSERT_EQ(OK, native_window_api_disconnect(mANW.get(),
-            NATIVE_WINDOW_API_CPU));
-}
-
-
 TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) {
     class ProducerThread : public Thread {
     public: