[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();