blob: 8900d23a53cfb55df39d79987c84a21a77419d38 [file] [log] [blame]
/*
* 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 DisplayList 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.reset();
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 DisplayList startRecording() {
assertType(LAYER_TYPE_DISPLAY_LIST);
if (mDisplayList == null) {
mDisplayList = DisplayList.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;
}
}
}
}