Full support for Canvas.drawPosText

This also introduces a small optimization when rendering text.

Change-Id: Iff620ac97bf878eaac406bccc6daa07052c93890
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 3bf28eb..064dcac 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -585,10 +585,10 @@
 
     const jchar* glyphs = value->getGlyphs();
     size_t glyphsCount = value->getGlyphsCount();
+    if (count < int(glyphsCount)) glyphsCount = count;
     int bytesCount = glyphsCount * sizeof(jchar);
 
-    renderer->drawPosText((const char*) glyphs, bytesCount,
-            count < int(glyphsCount) ? count : glyphsCount, positions, paint);
+    renderer->drawPosText((const char*) glyphs, bytesCount, glyphsCount, positions, paint);
 }
 
 static void android_view_GLES20Canvas_drawPosTextArray(JNIEnv* env, jobject clazz,
diff --git a/docs/html/guide/topics/graphics/hardware-accel.jd b/docs/html/guide/topics/graphics/hardware-accel.jd
index c8703a5..e3ff215 100644
--- a/docs/html/guide/topics/graphics/hardware-accel.jd
+++ b/docs/html/guide/topics/graphics/hardware-accel.jd
@@ -283,8 +283,6 @@
 
         <li>{@link android.graphics.Canvas#drawPicture drawPicture()}</li>
 
-        <li>{@link android.graphics.Canvas#drawPosText drawPosText()}</li>
-
         <li>{@link android.graphics.Canvas#drawTextOnPath drawTextOnPath()}</li>
 
         <li>{@link android.graphics.Canvas#drawVertices drawVertices()}</li>
@@ -318,9 +316,6 @@
         <li>{@link android.graphics.Canvas#drawBitmapMesh drawBitmapMesh()}: colors array is
         ignored</li>
 
-        <li>{@link android.graphics.Canvas#drawLines drawLines()}: anti-aliasing is not
-        supported</li>
-
         <li>{@link android.graphics.Canvas#setDrawFilter setDrawFilter()}: can be set, but is
         ignored</li>
       </ul>
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 790c143..74efda2 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -94,7 +94,8 @@
     }
 }
 
-void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
+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;
 
@@ -115,7 +116,8 @@
     }
 }
 
-void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
+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;
 
@@ -133,8 +135,8 @@
             nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture);
 }
 
-void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
-        uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+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;
 
@@ -181,13 +183,19 @@
         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);
+                bitmapW, bitmapH, NULL, NULL);
     } else {
         render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
-                0, 0, 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::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
         int numGlyphs, Rect *bounds) {
     if (bounds == NULL) {
@@ -195,62 +203,95 @@
         return;
     }
     bounds->set(1e6, -1e6, -1e6, 1e6);
-    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
+    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, NULL);
 }
 
 #define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
 
 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) {
+        uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions) {
     if (numGlyphs == 0 || text == NULL || len == 0) {
         return;
     }
 
-    float penX = x;
-    int penY = y;
-    int glyphsLeft = 1;
-    if (numGlyphs > 0) {
-        glyphsLeft = numGlyphs;
-    }
-
-    SkFixed prevRsbDelta = 0;
-    penX += 0.5f;
+    int glyphsCount = 0;
 
     text += start;
 
-    while (glyphsLeft > 0) {
-        glyph_t glyph = GET_GLYPH(text);
+    static RenderGlyph gRenderGlyph[] = {
+            &android::uirenderer::Font::drawCachedGlyph,
+            &android::uirenderer::Font::drawCachedGlyphBitmap,
+            &android::uirenderer::Font::measureCachedGlyph
+    };
+    RenderGlyph render = gRenderGlyph[mode];
 
-        // Reached the end of the string
-        if (IS_END_OF_STRING(glyph)) {
-            break;
-        }
+    if (positions == NULL) {
+        SkFixed prevRsbDelta = 0;
 
-        CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
-        penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
-        prevRsbDelta = cachedGlyph->mRsbDelta;
+        float penX = x;
+        int penY = y;
 
-        // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
-        if (cachedGlyph->mIsValid) {
-            switch(mode) {
-            case FRAMEBUFFER:
-                drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY);
-                break;
-            case BITMAP:
-                drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bitmap, bitmapW, bitmapH);
-                break;
-            case MEASURE:
-                measureCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bounds);
+        penX += 0.5f;
+
+        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(SkAutoKern_AdjustF(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();
 
-        penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
+        // This is for renderPosText()
+        while (glyphsCount < numGlyphs) {
+            glyph_t glyph = GET_GLYPH(text);
 
-        // If we were given a specific number of glyphs, decrement
-        if (numGlyphs > 0) {
-            glyphsLeft--;
+            // 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++;
         }
     }
 }
@@ -866,21 +907,15 @@
     return image;
 }
 
-bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
-        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
+void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
     checkInit();
 
-    if (!mCurrentFont) {
-        ALOGE("No font set");
-        return false;
-    }
-
     mDrawn = false;
     mBounds = bounds;
     mClip = clip;
+}
 
-    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
-
+void FontRenderer::finishRender() {
     mBounds = NULL;
     mClip = NULL;
 
@@ -888,6 +923,33 @@
         issueDrawCommand();
         mCurrentQuadIndex = 0;
     }
+}
+
+bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
+        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
+    if (!mCurrentFont) {
+        ALOGE("No font set");
+        return false;
+    }
+
+    initRender(clip, bounds);
+    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
+    finishRender();
+
+    return mDrawn;
+}
+
+bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
+        uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
+        const float* positions, Rect* bounds) {
+    if (!mCurrentFont) {
+        ALOGE("No font set");
+        return false;
+    }
+
+    initRender(clip, bounds);
+    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
+    finishRender();
 
     return mDrawn;
 }
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 005cdde..b767be5 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -151,6 +151,10 @@
     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);
+
     /**
      * Creates a new font associated with the specified font state.
      */
@@ -160,6 +164,8 @@
 
 protected:
     friend class FontRenderer;
+    typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
+            uint32_t, uint32_t, Rect*, const float*);
 
     enum RenderMode {
         FRAMEBUFFER,
@@ -169,7 +175,7 @@
 
     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);
+            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);
@@ -183,11 +189,17 @@
     void invalidateTextureCache(CacheTextureLine *cacheLine = NULL);
 
     CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph);
-    void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo *glyph);
-    void measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds);
-    void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y);
-    void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
-            uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH);
+    void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph);
+
+    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);
 
     CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit);
 
@@ -226,8 +238,12 @@
     }
 
     void setFont(SkPaint* paint, uint32_t fontId, float fontSize);
+    // bounds is an out parameter
     bool renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
             uint32_t len, int numGlyphs, int x, int y, Rect* bounds);
+    // bounds is an out parameter
+    bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
+            uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds);
 
     struct DropShadow {
         DropShadow() { };
@@ -297,6 +313,8 @@
     void initVertexArrayBuffers();
 
     void checkInit();
+    void initRender(const Rect* clip, Rect* bounds);
+    void finishRender();
 
     String16 mLatinPrecache;
     void precacheLatin(SkPaint* paint);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 6ec87f3..786a4f1 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2084,23 +2084,81 @@
 
 void OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
         const float* positions, SkPaint* paint) {
-    if (text == NULL || count == 0) {
+    if (text == NULL || count == 0 || mSnapshot->isIgnored() ||
+            (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
         return;
     }
-    if (mSnapshot->isIgnored()) return;
 
-    // TODO: implement properly
-    drawText(text, bytesCount, count, 0, 0, paint);
+    // NOTE: Skia does not support perspective transform on drawPosText yet
+    if (!mSnapshot->transform->isSimple()) {
+        return;
+    }
+
+    float x = 0.0f;
+    float y = 0.0f;
+    const bool pureTranslate = mSnapshot->transform->isPureTranslate();
+    if (pureTranslate) {
+        x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
+        y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
+    }
+
+    FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(paint);
+    fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
+            paint->getTextSize());
+
+    int alpha;
+    SkXfermode::Mode mode;
+    getAlphaAndMode(paint, &alpha, &mode);
+
+    // Pick the appropriate texture filtering
+    bool linearFilter = mSnapshot->transform->changesBounds();
+    if (pureTranslate && !linearFilter) {
+        linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
+    }
+
+    mCaches.activeTexture(0);
+    setupDraw();
+    setupDrawDirtyRegionsDisabled();
+    setupDrawWithTexture(true);
+    setupDrawAlpha8Color(paint->getColor(), alpha);
+    setupDrawColorFilter();
+    setupDrawShader();
+    setupDrawBlending(true, mode);
+    setupDrawProgram();
+    setupDrawModelView(x, y, x, y, pureTranslate, true);
+    setupDrawTexture(fontRenderer.getTexture(linearFilter));
+    setupDrawPureColorUniforms();
+    setupDrawColorFilterUniforms();
+    setupDrawShaderUniforms(pureTranslate);
+
+    const Rect* clip = pureTranslate ? mSnapshot->clipRect : &mSnapshot->getLocalClip();
+    Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
+
+#if RENDER_LAYERS_AS_REGIONS
+    bool hasActiveLayer = hasLayer();
+#else
+    bool hasActiveLayer = false;
+#endif
+
+    if (fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
+            positions, hasActiveLayer ? &bounds : NULL)) {
+#if RENDER_LAYERS_AS_REGIONS
+        if (hasActiveLayer) {
+            if (!pureTranslate) {
+                mSnapshot->transform->mapRect(bounds);
+            }
+            dirtyLayerUnchecked(bounds, getRegion());
+        }
+#endif
+    }
 }
 
 void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
         float x, float y, SkPaint* paint, float length) {
-    if (text == NULL || count == 0) {
+    if (text == NULL || count == 0 || mSnapshot->isIgnored() ||
+            (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
         return;
     }
-    if (mSnapshot->isIgnored()) return;
-
-    // NOTE: AA and glyph id encoding are set in DisplayListRenderer.cpp
 
     switch (paint->getTextAlign()) {
         case SkPaint::kCenter_Align:
@@ -2177,10 +2235,6 @@
         glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
     }
 
-    if (paint->getAlpha() == 0 && paint->getXfermode() == NULL) {
-        return;
-    }
-
     // Pick the appropriate texture filtering
     bool linearFilter = mSnapshot->transform->changesBounds();
     if (pureTranslate && !linearFilter) {
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PosTextActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PosTextActivity.java
index f0ff737..1c868d2 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/PosTextActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PosTextActivity.java
@@ -58,8 +58,21 @@
             canvas.drawRGB(255, 255, 255);
             
             canvas.save();
+
+            canvas.drawLine(100.0f, 0.0f, 100.0f, getHeight(), mLargePaint);
+            
             canvas.translate(100.0f, 100.0f);
+            mLargePaint.setTextAlign(Paint.Align.LEFT);
             canvas.drawPosText(mText, mPos, mLargePaint);
+
+            canvas.translate(0.0f, 50.0f);
+            mLargePaint.setTextAlign(Paint.Align.CENTER);
+            canvas.drawPosText(mText, mPos, mLargePaint);
+
+            canvas.translate(0.0f, 50.0f);
+            mLargePaint.setTextAlign(Paint.Align.RIGHT);
+            canvas.drawPosText(mText, mPos, mLargePaint);
+
             canvas.restore();
         }
     }