More efficient text rendering on path

Change-Id: I004c15473b527df0f296c54a6a3e9b29505fd9b9
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 4528a38..6dbcd3f 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -20,6 +20,7 @@
 #include "RecordedOp.h"
 #include "RenderNode.h"
 #include "VectorDrawable.h"
+#include "hwui/MinikinUtils.h"
 
 namespace android {
 namespace uirenderer {
@@ -541,14 +542,20 @@
     drawTextDecorations(x, y, totalAdvance, paint);
 }
 
-void RecordingCanvas::drawGlyphsOnPath(const uint16_t* glyphs, int glyphCount, const SkPath& path,
-            float hOffset, float vOffset, const SkPaint& paint) {
-    if (!glyphs || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return;
-    glyphs = refBuffer<glyph_t>(glyphs, glyphCount);
-    addOp(alloc().create_trivial<TextOnPathOp>(
-            *(mState.currentSnapshot()->transform),
-            getRecordedClip(),
-            refPaint(&paint), glyphs, glyphCount, refPath(&path), hOffset, vOffset));
+void RecordingCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
+        const SkPaint& paint, const SkPath& path, size_t start, size_t end) {
+    uint16_t glyphs[1];
+    for (size_t i = start; i < end; i++) {
+        glyphs[0] = layout.getGlyphId(i);
+        float x = hOffset + layout.getX(i);
+        float y = vOffset + layout.getY(i);
+        if (PaintUtils::paintWillNotDrawText(paint)) return;
+        const uint16_t* tempGlyphs = refBuffer<glyph_t>(glyphs, 1);
+        addOp(alloc().create_trivial<TextOnPathOp>(
+                *(mState.currentSnapshot()->transform),
+                getRecordedClip(),
+                refPaint(&paint), tempGlyphs, 1, refPath(&path), x, y));
+    }
 }
 
 void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) {
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 372be24..11773d4 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -196,8 +196,8 @@
             const SkPaint& paint, float x, float y,
             float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
             float totalAdvance) override;
-    virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
-            float hOffset, float vOffset, const SkPaint& paint) override;
+    virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
+            const SkPaint& paint, const SkPath& path, size_t start, size_t end) override;
 
 private:
     const ClipBase* getRecordedClip() {
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 99ea831..09775496 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -18,6 +18,7 @@
 
 #include "CanvasProperty.h"
 #include "VectorDrawable.h"
+#include "hwui/MinikinUtils.h"
 
 #include <SkDrawable.h>
 #include <SkDevice.h>
@@ -25,6 +26,7 @@
 #include <SkDrawFilter.h>
 #include <SkGraphics.h>
 #include <SkImage.h>
+#include <SkRSXform.h>
 #include <SkShader.h>
 #include <SkTemplates.h>
 
@@ -624,9 +626,32 @@
     drawTextDecorations(x, y, totalAdvance, paint);
 }
 
-void SkiaCanvas::drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
-        float hOffset, float vOffset, const SkPaint& paint) {
-    mCanvas->drawTextOnPathHV(glyphs, count << 1, path, hOffset, vOffset, paint);
+void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
+        const SkPaint& paint, const SkPath& path, size_t start, size_t end) {
+    const int N = end - start;
+    SkAutoSMalloc<1024> storage(N * (sizeof(uint16_t) + sizeof(SkRSXform)));
+    SkRSXform* xform = (SkRSXform*)storage.get();
+    uint16_t* glyphs = (uint16_t*)(xform + N);
+    SkPathMeasure meas(path, false);
+
+    for (size_t i = start; i < end; i++) {
+        glyphs[i - start] = layout.getGlyphId(i);
+        float x = hOffset + layout.getX(i);
+        float y = vOffset + layout.getY(i);
+
+        SkPoint pos;
+        SkVector tan;
+        if (!meas.getPosTan(x, &pos, &tan)) {
+            pos.set(x, y);
+            tan.set(1, 0);
+        }
+        xform[i - start].fSCos = tan.x();
+        xform[i - start].fSSin = tan.y();
+        xform[i - start].fTx   = pos.x() - tan.y() * y;
+        xform[i - start].fTy   = pos.y() + tan.x() * y;
+    }
+
+    this->asSkCanvas()->drawTextRSXform(glyphs, sizeof(uint16_t) * N, xform, nullptr, paint);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index c76971c..0e506f4 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -161,8 +161,8 @@
             const SkPaint& paint, float x, float y,
             float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
             float totalAdvance) override;
-    virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
-            float hOffset, float vOffset, const SkPaint& paint) override;
+    virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
+            const SkPaint& paint, const SkPath& path, size_t start, size_t end) override;
 
 private:
     struct SaveRec {
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index f264a35..c4556c4 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -23,6 +23,7 @@
 #include <SkPixelRef.h>
 #include <SkRect.h>
 #include <SkRRect.h>
+#include <SkRSXform.h>
 #include <SkSurface.h>
 
 #include <memory>
@@ -348,11 +349,26 @@
 
 void SkiaCanvasProxy::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
         const SkMatrix* matrix, const SkPaint& origPaint) {
-    // convert to glyphIDs if necessary
-    GlyphIDConverter glyphs(text, byteLength, origPaint);
-    mCanvas->drawGlyphsOnPath(glyphs.glyphIDs, glyphs.count, path, 0, 0, glyphs.paint);
+    SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextOnPath is not supported");
 }
 
+void SkiaCanvasProxy::onDrawTextRSXform(const void* text, size_t byteLength,
+        const SkRSXform xform[], const SkRect* cullRect, const SkPaint& paint) {
+    GlyphIDConverter glyphs(text, byteLength, paint); // Just get count
+    SkMatrix localM, currM, origM;
+    mCanvas->getMatrix(&currM);
+    origM = currM;
+    for (int i = 0; i < glyphs.count; i++) {
+        localM.setRSXform(*xform++);
+        currM.setConcat(origM, localM);
+        mCanvas->setMatrix(currM);
+        this->onDrawText((char*)text + (byteLength / glyphs.count * i),
+                         byteLength / glyphs.count, 0, 0, paint);
+    }
+    mCanvas->setMatrix(origM);
+}
+
+
 void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
         const SkPaint& paint) {
     SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextBlob is not supported");
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
index d1c9c6b..5bb4231 100644
--- a/libs/hwui/SkiaCanvasProxy.h
+++ b/libs/hwui/SkiaCanvasProxy.h
@@ -81,6 +81,8 @@
                                 SkScalar constY, const SkPaint&) override;
     virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
                                   const SkMatrix* matrix, const SkPaint&) override;
+    virtual void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[],
+                                   const SkRect* cullRect, const SkPaint& paint);
     virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                                 const SkPaint& paint) override;
 
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 461363f..b18e794 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -185,13 +185,7 @@
     }
 
     void operator()(size_t start, size_t end) {
-        uint16_t glyphs[1];
-        for (size_t i = start; i < end; i++) {
-            glyphs[0] = layout.getGlyphId(i);
-            float x = hOffset + layout.getX(i);
-            float y = vOffset + layout.getY(i);
-            canvas->drawGlyphsOnPath(glyphs, 1, path, x, y, paint);
-        }
+        canvas->drawLayoutOnPath(layout, hOffset, vOffset, paint, path, start, end);
     }
 private:
     const minikin::Layout& layout;
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 0b42099..57db0f2 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -26,6 +26,10 @@
 #include <SkCanvas.h>
 #include <SkMatrix.h>
 
+namespace minikin {
+    class Layout;
+}
+
 namespace android {
 
 namespace uirenderer {
@@ -242,10 +246,8 @@
             const SkPaint& paint, float x, float y,
             float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
             float totalAdvance) = 0;
-    /** drawTextOnPath: count is of glyphs */
-    virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
-            float hOffset, float vOffset, const SkPaint& paint) = 0;
-
+    virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
+            const SkPaint& paint, const SkPath& path, size_t start, size_t end) = 0;
     friend class DrawTextFunctor;
     friend class DrawTextOnPathFunctor;
     friend class uirenderer::SkiaCanvasProxy;