[canvaskit] Expand canvas2d API
Made addPath take one more arg to allow for append/expand
(which makes emulating the HTML canvas easier).
Add Gold test for various lineTo/pathTo, etc.
Make CanvasKit.Color() choose a better value for alpha
when omitted (was 0, should be 1).
Add some parsing logic to deal with colors/font sizes.
Fonts are going to be rather complex it seems.
Moves some arc-related logic to the JS side, (although
this should preserve the behavior of CanvasKit.arc() to
behave like the Canvas implementation)
Make Examples and tests to a side-by-side comparison to
HTML canvas where applicable.
Add a Changelog for PathKit. There was a bug (I thought), but
turns out I was wrong. The Changelog will be for future
bug fixes.
Bug: skia:
Change-Id: I1bd603fdb518232604b098e24543e3453015b504
Reviewed-on: https://skia-review.googlesource.com/c/170446
Reviewed-by: Kevin Lubick <kjlubick@google.com>
diff --git a/experimental/canvaskit/interface.js b/experimental/canvaskit/interface.js
index b671369..9106ae7 100644
--- a/experimental/canvaskit/interface.js
+++ b/experimental/canvaskit/interface.js
@@ -7,41 +7,114 @@
// after onRuntimeInitialized, otherwise, it can happen outside of that scope.
CanvasKit.onRuntimeInitialized = function() {
// All calls to 'this' need to go in externs.js so closure doesn't minify them away.
+
+
+ // Add some helpers for matrices. This is ported from SkMatrix.cpp
+ // to save complexity and overhead of going back and forth between
+ // C++ and JS layers.
+ CanvasKit.SkMatrix = {};
+ function sdot(a, b, c, d, e, f) {
+ e = e || 0;
+ f = f || 0;
+ return a * b + c * d + e * f;
+ }
+
+ // Return a matrix representing a rotation by n degrees.
+ // px, py optionally say which point the rotation should be around
+ // with the default being (0, 0);
+ CanvasKit.SkMatrix.rotated = function(degrees, px, py) {
+ px = px || 0;
+ py = py || 0;
+ var rad = degreesToRadians(degrees);
+ var sinV = Math.sin(rad);
+ var cosV = Math.cos(rad);
+ return [
+ cosV, -sinV, sdot( sinV, py, 1 - cosV, px),
+ sinV, cosV, sdot(-sinV, px, 1 - cosV, py),
+ 0, 0, 1,
+ ];
+ };
+ // TODO(kjlubick): translated, scaled
+
+
+ CanvasKit.SkPath.prototype.addArc = function(oval, startAngle, sweepAngle) {
+ // see arc() for the HTMLCanvas version
+ // note input angles are degrees.
+ this._addArc(oval, startAngle, sweepAngle);
+ return this;
+ };
+
CanvasKit.SkPath.prototype.addPath = function() {
- // Takes 1, 2, or 10 args, where the first arg is always the path.
+ // Takes 1, 2, 7, or 10 required args, where the first arg is always the path.
+ // The last arg is optional and chooses between add or extend mode.
// The options for the remaining args are:
- // - an array of 9 parameters
- // - the 9 parameters of a full matrix
- // an array of 6 parameters (omitting perspective)
+ // - an array of 6 or 9 parameters (perspective is optional)
+ // - the 9 parameters of a full matrix or
// the 6 non-perspective params of a matrix.
- if (arguments.length === 1) {
- // Add path, unchanged. Use identify matrix
- this._addPath(arguments[0], 1, 0, 0,
- 0, 1, 0,
- 0, 0, 1);
- } else if (arguments.length === 2) {
+ var args = Array.prototype.slice.call(arguments);
+ var path = args[0];
+ var extend = false;
+ if (typeof args[args.length-1] === "boolean") {
+ extend = args.pop();
+ }
+ if (args.length === 1) {
+ // Add path, unchanged. Use identity matrix
+ this._addPath(path, 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1,
+ extend);
+ } else if (args.length === 2) {
// User provided the 9 params of a full matrix as an array.
- var sm = arguments[1];
- this._addPath(arguments[0], a[1], a[2], a[3],
- a[4], a[5], a[6],
- a[7] || 0, a[8] || 0, a[9] || 1);
- } else if (arguments.length === 7 || arguments.length === 10) {
+ var a = args[1];
+ this._addPath(path, a[0], a[1], a[2],
+ a[3], a[4], a[5],
+ a[6] || 0, a[7] || 0, a[8] || 1,
+ extend);
+ } else if (args.length === 7 || args.length === 10) {
// User provided the 9 params of a (full) matrix directly.
// (or just the 6 non perspective ones)
// These are in the same order as what Skia expects.
- var a = arguments;
- this._addPath(arguments[0], a[1], a[2], a[3],
- a[4], a[5], a[6],
- a[7] || 0, a[8] || 0, a[9] || 1);
+ var a = args;
+ this._addPath(path, a[1], a[2], a[3],
+ a[4], a[5], a[6],
+ a[7] || 0, a[8] || 0, a[9] || 1,
+ extend);
} else {
- console.err('addPath expected to take 1, 2, 7, or 10 args. Got ' + arguments.length);
+ console.error('addPath expected to take 1, 2, 7, or 10 required args. Got ' + args.length);
+ return null;
+ }
+ return this;
+ };
+
+ CanvasKit.SkPath.prototype.addRect = function() {
+ // Takes 1, 2, 4 or 5 args
+ // - SkRect
+ // - SkRect, isCCW
+ // - left, top, right, bottom
+ // - left, top, right, bottom, isCCW
+ if (arguments.length === 1 || arguments.length === 2) {
+ var r = arguments[0];
+ var ccw = arguments[1] || false;
+ this._addRect(r.fLeft, r.fTop, r.fRight, r.fBottom, ccw);
+ } else if (arguments.length === 4 || arguments.length === 5) {
+ var a = arguments;
+ this._addRect(a[0], a[1], a[2], a[3], a[4] || false);
+ } else {
+ console.error('addRect expected to take 1, 2, 4, or 5 args. Got ' + arguments.length);
return null;
}
return this;
};
CanvasKit.SkPath.prototype.arc = function(x, y, radius, startAngle, endAngle, ccw) {
- this._arc(x, y, radius, startAngle, endAngle, !!ccw);
+ // emulates the HTMLCanvas behavior. See addArc() for the SkPath version.
+ // Note input angles are radians.
+ var bounds = CanvasKit.LTRBRect(x-radius, y-radius, x+radius, y+radius);
+ var sweep = radiansToDegrees(endAngle - startAngle) - (360 * !!ccw);
+ var temp = new CanvasKit.SkPath();
+ temp.addArc(bounds, radiansToDegrees(startAngle), sweep);
+ this.addPath(temp, true);
+ temp.delete();
return this;
};
@@ -179,7 +252,6 @@
}
} // end CanvasKit.onRuntimeInitialized, that is, anything changing prototypes or dynamic.
- // Likely only used for tests.
CanvasKit.LTRBRect = function(l, t, r, b) {
return {
fLeft: l,
@@ -356,3 +428,14 @@
}
}(Module)); // When this file is loaded in, the high level object is "Module";
+
+// Intentionally added outside the scope to allow usage in canvas2d.js and other
+// pre-js files. These names are unlikely to cause emscripten collisions.
+function radiansToDegrees(rad) {
+ return (rad / Math.PI) * 180;
+}
+
+function degreesToRadians(deg) {
+ return (deg / 180) * Math.PI;
+}
+