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

View File

@ -115,7 +115,8 @@ public:
*/ */
virtual status_t captureScreen(DisplayID dpy, virtual status_t captureScreen(DisplayID dpy,
sp<IMemoryHeap>* heap, 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 /* Signal surfaceflinger that there might be some work to do
* This is an ASYNCHRONOUS call. * This is an ASYNCHRONOUS call.

View File

@ -169,6 +169,36 @@ private:
sp<ISurfaceComposerClient> mClient; 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 }; // namespace android

View File

@ -126,11 +126,14 @@ public:
virtual status_t captureScreen(DisplayID dpy, virtual status_t captureScreen(DisplayID dpy,
sp<IMemoryHeap>* heap, 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; Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
data.writeInt32(dpy); data.writeInt32(dpy);
data.writeInt32(reqWidth);
data.writeInt32(reqHeight);
remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
*heap = interface_cast<IMemoryHeap>(reply.readStrongBinder()); *heap = interface_cast<IMemoryHeap>(reply.readStrongBinder());
*width = reply.readInt32(); *width = reply.readInt32();
@ -208,10 +211,13 @@ status_t BnSurfaceComposer::onTransact(
case CAPTURE_SCREEN: { case CAPTURE_SCREEN: {
CHECK_INTERFACE(ISurfaceComposer, data, reply); CHECK_INTERFACE(ISurfaceComposer, data, reply);
DisplayID dpy = data.readInt32(); DisplayID dpy = data.readInt32();
uint32_t reqWidth = data.readInt32();
uint32_t reqHeight = data.readInt32();
sp<IMemoryHeap> heap; sp<IMemoryHeap> heap;
uint32_t w, h; uint32_t w, h;
PixelFormat f; 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->writeStrongBinder(heap->asBinder());
reply->writeInt32(w); reply->writeInt32(w);
reply->writeInt32(h); reply->writeInt32(h);

View File

@ -544,6 +544,56 @@ status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint)
return NO_ERROR; 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 }; // namespace android

View File

@ -214,6 +214,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 void Layer::onDraw(const Region& clip) const
{ {
Texture tex(mBufferManager.getActiveTexture()); Texture tex(mBufferManager.getActiveTexture());

View File

@ -68,6 +68,7 @@ public:
bool isFixedSize() const; bool isFixedSize() const;
// LayerBase interface // LayerBase interface
virtual void drawForSreenShot() const;
virtual void onDraw(const Region& clip) const; virtual void onDraw(const Region& clip) const;
virtual uint32_t doTransaction(uint32_t transactionFlags); virtual uint32_t doTransaction(uint32_t transactionFlags);
virtual void lockPageFlip(bool& recomputeVisibleRegions); virtual void lockPageFlip(bool& recomputeVisibleRegions);

View File

@ -317,6 +317,12 @@ void LayerBase::draw(const Region& clip) const
onDraw(clip); onDraw(clip);
} }
void LayerBase::drawForSreenShot() const
{
const DisplayHardware& hw(graphicPlane(0).displayHardware());
onDraw( Region(hw.bounds()) );
}
void LayerBase::clearWithOpenGL(const Region& clip, GLclampf red, void LayerBase::clearWithOpenGL(const Region& clip, GLclampf red,
GLclampf green, GLclampf blue, GLclampf green, GLclampf blue,
GLclampf alpha) const GLclampf alpha) const

View File

@ -115,6 +115,7 @@ public:
* to perform the actual drawing. * to perform the actual drawing.
*/ */
virtual void draw(const Region& clip) const; virtual void draw(const Region& clip) const;
virtual void drawForSreenShot() const;
/** /**
* onDraw - draws the surface. * onDraw - draws the surface.

View File

@ -132,6 +132,12 @@ void LayerBuffer::unlockPageFlip(const Transform& planeTransform,
LayerBase::unlockPageFlip(planeTransform, outDirtyRegion); 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 void LayerBuffer::onDraw(const Region& clip) const
{ {
sp<Source> source(getSource()); sp<Source> source(getSource());

View File

@ -64,6 +64,7 @@ public:
virtual sp<LayerBaseClient::Surface> createSurface() const; virtual sp<LayerBaseClient::Surface> createSurface() const;
virtual status_t ditch(); virtual status_t ditch();
virtual void onDraw(const Region& clip) const; virtual void onDraw(const Region& clip) const;
virtual void drawForSreenShot() const;
virtual uint32_t doTransaction(uint32_t flags); virtual uint32_t doTransaction(uint32_t flags);
virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);

View File

@ -1546,9 +1546,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, status_t SurfaceFlinger::captureScreen(DisplayID dpy,
sp<IMemoryHeap>* heap, 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 // only one display supported for now
if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
@ -1564,12 +1672,15 @@ status_t SurfaceFlinger::captureScreen(DisplayID dpy,
uint32_t* w; uint32_t* w;
uint32_t* h; uint32_t* h;
PixelFormat* f; PixelFormat* f;
uint32_t sw;
uint32_t sh;
status_t result; status_t result;
public: public:
MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy, 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), : 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 { status_t getResult() const {
@ -1582,94 +1693,15 @@ status_t SurfaceFlinger::captureScreen(DisplayID dpy,
if (flinger->mSecureFrameBuffer) if (flinger->mSecureFrameBuffer)
return true; return true;
// make sure to clear all GL error flags result = flinger->captureScreenImplLocked(dpy,
while ( glGetError() != GL_NO_ERROR ) ; 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; return true;
} }
}; };
sp<MessageBase> msg = new MessageCaptureScreen(this, sp<MessageBase> msg = new MessageCaptureScreen(this,
dpy, heap, width, height, format); dpy, heap, width, height, format, sw, sh);
status_t res = postMessageSync(msg); status_t res = postMessageSync(msg);
if (res == NO_ERROR) { if (res == NO_ERROR) {
res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult(); res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();

View File

@ -197,7 +197,9 @@ public:
sp<IMemoryHeap>* heap, sp<IMemoryHeap>* heap,
uint32_t* width, uint32_t* width,
uint32_t* height, uint32_t* height,
PixelFormat* format); PixelFormat* format,
uint32_t reqWidth,
uint32_t reqHeight);
void screenReleased(DisplayID dpy); void screenReleased(DisplayID dpy);
void screenAcquired(DisplayID dpy); void screenAcquired(DisplayID dpy);
@ -318,6 +320,11 @@ private:
void commitTransaction(); 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; friend class FreezeLock;
sp<FreezeLock> getFreezeLock() const; sp<FreezeLock> getFreezeLock() const;
inline void incFreezeCount() { inline void incFreezeCount() {

View File

@ -42,7 +42,7 @@ int main(int argc, char** argv)
sp<IMemoryHeap> heap; sp<IMemoryHeap> heap;
uint32_t w, h; uint32_t w, h;
PixelFormat f; 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) { if (err != NO_ERROR) {
fprintf(stderr, "screen capture failed: %s\n", strerror(-err)); fprintf(stderr, "screen capture failed: %s\n", strerror(-err));
exit(0); exit(0);