Better backend for hardware layers.
With this new backend, a hardware layer is only recreated when
its associated view is udpated. This offers fast composition
in GL and fast update of the layer in GL as well.
Change-Id: I97c43a612f5955c6bf1c192c8ca4af10fdf1d076
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 04a521e..8c4e654 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -73,9 +73,21 @@
// Constructors
///////////////////////////////////////////////////////////////////////////
+ /**
+ * Creates a canvas to render directly on screen.
+ */
GLES20Canvas(boolean translucent) {
this(false, translucent);
}
+
+ /**
+ * Creates a canvas to render into an FBO.
+ */
+ GLES20Canvas(int fbo, boolean translucent) {
+ mOpaque = !translucent;
+ mRenderer = nCreateLayerRenderer(fbo);
+ setupFinalizer();
+ }
protected GLES20Canvas(boolean record, boolean translucent) {
mOpaque = !translucent;
@@ -89,7 +101,11 @@
} else {
mRenderer = nCreateRenderer();
}
-
+
+ setupFinalizer();
+ }
+
+ private void setupFinalizer() {
if (mRenderer == 0) {
throw new IllegalStateException("Could not create GLES20Canvas renderer");
} else {
@@ -97,7 +113,8 @@
}
}
- private native int nCreateRenderer();
+ private static native int nCreateRenderer();
+ private static native int nCreateLayerRenderer(int fbo);
private static native int nGetDisplayListRenderer(int renderer);
private static native void nDestroyRenderer(int renderer);
@@ -136,6 +153,15 @@
}
///////////////////////////////////////////////////////////////////////////
+ // Hardware layers
+ ///////////////////////////////////////////////////////////////////////////
+
+ static native int nCreateLayer(int width, int height, int[] layerInfo);
+ static native void nResizeLayer(int layerId, int layerTextureId, int width, int height,
+ int[] layerInfo);
+ static native void nDestroyLayer(int layerId, int layerTextureId);
+
+ ///////////////////////////////////////////////////////////////////////////
// Canvas management
///////////////////////////////////////////////////////////////////////////
@@ -227,6 +253,34 @@
private native void nDrawDisplayList(int renderer, int displayList);
///////////////////////////////////////////////////////////////////////////
+ // Hardware layer
+ ///////////////////////////////////////////////////////////////////////////
+
+ void drawHardwareLayer(float left, float top, float right, float bottom,
+ HardwareLayer layer, Paint paint) {
+ final GLES20Layer glLayer = (GLES20Layer) layer;
+ boolean hasColorFilter = paint != null && setupColorFilter(paint);
+ final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+ nDrawLayer(mRenderer, left, top, right, bottom, glLayer.mLayerTextureId,
+ glLayer.getU(), glLayer.getV(), nativePaint);
+ if (hasColorFilter) nResetModifiers(mRenderer);
+ }
+
+ private native void nDrawLayer(int renderer, float left, float top, float right, float bottom,
+ int layerTexture, float u, float v, int paint);
+
+ void interrupt() {
+ nInterrupt(mRenderer);
+ }
+
+ void resume() {
+ nResume(mRenderer);
+ }
+
+ private native void nInterrupt(int renderer);
+ private native void nResume(int renderer);
+
+ ///////////////////////////////////////////////////////////////////////////
// Clipping
///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
index c548659..e813bc9 100644
--- a/core/java/android/view/GLES20DisplayList.java
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -106,7 +106,11 @@
@Override
protected void finalize() throws Throwable {
- replaceNativeObject(0);
+ try {
+ replaceNativeObject(0);
+ } finally {
+ super.finalize();
+ }
}
}
}
diff --git a/core/java/android/view/GLES20Layer.java b/core/java/android/view/GLES20Layer.java
new file mode 100644
index 0000000..336e07a
--- /dev/null
+++ b/core/java/android/view/GLES20Layer.java
@@ -0,0 +1,143 @@
+/*
+ * 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.Canvas;
+
+/**
+ * An OpenGL ES 2.0 implementation of {@link HardwareLayer}.
+ */
+class GLES20Layer extends HardwareLayer {
+ private int mLayerId;
+ int mLayerTextureId;
+
+ private int mLayerWidth;
+ private int mLayerHeight;
+
+ private final GLES20Canvas mCanvas;
+
+ private float mU;
+ private float mV;
+
+ @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
+ private final Finalizer mFinalizer;
+
+ GLES20Layer(int width, int height, boolean isOpaque) {
+ super(width, height, isOpaque);
+
+ int[] layerInfo = new int[3];
+ mLayerId = GLES20Canvas.nCreateLayer(width, height, layerInfo);
+ if (mLayerId != 0) {
+ mLayerWidth = layerInfo[0];
+ mLayerHeight = layerInfo[1];
+ mLayerTextureId = layerInfo[2];
+
+ mCanvas = new GLES20Canvas(mLayerId, !isOpaque);
+ mFinalizer = new Finalizer(mLayerId, mLayerTextureId);
+
+ mU = mWidth / (float) mLayerWidth;
+ mV = mHeight/ (float) mLayerHeight;
+ } else {
+ mCanvas = null;
+ mFinalizer = null;
+ }
+ }
+
+ float getU() {
+ return mU;
+ }
+
+ float getV() {
+ return mV;
+ }
+
+ @Override
+ boolean isValid() {
+ return mLayerId != 0 && mLayerWidth > 0 && mLayerHeight > 0;
+ }
+
+ @Override
+ void resize(int width, int height) {
+ if (!isValid() || width <= 0 || height <= 0) return;
+ if (width > mLayerWidth || height > mLayerHeight) {
+ mWidth = width;
+ mHeight = height;
+
+ int[] layerInfo = new int[3];
+
+ GLES20Canvas.nResizeLayer(mLayerId, mLayerTextureId, width, height, layerInfo);
+
+ mLayerWidth = layerInfo[0];
+ mLayerHeight = layerInfo[1];
+
+ mU = mWidth / (float) mLayerWidth;
+ mV = mHeight/ (float) mLayerHeight;
+ }
+ }
+
+ @Override
+ HardwareCanvas getCanvas() {
+ return mCanvas;
+ }
+
+ @Override
+ void end(Canvas currentCanvas) {
+ if (currentCanvas instanceof GLES20Canvas) {
+ ((GLES20Canvas) currentCanvas).resume();
+ }
+ }
+
+ @Override
+ HardwareCanvas start(Canvas currentCanvas) {
+ if (currentCanvas instanceof GLES20Canvas) {
+ ((GLES20Canvas) currentCanvas).interrupt();
+ }
+ return getCanvas();
+ }
+
+ @Override
+ void destroy() {
+ mFinalizer.destroy();
+ mLayerId = mLayerTextureId = 0;
+ }
+
+ private static class Finalizer {
+ private int mLayerId;
+ private int mLayerTextureId;
+
+ public Finalizer(int layerId, int layerTextureId) {
+ mLayerId = layerId;
+ mLayerTextureId = layerTextureId;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mLayerId != 0 || mLayerTextureId != 0) {
+ GLES20Canvas.nDestroyLayer(mLayerId, mLayerTextureId);
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ void destroy() {
+ GLES20Canvas.nDestroyLayer(mLayerId, mLayerTextureId);
+ mLayerId = mLayerTextureId = 0;
+ }
+ }
+}
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index 7f86d99..2603281 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -36,7 +36,8 @@
class GLES20RecordingCanvas extends GLES20Canvas {
// These lists ensure that any Bitmaps recorded by a DisplayList are kept alive as long
// as the DisplayList is alive.
- private HashSet<Bitmap> mBitmaps = new HashSet<Bitmap>();
+ @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
+ private final HashSet<Bitmap> mBitmaps = new HashSet<Bitmap>();
GLES20RecordingCanvas(boolean translucent) {
super(true, translucent);
@@ -57,12 +58,6 @@
}
@Override
- public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
- Paint paint) {
- super.drawArc(oval, startAngle, sweepAngle, useCenter, paint);
- }
-
- @Override
public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
super.drawPatch(bitmap, chunks, dst, paint);
mBitmaps.add(bitmap);
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 8b8d15e..1a5df98 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -18,6 +18,7 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Paint;
/**
* Hardware accelerated canvas.
@@ -48,5 +49,18 @@
*
* @param displayList The display list to replay.
*/
- public abstract void drawDisplayList(DisplayList displayList);
+ abstract void drawDisplayList(DisplayList displayList);
+
+ /**
+ * Draws the specified layer onto this canvas.
+ *
+ * @param left The left coordinate of the layer
+ * @param top The top coordinate of the layer
+ * @param right The right coordinate of the layer
+ * @param bottom The bottom coordinate of the layer
+ * @param layer The layer to composite on this canvas
+ * @param paint The paint used to draw the layer
+ */
+ abstract void drawHardwareLayer(float left, float top, float right, float bottom,
+ HardwareLayer layer, Paint paint);
}
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
new file mode 100644
index 0000000..d01b8ce
--- /dev/null
+++ b/core/java/android/view/HardwareLayer.java
@@ -0,0 +1,115 @@
+/*
+ * 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.Canvas;
+
+/**
+ * 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.
+ */
+abstract class HardwareLayer {
+ int mWidth;
+ int mHeight;
+
+ final boolean mOpaque;
+
+ /**
+ * Creates a new hardware layer at least as large as the supplied
+ * dimensions.
+ *
+ * @param width The minimum width of the layer
+ * @param height The minimum height of the layer
+ * @param isOpaque Whether the layer should be opaque or not
+ */
+ HardwareLayer(int width, int height, boolean isOpaque) {
+ mWidth = width;
+ mHeight = height;
+ mOpaque = isOpaque;
+ }
+
+ /**
+ * Returns the minimum width of the layer.
+ *
+ * @return The minimum desired width of the hardware layer
+ */
+ int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * Returns the minimum height of the layer.
+ *
+ * @return The minimum desired height of the hardware layer
+ */
+ int getHeight() {
+ return mHeight;
+ }
+
+ /**
+ * Returns whether or not this layer is opaque.
+ *
+ * @return True if the layer is opaque, false otherwise
+ */
+ boolean isOpaque() {
+ return mOpaque;
+ }
+
+ /**
+ * Indicates whether this layer can be rendered.
+ *
+ * @return True if the layer can be rendered into, false otherwise
+ */
+ abstract boolean isValid();
+
+ /**
+ * Resizes the layer, if necessary, to be at least as large
+ * as the supplied dimensions.
+ *
+ * @param width The new desired minimum width for this layer
+ * @param height The new desired minimum height for this layer
+ */
+ abstract void resize(int width, int height);
+
+ /**
+ * Returns a hardware canvas that can be used to render onto
+ * this layer.
+ *
+ * @return A hardware canvas, or null if a canvas cannot be created
+ */
+ abstract HardwareCanvas getCanvas();
+
+ /**
+ * Destroys resources without waiting for a GC.
+ */
+ abstract void destroy();
+
+ /**
+ * This must be invoked before drawing onto this layer.
+ * @param currentCanvas
+ */
+ abstract HardwareCanvas start(Canvas currentCanvas);
+
+ /**
+ * This must be invoked after drawing onto this layer.
+ * @param currentCanvas
+ */
+ abstract void end(Canvas currentCanvas);
+}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 63490ee..05a9ff8 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -116,6 +116,17 @@
abstract DisplayList createDisplayList();
/**
+ * Creates a new hardware layer.
+ *
+ * @param width The minimum width of the layer
+ * @param height The minimum height of the layer
+ * @param isOpaque Whether the layer should be opaque or not
+ *
+ * @return A hardware layer
+ */
+ abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque);
+
+ /**
* Initializes the hardware renderer for the specified surface and setup the
* renderer for drawing, if needed. This is invoked when the ViewRoot has
* potentially lost the hardware renderer. The hardware renderer should be
@@ -682,6 +693,11 @@
DisplayList createDisplayList() {
return new GLES20DisplayList();
}
+
+ @Override
+ HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
+ return new GLES20Layer(width, height, isOpaque);
+ }
static HardwareRenderer create(boolean translucent) {
if (GLES20Canvas.isAvailable()) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 32f0b14..703084f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2049,6 +2049,7 @@
private Bitmap mDrawingCache;
private Bitmap mUnscaledDrawingCache;
private DisplayList mDisplayList;
+ private HardwareLayer mHardwareLayer;
/**
* When this view has focus and the next focus is {@link #FOCUS_LEFT},
@@ -2204,6 +2205,10 @@
*
* <p>A hardware layer is useful to apply a specific color filter and/or
* blending mode and/or translucency to a view and all its children.</p>
+ * <p>A hardware layer can be used to cache a complex view tree into a
+ * texture and reduce the complexity of drawing operations. For instance,
+ * when animating a complex view tree with a translation, a hardware layer can
+ * be used to render the view tree only once.</p>
* <p>A hardware layer can also be used to increase the rendering quality when
* rotation transformations are applied on a view. It can also be used to
* prevent potential clipping issues when applying 3D transforms on a view.</p>
@@ -7551,9 +7556,16 @@
*/
protected void onDetachedFromWindow() {
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
+
removeUnsetPressCallback();
removeLongPressCallback();
+
destroyDrawingCache();
+
+ if (mHardwareLayer != null) {
+ mHardwareLayer.destroy();
+ mHardwareLayer = null;
+ }
}
/**
@@ -7868,23 +7880,36 @@
throw new IllegalArgumentException("Layer type can only be one of: LAYER_TYPE_NONE, "
+ "LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE");
}
+
+ if (layerType == mLayerType) return;
// Destroy any previous software drawing cache if needed
- if (mLayerType == LAYER_TYPE_SOFTWARE && layerType != LAYER_TYPE_SOFTWARE) {
- if (mDrawingCache != null) {
- mDrawingCache.recycle();
- mDrawingCache = null;
- }
-
- if (mUnscaledDrawingCache != null) {
- mUnscaledDrawingCache.recycle();
- mUnscaledDrawingCache = null;
- }
+ switch (mLayerType) {
+ case LAYER_TYPE_SOFTWARE:
+ if (mDrawingCache != null) {
+ mDrawingCache.recycle();
+ mDrawingCache = null;
+ }
+
+ if (mUnscaledDrawingCache != null) {
+ mUnscaledDrawingCache.recycle();
+ mUnscaledDrawingCache = null;
+ }
+ break;
+ case LAYER_TYPE_HARDWARE:
+ if (mHardwareLayer != null) {
+ mHardwareLayer.destroy();
+ mHardwareLayer = null;
+ }
+ break;
+ default:
+ break;
}
mLayerType = layerType;
mLayerPaint = mLayerType == LAYER_TYPE_NONE ? null : paint;
+ // TODO: Make sure we invalidate the parent's display list
invalidate();
}
@@ -7905,6 +7930,62 @@
public int getLayerType() {
return mLayerType;
}
+
+ /**
+ * <p>Returns a hardware layer that can be used to draw this view again
+ * without executing its draw method.</p>
+ *
+ * @return A HardwareLayer ready to render, or null if an error occurred.
+ */
+ HardwareLayer getHardwareLayer(Canvas currentCanvas) {
+ if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
+ return null;
+ }
+
+ final int width = mRight - mLeft;
+ final int height = mBottom - mTop;
+
+ if (width == 0 || height == 0) {
+ return null;
+ }
+
+ if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || mHardwareLayer == null) {
+ if (mHardwareLayer == null) {
+ mHardwareLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(
+ width, height, isOpaque());
+ } else if (mHardwareLayer.getWidth() != width || mHardwareLayer.getHeight() != height) {
+ mHardwareLayer.resize(width, height);
+ }
+
+ final HardwareCanvas canvas = mHardwareLayer.start(currentCanvas);
+ try {
+ canvas.setViewport(width, height);
+ canvas.onPreDraw();
+
+ computeScroll();
+ canvas.translate(-mScrollX, -mScrollY);
+
+ final int restoreCount = canvas.save();
+
+ mPrivateFlags |= DRAWN | DRAWING_CACHE_VALID;
+
+ // Fast path for layouts with no backgrounds
+ if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
+ mPrivateFlags &= ~DIRTY_MASK;
+ dispatchDraw(canvas);
+ } else {
+ draw(canvas);
+ }
+
+ canvas.restoreToCount(restoreCount);
+ } finally {
+ canvas.onPostDraw();
+ mHardwareLayer.end(currentCanvas);
+ }
+ }
+
+ return mHardwareLayer;
+ }
/**
* <p>Enables or disables the drawing cache. When the drawing cache is enabled, the next call
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 7af611e..ed5eed1 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2179,7 +2179,7 @@
boolean scalingRequired = false;
boolean caching;
- final int layerType = child.getLayerType();
+ int layerType = child.getLayerType();
if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
(flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) {
@@ -2278,6 +2278,7 @@
if (caching) {
if (!canvas.isHardwareAccelerated()) {
if (layerType != LAYER_TYPE_NONE) {
+ layerType = LAYER_TYPE_SOFTWARE;
child.buildDrawingCache(true);
}
cache = child.getDrawingCache(true);
@@ -2354,13 +2355,11 @@
}
if (layerType != LAYER_TYPE_NONE && child.mLayerPaint != null) {
child.mLayerPaint.setAlpha(multipliedAlpha);
- canvas.saveLayer(sx, sy, sx + cr - cl, sy + cb - ct,
- child.mLayerPaint, layerFlags);
} else {
canvas.saveLayerAlpha(sx, sy, sx + cr - cl, sy + cb - ct,
multipliedAlpha, layerFlags);
+ layerSaved = true;
}
- layerSaved = true;
} else {
// Alpha is handled by the child directly, clobber the layer's alpha
if (layerType != LAYER_TYPE_NONE && child.mLayerPaint != null) {
@@ -2388,24 +2387,35 @@
}
if (hasNoCache) {
+ boolean layerRendered = false;
if (!layerSaved && layerType == LAYER_TYPE_HARDWARE) {
- canvas.saveLayer(sx, sy, sx + cr - cl, sy + cb - ct, child.mLayerPaint,
- Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
- }
- if (!hasDisplayList) {
- // Fast path for layouts with no backgrounds
- if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
- if (ViewDebug.TRACE_HIERARCHY) {
- ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
- }
- child.mPrivateFlags &= ~DIRTY_MASK;
- child.dispatchDraw(canvas);
+ final HardwareLayer layer = child.getHardwareLayer(canvas);
+ if (layer != null && layer.isValid()) {
+ ((HardwareCanvas) canvas).drawHardwareLayer(0, 0, cr - cl, cb - ct,
+ layer, child.mLayerPaint);
+ layerRendered = true;
} else {
- child.draw(canvas);
+ canvas.saveLayer(sx, sy, sx + cr - cl, sy + cb - ct, child.mLayerPaint,
+ Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
}
- } else {
- child.mPrivateFlags &= ~DIRTY_MASK;
- ((HardwareCanvas) canvas).drawDisplayList(displayList);
+ }
+
+ if (!layerRendered) {
+ if (!hasDisplayList) {
+ // Fast path for layouts with no backgrounds
+ if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
+ if (ViewDebug.TRACE_HIERARCHY) {
+ ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
+ }
+ child.mPrivateFlags &= ~DIRTY_MASK;
+ child.dispatchDraw(canvas);
+ } else {
+ child.draw(canvas);
+ }
+ } else {
+ child.mPrivateFlags &= ~DIRTY_MASK;
+ ((HardwareCanvas) canvas).drawDisplayList(displayList);
+ }
}
} else if (cache != null) {
child.mPrivateFlags &= ~DIRTY_MASK;
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index b4c868f..d6991e6 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -32,6 +32,7 @@
#include <SkXfermode.h>
#include <DisplayListRenderer.h>
+#include <LayerRenderer.h>
#include <OpenGLDebugRenderer.h>
#include <OpenGLRenderer.h>
#include <SkiaShader.h>
@@ -77,7 +78,7 @@
// Constructors
// ----------------------------------------------------------------------------
-static OpenGLRenderer* android_view_GLES20Canvas_createRenderer(JNIEnv* env, jobject canvas) {
+static OpenGLRenderer* android_view_GLES20Canvas_createRenderer(JNIEnv* env, jobject clazz) {
RENDERER_LOGD("Create OpenGLRenderer");
#if PROFILE_RENDERER
return new OpenGLDebugRenderer;
@@ -442,6 +443,71 @@
renderer->drawDisplayList(displayList);
}
+// ----------------------------------------------------------------------------
+// Layers
+// ----------------------------------------------------------------------------
+
+static void android_view_GLES20Canvas_interrupt(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer) {
+ renderer->interrupt();
+}
+
+static void android_view_GLES20Canvas_resume(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer) {
+ renderer->resume();
+}
+
+static OpenGLRenderer* android_view_GLES20Canvas_createLayerRenderer(JNIEnv* env,
+ jobject clazz, jint fbo) {
+ return new LayerRenderer(fbo);
+}
+
+static jint android_view_GLES20Canvas_createLayer(JNIEnv* env,
+ jobject clazz, jint width, jint height, jintArray layerInfo) {
+ uint32_t layerWidth = 0;
+ uint32_t layerHeight = 0;
+ GLuint textureId = 0;
+
+ jint layerId = LayerRenderer::createLayer(width, height,
+ &layerWidth, &layerHeight, &textureId);
+
+ if (layerId) {
+ jint* storage = env->GetIntArrayElements(layerInfo, NULL);
+ storage[0] = layerWidth;
+ storage[1] = layerHeight;
+ storage[2] = textureId;
+ env->ReleaseIntArrayElements(layerInfo, storage, 0);
+ }
+
+ return layerId;
+}
+
+static void android_view_GLES20Canvas_resizeLayer(JNIEnv* env,
+ jobject clazz, jint layerId, jint layerTextureId, jint width, jint height,
+ jintArray layerInfo) {
+ uint32_t layerWidth = 0;
+ uint32_t layerHeight = 0;
+
+ LayerRenderer::resizeLayer(layerId, layerTextureId, width, height, &layerWidth, &layerHeight);
+
+ jint* storage = env->GetIntArrayElements(layerInfo, NULL);
+ storage[0] = layerWidth;
+ storage[1] = layerHeight;
+ env->ReleaseIntArrayElements(layerInfo, storage, 0);
+}
+
+static void android_view_GLES20Canvas_destroyLayer(JNIEnv* env,
+ jobject clazz, jint layerId, jint layerTextureId) {
+ LayerRenderer::destroyLayer(layerId, layerTextureId);
+}
+
+static void android_view_GLES20Canvas_drawLayer(JNIEnv* env,
+ jobject canvas, OpenGLRenderer* renderer,
+ jfloat left, jfloat top, jfloat right, jfloat bottom,
+ jint layerTexture, jfloat u, jfloat v, SkPaint* paint) {
+ renderer->drawLayer(layerTexture, left, top, right, bottom, u, v, paint);
+}
+
#endif // USE_OPENGL_RENDERER
// ----------------------------------------------------------------------------
@@ -522,10 +588,20 @@
{ "nGetClipBounds", "(ILandroid/graphics/Rect;)Z",
(void*) android_view_GLES20Canvas_getClipBounds },
- { "nGetDisplayList", "(I)I", (void*) android_view_GLES20Canvas_getDisplayList },
- { "nDestroyDisplayList", "(I)V", (void*) android_view_GLES20Canvas_destroyDisplayList },
- { "nGetDisplayListRenderer", "(I)I", (void*) android_view_GLES20Canvas_getDisplayListRenderer },
- { "nDrawDisplayList", "(II)V", (void*) android_view_GLES20Canvas_drawDisplayList },
+ { "nGetDisplayList", "(I)I", (void*) android_view_GLES20Canvas_getDisplayList },
+ { "nDestroyDisplayList", "(I)V", (void*) android_view_GLES20Canvas_destroyDisplayList },
+ { "nGetDisplayListRenderer", "(I)I", (void*) android_view_GLES20Canvas_getDisplayListRenderer },
+ { "nDrawDisplayList", "(II)V", (void*) android_view_GLES20Canvas_drawDisplayList },
+
+ { "nInterrupt", "(I)V", (void*) android_view_GLES20Canvas_interrupt },
+ { "nResume", "(I)V", (void*) android_view_GLES20Canvas_resume },
+
+ { "nCreateLayerRenderer", "(I)I", (void*) android_view_GLES20Canvas_createLayerRenderer },
+ { "nCreateLayer", "(II[I)I", (void*) android_view_GLES20Canvas_createLayer },
+ { "nResizeLayer", "(IIII[I)V", (void*) android_view_GLES20Canvas_resizeLayer },
+ { "nDestroyLayer", "(II)V", (void*) android_view_GLES20Canvas_destroyLayer },
+ { "nDrawLayer", "(IFFFFIFFI)V",
+ (void*) android_view_GLES20Canvas_drawLayer },
#endif
};