diff options
-rw-r--r-- | api/current.txt | 4 | ||||
-rw-r--r-- | core/java/android/view/GLES20Canvas.java | 1 | ||||
-rw-r--r-- | core/java/android/view/GLES20Layer.java | 7 | ||||
-rw-r--r-- | core/java/android/view/HardwareRenderer.java | 20 | ||||
-rw-r--r-- | core/java/android/view/TextureView.java | 94 | ||||
-rw-r--r-- | core/jni/android_view_GLES20Canvas.cpp | 6 | ||||
-rw-r--r-- | libs/hwui/LayerRenderer.cpp | 90 | ||||
-rw-r--r-- | libs/hwui/LayerRenderer.h | 3 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.h | 16 | ||||
-rw-r--r-- | tests/HwAccelerationTest/AndroidManifest.xml | 1 |
10 files changed, 229 insertions, 13 deletions
diff --git a/api/current.txt b/api/current.txt index 3724d16..7c9c851 100644 --- a/api/current.txt +++ b/api/current.txt @@ -21566,8 +21566,12 @@ package android.view { ctor public TextureView(android.content.Context, android.util.AttributeSet); ctor public TextureView(android.content.Context, android.util.AttributeSet, int); method public final void draw(android.graphics.Canvas); + method public android.graphics.Bitmap getBitmap(); + method public android.graphics.Bitmap getBitmap(int, int); + method public android.graphics.Bitmap getBitmap(android.graphics.Bitmap); method public android.graphics.SurfaceTexture getSurfaceTexture(); method public android.view.TextureView.SurfaceTextureListener getSurfaceTextureListener(); + method public boolean isAvailable(); method protected final void onDraw(android.graphics.Canvas); method public void setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener); } diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index d5cad96..383bfb3 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -166,6 +166,7 @@ class GLES20Canvas extends HardwareCanvas { static native void nUpdateTextureLayer(int layerId, int width, int height, int surface); static native void nDestroyLayer(int layerId); static native void nDestroyLayerDeferred(int layerId); + static native boolean nCopyLayer(int layerId, int bitmap); /////////////////////////////////////////////////////////////////////////// // Canvas management diff --git a/core/java/android/view/GLES20Layer.java b/core/java/android/view/GLES20Layer.java index bc191a6..69dfc2b 100644 --- a/core/java/android/view/GLES20Layer.java +++ b/core/java/android/view/GLES20Layer.java @@ -17,6 +17,8 @@ package android.view; +import android.graphics.Bitmap; + /** * An OpenGL ES 2.0 implementation of {@link HardwareLayer}. */ @@ -40,7 +42,10 @@ abstract class GLES20Layer extends HardwareLayer { return mLayer; } - + boolean copyInto(Bitmap bitmap) { + return GLES20Canvas.nCopyLayer(mLayer, bitmap.mNativeBitmap); + } + @Override void destroy() { if (mFinalizer != null) mFinalizer.destroy(); diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 2611ec0..5944bd4 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -17,6 +17,7 @@ package android.view; +import android.graphics.Bitmap; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.SurfaceTexture; @@ -204,8 +205,18 @@ public abstract class HardwareRenderer { * @param surface The surface to update */ abstract void updateTextureLayer(HardwareLayer layer, int width, int height, - SurfaceTexture surface); - + SurfaceTexture surface); + + /** + * Copies the content of the specified layer into the specified bitmap. + * + * @param layer The hardware layer to copy + * @param bitmap The bitmap to copy the layer into + * + * @return True if the copy was successful, false otherwise + */ + abstract boolean copyLayer(HardwareLayer layer, Bitmap bitmap); + /** * Initializes the hardware renderer for the specified surface and setup the * renderer for drawing, if needed. This is invoked when the ViewAncestor has @@ -814,6 +825,11 @@ public abstract class HardwareRenderer { ((GLES20TextureLayer) layer).update(width, height, surface.mSurfaceTexture); } + @Override + boolean copyLayer(HardwareLayer layer, Bitmap bitmap) { + return ((GLES20Layer) layer).copyInto(bitmap); + } + static HardwareRenderer create(boolean translucent) { if (GLES20Canvas.isAvailable()) { return new Gl20Renderer(translucent); diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java index bc1ad3c..4daa892 100644 --- a/core/java/android/view/TextureView.java +++ b/core/java/android/view/TextureView.java @@ -17,6 +17,7 @@ package android.view; import android.content.Context; +import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.SurfaceTexture; @@ -89,6 +90,8 @@ import android.util.Log; * @see SurfaceTexture */ public class TextureView extends View { + private static final String LOG_TAG = "TextureView"; + private HardwareLayer mLayer; private SurfaceTexture mSurface; private SurfaceTextureListener mListener; @@ -148,7 +151,7 @@ public class TextureView extends View { super.onAttachedToWindow(); if (!isHardwareAccelerated()) { - Log.w("TextureView", "A TextureView or a subclass can only be " + Log.w(LOG_TAG, "A TextureView or a subclass can only be " + "used with hardware acceleration enabled."); } } @@ -293,8 +296,95 @@ public class TextureView extends View { } /** + * <p>Returns a {@link android.graphics.Bitmap} representation of the content + * of the associated surface texture. If the surface texture is not available, + * this method returns null.</p> + * + * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888} + * pixel format and its dimensions are the same as this view's.</p> + * + * <p><strong>Do not</strong> invoke this method from a drawing method + * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> + * + * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface + * texture is not available or the width <= 0 or the height <= 0 + * + * @see #isAvailable() + * @see #getBitmap(android.graphics.Bitmap) + * @see #getBitmap(int, int) + */ + public Bitmap getBitmap() { + return getBitmap(getWidth(), getHeight()); + } + + /** + * <p>Returns a {@link android.graphics.Bitmap} representation of the content + * of the associated surface texture. If the surface texture is not available, + * this method returns null.</p> + * + * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888} + * pixel format.</p> + * + * <p><strong>Do not</strong> invoke this method from a drawing method + * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> + * + * @param width The width of the bitmap to create + * @param height The height of the bitmap to create + * + * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface + * texture is not available or width is <= 0 or height is <= 0 + * + * @see #isAvailable() + * @see #getBitmap(android.graphics.Bitmap) + * @see #getBitmap() + */ + public Bitmap getBitmap(int width, int height) { + if (isAvailable() && width > 0 && height > 0) { + return getBitmap(Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)); + } + return null; + } + + /** + * <p>Copies the content of this view's surface texture into the specified + * bitmap. If the surface texture is not available, the copy is not executed. + * The content of the surface texture will be scaled to fit exactly inside + * the specified bitmap.</p> + * + * <p><strong>Do not</strong> invoke this method from a drawing method + * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> + * + * @param bitmap The bitmap to copy the content of the surface texture into, + * cannot be null, all configurations are supported + * + * @return The bitmap specified as a parameter + * + * @see #isAvailable() + * @see #getBitmap(int, int) + * @see #getBitmap() + */ + public Bitmap getBitmap(Bitmap bitmap) { + if (bitmap != null && isAvailable()) { + mAttachInfo.mHardwareRenderer.copyLayer(mLayer, bitmap); + } + return bitmap; + } + + /** + * Returns true if the {@link SurfaceTexture} associated with this + * TextureView is available for rendering. When this method returns + * true, {@link #getSurfaceTexture()} returns a valid surface texture. + */ + public boolean isAvailable() { + return mSurface != null; + } + + /** * Returns the {@link SurfaceTexture} used by this view. This method - * may return null if the view is not attached to a window. + * may return null if the view is not attached to a window or if the surface + * texture has not been initialized yet. + * + * @see #isAvailable() */ public SurfaceTexture getSurfaceTexture() { return mSurface; diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 57a97bd..7e82efb 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -667,6 +667,11 @@ static void android_view_GLES20Canvas_drawLayer(JNIEnv* env, jobject clazz, renderer->drawLayer(layer, x, y, paint); } +static jboolean android_view_GLES20Canvas_copyLayer(JNIEnv* env, jobject clazz, + Layer* layer, SkBitmap* bitmap) { + return LayerRenderer::copyLayer(layer, bitmap); +} + #endif // USE_OPENGL_RENDERER // ---------------------------------------------------------------------------- @@ -792,6 +797,7 @@ static JNINativeMethod gMethods[] = { { "nDestroyLayer", "(I)V", (void*) android_view_GLES20Canvas_destroyLayer }, { "nDestroyLayerDeferred", "(I)V", (void*) android_view_GLES20Canvas_destroyLayerDeferred }, { "nDrawLayer", "(IIFFI)V", (void*) android_view_GLES20Canvas_drawLayer }, + { "nCopyLayer", "(II)Z", (void*) android_view_GLES20Canvas_copyLayer }, #endif }; diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index f316ba7..146e789 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -315,5 +315,95 @@ void LayerRenderer::destroyLayerDeferred(Layer* layer) { } } +bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { + Caches& caches = Caches::getInstance(); + if (layer && layer->isTextureLayer && bitmap->width() <= caches.maxTextureSize && + bitmap->height() <= caches.maxTextureSize) { + + GLuint fbo = caches.fboCache.get(); + if (!fbo) { + LOGW("Could not obtain an FBO"); + return false; + } + + GLuint texture; + GLuint previousFbo; + + GLenum format; + GLenum type; + + switch (bitmap->config()) { + case SkBitmap::kA8_Config: + format = GL_ALPHA; + type = GL_UNSIGNED_BYTE; + break; + case SkBitmap::kRGB_565_Config: + format = GL_RGB; + type = GL_UNSIGNED_SHORT_5_6_5; + break; + case SkBitmap::kARGB_4444_Config: + format = GL_RGBA; + type = GL_UNSIGNED_SHORT_4_4_4_4; + break; + case SkBitmap::kARGB_8888_Config: + default: + format = GL_RGBA; + type = GL_UNSIGNED_BYTE; + break; + } + + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + glGenTextures(1, &texture); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexImage2D(GL_TEXTURE_2D, 0, format, bitmap->width(), bitmap->height(), + 0, format, type, NULL); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, texture, 0); + + glBindTexture(GL_TEXTURE_2D, layer->texture); + + float alpha = layer->alpha; + SkXfermode::Mode mode = layer->mode; + + layer->mode = SkXfermode::kSrc_Mode; + layer->alpha = 255; + layer->fbo = fbo; + + LayerRenderer renderer(layer); + renderer.setViewport(bitmap->width(), bitmap->height()); + renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f, + bitmap->width(), bitmap->height(), !layer->blend); + + Rect bounds; + bounds.set(0.0f, 0.0f, bitmap->width(), bitmap->height()); + renderer.drawTextureLayer(layer, bounds); + + SkAutoLockPixels alp(*bitmap); + glReadPixels(0, 0, bitmap->width(), bitmap->height(), format, type, bitmap->getPixels()); + + glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + + layer->mode = mode; + layer->alpha = alpha; + layer->fbo = 0; + glDeleteTextures(1, &texture); + caches.fboCache.put(fbo); + + return true; + } + return false; +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h index 59cab96..797dfc6 100644 --- a/libs/hwui/LayerRenderer.h +++ b/libs/hwui/LayerRenderer.h @@ -20,6 +20,8 @@ #include "OpenGLRenderer.h" #include "Layer.h" +#include <SkBitmap.h> + namespace android { namespace uirenderer { @@ -60,6 +62,7 @@ public: GLenum renderTarget, float* transform); static void destroyLayer(Layer* layer); static void destroyLayerDeferred(Layer* layer); + static bool copyLayer(Layer* layer, SkBitmap* bitmap); private: void generateMesh(); diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index b9e3ddc..0a3d5090 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -178,6 +178,14 @@ protected: return 0; } + /** + * Renders the specified layer as a textured quad. + * + * @param layer The layer to render + * @param rect The bounds of the layer + */ + void drawTextureLayer(Layer* layer, const Rect& rect); + private: /** * Saves the current state of the renderer as a new snapshot. @@ -256,14 +264,6 @@ private: void clearLayerRegions(); /** - * Renders the specified layer as a textured quad. - * - * @param layer The layer to render - * @param rect The bounds of the layer - */ - void drawTextureLayer(Layer* layer, const Rect& rect); - - /** * Mark the layer as dirty at the specified coordinates. The coordinates * are transformed with the supplied matrix. */ diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 01d30eb..3e7ca08 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -19,6 +19,7 @@ <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.CAMERA" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> |