1*c8dee2aaSAndroid Build Coastguard Workerfunction CanvasRenderingContext2D(skcanvas) { 2*c8dee2aaSAndroid Build Coastguard Worker this._canvas = skcanvas; 3*c8dee2aaSAndroid Build Coastguard Worker this._paint = new CanvasKit.Paint(); 4*c8dee2aaSAndroid Build Coastguard Worker this._paint.setAntiAlias(true); 5*c8dee2aaSAndroid Build Coastguard Worker 6*c8dee2aaSAndroid Build Coastguard Worker this._paint.setStrokeMiter(10); 7*c8dee2aaSAndroid Build Coastguard Worker this._paint.setStrokeCap(CanvasKit.StrokeCap.Butt); 8*c8dee2aaSAndroid Build Coastguard Worker this._paint.setStrokeJoin(CanvasKit.StrokeJoin.Miter); 9*c8dee2aaSAndroid Build Coastguard Worker this._fontString = '10px monospace'; 10*c8dee2aaSAndroid Build Coastguard Worker 11*c8dee2aaSAndroid Build Coastguard Worker this._font = new CanvasKit.Font(CanvasKit.Typeface.GetDefault(), 10); 12*c8dee2aaSAndroid Build Coastguard Worker this._font.setSubpixel(true); 13*c8dee2aaSAndroid Build Coastguard Worker 14*c8dee2aaSAndroid Build Coastguard Worker this._strokeStyle = CanvasKit.BLACK; 15*c8dee2aaSAndroid Build Coastguard Worker this._fillStyle = CanvasKit.BLACK; 16*c8dee2aaSAndroid Build Coastguard Worker this._shadowBlur = 0; 17*c8dee2aaSAndroid Build Coastguard Worker this._shadowColor = CanvasKit.TRANSPARENT; 18*c8dee2aaSAndroid Build Coastguard Worker this._shadowOffsetX = 0; 19*c8dee2aaSAndroid Build Coastguard Worker this._shadowOffsetY = 0; 20*c8dee2aaSAndroid Build Coastguard Worker this._globalAlpha = 1; 21*c8dee2aaSAndroid Build Coastguard Worker this._strokeWidth = 1; 22*c8dee2aaSAndroid Build Coastguard Worker this._lineDashOffset = 0; 23*c8dee2aaSAndroid Build Coastguard Worker this._lineDashList = []; 24*c8dee2aaSAndroid Build Coastguard Worker // aka BlendMode 25*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.SrcOver; 26*c8dee2aaSAndroid Build Coastguard Worker 27*c8dee2aaSAndroid Build Coastguard Worker this._paint.setStrokeWidth(this._strokeWidth); 28*c8dee2aaSAndroid Build Coastguard Worker this._paint.setBlendMode(this._globalCompositeOperation); 29*c8dee2aaSAndroid Build Coastguard Worker 30*c8dee2aaSAndroid Build Coastguard Worker this._currentPath = new CanvasKit.Path(); 31*c8dee2aaSAndroid Build Coastguard Worker this._currentTransform = CanvasKit.Matrix.identity(); 32*c8dee2aaSAndroid Build Coastguard Worker 33*c8dee2aaSAndroid Build Coastguard Worker // Use this for save/restore 34*c8dee2aaSAndroid Build Coastguard Worker this._canvasStateStack = []; 35*c8dee2aaSAndroid Build Coastguard Worker // Keep a reference to all the effects (e.g. gradients, patterns) 36*c8dee2aaSAndroid Build Coastguard Worker // that were allocated for cleanup in _dispose. 37*c8dee2aaSAndroid Build Coastguard Worker this._toCleanUp = []; 38*c8dee2aaSAndroid Build Coastguard Worker 39*c8dee2aaSAndroid Build Coastguard Worker this._dispose = function() { 40*c8dee2aaSAndroid Build Coastguard Worker this._currentPath.delete(); 41*c8dee2aaSAndroid Build Coastguard Worker this._paint.delete(); 42*c8dee2aaSAndroid Build Coastguard Worker this._font.delete(); 43*c8dee2aaSAndroid Build Coastguard Worker this._toCleanUp.forEach(function(c) { 44*c8dee2aaSAndroid Build Coastguard Worker c._dispose(); 45*c8dee2aaSAndroid Build Coastguard Worker }); 46*c8dee2aaSAndroid Build Coastguard Worker // Don't delete this._canvas as it will be disposed 47*c8dee2aaSAndroid Build Coastguard Worker // by the surface of which it is based. 48*c8dee2aaSAndroid Build Coastguard Worker }; 49*c8dee2aaSAndroid Build Coastguard Worker 50*c8dee2aaSAndroid Build Coastguard Worker // This always accepts DOMMatrix/SVGMatrix or any other 51*c8dee2aaSAndroid Build Coastguard Worker // object that has properties a,b,c,d,e,f defined. 52*c8dee2aaSAndroid Build Coastguard Worker // Returns a DOM-Matrix like dictionary 53*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'currentTransform', { 54*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 55*c8dee2aaSAndroid Build Coastguard Worker get: function() { 56*c8dee2aaSAndroid Build Coastguard Worker return { 57*c8dee2aaSAndroid Build Coastguard Worker 'a' : this._currentTransform[0], 58*c8dee2aaSAndroid Build Coastguard Worker 'c' : this._currentTransform[1], 59*c8dee2aaSAndroid Build Coastguard Worker 'e' : this._currentTransform[2], 60*c8dee2aaSAndroid Build Coastguard Worker 'b' : this._currentTransform[3], 61*c8dee2aaSAndroid Build Coastguard Worker 'd' : this._currentTransform[4], 62*c8dee2aaSAndroid Build Coastguard Worker 'f' : this._currentTransform[5], 63*c8dee2aaSAndroid Build Coastguard Worker }; 64*c8dee2aaSAndroid Build Coastguard Worker }, 65*c8dee2aaSAndroid Build Coastguard Worker // @param {DOMMatrix} matrix 66*c8dee2aaSAndroid Build Coastguard Worker set: function(matrix) { 67*c8dee2aaSAndroid Build Coastguard Worker if (matrix.a) { 68*c8dee2aaSAndroid Build Coastguard Worker // if we see a property named 'a', guess that b-f will 69*c8dee2aaSAndroid Build Coastguard Worker // also be there. 70*c8dee2aaSAndroid Build Coastguard Worker this.setTransform(matrix.a, matrix.b, matrix.c, 71*c8dee2aaSAndroid Build Coastguard Worker matrix.d, matrix.e, matrix.f); 72*c8dee2aaSAndroid Build Coastguard Worker } 73*c8dee2aaSAndroid Build Coastguard Worker } 74*c8dee2aaSAndroid Build Coastguard Worker }); 75*c8dee2aaSAndroid Build Coastguard Worker 76*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'fillStyle', { 77*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 78*c8dee2aaSAndroid Build Coastguard Worker get: function() { 79*c8dee2aaSAndroid Build Coastguard Worker if (isCanvasKitColor(this._fillStyle)) { 80*c8dee2aaSAndroid Build Coastguard Worker return colorToString(this._fillStyle); 81*c8dee2aaSAndroid Build Coastguard Worker } 82*c8dee2aaSAndroid Build Coastguard Worker return this._fillStyle; 83*c8dee2aaSAndroid Build Coastguard Worker }, 84*c8dee2aaSAndroid Build Coastguard Worker set: function(newStyle) { 85*c8dee2aaSAndroid Build Coastguard Worker if (typeof newStyle === 'string') { 86*c8dee2aaSAndroid Build Coastguard Worker this._fillStyle = parseColor(newStyle); 87*c8dee2aaSAndroid Build Coastguard Worker } else if (newStyle._getShader) { 88*c8dee2aaSAndroid Build Coastguard Worker // It's an effect that has a shader. 89*c8dee2aaSAndroid Build Coastguard Worker this._fillStyle = newStyle 90*c8dee2aaSAndroid Build Coastguard Worker } 91*c8dee2aaSAndroid Build Coastguard Worker } 92*c8dee2aaSAndroid Build Coastguard Worker }); 93*c8dee2aaSAndroid Build Coastguard Worker 94*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'font', { 95*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 96*c8dee2aaSAndroid Build Coastguard Worker get: function() { 97*c8dee2aaSAndroid Build Coastguard Worker return this._fontString; 98*c8dee2aaSAndroid Build Coastguard Worker }, 99*c8dee2aaSAndroid Build Coastguard Worker set: function(newFont) { 100*c8dee2aaSAndroid Build Coastguard Worker var tf = getTypeface(newFont); 101*c8dee2aaSAndroid Build Coastguard Worker if (tf) { 102*c8dee2aaSAndroid Build Coastguard Worker // tf is a "dict" according to closure, that is, the field 103*c8dee2aaSAndroid Build Coastguard Worker // names are not minified. Thus, we need to access it via 104*c8dee2aaSAndroid Build Coastguard Worker // bracket notation to tell closure not to minify these names. 105*c8dee2aaSAndroid Build Coastguard Worker this._font.setSize(tf['sizePx']); 106*c8dee2aaSAndroid Build Coastguard Worker this._font.setTypeface(tf['typeface']); 107*c8dee2aaSAndroid Build Coastguard Worker this._fontString = newFont; 108*c8dee2aaSAndroid Build Coastguard Worker } 109*c8dee2aaSAndroid Build Coastguard Worker } 110*c8dee2aaSAndroid Build Coastguard Worker }); 111*c8dee2aaSAndroid Build Coastguard Worker 112*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'globalAlpha', { 113*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 114*c8dee2aaSAndroid Build Coastguard Worker get: function() { 115*c8dee2aaSAndroid Build Coastguard Worker return this._globalAlpha; 116*c8dee2aaSAndroid Build Coastguard Worker }, 117*c8dee2aaSAndroid Build Coastguard Worker set: function(newAlpha) { 118*c8dee2aaSAndroid Build Coastguard Worker // ignore invalid values, as per the spec 119*c8dee2aaSAndroid Build Coastguard Worker if (!isFinite(newAlpha) || newAlpha < 0 || newAlpha > 1) { 120*c8dee2aaSAndroid Build Coastguard Worker return; 121*c8dee2aaSAndroid Build Coastguard Worker } 122*c8dee2aaSAndroid Build Coastguard Worker this._globalAlpha = newAlpha; 123*c8dee2aaSAndroid Build Coastguard Worker } 124*c8dee2aaSAndroid Build Coastguard Worker }); 125*c8dee2aaSAndroid Build Coastguard Worker 126*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'globalCompositeOperation', { 127*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 128*c8dee2aaSAndroid Build Coastguard Worker get: function() { 129*c8dee2aaSAndroid Build Coastguard Worker switch (this._globalCompositeOperation) { 130*c8dee2aaSAndroid Build Coastguard Worker // composite-mode 131*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.SrcOver: 132*c8dee2aaSAndroid Build Coastguard Worker return 'source-over'; 133*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.DstOver: 134*c8dee2aaSAndroid Build Coastguard Worker return 'destination-over'; 135*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.Src: 136*c8dee2aaSAndroid Build Coastguard Worker return 'copy'; 137*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.Dst: 138*c8dee2aaSAndroid Build Coastguard Worker return 'destination'; 139*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.Clear: 140*c8dee2aaSAndroid Build Coastguard Worker return 'clear'; 141*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.SrcIn: 142*c8dee2aaSAndroid Build Coastguard Worker return 'source-in'; 143*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.DstIn: 144*c8dee2aaSAndroid Build Coastguard Worker return 'destination-in'; 145*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.SrcOut: 146*c8dee2aaSAndroid Build Coastguard Worker return 'source-out'; 147*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.DstOut: 148*c8dee2aaSAndroid Build Coastguard Worker return 'destination-out'; 149*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.SrcATop: 150*c8dee2aaSAndroid Build Coastguard Worker return 'source-atop'; 151*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.DstATop: 152*c8dee2aaSAndroid Build Coastguard Worker return 'destination-atop'; 153*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.Xor: 154*c8dee2aaSAndroid Build Coastguard Worker return 'xor'; 155*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.Plus: 156*c8dee2aaSAndroid Build Coastguard Worker return 'lighter'; 157*c8dee2aaSAndroid Build Coastguard Worker 158*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.Multiply: 159*c8dee2aaSAndroid Build Coastguard Worker return 'multiply'; 160*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.Screen: 161*c8dee2aaSAndroid Build Coastguard Worker return 'screen'; 162*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.Overlay: 163*c8dee2aaSAndroid Build Coastguard Worker return 'overlay'; 164*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.Darken: 165*c8dee2aaSAndroid Build Coastguard Worker return 'darken'; 166*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.Lighten: 167*c8dee2aaSAndroid Build Coastguard Worker return 'lighten'; 168*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.ColorDodge: 169*c8dee2aaSAndroid Build Coastguard Worker return 'color-dodge'; 170*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.ColorBurn: 171*c8dee2aaSAndroid Build Coastguard Worker return 'color-burn'; 172*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.HardLight: 173*c8dee2aaSAndroid Build Coastguard Worker return 'hard-light'; 174*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.SoftLight: 175*c8dee2aaSAndroid Build Coastguard Worker return 'soft-light'; 176*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.Difference: 177*c8dee2aaSAndroid Build Coastguard Worker return 'difference'; 178*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.Exclusion: 179*c8dee2aaSAndroid Build Coastguard Worker return 'exclusion'; 180*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.Hue: 181*c8dee2aaSAndroid Build Coastguard Worker return 'hue'; 182*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.Saturation: 183*c8dee2aaSAndroid Build Coastguard Worker return 'saturation'; 184*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.Color: 185*c8dee2aaSAndroid Build Coastguard Worker return 'color'; 186*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.BlendMode.Luminosity: 187*c8dee2aaSAndroid Build Coastguard Worker return 'luminosity'; 188*c8dee2aaSAndroid Build Coastguard Worker } 189*c8dee2aaSAndroid Build Coastguard Worker }, 190*c8dee2aaSAndroid Build Coastguard Worker set: function(newMode) { 191*c8dee2aaSAndroid Build Coastguard Worker switch (newMode) { 192*c8dee2aaSAndroid Build Coastguard Worker // composite-mode 193*c8dee2aaSAndroid Build Coastguard Worker case 'source-over': 194*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.SrcOver; 195*c8dee2aaSAndroid Build Coastguard Worker break; 196*c8dee2aaSAndroid Build Coastguard Worker case 'destination-over': 197*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.DstOver; 198*c8dee2aaSAndroid Build Coastguard Worker break; 199*c8dee2aaSAndroid Build Coastguard Worker case 'copy': 200*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Src; 201*c8dee2aaSAndroid Build Coastguard Worker break; 202*c8dee2aaSAndroid Build Coastguard Worker case 'destination': 203*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Dst; 204*c8dee2aaSAndroid Build Coastguard Worker break; 205*c8dee2aaSAndroid Build Coastguard Worker case 'clear': 206*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Clear; 207*c8dee2aaSAndroid Build Coastguard Worker break; 208*c8dee2aaSAndroid Build Coastguard Worker case 'source-in': 209*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.SrcIn; 210*c8dee2aaSAndroid Build Coastguard Worker break; 211*c8dee2aaSAndroid Build Coastguard Worker case 'destination-in': 212*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.DstIn; 213*c8dee2aaSAndroid Build Coastguard Worker break; 214*c8dee2aaSAndroid Build Coastguard Worker case 'source-out': 215*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.SrcOut; 216*c8dee2aaSAndroid Build Coastguard Worker break; 217*c8dee2aaSAndroid Build Coastguard Worker case 'destination-out': 218*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.DstOut; 219*c8dee2aaSAndroid Build Coastguard Worker break; 220*c8dee2aaSAndroid Build Coastguard Worker case 'source-atop': 221*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.SrcATop; 222*c8dee2aaSAndroid Build Coastguard Worker break; 223*c8dee2aaSAndroid Build Coastguard Worker case 'destination-atop': 224*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.DstATop; 225*c8dee2aaSAndroid Build Coastguard Worker break; 226*c8dee2aaSAndroid Build Coastguard Worker case 'xor': 227*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Xor; 228*c8dee2aaSAndroid Build Coastguard Worker break; 229*c8dee2aaSAndroid Build Coastguard Worker case 'lighter': 230*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Plus; 231*c8dee2aaSAndroid Build Coastguard Worker break; 232*c8dee2aaSAndroid Build Coastguard Worker case 'plus-lighter': 233*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Plus; 234*c8dee2aaSAndroid Build Coastguard Worker break; 235*c8dee2aaSAndroid Build Coastguard Worker case 'plus-darker': 236*c8dee2aaSAndroid Build Coastguard Worker throw 'plus-darker is not supported'; 237*c8dee2aaSAndroid Build Coastguard Worker 238*c8dee2aaSAndroid Build Coastguard Worker // blend-mode 239*c8dee2aaSAndroid Build Coastguard Worker case 'multiply': 240*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Multiply; 241*c8dee2aaSAndroid Build Coastguard Worker break; 242*c8dee2aaSAndroid Build Coastguard Worker case 'screen': 243*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Screen; 244*c8dee2aaSAndroid Build Coastguard Worker break; 245*c8dee2aaSAndroid Build Coastguard Worker case 'overlay': 246*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Overlay; 247*c8dee2aaSAndroid Build Coastguard Worker break; 248*c8dee2aaSAndroid Build Coastguard Worker case 'darken': 249*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Darken; 250*c8dee2aaSAndroid Build Coastguard Worker break; 251*c8dee2aaSAndroid Build Coastguard Worker case 'lighten': 252*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Lighten; 253*c8dee2aaSAndroid Build Coastguard Worker break; 254*c8dee2aaSAndroid Build Coastguard Worker case 'color-dodge': 255*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.ColorDodge; 256*c8dee2aaSAndroid Build Coastguard Worker break; 257*c8dee2aaSAndroid Build Coastguard Worker case 'color-burn': 258*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.ColorBurn; 259*c8dee2aaSAndroid Build Coastguard Worker break; 260*c8dee2aaSAndroid Build Coastguard Worker case 'hard-light': 261*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.HardLight; 262*c8dee2aaSAndroid Build Coastguard Worker break; 263*c8dee2aaSAndroid Build Coastguard Worker case 'soft-light': 264*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.SoftLight; 265*c8dee2aaSAndroid Build Coastguard Worker break; 266*c8dee2aaSAndroid Build Coastguard Worker case 'difference': 267*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Difference; 268*c8dee2aaSAndroid Build Coastguard Worker break; 269*c8dee2aaSAndroid Build Coastguard Worker case 'exclusion': 270*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Exclusion; 271*c8dee2aaSAndroid Build Coastguard Worker break; 272*c8dee2aaSAndroid Build Coastguard Worker case 'hue': 273*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Hue; 274*c8dee2aaSAndroid Build Coastguard Worker break; 275*c8dee2aaSAndroid Build Coastguard Worker case 'saturation': 276*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Saturation; 277*c8dee2aaSAndroid Build Coastguard Worker break; 278*c8dee2aaSAndroid Build Coastguard Worker case 'color': 279*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Color; 280*c8dee2aaSAndroid Build Coastguard Worker break; 281*c8dee2aaSAndroid Build Coastguard Worker case 'luminosity': 282*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = CanvasKit.BlendMode.Luminosity; 283*c8dee2aaSAndroid Build Coastguard Worker break; 284*c8dee2aaSAndroid Build Coastguard Worker default: 285*c8dee2aaSAndroid Build Coastguard Worker return; 286*c8dee2aaSAndroid Build Coastguard Worker } 287*c8dee2aaSAndroid Build Coastguard Worker this._paint.setBlendMode(this._globalCompositeOperation); 288*c8dee2aaSAndroid Build Coastguard Worker } 289*c8dee2aaSAndroid Build Coastguard Worker }); 290*c8dee2aaSAndroid Build Coastguard Worker 291*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'imageSmoothingEnabled', { 292*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 293*c8dee2aaSAndroid Build Coastguard Worker get: function() { 294*c8dee2aaSAndroid Build Coastguard Worker return true; 295*c8dee2aaSAndroid Build Coastguard Worker }, 296*c8dee2aaSAndroid Build Coastguard Worker set: function(a) { 297*c8dee2aaSAndroid Build Coastguard Worker // ignored, we always use high quality image smoothing. 298*c8dee2aaSAndroid Build Coastguard Worker } 299*c8dee2aaSAndroid Build Coastguard Worker }); 300*c8dee2aaSAndroid Build Coastguard Worker 301*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'imageSmoothingQuality', { 302*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 303*c8dee2aaSAndroid Build Coastguard Worker get: function() { 304*c8dee2aaSAndroid Build Coastguard Worker return 'high'; 305*c8dee2aaSAndroid Build Coastguard Worker }, 306*c8dee2aaSAndroid Build Coastguard Worker set: function(a) { 307*c8dee2aaSAndroid Build Coastguard Worker // ignored, we always use high quality image smoothing. 308*c8dee2aaSAndroid Build Coastguard Worker } 309*c8dee2aaSAndroid Build Coastguard Worker }); 310*c8dee2aaSAndroid Build Coastguard Worker 311*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'lineCap', { 312*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 313*c8dee2aaSAndroid Build Coastguard Worker get: function() { 314*c8dee2aaSAndroid Build Coastguard Worker switch (this._paint.getStrokeCap()) { 315*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.StrokeCap.Butt: 316*c8dee2aaSAndroid Build Coastguard Worker return 'butt'; 317*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.StrokeCap.Round: 318*c8dee2aaSAndroid Build Coastguard Worker return 'round'; 319*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.StrokeCap.Square: 320*c8dee2aaSAndroid Build Coastguard Worker return 'square'; 321*c8dee2aaSAndroid Build Coastguard Worker } 322*c8dee2aaSAndroid Build Coastguard Worker }, 323*c8dee2aaSAndroid Build Coastguard Worker set: function(newCap) { 324*c8dee2aaSAndroid Build Coastguard Worker switch (newCap) { 325*c8dee2aaSAndroid Build Coastguard Worker case 'butt': 326*c8dee2aaSAndroid Build Coastguard Worker this._paint.setStrokeCap(CanvasKit.StrokeCap.Butt); 327*c8dee2aaSAndroid Build Coastguard Worker return; 328*c8dee2aaSAndroid Build Coastguard Worker case 'round': 329*c8dee2aaSAndroid Build Coastguard Worker this._paint.setStrokeCap(CanvasKit.StrokeCap.Round); 330*c8dee2aaSAndroid Build Coastguard Worker return; 331*c8dee2aaSAndroid Build Coastguard Worker case 'square': 332*c8dee2aaSAndroid Build Coastguard Worker this._paint.setStrokeCap(CanvasKit.StrokeCap.Square); 333*c8dee2aaSAndroid Build Coastguard Worker return; 334*c8dee2aaSAndroid Build Coastguard Worker } 335*c8dee2aaSAndroid Build Coastguard Worker } 336*c8dee2aaSAndroid Build Coastguard Worker }); 337*c8dee2aaSAndroid Build Coastguard Worker 338*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'lineDashOffset', { 339*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 340*c8dee2aaSAndroid Build Coastguard Worker get: function() { 341*c8dee2aaSAndroid Build Coastguard Worker return this._lineDashOffset; 342*c8dee2aaSAndroid Build Coastguard Worker }, 343*c8dee2aaSAndroid Build Coastguard Worker set: function(newOffset) { 344*c8dee2aaSAndroid Build Coastguard Worker if (!isFinite(newOffset)) { 345*c8dee2aaSAndroid Build Coastguard Worker return; 346*c8dee2aaSAndroid Build Coastguard Worker } 347*c8dee2aaSAndroid Build Coastguard Worker this._lineDashOffset = newOffset; 348*c8dee2aaSAndroid Build Coastguard Worker } 349*c8dee2aaSAndroid Build Coastguard Worker }); 350*c8dee2aaSAndroid Build Coastguard Worker 351*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'lineJoin', { 352*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 353*c8dee2aaSAndroid Build Coastguard Worker get: function() { 354*c8dee2aaSAndroid Build Coastguard Worker switch (this._paint.getStrokeJoin()) { 355*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.StrokeJoin.Miter: 356*c8dee2aaSAndroid Build Coastguard Worker return 'miter'; 357*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.StrokeJoin.Round: 358*c8dee2aaSAndroid Build Coastguard Worker return 'round'; 359*c8dee2aaSAndroid Build Coastguard Worker case CanvasKit.StrokeJoin.Bevel: 360*c8dee2aaSAndroid Build Coastguard Worker return 'bevel'; 361*c8dee2aaSAndroid Build Coastguard Worker } 362*c8dee2aaSAndroid Build Coastguard Worker }, 363*c8dee2aaSAndroid Build Coastguard Worker set: function(newJoin) { 364*c8dee2aaSAndroid Build Coastguard Worker switch (newJoin) { 365*c8dee2aaSAndroid Build Coastguard Worker case 'miter': 366*c8dee2aaSAndroid Build Coastguard Worker this._paint.setStrokeJoin(CanvasKit.StrokeJoin.Miter); 367*c8dee2aaSAndroid Build Coastguard Worker return; 368*c8dee2aaSAndroid Build Coastguard Worker case 'round': 369*c8dee2aaSAndroid Build Coastguard Worker this._paint.setStrokeJoin(CanvasKit.StrokeJoin.Round); 370*c8dee2aaSAndroid Build Coastguard Worker return; 371*c8dee2aaSAndroid Build Coastguard Worker case 'bevel': 372*c8dee2aaSAndroid Build Coastguard Worker this._paint.setStrokeJoin(CanvasKit.StrokeJoin.Bevel); 373*c8dee2aaSAndroid Build Coastguard Worker return; 374*c8dee2aaSAndroid Build Coastguard Worker } 375*c8dee2aaSAndroid Build Coastguard Worker } 376*c8dee2aaSAndroid Build Coastguard Worker }); 377*c8dee2aaSAndroid Build Coastguard Worker 378*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'lineWidth', { 379*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 380*c8dee2aaSAndroid Build Coastguard Worker get: function() { 381*c8dee2aaSAndroid Build Coastguard Worker return this._paint.getStrokeWidth(); 382*c8dee2aaSAndroid Build Coastguard Worker }, 383*c8dee2aaSAndroid Build Coastguard Worker set: function(newWidth) { 384*c8dee2aaSAndroid Build Coastguard Worker if (newWidth <= 0 || !newWidth) { 385*c8dee2aaSAndroid Build Coastguard Worker // Spec says to ignore NaN/Inf/0/negative values 386*c8dee2aaSAndroid Build Coastguard Worker return; 387*c8dee2aaSAndroid Build Coastguard Worker } 388*c8dee2aaSAndroid Build Coastguard Worker this._strokeWidth = newWidth; 389*c8dee2aaSAndroid Build Coastguard Worker this._paint.setStrokeWidth(newWidth); 390*c8dee2aaSAndroid Build Coastguard Worker } 391*c8dee2aaSAndroid Build Coastguard Worker }); 392*c8dee2aaSAndroid Build Coastguard Worker 393*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'miterLimit', { 394*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 395*c8dee2aaSAndroid Build Coastguard Worker get: function() { 396*c8dee2aaSAndroid Build Coastguard Worker return this._paint.getStrokeMiter(); 397*c8dee2aaSAndroid Build Coastguard Worker }, 398*c8dee2aaSAndroid Build Coastguard Worker set: function(newLimit) { 399*c8dee2aaSAndroid Build Coastguard Worker if (newLimit <= 0 || !newLimit) { 400*c8dee2aaSAndroid Build Coastguard Worker // Spec says to ignore NaN/Inf/0/negative values 401*c8dee2aaSAndroid Build Coastguard Worker return; 402*c8dee2aaSAndroid Build Coastguard Worker } 403*c8dee2aaSAndroid Build Coastguard Worker this._paint.setStrokeMiter(newLimit); 404*c8dee2aaSAndroid Build Coastguard Worker } 405*c8dee2aaSAndroid Build Coastguard Worker }); 406*c8dee2aaSAndroid Build Coastguard Worker 407*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'shadowBlur', { 408*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 409*c8dee2aaSAndroid Build Coastguard Worker get: function() { 410*c8dee2aaSAndroid Build Coastguard Worker return this._shadowBlur; 411*c8dee2aaSAndroid Build Coastguard Worker }, 412*c8dee2aaSAndroid Build Coastguard Worker set: function(newBlur) { 413*c8dee2aaSAndroid Build Coastguard Worker // ignore negative, inf and NAN (but not 0) as per the spec. 414*c8dee2aaSAndroid Build Coastguard Worker if (newBlur < 0 || !isFinite(newBlur)) { 415*c8dee2aaSAndroid Build Coastguard Worker return; 416*c8dee2aaSAndroid Build Coastguard Worker } 417*c8dee2aaSAndroid Build Coastguard Worker this._shadowBlur = newBlur; 418*c8dee2aaSAndroid Build Coastguard Worker } 419*c8dee2aaSAndroid Build Coastguard Worker }); 420*c8dee2aaSAndroid Build Coastguard Worker 421*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'shadowColor', { 422*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 423*c8dee2aaSAndroid Build Coastguard Worker get: function() { 424*c8dee2aaSAndroid Build Coastguard Worker return colorToString(this._shadowColor); 425*c8dee2aaSAndroid Build Coastguard Worker }, 426*c8dee2aaSAndroid Build Coastguard Worker set: function(newColor) { 427*c8dee2aaSAndroid Build Coastguard Worker this._shadowColor = parseColor(newColor); 428*c8dee2aaSAndroid Build Coastguard Worker } 429*c8dee2aaSAndroid Build Coastguard Worker }); 430*c8dee2aaSAndroid Build Coastguard Worker 431*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'shadowOffsetX', { 432*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 433*c8dee2aaSAndroid Build Coastguard Worker get: function() { 434*c8dee2aaSAndroid Build Coastguard Worker return this._shadowOffsetX; 435*c8dee2aaSAndroid Build Coastguard Worker }, 436*c8dee2aaSAndroid Build Coastguard Worker set: function(newOffset) { 437*c8dee2aaSAndroid Build Coastguard Worker if (!isFinite(newOffset)) { 438*c8dee2aaSAndroid Build Coastguard Worker return; 439*c8dee2aaSAndroid Build Coastguard Worker } 440*c8dee2aaSAndroid Build Coastguard Worker this._shadowOffsetX = newOffset; 441*c8dee2aaSAndroid Build Coastguard Worker } 442*c8dee2aaSAndroid Build Coastguard Worker }); 443*c8dee2aaSAndroid Build Coastguard Worker 444*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'shadowOffsetY', { 445*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 446*c8dee2aaSAndroid Build Coastguard Worker get: function() { 447*c8dee2aaSAndroid Build Coastguard Worker return this._shadowOffsetY; 448*c8dee2aaSAndroid Build Coastguard Worker }, 449*c8dee2aaSAndroid Build Coastguard Worker set: function(newOffset) { 450*c8dee2aaSAndroid Build Coastguard Worker if (!isFinite(newOffset)) { 451*c8dee2aaSAndroid Build Coastguard Worker return; 452*c8dee2aaSAndroid Build Coastguard Worker } 453*c8dee2aaSAndroid Build Coastguard Worker this._shadowOffsetY = newOffset; 454*c8dee2aaSAndroid Build Coastguard Worker } 455*c8dee2aaSAndroid Build Coastguard Worker }); 456*c8dee2aaSAndroid Build Coastguard Worker 457*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'strokeStyle', { 458*c8dee2aaSAndroid Build Coastguard Worker enumerable: true, 459*c8dee2aaSAndroid Build Coastguard Worker get: function() { 460*c8dee2aaSAndroid Build Coastguard Worker return colorToString(this._strokeStyle); 461*c8dee2aaSAndroid Build Coastguard Worker }, 462*c8dee2aaSAndroid Build Coastguard Worker set: function(newStyle) { 463*c8dee2aaSAndroid Build Coastguard Worker if (typeof newStyle === 'string') { 464*c8dee2aaSAndroid Build Coastguard Worker this._strokeStyle = parseColor(newStyle); 465*c8dee2aaSAndroid Build Coastguard Worker } else if (newStyle._getShader) { 466*c8dee2aaSAndroid Build Coastguard Worker // It's probably an effect. 467*c8dee2aaSAndroid Build Coastguard Worker this._strokeStyle = newStyle 468*c8dee2aaSAndroid Build Coastguard Worker } 469*c8dee2aaSAndroid Build Coastguard Worker } 470*c8dee2aaSAndroid Build Coastguard Worker }); 471*c8dee2aaSAndroid Build Coastguard Worker 472*c8dee2aaSAndroid Build Coastguard Worker this.arc = function(x, y, radius, startAngle, endAngle, ccw) { 473*c8dee2aaSAndroid Build Coastguard Worker arc(this._currentPath, x, y, radius, startAngle, endAngle, ccw); 474*c8dee2aaSAndroid Build Coastguard Worker }; 475*c8dee2aaSAndroid Build Coastguard Worker 476*c8dee2aaSAndroid Build Coastguard Worker this.arcTo = function(x1, y1, x2, y2, radius) { 477*c8dee2aaSAndroid Build Coastguard Worker arcTo(this._currentPath, x1, y1, x2, y2, radius); 478*c8dee2aaSAndroid Build Coastguard Worker }; 479*c8dee2aaSAndroid Build Coastguard Worker 480*c8dee2aaSAndroid Build Coastguard Worker // As per the spec this doesn't begin any paths, it only 481*c8dee2aaSAndroid Build Coastguard Worker // clears out any previous paths. 482*c8dee2aaSAndroid Build Coastguard Worker this.beginPath = function() { 483*c8dee2aaSAndroid Build Coastguard Worker this._currentPath.delete(); 484*c8dee2aaSAndroid Build Coastguard Worker this._currentPath = new CanvasKit.Path(); 485*c8dee2aaSAndroid Build Coastguard Worker }; 486*c8dee2aaSAndroid Build Coastguard Worker 487*c8dee2aaSAndroid Build Coastguard Worker this.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) { 488*c8dee2aaSAndroid Build Coastguard Worker bezierCurveTo(this._currentPath, cp1x, cp1y, cp2x, cp2y, x, y); 489*c8dee2aaSAndroid Build Coastguard Worker }; 490*c8dee2aaSAndroid Build Coastguard Worker 491*c8dee2aaSAndroid Build Coastguard Worker this.clearRect = function(x, y, width, height) { 492*c8dee2aaSAndroid Build Coastguard Worker this._paint.setStyle(CanvasKit.PaintStyle.Fill); 493*c8dee2aaSAndroid Build Coastguard Worker this._paint.setBlendMode(CanvasKit.BlendMode.Clear); 494*c8dee2aaSAndroid Build Coastguard Worker this._canvas.drawRect(CanvasKit.XYWHRect(x, y, width, height), this._paint); 495*c8dee2aaSAndroid Build Coastguard Worker this._paint.setBlendMode(this._globalCompositeOperation); 496*c8dee2aaSAndroid Build Coastguard Worker }; 497*c8dee2aaSAndroid Build Coastguard Worker 498*c8dee2aaSAndroid Build Coastguard Worker this.clip = function(path, fillRule) { 499*c8dee2aaSAndroid Build Coastguard Worker if (typeof path === 'string') { 500*c8dee2aaSAndroid Build Coastguard Worker // shift the args if a Path2D is supplied 501*c8dee2aaSAndroid Build Coastguard Worker fillRule = path; 502*c8dee2aaSAndroid Build Coastguard Worker path = this._currentPath; 503*c8dee2aaSAndroid Build Coastguard Worker } else if (path && path._getPath) { 504*c8dee2aaSAndroid Build Coastguard Worker path = path._getPath(); 505*c8dee2aaSAndroid Build Coastguard Worker } 506*c8dee2aaSAndroid Build Coastguard Worker if (!path) { 507*c8dee2aaSAndroid Build Coastguard Worker path = this._currentPath; 508*c8dee2aaSAndroid Build Coastguard Worker } 509*c8dee2aaSAndroid Build Coastguard Worker 510*c8dee2aaSAndroid Build Coastguard Worker var clip = path.copy(); 511*c8dee2aaSAndroid Build Coastguard Worker if (fillRule && fillRule.toLowerCase() === 'evenodd') { 512*c8dee2aaSAndroid Build Coastguard Worker clip.setFillType(CanvasKit.FillType.EvenOdd); 513*c8dee2aaSAndroid Build Coastguard Worker } else { 514*c8dee2aaSAndroid Build Coastguard Worker clip.setFillType(CanvasKit.FillType.Winding); 515*c8dee2aaSAndroid Build Coastguard Worker } 516*c8dee2aaSAndroid Build Coastguard Worker this._canvas.clipPath(clip, CanvasKit.ClipOp.Intersect, true); 517*c8dee2aaSAndroid Build Coastguard Worker clip.delete(); 518*c8dee2aaSAndroid Build Coastguard Worker }; 519*c8dee2aaSAndroid Build Coastguard Worker 520*c8dee2aaSAndroid Build Coastguard Worker this.closePath = function() { 521*c8dee2aaSAndroid Build Coastguard Worker closePath(this._currentPath); 522*c8dee2aaSAndroid Build Coastguard Worker }; 523*c8dee2aaSAndroid Build Coastguard Worker 524*c8dee2aaSAndroid Build Coastguard Worker this.createImageData = function() { 525*c8dee2aaSAndroid Build Coastguard Worker // either takes in 1 or 2 arguments: 526*c8dee2aaSAndroid Build Coastguard Worker // - imagedata on which to copy *width* and *height* only 527*c8dee2aaSAndroid Build Coastguard Worker // - width, height 528*c8dee2aaSAndroid Build Coastguard Worker if (arguments.length === 1) { 529*c8dee2aaSAndroid Build Coastguard Worker var oldData = arguments[0]; 530*c8dee2aaSAndroid Build Coastguard Worker var byteLength = 4 * oldData.width * oldData.height; 531*c8dee2aaSAndroid Build Coastguard Worker return new ImageData(new Uint8ClampedArray(byteLength), 532*c8dee2aaSAndroid Build Coastguard Worker oldData.width, oldData.height); 533*c8dee2aaSAndroid Build Coastguard Worker } else if (arguments.length === 2) { 534*c8dee2aaSAndroid Build Coastguard Worker var width = arguments[0]; 535*c8dee2aaSAndroid Build Coastguard Worker var height = arguments[1]; 536*c8dee2aaSAndroid Build Coastguard Worker var byteLength = 4 * width * height; 537*c8dee2aaSAndroid Build Coastguard Worker return new ImageData(new Uint8ClampedArray(byteLength), 538*c8dee2aaSAndroid Build Coastguard Worker width, height); 539*c8dee2aaSAndroid Build Coastguard Worker } else { 540*c8dee2aaSAndroid Build Coastguard Worker throw 'createImageData expects 1 or 2 arguments, got '+arguments.length; 541*c8dee2aaSAndroid Build Coastguard Worker } 542*c8dee2aaSAndroid Build Coastguard Worker }; 543*c8dee2aaSAndroid Build Coastguard Worker 544*c8dee2aaSAndroid Build Coastguard Worker this.createLinearGradient = function(x1, y1, x2, y2) { 545*c8dee2aaSAndroid Build Coastguard Worker if (!allAreFinite(arguments)) { 546*c8dee2aaSAndroid Build Coastguard Worker return; 547*c8dee2aaSAndroid Build Coastguard Worker } 548*c8dee2aaSAndroid Build Coastguard Worker var lcg = new LinearCanvasGradient(x1, y1, x2, y2); 549*c8dee2aaSAndroid Build Coastguard Worker this._toCleanUp.push(lcg); 550*c8dee2aaSAndroid Build Coastguard Worker return lcg; 551*c8dee2aaSAndroid Build Coastguard Worker }; 552*c8dee2aaSAndroid Build Coastguard Worker 553*c8dee2aaSAndroid Build Coastguard Worker this.createPattern = function(image, repetition) { 554*c8dee2aaSAndroid Build Coastguard Worker var cp = new CanvasPattern(image, repetition); 555*c8dee2aaSAndroid Build Coastguard Worker this._toCleanUp.push(cp); 556*c8dee2aaSAndroid Build Coastguard Worker return cp; 557*c8dee2aaSAndroid Build Coastguard Worker }; 558*c8dee2aaSAndroid Build Coastguard Worker 559*c8dee2aaSAndroid Build Coastguard Worker this.createRadialGradient = function(x1, y1, r1, x2, y2, r2) { 560*c8dee2aaSAndroid Build Coastguard Worker if (!allAreFinite(arguments)) { 561*c8dee2aaSAndroid Build Coastguard Worker return; 562*c8dee2aaSAndroid Build Coastguard Worker } 563*c8dee2aaSAndroid Build Coastguard Worker var rcg = new RadialCanvasGradient(x1, y1, r1, x2, y2, r2); 564*c8dee2aaSAndroid Build Coastguard Worker this._toCleanUp.push(rcg); 565*c8dee2aaSAndroid Build Coastguard Worker return rcg; 566*c8dee2aaSAndroid Build Coastguard Worker }; 567*c8dee2aaSAndroid Build Coastguard Worker 568*c8dee2aaSAndroid Build Coastguard Worker this.drawImage = function(img) { 569*c8dee2aaSAndroid Build Coastguard Worker // 3 potential sets of arguments 570*c8dee2aaSAndroid Build Coastguard Worker // - image, dx, dy 571*c8dee2aaSAndroid Build Coastguard Worker // - image, dx, dy, dWidth, dHeight 572*c8dee2aaSAndroid Build Coastguard Worker // - image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight 573*c8dee2aaSAndroid Build Coastguard Worker // use the fillPaint, which has the globalAlpha in it 574*c8dee2aaSAndroid Build Coastguard Worker // which drawImageRect will use. 575*c8dee2aaSAndroid Build Coastguard Worker if (img instanceof HTMLImage) { 576*c8dee2aaSAndroid Build Coastguard Worker img = img.getSkImage(); 577*c8dee2aaSAndroid Build Coastguard Worker } 578*c8dee2aaSAndroid Build Coastguard Worker var iPaint = this._fillPaint(); 579*c8dee2aaSAndroid Build Coastguard Worker if (arguments.length === 3 || arguments.length === 5) { 580*c8dee2aaSAndroid Build Coastguard Worker var destRect = CanvasKit.XYWHRect(arguments[1], arguments[2], 581*c8dee2aaSAndroid Build Coastguard Worker arguments[3] || img.width(), arguments[4] || img.height()); 582*c8dee2aaSAndroid Build Coastguard Worker var srcRect = CanvasKit.XYWHRect(0, 0, img.width(), img.height()); 583*c8dee2aaSAndroid Build Coastguard Worker } else if (arguments.length === 9){ 584*c8dee2aaSAndroid Build Coastguard Worker var destRect = CanvasKit.XYWHRect(arguments[5], arguments[6], 585*c8dee2aaSAndroid Build Coastguard Worker arguments[7], arguments[8]); 586*c8dee2aaSAndroid Build Coastguard Worker var srcRect = CanvasKit.XYWHRect(arguments[1], arguments[2], 587*c8dee2aaSAndroid Build Coastguard Worker arguments[3], arguments[4]); 588*c8dee2aaSAndroid Build Coastguard Worker } else { 589*c8dee2aaSAndroid Build Coastguard Worker throw 'invalid number of args for drawImage, need 3, 5, or 9; got '+ arguments.length; 590*c8dee2aaSAndroid Build Coastguard Worker } 591*c8dee2aaSAndroid Build Coastguard Worker this._canvas.drawImageRect(img, srcRect, destRect, iPaint, false); 592*c8dee2aaSAndroid Build Coastguard Worker 593*c8dee2aaSAndroid Build Coastguard Worker iPaint.dispose(); 594*c8dee2aaSAndroid Build Coastguard Worker }; 595*c8dee2aaSAndroid Build Coastguard Worker 596*c8dee2aaSAndroid Build Coastguard Worker this.ellipse = function(x, y, radiusX, radiusY, rotation, 597*c8dee2aaSAndroid Build Coastguard Worker startAngle, endAngle, ccw) { 598*c8dee2aaSAndroid Build Coastguard Worker ellipse(this._currentPath, x, y, radiusX, radiusY, rotation, 599*c8dee2aaSAndroid Build Coastguard Worker startAngle, endAngle, ccw); 600*c8dee2aaSAndroid Build Coastguard Worker }; 601*c8dee2aaSAndroid Build Coastguard Worker 602*c8dee2aaSAndroid Build Coastguard Worker // A helper to copy the current paint, ready for filling 603*c8dee2aaSAndroid Build Coastguard Worker // This applies the global alpha. 604*c8dee2aaSAndroid Build Coastguard Worker // Call dispose() after to clean up. 605*c8dee2aaSAndroid Build Coastguard Worker this._fillPaint = function() { 606*c8dee2aaSAndroid Build Coastguard Worker var paint = this._paint.copy(); 607*c8dee2aaSAndroid Build Coastguard Worker paint.setStyle(CanvasKit.PaintStyle.Fill); 608*c8dee2aaSAndroid Build Coastguard Worker if (isCanvasKitColor(this._fillStyle)) { 609*c8dee2aaSAndroid Build Coastguard Worker var alphaColor = CanvasKit.multiplyByAlpha(this._fillStyle, this._globalAlpha); 610*c8dee2aaSAndroid Build Coastguard Worker paint.setColor(alphaColor); 611*c8dee2aaSAndroid Build Coastguard Worker } else { 612*c8dee2aaSAndroid Build Coastguard Worker var shader = this._fillStyle._getShader(this._currentTransform); 613*c8dee2aaSAndroid Build Coastguard Worker paint.setColor(CanvasKit.Color(0,0,0, this._globalAlpha)); 614*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(shader); 615*c8dee2aaSAndroid Build Coastguard Worker } 616*c8dee2aaSAndroid Build Coastguard Worker 617*c8dee2aaSAndroid Build Coastguard Worker paint.dispose = function() { 618*c8dee2aaSAndroid Build Coastguard Worker // If there are some helper effects in the future, clean them up 619*c8dee2aaSAndroid Build Coastguard Worker // here. In any case, we have .dispose() to make _fillPaint behave 620*c8dee2aaSAndroid Build Coastguard Worker // like _strokePaint and _shadowPaint. 621*c8dee2aaSAndroid Build Coastguard Worker this.delete(); 622*c8dee2aaSAndroid Build Coastguard Worker }; 623*c8dee2aaSAndroid Build Coastguard Worker return paint; 624*c8dee2aaSAndroid Build Coastguard Worker }; 625*c8dee2aaSAndroid Build Coastguard Worker 626*c8dee2aaSAndroid Build Coastguard Worker this.fill = function(path, fillRule) { 627*c8dee2aaSAndroid Build Coastguard Worker if (typeof path === 'string') { 628*c8dee2aaSAndroid Build Coastguard Worker // shift the args if a Path2D is supplied 629*c8dee2aaSAndroid Build Coastguard Worker fillRule = path; 630*c8dee2aaSAndroid Build Coastguard Worker path = this._currentPath; 631*c8dee2aaSAndroid Build Coastguard Worker } else if (path && path._getPath) { 632*c8dee2aaSAndroid Build Coastguard Worker path = path._getPath(); 633*c8dee2aaSAndroid Build Coastguard Worker } 634*c8dee2aaSAndroid Build Coastguard Worker if (fillRule === 'evenodd') { 635*c8dee2aaSAndroid Build Coastguard Worker this._currentPath.setFillType(CanvasKit.FillType.EvenOdd); 636*c8dee2aaSAndroid Build Coastguard Worker } else if (fillRule === 'nonzero' || !fillRule) { 637*c8dee2aaSAndroid Build Coastguard Worker this._currentPath.setFillType(CanvasKit.FillType.Winding); 638*c8dee2aaSAndroid Build Coastguard Worker } else { 639*c8dee2aaSAndroid Build Coastguard Worker throw 'invalid fill rule'; 640*c8dee2aaSAndroid Build Coastguard Worker } 641*c8dee2aaSAndroid Build Coastguard Worker if (!path) { 642*c8dee2aaSAndroid Build Coastguard Worker path = this._currentPath; 643*c8dee2aaSAndroid Build Coastguard Worker } 644*c8dee2aaSAndroid Build Coastguard Worker 645*c8dee2aaSAndroid Build Coastguard Worker var fillPaint = this._fillPaint(); 646*c8dee2aaSAndroid Build Coastguard Worker 647*c8dee2aaSAndroid Build Coastguard Worker var shadowPaint = this._shadowPaint(fillPaint); 648*c8dee2aaSAndroid Build Coastguard Worker if (shadowPaint) { 649*c8dee2aaSAndroid Build Coastguard Worker this._canvas.save(); 650*c8dee2aaSAndroid Build Coastguard Worker this._applyShadowOffsetMatrix(); 651*c8dee2aaSAndroid Build Coastguard Worker this._canvas.drawPath(path, shadowPaint); 652*c8dee2aaSAndroid Build Coastguard Worker this._canvas.restore(); 653*c8dee2aaSAndroid Build Coastguard Worker shadowPaint.dispose(); 654*c8dee2aaSAndroid Build Coastguard Worker } 655*c8dee2aaSAndroid Build Coastguard Worker this._canvas.drawPath(path, fillPaint); 656*c8dee2aaSAndroid Build Coastguard Worker fillPaint.dispose(); 657*c8dee2aaSAndroid Build Coastguard Worker }; 658*c8dee2aaSAndroid Build Coastguard Worker 659*c8dee2aaSAndroid Build Coastguard Worker this.fillRect = function(x, y, width, height) { 660*c8dee2aaSAndroid Build Coastguard Worker var fillPaint = this._fillPaint(); 661*c8dee2aaSAndroid Build Coastguard Worker 662*c8dee2aaSAndroid Build Coastguard Worker var shadowPaint = this._shadowPaint(fillPaint); 663*c8dee2aaSAndroid Build Coastguard Worker if (shadowPaint) { 664*c8dee2aaSAndroid Build Coastguard Worker this._canvas.save(); 665*c8dee2aaSAndroid Build Coastguard Worker this._applyShadowOffsetMatrix(); 666*c8dee2aaSAndroid Build Coastguard Worker this._canvas.drawRect(CanvasKit.XYWHRect(x, y, width, height), shadowPaint); 667*c8dee2aaSAndroid Build Coastguard Worker this._canvas.restore(); 668*c8dee2aaSAndroid Build Coastguard Worker shadowPaint.dispose(); 669*c8dee2aaSAndroid Build Coastguard Worker } 670*c8dee2aaSAndroid Build Coastguard Worker 671*c8dee2aaSAndroid Build Coastguard Worker this._canvas.drawRect(CanvasKit.XYWHRect(x, y, width, height), fillPaint); 672*c8dee2aaSAndroid Build Coastguard Worker fillPaint.dispose(); 673*c8dee2aaSAndroid Build Coastguard Worker }; 674*c8dee2aaSAndroid Build Coastguard Worker 675*c8dee2aaSAndroid Build Coastguard Worker this.fillText = function(text, x, y, maxWidth) { 676*c8dee2aaSAndroid Build Coastguard Worker // TODO do something with maxWidth, probably involving measure 677*c8dee2aaSAndroid Build Coastguard Worker var fillPaint = this._fillPaint(); 678*c8dee2aaSAndroid Build Coastguard Worker var blob = CanvasKit.TextBlob.MakeFromText(text, this._font); 679*c8dee2aaSAndroid Build Coastguard Worker 680*c8dee2aaSAndroid Build Coastguard Worker var shadowPaint = this._shadowPaint(fillPaint); 681*c8dee2aaSAndroid Build Coastguard Worker if (shadowPaint) { 682*c8dee2aaSAndroid Build Coastguard Worker this._canvas.save(); 683*c8dee2aaSAndroid Build Coastguard Worker this._applyShadowOffsetMatrix(); 684*c8dee2aaSAndroid Build Coastguard Worker this._canvas.drawTextBlob(blob, x, y, shadowPaint); 685*c8dee2aaSAndroid Build Coastguard Worker this._canvas.restore(); 686*c8dee2aaSAndroid Build Coastguard Worker shadowPaint.dispose(); 687*c8dee2aaSAndroid Build Coastguard Worker } 688*c8dee2aaSAndroid Build Coastguard Worker this._canvas.drawTextBlob(blob, x, y, fillPaint); 689*c8dee2aaSAndroid Build Coastguard Worker blob.delete(); 690*c8dee2aaSAndroid Build Coastguard Worker fillPaint.dispose(); 691*c8dee2aaSAndroid Build Coastguard Worker }; 692*c8dee2aaSAndroid Build Coastguard Worker 693*c8dee2aaSAndroid Build Coastguard Worker this.getImageData = function(x, y, w, h) { 694*c8dee2aaSAndroid Build Coastguard Worker var pixels = this._canvas.readPixels(x, y, { 695*c8dee2aaSAndroid Build Coastguard Worker 'width': w, 696*c8dee2aaSAndroid Build Coastguard Worker 'height': h, 697*c8dee2aaSAndroid Build Coastguard Worker 'colorType': CanvasKit.ColorType.RGBA_8888, 698*c8dee2aaSAndroid Build Coastguard Worker 'alphaType': CanvasKit.AlphaType.Unpremul, 699*c8dee2aaSAndroid Build Coastguard Worker 'colorSpace': CanvasKit.ColorSpace.SRGB, 700*c8dee2aaSAndroid Build Coastguard Worker }); 701*c8dee2aaSAndroid Build Coastguard Worker if (!pixels) { 702*c8dee2aaSAndroid Build Coastguard Worker return null; 703*c8dee2aaSAndroid Build Coastguard Worker } 704*c8dee2aaSAndroid Build Coastguard Worker // This essentially re-wraps the pixels from a Uint8Array to 705*c8dee2aaSAndroid Build Coastguard Worker // a Uint8ClampedArray (without making a copy of pixels). 706*c8dee2aaSAndroid Build Coastguard Worker return new ImageData( 707*c8dee2aaSAndroid Build Coastguard Worker new Uint8ClampedArray(pixels.buffer), 708*c8dee2aaSAndroid Build Coastguard Worker w, h); 709*c8dee2aaSAndroid Build Coastguard Worker }; 710*c8dee2aaSAndroid Build Coastguard Worker 711*c8dee2aaSAndroid Build Coastguard Worker this.getLineDash = function() { 712*c8dee2aaSAndroid Build Coastguard Worker return this._lineDashList.slice(); 713*c8dee2aaSAndroid Build Coastguard Worker }; 714*c8dee2aaSAndroid Build Coastguard Worker 715*c8dee2aaSAndroid Build Coastguard Worker this._mapToLocalCoordinates = function(pts) { 716*c8dee2aaSAndroid Build Coastguard Worker var inverted = CanvasKit.Matrix.invert(this._currentTransform); 717*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Matrix.mapPoints(inverted, pts); 718*c8dee2aaSAndroid Build Coastguard Worker return pts; 719*c8dee2aaSAndroid Build Coastguard Worker }; 720*c8dee2aaSAndroid Build Coastguard Worker 721*c8dee2aaSAndroid Build Coastguard Worker this.isPointInPath = function(x, y, fillmode) { 722*c8dee2aaSAndroid Build Coastguard Worker var args = arguments; 723*c8dee2aaSAndroid Build Coastguard Worker if (args.length === 3) { 724*c8dee2aaSAndroid Build Coastguard Worker var path = this._currentPath; 725*c8dee2aaSAndroid Build Coastguard Worker } else if (args.length === 4) { 726*c8dee2aaSAndroid Build Coastguard Worker var path = args[0]; 727*c8dee2aaSAndroid Build Coastguard Worker x = args[1]; 728*c8dee2aaSAndroid Build Coastguard Worker y = args[2]; 729*c8dee2aaSAndroid Build Coastguard Worker fillmode = args[3]; 730*c8dee2aaSAndroid Build Coastguard Worker } else { 731*c8dee2aaSAndroid Build Coastguard Worker throw 'invalid arg count, need 3 or 4, got ' + args.length; 732*c8dee2aaSAndroid Build Coastguard Worker } 733*c8dee2aaSAndroid Build Coastguard Worker if (!isFinite(x) || !isFinite(y)) { 734*c8dee2aaSAndroid Build Coastguard Worker return false; 735*c8dee2aaSAndroid Build Coastguard Worker } 736*c8dee2aaSAndroid Build Coastguard Worker fillmode = fillmode || 'nonzero'; 737*c8dee2aaSAndroid Build Coastguard Worker if (!(fillmode === 'nonzero' || fillmode === 'evenodd')) { 738*c8dee2aaSAndroid Build Coastguard Worker return false; 739*c8dee2aaSAndroid Build Coastguard Worker } 740*c8dee2aaSAndroid Build Coastguard Worker // x and y are in canvas coordinates (i.e. unaffected by CTM) 741*c8dee2aaSAndroid Build Coastguard Worker var pts = this._mapToLocalCoordinates([x, y]); 742*c8dee2aaSAndroid Build Coastguard Worker x = pts[0]; 743*c8dee2aaSAndroid Build Coastguard Worker y = pts[1]; 744*c8dee2aaSAndroid Build Coastguard Worker path.setFillType(fillmode === 'nonzero' ? 745*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.FillType.Winding : 746*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.FillType.EvenOdd); 747*c8dee2aaSAndroid Build Coastguard Worker return path.contains(x, y); 748*c8dee2aaSAndroid Build Coastguard Worker }; 749*c8dee2aaSAndroid Build Coastguard Worker 750*c8dee2aaSAndroid Build Coastguard Worker this.isPointInStroke = function(x, y) { 751*c8dee2aaSAndroid Build Coastguard Worker var args = arguments; 752*c8dee2aaSAndroid Build Coastguard Worker if (args.length === 2) { 753*c8dee2aaSAndroid Build Coastguard Worker var path = this._currentPath; 754*c8dee2aaSAndroid Build Coastguard Worker } else if (args.length === 3) { 755*c8dee2aaSAndroid Build Coastguard Worker var path = args[0]; 756*c8dee2aaSAndroid Build Coastguard Worker x = args[1]; 757*c8dee2aaSAndroid Build Coastguard Worker y = args[2]; 758*c8dee2aaSAndroid Build Coastguard Worker } else { 759*c8dee2aaSAndroid Build Coastguard Worker throw 'invalid arg count, need 2 or 3, got ' + args.length; 760*c8dee2aaSAndroid Build Coastguard Worker } 761*c8dee2aaSAndroid Build Coastguard Worker if (!isFinite(x) || !isFinite(y)) { 762*c8dee2aaSAndroid Build Coastguard Worker return false; 763*c8dee2aaSAndroid Build Coastguard Worker } 764*c8dee2aaSAndroid Build Coastguard Worker var pts = this._mapToLocalCoordinates([x, y]); 765*c8dee2aaSAndroid Build Coastguard Worker x = pts[0]; 766*c8dee2aaSAndroid Build Coastguard Worker y = pts[1]; 767*c8dee2aaSAndroid Build Coastguard Worker var temp = path.copy(); 768*c8dee2aaSAndroid Build Coastguard Worker // fillmode is always nonzero 769*c8dee2aaSAndroid Build Coastguard Worker temp.setFillType(CanvasKit.FillType.Winding); 770*c8dee2aaSAndroid Build Coastguard Worker temp.stroke({'width': this.lineWidth, 'miter_limit': this.miterLimit, 771*c8dee2aaSAndroid Build Coastguard Worker 'cap': this._paint.getStrokeCap(), 'join': this._paint.getStrokeJoin(), 772*c8dee2aaSAndroid Build Coastguard Worker 'precision': 0.3, // this is what Chrome uses to compute this 773*c8dee2aaSAndroid Build Coastguard Worker }); 774*c8dee2aaSAndroid Build Coastguard Worker var retVal = temp.contains(x, y); 775*c8dee2aaSAndroid Build Coastguard Worker temp.delete(); 776*c8dee2aaSAndroid Build Coastguard Worker return retVal; 777*c8dee2aaSAndroid Build Coastguard Worker }; 778*c8dee2aaSAndroid Build Coastguard Worker 779*c8dee2aaSAndroid Build Coastguard Worker this.lineTo = function(x, y) { 780*c8dee2aaSAndroid Build Coastguard Worker lineTo(this._currentPath, x, y); 781*c8dee2aaSAndroid Build Coastguard Worker }; 782*c8dee2aaSAndroid Build Coastguard Worker 783*c8dee2aaSAndroid Build Coastguard Worker this.measureText = function(text) { 784*c8dee2aaSAndroid Build Coastguard Worker const ids = this._font.getGlyphIDs(text); 785*c8dee2aaSAndroid Build Coastguard Worker const widths = this._font.getGlyphWidths(ids); 786*c8dee2aaSAndroid Build Coastguard Worker let totalWidth = 0; 787*c8dee2aaSAndroid Build Coastguard Worker for (const w of widths) { 788*c8dee2aaSAndroid Build Coastguard Worker totalWidth += w; 789*c8dee2aaSAndroid Build Coastguard Worker } 790*c8dee2aaSAndroid Build Coastguard Worker return { 791*c8dee2aaSAndroid Build Coastguard Worker "width": totalWidth, 792*c8dee2aaSAndroid Build Coastguard Worker }; 793*c8dee2aaSAndroid Build Coastguard Worker }; 794*c8dee2aaSAndroid Build Coastguard Worker 795*c8dee2aaSAndroid Build Coastguard Worker this.moveTo = function(x, y) { 796*c8dee2aaSAndroid Build Coastguard Worker moveTo(this._currentPath, x, y); 797*c8dee2aaSAndroid Build Coastguard Worker }; 798*c8dee2aaSAndroid Build Coastguard Worker 799*c8dee2aaSAndroid Build Coastguard Worker this.putImageData = function(imageData, x, y, dirtyX, dirtyY, dirtyWidth, dirtyHeight) { 800*c8dee2aaSAndroid Build Coastguard Worker if (!allAreFinite([x, y, dirtyX, dirtyY, dirtyWidth, dirtyHeight])) { 801*c8dee2aaSAndroid Build Coastguard Worker return; 802*c8dee2aaSAndroid Build Coastguard Worker } 803*c8dee2aaSAndroid Build Coastguard Worker if (dirtyX === undefined) { 804*c8dee2aaSAndroid Build Coastguard Worker // fast, simple path for basic call 805*c8dee2aaSAndroid Build Coastguard Worker this._canvas.writePixels(imageData.data, imageData.width, imageData.height, x, y); 806*c8dee2aaSAndroid Build Coastguard Worker return; 807*c8dee2aaSAndroid Build Coastguard Worker } 808*c8dee2aaSAndroid Build Coastguard Worker dirtyX = dirtyX || 0; 809*c8dee2aaSAndroid Build Coastguard Worker dirtyY = dirtyY || 0; 810*c8dee2aaSAndroid Build Coastguard Worker dirtyWidth = dirtyWidth || imageData.width; 811*c8dee2aaSAndroid Build Coastguard Worker dirtyHeight = dirtyHeight || imageData.height; 812*c8dee2aaSAndroid Build Coastguard Worker 813*c8dee2aaSAndroid Build Coastguard Worker // as per https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-putimagedata 814*c8dee2aaSAndroid Build Coastguard Worker if (dirtyWidth < 0) { 815*c8dee2aaSAndroid Build Coastguard Worker dirtyX = dirtyX+dirtyWidth; 816*c8dee2aaSAndroid Build Coastguard Worker dirtyWidth = Math.abs(dirtyWidth); 817*c8dee2aaSAndroid Build Coastguard Worker } 818*c8dee2aaSAndroid Build Coastguard Worker if (dirtyHeight < 0) { 819*c8dee2aaSAndroid Build Coastguard Worker dirtyY = dirtyY+dirtyHeight; 820*c8dee2aaSAndroid Build Coastguard Worker dirtyHeight = Math.abs(dirtyHeight); 821*c8dee2aaSAndroid Build Coastguard Worker } 822*c8dee2aaSAndroid Build Coastguard Worker if (dirtyX < 0) { 823*c8dee2aaSAndroid Build Coastguard Worker dirtyWidth = dirtyWidth + dirtyX; 824*c8dee2aaSAndroid Build Coastguard Worker dirtyX = 0; 825*c8dee2aaSAndroid Build Coastguard Worker } 826*c8dee2aaSAndroid Build Coastguard Worker if (dirtyY < 0) { 827*c8dee2aaSAndroid Build Coastguard Worker dirtyHeight = dirtyHeight + dirtyY; 828*c8dee2aaSAndroid Build Coastguard Worker dirtyY = 0; 829*c8dee2aaSAndroid Build Coastguard Worker } 830*c8dee2aaSAndroid Build Coastguard Worker if (dirtyWidth <= 0 || dirtyHeight <= 0) { 831*c8dee2aaSAndroid Build Coastguard Worker return; 832*c8dee2aaSAndroid Build Coastguard Worker } 833*c8dee2aaSAndroid Build Coastguard Worker var img = CanvasKit.MakeImage({ 834*c8dee2aaSAndroid Build Coastguard Worker 'width': imageData.width, 835*c8dee2aaSAndroid Build Coastguard Worker 'height': imageData.height, 836*c8dee2aaSAndroid Build Coastguard Worker 'alphaType': CanvasKit.AlphaType.Unpremul, 837*c8dee2aaSAndroid Build Coastguard Worker 'colorType': CanvasKit.ColorType.RGBA_8888, 838*c8dee2aaSAndroid Build Coastguard Worker 'colorSpace': CanvasKit.ColorSpace.SRGB 839*c8dee2aaSAndroid Build Coastguard Worker }, imageData.data, 4 * imageData.width); 840*c8dee2aaSAndroid Build Coastguard Worker var src = CanvasKit.XYWHRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight); 841*c8dee2aaSAndroid Build Coastguard Worker var dst = CanvasKit.XYWHRect(x+dirtyX, y+dirtyY, dirtyWidth, dirtyHeight); 842*c8dee2aaSAndroid Build Coastguard Worker var inverted = CanvasKit.Matrix.invert(this._currentTransform); 843*c8dee2aaSAndroid Build Coastguard Worker this._canvas.save(); 844*c8dee2aaSAndroid Build Coastguard Worker // putImageData() operates in device space. 845*c8dee2aaSAndroid Build Coastguard Worker this._canvas.concat(inverted); 846*c8dee2aaSAndroid Build Coastguard Worker this._canvas.drawImageRect(img, src, dst, null, false); 847*c8dee2aaSAndroid Build Coastguard Worker this._canvas.restore(); 848*c8dee2aaSAndroid Build Coastguard Worker img.delete(); 849*c8dee2aaSAndroid Build Coastguard Worker }; 850*c8dee2aaSAndroid Build Coastguard Worker 851*c8dee2aaSAndroid Build Coastguard Worker this.quadraticCurveTo = function(cpx, cpy, x, y) { 852*c8dee2aaSAndroid Build Coastguard Worker quadraticCurveTo(this._currentPath, cpx, cpy, x, y); 853*c8dee2aaSAndroid Build Coastguard Worker }; 854*c8dee2aaSAndroid Build Coastguard Worker 855*c8dee2aaSAndroid Build Coastguard Worker this.rect = function(x, y, width, height) { 856*c8dee2aaSAndroid Build Coastguard Worker rect(this._currentPath, x, y, width, height); 857*c8dee2aaSAndroid Build Coastguard Worker }; 858*c8dee2aaSAndroid Build Coastguard Worker 859*c8dee2aaSAndroid Build Coastguard Worker this.resetTransform = function() { 860*c8dee2aaSAndroid Build Coastguard Worker // Apply the current transform to the path and then reset 861*c8dee2aaSAndroid Build Coastguard Worker // to the identity. Essentially "commit" the transform. 862*c8dee2aaSAndroid Build Coastguard Worker this._currentPath.transform(this._currentTransform); 863*c8dee2aaSAndroid Build Coastguard Worker var inverted = CanvasKit.Matrix.invert(this._currentTransform); 864*c8dee2aaSAndroid Build Coastguard Worker this._canvas.concat(inverted); 865*c8dee2aaSAndroid Build Coastguard Worker // This should be identity, modulo floating point drift. 866*c8dee2aaSAndroid Build Coastguard Worker this._currentTransform = this._canvas.getTotalMatrix(); 867*c8dee2aaSAndroid Build Coastguard Worker }; 868*c8dee2aaSAndroid Build Coastguard Worker 869*c8dee2aaSAndroid Build Coastguard Worker this.restore = function() { 870*c8dee2aaSAndroid Build Coastguard Worker var newState = this._canvasStateStack.pop(); 871*c8dee2aaSAndroid Build Coastguard Worker if (!newState) { 872*c8dee2aaSAndroid Build Coastguard Worker return; 873*c8dee2aaSAndroid Build Coastguard Worker } 874*c8dee2aaSAndroid Build Coastguard Worker // "commit" the current transform. We pop, then apply the inverse of the 875*c8dee2aaSAndroid Build Coastguard Worker // popped state, which has the effect of applying just the delta of 876*c8dee2aaSAndroid Build Coastguard Worker // transforms between old and new. 877*c8dee2aaSAndroid Build Coastguard Worker var combined = CanvasKit.Matrix.multiply( 878*c8dee2aaSAndroid Build Coastguard Worker this._currentTransform, 879*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Matrix.invert(newState.ctm) 880*c8dee2aaSAndroid Build Coastguard Worker ); 881*c8dee2aaSAndroid Build Coastguard Worker this._currentPath.transform(combined); 882*c8dee2aaSAndroid Build Coastguard Worker this._paint.delete(); 883*c8dee2aaSAndroid Build Coastguard Worker this._paint = newState.paint; 884*c8dee2aaSAndroid Build Coastguard Worker 885*c8dee2aaSAndroid Build Coastguard Worker this._lineDashList = newState.ldl; 886*c8dee2aaSAndroid Build Coastguard Worker this._strokeWidth = newState.sw; 887*c8dee2aaSAndroid Build Coastguard Worker this._strokeStyle = newState.ss; 888*c8dee2aaSAndroid Build Coastguard Worker this._fillStyle = newState.fs; 889*c8dee2aaSAndroid Build Coastguard Worker this._shadowOffsetX = newState.sox; 890*c8dee2aaSAndroid Build Coastguard Worker this._shadowOffsetY = newState.soy; 891*c8dee2aaSAndroid Build Coastguard Worker this._shadowBlur = newState.sb; 892*c8dee2aaSAndroid Build Coastguard Worker this._shadowColor = newState.shc; 893*c8dee2aaSAndroid Build Coastguard Worker this._globalAlpha = newState.ga; 894*c8dee2aaSAndroid Build Coastguard Worker this._globalCompositeOperation = newState.gco; 895*c8dee2aaSAndroid Build Coastguard Worker this._lineDashOffset = newState.ldo; 896*c8dee2aaSAndroid Build Coastguard Worker this._fontString = newState.fontstr; 897*c8dee2aaSAndroid Build Coastguard Worker 898*c8dee2aaSAndroid Build Coastguard Worker //TODO: textAlign, textBaseline 899*c8dee2aaSAndroid Build Coastguard Worker 900*c8dee2aaSAndroid Build Coastguard Worker // restores the clip and ctm 901*c8dee2aaSAndroid Build Coastguard Worker this._canvas.restore(); 902*c8dee2aaSAndroid Build Coastguard Worker this._currentTransform = this._canvas.getTotalMatrix(); 903*c8dee2aaSAndroid Build Coastguard Worker }; 904*c8dee2aaSAndroid Build Coastguard Worker 905*c8dee2aaSAndroid Build Coastguard Worker this.rotate = function(radians) { 906*c8dee2aaSAndroid Build Coastguard Worker if (!isFinite(radians)) { 907*c8dee2aaSAndroid Build Coastguard Worker return; 908*c8dee2aaSAndroid Build Coastguard Worker } 909*c8dee2aaSAndroid Build Coastguard Worker // retroactively apply the inverse of this transform to the previous 910*c8dee2aaSAndroid Build Coastguard Worker // path so it cancels out when we apply the transform at draw time. 911*c8dee2aaSAndroid Build Coastguard Worker var inverted = CanvasKit.Matrix.rotated(-radians); 912*c8dee2aaSAndroid Build Coastguard Worker this._currentPath.transform(inverted); 913*c8dee2aaSAndroid Build Coastguard Worker this._canvas.rotate(radiansToDegrees(radians), 0, 0); 914*c8dee2aaSAndroid Build Coastguard Worker this._currentTransform = this._canvas.getTotalMatrix(); 915*c8dee2aaSAndroid Build Coastguard Worker }; 916*c8dee2aaSAndroid Build Coastguard Worker 917*c8dee2aaSAndroid Build Coastguard Worker this.save = function() { 918*c8dee2aaSAndroid Build Coastguard Worker if (this._fillStyle._copy) { 919*c8dee2aaSAndroid Build Coastguard Worker var fs = this._fillStyle._copy(); 920*c8dee2aaSAndroid Build Coastguard Worker this._toCleanUp.push(fs); 921*c8dee2aaSAndroid Build Coastguard Worker } else { 922*c8dee2aaSAndroid Build Coastguard Worker var fs = this._fillStyle; 923*c8dee2aaSAndroid Build Coastguard Worker } 924*c8dee2aaSAndroid Build Coastguard Worker 925*c8dee2aaSAndroid Build Coastguard Worker if (this._strokeStyle._copy) { 926*c8dee2aaSAndroid Build Coastguard Worker var ss = this._strokeStyle._copy(); 927*c8dee2aaSAndroid Build Coastguard Worker this._toCleanUp.push(ss); 928*c8dee2aaSAndroid Build Coastguard Worker } else { 929*c8dee2aaSAndroid Build Coastguard Worker var ss = this._strokeStyle; 930*c8dee2aaSAndroid Build Coastguard Worker } 931*c8dee2aaSAndroid Build Coastguard Worker 932*c8dee2aaSAndroid Build Coastguard Worker this._canvasStateStack.push({ 933*c8dee2aaSAndroid Build Coastguard Worker ctm: this._currentTransform.slice(), 934*c8dee2aaSAndroid Build Coastguard Worker ldl: this._lineDashList.slice(), 935*c8dee2aaSAndroid Build Coastguard Worker sw: this._strokeWidth, 936*c8dee2aaSAndroid Build Coastguard Worker ss: ss, 937*c8dee2aaSAndroid Build Coastguard Worker fs: fs, 938*c8dee2aaSAndroid Build Coastguard Worker sox: this._shadowOffsetX, 939*c8dee2aaSAndroid Build Coastguard Worker soy: this._shadowOffsetY, 940*c8dee2aaSAndroid Build Coastguard Worker sb: this._shadowBlur, 941*c8dee2aaSAndroid Build Coastguard Worker shc: this._shadowColor, 942*c8dee2aaSAndroid Build Coastguard Worker ga: this._globalAlpha, 943*c8dee2aaSAndroid Build Coastguard Worker ldo: this._lineDashOffset, 944*c8dee2aaSAndroid Build Coastguard Worker gco: this._globalCompositeOperation, 945*c8dee2aaSAndroid Build Coastguard Worker paint: this._paint.copy(), 946*c8dee2aaSAndroid Build Coastguard Worker fontstr: this._fontString, 947*c8dee2aaSAndroid Build Coastguard Worker //TODO: textAlign, textBaseline 948*c8dee2aaSAndroid Build Coastguard Worker }); 949*c8dee2aaSAndroid Build Coastguard Worker // Saves the clip 950*c8dee2aaSAndroid Build Coastguard Worker this._canvas.save(); 951*c8dee2aaSAndroid Build Coastguard Worker }; 952*c8dee2aaSAndroid Build Coastguard Worker 953*c8dee2aaSAndroid Build Coastguard Worker this.scale = function(sx, sy) { 954*c8dee2aaSAndroid Build Coastguard Worker if (!allAreFinite(arguments)) { 955*c8dee2aaSAndroid Build Coastguard Worker return; 956*c8dee2aaSAndroid Build Coastguard Worker } 957*c8dee2aaSAndroid Build Coastguard Worker // retroactively apply the inverse of this transform to the previous 958*c8dee2aaSAndroid Build Coastguard Worker // path so it cancels out when we apply the transform at draw time. 959*c8dee2aaSAndroid Build Coastguard Worker var inverted = CanvasKit.Matrix.scaled(1/sx, 1/sy); 960*c8dee2aaSAndroid Build Coastguard Worker this._currentPath.transform(inverted); 961*c8dee2aaSAndroid Build Coastguard Worker this._canvas.scale(sx, sy); 962*c8dee2aaSAndroid Build Coastguard Worker this._currentTransform = this._canvas.getTotalMatrix(); 963*c8dee2aaSAndroid Build Coastguard Worker }; 964*c8dee2aaSAndroid Build Coastguard Worker 965*c8dee2aaSAndroid Build Coastguard Worker this.setLineDash = function(dashes) { 966*c8dee2aaSAndroid Build Coastguard Worker for (var i = 0; i < dashes.length; i++) { 967*c8dee2aaSAndroid Build Coastguard Worker if (!isFinite(dashes[i]) || dashes[i] < 0) { 968*c8dee2aaSAndroid Build Coastguard Worker Debug('dash list must have positive, finite values'); 969*c8dee2aaSAndroid Build Coastguard Worker return; 970*c8dee2aaSAndroid Build Coastguard Worker } 971*c8dee2aaSAndroid Build Coastguard Worker } 972*c8dee2aaSAndroid Build Coastguard Worker if (dashes.length % 2 === 1) { 973*c8dee2aaSAndroid Build Coastguard Worker // as per the spec, concatenate 2 copies of dashes 974*c8dee2aaSAndroid Build Coastguard Worker // to give it an even number of elements. 975*c8dee2aaSAndroid Build Coastguard Worker Array.prototype.push.apply(dashes, dashes); 976*c8dee2aaSAndroid Build Coastguard Worker } 977*c8dee2aaSAndroid Build Coastguard Worker this._lineDashList = dashes; 978*c8dee2aaSAndroid Build Coastguard Worker }; 979*c8dee2aaSAndroid Build Coastguard Worker 980*c8dee2aaSAndroid Build Coastguard Worker this.setTransform = function(a, b, c, d, e, f) { 981*c8dee2aaSAndroid Build Coastguard Worker if (!(allAreFinite(arguments))) { 982*c8dee2aaSAndroid Build Coastguard Worker return; 983*c8dee2aaSAndroid Build Coastguard Worker } 984*c8dee2aaSAndroid Build Coastguard Worker this.resetTransform(); 985*c8dee2aaSAndroid Build Coastguard Worker this.transform(a, b, c, d, e, f); 986*c8dee2aaSAndroid Build Coastguard Worker }; 987*c8dee2aaSAndroid Build Coastguard Worker 988*c8dee2aaSAndroid Build Coastguard Worker // We need to apply the shadowOffsets on the device coordinates, so we undo 989*c8dee2aaSAndroid Build Coastguard Worker // the CTM, apply the offsets, then re-apply the CTM. 990*c8dee2aaSAndroid Build Coastguard Worker this._applyShadowOffsetMatrix = function() { 991*c8dee2aaSAndroid Build Coastguard Worker var inverted = CanvasKit.Matrix.invert(this._currentTransform); 992*c8dee2aaSAndroid Build Coastguard Worker this._canvas.concat(inverted); 993*c8dee2aaSAndroid Build Coastguard Worker this._canvas.concat(CanvasKit.Matrix.translated(this._shadowOffsetX, this._shadowOffsetY)); 994*c8dee2aaSAndroid Build Coastguard Worker this._canvas.concat(this._currentTransform); 995*c8dee2aaSAndroid Build Coastguard Worker }; 996*c8dee2aaSAndroid Build Coastguard Worker 997*c8dee2aaSAndroid Build Coastguard Worker // Returns the shadow paint for the current settings or null if there 998*c8dee2aaSAndroid Build Coastguard Worker // should be no shadow. This ends up being a copy of the given 999*c8dee2aaSAndroid Build Coastguard Worker // paint with a blur maskfilter and the correct color. 1000*c8dee2aaSAndroid Build Coastguard Worker this._shadowPaint = function(basePaint) { 1001*c8dee2aaSAndroid Build Coastguard Worker // multiply first to see if the alpha channel goes to 0 after multiplication. 1002*c8dee2aaSAndroid Build Coastguard Worker var alphaColor = CanvasKit.multiplyByAlpha(this._shadowColor, this._globalAlpha); 1003*c8dee2aaSAndroid Build Coastguard Worker // if alpha is zero, no shadows 1004*c8dee2aaSAndroid Build Coastguard Worker if (!CanvasKit.getColorComponents(alphaColor)[3]) { 1005*c8dee2aaSAndroid Build Coastguard Worker return null; 1006*c8dee2aaSAndroid Build Coastguard Worker } 1007*c8dee2aaSAndroid Build Coastguard Worker // one of these must also be non-zero (otherwise the shadow is 1008*c8dee2aaSAndroid Build Coastguard Worker // completely hidden. And the spec says so). 1009*c8dee2aaSAndroid Build Coastguard Worker if (!(this._shadowBlur || this._shadowOffsetY || this._shadowOffsetX)) { 1010*c8dee2aaSAndroid Build Coastguard Worker return null; 1011*c8dee2aaSAndroid Build Coastguard Worker } 1012*c8dee2aaSAndroid Build Coastguard Worker var shadowPaint = basePaint.copy(); 1013*c8dee2aaSAndroid Build Coastguard Worker shadowPaint.setColor(alphaColor); 1014*c8dee2aaSAndroid Build Coastguard Worker var blurEffect = CanvasKit.MaskFilter.MakeBlur(CanvasKit.BlurStyle.Normal, 1015*c8dee2aaSAndroid Build Coastguard Worker BlurRadiusToSigma(this._shadowBlur), 1016*c8dee2aaSAndroid Build Coastguard Worker false); 1017*c8dee2aaSAndroid Build Coastguard Worker shadowPaint.setMaskFilter(blurEffect); 1018*c8dee2aaSAndroid Build Coastguard Worker 1019*c8dee2aaSAndroid Build Coastguard Worker // hack up a "destructor" which also cleans up the blurEffect. Otherwise, 1020*c8dee2aaSAndroid Build Coastguard Worker // we leak the blurEffect (since smart pointers don't help us in JS land). 1021*c8dee2aaSAndroid Build Coastguard Worker shadowPaint.dispose = function() { 1022*c8dee2aaSAndroid Build Coastguard Worker blurEffect.delete(); 1023*c8dee2aaSAndroid Build Coastguard Worker this.delete(); 1024*c8dee2aaSAndroid Build Coastguard Worker }; 1025*c8dee2aaSAndroid Build Coastguard Worker return shadowPaint; 1026*c8dee2aaSAndroid Build Coastguard Worker }; 1027*c8dee2aaSAndroid Build Coastguard Worker 1028*c8dee2aaSAndroid Build Coastguard Worker // A helper to get a copy of the current paint, ready for stroking. 1029*c8dee2aaSAndroid Build Coastguard Worker // This applies the global alpha and the dashedness. 1030*c8dee2aaSAndroid Build Coastguard Worker // Call dispose() after to clean up. 1031*c8dee2aaSAndroid Build Coastguard Worker this._strokePaint = function() { 1032*c8dee2aaSAndroid Build Coastguard Worker var paint = this._paint.copy(); 1033*c8dee2aaSAndroid Build Coastguard Worker paint.setStyle(CanvasKit.PaintStyle.Stroke); 1034*c8dee2aaSAndroid Build Coastguard Worker if (isCanvasKitColor(this._strokeStyle)) { 1035*c8dee2aaSAndroid Build Coastguard Worker var alphaColor = CanvasKit.multiplyByAlpha(this._strokeStyle, this._globalAlpha); 1036*c8dee2aaSAndroid Build Coastguard Worker paint.setColor(alphaColor); 1037*c8dee2aaSAndroid Build Coastguard Worker } else { 1038*c8dee2aaSAndroid Build Coastguard Worker var shader = this._strokeStyle._getShader(this._currentTransform); 1039*c8dee2aaSAndroid Build Coastguard Worker paint.setColor(CanvasKit.Color(0,0,0, this._globalAlpha)); 1040*c8dee2aaSAndroid Build Coastguard Worker paint.setShader(shader); 1041*c8dee2aaSAndroid Build Coastguard Worker } 1042*c8dee2aaSAndroid Build Coastguard Worker 1043*c8dee2aaSAndroid Build Coastguard Worker paint.setStrokeWidth(this._strokeWidth); 1044*c8dee2aaSAndroid Build Coastguard Worker 1045*c8dee2aaSAndroid Build Coastguard Worker if (this._lineDashList.length) { 1046*c8dee2aaSAndroid Build Coastguard Worker var dashedEffect = CanvasKit.PathEffect.MakeDash(this._lineDashList, this._lineDashOffset); 1047*c8dee2aaSAndroid Build Coastguard Worker paint.setPathEffect(dashedEffect); 1048*c8dee2aaSAndroid Build Coastguard Worker } 1049*c8dee2aaSAndroid Build Coastguard Worker 1050*c8dee2aaSAndroid Build Coastguard Worker paint.dispose = function() { 1051*c8dee2aaSAndroid Build Coastguard Worker dashedEffect && dashedEffect.delete(); 1052*c8dee2aaSAndroid Build Coastguard Worker this.delete(); 1053*c8dee2aaSAndroid Build Coastguard Worker }; 1054*c8dee2aaSAndroid Build Coastguard Worker return paint; 1055*c8dee2aaSAndroid Build Coastguard Worker }; 1056*c8dee2aaSAndroid Build Coastguard Worker 1057*c8dee2aaSAndroid Build Coastguard Worker this.stroke = function(path) { 1058*c8dee2aaSAndroid Build Coastguard Worker path = path ? path._getPath() : this._currentPath; 1059*c8dee2aaSAndroid Build Coastguard Worker var strokePaint = this._strokePaint(); 1060*c8dee2aaSAndroid Build Coastguard Worker 1061*c8dee2aaSAndroid Build Coastguard Worker var shadowPaint = this._shadowPaint(strokePaint); 1062*c8dee2aaSAndroid Build Coastguard Worker if (shadowPaint) { 1063*c8dee2aaSAndroid Build Coastguard Worker this._canvas.save(); 1064*c8dee2aaSAndroid Build Coastguard Worker this._applyShadowOffsetMatrix(); 1065*c8dee2aaSAndroid Build Coastguard Worker this._canvas.drawPath(path, shadowPaint); 1066*c8dee2aaSAndroid Build Coastguard Worker this._canvas.restore(); 1067*c8dee2aaSAndroid Build Coastguard Worker shadowPaint.dispose(); 1068*c8dee2aaSAndroid Build Coastguard Worker } 1069*c8dee2aaSAndroid Build Coastguard Worker 1070*c8dee2aaSAndroid Build Coastguard Worker this._canvas.drawPath(path, strokePaint); 1071*c8dee2aaSAndroid Build Coastguard Worker strokePaint.dispose(); 1072*c8dee2aaSAndroid Build Coastguard Worker }; 1073*c8dee2aaSAndroid Build Coastguard Worker 1074*c8dee2aaSAndroid Build Coastguard Worker this.strokeRect = function(x, y, width, height) { 1075*c8dee2aaSAndroid Build Coastguard Worker var strokePaint = this._strokePaint(); 1076*c8dee2aaSAndroid Build Coastguard Worker 1077*c8dee2aaSAndroid Build Coastguard Worker var shadowPaint = this._shadowPaint(strokePaint); 1078*c8dee2aaSAndroid Build Coastguard Worker if (shadowPaint) { 1079*c8dee2aaSAndroid Build Coastguard Worker this._canvas.save(); 1080*c8dee2aaSAndroid Build Coastguard Worker this._applyShadowOffsetMatrix(); 1081*c8dee2aaSAndroid Build Coastguard Worker this._canvas.drawRect(CanvasKit.XYWHRect(x, y, width, height), shadowPaint); 1082*c8dee2aaSAndroid Build Coastguard Worker this._canvas.restore(); 1083*c8dee2aaSAndroid Build Coastguard Worker shadowPaint.dispose(); 1084*c8dee2aaSAndroid Build Coastguard Worker } 1085*c8dee2aaSAndroid Build Coastguard Worker this._canvas.drawRect(CanvasKit.XYWHRect(x, y, width, height), strokePaint); 1086*c8dee2aaSAndroid Build Coastguard Worker strokePaint.dispose(); 1087*c8dee2aaSAndroid Build Coastguard Worker }; 1088*c8dee2aaSAndroid Build Coastguard Worker 1089*c8dee2aaSAndroid Build Coastguard Worker this.strokeText = function(text, x, y, maxWidth) { 1090*c8dee2aaSAndroid Build Coastguard Worker // TODO do something with maxWidth, probably involving measure 1091*c8dee2aaSAndroid Build Coastguard Worker var strokePaint = this._strokePaint(); 1092*c8dee2aaSAndroid Build Coastguard Worker var blob = CanvasKit.TextBlob.MakeFromText(text, this._font); 1093*c8dee2aaSAndroid Build Coastguard Worker 1094*c8dee2aaSAndroid Build Coastguard Worker var shadowPaint = this._shadowPaint(strokePaint); 1095*c8dee2aaSAndroid Build Coastguard Worker if (shadowPaint) { 1096*c8dee2aaSAndroid Build Coastguard Worker this._canvas.save(); 1097*c8dee2aaSAndroid Build Coastguard Worker this._applyShadowOffsetMatrix(); 1098*c8dee2aaSAndroid Build Coastguard Worker this._canvas.drawTextBlob(blob, x, y, shadowPaint); 1099*c8dee2aaSAndroid Build Coastguard Worker this._canvas.restore(); 1100*c8dee2aaSAndroid Build Coastguard Worker shadowPaint.dispose(); 1101*c8dee2aaSAndroid Build Coastguard Worker } 1102*c8dee2aaSAndroid Build Coastguard Worker this._canvas.drawTextBlob(blob, x, y, strokePaint); 1103*c8dee2aaSAndroid Build Coastguard Worker blob.delete(); 1104*c8dee2aaSAndroid Build Coastguard Worker strokePaint.dispose(); 1105*c8dee2aaSAndroid Build Coastguard Worker }; 1106*c8dee2aaSAndroid Build Coastguard Worker 1107*c8dee2aaSAndroid Build Coastguard Worker this.translate = function(dx, dy) { 1108*c8dee2aaSAndroid Build Coastguard Worker if (!allAreFinite(arguments)) { 1109*c8dee2aaSAndroid Build Coastguard Worker return; 1110*c8dee2aaSAndroid Build Coastguard Worker } 1111*c8dee2aaSAndroid Build Coastguard Worker // retroactively apply the inverse of this transform to the previous 1112*c8dee2aaSAndroid Build Coastguard Worker // path so it cancels out when we apply the transform at draw time. 1113*c8dee2aaSAndroid Build Coastguard Worker var inverted = CanvasKit.Matrix.translated(-dx, -dy); 1114*c8dee2aaSAndroid Build Coastguard Worker this._currentPath.transform(inverted); 1115*c8dee2aaSAndroid Build Coastguard Worker this._canvas.translate(dx, dy); 1116*c8dee2aaSAndroid Build Coastguard Worker this._currentTransform = this._canvas.getTotalMatrix(); 1117*c8dee2aaSAndroid Build Coastguard Worker }; 1118*c8dee2aaSAndroid Build Coastguard Worker 1119*c8dee2aaSAndroid Build Coastguard Worker this.transform = function(a, b, c, d, e, f) { 1120*c8dee2aaSAndroid Build Coastguard Worker var newTransform = [a, c, e, 1121*c8dee2aaSAndroid Build Coastguard Worker b, d, f, 1122*c8dee2aaSAndroid Build Coastguard Worker 0, 0, 1]; 1123*c8dee2aaSAndroid Build Coastguard Worker // retroactively apply the inverse of this transform to the previous 1124*c8dee2aaSAndroid Build Coastguard Worker // path so it cancels out when we apply the transform at draw time. 1125*c8dee2aaSAndroid Build Coastguard Worker var inverted = CanvasKit.Matrix.invert(newTransform); 1126*c8dee2aaSAndroid Build Coastguard Worker this._currentPath.transform(inverted); 1127*c8dee2aaSAndroid Build Coastguard Worker this._canvas.concat(newTransform); 1128*c8dee2aaSAndroid Build Coastguard Worker this._currentTransform = this._canvas.getTotalMatrix(); 1129*c8dee2aaSAndroid Build Coastguard Worker }; 1130*c8dee2aaSAndroid Build Coastguard Worker 1131*c8dee2aaSAndroid Build Coastguard Worker // Not supported operations (e.g. for Web only) 1132*c8dee2aaSAndroid Build Coastguard Worker this.addHitRegion = function() {}; 1133*c8dee2aaSAndroid Build Coastguard Worker this.clearHitRegions = function() {}; 1134*c8dee2aaSAndroid Build Coastguard Worker this.drawFocusIfNeeded = function() {}; 1135*c8dee2aaSAndroid Build Coastguard Worker this.removeHitRegion = function() {}; 1136*c8dee2aaSAndroid Build Coastguard Worker this.scrollPathIntoView = function() {}; 1137*c8dee2aaSAndroid Build Coastguard Worker 1138*c8dee2aaSAndroid Build Coastguard Worker Object.defineProperty(this, 'canvas', { 1139*c8dee2aaSAndroid Build Coastguard Worker value: null, 1140*c8dee2aaSAndroid Build Coastguard Worker writable: false 1141*c8dee2aaSAndroid Build Coastguard Worker }); 1142*c8dee2aaSAndroid Build Coastguard Worker} 1143*c8dee2aaSAndroid Build Coastguard Worker 1144*c8dee2aaSAndroid Build Coastguard Workerfunction BlurRadiusToSigma(radius) { 1145*c8dee2aaSAndroid Build Coastguard Worker // Blink (Chrome) does the following, for legacy reasons, even though it 1146*c8dee2aaSAndroid Build Coastguard Worker // is against the spec. https://bugs.chromium.org/p/chromium/issues/detail?id=179006 1147*c8dee2aaSAndroid Build Coastguard Worker // This may change in future releases. 1148*c8dee2aaSAndroid Build Coastguard Worker // This code is staying here in case any clients are interested in using it 1149*c8dee2aaSAndroid Build Coastguard Worker // to match Blink "exactly". 1150*c8dee2aaSAndroid Build Coastguard Worker // if (radius <= 0) 1151*c8dee2aaSAndroid Build Coastguard Worker // return 0; 1152*c8dee2aaSAndroid Build Coastguard Worker // return 0.288675 * radius + 0.5; 1153*c8dee2aaSAndroid Build Coastguard Worker // 1154*c8dee2aaSAndroid Build Coastguard Worker // This is what the spec says, which is how Firefox and others operate. 1155*c8dee2aaSAndroid Build Coastguard Worker return radius/2; 1156*c8dee2aaSAndroid Build Coastguard Worker} 1157