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:
parent
f67f0acc67
commit
df85c455c3
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user