drawTextRSXform

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2130643004

Review-Url: https://codereview.chromium.org/2130643004
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index ce90275..733ff19 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -2647,6 +2647,21 @@
     LOOPER_END
 }
 
+void SkCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
+                                 const SkRect* cullRect, const SkPaint& paint) {
+    if (cullRect && this->quickReject(*cullRect)) {
+        return;
+    }
+
+    LOOPER_BEGIN(paint, SkDrawFilter::kText_Type, nullptr)
+
+    while (iter.next()) {
+        iter.fDevice->drawTextRSXform(iter, text, byteLength, xform, looper.paint());
+    }
+
+    LOOPER_END
+}
+
 void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                               const SkPaint& paint) {
 
@@ -2699,6 +2714,13 @@
     TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextOnPath()");
     this->onDrawTextOnPath(text, byteLength, path, matrix, paint);
 }
+void SkCanvas::drawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
+                               const SkRect* cullRect, const SkPaint& paint) {
+    TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawTextRSXform()");
+    if (byteLength) {
+        this->onDrawTextRSXform(text, byteLength, xform, cullRect, paint);
+    }
+}
 void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                             const SkPaint& paint) {
     RETURN_ON_NULL(blob);
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 185b9bb..2dcedb4 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -403,6 +403,47 @@
     }
 }
 
+#include "SkUtils.h"
+typedef int (*CountTextProc)(const char* text);
+static int count_utf16(const char* text) {
+    const uint16_t* prev = (uint16_t*)text;
+    (void)SkUTF16_NextUnichar(&prev);
+    return SkToInt((const char*)prev - text);
+}
+static int return_4(const char* text) { return 4; }
+static int return_2(const char* text) { return 2; }
+
+void SkBaseDevice::drawTextRSXform(const SkDraw& draw, const void* text, size_t len,
+                                   const SkRSXform xform[], const SkPaint& paint) {
+    CountTextProc proc = nullptr;
+    switch (paint.getTextEncoding()) {
+        case SkPaint::kUTF8_TextEncoding:
+            proc = SkUTF8_CountUTF8Bytes;
+            break;
+        case SkPaint::kUTF16_TextEncoding:
+            proc = count_utf16;
+            break;
+        case SkPaint::kUTF32_TextEncoding:
+            proc = return_4;
+            break;
+        case SkPaint::kGlyphID_TextEncoding:
+            proc = return_2;
+            break;
+    }
+
+    SkDraw localD(draw);
+    SkMatrix localM, currM;
+    const void* stopText = (const char*)text + len;
+    while ((const char*)text < (const char*)stopText) {
+        localM.setRSXform(*xform++);
+        currM.setConcat(*draw.fMatrix, localM);
+        localD.fMatrix = &currM;
+        int subLen = proc((const char*)text);
+        this->drawText(localD, text, subLen, 0, 0, paint);
+        text = (const char*)text + subLen;
+    }
+}
+
 //////////////////////////////////////////////////////////////////////////////////////////
 
 void SkBaseDevice::drawSpriteWithFilter(const SkDraw& draw, const SkBitmap& bitmap,
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
index d4c8c1f..3546fb2 100644
--- a/src/core/SkPictureFlat.h
+++ b/src/core/SkPictureFlat.h
@@ -79,6 +79,7 @@
     DRAW_ANNOTATION,
     DRAW_DRAWABLE,
     DRAW_DRAWABLE_MATRIX,
+    DRAW_TEXT_RSXFORM,
 
     LAST_DRAWTYPE_ENUM = DRAW_DRAWABLE_MATRIX,
 };
@@ -98,6 +99,10 @@
     DRAW_ATLAS_HAS_CULL     = 1 << 1,
 };
 
+enum DrawTextRSXformFlags {
+    DRAW_TEXT_RSXFORM_HAS_CULL  = 1 << 0,
+};
+
 enum SaveLayerRecFlatFlags {
     SAVELAYERREC_HAS_BOUNDS     = 1 << 0,
     SAVELAYERREC_HAS_PAINT      = 1 << 1,
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index 3cbcdbb..6b89304 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -501,6 +501,19 @@
                 canvas->drawTextOnPath(text.text(), text.length(), path, &matrix, *paint);
             }
         } break;
+        case DRAW_TEXT_RSXFORM: {
+            const SkPaint* paint = fPictureData->getPaint(reader);
+            int count = reader->readInt();
+            uint32_t flags = reader->read32();
+            TextContainer text;
+            get_text(reader, &text);
+            const SkRSXform* xform = (const SkRSXform*)reader->skip(count * sizeof(SkRSXform));
+            const SkRect* cull = nullptr;
+            if (flags & DRAW_TEXT_RSXFORM_HAS_CULL) {
+                cull = (const SkRect*)reader->skip(sizeof(SkRect));
+            }
+            canvas->drawTextRSXform(text.text(), text.length(), xform, cull, *paint);
+        } break;
         case DRAW_VERTICES: {
             sk_sp<SkXfermode> xfer;
             const SkPaint* paint = fPictureData->getPaint(reader);
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index 4a6ece1..17ed1aa 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -603,6 +603,30 @@
     this->validate(initialOffset, size);
 }
 
+void SkPictureRecord::onDrawTextRSXform(const void* text, size_t byteLength,
+                                        const SkRSXform xform[], const SkRect* cull,
+                                        const SkPaint& paint) {
+    const int count = paint.countText(text, byteLength);
+    // [op + paint-index + count + flags + length] + [text] + [xform] + cull
+    size_t size = 5 * kUInt32Size + SkAlign4(byteLength) + count * sizeof(SkRSXform);
+    uint32_t flags = 0;
+    if (cull) {
+        flags |= DRAW_TEXT_RSXFORM_HAS_CULL;
+        size += sizeof(SkRect);
+    }
+
+    size_t initialOffset = this->addDraw(DRAW_TEXT_RSXFORM, &size);
+    this->addPaint(paint);
+    this->addInt(count);
+    this->addInt(flags);
+    this->addText(text, byteLength);
+    fWriter.write(xform, count * sizeof(SkRSXform));
+    if (cull) {
+        fWriter.write(cull, sizeof(SkRect));
+    }
+    this->validate(initialOffset, size);
+}
+
 void SkPictureRecord::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                                      const SkPaint& paint) {
 
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
index 8504c00..bdb6609 100644
--- a/src/core/SkPictureRecord.h
+++ b/src/core/SkPictureRecord.h
@@ -167,6 +167,8 @@
                         const SkPaint&) override;
     void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
                                   const SkMatrix* matrix, const SkPaint&) override;
+    void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
+                           const SkRect* cull, const SkPaint&) override;
     void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                                 const SkPaint& paint) override;
 
diff --git a/src/core/SkRecordDraw.cpp b/src/core/SkRecordDraw.cpp
index d0e8c4c..ec9aee9 100644
--- a/src/core/SkRecordDraw.cpp
+++ b/src/core/SkRecordDraw.cpp
@@ -114,6 +114,7 @@
 DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint));
 DRAW(DrawTextBlob, drawTextBlob(r.blob, r.x, r.y, r.paint));
 DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, &r.matrix, r.paint));
+DRAW(DrawTextRSXform, drawTextRSXform(r.text, r.byteLength, r.xforms, r.cull, r.paint));
 DRAW(DrawAtlas, drawAtlas(r.atlas, r.xforms, r.texs, r.colors, r.count, r.mode, r.cull, r.paint));
 DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.colors,
                                 r.xmode, r.indices, r.indexCount, r.paint));
@@ -454,6 +455,8 @@
 
     Bounds bounds(const DrawAtlas& op) const {
         if (op.cull) {
+            // TODO: <reed> can we pass nullptr for the paint? Isn't cull already "correct"
+            // for the paint (by the caller)?
             return this->adjustAndMap(*op.cull, op.paint);
         } else {
             return fCurrentClipBounds;
@@ -508,6 +511,14 @@
         return this->adjustAndMap(dst, &op.paint);
     }
 
+    Bounds bounds(const DrawTextRSXform& op) const {
+        if (op.cull) {
+            return this->adjustAndMap(*op.cull, nullptr);
+        } else {
+            return fCurrentClipBounds;
+        }
+    }
+
     Bounds bounds(const DrawTextBlob& op) const {
         SkRect dst = op.blob->bounds();
         dst.offset(op.x, op.y);
diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp
index ff5c27d..19cb663 100644
--- a/src/core/SkRecorder.cpp
+++ b/src/core/SkRecorder.cpp
@@ -278,6 +278,16 @@
            matrix ? *matrix : SkMatrix::I());
 }
 
+void SkRecorder::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
+                                   const SkRect* cull, const SkPaint& paint) {
+    APPEND(DrawTextRSXform,
+           paint,
+           this->copy((const char*)text, byteLength),
+           byteLength,
+           this->copy(xform, paint.countText(text, byteLength)),
+           this->copy(cull));
+}
+
 void SkRecorder::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                                 const SkPaint& paint) {
     TRY_MINIRECORDER(drawTextBlob, blob, x, y, paint);
diff --git a/src/core/SkRecorder.h b/src/core/SkRecorder.h
index 1299efb..3cf0be9 100644
--- a/src/core/SkRecorder.h
+++ b/src/core/SkRecorder.h
@@ -82,6 +82,11 @@
                           const SkPath& path,
                           const SkMatrix* matrix,
                           const SkPaint& paint) override;
+    void onDrawTextRSXform(const void* text,
+                           size_t byteLength,
+                           const SkRSXform[],
+                           const SkRect* cull,
+                           const SkPaint& paint) override;
     void onDrawTextBlob(const SkTextBlob* blob,
                         SkScalar x,
                         SkScalar y,
diff --git a/src/utils/SkDumpCanvas.cpp b/src/utils/SkDumpCanvas.cpp
index 7a37fcb..5166640 100644
--- a/src/utils/SkDumpCanvas.cpp
+++ b/src/utils/SkDumpCanvas.cpp
@@ -429,6 +429,14 @@
                str.c_str(), byteLength);
 }
 
+void SkDumpCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
+                                     const SkRect* cull, const SkPaint& paint) {
+    SkString str;
+    toString(text, byteLength, paint.getTextEncoding(), &str);
+    this->dump(kDrawText_Verb, &paint, "drawTextRSXform(%s [%d])",
+               str.c_str(), byteLength);
+}
+
 void SkDumpCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                                   const SkPaint& paint) {
     SkString str;
diff --git a/src/utils/SkLuaCanvas.cpp b/src/utils/SkLuaCanvas.cpp
index c51b0d8..a3160af 100644
--- a/src/utils/SkLuaCanvas.cpp
+++ b/src/utils/SkLuaCanvas.cpp
@@ -273,6 +273,14 @@
     lua.pushPaint(paint, "paint");
 }
 
+void SkLuaCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
+                                    const SkRect* cull, const SkPaint& paint) {
+    AUTO_LUA("drawTextRSXform");
+    lua.pushEncodedText(paint.getTextEncoding(), text, byteLength);
+    // TODO: export other params
+    lua.pushPaint(paint, "paint");
+}
+
 void SkLuaCanvas::onDrawTextBlob(const SkTextBlob *blob, SkScalar x, SkScalar y,
                                  const SkPaint &paint) {
     AUTO_LUA("drawTextBlob");
diff --git a/src/utils/SkNWayCanvas.cpp b/src/utils/SkNWayCanvas.cpp
index 4397814..dc9437c 100644
--- a/src/utils/SkNWayCanvas.cpp
+++ b/src/utils/SkNWayCanvas.cpp
@@ -254,6 +254,14 @@
     }
 }
 
+void SkNWayCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[],
+                                     const SkRect* cull, const SkPaint& paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawTextRSXform(text, byteLength, xform, cull, paint);
+    }
+}
+
 void SkNWayCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                                   const SkPaint &paint) {
     Iter iter(fList);
diff --git a/src/utils/SkPaintFilterCanvas.cpp b/src/utils/SkPaintFilterCanvas.cpp
index ea94068..75a7930 100644
--- a/src/utils/SkPaintFilterCanvas.cpp
+++ b/src/utils/SkPaintFilterCanvas.cpp
@@ -204,6 +204,15 @@
     }
 }
 
+void SkPaintFilterCanvas::onDrawTextRSXform(const void* text, size_t byteLength,
+                                            const SkRSXform xform[], const SkRect* cull,
+                                            const SkPaint& paint) {
+    AutoPaintFilter apf(this, kText_Type, paint);
+    if (apf.shouldDraw()) {
+        this->INHERITED::onDrawTextRSXform(text, byteLength, xform, cull, *apf.paint());
+    }
+}
+
 void SkPaintFilterCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
                                          const SkPaint& paint) {
     AutoPaintFilter apf(this, kTextBlob_Type, paint);