Update 9patch structure when rendering with different divs/colors.
Bug #3221488

Change-Id: Ifc9e42a991d630feadc9e8032322f37504d09d6d
diff --git a/libs/hwui/Line.h b/libs/hwui/Line.h
index 5c6f3d8..264fd19 100644
--- a/libs/hwui/Line.h
+++ b/libs/hwui/Line.h
@@ -58,6 +58,8 @@
         mXDivs[0] = mYDivs[0] = 2;
         mXDivs[1] = mYDivs[1] = 3;
 
+        mPatch->copy(mXDivs, mYDivs);
+
         glGenTextures(1, &mTexture);
         glBindTexture(GL_TEXTURE_2D, mTexture);
 
@@ -89,8 +91,7 @@
         const float half = lineWidth * 0.5f;
 
         mPatch->updateVertices(gLineTextureWidth, gLineTextureHeight,
-                -gLineAABias, -half - gLineAABias, length + gLineAABias, half + gLineAABias,
-                mXDivs, mYDivs, mXDivsCount, mYDivsCount);
+                -gLineAABias, -half - gLineAABias, length + gLineAABias, half + gLineAABias);
 
         tx = -gLineAABias;
         ty = lineWidth <= 1.0f ? -gLineAABias : -half - gLineAABias;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index ad72584..60343e0 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -952,6 +952,9 @@
                 mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset,
                 GL_TRIANGLES, mesh->verticesCount, false, false, mesh->meshBuffer,
                 true, !mesh->hasEmptyQuads);
+    } else {
+        PATCH_LOGD("Invisible 9patch, ignoring (%.2f, %.2f, %.2f, %.2f)",
+                left, top, right, bottom);
     }
 }
 
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 9b2d476..7ca289d 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -30,32 +30,81 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
-Patch::Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads) {
+Patch::Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads):
+        mXCount(xCount), mYCount(yCount) {
     // 2 triangles per patch, 3 vertices per triangle
     verticesCount = ((xCount + 1) * (yCount + 1) - emptyQuads) * 2 * 3;
     mVertices = new TextureVertex[verticesCount];
     hasEmptyQuads = emptyQuads > 0;
+    mUploaded = false;
+
+    mColorKey = 0;
+    mXDivs = new int32_t[mXCount];
+    mYDivs = new int32_t[mYCount];
 
     glGenBuffers(1, &meshBuffer);
 }
 
 Patch::~Patch() {
     delete[] mVertices;
+    delete[] mXDivs;
+    delete[] mYDivs;
     glDeleteBuffers(1, &meshBuffer);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// Patch management
+///////////////////////////////////////////////////////////////////////////////
+
+void Patch::copy(const int32_t* xDivs, const int32_t* yDivs) {
+    memcpy(mXDivs, xDivs, mXCount * sizeof(int32_t));
+    memcpy(mYDivs, yDivs, mYCount * sizeof(int32_t));
+}
+
+void Patch::copy(const int32_t* yDivs) {
+    memcpy(mYDivs, yDivs, mYCount * sizeof(int32_t));
+}
+
+void Patch::updateColorKey(const uint32_t colorKey) {
+    mColorKey = colorKey;
+}
+
+bool Patch::matches(const int32_t* xDivs, const int32_t* yDivs, const uint32_t colorKey) {
+    if (mColorKey != colorKey) {
+        updateColorKey(colorKey);
+        copy(xDivs, yDivs);
+        return false;
+    }
+
+    for (uint32_t i = 0; i < mXCount; i++) {
+        if (mXDivs[i] != xDivs[i]) {
+            // The Y divs may or may not match, copy everything
+            copy(xDivs, yDivs);
+            return false;
+        }
+    }
+
+    for (uint32_t i = 0; i < mYCount; i++) {
+        if (mYDivs[i] != yDivs[i]) {
+            // We know all the X divs match, copy only Y divs
+            copy(yDivs);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Vertices management
 ///////////////////////////////////////////////////////////////////////////////
 
 void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
-        float left, float top, float right, float bottom,
-        const int32_t* xDivs, const int32_t* yDivs,
-        const uint32_t width, const uint32_t height, const uint32_t colorKey) {
+        float left, float top, float right, float bottom) {
     if (hasEmptyQuads) quads.clear();
 
-    const uint32_t xStretchCount = (width + 1) >> 1;
-    const uint32_t yStretchCount = (height + 1) >> 1;
+    const uint32_t xStretchCount = (mXCount + 1) >> 1;
+    const uint32_t yStretchCount = (mYCount + 1) >> 1;
 
     float stretchX = 0.0f;
     float stretchY = 0.0;
@@ -64,8 +113,8 @@
 
     if (xStretchCount > 0) {
         uint32_t stretchSize = 0;
-        for (uint32_t i = 1; i < width; i += 2) {
-            stretchSize += xDivs[i] - xDivs[i - 1];
+        for (uint32_t i = 1; i < mXCount; i += 2) {
+            stretchSize += mXDivs[i] - mXDivs[i - 1];
         }
         const float xStretchTex = stretchSize;
         const float fixed = bitmapWidth - stretchSize;
@@ -75,8 +124,8 @@
 
     if (yStretchCount > 0) {
         uint32_t stretchSize = 0;
-        for (uint32_t i = 1; i < height; i += 2) {
-            stretchSize += yDivs[i] - yDivs[i - 1];
+        for (uint32_t i = 1; i < mYCount; i += 2) {
+            stretchSize += mYDivs[i] - mYDivs[i - 1];
         }
         const float yStretchTex = stretchSize;
         const float fixed = bitmapHeight - stretchSize;
@@ -92,8 +141,8 @@
     float y1 = 0.0f;
     float v1 = 0.0f;
 
-    for (uint32_t i = 0; i < height; i++) {
-        float stepY = yDivs[i];
+    for (uint32_t i = 0; i < mYCount; i++) {
+        float stepY = mYDivs[i];
 
         float y2 = 0.0f;
         if (i & 1) {
@@ -104,8 +153,7 @@
         }
         float v2 = fmax(0.0f, stepY - 0.5f) / bitmapHeight;
 
-        generateRow(vertex, y1, y2, v1, v2, xDivs, width, stretchX,
-                right - left, bitmapWidth, quadCount, colorKey);
+        generateRow(vertex, y1, y2, v1, v2, stretchX, right - left, bitmapWidth, quadCount);
 
         y1 = y2;
         v1 = (stepY + 0.5f) / bitmapHeight;
@@ -113,25 +161,30 @@
         previousStepY = stepY;
     }
 
-    generateRow(vertex, y1, bottom - top, v1, 1.0f, xDivs, width, stretchX,
-            right - left, bitmapWidth, quadCount, colorKey);
+    generateRow(vertex, y1, bottom - top, v1, 1.0f, stretchX, right - left,
+            bitmapWidth, quadCount);
 
     Caches::getInstance().bindMeshBuffer(meshBuffer);
-    glBufferData(GL_ARRAY_BUFFER, sizeof(TextureVertex) * verticesCount,
-                mVertices, GL_STATIC_DRAW);
+    if (!mUploaded) {
+        glBufferData(GL_ARRAY_BUFFER, sizeof(TextureVertex) * verticesCount,
+                mVertices, GL_DYNAMIC_DRAW);
+        mUploaded = true;
+    } else {
+        glBufferSubData(GL_ARRAY_BUFFER, 0,
+                sizeof(TextureVertex) * verticesCount, mVertices);
+    }
 }
 
 void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, float v2,
-        const int32_t xDivs[], uint32_t xCount, float stretchX, float width, float bitmapWidth,
-        uint32_t& quadCount, const uint32_t colorKey) {
+        float stretchX, float width, float bitmapWidth, uint32_t& quadCount) {
     float previousStepX = 0.0f;
 
     float x1 = 0.0f;
     float u1 = 0.0f;
 
     // Generate the row quad by quad
-    for (uint32_t i = 0; i < xCount; i++) {
-        float stepX = xDivs[i];
+    for (uint32_t i = 0; i < mXCount; i++) {
+        float stepX = mXDivs[i];
 
         float x2 = 0.0f;
         if (i & 1) {
@@ -142,7 +195,7 @@
         }
         float u2 = fmax(0.0f, stepX - 0.5f) / bitmapWidth;
 
-        generateQuad(vertex, x1, y1, x2, y2, u1, v1, u2, v2, quadCount, colorKey);
+        generateQuad(vertex, x1, y1, x2, y2, u1, v1, u2, v2, quadCount);
 
         x1 = x2;
         u1 = (stepX + 0.5f) / bitmapWidth;
@@ -150,12 +203,12 @@
         previousStepX = stepX;
     }
 
-    generateQuad(vertex, x1, y1, width, y2, u1, v1, 1.0f, v2, quadCount, colorKey);
+    generateQuad(vertex, x1, y1, width, y2, u1, v1, 1.0f, v2, quadCount);
 }
 
 void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2,
-            float u1, float v1, float u2, float v2, uint32_t& quadCount, const uint32_t colorKey) {
-    if (((colorKey >> quadCount++) & 0x1) == 1) {
+            float u1, float v1, float u2, float v2, uint32_t& quadCount) {
+    if (((mColorKey >> quadCount++) & 0x1) == 1) {
         return;
     }
 
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
index 1e78b2f..2cb8ecb 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -35,55 +35,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
- * Description of a patch.
- */
-struct PatchDescription {
-    PatchDescription(): bitmapWidth(0), bitmapHeight(0), pixelWidth(0), pixelHeight(0),
-            xCount(0), yCount(0), emptyCount(0), colorKey(0) { }
-    PatchDescription(const float bitmapWidth, const float bitmapHeight,
-            const float pixelWidth, const float pixelHeight,
-            const uint32_t xCount, const uint32_t yCount,
-            const int8_t emptyCount, const uint32_t colorKey):
-            bitmapWidth(bitmapWidth), bitmapHeight(bitmapHeight),
-            pixelWidth(pixelWidth), pixelHeight(pixelHeight),
-            xCount(xCount), yCount(yCount),
-            emptyCount(emptyCount), colorKey(colorKey) { }
-    PatchDescription(const PatchDescription& description):
-            bitmapWidth(description.bitmapWidth), bitmapHeight(description.bitmapHeight),
-            pixelWidth(description.pixelWidth), pixelHeight(description.pixelHeight),
-            xCount(description.xCount), yCount(description.yCount),
-            emptyCount(description.emptyCount), colorKey(description.colorKey) { }
-
-    float bitmapWidth;
-    float bitmapHeight;
-    float pixelWidth;
-    float pixelHeight;
-    uint32_t xCount;
-    uint32_t yCount;
-    int8_t emptyCount;
-    uint32_t colorKey;
-
-    bool operator<(const PatchDescription& rhs) const {
-        LTE_FLOAT(bitmapWidth) {
-            LTE_FLOAT(bitmapHeight) {
-                LTE_FLOAT(pixelWidth) {
-                    LTE_FLOAT(pixelHeight) {
-                        LTE_INT(xCount) {
-                            LTE_INT(yCount) {
-                                LTE_INT(emptyCount) {
-                                    LTE_INT(colorKey) return false;
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return false;
-    }
-}; // struct PatchDescription
-
-/**
  * An OpenGL patch. This contains an array of vertices and an array of
  * indices to render the vertices.
  */
@@ -92,10 +43,11 @@
     ~Patch();
 
     void updateVertices(const float bitmapWidth, const float bitmapHeight,
-            float left, float top, float right, float bottom,
-            const int32_t* xDivs, const int32_t* yDivs,
-            const uint32_t width, const uint32_t height,
-            const uint32_t colorKey = 0);
+            float left, float top, float right, float bottom);
+
+    void updateColorKey(const uint32_t colorKey);
+    void copy(const int32_t* xDivs, const int32_t* yDivs);
+    bool matches(const int32_t* xDivs, const int32_t* yDivs, const uint32_t colorKey);
 
     GLuint meshBuffer;
     uint32_t verticesCount;
@@ -104,15 +56,23 @@
 
 private:
     TextureVertex* mVertices;
+    bool mUploaded;
+
+    uint32_t mXCount;
+    int32_t* mXDivs;
+    uint32_t mYCount;
+    int32_t* mYDivs;
+    uint32_t mColorKey;
+
+    void copy(const int32_t* yDivs);
 
     void generateRow(TextureVertex*& vertex, float y1, float y2,
-            float v1, float v2, const int32_t xDivs[], uint32_t xCount,
-            float stretchX, float width, float bitmapWidth,
-            uint32_t& quadCount, const uint32_t colorKey);
+            float v1, float v2, float stretchX, float width, float bitmapWidth,
+            uint32_t& quadCount);
     void generateQuad(TextureVertex*& vertex,
             float x1, float y1, float x2, float y2,
             float u1, float v1, float u2, float v2,
-            uint32_t& quadCount, const uint32_t colorKey);
+            uint32_t& quadCount);
 }; // struct Patch
 
 }; // namespace uirenderer
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index 71bab91..9702c3d 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -87,8 +87,9 @@
                 width, height, pixelWidth, pixelHeight, bitmapWidth, bitmapHeight);
 
         mesh = new Patch(width, height, transparentQuads);
-        mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f,
-                pixelWidth, pixelHeight, xDivs, yDivs, width, height, colorKey);
+        mesh->updateColorKey(colorKey);
+        mesh->copy(xDivs, yDivs);
+        mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f, pixelWidth, pixelHeight);
 
         if (mCache.size() >= mMaxEntries) {
             delete mCache.valueAt(mCache.size() - 1);
@@ -96,6 +97,9 @@
         }
 
         mCache.add(description, mesh);
+    } else if (!mesh->matches(xDivs, yDivs, colorKey)) {
+        PATCH_LOGD("Patch mesh does not match, refreshing vertices");
+        mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f, pixelWidth, pixelHeight);
     }
 
     return mesh;
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index c38cd99..951fba3 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -61,8 +61,65 @@
     }
 
 private:
+    /**
+     * Description of a patch.
+     */
+    struct PatchDescription {
+        PatchDescription(): bitmapWidth(0), bitmapHeight(0), pixelWidth(0), pixelHeight(0),
+                xCount(0), yCount(0), emptyCount(0), colorKey(0) {
+        }
+
+        PatchDescription(const float bitmapWidth, const float bitmapHeight,
+                const float pixelWidth, const float pixelHeight,
+                const uint32_t xCount, const uint32_t yCount,
+                const int8_t emptyCount, const uint32_t colorKey):
+                bitmapWidth(bitmapWidth), bitmapHeight(bitmapHeight),
+                pixelWidth(pixelWidth), pixelHeight(pixelHeight),
+                xCount(xCount), yCount(yCount),
+                emptyCount(emptyCount), colorKey(colorKey) {
+        }
+
+        PatchDescription(const PatchDescription& description):
+                bitmapWidth(description.bitmapWidth), bitmapHeight(description.bitmapHeight),
+                pixelWidth(description.pixelWidth), pixelHeight(description.pixelHeight),
+                xCount(description.xCount), yCount(description.yCount),
+                emptyCount(description.emptyCount), colorKey(description.colorKey) {
+        }
+
+        bool operator<(const PatchDescription& rhs) const {
+            LTE_FLOAT(bitmapWidth) {
+                LTE_FLOAT(bitmapHeight) {
+                    LTE_FLOAT(pixelWidth) {
+                        LTE_FLOAT(pixelHeight) {
+                            LTE_INT(xCount) {
+                                LTE_INT(yCount) {
+                                    LTE_INT(emptyCount) {
+                                        LTE_INT(colorKey) return false;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+
+    private:
+        float bitmapWidth;
+        float bitmapHeight;
+        float pixelWidth;
+        float pixelHeight;
+        uint32_t xCount;
+        uint32_t yCount;
+        int8_t emptyCount;
+        uint32_t colorKey;
+
+    }; // struct PatchDescription
+
     uint32_t mMaxEntries;
     KeyedVector<PatchDescription, Patch*> mCache;
+
 }; // class PatchCache
 
 }; // namespace uirenderer