Merge "Merge all shapes/paths caches to PathCache" into jb-mr2-dev
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 1618110..06e658d 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -34,7 +34,6 @@
 		ProgramCache.cpp \
 		RenderBufferCache.cpp \
 		ResourceCache.cpp \
-		ShapeCache.cpp \
 		SkiaColorFilter.cpp \
 		SkiaShader.cpp \
 		Snapshot.cpp \
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 4642a4f..dc3a4e2 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -224,16 +224,6 @@
             gradientCache.getSize(), gradientCache.getMaxSize());
     log.appendFormat("  PathCache            %8d / %8d\n",
             pathCache.getSize(), pathCache.getMaxSize());
-    log.appendFormat("  CircleShapeCache     %8d / %8d\n",
-            circleShapeCache.getSize(), circleShapeCache.getMaxSize());
-    log.appendFormat("  OvalShapeCache       %8d / %8d\n",
-            ovalShapeCache.getSize(), ovalShapeCache.getMaxSize());
-    log.appendFormat("  RoundRectShapeCache  %8d / %8d\n",
-            roundRectShapeCache.getSize(), roundRectShapeCache.getMaxSize());
-    log.appendFormat("  RectShapeCache       %8d / %8d\n",
-            rectShapeCache.getSize(), rectShapeCache.getMaxSize());
-    log.appendFormat("  ArcShapeCache        %8d / %8d\n",
-            arcShapeCache.getSize(), arcShapeCache.getMaxSize());
     log.appendFormat("  TextDropShadowCache  %8d / %8d\n", dropShadowCache.getSize(),
             dropShadowCache.getMaxSize());
     for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
@@ -253,11 +243,6 @@
     total += gradientCache.getSize();
     total += pathCache.getSize();
     total += dropShadowCache.getSize();
-    total += roundRectShapeCache.getSize();
-    total += circleShapeCache.getSize();
-    total += ovalShapeCache.getSize();
-    total += rectShapeCache.getSize();
-    total += arcShapeCache.getSize();
     for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
         total += fontRenderer->getFontRendererSize(i);
     }
@@ -325,11 +310,6 @@
             fontRenderer->flush();
             textureCache.flush();
             pathCache.clear();
-            roundRectShapeCache.clear();
-            circleShapeCache.clear();
-            ovalShapeCache.clear();
-            rectShapeCache.clear();
-            arcShapeCache.clear();
             // fall through
         case kFlushMode_Layers:
             layerCache.clear();
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index dc32a7e..63836c1 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -36,7 +36,6 @@
 #include "GradientCache.h"
 #include "PatchCache.h"
 #include "ProgramCache.h"
-#include "ShapeCache.h"
 #include "PathCache.h"
 #include "TextDropShadowCache.h"
 #include "FboCache.h"
@@ -269,11 +268,6 @@
     GradientCache gradientCache;
     ProgramCache programCache;
     PathCache pathCache;
-    RoundRectShapeCache roundRectShapeCache;
-    CircleShapeCache circleShapeCache;
-    OvalShapeCache ovalShapeCache;
-    RectShapeCache rectShapeCache;
-    ArcShapeCache arcShapeCache;
     PatchCache patchCache;
     TextDropShadowCache dropShadowCache;
     FboCache fboCache;
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 773fe82..46beb94 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -64,7 +64,7 @@
 #define DEBUG_PATCHES_EMPTY_VERTICES 0
 
 // Turn on to display debug info about shapes
-#define DEBUG_SHAPES 0
+#define DEBUG_PATHS 0
 
 // Turn on to display debug info about textures
 #define DEBUG_TEXTURES 0
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 8455545..5c5bf45 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -17,6 +17,8 @@
 #define LOG_TAG "OpenGLRenderer"
 #define ATRACE_TAG ATRACE_TAG_VIEW
 
+#include <SkCanvas.h>
+
 #include <utils/Trace.h>
 
 #include "Debug.h"
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 4743f58..4944fe8 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <SkCanvas.h>
+
 #include "Debug.h"
 #include "DisplayList.h"
 #include "DisplayListOp.h"
@@ -121,9 +123,7 @@
     }
 
     for (size_t i = 0; i < mPaths.size(); i++) {
-        SkPath* path = mPaths.itemAt(i);
-        caches.pathCache.remove(path);
-        delete path;
+        delete mPaths.itemAt(i);
     }
 
     for (size_t i = 0; i < mMatrices.size(); i++) {
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 11a655e..8b5b54c 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "OpenGLRenderer"
 
 #include <SkCamera.h>
+#include <SkCanvas.h>
 
 #include <private/hwui/DrawGlInfo.h>
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 66681e0..e31f6f6 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2257,9 +2257,11 @@
     // TODO: try clipping large paths to viewport
     PathTessellator::tessellatePath(path, paint, mSnapshot->transform, vertexBuffer);
 
-    SkRect bounds = path.getBounds();
-    PathTessellator::expandBoundsForStroke(bounds, paint, false);
-    dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
+    if (hasLayer()) {
+        SkRect bounds = path.getBounds();
+        PathTessellator::expandBoundsForStroke(bounds, paint, false);
+        dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
+    }
 
     return drawVertexBuffer(vertexBuffer, paint);
 }
@@ -2389,7 +2391,7 @@
 
     if (p->getPathEffect() != 0) {
         mCaches.activeTexture(0);
-        const PathTexture* texture = mCaches.roundRectShapeCache.getRoundRect(
+        const PathTexture* texture = mCaches.pathCache.getRoundRect(
                 right - left, bottom - top, rx, ry, p);
         return drawShape(left, top, texture, p);
     }
@@ -2413,7 +2415,7 @@
     }
     if (p->getPathEffect() != 0) {
         mCaches.activeTexture(0);
-        const PathTexture* texture = mCaches.circleShapeCache.getCircle(radius, p);
+        const PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
         return drawShape(x - radius, y - radius, texture, p);
     }
 
@@ -2434,7 +2436,7 @@
 
     if (p->getPathEffect() != 0) {
         mCaches.activeTexture(0);
-        const PathTexture* texture = mCaches.ovalShapeCache.getOval(right - left, bottom - top, p);
+        const PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p);
         return drawShape(left, top, texture, p);
     }
 
@@ -2460,7 +2462,7 @@
     // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
     if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) {
         mCaches.activeTexture(0);
-        const PathTexture* texture = mCaches.arcShapeCache.getArc(right - left, bottom - top,
+        const PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top,
                 startAngle, sweepAngle, useCenter, p);
         return drawShape(left, top, texture, p);
     }
@@ -2495,7 +2497,7 @@
                 p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
             mCaches.activeTexture(0);
             const PathTexture* texture =
-                    mCaches.rectShapeCache.getRect(right - left, bottom - top, p);
+                    mCaches.pathCache.getRect(right - left, bottom - top, p);
             return drawShape(left, top, texture, p);
         }
 
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index fb687cd..490c22a 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,19 +15,301 @@
  */
 
 #define LOG_TAG "OpenGLRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
 
-#include <utils/Mutex.h>
+#include <SkBitmap.h>
+#include <SkCanvas.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+#include <SkRect.h>
 
-#include <sys/sysinfo.h>
+#include <utils/JenkinsHash.h>
+#include <utils/Trace.h>
 
 #include "Caches.h"
 #include "PathCache.h"
-#include "Properties.h"
+
+#include "thread/Signal.h"
+#include "thread/Task.h"
+#include "thread/TaskProcessor.h"
 
 namespace android {
 namespace uirenderer {
 
 ///////////////////////////////////////////////////////////////////////////////
+// Cache entries
+///////////////////////////////////////////////////////////////////////////////
+
+PathDescription::PathDescription():
+        type(kShapeNone),
+        join(SkPaint::kDefault_Join),
+        cap(SkPaint::kDefault_Cap),
+        style(SkPaint::kFill_Style),
+        miter(4.0f),
+        strokeWidth(1.0f),
+        pathEffect(NULL) {
+    memset(&shape, 0, sizeof(Shape));
+}
+
+PathDescription::PathDescription(ShapeType type, SkPaint* paint):
+        type(type),
+        join(paint->getStrokeJoin()),
+        cap(paint->getStrokeCap()),
+        style(paint->getStyle()),
+        miter(paint->getStrokeMiter()),
+        strokeWidth(paint->getStrokeWidth()),
+        pathEffect(paint->getPathEffect()) {
+    memset(&shape, 0, sizeof(Shape));
+}
+
+hash_t PathDescription::hash() const {
+    uint32_t hash = JenkinsHashMix(0, type);
+    hash = JenkinsHashMix(hash, join);
+    hash = JenkinsHashMix(hash, cap);
+    hash = JenkinsHashMix(hash, style);
+    hash = JenkinsHashMix(hash, android::hash_type(miter));
+    hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
+    hash = JenkinsHashMix(hash, android::hash_type(pathEffect));
+    hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape));
+    return JenkinsHashWhiten(hash);
+}
+
+int PathDescription::compare(const PathDescription& rhs) const {
+    return memcmp(this, &rhs, sizeof(PathDescription));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Utilities
+///////////////////////////////////////////////////////////////////////////////
+
+bool PathCache::canDrawAsConvexPath(SkPath* path, SkPaint* paint) {
+    // NOTE: This should only be used after PathTessellator handles joins properly
+    return paint->getPathEffect() == NULL && path->getConvexity() == SkPath::kConvex_Convexity;
+}
+
+void PathCache::computePathBounds(const SkPath* path, const SkPaint* paint,
+        float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
+    const SkRect& bounds = path->getBounds();
+    PathCache::computeBounds(bounds, paint, left, top, offset, width, height);
+}
+
+void PathCache::computeBounds(const SkRect& bounds, const SkPaint* paint,
+        float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
+    const float pathWidth = fmax(bounds.width(), 1.0f);
+    const float pathHeight = fmax(bounds.height(), 1.0f);
+
+    left = bounds.fLeft;
+    top = bounds.fTop;
+
+    offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
+
+    width = uint32_t(pathWidth + offset * 2.0 + 0.5);
+    height = uint32_t(pathHeight + offset * 2.0 + 0.5);
+}
+
+static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
+    bitmap.setConfig(SkBitmap::kA8_Config, width, height);
+    bitmap.allocPixels();
+    bitmap.eraseColor(0);
+}
+
+static void initPaint(SkPaint& paint) {
+    // Make sure the paint is opaque, color, alpha, filter, etc.
+    // will be applied later when compositing the alpha8 texture
+    paint.setColor(0xff000000);
+    paint.setAlpha(255);
+    paint.setColorFilter(NULL);
+    paint.setMaskFilter(NULL);
+    paint.setShader(NULL);
+    SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
+    SkSafeUnref(paint.setXfermode(mode));
+}
+
+static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
+        float left, float top, float offset, uint32_t width, uint32_t height) {
+    initBitmap(bitmap, width, height);
+
+    SkPaint pathPaint(*paint);
+    initPaint(pathPaint);
+
+    SkCanvas canvas(bitmap);
+    canvas.translate(-left + offset, -top + offset);
+    canvas.drawPath(*path, pathPaint);
+}
+
+static PathTexture* createTexture(float left, float top, float offset,
+        uint32_t width, uint32_t height, uint32_t id) {
+    PathTexture* texture = new PathTexture();
+    texture->left = left;
+    texture->top = top;
+    texture->offset = offset;
+    texture->width = width;
+    texture->height = height;
+    texture->generation = id;
+    return texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache constructor/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+PathCache::PathCache():
+        mCache(LruCache<PathDescription, PathTexture*>::kUnlimitedCapacity),
+        mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) {
+    char property[PROPERTY_VALUE_MAX];
+    if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) {
+        INIT_LOGD("  Setting %s cache size to %sMB", name, property);
+        setMaxSize(MB(atof(property)));
+    } else {
+        INIT_LOGD("  Using default %s cache size of %.2fMB", name, DEFAULT_PATH_CACHE_SIZE);
+    }
+    init();
+}
+
+PathCache::~PathCache() {
+    mCache.clear();
+}
+
+void PathCache::init() {
+    mCache.setOnEntryRemovedListener(this);
+
+    GLint maxTextureSize;
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+    mMaxTextureSize = maxTextureSize;
+
+    mDebugEnabled = readDebugLevel() & kDebugCaches;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t PathCache::getSize() {
+    return mSize;
+}
+
+uint32_t PathCache::getMaxSize() {
+    return mMaxSize;
+}
+
+void PathCache::setMaxSize(uint32_t maxSize) {
+    mMaxSize = maxSize;
+    while (mSize > mMaxSize) {
+        mCache.removeOldest();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void PathCache::operator()(PathDescription& entry, PathTexture*& texture) {
+    removeTexture(texture);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+void PathCache::removeTexture(PathTexture* texture) {
+    if (texture) {
+        const uint32_t size = texture->width * texture->height;
+        mSize -= size;
+
+        PATH_LOGD("PathCache::delete name, size, mSize = %d, %d, %d",
+                texture->id, size, mSize);
+        if (mDebugEnabled) {
+            ALOGD("Shape deleted, size = %d", size);
+        }
+
+        if (texture->id) {
+            glDeleteTextures(1, &texture->id);
+        }
+        delete texture;
+    }
+}
+
+void PathCache::purgeCache(uint32_t width, uint32_t height) {
+    const uint32_t size = width * height;
+    // Don't even try to cache a bitmap that's bigger than the cache
+    if (size < mMaxSize) {
+        while (mSize + size > mMaxSize) {
+            mCache.removeOldest();
+        }
+    }
+}
+
+void PathCache::trim() {
+    while (mSize > mMaxSize) {
+        mCache.removeOldest();
+    }
+}
+
+PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *path,
+        const SkPaint* paint) {
+    ATRACE_CALL();
+
+    float left, top, offset;
+    uint32_t width, height;
+    computePathBounds(path, paint, left, top, offset, width, height);
+
+    if (!checkTextureSize(width, height)) return NULL;
+
+    purgeCache(width, height);
+
+    SkBitmap bitmap;
+    drawPath(path, paint, bitmap, left, top, offset, width, height);
+
+    PathTexture* texture = createTexture(left, top, offset, width, height,
+            path->getGenerationID());
+    addTexture(entry, &bitmap, texture);
+
+    return texture;
+}
+
+void PathCache::addTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture) {
+    generateTexture(*bitmap, texture);
+
+    uint32_t size = texture->width * texture->height;
+    if (size < mMaxSize) {
+        mSize += size;
+        PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d",
+                texture->id, size, mSize);
+        if (mDebugEnabled) {
+            ALOGD("Shape created, size = %d", size);
+        }
+        mCache.put(entry, texture);
+    } else {
+        texture->cleanup = true;
+    }
+}
+
+void PathCache::clear() {
+    mCache.clear();
+}
+
+void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) {
+    SkAutoLockPixels alp(bitmap);
+    if (!bitmap.readyToDraw()) {
+        ALOGE("Cannot generate texture from bitmap");
+        return;
+    }
+
+    glGenTextures(1, &texture->id);
+
+    glBindTexture(GL_TEXTURE_2D, texture->id);
+    // Textures are Alpha8
+    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+    texture->blend = true;
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
+            GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
+
+    texture->setFilter(GL_LINEAR);
+    texture->setWrap(GL_CLAMP_TO_EDGE);
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Path precaching
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -52,7 +334,7 @@
 
     if (width <= mMaxTextureSize && height <= mMaxTextureSize) {
         SkBitmap* bitmap = new SkBitmap();
-        PathCache::drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height);
+        drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height);
         t->setResult(bitmap);
     } else {
         texture->width = 0;
@@ -62,23 +344,17 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// Path cache
+// Paths
 ///////////////////////////////////////////////////////////////////////////////
 
-PathCache::PathCache(): ShapeCache<PathCacheEntry>("path",
-        PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE) {
-}
-
-PathCache::~PathCache() {
-}
-
 void PathCache::remove(SkPath* path) {
-    Vector<PathCacheEntry> pathsToRemove;
-    LruCache<PathCacheEntry, PathTexture*>::Iterator i(mCache);
+    Vector<PathDescription> pathsToRemove;
+    LruCache<PathDescription, PathTexture*>::Iterator i(mCache);
 
     while (i.next()) {
-        const PathCacheEntry& key = i.key();
-        if (key.path == path || key.path == path->getSourcePath()) {
+        const PathDescription& key = i.key();
+        if (key.type == kShapePath &&
+                (key.shape.path.mPath == path || key.shape.path.mPath == path->getSourcePath())) {
             pathsToRemove.push(key);
         }
     }
@@ -121,7 +397,9 @@
 PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
     path = getSourcePath(path);
 
-    PathCacheEntry entry(path, paint);
+    PathDescription entry(kShapePath, paint);
+    entry.shape.path.mPath = path;
+
     PathTexture* texture = mCache.get(entry);
 
     if (!texture) {
@@ -159,7 +437,9 @@
 
     path = getSourcePath(path);
 
-    PathCacheEntry entry(path, paint);
+    PathDescription entry(kShapePath, paint);
+    entry.shape.path.mPath = path;
+
     PathTexture* texture = mCache.get(entry);
 
     bool generate = false;
@@ -193,5 +473,130 @@
     }
 }
 
+///////////////////////////////////////////////////////////////////////////////
+// Rounded rects
+///////////////////////////////////////////////////////////////////////////////
+
+PathTexture* PathCache::getRoundRect(float width, float height,
+        float rx, float ry, SkPaint* paint) {
+    PathDescription entry(kShapeRoundRect, paint);
+    entry.shape.roundRect.mWidth = width;
+    entry.shape.roundRect.mHeight = height;
+    entry.shape.roundRect.mRx = rx;
+    entry.shape.roundRect.mRy = ry;
+
+    PathTexture* texture = get(entry);
+
+    if (!texture) {
+        SkPath path;
+        SkRect r;
+        r.set(0.0f, 0.0f, width, height);
+        path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
+
+        texture = addTexture(entry, &path, paint);
+    }
+
+    return texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Circles
+///////////////////////////////////////////////////////////////////////////////
+
+PathTexture* PathCache::getCircle(float radius, SkPaint* paint) {
+    PathDescription entry(kShapeCircle, paint);
+    entry.shape.circle.mRadius = radius;
+
+    PathTexture* texture = get(entry);
+
+    if (!texture) {
+        SkPath path;
+        path.addCircle(radius, radius, radius, SkPath::kCW_Direction);
+
+        texture = addTexture(entry, &path, paint);
+    }
+
+    return texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Ovals
+///////////////////////////////////////////////////////////////////////////////
+
+PathTexture* PathCache::getOval(float width, float height, SkPaint* paint) {
+    PathDescription entry(kShapeOval, paint);
+    entry.shape.oval.mWidth = width;
+    entry.shape.oval.mHeight = height;
+
+    PathTexture* texture = get(entry);
+
+    if (!texture) {
+        SkPath path;
+        SkRect r;
+        r.set(0.0f, 0.0f, width, height);
+        path.addOval(r, SkPath::kCW_Direction);
+
+        texture = addTexture(entry, &path, paint);
+    }
+
+    return texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Rects
+///////////////////////////////////////////////////////////////////////////////
+
+PathTexture* PathCache::getRect(float width, float height, SkPaint* paint) {
+    PathDescription entry(kShapeRect, paint);
+    entry.shape.rect.mWidth = width;
+    entry.shape.rect.mHeight = height;
+
+    PathTexture* texture = get(entry);
+
+    if (!texture) {
+        SkPath path;
+        SkRect r;
+        r.set(0.0f, 0.0f, width, height);
+        path.addRect(r, SkPath::kCW_Direction);
+
+        texture = addTexture(entry, &path, paint);
+    }
+
+    return texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Arcs
+///////////////////////////////////////////////////////////////////////////////
+
+PathTexture* PathCache::getArc(float width, float height,
+        float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) {
+    PathDescription entry(kShapeArc, paint);
+    entry.shape.arc.mWidth = width;
+    entry.shape.arc.mHeight = height;
+    entry.shape.arc.mStartAngle = startAngle;
+    entry.shape.arc.mSweepAngle = sweepAngle;
+    entry.shape.arc.mUseCenter = useCenter;
+
+    PathTexture* texture = get(entry);
+
+    if (!texture) {
+        SkPath path;
+        SkRect r;
+        r.set(0.0f, 0.0f, width, height);
+        if (useCenter) {
+            path.moveTo(r.centerX(), r.centerY());
+        }
+        path.arcTo(r, startAngle, sweepAngle, !useCenter);
+        if (useCenter) {
+            path.close();
+        }
+
+        texture = addTexture(entry, &path, paint);
+    }
+
+    return texture;
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 27031a5..e6d92df 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,17 +17,21 @@
 #ifndef ANDROID_HWUI_PATH_CACHE_H
 #define ANDROID_HWUI_PATH_CACHE_H
 
-#include <utils/Thread.h>
+#include <GLES2/gl2.h>
+
+#include <utils/LruCache.h>
+#include <utils/Mutex.h>
 #include <utils/Vector.h>
 
 #include "Debug.h"
-#include "ShapeCache.h"
-#include "thread/Signal.h"
-#include "thread/Task.h"
-#include "thread/TaskProcessor.h"
+#include "Properties.h"
+#include "Texture.h"
 
+class SkBitmap;
+class SkCanvas;
 class SkPaint;
 class SkPath;
+class SkRect;
 
 namespace android {
 namespace uirenderer {
@@ -35,56 +39,181 @@
 class Caches;
 
 ///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#if DEBUG_PATHS
+    #define PATH_LOGD(...) ALOGD(__VA_ARGS__)
+#else
+    #define PATH_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
 // Classes
 ///////////////////////////////////////////////////////////////////////////////
 
-struct PathCacheEntry: public ShapeCacheEntry {
-    PathCacheEntry(SkPath* path, SkPaint* paint):
-            ShapeCacheEntry(ShapeCacheEntry::kShapePath, paint) {
-        this->path = path;
+/**
+ * Alpha texture used to represent a path.
+ */
+struct PathTexture: public Texture {
+    PathTexture(): Texture() {
     }
 
-    PathCacheEntry(): ShapeCacheEntry() {
-        path = NULL;
+    ~PathTexture() {
+        clearTask();
     }
 
-    hash_t hash() const {
-        uint32_t hash = ShapeCacheEntry::hash();
-        hash = JenkinsHashMix(hash, android::hash_type(path));
-        return JenkinsHashWhiten(hash);
+    /**
+     * Left coordinate of the path bounds.
+     */
+    float left;
+    /**
+     * Top coordinate of the path bounds.
+     */
+    float top;
+    /**
+     * Offset to draw the path at the correct origin.
+     */
+    float offset;
+
+    sp<Task<SkBitmap*> > task() const {
+        return mTask;
     }
 
-    int compare(const ShapeCacheEntry& r) const {
-        int deltaInt = ShapeCacheEntry::compare(r);
-        if (deltaInt != 0) return deltaInt;
-
-        const PathCacheEntry& rhs = (const PathCacheEntry&) r;
-        return path - rhs.path;
+    void setTask(const sp<Task<SkBitmap*> >& task) {
+        mTask = task;
     }
 
-    SkPath* path;
+    void clearTask() {
+        if (mTask != NULL) {
+            mTask.clear();
+        }
+    }
 
-}; // PathCacheEntry
+private:
+    sp<Task<SkBitmap*> > mTask;
+}; // struct PathTexture
 
-inline hash_t hash_type(const PathCacheEntry& entry) {
-    return entry.hash();
-}
+enum ShapeType {
+    kShapeNone,
+    kShapeRect,
+    kShapeRoundRect,
+    kShapeCircle,
+    kShapeOval,
+    kShapeArc,
+    kShapePath
+};
+
+struct PathDescription {
+    ShapeType type;
+    SkPaint::Join join;
+    SkPaint::Cap cap;
+    SkPaint::Style style;
+    float miter;
+    float strokeWidth;
+    SkPathEffect* pathEffect;
+    union Shape {
+        struct Path {
+            SkPath* mPath;
+        } path;
+        struct RoundRect {
+            float mWidth;
+            float mHeight;
+            float mRx;
+            float mRy;
+        } roundRect;
+        struct Circle {
+            float mRadius;
+        } circle;
+        struct Oval {
+            float mWidth;
+            float mHeight;
+        } oval;
+        struct Rect {
+            float mWidth;
+            float mHeight;
+        } rect;
+        struct Arc {
+            float mWidth;
+            float mHeight;
+            float mStartAngle;
+            float mSweepAngle;
+            bool mUseCenter;
+        } arc;
+    } shape;
+
+    PathDescription();
+    PathDescription(ShapeType shapeType, SkPaint* paint);
+
+    hash_t hash() const;
+
+    int compare(const PathDescription& rhs) const;
+
+    bool operator==(const PathDescription& other) const {
+        return compare(other) == 0;
+    }
+
+    bool operator!=(const PathDescription& other) const {
+        return compare(other) != 0;
+    }
+
+    friend inline int strictly_order_type(
+            const PathDescription& lhs, const PathDescription& rhs) {
+        return lhs.compare(rhs) < 0;
+    }
+
+    friend inline int compare_type(const PathDescription& lhs, const PathDescription& rhs) {
+        return lhs.compare(rhs);
+    }
+
+    friend inline hash_t hash_type(const PathDescription& entry) {
+        return entry.hash();
+    }
+};
 
 /**
- * A simple LRU path cache. The cache has a maximum size expressed in bytes.
+ * A simple LRU shape cache. The cache has a maximum size expressed in bytes.
  * Any texture added to the cache causing the cache to grow beyond the maximum
  * allowed size will also cause the oldest texture to be kicked out.
  */
-class PathCache: public ShapeCache<PathCacheEntry> {
+class PathCache: public OnEntryRemoved<PathDescription, PathTexture*> {
 public:
     PathCache();
     ~PathCache();
 
     /**
-     * Returns the texture associated with the specified path. If the texture
-     * cannot be found in the cache, a new texture is generated.
+     * Used as a callback when an entry is removed from the cache.
+     * Do not invoke directly.
      */
+    void operator()(PathDescription& path, PathTexture*& texture);
+
+    /**
+     * Clears the cache. This causes all textures to be deleted.
+     */
+    void clear();
+
+    /**
+     * Sets the maximum size of the cache in bytes.
+     */
+    void setMaxSize(uint32_t maxSize);
+    /**
+     * Returns the maximum size of the cache in bytes.
+     */
+    uint32_t getMaxSize();
+    /**
+     * Returns the current size of the cache in bytes.
+     */
+    uint32_t getSize();
+
+    PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
+    PathTexture* getCircle(float radius, SkPaint* paint);
+    PathTexture* getOval(float width, float height, SkPaint* paint);
+    PathTexture* getRect(float width, float height, SkPaint* paint);
+    PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
+            bool useCenter, SkPaint* paint);
     PathTexture* get(SkPath* path, SkPaint* paint);
+
     /**
      * Removes an entry.
      */
@@ -98,10 +227,63 @@
      * Process deferred removals.
      */
     void clearGarbage();
+    /**
+     * Trims the contents of the cache, removing items until it's under its
+     * specified limit.
+     *
+     * Trimming is used for caches that support pre-caching from a worker
+     * thread. During pre-caching the maximum limit of the cache can be
+     * exceeded for the duration of the frame. It is therefore required to
+     * trim the cache at the end of the frame to keep the total amount of
+     * memory used under control.
+     */
+    void trim();
 
+    /**
+     * Precaches the specified path using background threads.
+     */
     void precache(SkPath* path, SkPaint* paint);
 
+    static bool canDrawAsConvexPath(SkPath* path, SkPaint* paint);
+    static void computePathBounds(const SkPath* path, const SkPaint* paint,
+            float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
+    static void computeBounds(const SkRect& bounds, const SkPaint* paint,
+            float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
+
 private:
+    PathTexture* addTexture(const PathDescription& entry,
+            const SkPath *path, const SkPaint* paint);
+    PathTexture* addTexture(const PathDescription& entry, SkBitmap* bitmap);
+    void addTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture);
+
+    PathTexture* get(const PathDescription& entry) {
+        return mCache.get(entry);
+    }
+
+    /**
+     * Ensures there is enough space in the cache for a texture of the specified
+     * dimensions.
+     */
+    void purgeCache(uint32_t width, uint32_t height);
+
+    void removeTexture(PathTexture* texture);
+
+    bool checkTextureSize(uint32_t width, uint32_t height) {
+        if (width > mMaxTextureSize || height > mMaxTextureSize) {
+            ALOGW("Shape too large to be rendered into a texture (%dx%d, max=%dx%d)",
+                    width, height, mMaxTextureSize, mMaxTextureSize);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Generates the texture from a bitmap into the specified texture structure.
+     */
+    void generateTexture(SkBitmap& bitmap, Texture* texture);
+
+    void init();
+
     class PathTask: public Task<SkBitmap*> {
     public:
         PathTask(SkPath* path, SkPaint* paint, PathTexture* texture):
@@ -128,6 +310,13 @@
         uint32_t mMaxTextureSize;
     };
 
+    LruCache<PathDescription, PathTexture*> mCache;
+    uint32_t mSize;
+    uint32_t mMaxSize;
+    GLuint mMaxTextureSize;
+
+    bool mDebugEnabled;
+
     sp<PathProcessor> mProcessor;
     Vector<SkPath*> mGarbage;
     mutable Mutex mLock;
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index b8559bc8..5f39abf 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -115,7 +115,6 @@
 #define PROPERTY_RENDER_BUFFER_CACHE_SIZE "ro.hwui.r_buffer_cache_size"
 #define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
 #define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size"
-#define PROPERTY_SHAPE_CACHE_SIZE "ro.hwui.shape_cache_size"
 #define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size"
 #define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size"
 
@@ -159,8 +158,7 @@
 #define DEFAULT_TEXTURE_CACHE_SIZE 24.0f
 #define DEFAULT_LAYER_CACHE_SIZE 16.0f
 #define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f
-#define DEFAULT_PATH_CACHE_SIZE 4.0f
-#define DEFAULT_SHAPE_CACHE_SIZE 1.0f
+#define DEFAULT_PATH_CACHE_SIZE 10.0f
 #define DEFAULT_PATCH_CACHE_SIZE 512
 #define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
 #define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
diff --git a/libs/hwui/ShapeCache.cpp b/libs/hwui/ShapeCache.cpp
deleted file mode 100644
index 5a23235..0000000
--- a/libs/hwui/ShapeCache.cpp
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "OpenGLRenderer"
-
-#include "ShapeCache.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Rounded rects
-///////////////////////////////////////////////////////////////////////////////
-
-RoundRectShapeCache::RoundRectShapeCache(): ShapeCache<RoundRectShapeCacheEntry>(
-        "round rect", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
-}
-
-PathTexture* RoundRectShapeCache::getRoundRect(float width, float height,
-        float rx, float ry, SkPaint* paint) {
-    RoundRectShapeCacheEntry entry(width, height, rx, ry, paint);
-    PathTexture* texture = get(entry);
-
-    if (!texture) {
-        SkPath path;
-        SkRect r;
-        r.set(0.0f, 0.0f, width, height);
-        path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
-
-        texture = addTexture(entry, &path, paint);
-    }
-
-    return texture;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Circles
-///////////////////////////////////////////////////////////////////////////////
-
-CircleShapeCache::CircleShapeCache(): ShapeCache<CircleShapeCacheEntry>(
-        "circle", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
-}
-
-PathTexture* CircleShapeCache::getCircle(float radius, SkPaint* paint) {
-    CircleShapeCacheEntry entry(radius, paint);
-    PathTexture* texture = get(entry);
-
-    if (!texture) {
-        SkPath path;
-        path.addCircle(radius, radius, radius, SkPath::kCW_Direction);
-
-        texture = addTexture(entry, &path, paint);
-    }
-
-    return texture;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Ovals
-///////////////////////////////////////////////////////////////////////////////
-
-OvalShapeCache::OvalShapeCache(): ShapeCache<OvalShapeCacheEntry>(
-        "oval", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
-}
-
-PathTexture* OvalShapeCache::getOval(float width, float height, SkPaint* paint) {
-    OvalShapeCacheEntry entry(width, height, paint);
-    PathTexture* texture = get(entry);
-
-    if (!texture) {
-        SkPath path;
-        SkRect r;
-        r.set(0.0f, 0.0f, width, height);
-        path.addOval(r, SkPath::kCW_Direction);
-
-        texture = addTexture(entry, &path, paint);
-    }
-
-    return texture;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Rects
-///////////////////////////////////////////////////////////////////////////////
-
-RectShapeCache::RectShapeCache(): ShapeCache<RectShapeCacheEntry>(
-        "rect", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
-}
-
-PathTexture* RectShapeCache::getRect(float width, float height, SkPaint* paint) {
-    RectShapeCacheEntry entry(width, height, paint);
-    PathTexture* texture = get(entry);
-
-    if (!texture) {
-        SkRect bounds;
-        bounds.set(0.0f, 0.0f, width, height);
-
-        float left, top, offset;
-        uint32_t rectWidth, rectHeight;
-        computeBounds(bounds, paint, left, top, offset, rectWidth, rectHeight);
-
-        if (!checkTextureSize(rectWidth, rectHeight)) return NULL;
-
-        purgeCache(rectWidth, rectHeight);
-
-        SkBitmap bitmap;
-        initBitmap(bitmap, rectWidth, rectHeight);
-
-        SkPaint pathPaint(*paint);
-        initPaint(pathPaint);
-
-        SkCanvas canvas(bitmap);
-        canvas.translate(-left + offset, -top + offset);
-        canvas.drawRect(bounds, pathPaint);
-
-        texture = createTexture(0, 0, offset, rectWidth, rectHeight, 0);
-        addTexture(entry, &bitmap, texture);
-    }
-
-    return texture;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Arcs
-///////////////////////////////////////////////////////////////////////////////
-
-ArcShapeCache::ArcShapeCache(): ShapeCache<ArcShapeCacheEntry>(
-        "arc", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) {
-}
-
-PathTexture* ArcShapeCache::getArc(float width, float height,
-        float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) {
-    ArcShapeCacheEntry entry(width, height, startAngle, sweepAngle, useCenter, paint);
-    PathTexture* texture = get(entry);
-
-    if (!texture) {
-        SkPath path;
-        SkRect r;
-        r.set(0.0f, 0.0f, width, height);
-        if (useCenter) {
-            path.moveTo(r.centerX(), r.centerY());
-        }
-        path.arcTo(r, startAngle, sweepAngle, !useCenter);
-        if (useCenter) {
-            path.close();
-        }
-
-        texture = addTexture(entry, &path, paint);
-    }
-
-    return texture;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h
deleted file mode 100644
index 92314b0..0000000
--- a/libs/hwui/ShapeCache.h
+++ /dev/null
@@ -1,816 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_SHAPE_CACHE_H
-#define ANDROID_HWUI_SHAPE_CACHE_H
-
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
-#include <GLES2/gl2.h>
-
-#include <SkBitmap.h>
-#include <SkCanvas.h>
-#include <SkPaint.h>
-#include <SkPath.h>
-#include <SkRect.h>
-
-#include <utils/JenkinsHash.h>
-#include <utils/LruCache.h>
-#include <utils/Trace.h>
-
-#include "Debug.h"
-#include "Properties.h"
-#include "Texture.h"
-#include "thread/Task.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-// Debug
-#if DEBUG_SHAPES
-    #define SHAPE_LOGD(...) ALOGD(__VA_ARGS__)
-#else
-    #define SHAPE_LOGD(...)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// Classes
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Alpha texture used to represent a path.
- */
-struct PathTexture: public Texture {
-    PathTexture(): Texture() {
-    }
-
-    ~PathTexture() {
-        clearTask();
-    }
-
-    /**
-     * Left coordinate of the path bounds.
-     */
-    float left;
-    /**
-     * Top coordinate of the path bounds.
-     */
-    float top;
-    /**
-     * Offset to draw the path at the correct origin.
-     */
-    float offset;
-
-    sp<Task<SkBitmap*> > task() const {
-        return mTask;
-    }
-
-    void setTask(const sp<Task<SkBitmap*> >& task) {
-        mTask = task;
-    }
-
-    void clearTask() {
-        if (mTask != NULL) {
-            mTask.clear();
-        }
-    }
-
-private:
-    sp<Task<SkBitmap*> > mTask;
-}; // struct PathTexture
-
-/**
- * Describe a shape in the shape cache.
- */
-struct ShapeCacheEntry {
-    enum ShapeType {
-        kShapeNone,
-        kShapeRect,
-        kShapeRoundRect,
-        kShapeCircle,
-        kShapeOval,
-        kShapeArc,
-        kShapePath
-    };
-
-    ShapeCacheEntry() {
-        shapeType = kShapeNone;
-        join = SkPaint::kDefault_Join;
-        cap = SkPaint::kDefault_Cap;
-        style = SkPaint::kFill_Style;
-        miter = 4.0f;
-        strokeWidth = 1.0f;
-        pathEffect = NULL;
-    }
-
-    ShapeCacheEntry(ShapeType type, SkPaint* paint) {
-        shapeType = type;
-        join = paint->getStrokeJoin();
-        cap = paint->getStrokeCap();
-        miter = paint->getStrokeMiter();
-        strokeWidth = paint->getStrokeWidth();
-        style = paint->getStyle();
-        pathEffect = paint->getPathEffect();
-    }
-
-    virtual ~ShapeCacheEntry() {
-    }
-
-    virtual hash_t hash() const {
-        uint32_t hash = JenkinsHashMix(0, shapeType);
-        hash = JenkinsHashMix(hash, join);
-        hash = JenkinsHashMix(hash, cap);
-        hash = JenkinsHashMix(hash, style);
-        hash = JenkinsHashMix(hash, android::hash_type(miter));
-        hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
-        hash = JenkinsHashMix(hash, android::hash_type(pathEffect));
-        return JenkinsHashWhiten(hash);
-    }
-
-    virtual int compare(const ShapeCacheEntry& rhs) const {
-        int deltaInt = shapeType - rhs.shapeType;
-        if (deltaInt != 0) return deltaInt;
-
-        deltaInt = join - rhs.join;
-        if (deltaInt != 0) return deltaInt;
-
-        deltaInt = cap - rhs.cap;
-        if (deltaInt != 0) return deltaInt;
-
-        deltaInt = style - rhs.style;
-        if (deltaInt != 0) return deltaInt;
-
-        if (miter < rhs.miter) return -1;
-        if (miter > rhs.miter) return +1;
-
-        if (strokeWidth < rhs.strokeWidth) return -1;
-        if (strokeWidth > rhs.strokeWidth) return +1;
-
-        if (pathEffect < rhs.pathEffect) return -1;
-        if (pathEffect > rhs.pathEffect) return +1;
-
-        return 0;
-    }
-
-    bool operator==(const ShapeCacheEntry& other) const {
-        return compare(other) == 0;
-    }
-
-    bool operator!=(const ShapeCacheEntry& other) const {
-        return compare(other) != 0;
-    }
-
-    ShapeType shapeType;
-    SkPaint::Join join;
-    SkPaint::Cap cap;
-    SkPaint::Style style;
-    float miter;
-    float strokeWidth;
-    SkPathEffect* pathEffect;
-}; // struct ShapeCacheEntry
-
-// Cache support
-
-inline int strictly_order_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) {
-    return lhs.compare(rhs) < 0;
-}
-
-inline int compare_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) {
-    return lhs.compare(rhs);
-}
-
-inline hash_t hash_type(const ShapeCacheEntry& entry) {
-    return entry.hash();
-}
-
-struct RoundRectShapeCacheEntry: public ShapeCacheEntry {
-    RoundRectShapeCacheEntry(float width, float height, float rx, float ry, SkPaint* paint):
-            ShapeCacheEntry(ShapeCacheEntry::kShapeRoundRect, paint) {
-        mWidth = width;
-        mHeight = height;
-        mRx = rx;
-        mRy = ry;
-    }
-
-    RoundRectShapeCacheEntry(): ShapeCacheEntry() {
-        mWidth = 0;
-        mHeight = 0;
-        mRx = 0;
-        mRy = 0;
-    }
-
-    hash_t hash() const {
-        uint32_t hash = ShapeCacheEntry::hash();
-        hash = JenkinsHashMix(hash, android::hash_type(mWidth));
-        hash = JenkinsHashMix(hash, android::hash_type(mHeight));
-        hash = JenkinsHashMix(hash, android::hash_type(mRx));
-        hash = JenkinsHashMix(hash, android::hash_type(mRy));
-        return JenkinsHashWhiten(hash);
-    }
-
-    int compare(const ShapeCacheEntry& r) const {
-        int deltaInt = ShapeCacheEntry::compare(r);
-        if (deltaInt != 0) return deltaInt;
-
-        const RoundRectShapeCacheEntry& rhs = (const RoundRectShapeCacheEntry&) r;
-
-        if (mWidth < rhs.mWidth) return -1;
-        if (mWidth > rhs.mWidth) return +1;
-
-        if (mHeight < rhs.mHeight) return -1;
-        if (mHeight > rhs.mHeight) return +1;
-
-        if (mRx < rhs.mRx) return -1;
-        if (mRx > rhs.mRx) return +1;
-
-        if (mRy < rhs.mRy) return -1;
-        if (mRy > rhs.mRy) return +1;
-
-        return 0;
-    }
-
-private:
-    float mWidth;
-    float mHeight;
-    float mRx;
-    float mRy;
-}; // RoundRectShapeCacheEntry
-
-inline hash_t hash_type(const RoundRectShapeCacheEntry& entry) {
-    return entry.hash();
-}
-
-struct CircleShapeCacheEntry: public ShapeCacheEntry {
-    CircleShapeCacheEntry(float radius, SkPaint* paint):
-            ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) {
-        mRadius = radius;
-    }
-
-    CircleShapeCacheEntry(): ShapeCacheEntry() {
-        mRadius = 0;
-    }
-
-    hash_t hash() const {
-        uint32_t hash = ShapeCacheEntry::hash();
-        hash = JenkinsHashMix(hash, android::hash_type(mRadius));
-        return JenkinsHashWhiten(hash);
-    }
-
-    int compare(const ShapeCacheEntry& r) const {
-        int deltaInt = ShapeCacheEntry::compare(r);
-        if (deltaInt != 0) return deltaInt;
-
-        const CircleShapeCacheEntry& rhs = (const CircleShapeCacheEntry&) r;
-
-        if (mRadius < rhs.mRadius) return -1;
-        if (mRadius > rhs.mRadius) return +1;
-
-        return 0;
-    }
-
-private:
-    float mRadius;
-}; // CircleShapeCacheEntry
-
-inline hash_t hash_type(const CircleShapeCacheEntry& entry) {
-    return entry.hash();
-}
-
-struct OvalShapeCacheEntry: public ShapeCacheEntry {
-    OvalShapeCacheEntry(float width, float height, SkPaint* paint):
-            ShapeCacheEntry(ShapeCacheEntry::kShapeOval, paint) {
-        mWidth = width;
-        mHeight = height;
-    }
-
-    OvalShapeCacheEntry(): ShapeCacheEntry() {
-        mWidth = mHeight = 0;
-    }
-
-    hash_t hash() const {
-        uint32_t hash = ShapeCacheEntry::hash();
-        hash = JenkinsHashMix(hash, android::hash_type(mWidth));
-        hash = JenkinsHashMix(hash, android::hash_type(mHeight));
-        return JenkinsHashWhiten(hash);
-    }
-
-    int compare(const ShapeCacheEntry& r) const {
-        int deltaInt = ShapeCacheEntry::compare(r);
-        if (deltaInt != 0) return deltaInt;
-
-        const OvalShapeCacheEntry& rhs = (const OvalShapeCacheEntry&) r;
-
-        if (mWidth < rhs.mWidth) return -1;
-        if (mWidth > rhs.mWidth) return +1;
-
-        if (mHeight < rhs.mHeight) return -1;
-        if (mHeight > rhs.mHeight) return +1;
-
-        return 0;
-    }
-
-private:
-    float mWidth;
-    float mHeight;
-}; // OvalShapeCacheEntry
-
-inline hash_t hash_type(const OvalShapeCacheEntry& entry) {
-    return entry.hash();
-}
-
-struct RectShapeCacheEntry: public ShapeCacheEntry {
-    RectShapeCacheEntry(float width, float height, SkPaint* paint):
-            ShapeCacheEntry(ShapeCacheEntry::kShapeRect, paint) {
-        mWidth = width;
-        mHeight = height;
-    }
-
-    RectShapeCacheEntry(): ShapeCacheEntry() {
-        mWidth = mHeight = 0;
-    }
-
-    hash_t hash() const {
-        uint32_t hash = ShapeCacheEntry::hash();
-        hash = JenkinsHashMix(hash, android::hash_type(mWidth));
-        hash = JenkinsHashMix(hash, android::hash_type(mHeight));
-        return JenkinsHashWhiten(hash);
-    }
-
-    int compare(const ShapeCacheEntry& r) const {
-        int deltaInt = ShapeCacheEntry::compare(r);
-        if (deltaInt != 0) return deltaInt;
-
-        const RectShapeCacheEntry& rhs = (const RectShapeCacheEntry&) r;
-
-        if (mWidth < rhs.mWidth) return -1;
-        if (mWidth > rhs.mWidth) return +1;
-
-        if (mHeight < rhs.mHeight) return -1;
-        if (mHeight > rhs.mHeight) return +1;
-
-        return 0;
-    }
-
-private:
-    float mWidth;
-    float mHeight;
-}; // RectShapeCacheEntry
-
-inline hash_t hash_type(const RectShapeCacheEntry& entry) {
-    return entry.hash();
-}
-
-struct ArcShapeCacheEntry: public ShapeCacheEntry {
-    ArcShapeCacheEntry(float width, float height, float startAngle, float sweepAngle,
-            bool useCenter, SkPaint* paint):
-            ShapeCacheEntry(ShapeCacheEntry::kShapeArc, paint) {
-        mWidth = width;
-        mHeight = height;
-        mStartAngle = startAngle;
-        mSweepAngle = sweepAngle;
-        mUseCenter = useCenter ? 1 : 0;
-    }
-
-    ArcShapeCacheEntry(): ShapeCacheEntry() {
-        mWidth = 0;
-        mHeight = 0;
-        mStartAngle = 0;
-        mSweepAngle = 0;
-        mUseCenter = 0;
-    }
-
-    hash_t hash() const {
-        uint32_t hash = ShapeCacheEntry::hash();
-        hash = JenkinsHashMix(hash, android::hash_type(mWidth));
-        hash = JenkinsHashMix(hash, android::hash_type(mHeight));
-        hash = JenkinsHashMix(hash, android::hash_type(mStartAngle));
-        hash = JenkinsHashMix(hash, android::hash_type(mSweepAngle));
-        hash = JenkinsHashMix(hash, mUseCenter);
-        return JenkinsHashWhiten(hash);
-    }
-
-    int compare(const ShapeCacheEntry& r) const {
-        int deltaInt = ShapeCacheEntry::compare(r);
-        if (deltaInt != 0) return deltaInt;
-
-        const ArcShapeCacheEntry& rhs = (const ArcShapeCacheEntry&) r;
-
-        if (mWidth < rhs.mWidth) return -1;
-        if (mWidth > rhs.mWidth) return +1;
-
-        if (mHeight < rhs.mHeight) return -1;
-        if (mHeight > rhs.mHeight) return +1;
-
-        if (mStartAngle < rhs.mStartAngle) return -1;
-        if (mStartAngle > rhs.mStartAngle) return +1;
-
-        if (mSweepAngle < rhs.mSweepAngle) return -1;
-        if (mSweepAngle > rhs.mSweepAngle) return +1;
-
-        return mUseCenter - rhs.mUseCenter;
-    }
-
-private:
-    float mWidth;
-    float mHeight;
-    float mStartAngle;
-    float mSweepAngle;
-    uint32_t mUseCenter;
-}; // ArcShapeCacheEntry
-
-inline hash_t hash_type(const ArcShapeCacheEntry& entry) {
-    return entry.hash();
-}
-
-/**
- * A simple LRU shape cache. The cache has a maximum size expressed in bytes.
- * Any texture added to the cache causing the cache to grow beyond the maximum
- * allowed size will also cause the oldest texture to be kicked out.
- */
-template<typename Entry>
-class ShapeCache: public OnEntryRemoved<Entry, PathTexture*> {
-public:
-    ShapeCache(const char* name, const char* propertyName, float defaultSize);
-    ~ShapeCache();
-
-    /**
-     * Used as a callback when an entry is removed from the cache.
-     * Do not invoke directly.
-     */
-    void operator()(Entry& path, PathTexture*& texture);
-
-    /**
-     * Clears the cache. This causes all textures to be deleted.
-     */
-    void clear();
-
-    /**
-     * Sets the maximum size of the cache in bytes.
-     */
-    void setMaxSize(uint32_t maxSize);
-    /**
-     * Returns the maximum size of the cache in bytes.
-     */
-    uint32_t getMaxSize();
-    /**
-     * Returns the current size of the cache in bytes.
-     */
-    uint32_t getSize();
-
-    /**
-     * Trims the contents of the cache, removing items until it's under its
-     * specified limit.
-     *
-     * Trimming is used for caches that support pre-caching from a worker
-     * thread. During pre-caching the maximum limit of the cache can be
-     * exceeded for the duration of the frame. It is therefore required to
-     * trim the cache at the end of the frame to keep the total amount of
-     * memory used under control.
-     *
-     * Only the PathCache currently supports pre-caching.
-     */
-    void trim();
-
-    static void computePathBounds(const SkPath* path, const SkPaint* paint,
-            float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
-        const SkRect& bounds = path->getBounds();
-        computeBounds(bounds, paint, left, top, offset, width, height);
-    }
-
-    static void computeBounds(const SkRect& bounds, const SkPaint* paint,
-            float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
-        const float pathWidth = fmax(bounds.width(), 1.0f);
-        const float pathHeight = fmax(bounds.height(), 1.0f);
-
-        left = bounds.fLeft;
-        top = bounds.fTop;
-
-        offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
-
-        width = uint32_t(pathWidth + offset * 2.0 + 0.5);
-        height = uint32_t(pathHeight + offset * 2.0 + 0.5);
-    }
-
-    static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
-            float left, float top, float offset, uint32_t width, uint32_t height) {
-        initBitmap(bitmap, width, height);
-
-        SkPaint pathPaint(*paint);
-        initPaint(pathPaint);
-
-        SkCanvas canvas(bitmap);
-        canvas.translate(-left + offset, -top + offset);
-        canvas.drawPath(*path, pathPaint);
-    }
-
-protected:
-    PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint);
-    PathTexture* addTexture(const Entry& entry, SkBitmap* bitmap);
-    void addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture);
-
-    /**
-     * Ensures there is enough space in the cache for a texture of the specified
-     * dimensions.
-     */
-    void purgeCache(uint32_t width, uint32_t height);
-
-    PathTexture* get(Entry entry) {
-        return mCache.get(entry);
-    }
-
-    void removeTexture(PathTexture* texture);
-
-    bool checkTextureSize(uint32_t width, uint32_t height) {
-        if (width > mMaxTextureSize || height > mMaxTextureSize) {
-            ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)",
-                    mName, width, height, mMaxTextureSize, mMaxTextureSize);
-            return false;
-        }
-        return true;
-    }
-
-    static PathTexture* createTexture(float left, float top, float offset,
-            uint32_t width, uint32_t height, uint32_t id) {
-        PathTexture* texture = new PathTexture();
-        texture->left = left;
-        texture->top = top;
-        texture->offset = offset;
-        texture->width = width;
-        texture->height = height;
-        texture->generation = id;
-        return texture;
-    }
-
-    static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
-        bitmap.setConfig(SkBitmap::kA8_Config, width, height);
-        bitmap.allocPixels();
-        bitmap.eraseColor(0);
-    }
-
-    static void initPaint(SkPaint& paint) {
-        // Make sure the paint is opaque, color, alpha, filter, etc.
-        // will be applied later when compositing the alpha8 texture
-        paint.setColor(0xff000000);
-        paint.setAlpha(255);
-        paint.setColorFilter(NULL);
-        paint.setMaskFilter(NULL);
-        paint.setShader(NULL);
-        SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
-        SkSafeUnref(paint.setXfermode(mode));
-    }
-
-    LruCache<Entry, PathTexture*> mCache;
-    uint32_t mSize;
-    uint32_t mMaxSize;
-    GLuint mMaxTextureSize;
-
-    char* mName;
-    bool mDebugEnabled;
-
-private:
-    /**
-     * Generates the texture from a bitmap into the specified texture structure.
-     */
-    void generateTexture(SkBitmap& bitmap, Texture* texture);
-
-    void init();
-}; // class ShapeCache
-
-class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> {
-public:
-    RoundRectShapeCache();
-
-    PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
-}; // class RoundRectShapeCache
-
-class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> {
-public:
-    CircleShapeCache();
-
-    PathTexture* getCircle(float radius, SkPaint* paint);
-}; // class CircleShapeCache
-
-class OvalShapeCache: public ShapeCache<OvalShapeCacheEntry> {
-public:
-    OvalShapeCache();
-
-    PathTexture* getOval(float width, float height, SkPaint* paint);
-}; // class OvalShapeCache
-
-class RectShapeCache: public ShapeCache<RectShapeCacheEntry> {
-public:
-    RectShapeCache();
-
-    PathTexture* getRect(float width, float height, SkPaint* paint);
-}; // class RectShapeCache
-
-class ArcShapeCache: public ShapeCache<ArcShapeCacheEntry> {
-public:
-    ArcShapeCache();
-
-    PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
-            bool useCenter, SkPaint* paint);
-}; // class ArcShapeCache
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors/destructor
-///////////////////////////////////////////////////////////////////////////////
-
-template<class Entry>
-ShapeCache<Entry>::ShapeCache(const char* name, const char* propertyName, float defaultSize):
-        mCache(LruCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
-        mSize(0), mMaxSize(MB(defaultSize)) {
-    char property[PROPERTY_VALUE_MAX];
-    if (property_get(propertyName, property, NULL) > 0) {
-        INIT_LOGD("  Setting %s cache size to %sMB", name, property);
-        setMaxSize(MB(atof(property)));
-    } else {
-        INIT_LOGD("  Using default %s cache size of %.2fMB", name, defaultSize);
-    }
-
-    size_t len = strlen(name);
-    mName = new char[len + 1];
-    strcpy(mName, name);
-    mName[len] = '\0';
-
-    init();
-}
-
-template<class Entry>
-ShapeCache<Entry>::~ShapeCache() {
-    mCache.clear();
-    delete[] mName;
-}
-
-template<class Entry>
-void ShapeCache<Entry>::init() {
-    mCache.setOnEntryRemovedListener(this);
-
-    GLint maxTextureSize;
-    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
-    mMaxTextureSize = maxTextureSize;
-
-    mDebugEnabled = readDebugLevel() & kDebugCaches;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Size management
-///////////////////////////////////////////////////////////////////////////////
-
-template<class Entry>
-uint32_t ShapeCache<Entry>::getSize() {
-    return mSize;
-}
-
-template<class Entry>
-uint32_t ShapeCache<Entry>::getMaxSize() {
-    return mMaxSize;
-}
-
-template<class Entry>
-void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) {
-    mMaxSize = maxSize;
-    while (mSize > mMaxSize) {
-        mCache.removeOldest();
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Callbacks
-///////////////////////////////////////////////////////////////////////////////
-
-template<class Entry>
-void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) {
-    removeTexture(texture);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Caching
-///////////////////////////////////////////////////////////////////////////////
-
-template<class Entry>
-void ShapeCache<Entry>::removeTexture(PathTexture* texture) {
-    if (texture) {
-        const uint32_t size = texture->width * texture->height;
-        mSize -= size;
-
-        SHAPE_LOGD("ShapeCache::callback: delete %s: name, size, mSize = %d, %d, %d",
-                mName, texture->id, size, mSize);
-        if (mDebugEnabled) {
-            ALOGD("Shape %s deleted, size = %d", mName, size);
-        }
-
-        if (texture->id) {
-            glDeleteTextures(1, &texture->id);
-        }
-        delete texture;
-    }
-}
-
-template<class Entry>
-void ShapeCache<Entry>::purgeCache(uint32_t width, uint32_t height) {
-    const uint32_t size = width * height;
-    // Don't even try to cache a bitmap that's bigger than the cache
-    if (size < mMaxSize) {
-        while (mSize + size > mMaxSize) {
-            mCache.removeOldest();
-        }
-    }
-}
-
-template<class Entry>
-void ShapeCache<Entry>::trim() {
-    while (mSize > mMaxSize) {
-        mCache.removeOldest();
-    }
-}
-
-template<class Entry>
-PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
-        const SkPaint* paint) {
-    ATRACE_CALL();
-
-    float left, top, offset;
-    uint32_t width, height;
-    computePathBounds(path, paint, left, top, offset, width, height);
-
-    if (!checkTextureSize(width, height)) return NULL;
-
-    purgeCache(width, height);
-
-    SkBitmap bitmap;
-    drawPath(path, paint, bitmap, left, top, offset, width, height);
-
-    PathTexture* texture = createTexture(left, top, offset, width, height,
-            path->getGenerationID());
-    addTexture(entry, &bitmap, texture);
-
-    return texture;
-}
-
-template<class Entry>
-void ShapeCache<Entry>::addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture) {
-    generateTexture(*bitmap, texture);
-
-    uint32_t size = texture->width * texture->height;
-    if (size < mMaxSize) {
-        mSize += size;
-        SHAPE_LOGD("ShapeCache::get: create %s: name, size, mSize = %d, %d, %d",
-                mName, texture->id, size, mSize);
-        if (mDebugEnabled) {
-            ALOGD("Shape %s created, size = %d", mName, size);
-        }
-        mCache.put(entry, texture);
-    } else {
-        texture->cleanup = true;
-    }
-}
-
-template<class Entry>
-void ShapeCache<Entry>::clear() {
-    mCache.clear();
-}
-
-template<class Entry>
-void ShapeCache<Entry>::generateTexture(SkBitmap& bitmap, Texture* texture) {
-    SkAutoLockPixels alp(bitmap);
-    if (!bitmap.readyToDraw()) {
-        ALOGE("Cannot generate texture from bitmap");
-        return;
-    }
-
-    glGenTextures(1, &texture->id);
-
-    glBindTexture(GL_TEXTURE_2D, texture->id);
-    // Textures are Alpha8
-    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
-    texture->blend = true;
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
-            GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
-
-    texture->setFilter(GL_LINEAR);
-    texture->setWrap(GL_CLAMP_TO_EDGE);
-}
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_SHAPE_CACHE_H
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java
index 97e5526..61dca78 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ShapesActivity.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.Path;
 import android.graphics.RectF;
 import android.os.Bundle;
 import android.view.View;
@@ -34,12 +35,13 @@
     }
 
     static class ShapesView extends View {
-        private Paint mNormalPaint;
-        private Paint mStrokePaint;
-        private Paint mFillPaint;
-        private RectF mRect;
-        private RectF mOval;
-        private RectF mArc;
+        private final Paint mNormalPaint;
+        private final Paint mStrokePaint;
+        private final Paint mFillPaint;
+        private final RectF mRect;
+        private final RectF mOval;
+        private final RectF mArc;
+        private final Path mTriangle;
 
         ShapesView(Context c) {
             super(c);
@@ -65,6 +67,12 @@
 
             mOval = new RectF(0.0f, 0.0f, 80.0f, 45.0f);
             mArc = new RectF(0.0f, 0.0f, 100.0f, 120.0f);
+
+            mTriangle = new Path();
+            mTriangle.moveTo(0.0f, 90.0f);
+            mTriangle.lineTo(45.0f, 0.0f);
+            mTriangle.lineTo(90.0f, 90.0f);
+            mTriangle.close();
         }
 
         @Override
@@ -136,6 +144,17 @@
             canvas.translate(0.0f, 110.0f);
             canvas.drawArc(mArc, 30.0f, 100.0f, false, mFillPaint);
             canvas.restore();
+
+            canvas.save();
+            canvas.translate(50.0f, 400.0f);
+            canvas.drawPath(mTriangle, mNormalPaint);
+
+            canvas.translate(110.0f, 0.0f);
+            canvas.drawPath(mTriangle, mStrokePaint);
+
+            canvas.translate(110.0f, 0.0f);
+            canvas.drawPath(mTriangle, mFillPaint);
+            canvas.restore();
         }
     }
 }