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(&lt, 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;
     {