Add text rendering.

Change-Id: Ibe5a9fa844d531b31b55e43de403a98d49f659b9
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
new file mode 100644
index 0000000..8557b87
--- /dev/null
+++ b/libs/hwui/FontRenderer.cpp
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "FontRenderer.h"
+
+#include <SkUtils.h>
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
+Font::Font(FontRenderer* state, uint32_t fontId, float fontSize) :
+    mState(state), mFontId(fontId), mFontSize(fontSize) {
+}
+
+
+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++) {
+        CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i);
+        delete glyph;
+    }
+}
+
+void Font::invalidateTextureCache() {
+    for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+        mCachedGlyphs.valueAt(i)->mIsValid = false;
+    }
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
+    FontRenderer *state = mState;
+
+    int nPenX = x + glyph->mBitmapLeft;
+    int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
+
+    state->appendMeshQuad(nPenX, nPenY, 0, glyph->mBitmapMinU, glyph->mBitmapMaxV,
+            nPenX + (int) glyph->mBitmapWidth, nPenY, 0, glyph->mBitmapMaxU, glyph->mBitmapMaxV,
+            nPenX + (int) glyph->mBitmapWidth, nPenY - (int) glyph->mBitmapHeight,
+            0, glyph->mBitmapMaxU, glyph->mBitmapMinV, nPenX, nPenY - (int) glyph->mBitmapHeight,
+            0, glyph->mBitmapMinU, glyph->mBitmapMinV);
+}
+
+void Font::renderUTF(SkPaint* paint, const char *text, uint32_t len, uint32_t start, int numGlyphs,
+        int x, int y) {
+    if (numGlyphs == 0 || text == NULL || len == 0) {
+        return;
+    }
+
+    int penX = x, penY = y;
+    int glyphsLeft = 1;
+    if (numGlyphs > 0) {
+        glyphsLeft = numGlyphs;
+    }
+
+    //size_t index = start;
+    //size_t nextIndex = 0;
+
+    text += start;
+
+    while (glyphsLeft > 0) {
+        //int32_t utfChar = utf32_at(text, len, index, &nextIndex);
+        int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
+
+        // Reached the end of the string or encountered
+        if (utfChar < 0) {
+            break;
+        }
+
+        // Move to the next character in the array
+        //index = nextIndex;
+
+        CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor(utfChar);
+
+        if (cachedGlyph == NULL) {
+            cachedGlyph = cacheGlyph(paint, utfChar);
+        }
+        // Is the glyph still in texture cache?
+        if (!cachedGlyph->mIsValid) {
+            const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
+            updateGlyphCache(paint, skiaGlyph, cachedGlyph);
+        }
+
+        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
+        if (cachedGlyph->mIsValid) {
+            drawCachedGlyph(cachedGlyph, penX, penY);
+        }
+
+        // TODO: Check how to do this conversion
+        penX += SkFixedRound(cachedGlyph->mAdvanceX);
+
+        // If we were given a specific number of glyphs, decrement
+        if (numGlyphs > 0) {
+            glyphsLeft--;
+        }
+    }
+}
+
+void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo *glyph) {
+    glyph->mAdvanceX = skiaGlyph.fAdvanceX;
+    glyph->mAdvanceY = skiaGlyph.fAdvanceY;
+    glyph->mBitmapLeft = skiaGlyph.fLeft;
+    glyph->mBitmapTop = skiaGlyph.fTop;
+
+    uint32_t startX = 0;
+    uint32_t startY = 0;
+
+    // Let the font state figure out where to put the bitmap
+    FontRenderer *state = mState;
+    // Get the bitmap for the glyph
+    paint->findImage(skiaGlyph);
+    glyph->mIsValid = state->cacheBitmap(skiaGlyph, &startX, &startY);
+
+    if (!glyph->mIsValid) {
+        return;
+    }
+
+    uint32_t endX = startX + skiaGlyph.fWidth;
+    uint32_t endY = startY + skiaGlyph.fHeight;
+
+    glyph->mBitmapWidth = skiaGlyph.fWidth;
+    glyph->mBitmapHeight = skiaGlyph.fHeight;
+
+    uint32_t cacheWidth = state->getCacheWidth();
+    uint32_t cacheHeight = state->getCacheHeight();
+
+    glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
+    glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
+    glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
+    glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
+
+    state->mUploadTexture = true;
+}
+
+Font::CachedGlyphInfo *Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
+    CachedGlyphInfo *newGlyph = new CachedGlyphInfo();
+    mCachedGlyphs.add(glyph, newGlyph);
+
+    const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
+    newGlyph->mGlyphIndex = skiaGlyph.fID;
+    newGlyph->mIsValid = false;
+
+    updateGlyphCache(paint, skiaGlyph, newGlyph);
+
+    return newGlyph;
+}
+
+Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
+    Vector<Font*> &activeFonts = state->mActiveFonts;
+
+    for (uint32_t i = 0; i < activeFonts.size(); i++) {
+        Font *ithFont = activeFonts[i];
+        if (ithFont->mFontId == fontId && ithFont->mFontSize == fontSize) {
+            return ithFont;
+        }
+    }
+
+    Font* newFont = new Font(state, fontId, fontSize);
+    activeFonts.push(newFont);
+    return newFont;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FontRenderer
+///////////////////////////////////////////////////////////////////////////////
+
+FontRenderer::FontRenderer() {
+    mInitialized = false;
+    mMaxNumberOfQuads = 1024;
+    mCurrentQuadIndex = 0;
+
+    mIndexBufferID = 0;
+
+    mCacheWidth = 1024;
+    mCacheHeight = 256;
+}
+
+FontRenderer::~FontRenderer() {
+    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+        delete mCacheLines[i];
+    }
+    mCacheLines.clear();
+
+    delete mTextTexture;
+
+    Vector<Font*> fontsToDereference = mActiveFonts;
+    for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
+        delete fontsToDereference[i];
+    }
+}
+
+void FontRenderer::flushAllAndInvalidate() {
+    if (mCurrentQuadIndex != 0) {
+        issueDrawCommand();
+        mCurrentQuadIndex = 0;
+    }
+    for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
+        mActiveFonts[i]->invalidateTextureCache();
+    }
+    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+        mCacheLines[i]->mCurrentCol = 0;
+    }
+}
+
+bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
+    // If the glyph is too tall, don't cache it
+    if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
+        LOGE("Font size to large to fit in cache. width, height = %i, %i",
+                (int) glyph.fWidth, (int) glyph.fHeight);
+        return false;
+    }
+
+    // Now copy the bitmap into the cache texture
+    uint32_t startX = 0;
+    uint32_t startY = 0;
+
+    bool bitmapFit = false;
+    for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+        bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
+        if (bitmapFit) {
+            break;
+        }
+    }
+
+    // If the new glyph didn't fit, flush the state so far and invalidate everything
+    if (!bitmapFit) {
+        flushAllAndInvalidate();
+
+        // Try to fit it again
+        for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+            bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
+            if (bitmapFit) {
+                break;
+            }
+        }
+
+        // if we still don't fit, something is wrong and we shouldn't draw
+        if (!bitmapFit) {
+            LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
+                    (int) glyph.fWidth, (int) glyph.fHeight);
+            return false;
+        }
+    }
+
+    *retOriginX = startX;
+    *retOriginY = startY;
+
+    uint32_t endX = startX + glyph.fWidth;
+    uint32_t endY = startY + glyph.fHeight;
+
+    uint32_t cacheWidth = mCacheWidth;
+
+    unsigned char *cacheBuffer = mTextTexture;
+    unsigned char *bitmapBuffer = (unsigned char*) glyph.fImage;
+    unsigned int stride = glyph.rowBytes();
+
+    uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
+    for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
+        for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
+            unsigned char tempCol = bitmapBuffer[bY * stride + bX];
+            cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
+        }
+    }
+
+    return true;
+}
+
+void FontRenderer::initTextTexture() {
+    mTextTexture = new unsigned char[mCacheWidth * mCacheHeight];
+    mUploadTexture = false;
+
+    glGenTextures(1, &mTextureId);
+    glBindTexture(GL_TEXTURE_2D, mTextureId);
+    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+    // Split up our cache texture into lines of certain widths
+    int nextLine = 0;
+    mCacheLines.push(new CacheTextureLine(16, mCacheWidth, nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(24, mCacheWidth, nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(32, mCacheWidth, nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(32, mCacheWidth, nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(40, mCacheWidth, nextLine, 0));
+    nextLine += mCacheLines.top()->mMaxHeight;
+    mCacheLines.push(new CacheTextureLine(mCacheHeight - nextLine, mCacheWidth, nextLine, 0));
+}
+
+// Avoid having to reallocate memory and render quad by quad
+void FontRenderer::initVertexArrayBuffers() {
+    uint32_t numIndicies = mMaxNumberOfQuads * 6;
+    uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
+    uint16_t *indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
+
+    // Four verts, two triangles , six indices per quad
+    for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
+        int i6 = i * 6;
+        int i4 = i * 4;
+
+        indexBufferData[i6 + 0] = i4 + 0;
+        indexBufferData[i6 + 1] = i4 + 1;
+        indexBufferData[i6 + 2] = i4 + 2;
+
+        indexBufferData[i6 + 3] = i4 + 0;
+        indexBufferData[i6 + 4] = i4 + 2;
+        indexBufferData[i6 + 5] = i4 + 3;
+    }
+
+    glGenBuffers(1, &mIndexBufferID);
+    glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
+    glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+    free(indexBufferData);
+
+    uint32_t coordSize = 3;
+    uint32_t uvSize = 2;
+    uint32_t vertsPerQuad = 4;
+    uint32_t vertexBufferSizeBytes = mMaxNumberOfQuads * vertsPerQuad * coordSize *
+            uvSize * sizeof(float);
+    mTextMeshPtr = (float*) malloc(vertexBufferSizeBytes);
+}
+
+// We don't want to allocate anything unless we actually draw text
+void FontRenderer::checkInit() {
+    if (mInitialized) {
+        return;
+    }
+
+    initTextTexture();
+    initVertexArrayBuffers();
+
+    mInitialized = true;
+}
+
+void FontRenderer::issueDrawCommand() {
+    if (mUploadTexture) {
+        glBindTexture(GL_TEXTURE_2D, mTextureId);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, GL_ALPHA,
+                GL_UNSIGNED_BYTE, mTextTexture);
+        mUploadTexture = false;
+    }
+
+    float *vtx = mTextMeshPtr;
+    float *tex = vtx + 3;
+
+    // position is slot 0
+    uint32_t slot = 0;
+    glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
+
+    // texture0 is slot 1
+    slot = 1;
+    glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
+
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
+    glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
+}
+
+void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
+        float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
+        float x4, float y4, float z4, float u4, float v4) {
+    const uint32_t vertsPerQuad = 4;
+    const uint32_t floatsPerVert = 5;
+    float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
+
+    // TODO: Cull things that are off the screen
+    //    float width = (float)mRSC->getWidth();
+    //    float height = (float)mRSC->getHeight();
+    //
+    //    if(x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) {
+    //        return;
+    //    }
+
+    (*currentPos++) = x1;
+    (*currentPos++) = y1;
+    (*currentPos++) = z1;
+    (*currentPos++) = u1;
+    (*currentPos++) = v1;
+
+    (*currentPos++) = x2;
+    (*currentPos++) = y2;
+    (*currentPos++) = z2;
+    (*currentPos++) = u2;
+    (*currentPos++) = v2;
+
+    (*currentPos++) = x3;
+    (*currentPos++) = y3;
+    (*currentPos++) = z3;
+    (*currentPos++) = u3;
+    (*currentPos++) = v3;
+
+    (*currentPos++) = x4;
+    (*currentPos++) = y4;
+    (*currentPos++) = z4;
+    (*currentPos++) = u4;
+    (*currentPos++) = v4;
+
+    mCurrentQuadIndex++;
+
+    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
+        issueDrawCommand();
+        mCurrentQuadIndex = 0;
+    }
+}
+
+void FontRenderer::setFont(uint32_t fontId, float fontSize) {
+    mCurrentFont = Font::create(this, fontId, fontSize);
+}
+
+void FontRenderer::renderText(SkPaint* paint, const char *text, uint32_t len, uint32_t startIndex,
+        int numGlyphs, int x, int y) {
+    checkInit();
+
+    // Render code here
+    Font *currentFont = mCurrentFont;
+    if (!currentFont) {
+        LOGE("Unable to initialize any fonts");
+        return;
+    }
+
+    currentFont->renderUTF(paint, text, len, startIndex, numGlyphs, x, y);
+
+    if (mCurrentQuadIndex != 0) {
+        issueDrawCommand();
+        mCurrentQuadIndex = 0;
+    }
+}
+
+void FontRenderer::renderText(SkPaint* paint, const char *text, int x, int y) {
+    size_t textLen = strlen(text);
+    renderText(paint, text, textLen, 0, -1, x, y);
+}
+
+}; // namespace uirenderer
+}; // namespace android