Full implementation of Canvas.drawPath()

Change-Id: I23223b89770a0cd2b4762365bead9bfddb094290
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 3df105b..f2bb6ec 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -39,6 +39,8 @@
 #define MAX_TEXT_CACHE_WIDTH 2048
 #define TEXTURE_BORDER_SIZE 2
 
+#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
+
 ///////////////////////////////////////////////////////////////////////////////
 // CacheTextureLine
 ///////////////////////////////////////////////////////////////////////////////
@@ -163,6 +165,44 @@
     }
 }
 
+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;
+
+    float nPenX = glyph->mBitmapLeft;
+    vOffset += glyph->mBitmapTop + height;
+
+    const float u1 = glyph->mBitmapMinU;
+    const float u2 = glyph->mBitmapMaxU;
+    const float v1 = glyph->mBitmapMinV;
+    const float v2 = glyph->mBitmapMaxV;
+
+    SkPoint destination[4];
+    measure.getPosTan(x + hOffset + nPenX + 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);
+
+    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->mCachedTextureLine->mCacheTexture);
+}
+
 CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
     CachedGlyphInfo* cachedGlyph = NULL;
     ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
@@ -198,6 +238,56 @@
             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, roundf(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) {
     if (bounds == NULL) {
@@ -208,19 +298,13 @@
     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, const float* positions) {
+        uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
     if (numGlyphs == 0 || text == NULL || len == 0) {
         return;
     }
 
-    int glyphsCount = 0;
-
-    text += start;
-
     static RenderGlyph gRenderGlyph[] = {
             &android::uirenderer::Font::drawCachedGlyph,
             &android::uirenderer::Font::drawCachedGlyphBitmap,
@@ -228,14 +312,15 @@
     };
     RenderGlyph render = gRenderGlyph[mode];
 
+    text += start;
+    int glyphsCount = 0;
+
     if (CC_LIKELY(positions == NULL)) {
         SkFixed prevRsbDelta = 0;
 
-        float penX = x;
+        float penX = x + 0.5f;
         int penY = y;
 
-        penX += 0.5f;
-
         while (glyphsCount < numGlyphs) {
             glyph_t glyph = GET_GLYPH(text);
 
@@ -245,7 +330,7 @@
             }
 
             CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
-            penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
+            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
@@ -582,6 +667,7 @@
             cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
         }
     }
+
     cachedGlyph->mIsValid = true;
 }
 
@@ -758,15 +844,9 @@
     mDrawn = true;
 }
 
-void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
-        float x2, float y2, float u2, float v2,
-        float x3, float y3, float u3, float v3,
+void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
+        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
         float x4, float y4, float u4, float v4, CacheTexture* texture) {
-
-    if (mClip &&
-            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
-        return;
-    }
     if (texture != mCurrentCacheTexture) {
         if (mCurrentQuadIndex != 0) {
             // First, draw everything stored already which uses the previous texture
@@ -802,6 +882,18 @@
     (*currentPos++) = v4;
 
     mCurrentQuadIndex++;
+}
+
+void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
+        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
+        float x4, float y4, float u4, float v4, CacheTexture* texture) {
+
+    if (mClip &&
+            (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
+        return;
+    }
+
+    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
 
     if (mBounds) {
         mBounds->left = fmin(mBounds->left, x1);
@@ -816,6 +908,25 @@
     }
 }
 
+void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
+        float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3,
+        float x4, float y4, float u4, float v4, CacheTexture* texture) {
+
+    appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture);
+
+    if (mBounds) {
+        mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4))));
+        mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4))));
+        mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4))));
+        mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4))));
+    }
+
+    if (mCurrentQuadIndex == mMaxNumberOfQuads) {
+        issueDrawCommand();
+        mCurrentQuadIndex = 0;
+    }
+}
+
 uint32_t FontRenderer::getRemainingCacheCapacity() {
     uint32_t remainingCapacity = 0;
     float totalPixels = 0;
@@ -956,6 +1067,21 @@
     return mDrawn;
 }
 
+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) {
+    if (!mCurrentFont) {
+        ALOGE("No font set");
+        return false;
+    }
+
+    initRender(clip, bounds);
+    mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
+    finishRender();
+
+    return mDrawn;
+}
+
 void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
     // Compute gaussian weights for the blur
     // e is the euler's number