From 0ab84ef507f23505a72798fbe25cf4bb2c507ea3 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 13 Oct 2011 16:02:48 -0700 Subject: Add a LayerScreenshot A LayerScreenshot is a special type of layer that contains a screenshot of the screen acquired when its created. It works just like LayerDim. Make sure to call compositionComplete() after rendering into a FBO. Bug: 5446982, 5467587, 5466259 Change-Id: I5d8a1b4c327f9973d950cd4f4c0bca7f62825cd4 --- core/java/android/view/Surface.java | 3 + include/surfaceflinger/ISurfaceComposer.h | 1 + .../android/server/wm/ScreenRotationAnimation.java | 50 +++------ services/surfaceflinger/Android.mk | 1 + services/surfaceflinger/Layer.cpp | 12 +-- services/surfaceflinger/LayerScreenshot.cpp | 114 +++++++++++++++++++++ services/surfaceflinger/LayerScreenshot.h | 56 ++++++++++ services/surfaceflinger/SurfaceFlinger.cpp | 38 +++++-- services/surfaceflinger/SurfaceFlinger.h | 33 ++++-- 9 files changed, 244 insertions(+), 64 deletions(-) create mode 100644 services/surfaceflinger/LayerScreenshot.cpp create mode 100644 services/surfaceflinger/LayerScreenshot.h diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 64d3d31..2b254af 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -194,6 +194,9 @@ public class Surface implements Parcelable { */ public static final int FX_SURFACE_DIM = 0x00020000; + /** @hide */ + public static final int FX_SURFACE_SCREENSHOT = 0x00030000; + /** Mask used for FX values above @hide */ public static final int FX_SURFACE_MASK = 0x000F0000; diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h index ea022a6..e7a33f1 100644 --- a/include/surfaceflinger/ISurfaceComposer.h +++ b/include/surfaceflinger/ISurfaceComposer.h @@ -53,6 +53,7 @@ public: eFXSurfaceNormal = 0x00000000, eFXSurfaceBlur = 0x00010000, eFXSurfaceDim = 0x00020000, + eFXSurfaceScreenshot= 0x00030000, eFXSurfaceMask = 0x000F0000, }; diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java index 91576e7..131f11c 100644 --- a/services/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java @@ -64,17 +64,16 @@ class ScreenRotationAnimation { boolean inTransaction, int originalWidth, int originalHeight, int originalRotation) { mContext = context; - Bitmap screenshot = Surface.screenshot(0, 0); - - if (screenshot == null) { - // Device is not capable of screenshots... we can't do an animation. - return; - } - // Screenshot does NOT include rotation! mSnapshotRotation = 0; - mWidth = screenshot.getWidth(); - mHeight = screenshot.getHeight(); + if (originalRotation == Surface.ROTATION_90 + || originalRotation == Surface.ROTATION_270) { + mWidth = originalHeight; + mHeight = originalWidth; + } else { + mWidth = originalWidth; + mHeight = originalHeight; + } mOriginalRotation = originalRotation; mOriginalWidth = originalWidth; @@ -89,7 +88,12 @@ class ScreenRotationAnimation { try { try { mSurface = new Surface(session, 0, "FreezeSurface", - -1, mWidth, mHeight, PixelFormat.OPAQUE, 0); + -1, mWidth, mHeight, PixelFormat.OPAQUE, Surface.FX_SURFACE_SCREENSHOT); + if (mSurface == null || !mSurface.isValid()) { + // Screenshot failed, punt. + mSurface = null; + return; + } mSurface.setLayer(FREEZE_LAYER + 1); } catch (Surface.OutOfResourcesException e) { Slog.w(TAG, "Unable to allocate freeze surface", e); @@ -100,38 +104,12 @@ class ScreenRotationAnimation { " FREEZE " + mSurface + ": CREATE"); setRotation(originalRotation); - - if (mSurface != null) { - Rect dirty = new Rect(0, 0, mWidth, mHeight); - Canvas c = null; - try { - c = mSurface.lockCanvas(dirty); - } catch (IllegalArgumentException e) { - Slog.w(TAG, "Unable to lock surface", e); - } catch (Surface.OutOfResourcesException e) { - Slog.w(TAG, "Unable to lock surface", e); - } - if (c == null) { - Slog.w(TAG, "Null surface canvas"); - mSurface.destroy(); - mSurface = null; - return; - } - - Paint paint = new Paint(0); - paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); - c.drawBitmap(screenshot, 0, 0, paint); - - mSurface.unlockCanvasAndPost(c); - } } finally { if (!inTransaction) { Surface.closeTransaction(); if (WindowManagerService.SHOW_LIGHT_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "<<< CLOSE TRANSACTION ScreenRotationAnimation"); } - - screenshot.recycle(); } } diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index dab0705..61a8358 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -5,6 +5,7 @@ LOCAL_SRC_FILES:= \ Layer.cpp \ LayerBase.cpp \ LayerDim.cpp \ + LayerScreenshot.cpp \ DdmConnection.cpp \ DisplayHardware/DisplayHardware.cpp \ DisplayHardware/DisplayHardwareBase.cpp \ diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 34a0d73..f885fc7 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -88,16 +88,8 @@ void Layer::onFirstRef() Layer::~Layer() { - class MessageDestroyGLState : public MessageBase { - GLuint texture; - public: - MessageDestroyGLState(GLuint texture) : texture(texture) { } - virtual bool handler() { - glDeleteTextures(1, &texture); - return true; - } - }; - mFlinger->postMessageAsync( new MessageDestroyGLState(mTextureName) ); + mFlinger->postMessageAsync( + new SurfaceFlinger::MessageDestroyGLTexture(mTextureName) ); } void Layer::onFrameQueued() { diff --git a/services/surfaceflinger/LayerScreenshot.cpp b/services/surfaceflinger/LayerScreenshot.cpp new file mode 100644 index 0000000..e30ccbf --- /dev/null +++ b/services/surfaceflinger/LayerScreenshot.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2011 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 +#include +#include + +#include +#include + +#include + +#include "LayerScreenshot.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" + +namespace android { +// --------------------------------------------------------------------------- + +LayerScreenshot::LayerScreenshot(SurfaceFlinger* flinger, DisplayID display, + const sp& client) + : LayerBaseClient(flinger, display, client), + mTextureName(0), mFlinger(flinger) +{ +} + +LayerScreenshot::~LayerScreenshot() +{ + if (mTextureName) { + mFlinger->postMessageAsync( + new SurfaceFlinger::MessageDestroyGLTexture(mTextureName) ); + } +} + +status_t LayerScreenshot::capture() { + GLfloat u, v; + status_t result = mFlinger->renderScreenToTexture(0, &mTextureName, &u, &v); + if (result != NO_ERROR) { + return result; + } + + glBindTexture(GL_TEXTURE_2D, mTextureName); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + mTexCoords[0] = 0; mTexCoords[1] = v; + mTexCoords[2] = 0; mTexCoords[3] = 0; + mTexCoords[4] = u; mTexCoords[5] = 0; + mTexCoords[6] = u; mTexCoords[7] = v; + + return NO_ERROR; +} + +void LayerScreenshot::onDraw(const Region& clip) const +{ + const State& s(drawingState()); + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); + if (s.alpha>0 && (it != end)) { + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const GLfloat alpha = s.alpha/255.0f; + const uint32_t fbHeight = hw.getHeight(); + + if (s.alpha == 0xFF) { + glDisable(GL_BLEND); + } else { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + glColor4f(0, 0, 0, alpha); + + glDisable(GL_TEXTURE_EXTERNAL_OES); + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, mTextureName); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 0, mTexCoords); + glVertexPointer(2, GL_FLOAT, 0, mVertices); + + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/services/surfaceflinger/LayerScreenshot.h b/services/surfaceflinger/LayerScreenshot.h new file mode 100644 index 0000000..e3a2b19 --- /dev/null +++ b/services/surfaceflinger/LayerScreenshot.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2011 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. + */ + +#ifndef ANDROID_LAYER_SCREENSHOT_H +#define ANDROID_LAYER_SCREENSHOT_H + +#include +#include + +#include +#include + +#include "LayerBase.h" + +// --------------------------------------------------------------------------- + +namespace android { + +class LayerScreenshot : public LayerBaseClient +{ + GLuint mTextureName; + GLfloat mTexCoords[8]; + sp mFlinger; +public: + LayerScreenshot(SurfaceFlinger* flinger, DisplayID display, + const sp& client); + virtual ~LayerScreenshot(); + + status_t capture(); + + virtual void onDraw(const Region& clip) const; + virtual bool isOpaque() const { return false; } + virtual bool isSecure() const { return false; } + virtual bool isProtectedByApp() const { return false; } + virtual bool isProtectedByDRM() const { return false; } + virtual const char* getTypeId() const { return "LayerScreenshot"; } +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_SCREENSHOT_H diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 595ec1e..ad1995d 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -50,6 +50,7 @@ #include "DdmConnection.h" #include "Layer.h" #include "LayerDim.h" +#include "LayerScreenshot.h" #include "SurfaceFlinger.h" #include "DisplayHardware/DisplayHardware.h" @@ -1358,6 +1359,9 @@ sp SurfaceFlinger::createSurface( case eFXSurfaceDim: layer = createDimSurface(client, d, w, h, flags); break; + case eFXSurfaceScreenshot: + layer = createScreenshotSurface(client, d, w, h, flags); + break; } if (layer != 0) { @@ -1420,7 +1424,19 @@ sp SurfaceFlinger::createDimSurface( uint32_t w, uint32_t h, uint32_t flags) { sp layer = new LayerDim(this, display, client); - layer->initStates(w, h, flags); + return layer; +} + +sp SurfaceFlinger::createScreenshotSurface( + const sp& client, DisplayID display, + uint32_t w, uint32_t h, uint32_t flags) +{ + sp layer = new LayerScreenshot(this, display, client); + status_t err = layer->capture(); + if (err != NO_ERROR) { + layer.clear(); + LOGW("createScreenshotSurface failed (%s)", strerror(-err)); + } return layer; } @@ -1783,6 +1799,13 @@ void SurfaceFlinger::repaintEverything() { // --------------------------------------------------------------------------- +status_t SurfaceFlinger::renderScreenToTexture(DisplayID dpy, + GLuint* textureName, GLfloat* uOut, GLfloat* vOut) +{ + Mutex::Autolock _l(mStateLock); + return renderScreenToTextureLocked(dpy, textureName, uOut, vOut); +} + status_t SurfaceFlinger::renderScreenToTextureLocked(DisplayID dpy, GLuint* textureName, GLfloat* uOut, GLfloat* vOut) { @@ -1833,6 +1856,8 @@ status_t SurfaceFlinger::renderScreenToTextureLocked(DisplayID dpy, layer->drawForSreenShot(); } + hw.compositionComplete(); + // back to main framebuffer glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0); glDisable(GL_SCISSOR_TEST); @@ -1848,11 +1873,6 @@ status_t SurfaceFlinger::renderScreenToTextureLocked(DisplayID dpy, status_t SurfaceFlinger::electronBeamOffAnimationImplLocked() { - status_t result = PERMISSION_DENIED; - - if (!GLExtensions::getInstance().haveFramebufferObject()) - return INVALID_OPERATION; - // get screen geometry const DisplayHardware& hw(graphicPlane(0).displayHardware()); const uint32_t hw_w = hw.getWidth(); @@ -1861,7 +1881,7 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked() GLfloat u, v; GLuint tname; - result = renderScreenToTextureLocked(0, &tname, &u, &v); + status_t result = renderScreenToTextureLocked(0, &tname, &u, &v); if (result != NO_ERROR) { return result; } @@ -2038,10 +2058,6 @@ status_t SurfaceFlinger::electronBeamOnAnimationImplLocked() return result; } - // back to main framebuffer - glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0); - glDisable(GL_SCISSOR_TEST); - GLfloat vtx[8]; const GLfloat texCoords[4][2] = { {0,v}, {0,0}, {u,0}, {u,v} }; glBindTexture(GL_TEXTURE_2D, tname); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 0e642c1..3c8f4e5 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -49,6 +49,7 @@ class DisplayHardware; class FreezeLock; class Layer; class LayerDim; +class LayerScreenshot; struct surface_flinger_cblk_t; #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) @@ -186,6 +187,15 @@ public: void screenReleased(DisplayID dpy); void screenAcquired(DisplayID dpy); + status_t renderScreenToTexture(DisplayID dpy, + GLuint* textureName, GLfloat* uOut, GLfloat* vOut); + + status_t postMessageAsync(const sp& msg, + nsecs_t reltime=0, uint32_t flags = 0); + + status_t postMessageSync(const sp& msg, + nsecs_t reltime=0, uint32_t flags = 0); + status_t removeLayer(const sp& layer); status_t addLayer(const sp& layer); status_t invalidateLayerVisibility(const sp& layer); @@ -195,6 +205,18 @@ public: GLuint getProtectedTexName() const { return mProtectedTexName; } + + class MessageDestroyGLTexture : public MessageBase { + GLuint texture; + public: + MessageDestroyGLTexture(GLuint texture) : texture(texture) { } + virtual bool handler() { + glDeleteTextures(1, &texture); + return true; + } + }; + + private: // DeathRecipient interface virtual void binderDied(const wp& who); @@ -204,7 +226,6 @@ private: friend class LayerBase; friend class LayerBaseClient; friend class Layer; - friend class LayerDim; sp createSurface( ISurfaceComposerClient::surface_data_t* params, @@ -222,6 +243,10 @@ private: const sp& client, DisplayID display, uint32_t w, uint32_t h, uint32_t flags); + sp createScreenshotSurface( + const sp& client, DisplayID display, + uint32_t w, uint32_t h, uint32_t flags); + status_t removeSurface(const sp& client, SurfaceID sid); status_t destroySurface(const wp& layer); uint32_t setClientStateLocked(const sp& client, const layer_state_t& s); @@ -329,12 +354,6 @@ private: mutable MessageQueue mEventQueue; - status_t postMessageAsync(const sp& msg, - nsecs_t reltime=0, uint32_t flags = 0); - - status_t postMessageSync(const sp& msg, - nsecs_t reltime=0, uint32_t flags = 0); - // access must be protected by mStateLock mutable Mutex mStateLock; State mCurrentState; -- cgit v1.1