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
This commit is contained in:
Mathias Agopian 2010-09-29 13:02:36 -07:00
parent e0d26fbac8
commit 74c40c0a27
13 changed files with 242 additions and 90 deletions

View File

@ -115,7 +115,8 @@ public:
*/
virtual status_t captureScreen(DisplayID dpy,
sp<IMemoryHeap>* 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.

View File

@ -169,6 +169,36 @@ private:
sp<ISurfaceComposerClient> mClient;
};
// ---------------------------------------------------------------------------
class ScreenshotClient
{
sp<IMemoryHeap> 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

View File

@ -126,11 +126,14 @@ public:
virtual status_t captureScreen(DisplayID dpy,
sp<IMemoryHeap>* 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<IMemoryHeap>(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<IMemoryHeap> 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);

View File

@ -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<ISurfaceComposer> 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<ISurfaceComposer> 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

View File

@ -271,6 +271,17 @@ slowpath:
}
}
void Layer::drawForSreenShot() const
{
bool currentFixedSize = mFixedSize;
bool currentBlending = mNeedsBlending;
const_cast<Layer*>(this)->mFixedSize = false;
const_cast<Layer*>(this)->mFixedSize = true;
LayerBase::drawForSreenShot();
const_cast<Layer*>(this)->mFixedSize = currentFixedSize;
const_cast<Layer*>(this)->mNeedsBlending = currentBlending;
}
void Layer::onDraw(const Region& clip) const
{
Texture tex(mBufferManager.getActiveTexture());

View File

@ -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);

View File

@ -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

View File

@ -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.

View File

@ -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> source(getSource());

View File

@ -64,6 +64,7 @@ public:
virtual sp<LayerBaseClient::Surface> 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);

View File

@ -1653,9 +1653,117 @@ status_t SurfaceFlinger::onTransact(
// ---------------------------------------------------------------------------
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 @@ 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<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 @@ 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<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();

View File

@ -197,7 +197,9 @@ public:
sp<IMemoryHeap>* 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<IMemoryHeap>* heap,
uint32_t* width, uint32_t* height, PixelFormat* format,
uint32_t reqWidth = 0, uint32_t reqHeight = 0);
friend class FreezeLock;
sp<FreezeLock> getFreezeLock() const;
inline void incFreezeCount() {

View File

@ -42,7 +42,7 @@ int main(int argc, char** argv)
sp<IMemoryHeap> 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);