New widget: TextureView
Bug #4343984

TextureView can be used to render media content (video, OpenGL,
RenderScript) inside a View.

The key difference with SurfaceView is that TextureView does
not create a new Surface. This gives the ability to seamlessly
transform, animate, fade, etc. a TextureView, which was hard
if not impossible to do with a SurfaceView.
A TextureView also interacts perfectly with ScrollView,
ListView, etc. It allows application to embed media content
in a much more flexible way than before.

For instance, to render the camera preview at 50% opacity,
all you need to do is the following:

mTextureView.setAlpha(0.5f);
Camera c = Camera.open();
c.setPreviewTexture(mTextureView.getSurfaceTexture());
c.startPreview();

TextureView uses a SurfaceTexture to get the job done. More
APIs are required to make it easy to create OpenGL contexts
for a TextureView. It can currently be done with a bit of
JNI code.

Change-Id: Iaa7953097ab5beb8437bcbbfa03b2df5b7f80cd7
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 6c4a2a9..16566b8 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -45,6 +45,8 @@
         mesh = NULL;
         meshIndices = NULL;
         meshElementCount = 0;
+        isCacheable = true;
+        isTextureLayer = false;
     }
 
     ~Layer() {
@@ -137,6 +139,22 @@
     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
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index a9710ad..b2d795f 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -154,6 +154,8 @@
 }
 
 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) {
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index ca1e7ae..e167336 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -20,6 +20,7 @@
 
 #include "LayerCache.h"
 #include "LayerRenderer.h"
+#include "Matrix.h"
 #include "Properties.h"
 #include "Rect.h"
 
@@ -165,6 +166,40 @@
 // 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 @@
     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);
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index d2f565e..b3cd5db 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -53,8 +53,11 @@
     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);
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index e926d99..4dde342 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -633,15 +633,43 @@
     }
 }
 
+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 @@
     mDescription.hasAlpha8Texture = isAlpha8;
 }
 
+void OpenGLRenderer::setupDrawWithExternalTexture() {
+    mDescription.hasExternalTexture = true;
+}
+
 void OpenGLRenderer::setupDrawAALine() {
     mDescription.hasWidth = true;
 }
@@ -1055,6 +1087,19 @@
     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);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 0276095..a3e12f6 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -248,6 +248,8 @@
      */
     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.
@@ -400,6 +402,14 @@
     }
 
     /**
+     * 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.
      */
@@ -438,6 +448,7 @@
      * Various methods to setup OpenGL rendering.
      */
     void setupDrawWithTexture(bool isAlpha8 = false);
+    void setupDrawWithExternalTexture();
     void setupDrawAALine();
     void setupDrawPoint(float pointSize);
     void setupDrawColor(int color);
@@ -466,6 +477,8 @@
     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);
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 80b1917..62ac2ba 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -41,6 +41,8 @@
         "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 @@
         "\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* 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 @@
         "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 @@
 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 @@
     }
     // 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 @@
         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 @@
         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 @@
     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 @@
 
     // 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 @@
     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 @@
         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 @@
     // 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]);
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 18d98cb..70909fd 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -77,6 +77,8 @@
 
 #define PROGRAM_HAS_WIDTH_SHIFT 37
 
+#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
+
 ///////////////////////////////////////////////////////////////////////////////
 // Types
 ///////////////////////////////////////////////////////////////////////////////
@@ -113,6 +115,7 @@
     // Texturing
     bool hasTexture;
     bool hasAlpha8Texture;
+    bool hasExternalTexture;
 
     // Modulate, this should only be set when setColor() return true
     bool modulate;
@@ -151,6 +154,7 @@
     void reset() {
         hasTexture = false;
         hasAlpha8Texture = false;
+        hasExternalTexture = false;
 
         hasWidth = false;
 
@@ -240,6 +244,7 @@
         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;
     }