Refactor FontRenderer.cpp

FontRenderer.h defined several classes and structures that now live
in the font/ folder. This will make the code easier to read and
maintain.

Change-Id: I3dc044e9bde1d6515f8704f5c72462877d279fe2
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index c3a07a1..c0f79df 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -6,6 +6,8 @@
 ifeq ($(USE_OPENGL_RENDERER),true)
 	LOCAL_SRC_FILES:= \
 		utils/SortedListImpl.cpp \
+		font/CacheTexture.cpp \
+		font/Font.cpp \
 		FontRenderer.cpp \
 		GammaFontRenderer.cpp \
 		Caches.cpp \
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 95ccdfc..e66ab54 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -25,585 +25,12 @@
 #include "Caches.h"
 #include "Debug.h"
 #include "FontRenderer.h"
-#include "Caches.h"
+#include "Rect.h"
 
 namespace android {
 namespace uirenderer {
 
 ///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#define DEFAULT_TEXT_SMALL_CACHE_WIDTH 1024
-#define DEFAULT_TEXT_SMALL_CACHE_HEIGHT 256
-#define DEFAULT_TEXT_LARGE_CACHE_WIDTH 2048
-#define DEFAULT_TEXT_LARGE_CACHE_HEIGHT 512
-#define CACHE_BLOCK_ROUNDING_SIZE 4
-
-#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
-
-///////////////////////////////////////////////////////////////////////////////
-// CacheBlock
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
- * order, except for the final block (the remainder space at the right, since we fill from the
- * left).
- */
-CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock *newBlock) {
-#if DEBUG_FONT_RENDERER
-    ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
-            newBlock, newBlock->mX, newBlock->mY,
-            newBlock->mWidth, newBlock->mHeight);
-#endif
-    CacheBlock *currBlock = head;
-    CacheBlock *prevBlock = NULL;
-    while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
-        if (newBlock->mWidth < currBlock->mWidth) {
-            newBlock->mNext = currBlock;
-            newBlock->mPrev = prevBlock;
-            currBlock->mPrev = newBlock;
-            if (prevBlock) {
-                prevBlock->mNext = newBlock;
-                return head;
-            } else {
-                return newBlock;
-            }
-        }
-        prevBlock = currBlock;
-        currBlock = currBlock->mNext;
-    }
-    // new block larger than all others - insert at end (but before the remainder space, if there)
-    newBlock->mNext = currBlock;
-    newBlock->mPrev = prevBlock;
-    if (currBlock) {
-        currBlock->mPrev = newBlock;
-    }
-    if (prevBlock) {
-        prevBlock->mNext = newBlock;
-        return head;
-    } else {
-        return newBlock;
-    }
-}
-
-CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock *blockToRemove) {
-#if DEBUG_FONT_RENDERER
-    ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
-            blockToRemove, blockToRemove->mX, blockToRemove->mY,
-            blockToRemove->mWidth, blockToRemove->mHeight);
-#endif
-    CacheBlock* newHead = head;
-    CacheBlock* nextBlock = blockToRemove->mNext;
-    CacheBlock* prevBlock = blockToRemove->mPrev;
-    if (prevBlock) {
-        prevBlock->mNext = nextBlock;
-    } else {
-        newHead = nextBlock;
-    }
-    if (nextBlock) {
-        nextBlock->mPrev = prevBlock;
-    }
-    delete blockToRemove;
-    return newHead;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// CacheTexture
-///////////////////////////////////////////////////////////////////////////////
-
-bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
-    if (glyph.fHeight + TEXTURE_BORDER_SIZE > mHeight) {
-        return false;
-    }
-
-    uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
-    uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
-    // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
-    // This columns for glyphs that are close but not necessarily exactly the same size. It trades
-    // off the loss of a few pixels for some glyphs against the ability to store more glyphs
-    // of varying sizes in one block.
-    uint16_t roundedUpW =
-            (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
-    CacheBlock *cacheBlock = mCacheBlocks;
-    while (cacheBlock) {
-        // Store glyph in this block iff: it fits the block's remaining space and:
-        // it's the remainder space (mY == 0) or there's only enough height for this one glyph
-        // or it's within ROUNDING_SIZE of the block width
-        if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
-                (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
-                        (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
-            if (cacheBlock->mHeight - glyphH < glyphH) {
-                // Only enough space for this glyph - don't bother rounding up the width
-                roundedUpW = glyphW;
-            }
-            *retOriginX = cacheBlock->mX;
-            *retOriginY = cacheBlock->mY;
-            // If this is the remainder space, create a new cache block for this column. Otherwise,
-            // adjust the info about this column.
-            if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
-                uint16_t oldX = cacheBlock->mX;
-                // Adjust remainder space dimensions
-                cacheBlock->mWidth -= roundedUpW;
-                cacheBlock->mX += roundedUpW;
-                if (mHeight - glyphH >= glyphH) {
-                    // There's enough height left over to create a new CacheBlock
-                    CacheBlock *newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE,
-                            roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE);
-#if DEBUG_FONT_RENDERER
-                    ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
-                            newBlock, newBlock->mX, newBlock->mY,
-                            newBlock->mWidth, newBlock->mHeight);
-#endif
-                    mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
-                }
-            } else {
-                // Insert into current column and adjust column dimensions
-                cacheBlock->mY += glyphH;
-                cacheBlock->mHeight -= glyphH;
-#if DEBUG_FONT_RENDERER
-                ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
-                        cacheBlock, cacheBlock->mX, cacheBlock->mY,
-                        cacheBlock->mWidth, cacheBlock->mHeight);
-#endif
-            }
-            if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
-                // If remaining space in this block is too small to be useful, remove it
-                mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
-            }
-            mDirty = true;
-#if DEBUG_FONT_RENDERER
-            ALOGD("fitBitmap: current block list:");
-            mCacheBlocks->output();
-#endif
-            ++mNumGlyphs;
-            return true;
-        }
-        cacheBlock = cacheBlock->mNext;
-    }
-#if DEBUG_FONT_RENDERER
-    ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
-#endif
-    return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// 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() {
-    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
-        if (mState->mActiveFonts[ct] == this) {
-            mState->mActiveFonts.removeAt(ct);
-            break;
-        }
-    }
-
-    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
-        delete mCachedGlyphs.valueAt(i);
-    }
-}
-
-void Font::invalidateTextureCache(CacheTexture *cacheTexture) {
-    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
-        CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
-        if (cacheTexture == NULL || cachedGlyph->mCacheTexture == cacheTexture) {
-            cachedGlyph->mIsValid = false;
-        }
-    }
-}
-
-void Font::measureCachedGlyph(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;
-
-    int width = (int) glyph->mBitmapWidth;
-    int height = (int) glyph->mBitmapHeight;
-
-    if (bounds->bottom > nPenY) {
-        bounds->bottom = nPenY;
-    }
-    if (bounds->left > nPenX) {
-        bounds->left = nPenX;
-    }
-    if (bounds->right < nPenX + width) {
-        bounds->right = nPenX + width;
-    }
-    if (bounds->top < nPenY + height) {
-        bounds->top = nPenY + height;
-    }
-}
-
-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 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,
-            nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
-}
-
-void Font::drawCachedGlyphBitmap(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;
-
-    uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
-    uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
-
-    CacheTexture *cacheTexture = glyph->mCacheTexture;
-    uint32_t cacheWidth = cacheTexture->mWidth;
-    const uint8_t* cacheBuffer = cacheTexture->mTexture;
-
-    uint32_t cacheX = 0, cacheY = 0;
-    int32_t bX = 0, bY = 0;
-    for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
-        for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
-#if DEBUG_FONT_RENDERER
-            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
-                ALOGE("Skipping invalid index");
-                continue;
-            }
-#endif
-            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
-            bitmap[bY * bitmapW + bX] = tempCol;
-        }
-    }
-}
-
-void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
-        SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
-    const float halfWidth = glyph->mBitmapWidth * 0.5f;
-    const float height = glyph->mBitmapHeight;
-
-    vOffset += glyph->mBitmapTop + height;
-
-    SkPoint destination[4];
-    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,
-            -tangent->fY * halfWidth + tangent->fX * vOffset);
-    destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
-            tangent->fY * halfWidth + tangent->fX * vOffset);
-    destination[2].set(destination[1].fX + tangent->fY * height,
-            destination[1].fY - tangent->fX * height);
-    destination[3].set(destination[0].fX + tangent->fY * height,
-            destination[0].fY - tangent->fX * height);
-
-    const float u1 = glyph->mBitmapMinU;
-    const float u2 = glyph->mBitmapMaxU;
-    const float v1 = glyph->mBitmapMinV;
-    const float v2 = glyph->mBitmapMaxV;
-
-    mState->appendRotatedMeshQuad(
-            position->fX + destination[0].fX,
-            position->fY + destination[0].fY, u1, v2,
-            position->fX + destination[1].fX,
-            position->fY + destination[1].fY, u2, v2,
-            position->fX + destination[2].fX,
-            position->fY + destination[2].fY, u2, v1,
-            position->fX + destination[3].fX,
-            position->fY + destination[3].fY, u1, v1,
-            glyph->mCacheTexture);
-}
-
-CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) {
-    CachedGlyphInfo* cachedGlyph = NULL;
-    ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
-    if (index >= 0) {
-        cachedGlyph = mCachedGlyphs.valueAt(index);
-    } else {
-        cachedGlyph = cacheGlyph(paint, textUnit, precaching);
-    }
-
-    // Is the glyph still in texture cache?
-    if (!cachedGlyph->mIsValid) {
-        const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
-        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,
-            0, 0, NULL, positions);
-}
-
-void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
-        int numGlyphs, SkPath* path, float hOffset, float vOffset) {
-    if (numGlyphs == 0 || text == NULL || len == 0) {
-        return;
-    }
-
-    text += start;
-
-    int glyphsCount = 0;
-    SkFixed prevRsbDelta = 0;
-
-    float penX = 0.0f;
-
-    SkPoint position;
-    SkVector tangent;
-
-    SkPathMeasure measure(*path, false);
-    float pathLength = SkScalarToFloat(measure.getLength());
-
-    if (paint->getTextAlign() != SkPaint::kLeft_Align) {
-        float textWidth = SkScalarToFloat(paint->measureText(text, len));
-        float pathOffset = pathLength;
-        if (paint->getTextAlign() == SkPaint::kCenter_Align) {
-            textWidth *= 0.5f;
-            pathOffset *= 0.5f;
-        }
-        penX += pathOffset - textWidth;
-    }
-
-    while (glyphsCount < numGlyphs && penX < pathLength) {
-        glyph_t glyph = GET_GLYPH(text);
-
-        if (IS_END_OF_STRING(glyph)) {
-            break;
-        }
-
-        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
-        penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
-        prevRsbDelta = cachedGlyph->mRsbDelta;
-
-        if (cachedGlyph->mIsValid) {
-            drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
-        }
-
-        penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
-
-        glyphsCount++;
-    }
-}
-
-void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
-        int numGlyphs, Rect *bounds, const float* positions) {
-    if (bounds == NULL) {
-        ALOGE("No return rectangle provided to measure text");
-        return;
-    }
-    bounds->set(1e6, -1e6, -1e6, 1e6);
-    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
-}
-
-void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
-
-    if (numGlyphs == 0 || text == NULL) {
-        return;
-    }
-    int glyphsCount = 0;
-
-    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, true);
-
-        glyphsCount++;
-    }
-}
-
-void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
-        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
-        uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
-    if (numGlyphs == 0 || text == NULL || len == 0) {
-        return;
-    }
-
-    static RenderGlyph gRenderGlyph[] = {
-            &android::uirenderer::Font::drawCachedGlyph,
-            &android::uirenderer::Font::drawCachedGlyphBitmap,
-            &android::uirenderer::Font::measureCachedGlyph
-    };
-    RenderGlyph render = gRenderGlyph[mode];
-
-    text += start;
-    int glyphsCount = 0;
-
-    if (CC_LIKELY(positions == NULL)) {
-        SkFixed prevRsbDelta = 0;
-
-        float penX = x + 0.5f;
-        int penY = y;
-
-        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++;
-        }
-    } else {
-        const SkPaint::Align align = paint->getTextAlign();
-
-        // This is for renderPosText()
-        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);
-
-            // 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++;
-        }
-    }
-}
-
-void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
-        bool precaching) {
-    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
-    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
-    glyph->mBitmapLeft = skiaGlyph.fLeft;
-    glyph->mBitmapTop = skiaGlyph.fTop;
-    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
-    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
-
-    uint32_t startX = 0;
-    uint32_t startY = 0;
-
-    // Get the bitmap for the glyph
-    paint->findImage(skiaGlyph);
-    mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
-
-    if (!glyph->mIsValid) {
-        return;
-    }
-
-    uint32_t endX = startX + skiaGlyph.fWidth;
-    uint32_t endY = startY + skiaGlyph.fHeight;
-
-    glyph->mStartX = startX;
-    glyph->mStartY = startY;
-    glyph->mBitmapWidth = skiaGlyph.fWidth;
-    glyph->mBitmapHeight = skiaGlyph.fHeight;
-
-    uint32_t cacheWidth = glyph->mCacheTexture->mWidth;
-    uint32_t cacheHeight = glyph->mCacheTexture->mHeight;
-
-    glyph->mBitmapMinU = startX / (float) cacheWidth;
-    glyph->mBitmapMinV = startY / (float) cacheHeight;
-    glyph->mBitmapMaxU = endX / (float) cacheWidth;
-    glyph->mBitmapMaxV = endY / (float) cacheHeight;
-
-    mState->mUploadTexture = true;
-}
-
-CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
-    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
-    mCachedGlyphs.add(glyph, newGlyph);
-
-    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
-    newGlyph->mGlyphIndex = skiaGlyph.fID;
-    newGlyph->mIsValid = false;
-
-    updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
-
-    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;
-
-    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;
-        }
-    }
-
-    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
-            scaleX, style, strokeWidth);
-    activeFonts.push(newFont);
-    return newFont;
-}
-
-///////////////////////////////////////////////////////////////////////////////
 // FontRenderer
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -636,20 +63,25 @@
     if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
         mSmallCacheWidth = atoi(property);
     }
+
     if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
         mSmallCacheHeight = atoi(property);
     }
+
     if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
         mLargeCacheWidth = atoi(property);
     }
+
     if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
         mLargeCacheHeight = atoi(property);
     }
-    GLint maxTextureSize = Caches::getInstance().maxTextureSize;
-    mSmallCacheWidth = (mSmallCacheWidth > maxTextureSize) ? maxTextureSize : mSmallCacheWidth;
-    mSmallCacheHeight = (mSmallCacheHeight > maxTextureSize) ? maxTextureSize : mSmallCacheHeight;
-    mLargeCacheWidth = (mLargeCacheWidth > maxTextureSize) ? maxTextureSize : mLargeCacheWidth;
-    mLargeCacheHeight = (mLargeCacheHeight > maxTextureSize) ? maxTextureSize : mLargeCacheHeight;
+
+    uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
+    mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
+    mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
+    mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
+    mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
+
     if (sLogFontRendererCreate) {
         INIT_LOGD("  Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
                 mSmallCacheWidth, mSmallCacheHeight,
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 241b73e..58aa9c3 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -17,268 +17,22 @@
 #ifndef ANDROID_HWUI_FONT_RENDERER_H
 #define ANDROID_HWUI_FONT_RENDERER_H
 
-#include <utils/String8.h>
-#include <utils/String16.h>
 #include <utils/Vector.h>
-#include <utils/KeyedVector.h>
 
-#include <SkScalerContext.h>
 #include <SkPaint.h>
-#include <SkPathMeasure.h>
-#include <SkPoint.h>
 
 #include <GLES2/gl2.h>
 
-#include "Rect.h"
+#include "font/FontUtil.h"
+#include "font/CacheTexture.h"
+#include "font/CachedGlyphInfo.h"
+#include "font/Font.h"
 #include "Properties.h"
 
 namespace android {
 namespace uirenderer {
 
 ///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#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_GLYPH(text) nextGlyph((const uint16_t**) &text)
-    #define IS_END_OF_STRING(glyph) false
-#else
-    typedef SkUnichar glyph_t;
-    #define TO_GLYPH(g) ((SkUnichar) g)
-    #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph)
-    #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
-    #define IS_END_OF_STRING(glyph) glyph < 0
-#endif
-
-#define TEXTURE_BORDER_SIZE 1
-
-///////////////////////////////////////////////////////////////////////////////
-// Declarations
-///////////////////////////////////////////////////////////////////////////////
-
-class FontRenderer;
-
-/**
- * CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
- * Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
- * When we add a glyph to the cache, we see if it fits within one of the existing columns that
- * have already been started (this is the case if the glyph fits vertically as well as
- * horizontally, and if its width is sufficiently close to the column width to avoid
- * sub-optimal packing of small glyphs into wide columns). If there is no column in which the
- * glyph fits, we check the final node, which is the remaining space in the cache, creating
- * a new column as appropriate.
- *
- * As columns fill up, we remove their CacheBlock from the list to avoid having to check
- * small blocks in the future.
- */
-struct CacheBlock {
-    uint16_t mX;
-    uint16_t mY;
-    uint16_t mWidth;
-    uint16_t mHeight;
-    CacheBlock* mNext;
-    CacheBlock* mPrev;
-
-    CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height, bool empty = false):
-        mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL)
-    {
-    }
-
-    static CacheBlock* insertBlock(CacheBlock* head, CacheBlock *newBlock);
-
-    static CacheBlock* removeBlock(CacheBlock* head, CacheBlock *blockToRemove);
-
-    void output() {
-        CacheBlock *currBlock = this;
-        while (currBlock) {
-            ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d",
-                    currBlock, currBlock->mX, currBlock->mY, currBlock->mWidth, currBlock->mHeight);
-            currBlock = currBlock->mNext;
-        }
-    }
-};
-
-class CacheTexture {
-public:
-    CacheTexture(uint16_t width, uint16_t height) :
-            mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
-            mLinearFiltering(false), mDirty(false), mNumGlyphs(0) {
-        mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
-                mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
-    }
-
-    ~CacheTexture() {
-        if (mTexture) {
-            delete[] mTexture;
-        }
-        if (mTextureId) {
-            glDeleteTextures(1, &mTextureId);
-        }
-        reset();
-    }
-
-    void reset() {
-        // Delete existing cache blocks
-        while (mCacheBlocks != NULL) {
-            CacheBlock* tmpBlock = mCacheBlocks;
-            mCacheBlocks = mCacheBlocks->mNext;
-            delete tmpBlock;
-        }
-        mNumGlyphs = 0;
-    }
-
-    void init() {
-        // reset, then create a new remainder space to start again
-        reset();
-        mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
-                mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
-    }
-
-    bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
-
-    uint8_t* mTexture;
-    GLuint mTextureId;
-    uint16_t mWidth;
-    uint16_t mHeight;
-    bool mLinearFiltering;
-    bool mDirty;
-    uint16_t mNumGlyphs;
-    CacheBlock* mCacheBlocks;
-};
-
-struct CachedGlyphInfo {
-    // Has the cache been invalidated?
-    bool mIsValid;
-    // Location of the cached glyph in the bitmap
-    // in case we need to resize the texture or
-    // render to bitmap
-    uint32_t mStartX;
-    uint32_t mStartY;
-    uint32_t mBitmapWidth;
-    uint32_t mBitmapHeight;
-    // Also cache texture coords for the quad
-    float mBitmapMinU;
-    float mBitmapMinV;
-    float mBitmapMaxU;
-    float mBitmapMaxV;
-    // Minimize how much we call freetype
-    uint32_t mGlyphIndex;
-    uint32_t mAdvanceX;
-    uint32_t mAdvanceY;
-    // Values below contain a glyph's origin in the bitmap
-    int32_t mBitmapLeft;
-    int32_t mBitmapTop;
-    // Auto-kerning
-    SkFixed mLsbDelta;
-    SkFixed mRsbDelta;
-    CacheTexture* mCacheTexture;
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
-// Font
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Represents a font, defined by a Skia font id and a font size. A font is used
- * to generate glyphs and cache them in the FontState.
- */
-class Font {
-public:
-    enum Style {
-        kFakeBold = 1
-    };
-
-    ~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,
-            int numGlyphs, int x, int y, const float* positions);
-
-    void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
-            int numGlyphs, SkPath* path, float hOffset, float vOffset);
-
-    /**
-     * 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);
-
-protected:
-    friend class FontRenderer;
-    typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
-            uint32_t, uint32_t, Rect*, const float*);
-
-    enum RenderMode {
-        FRAMEBUFFER,
-        BITMAP,
-        MEASURE,
-    };
-
-    void precache(SkPaint* paint, const char* text, int numGlyphs);
-
-    void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
-            int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
-            uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
-
-    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);
-    void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
-            bool precaching);
-
-    void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
-            uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
-            Rect* bounds, const float* pos);
-    void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
-            uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
-            Rect* bounds, const float* pos);
-    void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
-            uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
-            Rect* bounds, const float* pos);
-    void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
-            SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
-
-    CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false);
-
-    static glyph_t nextGlyph(const uint16_t** srcPtr) {
-        const uint16_t* src = *srcPtr;
-        glyph_t g = *src++;
-        *srcPtr = src;
-        return g;
-    }
-
-    FontRenderer* mState;
-    uint32_t mFontId;
-    float mFontSize;
-    int mFlags;
-    uint32_t mItalicStyle;
-    uint32_t mScaleX;
-    SkPaint::Style mStyle;
-    uint32_t mStrokeWidth;
-};
-
-///////////////////////////////////////////////////////////////////////////////
 // Renderer
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
new file mode 100644
index 0000000..5fb2636
--- /dev/null
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <utils/Log.h>
+
+#include "Debug.h"
+#include "CacheTexture.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// CacheBlock
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
+ * order, except for the final block (the remainder space at the right, since we fill from the
+ * left).
+ */
+CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock *newBlock) {
+#if DEBUG_FONT_RENDERER
+    ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
+            newBlock, newBlock->mX, newBlock->mY,
+            newBlock->mWidth, newBlock->mHeight);
+#endif
+    CacheBlock *currBlock = head;
+    CacheBlock *prevBlock = NULL;
+    while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
+        if (newBlock->mWidth < currBlock->mWidth) {
+            newBlock->mNext = currBlock;
+            newBlock->mPrev = prevBlock;
+            currBlock->mPrev = newBlock;
+            if (prevBlock) {
+                prevBlock->mNext = newBlock;
+                return head;
+            } else {
+                return newBlock;
+            }
+        }
+        prevBlock = currBlock;
+        currBlock = currBlock->mNext;
+    }
+    // new block larger than all others - insert at end (but before the remainder space, if there)
+    newBlock->mNext = currBlock;
+    newBlock->mPrev = prevBlock;
+    if (currBlock) {
+        currBlock->mPrev = newBlock;
+    }
+    if (prevBlock) {
+        prevBlock->mNext = newBlock;
+        return head;
+    } else {
+        return newBlock;
+    }
+}
+
+CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock *blockToRemove) {
+#if DEBUG_FONT_RENDERER
+    ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
+            blockToRemove, blockToRemove->mX, blockToRemove->mY,
+            blockToRemove->mWidth, blockToRemove->mHeight);
+#endif
+    CacheBlock* newHead = head;
+    CacheBlock* nextBlock = blockToRemove->mNext;
+    CacheBlock* prevBlock = blockToRemove->mPrev;
+    if (prevBlock) {
+        prevBlock->mNext = nextBlock;
+    } else {
+        newHead = nextBlock;
+    }
+    if (nextBlock) {
+        nextBlock->mPrev = prevBlock;
+    }
+    delete blockToRemove;
+    return newHead;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CacheTexture
+///////////////////////////////////////////////////////////////////////////////
+
+bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
+    if (glyph.fHeight + TEXTURE_BORDER_SIZE > mHeight) {
+        return false;
+    }
+
+    uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
+    uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
+    // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
+    // This columns for glyphs that are close but not necessarily exactly the same size. It trades
+    // off the loss of a few pixels for some glyphs against the ability to store more glyphs
+    // of varying sizes in one block.
+    uint16_t roundedUpW =
+            (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
+    CacheBlock *cacheBlock = mCacheBlocks;
+    while (cacheBlock) {
+        // Store glyph in this block iff: it fits the block's remaining space and:
+        // it's the remainder space (mY == 0) or there's only enough height for this one glyph
+        // or it's within ROUNDING_SIZE of the block width
+        if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
+                (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
+                        (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
+            if (cacheBlock->mHeight - glyphH < glyphH) {
+                // Only enough space for this glyph - don't bother rounding up the width
+                roundedUpW = glyphW;
+            }
+            *retOriginX = cacheBlock->mX;
+            *retOriginY = cacheBlock->mY;
+            // If this is the remainder space, create a new cache block for this column. Otherwise,
+            // adjust the info about this column.
+            if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
+                uint16_t oldX = cacheBlock->mX;
+                // Adjust remainder space dimensions
+                cacheBlock->mWidth -= roundedUpW;
+                cacheBlock->mX += roundedUpW;
+                if (mHeight - glyphH >= glyphH) {
+                    // There's enough height left over to create a new CacheBlock
+                    CacheBlock *newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE,
+                            roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE);
+#if DEBUG_FONT_RENDERER
+                    ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
+                            newBlock, newBlock->mX, newBlock->mY,
+                            newBlock->mWidth, newBlock->mHeight);
+#endif
+                    mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
+                }
+            } else {
+                // Insert into current column and adjust column dimensions
+                cacheBlock->mY += glyphH;
+                cacheBlock->mHeight -= glyphH;
+#if DEBUG_FONT_RENDERER
+                ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
+                        cacheBlock, cacheBlock->mX, cacheBlock->mY,
+                        cacheBlock->mWidth, cacheBlock->mHeight);
+#endif
+            }
+            if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
+                // If remaining space in this block is too small to be useful, remove it
+                mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
+            }
+            mDirty = true;
+#if DEBUG_FONT_RENDERER
+            ALOGD("fitBitmap: current block list:");
+            mCacheBlocks->output();
+#endif
+            ++mNumGlyphs;
+            return true;
+        }
+        cacheBlock = cacheBlock->mNext;
+    }
+#if DEBUG_FONT_RENDERER
+    ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
+#endif
+    return false;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
new file mode 100644
index 0000000..7840d74
--- /dev/null
+++ b/libs/hwui/font/CacheTexture.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2012 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_CACHE_TEXTURE_H
+#define ANDROID_HWUI_CACHE_TEXTURE_H
+
+#include <GLES2/gl2.h>
+
+#include <SkScalerContext.h>
+
+#include <utils/Log.h>
+
+#include "FontUtil.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
+ * Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
+ * When we add a glyph to the cache, we see if it fits within one of the existing columns that
+ * have already been started (this is the case if the glyph fits vertically as well as
+ * horizontally, and if its width is sufficiently close to the column width to avoid
+ * sub-optimal packing of small glyphs into wide columns). If there is no column in which the
+ * glyph fits, we check the final node, which is the remaining space in the cache, creating
+ * a new column as appropriate.
+ *
+ * As columns fill up, we remove their CacheBlock from the list to avoid having to check
+ * small blocks in the future.
+ */
+struct CacheBlock {
+    uint16_t mX;
+    uint16_t mY;
+    uint16_t mWidth;
+    uint16_t mHeight;
+    CacheBlock* mNext;
+    CacheBlock* mPrev;
+
+    CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height, bool empty = false):
+        mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL)
+    {
+    }
+
+    static CacheBlock* insertBlock(CacheBlock* head, CacheBlock *newBlock);
+
+    static CacheBlock* removeBlock(CacheBlock* head, CacheBlock *blockToRemove);
+
+    void output() {
+        CacheBlock *currBlock = this;
+        while (currBlock) {
+            ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d",
+                    currBlock, currBlock->mX, currBlock->mY, currBlock->mWidth, currBlock->mHeight);
+            currBlock = currBlock->mNext;
+        }
+    }
+};
+
+class CacheTexture {
+public:
+    CacheTexture(uint16_t width, uint16_t height) :
+            mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
+            mLinearFiltering(false), mDirty(false), mNumGlyphs(0) {
+        mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
+                mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+    }
+
+    ~CacheTexture() {
+        if (mTexture) {
+            delete[] mTexture;
+        }
+        if (mTextureId) {
+            glDeleteTextures(1, &mTextureId);
+        }
+        reset();
+    }
+
+    void reset() {
+        // Delete existing cache blocks
+        while (mCacheBlocks != NULL) {
+            CacheBlock* tmpBlock = mCacheBlocks;
+            mCacheBlocks = mCacheBlocks->mNext;
+            delete tmpBlock;
+        }
+        mNumGlyphs = 0;
+    }
+
+    void init() {
+        // reset, then create a new remainder space to start again
+        reset();
+        mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
+                mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+    }
+
+    bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
+
+    uint8_t* mTexture;
+    GLuint mTextureId;
+    uint16_t mWidth;
+    uint16_t mHeight;
+    bool mLinearFiltering;
+    bool mDirty;
+    uint16_t mNumGlyphs;
+    CacheBlock* mCacheBlocks;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_CACHE_TEXTURE_H
diff --git a/libs/hwui/font/CachedGlyphInfo.h b/libs/hwui/font/CachedGlyphInfo.h
new file mode 100644
index 0000000..6680a00
--- /dev/null
+++ b/libs/hwui/font/CachedGlyphInfo.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 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_CACHED_GLYPH_INFO_H
+#define ANDROID_HWUI_CACHED_GLYPH_INFO_H
+
+#include <SkFixed.h>
+
+#include "CacheTexture.h"
+
+namespace android {
+namespace uirenderer {
+
+struct CachedGlyphInfo {
+    // Has the cache been invalidated?
+    bool mIsValid;
+    // Location of the cached glyph in the bitmap
+    // in case we need to resize the texture or
+    // render to bitmap
+    uint32_t mStartX;
+    uint32_t mStartY;
+    uint32_t mBitmapWidth;
+    uint32_t mBitmapHeight;
+    // Also cache texture coords for the quad
+    float mBitmapMinU;
+    float mBitmapMinV;
+    float mBitmapMaxU;
+    float mBitmapMaxV;
+    // Minimize how much we call freetype
+    uint32_t mGlyphIndex;
+    uint32_t mAdvanceX;
+    uint32_t mAdvanceY;
+    // Values below contain a glyph's origin in the bitmap
+    int32_t mBitmapLeft;
+    int32_t mBitmapTop;
+    // Auto-kerning
+    SkFixed mLsbDelta;
+    SkFixed mRsbDelta;
+    CacheTexture* mCacheTexture;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_CACHED_GLYPH_INFO_H
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
new file mode 100644
index 0000000..0439875
--- /dev/null
+++ b/libs/hwui/font/Font.cpp
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <cutils/compiler.h>
+
+#include <SkUtils.h>
+
+#include "Debug.h"
+#include "FontUtil.h"
+#include "Font.h"
+#include "FontRenderer.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// 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() {
+    for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
+        if (mState->mActiveFonts[ct] == this) {
+            mState->mActiveFonts.removeAt(ct);
+            break;
+        }
+    }
+
+    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+        delete mCachedGlyphs.valueAt(i);
+    }
+}
+
+void Font::invalidateTextureCache(CacheTexture *cacheTexture) {
+    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+        CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
+        if (cacheTexture == NULL || cachedGlyph->mCacheTexture == cacheTexture) {
+            cachedGlyph->mIsValid = false;
+        }
+    }
+}
+
+void Font::measureCachedGlyph(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;
+
+    int width = (int) glyph->mBitmapWidth;
+    int height = (int) glyph->mBitmapHeight;
+
+    if (bounds->bottom > nPenY) {
+        bounds->bottom = nPenY;
+    }
+    if (bounds->left > nPenX) {
+        bounds->left = nPenX;
+    }
+    if (bounds->right < nPenX + width) {
+        bounds->right = nPenX + width;
+    }
+    if (bounds->top < nPenY + height) {
+        bounds->top = nPenY + height;
+    }
+}
+
+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 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,
+            nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
+}
+
+void Font::drawCachedGlyphBitmap(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;
+
+    uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
+    uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
+
+    CacheTexture *cacheTexture = glyph->mCacheTexture;
+    uint32_t cacheWidth = cacheTexture->mWidth;
+    const uint8_t* cacheBuffer = cacheTexture->mTexture;
+
+    uint32_t cacheX = 0, cacheY = 0;
+    int32_t bX = 0, bY = 0;
+    for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
+        for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
+#if DEBUG_FONT_RENDERER
+            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
+                ALOGE("Skipping invalid index");
+                continue;
+            }
+#endif
+            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
+            bitmap[bY * bitmapW + bX] = tempCol;
+        }
+    }
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
+        SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
+    const float halfWidth = glyph->mBitmapWidth * 0.5f;
+    const float height = glyph->mBitmapHeight;
+
+    vOffset += glyph->mBitmapTop + height;
+
+    SkPoint destination[4];
+    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,
+            -tangent->fY * halfWidth + tangent->fX * vOffset);
+    destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
+            tangent->fY * halfWidth + tangent->fX * vOffset);
+    destination[2].set(destination[1].fX + tangent->fY * height,
+            destination[1].fY - tangent->fX * height);
+    destination[3].set(destination[0].fX + tangent->fY * height,
+            destination[0].fY - tangent->fX * height);
+
+    const float u1 = glyph->mBitmapMinU;
+    const float u2 = glyph->mBitmapMaxU;
+    const float v1 = glyph->mBitmapMinV;
+    const float v2 = glyph->mBitmapMaxV;
+
+    mState->appendRotatedMeshQuad(
+            position->fX + destination[0].fX,
+            position->fY + destination[0].fY, u1, v2,
+            position->fX + destination[1].fX,
+            position->fY + destination[1].fY, u2, v2,
+            position->fX + destination[2].fX,
+            position->fY + destination[2].fY, u2, v1,
+            position->fX + destination[3].fX,
+            position->fY + destination[3].fY, u1, v1,
+            glyph->mCacheTexture);
+}
+
+CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) {
+    CachedGlyphInfo* cachedGlyph = NULL;
+    ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
+    if (index >= 0) {
+        cachedGlyph = mCachedGlyphs.valueAt(index);
+    } else {
+        cachedGlyph = cacheGlyph(paint, textUnit, precaching);
+    }
+
+    // Is the glyph still in texture cache?
+    if (!cachedGlyph->mIsValid) {
+        const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
+        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,
+            0, 0, NULL, positions);
+}
+
+void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+        int numGlyphs, SkPath* path, float hOffset, float vOffset) {
+    if (numGlyphs == 0 || text == NULL || len == 0) {
+        return;
+    }
+
+    text += start;
+
+    int glyphsCount = 0;
+    SkFixed prevRsbDelta = 0;
+
+    float penX = 0.0f;
+
+    SkPoint position;
+    SkVector tangent;
+
+    SkPathMeasure measure(*path, false);
+    float pathLength = SkScalarToFloat(measure.getLength());
+
+    if (paint->getTextAlign() != SkPaint::kLeft_Align) {
+        float textWidth = SkScalarToFloat(paint->measureText(text, len));
+        float pathOffset = pathLength;
+        if (paint->getTextAlign() == SkPaint::kCenter_Align) {
+            textWidth *= 0.5f;
+            pathOffset *= 0.5f;
+        }
+        penX += pathOffset - textWidth;
+    }
+
+    while (glyphsCount < numGlyphs && penX < pathLength) {
+        glyph_t glyph = GET_GLYPH(text);
+
+        if (IS_END_OF_STRING(glyph)) {
+            break;
+        }
+
+        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+        penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
+        prevRsbDelta = cachedGlyph->mRsbDelta;
+
+        if (cachedGlyph->mIsValid) {
+            drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
+        }
+
+        penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
+
+        glyphsCount++;
+    }
+}
+
+void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+        int numGlyphs, Rect *bounds, const float* positions) {
+    if (bounds == NULL) {
+        ALOGE("No return rectangle provided to measure text");
+        return;
+    }
+    bounds->set(1e6, -1e6, -1e6, 1e6);
+    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
+}
+
+void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
+
+    if (numGlyphs == 0 || text == NULL) {
+        return;
+    }
+    int glyphsCount = 0;
+
+    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, true);
+
+        glyphsCount++;
+    }
+}
+
+void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+        int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
+        uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
+    if (numGlyphs == 0 || text == NULL || len == 0) {
+        return;
+    }
+
+    static RenderGlyph gRenderGlyph[] = {
+            &android::uirenderer::Font::drawCachedGlyph,
+            &android::uirenderer::Font::drawCachedGlyphBitmap,
+            &android::uirenderer::Font::measureCachedGlyph
+    };
+    RenderGlyph render = gRenderGlyph[mode];
+
+    text += start;
+    int glyphsCount = 0;
+
+    if (CC_LIKELY(positions == NULL)) {
+        SkFixed prevRsbDelta = 0;
+
+        float penX = x + 0.5f;
+        int penY = y;
+
+        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++;
+        }
+    } else {
+        const SkPaint::Align align = paint->getTextAlign();
+
+        // This is for renderPosText()
+        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);
+
+            // 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++;
+        }
+    }
+}
+
+void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
+        bool precaching) {
+    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
+    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
+    glyph->mBitmapLeft = skiaGlyph.fLeft;
+    glyph->mBitmapTop = skiaGlyph.fTop;
+    glyph->mLsbDelta = skiaGlyph.fLsbDelta;
+    glyph->mRsbDelta = skiaGlyph.fRsbDelta;
+
+    uint32_t startX = 0;
+    uint32_t startY = 0;
+
+    // Get the bitmap for the glyph
+    paint->findImage(skiaGlyph);
+    mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
+
+    if (!glyph->mIsValid) {
+        return;
+    }
+
+    uint32_t endX = startX + skiaGlyph.fWidth;
+    uint32_t endY = startY + skiaGlyph.fHeight;
+
+    glyph->mStartX = startX;
+    glyph->mStartY = startY;
+    glyph->mBitmapWidth = skiaGlyph.fWidth;
+    glyph->mBitmapHeight = skiaGlyph.fHeight;
+
+    uint32_t cacheWidth = glyph->mCacheTexture->mWidth;
+    uint32_t cacheHeight = glyph->mCacheTexture->mHeight;
+
+    glyph->mBitmapMinU = startX / (float) cacheWidth;
+    glyph->mBitmapMinV = startY / (float) cacheHeight;
+    glyph->mBitmapMaxU = endX / (float) cacheWidth;
+    glyph->mBitmapMaxV = endY / (float) cacheHeight;
+
+    mState->mUploadTexture = true;
+}
+
+CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
+    CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
+    mCachedGlyphs.add(glyph, newGlyph);
+
+    const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
+    newGlyph->mGlyphIndex = skiaGlyph.fID;
+    newGlyph->mIsValid = false;
+
+    updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
+
+    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;
+
+    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;
+        }
+    }
+
+    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
+            scaleX, style, strokeWidth);
+    activeFonts.push(newFont);
+    return newFont;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
new file mode 100644
index 0000000..d1b75bd
--- /dev/null
+++ b/libs/hwui/font/Font.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2012 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_FONT_H
+#define ANDROID_HWUI_FONT_H
+
+#include <utils/KeyedVector.h>
+
+#include <SkScalerContext.h>
+#include <SkPaint.h>
+#include <SkPathMeasure.h>
+
+#include "CachedGlyphInfo.h"
+#include "../Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
+class FontRenderer;
+
+/**
+ * Represents a font, defined by a Skia font id and a font size. A font is used
+ * to generate glyphs and cache them in the FontState.
+ */
+class Font {
+public:
+    enum Style {
+        kFakeBold = 1
+    };
+
+    ~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,
+            int numGlyphs, int x, int y, const float* positions);
+
+    void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+            int numGlyphs, SkPath* path, float hOffset, float vOffset);
+
+    /**
+     * 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);
+
+private:
+    friend class FontRenderer;
+    typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
+            uint32_t, uint32_t, Rect*, const float*);
+
+    enum RenderMode {
+        FRAMEBUFFER,
+        BITMAP,
+        MEASURE,
+    };
+
+    void precache(SkPaint* paint, const char* text, int numGlyphs);
+
+    void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+            int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
+            uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
+
+    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);
+    void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
+            bool precaching);
+
+    void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+            uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+            Rect* bounds, const float* pos);
+    void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+            uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+            Rect* bounds, const float* pos);
+    void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
+            uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+            Rect* bounds, const float* pos);
+    void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
+            SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
+
+    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;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_FONT_H
diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h
new file mode 100644
index 0000000..12247ba
--- /dev/null
+++ b/libs/hwui/font/FontUtil.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 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_FONT_UTIL_H
+#define ANDROID_HWUI_FONT_UTIL_H
+
+#include <SkUtils.h>
+
+#include "Properties.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define DEFAULT_TEXT_SMALL_CACHE_WIDTH 1024
+#define DEFAULT_TEXT_SMALL_CACHE_HEIGHT 256
+#define DEFAULT_TEXT_LARGE_CACHE_WIDTH 2048
+#define DEFAULT_TEXT_LARGE_CACHE_HEIGHT 512
+
+#define TEXTURE_BORDER_SIZE 1
+
+#define CACHE_BLOCK_ROUNDING_SIZE 4
+
+#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_GLYPH(text) nextGlyph((const uint16_t**) &text)
+    #define IS_END_OF_STRING(glyph) false
+
+    static glyph_t nextGlyph(const uint16_t** srcPtr) {
+        const uint16_t* src = *srcPtr;
+        glyph_t g = *src++;
+        *srcPtr = src;
+        return g;
+    }
+#else
+    typedef SkUnichar glyph_t;
+    #define TO_GLYPH(g) ((SkUnichar) g)
+    #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph)
+    #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
+    #define IS_END_OF_STRING(glyph) glyph < 0
+#endif
+
+#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
+
+#endif // ANDROID_HWUI_FONT_UTIL_H