Make sure atlas antries can correctly filter/wrap textures

The virtual textures would each have their own values for wrapping
and filtering which could lead to conflict and/or extraneous GL
commands being issued.

Change-Id: I64cb59a03e598f46bf645bd1d30fccfa63a07431
diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp
index 4d2fc014..8511e7c 100644
--- a/libs/hwui/AssetAtlas.cpp
+++ b/libs/hwui/AssetAtlas.cpp
@@ -31,11 +31,12 @@
     }
 
     mImage = new Image(buffer);
-    mTexture = mImage->getTexture();
 
-    if (mTexture) {
-        mWidth = buffer->getWidth();
-        mHeight = buffer->getHeight();
+    if (mImage->getTexture()) {
+        mTexture = new Texture();
+        mTexture->id = mImage->getTexture();
+        mTexture->width = buffer->getWidth();
+        mTexture->height = buffer->getHeight();
 
         createEntries(map, count);
     } else {
@@ -43,6 +44,7 @@
 
         delete mImage;
         mImage = NULL;
+        mTexture = NULL;
     }
 }
 
@@ -51,12 +53,13 @@
         delete mImage;
         mImage = NULL;
 
+        delete mTexture;
+        mTexture = NULL;
+
         for (size_t i = 0; i < mEntries.size(); i++) {
             delete mEntries.valueAt(i);
         }
         mEntries.clear();
-
-        mWidth = mHeight = 0;
     }
 }
 
@@ -71,13 +74,36 @@
 
 Texture* AssetAtlas::getEntryTexture(SkBitmap* const bitmap) const {
     ssize_t index = mEntries.indexOfKey(bitmap);
-    return index >= 0 ? &mEntries.valueAt(index)->texture : NULL;
+    return index >= 0 ? mEntries.valueAt(index)->texture : NULL;
 }
 
 /**
+ * Delegates changes to wrapping and filtering to the base atlas texture
+ * instead of applying the changes to the virtual textures.
+ */
+struct DelegateTexture: public Texture {
+    DelegateTexture(Texture* delegate): Texture(), mDelegate(delegate) { }
+
+    virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false,
+            bool force = false, GLenum renderTarget = GL_TEXTURE_2D) {
+        mDelegate->setWrapST(wrapS, wrapT, bindTexture, force, renderTarget);
+    }
+
+    virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false,
+            bool force = false, GLenum renderTarget = GL_TEXTURE_2D) {
+        mDelegate->setFilterMinMag(min, mag, bindTexture, force, renderTarget);
+    }
+private:
+    Texture* const mDelegate;
+}; // struct DelegateTexture
+
+/**
  * TODO: This method does not take the rotation flag into account
  */
 void AssetAtlas::createEntries(int* map, int count) {
+    const float width = float(mTexture->width);
+    const float height = float(mTexture->height);
+
     for (int i = 0; i < count; ) {
         SkBitmap* bitmap = (SkBitmap*) map[i++];
         int x = map[i++];
@@ -88,15 +114,17 @@
         if (!bitmap) continue;
 
         const UvMapper mapper(
-                x / (float) mWidth, (x + bitmap->width()) / (float) mWidth,
-                y / (float) mHeight, (y + bitmap->height()) / (float) mHeight);
+                x / width, (x + bitmap->width()) / width,
+                y / height, (y + bitmap->height()) / height);
 
-        Entry* entry = new Entry(bitmap, x, y, rotated, mapper, *this);
-        entry->texture.id = mTexture;
-        entry->texture.blend = !bitmap->isOpaque();
-        entry->texture.width = bitmap->width();
-        entry->texture.height = bitmap->height();
-        entry->texture.uvMapper = &entry->uvMapper;
+        Texture* texture = new DelegateTexture(mTexture);
+        Entry* entry = new Entry(bitmap, x, y, rotated, texture, mapper, *this);
+
+        texture->id = mTexture->id;
+        texture->blend = !bitmap->isOpaque();
+        texture->width = bitmap->width();
+        texture->height = bitmap->height();
+        texture->uvMapper = &entry->uvMapper;
 
         mEntries.add(entry->bitmap, entry);
     }
diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h
index 0bbd2a7..793e300 100644
--- a/libs/hwui/AssetAtlas.h
+++ b/libs/hwui/AssetAtlas.h
@@ -64,6 +64,13 @@
          */
         bool rotated;
 
+        /*
+         * A "virtual texture" object that represents the texture
+         * this entry belongs to. This texture should never be
+         * modified.
+         */
+        Texture* texture;
+
         /**
          * Maps texture coordinates in the [0..1] range into the
          * correct range to sample this entry from the atlas.
@@ -75,22 +82,20 @@
          */
         const AssetAtlas& atlas;
 
-        /*
-         * A "virtual texture" object that represents the texture
-         * this entry belongs to. This texture should never be
-         * modified.
-         */
-        Texture texture;
-
     private:
         Entry(SkBitmap* bitmap, int x, int y, bool rotated,
-                const UvMapper& mapper, const AssetAtlas& atlas):
-                bitmap(bitmap), x(x), y(y), rotated(rotated), uvMapper(mapper), atlas(atlas) { }
+                Texture* texture, const UvMapper& mapper, const AssetAtlas& atlas):
+                bitmap(bitmap), x(x), y(y), rotated(rotated),
+                texture(texture), uvMapper(mapper), atlas(atlas) { }
+
+        ~Entry() {
+            delete texture;
+        }
 
         friend class AssetAtlas;
     };
 
-    AssetAtlas(): mWidth(0), mHeight(0), mTexture(0), mImage(NULL) { }
+    AssetAtlas(): mTexture(NULL), mImage(NULL) { }
     ~AssetAtlas() { terminate(); }
 
     /**
@@ -120,7 +125,7 @@
      * Can return 0 if the atlas is not initialized.
      */
     uint32_t getWidth() const {
-        return mWidth;
+        return mTexture ? mTexture->width : 0;
     }
 
     /**
@@ -128,7 +133,7 @@
      * Can return 0 if the atlas is not initialized.
      */
     uint32_t getHeight() const {
-        return mHeight;
+        return mTexture ? mTexture->height : 0;
     }
 
     /**
@@ -136,7 +141,7 @@
      * Can return 0 if the atlas is not initialized.
      */
     GLuint getTexture() const {
-        return mTexture;
+        return mTexture ? mTexture->id : 0;
     }
 
     /**
@@ -154,10 +159,7 @@
 private:
     void createEntries(int* map, int count);
 
-    uint32_t mWidth;
-    uint32_t mHeight;
-
-    GLuint mTexture;
+    Texture* mTexture;
     Image* mImage;
 
     KeyedVector<SkBitmap*, Entry*> mEntries;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 2465b48..f12119a 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2352,7 +2352,7 @@
 
     if (CC_LIKELY(mesh && mesh->verticesCount > 0)) {
         mCaches.activeTexture(0);
-        Texture* texture = entry ? &entry->texture : mCaches.textureCache.get(bitmap);
+        Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
         if (!texture) return DrawGlInfo::kStatusDone;
         const AutoTexture autoCleanup(texture);
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index d9ba93a..6286e94 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -1083,7 +1083,6 @@
     // No-ops start/endTiling when set
     bool mSuppressTiling;
 
-
     // If true, this renderer will setup drawing to emulate
     // an increment stencil buffer in the color buffer
     bool mCountOverdraw;
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index dd39cae..f84cd67 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -48,13 +48,15 @@
         uvMapper = NULL;
     }
 
+    virtual ~Texture() { }
+
     void setWrap(GLenum wrap, bool bindTexture = false, bool force = false,
                 GLenum renderTarget = GL_TEXTURE_2D) {
         setWrapST(wrap, wrap, bindTexture, force, renderTarget);
     }
 
-    void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false, bool force = false,
-            GLenum renderTarget = GL_TEXTURE_2D) {
+    virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false,
+            bool force = false, GLenum renderTarget = GL_TEXTURE_2D) {
 
         if (firstWrap || force || wrapS != this->wrapS || wrapT != this->wrapT) {
             firstWrap = false;
@@ -76,8 +78,8 @@
         setFilterMinMag(filter, filter, bindTexture, force, renderTarget);
     }
 
-    void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false, bool force = false,
-            GLenum renderTarget = GL_TEXTURE_2D) {
+    virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false,
+            bool force = false, GLenum renderTarget = GL_TEXTURE_2D) {
 
         if (firstFilter || force || min != minFilter || mag != magFilter) {
             firstFilter = false;