Merge "New widget: TextureView Bug #4343984"

This commit is contained in:
Romain Guy
2011-04-28 18:47:01 -07:00
committed by Android (Google) Code Review
19 changed files with 847 additions and 86 deletions

View File

@@ -21010,6 +21010,21 @@ package android.view {
method public void setZOrderOnTop(boolean);
}
public class TextureView extends android.view.View {
ctor public TextureView(android.content.Context);
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.SurfaceTexture getSurfaceTexture();
method public android.view.TextureView.SurfaceTextureListener getSurfaceTextureListener();
method protected final void onDraw(android.graphics.Canvas);
method public void setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener);
}
public static abstract interface TextureView.SurfaceTextureListener {
method public abstract void onSurfaceTextureAvailable(android.graphics.SurfaceTexture);
}
public class TouchDelegate {
ctor public TouchDelegate(android.graphics.Rect, android.view.View);
method public boolean onTouchEvent(android.view.MotionEvent);

View File

@@ -160,8 +160,11 @@ class GLES20Canvas extends HardwareCanvas {
// Hardware layers
///////////////////////////////////////////////////////////////////////////
static native int nCreateTextureLayer(int[] layerInfo);
static native int nCreateLayer(int width, int height, boolean isOpaque, int[] layerInfo);
static native void nResizeLayer(int layerId, int width, int height, int[] layerInfo);
static native void nUpdateTextureLayer(int layerId, int width, int height,
float[] textureTransform);
static native void nDestroyLayer(int layerId);
static native void nDestroyLayerDeferred(int layerId);
@@ -272,7 +275,7 @@ class GLES20Canvas extends HardwareCanvas {
}
private static native void nDrawLayer(int renderer, int layer, float x, float y, int paint);
void interrupt() {
nInterrupt(mRenderer);
}

View File

@@ -14,39 +14,21 @@
* limitations under the License.
*/
package android.view;
import android.graphics.Canvas;
package android.view;
/**
* An OpenGL ES 2.0 implementation of {@link HardwareLayer}.
*/
class GLES20Layer extends HardwareLayer {
private int mLayer;
abstract class GLES20Layer extends HardwareLayer {
int mLayer;
Finalizer mFinalizer;
private int mLayerWidth;
private int mLayerHeight;
GLES20Layer() {
}
private final GLES20Canvas mCanvas;
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
private final Finalizer mFinalizer;
GLES20Layer(int width, int height, boolean isOpaque) {
super(width, height, isOpaque);
int[] layerInfo = new int[2];
mLayer = GLES20Canvas.nCreateLayer(width, height, isOpaque, layerInfo);
if (mLayer != 0) {
mLayerWidth = layerInfo[0];
mLayerHeight = layerInfo[1];
mCanvas = new GLES20Canvas(mLayer, !isOpaque);
mFinalizer = new Finalizer(mLayer);
} else {
mCanvas = null;
mFinalizer = null;
}
GLES20Layer(int width, int height, boolean opaque) {
super(width, height, opaque);
}
/**
@@ -58,55 +40,14 @@ class GLES20Layer extends HardwareLayer {
return mLayer;
}
@Override
boolean isValid() {
return mLayer != 0 && mLayerWidth > 0 && mLayerHeight > 0;
}
@Override
void resize(int width, int height) {
if (!isValid() || width <= 0 || height <= 0) return;
mWidth = width;
mHeight = height;
if (width != mLayerWidth || height != mLayerHeight) {
int[] layerInfo = new int[2];
GLES20Canvas.nResizeLayer(mLayer, width, height, layerInfo);
mLayerWidth = layerInfo[0];
mLayerHeight = layerInfo[1];
}
}
@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();
if (mFinalizer != null) mFinalizer.destroy();
mLayer = 0;
}
private static class Finalizer {
static class Finalizer {
private int mLayerId;
public Finalizer(int layerId) {

View File

@@ -0,0 +1,91 @@
/*
* 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}. This
* implementation can be used a rendering target. It generates a
* {@link Canvas} that can be used to render into an FBO using OpenGL.
*/
class GLES20RenderLayer extends GLES20Layer {
private int mLayerWidth;
private int mLayerHeight;
private final GLES20Canvas mCanvas;
GLES20RenderLayer(int width, int height, boolean isOpaque) {
super(width, height, isOpaque);
int[] layerInfo = new int[2];
mLayer = GLES20Canvas.nCreateLayer(width, height, isOpaque, layerInfo);
if (mLayer != 0) {
mLayerWidth = layerInfo[0];
mLayerHeight = layerInfo[1];
mCanvas = new GLES20Canvas(mLayer, !isOpaque);
mFinalizer = new Finalizer(mLayer);
} else {
mCanvas = null;
mFinalizer = null;
}
}
@Override
boolean isValid() {
return mLayer != 0 && mLayerWidth > 0 && mLayerHeight > 0;
}
@Override
void resize(int width, int height) {
if (!isValid() || width <= 0 || height <= 0) return;
mWidth = width;
mHeight = height;
if (width != mLayerWidth || height != mLayerHeight) {
int[] layerInfo = new int[2];
GLES20Canvas.nResizeLayer(mLayer, width, height, layerInfo);
mLayerWidth = layerInfo[0];
mLayerHeight = layerInfo[1];
}
}
@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();
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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;
import android.graphics.SurfaceTexture;
/**
* An OpenGL ES 2.0 implementation of {@link HardwareLayer}. This
* implementation can be used as a texture. Rendering into this
* layer is not controlled by a {@link HardwareCanvas}.
*/
class GLES20TextureLayer extends GLES20Layer {
private int mTexture;
private SurfaceTexture mSurface;
GLES20TextureLayer() {
int[] layerInfo = new int[2];
mLayer = GLES20Canvas.nCreateTextureLayer(layerInfo);
if (mLayer != 0) {
mTexture = layerInfo[0];
mFinalizer = new Finalizer(mLayer);
} else {
mFinalizer = null;
}
}
@Override
boolean isValid() {
return mLayer != 0 && mTexture != 0;
}
@Override
void resize(int width, int height) {
}
@Override
HardwareCanvas getCanvas() {
return null;
}
@Override
HardwareCanvas start(Canvas currentCanvas) {
return null;
}
@Override
void end(Canvas currentCanvas) {
}
SurfaceTexture getSurfaceTexture() {
if (mSurface == null) {
mSurface = new SurfaceTexture(mTexture);
}
return mSurface;
}
void update(int width, int height, float[] textureTransform) {
GLES20Canvas.nUpdateTextureLayer(mLayer, width, height, textureTransform);
}
}

View File

@@ -26,11 +26,23 @@ import android.graphics.Canvas;
* drawn several times.
*/
abstract class HardwareLayer {
/**
* Indicates an unknown dimension (width or height.)
*/
static final int DIMENSION_UNDEFINED = -1;
int mWidth;
int mHeight;
final boolean mOpaque;
/**
* Creates a new hardware layer with undefined dimensions.
*/
HardwareLayer() {
this(DIMENSION_UNDEFINED, DIMENSION_UNDEFINED, false);
}
/**
* Creates a new hardware layer at least as large as the supplied
* dimensions.

View File

@@ -20,6 +20,7 @@ package android.view;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.os.*;
import android.util.EventLog;
import android.util.Log;
@@ -165,6 +166,14 @@ public abstract class HardwareRenderer {
*/
abstract DisplayList createDisplayList(View v);
/**
* Creates a new hardware layer. A hardware layer built by calling this
* method will be treated as a texture layer, instead of as a render target.
*
* @return A hardware layer
*/
abstract HardwareLayer createHardwareLayer();
/**
* Creates a new hardware layer.
*
@@ -175,6 +184,29 @@ public abstract class HardwareRenderer {
* @return A hardware layer
*/
abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque);
/**
* Creates a new {@link SurfaceTexture} that can be used to render into the
* specified hardware layer.
*
*
* @param layer The layer to render into using a {@link android.graphics.SurfaceTexture}
*
* @return A {@link SurfaceTexture}
*/
abstract SurfaceTexture createSuraceTexture(HardwareLayer layer);
/**
* Updates the specified layer.
*
* @param layer The hardware layer to update
* @param width The layer's width
* @param height The layer's height
* @param textureTransform A 4x4 column-first transform matrix to apply to
* texture coordinates
*/
abstract void updateTextureLayer(HardwareLayer layer, int width, int height,
float[] textureTransform);
/**
* Initializes the hardware renderer for the specified surface and setup the
@@ -856,10 +888,26 @@ public abstract class HardwareRenderer {
DisplayList createDisplayList(View v) {
return new GLES20DisplayList(v);
}
@Override
HardwareLayer createHardwareLayer() {
return new GLES20TextureLayer();
}
@Override
HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
return new GLES20Layer(width, height, isOpaque);
return new GLES20RenderLayer(width, height, isOpaque);
}
@Override
SurfaceTexture createSuraceTexture(HardwareLayer layer) {
return ((GLES20TextureLayer) layer).getSurfaceTexture();
}
@Override
void updateTextureLayer(HardwareLayer layer, int width, int height,
float[] textureTransform) {
((GLES20TextureLayer) layer).update(width, height, textureTransform);
}
static HardwareRenderer create(boolean translucent) {

View File

@@ -0,0 +1,299 @@
/*
* 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.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.SurfaceTexture;
import android.util.AttributeSet;
import android.util.Log;
/**
* <p>A TextureView can be used to display a content stream. Such a content
* stream can for instance be a video or an OpenGL scene. The content stream
* can come from the application's process as well as a remote process.</p>
*
* <p>TextureView can only be used in a hardware accelerated window. When
* rendered in software, TextureView will draw nothing.</p>
*
* <p>Unlike {@link SurfaceView}, TextureView does not create a separate
* window but behaves as a regular View. This key difference allows a
* TextureView to be moved, transformed, animated, etc. For instance, you
* can make a TextureView semi-translucent by calling
* <code>myView.setAlpha(0.5f)</code>.</p>
*
* <p>Using a TextureView is simple: all you need to do is get its
* {@link SurfaceTexture}. The {@link SurfaceTexture} can then be used to
* render content. The following example demonstrates how to render the
* camera preview into a TextureView:</p>
*
* <pre>
* public class LiveCameraActivity extends Activity implements TextureView.SurfaceTextureListener {
* private Camera mCamera;
* private TextureView mTextureView;
*
* @Override
* protected void onCreate(Bundle savedInstanceState) {
* super.onCreate(savedInstanceState);
*
* mTextureView = new TextureView(this);
* mTextureView.setSurfaceTextureListener(this);
*
* setContentView(mTextureView);
* }
*
* @Override
* protected void onDestroy() {
* super.onDestroy();
*
* mCamera.stopPreview();
* mCamera.release();
* }
*
* @Override
* public void onSurfaceTextureAvailable(SurfaceTexture surface) {
* mCamera = Camera.open();
*
* try {
* mCamera.setPreviewTexture(surface);
* mCamera.startPreview();
* } catch (IOException ioe) {
* // Something bad happened
* }
* }
* }
* </pre>
*
* <p>A TextureView's SurfaceTexture can be obtained either by invoking
* {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}.
* It is important to know that a SurfaceTexture is available only after the
* TextureView is attached to a window (and {@link #onAttachedToWindow()} has
* been invoked.) It is therefore highly recommended you use a listener to
* be notified when the SurfaceTexture becomes available.</p>
*
* @see SurfaceView
* @see SurfaceTexture
*/
public class TextureView extends View {
private HardwareLayer mLayer;
private SurfaceTexture mSurface;
private SurfaceTextureListener mListener;
private final float[] mTextureTransform = new float[16];
private final Runnable mUpdateLayerAction = new Runnable() {
@Override
public void run() {
updateLayer();
}
};
/**
* Creates a new TextureView.
*
* @param context The context to associate this view with.
*/
public TextureView(Context context) {
super(context);
init();
}
/**
* Creates a new TextureView.
*
* @param context The context to associate this view with.
* @param attrs The attributes of the XML tag that is inflating the view.
*/
@SuppressWarnings({"UnusedDeclaration"})
public TextureView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
/**
* Creates a new TextureView.
*
* @param context The context to associate this view with.
* @param attrs The attributes of the XML tag that is inflating the view.
* @param defStyle The default style to apply to this view. If 0, no style
* will be applied (beyond what is included in the theme). This may
* either be an attribute resource, whose value will be retrieved
* from the current theme, or an explicit style resource.
*/
@SuppressWarnings({"UnusedDeclaration"})
public TextureView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mLayerPaint = new Paint();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (!isHardwareAccelerated()) {
Log.w("TextureView", "A TextureView or a subclass can only be "
+ "used with hardware acceleration enabled.");
}
}
/**
* The layer type of a TextureView is ignored since a TextureView is always
* considered to act as a hardware layer. The optional paint supplied to this
* method will however be taken into account when rendering the content of
* this TextureView.
*
* @param layerType The ype of layer to use with this view, must be one of
* {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
* {@link #LAYER_TYPE_HARDWARE}
* @param paint The paint used to compose the layer. This argument is optional
* and can be null. It is ignored when the layer type is
* {@link #LAYER_TYPE_NONE}
*/
@Override
public void setLayerType(int layerType, Paint paint) {
if (paint != mLayerPaint) {
mLayerPaint = paint;
invalidate();
}
}
/**
* Always returns {@link #LAYER_TYPE_HARDWARE}.
*/
@Override
public int getLayerType() {
return LAYER_TYPE_HARDWARE;
}
/**
* Calling this method has no effect.
*/
@Override
public void buildLayer() {
}
/**
* Subclasses of TextureView cannot do their own rendering
* with the {@link Canvas} object.
*
* @param canvas The Canvas to which the View is rendered.
*/
@Override
public final void draw(Canvas canvas) {
super.draw(canvas);
}
/**
* Subclasses of TextureView cannot do their own rendering
* with the {@link Canvas} object.
*
* @param canvas The Canvas to which the View is rendered.
*/
@Override
protected final void onDraw(Canvas canvas) {
}
@Override
HardwareLayer getHardwareLayer() {
if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
return null;
}
if (mLayer == null) {
mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer();
mSurface = mAttachInfo.mHardwareRenderer.createSuraceTexture(mLayer);
mSurface.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
// Per SurfaceTexture's documentation, the callback may be invoked
// from an arbitrary thread
post(mUpdateLayerAction);
}
});
if (mListener != null) {
mListener.onSurfaceTextureAvailable(mSurface);
}
}
return mLayer;
}
private void updateLayer() {
if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
return;
}
mSurface.updateTexImage();
mSurface.getTransformMatrix(mTextureTransform);
mAttachInfo.mHardwareRenderer.updateTextureLayer(mLayer, getWidth(), getHeight(),
mTextureTransform);
invalidate();
}
/**
* Returns the {@link SurfaceTexture} used by this view. This method
* may return null if the view is not attached to a window.
*/
public SurfaceTexture getSurfaceTexture() {
return mSurface;
}
/**
* Returns the {@link SurfaceTextureListener} currently associated with this
* texture view.
*
* @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener)
* @see SurfaceTextureListener
*/
public SurfaceTextureListener getSurfaceTextureListener() {
return mListener;
}
/**
* Sets the {@link SurfaceTextureListener} used to listen to surface
* texture events.
*
* @see #getSurfaceTextureListener()
* @see SurfaceTextureListener
*/
public void setSurfaceTextureListener(SurfaceTextureListener listener) {
mListener = listener;
}
/**
* This listener can be used to be notified when the surface texture
* associated with this texture view is available.
*/
public static interface SurfaceTextureListener {
/**
* Invoked when a {@link TextureView}'s SurfaceTexture is ready for use.
*
* @param surface The surface returned by
* {@link android.view.TextureView#getSurfaceTexture()}
*/
public void onSurfaceTextureAvailable(SurfaceTexture surface);
}
}

View File

@@ -539,6 +539,19 @@ static OpenGLRenderer* android_view_GLES20Canvas_createLayerRenderer(JNIEnv* env
return NULL;
}
static Layer* android_view_GLES20Canvas_createTextureLayer(JNIEnv* env, jobject clazz,
jintArray layerInfo) {
Layer* layer = LayerRenderer::createTextureLayer();
if (layer) {
jint* storage = env->GetIntArrayElements(layerInfo, NULL);
storage[0] = layer->texture;
env->ReleaseIntArrayElements(layerInfo, storage, 0);
}
return layer;
}
static Layer* android_view_GLES20Canvas_createLayer(JNIEnv* env, jobject clazz,
jint width, jint height, jboolean isOpaque, jintArray layerInfo) {
Layer* layer = LayerRenderer::createLayer(width, height, isOpaque);
@@ -563,6 +576,13 @@ static void android_view_GLES20Canvas_resizeLayer(JNIEnv* env, jobject clazz,
env->ReleaseIntArrayElements(layerInfo, storage, 0);
}
static void android_view_GLES20Canvas_updateTextureLayer(JNIEnv* env, jobject clazz,
Layer* layer, jint width, jint height, jfloatArray texTransform) {
jfloat* transform = env->GetFloatArrayElements(texTransform, NULL);
LayerRenderer::updateTextureLayer(layer, width, height, transform);
env->ReleaseFloatArrayElements(texTransform, transform, 0);
}
static void android_view_GLES20Canvas_destroyLayer(JNIEnv* env, jobject clazz, Layer* layer) {
LayerRenderer::destroyLayer(layer);
}
@@ -696,6 +716,8 @@ static JNINativeMethod gMethods[] = {
{ "nCreateLayerRenderer", "(I)I", (void*) android_view_GLES20Canvas_createLayerRenderer },
{ "nCreateLayer", "(IIZ[I)I", (void*) android_view_GLES20Canvas_createLayer },
{ "nResizeLayer", "(III[I)V" , (void*) android_view_GLES20Canvas_resizeLayer },
{ "nCreateTextureLayer", "([I)I", (void*) android_view_GLES20Canvas_createTextureLayer },
{ "nUpdateTextureLayer", "(III[F)V" , (void*) android_view_GLES20Canvas_updateTextureLayer },
{ "nDestroyLayer", "(I)V", (void*) android_view_GLES20Canvas_destroyLayer },
{ "nDestroyLayerDeferred", "(I)V", (void*) android_view_GLES20Canvas_destroyLayerDeferred },
{ "nDrawLayer", "(IIFFI)V", (void*) android_view_GLES20Canvas_drawLayer },

View File

@@ -45,6 +45,8 @@ struct Layer {
mesh = NULL;
meshIndices = NULL;
meshElementCount = 0;
isCacheable = true;
isTextureLayer = false;
}
~Layer() {
@@ -137,6 +139,22 @@ struct Layer {
TextureVertex* mesh;
uint16_t* meshIndices;
GLsizei meshElementCount;
/**
* If set to true (by default), the layer can be reused.
*/
bool isCacheable;
/**
* When set to true, this layer must be treated as a texture
* layer.
*/
bool isTextureLayer;
/**
* Optional texture coordinates transform.
*/
mat4 texTransform;
}; // struct Layer
}; // namespace uirenderer

View File

@@ -154,6 +154,8 @@ bool LayerCache::resize(Layer* layer, const uint32_t width, const uint32_t heigh
}
bool LayerCache::put(Layer* layer) {
if (!layer->isCacheable) return false;
const uint32_t size = layer->width * layer->height * 4;
// Don't even try to cache a layer that's bigger than the cache
if (size < mMaxSize) {

View File

@@ -20,6 +20,7 @@
#include "LayerCache.h"
#include "LayerRenderer.h"
#include "Matrix.h"
#include "Properties.h"
#include "Rect.h"
@@ -165,6 +166,40 @@ void LayerRenderer::generateMesh() {
// Layers management
///////////////////////////////////////////////////////////////////////////////
Layer* LayerRenderer::createTextureLayer() {
LAYER_RENDERER_LOGD("Creating new texture layer");
Layer* layer = new Layer(0, 0);
layer->isCacheable = false;
layer->isTextureLayer = true;
layer->blend = true;
layer->empty = true;
layer->fbo = 0;
layer->colorFilter = NULL;
layer->fbo = 0;
layer->layer.set(0.0f, 0.0f, 0.0f, 0.0f);
layer->texCoords.set(0.0f, 1.0f, 0.0f, 1.0f);
layer->alpha = 255;
layer->mode = SkXfermode::kSrcOver_Mode;
layer->colorFilter = NULL;
layer->region.clear();
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &layer->texture);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, layer->texture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
return layer;
}
Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) {
LAYER_RENDERER_LOGD("Creating new layer %dx%d", width, height);
@@ -244,6 +279,18 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) {
return true;
}
void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
float* transform) {
if (layer) {
layer->width = width;
layer->height = height;
layer->layer.set(0.0f, 0.0f, width, height);
layer->region.set(width, height);
layer->regionRect.set(0.0f, 0.0f, width, height);
layer->texTransform.load(transform);
}
}
void LayerRenderer::destroyLayer(Layer* layer) {
if (layer) {
LAYER_RENDERER_LOGD("Destroying layer, fbo = %d", layer->fbo);

View File

@@ -53,8 +53,11 @@ public:
Region* getRegion();
GLint getTargetFbo();
static Layer* createTextureLayer();
static Layer* createLayer(uint32_t width, uint32_t height, bool isOpaque = false);
static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height);
static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
float* transform);
static void destroyLayer(Layer* layer);
static void destroyLayerDeferred(Layer* layer);

View File

@@ -633,15 +633,43 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
}
}
void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
float alpha = layer->alpha / 255.0f;
setupDraw();
setupDrawWithExternalTexture();
setupDrawColor(alpha, alpha, alpha, alpha);
setupDrawColorFilter();
setupDrawBlending(layer->blend, layer->mode);
setupDrawProgram();
setupDrawModelView(rect.left, rect.top, rect.right, rect.bottom);
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms();
setupDrawExternalTexture(layer->texture);
setupDrawTextureTransform(layer->texTransform);
setupDrawMesh(&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
finishDrawTexture();
}
void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
const Rect& texCoords = layer->texCoords;
resetDrawTextureTexCoords(texCoords.left, texCoords.top, texCoords.right, texCoords.bottom);
if (!layer->isTextureLayer) {
const Rect& texCoords = layer->texCoords;
resetDrawTextureTexCoords(texCoords.left, texCoords.top,
texCoords.right, texCoords.bottom);
drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture,
layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0],
&mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, swap, swap);
drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture,
layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0],
&mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, swap, swap);
resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
} else {
resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f);
drawTextureLayer(layer, rect);
resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
}
}
void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
@@ -882,6 +910,10 @@ void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) {
mDescription.hasAlpha8Texture = isAlpha8;
}
void OpenGLRenderer::setupDrawWithExternalTexture() {
mDescription.hasExternalTexture = true;
}
void OpenGLRenderer::setupDrawAALine() {
mDescription.hasWidth = true;
}
@@ -1055,6 +1087,19 @@ void OpenGLRenderer::setupDrawTexture(GLuint texture) {
glEnableVertexAttribArray(mTexCoordsSlot);
}
void OpenGLRenderer::setupDrawExternalTexture(GLuint texture) {
bindExternalTexture(texture);
glUniform1i(mCaches.currentProgram->getUniform("sampler"), mTextureUnit++);
mTexCoordsSlot = mCaches.currentProgram->getAttrib("texCoords");
glEnableVertexAttribArray(mTexCoordsSlot);
}
void OpenGLRenderer::setupDrawTextureTransform(mat4& transform) {
glUniformMatrix4fv(mCaches.currentProgram->getUniform("mainTextureTransform"), 1,
GL_FALSE, &transform.data[0]);
}
void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
if (!vertices) {
mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);

View File

@@ -248,6 +248,8 @@ private:
*/
void composeLayerRect(Layer* layer, const Rect& rect, bool swap = false);
void drawTextureLayer(Layer* layer, const Rect& rect);
/**
* Mark the layer as dirty at the specified coordinates. The coordinates
* are transformed with the supplied matrix.
@@ -386,6 +388,14 @@ private:
glBindTexture(GL_TEXTURE_2D, texture);
}
/**
* Binds the specified EGLImage texture. The texture unit must have been selected
* prior to calling this method.
*/
inline void bindExternalTexture(GLuint texture) {
glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
}
/**
* Sets the wrap modes for the specified texture. The wrap modes are modified
* only when needed.
@@ -425,6 +435,7 @@ private:
* Various methods to setup OpenGL rendering.
*/
void setupDrawWithTexture(bool isAlpha8 = false);
void setupDrawWithExternalTexture();
void setupDrawAALine();
void setupDrawPoint(float pointSize);
void setupDrawColor(int color);
@@ -453,6 +464,8 @@ private:
void setupDrawColorFilterUniforms();
void setupDrawSimpleMesh();
void setupDrawTexture(GLuint texture);
void setupDrawExternalTexture(GLuint texture);
void setupDrawTextureTransform(mat4& transform);
void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0);
void setupDrawVertices(GLvoid* vertices);
void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, float strokeWidth);

View File

@@ -41,6 +41,8 @@ const char* gVS_Header_Attributes_TexCoords =
"attribute vec2 texCoords;\n";
const char* gVS_Header_Attributes_Distance =
"attribute float vtxDistance;\n";
const char* gVS_Header_Uniforms_TextureTransform =
"uniform mat4 mainTextureTransform;\n";
const char* gVS_Header_Uniforms =
"uniform mat4 transform;\n";
const char* gVS_Header_Uniforms_IsPoint =
@@ -76,6 +78,8 @@ const char* gVS_Main =
"\nvoid main(void) {\n";
const char* gVS_Main_OutTexCoords =
" outTexCoords = texCoords;\n";
const char* gVS_Main_OutTransformedTexCoords =
" outTexCoords = (mainTextureTransform * vec4(texCoords, 0.0, 1.0)).xy;\n";
const char* gVS_Main_OutGradient[3] = {
// Linear
" linear = vec2((screenSpace * position).x, 0.5);\n",
@@ -103,6 +107,8 @@ const char* gVS_Footer =
const char* gFS_Header_Extension_FramebufferFetch =
"#extension GL_NV_shader_framebuffer_fetch : enable\n\n";
const char* gFS_Header_Extension_ExternalTexture =
"#extension GL_OES_EGL_image_external : require\n\n";
const char* gFS_Header =
"precision mediump float;\n\n";
const char* gFS_Uniforms_Color =
@@ -116,6 +122,8 @@ const char* gFS_Header_Uniforms_PointHasBitmap =
"uniform float pointSize;\n";
const char* gFS_Uniforms_TextureSampler =
"uniform sampler2D sampler;\n";
const char* gFS_Uniforms_ExternalTextureSampler =
"uniform samplerExternalOES sampler;\n";
const char* gFS_Uniforms_GradientSampler[3] = {
// Linear
"uniform sampler2D gradientSampler;\n",
@@ -369,7 +377,7 @@ Program* ProgramCache::generateProgram(const ProgramDescription& description, pr
String8 ProgramCache::generateVertexShader(const ProgramDescription& description) {
// Add attributes
String8 shader(gVS_Header_Attributes);
if (description.hasTexture) {
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Attributes_TexCoords);
}
if (description.hasWidth) {
@@ -377,6 +385,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
}
// Uniforms
shader.append(gVS_Header_Uniforms);
if (description.hasExternalTexture) {
shader.append(gVS_Header_Uniforms_TextureTransform);
}
if (description.hasGradient) {
shader.append(gVS_Header_Uniforms_HasGradient[description.gradientType]);
}
@@ -387,7 +398,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
shader.append(gVS_Header_Uniforms_IsPoint);
}
// Varyings
if (description.hasTexture) {
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Varyings_HasTexture);
}
if (description.hasWidth) {
@@ -407,6 +418,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
if (description.hasTexture) {
shader.append(gVS_Main_OutTexCoords);
}
if (description.hasExternalTexture) {
shader.append(gVS_Main_OutTransformedTexCoords);
}
if (description.hasWidth) {
shader.append(gVS_Main_Width);
}
@@ -440,11 +454,14 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (blendFramebuffer) {
shader.append(gFS_Header_Extension_FramebufferFetch);
}
if (description.hasExternalTexture) {
shader.append(gFS_Header_Extension_ExternalTexture);
}
shader.append(gFS_Header);
// Varyings
if (description.hasTexture) {
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Varyings_HasTexture);
}
if (description.hasWidth) {
@@ -461,7 +478,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
// Uniforms
int modulateOp = MODULATE_OP_NO_MODULATE;
const bool singleColor = !description.hasTexture &&
const bool singleColor = !description.hasTexture && !description.hasExternalTexture &&
!description.hasGradient && !description.hasBitmap;
if (description.modulate || singleColor) {
@@ -471,6 +488,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
if (description.hasTexture) {
shader.append(gFS_Uniforms_TextureSampler);
}
if (description.hasExternalTexture) {
shader.append(gFS_Uniforms_ExternalTextureSampler);
}
if (description.hasWidth) {
shader.append(gFS_Uniforms_Width);
}
@@ -487,11 +507,11 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
bool fast = false;
const bool noShader = !description.hasGradient && !description.hasBitmap;
const bool singleTexture = description.hasTexture &&
const bool singleTexture = (description.hasTexture || description.hasExternalTexture) &&
!description.hasAlpha8Texture && noShader;
const bool singleA8Texture = description.hasTexture &&
description.hasAlpha8Texture && noShader;
const bool singleGradient = !description.hasTexture &&
const bool singleGradient = !description.hasTexture && !description.hasExternalTexture &&
description.hasGradient && !description.hasBitmap &&
description.gradientType == ProgramDescription::kGradientLinear;
@@ -554,7 +574,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
// Begin the shader
shader.append(gFS_Main); {
// Stores the result in fragColor directly
if (description.hasTexture) {
if (description.hasTexture || description.hasExternalTexture) {
if (description.hasAlpha8Texture) {
if (!description.hasGradient && !description.hasBitmap) {
shader.append(gFS_Main_FetchA8Texture[modulateOp]);

View File

@@ -77,6 +77,8 @@ namespace uirenderer {
#define PROGRAM_HAS_WIDTH_SHIFT 37
#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
///////////////////////////////////////////////////////////////////////////////
// Types
///////////////////////////////////////////////////////////////////////////////
@@ -113,6 +115,7 @@ struct ProgramDescription {
// Texturing
bool hasTexture;
bool hasAlpha8Texture;
bool hasExternalTexture;
// Modulate, this should only be set when setColor() return true
bool modulate;
@@ -151,6 +154,7 @@ struct ProgramDescription {
void reset() {
hasTexture = false;
hasAlpha8Texture = false;
hasExternalTexture = false;
hasWidth = false;
@@ -240,6 +244,7 @@ struct ProgramDescription {
if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT;
if (hasWidth) key |= programid(0x1) << PROGRAM_HAS_WIDTH_SHIFT;
if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
return key;
}

View File

@@ -18,12 +18,26 @@
package="com.android.test.hwui">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-sdk android:minSdkVersion="11" />
<application
android:label="HwUi"
android:hardwareAccelerated="true">
<activity
android:name="TextureViewActivity"
android:label="_TextureView">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="BitmapMeshActivity"
android:label="_BitmapMesh">

View File

@@ -0,0 +1,87 @@
/*
* 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.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.os.Bundle;
import android.view.Gravity;
import android.view.TextureView;
import android.view.View;
import android.widget.FrameLayout;
import java.io.IOException;
@SuppressWarnings({"UnusedDeclaration"})
public class TextureViewActivity extends Activity implements TextureView.SurfaceTextureListener {
private Camera mCamera;
private TextureView mTextureView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTextureView = new TextureView(this);
mTextureView.setSurfaceTextureListener(this);
setContentView(mTextureView, new FrameLayout.LayoutParams(500, 400, Gravity.CENTER));
}
@Override
protected void onDestroy() {
super.onDestroy();
mCamera.stopPreview();
mCamera.release();
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface) {
mCamera = Camera.open();
try {
mCamera.setPreviewTexture(surface);
} catch (IOException t) {
android.util.Log.e("TextureView", "Cannot set preview texture target!", t);
}
mCamera.startPreview();
mTextureView.setCameraDistance(5000);
ObjectAnimator animator = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f);
animator.setRepeatMode(ObjectAnimator.REVERSE);
animator.setRepeatCount(ObjectAnimator.INFINITE);
animator.setDuration(4000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
((View) mTextureView.getParent()).invalidate();
}
});
animator.start();
animator = ObjectAnimator.ofFloat(mTextureView, "alpha", 1.0f, 0.0f);
animator.setRepeatMode(ObjectAnimator.REVERSE);
animator.setRepeatCount(ObjectAnimator.INFINITE);
animator.setDuration(4000);
animator.start();
}
}