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/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(×tamp, &crop, &scalingMode, &transform,
- &activeRect);
+ input.deflate(×tamp, &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: