1*c8dee2aaSAndroid Build Coastguard Workerdescribe('Canvas 2D emulation', () => { 2*c8dee2aaSAndroid Build Coastguard Worker let container; 3*c8dee2aaSAndroid Build Coastguard Worker 4*c8dee2aaSAndroid Build Coastguard Worker beforeEach(async () => { 5*c8dee2aaSAndroid Build Coastguard Worker await EverythingLoaded; 6*c8dee2aaSAndroid Build Coastguard Worker container = document.createElement('div'); 7*c8dee2aaSAndroid Build Coastguard Worker container.innerHTML = ` 8*c8dee2aaSAndroid Build Coastguard Worker <canvas width=600 height=600 id=test></canvas> 9*c8dee2aaSAndroid Build Coastguard Worker <canvas width=600 height=600 id=report></canvas>`; 10*c8dee2aaSAndroid Build Coastguard Worker document.body.appendChild(container); 11*c8dee2aaSAndroid Build Coastguard Worker }); 12*c8dee2aaSAndroid Build Coastguard Worker 13*c8dee2aaSAndroid Build Coastguard Worker afterEach(() => { 14*c8dee2aaSAndroid Build Coastguard Worker document.body.removeChild(container); 15*c8dee2aaSAndroid Build Coastguard Worker }); 16*c8dee2aaSAndroid Build Coastguard Worker 17*c8dee2aaSAndroid Build Coastguard Worker const expectColorCloseTo = (a, b) => { 18*c8dee2aaSAndroid Build Coastguard Worker expect(a.length).toEqual(4); 19*c8dee2aaSAndroid Build Coastguard Worker expect(b.length).toEqual(4); 20*c8dee2aaSAndroid Build Coastguard Worker for (let i=0; i<4; i++) { 21*c8dee2aaSAndroid Build Coastguard Worker expect(a[i]).toBeCloseTo(b[i], 3); 22*c8dee2aaSAndroid Build Coastguard Worker } 23*c8dee2aaSAndroid Build Coastguard Worker } 24*c8dee2aaSAndroid Build Coastguard Worker 25*c8dee2aaSAndroid Build Coastguard Worker describe('color strings', () => { 26*c8dee2aaSAndroid Build Coastguard Worker const hex = (s) => { 27*c8dee2aaSAndroid Build Coastguard Worker return parseInt(s, 16); 28*c8dee2aaSAndroid Build Coastguard Worker } 29*c8dee2aaSAndroid Build Coastguard Worker 30*c8dee2aaSAndroid Build Coastguard Worker it('parses hex color strings', () => { 31*c8dee2aaSAndroid Build Coastguard Worker const parseColor = CanvasKit.parseColorString; 32*c8dee2aaSAndroid Build Coastguard Worker expectColorCloseTo(parseColor('#FED'), 33*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Color(hex('FF'), hex('EE'), hex('DD'), 1)); 34*c8dee2aaSAndroid Build Coastguard Worker expectColorCloseTo(parseColor('#FEDC'), 35*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Color(hex('FF'), hex('EE'), hex('DD'), hex('CC')/255)); 36*c8dee2aaSAndroid Build Coastguard Worker expectColorCloseTo(parseColor('#fed'), 37*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Color(hex('FF'), hex('EE'), hex('DD'), 1)); 38*c8dee2aaSAndroid Build Coastguard Worker expectColorCloseTo(parseColor('#fedc'), 39*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Color(hex('FF'), hex('EE'), hex('DD'), hex('CC')/255)); 40*c8dee2aaSAndroid Build Coastguard Worker }); 41*c8dee2aaSAndroid Build Coastguard Worker it('parses rgba color strings', () => { 42*c8dee2aaSAndroid Build Coastguard Worker const parseColor = CanvasKit.parseColorString; 43*c8dee2aaSAndroid Build Coastguard Worker expectColorCloseTo(parseColor('rgba(117, 33, 64, 0.75)'), 44*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Color(117, 33, 64, 0.75)); 45*c8dee2aaSAndroid Build Coastguard Worker expectColorCloseTo(parseColor('rgb(117, 33, 64, 0.75)'), 46*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Color(117, 33, 64, 0.75)); 47*c8dee2aaSAndroid Build Coastguard Worker expectColorCloseTo(parseColor('rgba(117,33,64)'), 48*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Color(117, 33, 64, 1.0)); 49*c8dee2aaSAndroid Build Coastguard Worker expectColorCloseTo(parseColor('rgb(117,33, 64)'), 50*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Color(117, 33, 64, 1.0)); 51*c8dee2aaSAndroid Build Coastguard Worker expectColorCloseTo(parseColor('rgb(117,33, 64, 32%)'), 52*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Color(117, 33, 64, 0.32)); 53*c8dee2aaSAndroid Build Coastguard Worker expectColorCloseTo(parseColor('rgb(117,33, 64, 0.001)'), 54*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Color(117, 33, 64, 0.001)); 55*c8dee2aaSAndroid Build Coastguard Worker expectColorCloseTo(parseColor('rgb(117,33,64,0)'), 56*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Color(117, 33, 64, 0.0)); 57*c8dee2aaSAndroid Build Coastguard Worker }); 58*c8dee2aaSAndroid Build Coastguard Worker it('parses named color strings', () => { 59*c8dee2aaSAndroid Build Coastguard Worker // Keep this one as the _testing version, because we don't include the large 60*c8dee2aaSAndroid Build Coastguard Worker // color map by default. 61*c8dee2aaSAndroid Build Coastguard Worker const parseColor = CanvasKit._testing.parseColor; 62*c8dee2aaSAndroid Build Coastguard Worker expectColorCloseTo(parseColor('grey'), 63*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Color(128, 128, 128, 1.0)); 64*c8dee2aaSAndroid Build Coastguard Worker expectColorCloseTo(parseColor('blanchedalmond'), 65*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Color(255, 235, 205, 1.0)); 66*c8dee2aaSAndroid Build Coastguard Worker expectColorCloseTo(parseColor('transparent'), 67*c8dee2aaSAndroid Build Coastguard Worker CanvasKit.Color(0, 0, 0, 0)); 68*c8dee2aaSAndroid Build Coastguard Worker }); 69*c8dee2aaSAndroid Build Coastguard Worker 70*c8dee2aaSAndroid Build Coastguard Worker it('properly produces color strings', () => { 71*c8dee2aaSAndroid Build Coastguard Worker const colorToString = CanvasKit._testing.colorToString; 72*c8dee2aaSAndroid Build Coastguard Worker 73*c8dee2aaSAndroid Build Coastguard Worker expect(colorToString(CanvasKit.Color(102, 51, 153, 1.0))).toEqual('#663399'); 74*c8dee2aaSAndroid Build Coastguard Worker 75*c8dee2aaSAndroid Build Coastguard Worker expect(colorToString(CanvasKit.Color(255, 235, 205, 0.5))).toEqual( 76*c8dee2aaSAndroid Build Coastguard Worker 'rgba(255, 235, 205, 0.50000000)'); 77*c8dee2aaSAndroid Build Coastguard Worker }); 78*c8dee2aaSAndroid Build Coastguard Worker 79*c8dee2aaSAndroid Build Coastguard Worker it('can multiply colors by alpha', () => { 80*c8dee2aaSAndroid Build Coastguard Worker const multiplyByAlpha = CanvasKit.multiplyByAlpha; 81*c8dee2aaSAndroid Build Coastguard Worker 82*c8dee2aaSAndroid Build Coastguard Worker const testCases = [ 83*c8dee2aaSAndroid Build Coastguard Worker { 84*c8dee2aaSAndroid Build Coastguard Worker inColor: CanvasKit.Color(102, 51, 153, 1.0), 85*c8dee2aaSAndroid Build Coastguard Worker inAlpha: 1.0, 86*c8dee2aaSAndroid Build Coastguard Worker outColor: CanvasKit.Color(102, 51, 153, 1.0), 87*c8dee2aaSAndroid Build Coastguard Worker }, 88*c8dee2aaSAndroid Build Coastguard Worker { 89*c8dee2aaSAndroid Build Coastguard Worker inColor: CanvasKit.Color(102, 51, 153, 1.0), 90*c8dee2aaSAndroid Build Coastguard Worker inAlpha: 0.8, 91*c8dee2aaSAndroid Build Coastguard Worker outColor: CanvasKit.Color(102, 51, 153, 0.8), 92*c8dee2aaSAndroid Build Coastguard Worker }, 93*c8dee2aaSAndroid Build Coastguard Worker { 94*c8dee2aaSAndroid Build Coastguard Worker inColor: CanvasKit.Color(102, 51, 153, 0.8), 95*c8dee2aaSAndroid Build Coastguard Worker inAlpha: 0.7, 96*c8dee2aaSAndroid Build Coastguard Worker outColor: CanvasKit.Color(102, 51, 153, 0.56), 97*c8dee2aaSAndroid Build Coastguard Worker }, 98*c8dee2aaSAndroid Build Coastguard Worker { 99*c8dee2aaSAndroid Build Coastguard Worker inColor: CanvasKit.Color(102, 51, 153, 0.8), 100*c8dee2aaSAndroid Build Coastguard Worker inAlpha: 1000, 101*c8dee2aaSAndroid Build Coastguard Worker outColor: CanvasKit.Color(102, 51, 153, 1.0), 102*c8dee2aaSAndroid Build Coastguard Worker }, 103*c8dee2aaSAndroid Build Coastguard Worker ]; 104*c8dee2aaSAndroid Build Coastguard Worker 105*c8dee2aaSAndroid Build Coastguard Worker for (const tc of testCases) { 106*c8dee2aaSAndroid Build Coastguard Worker // Print out the test case if the two don't match. 107*c8dee2aaSAndroid Build Coastguard Worker expect(multiplyByAlpha(tc.inColor, tc.inAlpha)) 108*c8dee2aaSAndroid Build Coastguard Worker .toEqual(tc.outColor, JSON.stringify(tc)); 109*c8dee2aaSAndroid Build Coastguard Worker } 110*c8dee2aaSAndroid Build Coastguard Worker }); 111*c8dee2aaSAndroid Build Coastguard Worker }); // end describe('color string parsing') 112*c8dee2aaSAndroid Build Coastguard Worker 113*c8dee2aaSAndroid Build Coastguard Worker describe('fonts', () => { 114*c8dee2aaSAndroid Build Coastguard Worker it('can parse font sizes', () => { 115*c8dee2aaSAndroid Build Coastguard Worker const parseFontString = CanvasKit._testing.parseFontString; 116*c8dee2aaSAndroid Build Coastguard Worker 117*c8dee2aaSAndroid Build Coastguard Worker const tests = [{ 118*c8dee2aaSAndroid Build Coastguard Worker 'input': '10px monospace', 119*c8dee2aaSAndroid Build Coastguard Worker 'output': { 120*c8dee2aaSAndroid Build Coastguard Worker 'style': '', 121*c8dee2aaSAndroid Build Coastguard Worker 'variant': '', 122*c8dee2aaSAndroid Build Coastguard Worker 'weight': '', 123*c8dee2aaSAndroid Build Coastguard Worker 'sizePx': 10, 124*c8dee2aaSAndroid Build Coastguard Worker 'family': 'monospace', 125*c8dee2aaSAndroid Build Coastguard Worker } 126*c8dee2aaSAndroid Build Coastguard Worker }, 127*c8dee2aaSAndroid Build Coastguard Worker { 128*c8dee2aaSAndroid Build Coastguard Worker 'input': '15pt Arial', 129*c8dee2aaSAndroid Build Coastguard Worker 'output': { 130*c8dee2aaSAndroid Build Coastguard Worker 'style': '', 131*c8dee2aaSAndroid Build Coastguard Worker 'variant': '', 132*c8dee2aaSAndroid Build Coastguard Worker 'weight': '', 133*c8dee2aaSAndroid Build Coastguard Worker 'sizePx': 20, 134*c8dee2aaSAndroid Build Coastguard Worker 'family': 'Arial', 135*c8dee2aaSAndroid Build Coastguard Worker } 136*c8dee2aaSAndroid Build Coastguard Worker }, 137*c8dee2aaSAndroid Build Coastguard Worker { 138*c8dee2aaSAndroid Build Coastguard Worker 'input': '1.5in Arial, san-serif ', 139*c8dee2aaSAndroid Build Coastguard Worker 'output': { 140*c8dee2aaSAndroid Build Coastguard Worker 'style': '', 141*c8dee2aaSAndroid Build Coastguard Worker 'variant': '', 142*c8dee2aaSAndroid Build Coastguard Worker 'weight': '', 143*c8dee2aaSAndroid Build Coastguard Worker 'sizePx': 144, 144*c8dee2aaSAndroid Build Coastguard Worker 'family': 'Arial, san-serif', 145*c8dee2aaSAndroid Build Coastguard Worker } 146*c8dee2aaSAndroid Build Coastguard Worker }, 147*c8dee2aaSAndroid Build Coastguard Worker { 148*c8dee2aaSAndroid Build Coastguard Worker 'input': '1.5em SuperFont', 149*c8dee2aaSAndroid Build Coastguard Worker 'output': { 150*c8dee2aaSAndroid Build Coastguard Worker 'style': '', 151*c8dee2aaSAndroid Build Coastguard Worker 'variant': '', 152*c8dee2aaSAndroid Build Coastguard Worker 'weight': '', 153*c8dee2aaSAndroid Build Coastguard Worker 'sizePx': 24, 154*c8dee2aaSAndroid Build Coastguard Worker 'family': 'SuperFont', 155*c8dee2aaSAndroid Build Coastguard Worker } 156*c8dee2aaSAndroid Build Coastguard Worker }, 157*c8dee2aaSAndroid Build Coastguard Worker ]; 158*c8dee2aaSAndroid Build Coastguard Worker 159*c8dee2aaSAndroid Build Coastguard Worker for (let i = 0; i < tests.length; i++) { 160*c8dee2aaSAndroid Build Coastguard Worker expect(parseFontString(tests[i].input)).toEqual(tests[i].output); 161*c8dee2aaSAndroid Build Coastguard Worker } 162*c8dee2aaSAndroid Build Coastguard Worker }); 163*c8dee2aaSAndroid Build Coastguard Worker 164*c8dee2aaSAndroid Build Coastguard Worker it('can parse font attributes', () => { 165*c8dee2aaSAndroid Build Coastguard Worker const parseFontString = CanvasKit._testing.parseFontString; 166*c8dee2aaSAndroid Build Coastguard Worker 167*c8dee2aaSAndroid Build Coastguard Worker const tests = [{ 168*c8dee2aaSAndroid Build Coastguard Worker 'input': 'bold 10px monospace', 169*c8dee2aaSAndroid Build Coastguard Worker 'output': { 170*c8dee2aaSAndroid Build Coastguard Worker 'style': '', 171*c8dee2aaSAndroid Build Coastguard Worker 'variant': '', 172*c8dee2aaSAndroid Build Coastguard Worker 'weight': 'bold', 173*c8dee2aaSAndroid Build Coastguard Worker 'sizePx': 10, 174*c8dee2aaSAndroid Build Coastguard Worker 'family': 'monospace', 175*c8dee2aaSAndroid Build Coastguard Worker } 176*c8dee2aaSAndroid Build Coastguard Worker }, 177*c8dee2aaSAndroid Build Coastguard Worker { 178*c8dee2aaSAndroid Build Coastguard Worker 'input': 'italic bold 10px monospace', 179*c8dee2aaSAndroid Build Coastguard Worker 'output': { 180*c8dee2aaSAndroid Build Coastguard Worker 'style': 'italic', 181*c8dee2aaSAndroid Build Coastguard Worker 'variant': '', 182*c8dee2aaSAndroid Build Coastguard Worker 'weight': 'bold', 183*c8dee2aaSAndroid Build Coastguard Worker 'sizePx': 10, 184*c8dee2aaSAndroid Build Coastguard Worker 'family': 'monospace', 185*c8dee2aaSAndroid Build Coastguard Worker } 186*c8dee2aaSAndroid Build Coastguard Worker }, 187*c8dee2aaSAndroid Build Coastguard Worker { 188*c8dee2aaSAndroid Build Coastguard Worker 'input': 'italic small-caps bold 10px monospace', 189*c8dee2aaSAndroid Build Coastguard Worker 'output': { 190*c8dee2aaSAndroid Build Coastguard Worker 'style': 'italic', 191*c8dee2aaSAndroid Build Coastguard Worker 'variant': 'small-caps', 192*c8dee2aaSAndroid Build Coastguard Worker 'weight': 'bold', 193*c8dee2aaSAndroid Build Coastguard Worker 'sizePx': 10, 194*c8dee2aaSAndroid Build Coastguard Worker 'family': 'monospace', 195*c8dee2aaSAndroid Build Coastguard Worker } 196*c8dee2aaSAndroid Build Coastguard Worker }, 197*c8dee2aaSAndroid Build Coastguard Worker { 198*c8dee2aaSAndroid Build Coastguard Worker 'input': 'small-caps bold 10px monospace', 199*c8dee2aaSAndroid Build Coastguard Worker 'output': { 200*c8dee2aaSAndroid Build Coastguard Worker 'style': '', 201*c8dee2aaSAndroid Build Coastguard Worker 'variant': 'small-caps', 202*c8dee2aaSAndroid Build Coastguard Worker 'weight': 'bold', 203*c8dee2aaSAndroid Build Coastguard Worker 'sizePx': 10, 204*c8dee2aaSAndroid Build Coastguard Worker 'family': 'monospace', 205*c8dee2aaSAndroid Build Coastguard Worker } 206*c8dee2aaSAndroid Build Coastguard Worker }, 207*c8dee2aaSAndroid Build Coastguard Worker { 208*c8dee2aaSAndroid Build Coastguard Worker 'input': 'italic 10px monospace', 209*c8dee2aaSAndroid Build Coastguard Worker 'output': { 210*c8dee2aaSAndroid Build Coastguard Worker 'style': 'italic', 211*c8dee2aaSAndroid Build Coastguard Worker 'variant': '', 212*c8dee2aaSAndroid Build Coastguard Worker 'weight': '', 213*c8dee2aaSAndroid Build Coastguard Worker 'sizePx': 10, 214*c8dee2aaSAndroid Build Coastguard Worker 'family': 'monospace', 215*c8dee2aaSAndroid Build Coastguard Worker } 216*c8dee2aaSAndroid Build Coastguard Worker }, 217*c8dee2aaSAndroid Build Coastguard Worker { 218*c8dee2aaSAndroid Build Coastguard Worker 'input': 'small-caps 10px monospace', 219*c8dee2aaSAndroid Build Coastguard Worker 'output': { 220*c8dee2aaSAndroid Build Coastguard Worker 'style': '', 221*c8dee2aaSAndroid Build Coastguard Worker 'variant': 'small-caps', 222*c8dee2aaSAndroid Build Coastguard Worker 'weight': '', 223*c8dee2aaSAndroid Build Coastguard Worker 'sizePx': 10, 224*c8dee2aaSAndroid Build Coastguard Worker 'family': 'monospace', 225*c8dee2aaSAndroid Build Coastguard Worker } 226*c8dee2aaSAndroid Build Coastguard Worker }, 227*c8dee2aaSAndroid Build Coastguard Worker { 228*c8dee2aaSAndroid Build Coastguard Worker 'input': 'normal bold 10px monospace', 229*c8dee2aaSAndroid Build Coastguard Worker 'output': { 230*c8dee2aaSAndroid Build Coastguard Worker 'style': 'normal', 231*c8dee2aaSAndroid Build Coastguard Worker 'variant': '', 232*c8dee2aaSAndroid Build Coastguard Worker 'weight': 'bold', 233*c8dee2aaSAndroid Build Coastguard Worker 'sizePx': 10, 234*c8dee2aaSAndroid Build Coastguard Worker 'family': 'monospace', 235*c8dee2aaSAndroid Build Coastguard Worker } 236*c8dee2aaSAndroid Build Coastguard Worker }, 237*c8dee2aaSAndroid Build Coastguard Worker ]; 238*c8dee2aaSAndroid Build Coastguard Worker 239*c8dee2aaSAndroid Build Coastguard Worker for (let i = 0; i < tests.length; i++) { 240*c8dee2aaSAndroid Build Coastguard Worker expect(parseFontString(tests[i].input)).toEqual(tests[i].output); 241*c8dee2aaSAndroid Build Coastguard Worker } 242*c8dee2aaSAndroid Build Coastguard Worker }); 243*c8dee2aaSAndroid Build Coastguard Worker }); 244*c8dee2aaSAndroid Build Coastguard Worker 245*c8dee2aaSAndroid Build Coastguard Worker const multipleCanvasTest = (testname, done, test) => { 246*c8dee2aaSAndroid Build Coastguard Worker const skcanvas = CanvasKit.MakeCanvas(CANVAS_WIDTH, CANVAS_HEIGHT); 247*c8dee2aaSAndroid Build Coastguard Worker skcanvas._config = 'software_canvas'; 248*c8dee2aaSAndroid Build Coastguard Worker const realCanvas = document.getElementById('test'); 249*c8dee2aaSAndroid Build Coastguard Worker realCanvas._config = 'html_canvas'; 250*c8dee2aaSAndroid Build Coastguard Worker realCanvas.width = CANVAS_WIDTH; 251*c8dee2aaSAndroid Build Coastguard Worker realCanvas.height = CANVAS_HEIGHT; 252*c8dee2aaSAndroid Build Coastguard Worker 253*c8dee2aaSAndroid Build Coastguard Worker if (!done) { 254*c8dee2aaSAndroid Build Coastguard Worker console.log('debugging canvaskit'); 255*c8dee2aaSAndroid Build Coastguard Worker test(realCanvas); 256*c8dee2aaSAndroid Build Coastguard Worker test(skcanvas); 257*c8dee2aaSAndroid Build Coastguard Worker const png = skcanvas.toDataURL(); 258*c8dee2aaSAndroid Build Coastguard Worker const img = document.createElement('img'); 259*c8dee2aaSAndroid Build Coastguard Worker document.body.appendChild(img); 260*c8dee2aaSAndroid Build Coastguard Worker img.src = png; 261*c8dee2aaSAndroid Build Coastguard Worker debugger; 262*c8dee2aaSAndroid Build Coastguard Worker return; 263*c8dee2aaSAndroid Build Coastguard Worker } 264*c8dee2aaSAndroid Build Coastguard Worker 265*c8dee2aaSAndroid Build Coastguard Worker let promises = []; 266*c8dee2aaSAndroid Build Coastguard Worker 267*c8dee2aaSAndroid Build Coastguard Worker for (let canvas of [skcanvas, realCanvas]) { 268*c8dee2aaSAndroid Build Coastguard Worker test(canvas); 269*c8dee2aaSAndroid Build Coastguard Worker // canvas has .toDataURL (even though skcanvas is not a real Canvas) 270*c8dee2aaSAndroid Build Coastguard Worker // so this will work. 271*c8dee2aaSAndroid Build Coastguard Worker promises.push(reportCanvas(canvas, testname, canvas._config)); 272*c8dee2aaSAndroid Build Coastguard Worker } 273*c8dee2aaSAndroid Build Coastguard Worker Promise.all(promises).then(() => { 274*c8dee2aaSAndroid Build Coastguard Worker skcanvas.dispose(); 275*c8dee2aaSAndroid Build Coastguard Worker done(); 276*c8dee2aaSAndroid Build Coastguard Worker }).catch(reportError(done)); 277*c8dee2aaSAndroid Build Coastguard Worker } 278*c8dee2aaSAndroid Build Coastguard Worker 279*c8dee2aaSAndroid Build Coastguard Worker describe('CanvasContext2D API', () => { 280*c8dee2aaSAndroid Build Coastguard Worker multipleCanvasGM('all_line_drawing_operations', (canvas) => { 281*c8dee2aaSAndroid Build Coastguard Worker const ctx = canvas.getContext('2d'); 282*c8dee2aaSAndroid Build Coastguard Worker ctx.scale(3.0, 3.0); 283*c8dee2aaSAndroid Build Coastguard Worker ctx.moveTo(20, 5); 284*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(30, 20); 285*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(40, 10); 286*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(50, 20); 287*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(60, 0); 288*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(20, 5); 289*c8dee2aaSAndroid Build Coastguard Worker 290*c8dee2aaSAndroid Build Coastguard Worker ctx.moveTo(20, 80); 291*c8dee2aaSAndroid Build Coastguard Worker ctx.bezierCurveTo(90, 10, 160, 150, 190, 10); 292*c8dee2aaSAndroid Build Coastguard Worker 293*c8dee2aaSAndroid Build Coastguard Worker ctx.moveTo(36, 148); 294*c8dee2aaSAndroid Build Coastguard Worker ctx.quadraticCurveTo(66, 188, 120, 136); 295*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(36, 148); 296*c8dee2aaSAndroid Build Coastguard Worker 297*c8dee2aaSAndroid Build Coastguard Worker ctx.rect(5, 170, 20, 25); 298*c8dee2aaSAndroid Build Coastguard Worker 299*c8dee2aaSAndroid Build Coastguard Worker ctx.moveTo(150, 180); 300*c8dee2aaSAndroid Build Coastguard Worker ctx.arcTo(150, 100, 50, 200, 20); 301*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(160, 160); 302*c8dee2aaSAndroid Build Coastguard Worker 303*c8dee2aaSAndroid Build Coastguard Worker ctx.moveTo(20, 120); 304*c8dee2aaSAndroid Build Coastguard Worker ctx.arc(20, 120, 18, 0, 1.75 * Math.PI); 305*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(20, 120); 306*c8dee2aaSAndroid Build Coastguard Worker 307*c8dee2aaSAndroid Build Coastguard Worker ctx.moveTo(150, 5); 308*c8dee2aaSAndroid Build Coastguard Worker ctx.ellipse(130, 25, 30, 10, -1*Math.PI/8, Math.PI/6, 1.5*Math.PI) 309*c8dee2aaSAndroid Build Coastguard Worker 310*c8dee2aaSAndroid Build Coastguard Worker ctx.lineWidth = 2; 311*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 312*c8dee2aaSAndroid Build Coastguard Worker 313*c8dee2aaSAndroid Build Coastguard Worker // Test edgecases and draw direction 314*c8dee2aaSAndroid Build Coastguard Worker ctx.beginPath(); 315*c8dee2aaSAndroid Build Coastguard Worker ctx.arc(50, 100, 10, Math.PI, -Math.PI/2); 316*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 317*c8dee2aaSAndroid Build Coastguard Worker ctx.beginPath(); 318*c8dee2aaSAndroid Build Coastguard Worker ctx.arc(75, 100, 10, Math.PI, -Math.PI/2, true); 319*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 320*c8dee2aaSAndroid Build Coastguard Worker ctx.beginPath(); 321*c8dee2aaSAndroid Build Coastguard Worker ctx.arc(100, 100, 10, Math.PI, 100.1 * Math.PI, true); 322*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 323*c8dee2aaSAndroid Build Coastguard Worker ctx.beginPath(); 324*c8dee2aaSAndroid Build Coastguard Worker ctx.arc(125, 100, 10, Math.PI, 100.1 * Math.PI, false); 325*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 326*c8dee2aaSAndroid Build Coastguard Worker ctx.beginPath(); 327*c8dee2aaSAndroid Build Coastguard Worker ctx.ellipse(155, 100, 10, 15, Math.PI/8, 100.1 * Math.PI, Math.PI, true); 328*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 329*c8dee2aaSAndroid Build Coastguard Worker ctx.beginPath(); 330*c8dee2aaSAndroid Build Coastguard Worker ctx.ellipse(180, 100, 10, 15, Math.PI/8, Math.PI, 100.1 * Math.PI, true); 331*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 332*c8dee2aaSAndroid Build Coastguard Worker }); 333*c8dee2aaSAndroid Build Coastguard Worker 334*c8dee2aaSAndroid Build Coastguard Worker multipleCanvasGM('all_matrix_operations', (canvas) => { 335*c8dee2aaSAndroid Build Coastguard Worker const ctx = canvas.getContext('2d'); 336*c8dee2aaSAndroid Build Coastguard Worker ctx.rect(10, 10, 20, 20); 337*c8dee2aaSAndroid Build Coastguard Worker 338*c8dee2aaSAndroid Build Coastguard Worker ctx.scale(2.0, 4.0); 339*c8dee2aaSAndroid Build Coastguard Worker ctx.rect(30, 10, 20, 20); 340*c8dee2aaSAndroid Build Coastguard Worker ctx.resetTransform(); 341*c8dee2aaSAndroid Build Coastguard Worker 342*c8dee2aaSAndroid Build Coastguard Worker ctx.rotate(Math.PI / 3); 343*c8dee2aaSAndroid Build Coastguard Worker ctx.rect(50, 10, 20, 20); 344*c8dee2aaSAndroid Build Coastguard Worker ctx.resetTransform(); 345*c8dee2aaSAndroid Build Coastguard Worker 346*c8dee2aaSAndroid Build Coastguard Worker ctx.translate(30, -2); 347*c8dee2aaSAndroid Build Coastguard Worker ctx.rect(70, 10, 20, 20); 348*c8dee2aaSAndroid Build Coastguard Worker ctx.resetTransform(); 349*c8dee2aaSAndroid Build Coastguard Worker 350*c8dee2aaSAndroid Build Coastguard Worker ctx.translate(60, 0); 351*c8dee2aaSAndroid Build Coastguard Worker ctx.rotate(Math.PI / 6); 352*c8dee2aaSAndroid Build Coastguard Worker ctx.transform(1.5, 0, 0, 0.5, 0, 0); // effectively scale 353*c8dee2aaSAndroid Build Coastguard Worker ctx.rect(90, 10, 20, 20); 354*c8dee2aaSAndroid Build Coastguard Worker ctx.resetTransform(); 355*c8dee2aaSAndroid Build Coastguard Worker 356*c8dee2aaSAndroid Build Coastguard Worker ctx.save(); 357*c8dee2aaSAndroid Build Coastguard Worker ctx.setTransform(2, 0, -.5, 2.5, -40, 120); 358*c8dee2aaSAndroid Build Coastguard Worker ctx.rect(110, 10, 20, 20); 359*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(110, 0); 360*c8dee2aaSAndroid Build Coastguard Worker ctx.restore(); 361*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(220, 120); 362*c8dee2aaSAndroid Build Coastguard Worker 363*c8dee2aaSAndroid Build Coastguard Worker ctx.scale(3.0, 3.0); 364*c8dee2aaSAndroid Build Coastguard Worker ctx.font = '6pt Noto Mono'; 365*c8dee2aaSAndroid Build Coastguard Worker ctx.fillText('This text should be huge', 10, 80); 366*c8dee2aaSAndroid Build Coastguard Worker ctx.resetTransform(); 367*c8dee2aaSAndroid Build Coastguard Worker 368*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeStyle = 'black'; 369*c8dee2aaSAndroid Build Coastguard Worker ctx.lineWidth = 2; 370*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 371*c8dee2aaSAndroid Build Coastguard Worker 372*c8dee2aaSAndroid Build Coastguard Worker ctx.beginPath(); 373*c8dee2aaSAndroid Build Coastguard Worker ctx.moveTo(250, 30); 374*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(250, 80); 375*c8dee2aaSAndroid Build Coastguard Worker ctx.scale(3.0, 3.0); 376*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(280/3, 90/3); 377*c8dee2aaSAndroid Build Coastguard Worker ctx.closePath(); 378*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeStyle = 'black'; 379*c8dee2aaSAndroid Build Coastguard Worker ctx.lineWidth = 5; 380*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 381*c8dee2aaSAndroid Build Coastguard Worker }); 382*c8dee2aaSAndroid Build Coastguard Worker 383*c8dee2aaSAndroid Build Coastguard Worker multipleCanvasGM('shadows_and_save_restore', (canvas) => { 384*c8dee2aaSAndroid Build Coastguard Worker const ctx = canvas.getContext('2d'); 385*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeStyle = '#000'; 386*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = '#CCC'; 387*c8dee2aaSAndroid Build Coastguard Worker ctx.shadowColor = 'rebeccapurple'; 388*c8dee2aaSAndroid Build Coastguard Worker ctx.shadowBlur = 1; 389*c8dee2aaSAndroid Build Coastguard Worker ctx.shadowOffsetX = 3; 390*c8dee2aaSAndroid Build Coastguard Worker ctx.shadowOffsetY = -8; 391*c8dee2aaSAndroid Build Coastguard Worker ctx.rect(10, 10, 30, 30); 392*c8dee2aaSAndroid Build Coastguard Worker 393*c8dee2aaSAndroid Build Coastguard Worker ctx.save(); 394*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeStyle = '#C00'; 395*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = '#00C'; 396*c8dee2aaSAndroid Build Coastguard Worker ctx.shadowBlur = 0; 397*c8dee2aaSAndroid Build Coastguard Worker ctx.shadowColor = 'transparent'; 398*c8dee2aaSAndroid Build Coastguard Worker 399*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 400*c8dee2aaSAndroid Build Coastguard Worker 401*c8dee2aaSAndroid Build Coastguard Worker ctx.restore(); 402*c8dee2aaSAndroid Build Coastguard Worker ctx.fill(); 403*c8dee2aaSAndroid Build Coastguard Worker 404*c8dee2aaSAndroid Build Coastguard Worker ctx.beginPath(); 405*c8dee2aaSAndroid Build Coastguard Worker ctx.moveTo(36, 148); 406*c8dee2aaSAndroid Build Coastguard Worker ctx.quadraticCurveTo(66, 188, 120, 136); 407*c8dee2aaSAndroid Build Coastguard Worker ctx.closePath(); 408*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 409*c8dee2aaSAndroid Build Coastguard Worker 410*c8dee2aaSAndroid Build Coastguard Worker ctx.beginPath(); 411*c8dee2aaSAndroid Build Coastguard Worker ctx.shadowColor = '#993366AA'; 412*c8dee2aaSAndroid Build Coastguard Worker ctx.shadowOffsetX = 8; 413*c8dee2aaSAndroid Build Coastguard Worker ctx.shadowBlur = 5; 414*c8dee2aaSAndroid Build Coastguard Worker ctx.setTransform(2, 0, -.5, 2.5, -40, 120); 415*c8dee2aaSAndroid Build Coastguard Worker ctx.rect(110, 10, 20, 20); 416*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(110, 0); 417*c8dee2aaSAndroid Build Coastguard Worker ctx.resetTransform(); 418*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(220, 120); 419*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 420*c8dee2aaSAndroid Build Coastguard Worker 421*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = 'green'; 422*c8dee2aaSAndroid Build Coastguard Worker ctx.font = '16pt Noto Mono'; 423*c8dee2aaSAndroid Build Coastguard Worker ctx.fillText('This should be shadowed', 20, 80); 424*c8dee2aaSAndroid Build Coastguard Worker 425*c8dee2aaSAndroid Build Coastguard Worker ctx.beginPath(); 426*c8dee2aaSAndroid Build Coastguard Worker ctx.lineWidth = 6; 427*c8dee2aaSAndroid Build Coastguard Worker ctx.ellipse(10, 290, 30, 30, 0, 0, Math.PI * 2); 428*c8dee2aaSAndroid Build Coastguard Worker ctx.scale(2, 1); 429*c8dee2aaSAndroid Build Coastguard Worker ctx.moveTo(10, 290) 430*c8dee2aaSAndroid Build Coastguard Worker ctx.ellipse(10, 290, 30, 60, 0, 0, Math.PI * 2); 431*c8dee2aaSAndroid Build Coastguard Worker ctx.resetTransform(); 432*c8dee2aaSAndroid Build Coastguard Worker ctx.shadowColor = '#993366AA'; 433*c8dee2aaSAndroid Build Coastguard Worker ctx.scale(3, 1); 434*c8dee2aaSAndroid Build Coastguard Worker ctx.moveTo(10, 290) 435*c8dee2aaSAndroid Build Coastguard Worker ctx.ellipse(10, 290, 30, 90, 0, 0, Math.PI * 2); 436*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 437*c8dee2aaSAndroid Build Coastguard Worker }); 438*c8dee2aaSAndroid Build Coastguard Worker 439*c8dee2aaSAndroid Build Coastguard Worker multipleCanvasGM('global_dashed_rects', (canvas) => { 440*c8dee2aaSAndroid Build Coastguard Worker const ctx = canvas.getContext('2d'); 441*c8dee2aaSAndroid Build Coastguard Worker ctx.scale(1.1, 1.1); 442*c8dee2aaSAndroid Build Coastguard Worker ctx.translate(10, 10); 443*c8dee2aaSAndroid Build Coastguard Worker // Shouldn't impact the fillRect calls 444*c8dee2aaSAndroid Build Coastguard Worker ctx.setLineDash([5, 3]); 445*c8dee2aaSAndroid Build Coastguard Worker 446*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = 'rgba(200, 0, 100, 0.81)'; 447*c8dee2aaSAndroid Build Coastguard Worker ctx.fillRect(20, 30, 100, 100); 448*c8dee2aaSAndroid Build Coastguard Worker 449*c8dee2aaSAndroid Build Coastguard Worker ctx.globalAlpha = 0.81; 450*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = 'rgba(200, 0, 100, 1.0)'; 451*c8dee2aaSAndroid Build Coastguard Worker ctx.fillRect(120, 30, 100, 100); 452*c8dee2aaSAndroid Build Coastguard Worker // This shouldn't do anything 453*c8dee2aaSAndroid Build Coastguard Worker ctx.globalAlpha = 0.1; 454*c8dee2aaSAndroid Build Coastguard Worker 455*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = 'rgba(200, 0, 100, 0.9)'; 456*c8dee2aaSAndroid Build Coastguard Worker ctx.globalAlpha = 0.9; 457*c8dee2aaSAndroid Build Coastguard Worker // Intentional no-op to check ordering 458*c8dee2aaSAndroid Build Coastguard Worker ctx.clearRect(220, 30, 100, 100); 459*c8dee2aaSAndroid Build Coastguard Worker ctx.fillRect(220, 30, 100, 100); 460*c8dee2aaSAndroid Build Coastguard Worker 461*c8dee2aaSAndroid Build Coastguard Worker ctx.fillRect(320, 30, 100, 100); 462*c8dee2aaSAndroid Build Coastguard Worker ctx.clearRect(330, 40, 80, 80); 463*c8dee2aaSAndroid Build Coastguard Worker 464*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeStyle = 'blue'; 465*c8dee2aaSAndroid Build Coastguard Worker ctx.lineWidth = 3; 466*c8dee2aaSAndroid Build Coastguard Worker ctx.setLineDash([5, 3]); 467*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeRect(20, 150, 100, 100); 468*c8dee2aaSAndroid Build Coastguard Worker ctx.setLineDash([50, 30]); 469*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeRect(125, 150, 100, 100); 470*c8dee2aaSAndroid Build Coastguard Worker ctx.lineDashOffset = 25; 471*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeRect(230, 150, 100, 100); 472*c8dee2aaSAndroid Build Coastguard Worker ctx.setLineDash([2, 5, 9]); 473*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeRect(335, 150, 100, 100); 474*c8dee2aaSAndroid Build Coastguard Worker 475*c8dee2aaSAndroid Build Coastguard Worker ctx.setLineDash([5, 2]); 476*c8dee2aaSAndroid Build Coastguard Worker ctx.moveTo(336, 400); 477*c8dee2aaSAndroid Build Coastguard Worker ctx.quadraticCurveTo(366, 488, 120, 450); 478*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(300, 400); 479*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 480*c8dee2aaSAndroid Build Coastguard Worker 481*c8dee2aaSAndroid Build Coastguard Worker ctx.font = '36pt Noto Mono'; 482*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeText('Dashed', 20, 350); 483*c8dee2aaSAndroid Build Coastguard Worker ctx.fillText('Not Dashed', 20, 400); 484*c8dee2aaSAndroid Build Coastguard Worker }); 485*c8dee2aaSAndroid Build Coastguard Worker 486*c8dee2aaSAndroid Build Coastguard Worker multipleCanvasGM('gradients_clip', (canvas) => { 487*c8dee2aaSAndroid Build Coastguard Worker const ctx = canvas.getContext('2d'); 488*c8dee2aaSAndroid Build Coastguard Worker 489*c8dee2aaSAndroid Build Coastguard Worker const rgradient = ctx.createRadialGradient(200, 300, 10, 100, 100, 300); 490*c8dee2aaSAndroid Build Coastguard Worker 491*c8dee2aaSAndroid Build Coastguard Worker rgradient.addColorStop(0, 'red'); 492*c8dee2aaSAndroid Build Coastguard Worker rgradient.addColorStop(.7, 'white'); 493*c8dee2aaSAndroid Build Coastguard Worker rgradient.addColorStop(1, 'blue'); 494*c8dee2aaSAndroid Build Coastguard Worker 495*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = rgradient; 496*c8dee2aaSAndroid Build Coastguard Worker ctx.globalAlpha = 0.7; 497*c8dee2aaSAndroid Build Coastguard Worker ctx.fillRect(0,0,600,600); 498*c8dee2aaSAndroid Build Coastguard Worker ctx.globalAlpha = 0.95; 499*c8dee2aaSAndroid Build Coastguard Worker 500*c8dee2aaSAndroid Build Coastguard Worker ctx.beginPath(); 501*c8dee2aaSAndroid Build Coastguard Worker ctx.arc(300, 100, 90, 0, Math.PI*1.66); 502*c8dee2aaSAndroid Build Coastguard Worker ctx.closePath(); 503*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeStyle = 'yellow'; 504*c8dee2aaSAndroid Build Coastguard Worker ctx.lineWidth = 5; 505*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 506*c8dee2aaSAndroid Build Coastguard Worker ctx.save(); 507*c8dee2aaSAndroid Build Coastguard Worker ctx.clip(); 508*c8dee2aaSAndroid Build Coastguard Worker 509*c8dee2aaSAndroid Build Coastguard Worker const lgradient = ctx.createLinearGradient(200, 20, 420, 40); 510*c8dee2aaSAndroid Build Coastguard Worker 511*c8dee2aaSAndroid Build Coastguard Worker lgradient.addColorStop(0, 'green'); 512*c8dee2aaSAndroid Build Coastguard Worker lgradient.addColorStop(.5, 'cyan'); 513*c8dee2aaSAndroid Build Coastguard Worker lgradient.addColorStop(1, 'orange'); 514*c8dee2aaSAndroid Build Coastguard Worker 515*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = lgradient; 516*c8dee2aaSAndroid Build Coastguard Worker 517*c8dee2aaSAndroid Build Coastguard Worker ctx.fillRect(200, 30, 200, 300); 518*c8dee2aaSAndroid Build Coastguard Worker 519*c8dee2aaSAndroid Build Coastguard Worker ctx.restore(); 520*c8dee2aaSAndroid Build Coastguard Worker ctx.fillRect(550, 550, 40, 40); 521*c8dee2aaSAndroid Build Coastguard Worker }); 522*c8dee2aaSAndroid Build Coastguard Worker 523*c8dee2aaSAndroid Build Coastguard Worker multipleCanvasGM('get_put_imagedata', (canvas) => { 524*c8dee2aaSAndroid Build Coastguard Worker const ctx = canvas.getContext('2d'); 525*c8dee2aaSAndroid Build Coastguard Worker // Make a gradient so we see if the pixels copying worked 526*c8dee2aaSAndroid Build Coastguard Worker const grad = ctx.createLinearGradient(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); 527*c8dee2aaSAndroid Build Coastguard Worker grad.addColorStop(0, 'yellow'); 528*c8dee2aaSAndroid Build Coastguard Worker grad.addColorStop(1, 'red'); 529*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = grad; 530*c8dee2aaSAndroid Build Coastguard Worker ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); 531*c8dee2aaSAndroid Build Coastguard Worker 532*c8dee2aaSAndroid Build Coastguard Worker const iData = ctx.getImageData(400, 100, 200, 150); 533*c8dee2aaSAndroid Build Coastguard Worker expect(iData.width).toBe(200); 534*c8dee2aaSAndroid Build Coastguard Worker expect(iData.height).toBe(150); 535*c8dee2aaSAndroid Build Coastguard Worker expect(iData.data.byteLength).toBe(200*150*4); 536*c8dee2aaSAndroid Build Coastguard Worker ctx.putImageData(iData, 10, 10); 537*c8dee2aaSAndroid Build Coastguard Worker ctx.putImageData(iData, 350, 350, 100, 75, 45, 40); 538*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeRect(350, 350, 200, 150); 539*c8dee2aaSAndroid Build Coastguard Worker 540*c8dee2aaSAndroid Build Coastguard Worker const box = ctx.createImageData(20, 40); 541*c8dee2aaSAndroid Build Coastguard Worker ctx.putImageData(box, 10, 300); 542*c8dee2aaSAndroid Build Coastguard Worker const biggerBox = ctx.createImageData(iData); 543*c8dee2aaSAndroid Build Coastguard Worker ctx.putImageData(biggerBox, 10, 350); 544*c8dee2aaSAndroid Build Coastguard Worker expect(biggerBox.width).toBe(iData.width); 545*c8dee2aaSAndroid Build Coastguard Worker expect(biggerBox.height).toBe(iData.height); 546*c8dee2aaSAndroid Build Coastguard Worker }); 547*c8dee2aaSAndroid Build Coastguard Worker 548*c8dee2aaSAndroid Build Coastguard Worker multipleCanvasGM('shadows_with_rotate_skbug_9947', (canvas) => { 549*c8dee2aaSAndroid Build Coastguard Worker const ctx = canvas.getContext('2d'); 550*c8dee2aaSAndroid Build Coastguard Worker const angle = 240; 551*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = 'white'; 552*c8dee2aaSAndroid Build Coastguard Worker ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); 553*c8dee2aaSAndroid Build Coastguard Worker ctx.save(); 554*c8dee2aaSAndroid Build Coastguard Worker ctx.translate(80, 80); 555*c8dee2aaSAndroid Build Coastguard Worker ctx.rotate((angle * Math.PI) / 180); 556*c8dee2aaSAndroid Build Coastguard Worker ctx.shadowOffsetX = 10; 557*c8dee2aaSAndroid Build Coastguard Worker ctx.shadowOffsetY = 10; 558*c8dee2aaSAndroid Build Coastguard Worker ctx.shadowColor = 'rgba(100,100,100,0.5)'; 559*c8dee2aaSAndroid Build Coastguard Worker ctx.shadowBlur = 1; 560*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = 'black'; 561*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeStyle = 'red'; 562*c8dee2aaSAndroid Build Coastguard Worker ctx.beginPath(); 563*c8dee2aaSAndroid Build Coastguard Worker ctx.rect(-20, -20, 40, 40); 564*c8dee2aaSAndroid Build Coastguard Worker ctx.fill(); 565*c8dee2aaSAndroid Build Coastguard Worker ctx.fillRect(30, 30, 40, 40); 566*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeRect(30, -20, 40, 40); 567*c8dee2aaSAndroid Build Coastguard Worker ctx.fillText('text', -20, -30); 568*c8dee2aaSAndroid Build Coastguard Worker ctx.restore(); 569*c8dee2aaSAndroid Build Coastguard Worker }); 570*c8dee2aaSAndroid Build Coastguard Worker 571*c8dee2aaSAndroid Build Coastguard Worker describe('using images', () => { 572*c8dee2aaSAndroid Build Coastguard Worker let skImageData = null; 573*c8dee2aaSAndroid Build Coastguard Worker let htmlImage = null; 574*c8dee2aaSAndroid Build Coastguard Worker const skPromise = fetch('/assets/mandrill_512.png') 575*c8dee2aaSAndroid Build Coastguard Worker .then((response) => response.arrayBuffer()) 576*c8dee2aaSAndroid Build Coastguard Worker .then((buffer) => { 577*c8dee2aaSAndroid Build Coastguard Worker skImageData = buffer; 578*c8dee2aaSAndroid Build Coastguard Worker 579*c8dee2aaSAndroid Build Coastguard Worker }); 580*c8dee2aaSAndroid Build Coastguard Worker const realPromise = fetch('/assets/mandrill_512.png') 581*c8dee2aaSAndroid Build Coastguard Worker .then((response) => response.blob()) 582*c8dee2aaSAndroid Build Coastguard Worker .then((blob) => createImageBitmap(blob)) 583*c8dee2aaSAndroid Build Coastguard Worker .then((bitmap) => { 584*c8dee2aaSAndroid Build Coastguard Worker htmlImage = bitmap; 585*c8dee2aaSAndroid Build Coastguard Worker }); 586*c8dee2aaSAndroid Build Coastguard Worker 587*c8dee2aaSAndroid Build Coastguard Worker beforeEach(async () => { 588*c8dee2aaSAndroid Build Coastguard Worker await skPromise; 589*c8dee2aaSAndroid Build Coastguard Worker await realPromise; 590*c8dee2aaSAndroid Build Coastguard Worker }); 591*c8dee2aaSAndroid Build Coastguard Worker 592*c8dee2aaSAndroid Build Coastguard Worker multipleCanvasGM('draw_patterns', (canvas) => { 593*c8dee2aaSAndroid Build Coastguard Worker const ctx = canvas.getContext('2d'); 594*c8dee2aaSAndroid Build Coastguard Worker let img = htmlImage; 595*c8dee2aaSAndroid Build Coastguard Worker if (canvas._config === 'software_canvas') { 596*c8dee2aaSAndroid Build Coastguard Worker img = canvas.decodeImage(skImageData); 597*c8dee2aaSAndroid Build Coastguard Worker } 598*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = '#EEE'; 599*c8dee2aaSAndroid Build Coastguard Worker ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); 600*c8dee2aaSAndroid Build Coastguard Worker ctx.lineWidth = 20; 601*c8dee2aaSAndroid Build Coastguard Worker ctx.scale(0.2, 0.4); 602*c8dee2aaSAndroid Build Coastguard Worker 603*c8dee2aaSAndroid Build Coastguard Worker let pattern = ctx.createPattern(img, 'repeat'); 604*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = pattern; 605*c8dee2aaSAndroid Build Coastguard Worker ctx.fillRect(0, 0, 1500, 750); 606*c8dee2aaSAndroid Build Coastguard Worker 607*c8dee2aaSAndroid Build Coastguard Worker pattern = ctx.createPattern(img, 'repeat-x'); 608*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = pattern; 609*c8dee2aaSAndroid Build Coastguard Worker ctx.fillRect(1500, 0, 3000, 750); 610*c8dee2aaSAndroid Build Coastguard Worker 611*c8dee2aaSAndroid Build Coastguard Worker ctx.globalAlpha = 0.7 612*c8dee2aaSAndroid Build Coastguard Worker pattern = ctx.createPattern(img, 'repeat-y'); 613*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = pattern; 614*c8dee2aaSAndroid Build Coastguard Worker ctx.fillRect(0, 750, 1500, 1500); 615*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeRect(0, 750, 1500, 1500); 616*c8dee2aaSAndroid Build Coastguard Worker 617*c8dee2aaSAndroid Build Coastguard Worker pattern = ctx.createPattern(img, 'no-repeat'); 618*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = pattern; 619*c8dee2aaSAndroid Build Coastguard Worker pattern.setTransform({a: 1, b: -.1, c:.1, d: 0.5, e: 1800, f:800}); 620*c8dee2aaSAndroid Build Coastguard Worker ctx.fillRect(0, 0, 3000, 1500); 621*c8dee2aaSAndroid Build Coastguard Worker }); 622*c8dee2aaSAndroid Build Coastguard Worker 623*c8dee2aaSAndroid Build Coastguard Worker multipleCanvasGM('draw_image', (canvas) => { 624*c8dee2aaSAndroid Build Coastguard Worker let ctx = canvas.getContext('2d'); 625*c8dee2aaSAndroid Build Coastguard Worker let img = htmlImage; 626*c8dee2aaSAndroid Build Coastguard Worker if (canvas._config === 'software_canvas') { 627*c8dee2aaSAndroid Build Coastguard Worker img = canvas.decodeImage(skImageData); 628*c8dee2aaSAndroid Build Coastguard Worker } 629*c8dee2aaSAndroid Build Coastguard Worker ctx.drawImage(img, 30, -200); 630*c8dee2aaSAndroid Build Coastguard Worker 631*c8dee2aaSAndroid Build Coastguard Worker ctx.globalAlpha = 0.7 632*c8dee2aaSAndroid Build Coastguard Worker ctx.rotate(.1); 633*c8dee2aaSAndroid Build Coastguard Worker ctx.imageSmoothingQuality = 'medium'; 634*c8dee2aaSAndroid Build Coastguard Worker ctx.drawImage(img, 200, 350, 150, 100); 635*c8dee2aaSAndroid Build Coastguard Worker ctx.rotate(-.2); 636*c8dee2aaSAndroid Build Coastguard Worker ctx.imageSmoothingEnabled = false; 637*c8dee2aaSAndroid Build Coastguard Worker ctx.drawImage(img, 100, 150, 400, 350, 10, 400, 150, 100); 638*c8dee2aaSAndroid Build Coastguard Worker }); 639*c8dee2aaSAndroid Build Coastguard Worker }); // end describe('using images') 640*c8dee2aaSAndroid Build Coastguard Worker 641*c8dee2aaSAndroid Build Coastguard Worker { 642*c8dee2aaSAndroid Build Coastguard Worker const drawPoint = (ctx, x, y, color) => { 643*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = color; 644*c8dee2aaSAndroid Build Coastguard Worker ctx.fillRect(x, y, 1, 1); 645*c8dee2aaSAndroid Build Coastguard Worker } 646*c8dee2aaSAndroid Build Coastguard Worker const IN = 'purple'; 647*c8dee2aaSAndroid Build Coastguard Worker const OUT = 'orange'; 648*c8dee2aaSAndroid Build Coastguard Worker const SCALE = 8; 649*c8dee2aaSAndroid Build Coastguard Worker 650*c8dee2aaSAndroid Build Coastguard Worker // Check to see if these points are in or out on each of the 651*c8dee2aaSAndroid Build Coastguard Worker // test configurations. 652*c8dee2aaSAndroid Build Coastguard Worker const pts = [[3, 3], [4, 4], [5, 5], [10, 10], [8, 10], [6, 10], 653*c8dee2aaSAndroid Build Coastguard Worker [6.5, 9], [15, 10], [17, 10], [17, 11], [24, 24], 654*c8dee2aaSAndroid Build Coastguard Worker [25, 25], [26, 26], [27, 27]]; 655*c8dee2aaSAndroid Build Coastguard Worker const tests = [ 656*c8dee2aaSAndroid Build Coastguard Worker { 657*c8dee2aaSAndroid Build Coastguard Worker xOffset: 0, 658*c8dee2aaSAndroid Build Coastguard Worker yOffset: 0, 659*c8dee2aaSAndroid Build Coastguard Worker fillType: 'nonzero', 660*c8dee2aaSAndroid Build Coastguard Worker strokeWidth: 0, 661*c8dee2aaSAndroid Build Coastguard Worker testFn: (ctx, x, y) => ctx.isPointInPath(x * SCALE, y * SCALE, 'nonzero'), 662*c8dee2aaSAndroid Build Coastguard Worker }, 663*c8dee2aaSAndroid Build Coastguard Worker { 664*c8dee2aaSAndroid Build Coastguard Worker xOffset: 30, 665*c8dee2aaSAndroid Build Coastguard Worker yOffset: 0, 666*c8dee2aaSAndroid Build Coastguard Worker fillType: 'evenodd', 667*c8dee2aaSAndroid Build Coastguard Worker strokeWidth: 0, 668*c8dee2aaSAndroid Build Coastguard Worker testFn: (ctx, x, y) => ctx.isPointInPath(x * SCALE, y * SCALE, 'evenodd'), 669*c8dee2aaSAndroid Build Coastguard Worker }, 670*c8dee2aaSAndroid Build Coastguard Worker { 671*c8dee2aaSAndroid Build Coastguard Worker xOffset: 0, 672*c8dee2aaSAndroid Build Coastguard Worker yOffset: 30, 673*c8dee2aaSAndroid Build Coastguard Worker fillType: null, 674*c8dee2aaSAndroid Build Coastguard Worker strokeWidth: 1, 675*c8dee2aaSAndroid Build Coastguard Worker testFn: (ctx, x, y) => ctx.isPointInStroke(x * SCALE, y * SCALE), 676*c8dee2aaSAndroid Build Coastguard Worker }, 677*c8dee2aaSAndroid Build Coastguard Worker { 678*c8dee2aaSAndroid Build Coastguard Worker xOffset: 30, 679*c8dee2aaSAndroid Build Coastguard Worker yOffset: 30, 680*c8dee2aaSAndroid Build Coastguard Worker fillType: null, 681*c8dee2aaSAndroid Build Coastguard Worker strokeWidth: 2, 682*c8dee2aaSAndroid Build Coastguard Worker testFn: (ctx, x, y) => ctx.isPointInStroke(x * SCALE, y * SCALE), 683*c8dee2aaSAndroid Build Coastguard Worker }, 684*c8dee2aaSAndroid Build Coastguard Worker ]; 685*c8dee2aaSAndroid Build Coastguard Worker multipleCanvasGM('points_in_path_stroke', (canvas) => { 686*c8dee2aaSAndroid Build Coastguard Worker const ctx = canvas.getContext('2d'); 687*c8dee2aaSAndroid Build Coastguard Worker ctx.font = '20px Noto Mono'; 688*c8dee2aaSAndroid Build Coastguard Worker // Draw some visual aids 689*c8dee2aaSAndroid Build Coastguard Worker ctx.fillText('path-nonzero', 60, 30); 690*c8dee2aaSAndroid Build Coastguard Worker ctx.fillText('path-evenodd', 300, 30); 691*c8dee2aaSAndroid Build Coastguard Worker ctx.fillText('stroke-1px-wide', 60, 260); 692*c8dee2aaSAndroid Build Coastguard Worker ctx.fillText('stroke-2px-wide', 300, 260); 693*c8dee2aaSAndroid Build Coastguard Worker ctx.fillText('purple is IN, orange is OUT', 20, 560); 694*c8dee2aaSAndroid Build Coastguard Worker 695*c8dee2aaSAndroid Build Coastguard Worker // Scale up to make single pixels easier to see 696*c8dee2aaSAndroid Build Coastguard Worker ctx.scale(SCALE, SCALE); 697*c8dee2aaSAndroid Build Coastguard Worker for (const test of tests) { 698*c8dee2aaSAndroid Build Coastguard Worker ctx.beginPath(); 699*c8dee2aaSAndroid Build Coastguard Worker const xOffset = test.xOffset; 700*c8dee2aaSAndroid Build Coastguard Worker const yOffset = test.yOffset; 701*c8dee2aaSAndroid Build Coastguard Worker 702*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = '#AAA'; 703*c8dee2aaSAndroid Build Coastguard Worker ctx.lineWidth = test.strokeWidth; 704*c8dee2aaSAndroid Build Coastguard Worker ctx.rect(5+xOffset, 5+yOffset, 20, 20); 705*c8dee2aaSAndroid Build Coastguard Worker ctx.arc(15+xOffset, 15+yOffset, 8, 0, Math.PI*2, false); 706*c8dee2aaSAndroid Build Coastguard Worker if (test.fillType) { 707*c8dee2aaSAndroid Build Coastguard Worker ctx.fill(test.fillType); 708*c8dee2aaSAndroid Build Coastguard Worker } else { 709*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 710*c8dee2aaSAndroid Build Coastguard Worker } 711*c8dee2aaSAndroid Build Coastguard Worker 712*c8dee2aaSAndroid Build Coastguard Worker for (const pt of pts) { 713*c8dee2aaSAndroid Build Coastguard Worker let [x, y] = pt; 714*c8dee2aaSAndroid Build Coastguard Worker x += xOffset; 715*c8dee2aaSAndroid Build Coastguard Worker y += yOffset; 716*c8dee2aaSAndroid Build Coastguard Worker // naively apply transform when querying because the points queried 717*c8dee2aaSAndroid Build Coastguard Worker // ignore the CTM. 718*c8dee2aaSAndroid Build Coastguard Worker if (test.testFn(ctx, x, y)) { 719*c8dee2aaSAndroid Build Coastguard Worker drawPoint(ctx, x, y, IN); 720*c8dee2aaSAndroid Build Coastguard Worker } else { 721*c8dee2aaSAndroid Build Coastguard Worker drawPoint(ctx, x, y, OUT); 722*c8dee2aaSAndroid Build Coastguard Worker } 723*c8dee2aaSAndroid Build Coastguard Worker } 724*c8dee2aaSAndroid Build Coastguard Worker } 725*c8dee2aaSAndroid Build Coastguard Worker }); 726*c8dee2aaSAndroid Build Coastguard Worker } 727*c8dee2aaSAndroid Build Coastguard Worker 728*c8dee2aaSAndroid Build Coastguard Worker describe('loading custom fonts', () => { 729*c8dee2aaSAndroid Build Coastguard Worker const realFontLoaded = new FontFace('BungeeNonSystem', 'url(/assets/Bungee-Regular.ttf)', { 730*c8dee2aaSAndroid Build Coastguard Worker 'family': 'BungeeNonSystem', // Make sure the canvas does not use the system font 731*c8dee2aaSAndroid Build Coastguard Worker 'style': 'normal', 732*c8dee2aaSAndroid Build Coastguard Worker 'weight': '400', 733*c8dee2aaSAndroid Build Coastguard Worker }).load().then((font) => { 734*c8dee2aaSAndroid Build Coastguard Worker document.fonts.add(font); 735*c8dee2aaSAndroid Build Coastguard Worker }); 736*c8dee2aaSAndroid Build Coastguard Worker 737*c8dee2aaSAndroid Build Coastguard Worker let fontBuffer = null; 738*c8dee2aaSAndroid Build Coastguard Worker const skFontLoaded = fetch('/assets/Bungee-Regular.ttf').then( 739*c8dee2aaSAndroid Build Coastguard Worker (response) => response.arrayBuffer()).then( 740*c8dee2aaSAndroid Build Coastguard Worker (buffer) => { 741*c8dee2aaSAndroid Build Coastguard Worker fontBuffer = buffer; 742*c8dee2aaSAndroid Build Coastguard Worker }); 743*c8dee2aaSAndroid Build Coastguard Worker 744*c8dee2aaSAndroid Build Coastguard Worker beforeEach(async () => { 745*c8dee2aaSAndroid Build Coastguard Worker await realFontLoaded; 746*c8dee2aaSAndroid Build Coastguard Worker await skFontLoaded; 747*c8dee2aaSAndroid Build Coastguard Worker }); 748*c8dee2aaSAndroid Build Coastguard Worker 749*c8dee2aaSAndroid Build Coastguard Worker multipleCanvasGM('custom_font', (canvas) => { 750*c8dee2aaSAndroid Build Coastguard Worker if (canvas.loadFont) { 751*c8dee2aaSAndroid Build Coastguard Worker canvas.loadFont(fontBuffer, { 752*c8dee2aaSAndroid Build Coastguard Worker 'family': 'BungeeNonSystem', 753*c8dee2aaSAndroid Build Coastguard Worker 'style': 'normal', 754*c8dee2aaSAndroid Build Coastguard Worker 'weight': '400', 755*c8dee2aaSAndroid Build Coastguard Worker }); 756*c8dee2aaSAndroid Build Coastguard Worker } 757*c8dee2aaSAndroid Build Coastguard Worker const ctx = canvas.getContext('2d'); 758*c8dee2aaSAndroid Build Coastguard Worker 759*c8dee2aaSAndroid Build Coastguard Worker ctx.font = '20px monospace'; 760*c8dee2aaSAndroid Build Coastguard Worker ctx.fillText('20 px monospace', 10, 30); 761*c8dee2aaSAndroid Build Coastguard Worker 762*c8dee2aaSAndroid Build Coastguard Worker ctx.font = '2.0em BungeeNonSystem'; 763*c8dee2aaSAndroid Build Coastguard Worker ctx.fillText('2.0em Bungee filled', 10, 80); 764*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeText('2.0em Bungee stroked', 10, 130); 765*c8dee2aaSAndroid Build Coastguard Worker 766*c8dee2aaSAndroid Build Coastguard Worker const m = ctx.measureText('A phrase in English'); 767*c8dee2aaSAndroid Build Coastguard Worker expect(m).toBeTruthy(); 768*c8dee2aaSAndroid Build Coastguard Worker expect(m['width']).toBeTruthy(); 769*c8dee2aaSAndroid Build Coastguard Worker 770*c8dee2aaSAndroid Build Coastguard Worker ctx.font = '40pt monospace'; 771*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeText('40pt monospace', 10, 200); 772*c8dee2aaSAndroid Build Coastguard Worker 773*c8dee2aaSAndroid Build Coastguard Worker // bold wasn't defined, so should fallback to just the 400 weight 774*c8dee2aaSAndroid Build Coastguard Worker ctx.font = 'bold 45px BungeeNonSystem'; 775*c8dee2aaSAndroid Build Coastguard Worker ctx.fillText('45px Bungee filled', 10, 260); 776*c8dee2aaSAndroid Build Coastguard Worker }); 777*c8dee2aaSAndroid Build Coastguard Worker }); // describe('loading custom fonts') 778*c8dee2aaSAndroid Build Coastguard Worker 779*c8dee2aaSAndroid Build Coastguard Worker it('can read default properties', () => { 780*c8dee2aaSAndroid Build Coastguard Worker const skcanvas = CanvasKit.MakeCanvas(CANVAS_WIDTH, CANVAS_HEIGHT); 781*c8dee2aaSAndroid Build Coastguard Worker const realCanvas = document.getElementById('test'); 782*c8dee2aaSAndroid Build Coastguard Worker realCanvas.width = CANVAS_WIDTH; 783*c8dee2aaSAndroid Build Coastguard Worker realCanvas.height = CANVAS_HEIGHT; 784*c8dee2aaSAndroid Build Coastguard Worker 785*c8dee2aaSAndroid Build Coastguard Worker const skcontext = skcanvas.getContext('2d'); 786*c8dee2aaSAndroid Build Coastguard Worker const realContext = realCanvas.getContext('2d'); 787*c8dee2aaSAndroid Build Coastguard Worker // The skia canvas only comes with a monospace font by default 788*c8dee2aaSAndroid Build Coastguard Worker // Set the html canvas to be monospace too. 789*c8dee2aaSAndroid Build Coastguard Worker realContext.font = '10px monospace'; 790*c8dee2aaSAndroid Build Coastguard Worker 791*c8dee2aaSAndroid Build Coastguard Worker const toTest = ['font', 'lineWidth', 'strokeStyle', 'lineCap', 792*c8dee2aaSAndroid Build Coastguard Worker 'lineJoin', 'miterLimit', 'shadowOffsetY', 793*c8dee2aaSAndroid Build Coastguard Worker 'shadowBlur', 'shadowColor', 'shadowOffsetX', 794*c8dee2aaSAndroid Build Coastguard Worker 'globalAlpha', 'globalCompositeOperation', 795*c8dee2aaSAndroid Build Coastguard Worker 'lineDashOffset', 'imageSmoothingEnabled', 796*c8dee2aaSAndroid Build Coastguard Worker 'imageFilterQuality']; 797*c8dee2aaSAndroid Build Coastguard Worker 798*c8dee2aaSAndroid Build Coastguard Worker // Compare all the default values of the properties of skcanvas 799*c8dee2aaSAndroid Build Coastguard Worker // to the default values on the properties of a real canvas. 800*c8dee2aaSAndroid Build Coastguard Worker for(let attr of toTest) { 801*c8dee2aaSAndroid Build Coastguard Worker expect(skcontext[attr]).toBe(realContext[attr], attr); 802*c8dee2aaSAndroid Build Coastguard Worker } 803*c8dee2aaSAndroid Build Coastguard Worker 804*c8dee2aaSAndroid Build Coastguard Worker skcanvas.dispose(); 805*c8dee2aaSAndroid Build Coastguard Worker }); 806*c8dee2aaSAndroid Build Coastguard Worker }); // end describe('CanvasContext2D API') 807*c8dee2aaSAndroid Build Coastguard Worker 808*c8dee2aaSAndroid Build Coastguard Worker describe('Path2D API', () => { 809*c8dee2aaSAndroid Build Coastguard Worker multipleCanvasGM('path2d_line_drawing_operations', (canvas) => { 810*c8dee2aaSAndroid Build Coastguard Worker const ctx = canvas.getContext('2d'); 811*c8dee2aaSAndroid Build Coastguard Worker let clock; 812*c8dee2aaSAndroid Build Coastguard Worker let path; 813*c8dee2aaSAndroid Build Coastguard Worker if (canvas.makePath2D) { 814*c8dee2aaSAndroid Build Coastguard Worker clock = canvas.makePath2D('M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z'); 815*c8dee2aaSAndroid Build Coastguard Worker path = canvas.makePath2D(); 816*c8dee2aaSAndroid Build Coastguard Worker } else { 817*c8dee2aaSAndroid Build Coastguard Worker clock = new Path2D('M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z') 818*c8dee2aaSAndroid Build Coastguard Worker path = new Path2D(); 819*c8dee2aaSAndroid Build Coastguard Worker } 820*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(20, 5); 821*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(30, 20); 822*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(40, 10); 823*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(50, 20); 824*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(60, 0); 825*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(20, 5); 826*c8dee2aaSAndroid Build Coastguard Worker 827*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(20, 80); 828*c8dee2aaSAndroid Build Coastguard Worker path.bezierCurveTo(90, 10, 160, 150, 190, 10); 829*c8dee2aaSAndroid Build Coastguard Worker 830*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(36, 148); 831*c8dee2aaSAndroid Build Coastguard Worker path.quadraticCurveTo(66, 188, 120, 136); 832*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(36, 148); 833*c8dee2aaSAndroid Build Coastguard Worker 834*c8dee2aaSAndroid Build Coastguard Worker path.rect(5, 170, 20, 25); 835*c8dee2aaSAndroid Build Coastguard Worker 836*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(150, 180); 837*c8dee2aaSAndroid Build Coastguard Worker path.arcTo(150, 100, 50, 200, 20); 838*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(160, 160); 839*c8dee2aaSAndroid Build Coastguard Worker 840*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(20, 120); 841*c8dee2aaSAndroid Build Coastguard Worker path.arc(20, 120, 18, 0, 1.75 * Math.PI); 842*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(20, 120); 843*c8dee2aaSAndroid Build Coastguard Worker 844*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(150, 5); 845*c8dee2aaSAndroid Build Coastguard Worker path.ellipse(130, 25, 30, 10, -1*Math.PI/8, Math.PI/6, 1.5*Math.PI) 846*c8dee2aaSAndroid Build Coastguard Worker 847*c8dee2aaSAndroid Build Coastguard Worker ctx.lineWidth = 2; 848*c8dee2aaSAndroid Build Coastguard Worker ctx.scale(3.0, 3.0); 849*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(path); 850*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(clock); 851*c8dee2aaSAndroid Build Coastguard Worker }); 852*c8dee2aaSAndroid Build Coastguard Worker }); // end describe('Path2D API') 853*c8dee2aaSAndroid Build Coastguard Worker}); 854