Don't blend transparent pixels when rendering layers.

With this change, the rendere keeps track of what regions are rendered into
and generates a mesh that matches these regions exactly. The mesh is used
to composite the layer on screen.

Change-Id: I1f342576b9134fb29caff7fb8f4c1da179fe956d
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 7d54d3b..bb284379 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -27,6 +27,7 @@
 
 #include "Rect.h"
 #include "SkiaColorFilter.h"
+#include "Vertex.h"
 
 namespace android {
 namespace uirenderer {
@@ -41,6 +42,14 @@
 struct Layer {
     Layer(const uint32_t layerWidth, const uint32_t layerHeight):
             width(layerWidth), height(layerHeight) {
+        mesh = NULL;
+        meshIndices = NULL;
+        meshElementCount = 0;
+    }
+
+    ~Layer() {
+        if (mesh) delete mesh;
+        if (meshIndices) delete meshIndices;
     }
 
     /**
@@ -99,6 +108,13 @@
      * Color filter used to draw this layer. Optional.
      */
     SkiaColorFilter* colorFilter;
+
+    /**
+     * If the layer can be rendered as a mesh, this is non-null.
+     */
+    TextureVertex* mesh;
+    uint16_t* meshIndices;
+    GLsizei meshElementCount;
 }; // struct Layer
 
 }; // namespace uirenderer
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index a25c95e..cd2554e 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -29,6 +29,10 @@
 void LayerRenderer::prepare(bool opaque) {
     LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->fbo);
 
+#if RENDER_LAYERS_AS_REGIONS
+    mLayer->region.clear();
+#endif
+
     glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &mPreviousFbo);
     glBindFramebuffer(GL_FRAMEBUFFER, mLayer->fbo);
 
@@ -39,11 +43,97 @@
     OpenGLRenderer::finish();
     glBindFramebuffer(GL_FRAMEBUFFER, mPreviousFbo);
 
+    generateMesh();
+
     LAYER_RENDERER_LOGD("Finished rendering into layer, fbo = %d", mLayer->mFbo);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// Static functions
+// Dirty region tracking
+///////////////////////////////////////////////////////////////////////////////
+
+bool LayerRenderer::hasLayer() {
+    return true;
+}
+
+Region* LayerRenderer::getRegion() {
+#if RENDER_LAYERS_AS_REGIONS
+    if (getSnapshot()->flags & Snapshot::kFlagFboTarget) {
+        return OpenGLRenderer::getRegion();
+    }
+    return &mLayer->region;
+#else
+    return OpenGLRenderer::getRegion();
+#endif
+}
+
+void LayerRenderer::generateMesh() {
+#if RENDER_LAYERS_AS_REGIONS
+    if (mLayer->region.isRect() || mLayer->region.isEmpty()) {
+        if (mLayer->mesh) {
+            delete mLayer->mesh;
+            delete mLayer->meshIndices;
+
+            mLayer->mesh = NULL;
+            mLayer->meshIndices = NULL;
+            mLayer->meshElementCount = 0;
+        }
+        return;
+    }
+
+    size_t count;
+    const android::Rect* rects = mLayer->region.getArray(&count);
+
+    GLsizei elementCount = count * 6;
+
+    if (mLayer->mesh && mLayer->meshElementCount < elementCount) {
+        delete mLayer->mesh;
+        delete mLayer->meshIndices;
+
+        mLayer->mesh = NULL;
+        mLayer->meshIndices = NULL;
+    }
+
+    if (!mLayer->mesh) {
+        mLayer->mesh = new TextureVertex[count * 4];
+        mLayer->meshIndices = new uint16_t[elementCount];
+        mLayer->meshElementCount = elementCount;
+    }
+
+    const float texX = 1.0f / float(mLayer->width);
+    const float texY = 1.0f / float(mLayer->height);
+    const float height = mLayer->layer.getHeight();
+
+    TextureVertex* mesh = mLayer->mesh;
+    uint16_t* indices = mLayer->meshIndices;
+
+    for (size_t i = 0; i < count; i++) {
+        const android::Rect* r = &rects[i];
+
+        const float u1 = r->left * texX;
+        const float v1 = (height - r->top) * texY;
+        const float u2 = r->right * texX;
+        const float v2 = (height - r->bottom) * texY;
+
+        TextureVertex::set(mesh++, r->left, r->top, u1, v1);
+        TextureVertex::set(mesh++, r->right, r->top, u2, v1);
+        TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
+        TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
+
+        uint16_t quad = i * 4;
+        int index = i * 6;
+        indices[index    ] = quad;       // top-left
+        indices[index + 1] = quad + 1;   // top-right
+        indices[index + 2] = quad + 2;   // bottom-left
+        indices[index + 3] = quad + 2;   // bottom-left
+        indices[index + 4] = quad + 1;   // top-right
+        indices[index + 5] = quad + 3;   // bottom-right
+    }
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Layers management
 ///////////////////////////////////////////////////////////////////////////////
 
 Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) {
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index ed5d960..f2fb898 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -49,12 +49,17 @@
     void prepare(bool opaque);
     void finish();
 
+    bool hasLayer();
+    Region* getRegion();
+
     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 destroyLayer(Layer* layer);
     static void destroyLayerDeferred(Layer* layer);
 
 private:
+    void generateMesh();
+
     Layer* mLayer;
     GLuint mPreviousFbo;
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index b933232..16a1de7 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -615,6 +615,7 @@
         const float alpha = layer->alpha / 255.0f;
         const float texX = 1.0f / float(layer->width);
         const float texY = 1.0f / float(layer->height);
+        const float height = rect.getHeight();
 
         TextureVertex* mesh = mCaches.getRegionMesh();
         GLsizei numQuads = 0;
@@ -636,9 +637,9 @@
             const android::Rect* r = &rects[i];
 
             const float u1 = r->left * texX;
-            const float v1 = (rect.getHeight() - r->top) * texY;
+            const float v1 = (height - r->top) * texY;
             const float u2 = r->right * texX;
-            const float v2 = (rect.getHeight() - r->bottom) * texY;
+            const float v2 = (height - r->bottom) * texY;
 
             // TODO: Reject quads outside of the clip
             TextureVertex::set(mesh++, r->left, r->top, u1, v1);
@@ -694,10 +695,10 @@
 void OpenGLRenderer::dirtyLayer(const float left, const float top,
         const float right, const float bottom, const mat4 transform) {
 #if RENDER_LAYERS_AS_REGIONS
-    if ((mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region) {
+    if (hasLayer()) {
         Rect bounds(left, top, right, bottom);
         transform.mapRect(bounds);
-        dirtyLayerUnchecked(bounds, mSnapshot->region);
+        dirtyLayerUnchecked(bounds, getRegion());
     }
 #endif
 }
@@ -705,9 +706,9 @@
 void OpenGLRenderer::dirtyLayer(const float left, const float top,
         const float right, const float bottom) {
 #if RENDER_LAYERS_AS_REGIONS
-    if ((mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region) {
+    if (hasLayer()) {
         Rect bounds(left, top, right, bottom);
-        dirtyLayerUnchecked(bounds, mSnapshot->region);
+        dirtyLayerUnchecked(bounds, getRegion());
     }
 #endif
 }
@@ -1419,24 +1420,20 @@
     Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
 
 #if RENDER_LAYERS_AS_REGIONS
-    bool hasLayer = (mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region;
+    bool hasActiveLayer = hasLayer();
 #else
-    bool hasLayer = false;
+    bool hasActiveLayer = false;
 #endif
 
     mCaches.unbindMeshBuffer();
     if (fontRenderer.renderText(paint, clip, text, 0, bytesCount, count, x, y,
-            hasLayer ? &bounds : NULL)) {
+            hasActiveLayer ? &bounds : NULL)) {
 #if RENDER_LAYERS_AS_REGIONS
-        if (hasLayer) {
+        if (hasActiveLayer) {
             if (!pureTranslate) {
                 mSnapshot->transform->mapRect(bounds);
             }
-            bounds.intersect(*mSnapshot->clipRect);
-            bounds.snapToPixelBoundaries();
-
-            android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
-            mSnapshot->region->orSelf(dirty);
+            dirtyLayerUnchecked(bounds, getRegion());
         }
 #endif
     }
@@ -1501,8 +1498,36 @@
     layer->alpha = alpha;
     layer->mode = mode;
 
+
+#if RENDER_LAYERS_AS_REGIONS
+    if (layer->region.isRect()) {
+        const Rect r(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight());
+        composeLayerRect(layer, r);
+    } else if (!layer->region.isEmpty() && layer->mesh) {
+        const Rect& rect = layer->layer;
+
+        setupDraw();
+        setupDrawWithTexture();
+        setupDrawColor(alpha, alpha, alpha, alpha);
+        setupDrawColorFilter();
+        setupDrawBlending(layer->blend || layer->alpha < 255, layer->mode, false);
+        setupDrawProgram();
+        setupDrawDirtyRegionsDisabled();
+        setupDrawPureColorUniforms();
+        setupDrawColorFilterUniforms();
+        setupDrawTexture(layer->texture);
+        setupDrawModelViewTranslate(rect.left, rect.top, rect.right, rect.bottom);
+        setupDrawMesh(&layer->mesh[0].position[0], &layer->mesh[0].texture[0]);
+
+        glDrawElements(GL_TRIANGLES, layer->meshElementCount,
+                GL_UNSIGNED_SHORT, layer->meshIndices);
+
+        finishDrawTexture();
+    }
+#else
     const Rect r(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight());
     composeLayerRect(layer, r);
+#endif
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 56be134..7387b92 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -133,19 +133,24 @@
     virtual void composeLayer(sp<Snapshot> current, sp<Snapshot> previous);
 
     /**
-     * Mark the layer as dirty at the specified coordinates. The coordinates
-     * are transformed with the supplied matrix.
+     * Marks the specified region as dirty at the specified bounds.
      */
-    virtual void dirtyLayer(const float left, const float top,
-            const float right, const float bottom, const mat4 transform);
+    void dirtyLayerUnchecked(Rect& bounds, Region* region);
 
     /**
-     * Mark the layer as dirty at the specified coordinates.
+     * Returns the current snapshot.
      */
-    virtual void dirtyLayer(const float left, const float top,
-            const float right, const float bottom);
+    sp<Snapshot> getSnapshot() {
+        return mSnapshot;
+    }
 
-    void dirtyLayerUnchecked(Rect& bounds, Region* region);
+    virtual Region* getRegion() {
+        return mSnapshot->region;
+    }
+
+    virtual bool hasLayer() {
+        return (mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region;
+    }
 
 private:
     /**
@@ -225,6 +230,19 @@
     void clearLayerRegions();
 
     /**
+     * Mark the layer as dirty at the specified coordinates. The coordinates
+     * are transformed with the supplied matrix.
+     */
+    void dirtyLayer(const float left, const float top,
+            const float right, const float bottom, const mat4 transform);
+
+    /**
+     * Mark the layer as dirty at the specified coordinates.
+     */
+    void dirtyLayer(const float left, const float top,
+            const float right, const float bottom);
+
+    /**
      * Draws a colored rectangle with the specified color. The specified coordinates
      * are transformed by the current snapshot's transform matrix.
      *
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 9898df4..595ad4e 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -268,7 +268,7 @@
     Rect* clipRect;
 
     /**
-     * The ancestor layer's dirty region..
+     * The ancestor layer's dirty region.
      */
     Region* region;