From 597c7f67b5f2491c6098a1de241a3f0fd274688a Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 29 Sep 2010 13:02:36 -0700 Subject: 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: I33689bba98507ab928d0898b21596d0d2fe4b953 --- services/surfaceflinger/SurfaceFlinger.cpp | 202 +++++++++++++++++------------ 1 file changed, 117 insertions(+), 85 deletions(-) (limited to 'services/surfaceflinger/SurfaceFlinger.cpp') diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 2b06f6f..e5e87c6 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 ) ; - - // 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; - } + result = flinger->captureScreenImplLocked(dpy, + heap, w, h, f, sw, sh); - // 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(); -- cgit v1.1