[canvaskit] Use textblob

Removes old API and replaces with new version.
drawText now requires SkFont and other changes.

Bug: skia:
Change-Id: Ie42a5243629542934c761223ed2e8dc6685d3572
Reviewed-on: https://skia-review.googlesource.com/c/183389
Reviewed-by: Mike Reed <reed@google.com>
diff --git a/experimental/canvaskit/CHANGELOG.md b/experimental/canvaskit/CHANGELOG.md
index bb86db4..5fd1a52 100644
--- a/experimental/canvaskit/CHANGELOG.md
+++ b/experimental/canvaskit/CHANGELOG.md
@@ -11,6 +11,16 @@
  - `SkCanvas.drawArc`, `SkCanvas.drawLine`, `SkCanvas.drawOval`, `SkCanvas.drawRoundRect` exposed.
  - Can import/export a SkPath to an array of commands. See `CanvasKit.MakePathFromCmds` and
    `SkPath.toCmds`.
+ - `SkCanvas.drawTextBlob()` and `SkCanvas.SkTextBlob.MakeFromText()` to draw text to a canvas.
+ - `CanvasKit.TextEncoding` enum. For use with `SkTextBlob`.
+
+### Changed
+ - `SkCanvas.drawText()` now requires an `SkFont` object.
+
+### Removed
+ -  `SkPaint.setTextSize()`, `SkPaint.getTextSize()`, `SkPaint.setTypeface()`
+   which should be replaced by using `SkFont`.
+
 
 ### Fixed
  - Potential bug in `ready()` if already loaded.
diff --git a/experimental/canvaskit/canvaskit/example.html b/experimental/canvaskit/canvaskit/example.html
index c018b4b..0007881 100644
--- a/experimental/canvaskit/canvaskit/example.html
+++ b/experimental/canvaskit/canvaskit/example.html
@@ -156,9 +156,9 @@
 
     const textPaint = new CanvasKit.SkPaint();
     textPaint.setColor(CanvasKit.RED);
-    textPaint.setTextSize(30);
     textPaint.setAntiAlias(true);
-    textPaint.setTypeface(roboto);
+
+    const textFont = new CanvasKit.SkFont(roboto, 30);
 
     let i = 0;
 
@@ -183,7 +183,7 @@
       canvas.clear(CanvasKit.TRANSPARENT);
 
       canvas.drawPath(path, paint);
-      canvas.drawText('Try Clicking!', 10, 280, textPaint);
+      canvas.drawText('Try Clicking!', 10, 280, textFont, textPaint);
 
       surface.flush();
 
@@ -205,7 +205,9 @@
     document.getElementById('patheffect').addEventListener('pointerdown', interact);
     preventScrolling(document.getElementById('patheffect'));
     // A client would need to delete this if it didn't go on for ever.
-    //paint.delete();
+    // paint.delete();
+    // textPaint.delete();
+    // textFont.delete();
   }
 
   function PathExample(CanvasKit) {
@@ -1067,8 +1069,13 @@
                               pos, CanvasKit.TileMode.Mirror, transform);
 
     paint.setShader(shader);
-    paint.setTextSize(75);
-    canvas.drawText('Radial', 10, 200, paint);
+    const textFont = new CanvasKit.SkFont(null, 75);
+    const textBlob = CanvasKit.SkTextBlob.MakeFromText('Radial', textFont);
+
+    canvas.drawTextBlob(textBlob, 10, 200, paint);
+    paint.delete()
+    textFont.delete();
+    textBlob.delete();
     surface.flush();
   }
 </script>
diff --git a/experimental/canvaskit/canvaskit/node.example.js b/experimental/canvaskit/canvaskit/node.example.js
index f9ae3f6..65fbe79 100644
--- a/experimental/canvaskit/canvaskit/node.example.js
+++ b/experimental/canvaskit/canvaskit/node.example.js
@@ -41,9 +41,10 @@
   ctx.drawImage(img, 100, 150, 400, 350, 10, 200, 150, 100);
 
   console.log('<img src="' + canvas.toDataURL() + '" />');
+
+  fancyAPI(CanvasKit);
 });
 
-// Not currently called
 function fancyAPI(CanvasKit) {
   let surface = CanvasKit.MakeSurface(300, 300);
   const canvas = surface.getCanvas();
@@ -56,9 +57,9 @@
 
   const textPaint = new CanvasKit.SkPaint();
   textPaint.setColor(CanvasKit.Color(40, 0, 0));
-  textPaint.setTextSize(30);
   textPaint.setAntiAlias(true);
-  textPaint.setTypeface(roboto);
+
+  const textFont = new CanvasKit.SkFont(roboto, 30);
 
   const skpath = starPath(CanvasKit);
   const dpe = CanvasKit.MakeSkDashPathEffect([15, 5, 5, 10], 1);
@@ -72,7 +73,7 @@
   canvas.clear(CanvasKit.Color(255, 255, 255, 1.0));
 
   canvas.drawPath(skpath, paint);
-  canvas.drawText('Try Clicking!', 10, 280, textPaint);
+  canvas.drawText('Try Clicking!', 10, 280, textFont, textPaint);
 
   surface.flush();
 
@@ -97,6 +98,7 @@
   textPaint.delete();
   paint.delete();
   roboto.delete();
+  textFont.delete();
 
   surface.dispose();
 }
diff --git a/experimental/canvaskit/canvaskit_bindings.cpp b/experimental/canvaskit/canvaskit_bindings.cpp
index df90f14..27edd7d 100644
--- a/experimental/canvaskit/canvaskit_bindings.cpp
+++ b/experimental/canvaskit/canvaskit_bindings.cpp
@@ -44,6 +44,7 @@
 #include "SkStrokeRec.h"
 #include "SkSurface.h"
 #include "SkSurfaceProps.h"
+#include "SkTextBlob.h"
 #include "SkTrimPathEffect.h"
 #include "SkTypeface.h"
 #include "SkTypes.h"
@@ -479,6 +480,10 @@
         template<>
         void raw_destructor<SkVertices>(SkVertices *ptr) {
         }
+
+        template<>
+        void raw_destructor<SkTextBlob>(SkTextBlob *ptr) {
+        }
     }
 }
 
@@ -685,13 +690,15 @@
             SkShadowUtils::DrawShadow(&self, path, zPlaneParams, lightPos, lightRadius,
                                       SkColor(ambientColor), SkColor(spotColor), flags);
         }))
-        .function("drawText", optional_override([](SkCanvas& self, std::string text, SkScalar x,
-                                                   SkScalar y, const SkPaint& p) {
-            // TODO(kjlubick): This does not work well for non-ascii
-            // Need to maybe add a helper in interface.js that supports UTF-8
-            // Otherwise, go with std::wstring and set UTF-32 encoding.
-            self.drawText(text.c_str(), text.length(), x, y, p);
+        .function("_drawSimpleText", optional_override([](SkCanvas& self, uintptr_t /* char* */ sptr,
+                                                          size_t len, SkScalar x, SkScalar y, const SkFont& font,
+                                                          const SkPaint& paint) {
+            // See comment above for uintptr_t explanation
+            const char* str = reinterpret_cast<const char*>(sptr);
+
+            self.drawSimpleText(str, len, SkTextEncoding::kUTF8, x, y, font, paint);
         }))
+        .function("drawTextBlob", select_overload<void (const sk_sp<SkTextBlob>&, SkScalar, SkScalar, const SkPaint&)>(&SkCanvas::drawTextBlob))
         .function("drawVertices", select_overload<void (const sk_sp<SkVertices>&, SkBlendMode, const SkPaint&)>(&SkCanvas::drawVertices))
         .function("flush", &SkCanvas::flush)
         .function("getTotalMatrix", optional_override([](const SkCanvas& self)->SimpleMatrix {
@@ -798,7 +805,6 @@
         .function("getStrokeJoin", &SkPaint::getStrokeJoin)
         .function("getStrokeMiter", &SkPaint::getStrokeMiter)
         .function("getStrokeWidth", &SkPaint::getStrokeWidth)
-        .function("getTextSize", &SkPaint::getTextSize)
         .function("setAntiAlias", &SkPaint::setAntiAlias)
         .function("setBlendMode", &SkPaint::setBlendMode)
         .function("setColor", optional_override([](SkPaint& self, JSColor color)->void {
@@ -814,9 +820,7 @@
         .function("setStrokeJoin", &SkPaint::setStrokeJoin)
         .function("setStrokeMiter", &SkPaint::setStrokeMiter)
         .function("setStrokeWidth", &SkPaint::setStrokeWidth)
-        .function("setStyle", &SkPaint::setStyle)
-        .function("setTypeface", &SkPaint::setTypeface)
-        .function("setTextSize", &SkPaint::setTextSize);
+        .function("setStyle", &SkPaint::setStyle);
 
     class_<SkPathEffect>("SkPathEffect")
         .smart_ptr<sk_sp<SkPathEffect>>("sk_sp<SkPathEffect>");
@@ -886,6 +890,17 @@
         .function("makeImageSnapshot", select_overload<sk_sp<SkImage>(const SkIRect& bounds)>(&SkSurface::makeImageSnapshot))
         .function("getCanvas", &SkSurface::getCanvas, allow_raw_pointers());
 
+    class_<SkTextBlob>("SkTextBlob")
+        .smart_ptr<sk_sp<SkTextBlob>>("sk_sp<SkTextBlob>>")
+        .class_function("_MakeFromText",optional_override([](uintptr_t /* char* */ sptr,
+                                                             size_t len, const SkFont& font,
+                                                             SkTextEncoding encoding)->sk_sp<SkTextBlob> {
+            // See comment above for uintptr_t explanation
+            const char* str = reinterpret_cast<const char*>(sptr);
+            return SkTextBlob::MakeFromText(str, len, font, encoding);
+        }), allow_raw_pointers());
+
+
     class_<SkTypeface>("SkTypeface")
         .smart_ptr<sk_sp<SkTypeface>>("sk_sp<SkTypeface>");
 
@@ -1006,12 +1021,11 @@
         .value("Round", SkPaint::Join::kRound_Join)
         .value("Bevel", SkPaint::Join::kBevel_Join);
 
-    value_object<StrokeOpts>("StrokeOpts")
-        .field("width",       &StrokeOpts::width)
-        .field("miter_limit", &StrokeOpts::miter_limit)
-        .field("join",        &StrokeOpts::join)
-        .field("cap",         &StrokeOpts::cap)
-        .field("precision",   &StrokeOpts::precision);
+    enum_<SkTextEncoding>("TextEncoding")
+        .value("UTF8",    SkTextEncoding::kUTF8)
+        .value("UTF16",   SkTextEncoding::kUTF16)
+        .value("UTF32",   SkTextEncoding::kUTF32)
+        .value("GlyphID", SkTextEncoding::kGlyphID);
 
     enum_<SkShader::TileMode>("TileMode")
         .value("Clamp",    SkShader::TileMode::kClamp_TileMode)
@@ -1067,6 +1081,13 @@
         .field("w",   &SkISize::fWidth)
         .field("h",   &SkISize::fHeight);
 
+    value_object<StrokeOpts>("StrokeOpts")
+        .field("width",       &StrokeOpts::width)
+        .field("miter_limit", &StrokeOpts::miter_limit)
+        .field("join",        &StrokeOpts::join)
+        .field("cap",         &StrokeOpts::cap)
+        .field("precision",   &StrokeOpts::precision);
+
     // Allows clients to supply a 1D array of 9 elements and the bindings
     // will automatically turn it into a 3x3 2D matrix.
     // e.g. path.transform([0,1,2,3,4,5,6,7,8])
diff --git a/experimental/canvaskit/externs.js b/experimental/canvaskit/externs.js
index e445798..eda3fad 100644
--- a/experimental/canvaskit/externs.js
+++ b/experimental/canvaskit/externs.js
@@ -94,6 +94,7 @@
 		drawRoundRect: function() {},
 		drawShadow: function() {},
 		drawText: function() {},
+		drawTextBlob: function() {},
 		drawVertices: function() {},
 		flush: function() {},
 		getTotalMatrix: function() {},
@@ -105,6 +106,7 @@
 		translate: function() {},
 
 		// private API
+		_drawSimpleText: function() {},
 		_readPixels: function() {},
 		_writePixels: function() {},
 		delete: function() {},
@@ -162,7 +164,6 @@
 		getStrokeJoin: function() {},
 		getStrokeMiter: function() {},
 		getStrokeWidth: function() {},
-		getTextSize: function() {},
 		setAntiAlias: function() {},
 		setBlendMode: function() {},
 		setColor: function() {},
@@ -175,8 +176,6 @@
 		setStrokeMiter: function() {},
 		setStrokeWidth: function() {},
 		setStyle: function() {},
-		setTextSize: function() {},
-		setTypeface: function() {},
 
 		//private API
 		delete: function() {},
@@ -246,6 +245,11 @@
 		delete: function() {},
 	},
 
+	SkTextBlob: {
+		MakeFromText: function() {},
+		_MakeFromText: function() {},
+	},
+
 	SkVertices: {
 		// public API (from C++ bindings)
 		bounds: function() {},
@@ -386,6 +390,13 @@
 		Bevel: {},
 	},
 
+	TextEncoding: {
+		UTF8: {},
+		UTF16: {},
+		UTF32: {},
+		GlyphID: {},
+	},
+
 	TileMode: {
 		Clamp: {},
 		Repeat: {},
@@ -458,6 +469,7 @@
 
 CanvasKit.SkImage.prototype.encodeToData = function() {};
 
+CanvasKit.SkCanvas.prototype.drawText = function() {};
 /** @return {Uint8Array} */
 CanvasKit.SkCanvas.prototype.readPixels = function() {};
 CanvasKit.SkCanvas.prototype.writePixels = function() {};
diff --git a/experimental/canvaskit/htmlcanvas/canvas2dcontext.js b/experimental/canvaskit/htmlcanvas/canvas2dcontext.js
index 3d79f19..350ff30 100644
--- a/experimental/canvaskit/htmlcanvas/canvas2dcontext.js
+++ b/experimental/canvaskit/htmlcanvas/canvas2dcontext.js
@@ -6,11 +6,9 @@
   this._paint.setStrokeMiter(10);
   this._paint.setStrokeCap(CanvasKit.StrokeCap.Butt);
   this._paint.setStrokeJoin(CanvasKit.StrokeJoin.Miter);
-  this._paint.setTextSize(10);
-  this._paint.setTypeface(null);
   this._fontString = '10px monospace';
 
-  this._font = new CanvasKit.SkFont();
+  this._font = new CanvasKit.SkFont(null, 10);
 
   this._strokeStyle    = CanvasKit.BLACK;
   this._fillStyle      = CanvasKit.BLACK;
@@ -106,8 +104,6 @@
         // tf is a "dict" according to closure, that is, the field
         // names are not minified. Thus, we need to access it via
         // bracket notation to tell closure not to minify these names.
-        this._paint.setTextSize(tf['sizePx']);
-        this._paint.setTypeface(tf['typeface']);
         this._font.setSize(tf['sizePx']);
         this._font.setTypeface(tf['typeface']);
         this._fontString = newFont;
@@ -694,16 +690,19 @@
 
   this.fillText = function(text, x, y, maxWidth) {
     // TODO do something with maxWidth, probably involving measure
-    var fillPaint = this._fillPaint()
+    var fillPaint = this._fillPaint();
+    var blob = CanvasKit.SkTextBlob.MakeFromText(text, this._font);
+
     var shadowPaint = this._shadowPaint(fillPaint);
     if (shadowPaint) {
       this._canvas.save();
       this._canvas.concat(this._shadowOffsetMatrix());
-      this._canvas.drawText(text, x, y, shadowPaint);
+      this._canvas.drawTextBlob(blob, x, y, shadowPaint);
       this._canvas.restore();
       shadowPaint.dispose();
     }
-    this._canvas.drawText(text, x, y, fillPaint);
+    this._canvas.drawTextBlob(blob, x, y, fillPaint);
+    blob.delete();
     fillPaint.dispose();
   }
 
@@ -1085,16 +1084,18 @@
   this.strokeText = function(text, x, y, maxWidth) {
     // TODO do something with maxWidth, probably involving measure
     var strokePaint = this._strokePaint();
+    var blob = CanvasKit.SkTextBlob.MakeFromText(text, this._font);
 
     var shadowPaint = this._shadowPaint(strokePaint);
     if (shadowPaint) {
       this._canvas.save();
       this._canvas.concat(this._shadowOffsetMatrix());
-      this._canvas.drawText(text, x, y, shadowPaint);
+      this._canvas.drawTextBlob(blob, x, y, shadowPaint);
       this._canvas.restore();
       shadowPaint.dispose();
     }
-    this._canvas.drawText(text, x, y, strokePaint);
+    this._canvas.drawTextBlob(blob, x, y, strokePaint);
+    blob.delete();
     strokePaint.dispose();
   }
 
diff --git a/experimental/canvaskit/interface.js b/experimental/canvaskit/interface.js
index fbee8f4..fe0d6be 100644
--- a/experimental/canvaskit/interface.js
+++ b/experimental/canvaskit/interface.js
@@ -382,6 +382,17 @@
       throw 'encodeToData expected to take 0 or 2 arguments. Got ' + arguments.length;
     }
 
+    CanvasKit.SkCanvas.prototype.drawText = function(str, x, y, font, paint) {
+      // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
+      // JS.  See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
+      // Add 1 for null terminator
+      var strLen = lengthBytesUTF8(str) + 1;
+      var strPtr = CanvasKit._malloc(strLen);
+      // Add 1 for the null terminator.
+      stringToUTF8(str, strPtr, strLen);
+      this._drawSimpleText(strPtr, strLen, x, y, font, paint);
+    }
+
     // returns Uint8Array
     CanvasKit.SkCanvas.prototype.readPixels = function(x, y, w, h, alphaType,
                                                        colorType, dstRowBytes) {
@@ -453,6 +464,29 @@
       return font;
     }
 
+    CanvasKit.SkTextBlob.MakeFromText = function(str, font) {
+      // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten
+      // JS.  See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8
+      // Add 1 for null terminator
+      var strLen = lengthBytesUTF8(str) + 1;
+      var strPtr = CanvasKit._malloc(strLen);
+      // Add 1 for the null terminator.
+      stringToUTF8(str, strPtr, strLen);
+
+      var blob = CanvasKit.SkTextBlob._MakeFromText(strPtr, strLen - 1, font, CanvasKit.TextEncoding.UTF8);
+      if (!blob) {
+        SkDebug('Could not make textblob from string "' + str + '"');
+        return null;
+      }
+
+      var origDelete = blob.delete.bind(blob);
+      blob.delete = function() {
+        CanvasKit._free(strPtr);
+        origDelete();
+      }
+      return blob;
+    }
+
     // Run through the JS files that are added at compile time.
     if (CanvasKit._extraInitializations) {
       CanvasKit._extraInitializations.forEach(function(init) {
diff --git a/experimental/canvaskit/tests/path.spec.js b/experimental/canvaskit/tests/path.spec.js
index b073670..4a3e455 100644
--- a/experimental/canvaskit/tests/path.spec.js
+++ b/experimental/canvaskit/tests/path.spec.js
@@ -133,13 +133,15 @@
 
             canvas.drawArc(CanvasKit.LTRBRect(55, 35, 95, 80), 15, 270, true, paint);
 
-            paint.setTextSize(20);
-            canvas.drawText('this is ascii text', 5, 100, paint);
+            const font = new CanvasKit.SkFont(null, 20);
+            canvas.drawText('this is ascii text', 5, 100, font, paint);
 
-            canvas.drawText('Unicode chars 💩 é É Øµ', 5, 130, paint);
+            const blob = CanvasKit.SkTextBlob.MakeFromText('Unicode chars 💩 é É Øµ', font);
+            canvas.drawTextBlob(blob, 5, 130, paint);
 
             surface.flush();
-
+            font.delete();
+            blob.delete();
             paint.delete();
 
             reportSurface(surface, 'canvas_api_example', done);
@@ -172,9 +174,10 @@
 
             const textPaint = new CanvasKit.SkPaint();
             textPaint.setColor(CanvasKit.Color(40, 0, 0, 1.0));
-            textPaint.setTextSize(30);
             textPaint.setAntiAlias(true);
 
+            const textFont = new CanvasKit.SkFont(null, 30);
+
             const dpe = CanvasKit.MakeSkDashPathEffect([15, 5, 5, 10], 1);
 
             paint.setPathEffect(dpe);
@@ -186,7 +189,7 @@
             canvas.clear(CanvasKit.Color(255, 255, 255, 1.0));
 
             canvas.drawPath(path, paint);
-            canvas.drawText('This is text', 10, 280, textPaint);
+            canvas.drawText('This is text', 10, 280, textFont, textPaint);
             surface.flush();
             dpe.delete();
             path.delete();