Merge "refactored screenshot code" into gingerbread

This commit is contained in:
Mathias Agopian 2010-10-04 20:04:42 -07:00 committed by Android (Google) Code Review
commit 566b728c93
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

@ -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
{
Texture tex(mBufferManager.getActiveTexture());

View File

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

View File

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

View File

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

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

@ -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,
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))
@ -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<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 {
@ -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<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);
@ -318,6 +320,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);