Support RGBA fonts and bitmap fonts (and RGBA bitmap fonts)

Quite a few things going on in this commit:

- Enable bitmap strikes by default in Paint objects.

The SkPaint parameter that enables bitmap strikes was not previously
included in DEFAULT_PAINT_FLAGS. This effectively disabled bitmap
fonts. Oops! It's for the best, though, as additional work was needed
in Skia to make bitmap fonts work anyway.

- Complain if TEXTURE_BORDER_SIZE is not 1.

Our glyph cache code does not currently handle any value other than 1
here, including zero. I've added a little C preprocessor check to
prevent future engineers (including especially future-me) from
thinking that they can change this value without updating the related
code.

- Add GL_RGBA support to hwui's FontRenderer and friends

This also happened to involve some refactoring for convenience and
cleanliness.

Bug: 9577689
Change-Id: I0abd1e5a0d6623106247fb6421787e2c2f2ea19c
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 6ac637e..df966e1 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -256,8 +256,12 @@
     log.appendFormat("  PatchCache           %8d / %8d\n",
             patchCache.getSize(), patchCache.getMaxSize());
     for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
-        const uint32_t size = fontRenderer->getFontRendererSize(i);
-        log.appendFormat("  FontRenderer %d       %8d / %8d\n", i, size, size);
+        const uint32_t sizeA8 = fontRenderer->getFontRendererSize(i, GL_ALPHA);
+        const uint32_t sizeRGBA = fontRenderer->getFontRendererSize(i, GL_RGBA);
+        log.appendFormat("  FontRenderer %d A8    %8d / %8d\n", i, sizeA8, sizeA8);
+        log.appendFormat("  FontRenderer %d RGBA  %8d / %8d\n", i, sizeRGBA, sizeRGBA);
+        log.appendFormat("  FontRenderer %d total %8d / %8d\n", i, sizeA8 + sizeRGBA,
+                sizeA8 + sizeRGBA);
     }
     log.appendFormat("Other:\n");
     log.appendFormat("  FboCache             %8d / %8d\n",
@@ -272,7 +276,8 @@
     total += dropShadowCache.getSize();
     total += patchCache.getSize();
     for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
-        total += fontRenderer->getFontRendererSize(i);
+        total += fontRenderer->getFontRendererSize(i, GL_ALPHA);
+        total += fontRenderer->getFontRendererSize(i, GL_RGBA);
     }
 
     log.appendFormat("Total memory usage:\n");
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 79a7a93..1b2f651 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -21,7 +21,6 @@
 
 #include <cutils/properties.h>
 
-#include <utils/Functor.h>
 #include <utils/Log.h>
 
 #ifdef ANDROID_ENABLE_RENDERSCRIPT
@@ -35,6 +34,7 @@
 #include "Debug.h"
 #include "Extensions.h"
 #include "FontRenderer.h"
+#include "OpenGLRenderer.h"
 #include "PixelBuffer.h"
 #include "Rect.h"
 
@@ -45,6 +45,52 @@
 #define RS_MIN_INPUT_CUTOFF 10000
 
 ///////////////////////////////////////////////////////////////////////////////
+// TextSetupFunctor
+///////////////////////////////////////////////////////////////////////////////
+status_t TextSetupFunctor::operator ()(int what, void* data) {
+    Data* typedData = reinterpret_cast<Data*>(data);
+    GLenum glyphFormat = typedData ? typedData->glyphFormat : GL_ALPHA;
+
+    renderer->setupDraw();
+    renderer->setupDrawTextGamma(paint);
+    renderer->setupDrawDirtyRegionsDisabled();
+    renderer->setupDrawWithTexture(glyphFormat == GL_ALPHA);
+    switch (glyphFormat) {
+        case GL_ALPHA: {
+            renderer->setupDrawAlpha8Color(paint->getColor(), alpha);
+            break;
+        }
+        case GL_RGBA: {
+            float floatAlpha = alpha / 255.0f;
+            renderer->setupDrawColor(floatAlpha, floatAlpha, floatAlpha, floatAlpha);
+            break;
+        }
+        default: {
+#if DEBUG_FONT_RENDERER
+            ALOGD("TextSetupFunctor: called with unknown glyph format %x", glyphFormat);
+#endif
+            break;
+        }
+    }
+    renderer->setupDrawColorFilter();
+    renderer->setupDrawShader();
+    renderer->setupDrawBlending(true, mode);
+    renderer->setupDrawProgram();
+    renderer->setupDrawModelView(x, y, x, y, pureTranslate, true);
+    // Calling setupDrawTexture with the name 0 will enable the
+    // uv attributes and increase the texture unit count
+    // texture binding will be performed by the font renderer as
+    // needed
+    renderer->setupDrawTexture(0);
+    renderer->setupDrawPureColorUniforms();
+    renderer->setupDrawColorFilterUniforms();
+    renderer->setupDrawShaderUniforms(pureTranslate);
+    renderer->setupDrawTextGammaUniforms();
+
+    return NO_ERROR;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // FontRenderer
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -103,11 +149,16 @@
     sLogFontRendererCreate = false;
 }
 
-FontRenderer::~FontRenderer() {
-    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
-        delete mCacheTextures[i];
+void clearCacheTextures(Vector<CacheTexture*>& cacheTextures) {
+    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
+        delete cacheTextures[i];
     }
-    mCacheTextures.clear();
+    cacheTextures.clear();
+}
+
+FontRenderer::~FontRenderer() {
+    clearCacheTextures(mACacheTextures);
+    clearCacheTextures(mRGBACacheTextures);
 
     LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
     while (it.next()) {
@@ -124,15 +175,19 @@
         it.value()->invalidateTextureCache();
     }
 
-    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
-        mCacheTextures[i]->init();
+    for (uint32_t i = 0; i < mACacheTextures.size(); i++) {
+        mACacheTextures[i]->init();
+    }
+
+    for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) {
+        mRGBACacheTextures[i]->init();
     }
 }
 
-void FontRenderer::flushLargeCaches() {
+void FontRenderer::flushLargeCaches(Vector<CacheTexture*>& cacheTextures) {
     // Start from 1; don't deallocate smallest/default texture
-    for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
-        CacheTexture* cacheTexture = mCacheTextures[i];
+    for (uint32_t i = 1; i < cacheTextures.size(); i++) {
+        CacheTexture* cacheTexture = cacheTextures[i];
         if (cacheTexture->getPixelBuffer()) {
             cacheTexture->init();
             LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
@@ -144,11 +199,16 @@
     }
 }
 
-CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
-        uint32_t* startX, uint32_t* startY) {
-    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
-        if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
-            return mCacheTextures[i];
+void FontRenderer::flushLargeCaches() {
+    flushLargeCaches(mACacheTextures);
+    flushLargeCaches(mRGBACacheTextures);
+}
+
+CacheTexture* FontRenderer::cacheBitmapInTexture(Vector<CacheTexture*>& cacheTextures,
+        const SkGlyph& glyph, uint32_t* startX, uint32_t* startY) {
+    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
+        if (cacheTextures[i]->fitBitmap(glyph, startX, startY)) {
+            return cacheTextures[i];
         }
     }
     // Could not fit glyph into current cache textures
@@ -169,9 +229,26 @@
 
     cachedGlyph->mIsValid = false;
 
+    // choose an appropriate cache texture list for this glyph format
+    SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
+    Vector<CacheTexture*>* cacheTextures = NULL;
+    switch (format) {
+        case SkMask::kA8_Format:
+            cacheTextures = &mACacheTextures;
+            break;
+        case SkMask::kARGB32_Format:
+            cacheTextures = &mRGBACacheTextures;
+            break;
+        default:
+#if DEBUG_FONT_RENDERER
+            ALOGD("getCacheTexturesForFormat: unknown SkMask format %x", format);
+#endif
+        return;
+    }
+
     // If the glyph is too tall, don't cache it
     if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
-                mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
+                (*cacheTextures)[cacheTextures->size() - 1]->getHeight()) {
         ALOGE("Font size too large to fit in cache. width, height = %i, %i",
                 (int) glyph.fWidth, (int) glyph.fHeight);
         return;
@@ -181,14 +258,14 @@
     uint32_t startX = 0;
     uint32_t startY = 0;
 
-    CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
+    CacheTexture* cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
 
     if (!cacheTexture) {
         if (!precaching) {
             // If the new glyph didn't fit and we are not just trying to precache it,
             // clear out the cache and try again
             flushAllAndInvalidate();
-            cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
+            cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
         }
 
         if (!cacheTexture) {
@@ -216,24 +293,21 @@
         cacheTexture->allocateMesh();
     }
 
-    // Tells us whether the glyphs is B&W (1 bit per pixel)
-    // or anti-aliased (8 bits per pixel)
-    SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
-
     uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
-    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
+    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
+    int srcStride = glyph.rowBytes();
 
     // Copy the glyph image, taking the mask format into account
-    uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
-    int stride = glyph.rowBytes();
-
-    uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
-    memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
-
     switch (format) {
         case SkMask::kA8_Format: {
+            uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
+            uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
+                    - TEXTURE_BORDER_SIZE;
+            // write leading border line
+            memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
+            // write glyph data
             if (mGammaTable) {
-                for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
+                for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
                     row = cacheY * cacheWidth;
                     cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
                     for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
@@ -243,21 +317,55 @@
                     cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
                 }
             } else {
-                for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
+                for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
                     row = cacheY * cacheWidth;
                     memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
                     cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
                     cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
                 }
             }
+            // write trailing border line
+            row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
+            memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
+            break;
+        }
+        case SkMask::kARGB32_Format: {
+            // prep data lengths
+            const size_t formatSize = PixelBuffer::formatSize(GL_RGBA);
+            const size_t borderSize = formatSize * TEXTURE_BORDER_SIZE;
+            size_t rowSize = formatSize * glyph.fWidth;
+            // prep advances
+            size_t dstStride = formatSize * cacheWidth;
+            // prep indices
+            // - we actually start one row early, and then increment before first copy
+            uint8_t* src = &bitmapBuffer[0 - srcStride];
+            uint8_t* dst = &cacheBuffer[cacheTexture->getOffset(startX, startY - 1)];
+            uint8_t* dstEnd = &cacheBuffer[cacheTexture->getOffset(startX, endY - 1)];
+            uint8_t* dstL = dst - borderSize;
+            uint8_t* dstR = dst + rowSize;
+            // write leading border line
+            memset(dstL, 0, rowSize + 2 * borderSize);
+            // write glyph data
+            while (dst < dstEnd) {
+                memset(dstL += dstStride, 0, borderSize); // leading border column
+                memcpy(dst += dstStride, src += srcStride, rowSize); // glyph data
+                memset(dstR += dstStride, 0, borderSize); // trailing border column
+            }
+            // write trailing border line
+            memset(dstL, 0, rowSize + 2 * borderSize);
             break;
         }
         case SkMask::kBW_Format: {
+            uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
+            uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
+                    - TEXTURE_BORDER_SIZE;
             static const uint8_t COLORS[2] = { 0, 255 };
-
+            // write leading border line
+            memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
+            // write glyph data
             for (cacheY = startY; cacheY < endY; cacheY++) {
                 cacheX = startX;
-                int rowBytes = stride;
+                int rowBytes = srcStride;
                 uint8_t* buffer = bitmapBuffer;
 
                 row = cacheY * cacheWidth;
@@ -270,23 +378,24 @@
                 }
                 cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
 
-                bitmapBuffer += stride;
+                bitmapBuffer += srcStride;
             }
+            // write trailing border line
+            row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
+            memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
             break;
         }
         default:
-            ALOGW("Unkown glyph format: 0x%x", format);
+            ALOGW("Unknown glyph format: 0x%x", format);
             break;
     }
 
-    row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
-    memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
-
     cachedGlyph->mIsValid = true;
 }
 
-CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
-    CacheTexture* cacheTexture = new CacheTexture(width, height, gMaxNumberOfQuads);
+CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format,
+        bool allocate) {
+    CacheTexture* cacheTexture = new CacheTexture(width, height, format, gMaxNumberOfQuads);
 
     if (allocate) {
         Caches::getInstance().activeTexture(0);
@@ -298,17 +407,23 @@
 }
 
 void FontRenderer::initTextTexture() {
-    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
-        delete mCacheTextures[i];
-    }
-    mCacheTextures.clear();
+    clearCacheTextures(mACacheTextures);
+    clearCacheTextures(mRGBACacheTextures);
 
     mUploadTexture = false;
-    mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
-    mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
-    mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
-    mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
-    mCurrentCacheTexture = mCacheTextures[0];
+    mACacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight,
+            GL_ALPHA, true));
+    mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
+            GL_ALPHA, false));
+    mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
+            GL_ALPHA, false));
+    mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight,
+            GL_ALPHA, false));
+    mRGBACacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight,
+            GL_RGBA, false));
+    mRGBACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
+            GL_RGBA, false));
+    mCurrentCacheTexture = mACacheTextures[0];
 }
 
 // We don't want to allocate anything unless we actually draw text
@@ -322,6 +437,24 @@
     mInitialized = true;
 }
 
+void checkTextureUpdateForCache(Caches& caches, Vector<CacheTexture*>& cacheTextures,
+        bool& resetPixelStore, GLuint& lastTextureId) {
+    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
+        CacheTexture* cacheTexture = cacheTextures[i];
+        if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
+            if (cacheTexture->getTextureId() != lastTextureId) {
+                lastTextureId = cacheTexture->getTextureId();
+                caches.activeTexture(0);
+                caches.bindTexture(lastTextureId);
+            }
+
+            if (cacheTexture->upload()) {
+                resetPixelStore = true;
+            }
+        }
+    }
+}
+
 void FontRenderer::checkTextureUpdate() {
     if (!mUploadTexture) {
         return;
@@ -334,25 +467,8 @@
     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 
     // Iterate over all the cache textures and see which ones need to be updated
-    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
-        CacheTexture* cacheTexture = mCacheTextures[i];
-        if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
-            if (cacheTexture->getTextureId() != lastTextureId) {
-                lastTextureId = cacheTexture->getTextureId();
-                caches.activeTexture(0);
-                caches.bindTexture(lastTextureId);
-            }
-
-            if (cacheTexture->upload()) {
-                resetPixelStore = true;
-            }
-
-#if DEBUG_FONT_RENDERER
-            ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
-                    i, x, y, width, height);
-#endif
-        }
-    }
+    checkTextureUpdateForCache(caches, mACacheTextures, resetPixelStore, lastTextureId);
+    checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId);
 
     // Unbind any PBO we might have used to update textures
     caches.unbindPixelBuffer();
@@ -366,18 +482,18 @@
     mUploadTexture = false;
 }
 
-void FontRenderer::issueDrawCommand() {
+void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) {
+    Caches& caches = Caches::getInstance();
     bool first = true;
     bool force = false;
-
-    GLuint lastId = 0;
-    Caches& caches = Caches::getInstance();
-
-    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
-        CacheTexture* texture = mCacheTextures[i];
+    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
+        CacheTexture* texture = cacheTextures[i];
         if (texture->canDraw()) {
             if (first) {
-                if (mFunctor) (*mFunctor)(0, NULL);
+                if (mFunctor) {
+                    TextSetupFunctor::Data functorData(texture->getFormat());
+                    (*mFunctor)(0, &functorData);
+                }
 
                 checkTextureUpdate();
                 caches.bindIndicesBuffer();
@@ -407,6 +523,11 @@
             texture->resetMesh();
         }
     }
+}
+
+void FontRenderer::issueDrawCommand() {
+    issueDrawCommand(mACacheTextures);
+    issueDrawCommand(mRGBACacheTextures);
 
     mDrawn = true;
 }
@@ -582,13 +703,13 @@
 
 bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
         uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
-        float hOffset, float vOffset, Rect* bounds) {
+        float hOffset, float vOffset, Rect* bounds, Functor* functor) {
     if (!mCurrentFont) {
         ALOGE("No font set");
         return false;
     }
 
-    initRender(clip, bounds, NULL);
+    initRender(clip, bounds, functor);
     mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
     finishRender();
 
@@ -646,10 +767,10 @@
     delete[] scratch;
 }
 
-uint32_t FontRenderer::getCacheSize() const {
+static uint32_t calculateCacheSize(const Vector<CacheTexture*>& cacheTextures) {
     uint32_t size = 0;
-    for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
-        CacheTexture* cacheTexture = mCacheTextures[i];
+    for (uint32_t i = 0; i < cacheTextures.size(); i++) {
+        CacheTexture* cacheTexture = cacheTextures[i];
         if (cacheTexture && cacheTexture->getPixelBuffer()) {
             size += cacheTexture->getPixelBuffer()->getSize();
         }
@@ -657,5 +778,19 @@
     return size;
 }
 
+uint32_t FontRenderer::getCacheSize(GLenum format) const {
+    switch (format) {
+        case GL_ALPHA: {
+            return calculateCacheSize(mACacheTextures);
+        }
+        case GL_RGBA: {
+            return calculateCacheSize(mRGBACacheTextures);
+        }
+        default: {
+            return 0;
+        }
+    }
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index c1072ed..aca47b4 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/Functor.h>
 #include <utils/LruCache.h>
 #include <utils/Vector.h>
 #include <utils/StrongPointer.h>
@@ -46,8 +47,40 @@
 namespace android {
 namespace uirenderer {
 
+class OpenGLRenderer;
+
 ///////////////////////////////////////////////////////////////////////////////
-// Renderer
+// TextSetupFunctor
+///////////////////////////////////////////////////////////////////////////////
+class TextSetupFunctor: public Functor {
+public:
+    struct Data {
+        Data(GLenum glyphFormat) : glyphFormat(glyphFormat) {
+        }
+
+        GLenum glyphFormat;
+    };
+
+    TextSetupFunctor(OpenGLRenderer* renderer, float x, float y, bool pureTranslate,
+            int alpha, SkXfermode::Mode mode, SkPaint* paint): Functor(),
+            renderer(renderer), x(x), y(y), pureTranslate(pureTranslate),
+            alpha(alpha), mode(mode), paint(paint) {
+    }
+    ~TextSetupFunctor() { }
+
+    status_t operator ()(int what, void* data);
+
+    OpenGLRenderer* renderer;
+    float x;
+    float y;
+    bool pureTranslate;
+    int alpha;
+    SkXfermode::Mode mode;
+    SkPaint* paint;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// FontRenderer
 ///////////////////////////////////////////////////////////////////////////////
 
 class FontRenderer {
@@ -55,6 +88,7 @@
     FontRenderer();
     ~FontRenderer();
 
+    void flushLargeCaches(Vector<CacheTexture*>& cacheTextures);
     void flushLargeCaches();
 
     void setGammaTable(const uint8_t* gammaTable) {
@@ -73,7 +107,8 @@
 
     // bounds is an out parameter
     bool renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
-            uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds);
+            uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds,
+            Functor* functor);
 
     struct DropShadow {
         DropShadow() { };
@@ -100,7 +135,7 @@
         mLinearFiltering = linearFiltering;
     }
 
-    uint32_t getCacheSize() const;
+    uint32_t getCacheSize(GLenum format) const;
 
 private:
     friend class Font;
@@ -110,10 +145,11 @@
     void allocateTextureMemory(CacheTexture* cacheTexture);
     void deallocateTextureMemory(CacheTexture* cacheTexture);
     void initTextTexture();
-    CacheTexture* createCacheTexture(int width, int height, bool allocate);
+    CacheTexture* createCacheTexture(int width, int height, GLenum format, bool allocate);
     void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
             uint32_t *retOriginX, uint32_t *retOriginY, bool precaching);
-    CacheTexture* cacheBitmapInTexture(const SkGlyph& glyph, uint32_t* startX, uint32_t* startY);
+    CacheTexture* cacheBitmapInTexture(Vector<CacheTexture*>& cacheTextures, const SkGlyph& glyph,
+            uint32_t* startX, uint32_t* startY);
 
     void flushAllAndInvalidate();
 
@@ -121,6 +157,7 @@
     void initRender(const Rect* clip, Rect* bounds, Functor* functor);
     void finishRender();
 
+    void issueDrawCommand(Vector<CacheTexture*>& cacheTextures);
     void issueDrawCommand();
     void appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
             float x2, float y2, float u2, float v2,
@@ -148,7 +185,8 @@
     uint32_t mLargeCacheWidth;
     uint32_t mLargeCacheHeight;
 
-    Vector<CacheTexture*> mCacheTextures;
+    Vector<CacheTexture*> mACacheTextures;
+    Vector<CacheTexture*> mRGBACacheTextures;
 
     Font* mCurrentFont;
     LruCache<Font::FontDescription, Font*> mActiveFonts;
diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
index bbfa66d..46cfd04 100644
--- a/libs/hwui/GammaFontRenderer.h
+++ b/libs/hwui/GammaFontRenderer.h
@@ -35,7 +35,7 @@
     virtual FontRenderer& getFontRenderer(const SkPaint* paint) = 0;
 
     virtual uint32_t getFontRendererCount() const = 0;
-    virtual uint32_t getFontRendererSize(uint32_t fontRenderer) const = 0;
+    virtual uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const = 0;
 
     virtual void describe(ProgramDescription& description, const SkPaint* paint) const = 0;
     virtual void setupProgram(ProgramDescription& description, Program* program) const = 0;
@@ -81,8 +81,8 @@
         return 1;
     }
 
-    uint32_t getFontRendererSize(uint32_t fontRenderer) const {
-        return mRenderer ? mRenderer->getCacheSize() : 0;
+    uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const {
+        return mRenderer ? mRenderer->getCacheSize(format) : 0;
     }
 
     void describe(ProgramDescription& description, const SkPaint* paint) const;
@@ -128,8 +128,8 @@
         return 1;
     }
 
-    uint32_t getFontRendererSize(uint32_t fontRenderer) const {
-        return mRenderer ? mRenderer->getCacheSize() : 0;
+    uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const {
+        return mRenderer ? mRenderer->getCacheSize(format) : 0;
     }
 
     void describe(ProgramDescription& description, const SkPaint* paint) const {
@@ -162,13 +162,13 @@
         return kGammaCount;
     }
 
-    uint32_t getFontRendererSize(uint32_t fontRenderer) const {
+    uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const {
         if (fontRenderer >= kGammaCount) return 0;
 
         FontRenderer* renderer = mRenderers[fontRenderer];
         if (!renderer) return 0;
 
-        return renderer->getCacheSize();
+        return renderer->getCacheSize(format);
     }
 
     void describe(ProgramDescription& description, const SkPaint* paint) const {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index bc00ce8..be0cd2a 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2825,48 +2825,6 @@
     return alpha == 0.0f && getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
 }
 
-class TextSetupFunctor: public Functor {
-public:
-    TextSetupFunctor(OpenGLRenderer& renderer, float x, float y, bool pureTranslate,
-            int alpha, SkXfermode::Mode mode, SkPaint* paint): Functor(),
-            renderer(renderer), x(x), y(y), pureTranslate(pureTranslate),
-            alpha(alpha), mode(mode), paint(paint) {
-    }
-    ~TextSetupFunctor() { }
-
-    status_t operator ()(int what, void* data) {
-        renderer.setupDraw();
-        renderer.setupDrawTextGamma(paint);
-        renderer.setupDrawDirtyRegionsDisabled();
-        renderer.setupDrawWithTexture(true);
-        renderer.setupDrawAlpha8Color(paint->getColor(), alpha);
-        renderer.setupDrawColorFilter();
-        renderer.setupDrawShader();
-        renderer.setupDrawBlending(true, mode);
-        renderer.setupDrawProgram();
-        renderer.setupDrawModelView(x, y, x, y, pureTranslate, true);
-        // Calling setupDrawTexture with the name 0 will enable the
-        // uv attributes and increase the texture unit count
-        // texture binding will be performed by the font renderer as
-        // needed
-        renderer.setupDrawTexture(0);
-        renderer.setupDrawPureColorUniforms();
-        renderer.setupDrawColorFilterUniforms();
-        renderer.setupDrawShaderUniforms(pureTranslate);
-        renderer.setupDrawTextGammaUniforms();
-
-        return NO_ERROR;
-    }
-
-    OpenGLRenderer& renderer;
-    float x;
-    float y;
-    bool pureTranslate;
-    int alpha;
-    SkXfermode::Mode mode;
-    SkPaint* paint;
-};
-
 status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
         const float* positions, SkPaint* paint) {
     if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) {
@@ -2912,7 +2870,7 @@
 
     const bool hasActiveLayer = hasLayer();
 
-    TextSetupFunctor functor(*this, x, y, pureTranslate, alpha, mode, paint);
+    TextSetupFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
     if (fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
             positions, hasActiveLayer ? &bounds : NULL, &functor)) {
         if (hasActiveLayer) {
@@ -3003,7 +2961,7 @@
     Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
 
     bool status;
-    TextSetupFunctor functor(*this, x, y, pureTranslate, alpha, mode, paint);
+    TextSetupFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
 
     // don't call issuedrawcommand, do it at end of batch
     bool forceFinish = (drawOpMode != kDrawOpMode_Defer);
@@ -3045,26 +3003,7 @@
     int alpha;
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
-
-    setupDraw();
-    setupDrawTextGamma(paint);
-    setupDrawDirtyRegionsDisabled();
-    setupDrawWithTexture(true);
-    setupDrawAlpha8Color(paint->getColor(), alpha);
-    setupDrawColorFilter();
-    setupDrawShader();
-    setupDrawBlending(true, mode);
-    setupDrawProgram();
-    setupDrawModelView(0.0f, 0.0f, 0.0f, 0.0f, false, true);
-    // Calling setupDrawTexture with the name 0 will enable the
-    // uv attributes and increase the texture unit count
-    // texture binding will be performed by the font renderer as
-    // needed
-    setupDrawTexture(0);
-    setupDrawPureColorUniforms();
-    setupDrawColorFilterUniforms();
-    setupDrawShaderUniforms(false);
-    setupDrawTextGammaUniforms();
+    TextSetupFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint);
 
     const Rect* clip = &mSnapshot->getLocalClip();
     Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
@@ -3072,7 +3011,7 @@
     const bool hasActiveLayer = hasLayer();
 
     if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path,
-            hOffset, vOffset, hasActiveLayer ? &bounds : NULL)) {
+            hOffset, vOffset, hasActiveLayer ? &bounds : NULL, &functor)) {
         if (hasActiveLayer) {
             currentTransform().mapRect(bounds);
             dirtyLayerUnchecked(bounds, getRegion());
diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h
index 32d5417..9725a61 100644
--- a/libs/hwui/PixelBuffer.h
+++ b/libs/hwui/PixelBuffer.h
@@ -112,13 +112,25 @@
     virtual uint8_t* getMappedPointer() const = 0;
 
     /**
-     * Upload the specified rectangle of this pixe buffer as a
+     * Upload the specified rectangle of this pixel buffer as a
      * GL_TEXTURE_2D texture. Calling this method will trigger
      * an unmap() if necessary.
      */
     virtual void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) = 0;
 
     /**
+     * Upload the specified rectangle of this pixel buffer as a
+     * GL_TEXTURE_2D texture. Calling this method will trigger
+     * an unmap() if necessary.
+     *
+     * This is a convenience function provided to save callers the
+     * trouble of computing the offset parameter.
+     */
+    void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
+        upload(x, y, width, height, getOffset(x, y));
+    }
+
+    /**
      * Returns the width of the render buffer in pixels.
      */
     uint32_t getWidth() const {
@@ -140,6 +152,13 @@
     }
 
     /**
+     * Returns the offset of a pixel in this pixel buffer, in bytes.
+     */
+    uint32_t getOffset(uint32_t x, uint32_t y) const {
+        return (y * mWidth + x) * formatSize(mFormat);
+    }
+
+    /**
      * Returns the number of bytes per pixel in the specified format.
      *
      * Supported formats:
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index 5f15724..55503ce 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -108,8 +108,8 @@
 // CacheTexture
 ///////////////////////////////////////////////////////////////////////////////
 
-CacheTexture::CacheTexture(uint16_t width, uint16_t height, uint32_t maxQuadCount) :
-            mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
+CacheTexture::CacheTexture(uint16_t width, uint16_t height, GLenum format, uint32_t maxQuadCount) :
+            mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height), mFormat(format),
             mLinearFiltering(false), mDirty(false), mNumGlyphs(0),
             mMesh(NULL), mCurrentQuad(0), mMaxQuadCount(maxQuadCount),
             mCaches(Caches::getInstance()) {
@@ -182,7 +182,7 @@
 
 void CacheTexture::allocateTexture() {
     if (!mTexture) {
-        mTexture = PixelBuffer::create(GL_ALPHA, mWidth, mHeight);
+        mTexture = PixelBuffer::create(mFormat, mWidth, mHeight);
     }
 
     if (!mTextureId) {
@@ -191,8 +191,8 @@
         mCaches.bindTexture(mTextureId);
         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
         // Initialize texture dimensions
-        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mWidth, mHeight, 0,
-                GL_ALPHA, GL_UNSIGNED_BYTE, 0);
+        glTexImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0,
+                mFormat, GL_UNSIGNED_BYTE, 0);
 
         const GLenum filtering = getLinearFiltering() ? GL_LINEAR : GL_NEAREST;
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
@@ -217,8 +217,7 @@
         glPixelStorei(GL_UNPACK_ROW_LENGTH, mWidth);
     }
 
-    mTexture->upload(x, y, width, height, y * mWidth + x);
-
+    mTexture->upload(x, y, width, height);
     setDirty(false);
 
     return mHasES3;
@@ -232,6 +231,30 @@
 }
 
 bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
+    switch (glyph.fMaskFormat) {
+        case SkMask::kA8_Format:
+            if (mFormat != GL_ALPHA) {
+#if DEBUG_FONT_RENDERER
+                ALOGD("fitBitmap: kA8_Format glyph cannot fit into texture format %x", mFormat);
+#endif
+                return false;
+            }
+            break;
+        case SkMask::kARGB32_Format:
+            if (mFormat != GL_RGBA) {
+#if DEBUG_FONT_RENDERER
+                ALOGD("fitBitmap: kARGB32_Format glyph cannot fit into texture format %x", mFormat);
+#endif
+                return false;
+            }
+            break;
+        default:
+#if DEBUG_FONT_RENDERER
+            ALOGD("fitBitmap: unknown glyph format %x encountered", glyph.fMaskFormat);
+#endif
+            return false;
+    }
+
     if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mHeight) {
         return false;
     }
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
index 208b1ff..028b611 100644
--- a/libs/hwui/font/CacheTexture.h
+++ b/libs/hwui/font/CacheTexture.h
@@ -24,6 +24,7 @@
 #include <utils/Log.h>
 
 #include "FontUtil.h"
+#include "../PixelBuffer.h"
 #include "../Rect.h"
 #include "../Vertex.h"
 
@@ -31,7 +32,6 @@
 namespace uirenderer {
 
 class Caches;
-class PixelBuffer;
 
 /**
  * CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
@@ -74,7 +74,7 @@
 
 class CacheTexture {
 public:
-    CacheTexture(uint16_t width, uint16_t height, uint32_t maxQuadCount);
+    CacheTexture(uint16_t width, uint16_t height, GLenum format, uint32_t maxQuadCount);
     ~CacheTexture();
 
     void reset();
@@ -100,6 +100,14 @@
         return mHeight;
     }
 
+    inline GLenum getFormat() const {
+        return mFormat;
+    }
+
+    inline uint32_t getOffset(uint16_t x, uint16_t y) const {
+        return (y * mWidth + x) * PixelBuffer::formatSize(mFormat);
+    }
+
     inline const Rect* getDirtyRect() const {
         return &mDirtyRect;
     }
@@ -173,6 +181,7 @@
     GLuint mTextureId;
     uint16_t mWidth;
     uint16_t mHeight;
+    GLenum mFormat;
     bool mLinearFiltering;
     bool mDirty;
     uint16_t mNumGlyphs;
diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h
index f758666..cdcb23c 100644
--- a/libs/hwui/font/FontUtil.h
+++ b/libs/hwui/font/FontUtil.h
@@ -31,6 +31,9 @@
 #define DEFAULT_TEXT_LARGE_CACHE_HEIGHT 512
 
 #define TEXTURE_BORDER_SIZE 1
+#if TEXTURE_BORDER_SIZE != 1
+# error TEXTURE_BORDER_SIZE other than 1 is not currently supported
+#endif
 
 #define CACHE_BLOCK_ROUNDING_SIZE 4