SurfaceFlinger: Add sourceCrop to screenshot

Adds a sourceCrop Rect parameter to screenshot commands, which allows
clients to capture only a portion of the screen instead of the whole
screen.

Bug: 15137922
Change-Id: I629447573cd34ffb96334cde7ba02490b9ea06d8
diff --git a/include/gui/ISurfaceComposer.h b/include/gui/ISurfaceComposer.h
index 8042211..1581084 100644
--- a/include/gui/ISurfaceComposer.h
+++ b/include/gui/ISurfaceComposer.h
@@ -41,6 +41,7 @@
 class DisplayInfo;
 class IDisplayEventConnection;
 class IMemoryHeap;
+class Rect;
 
 /*
  * This class defines the Binder IPC interface for accessing various
@@ -131,11 +132,10 @@
      */
     virtual status_t captureScreen(const sp<IBinder>& display,
             const sp<IGraphicBufferProducer>& producer,
-            uint32_t reqWidth, uint32_t reqHeight,
+            Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
             uint32_t minLayerZ, uint32_t maxLayerZ,
             bool useIdentityTransform) = 0;
 
-
     /* Clears the frame statistics for animations.
      *
      * Requires the ACCESS_SURFACE_FLINGER permission.
diff --git a/include/gui/SurfaceComposerClient.h b/include/gui/SurfaceComposerClient.h
index c2192af..e666329 100644
--- a/include/gui/SurfaceComposerClient.h
+++ b/include/gui/SurfaceComposerClient.h
@@ -180,10 +180,12 @@
 class ScreenshotClient
 {
 public:
+    // if cropping isn't required, callers may pass in a default Rect, e.g.:
+    //   capture(display, producer, Rect(), reqWidth, ...);
     static status_t capture(
             const sp<IBinder>& display,
             const sp<IGraphicBufferProducer>& producer,
-            uint32_t reqWidth, uint32_t reqHeight,
+            Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
             uint32_t minLayerZ, uint32_t maxLayerZ,
             bool useIdentityTransform);
 
@@ -197,13 +199,16 @@
     ScreenshotClient();
     ~ScreenshotClient();
 
-    // frees the previous screenshot and capture a new one
-    status_t update(const sp<IBinder>& display, bool useIdentityTransform);
+    // frees the previous screenshot and captures a new one
+    // if cropping isn't required, callers may pass in a default Rect, e.g.:
+    //   update(display, Rect(), useIdentityTransform);
     status_t update(const sp<IBinder>& display,
-            uint32_t reqWidth, uint32_t reqHeight,
+            Rect sourceCrop, bool useIdentityTransform);
+    status_t update(const sp<IBinder>& display,
+            Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
             bool useIdentityTransform);
     status_t update(const sp<IBinder>& display,
-            uint32_t reqWidth, uint32_t reqHeight,
+            Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
             uint32_t minLayerZ, uint32_t maxLayerZ,
             bool useIdentityTransform);
 
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 58a2ae2..c6c88a9 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -104,7 +104,7 @@
 
     virtual status_t captureScreen(const sp<IBinder>& display,
             const sp<IGraphicBufferProducer>& producer,
-            uint32_t reqWidth, uint32_t reqHeight,
+            Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
             uint32_t minLayerZ, uint32_t maxLayerZ,
             bool useIdentityTransform)
     {
@@ -112,6 +112,7 @@
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
         data.writeStrongBinder(display);
         data.writeStrongBinder(producer->asBinder());
+        data.write(sourceCrop);
         data.writeInt32(reqWidth);
         data.writeInt32(reqHeight);
         data.writeInt32(minLayerZ);
@@ -328,6 +329,8 @@
             sp<IBinder> display = data.readStrongBinder();
             sp<IGraphicBufferProducer> producer =
                     interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
+            Rect sourceCrop;
+            data.read(sourceCrop);
             uint32_t reqWidth = data.readInt32();
             uint32_t reqHeight = data.readInt32();
             uint32_t minLayerZ = data.readInt32();
@@ -335,7 +338,7 @@
             bool useIdentityTransform = static_cast<bool>(data.readInt32());
 
             status_t res = captureScreen(display, producer,
-                    reqWidth, reqHeight, minLayerZ, maxLayerZ,
+                    sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ,
                     useIdentityTransform);
             reply->writeInt32(res);
             return NO_ERROR;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 1dffdb2..eedeca1 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -676,11 +676,11 @@
 status_t ScreenshotClient::capture(
         const sp<IBinder>& display,
         const sp<IGraphicBufferProducer>& producer,
-        uint32_t reqWidth, uint32_t reqHeight,
+        Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
         uint32_t minLayerZ, uint32_t maxLayerZ, bool useIdentityTransform) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
     if (s == NULL) return NO_INIT;
-    return s->captureScreen(display, producer,
+    return s->captureScreen(display, producer, sourceCrop,
             reqWidth, reqHeight, minLayerZ, maxLayerZ, useIdentityTransform);
 }
 
@@ -704,7 +704,7 @@
 }
 
 status_t ScreenshotClient::update(const sp<IBinder>& display,
-        uint32_t reqWidth, uint32_t reqHeight,
+        Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
         uint32_t minLayerZ, uint32_t maxLayerZ,
         bool useIdentityTransform) {
     sp<ISurfaceComposer> s(ComposerService::getComposerService());
@@ -717,7 +717,7 @@
         mHaveBuffer = false;
     }
 
-    status_t err = s->captureScreen(display, mProducer,
+    status_t err = s->captureScreen(display, mProducer, sourceCrop,
             reqWidth, reqHeight, minLayerZ, maxLayerZ, useIdentityTransform);
 
     if (err == NO_ERROR) {
@@ -729,16 +729,16 @@
     return err;
 }
 
-status_t ScreenshotClient::update(const sp<IBinder>& display,
+status_t ScreenshotClient::update(const sp<IBinder>& display, Rect sourceCrop,
         bool useIdentityTransform) {
-    return ScreenshotClient::update(display, 0, 0, 0, -1UL,
+    return ScreenshotClient::update(display, sourceCrop, 0, 0, 0, -1UL,
             useIdentityTransform);
 }
 
-status_t ScreenshotClient::update(const sp<IBinder>& display,
+status_t ScreenshotClient::update(const sp<IBinder>& display, Rect sourceCrop,
         uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform) {
-    return ScreenshotClient::update(display, reqWidth, reqHeight, 0, -1UL,
-            useIdentityTransform);
+    return ScreenshotClient::update(display, sourceCrop, reqWidth, reqHeight,
+            0, -1UL, useIdentityTransform);
 }
 
 void ScreenshotClient::release() {
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index bf87fad..5e6aeef 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -21,6 +21,7 @@
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
 #include <gui/BufferItemConsumer.h>
+#include <ui/Rect.h>
 #include <utils/String8.h>
 
 #include <private/gui/ComposerService.h>
@@ -94,7 +95,7 @@
     sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
     sp<ISurfaceComposer> sf(ComposerService::getComposerService());
     sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
-    ASSERT_EQ(NO_ERROR, sf->captureScreen(display, producer,
+    ASSERT_EQ(NO_ERROR, sf->captureScreen(display, producer, Rect(),
             64, 64, 0, 0x7fffffff, false));
 
     // Set the PROTECTED usage bit and verify that the screenshot fails.  Note
@@ -123,7 +124,7 @@
                 &buf));
         ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1));
     }
-    ASSERT_EQ(NO_ERROR, sf->captureScreen(display, producer,
+    ASSERT_EQ(NO_ERROR, sf->captureScreen(display, producer, Rect(),
             64, 64, 0, 0x7fffffff, false));
 }
 
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 42993b9..4ebe291 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -283,7 +283,8 @@
 void DisplayDevice::setViewportAndProjection() const {
     size_t w = mDisplayWidth;
     size_t h = mDisplayHeight;
-    mFlinger->getRenderEngine().setViewportAndProjection(w, h, w, h, false);
+    Rect sourceCrop(0, 0, w, h);
+    mFlinger->getRenderEngine().setViewportAndProjection(w, h, sourceCrop, h, false);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp
index cbff320..d1e324c 100644
--- a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.cpp
@@ -17,6 +17,8 @@
 #include <GLES/gl.h>
 #include <GLES/glext.h>
 
+#include <ui/Rect.h>
+
 #include <utils/String8.h>
 #include <cutils/compiler.h>
 
@@ -72,13 +74,23 @@
 }
 
 void GLES11RenderEngine::setViewportAndProjection(
-        size_t vpw, size_t vph, size_t w, size_t h, bool yswap) {
+        size_t vpw, size_t vph, Rect sourceCrop, size_t hwh, bool yswap) {
     glViewport(0, 0, vpw, vph);
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();
-    // put the origin in the left-bottom corner
-    if (yswap)  glOrthof(0, w, h, 0, 0, 1);
-    else        glOrthof(0, w, 0, h, 0, 1);
+
+    size_t l = sourceCrop.left;
+    size_t r = sourceCrop.right;
+
+    // In GL, (0, 0) is the bottom-left corner, so flip y coordinates
+    size_t t = hwh - sourceCrop.top;
+    size_t b = hwh - sourceCrop.bottom;
+
+    if (yswap) {
+        glOrthof(l, r, t, b, 0, 1);
+    } else {
+        glOrthof(l, r, b, t, 0, 1);
+    }
     glMatrixMode(GL_MODELVIEW);
 }
 
diff --git a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h
index cd53aab..1a94592 100644
--- a/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/GLES11RenderEngine.h
@@ -49,7 +49,8 @@
     virtual ~GLES11RenderEngine();
 
     virtual void dump(String8& result);
-    virtual void setViewportAndProjection(size_t vpw, size_t vph, size_t w, size_t h, bool yswap);
+    virtual void setViewportAndProjection(size_t vpw, size_t vph,
+            Rect sourceCrop, size_t hwh, bool yswap);
     virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha);
     virtual void setupDimLayerBlending(int alpha);
     virtual void setupLayerTexturing(const Texture& texture);
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
index a2a6270..8c1f04e 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.cpp
@@ -19,6 +19,8 @@
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
+#include <ui/Rect.h>
+
 #include <utils/String8.h>
 #include <utils/Trace.h>
 
@@ -78,10 +80,21 @@
 }
 
 void GLES20RenderEngine::setViewportAndProjection(
-        size_t vpw, size_t vph, size_t w, size_t h, bool yswap) {
+        size_t vpw, size_t vph, Rect sourceCrop, size_t hwh, bool yswap) {
+
+    size_t l = sourceCrop.left;
+    size_t r = sourceCrop.right;
+
+    // In GL, (0, 0) is the bottom-left corner, so flip y coordinates
+    size_t t = hwh - sourceCrop.top;
+    size_t b = hwh - sourceCrop.bottom;
+
     mat4 m;
-    if (yswap)  m = mat4::ortho(0, w, h, 0, 0, 1);
-    else        m = mat4::ortho(0, w, 0, h, 0, 1);
+    if (yswap) {
+        m = mat4::ortho(l, r, t, b, 0, 1);
+    } else {
+        m = mat4::ortho(l, r, b, t, 0, 1);
+    }
 
     glViewport(0, 0, vpw, vph);
     mState.setProjectionMatrix(m);
diff --git a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
index 8b67fcc..b6d32fc 100644
--- a/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/GLES20RenderEngine.h
@@ -64,7 +64,8 @@
     virtual ~GLES20RenderEngine();
 
     virtual void dump(String8& result);
-    virtual void setViewportAndProjection(size_t vpw, size_t vph, size_t w, size_t h, bool yswap);
+    virtual void setViewportAndProjection(size_t vpw, size_t vph,
+            Rect sourceCrop, size_t hwh, bool yswap);
     virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha);
     virtual void setupDimLayerBlending(int alpha);
     virtual void setupLayerTexturing(const Texture& texture);
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
index 577dc0a..a2d8242 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.h
@@ -89,7 +89,8 @@
 
     // set-up
     virtual void checkErrors() const;
-    virtual void setViewportAndProjection(size_t vpw, size_t vph, size_t w, size_t h, bool yswap) = 0;
+    virtual void setViewportAndProjection(size_t vpw, size_t vph,
+            Rect sourceCrop, size_t hwh, bool yswap) = 0;
     virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, int alpha) = 0;
     virtual void setupDimLayerBlending(int alpha) = 0;
     virtual void setupLayerTexturing(const Texture& texture) = 0;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 59afa66..7152f93 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2790,7 +2790,7 @@
 
 status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display,
         const sp<IGraphicBufferProducer>& producer,
-        uint32_t reqWidth, uint32_t reqHeight,
+        Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
         uint32_t minLayerZ, uint32_t maxLayerZ,
         bool useIdentityTransform) {
 
@@ -2816,6 +2816,7 @@
         SurfaceFlinger* flinger;
         sp<IBinder> display;
         sp<IGraphicBufferProducer> producer;
+        Rect sourceCrop;
         uint32_t reqWidth, reqHeight;
         uint32_t minLayerZ,maxLayerZ;
         bool useIdentityTransform;
@@ -2824,11 +2825,11 @@
         MessageCaptureScreen(SurfaceFlinger* flinger,
                 const sp<IBinder>& display,
                 const sp<IGraphicBufferProducer>& producer,
-                uint32_t reqWidth, uint32_t reqHeight,
+                Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
                 uint32_t minLayerZ, uint32_t maxLayerZ,
                 bool useIdentityTransform)
             : flinger(flinger), display(display), producer(producer),
-              reqWidth(reqWidth), reqHeight(reqHeight),
+              sourceCrop(sourceCrop), reqWidth(reqWidth), reqHeight(reqHeight),
               minLayerZ(minLayerZ), maxLayerZ(maxLayerZ),
               useIdentityTransform(useIdentityTransform),
               result(PERMISSION_DENIED)
@@ -2841,7 +2842,7 @@
             Mutex::Autolock _l(flinger->mStateLock);
             sp<const DisplayDevice> hw(flinger->getDisplayDevice(display));
             result = flinger->captureScreenImplLocked(hw, producer,
-                    reqWidth, reqHeight, minLayerZ, maxLayerZ,
+                    sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ,
                     useIdentityTransform);
             static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(result);
             return true;
@@ -2864,7 +2865,8 @@
     // which does the marshaling work forwards to our "fake remote" above.
     sp<MessageBase> msg = new MessageCaptureScreen(this,
             display, IGraphicBufferProducer::asInterface( wrapper ),
-            reqWidth, reqHeight, minLayerZ, maxLayerZ, useIdentityTransform);
+            sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ,
+            useIdentityTransform);
 
     status_t res = postMessageAsync(msg);
     if (res == NO_ERROR) {
@@ -2876,7 +2878,7 @@
 
 void SurfaceFlinger::renderScreenImplLocked(
         const sp<const DisplayDevice>& hw,
-        uint32_t reqWidth, uint32_t reqHeight,
+        Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
         uint32_t minLayerZ, uint32_t maxLayerZ,
         bool yswap, bool useIdentityTransform)
 {
@@ -2888,11 +2890,32 @@
     const uint32_t hw_h = hw->getHeight();
     const bool filtering = reqWidth != hw_w || reqWidth != hw_h;
 
+    // if a default or invalid sourceCrop is passed in, set reasonable values
+    if (sourceCrop.width() == 0 || sourceCrop.height() == 0 ||
+            !sourceCrop.isValid()) {
+        sourceCrop.setLeftTop(Point(0, 0));
+        sourceCrop.setRightBottom(Point(hw_w, hw_h));
+    }
+
+    // ensure that sourceCrop is inside screen
+    if (sourceCrop.left < 0) {
+        ALOGE("Invalid crop rect: l = %d (< 0)", sourceCrop.left);
+    }
+    if (sourceCrop.right >= hw_w) {
+        ALOGE("Invalid crop rect: r = %d (>= %d)", sourceCrop.right, hw_w);
+    }
+    if (sourceCrop.top < 0) {
+        ALOGE("Invalid crop rect: t = %d (< 0)", sourceCrop.top);
+    }
+    if (sourceCrop.bottom >= hw_h) {
+        ALOGE("Invalid crop rect: b = %d (>= %d)", sourceCrop.bottom, hw_h);
+    }
+
     // make sure to clear all GL error flags
     engine.checkErrors();
 
     // set-up our viewport
-    engine.setViewportAndProjection(reqWidth, reqHeight, hw_w, hw_h, yswap);
+    engine.setViewportAndProjection(reqWidth, reqHeight, sourceCrop, hw_h, yswap);
     engine.disableTexturing();
 
     // redraw the screen entirely...
@@ -2923,7 +2946,7 @@
 status_t SurfaceFlinger::captureScreenImplLocked(
         const sp<const DisplayDevice>& hw,
         const sp<IGraphicBufferProducer>& producer,
-        uint32_t reqWidth, uint32_t reqHeight,
+        Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
         uint32_t minLayerZ, uint32_t maxLayerZ,
         bool useIdentityTransform)
 {
@@ -2978,7 +3001,7 @@
                         // via an FBO, which means we didn't have to create
                         // an EGLSurface and therefore we're not
                         // dependent on the context's EGLConfig.
-                        renderScreenImplLocked(hw, reqWidth, reqHeight,
+                        renderScreenImplLocked(hw, sourceCrop, reqWidth, reqHeight,
                                 minLayerZ, maxLayerZ, true, useIdentityTransform);
 
                         // Create a sync point and wait on it, so we know the buffer is
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 0b868e2..cc01eb3 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -202,7 +202,7 @@
     virtual sp<IDisplayEventConnection> createDisplayEventConnection();
     virtual status_t captureScreen(const sp<IBinder>& display,
             const sp<IGraphicBufferProducer>& producer,
-            uint32_t reqWidth, uint32_t reqHeight,
+            Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
             uint32_t minLayerZ, uint32_t maxLayerZ,
             bool useIdentityTransform);
     // called when screen needs to turn off
@@ -311,14 +311,14 @@
 
     void renderScreenImplLocked(
             const sp<const DisplayDevice>& hw,
-            uint32_t reqWidth, uint32_t reqHeight,
+            Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
             uint32_t minLayerZ, uint32_t maxLayerZ,
             bool yswap, bool useIdentityTransform);
 
     status_t captureScreenImplLocked(
             const sp<const DisplayDevice>& hw,
             const sp<IGraphicBufferProducer>& producer,
-            uint32_t reqWidth, uint32_t reqHeight,
+            Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
             uint32_t minLayerZ, uint32_t maxLayerZ,
             bool useIdentityTransform);