[canvaskit] Add Path2D to Canvas API
This moves the shared logic into path2d.js from
canvas2dcontext.js.
Bug: skia:
Change-Id: Id63bc52a190109f7cbc4e17ddb5603e6e87d5dc0
Reviewed-on: https://skia-review.googlesource.com/c/178268
Reviewed-by: Joe Gregorio <jcgregorio@google.com>
diff --git a/experimental/canvaskit/canvaskit/example.html b/experimental/canvaskit/canvaskit/example.html
index 7c8416d..9a79935 100644
--- a/experimental/canvaskit/canvaskit/example.html
+++ b/experimental/canvaskit/canvaskit/example.html
@@ -497,6 +497,10 @@
realCanvas.width = 300;
realCanvas.height = 300;
+ // svg data for a clock
+ skcanvas._path = skcanvas.makePath2D('M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z');
+ realCanvas._path = new Path2D('M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z');
+
for (let canvas of [skcanvas, realCanvas]) {
let ctx = canvas.getContext('2d');
ctx.scale(1.5, 1.5);
@@ -530,6 +534,9 @@
ctx.lineWidth = 4/3;
ctx.stroke();
+ // make a clock
+ ctx.stroke(canvas._path);
+
// Test edgecases and draw direction
ctx.beginPath();
ctx.arc(50, 100, 10, Math.PI, -Math.PI/2);
diff --git a/experimental/canvaskit/canvaskit_bindings.cpp b/experimental/canvaskit/canvaskit_bindings.cpp
index 02616e7..1618d48 100644
--- a/experimental/canvaskit/canvaskit_bindings.cpp
+++ b/experimental/canvaskit/canvaskit_bindings.cpp
@@ -344,6 +344,14 @@
return emscripten::val(s.c_str());
}
+SkPathOrNull EMSCRIPTEN_KEEPALIVE MakePathFromSVGString(std::string str) {
+ SkPath path;
+ if (SkParsePath::FromSVGString(str.c_str(), &path)) {
+ return emscripten::val(path);
+ }
+ return emscripten::val::null();
+}
+
SkPathOrNull EMSCRIPTEN_KEEPALIVE MakePathFromOp(const SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
SkPath out;
if (Op(pathOne, pathTwo, op, &out)) {
@@ -486,6 +494,7 @@
return SkMaskFilter::MakeBlur(style, sigma, respectCTM);
}), allow_raw_pointers());
function("MakePathFromOp", &MakePathFromOp);
+ function("MakePathFromSVGString", &MakePathFromSVGString);
// These won't be called directly, there's a JS helper to deal with typed arrays.
function("_MakeSkDashPathEffect", optional_override([](uintptr_t /* float* */ cptr, int count, SkScalar phase)->sk_sp<SkPathEffect> {
diff --git a/experimental/canvaskit/compile.sh b/experimental/canvaskit/compile.sh
index c76d5de..327afd3 100755
--- a/experimental/canvaskit/compile.sh
+++ b/experimental/canvaskit/compile.sh
@@ -90,6 +90,7 @@
--pre-js $BASE_DIR/htmlcanvas/htmlcanvas.js \
--pre-js $BASE_DIR/htmlcanvas/imagedata.js \
--pre-js $BASE_DIR/htmlcanvas/lineargradient.js \
+--pre-js $BASE_DIR/htmlcanvas/path2d.js \
--pre-js $BASE_DIR/htmlcanvas/pattern.js \
--pre-js $BASE_DIR/htmlcanvas/radialgradient.js \
--pre-js $BASE_DIR/htmlcanvas/postamble.js "
diff --git a/experimental/canvaskit/externs.js b/experimental/canvaskit/externs.js
index 56d2669..f4882c3 100644
--- a/experimental/canvaskit/externs.js
+++ b/experimental/canvaskit/externs.js
@@ -39,6 +39,8 @@
MakeImageFromEncoded: function() {},
/** @return {LinearCanvasGradient} */
MakeLinearGradientShader: function() {},
+ MakePathFromOp: function() {},
+ MakePathFromSVGString: function() {},
MakeRadialGradientShader: function() {},
MakeSWCanvasSurface: function() {},
MakeSkDashPathEffect: function() {},
@@ -447,6 +449,7 @@
HTMLCanvas.prototype.dispose = function() {};
HTMLCanvas.prototype.getContext = function() {};
HTMLCanvas.prototype.loadFont = function() {};
+HTMLCanvas.prototype.makePath2D = function() {};
HTMLCanvas.prototype.toDataURL = function() {};
var CanvasRenderingContext2D = {};
@@ -494,6 +497,18 @@
CanvasRenderingContext2D.prototype.transform = function() {};
CanvasRenderingContext2D.prototype.translate = function() {};
+var Path2D = {};
+Path2D.prototype.addPath = function() {};
+Path2D.prototype.arc = function() {};
+Path2D.prototype.arcTo = function() {};
+Path2D.prototype.bezierCurveTo = function() {};
+Path2D.prototype.closePath = function() {};
+Path2D.prototype.ellipse = function() {};
+Path2D.prototype.lineTo = function() {};
+Path2D.prototype.moveTo = function() {};
+Path2D.prototype.quadraticCurveTo = function() {};
+Path2D.prototype.rect = function() {};
+
var LinearCanvasGradient = {};
LinearCanvasGradient.prototype.addColorStop = function() {};
var RadialCanvasGradient = {};
diff --git a/experimental/canvaskit/htmlcanvas/canvas2dcontext.js b/experimental/canvaskit/htmlcanvas/canvas2dcontext.js
index 5a07152..06e47dc 100644
--- a/experimental/canvaskit/htmlcanvas/canvas2dcontext.js
+++ b/experimental/canvaskit/htmlcanvas/canvas2dcontext.js
@@ -488,22 +488,11 @@
});
this.arc = function(x, y, radius, startAngle, endAngle, ccw) {
- // As per https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-arc
- // arc is essentially a simpler version of ellipse.
- this.ellipse(x, y, radius, radius, 0, startAngle, endAngle, ccw);
+ arc(this._currentPath, x, y, radius, startAngle, endAngle, ccw);
}
this.arcTo = function(x1, y1, x2, y2, radius) {
- if (!allAreFinite(arguments)) {
- return;
- }
- if (radius < 0) {
- throw 'radii cannot be negative';
- }
- if (this._currentPath.isEmpty()) {
- this.moveTo(x1, y1);
- }
- this._currentPath.arcTo(x1, y1, x2, y2, radius);
+ arcTo(this._currentPath, x1, y1, x2, y2, radius);
}
// As per the spec this doesn't begin any paths, it only
@@ -514,13 +503,7 @@
}
this.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
- if (!allAreFinite(arguments)) {
- return;
- }
- if (this._currentPath.isEmpty()) {
- this.moveTo(cp1x, cp1y);
- }
- this._currentPath.cubicTo(cp1x, cp1y, cp2x, cp2y, x, y);
+ bezierCurveTo(this._currentPath, cp1x, cp1y, cp2x, cp2y, x, y);
}
this.clearRect = function(x, y, width, height) {
@@ -530,25 +513,30 @@
this._paint.setBlendMode(this._globalCompositeOperation);
}
- this.clip = function(fillRule) {
- var clip = this._currentPath.copy();
+ this.clip = function(path, fillRule) {
+ if (typeof path === 'string') {
+ // shift the args if a Path2D is supplied
+ fillRule = path;
+ path = this._currentPath;
+ } else if (path && path._getPath) {
+ path = path._getPath();
+ }
+ if (!path) {
+ path = this._currentPath;
+ }
+
+ var clip = path.copy();
if (fillRule && fillRule.toLowerCase() === 'evenodd') {
clip.setFillType(CanvasKit.FillType.EvenOdd);
} else {
clip.setFillType(CanvasKit.FillType.Winding);
}
this._canvas.clipPath(clip, CanvasKit.ClipOp.Intersect, true);
+ clip.delete();
}
this.closePath = function() {
- if (this._currentPath.isEmpty()) {
- return;
- }
- // Check to see if we are not just a single point
- var bounds = this._currentPath.getBounds();
- if ((bounds.fBottom - bounds.fTop) || (bounds.fRight - bounds.fLeft)) {
- this._currentPath.close();
- }
+ closePath(this._currentPath);
}
this.createImageData = function() {
@@ -630,69 +618,10 @@
iPaint.dispose();
}
- this._ellipseHelper = function(x, y, radiusX, radiusY, startAngle, endAngle) {
- var sweepDegrees = radiansToDegrees(endAngle - startAngle);
- var startDegrees = radiansToDegrees(startAngle);
-
- var oval = CanvasKit.LTRBRect(x - radiusX, y - radiusY, x + radiusX, y + radiusY);
-
- // draw in 2 180 degree segments because trying to draw all 360 degrees at once
- // draws nothing.
- if (almostEqual(Math.abs(sweepDegrees), 360)) {
- var halfSweep = sweepDegrees/2;
- this._currentPath.arcTo(oval, startDegrees, halfSweep, false);
- this._currentPath.arcTo(oval, startDegrees + halfSweep, halfSweep, false);
- return;
- }
- this._currentPath.arcTo(oval, startDegrees, sweepDegrees, false);
- }
-
this.ellipse = function(x, y, radiusX, radiusY, rotation,
startAngle, endAngle, ccw) {
- if (!allAreFinite([x, y, radiusX, radiusY, rotation, startAngle, endAngle])) {
- return;
- }
- if (radiusX < 0 || radiusY < 0) {
- throw 'radii cannot be negative';
- }
-
- // based off of CanonicalizeAngle in Chrome
- var tao = 2 * Math.PI;
- var newStartAngle = startAngle % tao;
- if (newStartAngle < 0) {
- newStartAngle += tao;
- }
- var delta = newStartAngle - startAngle;
- startAngle = newStartAngle;
- endAngle += delta;
-
- // Based off of AdjustEndAngle in Chrome.
- if (!ccw && (endAngle - startAngle) >= tao) {
- // Draw complete ellipse
- endAngle = startAngle + tao;
- } else if (ccw && (startAngle - endAngle) >= tao) {
- // Draw complete ellipse
- endAngle = startAngle - tao;
- } else if (!ccw && startAngle > endAngle) {
- endAngle = startAngle + (tao - (startAngle - endAngle) % tao);
- } else if (ccw && startAngle < endAngle) {
- endAngle = startAngle - (tao - (endAngle - startAngle) % tao);
- }
-
-
- // Based off of Chrome's implementation in
- // https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/path.cc
- // of note, can't use addArc or addOval because they close the arc, which
- // the spec says not to do (unless the user explicitly calls closePath).
- // This throws off points being in/out of the arc.
- if (!rotation) {
- this._ellipseHelper(x, y, radiusX, radiusY, startAngle, endAngle);
- return;
- }
- var rotated = CanvasKit.SkMatrix.rotated(rotation, x, y);
- this._currentPath.transform(CanvasKit.SkMatrix.invert(rotated));
- this._ellipseHelper(x, y, radiusX, radiusY, startAngle, endAngle);
- this._currentPath.transform(rotated);
+ ellipse(this._currentPath, x, y, radiusX, radiusY, rotation,
+ startAngle, endAngle, ccw);
}
// A helper to copy the current paint, ready for filling
@@ -719,7 +648,14 @@
return paint;
}
- this.fill = function(fillRule) {
+ this.fill = function(path, fillRule) {
+ if (typeof path === 'string') {
+ // shift the args if a Path2D is supplied
+ fillRule = path;
+ path = this._currentPath;
+ } else if (path && path._getPath) {
+ path = path._getPath();
+ }
if (fillRule === 'evenodd') {
this._currentPath.setFillType(CanvasKit.FillType.EvenOdd);
} else if (fillRule === 'nonzero' || !fillRule) {
@@ -727,17 +663,21 @@
} else {
throw 'invalid fill rule';
}
+ if (!path) {
+ path = this._currentPath;
+ }
+
var fillPaint = this._fillPaint();
var shadowPaint = this._shadowPaint(fillPaint);
if (shadowPaint) {
this._canvas.save();
this._canvas.concat(this._shadowOffsetMatrix());
- this._canvas.drawPath(this._currentPath, shadowPaint);
+ this._canvas.drawPath(path, shadowPaint);
this._canvas.restore();
shadowPaint.dispose();
}
- this._canvas.drawPath(this._currentPath, fillPaint);
+ this._canvas.drawPath(path, fillPaint);
fillPaint.dispose();
}
@@ -785,6 +725,17 @@
}
this.isPointInPath = function(x, y, fillmode) {
+ var args = arguments;
+ if (args.length === 3) {
+ var path = this._currentPath;
+ } else if (args.length === 4) {
+ var path = args[0];
+ x = args[1];
+ y = args[2];
+ fillmode = args[3];
+ } else {
+ throw 'invalid arg count, need 3 or 4, got ' + args.length;
+ }
if (!isFinite(x) || !isFinite(y)) {
return false;
}
@@ -796,20 +747,30 @@
var pts = this._mapToLocalCoordinates([x, y]);
x = pts[0];
y = pts[1];
- this._currentPath.setFillType(fillmode === 'nonzero' ?
+ path.setFillType(fillmode === 'nonzero' ?
CanvasKit.FillType.Winding :
CanvasKit.FillType.EvenOdd);
- return this._currentPath.contains(x, y);
+ return path.contains(x, y);
}
this.isPointInStroke = function(x, y) {
+ var args = arguments;
+ if (args.length === 2) {
+ var path = this._currentPath;
+ } else if (args.length === 3) {
+ var path = args[0];
+ x = args[1];
+ y = args[2];
+ } else {
+ throw 'invalid arg count, need 2 or 3, got ' + args.length;
+ }
if (!isFinite(x) || !isFinite(y)) {
return false;
}
var pts = this._mapToLocalCoordinates([x, y]);
x = pts[0];
y = pts[1];
- var temp = this._currentPath.copy();
+ var temp = path.copy();
// fillmode is always nonzero
temp.setFillType(CanvasKit.FillType.Winding);
temp.stroke({'width': this.lineWidth, 'miter_limit': this.miterLimit,
@@ -822,14 +783,7 @@
}
this.lineTo = function(x, y) {
- if (!allAreFinite(arguments)) {
- return;
- }
- // A lineTo without a previous point has a moveTo inserted before it
- if (this._currentPath.isEmpty()) {
- this._currentPath.moveTo(x, y);
- }
- this._currentPath.lineTo(x, y);
+ lineTo(this._currentPath, x, y);
}
this.measureText = function(text) {
@@ -840,10 +794,7 @@
}
this.moveTo = function(x, y) {
- if (!allAreFinite(arguments)) {
- return;
- }
- this._currentPath.moveTo(x, y);
+ moveTo(this._currentPath, x, y);
}
this.putImageData = function(imageData, x, y, dirtyX, dirtyY, dirtyWidth, dirtyHeight) {
@@ -895,21 +846,11 @@
}
this.quadraticCurveTo = function(cpx, cpy, x, y) {
- if (!allAreFinite(arguments)) {
- return;
- }
- if (this._currentPath.isEmpty()) {
- this._currentPath.moveTo(cpx, cpy);
- }
- this._currentPath.quadTo(cpx, cpy, x, y);
+ quadraticCurveTo(this._currentPath, cpx, cpy, x, y);
}
this.rect = function(x, y, width, height) {
- if (!allAreFinite(arguments)) {
- return;
- }
- // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-rect
- this._currentPath.addRect(x, y, x+width, y+height);
+ rect(this._currentPath, x, y, width, height);
}
this.resetTransform = function() {
@@ -1113,19 +1054,20 @@
return paint;
}
- this.stroke = function() {
+ this.stroke = function(path) {
+ path = path ? path._getPath() : this._currentPath;
var strokePaint = this._strokePaint();
var shadowPaint = this._shadowPaint(strokePaint);
if (shadowPaint) {
this._canvas.save();
this._canvas.concat(this._shadowOffsetMatrix());
- this._canvas.drawPath(this._currentPath, shadowPaint);
+ this._canvas.drawPath(path, shadowPaint);
this._canvas.restore();
shadowPaint.dispose();
}
- this._canvas.drawPath(this._currentPath, strokePaint);
+ this._canvas.drawPath(path, strokePaint);
strokePaint.dispose();
}
diff --git a/experimental/canvaskit/htmlcanvas/htmlcanvas.js b/experimental/canvaskit/htmlcanvas/htmlcanvas.js
index 6f118e5..de9794f 100644
--- a/experimental/canvaskit/htmlcanvas/htmlcanvas.js
+++ b/experimental/canvaskit/htmlcanvas/htmlcanvas.js
@@ -32,6 +32,12 @@
addToFontCache(newFont, descriptors);
}
+ this.makePath2D = function(path) {
+ var p2d = new Path2D(path);
+ this._toCleanup.push(p2d._getPath());
+ return p2d;
+ }
+
// A normal <canvas> requires that clients call getContext
this.getContext = function(type) {
if (type === '2d') {
diff --git a/experimental/canvaskit/htmlcanvas/path2d.js b/experimental/canvaskit/htmlcanvas/path2d.js
new file mode 100644
index 0000000..dd9c682
--- /dev/null
+++ b/experimental/canvaskit/htmlcanvas/path2d.js
@@ -0,0 +1,206 @@
+// CanvasPath methods, which all take an SkPath object as the first param
+
+function arc(skpath, x, y, radius, startAngle, endAngle, ccw) {
+ // As per https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-arc
+ // arc is essentially a simpler version of ellipse.
+ ellipse(skpath, x, y, radius, radius, 0, startAngle, endAngle, ccw);
+}
+
+function arcTo(skpath, x1, y1, x2, y2, radius) {
+ if (!allAreFinite([x1, y1, x2, y2, radius])) {
+ return;
+ }
+ if (radius < 0) {
+ throw 'radii cannot be negative';
+ }
+ if (skpath.isEmpty()) {
+ skpath.moveTo(x1, y1);
+ }
+ skpath.arcTo(x1, y1, x2, y2, radius);
+}
+
+function bezierCurveTo(skpath, cp1x, cp1y, cp2x, cp2y, x, y) {
+ if (!allAreFinite([cp1x, cp1y, cp2x, cp2y, x, y])) {
+ return;
+ }
+ if (skpath.isEmpty()) {
+ skpath.moveTo(cp1x, cp1y);
+ }
+ skpath.cubicTo(cp1x, cp1y, cp2x, cp2y, x, y);
+}
+
+function closePath(skpath) {
+ if (skpath.isEmpty()) {
+ return;
+ }
+ // Check to see if we are not just a single point
+ var bounds = skpath.getBounds();
+ if ((bounds.fBottom - bounds.fTop) || (bounds.fRight - bounds.fLeft)) {
+ skpath.close();
+ }
+}
+
+function _ellipseHelper(skpath, x, y, radiusX, radiusY, startAngle, endAngle) {
+ var sweepDegrees = radiansToDegrees(endAngle - startAngle);
+ var startDegrees = radiansToDegrees(startAngle);
+
+ var oval = CanvasKit.LTRBRect(x - radiusX, y - radiusY, x + radiusX, y + radiusY);
+
+ // draw in 2 180 degree segments because trying to draw all 360 degrees at once
+ // draws nothing.
+ if (almostEqual(Math.abs(sweepDegrees), 360)) {
+ var halfSweep = sweepDegrees/2;
+ skpath.arcTo(oval, startDegrees, halfSweep, false);
+ skpath.arcTo(oval, startDegrees + halfSweep, halfSweep, false);
+ return;
+ }
+ skpath.arcTo(oval, startDegrees, sweepDegrees, false);
+}
+
+function ellipse(skpath, x, y, radiusX, radiusY, rotation,
+ startAngle, endAngle, ccw) {
+ if (!allAreFinite([x, y, radiusX, radiusY, rotation, startAngle, endAngle])) {
+ return;
+ }
+ if (radiusX < 0 || radiusY < 0) {
+ throw 'radii cannot be negative';
+ }
+
+ // based off of CanonicalizeAngle in Chrome
+ var tao = 2 * Math.PI;
+ var newStartAngle = startAngle % tao;
+ if (newStartAngle < 0) {
+ newStartAngle += tao;
+ }
+ var delta = newStartAngle - startAngle;
+ startAngle = newStartAngle;
+ endAngle += delta;
+
+ // Based off of AdjustEndAngle in Chrome.
+ if (!ccw && (endAngle - startAngle) >= tao) {
+ // Draw complete ellipse
+ endAngle = startAngle + tao;
+ } else if (ccw && (startAngle - endAngle) >= tao) {
+ // Draw complete ellipse
+ endAngle = startAngle - tao;
+ } else if (!ccw && startAngle > endAngle) {
+ endAngle = startAngle + (tao - (startAngle - endAngle) % tao);
+ } else if (ccw && startAngle < endAngle) {
+ endAngle = startAngle - (tao - (endAngle - startAngle) % tao);
+ }
+
+ // Based off of Chrome's implementation in
+ // https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/path.cc
+ // of note, can't use addArc or addOval because they close the arc, which
+ // the spec says not to do (unless the user explicitly calls closePath).
+ // This throws off points being in/out of the arc.
+ if (!rotation) {
+ _ellipseHelper(skpath, x, y, radiusX, radiusY, startAngle, endAngle);
+ return;
+ }
+ var rotated = CanvasKit.SkMatrix.rotated(rotation, x, y);
+ var rotatedInvert = CanvasKit.SkMatrix.rotated(-rotation, x, y);
+ skpath.transform(rotatedInvert);
+ _ellipseHelper(skpath, x, y, radiusX, radiusY, startAngle, endAngle);
+ skpath.transform(rotated);
+}
+
+function lineTo(skpath, x, y) {
+ if (!allAreFinite([x, y])) {
+ return;
+ }
+ // A lineTo without a previous point has a moveTo inserted before it
+ if (skpath.isEmpty()) {
+ skpath.moveTo(x, y);
+ }
+ skpath.lineTo(x, y);
+}
+
+function moveTo(skpath, x, y) {
+ if (!allAreFinite([x, y])) {
+ return;
+ }
+ skpath.moveTo(x, y);
+}
+
+function quadraticCurveTo(skpath, cpx, cpy, x, y) {
+ if (!allAreFinite([cpx, cpy, x, y])) {
+ return;
+ }
+ if (skpath.isEmpty()) {
+ skpath.moveTo(cpx, cpy);
+ }
+ skpath.quadTo(cpx, cpy, x, y);
+}
+
+function rect(skpath, x, y, width, height) {
+ if (!allAreFinite([x, y, width, height])) {
+ return;
+ }
+ // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-rect
+ skpath.addRect(x, y, x+width, y+height);
+}
+
+function Path2D(path) {
+ this._path = null;
+ if (typeof path === 'string') {
+ this._path = CanvasKit.MakePathFromSVGString(path);
+ } else if (path && path._getPath) {
+ this._path = path._getPath().copy();
+ } else {
+ this._path = new CanvasKit.SkPath();
+ }
+
+ this._getPath = function() {
+ return this._path;
+ }
+
+ this.addPath = function(path2d, transform) {
+ if (!transform) {
+ transform = {
+ 'a': 1, 'c': 0, 'e': 0,
+ 'b': 0, 'd': 1, 'f': 0,
+ };
+ }
+ this._path.addPath(path2d._getPath(), [transform.a, transform.c, transform.e,
+ transform.b, transform.d, transform.f]);
+ }
+
+ this.arc = function(x, y, radius, startAngle, endAngle, ccw) {
+ arc(this._path, x, y, radius, startAngle, endAngle, ccw);
+ }
+
+ this.arcTo = function(x1, y1, x2, y2, radius) {
+ arcTo(this._path, x1, y1, x2, y2, radius);
+ }
+
+ this.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
+ bezierCurveTo(this._path, cp1x, cp1y, cp2x, cp2y, x, y);
+ }
+
+ this.closePath = function() {
+ closePath(this._path);
+ }
+
+ this.ellipse = function(x, y, radiusX, radiusY, rotation,
+ startAngle, endAngle, ccw) {
+ ellipse(this._path, x, y, radiusX, radiusY, rotation,
+ startAngle, endAngle, ccw);
+ }
+
+ this.lineTo = function(x, y) {
+ lineTo(this._path, x, y);
+ }
+
+ this.moveTo = function(x, y) {
+ moveTo(this._path, x, y);
+ }
+
+ this.quadraticCurveTo = function(cpx, cpy, x, y) {
+ quadraticCurveTo(this._path, cpx, cpy, x, y);
+ }
+
+ this.rect = function(x, y, width, height) {
+ rect(this._path, x, y, width, height);
+ }
+}
diff --git a/experimental/canvaskit/tests/canvas2d.spec.js b/experimental/canvaskit/tests/canvas2d.spec.js
index 002ccad..4ce051f 100644
--- a/experimental/canvaskit/tests/canvas2d.spec.js
+++ b/experimental/canvaskit/tests/canvas2d.spec.js
@@ -291,7 +291,7 @@
}).catch(reportError(done));
}
- describe('Path drawing API', function() {
+ describe('CanvasContext2D API', function() {
it('supports all the line types', function(done) {
LoadCanvasKit.then(catchException(done, () => {
multipleCanvasTest('all_line_drawing_operations', done, (canvas) => {
@@ -837,7 +837,57 @@
done();
}));
});
- }); // end describe('Path drawing API')
+ }); // end describe('CanvasContext2D API')
+
+ describe('Path2D API', function() {
+ it('supports all the line types', function(done) {
+ LoadCanvasKit.then(catchException(done, () => {
+ multipleCanvasTest('path2d_line_drawing_operations', done, (canvas) => {
+ let ctx = canvas.getContext('2d');
+ var clock;
+ var path;
+ if (canvas.makePath2D) {
+ clock = canvas.makePath2D('M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z');
+ path = canvas.makePath2D();
+ } else {
+ clock = new Path2D('M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z')
+ path = new Path2D();
+ }
+ path.moveTo(20, 5);
+ path.lineTo(30, 20);
+ path.lineTo(40, 10);
+ path.lineTo(50, 20);
+ path.lineTo(60, 0);
+ path.lineTo(20, 5);
+
+ path.moveTo(20, 80);
+ path.bezierCurveTo(90, 10, 160, 150, 190, 10);
+
+ path.moveTo(36, 148);
+ path.quadraticCurveTo(66, 188, 120, 136);
+ path.lineTo(36, 148);
+
+ path.rect(5, 170, 20, 25);
+
+ path.moveTo(150, 180);
+ path.arcTo(150, 100, 50, 200, 20);
+ path.lineTo(160, 160);
+
+ path.moveTo(20, 120);
+ path.arc(20, 120, 18, 0, 1.75 * Math.PI);
+ path.lineTo(20, 120);
+
+ path.moveTo(150, 5);
+ path.ellipse(130, 25, 30, 10, -1*Math.PI/8, Math.PI/6, 1.5*Math.PI)
+
+ ctx.lineWidth = 2;
+ ctx.scale(3.0, 3.0);
+ ctx.stroke(path);
+ ctx.stroke(clock);
+ });
+ }));
+ });
+ }); // end describe('Path2D API')
});