Merge "Better backend for hardware layers." into honeycomb

This commit is contained in:
Romain Guy
2011-01-11 17:52:05 -08:00
committed by Android (Google) Code Review
20 changed files with 907 additions and 48 deletions

View File

@@ -73,9 +73,21 @@ class GLES20Canvas extends HardwareCanvas {
// 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 @@ class GLES20Canvas extends HardwareCanvas {
} else {
mRenderer = nCreateRenderer();
}
setupFinalizer();
}
private void setupFinalizer() {
if (mRenderer == 0) {
throw new IllegalStateException("Could not create GLES20Canvas renderer");
} else {
@@ -97,7 +113,8 @@ class GLES20Canvas extends HardwareCanvas {
}
}
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);
@@ -135,6 +152,15 @@ class GLES20Canvas extends HardwareCanvas {
}
}
///////////////////////////////////////////////////////////////////////////
// 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
///////////////////////////////////////////////////////////////////////////
@@ -226,6 +252,34 @@ class GLES20Canvas extends HardwareCanvas {
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
///////////////////////////////////////////////////////////////////////////

View File

@@ -106,7 +106,11 @@ class GLES20DisplayList extends DisplayList {
@Override
protected void finalize() throws Throwable {
replaceNativeObject(0);
try {
replaceNativeObject(0);
} finally {
super.finalize();
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -36,7 +36,8 @@ import java.util.HashSet;
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);
@@ -56,12 +57,6 @@ class GLES20RecordingCanvas extends GLES20Canvas {
setupRenderer(true);
}
@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);

View File

@@ -18,6 +18,7 @@ package android.view;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
/**
* Hardware accelerated canvas.
@@ -48,5 +49,18 @@ abstract class HardwareCanvas extends Canvas {
*
* @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);
}

View File

@@ -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);
}

View File

@@ -115,6 +115,17 @@ public abstract class HardwareRenderer {
*/
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
@@ -682,6 +693,11 @@ public abstract class HardwareRenderer {
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()) {

View File

@@ -2049,6 +2049,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*
* <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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*/
protected void onDetachedFromWindow() {
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
removeUnsetPressCallback();
removeLongPressCallback();
destroyDrawingCache();
if (mHardwareLayer != null) {
mHardwareLayer.destroy();
mHardwareLayer = null;
}
}
/**
@@ -7868,23 +7880,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
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 class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
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

View File

@@ -2179,7 +2179,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
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);
}
}
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 {
child.mPrivateFlags &= ~DIRTY_MASK;
((HardwareCanvas) canvas).drawDisplayList(displayList);
}
} else if (cache != null) {
child.mPrivateFlags &= ~DIRTY_MASK;

View File

@@ -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 @@ static struct {
// 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 @@ static void android_view_GLES20Canvas_drawDisplayList(JNIEnv* env,
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 @@ static JNINativeMethod gMethods[] = {
{ "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
};

View File

@@ -13,6 +13,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
FboCache.cpp \
GradientCache.cpp \
LayerCache.cpp \
LayerRenderer.cpp \
Matrix.cpp \
OpenGLDebugRenderer.cpp \
OpenGLRenderer.cpp \

View File

@@ -241,6 +241,11 @@ void DisplayList::replay(OpenGLRenderer& renderer) {
renderer.drawDisplayList(getDisplayList());
}
break;
case DrawLayer: {
renderer.drawLayer(getInt(), getFloat(), getFloat(), getFloat(), getFloat(),
getFloat(), getFloat(), getPaint());
}
break;
case DrawBitmap: {
renderer.drawBitmap(getBitmap(), getFloat(), getFloat(), getPaint());
}
@@ -483,6 +488,16 @@ void DisplayListRenderer::drawDisplayList(DisplayList* displayList) {
addDisplayList(displayList);
}
void DisplayListRenderer::drawLayer(int texture, float left, float top, float right, float bottom,
float u, float v, SkPaint* paint) {
addOp(DisplayList::DrawLayer);
addInt(texture);
addBounds(left, top, right, bottom);
addFloat(u);
addFloat(v);
addPaint(paint);
}
void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top,
SkPaint* paint) {
addOp(DisplayList::DrawBitmap);

View File

@@ -93,6 +93,7 @@ public:
ConcatMatrix,
ClipRect,
DrawDisplayList,
DrawLayer,
DrawBitmap,
DrawBitmapMatrix,
DrawBitmapRect,
@@ -245,6 +246,8 @@ public:
bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
void drawDisplayList(DisplayList* displayList);
void drawLayer(int texture, float left, float top, float right, float bottom,
float u, float v, SkPaint* paint);
void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,

121
libs/hwui/LayerRenderer.cpp Normal file
View File

@@ -0,0 +1,121 @@
/*
* 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.
*/
#define LOG_TAG "OpenGLRenderer"
#include "LayerRenderer.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// Rendering
///////////////////////////////////////////////////////////////////////////////
void LayerRenderer::prepare(bool opaque) {
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &mPreviousFbo);
glBindFramebuffer(GL_FRAMEBUFFER, mFbo);
OpenGLRenderer::prepare(opaque);
}
void LayerRenderer::finish() {
OpenGLRenderer::finish();
glBindFramebuffer(GL_FRAMEBUFFER, mPreviousFbo);
}
///////////////////////////////////////////////////////////////////////////////
// Static functions
///////////////////////////////////////////////////////////////////////////////
GLuint LayerRenderer::createLayer(uint32_t width, uint32_t height,
uint32_t* layerWidth, uint32_t* layerHeight, GLuint* texture) {
GLuint previousFbo;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
GLuint fbo = 0;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, texture);
glBindTexture(GL_TEXTURE_2D, *texture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
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, GL_RGBA, width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
if (glGetError() != GL_NO_ERROR) {
glDeleteBuffers(1, &fbo);
glDeleteTextures(1, texture);
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
return 0;
}
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
*texture, 0);
if (glGetError() != GL_NO_ERROR) {
glDeleteBuffers(1, &fbo);
glDeleteTextures(1, texture);
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
return 0;
}
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
*layerWidth = width;
*layerHeight = height;
return fbo;
}
void LayerRenderer::resizeLayer(GLuint fbo, GLuint texture, uint32_t width, uint32_t height,
uint32_t* layerWidth, uint32_t* layerHeight) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
if (glGetError() != GL_NO_ERROR) {
glDeleteBuffers(1, &fbo);
glDeleteTextures(1, texture);
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
*layerWidth = 0;
*layerHeight = 0;
return;
}
*layerWidth = width;
*layerHeight = height;
}
void LayerRenderer::destroyLayer(GLuint fbo, GLuint texture) {
if (fbo) glDeleteFramebuffers(1, &fbo);
if (texture) glDeleteTextures(1, &texture);
}
}; // namespace uirenderer
}; // namespace android

55
libs/hwui/LayerRenderer.h Normal file
View File

@@ -0,0 +1,55 @@
/*
* 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_HWUI_LAYER_RENDERER_H
#define ANDROID_HWUI_LAYER_RENDERER_H
#include "OpenGLRenderer.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// Renderer
///////////////////////////////////////////////////////////////////////////////
class LayerRenderer: public OpenGLRenderer {
public:
LayerRenderer(GLuint fbo): mFbo(fbo) {
}
~LayerRenderer() {
}
void prepare(bool opaque);
void finish();
static GLuint createLayer(uint32_t width, uint32_t height,
uint32_t* layerWidth, uint32_t* layerHeight, GLuint* texture);
static void resizeLayer(GLuint fbo, GLuint texture, uint32_t width, uint32_t height,
uint32_t* layerWidth, uint32_t* layerHeight);
static void destroyLayer(GLuint fbo, GLuint texture);
private:
GLuint mFbo;
GLuint mPreviousFbo;
}; // class LayerRenderer
}; // namespace uirenderer
}; // namespace android
#endif // ANDROID_HWUI_LAYER_RENDERER_H

View File

@@ -178,7 +178,7 @@ void OpenGLRenderer::finish() {
#endif
}
void OpenGLRenderer::acquireContext() {
void OpenGLRenderer::interrupt() {
if (mCaches.currentProgram) {
if (mCaches.currentProgram->isInUse()) {
mCaches.currentProgram->remove();
@@ -188,7 +188,11 @@ void OpenGLRenderer::acquireContext() {
mCaches.unbindMeshBuffer();
}
void OpenGLRenderer::releaseContext() {
void OpenGLRenderer::acquireContext() {
interrupt();
}
void OpenGLRenderer::resume() {
glViewport(0, 0, mSnapshot->viewport.getWidth(), mSnapshot->viewport.getHeight());
glEnable(GL_SCISSOR_TEST);
@@ -205,6 +209,10 @@ void OpenGLRenderer::releaseContext() {
glBlendEquation(GL_FUNC_ADD);
}
void OpenGLRenderer::releaseContext() {
resume();
}
///////////////////////////////////////////////////////////////////////////////
// State management
///////////////////////////////////////////////////////////////////////////////
@@ -1477,6 +1485,30 @@ void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
finishDrawTexture();
}
void OpenGLRenderer::drawLayer(int texture, float left, float top, float right, float bottom,
float u, float v, SkPaint* paint) {
if (quickReject(left, top, right, bottom)) {
return;
}
glActiveTexture(gTextureUnits[0]);
if (!texture) return;
mCaches.unbindMeshBuffer();
resetDrawTextureTexCoords(0.0f, v, u, 0.0f);
int alpha;
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
// TODO: Should get the blend info from the caller
drawTextureMesh(left, top, right, bottom, texture, alpha / 255.0f, mode, true,
&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0],
GL_TRIANGLE_STRIP, gMeshCount);
resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
}
///////////////////////////////////////////////////////////////////////////////
// Shaders
///////////////////////////////////////////////////////////////////////////////

View File

@@ -64,6 +64,10 @@ public:
virtual void prepare(bool opaque);
virtual void finish();
// These two calls must not be recorded in display lists
void interrupt();
void resume();
virtual void acquireContext();
virtual void releaseContext();
@@ -91,6 +95,8 @@ public:
virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
virtual void drawDisplayList(DisplayList* displayList);
virtual void drawLayer(int texture, float left, float top, float right, float bottom,
float u, float v, SkPaint* paint);
virtual void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
virtual void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
virtual void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,

View File

@@ -33,6 +33,15 @@
</intent-filter>
</activity>
<activity
android:name="ViewLayersActivity2"
android:label="_ViewLayers2">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="AlphaLayersActivity"
android:label="_αLayers">

View File

@@ -69,7 +69,7 @@ public class PathsActivity extends Activity {
mMediumPaint = new Paint();
mMediumPaint.setAntiAlias(true);
mMediumPaint.setColor(0xff0000ff);
mMediumPaint.setColor(0xe00000ff);
mMediumPaint.setStrokeWidth(10.0f);
mMediumPaint.setStyle(Paint.Style.STROKE);

View File

@@ -0,0 +1,109 @@
/*
* 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 com.android.test.hwui;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
@SuppressWarnings({"UnusedDeclaration"})
public class ViewLayersActivity2 extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_layers);
setupList(R.id.list1);
setupList(R.id.list2);
setupList(R.id.list3);
}
private void setupList(int listId) {
final ListView list = (ListView) findViewById(listId);
list.setAdapter(new SimpleListAdapter(this));
list.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
private static class SimpleListAdapter extends ArrayAdapter<String> {
public SimpleListAdapter(Context context) {
super(context, android.R.layout.simple_list_item_1, DATA_LIST);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView v = (TextView) super.getView(position, convertView, parent);
final Resources r = getContext().getResources();
final DisplayMetrics metrics = r.getDisplayMetrics();
v.setCompoundDrawablePadding((int) (6 * metrics.density + 0.5f));
v.setCompoundDrawablesWithIntrinsicBounds(r.getDrawable(R.drawable.icon),
null, null, null);
return v;
}
}
private static final String[] DATA_LIST = {
"Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
"Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
"Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
"Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
"Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
"Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil",
"British Indian Ocean Territory", "British Virgin Islands", "Brunei", "Bulgaria",
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
"Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
"Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
"French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
"Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
"Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
"Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
"Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
"Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
"Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
"Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
"Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
"Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
"Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
"Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
"Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
"Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
"Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
"Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
"Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
"Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
"Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
"Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
"The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
"Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
"Ukraine", "United Arab Emirates", "United Kingdom",
"United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
"Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
"Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
};
}