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 695cbfa0a..a060d316d 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -214,6 +214,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 e1d283bed..263c37271 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -68,6 +68,7 @@ public: bool isFixedSize() const; // LayerBase interface + 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 6fc50109e..758b40878 100644 --- a/services/surfaceflinger/LayerBase.cpp +++ b/services/surfaceflinger/LayerBase.cpp @@ -317,6 +317,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 8cba287f8..d688f6523 100644 --- a/services/surfaceflinger/LayerBase.h +++ b/services/surfaceflinger/LayerBase.h @@ -115,6 +115,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 0240748ce..87cabedc0 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 2b06f6fa4..e5e87c60e 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1546,9 +1546,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)) @@ -1564,12 +1672,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 { @@ -1582,94 +1693,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 f09fdbc05..f0a167b96 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); @@ -318,6 +320,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);