Merge changes I1f7c4535,I741c68a2 into gingerbread
* changes: simple test app for screen capture API add support for [1974164] Be able to take a screen shot on the device
This commit is contained in:
committed by
Android (Google) Code Review
commit
beabe75a84
@@ -110,6 +110,13 @@ public:
|
||||
*/
|
||||
virtual void bootFinished() = 0;
|
||||
|
||||
/* Capture the specified screen. requires READ_FRAME_BUFFER permission
|
||||
* This function will fail if there is a secure window on screen.
|
||||
*/
|
||||
virtual status_t captureScreen(DisplayID dpy,
|
||||
sp<IMemoryHeap>* heap,
|
||||
uint32_t* width, uint32_t* height, PixelFormat* format) = 0;
|
||||
|
||||
/* Signal surfaceflinger that there might be some work to do
|
||||
* This is an ASYNCHRONOUS call.
|
||||
*/
|
||||
@@ -133,7 +140,8 @@ public:
|
||||
SET_ORIENTATION,
|
||||
FREEZE_DISPLAY,
|
||||
UNFREEZE_DISPLAY,
|
||||
SIGNAL
|
||||
SIGNAL,
|
||||
CAPTURE_SCREEN
|
||||
};
|
||||
|
||||
virtual status_t onTransact( uint32_t code,
|
||||
|
||||
@@ -124,6 +124,21 @@ public:
|
||||
remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
|
||||
}
|
||||
|
||||
virtual status_t captureScreen(DisplayID dpy,
|
||||
sp<IMemoryHeap>* heap,
|
||||
uint32_t* width, uint32_t* height, PixelFormat* format)
|
||||
{
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
|
||||
data.writeInt32(dpy);
|
||||
remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply);
|
||||
*heap = interface_cast<IMemoryHeap>(reply.readStrongBinder());
|
||||
*width = reply.readInt32();
|
||||
*height = reply.readInt32();
|
||||
*format = reply.readInt32();
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual void signal() const
|
||||
{
|
||||
Parcel data, reply;
|
||||
@@ -190,6 +205,19 @@ status_t BnSurfaceComposer::onTransact(
|
||||
sp<IBinder> b = getCblk()->asBinder();
|
||||
reply->writeStrongBinder(b);
|
||||
} break;
|
||||
case CAPTURE_SCREEN: {
|
||||
CHECK_INTERFACE(ISurfaceComposer, data, reply);
|
||||
DisplayID dpy = data.readInt32();
|
||||
sp<IMemoryHeap> heap;
|
||||
uint32_t w, h;
|
||||
PixelFormat f;
|
||||
status_t res = captureScreen(dpy, &heap, &w, &h, &f);
|
||||
reply->writeStrongBinder(heap->asBinder());
|
||||
reply->writeInt32(w);
|
||||
reply->writeInt32(h);
|
||||
reply->writeInt32(f);
|
||||
reply->writeInt32(res);
|
||||
} break;
|
||||
default:
|
||||
return BBinder::onTransact(code, data, reply, flags);
|
||||
}
|
||||
|
||||
@@ -92,6 +92,10 @@ void GLExtensions::initWithGLStrings(
|
||||
// hack for Adreno 200
|
||||
mHaveTextureExternal = true;
|
||||
}
|
||||
|
||||
if (hasExtension("GL_OES_framebuffer_object")) {
|
||||
mHaveFramebufferObject = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool GLExtensions::hasExtension(char const* extension) const
|
||||
|
||||
@@ -39,6 +39,7 @@ class GLExtensions : public Singleton<GLExtensions>
|
||||
bool mHaveTextureExternal : 1;
|
||||
bool mHaveNpot : 1;
|
||||
bool mHaveDirectTexture : 1;
|
||||
bool mHaveFramebufferObject : 1;
|
||||
|
||||
String8 mVendor;
|
||||
String8 mRenderer;
|
||||
@@ -66,6 +67,10 @@ public:
|
||||
return mHaveDirectTexture;
|
||||
}
|
||||
|
||||
inline bool haveFramebufferObject() const {
|
||||
return mHaveFramebufferObject;
|
||||
}
|
||||
|
||||
void initWithGLStrings(
|
||||
GLubyte const* vendor,
|
||||
GLubyte const* renderer,
|
||||
|
||||
@@ -75,6 +75,7 @@ SurfaceFlinger::SurfaceFlinger()
|
||||
mBootTime(systemTime()),
|
||||
mHardwareTest("android.permission.HARDWARE_TEST"),
|
||||
mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"),
|
||||
mReadFramebuffer("android.permission.READ_FRAME_BUFFER"),
|
||||
mDump("android.permission.DUMP"),
|
||||
mVisibleRegionsDirty(false),
|
||||
mDeferReleaseConsole(false),
|
||||
@@ -1465,8 +1466,23 @@ status_t SurfaceFlinger::onTransact(
|
||||
"can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
|
||||
return PERMISSION_DENIED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CAPTURE_SCREEN:
|
||||
{
|
||||
// codes that require permission check
|
||||
IPCThreadState* ipc = IPCThreadState::self();
|
||||
const int pid = ipc->getCallingPid();
|
||||
const int uid = ipc->getCallingUid();
|
||||
if ((uid != AID_GRAPHICS) && !mReadFramebuffer.check(pid, uid)) {
|
||||
LOGE("Permission Denial: "
|
||||
"can't read framebuffer pid=%d, uid=%d", pid, uid);
|
||||
return PERMISSION_DENIED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
|
||||
if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
|
||||
CHECK_INTERFACE(ISurfaceComposer, data, reply);
|
||||
@@ -1530,6 +1546,139 @@ status_t SurfaceFlinger::onTransact(
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
status_t SurfaceFlinger::captureScreen(DisplayID dpy,
|
||||
sp<IMemoryHeap>* heap,
|
||||
uint32_t* width, uint32_t* height, PixelFormat* format)
|
||||
{
|
||||
// only one display supported for now
|
||||
if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT))
|
||||
return BAD_VALUE;
|
||||
|
||||
if (!GLExtensions::getInstance().haveFramebufferObject())
|
||||
return INVALID_OPERATION;
|
||||
|
||||
class MessageCaptureScreen : public MessageBase {
|
||||
SurfaceFlinger* flinger;
|
||||
DisplayID dpy;
|
||||
sp<IMemoryHeap>* heap;
|
||||
uint32_t* w;
|
||||
uint32_t* h;
|
||||
PixelFormat* f;
|
||||
status_t result;
|
||||
public:
|
||||
MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy,
|
||||
sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f)
|
||||
: flinger(flinger), dpy(dpy),
|
||||
heap(heap), w(w), h(h), f(f), result(PERMISSION_DENIED)
|
||||
{
|
||||
}
|
||||
status_t getResult() const {
|
||||
return result;
|
||||
}
|
||||
virtual bool handler() {
|
||||
Mutex::Autolock _l(flinger->mStateLock);
|
||||
|
||||
// if we have secure windows, never allow the screen capture
|
||||
if (flinger->mSecureFrameBuffer)
|
||||
return true;
|
||||
|
||||
// make sure to clear all GL error flags
|
||||
while ( glGetError() != GL_NO_ERROR ) ;
|
||||
|
||||
// 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);
|
||||
status_t res = postMessageSync(msg);
|
||||
if (res == NO_ERROR) {
|
||||
res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
sp<Layer> SurfaceFlinger::getLayer(const sp<ISurface>& sur) const
|
||||
{
|
||||
sp<Layer> result;
|
||||
|
||||
@@ -193,6 +193,11 @@ public:
|
||||
virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags);
|
||||
virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags);
|
||||
virtual void signal() const;
|
||||
virtual status_t captureScreen(DisplayID dpy,
|
||||
sp<IMemoryHeap>* heap,
|
||||
uint32_t* width,
|
||||
uint32_t* height,
|
||||
PixelFormat* format);
|
||||
|
||||
void screenReleased(DisplayID dpy);
|
||||
void screenAcquired(DisplayID dpy);
|
||||
@@ -361,6 +366,7 @@ private:
|
||||
nsecs_t mBootTime;
|
||||
Permission mHardwareTest;
|
||||
Permission mAccessSurfaceFlinger;
|
||||
Permission mReadFramebuffer;
|
||||
Permission mDump;
|
||||
|
||||
// Can only accessed from the main thread, these members
|
||||
|
||||
26
services/surfaceflinger/tests/screencap/Android.mk
Normal file
26
services/surfaceflinger/tests/screencap/Android.mk
Normal file
@@ -0,0 +1,26 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES:= \
|
||||
screencap.cpp
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libcutils \
|
||||
libutils \
|
||||
libbinder \
|
||||
libskia \
|
||||
libui \
|
||||
libsurfaceflinger_client
|
||||
|
||||
LOCAL_MODULE:= test-screencap
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
LOCAL_C_INCLUDES += \
|
||||
external/skia/include/core \
|
||||
external/skia/include/effects \
|
||||
external/skia/include/images \
|
||||
external/skia/src/ports \
|
||||
external/skia/include/utils
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
63
services/surfaceflinger/tests/screencap/screencap.cpp
Normal file
63
services/surfaceflinger/tests/screencap/screencap.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <binder/IPCThreadState.h>
|
||||
#include <binder/ProcessState.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
|
||||
#include <binder/IMemory.h>
|
||||
#include <surfaceflinger/ISurfaceComposer.h>
|
||||
|
||||
#include <SkImageEncoder.h>
|
||||
#include <SkBitmap.h>
|
||||
|
||||
using namespace android;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (argc != 2) {
|
||||
printf("usage: %s path\n", argv[0]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
const String16 name("SurfaceFlinger");
|
||||
sp<ISurfaceComposer> composer;
|
||||
getService(name, &composer);
|
||||
|
||||
sp<IMemoryHeap> heap;
|
||||
uint32_t w, h;
|
||||
PixelFormat f;
|
||||
status_t err = composer->captureScreen(0, &heap, &w, &h, &f);
|
||||
if (err != NO_ERROR) {
|
||||
fprintf(stderr, "screen capture failed: %s\n", strerror(-err));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
printf("screen capture success: w=%u, h=%u, pixels=%p\n",
|
||||
w, h, heap->getBase());
|
||||
|
||||
printf("saving file as PNG in %s ...\n", argv[1]);
|
||||
|
||||
SkBitmap b;
|
||||
b.setConfig(SkBitmap::kARGB_8888_Config, w, h);
|
||||
b.setPixels(heap->getBase());
|
||||
SkImageEncoder::EncodeFile(argv[1], b,
|
||||
SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user