rework screenshot API and implementation

- SurfaceFlinger now supports to take a screenshot
  directly into an IGraphicBufferProducer

- reimplement the IMemoryHeap screenshot on top
  of the above

- reimplement LayerScreenshot such that its
  BufferQueue is directly used as the destination
  of the screenshot. LayerScreenshot is now a thin
  wrapper around Layer

Bug: 6940974

Change-Id: I69a2096b44b91acbb99eba16f83a9c78d94e0d10
This commit is contained in:
Mathias Agopian 2013-03-01 13:42:57 -08:00
parent eb0d12963d
commit 2a9fc493df
9 changed files with 285 additions and 346 deletions

View File

@ -103,7 +103,6 @@ public:
uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ) = 0;
/* triggers screen off and waits for it to complete */
virtual void blank(const sp<IBinder>& display) = 0;
@ -113,6 +112,11 @@ public:
/* returns information about a display
* intended to be used to get information about built-in displays */
virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) = 0;
virtual status_t captureScreen(const sp<IBinder>& display,
const sp<IGraphicBufferProducer>& producer,
uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ) = 0;
};
// ----------------------------------------------------------------------------
@ -130,11 +134,12 @@ public:
GET_BUILT_IN_DISPLAY,
SET_TRANSACTION_STATE,
AUTHENTICATE_SURFACE,
CAPTURE_SCREEN,
CAPTURE_SCREEN_DEPRECATED,
BLANK,
UNBLANK,
GET_DISPLAY_INFO,
CONNECT_DISPLAY,
CAPTURE_SCREEN,
};
virtual status_t onTransact(uint32_t code, const Parcel& data,

View File

@ -156,10 +156,19 @@ private:
class ScreenshotClient
{
public:
static status_t capture(
const sp<IBinder>& display,
const sp<IGraphicBufferProducer>& producer,
uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ);
private:
sp<IMemoryHeap> mHeap;
uint32_t mWidth;
uint32_t mHeight;
PixelFormat mFormat;
public:
ScreenshotClient();

View File

@ -115,7 +115,7 @@ public:
data.writeInt32(reqHeight);
data.writeInt32(minLayerZ);
data.writeInt32(maxLayerZ);
remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN_DEPRECATED, data, &reply);
*heap = interface_cast<IMemoryHeap>(reply.readStrongBinder());
*width = reply.readInt32();
*height = reply.readInt32();
@ -123,6 +123,23 @@ public:
return reply.readInt32();
}
virtual status_t captureScreen(const sp<IBinder>& display,
const sp<IGraphicBufferProducer>& producer,
uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ)
{
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeStrongBinder(display);
data.writeStrongBinder(producer->asBinder());
data.writeInt32(reqWidth);
data.writeInt32(reqHeight);
data.writeInt32(minLayerZ);
data.writeInt32(maxLayerZ);
remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
return reply.readInt32();
}
virtual bool authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) const
{
@ -268,7 +285,7 @@ status_t BnSurfaceComposer::onTransact(
CHECK_INTERFACE(ISurfaceComposer, data, reply);
bootFinished();
} break;
case CAPTURE_SCREEN: {
case CAPTURE_SCREEN_DEPRECATED: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> display = data.readStrongBinder();
uint32_t reqWidth = data.readInt32();
@ -286,6 +303,19 @@ status_t BnSurfaceComposer::onTransact(
reply->writeInt32(f);
reply->writeInt32(res);
} break;
case CAPTURE_SCREEN: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> display = data.readStrongBinder();
sp<IGraphicBufferProducer> producer =
interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
uint32_t reqWidth = data.readInt32();
uint32_t reqHeight = data.readInt32();
uint32_t minLayerZ = data.readInt32();
uint32_t maxLayerZ = data.readInt32();
status_t res = captureScreen(display, producer,
reqWidth, reqHeight, minLayerZ, maxLayerZ);
reply->writeInt32(res);
} break;
case AUTHENTICATE_SURFACE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IGraphicBufferProducer> bufferProducer =

View File

@ -606,6 +606,17 @@ void SurfaceComposerClient::unblankDisplay(const sp<IBinder>& token) {
// ----------------------------------------------------------------------------
status_t ScreenshotClient::capture(
const sp<IBinder>& display,
const sp<IGraphicBufferProducer>& producer,
uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ) {
sp<ISurfaceComposer> s(ComposerService::getComposerService());
if (s == NULL) return NO_INIT;
return s->captureScreen(display, producer,
reqWidth, reqHeight, minLayerZ, maxLayerZ);
}
ScreenshotClient::ScreenshotClient()
: mWidth(0), mHeight(0), mFormat(PIXEL_FORMAT_NONE) {
}

View File

@ -109,6 +109,10 @@ protected:
virtual void dumpStats(String8& result, char* buffer, size_t SIZE) const;
virtual void clearStats();
sp<SurfaceFlingerConsumer> getConsumer() const {
return mSurfaceFlingerConsumer;
}
private:
// Creates an instance of ISurface for this Layer.
virtual sp<ISurface> createSurface();

View File

@ -18,152 +18,28 @@
#include <stdint.h>
#include <sys/types.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <ui/GraphicBuffer.h>
#include "LayerScreenshot.h"
#include "SurfaceFlinger.h"
#include "DisplayDevice.h"
namespace android {
// ---------------------------------------------------------------------------
LayerScreenshot::LayerScreenshot(SurfaceFlinger* flinger,
const sp<Client>& client)
: LayerBaseClient(flinger, client),
mTextureName(0), mFlinger(flinger), mIsSecure(false)
: Layer(flinger, client)
{
}
LayerScreenshot::~LayerScreenshot()
void LayerScreenshot::onFirstRef()
{
if (mTextureName) {
mFlinger->deleteTextureAsync(mTextureName);
}
}
Layer::onFirstRef();
status_t LayerScreenshot::captureLocked(int32_t layerStack) {
GLfloat u, v;
status_t result = mFlinger->renderScreenToTextureLocked(layerStack,
&mTextureName, &u, &v);
if (result != NO_ERROR) {
return result;
}
initTexture(u, v);
// Currently screenshot always comes from the default display
mIsSecure = mFlinger->getDefaultDisplayDevice()->getSecureLayerVisible();
return NO_ERROR;
}
status_t LayerScreenshot::capture() {
GLfloat u, v;
status_t result = mFlinger->renderScreenToTexture(0, &mTextureName, &u, &v);
if (result != NO_ERROR) {
return result;
}
initTexture(u, v);
// Currently screenshot always comes from the default display
mIsSecure = mFlinger->getDefaultDisplayDevice()->getSecureLayerVisible();
return NO_ERROR;
}
void LayerScreenshot::initTexture(GLfloat u, GLfloat v) {
glBindTexture(GL_TEXTURE_2D, mTextureName);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
mTexCoords[0] = 0; mTexCoords[1] = v;
mTexCoords[2] = 0; mTexCoords[3] = 0;
mTexCoords[4] = u; mTexCoords[5] = 0;
mTexCoords[6] = u; mTexCoords[7] = v;
}
void LayerScreenshot::initStates(uint32_t w, uint32_t h, uint32_t flags) {
LayerBaseClient::initStates(w, h, flags);
if (!(flags & ISurfaceComposerClient::eHidden)) {
capture();
}
if (flags & ISurfaceComposerClient::eSecure) {
ALOGW("ignoring surface flag eSecure - LayerScreenshot is considered "
"secure iff it captures the contents of a secure surface.");
}
}
uint32_t LayerScreenshot::doTransaction(uint32_t flags)
{
const LayerBase::State& draw(drawingState());
const LayerBase::State& curr(currentState());
if (draw.flags & layer_state_t::eLayerHidden) {
if (!(curr.flags & layer_state_t::eLayerHidden)) {
// we're going from hidden to visible
status_t err = captureLocked(curr.layerStack);
if (err != NO_ERROR) {
ALOGW("createScreenshotSurface failed (%s)", strerror(-err));
}
}
} else if (curr.flags & layer_state_t::eLayerHidden) {
// we're going from visible to hidden
if (mTextureName) {
glDeleteTextures(1, &mTextureName);
mTextureName = 0;
}
}
return LayerBaseClient::doTransaction(flags);
}
void LayerScreenshot::onDraw(const sp<const DisplayDevice>& hw, const Region& clip) const
{
const State& s(drawingState());
if (s.alpha>0) {
const GLfloat alpha = s.alpha/255.0f;
const uint32_t fbHeight = hw->getHeight();
if (s.alpha == 0xFF) {
glDisable(GL_BLEND);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
} else {
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
GLuint texName = mTextureName;
if (isSecure() && !hw->isSecure()) {
texName = mFlinger->getProtectedTexName();
}
LayerMesh mesh;
computeGeometry(hw, &mesh);
glColor4f(alpha, alpha, alpha, alpha);
glDisable(GL_TEXTURE_EXTERNAL_OES);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texName);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, mTexCoords);
glVertexPointer(2, GL_FLOAT, 0, mesh.getVertices());
glDrawArrays(GL_TRIANGLE_FAN, 0, mesh.getVertexCount());
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
// FIXME: we currently hardcode the default display
// it's unclear what should we do instead.
sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice());
mFlinger->captureScreenImplLocked(hw, getConsumer()->getBufferQueue(),
0, 0, 0, 0x7FFFFFFF);
}
// ---------------------------------------------------------------------------

View File

@ -20,39 +20,18 @@
#include <stdint.h>
#include <sys/types.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "LayerBase.h"
#include "Layer.h"
// ---------------------------------------------------------------------------
namespace android {
class LayerScreenshot : public LayerBaseClient
class LayerScreenshot : public Layer
{
GLuint mTextureName;
GLfloat mTexCoords[8];
sp<SurfaceFlinger> mFlinger;
bool mIsSecure;
public:
LayerScreenshot(SurfaceFlinger* flinger, const sp<Client>& client);
virtual ~LayerScreenshot();
status_t capture();
virtual void initStates(uint32_t w, uint32_t h, uint32_t flags);
virtual uint32_t doTransaction(uint32_t flags);
virtual void onDraw(const sp<const DisplayDevice>& hw, const Region& clip) const;
virtual bool isOpaque() const { return false; }
virtual bool isSecure() const { return mIsSecure; }
virtual bool isProtectedByApp() const { return false; }
virtual bool isProtectedByDRM() const { return false; }
virtual const char* getTypeId() const { return "LayerScreenshot"; }
private:
status_t captureLocked(int32_t layerStack);
void initTexture(GLfloat u, GLfloat v);
LayerScreenshot(SurfaceFlinger* flinger, const sp<Client>& client);
protected:
virtual void onFirstRef();
};
// ---------------------------------------------------------------------------

View File

@ -2020,6 +2020,7 @@ sp<LayerScreenshot> SurfaceFlinger::createScreenshotLayer(
uint32_t w, uint32_t h, uint32_t flags)
{
sp<LayerScreenshot> layer = new LayerScreenshot(this, client);
layer->setBuffers(w, h, PIXEL_FORMAT_RGBA_8888, flags);
return layer;
}
@ -2603,101 +2604,69 @@ void SurfaceFlinger::repaintEverything() {
}
// ---------------------------------------------------------------------------
status_t SurfaceFlinger::renderScreenToTexture(uint32_t layerStack,
GLuint* textureName, GLfloat* uOut, GLfloat* vOut)
{
Mutex::Autolock _l(mStateLock);
return renderScreenToTextureLocked(layerStack, textureName, uOut, vOut);
}
status_t SurfaceFlinger::renderScreenToTextureLocked(uint32_t layerStack,
GLuint* textureName, GLfloat* uOut, GLfloat* vOut)
{
ATRACE_CALL();
if (!GLExtensions::getInstance().haveFramebufferObject())
return INVALID_OPERATION;
// get screen geometry
// FIXME: figure out what it means to have a screenshot texture w/ multi-display
sp<const DisplayDevice> hw(getDefaultDisplayDevice());
const uint32_t hw_w = hw->getWidth();
const uint32_t hw_h = hw->getHeight();
GLfloat u = 1;
GLfloat v = 1;
// make sure to clear all GL error flags
while ( glGetError() != GL_NO_ERROR ) ;
// create a FBO
GLuint name, tname;
glGenTextures(1, &tname);
glBindTexture(GL_TEXTURE_2D, tname);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
hw_w, hw_h, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
if (glGetError() != GL_NO_ERROR) {
while ( glGetError() != GL_NO_ERROR ) ;
GLint tw = (2 << (31 - clz(hw_w)));
GLint th = (2 << (31 - clz(hw_h)));
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
tw, th, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
u = GLfloat(hw_w) / tw;
v = GLfloat(hw_h) / th;
}
glGenFramebuffersOES(1, &name);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES,
GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tname, 0);
DisplayDevice::setViewportAndProjection(hw);
// redraw the screen entirely...
glDisable(GL_TEXTURE_EXTERNAL_OES);
glDisable(GL_TEXTURE_2D);
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
const Vector< sp<LayerBase> >& layers(hw->getVisibleLayersSortedByZ());
const size_t count = layers.size();
for (size_t i=0 ; i<count ; ++i) {
const sp<LayerBase>& layer(layers[i]);
layer->draw(hw);
}
hw->compositionComplete();
// back to main framebuffer
glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
glDeleteFramebuffersOES(1, &name);
*textureName = tname;
*uOut = u;
*vOut = v;
return NO_ERROR;
}
// Capture screen into an IGraphiBufferProducer
// ---------------------------------------------------------------------------
status_t SurfaceFlinger::captureScreenImplLocked(const sp<IBinder>& display,
sp<IMemoryHeap>* heap,
uint32_t* w, uint32_t* h, PixelFormat* f,
uint32_t sw, uint32_t sh,
status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display,
const sp<IGraphicBufferProducer>& producer,
uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ) {
if (CC_UNLIKELY(display == 0))
return BAD_VALUE;
if (CC_UNLIKELY(producer == 0))
return BAD_VALUE;
class MessageCaptureScreen : public MessageBase {
SurfaceFlinger* flinger;
sp<IBinder> display;
sp<IGraphicBufferProducer> producer;
uint32_t reqWidth, reqHeight;
uint32_t minLayerZ,maxLayerZ;
status_t result;
public:
MessageCaptureScreen(SurfaceFlinger* flinger,
const sp<IBinder>& display,
const sp<IGraphicBufferProducer>& producer,
uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ)
: flinger(flinger), display(display), producer(producer),
reqWidth(reqWidth), reqHeight(reqHeight),
minLayerZ(minLayerZ), maxLayerZ(maxLayerZ),
result(PERMISSION_DENIED)
{
}
status_t getResult() const {
return result;
}
virtual bool handler() {
Mutex::Autolock _l(flinger->mStateLock);
sp<const DisplayDevice> hw(flinger->getDisplayDevice(display));
result = flinger->captureScreenImplLocked(hw, producer,
reqWidth, reqHeight, minLayerZ, maxLayerZ);
return true;
}
};
sp<MessageBase> msg = new MessageCaptureScreen(this,
display, producer, reqWidth, reqHeight, minLayerZ, maxLayerZ);
status_t res = postMessageSync(msg);
if (res == NO_ERROR) {
res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();
}
return res;
}
status_t SurfaceFlinger::captureScreenImplLocked(
const sp<const DisplayDevice>& hw,
const sp<IGraphicBufferProducer>& producer,
uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ)
{
ATRACE_CALL();
status_t result = PERMISSION_DENIED;
if (!GLExtensions::getInstance().haveFramebufferObject()) {
return INVALID_OPERATION;
}
// get screen geometry
sp<const DisplayDevice> hw(getDisplayDevice(display));
const uint32_t hw_w = hw->getWidth();
const uint32_t hw_h = hw->getHeight();
@ -2707,68 +2676,128 @@ status_t SurfaceFlinger::captureScreenImplLocked(const sp<IBinder>& display,
return PERMISSION_DENIED;
}
if ((sw > hw_w) || (sh > hw_h)) {
ALOGE("size mismatch (%d, %d) > (%d, %d)", sw, sh, hw_w, hw_h);
if ((reqWidth > hw_w) || (reqHeight > hw_h)) {
ALOGE("size mismatch (%d, %d) > (%d, %d)",
reqWidth, reqHeight, hw_w, hw_h);
return BAD_VALUE;
}
sw = (!sw) ? hw_w : sw;
sh = (!sh) ? hw_h : sh;
const size_t size = sw * sh * 4;
const bool filtering = sw != hw_w || sh != hw_h;
reqWidth = (!reqWidth) ? hw_w : reqWidth;
reqHeight = (!reqHeight) ? hw_h : reqHeight;
const bool filtering = reqWidth != hw_w || reqWidth != hw_h;
// ALOGD("screenshot: sw=%d, sh=%d, minZ=%d, maxZ=%d",
// sw, sh, minLayerZ, maxLayerZ);
// Create a surface to render into
sp<Surface> surface = new Surface(producer);
ANativeWindow* const window = surface.get();
// set the buffer size to what the user requested
native_window_set_buffers_user_dimensions(window, reqWidth, reqHeight);
// and create the corresponding EGLSurface
EGLSurface eglSurface = eglCreateWindowSurface(
mEGLDisplay, mEGLConfig, window, NULL);
if (eglSurface == EGL_NO_SURFACE) {
ALOGE("captureScreenImplLocked: eglCreateWindowSurface() failed 0x%4x",
eglGetError());
return BAD_VALUE;
}
if (!eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext)) {
ALOGE("captureScreenImplLocked: eglMakeCurrent() failed 0x%4x",
eglGetError());
eglDestroySurface(mEGLDisplay, eglSurface);
return BAD_VALUE;
}
// 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);
// set-up our viewport
glViewport(0, 0, reqWidth, reqHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof(0, hw_w, 0, hw_h, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glGenFramebuffersOES(1, &name);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname);
// redraw the screen entirely...
glDisable(GL_TEXTURE_EXTERNAL_OES);
glDisable(GL_TEXTURE_2D);
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT);
GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
if (status == GL_FRAMEBUFFER_COMPLETE_OES) {
// invert everything, b/c glReadPixel() below will invert the FB
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
glViewport(0, 0, sw, sh);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrthof(0, hw_w, hw_h, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
// redraw the screen entirely...
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT);
const Vector< sp<LayerBase> >& layers(hw->getVisibleLayersSortedByZ());
const size_t count = layers.size();
for (size_t i=0 ; i<count ; ++i) {
const sp<LayerBase>& layer(layers[i]);
const uint32_t z = layer->drawingState().z;
if (z >= minLayerZ && z <= maxLayerZ) {
if (filtering) layer->setFiltering(true);
layer->draw(hw);
if (filtering) layer->setFiltering(false);
}
const Vector< sp<LayerBase> >& layers(hw->getVisibleLayersSortedByZ());
const size_t count = layers.size();
for (size_t i=0 ; i<count ; ++i) {
const sp<LayerBase>& layer(layers[i]);
const uint32_t z = layer->drawingState().z;
if (z >= minLayerZ && z <= maxLayerZ) {
if (filtering) layer->setFiltering(true);
layer->draw(hw);
if (filtering) layer->setFiltering(false);
}
}
// and finishing things up...
if (eglSwapBuffers(mEGLDisplay, eglSurface) != EGL_TRUE) {
ALOGE("captureScreenImplLocked: eglSwapBuffers() failed 0x%4x",
eglGetError());
eglDestroySurface(mEGLDisplay, eglSurface);
return BAD_VALUE;
}
eglDestroySurface(mEGLDisplay, eglSurface);
return NO_ERROR;
}
// ---------------------------------------------------------------------------
// Capture screen into an IMemoryHeap (legacy)
// ---------------------------------------------------------------------------
status_t SurfaceFlinger::captureScreenImplLocked(
const sp<const DisplayDevice>& hw,
sp<IMemoryHeap>* heap,
uint32_t* w, uint32_t* h, PixelFormat* f,
uint32_t sw, uint32_t sh,
uint32_t minLayerZ, uint32_t maxLayerZ)
{
ATRACE_CALL();
if (!GLExtensions::getInstance().haveFramebufferObject()) {
return INVALID_OPERATION;
}
// create the texture that will receive the screenshot, later we'll
// attach a FBO to it so we can call glReadPixels().
GLuint tname;
glGenTextures(1, &tname);
glBindTexture(GL_TEXTURE_2D, tname);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// the GLConsumer will provide the BufferQueue
sp<GLConsumer> consumer = new GLConsumer(tname, true, GL_TEXTURE_2D);
consumer->getBufferQueue()->setDefaultBufferFormat(HAL_PIXEL_FORMAT_RGBA_8888);
// call the new screenshot taking code, passing a BufferQueue to it
status_t result = captureScreenImplLocked(hw,
consumer->getBufferQueue(), sw, sh, minLayerZ, maxLayerZ);
if (result == NO_ERROR) {
result = consumer->updateTexImage();
if (result == NO_ERROR) {
// create a FBO
GLuint name;
glGenFramebuffersOES(1, &name);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, name);
glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES,
GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, tname, 0);
sp<GraphicBuffer> buf(consumer->getCurrentBuffer());
sw = buf->getWidth();
sh = buf->getHeight();
size_t size = buf->getStride() * sh * 4;
// 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(
@ -2788,59 +2817,48 @@ status_t SurfaceFlinger::captureScreenImplLocked(const sp<IBinder>& display,
} else {
result = NO_MEMORY;
}
// back to main framebuffer
glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
glDeleteFramebuffersOES(1, &name);
}
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
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);
hw->compositionComplete();
// ALOGD("screenshot: result = %s", result<0 ? strerror(result) : "OK");
glDeleteTextures(1, &tname);
return result;
}
status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display,
sp<IMemoryHeap>* heap,
uint32_t* width, uint32_t* height, PixelFormat* format,
uint32_t sw, uint32_t sh,
uint32_t* outWidth, uint32_t* outHeight, PixelFormat* outFormat,
uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ)
{
if (CC_UNLIKELY(display == 0))
return BAD_VALUE;
if (!GLExtensions::getInstance().haveFramebufferObject())
return INVALID_OPERATION;
class MessageCaptureScreen : public MessageBase {
SurfaceFlinger* flinger;
sp<IBinder> display;
sp<IMemoryHeap>* heap;
uint32_t* w;
uint32_t* h;
PixelFormat* f;
uint32_t sw;
uint32_t sh;
uint32_t* outWidth;
uint32_t* outHeight;
PixelFormat* outFormat;
uint32_t reqWidth;
uint32_t reqHeight;
uint32_t minLayerZ;
uint32_t maxLayerZ;
status_t result;
public:
MessageCaptureScreen(SurfaceFlinger* flinger, const sp<IBinder>& display,
sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f,
uint32_t sw, uint32_t sh,
MessageCaptureScreen(SurfaceFlinger* flinger,
const sp<IBinder>& display, sp<IMemoryHeap>* heap,
uint32_t* outWidth, uint32_t* outHeight, PixelFormat* outFormat,
uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ)
: flinger(flinger), display(display),
heap(heap), w(w), h(h), f(f), sw(sw), sh(sh),
: flinger(flinger), display(display), heap(heap),
outWidth(outWidth), outHeight(outHeight), outFormat(outFormat),
reqWidth(reqWidth), reqHeight(reqHeight),
minLayerZ(minLayerZ), maxLayerZ(maxLayerZ),
result(PERMISSION_DENIED)
{
@ -2850,14 +2868,17 @@ status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display,
}
virtual bool handler() {
Mutex::Autolock _l(flinger->mStateLock);
result = flinger->captureScreenImplLocked(display,
heap, w, h, f, sw, sh, minLayerZ, maxLayerZ);
sp<const DisplayDevice> hw(flinger->getDisplayDevice(display));
result = flinger->captureScreenImplLocked(hw, heap,
outWidth, outHeight, outFormat,
reqWidth, reqHeight, minLayerZ, maxLayerZ);
return true;
}
};
sp<MessageBase> msg = new MessageCaptureScreen(this,
display, heap, width, height, format, sw, sh, minLayerZ, maxLayerZ);
sp<MessageBase> msg = new MessageCaptureScreen(this, display, heap,
outWidth, outHeight, outFormat,
reqWidth, reqHeight, minLayerZ, maxLayerZ);
status_t res = postMessageSync(msg);
if (res == NO_ERROR) {
res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();

View File

@ -103,14 +103,6 @@ public:
// force full composition on all displays
void repaintEverything();
// renders content on given display to a texture. thread-safe version.
status_t renderScreenToTexture(uint32_t layerStack, GLuint* textureName,
GLfloat* uOut, GLfloat* vOut);
// renders content on given display to a texture, w/o acquiring main lock
status_t renderScreenToTextureLocked(uint32_t layerStack, GLuint* textureName,
GLfloat* uOut, GLfloat* vOut);
// returns the default Display
sp<const DisplayDevice> getDefaultDisplayDevice() const {
return getDisplayDevice(mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY]);
@ -202,6 +194,10 @@ private:
uint32_t* width, uint32_t* height, PixelFormat* format,
uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ,
uint32_t maxLayerZ);
virtual status_t captureScreen(const sp<IBinder>& display,
const sp<IGraphicBufferProducer>& producer,
uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ);
// called when screen needs to turn off
virtual void blank(const sp<IBinder>& display);
// called when screen is turning back on
@ -306,10 +302,18 @@ private:
void startBootAnim();
status_t captureScreenImplLocked(const sp<IBinder>& display, sp<IMemoryHeap>* heap,
uint32_t* width, uint32_t* height, PixelFormat* format,
uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ,
uint32_t maxLayerZ);
status_t captureScreenImplLocked(
const sp<const DisplayDevice>& hw,
sp<IMemoryHeap>* heap,
uint32_t* width, uint32_t* height, PixelFormat* format,
uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ,
uint32_t maxLayerZ);
status_t captureScreenImplLocked(
const sp<const DisplayDevice>& hw,
const sp<IGraphicBufferProducer>& producer,
uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ);
/* ------------------------------------------------------------------------
* EGL