Enabled cropping support in SurfaceTexture

SurfaceTexture will modify the crop rect so it matches
the desired output aspect ratio when the scaling
mode is NATIVE_WINDOW_SCALING_MODE_CROP.  Added a test
for this new scaling mode.

Change-Id: I60f24dcbc294b65cd10a393d9e27d40f07d27bb6
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index ddbfe05..3c1fcc5 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -531,7 +531,8 @@
     ATRACE_CALL();
     ATRACE_BUFFER_INDEX(buf);
 
-    ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp);
+    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;
 
@@ -592,6 +593,7 @@
         switch (scalingMode) {
             case NATIVE_WINDOW_SCALING_MODE_FREEZE:
             case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
+            case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
                 break;
             default:
                 ST_LOGE("unknown scaling mode: %d (ignoring)", scalingMode);
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 4fe2cca..0fa9ca1 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -168,6 +168,8 @@
 status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
 {
     Mutex::Autolock lock(mMutex);
+    mDefaultWidth = w;
+    mDefaultHeight = h;
     return mBufferQueue->setDefaultBufferSize(w, h);
 }
 
@@ -621,7 +623,40 @@
 
 Rect SurfaceTexture::getCurrentCrop() const {
     Mutex::Autolock lock(mMutex);
-    return mCurrentCrop;
+
+    Rect outCrop = mCurrentCrop;
+    if (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP) {
+        int32_t newWidth = mCurrentCrop.width();
+        int32_t newHeight = mCurrentCrop.height();
+
+        if (newWidth * mDefaultHeight > newHeight * mDefaultWidth) {
+            newWidth = newHeight * mDefaultWidth / mDefaultHeight;
+            ST_LOGV("too wide: newWidth = %d", newWidth);
+        } else if (newWidth * mDefaultHeight < newHeight * mDefaultWidth) {
+            newHeight = newWidth * mDefaultHeight / mDefaultWidth;
+            ST_LOGV("too tall: newHeight = %d", newHeight);
+        }
+
+        // The crop is too wide
+        if (newWidth < mCurrentCrop.width()) {
+            int32_t dw = (newWidth - mCurrentCrop.width())/2;
+            outCrop.left -=dw;
+            outCrop.right += dw;
+        // The crop is too tall
+        } else if (newHeight < mCurrentCrop.height()) {
+            int32_t dh = (newHeight - mCurrentCrop.height())/2;
+            outCrop.top -= dh;
+            outCrop.bottom += dh;
+        }
+
+        ST_LOGV("getCurrentCrop final crop [%d,%d,%d,%d]",
+            outCrop.left, outCrop.top,
+            outCrop.right,outCrop.bottom);
+    }
+
+
+
+    return outCrop;
 }
 
 uint32_t SurfaceTexture::getCurrentTransform() const {
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index aa114ed..99c025f 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -555,6 +555,7 @@
     switch (mode) {
         case NATIVE_WINDOW_SCALING_MODE_FREEZE:
         case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
+        case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
             break;
         default:
             ALOGE("unknown scaling mode: %d", mode);
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index ec4f8a4..cfe89bc 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -236,6 +236,44 @@
         }
     }
 
+    ::testing::AssertionResult assertRectEq(const Rect &r1,
+        const Rect &r2, int tolerance=1) {
+
+        String8 msg;
+
+        if (abs(r1.left - r2.left) > tolerance) {
+            msg += String8::format("left(%d isn't %d)", r1.left, r2.left);
+        }
+        if (abs(r1.top - r2.top) > tolerance) {
+            if (!msg.isEmpty()) {
+                msg += " ";
+            }
+            msg += String8::format("top(%d isn't %d)", r1.top, r2.top);
+        }
+        if (abs(r1.right - r2.right) > tolerance) {
+            if (!msg.isEmpty()) {
+                msg += " ";
+            }
+            msg += String8::format("right(%d isn't %d)", r1.right, r2.right);
+        }
+        if (abs(r1.bottom - r2.bottom) > tolerance) {
+            if (!msg.isEmpty()) {
+                msg += " ";
+            }
+            msg += String8::format("bottom(%d isn't %d)", r1.bottom, r2.bottom);
+        }
+        if (!msg.isEmpty()) {
+            msg += String8::format(" R1: [%d %d %d %d] R2: [%d %d %d %d]",
+                r1.left, r1.top, r1.right, r1.bottom,
+                r2.left, r2.top, r2.right, r2.bottom);
+            fprintf(stderr, "assertRectEq: %s\n", msg.string());
+            return ::testing::AssertionFailure(
+                    ::testing::Message(msg.string()));
+        } else {
+            return ::testing::AssertionSuccess();
+        }
+    }
+
     int mDisplaySecs;
     sp<SurfaceComposerClient> mComposerClient;
     sp<SurfaceControl> mSurfaceControl;
@@ -1129,6 +1167,88 @@
     EXPECT_EQ(OK,mST->updateTexImage());
 }
 
+TEST_F(SurfaceTextureGLTest, ScaleToWindowMode) {
+    ASSERT_EQ(OK, mST->setSynchronousMode(true));
+
+    ASSERT_EQ(OK, native_window_set_scaling_mode(mANW.get(),
+        NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW));
+
+    // The producer image size
+    ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 512, 512));
+
+    // The consumer image size (16 x 9) ratio
+    mST->setDefaultBufferSize(1280, 720);
+
+    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_crop(mANW.get(), &odd));
+    EXPECT_EQ (OK, mANW->dequeueBuffer(mANW.get(), &anb));
+    EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb));
+    EXPECT_EQ(OK,mST->updateTexImage());
+    Rect r = mST->getCurrentCrop();
+    assertRectEq(Rect(23, 78, 123, 477), r);
+
+    native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_EGL);
+}
+
+// This test ensures the scaling mode does the right thing
+// ie NATIVE_WINDOW_SCALING_MODE_CROP should crop
+// the image such that it has the same aspect ratio as the
+// default buffer size
+TEST_F(SurfaceTextureGLTest, CroppedScalingMode) {
+    ASSERT_EQ(OK, mST->setSynchronousMode(true));
+
+    ASSERT_EQ(OK, native_window_set_scaling_mode(mANW.get(),
+        NATIVE_WINDOW_SCALING_MODE_SCALE_CROP));
+
+    // The producer image size
+    ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 512, 512));
+
+    // The consumer image size (16 x 9) ratio
+    mST->setDefaultBufferSize(1280, 720);
+
+    native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU);
+
+    ANativeWindowBuffer *anb;
+
+    // The crop is in the shape of (320, 180) === 16 x 9
+    android_native_rect_t standard = {10, 20, 330, 200};
+    ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &standard));
+    EXPECT_EQ (OK, mANW->dequeueBuffer(mANW.get(), &anb));
+    EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb));
+    EXPECT_EQ(OK,mST->updateTexImage());
+    Rect r = mST->getCurrentCrop();
+    // crop should be the same as crop (same aspect ratio)
+    assertRectEq(Rect(10, 20, 330, 200), r);
+
+    // make this wider then desired aspect 239 x 100 (2.39:1)
+    android_native_rect_t wide = {20, 30, 259, 130};
+    ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &wide));
+    EXPECT_EQ (OK, mANW->dequeueBuffer(mANW.get(), &anb));
+    EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb));
+    EXPECT_EQ(OK,mST->updateTexImage());
+    r = mST->getCurrentCrop();
+    // crop should be the same height, but have cropped left and right borders
+    // offset is 30.6 px L+, R-
+    assertRectEq(Rect(51, 30, 228, 130), r);
+
+    // This image is taller then desired aspect 400 x 300 (4:3)
+    android_native_rect_t narrow = {0, 0, 400, 300};
+    ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &narrow));
+    EXPECT_EQ (OK, mANW->dequeueBuffer(mANW.get(), &anb));
+    EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb));
+    EXPECT_EQ(OK,mST->updateTexImage());
+    r = mST->getCurrentCrop();
+    // crop should be the same width, but have cropped top and bottom borders
+    // offset is 37.5 px
+    assertRectEq(Rect(0, 37, 400, 262), r);
+
+    native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU);
+}
+
 TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) {
     class ProducerThread : public Thread {
     public: