Add plumbing for better text scaling

Fonts are now described by a transform matrix. This lead to switching
from a vector to a hashmap. This change therefore adds new comparators
and hash computations to Font.

Change-Id: I2daffa7d6287c18554c606b8bfa06640d28b4530
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 747856c..06574cd 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -1775,7 +1775,7 @@
     paint->setAntiAlias(true);
     SkPaint* addedPaint = addPaint(paint);
     FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
-    fontRenderer.precache(addedPaint, text, count);
+    fontRenderer.precache(addedPaint, text, count, *mSnapshot->transform);
     return DrawGlInfo::kStatusDone;
 }
 
@@ -1789,7 +1789,7 @@
     paint->setAntiAlias(true);
     SkPaint* addedPaint = addPaint(paint);
     FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
-    fontRenderer.precache(addedPaint, text, count);
+    fontRenderer.precache(addedPaint, text, count, *mSnapshot->transform);
     return DrawGlInfo::kStatusDone;
 }
 
@@ -1823,7 +1823,7 @@
     SkPaint* addedPaint = addPaint(paint);
     if (!reject) {
         FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
-        fontRenderer.precache(addedPaint, text, count);
+        fontRenderer.precache(addedPaint, text, count, *mSnapshot->transform);
     }
     addFloat(length);
     addSkip(location);
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 47784a4..5c1eb38 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -36,7 +36,9 @@
 
 static bool sLogFontRendererCreate = true;
 
-FontRenderer::FontRenderer() {
+FontRenderer::FontRenderer() :
+        mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
+
     if (sLogFontRendererCreate) {
         INIT_LOGD("Creating FontRenderer");
     }
@@ -107,10 +109,11 @@
         delete[] mTextMesh;
     }
 
-    Vector<Font*> fontsToDereference = mActiveFonts;
-    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
-        delete fontsToDereference[i];
+    LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
+    while (it.next()) {
+        delete it.value();
     }
+    mActiveFonts.clear();
 }
 
 void FontRenderer::flushAllAndInvalidate() {
@@ -118,8 +121,9 @@
         issueDrawCommand();
     }
 
-    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
-        mActiveFonts[i]->invalidateTextureCache();
+    LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
+    while (it.next()) {
+        it.value()->invalidateTextureCache();
     }
 
     for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
@@ -146,8 +150,9 @@
         CacheTexture* cacheTexture = mCacheTextures[i];
         if (cacheTexture->getTexture()) {
             cacheTexture->init();
-            for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
-                mActiveFonts[j]->invalidateTextureCache(cacheTexture);
+            LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
+            while (it.next()) {
+                it.value()->invalidateTextureCache(cacheTexture);
             }
             cacheTexture->releaseTexture();
         }
@@ -480,22 +485,8 @@
     }
 }
 
-void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
-    int flags = 0;
-    if (paint->isFakeBoldText()) {
-        flags |= Font::kFakeBold;
-    }
-
-    const float skewX = paint->getTextSkewX();
-    uint32_t italicStyle = *(uint32_t*) &skewX;
-    const float scaleXFloat = paint->getTextScaleX();
-    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
-    SkPaint::Style style = paint->getStyle();
-    const float strokeWidthFloat = paint->getStrokeWidth();
-    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
-    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
-            scaleX, style, strokeWidth);
-
+void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
+    mCurrentFont = Font::create(this, paint, matrix);
 }
 
 FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
@@ -561,39 +552,11 @@
     }
 }
 
-void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
-    int flags = 0;
-    if (paint->isFakeBoldText()) {
-        flags |= Font::kFakeBold;
-    }
-    const float skewX = paint->getTextSkewX();
-    uint32_t italicStyle = *(uint32_t*) &skewX;
-    const float scaleXFloat = paint->getTextScaleX();
-    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
-    SkPaint::Style style = paint->getStyle();
-    const float strokeWidthFloat = paint->getStrokeWidth();
-    uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
-    float fontSize = paint->getTextSize();
-    Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
-            fontSize, flags, italicStyle, scaleX, style, strokeWidth);
-
+void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
+    Font* font = Font::create(this, paint, matrix);
     font->precache(paint, text, numGlyphs);
 }
 
-bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
-        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
-    if (!mCurrentFont) {
-        ALOGE("No font set");
-        return false;
-    }
-
-    initRender(clip, bounds);
-    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
-    finishRender();
-
-    return mDrawn;
-}
-
 bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
         uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
         const float* positions, Rect* bounds) {
@@ -625,12 +588,7 @@
 }
 
 void FontRenderer::removeFont(const Font* font) {
-    for (uint32_t ct = 0; ct < mActiveFonts.size(); ct++) {
-        if (mActiveFonts[ct] == font) {
-            mActiveFonts.removeAt(ct);
-            break;
-        }
-    }
+    mActiveFonts.remove(font->getDescription());
 
     if (mCurrentFont == font) {
         mCurrentFont = NULL;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 09a3c25..3964bca 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_HWUI_FONT_RENDERER_H
 #define ANDROID_HWUI_FONT_RENDERER_H
 
+#include <utils/LruCache.h>
 #include <utils/Vector.h>
 
 #include <SkPaint.h>
@@ -27,6 +28,7 @@
 #include "font/CacheTexture.h"
 #include "font/CachedGlyphInfo.h"
 #include "font/Font.h"
+#include "Matrix.h"
 #include "Properties.h"
 
 namespace android {
@@ -47,14 +49,11 @@
         mGammaTable = gammaTable;
     }
 
-    void setFont(SkPaint* paint, uint32_t fontId, float fontSize);
+    void setFont(SkPaint* paint, const mat4& matrix);
 
-    void precache(SkPaint* paint, const char* text, int numGlyphs);
+    void precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix);
 
     // bounds is an out parameter
-    bool renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
-            uint32_t len, int numGlyphs, int x, int y, Rect* bounds);
-    // bounds is an out parameter
     bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
             uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds);
     // bounds is an out parameter
@@ -153,7 +152,7 @@
     Vector<CacheTexture*> mCacheTextures;
 
     Font* mCurrentFont;
-    Vector<Font*> mActiveFonts;
+    LruCache<Font::FontDescription, Font*> mActiveFonts;
 
     CacheTexture* mCurrentCacheTexture;
 
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index ce74cee..cfc5b04 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -67,6 +67,14 @@
 // Caching
 ///////////////////////////////////////////////////////////////////////////////
 
+int LayerCache::LayerEntry::compare(const LayerCache::LayerEntry& lhs,
+        const LayerCache::LayerEntry& rhs) {
+    int deltaInt = int(lhs.mWidth) - int(rhs.mWidth);
+    if (deltaInt != 0) return deltaInt;
+
+    return int(lhs.mHeight) - int(rhs.mHeight);
+}
+
 void LayerCache::deleteLayer(Layer* layer) {
     if (layer) {
         LAYER_LOGD("Destroying layer %dx%d, fbo %d", layer->getWidth(), layer->getHeight(),
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
index fd698e2..fc2cd91 100644
--- a/libs/hwui/LayerCache.h
+++ b/libs/hwui/LayerCache.h
@@ -102,9 +102,6 @@
      */
     void dump();
 
-private:
-    void deleteLayer(Layer* layer);
-
     struct LayerEntry {
         LayerEntry():
             mLayer(NULL), mWidth(0), mHeight(0) {
@@ -119,15 +116,14 @@
             mLayer(layer), mWidth(layer->getWidth()), mHeight(layer->getHeight()) {
         }
 
-        bool operator<(const LayerEntry& rhs) const {
-            if (mWidth == rhs.mWidth) {
-                return mHeight < rhs.mHeight;
-            }
-            return mWidth < rhs.mWidth;
+        static int compare(const LayerEntry& lhs, const LayerEntry& rhs);
+
+        bool operator==(const LayerEntry& other) const {
+            return compare(*this, other) == 0;
         }
 
-        bool operator==(const LayerEntry& rhs) const {
-            return mWidth == rhs.mWidth && mHeight == rhs.mHeight;
+        bool operator!=(const LayerEntry& other) const {
+            return compare(*this, other) != 0;
         }
 
         Layer* mLayer;
@@ -135,12 +131,24 @@
         uint32_t mHeight;
     }; // struct LayerEntry
 
+private:
+    void deleteLayer(Layer* layer);
+
     SortedList<LayerEntry> mCache;
 
     uint32_t mSize;
     uint32_t mMaxSize;
 }; // class LayerCache
 
+inline int strictly_order_type(const LayerCache::LayerEntry& lhs,
+        const LayerCache::LayerEntry& rhs) {
+    return LayerCache::LayerEntry::compare(lhs, rhs) < 0;
+}
+
+inline int compare_type(const LayerCache::LayerEntry& lhs, const LayerCache::LayerEntry& rhs) {
+    return LayerCache::LayerEntry::compare(lhs, rhs);
+}
+
 }; // namespace uirenderer
 }; // namespace android
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 8756805..8cda729 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1716,7 +1716,6 @@
         return DrawGlInfo::kStatusDone;
     }
 
-    // TODO: We should compute the bounding box when recording the display list
     float left = FLT_MAX;
     float top = FLT_MAX;
     float right = FLT_MIN;
@@ -1754,7 +1753,6 @@
             TextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1);
             TextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2);
 
-            // TODO: This could be optimized to avoid unnecessary ops
             left = fminf(left, fminf(vertices[ax], fminf(vertices[bx], vertices[cx])));
             top = fminf(top, fminf(vertices[ay], fminf(vertices[by], vertices[cy])));
             right = fmaxf(right, fmaxf(vertices[ax], fmaxf(vertices[bx], vertices[cx])));
@@ -2453,7 +2451,8 @@
     }
 
     // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
-    if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || p->getStrokeCap() != SkPaint::kButt_Cap || useCenter) {
+    if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 ||
+            p->getStrokeCap() != SkPaint::kButt_Cap || useCenter) {
         mCaches.activeTexture(0);
         const PathTexture* texture = mCaches.arcShapeCache.getArc(right - left, bottom - top,
                 startAngle, sweepAngle, useCenter, p);
@@ -2577,16 +2576,15 @@
     }
 
     FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
-    fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
-            paint->getTextSize());
+    fontRenderer.setFont(paint, *mSnapshot->transform);
 
     int alpha;
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
 
     if (CC_UNLIKELY(mHasShadow)) {
-        drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, alpha, mode,
-                0.0f, 0.0f);
+        drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
+                alpha, mode, 0.0f, 0.0f);
     }
 
     // Pick the appropriate texture filtering
@@ -2655,6 +2653,14 @@
         return DrawGlInfo::kStatusDone;
     }
 
+#if DEBUG_GLYPHS
+    ALOGD("OpenGLRenderer drawText() with FontID=%d",
+            SkTypeface::UniqueID(paint->getTypeface()));
+#endif
+
+    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
+    fontRenderer.setFont(paint, *mSnapshot->transform);
+
     const float oldX = x;
     const float oldY = y;
     const bool pureTranslate = mSnapshot->transform->isPureTranslate();
@@ -2663,15 +2669,6 @@
         y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
     }
 
-#if DEBUG_GLYPHS
-    ALOGD("OpenGLRenderer drawText() with FontID=%d",
-            SkTypeface::UniqueID(paint->getTypeface()));
-#endif
-
-    FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
-    fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
-            paint->getTextSize());
-
     int alpha;
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
@@ -2744,8 +2741,7 @@
     }
 
     FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
-    fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
-            paint->getTextSize());
+    fontRenderer.setFont(paint, *mSnapshot->transform);
 
     int alpha;
     SkXfermode::Mode mode;
@@ -2789,7 +2785,6 @@
 
     mCaches.activeTexture(0);
 
-    // TODO: Perform early clip test before we rasterize the path
     const PathTexture* texture = mCaches.pathCache.get(path, paint);
     if (!texture) return DrawGlInfo::kStatusDone;
     const AutoTexture autoCleanup(texture);
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 03ddf59..4f682ed 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -93,9 +93,6 @@
     PathCacheEntry entry(path, paint);
     PathTexture* texture = mCache.get(entry);
 
-    float left, top, offset;
-    uint32_t width, height;
-
     if (!texture) {
         texture = addTexture(entry, path, paint);
     } else if (path->getGenerationID() != texture->generation) {
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index 7bfa63d..34afe97 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -18,6 +18,8 @@
 
 #include <cutils/compiler.h>
 
+#include <utils/JenkinsHash.h>
+
 #include <SkUtils.h>
 
 #include "Debug.h"
@@ -33,14 +35,22 @@
 // Font
 ///////////////////////////////////////////////////////////////////////////////
 
-Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
-        int flags, uint32_t italicStyle, uint32_t scaleX,
-        SkPaint::Style style, uint32_t strokeWidth) :
-        mState(state), mFontId(fontId), mFontSize(fontSize),
-        mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
-        mStyle(style), mStrokeWidth(mStrokeWidth) {
+Font::Font(FontRenderer* state, const Font::FontDescription& desc) :
+        mState(state), mDescription(desc) {
 }
 
+Font::FontDescription::FontDescription(const SkPaint* paint, const mat4& matrix) {
+    mFontId = SkTypeface::UniqueID(paint->getTypeface());
+    mFontSize = paint->getTextSize();
+    mFlags = 0;
+    if (paint->isFakeBoldText()) {
+        mFlags |= Font::kFakeBold;
+    }
+    mItalicStyle = paint->getTextSkewX();
+    mScaleX = paint->getTextScaleX();
+    mStyle = paint->getStyle();
+    mStrokeWidth = paint->getStrokeWidth();
+}
 
 Font::~Font() {
     mState->removeFont(this);
@@ -50,6 +60,43 @@
     }
 }
 
+hash_t Font::FontDescription::hash() const {
+    uint32_t hash = JenkinsHashMix(0, mFontId);
+    hash = JenkinsHashMix(hash, android::hash_type(mFontSize));
+    hash = JenkinsHashMix(hash, android::hash_type(mFlags));
+    hash = JenkinsHashMix(hash, android::hash_type(mItalicStyle));
+    hash = JenkinsHashMix(hash, android::hash_type(mScaleX));
+    hash = JenkinsHashMix(hash, android::hash_type(mStyle));
+    hash = JenkinsHashMix(hash, android::hash_type(mStrokeWidth));
+    return JenkinsHashWhiten(hash);
+}
+
+int Font::FontDescription::compare(const Font::FontDescription& lhs,
+        const Font::FontDescription& rhs) {
+    int deltaInt = int(lhs.mFontId) - int(rhs.mFontId);
+    if (deltaInt != 0) return deltaInt;
+
+    if (lhs.mFontSize < rhs.mFontSize) return -1;
+    if (lhs.mFontSize > rhs.mFontSize) return +1;
+
+    if (lhs.mItalicStyle < rhs.mItalicStyle) return -1;
+    if (lhs.mItalicStyle > rhs.mItalicStyle) return +1;
+
+    deltaInt = int(lhs.mFlags) - int(rhs.mFlags);
+    if (deltaInt != 0) return deltaInt;
+
+    if (lhs.mScaleX < rhs.mScaleX) return -1;
+    if (lhs.mScaleX > rhs.mScaleX) return +1;
+
+    deltaInt = int(lhs.mStyle) - int(rhs.mStyle);
+    if (deltaInt != 0) return deltaInt;
+
+    if (lhs.mStrokeWidth < rhs.mStrokeWidth) return -1;
+    if (lhs.mStrokeWidth > rhs.mStrokeWidth) return +1;
+
+    return 0;
+}
+
 void Font::invalidateTextureCache(CacheTexture* cacheTexture) {
     for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
         CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
@@ -83,17 +130,17 @@
 
 void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
         uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
-    int nPenX = x + glyph->mBitmapLeft;
-    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
+    float nPenX = x + glyph->mBitmapLeft;
+    float nPenY = y + (glyph->mBitmapTop + glyph->mBitmapHeight);
+
+    float width = (float) glyph->mBitmapWidth;
+    float height = (float) glyph->mBitmapHeight;
 
     float u1 = glyph->mBitmapMinU;
     float u2 = glyph->mBitmapMaxU;
     float v1 = glyph->mBitmapMinV;
     float v2 = glyph->mBitmapMaxV;
 
-    int width = (int) glyph->mBitmapWidth;
-    int height = (int) glyph->mBitmapHeight;
-
     mState->appendMeshQuad(nPenX, nPenY, u1, v2,
             nPenX + width, nPenY, u2, v2,
             nPenX + width, nPenY - height, u2, v1,
@@ -136,7 +183,7 @@
     vOffset += glyph->mBitmapTop + height;
 
     SkPoint destination[4];
-    measure.getPosTan(x + hOffset +  glyph->mBitmapLeft + halfWidth, position, tangent);
+    measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
 
     // Move along the tangent and offset by the normal
     destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
@@ -176,24 +223,13 @@
 
     // Is the glyph still in texture cache?
     if (!cachedGlyph->mIsValid) {
-        const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
+        const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit, NULL);
         updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
     }
 
     return cachedGlyph;
 }
 
-void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
-        int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
-    if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
-        render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
-                bitmapW, bitmapH, NULL, NULL);
-    } else {
-        render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
-                0, 0, NULL, NULL);
-    }
-}
-
 void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
             int numGlyphs, int x, int y, const float* positions) {
     render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
@@ -298,71 +334,40 @@
     text += start;
     int glyphsCount = 0;
 
-    if (CC_LIKELY(positions == NULL)) {
-        SkFixed prevRsbDelta = 0;
+    const SkPaint::Align align = paint->getTextAlign();
 
-        float penX = x + 0.5f;
-        int penY = y;
+    while (glyphsCount < numGlyphs) {
+        glyph_t glyph = GET_GLYPH(text);
 
-        while (glyphsCount < numGlyphs) {
-            glyph_t glyph = GET_GLYPH(text);
-
-            // Reached the end of the string
-            if (IS_END_OF_STRING(glyph)) {
-                break;
-            }
-
-            CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
-            penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
-            prevRsbDelta = cachedGlyph->mRsbDelta;
-
-            // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
-            if (cachedGlyph->mIsValid) {
-                (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
-                        bitmap, bitmapW, bitmapH, bounds, positions);
-            }
-
-            penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
-
-            glyphsCount++;
+        // Reached the end of the string
+        if (IS_END_OF_STRING(glyph)) {
+            break;
         }
-    } else {
-        const SkPaint::Align align = paint->getTextAlign();
 
-        // This is for renderPosText()
-        while (glyphsCount < numGlyphs) {
-            glyph_t glyph = GET_GLYPH(text);
+        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
 
-            // Reached the end of the string
-            if (IS_END_OF_STRING(glyph)) {
-                break;
+        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
+        if (cachedGlyph->mIsValid) {
+            int penX = x + positions[(glyphsCount << 1)];
+            int penY = y + positions[(glyphsCount << 1) + 1];
+
+            switch (align) {
+                case SkPaint::kRight_Align:
+                    penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
+                    penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
+                    break;
+                case SkPaint::kCenter_Align:
+                    penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
+                    penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
+                default:
+                    break;
             }
 
-            CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
-
-            // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
-            if (cachedGlyph->mIsValid) {
-                int penX = x + positions[(glyphsCount << 1)];
-                int penY = y + positions[(glyphsCount << 1) + 1];
-
-                switch (align) {
-                    case SkPaint::kRight_Align:
-                        penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
-                        penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
-                        break;
-                    case SkPaint::kCenter_Align:
-                        penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
-                        penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
-                    default:
-                        break;
-                }
-
-                (*this.*render)(cachedGlyph, penX, penY,
-                        bitmap, bitmapW, bitmapH, bounds, positions);
-            }
-
-            glyphsCount++;
+            (*this.*render)(cachedGlyph, penX, penY,
+                    bitmap, bitmapW, bitmapH, bounds, positions);
         }
+
+        glyphsCount++;
     }
 }
 
@@ -379,7 +384,7 @@
     uint32_t startY = 0;
 
     // Get the bitmap for the glyph
-    paint->findImage(skiaGlyph);
+    paint->findImage(skiaGlyph, NULL);
     mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
 
     if (!glyph->mIsValid) {
@@ -409,7 +414,7 @@
     CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
     mCachedGlyphs.add(glyph, newGlyph);
 
-    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
+    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph, NULL);
     newGlyph->mGlyphIndex = skiaGlyph.fID;
     newGlyph->mIsValid = false;
 
@@ -418,24 +423,16 @@
     return newGlyph;
 }
 
-Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
-        int flags, uint32_t italicStyle, uint32_t scaleX,
-        SkPaint::Style style, uint32_t strokeWidth) {
-    Vector<Font*> &activeFonts = state->mActiveFonts;
+Font* Font::create(FontRenderer* state, const SkPaint* paint, const mat4& matrix) {
+    FontDescription description(paint, matrix);
+    Font* font = state->mActiveFonts.get(description);
 
-    for (uint32_t i = 0; i < activeFonts.size(); i++) {
-        Font* font = activeFonts[i];
-        if (font->mFontId == fontId && font->mFontSize == fontSize &&
-                font->mFlags == flags && font->mItalicStyle == italicStyle &&
-                font->mScaleX == scaleX && font->mStyle == style &&
-                (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
-            return font;
-        }
+    if (font) {
+        return font;
     }
 
-    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
-            scaleX, style, strokeWidth);
-    activeFonts.push(newFont);
+    Font* newFont = new Font(state, description);
+    state->mActiveFonts.put(description, newFont);
     return newFont;
 }
 
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
index 7cab31e..6ddf162 100644
--- a/libs/hwui/font/Font.h
+++ b/libs/hwui/font/Font.h
@@ -25,6 +25,7 @@
 
 #include "CachedGlyphInfo.h"
 #include "../Rect.h"
+#include "../Matrix.h"
 
 namespace android {
 namespace uirenderer {
@@ -45,31 +46,52 @@
         kFakeBold = 1
     };
 
+    struct FontDescription {
+        FontDescription(const SkPaint* paint, const mat4& matrix);
+
+        static int compare(const FontDescription& lhs, const FontDescription& rhs);
+
+        hash_t hash() const;
+
+        bool operator==(const FontDescription& other) const {
+            return compare(*this, other) == 0;
+        }
+
+        bool operator!=(const FontDescription& other) const {
+            return compare(*this, other) != 0;
+        }
+
+        SkFontID mFontId;
+        float mFontSize;
+        int mFlags;
+        float mItalicStyle;
+        float mScaleX;
+        uint8_t mStyle;
+        float mStrokeWidth;
+    };
+
     ~Font();
 
-    /**
-     * Renders the specified string of text.
-     * If bitmap is specified, it will be used as the render target
-     */
-    void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
-            int numGlyphs, int x, int y, uint8_t *bitmap = NULL,
-            uint32_t bitmapW = 0, uint32_t bitmapH = 0);
-
-    void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+    void render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
             int numGlyphs, int x, int y, const float* positions);
 
-    void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+    void render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
             int numGlyphs, SkPath* path, float hOffset, float vOffset);
 
+    const Font::FontDescription& getDescription() const {
+        return mDescription;
+    }
+
     /**
      * Creates a new font associated with the specified font state.
      */
-    static Font* create(FontRenderer* state, uint32_t fontId, float fontSize,
-            int flags, uint32_t italicStyle, uint32_t scaleX, SkPaint::Style style,
-            uint32_t strokeWidth);
+    static Font* create(FontRenderer* state, const SkPaint* paint, const mat4& matrix);
 
 private:
     friend class FontRenderer;
+
+    Font(FontRenderer* state, const Font::FontDescription& desc);
+
     typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
             uint32_t, uint32_t, Rect*, const float*);
 
@@ -88,12 +110,6 @@
     void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
             int numGlyphs, Rect *bounds, const float* positions);
 
-    Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
-            uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
-
-    // Cache of glyphs
-    DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
-
     void invalidateTextureCache(CacheTexture* cacheTexture = NULL);
 
     CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching);
@@ -115,15 +131,25 @@
     CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false);
 
     FontRenderer* mState;
-    uint32_t mFontId;
-    float mFontSize;
-    int mFlags;
-    uint32_t mItalicStyle;
-    uint32_t mScaleX;
-    SkPaint::Style mStyle;
-    uint32_t mStrokeWidth;
+    FontDescription mDescription;
+
+    // Cache of glyphs
+    DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
 };
 
+inline int strictly_order_type(const Font::FontDescription& lhs,
+        const Font::FontDescription& rhs) {
+    return Font::FontDescription::compare(lhs, rhs) < 0;
+}
+
+inline int compare_type(const Font::FontDescription& lhs, const Font::FontDescription& rhs) {
+    return Font::FontDescription::compare(lhs, rhs);
+}
+
+inline hash_t hash_type(const Font::FontDescription& entry) {
+    return entry.hash();
+}
+
 }; // namespace uirenderer
 }; // namespace android
 
diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h
index 12247ba..4f9c46b 100644
--- a/libs/hwui/font/FontUtil.h
+++ b/libs/hwui/font/FontUtil.h
@@ -37,7 +37,7 @@
 #if RENDER_TEXT_AS_GLYPHS
     typedef uint16_t glyph_t;
     #define TO_GLYPH(g) g
-    #define GET_METRICS(paint, glyph) paint->getGlyphMetrics(glyph)
+    #define GET_METRICS(paint, glyph, matrix) paint->getGlyphMetrics(glyph, matrix)
     #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
     #define IS_END_OF_STRING(glyph) false
 
@@ -50,7 +50,7 @@
 #else
     typedef SkUnichar glyph_t;
     #define TO_GLYPH(g) ((SkUnichar) g)
-    #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph)
+    #define GET_METRICS(paint, glyph, matrix) paint->getUnicharMetrics(glyph, matrix)
     #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
     #define IS_END_OF_STRING(glyph) glyph < 0
 #endif
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ScaledTextActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ScaledTextActivity.java
index 2e1487d..e1bf3ea 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ScaledTextActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ScaledTextActivity.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.Path;
 import android.os.Bundle;
 import android.view.View;
 
@@ -43,15 +44,27 @@
     }
 
     public static class ScaledTextView extends View {
+        private static final String TEXT = "Hello libhwui! ";
+
         private final Paint mPaint;
+        private final Paint mShadowPaint;
+        private final Path mPath;
+
         private float mScale = 1.0f;
 
         public ScaledTextView(Context c) {
             super(c);
 
+            mPath = makePath();
+
             mPaint = new Paint();
             mPaint.setAntiAlias(true);
             mPaint.setTextSize(20.0f);
+
+            mShadowPaint = new Paint();
+            mShadowPaint.setAntiAlias(true);
+            mShadowPaint.setShadowLayer(3.0f, 0.0f, 3.0f, 0xff000000);
+            mShadowPaint.setTextSize(20.0f);
         }
 
         public float getTextScale() {
@@ -63,17 +76,47 @@
             invalidate();
         }
 
+        private static Path makePath() {
+            Path path = new Path();
+            buildPath(path);
+            return path;
+        }
+
+        private static void buildPath(Path path) {
+            path.moveTo(0.0f, 0.0f);
+            path.cubicTo(0.0f, 0.0f, 100.0f, 150.0f, 100.0f, 200.0f);
+            path.cubicTo(100.0f, 200.0f, 50.0f, 300.0f, -80.0f, 200.0f);
+            path.cubicTo(-80.0f, 200.0f, 100.0f, 200.0f, 200.0f, 0.0f);
+        }
+
         @Override
         protected void onDraw(Canvas canvas) {
             super.onDraw(canvas);
             canvas.drawARGB(255, 255, 255, 255);
 
-            canvas.drawText("Hello libhwui!", 30.0f, 30.0f, mPaint);
+            canvas.drawText(TEXT, 30.0f, 30.0f, mPaint);
+
             canvas.translate(0.0f, 50.0f);
+
             canvas.save();
             canvas.scale(mScale, mScale);
-            canvas.drawText("Hello libhwui!", 30.0f, 30.0f, mPaint);
+            canvas.drawText(TEXT, 30.0f, 30.0f, mPaint);
             canvas.restore();
+
+            canvas.translate(0.0f, 250.0f);
+            canvas.save();
+            canvas.scale(3.0f, 3.0f);
+            canvas.drawText(TEXT, 30.0f, 30.0f, mShadowPaint);
+            canvas.translate(100.0f, 0.0f);
+//            canvas.drawTextOnPath(TEXT + TEXT + TEXT, mPath, 0.0f, 0.0f, mPaint);
+            canvas.restore();
+
+            float width = mPaint.measureText(TEXT);
+
+            canvas.translate(500.0f, 0.0f);
+            canvas.rotate(45.0f, width * 3.0f / 2.0f, 0.0f);
+            canvas.scale(3.0f, 3.0f);
+            canvas.drawText(TEXT, 30.0f, 30.0f, mPaint);
         }
     }
 }
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java
index 9849e3c..ceccfaa 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java
@@ -41,26 +41,26 @@
         setContentView(view);
     }
 
-    private Path makePath() {
+    private static Path makePath() {
         Path path = new Path();
         buildPath(path);
         return path;
     }
 
-    private void buildPath(Path path) {
+    private static void buildPath(Path path) {
         path.moveTo(0.0f, 0.0f);
         path.cubicTo(0.0f, 0.0f, 100.0f, 150.0f, 100.0f, 200.0f);
         path.cubicTo(100.0f, 200.0f, 50.0f, 300.0f, -80.0f, 200.0f);
         path.cubicTo(-80.0f, 200.0f, 100.0f, 200.0f, 200.0f, 0.0f);
     }
 
-    private Path makeStraightPath() {
+    private static Path makeStraightPath() {
         Path path = new Path();
         buildStraightPath(path);
         return path;
     }
 
-    private void buildStraightPath(Path path) {
+    private static void buildStraightPath(Path path) {
         path.moveTo(0.0f, 0.0f);
         path.lineTo(400.0f, 0.0f);
     }