Add final crop implementation
Bug 26559810
Change-Id: Idaccd13cd625c92d18665ddecebdbb266ea365f3
diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h
index aa48718..794fe4c 100644
--- a/include/gui/SurfaceComposerClient.h
+++ b/include/gui/SurfaceComposerClient.h
@@ -133,6 +133,7 @@
status_t setPosition(const sp<IBinder>& id, float x, float y);
status_t setSize(const sp<IBinder>& id, uint32_t w, uint32_t h);
status_t setCrop(const sp<IBinder>& id, const Rect& crop);
+ status_t setFinalCrop(const sp<IBinder>& id, const Rect& crop);
status_t setLayerStack(const sp<IBinder>& id, uint32_t layerStack);
status_t deferTransactionUntil(const sp<IBinder>& id,
const sp<IBinder>& handle, uint64_t frameNumber);
diff --git a/include/gui/SurfaceControl.h b/include/gui/SurfaceControl.h
index 76ce68d..35644db 100644
--- a/include/gui/SurfaceControl.h
+++ b/include/gui/SurfaceControl.h
@@ -71,6 +71,7 @@
status_t setAlpha(float alpha=1.0f);
status_t setMatrix(float dsdx, float dtdx, float dsdy, float dtdy);
status_t setCrop(const Rect& crop);
+ status_t setFinalCrop(const Rect& crop);
// Defers applying any changes made in this transaction until the Layer
// identified by handle reaches the given frameNumber
diff --git a/include/private/gui/LayerState.h b/include/private/gui/LayerState.h
index 5cf316f..078720a 100644
--- a/include/private/gui/LayerState.h
+++ b/include/private/gui/LayerState.h
@@ -52,14 +52,16 @@
eFlagsChanged = 0x00000040,
eLayerStackChanged = 0x00000080,
eCropChanged = 0x00000100,
- eDeferTransaction = 0x00000200
+ eDeferTransaction = 0x00000200,
+ eFinalCropChanged = 0x00000400
};
layer_state_t()
: what(0),
x(0), y(0), z(0), w(0), h(0), layerStack(0),
alpha(0), flags(0), mask(0),
- reserved(0), crop(Rect::INVALID_RECT), frameNumber(0)
+ reserved(0), crop(Rect::INVALID_RECT),
+ finalCrop(Rect::INVALID_RECT), frameNumber(0)
{
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
@@ -88,6 +90,7 @@
uint8_t reserved;
matrix22_t matrix;
Rect crop;
+ Rect finalCrop;
sp<IBinder> handle;
uint64_t frameNumber;
// non POD must be last. see write/read
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 06f13e8..e43342e 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -38,6 +38,7 @@
*reinterpret_cast<layer_state_t::matrix22_t *>(
output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix;
output.write(crop);
+ output.write(finalCrop);
output.writeStrongBinder(handle);
output.writeUint64(frameNumber);
output.write(transparentRegion);
@@ -64,6 +65,7 @@
return BAD_VALUE;
}
input.read(crop);
+ input.read(finalCrop);
handle = input.readStrongBinder();
frameNumber = input.readUint64();
input.read(transparentRegion);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 3242f55..04b5446 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -156,6 +156,8 @@
status_t setOrientation(int orientation);
status_t setCrop(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
const Rect& crop);
+ status_t setFinalCrop(const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id, const Rect& crop);
status_t setLayerStack(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id, uint32_t layerStack);
status_t deferTransactionUntil(const sp<SurfaceComposerClient>& client,
@@ -386,6 +388,18 @@
return NO_ERROR;
}
+status_t Composer::setFinalCrop(const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id, const Rect& crop) {
+ Mutex::Autolock _l(mLock);
+ layer_state_t* s = getLayerStateLocked(client, id);
+ if (!s) {
+ return BAD_INDEX;
+ }
+ s->what |= layer_state_t::eFinalCropChanged;
+ s->finalCrop = crop;
+ return NO_ERROR;
+}
+
status_t Composer::deferTransactionUntil(
const sp<SurfaceComposerClient>& client, const sp<IBinder>& id,
const sp<IBinder>& handle, uint64_t frameNumber) {
@@ -579,6 +593,11 @@
return getComposer().setCrop(this, id, crop);
}
+status_t SurfaceComposerClient::setFinalCrop(const sp<IBinder>& id,
+ const Rect& crop) {
+ return getComposer().setFinalCrop(this, id, crop);
+}
+
status_t SurfaceComposerClient::setPosition(const sp<IBinder>& id, float x, float y) {
return getComposer().setPosition(this, id, x, y);
}
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index e1a951c..184de71 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -152,6 +152,11 @@
if (err < 0) return err;
return mClient->setCrop(mHandle, crop);
}
+status_t SurfaceControl::setFinalCrop(const Rect& crop) {
+ status_t err = validate();
+ if (err < 0) return err;
+ return mClient->setFinalCrop(mHandle, crop);
+}
status_t SurfaceControl::deferTransactionUntil(sp<IBinder> handle,
uint64_t frameNumber) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 42d0810..910aeae 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -118,6 +118,7 @@
mCurrentState.active.h = h;
mCurrentState.active.transform.set(0, 0);
mCurrentState.active.crop.makeInvalid();
+ mCurrentState.active.finalCrop.makeInvalid();
mCurrentState.z = 0;
#ifdef USE_HWC2
mCurrentState.alpha = 1.0f;
@@ -401,7 +402,14 @@
}
activeCrop = s.active.transform.transform(activeCrop);
- activeCrop.intersect(hw->getViewport(), &activeCrop);
+ if (!activeCrop.intersect(hw->getViewport(), &activeCrop)) {
+ activeCrop.clear();
+ }
+ if (!s.active.finalCrop.isEmpty()) {
+ if(!activeCrop.intersect(s.active.finalCrop, &activeCrop)) {
+ activeCrop.clear();
+ }
+ }
activeCrop = s.active.transform.inverse().transform(activeCrop);
// This needs to be here as transform.transform(Rect) computes the
@@ -410,72 +418,73 @@
// transform.inverse().transform(transform.transform(Rect)) != Rect
// in which case we need to make sure the final rect is clipped to the
// display bounds.
- activeCrop.intersect(Rect(s.active.w, s.active.h), &activeCrop);
+ if (!activeCrop.intersect(Rect(s.active.w, s.active.h), &activeCrop)) {
+ activeCrop.clear();
+ }
// subtract the transparent region and snap to the bounds
activeCrop = reduce(activeCrop, s.activeTransparentRegion);
- if (!activeCrop.isEmpty()) {
- // Transform the window crop to match the buffer coordinate system,
- // which means using the inverse of the current transform set on the
- // SurfaceFlingerConsumer.
- uint32_t invTransform = mCurrentTransform;
- if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) {
- /*
- * the code below applies the display's inverse transform to the buffer
- */
- uint32_t invTransformOrient = hw->getOrientationTransform();
- // calculate the inverse transform
- if (invTransformOrient & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- invTransformOrient ^= NATIVE_WINDOW_TRANSFORM_FLIP_V |
- NATIVE_WINDOW_TRANSFORM_FLIP_H;
- // If the transform has been rotated the axis of flip has been swapped
- // so we need to swap which flip operations we are performing
- bool is_h_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) != 0;
- bool is_v_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) != 0;
- if (is_h_flipped != is_v_flipped) {
- invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V |
- NATIVE_WINDOW_TRANSFORM_FLIP_H;
- }
- }
- // and apply to the current transform
- invTransform = (Transform(invTransform) * Transform(invTransformOrient)).getOrientation();
- }
-
- int winWidth = s.active.w;
- int winHeight = s.active.h;
- if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
- // If the activeCrop has been rotate the ends are rotated but not
- // the space itself so when transforming ends back we can't rely on
- // a modification of the axes of rotation. To account for this we
- // need to reorient the inverse rotation in terms of the current
- // axes of rotation.
+ // Transform the window crop to match the buffer coordinate system,
+ // which means using the inverse of the current transform set on the
+ // SurfaceFlingerConsumer.
+ uint32_t invTransform = mCurrentTransform;
+ if (mSurfaceFlingerConsumer->getTransformToDisplayInverse()) {
+ /*
+ * the code below applies the display's inverse transform to the buffer
+ */
+ uint32_t invTransformOrient = hw->getOrientationTransform();
+ // calculate the inverse transform
+ if (invTransformOrient & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ invTransformOrient ^= NATIVE_WINDOW_TRANSFORM_FLIP_V |
+ NATIVE_WINDOW_TRANSFORM_FLIP_H;
+ // If the transform has been rotated the axis of flip has been swapped
+ // so we need to swap which flip operations we are performing
bool is_h_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) != 0;
bool is_v_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) != 0;
- if (is_h_flipped == is_v_flipped) {
+ if (is_h_flipped != is_v_flipped) {
invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V |
NATIVE_WINDOW_TRANSFORM_FLIP_H;
}
- winWidth = s.active.h;
- winHeight = s.active.w;
}
- const Rect winCrop = activeCrop.transform(
- invTransform, s.active.w, s.active.h);
-
- // below, crop is intersected with winCrop expressed in crop's coordinate space
- float xScale = crop.getWidth() / float(winWidth);
- float yScale = crop.getHeight() / float(winHeight);
-
- float insetL = winCrop.left * xScale;
- float insetT = winCrop.top * yScale;
- float insetR = (winWidth - winCrop.right ) * xScale;
- float insetB = (winHeight - winCrop.bottom) * yScale;
-
- crop.left += insetL;
- crop.top += insetT;
- crop.right -= insetR;
- crop.bottom -= insetB;
+ // and apply to the current transform
+ invTransform = (Transform(invTransform) * Transform(invTransformOrient)).getOrientation();
}
+
+ int winWidth = s.active.w;
+ int winHeight = s.active.h;
+ if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ // If the activeCrop has been rotate the ends are rotated but not
+ // the space itself so when transforming ends back we can't rely on
+ // a modification of the axes of rotation. To account for this we
+ // need to reorient the inverse rotation in terms of the current
+ // axes of rotation.
+ bool is_h_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) != 0;
+ bool is_v_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) != 0;
+ if (is_h_flipped == is_v_flipped) {
+ invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V |
+ NATIVE_WINDOW_TRANSFORM_FLIP_H;
+ }
+ winWidth = s.active.h;
+ winHeight = s.active.w;
+ }
+ const Rect winCrop = activeCrop.transform(
+ invTransform, s.active.w, s.active.h);
+
+ // below, crop is intersected with winCrop expressed in crop's coordinate space
+ float xScale = crop.getWidth() / float(winWidth);
+ float yScale = crop.getHeight() / float(winHeight);
+
+ float insetL = winCrop.left * xScale;
+ float insetT = winCrop.top * yScale;
+ float insetR = (winWidth - winCrop.right ) * xScale;
+ float insetB = (winHeight - winCrop.bottom) * yScale;
+
+ crop.left += insetL;
+ crop.top += insetT;
+ crop.right -= insetR;
+ crop.bottom -= insetB;
+
return crop;
}
@@ -537,10 +546,12 @@
Rect activeCrop(s.active.crop);
activeCrop = s.active.transform.transform(activeCrop);
#ifdef USE_HWC2
- activeCrop.intersect(displayDevice->getViewport(), &activeCrop);
+ if(!activeCrop.intersect(displayDevice->getViewport(), &activeCrop)) {
#else
- activeCrop.intersect(hw->getViewport(), &activeCrop);
+ if(!activeCrop.intersect(hw->getViewport(), &activeCrop)) {
#endif
+ activeCrop.clear();
+ }
activeCrop = s.active.transform.inverse().transform(activeCrop);
// This needs to be here as transform.transform(Rect) computes the
// transformed rect and then takes the bounding box of the result before
@@ -548,7 +559,9 @@
// transform.inverse().transform(transform.transform(Rect)) != Rect
// in which case we need to make sure the final rect is clipped to the
// display bounds.
- activeCrop.intersect(Rect(s.active.w, s.active.h), &activeCrop);
+ if(!activeCrop.intersect(Rect(s.active.w, s.active.h), &activeCrop)) {
+ activeCrop.clear();
+ }
// mark regions outside the crop as transparent
activeTransparentRegion.orSelf(Rect(0, 0, s.active.w, activeCrop.top));
activeTransparentRegion.orSelf(Rect(0, activeCrop.bottom,
@@ -559,8 +572,15 @@
s.active.w, activeCrop.bottom));
}
Rect frame(s.active.transform.transform(computeBounds(activeTransparentRegion)));
+ if (!s.active.finalCrop.isEmpty()) {
+ if(!frame.intersect(s.active.finalCrop, &frame)) {
+ frame.clear();
+ }
+ }
#ifdef USE_HWC2
- frame.intersect(displayDevice->getViewport(), &frame);
+ if (!frame.intersect(displayDevice->getViewport(), &frame)) {
+ frame.clear();
+ }
const Transform& tr(displayDevice->getTransform());
Rect transformedFrame = tr.transform(frame);
auto error = hwcLayer->setDisplayFrame(transformedFrame);
@@ -588,7 +608,9 @@
mName.string(), s.z, to_string(error).c_str(),
static_cast<int32_t>(error));
#else
- frame.intersect(hw->getViewport(), &frame);
+ if (!frame.intersect(hw->getViewport(), &frame)) {
+ frame.clear();
+ }
const Transform& tr(hw->getTransform());
layer.setFrame(tr.transform(frame));
layer.setCrop(computeCrop(hw));
@@ -782,6 +804,9 @@
Rect bounds = reduce(win, s.activeTransparentRegion);
Rect frame(s.active.transform.transform(bounds));
frame.intersect(displayDevice->getViewport(), &frame);
+ if (!s.active.finalCrop.isEmpty()) {
+ frame.intersect(s.active.finalCrop, &frame);
+ }
auto& displayTransform(displayDevice->getTransform());
auto position = displayTransform.transform(frame);
@@ -828,6 +853,9 @@
Rect bounds = reduce(win, s.activeTransparentRegion);
Rect frame(s.active.transform.transform(bounds));
frame.intersect(hw->getViewport(), &frame);
+ if (!s.active.finalCrop.isEmpty()) {
+ frame.intersect(s.active.finalCrop, &frame);
+ }
const Transform& tr(hw->getTransform());
return Rect(tr.transform(frame));
}
@@ -982,7 +1010,18 @@
* minimal value)? Or, we could make GL behave like HWC -- but this feel
* like more of a hack.
*/
- const Rect win(computeBounds());
+ Rect win(computeBounds());
+
+ if (!s.active.finalCrop.isEmpty()) {
+ win = s.active.transform.transform(win);
+ if (!win.intersect(s.active.finalCrop, &win)) {
+ win.clear();
+ }
+ win = s.active.transform.inverse().transform(win);
+ if (!win.intersect(computeBounds(), &win)) {
+ win.clear();
+ }
+ }
float left = float(win.left) / float(s.active.w);
float top = float(win.top) / float(s.active.h);
@@ -1116,12 +1155,26 @@
// local state
// ----------------------------------------------------------------------------
+static void boundPoint(vec2* point, const Rect& crop) {
+ if (point->x < crop.left) {
+ point->x = crop.left;
+ }
+ if (point->x > crop.right) {
+ point->x = crop.right;
+ }
+ if (point->y < crop.top) {
+ point->y = crop.top;
+ }
+ if (point->y > crop.bottom) {
+ point->y = crop.bottom;
+ }
+}
+
void Layer::computeGeometry(const sp<const DisplayDevice>& hw, Mesh& mesh,
bool useIdentityTransform) const
{
const Layer::State& s(getDrawingState());
- const Transform tr(useIdentityTransform ?
- hw->getTransform() : hw->getTransform() * s.active.transform);
+ const Transform tr(hw->getTransform());
const uint32_t hw_h = hw->getHeight();
Rect win(s.active.w, s.active.h);
if (!s.active.crop.isEmpty()) {
@@ -1130,11 +1183,30 @@
// subtract the transparent region and snap to the bounds
win = reduce(win, s.activeTransparentRegion);
+ vec2 lt = vec2(win.left, win.top);
+ vec2 lb = vec2(win.left, win.bottom);
+ vec2 rb = vec2(win.right, win.bottom);
+ vec2 rt = vec2(win.right, win.top);
+
+ if (!useIdentityTransform) {
+ lt = s.active.transform.transform(lt);
+ lb = s.active.transform.transform(lb);
+ rb = s.active.transform.transform(rb);
+ rt = s.active.transform.transform(rt);
+ }
+
+ if (!s.active.finalCrop.isEmpty()) {
+ boundPoint(<, s.active.finalCrop);
+ boundPoint(&lb, s.active.finalCrop);
+ boundPoint(&rb, s.active.finalCrop);
+ boundPoint(&rt, s.active.finalCrop);
+ }
+
Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
- position[0] = tr.transform(win.left, win.top);
- position[1] = tr.transform(win.left, win.bottom);
- position[2] = tr.transform(win.right, win.bottom);
- position[3] = tr.transform(win.right, win.top);
+ position[0] = tr.transform(lt);
+ position[1] = tr.transform(lb);
+ position[2] = tr.transform(rb);
+ position[3] = tr.transform(rt);
for (size_t i=0 ; i<4 ; i++) {
position[i].y = hw_h - position[i].y;
}
@@ -1500,6 +1572,15 @@
setTransactionFlags(eTransactionNeeded);
return true;
}
+bool Layer::setFinalCrop(const Rect& crop) {
+ if (mCurrentState.requested.finalCrop == crop)
+ return false;
+ mCurrentState.sequence++;
+ mCurrentState.requested.finalCrop = crop;
+ mCurrentState.modified = true;
+ setTransactionFlags(eTransactionNeeded);
+ return true;
+}
bool Layer::setLayerStack(uint32_t layerStack) {
if (mCurrentState.layerStack == layerStack)
@@ -1993,7 +2074,8 @@
sp<Client> client(mClientRef.promote());
result.appendFormat( " "
- "layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), crop=(%4d,%4d,%4d,%4d), "
+ "layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), "
+ "crop=(%4d,%4d,%4d,%4d), finalCrop=(%4d,%4d,%4d,%4d), "
"isOpaque=%1d, invalidate=%1d, "
#ifdef USE_HWC2
"alpha=%.3f, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n"
@@ -2004,6 +2086,8 @@
s.layerStack, s.z, s.active.transform.tx(), s.active.transform.ty(), s.active.w, s.active.h,
s.active.crop.left, s.active.crop.top,
s.active.crop.right, s.active.crop.bottom,
+ s.active.finalCrop.left, s.active.finalCrop.top,
+ s.active.finalCrop.right, s.active.finalCrop.bottom,
isOpaque(s), contentDirty,
s.alpha, s.flags,
s.active.transform[0][0], s.active.transform[0][1],
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index b0088e6..4dc0764 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -94,6 +94,7 @@
uint32_t w;
uint32_t h;
Rect crop;
+ Rect finalCrop;
Transform transform;
inline bool operator ==(const Geometry& rhs) const {
@@ -155,6 +156,7 @@
bool setTransparentRegionHint(const Region& transparent);
bool setFlags(uint8_t flags, uint8_t mask);
bool setCrop(const Rect& crop);
+ bool setFinalCrop(const Rect& crop);
bool setLayerStack(uint32_t layerStack);
void deferTransactionUntil(const sp<IBinder>& handle, uint64_t frameNumber);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3a0e21f..019bcec 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2236,6 +2236,10 @@
if (layer->setCrop(s.crop))
flags |= eTraversalNeeded;
}
+ if (what & layer_state_t::eFinalCropChanged) {
+ if (layer->setFinalCrop(s.finalCrop))
+ flags |= eTraversalNeeded;
+ }
if (what & layer_state_t::eLayerStackChanged) {
// NOTE: index needs to be calculated before we update the state
ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index d864874..e41fbdd 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -2274,6 +2274,10 @@
if (layer->setCrop(s.crop))
flags |= eTraversalNeeded;
}
+ if (what & layer_state_t::eFinalCropChanged) {
+ if (layer->setFinalCrop(s.finalCrop))
+ flags |= eTraversalNeeded;
+ }
if (what & layer_state_t::eLayerStackChanged) {
// NOTE: index needs to be calculated before we update the state
ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h
index c2f0010..9efeb9e 100644
--- a/services/surfaceflinger/Transform.h
+++ b/services/surfaceflinger/Transform.h
@@ -81,6 +81,9 @@
Region transform(const Region& reg) const;
Rect transform(const Rect& bounds) const;
Transform operator * (const Transform& rhs) const;
+ // assumes the last row is < 0 , 0 , 1 >
+ vec2 transform(const vec2& v) const;
+ vec3 transform(const vec3& v) const;
Transform inverse() const;
@@ -96,9 +99,6 @@
enum { UNKNOWN_TYPE = 0x80000000 };
- // assumes the last row is < 0 , 0 , 1 >
- vec2 transform(const vec2& v) const;
- vec3 transform(const vec3& v) const;
uint32_t type() const;
static bool absIsOne(float f);
static bool isZero(float f);
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index ee4ad4e..d7cb899 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -280,6 +280,31 @@
}
}
+TEST_F(LayerUpdateTest, LayerFinalCropWorks) {
+ sp<ScreenCapture> sc;
+ {
+ SCOPED_TRACE("before crop");
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel( 24, 24, 63, 63, 195);
+ sc->checkPixel( 75, 75, 195, 63, 63);
+ sc->checkPixel(145, 145, 63, 63, 195);
+ }
+ SurfaceComposerClient::openGlobalTransaction();
+ Rect cropRect(16, 16, 32, 32);
+ ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setFinalCrop(cropRect));
+ SurfaceComposerClient::closeGlobalTransaction(true);
+ {
+ // This should crop the foreground surface.
+ SCOPED_TRACE("after crop");
+ ScreenCapture::captureScreen(&sc);
+ sc->checkPixel( 24, 24, 63, 63, 195);
+ sc->checkPixel( 75, 75, 63, 63, 195);
+ sc->checkPixel( 95, 80, 63, 63, 195);
+ sc->checkPixel( 80, 95, 63, 63, 195);
+ sc->checkPixel( 96, 96, 63, 63, 195);
+ }
+}
+
TEST_F(LayerUpdateTest, LayerSetLayerWorks) {
sp<ScreenCapture> sc;
{