diff options
author | Mathias Agopian <mathias@google.com> | 2010-09-24 18:02:10 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-09-24 18:02:10 -0700 |
commit | 8405dff685a804f39f1b7fccc79d28dac9b457c3 (patch) | |
tree | 82bbdeddb300c6de3f9f5bd147a402153ead3c25 | |
parent | 84b804678e54cdf8c84aa9f05c395457479662fe (diff) | |
parent | 06e7056660d65d4b0bbe1bb2cbf3e779074dbd9f (diff) | |
download | frameworks_native-8405dff685a804f39f1b7fccc79d28dac9b457c3.zip frameworks_native-8405dff685a804f39f1b7fccc79d28dac9b457c3.tar.gz frameworks_native-8405dff685a804f39f1b7fccc79d28dac9b457c3.tar.bz2 |
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
-rw-r--r-- | include/surfaceflinger/ISurfaceComposer.h | 10 | ||||
-rw-r--r-- | libs/surfaceflinger_client/ISurfaceComposer.cpp | 28 | ||||
-rw-r--r-- | services/surfaceflinger/GLExtensions.cpp | 4 | ||||
-rw-r--r-- | services/surfaceflinger/GLExtensions.h | 5 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 149 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlinger.h | 6 | ||||
-rw-r--r-- | services/surfaceflinger/tests/screencap/Android.mk | 26 | ||||
-rw-r--r-- | services/surfaceflinger/tests/screencap/screencap.cpp | 63 |
8 files changed, 290 insertions, 1 deletions
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h index dd44aa5..76307b2 100644 --- a/include/surfaceflinger/ISurfaceComposer.h +++ b/include/surfaceflinger/ISurfaceComposer.h @@ -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, diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp index 5c111f6..040060e 100644 --- a/libs/surfaceflinger_client/ISurfaceComposer.cpp +++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp @@ -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); } diff --git a/services/surfaceflinger/GLExtensions.cpp b/services/surfaceflinger/GLExtensions.cpp index 850866a..493122d 100644 --- a/services/surfaceflinger/GLExtensions.cpp +++ b/services/surfaceflinger/GLExtensions.cpp @@ -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 diff --git a/services/surfaceflinger/GLExtensions.h b/services/surfaceflinger/GLExtensions.h index bbb284e..c86c66a 100644 --- a/services/surfaceflinger/GLExtensions.h +++ b/services/surfaceflinger/GLExtensions.h @@ -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, diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index dc241d4..2b06f6f 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -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; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 8ecfc01..f09fdbc 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -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 diff --git a/services/surfaceflinger/tests/screencap/Android.mk b/services/surfaceflinger/tests/screencap/Android.mk new file mode 100644 index 0000000..1cfb471 --- /dev/null +++ b/services/surfaceflinger/tests/screencap/Android.mk @@ -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) diff --git a/services/surfaceflinger/tests/screencap/screencap.cpp b/services/surfaceflinger/tests/screencap/screencap.cpp new file mode 100644 index 0000000..9e893f4 --- /dev/null +++ b/services/surfaceflinger/tests/screencap/screencap.cpp @@ -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; +} |