From 74c40c0a273dbfd7d10617c4cc1b0c066bfc812e Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 29 Sep 2010 13:02:36 -0700 Subject: [PATCH] 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 --- include/surfaceflinger/ISurfaceComposer.h | 3 +- .../surfaceflinger/SurfaceComposerClient.h | 30 +++ .../ISurfaceComposer.cpp | 10 +- .../SurfaceComposerClient.cpp | 50 +++++ services/surfaceflinger/Layer.cpp | 11 + services/surfaceflinger/Layer.h | 1 + services/surfaceflinger/LayerBase.cpp | 6 + services/surfaceflinger/LayerBase.h | 1 + services/surfaceflinger/LayerBuffer.cpp | 6 + services/surfaceflinger/LayerBuffer.h | 1 + services/surfaceflinger/SurfaceFlinger.cpp | 202 ++++++++++-------- services/surfaceflinger/SurfaceFlinger.h | 9 +- .../tests/screencap/screencap.cpp | 2 +- 13 files changed, 242 insertions(+), 90 deletions(-) diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h index 76307b283..653360023 100644 --- a/include/surfaceflinger/ISurfaceComposer.h +++ b/include/surfaceflinger/ISurfaceComposer.h @@ -115,7 +115,8 @@ public: */ virtual status_t captureScreen(DisplayID dpy, sp* heap, - uint32_t* width, uint32_t* height, PixelFormat* format) = 0; + uint32_t* width, uint32_t* height, PixelFormat* format, + uint32_t reqWidth, uint32_t reqHeight) = 0; /* Signal surfaceflinger that there might be some work to do * This is an ASYNCHRONOUS call. diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h index 8773d7133..a80832d5d 100644 --- a/include/surfaceflinger/SurfaceComposerClient.h +++ b/include/surfaceflinger/SurfaceComposerClient.h @@ -169,6 +169,36 @@ private: sp mClient; }; +// --------------------------------------------------------------------------- + +class ScreenshotClient +{ + sp mHeap; + uint32_t mWidth; + uint32_t mHeight; + PixelFormat mFormat; +public: + ScreenshotClient(); + + // frees the previous screenshot and capture a new one + status_t update(); + status_t update(uint32_t reqWidth, uint32_t reqHeight); + + // release memory occupied by the screenshot + void release(); + + // pixels are valid until this object is freed or + // release() or update() is called + void const* getPixels() const; + + uint32_t getWidth() const; + uint32_t getHeight() const; + PixelFormat getFormat() const; + uint32_t getStride() const; + // size of allocated memory in bytes + size_t getSize() const; +}; + // --------------------------------------------------------------------------- }; // namespace android diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp index 040060ef2..d676f5e90 100644 --- a/libs/surfaceflinger_client/ISurfaceComposer.cpp +++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp @@ -126,11 +126,14 @@ public: virtual status_t captureScreen(DisplayID dpy, sp* heap, - uint32_t* width, uint32_t* height, PixelFormat* format) + uint32_t* width, uint32_t* height, PixelFormat* format, + uint32_t reqWidth, uint32_t reqHeight) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeInt32(dpy); + data.writeInt32(reqWidth); + data.writeInt32(reqHeight); remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); *heap = interface_cast(reply.readStrongBinder()); *width = reply.readInt32(); @@ -208,10 +211,13 @@ status_t BnSurfaceComposer::onTransact( case CAPTURE_SCREEN: { CHECK_INTERFACE(ISurfaceComposer, data, reply); DisplayID dpy = data.readInt32(); + uint32_t reqWidth = data.readInt32(); + uint32_t reqHeight = data.readInt32(); sp heap; uint32_t w, h; PixelFormat f; - status_t res = captureScreen(dpy, &heap, &w, &h, &f); + status_t res = captureScreen(dpy, &heap, &w, &h, &f, + reqWidth, reqHeight); reply->writeStrongBinder(heap->asBinder()); reply->writeInt32(w); reply->writeInt32(h); diff --git a/libs/surfaceflinger_client/SurfaceComposerClient.cpp b/libs/surfaceflinger_client/SurfaceComposerClient.cpp index 4096ac685..f27046160 100644 --- a/libs/surfaceflinger_client/SurfaceComposerClient.cpp +++ b/libs/surfaceflinger_client/SurfaceComposerClient.cpp @@ -544,6 +544,56 @@ status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint) return NO_ERROR; } +// ---------------------------------------------------------------------------- + +ScreenshotClient::ScreenshotClient() + : mWidth(0), mHeight(0), mFormat(PIXEL_FORMAT_NONE) { +} + +status_t ScreenshotClient::update() { + sp s(ComposerService::getComposerService()); + if (s == NULL) return NO_INIT; + mHeap = 0; + return s->captureScreen(0, &mHeap, + &mWidth, &mHeight, &mFormat, 0, 0); +} + +status_t ScreenshotClient::update(uint32_t reqWidth, uint32_t reqHeight) { + sp s(ComposerService::getComposerService()); + if (s == NULL) return NO_INIT; + mHeap = 0; + return s->captureScreen(0, &mHeap, + &mWidth, &mHeight, &mFormat, reqWidth, reqHeight); +} + +void ScreenshotClient::release() { + mHeap = 0; +} + +void const* ScreenshotClient::getPixels() const { + return mHeap->getBase(); +} + +uint32_t ScreenshotClient::getWidth() const { + return mWidth; +} + +uint32_t ScreenshotClient::getHeight() const { + return mHeight; +} + +PixelFormat ScreenshotClient::getFormat() const { + return mFormat; +} + +uint32_t ScreenshotClient::getStride() const { + return mWidth; +} + +size_t ScreenshotClient::getSize() const { + return mHeap->getSize(); +} + // ---------------------------------------------------------------------------- }; // namespace android diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 1b21a8d05..fb7672008 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -271,6 +271,17 @@ slowpath: } } +void Layer::drawForSreenShot() const +{ + bool currentFixedSize = mFixedSize; + bool currentBlending = mNeedsBlending; + const_cast(this)->mFixedSize = false; + const_cast(this)->mFixedSize = true; + LayerBase::drawForSreenShot(); + const_cast(this)->mFixedSize = currentFixedSize; + const_cast(this)->mNeedsBlending = currentBlending; +} + void Layer::onDraw(const Region& clip) const { Texture tex(mBufferManager.getActiveTexture()); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 188da6a78..caa6d1711 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -70,6 +70,7 @@ public: // LayerBase interface virtual void setGeometry(hwc_layer_t* hwcl); virtual void setPerFrameData(hwc_layer_t* hwcl); + virtual void drawForSreenShot() const; virtual void onDraw(const Region& clip) const; virtual uint32_t doTransaction(uint32_t transactionFlags); virtual void lockPageFlip(bool& recomputeVisibleRegions); diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp index 3d049a796..14191cbc2 100644 --- a/services/surfaceflinger/LayerBase.cpp +++ b/services/surfaceflinger/LayerBase.cpp @@ -326,6 +326,12 @@ void LayerBase::draw(const Region& clip) const onDraw(clip); } +void LayerBase::drawForSreenShot() const +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + onDraw( Region(hw.bounds()) ); +} + void LayerBase::clearWithOpenGL(const Region& clip, GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) const diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h index c66dc3450..bdee05b95 100644 --- a/services/surfaceflinger/LayerBase.h +++ b/services/surfaceflinger/LayerBase.h @@ -121,6 +121,7 @@ public: * to perform the actual drawing. */ virtual void draw(const Region& clip) const; + virtual void drawForSreenShot() const; /** * onDraw - draws the surface. diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp index fdf9abc81..c060895bf 100644 --- a/services/surfaceflinger/LayerBuffer.cpp +++ b/services/surfaceflinger/LayerBuffer.cpp @@ -132,6 +132,12 @@ void LayerBuffer::unlockPageFlip(const Transform& planeTransform, LayerBase::unlockPageFlip(planeTransform, outDirtyRegion); } +void LayerBuffer::drawForSreenShot() const +{ + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + clearWithOpenGL( Region(hw.bounds()) ); +} + void LayerBuffer::onDraw(const Region& clip) const { sp source(getSource()); diff --git a/services/surfaceflinger/LayerBuffer.h b/services/surfaceflinger/LayerBuffer.h index 1c0bf830e..fece858a2 100644 --- a/services/surfaceflinger/LayerBuffer.h +++ b/services/surfaceflinger/LayerBuffer.h @@ -64,6 +64,7 @@ public: virtual sp createSurface() const; virtual status_t ditch(); virtual void onDraw(const Region& clip) const; + virtual void drawForSreenShot() const; virtual uint32_t doTransaction(uint32_t flags); virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 17b98a680..e6bdfd141 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1653,9 +1653,117 @@ status_t SurfaceFlinger::onTransact( // --------------------------------------------------------------------------- +status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy, + sp* 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 >& layers(mVisibleLayersSortedByZ); + const size_t count = layers.size(); + for (size_t i=0 ; i& 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 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* 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 @@ status_t SurfaceFlinger::captureScreen(DisplayID dpy, uint32_t* w; uint32_t* h; PixelFormat* f; + uint32_t sw; + uint32_t sh; status_t result; public: MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy, - sp* heap, uint32_t* w, uint32_t* h, PixelFormat* f) + sp* 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 @@ status_t SurfaceFlinger::captureScreen(DisplayID dpy, 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 >& layers( - flinger->mVisibleLayersSortedByZ); - const size_t count = layers.size(); - for (size_t i=0 ; i& 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 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 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( msg.get() )->getResult(); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 6e9ecbdd1..732e57e36 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -197,7 +197,9 @@ public: sp* heap, uint32_t* width, uint32_t* height, - PixelFormat* format); + PixelFormat* format, + uint32_t reqWidth, + uint32_t reqHeight); void screenReleased(DisplayID dpy); void screenAcquired(DisplayID dpy); @@ -319,6 +321,11 @@ private: void commitTransaction(); + status_t captureScreenImplLocked(DisplayID dpy, + sp* heap, + uint32_t* width, uint32_t* height, PixelFormat* format, + uint32_t reqWidth = 0, uint32_t reqHeight = 0); + friend class FreezeLock; sp getFreezeLock() const; inline void incFreezeCount() { diff --git a/services/surfaceflinger/tests/screencap/screencap.cpp b/services/surfaceflinger/tests/screencap/screencap.cpp index 9e893f48f..6cf150417 100644 --- a/services/surfaceflinger/tests/screencap/screencap.cpp +++ b/services/surfaceflinger/tests/screencap/screencap.cpp @@ -42,7 +42,7 @@ int main(int argc, char** argv) sp heap; uint32_t w, h; PixelFormat f; - status_t err = composer->captureScreen(0, &heap, &w, &h, &f); + status_t err = composer->captureScreen(0, &heap, &w, &h, &f, 0, 0); if (err != NO_ERROR) { fprintf(stderr, "screen capture failed: %s\n", strerror(-err)); exit(0);