[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>
This commit is contained in:
Kevin Lubick 2019-01-14 08:36:08 -05:00
parent 9f810c2da4
commit ec4903d515
8 changed files with 135 additions and 45 deletions

View File

@ -11,6 +11,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `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.

View File

@ -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>

View File

@ -41,9 +41,10 @@ CanvasKitInit({
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 @@ function fancyAPI(CanvasKit) {
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 @@ function fancyAPI(CanvasKit) {
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 @@ function fancyAPI(CanvasKit) {
textPaint.delete();
paint.delete();
roboto.delete();
textFont.delete();
surface.dispose();
}

View File

@ -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 @@ namespace emscripten {
template<>
void raw_destructor<SkVertices>(SkVertices *ptr) {
}
template<>
void raw_destructor<SkTextBlob>(SkTextBlob *ptr) {
}
}
}
@ -685,13 +690,15 @@ EMSCRIPTEN_BINDINGS(Skia) {
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 @@ EMSCRIPTEN_BINDINGS(Skia) {
.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 @@ EMSCRIPTEN_BINDINGS(Skia) {
.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 @@ EMSCRIPTEN_BINDINGS(Skia) {
.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 @@ EMSCRIPTEN_BINDINGS(Skia) {
.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 @@ EMSCRIPTEN_BINDINGS(Skia) {
.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])

View File

@ -94,6 +94,7 @@ var CanvasKit = {
drawRoundRect: function() {},
drawShadow: function() {},
drawText: function() {},
drawTextBlob: function() {},
drawVertices: function() {},
flush: function() {},
getTotalMatrix: function() {},
@ -105,6 +106,7 @@ var CanvasKit = {
translate: function() {},
// private API
_drawSimpleText: function() {},
_readPixels: function() {},
_writePixels: function() {},
delete: function() {},
@ -162,7 +164,6 @@ var CanvasKit = {
getStrokeJoin: function() {},
getStrokeMiter: function() {},
getStrokeWidth: function() {},
getTextSize: function() {},
setAntiAlias: function() {},
setBlendMode: function() {},
setColor: function() {},
@ -175,8 +176,6 @@ var CanvasKit = {
setStrokeMiter: function() {},
setStrokeWidth: function() {},
setStyle: function() {},
setTextSize: function() {},
setTypeface: function() {},
//private API
delete: function() {},
@ -246,6 +245,11 @@ var CanvasKit = {
delete: function() {},
},
SkTextBlob: {
MakeFromText: function() {},
_MakeFromText: function() {},
},
SkVertices: {
// public API (from C++ bindings)
bounds: function() {},
@ -386,6 +390,13 @@ var CanvasKit = {
Bevel: {},
},
TextEncoding: {
UTF8: {},
UTF16: {},
UTF32: {},
GlyphID: {},
},
TileMode: {
Clamp: {},
Repeat: {},
@ -458,6 +469,7 @@ CanvasKit.SkVertices.prototype.applyBones = function() {};
CanvasKit.SkImage.prototype.encodeToData = function() {};
CanvasKit.SkCanvas.prototype.drawText = function() {};
/** @return {Uint8Array} */
CanvasKit.SkCanvas.prototype.readPixels = function() {};
CanvasKit.SkCanvas.prototype.writePixels = function() {};

View File

@ -6,11 +6,9 @@ function CanvasRenderingContext2D(skcanvas) {
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 @@ function CanvasRenderingContext2D(skcanvas) {
// 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 @@ function CanvasRenderingContext2D(skcanvas) {
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 @@ function CanvasRenderingContext2D(skcanvas) {
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();
}

View File

@ -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) {

View File

@ -133,13 +133,15 @@ describe('CanvasKit\'s Path Behavior', function() {
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 @@ describe('CanvasKit\'s Path Behavior', function() {
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 @@ describe('CanvasKit\'s Path Behavior', function() {
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();