From d72f233ffa125856a44976a50a66ceb669f49ab2 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Mon, 7 May 2012 13:50:11 -0700 Subject: [PATCH] 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 --- include/gui/BufferQueue.h | 13 ---- include/gui/ISurfaceTexture.h | 10 +-- include/gui/SurfaceTexture.h | 9 --- include/gui/SurfaceTextureClient.h | 15 ++-- libs/gui/BufferQueue.cpp | 23 +++--- libs/gui/SurfaceTexture.cpp | 27 +++---- libs/gui/SurfaceTextureClient.cpp | 82 +++++++++++++------- libs/gui/tests/SurfaceTextureClient_test.cpp | 63 +++++++++++++++ libs/gui/tests/SurfaceTexture_test.cpp | 22 ------ 9 files changed, 153 insertions(+), 111 deletions(-) diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h index e7fac3de6..1c80d0cb7 100644 --- a/include/gui/BufferQueue.h +++ b/include/gui/BufferQueue.h @@ -170,7 +170,6 @@ public: 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 @@ public: // 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 @@ private: mAcquireCalled(false), mNeedsCleanupOnRelease(false) { mCrop.makeInvalid(); - mActiveRect.makeInvalid(); } // mGraphicBuffer points to the buffer allocated for this slot or is NULL @@ -359,12 +352,6 @@ private: // 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 7abc7db98..1e337642b 100644 --- a/include/gui/ISurfaceTexture.h +++ b/include/gui/ISurfaceTexture.h @@ -86,25 +86,21 @@ protected: // 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 7540ed8c6..91cba76f8 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -154,9 +154,6 @@ public: // 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 @@ private: // 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 c4d4320e0..b11d57271 100644 --- a/include/gui/SurfaceTextureClient.h +++ b/include/gui/SurfaceTextureClient.h @@ -80,10 +80,10 @@ private: 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 @@ protected: 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 @@ private: // 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 @@ private: // 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 5095ebd12..e53162ba4 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -540,15 +540,11 @@ status_t BufferQueue::queueBuffer(int buf, 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 listener; @@ -572,6 +568,16 @@ status_t BufferQueue::queueBuffer(int buf, return -EINVAL; } + const sp& 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 @@ status_t BufferQueue::queueBuffer(int buf, 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 @@ status_t BufferQueue::acquireBuffer(BufferItem *buffer) { 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 a6ca085e5..73f7c5443 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -294,7 +294,6 @@ status_t SurfaceTexture::updateTexImage() { mCurrentTransform = item.mTransform; mCurrentScalingMode = item.mScalingMode; mCurrentTimestamp = item.mTimestamp; - mCurrentActiveRect = item.mActiveRect; computeCurrentTransformMatrix(); } else { if (err < 0) { @@ -534,8 +533,9 @@ void SurfaceTexture::computeCurrentTransformMatrix() { } sp& 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 @@ void SurfaceTexture::computeCurrentTransformMatrix() { // 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 @@ Rect SurfaceTexture::getCurrentCrop() const { 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 fdd14c801..9c3e28db3 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -76,9 +76,9 @@ void SurfaceTextureClient::init() { 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 @@ int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) { 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 @@ int SurfaceTextureClient::perform(int operation, va_list args) 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 @@ int SurfaceTextureClient::perform(int operation, va_list args) 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 @@ int SurfaceTextureClient::dispatchSetCrop(va_list args) { return setCrop(reinterpret_cast(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)); +} + int SurfaceTextureClient::dispatchSetBufferCount(va_list args) { size_t bufferCount = va_arg(args, size_t); return setBufferCount(bufferCount); @@ -430,11 +455,6 @@ int SurfaceTextureClient::dispatchSetBuffersTransform(va_list args) { 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)); -} - int SurfaceTextureClient::dispatchSetBuffersTimestamp(va_list args) { int64_t timestamp = va_arg(args, int64_t); return setBuffersTimestamp(timestamp); @@ -481,6 +501,7 @@ int SurfaceTextureClient::disconnect(int api) { 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 @@ int SurfaceTextureClient::setCrop(Rect const* rect) 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 @@ int SurfaceTextureClient::setBuffersDimensions(int w, int h) Mutex::Autolock lock(mMutex); mReqWidth = w; mReqHeight = h; - mCrop.clear(); return NO_ERROR; } @@ -564,7 +603,6 @@ int SurfaceTextureClient::setBuffersUserDimensions(int w, int h) Mutex::Autolock lock(mMutex); mUserWidth = w; mUserHeight = h; - mCrop.clear(); return NO_ERROR; } @@ -589,6 +627,7 @@ int SurfaceTextureClient::setScalingMode(int mode) 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 @@ int SurfaceTextureClient::setBuffersTransform(int transform) 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 b576ca573..8546fb94b 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -441,6 +442,68 @@ TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeMinUndequeued) { 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 cff4476b5..d708f6dbb 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -1262,28 +1262,6 @@ TEST_F(SurfaceTextureGLTest, CroppedScalingMode) { 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: