Enforce maximum texture size.

When an app tries to render a bitmap or path larger than the GPU's maximum
texture size, the drawing command is ignored and a warning is logged. This
change also makes texture drawing more robust by catching potential errors
during texture creation.

This change also fixes a crash in the FontRenderer. The destructor would
sometimes try to free an uninitialized array.

Change-Id: I95ae0939c52192d97b340aa02417bf6d0c962c57
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index dbea114..8d00e85 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -292,6 +292,9 @@
     mCurrentQuadIndex = 0;
     mTextureId = 0;
 
+    mTextMeshPtr = NULL;
+    mTextTexture = NULL;
+
     mIndexBufferID = 0;
 
     mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
@@ -319,10 +322,12 @@
     }
     mCacheLines.clear();
 
-    delete[] mTextMeshPtr;
-    delete[] mTextTexture;
+    if (mInitialized) {
+        delete[] mTextMeshPtr;
+        delete[] mTextTexture;
+    }
 
-    if(mTextureId) {
+    if (mTextureId) {
         glDeleteTextures(1, &mTextureId);
     }
 
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index aeda416..59fa0a7 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -80,8 +80,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 Texture* GradientCache::get(SkShader* shader) {
-    Texture* texture = mCache.get(shader);
-    return texture;
+    return mCache.get(shader);
 }
 
 void GradientCache::remove(SkShader* shader) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index db8c863..5d30b1a 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -436,6 +436,7 @@
     }
 
     const Texture* texture = mTextureCache.get(bitmap);
+    if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
     drawTextureRect(left, top, right, bottom, texture, paint);
@@ -451,6 +452,7 @@
     }
 
     const Texture* texture = mTextureCache.get(bitmap);
+    if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
     drawTextureRect(r.left, r.top, r.right, r.bottom, texture, paint);
@@ -465,6 +467,7 @@
     }
 
     const Texture* texture = mTextureCache.get(bitmap);
+    if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
     const float width = texture->width;
@@ -489,6 +492,7 @@
     }
 
     const Texture* texture = mTextureCache.get(bitmap);
+    if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
     int alpha;
@@ -617,6 +621,7 @@
     glActiveTexture(gTextureUnits[textureUnit]);
 
     const PathTexture* texture = mPathCache.get(path, paint);
+    if (!texture) return;
     const AutoTexture autoCleanup(texture);
 
     int alpha;
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index fa6ea25..4a01ffa 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -34,6 +34,10 @@
         mCache(GenerationCache<PathCacheEntry, PathTexture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(maxByteSize) {
     mCache.setOnEntryRemovedListener(this);
+
+    GLint maxTextureSize;
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+    mMaxTextureSize = maxTextureSize;
 }
 
 PathCache::~PathCache() {
@@ -94,9 +98,18 @@
 PathTexture* PathCache::addTexture(const PathCacheEntry& entry,
         const SkPath *path, const SkPaint* paint) {
     const SkRect& bounds = path->getBounds();
+
+    const float pathWidth = bounds.width();
+    const float pathHeight = bounds.height();
+
+    if (pathWidth > mMaxTextureSize || pathHeight > mMaxTextureSize) {
+        LOGW("Path too large to be rendered into a texture");
+        return NULL;
+    }
+
     const float offset = entry.strokeWidth * 1.5f;
-    const uint32_t width = uint32_t(bounds.width() + offset * 2.0 + 0.5);
-    const uint32_t height = uint32_t(bounds.height() + offset * 2.0 + 0.5);
+    const uint32_t width = uint32_t(pathWidth + offset * 2.0 + 0.5);
+    const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5);
 
     const uint32_t size = width * height;
     // Don't even try to cache a bitmap that's bigger than the cache
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 206fb49..d09789f 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -139,6 +139,7 @@
 
     uint32_t mSize;
     uint32_t mMaxSize;
+    GLuint mMaxTextureSize;
 }; // class PathCache
 
 }; // namespace uirenderer
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index ffdb348..42c0621 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -75,11 +75,13 @@
 
 SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
         SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
-        SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap) {
+        SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap), mTexture(NULL) {
 }
 
 void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
     const Texture* texture = mTextureCache->get(mBitmap);
+    if (!texture) return;
+    mTexture = texture;
 
     const float width = texture->width;
     const float height = texture->height;
@@ -98,7 +100,11 @@
         const Snapshot& snapshot, GLuint* textureUnit) {
     GLuint textureSlot = (*textureUnit)++;
     glActiveTexture(gTextureUnitsMap[textureSlot]);
-    const Texture* texture = mTextureCache->get(mBitmap);
+
+    const Texture* texture = mTexture;
+    mTexture = NULL;
+    if (!texture) return;
+    const AutoTexture autoCleanup(texture);
 
     const float width = texture->width;
     const float height = texture->height;
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index 58f2870..d95e3b0 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -116,6 +116,7 @@
     }
 
     SkBitmap* mBitmap;
+    const Texture* mTexture;
 }; // struct SkiaBitmapShader
 
 /**
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 59903b7..1cb5932 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -31,6 +31,11 @@
         mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
         mSize(0), mMaxSize(maxByteSize) {
     mCache.setOnEntryRemovedListener(this);
+
+    GLint maxTextureSize;
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+    LOGD("Maximum texture dimension is %d pixels", maxTextureSize);
+    mMaxTextureSize = maxTextureSize;
 }
 
 TextureCache::~TextureCache() {
@@ -79,6 +84,11 @@
 Texture* TextureCache::get(SkBitmap* bitmap) {
     Texture* texture = mCache.get(bitmap);
     if (!texture) {
+        if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) {
+            LOGW("Bitmap too large to be uploaded into a texture");
+            return NULL;
+        }
+
         const uint32_t size = bitmap->rowBytes() * bitmap->height();
         // Don't even try to cache a bitmap that's bigger than the cache
         if (size < mMaxSize) {
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index bed1191..c7e50a1 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -82,6 +82,7 @@
 
     uint32_t mSize;
     uint32_t mMaxSize;
+    GLuint mMaxTextureSize;
 }; // class TextureCache
 
 }; // namespace uirenderer