1*c8dee2aaSAndroid Build Coastguard Worker// CanvasPath methods, which all take an Path object as the first param 2*c8dee2aaSAndroid Build Coastguard Worker 3*c8dee2aaSAndroid Build Coastguard Workerfunction arc(skpath, x, y, radius, startAngle, endAngle, ccw) { 4*c8dee2aaSAndroid Build Coastguard Worker // As per https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-arc 5*c8dee2aaSAndroid Build Coastguard Worker // arc is essentially a simpler version of ellipse. 6*c8dee2aaSAndroid Build Coastguard Worker ellipse(skpath, x, y, radius, radius, 0, startAngle, endAngle, ccw); 7*c8dee2aaSAndroid Build Coastguard Worker} 8*c8dee2aaSAndroid Build Coastguard Worker 9*c8dee2aaSAndroid Build Coastguard Workerfunction arcTo(skpath, x1, y1, x2, y2, radius) { 10*c8dee2aaSAndroid Build Coastguard Worker if (!allAreFinite([x1, y1, x2, y2, radius])) { 11*c8dee2aaSAndroid Build Coastguard Worker return; 12*c8dee2aaSAndroid Build Coastguard Worker } 13*c8dee2aaSAndroid Build Coastguard Worker if (radius < 0) { 14*c8dee2aaSAndroid Build Coastguard Worker throw 'radii cannot be negative'; 15*c8dee2aaSAndroid Build Coastguard Worker } 16*c8dee2aaSAndroid Build Coastguard Worker if (skpath.isEmpty()) { 17*c8dee2aaSAndroid Build Coastguard Worker skpath.moveTo(x1, y1); 18*c8dee2aaSAndroid Build Coastguard Worker } 19*c8dee2aaSAndroid Build Coastguard Worker skpath.arcToTangent(x1, y1, x2, y2, radius); 20*c8dee2aaSAndroid Build Coastguard Worker} 21*c8dee2aaSAndroid Build Coastguard Worker 22*c8dee2aaSAndroid Build Coastguard Workerfunction bezierCurveTo(skpath, cp1x, cp1y, cp2x, cp2y, x, y) { 23*c8dee2aaSAndroid Build Coastguard Worker if (!allAreFinite([cp1x, cp1y, cp2x, cp2y, x, y])) { 24*c8dee2aaSAndroid Build Coastguard Worker return; 25*c8dee2aaSAndroid Build Coastguard Worker } 26*c8dee2aaSAndroid Build Coastguard Worker if (skpath.isEmpty()) { 27*c8dee2aaSAndroid Build Coastguard Worker skpath.moveTo(cp1x, cp1y); 28*c8dee2aaSAndroid Build Coastguard Worker } 29*c8dee2aaSAndroid Build Coastguard Worker skpath.cubicTo(cp1x, cp1y, cp2x, cp2y, x, y); 30*c8dee2aaSAndroid Build Coastguard Worker} 31*c8dee2aaSAndroid Build Coastguard Worker 32*c8dee2aaSAndroid Build Coastguard Workerfunction closePath(skpath) { 33*c8dee2aaSAndroid Build Coastguard Worker if (skpath.isEmpty()) { 34*c8dee2aaSAndroid Build Coastguard Worker return; 35*c8dee2aaSAndroid Build Coastguard Worker } 36*c8dee2aaSAndroid Build Coastguard Worker // Check to see if we are not just a single point 37*c8dee2aaSAndroid Build Coastguard Worker var bounds = skpath.getBounds(); 38*c8dee2aaSAndroid Build Coastguard Worker if ((bounds[3] - bounds[1]) || (bounds[2] - bounds[0])) { 39*c8dee2aaSAndroid Build Coastguard Worker skpath.close(); 40*c8dee2aaSAndroid Build Coastguard Worker } 41*c8dee2aaSAndroid Build Coastguard Worker} 42*c8dee2aaSAndroid Build Coastguard Worker 43*c8dee2aaSAndroid Build Coastguard Workerfunction _ellipseHelper(skpath, x, y, radiusX, radiusY, startAngle, endAngle) { 44*c8dee2aaSAndroid Build Coastguard Worker var sweepDegrees = radiansToDegrees(endAngle - startAngle); 45*c8dee2aaSAndroid Build Coastguard Worker var startDegrees = radiansToDegrees(startAngle); 46*c8dee2aaSAndroid Build Coastguard Worker 47*c8dee2aaSAndroid Build Coastguard Worker var oval = CanvasKit.LTRBRect(x - radiusX, y - radiusY, x + radiusX, y + radiusY); 48*c8dee2aaSAndroid Build Coastguard Worker 49*c8dee2aaSAndroid Build Coastguard Worker // draw in 2 180 degree segments because trying to draw all 360 degrees at once 50*c8dee2aaSAndroid Build Coastguard Worker // draws nothing. 51*c8dee2aaSAndroid Build Coastguard Worker if (almostEqual(Math.abs(sweepDegrees), 360)) { 52*c8dee2aaSAndroid Build Coastguard Worker var halfSweep = sweepDegrees/2; 53*c8dee2aaSAndroid Build Coastguard Worker skpath.arcToOval(oval, startDegrees, halfSweep, false); 54*c8dee2aaSAndroid Build Coastguard Worker skpath.arcToOval(oval, startDegrees + halfSweep, halfSweep, false); 55*c8dee2aaSAndroid Build Coastguard Worker return; 56*c8dee2aaSAndroid Build Coastguard Worker } 57*c8dee2aaSAndroid Build Coastguard Worker skpath.arcToOval(oval, startDegrees, sweepDegrees, false); 58*c8dee2aaSAndroid Build Coastguard Worker} 59*c8dee2aaSAndroid Build Coastguard Worker 60*c8dee2aaSAndroid Build Coastguard Workerfunction ellipse(skpath, x, y, radiusX, radiusY, rotation, 61*c8dee2aaSAndroid Build Coastguard Worker startAngle, endAngle, ccw) { 62*c8dee2aaSAndroid Build Coastguard Worker if (!allAreFinite([x, y, radiusX, radiusY, rotation, startAngle, endAngle])) { 63*c8dee2aaSAndroid Build Coastguard Worker return; 64*c8dee2aaSAndroid Build Coastguard Worker } 65*c8dee2aaSAndroid Build Coastguard Worker if (radiusX < 0 || radiusY < 0) { 66*c8dee2aaSAndroid Build Coastguard Worker throw 'radii cannot be negative'; 67*c8dee2aaSAndroid Build Coastguard Worker } 68*c8dee2aaSAndroid Build Coastguard Worker 69*c8dee2aaSAndroid Build Coastguard Worker // based off of CanonicalizeAngle in Chrome 70*c8dee2aaSAndroid Build Coastguard Worker var tao = 2 * Math.PI; 71*c8dee2aaSAndroid Build Coastguard Worker var newStartAngle = startAngle % tao; 72*c8dee2aaSAndroid Build Coastguard Worker if (newStartAngle < 0) { 73*c8dee2aaSAndroid Build Coastguard Worker newStartAngle += tao; 74*c8dee2aaSAndroid Build Coastguard Worker } 75*c8dee2aaSAndroid Build Coastguard Worker var delta = newStartAngle - startAngle; 76*c8dee2aaSAndroid Build Coastguard Worker startAngle = newStartAngle; 77*c8dee2aaSAndroid Build Coastguard Worker endAngle += delta; 78*c8dee2aaSAndroid Build Coastguard Worker 79*c8dee2aaSAndroid Build Coastguard Worker // Based off of AdjustEndAngle in Chrome. 80*c8dee2aaSAndroid Build Coastguard Worker if (!ccw && (endAngle - startAngle) >= tao) { 81*c8dee2aaSAndroid Build Coastguard Worker // Draw complete ellipse 82*c8dee2aaSAndroid Build Coastguard Worker endAngle = startAngle + tao; 83*c8dee2aaSAndroid Build Coastguard Worker } else if (ccw && (startAngle - endAngle) >= tao) { 84*c8dee2aaSAndroid Build Coastguard Worker // Draw complete ellipse 85*c8dee2aaSAndroid Build Coastguard Worker endAngle = startAngle - tao; 86*c8dee2aaSAndroid Build Coastguard Worker } else if (!ccw && startAngle > endAngle) { 87*c8dee2aaSAndroid Build Coastguard Worker endAngle = startAngle + (tao - (startAngle - endAngle) % tao); 88*c8dee2aaSAndroid Build Coastguard Worker } else if (ccw && startAngle < endAngle) { 89*c8dee2aaSAndroid Build Coastguard Worker endAngle = startAngle - (tao - (endAngle - startAngle) % tao); 90*c8dee2aaSAndroid Build Coastguard Worker } 91*c8dee2aaSAndroid Build Coastguard Worker 92*c8dee2aaSAndroid Build Coastguard Worker // Based off of Chrome's implementation in 93*c8dee2aaSAndroid Build Coastguard Worker // https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/path.cc 94*c8dee2aaSAndroid Build Coastguard Worker // of note, can't use addArc or addOval because they close the arc, which 95*c8dee2aaSAndroid Build Coastguard Worker // the spec says not to do (unless the user explicitly calls closePath). 96*c8dee2aaSAndroid Build Coastguard Worker // This throws off points being in/out of the arc. 97*c8dee2aaSAndroid Build Coastguard Worker if (!rotation) { 98*c8dee2aaSAndroid Build Coastguard Worker _ellipseHelper(skpath, x, y, radiusX, radiusY, startAngle, endAngle); 99*c8dee2aaSAndroid Build Coastguard Worker return; 100*c8dee2aaSAndroid Build Coastguard Worker } 101*c8dee2aaSAndroid Build Coastguard Worker var rotated = CanvasKit.Matrix.rotated(rotation, x, y); 102*c8dee2aaSAndroid Build Coastguard Worker var rotatedInvert = CanvasKit.Matrix.rotated(-rotation, x, y); 103*c8dee2aaSAndroid Build Coastguard Worker skpath.transform(rotatedInvert); 104*c8dee2aaSAndroid Build Coastguard Worker _ellipseHelper(skpath, x, y, radiusX, radiusY, startAngle, endAngle); 105*c8dee2aaSAndroid Build Coastguard Worker skpath.transform(rotated); 106*c8dee2aaSAndroid Build Coastguard Worker} 107*c8dee2aaSAndroid Build Coastguard Worker 108*c8dee2aaSAndroid Build Coastguard Workerfunction lineTo(skpath, x, y) { 109*c8dee2aaSAndroid Build Coastguard Worker if (!allAreFinite([x, y])) { 110*c8dee2aaSAndroid Build Coastguard Worker return; 111*c8dee2aaSAndroid Build Coastguard Worker } 112*c8dee2aaSAndroid Build Coastguard Worker // A lineTo without a previous point has a moveTo inserted before it 113*c8dee2aaSAndroid Build Coastguard Worker if (skpath.isEmpty()) { 114*c8dee2aaSAndroid Build Coastguard Worker skpath.moveTo(x, y); 115*c8dee2aaSAndroid Build Coastguard Worker } 116*c8dee2aaSAndroid Build Coastguard Worker skpath.lineTo(x, y); 117*c8dee2aaSAndroid Build Coastguard Worker} 118*c8dee2aaSAndroid Build Coastguard Worker 119*c8dee2aaSAndroid Build Coastguard Workerfunction moveTo(skpath, x, y) { 120*c8dee2aaSAndroid Build Coastguard Worker if (!allAreFinite([x, y])) { 121*c8dee2aaSAndroid Build Coastguard Worker return; 122*c8dee2aaSAndroid Build Coastguard Worker } 123*c8dee2aaSAndroid Build Coastguard Worker skpath.moveTo(x, y); 124*c8dee2aaSAndroid Build Coastguard Worker} 125*c8dee2aaSAndroid Build Coastguard Worker 126*c8dee2aaSAndroid Build Coastguard Workerfunction quadraticCurveTo(skpath, cpx, cpy, x, y) { 127*c8dee2aaSAndroid Build Coastguard Worker if (!allAreFinite([cpx, cpy, x, y])) { 128*c8dee2aaSAndroid Build Coastguard Worker return; 129*c8dee2aaSAndroid Build Coastguard Worker } 130*c8dee2aaSAndroid Build Coastguard Worker if (skpath.isEmpty()) { 131*c8dee2aaSAndroid Build Coastguard Worker skpath.moveTo(cpx, cpy); 132*c8dee2aaSAndroid Build Coastguard Worker } 133*c8dee2aaSAndroid Build Coastguard Worker skpath.quadTo(cpx, cpy, x, y); 134*c8dee2aaSAndroid Build Coastguard Worker} 135*c8dee2aaSAndroid Build Coastguard Worker 136*c8dee2aaSAndroid Build Coastguard Workerfunction rect(skpath, x, y, width, height) { 137*c8dee2aaSAndroid Build Coastguard Worker var rect = CanvasKit.XYWHRect(x, y, width, height); 138*c8dee2aaSAndroid Build Coastguard Worker if (!allAreFinite(rect)) { 139*c8dee2aaSAndroid Build Coastguard Worker return; 140*c8dee2aaSAndroid Build Coastguard Worker } 141*c8dee2aaSAndroid Build Coastguard Worker // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-rect 142*c8dee2aaSAndroid Build Coastguard Worker skpath.addRect(rect); 143*c8dee2aaSAndroid Build Coastguard Worker} 144*c8dee2aaSAndroid Build Coastguard Worker 145*c8dee2aaSAndroid Build Coastguard Workerfunction Path2D(path) { 146*c8dee2aaSAndroid Build Coastguard Worker this._path = null; 147*c8dee2aaSAndroid Build Coastguard Worker if (typeof path === 'string') { 148*c8dee2aaSAndroid Build Coastguard Worker this._path = CanvasKit.Path.MakeFromSVGString(path); 149*c8dee2aaSAndroid Build Coastguard Worker } else if (path && path._getPath) { 150*c8dee2aaSAndroid Build Coastguard Worker this._path = path._getPath().copy(); 151*c8dee2aaSAndroid Build Coastguard Worker } else { 152*c8dee2aaSAndroid Build Coastguard Worker this._path = new CanvasKit.Path(); 153*c8dee2aaSAndroid Build Coastguard Worker } 154*c8dee2aaSAndroid Build Coastguard Worker 155*c8dee2aaSAndroid Build Coastguard Worker this._getPath = function() { 156*c8dee2aaSAndroid Build Coastguard Worker return this._path; 157*c8dee2aaSAndroid Build Coastguard Worker } 158*c8dee2aaSAndroid Build Coastguard Worker 159*c8dee2aaSAndroid Build Coastguard Worker this.addPath = function(path2d, transform) { 160*c8dee2aaSAndroid Build Coastguard Worker if (!transform) { 161*c8dee2aaSAndroid Build Coastguard Worker transform = { 162*c8dee2aaSAndroid Build Coastguard Worker 'a': 1, 'c': 0, 'e': 0, 163*c8dee2aaSAndroid Build Coastguard Worker 'b': 0, 'd': 1, 'f': 0, 164*c8dee2aaSAndroid Build Coastguard Worker }; 165*c8dee2aaSAndroid Build Coastguard Worker } 166*c8dee2aaSAndroid Build Coastguard Worker this._path.addPath(path2d._getPath(), [transform.a, transform.c, transform.e, 167*c8dee2aaSAndroid Build Coastguard Worker transform.b, transform.d, transform.f]); 168*c8dee2aaSAndroid Build Coastguard Worker } 169*c8dee2aaSAndroid Build Coastguard Worker 170*c8dee2aaSAndroid Build Coastguard Worker this.arc = function(x, y, radius, startAngle, endAngle, ccw) { 171*c8dee2aaSAndroid Build Coastguard Worker arc(this._path, x, y, radius, startAngle, endAngle, ccw); 172*c8dee2aaSAndroid Build Coastguard Worker } 173*c8dee2aaSAndroid Build Coastguard Worker 174*c8dee2aaSAndroid Build Coastguard Worker this.arcTo = function(x1, y1, x2, y2, radius) { 175*c8dee2aaSAndroid Build Coastguard Worker arcTo(this._path, x1, y1, x2, y2, radius); 176*c8dee2aaSAndroid Build Coastguard Worker } 177*c8dee2aaSAndroid Build Coastguard Worker 178*c8dee2aaSAndroid Build Coastguard Worker this.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) { 179*c8dee2aaSAndroid Build Coastguard Worker bezierCurveTo(this._path, cp1x, cp1y, cp2x, cp2y, x, y); 180*c8dee2aaSAndroid Build Coastguard Worker } 181*c8dee2aaSAndroid Build Coastguard Worker 182*c8dee2aaSAndroid Build Coastguard Worker this.closePath = function() { 183*c8dee2aaSAndroid Build Coastguard Worker closePath(this._path); 184*c8dee2aaSAndroid Build Coastguard Worker } 185*c8dee2aaSAndroid Build Coastguard Worker 186*c8dee2aaSAndroid Build Coastguard Worker this.ellipse = function(x, y, radiusX, radiusY, rotation, 187*c8dee2aaSAndroid Build Coastguard Worker startAngle, endAngle, ccw) { 188*c8dee2aaSAndroid Build Coastguard Worker ellipse(this._path, x, y, radiusX, radiusY, rotation, 189*c8dee2aaSAndroid Build Coastguard Worker startAngle, endAngle, ccw); 190*c8dee2aaSAndroid Build Coastguard Worker } 191*c8dee2aaSAndroid Build Coastguard Worker 192*c8dee2aaSAndroid Build Coastguard Worker this.lineTo = function(x, y) { 193*c8dee2aaSAndroid Build Coastguard Worker lineTo(this._path, x, y); 194*c8dee2aaSAndroid Build Coastguard Worker } 195*c8dee2aaSAndroid Build Coastguard Worker 196*c8dee2aaSAndroid Build Coastguard Worker this.moveTo = function(x, y) { 197*c8dee2aaSAndroid Build Coastguard Worker moveTo(this._path, x, y); 198*c8dee2aaSAndroid Build Coastguard Worker } 199*c8dee2aaSAndroid Build Coastguard Worker 200*c8dee2aaSAndroid Build Coastguard Worker this.quadraticCurveTo = function(cpx, cpy, x, y) { 201*c8dee2aaSAndroid Build Coastguard Worker quadraticCurveTo(this._path, cpx, cpy, x, y); 202*c8dee2aaSAndroid Build Coastguard Worker } 203*c8dee2aaSAndroid Build Coastguard Worker 204*c8dee2aaSAndroid Build Coastguard Worker this.rect = function(x, y, width, height) { 205*c8dee2aaSAndroid Build Coastguard Worker rect(this._path, x, y, width, height); 206*c8dee2aaSAndroid Build Coastguard Worker } 207*c8dee2aaSAndroid Build Coastguard Worker} 208