/* * 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. */ package android.view; import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.SurfaceTexture; /** * A hardware layer can be used to render graphics operations into a hardware * friendly buffer. For instance, with an OpenGL backend a hardware layer * would use a Frame Buffer Object (FBO.) The hardware layer can be used as * a drawing cache when a complex set of graphics operations needs to be * drawn several times. * * @hide */ final class HardwareLayer { private static final int LAYER_TYPE_TEXTURE = 1; private static final int LAYER_TYPE_DISPLAY_LIST = 2; private HardwareRenderer mRenderer; private Finalizer mFinalizer; private RenderNode mDisplayList; private final int mLayerType; private HardwareLayer(HardwareRenderer renderer, long deferredUpdater, int type) { if (renderer == null || deferredUpdater == 0) { throw new IllegalArgumentException("Either hardware renderer: " + renderer + " or deferredUpdater: " + deferredUpdater + " is invalid"); } mRenderer = renderer; mLayerType = type; mFinalizer = new Finalizer(deferredUpdater); // Layer is considered initialized at this point, notify the HardwareRenderer mRenderer.onLayerCreated(this); } private void assertType(int type) { if (mLayerType != type) { throw new IllegalAccessError("Method not appropriate for this layer type! " + mLayerType); } } /** * Update the paint used when drawing this layer. * * @param paint The paint used when the layer is drawn into the destination canvas. * @see View#setLayerPaint(android.graphics.Paint) */ public void setLayerPaint(Paint paint) { nSetLayerPaint(mFinalizer.mDeferredUpdater, paint.mNativePaint); } /** * Indicates whether this layer can be rendered. * * @return True if the layer can be rendered into, false otherwise */ public boolean isValid() { return mFinalizer != null && mFinalizer.mDeferredUpdater != 0; } /** * Destroys resources without waiting for a GC. */ public void destroy() { if (!isValid()) { // Already destroyed return; } if (mDisplayList != null) { mDisplayList.destroyDisplayListData(); mDisplayList = null; } if (mRenderer != null) { mRenderer.onLayerDestroyed(this); mRenderer = null; } doDestroyLayerUpdater(); } public long getDeferredLayerUpdater() { return mFinalizer.mDeferredUpdater; } /** * Destroys the deferred layer updater but not the backing layer. The * backing layer is instead returned and is the caller's responsibility * to destroy/recycle as appropriate. * * It is safe to call this in onLayerDestroyed only */ public long detachBackingLayer() { long backingLayer = nDetachBackingLayer(mFinalizer.mDeferredUpdater); doDestroyLayerUpdater(); return backingLayer; } private void doDestroyLayerUpdater() { if (mFinalizer != null) { mFinalizer.destroy(); mFinalizer = null; } } public RenderNode startRecording() { assertType(LAYER_TYPE_DISPLAY_LIST); if (mDisplayList == null) { mDisplayList = RenderNode.create("HardwareLayer"); } return mDisplayList; } public void endRecording(Rect dirtyRect) { nUpdateRenderLayer(mFinalizer.mDeferredUpdater, mDisplayList.getNativeDisplayList(), dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom); mRenderer.pushLayerUpdate(this); } /** * Copies this layer into the specified bitmap. * * @param bitmap The bitmap to copy they layer into * * @return True if the copy was successful, false otherwise */ public boolean copyInto(Bitmap bitmap) { return mRenderer.copyLayerInto(this, bitmap); } /** * Update the layer's properties. Note that after calling this isValid() may * return false if the requested width/height cannot be satisfied * * @param width The new width of this layer * @param height The new height of this layer * @param isOpaque Whether this layer is opaque * * @return true if the layer's properties will change, false if they already * match the desired values. */ public boolean prepare(int width, int height, boolean isOpaque) { return nPrepare(mFinalizer.mDeferredUpdater, width, height, isOpaque); } /** * Sets an optional transform on this layer. * * @param matrix The transform to apply to the layer. */ public void setTransform(Matrix matrix) { nSetTransform(mFinalizer.mDeferredUpdater, matrix.native_instance); } /** * Indicates that this layer has lost its texture. */ public void detachSurfaceTexture(final SurfaceTexture surface) { assertType(LAYER_TYPE_TEXTURE); mRenderer.safelyRun(new Runnable() { @Override public void run() { surface.detachFromGLContext(); // SurfaceTexture owns the texture name and detachFromGLContext // should have deleted it nOnTextureDestroyed(mFinalizer.mDeferredUpdater); } }); } /** * This exists to minimize impact into the current HardwareLayer paths as * some of the specifics of how to handle error cases in the fully * deferred model will work */ @Deprecated public void flushChanges() { if (HardwareRenderer.sUseRenderThread) { // Not supported, don't try. return; } boolean success = nFlushChanges(mFinalizer.mDeferredUpdater); if (!success) { destroy(); } } public long getLayer() { return nGetLayer(mFinalizer.mDeferredUpdater); } public void setSurfaceTexture(SurfaceTexture surface) { assertType(LAYER_TYPE_TEXTURE); nSetSurfaceTexture(mFinalizer.mDeferredUpdater, surface, false); } public void updateSurfaceTexture() { assertType(LAYER_TYPE_TEXTURE); nUpdateSurfaceTexture(mFinalizer.mDeferredUpdater); } /** * This should only be used by HardwareRenderer! Do not call directly */ SurfaceTexture createSurfaceTexture() { assertType(LAYER_TYPE_TEXTURE); SurfaceTexture st = new SurfaceTexture(nGetTexName(mFinalizer.mDeferredUpdater)); nSetSurfaceTexture(mFinalizer.mDeferredUpdater, st, true); return st; } /** * This should only be used by HardwareRenderer! Do not call directly */ static HardwareLayer createTextureLayer(HardwareRenderer renderer) { return new HardwareLayer(renderer, nCreateTextureLayer(), LAYER_TYPE_TEXTURE); } static HardwareLayer adoptTextureLayer(HardwareRenderer renderer, long layer) { return new HardwareLayer(renderer, layer, LAYER_TYPE_TEXTURE); } /** * This should only be used by HardwareRenderer! Do not call directly */ static HardwareLayer createDisplayListLayer(HardwareRenderer renderer, int width, int height) { return new HardwareLayer(renderer, nCreateRenderLayer(width, height), LAYER_TYPE_DISPLAY_LIST); } static HardwareLayer adoptDisplayListLayer(HardwareRenderer renderer, long layer) { return new HardwareLayer(renderer, layer, LAYER_TYPE_DISPLAY_LIST); } /** This also creates the underlying layer */ private static native long nCreateTextureLayer(); private static native long nCreateRenderLayer(int width, int height); private static native void nOnTextureDestroyed(long layerUpdater); private static native long nDetachBackingLayer(long layerUpdater); /** This also destroys the underlying layer if it is still attached. * Note it does not recycle the underlying layer, but instead queues it * for deferred deletion. * The HardwareRenderer should use detachBackingLayer() in the * onLayerDestroyed() callback to do recycling if desired. */ private static native void nDestroyLayerUpdater(long layerUpdater); private static native boolean nPrepare(long layerUpdater, int width, int height, boolean isOpaque); private static native void nSetLayerPaint(long layerUpdater, long paint); private static native void nSetTransform(long layerUpdater, long matrix); private static native void nSetSurfaceTexture(long layerUpdater, SurfaceTexture surface, boolean isAlreadyAttached); private static native void nUpdateSurfaceTexture(long layerUpdater); private static native void nUpdateRenderLayer(long layerUpdater, long displayList, int left, int top, int right, int bottom); private static native boolean nFlushChanges(long layerUpdater); private static native long nGetLayer(long layerUpdater); private static native int nGetTexName(long layerUpdater); private static class Finalizer { private long mDeferredUpdater; public Finalizer(long deferredUpdater) { mDeferredUpdater = deferredUpdater; } @Override protected void finalize() throws Throwable { try { destroy(); } finally { super.finalize(); } } void destroy() { if (mDeferredUpdater != 0) { nDestroyLayerUpdater(mDeferredUpdater); mDeferredUpdater = 0; } } } }