refactored screenshot code

the core screenshot function now can capture the screen at any lower resolution
performing bilinear filtering.

we also now have some client code to interface with the screenshot service.

it's now possible to request a screenshot at a lower resolution.

Change-Id: I5a3b0e431421800e3aad601d9af8f94adffbc71f
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 17b98a6..e6bdfd1 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1653,9 +1653,117 @@
 
 // ---------------------------------------------------------------------------
 
+status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy,
+        sp<IMemoryHeap>* heap,
+        uint32_t* w, uint32_t* h, PixelFormat* f,
+        uint32_t sw, uint32_t sh)
+{
+    status_t result = PERMISSION_DENIED;
+
+    // only one display supported for now
+    if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
+        return BAD_VALUE;
+
+    if (!GLExtensions::getInstance().haveFramebufferObject())
+        return INVALID_OPERATION;
+
+    // get screen geometry
+    const DisplayHardware& hw(graphicPlane(dpy).displayHardware());
+    const uint32_t hw_w = hw.getWidth();
+    const uint32_t hw_h = hw.getHeight();
+
+    if ((sw > hw_w) || (sh > hw_h))
+        return BAD_VALUE;
+
+    sw = (!sw) ? hw_w : sw;
+    sh = (!sh) ? hw_h : sh;
+    const size_t size = sw * sh * 4;
+
+    // make sure to clear all GL error flags
+    while ( glGetError() != GL_NO_ERROR ) ;
+
+    // create a FBO
+    GLuint name, tname;
+    glGenRenderbuffersOES(1, &tname);
+    glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
+    glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);
+    glGenFramebuffersOES(1, &name);
+    glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
+    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
+            GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
+
+    GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
+    if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
+
+        // invert everything, b/c glReadPixel() below will invert the FB
+        glViewport(0, 0, sw, sh);
+        glMatrixMode(GL_PROJECTION);
+        glPushMatrix();
+        glLoadIdentity();
+        glOrthof(0, hw_w, 0, hw_h, 0, 1);
+        glMatrixMode(GL_MODELVIEW);
+
+        // redraw the screen entirely...
+        glClearColor(0,0,0,1);
+        glClear(GL_COLOR_BUFFER_BIT);
+        const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
+        const size_t count = layers.size();
+        for (size_t i=0 ; i<count ; ++i) {
+            const sp<LayerBase>& layer(layers[i]);
+            layer->drawForSreenShot();
+        }
+
+        // XXX: this is needed on tegra
+        glScissor(0, 0, sw, sh);
+
+        // check for errors and return screen capture
+        if (glGetError() != GL_NO_ERROR) {
+            // error while rendering
+            result = INVALID_OPERATION;
+        } else {
+            // allocate shared memory large enough to hold the
+            // screen capture
+            sp<MemoryHeapBase> base(
+                    new MemoryHeapBase(size, 0, "screen-capture") );
+            void* const ptr = base->getBase();
+            if (ptr) {
+                // capture the screen with glReadPixels()
+                glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
+                if (glGetError() == GL_NO_ERROR) {
+                    *heap = base;
+                    *w = sw;
+                    *h = sh;
+                    *f = PIXEL_FORMAT_RGBA_8888;
+                    result = NO_ERROR;
+                }
+            } else {
+                result = NO_MEMORY;
+            }
+        }
+
+        glEnable(GL_SCISSOR_TEST);
+        glViewport(0, 0, hw_w, hw_h);
+        glMatrixMode(GL_PROJECTION);
+        glPopMatrix();
+        glMatrixMode(GL_MODELVIEW);
+
+
+    } else {
+        result = BAD_VALUE;
+    }
+
+    // release FBO resources
+    glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
+    glDeleteRenderbuffersOES(1, &tname);
+    glDeleteFramebuffersOES(1, &name);
+    return result;
+}
+
+
 status_t SurfaceFlinger::captureScreen(DisplayID dpy,
         sp<IMemoryHeap>* heap,
-        uint32_t* width, uint32_t* height, PixelFormat* format)
+        uint32_t* width, uint32_t* height, PixelFormat* format,
+        uint32_t sw, uint32_t sh)
 {
     // only one display supported for now
     if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
@@ -1671,12 +1779,15 @@
         uint32_t* w;
         uint32_t* h;
         PixelFormat* f;
+        uint32_t sw;
+        uint32_t sh;
         status_t result;
     public:
         MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy,
-                sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f)
+                sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f,
+                uint32_t sw, uint32_t sh)
             : flinger(flinger), dpy(dpy),
-              heap(heap), w(w), h(h), f(f), result(PERMISSION_DENIED)
+              heap(heap), w(w), h(h), f(f), sw(sw), sh(sh), result(PERMISSION_DENIED)
         {
         }
         status_t getResult() const {
@@ -1689,94 +1800,15 @@
             if (flinger->mSecureFrameBuffer)
                 return true;
 
-            // make sure to clear all GL error flags
-            while ( glGetError() != GL_NO_ERROR ) ;
+            result = flinger->captureScreenImplLocked(dpy,
+                    heap, w, h, f, sw, sh);
 
-            // get screen geometry
-            const DisplayHardware& hw(flinger->graphicPlane(dpy).displayHardware());
-            const uint32_t sw = hw.getWidth();
-            const uint32_t sh = hw.getHeight();
-            const Region screenBounds(hw.bounds());
-            const size_t size = sw * sh * 4;
-
-            // create a FBO
-            GLuint name, tname;
-            glGenRenderbuffersOES(1, &tname);
-            glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname);
-            glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh);
-            glGenFramebuffersOES(1, &name);
-            glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
-            glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
-                    GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
-
-            GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
-            if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
-
-                // invert everything, b/c glReadPixel() below will invert the FB
-                glMatrixMode(GL_PROJECTION);
-                glPushMatrix();
-                glLoadIdentity();
-                glOrthof(0, sw, 0, sh, 0, 1);
-                glMatrixMode(GL_MODELVIEW);
-
-                // redraw the screen entirely...
-                glClearColor(0,0,0,1);
-                glClear(GL_COLOR_BUFFER_BIT);
-                const Vector< sp<LayerBase> >& layers(
-                        flinger->mVisibleLayersSortedByZ);
-                const size_t count = layers.size();
-                for (size_t i=0 ; i<count ; ++i) {
-                    const sp<LayerBase>& layer(layers[i]);
-                    if (!strcmp(layer->getTypeId(), "LayerBuffer")) {
-                        // we cannot render LayerBuffer because it doens't
-                        // use OpenGL, and won't show-up in the FBO.
-                        continue;
-                    }
-                    layer->draw(screenBounds);
-                }
-
-                glMatrixMode(GL_PROJECTION);
-                glPopMatrix();
-                glMatrixMode(GL_MODELVIEW);
-
-                // check for errors and return screen capture
-                if (glGetError() != GL_NO_ERROR) {
-                    // error while rendering
-                    result = INVALID_OPERATION;
-                } else {
-                    // allocate shared memory large enough to hold the
-                    // screen capture
-                    sp<MemoryHeapBase> base(
-                            new MemoryHeapBase(size, 0, "screen-capture") );
-                    void* const ptr = base->getBase();
-                    if (ptr) {
-                        // capture the screen with glReadPixels()
-                        glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr);
-                        if (glGetError() == GL_NO_ERROR) {
-                            *heap = base;
-                            *w = sw;
-                            *h = sh;
-                            *f = PIXEL_FORMAT_RGBA_8888;
-                            result = NO_ERROR;
-                        }
-                    } else {
-                        result = NO_MEMORY;
-                    }
-                }
-            } else {
-                result = BAD_VALUE;
-            }
-
-            // release FBO resources
-            glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
-            glDeleteRenderbuffersOES(1, &tname);
-            glDeleteFramebuffersOES(1, &name);
             return true;
         }
     };
 
     sp<MessageBase> msg = new MessageCaptureScreen(this,
-            dpy, heap, width, height, format);
+            dpy, heap, width, height, format, sw, sh);
     status_t res = postMessageSync(msg);
     if (res == NO_ERROR) {
         res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();