| - name: 2d.path.initial |
| testing: |
| - 2d.path.initial |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.closePath(); |
| ctx.fillStyle = '#f00'; |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.beginPath |
| testing: |
| - 2d.path.beginPath |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.rect(0, 0, 100, 50); |
| ctx.beginPath(); |
| ctx.fillStyle = '#f00'; |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.moveTo.basic |
| testing: |
| - 2d.path.moveTo |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.rect(0, 0, 10, 50); |
| ctx.moveTo(100, 0); |
| ctx.lineTo(10, 0); |
| ctx.lineTo(10, 50); |
| ctx.lineTo(100, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.fill(); |
| @assert pixel 90,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.moveTo.newsubpath |
| testing: |
| - 2d.path.moveTo |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.beginPath(); |
| ctx.moveTo(0, 0); |
| ctx.moveTo(100, 0); |
| ctx.moveTo(100, 50); |
| ctx.moveTo(0, 50); |
| ctx.fillStyle = '#f00'; |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.moveTo.multiple |
| testing: |
| - 2d.path.moveTo |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.moveTo(0, 25); |
| ctx.moveTo(100, 25); |
| ctx.moveTo(0, 25); |
| ctx.lineTo(100, 25); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 50; |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.moveTo.nonfinite |
| desc: moveTo() with Infinity/NaN is ignored |
| testing: |
| - 2d.nonfinite |
| code: | |
| ctx.moveTo(0, 0); |
| ctx.lineTo(100, 0); |
| @nonfinite ctx.moveTo(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>); |
| ctx.lineTo(100, 50); |
| ctx.lineTo(0, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.closePath.empty |
| testing: |
| - 2d.path.closePath.empty |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.closePath(); |
| ctx.fillStyle = '#f00'; |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.closePath.newline |
| testing: |
| - 2d.path.closePath.nonempty |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 50; |
| ctx.moveTo(-100, 25); |
| ctx.lineTo(-100, -100); |
| ctx.lineTo(200, -100); |
| ctx.lineTo(200, 25); |
| ctx.closePath(); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.closePath.nextpoint |
| testing: |
| - 2d.path.closePath.nonempty |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 50; |
| ctx.moveTo(-100, 25); |
| ctx.lineTo(-100, -1000); |
| ctx.closePath(); |
| ctx.lineTo(1000, 25); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.lineTo.ensuresubpath.1 |
| desc: If there is no subpath, the point is added and nothing is drawn |
| testing: |
| - 2d.path.lineTo.empty |
| - 2d.path.ensure |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 50; |
| ctx.beginPath(); |
| ctx.lineTo(100, 50); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.lineTo.ensuresubpath.2 |
| desc: If there is no subpath, the point is added and used for subsequent drawing |
| testing: |
| - 2d.path.lineTo.empty |
| - 2d.path.ensure |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 50; |
| ctx.beginPath(); |
| ctx.lineTo(0, 25); |
| ctx.lineTo(100, 25); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.lineTo.basic |
| testing: |
| - 2d.path.lineTo.nonempty |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 50; |
| ctx.beginPath(); |
| ctx.moveTo(0, 25); |
| ctx.lineTo(100, 25); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.lineTo.nextpoint |
| testing: |
| - 2d.path.lineTo.nonempty |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 50; |
| ctx.beginPath(); |
| ctx.moveTo(-100, -100); |
| ctx.lineTo(0, 25); |
| ctx.lineTo(100, 25); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.lineTo.nonfinite |
| desc: lineTo() with Infinity/NaN is ignored |
| testing: |
| - 2d.nonfinite |
| code: | |
| ctx.moveTo(0, 0); |
| ctx.lineTo(100, 0); |
| @nonfinite ctx.lineTo(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>); |
| ctx.lineTo(100, 50); |
| ctx.lineTo(0, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 90,45 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.lineTo.nonfinite.details |
| desc: lineTo() with Infinity/NaN for first arg still converts the second arg |
| testing: |
| - 2d.nonfinite |
| code: | |
| for (var arg1 of [Infinity, -Infinity, NaN]) { |
| var converted = false; |
| ctx.lineTo(arg1, { valueOf: function() { converted = true; return 0; } }); |
| @assert converted; |
| } |
| t.done(); |
| |
| - name: 2d.path.quadraticCurveTo.ensuresubpath.1 |
| desc: If there is no subpath, the first control point is added (and nothing is drawn |
| up to it) |
| testing: |
| - 2d.path.quadratic.empty |
| - 2d.path.ensure |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 50; |
| ctx.beginPath(); |
| ctx.quadraticCurveTo(100, 50, 200, 50); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 95,45 == 0,255,0,255; @moz-todo |
| t.done(); |
| |
| - name: 2d.path.quadraticCurveTo.ensuresubpath.2 |
| desc: If there is no subpath, the first control point is added |
| testing: |
| - 2d.path.quadratic.empty |
| - 2d.path.ensure |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 50; |
| ctx.beginPath(); |
| ctx.quadraticCurveTo(0, 25, 100, 25); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 5,45 == 0,255,0,255; @moz-todo |
| t.done(); |
| |
| - name: 2d.path.quadraticCurveTo.basic |
| testing: |
| - 2d.path.quadratic.nonempty |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 50; |
| ctx.beginPath(); |
| ctx.moveTo(0, 25); |
| ctx.quadraticCurveTo(100, 25, 100, 25); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.quadraticCurveTo.shape |
| testing: |
| - 2d.path.quadratic.nonempty |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 55; |
| ctx.beginPath(); |
| ctx.moveTo(-1000, 1050); |
| ctx.quadraticCurveTo(0, -1000, 1200, 1050); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 1,1 == 0,255,0,255; |
| @assert pixel 98,1 == 0,255,0,255; |
| @assert pixel 1,48 == 0,255,0,255; |
| @assert pixel 98,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.quadraticCurveTo.scaled |
| testing: |
| - 2d.path.quadratic.nonempty |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.scale(1000, 1000); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 0.055; |
| ctx.beginPath(); |
| ctx.moveTo(-1, 1.05); |
| ctx.quadraticCurveTo(0, -1, 1.2, 1.05); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 1,1 == 0,255,0,255; |
| @assert pixel 98,1 == 0,255,0,255; |
| @assert pixel 1,48 == 0,255,0,255; |
| @assert pixel 98,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.quadraticCurveTo.nonfinite |
| desc: quadraticCurveTo() with Infinity/NaN is ignored |
| testing: |
| - 2d.nonfinite |
| code: | |
| ctx.moveTo(0, 0); |
| ctx.lineTo(100, 0); |
| @nonfinite ctx.quadraticCurveTo(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>); |
| ctx.lineTo(100, 50); |
| ctx.lineTo(0, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 90,45 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.bezierCurveTo.ensuresubpath.1 |
| desc: If there is no subpath, the first control point is added (and nothing is drawn |
| up to it) |
| testing: |
| - 2d.path.bezier.empty |
| - 2d.path.ensure |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 50; |
| ctx.beginPath(); |
| ctx.bezierCurveTo(100, 50, 200, 50, 200, 50); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 95,45 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.bezierCurveTo.ensuresubpath.2 |
| desc: If there is no subpath, the first control point is added |
| testing: |
| - 2d.path.bezier.empty |
| - 2d.path.ensure |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 50; |
| ctx.beginPath(); |
| ctx.bezierCurveTo(0, 25, 100, 25, 100, 25); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 5,45 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.bezierCurveTo.basic |
| testing: |
| - 2d.path.bezier.nonempty |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 50; |
| ctx.beginPath(); |
| ctx.moveTo(0, 25); |
| ctx.bezierCurveTo(100, 25, 100, 25, 100, 25); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.bezierCurveTo.shape |
| testing: |
| - 2d.path.bezier.nonempty |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 55; |
| ctx.beginPath(); |
| ctx.moveTo(-2000, 3100); |
| ctx.bezierCurveTo(-2000, -1000, 2100, -1000, 2100, 3100); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 1,1 == 0,255,0,255; |
| @assert pixel 98,1 == 0,255,0,255; |
| @assert pixel 1,48 == 0,255,0,255; |
| @assert pixel 98,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.bezierCurveTo.scaled |
| testing: |
| - 2d.path.bezier.nonempty |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.scale(1000, 1000); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 0.055; |
| ctx.beginPath(); |
| ctx.moveTo(-2, 3.1); |
| ctx.bezierCurveTo(-2, -1, 2.1, -1, 2.1, 3.1); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 1,1 == 0,255,0,255; |
| @assert pixel 98,1 == 0,255,0,255; |
| @assert pixel 1,48 == 0,255,0,255; |
| @assert pixel 98,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.bezierCurveTo.nonfinite |
| desc: bezierCurveTo() with Infinity/NaN is ignored |
| testing: |
| - 2d.nonfinite |
| code: | |
| ctx.moveTo(0, 0); |
| ctx.lineTo(100, 0); |
| @nonfinite ctx.bezierCurveTo(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>); |
| ctx.lineTo(100, 50); |
| ctx.lineTo(0, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 90,45 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arcTo.ensuresubpath.1 |
| desc: If there is no subpath, the first control point is added (and nothing is drawn |
| up to it) |
| testing: |
| - 2d.path.arcTo.empty |
| - 2d.path.ensure |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 50; |
| ctx.strokeStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.arcTo(100, 50, 200, 50, 0.1); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arcTo.ensuresubpath.2 |
| desc: If there is no subpath, the first control point is added |
| testing: |
| - 2d.path.arcTo.empty |
| - 2d.path.ensure |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 50; |
| ctx.strokeStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.arcTo(0, 25, 50, 250, 0.1); // adds (x1,y1), draws nothing |
| ctx.lineTo(100, 25); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arcTo.coincide.1 |
| desc: arcTo() has no effect if P0 = P1 |
| testing: |
| - 2d.path.arcTo.coincide.01 |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 50; |
| ctx.strokeStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.moveTo(0, 25); |
| ctx.arcTo(0, 25, 50, 1000, 1); |
| ctx.lineTo(100, 25); |
| ctx.stroke(); |
| ctx.strokeStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.moveTo(50, 25); |
| ctx.arcTo(50, 25, 100, 25, 1); |
| ctx.stroke(); |
| @assert pixel 50,1 == 0,255,0,255; |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 50,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arcTo.coincide.2 |
| desc: arcTo() draws a straight line to P1 if P1 = P2 |
| testing: |
| - 2d.path.arcTo.coincide.12 |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 50; |
| ctx.strokeStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.moveTo(0, 25); |
| ctx.arcTo(100, 25, 100, 25, 1); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arcTo.collinear.1 |
| desc: arcTo() with all points on a line, and P1 between P0/P2, draws a straight |
| line to P1 |
| testing: |
| - 2d.path.arcTo.collinear |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 50; |
| ctx.strokeStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.moveTo(0, 25); |
| ctx.arcTo(100, 25, 200, 25, 1); |
| ctx.stroke(); |
| ctx.strokeStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.moveTo(-100, 25); |
| ctx.arcTo(0, 25, 100, 25, 1); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arcTo.collinear.2 |
| desc: arcTo() with all points on a line, and P2 between P0/P1, draws a straight |
| line to P1 |
| testing: |
| - 2d.path.arcTo.collinear |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 50; |
| ctx.strokeStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.moveTo(0, 25); |
| ctx.arcTo(100, 25, 10, 25, 1); |
| ctx.stroke(); |
| ctx.strokeStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.moveTo(100, 25); |
| ctx.arcTo(200, 25, 110, 25, 1); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arcTo.collinear.3 |
| desc: arcTo() with all points on a line, and P0 between P1/P2, draws a straight |
| line to P1 |
| testing: |
| - 2d.path.arcTo.collinear |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 50; |
| ctx.strokeStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.moveTo(0, 25); |
| ctx.arcTo(100, 25, -100, 25, 1); |
| ctx.stroke(); |
| ctx.strokeStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.moveTo(100, 25); |
| ctx.arcTo(200, 25, 0, 25, 1); |
| ctx.stroke(); |
| ctx.beginPath(); |
| ctx.moveTo(-100, 25); |
| ctx.arcTo(0, 25, -200, 25, 1); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arcTo.shape.curve1 |
| desc: arcTo() curves in the right kind of shape |
| testing: |
| - 2d.path.arcTo.shape |
| code: | |
| var tol = 1.5; // tolerance to avoid antialiasing artifacts |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 10; |
| ctx.beginPath(); |
| ctx.moveTo(10, 25); |
| ctx.arcTo(75, 25, 75, 60, 20); |
| ctx.stroke(); |
| ctx.fillStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.rect(10, 20, 45, 10); |
| ctx.moveTo(80, 45); |
| ctx.arc(55, 45, 25+tol, 0, -Math.PI/2, true); |
| ctx.arc(55, 45, 15-tol, -Math.PI/2, 0, false); |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 55,19 == 0,255,0,255; |
| @assert pixel 55,20 == 0,255,0,255; |
| @assert pixel 55,21 == 0,255,0,255; |
| @assert pixel 64,22 == 0,255,0,255; |
| @assert pixel 65,21 == 0,255,0,255; |
| @assert pixel 72,28 == 0,255,0,255; |
| @assert pixel 73,27 == 0,255,0,255; |
| @assert pixel 78,36 == 0,255,0,255; |
| @assert pixel 79,35 == 0,255,0,255; |
| @assert pixel 80,44 == 0,255,0,255; |
| @assert pixel 80,45 == 0,255,0,255; |
| @assert pixel 80,46 == 0,255,0,255; |
| @assert pixel 65,45 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arcTo.shape.curve2 |
| desc: arcTo() curves in the right kind of shape |
| testing: |
| - 2d.path.arcTo.shape |
| code: | |
| var tol = 1.5; // tolerance to avoid antialiasing artifacts |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.rect(10, 20, 45, 10); |
| ctx.moveTo(80, 45); |
| ctx.arc(55, 45, 25-tol, 0, -Math.PI/2, true); |
| ctx.arc(55, 45, 15+tol, -Math.PI/2, 0, false); |
| ctx.fill(); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 10; |
| ctx.beginPath(); |
| ctx.moveTo(10, 25); |
| ctx.arcTo(75, 25, 75, 60, 20); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 55,19 == 0,255,0,255; |
| @assert pixel 55,20 == 0,255,0,255; |
| @assert pixel 55,21 == 0,255,0,255; |
| @assert pixel 64,22 == 0,255,0,255; |
| @assert pixel 65,21 == 0,255,0,255; |
| @assert pixel 72,28 == 0,255,0,255; |
| @assert pixel 73,27 == 0,255,0,255; |
| @assert pixel 78,36 == 0,255,0,255; |
| @assert pixel 79,35 == 0,255,0,255; |
| @assert pixel 80,44 == 0,255,0,255; |
| @assert pixel 80,45 == 0,255,0,255; |
| @assert pixel 80,46 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arcTo.shape.start |
| desc: arcTo() draws a straight line from P0 to P1 |
| testing: |
| - 2d.path.arcTo.shape |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 50; |
| ctx.beginPath(); |
| ctx.moveTo(0, 25); |
| ctx.arcTo(200, 25, 200, 50, 10); |
| ctx.stroke(); |
| @assert pixel 1,1 == 0,255,0,255; |
| @assert pixel 1,48 == 0,255,0,255; |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 98,1 == 0,255,0,255; |
| @assert pixel 98,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arcTo.shape.end |
| desc: arcTo() does not draw anything from P1 to P2 |
| testing: |
| - 2d.path.arcTo.shape |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 50; |
| ctx.beginPath(); |
| ctx.moveTo(-100, -100); |
| ctx.arcTo(-100, 25, 200, 25, 10); |
| ctx.stroke(); |
| @assert pixel 1,1 == 0,255,0,255; |
| @assert pixel 1,48 == 0,255,0,255; |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 98,1 == 0,255,0,255; |
| @assert pixel 98,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arcTo.negative |
| desc: arcTo() with negative radius throws an exception |
| testing: |
| - 2d.path.arcTo.negative |
| code: | |
| @assert throws INDEX_SIZE_ERR ctx.arcTo(0, 0, 0, 0, -1); |
| t.done(); |
| |
| - name: 2d.path.arcTo.zero.1 |
| desc: arcTo() with zero radius draws a straight line from P0 to P1 |
| testing: |
| - 2d.path.arcTo.zeroradius |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 50; |
| ctx.strokeStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.moveTo(0, 25); |
| ctx.arcTo(100, 25, 100, 100, 0); |
| ctx.stroke(); |
| ctx.strokeStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.moveTo(0, -25); |
| ctx.arcTo(50, -25, 50, 50, 0); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arcTo.zero.2 |
| desc: arcTo() with zero radius draws a straight line from P0 to P1, even when all |
| points are collinear |
| testing: |
| - 2d.path.arcTo.zeroradius |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 50; |
| ctx.strokeStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.moveTo(0, 25); |
| ctx.arcTo(100, 25, -100, 25, 0); |
| ctx.stroke(); |
| ctx.strokeStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.moveTo(100, 25); |
| ctx.arcTo(200, 25, 50, 25, 0); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arcTo.transformation |
| desc: arcTo joins up to the last subpath point correctly |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.moveTo(0, 50); |
| ctx.translate(100, 0); |
| ctx.arcTo(50, 50, 50, 0, 50); |
| ctx.lineTo(-100, 0); |
| ctx.fill(); |
| @assert pixel 0,0 == 0,255,0,255; |
| @assert pixel 50,0 == 0,255,0,255; |
| @assert pixel 99,0 == 0,255,0,255; |
| @assert pixel 0,25 == 0,255,0,255; |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 99,25 == 0,255,0,255; |
| @assert pixel 0,49 == 0,255,0,255; |
| @assert pixel 50,49 == 0,255,0,255; |
| @assert pixel 99,49 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arcTo.scale |
| desc: arcTo scales the curve, not just the control points |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.moveTo(0, 50); |
| ctx.translate(100, 0); |
| ctx.scale(0.1, 1); |
| ctx.arcTo(50, 50, 50, 0, 50); |
| ctx.lineTo(-1000, 0); |
| ctx.fill(); |
| @assert pixel 0,0 == 0,255,0,255; |
| @assert pixel 50,0 == 0,255,0,255; |
| @assert pixel 99,0 == 0,255,0,255; |
| @assert pixel 0,25 == 0,255,0,255; |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 99,25 == 0,255,0,255; |
| @assert pixel 0,49 == 0,255,0,255; |
| @assert pixel 50,49 == 0,255,0,255; |
| @assert pixel 99,49 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arcTo.nonfinite |
| desc: arcTo() with Infinity/NaN is ignored |
| testing: |
| - 2d.nonfinite |
| code: | |
| ctx.moveTo(0, 0); |
| ctx.lineTo(100, 0); |
| @nonfinite ctx.arcTo(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>); |
| ctx.lineTo(100, 50); |
| ctx.lineTo(0, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 90,45 == 0,255,0,255; |
| t.done(); |
| |
| |
| - name: 2d.path.arc.empty |
| desc: arc() with an empty path does not draw a straight line to the start point |
| testing: |
| - 2d.path.arc.nonempty |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 50; |
| ctx.strokeStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.arc(200, 25, 5, 0, 2*Math.PI, true); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.nonempty |
| desc: arc() with a non-empty path does draw a straight line to the start point |
| testing: |
| - 2d.path.arc.nonempty |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 50; |
| ctx.strokeStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.moveTo(0, 25); |
| ctx.arc(200, 25, 5, 0, 2*Math.PI, true); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.end |
| desc: arc() adds the end point of the arc to the subpath |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 50; |
| ctx.strokeStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.moveTo(-100, 0); |
| ctx.arc(-100, 0, 25, -Math.PI/2, Math.PI/2, true); |
| ctx.lineTo(100, 25); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.default |
| desc: arc() with missing last argument defaults to clockwise |
| testing: |
| - 2d.path.arc.omitted |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.moveTo(100, 0); |
| ctx.arc(100, 0, 150, -Math.PI, Math.PI/2); |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.angle.1 |
| desc: arc() draws pi/2 .. -pi anticlockwise correctly |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.moveTo(100, 0); |
| ctx.arc(100, 0, 150, Math.PI/2, -Math.PI, true); |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.angle.2 |
| desc: arc() draws -3pi/2 .. -pi anticlockwise correctly |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.moveTo(100, 0); |
| ctx.arc(100, 0, 150, -3*Math.PI/2, -Math.PI, true); |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.angle.3 |
| desc: arc() wraps angles mod 2pi when anticlockwise and end > start+2pi |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.moveTo(100, 0); |
| ctx.arc(100, 0, 150, (512+1/2)*Math.PI, (1024-1)*Math.PI, true); |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.angle.4 |
| desc: arc() draws a full circle when clockwise and end > start+2pi |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.moveTo(50, 25); |
| ctx.arc(50, 25, 60, (512+1/2)*Math.PI, (1024-1)*Math.PI, false); |
| ctx.fill(); |
| @assert pixel 1,1 == 0,255,0,255; |
| @assert pixel 98,1 == 0,255,0,255; |
| @assert pixel 1,48 == 0,255,0,255; |
| @assert pixel 98,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.angle.5 |
| desc: arc() wraps angles mod 2pi when clockwise and start > end+2pi |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.moveTo(100, 0); |
| ctx.arc(100, 0, 150, (1024-1)*Math.PI, (512+1/2)*Math.PI, false); |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.angle.6 |
| desc: arc() draws a full circle when anticlockwise and start > end+2pi |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.moveTo(50, 25); |
| ctx.arc(50, 25, 60, (1024-1)*Math.PI, (512+1/2)*Math.PI, true); |
| ctx.fill(); |
| @assert pixel 1,1 == 0,255,0,255; |
| @assert pixel 98,1 == 0,255,0,255; |
| @assert pixel 1,48 == 0,255,0,255; |
| @assert pixel 98,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.zero.1 |
| desc: arc() draws nothing when startAngle = endAngle and anticlockwise |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 100; |
| ctx.beginPath(); |
| ctx.arc(50, 25, 50, 0, 0, true); |
| ctx.stroke(); |
| @assert pixel 50,20 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.zero.2 |
| desc: arc() draws nothing when startAngle = endAngle and clockwise |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 100; |
| ctx.beginPath(); |
| ctx.arc(50, 25, 50, 0, 0, false); |
| ctx.stroke(); |
| @assert pixel 50,20 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.twopie.1 |
| desc: arc() draws nothing when end = start + 2pi-e and anticlockwise |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 100; |
| ctx.beginPath(); |
| ctx.arc(50, 25, 50, 0, 2*Math.PI - 1e-4, true); |
| ctx.stroke(); |
| @assert pixel 50,20 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.twopie.2 |
| desc: arc() draws a full circle when end = start + 2pi-e and clockwise |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 100; |
| ctx.beginPath(); |
| ctx.arc(50, 25, 50, 0, 2*Math.PI - 1e-4, false); |
| ctx.stroke(); |
| @assert pixel 50,20 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.twopie.3 |
| desc: arc() draws a full circle when end = start + 2pi+e and anticlockwise |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 100; |
| ctx.beginPath(); |
| ctx.arc(50, 25, 50, 0, 2*Math.PI + 1e-4, true); |
| ctx.stroke(); |
| @assert pixel 50,20 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.twopie.4 |
| desc: arc() draws nothing when end = start + 2pi+e and clockwise |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 100; |
| ctx.beginPath(); |
| ctx.arc(50, 25, 50, 0, 2*Math.PI + 1e-4, false); |
| ctx.stroke(); |
| @assert pixel 50,20 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.shape.1 |
| desc: arc() from 0 to pi does not draw anything in the wrong half |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 50; |
| ctx.strokeStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.arc(50, 50, 50, 0, Math.PI, false); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 1,1 == 0,255,0,255; |
| @assert pixel 98,1 == 0,255,0,255; |
| @assert pixel 1,48 == 0,255,0,255; |
| @assert pixel 20,48 == 0,255,0,255; |
| @assert pixel 98,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.shape.2 |
| desc: arc() from 0 to pi draws stuff in the right half |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 100; |
| ctx.strokeStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.arc(50, 50, 50, 0, Math.PI, true); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 1,1 == 0,255,0,255; |
| @assert pixel 98,1 == 0,255,0,255; |
| @assert pixel 1,48 == 0,255,0,255; |
| @assert pixel 20,48 == 0,255,0,255; |
| @assert pixel 98,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.shape.3 |
| desc: arc() from 0 to -pi/2 does not draw anything in the wrong quadrant |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 100; |
| ctx.strokeStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.arc(0, 50, 50, 0, -Math.PI/2, false); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 1,1 == 0,255,0,255; |
| @assert pixel 98,1 == 0,255,0,255; |
| @assert pixel 1,48 == 0,255,0,255; @moz-todo |
| @assert pixel 98,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.shape.4 |
| desc: arc() from 0 to -pi/2 draws stuff in the right quadrant |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 150; |
| ctx.strokeStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.arc(-50, 50, 100, 0, -Math.PI/2, true); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 1,1 == 0,255,0,255; |
| @assert pixel 98,1 == 0,255,0,255; |
| @assert pixel 1,48 == 0,255,0,255; |
| @assert pixel 98,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.shape.5 |
| desc: arc() from 0 to 5pi does not draw crazy things |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 200; |
| ctx.strokeStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.arc(300, 0, 100, 0, 5*Math.PI, false); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 1,1 == 0,255,0,255; |
| @assert pixel 98,1 == 0,255,0,255; |
| @assert pixel 1,48 == 0,255,0,255; |
| @assert pixel 98,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.selfintersect.1 |
| desc: arc() with lineWidth > 2*radius is drawn sensibly |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 200; |
| ctx.strokeStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.arc(100, 50, 25, 0, -Math.PI/2, true); |
| ctx.stroke(); |
| ctx.beginPath(); |
| ctx.arc(0, 0, 25, 0, -Math.PI/2, true); |
| ctx.stroke(); |
| @assert pixel 1,1 == 0,255,0,255; @moz-todo |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.selfintersect.2 |
| desc: arc() with lineWidth > 2*radius is drawn sensibly |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 180; |
| ctx.strokeStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.arc(-50, 50, 25, 0, -Math.PI/2, true); |
| ctx.stroke(); |
| ctx.beginPath(); |
| ctx.arc(100, 0, 25, 0, -Math.PI/2, true); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 90,10 == 0,255,0,255; |
| @assert pixel 97,1 == 0,255,0,255; |
| @assert pixel 97,2 == 0,255,0,255; |
| @assert pixel 97,3 == 0,255,0,255; |
| @assert pixel 2,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.negative |
| desc: arc() with negative radius throws INDEX_SIZE_ERR |
| testing: |
| - 2d.path.arc.negative |
| code: | |
| @assert throws INDEX_SIZE_ERR ctx.arc(0, 0, -1, 0, 0, true); |
| t.done(); |
| |
| - name: 2d.path.arc.zeroradius |
| desc: arc() with zero radius draws a line to the start point |
| testing: |
| - 2d.path.arc.zero |
| code: | |
| ctx.fillStyle = '#f00' |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 50; |
| ctx.strokeStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.moveTo(0, 25); |
| ctx.arc(200, 25, 0, 0, Math.PI, true); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.scale.1 |
| desc: Non-uniformly scaled arcs are the right shape |
| testing: |
| - 2d.path.transformation |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.scale(2, 0.5); |
| ctx.fillStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.arc(25, 50, 56, 0, 2*Math.PI, false); |
| ctx.fill(); |
| ctx.fillStyle = '#f00'; |
| ctx.beginPath(); |
| ctx.moveTo(-25, 50); |
| ctx.arc(-25, 50, 24, 0, 2*Math.PI, false); |
| ctx.moveTo(75, 50); |
| ctx.arc(75, 50, 24, 0, 2*Math.PI, false); |
| ctx.moveTo(25, -25); |
| ctx.arc(25, -25, 24, 0, 2*Math.PI, false); |
| ctx.moveTo(25, 125); |
| ctx.arc(25, 125, 24, 0, 2*Math.PI, false); |
| ctx.fill(); |
| @assert pixel 0,0 == 0,255,0,255; |
| @assert pixel 50,0 == 0,255,0,255; |
| @assert pixel 99,0 == 0,255,0,255; |
| @assert pixel 0,25 == 0,255,0,255; |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 99,25 == 0,255,0,255; |
| @assert pixel 0,49 == 0,255,0,255; |
| @assert pixel 50,49 == 0,255,0,255; |
| @assert pixel 99,49 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.scale.2 |
| desc: Highly scaled arcs are the right shape |
| testing: |
| - 2d.path.arc.draw |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.scale(100, 100); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 1.2; |
| ctx.beginPath(); |
| ctx.arc(0, 0, 0.6, 0, Math.PI/2, false); |
| ctx.stroke(); |
| @assert pixel 1,1 == 0,255,0,255; |
| @assert pixel 50,1 == 0,255,0,255; |
| @assert pixel 98,1 == 0,255,0,255; |
| @assert pixel 1,25 == 0,255,0,255; |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 98,25 == 0,255,0,255; |
| @assert pixel 1,48 == 0,255,0,255; |
| @assert pixel 50,48 == 0,255,0,255; |
| @assert pixel 98,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.arc.nonfinite |
| desc: arc() with Infinity/NaN is ignored |
| testing: |
| - 2d.nonfinite |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.moveTo(0, 0); |
| ctx.lineTo(100, 0); |
| @nonfinite ctx.arc(<0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <2*Math.PI Infinity -Infinity NaN>, <true>); |
| ctx.lineTo(100, 50); |
| ctx.lineTo(0, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 90,45 == 0,255,0,255; |
| t.done(); |
| |
| |
| - name: 2d.path.rect.basic |
| testing: |
| - 2d.path.rect.subpath |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.rect(0, 0, 100, 50); |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.rect.newsubpath |
| testing: |
| - 2d.path.rect.subpath |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.beginPath(); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 50; |
| ctx.moveTo(-100, 25); |
| ctx.lineTo(-50, 25); |
| ctx.rect(200, 25, 1, 1); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.rect.closed |
| testing: |
| - 2d.path.rect.closed |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 200; |
| ctx.lineJoin = 'miter'; |
| ctx.rect(100, 50, 100, 100); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.rect.end.1 |
| testing: |
| - 2d.path.rect.newsubpath |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 100; |
| ctx.rect(200, 100, 400, 1000); |
| ctx.lineTo(-2000, -1000); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.rect.end.2 |
| testing: |
| - 2d.path.rect.newsubpath |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 450; |
| ctx.lineCap = 'round'; |
| ctx.lineJoin = 'bevel'; |
| ctx.rect(150, 150, 2000, 2000); |
| ctx.lineTo(160, 160); |
| ctx.stroke(); |
| @assert pixel 1,1 == 0,255,0,255; |
| @assert pixel 98,1 == 0,255,0,255; |
| @assert pixel 1,48 == 0,255,0,255; |
| @assert pixel 98,48 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.rect.zero.1 |
| testing: |
| - 2d.path.rect.subpath |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 100; |
| ctx.beginPath(); |
| ctx.rect(0, 50, 100, 0); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.rect.zero.2 |
| testing: |
| - 2d.path.rect.subpath |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 100; |
| ctx.beginPath(); |
| ctx.rect(50, -100, 0, 250); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.rect.zero.3 |
| testing: |
| - 2d.path.rect.subpath |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 100; |
| ctx.beginPath(); |
| ctx.rect(50, 25, 0, 0); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.rect.zero.4 |
| testing: |
| - 2d.path.rect.subpath |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 50; |
| ctx.rect(100, 25, 0, 0); |
| ctx.lineTo(0, 25); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.rect.zero.5 |
| testing: |
| - 2d.path.rect.subpath |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 50; |
| ctx.moveTo(0, 0); |
| ctx.rect(100, 25, 0, 0); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.rect.zero.6 |
| testing: |
| - 2d.path.rect.subpath |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineJoin = 'miter'; |
| ctx.miterLimit = 1.5; |
| ctx.lineWidth = 200; |
| ctx.beginPath(); |
| ctx.rect(100, 25, 1000, 0); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; @moz-todo |
| t.done(); |
| |
| - name: 2d.path.rect.negative |
| testing: |
| - 2d.path.rect.subpath |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.beginPath(); |
| ctx.fillStyle = '#0f0'; |
| ctx.rect(0, 0, 50, 25); |
| ctx.rect(100, 0, -50, 25); |
| ctx.rect(0, 50, 50, -25); |
| ctx.rect(100, 50, -50, -25); |
| ctx.fill(); |
| @assert pixel 25,12 == 0,255,0,255; |
| @assert pixel 75,12 == 0,255,0,255; |
| @assert pixel 25,37 == 0,255,0,255; |
| @assert pixel 75,37 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.rect.winding |
| testing: |
| - 2d.path.rect.subpath |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.beginPath(); |
| ctx.fillStyle = '#f00'; |
| ctx.rect(0, 0, 50, 50); |
| ctx.rect(100, 50, -50, -50); |
| ctx.rect(0, 25, 100, -25); |
| ctx.rect(100, 25, -100, 25); |
| ctx.fill(); |
| @assert pixel 25,12 == 0,255,0,255; |
| @assert pixel 75,12 == 0,255,0,255; |
| @assert pixel 25,37 == 0,255,0,255; |
| @assert pixel 75,37 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.rect.selfintersect |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 90; |
| ctx.beginPath(); |
| ctx.rect(45, 20, 10, 10); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; @moz-todo |
| t.done(); |
| |
| - name: 2d.path.rect.nonfinite |
| desc: rect() with Infinity/NaN is ignored |
| testing: |
| - 2d.nonfinite |
| code: | |
| ctx.moveTo(0, 0); |
| ctx.lineTo(100, 0); |
| @nonfinite ctx.rect(<0 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>); |
| ctx.lineTo(100, 50); |
| ctx.lineTo(0, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 90,45 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.fill.overlap |
| testing: |
| - 2d.path.fill.basic |
| code: | |
| ctx.fillStyle = '#000'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = 'rgba(0, 255, 0, 0.5)'; |
| ctx.rect(0, 0, 100, 50); |
| ctx.closePath(); |
| ctx.rect(10, 10, 80, 30); |
| ctx.fill(); |
| @assert pixel 50,25 ==~ 0,127,0,255 +/- 1; |
| t.done(); |
| |
| - name: 2d.path.fill.winding.add |
| testing: |
| - 2d.path.fill.basic |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.moveTo(-10, -10); |
| ctx.lineTo(110, -10); |
| ctx.lineTo(110, 60); |
| ctx.lineTo(-10, 60); |
| ctx.lineTo(-10, -10); |
| ctx.lineTo(0, 0); |
| ctx.lineTo(100, 0); |
| ctx.lineTo(100, 50); |
| ctx.lineTo(0, 50); |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.fill.winding.subtract.1 |
| testing: |
| - 2d.path.fill.basic |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#f00'; |
| ctx.moveTo(-10, -10); |
| ctx.lineTo(110, -10); |
| ctx.lineTo(110, 60); |
| ctx.lineTo(-10, 60); |
| ctx.lineTo(-10, -10); |
| ctx.lineTo(0, 0); |
| ctx.lineTo(0, 50); |
| ctx.lineTo(100, 50); |
| ctx.lineTo(100, 0); |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.fill.winding.subtract.2 |
| testing: |
| - 2d.path.fill.basic |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#f00'; |
| ctx.moveTo(-10, -10); |
| ctx.lineTo(110, -10); |
| ctx.lineTo(110, 60); |
| ctx.lineTo(-10, 60); |
| ctx.moveTo(0, 0); |
| ctx.lineTo(0, 50); |
| ctx.lineTo(100, 50); |
| ctx.lineTo(100, 0); |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.fill.winding.subtract.3 |
| testing: |
| - 2d.path.fill.basic |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.moveTo(-10, -10); |
| ctx.lineTo(110, -10); |
| ctx.lineTo(110, 60); |
| ctx.lineTo(-10, 60); |
| ctx.lineTo(-10, -10); |
| ctx.lineTo(-20, -20); |
| ctx.lineTo(120, -20); |
| ctx.lineTo(120, 70); |
| ctx.lineTo(-20, 70); |
| ctx.lineTo(-20, -20); |
| ctx.lineTo(0, 0); |
| ctx.lineTo(0, 50); |
| ctx.lineTo(100, 50); |
| ctx.lineTo(100, 0); |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.fill.closed.basic |
| testing: |
| - 2d.path.fill.closed |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.moveTo(0, 0); |
| ctx.lineTo(100, 0); |
| ctx.lineTo(100, 50); |
| ctx.lineTo(0, 50); |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.fill.closed.unaffected |
| testing: |
| - 2d.path.fill.closed |
| code: | |
| ctx.fillStyle = '#00f'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.moveTo(0, 0); |
| ctx.lineTo(100, 0); |
| ctx.lineTo(100, 50); |
| ctx.fillStyle = '#f00'; |
| ctx.fill(); |
| ctx.lineTo(0, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.fill(); |
| @assert pixel 90,10 == 0,255,0,255; |
| @assert pixel 10,40 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.stroke.overlap |
| desc: Stroked subpaths are combined before being drawn |
| testing: |
| - 2d.path.stroke.basic |
| code: | |
| ctx.fillStyle = '#000'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = 'rgba(0, 255, 0, 0.5)'; |
| ctx.lineWidth = 50; |
| ctx.moveTo(0, 20); |
| ctx.lineTo(100, 20); |
| ctx.moveTo(0, 30); |
| ctx.lineTo(100, 30); |
| ctx.stroke(); |
| @assert pixel 50,25 ==~ 0,127,0,255 +/- 1; |
| t.done(); |
| |
| - name: 2d.path.stroke.union |
| desc: Strokes in opposite directions are unioned, not subtracted |
| testing: |
| - 2d.path.stroke.basic |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.lineWidth = 40; |
| ctx.moveTo(0, 10); |
| ctx.lineTo(100, 10); |
| ctx.moveTo(100, 40); |
| ctx.lineTo(0, 40); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.stroke.unaffected |
| desc: Stroking does not start a new path or subpath |
| testing: |
| - 2d.path.stroke.basic |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.lineWidth = 50; |
| ctx.moveTo(-100, 25); |
| ctx.lineTo(-100, -100); |
| ctx.lineTo(200, -100); |
| ctx.lineTo(200, 25); |
| ctx.strokeStyle = '#f00'; |
| ctx.stroke(); |
| ctx.closePath(); |
| ctx.strokeStyle = '#0f0'; |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.stroke.scale1 |
| desc: Stroke line widths are scaled by the current transformation matrix |
| testing: |
| - 2d.path.transformation |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.beginPath(); |
| ctx.rect(25, 12.5, 50, 25); |
| ctx.save(); |
| ctx.scale(50, 25); |
| ctx.strokeStyle = '#0f0'; |
| ctx.stroke(); |
| ctx.restore(); |
| ctx.beginPath(); |
| ctx.rect(-25, -12.5, 150, 75); |
| ctx.save(); |
| ctx.scale(50, 25); |
| ctx.strokeStyle = '#f00'; |
| ctx.stroke(); |
| ctx.restore(); |
| @assert pixel 0,0 == 0,255,0,255; |
| @assert pixel 50,0 == 0,255,0,255; |
| @assert pixel 99,0 == 0,255,0,255; |
| @assert pixel 0,25 == 0,255,0,255; |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 99,25 == 0,255,0,255; |
| @assert pixel 0,49 == 0,255,0,255; |
| @assert pixel 50,49 == 0,255,0,255; |
| @assert pixel 99,49 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.stroke.scale2 |
| desc: Stroke line widths are scaled by the current transformation matrix |
| testing: |
| - 2d.path.transformation |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.beginPath(); |
| ctx.rect(25, 12.5, 50, 25); |
| ctx.save(); |
| ctx.rotate(Math.PI/2); |
| ctx.scale(25, 50); |
| ctx.strokeStyle = '#0f0'; |
| ctx.stroke(); |
| ctx.restore(); |
| ctx.beginPath(); |
| ctx.rect(-25, -12.5, 150, 75); |
| ctx.save(); |
| ctx.rotate(Math.PI/2); |
| ctx.scale(25, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.stroke(); |
| ctx.restore(); |
| @assert pixel 0,0 == 0,255,0,255; |
| @assert pixel 50,0 == 0,255,0,255; |
| @assert pixel 99,0 == 0,255,0,255; |
| @assert pixel 0,25 == 0,255,0,255; |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 99,25 == 0,255,0,255; |
| @assert pixel 0,49 == 0,255,0,255; |
| @assert pixel 50,49 == 0,255,0,255; |
| @assert pixel 99,49 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.stroke.skew |
| desc: Strokes lines are skewed by the current transformation matrix |
| testing: |
| - 2d.path.transformation |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.save(); |
| ctx.beginPath(); |
| ctx.moveTo(49, -50); |
| ctx.lineTo(201, -50); |
| ctx.rotate(Math.PI/4); |
| ctx.scale(1, 283); |
| ctx.strokeStyle = '#0f0'; |
| ctx.stroke(); |
| ctx.restore(); |
| ctx.save(); |
| ctx.beginPath(); |
| ctx.translate(-150, 0); |
| ctx.moveTo(49, -50); |
| ctx.lineTo(199, -50); |
| ctx.rotate(Math.PI/4); |
| ctx.scale(1, 142); |
| ctx.strokeStyle = '#f00'; |
| ctx.stroke(); |
| ctx.restore(); |
| ctx.save(); |
| ctx.beginPath(); |
| ctx.translate(-150, 0); |
| ctx.moveTo(49, -50); |
| ctx.lineTo(199, -50); |
| ctx.rotate(Math.PI/4); |
| ctx.scale(1, 142); |
| ctx.strokeStyle = '#f00'; |
| ctx.stroke(); |
| ctx.restore(); |
| @assert pixel 0,0 == 0,255,0,255; |
| @assert pixel 50,0 == 0,255,0,255; |
| @assert pixel 99,0 == 0,255,0,255; |
| @assert pixel 0,25 == 0,255,0,255; |
| @assert pixel 50,25 == 0,255,0,255; |
| @assert pixel 99,25 == 0,255,0,255; |
| @assert pixel 0,49 == 0,255,0,255; |
| @assert pixel 50,49 == 0,255,0,255; |
| @assert pixel 99,49 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.stroke.empty |
| desc: Empty subpaths are not stroked |
| testing: |
| - 2d.path.stroke.empty |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 100; |
| ctx.lineCap = 'round'; |
| ctx.lineJoin = 'round'; |
| ctx.beginPath(); |
| ctx.moveTo(40, 25); |
| ctx.moveTo(60, 25); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.stroke.prune.line |
| desc: Zero-length line segments from lineTo are removed before stroking |
| testing: |
| - 2d.path.stroke.prune |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 100; |
| ctx.lineCap = 'round'; |
| ctx.lineJoin = 'round'; |
| ctx.beginPath(); |
| ctx.moveTo(50, 25); |
| ctx.lineTo(50, 25); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; @moz-todo |
| t.done(); |
| |
| - name: 2d.path.stroke.prune.closed |
| desc: Zero-length line segments from closed paths are removed before stroking |
| testing: |
| - 2d.path.stroke.prune |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 100; |
| ctx.lineCap = 'round'; |
| ctx.lineJoin = 'round'; |
| ctx.beginPath(); |
| ctx.moveTo(50, 25); |
| ctx.lineTo(50, 25); |
| ctx.closePath(); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; @moz-todo |
| t.done(); |
| |
| - name: 2d.path.stroke.prune.curve |
| desc: Zero-length line segments from quadraticCurveTo and bezierCurveTo are removed |
| before stroking |
| testing: |
| - 2d.path.stroke.prune |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 100; |
| ctx.lineCap = 'round'; |
| ctx.lineJoin = 'round'; |
| ctx.beginPath(); |
| ctx.moveTo(50, 25); |
| ctx.quadraticCurveTo(50, 25, 50, 25); |
| ctx.stroke(); |
| ctx.beginPath(); |
| ctx.moveTo(50, 25); |
| ctx.bezierCurveTo(50, 25, 50, 25, 50, 25); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; @moz-todo |
| t.done(); |
| |
| - name: 2d.path.stroke.prune.arc |
| desc: Zero-length line segments from arcTo and arc are removed before stroking |
| testing: |
| - 2d.path.stroke.prune |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 100; |
| ctx.lineCap = 'round'; |
| ctx.lineJoin = 'round'; |
| ctx.beginPath(); |
| ctx.moveTo(50, 25); |
| ctx.arcTo(50, 25, 150, 25, 10); |
| ctx.stroke(); |
| ctx.beginPath(); |
| ctx.moveTo(60, 25); |
| ctx.arc(50, 25, 10, 0, 0, false); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; @moz-todo |
| t.done(); |
| |
| - name: 2d.path.stroke.prune.rect |
| desc: Zero-length line segments from rect and strokeRect are removed before stroking |
| testing: |
| - 2d.path.stroke.prune |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 100; |
| ctx.lineCap = 'round'; |
| ctx.lineJoin = 'round'; |
| ctx.beginPath(); |
| ctx.rect(50, 25, 0, 0); |
| ctx.stroke(); |
| ctx.strokeRect(50, 25, 0, 0); |
| @assert pixel 50,25 == 0,255,0,255; @moz-todo |
| t.done(); |
| |
| - name: 2d.path.stroke.prune.corner |
| desc: Zero-length line segments are removed before stroking with miters |
| testing: |
| - 2d.path.stroke.prune |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 400; |
| ctx.lineJoin = 'miter'; |
| ctx.miterLimit = 1.4; |
| ctx.beginPath(); |
| ctx.moveTo(-1000, 200); |
| ctx.lineTo(-100, 200); |
| ctx.lineTo(-100, 200); |
| ctx.lineTo(-100, 200); |
| ctx.lineTo(-100, 1000); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.transformation.basic |
| testing: |
| - 2d.path.transformation |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.translate(-100, 0); |
| ctx.rect(100, 0, 100, 50); |
| ctx.translate(0, -100); |
| ctx.fillStyle = '#0f0'; |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.transformation.multiple |
| desc: Transformations are applied while building paths, not when drawing |
| testing: |
| - 2d.path.transformation |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#f00'; |
| ctx.translate(-100, 0); |
| ctx.rect(0, 0, 100, 50); |
| ctx.fill(); |
| ctx.translate(100, 0); |
| ctx.fill(); |
| ctx.beginPath(); |
| ctx.strokeStyle = '#f00'; |
| ctx.lineWidth = 50; |
| ctx.translate(0, -50); |
| ctx.moveTo(0, 25); |
| ctx.lineTo(100, 25); |
| ctx.stroke(); |
| ctx.translate(0, 50); |
| ctx.stroke(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.transformation.changing |
| desc: Transformations are applied while building paths, not when drawing |
| testing: |
| - 2d.path.transformation |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.moveTo(0, 0); |
| ctx.translate(100, 0); |
| ctx.lineTo(0, 0); |
| ctx.translate(0, 50); |
| ctx.lineTo(0, 0); |
| ctx.translate(-100, 0); |
| ctx.lineTo(0, 0); |
| ctx.translate(1000, 1000); |
| ctx.rotate(Math.PI/2); |
| ctx.scale(0.1, 0.1); |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.clip.empty |
| testing: |
| - 2d.path.clip.basic |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.beginPath(); |
| ctx.clip(); |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.clip.basic.1 |
| testing: |
| - 2d.path.clip.basic |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.beginPath(); |
| ctx.rect(0, 0, 100, 50); |
| ctx.clip(); |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.clip.basic.2 |
| testing: |
| - 2d.path.clip.basic |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.beginPath(); |
| ctx.rect(-100, 0, 100, 50); |
| ctx.clip(); |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.clip.intersect |
| testing: |
| - 2d.path.clip.basic |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.beginPath(); |
| ctx.rect(0, 0, 50, 50); |
| ctx.clip(); |
| ctx.beginPath(); |
| ctx.rect(50, 0, 50, 50) |
| ctx.clip(); |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.clip.winding.1 |
| testing: |
| - 2d.path.clip.basic |
| code: | |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.beginPath(); |
| ctx.moveTo(-10, -10); |
| ctx.lineTo(110, -10); |
| ctx.lineTo(110, 60); |
| ctx.lineTo(-10, 60); |
| ctx.lineTo(-10, -10); |
| ctx.lineTo(0, 0); |
| ctx.lineTo(0, 50); |
| ctx.lineTo(100, 50); |
| ctx.lineTo(100, 0); |
| ctx.clip(); |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.clip.winding.2 |
| testing: |
| - 2d.path.clip.basic |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.beginPath(); |
| ctx.moveTo(-10, -10); |
| ctx.lineTo(110, -10); |
| ctx.lineTo(110, 60); |
| ctx.lineTo(-10, 60); |
| ctx.lineTo(-10, -10); |
| ctx.clip(); |
| ctx.beginPath(); |
| ctx.moveTo(0, 0); |
| ctx.lineTo(0, 50); |
| ctx.lineTo(100, 50); |
| ctx.lineTo(100, 0); |
| ctx.lineTo(0, 0); |
| ctx.clip(); |
| ctx.fillStyle = '#0f0'; |
| ctx.fillRect(0, 0, 100, 50); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.clip.unaffected |
| testing: |
| - 2d.path.clip.closed |
| code: | |
| ctx.fillStyle = '#f00'; |
| ctx.fillRect(0, 0, 100, 50); |
| ctx.fillStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.moveTo(0, 0); |
| ctx.lineTo(0, 50); |
| ctx.lineTo(100, 50); |
| ctx.lineTo(100, 0); |
| ctx.clip(); |
| ctx.lineTo(0, 0); |
| ctx.fill(); |
| @assert pixel 50,25 == 0,255,0,255; |
| t.done(); |
| |
| - name: 2d.path.isPointInPath.basic.1 |
| desc: isPointInPath() detects whether the point is inside the path |
| testing: |
| - 2d.path.isPointInPath |
| code: | |
| ctx.rect(0, 0, 20, 20); |
| @assert ctx.isPointInPath(10, 10) === true; |
| @assert ctx.isPointInPath(30, 10) === false; |
| t.done(); |
| |
| - name: 2d.path.isPointInPath.basic.2 |
| desc: isPointInPath() detects whether the point is inside the path |
| testing: |
| - 2d.path.isPointInPath |
| code: | |
| ctx.rect(20, 0, 20, 20); |
| @assert ctx.isPointInPath(10, 10) === false; |
| @assert ctx.isPointInPath(30, 10) === true; |
| t.done(); |
| |
| - name: 2d.path.isPointInPath.edge |
| desc: isPointInPath() counts points on the path as being inside |
| testing: |
| - 2d.path.isPointInPath.edge |
| code: | |
| ctx.rect(0, 0, 20, 20); |
| @assert ctx.isPointInPath(0, 0) === true; |
| @assert ctx.isPointInPath(10, 0) === true; |
| @assert ctx.isPointInPath(20, 0) === true; |
| @assert ctx.isPointInPath(20, 10) === true; |
| @assert ctx.isPointInPath(20, 20) === true; |
| @assert ctx.isPointInPath(10, 20) === true; |
| @assert ctx.isPointInPath(0, 20) === true; |
| @assert ctx.isPointInPath(0, 10) === true; |
| @assert ctx.isPointInPath(10, -0.01) === false; |
| @assert ctx.isPointInPath(10, 20.01) === false; |
| @assert ctx.isPointInPath(-0.01, 10) === false; |
| @assert ctx.isPointInPath(20.01, 10) === false; |
| t.done(); |
| |
| - name: 2d.path.isPointInPath.empty |
| desc: isPointInPath() works when there is no path |
| testing: |
| - 2d.path.isPointInPath |
| code: | |
| @assert ctx.isPointInPath(0, 0) === false; |
| t.done(); |
| |
| - name: 2d.path.isPointInPath.subpath |
| desc: isPointInPath() uses the current path, not just the subpath |
| testing: |
| - 2d.path.isPointInPath |
| code: | |
| ctx.rect(0, 0, 20, 20); |
| ctx.beginPath(); |
| ctx.rect(20, 0, 20, 20); |
| ctx.closePath(); |
| ctx.rect(40, 0, 20, 20); |
| @assert ctx.isPointInPath(10, 10) === false; |
| @assert ctx.isPointInPath(30, 10) === true; |
| @assert ctx.isPointInPath(50, 10) === true; |
| t.done(); |
| |
| - name: 2d.path.isPointInPath.outside |
| desc: isPointInPath() works on paths outside the canvas |
| testing: |
| - 2d.path.isPointInPath |
| code: | |
| ctx.rect(0, -100, 20, 20); |
| ctx.rect(20, -10, 20, 20); |
| @assert ctx.isPointInPath(10, -110) === false; |
| @assert ctx.isPointInPath(10, -90) === true; |
| @assert ctx.isPointInPath(10, -70) === false; |
| @assert ctx.isPointInPath(30, -20) === false; |
| @assert ctx.isPointInPath(30, 0) === true; |
| @assert ctx.isPointInPath(30, 20) === false; |
| t.done(); |
| |
| - name: 2d.path.isPointInPath.unclosed |
| desc: isPointInPath() works on unclosed subpaths |
| testing: |
| - 2d.path.isPointInPath |
| code: | |
| ctx.moveTo(0, 0); |
| ctx.lineTo(20, 0); |
| ctx.lineTo(20, 20); |
| ctx.lineTo(0, 20); |
| @assert ctx.isPointInPath(10, 10) === true; |
| @assert ctx.isPointInPath(30, 10) === false; |
| t.done(); |
| |
| - name: 2d.path.isPointInPath.arc |
| desc: isPointInPath() works on arcs |
| testing: |
| - 2d.path.isPointInPath |
| code: | |
| ctx.arc(50, 25, 10, 0, Math.PI, false); |
| @assert ctx.isPointInPath(50, 10) === false; |
| @assert ctx.isPointInPath(50, 20) === false; |
| @assert ctx.isPointInPath(50, 30) === true; |
| @assert ctx.isPointInPath(50, 40) === false; |
| @assert ctx.isPointInPath(30, 20) === false; |
| @assert ctx.isPointInPath(70, 20) === false; |
| @assert ctx.isPointInPath(30, 30) === false; |
| @assert ctx.isPointInPath(70, 30) === false; |
| t.done(); |
| |
| - name: 2d.path.isPointInPath.bigarc |
| desc: isPointInPath() works on unclosed arcs larger than 2pi |
| opera: {bug: 320937} |
| testing: |
| - 2d.path.isPointInPath |
| code: | |
| ctx.arc(50, 25, 10, 0, 7, false); |
| @assert ctx.isPointInPath(50, 10) === false; |
| @assert ctx.isPointInPath(50, 20) === true; |
| @assert ctx.isPointInPath(50, 30) === true; |
| @assert ctx.isPointInPath(50, 40) === false; |
| @assert ctx.isPointInPath(30, 20) === false; |
| @assert ctx.isPointInPath(70, 20) === false; |
| @assert ctx.isPointInPath(30, 30) === false; |
| @assert ctx.isPointInPath(70, 30) === false; |
| t.done(); |
| |
| - name: 2d.path.isPointInPath.bezier |
| desc: isPointInPath() works on Bezier curves |
| testing: |
| - 2d.path.isPointInPath |
| code: | |
| ctx.moveTo(25, 25); |
| ctx.bezierCurveTo(50, -50, 50, 100, 75, 25); |
| @assert ctx.isPointInPath(25, 20) === false; |
| @assert ctx.isPointInPath(25, 30) === false; |
| @assert ctx.isPointInPath(30, 20) === true; |
| @assert ctx.isPointInPath(30, 30) === false; |
| @assert ctx.isPointInPath(40, 2) === false; |
| @assert ctx.isPointInPath(40, 20) === true; |
| @assert ctx.isPointInPath(40, 30) === false; |
| @assert ctx.isPointInPath(40, 47) === false; |
| @assert ctx.isPointInPath(45, 20) === true; |
| @assert ctx.isPointInPath(45, 30) === false; |
| @assert ctx.isPointInPath(55, 20) === false; |
| @assert ctx.isPointInPath(55, 30) === true; |
| @assert ctx.isPointInPath(60, 2) === false; |
| @assert ctx.isPointInPath(60, 20) === false; |
| @assert ctx.isPointInPath(60, 30) === true; |
| @assert ctx.isPointInPath(60, 47) === false; |
| @assert ctx.isPointInPath(70, 20) === false; |
| @assert ctx.isPointInPath(70, 30) === true; |
| @assert ctx.isPointInPath(75, 20) === false; |
| @assert ctx.isPointInPath(75, 30) === false; |
| t.done(); |
| |
| - name: 2d.path.isPointInPath.winding |
| desc: isPointInPath() uses the non-zero winding number rule |
| testing: |
| - 2d.path.isPointInPath |
| code: | |
| // Create a square ring, using opposite windings to make a hole in the centre |
| ctx.moveTo(0, 0); |
| ctx.lineTo(50, 0); |
| ctx.lineTo(50, 50); |
| ctx.lineTo(0, 50); |
| ctx.lineTo(0, 0); |
| ctx.lineTo(10, 10); |
| ctx.lineTo(10, 40); |
| ctx.lineTo(40, 40); |
| ctx.lineTo(40, 10); |
| ctx.lineTo(10, 10); |
| @assert ctx.isPointInPath(5, 5) === true; |
| @assert ctx.isPointInPath(25, 5) === true; |
| @assert ctx.isPointInPath(45, 5) === true; |
| @assert ctx.isPointInPath(5, 25) === true; |
| @assert ctx.isPointInPath(25, 25) === false; |
| @assert ctx.isPointInPath(45, 25) === true; |
| @assert ctx.isPointInPath(5, 45) === true; |
| @assert ctx.isPointInPath(25, 45) === true; |
| @assert ctx.isPointInPath(45, 45) === true; |
| t.done(); |
| |
| - name: 2d.path.isPointInPath.transform.1 |
| desc: isPointInPath() handles transformations correctly |
| testing: |
| - 2d.path.isPointInPath |
| code: | |
| ctx.translate(50, 0); |
| ctx.rect(0, 0, 20, 20); |
| @assert ctx.isPointInPath(-40, 10) === false; |
| @assert ctx.isPointInPath(10, 10) === false; |
| @assert ctx.isPointInPath(49, 10) === false; |
| @assert ctx.isPointInPath(51, 10) === true; |
| @assert ctx.isPointInPath(69, 10) === true; |
| @assert ctx.isPointInPath(71, 10) === false; |
| t.done(); |
| |
| - name: 2d.path.isPointInPath.transform.2 |
| desc: isPointInPath() handles transformations correctly |
| testing: |
| - 2d.path.isPointInPath |
| code: | |
| ctx.rect(50, 0, 20, 20); |
| ctx.translate(50, 0); |
| @assert ctx.isPointInPath(-40, 10) === false; |
| @assert ctx.isPointInPath(10, 10) === false; |
| @assert ctx.isPointInPath(49, 10) === false; |
| @assert ctx.isPointInPath(51, 10) === true; |
| @assert ctx.isPointInPath(69, 10) === true; |
| @assert ctx.isPointInPath(71, 10) === false; |
| t.done(); |
| |
| - name: 2d.path.isPointInPath.transform.3 |
| desc: isPointInPath() handles transformations correctly |
| testing: |
| - 2d.path.isPointInPath |
| code: | |
| ctx.scale(-1, 1); |
| ctx.rect(-70, 0, 20, 20); |
| @assert ctx.isPointInPath(-40, 10) === false; |
| @assert ctx.isPointInPath(10, 10) === false; |
| @assert ctx.isPointInPath(49, 10) === false; |
| @assert ctx.isPointInPath(51, 10) === true; |
| @assert ctx.isPointInPath(69, 10) === true; |
| @assert ctx.isPointInPath(71, 10) === false; |
| t.done(); |
| |
| - name: 2d.path.isPointInPath.transform.4 |
| desc: isPointInPath() handles transformations correctly |
| testing: |
| - 2d.path.isPointInPath |
| code: | |
| ctx.translate(50, 0); |
| ctx.rect(50, 0, 20, 20); |
| ctx.translate(0, 50); |
| @assert ctx.isPointInPath(60, 10) === false; |
| @assert ctx.isPointInPath(110, 10) === true; |
| @assert ctx.isPointInPath(110, 60) === false; |
| t.done(); |
| |
| - name: 2d.path.isPointInPath.nonfinite |
| desc: isPointInPath() returns false for non-finite arguments |
| testing: |
| - 2d.path.isPointInPath.nonfinite |
| code: | |
| ctx.rect(-100, -50, 200, 100); |
| @assert ctx.isPointInPath(Infinity, 0) === false; |
| @assert ctx.isPointInPath(-Infinity, 0) === false; |
| @assert ctx.isPointInPath(NaN, 0) === false; |
| @assert ctx.isPointInPath(0, Infinity) === false; |
| @assert ctx.isPointInPath(0, -Infinity) === false; |
| @assert ctx.isPointInPath(0, NaN) === false; |
| @assert ctx.isPointInPath(NaN, NaN) === false; |
| t.done(); |
| |
| - name: 2d.path.isPointInStroke.basic |
| desc: detects whether point is in the area contained by the stroke of the path |
| testing: |
| - 2d.path.isPointInStroke.basic |
| code: | |
| ctx.strokeStyle = '#0f0'; |
| ctx.beginPath(); |
| ctx.rect(0, 0, 20, 20); |
| @assert ctx.isPointInStroke(0, 0) === true; |
| @assert ctx.isPointInStroke(30, 10) === false; |
| |
| var path = new Path2D(); |
| path.rect(20, 20, 100, 100); |
| @assert ctx.isPointInStroke(path, 20, 20) === true; |
| @assert ctx.isPointInStroke(path, 120, 20) === true; |
| t.done(); |
| |